* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include "vl.h"
+#include "hw.h"
+#include "isa.h"
+#include "pc.h"
+#include "ps2.h"
+#include "sysemu.h"
/* debug PC keyboard */
//#define DEBUG_KBD
-
-/* debug PC keyboard : only mouse */
-//#define DEBUG_MOUSE
+#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 */
#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 */
#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 */
#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
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;
qemu_irq irq_kbd;
qemu_irq irq_mouse;
- target_phys_addr_t base;
- int it_shift;
+ qemu_irq *a20_out;
+ hwaddr 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 */
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 {
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;
}
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);
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);
static uint32_t kbd_read_data(void *opaque, uint32_t addr)
{
KBDState *s = opaque;
+ uint32_t val;
if (s->pending == KBD_PENDING_AUX)
- return ps2_read_data(s->mouse);
+ val = ps2_read_data(s->mouse);
+ else
+ val = ps2_read_data(s->kbd);
- return ps2_read_data(s->kbd);
+ DPRINTF("kbd: read data=0x%02x\n", val);
+ return val;
}
-void kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
+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:
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);
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, hwaddr 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, hwaddr 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,
+ hwaddr mask)
{
- KBDState *s = &kbd_state;
+ KBDState *s = g_malloc0(sizeof(KBDState));
s->irq_kbd = kbd_irq;
s->irq_mouse = mouse_irq;
+ s->mask = mask;
+
+ vmstate_register(NULL, 0, &vmstate_kbd, s);
- 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);
+ 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 */
-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);
}
-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);
- 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, 8 << it_shift, s_io_memory);
+ memory_region_init_io(isa_s->io + 0, &i8042_data_ops, s, "i8042-data", 1);
+ isa_register_ioport(dev, isa_s->io + 0, 0x60);
+
+ 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)