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.
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 @@
 }
 
 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 @@
     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 @@
     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 @@
     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 @@
     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.
+    // Wait for threads to complete.
+    while (hub.threads)
+        yield();
 
-    // 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;
-
-        // 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;
 }
diff --git a/src/usb-hub.h b/src/usb-hub.h
index 852f771..399df57 100644
--- a/src/usb-hub.h
+++ b/src/usb-hub.h
@@ -1,6 +1,16 @@
 #ifndef __USB_HUB_H
 #define __USB_HUB_H
 
+struct usbhub_s {
+    struct usb_pipe *pipe;
+    struct usb_s *cntl;
+    struct mutex_s lock;
+    u32 powerwait;
+    u32 port;
+    u32 threads;
+    u32 devcount;
+};
+
 // usb-hub.c
 struct usb_pipe;
 int usb_hub_init(struct usb_pipe *pipe);
diff --git a/src/usb-ohci.c b/src/usb-ohci.c
index 828507b..3e94de6 100644
--- a/src/usb-ohci.c
+++ b/src/usb-ohci.c
@@ -11,11 +11,109 @@
 #include "pci_regs.h" // PCI_BASE_ADDRESS_0
 #include "usb.h" // struct usb_s
 #include "farptr.h" // GET_FLATPTR
+#include "usb-hub.h" // struct usbhub_s
 
 #define FIT                     (1 << 31)
 
 
 /****************************************************************
+ * Root hub
+ ****************************************************************/
+
+static void
+init_ohci_port(void *data)
+{
+    struct usbhub_s *hub = data;
+    u32 port = hub->port; // XXX - find better way to pass port
+
+    u32 sts = readl(&hub->cntl->ohci.regs->roothub_portstatus[port]);
+    if (!(sts & RH_PS_CCS))
+        // No device.
+        goto done;
+
+    // XXX - need to wait for USB_TIME_ATTDB if just powered up?
+
+    // Signal reset
+    mutex_lock(&hub->cntl->resetlock);
+    writel(&hub->cntl->ohci.regs->roothub_portstatus[port], RH_PS_PRS);
+    u64 end = calc_future_tsc(USB_TIME_DRSTR * 2);
+    for (;;) {
+        sts = readl(&hub->cntl->ohci.regs->roothub_portstatus[port]);
+        if (!(sts & RH_PS_PRS))
+            // XXX - need to ensure USB_TIME_DRSTR time in reset?
+            break;
+        if (check_time(end)) {
+            // Timeout.
+            warn_timeout();
+            goto resetfail;
+        }
+        yield();
+    }
+
+    if ((sts & (RH_PS_CCS|RH_PS_PES)) != (RH_PS_CCS|RH_PS_PES))
+        // Device no longer present
+        goto resetfail;
+
+    // Set address of port
+    struct usb_pipe *pipe = usb_set_address(hub->cntl, !!(sts & RH_PS_LSDA));
+    if (!pipe)
+        goto resetfail;
+    mutex_unlock(&hub->cntl->resetlock);
+
+    // Configure the device
+    int count = configure_usb_device(pipe);
+    free_pipe(pipe);
+    if (! count)
+        // Shutdown port
+        writel(&hub->cntl->ohci.regs->roothub_portstatus[port]
+               , RH_PS_CCS|RH_PS_LSDA);
+    hub->devcount += count;
+done:
+    hub->threads--;
+    return;
+
+resetfail:
+    // Shutdown port
+    writel(&hub->cntl->ohci.regs->roothub_portstatus[port]
+           , RH_PS_CCS|RH_PS_LSDA);
+    mutex_unlock(&hub->cntl->resetlock);
+    goto done;
+}
+
+// Find any devices connected to the root hub.
+static int
+check_ohci_ports(struct usb_s *cntl)
+{
+    ASSERT32FLAT();
+    // Turn on power for all devices on roothub.
+    u32 rha = readl(&cntl->ohci.regs->roothub_a);
+    rha &= ~(RH_A_PSM | RH_A_OCPM);
+    writel(&cntl->ohci.regs->roothub_status, RH_HS_LPSC);
+    writel(&cntl->ohci.regs->roothub_b, RH_B_PPCM);
+    msleep((rha >> 24) * 2);
+    // XXX - need to sleep for USB_TIME_SIGATT if just powered up?
+
+    // Lanuch a thread per port.
+    struct usbhub_s hub;
+    memset(&hub, 0, sizeof(hub));
+    hub.cntl = cntl;
+    int ports = rha & RH_A_NDP;
+    hub.threads = ports;
+    int i;
+    for (i=0; i<ports; i++) {
+        hub.port = i;
+        run_thread(init_ohci_port, &hub);
+    }
+
+    // Wait for threads to complete.
+    while (hub.threads)
+        yield();
+
+    return hub.devcount;
+}
+
+
+/****************************************************************
  * Setup
  ****************************************************************/
 
@@ -77,67 +175,6 @@
     readl(&cntl->ohci.regs->control); // flush writes
 }
 
-// Find any devices connected to the root hub.
-static int
-check_ohci_ports(struct usb_s *cntl)
-{
-    // Turn on power for all devices on roothub.
-    u32 rha = readl(&cntl->ohci.regs->roothub_a);
-    rha &= ~(RH_A_PSM | RH_A_OCPM);
-    writel(&cntl->ohci.regs->roothub_status, RH_HS_LPSC);
-    writel(&cntl->ohci.regs->roothub_b, RH_B_PPCM);
-    msleep((rha >> 24) * 2);
-    // XXX - need to sleep for USB_TIME_SIGATT if just powered up?
-
-    // Count and reset connected devices
-    int ports = rha & RH_A_NDP;
-    int totalcount = 0;
-    int i;
-    for (i=0; i<ports; i++) {
-        u32 sts = readl(&cntl->ohci.regs->roothub_portstatus[i]);
-        if (!(sts & RH_PS_CCS))
-            continue;
-        // XXX - need to wait for USB_TIME_ATTDB if just powered up?
-        writel(&cntl->ohci.regs->roothub_portstatus[i], RH_PS_PRS);
-        u64 end = calc_future_tsc(USB_TIME_DRSTR * 2);
-        for (;;) {
-            sts = readl(&cntl->ohci.regs->roothub_portstatus[i]);
-            if (!(sts & RH_PS_PRS))
-                // XXX - need to ensure USB_TIME_DRSTR time in reset?
-                break;
-            if (check_time(end)) {
-                // Timeout.
-                warn_timeout();
-                goto shutdown;
-            }
-            yield();
-        }
-
-        if ((sts & (RH_PS_CCS|RH_PS_PES)) != (RH_PS_CCS|RH_PS_PES))
-            // Device no longer present
-            continue;
-
-        msleep(USB_TIME_RSTRCY);
-
-        // XXX - should try to parallelize configuration.
-        int count = configure_usb_device(cntl, !!(sts & RH_PS_LSDA));
-        if (! count)
-            // Shutdown port
-            writel(&cntl->ohci.regs->roothub_portstatus[i]
-                   , RH_PS_CCS|RH_PS_LSDA);
-        totalcount += count;
-    }
-    if (!totalcount)
-        // No devices connected
-        goto shutdown;
-    return totalcount;
-
-shutdown:
-    // Turn off power to all ports
-    writel(&cntl->ohci.regs->roothub_status, RH_HS_LPS);
-    return 0;
-}
-
 void
 ohci_init(void *data)
 {
diff --git a/src/usb-uhci.c b/src/usb-uhci.c
index d4497bd..86eecea 100644
--- a/src/usb-uhci.c
+++ b/src/usb-uhci.c
@@ -13,6 +13,82 @@
 #include "usb.h" // struct usb_s
 #include "farptr.h" // GET_FLATPTR
 #include "biosvar.h" // GET_GLOBAL
+#include "usb-hub.h" // struct usbhub_s
+
+
+/****************************************************************
+ * Root hub
+ ****************************************************************/
+
+static void
+init_uhci_port(void *data)
+{
+    struct usbhub_s *hub = data;
+    u32 port = hub->port; // XXX - find better way to pass port
+    u16 ioport = hub->cntl->uhci.iobase + USBPORTSC1 + port * 2;
+
+    u16 status = inw(ioport);
+    if (!(status & USBPORTSC_CCS))
+        // No device
+        goto done;
+
+    // XXX - if just powered up, need to wait for USB_TIME_ATTDB?
+
+    // Reset port
+    outw(USBPORTSC_PR, ioport);
+    msleep(USB_TIME_DRSTR);
+    mutex_lock(&hub->cntl->resetlock);
+    outw(0, ioport);
+    udelay(6); // 64 high-speed bit times
+    status = inw(ioport);
+    if (!(status & USBPORTSC_CCS))
+        // No longer connected
+        goto resetfail;
+    outw(USBPORTSC_PE, ioport);
+    struct usb_pipe *pipe = usb_set_address(
+        hub->cntl, !!(status & USBPORTSC_LSDA));
+    if (!pipe)
+        goto resetfail;
+    mutex_unlock(&hub->cntl->resetlock);
+
+    // Configure port
+    int count = configure_usb_device(pipe);
+    free_pipe(pipe);
+    if (! count)
+        // Disable port
+        outw(0, ioport);
+    hub->devcount += count;
+done:
+    hub->threads--;
+    return;
+
+resetfail:
+    outw(0, ioport);
+    mutex_unlock(&hub->cntl->resetlock);
+    goto done;
+}
+
+// Find any devices connected to the root hub.
+static int
+check_ports(struct usb_s *cntl)
+{
+    ASSERT32FLAT();
+    struct usbhub_s hub;
+    memset(&hub, 0, sizeof(hub));
+    hub.cntl = cntl;
+    hub.threads = 2;
+
+    // Launch a thread for every port.
+    run_thread(init_uhci_port, &hub);
+    hub.port = 1;
+    run_thread(init_uhci_port, &hub);
+
+    // Wait for threads to complete.
+    while (hub.threads)
+        yield();
+
+    return hub.devcount;
+}
 
 
 /****************************************************************
@@ -90,54 +166,6 @@
     outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, cntl->uhci.iobase + USBCMD);
 }
 
-// Find any devices connected to the root hub.
-static int
-check_ports(struct usb_s *cntl)
-{
-    // XXX - if just powered up, need to wait for USB_TIME_SIGATT?
-    u16 port1 = inw(cntl->uhci.iobase + USBPORTSC1);
-    u16 port2 = inw(cntl->uhci.iobase + USBPORTSC2);
-
-    if (!((port1 & USBPORTSC_CCS) || (port2 & USBPORTSC_CCS)))
-        // No devices
-        return 0;
-
-    // XXX - if just powered up, need to wait for USB_TIME_ATTDB?
-
-    // reset ports
-    if (port1 & USBPORTSC_CCS)
-        outw(USBPORTSC_PR, cntl->uhci.iobase + USBPORTSC1);
-    if (port2 & USBPORTSC_CCS)
-        outw(USBPORTSC_PR, cntl->uhci.iobase + USBPORTSC2);
-    msleep(USB_TIME_DRSTR);
-
-    // Configure ports
-    int totalcount = 0;
-    outw(0, cntl->uhci.iobase + USBPORTSC1);
-    udelay(6); // 64 high-speed bit times
-    port1 = inw(cntl->uhci.iobase + USBPORTSC1);
-    if (port1 & USBPORTSC_CCS) {
-        outw(USBPORTSC_PE, cntl->uhci.iobase + USBPORTSC1);
-        msleep(USB_TIME_RSTRCY);
-        int count = configure_usb_device(cntl, !!(port1 & USBPORTSC_LSDA));
-        if (! count)
-            outw(0, cntl->uhci.iobase + USBPORTSC1);
-        totalcount += count;
-    }
-    outw(0, cntl->uhci.iobase + USBPORTSC2);
-    udelay(6);
-    port2 = inw(cntl->uhci.iobase + USBPORTSC2);
-    if (port2 & USBPORTSC_CCS) {
-        outw(USBPORTSC_PE, cntl->uhci.iobase + USBPORTSC2);
-        msleep(USB_TIME_RSTRCY);
-        int count = configure_usb_device(cntl, !!(port2 & USBPORTSC_LSDA));
-        if (! count)
-            outw(0, cntl->uhci.iobase + USBPORTSC2);
-        totalcount += count;
-    }
-    return totalcount;
-}
-
 void
 uhci_init(void *data)
 {
diff --git a/src/usb.c b/src/usb.c
index 5b3649c..ba70181 100644
--- a/src/usb.c
+++ b/src/usb.c
@@ -218,32 +218,6 @@
     return config;
 }
 
-static struct usb_pipe *
-set_address(struct usb_pipe *pipe)
-{
-    ASSERT32FLAT();
-    dprintf(3, "set_address %x\n", pipe->endp);
-    struct usb_s *cntl = endp2cntl(pipe->endp);
-    if (cntl->maxaddr >= USB_MAXADDR)
-        return 0;
-
-    struct usb_ctrlrequest req;
-    req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
-    req.bRequest = USB_REQ_SET_ADDRESS;
-    req.wValue = cntl->maxaddr + 1;
-    req.wIndex = 0;
-    req.wLength = 0;
-    int ret = send_default_control(pipe, &req, NULL);
-    if (ret)
-        return 0;
-    msleep(USB_TIME_SETADDR_RECOVERY);
-
-    cntl->maxaddr++;
-    u32 endp = mkendp(cntl, cntl->maxaddr, 0
-                      , endp2speed(pipe->endp), endp2maxsize(pipe->endp));
-    return alloc_control_pipe(endp);
-}
-
 static int
 set_configuration(struct usb_pipe *pipe, u16 val)
 {
@@ -261,25 +235,56 @@
  * Initialization and enumeration
  ****************************************************************/
 
-// Called for every found device - see if a driver is available for
-// this device and do setup if so.
-int
-configure_usb_device(struct usb_s *cntl, int lowspeed)
+// Assign an address to a device in the default state on the given
+// controller.
+struct usb_pipe *
+usb_set_address(struct usb_s *cntl, int lowspeed)
 {
     ASSERT32FLAT();
-    dprintf(3, "config_usb: %p %d\n", cntl, lowspeed);
+    dprintf(3, "set_address %p\n", cntl);
+    if (cntl->maxaddr >= USB_MAXADDR)
+        return NULL;
 
-    // Get device info
     struct usb_pipe *defpipe = cntl->defaultpipe;
     u32 endp = mkendp(cntl, 0, 0, lowspeed, 8);
     if (!defpipe) {
         cntl->defaultpipe = defpipe = alloc_control_pipe(endp);
         if (!defpipe)
-            return 0;
+            return NULL;
     }
     usb_alter_control(defpipe, endp);
+
+    msleep(USB_TIME_RSTRCY);
+
+    struct usb_ctrlrequest req;
+    req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
+    req.bRequest = USB_REQ_SET_ADDRESS;
+    req.wValue = cntl->maxaddr + 1;
+    req.wIndex = 0;
+    req.wLength = 0;
+    int ret = send_default_control(defpipe, &req, NULL);
+    if (ret)
+        return NULL;
+
+    msleep(USB_TIME_SETADDR_RECOVERY);
+
+    cntl->maxaddr++;
+    endp = mkendp(cntl, cntl->maxaddr, 0, lowspeed, 8);
+    return alloc_control_pipe(endp);
+}
+
+// Called for every found device - see if a driver is available for
+// this device and do setup if so.
+int
+configure_usb_device(struct usb_pipe *pipe)
+{
+    ASSERT32FLAT();
+    struct usb_s *cntl = endp2cntl(pipe->endp);
+    dprintf(3, "config_usb: %p\n", cntl);
+
+    // Set the max packet size for endpoint 0 of this device.
     struct usb_device_descriptor dinfo;
-    int ret = get_device_info8(defpipe, &dinfo);
+    int ret = get_device_info8(pipe, &dinfo);
     if (ret)
         return 0;
     dprintf(3, "device rev=%04x cls=%02x sub=%02x proto=%02x size=%02x\n"
@@ -287,12 +292,12 @@
             , dinfo.bDeviceProtocol, dinfo.bMaxPacketSize0);
     if (dinfo.bMaxPacketSize0 < 8 || dinfo.bMaxPacketSize0 > 64)
         return 0;
-    endp = mkendp(cntl, 0, 0, lowspeed, dinfo.bMaxPacketSize0);
-    usb_alter_control(defpipe, endp);
+    u32 endp = mkendp(cntl, endp2devaddr(pipe->endp), 0
+                      , endp2speed(pipe->endp), dinfo.bMaxPacketSize0);
+    usb_alter_control(pipe, endp);
 
     // Get configuration
-    struct usb_pipe *pipe = NULL;
-    struct usb_config_descriptor *config = get_device_config(defpipe);
+    struct usb_config_descriptor *config = get_device_config(pipe);
     if (!config)
         return 0;
 
@@ -307,10 +312,7 @@
         // Not a supported device.
         goto fail;
 
-    // Set the address and configure device.
-    pipe = set_address(defpipe);
-    if (!pipe)
-        goto fail;
+    // Set the configuration.
     ret = set_configuration(pipe, config->bConfigurationValue);
     if (ret)
         goto fail;
@@ -326,7 +328,6 @@
     if (ret)
         goto fail;
 
-    free_pipe(pipe);
     free(config);
     return 1;
 fail:
diff --git a/src/usb.h b/src/usb.h
index 0f65e39..5a6fc0f 100644
--- a/src/usb.h
+++ b/src/usb.h
@@ -2,6 +2,8 @@
 #ifndef __USB_H
 #define __USB_H
 
+#include "util.h" // struct mutex_s
+
 struct usb_pipe {
     u32 endp;
 };
@@ -12,6 +14,7 @@
     u8 maxaddr;
     u16 bdf;
     struct usb_pipe *defaultpipe;
+    struct mutex_s resetlock;
 
     union {
         struct {
@@ -33,7 +36,8 @@
 
 // usb.c
 void usb_setup(void);
-int configure_usb_device(struct usb_s *cntl, int lowspeed);
+struct usb_pipe *usb_set_address(struct usb_s *cntl, int lowspeed);
+int configure_usb_device(struct usb_pipe *pipe);
 struct usb_ctrlrequest;
 int send_default_control(struct usb_pipe *pipe, const struct usb_ctrlrequest *req
                          , void *data);