aboutsummaryrefslogtreecommitdiff
path: root/src/usb-hub.c
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2010-02-15 18:58:12 -0500
committerKevin O'Connor <kevin@koconnor.net>2010-02-15 18:58:12 -0500
commit54671c1e9120287f7aaa2252892c9be4c2de5a76 (patch)
tree6d0731e61d3bea5fd195570fab7dd1446fefdeb4 /src/usb-hub.c
parent78523310ad4166597b0f2fbbfcfc915a7ca22fa5 (diff)
downloadseabios-hppa-54671c1e9120287f7aaa2252892c9be4c2de5a76.zip
seabios-hppa-54671c1e9120287f7aaa2252892c9be4c2de5a76.tar.gz
seabios-hppa-54671c1e9120287f7aaa2252892c9be4c2de5a76.tar.bz2
Initial support for USB hubs.
Add support for detecting, initializing, and enumerating USB hubs.
Diffstat (limited to 'src/usb-hub.c')
-rw-r--r--src/usb-hub.c138
1 files changed, 138 insertions, 0 deletions
diff --git a/src/usb-hub.c b/src/usb-hub.c
new file mode 100644
index 0000000..ce9326b
--- /dev/null
+++ b/src/usb-hub.c
@@ -0,0 +1,138 @@
+// Code for handling standard USB hubs.
+//
+// Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU LGPLv3 license.
+
+#include "util.h" // dprintf
+#include "config.h" // CONFIG_USB_HUB
+#include "usb-hub.h" // struct usb_hub_descriptor
+#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);
+}
+
+// Configure a usb hub and then find devices connected to it.
+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;
+}