# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#                  ChangeSet    1.588   -> 1.588.1.1
#       drivers/usb/host/ehci-dbg.c     1.3     -> 1.4    
#       drivers/usb/host/ehci.h 1.3     -> 1.4    
#       drivers/usb/host/ehci-sched.c   1.12    -> 1.13   
#       drivers/usb/core/hcd.h  1.7     -> 1.8    
#       drivers/usb/core/hcd.c  1.20    -> 1.21   
#       drivers/usb/host/ehci-hcd.c     1.17    -> 1.18   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/05/26      [EMAIL PROTECTED]     1.588.1.1
# [PATCH] ehci split interrupt transactions
# 
# This patch lets more devices hook up to USB 2.0 hubs, stuff
# like keyboards, mice, hubs that hasn't worked yet:
# 
# - schedules full/low speed interrupt transactions
# - tracks CSPLIT bandwidth for full/low speed interrupt
#   transactions
# - moves some bus bandwidth calculation out of the EHCI code
# - makes the bandwidth calculation primitive public, and
#   adds kerneldoc for it
# 
# It still takes a scheduling shortcut, placing at most one
# interrupt transaction in a frame (vs potentially over 100),
# but it should do for now.
# --------------------------------------------
#
diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
--- a/drivers/usb/core/hcd.c    Tue May 28 23:49:09 2002
+++ b/drivers/usb/core/hcd.c    Tue May 28 23:49:09 2002
@@ -745,12 +745,18 @@
 
 /*-------------------------------------------------------------------------*/
 
-/*
- * usb_calc_bus_time:
+/**
+ * usb_calc_bus_time: approximate periodic transaction time in nanoseconds
+ * @speed: from dev->speed; USB_SPEED_{LOW,FULL,HIGH}
+ * @is_input: true iff the transaction sends data to the host
+ * @is_isoc: true for isochronous transactions, false for interrupt ones
+ * @bytecount: how many bytes in the transaction.
+ *
  * Returns approximate bus time in nanoseconds for a periodic transaction.
- * See USB 2.0 spec section 5.11.3
+ * See USB 2.0 spec section 5.11.3; only periodic transfers need to be
+ * scheduled in software, this function is only used for such scheduling.
  */
-static long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
+long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
 {
        unsigned long   tmp;
 
@@ -772,14 +778,18 @@
                        return (9107L + BW_HOST_DELAY + tmp);
                }
        case USB_SPEED_HIGH:    /* ISOC or INTR */
-               // FIXME merge from EHCI code; caller will need to handle
-               // each part of a split separately.
-               return 0;
+               // FIXME adjust for input vs output
+               if (isoc)
+                       tmp = HS_USECS (bytecount);
+               else
+                       tmp = HS_USECS_ISO (bytecount);
+               return tmp;
        default:
                dbg ("bogus device speed!");
                return -1;
        }
 }
+EXPORT_SYMBOL (usb_calc_bus_time);
 
 /*
  * usb_check_bandwidth():
diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
--- a/drivers/usb/core/hcd.h    Tue May 28 23:49:09 2002
+++ b/drivers/usb/core/hcd.h    Tue May 28 23:49:09 2002
@@ -263,6 +263,22 @@
 
 extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb);
 
+/*
+ * Ceiling microseconds (typical) for that many bytes at high speed
+ * ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed
+ * to preallocate bandwidth)
+ */
+#define USB2_HOST_DELAY        5       /* nsec, guess */
+#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \
+       + ((2083UL * (3167 + BitTime (bytes)))/1000) \
+       + USB2_HOST_DELAY)
+#define HS_USECS_ISO(bytes) NS_TO_US ( ((long)(38 * 8 * 2.083)) \
+       + ((2083UL * (3167 + BitTime (bytes)))/1000) \
+       + USB2_HOST_DELAY)
+
+extern long usb_calc_bus_time (int speed, int is_input,
+                       int isoc, int bytecount);
+
 /*-------------------------------------------------------------------------*/
 
 extern struct usb_bus *usb_alloc_bus (struct usb_operations *);
diff -Nru a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
--- a/drivers/usb/host/ehci-dbg.c       Tue May 28 23:49:09 2002
+++ b/drivers/usb/host/ehci-dbg.c       Tue May 28 23:49:09 2002
@@ -102,8 +102,8 @@
 
 #ifdef DEBUG
 
-#if 0
-static void dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
+static void __attribute__((__unused__))
+dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
        dbg ("%s %p info1 %x info2 %x hw_curr %x qtd_next %x", label,
                qh, qh->hw_info1, qh->hw_info2,
@@ -117,15 +117,13 @@
                        qh->hw_buf [4]);
        }
 }
-#endif
 
 static const char *const fls_strings [] =
     { "1024", "512", "256", "??" };
 
 #else
-#if 0
-static inline void dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) {}
-#endif
+static inline void __attribute__((__unused__))
+dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) {}
 #endif /* DEBUG */
 
 /* functions have the "wrong" filename when they're output... */
diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
--- a/drivers/usb/host/ehci-hcd.c       Tue May 28 23:49:09 2002
+++ b/drivers/usb/host/ehci-hcd.c       Tue May 28 23:49:09 2002
@@ -65,6 +65,7 @@
  *
  * HISTORY:
  *
+ * 2002-05-24  Preliminary FS/LS interrupts, using scheduling shortcuts
  * 2002-05-11  Clear TT errors for FS/LS ctrl/bulk.  Fill in some other
  *     missing pieces:  enabling 64bit dma, handoff from BIOS/SMM.
  * 2002-05-07  Some error path cleanups to report better errors; wmb();
@@ -82,7 +83,7 @@
  * 2001-June   Works with usb-storage and NEC EHCI on 2.4
  */
 
-#define DRIVER_VERSION "2002-May-11"
+#define DRIVER_VERSION "2002-May-24"
 #define DRIVER_AUTHOR "David Brownell"
 #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
 
@@ -622,7 +623,10 @@
                return 0;
 
        case PIPE_INTERRUPT:
-               intr_deschedule (ehci, urb->start_frame, qh, urb->interval);
+               intr_deschedule (ehci, urb->start_frame, qh,
+                       (urb->dev->speed == USB_SPEED_HIGH)
+                           ? urb->interval
+                           : (urb->interval << 3));
                if (ehci->hcd.state == USB_STATE_HALT)
                        urb->status = -ESHUTDOWN;
                qh_completions (ehci, qh, 1);
diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
--- a/drivers/usb/host/ehci-sched.c     Tue May 28 23:49:09 2002
+++ b/drivers/usb/host/ehci-sched.c     Tue May 28 23:49:09 2002
@@ -33,19 +33,6 @@
  * or with "USB On The Go" additions to USB 2.0 ...)
  */
 
-/*
- * Ceiling microseconds (typical) for that many bytes at high speed
- * ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed
- * to preallocate bandwidth)
- */
-#define EHCI_HOST_DELAY        5       /* nsec, guess */
-#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \
-       + ((2083UL * (3167 + BitTime (bytes)))/1000) \
-       + EHCI_HOST_DELAY)
-#define HS_USECS_ISO(bytes) NS_TO_US ( ((long)(38 * 8 * 2.083)) \
-       + ((2083UL * (3167 + BitTime (bytes)))/1000) \
-       + EHCI_HOST_DELAY)
-       
 static int ehci_get_frame (struct usb_hcd *hcd);
 
 /*-------------------------------------------------------------------------*/
@@ -124,6 +111,9 @@
                        /* is it in the S-mask? */
                        if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe))
                                usecs += q->qh->usecs;
+                       /* ... or C-mask? */
+                       if (q->qh->hw_info2 & cpu_to_le32 (1 << (8 + uframe)))
+                               usecs += q->qh->c_usecs;
                        q = &q->qh->qh_next;
                        break;
                case Q_TYPE_FSTN:
@@ -273,6 +263,12 @@
        unsigned        period,
        unsigned        usecs
 ) {
+       /* complete split running into next frame?
+        * given FSTN support, we could sometimes check...
+        */
+       if (uframe >= 8)
+               return 0;
+
        /*
         * 80% periodic == 100 usec/uframe available
         * convert "usecs we need" to "max already claimed" 
@@ -284,6 +280,8 @@
 
 // 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;
 
@@ -305,20 +303,54 @@
        int                     mem_flags
 ) {
        unsigned                epnum, period;
-       unsigned short          usecs;
+       unsigned short          usecs, c_usecs, gap_uf;
        unsigned long           flags;
        struct ehci_qh          *qh;
        struct hcd_dev          *dev;
+       int                     is_input;
        int                     status = 0;
 
-       /* get endpoint and transfer data */
+       /* get endpoint and transfer/schedule data */
        epnum = usb_pipeendpoint (urb->pipe);
-       if (usb_pipein (urb->pipe))
+       is_input = usb_pipein (urb->pipe);
+       if (is_input)
                epnum |= 0x10;
-       if (urb->dev->speed != USB_SPEED_HIGH) {
-               dbg ("no intr/tt scheduling yet"); 
-               status = -ENOSYS;
-               goto done;
+
+       /*
+        * HS interrupt transfers are simple -- only one microframe.  FS/LS
+        * interrupt transfers involve a SPLIT in one microframe and CSPLIT
+        * sometime later.  We need to know how much time each will be
+        * needed in each microframe and, for FS/LS, how many microframes
+        * separate the two in the best case.
+        */
+       usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0,
+                       urb->transfer_buffer_length);
+       if (urb->dev->speed == USB_SPEED_HIGH) {
+               gap_uf = 0;
+               c_usecs = 0;
+
+               /* FIXME handle HS periods of less than 1 frame. */
+               period = urb->interval >> 3;
+               if (period < 1) {
+                       dbg ("intr period %d uframes, NYET!", urb->interval);
+                       status = -EINVAL;
+                       goto done;
+               }
+       } else {
+               /* gap is a function of full/low speed transfer times */
+               gap_uf = 1 + usb_calc_bus_time (urb->dev->speed, is_input, 0,
+                               urb->transfer_buffer_length) / (125 * 1000);
+
+               /* FIXME this just approximates SPLIT/CSPLIT times */
+               if (is_input) {         // SPLIT, gap, CSPLIT+DATA
+                       c_usecs = usecs + HS_USECS (0);
+                       usecs = HS_USECS (1);
+               } else {                // SPLIT+DATA, gap, CSPLIT
+                       usecs = usecs + HS_USECS (1);
+                       c_usecs = HS_USECS (0);
+               }
+
+               period = urb->interval;
        }
 
        /*
@@ -339,16 +371,6 @@
                goto done;
        }
 
-       usecs = HS_USECS (urb->transfer_buffer_length);
-
-       /* FIXME handle HS periods of less than 1 frame. */
-       period = urb->interval >> 3;
-       if (period < 1) {
-               dbg ("intr period %d uframes, NYET!", urb->interval);
-               status = -EINVAL;
-               goto done;
-       }
-
        spin_lock_irqsave (&ehci->lock, flags);
 
        /* get the qh (must be empty and idle) */
@@ -392,6 +414,7 @@
 
                qh->hw_next = EHCI_LIST_END;
                qh->usecs = usecs;
+               qh->c_usecs = c_usecs;
 
                urb->hcpriv = qh_get (qh);
                status = -ENOSPC;
@@ -399,18 +422,47 @@
                /* pick a set of schedule slots, link the QH into them */
                do {
                        int     uframe;
+                       u32     c_mask = 0;
 
                        /* pick a set of slots such that all uframes have
                         * enough periodic bandwidth available.
-                        *
-                        * FIXME for TT splits, need uframes for start and end.
-                        * FSTNs can put end into next frame (uframes 0 or 1).
                         */
                        frame--;
                        for (uframe = 0; uframe < 8; uframe++) {
                                if (check_period (ehci, frame, uframe,
-                                               period, usecs) != 0)
-                                       break;
+                                               period, usecs) == 0)
+                                       continue;
+
+                               /* If this is a split transaction, check the
+                                * bandwidth available for the completion
+                                * too.  check both best and worst case gaps:
+                                * worst case is SPLIT near uframe end, and
+                                * CSPLIT near start ... best is vice versa.
+                                * Difference can be almost two uframe times.
+                                *
+                                * FIXME don't even bother unless we know
+                                * this TT is idle in that uframe ... right
+                                * now we know only one interrupt transfer
+                                * will be scheduled per frame, so we don't
+                                * need to update/check TT state when we
+                                * schedule a split (QH, SITD, or FSTN).
+                                *
+                                * FIXME ehci 0.96 and above can use FSTNs
+                                */
+                               if (!c_usecs)
+                                       break;
+                               if (check_period (ehci, frame,
+                                               uframe + gap_uf,
+                                               period, c_usecs) == 0)
+                                       continue;
+                               if (check_period (ehci, frame,
+                                               uframe + gap_uf + 1,
+                                               period, c_usecs) == 0)
+                                       continue;
+
+                               c_mask = 0x03 << (8 + uframe + gap_uf);
+                               c_mask = cpu_to_le32 (c_mask);
+                               break;
                        }
                        if (uframe == 8)
                                continue;
@@ -419,13 +471,14 @@
                        urb->start_frame = frame;
                        status = 0;
 
-                       /* set S-frame mask */
-                       qh->hw_info2 |= cpu_to_le32 (1 << uframe);
+                       /* reset S-frame and (maybe) C-frame masks */
+                       qh->hw_info2 &= ~0xffff;
+                       qh->hw_info2 |= cpu_to_le32 (1 << uframe) | c_mask;
                        // dbg_qh ("Schedule INTR qh", ehci, qh);
 
                        /* stuff into the periodic schedule */
                        qh->qh_state = QH_STATE_LINKED;
-                       vdbg ("qh %p usecs %d period %d starting %d.%d",
+                       vdbg ("qh %p usecs %d period %d.0 starting %d.%d",
                                qh, qh->usecs, period, frame, uframe);
                        do {
                                if (unlikely (ehci->pshadow [frame].ptr != 0)) {
@@ -443,7 +496,8 @@
                        } while (frame < ehci->periodic_size);
 
                        /* update bandwidth utilization records (for usbfs) */
-                       usb_claim_bandwidth (urb->dev, urb, usecs/period, 0);
+                       usb_claim_bandwidth (urb->dev, urb,
+                               (usecs + c_usecs) / period, 0);
 
                        /* maybe enable periodic schedule processing */
                        if (!ehci->periodic_urbs++)
@@ -557,6 +611,7 @@
        u32             buf1;
        unsigned        i, epnum, maxp, multi;
        unsigned        length;
+       int             is_input;
 
        itd->hw_next = EHCI_LIST_END;
        itd->urb = urb;
@@ -578,7 +633,8 @@
         * as encoded in the ep descriptor's maxpacket field
         */
        epnum = usb_pipeendpoint (urb->pipe);
-       if (usb_pipein (urb->pipe)) {
+       is_input = usb_pipein (urb->pipe);
+       if (is_input) {
                maxp = urb->dev->epmaxpacketin [epnum];
                buf1 = (1 << 11);
        } else {
@@ -598,7 +654,7 @@
                        urb->iso_frame_desc [index].length);
                return -ENOSPC;
        }
-       itd->usecs = HS_USECS_ISO (length);
+       itd->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 1, length);
 
        /* "plus" info in low order bits of buffer pointers */
        itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum);
@@ -919,17 +975,9 @@
                return flags;
 
        /*
-        * For now, always give the urb back to the driver ... expect it
-        * to submit a new urb (or resubmit this), and to have another
-        * already queued when un-interrupted transfers are needed.
-        * No, that's not what OHCI or UHCI are now doing.
-        *
-        * FIXME Revisit the ISO URB model.  It's cleaner not to have all
-        * the special case magic, but it'd be faster to reuse existing
-        * ITD/DMA setup and schedule state.  Easy to dma_sync/complete(),
-        * then either reschedule or, if unlinking, free and giveback().
-        * But we can't overcommit like the full and low speed HCs do, and
-        * there's no clean way to report an error when rescheduling...
+        * Always give the urb back to the driver ... expect it to submit
+        * a new urb (or resubmit this), and to have another already queued
+        * when un-interrupted transfers are needed.
         *
         * NOTE that for now we don't accelerate ISO unlinks; they just
         * happen according to the current schedule.  Means a delay of
diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
--- a/drivers/usb/host/ehci.h   Tue May 28 23:49:09 2002
+++ b/drivers/usb/host/ehci.h   Tue May 28 23:49:09 2002
@@ -288,14 +288,11 @@
 
        atomic_t                refcount;
        unsigned short          usecs;          /* intr bandwidth */
+       unsigned short          c_usecs;        /* ... split completion bw */
        short                   qh_state;
 #define        QH_STATE_LINKED         1               /* HC sees this */
 #define        QH_STATE_UNLINK         2               /* HC may still see this */
 #define        QH_STATE_IDLE           3               /* HC doesn't see this */
-
-#ifdef EHCI_SOFT_RETRIES
-       int                     retries;
-#endif
 } __attribute__ ((aligned (32)));
 
 /*-------------------------------------------------------------------------*/
@@ -360,6 +357,9 @@
        union ehci_shadow       sitd_next;      /* ptr to periodic q entry */
        struct urb              *urb;
        dma_addr_t              buf_dma;        /* buffer address */
+
+       unsigned short          usecs;          /* start bandwidth */
+       unsigned short          c_usecs;        /* completion bandwidth */
 } __attribute__ ((aligned (32)));
 
 /*-------------------------------------------------------------------------*/

_______________________________________________________________

Don't miss the 2002 Sprint PCS Application Developer's Conference
August 25-28 in Las Vegas -- http://devcon.sprintpcs.com/adp/index.cfm

_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to