aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/hw/usb-ohci.c88
-rw-r--r--src/hw/usb-ohci.h1
-rw-r--r--src/hw/usb.c5
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);
}