X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fehci-sched.c;h=5871944e61459cca9cbd9fa85a992186b838c691;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=e067274efeb51c512fe92791b30e34e2d6dd0de3;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index e067274ef..5871944e6 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2003 by David Brownell + * Copyright (c) 2001-2004 by David Brownell * Copyright (c) 2003 Michal Sojka, for high-speed iso transfers * * This program is free software; you can redistribute it and/or modify it @@ -44,7 +44,7 @@ static int ehci_get_frame (struct usb_hcd *hcd); * @tag: hardware tag for type of this record */ static union ehci_shadow * -periodic_next_shadow (union ehci_shadow *periodic, int tag) +periodic_next_shadow (union ehci_shadow *periodic, __le32 tag) { switch (tag) { case Q_TYPE_QH: @@ -59,46 +59,35 @@ periodic_next_shadow (union ehci_shadow *periodic, int tag) } } -/* returns true after successful unlink */ /* caller must hold ehci->lock */ -static int periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr) +static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr) { union ehci_shadow *prev_p = &ehci->pshadow [frame]; - u32 *hw_p = &ehci->periodic [frame]; + __le32 *hw_p = &ehci->periodic [frame]; union ehci_shadow here = *prev_p; - union ehci_shadow *next_p; /* find predecessor of "ptr"; hw and shadow lists are in sync */ while (here.ptr && here.ptr != ptr) { prev_p = periodic_next_shadow (prev_p, Q_NEXT_TYPE (*hw_p)); - hw_p = &here.qh->hw_next; + hw_p = here.hw_next; here = *prev_p; } /* an interrupt entry (at list end) could have been shared */ - if (!here.ptr) { - dbg ("entry %p no longer on frame [%d]", ptr, frame); - return 0; - } - // vdbg ("periodic unlink %p from frame %d", ptr, frame); + if (!here.ptr) + return; - /* update hardware list ... HC may still know the old structure, so - * don't change hw_next until it'll have purged its cache + /* update shadow and hardware lists ... the old "next" pointers + * from ptr may still be in use, the caller updates them. */ - next_p = periodic_next_shadow (&here, Q_NEXT_TYPE (*hw_p)); - *hw_p = here.qh->hw_next; - - /* unlink from shadow list; HCD won't see old structure again */ - *prev_p = *next_p; - next_p->ptr = 0; - - return 1; + *prev_p = *periodic_next_shadow (&here, Q_NEXT_TYPE (*hw_p)); + *hw_p = *here.hw_next; } /* how many of the uframe's 125 usecs are allocated? */ static unsigned short periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) { - u32 *hw_p = &ehci->periodic [frame]; + __le32 *hw_p = &ehci->periodic [frame]; union ehci_shadow *q = &ehci->pshadow [frame]; unsigned usecs = 0; @@ -114,7 +103,8 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) hw_p = &q->qh->hw_next; q = &q->qh->qh_next; break; - case Q_TYPE_FSTN: + // case Q_TYPE_FSTN: + default: /* for "save place" FSTNs, count the relevant INTR * bandwidth from the previous frame */ @@ -149,13 +139,11 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) hw_p = &q->sitd->hw_next; q = &q->sitd->sitd_next; break; - default: - BUG (); } } #ifdef DEBUG if (usecs > 100) - err ("overallocated uframe %d, periodic is %d usecs", + ehci_err (ehci, "uframe %d sched overrun: %d usecs\n", frame * 8 + uframe, usecs); #endif return usecs; @@ -196,7 +184,7 @@ static int tt_no_collision ( */ for (; frame < ehci->periodic_size; frame += period) { union ehci_shadow here; - u32 type; + __le32 type; here = ehci->pshadow [frame]; type = Q_NEXT_TYPE (ehci->periodic [frame]); @@ -220,7 +208,7 @@ static int tt_no_collision ( here = here.qh->qh_next; continue; case Q_TYPE_SITD: - if (same_tt (dev, here.itd->urb->dev)) { + if (same_tt (dev, here.sitd->urb->dev)) { u16 mask; mask = le32_to_cpu (here.sitd @@ -230,7 +218,7 @@ static int tt_no_collision ( if (mask & uf_mask) break; } - type = Q_NEXT_TYPE (here.qh->hw_next); + type = Q_NEXT_TYPE (here.sitd->hw_next); here = here.sitd->sitd_next; continue; // case Q_TYPE_FSTN: @@ -261,14 +249,14 @@ static int enable_periodic (struct ehci_hcd *ehci) */ status = handshake (&ehci->regs->status, STS_PSS, 0, 9 * 125); if (status != 0) { - ehci->hcd.state = USB_STATE_HALT; + ehci_to_hcd(ehci)->state = HC_STATE_HALT; return status; } cmd = readl (&ehci->regs->command) | CMD_PSE; writel (cmd, &ehci->regs->command); /* posted write ... PSS happens later */ - ehci->hcd.state = USB_STATE_RUNNING; + ehci_to_hcd(ehci)->state = HC_STATE_RUNNING; /* make sure ehci_work scans these */ ehci->next_uframe = readl (&ehci->regs->frame_index) @@ -286,7 +274,7 @@ static int disable_periodic (struct ehci_hcd *ehci) */ status = handshake (&ehci->regs->status, STS_PSS, STS_PSS, 9 * 125); if (status != 0) { - ehci->hcd.state = USB_STATE_HALT; + ehci_to_hcd(ehci)->state = HC_STATE_HALT; return status; } @@ -300,64 +288,144 @@ static int disable_periodic (struct ehci_hcd *ehci) /*-------------------------------------------------------------------------*/ -// FIXME microframe periods not yet handled +/* periodic schedule slots have iso tds (normal or split) first, then a + * sparse tree for active interrupt transfers. + * + * this just links in a qh; caller guarantees uframe masks are set right. + * no FSTN support (yet; ehci 0.96+) + */ +static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + unsigned i; + unsigned period = qh->period; -static void intr_deschedule ( - struct ehci_hcd *ehci, - struct ehci_qh *qh, - int wait -) { - int status; - unsigned frame = qh->start; + dev_dbg (&qh->dev->dev, + "link qh%d-%04x/%p start %d [%d/%d us]\n", + period, le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK), + qh, qh->start, qh->usecs, qh->c_usecs); - do { - periodic_unlink (ehci, frame, qh); - qh_put (ehci, qh); - frame += qh->period; - } while (frame < ehci->periodic_size); + /* high bandwidth, or otherwise every microframe */ + if (period == 0) + period = 1; + + for (i = qh->start; i < ehci->periodic_size; i += period) { + union ehci_shadow *prev = &ehci->pshadow [i]; + __le32 *hw_p = &ehci->periodic [i]; + union ehci_shadow here = *prev; + __le32 type = 0; + /* skip the iso nodes at list head */ + while (here.ptr) { + type = Q_NEXT_TYPE (*hw_p); + if (type == Q_TYPE_QH) + break; + prev = periodic_next_shadow (prev, type); + hw_p = &here.qh->hw_next; + here = *prev; + } + + /* sorting each branch by period (slow-->fast) + * enables sharing interior tree nodes + */ + while (here.ptr && qh != here.qh) { + if (qh->period > here.qh->period) + break; + prev = &here.qh->qh_next; + hw_p = &here.qh->hw_next; + here = *prev; + } + /* link in this qh, unless some earlier pass did that */ + if (qh != here.qh) { + qh->qh_next = here; + if (here.qh) + qh->hw_next = *hw_p; + wmb (); + prev->qh = qh; + *hw_p = QH_NEXT (qh->qh_dma); + } + } + qh->qh_state = QH_STATE_LINKED; + qh_get (qh); + + /* update per-qh bandwidth for usbfs */ + ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->period + ? ((qh->usecs + qh->c_usecs) / qh->period) + : (qh->usecs * 8); + + /* maybe enable periodic schedule processing */ + if (!ehci->periodic_sched++) + return enable_periodic (ehci); + + return 0; +} + +static void qh_unlink_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + unsigned i; + unsigned period; + + // FIXME: + // IF this isn't high speed + // and this qh is active in the current uframe + // (and overlay token SplitXstate is false?) + // THEN + // qh->hw_info1 |= __constant_cpu_to_le32 (1 << 7 /* "ignore" */); + + /* high bandwidth, or otherwise part of every microframe */ + if ((period = qh->period) == 0) + period = 1; + + for (i = qh->start; i < ehci->periodic_size; i += period) + periodic_unlink (ehci, i, qh); + + /* update per-qh bandwidth for usbfs */ + ehci_to_hcd(ehci)->self.bandwidth_allocated -= qh->period + ? ((qh->usecs + qh->c_usecs) / qh->period) + : (qh->usecs * 8); + + dev_dbg (&qh->dev->dev, + "unlink qh%d-%04x/%p start %d [%d/%d us]\n", + qh->period, + le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK), + qh, qh->start, qh->usecs, qh->c_usecs); + + /* qh->qh_next still "live" to HC */ qh->qh_state = QH_STATE_UNLINK; - qh->qh_next.ptr = 0; - ehci->periodic_sched--; + qh->qh_next.ptr = NULL; + qh_put (qh); /* maybe turn off periodic schedule */ + ehci->periodic_sched--; if (!ehci->periodic_sched) - status = disable_periodic (ehci); - else { - status = 0; - vdbg ("periodic schedule still enabled"); - } + (void) disable_periodic (ehci); +} - /* - * If the hc may be looking at this qh, then delay a uframe - * (yeech!) to be sure it's done. - * No other threads may be mucking with this qh. - */ - if (((ehci_get_frame (&ehci->hcd) - frame) % qh->period) == 0) { - if (wait) { - udelay (125); - qh->hw_next = EHCI_LIST_END; - } else { - /* we may not be IDLE yet, but if the qh is empty - * the race is very short. then if qh also isn't - * rescheduled soon, it won't matter. otherwise... - */ - vdbg ("intr_deschedule..."); - } - } else - qh->hw_next = EHCI_LIST_END; +static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + unsigned wait; - qh->qh_state = QH_STATE_IDLE; + qh_unlink_periodic (ehci, qh); - /* update per-qh bandwidth utilization (for usbfs) */ - hcd_to_bus (&ehci->hcd)->bandwidth_allocated -= - (qh->usecs + qh->c_usecs) / qh->period; + /* simple/paranoid: always delay, expecting the HC needs to read + * qh->hw_next or finish a writeback after SPLIT/CSPLIT ... and + * expect khubd to clean up after any CSPLITs we won't issue. + * active high speed queues may need bigger delays... + */ + if (list_empty (&qh->qtd_list) + || (__constant_cpu_to_le32 (QH_CMASK) + & qh->hw_info2) != 0) + wait = 2; + else + wait = 55; /* worst case: 3 * 1024 */ - dbg ("descheduled qh %p, period = %d frame = %d count = %d, urbs = %d", - qh, qh->period, frame, - atomic_read (&qh->refcount), ehci->periodic_sched); + udelay (wait); + qh->qh_state = QH_STATE_IDLE; + qh->hw_next = EHCI_LIST_END; + wmb (); } +/*-------------------------------------------------------------------------*/ + static int check_period ( struct ehci_hcd *ehci, unsigned frame, @@ -365,6 +433,8 @@ static int check_period ( unsigned period, unsigned usecs ) { + int claimed; + /* complete split running into next frame? * given FSTN support, we could sometimes check... */ @@ -377,22 +447,26 @@ static int check_period ( */ usecs = 100 - usecs; - do { - int claimed; - -// FIXME delete when intr_submit handles non-empty queues -// this gives us a one intr/frame limit (vs N/uframe) -// ... and also lets us avoid tracking split transactions -// that might collide at a given TT/hub. - if (ehci->pshadow [frame].ptr) - return 0; - - claimed = periodic_usecs (ehci, frame, uframe); - if (claimed > usecs) - return 0; + /* we "know" 2 and 4 uframe intervals were rejected; so + * for period 0, check _every_ microframe in the schedule. + */ + if (unlikely (period == 0)) { + do { + for (uframe = 0; uframe < 7; uframe++) { + claimed = periodic_usecs (ehci, frame, uframe); + if (claimed > usecs) + return 0; + } + } while ((frame += 1) < ehci->periodic_size); -// FIXME update to handle sub-frame periods - } while ((frame += period) < ehci->periodic_size); + /* just check the specified uframe, at that period */ + } else { + do { + claimed = periodic_usecs (ehci, frame, uframe); + if (claimed > usecs) + return 0; + } while ((frame += period) < ehci->periodic_size); + } // success! return 1; @@ -403,59 +477,64 @@ static int check_intr_schedule ( unsigned frame, unsigned uframe, const struct ehci_qh *qh, - u32 *c_maskp + __le32 *c_maskp ) { int retval = -ENOSPC; + u8 mask; + + if (qh->c_usecs && uframe >= 6) /* FSTN territory? */ + goto done; if (!check_period (ehci, frame, uframe, qh->period, qh->usecs)) goto done; if (!qh->c_usecs) { retval = 0; - *c_maskp = cpu_to_le32 (0); + *c_maskp = 0; goto done; } - /* This is a split transaction; check the bandwidth available for - * the completion too. Check both worst and best case gaps: worst - * case is SPLIT near uframe end, and CSPLIT near start ... best is - * vice versa. Difference can be almost two uframe times, but we - * reserve unnecessary bandwidth (waste it) this way. (Actually - * even better cases exist, like immediate device NAK.) - * - * FIXME don't even bother unless we know this TT is idle in that - * range of uframes ... for now, check_period() allows only one - * interrupt transfer per frame, so needn't check "TT busy" status - * when scheduling a split (QH, SITD, or FSTN). - * - * FIXME ehci 0.96 and above can use FSTNs + /* Make sure this tt's buffer is also available for CSPLITs. + * We pessimize a bit; probably the typical full speed case + * doesn't need the second CSPLIT. + * + * NOTE: both SPLIT and CSPLIT could be checked in just + * one smart pass... */ - if (!check_period (ehci, frame, uframe + qh->gap_uf + 1, - qh->period, qh->c_usecs)) - goto done; - if (!check_period (ehci, frame, uframe + qh->gap_uf, - qh->period, qh->c_usecs)) - goto done; + mask = 0x03 << (uframe + qh->gap_uf); + *c_maskp = cpu_to_le32 (mask << 8); - *c_maskp = cpu_to_le32 (0x03 << (8 + uframe + qh->gap_uf)); - retval = 0; + mask |= 1 << uframe; + if (tt_no_collision (ehci, qh->period, qh->dev, frame, mask)) { + if (!check_period (ehci, frame, uframe + qh->gap_uf + 1, + qh->period, qh->c_usecs)) + goto done; + if (!check_period (ehci, frame, uframe + qh->gap_uf, + qh->period, qh->c_usecs)) + goto done; + retval = 0; + } done: return retval; } +/* "first fit" scheduling policy used the first time through, + * or when the previous schedule slot can't be re-used. + */ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) { int status; unsigned uframe; - u32 c_mask; + __le32 c_mask; unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ + qh_refresh(ehci, qh); qh->hw_next = EHCI_LIST_END; frame = qh->start; /* reuse the previous schedule slots, if we can */ if (frame < qh->period) { - uframe = ffs (le32_to_cpup (&qh->hw_info2) & 0x00ff); + uframe = ffs (le32_to_cpup (&qh->hw_info2) & QH_SMASK); status = check_intr_schedule (ehci, frame, --uframe, qh, &c_mask); } else { @@ -468,86 +547,71 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) * uframes have enough periodic bandwidth available. */ if (status) { - frame = qh->period - 1; - do { - for (uframe = 0; uframe < 8; uframe++) { - status = check_intr_schedule (ehci, - frame, uframe, qh, - &c_mask); - if (status == 0) - break; - } - } while (status && frame--); + /* "normal" case, uframing flexible except with splits */ + if (qh->period) { + frame = qh->period - 1; + do { + for (uframe = 0; uframe < 8; uframe++) { + status = check_intr_schedule (ehci, + frame, uframe, qh, + &c_mask); + if (status == 0) + break; + } + } while (status && frame--); + + /* qh->period == 0 means every uframe */ + } else { + frame = 0; + status = check_intr_schedule (ehci, 0, 0, qh, &c_mask); + } if (status) goto done; qh->start = frame; /* reset S-frame and (maybe) C-frame masks */ - qh->hw_info2 &= ~0xffff; - qh->hw_info2 |= cpu_to_le32 (1 << uframe) | c_mask; + qh->hw_info2 &= __constant_cpu_to_le32(~(QH_CMASK | QH_SMASK)); + qh->hw_info2 |= qh->period + ? cpu_to_le32 (1 << uframe) + : __constant_cpu_to_le32 (QH_SMASK); + qh->hw_info2 |= c_mask; } else - dbg ("reused previous qh %p schedule", qh); + ehci_dbg (ehci, "reused qh %p schedule\n", qh); /* stuff into the periodic schedule */ - qh->qh_state = QH_STATE_LINKED; - dbg ("scheduled qh %p usecs %d/%d period %d.0 starting %d.%d (gap %d)", - qh, qh->usecs, qh->c_usecs, - qh->period, frame, uframe, qh->gap_uf); - do { - if (unlikely (ehci->pshadow [frame].ptr != 0)) { - -// FIXME -- just link toward the end, before any qh with a shorter period, -// AND accommodate it already having been linked here (after some other qh) -// AS WELL AS updating the schedule checking logic - - BUG (); - } else { - ehci->pshadow [frame].qh = qh_get (qh); - ehci->periodic [frame] = - QH_NEXT (qh->qh_dma); - } - wmb (); - frame += qh->period; - } while (frame < ehci->periodic_size); - - /* update per-qh bandwidth for usbfs */ - hcd_to_bus (&ehci->hcd)->bandwidth_allocated += - (qh->usecs + qh->c_usecs) / qh->period; - - /* maybe enable periodic schedule processing */ - if (!ehci->periodic_sched++) - status = enable_periodic (ehci); + status = qh_link_periodic (ehci, qh); done: return status; } static int intr_submit ( struct ehci_hcd *ehci, + struct usb_host_endpoint *ep, struct urb *urb, struct list_head *qtd_list, - int mem_flags + gfp_t mem_flags ) { unsigned epnum; unsigned long flags; struct ehci_qh *qh; - struct hcd_dev *dev; - int is_input; int status = 0; struct list_head empty; /* get endpoint and transfer/schedule data */ - epnum = usb_pipeendpoint (urb->pipe); - is_input = usb_pipein (urb->pipe); - if (is_input) - epnum |= 0x10; + epnum = ep->desc.bEndpointAddress; spin_lock_irqsave (&ehci->lock, flags); - dev = (struct hcd_dev *)urb->dev->hcpriv; + + if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, + &ehci_to_hcd(ehci)->flags))) { + status = -ESHUTDOWN; + goto done; + } /* get qh and force any scheduling errors */ INIT_LIST_HEAD (&empty); - qh = qh_append_tds (ehci, urb, &empty, epnum, &dev->ep [epnum]); - if (qh == 0) { + qh = qh_append_tds (ehci, urb, &empty, epnum, &ep->hcpriv); + if (qh == NULL) { status = -ENOMEM; goto done; } @@ -557,11 +621,11 @@ static int intr_submit ( } /* then queue the urb's tds to the qh */ - qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]); - BUG_ON (qh == 0); + qh = qh_append_tds (ehci, urb, qtd_list, epnum, &ep->hcpriv); + BUG_ON (qh == NULL); /* ... update usbfs periodic stats */ - hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs++; + ehci_to_hcd(ehci)->self.bandwidth_int_reqs++; done: spin_unlock_irqrestore (&ehci->lock, flags); @@ -576,13 +640,12 @@ done: /* ehci_iso_stream ops work with both ITD and SITD */ static struct ehci_iso_stream * -iso_stream_alloc (int mem_flags) +iso_stream_alloc (gfp_t mem_flags) { struct ehci_iso_stream *stream; - stream = kmalloc(sizeof *stream, mem_flags); - if (likely (stream != 0)) { - memset (stream, 0, sizeof(*stream)); + stream = kzalloc(sizeof *stream, mem_flags); + if (likely (stream != NULL)) { INIT_LIST_HEAD(&stream->td_list); INIT_LIST_HEAD(&stream->free_list); stream->next_uframe = -1; @@ -593,6 +656,7 @@ iso_stream_alloc (int mem_flags) static void iso_stream_init ( + struct ehci_hcd *ehci, struct ehci_iso_stream *stream, struct usb_device *dev, int pipe, @@ -612,11 +676,10 @@ iso_stream_init ( */ epnum = usb_pipeendpoint (pipe); is_input = usb_pipein (pipe) ? USB_DIR_IN : 0; + maxp = usb_maxpacket(dev, pipe, !is_input); if (is_input) { - maxp = dev->epmaxpacketin [epnum]; buf1 = (1 << 11); } else { - maxp = dev->epmaxpacketout [epnum]; buf1 = 0; } @@ -643,12 +706,21 @@ iso_stream_init ( } else { u32 addr; + int think_time; + int hs_transfers; addr = dev->ttport << 24; - addr |= dev->tt->hub->devnum << 16; + if (!ehci_is_TDI(ehci) + || (dev->tt->hub != + ehci_to_hcd(ehci)->self.root_hub)) + addr |= dev->tt->hub->devnum << 16; addr |= epnum << 8; addr |= dev->devnum; stream->usecs = HS_USECS_ISO (maxp); + think_time = dev->tt ? dev->tt->think_time : 0; + stream->tt_usecs = NS_TO_US (think_time + usb_calc_bus_time ( + dev->speed, is_input, 1, maxp)); + hs_transfers = max (1u, (maxp + 187) / 188); if (is_input) { u32 tmp; @@ -657,12 +729,11 @@ iso_stream_init ( stream->usecs = HS_USECS_ISO (1); stream->raw_mask = 1; - /* pessimistic c-mask */ - tmp = usb_calc_bus_time (USB_SPEED_FULL, 1, 0, maxp) - / (125 * 1000); - stream->raw_mask |= 3 << (tmp + 9); + /* c-mask as specified in USB 2.0 11.18.4 3.c */ + tmp = (1 << (hs_transfers + 2)) - 1; + stream->raw_mask |= tmp << (8 + 2); } else - stream->raw_mask = smask_out [maxp / 188]; + stream->raw_mask = smask_out [hs_transfers - 1]; bandwidth = stream->usecs + stream->c_usecs; bandwidth /= 1 << (interval + 2); @@ -688,7 +759,6 @@ iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream) */ if (stream->refcount == 1) { int is_in; - struct hcd_dev *dev = stream->udev->hcpriv; // BUG_ON (!list_empty(&stream->td_list)); @@ -718,7 +788,7 @@ iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream) is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0; stream->bEndpointAddress &= 0x0f; - dev->ep [is_in + stream->bEndpointAddress] = 0; + stream->ep->hcpriv = NULL; if (stream->rescheduled) { ehci_info (ehci, "ep%d%s-iso rescheduled " @@ -736,7 +806,7 @@ iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream) static inline struct ehci_iso_stream * iso_stream_get (struct ehci_iso_stream *stream) { - if (likely (stream != 0)) + if (likely (stream != NULL)) stream->refcount++; return stream; } @@ -745,34 +815,35 @@ static struct ehci_iso_stream * iso_stream_find (struct ehci_hcd *ehci, struct urb *urb) { unsigned epnum; - struct hcd_dev *dev; struct ehci_iso_stream *stream; + struct usb_host_endpoint *ep; unsigned long flags; epnum = usb_pipeendpoint (urb->pipe); if (usb_pipein(urb->pipe)) - epnum += 0x10; + ep = urb->dev->ep_in[epnum]; + else + ep = urb->dev->ep_out[epnum]; spin_lock_irqsave (&ehci->lock, flags); + stream = ep->hcpriv; - dev = (struct hcd_dev *)urb->dev->hcpriv; - stream = dev->ep [epnum]; - - if (unlikely (stream == 0)) { + if (unlikely (stream == NULL)) { stream = iso_stream_alloc(GFP_ATOMIC); - if (likely (stream != 0)) { + if (likely (stream != NULL)) { /* dev->ep owns the initial refcount */ - dev->ep[epnum] = stream; - iso_stream_init(stream, urb->dev, urb->pipe, + ep->hcpriv = stream; + stream->ep = ep; + iso_stream_init(ehci, stream, urb->dev, urb->pipe, urb->interval); } /* if dev->ep [epnum] is a QH, info1.maxpacket is nonzero */ } else if (unlikely (stream->hw_info1 != 0)) { ehci_dbg (ehci, "dev %s ep%d%s, not iso??\n", - urb->dev->devpath, epnum & 0x0f, - (epnum & 0x10) ? "in" : "out"); - stream = 0; + urb->dev->devpath, epnum, + usb_pipein(urb->pipe) ? "in" : "out"); + stream = NULL; } /* caller guarantees an eventual matching iso_stream_put */ @@ -784,18 +855,17 @@ iso_stream_find (struct ehci_hcd *ehci, struct urb *urb) /*-------------------------------------------------------------------------*/ -/* ehci_iso_sched ops can be shared, ITD-only, or SITD-only */ +/* ehci_iso_sched ops can be ITD-only or SITD-only */ static struct ehci_iso_sched * -iso_sched_alloc (unsigned packets, int mem_flags) +iso_sched_alloc (unsigned packets, gfp_t mem_flags) { struct ehci_iso_sched *iso_sched; int size = sizeof *iso_sched; size += packets * sizeof (struct ehci_iso_packet); - iso_sched = kmalloc (size, mem_flags); - if (likely (iso_sched != 0)) { - memset(iso_sched, 0, size); + iso_sched = kzalloc(size, mem_flags); + if (likely (iso_sched != NULL)) { INIT_LIST_HEAD (&iso_sched->td_list); } return iso_sched; @@ -834,7 +904,7 @@ itd_sched_init ( trans |= length << 16; uframe->transaction = cpu_to_le32 (trans); - /* might need to cross a buffer page within a td */ + /* might need to cross a buffer page within a uframe */ uframe->bufp = (buf & ~(u64)0x0fff); buf += length; if (unlikely ((uframe->bufp != (buf & ~(u64)0x0fff)))) @@ -860,7 +930,7 @@ itd_urb_transaction ( struct ehci_iso_stream *stream, struct ehci_hcd *ehci, struct urb *urb, - int mem_flags + gfp_t mem_flags ) { struct ehci_itd *itd; @@ -871,7 +941,7 @@ itd_urb_transaction ( unsigned long flags; sched = iso_sched_alloc (urb->number_of_packets, mem_flags); - if (unlikely (sched == 0)) + if (unlikely (sched == NULL)) return -ENOMEM; itd_sched_init (sched, stream, urb); @@ -896,7 +966,7 @@ itd_urb_transaction ( list_del (&itd->itd_list); itd_dma = itd->itd_dma; } else - itd = 0; + itd = NULL; if (!itd) { spin_unlock_irqrestore (&ehci->lock, flags); @@ -905,8 +975,9 @@ itd_urb_transaction ( spin_lock_irqsave (&ehci->lock, flags); } - if (unlikely (0 == itd)) { + if (unlikely (NULL == itd)) { iso_sched_free (stream, sched); + spin_unlock_irqrestore (&ehci->lock, flags); return -ENOMEM; } memset (itd, 0, sizeof *itd); @@ -992,6 +1063,7 @@ sitd_slot_ok ( /* for IN, check CSPLIT */ if (stream->c_usecs) { + uf = uframe & 7; max_used = 100 - stream->c_usecs; do { tmp = 1 << uf; @@ -1008,8 +1080,7 @@ sitd_slot_ok ( uframe += period_uframes; } while (uframe < mod); - stream->splits = stream->raw_mask << (uframe & 7); - cpu_to_le32s (&stream->splits); + stream->splits = cpu_to_le32(stream->raw_mask << (uframe & 7)); return 1; } @@ -1116,11 +1187,14 @@ iso_stream_schedule ( fail: iso_sched_free (stream, sched); - urb->hcpriv = 0; + urb->hcpriv = NULL; return status; ready: + /* report high speed start in uframes; full speed, in frames */ urb->start_frame = stream->next_uframe; + if (!stream->highspeed) + urb->start_frame >>= 3; return 0; } @@ -1131,6 +1205,7 @@ itd_init (struct ehci_iso_stream *stream, struct ehci_itd *itd) { int i; + /* it's been recently zeroed */ itd->hw_next = EHCI_LIST_END; itd->hw_bufp [0] = stream->buf0; itd->hw_bufp [1] = stream->buf1; @@ -1147,8 +1222,7 @@ itd_patch ( struct ehci_itd *itd, struct ehci_iso_sched *iso_sched, unsigned index, - u16 uframe, - int first + u16 uframe ) { struct ehci_iso_packet *uf = &iso_sched->packet [index]; @@ -1165,7 +1239,7 @@ itd_patch ( itd->hw_bufp_hi [pg] |= cpu_to_le32 ((u32)(uf->bufp >> 32)); /* iso_frame_desc[].offset must be strictly increasing */ - if (unlikely (!first && uf->cross)) { + if (unlikely (uf->cross)) { u64 bufp = uf->bufp + 4096; itd->pg = ++pg; itd->hw_bufp [pg] |= cpu_to_le32 (bufp & ~(u32)0); @@ -1194,7 +1268,7 @@ itd_link_urb ( struct ehci_iso_stream *stream ) { - int packet, first = 1; + int packet; unsigned next_uframe, uframe, frame; struct ehci_iso_sched *iso_sched = urb->hcpriv; struct ehci_itd *itd; @@ -1202,7 +1276,7 @@ itd_link_urb ( next_uframe = stream->next_uframe % mod; if (unlikely (list_empty(&stream->td_list))) { - hcd_to_bus (&ehci->hcd)->bandwidth_allocated + ehci_to_hcd(ehci)->self.bandwidth_allocated += stream->bandwidth; ehci_vdbg (ehci, "schedule devp %s ep%d%s-iso period %d start %d.%d\n", @@ -1212,11 +1286,11 @@ itd_link_urb ( next_uframe >> 3, next_uframe & 0x7); stream->start = jiffies; } - hcd_to_bus (&ehci->hcd)->bandwidth_isoc_reqs++; + ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; /* fill iTDs uframe by uframe */ - for (packet = 0, itd = 0; packet < urb->number_of_packets; ) { - if (itd == 0) { + for (packet = 0, itd = NULL; packet < urb->number_of_packets; ) { + if (itd == NULL) { /* ASSERT: we have all necessary itds */ // BUG_ON (list_empty (&iso_sched->td_list)); @@ -1227,7 +1301,6 @@ itd_link_urb ( list_move_tail (&itd->itd_list, &stream->td_list); itd->stream = iso_stream_get (stream); itd->urb = usb_get_urb (urb); - first = 1; itd_init (stream, itd); } @@ -1235,8 +1308,7 @@ itd_link_urb ( frame = next_uframe >> 3; itd->usecs [uframe] = stream->usecs; - itd_patch (itd, iso_sched, packet, uframe, first); - first = 0; + itd_patch (itd, iso_sched, packet, uframe); next_uframe += stream->interval; stream->depth += stream->interval; @@ -1247,14 +1319,14 @@ itd_link_urb ( if (((next_uframe >> 3) != frame) || packet == urb->number_of_packets) { itd_link (ehci, frame % ehci->periodic_size, itd); - itd = 0; + itd = NULL; } } stream->next_uframe = next_uframe; /* don't need that schedule data any more */ iso_sched_free (stream, iso_sched); - urb->hcpriv = 0; + urb->hcpriv = NULL; timer_action (ehci, TIMER_IO_WATCHDOG); if (unlikely (!ehci->periodic_sched++)) @@ -1311,8 +1383,8 @@ itd_complete ( } usb_put_urb (urb); - itd->urb = 0; - itd->stream = 0; + itd->urb = NULL; + itd->stream = NULL; list_move (&itd->itd_list, &stream->free_list); iso_stream_put (ehci, stream); @@ -1326,18 +1398,18 @@ itd_complete ( */ /* give urb back to the driver ... can be out-of-order */ - dev = usb_get_dev (urb->dev); + dev = urb->dev; ehci_urb_done (ehci, urb, regs); - urb = 0; + urb = NULL; /* defer stopping schedule; completion can submit */ ehci->periodic_sched--; if (unlikely (!ehci->periodic_sched)) (void) disable_periodic (ehci); - hcd_to_bus (&ehci->hcd)->bandwidth_isoc_reqs--; + ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; if (unlikely (list_empty (&stream->td_list))) { - hcd_to_bus (&ehci->hcd)->bandwidth_allocated + ehci_to_hcd(ehci)->self.bandwidth_allocated -= stream->bandwidth; ehci_vdbg (ehci, "deschedule devp %s ep%d%s-iso\n", @@ -1345,14 +1417,14 @@ itd_complete ( (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); } iso_stream_put (ehci, stream); - usb_put_dev (dev); return 1; } /*-------------------------------------------------------------------------*/ -static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) +static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, + gfp_t mem_flags) { int status = -EINVAL; unsigned long flags; @@ -1360,7 +1432,7 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) /* Get iso_stream head */ stream = iso_stream_find (ehci, urb); - if (unlikely (stream == 0)) { + if (unlikely (stream == NULL)) { ehci_dbg (ehci, "can't get iso stream\n"); return -ENOMEM; } @@ -1390,7 +1462,11 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) /* schedule ... need to lock */ spin_lock_irqsave (&ehci->lock, flags); - status = iso_stream_schedule (ehci, urb, stream); + if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, + &ehci_to_hcd(ehci)->flags))) + status = -ESHUTDOWN; + else + status = iso_stream_schedule (ehci, urb, stream); if (likely (status == 0)) itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); spin_unlock_irqrestore (&ehci->lock, flags); @@ -1444,18 +1520,17 @@ sitd_sched_init ( /* might need to cross a buffer page within a td */ packet->bufp = buf; - buf += length; - packet->buf1 = buf & ~0x0fff; + packet->buf1 = (buf + length) & ~0x0fff; if (packet->buf1 != (buf & ~(u64)0x0fff)) packet->cross = 1; /* OUT uses multiple start-splits */ if (stream->bEndpointAddress & USB_DIR_IN) continue; - length = 1 + (length / 188); - packet->buf1 |= length; + length = (length + 187) / 188; if (length > 1) /* BEGIN vs ALL */ - packet->buf1 |= 1 << 3; + length |= 1 << 3; + packet->buf1 |= length; } } @@ -1464,7 +1539,7 @@ sitd_urb_transaction ( struct ehci_iso_stream *stream, struct ehci_hcd *ehci, struct urb *urb, - int mem_flags + gfp_t mem_flags ) { struct ehci_sitd *sitd; @@ -1474,7 +1549,7 @@ sitd_urb_transaction ( unsigned long flags; iso_sched = iso_sched_alloc (urb->number_of_packets, mem_flags); - if (iso_sched == 0) + if (iso_sched == NULL) return -ENOMEM; sitd_sched_init (iso_sched, stream, urb); @@ -1499,7 +1574,7 @@ sitd_urb_transaction ( list_del (&sitd->sitd_list); sitd_dma = sitd->sitd_dma; } else - sitd = 0; + sitd = NULL; if (!sitd) { spin_unlock_irqrestore (&ehci->lock, flags); @@ -1550,10 +1625,9 @@ sitd_patch ( sitd->hw_buf_hi [0] = cpu_to_le32 (bufp >> 32); sitd->hw_buf [1] = cpu_to_le32 (uf->buf1); - if (uf->cross) { + if (uf->cross) bufp += 4096; - sitd->hw_buf_hi [1] = cpu_to_le32 (bufp >> 32); - } + sitd->hw_buf_hi [1] = cpu_to_le32 (bufp >> 32); sitd->index = index; } @@ -1587,20 +1661,20 @@ sitd_link_urb ( if (list_empty(&stream->td_list)) { /* usbfs ignores TT bandwidth */ - hcd_to_bus (&ehci->hcd)->bandwidth_allocated + ehci_to_hcd(ehci)->self.bandwidth_allocated += stream->bandwidth; ehci_vdbg (ehci, - "sched dev%s ep%d%s-iso [%d] %dms/%04x\n", + "sched devp %s ep%d%s-iso [%d] %dms/%04x\n", urb->dev->devpath, stream->bEndpointAddress & 0x0f, (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", (next_uframe >> 3) % ehci->periodic_size, stream->interval, le32_to_cpu (stream->splits)); stream->start = jiffies; } - hcd_to_bus (&ehci->hcd)->bandwidth_isoc_reqs++; + ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; /* fill sITDs frame by frame */ - for (packet = 0, sitd = 0; + for (packet = 0, sitd = NULL; packet < urb->number_of_packets; packet++) { @@ -1626,7 +1700,7 @@ sitd_link_urb ( /* don't need that schedule data any more */ iso_sched_free (stream, sched); - urb->hcpriv = 0; + urb->hcpriv = NULL; timer_action (ehci, TIMER_IO_WATCHDOG); if (!ehci->periodic_sched++) @@ -1637,7 +1711,7 @@ sitd_link_urb ( /*-------------------------------------------------------------------------*/ #define SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \ - | SITD_STS_XACT | SITD_STS_MMF | SITD_STS_STS) + | SITD_STS_XACT | SITD_STS_MMF) static unsigned sitd_complete ( @@ -1673,8 +1747,8 @@ sitd_complete ( } usb_put_urb (urb); - sitd->urb = 0; - sitd->stream = 0; + sitd->urb = NULL; + sitd->stream = NULL; list_move (&sitd->sitd_list, &stream->free_list); stream->depth -= stream->interval << 3; iso_stream_put (ehci, stream); @@ -1689,18 +1763,18 @@ sitd_complete ( */ /* give urb back to the driver */ - dev = usb_get_dev (urb->dev); + dev = urb->dev; ehci_urb_done (ehci, urb, regs); - urb = 0; + urb = NULL; /* defer stopping schedule; completion can submit */ ehci->periodic_sched--; if (!ehci->periodic_sched) (void) disable_periodic (ehci); - hcd_to_bus (&ehci->hcd)->bandwidth_isoc_reqs--; + ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; if (list_empty (&stream->td_list)) { - hcd_to_bus (&ehci->hcd)->bandwidth_allocated + ehci_to_hcd(ehci)->self.bandwidth_allocated -= stream->bandwidth; ehci_vdbg (ehci, "deschedule devp %s ep%d%s-iso\n", @@ -1708,27 +1782,21 @@ sitd_complete ( (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); } iso_stream_put (ehci, stream); - usb_put_dev (dev); return 1; } -static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) +static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, + gfp_t mem_flags) { int status = -EINVAL; unsigned long flags; struct ehci_iso_stream *stream; - // FIXME remove when csplits behave - if (usb_pipein(urb->pipe)) { - ehci_dbg (ehci, "no iso-IN split transactions yet\n"); - return -ENOMEM; - } - /* Get iso_stream head */ stream = iso_stream_find (ehci, urb); - if (stream == 0) { + if (stream == NULL) { ehci_dbg (ehci, "can't get iso stream\n"); return -ENOMEM; } @@ -1756,7 +1824,11 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) /* schedule ... need to lock */ spin_lock_irqsave (&ehci->lock, flags); - status = iso_stream_schedule (ehci, urb, stream); + if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, + &ehci_to_hcd(ehci)->flags))) + status = -ESHUTDOWN; + else + status = iso_stream_schedule (ehci, urb, stream); if (status == 0) sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); spin_unlock_irqrestore (&ehci->lock, flags); @@ -1770,7 +1842,7 @@ done: #else static inline int -sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) +sitd_submit (struct ehci_hcd *ehci, struct urb *urb, gfp_t mem_flags) { ehci_dbg (ehci, "split iso support is disabled\n"); return -ENOSYS; @@ -1804,7 +1876,7 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs) * Touches as few pages as possible: cache-friendly. */ now_uframe = ehci->next_uframe; - if (HCD_IS_RUNNING (ehci->hcd.state)) + if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) clock = readl (&ehci->regs->frame_index); else clock = now_uframe + mod - 1; @@ -1812,7 +1884,7 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs) for (;;) { union ehci_shadow q, *q_p; - u32 type, *hw_p; + __le32 type, *hw_p; unsigned uframes; /* don't scan past the live uframe */ @@ -1833,10 +1905,12 @@ restart: type = Q_NEXT_TYPE (*hw_p); modified = 0; - while (q.ptr != 0) { + while (q.ptr != NULL) { unsigned uf; union ehci_shadow temp; + int live; + live = HC_IS_RUNNING (ehci_to_hcd(ehci)->state); switch (type) { case Q_TYPE_QH: /* handle any completions */ @@ -1845,8 +1919,8 @@ restart: q = q.qh->qh_next; modified = qh_completions (ehci, temp.qh, regs); if (unlikely (list_empty (&temp.qh->qtd_list))) - intr_deschedule (ehci, temp.qh, 0); - qh_put (ehci, temp.qh); + intr_deschedule (ehci, temp.qh); + qh_put (temp.qh); break; case Q_TYPE_FSTN: /* for "save place" FSTNs, look at QH entries @@ -1861,7 +1935,7 @@ restart: case Q_TYPE_ITD: /* skip itds for later in the frame */ rmb (); - for (uf = uframes; uf < 8; uf++) { + for (uf = live ? uframes : 8; uf < 8; uf++) { if (0 == (q.itd->hw_transaction [uf] & ITD_ACTIVE)) continue; @@ -1885,7 +1959,8 @@ restart: q = *q_p; break; case Q_TYPE_SITD: - if (q.sitd->hw_results & SITD_ACTIVE) { + if ((q.sitd->hw_results & SITD_ACTIVE) + && live) { q_p = &q.sitd->sitd_next; hw_p = &q.sitd->hw_next; type = Q_NEXT_TYPE (q.sitd->hw_next); @@ -1903,7 +1978,7 @@ restart: dbg ("corrupt type %d frame %d shadow %p", type, frame, q.ptr); // BUG (); - q.ptr = 0; + q.ptr = NULL; } /* assume completion callbacks modify the queue */ @@ -1924,7 +1999,7 @@ restart: if (now_uframe == clock) { unsigned now; - if (!HCD_IS_RUNNING (ehci->hcd.state)) + if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) break; ehci->next_uframe = now_uframe; now = readl (&ehci->regs->frame_index) % mod;