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@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); }
On Mon, Dec 29, 2014 at 02:30:05PM -0500, Kevin O'Connor wrote:
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.
It works* on a Nvidia CK804 OHCI for the handful of flash drives I tried.
[*] the finnix and debian wheezy usb flash boot images i tried failed to transition to a running kernel however, not sure how related this is. NetBSD's bootloader managed to load and run a kernel.
Jonathan Kollasch
On Mon, Dec 29, 2014 at 02:30:05PM -0500, Kevin O'Connor wrote:
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.
FYI, I committed this series as well as the previous two series (that limited transfer sizes to 64K).
-Kevin
On Di, 2015-01-06 at 10:12 -0500, Kevin O'Connor wrote:
On Mon, Dec 29, 2014 at 02:30:05PM -0500, Kevin O'Connor wrote:
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.
FYI, I committed this series as well as the previous two series (that limited transfer sizes to 64K).
That seems to have made seabios hit the 128k limit again. Turned off ohci support for the 128k bios as qemu by default uses uhci for usb 1.1 support. Kicked jenkins rebuild.
cheers, Gerd
On Wed, Jan 07, 2015 at 03:55:10PM +0100, Gerd Hoffmann wrote:
On Di, 2015-01-06 at 10:12 -0500, Kevin O'Connor wrote:
On Mon, Dec 29, 2014 at 02:30:05PM -0500, Kevin O'Connor wrote:
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.
FYI, I committed this series as well as the previous two series (that limited transfer sizes to 64K).
That seems to have made seabios hit the 128k limit again. Turned off ohci support for the 128k bios as qemu by default uses uhci for usb 1.1 support. Kicked jenkins rebuild.
Thanks.
BTW, the USB patch series I have at:
https://github.com/KevinOConnor/seabios/tree/testing
trims the final binary by a few hundred bytes. So, it may fit again in a few days.
-Kevin