* [RFC 0/1] hw/input: add basic support for a PCI PS/2 controller
@ 2023-05-19 14:50 Liav Albani
2023-05-19 14:50 ` [RFC 1/1] " Liav Albani
0 siblings, 1 reply; 2+ messages in thread
From: Liav Albani @ 2023-05-19 14:50 UTC (permalink / raw)
To: qemu-devel; +Cc: Liav Albani
This small patch I wrote might raise few questions on the motivation to
why someone would write such thing like this - I'll try my best to be
descriptive about this.
I am SerenityOS developer. In that project I mainly work on kernel stuff
for the most part.
Recently I started to fix issues with the HID code in the SerenityOS
kernel and I stumbled upon fixing some issues with our PS/2 abstractions
in relation to hardware implementations (such as the i8042) and the rest
of the HID subsystem. I figured that it would be nice that with strong
abstractions in place that I could write more drivers for other PS2
serial IO controllers.
That's where I started to look into the Linux source code and I found
that sometime in the past, someone wrote a driver for PS/2 controller,
which is connected over the PCI bus. This driver is called pcips2 and
almost nobody touched it since its initial write in 2003 (by now it's 20
years old, so getting hardware to test it might be even harder today!).
Therefore, my goal was to see if I could actually use that driver, and
so I wrote this small patch for letting QEMU to emulate that very old
PS/2 PCI card. If this ever goes into the tree, a SerenityOS kernel
driver could be written too, and will probably be used someday in our
upcoming aarch64 port (probably for the virt machine as it supports the
PCI bus).
As for what it gives, keyboard works perfectly fine. I had some struggle
with interrupts initially (mainly getting "bogus" interrupts).
As for the mouse support - I tried to boot Manjaro live-CD on QEMU with
either both of the "pci-ps2-keyboard" and "pci-ps2-mouse" devices and
also with only "pci-ps2-mouse" - both options resulted in a kernel panic
a few seconds after booting. The panic seemed to be some sort of nullptr
dereference in the linux PS/2 core code, which I still need to debug.
In general, this potentially could benefit multiple projects, which is
the best case I'd imagine - more emulation options for QEMU, more
drivers for SerenityOS, and maybe even fixing issues in PS/2 core (in
Linux) and improving the pcips2 driver code in Linux as well.
Liav Albani (1):
hw/input: add basic support for a PCI PS/2 controller
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
--
2.40.1
^ permalink raw reply [flat|nested] 2+ messages in thread
* [RFC 1/1] hw/input: add basic support for a PCI PS/2 controller
2023-05-19 14:50 [RFC 0/1] hw/input: add basic support for a PCI PS/2 controller Liav Albani
@ 2023-05-19 14:50 ` Liav Albani
0 siblings, 0 replies; 2+ messages in thread
From: Liav Albani @ 2023-05-19 14:50 UTC (permalink / raw)
To: qemu-devel; +Cc: Liav Albani
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
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2023-05-19 14:54 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-05-19 14:50 [RFC 0/1] hw/input: add basic support for a PCI PS/2 controller Liav Albani
2023-05-19 14:50 ` [RFC 1/1] " Liav Albani
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).