aboutsummaryrefslogtreecommitdiff
path: root/src/usb-hub.c
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2010-03-09 19:58:23 -0500
committerKevin O'Connor <kevin@koconnor.net>2010-03-09 19:58:23 -0500
commit8ebcac023dad947a5229dce39e5864bbceeb18f3 (patch)
tree3107052860fb815111a9237ebfa9908ce64f6dbe /src/usb-hub.c
parente9086652b3901df4ff29bdd5eb095d0e3b8c349a (diff)
downloadseabios-hppa-8ebcac023dad947a5229dce39e5864bbceeb18f3.zip
seabios-hppa-8ebcac023dad947a5229dce39e5864bbceeb18f3.tar.gz
seabios-hppa-8ebcac023dad947a5229dce39e5864bbceeb18f3.tar.bz2
Further parallelize USB init by launching a thread per usb port.
Run a thread per usb port in addition to the existing thread per usb controller. This can reduce total boot time by allowing multiple USB devices on the same controller to initialize in parallel. It also makes startup time for critical devices (eg, the keyboard) less dependent on which port they are plugged into.
Diffstat (limited to 'src/usb-hub.c')
-rw-r--r--src/usb-hub.c185
1 files changed, 117 insertions, 68 deletions
diff --git a/src/usb-hub.c b/src/usb-hub.c
index b301f1c..da7d9f8 100644
--- a/src/usb-hub.c
+++ b/src/usb-hub.c
@@ -22,7 +22,7 @@ get_hub_desc(struct usb_pipe *pipe, struct usb_hub_descriptor *desc)
}
static int
-set_port_feature(struct usb_pipe *pipe, int port, int feature)
+set_port_feature(struct usbhub_s *hub, int port, int feature)
{
struct usb_ctrlrequest req;
req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
@@ -30,11 +30,14 @@ set_port_feature(struct usb_pipe *pipe, int port, int feature)
req.wValue = feature;
req.wIndex = port;
req.wLength = 0;
- return send_default_control(pipe, &req, NULL);
+ mutex_lock(&hub->lock);
+ int ret = send_default_control(hub->pipe, &req, NULL);
+ mutex_unlock(&hub->lock);
+ return ret;
}
static int
-clear_port_feature(struct usb_pipe *pipe, int port, int feature)
+clear_port_feature(struct usbhub_s *hub, int port, int feature)
{
struct usb_ctrlrequest req;
req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
@@ -42,11 +45,14 @@ clear_port_feature(struct usb_pipe *pipe, int port, int feature)
req.wValue = feature;
req.wIndex = port;
req.wLength = 0;
- return send_default_control(pipe, &req, NULL);
+ mutex_lock(&hub->lock);
+ int ret = send_default_control(hub->pipe, &req, NULL);
+ mutex_unlock(&hub->lock);
+ return ret;
}
static int
-get_port_status(struct usb_pipe *pipe, int port, struct usb_port_status *sts)
+get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts)
{
struct usb_ctrlrequest req;
req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_OTHER;
@@ -54,13 +60,103 @@ get_port_status(struct usb_pipe *pipe, int port, struct usb_port_status *sts)
req.wValue = 0;
req.wIndex = port;
req.wLength = sizeof(*sts);
- return send_default_control(pipe, &req, sts);
+ mutex_lock(&hub->lock);
+ int ret = send_default_control(hub->pipe, &req, sts);
+ mutex_unlock(&hub->lock);
+ return ret;
+}
+
+static void
+init_hub_port(void *data)
+{
+ struct usbhub_s *hub = data;
+ u32 port = hub->port; // XXX - find better way to pass port
+
+ // Turn on power to port.
+ int ret = set_port_feature(hub, port, USB_PORT_FEAT_POWER);
+ if (ret)
+ goto fail;
+
+ // Wait for port power to stabilize.
+ msleep(hub->powerwait);
+
+ // Check periodically for a device connect.
+ struct usb_port_status sts;
+ u64 end = calc_future_tsc(USB_TIME_SIGATT);
+ for (;;) {
+ ret = get_port_status(hub, port, &sts);
+ if (ret)
+ goto fail;
+ if (sts.wPortStatus & USB_PORT_STAT_CONNECTION)
+ // Device connected.
+ break;
+ if (check_time(end))
+ // No device found.
+ goto done;
+ msleep(5);
+ }
+
+ // XXX - wait USB_TIME_ATTDB time?
+
+ // Reset port.
+ mutex_lock(&hub->cntl->resetlock);
+ ret = set_port_feature(hub, port, USB_PORT_FEAT_RESET);
+ if (ret)
+ goto resetfail;
+
+ // Wait for reset to complete.
+ end = calc_future_tsc(USB_TIME_DRST * 2);
+ for (;;) {
+ ret = get_port_status(hub, port, &sts);
+ if (ret)
+ goto resetfail;
+ if (!(sts.wPortStatus & USB_PORT_STAT_RESET))
+ break;
+ if (check_time(end)) {
+ warn_timeout();
+ goto resetfail;
+ }
+ msleep(5);
+ }
+
+ // Reset complete.
+ if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
+ // Device no longer present
+ goto resetfail;
+
+ // Set address of port
+ struct usb_pipe *pipe = usb_set_address(
+ hub->cntl, !!(sts.wPortStatus & USB_PORT_STAT_LOW_SPEED));
+ if (!pipe)
+ goto resetfail;
+ mutex_unlock(&hub->cntl->resetlock);
+
+ // Configure the device
+ int count = configure_usb_device(pipe);
+ free_pipe(pipe);
+ if (!count) {
+ ret = clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE);
+ if (ret)
+ goto fail;
+ }
+ hub->devcount += count;
+done:
+ hub->threads--;
+ return;
+
+resetfail:
+ clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE);
+ mutex_unlock(&hub->cntl->resetlock);
+fail:
+ dprintf(1, "Failure on hub port %d setup\n", port);
+ goto done;
}
// Configure a usb hub and then find devices connected to it.
int
usb_hub_init(struct usb_pipe *pipe)
{
+ ASSERT32FLAT();
if (!CONFIG_USB_HUB)
return -1;
@@ -69,73 +165,26 @@ usb_hub_init(struct usb_pipe *pipe)
if (ret)
return ret;
- // Turn on power to all ports.
+ struct usbhub_s hub;
+ memset(&hub, 0, sizeof(hub));
+ hub.pipe = pipe;
+ hub.cntl = endp2cntl(pipe->endp);
+ hub.powerwait = desc.bPwrOn2PwrGood * 2;
+
+ // Launch a thread for every port.
int i;
for (i=1; i<=desc.bNbrPorts; i++) {
- ret = set_port_feature(pipe, i, USB_PORT_FEAT_POWER);
- if (ret)
- goto fail;
+ hub.port = i;
+ hub.threads++;
+ run_thread(init_hub_port, &hub);
}
- // 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(pipe->endp);
- int totalcount = 0;
- for (i=1; i<=desc.bNbrPorts; i++) {
- struct usb_port_status sts;
- ret = get_port_status(pipe, i, &sts);
- if (ret)
- goto fail;
- if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
- // XXX - power down port?
- continue;
+ // Wait for threads to complete.
+ while (hub.threads)
+ yield();
- // Reset port.
- ret = set_port_feature(pipe, i, USB_PORT_FEAT_RESET);
- if (ret)
- goto fail;
-
- // Wait for reset to complete.
- u64 end = calc_future_tsc(USB_TIME_DRST * 2);
- for (;;) {
- ret = get_port_status(pipe, i, &sts);
- 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(pipe, i, USB_PORT_FEAT_ENABLE);
- if (ret)
- goto fail;
- }
- totalcount += count;
- }
-
- dprintf(1, "Initialized USB HUB (%d ports used)\n", totalcount);
- if (totalcount)
+ dprintf(1, "Initialized USB HUB (%d ports used)\n", hub.devcount);
+ if (hub.devcount)
return 0;
return -1;
-
-fail:
- dprintf(1, "Failure on hub setup\n");
- return -1;
}