qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFC 1/1] hw/input: add basic support for a PCI PS/2 controller


From: Liav Albani
Subject: [RFC 1/1] hw/input: add basic support for a PCI PS/2 controller
Date: Fri, 19 May 2023 17:50:26 +0300

The linux pcips2 driver is responsible for driving this device.

Signed-off-by: Liav Albani <liavalb@gmail.com>
---
 hw/i386/Kconfig           |   1 +
 hw/input/Kconfig          |   5 +
 hw/input/meson.build      |   2 +
 hw/input/ps2pci.c         | 282 ++++++++++++++++++++++++++++++++++++++
 include/hw/input/ps2pci.h |  52 +++++++
 5 files changed, 342 insertions(+)
 create mode 100644 hw/input/ps2pci.c
 create mode 100644 include/hw/input/ps2pci.h

diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig
index d40802d..dad6188 100644
--- a/hw/i386/Kconfig
+++ b/hw/i386/Kconfig
@@ -36,6 +36,7 @@ config PC
     select I8259
     select I8254
     select PCKBD
+    select PS2PCI
     select PCSPK
     select I8257
     select MC146818RTC
diff --git a/hw/input/Kconfig b/hw/input/Kconfig
index 55865bb..04c97e8 100644
--- a/hw/input/Kconfig
+++ b/hw/input/Kconfig
@@ -17,6 +17,11 @@ config PL050
     bool
     select PS2
 
+config PS2PCI
+    bool
+    depends on PCI
+    select PS2
+
 config PS2
     bool
 
diff --git a/hw/input/meson.build b/hw/input/meson.build
index 8deb011..4fa1462 100644
--- a/hw/input/meson.build
+++ b/hw/input/meson.build
@@ -16,3 +16,5 @@ softmmu_ss.add(when: 'CONFIG_VHOST_USER_INPUT', if_true: 
files('vhost-user-input
 softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_keypad.c'))
 softmmu_ss.add(when: 'CONFIG_TSC210X', if_true: files('tsc210x.c'))
 softmmu_ss.add(when: 'CONFIG_LASIPS2', if_true: files('lasips2.c'))
+
+softmmu_ss.add(when: 'CONFIG_PS2PCI', if_true: files('ps2pci.c'))
diff --git a/hw/input/ps2pci.c b/hw/input/ps2pci.c
new file mode 100644
index 0000000..4340af1
--- /dev/null
+++ b/hw/input/ps2pci.c
@@ -0,0 +1,282 @@
+/*
+ * QEMU PCI PS/2 adapter.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+#include "hw/pci/pci_device.h"
+#include "hw/input/ps2pci.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "hw/irq.h"
+#include "qapi/error.h"
+#include "qom/object.h"
+#include "qemu/log.h"
+
+static const VMStateDescription vmstate_ps2_pci = {
+    .name = "ps2-pci",
+    .fields = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(parent_obj, PS2PCIState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+#define PS2_CTRL               (0)
+#define PS2_STATUS             (1)
+#define PS2_DATA               (2)
+
+#define PS2_CTRL_CLK           (1<<0)
+#define PS2_CTRL_DAT           (1<<1)
+#define PS2_CTRL_TXIRQ         (1<<2)
+#define PS2_CTRL_ENABLE                (1<<3)
+#define PS2_CTRL_RXIRQ         (1<<4)
+
+#define PS2_STAT_CLK           (1<<0)
+#define PS2_STAT_DAT           (1<<1)
+#define PS2_STAT_PARITY                (1<<2)
+#define PS2_STAT_RXFULL                (1<<5)
+#define PS2_STAT_TXBUSY                (1<<6)
+#define PS2_STAT_TXEMPTY       (1<<7)
+
+static void ps2_pci_update_irq(PS2PCIState *s)
+{
+    int level = (s->pending && (s->cr & PS2_CTRL_RXIRQ) != 0)
+                 || (s->cr & PS2_CTRL_TXIRQ) != 0;
+
+    qemu_set_irq(s->irq, level);
+}
+
+static void ps2_pci_set_irq(void *opaque, int n, int level)
+{
+    PS2PCIState *s = (PS2PCIState *)opaque;
+
+    s->pending = level;
+    ps2_pci_update_irq(s);
+}
+
+static uint64_t ps2_pci_io_read(void *opaque, hwaddr offset,
+                           unsigned size)
+{
+    PS2PCIState *s = (PS2PCIState*)opaque;
+    switch (offset) {
+    case PS2_CTRL: /* CTRL */
+        return s->cr;
+    case PS2_STATUS: /* STATUS */
+        {
+            uint32_t stat = 0;
+            if (s->pending)
+                stat = PS2_STAT_RXFULL;
+            else
+                stat = PS2_STAT_TXEMPTY;
+            uint8_t val = 0;
+            val = s->last;
+            val = val ^ (val >> 4);
+            val = val ^ (val >> 2);
+            val = (val ^ (val >> 1)) & 1;
+            if (val) {
+                stat |= PS2_STAT_PARITY;
+            }
+            return stat;
+        }
+    case PS2_DATA: /* DATA */
+        if (s->pending && s->cr & PS2_CTRL_ENABLE) {
+            s->last = ps2_read_data(s->ps2dev);
+            if (ps2_queue_empty(s->ps2dev))
+                s->pending = 0;
+        } else {
+            return 0;
+        }
+        return s->last;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "ps2_pci_io_read: Bad offset %x\n", (int)offset);
+        return 0;
+    }
+}
+
+static void ps2_pci_io_write(void *opaque, hwaddr offset,
+                        uint64_t value, unsigned size)
+{
+    PS2PCIState *s = (PS2PCIState *)opaque;
+    switch (offset) {
+    case PS2_CTRL: /* CTRL */
+        s->cr = value;
+        break;
+    case PS2_STATUS: /* STATUS */
+        break;
+    case PS2_DATA: /* DATA */
+        if (s->cr & PS2_CTRL_ENABLE) {
+            if (s->is_mouse) {
+                ps2_write_mouse(PS2_MOUSE_DEVICE(s->ps2dev), value);
+            } else {
+                ps2_write_keyboard(PS2_KBD_DEVICE(s->ps2dev), value);
+            }
+        }
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "ps2_pci_io_write: Bad offset %x\n", (int)offset);
+    }
+}
+
+static const MemoryRegionOps ps2_pci_io_ops = {
+    .read = ps2_pci_io_read,
+    .write = ps2_pci_io_write,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ps2_pci_realize_common(PCIDevice *dev, Error **errp)
+{
+    PS2PCIState *s = PS2_PCI(dev);
+    Object *obj = OBJECT(dev);
+    int ret;
+
+    uint8_t *pci_conf = dev->config;
+    pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */
+
+    s->irq = pci_allocate_irq(&s->parent_obj);
+    memory_region_init_io(&s->io, obj, &ps2_pci_io_ops, s,
+                          "ps2-pci-io", 16);
+    pci_set_byte(&s->parent_obj.config[PCI_REVISION_ID], 0);
+    pci_register_bar(&s->parent_obj, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io);
+
+    if (pci_bus_is_express(pci_get_bus(dev))) {
+        ret = pcie_endpoint_cap_init(dev, 0x80);
+        assert(ret > 0);
+    } else {
+        dev->cap_present &= ~QEMU_PCI_CAP_EXPRESS;
+    }
+    qdev_connect_gpio_out(DEVICE(s->ps2dev), PS2_DEVICE_IRQ,
+                       qdev_get_gpio_in_named((DeviceState*)dev, 
"ps2-input-irq", 0));
+}
+
+static void ps2_pci_keyboard_realize(PCIDevice *dev, Error **errp)
+{
+    PS2PCIKbdState *s = PS2_PCI_KBD_DEVICE(dev);
+    PS2PCIState *ps = PS2_PCI(dev);
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->kbd), errp)) {
+        return;
+    }
+
+    ps->ps2dev = PS2_DEVICE(&s->kbd);
+    ps2_pci_realize_common(dev, errp);
+}
+
+static void ps2_pci_mouse_realize(PCIDevice *dev, Error **errp)
+{
+    PS2PCIMouseState *s = PS2_PCI_MOUSE_DEVICE(dev);
+    PS2PCIState *ps = PS2_PCI(dev);
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->mouse), errp)) {
+        return;
+    }
+
+    ps->ps2dev = PS2_DEVICE(&s->mouse);
+    ps2_pci_realize_common(dev, errp);
+}
+
+static void ps2_pci_kbd_init(Object *obj)
+{
+    PCIDevice *dev = PCI_DEVICE(obj);
+    PS2PCIKbdState *s = PS2_PCI_KBD_DEVICE(obj);
+    PS2PCIState *ps = PS2_PCI(obj);
+
+    ps->is_mouse = false;
+    object_initialize_child(obj, "kbd", &s->kbd, TYPE_PS2_KBD_DEVICE);
+
+    dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
+    
+    qdev_init_gpio_out(DEVICE(obj), &ps->irq, 1);
+    qdev_init_gpio_in_named(DEVICE(obj), ps2_pci_set_irq,
+                            "ps2-input-irq", 1);
+}
+
+static void ps2_pci_mouse_init(Object *obj)
+{
+    PCIDevice *dev = PCI_DEVICE(obj);
+    PS2PCIMouseState *s = PS2_PCI_MOUSE_DEVICE(obj);
+    PS2PCIState *ps = PS2_PCI(obj);
+
+    ps->is_mouse = true;
+    object_initialize_child(obj, "mouse", &s->mouse, TYPE_PS2_MOUSE_DEVICE);
+
+    dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
+    
+    qdev_init_gpio_out(DEVICE(obj), &ps->irq, 1);
+    qdev_init_gpio_in_named(DEVICE(obj), ps2_pci_set_irq,
+                            "ps2-input-irq", 1);
+}
+
+static void ps2_pci_keyboard_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->class_id  = PCI_CLASS_INPUT_KEYBOARD;
+    k->vendor_id = 0x14f2;
+    k->device_id = 0x0123;
+
+    k->realize   = ps2_pci_keyboard_realize;
+    k->exit      = NULL;
+    dc->vmsd     = &vmstate_ps2_pci;
+    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+}
+
+static void ps2_pci_mouse_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->class_id  = PCI_CLASS_INPUT_MOUSE;
+    k->vendor_id = 0x14f2;
+    k->device_id = 0x0124;
+
+    k->realize   = ps2_pci_mouse_realize;
+    k->exit      = NULL;
+    dc->vmsd     = &vmstate_ps2_pci;
+    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+}
+
+static const TypeInfo ps2_pci_keyboard_type_info = {
+    .name           = TYPE_PS2_PCI_KBD_DEVICE,
+    .parent         = TYPE_PS2_PCI,
+    .instance_size  = sizeof(PS2PCIKbdState),
+    .instance_init  = ps2_pci_kbd_init,
+    .class_init     = ps2_pci_keyboard_class_init,
+};
+
+static const TypeInfo ps2_pci_mouse_type_info = {
+    .name           = TYPE_PS2_PCI_MOUSE_DEVICE,
+    .parent         = TYPE_PS2_PCI,
+    .instance_size  = sizeof(PS2PCIMouseState),
+    .instance_init  = ps2_pci_mouse_init,
+    .class_init     = ps2_pci_mouse_class_init,
+};
+
+static const TypeInfo ps2_pci_type_info = {
+    .name           = TYPE_PS2_PCI,
+    .parent         = TYPE_PCI_DEVICE,
+    .instance_size  = sizeof(PS2PCIState),
+    .abstract       = true,
+    .interfaces     = (InterfaceInfo[]) {
+        { INTERFACE_PCIE_DEVICE },
+        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+        { },
+    },
+};
+
+static void ps2_pci_register_types(void)
+{
+    type_register_static(&ps2_pci_keyboard_type_info);
+    type_register_static(&ps2_pci_mouse_type_info);
+    type_register_static(&ps2_pci_type_info);
+}
+
+type_init(ps2_pci_register_types)
diff --git a/include/hw/input/ps2pci.h b/include/hw/input/ps2pci.h
new file mode 100644
index 0000000..68a8c40
--- /dev/null
+++ b/include/hw/input/ps2pci.h
@@ -0,0 +1,52 @@
+/*
+ * QEMU PCI PS/2 adapter.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_PS2_PCI_H
+#define HW_PS2_PCI_H
+
+#include "hw/input/ps2.h"
+
+#define TYPE_PS2_PCI "ps2-pci"
+
+OBJECT_DECLARE_SIMPLE_TYPE(PS2PCIState, PS2_PCI)
+
+struct PS2PCIState {
+    /*< private >*/
+    PCIDevice parent_obj;
+    /*< public >*/
+
+    MemoryRegion     io;
+
+    PS2State *ps2dev;
+    uint32_t cr;
+    uint32_t clk;
+    uint32_t last;
+    int pending;
+    qemu_irq irq;
+    bool is_mouse;
+};
+
+#define TYPE_PS2_PCI_KBD_DEVICE "ps2-pci-keyboard"
+OBJECT_DECLARE_SIMPLE_TYPE(PS2PCIKbdState, PS2_PCI_KBD_DEVICE)
+
+struct PS2PCIKbdState {
+    PS2PCIState parent_obj;
+
+    PS2KbdState kbd;
+};
+
+#define TYPE_PS2_PCI_MOUSE_DEVICE "ps2-pci-mouse"
+OBJECT_DECLARE_SIMPLE_TYPE(PS2PCIMouseState, PS2_PCI_MOUSE_DEVICE)
+
+struct PS2PCIMouseState {
+    PS2PCIState parent_obj;
+
+    PS2MouseState mouse;
+};
+
+
+#endif /* HW_PS2_PCI_H */
-- 
2.40.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]