[SeaBIOS] [PATCH] usb: Add support for OHCI bulk transfers

Kevin O'Connor kevin at koconnor.net
Mon Dec 29 20:30:05 CET 2014


Support bulk transfers (usb drives) on OHCI USB controllers.  Now that
there is support for 32bit only drive controllers, it is not that hard
to support disks on OHCI controllers, and it may be useful for some
old hardware.

Signed-off-by: Kevin O'Connor <kevin at koconnor.net>
--

This patch (along with the previous two patch series) is also available at:
  https://github.com/KevinOConnor/seabios/tree/testing

---
 src/hw/usb-ohci.c | 88 ++++++++++++++++++++++++++++++++++++++++++++-----------
 src/hw/usb-ohci.h |  1 +
 src/hw/usb.c      |  5 +++-
 3 files changed, 76 insertions(+), 18 deletions(-)

diff --git a/src/hw/usb-ohci.c b/src/hw/usb-ohci.c
index 4789768..cc8be7d 100644
--- a/src/hw/usb-ohci.c
+++ b/src/hw/usb-ohci.c
@@ -7,6 +7,7 @@
 #include "biosvar.h" // GET_LOWFLAT
 #include "config.h" // CONFIG_*
 #include "malloc.h" // free
+#include "memmap.h" // PAGE_SIZE
 #include "output.h" // dprintf
 #include "pci.h" // pci_bdf_to_bus
 #include "pci_ids.h" // PCI_CLASS_SERIAL_USB_OHCI
@@ -27,6 +28,7 @@ struct usb_ohci_s {
 struct ohci_pipe {
     struct ohci_ed ed;
     struct usb_pipe pipe;
+    struct ohci_regs *regs;
     void *data;
     int count;
     struct ohci_td *tds;
@@ -118,10 +120,10 @@ check_ohci_ports(struct usb_ohci_s *cntl)
 
 // Wait for next USB frame to start - for ensuring safe memory release.
 static void
-ohci_waittick(struct usb_ohci_s *cntl)
+ohci_waittick(struct ohci_regs *regs)
 {
     barrier();
-    struct ohci_hcca *hcca = (void*)cntl->regs->hcca;
+    struct ohci_hcca *hcca = (void*)regs->hcca;
     u32 startframe = hcca->frame_no;
     u32 end = timer_calc(1000 * 5);
     for (;;) {
@@ -143,7 +145,7 @@ ohci_free_pipes(struct usb_ohci_s *cntl)
     u32 creg = readl(&cntl->regs->control);
     if (creg & (OHCI_CTRL_CLE|OHCI_CTRL_BLE)) {
         writel(&cntl->regs->control, creg & ~(OHCI_CTRL_CLE|OHCI_CTRL_BLE));
-        ohci_waittick(cntl);
+        ohci_waittick(cntl->regs);
     }
 
     u32 *pos = &cntl->regs->ed_controlhead;
@@ -209,7 +211,7 @@ start_ohci(struct usb_ohci_s *cntl, struct ohci_hcca *hcca)
 
     // Go into operational state
     writel(&cntl->regs->control
-           , (OHCI_CTRL_CBSR | OHCI_CTRL_CLE | OHCI_CTRL_PLE
+           , (OHCI_CTRL_CBSR | OHCI_CTRL_CLE | OHCI_CTRL_BLE | OHCI_CTRL_PLE
               | OHCI_USB_OPER | oldrwc));
     readl(&cntl->regs->control); // flush writes
 
@@ -320,6 +322,9 @@ ohci_desc2pipe(struct ohci_pipe *pipe, struct usbdevice_s *usbdev
     pipe->ed.hwINFO = (ED_SKIP | usbdev->devaddr | (pipe->pipe.ep << 7)
                        | (epdesc->wMaxPacketSize << 16)
                        | (usbdev->speed ? ED_LOWSPEED : 0));
+    struct usb_ohci_s *cntl = container_of(
+        usbdev->hub->cntl, struct usb_ohci_s, usb);
+    pipe->regs = cntl->regs;
 }
 
 static struct usb_pipe *
@@ -398,10 +403,6 @@ ohci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe
     u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
     if (eptype == USB_ENDPOINT_XFER_INT)
         return ohci_alloc_intr_pipe(usbdev, epdesc);
-    if (eptype != USB_ENDPOINT_XFER_CONTROL) {
-        dprintf(1, "OHCI Bulk transfers not supported.\n");
-        return NULL;
-    }
     struct usb_ohci_s *cntl = container_of(
         usbdev->hub->cntl, struct usb_ohci_s, usb);
     dprintf(7, "ohci_alloc_async_pipe %p\n", &cntl->usb);
@@ -415,7 +416,11 @@ ohci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe
     }
 
     // Allocate a new queue head.
-    struct ohci_pipe *pipe = malloc_tmphigh(sizeof(*pipe));
+    struct ohci_pipe *pipe;
+    if (eptype == USB_ENDPOINT_XFER_CONTROL)
+        pipe = malloc_tmphigh(sizeof(*pipe));
+    else
+        pipe = malloc_low(sizeof(*pipe));
     if (!pipe) {
         warn_noalloc();
         return NULL;
@@ -424,9 +429,12 @@ ohci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe
     ohci_desc2pipe(pipe, usbdev, epdesc);
 
     // Add queue head to controller list.
-    pipe->ed.hwNextED = cntl->regs->ed_controlhead;
+    u32 *head = &cntl->regs->ed_controlhead;
+    if (eptype != USB_ENDPOINT_XFER_CONTROL)
+        head = &cntl->regs->ed_bulkhead;
+    pipe->ed.hwNextED = *head;
     barrier();
-    cntl->regs->ed_controlhead = (u32)&pipe->ed;
+    *head = (u32)&pipe->ed;
     return &pipe->pipe;
 }
 
@@ -435,10 +443,12 @@ wait_ed(struct ohci_ed *ed, int timeout)
 {
     u32 end = timer_calc(timeout);
     for (;;) {
-        if (ed->hwHeadP == ed->hwTailP)
+        if ((ed->hwHeadP & ~(ED_C|ED_H)) == ed->hwTailP)
             return 0;
         if (timer_check(end)) {
             warn_timeout();
+            dprintf(1, "ohci ed info=%x tail=%x head=%x next=%x\n"
+                    , ed->hwINFO, ed->hwTailP, ed->hwHeadP, ed->hwNextED);
             return -1;
         }
         yield();
@@ -458,8 +468,6 @@ ohci_send_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
         return -1;
     }
     struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe);
-    struct usb_ohci_s *cntl = container_of(
-        pipe->pipe.cntl, struct usb_ohci_s, usb);
 
     // Setup transfer descriptors
     struct ohci_td *tds = malloc_tmphigh(sizeof(*tds) * 3);
@@ -491,20 +499,66 @@ ohci_send_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
     pipe->ed.hwTailP = (u32)td;
     barrier();
     pipe->ed.hwINFO &= ~ED_SKIP;
-    writel(&cntl->regs->cmdstatus, OHCI_CLF);
+    writel(&pipe->regs->cmdstatus, OHCI_CLF);
 
     int ret = wait_ed(&pipe->ed, usb_xfer_time(p, datasize));
     pipe->ed.hwINFO |= ED_SKIP;
     if (ret)
-        ohci_waittick(cntl);
+        ohci_waittick(pipe->regs);
     free(tds);
     return ret;
 }
 
+#define STACKOTDS 16
+#define OHCI_TD_ALIGN 16
+
 int
 ohci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize)
 {
-    return -1;
+    ASSERT32FLAT();
+    if (! CONFIG_USB_OHCI)
+        return -1;
+    dprintf(5, "ohci_send_bulk %p\n", p);
+    struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe);
+
+    // Allocate 16 tds on stack (with required alignment)
+    u8 tdsbuf[sizeof(struct ohci_td) * STACKOTDS + OHCI_TD_ALIGN - 1];
+    struct ohci_td *tds = (void*)ALIGN((u32)tdsbuf, OHCI_TD_ALIGN);
+    memset(tds, 0, sizeof(*tds) * STACKOTDS);
+
+    // Setup transfer descriptors
+    struct ohci_td *td = tds;
+    u16 maxpacket = pipe->pipe.maxpacket;
+    u32 dest = (u32)data, end = dest + datasize - 1;
+    while (dest <= end) {
+        if (td >= &tds[STACKOTDS]) {
+            warn_noalloc();
+            return -1;
+        }
+        u32 tend = end;
+        if ((tend / PAGE_SIZE) - (dest / PAGE_SIZE) > 1)
+            // Transfer must not span two pages in single td
+            tend = dest+2*PAGE_SIZE - ALIGN(dest & (PAGE_SIZE-1), maxpacket) - 1;
+        td->hwINFO = (dir ? TD_DP_IN : TD_DP_OUT) | TD_CC;
+        td->hwCBP = dest;
+        td->hwNextTD = (u32)&td[1];
+        td->hwBE = tend;
+        td++;
+        dest = tend+1;
+    }
+
+    // Transfer data
+    pipe->ed.hwHeadP = (u32)tds | (pipe->ed.hwHeadP & ED_C);
+    pipe->ed.hwTailP = (u32)td;
+    barrier();
+    pipe->ed.hwINFO &= ~ED_SKIP;
+    writel(&pipe->regs->cmdstatus, OHCI_BLF);
+
+    int ret = wait_ed(&pipe->ed, usb_xfer_time(p, datasize));
+    pipe->ed.hwINFO |= ED_SKIP;
+    if (ret)
+        ohci_waittick(pipe->regs);
+    return ret;
 }
 
 int
diff --git a/src/hw/usb-ohci.h b/src/hw/usb-ohci.h
index 5699523..db935ca 100644
--- a/src/hw/usb-ohci.h
+++ b/src/hw/usb-ohci.h
@@ -107,6 +107,7 @@ struct ohci_regs {
 
 #define OHCI_HCR        (1 << 0)
 #define OHCI_CLF        (1 << 1)
+#define OHCI_BLF        (1 << 2)
 
 #define OHCI_INTR_MIE   (1 << 31)
 
diff --git a/src/hw/usb.c b/src/hw/usb.c
index bb646a7..75412f9 100644
--- a/src/hw/usb.c
+++ b/src/hw/usb.c
@@ -71,6 +71,8 @@ usb_send_bulk(struct usb_pipe *pipe_fl, int dir, void *data, int datasize)
     case USB_TYPE_UHCI:
         return uhci_send_bulk(pipe_fl, dir, data, datasize);
     case USB_TYPE_OHCI:
+        if (MODESEGMENT)
+            return -1;
         return ohci_send_bulk(pipe_fl, dir, data, datasize);
     case USB_TYPE_EHCI:
         return ehci_send_bulk(pipe_fl, dir, data, datasize);
@@ -102,7 +104,8 @@ usb_poll_intr(struct usb_pipe *pipe_fl, void *data)
 
 int usb_32bit_pipe(struct usb_pipe *pipe_fl)
 {
-    return CONFIG_USB_XHCI && GET_LOWFLAT(pipe_fl->type) == USB_TYPE_XHCI;
+    return (CONFIG_USB_XHCI && GET_LOWFLAT(pipe_fl->type) == USB_TYPE_XHCI)
+        || (CONFIG_USB_OHCI && GET_LOWFLAT(pipe_fl->type) == USB_TYPE_OHCI);
 }
 
 
-- 
1.9.3




More information about the SeaBIOS mailing list