diff options
-rw-r--r-- | src/hw/usb-ohci.c | 88 | ||||
-rw-r--r-- | src/hw/usb-ohci.h | 1 | ||||
-rw-r--r-- | 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..ee31b83 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(7, "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), *td = tds; + memset(tds, 0, sizeof(*tds) * STACKOTDS); + + // Setup transfer descriptors + u16 maxpacket = pipe->pipe.maxpacket; + u32 dest = (u32)data, dataend = dest + datasize; + while (dest < dataend) { + // Send data pids + if (td >= &tds[STACKOTDS]) { + warn_noalloc(); + return -1; + } + int maxtransfer = 2*PAGE_SIZE - (dest & (PAGE_SIZE-1)); + int transfer = dataend - dest; + if (transfer > maxtransfer) + transfer = ALIGN_DOWN(maxtransfer, maxpacket); + td->hwINFO = (dir ? TD_DP_IN : TD_DP_OUT) | TD_CC; + td->hwCBP = dest; + td->hwNextTD = (u32)&td[1]; + td->hwBE = dest + transfer - 1; + td++; + dest += transfer; + } + + // 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); } |