ChangeSet 1.1608.24.18, 2004/02/27 12:12:45-08:00, david-b@pacbell.net [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