X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=src%2Fusb-uhci.c;h=a78dbcaf63e679ac55d1c8f3fc11fd93454ec6b0;hb=refs%2Fheads%2Fcoreboot;hp=ca513374b36975c51bba77478cc5c2506706db0c;hpb=9571439d9169bbd338de31c1f308404c86d57b1b;p=seabios.git diff --git a/src/usb-uhci.c b/src/usb-uhci.c index ca51337..a78dbca 100644 --- a/src/usb-uhci.c +++ b/src/usb-uhci.c @@ -13,40 +13,120 @@ #include "usb.h" // struct usb_s #include "farptr.h" // GET_FLATPTR +struct usb_uhci_s { + struct usb_s usb; + u16 iobase; + struct uhci_qh *control_qh, *bulk_qh; + struct uhci_framelist *framelist; +}; + + +/**************************************************************** + * Root hub + ****************************************************************/ + +// Check if device attached to a given port +static int +uhci_hub_detect(struct usbhub_s *hub, u32 port) +{ + 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)) + // No device + return -1; + + // XXX - if just powered up, need to wait for USB_TIME_ATTDB? + + // Begin reset on port + outw(USBPORTSC_PR, ioport); + msleep(USB_TIME_DRSTR); + return 0; +} + +// Reset device on port +static int +uhci_hub_reset(struct usbhub_s *hub, u32 port) +{ + struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb); + u16 ioport = cntl->iobase + USBPORTSC1 + port * 2; + + // Finish reset on port + outw(0, ioport); + udelay(6); // 64 high-speed bit times + u16 status = inw(ioport); + if (!(status & USBPORTSC_CCS)) + // No longer connected + return -1; + outw(USBPORTSC_PE, ioport); + return !!(status & USBPORTSC_LSDA); +} + +// Disable port +static void +uhci_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb); + u16 ioport = cntl->iobase + USBPORTSC1 + port * 2; + outw(0, ioport); +} + +static struct usbhub_op_s uhci_HubOp = { + .detect = uhci_hub_detect, + .reset = uhci_hub_reset, + .disconnect = uhci_hub_disconnect, +}; + +// Find any devices connected to the root hub. +static int +check_uhci_ports(struct usb_uhci_s *cntl) +{ + ASSERT32FLAT(); + struct usbhub_s hub; + memset(&hub, 0, sizeof(hub)); + hub.cntl = &cntl->usb; + hub.portcount = 2; + hub.op = &uhci_HubOp; + usb_enumerate(&hub); + return hub.devcount; +} + + +/**************************************************************** + * Setup + ****************************************************************/ + 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)); struct uhci_qh *intr_qh = malloc_high(sizeof(*intr_qh)); - struct uhci_qh *data_qh = malloc_high(sizeof(*data_qh)); struct uhci_qh *term_qh = malloc_high(sizeof(*term_qh)); - if (!term_td || !fl || !intr_qh || !data_qh || !term_qh) { + if (!term_td || !fl || !intr_qh || !term_qh) { warn_noalloc(); - free(term_td); - free(fl); - free(intr_qh); - free(data_qh); - free(term_qh); - return; + goto fail; } // Work around for PIIX errata @@ -58,159 +138,214 @@ configure_uhci(struct usb_s *cntl) term_qh->element = (u32)term_td; term_qh->link = UHCI_PTR_TERM; - // Setup primary queue head. - memset(data_qh, 0, sizeof(*data_qh)); - data_qh->element = UHCI_PTR_TERM; - data_qh->link = (u32)term_qh | UHCI_PTR_QH; - cntl->uhci.qh = data_qh; - // Set schedule to point to primary intr queue head memset(intr_qh, 0, sizeof(*intr_qh)); intr_qh->element = UHCI_PTR_TERM; - intr_qh->link = (u32)data_qh | UHCI_PTR_QH; + intr_qh->link = (u32)term_qh | UHCI_PTR_QH; int i; for (i=0; ilinks); i++) fl->links[i] = (u32)intr_qh | UHCI_PTR_QH; - cntl->uhci.framelist = fl; + 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 any devices connected to the root hub. -static int -check_ports(struct usb_s *cntl) -{ - // XXX - if just powered up, need to wait for USB_TIME_SIGATT? - u16 port1 = inw(cntl->uhci.iobase + USBPORTSC1); - u16 port2 = inw(cntl->uhci.iobase + USBPORTSC2); - - if (!((port1 & USBPORTSC_CCS) || (port2 & USBPORTSC_CCS))) - // No devices - return 0; - - // XXX - if just powered up, need to wait for USB_TIME_ATTDB? - - // reset ports - if (port1 & USBPORTSC_CCS) - outw(USBPORTSC_PR, cntl->uhci.iobase + USBPORTSC1); - if (port2 & USBPORTSC_CCS) - outw(USBPORTSC_PR, cntl->uhci.iobase + USBPORTSC2); - msleep(USB_TIME_DRSTR); + // Find devices + int count = check_uhci_ports(cntl); + free_pipe(cntl->usb.defaultpipe); + if (count) + // Success + return; - // Configure ports - int totalcount = 0; - outw(0, cntl->uhci.iobase + USBPORTSC1); - udelay(6); // 64 high-speed bit times - port1 = inw(cntl->uhci.iobase + USBPORTSC1); - if (port1 & USBPORTSC_CCS) { - outw(USBPORTSC_PE, cntl->uhci.iobase + USBPORTSC1); - msleep(USB_TIME_RSTRCY); - int count = configure_usb_device(cntl, !!(port1 & USBPORTSC_LSDA)); - if (! count) - outw(0, cntl->uhci.iobase + USBPORTSC1); - totalcount += count; - } - outw(0, cntl->uhci.iobase + USBPORTSC2); - udelay(6); - port2 = inw(cntl->uhci.iobase + USBPORTSC2); - if (port2 & USBPORTSC_CCS) { - outw(USBPORTSC_PE, cntl->uhci.iobase + USBPORTSC2); - msleep(USB_TIME_RSTRCY); - int count = configure_usb_device(cntl, !!(port2 & USBPORTSC_LSDA)); - if (! count) - outw(0, cntl->uhci.iobase + USBPORTSC2); - totalcount += count; - } - return totalcount; + // No devices found - shutdown and free controller. + outw(0, cntl->iobase + USBCMD); +fail: + free(term_td); + free(fl); + free(intr_qh); + free(term_qh); + free(cntl); } void -uhci_init(void *data) +uhci_init(struct pci_device *pci, int busid) { if (! CONFIG_USB_UHCI) return; - struct usb_s *cntl = data; + u16 bdf = pci->bdf; + struct usb_uhci_s *cntl = malloc_tmphigh(sizeof(*cntl)); + if (!cntl) { + warn_noalloc(); + return; + } + memset(cntl, 0, sizeof(*cntl)); + cntl->usb.busid = busid; + cntl->usb.pci = pci; + cntl->usb.type = USB_TYPE_UHCI; + cntl->iobase = (pci_config_readl(bdf, PCI_BASE_ADDRESS_4) + & PCI_BASE_ADDRESS_IO_MASK); + + dprintf(1, "UHCI init on dev %02x:%02x.%x (io=%x)\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf) + , pci_bdf_to_fn(bdf), cntl->iobase); - // 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); + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); - 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); + reset_uhci(cntl, bdf); + + run_thread(configure_uhci, cntl); +} - pci_config_maskw(cntl->bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); - reset_uhci(cntl); - configure_uhci(cntl); - start_uhci(cntl); +/**************************************************************** + * End point communication + ****************************************************************/ - int count = check_ports(cntl); - if (! count) { - // XXX - no devices; free data structures. +struct uhci_pipe { + struct uhci_qh qh; + struct uhci_td *next_td; + struct usb_pipe pipe; + u16 iobase; + u8 toggle; +}; + +// Wait for next USB frame to start - for ensuring safe memory release. +static void +uhci_waittick(u16 iobase) +{ + barrier(); + u16 startframe = inw(iobase + USBFRNUM); + u64 end = calc_future_tsc(1000 * 5); + for (;;) { + if (inw(iobase + USBFRNUM) != startframe) + break; + if (check_tsc(end)) { + warn_timeout(); + return; + } + yield(); } } static int -wait_qh(struct usb_s *cntl, struct uhci_qh *qh) +wait_pipe(struct uhci_pipe *pipe, int timeout) { - // XXX - 500ms just a guess - u64 end = calc_future_tsc(500); + u64 end = calc_future_tsc(timeout); for (;;) { - if (qh->element & UHCI_PTR_TERM) + u32 el_link = GET_FLATPTR(pipe->qh.element); + if (el_link & UHCI_PTR_TERM) return 0; - if (check_time(end)) { + if (check_tsc(end)) { warn_timeout(); - 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)); + u16 iobase = GET_FLATPTR(pipe->iobase); + struct uhci_td *td = (void*)(el_link & ~UHCI_PTR_BITS); + dprintf(1, "Timeout on wait_pipe %p (td=%p s=%x c=%x/%x)\n" + , pipe, (void*)el_link, GET_FLATPTR(td->status) + , inw(iobase + USBCMD) + , inw(iobase + USBSTS)); + SET_FLATPTR(pipe->qh.element, UHCI_PTR_TERM); + uhci_waittick(iobase); return -1; } yield(); } } -static void -uhci_waittick(void) +void +uhci_free_pipe(struct usb_pipe *p) { - // XXX - implement real tick detection. - msleep(2); + if (! CONFIG_USB_UHCI) + return; + dprintf(7, "uhci_free_pipe %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 uhci_qh *pos = (void*)(cntl->framelist->links[0] & ~UHCI_PTR_BITS); + for (;;) { + u32 link = pos->link; + if (link == UHCI_PTR_TERM) { + // Not found?! Exit without freeing. + warn_internalerror(); + return; + } + struct uhci_qh *next = (void*)(link & ~UHCI_PTR_BITS); + if (next == &pipe->qh) { + pos->link = next->link; + 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; + } + pos = next; + } +} + +struct usb_pipe * +uhci_alloc_control_pipe(struct usb_pipe *dummy) +{ + if (! CONFIG_USB_UHCI) + return NULL; + 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)); + if (!pipe) { + warn_noalloc(); + return NULL; + } + memset(pipe, 0, sizeof(*pipe)); + memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe)); + pipe->qh.element = UHCI_PTR_TERM; + pipe->iobase = cntl->iobase; + + // Add queue head to controller list. + 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->bulk_qh == control_qh) + cntl->bulk_qh = &pipe->qh; + return &pipe->pipe; } int -uhci_control(u32 endp, int dir, const void *cmd, int cmdsize +uhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize , void *data, int datasize) { + ASSERT32FLAT(); if (! CONFIG_USB_UHCI) return -1; + dprintf(5, "uhci_control %p\n", p); + struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe); - dprintf(5, "uhci_control %x\n", endp); - struct usb_s *cntl = endp2cntl(endp); - int maxpacket = endp2maxsize(endp); - int lowspeed = endp2speed(endp); - int devaddr = endp2devaddr(endp) | (endp2ep(endp) << 7); + int maxpacket = pipe->pipe.maxpacket; + int lowspeed = pipe->pipe.speed; + int devaddr = pipe->pipe.devaddr | (pipe->pipe.ep << 7); // 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) @@ -240,43 +375,155 @@ uhci_control(u32 endp, int dir, const void *cmd, int cmdsize tds[i].buffer = 0; // Transfer data - struct uhci_qh *data_qh = cntl->uhci.qh; barrier(); - data_qh->element = (u32)&tds[0]; - int ret = wait_qh(cntl, data_qh); - if (ret) { - data_qh->element = UHCI_PTR_TERM; - uhci_waittick(); - } + pipe->qh.element = (u32)&tds[0]; + int ret = wait_pipe(pipe, 500); free(tds); return ret; } struct usb_pipe * -uhci_alloc_intr_pipe(u32 endp, int frameexp) +uhci_alloc_bulk_pipe(struct usb_pipe *dummy) +{ + if (! CONFIG_USB_UHCI) + return NULL; + 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)); + if (!pipe) { + warn_noalloc(); + return NULL; + } + memset(pipe, 0, sizeof(*pipe)); + memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe)); + pipe->qh.element = UHCI_PTR_TERM; + pipe->iobase = cntl->iobase; + + // Add queue head to controller list. + struct uhci_qh *bulk_qh = cntl->bulk_qh; + pipe->qh.link = bulk_qh->link; + barrier(); + bulk_qh->link = (u32)&pipe->qh | UHCI_PTR_QH; + + return &pipe->pipe; +} + +static int +wait_td(struct uhci_td *td) +{ + u64 end = calc_future_tsc(5000); // XXX - lookup real time. + u32 status; + for (;;) { + status = td->status; + if (!(status & TD_CTRL_ACTIVE)) + break; + if (check_tsc(end)) { + warn_timeout(); + return -1; + } + yield(); + } + if (status & TD_CTRL_ANY_ERROR) { + dprintf(1, "wait_td error - status=%x\n", status); + return -2; + } + return 0; +} + +#define STACKTDS 4 +#define TDALIGN 16 + +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); + int maxpacket = GET_FLATPTR(pipe->pipe.maxpacket); + 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; + + // Allocate 4 tds on stack (16byte aligned) + u8 tdsbuf[sizeof(struct uhci_td) * STACKTDS + TDALIGN - 1]; + struct uhci_td *tds = (void*)ALIGN((u32)tdsbuf, TDALIGN); + memset(tds, 0, sizeof(*tds) * STACKTDS); + + // Enable tds + barrier(); + SET_FLATPTR(pipe->qh.element, (u32)MAKE_FLATPTR(GET_SEG(SS), tds)); + + int tdpos = 0; + while (datasize) { + struct uhci_td *td = &tds[tdpos++ % STACKTDS]; + int ret = wait_td(td); + if (ret) + goto fail; + + int transfer = datasize; + if (transfer > maxpacket) + transfer = maxpacket; + struct uhci_td *nexttd_fl = MAKE_FLATPTR(GET_SEG(SS) + , &tds[tdpos % STACKTDS]); + td->link = (transfer==datasize ? UHCI_PTR_TERM : (u32)nexttd_fl); + td->token = (uhci_explen(transfer) | toggle + | (devaddr << TD_TOKEN_DEVADDR_SHIFT) + | (dir ? USB_PID_IN : USB_PID_OUT)); + td->buffer = data; + barrier(); + td->status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0) + | TD_CTRL_ACTIVE); + toggle ^= TD_TOKEN_TOGGLE; + + data += transfer; + datasize -= transfer; + } + SET_FLATPTR(pipe->toggle, !!toggle); + return wait_pipe(pipe, 5000); +fail: + dprintf(1, "uhci_send_bulk failed\n"); + SET_FLATPTR(pipe->qh.element, UHCI_PTR_TERM); + uhci_waittick(GET_FLATPTR(pipe->iobase)); + return -1; +} + +struct usb_pipe * +uhci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp) { if (! CONFIG_USB_UHCI) return NULL; + 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); - dprintf(7, "uhci_alloc_intr_pipe %x %d\n", endp, frameexp); if (frameexp > 10) frameexp = 10; - struct usb_s *cntl = endp2cntl(endp); - int maxpacket = endp2maxsize(endp); - int lowspeed = endp2speed(endp); - int devaddr = endp2devaddr(endp) | (endp2ep(endp) << 7); + int maxpacket = dummy->maxpacket; + int lowspeed = dummy->speed; + int devaddr = dummy->devaddr | (dummy->ep << 7); // Determine number of entries needed for 2 timer ticks. int ms = 1< sizeof(tds[0].data)) - goto fail; - qh->element = (u32)tds; + 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; + int toggle = 0; int i; for (i=0; inext_td = &tds[0]; - qh->pipe.endp = endp; - // 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); - qh->link = intr_qh->link; + pipe->qh.link = intr_qh->link; barrier(); - intr_qh->link = (u32)qh | UHCI_PTR_QH; + intr_qh->link = (u32)&pipe->qh | UHCI_PTR_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); - qh->link = fl->links[startpos]; + pipe->qh.link = fl->links[startpos]; barrier(); for (i=startpos; ilinks); i+=ms) - fl->links[i] = (u32)qh | UHCI_PTR_QH; + fl->links[i] = (u32)&pipe->qh | UHCI_PTR_QH; } - return &qh->pipe; + return &pipe->pipe; fail: - free(qh); + free(pipe); free(tds); + free(data); return NULL; } int -uhci_poll_intr(struct usb_pipe *pipe, void *data) +uhci_poll_intr(struct usb_pipe *p, void *data) { ASSERT16(); if (! CONFIG_USB_UHCI) return -1; - struct uhci_qh *qh = container_of(pipe, struct uhci_qh, pipe); - struct uhci_td *td = GET_FLATPTR(qh->next_td); + struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe); + struct uhci_td *td = GET_FLATPTR(pipe->next_td); u32 status = GET_FLATPTR(td->status); u32 token = GET_FLATPTR(td->token); if (status & TD_CTRL_ACTIVE) @@ -333,16 +582,17 @@ uhci_poll_intr(struct usb_pipe *pipe, 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(qh->next_td, (void*)(next & ~UHCI_PTR_BITS)); return 0; }