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
