PS2 mouse and keyboard separation (Paul Brook)

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1658 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
bellard 2005-11-26 10:14:03 +00:00
parent e80e1cc4b1
commit daa579632d
3 changed files with 51 additions and 381 deletions

View File

@ -298,13 +298,13 @@ VL_OBJS+= usb.o usb-uhci.o usb-linux.o usb-hid.o
ifeq ($(TARGET_BASE_ARCH), i386)
# Hardware support
VL_OBJS+= ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
VL_OBJS+= ide.o ne2000.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o
VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o
DEFINES += -DHAS_AUDIO
endif
ifeq ($(TARGET_BASE_ARCH), ppc)
VL_OBJS+= ppc.o ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
VL_OBJS+= ppc.o ide.o ne2000.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o
VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o heathrow_pic.o mixeng.o
DEFINES += -DHAS_AUDIO
@ -315,7 +315,7 @@ VL_OBJS+= mips_r4k.o dma.o vga.o serial.o ne2000.o i8254.o i8259.o
endif
ifeq ($(TARGET_BASE_ARCH), sparc)
ifeq ($(TARGET_ARCH), sparc64)
VL_OBJS+= sun4u.o ide.o ne2000.o pckbd.o vga.o
VL_OBJS+= sun4u.o ide.o ne2000.o pckbd.o ps2.o vga.o
VL_OBJS+= fdc.o mc146818rtc.o serial.o m48t59.o
VL_OBJS+= cirrus_vga.o parallel.o
VL_OBJS+= magic-load.o

View File

@ -110,32 +110,17 @@
#define KBD_QUEUE_SIZE 256
typedef struct {
uint8_t aux[KBD_QUEUE_SIZE];
uint8_t data[KBD_QUEUE_SIZE];
int rptr, wptr, count;
} KBDQueue;
#define KBD_PENDING_KBD 1
#define KBD_PENDING_AUX 2
typedef struct KBDState {
KBDQueue queue;
uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
uint8_t status;
uint8_t mode;
/* keyboard state */
int kbd_write_cmd;
int scan_enabled;
/* mouse state */
int mouse_write_cmd;
uint8_t mouse_status;
uint8_t mouse_resolution;
uint8_t mouse_sample_rate;
uint8_t mouse_wrap;
uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
uint8_t mouse_detect_state;
int mouse_dx; /* current values, needed for 'poll' mode */
int mouse_dy;
int mouse_dz;
uint8_t mouse_buttons;
/* Bitmask of devices with data available. */
int pending;
void *kbd;
void *mouse;
} KBDState;
KBDState kbd_state;
@ -145,15 +130,15 @@ KBDState kbd_state;
incorrect, but it avoids having to simulate exact delays */
static void kbd_update_irq(KBDState *s)
{
KBDQueue *q = &s->queue;
int irq12_level, irq1_level;
irq1_level = 0;
irq12_level = 0;
s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
if (q->count != 0) {
if (s->pending) {
s->status |= KBD_STAT_OBF;
if (q->aux[q->rptr]) {
/* kdb data takes priority over aux data. */
if (s->pending == KBD_PENDING_AUX) {
s->status |= KBD_STAT_MOUSE_OBF;
if (s->mode & KBD_MODE_MOUSE_INT)
irq12_level = 1;
@ -167,32 +152,26 @@ static void kbd_update_irq(KBDState *s)
pic_set_irq(12, irq12_level);
}
static void kbd_queue(KBDState *s, int b, int aux)
static void kbd_update_kbd_irq(void *opaque, int level)
{
KBDQueue *q = &s->queue;
KBDState *s = (KBDState *)opaque;
#if defined(DEBUG_MOUSE) || defined(DEBUG_KBD)
if (aux)
printf("mouse event: 0x%02x\n", b);
#ifdef DEBUG_KBD
if (level)
s->pending |= KBD_PENDING_KBD;
else
printf("kbd event: 0x%02x\n", b);
#endif
#endif
if (q->count >= KBD_QUEUE_SIZE)
return;
q->aux[q->wptr] = aux;
q->data[q->wptr] = b;
if (++q->wptr == KBD_QUEUE_SIZE)
q->wptr = 0;
q->count++;
s->pending &= ~KBD_PENDING_KBD;
kbd_update_irq(s);
}
static void pc_kbd_put_keycode(void *opaque, int keycode)
static void kbd_update_aux_irq(void *opaque, int level)
{
KBDState *s = opaque;
kbd_queue(s, keycode, 0);
KBDState *s = (KBDState *)opaque;
if (level)
s->pending |= KBD_PENDING_AUX;
else
s->pending &= ~KBD_PENDING_AUX;
kbd_update_irq(s);
}
static uint32_t kbd_read_status(void *opaque, uint32_t addr)
@ -206,6 +185,14 @@ static uint32_t kbd_read_status(void *opaque, uint32_t addr)
return val;
}
static void kbd_queue(KBDState *s, int b, int aux)
{
if (aux)
ps2_queue(s->mouse, b);
else
ps2_queue(s->kbd, b);
}
static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
{
KBDState *s = opaque;
@ -287,304 +274,11 @@ static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
static uint32_t kbd_read_data(void *opaque, uint32_t addr)
{
KBDState *s = opaque;
KBDQueue *q;
int val, index, aux;
q = &s->queue;
if (q->count == 0) {
/* NOTE: if no data left, we return the last keyboard one
(needed for EMM386) */
/* XXX: need a timer to do things correctly */
index = q->rptr - 1;
if (index < 0)
index = KBD_QUEUE_SIZE - 1;
val = q->data[index];
} else {
aux = q->aux[q->rptr];
val = q->data[q->rptr];
if (++q->rptr == KBD_QUEUE_SIZE)
q->rptr = 0;
q->count--;
/* reading deasserts IRQ */
if (aux)
pic_set_irq(12, 0);
else
pic_set_irq(1, 0);
}
/* reassert IRQs if data left */
kbd_update_irq(s);
#ifdef DEBUG_KBD
printf("kbd: read data=0x%02x\n", val);
#endif
return val;
}
static void kbd_reset_keyboard(KBDState *s)
{
s->scan_enabled = 1;
}
if (s->pending == KBD_PENDING_AUX)
return ps2_read_data(s->mouse);
static void kbd_write_keyboard(KBDState *s, int val)
{
switch(s->kbd_write_cmd) {
default:
case -1:
switch(val) {
case 0x00:
kbd_queue(s, KBD_REPLY_ACK, 0);
break;
case 0x05:
kbd_queue(s, KBD_REPLY_RESEND, 0);
break;
case KBD_CMD_GET_ID:
kbd_queue(s, KBD_REPLY_ACK, 0);
kbd_queue(s, 0xab, 0);
kbd_queue(s, 0x83, 0);
break;
case KBD_CMD_ECHO:
kbd_queue(s, KBD_CMD_ECHO, 0);
break;
case KBD_CMD_ENABLE:
s->scan_enabled = 1;
kbd_queue(s, KBD_REPLY_ACK, 0);
break;
case KBD_CMD_SET_LEDS:
case KBD_CMD_SET_RATE:
s->kbd_write_cmd = val;
kbd_queue(s, KBD_REPLY_ACK, 0);
break;
case KBD_CMD_RESET_DISABLE:
kbd_reset_keyboard(s);
s->scan_enabled = 0;
kbd_queue(s, KBD_REPLY_ACK, 0);
break;
case KBD_CMD_RESET_ENABLE:
kbd_reset_keyboard(s);
s->scan_enabled = 1;
kbd_queue(s, KBD_REPLY_ACK, 0);
break;
case KBD_CMD_RESET:
kbd_reset_keyboard(s);
kbd_queue(s, KBD_REPLY_ACK, 0);
kbd_queue(s, KBD_REPLY_POR, 0);
break;
default:
kbd_queue(s, KBD_REPLY_ACK, 0);
break;
}
break;
case KBD_CMD_SET_LEDS:
kbd_queue(s, KBD_REPLY_ACK, 0);
s->kbd_write_cmd = -1;
break;
case KBD_CMD_SET_RATE:
kbd_queue(s, KBD_REPLY_ACK, 0);
s->kbd_write_cmd = -1;
break;
}
}
static void kbd_mouse_send_packet(KBDState *s)
{
unsigned int b;
int dx1, dy1, dz1;
dx1 = s->mouse_dx;
dy1 = s->mouse_dy;
dz1 = s->mouse_dz;
/* XXX: increase range to 8 bits ? */
if (dx1 > 127)
dx1 = 127;
else if (dx1 < -127)
dx1 = -127;
if (dy1 > 127)
dy1 = 127;
else if (dy1 < -127)
dy1 = -127;
b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
kbd_queue(s, b, 1);
kbd_queue(s, dx1 & 0xff, 1);
kbd_queue(s, dy1 & 0xff, 1);
/* extra byte for IMPS/2 or IMEX */
switch(s->mouse_type) {
default:
break;
case 3:
if (dz1 > 127)
dz1 = 127;
else if (dz1 < -127)
dz1 = -127;
kbd_queue(s, dz1 & 0xff, 1);
break;
case 4:
if (dz1 > 7)
dz1 = 7;
else if (dz1 < -7)
dz1 = -7;
b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
kbd_queue(s, b, 1);
break;
}
/* update deltas */
s->mouse_dx -= dx1;
s->mouse_dy -= dy1;
s->mouse_dz -= dz1;
}
static void pc_kbd_mouse_event(void *opaque,
int dx, int dy, int dz, int buttons_state)
{
KBDState *s = opaque;
/* check if deltas are recorded when disabled */
if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
return;
s->mouse_dx += dx;
s->mouse_dy -= dy;
s->mouse_dz += dz;
/* XXX: SDL sometimes generates nul events: we delete them */
if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 &&
s->mouse_buttons == buttons_state)
return;
s->mouse_buttons = buttons_state;
if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
(s->queue.count < (KBD_QUEUE_SIZE - 16))) {
for(;;) {
/* if not remote, send event. Multiple events are sent if
too big deltas */
kbd_mouse_send_packet(s);
if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
break;
}
}
}
static void kbd_write_mouse(KBDState *s, int val)
{
#ifdef DEBUG_MOUSE
printf("kbd: write mouse 0x%02x\n", val);
#endif
switch(s->mouse_write_cmd) {
default:
case -1:
/* mouse command */
if (s->mouse_wrap) {
if (val == AUX_RESET_WRAP) {
s->mouse_wrap = 0;
kbd_queue(s, AUX_ACK, 1);
return;
} else if (val != AUX_RESET) {
kbd_queue(s, val, 1);
return;
}
}
switch(val) {
case AUX_SET_SCALE11:
s->mouse_status &= ~MOUSE_STATUS_SCALE21;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_SET_SCALE21:
s->mouse_status |= MOUSE_STATUS_SCALE21;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_SET_STREAM:
s->mouse_status &= ~MOUSE_STATUS_REMOTE;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_SET_WRAP:
s->mouse_wrap = 1;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_SET_REMOTE:
s->mouse_status |= MOUSE_STATUS_REMOTE;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_GET_TYPE:
kbd_queue(s, AUX_ACK, 1);
kbd_queue(s, s->mouse_type, 1);
break;
case AUX_SET_RES:
case AUX_SET_SAMPLE:
s->mouse_write_cmd = val;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_GET_SCALE:
kbd_queue(s, AUX_ACK, 1);
kbd_queue(s, s->mouse_status, 1);
kbd_queue(s, s->mouse_resolution, 1);
kbd_queue(s, s->mouse_sample_rate, 1);
break;
case AUX_POLL:
kbd_queue(s, AUX_ACK, 1);
kbd_mouse_send_packet(s);
break;
case AUX_ENABLE_DEV:
s->mouse_status |= MOUSE_STATUS_ENABLED;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_DISABLE_DEV:
s->mouse_status &= ~MOUSE_STATUS_ENABLED;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_SET_DEFAULT:
s->mouse_sample_rate = 100;
s->mouse_resolution = 2;
s->mouse_status = 0;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_RESET:
s->mouse_sample_rate = 100;
s->mouse_resolution = 2;
s->mouse_status = 0;
s->mouse_type = 0;
kbd_queue(s, AUX_ACK, 1);
kbd_queue(s, 0xaa, 1);
kbd_queue(s, s->mouse_type, 1);
break;
default:
break;
}
break;
case AUX_SET_SAMPLE:
s->mouse_sample_rate = val;
/* detect IMPS/2 or IMEX */
switch(s->mouse_detect_state) {
default:
case 0:
if (val == 200)
s->mouse_detect_state = 1;
break;
case 1:
if (val == 100)
s->mouse_detect_state = 2;
else if (val == 200)
s->mouse_detect_state = 3;
else
s->mouse_detect_state = 0;
break;
case 2:
if (val == 80)
s->mouse_type = 3; /* IMPS/2 */
s->mouse_detect_state = 0;
break;
case 3:
if (val == 80)
s->mouse_type = 4; /* IMEX */
s->mouse_detect_state = 0;
break;
}
kbd_queue(s, AUX_ACK, 1);
s->mouse_write_cmd = -1;
break;
case AUX_SET_RES:
s->mouse_resolution = val;
kbd_queue(s, AUX_ACK, 1);
s->mouse_write_cmd = -1;
break;
}
return ps2_read_data(s->kbd);
}
void kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
@ -597,10 +291,11 @@ void kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
switch(s->write_cmd) {
case 0:
kbd_write_keyboard(s, val);
ps2_write_keyboard(s->kbd, val);
break;
case KBD_CCMD_WRITE_MODE:
s->mode = val;
/* ??? */
kbd_update_irq(s);
break;
case KBD_CCMD_WRITE_OBUF:
@ -618,7 +313,7 @@ void kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
}
break;
case KBD_CCMD_WRITE_MOUSE:
kbd_write_mouse(s, val);
ps2_write_mouse(s->mouse, val);
break;
default:
break;
@ -629,16 +324,9 @@ void kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
static void kbd_reset(void *opaque)
{
KBDState *s = opaque;
KBDQueue *q;
s->kbd_write_cmd = -1;
s->mouse_write_cmd = -1;
s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
q = &s->queue;
q->rptr = 0;
q->wptr = 0;
q->count = 0;
}
static void kbd_save(QEMUFile* f, void* opaque)
@ -648,43 +336,17 @@ static void kbd_save(QEMUFile* f, void* opaque)
qemu_put_8s(f, &s->write_cmd);
qemu_put_8s(f, &s->status);
qemu_put_8s(f, &s->mode);
qemu_put_be32s(f, &s->kbd_write_cmd);
qemu_put_be32s(f, &s->scan_enabled);
qemu_put_be32s(f, &s->mouse_write_cmd);
qemu_put_8s(f, &s->mouse_status);
qemu_put_8s(f, &s->mouse_resolution);
qemu_put_8s(f, &s->mouse_sample_rate);
qemu_put_8s(f, &s->mouse_wrap);
qemu_put_8s(f, &s->mouse_type);
qemu_put_8s(f, &s->mouse_detect_state);
qemu_put_be32s(f, &s->mouse_dx);
qemu_put_be32s(f, &s->mouse_dy);
qemu_put_be32s(f, &s->mouse_dz);
qemu_put_8s(f, &s->mouse_buttons);
}
static int kbd_load(QEMUFile* f, void* opaque, int version_id)
{
KBDState *s = (KBDState*)opaque;
if (version_id != 1)
if (version_id != 2)
return -EINVAL;
qemu_get_8s(f, &s->write_cmd);
qemu_get_8s(f, &s->status);
qemu_get_8s(f, &s->mode);
qemu_get_be32s(f, &s->kbd_write_cmd);
qemu_get_be32s(f, &s->scan_enabled);
qemu_get_be32s(f, &s->mouse_write_cmd);
qemu_get_8s(f, &s->mouse_status);
qemu_get_8s(f, &s->mouse_resolution);
qemu_get_8s(f, &s->mouse_sample_rate);
qemu_get_8s(f, &s->mouse_wrap);
qemu_get_8s(f, &s->mouse_type);
qemu_get_8s(f, &s->mouse_detect_state);
qemu_get_be32s(f, &s->mouse_dx);
qemu_get_be32s(f, &s->mouse_dy);
qemu_get_be32s(f, &s->mouse_dz);
qemu_get_8s(f, &s->mouse_buttons);
return 0;
}
@ -693,13 +355,13 @@ void kbd_init(void)
KBDState *s = &kbd_state;
kbd_reset(s);
register_savevm("pckbd", 0, 1, kbd_save, kbd_load, s);
register_savevm("pckbd", 0, 2, kbd_save, kbd_load, s);
register_ioport_read(0x60, 1, 1, kbd_read_data, s);
register_ioport_write(0x60, 1, 1, kbd_write_data, s);
register_ioport_read(0x64, 1, 1, kbd_read_status, s);
register_ioport_write(0x64, 1, 1, kbd_write_command, s);
qemu_add_kbd_event_handler(pc_kbd_put_keycode, s);
qemu_add_mouse_event_handler(pc_kbd_mouse_event, s);
s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
qemu_register_reset(kbd_reset, s);
}

8
vl.h
View File

@ -928,6 +928,14 @@ void do_usb_add(const char *devname);
void do_usb_del(const char *devname);
void usb_info(void);
/* ps2.c */
void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg);
void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg);
void ps2_write_mouse(void *, int val);
void ps2_write_keyboard(void *, int val);
uint32_t ps2_read_data(void *);
void ps2_queue(void *, int b);
#endif /* defined(QEMU_TOOL) */
/* monitor.c */