Hi,
Here's a patch against 2.5.2-pre10 that provides the following changes
to the ehci driver:
- Bulk/control queueing works now
- Resolves two FIXMEs about unlinking with queued bulk or
control urbs
- Removes old code to support a non-production stepping of the
NEC controller
- Updates two messages to not use 2.5-only features
The patch was written by David Brownell.
thanks,
greg k-h
diff -Nru a/drivers/usb/hcd/ehci-q.c b/drivers/usb/hcd/ehci-q.c
--- a/drivers/usb/hcd/ehci-q.c Tue Jan 8 09:29:37 2002
+++ b/drivers/usb/hcd/ehci-q.c Tue Jan 8 09:29:37 2002
@@ -39,12 +39,6 @@
* buffer low/full speed data so the host collects it at high speed.
*/
-#ifdef EHCI_SOFT_RETRIES
-static int soft_retries = EHCI_SOFT_RETRIES;
-MODULE_PARM (soft_retries, "i");
-MODULE_PARM_DESC (soft_retries, "Number of software retries for endpoint i/o");
-#endif
-
/*-------------------------------------------------------------------------*/
/* fill a qtd, returning how much of the buffer we were able to queue up */
@@ -134,8 +128,9 @@
urb->status = -EPIPE;
else /* unknown */
urb->status = -EPROTO;
- dbg ("devpath %s ep %d-%s qtd token %x --> status %d",
- urb->dev->devpath, usb_pipeendpoint (urb->pipe),
+ dbg ("ep %d-%s qtd token %08x --> status %d",
+ /* devpath */
+ usb_pipeendpoint (urb->pipe),
usb_pipein (urb->pipe) ? "in" : "out",
token, urb->status);
@@ -148,8 +143,8 @@
usb_pipeendpoint (pipe),
usb_pipeout (pipe));
if (urb->dev->tt && !usb_pipeint (pipe)) {
-err ("must CLEAR_TT_BUFFER, hub %s port %d%s addr %d ep %d",
- urb->dev->tt->hub->devpath, urb->dev->ttport,
+err ("must CLEAR_TT_BUFFER, hub port %d%s addr %d ep %d",
+ urb->dev->ttport, /* devpath */
urb->dev->tt->multi ? "" : " (all-ports TT)",
urb->dev->devnum, usb_pipeendpoint (urb->pipe));
// FIXME something (khubd?) should make the hub
@@ -228,12 +223,10 @@
struct list_head *qtd_list,
int freeing
) {
- struct ehci_qtd *qtd = 0;
- struct list_head *next = 0;
- u32 token;
+ struct ehci_qtd *qtd, *last;
+ struct list_head *next;
struct ehci_qh *qh = 0;
- struct urb *urb = 0;
- int halted = 0;
+ int unlink = 0, halted = 0;
unsigned long flags;
int retval = 0;
@@ -243,89 +236,116 @@
return retval;
}
- for (qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list);
+ /* scan QTDs till end of list, or we reach an active one */
+ for (qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list),
+ last = 0, next = 0;
next != qtd_list;
- qtd = list_entry (next, struct ehci_qtd, qtd_list)) {
- token = le32_to_cpu (qtd->hw_token);
- if (!qh) {
- urb = qtd->urb;
- qh = (struct ehci_qh *) urb->hcpriv;
+ last = qtd, qtd = list_entry (next,
+ struct ehci_qtd, qtd_list)) {
+ struct urb *urb = qtd->urb;
+ u32 token = 0;
+
+ /* qh is non-null iff these qtds were queued to the HC */
+ qh = (struct ehci_qh *) urb->hcpriv;
+
+ /* clean up any state from previous QTD ...*/
+ if (last) {
+ if (likely (last->urb != urb)) {
+ /* complete() can reenter this HCD */
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ if (likely (freeing != 0))
+ ehci_urb_done (ehci, last->buf_dma,
+ last->urb);
+ else
+ ehci_urb_complete (ehci, last->buf_dma,
+ last->urb);
+ spin_lock_irqsave (&ehci->lock, flags);
+ retval++;
+ }
+
+ /* qh overlays can have HC's old cached copies of
+ * next qtd ptrs, if an URB was queued afterwards.
+ */
+ if (qh && cpu_to_le32 (last->qtd_dma) == qh->hw_current
+ && last->hw_next != qh->hw_qtd_next) {
+ qh->hw_alt_next = last->hw_alt_next;
+ qh->hw_qtd_next = last->hw_next;
+ }
+
+ if (likely (freeing != 0))
+ ehci_qtd_free (ehci, last);
+ last = 0;
}
+ next = qtd->qtd_list.next;
+
+ /* if these qtds were queued to the HC, some may be active.
+ * else we're cleaning up after a failed URB submission.
+ */
if (likely (qh != 0)) {
+ int qh_halted;
+
+ qh_halted = __constant_cpu_to_le32 (QTD_STS_HALT)
+ & qh->hw_token;
+ token = le32_to_cpu (qtd->hw_token);
halted = halted
+ || qh_halted
|| (ehci->hcd.state == USB_STATE_HALT)
|| (qh->qh_state == QH_STATE_IDLE);
- if (unlikely ((token & QTD_STS_HALT) != 0)) {
-#ifdef EHCI_SOFT_RETRIES
- /* extra soft retries for protocol errors */
- if (!halted
- && qh->retries < soft_retries
- && (QTD_STS_HALT|QTD_STS_XACT)
- == (token & 0xff)
- && QTD_CERR (token) == 0) {
- if (qh->retries == 0)
- dbg ("soft retry, qh %p qtd %p",
- qh, qtd);
- qh->retries++;
- token &= ~0x0ff;
- token |= QTD_STS_ACTIVE;
- token |= (EHCI_TUNE_CERR << 10);
- /* qtd update not needed */
- qh->hw_token = cpu_to_le32 (token);
- spin_unlock_irqrestore (&ehci->lock,
- flags);
- return;
-
- } else if (qh->retries >= soft_retries
- && soft_retries) {
- dbg ("retried %d times, qh %p qtd %p",
- qh->retries, qh, qtd);
- }
-#endif /* EHCI_SOFT_RETRIES */
- halted = 1;
- }
-
- if (unlikely ((token & QTD_STS_ACTIVE) != 0)) {
- /* stop scan if qtd is visible to the HC */
- if (!halted) {
- urb = 0;
- break;
- }
+ /* QH halts only because of fault or unlink; in both
+ * cases, queued URBs get unlinked. But for unlink,
+ * URBs at the head of the queue can stay linked.
+ */
+ if (unlikely (halted != 0)) {
- /* continue cleanup if HC is halted */
+ /* unlink everything because of HC shutdown? */
if (ehci->hcd.state == USB_STATE_HALT) {
+ freeing = unlink = 1;
urb->status = -ESHUTDOWN;
- goto scrub;
- }
- /* stall? some other urb was unlinked? */
- if (urb->status == -EINPROGRESS) {
-dbg ("?why? qh %p, qtd %p halted, urb %p, token %8x, len %d",
- qh, qtd, urb, token, urb->actual_length);
-spin_unlock_irqrestore (&ehci->lock, flags);
-return retval;
- /*
- * FIXME: write this code. When one queued urb is unlinked,
- * unlink every succeeding urb.
- */
+ /* explicit unlink, starting here? */
+ } else if (qh->qh_state == QH_STATE_IDLE
+ && (urb->status == -ECONNRESET
+ || urb->status == -ENOENT)) {
+ freeing = unlink = 1;
+
+ /* unlink everything because of error? */
+ } else if (qh_halted
+ && !(token & QTD_STS_HALT)) {
+ freeing = unlink = 1;
+ if (urb->status == -EINPROGRESS)
+ urb->status = -ECONNRESET;
+
+ /* unlink the rest? */
+ } else if (unlink) {
+ urb->status = -ECONNRESET;
+
+ /* QH halted to unlink urbs after this? */
+ } else if ((token & QTD_STS_ACTIVE) != 0) {
+ qtd = 0;
continue;
}
- /* else stopped for some other reason */
- }
-scrub:
+ /* Else QH is active, so we must not modify QTDs
+ * that HC may be working on. Break from loop.
+ */
+ } else if (unlikely ((token & QTD_STS_ACTIVE) != 0)) {
+ next = qtd_list;
+ qtd = 0;
+ continue;
+ }
+
spin_lock (&urb->lock);
qtd_copy_status (urb, qtd->length, token);
spin_unlock (&urb->lock);
}
- next = qtd->qtd_list.next;
/*
* NOTE: this won't work right with interrupt urbs that
* need multiple qtds ... only the first scan of qh->qtd_list
* starts at the right qtd, yet multiple scans could happen
* for transfers that are scheduled across multiple uframes.
+ * (Such schedules are not currently allowed!)
*/
if (likely (freeing != 0))
list_del (&qtd->qtd_list);
@@ -347,8 +367,6 @@
qtd->hw_buf [0] |= cpu_to_le32 (0x0fff & qtd->buf_dma);
}
- spin_unlock_irqrestore (&ehci->lock, flags);
-
#if 0
if (urb->status == -EINPROGRESS)
vdbg (" qtd %p ok, urb %p, token %8x, len %d",
@@ -364,21 +382,6 @@
pci_unmap_single (ehci->hcd.pdev,
qtd->buf_dma, sizeof (struct usb_ctrlrequest),
PCI_DMA_TODEVICE);
-
- /* another queued urb? */
- if (unlikely (qtd->urb != urb)) {
- if (likely (freeing != 0))
- ehci_urb_done (ehci, qtd->buf_dma, urb);
- else
- ehci_urb_complete (ehci, qtd->buf_dma, urb);
- retval++;
- urb = qtd->urb;
- }
-
- if (likely (freeing != 0))
- ehci_qtd_free (ehci, qtd);
- spin_lock_irqsave (&ehci->lock, flags);
- qtd = list_entry (next, struct ehci_qtd, qtd_list);
}
/* patch up list head? */
@@ -389,11 +392,12 @@
spin_unlock_irqrestore (&ehci->lock, flags);
/* last urb's completion might still need calling */
- if (likely (qtd && urb)) {
- if (likely (freeing != 0))
- ehci_urb_done (ehci, qtd->buf_dma, urb);
- else
- ehci_urb_complete (ehci, qtd->buf_dma, urb);
+ if (likely (last != 0)) {
+ if (likely (freeing != 0)) {
+ ehci_urb_done (ehci, last->buf_dma, last->urb);
+ ehci_qtd_free (ehci, last);
+ } else
+ ehci_urb_complete (ehci, last->buf_dma, last->urb);
retval++;
}
return retval;
@@ -749,7 +753,9 @@
/* is an URB is queued to this qh already? */
if (unlikely (!list_empty (&qh->qtd_list))) {
struct ehci_qtd *last_qtd;
+ int short_rx = 0;
+ /* update the last qtd's "next" pointer */
// dbg_qh ("non-empty qh", ehci, qh);
last_qtd = list_entry (qh->qtd_list.prev,
struct ehci_qtd, qtd_list);
@@ -760,6 +766,21 @@
&& (epnum & 0x10)) {
// only the last QTD for now
last_qtd->hw_alt_next = hw_next;
+ short_rx = 1;
+ }
+
+ /* Adjust any old copies in qh overlay too.
+ * Interrupt code must cope with case of HC having it
+ * cached, and clobbering these updates.
+ * ... complicates getting rid of extra interrupts!
+ */
+ if (qh->hw_current == cpu_to_le32 (last_qtd->qtd_dma)) {
+ wmb ();
+ qh->hw_qtd_next = hw_next;
+ if (short_rx)
+ qh->hw_alt_next = hw_next
+ | (qh->hw_alt_next & 0x1e);
+ vdbg ("queue to qh %p, patch", qh);
}
/* no URB queued */
@@ -822,8 +843,8 @@
qh_completions (ehci, &qh->qtd_list, 1);
- // FIXME unlink any urb should unlink all following urbs,
- // so that this will never happen
+ // unlink any urb should now unlink all following urbs, so that
+ // relinking only happens for urbs before the unlinked ones.
if (!list_empty (&qh->qtd_list)
&& HCD_IS_RUNNING (ehci->hcd.state))
qh_link_async (ehci, qh);
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel