This patch should apply against current BK (or snapshots),
but it needs testing!  If you have USB 1.1 speakers, EHCI,
and a high speed hub, you can help.

The test is simple:  connect hub to host, speakers to hub,
and play audio.  Report any problems, along with listings
of the devices (/proc/bus/usb/devices, lspci -v) you're
using ... be sure you can reproduce the problem with only
EHCI loaded (not OHCI or UHCI).  Success reports would be
nice to see, too!

Newer USB audio devices should be relatively OK, but I've
got a relatively old one that wouldn't enumerate until
I tweaked usbcore a bunch.  It seems that the transaction
translaters in the high speed hubs don't all act the same
when faced with certain control traffic quirks.  (This
device gives "lsusb" indigetstion too, even without a TT
in the picture.)

- Dave
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 control traffic
   from some older audio devices.

 - Simplified the ISO scheduler:  doesn't attempt to re-schedule
   after lossage, or to short-circuit scanning.

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

--- a/drivers/usb/host/Kconfig  Sat Feb 21 11:01:53 2004
+++ b/drivers/usb/host/Kconfig  Sat Feb 21 11:01:53 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
--- a/drivers/usb/host/ehci-dbg.c       Sat Feb 21 11:01:53 2004
+++ b/drivers/usb/host/ehci-dbg.c       Sat Feb 21 11:01:53 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;
--- a/drivers/usb/host/ehci-hcd.c       Sat Feb 21 11:01:53 2004
+++ b/drivers/usb/host/ehci-hcd.c       Sat Feb 21 11:01:53 2004
@@ -106,8 +106,6 @@
 #undef EHCI_VERBOSE_DEBUG
 #undef EHCI_URB_TRACE
 
-// #define have_split_iso
-
 #ifdef DEBUG
 #define EHCI_STATS
 #endif
@@ -796,13 +794,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 */
        }
 }
 
--- a/drivers/usb/host/ehci-sched.c     Sat Feb 21 11:01:53 2004
+++ b/drivers/usb/host/ehci-sched.c     Sat Feb 21 11:01:53 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:
@@ -1404,18 +1400,391 @@
        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;
+
+       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 +1882,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 +1897,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);
--- a/drivers/usb/host/ehci.h   Sat Feb 21 11:01:53 2004
+++ b/drivers/usb/host/ehci.h   Sat Feb 21 11:01:53 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

Reply via email to