/*
* 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
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.
return 1;
}
+#endif /* CONFIG_USB_EHCI_TT_NEWSCHED */
+
/*-------------------------------------------------------------------------*/
static int enable_periodic (struct ehci_hcd *ehci)
/*-------------------------------------------------------------------------*/
static int check_period (
- struct ehci_hcd *ehci,
+ struct ehci_hcd *ehci,
unsigned frame,
unsigned uframe,
unsigned 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;
}
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;
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 && i<uframe+4; i++)
+ if (!check_period (ehci, frame, i,
+ qh->period, 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...
*/
goto done;
retval = 0;
}
+#endif
done:
return retval;
}
*/
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 */
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;
}
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;
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;
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;
*/
/* 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 */
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
}
iso_stream_put (ehci, stream);
- usb_put_dev (dev);
return 1;
}
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);
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;
/*-------------------------------------------------------------------------*/
#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;
*/
/* 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 */
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
}
iso_stream_put (ehci, stream);
- usb_put_dev (dev);
return 1;
}
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);
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;
/*-------------------------------------------------------------------------*/
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;
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);
*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:
*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:
now_uframe++;
now_uframe %= mod;
}
- }
+ }
}