[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