# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.649 -> 1.650 # drivers/usb/host/ehci-q.c 1.24 -> 1.25 # drivers/usb/host/ehci-sched.c 1.19 -> 1.20 # drivers/usb/host/ehci-hcd.c 1.26 -> 1.27 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/09/11 david-b@pacbell.net 1.650 # [PATCH] ehci misc fixes # # This removes some bugs: # # - a short read problem with control requests # - only creates one control qh (memleak fix) # - adds an omitted hardware handshake # - reset timeout in octal, say what? # - a couple BUG()s outlived their value # # Plus it deletes unused stub code for split ISO # and updates some internal doc. # -------------------------------------------- # diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c --- a/drivers/usb/host/ehci-hcd.c Thu Sep 12 10:26:05 2002 +++ b/drivers/usb/host/ehci-hcd.c Thu Sep 12 10:26:05 2002 @@ -190,7 +190,7 @@ dbg_cmd (ehci, "reset", command); writel (command, &ehci->regs->command); ehci->hcd.state = USB_STATE_HALT; - return handshake (&ehci->regs->command, CMD_RESET, 0, 050); + return handshake (&ehci->regs->command, CMD_RESET, 0, 250); } /* idle the controller (from running) */ @@ -368,6 +368,7 @@ * * NOTE: layered drivers can't yet tell when we enable that, * so they can't pass this info along (like NETIF_F_HIGHDMA) + * (or like Scsi_Host.highmem_io) ... usb_bus.flags? */ if (HCC_64BIT_ADDR (hcc_params)) { writel (0, &ehci->regs->segment); @@ -585,6 +586,10 @@ { struct ehci_hcd *ehci = (struct ehci_hcd *) param; unsigned long flags; + + // FIXME don't pass flags; on sparc they aren't really flags. + // qh_completions can just leave irqs blocked, + // then have scan_async() allow IRQs if it's very busy spin_lock_irqsave (&ehci->lock, flags); diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c --- a/drivers/usb/host/ehci-q.c Thu Sep 12 10:26:05 2002 +++ b/drivers/usb/host/ehci-q.c Thu Sep 12 10:26:05 2002 @@ -26,8 +26,7 @@ * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned * buffers needed for the larger number). We use one QH per endpoint, queue - * multiple (bulk or control) urbs per endpoint. URBs may need several qtds. - * A scheduled interrupt qh always (for now) has one qtd, one urb. + * multiple urbs (all three types) per endpoint. URBs may need several qtds. * * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with * interrupts) needs careful scheduling. Performance improvements can be @@ -281,7 +280,12 @@ || (qh->qh_state == QH_STATE_IDLE); // FIXME Remove the automagic unlink mode. - // Drivers can now clean up safely; its' their job. + // Drivers can now clean up safely; it's their job. + // + // FIXME Removing it should fix the short read scenarios + // with "huge" urb data (more than one 16+KByte td) with + // the short read someplace other than the last data TD. + // Except the control case: 'retrigger' status ACKs. /* fault: unlink the rest, since this qtd saw an error? */ if (unlikely ((token & QTD_STS_HALT) != 0)) { @@ -391,7 +395,7 @@ struct ehci_qtd *qtd, *qtd_prev; dma_addr_t buf; int len, maxpacket; - int is_input; + int is_input, status_patch = 0; u32 token; /* @@ -422,6 +426,9 @@ qtd->urb = urb; qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma); list_add_tail (&qtd->qtd_list, head); + + if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) + status_patch = 1; } /* @@ -499,6 +506,19 @@ } } + /* if we're permitting a short control read, we want the hardware to + * just continue after short data and send the status ack. it can do + * that on the last data packet (typically the only one). for other + * packets, software fixup is needed (in qh_completions). + */ + if (status_patch) { + struct ehci_qtd *prev; + + prev = list_entry (qtd->qtd_list.prev, + struct ehci_qtd, qtd_list); + prev->hw_alt_next = QTD_NEXT (qtd->qtd_dma); + } + /* by default, enable interrupt on urb completion */ if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT))) qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC); @@ -653,9 +673,8 @@ } break; default: -#ifdef DEBUG - BUG (); -#endif + dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed); + return 0; } /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ @@ -837,7 +856,7 @@ qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list); dev = (struct hcd_dev *)urb->dev->hcpriv; epnum = usb_pipeendpoint (urb->pipe); - if (usb_pipein (urb->pipe)) + if (usb_pipein (urb->pipe) && !usb_pipecontrol (urb->pipe)) epnum |= 0x10; vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]", @@ -923,9 +942,11 @@ if (unlikely (qh == ehci->async && qh->qh_next.qh == qh)) { /* can't get here without STS_ASS set */ if (ehci->hcd.state != USB_STATE_HALT) { - if (cmd & CMD_PSE) + if (cmd & CMD_PSE) { writel (cmd & ~CMD_ASE, &ehci->regs->command); - else + (void) handshake (&ehci->regs->status, + STS_ASS, 0, 150); + } else ehci_ready (ehci); } qh->qh_next.qh = ehci->async = 0; @@ -944,10 +965,6 @@ prev = ehci->async; while (prev->qh_next.qh != qh && prev->qh_next.qh != ehci->async) prev = prev->qh_next.qh; -#ifdef DEBUG - if (prev->qh_next.qh != qh) - BUG (); -#endif if (qh->hw_info1 & __constant_cpu_to_le32 (QH_HEAD)) { ehci->async = prev; diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c --- a/drivers/usb/host/ehci-sched.c Thu Sep 12 10:26:05 2002 +++ b/drivers/usb/host/ehci-sched.c Thu Sep 12 10:26:05 2002 @@ -971,119 +971,10 @@ /* * "Split ISO TDs" ... used for USB 1.1 devices going through * the TTs in USB 2.0 hubs. + * + * FIXME not yet implemented */ -static void -sitd_free (struct ehci_hcd *ehci, struct ehci_sitd *sitd) -{ - pci_pool_free (ehci->sitd_pool, sitd, sitd->sitd_dma); -} - -static struct ehci_sitd * -sitd_make ( - struct ehci_hcd *ehci, - struct urb *urb, - unsigned index, // urb->iso_frame_desc [index] - unsigned uframe, // scheduled start - dma_addr_t dma, // mapped transfer buffer - int mem_flags -) { - struct ehci_sitd *sitd; - unsigned length; - - sitd = pci_pool_alloc (ehci->sitd_pool, mem_flags, &dma); - if (!sitd) - return sitd; - sitd->urb = urb; - length = urb->iso_frame_desc [index].length; - dma += urb->iso_frame_desc [index].offset; - -#if 0 - // FIXME: do the rest! -#else - sitd_free (ehci, sitd); - return 0; -#endif - -} - -static void -sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd) -{ - u32 ptr; - - ptr = cpu_to_le32 (sitd->sitd_dma | 2); // type 2 == sitd - if (ehci->pshadow [frame].ptr) { - if (!sitd->sitd_next.ptr) { - sitd->sitd_next = ehci->pshadow [frame]; - sitd->hw_next = ehci->periodic [frame]; - } else if (sitd->sitd_next.ptr != ehci->pshadow [frame].ptr) { - dbg ("frame %d sitd link goof", frame); - BUG (); - } - } - ehci->pshadow [frame].sitd = sitd; - ehci->periodic [frame] = ptr; -} - -static unsigned long -sitd_complete ( - struct ehci_hcd *ehci, - struct ehci_sitd *sitd, - unsigned long flags -) { - // FIXME -- implement! - - dbg ("NYI -- sitd_complete"); - return flags; -} - -/*-------------------------------------------------------------------------*/ - -static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) -{ - // struct ehci_sitd *first_sitd = 0; - unsigned frame_index; - dma_addr_t dma; - - dbg ("NYI -- sitd_submit"); - - // FIXME -- implement! - - // FIXME: setup one big dma mapping - dma = 0; - - for (frame_index = 0; - frame_index < urb->number_of_packets; - frame_index++) { - struct ehci_sitd *sitd; - unsigned uframe; - - // FIXME: use real arguments, schedule this! - uframe = -1; - - sitd = sitd_make (ehci, urb, frame_index, - uframe, dma, mem_flags); - - if (sitd) { - /* - if (first_sitd) - list_add_tail (&sitd->sitd_list, - &first_sitd->sitd_list); - else - first_sitd = sitd; - */ - } else { - // FIXME: clean everything up - } - } - - // if we have a first sitd, then - // store them all into the periodic schedule! - // urb->hcpriv = first sitd in sitd_list - - return -ENOSYS; -} #endif /* have_split_iso */ /*-------------------------------------------------------------------------*/