X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fehci-sched.c;h=65c402a0fa7a10d92e06035eb52c02afc408a075;hb=refs%2Fheads%2Fvserver;hp=88419c6823a835a6f6980019bebaf30df6f536f8;hpb=76828883507a47dae78837ab5dec5a5b4513c667;p=linux-2.6.git diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 88419c682..65c402a0f 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1,7 +1,7 @@ /* * 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 * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your @@ -163,6 +163,190 @@ static int same_tt (struct usb_device *dev1, struct usb_device *dev2) return 1; } +#ifdef CONFIG_USB_EHCI_TT_NEWSCHED + +/* Which uframe does the low/fullspeed transfer start in? + * + * The parameter is the mask of ssplits in "H-frame" terms + * and this returns the transfer start uframe in "B-frame" terms, + * which allows both to match, e.g. a ssplit in "H-frame" uframe 0 + * will cause a transfer in "B-frame" uframe 0. "B-frames" lag + * "H-frames" by 1 uframe. See the EHCI spec sec 4.5 and figure 4.7. + */ +static inline unsigned char tt_start_uframe(struct ehci_hcd *ehci, __le32 mask) +{ + unsigned char smask = QH_SMASK & le32_to_cpu(mask); + if (!smask) { + ehci_err(ehci, "invalid empty smask!\n"); + /* uframe 7 can't have bw so this will indicate failure */ + return 7; + } + return ffs(smask) - 1; +} + +static const unsigned char +max_tt_usecs[] = { 125, 125, 125, 125, 125, 125, 30, 0 }; + +/* carryover low/fullspeed bandwidth that crosses uframe boundries */ +static inline void carryover_tt_bandwidth(unsigned short tt_usecs[8]) +{ + int i; + for (i=0; i<7; i++) { + if (max_tt_usecs[i] < tt_usecs[i]) { + tt_usecs[i+1] += tt_usecs[i] - max_tt_usecs[i]; + tt_usecs[i] = max_tt_usecs[i]; + } + } +} + +/* How many of the tt's periodic downstream 1000 usecs are allocated? + * + * While this measures the bandwidth in terms of usecs/uframe, + * the low/fullspeed bus has no notion of uframes, so any particular + * low/fullspeed transfer can "carry over" from one uframe to the next, + * since the TT just performs downstream transfers in sequence. + * + * For example two seperate 100 usec transfers can start in the same uframe, + * and the second one would "carry over" 75 usecs into the next uframe. + */ +static void +periodic_tt_usecs ( + struct ehci_hcd *ehci, + struct usb_device *dev, + unsigned frame, + unsigned short tt_usecs[8] +) +{ + __le32 *hw_p = &ehci->periodic [frame]; + union ehci_shadow *q = &ehci->pshadow [frame]; + unsigned char uf; + + memset(tt_usecs, 0, 16); + + while (q->ptr) { + switch (Q_NEXT_TYPE(*hw_p)) { + case Q_TYPE_ITD: + hw_p = &q->itd->hw_next; + q = &q->itd->itd_next; + continue; + case Q_TYPE_QH: + if (same_tt(dev, q->qh->dev)) { + uf = tt_start_uframe(ehci, q->qh->hw_info2); + tt_usecs[uf] += q->qh->tt_usecs; + } + hw_p = &q->qh->hw_next; + q = &q->qh->qh_next; + continue; + case Q_TYPE_SITD: + if (same_tt(dev, q->sitd->urb->dev)) { + uf = tt_start_uframe(ehci, q->sitd->hw_uframe); + tt_usecs[uf] += q->sitd->stream->tt_usecs; + } + hw_p = &q->sitd->hw_next; + q = &q->sitd->sitd_next; + continue; + // case Q_TYPE_FSTN: + default: + ehci_dbg(ehci, + "ignoring periodic frame %d FSTN\n", frame); + hw_p = &q->fstn->hw_next; + q = &q->fstn->fstn_next; + } + } + + carryover_tt_bandwidth(tt_usecs); + + if (max_tt_usecs[7] < tt_usecs[7]) + ehci_err(ehci, "frame %d tt sched overrun: %d usecs\n", + frame, tt_usecs[7] - max_tt_usecs[7]); +} + +/* + * Return true if the device's tt's downstream bus is available for a + * periodic transfer of the specified length (usecs), starting at the + * specified frame/uframe. Note that (as summarized in section 11.19 + * of the usb 2.0 spec) TTs can buffer multiple transactions for each + * uframe. + * + * The uframe parameter is when the fullspeed/lowspeed transfer + * should be executed in "B-frame" terms, which is the same as the + * highspeed ssplit's uframe (which is in "H-frame" terms). For example + * a ssplit in "H-frame" 0 causes a transfer in "B-frame" 0. + * See the EHCI spec sec 4.5 and fig 4.7. + * + * This checks if the full/lowspeed bus, at the specified starting uframe, + * has the specified bandwidth available, according to rules listed + * in USB 2.0 spec section 11.18.1 fig 11-60. + * + * This does not check if the transfer would exceed the max ssplit + * limit of 16, specified in USB 2.0 spec section 11.18.4 requirement #4, + * since proper scheduling limits ssplits to less than 16 per uframe. + */ +static int tt_available ( + struct ehci_hcd *ehci, + unsigned period, + struct usb_device *dev, + unsigned frame, + unsigned uframe, + u16 usecs +) +{ + if ((period == 0) || (uframe >= 7)) /* error */ + return 0; + + for (; frame < ehci->periodic_size; frame += period) { + unsigned short tt_usecs[8]; + + periodic_tt_usecs (ehci, dev, frame, tt_usecs); + + ehci_vdbg(ehci, "tt frame %d check %d usecs start uframe %d in" + " schedule %d/%d/%d/%d/%d/%d/%d/%d\n", + frame, usecs, uframe, + tt_usecs[0], tt_usecs[1], tt_usecs[2], tt_usecs[3], + tt_usecs[4], tt_usecs[5], tt_usecs[6], tt_usecs[7]); + + if (max_tt_usecs[uframe] <= tt_usecs[uframe]) { + ehci_vdbg(ehci, "frame %d uframe %d fully scheduled\n", + frame, uframe); + return 0; + } + + /* special case for isoc transfers larger than 125us: + * the first and each subsequent fully used uframe + * must be empty, so as to not illegally delay + * already scheduled transactions + */ + if (125 < usecs) { + int ufs = (usecs / 125) - 1; + int i; + for (i = uframe; i < (uframe + ufs) && i < 8; i++) + if (0 < tt_usecs[i]) { + ehci_vdbg(ehci, + "multi-uframe xfer can't fit " + "in frame %d uframe %d\n", + frame, i); + return 0; + } + } + + tt_usecs[uframe] += usecs; + + carryover_tt_bandwidth(tt_usecs); + + /* fail if the carryover pushed bw past the last uframe's limit */ + if (max_tt_usecs[7] < tt_usecs[7]) { + ehci_vdbg(ehci, + "tt unavailable usecs %d frame %d uframe %d\n", + usecs, frame, uframe); + return 0; + } + } + + return 1; +} + +#else + /* return true iff the device's transaction translator is available * for a periodic transfer starting at the specified frame, using * all the uframes in the mask. @@ -237,6 +421,8 @@ static int tt_no_collision ( return 1; } +#endif /* CONFIG_USB_EHCI_TT_NEWSCHED */ + /*-------------------------------------------------------------------------*/ static int enable_periodic (struct ehci_hcd *ehci) @@ -427,7 +613,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) /*-------------------------------------------------------------------------*/ static int check_period ( - struct ehci_hcd *ehci, + struct ehci_hcd *ehci, unsigned frame, unsigned uframe, unsigned period, @@ -443,7 +629,7 @@ static int check_period ( /* * 80% periodic == 100 usec/uframe available - * convert "usecs we need" to "max already claimed" + * convert "usecs we need" to "max already claimed" */ usecs = 100 - usecs; @@ -473,15 +659,15 @@ static int check_period ( } static int check_intr_schedule ( - struct ehci_hcd *ehci, + struct ehci_hcd *ehci, unsigned frame, unsigned uframe, const struct ehci_qh *qh, __le32 *c_maskp ) { - int retval = -ENOSPC; - u8 mask; + int retval = -ENOSPC; + u8 mask = 0; if (qh->c_usecs && uframe >= 6) /* FSTN territory? */ goto done; @@ -494,10 +680,28 @@ static int check_intr_schedule ( goto done; } +#ifdef CONFIG_USB_EHCI_TT_NEWSCHED + if (tt_available (ehci, qh->period, qh->dev, frame, uframe, + qh->tt_usecs)) { + unsigned i; + + /* TODO : this may need FSTN for SSPLIT in uframe 5. */ + for (i=uframe+1; i<8 && iperiod, qh->c_usecs)) + goto done; + else + mask |= 1 << i; + + retval = 0; + + *c_maskp = cpu_to_le32 (mask << 8); + } +#else /* 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... */ @@ -514,6 +718,7 @@ static int check_intr_schedule ( goto done; retval = 0; } +#endif done: return retval; } @@ -523,7 +728,7 @@ done: */ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) { - int status; + int status; unsigned uframe; __le32 c_mask; unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ @@ -579,7 +784,7 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) ehci_dbg (ehci, "reused qh %p schedule\n", qh); /* stuff into the periodic schedule */ - status = qh_link_periodic (ehci, qh); + status = qh_link_periodic (ehci, qh); done: return status; } @@ -864,9 +1069,8 @@ iso_sched_alloc (unsigned packets, gfp_t mem_flags) int size = sizeof *iso_sched; size += packets * sizeof (struct ehci_iso_packet); - iso_sched = kmalloc (size, mem_flags); + iso_sched = kzalloc(size, mem_flags); if (likely (iso_sched != NULL)) { - memset(iso_sched, 0, size); INIT_LIST_HEAD (&iso_sched->td_list); } return iso_sched; @@ -1048,12 +1252,21 @@ sitd_slot_ok ( frame = uframe >> 3; uf = uframe & 7; +#ifdef CONFIG_USB_EHCI_TT_NEWSCHED + /* The tt's fullspeed bus bandwidth must be available. + * tt_available scheduling guarantees 10+% for control/bulk. + */ + if (!tt_available (ehci, period_uframes << 3, + stream->udev, frame, uf, stream->tt_usecs)) + return 0; +#else /* tt must be idle for start(s), any gap, and csplit. * assume scheduling slop leaves 10+% for control/bulk. */ if (!tt_no_collision (ehci, period_uframes << 3, stream->udev, frame, mask)) return 0; +#endif /* check starts (OUT uses more than one) */ max_used = 100 - stream->usecs; @@ -1340,8 +1553,7 @@ itd_link_urb ( static unsigned itd_complete ( struct ehci_hcd *ehci, - struct ehci_itd *itd, - struct pt_regs *regs + struct ehci_itd *itd ) { struct urb *urb = itd->urb; struct usb_iso_packet_descriptor *desc; @@ -1399,8 +1611,8 @@ itd_complete ( */ /* give urb back to the driver ... can be out-of-order */ - dev = usb_get_dev (urb->dev); - ehci_urb_done (ehci, urb, regs); + dev = urb->dev; + ehci_urb_done (ehci, urb); urb = NULL; /* defer stopping schedule; completion can submit */ @@ -1418,7 +1630,6 @@ itd_complete ( (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); } iso_stream_put (ehci, stream); - usb_put_dev (dev); return 1; } @@ -1469,7 +1680,7 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, status = -ESHUTDOWN; else status = iso_stream_schedule (ehci, urb, stream); - if (likely (status == 0)) + if (likely (status == 0)) itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); spin_unlock_irqrestore (&ehci->lock, flags); @@ -1526,7 +1737,7 @@ sitd_sched_init ( if (packet->buf1 != (buf & ~(u64)0x0fff)) packet->cross = 1; - /* OUT uses multiple start-splits */ + /* OUT uses multiple start-splits */ if (stream->bEndpointAddress & USB_DIR_IN) continue; length = (length + 187) / 188; @@ -1713,13 +1924,12 @@ sitd_link_urb ( /*-------------------------------------------------------------------------*/ #define SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \ - | SITD_STS_XACT | SITD_STS_MMF) + | SITD_STS_XACT | SITD_STS_MMF) static unsigned sitd_complete ( struct ehci_hcd *ehci, - struct ehci_sitd *sitd, - struct pt_regs *regs + struct ehci_sitd *sitd ) { struct urb *urb = sitd->urb; struct usb_iso_packet_descriptor *desc; @@ -1765,8 +1975,8 @@ sitd_complete ( */ /* give urb back to the driver */ - dev = usb_get_dev (urb->dev); - ehci_urb_done (ehci, urb, regs); + dev = urb->dev; + ehci_urb_done (ehci, urb); urb = NULL; /* defer stopping schedule; completion can submit */ @@ -1784,7 +1994,6 @@ sitd_complete ( (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); } iso_stream_put (ehci, stream); - usb_put_dev (dev); return 1; } @@ -1832,7 +2041,7 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, status = -ESHUTDOWN; else status = iso_stream_schedule (ehci, urb, stream); - if (status == 0) + if (status == 0) sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); spin_unlock_irqrestore (&ehci->lock, flags); @@ -1854,8 +2063,7 @@ sitd_submit (struct ehci_hcd *ehci, struct urb *urb, gfp_t mem_flags) static inline unsigned sitd_complete ( struct ehci_hcd *ehci, - struct ehci_sitd *sitd, - struct pt_regs *regs + struct ehci_sitd *sitd ) { ehci_err (ehci, "sitd_complete %p?\n", sitd); return 0; @@ -1866,7 +2074,7 @@ sitd_complete ( /*-------------------------------------------------------------------------*/ static void -scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs) +scan_periodic (struct ehci_hcd *ehci) { unsigned frame, clock, now_uframe, mod; unsigned modified; @@ -1920,7 +2128,7 @@ restart: temp.qh = qh_get (q.qh); type = Q_NEXT_TYPE (q.qh->hw_next); q = q.qh->qh_next; - modified = qh_completions (ehci, temp.qh, regs); + modified = qh_completions (ehci, temp.qh); if (unlikely (list_empty (&temp.qh->qtd_list))) intr_deschedule (ehci, temp.qh); qh_put (temp.qh); @@ -1958,7 +2166,7 @@ restart: *hw_p = q.itd->hw_next; type = Q_NEXT_TYPE (q.itd->hw_next); wmb(); - modified = itd_complete (ehci, q.itd, regs); + modified = itd_complete (ehci, q.itd); q = *q_p; break; case Q_TYPE_SITD: @@ -1974,7 +2182,7 @@ restart: *hw_p = q.sitd->hw_next; type = Q_NEXT_TYPE (q.sitd->hw_next); wmb(); - modified = sitd_complete (ehci, q.sitd, regs); + modified = sitd_complete (ehci, q.sitd); q = *q_p; break; default: @@ -2015,5 +2223,5 @@ restart: now_uframe++; now_uframe %= mod; } - } + } }