X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fehci-sched.c;h=5871944e61459cca9cbd9fa85a992186b838c691;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=0000c460850ba48c7dcfc2cd0d5919713b8115be;hpb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;p=linux-2.6.git diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 0000c4608..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 @@ -59,39 +59,28 @@ periodic_next_shadow (union ehci_shadow *periodic, __le32 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]; __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 = NULL; - - 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? */ @@ -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; @@ -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 (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 = NULL; - ehci->periodic_sched--; + 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->kref.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; @@ -407,6 +481,10 @@ static int check_intr_schedule ( ) { 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; @@ -416,33 +494,33 @@ static int check_intr_schedule ( 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; @@ -450,12 +528,13 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) __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 &= ~__constant_cpu_to_le32(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] = NULL; + 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,33 +815,34 @@ 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"); + urb->dev->devpath, epnum, + usb_pipein(urb->pipe) ? "in" : "out"); stream = NULL; } @@ -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); @@ -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; @@ -1119,7 +1191,10 @@ fail: 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; } @@ -1130,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; @@ -1146,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]; @@ -1164,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); @@ -1193,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; @@ -1201,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", @@ -1211,7 +1286,7 @@ 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 = NULL; packet < urb->number_of_packets; ) { @@ -1226,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); } @@ -1234,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; @@ -1325,7 +1398,7 @@ 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 = NULL; @@ -1333,10 +1406,10 @@ itd_complete ( 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", @@ -1344,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; @@ -1359,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; } @@ -1389,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); @@ -1443,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; } } @@ -1463,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; @@ -1473,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); @@ -1549,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; } @@ -1586,17 +1661,17 @@ 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 = NULL; @@ -1636,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 ( @@ -1688,7 +1763,7 @@ sitd_complete ( */ /* give urb back to the driver */ - dev = usb_get_dev (urb->dev); + dev = urb->dev; ehci_urb_done (ehci, urb, regs); urb = NULL; @@ -1696,10 +1771,10 @@ sitd_complete ( 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", @@ -1707,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; } @@ -1755,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); @@ -1769,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; @@ -1803,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; @@ -1832,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 */ @@ -1844,7 +1919,7 @@ 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); + intr_deschedule (ehci, temp.qh); qh_put (temp.qh); break; case Q_TYPE_FSTN: @@ -1860,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; @@ -1884,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); @@ -1923,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;