This patch set adds support for high-speed USB (EHCI) controllers. I've tested this on my epia-cn machine. The EHCI support noticeably decreases the time required to boot from a USB drive.
This support includes the code necessary to hand off full/low speed devices to "companion controllers" (both uhci and ohci). It also supports full/low speed devices attached to high-speed hubs.
This is an initial implementation, but I think it's ready for more wide spread testing. Feedback is welcome.
-Kevin
Kevin O'Connor (5): Dynamically allocate USB controller structures. Reduce size of USB 'struct uhci_td'. Minor - USB OHCI interrupt queue should be one larger. Some USB UHCI and OHCI fixes and cleanups. Add USB EHCI controller support.
Makefile | 2 +- src/config.h | 2 + src/usb-ehci.c | 751 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/usb-ehci.h | 173 +++++++++++++ src/usb-hub.c | 3 +- src/usb-hub.h | 2 + src/usb-ohci.c | 232 ++++++++++-------- src/usb-ohci.h | 3 +- src/usb-uhci.c | 214 +++++++++------- src/usb-uhci.h | 5 +- src/usb.c | 82 +++++-- src/usb.h | 31 +-- 12 files changed, 1261 insertions(+), 239 deletions(-) create mode 100644 src/usb-ehci.c create mode 100644 src/usb-ehci.h
Have each controller type allocate its own data structure.
Also, implement freeing of unused uhci controller structures. --- src/usb-ohci.c | 175 +++++++++++++++++++++++++++++++------------------------- src/usb-ohci.h | 3 +- src/usb-uhci.c | 172 ++++++++++++++++++++++++++++++------------------------- src/usb-uhci.h | 2 +- src/usb.c | 12 +--- src/usb.h | 20 +----- 6 files changed, 199 insertions(+), 185 deletions(-)
diff --git a/src/usb-ohci.c b/src/usb-ohci.c index d825964..28bbdc6 100644 --- a/src/usb-ohci.c +++ b/src/usb-ohci.c @@ -15,6 +15,11 @@
#define FIT (1 << 31)
+struct usb_ohci_s { + struct usb_s usb; + struct ohci_regs *regs; +}; +
/**************************************************************** * Root hub @@ -25,8 +30,9 @@ init_ohci_port(void *data) { struct usbhub_s *hub = data; u32 port = hub->port; // XXX - find better way to pass port + struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb);
- u32 sts = readl(&hub->cntl->ohci.regs->roothub_portstatus[port]); + u32 sts = readl(&cntl->regs->roothub_portstatus[port]); if (!(sts & RH_PS_CCS)) // No device. goto done; @@ -34,11 +40,11 @@ init_ohci_port(void *data) // XXX - need to wait for USB_TIME_ATTDB if just powered up?
// Signal reset - mutex_lock(&hub->cntl->resetlock); - writel(&hub->cntl->ohci.regs->roothub_portstatus[port], RH_PS_PRS); + mutex_lock(&cntl->usb.resetlock); + writel(&cntl->regs->roothub_portstatus[port], RH_PS_PRS); u64 end = calc_future_tsc(USB_TIME_DRSTR * 2); for (;;) { - sts = readl(&hub->cntl->ohci.regs->roothub_portstatus[port]); + sts = readl(&cntl->regs->roothub_portstatus[port]); if (!(sts & RH_PS_PRS)) // XXX - need to ensure USB_TIME_DRSTR time in reset? break; @@ -55,18 +61,17 @@ init_ohci_port(void *data) goto resetfail;
// Set address of port - struct usb_pipe *pipe = usb_set_address(hub->cntl, !!(sts & RH_PS_LSDA)); + struct usb_pipe *pipe = usb_set_address(&cntl->usb, !!(sts & RH_PS_LSDA)); if (!pipe) goto resetfail; - mutex_unlock(&hub->cntl->resetlock); + mutex_unlock(&cntl->usb.resetlock);
// Configure the device int count = configure_usb_device(pipe); free_pipe(pipe); if (! count) // Shutdown port - writel(&hub->cntl->ohci.regs->roothub_portstatus[port] - , RH_PS_CCS|RH_PS_LSDA); + writel(&cntl->regs->roothub_portstatus[port], RH_PS_CCS|RH_PS_LSDA); hub->devcount += count; done: hub->threads--; @@ -74,29 +79,28 @@ done:
resetfail: // Shutdown port - writel(&hub->cntl->ohci.regs->roothub_portstatus[port] - , RH_PS_CCS|RH_PS_LSDA); - mutex_unlock(&hub->cntl->resetlock); + writel(&cntl->regs->roothub_portstatus[port], RH_PS_CCS|RH_PS_LSDA); + mutex_unlock(&cntl->usb.resetlock); goto done; }
// Find any devices connected to the root hub. static int -check_ohci_ports(struct usb_s *cntl) +check_ohci_ports(struct usb_ohci_s *cntl) { ASSERT32FLAT(); // Turn on power for all devices on roothub. - u32 rha = readl(&cntl->ohci.regs->roothub_a); + u32 rha = readl(&cntl->regs->roothub_a); rha &= ~(RH_A_PSM | RH_A_OCPM); - writel(&cntl->ohci.regs->roothub_status, RH_HS_LPSC); - writel(&cntl->ohci.regs->roothub_b, RH_B_PPCM); + writel(&cntl->regs->roothub_status, RH_HS_LPSC); + writel(&cntl->regs->roothub_b, RH_B_PPCM); msleep((rha >> 24) * 2); // XXX - need to sleep for USB_TIME_SIGATT if just powered up?
// Lanuch a thread per port. struct usbhub_s hub; memset(&hub, 0, sizeof(hub)); - hub.cntl = cntl; + hub.cntl = &cntl->usb; int ports = rha & RH_A_NDP; hub.threads = ports; int i; @@ -118,23 +122,23 @@ check_ohci_ports(struct usb_s *cntl) ****************************************************************/
static int -start_ohci(struct usb_s *cntl, struct ohci_hcca *hcca) +start_ohci(struct usb_ohci_s *cntl, struct ohci_hcca *hcca) { - u32 oldfminterval = readl(&cntl->ohci.regs->fminterval); - u32 oldrwc = readl(&cntl->ohci.regs->control) & OHCI_CTRL_RWC; + u32 oldfminterval = readl(&cntl->regs->fminterval); + u32 oldrwc = readl(&cntl->regs->control) & OHCI_CTRL_RWC;
// XXX - check if already running?
// Do reset - writel(&cntl->ohci.regs->control, OHCI_USB_RESET | oldrwc); - readl(&cntl->ohci.regs->control); // flush writes + writel(&cntl->regs->control, OHCI_USB_RESET | oldrwc); + readl(&cntl->regs->control); // flush writes msleep(USB_TIME_DRSTR);
// Do software init (min 10us, max 2ms) u64 end = calc_future_tsc_usec(10); - writel(&cntl->ohci.regs->cmdstatus, OHCI_HCR); + writel(&cntl->regs->cmdstatus, OHCI_HCR); for (;;) { - u32 status = readl(&cntl->ohci.regs->cmdstatus); + u32 status = readl(&cntl->regs->cmdstatus); if (! status & OHCI_HCR) break; if (check_time(end)) { @@ -144,62 +148,41 @@ start_ohci(struct usb_s *cntl, struct ohci_hcca *hcca) }
// Init memory - writel(&cntl->ohci.regs->ed_controlhead, 0); - writel(&cntl->ohci.regs->ed_bulkhead, 0); - writel(&cntl->ohci.regs->hcca, (u32)hcca); + writel(&cntl->regs->ed_controlhead, 0); + writel(&cntl->regs->ed_bulkhead, 0); + writel(&cntl->regs->hcca, (u32)hcca);
// Init fminterval u32 fi = oldfminterval & 0x3fff; - writel(&cntl->ohci.regs->fminterval + writel(&cntl->regs->fminterval , (((oldfminterval & FIT) ^ FIT) | fi | (((6 * (fi - 210)) / 7) << 16))); - writel(&cntl->ohci.regs->periodicstart, ((9 * fi) / 10) & 0x3fff); - readl(&cntl->ohci.regs->control); // flush writes + writel(&cntl->regs->periodicstart, ((9 * fi) / 10) & 0x3fff); + readl(&cntl->regs->control); // flush writes
// XXX - verify that fminterval was setup correctly.
// Go into operational state - writel(&cntl->ohci.regs->control + writel(&cntl->regs->control , (OHCI_CTRL_CBSR | OHCI_CTRL_CLE | OHCI_CTRL_PLE | OHCI_USB_OPER | oldrwc)); - readl(&cntl->ohci.regs->control); // flush writes + readl(&cntl->regs->control); // flush writes
return 0; }
static void -stop_ohci(struct usb_s *cntl) +stop_ohci(struct usb_ohci_s *cntl) { - u32 oldrwc = readl(&cntl->ohci.regs->control) & OHCI_CTRL_RWC; - writel(&cntl->ohci.regs->control, oldrwc); - readl(&cntl->ohci.regs->control); // flush writes + u32 oldrwc = readl(&cntl->regs->control) & OHCI_CTRL_RWC; + writel(&cntl->regs->control, oldrwc); + readl(&cntl->regs->control); // flush writes }
-void -ohci_init(void *data) +static void +configure_ohci(void *data) { - if (! CONFIG_USB_OHCI) - return; - struct usb_s *cntl = data; - - // XXX - don't call pci_config_XXX from a thread - cntl->type = USB_TYPE_OHCI; - u32 baseaddr = pci_config_readl(cntl->bdf, PCI_BASE_ADDRESS_0); - cntl->ohci.regs = (void*)(baseaddr & PCI_BASE_ADDRESS_MEM_MASK); - - dprintf(3, "OHCI init on dev %02x:%02x.%x (regs=%p)\n" - , pci_bdf_to_bus(cntl->bdf), pci_bdf_to_dev(cntl->bdf) - , pci_bdf_to_fn(cntl->bdf), cntl->ohci.regs); - - // Enable bus mastering and memory access. - pci_config_maskw(cntl->bdf, PCI_COMMAND - , 0, PCI_COMMAND_MASTER|PCI_COMMAND_MEMORY); - - // XXX - check for and disable SMM control? - - // Disable interrupts - writel(&cntl->ohci.regs->intrdisable, ~0); - writel(&cntl->ohci.regs->intrstatus, ~0); + struct usb_ohci_s *cntl = data;
// Allocate memory struct ohci_hcca *hcca = memalign_high(256, sizeof(*hcca)); @@ -220,7 +203,7 @@ ohci_init(void *data) goto err;
int count = check_ohci_ports(cntl); - free_pipe(cntl->defaultpipe); + free_pipe(cntl->usb.defaultpipe); if (! count) goto err; return; @@ -232,6 +215,36 @@ free: free(intr_ed); }
+void +ohci_init(u16 bdf, int busid) +{ + if (! CONFIG_USB_OHCI) + return; + struct usb_ohci_s *cntl = malloc_tmphigh(sizeof(*cntl)); + memset(cntl, 0, sizeof(*cntl)); + cntl->usb.busid = busid; + cntl->usb.type = USB_TYPE_OHCI; + + u32 baseaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0); + cntl->regs = (void*)(baseaddr & PCI_BASE_ADDRESS_MEM_MASK); + + dprintf(3, "OHCI init on dev %02x:%02x.%x (regs=%p)\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf) + , pci_bdf_to_fn(bdf), cntl->regs); + + // Enable bus mastering and memory access. + pci_config_maskw(bdf, PCI_COMMAND + , 0, PCI_COMMAND_MASTER|PCI_COMMAND_MEMORY); + + // XXX - check for and disable SMM control? + + // Disable interrupts + writel(&cntl->regs->intrdisable, ~0); + writel(&cntl->regs->intrstatus, ~0); + + run_thread(configure_ohci, cntl); +} +
/**************************************************************** * End point communication @@ -255,10 +268,10 @@ 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) +ohci_waittick(struct usb_ohci_s *cntl) { barrier(); - struct ohci_hcca *hcca = (void*)cntl->ohci.regs->hcca; + struct ohci_hcca *hcca = (void*)cntl->regs->hcca; u32 startframe = hcca->frame_no; u64 end = calc_future_tsc(1000 * 5); for (;;) { @@ -273,15 +286,15 @@ ohci_waittick(struct usb_s *cntl) }
static void -signal_freelist(struct usb_s *cntl) +signal_freelist(struct usb_ohci_s *cntl) { - u32 v = readl(&cntl->ohci.regs->control); + u32 v = readl(&cntl->regs->control); if (v & OHCI_CTRL_CLE) { - writel(&cntl->ohci.regs->control, v & ~(OHCI_CTRL_CLE|OHCI_CTRL_BLE)); + writel(&cntl->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); + writel(&cntl->regs->ed_controlcurrent, 0); + writel(&cntl->regs->ed_bulkcurrent, 0); + writel(&cntl->regs->control, v); } else { ohci_waittick(cntl); } @@ -302,9 +315,10 @@ ohci_free_pipe(struct usb_pipe *p) return; dprintf(7, "ohci_free_pipe %p\n", p); struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe); - struct usb_s *cntl = pipe->pipe.cntl; + struct usb_ohci_s *cntl = container_of( + pipe->pipe.cntl, struct usb_ohci_s, usb);
- u32 *pos = &cntl->ohci.regs->ed_controlhead; + u32 *pos = &cntl->regs->ed_controlhead; for (;;) { struct ohci_ed *next = (void*)*pos; if (!next) { @@ -327,8 +341,9 @@ ohci_alloc_control_pipe(struct usb_pipe *dummy) { if (! CONFIG_USB_OHCI) return NULL; - struct usb_s *cntl = dummy->cntl; - dprintf(7, "ohci_alloc_control_pipe %p\n", cntl); + struct usb_ohci_s *cntl = container_of( + dummy->cntl, struct usb_ohci_s, usb); + dprintf(7, "ohci_alloc_control_pipe %p\n", &cntl->usb);
// Allocate a queue head. struct ohci_pipe *pipe = malloc_tmphigh(sizeof(*pipe)); @@ -341,9 +356,9 @@ ohci_alloc_control_pipe(struct usb_pipe *dummy) memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe));
// Add queue head to controller list. - pipe->ed.hwNextED = cntl->ohci.regs->ed_controlhead; + pipe->ed.hwNextED = cntl->regs->ed_controlhead; barrier(); - cntl->ohci.regs->ed_controlhead = (u32)&pipe->ed; + cntl->regs->ed_controlhead = (u32)&pipe->ed; return &pipe->pipe; }
@@ -360,7 +375,8 @@ ohci_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_s *cntl = pipe->pipe.cntl; + struct usb_ohci_s *cntl = container_of( + pipe->pipe.cntl, struct usb_ohci_s, usb); int maxpacket = pipe->pipe.maxpacket; int lowspeed = pipe->pipe.lowspeed; int devaddr = pipe->pipe.devaddr | (pipe->pipe.ep << 7); @@ -387,7 +403,7 @@ ohci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize pipe->ed.hwTailP = (u32)&tds[3]; barrier(); pipe->ed.hwINFO = devaddr | (maxpacket << 16) | (lowspeed ? ED_LOWSPEED : 0); - writel(&cntl->ohci.regs->cmdstatus, OHCI_CLF); + writel(&cntl->regs->cmdstatus, OHCI_CLF);
int ret = wait_ed(&pipe->ed); pipe->ed.hwINFO = ED_SKIP; @@ -402,8 +418,9 @@ ohci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp) { if (! CONFIG_USB_OHCI) return NULL; - struct usb_s *cntl = dummy->cntl; - dprintf(7, "ohci_alloc_intr_pipe %p %d\n", cntl, frameexp); + struct usb_ohci_s *cntl = container_of( + dummy->cntl, struct usb_ohci_s, usb); + dprintf(7, "ohci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp);
if (frameexp > 5) frameexp = 5; @@ -434,7 +451,7 @@ ohci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp)
// Add to interrupt schedule. barrier(); - struct ohci_hcca *hcca = (void*)cntl->ohci.regs->hcca; + struct ohci_hcca *hcca = (void*)cntl->regs->hcca; if (frameexp == 0) { // Add to existing interrupt entry. struct ohci_ed *intr_ed = (void*)hcca->int_table[0]; diff --git a/src/usb-ohci.h b/src/usb-ohci.h index b7b2b29..0cadbd6 100644 --- a/src/usb-ohci.h +++ b/src/usb-ohci.h @@ -2,8 +2,7 @@ #define __USB_OHCI_H
// usb-ohci.c -struct usb_s; -void ohci_init(void *data); +void ohci_init(u16 bdf, int busid); struct usb_pipe; void ohci_free_pipe(struct usb_pipe *p); struct usb_pipe *ohci_alloc_control_pipe(struct usb_pipe *dummy); diff --git a/src/usb-uhci.c b/src/usb-uhci.c index a870963..f986a2a 100644 --- a/src/usb-uhci.c +++ b/src/usb-uhci.c @@ -15,6 +15,13 @@ #include "biosvar.h" // GET_GLOBAL #include "usb-hub.h" // struct usbhub_s
+struct usb_uhci_s { + struct usb_s usb; + u16 iobase; + struct uhci_qh *control_qh, *bulk_qh; + struct uhci_framelist *framelist; +}; +
/**************************************************************** * Root hub @@ -25,7 +32,8 @@ init_uhci_port(void *data) { struct usbhub_s *hub = data; u32 port = hub->port; // XXX - find better way to pass port - u16 ioport = hub->cntl->uhci.iobase + USBPORTSC1 + port * 2; + struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb); + u16 ioport = cntl->iobase + USBPORTSC1 + port * 2;
u16 status = inw(ioport); if (!(status & USBPORTSC_CCS)) @@ -37,7 +45,7 @@ init_uhci_port(void *data) // Reset port outw(USBPORTSC_PR, ioport); msleep(USB_TIME_DRSTR); - mutex_lock(&hub->cntl->resetlock); + mutex_lock(&cntl->usb.resetlock); outw(0, ioport); udelay(6); // 64 high-speed bit times status = inw(ioport); @@ -46,10 +54,10 @@ init_uhci_port(void *data) goto resetfail; outw(USBPORTSC_PE, ioport); struct usb_pipe *pipe = usb_set_address( - hub->cntl, !!(status & USBPORTSC_LSDA)); + &cntl->usb, !!(status & USBPORTSC_LSDA)); if (!pipe) goto resetfail; - mutex_unlock(&hub->cntl->resetlock); + mutex_unlock(&cntl->usb.resetlock);
// Configure port int count = configure_usb_device(pipe); @@ -64,18 +72,18 @@ done:
resetfail: outw(0, ioport); - mutex_unlock(&hub->cntl->resetlock); + mutex_unlock(&cntl->usb.resetlock); goto done; }
// Find any devices connected to the root hub. static int -check_ports(struct usb_s *cntl) +check_uhci_ports(struct usb_uhci_s *cntl) { ASSERT32FLAT(); struct usbhub_s hub; memset(&hub, 0, sizeof(hub)); - hub.cntl = cntl; + hub.cntl = &cntl->usb; hub.threads = 2;
// Launch a thread for every port. @@ -96,25 +104,27 @@ check_ports(struct usb_s *cntl) ****************************************************************/
static void -reset_uhci(struct usb_s *cntl) +reset_uhci(struct usb_uhci_s *cntl, u16 bdf) { // XXX - don't reset if not needed.
// Reset PIRQ and SMI - pci_config_writew(cntl->bdf, USBLEGSUP, USBLEGSUP_RWC); + pci_config_writew(bdf, USBLEGSUP, USBLEGSUP_RWC);
// Reset the HC - outw(USBCMD_HCRESET, cntl->uhci.iobase + USBCMD); + outw(USBCMD_HCRESET, cntl->iobase + USBCMD); udelay(5);
// Disable interrupts and commands (just to be safe). - outw(0, cntl->uhci.iobase + USBINTR); - outw(0, cntl->uhci.iobase + USBCMD); + outw(0, cntl->iobase + USBINTR); + outw(0, cntl->iobase + USBCMD); }
static void -configure_uhci(struct usb_s *cntl) +configure_uhci(void *data) { + struct usb_uhci_s *cntl = data; + // Allocate ram for schedule storage struct uhci_td *term_td = malloc_high(sizeof(*term_td)); struct uhci_framelist *fl = memalign_high(sizeof(*fl), sizeof(*fl)); @@ -122,11 +132,7 @@ configure_uhci(struct usb_s *cntl) struct uhci_qh *term_qh = malloc_high(sizeof(*term_qh)); if (!term_td || !fl || !intr_qh || !term_qh) { warn_noalloc(); - free(term_td); - free(fl); - free(intr_qh); - free(term_qh); - return; + goto fail; }
// Work around for PIIX errata @@ -145,54 +151,59 @@ configure_uhci(struct usb_s *cntl) 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; + cntl->framelist = fl; + cntl->control_qh = cntl->bulk_qh = intr_qh; barrier();
// Set the frame length to the default: 1 ms exactly - outb(USBSOF_DEFAULT, cntl->uhci.iobase + USBSOF); + outb(USBSOF_DEFAULT, cntl->iobase + USBSOF);
// Store the frame list base address - outl((u32)fl->links, cntl->uhci.iobase + USBFLBASEADD); + outl((u32)fl->links, cntl->iobase + USBFLBASEADD);
// Set the current frame number - outw(0, cntl->uhci.iobase + USBFRNUM); -} + outw(0, cntl->iobase + USBFRNUM);
-static void -start_uhci(struct usb_s *cntl) -{ // Mark as configured and running with a 64-byte max packet. - outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, cntl->uhci.iobase + USBCMD); + outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, cntl->iobase + USBCMD); + + // Find devices + int count = check_uhci_ports(cntl); + free_pipe(cntl->usb.defaultpipe); + if (count) + // Success + return; + + // No devices found - shutdown and free controller. + outw(0, cntl->iobase + USBCMD); +fail: + free(term_td); + free(fl); + free(intr_qh); + free(term_qh); }
void -uhci_init(void *data) +uhci_init(u16 bdf, int busid) { if (! CONFIG_USB_UHCI) return; - struct usb_s *cntl = data; - - // XXX - don't call pci_config_XXX from a thread - cntl->type = USB_TYPE_UHCI; - cntl->uhci.iobase = (pci_config_readl(cntl->bdf, PCI_BASE_ADDRESS_4) - & PCI_BASE_ADDRESS_IO_MASK); + struct usb_uhci_s *cntl = malloc_tmphigh(sizeof(*cntl)); + memset(cntl, 0, sizeof(*cntl)); + cntl->usb.busid = busid; + cntl->usb.type = USB_TYPE_UHCI; + cntl->iobase = (pci_config_readl(bdf, PCI_BASE_ADDRESS_4) + & PCI_BASE_ADDRESS_IO_MASK);
dprintf(3, "UHCI init on dev %02x:%02x.%x (io=%x)\n" - , pci_bdf_to_bus(cntl->bdf), pci_bdf_to_dev(cntl->bdf) - , pci_bdf_to_fn(cntl->bdf), cntl->uhci.iobase); + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf) + , pci_bdf_to_fn(bdf), cntl->iobase);
- pci_config_maskw(cntl->bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER);
- reset_uhci(cntl); - configure_uhci(cntl); - start_uhci(cntl); + reset_uhci(cntl, bdf);
- int count = check_ports(cntl); - free_pipe(cntl->defaultpipe); - if (! count) { - // XXX - no devices; free data structures. - } + run_thread(configure_uhci, cntl); }
@@ -201,7 +212,7 @@ uhci_init(void *data) ****************************************************************/
static int -wait_qh(struct usb_s *cntl, struct uhci_qh *qh) +wait_qh(struct usb_uhci_s *cntl, struct uhci_qh *qh) { // XXX - 500ms just a guess u64 end = calc_future_tsc(500); @@ -213,8 +224,8 @@ wait_qh(struct usb_s *cntl, struct uhci_qh *qh) struct uhci_td *td = (void*)(qh->element & ~UHCI_PTR_BITS); dprintf(1, "Timeout on wait_qh %p (td=%p s=%x c=%x/%x)\n" , qh, td, td->status - , inw(cntl->uhci.iobase + USBCMD) - , inw(cntl->uhci.iobase + USBSTS)); + , inw(cntl->iobase + USBCMD) + , inw(cntl->iobase + USBSTS)); return -1; } yield(); @@ -223,10 +234,9 @@ 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(struct usb_s *cntl) +uhci_waittick(u16 iobase) { barrier(); - u16 iobase = GET_GLOBAL(cntl->uhci.iobase); u16 startframe = inw(iobase + USBFRNUM); u64 end = calc_future_tsc(1000 * 5); for (;;) { @@ -244,6 +254,7 @@ struct uhci_pipe { struct uhci_qh qh; struct uhci_td *next_td; struct usb_pipe pipe; + u16 iobase; };
void @@ -253,10 +264,10 @@ uhci_free_pipe(struct usb_pipe *p) return; dprintf(7, "uhci_free_pipe %p\n", p); struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe); - struct usb_s *cntl = pipe->pipe.cntl; + struct usb_uhci_s *cntl = container_of( + pipe->pipe.cntl, struct usb_uhci_s, usb);
- struct uhci_framelist *fl = cntl->uhci.framelist; - struct uhci_qh *pos = (void*)(fl->links[0] & ~UHCI_PTR_BITS); + struct uhci_qh *pos = (void*)(cntl->framelist->links[0] & ~UHCI_PTR_BITS); for (;;) { u32 link = pos->link; if (link == UHCI_PTR_TERM) { @@ -267,11 +278,11 @@ uhci_free_pipe(struct usb_pipe *p) 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); + if (cntl->control_qh == next) + cntl->control_qh = pos; + if (cntl->bulk_qh == next) + cntl->bulk_qh = pos; + uhci_waittick(cntl->iobase); free(pipe); return; } @@ -284,8 +295,9 @@ uhci_alloc_control_pipe(struct usb_pipe *dummy) { if (! CONFIG_USB_UHCI) return NULL; - struct usb_s *cntl = dummy->cntl; - dprintf(7, "uhci_alloc_control_pipe %p\n", cntl); + struct usb_uhci_s *cntl = container_of( + dummy->cntl, struct usb_uhci_s, usb); + dprintf(7, "uhci_alloc_control_pipe %p\n", &cntl->usb);
// Allocate a queue head. struct uhci_pipe *pipe = malloc_tmphigh(sizeof(*pipe)); @@ -295,15 +307,16 @@ uhci_alloc_control_pipe(struct usb_pipe *dummy) } memset(pipe, 0, sizeof(*pipe)); pipe->qh.element = UHCI_PTR_TERM; + pipe->iobase = cntl->iobase; memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe));
// Add queue head to controller list. - struct uhci_qh *control_qh = cntl->uhci.control_qh; + struct uhci_qh *control_qh = cntl->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; + if (cntl->bulk_qh == control_qh) + cntl->bulk_qh = &pipe->qh; return &pipe->pipe; }
@@ -316,8 +329,9 @@ uhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize return -1; dprintf(5, "uhci_control %p\n", p); struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe); + struct usb_uhci_s *cntl = container_of( + pipe->pipe.cntl, struct usb_uhci_s, usb);
- struct usb_s *cntl = pipe->pipe.cntl; int maxpacket = pipe->pipe.maxpacket; int lowspeed = pipe->pipe.lowspeed; int devaddr = pipe->pipe.devaddr | (pipe->pipe.ep << 7); @@ -359,7 +373,7 @@ uhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize int ret = wait_qh(cntl, &pipe->qh); if (ret) { pipe->qh.element = UHCI_PTR_TERM; - uhci_waittick(cntl); + uhci_waittick(pipe->iobase); } free(tds); return ret; @@ -370,8 +384,9 @@ uhci_alloc_bulk_pipe(struct usb_pipe *dummy) { if (! CONFIG_USB_UHCI) return NULL; - struct usb_s *cntl = dummy->cntl; - dprintf(7, "uhci_alloc_bulk_pipe %p\n", cntl); + struct usb_uhci_s *cntl = container_of( + dummy->cntl, struct usb_uhci_s, usb); + dprintf(7, "uhci_alloc_bulk_pipe %p\n", &cntl->usb);
// Allocate a queue head. struct uhci_pipe *pipe = malloc_low(sizeof(*pipe)); @@ -381,10 +396,11 @@ uhci_alloc_bulk_pipe(struct usb_pipe *dummy) } memset(pipe, 0, sizeof(*pipe)); pipe->qh.element = UHCI_PTR_TERM; + pipe->iobase = cntl->iobase; memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe));
// Add queue head to controller list. - struct uhci_qh *bulk_qh = cntl->uhci.bulk_qh; + struct uhci_qh *bulk_qh = cntl->bulk_qh; pipe->qh.link = bulk_qh->link; barrier(); bulk_qh->link = (u32)&pipe->qh | UHCI_PTR_QH; @@ -475,7 +491,7 @@ uhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize) fail: dprintf(1, "uhci_send_bulk failed\n"); SET_FLATPTR(pipe->qh.element, UHCI_PTR_TERM); - uhci_waittick(GET_FLATPTR(pipe->pipe.cntl)); + uhci_waittick(GET_FLATPTR(pipe->iobase)); return -1; }
@@ -484,8 +500,9 @@ uhci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp) { if (! CONFIG_USB_UHCI) return NULL; - struct usb_s *cntl = dummy->cntl; - dprintf(7, "uhci_alloc_intr_pipe %p %d\n", cntl, frameexp); + struct usb_uhci_s *cntl = container_of( + dummy->cntl, struct usb_uhci_s, usb); + dprintf(7, "uhci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp);
if (frameexp > 10) frameexp = 10; @@ -506,6 +523,7 @@ uhci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp) memset(pipe, 0, sizeof(*pipe)); pipe->qh.element = (u32)tds; pipe->next_td = &tds[0]; + pipe->iobase = cntl->iobase; memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe));
int toggle = 0; @@ -522,17 +540,17 @@ uhci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp) }
// Add to interrupt schedule. - struct uhci_framelist *fl = cntl->uhci.framelist; + struct uhci_framelist *fl = cntl->framelist; if (frameexp == 0) { // Add to existing interrupt entry. struct uhci_qh *intr_qh = (void*)(fl->links[0] & ~UHCI_PTR_BITS); pipe->qh.link = intr_qh->link; barrier(); 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; + if (cntl->control_qh == intr_qh) + cntl->control_qh = &pipe->qh; + if (cntl->bulk_qh == intr_qh) + cntl->bulk_qh = &pipe->qh; } else { int startpos = 1<<(frameexp-1); pipe->qh.link = fl->links[startpos]; diff --git a/src/usb-uhci.h b/src/usb-uhci.h index 894027b..78ec57b 100644 --- a/src/usb-uhci.h +++ b/src/usb-uhci.h @@ -2,7 +2,7 @@ #define __USB_UHCI_H
// usb-uhci.c -void uhci_init(void *data); +void uhci_init(u16 bdf, int busid); struct usb_pipe; void uhci_free_pipe(struct usb_pipe *p); struct usb_pipe *uhci_alloc_control_pipe(struct usb_pipe *dummy); diff --git a/src/usb.c b/src/usb.c index 57d1ad5..9575185 100644 --- a/src/usb.c +++ b/src/usb.c @@ -17,8 +17,6 @@ #include "usb.h" // struct usb_s #include "biosvar.h" // GET_GLOBAL
-struct usb_s USBControllers[16] VAR16VISIBLE; -
/**************************************************************** * Controller function wrappers @@ -338,7 +336,6 @@ usb_setup(void)
dprintf(3, "init usb\n");
- memset(&USBControllers, 0, sizeof(USBControllers)); usb_keyboard_setup();
// Look for USB controllers @@ -350,18 +347,13 @@ usb_setup(void) if (code >> 8 != PCI_CLASS_SERIAL_USB) continue;
- struct usb_s *cntl = &USBControllers[count]; - cntl->bdf = bdf; - if (code == PCI_CLASS_SERIAL_USB_UHCI) - run_thread(uhci_init, cntl); + uhci_init(bdf, count); else if (code == PCI_CLASS_SERIAL_USB_OHCI) - run_thread(ohci_init, cntl); + ohci_init(bdf, count); else continue;
count++; - if (count >= ARRAY_SIZE(USBControllers)) - break; } } diff --git a/src/usb.h b/src/usb.h index 6bc0cfd..e1ca45a 100644 --- a/src/usb.h +++ b/src/usb.h @@ -14,30 +14,18 @@ struct usb_pipe { u8 toggle; };
-// Local information for a usb controller. +// Common information for usb controllers. struct usb_s { - u8 type; - u8 maxaddr; - u16 bdf; struct usb_pipe *defaultpipe; struct mutex_s resetlock; - - union { - struct { - u16 iobase; - void *control_qh, *bulk_qh, *framelist; - } uhci; - struct { - struct ohci_regs *regs; - } ohci; - }; + int busid; + u8 type; + u8 maxaddr; };
#define USB_TYPE_UHCI 1 #define USB_TYPE_OHCI 2
-extern struct usb_s USBControllers[]; - #define USB_MAXADDR 127
Reduce the size to make arrays of tds smaller. For interrupt in pipes, allocate the data space separately. --- src/usb-uhci.c | 15 ++++++++------- src/usb-uhci.h | 3 --- 2 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/src/usb-uhci.c b/src/usb-uhci.c index f986a2a..3384504 100644 --- a/src/usb-uhci.c +++ b/src/usb-uhci.c @@ -514,12 +514,11 @@ uhci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp) int count = DIV_ROUND_UP(PIT_TICK_INTERVAL * 1000 * 2, PIT_TICK_RATE * ms); struct uhci_pipe *pipe = malloc_low(sizeof(*pipe)); struct uhci_td *tds = malloc_low(sizeof(*tds) * count); - if (!pipe || !tds) { + void *data = malloc_low(maxpacket * count); + if (!pipe || !tds || !data) { warn_noalloc(); goto fail; } - if (maxpacket > sizeof(tds[0].data)) - goto fail; memset(pipe, 0, sizeof(*pipe)); pipe->qh.element = (u32)tds; pipe->next_td = &tds[0]; @@ -535,7 +534,7 @@ uhci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp) tds[i].token = (uhci_explen(maxpacket) | toggle | (devaddr << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN); - tds[i].buffer = &tds[i].data; + tds[i].buffer = data + maxpacket * i; toggle ^= TD_TOKEN_TOGGLE; }
@@ -563,6 +562,7 @@ uhci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp) fail: free(pipe); free(tds); + free(data); return NULL; }
@@ -583,16 +583,17 @@ uhci_poll_intr(struct usb_pipe *p, void *data) // XXX - check for errors.
// Copy data. + void *tddata = GET_FLATPTR(td->buffer); memcpy_far(GET_SEG(SS), data - , FLATPTR_TO_SEG(td->data), (void*)FLATPTR_TO_OFFSET(td->data) + , FLATPTR_TO_SEG(tddata), (void*)FLATPTR_TO_OFFSET(tddata) , uhci_expected_length(token));
// Reenable this td. - u32 next = GET_FLATPTR(td->link); + struct uhci_td *next = (void*)(GET_FLATPTR(td->link) & ~UHCI_PTR_BITS); + SET_FLATPTR(pipe->next_td, next); barrier(); SET_FLATPTR(td->status, (uhci_maxerr(0) | (status & TD_CTRL_LS) | TD_CTRL_ACTIVE)); - 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 78ec57b..3c2c298 100644 --- a/src/usb-uhci.h +++ b/src/usb-uhci.h @@ -112,9 +112,6 @@ struct uhci_td { u32 status; u32 token; void *buffer; - - // Software fields - u32 data[4]; } PACKED;
struct uhci_qh {
One of the entries in the queue can't be used, so the total queue size needs to be one larger than it currently is. --- src/usb-ohci.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/usb-ohci.c b/src/usb-ohci.c index 28bbdc6..0048e2b 100644 --- a/src/usb-ohci.c +++ b/src/usb-ohci.c @@ -429,7 +429,7 @@ ohci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp) int devaddr = dummy->devaddr | (dummy->ep << 7); // 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); + int count = DIV_ROUND_UP(PIT_TICK_INTERVAL * 1000 * 2, PIT_TICK_RATE * ms)+1; struct ohci_pipe *pipe = malloc_low(sizeof(*pipe)); struct ohci_td *tds = malloc_low(sizeof(*tds) * count); void *data = malloc_low(maxpacket * count);
Don't send a data packet on OHCI if no data to be sent. Add some barrier() calls where needed. Move toggle definition from generic pipe struct to uhci pipe struct. Check for malloc failure on ohci and uhci "tds" request. Be sure to always allocate an even number of intr tds on uhci - toggle setting depends on it. --- src/usb-ohci.c | 51 +++++++++++++++++++++++++++++++-------------------- src/usb-uhci.c | 21 +++++++++++++++------ src/usb.h | 1 - 3 files changed, 46 insertions(+), 27 deletions(-)
diff --git a/src/usb-ohci.c b/src/usb-ohci.c index 0048e2b..5fb7fec 100644 --- a/src/usb-ohci.c +++ b/src/usb-ohci.c @@ -352,8 +352,8 @@ ohci_alloc_control_pipe(struct usb_pipe *dummy) return NULL; } memset(pipe, 0, sizeof(*pipe)); - pipe->ed.hwINFO = ED_SKIP; memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe)); + pipe->ed.hwINFO = ED_SKIP;
// Add queue head to controller list. pipe->ed.hwNextED = cntl->regs->ed_controlhead; @@ -383,24 +383,34 @@ ohci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
// Setup transfer descriptors struct ohci_td *tds = malloc_tmphigh(sizeof(*tds) * 3); - tds[0].hwINFO = TD_DP_SETUP | TD_T_DATA0 | TD_CC; - tds[0].hwCBP = (u32)cmd; - tds[0].hwNextTD = (u32)&tds[1]; - tds[0].hwBE = (u32)cmd + cmdsize - 1; - tds[1].hwINFO = (dir ? TD_DP_IN : TD_DP_OUT) | TD_T_DATA1 | TD_CC; - tds[1].hwCBP = datasize ? (u32)data : 0; - tds[1].hwNextTD = (u32)&tds[2]; - tds[1].hwBE = (u32)data + datasize - 1; - tds[2].hwINFO = (dir ? TD_DP_OUT : TD_DP_IN) | TD_T_DATA1 | TD_CC; - tds[2].hwCBP = 0; - tds[2].hwNextTD = (u32)&tds[3]; - tds[2].hwBE = 0; + if (!tds) { + warn_noalloc(); + return -1; + } + struct ohci_td *td = tds; + td->hwINFO = TD_DP_SETUP | TD_T_DATA0 | TD_CC; + td->hwCBP = (u32)cmd; + td->hwNextTD = (u32)&td[1]; + td->hwBE = (u32)cmd + cmdsize - 1; + td++; + if (datasize) { + td->hwINFO = (dir ? TD_DP_IN : TD_DP_OUT) | TD_T_DATA1 | TD_CC; + td->hwCBP = (u32)data; + td->hwNextTD = (u32)&td[1]; + td->hwBE = (u32)data + datasize - 1; + td++; + } + td->hwINFO = (dir ? TD_DP_OUT : TD_DP_IN) | TD_T_DATA1 | TD_CC; + td->hwCBP = 0; + td->hwNextTD = (u32)&td[1]; + td->hwBE = 0; + td++;
// Transfer data pipe->ed.hwINFO = ED_SKIP; barrier(); - pipe->ed.hwHeadP = (u32)&tds[0]; - pipe->ed.hwTailP = (u32)&tds[3]; + pipe->ed.hwHeadP = (u32)tds; + pipe->ed.hwTailP = (u32)td; barrier(); pipe->ed.hwINFO = devaddr | (maxpacket << 16) | (lowspeed ? ED_LOWSPEED : 0); writel(&cntl->regs->cmdstatus, OHCI_CLF); @@ -435,6 +445,11 @@ ohci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp) void *data = malloc_low(maxpacket * count); if (!pipe || !tds || !data) goto err; + memset(pipe, 0, sizeof(*pipe)); + memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe)); + pipe->data = data; + pipe->count = count; + pipe->tds = tds;
struct ohci_ed *ed = &pipe->ed; ed->hwHeadP = (u32)&tds[0]; @@ -464,10 +479,6 @@ ohci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp) hcca->int_table[i] = (u32)ed; }
- pipe->data = data; - pipe->count = count; - pipe->tds = tds; - memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe)); return &pipe->pipe;
err: @@ -510,7 +521,7 @@ ohci_poll_intr(struct usb_pipe *p, void *data) SET_FLATPTR(tail->hwCBP, (u32)intrdata); SET_FLATPTR(tail->hwNextTD, (u32)next); SET_FLATPTR(tail->hwBE, (u32)intrdata + maxpacket - 1); - + barrier(); SET_FLATPTR(pipe->ed.hwTailP, (u32)next);
return 0; diff --git a/src/usb-uhci.c b/src/usb-uhci.c index 3384504..c26b95b 100644 --- a/src/usb-uhci.c +++ b/src/usb-uhci.c @@ -12,7 +12,6 @@ #include "pci_regs.h" // PCI_BASE_ADDRESS_4 #include "usb.h" // struct usb_s #include "farptr.h" // GET_FLATPTR -#include "biosvar.h" // GET_GLOBAL #include "usb-hub.h" // struct usbhub_s
struct usb_uhci_s { @@ -181,6 +180,7 @@ fail: free(fl); free(intr_qh); free(term_qh); + free(cntl); }
void @@ -255,6 +255,7 @@ struct uhci_pipe { struct uhci_td *next_td; struct usb_pipe pipe; u16 iobase; + u8 toggle; };
void @@ -306,9 +307,9 @@ uhci_alloc_control_pipe(struct usb_pipe *dummy) return NULL; } memset(pipe, 0, sizeof(*pipe)); + memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe)); pipe->qh.element = UHCI_PTR_TERM; pipe->iobase = cntl->iobase; - memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe));
// Add queue head to controller list. struct uhci_qh *control_qh = cntl->control_qh; @@ -339,6 +340,10 @@ uhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize // Setup transfer descriptors int count = 2 + DIV_ROUND_UP(datasize, maxpacket); struct uhci_td *tds = malloc_tmphigh(sizeof(*tds) * count); + if (!tds) { + warn_noalloc(); + return -1; + }
tds[0].link = (u32)&tds[1] | UHCI_PTR_DEPTH; tds[0].status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0) @@ -395,9 +400,9 @@ uhci_alloc_bulk_pipe(struct usb_pipe *dummy) return NULL; } memset(pipe, 0, sizeof(*pipe)); + memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe)); pipe->qh.element = UHCI_PTR_TERM; pipe->iobase = cntl->iobase; - memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe));
// Add queue head to controller list. struct uhci_qh *bulk_qh = cntl->bulk_qh; @@ -436,6 +441,8 @@ wait_td(struct uhci_td *td) int uhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize) { + if (! CONFIG_USB_UHCI) + return -1; struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe); dprintf(7, "uhci_send_bulk qh=%p dir=%d data=%p size=%d\n" , &pipe->qh, dir, data, datasize); @@ -443,7 +450,7 @@ uhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize) int lowspeed = GET_FLATPTR(pipe->pipe.lowspeed); int devaddr = (GET_FLATPTR(pipe->pipe.devaddr) | (GET_FLATPTR(pipe->pipe.ep) << 7)); - int toggle = GET_FLATPTR(pipe->pipe.toggle) ? TD_TOKEN_TOGGLE : 0; + int toggle = GET_FLATPTR(pipe->toggle) ? TD_TOKEN_TOGGLE : 0;
// Allocate 4 tds on stack (16byte aligned) u8 tdsbuf[sizeof(struct uhci_td) * STACKTDS + TDALIGN - 1]; @@ -451,6 +458,7 @@ uhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize) memset(tds, 0, sizeof(*tds) * STACKTDS);
// Enable tds + barrier(); SET_FLATPTR(pipe->qh.element, (u32)MAKE_FLATPTR(GET_SEG(SS), tds));
int tdpos = 0; @@ -486,7 +494,7 @@ uhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize) goto fail; }
- SET_FLATPTR(pipe->pipe.toggle, !!toggle); + SET_FLATPTR(pipe->toggle, !!toggle); return 0; fail: dprintf(1, "uhci_send_bulk failed\n"); @@ -512,6 +520,7 @@ uhci_alloc_intr_pipe(struct usb_pipe *dummy, 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); + count = ALIGN(count, 2); struct uhci_pipe *pipe = malloc_low(sizeof(*pipe)); struct uhci_td *tds = malloc_low(sizeof(*tds) * count); void *data = malloc_low(maxpacket * count); @@ -520,10 +529,10 @@ uhci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp) goto fail; } memset(pipe, 0, sizeof(*pipe)); + memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe)); pipe->qh.element = (u32)tds; pipe->next_td = &tds[0]; pipe->iobase = cntl->iobase; - memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe));
int toggle = 0; int i; diff --git a/src/usb.h b/src/usb.h index e1ca45a..10f824f 100644 --- a/src/usb.h +++ b/src/usb.h @@ -11,7 +11,6 @@ struct usb_pipe { u8 devaddr; u8 lowspeed; u16 maxpacket; - u8 toggle; };
// Common information for usb controllers.
Initial support for EHCI high-speed USB controllers. --- Makefile | 2 +- src/config.h | 2 + src/usb-ehci.c | 751 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/usb-ehci.h | 173 +++++++++++++ src/usb-hub.c | 3 +- src/usb-hub.h | 2 + src/usb-ohci.c | 6 +- src/usb-uhci.c | 8 +- src/usb.c | 76 +++++- src/usb.h | 12 +- 10 files changed, 1013 insertions(+), 22 deletions(-) create mode 100644 src/usb-ehci.c create mode 100644 src/usb-ehci.h
diff --git a/Makefile b/Makefile index 82651d8..d2c0ffd 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ OUT=out/ SRCBOTH=misc.c pmm.c stacks.c output.c util.c block.c floppy.c ata.c mouse.c \ kbd.c pci.c serial.c clock.c pic.c cdrom.c ps2port.c smp.c resume.c \ pnpbios.c pirtable.c vgahooks.c ramdisk.c pcibios.c blockcmd.c \ - usb.c usb-uhci.c usb-ohci.c usb-hid.c usb-msc.c + usb.c usb-uhci.c usb-ohci.c usb-ehci.c usb-hid.c usb-msc.c SRC16=$(SRCBOTH) system.c disk.c apm.c font.c SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \ acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \ diff --git a/src/config.h b/src/config.h index a81c4ac..5316f22 100644 --- a/src/config.h +++ b/src/config.h @@ -36,6 +36,8 @@ #define CONFIG_USB_UHCI 1 // Support USB OHCI controllers #define CONFIG_USB_OHCI 1 +// Support USB EHCI controllers +#define CONFIG_USB_EHCI 1 // Support USB disks #define CONFIG_USB_MSC 1 // Support USB hubs diff --git a/src/usb-ehci.c b/src/usb-ehci.c new file mode 100644 index 0000000..6f23722 --- /dev/null +++ b/src/usb-ehci.c @@ -0,0 +1,751 @@ +// Code for handling EHCI USB controllers. +// +// Copyright (C) 2010 Kevin O'Connor kevin@koconnor.net +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "util.h" // dprintf +#include "pci.h" // pci_bdf_to_bus +#include "config.h" // CONFIG_* +#include "ioport.h" // outw +#include "usb-ehci.h" // struct ehci_qh +#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI +#include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "usb.h" // struct usb_s +#include "farptr.h" // GET_FLATPTR +#include "usb-hub.h" // struct usbhub_s +#include "usb-uhci.h" // init_uhci +#include "usb-ohci.h" // init_ohci + +struct companion_s { + u16 bdf; + u16 type; +}; + +struct usb_ehci_s { + struct usb_s usb; + struct ehci_caps *caps; + struct ehci_regs *regs; + struct ehci_qh *async_qh; + struct companion_s companion[8]; + int checkports; + int legacycount; +}; + + +/**************************************************************** + * Root hub + ****************************************************************/ + +#define EHCI_TIME_POSTPOWER 20 +#define EHCI_TIME_POSTRESET 2 + +// Start processing of companion controllers for full/low speed devices +static void +ehci_startcompanion(struct usb_ehci_s *cntl) +{ + if (! cntl->legacycount) + // No full/low speed devices found. + return; + int i; + for (i=0; i<ARRAY_SIZE(cntl->companion); i++) { + u16 type = cntl->companion[i].type; + if (type == USB_TYPE_UHCI) + uhci_init(cntl->companion[i].bdf, cntl->usb.busid + i); + else if (type == USB_TYPE_OHCI) + ohci_init(cntl->companion[i].bdf, cntl->usb.busid + i); + else + return; + } +} + +static void +init_ehci_port(void *data) +{ + struct usbhub_s *hub = data; + u32 port = hub->port; // XXX - find better way to pass port + struct usb_ehci_s *cntl = container_of(hub->cntl, struct usb_ehci_s, usb); + + u32 *portreg = &cntl->regs->portsc[port]; + u32 portsc = readl(portreg); + + // Power up port. + if (!(portsc & PORT_POWER)) { + portsc |= PORT_POWER; + writel(portreg, portsc); + msleep(EHCI_TIME_POSTPOWER); + portsc = readl(portreg); + } + + if (!(portsc & PORT_CONNECT)) + // No device present + goto done; + + if ((portsc & PORT_LINESTATUS_MASK) == PORT_LINESTATUS_KSTATE) { + // low speed device + cntl->legacycount++; + writel(portreg, portsc | PORT_OWNER); + goto done; + } + + // XXX - if just powered up, need to wait for USB_TIME_ATTDB? + + // Reset port + portsc = (portsc & ~PORT_PE) | PORT_RESET; + writel(portreg, portsc); + msleep(USB_TIME_DRSTR); + mutex_lock(&cntl->usb.resetlock); + portsc &= ~PORT_RESET; + writel(portreg, portsc); + msleep(EHCI_TIME_POSTRESET); + + portsc = readl(portreg); + if (!(portsc & PORT_CONNECT)) + // No longer connected + goto resetfail; + if (!(portsc & PORT_PE)) { + // full speed device + cntl->legacycount++; + writel(portreg, portsc | PORT_OWNER); + goto resetfail; + } + struct usb_pipe *pipe = usb_set_address(hub, port, USB_HIGHSPEED); + if (!pipe) + goto resetfail; + mutex_unlock(&cntl->usb.resetlock); + + // Configure port + int count = configure_usb_device(pipe); + free_pipe(pipe); + if (! count) + // Disable port + writel(portreg, portsc & ~PORT_PE); + hub->devcount += count; +done: + if (! --cntl->checkports) + ehci_startcompanion(cntl); + hub->threads--; + return; + +resetfail: + mutex_unlock(&cntl->usb.resetlock); + goto done; +} + +// Find any devices connected to the root hub. +static int +check_ehci_ports(struct usb_ehci_s *cntl) +{ + ASSERT32FLAT(); + + // Launch a thread for every port. + struct usbhub_s hub; + memset(&hub, 0, sizeof(hub)); + hub.cntl = &cntl->usb; + int ports = cntl->checkports; + hub.threads = ports; + int i; + for (i=0; i<ports; i++) { + hub.port = i; + run_thread(init_ehci_port, &hub); + } + + // Wait for threads to complete. + while (hub.threads) + yield(); + + return hub.devcount; +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +static void +configure_ehci(void *data) +{ + struct usb_ehci_s *cntl = data; + + // Allocate ram for schedule storage + struct ehci_framelist *fl = memalign_high(sizeof(*fl), sizeof(*fl)); + struct ehci_qh *intr_qh = memalign_high(EHCI_QH_ALIGN, sizeof(*intr_qh)); + struct ehci_qh *async_qh = memalign_high(EHCI_QH_ALIGN, sizeof(*async_qh)); + if (!fl || !intr_qh || !async_qh) { + warn_noalloc(); + goto fail; + } + + // XXX - check for halted? + + // Reset the HC + u32 cmd = readl(&cntl->regs->usbcmd); + writel(&cntl->regs->usbcmd, (cmd & ~(CMD_ASE | CMD_PSE)) | CMD_HCRESET); + u64 end = calc_future_tsc(250); + for (;;) { + cmd = readl(&cntl->regs->usbcmd); + if (!(cmd & CMD_HCRESET)) + break; + if (check_time(end)) { + warn_timeout(); + goto fail; + } + } + + // Disable interrupts (just to be safe). + writel(&cntl->regs->usbintr, 0); + + // Set schedule to point to primary intr queue head + memset(intr_qh, 0, sizeof(*intr_qh)); + intr_qh->next = EHCI_PTR_TERM; + intr_qh->info2 = (0x01 << QH_SMASK_SHIFT); + intr_qh->token = QTD_STS_HALT; + intr_qh->qtd_next = intr_qh->alt_next = EHCI_PTR_TERM; + int i; + for (i=0; i<ARRAY_SIZE(fl->links); i++) + fl->links[i] = (u32)intr_qh | EHCI_PTR_QH; + writel(&cntl->regs->periodiclistbase, (u32)fl); + + // Set async list to point to primary async queue head + memset(async_qh, 0, sizeof(*async_qh)); + async_qh->next = (u32)async_qh | EHCI_PTR_QH; + async_qh->info1 = QH_HEAD; + async_qh->token = QTD_STS_HALT; + async_qh->qtd_next = async_qh->alt_next = EHCI_PTR_TERM; + cntl->async_qh = async_qh; + writel(&cntl->regs->asynclistbase, (u32)async_qh); + + // Enable queues + writel(&cntl->regs->usbcmd, cmd | CMD_ASE | CMD_PSE | CMD_RUN); + + // Set default of high speed for root hub. + writel(&cntl->regs->configflag, 1); + cntl->checkports = readl(&cntl->caps->hcsparams) & HCS_N_PORTS_MASK; + + // Find devices + int count = check_ehci_ports(cntl); + free_pipe(cntl->usb.defaultpipe); + if (count) + // Success + return; + + // No devices found - shutdown and free controller. + writel(&cntl->regs->usbcmd, cmd & ~CMD_RUN); + msleep(4); // 2ms to stop reading memory - XXX +fail: + free(fl); + free(intr_qh); + free(async_qh); + free(cntl); +} + +int +ehci_init(u16 bdf, int busid, int compbdf) +{ + if (! CONFIG_USB_EHCI) + return -1; + + u32 baseaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0); + struct ehci_caps *caps = (void*)(baseaddr & PCI_BASE_ADDRESS_MEM_MASK); + u32 hcc_params = readl(&caps->hccparams); + if (hcc_params & HCC_64BIT_ADDR) { + dprintf(1, "No support for 64bit EHCI\n"); + return -1; + } + + struct usb_ehci_s *cntl = malloc_tmphigh(sizeof(*cntl)); + memset(cntl, 0, sizeof(*cntl)); + cntl->usb.busid = busid; + cntl->usb.type = USB_TYPE_EHCI; + cntl->caps = caps; + cntl->regs = (void*)caps + readb(&caps->caplength); + + dprintf(3, "EHCI init on dev %02x:%02x.%x (regs=%p)\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf) + , pci_bdf_to_fn(bdf), cntl->regs); + + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + // XXX - check for and disable SMM control? + + // Find companion controllers. + int count = 0; + int max = pci_to_bdf(pci_bdf_to_bus(bdf) + 1, 0, 0); + for (;;) { + if (compbdf < 0 || compbdf >= bdf) + break; + u32 code = pci_config_readl(compbdf, PCI_CLASS_REVISION) >> 8; + if (code == PCI_CLASS_SERIAL_USB_UHCI) { + cntl->companion[count].bdf = compbdf; + cntl->companion[count].type = USB_TYPE_UHCI; + count++; + } else if (code == PCI_CLASS_SERIAL_USB_OHCI) { + cntl->companion[count].bdf = compbdf; + cntl->companion[count].type = USB_TYPE_OHCI; + count++; + } + compbdf = pci_next(compbdf+1, &max); + } + + run_thread(configure_ehci, cntl); + return 0; +} + + +/**************************************************************** + * End point communication + ****************************************************************/ + +static int +ehci_wait_qh(struct usb_ehci_s *cntl, struct ehci_qh *qh) +{ + // XXX - 500ms just a guess + u64 end = calc_future_tsc(500); + for (;;) { + if (qh->qtd_next & EHCI_PTR_TERM) + // XXX - confirm + return 0; + if (check_time(end)) { + warn_timeout(); + return -1; + } + yield(); + } +} + +// Wait for next USB async frame to start - for ensuring safe memory release. +static void +ehci_waittick(struct usb_ehci_s *cntl) +{ + if (MODE16) { + msleep(10); + return; + } + // Wait for access to "doorbell" + barrier(); + u32 cmd, sts; + u64 end = calc_future_tsc(100); + for (;;) { + sts = readl(&cntl->regs->usbsts); + if (!(sts & STS_IAA)) { + cmd = readl(&cntl->regs->usbcmd); + if (!(cmd & CMD_IAAD)) + break; + } + if (check_time(end)) { + warn_timeout(); + return; + } + yield(); + } + // Ring "doorbell" + writel(&cntl->regs->usbcmd, cmd | CMD_IAAD); + // Wait for completion + for (;;) { + sts = readl(&cntl->regs->usbsts); + if (sts & STS_IAA) + break; + if (check_time(end)) { + warn_timeout(); + return; + } + yield(); + } + // Ack completion + writel(&cntl->regs->usbsts, STS_IAA); +} + +struct ehci_pipe { + struct ehci_qh qh; + struct ehci_qtd *next_td, *tds; + void *data; + struct usb_pipe pipe; +}; + +void +ehci_free_pipe(struct usb_pipe *p) +{ + if (! CONFIG_USB_EHCI) + return; + dprintf(7, "ehci_free_pipe %p\n", p); + struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe); + struct usb_ehci_s *cntl = container_of( + pipe->pipe.cntl, struct usb_ehci_s, usb); + + struct ehci_qh *start = cntl->async_qh; + struct ehci_qh *pos = start; + for (;;) { + struct ehci_qh *next = (void*)(pos->next & ~EHCI_PTR_BITS); + if (next == start) { + // Not found?! Exit without freeing. + warn_internalerror(); + return; + } + if (next == &pipe->qh) { + pos->next = next->next; + ehci_waittick(cntl); + free(pipe); + return; + } + pos = next; + } +} + +struct usb_pipe * +ehci_alloc_control_pipe(struct usb_pipe *dummy) +{ + if (! CONFIG_USB_EHCI) + return NULL; + struct usb_ehci_s *cntl = container_of( + dummy->cntl, struct usb_ehci_s, usb); + dprintf(7, "ehci_alloc_control_pipe %p\n", &cntl->usb); + + // Allocate a queue head. + struct ehci_pipe *pipe = memalign_tmphigh(EHCI_QH_ALIGN, sizeof(*pipe)); + if (!pipe) { + warn_noalloc(); + return NULL; + } + memset(pipe, 0, sizeof(*pipe)); + memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe)); + pipe->qh.qtd_next = pipe->qh.alt_next = EHCI_PTR_TERM; + pipe->qh.token = QTD_STS_HALT; + + // Add queue head to controller list. + struct ehci_qh *async_qh = cntl->async_qh; + pipe->qh.next = async_qh->next; + barrier(); + async_qh->next = (u32)&pipe->qh | EHCI_PTR_QH; + return &pipe->pipe; +} + +static int +fillTDbuffer(struct ehci_qtd *td, u16 maxpacket, const void *buf, int bytes) +{ + u32 dest = (u32)buf; + u32 *pos = td->buf; + while (bytes) { + if (pos >= &td->buf[ARRAY_SIZE(td->buf)]) + // More data than can transfer in a single qtd - only use + // full packets to prevent a babble error. + return ALIGN_DOWN(dest - (u32)buf, maxpacket); + u32 count = bytes; + u32 max = 0x1000 - (dest & 0xfff); + if (count > max) + count = max; + *pos = dest; + bytes -= count; + dest += count; + pos++; + } + return dest - (u32)buf; +} + +int +ehci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize + , void *data, int datasize) +{ + ASSERT32FLAT(); + if (! CONFIG_USB_EHCI) + return -1; + dprintf(5, "ehci_control %p\n", p); + if (datasize > 4*4096 || cmdsize > 4*4096) { + // XXX - should support larger sizes. + warn_noalloc(); + return -1; + } + struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe); + struct usb_ehci_s *cntl = container_of( + pipe->pipe.cntl, struct usb_ehci_s, usb); + + u16 maxpacket = pipe->pipe.maxpacket; + int speed = pipe->pipe.speed; + + // Setup fields in qh + pipe->qh.info1 = ( + (1 << QH_MULT_SHIFT) | (speed != USB_HIGHSPEED ? QH_CONTROL : 0) + | (maxpacket << QH_MAXPACKET_SHIFT) + | QH_TOGGLECONTROL + | (speed << QH_SPEED_SHIFT) + | (pipe->pipe.ep << QH_EP_SHIFT) + | (pipe->pipe.devaddr << QH_DEVADDR_SHIFT)); + pipe->qh.info2 = ((1 << QH_MULT_SHIFT) + | (pipe->pipe.tt_port << QH_HUBPORT_SHIFT) + | (pipe->pipe.tt_devaddr << QH_HUBADDR_SHIFT)); + + // Setup transfer descriptors + struct ehci_qtd *tds = memalign_tmphigh(EHCI_QTD_ALIGN, sizeof(*tds) * 3); + if (!tds) { + warn_noalloc(); + return -1; + } + memset(tds, 0, sizeof(*tds) * 3); + struct ehci_qtd *td = tds; + + td->qtd_next = (u32)&td[1]; + td->alt_next = EHCI_PTR_TERM; + td->token = (ehci_explen(cmdsize) | QTD_STS_ACTIVE + | QTD_PID_SETUP | ehci_maxerr(3)); + fillTDbuffer(td, maxpacket, cmd, cmdsize); + td++; + + if (datasize) { + td->qtd_next = (u32)&td[1]; + td->alt_next = EHCI_PTR_TERM; + td->token = (QTD_TOGGLE | ehci_explen(datasize) | QTD_STS_ACTIVE + | (dir ? QTD_PID_IN : QTD_PID_OUT) | ehci_maxerr(3)); + fillTDbuffer(td, maxpacket, data, datasize); + td++; + } + + td->qtd_next = EHCI_PTR_TERM; + td->alt_next = EHCI_PTR_TERM; + td->token = (QTD_TOGGLE | QTD_STS_ACTIVE + | (dir ? QTD_PID_OUT : QTD_PID_IN) | ehci_maxerr(3)); + + // Transfer data + barrier(); + pipe->qh.qtd_next = (u32)tds; + barrier(); + pipe->qh.token = 0; + int ret = ehci_wait_qh(cntl, &pipe->qh); + pipe->qh.token = QTD_STS_HALT; + if (ret) { + pipe->qh.qtd_next = pipe->qh.alt_next = EHCI_PTR_TERM; + // XXX - halt qh? + ehci_waittick(cntl); + } + free(tds); + return ret; +} + +struct usb_pipe * +ehci_alloc_bulk_pipe(struct usb_pipe *dummy) +{ + // XXX - this func is same as alloc_control except for malloc_low + if (! CONFIG_USB_EHCI) + return NULL; + struct usb_ehci_s *cntl = container_of( + dummy->cntl, struct usb_ehci_s, usb); + dprintf(7, "ehci_alloc_bulk_pipe %p\n", &cntl->usb); + + // Allocate a queue head. + struct ehci_pipe *pipe = memalign_low(EHCI_QH_ALIGN, sizeof(*pipe)); + if (!pipe) { + warn_noalloc(); + return NULL; + } + memset(pipe, 0, sizeof(*pipe)); + memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe)); + pipe->qh.qtd_next = pipe->qh.alt_next = EHCI_PTR_TERM; + pipe->qh.token = QTD_STS_HALT; + + // Add queue head to controller list. + struct ehci_qh *async_qh = cntl->async_qh; + pipe->qh.next = async_qh->next; + barrier(); + async_qh->next = (u32)&pipe->qh | EHCI_PTR_QH; + return &pipe->pipe; +} + +static int +ehci_wait_td(struct ehci_qtd *td) +{ + u64 end = calc_future_tsc(5000); // XXX - lookup real time. + u32 status; + for (;;) { + status = td->token; + if (!(status & QTD_STS_ACTIVE)) + break; + if (check_time(end)) { + warn_timeout(); + return -1; + } + yield(); + } + if (status & QTD_STS_HALT) { + dprintf(1, "ehci_wait_td error - status=%x\n", status); + return -2; + } + return 0; +} + +#define STACKQTDS 4 + +int +ehci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize) +{ + if (! CONFIG_USB_EHCI) + return -1; + struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe); + dprintf(7, "ehci_send_bulk qh=%p dir=%d data=%p size=%d\n" + , &pipe->qh, dir, data, datasize); + + // Allocate 4 tds on stack (16byte aligned) + u8 tdsbuf[sizeof(struct ehci_qtd) * STACKQTDS + EHCI_QTD_ALIGN - 1]; + struct ehci_qtd *tds = (void*)ALIGN((u32)tdsbuf, EHCI_QTD_ALIGN); + memset(tds, 0, sizeof(*tds) * STACKQTDS); + + // Setup fields in qh + u16 maxpacket = GET_FLATPTR(pipe->pipe.maxpacket); + SET_FLATPTR(pipe->qh.info1 + , ((1 << QH_MULT_SHIFT) + | (maxpacket << QH_MAXPACKET_SHIFT) + | (GET_FLATPTR(pipe->pipe.speed) << QH_SPEED_SHIFT) + | (GET_FLATPTR(pipe->pipe.ep) << QH_EP_SHIFT) + | (GET_FLATPTR(pipe->pipe.devaddr) << QH_DEVADDR_SHIFT))); + SET_FLATPTR(pipe->qh.info2 + , ((1 << QH_MULT_SHIFT) + | (GET_FLATPTR(pipe->pipe.tt_port) << QH_HUBPORT_SHIFT) + | (GET_FLATPTR(pipe->pipe.tt_devaddr) << QH_HUBADDR_SHIFT))); + barrier(); + SET_FLATPTR(pipe->qh.qtd_next, (u32)MAKE_FLATPTR(GET_SEG(SS), tds)); + barrier(); + SET_FLATPTR(pipe->qh.token, GET_FLATPTR(pipe->qh.token) & QTD_TOGGLE); + + int tdpos = 0; + while (datasize) { + struct ehci_qtd *td = &tds[tdpos++ % STACKQTDS]; + int ret = ehci_wait_td(td); + if (ret) + goto fail; + + struct ehci_qtd *nexttd_fl = MAKE_FLATPTR(GET_SEG(SS) + , &tds[tdpos % STACKQTDS]); + + int transfer = fillTDbuffer(td, maxpacket, data, datasize); + td->qtd_next = (transfer==datasize ? EHCI_PTR_TERM : (u32)nexttd_fl); + td->alt_next = EHCI_PTR_TERM; + barrier(); + td->token = (ehci_explen(transfer) | QTD_STS_ACTIVE + | (dir ? QTD_PID_IN : QTD_PID_OUT) | ehci_maxerr(3)); + + data += transfer; + datasize -= transfer; + } + int i; + for (i=0; i<STACKQTDS; i++) { + struct ehci_qtd *td = &tds[tdpos++ % STACKQTDS]; + int ret = ehci_wait_td(td); + if (ret) + goto fail; + } + + return 0; +fail: + dprintf(1, "ehci_send_bulk failed\n"); + SET_FLATPTR(pipe->qh.qtd_next, EHCI_PTR_TERM); + SET_FLATPTR(pipe->qh.alt_next, EHCI_PTR_TERM); + // XXX - halt qh? + struct usb_ehci_s *cntl = container_of( + GET_FLATPTR(pipe->pipe.cntl), struct usb_ehci_s, usb); + ehci_waittick(cntl); + return -1; +} + +struct usb_pipe * +ehci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp) +{ + if (! CONFIG_USB_EHCI) + return NULL; + struct usb_ehci_s *cntl = container_of( + dummy->cntl, struct usb_ehci_s, usb); + dprintf(7, "ehci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp); + + if (frameexp > 10) + frameexp = 10; + int maxpacket = dummy->maxpacket; + // 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 ehci_pipe *pipe = memalign_low(EHCI_QH_ALIGN, sizeof(*pipe)); + struct ehci_qtd *tds = memalign_low(EHCI_QTD_ALIGN, sizeof(*tds) * count); + void *data = malloc_low(maxpacket * count); + if (!pipe || !tds || !data) { + warn_noalloc(); + goto fail; + } + memset(pipe, 0, sizeof(*pipe)); + memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe)); + pipe->next_td = pipe->tds = tds; + pipe->data = data; + + pipe->qh.info1 = ( + (1 << QH_MULT_SHIFT) + | (maxpacket << QH_MAXPACKET_SHIFT) + | (pipe->pipe.speed << QH_SPEED_SHIFT) + | (pipe->pipe.ep << QH_EP_SHIFT) + | (pipe->pipe.devaddr << QH_DEVADDR_SHIFT)); + pipe->qh.info2 = ((1 << QH_MULT_SHIFT) + | (pipe->pipe.tt_port << QH_HUBPORT_SHIFT) + | (pipe->pipe.tt_devaddr << QH_HUBADDR_SHIFT) + | (0x01 << QH_SMASK_SHIFT) + | (0x1c << QH_CMASK_SHIFT)); + pipe->qh.qtd_next = (u32)tds; + + int i; + for (i=0; i<count; i++) { + struct ehci_qtd *td = &tds[i]; + td->qtd_next = (i==count-1 ? (u32)tds : (u32)&td[1]); + td->alt_next = EHCI_PTR_TERM; + td->token = (ehci_explen(maxpacket) | QTD_STS_ACTIVE + | QTD_PID_IN | ehci_maxerr(3)); + td->buf[0] = (u32)data + maxpacket * i; + } + + // Add to interrupt schedule. + struct ehci_framelist *fl = (void*)readl(&cntl->regs->periodiclistbase); + if (frameexp == 0) { + // Add to existing interrupt entry. + struct ehci_qh *intr_qh = (void*)(fl->links[0] & ~EHCI_PTR_BITS); + pipe->qh.next = intr_qh->next; + barrier(); + intr_qh->next = (u32)&pipe->qh | EHCI_PTR_QH; + } else { + int startpos = 1<<(frameexp-1); + pipe->qh.next = fl->links[startpos]; + barrier(); + for (i=startpos; i<ARRAY_SIZE(fl->links); i+=ms) + fl->links[i] = (u32)&pipe->qh | EHCI_PTR_QH; + } + + return &pipe->pipe; +fail: + free(pipe); + free(tds); + free(data); + return NULL; +} + +int +ehci_poll_intr(struct usb_pipe *p, void *data) +{ + ASSERT16(); + if (! CONFIG_USB_EHCI) + return -1; + struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe); + struct ehci_qtd *td = GET_FLATPTR(pipe->next_td); + u32 token = GET_FLATPTR(td->token); + if (token & QTD_STS_ACTIVE) + // No intrs found. + return -1; + // XXX - check for errors. + + // Copy data. + int maxpacket = GET_FLATPTR(pipe->pipe.maxpacket); + int pos = td - GET_FLATPTR(pipe->tds); + void *tddata = GET_FLATPTR(pipe->data) + maxpacket * pos; + memcpy_far(GET_SEG(SS), data + , FLATPTR_TO_SEG(tddata), (void*)FLATPTR_TO_OFFSET(tddata) + , maxpacket); + + // Reenable this td. + struct ehci_qtd *next = (void*)(GET_FLATPTR(td->qtd_next) & ~EHCI_PTR_BITS); + SET_FLATPTR(pipe->next_td, next); + SET_FLATPTR(td->buf[0], (u32)tddata); + barrier(); + SET_FLATPTR(td->token, (ehci_explen(maxpacket) | QTD_STS_ACTIVE + | QTD_PID_IN | ehci_maxerr(3))); + + return 0; +} diff --git a/src/usb-ehci.h b/src/usb-ehci.h new file mode 100644 index 0000000..bb8df52 --- /dev/null +++ b/src/usb-ehci.h @@ -0,0 +1,173 @@ +#ifndef __USB_EHCI_H +#define __USB_EHCI_H + +// usb-ehci.c +int ehci_init(u16 bdf, int busid, int compbdf); +struct usb_pipe; +void ehci_free_pipe(struct usb_pipe *p); +struct usb_pipe *ehci_alloc_control_pipe(struct usb_pipe *dummy); +int ehci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize + , void *data, int datasize); +struct usb_pipe *ehci_alloc_bulk_pipe(struct usb_pipe *dummy); +int ehci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize); +struct usb_pipe *ehci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp); +int ehci_poll_intr(struct usb_pipe *p, void *data); + + +/**************************************************************** + * ehci structs and flags + ****************************************************************/ + +struct ehci_caps { + u8 caplength; + u8 reserved_01; + u16 hciversion; + u32 hcsparams; + u32 hccparams; + u64 portroute; +} PACKED; + +#define HCC_64BIT_ADDR 1 + +#define HCS_N_PORTS_MASK 0xf + +struct ehci_regs { + u32 usbcmd; + u32 usbsts; + u32 usbintr; + u32 frindex; + u32 ctrldssegment; + u32 periodiclistbase; + u32 asynclistbase; + u32 reserved[9]; + u32 configflag; + u32 portsc[0]; +} PACKED; + +#define CMD_PARK (1<<11) +#define CMD_PARK_CNT(c) (((c)>>8)&3) +#define CMD_LRESET (1<<7) +#define CMD_IAAD (1<<6) +#define CMD_ASE (1<<5) +#define CMD_PSE (1<<4) +#define CMD_HCRESET (1<<1) +#define CMD_RUN (1<<0) + +#define STS_ASS (1<<15) +#define STS_PSS (1<<14) +#define STS_RECL (1<<13) +#define STS_HALT (1<<12) +#define STS_IAA (1<<5) +#define STS_FATAL (1<<4) +#define STS_FLR (1<<3) +#define STS_PCD (1<<2) +#define STS_ERR (1<<1) +#define STS_INT (1<<0) + +#define FLAG_CF (1<<0) + +#define PORT_WKOC_E (1<<22) +#define PORT_WKDISC_E (1<<21) +#define PORT_WKCONN_E (1<<20) +#define PORT_TEST_PKT (0x4<<16) +#define PORT_LED_OFF (0<<14) +#define PORT_LED_AMBER (1<<14) +#define PORT_LED_GREEN (2<<14) +#define PORT_LED_MASK (3<<14) +#define PORT_OWNER (1<<13) +#define PORT_POWER (1<<12) +#define PORT_LINESTATUS_MASK (3<<10) +#define PORT_LINESTATUS_KSTATE (1<<10) +#define PORT_RESET (1<<8) +#define PORT_SUSPEND (1<<7) +#define PORT_RESUME (1<<6) +#define PORT_OCC (1<<5) +#define PORT_OC (1<<4) +#define PORT_PEC (1<<3) +#define PORT_PE (1<<2) +#define PORT_CSC (1<<1) +#define PORT_CONNECT (1<<0) +#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) + + +#define EHCI_QH_ALIGN 64 // Can't span a 4K boundary, so increase to 64 + +struct ehci_qh { + u32 next; + u32 info1; + u32 info2; + u32 current; + + u32 qtd_next; + u32 alt_next; + u32 token; + u32 buf[5]; + // u32 buf_hi[5]; +} PACKED; + +#define QH_CONTROL (1 << 27) +#define QH_MAXPACKET_SHIFT 16 +#define QH_MAXPACKET_MASK (0x7ff << QH_MAXPACKET_SHIFT) +#define QH_HEAD (1 << 15) +#define QH_TOGGLECONTROL (1 << 14) +#define QH_SPEED_SHIFT 12 +#define QH_SPEED_MASK (0x3 << QH_SPEED_SHIFT) +#define QH_EP_SHIFT 8 +#define QH_EP_MASK (0xf << QH_EP_SHIFT) +#define QH_DEVADDR_SHIFT 0 +#define QH_DEVADDR_MASK (0x7f << QH_DEVADDR_SHIFT) + +#define QH_SMASK_SHIFT 0 +#define QH_SMASK_MASK (0xff << QH_SMASK_SHIFT) +#define QH_CMASK_SHIFT 8 +#define QH_CMASK_MASK (0xff << QH_CMASK_SHIFT) +#define QH_HUBADDR_SHIFT 16 +#define QH_HUBADDR_MASK (0x7f << QH_HUBADDR_SHIFT) +#define QH_HUBPORT_SHIFT 23 +#define QH_HUBPORT_MASK (0x7f << QH_HUBPORT_SHIFT) +#define QH_MULT_SHIFT 30 +#define QH_MULT_MASK (0x3 << QH_MULT_SHIFT) + +#define EHCI_PTR_BITS 0x001F +#define EHCI_PTR_TERM 0x0001 +#define EHCI_PTR_QH 0x0002 + + +#define EHCI_QTD_ALIGN 32 + +struct ehci_qtd { + u32 qtd_next; + u32 alt_next; + u32 token; + u32 buf[5]; + //u32 buf_hi[5]; +} PACKED; + +#define QTD_TOGGLE (1 << 31) +#define QTD_LENGTH_SHIFT 16 +#define QTD_LENGTH_MASK (0x7fff << QTD_LENGTH_SHIFT) +#define QTD_CERR_SHIFT 10 +#define QTD_CERR_MASK (0x3 << QTD_CERR_SHIFT) +#define QTD_IOC (1 << 15) +#define QTD_PID_OUT (0x0 << 8) +#define QTD_PID_IN (0x1 << 8) +#define QTD_PID_SETUP (0x2 << 8) +#define QTD_STS_ACTIVE (1 << 7) +#define QTD_STS_HALT (1 << 6) +#define QTD_STS_DBE (1 << 5) +#define QTD_STS_BABBLE (1 << 4) +#define QTD_STS_XACT (1 << 3) +#define QTD_STS_MMF (1 << 2) +#define QTD_STS_STS (1 << 1) +#define QTD_STS_PING (1 << 0) + +#define ehci_explen(len) (((len) << QTD_LENGTH_SHIFT) & QTD_LENGTH_MASK) + +#define ehci_maxerr(err) (((err) << QTD_CERR_SHIFT) & QTD_CERR_MASK) + + +struct ehci_framelist { + u32 links[1024]; +} PACKED; + +#endif // usb-ehci.h diff --git a/src/usb-hub.c b/src/usb-hub.c index ce40099..9effbc3 100644 --- a/src/usb-hub.c +++ b/src/usb-hub.c @@ -126,7 +126,8 @@ init_hub_port(void *data)
// Set address of port struct usb_pipe *pipe = usb_set_address( - hub->cntl, !!(sts.wPortStatus & USB_PORT_STAT_LOW_SPEED)); + hub, port, ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK) + >> USB_PORT_STAT_SPEED_SHIFT)); if (!pipe) goto resetfail; mutex_unlock(&hub->cntl->resetlock); diff --git a/src/usb-hub.h b/src/usb-hub.h index 399df57..0994320 100644 --- a/src/usb-hub.h +++ b/src/usb-hub.h @@ -60,6 +60,8 @@ struct usb_port_status { #define USB_PORT_STAT_RESET 0x0010 #define USB_PORT_STAT_L1 0x0020 #define USB_PORT_STAT_POWER 0x0100 +#define USB_PORT_STAT_SPEED_SHIFT 9 +#define USB_PORT_STAT_SPEED_MASK (0x3 << USB_PORT_STAT_SPEED_SHIFT) #define USB_PORT_STAT_LOW_SPEED 0x0200 #define USB_PORT_STAT_HIGH_SPEED 0x0400 #define USB_PORT_STAT_TEST 0x0800 diff --git a/src/usb-ohci.c b/src/usb-ohci.c index 5fb7fec..8bf8d75 100644 --- a/src/usb-ohci.c +++ b/src/usb-ohci.c @@ -61,7 +61,7 @@ init_ohci_port(void *data) goto resetfail;
// Set address of port - struct usb_pipe *pipe = usb_set_address(&cntl->usb, !!(sts & RH_PS_LSDA)); + struct usb_pipe *pipe = usb_set_address(hub, port, !!(sts & RH_PS_LSDA)); if (!pipe) goto resetfail; mutex_unlock(&cntl->usb.resetlock); @@ -378,7 +378,7 @@ ohci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize struct usb_ohci_s *cntl = container_of( pipe->pipe.cntl, struct usb_ohci_s, usb); int maxpacket = pipe->pipe.maxpacket; - int lowspeed = pipe->pipe.lowspeed; + int lowspeed = pipe->pipe.speed; int devaddr = pipe->pipe.devaddr | (pipe->pipe.ep << 7);
// Setup transfer descriptors @@ -435,7 +435,7 @@ ohci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp) if (frameexp > 5) frameexp = 5; int maxpacket = dummy->maxpacket; - int lowspeed = dummy->lowspeed; + int lowspeed = dummy->speed; int devaddr = dummy->devaddr | (dummy->ep << 7); // Determine number of entries needed for 2 timer ticks. int ms = 1<<frameexp; diff --git a/src/usb-uhci.c b/src/usb-uhci.c index c26b95b..f666ab7 100644 --- a/src/usb-uhci.c +++ b/src/usb-uhci.c @@ -53,7 +53,7 @@ init_uhci_port(void *data) goto resetfail; outw(USBPORTSC_PE, ioport); struct usb_pipe *pipe = usb_set_address( - &cntl->usb, !!(status & USBPORTSC_LSDA)); + hub, port, !!(status & USBPORTSC_LSDA)); if (!pipe) goto resetfail; mutex_unlock(&cntl->usb.resetlock); @@ -334,7 +334,7 @@ uhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize pipe->pipe.cntl, struct usb_uhci_s, usb);
int maxpacket = pipe->pipe.maxpacket; - int lowspeed = pipe->pipe.lowspeed; + int lowspeed = pipe->pipe.speed; int devaddr = pipe->pipe.devaddr | (pipe->pipe.ep << 7);
// Setup transfer descriptors @@ -447,7 +447,7 @@ uhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize) dprintf(7, "uhci_send_bulk qh=%p dir=%d data=%p size=%d\n" , &pipe->qh, dir, data, datasize); int maxpacket = GET_FLATPTR(pipe->pipe.maxpacket); - int lowspeed = GET_FLATPTR(pipe->pipe.lowspeed); + int lowspeed = GET_FLATPTR(pipe->pipe.speed); int devaddr = (GET_FLATPTR(pipe->pipe.devaddr) | (GET_FLATPTR(pipe->pipe.ep) << 7)); int toggle = GET_FLATPTR(pipe->toggle) ? TD_TOKEN_TOGGLE : 0; @@ -515,7 +515,7 @@ uhci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp) if (frameexp > 10) frameexp = 10; int maxpacket = dummy->maxpacket; - int lowspeed = dummy->lowspeed; + int lowspeed = dummy->speed; int devaddr = dummy->devaddr | (dummy->ep << 7); // Determine number of entries needed for 2 timer ticks. int ms = 1<<frameexp; diff --git a/src/usb.c b/src/usb.c index 9575185..8b3c36e 100644 --- a/src/usb.c +++ b/src/usb.c @@ -11,6 +11,7 @@ #include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI #include "usb-uhci.h" // uhci_init #include "usb-ohci.h" // ohci_init +#include "usb-ehci.h" // ehci_init #include "usb-hid.h" // usb_keyboard_setup #include "usb-hub.h" // usb_hub_init #include "usb-msc.h" // usb_msc_init @@ -35,6 +36,8 @@ free_pipe(struct usb_pipe *pipe) return uhci_free_pipe(pipe); case USB_TYPE_OHCI: return ohci_free_pipe(pipe); + case USB_TYPE_EHCI: + return ehci_free_pipe(pipe); } }
@@ -49,6 +52,8 @@ alloc_default_control_pipe(struct usb_pipe *dummy) return uhci_alloc_control_pipe(dummy); case USB_TYPE_OHCI: return ohci_alloc_control_pipe(dummy); + case USB_TYPE_EHCI: + return ehci_alloc_control_pipe(dummy); } }
@@ -64,6 +69,8 @@ send_control(struct usb_pipe *pipe, int dir, const void *cmd, int cmdsize return uhci_control(pipe, dir, cmd, cmdsize, data, datasize); case USB_TYPE_OHCI: return ohci_control(pipe, dir, cmd, cmdsize, data, datasize); + case USB_TYPE_EHCI: + return ehci_control(pipe, dir, cmd, cmdsize, data, datasize); } }
@@ -88,6 +95,8 @@ alloc_bulk_pipe(struct usb_pipe *pipe, struct usb_endpoint_descriptor *epdesc) return uhci_alloc_bulk_pipe(&dummy); case USB_TYPE_OHCI: return NULL; + case USB_TYPE_EHCI: + return ehci_alloc_bulk_pipe(&dummy); } }
@@ -100,6 +109,8 @@ usb_send_bulk(struct usb_pipe *pipe_fl, int dir, void *data, int datasize) return uhci_send_bulk(pipe_fl, dir, data, datasize); case USB_TYPE_OHCI: return -1; + case USB_TYPE_EHCI: + return ehci_send_bulk(pipe_fl, dir, data, datasize); } }
@@ -110,15 +121,19 @@ alloc_intr_pipe(struct usb_pipe *pipe, struct usb_endpoint_descriptor *epdesc) desc2pipe(&dummy, pipe, epdesc); // Find the exponential period of the requested time. int period = epdesc->bInterval; - if (period <= 0) - period = 1; - int frameexp = __fls(period); + int frameexp; + if (pipe->speed != USB_HIGHSPEED) + frameexp = (period <= 0) ? 0 : __fls(period); + else + frameexp = (period <= 4) ? 0 : period - 4; switch (pipe->type) { default: case USB_TYPE_UHCI: return uhci_alloc_intr_pipe(&dummy, frameexp); case USB_TYPE_OHCI: return ohci_alloc_intr_pipe(&dummy, frameexp); + case USB_TYPE_EHCI: + return ehci_alloc_intr_pipe(&dummy, frameexp); } }
@@ -131,6 +146,8 @@ usb_poll_intr(struct usb_pipe *pipe_fl, void *data) return uhci_poll_intr(pipe_fl, data); case USB_TYPE_OHCI: return ohci_poll_intr(pipe_fl, data); + case USB_TYPE_EHCI: + return ehci_poll_intr(pipe_fl, data); } }
@@ -226,9 +243,10 @@ set_configuration(struct usb_pipe *pipe, u16 val) // Assign an address to a device in the default state on the given // controller. struct usb_pipe * -usb_set_address(struct usb_s *cntl, int lowspeed) +usb_set_address(struct usbhub_s *hub, int port, int speed) { ASSERT32FLAT(); + struct usb_s *cntl = hub->cntl; dprintf(3, "set_address %p\n", cntl); if (cntl->maxaddr >= USB_MAXADDR) return NULL; @@ -245,7 +263,18 @@ usb_set_address(struct usb_s *cntl, int lowspeed) if (!defpipe) return NULL; } - defpipe->lowspeed = lowspeed; + defpipe->speed = speed; + if (hub->pipe) { + if (hub->pipe->speed == USB_HIGHSPEED) { + defpipe->tt_devaddr = hub->pipe->devaddr; + defpipe->tt_port = port; + } else { + defpipe->tt_devaddr = hub->pipe->tt_devaddr; + defpipe->tt_port = hub->pipe->tt_port; + } + } else { + defpipe->tt_devaddr = defpipe->tt_port = 0; + }
msleep(USB_TIME_RSTRCY);
@@ -339,6 +368,7 @@ usb_setup(void) usb_keyboard_setup();
// Look for USB controllers + int ehcibdf = -1; int count = 0; int bdf, max; foreachpci(bdf, max) { @@ -347,13 +377,37 @@ usb_setup(void) if (code >> 8 != PCI_CLASS_SERIAL_USB) continue;
+ if (bdf > ehcibdf) { + // Check to see if this device has an ehci controller + ehcibdf = bdf; + u32 ehcicode = code; + int found = 0; + for (;;) { + if (ehcicode == PCI_CLASS_SERIAL_USB_EHCI) { + // Found an ehci controller. + int ret = ehci_init(ehcibdf, count++, bdf); + if (ret) + // Error + break; + count += found; + bdf = ehcibdf; + code = 0; + break; + } + if (ehcicode >> 8 == PCI_CLASS_SERIAL_USB) + found++; + ehcibdf = pci_next(ehcibdf+1, &max); + if (ehcibdf < 0 + || pci_bdf_to_busdev(ehcibdf) != pci_bdf_to_busdev(bdf)) + // No ehci controller found. + break; + ehcicode = pci_config_readl(ehcibdf, PCI_CLASS_REVISION) >> 8; + } + } + if (code == PCI_CLASS_SERIAL_USB_UHCI) - uhci_init(bdf, count); + uhci_init(bdf, count++); else if (code == PCI_CLASS_SERIAL_USB_OHCI) - ohci_init(bdf, count); - else - continue; - - count++; + ohci_init(bdf, count++); } } diff --git a/src/usb.h b/src/usb.h index 10f824f..b5cbf2e 100644 --- a/src/usb.h +++ b/src/usb.h @@ -9,8 +9,10 @@ struct usb_pipe { u8 type; u8 ep; u8 devaddr; - u8 lowspeed; + u8 speed; u16 maxpacket; + u8 tt_devaddr; + u8 tt_port; };
// Common information for usb controllers. @@ -24,6 +26,11 @@ struct usb_s {
#define USB_TYPE_UHCI 1 #define USB_TYPE_OHCI 2 +#define USB_TYPE_EHCI 3 + +#define USB_FULLSPEED 0 +#define USB_LOWSPEED 1 +#define USB_HIGHSPEED 2
#define USB_MAXADDR 127
@@ -168,7 +175,8 @@ struct usb_endpoint_descriptor {
// usb.c void usb_setup(void); -struct usb_pipe *usb_set_address(struct usb_s *cntl, int lowspeed); +struct usbhub_s; +struct usb_pipe *usb_set_address(struct usbhub_s *hub, int port, int speed); int configure_usb_device(struct usb_pipe *pipe); int send_default_control(struct usb_pipe *pipe, const struct usb_ctrlrequest *req , void *data);