ChangeSet 1.883.3.1, 2002/12/16 10:31:05-08:00, [EMAIL PROTECTED]

[PATCH] ehci-hcd (1/2):  portability (2.4), tasklet,

This should be innocuous; I expect most folk won't notice anything
better (or worse) from this patch unless they're using Intel EHCI.

removing tasklet
     - parts of davem's patch (passing pt_regs down)
     - remove 'max_completions'
     - update cleanup after hc died
     - fix an urb unlink oops (null ptr) that happens more often this way

talking to hardware
     - fixes for some short read issues (may still be others)
        * use qtd->hw_alt_next to stop qh processing after short reads
        * detect/report short reads differently
     - longer reset timeout (it was excessively short, broke Intel)

other
     - simpler diagnostics portability to 2.4:  wrap dev_err() etc
     - urb unlink wait and non-wait unlink codepaths share most code
     - don't try ehci_stop() in interrupt context (bug from hcd layer)
     - minor stuff, including
        * some "after hc died" paths were wrong
        * verbose debug messages compile again
        * don't break error irq count


diff -Nru a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
--- a/drivers/usb/host/ehci-dbg.c       Wed Dec 18 00:35:29 2002
+++ b/drivers/usb/host/ehci-dbg.c       Wed Dec 18 00:35:29 2002
@@ -18,21 +18,45 @@
 
 /* this file is part of ehci-hcd.c */
 
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,50)
+
 #define ehci_dbg(ehci, fmt, args...) \
        dev_dbg (*(ehci)->hcd.controller, fmt, ## args )
+#define ehci_err(ehci, fmt, args...) \
+       dev_err (*(ehci)->hcd.controller, fmt, ## args )
+#define ehci_info(ehci, fmt, args...) \
+       dev_info (*(ehci)->hcd.controller, fmt, ## args )
+#define ehci_warn(ehci, fmt, args...) \
+       dev_warn (*(ehci)->hcd.controller, fmt, ## args )
 
-#ifdef EHCI_VERBOSE_DEBUG
-#define ehci_vdbg(ehci, fmt, args...) \
-       dev_dbg (*(ehci)->hcd.controller, fmt, ## args )
 #else
-#define ehci_vdbg(ehci, fmt, args...) do { } while (0)
+
+#ifdef DEBUG
+#define ehci_dbg(ehci, fmt, args...) \
+       printk(KERN_DEBUG "%s %s: " fmt, hcd_name, \
+               (ehci)->hcd.pdev->slot_name, ## args )
+#else
+#define ehci_dbg(ehci, fmt, args...) do { } while (0)
+#endif
+
+#define ehci_err(ehci, fmt, args...) \
+       printk(KERN_ERR "%s %s: " fmt, hcd_name, \
+               (ehci)->hcd.pdev->slot_name, ## args )
+#define ehci_info(ehci, fmt, args...) \
+       printk(KERN_INFO "%s %s: " fmt, hcd_name, \
+               (ehci)->hcd.pdev->slot_name, ## args )
+#define ehci_warn(ehci, fmt, args...) \
+       printk(KERN_WARNING "%s %s: " fmt, hcd_name, \
+               (ehci)->hcd.pdev->slot_name, ## args )
 #endif
 
 
 #ifdef EHCI_VERBOSE_DEBUG
 #      define vdbg dbg
+#      define ehci_vdbg ehci_dbg
 #else
-       static inline void vdbg (char *fmt, ...) { }
+#      define vdbg(fmt,args...) do { } while (0)
+#      define ehci_vdbg(ehci, fmt, args...) do { } while (0)
 #endif
 
 #ifdef DEBUG
@@ -289,7 +313,8 @@
 
        scratch = cpu_to_le32p (&qh->hw_info1);
        hw_curr = cpu_to_le32p (&qh->hw_current);
-       temp = snprintf (next, size, "qh/%p dev%d %cs ep%d %08x %08x (%08x %08x)",
+       temp = snprintf (next, size,
+                       "qh/%p dev%d %cs ep%d %08x %08x (%08x %08x)",
                        qh, scratch & 0x007f,
                        speed_char (scratch),
                        (scratch >> 8) & 0x000f,
diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
--- a/drivers/usb/host/ehci-hcd.c       Wed Dec 18 00:35:29 2002
+++ b/drivers/usb/host/ehci-hcd.c       Wed Dec 18 00:35:29 2002
@@ -123,12 +123,6 @@
 MODULE_PARM (log2_irq_thresh, "i");
 MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
 
-/* allow irqs at least every N URB completions */
-static int max_completions = 16;
-MODULE_PARM (max_completions, "i");
-MODULE_PARM_DESC (max_completions,
-               "limit for urb completions called with irqs disenabled");
-
 #define        INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT)
 
 /*-------------------------------------------------------------------------*/
@@ -202,7 +196,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, 250);
+       return handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000);
 }
 
 /* idle the controller (from running) */
@@ -289,8 +283,7 @@
                        pci_read_config_dword (ehci->hcd.pdev, where, &cap);
                } while ((cap & (1 << 16)) && msec);
                if (cap & (1 << 16)) {
-                       dev_info (*ehci->hcd.controller,
-                               "BIOS handoff failed (%d, %04x)\n",
+                       ehci_err (ehci, "BIOS handoff failed (%d, %04x)\n",
                                where, cap);
                        return 1;
                } 
@@ -333,8 +326,7 @@
                                return -EOPNOTSUPP;
                        break;
                case 0:                 /* illegal reserved capability */
-                       dev_warn (*ehci->hcd.controller,
-                                       "illegal capability!\n");
+                       ehci_warn (ehci, "illegal capability!\n");
                        cap = 0;
                        /* FALLTHROUGH */
                default:                /* unknown */
@@ -381,6 +373,8 @@
         * dedicate a qh for the async ring head, since we couldn't unlink
         * a 'real' qh without stopping the async schedule [4.8].  use it
         * as the 'reclamation list head' too.
+        * its dummy is used in hw_alt_next of many tds, to prevent the qh
+        * from automatically advancing to the next td after short reads.
         */
        ehci->async->qh_next.qh = 0;
        ehci->async->hw_next = QH_NEXT (ehci->async->qh_dma);
@@ -388,8 +382,7 @@
        ehci->async->hw_token = cpu_to_le32 (QTD_STS_HALT);
        ehci->async->hw_qtd_next = EHCI_LIST_END;
        ehci->async->qh_state = QH_STATE_LINKED;
-       ehci_qtd_free (ehci, ehci->async->dummy);
-       ehci->async->dummy = 0;
+       ehci->async->hw_alt_next = QTD_NEXT (ehci->async->dummy->qtd_dma);
        writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
 
        /*
@@ -406,8 +399,7 @@
        if (HCC_64BIT_ADDR (hcc_params)) {
                writel (0, &ehci->regs->segment);
                if (!pci_set_dma_mask (ehci->hcd.pdev, 0xffffffffffffffffULL))
-                       dev_info (*ehci->hcd.controller,
-                               "enabled 64bit PCI DMA (DAC)\n");
+                       ehci_info (ehci, "enabled 64bit PCI DMA\n");
        }
 
        /* clear interrupt enables, set irq latency */
@@ -454,7 +446,7 @@
         /* PCI Serial Bus Release Number is at 0x60 offset */
        pci_read_config_byte (hcd->pdev, 0x60, &tempbyte);
        temp = readw (&ehci->caps->hci_version);
-       dev_info (*hcd->controller,
+       ehci_info (ehci,
                "USB %x.%x enabled, EHCI %x.%02x, driver %s\n",
                ((tempbyte & 0xf0)>>4), (tempbyte & 0x0f),
                temp >> 8, temp & 0xff, DRIVER_VERSION);
@@ -494,10 +486,11 @@
        /* no more interrupts ... */
        if (hcd->state == USB_STATE_RUNNING)
                ehci_ready (ehci);
-       if (in_interrupt ())            /* should not happen!! */
-               dev_err (*hcd->controller, "stopped %s!\n", RUN_CONTEXT);
-       else
-               del_timer_sync (&ehci->watchdog);
+       if (in_interrupt ()) {          /* must not happen!! */
+               ehci_err (ehci, "stopped in_interrupt!\n");
+               return;
+       }
+       del_timer_sync (&ehci->watchdog);
        ehci_reset (ehci);
 
        /* let companion controllers work when we aren't */
@@ -621,14 +614,15 @@
 static void ehci_tasklet (unsigned long param)
 {
        struct ehci_hcd         *ehci = (struct ehci_hcd *) param;
+       struct pt_regs          *regs = NULL;
 
        spin_lock_irq (&ehci->lock);
 
        if (ehci->reclaim_ready)
-               end_unlink_async (ehci);
-       scan_async (ehci);
+               end_unlink_async (ehci, regs);
+       scan_async (ehci, regs);
        if (ehci->next_uframe != -1)
-               scan_periodic (ehci);
+               scan_periodic (ehci, regs);
 
        spin_unlock_irq (&ehci->lock);
 }
@@ -643,7 +637,7 @@
 
        /* e.g. cardbus physical eject */
        if (status == ~(u32) 0) {
-               dbg ("%s: device removed!", hcd_to_bus (hcd)->bus_name);
+               ehci_dbg (ehci, "device removed\n");
                goto dead;
        }
 
@@ -681,8 +675,7 @@
 
        /* PCI errors [4.15.2.4] */
        if (unlikely ((status & STS_FATAL) != 0)) {
-               err ("%s: fatal error, state %x",
-                       hcd_to_bus (hcd)->bus_name, hcd->state);
+               ehci_err (ehci, "fatal error\n");
 dead:
                ehci_reset (ehci);
                /* generic layer kills/unlinks all urbs, then
@@ -754,52 +747,52 @@
 static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
 {
        struct ehci_hcd         *ehci = hcd_to_ehci (hcd);
-       struct ehci_qh          *qh = (struct ehci_qh *) urb->hcpriv;
+       struct ehci_qh          *qh;
        unsigned long           flags;
+       int                     maybe_irq = 1;
 
-       ehci_vdbg (ehci, "urb_dequeue %p qh %p state %d\n",
-               urb, qh, qh->qh_state);
-
+       spin_lock_irqsave (&ehci->lock, flags);
        switch (usb_pipetype (urb->pipe)) {
        // case PIPE_CONTROL:
        // case PIPE_BULK:
        default:
-               spin_lock_irqsave (&ehci->lock, flags);
-               if (ehci->reclaim) {
-                       vdbg ("dq %p: reclaim = %p, %s",
-                               qh, ehci->reclaim, RUN_CONTEXT);
-                       if (qh == ehci->reclaim) {
-                               /* unlinking qh for another queued urb? */
-                               spin_unlock_irqrestore (&ehci->lock, flags);
-                               return 0;
-                       }
-                       if (in_interrupt ()) {
-                               spin_unlock_irqrestore (&ehci->lock, flags);
-                               return -EAGAIN;
-                       }
-                       while (qh->qh_state == QH_STATE_LINKED
-                                       && ehci->reclaim
-                                       && ehci->hcd.state != USB_STATE_HALT
-                                       ) {
-                               spin_unlock_irqrestore (&ehci->lock, flags);
-                               /* let pending unlinks complete */
-                               wait_ms (1);
-                               spin_lock_irqsave (&ehci->lock, flags);
+               qh = (struct ehci_qh *) urb->hcpriv;
+               if (!qh)
+                       break;
+               while (qh->qh_state == QH_STATE_LINKED
+                               && ehci->reclaim
+                               && HCD_IS_RUNNING (ehci->hcd.state)
+                               ) {
+                       spin_unlock_irqrestore (&ehci->lock, flags);
+
+                       if (maybe_irq) {
+                               if (in_interrupt ())
+                                       return -EAGAIN;
+                               maybe_irq = 0;
                        }
+                       /* let pending unlinks complete, so this can start */
+                       wait_ms (1);
+
+                       spin_lock_irqsave (&ehci->lock, flags);
                }
+               if (!HCD_IS_RUNNING (ehci->hcd.state) && ehci->reclaim)
+                       end_unlink_async (ehci, NULL);
+
+               /* something else might have unlinked the qh by now */
                if (qh->qh_state == QH_STATE_LINKED)
                        start_unlink_async (ehci, qh);
-               spin_unlock_irqrestore (&ehci->lock, flags);
                break;
 
        case PIPE_INTERRUPT:
-               spin_lock_irqsave (&ehci->lock, flags);
+               qh = (struct ehci_qh *) urb->hcpriv;
+               if (!qh)
+                       break;
                if (qh->qh_state == QH_STATE_LINKED) {
                        /* messy, can spin or block a microframe ... */
                        intr_deschedule (ehci, qh, 1);
                        /* qh_state == IDLE */
                }
-               qh_completions (ehci, qh);
+               qh_completions (ehci, qh, NULL);
 
                /* reschedule QH iff another request is queued */
                if (!list_empty (&qh->qtd_list)
@@ -817,7 +810,6 @@
                        }
                        return status;
                }
-               spin_unlock_irqrestore (&ehci->lock, flags);
                break;
 
        case PIPE_ISOCHRONOUS:
@@ -828,6 +820,7 @@
                urb->transfer_flags |= EHCI_STATE_UNLINK;
                break;
        }
+       spin_unlock_irqrestore (&ehci->lock, flags);
        return 0;
 }
 
diff -Nru a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
--- a/drivers/usb/host/ehci-hub.c       Wed Dec 18 00:35:29 2002
+++ b/drivers/usb/host/ehci-hub.c       Wed Dec 18 00:35:29 2002
@@ -315,7 +315,7 @@
                                        wIndex + 1);
                                temp |= PORT_OWNER;
                        } else {
-                               ehci_vdbg (ehci, "port %d reset", wIndex + 1);
+                               ehci_vdbg (ehci, "port %d reset\n", wIndex + 1);
                                temp |= PORT_RESET;
                                temp &= ~PORT_PE;
 
diff -Nru a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
--- a/drivers/usb/host/ehci-mem.c       Wed Dec 18 00:35:29 2002
+++ b/drivers/usb/host/ehci-mem.c       Wed Dec 18 00:35:29 2002
@@ -73,8 +73,11 @@
        dma_addr_t              dma;
 
        qtd = pci_pool_alloc (ehci->qtd_pool, flags, &dma);
-       if (qtd != 0)
+       if (qtd != 0) {
                ehci_qtd_init (qtd, dma);
+               if (ehci->async)
+                       qtd->hw_alt_next = ehci->async->hw_alt_next;
+       }
        return qtd;
 }
 
diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
--- a/drivers/usb/host/ehci-q.c Wed Dec 18 00:35:29 2002
+++ b/drivers/usb/host/ehci-q.c Wed Dec 18 00:35:29 2002
@@ -85,11 +85,12 @@
 
 /* update halted (but potentially linked) qh */
 
-static void qh_update (struct ehci_qh *qh, struct ehci_qtd *qtd)
+static void
+qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
 {
        qh->hw_current = 0;
        qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma);
-       qh->hw_alt_next = EHCI_LIST_END;
+       qh->hw_alt_next = ehci->async->hw_alt_next;
 
        /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
        wmb ();
@@ -98,7 +99,12 @@
 
 /*-------------------------------------------------------------------------*/
 
-static inline void qtd_copy_status (struct urb *urb, size_t length, u32 token)
+static inline void qtd_copy_status (
+       struct ehci_hcd *ehci,
+       struct urb *urb,
+       size_t length,
+       u32 token
+)
 {
        /* count IN/OUT bytes, not SETUP (even short packets) */
        if (likely (QTD_PID (token) != 2))
@@ -132,7 +138,7 @@
 
                ehci_vdbg (ehci,
                        "dev%d ep%d%s qtd token %08x --> status %d\n",
-                       usb_pipedev (urb->pipe),
+                       usb_pipedevice (urb->pipe),
                        usb_pipeendpoint (urb->pipe),
                        usb_pipein (urb->pipe) ? "in" : "out",
                        token, urb->status);
@@ -157,9 +163,17 @@
                        }
                }
        }
+
+       /* force cleanup after short read; not necessarily an error */
+       if (unlikely (urb->status == -EINPROGRESS
+                       && QTD_LENGTH (token) != 0
+                       && QTD_PID (token) == 1)) {
+               urb->status = -EREMOTEIO;
+       }
 }
 
-static void ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb)
+static void
+ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb, struct pt_regs *regs)
 {
        if (likely (urb->hcpriv != 0)) {
                struct ehci_qh  *qh = (struct ehci_qh *) urb->hcpriv;
@@ -174,24 +188,26 @@
                urb->hcpriv = 0;
        }
 
-       if (likely (urb->status == -EINPROGRESS)) {
-               if (urb->actual_length != urb->transfer_buffer_length
-                               && (urb->transfer_flags & URB_SHORT_NOT_OK))
-                       urb->status = -EREMOTEIO;
-               else
+       switch (urb->status) {
+       case -EINPROGRESS:              /* success */
+               urb->status = 0;
+       default:                        /* fault */
+               COUNT (ehci->stats.complete);
+               break;
+       case -EREMOTEIO:                /* fault or normal */
+               if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
                        urb->status = 0;
-       }
-
-       if (likely (urb->status == 0))
                COUNT (ehci->stats.complete);
-       else if (urb->status == -ECONNRESET || urb->status == -ENOENT)
+               break;
+       case -ECONNRESET:               /* canceled */
+       case -ENOENT:
                COUNT (ehci->stats.unlink);
-       else
-               COUNT (ehci->stats.error);
+               break;
+       }
 
        /* complete() can reenter this HCD */
        spin_unlock (&ehci->lock);
-       usb_hcd_giveback_urb (&ehci->hcd, urb, NULL);
+       usb_hcd_giveback_urb (&ehci->hcd, urb, regs);
 
        spin_lock (&ehci->lock);
 }
@@ -203,14 +219,14 @@
  * indicating how much "real" work we did.
  */
 static unsigned
-qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
+qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs)
 {
        struct ehci_qtd         *qtd, *last;
        struct list_head        *next, *qtd_list = &qh->qtd_list;
-       int                     unlink = 0, halted = 0;
+       int                     unlink = 0, stopped = 0;
        unsigned                count = 0;
 
-       if (unlikely (list_empty (qtd_list)))
+       if (unlikely (list_empty (&qh->qtd_list)))
                return count;
 
        /* scan QTDs till end of list, or we reach an active one */
@@ -225,7 +241,7 @@
                /* clean up any state from previous QTD ...*/
                if (last) {
                        if (likely (last->urb != urb)) {
-                               ehci_urb_done (ehci, last->urb);
+                               ehci_urb_done (ehci, last->urb, regs);
                                count++;
                        }
                        ehci_qtd_free (ehci, last);
@@ -236,12 +252,14 @@
                /* QTDs at tail may be active if QH+HC are running,
                 * or when unlinking some urbs queued to this QH
                 */
+               rmb ();
                token = le32_to_cpu (qtd->hw_token);
-               halted = halted
+               stopped = stopped
+                       || (qh->qh_state == QH_STATE_IDLE)
                        || (__constant_cpu_to_le32 (QTD_STS_HALT)
                                & qh->hw_token) != 0
                        || (ehci->hcd.state == USB_STATE_HALT)
-                       || (qh->qh_state == QH_STATE_IDLE);
+                       || (qh->hw_current == ehci->async->hw_alt_next);
 
                // FIXME Remove the automagic unlink mode.
                // Drivers can now clean up safely; it's their job.
@@ -257,7 +275,7 @@
                        /* status copied below */
 
                /* QH halts only because of fault (above) or unlink (here). */
-               } else if (unlikely (halted != 0)) {
+               } else if (unlikely (stopped != 0)) {
 
                        /* unlinking everything because of HC shutdown? */
                        if (ehci->hcd.state == USB_STATE_HALT) {
@@ -293,7 +311,7 @@
                }
 
                spin_lock (&urb->lock);
-               qtd_copy_status (urb, qtd->length, token);
+               qtd_copy_status (ehci, urb, qtd->length, token);
                spin_unlock (&urb->lock);
 
                list_del (&qtd->qtd_list);
@@ -311,14 +329,14 @@
 
        /* last urb's completion might still need calling */
        if (likely (last != 0)) {
-               ehci_urb_done (ehci, last->urb);
+               ehci_urb_done (ehci, last->urb, regs);
                count++;
                ehci_qtd_free (ehci, last);
        }
 
        /* reactivate queue after error and driver's cleanup */
-       if (unlikely (halted && !list_empty (qtd_list))) {
-               qh_update (qh, list_entry (qtd_list->next,
+       if (unlikely (stopped && !list_empty (&qh->qtd_list))) {
+               qh_update (ehci, qh, list_entry (qh->qtd_list.next,
                                struct ehci_qtd, qtd_list));
        }
 
@@ -420,7 +438,6 @@
        for (;;) {
                int this_qtd_len;
 
-               qtd->urb = urb;
                this_qtd_len = qtd_fill (qtd, buf, len, token);
                len -= this_qtd_len;
                buf += this_qtd_len;
@@ -658,7 +675,7 @@
 
                list_splice (qtd_list, &qh->qtd_list);
                qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list);
-               qh_update (qh, qtd);
+               qh_update (ehci, qh, qtd);
        } else {
                qh->hw_qtd_next = qh->hw_alt_next = EHCI_LIST_END;
        }
@@ -784,6 +801,7 @@
                        list_add (&dummy->qtd_list, qtd_list);
 
                        ehci_qtd_init (qtd, qtd->qtd_dma);
+                       qtd->hw_alt_next = ehci->async->hw_alt_next;
                        qh->dummy = qtd;
 
                        /* hc must see the new dummy at list end */
@@ -797,15 +815,6 @@
 
                /* no URB queued */
                } else {
-                       struct ehci_qtd         *last_qtd;
-
-                       /* make sure hc sees current dummy at the end */
-                       last_qtd = list_entry (qtd_list->prev,
-                                       struct ehci_qtd, qtd_list);
-                       last_qtd->hw_next = QTD_NEXT (qh->dummy->qtd_dma);
-
-                       // dbg_qh ("empty qh", ehci, qh);
-
                        /* usb_clear_halt() means qh data toggle gets reset */
                        if (unlikely (!usb_gettoggle (urb->dev,
                                                (epnum & 0x0f),
@@ -813,8 +822,17 @@
                                clear_toggle (urb->dev,
                                        epnum & 0x0f, !(epnum & 0x10), qh);
                        }
-                       if (qtd)
-                               qh_update (qh, qtd);
+
+                       /* make sure hc sees current dummy at the end */
+                       if (qtd) {
+                               struct ehci_qtd         *last_qtd;
+
+                               last_qtd = list_entry (qtd_list->prev,
+                                               struct ehci_qtd, qtd_list);
+                               last_qtd->hw_next = QTD_NEXT (
+                                               qh->dummy->qtd_dma);
+                               qh_update (ehci, qh, qtd);
+                       }
                }
                list_splice (qtd_list, qh->qtd_list.prev);
 
@@ -877,34 +895,38 @@
 /*-------------------------------------------------------------------------*/
 
 /* the async qh for the qtds being reclaimed are now unlinked from the HC */
-/* caller must not own ehci->lock */
 
-static void end_unlink_async (struct ehci_hcd *ehci)
+static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
 {
        struct ehci_qh          *qh = ehci->reclaim;
 
        del_timer (&ehci->watchdog);
 
+       qh->hw_next = cpu_to_le32 (qh->qh_dma);
        qh->qh_state = QH_STATE_IDLE;
        qh->qh_next.qh = 0;
        qh_put (ehci, qh);                      // refcount from reclaim 
        ehci->reclaim = 0;
        ehci->reclaim_ready = 0;
 
-       qh_completions (ehci, qh);
+       qh_completions (ehci, qh, regs);
 
        if (!list_empty (&qh->qtd_list)
                        && HCD_IS_RUNNING (ehci->hcd.state))
                qh_link_async (ehci, qh);
-       else
+       else {
                qh_put (ehci, qh);              // refcount from async list
 
-       /* it's not free to turn the async schedule on/off, so we leave it
-        * active but idle for a while once it empties.
-        */
-       if (!ehci->async->qh_next.qh && !timer_pending (&ehci->watchdog)) {
-               ehci->async_idle = 1;
-               mod_timer (&ehci->watchdog, jiffies + EHCI_ASYNC_JIFFIES);
+               /* it's not free to turn the async schedule on/off; leave it
+                * active but idle for a while once it empties.
+                */
+               if (HCD_IS_RUNNING (ehci->hcd.state)
+                               && ehci->async->qh_next.qh == 0
+                               && !timer_pending (&ehci->watchdog)) {
+                       ehci->async_idle = 1;
+                       mod_timer (&ehci->watchdog,
+                                       jiffies + EHCI_ASYNC_JIFFIES);
+               }
        }
 }
 
@@ -941,12 +963,6 @@
        qh->qh_state = QH_STATE_UNLINK;
        ehci->reclaim = qh = qh_get (qh);
 
-       if (unlikely (ehci->hcd.state == USB_STATE_HALT)) {
-               ehci->reclaim_ready = 1;
-               tasklet_schedule (&ehci->tasklet);
-               return;
-       }
-
        prev = ehci->async;
        while (prev->qh_next.qh != qh)
                prev = prev->qh_next.qh;
@@ -955,6 +971,11 @@
        prev->qh_next = qh->qh_next;
        wmb ();
 
+       if (unlikely (ehci->hcd.state == USB_STATE_HALT)) {
+               end_unlink_async (ehci, NULL);
+               return;
+       }
+
        ehci->reclaim_ready = 0;
        cmd |= CMD_IAAD;
        writel (cmd, &ehci->regs->command);
@@ -966,7 +987,7 @@
 /*-------------------------------------------------------------------------*/
 
 static void
-scan_async (struct ehci_hcd *ehci)
+scan_async (struct ehci_hcd *ehci, struct pt_regs *regs)
 {
        struct ehci_qh          *qh;
        unsigned                count;
@@ -984,7 +1005,7 @@
                                 * reporting drops the lock.
                                 */
                                qh = qh_get (qh);
-                               temp = qh_completions (ehci, qh);
+                               temp = qh_completions (ehci, qh, regs);
                                qh_put (ehci, qh);
                                if (temp != 0) {
                                        count += temp;
@@ -1000,14 +1021,6 @@
                         */
                        if (list_empty (&qh->qtd_list) && !ehci->reclaim) {
                                start_unlink_async (ehci, qh);
-                       }
-
-                       /* keep latencies down: let any irqs in */
-                       if (count > max_completions) {
-                               spin_unlock_irq (&ehci->lock);
-                               cpu_relax ();
-                               spin_lock_irq (&ehci->lock);
-                               goto rescan;
                        }
 
                        qh = qh->qh_next.qh;
diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
--- a/drivers/usb/host/ehci-sched.c     Wed Dec 18 00:35:29 2002
+++ b/drivers/usb/host/ehci-sched.c     Wed Dec 18 00:35:29 2002
@@ -495,7 +495,8 @@
 intr_complete (
        struct ehci_hcd *ehci,
        unsigned        frame,
-       struct ehci_qh  *qh
+       struct ehci_qh  *qh,
+       struct pt_regs  *regs
 ) {
        unsigned        count;
 
@@ -509,7 +510,7 @@
        }
        
        /* handle any completions */
-       count = qh_completions (ehci, qh);
+       count = qh_completions (ehci, qh, regs);
 
        if (unlikely (list_empty (&qh->qtd_list)))
                intr_deschedule (ehci, qh, 0);
@@ -867,7 +868,8 @@
 itd_complete (
        struct ehci_hcd *ehci,
        struct ehci_itd *itd,
-       unsigned        uframe
+       unsigned        uframe,
+       struct pt_regs  *regs
 ) {
        struct urb                              *urb = itd->urb;
        struct usb_iso_packet_descriptor        *desc;
@@ -922,7 +924,7 @@
 
        /* complete() can reenter this HCD */
        spin_unlock (&ehci->lock);
-       usb_hcd_giveback_urb (&ehci->hcd, urb, NULL);
+       usb_hcd_giveback_urb (&ehci->hcd, urb, regs);
        spin_lock (&ehci->lock);
 
        /* defer stopping schedule; completion can submit */
@@ -973,7 +975,7 @@
 /*-------------------------------------------------------------------------*/
 
 static void
-scan_periodic (struct ehci_hcd *ehci)
+scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
 {
        unsigned        frame, clock, now_uframe, mod;
        unsigned        count = 0;
@@ -999,14 +1001,6 @@
                u32                     type, *hw_p;
                unsigned                uframes;
 
-               /* keep latencies down: let any irqs in */
-               if (count > max_completions) {
-                       spin_unlock_irq (&ehci->lock);
-                       cpu_relax ();
-                       count = 0;
-                       spin_lock_irq (&ehci->lock);
-               }
-
 restart:
                /* scan schedule to _before_ current frame index */
                if (frame == clock)
@@ -1031,7 +1025,7 @@
                                temp = q.qh->qh_next;
                                type = Q_NEXT_TYPE (q.qh->hw_next);
                                count += intr_complete (ehci, frame,
-                                               qh_get (q.qh));
+                                               qh_get (q.qh), regs);
                                qh_put (ehci, q.qh);
                                q = temp;
                                break;
@@ -1064,7 +1058,7 @@
 
                                                /* might free q.itd ... */
                                                count += itd_complete (ehci,
-                                                       temp.itd, uf);
+                                                       temp.itd, uf, regs);
                                                break;
                                        }
                                }


-------------------------------------------------------
This sf.net email is sponsored by:
With Great Power, Comes Great Responsibility 
Learn to use your power at OSDN's High Performance Computing Channel
http://hpc.devchannel.org/
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to