From 4d647e6a33638e0e889607d01de24df39ded59fd Mon Sep 17 00:00:00 2001
From: Daniel Khodabakhsh <d.khodabakhsh@gmail.com>
Date: Wed, 13 Nov 2024 01:54:06 -0800
Subject: [PATCH] Use hlists to track USB HID devices.

---
 src/hw/usb-hid.c | 123 ++++++++++++++++++++++++++++++++---------------
 1 file changed, 84 insertions(+), 39 deletions(-)

diff --git a/src/hw/usb-hid.c b/src/hw/usb-hid.c
index dec198ac..b1432888 100644
--- a/src/hw/usb-hid.c
+++ b/src/hw/usb-hid.c
@@ -1,20 +1,54 @@
 // Code for handling USB Human Interface Devices (HID).
 //
 // Copyright (C) 2009  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2024  Daniel Khodabakhsh <d.khodabakhsh@gmail.com>
 //
 // This file may be distributed under the terms of the GNU LGPLv3 license.
 
 #include "biosvar.h" // GET_GLOBAL
 #include "config.h" // CONFIG_*
-#include "output.h" // dprintf
+#include "output.h" // dprintf, warn_noalloc
+#include "list.h" // hlist_*
+#include "malloc.h" // malloc_fseg
 #include "ps2port.h" // ATKBD_CMD_GETID
+#include "stacks.h" // mutex_lock, mutex_unlock
 #include "usb.h" // usb_ctrlrequest
 #include "usb-hid.h" // usb_keyboard_setup
 #include "util.h" // process_key
 
-struct usb_pipe *keyboard_pipe VARFSEG;
-struct usb_pipe *mouse_pipe VARFSEG;
+struct pipe_node {
+    struct usb_pipe *pipe;
+    struct hlist_node node;
+};
+
+static struct mutex_s usb_hid_lock;
+
+struct hlist_head keyboards VARFSEG = { .first = NULL };
+struct hlist_head mice VARFSEG = { .first = NULL };
+
+static int
+add_pipe_node(struct hlist_head *list
+              , struct usbdevice_s *usbdev
+              , struct usb_endpoint_descriptor *epdesc)
+{
+    struct usb_pipe *pipe = usb_alloc_pipe(usbdev, epdesc);
+    if (!pipe)
+        return -1;
+
+    struct pipe_node *new_pipe_node = malloc_fseg(sizeof(struct pipe_node));
+    if (!new_pipe_node) {
+        warn_noalloc();
+        return -1;
+    }
+
+    new_pipe_node->pipe = pipe;
 
+    mutex_lock(&usb_hid_lock);
+    hlist_add_head(&new_pipe_node->node, list);
+    mutex_unlock(&usb_hid_lock);
+
+    return 0;
+}
 
 /****************************************************************
  * Setup
@@ -64,9 +98,6 @@ usb_kbd_setup(struct usbdevice_s *usbdev
 {
     if (! CONFIG_USB_KEYBOARD)
         return -1;
-    if (keyboard_pipe)
-        // XXX - this enables the first found keyboard (could be random)
-        return -1;
 
     if (epdesc->wMaxPacketSize < sizeof(struct keyevent)
         || epdesc->wMaxPacketSize > MAX_KBD_EVENT) {
@@ -76,19 +107,16 @@ usb_kbd_setup(struct usbdevice_s *usbdev
     }
 
     // Enable "boot" protocol.
-    int ret = set_protocol(usbdev->defpipe, 0, usbdev->iface->bInterfaceNumber);
-    if (ret) {
+    if (set_protocol(usbdev->defpipe, 0, usbdev->iface->bInterfaceNumber)) {
         dprintf(3, "Failed to set boot protocol\n");
         return -1;
     }
 
     // Periodically send reports to enable key repeat.
-    ret = set_idle(usbdev->defpipe, KEYREPEATMS);
-    if (ret)
+    if (set_idle(usbdev->defpipe, KEYREPEATMS))
         dprintf(3, "Warning: Failed to set key repeat rate\n");
 
-    keyboard_pipe = usb_alloc_pipe(usbdev, epdesc);
-    if (!keyboard_pipe)
+    if (add_pipe_node(&keyboards, usbdev, epdesc))
         return -1;
 
     dprintf(1, "USB keyboard initialized\n");
@@ -109,9 +137,6 @@ usb_mouse_setup(struct usbdevice_s *usbdev
 {
     if (! CONFIG_USB_MOUSE)
         return -1;
-    if (mouse_pipe)
-        // XXX - this enables the first found mouse (could be random)
-        return -1;
 
     if (epdesc->wMaxPacketSize < sizeof(struct mouseevent)
         || epdesc->wMaxPacketSize > MAX_MOUSE_EVENT) {
@@ -121,12 +146,10 @@ usb_mouse_setup(struct usbdevice_s *usbdev
     }
 
     // Enable "boot" protocol.
-    int ret = set_protocol(usbdev->defpipe, 0, usbdev->iface->bInterfaceNumber);
-    if (ret)
+    if (set_protocol(usbdev->defpipe, 0, usbdev->iface->bInterfaceNumber))
         return -1;
 
-    mouse_pipe = usb_alloc_pipe(usbdev, epdesc);
-    if (!mouse_pipe)
+    if (add_pipe_node(&mice, usbdev, epdesc))
         return -1;
 
     dprintf(1, "USB mouse initialized\n");
@@ -325,16 +348,22 @@ usb_check_key(void)
 {
     if (! CONFIG_USB_KEYBOARD)
         return;
-    struct usb_pipe *pipe = GET_GLOBAL(keyboard_pipe);
-    if (!pipe)
-        return;
 
-    for (;;) {
-        u8 data[MAX_KBD_EVENT];
-        int ret = usb_poll_intr(pipe, data);
-        if (ret)
-            break;
-        handle_key((void*)data);
+    struct pipe_node *keyboard;
+    struct hlist_head keyboards_global = GET_GLOBAL(keyboards);
+    hlist_for_each_entry(keyboard, &keyboards_global, node) {
+        struct usb_pipe *pipe = GET_GLOBALFLAT(keyboard->pipe);
+
+        if (!pipe)
+            continue;
+
+        for (;;) {
+            u8 data[MAX_KBD_EVENT];
+            int ret = usb_poll_intr(pipe, data);
+            if (ret)
+                break;
+            handle_key((void*)data);
+        }
     }
 }
 
@@ -344,7 +373,12 @@ usb_kbd_active(void)
 {
     if (! CONFIG_USB_KEYBOARD)
         return 0;
-    return GET_GLOBAL(keyboard_pipe) != NULL;
+
+    struct hlist_head keyboards_global = GET_GLOBAL(keyboards);
+    if (!hlist_empty(&keyboards_global))
+        return 1;
+
+    return 0;
 }
 
 // Handle a ps2 style keyboard command.
@@ -390,16 +424,22 @@ usb_check_mouse(void)
 {
     if (! CONFIG_USB_MOUSE)
         return;
-    struct usb_pipe *pipe = GET_GLOBAL(mouse_pipe);
-    if (!pipe)
-        return;
 
-    for (;;) {
-        u8 data[MAX_MOUSE_EVENT];
-        int ret = usb_poll_intr(pipe, data);
-        if (ret)
-            break;
-        handle_mouse((void*)data);
+    struct pipe_node *mouse;
+    struct hlist_head mouse_global = GET_GLOBAL(mice);
+    hlist_for_each_entry(mouse, &mouse_global, node) {
+        struct usb_pipe *pipe = GET_GLOBALFLAT(mouse->pipe);
+
+        if (!pipe)
+            continue;
+
+        for (;;) {
+            u8 data[MAX_MOUSE_EVENT];
+            int ret = usb_poll_intr(pipe, data);
+            if (ret)
+                break;
+            handle_mouse((void*)data);
+        }
     }
 }
 
@@ -409,7 +449,12 @@ usb_mouse_active(void)
 {
     if (! CONFIG_USB_MOUSE)
         return 0;
-    return GET_GLOBAL(mouse_pipe) != NULL;
+
+    struct hlist_head mouse_global = GET_GLOBAL(mice);
+    if (!hlist_empty(&mouse_global))
+        return 1;
+
+    return 0;
 }
 
 // Handle a ps2 style mouse command.
-- 
2.46.0

