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.
Please merge.
- 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 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.)
--- a/drivers/usb/host/Kconfig Fri Feb 27 08:46:23 2004
+++ b/drivers/usb/host/Kconfig Fri Feb 27 08:46:23 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 Fri Feb 27 08:46:23 2004
+++ b/drivers/usb/host/ehci-dbg.c Fri Feb 27 08:46:23 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 Fri Feb 27 08:46:23 2004
+++ b/drivers/usb/host/ehci-hcd.c Fri Feb 27 08:46:23 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 */
}
}
--- a/drivers/usb/host/ehci-sched.c Fri Feb 27 08:46:23 2004
+++ b/drivers/usb/host/ehci-sched.c Fri Feb 27 08:46:23 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);
--- a/drivers/usb/host/ehci.h Fri Feb 27 08:46:23 2004
+++ b/drivers/usb/host/ehci.h Fri Feb 27 08:46:23 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
