From ca337db6f92a32347172a20b96f6f51b7bb63b7c Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sat, 21 Jul 2007 17:02:59 +0200 Subject: USB: Clean up duplicate includes in drivers/usb/ This patch cleans up duplicate includes in drivers/usb/ Signed-off-by: Jesper Juhl Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/u132-hcd.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index b88eb3c62c0..598ad098aee 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -51,7 +51,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 89a0fd18a96eb1f8732714b575073f8a8d69c009 Mon Sep 17 00:00:00 2001 From: Mike Nuss Date: Wed, 1 Aug 2007 13:24:30 -0700 Subject: USB: OHCI handles more ZFMicro quirks The ZF Micro OHCI controller exhibits unexpected behavior that seems to be related to high load. Under certain conditions, the controller will complete a TD, remove it from the endpoint's queue, and fail to add it to the donelist. This causes the endpoint to appear to stop responding. Worse, if the device is removed while in that state, OHCI will hang while waiting for the orphaned TD to complete. The situation is not recoverable without rebooting. This fix enhances the scope of the existing OHCI_QUIRK_ZFMICRO flag: 1. A watchdog routine periodically scans the OHCI structures to check for orphaned TDs. In these cases the TD is taken back from the controller and completed normally. 2. If a device is removed while the endpoint is hung but before the watchdog catches the situation, any outstanding TDs are taken back from the controller in the 'sanitize' phase. The ohci-hcd driver used to print "INTR_SF lossage" in this situation; this changes it to the universally accurate "ED unlink timeout". Other instances of this message presumably have different root causes. Both this Compaq quirk and a NEC quirk are now properly compiled out for non-PCI builds of this driver. Signed-off-by: Mike Nuss Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-hcd.c | 166 +++++++++++++++++++++++++++++++++++++------- drivers/usb/host/ohci-mem.c | 1 - drivers/usb/host/ohci-pci.c | 22 +++++- drivers/usb/host/ohci-q.c | 113 ++++++++++++++++++------------ drivers/usb/host/ohci.h | 26 +++++++ 5 files changed, 253 insertions(+), 75 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 6edf4097d2d..d673cb9c36b 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -81,7 +81,6 @@ static void ohci_dump (struct ohci_hcd *ohci, int verbose); static int ohci_init (struct ohci_hcd *ohci); static void ohci_stop (struct usb_hcd *hcd); static int ohci_restart (struct ohci_hcd *ohci); -static void ohci_quirk_nec_worker (struct work_struct *work); #include "ohci-hub.c" #include "ohci-dbg.c" @@ -314,6 +313,8 @@ rescan: if (!HC_IS_RUNNING (hcd->state)) { sanitize: ed->state = ED_IDLE; + if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT) + ohci->eds_scheduled--; finish_unlinks (ohci, 0); } @@ -321,7 +322,12 @@ sanitize: case ED_UNLINK: /* wait for hw to finish? */ /* major IRQ delivery trouble loses INTR_SF too... */ if (limit-- == 0) { - ohci_warn (ohci, "IRQ INTR_SF lossage\n"); + 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); @@ -379,6 +385,93 @@ ohci_shutdown (struct usb_hcd *hcd) (void) ohci_readl (ohci, &ohci->regs->control); } +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) +{ + 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_relative(HZ)); +done: + spin_unlock_irqrestore(&ohci->lock, flags); +} + /*-------------------------------------------------------------------------* * HC functions *-------------------------------------------------------------------------*/ @@ -616,6 +709,15 @@ retry: mdelay ((temp >> 23) & 0x1fe); hcd->state = HC_STATE_RUNNING; + 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); return 0; @@ -629,10 +731,11 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); struct ohci_regs __iomem *regs = ohci->regs; - int ints; + int ints; /* we can eliminate a (slow) ohci_readl() - if _only_ WDH caused this irq */ + * if _only_ WDH caused this irq + */ if ((ohci->hcca->done_head != 0) && ! (hc32_to_cpup (ohci, &ohci->hcca->done_head) & 0x01)) { @@ -651,7 +754,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) if (ints & OHCI_INTR_UE) { // e.g. due to PCI Master/Target Abort - if (ohci->flags & OHCI_QUIRK_NEC) { + if (quirk_nec(ohci)) { /* Workaround for a silicon bug in some NEC chips used * in Apple's PowerBooks. Adapted from Darwin code. */ @@ -713,6 +816,31 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) ohci_writel (ohci, OHCI_INTR_WDH, ®s->intrenable); } + 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); + } + /* could track INTR_SO to reduce available PCI/... bandwidth */ /* handle any pending URB/ED unlinks, leaving INTR_SF enabled @@ -721,7 +849,9 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) 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 + if ((ints & OHCI_INTR_SF) != 0 + && !ohci->ed_rm_list + && !ohci->ed_to_check && HC_IS_RUNNING(hcd->state)) ohci_writel (ohci, OHCI_INTR_SF, ®s->intrdisable); spin_unlock (&ohci->lock); @@ -751,6 +881,9 @@ static void ohci_stop (struct usb_hcd *hcd) free_irq(hcd->irq, hcd); hcd->irq = -1; + if (quirk_zfmicro(ohci)) + del_timer(&ohci->unlink_watchdog); + remove_debug_files (ohci); ohci_mem_cleanup (ohci); if (ohci->hcca) { @@ -828,27 +961,6 @@ static int ohci_restart (struct ohci_hcd *ohci) /*-------------------------------------------------------------------------*/ -/* NEC workaround */ -static void ohci_quirk_nec_worker(struct work_struct *work) -{ - struct ohci_hcd *ohci = container_of(work, struct ohci_hcd, nec_work); - int status; - - status = ohci_init(ohci); - if (status != 0) { - ohci_err(ohci, "Restarting NEC controller failed " - "in ohci_init, %d\n", status); - return; - } - - status = ohci_restart(ohci); - if (status != 0) - ohci_err(ohci, "Restarting NEC controller failed " - "in ohci_restart, %d\n", status); -} - -/*-------------------------------------------------------------------------*/ - #define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC MODULE_AUTHOR (DRIVER_AUTHOR); diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c index 450c7b460c5..2f20d3dc895 100644 --- a/drivers/usb/host/ohci-mem.c +++ b/drivers/usb/host/ohci-mem.c @@ -28,7 +28,6 @@ static void ohci_hcd_init (struct ohci_hcd *ohci) ohci->next_statechange = jiffies; spin_lock_init (&ohci->lock); INIT_LIST_HEAD (&ohci->pending); - INIT_WORK (&ohci->nec_work, ohci_quirk_nec_worker); } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index a5e2eb85d07..d0360f65ebd 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -84,7 +84,7 @@ static int ohci_quirk_zfmicro(struct usb_hcd *hcd) struct ohci_hcd *ohci = hcd_to_ohci (hcd); ohci->flags |= OHCI_QUIRK_ZFMICRO; - ohci_dbg (ohci, "enabled Compaq ZFMicro chipset quirk\n"); + ohci_dbg(ohci, "enabled Compaq ZFMicro chipset quirks\n"); return 0; } @@ -113,11 +113,31 @@ static int ohci_quirk_toshiba_scc(struct usb_hcd *hcd) /* Check for NEC chip and apply quirk for allegedly lost interrupts. */ + +static void ohci_quirk_nec_worker(struct work_struct *work) +{ + struct ohci_hcd *ohci = container_of(work, struct ohci_hcd, nec_work); + int status; + + status = ohci_init(ohci); + if (status != 0) { + ohci_err(ohci, "Restarting NEC controller failed in %s, %d\n", + "ohci_init", status); + return; + } + + status = ohci_restart(ohci); + if (status != 0) + ohci_err(ohci, "Restarting NEC controller failed in %s, %d\n", + "ohci_restart", status); +} + static int ohci_quirk_nec(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); ohci->flags |= OHCI_QUIRK_NEC; + INIT_WORK(&ohci->nec_work, ohci_quirk_nec_worker); ohci_dbg (ohci, "enabled NEC chipset lost interrupt quirk\n"); return 0; diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 830a3fe8615..547d39be3eb 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -179,6 +179,10 @@ static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed) 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_relative(HZ)); wmb (); /* we care about rm_list when setting CLE/BLE in case the HC was at @@ -940,8 +944,12 @@ skip_ed: TD_MASK; /* INTR_WDH may need to clean up first */ - if (td->td_dma != head) - goto skip_ed; + if (td->td_dma != head) { + if (ed == ohci->ed_to_check) + ohci->ed_to_check = NULL; + else + goto skip_ed; + } } } @@ -998,6 +1006,8 @@ 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 (); @@ -1021,7 +1031,7 @@ rescan_this: if (ohci->ed_controltail) { command |= OHCI_CLF; - if (ohci->flags & OHCI_QUIRK_ZFMICRO) + if (quirk_zfmicro(ohci)) mdelay(1); if (!(ohci->hc_control & OHCI_CTRL_CLE)) { control |= OHCI_CTRL_CLE; @@ -1031,7 +1041,7 @@ rescan_this: } if (ohci->ed_bulktail) { command |= OHCI_BLF; - if (ohci->flags & OHCI_QUIRK_ZFMICRO) + if (quirk_zfmicro(ohci)) mdelay(1); if (!(ohci->hc_control & OHCI_CTRL_BLE)) { control |= OHCI_CTRL_BLE; @@ -1043,13 +1053,13 @@ rescan_this: /* CLE/BLE to enable, CLF/BLF to (maybe) kickstart */ if (control) { ohci->hc_control |= control; - if (ohci->flags & OHCI_QUIRK_ZFMICRO) + if (quirk_zfmicro(ohci)) mdelay(1); ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); } if (command) { - if (ohci->flags & OHCI_QUIRK_ZFMICRO) + if (quirk_zfmicro(ohci)) mdelay(1); ohci_writel (ohci, command, &ohci->regs->cmdstatus); } @@ -1060,12 +1070,60 @@ 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. + */ +static void takeback_td(struct ohci_hcd *ohci, struct td *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(ohci, urb, td); + urb_priv->td_cnt++; + + /* If all this urb's TDs are done, call complete() */ + if (urb_priv->td_cnt == urb_priv->length) + finish_urb(ohci, urb); + + /* clean schedule: unlink EDs that are no longer busy */ + if (list_empty(&ed->td_list)) { + if (ed->state == ED_OPER) + start_ed_unlink(ohci, ed); + + /* ... reenabling halted EDs only after fault cleanup */ + } else if ((ed->hwINFO & cpu_to_hc32(ohci, ED_SKIP | ED_DEQUEUE)) + == cpu_to_hc32(ohci, ED_SKIP)) { + td = list_entry(ed->td_list.next, struct td, td_list); + if (!(td->hwINFO & cpu_to_hc32(ohci, TD_DONE))) { + ed->hwINFO &= ~cpu_to_hc32(ohci, ED_SKIP); + /* ... hc may need waking-up */ + switch (ed->type) { + case PIPE_CONTROL: + ohci_writel(ohci, OHCI_CLF, + &ohci->regs->cmdstatus); + break; + case PIPE_BULK: + ohci_writel(ohci, OHCI_BLF, + &ohci->regs->cmdstatus); + break; + } + } + } +} + /* * 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 finish_unlinks(), which unlinks URBs using ed_rm_list, instead of - * scanning the (re-reversed) donelist as this does. + * 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. */ static void dl_done_list (struct ohci_hcd *ohci) @@ -1074,44 +1132,7 @@ dl_done_list (struct ohci_hcd *ohci) 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 (ohci, urb, td); - urb_priv->td_cnt++; - - /* If all this urb's TDs are done, call complete() */ - if (urb_priv->td_cnt == urb_priv->length) - finish_urb (ohci, urb); - - /* clean schedule: unlink EDs that are no longer busy */ - if (list_empty (&ed->td_list)) { - if (ed->state == ED_OPER) - start_ed_unlink (ohci, ed); - - /* ... reenabling halted EDs only after fault cleanup */ - } else if ((ed->hwINFO & cpu_to_hc32 (ohci, - ED_SKIP | ED_DEQUEUE)) - == cpu_to_hc32 (ohci, ED_SKIP)) { - td = list_entry (ed->td_list.next, struct td, td_list); - if (!(td->hwINFO & cpu_to_hc32 (ohci, TD_DONE))) { - ed->hwINFO &= ~cpu_to_hc32 (ohci, ED_SKIP); - /* ... hc may need waking-up */ - switch (ed->type) { - case PIPE_CONTROL: - ohci_writel (ohci, OHCI_CLF, - &ohci->regs->cmdstatus); - break; - case PIPE_BULK: - ohci_writel (ohci, OHCI_BLF, - &ohci->regs->cmdstatus); - break; - } - } - } - + takeback_td(ohci, td); td = td_next; } } diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 4ada43cf138..dd4d5b4dcb6 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -401,8 +401,34 @@ struct ohci_hcd { // there are also chip quirks/bugs in init logic struct work_struct nec_work; /* Worker for NEC quirk */ + + /* Needed for ZF Micro quirk */ + struct timer_list unlink_watchdog; + unsigned eds_scheduled; + struct ed *ed_to_check; + unsigned zf_delay; }; +#ifdef CONFIG_PCI +static inline int quirk_nec(struct ohci_hcd *ohci) +{ + return ohci->flags & OHCI_QUIRK_NEC; +} +static inline int quirk_zfmicro(struct ohci_hcd *ohci) +{ + return ohci->flags & OHCI_QUIRK_ZFMICRO; +} +#else +static inline int quirk_nec(struct ohci_hcd *ohci) +{ + return 0; +} +static inline int quirk_zfmicro(struct ohci_hcd *ohci) +{ + return 0; +} +#endif + /* convert between an hcd pointer and the corresponding ohci_hcd */ static inline struct ohci_hcd *hcd_to_ohci (struct usb_hcd *hcd) { -- cgit v1.2.3 From e9df41c5c5899259541dc928872cad4d07b82076 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 8 Aug 2007 11:48:02 -0400 Subject: USB: make HCDs responsible for managing endpoint queues This patch (as954) implements a suggestion of David Brownell's. Now the host controller drivers are responsible for linking and unlinking URBs to/from their endpoint queues. This eliminates the possiblity of strange situations where usbcore thinks an URB is linked but the HCD thinks it isn't. It also means HCDs no longer have to check for URBs being dequeued before they were fully enqueued. In addition to the core changes, this requires changing every host controller driver and the root-hub URB handler. For the most part the required changes are fairly small; drivers have to call usb_hcd_link_urb_to_ep() in their urb_enqueue method, usb_hcd_check_unlink_urb() in their urb_dequeue method, and usb_hcd_unlink_urb_from_ep() before giving URBs back. A few HCDs make matters more complicated by the way they split up the flow of control. In addition some method interfaces get changed. The endpoint argument for urb_enqueue is now redundant so it is removed. The unlink status is required by usb_hcd_check_unlink_urb(), so it has been added to urb_dequeue. Signed-off-by: Alan Stern CC: David Brownell CC: Olav Kongas CC: Tony Olech CC: Yoshihiro Shimoda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 14 ++-- drivers/usb/host/ehci-q.c | 14 ++-- drivers/usb/host/ehci-sched.c | 43 +++++++--- drivers/usb/host/isp116x-hcd.c | 31 +++++--- drivers/usb/host/ohci-hcd.c | 32 ++++---- drivers/usb/host/ohci-q.c | 1 + drivers/usb/host/r8a66597-hcd.c | 37 ++++++--- drivers/usb/host/sl811-hcd.c | 26 ++++-- drivers/usb/host/u132-hcd.c | 170 ++++++++++++++++++++++++++++------------ drivers/usb/host/uhci-q.c | 32 ++++---- 10 files changed, 263 insertions(+), 137 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 35cdba10411..db00492588b 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -719,7 +719,6 @@ dead: */ static int ehci_urb_enqueue ( struct usb_hcd *hcd, - struct usb_host_endpoint *ep, struct urb *urb, gfp_t mem_flags ) { @@ -734,12 +733,12 @@ static int ehci_urb_enqueue ( default: if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) return -ENOMEM; - return submit_async (ehci, ep, urb, &qtd_list, mem_flags); + return submit_async(ehci, urb, &qtd_list, mem_flags); case PIPE_INTERRUPT: if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) return -ENOMEM; - return intr_submit (ehci, ep, urb, &qtd_list, mem_flags); + return intr_submit(ehci, urb, &qtd_list, mem_flags); case PIPE_ISOCHRONOUS: if (urb->dev->speed == USB_SPEED_HIGH) @@ -777,13 +776,18 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) * completions normally happen asynchronously */ -static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) +static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_qh *qh; unsigned long flags; + int rc; spin_lock_irqsave (&ehci->lock, flags); + rc = usb_hcd_check_unlink_urb(hcd, urb, status); + if (rc) + goto done; + switch (usb_pipetype (urb->pipe)) { // case PIPE_CONTROL: // case PIPE_BULK: @@ -838,7 +842,7 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) } done: spin_unlock_irqrestore (&ehci->lock, flags); - return 0; + return rc; } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 140bfa423e0..1da2de4d34e 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -262,6 +262,7 @@ __acquires(ehci->lock) #endif /* complete() can reenter this HCD */ + usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); spin_unlock (&ehci->lock); usb_hcd_giveback_urb (ehci_to_hcd(ehci), urb); spin_lock (&ehci->lock); @@ -913,7 +914,6 @@ static struct ehci_qh *qh_append_tds ( static int submit_async ( struct ehci_hcd *ehci, - struct usb_host_endpoint *ep, struct urb *urb, struct list_head *qtd_list, gfp_t mem_flags @@ -922,10 +922,10 @@ submit_async ( int epnum; unsigned long flags; struct ehci_qh *qh = NULL; - int rc = 0; + int rc; qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list); - epnum = ep->desc.bEndpointAddress; + epnum = urb->ep->desc.bEndpointAddress; #ifdef EHCI_URB_TRACE ehci_dbg (ehci, @@ -933,7 +933,7 @@ submit_async ( __FUNCTION__, urb->dev->devpath, urb, epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out", urb->transfer_buffer_length, - qtd, ep->hcpriv); + qtd, urb->ep->hcpriv); #endif spin_lock_irqsave (&ehci->lock, flags); @@ -942,9 +942,13 @@ submit_async ( rc = -ESHUTDOWN; goto done; } + rc = usb_hcd_link_urb_to_ep(ehci_to_hcd(ehci), urb); + if (unlikely(rc)) + goto done; - qh = qh_append_tds (ehci, urb, qtd_list, epnum, &ep->hcpriv); + qh = qh_append_tds(ehci, urb, qtd_list, epnum, &urb->ep->hcpriv); if (unlikely(qh == NULL)) { + usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); rc = -ENOMEM; goto done; } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index e682f2342ef..8b267b3fd2b 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -797,7 +797,6 @@ done: static int intr_submit ( struct ehci_hcd *ehci, - struct usb_host_endpoint *ep, struct urb *urb, struct list_head *qtd_list, gfp_t mem_flags @@ -805,23 +804,26 @@ static int intr_submit ( unsigned epnum; unsigned long flags; struct ehci_qh *qh; - int status = 0; + int status; struct list_head empty; /* get endpoint and transfer/schedule data */ - epnum = ep->desc.bEndpointAddress; + epnum = urb->ep->desc.bEndpointAddress; spin_lock_irqsave (&ehci->lock, flags); if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &ehci_to_hcd(ehci)->flags))) { status = -ESHUTDOWN; - goto done; + goto done_not_linked; } + status = usb_hcd_link_urb_to_ep(ehci_to_hcd(ehci), urb); + if (unlikely(status)) + goto done_not_linked; /* get qh and force any scheduling errors */ INIT_LIST_HEAD (&empty); - qh = qh_append_tds (ehci, urb, &empty, epnum, &ep->hcpriv); + qh = qh_append_tds(ehci, urb, &empty, epnum, &urb->ep->hcpriv); if (qh == NULL) { status = -ENOMEM; goto done; @@ -832,13 +834,16 @@ static int intr_submit ( } /* then queue the urb's tds to the qh */ - qh = qh_append_tds (ehci, urb, qtd_list, epnum, &ep->hcpriv); + qh = qh_append_tds(ehci, urb, qtd_list, epnum, &urb->ep->hcpriv); BUG_ON (qh == NULL); /* ... update usbfs periodic stats */ ehci_to_hcd(ehci)->self.bandwidth_int_reqs++; done: + if (unlikely(status)) + usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); +done_not_linked: spin_unlock_irqrestore (&ehci->lock, flags); if (status) qtd_list_free (ehci, urb, qtd_list); @@ -1686,12 +1691,19 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, /* schedule ... need to lock */ spin_lock_irqsave (&ehci->lock, flags); if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, - &ehci_to_hcd(ehci)->flags))) + &ehci_to_hcd(ehci)->flags))) { status = -ESHUTDOWN; - else - status = iso_stream_schedule (ehci, urb, stream); + goto done_not_linked; + } + status = usb_hcd_link_urb_to_ep(ehci_to_hcd(ehci), urb); + if (unlikely(status)) + goto done_not_linked; + status = iso_stream_schedule(ehci, urb, stream); if (likely (status == 0)) itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); + else + usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); +done_not_linked: spin_unlock_irqrestore (&ehci->lock, flags); done: @@ -2049,12 +2061,19 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, /* schedule ... need to lock */ spin_lock_irqsave (&ehci->lock, flags); if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, - &ehci_to_hcd(ehci)->flags))) + &ehci_to_hcd(ehci)->flags))) { status = -ESHUTDOWN; - else - status = iso_stream_schedule (ehci, urb, stream); + goto done_not_linked; + } + status = usb_hcd_link_urb_to_ep(ehci_to_hcd(ehci), urb); + if (unlikely(status)) + goto done_not_linked; + status = iso_stream_schedule(ehci, urb, stream); if (status == 0) sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); + else + usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); +done_not_linked: spin_unlock_irqrestore (&ehci->lock, flags); done: diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 5c851a36de7..d5027dc75a5 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -290,6 +290,7 @@ __releases(isp116x->lock) __acquires(isp116x->lock) urb_dbg(urb, "Finish"); + usb_hcd_unlink_urb_from_ep(isp116x_to_hcd(isp116x), urb); spin_unlock(&isp116x->lock); usb_hcd_giveback_urb(isp116x_to_hcd(isp116x), urb); spin_lock(&isp116x->lock); @@ -673,7 +674,7 @@ static int balance(struct isp116x *isp116x, u16 period, u16 load) /*-----------------------------------------------------------------*/ static int isp116x_urb_enqueue(struct usb_hcd *hcd, - struct usb_host_endpoint *hep, struct urb *urb, + struct urb *urb, gfp_t mem_flags) { struct isp116x *isp116x = hcd_to_isp116x(hcd); @@ -682,6 +683,7 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd, int is_out = !usb_pipein(pipe); int type = usb_pipetype(pipe); int epnum = usb_pipeendpoint(pipe); + struct usb_host_endpoint *hep = urb->ep; struct isp116x_ep *ep = NULL; unsigned long flags; int i; @@ -705,7 +707,12 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd, if (!HC_IS_RUNNING(hcd->state)) { kfree(ep); ret = -ENODEV; - goto fail; + goto fail_not_linked; + } + ret = usb_hcd_link_urb_to_ep(hcd, urb); + if (ret) { + kfree(ep); + goto fail_not_linked; } if (hep->hcpriv) @@ -818,6 +825,9 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd, start_atl_transfers(isp116x); fail: + if (ret) + usb_hcd_unlink_urb_from_ep(hcd, urb); + fail_not_linked: spin_unlock_irqrestore(&isp116x->lock, flags); return ret; } @@ -825,20 +835,21 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd, /* Dequeue URBs. */ -static int isp116x_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) +static int isp116x_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, + int status) { struct isp116x *isp116x = hcd_to_isp116x(hcd); struct usb_host_endpoint *hep; struct isp116x_ep *ep, *ep_act; unsigned long flags; + int rc; spin_lock_irqsave(&isp116x->lock, flags); + rc = usb_hcd_check_unlink_urb(hcd, urb, status); + if (rc) + goto done; + hep = urb->hcpriv; - /* URB already unlinked (or never linked)? */ - if (!hep) { - spin_unlock_irqrestore(&isp116x->lock, flags); - return 0; - } ep = hep->hcpriv; WARN_ON(hep != ep->hep); @@ -856,9 +867,9 @@ static int isp116x_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) if (urb) finish_request(isp116x, ep, urb); - + done: spin_unlock_irqrestore(&isp116x->lock, flags); - return 0; + return rc; } static void isp116x_endpoint_disable(struct usb_hcd *hcd, diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index d673cb9c36b..6b06ab69938 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -117,7 +117,6 @@ MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake"); */ static int ohci_urb_enqueue ( struct usb_hcd *hcd, - struct usb_host_endpoint *ep, struct urb *urb, gfp_t mem_flags ) { @@ -134,7 +133,7 @@ static int ohci_urb_enqueue ( #endif /* every endpoint has a ed, locate and maybe (re)initialize it */ - if (! (ed = ed_get (ohci, ep, urb->dev, pipe, urb->interval))) + if (! (ed = ed_get (ohci, urb->ep, urb->dev, pipe, urb->interval))) return -ENOMEM; /* for the private part of the URB we need the number of TDs (size) */ @@ -199,22 +198,17 @@ static int ohci_urb_enqueue ( retval = -ENODEV; goto fail; } - - /* in case of unlink-during-submit */ - spin_lock (&urb->lock); - if (urb->status != -EINPROGRESS) { - spin_unlock (&urb->lock); - urb->hcpriv = urb_priv; - finish_urb (ohci, urb); - retval = 0; + retval = usb_hcd_link_urb_to_ep(hcd, urb); + if (retval) goto fail; - } /* schedule the ed if needed */ if (ed->state == ED_IDLE) { retval = ed_schedule (ohci, ed); - if (retval < 0) - goto fail0; + if (retval < 0) { + usb_hcd_unlink_urb_from_ep(hcd, urb); + goto fail; + } if (ed->type == PIPE_ISOCHRONOUS) { u16 frame = ohci_frame_no(ohci); @@ -238,8 +232,6 @@ static int ohci_urb_enqueue ( urb->hcpriv = urb_priv; td_submit_urb (ohci, urb); -fail0: - spin_unlock (&urb->lock); fail: if (retval) urb_free_priv (ohci, urb_priv); @@ -253,17 +245,21 @@ fail: * asynchronously, and we might be dealing with an urb that's * partially transferred, or an ED with other urbs being unlinked. */ -static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) +static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); unsigned long flags; + int rc; #ifdef OHCI_VERBOSE_DEBUG urb_print (urb, "UNLINK", 1); #endif spin_lock_irqsave (&ohci->lock, flags); - if (HC_IS_RUNNING(hcd->state)) { + rc = usb_hcd_check_unlink_urb(hcd, urb, status); + if (rc) { + ; /* Do nothing */ + } else if (HC_IS_RUNNING(hcd->state)) { urb_priv_t *urb_priv; /* Unless an IRQ completed the unlink while it was being @@ -284,7 +280,7 @@ static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) finish_urb (ohci, urb); } spin_unlock_irqrestore (&ohci->lock, flags); - return 0; + return rc; } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 547d39be3eb..889c0720743 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -74,6 +74,7 @@ __acquires(ohci->lock) #endif /* urb->complete() can reenter this HCD */ + usb_hcd_unlink_urb_from_ep(ohci_to_hcd(ohci), urb); spin_unlock (&ohci->lock); usb_hcd_giveback_urb (ohci_to_hcd(ohci), urb); spin_lock (&ohci->lock); diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 40a1de4c256..94bb229df3b 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -784,6 +784,9 @@ static void force_dequeue(struct r8a66597 *r8a66597, u16 pipenum, u16 address) if (urb) { urb->status = -ENODEV; urb->hcpriv = NULL; + usb_hcd_unlink_urb_from_ep(r8a66597_to_hcd(r8a66597), + urb); + spin_unlock(&r8a66597->lock); usb_hcd_giveback_urb(r8a66597_to_hcd(r8a66597), urb); spin_lock(&r8a66597->lock); @@ -1131,6 +1134,8 @@ static void done(struct r8a66597 *r8a66597, struct r8a66597_td *td, urb->start_frame = r8a66597_get_frame(hcd); urb->hcpriv = NULL; + usb_hcd_unlink_urb_from_ep(r8a66597_to_hcd(r8a66597), urb); + spin_unlock(&r8a66597->lock); usb_hcd_giveback_urb(hcd, urb); spin_lock(&r8a66597->lock); @@ -1722,21 +1727,25 @@ static struct r8a66597_td *r8a66597_make_td(struct r8a66597 *r8a66597, } static int r8a66597_urb_enqueue(struct usb_hcd *hcd, - struct usb_host_endpoint *hep, struct urb *urb, gfp_t mem_flags) { + struct usb_host_endpoint *hep = urb->ep; struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd); struct r8a66597_td *td = NULL; - int ret = 0, request = 0; + int ret, request = 0; unsigned long flags; spin_lock_irqsave(&r8a66597->lock, flags); if (!get_urb_to_r8a66597_dev(r8a66597, urb)) { ret = -ENODEV; - goto error; + goto error_not_linked; } + ret = usb_hcd_link_urb_to_ep(hcd, urb); + if (ret) + goto error_not_linked; + if (!hep->hcpriv) { hep->hcpriv = kzalloc(sizeof(struct r8a66597_pipe), GFP_ATOMIC); @@ -1761,15 +1770,7 @@ static int r8a66597_urb_enqueue(struct usb_hcd *hcd, if (list_empty(&r8a66597->pipe_queue[td->pipenum])) request = 1; list_add_tail(&td->queue, &r8a66597->pipe_queue[td->pipenum]); - - spin_lock(&urb->lock); - if (urb->status != -EINPROGRESS) { - spin_unlock(&urb->lock); - ret = -EPIPE; - goto error; - } urb->hcpriv = td; - spin_unlock(&urb->lock); if (request) { ret = start_transfer(r8a66597, td); @@ -1781,17 +1782,26 @@ static int r8a66597_urb_enqueue(struct usb_hcd *hcd, set_td_timer(r8a66597, td); error: + if (ret) + usb_hcd_unlink_urb_from_ep(hcd, urb); +error_not_linked: spin_unlock_irqrestore(&r8a66597->lock, flags); return ret; } -static int r8a66597_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) +static int r8a66597_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, + int status) { struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd); struct r8a66597_td *td; unsigned long flags; + int rc; spin_lock_irqsave(&r8a66597->lock, flags); + rc = usb_hcd_check_unlink_urb(hcd, urb, status); + if (rc) + goto done; + if (urb->hcpriv) { td = urb->hcpriv; pipe_stop(r8a66597, td->pipe); @@ -1799,8 +1809,9 @@ static int r8a66597_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) disable_irq_empty(r8a66597, td->pipenum); done(r8a66597, td, td->pipenum, urb); } + done: spin_unlock_irqrestore(&r8a66597->lock, flags); - return 0; + return rc; } static void r8a66597_endpoint_disable(struct usb_hcd *hcd, diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 4cfa3ff2c99..3d3a63d002c 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -441,6 +441,7 @@ static void finish_request( urb->hcpriv = NULL; spin_unlock(&urb->lock); + usb_hcd_unlink_urb_from_ep(sl811_to_hcd(sl811), urb); spin_unlock(&sl811->lock); usb_hcd_giveback_urb(sl811_to_hcd(sl811), urb); spin_lock(&sl811->lock); @@ -807,7 +808,6 @@ static int balance(struct sl811 *sl811, u16 period, u16 load) static int sl811h_urb_enqueue( struct usb_hcd *hcd, - struct usb_host_endpoint *hep, struct urb *urb, gfp_t mem_flags ) { @@ -820,7 +820,8 @@ static int sl811h_urb_enqueue( struct sl811h_ep *ep = NULL; unsigned long flags; int i; - int retval = 0; + int retval; + struct usb_host_endpoint *hep = urb->ep; #ifdef DISABLE_ISO if (type == PIPE_ISOCHRONOUS) @@ -838,7 +839,12 @@ static int sl811h_urb_enqueue( || !HC_IS_RUNNING(hcd->state)) { retval = -ENODEV; kfree(ep); - goto fail; + goto fail_not_linked; + } + retval = usb_hcd_link_urb_to_ep(hcd, urb); + if (retval) { + kfree(ep); + goto fail_not_linked; } if (hep->hcpriv) { @@ -965,23 +971,27 @@ static int sl811h_urb_enqueue( start_transfer(sl811); sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); fail: + if (retval) + usb_hcd_unlink_urb_from_ep(hcd, urb); +fail_not_linked: spin_unlock_irqrestore(&sl811->lock, flags); return retval; } -static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) +static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { struct sl811 *sl811 = hcd_to_sl811(hcd); struct usb_host_endpoint *hep; unsigned long flags; struct sl811h_ep *ep; - int retval = 0; + int retval; spin_lock_irqsave(&sl811->lock, flags); - hep = urb->hcpriv; - if (!hep) + retval = usb_hcd_check_unlink_urb(hcd, urb, status); + if (retval) goto fail; + hep = urb->hcpriv; ep = hep->hcpriv; if (ep) { /* finish right away if this urb can't be active ... @@ -1029,8 +1039,8 @@ static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) VDBG("dequeue, urb %p active %s; wait4irq\n", urb, (sl811->active_a == ep) ? "A" : "B"); } else -fail: retval = -EINVAL; + fail: spin_unlock_irqrestore(&sl811->lock, flags); return retval; } diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index 598ad098aee..c87660b5edc 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -521,6 +521,7 @@ static void u132_hcd_giveback_urb(struct u132 *u132, struct u132_endp *endp, urb->status = status; urb->hcpriv = NULL; spin_lock_irqsave(&endp->queue_lock.slock, irqs); + usb_hcd_unlink_urb_from_ep(hcd, urb); endp->queue_next += 1; if (ENDP_QUEUE_SIZE > --endp->queue_size) { endp->active = 0; @@ -561,6 +562,7 @@ static void u132_hcd_abandon_urb(struct u132 *u132, struct u132_endp *endp, urb->status = status; urb->hcpriv = NULL; spin_lock_irqsave(&endp->queue_lock.slock, irqs); + usb_hcd_unlink_urb_from_ep(hcd, urb); endp->queue_next += 1; if (ENDP_QUEUE_SIZE > --endp->queue_size) { endp->active = 0; @@ -1876,20 +1878,32 @@ static int u132_hcd_reset(struct usb_hcd *hcd) } static int create_endpoint_and_queue_int(struct u132 *u132, - struct u132_udev *udev, struct usb_host_endpoint *hep, struct urb *urb, + struct u132_udev *udev, struct urb *urb, struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp, u8 address, gfp_t mem_flags) { struct u132_ring *ring; unsigned long irqs; - u8 endp_number = ++u132->num_endpoints; - struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] = - kmalloc(sizeof(struct u132_endp), mem_flags); + int rc; + u8 endp_number; + struct u132_endp *endp = kmalloc(sizeof(struct u132_endp), mem_flags); + if (!endp) { return -ENOMEM; } + + spin_lock_init(&endp->queue_lock.slock); + spin_lock_irqsave(&endp->queue_lock.slock, irqs); + rc = usb_hcd_link_urb_to_ep(u132_to_hcd(u132), urb); + if (rc) { + spin_unlock_irqrestore(&endp->queue_lock.slock, irqs); + kfree(endp); + return rc; + } + + endp_number = ++u132->num_endpoints; + urb->ep->hcpriv = u132->endp[endp_number - 1] = endp; INIT_DELAYED_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler); - spin_lock_init(&endp->queue_lock.slock); INIT_LIST_HEAD(&endp->urb_more); ring = endp->ring = &u132->ring[0]; if (ring->curr_endp) { @@ -1905,7 +1919,7 @@ static int create_endpoint_and_queue_int(struct u132 *u132, endp->delayed = 0; endp->endp_number = endp_number; endp->u132 = u132; - endp->hep = hep; + endp->hep = urb->ep; endp->pipetype = usb_pipetype(urb->pipe); u132_endp_init_kref(u132, endp); if (usb_pipein(urb->pipe)) { @@ -1924,7 +1938,6 @@ static int create_endpoint_and_queue_int(struct u132 *u132, u132_udev_get_kref(u132, udev); } urb->hcpriv = u132; - spin_lock_irqsave(&endp->queue_lock.slock, irqs); endp->delayed = 1; endp->jiffies = jiffies + msecs_to_jiffies(urb->interval); endp->udev_number = address; @@ -1939,8 +1952,8 @@ static int create_endpoint_and_queue_int(struct u132 *u132, return 0; } -static int queue_int_on_old_endpoint(struct u132 *u132, struct u132_udev *udev, - struct usb_host_endpoint *hep, struct urb *urb, +static int queue_int_on_old_endpoint(struct u132 *u132, + struct u132_udev *udev, struct urb *urb, struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr, u8 usb_endp, u8 address) { @@ -1964,21 +1977,33 @@ static int queue_int_on_old_endpoint(struct u132 *u132, struct u132_udev *udev, } static int create_endpoint_and_queue_bulk(struct u132 *u132, - struct u132_udev *udev, struct usb_host_endpoint *hep, struct urb *urb, + struct u132_udev *udev, struct urb *urb, struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp, u8 address, gfp_t mem_flags) { int ring_number; struct u132_ring *ring; unsigned long irqs; - u8 endp_number = ++u132->num_endpoints; - struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] = - kmalloc(sizeof(struct u132_endp), mem_flags); + int rc; + u8 endp_number; + struct u132_endp *endp = kmalloc(sizeof(struct u132_endp), mem_flags); + if (!endp) { return -ENOMEM; } + + spin_lock_init(&endp->queue_lock.slock); + spin_lock_irqsave(&endp->queue_lock.slock, irqs); + rc = usb_hcd_link_urb_to_ep(u132_to_hcd(u132), urb); + if (rc) { + spin_unlock_irqrestore(&endp->queue_lock.slock, irqs); + kfree(endp); + return rc; + } + + endp_number = ++u132->num_endpoints; + urb->ep->hcpriv = u132->endp[endp_number - 1] = endp; INIT_DELAYED_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler); - spin_lock_init(&endp->queue_lock.slock); INIT_LIST_HEAD(&endp->urb_more); endp->dequeueing = 0; endp->edset_flush = 0; @@ -1986,7 +2011,7 @@ static int create_endpoint_and_queue_bulk(struct u132 *u132, endp->delayed = 0; endp->endp_number = endp_number; endp->u132 = u132; - endp->hep = hep; + endp->hep = urb->ep; endp->pipetype = usb_pipetype(urb->pipe); u132_endp_init_kref(u132, endp); if (usb_pipein(urb->pipe)) { @@ -2015,7 +2040,6 @@ static int create_endpoint_and_queue_bulk(struct u132 *u132, } ring->length += 1; urb->hcpriv = u132; - spin_lock_irqsave(&endp->queue_lock.slock, irqs); endp->udev_number = address; endp->usb_addr = usb_addr; endp->usb_endp = usb_endp; @@ -2029,7 +2053,7 @@ static int create_endpoint_and_queue_bulk(struct u132 *u132, } static int queue_bulk_on_old_endpoint(struct u132 *u132, struct u132_udev *udev, - struct usb_host_endpoint *hep, struct urb *urb, + struct urb *urb, struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr, u8 usb_endp, u8 address) { @@ -2051,19 +2075,32 @@ static int queue_bulk_on_old_endpoint(struct u132 *u132, struct u132_udev *udev, } static int create_endpoint_and_queue_control(struct u132 *u132, - struct usb_host_endpoint *hep, struct urb *urb, + struct urb *urb, struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp, gfp_t mem_flags) { struct u132_ring *ring; - u8 endp_number = ++u132->num_endpoints; - struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] = - kmalloc(sizeof(struct u132_endp), mem_flags); + unsigned long irqs; + int rc; + u8 endp_number; + struct u132_endp *endp = kmalloc(sizeof(struct u132_endp), mem_flags); + if (!endp) { return -ENOMEM; } + + spin_lock_init(&endp->queue_lock.slock); + spin_lock_irqsave(&endp->queue_lock.slock, irqs); + rc = usb_hcd_link_urb_to_ep(u132_to_hcd(u132), urb); + if (rc) { + spin_unlock_irqrestore(&endp->queue_lock.slock, irqs); + kfree(endp); + return rc; + } + + endp_number = ++u132->num_endpoints; + urb->ep->hcpriv = u132->endp[endp_number - 1] = endp; INIT_DELAYED_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler); - spin_lock_init(&endp->queue_lock.slock); INIT_LIST_HEAD(&endp->urb_more); ring = endp->ring = &u132->ring[0]; if (ring->curr_endp) { @@ -2079,11 +2116,10 @@ static int create_endpoint_and_queue_control(struct u132 *u132, endp->delayed = 0; endp->endp_number = endp_number; endp->u132 = u132; - endp->hep = hep; + endp->hep = urb->ep; u132_endp_init_kref(u132, endp); u132_endp_get_kref(u132, endp); if (usb_addr == 0) { - unsigned long irqs; u8 address = u132->addr[usb_addr].address; struct u132_udev *udev = &u132->udev[address]; endp->udev_number = address; @@ -2097,7 +2133,6 @@ static int create_endpoint_and_queue_control(struct u132 *u132, udev->endp_number_in[usb_endp] = endp_number; udev->endp_number_out[usb_endp] = endp_number; urb->hcpriv = u132; - spin_lock_irqsave(&endp->queue_lock.slock, irqs); endp->queue_size = 1; endp->queue_last = 0; endp->queue_next = 0; @@ -2106,7 +2141,6 @@ static int create_endpoint_and_queue_control(struct u132 *u132, u132_endp_queue_work(u132, endp, 0); return 0; } else { /*(usb_addr > 0) */ - unsigned long irqs; u8 address = u132->addr[usb_addr].address; struct u132_udev *udev = &u132->udev[address]; endp->udev_number = address; @@ -2120,7 +2154,6 @@ static int create_endpoint_and_queue_control(struct u132 *u132, udev->endp_number_in[usb_endp] = endp_number; udev->endp_number_out[usb_endp] = endp_number; urb->hcpriv = u132; - spin_lock_irqsave(&endp->queue_lock.slock, irqs); endp->queue_size = 1; endp->queue_last = 0; endp->queue_next = 0; @@ -2132,7 +2165,7 @@ static int create_endpoint_and_queue_control(struct u132 *u132, } static int queue_control_on_old_endpoint(struct u132 *u132, - struct usb_host_endpoint *hep, struct urb *urb, + struct urb *urb, struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr, u8 usb_endp) { @@ -2232,8 +2265,8 @@ static int queue_control_on_old_endpoint(struct u132 *u132, } } -static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep, - struct urb *urb, gfp_t mem_flags) +static int u132_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) { struct u132 *u132 = hcd_to_u132(hcd); if (irqs_disabled()) { @@ -2258,16 +2291,24 @@ static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep, if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { u8 address = u132->addr[usb_addr].address; struct u132_udev *udev = &u132->udev[address]; - struct u132_endp *endp = hep->hcpriv; + struct u132_endp *endp = urb->ep->hcpriv; urb->actual_length = 0; if (endp) { unsigned long irqs; int retval; spin_lock_irqsave(&endp->queue_lock.slock, irqs); - retval = queue_int_on_old_endpoint(u132, udev, - hep, urb, usb_dev, endp, usb_addr, - usb_endp, address); + retval = usb_hcd_link_urb_to_ep(hcd, urb); + if (retval == 0) { + retval = queue_int_on_old_endpoint( + u132, udev, urb, + usb_dev, endp, + usb_addr, usb_endp, + address); + if (retval) + usb_hcd_unlink_urb_from_ep( + hcd, urb); + } spin_unlock_irqrestore(&endp->queue_lock.slock, irqs); if (retval) { @@ -2282,8 +2323,8 @@ static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep, return -EINVAL; } else { /*(endp == NULL) */ return create_endpoint_and_queue_int(u132, udev, - hep, urb, usb_dev, usb_addr, usb_endp, - address, mem_flags); + urb, usb_dev, usb_addr, + usb_endp, address, mem_flags); } } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { dev_err(&u132->platform_dev->dev, "the hardware does no" @@ -2292,16 +2333,24 @@ static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep, } else if (usb_pipetype(urb->pipe) == PIPE_BULK) { u8 address = u132->addr[usb_addr].address; struct u132_udev *udev = &u132->udev[address]; - struct u132_endp *endp = hep->hcpriv; + struct u132_endp *endp = urb->ep->hcpriv; urb->actual_length = 0; if (endp) { unsigned long irqs; int retval; spin_lock_irqsave(&endp->queue_lock.slock, irqs); - retval = queue_bulk_on_old_endpoint(u132, udev, - hep, urb, usb_dev, endp, usb_addr, - usb_endp, address); + retval = usb_hcd_link_urb_to_ep(hcd, urb); + if (retval == 0) { + retval = queue_bulk_on_old_endpoint( + u132, udev, urb, + usb_dev, endp, + usb_addr, usb_endp, + address); + if (retval) + usb_hcd_unlink_urb_from_ep( + hcd, urb); + } spin_unlock_irqrestore(&endp->queue_lock.slock, irqs); if (retval) { @@ -2314,10 +2363,10 @@ static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep, return -EINVAL; } else return create_endpoint_and_queue_bulk(u132, - udev, hep, urb, usb_dev, usb_addr, + udev, urb, usb_dev, usb_addr, usb_endp, address, mem_flags); } else { - struct u132_endp *endp = hep->hcpriv; + struct u132_endp *endp = urb->ep->hcpriv; u16 urb_size = 8; u8 *b = urb->setup_packet; int i = 0; @@ -2340,9 +2389,16 @@ static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep, int retval; spin_lock_irqsave(&endp->queue_lock.slock, irqs); - retval = queue_control_on_old_endpoint(u132, - hep, urb, usb_dev, endp, usb_addr, - usb_endp); + retval = usb_hcd_link_urb_to_ep(hcd, urb); + if (retval == 0) { + retval = queue_control_on_old_endpoint( + u132, urb, usb_dev, + endp, usb_addr, + usb_endp); + if (retval) + usb_hcd_unlink_urb_from_ep( + hcd, urb); + } spin_unlock_irqrestore(&endp->queue_lock.slock, irqs); if (retval) { @@ -2355,7 +2411,7 @@ static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep, return -EINVAL; } else return create_endpoint_and_queue_control(u132, - hep, urb, usb_dev, usb_addr, usb_endp, + urb, usb_dev, usb_addr, usb_endp, mem_flags); } } @@ -2390,10 +2446,17 @@ static int dequeue_from_overflow_chain(struct u132 *u132, } static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp, - struct urb *urb) + struct urb *urb, int status) { unsigned long irqs; + int rc; + spin_lock_irqsave(&endp->queue_lock.slock, irqs); + rc = usb_hcd_check_unlink_urb(u132_to_hcd(u132), urb, status); + if (rc) { + spin_unlock_irqrestore(&endp->queue_lock.slock, irqs); + return rc; + } if (endp->queue_size == 0) { dev_err(&u132->platform_dev->dev, "urb=%p not found in endp[%d]" "=%p ring[%d] %c%c usb_endp=%d usb_addr=%d\n", urb, @@ -2438,6 +2501,8 @@ static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp, } if (urb_slot) { struct usb_hcd *hcd = u132_to_hcd(u132); + + usb_hcd_unlink_urb_from_ep(hcd, urb); endp->queue_size -= 1; if (list_empty(&endp->urb_more)) { spin_unlock_irqrestore(&endp->queue_lock.slock, @@ -2467,7 +2532,10 @@ static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp, spin_unlock_irqrestore(&endp->queue_lock.slock, irqs); return -EINVAL; } else { - int retval = dequeue_from_overflow_chain(u132, endp, + int retval; + + usb_hcd_unlink_urb_from_ep(u132_to_hcd(u132), urb); + retval = dequeue_from_overflow_chain(u132, endp, urb); spin_unlock_irqrestore(&endp->queue_lock.slock, irqs); return retval; @@ -2475,7 +2543,7 @@ static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp, } } -static int u132_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) +static int u132_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { struct u132 *u132 = hcd_to_u132(hcd); if (u132->going > 2) { @@ -2490,11 +2558,11 @@ static int u132_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) if (usb_pipein(urb->pipe)) { u8 endp_number = udev->endp_number_in[usb_endp]; struct u132_endp *endp = u132->endp[endp_number - 1]; - return u132_endp_urb_dequeue(u132, endp, urb); + return u132_endp_urb_dequeue(u132, endp, urb, status); } else { u8 endp_number = udev->endp_number_out[usb_endp]; struct u132_endp *endp = u132->endp[endp_number - 1]; - return u132_endp_urb_dequeue(u132, endp, urb); + return u132_endp_urb_dequeue(u132, endp, urb, status); } } } diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 3bb908ca38e..bff200cb3d2 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -1376,7 +1376,6 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) } static int uhci_urb_enqueue(struct usb_hcd *hcd, - struct usb_host_endpoint *hep, struct urb *urb, gfp_t mem_flags) { int ret; @@ -1387,19 +1386,19 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd, spin_lock_irqsave(&uhci->lock, flags); - ret = urb->status; - if (ret != -EINPROGRESS) /* URB already unlinked! */ - goto done; + ret = usb_hcd_link_urb_to_ep(hcd, urb); + if (ret) + goto done_not_linked; ret = -ENOMEM; urbp = uhci_alloc_urb_priv(uhci, urb); if (!urbp) goto done; - if (hep->hcpriv) - qh = (struct uhci_qh *) hep->hcpriv; + if (urb->ep->hcpriv) + qh = urb->ep->hcpriv; else { - qh = uhci_alloc_qh(uhci, urb->dev, hep); + qh = uhci_alloc_qh(uhci, urb->dev, urb->ep); if (!qh) goto err_no_qh; } @@ -1440,27 +1439,29 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd, err_submit_failed: if (qh->state == QH_STATE_IDLE) uhci_make_qh_idle(uhci, qh); /* Reclaim unused QH */ - err_no_qh: uhci_free_urb_priv(uhci, urbp); - done: + if (ret) + usb_hcd_unlink_urb_from_ep(hcd, urb); +done_not_linked: spin_unlock_irqrestore(&uhci->lock, flags); return ret; } -static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) +static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); unsigned long flags; - struct urb_priv *urbp; struct uhci_qh *qh; + int rc; spin_lock_irqsave(&uhci->lock, flags); - urbp = urb->hcpriv; - if (!urbp) /* URB was never linked! */ + rc = usb_hcd_check_unlink_urb(hcd, urb, status); + if (rc) goto done; - qh = urbp->qh; + + qh = ((struct urb_priv *) urb->hcpriv)->qh; /* Remove Isochronous TDs from the frame list ASAP */ if (qh->type == USB_ENDPOINT_XFER_ISOC) { @@ -1477,7 +1478,7 @@ static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) done: spin_unlock_irqrestore(&uhci->lock, flags); - return 0; + return rc; } /* @@ -1529,6 +1530,7 @@ __acquires(uhci->lock) } uhci_free_urb_priv(uhci, urbp); + usb_hcd_unlink_urb_from_ep(uhci_to_hcd(uhci), urb); spin_unlock(&uhci->lock); usb_hcd_giveback_urb(uhci_to_hcd(uhci), urb); -- cgit v1.2.3 From e39ab592f182cd0be48acc4ad49f93ef4100017c Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 16 Aug 2007 16:17:49 -0400 Subject: USB: remove unnecessary tests in isp116x and sl811 This patch (as962) cleans up some code I forgot to remove earlier in the isp116x and sl811 HCDs. There is no longer any need to check for unlink-during-submit; it can't happen since the endpoint queues are now under the protection of the HCD-private spinlock. Signed-off-by: Alan Stern CC: David Brownell CC: Olav Kongas Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp116x-hcd.c | 6 ------ drivers/usb/host/sl811-hcd.c | 10 ---------- 2 files changed, 16 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index d5027dc75a5..f2b5d6281c5 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -815,12 +815,6 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd, } } - /* in case of unlink-during-submit */ - if (urb->status != -EINPROGRESS) { - finish_request(isp116x, ep, urb); - ret = 0; - goto fail; - } urb->hcpriv = hep; start_atl_transfers(isp116x); diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 3d3a63d002c..15a93f946af 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -957,17 +957,7 @@ static int sl811h_urb_enqueue( sofirq_on(sl811); } - /* in case of unlink-during-submit */ - spin_lock(&urb->lock); - if (urb->status != -EINPROGRESS) { - spin_unlock(&urb->lock); - finish_request(sl811, ep, urb, 0); - retval = 0; - goto fail; - } urb->hcpriv = hep; - spin_unlock(&urb->lock); - start_transfer(sl811); sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); fail: -- cgit v1.2.3 From af1c51fcb2aea23ec2dfe73b7d66515d1622e689 Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Mon, 20 Aug 2007 18:13:27 -0700 Subject: USB: EHCI restart speedup It is not necessary to powerdown the ports on ehci_pci_reinit() when the chip reset already did that. Removing this saves 20ms during restart after poweroff paths (which OLPC uses a lot). To ensure driver startup then behaves consistently, force a reset during driver startup. (Not doing this was an accident of some previous changes to the init sequence.) Make the corresponding change in the PS3 support. It's not clear what ehci-fsl should do here; it has similar code to the PS3. Signed-off-by: Marcelo Tosatti Signed-off-by: David Brownell Cc: Acked-by: Geoff Levand Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-pci.c | 5 +---- drivers/usb/host/ehci-ps3.c | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index a7816e392a8..ad0d4965f2f 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -58,8 +58,6 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) if (!retval) ehci_dbg(ehci, "MWI active\n"); - ehci_port_power(ehci, 0); - return 0; } @@ -156,8 +154,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd) break; } - if (ehci_is_TDI(ehci)) - ehci_reset(ehci); + ehci_reset(ehci); /* at least the Genesys GL880S needs fixup here */ temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params); diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 829fe649a98..03a6b2f4e6e 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -47,7 +47,7 @@ static int ps3_ehci_hc_reset(struct usb_hcd *hcd) if (result) return result; - ehci_port_power(ehci, 0); + ehci_reset(ehci); return result; } -- cgit v1.2.3 From dfd1e53777afe1050e3a0a3f0dd063a64242b818 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 21 Aug 2007 15:36:52 -0400 Subject: USB: minor fixes for r8a66597 driver This patch (as967) makes a few relatively minor changes to the r8a66597 driver: finish_request() does nothing but call done(), so merge the two routines. Detect and report -EOVERFLOW errors. Fix the calculation that checks for short packets. Signed-off-by: Alan Stern CC: Yoshihiro Shimoda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/r8a66597-hcd.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 94bb229df3b..49cf998c172 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -1109,8 +1109,9 @@ static void set_td_timer(struct r8a66597 *r8a66597, struct r8a66597_td *td) } /* this function must be called with interrupt disabled */ -static void done(struct r8a66597 *r8a66597, struct r8a66597_td *td, - u16 pipenum, struct urb *urb) +static void finish_request(struct r8a66597 *r8a66597, struct r8a66597_td *td, + u16 pipenum, struct urb *urb) +__releases(r8a66597->lock) __acquires(r8a66597->lock) { int restart = 0; struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597); @@ -1151,14 +1152,6 @@ static void done(struct r8a66597 *r8a66597, struct r8a66597_td *td, } } -/* this function must be called with interrupt disabled */ -static void finish_request(struct r8a66597 *r8a66597, struct r8a66597_td *td, - u16 pipenum, struct urb *urb) -__releases(r8a66597->lock) __acquires(r8a66597->lock) -{ - done(r8a66597, td, pipenum, urb); -} - static void packet_read(struct r8a66597 *r8a66597, u16 pipenum) { u16 tmp; @@ -1167,6 +1160,7 @@ static void packet_read(struct r8a66597 *r8a66597, u16 pipenum) struct r8a66597_td *td = r8a66597_get_td(r8a66597, pipenum); struct urb *urb; int finish = 0; + int status = 0; if (unlikely(!td)) return; @@ -1185,7 +1179,6 @@ static void packet_read(struct r8a66597 *r8a66597, u16 pipenum) /* prepare parameters */ rcv_len = tmp & DTLN; - bufsize = td->maxpacket; if (usb_pipeisoc(urb->pipe)) { buf = (u16 *)(urb->transfer_buffer + urb->iso_frame_desc[td->iso_cnt].offset); @@ -1194,25 +1187,30 @@ static void packet_read(struct r8a66597 *r8a66597, u16 pipenum) buf = (void *)urb->transfer_buffer + urb->actual_length; urb_len = urb->transfer_buffer_length - urb->actual_length; } - if (rcv_len < bufsize) - size = min(rcv_len, urb_len); - else - size = min(bufsize, urb_len); + bufsize = min(urb_len, (int) td->maxpacket); + if (rcv_len <= bufsize) { + size = rcv_len; + } else { + size = bufsize; + status = -EOVERFLOW; + finish = 1; + } /* update parameters */ urb->actual_length += size; if (rcv_len == 0) td->zero_packet = 1; - if ((size % td->maxpacket) > 0) { + if (rcv_len < bufsize) { td->short_packet = 1; if (urb->transfer_buffer_length != urb->actual_length && urb->transfer_flags & URB_SHORT_NOT_OK) - td->urb->status = -EREMOTEIO; + status = -EREMOTEIO; } if (usb_pipeisoc(urb->pipe)) { urb->iso_frame_desc[td->iso_cnt].actual_length = size; - urb->iso_frame_desc[td->iso_cnt].status = 0; + urb->iso_frame_desc[td->iso_cnt].status = status; td->iso_cnt++; + finish = 0; } /* check transfer finish */ @@ -1233,7 +1231,7 @@ static void packet_read(struct r8a66597 *r8a66597, u16 pipenum) if (finish && pipenum != 0) { if (td->urb->status == -EINPROGRESS) - td->urb->status = 0; + td->urb->status = status; finish_request(r8a66597, td, pipenum, urb); } } @@ -1807,7 +1805,7 @@ static int r8a66597_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, pipe_stop(r8a66597, td->pipe); pipe_irq_disable(r8a66597, td->pipenum); disable_irq_empty(r8a66597, td->pipenum); - done(r8a66597, td, td->pipenum, urb); + finish_request(r8a66597, td, td->pipenum, urb); } done: spin_unlock_irqrestore(&r8a66597->lock, flags); @@ -1841,7 +1839,7 @@ static void r8a66597_endpoint_disable(struct usb_hcd *hcd, td = r8a66597_get_td(r8a66597, pipenum); if (td) urb = td->urb; - done(r8a66597, td, pipenum, urb); + finish_request(r8a66597, td, pipenum, urb); kfree(hep->hcpriv); hep->hcpriv = NULL; spin_unlock_irqrestore(&r8a66597->lock, flags); -- cgit v1.2.3 From ee7d1f3f0c32d8abe9627aa73dc62ee5bf2daf7f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 21 Aug 2007 15:37:50 -0400 Subject: USB: remove Iso status value in uhci-hcd This patch (968) changes the way uhci-hcd reports status for Isochronous URBs. Until now urb->status has been set to the last detected error code. But other HCDs don't do this; they leave the status set to 0 and report errors only in the individual iso packet descriptors. So this patch removes the extra computation and makes uhci-hcd behave like the others. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.h | 1 - drivers/usb/host/uhci-q.c | 11 +++-------- 2 files changed, 3 insertions(+), 9 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 1b3d23406ac..e46d2b0203c 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -146,7 +146,6 @@ struct uhci_qh { short phase; /* Between 0 and period-1 */ short load; /* Periodic time requirement, in us */ unsigned int iso_frame; /* Frame # for iso_packet_desc */ - int iso_status; /* Status for Isochronous URBs */ int state; /* QH_STATE_xxx; see above */ int type; /* Queue type (control, bulk, etc) */ diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index bff200cb3d2..793a04685ef 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -1324,7 +1324,6 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, if (list_empty(&qh->queue)) { qh->iso_packet_desc = &urb->iso_frame_desc[0]; qh->iso_frame = urb->start_frame; - qh->iso_status = 0; } qh->skel = SKEL_ISO; @@ -1361,18 +1360,15 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) qh->iso_packet_desc->actual_length = actlength; qh->iso_packet_desc->status = status; } - - if (status) { + if (status) urb->error_count++; - qh->iso_status = status; - } uhci_remove_td_from_urbp(td); uhci_free_td(uhci, td); qh->iso_frame += qh->period; ++qh->iso_packet_desc; } - return qh->iso_status; + return 0; } static int uhci_urb_enqueue(struct usb_hcd *hcd, @@ -1517,7 +1513,6 @@ __acquires(uhci->lock) qh->iso_packet_desc = &nurb->iso_frame_desc[0]; qh->iso_frame = nurb->start_frame; - qh->iso_status = 0; } /* Take the URB off the QH's queue. If the queue is now empty, @@ -1586,7 +1581,7 @@ static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) } uhci_giveback_urb(uhci, qh, urb); - if (status < 0 && qh->type != USB_ENDPOINT_XFER_ISOC) + if (status < 0) break; } -- cgit v1.2.3 From b0d9efba3ec53468984aecef8eeaf079089f2e5a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 21 Aug 2007 15:39:21 -0400 Subject: USB: centralize -EREMOTEIO handling This patch (as969) continues the ongoing changes to the way HCDs report URB statuses. The programming interface has been simplified by making usbcore responsible for clearing urb->hcpriv and for setting -EREMOTEIO status when an URB with the URB_SHORT_NOT_OK flag ends up as a short transfer. By moving the work out of the HCDs, this removes a fair amount of repeated code. Signed-off-by: Alan Stern CC: David Brownell CC: Olav Kongas CC: Yoshihiro Shimoda CC: Tony Olech Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-q.c | 7 ++++--- drivers/usb/host/isp116x-hcd.c | 8 +------- drivers/usb/host/ohci-q.c | 11 ----------- drivers/usb/host/r8a66597-hcd.c | 7 +------ drivers/usb/host/sl811-hcd.c | 14 +++----------- drivers/usb/host/u132-hcd.c | 5 ----- drivers/usb/host/uhci-q.c | 8 -------- 7 files changed, 9 insertions(+), 51 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 1da2de4d34e..e80b5c417d7 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -232,7 +232,6 @@ __acquires(ehci->lock) } spin_lock (&urb->lock); - urb->hcpriv = NULL; switch (urb->status) { case -EINPROGRESS: /* success */ urb->status = 0; @@ -395,8 +394,10 @@ halt: /* remove it from the queue */ spin_lock (&urb->lock); qtd_copy_status (ehci, urb, qtd->length, token); - do_status = (urb->status == -EREMOTEIO) - && usb_pipecontrol (urb->pipe); + if (unlikely(urb->status == -EREMOTEIO)) { + do_status = usb_pipecontrol(urb->pipe); + urb->status = 0; + } spin_unlock (&urb->lock); if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index f2b5d6281c5..c2919dbc3f5 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -282,7 +282,6 @@ __releases(isp116x->lock) __acquires(isp116x->lock) { unsigned i; - urb->hcpriv = NULL; ep->error_count = 0; if (usb_pipecontrol(urb->pipe)) @@ -446,12 +445,7 @@ static void postproc_atl_queue(struct isp116x *isp116x) if (PTD_GET_ACTIVE(ptd) || (cc != TD_CC_NOERROR && cc < 0x0E)) break; - if ((urb->transfer_flags & URB_SHORT_NOT_OK) && - urb->actual_length < - urb->transfer_buffer_length) - status = -EREMOTEIO; - else - status = 0; + status = 0; ep->nextpid = 0; break; default: diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 889c0720743..8aad6199cdc 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -43,21 +43,10 @@ __acquires(ohci->lock) // ASSERT (urb->hcpriv != 0); urb_free_priv (ohci, urb->hcpriv); - urb->hcpriv = NULL; spin_lock (&urb->lock); if (likely (urb->status == -EINPROGRESS)) urb->status = 0; - /* report short control reads right even though the data TD always - * has TD_R set. (much simpler, but creates the 1-td limit.) - */ - if (unlikely (urb->transfer_flags & URB_SHORT_NOT_OK) - && unlikely (usb_pipecontrol (urb->pipe)) - && urb->actual_length < urb->transfer_buffer_length - && usb_pipein (urb->pipe) - && urb->status == 0) { - urb->status = -EREMOTEIO; - } spin_unlock (&urb->lock); switch (usb_pipetype (urb->pipe)) { diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 49cf998c172..60248b01ce1 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -783,7 +783,6 @@ static void force_dequeue(struct r8a66597 *r8a66597, u16 pipenum, u16 address) if (urb) { urb->status = -ENODEV; - urb->hcpriv = NULL; usb_hcd_unlink_urb_from_ep(r8a66597_to_hcd(r8a66597), urb); @@ -1134,7 +1133,6 @@ __releases(r8a66597->lock) __acquires(r8a66597->lock) if (usb_pipeisoc(urb->pipe)) urb->start_frame = r8a66597_get_frame(hcd); - urb->hcpriv = NULL; usb_hcd_unlink_urb_from_ep(r8a66597_to_hcd(r8a66597), urb); spin_unlock(&r8a66597->lock); @@ -1202,9 +1200,6 @@ static void packet_read(struct r8a66597 *r8a66597, u16 pipenum) td->zero_packet = 1; if (rcv_len < bufsize) { td->short_packet = 1; - if (urb->transfer_buffer_length != urb->actual_length && - urb->transfer_flags & URB_SHORT_NOT_OK) - status = -EREMOTEIO; } if (usb_pipeisoc(urb->pipe)) { urb->iso_frame_desc[td->iso_cnt].actual_length = size; @@ -1214,7 +1209,7 @@ static void packet_read(struct r8a66597 *r8a66597, u16 pipenum) } /* check transfer finish */ - if (check_transfer_finish(td, urb)) { + if (finish || check_transfer_finish(td, urb)) { pipe_stop(r8a66597, td->pipe); pipe_irq_disable(r8a66597, pipenum); finish = 1; diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 15a93f946af..e90953a9c9f 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -438,7 +438,6 @@ static void finish_request( spin_lock(&urb->lock); if (urb->status == -EINPROGRESS) urb->status = status; - urb->hcpriv = NULL; spin_unlock(&urb->lock); usb_hcd_unlink_urb_from_ep(sl811_to_hcd(sl811), urb); @@ -545,17 +544,10 @@ done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank) sl811_read_buf(sl811, SL811HS_PACKET_BUF(bank == 0), buf, len); usb_dotoggle(udev, ep->epnum, 0); - if (urb->actual_length == urb->transfer_buffer_length) + if (urb->actual_length == urb->transfer_buffer_length + || len < ep->maxpacket) urbstat = 0; - else if (len < ep->maxpacket) { - if (urb->transfer_flags & URB_SHORT_NOT_OK) - urbstat = -EREMOTEIO; - else - urbstat = 0; - } - if (usb_pipecontrol(urb->pipe) - && (urbstat == -EREMOTEIO - || urbstat == 0)) { + if (usb_pipecontrol(urb->pipe) && urbstat == 0) { /* NOTE if the status stage STALLs (why?), * this reports the wrong urb status. diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index c87660b5edc..1381275d448 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -519,7 +519,6 @@ static void u132_hcd_giveback_urb(struct u132 *u132, struct u132_endp *endp, struct usb_hcd *hcd = u132_to_hcd(u132); urb->error_count = 0; urb->status = status; - urb->hcpriv = NULL; spin_lock_irqsave(&endp->queue_lock.slock, irqs); usb_hcd_unlink_urb_from_ep(hcd, urb); endp->queue_next += 1; @@ -560,7 +559,6 @@ static void u132_hcd_abandon_urb(struct u132 *u132, struct u132_endp *endp, struct usb_hcd *hcd = u132_to_hcd(u132); urb->error_count = 0; urb->status = status; - urb->hcpriv = NULL; spin_lock_irqsave(&endp->queue_lock.slock, irqs); usb_hcd_unlink_urb_from_ep(hcd, urb); endp->queue_next += 1; @@ -2430,7 +2428,6 @@ static int dequeue_from_overflow_chain(struct u132 *u132, list_del(scan); endp->queue_size -= 1; urb->error_count = 0; - urb->hcpriv = NULL; usb_hcd_giveback_urb(hcd, urb); return 0; } else @@ -2472,7 +2469,6 @@ static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp, endp->edset_flush = 1; u132_endp_queue_work(u132, endp, 0); spin_unlock_irqrestore(&endp->queue_lock.slock, irqs); - urb->hcpriv = NULL; return 0; } else { spin_unlock_irqrestore(&endp->queue_lock.slock, irqs); @@ -2517,7 +2513,6 @@ static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp, irqs); kfree(urbq); } urb->error_count = 0; - urb->hcpriv = NULL; usb_hcd_giveback_urb(hcd, urb); return 0; } else if (list_empty(&endp->urb_more)) { diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 793a04685ef..fbc3af392c2 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -757,7 +757,6 @@ static void uhci_free_urb_priv(struct uhci_hcd *uhci, uhci_free_td(uhci, td); } - urbp->urb->hcpriv = NULL; kmem_cache_free(uhci_up_cachep, urbp); } @@ -1494,13 +1493,6 @@ __acquires(uhci->lock) * unlinked first. Regardless, don't confuse people with a * negative length. */ urb->actual_length = max(urb->actual_length, 0); - - /* Report erroneous short transfers */ - if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) && - urb->actual_length < - urb->transfer_buffer_length && - urb->status == 0)) - urb->status = -EREMOTEIO; } /* When giving back the first URB in an Isochronous queue, -- cgit v1.2.3 From eb23105462304fd35571fd0cab1de7aec79a9ec5 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 21 Aug 2007 15:40:36 -0400 Subject: USB: add urb->unlinked field This patch (as970) adds a new urb->unlinked field, which is used to store the status of unlinked URBs since we can't use urb->status for that purpose any more. To help simplify the HCDs, usbcore will check urb->unlinked before calling the completion handler; if the value is set it will automatically override the status reported by the HCD. Signed-off-by: Alan Stern CC: David Brownell CC: Olav Kongas CC: Yoshihiro Shimoda CC: Tony Olech Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-q.c | 29 ++++------ drivers/usb/host/isp116x-hcd.c | 5 +- drivers/usb/host/ohci-q.c | 5 +- drivers/usb/host/r8a66597-hcd.c | 24 +++----- drivers/usb/host/sl811-hcd.c | 5 +- drivers/usb/host/u132-hcd.c | 124 ++++++++++++++++++++-------------------- drivers/usb/host/uhci-debug.c | 4 +- drivers/usb/host/uhci-q.c | 9 +-- 8 files changed, 92 insertions(+), 113 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index e80b5c417d7..a8f5408c161 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -151,7 +151,7 @@ static void qtd_copy_status ( urb->actual_length += length - QTD_LENGTH (token); /* don't modify error codes */ - if (unlikely (urb->status != -EINPROGRESS)) + if (unlikely(urb->unlinked)) return; /* force cleanup after short read; not always an error */ @@ -232,21 +232,14 @@ __acquires(ehci->lock) } spin_lock (&urb->lock); - 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)) + if (unlikely(urb->unlinked)) { + COUNT(ehci->stats.unlink); + } else { + if (likely(urb->status == -EINPROGRESS || + (urb->status == -EREMOTEIO && + !(urb->transfer_flags & URB_SHORT_NOT_OK)))) urb->status = 0; - COUNT (ehci->stats.complete); - break; - case -ECONNRESET: /* canceled */ - case -ENOENT: - COUNT (ehci->stats.unlink); - break; + COUNT(ehci->stats.complete); } spin_unlock (&urb->lock); @@ -364,7 +357,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) * for the urb faulted (including short read) or * its urb was canceled. we may patch qh or qtds. */ - if (likely (urb->status == -EINPROGRESS)) + if (likely(urb->status == -EINPROGRESS && + !urb->unlinked)) continue; /* issue status after short control reads */ @@ -395,7 +389,8 @@ halt: spin_lock (&urb->lock); qtd_copy_status (ehci, urb, qtd->length, token); if (unlikely(urb->status == -EREMOTEIO)) { - do_status = usb_pipecontrol(urb->pipe); + do_status = (!urb->unlinked && + usb_pipecontrol(urb->pipe)); urb->status = 0; } spin_unlock (&urb->lock); diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index c2919dbc3f5..35b3507ff40 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -455,11 +455,10 @@ static void postproc_atl_queue(struct isp116x *isp116x) done: if (status != -EINPROGRESS) { spin_lock(&urb->lock); - if (urb->status == -EINPROGRESS) - urb->status = status; + urb->status = status; spin_unlock(&urb->lock); } - if (urb->status != -EINPROGRESS) + if (urb->status != -EINPROGRESS || urb->unlinked) finish_request(isp116x, ep, urb); } } diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 8aad6199cdc..3c793fad178 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -758,8 +758,7 @@ static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td) cc = TD_CC_NOERROR; if (cc != TD_CC_NOERROR && cc < 0x0E) { spin_lock (&urb->lock); - if (urb->status == -EINPROGRESS) - urb->status = cc_to_error [cc]; + urb->status = cc_to_error[cc]; spin_unlock (&urb->lock); } @@ -972,7 +971,7 @@ rescan_this: urb = td->urb; urb_priv = td->urb->hcpriv; - if (urb->status == -EINPROGRESS) { + if (!urb->unlinked) { prev = &td->hwNextTD; continue; } diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 60248b01ce1..98b9e054754 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -1118,7 +1118,7 @@ __releases(r8a66597->lock) __acquires(r8a66597->lock) r8a66597->timeout_map &= ~(1 << pipenum); if (likely(td)) { - if (td->set_address && urb->status != 0) + if (td->set_address && (urb->status != 0 || urb->unlinked)) r8a66597->address_map &= ~(1 << urb->setup_packet[2]); pipe_toggle_save(r8a66597, td->pipe, urb); @@ -1225,8 +1225,7 @@ static void packet_read(struct r8a66597 *r8a66597, u16 pipenum) } if (finish && pipenum != 0) { - if (td->urb->status == -EINPROGRESS) - td->urb->status = status; + td->urb->status = status; finish_request(r8a66597, td, pipenum, urb); } } @@ -1308,32 +1307,24 @@ static void check_next_phase(struct r8a66597 *r8a66597) switch (td->type) { case USB_PID_IN: case USB_PID_OUT: - if (urb->status != -EINPROGRESS) { - finish = 1; - break; - } if (check_transfer_finish(td, urb)) td->type = USB_PID_ACK; break; case USB_PID_SETUP: - if (urb->status != -EINPROGRESS) - finish = 1; - else if (urb->transfer_buffer_length == urb->actual_length) { + if (urb->transfer_buffer_length == urb->actual_length) td->type = USB_PID_ACK; - urb->status = 0; - } else if (usb_pipeout(urb->pipe)) + else if (usb_pipeout(urb->pipe)) td->type = USB_PID_OUT; else td->type = USB_PID_IN; break; case USB_PID_ACK: finish = 1; - if (urb->status == -EINPROGRESS) - urb->status = 0; + urb->status = 0; break; } - if (finish) + if (finish || urb->unlinked) finish_request(r8a66597, td, 0, urb); else start_transfer(r8a66597, td); @@ -1418,8 +1409,7 @@ static void irq_pipe_empty(struct r8a66597 *r8a66597) if ((tmp & INBUFM) == 0) { disable_irq_empty(r8a66597, pipenum); pipe_irq_disable(r8a66597, pipenum); - if (td->urb->status == -EINPROGRESS) - td->urb->status = 0; + td->urb->status = 0; finish_request(r8a66597, td, pipenum, td->urb); } } diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index e90953a9c9f..f0fa94148d9 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -436,8 +436,7 @@ static void finish_request( ep->nextpid = USB_PID_SETUP; spin_lock(&urb->lock); - if (urb->status == -EINPROGRESS) - urb->status = status; + urb->status = status; spin_unlock(&urb->lock); usb_hcd_unlink_urb_from_ep(sl811_to_hcd(sl811), urb); @@ -598,7 +597,7 @@ done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank) bank, status, ep, urbstat); } - if (urb && (urbstat != -EINPROGRESS || urb->status != -EINPROGRESS)) + if (urb && (urbstat != -EINPROGRESS || urb->unlinked)) finish_request(sl811, ep, urb, urbstat); } diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index 1381275d448..db800a434b8 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -645,12 +645,12 @@ static void u132_hcd_interrupt_recv(void *data, struct urb *urb, u8 *buf, u132_hcd_giveback_urb(u132, endp, urb, -EINTR); return; } else if (u132->going > 0) { - dev_err(&u132->platform_dev->dev, "device is being removed urb=" - "%p status=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "device is being removed " + "urb=%p\n", urb); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, -ENODEV); return; - } else if (urb->status == -EINPROGRESS) { + } else if (!urb->unlinked) { struct u132_ring *ring = endp->ring; u8 *u = urb->transfer_buffer + urb->actual_length; u8 *b = buf; @@ -716,8 +716,8 @@ static void u132_hcd_interrupt_recv(void *data, struct urb *urb, u8 *buf, return; } } else { - dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu" - "s=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " + "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, urb->status); return; @@ -744,12 +744,12 @@ static void u132_hcd_bulk_output_sent(void *data, struct urb *urb, u8 *buf, u132_hcd_giveback_urb(u132, endp, urb, -EINTR); return; } else if (u132->going > 0) { - dev_err(&u132->platform_dev->dev, "device is being removed urb=" - "%p status=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "device is being removed " + "urb=%p\n", urb); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, -ENODEV); return; - } else if (urb->status == -EINPROGRESS) { + } else if (!urb->unlinked) { struct u132_ring *ring = endp->ring; urb->actual_length += len; endp->toggle_bits = toggle_bits; @@ -768,8 +768,8 @@ static void u132_hcd_bulk_output_sent(void *data, struct urb *urb, u8 *buf, return; } } else { - dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu" - "s=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " + "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, urb->status); return; @@ -797,12 +797,12 @@ static void u132_hcd_bulk_input_recv(void *data, struct urb *urb, u8 *buf, u132_hcd_giveback_urb(u132, endp, urb, -EINTR); return; } else if (u132->going > 0) { - dev_err(&u132->platform_dev->dev, "device is being removed urb=" - "%p status=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "device is being removed " + "urb=%p\n", urb); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, -ENODEV); return; - } else if (urb->status == -EINPROGRESS) { + } else if (!urb->unlinked) { struct u132_ring *ring = endp->ring; u8 *u = urb->transfer_buffer + urb->actual_length; u8 *b = buf; @@ -871,8 +871,8 @@ static void u132_hcd_bulk_input_recv(void *data, struct urb *urb, u8 *buf, return; } } else { - dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu" - "s=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " + "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, urb->status); return; @@ -898,18 +898,18 @@ static void u132_hcd_configure_empty_sent(void *data, struct urb *urb, u8 *buf, u132_hcd_giveback_urb(u132, endp, urb, -EINTR); return; } else if (u132->going > 0) { - dev_err(&u132->platform_dev->dev, "device is being removed urb=" - "%p status=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "device is being removed " + "urb=%p\n", urb); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, -ENODEV); return; - } else if (urb->status == -EINPROGRESS) { + } else if (!urb->unlinked) { up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, 0); return; } else { - dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu" - "s=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " + "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, urb->status); return; @@ -936,12 +936,12 @@ static void u132_hcd_configure_input_recv(void *data, struct urb *urb, u8 *buf, u132_hcd_giveback_urb(u132, endp, urb, -EINTR); return; } else if (u132->going > 0) { - dev_err(&u132->platform_dev->dev, "device is being removed urb=" - "%p status=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "device is being removed " + "urb=%p\n", urb); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, -ENODEV); return; - } else if (urb->status == -EINPROGRESS) { + } else if (!urb->unlinked) { struct u132_ring *ring = endp->ring; u8 *u = urb->transfer_buffer; u8 *b = buf; @@ -980,8 +980,8 @@ static void u132_hcd_configure_input_recv(void *data, struct urb *urb, u8 *buf, return; } } else { - dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu" - "s=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " + "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, urb->status); return; @@ -1007,18 +1007,18 @@ static void u132_hcd_configure_empty_recv(void *data, struct urb *urb, u8 *buf, u132_hcd_giveback_urb(u132, endp, urb, -EINTR); return; } else if (u132->going > 0) { - dev_err(&u132->platform_dev->dev, "device is being removed urb=" - "%p status=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "device is being removed " + "urb=%p\n", urb); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, -ENODEV); return; - } else if (urb->status == -EINPROGRESS) { + } else if (!urb->unlinked) { up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, 0); return; } else { - dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu" - "s=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " + "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, urb->status); return; @@ -1045,12 +1045,12 @@ static void u132_hcd_configure_setup_sent(void *data, struct urb *urb, u8 *buf, u132_hcd_giveback_urb(u132, endp, urb, -EINTR); return; } else if (u132->going > 0) { - dev_err(&u132->platform_dev->dev, "device is being removed urb=" - "%p status=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "device is being removed " + "urb=%p\n", urb); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, -ENODEV); return; - } else if (urb->status == -EINPROGRESS) { + } else if (!urb->unlinked) { if (usb_pipein(urb->pipe)) { int retval; struct u132_ring *ring = endp->ring; @@ -1077,8 +1077,8 @@ static void u132_hcd_configure_setup_sent(void *data, struct urb *urb, u8 *buf, return; } } else { - dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu" - "s=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " + "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, urb->status); return; @@ -1106,20 +1106,20 @@ static void u132_hcd_enumeration_empty_recv(void *data, struct urb *urb, u132_hcd_giveback_urb(u132, endp, urb, -EINTR); return; } else if (u132->going > 0) { - dev_err(&u132->platform_dev->dev, "device is being removed urb=" - "%p status=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "device is being removed " + "urb=%p\n", urb); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, -ENODEV); return; - } else if (urb->status == -EINPROGRESS) { + } else if (!urb->unlinked) { u132->addr[0].address = 0; endp->usb_addr = udev->usb_addr; up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, 0); return; } else { - dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu" - "s=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " + "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, urb->status); return; @@ -1145,12 +1145,12 @@ static void u132_hcd_enumeration_address_sent(void *data, struct urb *urb, u132_hcd_giveback_urb(u132, endp, urb, -EINTR); return; } else if (u132->going > 0) { - dev_err(&u132->platform_dev->dev, "device is being removed urb=" - "%p status=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "device is being removed " + "urb=%p\n", urb); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, -ENODEV); return; - } else if (urb->status == -EINPROGRESS) { + } else if (!urb->unlinked) { int retval; struct u132_ring *ring = endp->ring; up(&u132->scheduler_lock); @@ -1162,8 +1162,8 @@ static void u132_hcd_enumeration_address_sent(void *data, struct urb *urb, u132_hcd_giveback_urb(u132, endp, urb, retval); return; } else { - dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu" - "s=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " + "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, urb->status); return; @@ -1189,18 +1189,18 @@ static void u132_hcd_initial_empty_sent(void *data, struct urb *urb, u8 *buf, u132_hcd_giveback_urb(u132, endp, urb, -EINTR); return; } else if (u132->going > 0) { - dev_err(&u132->platform_dev->dev, "device is being removed urb=" - "%p status=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "device is being removed " + "urb=%p\n", urb); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, -ENODEV); return; - } else if (urb->status == -EINPROGRESS) { + } else if (!urb->unlinked) { up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, 0); return; } else { - dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu" - "s=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " + "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, urb->status); return; @@ -1227,12 +1227,12 @@ static void u132_hcd_initial_input_recv(void *data, struct urb *urb, u8 *buf, u132_hcd_giveback_urb(u132, endp, urb, -EINTR); return; } else if (u132->going > 0) { - dev_err(&u132->platform_dev->dev, "device is being removed urb=" - "%p status=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "device is being removed " + "urb=%p\n", urb); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, -ENODEV); return; - } else if (urb->status == -EINPROGRESS) { + } else if (!urb->unlinked) { int retval; struct u132_ring *ring = endp->ring; u8 *u = urb->transfer_buffer; @@ -1251,8 +1251,8 @@ static void u132_hcd_initial_input_recv(void *data, struct urb *urb, u8 *buf, u132_hcd_giveback_urb(u132, endp, urb, retval); return; } else { - dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu" - "s=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " + "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, urb->status); return; @@ -1279,12 +1279,12 @@ static void u132_hcd_initial_setup_sent(void *data, struct urb *urb, u8 *buf, u132_hcd_giveback_urb(u132, endp, urb, -EINTR); return; } else if (u132->going > 0) { - dev_err(&u132->platform_dev->dev, "device is being removed urb=" - "%p status=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "device is being removed " + "urb=%p\n", urb); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, -ENODEV); return; - } else if (urb->status == -EINPROGRESS) { + } else if (!urb->unlinked) { int retval; struct u132_ring *ring = endp->ring; up(&u132->scheduler_lock); @@ -1296,8 +1296,8 @@ static void u132_hcd_initial_setup_sent(void *data, struct urb *urb, u8 *buf, u132_hcd_giveback_urb(u132, endp, urb, retval); return; } else { - dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu" - "s=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " + "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); u132_hcd_giveback_urb(u132, endp, urb, urb->status); return; @@ -2279,8 +2279,8 @@ static int u132_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, , u132->going); return -ENODEV; } else if (u132->going > 0) { - dev_err(&u132->platform_dev->dev, "device is being removed urb=" - "%p status=%d\n", urb, urb->status); + dev_err(&u132->platform_dev->dev, "device is being removed " + "urb=%p\n", urb); return -ESHUTDOWN; } else { u8 usb_addr = usb_pipedevice(urb->pipe); diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index 1497371583b..20cc58b9780 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -120,8 +120,8 @@ static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space) out += sprintf(out, "%s%s", ptype, (urbp->fsbr ? " FSBR" : "")); out += sprintf(out, " Actlen=%d", urbp->urb->actual_length); - if (urbp->urb->status != -EINPROGRESS) - out += sprintf(out, " Status=%d", urbp->urb->status); + if (urbp->urb->unlinked) + out += sprintf(out, " Unlinked=%d", urbp->urb->unlinked); out += sprintf(out, "\n"); i = nactive = ninactive = 0; diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index fbc3af392c2..bab56726655 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -1557,15 +1557,12 @@ static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) break; spin_lock(&urb->lock); - if (urb->status == -EINPROGRESS) /* Not dequeued */ - urb->status = status; - else - status = ECONNRESET; /* Not -ECONNRESET */ + urb->status = status; spin_unlock(&urb->lock); /* Dequeued but completed URBs can't be given back unless * the QH is stopped or has finished unlinking. */ - if (status == ECONNRESET) { + if (urb->unlinked) { if (QH_FINISHED_UNLINKING(qh)) qh->is_stopped = 1; else if (!qh->is_stopped) @@ -1588,7 +1585,7 @@ static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) restart: list_for_each_entry(urbp, &qh->queue, node) { urb = urbp->urb; - if (urb->status != -EINPROGRESS) { + if (urb->unlinked) { /* Fix up the TD links and save the toggles for * non-Isochronous queues. For Isochronous queues, -- cgit v1.2.3 From 6e8fe43b26085a64327d1cbb751ab895f3ad3f5d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 22 Aug 2007 13:08:40 -0400 Subject: USB: avoid the donelist after an error in ohci-hcd This patch (as972) changes ohci-hcd so that after an error occurs, the remaining TDs for the URB will be skipped over entirely instead of going through the donelist. This enables the driver to give back the URB as soon as the error is detected, avoiding the need to store the error status in urb->status. Signed-off-by: Alan Stern CC: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-q.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 3c793fad178..860e55ff67a 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -783,10 +783,10 @@ static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td) /*-------------------------------------------------------------------------*/ -static inline struct td * -ed_halted (struct ohci_hcd *ohci, struct td *td, int cc, struct td *rev) +static void ed_halted(struct ohci_hcd *ohci, struct td *td, int cc) { struct urb *urb = td->urb; + urb_priv_t *urb_priv = urb->hcpriv; struct ed *ed = td->ed; struct list_head *tmp = td->td_list.next; __hc32 toggle = ed->hwHeadP & cpu_to_hc32 (ohci, ED_C); @@ -798,13 +798,12 @@ ed_halted (struct ohci_hcd *ohci, struct td *td, int cc, struct td *rev) wmb (); ed->hwHeadP &= ~cpu_to_hc32 (ohci, ED_H); - /* put any later tds from this urb onto the donelist, after 'td', - * order won't matter here: no errors, and nothing was transferred. - * also patch the ed so it looks as if those tds completed normally. + /* Get rid of all later tds from this urb. We don't have + * to be careful: no errors and nothing was transferred. + * Also patch the ed so it looks as if those tds completed normally. */ while (tmp != &ed->td_list) { struct td *next; - __hc32 info; next = list_entry (tmp, struct td, td_list); tmp = next->td_list.next; @@ -819,14 +818,9 @@ ed_halted (struct ohci_hcd *ohci, struct td *td, int cc, struct td *rev) * then we need to leave the control STATUS packet queued * and clear ED_SKIP. */ - info = next->hwINFO; - info |= cpu_to_hc32 (ohci, TD_DONE); - info &= ~cpu_to_hc32 (ohci, TD_CC); - next->hwINFO = info; - - next->next_dl_td = rev; - rev = next; + list_del(&next->td_list); + urb_priv->td_cnt++; ed->hwHeadP = next->hwNextTD | toggle; } @@ -852,8 +846,6 @@ ed_halted (struct ohci_hcd *ohci, struct td *td, int cc, struct td *rev) hc32_to_cpu (ohci, td->hwINFO), cc, cc_to_error [cc]); } - - return rev; } /* replies to the request have to be on a FIFO basis so @@ -890,7 +882,7 @@ static struct td *dl_reverse_done_list (struct ohci_hcd *ohci) */ if (cc != TD_CC_NOERROR && (td->ed->hwHeadP & cpu_to_hc32 (ohci, ED_H))) - td_rev = ed_halted (ohci, td, cc, td_rev); + ed_halted(ohci, td, cc); td->next_dl_td = td_rev; td_rev = td; -- cgit v1.2.3 From 14c04c0f88f228fee1f412be91d6edcb935c78aa Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 24 Aug 2007 15:40:19 -0400 Subject: USB: reorganize urb->status use in ehci-hcd This patch (as974) reorganizes the way ehci-hcd sets urb->status. It now keeps the information in a local variable until the last moment. The patch also simplifies the handling of -EREMOTEIO, since the only use of that code is to set the do_status flag. Signed-off-by: Alan Stern CC: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-q.c | 62 +++++++++++++++++++++++-------------------- drivers/usb/host/ehci-sched.c | 4 +-- 2 files changed, 35 insertions(+), 31 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index a8f5408c161..794d27e0780 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -139,63 +139,65 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh) /*-------------------------------------------------------------------------*/ -static void qtd_copy_status ( +static int qtd_copy_status ( struct ehci_hcd *ehci, struct urb *urb, size_t length, u32 token ) { + int status = -EINPROGRESS; + /* count IN/OUT bytes, not SETUP (even short packets) */ if (likely (QTD_PID (token) != 2)) urb->actual_length += length - QTD_LENGTH (token); /* don't modify error codes */ if (unlikely(urb->unlinked)) - return; + return status; /* force cleanup after short read; not always an error */ if (unlikely (IS_SHORT_READ (token))) - urb->status = -EREMOTEIO; + status = -EREMOTEIO; /* serious "can't proceed" faults reported by the hardware */ if (token & QTD_STS_HALT) { if (token & QTD_STS_BABBLE) { /* FIXME "must" disable babbling device's port too */ - urb->status = -EOVERFLOW; + status = -EOVERFLOW; } else if (token & QTD_STS_MMF) { /* fs/ls interrupt xfer missed the complete-split */ - urb->status = -EPROTO; + status = -EPROTO; } else if (token & QTD_STS_DBE) { - urb->status = (QTD_PID (token) == 1) /* IN ? */ + status = (QTD_PID (token) == 1) /* IN ? */ ? -ENOSR /* hc couldn't read data */ : -ECOMM; /* hc couldn't write data */ } else if (token & QTD_STS_XACT) { /* timeout, bad crc, wrong PID, etc; retried */ if (QTD_CERR (token)) - urb->status = -EPIPE; + status = -EPIPE; else { ehci_dbg (ehci, "devpath %s ep%d%s 3strikes\n", urb->dev->devpath, usb_pipeendpoint (urb->pipe), usb_pipein (urb->pipe) ? "in" : "out"); - urb->status = -EPROTO; + status = -EPROTO; } /* CERR nonzero + no errors + halt --> stall */ } else if (QTD_CERR (token)) - urb->status = -EPIPE; + status = -EPIPE; else /* unknown */ - urb->status = -EPROTO; + status = -EPROTO; ehci_vdbg (ehci, "dev%d ep%d%s qtd token %08x --> status %d\n", usb_pipedevice (urb->pipe), usb_pipeendpoint (urb->pipe), usb_pipein (urb->pipe) ? "in" : "out", - token, urb->status); + token, status); /* if async CSPLIT failed, try cleaning out the TT buffer */ - if (urb->status != -EPIPE + if (status != -EPIPE && urb->dev->tt && !usb_pipeint (urb->pipe) && ((token & QTD_STS_MMF) != 0 || QTD_CERR(token) == 0) @@ -212,10 +214,12 @@ static void qtd_copy_status ( usb_hub_tt_clear_buffer (urb->dev, urb->pipe); } } + + return status; } static void -ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb) +ehci_urb_done(struct ehci_hcd *ehci, struct urb *urb, int status) __releases(ehci->lock) __acquires(ehci->lock) { @@ -231,17 +235,13 @@ __acquires(ehci->lock) qh_put (qh); } - spin_lock (&urb->lock); if (unlikely(urb->unlinked)) { COUNT(ehci->stats.unlink); } else { - if (likely(urb->status == -EINPROGRESS || - (urb->status == -EREMOTEIO && - !(urb->transfer_flags & URB_SHORT_NOT_OK)))) - urb->status = 0; + if (likely(status == -EINPROGRESS)) + status = 0; COUNT(ehci->stats.complete); } - spin_unlock (&urb->lock); #ifdef EHCI_URB_TRACE ehci_dbg (ehci, @@ -249,13 +249,14 @@ __acquires(ehci->lock) __FUNCTION__, urb->dev->devpath, urb, usb_pipeendpoint (urb->pipe), usb_pipein (urb->pipe) ? "in" : "out", - urb->status, + status, urb->actual_length, urb->transfer_buffer_length); #endif /* complete() can reenter this HCD */ usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); spin_unlock (&ehci->lock); + urb->status = status; usb_hcd_giveback_urb (ehci_to_hcd(ehci), urb); spin_lock (&ehci->lock); } @@ -276,6 +277,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) { struct ehci_qtd *last = NULL, *end = qh->dummy; struct list_head *entry, *tmp; + int last_status = -EINPROGRESS; int stopped; unsigned count = 0; int do_status = 0; @@ -304,6 +306,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) struct ehci_qtd *qtd; struct urb *urb; u32 token = 0; + int qtd_status; qtd = list_entry (entry, struct ehci_qtd, qtd_list); urb = qtd->urb; @@ -311,11 +314,12 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) /* 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, last_status); count++; } ehci_qtd_free (ehci, last); last = NULL; + last_status = -EINPROGRESS; } /* ignore urbs submitted during completions we reported */ @@ -351,13 +355,13 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) stopped = 1; if (unlikely (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))) - urb->status = -ESHUTDOWN; + last_status = -ESHUTDOWN; /* ignore active urbs unless some previous qtd * for the urb faulted (including short read) or * its urb was canceled. we may patch qh or qtds. */ - if (likely(urb->status == -EINPROGRESS && + if (likely(last_status == -EINPROGRESS && !urb->unlinked)) continue; @@ -386,14 +390,14 @@ halt: } /* remove it from the queue */ - spin_lock (&urb->lock); - qtd_copy_status (ehci, urb, qtd->length, token); - if (unlikely(urb->status == -EREMOTEIO)) { + qtd_status = qtd_copy_status(ehci, urb, qtd->length, token); + if (unlikely(qtd_status == -EREMOTEIO)) { do_status = (!urb->unlinked && usb_pipecontrol(urb->pipe)); - urb->status = 0; + qtd_status = 0; } - spin_unlock (&urb->lock); + if (likely(last_status == -EINPROGRESS)) + last_status = qtd_status; if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { last = list_entry (qtd->qtd_list.prev, @@ -406,7 +410,7 @@ halt: /* last urb's completion might still need calling */ if (likely (last != NULL)) { - ehci_urb_done (ehci, last->urb); + ehci_urb_done(ehci, last->urb, last_status); count++; ehci_qtd_free (ehci, last); } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 8b267b3fd2b..80d99bce2b3 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1627,7 +1627,7 @@ itd_complete ( /* give urb back to the driver ... can be out-of-order */ dev = urb->dev; - ehci_urb_done (ehci, urb); + ehci_urb_done(ehci, urb, 0); urb = NULL; /* defer stopping schedule; completion can submit */ @@ -2000,7 +2000,7 @@ sitd_complete ( /* give urb back to the driver */ dev = urb->dev; - ehci_urb_done (ehci, urb); + ehci_urb_done(ehci, urb, 0); urb = NULL; /* defer stopping schedule; completion can submit */ -- cgit v1.2.3 From 55d8496837cf124f68656e4242a5e20eb592fd54 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 24 Aug 2007 15:40:34 -0400 Subject: USB: reorganize urb->status use in ohci-hcd This patch (as975) reorganizes the way ohci-hcd sets urb->status. It now keeps the information in a local variable until the last moment. Signed-off-by: Alan Stern CC: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-dbg.c | 8 ++++---- drivers/usb/host/ohci-hcd.c | 15 +++++++-------- drivers/usb/host/ohci-q.c | 38 +++++++++++++++++--------------------- 3 files changed, 28 insertions(+), 33 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index f61c6cdd06f..ebab5ce8f5c 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -24,7 +24,7 @@ * small: 0) header + data packets 1) just header */ static void __maybe_unused -urb_print (struct urb * urb, char * str, int small) +urb_print(struct urb * urb, char * str, int small, int status) { unsigned int pipe= urb->pipe; @@ -34,7 +34,7 @@ urb_print (struct urb * urb, char * str, int small) } #ifndef OHCI_VERBOSE_DEBUG - if (urb->status != 0) + if (status != 0) #endif dbg("%s %p dev=%d ep=%d%s-%s flags=%x len=%d/%d stat=%d", str, @@ -46,7 +46,7 @@ urb_print (struct urb * urb, char * str, int small) urb->transfer_flags, urb->actual_length, urb->transfer_buffer_length, - urb->status); + status); #ifdef OHCI_VERBOSE_DEBUG if (!small) { @@ -66,7 +66,7 @@ urb_print (struct urb * urb, char * str, int small) urb->transfer_buffer_length: urb->actual_length; for (i = 0; i < 16 && i < len; i++) printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]); - printk ("%s stat:%d\n", i < len? "...": "", urb->status); + printk ("%s stat:%d\n", i < len? "...": "", status); } } #endif diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 6b06ab69938..f7c6ced2bc0 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -129,7 +129,7 @@ static int ohci_urb_enqueue ( int retval = 0; #ifdef OHCI_VERBOSE_DEBUG - urb_print (urb, "SUB", usb_pipein (pipe)); + urb_print(urb, "SUB", usb_pipein(pipe), -EINPROGRESS); #endif /* every endpoint has a ed, locate and maybe (re)initialize it */ @@ -240,8 +240,8 @@ fail: } /* - * decouple the URB from the HC queues (TDs, urb_priv); it's - * already marked using urb->status. reporting is always done + * decouple the URB from the HC queues (TDs, urb_priv). + * reporting is always done * asynchronously, and we might be dealing with an urb that's * partially transferred, or an ED with other urbs being unlinked. */ @@ -252,7 +252,7 @@ static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) int rc; #ifdef OHCI_VERBOSE_DEBUG - urb_print (urb, "UNLINK", 1); + urb_print(urb, "UNLINK", 1, status); #endif spin_lock_irqsave (&ohci->lock, flags); @@ -277,7 +277,7 @@ static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) * any more ... just clean up every urb's memory. */ if (urb->hcpriv) - finish_urb (ohci, urb); + finish_urb(ohci, urb, status); } spin_unlock_irqrestore (&ohci->lock, flags); return rc; @@ -927,9 +927,8 @@ static int ohci_restart (struct ohci_hcd *ohci) ed, ed->state); } - spin_lock (&urb->lock); - urb->status = -ESHUTDOWN; - spin_unlock (&urb->lock); + if (!urb->unlinked) + urb->unlinked = -ESHUTDOWN; } finish_unlinks (ohci, 0); spin_unlock_irq(&ohci->lock); diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 860e55ff67a..13d31edd1a8 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -36,18 +36,15 @@ static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv) * PRECONDITION: ohci lock held, irqs blocked. */ static void -finish_urb (struct ohci_hcd *ohci, struct urb *urb) +finish_urb(struct ohci_hcd *ohci, struct urb *urb, int status) __releases(ohci->lock) __acquires(ohci->lock) { // ASSERT (urb->hcpriv != 0); urb_free_priv (ohci, urb->hcpriv); - - spin_lock (&urb->lock); - if (likely (urb->status == -EINPROGRESS)) - urb->status = 0; - spin_unlock (&urb->lock); + if (likely(status == -EINPROGRESS)) + status = 0; switch (usb_pipetype (urb->pipe)) { case PIPE_ISOCHRONOUS: @@ -59,12 +56,13 @@ __acquires(ohci->lock) } #ifdef OHCI_VERBOSE_DEBUG - urb_print (urb, "RET", usb_pipeout (urb->pipe)); + urb_print(urb, "RET", usb_pipeout (urb->pipe), status); #endif /* urb->complete() can reenter this HCD */ usb_hcd_unlink_urb_from_ep(ohci_to_hcd(ohci), urb); spin_unlock (&ohci->lock); + urb->status = status; usb_hcd_giveback_urb (ohci_to_hcd(ohci), urb); spin_lock (&ohci->lock); @@ -702,19 +700,18 @@ static void td_submit_urb ( * Done List handling functions *-------------------------------------------------------------------------*/ -/* calculate transfer length/status and update the urb - * PRECONDITION: irqsafe (only for urb->status locking) - */ -static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td) +/* calculate transfer length/status and update the urb */ +static int td_done(struct ohci_hcd *ohci, struct urb *urb, struct td *td) { u32 tdINFO = hc32_to_cpup (ohci, &td->hwINFO); int cc = 0; + int status = -EINPROGRESS; list_del (&td->td_list); /* ISO ... drivers see per-TD length/status */ if (tdINFO & TD_ISO) { - u16 tdPSW = ohci_hwPSW (ohci, td, 0); + u16 tdPSW = ohci_hwPSW(ohci, td, 0); int dlen = 0; /* NOTE: assumes FC in tdINFO == 0, and that @@ -723,7 +720,7 @@ static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td) cc = (tdPSW >> 12) & 0xF; if (tdINFO & TD_CC) /* hc didn't touch? */ - return; + return status; if (usb_pipeout (urb->pipe)) dlen = urb->iso_frame_desc [td->index].length; @@ -756,11 +753,8 @@ static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td) if (cc == TD_DATAUNDERRUN && !(urb->transfer_flags & URB_SHORT_NOT_OK)) cc = TD_CC_NOERROR; - if (cc != TD_CC_NOERROR && cc < 0x0E) { - spin_lock (&urb->lock); - urb->status = cc_to_error[cc]; - spin_unlock (&urb->lock); - } + if (cc != TD_CC_NOERROR && cc < 0x0E) + status = cc_to_error[cc]; /* count all non-empty packets except control SETUP packet */ if ((type != PIPE_CONTROL || td->index != 0) && tdBE != 0) { @@ -779,6 +773,7 @@ static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td) urb->actual_length, urb->transfer_buffer_length); } + return status; } /*-------------------------------------------------------------------------*/ @@ -979,7 +974,7 @@ rescan_this: /* if URB is done, clean up */ if (urb_priv->td_cnt == urb_priv->length) { modified = completed = 1; - finish_urb (ohci, urb); + finish_urb(ohci, urb, 0); } } if (completed && !list_empty (&ed->td_list)) @@ -1062,14 +1057,15 @@ static void takeback_td(struct ohci_hcd *ohci, struct td *td) struct urb *urb = td->urb; urb_priv_t *urb_priv = urb->hcpriv; struct ed *ed = td->ed; + int status; /* update URB's length and status from TD */ - td_done(ohci, urb, td); + status = td_done(ohci, urb, td); urb_priv->td_cnt++; /* If all this urb's TDs are done, call complete() */ if (urb_priv->td_cnt == urb_priv->length) - finish_urb(ohci, urb); + finish_urb(ohci, urb, status); /* clean schedule: unlink EDs that are no longer busy */ if (list_empty(&ed->td_list)) { -- cgit v1.2.3 From 65e51098d9094c7e840b6d6291867b95538d9442 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 24 Aug 2007 15:40:47 -0400 Subject: USB: reorganize urb->status use in sl811-hcd This patch (as976) reorganizes the way sl811-hcd sets urb->status. It now keeps the information in a local variable until the last moment. The patch also improves the handling of faults during the status stage of a control transfer, since it no longer needs to retain the error information from the earlier stages. Signed-off-by: Alan Stern CC: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/sl811-hcd.c | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index f0fa94148d9..515152809d3 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -435,11 +435,8 @@ static void finish_request( if (usb_pipecontrol(urb->pipe)) ep->nextpid = USB_PID_SETUP; - spin_lock(&urb->lock); - urb->status = status; - spin_unlock(&urb->lock); - usb_hcd_unlink_urb_from_ep(sl811_to_hcd(sl811), urb); + urb->status = status; spin_unlock(&sl811->lock); usb_hcd_giveback_urb(sl811_to_hcd(sl811), urb); spin_lock(&sl811->lock); @@ -537,27 +534,20 @@ done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank) bank + SL11H_XFERCNTREG); if (len > ep->length) { len = ep->length; - urb->status = -EOVERFLOW; + urbstat = -EOVERFLOW; } urb->actual_length += len; sl811_read_buf(sl811, SL811HS_PACKET_BUF(bank == 0), buf, len); usb_dotoggle(udev, ep->epnum, 0); - if (urb->actual_length == urb->transfer_buffer_length - || len < ep->maxpacket) - urbstat = 0; - if (usb_pipecontrol(urb->pipe) && urbstat == 0) { - - /* NOTE if the status stage STALLs (why?), - * this reports the wrong urb status. - */ - spin_lock(&urb->lock); - if (urb->status == -EINPROGRESS) - urb->status = urbstat; - spin_unlock(&urb->lock); - - urb = NULL; - ep->nextpid = USB_PID_ACK; + if (urbstat == -EINPROGRESS && + (len < ep->maxpacket || + urb->actual_length == + urb->transfer_buffer_length)) { + if (usb_pipecontrol(urb->pipe)) + ep->nextpid = USB_PID_ACK; + else + urbstat = 0; } break; case USB_PID_SETUP: @@ -597,7 +587,7 @@ done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank) bank, status, ep, urbstat); } - if (urb && (urbstat != -EINPROGRESS || urb->unlinked)) + if (urbstat != -EINPROGRESS || urb->unlinked) finish_request(sl811, ep, urb, urbstat); } -- cgit v1.2.3 From 888fda47051716765175d0008450126c837adb32 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 24 Aug 2007 15:41:18 -0400 Subject: USB: reorganize urb->status use in r8a66597-hcd This patch (as977) reorganizes the way r8a66597-hcd sets urb->status. It now keeps the information in a local variable until the last moment. Parts of this patch were written by Yoshihiro Shimoda. Signed-off-by: Alan Stern CC: Yoshihiro Shimoda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/r8a66597-hcd.c | 61 ++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 31 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 98b9e054754..fea6036771f 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -1109,7 +1109,7 @@ static void set_td_timer(struct r8a66597 *r8a66597, struct r8a66597_td *td) /* this function must be called with interrupt disabled */ static void finish_request(struct r8a66597 *r8a66597, struct r8a66597_td *td, - u16 pipenum, struct urb *urb) + u16 pipenum, struct urb *urb, int status) __releases(r8a66597->lock) __acquires(r8a66597->lock) { int restart = 0; @@ -1118,7 +1118,7 @@ __releases(r8a66597->lock) __acquires(r8a66597->lock) r8a66597->timeout_map &= ~(1 << pipenum); if (likely(td)) { - if (td->set_address && (urb->status != 0 || urb->unlinked)) + if (td->set_address && (status != 0 || urb->unlinked)) r8a66597->address_map &= ~(1 << urb->setup_packet[2]); pipe_toggle_save(r8a66597, td->pipe, urb); @@ -1135,6 +1135,7 @@ __releases(r8a66597->lock) __acquires(r8a66597->lock) usb_hcd_unlink_urb_from_ep(r8a66597_to_hcd(r8a66597), urb); + urb->status = status; spin_unlock(&r8a66597->lock); usb_hcd_giveback_urb(hcd, urb); spin_lock(&r8a66597->lock); @@ -1167,11 +1168,10 @@ static void packet_read(struct r8a66597 *r8a66597, u16 pipenum) fifo_change_from_pipe(r8a66597, td->pipe); tmp = r8a66597_read(r8a66597, td->pipe->fifoctr); if (unlikely((tmp & FRDY) == 0)) { - urb->status = -EPIPE; pipe_stop(r8a66597, td->pipe); pipe_irq_disable(r8a66597, pipenum); err("in fifo not ready (%d)", pipenum); - finish_request(r8a66597, td, pipenum, td->urb); + finish_request(r8a66597, td, pipenum, td->urb, -EPIPE); return; } @@ -1224,10 +1224,8 @@ static void packet_read(struct r8a66597 *r8a66597, u16 pipenum) buf, size); } - if (finish && pipenum != 0) { - td->urb->status = status; - finish_request(r8a66597, td, pipenum, urb); - } + if (finish && pipenum != 0) + finish_request(r8a66597, td, pipenum, urb, status); } static void packet_write(struct r8a66597 *r8a66597, u16 pipenum) @@ -1245,11 +1243,10 @@ static void packet_write(struct r8a66597 *r8a66597, u16 pipenum) fifo_change_from_pipe(r8a66597, td->pipe); tmp = r8a66597_read(r8a66597, td->pipe->fifoctr); if (unlikely((tmp & FRDY) == 0)) { - urb->status = -EPIPE; pipe_stop(r8a66597, td->pipe); pipe_irq_disable(r8a66597, pipenum); err("out write fifo not ready. (%d)", pipenum); - finish_request(r8a66597, td, pipenum, td->urb); + finish_request(r8a66597, td, pipenum, urb, -EPIPE); return; } @@ -1294,7 +1291,7 @@ static void packet_write(struct r8a66597 *r8a66597, u16 pipenum) } -static void check_next_phase(struct r8a66597 *r8a66597) +static void check_next_phase(struct r8a66597 *r8a66597, int status) { struct r8a66597_td *td = r8a66597_get_td(r8a66597, 0); struct urb *urb; @@ -1320,28 +1317,28 @@ static void check_next_phase(struct r8a66597 *r8a66597) break; case USB_PID_ACK: finish = 1; - urb->status = 0; break; } - if (finish || urb->unlinked) - finish_request(r8a66597, td, 0, urb); + if (finish || status != 0 || urb->unlinked) + finish_request(r8a66597, td, 0, urb, status); else start_transfer(r8a66597, td); } -static void set_urb_error(struct r8a66597 *r8a66597, u16 pipenum) +static int get_urb_error(struct r8a66597 *r8a66597, u16 pipenum) { struct r8a66597_td *td = r8a66597_get_td(r8a66597, pipenum); - if (td && td->urb) { + if (td) { u16 pid = r8a66597_read(r8a66597, td->pipe->pipectr) & PID; if (pid == PID_NAK) - td->urb->status = -ECONNRESET; + return -ECONNRESET; else - td->urb->status = -EPIPE; + return -EPIPE; } + return 0; } static void irq_pipe_ready(struct r8a66597 *r8a66597) @@ -1360,7 +1357,7 @@ static void irq_pipe_ready(struct r8a66597 *r8a66597) packet_read(r8a66597, 0); else pipe_irq_disable(r8a66597, 0); - check_next_phase(r8a66597); + check_next_phase(r8a66597, 0); } for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) { @@ -1394,7 +1391,7 @@ static void irq_pipe_empty(struct r8a66597 *r8a66597) td = r8a66597_get_td(r8a66597, 0); if (td && td->type != USB_PID_OUT) disable_irq_empty(r8a66597, 0); - check_next_phase(r8a66597); + check_next_phase(r8a66597, 0); } for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) { @@ -1409,8 +1406,8 @@ static void irq_pipe_empty(struct r8a66597 *r8a66597) if ((tmp & INBUFM) == 0) { disable_irq_empty(r8a66597, pipenum); pipe_irq_disable(r8a66597, pipenum); - td->urb->status = 0; - finish_request(r8a66597, td, pipenum, td->urb); + finish_request(r8a66597, td, pipenum, td->urb, + 0); } } } @@ -1421,15 +1418,16 @@ static void irq_pipe_nrdy(struct r8a66597 *r8a66597) u16 check; u16 pipenum; u16 mask; + int status; mask = r8a66597_read(r8a66597, NRDYSTS) & r8a66597_read(r8a66597, NRDYENB); r8a66597_write(r8a66597, ~mask, NRDYSTS); if (mask & NRDY0) { cfifo_change(r8a66597, 0); - set_urb_error(r8a66597, 0); + status = get_urb_error(r8a66597, 0); pipe_irq_disable(r8a66597, 0); - check_next_phase(r8a66597); + check_next_phase(r8a66597, status); } for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) { @@ -1440,10 +1438,10 @@ static void irq_pipe_nrdy(struct r8a66597 *r8a66597) if (unlikely(!td)) continue; - set_urb_error(r8a66597, pipenum); + status = get_urb_error(r8a66597, pipenum); pipe_irq_disable(r8a66597, pipenum); pipe_stop(r8a66597, td->pipe); - finish_request(r8a66597, td, pipenum, td->urb); + finish_request(r8a66597, td, pipenum, td->urb, status); } } } @@ -1463,6 +1461,7 @@ static irqreturn_t r8a66597_irq(struct usb_hcd *hcd) u16 intsts0, intsts1, intsts2; u16 intenb0, intenb1, intenb2; u16 mask0, mask1, mask2; + int status; spin_lock(&r8a66597->lock); @@ -1506,12 +1505,12 @@ static irqreturn_t r8a66597_irq(struct usb_hcd *hcd) } if (mask1 & SIGN) { r8a66597_write(r8a66597, ~SIGN, INTSTS1); - set_urb_error(r8a66597, 0); - check_next_phase(r8a66597); + status = get_urb_error(r8a66597, 0); + check_next_phase(r8a66597, status); } if (mask1 & SACK) { r8a66597_write(r8a66597, ~SACK, INTSTS1); - check_next_phase(r8a66597); + check_next_phase(r8a66597, 0); } } if (mask0) { @@ -1790,7 +1789,7 @@ static int r8a66597_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, pipe_stop(r8a66597, td->pipe); pipe_irq_disable(r8a66597, td->pipenum); disable_irq_empty(r8a66597, td->pipenum); - finish_request(r8a66597, td, td->pipenum, urb); + finish_request(r8a66597, td, td->pipenum, urb, status); } done: spin_unlock_irqrestore(&r8a66597->lock, flags); @@ -1824,7 +1823,7 @@ static void r8a66597_endpoint_disable(struct usb_hcd *hcd, td = r8a66597_get_td(r8a66597, pipenum); if (td) urb = td->urb; - finish_request(r8a66597, td, pipenum, urb); + finish_request(r8a66597, td, pipenum, urb, -ESHUTDOWN); kfree(hep->hcpriv); hep->hcpriv = NULL; spin_unlock_irqrestore(&r8a66597->lock, flags); -- cgit v1.2.3 From 4a00027dcb088bf90fa8fb14a7e8ba3506d78f22 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 24 Aug 2007 15:42:24 -0400 Subject: USB: Eliminate urb->status usage! This patch (as979) removes the last vestiges of urb->status from the host controller drivers and the root-hub emulator. Now the field doesn't get set until just before the URB's completion routine is called. Signed-off-by: Alan Stern CC: David Brownell CC: Olav Kongas CC: Yoshihiro Shimoda CC: Tony Olech Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-q.c | 3 +-- drivers/usb/host/isp116x-hcd.c | 15 +++++---------- drivers/usb/host/ohci-q.c | 3 +-- drivers/usb/host/r8a66597-hcd.c | 8 +++----- drivers/usb/host/sl811-hcd.c | 3 +-- drivers/usb/host/u132-hcd.c | 36 +++++++++++++++++------------------- drivers/usb/host/uhci-hcd.h | 15 --------------- drivers/usb/host/uhci-q.c | 12 ++++-------- 8 files changed, 32 insertions(+), 63 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 794d27e0780..b10f39c047e 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -256,8 +256,7 @@ __acquires(ehci->lock) /* complete() can reenter this HCD */ usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); spin_unlock (&ehci->lock); - urb->status = status; - usb_hcd_giveback_urb (ehci_to_hcd(ehci), urb); + usb_hcd_giveback_urb(ehci_to_hcd(ehci), urb, status); spin_lock (&ehci->lock); } diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 35b3507ff40..c27417f5b9d 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -277,7 +277,7 @@ static void preproc_atl_queue(struct isp116x *isp116x) processed urbs. */ static void finish_request(struct isp116x *isp116x, struct isp116x_ep *ep, - struct urb *urb) + struct urb *urb, int status) __releases(isp116x->lock) __acquires(isp116x->lock) { unsigned i; @@ -291,7 +291,7 @@ __releases(isp116x->lock) __acquires(isp116x->lock) usb_hcd_unlink_urb_from_ep(isp116x_to_hcd(isp116x), urb); spin_unlock(&isp116x->lock); - usb_hcd_giveback_urb(isp116x_to_hcd(isp116x), urb); + usb_hcd_giveback_urb(isp116x_to_hcd(isp116x), urb, status); spin_lock(&isp116x->lock); /* take idle endpoints out of the schedule */ @@ -453,13 +453,8 @@ static void postproc_atl_queue(struct isp116x *isp116x) } done: - if (status != -EINPROGRESS) { - spin_lock(&urb->lock); - urb->status = status; - spin_unlock(&urb->lock); - } - if (urb->status != -EINPROGRESS || urb->unlinked) - finish_request(isp116x, ep, urb); + if (status != -EINPROGRESS || urb->unlinked) + finish_request(isp116x, ep, urb, status); } } @@ -853,7 +848,7 @@ static int isp116x_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, } if (urb) - finish_request(isp116x, ep, urb); + finish_request(isp116x, ep, urb, status); done: spin_unlock_irqrestore(&isp116x->lock, flags); return rc; diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 13d31edd1a8..51817322232 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -62,8 +62,7 @@ __acquires(ohci->lock) /* urb->complete() can reenter this HCD */ usb_hcd_unlink_urb_from_ep(ohci_to_hcd(ohci), urb); spin_unlock (&ohci->lock); - urb->status = status; - usb_hcd_giveback_urb (ohci_to_hcd(ohci), urb); + usb_hcd_giveback_urb(ohci_to_hcd(ohci), urb, status); spin_lock (&ohci->lock); /* stop periodic dma if it's not needed */ diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index fea6036771f..fd00f1e33fb 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -782,12 +782,12 @@ static void force_dequeue(struct r8a66597 *r8a66597, u16 pipenum, u16 address) kfree(td); if (urb) { - urb->status = -ENODEV; usb_hcd_unlink_urb_from_ep(r8a66597_to_hcd(r8a66597), urb); spin_unlock(&r8a66597->lock); - usb_hcd_giveback_urb(r8a66597_to_hcd(r8a66597), urb); + usb_hcd_giveback_urb(r8a66597_to_hcd(r8a66597), urb, + -ENODEV); spin_lock(&r8a66597->lock); } break; @@ -1134,10 +1134,8 @@ __releases(r8a66597->lock) __acquires(r8a66597->lock) urb->start_frame = r8a66597_get_frame(hcd); usb_hcd_unlink_urb_from_ep(r8a66597_to_hcd(r8a66597), urb); - - urb->status = status; spin_unlock(&r8a66597->lock); - usb_hcd_giveback_urb(hcd, urb); + usb_hcd_giveback_urb(hcd, urb, status); spin_lock(&r8a66597->lock); } diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 515152809d3..94d859aa73f 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -436,9 +436,8 @@ static void finish_request( ep->nextpid = USB_PID_SETUP; usb_hcd_unlink_urb_from_ep(sl811_to_hcd(sl811), urb); - urb->status = status; spin_unlock(&sl811->lock); - usb_hcd_giveback_urb(sl811_to_hcd(sl811), urb); + usb_hcd_giveback_urb(sl811_to_hcd(sl811), urb, status); spin_lock(&sl811->lock); /* leave active endpoints in the schedule */ diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index db800a434b8..2b379a78d0d 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -518,7 +518,6 @@ static void u132_hcd_giveback_urb(struct u132 *u132, struct u132_endp *endp, unsigned long irqs; struct usb_hcd *hcd = u132_to_hcd(u132); urb->error_count = 0; - urb->status = status; spin_lock_irqsave(&endp->queue_lock.slock, irqs); usb_hcd_unlink_urb_from_ep(hcd, urb); endp->queue_next += 1; @@ -542,7 +541,7 @@ static void u132_hcd_giveback_urb(struct u132 *u132, struct u132_endp *endp, u132_ring_queue_work(u132, ring, 0); up(&u132->scheduler_lock); u132_endp_put_kref(u132, endp); - usb_hcd_giveback_urb(hcd, urb); + usb_hcd_giveback_urb(hcd, urb, status); return; } @@ -558,7 +557,6 @@ static void u132_hcd_abandon_urb(struct u132 *u132, struct u132_endp *endp, unsigned long irqs; struct usb_hcd *hcd = u132_to_hcd(u132); urb->error_count = 0; - urb->status = status; spin_lock_irqsave(&endp->queue_lock.slock, irqs); usb_hcd_unlink_urb_from_ep(hcd, urb); endp->queue_next += 1; @@ -575,7 +573,7 @@ static void u132_hcd_abandon_urb(struct u132 *u132, struct u132_endp *endp, endp->active = 0; spin_unlock_irqrestore(&endp->queue_lock.slock, irqs); kfree(urbq); - } usb_hcd_giveback_urb(hcd, urb); + } usb_hcd_giveback_urb(hcd, urb, status); return; } @@ -719,7 +717,7 @@ static void u132_hcd_interrupt_recv(void *data, struct urb *urb, u8 *buf, dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); - u132_hcd_giveback_urb(u132, endp, urb, urb->status); + u132_hcd_giveback_urb(u132, endp, urb, 0); return; } } @@ -771,7 +769,7 @@ static void u132_hcd_bulk_output_sent(void *data, struct urb *urb, u8 *buf, dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); - u132_hcd_giveback_urb(u132, endp, urb, urb->status); + u132_hcd_giveback_urb(u132, endp, urb, 0); return; } } @@ -874,7 +872,7 @@ static void u132_hcd_bulk_input_recv(void *data, struct urb *urb, u8 *buf, dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); - u132_hcd_giveback_urb(u132, endp, urb, urb->status); + u132_hcd_giveback_urb(u132, endp, urb, 0); return; } } @@ -911,7 +909,7 @@ static void u132_hcd_configure_empty_sent(void *data, struct urb *urb, u8 *buf, dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); - u132_hcd_giveback_urb(u132, endp, urb, urb->status); + u132_hcd_giveback_urb(u132, endp, urb, 0); return; } } @@ -983,7 +981,7 @@ static void u132_hcd_configure_input_recv(void *data, struct urb *urb, u8 *buf, dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); - u132_hcd_giveback_urb(u132, endp, urb, urb->status); + u132_hcd_giveback_urb(u132, endp, urb, 0); return; } } @@ -1020,7 +1018,7 @@ static void u132_hcd_configure_empty_recv(void *data, struct urb *urb, u8 *buf, dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); - u132_hcd_giveback_urb(u132, endp, urb, urb->status); + u132_hcd_giveback_urb(u132, endp, urb, 0); return; } } @@ -1080,7 +1078,7 @@ static void u132_hcd_configure_setup_sent(void *data, struct urb *urb, u8 *buf, dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); - u132_hcd_giveback_urb(u132, endp, urb, urb->status); + u132_hcd_giveback_urb(u132, endp, urb, 0); return; } } @@ -1121,7 +1119,7 @@ static void u132_hcd_enumeration_empty_recv(void *data, struct urb *urb, dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); - u132_hcd_giveback_urb(u132, endp, urb, urb->status); + u132_hcd_giveback_urb(u132, endp, urb, 0); return; } } @@ -1165,7 +1163,7 @@ static void u132_hcd_enumeration_address_sent(void *data, struct urb *urb, dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); - u132_hcd_giveback_urb(u132, endp, urb, urb->status); + u132_hcd_giveback_urb(u132, endp, urb, 0); return; } } @@ -1202,7 +1200,7 @@ static void u132_hcd_initial_empty_sent(void *data, struct urb *urb, u8 *buf, dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); - u132_hcd_giveback_urb(u132, endp, urb, urb->status); + u132_hcd_giveback_urb(u132, endp, urb, 0); return; } } @@ -1254,7 +1252,7 @@ static void u132_hcd_initial_input_recv(void *data, struct urb *urb, u8 *buf, dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); - u132_hcd_giveback_urb(u132, endp, urb, urb->status); + u132_hcd_giveback_urb(u132, endp, urb, 0); return; } } @@ -1299,7 +1297,7 @@ static void u132_hcd_initial_setup_sent(void *data, struct urb *urb, u8 *buf, dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p " "unlinked=%d\n", urb, urb->unlinked); up(&u132->scheduler_lock); - u132_hcd_giveback_urb(u132, endp, urb, urb->status); + u132_hcd_giveback_urb(u132, endp, urb, 0); return; } } @@ -2428,7 +2426,7 @@ static int dequeue_from_overflow_chain(struct u132 *u132, list_del(scan); endp->queue_size -= 1; urb->error_count = 0; - usb_hcd_giveback_urb(hcd, urb); + usb_hcd_giveback_urb(hcd, urb, 0); return 0; } else continue; @@ -2472,7 +2470,7 @@ static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp, return 0; } else { spin_unlock_irqrestore(&endp->queue_lock.slock, irqs); - u132_hcd_abandon_urb(u132, endp, urb, urb->status); + u132_hcd_abandon_urb(u132, endp, urb, status); return 0; } } else { @@ -2513,7 +2511,7 @@ static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp, irqs); kfree(urbq); } urb->error_count = 0; - usb_hcd_giveback_urb(hcd, urb); + usb_hcd_giveback_urb(hcd, urb, status); return 0; } else if (list_empty(&endp->urb_more)) { dev_err(&u132->platform_dev->dev, "urb=%p not found in " diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index e46d2b0203c..340d6ed3e6e 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -456,21 +456,6 @@ struct urb_priv { }; -/* - * Locking in uhci.c - * - * Almost everything relating to the hardware schedule and processing - * of URBs is protected by uhci->lock. urb->status is protected by - * urb->lock; that's the one exception. - * - * To prevent deadlocks, never lock uhci->lock while holding urb->lock. - * The safe order of locking is: - * - * #1 uhci->lock - * #2 urb->lock - */ - - /* Some special IDs */ #define PCI_VENDOR_ID_GENESYS 0x17a0 diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index bab56726655..e5d60d5b105 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -1480,7 +1480,7 @@ done: * Finish unlinking an URB and give it back */ static void uhci_giveback_urb(struct uhci_hcd *uhci, struct uhci_qh *qh, - struct urb *urb) + struct urb *urb, int status) __releases(uhci->lock) __acquires(uhci->lock) { @@ -1520,7 +1520,7 @@ __acquires(uhci->lock) usb_hcd_unlink_urb_from_ep(uhci_to_hcd(uhci), urb); spin_unlock(&uhci->lock); - usb_hcd_giveback_urb(uhci_to_hcd(uhci), urb); + usb_hcd_giveback_urb(uhci_to_hcd(uhci), urb, status); spin_lock(&uhci->lock); /* If the queue is now empty, we can unlink the QH and give up its @@ -1556,10 +1556,6 @@ static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) if (status == -EINPROGRESS) break; - spin_lock(&urb->lock); - urb->status = status; - spin_unlock(&urb->lock); - /* Dequeued but completed URBs can't be given back unless * the QH is stopped or has finished unlinking. */ if (urb->unlinked) { @@ -1569,7 +1565,7 @@ static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) return; } - uhci_giveback_urb(uhci, qh, urb); + uhci_giveback_urb(uhci, qh, urb, status); if (status < 0) break; } @@ -1594,7 +1590,7 @@ restart: qh->is_stopped = 0; return; } - uhci_giveback_urb(uhci, qh, urb); + uhci_giveback_urb(uhci, qh, urb, 0); goto restart; } } -- cgit v1.2.3 From b5b80f2b20c833f979060cf48ae3ee6fbfa552be Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Thu, 6 Sep 2007 23:18:15 +0200 Subject: USB: ELAN U132 Host Controller Driver: convert sw_lock to mutex The ELAN U132 Host Controller Driver uses the semaphore sw_lock as mutex. Use the mutex API instead of the (binary) semaphore. Signed-off-by: Matthias Kaehlcke Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/u132-hcd.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index 2b379a78d0d..ac283b09a63 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -183,7 +183,7 @@ struct u132_ring { struct u132 { struct kref kref; struct list_head u132_list; - struct semaphore sw_lock; + struct mutex sw_lock; struct semaphore scheduler_lock; struct u132_platform_data *board; struct platform_device *platform_dev; @@ -492,20 +492,20 @@ static void u132_hcd_monitor_work(struct work_struct *work) return; } else { int retval; - down(&u132->sw_lock); + mutex_lock(&u132->sw_lock); retval = read_roothub_info(u132); if (retval) { struct usb_hcd *hcd = u132_to_hcd(u132); u132_disable(u132); u132->going = 1; - up(&u132->sw_lock); + mutex_unlock(&u132->sw_lock); usb_hc_died(hcd); ftdi_elan_gone_away(u132->platform_dev); u132_monitor_put_kref(u132); return; } else { u132_monitor_requeue_work(u132, 500); - up(&u132->sw_lock); + mutex_unlock(&u132->sw_lock); return; } } @@ -1802,10 +1802,10 @@ static void u132_hcd_stop(struct usb_hcd *hcd) dev_err(&u132->platform_dev->dev, "device hcd=%p is being remov" "ed\n", hcd); } else { - down(&u132->sw_lock); + mutex_lock(&u132->sw_lock); msleep(100); u132_power(u132, 0); - up(&u132->sw_lock); + mutex_unlock(&u132->sw_lock); } } @@ -1827,7 +1827,7 @@ static int u132_hcd_start(struct usb_hcd *hcd) (pdev->dev.platform_data))->vendor; u16 device = ((struct u132_platform_data *) (pdev->dev.platform_data))->device; - down(&u132->sw_lock); + mutex_lock(&u132->sw_lock); msleep(10); if (vendor == PCI_VENDOR_ID_AMD && device == 0x740c) { u132->flags = OHCI_QUIRK_AMD756; @@ -1842,7 +1842,7 @@ static int u132_hcd_start(struct usb_hcd *hcd) u132->going = 1; } msleep(100); - up(&u132->sw_lock); + mutex_unlock(&u132->sw_lock); return retval; } else { dev_err(&u132->platform_dev->dev, "platform_device missing\n"); @@ -1862,13 +1862,13 @@ static int u132_hcd_reset(struct usb_hcd *hcd) return -ESHUTDOWN; } else { int retval; - down(&u132->sw_lock); + mutex_lock(&u132->sw_lock); retval = u132_init(u132); if (retval) { u132_disable(u132); u132->going = 1; } - up(&u132->sw_lock); + mutex_unlock(&u132->sw_lock); return retval; } } @@ -2865,7 +2865,7 @@ static int u132_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, return -ESHUTDOWN; } else { int retval = 0; - down(&u132->sw_lock); + mutex_lock(&u132->sw_lock); switch (typeReq) { case ClearHubFeature: switch (wValue) { @@ -2928,7 +2928,7 @@ static int u132_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, stall:retval = -EPIPE; break; } - up(&u132->sw_lock); + mutex_unlock(&u132->sw_lock); return retval; } } @@ -3064,7 +3064,7 @@ static int __devexit u132_remove(struct platform_device *pdev) dev_err(&u132->platform_dev->dev, "removing device u132" ".%d\n", u132->sequence_num); msleep(100); - down(&u132->sw_lock); + mutex_lock(&u132->sw_lock); u132_monitor_cancel_work(u132); while (rings-- > 0) { struct u132_ring *ring = &u132->ring[rings]; @@ -3077,7 +3077,7 @@ static int __devexit u132_remove(struct platform_device *pdev) u132->going += 1; printk(KERN_INFO "removing device u132.%d\n", u132->sequence_num); - up(&u132->sw_lock); + mutex_unlock(&u132->sw_lock); usb_remove_hcd(hcd); u132_u132_put_kref(u132); return 0; @@ -3097,7 +3097,7 @@ static void u132_initialise(struct u132 *u132, struct platform_device *pdev) u132->platform_dev = pdev; u132->power = 0; u132->reset = 0; - init_MUTEX(&u132->sw_lock); + mutex_init(&u132->sw_lock); init_MUTEX(&u132->scheduler_lock); while (rings-- > 0) { struct u132_ring *ring = &u132->ring[rings]; @@ -3107,7 +3107,7 @@ static void u132_initialise(struct u132 *u132, struct platform_device *pdev) ring->curr_endp = NULL; INIT_DELAYED_WORK(&ring->scheduler, u132_hcd_ring_work_scheduler); - } down(&u132->sw_lock); + } mutex_lock(&u132->sw_lock); INIT_DELAYED_WORK(&u132->monitor, u132_hcd_monitor_work); while (ports-- > 0) { struct u132_port *port = &u132->port[ports]; @@ -3137,7 +3137,7 @@ static void u132_initialise(struct u132 *u132, struct platform_device *pdev) while (endps-- > 0) { u132->endp[endps] = NULL; } - up(&u132->sw_lock); + mutex_unlock(&u132->sw_lock); return; } -- cgit v1.2.3 From e3a09051a16fcdb190ad0b7dc6064904c8118409 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 3 Oct 2007 18:53:13 +0900 Subject: USB: r8a66597-hcd: fix Class or Vendor Request Fixed the problem that does not work in the case of bRequest = 0x05 in Class or Vendor Request. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/r8a66597-hcd.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index fd00f1e33fb..0a699efe6c9 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -1034,6 +1034,15 @@ static void prepare_status_packet(struct r8a66597 *r8a66597, pipe_start(r8a66597, td->pipe); } +static int is_set_address(unsigned char *setup_packet) +{ + if (((setup_packet[0] & USB_TYPE_MASK) == USB_TYPE_STANDARD) && + setup_packet[1] == USB_REQ_SET_ADDRESS) + return 1; + else + return 0; +} + /* this function must be called with interrupt disabled */ static int start_transfer(struct r8a66597 *r8a66597, struct r8a66597_td *td) { @@ -1041,7 +1050,7 @@ static int start_transfer(struct r8a66597 *r8a66597, struct r8a66597_td *td) switch (td->type) { case USB_PID_SETUP: - if (td->urb->setup_packet[1] == USB_REQ_SET_ADDRESS) { + if (is_set_address(td->urb->setup_packet)) { td->set_address = 1; td->urb->setup_packet[2] = alloc_usb_address(r8a66597, td->urb); -- cgit v1.2.3 From 05eac910b69209164b1d9357bf8877a7c81b2e4e Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 3 Oct 2007 18:53:28 +0900 Subject: USB: r8a66597-hcd: fix endian problem Fixed the problem that does not work in the big endian machine. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/r8a66597-hcd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 0a699efe6c9..64fba53c263 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -834,7 +834,7 @@ static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb, info.pipenum = get_empty_pipenum(r8a66597, ep); info.address = get_urb_to_r8a66597_addr(r8a66597, urb); info.epnum = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - info.maxpacket = ep->wMaxPacketSize; + info.maxpacket = le16_to_cpu(ep->wMaxPacketSize); info.type = get_r8a66597_type(ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); info.bufnum = get_bufnum(info.pipenum); @@ -925,7 +925,7 @@ static void prepare_setup_packet(struct r8a66597 *r8a66597, r8a66597_write(r8a66597, ~(SIGN | SACK), INTSTS1); for (i = 0; i < 4; i++) { - r8a66597_write(r8a66597, p[i], setup_addr); + r8a66597_write(r8a66597, cpu_to_le16(p[i]), setup_addr); setup_addr += 2; } r8a66597_write(r8a66597, SUREQ, DCPCTR); @@ -2027,7 +2027,7 @@ static int r8a66597_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case GetPortStatus: if (wIndex > R8A66597_MAX_ROOT_HUB) goto error; - *(u32 *)buf = rh->port; + *(u32 *)buf = cpu_to_le32(rh->port); break; case SetPortFeature: if (wIndex > R8A66597_MAX_ROOT_HUB) -- cgit v1.2.3 From ca0677a29bddee35918bf3c0c55cc3badd69079e Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 5 Oct 2007 15:53:12 +0900 Subject: USB: r8a66597-hcd: fix driver removing Fixed the problem that accessed register of this controller after having called iounmap(). Signed-off-by: Yoshihiro Shimoda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/r8a66597-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 64fba53c263..ae8ec4474eb 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -2126,8 +2126,8 @@ static int __init_or_module r8a66597_remove(struct platform_device *pdev) struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597); del_timer_sync(&r8a66597->rh_timer); - iounmap((void *)r8a66597->reg); usb_remove_hcd(hcd); + iounmap((void *)r8a66597->reg); usb_put_hcd(hcd); return 0; } -- cgit v1.2.3 From 4f45426cfd6170311e116442ccd8ce0e31979237 Mon Sep 17 00:00:00 2001 From: Valentine Barshak Date: Tue, 9 Oct 2007 15:00:05 -0700 Subject: USB: add runtime frame_no quirk for big-endian OHCI Add OHCI big endian frame_no quirk. The frame_no value stored in the HCCA is a 16 bit field at a specific offset, but since not all CPUs can do 16-bit memory accesses it's used as a 32 bit field. And that's why big-endian OHCI must shift 16 bits ... unless the spec is not followed. Currently there's one MPC52xx platform that doesn't need the shift. This patch adds a new "big endian frame_no" quirk to control that at runtime. Signed-off-by: Valentine Barshak Acked-by: Dale Farnsworth Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-ppc-of.c | 5 ++++- drivers/usb/host/ohci-ppc-soc.c | 5 +++++ drivers/usb/host/ohci.h | 13 ++++++------- 3 files changed, 15 insertions(+), 8 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index c43b66acd4d..0a742692015 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -134,8 +134,11 @@ ohci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match) } ohci = hcd_to_ohci(hcd); - if (is_bigendian) + if (is_bigendian) { ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC; + if (of_device_is_compatible(dn, "mpc5200-ohci")) + ohci->flags |= OHCI_QUIRK_FRAME_NO; + } ohci_hcd_init(ohci); diff --git a/drivers/usb/host/ohci-ppc-soc.c b/drivers/usb/host/ohci-ppc-soc.c index 1a2e1777ca6..f95be1896b0 100644 --- a/drivers/usb/host/ohci-ppc-soc.c +++ b/drivers/usb/host/ohci-ppc-soc.c @@ -73,6 +73,11 @@ static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver, ohci = hcd_to_ohci(hcd); ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC; + +#ifdef CONFIG_PPC_MPC52xx + /* MPC52xx doesn't need frame_no shift */ + ohci->flags |= OHCI_QUIRK_FRAME_NO; +#endif ohci_hcd_init(ohci); retval = usb_add_hcd(hcd, irq, IRQF_DISABLED); diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index dd4d5b4dcb6..47c5c66a282 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -398,6 +398,7 @@ struct ohci_hcd { #define OHCI_QUIRK_BE_MMIO 0x10 /* BE registers */ #define OHCI_QUIRK_ZFMICRO 0x20 /* Compaq ZFMicro chipset*/ #define OHCI_QUIRK_NEC 0x40 /* lost interrupts */ +#define OHCI_QUIRK_FRAME_NO 0x80 /* no big endian frame_no shift */ // there are also chip quirks/bugs in init logic struct work_struct nec_work; /* Worker for NEC quirk */ @@ -633,15 +634,12 @@ static inline u32 hc32_to_cpup (const struct ohci_hcd *ohci, const __hc32 *x) /* HCCA frame number is 16 bits, but is accessed as 32 bits since not all * hardware handles 16 bit reads. That creates a different confusion on * some big-endian SOC implementations. Same thing happens with PSW access. - * - * FIXME: Deal with that as a runtime quirk when STB03xxx is ported over - * to arch/powerpc */ -#ifdef CONFIG_STB03xxx -#define OHCI_BE_FRAME_NO_SHIFT 16 +#ifdef CONFIG_PPC_MPC52xx +#define big_endian_frame_no_quirk(ohci) (ohci->flags & OHCI_QUIRK_FRAME_NO) #else -#define OHCI_BE_FRAME_NO_SHIFT 0 +#define big_endian_frame_no_quirk(ohci) 0 #endif static inline u16 ohci_frame_no(const struct ohci_hcd *ohci) @@ -649,7 +647,8 @@ static inline u16 ohci_frame_no(const struct ohci_hcd *ohci) u32 tmp; if (big_endian_desc(ohci)) { tmp = be32_to_cpup((__force __be32 *)&ohci->hcca->frame_no); - tmp >>= OHCI_BE_FRAME_NO_SHIFT; + if (!big_endian_frame_no_quirk(ohci)) + tmp >>= 16; } else tmp = le32_to_cpup((__force __le32 *)&ohci->hcca->frame_no); -- cgit v1.2.3 From b24896c6b7ddb37ab20ba0bbfd0ed36a38923f67 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 9 Oct 2007 22:04:16 -0700 Subject: USB: ehci build fixes on au1xxx, ppc-soc Cleanup: references to two PM routines (and HCD entry points) that no longer exist are swapped with their replacements. Evidently au1xxx and ppc-soc EHCI support doesn't get compiled with power management very much, or these build bugs would have been patched long ago. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-au1xxx.c | 6 ++---- drivers/usb/host/ehci-ppc-soc.c | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index b1d19268cb2..766ef68a0b4 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -220,10 +220,8 @@ static const struct hc_driver ehci_au1xxx_hc_driver = { */ .hub_status_data = ehci_hub_status_data, .hub_control = ehci_hub_control, -#ifdef CONFIG_PM - .hub_suspend = ehci_hub_suspend, - .hub_resume = ehci_hub_resume, -#endif + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ehci-ppc-soc.c b/drivers/usb/host/ehci-ppc-soc.c index 4f99b0eb27b..452d4b1bc85 100644 --- a/drivers/usb/host/ehci-ppc-soc.c +++ b/drivers/usb/host/ehci-ppc-soc.c @@ -160,10 +160,8 @@ static const struct hc_driver ehci_ppc_soc_hc_driver = { */ .hub_status_data = ehci_hub_status_data, .hub_control = ehci_hub_control, -#ifdef CONFIG_PM - .hub_suspend = ehci_hub_suspend, - .hub_resume = ehci_hub_resume, -#endif + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, }; static int ehci_hcd_ppc_soc_drv_probe(struct platform_device *pdev) -- cgit v1.2.3 From c604e851486eabcbeb73e984279d436ce121fd5d Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Tue, 9 Oct 2007 23:47:17 -0700 Subject: USB: ohci SSB bus glue This adds SSB bus glue for the USB OHCI HCD. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 13 +++ drivers/usb/host/ohci-hcd.c | 21 +++- drivers/usb/host/ohci-ssb.c | 247 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/host/ohci-ssb.c (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 565d6ef4c4c..c978d622fa8 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -154,6 +154,19 @@ config USB_OHCI_HCD_PCI Enables support for PCI-bus plug-in USB controller cards. If unsure, say Y. +config USB_OHCI_HCD_SSB + bool "OHCI support for Broadcom SSB OHCI core" + depends on USB_OHCI_HCD && SSB && EXPERIMENTAL + default n + ---help--- + Support for the Sonics Silicon Backplane (SSB) attached + Broadcom USB OHCI core. + + This device is present in some embedded devices with + Broadcom based SSB bus. + + If unsure, say N. + config USB_OHCI_BIG_ENDIAN_DESC bool depends on USB_OHCI_HCD diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index f7c6ced2bc0..240c7f50754 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1033,11 +1033,17 @@ MODULE_LICENSE ("GPL"); #define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver #endif +#ifdef CONFIG_USB_OHCI_HCD_SSB +#include "ohci-ssb.c" +#define SSB_OHCI_DRIVER ssb_ohci_driver +#endif + #if !defined(PCI_DRIVER) && \ !defined(PLATFORM_DRIVER) && \ !defined(OF_PLATFORM_DRIVER) && \ !defined(SA1111_DRIVER) && \ - !defined(PS3_SYSTEM_BUS_DRIVER) + !defined(PS3_SYSTEM_BUS_DRIVER) && \ + !defined(SSB_OHCI_DRIVER) #error "missing bus glue for ohci-hcd" #endif @@ -1082,10 +1088,20 @@ static int __init ohci_hcd_mod_init(void) goto error_pci; #endif +#ifdef SSB_OHCI_DRIVER + retval = ssb_driver_register(&SSB_OHCI_DRIVER); + if (retval) + goto error_ssb; +#endif + return retval; /* Error path */ +#ifdef SSB_OHCI_DRIVER + error_ssb: +#endif #ifdef PCI_DRIVER + pci_unregister_driver(&PCI_DRIVER); error_pci: #endif #ifdef SA1111_DRIVER @@ -1110,6 +1126,9 @@ module_init(ohci_hcd_mod_init); static void __exit ohci_hcd_mod_exit(void) { +#ifdef SSB_OHCI_DRIVER + ssb_driver_unregister(&SSB_OHCI_DRIVER); +#endif #ifdef PCI_DRIVER pci_unregister_driver(&PCI_DRIVER); #endif diff --git a/drivers/usb/host/ohci-ssb.c b/drivers/usb/host/ohci-ssb.c new file mode 100644 index 00000000000..bc3e785d8c0 --- /dev/null +++ b/drivers/usb/host/ohci-ssb.c @@ -0,0 +1,247 @@ +/* + * Sonics Silicon Backplane + * Broadcom USB-core OHCI driver + * + * Copyright 2007 Michael Buesch + * + * Derived from the OHCI-PCI driver + * Copyright 1999 Roman Weissgaerber + * Copyright 2000-2002 David Brownell + * Copyright 1999 Linus Torvalds + * Copyright 1999 Gregory P. Smith + * + * Derived from the USBcore related parts of Broadcom-SB + * Copyright 2005 Broadcom Corporation + * + * Licensed under the GNU/GPL. See COPYING for details. + */ +#include + + +#define SSB_OHCI_TMSLOW_HOSTMODE (1 << 29) + +struct ssb_ohci_device { + struct ohci_hcd ohci; /* _must_ be at the beginning. */ + + u32 enable_flags; +}; + +static inline +struct ssb_ohci_device *hcd_to_ssb_ohci(struct usb_hcd *hcd) +{ + return (struct ssb_ohci_device *)(hcd->hcd_priv); +} + + +static int ssb_ohci_reset(struct usb_hcd *hcd) +{ + struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); + struct ohci_hcd *ohci = &ohcidev->ohci; + int err; + + ohci_hcd_init(ohci); + err = ohci_init(ohci); + + return err; +} + +static int ssb_ohci_start(struct usb_hcd *hcd) +{ + struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); + struct ohci_hcd *ohci = &ohcidev->ohci; + int err; + + err = ohci_run(ohci); + if (err < 0) { + ohci_err(ohci, "can't start\n"); + ohci_stop(hcd); + } + + return err; +} + +#ifdef CONFIG_PM +static int ssb_ohci_hcd_suspend(struct usb_hcd *hcd, pm_message_t message) +{ + struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); + struct ohci_hcd *ohci = &ohcidev->ohci; + unsigned long flags; + + spin_lock_irqsave(&ohci->lock, flags); + + ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); + ohci_readl(ohci, &ohci->regs->intrdisable); /* commit write */ + + /* make sure snapshot being resumed re-enumerates everything */ + if (message.event == PM_EVENT_PRETHAW) + ohci_usb_reset(ohci); + + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + spin_unlock_irqrestore(&ohci->lock, flags); + return 0; +} + +static int ssb_ohci_hcd_resume(struct usb_hcd *hcd) +{ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + usb_hcd_resume_root_hub(hcd); + return 0; +} +#endif /* CONFIG_PM */ + +static const struct hc_driver ssb_ohci_hc_driver = { + .description = "ssb-usb-ohci", + .product_desc = "SSB OHCI Controller", + .hcd_priv_size = sizeof(struct ssb_ohci_device), + + .irq = ohci_irq, + .flags = HCD_MEMORY | HCD_USB11, + + .reset = ssb_ohci_reset, + .start = ssb_ohci_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + +#ifdef CONFIG_PM + .suspend = ssb_ohci_hcd_suspend, + .resume = ssb_ohci_hcd_resume, +#endif + + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + .get_frame_number = ohci_get_frame, + + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, + + .start_port_reset = ohci_start_port_reset, +}; + +static void ssb_ohci_detach(struct ssb_device *dev) +{ + struct usb_hcd *hcd = ssb_get_drvdata(dev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + usb_put_hcd(hcd); + ssb_device_disable(dev, 0); +} + +static int ssb_ohci_attach(struct ssb_device *dev) +{ + struct ssb_ohci_device *ohcidev; + struct usb_hcd *hcd; + int err = -ENOMEM; + u32 tmp, flags = 0; + + if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) + flags |= SSB_OHCI_TMSLOW_HOSTMODE; + + ssb_device_enable(dev, flags); + + hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev, + dev->dev->bus_id); + if (!hcd) + goto err_dev_disable; + ohcidev = hcd_to_ssb_ohci(hcd); + ohcidev->enable_flags = flags; + + tmp = ssb_read32(dev, SSB_ADMATCH0); + hcd->rsrc_start = ssb_admatch_base(tmp); + hcd->rsrc_len = ssb_admatch_size(tmp); + hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) + goto err_put_hcd; + err = usb_add_hcd(hcd, dev->irq, IRQF_SHARED); + if (err) + goto err_iounmap; + + ssb_set_drvdata(dev, hcd); + + return err; + +err_iounmap: + iounmap(hcd->regs); +err_put_hcd: + usb_put_hcd(hcd); +err_dev_disable: + ssb_device_disable(dev, flags); + return err; +} + +static int ssb_ohci_probe(struct ssb_device *dev, + const struct ssb_device_id *id) +{ + int err; + u16 chipid_top; + + /* USBcores are only connected on embedded devices. */ + chipid_top = (dev->bus->chip_id & 0xFF00); + if (chipid_top != 0x4700 && chipid_top != 0x5300) + return -ENODEV; + + /* TODO: Probably need checks here; is the core connected? */ + + if (usb_disabled()) + return -ENODEV; + + /* We currently always attach SSB_DEV_USB11_HOSTDEV + * as HOST OHCI. If we want to attach it as Client device, + * we must branch here and call into the (yet to + * be written) Client mode driver. Same for remove(). */ + + err = ssb_ohci_attach(dev); + + return err; +} + +static void ssb_ohci_remove(struct ssb_device *dev) +{ + ssb_ohci_detach(dev); +} + +#ifdef CONFIG_PM + +static int ssb_ohci_suspend(struct ssb_device *dev, pm_message_t state) +{ + ssb_device_disable(dev, 0); + + return 0; +} + +static int ssb_ohci_resume(struct ssb_device *dev) +{ + struct usb_hcd *hcd = ssb_get_drvdata(dev); + struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); + + ssb_device_enable(dev, ohcidev->enable_flags); + + return 0; +} + +#else /* !CONFIG_PM */ +#define ssb_ohci_suspend NULL +#define ssb_ohci_resume NULL +#endif /* CONFIG_PM */ + +static const struct ssb_device_id ssb_ohci_table[] = { + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV), + SSB_DEVTABLE_END +}; +MODULE_DEVICE_TABLE(ssb, ssb_ohci_table); + +static struct ssb_driver ssb_ohci_driver = { + .name = KBUILD_MODNAME, + .id_table = ssb_ohci_table, + .probe = ssb_ohci_probe, + .remove = ssb_ohci_remove, + .suspend = ssb_ohci_suspend, + .resume = ssb_ohci_resume, +}; -- cgit v1.2.3 From 32fe01985aa2cb2562f6fc171e526e279abe10db Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 10 Oct 2007 16:27:07 -0400 Subject: USB: mutual exclusion for EHCI init and port resets This patch (as999) fixes a problem that sometimes shows up when host controller driver modules are loaded in the wrong order. If ehci-hcd happens to initialize an EHCI controller while the companion OHCI or UHCI controller is in the middle of a port reset, the reset can fail and the companion may get very confused. The patch adds an rw-semaphore and uses it to keep EHCI initialization and port resets mutually exclusive. Signed-off-by: Alan Stern Acked-by: David Brownell Cc: David Miller Cc: Dely L Sy Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index db00492588b..c1514442883 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -570,10 +570,18 @@ static int ehci_run (struct usb_hcd *hcd) * are explicitly handed to companion controller(s), so no TT is * involved with the root hub. (Except where one is integrated, * and there's no companion controller unless maybe for USB OTG.) + * + * Turning on the CF flag will transfer ownership of all ports + * from the companions to the EHCI controller. If any of the + * companions are in the middle of a port reset at the time, it + * could cause trouble. Write-locking ehci_cf_port_reset_rwsem + * guarantees that no resets are in progress. */ + down_write(&ehci_cf_port_reset_rwsem); hcd->state = HC_STATE_RUNNING; ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ + up_write(&ehci_cf_port_reset_rwsem); temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase)); ehci_info (ehci, -- cgit v1.2.3