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