[SeaBIOS] [PATCH] Initial support for USB hubs.

Kevin O'Connor kevin at koconnor.net
Mon Feb 15 04:13:00 CET 2010


Initial support for USB hubs.  Note, I've only tested this on qemu - I
don't have a real USB hub to test on.  Help with testing would be
appreciated.

-Kevin



    Initial support for USB hubs.
    
    Add support for detecting, initializing, and enumerating USB hubs.

diff --git a/Makefile b/Makefile
index 5110267..17d4e1f 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ OUT=out/
 SRCBOTH=misc.c pmm.c stacks.c output.c util.c block.c floppy.c ata.c mouse.c \
         kbd.c pci.c serial.c clock.c pic.c cdrom.c ps2port.c smp.c resume.c \
         pnpbios.c pirtable.c vgahooks.c ramdisk.c pcibios.c \
-        usb.c usb-uhci.c usb-ohci.c usb-hid.c paravirt.c
+        usb.c usb-uhci.c usb-ohci.c usb-hid.c usb-hub.c paravirt.c
 SRC16=$(SRCBOTH) system.c disk.c apm.c font.c
 SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \
       acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \
diff --git a/src/config.h b/src/config.h
index d83d6ea..c02d496 100644
--- a/src/config.h
+++ b/src/config.h
@@ -36,6 +36,8 @@
 #define CONFIG_USB_UHCI 1
 // Support USB OHCI controllers
 #define CONFIG_USB_OHCI 1
+// Support USB hubs
+#define CONFIG_USB_HUB 1
 // Support USB keyboards
 #define CONFIG_USB_KEYBOARD 1
 // Support PS2 ports (keyboard and mouse)
diff --git a/src/usb-hub.c b/src/usb-hub.c
new file mode 100644
index 0000000..4ad783c
--- /dev/null
+++ b/src/usb-hub.c
@@ -0,0 +1,136 @@
+// Code for handling standard USB hubs.
+//
+// Copyright (C) 2010  Kevin O'Connor <kevin at koconnor.net>
+//
+// This file may be distributed under the terms of the GNU LGPLv3 license.
+
+#include "util.h" // dprintf
+#include "usb.h" // struct usb_s
+
+static int
+get_hub_desc(struct usb_hub_descriptor *desc, u32 endp)
+{
+    struct usb_ctrlrequest req;
+    req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE;
+    req.bRequest = USB_REQ_GET_DESCRIPTOR;
+    req.wValue = USB_DT_HUB<<8;
+    req.wIndex = 0;
+    req.wLength = sizeof(*desc);
+    return send_default_control(endp, &req, desc);
+}
+
+static int
+set_port_feature(int port, int feature, u32 endp)
+{
+    struct usb_ctrlrequest req;
+    req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
+    req.bRequest = USB_REQ_SET_FEATURE;
+    req.wValue = feature;
+    req.wIndex = port;
+    req.wLength = 0;
+    return send_default_control(endp, &req, NULL);
+}
+
+static int
+clear_port_feature(int port, int feature, u32 endp)
+{
+    struct usb_ctrlrequest req;
+    req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
+    req.bRequest = USB_REQ_CLEAR_FEATURE;
+    req.wValue = feature;
+    req.wIndex = port;
+    req.wLength = 0;
+    return send_default_control(endp, &req, NULL);
+}
+
+static int
+get_port_status(int port, struct usb_port_status *sts, u32 endp)
+{
+    struct usb_ctrlrequest req;
+    req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_OTHER;
+    req.bRequest = USB_REQ_GET_STATUS;
+    req.wValue = 0;
+    req.wIndex = port;
+    req.wLength = sizeof(*sts);
+    return send_default_control(endp, &req, sts);
+}
+
+// Find any devices connected to the root hub.
+int
+usb_hub_init(u32 endp)
+{
+    if (!CONFIG_USB_HUB)
+        return 0;
+
+    struct usb_hub_descriptor desc;
+    int ret = get_hub_desc(&desc, endp);
+    if (ret)
+        return ret;
+
+    // Turn on power to all ports.
+    int i;
+    for (i=1; i<=desc.bNbrPorts; i++) {
+        ret = set_port_feature(i, USB_PORT_FEAT_POWER, endp);
+        if (ret)
+            goto fail;
+    }
+
+    // Wait for port detection.
+    msleep(desc.bPwrOn2PwrGood * 2 + USB_TIME_SIGATT);
+    // XXX - should poll for ports becoming active sooner and then
+    // possibly wait USB_TIME_ATTDB.
+
+    // Detect down stream devices.
+    struct usb_s *cntl = endp2cntl(endp);
+    int totalcount = 0;
+    for (i=1; i<=desc.bNbrPorts; i++) {
+        struct usb_port_status sts;
+        ret = get_port_status(i, &sts, endp);
+        if (ret)
+            goto fail;
+        if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
+            // XXX - power down port?
+            continue;
+
+        // Reset port.
+        ret = set_port_feature(i, USB_PORT_FEAT_RESET, endp);
+        if (ret)
+            goto fail;
+
+        // Wait for reset to complete.
+        u64 end = calc_future_tsc(USB_TIME_DRST * 2);
+        for (;;) {
+            ret = get_port_status(i, &sts, endp);
+            if (ret)
+                goto fail;
+            if (!(sts.wPortStatus & USB_PORT_STAT_RESET))
+                break;
+            if (check_time(end)) {
+                // Timeout.
+                warn_timeout();
+                goto fail;
+            }
+            yield();
+        }
+        if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
+            // Device no longer present.  XXX - power down port?
+            continue;
+
+        // XXX - should try to parallelize configuration.
+        int count = configure_usb_device(
+            cntl, !!(sts.wPortStatus & USB_PORT_STAT_LOW_SPEED));
+        if (! count) {
+            // Shutdown port
+            ret = clear_port_feature(i, USB_PORT_FEAT_ENABLE, endp);
+            if (ret)
+                goto fail;
+        }
+        totalcount += count;
+    }
+
+    return totalcount;
+
+fail:
+    dprintf(1, "Failure on hub setup\n");
+    return 0;
+}
diff --git a/src/usb-hub.h b/src/usb-hub.h
new file mode 100644
index 0000000..24d8f1f
--- /dev/null
+++ b/src/usb-hub.h
@@ -0,0 +1,57 @@
+#ifndef __USB_HUB_H
+#define __USB_HUB_H
+
+// usb-hub.c
+int usb_hub_init(u32 endp);
+
+
+/****************************************************************
+ * hub flags
+ ****************************************************************/
+
+#define USB_DT_HUB                      (USB_TYPE_CLASS | 0x09)
+
+struct usb_hub_descriptor {
+    u8  bDescLength;
+    u8  bDescriptorType;
+    u8  bNbrPorts;
+    u16 wHubCharacteristics;
+    u8  bPwrOn2PwrGood;
+    u8  bHubContrCurrent;
+    // Variable length fields for DeviceRemovable[], PortPwrCtrlMask[] follow.
+} PACKED;
+
+#define USB_PORT_FEAT_CONNECTION        0
+#define USB_PORT_FEAT_ENABLE            1
+#define USB_PORT_FEAT_SUSPEND           2
+#define USB_PORT_FEAT_OVER_CURRENT      3
+#define USB_PORT_FEAT_RESET             4
+#define USB_PORT_FEAT_POWER             8
+#define USB_PORT_FEAT_LOWSPEED          9
+#define USB_PORT_FEAT_C_CONNECTION      16
+#define USB_PORT_FEAT_C_ENABLE          17
+#define USB_PORT_FEAT_C_SUSPEND         18
+#define USB_PORT_FEAT_C_OVER_CURRENT    19
+#define USB_PORT_FEAT_C_RESET           20
+#define USB_PORT_FEAT_TEST              21
+#define USB_PORT_FEAT_INDICATOR         22
+#define USB_PORT_FEAT_C_PORT_L1         23
+
+struct usb_port_status {
+    u16 wPortStatus;
+    u16 wPortChange;
+} PACKED;
+
+#define USB_PORT_STAT_CONNECTION        0x0001
+#define USB_PORT_STAT_ENABLE            0x0002
+#define USB_PORT_STAT_SUSPEND           0x0004
+#define USB_PORT_STAT_OVERCURRENT       0x0008
+#define USB_PORT_STAT_RESET             0x0010
+#define USB_PORT_STAT_L1                0x0020
+#define USB_PORT_STAT_POWER             0x0100
+#define USB_PORT_STAT_LOW_SPEED         0x0200
+#define USB_PORT_STAT_HIGH_SPEED        0x0400
+#define USB_PORT_STAT_TEST              0x0800
+#define USB_PORT_STAT_INDICATOR         0x1000
+
+#endif // ush-hid.h
diff --git a/src/usb.c b/src/usb.c
index 7108991..cb6f391 100644
--- a/src/usb.c
+++ b/src/usb.c
@@ -12,6 +12,7 @@
 #include "usb-uhci.h" // uhci_init
 #include "usb-ohci.h" // ohci_init
 #include "usb-hid.h" // usb_keyboard_setup
+#include "usb-hub.h" // usb_hub_init
 #include "usb.h" // struct usb_s
 #include "biosvar.h" // GET_GLOBAL
 
@@ -171,10 +172,11 @@ configure_usb_device(struct usb_s *cntl, int lowspeed)
     // Determine if a driver exists for this device - only look at the
     // first interface of the first configuration.
     struct usb_interface_descriptor *iface = (void*)(&config[1]);
-    if (iface->bInterfaceClass != USB_CLASS_HID
-        || iface->bInterfaceSubClass != USB_INTERFACE_SUBCLASS_BOOT
-        || iface->bInterfaceProtocol != USB_INTERFACE_PROTOCOL_KEYBOARD)
-        // Not a "boot" keyboard
+    if ((iface->bInterfaceClass != USB_CLASS_HID
+         || iface->bInterfaceSubClass != USB_INTERFACE_SUBCLASS_BOOT
+         || iface->bInterfaceProtocol != USB_INTERFACE_PROTOCOL_KEYBOARD)
+        && (iface->bInterfaceClass != USB_CLASS_HUB))
+        // Not a supported device.
         goto fail;
 
     // Set the address and configure device.
@@ -186,6 +188,10 @@ configure_usb_device(struct usb_s *cntl, int lowspeed)
         goto fail;
 
     // Configure driver.
+    if (iface->bInterfaceClass == USB_CLASS_HUB) {
+        free(config);
+        return usb_hub_init(endp);
+    }
     ret = usb_keyboard_init(endp, iface, ((void*)config + config->wTotalLength
                                           - (void*)iface));
     if (ret)



More information about the SeaBIOS mailing list