Commit adffcfda authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] USB: ehci update: 3/3, highspeed iso rewrite

This is an updated version of a patch submitted to me from
Michal Sojka <sojkam1@fel.cvut.cz>, basically providing a
much-needed rewrite of the highspeed ISO support.  I updated
the scheduling and made it a closer match to how OHCI works;
and also tested it a bunch.

So far it seems most of the requests for highspeed ISO support
have been for realtime data collection -- custom apps, nothing
a mainstream kernel would ship with.   The USB Video class is
now defined; highspeed video will also need these updates.

Key changes:

   - Define an "iso_stream" head for iso endpoints.  This acts
     enough like a QH that endpoint_disable() works.  It holds the
     queue of ITDs, and the endpoint's current schedule state.
     And it's easy to find (spinlocked array access), no search.

   - Uses a temporary "itd_sched" while submitting each URB, with
     not-yet-linked ITDs and request-specific metadata.  There's
     a per-stream cache of ITDs, so resubmitting ISO urbs (to
     achieve a "ring" of transfers) is typically cheap.

   - Scheduling for most URBs is almost a NOP:  just a sanity
     check to make sure there's no need to reschedule, and then
     just link into the schedule at the current schedule slot.
     (The previous code was a gross hack that didn't even work
     reasonably with more than two URBs queued.)

   - Is a reasonable model to use with full speed ISO transfers.
     (They need additional TT scheduling hooks, most of which
     are already written but not merged.)

   - Handles several cases the previous code didn't, including
     high bandwidth transfers (loads up to 24 MByte/sec)

   - Has had more testing than the old code, including 20+ hour
     successful IN+OUT runs, more varied transfer intervals and
     maxpacket sizes.  (Using net2280 and a gadgetfs driver.)

So it's worth replacing the existing code with this; there
aren't too many rough edges, and it's much more fixable than
the previous version.


p.s. Many thanks, Michal!
parent 49597169
...@@ -140,6 +140,36 @@ dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -140,6 +140,36 @@ dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next); dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next);
} }
static void __attribute__((__unused__))
dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
{
ehci_dbg (ehci, "%s [%d] itd %p, next %08x, urb %p\n",
label, itd->frame, itd, le32_to_cpu(itd->hw_next), itd->urb);
ehci_dbg (ehci,
" trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
le32_to_cpu(itd->hw_transaction[0]),
le32_to_cpu(itd->hw_transaction[1]),
le32_to_cpu(itd->hw_transaction[2]),
le32_to_cpu(itd->hw_transaction[3]),
le32_to_cpu(itd->hw_transaction[4]),
le32_to_cpu(itd->hw_transaction[5]),
le32_to_cpu(itd->hw_transaction[6]),
le32_to_cpu(itd->hw_transaction[7]));
ehci_dbg (ehci,
" buf: %08x %08x %08x %08x %08x %08x %08x\n",
le32_to_cpu(itd->hw_bufp[0]),
le32_to_cpu(itd->hw_bufp[1]),
le32_to_cpu(itd->hw_bufp[2]),
le32_to_cpu(itd->hw_bufp[3]),
le32_to_cpu(itd->hw_bufp[4]),
le32_to_cpu(itd->hw_bufp[5]),
le32_to_cpu(itd->hw_bufp[6]));
ehci_dbg (ehci, " index: %d %d %d %d %d %d %d %d\n",
itd->index[0], itd->index[1], itd->index[2],
itd->index[3], itd->index[4], itd->index[5],
itd->index[6], itd->index[7]);
}
static int __attribute__((__unused__)) static int __attribute__((__unused__))
dbg_status_buf (char *buf, unsigned len, char *label, u32 status) dbg_status_buf (char *buf, unsigned len, char *label, u32 status)
{ {
......
...@@ -67,6 +67,9 @@ ...@@ -67,6 +67,9 @@
* *
* HISTORY: * HISTORY:
* *
* 2003-12-29 Rewritten high speed iso transfer support (by Michal Sojka,
* <sojkam@centrum.cz>, updates by DB).
*
* 2002-11-29 Correct handling for hw async_next register. * 2002-11-29 Correct handling for hw async_next register.
* 2002-08-06 Handling for bulk and interrupt transfers is mostly shared; * 2002-08-06 Handling for bulk and interrupt transfers is mostly shared;
* only scheduling is different, no arbitrary limitations. * only scheduling is different, no arbitrary limitations.
...@@ -90,7 +93,7 @@ ...@@ -90,7 +93,7 @@
* 2001-June Works with usb-storage and NEC EHCI on 2.4 * 2001-June Works with usb-storage and NEC EHCI on 2.4
*/ */
#define DRIVER_VERSION "2003-Jun-13" #define DRIVER_VERSION "2003-Dec-29"
#define DRIVER_AUTHOR "David Brownell" #define DRIVER_AUTHOR "David Brownell"
#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
...@@ -901,10 +904,19 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct hcd_dev *dev, int ep) ...@@ -901,10 +904,19 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct hcd_dev *dev, int ep)
if (!qh) if (!qh)
goto done; goto done;
/* endpoints can be iso streams. for now, we don't
* accelerate iso completions ... so spin a while.
*/
if (qh->hw_info1 == 0) {
ehci_vdbg (ehci, "iso delay\n");
goto idle_timeout;
}
if (!HCD_IS_RUNNING (ehci->hcd.state)) if (!HCD_IS_RUNNING (ehci->hcd.state))
qh->qh_state = QH_STATE_IDLE; qh->qh_state = QH_STATE_IDLE;
switch (qh->qh_state) { switch (qh->qh_state) {
case QH_STATE_UNLINK: /* wait for hw to finish? */ case QH_STATE_UNLINK: /* wait for hw to finish? */
idle_timeout:
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
set_current_state (TASK_UNINTERRUPTIBLE); set_current_state (TASK_UNINTERRUPTIBLE);
schedule_timeout (1); schedule_timeout (1);
......
This diff is collapsed.
...@@ -386,6 +386,63 @@ struct ehci_qh { ...@@ -386,6 +386,63 @@ struct ehci_qh {
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/* description of one iso highspeed transaction (up to 3 KB data) */
struct ehci_iso_uframe {
/* These will be copied to iTD when scheduling */
u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */
u32 transaction; /* itd->hw_transaction[i] |= */
u8 cross; /* buf crosses pages */
};
/* temporary schedule data for highspeed packets from iso urbs
* each packet is one uframe's usb transactions, in some itd,
* beginning at stream->next_uframe
*/
struct ehci_itd_sched {
struct list_head itd_list;
unsigned span;
struct ehci_iso_uframe packet [0];
};
/*
* ehci_iso_stream - groups all (s)itds for this endpoint.
* acts like a qh would, if EHCI had them for ISO.
*/
struct ehci_iso_stream {
/* first two fields match QH, but info1 == 0 */
u32 hw_next;
u32 hw_info1;
u32 refcount;
u8 bEndpointAddress;
struct list_head itd_list; /* queued itds */
struct list_head free_itd_list; /* list of unused itds */
struct hcd_dev *dev;
/* output of (re)scheduling */
unsigned long start; /* jiffies */
unsigned long rescheduled;
int next_uframe;
/* the rest is derived from the endpoint descriptor,
* trusting urb->interval == (1 << (epdesc->bInterval - 1)),
* including the extra info for hw_bufp[0..2]
*/
u8 interval;
u8 usecs;
u16 maxp;
unsigned bandwidth;
/* This is used to initialize iTD's hw_bufp fields */
u32 buf0;
u32 buf1;
u32 buf2;
/* ... sITD won't use buf[012], and needs TT access ... */
};
/*-------------------------------------------------------------------------*/
/* /*
* EHCI Specification 0.95 Section 3.3 * EHCI Specification 0.95 Section 3.3
* Fig 3-4 "Isochronous Transaction Descriptor (iTD)" * Fig 3-4 "Isochronous Transaction Descriptor (iTD)"
...@@ -413,14 +470,14 @@ struct ehci_itd { ...@@ -413,14 +470,14 @@ struct ehci_itd {
union ehci_shadow itd_next; /* ptr to periodic q entry */ union ehci_shadow itd_next; /* ptr to periodic q entry */
struct urb *urb; struct urb *urb;
struct list_head itd_list; /* list of urb frames' itds */ struct ehci_iso_stream *stream; /* endpoint's queue */
dma_addr_t buf_dma; /* frame's buffer address */ struct list_head itd_list; /* list of stream's itds */
/* for now, only one hw_transaction per itd */ /* any/all hw_transactions here may be used by that urb */
u32 transaction; unsigned frame; /* where scheduled */
u16 index; /* in urb->iso_frame_desc */ unsigned pg;
u16 uframe; /* in periodic schedule */ unsigned index[8]; /* in urb->iso_frame_desc */
u16 usecs; u8 usecs[8];
} __attribute__ ((aligned (32))); } __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment