fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / usb / host / ehci-sched.c
index 88419c6..65c402a 100644 (file)
@@ -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 && 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...
         */
@@ -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;
                }
-       } 
+       }
 }