# 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.565   -> 1.566  
#       drivers/usb/hcd/ohci-hcd.c      1.6     -> 1.7    
#       drivers/usb/hcd/ohci-hub.c      1.2     -> 1.3    
#       drivers/usb/hcd/ohci-dbg.c      1.2     -> 1.3    
#       drivers/usb/hcd/ohci-mem.c      1.3     -> 1.4    
#       drivers/usb/hcd/ohci-q.c        1.4     -> 1.5    
#       drivers/usb/hcd/ohci.h  1.2     -> 1.3    
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/03/27      [EMAIL PROTECTED]     1.566
# USB ohci-hcd driver update
#   
#       - bugfix: control endpoints can't stall
#       - bugfix: remove bogus intr unlink optimization,
#           by sharing intr/iso code
#       - bugfix: iso submit uses urb->interval
#       - removed iso urb->next ring logic
#           (belongs in hcd layer if anywhere)
#       - simplify/shorten/correct completion handling
#       - in debug, labels setup packets as such
#       - bring CVS ids back up to date
# --------------------------------------------
#
diff -Nru a/drivers/usb/hcd/ohci-dbg.c b/drivers/usb/hcd/ohci-dbg.c
--- a/drivers/usb/hcd/ohci-dbg.c        Wed Apr  3 16:39:30 2002
+++ b/drivers/usb/hcd/ohci-dbg.c        Wed Apr  3 16:39:30 2002
@@ -5,7 +5,7 @@
  * (C) Copyright 2000-2002 David Brownell <[EMAIL PROTECTED]>
  * 
  * This file is licenced under the GPL.
- * $Id: ohci-dbg.c,v 1.2 2002/01/19 00:15:45 dbrownell Exp $
+ * $Id: ohci-dbg.c,v 1.4 2002/03/27 20:40:40 dbrownell Exp $
  */
  
 /*-------------------------------------------------------------------------*/
@@ -52,7 +52,7 @@
                int i, len;
 
                if (usb_pipecontrol (pipe)) {
-                       printk (KERN_DEBUG __FILE__ ": cmd(8):");
+                       printk (KERN_DEBUG __FILE__ ": setup(8):");
                        for (i = 0; i < 8 ; i++) 
                                printk (" %02x", ((__u8 *) urb->setup_packet) [i]);
                        printk ("\n");
diff -Nru a/drivers/usb/hcd/ohci-hcd.c b/drivers/usb/hcd/ohci-hcd.c
--- a/drivers/usb/hcd/ohci-hcd.c        Wed Apr  3 16:39:30 2002
+++ b/drivers/usb/hcd/ohci-hcd.c        Wed Apr  3 16:39:30 2002
@@ -56,7 +56,7 @@
  * v1.0 1999/04/27 initial release
  *
  * This file is licenced under the GPL.
- * $Id: ohci-hcd.c,v 1.7 2002/01/19 00:20:56 dbrownell Exp $
+ * $Id: ohci-hcd.c,v 1.9 2002/03/27 20:41:57 dbrownell Exp $
  */
  
 #include <linux/config.h>
@@ -106,7 +106,7 @@
  *     - lots more testing!!
  */
 
-#define DRIVER_VERSION "$Revision: 1.7 $"
+#define DRIVER_VERSION "$Revision: 1.9 $"
 #define DRIVER_AUTHOR "Roman Weissgaerber <[EMAIL PROTECTED]>, David Brownell"
 #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
 
diff -Nru a/drivers/usb/hcd/ohci-hub.c b/drivers/usb/hcd/ohci-hub.c
--- a/drivers/usb/hcd/ohci-hub.c        Wed Apr  3 16:39:30 2002
+++ b/drivers/usb/hcd/ohci-hub.c        Wed Apr  3 16:39:30 2002
@@ -5,7 +5,7 @@
  * (C) Copyright 2000-2002 David Brownell <[EMAIL PROTECTED]>
  * 
  * This file is licenced under GPL
- * $Id: ohci-hub.c,v 1.2 2002/01/19 00:21:49 dbrownell Exp $
+ * $Id: ohci-hub.c,v 1.3 2002/03/22 16:04:54 dbrownell Exp $
  */
 
 /*-------------------------------------------------------------------------*/
diff -Nru a/drivers/usb/hcd/ohci-mem.c b/drivers/usb/hcd/ohci-mem.c
--- a/drivers/usb/hcd/ohci-mem.c        Wed Apr  3 16:39:30 2002
+++ b/drivers/usb/hcd/ohci-mem.c        Wed Apr  3 16:39:30 2002
@@ -5,7 +5,7 @@
  * (C) Copyright 2000-2002 David Brownell <[EMAIL PROTECTED]>
  * 
  * This file is licenced under the GPL.
- * $Id: ohci-mem.c,v 1.2 2002/01/19 00:22:13 dbrownell Exp $
+ * $Id: ohci-mem.c,v 1.3 2002/03/22 16:04:54 dbrownell Exp $
  */
 
 /*-------------------------------------------------------------------------*/
diff -Nru a/drivers/usb/hcd/ohci-q.c b/drivers/usb/hcd/ohci-q.c
--- a/drivers/usb/hcd/ohci-q.c  Wed Apr  3 16:39:30 2002
+++ b/drivers/usb/hcd/ohci-q.c  Wed Apr  3 16:39:30 2002
@@ -5,7 +5,7 @@
  * (C) Copyright 2000-2002 David Brownell <[EMAIL PROTECTED]>
  * 
  * This file is licenced under the GPL.
- * $Id: ohci-q.c,v 1.6 2002/01/19 00:23:15 dbrownell Exp $
+ * $Id: ohci-q.c,v 1.8 2002/03/27 20:57:01 dbrownell Exp $
  */
 
 static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv)
@@ -54,124 +54,81 @@
 
 /*
  * URB goes back to driver, and isn't reissued.
- * It's completely gone from HC data structures, so no locking
- * is needed ... or desired! (Giveback can call back to hcd.)
+ * It's completely gone from HC data structures.
+ * PRECONDITION:  no locks held  (Giveback can call into HCD.)
  */
-static inline void finish_urb (struct ohci_hcd *ohci, struct urb *urb)
+static void finish_urb (struct ohci_hcd *ohci, struct urb *urb)
 {
-       if (urb->hcpriv) {
-               urb_free_priv (ohci, urb->hcpriv);
-               urb->hcpriv = NULL;
-       }
-       usb_hcd_giveback_urb (&ohci->hcd, urb);
-}
-
-static void td_submit_urb (struct urb *urb);
-
-/*
- * URB is reported to driver, is reissued if it's periodic.
- */
-static int return_urb (struct ohci_hcd *hc, struct urb *urb)
-{
-       urb_priv_t      *urb_priv = urb->hcpriv;
-       struct urb      *urbt;
        unsigned long   flags;
-       int             i;
 
 #ifdef DEBUG
-       if (!urb_priv) {
+       if (!urb->hcpriv) {
                err ("already unlinked!");
                BUG ();
        }
-
-       /* just to be sure */
-       if (!urb->complete) {
-               err ("no completion!");
-               BUG ();
-       }
 #endif
 
+       urb_free_priv (ohci, urb->hcpriv);
+       urb->hcpriv = NULL;
+
+       spin_lock_irqsave (&urb->lock, flags);
+       if (likely (urb->status == -EINPROGRESS))
+               urb->status = 0;
+       spin_unlock_irqrestore (&urb->lock, flags);
+
 #ifdef OHCI_VERBOSE_DEBUG
        urb_print (urb, "RET", usb_pipeout (urb->pipe));
 #endif
+       usb_hcd_giveback_urb (&ohci->hcd, urb);
+}
+
+static void td_submit_urb (struct urb *urb);
+
+/* Report interrupt transfer completion, maybe reissue */
+static void intr_resub (struct ohci_hcd *hc, struct urb *urb)
+{
+       urb_priv_t      *urb_priv = urb->hcpriv;
+       unsigned long   flags;
 
-       switch (usb_pipetype (urb->pipe)) {
-               case PIPE_INTERRUPT:
 #ifdef CONFIG_PCI
 // FIXME rewrite this resubmit path.  use pci_dma_sync_single()
 // and requeue more cheaply, and only if needed.
-                       pci_unmap_single (hc->hcd.pdev,
-                               urb_priv->td [0]->data_dma,
-                               urb->transfer_buffer_length,
-                               usb_pipeout (urb->pipe)
-                                       ? PCI_DMA_TODEVICE
-                                       : PCI_DMA_FROMDEVICE);
-#endif
-                       /* FIXME: MP race.  If another CPU partially unlinks
-                        * this URB (urb->status was updated, hasn't yet told
-                        * us to dequeue) before we call complete() here, an
-                        * extra "unlinked" completion will be reported...
-                        */
-                       urb->complete (urb);
+// Better yet ... abolish the notion of automagic resubmission.
+       pci_unmap_single (hc->hcd.pdev,
+               urb_priv->td [0]->data_dma,
+               urb->transfer_buffer_length,
+               usb_pipeout (urb->pipe)
+                       ? PCI_DMA_TODEVICE
+                       : PCI_DMA_FROMDEVICE);
+#endif
+       /* FIXME: MP race.  If another CPU partially unlinks
+        * this URB (urb->status was updated, hasn't yet told
+        * us to dequeue) before we call complete() here, an
+        * extra "unlinked" completion will be reported...
+        */
 
-                       /* always requeued, but ED_SKIP if complete() unlinks.
-                        * removed from periodic table only at SOF intr.
-                        */
-                       urb->actual_length = 0;
-                       if (urb_priv->state != URB_DEL)
-                               urb->status = -EINPROGRESS;
-                       spin_lock_irqsave (&hc->lock, flags);
-                       td_submit_urb (urb);
-                       spin_unlock_irqrestore (&hc->lock, flags);
-                       break;
+       spin_lock_irqsave (&urb->lock, flags);
+       if (likely (urb->status == -EINPROGRESS))
+               urb->status = 0;
+       spin_unlock_irqrestore (&urb->lock, flags);
 
-               case PIPE_ISOCHRONOUS:
-                       for (urbt = urb->next;
-                                       urbt && (urbt != urb);
-                                       urbt = urbt->next)
-                               continue;
-                       if (urbt) { /* send the reply and requeue URB */        
-#ifdef CONFIG_PCI
-// FIXME rewrite this resubmit path too
-                               pci_unmap_single (hc->hcd.pdev,
-                                       urb_priv->td [0]->data_dma,
-                                       urb->transfer_buffer_length,
-                                       usb_pipeout (urb->pipe)
-                                               ? PCI_DMA_TODEVICE
-                                               : PCI_DMA_FROMDEVICE);
-#endif
-                               urb->complete (urb);
-                               spin_lock_irqsave (&hc->lock, flags);
-                               urb->actual_length = 0;
-                               urb->status = -EINPROGRESS;
-                               urb->start_frame = urb_priv->ed->last_iso + 1;
-                               if (urb_priv->state != URB_DEL) {
-                                       for (i = 0; i < urb->number_of_packets;
-                                                       i++) {
-                                               urb->iso_frame_desc [i]
-                                                       .actual_length = 0;
-                                               urb->iso_frame_desc [i]
-                                                       .status = -EXDEV;
-                                       }
-                                       td_submit_urb (urb);
-                               }
-// FIXME if not deleted, should have been "finished"
-                               spin_unlock_irqrestore (&hc->lock, flags);
-
-                       } else { /* not reissued */
-                               finish_urb (hc, urb);
-                       }               
-                       break;
+#ifdef OHCI_VERBOSE_DEBUG
+       urb_print (urb, "INTR", usb_pipeout (urb->pipe));
+#endif
+       urb->complete (urb);
 
-               /*
-                * C/B requests that get here are never reissued.
-                */
-               case PIPE_BULK:
-               case PIPE_CONTROL:
-                       finish_urb (hc, urb);
-                       break;
-       }
-       return 0;
+       /* always requeued, but ED_SKIP if complete() unlinks.
+        * EDs are removed from periodic table only at SOF intr.
+        */
+       urb->actual_length = 0;
+       spin_lock_irqsave (&urb->lock, flags);
+       if (urb_priv->state != URB_DEL)
+               urb->status = -EINPROGRESS;
+       spin_unlock (&urb->lock);
+
+       spin_lock (&hc->lock);
+       td_submit_urb (urb);
+       spin_unlock_irqrestore (&hc->lock, flags);
 }
 
 
@@ -329,6 +286,26 @@
 
 /*-------------------------------------------------------------------------*/
 
+/* scan the periodic table to find and unlink this ED */
+static void periodic_unlink (
+       struct ohci_hcd *ohci,
+       struct ed       *ed,
+       unsigned        index,
+       unsigned        period
+) {
+       for (; index < NUM_INTS; index += period) {
+               __u32   *ed_p = &ohci->hcca->int_table [index];
+
+               while (*ed_p != 0) {
+                       if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
+                               *ed_p = ed->hwNextED;           
+                               break;
+                       }
+                       ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED);
+               }
+       }       
+}
+
 /* unlink an ed from one of the HC chains. 
  * just the link to the ed is unlinked.
  * the link from the ed still points to another operational ed or 0
@@ -336,11 +313,7 @@
  */
 static int ep_unlink (struct ohci_hcd *ohci, struct ed *ed) 
 {
-       int     int_branch;
        int     i;
-       int     inter;
-       int     interval;
-       __u32   *ed_p;
 
        ed->hwINFO |= ED_SKIP;
 
@@ -384,22 +357,9 @@
                break;
 
        case PIPE_INTERRUPT:
-               int_branch = ed->int_branch;
-               interval = ed->int_interval;
-
-               for (i = 0; i < ep_rev (6, interval); i += inter) {
-                       for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i) + 
int_branch]), inter = 1; 
-                                (*ed_p != 0) && (*ed_p != ed->hwNextED); 
-                               ed_p = & ((dma_to_ed (ohci, le32_to_cpup 
(ed_p)))->hwNextED), 
-                               inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup 
(ed_p)))->int_interval)) {                            
-                                       if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == 
ed) {
-                                               *ed_p = ed->hwNextED;           
-                                               break;
-                                       }
-                         }
-               }
-               for (i = int_branch; i < NUM_INTS; i += interval)
-                   ohci->ohci_int_load [i] -= ed->int_load;
+               periodic_unlink (ohci, ed, ed->int_branch, ed->int_interval);
+               for (i = ed->int_branch; i < NUM_INTS; i += ed->int_interval)
+                       ohci->ohci_int_load [i] -= ed->int_load;
 #ifdef OHCI_VERBOSE_DEBUG
                ohci_dump_periodic (ohci, "UNLINK_INT");
 #endif
@@ -412,21 +372,10 @@
                        (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))
                                ->ed_prev = ed->ed_prev;
 
-               if (ed->ed_prev != NULL) {
+               if (ed->ed_prev != NULL)
                        ed->ed_prev->hwNextED = ed->hwNextED;
-               } else {
-                       for (i = 0; i < NUM_INTS; i++) {
-                               for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i)]); 
-                                               *ed_p != 0; 
-                                               ed_p = & ((dma_to_ed (ohci, 
le32_to_cpup (ed_p)))->hwNextED)) {
-                                       // inter = ep_rev (6, (dma_to_ed (ohci, 
le32_to_cpup (ed_p)))->int_interval);
-                                       if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == 
ed) {
-                                               *ed_p = ed->hwNextED;           
-                                               break;
-                                       }
-                               }
-                       }       
-               }       
+               else
+                       periodic_unlink (ohci, ed, 0, 1);
 #ifdef OHCI_VERBOSE_DEBUG
                ohci_dump_periodic (ohci, "UNLINK_ISO");
 #endif
@@ -584,6 +533,12 @@
                return;
        }
 
+#if 0
+       /* no interrupt needed except for URB's last TD */
+       if (index != (urb_priv->length - 1))
+               info |= TD_DI;
+#endif
+
        /* use this td as the next dummy */
        td_pt = urb_priv->td [index];
        td_pt->hwNextTD = 0;
@@ -660,6 +615,9 @@
        } else
                data = 0;
 
+       /* NOTE:  TD_CC is set so we can tell which TDs the HC processed by
+        * using TD_CC_GET, as well as by seeing them on the done list.
+        */
        switch (usb_pipetype (urb->pipe)) {
                case PIPE_BULK:
                        info = usb_pipeout (urb->pipe)
@@ -726,8 +684,14 @@
 
                case PIPE_ISOCHRONOUS:
                        for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
-                               td_fill (ohci, TD_CC | TD_ISO
-                                       | ((urb->start_frame + cnt) & 0xffff), 
+                               int     frame = urb->start_frame;
+
+                               // FIXME scheduling should handle frame counter
+                               // roll-around ... exotic case (and OHCI has
+                               // a 2^16 iso range, vs other HCs max of 2^10)
+                               frame += cnt * urb->interval;
+                               frame &= 0xffff;
+                               td_fill (ohci, TD_CC | TD_ISO | frame,
                                    data + urb->iso_frame_desc [cnt].offset, 
                                    urb->iso_frame_desc [cnt].length, urb, cnt); 
                        }
@@ -741,50 +705,77 @@
  * Done List handling functions
  *-------------------------------------------------------------------------*/
 
-/* calculate the transfer length and update the urb */
-
-static void dl_transfer_length (struct td *td)
+/* calculate transfer length/status and update the urb
+ * PRECONDITION:  irqsafe (only for urb->status locking)
+ */
+static void td_done (struct urb *urb, struct td *td)
 {
-       __u32           tdINFO, tdBE, tdCBP;
-       __u16           tdPSW;
-       struct urb      *urb = td->urb;
-       urb_priv_t      *urb_priv = urb->hcpriv;
-       int             dlen = 0;
-       int             cc = 0;
-
-       tdINFO = le32_to_cpup (&td->hwINFO);
-       tdBE   = le32_to_cpup (&td->hwBE);
-       tdCBP  = le32_to_cpup (&td->hwCBP);
+       u32     tdINFO = le32_to_cpup (&td->hwINFO);
+       int     cc = 0;
 
 
+       /* ISO ... drivers see per-TD length/status */
        if (tdINFO & TD_ISO) {
-               tdPSW = le16_to_cpu (td->hwPSW [0]);
+               u16     tdPSW = le16_to_cpu (td->hwPSW [0]);
+               int     dlen = 0;
+
                cc = (tdPSW >> 12) & 0xF;
-               if (cc < 0xE)  {
-                       if (usb_pipeout (urb->pipe)) {
-                               dlen = urb->iso_frame_desc [td->index].length;
-                       } else {
-                               dlen = tdPSW & 0x3ff;
-                       }
-                       urb->actual_length += dlen;
-                       urb->iso_frame_desc [td->index].actual_length = dlen;
-                       if (! (urb->transfer_flags & USB_DISABLE_SPD)
-                                       && (cc == TD_DATAUNDERRUN))
-                               cc = TD_CC_NOERROR;
-
-                       urb->iso_frame_desc [td->index].status
-                               = cc_to_error [cc];
-               }
-       } else { /* BULK, INT, CONTROL DATA */
-               if (! (usb_pipetype (urb->pipe) == PIPE_CONTROL && 
-                                ((td->index == 0)
-                                || (td->index == urb_priv->length - 1)))) {
-                       if (tdBE != 0) {
-                               urb->actual_length += (td->hwCBP == 0)
-                                       ? (tdBE - td->data_dma + 1)
-                                       : (tdCBP - td->data_dma);
-                       }
-               }
+               if (! ((urb->transfer_flags & USB_DISABLE_SPD)
+                               && (cc == TD_DATAUNDERRUN)))
+                       cc = TD_CC_NOERROR;
+
+               if (usb_pipeout (urb->pipe))
+                       dlen = urb->iso_frame_desc [td->index].length;
+               else
+                       dlen = tdPSW & 0x3ff;
+               urb->actual_length += dlen;
+               urb->iso_frame_desc [td->index].actual_length = dlen;
+               urb->iso_frame_desc [td->index].status = cc_to_error [cc];
+
+               if (cc != 0)
+                       dbg ("  urb %p iso TD %d len %d CC %d",
+                               urb, td->index, dlen, cc);
+
+       /* BULK, INT, CONTROL ... drivers see aggregate length/status,
+        * except that "setup" bytes aren't counted and "short" transfers
+        * might not be reported as errors.
+        */
+       } else {
+               int     type = usb_pipetype (urb->pipe);
+               u32     tdBE = le32_to_cpup (&td->hwBE);
+
+               cc = TD_CC_GET (tdINFO);
+
+               /* control endpoints only have soft stalls */
+               if (type != PIPE_CONTROL && cc == TD_CC_STALL)
+                       usb_endpoint_halt (urb->dev,
+                               usb_pipeendpoint (urb->pipe),
+                               usb_pipeout (urb->pipe));
+
+               /* update packet status if needed (short may be ok) */
+               if (((urb->transfer_flags & USB_DISABLE_SPD) != 0
+                               && cc == TD_DATAUNDERRUN))
+                       cc = TD_CC_NOERROR;
+               if (cc != TD_CC_NOERROR) {
+                       spin_lock (&urb->lock);
+                       if (urb->status == -EINPROGRESS)
+                               urb->status = cc_to_error [cc];
+                       spin_unlock (&urb->lock);
+               }
+
+               /* count all non-empty packets except control SETUP packet */
+               if ((type != PIPE_CONTROL || td->index != 0) && tdBE != 0) {
+                       if (td->hwCBP == 0)
+                               urb->actual_length += tdBE - td->data_dma + 1;
+                       else
+                               urb->actual_length +=
+                                         le32_to_cpup (&td->hwCBP)
+                                       - td->data_dma;
+               }
+
+               if (cc != 0)
+                       dbg ("  urb %p TD %d CC %d, len=%d",
+                               urb, td->index, cc, urb->actual_length);
        }
 }
 
@@ -811,13 +802,16 @@
 
                if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) {
                        urb_priv = (urb_priv_t *) td_list->urb->hcpriv;
-                       dbg (" USB-error/status: %x : %p", 
-                               TD_CC_GET (le32_to_cpup (&td_list->hwINFO)),
-                               td_list);
-                       /* typically the endpoint halted too */
+                       /* typically the endpoint halts on error; un-halt,
+                        * and maybe dequeue other TDs from this urb
+                        */
                        if (td_list->ed->hwHeadP & ED_H) {
                                if (urb_priv && ((td_list->index + 1)
                                                < urb_priv->length)) {
+                                       dbg ("urb %p TD %d of %d, patch ED",
+                                               td_list->urb,
+                                               1 + td_list->index,
+                                               urb_priv->length);
                                        td_list->ed->hwHeadP = 
                            (urb_priv->td [urb_priv->length - 1]->hwNextTD
                                    & __constant_cpu_to_le32 (TD_MASK))
@@ -870,16 +864,19 @@
                                le32_to_cpup (&td->hwNextTD));
                        if ((urb_priv->state == URB_DEL)) {
                                tdINFO = le32_to_cpup (&td->hwINFO);
+                               /* HC may have partly processed this TD */
                                if (TD_CC_GET (tdINFO) < 0xE)
-                                       dl_transfer_length (td);
+                                       td_done (urb, td);
                                *td_p = td->hwNextTD | (*td_p
                                        & __constant_cpu_to_le32 (0x3));
 
                                /* URB is done; clean up */
-                               if (++ (urb_priv->td_cnt) == urb_priv->length)
-// FIXME:  we shouldn't hold ohci->lock here, else the
-// completion function can't talk to this hcd ...
+                               if (++ (urb_priv->td_cnt) == urb_priv->length) {
+                                       spin_unlock_irqrestore (&ohci->lock,
+                                               flags);
                                        finish_urb (ohci, urb);
+                                       spin_lock_irqsave (&ohci->lock, flags);
+                               }
                        } else {
                                td_p = &td->hwNextTD;
                        }
@@ -931,71 +928,52 @@
 /*-------------------------------------------------------------------------*/
 
 /*
- * process normal completions (error or success) and some unlinked eds
- * this is the main path for handing urbs back to drivers
+ * Process normal completions (error or success) and clean the schedules.
+ *
+ * This is the main path for handing urbs back to drivers.  The only other
+ * path is dl_del_list(), which unlinks URBs by scanning EDs, instead of
+ * scanning the (re-reversed) donelist as this does.
  */
-static void dl_done_list (struct ohci_hcd *ohci, struct td *td_list)
+static void dl_done_list (struct ohci_hcd *ohci, struct td *td)
 {
-       struct td       *td_list_next = NULL;
-       struct ed       *ed;
-       int             cc = 0;
-       struct urb      *urb;
-       urb_priv_t      *urb_priv;
-       __u32           tdINFO;
-
-       unsigned long flags;
-
-       while (td_list) {
-               td_list_next = td_list->next_dl_td;
-
-               urb = td_list->urb;
-               urb_priv = urb->hcpriv;
-               tdINFO = le32_to_cpup (&td_list->hwINFO);
-
-               ed = td_list->ed;
-
-               dl_transfer_length (td_list);
+       unsigned long   flags;
 
-               /* error code of transfer */
-               cc = TD_CC_GET (tdINFO);
-               if (cc == TD_CC_STALL)
-                       usb_endpoint_halt (urb->dev,
-                               usb_pipeendpoint (urb->pipe),
-                               usb_pipeout (urb->pipe));
+       spin_lock_irqsave (&ohci->lock, flags);
+       while (td) {
+               struct td       *td_next = td->next_dl_td;
+               struct urb      *urb = td->urb;
+               urb_priv_t      *urb_priv = urb->hcpriv;
+               struct ed       *ed = td->ed;
+
+               /* update URB's length and status from TD */
+               td_done (urb, td);
+               urb_priv->td_cnt++;
+
+               /* If all this urb's TDs are done, call complete().
+                * Interrupt transfers are the only special case:
+                * they're reissued, until "deleted" by usb_unlink_urb
+                * (real work done in a SOF intr, by dl_del_list).
+                */
+               if (urb_priv->td_cnt == urb_priv->length) {
+                       int     resubmit;
 
-               if (! (urb->transfer_flags & USB_DISABLE_SPD)
-                               && (cc == TD_DATAUNDERRUN))
-                       cc = TD_CC_NOERROR;
+                       resubmit = usb_pipeint (urb->pipe)
+                                       && (urb_priv->state != URB_DEL);
 
-               if (++ (urb_priv->td_cnt) == urb_priv->length) {
-                       /*
-                        * Except for periodic transfers, both branches do
-                        * the same thing.  Periodic urbs get reissued until
-                        * they're "deleted" (in SOF intr) by usb_unlink_urb.
-                        */
-                       if ((ed->state & (ED_OPER | ED_UNLINK))
-                                       && (urb_priv->state != URB_DEL)) {
-                               spin_lock (&urb->lock);
-                               if (urb->status == -EINPROGRESS)
-                                       urb->status = cc_to_error [cc];
-                               spin_unlock (&urb->lock);
-                               return_urb (ohci, urb);
-                       } else
+                       spin_unlock_irqrestore (&ohci->lock, flags);
+                       if (resubmit)
+                               intr_resub (ohci, urb);
+                       else
                                finish_urb (ohci, urb);
+                       spin_lock_irqsave (&ohci->lock, flags);
                }
 
-               spin_lock_irqsave (&ohci->lock, flags);
-               if (ed->state != ED_NEW) { 
-                       u32 edHeadP = ed->hwHeadP;
-
-                       /* unlink eds if they are not busy */
-                       edHeadP &= __constant_cpu_to_le32 (ED_MASK);
-                       if ((edHeadP == ed->hwTailP) && (ed->state == ED_OPER)) 
-                               ep_unlink (ohci, ed);
-               }       
-               spin_unlock_irqrestore (&ohci->lock, flags);
-
-               td_list = td_list_next;
+               /* clean schedule:  unlink EDs that are no longer busy */
+               if ((ed->hwHeadP & __constant_cpu_to_le32 (TD_MASK))
+                                       == ed->hwTailP
+                               && (ed->state == ED_OPER)) 
+                       ep_unlink (ohci, ed);
+               td = td_next;
        }  
+       spin_unlock_irqrestore (&ohci->lock, flags);
 }
-
diff -Nru a/drivers/usb/hcd/ohci.h b/drivers/usb/hcd/ohci.h
--- a/drivers/usb/hcd/ohci.h    Wed Apr  3 16:39:30 2002
+++ b/drivers/usb/hcd/ohci.h    Wed Apr  3 16:39:30 2002
@@ -5,7 +5,7 @@
  * (C) Copyright 2000-2002 David Brownell <[EMAIL PROTECTED]>
  * 
  * This file is licenced under the GPL.
- * $Id: ohci.h,v 1.5 2002/01/19 00:24:01 dbrownell Exp $
+ * $Id: ohci.h,v 1.6 2002/03/22 16:04:54 dbrownell Exp $
  */
  
 /*

_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to