X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/889bec69d67d36f58f5a1a05fc2832816cca8d6f..de550a6afb468ed3b8171019e19b63ae8254886d:/hw/pckbd.c diff --git a/hw/pckbd.c b/hw/pckbd.c index 0845c96497..69857bade9 100644 --- a/hw/pckbd.c +++ b/hw/pckbd.c @@ -29,6 +29,12 @@ /* debug PC keyboard */ //#define DEBUG_KBD +#ifdef DEBUG_KBD +#define DPRINTF(fmt, ...) \ + do { printf("KBD: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) +#endif /* Keyboard Controller Commands */ #define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */ @@ -50,7 +56,9 @@ #define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */ #define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */ #define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */ -#define KBD_CCMD_RESET 0xFE +#define KBD_CCMD_PULSE_BITS_3_0 0xF0 /* Pulse bits 3-0 of the output port P2. */ +#define KBD_CCMD_RESET 0xFE /* Pulse bit 0 of the output port P2 = CPU reset. */ +#define KBD_CCMD_NO_OP 0xFF /* Pulse no bits of the output port P2. */ /* Keyboard Commands */ #define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */ @@ -87,6 +95,12 @@ #define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */ #define KBD_MODE_RFU 0x80 +/* Output Port Bits */ +#define KBD_OUT_RESET 0x01 /* 1=normal mode, 0=reset */ +#define KBD_OUT_A20 0x02 /* x86 only */ +#define KBD_OUT_OBF 0x10 /* Keyboard output buffer full */ +#define KBD_OUT_MOUSE_OBF 0x20 /* Mouse output buffer full */ + /* Mouse Commands */ #define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */ #define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */ @@ -109,8 +123,6 @@ #define MOUSE_STATUS_ENABLED 0x20 #define MOUSE_STATUS_SCALE21 0x10 -#define KBD_QUEUE_SIZE 256 - #define KBD_PENDING_KBD 1 #define KBD_PENDING_AUX 2 @@ -118,6 +130,7 @@ typedef struct KBDState { uint8_t write_cmd; /* if non zero, write data to port 60 is expected */ uint8_t status; uint8_t mode; + uint8_t outport; /* Bitmask of devices with data available. */ uint8_t pending; void *kbd; @@ -125,12 +138,10 @@ typedef struct KBDState { qemu_irq irq_kbd; qemu_irq irq_mouse; - target_phys_addr_t base; - int it_shift; + qemu_irq *a20_out; + target_phys_addr_t mask; } KBDState; -KBDState kbd_state; - /* 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 */ @@ -141,11 +152,14 @@ static void kbd_update_irq(KBDState *s) irq_kbd_level = 0; irq_mouse_level = 0; s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF); + s->outport &= ~(KBD_OUT_OBF | KBD_OUT_MOUSE_OBF); if (s->pending) { s->status |= KBD_STAT_OBF; + s->outport |= KBD_OUT_OBF; /* kbd data takes priority over aux data. */ if (s->pending == KBD_PENDING_AUX) { s->status |= KBD_STAT_MOUSE_OBF; + s->outport |= KBD_OUT_MOUSE_OBF; if (s->mode & KBD_MODE_MOUSE_INT) irq_mouse_level = 1; } else { @@ -185,9 +199,7 @@ static uint32_t kbd_read_status(void *opaque, uint32_t addr) KBDState *s = opaque; int val; val = s->status; -#if defined(DEBUG_KBD) - printf("kbd: read status=0x%02x\n", val); -#endif + DPRINTF("kbd: read status=0x%02x\n", val); return val; } @@ -199,13 +211,38 @@ static void kbd_queue(KBDState *s, int b, int aux) ps2_queue(s->kbd, b); } +static void outport_write(KBDState *s, uint32_t val) +{ + DPRINTF("kbd: write outport=0x%02x\n", val); + s->outport = val; + if (s->a20_out) { + qemu_set_irq(*s->a20_out, (val >> 1) & 1); + } + if (!(val & 1)) { + qemu_system_reset_request(); + } +} + static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val) { KBDState *s = opaque; -#ifdef DEBUG_KBD - printf("kbd: write cmd=0x%02x\n", val); -#endif + DPRINTF("kbd: write cmd=0x%02x\n", val); + + /* Bits 3-0 of the output port P2 of the keyboard controller may be pulsed + * low for approximately 6 micro seconds. Bits 3-0 of the KBD_CCMD_PULSE + * command specify the output port bits to be pulsed. + * 0: Bit should be pulsed. 1: Bit should not be modified. + * The only useful version of this command is pulsing bit 0, + * which does a CPU reset. + */ + if((val & KBD_CCMD_PULSE_BITS_3_0) == KBD_CCMD_PULSE_BITS_3_0) { + if(!(val & 1)) + val = KBD_CCMD_RESET; + else + val = KBD_CCMD_NO_OP; + } + switch(val) { case KBD_CCMD_READ_MODE: kbd_queue(s, s->mode, 0); @@ -245,31 +282,25 @@ static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val) kbd_queue(s, 0x00, 0); break; case KBD_CCMD_READ_OUTPORT: - /* XXX: check that */ -#ifdef TARGET_I386 - val = 0x01 | (ioport_get_a20() << 1); -#else - val = 0x01; -#endif - if (s->status & KBD_STAT_OBF) - val |= 0x10; - if (s->status & KBD_STAT_MOUSE_OBF) - val |= 0x20; - kbd_queue(s, val, 0); + kbd_queue(s, s->outport, 0); break; -#ifdef TARGET_I386 case KBD_CCMD_ENABLE_A20: - ioport_set_a20(1); + if (s->a20_out) { + qemu_irq_raise(*s->a20_out); + } + s->outport |= KBD_OUT_A20; break; case KBD_CCMD_DISABLE_A20: - ioport_set_a20(0); + if (s->a20_out) { + qemu_irq_lower(*s->a20_out); + } + s->outport &= ~KBD_OUT_A20; break; -#endif case KBD_CCMD_RESET: qemu_system_reset_request(); break; - case 0xff: - /* ignore that - I don't know what is its use */ + case KBD_CCMD_NO_OP: + /* ignore that */ break; default: fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", val); @@ -287,9 +318,7 @@ static uint32_t kbd_read_data(void *opaque, uint32_t addr) else val = ps2_read_data(s->kbd); -#if defined(DEBUG_KBD) - printf("kbd: read data=0x%02x\n", val); -#endif + DPRINTF("kbd: read data=0x%02x\n", val); return val; } @@ -297,9 +326,7 @@ static void kbd_write_data(void *opaque, uint32_t addr, uint32_t val) { KBDState *s = opaque; -#ifdef DEBUG_KBD - printf("kbd: write data=0x%02x\n", val); -#endif + DPRINTF("kbd: write data=0x%02x\n", val); switch(s->write_cmd) { case 0: @@ -318,12 +345,7 @@ static void kbd_write_data(void *opaque, uint32_t addr, uint32_t val) kbd_queue(s, val, 1); break; case KBD_CCMD_WRITE_OUTPORT: -#ifdef TARGET_I386 - ioport_set_a20((val >> 1) & 1); -#endif - if (!(val & 1)) { - qemu_system_reset_request(); - } + outport_write(s, val); break; case KBD_CCMD_WRITE_MOUSE: ps2_write_mouse(s->mouse, val); @@ -340,114 +362,160 @@ static void kbd_reset(void *opaque) s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT; s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED; + s->outport = KBD_OUT_RESET | KBD_OUT_A20; } -static void kbd_save(QEMUFile* f, void* opaque) +static const VMStateDescription vmstate_kbd = { + .name = "pckbd", + .version_id = 3, + .minimum_version_id = 3, + .minimum_version_id_old = 3, + .fields = (VMStateField []) { + VMSTATE_UINT8(write_cmd, KBDState), + VMSTATE_UINT8(status, KBDState), + VMSTATE_UINT8(mode, KBDState), + VMSTATE_UINT8(pending, KBDState), + VMSTATE_END_OF_LIST() + } +}; + +/* Memory mapped interface */ +static uint32_t kbd_mm_readb (void *opaque, target_phys_addr_t addr) { - KBDState *s = (KBDState*)opaque; + KBDState *s = 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); + if (addr & s->mask) + return kbd_read_status(s, 0) & 0xff; + else + return kbd_read_data(s, 0) & 0xff; } -static int kbd_load(QEMUFile* f, void* opaque, int version_id) +static void kbd_mm_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) { - 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; + KBDState *s = opaque; + + if (addr & s->mask) + kbd_write_command(s, 0, value & 0xff); + else + kbd_write_data(s, 0, value & 0xff); } -void i8042_init(qemu_irq kbd_irq, qemu_irq mouse_irq, uint32_t io_base) +static const MemoryRegionOps i8042_mmio_ops = { + .endianness = DEVICE_NATIVE_ENDIAN, + .old_mmio = { + .read = { kbd_mm_readb, kbd_mm_readb, kbd_mm_readb }, + .write = { kbd_mm_writeb, kbd_mm_writeb, kbd_mm_writeb }, + }, +}; + +void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, + MemoryRegion *region, ram_addr_t size, + target_phys_addr_t mask) { - KBDState *s = &kbd_state; + KBDState *s = g_malloc0(sizeof(KBDState)); s->irq_kbd = kbd_irq; s->irq_mouse = mouse_irq; + s->mask = mask; - 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); + vmstate_register(NULL, 0, &vmstate_kbd, s); + + memory_region_init_io(region, &i8042_mmio_ops, s, "i8042", size); 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 */ -static uint32_t kbd_mm_readb (void *opaque, target_phys_addr_t addr) +typedef struct ISAKBDState { + ISADevice dev; + KBDState kbd; + MemoryRegion io[2]; +} ISAKBDState; + +void i8042_isa_mouse_fake_event(void *opaque) { - KBDState *s = opaque; + ISADevice *dev = opaque; + KBDState *s = &(DO_UPCAST(ISAKBDState, dev, dev)->kbd); - 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; - } + ps2_mouse_fake_event(s->mouse); } -static void kbd_mm_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) +void i8042_setup_a20_line(ISADevice *dev, qemu_irq *a20_out) { - KBDState *s = opaque; + KBDState *s = &(DO_UPCAST(ISAKBDState, dev, dev)->kbd); - 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; - } + s->a20_out = a20_out; } -static CPUReadMemoryFunc *kbd_mm_read[] = { - &kbd_mm_readb, - &kbd_mm_readb, - &kbd_mm_readb, +static const VMStateDescription vmstate_kbd_isa = { + .name = "pckbd", + .version_id = 3, + .minimum_version_id = 3, + .minimum_version_id_old = 3, + .fields = (VMStateField []) { + VMSTATE_STRUCT(kbd, ISAKBDState, 0, vmstate_kbd, KBDState), + VMSTATE_END_OF_LIST() + } }; -static CPUWriteMemoryFunc *kbd_mm_write[] = { - &kbd_mm_writeb, - &kbd_mm_writeb, - &kbd_mm_writeb, +static const MemoryRegionPortio i8042_data_portio[] = { + { 0, 1, 1, .read = kbd_read_data, .write = kbd_write_data }, + PORTIO_END_OF_LIST() }; -void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, - target_phys_addr_t base, int it_shift) +static const MemoryRegionPortio i8042_cmd_portio[] = { + { 0, 1, 1, .read = kbd_read_status, .write = kbd_write_command }, + PORTIO_END_OF_LIST() +}; + +static const MemoryRegionOps i8042_data_ops = { + .old_portio = i8042_data_portio +}; + +static const MemoryRegionOps i8042_cmd_ops = { + .old_portio = i8042_cmd_portio +}; + +static int i8042_initfn(ISADevice *dev) { - KBDState *s = &kbd_state; - int s_io_memory; + ISAKBDState *isa_s = DO_UPCAST(ISAKBDState, dev, dev); + KBDState *s = &isa_s->kbd; - s->irq_kbd = kbd_irq; - s->irq_mouse = mouse_irq; - s->base = base; - s->it_shift = it_shift; + isa_init_irq(dev, &s->irq_kbd, 1); + isa_init_irq(dev, &s->irq_mouse, 12); + + memory_region_init_io(isa_s->io + 0, &i8042_data_ops, s, "i8042-data", 1); + isa_register_ioport(dev, isa_s->io + 0, 0x60); - kbd_reset(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, 2 << it_shift, s_io_memory); + memory_region_init_io(isa_s->io + 1, &i8042_cmd_ops, s, "i8042-cmd", 1); + isa_register_ioport(dev, isa_s->io + 1, 0x64); 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); + return 0; +} + +static void i8042_class_initfn(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ISADeviceClass *ic = ISA_DEVICE_CLASS(klass); + ic->init = i8042_initfn; + dc->no_user = 1; + dc->vmsd = &vmstate_kbd_isa; } + +static TypeInfo i8042_info = { + .name = "i8042", + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(ISAKBDState), + .class_init = i8042_class_initfn, +}; + +static void i8042_register_types(void) +{ + type_register_static(&i8042_info); +} + +type_init(i8042_register_types)