This patch applies on top of the other two (for init problems):
- Uses time to balance interrupt load, not number of transfers.
One 8-byte lowspeed transfer costs as much as ten same-size
at full speed ... previous code could overcommit branches.
- Shrinks the code a smidgeon, mostly in the submit path.
- Updates comments, remove some magic numbers, etc.
- Adds some debug dump routines for EDs and TDs, which can
be rather helpful when debugging!
- Lays ground work for a "shadow" <linux/list.h> TD queue
(but doesn't enlarge the TD or ED on 32bit cpus)
I'm not sure anyone would have run into that time/balance
issue, though some folk have talked about hooking up lots
of lowspeed devices and that would have made trouble.
Please merge to Linus' latest.
- Dave
--- ./drivers/usb-dist/host/ohci-hcd.c Thu Jun 13 07:59:04 2002
+++ ./drivers/usb/host/ohci-hcd.c Sat Jun 15 09:07:57 2002
@@ -100,7 +100,7 @@
* - lots more testing!!
*/
-#define DRIVER_VERSION "2002-Jun-10"
+#define DRIVER_VERSION "2002-Jun-15"
#define DRIVER_AUTHOR "Roman Weissgaerber <[EMAIL PROTECTED]>, David Brownell"
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
@@ -145,8 +145,8 @@
urb_print (urb, "SUB", usb_pipein (pipe));
#endif
- /* every endpoint has a ed, locate and fill it */
- if (! (ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags)))
+ /* every endpoint has a ed, locate and maybe (re)initialize it */
+ if (! (ed = ed_get (ohci, urb->dev, pipe, urb->interval)))
return -ENOMEM;
/* for the private part of the URB we need the number of TDs (size) */
@@ -498,6 +498,7 @@
struct ohci_regs *regs = ohci->regs;
int ints;
+ /* we can eliminate a (slow) readl() if _only_ WDH caused this irq */
if ((ohci->hcca->done_head != 0)
&& ! (le32_to_cpup (&ohci->hcca->done_head) & 0x01)) {
ints = OHCI_INTR_WDH;
--- ./drivers/usb-dist/host/ohci-q.c Thu Jun 13 07:54:54 2002
+++ ./drivers/usb/host/ohci-q.c Sat Jun 15 09:07:57 2002
@@ -131,8 +131,9 @@
/* search for the right branch to insert an interrupt ed into the int tree
* do some load balancing;
- * returns the branch and
- * sets the interval to interval = 2^integer (ld (interval))
+ * returns the branch
+ * FIXME allow for failure, when there's no bandwidth left;
+ * and consider iso loads too
*/
static int ep_int_balance (struct ohci_hcd *ohci, int interval, int load)
{
@@ -152,19 +153,6 @@
/*-------------------------------------------------------------------------*/
-/* 2^int ( ld (inter)) */
-
-static int ep_2_n_interval (int inter)
-{
- int i;
-
- for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++)
- continue;
- return 1 << i;
-}
-
-/*-------------------------------------------------------------------------*/
-
/* the int tree is a binary tree
* in order to process it sequentially the indexes of the branches have
* to be mapped the mapping reverses the bits of a word of num_bits length
@@ -230,8 +218,7 @@
case PIPE_INTERRUPT:
load = ed->intriso.intr_info.int_load;
- interval = ep_2_n_interval (ed->intriso.intr_info.int_period);
- ed->interval = interval;
+ interval = ed->interval;
int_branch = ep_int_balance (ohci, interval, load);
ed->intriso.intr_info.int_branch = int_branch;
@@ -301,6 +288,7 @@
* just the link to the ed is unlinked.
* the link from the ed still points to another operational ed or 0
* so the HC can eventually finish the processing of the unlinked ed
+ * caller guarantees the ED has no active TDs.
*/
static void start_ed_unlink (struct ohci_hcd *ohci, struct ed *ed)
{
@@ -388,84 +376,99 @@
/*-------------------------------------------------------------------------*/
-/* (re)init an endpoint; this _should_ be done once at the
- * usb_set_configuration command, but the USB stack is a bit stateless
- * so we do it at every transaction.
- * if the state of the ed is ED_NEW then a dummy td is added and the
- * state is changed to ED_UNLINK
- * in all other cases the state is left unchanged
- * the ed info fields are set even though most of them should
- * not change
+/* get and maybe (re)init an endpoint. init _should_ be done only as part
+ * of usb_set_configuration() or usb_set_interface() ... but the USB stack
+ * isn't very stateful, so we re-init whenever the HC isn't looking.
*/
-static struct ed *ep_add_ed (
+static struct ed *ed_get (
+ struct ohci_hcd *ohci,
struct usb_device *udev,
unsigned int pipe,
- int interval,
- int load,
- int mem_flags
+ int interval
) {
- struct ohci_hcd *ohci = hcd_to_ohci (udev->bus->hcpriv);
+ int is_out = !usb_pipein (pipe);
+ int type = usb_pipetype (pipe);
+ int bus_msecs = 0;
struct hcd_dev *dev = (struct hcd_dev *) udev->hcpriv;
- struct td *td;
struct ed *ed;
unsigned ep;
unsigned long flags;
- spin_lock_irqsave (&ohci->lock, flags);
-
ep = usb_pipeendpoint (pipe) << 1;
- if (!usb_pipecontrol (pipe) && usb_pipeout (pipe))
+ if (type != PIPE_CONTROL && is_out)
ep |= 1;
+ if (type == PIPE_INTERRUPT)
+ bus_msecs = usb_calc_bus_time (udev->speed, !is_out, 0,
+ usb_maxpacket (udev, pipe, is_out)) / 1000;
+
+ spin_lock_irqsave (&ohci->lock, flags);
+
if (!(ed = dev->ep [ep])) {
ed = ed_alloc (ohci, SLAB_ATOMIC);
if (!ed) {
/* out of memory */
- spin_unlock_irqrestore (&ohci->lock, flags);
- return NULL;
+ goto done;
}
dev->ep [ep] = ed;
}
if (ed->state & ED_URB_DEL) {
/* pending unlink request */
- spin_unlock_irqrestore (&ohci->lock, flags);
- return NULL;
+ ed = 0;
+ goto done;
}
if (ed->state == ED_NEW) {
+ struct td *td;
+
ed->hwINFO = ED_SKIP;
/* dummy td; end of td list for ed */
td = td_alloc (ohci, SLAB_ATOMIC);
if (!td) {
/* out of memory */
- spin_unlock_irqrestore (&ohci->lock, flags);
- return NULL;
+ ed = 0;
+ goto done;
}
ed->dummy = td;
ed->hwTailP = cpu_to_le32 (td->td_dma);
ed->hwHeadP = ed->hwTailP; /* ED_C, ED_H zeroed */
ed->state = ED_UNLINK;
- ed->type = usb_pipetype (pipe);
+ ed->type = type;
}
-// FIXME: don't do this if it's linked to the HC, or without knowing it's
-// safe to clobber state/mode info tied to (previous) config/altsetting.
-// (but dev0/ep0, used by set_address, must get clobbered)
-
- ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe)
- | usb_pipeendpoint (pipe) << 7
- | (usb_pipeisoc (pipe)? 0x8000: 0)
- | (usb_pipecontrol (pipe)
- ? 0: (usb_pipeout (pipe)? 0x800: 0x1000))
- | (udev->speed == USB_SPEED_LOW) << 13
- | usb_maxpacket (udev, pipe, usb_pipeout (pipe))
- << 16);
-
- if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) {
- ed->intriso.intr_info.int_period = interval;
- ed->intriso.intr_info.int_load = load;
- }
+ /* FIXME: Don't do this without knowing it's safe to clobber this
+ * state/mode info. Currently the upper layers don't support such
+ * guarantees; we're lucky changing config/altsetting is rare.
+ */
+ if (ed->state == ED_UNLINK) {
+ u32 info;
+ info = usb_pipedevice (pipe);
+ info |= (ep >> 1) << 7;
+ info |= usb_maxpacket (udev, pipe, is_out) << 16;
+ info = cpu_to_le32 (info);
+ if (udev->speed == USB_SPEED_LOW)
+ info |= ED_LOWSPEED;
+ /* control transfers store pids in tds */
+ if (type != PIPE_CONTROL) {
+ info |= is_out ? ED_OUT : ED_IN;
+ if (type == PIPE_ISOCHRONOUS)
+ info |= ED_ISO;
+ if (type == PIPE_INTERRUPT) {
+ ed->intriso.intr_info.int_load = bus_msecs;
+ if (interval > 32)
+ interval = 32;
+ }
+ }
+ ed->hwINFO = info;
+
+ /* value ignored except on periodic EDs, where
+ * we know it's already a power of 2
+ */
+ ed->interval = interval;
+ }
+
+done:
spin_unlock_irqrestore (&ohci->lock, flags);
return ed;
}
@@ -737,8 +740,8 @@
urb->iso_frame_desc [td->index].status = cc_to_error [cc];
if (cc != 0)
- dbg (" urb %p iso TD %d len %d CC %d",
- urb, td->index, dlen, cc);
+ dbg (" urb %p iso TD %p (%d) len %d CC %d",
+ urb, td, 1 + td->index, dlen, cc);
/* BULK, INT, CONTROL ... drivers see aggregate length/status,
* except that "setup" bytes aren't counted and "short" transfers
@@ -777,9 +780,13 @@
- td->data_dma;
}
+#ifdef VERBOSE_DEBUG
if (cc != 0)
- dbg (" urb %p TD %d CC %d, len=%d",
- urb, td->index, cc, urb->actual_length);
+ dbg (" urb %p TD %p (%d) CC %d, len=%d/%d",
+ urb, td, 1 + td->index, cc,
+ urb->actual_length,
+ urb->transfer_buffer_length);
+#endif
}
}
@@ -813,8 +820,8 @@
if (urb_priv && ((td_list->index + 1)
< urb_priv->length)) {
#ifdef OHCI_VERBOSE_DEBUG
- dbg ("urb %p TD %d of %d, patch ED",
- td_list->urb,
+ dbg ("urb %p TD %p (%d/%d), patch ED",
+ td_list->urb, td_list,
1 + td_list->index,
urb_priv->length);
#endif
--- ./drivers/usb-dist/host/ohci-mem.c Sun Jun 9 10:40:07 2002
+++ ./drivers/usb/host/ohci-mem.c Sat Jun 15 09:07:57 2002
@@ -221,6 +221,7 @@
ed = pci_pool_alloc (hc->ed_cache, mem_flags, &dma);
if (ed) {
memset (ed, 0, sizeof (*ed));
+ INIT_LIST_HEAD (&ed->td_list);
ed->dma = dma;
/* hash it for later reverse mapping */
if (!hash_add_ed (hc, ed, mem_flags)) {
--- ./drivers/usb-dist/host/ohci-dbg.c Tue Apr 23 08:46:40 2002
+++ ./drivers/usb/host/ohci-dbg.c Sat Jun 15 09:07:57 2002
@@ -74,9 +74,9 @@
static inline struct ed *
dma_to_ed (struct ohci_hcd *hc, dma_addr_t ed_dma);
-#ifdef OHCI_VERBOSE_DEBUG
/* print non-empty branches of the periodic ed tree */
-void ohci_dump_periodic (struct ohci_hcd *ohci, char *label)
+static void __attribute__ ((unused))
+ohci_dump_periodic (struct ohci_hcd *ohci, char *label)
{
int i, j;
u32 *ed_p;
@@ -101,7 +101,6 @@
printk (KERN_DEBUG "%s, ohci %s, empty periodic schedule\n",
label, ohci->hcd.self.bus_name);
}
-#endif
static void ohci_dump_intr_mask (char *label, __u32 mask)
{
@@ -241,6 +240,97 @@
ohci_dump_roothub (controller, 1);
}
+static void ohci_dump_td (char *label, struct td *td)
+{
+ u32 tmp = le32_to_cpup (&td->hwINFO);
+
+ dbg ("%s td %p; urb %p index %d; hw next td %08x",
+ label, td,
+ td->urb, td->index,
+ le32_to_cpup (&td->hwNextTD));
+ if ((tmp & TD_ISO) == 0) {
+ char *toggle, *pid;
+ u32 cbp, be;
+
+ switch (tmp & TD_T) {
+ case TD_T_DATA0: toggle = "DATA0"; break;
+ case TD_T_DATA1: toggle = "DATA1"; break;
+ case TD_T_TOGGLE: toggle = "(CARRY)"; break;
+ default: toggle = "(?)"; break;
+ }
+ switch (tmp & TD_DP) {
+ case TD_DP_SETUP: pid = "SETUP"; break;
+ case TD_DP_IN: pid = "IN"; break;
+ case TD_DP_OUT: pid = "OUT"; break;
+ default: pid = "(bad pid)"; break;
+ }
+ dbg (" info %08x CC=%x %s DI=%d %s %s", tmp,
+ TD_CC_GET(tmp), /* EC, */ toggle,
+ (tmp & TD_DI) >> 21, pid,
+ (tmp & TD_R) ? "R" : "");
+ cbp = le32_to_cpup (&td->hwCBP);
+ be = le32_to_cpup (&td->hwBE);
+ dbg (" cbp %08x be %08x (len %d)", cbp, be,
+ cbp ? (be + 1 - cbp) : 0);
+ } else {
+ unsigned i;
+ dbg (" info %08x CC=%x DI=%d START=%04x", tmp,
+ TD_CC_GET(tmp), /* FC, */
+ (tmp & TD_DI) >> 21,
+ tmp & 0x0000ffff);
+ dbg (" bp0 %08x be %08x",
+ le32_to_cpup (&td->hwCBP) & ~0x0fff,
+ le32_to_cpup (&td->hwBE));
+ for (i = 0; i < MAXPSW; i++) {
+ dbg (" psw [%d] = %2x", i,
+ le16_to_cpu (td->hwPSW [i]));
+ }
+ }
+}
+
+/* caller MUST own hcd spinlock if verbose is set! */
+static void __attribute__((unused))
+ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose)
+{
+ u32 tmp = ed->hwINFO;
+ char *type = "";
+
+ dbg ("%s: %s, ed %p state 0x%x type %d; next ed %08x",
+ ohci->hcd.self.bus_name, label,
+ ed, ed->state, ed->type,
+ le32_to_cpup (&ed->hwNextED));
+ switch (tmp & (ED_IN|ED_OUT)) {
+ case ED_OUT: type = "-OUT"; break;
+ case ED_IN: type = "-IN"; break;
+ /* else from TDs ... control */
+ }
+ dbg (" info %08x MAX=%d%s%s%s EP=%d%s DEV=%d", le32_to_cpu (tmp),
+ 0x0fff & (le32_to_cpu (tmp) >> 16),
+ (tmp & ED_ISO) ? " ISO" : "",
+ (tmp & ED_SKIP) ? " SKIP" : "",
+ (tmp & ED_LOWSPEED) ? " LOW" : "",
+ 0x000f & (le32_to_cpu (tmp) >> 7),
+ type,
+ 0x007f & le32_to_cpu (tmp));
+ dbg (" tds: head %08x%s%s tail %08x%s",
+ tmp = le32_to_cpup (&ed->hwHeadP),
+ (ed->hwHeadP & ED_H) ? " HALT" : "",
+ (ed->hwHeadP & ED_C) ? " CARRY" : "",
+ le32_to_cpup (&ed->hwTailP),
+ verbose ? "" : " (not listing)");
+ if (verbose) {
+ struct list_head *tmp;
+
+ /* use ed->td_list because HC concurrently modifies
+ * hwNextTD as it accumulates ed_donelist.
+ */
+ list_for_each (tmp, &ed->td_list) {
+ struct td *td;
+ td = list_entry (tmp, struct td, td_list);
+ ohci_dump_td (" ->", td);
+ }
+ }
+}
#endif
--- ./drivers/usb-dist/host/ohci.h Thu Jun 13 07:54:54 2002
+++ ./drivers/usb/host/ohci.h Sat Jun 15 09:07:57 2002
@@ -19,7 +19,7 @@
#define ED_SKIP __constant_cpu_to_le32(1 << 14)
#define ED_LOWSPEED __constant_cpu_to_le32(1 << 13)
#define ED_OUT __constant_cpu_to_le32(0x01 << 11)
-#define ED_IN __constant_cpu_to_le32(0x10 << 11)
+#define ED_IN __constant_cpu_to_le32(0x02 << 11)
__u32 hwTailP; /* tail of TD list */
__u32 hwHeadP; /* head of TD list */
#define ED_C __constant_cpu_to_le32(0x02) /* toggle carry */
@@ -30,24 +30,24 @@
dma_addr_t dma; /* addr of ED */
struct ed *ed_prev; /* for non-interrupt EDs */
struct td *dummy;
+ struct list_head td_list; /* "shadow list" of our TDs */
+
+ u8 state; /* ED_{NEW,UNLINK,OPER} */
+#define ED_NEW 0x00 /* unused, no dummy td */
+#define ED_UNLINK 0x01 /* dummy td, maybe linked to hc */
+#define ED_OPER 0x02 /* dummy td, _is_ linked to hc */
+#define ED_URB_DEL 0x08 /* for unlinking; masked in */
u8 type; /* PIPE_{BULK,...} */
- u8 interval; /* interrupt, isochronous */
+ u16 interval; /* interrupt, isochronous */
union {
struct intr_info { /* interrupt */
- u8 int_period;
u8 int_branch;
u8 int_load;
} intr_info;
u16 last_iso; /* isochronous */
} intriso;
- u8 state; /* ED_{NEW,UNLINK,OPER} */
-#define ED_NEW 0x00 /* unused, no dummy td */
-#define ED_UNLINK 0x01 /* dummy td, maybe linked to hc */
-#define ED_OPER 0x02 /* dummy td, _is_ linked to hc */
-#define ED_URB_DEL 0x08 /* for unlinking; masked in */
-
/* HC may see EDs on rm_list until next frame (frame_no == tick) */
u16 tick;
struct ed *ed_rm_list;
@@ -108,6 +108,8 @@
dma_addr_t td_dma; /* addr of this TD */
dma_addr_t data_dma; /* addr of data it points to */
+
+ struct list_head td_list; /* "shadow list", TDs on same ED */
} __attribute__ ((aligned(32))); /* c/b/i need 16; only iso needs 32 */
#define TD_MASK ((u32)~0x1f) /* strip hw status in low addr bits */