ChangeSet 1.1608.24.18, 2004/02/27 12:12:45-08:00, [EMAIL PROTECTED]

[PATCH] USB: EHCI and full-speed ISO-OUT

This is a minor update to the patch I sent out about a week ago.
The key change is to use the I/O watchdog while doing ISO streaming.
Bernd Porr reports that a VT8235 system needs that; it seems like
IDE activity can interfere with the delivery of USB IRQs.


EHCI periodic scheduling updates.

 - Initial version of full speed ISO transaction support.  This
   should handle OUT transactions, such as those for usb speakers.
   For now, it's controlled using an EXPERIMENTAL config option:

   * I've run into interesting differences in how different USB 2.0
     hub silicon (the transaction translators) handle some older
     audio devices.  Needs more investigation.

   * Interrupt transfer scheduling doesn't yet cope well with schedules
     where every slot already has activity.  For now, don't plug in
     devices like hubs, mice, or keyboards while EHCI is streaming.

 - Protect freelist for highspeed ITDs, using spinlock.  Could be
   an issue for some drivers.

 - Kick in the I/O watchdog timer (5 msec) for periodic transfers.
   In this case, IDE activity on a VT8235 lost the IRQs which should
   have kept the ISO stream active.  Queues shorter than 5 msec are
   not going to work on all USB hosts.

 - Simplified the ISO scheduler:  doesn't attempt to re-schedule
   after lossage, or to short-circuit scanning.  (Rescheduling will
   probably come back later ... for now, the "hard" error here is
   highlighting problems that need attention.)


 drivers/usb/host/Kconfig      |    9 
 drivers/usb/host/ehci-dbg.c   |    6 
 drivers/usb/host/ehci-hcd.c   |    8 
 drivers/usb/host/ehci-sched.c |  523 +++++++++++++++++++++++++++++++++++-------
 drivers/usb/host/ehci.h       |   14 -
 5 files changed, 467 insertions(+), 93 deletions(-)


diff -Nru a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
--- a/drivers/usb/host/Kconfig  Tue Mar 16 15:05:56 2004
+++ b/drivers/usb/host/Kconfig  Tue Mar 16 15:05:56 2004
@@ -29,6 +29,15 @@
          To compile this driver as a module, choose M here: the
          module will be called ehci-hcd.
 
+config USB_EHCI_SPLIT_ISO
+       bool "Full speed ISO transactions (EXPERIMENTAL)"
+       depends on USB_EHCI_HCD && EXPERIMENTAL
+       default n
+       ---help---
+         This code is new and hasn't been used with many different
+         EHCI or USB 2.0 transaction translator implementations.
+         It should work for ISO-OUT transfers, like audio.
+
 config USB_OHCI_HCD
        tristate "OHCI HCD support"
        depends on USB
diff -Nru a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
--- a/drivers/usb/host/ehci-dbg.c       Tue Mar 16 15:05:56 2004
+++ b/drivers/usb/host/ehci-dbg.c       Tue Mar 16 15:05:56 2004
@@ -579,7 +579,11 @@
                                break;
                        case Q_TYPE_SITD:
                                temp = scnprintf (next, size,
-                                       " sitd/%p", p.sitd);
+                                       " sitd%d-%04x/%p",
+                                       p.sitd->stream->interval,
+                                       le32_to_cpup (&p.sitd->hw_uframe)
+                                               & 0x0000ffff,
+                                       p.sitd);
                                tag = Q_NEXT_TYPE (p.sitd->hw_next);
                                p = p.sitd->sitd_next;
                                break;
diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
--- a/drivers/usb/host/ehci-hcd.c       Tue Mar 16 15:05:56 2004
+++ b/drivers/usb/host/ehci-hcd.c       Tue Mar 16 15:05:56 2004
@@ -106,8 +106,6 @@
 #undef EHCI_VERBOSE_DEBUG
 #undef EHCI_URB_TRACE
 
-// #define have_split_iso
-
 #ifdef DEBUG
 #define EHCI_STATS
 #endif
@@ -676,6 +674,7 @@
 
        /* the IO watchdog guards against hardware or driver bugs that
         * misplace IRQs, and should let us run completely without IRQs.
+        * such lossage has been observed on both VT6202 and VT8235. 
         */
        if ((ehci->async->qh_next.ptr != 0) || (ehci->periodic_sched != 0))
                timer_action (ehci, TIMER_IO_WATCHDOG);
@@ -796,13 +795,8 @@
        case PIPE_ISOCHRONOUS:
                if (urb->dev->speed == USB_SPEED_HIGH)
                        return itd_submit (ehci, urb, mem_flags);
-#ifdef have_split_iso
                else
                        return sitd_submit (ehci, urb, mem_flags);
-#else
-               dbg ("no split iso support yet");
-               return -ENOSYS;
-#endif /* have_split_iso */
        }
 }
 
diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
--- a/drivers/usb/host/ehci-sched.c     Tue Mar 16 15:05:56 2004
+++ b/drivers/usb/host/ehci-sched.c     Tue Mar 16 15:05:56 2004
@@ -53,14 +53,10 @@
                return &periodic->fstn->fstn_next;
        case Q_TYPE_ITD:
                return &periodic->itd->itd_next;
-#ifdef have_split_iso
-       case Q_TYPE_SITD:
+       // case Q_TYPE_SITD:
+       default:
                return &periodic->sitd->sitd_next;
-#endif /* have_split_iso */
        }
-       dbg ("BAD shadow %p tag %d", periodic->ptr, tag);
-       // BUG ();
-       return 0;
 }
 
 /* returns true after successful unlink */
@@ -133,7 +129,6 @@
                        hw_p = &q->itd->hw_next;
                        q = &q->itd->itd_next;
                        break;
-#ifdef have_split_iso
                case Q_TYPE_SITD:
                        /* is it in the S-mask?  (count SPLIT, DATA) */
                        if (q->sitd->hw_uframe & cpu_to_le32 (1 << uframe)) {
@@ -154,7 +149,6 @@
                        hw_p = &q->sitd->hw_next;
                        q = &q->sitd->sitd_next;
                        break;
-#endif /* have_split_iso */
                default:
                        BUG ();
                }
@@ -229,7 +223,8 @@
                                if (same_tt (dev, here.itd->urb->dev)) {
                                        u16             mask;
 
-                                       mask = le32_to_cpu (here.sitd->hw_uframe);
+                                       mask = le32_to_cpu (here.sitd
+                                                               ->hw_uframe);
                                        /* FIXME assumes no gap for IN! */
                                        mask |= mask >> 8;
                                        if (mask & uf_mask)
@@ -237,7 +232,7 @@
                                }
                                type = Q_NEXT_TYPE (here.qh->hw_next);
                                here = here.sitd->sitd_next;
-                               break;
+                               continue;
                        // case Q_TYPE_FSTN:
                        default:
                                ehci_dbg (ehci,
@@ -698,12 +693,27 @@
                // BUG_ON (!list_empty(&stream->td_list));
 
                while (!list_empty (&stream->free_list)) {
-                       struct ehci_itd *itd;
+                       struct list_head        *entry;
 
-                       itd = list_entry (stream->free_list.next,
-                               struct ehci_itd, itd_list);
-                       list_del (&itd->itd_list);
-                       dma_pool_free (ehci->itd_pool, itd, itd->itd_dma);
+                       entry = stream->free_list.next;
+                       list_del (entry);
+
+                       /* knows about ITD vs SITD */
+                       if (stream->highspeed) {
+                               struct ehci_itd         *itd;
+
+                               itd = list_entry (entry, struct ehci_itd,
+                                               itd_list);
+                               dma_pool_free (ehci->itd_pool, itd,
+                                               itd->itd_dma);
+                       } else {
+                               struct ehci_sitd        *sitd;
+
+                               sitd = list_entry (entry, struct ehci_sitd,
+                                               sitd_list);
+                               dma_pool_free (ehci->sitd_pool, sitd,
+                                               sitd->sitd_dma);
+                       }
                }
 
                is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0;
@@ -858,6 +868,7 @@
        int                     i;
        unsigned                num_itds;
        struct ehci_iso_sched   *sched;
+       unsigned long           flags;
 
        sched = iso_sched_alloc (urb->number_of_packets, mem_flags);
        if (unlikely (sched == 0))
@@ -871,6 +882,7 @@
                num_itds = urb->number_of_packets;
 
        /* allocate/init ITDs */
+       spin_lock_irqsave (&ehci->lock, flags);
        for (i = 0; i < num_itds; i++) {
 
                /* free_list.next might be cache-hot ... but maybe
@@ -884,8 +896,14 @@
                        list_del (&itd->itd_list);
                        itd_dma = itd->itd_dma;
                } else
+                       itd = 0;
+
+               if (!itd) {
+                       spin_unlock_irqrestore (&ehci->lock, flags);
                        itd = dma_pool_alloc (ehci->itd_pool, mem_flags,
                                        &itd_dma);
+                       spin_lock_irqsave (&ehci->lock, flags);
+               }
 
                if (unlikely (0 == itd)) {
                        iso_sched_free (stream, sched);
@@ -895,6 +913,7 @@
                itd->itd_dma = itd_dma;
                list_add (&itd->itd_list, &sched->td_list);
        }
+       spin_unlock_irqrestore (&ehci->lock, flags);
 
        /* temporarily store schedule info in hcpriv */
        urb->hcpriv = sched;
@@ -909,11 +928,11 @@
        struct ehci_hcd         *ehci,
        u32                     mod,
        u32                     uframe,
-       u32                     end,
        u8                      usecs,
        u32                     period
 )
 {
+       uframe %= period;
        do {
                /* can't commit more than 80% periodic == 100 usec */
                if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7)
@@ -922,8 +941,7 @@
 
                /* we know urb->interval is 2^N uframes */
                uframe += period;
-               uframe %= mod;
-       } while (uframe != end);
+       } while (uframe < mod);
        return 1;
 }
 
@@ -933,7 +951,6 @@
        u32                     mod,
        struct ehci_iso_stream  *stream,
        u32                     uframe,
-       u32                     end,
        struct ehci_iso_sched   *sched,
        u32                     period_uframes
 )
@@ -952,12 +969,20 @@
         */
 
        /* check bandwidth */
+       uframe %= period_uframes;
        do {
                u32             max_used;
 
                frame = uframe >> 3;
                uf = uframe & 7;
 
+               /* 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;
+
                /* check starts (OUT uses more than one) */
                max_used = 100 - stream->usecs;
                for (tmp = stream->raw_mask & 0xff; tmp; tmp >>= 1, uf++) {
@@ -969,25 +994,19 @@
                if (stream->c_usecs) {
                        max_used = 100 - stream->c_usecs;
                        do {
-                               /* tt is busy in the gap before CSPLIT */
                                tmp = 1 << uf;
-                               mask |= tmp;
                                tmp <<= 8;
-                               if (stream->raw_mask & tmp)
-                                       break;
+                               if ((stream->raw_mask & tmp) == 0)
+                                       continue;
+                               if (periodic_usecs (ehci, frame, uf)
+                                               > max_used)
+                                       return 0;
                        } while (++uf < 8);
-                       if (periodic_usecs (ehci, frame, uf) > max_used)
-                               return 0;
                }
 
                /* we know urb->interval is 2^N uframes */
                uframe += period_uframes;
-               uframe %= mod;
-       } while (uframe != end);
-
-       /* tt must be idle for start(s), any gap, and csplit */
-       if (!tt_no_collision (ehci, period_uframes, stream->udev, frame, mask))
-               return 0;
+       } while (uframe < mod);
 
        stream->splits = stream->raw_mask << (uframe & 7);
        cpu_to_le32s (&stream->splits);
@@ -1014,7 +1033,7 @@
        struct ehci_iso_stream  *stream
 )
 {
-       u32                     now, start, end, max, period;
+       u32                     now, start, max, period;
        int                     status;
        unsigned                mod = ehci->periodic_size << 3;
        struct ehci_iso_sched   *sched = urb->hcpriv;
@@ -1036,8 +1055,6 @@
 
        /* when's the last uframe this urb could start? */
        max = now + mod;
-       max -= sched->span;
-       max -= 8 * SCHEDULE_SLOP;
 
        /* typical case: reuse current schedule. stream is still active,
         * and no gaps from host falling behind (irq delays etc)
@@ -1046,9 +1063,11 @@
                start = stream->next_uframe;
                if (start < now)
                        start += mod;
-               if (likely (start < max))
+               if (likely ((start + sched->span) < max))
                        goto ready;
-               /* else fell behind; try to reschedule */
+               /* else fell behind; someday, try to reschedule */
+               status = -EL2NSYNC;
+               goto fail;
        }
 
        /* need to schedule; when's the next (u)frame we could start?
@@ -1059,63 +1078,40 @@
         */
        start = SCHEDULE_SLOP * 8 + (now & ~0x07);
        start %= mod;
-       end = start;
+       stream->next_uframe = start;
 
        /* NOTE:  assumes URB_ISO_ASAP, to limit complexity/bugs */
 
        period = urb->interval;
        if (!stream->highspeed)
                period <<= 3;
-       if (max > (start + period))
-               max = start + period;
 
-       /* hack:  account for itds already scheduled to this endpoint */
-       if (list_empty (&stream->td_list))
-               end = max;
-
-       /* within [start..max] find a uframe slot with enough bandwidth */
-       end %= mod;
-       do {
+       /* find a uframe slot with enough bandwidth */
+       for (; start < (stream->next_uframe + period); start++) {
                int             enough_space;
 
                /* check schedule: enough space? */
                if (stream->highspeed)
-                       enough_space = itd_slot_ok (ehci, mod, start, end,
+                       enough_space = itd_slot_ok (ehci, mod, start,
                                        stream->usecs, period);
                else {
                        if ((start % 8) >= 6)
                                continue;
                        enough_space = sitd_slot_ok (ehci, mod, stream,
-                                       start, end, sched, period);
+                                       start, sched, period);
                }
 
-               /* (re)schedule it here if there's enough bandwidth */
+               /* schedule it here if there's enough bandwidth */
                if (enough_space) {
-                       start %= mod;
-                       if (unlikely (!list_empty (&stream->td_list))) {
-                               /* host fell behind ... maybe irq latencies
-                                * delayed this request queue for too long.
-                                */
-                               stream->rescheduled++;
-                               dev_dbg (&urb->dev->dev,
-                                       "iso%d%s %d.%d skip %d.%d\n",
-                                       stream->bEndpointAddress & 0x0f,
-                                       (stream->bEndpointAddress & USB_DIR_IN)
-                                               ? "in" : "out",
-                                       stream->next_uframe >> 3,
-                                       stream->next_uframe & 0x7,
-                                       start >> 3, start & 0x7);
-                       }
-                       stream->next_uframe = start;
+                       stream->next_uframe = start % mod;
                        goto ready;
                }
-
-       } while (++start < max);
+       }
 
        /* no room in the schedule */
-       ehci_dbg (ehci, "iso %ssched full %p (now %d end %d max %d)\n",
+       ehci_dbg (ehci, "iso %ssched full %p (now %d max %d)\n",
                list_empty (&stream->td_list) ? "" : "re",
-               urb, now, end, max);
+               urb, now, max);
        status = -ENOSPC;
 
 fail:
@@ -1260,6 +1256,7 @@
        iso_sched_free (stream, iso_sched);
        urb->hcpriv = 0;
 
+       timer_action (ehci, TIMER_IO_WATCHDOG);
        if (unlikely (!ehci->periodic_sched++))
                return enable_periodic (ehci);
        return 0;
@@ -1404,18 +1401,392 @@
        return status;
 }
 
-#ifdef have_split_iso
+#ifdef CONFIG_USB_EHCI_SPLIT_ISO
 
 /*-------------------------------------------------------------------------*/
 
 /*
- * "Split ISO TDs" ... used for USB 1.1 devices going through
- * the TTs in USB 2.0 hubs.
- *
- * FIXME not yet implemented
+ * "Split ISO TDs" ... used for USB 1.1 devices going through the
+ * TTs in USB 2.0 hubs.  These need microframe scheduling.
  */
 
-#endif /* have_split_iso */
+static inline void
+sitd_sched_init (
+       struct ehci_iso_sched   *iso_sched,
+       struct ehci_iso_stream  *stream,
+       struct urb              *urb
+)
+{
+       unsigned        i;
+       dma_addr_t      dma = urb->transfer_dma;
+
+       /* how many frames are needed for these transfers */
+       iso_sched->span = urb->number_of_packets * stream->interval;
+
+       /* figure out per-frame sitd fields that we'll need later
+        * when we fit new sitds into the schedule.
+        */
+       for (i = 0; i < urb->number_of_packets; i++) {
+               struct ehci_iso_packet  *packet = &iso_sched->packet [i];
+               unsigned                length;
+               dma_addr_t              buf;
+               u32                     trans;
+
+               length = urb->iso_frame_desc [i].length & 0x03ff;
+               buf = dma + urb->iso_frame_desc [i].offset;
+
+               trans = SITD_STS_ACTIVE;
+               if (((i + 1) == urb->number_of_packets)
+                               && !(urb->transfer_flags & URB_NO_INTERRUPT))
+                       trans |= SITD_IOC;
+               trans |= length << 16;
+               packet->transaction = cpu_to_le32 (trans);
+
+               /* might need to cross a buffer page within a td */
+               packet->bufp = buf;
+               buf += length;
+               packet->buf1 = buf & ~0x0fff;
+               if (packet->buf1 != (buf & ~(u64)0x0fff))
+                       packet->cross = 1;
+
+               /* OUT uses multiple start-splits */ 
+               if (stream->bEndpointAddress & USB_DIR_IN)
+                       continue;
+               length = 1 + (length / 188);
+               packet->buf1 |= length;
+               if (length > 1) /* BEGIN vs ALL */
+                       packet->buf1 |= 1 << 3;
+       }
+}
+
+static int
+sitd_urb_transaction (
+       struct ehci_iso_stream  *stream,
+       struct ehci_hcd         *ehci,
+       struct urb              *urb,
+       int                     mem_flags
+)
+{
+       struct ehci_sitd        *sitd;
+       dma_addr_t              sitd_dma;
+       int                     i;
+       struct ehci_iso_sched   *iso_sched;
+       unsigned long           flags;
+
+       iso_sched = iso_sched_alloc (urb->number_of_packets, mem_flags);
+       if (iso_sched == 0)
+               return -ENOMEM;
+
+       sitd_sched_init (iso_sched, stream, urb);
+
+       /* allocate/init sITDs */
+       spin_lock_irqsave (&ehci->lock, flags);
+       for (i = 0; i < urb->number_of_packets; i++) {
+
+               /* NOTE:  for now, we don't try to handle wraparound cases
+                * for IN (using sitd->hw_backpointer, like a FSTN), which
+                * means we never need two sitds for full speed packets.
+                */
+
+               /* free_list.next might be cache-hot ... but maybe
+                * the HC caches it too. avoid that issue for now.
+                */
+
+               /* prefer previously-allocated sitds */
+               if (!list_empty(&stream->free_list)) {
+                       sitd = list_entry (stream->free_list.prev,
+                                        struct ehci_sitd, sitd_list);
+                       list_del (&sitd->sitd_list);
+                       sitd_dma = sitd->sitd_dma;
+               } else
+                       sitd = 0;
+
+               if (!sitd) {
+                       spin_unlock_irqrestore (&ehci->lock, flags);
+                       sitd = dma_pool_alloc (ehci->sitd_pool, mem_flags,
+                                       &sitd_dma);
+                       spin_lock_irqsave (&ehci->lock, flags);
+               }
+
+               if (!sitd) {
+                       iso_sched_free (stream, iso_sched);
+                       spin_unlock_irqrestore (&ehci->lock, flags);
+                       return -ENOMEM;
+               }
+               memset (sitd, 0, sizeof *sitd);
+               sitd->sitd_dma = sitd_dma;
+               list_add (&sitd->sitd_list, &iso_sched->td_list);
+       }
+
+       /* temporarily store schedule info in hcpriv */
+       urb->hcpriv = iso_sched;
+       urb->error_count = 0;
+
+       spin_unlock_irqrestore (&ehci->lock, flags);
+       return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void
+sitd_patch (
+       struct ehci_iso_stream  *stream,
+       struct ehci_sitd        *sitd,
+       struct ehci_iso_sched   *iso_sched,
+       unsigned                index
+)
+{
+       struct ehci_iso_packet  *uf = &iso_sched->packet [index];
+       u64                     bufp = uf->bufp;
+
+       sitd->hw_next = EHCI_LIST_END;
+       sitd->hw_fullspeed_ep = stream->address;
+       sitd->hw_uframe = stream->splits;
+       sitd->hw_results = uf->transaction;
+       sitd->hw_backpointer = EHCI_LIST_END;
+
+       bufp = uf->bufp;
+       sitd->hw_buf [0] = cpu_to_le32 (bufp);
+       sitd->hw_buf_hi [0] = cpu_to_le32 (bufp >> 32);
+
+       sitd->hw_buf [1] = cpu_to_le32 (uf->buf1);
+       if (uf->cross) {
+               bufp += 4096;
+               sitd->hw_buf_hi [1] = cpu_to_le32 (bufp >> 32);
+       }
+       sitd->index = index;
+}
+
+static inline void
+sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd)
+{
+       /* note: sitd ordering could matter (CSPLIT then SSPLIT) */
+       sitd->sitd_next = ehci->pshadow [frame];
+       sitd->hw_next = ehci->periodic [frame];
+       ehci->pshadow [frame].sitd = sitd;
+       sitd->frame = frame;
+       wmb ();
+       ehci->periodic [frame] = cpu_to_le32 (sitd->sitd_dma) | Q_TYPE_SITD;
+}
+
+/* fit urb's sitds into the selected schedule slot; activate as needed */
+static int
+sitd_link_urb (
+       struct ehci_hcd         *ehci,
+       struct urb              *urb,
+       unsigned                mod,
+       struct ehci_iso_stream  *stream
+)
+{
+       int                     packet;
+       unsigned                next_uframe;
+       struct ehci_iso_sched   *sched = urb->hcpriv;
+       struct ehci_sitd        *sitd;
+
+       next_uframe = stream->next_uframe;
+
+       if (list_empty(&stream->td_list)) {
+               /* usbfs ignores TT bandwidth */
+               hcd_to_bus (&ehci->hcd)->bandwidth_allocated
+                               += stream->bandwidth;
+               ehci_vdbg (ehci,
+                       "sched dev%s ep%d%s-iso [%d] %dms/%04x\n",
+                       urb->dev->devpath, stream->bEndpointAddress & 0x0f,
+                       (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
+                       (next_uframe >> 3) % ehci->periodic_size,
+                       stream->interval, le32_to_cpu (stream->splits));
+               stream->start = jiffies;
+       }
+       hcd_to_bus (&ehci->hcd)->bandwidth_isoc_reqs++;
+
+       /* fill sITDs frame by frame */
+       for (packet = 0, sitd = 0;
+                       packet < urb->number_of_packets;
+                       packet++) {
+
+               /* ASSERT:  we have all necessary sitds */
+               BUG_ON (list_empty (&sched->td_list));
+
+               /* ASSERT:  no itds for this endpoint in this frame */
+
+               sitd = list_entry (sched->td_list.next,
+                               struct ehci_sitd, sitd_list);
+               list_move_tail (&sitd->sitd_list, &stream->td_list);
+               sitd->stream = iso_stream_get (stream);
+               sitd->urb = usb_get_urb (urb);
+
+               sitd_patch (stream, sitd, sched, packet);
+               sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size,
+                               sitd);
+
+               next_uframe += stream->interval << 3;
+               stream->depth += stream->interval << 3;
+       }
+       stream->next_uframe = next_uframe % mod;
+
+       /* don't need that schedule data any more */
+       iso_sched_free (stream, sched);
+       urb->hcpriv = 0;
+
+       timer_action (ehci, TIMER_IO_WATCHDOG);
+       if (!ehci->periodic_sched++)
+               return enable_periodic (ehci);
+       return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define        SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \
+                       | SITD_STS_XACT | SITD_STS_MMF | SITD_STS_STS)
+
+static unsigned
+sitd_complete (
+       struct ehci_hcd         *ehci,
+       struct ehci_sitd        *sitd,
+       struct pt_regs          *regs
+) {
+       struct urb                              *urb = sitd->urb;
+       struct usb_iso_packet_descriptor        *desc;
+       u32                                     t;
+       int                                     urb_index = -1;
+       struct ehci_iso_stream                  *stream = sitd->stream;
+       struct usb_device                       *dev;
+
+       urb_index = sitd->index;
+       desc = &urb->iso_frame_desc [urb_index];
+       t = le32_to_cpup (&sitd->hw_results);
+
+       /* report transfer status */
+       if (t & SITD_ERRS) {
+               urb->error_count++;
+               if (t & SITD_STS_DBE)
+                       desc->status = usb_pipein (urb->pipe)
+                               ? -ENOSR  /* hc couldn't read */
+                               : -ECOMM; /* hc couldn't write */
+               else if (t & SITD_STS_BABBLE)
+                       desc->status = -EOVERFLOW;
+               else /* XACT, MMF, etc */
+                       desc->status = -EPROTO;
+       } else {
+               desc->status = 0;
+               desc->actual_length = desc->length - SITD_LENGTH (t);
+       }
+
+       usb_put_urb (urb);
+       sitd->urb = 0;
+       sitd->stream = 0;
+       list_move (&sitd->sitd_list, &stream->free_list);
+       stream->depth -= stream->interval << 3;
+       iso_stream_put (ehci, stream);
+
+       /* handle completion now? */
+       if ((urb_index + 1) != urb->number_of_packets)
+               return 0;
+
+       /* ASSERT: it's really the last sitd for this urb
+       list_for_each_entry (sitd, &stream->td_list, sitd_list)
+               BUG_ON (sitd->urb == urb);
+        */
+
+       /* give urb back to the driver */
+       dev = usb_get_dev (urb->dev);
+       ehci_urb_done (ehci, urb, regs);
+       urb = 0;
+
+       /* defer stopping schedule; completion can submit */
+       ehci->periodic_sched--;
+       if (!ehci->periodic_sched)
+               (void) disable_periodic (ehci);
+       hcd_to_bus (&ehci->hcd)->bandwidth_isoc_reqs--;
+
+       if (list_empty (&stream->td_list)) {
+               hcd_to_bus (&ehci->hcd)->bandwidth_allocated
+                               -= stream->bandwidth;
+               ehci_vdbg (ehci,
+                       "deschedule devp %s ep%d%s-iso\n",
+                       dev->devpath, stream->bEndpointAddress & 0x0f,
+                       (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
+       }
+       iso_stream_put (ehci, stream);
+       usb_put_dev (dev);
+
+       return 1;
+}
+
+
+static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
+{
+       int                     status = -EINVAL;
+       unsigned long           flags;
+       struct ehci_iso_stream  *stream;
+
+       // FIXME remove when csplits behave
+       if (usb_pipein(urb->pipe)) {
+               ehci_dbg (ehci, "no iso-IN split transactions yet\n");
+               return -ENOMEM;
+       }
+
+       /* Get iso_stream head */
+       stream = iso_stream_find (ehci, urb);
+       if (stream == 0) {
+               ehci_dbg (ehci, "can't get iso stream\n");
+               return -ENOMEM;
+       }
+       if (urb->interval != stream->interval) {
+               ehci_dbg (ehci, "can't change iso interval %d --> %d\n",
+                       stream->interval, urb->interval);
+               goto done;
+       }
+
+#ifdef EHCI_URB_TRACE
+       ehci_dbg (ehci,
+               "submit %p dev%s ep%d%s-iso len %d\n",
+               urb, urb->dev->devpath,
+               usb_pipeendpoint (urb->pipe),
+               usb_pipein (urb->pipe) ? "in" : "out",
+               urb->transfer_buffer_length);
+#endif
+
+       /* allocate SITDs */
+       status = sitd_urb_transaction (stream, ehci, urb, mem_flags);
+       if (status < 0) {
+               ehci_dbg (ehci, "can't init sitds\n");
+               goto done;
+       }
+
+       /* schedule ... need to lock */
+       spin_lock_irqsave (&ehci->lock, flags);
+       status = iso_stream_schedule (ehci, urb, stream);
+       if (status == 0)
+               sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
+       spin_unlock_irqrestore (&ehci->lock, flags);
+
+done:
+       if (status < 0)
+               iso_stream_put (ehci, stream);
+       return status;
+}
+
+#else
+
+static inline int
+sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
+{
+       ehci_dbg (ehci, "split iso support is disabled\n");
+       return -ENOSYS;
+}
+
+static inline unsigned
+sitd_complete (
+       struct ehci_hcd         *ehci,
+       struct ehci_sitd        *sitd,
+       struct pt_regs          *regs
+) {
+       ehci_err (ehci, "sitd_complete %p?\n", sitd);
+       return 0;
+}
+
+#endif /* USB_EHCI_SPLIT_ISO */
 
 /*-------------------------------------------------------------------------*/
 
@@ -1513,7 +1884,6 @@
                                modified = itd_complete (ehci, q.itd, regs);
                                q = *q_p;
                                break;
-#ifdef have_split_iso
                        case Q_TYPE_SITD:
                                if (q.sitd->hw_results & SITD_ACTIVE) {
                                        q_p = &q.sitd->sitd_next;
@@ -1529,7 +1899,6 @@
                                modified = sitd_complete (ehci, q.sitd, regs);
                                q = *q_p;
                                break;
-#endif /* have_split_iso */
                        default:
                                dbg ("corrupt type %d frame %d shadow %p",
                                        type, frame, q.ptr);
diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
--- a/drivers/usb/host/ehci.h   Tue Mar 16 15:05:56 2004
+++ b/drivers/usb/host/ehci.h   Tue Mar 16 15:05:56 2004
@@ -492,16 +492,16 @@
 /*
  * EHCI Specification 0.95 Section 3.4 
  * siTD, aka split-transaction isochronous Transfer Descriptor
- *       ... describe low/full speed iso xfers through TT in hubs
+ *       ... describe full speed iso xfers through TT in hubs
  * see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD)
  */
 struct ehci_sitd {
        /* first part defined by EHCI spec */
        u32                     hw_next;
 /* uses bit field macros above - see EHCI 0.95 Table 3-8 */
-       u32                     hw_fullspeed_ep;        /* see EHCI table 3-9 */
-       u32                     hw_uframe;              /* see EHCI table 3-10 */
-       u32                     hw_results;             /* see EHCI table 3-11 */
+       u32                     hw_fullspeed_ep;        /* EHCI table 3-9 */
+       u32                     hw_uframe;              /* EHCI table 3-10 */
+       u32                     hw_results;             /* EHCI table 3-11 */
 #define        SITD_IOC        (1 << 31)       /* interrupt on completion */
 #define        SITD_PAGE       (1 << 30)       /* buffer 0/1 */
 #define        SITD_LENGTH(x)  (0x3ff & ((x)>>16))
@@ -515,8 +515,8 @@
 
 #define SITD_ACTIVE    __constant_cpu_to_le32(SITD_STS_ACTIVE)
 
-       u32                     hw_buf [2];             /* see EHCI table 3-12 */
-       u32                     hw_backpointer;         /* see EHCI table 3-13 */
+       u32                     hw_buf [2];             /* EHCI table 3-12 */
+       u32                     hw_backpointer;         /* EHCI table 3-13 */
        u32                     hw_buf_hi [2];          /* Appendix B */
 
        /* the rest is HCD-private */
@@ -551,8 +551,6 @@
 } __attribute__ ((aligned (32)));
 
 /*-------------------------------------------------------------------------*/
-
-#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb,mem_flags)
 
 #ifndef DEBUG
 #define STUB_DEBUG_FILES



-------------------------------------------------------
This SF.Net email is sponsored by: IBM Linux Tutorials
Free Linux tutorial presented by Daniel Robbins, President and CEO of
GenToo technologies. Learn everything from fundamentals to system
administration.http://ads.osdn.com/?ad_id70&alloc_id638&op=click
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to