diff options
author | Kevin O'Connor <kevin@koconnor.net> | 2010-02-15 18:58:12 -0500 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2010-02-15 18:58:12 -0500 |
commit | 54671c1e9120287f7aaa2252892c9be4c2de5a76 (patch) | |
tree | 6d0731e61d3bea5fd195570fab7dd1446fefdeb4 /src/usb-hub.c | |
parent | 78523310ad4166597b0f2fbbfcfc915a7ca22fa5 (diff) | |
download | seabios-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.c | 138 |
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; +} |