[SeaBIOS] [PATCH 1/4] Prefer passing a USB "pipe" structure over a USB endp encoding.

Kevin O'Connor kevin at koconnor.net
Wed Mar 10 02:16:52 CET 2010


Instead of passing the "u32 endp" encoding of the usb endpoint,
allocate a "struct usb_pipe" for each end point and pass that.
Allocate a control pipe for every device found.  Support freeing the
pipes after they are done.

Implement pipe allocation and freeing for both UHCI and OHCI
controllers.

Also, don't define every UHCI qh to include a pipe - create a separate
structure "struct uhci_pipe".  Also, be sure to clear the
USBControllers on reset.  Also, convert usb_hub_init to return 0 on
success.  Also, cleanup some of the USB debug messages to make them
more consistent.
---
 src/ps2port.c  |    2 +-
 src/usb-hid.c  |   25 ++++----
 src/usb-hid.h  |    5 +-
 src/usb-hub.c  |   41 ++++++------
 src/usb-hub.h  |    3 +-
 src/usb-msc.c  |    9 ++-
 src/usb-msc.h  |    4 +-
 src/usb-ohci.c |  166 ++++++++++++++++++++++++++++++++++++++----------
 src/usb-ohci.h |    6 ++-
 src/usb-uhci.c |  195 +++++++++++++++++++++++++++++++++++++++++---------------
 src/usb-uhci.h |   12 +--
 src/usb.c      |  118 +++++++++++++++++++++++++---------
 src/usb.h      |   18 +++--
 13 files changed, 429 insertions(+), 175 deletions(-)

diff --git a/src/ps2port.c b/src/ps2port.c
index 49bf551..26c55f2 100644
--- a/src/ps2port.c
+++ b/src/ps2port.c
@@ -415,7 +415,7 @@ keyboard_init(void *data)
     if (ret)
         return;
 
-    dprintf(1, "keyboard initialized\n");
+    dprintf(1, "PS2 keyboard initialized\n");
 }
 
 void
diff --git a/src/usb-hid.c b/src/usb-hid.c
index ae16686..eae6823 100644
--- a/src/usb-hid.c
+++ b/src/usb-hid.c
@@ -18,7 +18,7 @@ struct usb_pipe *keyboard_pipe VAR16VISIBLE;
  ****************************************************************/
 
 static int
-set_protocol(u32 endp, u16 val)
+set_protocol(struct usb_pipe *pipe, u16 val)
 {
     struct usb_ctrlrequest req;
     req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
@@ -26,11 +26,11 @@ set_protocol(u32 endp, u16 val)
     req.wValue = val;
     req.wIndex = 0;
     req.wLength = 0;
-    return send_default_control(endp, &req, NULL);
+    return send_default_control(pipe, &req, NULL);
 }
 
 static int
-set_idle(u32 endp, int ms)
+set_idle(struct usb_pipe *pipe, int ms)
 {
     struct usb_ctrlrequest req;
     req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
@@ -38,21 +38,22 @@ set_idle(u32 endp, int ms)
     req.wValue = (ms/4)<<8;
     req.wIndex = 0;
     req.wLength = 0;
-    return send_default_control(endp, &req, NULL);
+    return send_default_control(pipe, &req, NULL);
 }
 
 #define KEYREPEATWAITMS 500
 #define KEYREPEATMS 33
 
 int
-usb_keyboard_init(u32 endp, struct usb_interface_descriptor *iface, int imax)
+usb_keyboard_init(struct usb_pipe *pipe
+                  , struct usb_interface_descriptor *iface, int imax)
 {
     if (! CONFIG_USB_KEYBOARD)
         return -1;
     if (keyboard_pipe)
         // XXX - this enables the first found keyboard (could be random)
         return -1;
-    dprintf(2, "usb_keyboard_setup %x\n", endp);
+    dprintf(2, "usb_keyboard_setup %x\n", pipe->endp);
 
     // Find intr in endpoint.
     struct usb_endpoint_descriptor *epdesc = findEndPointDesc(
@@ -61,22 +62,22 @@ usb_keyboard_init(u32 endp, struct usb_interface_descriptor *iface, int imax)
         dprintf(1, "No keyboard intr in?\n");
         return -1;
     }
-    u32 inendp = mkendpFromDesc(endp, epdesc);
 
     // Enable "boot" protocol.
-    int ret = set_protocol(endp, 1);
+    int ret = set_protocol(pipe, 1);
     if (ret)
         return -1;
     // Periodically send reports to enable key repeat.
-    ret = set_idle(endp, KEYREPEATMS);
+    ret = set_idle(pipe, KEYREPEATMS);
     if (ret)
         return -1;
 
-    struct usb_pipe *pipe = alloc_intr_pipe(inendp, epdesc->bInterval);
-    if (!pipe)
+    u32 inendp = mkendpFromDesc(pipe, epdesc);
+    keyboard_pipe = alloc_intr_pipe(inendp, epdesc->bInterval);
+    if (!keyboard_pipe)
         return -1;
-    keyboard_pipe = pipe;
 
+    dprintf(1, "USB keyboard initialized\n");
     return 0;
 }
 
diff --git a/src/usb-hid.h b/src/usb-hid.h
index 6dbb2ad..1c75560 100644
--- a/src/usb-hid.h
+++ b/src/usb-hid.h
@@ -3,8 +3,9 @@
 
 // usb-hid.c
 struct usb_interface_descriptor;
-int usb_keyboard_init(u32 endp, struct usb_interface_descriptor *iface
-                      , int imax);
+struct usb_pipe;
+int usb_keyboard_init(struct usb_pipe *pipe
+                      , struct usb_interface_descriptor *iface, int imax);
 void usb_keyboard_setup(void);
 void usb_check_key(void);
 
diff --git a/src/usb-hub.c b/src/usb-hub.c
index ce9326b..b301f1c 100644
--- a/src/usb-hub.c
+++ b/src/usb-hub.c
@@ -10,7 +10,7 @@
 #include "usb.h" // struct usb_s
 
 static int
-get_hub_desc(struct usb_hub_descriptor *desc, u32 endp)
+get_hub_desc(struct usb_pipe *pipe, struct usb_hub_descriptor *desc)
 {
     struct usb_ctrlrequest req;
     req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE;
@@ -18,11 +18,11 @@ get_hub_desc(struct usb_hub_descriptor *desc, u32 endp)
     req.wValue = USB_DT_HUB<<8;
     req.wIndex = 0;
     req.wLength = sizeof(*desc);
-    return send_default_control(endp, &req, desc);
+    return send_default_control(pipe, &req, desc);
 }
 
 static int
-set_port_feature(int port, int feature, u32 endp)
+set_port_feature(struct usb_pipe *pipe, int port, int feature)
 {
     struct usb_ctrlrequest req;
     req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
@@ -30,11 +30,11 @@ set_port_feature(int port, int feature, u32 endp)
     req.wValue = feature;
     req.wIndex = port;
     req.wLength = 0;
-    return send_default_control(endp, &req, NULL);
+    return send_default_control(pipe, &req, NULL);
 }
 
 static int
-clear_port_feature(int port, int feature, u32 endp)
+clear_port_feature(struct usb_pipe *pipe, int port, int feature)
 {
     struct usb_ctrlrequest req;
     req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
@@ -42,11 +42,11 @@ clear_port_feature(int port, int feature, u32 endp)
     req.wValue = feature;
     req.wIndex = port;
     req.wLength = 0;
-    return send_default_control(endp, &req, NULL);
+    return send_default_control(pipe, &req, NULL);
 }
 
 static int
-get_port_status(int port, struct usb_port_status *sts, u32 endp)
+get_port_status(struct usb_pipe *pipe, int port, struct usb_port_status *sts)
 {
     struct usb_ctrlrequest req;
     req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_OTHER;
@@ -54,25 +54,25 @@ get_port_status(int port, struct usb_port_status *sts, u32 endp)
     req.wValue = 0;
     req.wIndex = port;
     req.wLength = sizeof(*sts);
-    return send_default_control(endp, &req, sts);
+    return send_default_control(pipe, &req, sts);
 }
 
 // Configure a usb hub and then find devices connected to it.
 int
-usb_hub_init(u32 endp)
+usb_hub_init(struct usb_pipe *pipe)
 {
     if (!CONFIG_USB_HUB)
-        return 0;
+        return -1;
 
     struct usb_hub_descriptor desc;
-    int ret = get_hub_desc(&desc, endp);
+    int ret = get_hub_desc(pipe, &desc);
     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);
+        ret = set_port_feature(pipe, i, USB_PORT_FEAT_POWER);
         if (ret)
             goto fail;
     }
@@ -83,11 +83,11 @@ usb_hub_init(u32 endp)
     // possibly wait USB_TIME_ATTDB.
 
     // Detect down stream devices.
-    struct usb_s *cntl = endp2cntl(endp);
+    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(i, &sts, endp);
+        ret = get_port_status(pipe, i, &sts);
         if (ret)
             goto fail;
         if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
@@ -95,14 +95,14 @@ usb_hub_init(u32 endp)
             continue;
 
         // Reset port.
-        ret = set_port_feature(i, USB_PORT_FEAT_RESET, endp);
+        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(i, &sts, endp);
+            ret = get_port_status(pipe, i, &sts);
             if (ret)
                 goto fail;
             if (!(sts.wPortStatus & USB_PORT_STAT_RESET))
@@ -123,16 +123,19 @@ usb_hub_init(u32 endp)
             cntl, !!(sts.wPortStatus & USB_PORT_STAT_LOW_SPEED));
         if (! count) {
             // Shutdown port
-            ret = clear_port_feature(i, USB_PORT_FEAT_ENABLE, endp);
+            ret = clear_port_feature(pipe, i, USB_PORT_FEAT_ENABLE);
             if (ret)
                 goto fail;
         }
         totalcount += count;
     }
 
-    return totalcount;
+    dprintf(1, "Initialized USB HUB (%d ports used)\n", totalcount);
+    if (totalcount)
+        return 0;
+    return -1;
 
 fail:
     dprintf(1, "Failure on hub setup\n");
-    return 0;
+    return -1;
 }
diff --git a/src/usb-hub.h b/src/usb-hub.h
index 24d8f1f..852f771 100644
--- a/src/usb-hub.h
+++ b/src/usb-hub.h
@@ -2,7 +2,8 @@
 #define __USB_HUB_H
 
 // usb-hub.c
-int usb_hub_init(u32 endp);
+struct usb_pipe;
+int usb_hub_init(struct usb_pipe *pipe);
 
 
 /****************************************************************
diff --git a/src/usb-msc.c b/src/usb-msc.c
index c1fa45d..cb60c3e 100644
--- a/src/usb-msc.c
+++ b/src/usb-msc.c
@@ -50,7 +50,7 @@ struct csw_s {
 int
 usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
 {
-    dprintf(1, "usb_cmd_data id=%p write=%d count=%d bs=%d buf=%p\n"
+    dprintf(16, "usb_cmd_data id=%p write=%d count=%d bs=%d buf=%p\n"
             , op->drive_g, 0, op->count, blocksize, op->buf_fl);
     struct usbdrive_s *udrive_g = container_of(
         op->drive_g, struct usbdrive_s, drive);
@@ -173,7 +173,8 @@ setup_drive_hd(struct disk_op_s *op)
 
 // Configure a usb msc device.
 int
-usb_msc_init(u32 endp, struct usb_interface_descriptor *iface, int imax)
+usb_msc_init(struct usb_pipe *pipe
+             , struct usb_interface_descriptor *iface, int imax)
 {
     if (!CONFIG_USB_MSC)
         return -1;
@@ -193,9 +194,9 @@ usb_msc_init(u32 endp, struct usb_interface_descriptor *iface, int imax)
         iface, imax, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT);
     if (!indesc || !outdesc)
         goto fail;
-    u32 inendp = mkendpFromDesc(endp, indesc);
+    u32 inendp = mkendpFromDesc(pipe, indesc);
     struct usb_pipe *bulkin = alloc_bulk_pipe(inendp);
-    u32 outendp = mkendpFromDesc(endp, outdesc);
+    u32 outendp = mkendpFromDesc(pipe, outdesc);
     struct usb_pipe *bulkout = alloc_bulk_pipe(outendp);
     if (!bulkin || !bulkout)
         goto fail;
diff --git a/src/usb-msc.h b/src/usb-msc.h
index 50c2193..467ccf3 100644
--- a/src/usb-msc.h
+++ b/src/usb-msc.h
@@ -5,7 +5,9 @@
 struct disk_op_s;
 int usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize);
 struct usb_interface_descriptor;
-int usb_msc_init(u32 endp, struct usb_interface_descriptor *iface, int imax);
+struct usb_pipe;
+int usb_msc_init(struct usb_pipe *pipe
+                 , struct usb_interface_descriptor *iface, int imax);
 int process_usb_op(struct disk_op_s *op);
 
 
diff --git a/src/usb-ohci.c b/src/usb-ohci.c
index 4ce75a6..828507b 100644
--- a/src/usb-ohci.c
+++ b/src/usb-ohci.c
@@ -14,6 +14,11 @@
 
 #define FIT                     (1 << 31)
 
+
+/****************************************************************
+ * Setup
+ ****************************************************************/
+
 static int
 start_ohci(struct usb_s *cntl, struct ohci_hcca *hcca)
 {
@@ -41,7 +46,7 @@ start_ohci(struct usb_s *cntl, struct ohci_hcca *hcca)
     }
 
     // Init memory
-    writel(&cntl->ohci.regs->ed_controlhead, (u32)cntl->ohci.control_ed);
+    writel(&cntl->ohci.regs->ed_controlhead, 0);
     writel(&cntl->ohci.regs->ed_bulkhead, 0);
     writel(&cntl->ohci.regs->hcca, (u32)hcca);
 
@@ -162,8 +167,7 @@ ohci_init(void *data)
     // Allocate memory
     struct ohci_hcca *hcca = memalign_high(256, sizeof(*hcca));
     struct ohci_ed *intr_ed = malloc_high(sizeof(*intr_ed));
-    struct ohci_ed *control_ed = malloc_high(sizeof(*control_ed));
-    if (!hcca || !intr_ed || !control_ed) {
+    if (!hcca || !intr_ed) {
         warn_noalloc();
         goto free;
     }
@@ -173,15 +177,13 @@ ohci_init(void *data)
     int i;
     for (i=0; i<ARRAY_SIZE(hcca->int_table); i++)
         hcca->int_table[i] = (u32)intr_ed;
-    memset(control_ed, 0, sizeof(*control_ed));
-    control_ed->hwINFO = ED_SKIP;
-    cntl->ohci.control_ed = control_ed;
 
     int ret = start_ohci(cntl, hcca);
     if (ret)
         goto err;
 
     int count = check_ohci_ports(cntl);
+    free_pipe(cntl->defaultpipe);
     if (! count)
         goto err;
     return;
@@ -191,9 +193,13 @@ err:
 free:
     free(hcca);
     free(intr_ed);
-    free(control_ed);
 }
 
+
+/****************************************************************
+ * End point communication
+ ****************************************************************/
+
 static int
 wait_ed(struct ohci_ed *ed)
 {
@@ -210,13 +216,114 @@ wait_ed(struct ohci_ed *ed)
     }
 }
 
+// Wait for next USB frame to start - for ensuring safe memory release.
+static void
+ohci_waittick(struct usb_s *cntl)
+{
+    barrier();
+    struct ohci_hcca *hcca = (void*)cntl->ohci.regs->hcca;
+    u32 startframe = hcca->frame_no;
+    u64 end = calc_future_tsc(1000 * 5);
+    for (;;) {
+        if (hcca->frame_no != startframe)
+            break;
+        if (check_time(end)) {
+            warn_timeout();
+            return;
+        }
+        yield();
+    }
+}
+
+static void
+signal_freelist(struct usb_s *cntl)
+{
+    u32 v = readl(&cntl->ohci.regs->control);
+    if (v & OHCI_CTRL_CLE) {
+        writel(&cntl->ohci.regs->control, v & ~(OHCI_CTRL_CLE|OHCI_CTRL_BLE));
+        ohci_waittick(cntl);
+        writel(&cntl->ohci.regs->ed_controlcurrent, 0);
+        writel(&cntl->ohci.regs->ed_bulkcurrent, 0);
+        writel(&cntl->ohci.regs->control, v);
+    } else {
+        ohci_waittick(cntl);
+    }
+}
+
+struct ohci_pipe {
+    struct ohci_ed ed;
+    struct usb_pipe pipe;
+    void *data;
+    int count;
+    struct ohci_td *tds;
+};
+
+void
+ohci_free_pipe(struct usb_pipe *p)
+{
+    if (! CONFIG_USB_OHCI)
+        return;
+    struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe);
+    u32 endp = pipe->pipe.endp;
+    dprintf(7, "ohci_free_pipe %x\n", endp);
+    struct usb_s *cntl = endp2cntl(endp);
+
+    u32 *pos = &cntl->ohci.regs->ed_controlhead;
+    for (;;) {
+        struct ohci_ed *next = (void*)*pos;
+        if (!next) {
+            // Not found?!  Exit without freeing.
+            warn_internalerror();
+            return;
+        }
+        if (next == &pipe->ed) {
+            *pos = next->hwNextED;
+            signal_freelist(cntl);
+            free(pipe);
+            return;
+        }
+        pos = &next->hwNextED;
+    }
+}
+
+struct usb_pipe *
+ohci_alloc_control_pipe(u32 endp)
+{
+    if (! CONFIG_USB_OHCI)
+        return NULL;
+    struct usb_s *cntl = endp2cntl(endp);
+    dprintf(7, "ohci_alloc_control_pipe %x\n", endp);
+
+    // Allocate a queue head.
+    struct ohci_pipe *pipe = malloc_tmphigh(sizeof(*pipe));
+    if (!pipe) {
+        warn_noalloc();
+        return NULL;
+    }
+    memset(pipe, 0, sizeof(*pipe));
+    pipe->ed.hwINFO = ED_SKIP;
+    pipe->pipe.endp = endp;
+
+    // Add queue head to controller list.
+    pipe->ed.hwNextED = cntl->ohci.regs->ed_controlhead;
+    barrier();
+    cntl->ohci.regs->ed_controlhead = (u32)&pipe->ed;
+    return &pipe->pipe;
+}
+
 int
-ohci_control(u32 endp, int dir, const void *cmd, int cmdsize
+ohci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
              , void *data, int datasize)
 {
     if (! CONFIG_USB_OHCI)
         return -1;
-
+    if (datasize > 4096) {
+        // XXX - should support larger sizes.
+        warn_noalloc();
+        return -1;
+    }
+    struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe);
+    u32 endp = pipe->pipe.endp;
     dprintf(5, "ohci_control %x\n", endp);
     struct usb_s *cntl = endp2cntl(endp);
     int maxpacket = endp2maxsize(endp);
@@ -239,31 +346,22 @@ ohci_control(u32 endp, int dir, const void *cmd, int cmdsize
     tds[2].hwBE = 0;
 
     // Transfer data
-    struct ohci_ed *ed = cntl->ohci.control_ed;
-    ed->hwINFO = ED_SKIP;
+    pipe->ed.hwINFO = ED_SKIP;
     barrier();
-    ed->hwHeadP = (u32)&tds[0];
-    ed->hwTailP = (u32)&tds[3];
+    pipe->ed.hwHeadP = (u32)&tds[0];
+    pipe->ed.hwTailP = (u32)&tds[3];
     barrier();
-    ed->hwINFO = devaddr | (maxpacket << 16) | (lowspeed ? ED_LOWSPEED : 0);
+    pipe->ed.hwINFO = devaddr | (maxpacket << 16) | (lowspeed ? ED_LOWSPEED : 0);
     writel(&cntl->ohci.regs->cmdstatus, OHCI_CLF);
 
-    int ret = wait_ed(ed);
-    ed->hwINFO = ED_SKIP;
+    int ret = wait_ed(&pipe->ed);
+    pipe->ed.hwINFO = ED_SKIP;
     if (ret)
-        usleep(1); // XXX - in case controller still accessing tds
+        ohci_waittick(cntl);
     free(tds);
     return ret;
 }
 
-struct ohci_pipe {
-    struct ohci_ed ed;
-    struct usb_pipe pipe;
-    void *data;
-    int count;
-    struct ohci_td *tds;
-};
-
 struct usb_pipe *
 ohci_alloc_intr_pipe(u32 endp, int frameexp)
 {
@@ -328,17 +426,17 @@ err:
 }
 
 int
-ohci_poll_intr(struct usb_pipe *pipe, void *data)
+ohci_poll_intr(struct usb_pipe *p, void *data)
 {
     ASSERT16();
     if (! CONFIG_USB_OHCI)
         return -1;
 
-    struct ohci_pipe *p = container_of(pipe, struct ohci_pipe, pipe);
-    struct ohci_td *tds = GET_FLATPTR(p->tds);
-    struct ohci_td *head = (void*)GET_FLATPTR(p->ed.hwHeadP);
-    struct ohci_td *tail = (void*)GET_FLATPTR(p->ed.hwTailP);
-    int count = GET_FLATPTR(p->count);
+    struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe);
+    struct ohci_td *tds = GET_FLATPTR(pipe->tds);
+    struct ohci_td *head = (void*)GET_FLATPTR(pipe->ed.hwHeadP);
+    struct ohci_td *tail = (void*)GET_FLATPTR(pipe->ed.hwTailP);
+    int count = GET_FLATPTR(pipe->count);
     int pos = (tail - tds + 1) % count;
     struct ohci_td *next = &tds[pos];
     if (head == next)
@@ -347,9 +445,9 @@ ohci_poll_intr(struct usb_pipe *pipe, void *data)
     // XXX - check for errors.
 
     // Copy data.
-    u32 endp = GET_FLATPTR(p->pipe.endp);
+    u32 endp = GET_FLATPTR(pipe->pipe.endp);
     int maxpacket = endp2maxsize(endp);
-    void *pipedata = GET_FLATPTR(p->data);
+    void *pipedata = GET_FLATPTR(pipe->data);
     void *intrdata = pipedata + maxpacket * pos;
     memcpy_far(GET_SEG(SS), data
                , FLATPTR_TO_SEG(intrdata), (void*)FLATPTR_TO_OFFSET(intrdata)
@@ -362,7 +460,7 @@ ohci_poll_intr(struct usb_pipe *pipe, void *data)
     SET_FLATPTR(tail->hwNextTD, (u32)next);
     SET_FLATPTR(tail->hwBE, (u32)intrdata + maxpacket - 1);
 
-    SET_FLATPTR(p->ed.hwTailP, (u32)next);
+    SET_FLATPTR(pipe->ed.hwTailP, (u32)next);
 
     return 0;
 }
diff --git a/src/usb-ohci.h b/src/usb-ohci.h
index 7ff84f6..fadd396 100644
--- a/src/usb-ohci.h
+++ b/src/usb-ohci.h
@@ -4,7 +4,10 @@
 // usb-ohci.c
 struct usb_s;
 void ohci_init(void *data);
-int ohci_control(u32 endp, int dir, const void *cmd, int cmdsize
+struct usb_pipe;
+void ohci_free_pipe(struct usb_pipe *p);
+struct usb_pipe *ohci_alloc_control_pipe(u32 endp);
+int ohci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
                  , void *data, int datasize);
 struct usb_pipe *ohci_alloc_intr_pipe(u32 endp, int frameexp);
 int ohci_poll_intr(struct usb_pipe *pipe, void *data);
@@ -94,6 +97,7 @@ struct ohci_regs {
 #define OHCI_CTRL_CBSR  (3 << 0)
 #define OHCI_CTRL_PLE   (1 << 2)
 #define OHCI_CTRL_CLE   (1 << 4)
+#define OHCI_CTRL_BLE   (1 << 5)
 #define OHCI_CTRL_HCFS  (3 << 6)
 #       define OHCI_USB_RESET   (0 << 6)
 #       define OHCI_USB_OPER    (2 << 6)
diff --git a/src/usb-uhci.c b/src/usb-uhci.c
index 881e345..d4497bd 100644
--- a/src/usb-uhci.c
+++ b/src/usb-uhci.c
@@ -12,6 +12,12 @@
 #include "pci_regs.h" // PCI_BASE_ADDRESS_4
 #include "usb.h" // struct usb_s
 #include "farptr.h" // GET_FLATPTR
+#include "biosvar.h" // GET_GLOBAL
+
+
+/****************************************************************
+ * Setup
+ ****************************************************************/
 
 static void
 reset_uhci(struct usb_s *cntl)
@@ -37,14 +43,12 @@ configure_uhci(struct usb_s *cntl)
     struct uhci_td *term_td = malloc_high(sizeof(*term_td));
     struct uhci_framelist *fl = memalign_high(sizeof(*fl), sizeof(*fl));
     struct uhci_qh *intr_qh = malloc_high(sizeof(*intr_qh));
-    struct uhci_qh *data_qh = malloc_high(sizeof(*data_qh));
     struct uhci_qh *term_qh = malloc_high(sizeof(*term_qh));
-    if (!term_td || !fl || !intr_qh || !data_qh || !term_qh) {
+    if (!term_td || !fl || !intr_qh || !term_qh) {
         warn_noalloc();
         free(term_td);
         free(fl);
         free(intr_qh);
-        free(data_qh);
         free(term_qh);
         return;
     }
@@ -58,20 +62,15 @@ configure_uhci(struct usb_s *cntl)
     term_qh->element = (u32)term_td;
     term_qh->link = UHCI_PTR_TERM;
 
-    // Setup primary queue head.
-    memset(data_qh, 0, sizeof(*data_qh));
-    data_qh->element = UHCI_PTR_TERM;
-    data_qh->link = (u32)term_qh | UHCI_PTR_QH;
-    cntl->uhci.qh = data_qh;
-
     // Set schedule to point to primary intr queue head
     memset(intr_qh, 0, sizeof(*intr_qh));
     intr_qh->element = UHCI_PTR_TERM;
-    intr_qh->link = (u32)data_qh | UHCI_PTR_QH;
+    intr_qh->link = (u32)term_qh | UHCI_PTR_QH;
     int i;
     for (i=0; i<ARRAY_SIZE(fl->links); i++)
         fl->links[i] = (u32)intr_qh | UHCI_PTR_QH;
     cntl->uhci.framelist = fl;
+    cntl->uhci.control_qh = cntl->uhci.bulk_qh = intr_qh;
     barrier();
 
     // Set the frame length to the default: 1 ms exactly
@@ -162,11 +161,17 @@ uhci_init(void *data)
     start_uhci(cntl);
 
     int count = check_ports(cntl);
+    free_pipe(cntl->defaultpipe);
     if (! count) {
         // XXX - no devices; free data structures.
     }
 }
 
+
+/****************************************************************
+ * End point communication
+ ****************************************************************/
+
 static int
 wait_qh(struct usb_s *cntl, struct uhci_qh *qh)
 {
@@ -188,19 +193,102 @@ wait_qh(struct usb_s *cntl, struct uhci_qh *qh)
     }
 }
 
+// Wait for next USB frame to start - for ensuring safe memory release.
 static void
-uhci_waittick(void)
+uhci_waittick(struct usb_s *cntl)
+{
+    barrier();
+    u16 iobase = GET_GLOBAL(cntl->uhci.iobase);
+    u16 startframe = inw(iobase + USBFRNUM);
+    u64 end = calc_future_tsc(1000 * 5);
+    for (;;) {
+        if (inw(iobase + USBFRNUM) != startframe)
+            break;
+        if (check_time(end)) {
+            warn_timeout();
+            return;
+        }
+        yield();
+    }
+}
+
+struct uhci_pipe {
+    struct uhci_qh qh;
+    struct uhci_td *next_td;
+    struct usb_pipe pipe;
+};
+
+void
+uhci_free_pipe(struct usb_pipe *p)
+{
+    if (! CONFIG_USB_UHCI)
+        return;
+    struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe);
+    u32 endp = pipe->pipe.endp;
+    dprintf(7, "uhci_free_pipe %x\n", endp);
+    struct usb_s *cntl = endp2cntl(endp);
+
+    struct uhci_framelist *fl = cntl->uhci.framelist;
+    struct uhci_qh *pos = (void*)(fl->links[0] & ~UHCI_PTR_BITS);
+    for (;;) {
+        u32 link = pos->link;
+        if (link == UHCI_PTR_TERM) {
+            // Not found?!  Exit without freeing.
+            warn_internalerror();
+            return;
+        }
+        struct uhci_qh *next = (void*)(link & ~UHCI_PTR_BITS);
+        if (next == &pipe->qh) {
+            pos->link = next->link;
+            if (cntl->uhci.control_qh == next)
+                cntl->uhci.control_qh = pos;
+            if (cntl->uhci.bulk_qh == next)
+                cntl->uhci.bulk_qh = pos;
+            uhci_waittick(cntl);
+            free(pipe);
+            return;
+        }
+        pos = next;
+    }
+}
+
+struct usb_pipe *
+uhci_alloc_control_pipe(u32 endp)
 {
-    // XXX - implement real tick detection.
-    msleep(2);
+    if (! CONFIG_USB_UHCI)
+        return NULL;
+    struct usb_s *cntl = endp2cntl(endp);
+    dprintf(7, "uhci_alloc_control_pipe %x\n", endp);
+
+    // Allocate a queue head.
+    struct uhci_pipe *pipe = malloc_tmphigh(sizeof(*pipe));
+    if (!pipe) {
+        warn_noalloc();
+        return NULL;
+    }
+    pipe->qh.element = UHCI_PTR_TERM;
+    pipe->next_td = 0;
+    pipe->pipe.endp = endp;
+
+    // Add queue head to controller list.
+    struct uhci_qh *control_qh = cntl->uhci.control_qh;
+    pipe->qh.link = control_qh->link;
+    barrier();
+    control_qh->link = (u32)&pipe->qh | UHCI_PTR_QH;
+    if (cntl->uhci.bulk_qh == control_qh)
+        cntl->uhci.bulk_qh = &pipe->qh;
+    return &pipe->pipe;
 }
 
 int
-uhci_control(u32 endp, int dir, const void *cmd, int cmdsize
+uhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
              , void *data, int datasize)
 {
+    ASSERT32FLAT();
     if (! CONFIG_USB_UHCI)
         return -1;
+    struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe);
+    u32 endp = pipe->pipe.endp;
 
     dprintf(5, "uhci_control %x\n", endp);
     struct usb_s *cntl = endp2cntl(endp);
@@ -240,13 +328,12 @@ uhci_control(u32 endp, int dir, const void *cmd, int cmdsize
     tds[i].buffer = 0;
 
     // Transfer data
-    struct uhci_qh *data_qh = cntl->uhci.qh;
     barrier();
-    data_qh->element = (u32)&tds[0];
-    int ret = wait_qh(cntl, data_qh);
+    pipe->qh.element = (u32)&tds[0];
+    int ret = wait_qh(cntl, &pipe->qh);
     if (ret) {
-        data_qh->element = UHCI_PTR_TERM;
-        uhci_waittick();
+        pipe->qh.element = UHCI_PTR_TERM;
+        uhci_waittick(cntl);
     }
     free(tds);
     return ret;
@@ -261,22 +348,22 @@ uhci_alloc_bulk_pipe(u32 endp)
     dprintf(7, "uhci_alloc_bulk_pipe %x\n", endp);
 
     // Allocate a queue head.
-    struct uhci_qh *qh = malloc_low(sizeof(*qh));
-    if (!qh) {
+    struct uhci_pipe *pipe = malloc_low(sizeof(*pipe));
+    if (!pipe) {
         warn_noalloc();
         return NULL;
     }
-    qh->element = UHCI_PTR_TERM;
-    qh->next_td = 0;
-    qh->pipe.endp = endp;
+    pipe->qh.element = UHCI_PTR_TERM;
+    pipe->next_td = 0;
+    pipe->pipe.endp = endp;
 
     // Add queue head to controller list.
-    struct uhci_qh *data_qh = cntl->uhci.qh;
-    qh->link = data_qh->link;
+    struct uhci_qh *bulk_qh = cntl->uhci.bulk_qh;
+    pipe->qh.link = bulk_qh->link;
     barrier();
-    data_qh->link = (u32)qh | UHCI_PTR_QH;
+    bulk_qh->link = (u32)&pipe->qh | UHCI_PTR_QH;
 
-    return &qh->pipe;
+    return &pipe->pipe;
 }
 
 static int
@@ -305,16 +392,16 @@ wait_td(struct uhci_td *td)
 #define TDALIGN 16
 
 int
-uhci_send_bulk(struct usb_pipe *pipe, int dir, void *data, int datasize)
+uhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize)
 {
-    struct uhci_qh *qh = container_of(pipe, struct uhci_qh, pipe);
-    u32 endp = GET_FLATPTR(qh->pipe.endp);
+    struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe);
+    u32 endp = GET_FLATPTR(pipe->pipe.endp);
     dprintf(7, "uhci_send_bulk qh=%p endp=%x dir=%d data=%p size=%d\n"
-            , qh, endp, dir, data, datasize);
+            , &pipe->qh, endp, dir, data, datasize);
     int maxpacket = endp2maxsize(endp);
     int lowspeed = endp2speed(endp);
     int devaddr = endp2devaddr(endp) | (endp2ep(endp) << 7);
-    int toggle = (u32)GET_FLATPTR(qh->next_td); // XXX
+    int toggle = (u32)GET_FLATPTR(pipe->next_td); // XXX
 
     // Allocate 4 tds on stack (16byte aligned)
     u8 tdsbuf[sizeof(struct uhci_td) * STACKTDS + TDALIGN - 1];
@@ -322,7 +409,7 @@ uhci_send_bulk(struct usb_pipe *pipe, int dir, void *data, int datasize)
     memset(tds, 0, sizeof(*tds) * STACKTDS);
 
     // Enable tds
-    SET_FLATPTR(qh->element, (u32)MAKE_FLATPTR(GET_SEG(SS), tds));
+    SET_FLATPTR(pipe->qh.element, (u32)MAKE_FLATPTR(GET_SEG(SS), tds));
 
     int tdpos = 0;
     while (datasize) {
@@ -357,12 +444,12 @@ uhci_send_bulk(struct usb_pipe *pipe, int dir, void *data, int datasize)
             goto fail;
     }
 
-    SET_FLATPTR(qh->next_td, (void*)toggle); // XXX
+    SET_FLATPTR(pipe->next_td, (void*)toggle); // XXX
     return 0;
 fail:
     dprintf(1, "uhci_send_bulk failed\n");
-    SET_FLATPTR(qh->element, UHCI_PTR_TERM);
-    uhci_waittick();
+    SET_FLATPTR(pipe->qh.element, UHCI_PTR_TERM);
+    uhci_waittick(endp2cntl(endp));
     return -1;
 }
 
@@ -382,15 +469,15 @@ uhci_alloc_intr_pipe(u32 endp, int frameexp)
     // Determine number of entries needed for 2 timer ticks.
     int ms = 1<<frameexp;
     int count = DIV_ROUND_UP(PIT_TICK_INTERVAL * 1000 * 2, PIT_TICK_RATE * ms);
-    struct uhci_qh *qh = malloc_low(sizeof(*qh));
+    struct uhci_pipe *pipe = malloc_low(sizeof(*pipe));
     struct uhci_td *tds = malloc_low(sizeof(*tds) * count);
-    if (!qh || !tds) {
+    if (!pipe || !tds) {
         warn_noalloc();
         goto fail;
     }
     if (maxpacket > sizeof(tds[0].data))
         goto fail;
-    qh->element = (u32)tds;
+    pipe->qh.element = (u32)tds;
     int toggle = 0;
     int i;
     for (i=0; i<count; i++) {
@@ -404,41 +491,45 @@ uhci_alloc_intr_pipe(u32 endp, int frameexp)
         toggle ^= TD_TOKEN_TOGGLE;
     }
 
-    qh->next_td = &tds[0];
-    qh->pipe.endp = endp;
+    pipe->next_td = &tds[0];
+    pipe->pipe.endp = endp;
 
     // Add to interrupt schedule.
     struct uhci_framelist *fl = cntl->uhci.framelist;
     if (frameexp == 0) {
         // Add to existing interrupt entry.
         struct uhci_qh *intr_qh = (void*)(fl->links[0] & ~UHCI_PTR_BITS);
-        qh->link = intr_qh->link;
+        pipe->qh.link = intr_qh->link;
         barrier();
-        intr_qh->link = (u32)qh | UHCI_PTR_QH;
+        intr_qh->link = (u32)&pipe->qh | UHCI_PTR_QH;
+        if (cntl->uhci.control_qh == intr_qh)
+            cntl->uhci.control_qh = &pipe->qh;
+        if (cntl->uhci.bulk_qh == intr_qh)
+            cntl->uhci.bulk_qh = &pipe->qh;
     } else {
         int startpos = 1<<(frameexp-1);
-        qh->link = fl->links[startpos];
+        pipe->qh.link = fl->links[startpos];
         barrier();
         for (i=startpos; i<ARRAY_SIZE(fl->links); i+=ms)
-            fl->links[i] = (u32)qh | UHCI_PTR_QH;
+            fl->links[i] = (u32)&pipe->qh | UHCI_PTR_QH;
     }
 
-    return &qh->pipe;
+    return &pipe->pipe;
 fail:
-    free(qh);
+    free(pipe);
     free(tds);
     return NULL;
 }
 
 int
-uhci_poll_intr(struct usb_pipe *pipe, void *data)
+uhci_poll_intr(struct usb_pipe *p, void *data)
 {
     ASSERT16();
     if (! CONFIG_USB_UHCI)
         return -1;
 
-    struct uhci_qh *qh = container_of(pipe, struct uhci_qh, pipe);
-    struct uhci_td *td = GET_FLATPTR(qh->next_td);
+    struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe);
+    struct uhci_td *td = GET_FLATPTR(pipe->next_td);
     u32 status = GET_FLATPTR(td->status);
     u32 token = GET_FLATPTR(td->token);
     if (status & TD_CTRL_ACTIVE)
@@ -456,7 +547,7 @@ uhci_poll_intr(struct usb_pipe *pipe, void *data)
     barrier();
     SET_FLATPTR(td->status, (uhci_maxerr(0) | (status & TD_CTRL_LS)
                              | TD_CTRL_ACTIVE));
-    SET_FLATPTR(qh->next_td, (void*)(next & ~UHCI_PTR_BITS));
+    SET_FLATPTR(pipe->next_td, (void*)(next & ~UHCI_PTR_BITS));
 
     return 0;
 }
diff --git a/src/usb-uhci.h b/src/usb-uhci.h
index d7ebb4e..8dbee9c 100644
--- a/src/usb-uhci.h
+++ b/src/usb-uhci.h
@@ -1,12 +1,12 @@
 #ifndef __USB_UHCI_H
 #define __USB_UHCI_H
 
-#include "usb.h" // struct usb_pipe
-
 // usb-uhci.c
-struct usb_s;
 void uhci_init(void *data);
-int uhci_control(u32 endp, int dir, const void *cmd, int cmdsize
+struct usb_pipe;
+void uhci_free_pipe(struct usb_pipe *pipe);
+struct usb_pipe *uhci_alloc_control_pipe(u32 endp);
+int uhci_control(struct usb_pipe *pipe, int dir, const void *cmd, int cmdsize
                  , void *data, int datasize);
 struct usb_pipe *uhci_alloc_bulk_pipe(u32 endp);
 int uhci_send_bulk(struct usb_pipe *pipe, int dir, void *data, int datasize);
@@ -120,10 +120,6 @@ struct uhci_td {
 struct uhci_qh {
     u32 link;
     u32 element;
-
-    // Software fields
-    struct uhci_td *next_td;
-    struct usb_pipe pipe;
 } PACKED;
 
 #define UHCI_PTR_BITS           0x000F
diff --git a/src/usb.c b/src/usb.c
index cc3b201..5b3649c 100644
--- a/src/usb.c
+++ b/src/usb.c
@@ -24,18 +24,50 @@ struct usb_s USBControllers[16] VAR16VISIBLE;
  * Controller function wrappers
  ****************************************************************/
 
+// Free an allocated control or bulk pipe.
+void
+free_pipe(struct usb_pipe *pipe)
+{
+    ASSERT32FLAT();
+    if (!pipe)
+        return;
+    struct usb_s *cntl = endp2cntl(pipe->endp);
+    switch (cntl->type) {
+    default:
+    case USB_TYPE_UHCI:
+        return uhci_free_pipe(pipe);
+    case USB_TYPE_OHCI:
+        return ohci_free_pipe(pipe);
+    }
+}
+
+// Allocate a control pipe (which can only be used by 32bit code)
+static struct usb_pipe *
+alloc_control_pipe(u32 endp)
+{
+    struct usb_s *cntl = endp2cntl(endp);
+    switch (cntl->type) {
+    default:
+    case USB_TYPE_UHCI:
+        return uhci_alloc_control_pipe(endp);
+    case USB_TYPE_OHCI:
+        return ohci_alloc_control_pipe(endp);
+    }
+}
+
 // Send a message on a control pipe using the default control descriptor.
 static int
-send_control(u32 endp, int dir, const void *cmd, int cmdsize
+send_control(struct usb_pipe *pipe, int dir, const void *cmd, int cmdsize
              , void *data, int datasize)
 {
-    struct usb_s *cntl = endp2cntl(endp);
+    ASSERT32FLAT();
+    struct usb_s *cntl = endp2cntl(pipe->endp);
     switch (cntl->type) {
     default:
     case USB_TYPE_UHCI:
-        return uhci_control(endp, dir, cmd, cmdsize, data, datasize);
+        return uhci_control(pipe, dir, cmd, cmdsize, data, datasize);
     case USB_TYPE_OHCI:
-        return ohci_control(endp, dir, cmd, cmdsize, data, datasize);
+        return ohci_control(pipe, dir, cmd, cmdsize, data, datasize);
     }
 }
 
@@ -121,10 +153,18 @@ findEndPointDesc(struct usb_interface_descriptor *iface, int imax
     }
 }
 
+// Change endpoint characteristics of the default control pipe.
+static void
+usb_alter_control(struct usb_pipe *pipe, u32 endp)
+{
+    pipe->endp = endp;
+}
+
 // Build an encoded "endp" from an endpoint descriptor.
 u32
-mkendpFromDesc(u32 endp, struct usb_endpoint_descriptor *epdesc)
+mkendpFromDesc(struct usb_pipe *pipe, struct usb_endpoint_descriptor *epdesc)
 {
+    u32 endp = pipe->endp;
     return mkendp(endp2cntl(endp), endp2devaddr(endp)
                   , epdesc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK
                   , endp2speed(endp), epdesc->wMaxPacketSize);
@@ -132,15 +172,16 @@ mkendpFromDesc(u32 endp, struct usb_endpoint_descriptor *epdesc)
 
 // Send a message to the default control pipe of a device.
 int
-send_default_control(u32 endp, const struct usb_ctrlrequest *req, void *data)
+send_default_control(struct usb_pipe *pipe, const struct usb_ctrlrequest *req
+                     , void *data)
 {
-    return send_control(endp, req->bRequestType & USB_DIR_IN
+    return send_control(pipe, req->bRequestType & USB_DIR_IN
                         , req, sizeof(*req), data, req->wLength);
 }
 
 // Get the first 8 bytes of the device descriptor.
 static int
-get_device_info8(struct usb_device_descriptor *dinfo, u32 endp)
+get_device_info8(struct usb_pipe *pipe, struct usb_device_descriptor *dinfo)
 {
     struct usb_ctrlrequest req;
     req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
@@ -148,11 +189,11 @@ get_device_info8(struct usb_device_descriptor *dinfo, u32 endp)
     req.wValue = USB_DT_DEVICE<<8;
     req.wIndex = 0;
     req.wLength = 8;
-    return send_default_control(endp, &req, dinfo);
+    return send_default_control(pipe, &req, dinfo);
 }
 
 static struct usb_config_descriptor *
-get_device_config(u32 endp)
+get_device_config(struct usb_pipe *pipe)
 {
     struct usb_config_descriptor cfg;
 
@@ -162,7 +203,7 @@ get_device_config(u32 endp)
     req.wValue = USB_DT_CONFIG<<8;
     req.wIndex = 0;
     req.wLength = sizeof(cfg);
-    int ret = send_default_control(endp, &req, &cfg);
+    int ret = send_default_control(pipe, &req, &cfg);
     if (ret)
         return NULL;
 
@@ -170,18 +211,19 @@ get_device_config(u32 endp)
     if (!config)
         return NULL;
     req.wLength = cfg.wTotalLength;
-    ret = send_default_control(endp, &req, config);
+    ret = send_default_control(pipe, &req, config);
     if (ret)
         return NULL;
     //hexdump(config, cfg.wTotalLength);
     return config;
 }
 
-static u32
-set_address(u32 endp)
+static struct usb_pipe *
+set_address(struct usb_pipe *pipe)
 {
-    dprintf(3, "set_address %x\n", endp);
-    struct usb_s *cntl = endp2cntl(endp);
+    ASSERT32FLAT();
+    dprintf(3, "set_address %x\n", pipe->endp);
+    struct usb_s *cntl = endp2cntl(pipe->endp);
     if (cntl->maxaddr >= USB_MAXADDR)
         return 0;
 
@@ -191,17 +233,19 @@ set_address(u32 endp)
     req.wValue = cntl->maxaddr + 1;
     req.wIndex = 0;
     req.wLength = 0;
-    int ret = send_default_control(endp, &req, NULL);
+    int ret = send_default_control(pipe, &req, NULL);
     if (ret)
         return 0;
     msleep(USB_TIME_SETADDR_RECOVERY);
 
     cntl->maxaddr++;
-    return mkendp(cntl, cntl->maxaddr, 0, endp2speed(endp), endp2maxsize(endp));
+    u32 endp = mkendp(cntl, cntl->maxaddr, 0
+                      , endp2speed(pipe->endp), endp2maxsize(pipe->endp));
+    return alloc_control_pipe(endp);
 }
 
 static int
-set_configuration(u32 endp, u16 val)
+set_configuration(struct usb_pipe *pipe, u16 val)
 {
     struct usb_ctrlrequest req;
     req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
@@ -209,7 +253,7 @@ set_configuration(u32 endp, u16 val)
     req.wValue = val;
     req.wIndex = 0;
     req.wLength = 0;
-    return send_default_control(endp, &req, NULL);
+    return send_default_control(pipe, &req, NULL);
 }
 
 
@@ -222,12 +266,20 @@ set_configuration(u32 endp, u16 val)
 int
 configure_usb_device(struct usb_s *cntl, int lowspeed)
 {
+    ASSERT32FLAT();
     dprintf(3, "config_usb: %p %d\n", cntl, lowspeed);
 
     // 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;
+    }
+    usb_alter_control(defpipe, endp);
     struct usb_device_descriptor dinfo;
-    int ret = get_device_info8(&dinfo, endp);
+    int ret = get_device_info8(defpipe, &dinfo);
     if (ret)
         return 0;
     dprintf(3, "device rev=%04x cls=%02x sub=%02x proto=%02x size=%02x\n"
@@ -236,9 +288,11 @@ configure_usb_device(struct usb_s *cntl, int lowspeed)
     if (dinfo.bMaxPacketSize0 < 8 || dinfo.bMaxPacketSize0 > 64)
         return 0;
     endp = mkendp(cntl, 0, 0, lowspeed, dinfo.bMaxPacketSize0);
+    usb_alter_control(defpipe, endp);
 
     // Get configuration
-    struct usb_config_descriptor *config = get_device_config(endp);
+    struct usb_pipe *pipe = NULL;
+    struct usb_config_descriptor *config = get_device_config(defpipe);
     if (!config)
         return 0;
 
@@ -254,26 +308,25 @@ configure_usb_device(struct usb_s *cntl, int lowspeed)
         goto fail;
 
     // Set the address and configure device.
-    endp = set_address(endp);
-    if (!endp)
+    pipe = set_address(defpipe);
+    if (!pipe)
         goto fail;
-    ret = set_configuration(endp, config->bConfigurationValue);
+    ret = set_configuration(pipe, config->bConfigurationValue);
     if (ret)
         goto fail;
 
     // Configure driver.
-    if (iface->bInterfaceClass == USB_CLASS_HUB) {
-        free(config);
-        return usb_hub_init(endp);
-    }
     int imax = (void*)config + config->wTotalLength - (void*)iface;
-    if (iface->bInterfaceClass == USB_CLASS_MASS_STORAGE)
-        ret = usb_msc_init(endp, iface, imax);
+    if (iface->bInterfaceClass == USB_CLASS_HUB)
+        ret = usb_hub_init(pipe);
+    else if (iface->bInterfaceClass == USB_CLASS_MASS_STORAGE)
+        ret = usb_msc_init(pipe, iface, imax);
     else
-        ret = usb_keyboard_init(endp, iface, imax);
+        ret = usb_keyboard_init(pipe, iface, imax);
     if (ret)
         goto fail;
 
+    free_pipe(pipe);
     free(config);
     return 1;
 fail:
@@ -290,6 +343,7 @@ usb_setup(void)
 
     dprintf(3, "init usb\n");
 
+    memset(&USBControllers, 0, sizeof(USBControllers));
     usb_keyboard_setup();
 
     // Look for USB controllers
diff --git a/src/usb.h b/src/usb.h
index d947891..0f65e39 100644
--- a/src/usb.h
+++ b/src/usb.h
@@ -2,20 +2,24 @@
 #ifndef __USB_H
 #define __USB_H
 
+struct usb_pipe {
+    u32 endp;
+};
+
 // Local information for a usb controller.
 struct usb_s {
     u8 type;
     u8 maxaddr;
     u16 bdf;
+    struct usb_pipe *defaultpipe;
 
     union {
         struct {
             u16 iobase;
-            void *qh, *framelist;
+            void *control_qh, *bulk_qh, *framelist;
         } uhci;
         struct {
             struct ohci_regs *regs;
-            void *control_ed;
         } ohci;
     };
 };
@@ -25,26 +29,24 @@ struct usb_s {
 
 extern struct usb_s USBControllers[];
 
-struct usb_pipe {
-    u32 endp;
-};
-
 #define USB_MAXADDR 127
 
 // usb.c
 void usb_setup(void);
 int configure_usb_device(struct usb_s *cntl, int lowspeed);
 struct usb_ctrlrequest;
-int send_default_control(u32 endp, const struct usb_ctrlrequest *req
+int send_default_control(struct usb_pipe *pipe, const struct usb_ctrlrequest *req
                          , void *data);
 int usb_send_bulk(struct usb_pipe *pipe, int dir, void *data, int datasize);
+void free_pipe(struct usb_pipe *pipe);
 struct usb_pipe *alloc_bulk_pipe(u32 endp);
 struct usb_pipe *alloc_intr_pipe(u32 endp, int period);
 int usb_poll_intr(struct usb_pipe *pipe, void *data);
 struct usb_interface_descriptor;
 struct usb_endpoint_descriptor *findEndPointDesc(
     struct usb_interface_descriptor *iface, int imax, int type, int dir);
-u32 mkendpFromDesc(u32 endp, struct usb_endpoint_descriptor *epdesc);
+u32 mkendpFromDesc(struct usb_pipe *pipe
+                   , struct usb_endpoint_descriptor *epdesc);
 
 
 /****************************************************************
-- 
1.6.6.1




More information about the SeaBIOS mailing list