# 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.579.1.21 -> 1.579.9.1
#       drivers/usb/host/ohci-hcd.c     1.25    -> 1.26   
#       drivers/usb/host/ohci-q.c       1.22    -> 1.23   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/09/23      [EMAIL PROTECTED]     1.579.9.1
# [PATCH] ohci-hcd, queue fault recovery + rm DEBUG
# 
# This USB patch updates the OHCI driver:
# 
#   - converts to relying on td_list shadowing the hardware's
#     schedule; only collecting the donelist needs dma_to_td(),
#     and td list handling works much like EHCI or UHCI.
# 
#   - leaves faulted endpoint queues (bulk/intr) disabled until
#     the relevant drivers had a chance to clean up.
# 
#   - fixes minor bugs (unreported) in the affected code:
#       * byteswap problem when unlinking urbs ... symptom would
#         be data toggle confusion (since 2.4.2x) on big-endian cpus
#       * latent bug if folk unlinked queue in LIFO order, not FIFO
# 
#   - removes unnecessary debug code; mostly de-BUG()ged
# 
# The interesting fix is the "leave queues halted" one.  As
# discussed on email a while back, this HCD fault handling
# policy (also followed by EHCI) is sufficient to let device
# drivers implement the two key fault handling policies that
# seem to be necessary:
# 
#     (a) Datagram style, where issues on one I/O won't affect
#         the next unless the device halted the endpoint.  The
#         device driver can ignore most errors other than -EPIPE.
# 
#     (b) Stream style, where for example it'd be wrong to ever
#         let block N+1 overwrite block N on the disk.  Once
#         the first URB fails, the rest would just be unlinked
#         in the completion handler.
# 
# As a consequence of using the td_list, you can now see urb
# queuing in action in the driverfs 'async' file.  At least, if
# you look at the right time, or use drivers (networking, etc)
# that queue (bulk) reads for a long time.
# --------------------------------------------
#
diff -Nru a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
--- a/drivers/usb/host/ohci-hcd.c       Mon Sep 23 15:16:11 2002
+++ b/drivers/usb/host/ohci-hcd.c       Mon Sep 23 15:16:11 2002
@@ -108,7 +108,7 @@
  *     - lots more testing!!
  */
 
-#define DRIVER_VERSION "2002-Sep-03"
+#define DRIVER_VERSION "2002-Sep-17"
 #define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
 #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
 
@@ -318,9 +318,6 @@
        struct hcd_dev          *dev = (struct hcd_dev *) udev->hcpriv;
        int                     i;
        unsigned long           flags;
-#ifdef DEBUG
-       int                     rescans = 0;
-#endif
 
 rescan:
        /* free any eds, and dummy tds, still hanging around */
@@ -340,16 +337,18 @@
                        td_free (ohci, ed->dummy);
                        break;
                default:
-#ifdef DEBUG
-                       err ("illegal ED %d state in free_config, %d",
+                       err ("%s-%s ed %p (#%d) not unlinked; disconnect() bug? %d",
+                               ohci->hcd.self.bus_name, udev->devpath, ed,
                                i, ed->state);
-#endif
                        /* ED_OPER: some driver disconnect() is broken,
                         * it didn't even start its unlinks much less wait
                         * for their completions.
                         * OTHERWISE:  hcd bug, ed is garbage
+                        *
+                        * ... we can't recycle this memory in either case,
+                        * so just leak it to avoid oopsing.
                         */
-                       BUG ();
+                       continue;
                }
                ed_free (ohci, ed);
        }
@@ -360,13 +359,9 @@
 #ifdef DEBUG
        /* a driver->disconnect() returned before its unlinks completed? */
        if (in_interrupt ()) {
-               dbg ("WARNING: spin in interrupt; driver->disconnect() bug");
-               dbg ("dev usb-%s-%s ep 0x%x", 
+               warn ("disconnect() bug for dev usb-%s-%s ep 0x%x", 
                        ohci->hcd.self.bus_name, udev->devpath, i);
        }
-       BUG_ON (!(readl (&ohci->regs->intrenable) & OHCI_INTR_SF));
-       BUG_ON (rescans >= 2);  /* HWBUG */
-       rescans++;
 #endif
 
        spin_unlock_irqrestore (&ohci->lock, flags);
diff -Nru a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
--- a/drivers/usb/host/ohci-q.c Mon Sep 23 15:16:11 2002
+++ b/drivers/usb/host/ohci-q.c Mon Sep 23 15:16:11 2002
@@ -36,12 +36,7 @@
 {
        unsigned long   flags;
 
-#ifdef DEBUG
-       if (!urb->hcpriv) {
-               err ("already unlinked!");
-               BUG ();
-       }
-#endif
+       // ASSERT (urb->hcpriv != 0);
 
        urb_free_priv (ohci, urb->hcpriv);
        urb->hcpriv = NULL;
@@ -51,6 +46,7 @@
                urb->status = 0;
        spin_unlock_irqrestore (&urb->lock, flags);
 
+       // what lock protects these?
        switch (usb_pipetype (urb->pipe)) {
        case PIPE_ISOCHRONOUS:
                ohci->hcd.self.bandwidth_isoc_reqs--;
@@ -425,6 +421,9 @@
        /* FIXME:  Don't do this without knowing it's safe to clobber this
         * state/mode info.  Currently the upper layers don't support such
         * guarantees; we're lucky changing config/altsetting is rare.
+        * The state/mode info also changes during enumeration: set_address
+        * uses the 'wrong' device address, and ep0 maxpacketsize will often
+        * improve on the initial value.
         */
        if (ed->state == ED_IDLE) {
                u32     info;
@@ -454,25 +453,6 @@
                }
                ed->hwINFO = info;
 
-#ifdef DEBUG
-       /*
-        * There are two other cases we ought to change hwINFO, both during
-        * enumeration.  There, the control request completes, unlinks, and
-        * the next request gets queued before the unlink completes, so it
-        * uses old/wrong hwINFO.  How much of a problem is this?  khubd is
-        * already retrying after such failures...
-        */
-       } else if (type == PIPE_CONTROL) {
-               u32     info = le32_to_cpup (&ed->hwINFO);
-
-               if (!(info & 0x7f))
-                       dbg ("RETRY ctrl: address != 0");
-               info >>= 16;
-               if (info != udev->epmaxpacketin [0])
-                       dbg ("RETRY ctrl: maxpacket %d != 8",
-                               udev->epmaxpacketin [0]);
-
-#endif /* DEBUG */
        }
 
 done:
@@ -526,10 +506,7 @@
        struct urb_priv         *urb_priv = urb->hcpriv;
        int                     is_iso = info & TD_ISO;
 
-       if (index >= urb_priv->length) {
-               err ("internal OHCI error: TD index > length");
-               return;
-       }
+       // ASSERT (index < urb_priv->length);
 
        /* aim for only one interrupt per urb.  mostly applies to control
         * and iso; other urbs rarely need more than one TD per urb.
@@ -578,6 +555,7 @@
        wmb ();
 
        /* append to queue */
+       list_add_tail (&td->td_list, &td->ed->td_list);
        td->ed->hwTailP = td->hwNextTD;
 }
 
@@ -698,8 +676,7 @@
                ohci->hcd.self.bandwidth_isoc_reqs++;
                break;
        }
-       if (urb_priv->length != cnt)
-               dbg ("TD LENGTH %d != CNT %d", urb_priv->length, cnt);
+       // ASSERT (urb_priv->length == cnt);
 }
 
 /*-------------------------------------------------------------------------*
@@ -714,6 +691,8 @@
        u32     tdINFO = le32_to_cpup (&td->hwINFO);
        int     cc = 0;
 
+       list_del (&td->td_list);
+
        /* ISO ... drivers see per-TD length/status */
        if (tdINFO & TD_ISO) {
                u16     tdPSW = le16_to_cpu (td->hwPSW [0]);
@@ -792,74 +771,106 @@
 
 /*-------------------------------------------------------------------------*/
 
+static inline struct td *
+ed_halted (struct ohci_hcd *ohci, struct td *td, int cc, struct td *rev)
+{
+       struct urb              *urb = td->urb;
+       struct ed               *ed = td->ed;
+       struct list_head        *tmp = td->td_list.next;
+       u32                     toggle = ed->hwHeadP & ED_C;
+
+       /* clear ed halt; this is the td that caused it, but keep it inactive
+        * until its urb->complete() has a chance to clean up.
+        */
+       ed->hwINFO |= ED_SKIP;
+       wmb ();
+       td->ed->hwHeadP &= ~ED_H; 
+
+       while (tmp != &ed->td_list) {
+               struct td       *next;
+
+               next = list_entry (tmp, struct td, td_list);
+               tmp = next->td_list.next;
+
+               /* move other tds from this urb to the donelist, after 'td'.
+                * order won't matter here: no errors, nothing transferred.
+                *
+                * NOTE: this "knows" short control reads won't need fixup:
+                * hc went from the (one) data TD to the status td. that'll
+                * change if multi-td control DATA segments are supported,
+                * and we want to send the status packet.
+                */
+               if (next->urb == urb) {
+                       u32     info = next->hwINFO;
+
+                       info |= cpu_to_le32 (TD_DONE);
+                       info &= ~cpu_to_le32 (TD_CC);
+                       next->hwINFO = info;
+                       next->next_dl_td = rev; 
+                       rev = next;
+                       continue;
+               }
+
+               /* restart ed with first td of this next urb */
+               ed->hwHeadP = cpu_to_le32 (next->td_dma) | toggle;
+               tmp = 0;
+               break;
+       }
+
+       /* no urbs queued? then ED is empty. */
+       if (tmp)
+               ed->hwHeadP = cpu_to_le32 (ed->dummy->td_dma) | toggle;
+
+       /* help for troubleshooting: */
+       dbg ("urb %p usb-%s-%s ep-%d-%s cc %d --> status %d",
+               urb,
+               urb->dev->bus->bus_name, urb->dev->devpath,
+               usb_pipeendpoint (urb->pipe),
+               usb_pipein (urb->pipe) ? "IN" : "OUT",
+               cc, cc_to_error [cc]);
+
+       return rev;
+}
+
 /* replies to the request have to be on a FIFO basis so
  * we unreverse the hc-reversed done-list
  */
 static struct td *dl_reverse_done_list (struct ohci_hcd *ohci)
 {
-       __u32           td_list_hc;
+       u32             td_dma;
        struct td       *td_rev = NULL;
-       struct td       *td_list = NULL;
-       urb_priv_t      *urb_priv = NULL;
+       struct td       *td = NULL;
        unsigned long   flags;
 
        spin_lock_irqsave (&ohci->lock, flags);
 
-       td_list_hc = le32_to_cpup (&ohci->hcca->done_head);
+       td_dma = le32_to_cpup (&ohci->hcca->done_head);
        ohci->hcca->done_head = 0;
 
-       while (td_list_hc) {            
+       /* get TD from hc's singly linked list, and
+        * prepend to ours.  ed->td_list changes later.
+        */
+       while (td_dma) {                
                int             cc;
 
-               td_list = dma_to_td (ohci, td_list_hc);
+               td = dma_to_td (ohci, td_dma);
 
-               td_list->hwINFO |= cpu_to_le32 (TD_DONE);
+               td->hwINFO |= cpu_to_le32 (TD_DONE);
+               cc = TD_CC_GET (le32_to_cpup (&td->hwINFO));
 
-               cc = TD_CC_GET (le32_to_cpup (&td_list->hwINFO));
-               if (cc != TD_CC_NOERROR) {
-                       urb_priv = (urb_priv_t *) td_list->urb->hcpriv;
-
-                       /* Non-iso endpoints can halt on error; un-halt,
-                        * and dequeue any other TDs from this urb.
-                        * No other TD could have caused the halt.
-                        */
-                       if (td_list->ed->hwHeadP & ED_H) {
-                               if (urb_priv && ((td_list->index + 1)
-                                               < urb_priv->length)) {
-#ifdef DEBUG
-                                       struct urb *urb = td_list->urb;
-
-                                       /* help for troubleshooting: */
-                                       dbg ("urb %p usb-%s-%s ep-%d-%s "
-                                                   "(td %d/%d), "
-                                                   "cc %d --> status %d",
-                                               td_list->urb,
-                                               urb->dev->bus->bus_name,
-                                               urb->dev->devpath,
-                                               usb_pipeendpoint (urb->pipe),
-                                               usb_pipein (urb->pipe)
-                                                   ? "IN" : "OUT",
-                                               1 + td_list->index,
-                                               urb_priv->length,
-                                               cc, cc_to_error [cc]);
-#endif
-                                       td_list->ed->hwHeadP = 
-                           (urb_priv->td [urb_priv->length - 1]->hwNextTD
-                                   & __constant_cpu_to_le32 (TD_MASK))
-                           | (td_list->ed->hwHeadP & ED_C);
-                                       urb_priv->td_cnt += urb_priv->length
-                                               - td_list->index - 1;
-                               } else 
-                                       td_list->ed->hwHeadP &= ~ED_H;
-                       }
-               }
+               /* Non-iso endpoints can halt on error; un-halt,
+                * and dequeue any other TDs from this urb.
+                * No other TD could have caused the halt.
+                */
+               if (cc != TD_CC_NOERROR && (td->ed->hwHeadP & ED_H))
+                       td_rev = ed_halted (ohci, td, cc, td_rev);
 
-               td_list->next_dl_td = td_rev;   
-               td_rev = td_list;
-               td_list_hc = le32_to_cpup (&td_list->hwNextTD);
+               td->next_dl_td = td_rev;        
+               td_rev = td;
+               td_dma = le32_to_cpup (&td->hwNextTD);
        }       
        spin_unlock_irqrestore (&ohci->lock, flags);
-       return td_list;
+       return td_rev;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -874,9 +885,9 @@
 
 rescan_all:
        for (last = &ohci->ed_rm_list, ed = *last; ed != NULL; ed = *last) {
-               struct td       *td, *td_next, *tdHeadP, *tdTailP;
-               u32             *td_p;
-               int             completed, modified;
+               struct list_head        *entry, *tmp;
+               int                     completed, modified;
+               u32                     *prev;
 
                /* only take off EDs that the HC isn't using, accounting for
                 * frame counter wraps.
@@ -897,53 +908,52 @@
                /* unlink urbs as requested, but rescan the list after
                 * we call a completion since it might have unlinked
                 * another (earlier) urb
-                *
-                * FIXME use td_list to scan, not td hashtables.
                 */
 rescan_this:
                completed = 0;
+               prev = &ed->hwHeadP;
+               list_for_each_safe (entry, tmp, &ed->td_list) {
+                       struct td       *td;
+                       struct urb      *urb;
+                       urb_priv_t      *urb_priv;
+                       u32             savebits;
+
+                       td = list_entry (entry, struct td, td_list);
+                       urb = td->urb;
+                       urb_priv = td->urb->hcpriv;
 
-               tdTailP = dma_to_td (ohci, le32_to_cpup (&ed->hwTailP));
-               tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP));
-               td_p = &ed->hwHeadP;
-
-               for (td = tdHeadP; td != tdTailP; td = td_next) {
-                       struct urb *urb = td->urb;
-                       urb_priv_t *urb_priv = td->urb->hcpriv;
-
-                       td_next = dma_to_td (ohci,
-                               le32_to_cpup (&td->hwNextTD));
-                       if (urb_priv->state == URB_DEL) {
-
-                               /* HC may have partly processed this TD */
-                               td_done (urb, td);
-                               urb_priv->td_cnt++;
-
-                               *td_p = td->hwNextTD | (*td_p & ~TD_MASK);
-
-                               /* URB is done; clean up */
-                               if (urb_priv->td_cnt == urb_priv->length) {
-                                       modified = completed = 1;
-                                       spin_unlock (&ohci->lock);
-                                       finish_urb (ohci, urb);
-                                       spin_lock (&ohci->lock);
-                               }
-                       } else {
-                               td_p = &td->hwNextTD;
+                       if (urb_priv->state != URB_DEL) {
+                               prev = &td->hwNextTD;
+                               continue;
+                       }
+
+                       /* patch pointer hc uses */
+                       savebits = *prev & cpu_to_le32 (TD_MASK);
+                       *prev = td->hwNextTD | savebits;
+
+                       /* HC may have partly processed this TD */
+                       td_done (urb, td);
+                       urb_priv->td_cnt++;
+
+                       /* if URB is done, clean up */
+                       if (urb_priv->td_cnt == urb_priv->length) {
+                               modified = completed = 1;
+                               spin_unlock (&ohci->lock);
+                               finish_urb (ohci, urb);
+                               spin_lock (&ohci->lock);
                        }
                }
+               if (completed && !list_empty (&ed->td_list))
+                       goto rescan_this;
 
                /* ED's now officially unlinked, hc doesn't see */
                ed->state = ED_IDLE;
-               ed->hwINFO &= ~ED_SKIP;
+               ed->hwINFO &= ~(ED_SKIP | ED_DEQUEUE);
                ed->hwHeadP &= ~ED_H;
                ed->hwNextED = 0;
 
                /* but if there's work queued, reschedule */
-               tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP));
-               if (tdHeadP != tdTailP) {
-                       if (completed)
-                               goto rescan_this;
+               if (!list_empty (&ed->td_list)) {
                        if (!ohci->disabled && !ohci->sleeping)
                                ed_schedule (ohci, ed);
                }
@@ -1027,10 +1037,15 @@
                }
 
                /* clean schedule:  unlink EDs that are no longer busy */
-               if ((ed->hwHeadP & __constant_cpu_to_le32 (TD_MASK))
-                                       == ed->hwTailP
-                               && (ed->state == ED_OPER)) 
+               if (list_empty (&ed->td_list))
                        ed_deschedule (ohci, ed);
+               /* ... reenabling halted EDs only after fault cleanup */
+               else if (!(ed->hwINFO & ED_DEQUEUE)) {
+                       td = list_entry (ed->td_list.next, struct td, td_list);
+                       if (!(td->hwINFO & TD_DONE))
+                               ed->hwINFO &= ~ED_SKIP;
+               }
+
                td = td_next;
        }  
        spin_unlock_irqrestore (&ohci->lock, flags);


-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
Welcome to geek heaven.
http://thinkgeek.com/sf
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to