Ok here's the biggest patch... :)

This adds a function "tt_available" and helper functions that determine if
a particular transaction translator is "available" for a periodic
transfer.  This is similar to the existing "tt_no_collision" but that
function simply uses the ssplit/csplit masks to prevent multiple
ssplits/csplits during the same uframe, while this new function does
downstream bus scheduling, i.e. using the tt_usecs from each ehci_qh or
ehci_iso_stream to schedule transfers when the downstream low/full speed
bus is available.  This adheres to the USB 2.0 spec section 11.18.1
(figure 11-60) and section 11.18.4 requirement #4.  This does allow 
multiple ssplits/csplits during a single uframe, up to 16 as specified by 
the USB 2.0 spec.

Note this only adds the function, later patches change the driver to use 
it and remove the tt_no_collision function.

Signed-off-by: Dan Streetman <[EMAIL PROTECTED]>



diff -urpN a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
--- a/drivers/usb/host/ehci-sched.c     2005-08-03 12:25:25.000000000 -0400
+++ b/drivers/usb/host/ehci-sched.c     2005-08-03 12:43:41.000000000 -0400
@@ -163,6 +163,196 @@ static int same_tt (struct usb_device *d
                return 1;
 }
 
+static const unsigned char
+MAX_TT_USECS[] = { 125, 125, 125, 125, 125, 125, 30, 0 };
+
+/* FIXME - can have ssplits in uframe 7 with FSTN */
+static const unsigned char
+MAX_TT_SSPLIT[] = { 16, 16, 16, 16, 16, 16, 0, 0 };
+
+struct tt_frame_bw {
+       short usecs[8];         /* used usecs per uframe */
+       char ssplits[8];        /* number of ssplits per uframe */
+};
+
+static void
+update_tt_frame_bw (struct tt_frame_bw *bw, unsigned uf, int tt_usecs)
+{
+       if (!uf) {
+               dbg ("%s : no ssplit, is this FSTN?\n", __FUNCTION__);
+               return;
+       }
+
+       do {
+               bw->usecs[uf] += tt_usecs;
+               tt_usecs = bw->usecs[uf] - MAX_TT_USECS[uf];
+               if (0 < tt_usecs)
+                       bw->usecs[uf] = MAX_TT_USECS[uf];
+       } while ((0 < tt_usecs) && (++uf < 7));
+}
+
+/* how many of the tt's periodic downstream 1000 usecs are allocated? */
+static void
+calc_periodic_tt_usecs (
+       struct ehci_hcd *ehci,
+       struct usb_device *dev,
+       unsigned frame,
+       struct tt_frame_bw *bw
+)
+{
+       union ehci_shadow       here;
+       __le32                  type;
+
+       memset(bw, 0, sizeof(*bw));
+
+       here = ehci->pshadow [frame];
+       type = Q_NEXT_TYPE (ehci->periodic [frame]);
+       while (here.ptr) {
+               switch (type) {
+               case Q_TYPE_ITD:
+                       type = Q_NEXT_TYPE (here.itd->hw_next);
+                       here = here.itd->itd_next;
+                       continue;
+               case Q_TYPE_QH:
+                       if (same_tt (dev, here.qh->dev)) {
+                               unsigned uf = ffs (0x3f &
+                                       le32_to_cpu (here.qh->hw_info2));
+
+                               /* the ssplit is 1 uframe before the
+                                * tt downstream transfer
+                                */
+                               if (uf)
+                                       bw->ssplits[uf-1]++;
+
+                               update_tt_frame_bw (bw, uf,
+                                       here.qh->tt_usecs);
+                       }
+                       type = Q_NEXT_TYPE (here.qh->hw_next);
+                       here = here.qh->qh_next;
+                       continue;
+               case Q_TYPE_SITD:
+                       if (same_tt (dev, here.sitd->urb->dev)) {
+                               int i;
+                               long smask = le32_to_cpu (here.sitd->hw_uframe);
+                               unsigned uf = ffs (0x3f & smask);
+
+                               /* isoc out can have multiple ssplits,
+                                * each ssplit 1 uframe before its
+                                * tt downstream transfer
+                                */
+                               for (i=0; i<8; i++)
+                                       if (test_bit(i, &smask))
+                                               bw->ssplits[i]++;
+
+                               update_tt_frame_bw (bw, uf,
+                                       here.sitd->stream->tt_usecs);
+                       }
+                       type = Q_NEXT_TYPE (here.sitd->hw_next);
+                       here = here.sitd->sitd_next;
+                       continue;
+               // case Q_TYPE_FSTN:
+               default:
+                       ehci_dbg (ehci,
+                                 "ignoring periodic frame %d bogus type %d\n",
+                                 frame, type);
+               }
+       }
+}
+
+/* return true if the device's tt's downstream bus is available
+ * for a periodic transfer starting at the specified frame/uframe.
+ * Note that the uframe parameter is when the downstream transfer
+ * should be executed, which is 1 uframe after the ssplit.
+ * This checks 2 things:
+ * 1) if the bus, at the specified starting uframe,
+ *    has the specified bandwidth (max 125 us).
+ *    note no periodic xfers allowed in uframe 7,
+ *    and the max bw in uframe 6 is only (approx calc) ~30 us.
+ *    (USB 2.0 spec section 11.18.1 fig 11-60)
+ * 2) if the transfer would exceed the max ssplit limit of 16
+ *    (USB 2.0 spec section 11.18.4 requirement #4).
+ */
+static int tt_available (
+       struct ehci_hcd         *ehci,
+       unsigned                period,
+       struct usb_device       *dev,
+       unsigned                frame,
+       unsigned                uframe,
+       u16                     tt_usecs
+)
+{
+       if ((period == 0) || (uframe >= 7))     /* error */
+               return 0;
+
+       /* not yet supported : FSTN (ssplit in uframe 7) */
+       if (uframe == 0)
+               return 0;
+
+       for (; frame < ehci->periodic_size; frame += period) {
+               struct tt_frame_bw bw;
+               int i;
+
+               calc_periodic_tt_usecs (ehci, dev, frame, &bw);
+
+               ehci_vdbg (ehci, "tt frame %d check %d usecs start uframe %d in"
+                       " schedule %d/%d/%d/%d/%d/%d/%d/%d\n",
+                       frame, tt_usecs, uframe,
+                       bw.usecs[0], bw.usecs[1], bw.usecs[2], bw.usecs[3],
+                       bw.usecs[4], bw.usecs[5], bw.usecs[6], bw.usecs[7]);
+
+               /* reject if desired uframe is already 100% scheduled */
+               if (MAX_TT_USECS[uframe] <= bw.usecs[uframe]) {
+                       ehci_dbg (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 < tt_usecs) {
+                       int ufs = tt_usecs / 125;
+                       for (i = uframe; i < (uframe + ufs) && i < 8; i++)
+                               if (0 < bw.usecs[i]) {
+                                       ehci_dbg(ehci, "Isoc transfer would "
+                                               "delay existing transfer in "
+                                               "frame %d uframe %d\n",
+                                               frame, uframe);
+                                       return 0;
+                               }
+               }
+
+               update_tt_frame_bw (&bw, uframe, tt_usecs);
+
+               /* overscheduled if any bw has rolled over into uframe 7 */
+               if (bw.usecs[7]) {
+                       ehci_dbg (ehci,
+                               "tt does not have %d usecs available "
+                               "in frame %d uframe %d\n",
+                               tt_usecs, frame, uframe);
+                       return 0;
+               }
+
+               /* no uframe can have more than MAX_TT_SSPLIT ssplits */
+               for (i = 0; i < 8; i++)
+                       if (MAX_TT_SSPLIT[i] < bw.ssplits[i]) {
+                               ehci_dbg (ehci,
+                                       "tt max ssplit in frame %d uframe %d\n",
+                                       frame, uframe);
+                               return 0;
+                       }
+       }
+
+       /* tt's downstream bus has available bw */
+       ehci_dbg (ehci,
+               "tt available for %d usec transfer starting "
+               "in frame %d uframe %d every %d frames\n",
+               tt_usecs, frame % period, uframe, period);
+       return 1;
+}
+
 /* 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.


-------------------------------------------------------
SF.Net email is Sponsored by the Better Software Conference & EXPO
September 19-22, 2005 * San Francisco, CA * Development Lifecycle Practices
Agile & Plan-Driven Development * Managing Projects & Teams * Testing & QA
Security * Process Improvement & Measurement * http://www.sqe.com/bsce5sf
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to