X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=hw%2Fpckbd.c;h=9b96b1cba6d75d90db35178f7b852a8dfcc79b53;hb=cd346349b45ef056f138a184f660b8c34c3213cc;hp=f9f6333315e3b775b053d5dabc971e6d20b81b27;hpb=67b915a5dd52a05f8030cd9edc005effd9c8eea5;p=qemu diff --git a/hw/pckbd.c b/hw/pckbd.c index f9f6333..9b96b1c 100644 --- a/hw/pckbd.c +++ b/hw/pckbd.c @@ -1,8 +1,8 @@ /* * QEMU PC keyboard emulation - * + * * Copyright (c) 2003 Fabrice Bellard - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -110,88 +110,73 @@ #define KBD_QUEUE_SIZE 256 -typedef struct { - 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 queues[2]; 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. */ + uint8_t pending; + void *kbd; + void *mouse; + + qemu_irq irq_kbd; + qemu_irq irq_mouse; + target_phys_addr_t base; + int it_shift; } KBDState; KBDState kbd_state; -int reset_requested; /* update irq and KBD_STAT_[MOUSE_]OBF */ /* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be incorrect, but it avoids having to simulate exact delays */ static void kbd_update_irq(KBDState *s) { - int irq12_level, irq1_level; + int irq_kbd_level, irq_mouse_level; - irq1_level = 0; - irq12_level = 0; + irq_kbd_level = 0; + irq_mouse_level = 0; s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF); - if (s->queues[0].count != 0 || - s->queues[1].count != 0) { + if (s->pending) { s->status |= KBD_STAT_OBF; - if (s->queues[1].count != 0) { + /* kbd 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; + irq_mouse_level = 1; } else { - if ((s->mode & KBD_MODE_KBD_INT) && + if ((s->mode & KBD_MODE_KBD_INT) && !(s->mode & KBD_MODE_DISABLE_KBD)) - irq1_level = 1; + irq_kbd_level = 1; } } - pic_set_irq(1, irq1_level); - pic_set_irq(12, irq12_level); + qemu_set_irq(s->irq_kbd, irq_kbd_level); + qemu_set_irq(s->irq_mouse, irq_mouse_level); } -static void kbd_queue(KBDState *s, int b, int aux) +static void kbd_update_kbd_irq(void *opaque, int level) { - KBDQueue *q = &s->queues[aux]; + 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->data[q->wptr] = b; - if (++q->wptr == KBD_QUEUE_SIZE) - q->wptr = 0; - q->count++; + s->pending &= ~KBD_PENDING_KBD; kbd_update_irq(s); } -void kbd_put_keycode(int keycode) +static void kbd_update_aux_irq(void *opaque, int level) { - KBDState *s = &kbd_state; - 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) @@ -205,6 +190,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; @@ -214,7 +207,7 @@ static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val) #endif switch(val) { case KBD_CCMD_READ_MODE: - kbd_queue(s, s->mode, 0); + kbd_queue(s, s->mode, 1); break; case KBD_CCMD_WRITE_MODE: case KBD_CCMD_WRITE_OBUF: @@ -253,7 +246,7 @@ static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val) case KBD_CCMD_READ_OUTPORT: /* XXX: check that */ #ifdef TARGET_I386 - val = 0x01 | (((cpu_single_env->a20_mask >> 20) & 1) << 1); + val = 0x01 | (ioport_get_a20() << 1); #else val = 0x01; #endif @@ -265,15 +258,14 @@ static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val) break; #ifdef TARGET_I386 case KBD_CCMD_ENABLE_A20: - cpu_x86_set_a20(cpu_single_env, 1); + ioport_set_a20(1); break; case KBD_CCMD_DISABLE_A20: - cpu_x86_set_a20(cpu_single_env, 0); + ioport_set_a20(0); break; #endif case KBD_CCMD_RESET: - reset_requested = 1; - cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); + qemu_system_reset_request(); break; case 0xff: /* ignore that - I don't know what is its use */ @@ -287,302 +279,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; - - q = &s->queues[0]; /* first check KBD data */ - if (q->count == 0) - q = &s->queues[1]; /* then check AUX data */ - 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 */ - q = &s->queues[0]; - index = q->rptr - 1; - if (index < 0) - index = KBD_QUEUE_SIZE - 1; - val = q->data[index]; - } else { - val = q->data[q->rptr]; - if (++q->rptr == KBD_QUEUE_SIZE) - q->rptr = 0; - q->count--; - /* reading deasserts IRQ */ - if (q == &s->queues[0]) - pic_set_irq(1, 0); - else - pic_set_irq(12, 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; -} - -void kbd_mouse_event(int dx, int dy, int dz, int buttons_state) -{ - KBDState *s = &kbd_state; - - /* 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; - s->mouse_buttons = buttons_state; - - if (!(s->mouse_status & MOUSE_STATUS_REMOTE) && - (s->queues[1].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; - 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; -#if 0 - /* 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; - } -#endif - 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) @@ -595,10 +296,12 @@ 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; + ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0); + /* ??? */ kbd_update_irq(s); break; case KBD_CCMD_WRITE_OBUF: @@ -609,15 +312,14 @@ void kbd_write_data(void *opaque, uint32_t addr, uint32_t val) break; case KBD_CCMD_WRITE_OUTPORT: #ifdef TARGET_I386 - cpu_x86_set_a20(cpu_single_env, (val >> 1) & 1); + ioport_set_a20((val >> 1) & 1); #endif if (!(val & 1)) { - reset_requested = 1; - cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); + qemu_system_reset_request(); } break; case KBD_CCMD_WRITE_MOUSE: - kbd_write_mouse(s, val); + ps2_write_mouse(s->mouse, val); break; default: break; @@ -625,30 +327,120 @@ void kbd_write_data(void *opaque, uint32_t addr, uint32_t val) s->write_cmd = 0; } -void kbd_reset(KBDState *s) +static void kbd_reset(void *opaque) { - KBDQueue *q; - int i; + KBDState *s = opaque; - 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; - for(i = 0; i < 2; i++) { - q = &s->queues[i]; - q->rptr = 0; - q->wptr = 0; - q->count = 0; +} + +static void kbd_save(QEMUFile* f, void* opaque) +{ + KBDState *s = (KBDState*)opaque; + + qemu_put_8s(f, &s->write_cmd); + qemu_put_8s(f, &s->status); + qemu_put_8s(f, &s->mode); + qemu_put_8s(f, &s->pending); +} + +static int kbd_load(QEMUFile* f, void* opaque, int version_id) +{ + KBDState *s = (KBDState*)opaque; + + if (version_id != 3) + return -EINVAL; + qemu_get_8s(f, &s->write_cmd); + qemu_get_8s(f, &s->status); + qemu_get_8s(f, &s->mode); + qemu_get_8s(f, &s->pending); + return 0; +} + +void i8042_init(qemu_irq kbd_irq, qemu_irq mouse_irq, uint32_t io_base) +{ + KBDState *s = &kbd_state; + + s->irq_kbd = kbd_irq; + s->irq_mouse = mouse_irq; + + kbd_reset(s); + register_savevm("pckbd", 0, 3, kbd_save, kbd_load, s); + register_ioport_read(io_base, 1, 1, kbd_read_data, s); + register_ioport_write(io_base, 1, 1, kbd_write_data, s); + register_ioport_read(io_base + 4, 1, 1, kbd_read_status, s); + register_ioport_write(io_base + 4, 1, 1, kbd_write_command, s); + + s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s); + s->mouse = ps2_mouse_init(kbd_update_aux_irq, s); +#ifdef TARGET_I386 + vmmouse_init(s->mouse); +#endif + qemu_register_reset(kbd_reset, s); +} + +/* Memory mapped interface */ +uint32_t kbd_mm_readb (void *opaque, target_phys_addr_t addr) +{ + KBDState *s = opaque; + + switch ((addr - s->base) >> s->it_shift) { + case 0: + return kbd_read_data(s, 0) & 0xff; + case 1: + return kbd_read_status(s, 0) & 0xff; + default: + return 0xff; } } -void kbd_init(void) +void kbd_mm_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + KBDState *s = opaque; + + switch ((addr - s->base) >> s->it_shift) { + case 0: + kbd_write_data(s, 0, value & 0xff); + break; + case 1: + kbd_write_command(s, 0, value & 0xff); + break; + } +} + +static CPUReadMemoryFunc *kbd_mm_read[] = { + &kbd_mm_readb, + &kbd_mm_readb, + &kbd_mm_readb, +}; + +static CPUWriteMemoryFunc *kbd_mm_write[] = { + &kbd_mm_writeb, + &kbd_mm_writeb, + &kbd_mm_writeb, +}; + +void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, + target_phys_addr_t base, int it_shift) { KBDState *s = &kbd_state; - + int s_io_memory; + + s->irq_kbd = kbd_irq; + s->irq_mouse = mouse_irq; + s->base = base; + s->it_shift = it_shift; + kbd_reset(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); + register_savevm("pckbd", 0, 3, kbd_save, kbd_load, s); + s_io_memory = cpu_register_io_memory(0, kbd_mm_read, kbd_mm_write, s); + cpu_register_physical_memory(base, 8 << it_shift, s_io_memory); + + s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s); + s->mouse = ps2_mouse_init(kbd_update_aux_irq, s); +#ifdef TARGET_I386 + vmmouse_init(s->mouse); +#endif + qemu_register_reset(kbd_reset, s); }