On Fri, 20 Jun 2014, Dennis New wrote:
> > However, lsusb does not work, and I cannot kill it -- it remains in D
> > (uninterruptible sleep) state. I am also unable to rmmod the
> > snd_usb_audio module -- rmmod gets stuck in D state too.
> >
> > When I tried echoing (0000:00:13.0) to ohci_pci/unbind, although dmesg
> > says:
> >
> > [140078.654462] ohci-pci 0000:00:13.0: remove, state 1
> > [140078.654480] usb usb2: USB disconnect, device number 1
> >
> > ... the echo command (a bash internal) does not terminate, and also
> > gets stuck in D state.
> >
> > Although the symlink to 0000:00:13.0 does get removed from that
> > directory. But trying to echo this back to ohci_pci/bind doesn't work:
> >
> > "write error: No such device"
> >
> > Even though 0000:00:13.0 is still listed
> > in /sys/kernel/debug/usb/ohci.
> >
> > Try to "cat" /sys/kernel/debug/usb/devices also fails, and cat gets
> > stuck in D state too.
> >
> > My usb thumbdrive also no longer works after this occurs, although I'm
> > pretty sure it uses EHCI. I'm forced to reboot to get usb working
> > again :s.
Here's a revised patch for you to try. This is meant to apply on top
of 3.15.
As it turns out, when I tested this, it revealed a bug in the USB audio
driver. So I'm including a work-around for that bug at the start of
the patch.
Alan Stern
Index: 3.15/sound/usb/pcm.c
===================================================================
--- 3.15.orig/sound/usb/pcm.c
+++ 3.15/sound/usb/pcm.c
@@ -1211,7 +1211,8 @@ static int snd_usb_pcm_close(struct snd_
struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
struct snd_usb_substream *subs = &as->substream[direction];
- stop_endpoints(subs, true);
+ if (!as->chip->shutdown)
+ stop_endpoints(subs, true);
if (!as->chip->shutdown && subs->interface >= 0) {
usb_set_interface(subs->dev, subs->interface, 0);
Index: 3.15/drivers/usb/host/ohci.h
===================================================================
--- 3.15.orig/drivers/usb/host/ohci.h
+++ 3.15/drivers/usb/host/ohci.h
@@ -47,6 +47,7 @@ struct ed {
struct ed *ed_next; /* on schedule or rm_list */
struct ed *ed_prev; /* for non-interrupt EDs */
struct list_head td_list; /* "shadow list" of our TDs */
+ struct list_head in_use_list;
/* create --> IDLE --> OPER --> ... --> IDLE --> destroy
* usually: OPER --> UNLINK --> (IDLE | OPER) --> ...
@@ -66,6 +67,13 @@ struct ed {
/* HC may see EDs on rm_list until next frame (frame_no == tick) */
u16 tick;
+
+ /* Detect TDs not added to the done list */
+ unsigned takeback_wdh_cnt;
+ struct td *pending_td;
+#define OKAY_TO_TAKEBACK(ohci, ed) \
+ ((int) (ohci->wdh_cnt - ed->takeback_wdh_cnt) >= 0)
+
} __attribute__ ((aligned(16)));
#define ED_MASK ((u32)~0x0f) /* strip hw status in low addr
bits */
@@ -380,7 +388,9 @@ struct ohci_hcd {
struct dma_pool *td_cache;
struct dma_pool *ed_cache;
struct td *td_hash [TD_HASH_SIZE];
+ struct td *dl_start, *dl_end; /* the done list */
struct list_head pending;
+ struct list_head eds_in_use; /* all EDs with at least 1 TD */
/*
* driver state
@@ -392,6 +402,8 @@ struct ohci_hcd {
unsigned long next_statechange; /* suspend/resume */
u32 fminterval; /* saved register */
unsigned autostop:1; /* rh auto stopping/stopped */
+ unsigned working:1;
+ unsigned restart_work:1;
unsigned long flags; /* for HC bugs */
#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */
@@ -409,13 +421,12 @@ struct ohci_hcd {
// there are also chip quirks/bugs in init logic
- struct work_struct nec_work; /* Worker for NEC quirk */
+ unsigned prev_frame_no;
+ unsigned wdh_cnt, prev_wdh_cnt;
+ u32 prev_donehead;
+ struct timer_list io_watchdog;
- /* Needed for ZF Micro quirk */
- struct timer_list unlink_watchdog;
- unsigned eds_scheduled;
- struct ed *ed_to_check;
- unsigned zf_delay;
+ struct work_struct nec_work; /* Worker for NEC quirk */
struct dentry *debug_dir;
struct dentry *debug_async;
Index: 3.15/drivers/usb/host/ohci-hcd.c
===================================================================
--- 3.15.orig/drivers/usb/host/ohci-hcd.c
+++ 3.15/drivers/usb/host/ohci-hcd.c
@@ -72,12 +72,14 @@
static const char hcd_name [] = "ohci_hcd";
#define STATECHANGE_DELAY msecs_to_jiffies(300)
+#define IO_WATCHDOG_DELAY msecs_to_jiffies(250)
#include "ohci.h"
#include "pci-quirks.h"
-static void ohci_dump (struct ohci_hcd *ohci, int verbose);
-static void ohci_stop (struct usb_hcd *hcd);
+static void ohci_dump(struct ohci_hcd *ohci);
+static void ohci_stop(struct usb_hcd *hcd);
+static void io_watchdog_func(unsigned long _ohci);
#include "ohci-hub.c"
#include "ohci-dbg.c"
@@ -202,6 +204,16 @@ static int ohci_urb_enqueue (
usb_hcd_unlink_urb_from_ep(hcd, urb);
goto fail;
}
+
+ /* Start up the IO watchdog timer, if it's not running */
+ if (!timer_pending(&ohci->io_watchdog) &&
+ list_empty(&ohci->eds_in_use)) {
+ ohci->prev_frame_no = ohci_frame_no(ohci);
+ mod_timer(&ohci->io_watchdog,
+ jiffies + IO_WATCHDOG_DELAY);
+ }
+ list_add(&ed->in_use_list, &ohci->eds_in_use);
+
if (ed->type == PIPE_ISOCHRONOUS) {
u16 frame = ohci_frame_no(ohci);
@@ -277,30 +289,24 @@ static int ohci_urb_dequeue(struct usb_h
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
unsigned long flags;
int rc;
+ urb_priv_t *urb_priv;
spin_lock_irqsave (&ohci->lock, flags);
rc = usb_hcd_check_unlink_urb(hcd, urb, status);
- if (rc) {
- ; /* Do nothing */
- } else if (ohci->rh_state == OHCI_RH_RUNNING) {
- urb_priv_t *urb_priv;
+ if (rc == 0) {
/* Unless an IRQ completed the unlink while it was being
* handed to us, flag it for unlink and giveback, and force
* some upcoming INTR_SF to call finish_unlinks()
*/
urb_priv = urb->hcpriv;
- if (urb_priv) {
- if (urb_priv->ed->state == ED_OPER)
- start_ed_unlink (ohci, urb_priv->ed);
+ if (urb_priv->ed->state == ED_OPER)
+ start_ed_unlink(ohci, urb_priv->ed);
+
+ if (ohci->rh_state != OHCI_RH_RUNNING) {
+ /* With HC dead, we can clean up right away */
+ ohci_work(ohci);
}
- } else {
- /*
- * with HC dead, we won't respect hc queue pointers
- * any more ... just clean up every urb's memory.
- */
- if (urb->hcpriv)
- finish_urb(ohci, urb, status);
}
spin_unlock_irqrestore (&ohci->lock, flags);
return rc;
@@ -332,9 +338,7 @@ rescan:
if (ohci->rh_state != OHCI_RH_RUNNING) {
sanitize:
ed->state = ED_IDLE;
- if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT)
- ohci->eds_scheduled--;
- finish_unlinks (ohci, 0);
+ ohci_work(ohci);
}
switch (ed->state) {
@@ -342,11 +346,6 @@ sanitize:
/* major IRQ delivery trouble loses INTR_SF too... */
if (limit-- == 0) {
ohci_warn(ohci, "ED unlink timeout\n");
- if (quirk_zfmicro(ohci)) {
- ohci_warn(ohci, "Attempting ZF TD recovery\n");
- ohci->ed_to_check = ed;
- ohci->zf_delay = 2;
- }
goto sanitize;
}
spin_unlock_irqrestore (&ohci->lock, flags);
@@ -406,93 +405,7 @@ ohci_shutdown (struct usb_hcd *hcd)
udelay(10);
ohci_writel(ohci, ohci->fminterval, &ohci->regs->fminterval);
-}
-
-static int check_ed(struct ohci_hcd *ohci, struct ed *ed)
-{
- return (hc32_to_cpu(ohci, ed->hwINFO) & ED_IN) != 0
- && (hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK)
- == (hc32_to_cpu(ohci, ed->hwTailP) & TD_MASK)
- && !list_empty(&ed->td_list);
-}
-
-/* ZF Micro watchdog timer callback. The ZF Micro chipset sometimes completes
- * an interrupt TD but neglects to add it to the donelist. On systems with
- * this chipset, we need to periodically check the state of the queues to look
- * for such "lost" TDs.
- */
-static void unlink_watchdog_func(unsigned long _ohci)
-{
- unsigned long flags;
- unsigned max;
- unsigned seen_count = 0;
- unsigned i;
- struct ed **seen = NULL;
- struct ohci_hcd *ohci = (struct ohci_hcd *) _ohci;
-
- spin_lock_irqsave(&ohci->lock, flags);
- max = ohci->eds_scheduled;
- if (!max)
- goto done;
-
- if (ohci->ed_to_check)
- goto out;
-
- seen = kcalloc(max, sizeof *seen, GFP_ATOMIC);
- if (!seen)
- goto out;
-
- for (i = 0; i < NUM_INTS; i++) {
- struct ed *ed = ohci->periodic[i];
-
- while (ed) {
- unsigned temp;
-
- /* scan this branch of the periodic schedule tree */
- for (temp = 0; temp < seen_count; temp++) {
- if (seen[temp] == ed) {
- /* we've checked it and what's after */
- ed = NULL;
- break;
- }
- }
- if (!ed)
- break;
- seen[seen_count++] = ed;
- if (!check_ed(ohci, ed)) {
- ed = ed->ed_next;
- continue;
- }
-
- /* HC's TD list is empty, but HCD sees at least one
- * TD that's not been sent through the donelist.
- */
- ohci->ed_to_check = ed;
- ohci->zf_delay = 2;
-
- /* The HC may wait until the next frame to report the
- * TD as done through the donelist and INTR_WDH. (We
- * just *assume* it's not a multi-TD interrupt URB;
- * those could defer the IRQ more than one frame, using
- * DI...) Check again after the next INTR_SF.
- */
- ohci_writel(ohci, OHCI_INTR_SF,
- &ohci->regs->intrstatus);
- ohci_writel(ohci, OHCI_INTR_SF,
- &ohci->regs->intrenable);
-
- /* flush those writes */
- (void) ohci_readl(ohci, &ohci->regs->control);
-
- goto out;
- }
- }
-out:
- kfree(seen);
- if (ohci->eds_scheduled)
- mod_timer(&ohci->unlink_watchdog, round_jiffies(jiffies + HZ));
-done:
- spin_unlock_irqrestore(&ohci->lock, flags);
+ ohci->rh_state = OHCI_RH_HALTED;
}
/*-------------------------------------------------------------------------*
@@ -558,6 +471,10 @@ static int ohci_init (struct ohci_hcd *o
if (ohci->hcca)
return 0;
+ setup_timer(&ohci->io_watchdog, io_watchdog_func,
+ (unsigned long) ohci);
+ set_timer_slack(&ohci->io_watchdog, msecs_to_jiffies(20));
+
ohci->hcca = dma_alloc_coherent (hcd->self.controller,
sizeof *ohci->hcca, &ohci->hcca_dma, 0);
if (!ohci->hcca)
@@ -735,16 +652,7 @@ retry:
// POTPGT delay is bits 24-31, in 2 ms units.
mdelay ((val >> 23) & 0x1fe);
- if (quirk_zfmicro(ohci)) {
- /* Create timer to watch for bad queue state on ZF Micro */
- setup_timer(&ohci->unlink_watchdog, unlink_watchdog_func,
- (unsigned long) ohci);
-
- ohci->eds_scheduled = 0;
- ohci->ed_to_check = NULL;
- }
-
- ohci_dump (ohci, 1);
+ ohci_dump(ohci);
return 0;
}
@@ -777,6 +685,142 @@ static int ohci_start(struct usb_hcd *hc
/*-------------------------------------------------------------------------*/
+/*
+ * Some OHCI controllers are known to lose track of completed TDs. They
+ * don't add the TDs to the hardware done list, which means we never see
+ * them as being completed.
+ *
+ * This watchdog routine checks for such problems. Without some way to
+ * tell when those TDs have completed, we would never take their EDs off
+ * the unlink list. As a result, URBs could never be dequeued and
+ * endpoints could never be released.
+ */
+static void io_watchdog_func(unsigned long _ohci)
+{
+ struct ohci_hcd *ohci = (struct ohci_hcd *) _ohci;
+ bool takeback_all_pending = false;
+ u32 status;
+ u32 head;
+ struct ed *ed;
+ struct td *td, *td_start, *td_next;
+ unsigned frame_no;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ohci->lock, flags);
+
+ /*
+ * One way to lose track of completed TDs is if the controller
+ * never writes back the done list head. If it hasn't been
+ * written back since the last time this function ran and if it
+ * was non-empty at that time, something is badly wrong with the
+ * hardware.
+ */
+ status = ohci_readl(ohci, &ohci->regs->intrstatus);
+ if (!(status & OHCI_INTR_WDH) && ohci->wdh_cnt == ohci->prev_wdh_cnt) {
+ if (ohci->prev_donehead) {
+ ohci_err(ohci, "HcDoneHead not written back;
disabled\n");
+ died:
+ usb_hc_died(ohci_to_hcd(ohci));
+ ohci_dump(ohci);
+ ohci_shutdown(ohci_to_hcd(ohci));
+ goto done;
+ } else {
+ /* No write back because the done list was empty */
+ takeback_all_pending = true;
+ }
+ }
+
+ /* Check every ED which might have pending TDs */
+ list_for_each_entry(ed, &ohci->eds_in_use, in_use_list) {
+ if (ed->pending_td) {
+ if (takeback_all_pending ||
+ OKAY_TO_TAKEBACK(ohci, ed)) {
+ unsigned tmp = hc32_to_cpu(ohci, ed->hwINFO);
+
+ ohci_dbg(ohci, "takeback pending TD for dev %d
ep 0x%x\n",
+ 0x007f & tmp,
+ (0x000f & (tmp >> 7)) +
+ ((tmp & ED_IN) >> 5));
+ add_to_done_list(ohci, ed->pending_td);
+ }
+ }
+
+ /* Starting from the latest pending TD, */
+ td = ed->pending_td;
+
+ /* or the last TD on the done list, */
+ if (!td) {
+ list_for_each_entry(td_next, &ed->td_list, td_list) {
+ if (!td_next->next_dl_td)
+ break;
+ td = td_next;
+ }
+ }
+
+ /* find the last TD processed by the controller. */
+ head = hc32_to_cpu(ohci, ACCESS_ONCE(ed->hwHeadP)) & TD_MASK;
+ td_start = td;
+ td_next = list_prepare_entry(td, &ed->td_list, td_list);
+ list_for_each_entry_continue(td_next, &ed->td_list, td_list) {
+ if (head == (u32) td_next->td_dma)
+ break;
+ td = td_next; /* head pointer has passed this TD */
+ }
+ if (td != td_start) {
+ /*
+ * In case a WDH cycle is in progress, we will wait
+ * for the next two cycles to complete before assuming
+ * this TD will never get on the done list.
+ */
+ ed->takeback_wdh_cnt = ohci->wdh_cnt + 2;
+ ed->pending_td = td;
+ }
+ }
+
+ ohci_work(ohci);
+
+ if (ohci->rh_state == OHCI_RH_RUNNING) {
+
+ /*
+ * Sometimes a controller just stops working. We can tell
+ * by checking that the frame counter has advanced since
+ * the last time we ran.
+ *
+ * But be careful: Some controllers violate the spec by
+ * stopping their frame counter when no ports are active.
+ */
+ frame_no = ohci_frame_no(ohci);
+ if (frame_no == ohci->prev_frame_no) {
+ int active_cnt = 0;
+ int i;
+ unsigned tmp;
+
+ for (i = 0; i < ohci->num_ports; ++i) {
+ tmp = roothub_portstatus(ohci, i);
+ /* Enabled and not suspended? */
+ if ((tmp & RH_PS_PES) && !(tmp & RH_PS_PSS))
+ ++active_cnt;
+ }
+
+ if (active_cnt > 0) {
+ ohci_err(ohci, "frame counter not updating;
disabled\n");
+ goto died;
+ }
+ }
+ if (!list_empty(&ohci->eds_in_use)) {
+ ohci->prev_frame_no = frame_no;
+ ohci->prev_wdh_cnt = ohci->wdh_cnt;
+ ohci->prev_donehead = ohci_readl(ohci,
+ &ohci->regs->donehead);
+ mod_timer(&ohci->io_watchdog,
+ jiffies + IO_WATCHDOG_DELAY);
+ }
+ }
+
+ done:
+ spin_unlock_irqrestore(&ohci->lock, flags);
+}
+
/* an interrupt happens */
static irqreturn_t ohci_irq (struct usb_hcd *hcd)
@@ -825,7 +869,7 @@ static irqreturn_t ohci_irq (struct usb_
usb_hc_died(hcd);
}
- ohci_dump (ohci, 1);
+ ohci_dump(ohci);
ohci_usb_reset (ohci);
}
@@ -863,58 +907,30 @@ static irqreturn_t ohci_irq (struct usb_
usb_hcd_resume_root_hub(hcd);
}
- if (ints & OHCI_INTR_WDH) {
- spin_lock (&ohci->lock);
- dl_done_list (ohci);
- spin_unlock (&ohci->lock);
- }
-
- if (quirk_zfmicro(ohci) && (ints & OHCI_INTR_SF)) {
- spin_lock(&ohci->lock);
- if (ohci->ed_to_check) {
- struct ed *ed = ohci->ed_to_check;
-
- if (check_ed(ohci, ed)) {
- /* HC thinks the TD list is empty; HCD knows
- * at least one TD is outstanding
- */
- if (--ohci->zf_delay == 0) {
- struct td *td = list_entry(
- ed->td_list.next,
- struct td, td_list);
- ohci_warn(ohci,
- "Reclaiming orphan TD %p\n",
- td);
- takeback_td(ohci, td);
- ohci->ed_to_check = NULL;
- }
- } else
- ohci->ed_to_check = NULL;
- }
- spin_unlock(&ohci->lock);
- }
+ spin_lock(&ohci->lock);
+ if (ints & OHCI_INTR_WDH)
+ update_done_list(ohci);
/* could track INTR_SO to reduce available PCI/... bandwidth */
/* handle any pending URB/ED unlinks, leaving INTR_SF enabled
* when there's still unlinking to be done (next frame).
*/
- spin_lock (&ohci->lock);
- if (ohci->ed_rm_list)
- finish_unlinks (ohci, ohci_frame_no(ohci));
- if ((ints & OHCI_INTR_SF) != 0
- && !ohci->ed_rm_list
- && !ohci->ed_to_check
+ ohci_work(ohci);
+ if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list
&& ohci->rh_state == OHCI_RH_RUNNING)
ohci_writel (ohci, OHCI_INTR_SF, ®s->intrdisable);
- spin_unlock (&ohci->lock);
if (ohci->rh_state == OHCI_RH_RUNNING) {
ohci_writel (ohci, ints, ®s->intrstatus);
+ if (ints & OHCI_INTR_WDH)
+ ++ohci->wdh_cnt;
+
ohci_writel (ohci, OHCI_INTR_MIE, ®s->intrenable);
// flush those writes
(void) ohci_readl (ohci, &ohci->regs->control);
}
+ spin_unlock(&ohci->lock);
return IRQ_HANDLED;
}
@@ -925,18 +941,17 @@ static void ohci_stop (struct usb_hcd *h
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
- ohci_dump (ohci, 1);
+ ohci_dump(ohci);
if (quirk_nec(ohci))
flush_work(&ohci->nec_work);
+ del_timer_sync(&ohci->io_watchdog);
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
ohci_usb_reset(ohci);
free_irq(hcd->irq, hcd);
hcd->irq = 0;
- if (quirk_zfmicro(ohci))
- del_timer(&ohci->unlink_watchdog);
if (quirk_amdiso(ohci))
usb_amd_dev_put();
@@ -993,7 +1008,7 @@ int ohci_restart(struct ohci_hcd *ohci)
if (!urb->unlinked)
urb->unlinked = -ESHUTDOWN;
}
- finish_unlinks (ohci, 0);
+ ohci_work(ohci);
spin_unlock_irq(&ohci->lock);
/* paranoia, in case that didn't work: */
Index: 3.15/drivers/usb/host/ohci-q.c
===================================================================
--- 3.15.orig/drivers/usb/host/ohci-q.c
+++ 3.15/drivers/usb/host/ohci-q.c
@@ -187,10 +187,6 @@ static int ed_schedule (struct ohci_hcd
ed->ed_prev = NULL;
ed->ed_next = NULL;
ed->hwNextED = 0;
- if (quirk_zfmicro(ohci)
- && (ed->type == PIPE_INTERRUPT)
- && !(ohci->eds_scheduled++))
- mod_timer(&ohci->unlink_watchdog, round_jiffies(jiffies + HZ));
wmb ();
/* we care about rm_list when setting CLE/BLE in case the HC was at
@@ -311,8 +307,7 @@ static void periodic_unlink (struct ohci
* - ED_OPER: when there's any request queued, the ED gets rescheduled
* immediately. HC should be working on them.
*
- * - ED_IDLE: when there's no TD queue. there's no reason for the HC
- * to care about this ED; safe to disable the endpoint.
+ * - ED_IDLE: when there's no TD queue or the HC isn't running.
*
* When finish_unlinks() runs later, after SOF interrupt, it will often
* complete one or more URB unlinks before making that state change.
@@ -869,13 +864,46 @@ static void ed_halted(struct ohci_hcd *o
}
}
-/* 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)
+/* Add a TD to the done list */
+static void add_to_done_list(struct ohci_hcd *ohci, struct td *td)
+{
+ struct td *td2, *td_prev;
+ struct ed *ed;
+
+ if (td->next_dl_td)
+ return; /* Already on the list */
+
+ /* Add all the TDs going back until we reach one that's on the list */
+ ed = td->ed;
+ td2 = td_prev = td;
+ list_for_each_entry_continue_reverse(td2, &ed->td_list, td_list) {
+ if (td2->next_dl_td)
+ break;
+ td2->next_dl_td = td_prev;
+ td_prev = td2;
+ }
+
+ if (ohci->dl_end)
+ ohci->dl_end->next_dl_td = td_prev;
+ else
+ ohci->dl_start = td_prev;
+
+ /*
+ * Make td->next_dl_td point to td itself, to mark the fact
+ * that td is on the done list.
+ */
+ ohci->dl_end = td->next_dl_td = td;
+
+ /* Did we just add the latest pending TD? */
+ td2 = ed->pending_td;
+ if (td2 && td2->next_dl_td)
+ ed->pending_td = NULL;
+}
+
+/* Get the entries on the hardware done list and put them on our list */
+static void update_done_list(struct ohci_hcd *ohci)
{
u32 td_dma;
- struct td *td_rev = NULL;
struct td *td = NULL;
td_dma = hc32_to_cpup (ohci, &ohci->hcca->done_head);
@@ -883,7 +911,7 @@ static struct td *dl_reverse_done_list (
wmb();
/* get TD from hc's singly linked list, and
- * prepend to ours. ed->td_list changes later.
+ * add to ours. ed->td_list changes later.
*/
while (td_dma) {
int cc;
@@ -905,19 +933,17 @@ static struct td *dl_reverse_done_list (
&& (td->ed->hwHeadP & cpu_to_hc32 (ohci, ED_H)))
ed_halted(ohci, td, cc);
- td->next_dl_td = td_rev;
- td_rev = td;
td_dma = hc32_to_cpup (ohci, &td->hwNextTD);
+ add_to_done_list(ohci, td);
}
- return td_rev;
}
/*-------------------------------------------------------------------------*/
/* there are some urbs/eds to unlink; called in_irq(), with HCD locked */
-static void
-finish_unlinks (struct ohci_hcd *ohci, u16 tick)
+static void finish_unlinks(struct ohci_hcd *ohci)
{
+ unsigned tick = ohci_frame_no(ohci);
struct ed *ed, **last;
rescan_all:
@@ -926,41 +952,48 @@ rescan_all:
int completed, modified;
__hc32 *prev;
+ /* Is this ed already invisible to the hardware? */
+ if (ed->state == ED_IDLE)
+ goto ed_idle;
+
/* only take off EDs that the HC isn't using, accounting for
* frame counter wraps and EDs with partially retired TDs
*/
- if (likely(ohci->rh_state == OHCI_RH_RUNNING)) {
- if (tick_before (tick, ed->tick)) {
+ if (likely(ohci->rh_state == OHCI_RH_RUNNING) &&
+ tick_before(tick, ed->tick)) {
skip_ed:
- last = &ed->ed_next;
- continue;
- }
+ last = &ed->ed_next;
+ continue;
+ }
+ if (!list_empty(&ed->td_list)) {
+ struct td *td;
+ u32 head;
- if (!list_empty (&ed->td_list)) {
- struct td *td;
- u32 head;
-
- td = list_entry (ed->td_list.next, struct td,
- td_list);
- head = hc32_to_cpu (ohci, ed->hwHeadP) &
- TD_MASK;
-
- /* INTR_WDH may need to clean up first */
- if (td->td_dma != head) {
- if (ed == ohci->ed_to_check)
- ohci->ed_to_check = NULL;
- else
- goto skip_ed;
- }
- }
+ td = list_first_entry(&ed->td_list, struct td, td_list);
+
+ /* INTR_WDH may need to clean up first */
+ head = hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK;
+ if (td->td_dma != head &&
+ ohci->rh_state == OHCI_RH_RUNNING)
+ goto skip_ed;
+
+ /* Don't mess up anything already on the done list */
+ if (td->next_dl_td)
+ goto skip_ed;
}
+ /* ED's now officially unlinked, hc doesn't see */
+ ed->state = ED_IDLE;
+ ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H);
+ ed->hwNextED = 0;
+ wmb();
+ ed->hwINFO &= ~cpu_to_hc32(ohci, ED_SKIP | ED_DEQUEUE);
+ed_idle:
+
/* reentrancy: if we drop the schedule lock, someone might
* have modified this list. normally it's just prepending
* entries (which we'd ignore), but paranoia won't hurt.
*/
- *last = ed->ed_next;
- ed->ed_next = NULL;
modified = 0;
/* unlink urbs as requested, but rescan the list after
@@ -1018,19 +1051,21 @@ rescan_this:
if (completed && !list_empty (&ed->td_list))
goto rescan_this;
- /* ED's now officially unlinked, hc doesn't see */
- ed->state = ED_IDLE;
- if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT)
- ohci->eds_scheduled--;
- ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H);
- ed->hwNextED = 0;
- wmb ();
- ed->hwINFO &= ~cpu_to_hc32 (ohci, ED_SKIP | ED_DEQUEUE);
-
- /* but if there's work queued, reschedule */
- if (!list_empty (&ed->td_list)) {
- if (ohci->rh_state == OHCI_RH_RUNNING)
- ed_schedule (ohci, ed);
+ /*
+ * If no TD's are queued, take it off the ed_rm_list.
+ * Otherwise, if the HC is running, reschedule.
+ * If not, leave it on the list for further dequeues.
+ */
+ if (list_empty(&ed->td_list)) {
+ *last = ed->ed_next;
+ ed->ed_next = NULL;
+ list_del(&ed->in_use_list);
+ } else if (ohci->rh_state == OHCI_RH_RUNNING) {
+ *last = ed->ed_next;
+ ed->ed_next = NULL;
+ ed_schedule(ohci, ed);
+ } else {
+ last = &ed->ed_next;
}
if (modified)
@@ -1082,12 +1117,7 @@ rescan_this:
/*-------------------------------------------------------------------------*/
-/*
- * Used to take back a TD from the host controller. This would normally be
- * called from within dl_done_list, however it may be called directly if the
- * HC no longer sees the TD and it has not appeared on the donelist (after
- * two frames). This bug has been observed on ZF Micro systems.
- */
+/* Take back a TD from the host controller */
static void takeback_td(struct ohci_hcd *ohci, struct td *td)
{
struct urb *urb = td->urb;
@@ -1134,37 +1164,43 @@ static void takeback_td(struct ohci_hcd
*
* This is the main path for handing urbs back to drivers. The only other
* normal path is finish_unlinks(), which unlinks URBs using ed_rm_list,
- * instead of scanning the (re-reversed) donelist as this does. There's
- * an abnormal path too, handling a quirk in some Compaq silicon: URBs
- * with TDs that appear to be orphaned are directly reclaimed.
+ * instead of scanning the (re-reversed) donelist as this does.
*/
-static void
-dl_done_list (struct ohci_hcd *ohci)
+static void process_done_list(struct ohci_hcd *ohci)
{
- struct td *td = dl_reverse_done_list (ohci);
+ struct td *td;
- while (td) {
- struct td *td_next = td->next_dl_td;
- struct ed *ed = td->ed;
-
- /*
- * Some OHCI controllers (NVIDIA for sure, maybe others)
- * occasionally forget to add TDs to the done queue. Since
- * TDs for a given endpoint are always processed in order,
- * if we find a TD on the donelist then all of its
- * predecessors must be finished as well.
- */
- for (;;) {
- struct td *td2;
-
- td2 = list_first_entry(&ed->td_list, struct td,
- td_list);
- if (td2 == td)
- break;
- takeback_td(ohci, td2);
- }
+ while (ohci->dl_start) {
+ td = ohci->dl_start;
+ if (td == ohci->dl_end)
+ ohci->dl_start = ohci->dl_end = NULL;
+ else
+ ohci->dl_start = td->next_dl_td;
takeback_td(ohci, td);
- td = td_next;
}
}
+
+/*
+ * TD takeback and URB giveback must be single-threaded.
+ * This routine takes care of it all.
+ */
+static void ohci_work(struct ohci_hcd *ohci)
+{
+ if (ohci->working) {
+ ohci->restart_work = 1;
+ return;
+ }
+ ohci->working = 1;
+
+ restart:
+ process_done_list(ohci);
+ if (ohci->ed_rm_list)
+ finish_unlinks(ohci);
+
+ if (ohci->restart_work) {
+ ohci->restart_work = 0;
+ goto restart;
+ }
+ ohci->working = 0;
+}
Index: 3.15/drivers/usb/host/ohci-hub.c
===================================================================
--- 3.15.orig/drivers/usb/host/ohci-hub.c
+++ 3.15/drivers/usb/host/ohci-hub.c
@@ -39,8 +39,8 @@
#define OHCI_SCHED_ENABLES \
(OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
-static void dl_done_list (struct ohci_hcd *);
-static void finish_unlinks (struct ohci_hcd *, u16);
+static void update_done_list(struct ohci_hcd *);
+static void ohci_work(struct ohci_hcd *);
#ifdef CONFIG_PM
static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop)
@@ -87,8 +87,8 @@ __acquires(ohci->lock)
msleep (8);
spin_lock_irq (&ohci->lock);
}
- dl_done_list (ohci);
- finish_unlinks (ohci, ohci_frame_no(ohci));
+ update_done_list(ohci);
+ ohci_work(ohci);
/*
* Some controllers don't handle "global" suspend properly if
@@ -309,6 +309,9 @@ static int ohci_bus_suspend (struct usb_
else
rc = ohci_rh_suspend (ohci, 0);
spin_unlock_irq (&ohci->lock);
+
+ if (rc == 0)
+ del_timer_sync(&ohci->io_watchdog);
return rc;
}
Index: 3.15/drivers/usb/host/ohci-dbg.c
===================================================================
--- 3.15.orig/drivers/usb/host/ohci-dbg.c
+++ 3.15/drivers/usb/host/ohci-dbg.c
@@ -236,7 +236,7 @@ ohci_dump_roothub (
}
}
-static void ohci_dump (struct ohci_hcd *controller, int verbose)
+static void ohci_dump(struct ohci_hcd *controller)
{
ohci_dbg (controller, "OHCI controller state\n");
@@ -468,6 +468,7 @@ static ssize_t fill_async_buffer(struct
unsigned long flags;
ohci = buf->ohci;
+ buf->count = PAGE_SIZE;
/* display control and bulk lists together, for simplicity */
spin_lock_irqsave (&ohci->lock, flags);
Index: 3.15/drivers/usb/host/ohci-mem.c
===================================================================
--- 3.15.orig/drivers/usb/host/ohci-mem.c
+++ 3.15/drivers/usb/host/ohci-mem.c
@@ -28,6 +28,7 @@ static void ohci_hcd_init (struct ohci_h
ohci->next_statechange = jiffies;
spin_lock_init (&ohci->lock);
INIT_LIST_HEAD (&ohci->pending);
+ INIT_LIST_HEAD(&ohci->eds_in_use);
}
/*-------------------------------------------------------------------------*/
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html