unsigned long flags;
spin_lock_irqsave(&ahcd->lock, flags);
+
ed = ep->hcpriv;
if (!ed) {
u32 info;
if (ed)
ep->hcpriv = ed;
}
+
spin_unlock_irqrestore(&ahcd->lock, flags);
return ed;
/* calculate transfer length/status and update the urb
* PRECONDITION: irqsafe (only for urb->status locking)
*/
-static void td_done(struct admhcd *ahcd, struct urb *urb, struct td *td)
+static int td_done(struct admhcd *ahcd, struct urb *urb, struct td *td)
{
+ struct urb_priv *urb_priv = urb->hcpriv;
u32 info = hc32_to_cpup(ahcd, &td->hwINFO);
int type = usb_pipetype(urb->pipe);
- int cc = TD_CC_NOERROR;
+ int cc;
+
+ cc = TD_CC_GET(info);
/* ISO ... drivers see per-TD length/status */
if (type == PIPE_ISOCHRONOUS) {
u32 bl = TD_BL_GET(hc32_to_cpup(ahcd, &td->hwCBL));
u32 tdDBP = hc32_to_cpup(ahcd, &td->hwDBP);
- cc = TD_CC_GET(info);
-
/* update packet status if needed (short is normally ok) */
if (cc == TD_CC_DATAUNDERRUN
&& !(urb->transfer_flags & URB_SHORT_NOT_OK))
cc = TD_CC_NOERROR;
if (cc != TD_CC_NOERROR && cc < TD_CC_HCD0) {
- admhc_dump_ed(ahcd, "CC ERROR", td->ed, 1);
spin_lock(&urb->lock);
if (urb->status == -EINPROGRESS)
urb->status = cc_to_error[cc];
}
list_del(&td->td_list);
+ urb_priv->td_idx++;
+ return cc;
}
/*-------------------------------------------------------------------------*/
* until its urb->complete() has a chance to clean up.
*/
ed->hwINFO |= cpu_to_hc32 (ahcd, ED_SKIP);
- wmb ();
+ wmb();
ed->hwHeadP &= ~cpu_to_hc32 (ahcd, ED_H);
/* put any later tds from this urb onto the donelist, after 'td',
info = next->hwINFO;
#if 0 /* FIXME */
info |= cpu_to_hc32 (ahcd, TD_DONE);
- info &= ~cpu_to_hc32 (ahcd, TD_CC);
#endif
+ info &= ~cpu_to_hc32 (ahcd, TD_CC);
next->hwINFO = info;
next->next_dl_td = rev;
break;
/* fallthrough */
case TD_CC_STALL:
- if (usb_pipecontrol (urb->pipe))
+ if (usb_pipecontrol(urb->pipe))
break;
/* fallthrough */
default:
*prev = td->hwNextTD | savebits;
/* HC may have partly processed this TD */
- urb_print(urb, "PARTIAL",1);
+ urb_print(urb, "PARTIAL", 1);
td_done(ahcd, urb, td);
- urb_priv->td_idx++;
/* if URB is done, clean up */
if (urb_priv->td_idx == urb_priv->td_cnt) {
* scanning the (re-reversed) donelist as this does.
*/
+static void ed_unhalt(struct admhcd *ahcd, struct ed *ed, struct urb *urb)
+{
+ struct list_head *entry,*tmp;
+ struct urb_priv *urb_priv = urb->hcpriv;
+ __hc32 toggle = ed->hwHeadP & cpu_to_hc32 (ahcd, ED_C);
+
+
+#ifdef ADMHC_VERBOSE_DEBUG
+ admhc_dump_ed(ahcd, "UNHALT", ed, 0);
+#endif
+ /* clear ed halt; this is the td that caused it, but keep it inactive
+ * until its urb->complete() has a chance to clean up.
+ */
+ ed->hwINFO |= cpu_to_hc32 (ahcd, ED_SKIP);
+ wmb();
+ ed->hwHeadP &= ~cpu_to_hc32 (ahcd, ED_H);
+
+ list_for_each_safe(entry, tmp, &ed->td_list) {
+ struct td *td = list_entry(entry, struct td, td_list);
+ __hc32 info;
+
+ if (td->urb != urb)
+ break;
+
+ info = td->hwINFO;
+ info &= ~cpu_to_hc32(ahcd, TD_CC | TD_OWN);
+ td->hwINFO = info;
+
+ ed->hwHeadP = td->hwNextTD | toggle;
+ wmb();
+ }
+
+}
+
+static inline int is_ed_halted(struct admhcd *ahcd, struct ed *ed)
+{
+ return ((hc32_to_cpup(ahcd, &ed->hwHeadP) & ED_H) == ED_H);
+}
+
+static inline int is_td_halted(struct admhcd *ahcd, struct ed *ed,
+ struct td *td)
+{
+ return ((hc32_to_cpup(ahcd, &ed->hwHeadP) & TD_MASK) ==
+ (hc32_to_cpup(ahcd, &td->hwNextTD) & TD_MASK));
+}
+
static void ed_update(struct admhcd *ahcd, struct ed *ed)
{
struct list_head *entry,*tmp;
- admhc_dump_ed(ahcd, "ed update", ed, 1);
+#ifdef ADMHC_VERBOSE_DEBUG
+ admhc_dump_ed(ahcd, "UPDATE", ed, 0);
+#endif
list_for_each_safe(entry, tmp, &ed->td_list) {
struct td *td = list_entry(entry, struct td, td_list);
struct urb *urb = td->urb;
struct urb_priv *urb_priv = urb->hcpriv;
+ int cc;
if (hc32_to_cpup(ahcd, &td->hwINFO) & TD_OWN)
break;
/* update URB's length and status from TD */
- td_done(ahcd, urb, td);
- urb_priv->td_idx++;
+ cc = td_done(ahcd, urb, td);
+ if (is_ed_halted(ahcd, ed) && is_td_halted(ahcd, ed, td))
+ ed_unhalt(ahcd, ed, urb);
/* If all this urb's TDs are done, call complete() */
if (urb_priv->td_idx == urb_priv->td_cnt)
}
}
-static void ed_halt(struct admhcd *ahcd, struct ed *ed)
-{
- admhc_dump_ed(ahcd, "ed_halt", ed, 1);
-}
-
/* there are some tds completed; called in_irq(), with HCD locked */
static void admhc_td_complete(struct admhcd *ahcd)
{
if (ed->state != ED_OPER)
continue;
- if (hc32_to_cpup(ahcd, &ed->hwINFO) & ED_SKIP)
- continue;
-
- if (hc32_to_cpup(ahcd, &ed->hwHeadP) & ED_H) {
- ed_halt(ahcd, ed);
- continue;
- }
-
ed_update(ahcd, ed);
}
}