ChangeSet 1.749, 2002/10/21 14:48:25-07:00, dbrownell@users.sourceforge.net [PATCH] USB: USB 2.0 controller and hubs bugfixes Yes, this looks like a big patch, but for users with USB 2.0 devices it is necessary. It contains the following things: - Key point: this works, more reliably, on a lot of hardware that previously did not work. So it's got all the bugfixes that went into 2.5 since three months into the 2.4.19 series, and a fair degree of user testing. Quite a few users have reported complete failure on their 2.4 systems until they updated ... and that the update gave them no troubles. - Adds missing locking to some queue unlink paths. This resolves some oopsing problems (often null pointer exceptions) that were rare quite some time ago, but became more common as the driver is (a) used much more, and (b) used on faster EHCI implementations, like the VIA VT8235 and other recent silicon. - Fixes the problems when used with cardbus. Previously if you did a physical eject without first "rmmod ehci-hcd" (or even a system shutdown, which is a cardbus issue) the system would lock up. No more. diff -Nru a/drivers/usb/hcd/ehci-dbg.c b/drivers/usb/hcd/ehci-dbg.c --- a/drivers/usb/hcd/ehci-dbg.c Mon Oct 21 22:14:31 2002 +++ b/drivers/usb/hcd/ehci-dbg.c Mon Oct 21 22:14:31 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001 by David Brownell + * Copyright (c) 2001-2002 by David Brownell * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -26,8 +26,10 @@ #ifdef DEBUG -/* check the values in the HCSPARAMS register - host controller structural parameters */ -/* see EHCI 0.95 Spec, Table 2-4 for each value */ +/* check the values in the HCSPARAMS register + * (host controller _Structural_ parameters) + * see EHCI spec, Table 2-4 for each value + */ static void dbg_hcs_params (struct ehci_hcd *ehci, char *label) { u32 params = readl (&ehci->caps->hcs_params); @@ -55,7 +57,7 @@ strcat(buf, tmp); } dbg ("%s: %s portroute %s", - ehci->hcd.bus_name, label, + hcd_to_bus (&ehci->hcd)->bus_name, label, buf); } } @@ -67,25 +69,27 @@ #ifdef DEBUG -/* check the values in the HCCPARAMS register - host controller capability parameters */ -/* see EHCI 0.95 Spec, Table 2-5 for each value */ +/* check the values in the HCCPARAMS register + * (host controller _Capability_ parameters) + * see EHCI Spec, Table 2-5 for each value + * */ static void dbg_hcc_params (struct ehci_hcd *ehci, char *label) { u32 params = readl (&ehci->caps->hcc_params); if (HCC_EXT_CAPS (params)) { // EHCI 0.96 ... could interpret these (legacy?) - dbg ("%s extended capabilities at pci %d", + dbg ("%s extended capabilities at pci %2x", label, HCC_EXT_CAPS (params)); } if (HCC_ISOC_CACHE (params)) { - dbg ("%s hcc_params 0x%04x caching frame %s%s%s", + dbg ("%s hcc_params %04x caching frame %s%s%s", label, params, HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024", HCC_CANPARK (params) ? " park" : "", HCC_64BIT_ADDR (params) ? " 64 bit addr" : ""); } else { - dbg ("%s hcc_params 0x%04x caching %d uframes %s%s%s", + dbg ("%s hcc_params %04x caching %d uframes %s%s%s", label, params, HCC_ISOC_THRES (params), @@ -102,8 +106,8 @@ #ifdef DEBUG -#if 0 -static void dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) +static void __attribute__((__unused__)) +dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) { dbg ("%s %p info1 %x info2 %x hw_curr %x qtd_next %x", label, qh, qh->hw_info1, qh->hw_info2, @@ -117,63 +121,462 @@ qh->hw_buf [4]); } } -#endif + +static int __attribute__((__unused__)) +dbg_status_buf (char *buf, unsigned len, char *label, u32 status) +{ + return snprintf (buf, len, + "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", + label, label [0] ? " " : "", status, + (status & STS_ASS) ? " Async" : "", + (status & STS_PSS) ? " Periodic" : "", + (status & STS_RECL) ? " Recl" : "", + (status & STS_HALT) ? " Halt" : "", + (status & STS_IAA) ? " IAA" : "", + (status & STS_FATAL) ? " FATAL" : "", + (status & STS_FLR) ? " FLR" : "", + (status & STS_PCD) ? " PCD" : "", + (status & STS_ERR) ? " ERR" : "", + (status & STS_INT) ? " INT" : "" + ); +} + +static int __attribute__((__unused__)) +dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable) +{ + return snprintf (buf, len, + "%s%sintrenable %02x%s%s%s%s%s%s", + label, label [0] ? " " : "", enable, + (enable & STS_IAA) ? " IAA" : "", + (enable & STS_FATAL) ? " FATAL" : "", + (enable & STS_FLR) ? " FLR" : "", + (enable & STS_PCD) ? " PCD" : "", + (enable & STS_ERR) ? " ERR" : "", + (enable & STS_INT) ? " INT" : "" + ); +} static const char *const fls_strings [] = { "1024", "512", "256", "??" }; +static int dbg_command_buf (char *buf, unsigned len, char *label, u32 command) +{ + return snprintf (buf, len, + "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s", + label, label [0] ? " " : "", command, + (command & CMD_PARK) ? "park" : "(park)", + CMD_PARK_CNT (command), + (command >> 16) & 0x3f, + (command & CMD_LRESET) ? " LReset" : "", + (command & CMD_IAAD) ? " IAAD" : "", + (command & CMD_ASE) ? " Async" : "", + (command & CMD_PSE) ? " Periodic" : "", + fls_strings [(command >> 2) & 0x3], + (command & CMD_RESET) ? " Reset" : "", + (command & CMD_RUN) ? "RUN" : "HALT" + ); +} + +static int +dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status) +{ + char *sig; + + /* signaling state */ + switch (status & (3 << 10)) { + case 0 << 10: sig = "se0"; break; + case 1 << 10: sig = "k"; break; /* low speed */ + case 2 << 10: sig = "j"; break; + default: sig = "?"; break; + } + + return snprintf (buf, len, + "%s%sport %d status %06x%s%s sig=%s %s%s%s%s%s%s%s%s%s", + label, label [0] ? " " : "", port, status, + (status & PORT_POWER) ? " POWER" : "", + (status & PORT_OWNER) ? " OWNER" : "", + sig, + (status & PORT_RESET) ? " RESET" : "", + (status & PORT_SUSPEND) ? " SUSPEND" : "", + (status & PORT_RESUME) ? " RESUME" : "", + (status & PORT_OCC) ? " OCC" : "", + (status & PORT_OC) ? " OC" : "", + (status & PORT_PEC) ? " PEC" : "", + (status & PORT_PE) ? " PE" : "", + (status & PORT_CSC) ? " CSC" : "", + (status & PORT_CONNECT) ? " CONNECT" : "" + ); +} + #else -#if 0 -static inline void dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) {} -#endif +static inline void __attribute__((__unused__)) +dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) +{} + +static inline int __attribute__((__unused__)) +dbg_status_buf (char *buf, unsigned len, char *label, u32 status) +{ return 0; } + +static inline int __attribute__((__unused__)) +dbg_command_buf (char *buf, unsigned len, char *label, u32 command) +{ return 0; } + +static inline int __attribute__((__unused__)) +dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable) +{ return 0; } + +static inline int __attribute__((__unused__)) +dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status) +{ return 0; } + #endif /* DEBUG */ /* functions have the "wrong" filename when they're output... */ +#define dbg_status(ehci, label, status) { \ + char _buf [80]; \ + dbg_status_buf (_buf, sizeof _buf, label, status); \ + dbg ("%s", _buf); \ +} + +#define dbg_cmd(ehci, label, command) { \ + char _buf [80]; \ + dbg_command_buf (_buf, sizeof _buf, label, command); \ + dbg ("%s", _buf); \ +} + +#define dbg_port(hcd, label, port, status) { \ + char _buf [80]; \ + dbg_port_buf (_buf, sizeof _buf, label, port, status); \ + dbg ("%s", _buf); \ +} + +/*-------------------------------------------------------------------------*/ + +#ifdef STUB_DEBUG_FILES + +static inline void create_debug_files (struct ehci_hcd *bus) { } +static inline void remove_debug_files (struct ehci_hcd *bus) { } + +#else + +/* troubleshooting help: expose state in driverfs */ + +#define speed_char(info1) ({ char tmp; \ + switch (info1 & (3 << 12)) { \ + case 0 << 12: tmp = 'f'; break; \ + case 1 << 12: tmp = 'l'; break; \ + case 2 << 12: tmp = 'h'; break; \ + default: tmp = '?'; break; \ + }; tmp; }) + +static void qh_lines (struct ehci_qh *qh, char **nextp, unsigned *sizep) +{ + u32 scratch; + struct list_head *entry; + struct ehci_qtd *td; + unsigned temp; + unsigned size = *sizep; + char *next = *nextp; + + scratch = cpu_to_le32p (&qh->hw_info1); + temp = snprintf (next, size, "qh/%p dev%d %cs ep%d %08x %08x", + qh, scratch & 0x007f, + speed_char (scratch), + (scratch >> 8) & 0x000f, + scratch, cpu_to_le32p (&qh->hw_info2)); + size -= temp; + next += temp; + + list_for_each (entry, &qh->qtd_list) { + td = list_entry (entry, struct ehci_qtd, + qtd_list); + scratch = cpu_to_le32p (&td->hw_token); + temp = snprintf (next, size, + "\n\ttd/%p %s len=%d %08x urb %p", + td, ({ char *tmp; + switch ((scratch>>8)&0x03) { + case 0: tmp = "out"; break; + case 1: tmp = "in"; break; + case 2: tmp = "setup"; break; + default: tmp = "?"; break; + } tmp;}), + (scratch >> 16) & 0x7fff, + scratch, + td->urb); + size -= temp; + next += temp; + } + + temp = snprintf (next, size, "\n"); + *sizep = size - temp; + *nextp = next + temp; +} + +static ssize_t +show_async (struct device *dev, char *buf, size_t count, loff_t off) +{ + struct pci_dev *pdev; + struct ehci_hcd *ehci; + unsigned long flags; + unsigned temp, size; + char *next; + struct ehci_qh *qh; + + if (off != 0) + return 0; + + pdev = container_of (dev, struct pci_dev, dev); + ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd); + next = buf; + size = count; + + /* dumps a snapshot of the async schedule. + * usually empty except for long-term bulk reads, or head. + * one QH per line, and TDs we know about + */ + spin_lock_irqsave (&ehci->lock, flags); + if (ehci->async) { + qh = ehci->async; + do { + qh_lines (qh, &next, &size); + } while ((qh = qh->qh_next.qh) != ehci->async); + } + if (ehci->reclaim) { + temp = snprintf (next, size, "\nreclaim =\n"); + size -= temp; + next += temp; + + qh_lines (ehci->reclaim, &next, &size); + } + spin_unlock_irqrestore (&ehci->lock, flags); + + return count - size; +} +static DEVICE_ATTR (async, S_IRUGO, show_async, NULL); + +#define DBG_SCHED_LIMIT 64 + +static ssize_t +show_periodic (struct device *dev, char *buf, size_t count, loff_t off) +{ + struct pci_dev *pdev; + struct ehci_hcd *ehci; + unsigned long flags; + union ehci_shadow p, *seen; + unsigned temp, size, seen_count; + char *next; + unsigned i, tag; + + if (off != 0) + return 0; + if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC))) + return 0; + seen_count = 0; + + pdev = container_of (dev, struct pci_dev, dev); + ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd); + next = buf; + size = count; + + temp = snprintf (next, size, "size = %d\n", ehci->periodic_size); + size -= temp; + next += temp; + + /* dump a snapshot of the periodic schedule. + * iso changes, interrupt usually doesn't. + */ + spin_lock_irqsave (&ehci->lock, flags); + for (i = 0; i < ehci->periodic_size; i++) { + p = ehci->pshadow [i]; + if (!p.ptr) + continue; + tag = Q_NEXT_TYPE (ehci->periodic [i]); + + temp = snprintf (next, size, "%4d: ", i); + size -= temp; + next += temp; + + do { + switch (tag) { + case Q_TYPE_QH: + temp = snprintf (next, size, " qh%d/%p", + p.qh->period, p.qh); + size -= temp; + next += temp; + for (temp = 0; temp < seen_count; temp++) { + if (seen [temp].ptr == p.ptr) + break; + } + /* show more info the first time around */ + if (temp == seen_count) { + u32 scratch = cpu_to_le32p ( + &p.qh->hw_info1); + + temp = snprintf (next, size, + " (%cs dev%d ep%d [%d/%d] %d)", + speed_char (scratch), + scratch & 0x007f, + (scratch >> 8) & 0x000f, + p.qh->usecs, p.qh->c_usecs, + scratch >> 16); + + /* FIXME TD info too */ + + if (seen_count < DBG_SCHED_LIMIT) + seen [seen_count++].qh = p.qh; + } else + temp = 0; + tag = Q_NEXT_TYPE (p.qh->hw_next); + p = p.qh->qh_next; + break; + case Q_TYPE_FSTN: + temp = snprintf (next, size, + " fstn-%8x/%p", p.fstn->hw_prev, + p.fstn); + tag = Q_NEXT_TYPE (p.fstn->hw_next); + p = p.fstn->fstn_next; + break; + case Q_TYPE_ITD: + temp = snprintf (next, size, + " itd/%p", p.itd); + tag = Q_NEXT_TYPE (p.itd->hw_next); + p = p.itd->itd_next; + break; + case Q_TYPE_SITD: + temp = snprintf (next, size, + " sitd/%p", p.sitd); + tag = Q_NEXT_TYPE (p.sitd->hw_next); + p = p.sitd->sitd_next; + break; + } + size -= temp; + next += temp; + } while (p.ptr); + + temp = snprintf (next, size, "\n"); + size -= temp; + next += temp; + } + spin_unlock_irqrestore (&ehci->lock, flags); + kfree (seen); + + return count - size; +} +static DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL); + +#undef DBG_SCHED_LIMIT + +static ssize_t +show_registers (struct device *dev, char *buf, size_t count, loff_t off) +{ + struct pci_dev *pdev; + struct ehci_hcd *ehci; + unsigned long flags; + unsigned temp, size, i; + char *next, scratch [80]; + static char fmt [] = "%*s\n"; + static char label [] = ""; + + if (off != 0) + return 0; + + pdev = container_of (dev, struct pci_dev, dev); + ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd); + + next = buf; + size = count; + + spin_lock_irqsave (&ehci->lock, flags); + + /* Capability Registers */ + i = readw (&ehci->caps->hci_version); + temp = snprintf (next, size, "EHCI %x.%02x, hcd state %d\n", + i >> 8, i & 0x0ff, ehci->hcd.state); + size -= temp; + next += temp; + + // FIXME interpret both types of params + i = readl (&ehci->caps->hcs_params); + temp = snprintf (next, size, "structural params 0x%08x\n", i); + size -= temp; + next += temp; + + i = readl (&ehci->caps->hcc_params); + temp = snprintf (next, size, "capability params 0x%08x\n", i); + size -= temp; + next += temp; + + /* Operational Registers */ + temp = dbg_status_buf (scratch, sizeof scratch, label, + readl (&ehci->regs->status)); + temp = snprintf (next, size, fmt, temp, scratch); + size -= temp; + next += temp; + + temp = dbg_command_buf (scratch, sizeof scratch, label, + readl (&ehci->regs->command)); + temp = snprintf (next, size, fmt, temp, scratch); + size -= temp; + next += temp; + + temp = dbg_intr_buf (scratch, sizeof scratch, label, + readl (&ehci->regs->intr_enable)); + temp = snprintf (next, size, fmt, temp, scratch); + size -= temp; + next += temp; + + temp = snprintf (next, size, "uframe %04x\n", + readl (&ehci->regs->frame_index)); + size -= temp; + next += temp; + + for (i = 0; i < HCS_N_PORTS (ehci->hcs_params); i++) { + temp = dbg_port_buf (scratch, sizeof scratch, label, i, + readl (&ehci->regs->port_status [i])); + temp = snprintf (next, size, fmt, temp, scratch); + size -= temp; + next += temp; + } + + if (ehci->reclaim) { + temp = snprintf (next, size, "reclaim qh %p%s\n", + ehci->reclaim, + ehci->reclaim_ready ? " ready" : ""); + size -= temp; + next += temp; + } + +#ifdef EHCI_STATS + temp = snprintf (next, size, "irq normal %ld err %ld reclaim %ld\n", + ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim); + size -= temp; + next += temp; + + temp = snprintf (next, size, "complete %ld unlink %ld qpatch %ld\n", + ehci->stats.complete, ehci->stats.unlink, ehci->stats.qpatch); + size -= temp; + next += temp; +#endif + + spin_unlock_irqrestore (&ehci->lock, flags); + + return count - size; +} +static DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL); + +static inline void create_debug_files (struct ehci_hcd *bus) +{ + device_create_file (&bus->hcd.pdev->dev, &dev_attr_async); + device_create_file (&bus->hcd.pdev->dev, &dev_attr_periodic); + device_create_file (&bus->hcd.pdev->dev, &dev_attr_registers); +} + +static inline void remove_debug_files (struct ehci_hcd *bus) +{ + device_remove_file (&bus->hcd.pdev->dev, &dev_attr_async); + device_remove_file (&bus->hcd.pdev->dev, &dev_attr_periodic); + device_remove_file (&bus->hcd.pdev->dev, &dev_attr_registers); +} -#define dbg_status(ehci, label, status) \ - dbg ("%s status 0x%x%s%s%s%s%s%s%s%s%s%s", \ - label, status, \ - (status & STS_ASS) ? " Async" : "", \ - (status & STS_PSS) ? " Periodic" : "", \ - (status & STS_RECL) ? " Recl" : "", \ - (status & STS_HALT) ? " Halt" : "", \ - (status & STS_IAA) ? " IAA" : "", \ - (status & STS_FATAL) ? " FATAL" : "", \ - (status & STS_FLR) ? " FLR" : "", \ - (status & STS_PCD) ? " PCD" : "", \ - (status & STS_ERR) ? " ERR" : "", \ - (status & STS_INT) ? " INT" : "" \ - ) - -#define dbg_cmd(ehci, label, command) \ - dbg ("%s %x cmd %s=%d ithresh=%d%s%s%s%s period=%s%s %s", \ - label, command, \ - (command & CMD_PARK) ? "park" : "(park)", \ - CMD_PARK_CNT (command), \ - (command >> 16) & 0x3f, \ - (command & CMD_LRESET) ? " LReset" : "", \ - (command & CMD_IAAD) ? " IAAD" : "", \ - (command & CMD_ASE) ? " Async" : "", \ - (command & CMD_PSE) ? " Periodic" : "", \ - fls_strings [(command >> 2) & 0x3], \ - (command & CMD_RESET) ? " Reset" : "", \ - (command & CMD_RUN) ? "RUN" : "HALT" \ - ) - -#define dbg_port(hcd, label, port, status) \ - dbg ("%s port %d status 0x%x%s%s speed=%d%s%s%s%s%s%s%s%s%s", \ - label, port, status, \ - (status & PORT_OWNER) ? " OWNER" : "", \ - (status & PORT_POWER) ? " POWER" : "", \ - (status >> 10) & 3, \ - (status & PORT_RESET) ? " RESET" : "", \ - (status & PORT_SUSPEND) ? " SUSPEND" : "", \ - (status & PORT_RESUME) ? " RESUME" : "", \ - (status & PORT_OCC) ? " OCC" : "", \ - (status & PORT_OC) ? " OC" : "", \ - (status & PORT_PEC) ? " PEC" : "", \ - (status & PORT_PE) ? " PE" : "", \ - (status & PORT_CSC) ? " CSC" : "", \ - (status & PORT_CONNECT) ? " CONNECT" : "" \ - ) +#endif /* STUB_DEBUG_FILES */ diff -Nru a/drivers/usb/hcd/ehci-hcd.c b/drivers/usb/hcd/ehci-hcd.c --- a/drivers/usb/hcd/ehci-hcd.c Mon Oct 21 22:14:31 2002 +++ b/drivers/usb/hcd/ehci-hcd.c Mon Oct 21 22:14:31 2002 @@ -38,7 +38,13 @@ #endif #include + +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32) #include "../hcd.h" +#else +#include "../core/hcd.h" +#endif #include #include @@ -46,8 +52,6 @@ #include #include -//#undef KERN_DEBUG -//#define KERN_DEBUG "" /*-------------------------------------------------------------------------*/ @@ -55,21 +59,24 @@ * EHCI hc_driver implementation ... experimental, incomplete. * Based on the final 1.0 register interface specification. * - * There are lots of things to help out with here ... notably - * everything "periodic", and of course testing with all sorts - * of usb 2.0 devices and configurations. - * * USB 2.0 shows up in upcoming www.pcmcia.org technology. * First was PCMCIA, like ISA; then CardBus, which is PCI. * Next comes "CardBay", using USB 2.0 signals. * - * Contains additional contributions by: - * Brad Hards - * Rory Bolt - * ... + * Contains additional contributions by: Brad Hards, Rory Bolt, and more. + * Special thanks to Intel and VIA for providing host controllers to + * test this driver on, and Cypress (including In-System Design) for + * providing early devices for those host controllers to talk to! * * HISTORY: * + * 2002-08-06 Handling for bulk and interrupt transfers is mostly shared; + * only scheduling is different, no arbitrary limitations. + * 2002-07-25 Sanity check PCI reads, mostly for better cardbus support, + * clean up HC run state handshaking. + * 2002-05-24 Preliminary FS/LS interrupts, using scheduling shortcuts + * 2002-05-11 Clear TT errors for FS/LS ctrl/bulk. Fill in some other + * missing pieces: enabling 64bit dma, handoff from BIOS/SMM. * 2002-05-07 Some error path cleanups to report better errors; wmb(); * use non-CVS version id; better iso bandwidth claim. * 2002-04-19 Control/bulk/interrupt submit no longer uses giveback() on @@ -85,14 +92,22 @@ * 2001-June Works with usb-storage and NEC EHCI on 2.4 */ -#define DRIVER_VERSION "2002-May-07" +#define DRIVER_VERSION "2002-Sep-23" #define DRIVER_AUTHOR "David Brownell" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" +static const char hcd_name [] = "ehci-hcd"; + // #define EHCI_VERBOSE_DEBUG // #define have_split_iso +#ifdef DEBUG +#define EHCI_STATS +#endif + +#define INTR_AUTOMAGIC /* to be removed later in 2.5 */ + /* magic numbers that can affect system performance */ #define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ #define EHCI_TUNE_RL_HS 0 /* nak throttle; see 4.9 */ @@ -100,11 +115,20 @@ #define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ #define EHCI_TUNE_MULT_TT 1 +#define EHCI_WATCHDOG_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ +#define EHCI_ASYNC_JIFFIES (HZ/3) /* async idle timeout */ + /* Initial IRQ latency: lower than default */ static int log2_irq_thresh = 0; // 0 to 6 MODULE_PARM (log2_irq_thresh, "i"); MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); +/* allow irqs at least every N URB completions */ +static int max_completions = 16; +MODULE_PARM (max_completions, "i"); +MODULE_PARM_DESC (max_completions, + "limit for urb completions called with irqs disenabled"); + #define INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT) /*-------------------------------------------------------------------------*/ @@ -115,42 +139,105 @@ /*-------------------------------------------------------------------------*/ /* + * handshake - spin reading hc until handshake completes or fails + * @ptr: address of hc register to be read + * @mask: bits to look at in result of read + * @done: value of those bits when handshake succeeds + * @usec: timeout in microseconds + * + * Returns negative errno, or zero on success + * + * Success happens when the "mask" bits have the specified value (hardware + * handshake done). There are two failure modes: "usec" have passed (major + * hardware flakeout), or the register reads as all-ones (hardware removed). + * + * That last failure should_only happen in cases like physical cardbus eject + * before driver shutdown. But it also seems to be caused by bugs in cardbus + * bridge shutdown: shutting down the bridge before the devices using it. + */ +static int handshake (u32 *ptr, u32 mask, u32 done, int usec) +{ + u32 result; + + do { + result = readl (ptr); + if (result == ~(u32)0) /* card removed */ + return -ENODEV; + result &= mask; + if (result == done) + return 0; + udelay (1); + usec--; + } while (usec > 0); + return -ETIMEDOUT; +} + +/* * hc states include: unknown, halted, ready, running * transitional states are messy just now * trying to avoid "running" unless urbs are active * a "ready" hc can be finishing prefetched work */ -/* halt a non-running controller */ -static void ehci_reset (struct ehci_hcd *ehci) +/* force HC to halt state from unknown (EHCI spec section 2.3) */ +static int ehci_halt (struct ehci_hcd *ehci) +{ + u32 temp = readl (&ehci->regs->status); + + if ((temp & STS_HALT) != 0) + return 0; + + temp = readl (&ehci->regs->command); + temp &= ~CMD_RUN; + writel (temp, &ehci->regs->command); + return handshake (&ehci->regs->status, STS_HALT, STS_HALT, 16 * 125); +} + +/* reset a non-running (STS_HALT == 1) controller */ +static int ehci_reset (struct ehci_hcd *ehci) { u32 command = readl (&ehci->regs->command); command |= CMD_RESET; dbg_cmd (ehci, "reset", command); writel (command, &ehci->regs->command); - while (readl (&ehci->regs->command) & CMD_RESET) - continue; ehci->hcd.state = USB_STATE_HALT; + return handshake (&ehci->regs->command, CMD_RESET, 0, 250); } /* idle the controller (from running) */ static void ehci_ready (struct ehci_hcd *ehci) { - u32 command; + u32 temp; #ifdef DEBUG if (!HCD_IS_RUNNING (ehci->hcd.state)) BUG (); #endif - while (!(readl (&ehci->regs->status) & (STS_ASS | STS_PSS))) - udelay (100); - command = readl (&ehci->regs->command); - command &= ~(CMD_ASE | CMD_IAAD | CMD_PSE); - writel (command, &ehci->regs->command); + /* wait for any schedule enables/disables to take effect */ + temp = 0; + if (ehci->async) + temp = STS_ASS; + if (ehci->next_uframe != -1) + temp |= STS_PSS; + if (handshake (&ehci->regs->status, STS_ASS | STS_PSS, + temp, 16 * 125) != 0) { + ehci->hcd.state = USB_STATE_HALT; + return; + } + + /* then disable anything that's still active */ + temp = readl (&ehci->regs->command); + temp &= ~(CMD_ASE | CMD_IAAD | CMD_PSE); + writel (temp, &ehci->regs->command); - // hardware can take 16 microframes to turn off ... + /* hardware can take 16 microframes to turn off ... */ + if (handshake (&ehci->regs->status, STS_ASS | STS_PSS, + 0, 16 * 125) != 0) { + ehci->hcd.state = USB_STATE_HALT; + return; + } ehci->hcd.state = USB_STATE_READY; } @@ -165,6 +252,52 @@ static void ehci_tasklet (unsigned long param); +static void ehci_irq (struct usb_hcd *hcd); + +static void ehci_watchdog (unsigned long param) +{ + struct ehci_hcd *ehci = (struct ehci_hcd *) param; + unsigned long flags; + + spin_lock_irqsave (&ehci->lock, flags); + /* guard against lost IAA, which wedges everything */ + ehci_irq (&ehci->hcd); + /* unlink the last qh after it's idled a while */ + if (ehci->async_idle) { + start_unlink_async (ehci, ehci->async); + ehci->async_idle = 0; + } + spin_unlock_irqrestore (&ehci->lock, flags); +} + +/* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/... + * off the controller (maybe it can boot from highspeed USB disks). + */ +static int bios_handoff (struct ehci_hcd *ehci, int where, u32 cap) +{ + if (cap & (1 << 16)) { + int msec = 500; + + /* request handoff to OS */ + cap &= 1 << 24; + pci_write_config_dword (ehci->hcd.pdev, where, cap); + + /* and wait a while for it to happen */ + do { + wait_ms (10); + msec -= 10; + pci_read_config_dword (ehci->hcd.pdev, where, &cap); + } while ((cap & (1 << 16)) && msec); + if (cap & (1 << 16)) { + info ("BIOS handoff failed (%d, %04x)", where, cap); + return 1; + } + dbg ("BIOS handoff succeeded"); + } else + dbg ("BIOS handoff not needed"); + return 0; +} + /* called by khubd or root hub init threads */ static int ehci_start (struct usb_hcd *hcd) @@ -172,14 +305,11 @@ struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 temp; struct usb_device *udev; + struct usb_bus *bus; int retval; u32 hcc_params; u8 tempbyte; - // FIXME: given EHCI 0.96 or later, and a controller with - // the USBLEGSUP/USBLEGCTLSTS extended capability, make sure - // the BIOS doesn't still own this controller. - spin_lock_init (&ehci->lock); ehci->caps = (struct ehci_caps *) hcd->regs; @@ -187,9 +317,37 @@ dbg_hcs_params (ehci, "ehci_start"); dbg_hcc_params (ehci, "ehci_start"); + hcc_params = readl (&ehci->caps->hcc_params); + + /* EHCI 0.96 and later may have "extended capabilities" */ + temp = HCC_EXT_CAPS (hcc_params); + while (temp) { + u32 cap; + + pci_read_config_dword (ehci->hcd.pdev, temp, &cap); + dbg ("capability %04x at %02x", cap, temp); + switch (cap & 0xff) { + case 1: /* BIOS/SMM/... handoff */ + if (bios_handoff (ehci, temp, cap) != 0) + return -EOPNOTSUPP; + break; + case 0: /* illegal reserved capability */ + warn ("illegal capability!"); + cap = 0; + /* FALLTHROUGH */ + default: /* unknown */ + break; + } + temp = (cap >> 8) & 0xff; + } + /* cache this readonly data; minimize PCI reads */ ehci->hcs_params = readl (&ehci->caps->hcs_params); + /* force HC to halt state */ + if ((retval = ehci_halt (ehci)) != 0) + return retval; + /* * hw default: 1K periodic list heads, one per frame. * periodic_size can shrink by USBCMD update if hcc_params allows. @@ -197,7 +355,6 @@ ehci->periodic_size = DEFAULT_I_TDPS; if ((retval = ehci_mem_init (ehci, SLAB_KERNEL)) < 0) return retval; - hcc_params = readl (&ehci->caps->hcc_params); /* controllers may cache some of the periodic schedule ... */ if (HCC_ISOC_CACHE (hcc_params)) // full frame cache @@ -212,8 +369,10 @@ /* controller state: unknown --> reset */ /* EHCI spec section 4.1 */ - // FIXME require STS_HALT before reset... - ehci_reset (ehci); + if ((retval = ehci_reset (ehci)) != 0) { + ehci_mem_cleanup (ehci); + return retval; + } writel (INTR_MASK, &ehci->regs->intr_enable); writel (ehci->periodic_dma, &ehci->regs->frame_list); @@ -221,23 +380,25 @@ * hcc_params controls whether ehci->regs->segment must (!!!) * be used; it constrains QH/ITD/SITD and QTD locations. * pci_pool consistent memory always uses segment zero. + * streaming mappings for I/O buffers, like pci_map_single(), + * can return segments above 4GB, if the device allows. + * + * NOTE: layered drivers can't yet tell when we enable that, + * so they can't pass this info along (like NETIF_F_HIGHDMA) + * (or like Scsi_Host.highmem_io) ... usb_bus.flags? */ if (HCC_64BIT_ADDR (hcc_params)) { writel (0, &ehci->regs->segment); - /* - * FIXME Enlarge pci_set_dma_mask() when possible. The DMA - * mapping API spec now says that'll affect only single shot - * mappings, and the pci_pool data will stay safe in seg 0. - * That's what we want: no extra copies for USB transfers. - */ - info ("restricting 64bit DMA mappings to segment 0 ..."); + if (!pci_set_dma_mask (ehci->hcd.pdev, 0xffffffffffffffffULL)) + info ("enabled 64bit PCI DMA (DAC)"); } /* clear interrupt enables, set irq latency */ temp = readl (&ehci->regs->command) & 0xff; if (log2_irq_thresh < 0 || log2_irq_thresh > 6) - log2_irq_thresh = 0; + log2_irq_thresh = 0; temp |= 1 << (16 + log2_irq_thresh); + // if hc can park (ehci >= 0.96), default is 3 packets per async QH // keeping default periodic framelist size temp &= ~(CMD_IAAD | CMD_ASE | CMD_PSE), // Philips, Intel, and maybe others need CMD_RUN before the @@ -251,8 +412,13 @@ ehci->tasklet.func = ehci_tasklet; ehci->tasklet.data = (unsigned long) ehci; + init_timer (&ehci->watchdog); + ehci->watchdog.function = ehci_watchdog; + ehci->watchdog.data = (unsigned long) ehci; + /* wire up the root hub */ - hcd->bus->root_hub = udev = usb_alloc_dev (NULL, hcd->bus); + bus = hcd_to_bus (hcd); + bus->root_hub = udev = usb_alloc_dev (NULL, bus); if (!udev) { done2: ehci_mem_cleanup (ehci); @@ -271,11 +437,10 @@ /* PCI Serial Bus Release Number is at 0x60 offset */ pci_read_config_byte (hcd->pdev, 0x60, &tempbyte); temp = readw (&ehci->caps->hci_version); - info ("USB %x.%x support enabled, EHCI rev %x.%2x", - ((tempbyte & 0xf0)>>4), - (tempbyte & 0x0f), - temp >> 8, - temp & 0xff); + info ("USB %x.%x support enabled, EHCI rev %x.%02x, %s %s", + ((tempbyte & 0xf0)>>4), (tempbyte & 0x0f), + temp >> 8, temp & 0xff, + hcd_name, DRIVER_VERSION); /* * From here on, khubd concurrently accesses the root @@ -286,19 +451,18 @@ */ usb_connect (udev); udev->speed = USB_SPEED_HIGH; - if (usb_new_device (udev) != 0) { + if (hcd_register_root (hcd) != 0) { if (hcd->state == USB_STATE_RUNNING) ehci_ready (ehci); - while (readl (&ehci->regs->status) & (STS_ASS | STS_PSS)) - udelay (100); ehci_reset (ehci); - // usb_disconnect (udev); - hcd->bus->root_hub = 0; + bus->root_hub = 0; usb_free_dev (udev); retval = -ENODEV; goto done2; } + create_debug_files (ehci); + return 0; } @@ -308,20 +472,34 @@ { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - dbg ("%s: stop", hcd->bus_name); + dbg ("%s: stop", hcd_to_bus (hcd)->bus_name); + /* no more interrupts ... */ if (hcd->state == USB_STATE_RUNNING) ehci_ready (ehci); - while (readl (&ehci->regs->status) & (STS_ASS | STS_PSS)) - udelay (100); + if (in_interrupt ()) /* should not happen!! */ + err ("stopped %s!", RUN_CONTEXT); + else + del_timer_sync (&ehci->watchdog); ehci_reset (ehci); - // root hub is shut down separately (first, when possible) - scan_async (ehci); - if (ehci->next_uframe != -1) - scan_periodic (ehci); + /* let companion controllers work when we aren't */ + writel (0, &ehci->regs->configured_flag); + + remove_debug_files (ehci); + + /* root hub is shut down separately (first, when possible) */ + tasklet_disable (&ehci->tasklet); + ehci_tasklet ((unsigned long) ehci); ehci_mem_cleanup (ehci); +#ifdef EHCI_STATS + dbg ("irq normal %ld err %ld reclaim %ld", + ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim); + dbg ("complete %ld unlink %ld qpatch %ld", + ehci->stats.complete, ehci->stats.unlink, ehci->stats.qpatch); +#endif + dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status)); } @@ -343,7 +521,7 @@ int ports; int i; - dbg ("%s: suspend to %d", hcd->bus_name, state); + dbg ("%s: suspend to %d", hcd_to_bus (hcd)->bus_name, state); ports = HCS_N_PORTS (ehci->hcs_params); @@ -360,15 +538,13 @@ if ((temp & PORT_PE) == 0 || (temp & PORT_OWNER) != 0) continue; -dbg ("%s: suspend port %d", hcd->bus_name, i); +dbg ("%s: suspend port %d", hcd_to_bus (hcd)->bus_name, i); temp |= PORT_SUSPEND; writel (temp, &ehci->regs->port_status [i]); } if (hcd->state == USB_STATE_RUNNING) ehci_ready (ehci); - while (readl (&ehci->regs->status) & (STS_ASS | STS_PSS)) - udelay (100); writel (readl (&ehci->regs->command) & ~CMD_RUN, &ehci->regs->command); // save pci FLADJ value @@ -384,7 +560,7 @@ int ports; int i; - dbg ("%s: resume", hcd->bus_name); + dbg ("%s: resume", hcd_to_bus (hcd)->bus_name); ports = HCS_N_PORTS (ehci->hcs_params); @@ -404,7 +580,7 @@ if ((temp & PORT_PE) == 0 || (temp & PORT_SUSPEND) != 0) continue; -dbg ("%s: resume port %d", hcd->bus_name, i); +dbg ("%s: resume port %d", hcd_to_bus (hcd)->bus_name, i); temp |= PORT_RESUME; writel (temp, &ehci->regs->port_status [i]); readl (&ehci->regs->command); /* unblock posted writes */ @@ -429,11 +605,15 @@ { struct ehci_hcd *ehci = (struct ehci_hcd *) param; + spin_lock_irq (&ehci->lock); + if (ehci->reclaim_ready) end_unlink_async (ehci); scan_async (ehci); if (ehci->next_uframe != -1) scan_periodic (ehci); + + spin_unlock_irq (&ehci->lock); } /*-------------------------------------------------------------------------*/ @@ -444,6 +624,12 @@ u32 status = readl (&ehci->regs->status); int bh; + /* e.g. cardbus physical eject */ + if (status == ~(u32) 0) { + dbg ("%s: device removed!", hcd_to_bus (hcd)->bus_name); + goto dead; + } + status &= INTR_MASK; if (!status) /* irq sharing? */ return; @@ -461,21 +647,30 @@ /* INT, ERR, and IAA interrupt rates can be throttled */ /* normal [4.15.1.2] or error [4.15.1.1] completion */ - if (likely ((status & (STS_INT|STS_ERR)) != 0)) + if (likely ((status & (STS_INT|STS_ERR)) != 0)) { + if (likely ((status & STS_ERR) == 0)) + COUNT (ehci->stats.normal); + else + COUNT (ehci->stats.error); bh = 1; + } /* complete the unlinking of some qh [4.15.2.3] */ if (status & STS_IAA) { + COUNT (ehci->stats.reclaim); ehci->reclaim_ready = 1; bh = 1; } /* PCI errors [4.15.2.4] */ if (unlikely ((status & STS_FATAL) != 0)) { - err ("%s: fatal error, state %x", hcd->bus_name, hcd->state); + err ("%s: fatal error, state %x", + hcd_to_bus (hcd)->bus_name, hcd->state); +dead: ehci_reset (ehci); - // generic layer kills/unlinks all urbs - // then tasklet cleans up the rest + /* generic layer kills/unlinks all urbs, then + * uses ehci_stop to clean up the rest + */ bh = 1; } @@ -495,7 +690,8 @@ * * hcd-specific init for hcpriv hasn't been done yet * - * NOTE: EHCI queues control and bulk requests transparently, like OHCI. + * NOTE: control, bulk, and interrupt share the same code to append TDs + * to a (possibly active) QH, and the same QH scanning code. */ static int ehci_urb_enqueue ( struct usb_hcd *hcd, @@ -507,10 +703,11 @@ urb->transfer_flags &= ~EHCI_STATE_UNLINK; INIT_LIST_HEAD (&qtd_list); - switch (usb_pipetype (urb->pipe)) { - case PIPE_CONTROL: - case PIPE_BULK: + switch (usb_pipetype (urb->pipe)) { + // case PIPE_CONTROL: + // case PIPE_BULK: + default: if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) return -ENOMEM; return submit_async (ehci, urb, &qtd_list, mem_flags); @@ -530,9 +727,6 @@ dbg ("no split iso support yet"); return -ENOSYS; #endif /* have_split_iso */ - - default: /* can't happen */ - return -ENOSYS; } } @@ -546,15 +740,22 @@ struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; unsigned long flags; - dbg ("%s urb_dequeue %p qh state %d", - hcd->bus_name, urb, qh->qh_state); + dbg ("%s urb_dequeue %p qh %p state %d", + hcd_to_bus (hcd)->bus_name, urb, qh, qh->qh_state); switch (usb_pipetype (urb->pipe)) { - case PIPE_CONTROL: - case PIPE_BULK: + // case PIPE_CONTROL: + // case PIPE_BULK: + default: spin_lock_irqsave (&ehci->lock, flags); if (ehci->reclaim) { -dbg ("dq: reclaim busy, %s", RUN_CONTEXT); + dbg ("dq %p: reclaim = %p, %s", + qh, ehci->reclaim, RUN_CONTEXT); + if (qh == ehci->reclaim) { + /* unlinking qh for another queued urb? */ + spin_unlock_irqrestore (&ehci->lock, flags); + return 0; + } if (in_interrupt ()) { spin_unlock_irqrestore (&ehci->lock, flags); return -EAGAIN; @@ -564,28 +765,43 @@ && ehci->hcd.state != USB_STATE_HALT ) { spin_unlock_irqrestore (&ehci->lock, flags); -// yeech ... this could spin for up to two frames! -dbg ("wait for dequeue: state %d, reclaim %p, hcd state %d", - qh->qh_state, ehci->reclaim, ehci->hcd.state -); - udelay (100); + /* let pending unlinks complete */ + wait_ms (1); spin_lock_irqsave (&ehci->lock, flags); } } if (qh->qh_state == QH_STATE_LINKED) start_unlink_async (ehci, qh); spin_unlock_irqrestore (&ehci->lock, flags); - return 0; + break; case PIPE_INTERRUPT: - intr_deschedule (ehci, urb->start_frame, qh, - (urb->dev->speed == USB_SPEED_HIGH) - ? urb->interval - : (urb->interval << 3)); - if (ehci->hcd.state == USB_STATE_HALT) - urb->status = -ESHUTDOWN; - qh_completions (ehci, qh, 1); - return 0; + spin_lock_irqsave (&ehci->lock, flags); + if (qh->qh_state == QH_STATE_LINKED) { + /* messy, can spin or block a microframe ... */ + intr_deschedule (ehci, qh, 1); + /* qh_state == IDLE */ + } + qh_completions (ehci, qh); + + /* reschedule QH iff another request is queued */ + if (!list_empty (&qh->qtd_list) + && HCD_IS_RUNNING (ehci->hcd.state)) { + int status; + + status = qh_schedule (ehci, qh); + spin_unlock_irqrestore (&ehci->lock, flags); + + if (status != 0) { + // shouldn't happen often, but ... + // FIXME kill those tds' urbs + err ("can't reschedule qh %p, err %d", + qh, status); + } + return status; + } + spin_unlock_irqrestore (&ehci->lock, flags); + break; case PIPE_ISOCHRONOUS: // itd or sitd ... @@ -593,9 +809,9 @@ // wait till next completion, do it then. // completion irqs can wait up to 1024 msec, urb->transfer_flags |= EHCI_STATE_UNLINK; - return 0; + break; } - return -EINVAL; + return 0; } /*-------------------------------------------------------------------------*/ @@ -609,42 +825,67 @@ int i; unsigned long flags; + /* ASSERT: no requests/urbs are still linked (so no TDs) */ /* ASSERT: nobody can be submitting urbs for this any more */ - dbg ("%s: free_config devnum %d", hcd->bus_name, udev->devnum); + dbg ("%s: free_config devnum %d", + hcd_to_bus (hcd)->bus_name, udev->devnum); spin_lock_irqsave (&ehci->lock, flags); for (i = 0; i < 32; i++) { if (dev->ep [i]) { struct ehci_qh *qh; + char *why; /* dev->ep never has ITDs or SITDs */ qh = (struct ehci_qh *) dev->ep [i]; - vdbg ("free_config, ep 0x%02x qh %p", i, qh); - if (!list_empty (&qh->qtd_list)) { - dbg ("ep 0x%02x qh %p not empty!", i, qh); + + /* detect/report non-recoverable errors */ + if (in_interrupt ()) + why = "disconnect() didn't"; + else if ((qh->hw_info2 & cpu_to_le32 (0xffff)) != 0 + && qh->qh_state != QH_STATE_IDLE) + why = "(active periodic)"; + else + why = 0; + if (why) { + err ("dev %s-%s ep %d-%s error: %s", + hcd_to_bus (hcd)->bus_name, + udev->devpath, + i & 0xf, (i & 0x10) ? "IN" : "OUT", + why); BUG (); } - dev->ep [i] = 0; - /* wait_ms() won't spin here -- we're a thread */ + dev->ep [i] = 0; + if (qh->qh_state == QH_STATE_IDLE) + goto idle; + dbg ("free_config, async ep 0x%02x qh %p", i, qh); + + /* scan_async() empties the ring as it does its work, + * using IAA, but doesn't (yet?) turn it off. if it + * doesn't empty this qh, likely it's the last entry. + */ while (qh->qh_state == QH_STATE_LINKED && ehci->reclaim && ehci->hcd.state != USB_STATE_HALT ) { spin_unlock_irqrestore (&ehci->lock, flags); + /* wait_ms() won't spin, we're a thread; + * and we know IRQ+tasklet can progress + */ wait_ms (1); spin_lock_irqsave (&ehci->lock, flags); } - if (qh->qh_state == QH_STATE_LINKED) { + if (qh->qh_state == QH_STATE_LINKED) start_unlink_async (ehci, qh); - while (qh->qh_state != QH_STATE_IDLE) { - spin_unlock_irqrestore (&ehci->lock, - flags); - wait_ms (1); - spin_lock_irqsave (&ehci->lock, flags); - } + while (qh->qh_state != QH_STATE_IDLE + && ehci->hcd.state != USB_STATE_HALT) { + spin_unlock_irqrestore (&ehci->lock, flags); + wait_ms (1); + spin_lock_irqsave (&ehci->lock, flags); } +idle: qh_put (ehci, qh); } } @@ -654,50 +895,48 @@ /*-------------------------------------------------------------------------*/ -static const char hcd_name [] = "ehci-hcd"; - static const struct hc_driver ehci_driver = { - description: hcd_name, + .description = hcd_name, /* * generic hardware linkage */ - irq: ehci_irq, - flags: HCD_MEMORY | HCD_USB2, + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, /* * basic lifecycle operations */ - start: ehci_start, + .start = ehci_start, #ifdef CONFIG_PM - suspend: ehci_suspend, - resume: ehci_resume, + .suspend = ehci_suspend, + .resume = ehci_resume, #endif - stop: ehci_stop, + .stop = ehci_stop, /* * memory lifecycle (except per-request) */ - hcd_alloc: ehci_hcd_alloc, - hcd_free: ehci_hcd_free, + .hcd_alloc = ehci_hcd_alloc, + .hcd_free = ehci_hcd_free, /* * managing i/o requests and associated device resources */ - urb_enqueue: ehci_urb_enqueue, - urb_dequeue: ehci_urb_dequeue, - free_config: ehci_free_config, + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .free_config = ehci_free_config, /* * scheduling support */ - get_frame_number: ehci_get_frame, + .get_frame_number = ehci_get_frame, /* * root hub support */ - hub_status_data: ehci_hub_status_data, - hub_control: ehci_hub_control, + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, }; /*-------------------------------------------------------------------------*/ @@ -709,15 +948,15 @@ /* handle any USB 2.0 EHCI controller */ - class: ((PCI_CLASS_SERIAL_USB << 8) | 0x20), - class_mask: ~0, - driver_data: (unsigned long) &ehci_driver, + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0x20), + .class_mask = ~0, + .driver_data = (unsigned long) &ehci_driver, /* no matter who makes it */ - vendor: PCI_ANY_ID, - device: PCI_ANY_ID, - subvendor: PCI_ANY_ID, - subdevice: PCI_ANY_ID, + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, }, { /* end: all zeroes */ } }; @@ -725,21 +964,20 @@ /* pci driver glue; this is a "new style" PCI driver module */ static struct pci_driver ehci_pci_driver = { - name: (char *) hcd_name, - id_table: pci_ids, + .name = (char *) hcd_name, + .id_table = pci_ids, - probe: usb_hcd_pci_probe, - remove: usb_hcd_pci_remove, + .probe = usb_hcd_pci_probe, + .remove = usb_hcd_pci_remove, #ifdef CONFIG_PM - suspend: usb_hcd_pci_suspend, - resume: usb_hcd_pci_resume, + .suspend = usb_hcd_pci_suspend, + .resume = usb_hcd_pci_resume, #endif }; #define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC -EXPORT_NO_SYMBOLS; MODULE_DESCRIPTION (DRIVER_INFO); MODULE_AUTHOR (DRIVER_AUTHOR); MODULE_LICENSE ("GPL"); diff -Nru a/drivers/usb/hcd/ehci-hub.c b/drivers/usb/hcd/ehci-hub.c --- a/drivers/usb/hcd/ehci-hub.c Mon Oct 21 22:14:31 2002 +++ b/drivers/usb/hcd/ehci-hub.c Mon Oct 21 22:14:31 2002 @@ -41,14 +41,17 @@ /* if reset finished and it's still not enabled -- handoff */ if (!(port_status & PORT_PE)) { dbg ("%s port %d full speed, give to companion, 0x%x", - ehci->hcd.bus_name, index + 1, port_status); + hcd_to_bus (&ehci->hcd)->bus_name, + index + 1, port_status); // what happens if HCS_N_CC(params) == 0 ? port_status |= PORT_OWNER; writel (port_status, &ehci->regs->port_status [index]); } else - dbg ("%s port %d high speed", ehci->hcd.bus_name, index + 1); + dbg ("%s port %d high speed", + hcd_to_bus (&ehci->hcd)->bus_name, + index + 1); return port_status; } @@ -236,7 +239,8 @@ /* whoever resets must GetPortStatus to complete it!! */ if ((temp & PORT_RESET) - && jiffies > ehci->reset_done [wIndex]) { + && time_after (jiffies, + ehci->reset_done [wIndex])) { status |= 1 << USB_PORT_FEAT_C_RESET; /* force reset to complete */ @@ -310,11 +314,13 @@ if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT && PORT_USB11 (temp)) { dbg ("%s port %d low speed, give to companion", - hcd->bus_name, wIndex + 1); + hcd_to_bus (&ehci->hcd)->bus_name, + wIndex + 1); temp |= PORT_OWNER; } else { vdbg ("%s port %d reset", - hcd->bus_name, wIndex + 1); + hcd_to_bus (&ehci->hcd)->bus_name, + wIndex + 1); temp |= PORT_RESET; temp &= ~PORT_PE; diff -Nru a/drivers/usb/hcd/ehci-q.c b/drivers/usb/hcd/ehci-q.c --- a/drivers/usb/hcd/ehci-q.c Mon Oct 21 22:14:31 2002 +++ b/drivers/usb/hcd/ehci-q.c Mon Oct 21 22:14:31 2002 @@ -26,8 +26,7 @@ * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned * buffers needed for the larger number). We use one QH per endpoint, queue - * multiple (bulk or control) urbs per endpoint. URBs may need several qtds. - * A scheduled interrupt qh always (for now) has one qtd, one urb. + * multiple urbs (all three types) per endpoint. URBs may need several qtds. * * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with * interrupts) needs careful scheduling. Performance improvements can be @@ -47,9 +46,11 @@ qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, int token) { int i, count; + u64 addr = buf; /* one buffer entry per 4K ... first might be short or unaligned */ - qtd->hw_buf [0] = cpu_to_le32 (buf); + qtd->hw_buf [0] = cpu_to_le32 ((u32)addr); + qtd->hw_buf_hi [0] = cpu_to_le32 ((u32)(addr >> 32)); count = 0x1000 - (buf & 0x0fff); /* rest of that page */ if (likely (len < count)) /* ... iff needed */ count = len; @@ -59,7 +60,7 @@ /* per-qtd limit: from 16K to 20K (best alignment) */ for (i = 1; count < len && i < 5; i++) { - u64 addr = buf; + addr = buf; qtd->hw_buf [i] = cpu_to_le32 ((u32)addr); qtd->hw_buf_hi [i] = cpu_to_le32 ((u32)(addr >> 32)); buf += 0x1000; @@ -144,97 +145,106 @@ usb_pipeendpoint (pipe), usb_pipeout (pipe)); if (urb->dev->tt && !usb_pipeint (pipe)) { -err ("must CLEAR_TT_BUFFER, hub port %d%s addr %d ep %d", - urb->dev->ttport, /* devpath */ - urb->dev->tt->multi ? "" : " (all-ports TT)", - urb->dev->devnum, usb_pipeendpoint (urb->pipe)); - // FIXME something (khubd?) should make the hub - // CLEAR_TT_BUFFER ASAP, it's blocking other - // fs/ls requests... hub_tt_clear_buffer() ? +#ifdef DEBUG + struct usb_device *tt = urb->dev->tt->hub; + dbg ("clear tt %s-%s p%d buffer, a%d ep%d", + tt->bus->bus_name, tt->devpath, + urb->dev->ttport, urb->dev->devnum, + usb_pipeendpoint (pipe)); +#endif /* DEBUG */ + usb_hub_tt_clear_buffer (urb->dev, pipe); } } } } -static void ehci_urb_complete ( - struct ehci_hcd *ehci, - dma_addr_t addr, - struct urb *urb -) { - if (urb->transfer_buffer_length && usb_pipein (urb->pipe)) - pci_dma_sync_single (ehci->hcd.pdev, addr, - urb->transfer_buffer_length, - PCI_DMA_FROMDEVICE); +static void ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb) +{ +#ifdef INTR_AUTOMAGIC + struct urb *resubmit = 0; + struct usb_device *dev = 0; - /* cleanse status if we saw no error */ - if (likely (urb->status == -EINPROGRESS)) { - if (urb->actual_length != urb->transfer_buffer_length - && (urb->transfer_flags & USB_DISABLE_SPD)) - urb->status = -EREMOTEIO; - else - urb->status = 0; - } + static int ehci_urb_enqueue (struct usb_hcd *, struct urb *, int); +#endif - /* only report unlinks once */ - if (likely (urb->status != -ENOENT && urb->status != -ENOTCONN)) - urb->complete (urb); -} + if (likely (urb->hcpriv != 0)) { + struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; -/* urb->lock ignored from here on (hcd is done with urb) */ + /* S-mask in a QH means it's an interrupt urb */ + if ((qh->hw_info2 & cpu_to_le32 (0x00ff)) != 0) { -static void ehci_urb_done ( - struct ehci_hcd *ehci, - dma_addr_t addr, - struct urb *urb -) { - if (urb->transfer_buffer_length) - pci_unmap_single (ehci->hcd.pdev, - addr, - urb->transfer_buffer_length, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); - if (likely (urb->hcpriv != 0)) { - qh_put (ehci, (struct ehci_qh *) urb->hcpriv); + /* ... update hc-wide periodic stats (for usbfs) */ + hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs--; + +#ifdef INTR_AUTOMAGIC + if (!((urb->status == -ENOENT) + || (urb->status == -ECONNRESET))) { + resubmit = usb_get_urb (urb); + dev = urb->dev; + } +#endif + } + qh_put (ehci, qh); urb->hcpriv = 0; } if (likely (urb->status == -EINPROGRESS)) { if (urb->actual_length != urb->transfer_buffer_length - && (urb->transfer_flags & USB_DISABLE_SPD)) + && (urb->transfer_flags & URB_SHORT_NOT_OK)) urb->status = -EREMOTEIO; else urb->status = 0; } - /* hand off urb ownership */ + if (likely (urb->status == 0)) + COUNT (ehci->stats.complete); + else if (urb->status == -ECONNRESET || urb->status == -ENOENT) + COUNT (ehci->stats.unlink); + else + COUNT (ehci->stats.error); + + /* complete() can reenter this HCD */ + spin_unlock (&ehci->lock); usb_hcd_giveback_urb (&ehci->hcd, urb); + +#ifdef INTR_AUTOMAGIC + if (resubmit && ((urb->status == -ENOENT) + || (urb->status == -ECONNRESET))) { + usb_put_urb (resubmit); + resubmit = 0; + } + // device drivers will soon be doing something like this + if (resubmit) { + int status; + + resubmit->dev = dev; + status = SUBMIT_URB (resubmit, SLAB_KERNEL); + if (status != 0) + err ("can't resubmit interrupt urb %p: status %d", + resubmit, status); + usb_put_urb (resubmit); + } +#endif + + spin_lock (&ehci->lock); } /* - * Process completed qtds for a qh, issuing completions if needed. - * When freeing: frees qtds, unmaps buf, returns URB to driver. - * When not freeing (queued periodic qh): retain qtds, mapping, and urb. - * Races up to qh->hw_current; returns number of urb completions. + * Process and free completed qtds for a qh, returning URBs to drivers. + * Chases up to qh->hw_current. Returns number of completions called, + * indicating how much "real" work we did. */ -static int -qh_completions ( - struct ehci_hcd *ehci, - struct ehci_qh *qh, - int freeing -) { +static unsigned +qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) +{ struct ehci_qtd *qtd, *last; struct list_head *next, *qtd_list = &qh->qtd_list; int unlink = 0, halted = 0; - unsigned long flags; - int retval = 0; + unsigned count = 0; - spin_lock_irqsave (&ehci->lock, flags); - if (unlikely (list_empty (qtd_list))) { - spin_unlock_irqrestore (&ehci->lock, flags); - return retval; - } + if (unlikely (list_empty (qtd_list))) + return count; /* scan QTDs till end of list, or we reach an active one */ for (qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list), @@ -248,16 +258,8 @@ /* clean up any state from previous QTD ...*/ if (last) { if (likely (last->urb != urb)) { - /* complete() can reenter this HCD */ - spin_unlock_irqrestore (&ehci->lock, flags); - if (likely (freeing != 0)) - ehci_urb_done (ehci, last->buf_dma, - last->urb); - else - ehci_urb_complete (ehci, last->buf_dma, - last->urb); - spin_lock_irqsave (&ehci->lock, flags); - retval++; + ehci_urb_done (ehci, last->urb); + count++; } /* qh overlays can have HC's old cached copies of @@ -267,10 +269,10 @@ && last->hw_next != qh->hw_qtd_next) { qh->hw_alt_next = last->hw_alt_next; qh->hw_qtd_next = last->hw_next; + COUNT (ehci->stats.qpatch); } - if (likely (freeing != 0)) - ehci_qtd_free (ehci, last); + ehci_qtd_free (ehci, last); last = 0; } next = qtd->qtd_list.next; @@ -285,9 +287,17 @@ || (ehci->hcd.state == USB_STATE_HALT) || (qh->qh_state == QH_STATE_IDLE); + // FIXME Remove the automagic unlink mode. + // Drivers can now clean up safely; it's their job. + // + // FIXME Removing it should fix the short read scenarios + // with "huge" urb data (more than one 16+KByte td) with + // the short read someplace other than the last data TD. + // Except the control case: 'retrigger' status ACKs. + /* fault: unlink the rest, since this qtd saw an error? */ if (unlikely ((token & QTD_STS_HALT) != 0)) { - freeing = unlink = 1; + unlink = 1; /* status copied below */ /* QH halts only because of fault (above) or unlink (here). */ @@ -295,13 +305,14 @@ /* unlinking everything because of HC shutdown? */ if (ehci->hcd.state == USB_STATE_HALT) { - freeing = unlink = 1; + unlink = 1; /* explicit unlink, maybe starting here? */ } else if (qh->qh_state == QH_STATE_IDLE && (urb->status == -ECONNRESET + || urb->status == -ESHUTDOWN || urb->status == -ENOENT)) { - freeing = unlink = 1; + unlink = 1; /* QH halted to unlink urbs _after_ this? */ } else if (!unlink && (token & QTD_STS_ACTIVE) != 0) { @@ -311,7 +322,7 @@ /* unlink the rest? once we start unlinking, after * a fault or explicit unlink, we unlink all later - * urbs. usb spec requires that. + * urbs. usb spec requires that for faults... */ if (unlink && urb->status == -EINPROGRESS) urb->status = -ECONNRESET; @@ -329,31 +340,7 @@ qtd_copy_status (urb, qtd->length, token); spin_unlock (&urb->lock); - /* - * NOTE: this won't work right with interrupt urbs that - * need multiple qtds ... only the first scan of qh->qtd_list - * starts at the right qtd, yet multiple scans could happen - * for transfers that are scheduled across multiple uframes. - * (Such schedules are not currently allowed!) - */ - if (likely (freeing != 0)) - list_del (&qtd->qtd_list); - else { - /* restore everything the HC could change - * from an interrupt QTD - */ - qtd->hw_token = (qtd->hw_token - & __constant_cpu_to_le32 (0x8300)) - | cpu_to_le32 (qtd->length << 16) - | __constant_cpu_to_le32 (QTD_STS_ACTIVE - | (EHCI_TUNE_CERR << 10)); - qtd->hw_buf [0] &= ~__constant_cpu_to_le32 (0x0fff); - - /* this offset, and the length above, - * are likely wrong on QTDs #2..N - */ - qtd->hw_buf [0] |= cpu_to_le32 (0x0fff & qtd->buf_dma); - } + list_del (&qtd->qtd_list); #if 0 if (urb->status == -EINPROGRESS) @@ -364,31 +351,22 @@ urb, urb->status, qtd, token, urb->actual_length); #endif + } - /* SETUP for control urb? */ - if (unlikely (QTD_PID (token) == 2)) - pci_unmap_single (ehci->hcd.pdev, - qtd->buf_dma, sizeof (struct usb_ctrlrequest), - PCI_DMA_TODEVICE); + /* last urb's completion might still need calling */ + if (likely (last != 0)) { + ehci_urb_done (ehci, last->urb); + count++; + ehci_qtd_free (ehci, last); } - /* patch up list head? */ + /* reactivate queue after error and driver's cleanup */ if (unlikely (halted && !list_empty (qtd_list))) { qh_update (qh, list_entry (qtd_list->next, struct ehci_qtd, qtd_list)); } - spin_unlock_irqrestore (&ehci->lock, flags); - /* last urb's completion might still need calling */ - if (likely (last != 0)) { - if (likely (freeing != 0)) { - ehci_urb_done (ehci, last->buf_dma, last->urb); - ehci_qtd_free (ehci, last); - } else - ehci_urb_complete (ehci, last->buf_dma, last->urb); - retval++; - } - return retval; + return count; } /*-------------------------------------------------------------------------*/ @@ -403,35 +381,12 @@ struct list_head *qtd_list ) { struct list_head *entry, *temp; - int unmapped = 0; list_for_each_safe (entry, temp, qtd_list) { struct ehci_qtd *qtd; qtd = list_entry (entry, struct ehci_qtd, qtd_list); list_del (&qtd->qtd_list); - if (unmapped != 2) { - int direction; - size_t size; - - /* for ctrl unmap twice: SETUP and DATA; - * else (bulk, intr) just once: DATA - */ - if (!unmapped++ && usb_pipecontrol (urb->pipe)) { - direction = PCI_DMA_TODEVICE; - size = sizeof (struct usb_ctrlrequest); - } else { - direction = usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE; - size = qtd->urb->transfer_buffer_length; - unmapped++; - } - if (qtd->buf_dma) - pci_unmap_single (ehci->hcd.pdev, - qtd->buf_dma, - size, direction); - } ehci_qtd_free (ehci, qtd); } } @@ -447,8 +402,9 @@ int flags ) { struct ehci_qtd *qtd, *qtd_prev; - dma_addr_t buf, map_buf; + dma_addr_t buf; int len, maxpacket; + int is_input, status_patch = 0; u32 token; /* @@ -466,17 +422,8 @@ /* for split transactions, SplitXState initialized to zero */ if (usb_pipecontrol (urb->pipe)) { - /* control request data is passed in the "setup" pid */ - qtd->buf_dma = pci_map_single ( - ehci->hcd.pdev, - urb->setup_packet, - sizeof (struct usb_ctrlrequest), - PCI_DMA_TODEVICE); - if (unlikely (!qtd->buf_dma)) - goto cleanup; - /* SETUP pid */ - qtd_fill (qtd, qtd->buf_dma, sizeof (struct usb_ctrlrequest), + qtd_fill (qtd, urb->setup_dma, sizeof (struct usb_ctrlrequest), token | (2 /* "setup" */ << 8)); /* ... and always at least one more pid */ @@ -488,29 +435,26 @@ qtd->urb = urb; qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma); list_add_tail (&qtd->qtd_list, head); + + if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) + status_patch = 1; } /* * data transfer stage: buffer setup */ len = urb->transfer_buffer_length; - if (likely (len > 0)) { - buf = map_buf = pci_map_single (ehci->hcd.pdev, - urb->transfer_buffer, len, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); - if (unlikely (!buf)) - goto cleanup; - } else - buf = map_buf = 0; + is_input = usb_pipein (urb->pipe); + if (likely (len > 0)) + buf = urb->transfer_dma; + else + buf = 0; - if (!buf || usb_pipein (urb->pipe)) + if (!buf || is_input) token |= (1 /* "in" */ << 8); /* else it's already initted to "out" pid (0 << 8) */ - maxpacket = usb_maxpacket (urb->dev, urb->pipe, - usb_pipeout (urb->pipe)); + maxpacket = usb_maxpacket (urb->dev, urb->pipe, !is_input) & 0x03ff; /* * buffer gets wrapped in one or more qtds; @@ -521,7 +465,6 @@ int this_qtd_len; qtd->urb = urb; - qtd->buf_dma = map_buf; this_qtd_len = qtd_fill (qtd, buf, len, token); len -= this_qtd_len; buf += this_qtd_len; @@ -572,6 +515,19 @@ } } + /* if we're permitting a short control read, we want the hardware to + * just continue after short data and send the status ack. it can do + * that on the last data packet (typically the only one). for other + * packets, software fixup is needed (in qh_completions). + */ + if (status_patch) { + struct ehci_qtd *prev; + + prev = list_entry (qtd->qtd_list.prev, + struct ehci_qtd, qtd_list); + prev->hw_alt_next = QTD_NEXT (qtd->qtd_dma); + } + /* by default, enable interrupt on urb completion */ if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT))) qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC); @@ -606,6 +562,11 @@ // That'd mean updating how usbcore talks to HCDs. (2.5?) +// high bandwidth multiplier, as encoded in highspeed endpoint descriptors +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) +// ... and packet size, for any kind of endpoint descriptor +#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x03ff) + /* * Each QH holds a qtd list; a QH is used for everything except iso. * @@ -623,6 +584,8 @@ ) { struct ehci_qh *qh = ehci_qh_alloc (ehci, flags); u32 info1 = 0, info2 = 0; + int is_input, type; + int maxp = 0; if (!qh) return qh; @@ -633,6 +596,53 @@ info1 |= usb_pipeendpoint (urb->pipe) << 8; info1 |= usb_pipedevice (urb->pipe) << 0; + is_input = usb_pipein (urb->pipe); + type = usb_pipetype (urb->pipe); + maxp = usb_maxpacket (urb->dev, urb->pipe, !is_input); + + /* Compute interrupt scheduling parameters just once, and save. + * - allowing for high bandwidth, how many nsec/uframe are used? + * - split transactions need a second CSPLIT uframe; same question + * - splits also need a schedule gap (for full/low speed I/O) + * - qh has a polling interval + * + * For control/bulk requests, the HC or TT handles these. + */ + if (type == PIPE_INTERRUPT) { + qh->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0, + hb_mult (maxp) * max_packet (maxp)); + qh->start = NO_FRAME; + + if (urb->dev->speed == USB_SPEED_HIGH) { + qh->c_usecs = 0; + qh->gap_uf = 0; + + /* FIXME handle HS periods of less than 1 frame. */ + qh->period = urb->interval >> 3; + if (qh->period < 1) { + dbg ("intr period %d uframes, NYET!", + urb->interval); + qh = 0; + goto done; + } + } else { + /* gap is f(FS/LS transfer times) */ + qh->gap_uf = 1 + usb_calc_bus_time (urb->dev->speed, + is_input, 0, maxp) / (125 * 1000); + + /* FIXME this just approximates SPLIT/CSPLIT times */ + if (is_input) { // SPLIT, gap, CSPLIT+DATA + qh->c_usecs = qh->usecs + HS_USECS (0); + qh->usecs = HS_USECS (1); + } else { // SPLIT+DATA, gap, CSPLIT + qh->usecs += HS_USECS (1); + qh->c_usecs = HS_USECS (0); + } + + qh->period = urb->interval; + } + } + /* using TT? */ switch (urb->dev->speed) { case USB_SPEED_LOW: @@ -642,69 +652,62 @@ case USB_SPEED_FULL: /* EPS 0 means "full" */ info1 |= (EHCI_TUNE_RL_TT << 28); - if (usb_pipecontrol (urb->pipe)) { + if (type == PIPE_CONTROL) { info1 |= (1 << 27); /* for TT */ info1 |= 1 << 14; /* toggle from qtd */ } - info1 |= usb_maxpacket (urb->dev, urb->pipe, - usb_pipeout (urb->pipe)) << 16; + info1 |= maxp << 16; info2 |= (EHCI_TUNE_MULT_TT << 30); info2 |= urb->dev->ttport << 23; info2 |= urb->dev->tt->hub->devnum << 16; - /* NOTE: if (usb_pipeint (urb->pipe)) { scheduler sets c-mask } - * ... and a 0.96 scheduler might use FSTN nodes too - */ + /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ + break; case USB_SPEED_HIGH: /* no TT involved */ info1 |= (2 << 12); /* EPS "high" */ info1 |= (EHCI_TUNE_RL_HS << 28); - if (usb_pipecontrol (urb->pipe)) { + if (type == PIPE_CONTROL) { info1 |= 64 << 16; /* usb2 fixed maxpacket */ info1 |= 1 << 14; /* toggle from qtd */ info2 |= (EHCI_TUNE_MULT_HS << 30); - } else if (usb_pipebulk (urb->pipe)) { + } else if (type == PIPE_BULK) { info1 |= 512 << 16; /* usb2 fixed maxpacket */ info2 |= (EHCI_TUNE_MULT_HS << 30); - } else { - u32 temp; - temp = usb_maxpacket (urb->dev, urb->pipe, - usb_pipeout (urb->pipe)); - info1 |= (temp & 0x3ff) << 16; /* maxpacket */ - /* HS intr can be "high bandwidth" */ - temp = 1 + ((temp >> 11) & 0x03); - info2 |= temp << 30; /* mult */ + } else { /* PIPE_INTERRUPT */ + info1 |= max_packet (maxp) << 16; + info2 |= hb_mult (maxp) << 30; } break; default: -#ifdef DEBUG - BUG (); -#else - ; -#endif + dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed); + return 0; } - /* NOTE: if (usb_pipeint (urb->pipe)) { scheduler sets s-mask } */ + /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ qh->qh_state = QH_STATE_IDLE; qh->hw_info1 = cpu_to_le32 (info1); qh->hw_info2 = cpu_to_le32 (info2); /* initialize sw and hw queues with these qtds */ - list_splice (qtd_list, &qh->qtd_list); - qh_update (qh, list_entry (qtd_list->next, struct ehci_qtd, qtd_list)); + if (!list_empty (qtd_list)) { + list_splice (qtd_list, &qh->qtd_list); + qh_update (qh, list_entry (qtd_list->next, struct ehci_qtd, qtd_list)); + } else { + qh->hw_qtd_next = qh->hw_alt_next = EHCI_LIST_END; + } /* initialize data toggle state */ - if (!usb_pipecontrol (urb->pipe)) - clear_toggle (urb->dev, - usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe), - qh); + clear_toggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, qh); +done: return qh; } +#undef hb_mult +#undef hb_packet /*-------------------------------------------------------------------------*/ @@ -719,8 +722,7 @@ u32 cmd = readl (&ehci->regs->command); /* in case a clear of CMD_ASE didn't take yet */ - while (readl (&ehci->regs->status) & STS_ASS) - udelay (100); + (void) handshake (&ehci->regs->status, STS_ASS, 0, 150); qh->hw_info1 |= __constant_cpu_to_le32 (QH_HEAD); /* [4.8] */ qh->qh_next.qh = qh; @@ -743,58 +745,58 @@ } qh->qh_state = QH_STATE_LINKED; /* qtd completions reported later by interrupt */ + + ehci->async_idle = 0; } /*-------------------------------------------------------------------------*/ -static int -submit_async ( +/* + * For control/bulk/interrupt, return QH with these TDs appended. + * Allocates and initializes the QH if necessary. + * Returns null if it can't allocate a QH it needs to. + * If the QH has TDs (urbs) already, that's great. + */ +static struct ehci_qh *qh_append_tds ( struct ehci_hcd *ehci, struct urb *urb, struct list_head *qtd_list, - int mem_flags -) { - struct ehci_qtd *qtd; - struct hcd_dev *dev; - int epnum; - unsigned long flags; + int epnum, + void **ptr +) +{ struct ehci_qh *qh = 0; - qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list); - dev = (struct hcd_dev *)urb->dev->hcpriv; - epnum = usb_pipeendpoint (urb->pipe); - if (usb_pipein (urb->pipe)) - epnum |= 0x10; - - vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]", - ehci->hcd.bus_name, urb, urb->transfer_buffer_length, - epnum & 0x0f, (epnum & 0x10) ? "in" : "out", - qtd, dev ? dev->ep [epnum] : (void *)~0); - - spin_lock_irqsave (&ehci->lock, flags); - - qh = (struct ehci_qh *) dev->ep [epnum]; + qh = (struct ehci_qh *) *ptr; if (likely (qh != 0)) { - u32 hw_next = QTD_NEXT (qtd->qtd_dma); + struct ehci_qtd *qtd; + + if (unlikely (list_empty (qtd_list))) + qtd = 0; + else + qtd = list_entry (qtd_list->next, struct ehci_qtd, + qtd_list); /* maybe patch the qh used for set_address */ if (unlikely (epnum == 0 && le32_to_cpu (qh->hw_info1 & 0x7f) == 0)) qh->hw_info1 |= cpu_to_le32 (usb_pipedevice(urb->pipe)); - /* is an URB is queued to this qh already? */ - if (unlikely (!list_empty (&qh->qtd_list))) { + /* append to tds already queued to this qh? */ + if (unlikely (!list_empty (&qh->qtd_list) && qtd)) { struct ehci_qtd *last_qtd; int short_rx = 0; + u32 hw_next; /* update the last qtd's "next" pointer */ // dbg_qh ("non-empty qh", ehci, qh); last_qtd = list_entry (qh->qtd_list.prev, struct ehci_qtd, qtd_list); + hw_next = QTD_NEXT (qtd->qtd_dma); last_qtd->hw_next = hw_next; /* previous urb allows short rx? maybe optimize. */ - if (!(last_qtd->urb->transfer_flags & USB_DISABLE_SPD) + if (!(last_qtd->urb->transfer_flags & URB_SHORT_NOT_OK) && (epnum & 0x10)) { // only the last QTD for now last_qtd->hw_alt_next = hw_next; @@ -805,6 +807,7 @@ * Interrupt code must cope with case of HC having it * cached, and clobbering these updates. * ... complicates getting rid of extra interrupts! + * (Or: use dummy td, so cache always stays valid.) */ if (qh->hw_current == cpu_to_le32 (last_qtd->qtd_dma)) { wmb (); @@ -824,31 +827,62 @@ */ /* usb_clear_halt() means qh data toggle gets reset */ - if (usb_pipebulk (urb->pipe) - && unlikely (!usb_gettoggle (urb->dev, + if (unlikely (!usb_gettoggle (urb->dev, (epnum & 0x0f), !(epnum & 0x10)))) { clear_toggle (urb->dev, epnum & 0x0f, !(epnum & 0x10), qh); } - qh_update (qh, qtd); + if (qtd) + qh_update (qh, qtd); } list_splice (qtd_list, qh->qtd_list.prev); } else { /* can't sleep here, we have ehci->lock... */ qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC); - if (likely (qh != 0)) { - // dbg_qh ("new qh", ehci, qh); - dev->ep [epnum] = qh; - } + // if (qh) dbg_qh ("new qh", ehci, qh); + *ptr = qh; } + if (qh) + urb->hcpriv = qh_get (qh); + return qh; +} + +/*-------------------------------------------------------------------------*/ + +static int +submit_async ( + struct ehci_hcd *ehci, + struct urb *urb, + struct list_head *qtd_list, + int mem_flags +) { + struct ehci_qtd *qtd; + struct hcd_dev *dev; + int epnum; + unsigned long flags; + struct ehci_qh *qh = 0; + + qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list); + dev = (struct hcd_dev *)urb->dev->hcpriv; + epnum = usb_pipeendpoint (urb->pipe); + if (usb_pipein (urb->pipe) && !usb_pipecontrol (urb->pipe)) + epnum |= 0x10; + + vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]", + hcd_to_bus (&ehci->hcd)->bus_name, + urb, urb->transfer_buffer_length, + epnum & 0x0f, (epnum & 0x10) ? "in" : "out", + qtd, dev ? dev->ep [epnum] : (void *)~0); + + spin_lock_irqsave (&ehci->lock, flags); + qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]); /* Control/bulk operations through TTs don't need scheduling, * the HC and TT handle it when the TT has a buffer ready. */ if (likely (qh != 0)) { - urb->hcpriv = qh_get (qh); if (likely (qh->qh_state == QH_STATE_IDLE)) qh_link_async (ehci, qh_get (qh)); } @@ -869,16 +903,16 @@ { struct ehci_qh *qh = ehci->reclaim; + del_timer (&ehci->watchdog); + qh->qh_state = QH_STATE_IDLE; qh->qh_next.qh = 0; qh_put (ehci, qh); // refcount from reclaim ehci->reclaim = 0; ehci->reclaim_ready = 0; - qh_completions (ehci, qh, 1); + qh_completions (ehci, qh); - // unlink any urb should now unlink all following urbs, so that - // relinking only happens for urbs before the unlinked ones. if (!list_empty (&qh->qtd_list) && HCD_IS_RUNNING (ehci->hcd.state)) qh_link_async (ehci, qh); @@ -886,7 +920,6 @@ qh_put (ehci, qh); // refcount from async list } - /* makes sure the async qh will become idle */ /* caller must own ehci->lock */ @@ -916,13 +949,14 @@ if (unlikely (qh == ehci->async && qh->qh_next.qh == qh)) { /* can't get here without STS_ASS set */ if (ehci->hcd.state != USB_STATE_HALT) { - if (cmd & CMD_PSE) - writel (cmd & ~CMD_ASE, &ehci->regs->command); - else { - ehci_ready (ehci); - while (readl (&ehci->regs->status) & STS_ASS) - udelay (100); - } + writel (cmd & ~CMD_ASE, &ehci->regs->command); + (void) handshake (&ehci->regs->status, STS_ASS, 0, 150); +#if 0 + // one VT8235 system wants to die with STS_FATAL + // unless this qh is leaked here. others seem ok... + qh = qh_get (qh); + dbg_qh ("async/off", ehci, qh); +#endif } qh->qh_next.qh = ehci->async = 0; @@ -940,10 +974,6 @@ prev = ehci->async; while (prev->qh_next.qh != qh && prev->qh_next.qh != ehci->async) prev = prev->qh_next.qh; -#ifdef DEBUG - if (prev->qh_next.qh != qh) - BUG (); -#endif if (qh->hw_info1 & __constant_cpu_to_le32 (QH_HEAD)) { ehci->async = prev; @@ -957,48 +987,63 @@ cmd |= CMD_IAAD; writel (cmd, &ehci->regs->command); /* posted write need not be known to HC yet ... */ + + mod_timer (&ehci->watchdog, jiffies + EHCI_WATCHDOG_JIFFIES); } /*-------------------------------------------------------------------------*/ -static void scan_async (struct ehci_hcd *ehci) +static void +scan_async (struct ehci_hcd *ehci) { struct ehci_qh *qh; - unsigned long flags; + unsigned count; - spin_lock_irqsave (&ehci->lock, flags); rescan: qh = ehci->async; + count = 0; if (likely (qh != 0)) { do { /* clean any finished work for this qh */ if (!list_empty (&qh->qtd_list)) { // dbg_qh ("scan_async", ehci, qh); qh = qh_get (qh); - spin_unlock_irqrestore (&ehci->lock, flags); /* concurrent unlink could happen here */ - qh_completions (ehci, qh, 1); - - spin_lock_irqsave (&ehci->lock, flags); + count += qh_completions (ehci, qh); qh_put (ehci, qh); } - /* unlink idle entries (reduces PCI usage) */ + /* unlink idle entries, reducing HC PCI usage as + * well as HCD schedule-scanning costs. removing + * the last qh is deferred, since it's costly. + * + * FIXME don't unlink idle entries so quickly; it + * can penalize (common) half duplex protocols. + */ if (list_empty (&qh->qtd_list) && !ehci->reclaim) { if (qh->qh_next.qh != qh) { // dbg ("irq/empty"); start_unlink_async (ehci, qh); - } else { - // FIXME: arrange to stop - // after it's been idle a while. + } else if (!timer_pending (&ehci->watchdog)) { + /* can't use IAA for last entry */ + ehci->async_idle = 1; + mod_timer (&ehci->watchdog, + jiffies + EHCI_ASYNC_JIFFIES); } } + + /* keep latencies down: let any irqs in */ + if (count > max_completions) { + spin_unlock_irq (&ehci->lock); + cpu_relax (); + spin_lock_irq (&ehci->lock); + goto rescan; + } + qh = qh->qh_next.qh; if (!qh) /* unlinked? */ goto rescan; } while (qh != ehci->async); } - - spin_unlock_irqrestore (&ehci->lock, flags); } diff -Nru a/drivers/usb/hcd/ehci-sched.c b/drivers/usb/hcd/ehci-sched.c --- a/drivers/usb/hcd/ehci-sched.c Mon Oct 21 22:14:31 2002 +++ b/drivers/usb/hcd/ehci-sched.c Mon Oct 21 22:14:31 2002 @@ -33,19 +33,6 @@ * or with "USB On The Go" additions to USB 2.0 ...) */ -/* - * Ceiling microseconds (typical) for that many bytes at high speed - * ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed - * to preallocate bandwidth) - */ -#define EHCI_HOST_DELAY 5 /* nsec, guess */ -#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \ - + ((2083UL * (3167 + BitTime (bytes)))/1000) \ - + EHCI_HOST_DELAY) -#define HS_USECS_ISO(bytes) NS_TO_US ( ((long)(38 * 8 * 2.083)) \ - + ((2083UL * (3167 + BitTime (bytes)))/1000) \ - + EHCI_HOST_DELAY) - static int ehci_get_frame (struct usb_hcd *hcd); /*-------------------------------------------------------------------------*/ @@ -124,6 +111,9 @@ /* is it in the S-mask? */ if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe)) usecs += q->qh->usecs; + /* ... or C-mask? */ + if (q->qh->hw_info2 & cpu_to_le32 (1 << (8 + uframe))) + usecs += q->qh->c_usecs; q = &q->qh->qh_next; break; case Q_TYPE_FSTN: @@ -181,15 +171,19 @@ /*-------------------------------------------------------------------------*/ -static void enable_periodic (struct ehci_hcd *ehci) +static int enable_periodic (struct ehci_hcd *ehci) { u32 cmd; + int status; /* did clearing PSE did take effect yet? * takes effect only at frame boundaries... */ - while (readl (&ehci->regs->status) & STS_PSS) - udelay (20); + status = handshake (&ehci->regs->status, STS_PSS, 0, 9 * 125); + if (status != 0) { + ehci->hcd.state = USB_STATE_HALT; + return status; + } cmd = readl (&ehci->regs->command) | CMD_PSE; writel (cmd, &ehci->regs->command); @@ -199,80 +193,104 @@ /* make sure tasklet scans these */ ehci->next_uframe = readl (&ehci->regs->frame_index) % (ehci->periodic_size << 3); + return 0; } -static void disable_periodic (struct ehci_hcd *ehci) +static int disable_periodic (struct ehci_hcd *ehci) { u32 cmd; + int status; /* did setting PSE not take effect yet? * takes effect only at frame boundaries... */ - while (!(readl (&ehci->regs->status) & STS_PSS)) - udelay (20); + status = handshake (&ehci->regs->status, STS_PSS, STS_PSS, 9 * 125); + if (status != 0) { + ehci->hcd.state = USB_STATE_HALT; + return status; + } cmd = readl (&ehci->regs->command) & ~CMD_PSE; writel (cmd, &ehci->regs->command); /* posted write ... */ ehci->next_uframe = -1; + return 0; } /*-------------------------------------------------------------------------*/ +// FIXME microframe periods not yet handled + static void intr_deschedule ( struct ehci_hcd *ehci, - unsigned frame, struct ehci_qh *qh, - unsigned period + int wait ) { - unsigned long flags; - - period >>= 3; // FIXME microframe periods not handled yet - - spin_lock_irqsave (&ehci->lock, flags); + int status; + unsigned frame = qh->start; do { periodic_unlink (ehci, frame, qh); qh_put (ehci, qh); - frame += period; + frame += qh->period; } while (frame < ehci->periodic_size); qh->qh_state = QH_STATE_UNLINK; qh->qh_next.ptr = 0; - ehci->periodic_urbs--; + ehci->periodic_sched--; /* maybe turn off periodic schedule */ - if (!ehci->periodic_urbs) - disable_periodic (ehci); - else + if (!ehci->periodic_sched) + status = disable_periodic (ehci); + else { + status = 0; vdbg ("periodic schedule still enabled"); - - spin_unlock_irqrestore (&ehci->lock, flags); + } /* * If the hc may be looking at this qh, then delay a uframe * (yeech!) to be sure it's done. * No other threads may be mucking with this qh. */ - if (((ehci_get_frame (&ehci->hcd) - frame) % period) == 0) - udelay (125); + if (((ehci_get_frame (&ehci->hcd) - frame) % qh->period) == 0) { + if (wait) { + udelay (125); + qh->hw_next = EHCI_LIST_END; + } else { + /* we may not be IDLE yet, but if the qh is empty + * the race is very short. then if qh also isn't + * rescheduled soon, it won't matter. otherwise... + */ + vdbg ("intr_deschedule..."); + } + } else + qh->hw_next = EHCI_LIST_END; qh->qh_state = QH_STATE_IDLE; - qh->hw_next = EHCI_LIST_END; - vdbg ("descheduled qh %p, per = %d frame = %d count = %d, urbs = %d", - qh, period, frame, - atomic_read (&qh->refcount), ehci->periodic_urbs); + /* update per-qh bandwidth utilization (for usbfs) */ + hcd_to_bus (&ehci->hcd)->bandwidth_allocated -= + (qh->usecs + qh->c_usecs) / qh->period; + + dbg ("descheduled qh %p, period = %d frame = %d count = %d, urbs = %d", + qh, qh->period, frame, + atomic_read (&qh->refcount), ehci->periodic_sched); } static int check_period ( struct ehci_hcd *ehci, unsigned frame, - int uframe, + unsigned uframe, unsigned period, unsigned usecs ) { + /* complete split running into next frame? + * given FSTN support, we could sometimes check... + */ + if (uframe >= 8) + return 0; + /* * 80% periodic == 100 usec/uframe available * convert "usecs we need" to "max already claimed" @@ -284,6 +302,8 @@ // FIXME delete when intr_submit handles non-empty queues // this gives us a one intr/frame limit (vs N/uframe) +// ... and also lets us avoid tracking split transactions +// that might collide at a given TT/hub. if (ehci->pshadow [frame].ptr) return 0; @@ -298,226 +318,203 @@ return 1; } -static int intr_submit ( - struct ehci_hcd *ehci, - struct urb *urb, - struct list_head *qtd_list, - int mem_flags -) { - unsigned epnum, period; - unsigned short usecs; - unsigned long flags; - struct ehci_qh *qh; - struct hcd_dev *dev; - int status = 0; +static int check_intr_schedule ( + struct ehci_hcd *ehci, + unsigned frame, + unsigned uframe, + const struct ehci_qh *qh, + u32 *c_maskp +) +{ + int retval = -ENOSPC; - /* get endpoint and transfer data */ - epnum = usb_pipeendpoint (urb->pipe); - if (usb_pipein (urb->pipe)) - epnum |= 0x10; - if (urb->dev->speed != USB_SPEED_HIGH) { - dbg ("no intr/tt scheduling yet"); - status = -ENOSYS; + if (!check_period (ehci, frame, uframe, qh->period, qh->usecs)) + goto done; + if (!qh->c_usecs) { + retval = 0; + *c_maskp = cpu_to_le32 (0); goto done; } - /* - * NOTE: current completion/restart logic doesn't handle more than - * one qtd in a periodic qh ... 16-20 KB/urb is pretty big for this. - * such big requests need many periods to transfer. + /* This is a split transaction; check the bandwidth available for + * the completion too. Check both worst and best case gaps: worst + * case is SPLIT near uframe end, and CSPLIT near start ... best is + * vice versa. Difference can be almost two uframe times, but we + * reserve unnecessary bandwidth (waste it) this way. (Actually + * even better cases exist, like immediate device NAK.) * - * FIXME want to change hcd core submit model to expect queuing - * for all transfer types ... not just ISO and (with flag) BULK. - * that means: getting rid of this check; handling the "interrupt - * urb already queued" case below like bulk queuing is handled (no - * errors possible!); and completly getting rid of that annoying - * qh restart logic. simpler/smaller overall, and more flexible. + * FIXME don't even bother unless we know this TT is idle in that + * range of uframes ... for now, check_period() allows only one + * interrupt transfer per frame, so needn't check "TT busy" status + * when scheduling a split (QH, SITD, or FSTN). + * + * FIXME ehci 0.96 and above can use FSTNs */ - if (unlikely (qtd_list->next != qtd_list->prev)) { - dbg ("only one intr qtd per urb allowed"); - status = -EINVAL; + if (!check_period (ehci, frame, uframe + qh->gap_uf + 1, + qh->period, qh->c_usecs)) goto done; - } - - usecs = HS_USECS (urb->transfer_buffer_length); - - /* FIXME handle HS periods of less than 1 frame. */ - period = urb->interval >> 3; - if (period < 1) { - dbg ("intr period %d uframes, NYET!", urb->interval); - status = -EINVAL; + if (!check_period (ehci, frame, uframe + qh->gap_uf, + qh->period, qh->c_usecs)) goto done; - } - spin_lock_irqsave (&ehci->lock, flags); - - /* get the qh (must be empty and idle) */ - dev = (struct hcd_dev *)urb->dev->hcpriv; - qh = (struct ehci_qh *) dev->ep [epnum]; - if (qh) { - /* only allow one queued interrupt urb per EP */ - if (unlikely (qh->qh_state != QH_STATE_IDLE - || !list_empty (&qh->qtd_list))) { - dbg ("interrupt urb already queued"); - status = -EBUSY; - } else { - /* maybe reset hardware's data toggle in the qh */ - if (unlikely (!usb_gettoggle (urb->dev, epnum & 0x0f, - !(epnum & 0x10)))) { - qh->hw_token |= - __constant_cpu_to_le32 (QTD_TOGGLE); - usb_settoggle (urb->dev, epnum & 0x0f, - !(epnum & 0x10), 1); - } - /* trust the QH was set up as interrupt ... */ - list_splice (qtd_list, &qh->qtd_list); - qh_update (qh, list_entry (qtd_list->next, - struct ehci_qtd, qtd_list)); - qtd_list = &qh->qtd_list; - } - } else { - /* can't sleep here, we have ehci->lock... */ - qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC); - if (likely (qh != 0)) { - // dbg ("new INTR qh %p", qh); - dev->ep [epnum] = qh; - qtd_list = &qh->qtd_list; - } else - status = -ENOMEM; - } + *c_maskp = cpu_to_le32 (0x03 << (8 + uframe + qh->gap_uf)); + retval = 0; +done: + return retval; +} - /* Schedule this periodic QH. */ - if (likely (status == 0)) { - unsigned frame = period; +static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + int status; + unsigned uframe; + u32 c_mask; + unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ - qh->hw_next = EHCI_LIST_END; - qh->usecs = usecs; + qh->hw_next = EHCI_LIST_END; + frame = qh->start; - urb->hcpriv = qh_get (qh); + /* reuse the previous schedule slots, if we can */ + if (frame < qh->period) { + uframe = ffs (le32_to_cpup (&qh->hw_info2) & 0x00ff); + status = check_intr_schedule (ehci, frame, --uframe, + qh, &c_mask); + } else { + uframe = 0; + c_mask = 0; status = -ENOSPC; + } - /* pick a set of schedule slots, link the QH into them */ + /* else scan the schedule to find a group of slots such that all + * uframes have enough periodic bandwidth available. + */ + if (status) { + frame = qh->period - 1; do { - int uframe; - - /* pick a set of slots such that all uframes have - * enough periodic bandwidth available. - * - * FIXME for TT splits, need uframes for start and end. - * FSTNs can put end into next frame (uframes 0 or 1). - */ - frame--; for (uframe = 0; uframe < 8; uframe++) { - if (check_period (ehci, frame, uframe, - period, usecs) != 0) + status = check_intr_schedule (ehci, + frame, uframe, qh, + &c_mask); + if (status == 0) break; } - if (uframe == 8) - continue; + } while (status && --frame); + if (status) + goto done; + qh->start = frame; + + /* reset S-frame and (maybe) C-frame masks */ + qh->hw_info2 &= ~0xffff; + qh->hw_info2 |= cpu_to_le32 (1 << uframe) | c_mask; + } else + dbg ("reused previous qh %p schedule", qh); + + /* stuff into the periodic schedule */ + qh->qh_state = QH_STATE_LINKED; + dbg ("scheduled qh %p usecs %d/%d period %d.0 starting %d.%d (gap %d)", + qh, qh->usecs, qh->c_usecs, + qh->period, frame, uframe, qh->gap_uf); + do { + if (unlikely (ehci->pshadow [frame].ptr != 0)) { - /* QH will run once each period, starting there */ - urb->start_frame = frame; - status = 0; - - /* set S-frame mask */ - qh->hw_info2 |= cpu_to_le32 (1 << uframe); - // dbg_qh ("Schedule INTR qh", ehci, qh); - - /* stuff into the periodic schedule */ - qh->qh_state = QH_STATE_LINKED; - vdbg ("qh %p usecs %d period %d starting %d.%d", - qh, qh->usecs, period, frame, uframe); - do { - if (unlikely (ehci->pshadow [frame].ptr != 0)) { // FIXME -- just link toward the end, before any qh with a shorter period, -// AND handle it already being (implicitly) linked into this frame -// AS WELL AS updating the check_period() logic - BUG (); - } else { - ehci->pshadow [frame].qh = qh_get (qh); - ehci->periodic [frame] = - QH_NEXT (qh->qh_dma); - } - wmb (); - frame += period; - } while (frame < ehci->periodic_size); - - /* update bandwidth utilization records (for usbfs) */ - usb_claim_bandwidth (urb->dev, urb, usecs/period, 0); - - /* maybe enable periodic schedule processing */ - if (!ehci->periodic_urbs++) - enable_periodic (ehci); - break; +// AND accomodate it already having been linked here (after some other qh) +// AS WELL AS updating the schedule checking logic + + BUG (); + } else { + ehci->pshadow [frame].qh = qh_get (qh); + ehci->periodic [frame] = + QH_NEXT (qh->qh_dma); + } + wmb (); + frame += qh->period; + } while (frame < ehci->periodic_size); + + /* update per-qh bandwidth for usbfs */ + hcd_to_bus (&ehci->hcd)->bandwidth_allocated += + (qh->usecs + qh->c_usecs) / qh->period; + + /* maybe enable periodic schedule processing */ + if (!ehci->periodic_sched++) + status = enable_periodic (ehci); +done: + return status; +} - } while (frame); +static int intr_submit ( + struct ehci_hcd *ehci, + struct urb *urb, + struct list_head *qtd_list, + int mem_flags +) { + unsigned epnum; + unsigned long flags; + struct ehci_qh *qh; + struct hcd_dev *dev; + int is_input; + int status = 0; + struct list_head empty; + + /* get endpoint and transfer/schedule data */ + epnum = usb_pipeendpoint (urb->pipe); + is_input = usb_pipein (urb->pipe); + if (is_input) + epnum |= 0x10; + + spin_lock_irqsave (&ehci->lock, flags); + dev = (struct hcd_dev *)urb->dev->hcpriv; + + /* get qh and force any scheduling errors */ + INIT_LIST_HEAD (&empty); + qh = qh_append_tds (ehci, urb, &empty, epnum, &dev->ep [epnum]); + if (qh == 0) { + status = -ENOMEM; + goto done; } - spin_unlock_irqrestore (&ehci->lock, flags); + if (qh->qh_state == QH_STATE_IDLE) { + if ((status = qh_schedule (ehci, qh)) != 0) + goto done; + } + + /* then queue the urb's tds to the qh */ + qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]); + BUG_ON (qh == 0); + + /* ... update usbfs periodic stats */ + hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs++; + done: + spin_unlock_irqrestore (&ehci->lock, flags); if (status) qtd_list_free (ehci, urb, qtd_list); return status; } -static unsigned long +static unsigned intr_complete ( struct ehci_hcd *ehci, unsigned frame, - struct ehci_qh *qh, - unsigned long flags /* caller owns ehci->lock ... */ + struct ehci_qh *qh ) { - struct ehci_qtd *qtd; - struct urb *urb; - int unlinking; + unsigned count; /* nothing to report? */ if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE)) != 0)) - return flags; + return 0; if (unlikely (list_empty (&qh->qtd_list))) { dbg ("intr qh %p no TDs?", qh); - return flags; + return 0; } - qtd = list_entry (qh->qtd_list.next, struct ehci_qtd, qtd_list); - urb = qtd->urb; - unlinking = (urb->status == -ENOENT) || (urb->status == -ECONNRESET); - - /* call any completions, after patching for reactivation */ - spin_unlock_irqrestore (&ehci->lock, flags); - /* NOTE: currently restricted to one qtd per qh! */ - if (qh_completions (ehci, qh, 0) == 0) - urb = 0; - spin_lock_irqsave (&ehci->lock, flags); + /* handle any completions */ + count = qh_completions (ehci, qh); - /* never reactivate requests that were unlinked ... */ - if (likely (urb != 0)) { - if (unlinking - || urb->status == -ECONNRESET - || urb->status == -ENOENT - // || (urb->dev == null) - || ehci->hcd.state == USB_STATE_HALT) - urb = 0; - // FIXME look at all those unlink cases ... we always - // need exactly one completion that reports unlink. - // the one above might not have been it! - } - - /* normally reactivate */ - if (likely (urb != 0)) { - if (usb_pipeout (urb->pipe)) - pci_dma_sync_single (ehci->hcd.pdev, - qtd->buf_dma, - urb->transfer_buffer_length, - PCI_DMA_TODEVICE); - urb->status = -EINPROGRESS; - urb->actual_length = 0; + if (unlikely (list_empty (&qh->qtd_list))) + intr_deschedule (ehci, qh, 0); - /* patch qh and restart */ - qh_update (qh, qtd); - } - return flags; + return count; } /*-------------------------------------------------------------------------*/ @@ -527,11 +524,6 @@ { struct ehci_itd *first_itd = urb->hcpriv; - pci_unmap_single (ehci->hcd.pdev, - first_itd->buf_dma, urb->transfer_buffer_length, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); while (!list_empty (&first_itd->itd_list)) { struct ehci_itd *itd; @@ -557,6 +549,7 @@ u32 buf1; unsigned i, epnum, maxp, multi; unsigned length; + int is_input; itd->hw_next = EHCI_LIST_END; itd->urb = urb; @@ -578,7 +571,8 @@ * as encoded in the ep descriptor's maxpacket field */ epnum = usb_pipeendpoint (urb->pipe); - if (usb_pipein (urb->pipe)) { + is_input = usb_pipein (urb->pipe); + if (is_input) { maxp = urb->dev->epmaxpacketin [epnum]; buf1 = (1 << 11); } else { @@ -598,7 +592,7 @@ urb->iso_frame_desc [index].length); return -ENOSPC; } - itd->usecs = HS_USECS_ISO (length); + itd->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 1, length); /* "plus" info in low order bits of buffer pointers */ itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum); @@ -625,16 +619,7 @@ int frame_index; struct ehci_itd *first_itd, *itd; int status; - dma_addr_t buf_dma, itd_dma; - - /* set up one dma mapping for this urb */ - buf_dma = pci_map_single (ehci->hcd.pdev, - urb->transfer_buffer, urb->transfer_buffer_length, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); - if (buf_dma == 0) - return -ENOMEM; + dma_addr_t itd_dma; /* allocate/init ITDs */ for (frame_index = 0, first_itd = 0; @@ -648,7 +633,8 @@ memset (itd, 0, sizeof *itd); itd->itd_dma = itd_dma; - status = itd_fill (ehci, itd, urb, frame_index, buf_dma); + status = itd_fill (ehci, itd, urb, frame_index, + urb->transfer_dma); if (status != 0) goto fail; @@ -737,7 +723,7 @@ /* calculate the legal range [start,max) */ now = readl (&ehci->regs->frame_index) + 1; /* next uframe */ - if (!ehci->periodic_urbs) + if (!ehci->periodic_sched) now += 8; /* startup delay */ now %= mod; end = now + mod; @@ -857,8 +843,12 @@ usb_claim_bandwidth (urb->dev, urb, usecs, 1); /* maybe enable periodic schedule processing */ - if (!ehci->periodic_urbs++) - enable_periodic (ehci); + if (!ehci->periodic_sched++) { + if ((status = enable_periodic (ehci)) != 0) { + // FIXME deschedule right away + err ("itd_schedule, enable = %d", status); + } + } return 0; @@ -873,15 +863,14 @@ #define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR) -static unsigned long +static unsigned itd_complete ( struct ehci_hcd *ehci, struct ehci_itd *itd, - unsigned uframe, - unsigned long flags + unsigned uframe ) { struct urb *urb = itd->urb; - struct iso_packet_descriptor *desc; + struct usb_iso_packet_descriptor *desc; u32 t; /* update status for this uframe's transfers */ @@ -916,7 +905,7 @@ /* handle completion now? */ if ((itd->index + 1) != urb->number_of_packets) - return flags; + return 0; /* * Always give the urb back to the driver ... expect it to submit @@ -931,16 +920,17 @@ if (urb->status == -EINPROGRESS) urb->status = 0; - spin_unlock_irqrestore (&ehci->lock, flags); + /* complete() can reenter this HCD */ + spin_unlock (&ehci->lock); usb_hcd_giveback_urb (&ehci->hcd, urb); - spin_lock_irqsave (&ehci->lock, flags); + spin_lock (&ehci->lock); /* defer stopping schedule; completion can submit */ - ehci->periodic_urbs--; - if (!ehci->periodic_urbs) - disable_periodic (ehci); + ehci->periodic_sched--; + if (!ehci->periodic_sched) + (void) disable_periodic (ehci); - return flags; + return 1; } /*-------------------------------------------------------------------------*/ @@ -952,10 +942,6 @@ dbg ("itd_submit urb %p", urb); - /* NOTE DMA mapping assumes this ... */ - if (urb->iso_frame_desc [0].offset != 0) - return -EINVAL; - /* allocate ITDs w/o locking anything */ status = itd_urb_transaction (ehci, urb, mem_flags); if (status < 0) @@ -978,130 +964,21 @@ /* * "Split ISO TDs" ... used for USB 1.1 devices going through * the TTs in USB 2.0 hubs. + * + * FIXME not yet implemented */ -static void -sitd_free (struct ehci_hcd *ehci, struct ehci_sitd *sitd) -{ - pci_pool_free (ehci->sitd_pool, sitd, sitd->sitd_dma); -} - -static struct ehci_sitd * -sitd_make ( - struct ehci_hcd *ehci, - struct urb *urb, - unsigned index, // urb->iso_frame_desc [index] - unsigned uframe, // scheduled start - dma_addr_t dma, // mapped transfer buffer - int mem_flags -) { - struct ehci_sitd *sitd; - unsigned length; - - sitd = pci_pool_alloc (ehci->sitd_pool, mem_flags, &dma); - if (!sitd) - return sitd; - sitd->urb = urb; - length = urb->iso_frame_desc [index].length; - dma += urb->iso_frame_desc [index].offset; - -#if 0 - // FIXME: do the rest! -#else - sitd_free (ehci, sitd); - return 0; -#endif - -} - -static void -sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd) -{ - u32 ptr; - - ptr = cpu_to_le32 (sitd->sitd_dma | 2); // type 2 == sitd - if (ehci->pshadow [frame].ptr) { - if (!sitd->sitd_next.ptr) { - sitd->sitd_next = ehci->pshadow [frame]; - sitd->hw_next = ehci->periodic [frame]; - } else if (sitd->sitd_next.ptr != ehci->pshadow [frame].ptr) { - dbg ("frame %d sitd link goof", frame); - BUG (); - } - } - ehci->pshadow [frame].sitd = sitd; - ehci->periodic [frame] = ptr; -} - -static unsigned long -sitd_complete ( - struct ehci_hcd *ehci, - struct ehci_sitd *sitd, - unsigned long flags -) { - // FIXME -- implement! - - dbg ("NYI -- sitd_complete"); - return flags; -} - -/*-------------------------------------------------------------------------*/ - -static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) -{ - // struct ehci_sitd *first_sitd = 0; - unsigned frame_index; - dma_addr_t dma; - - dbg ("NYI -- sitd_submit"); - - // FIXME -- implement! - - // FIXME: setup one big dma mapping - dma = 0; - - for (frame_index = 0; - frame_index < urb->number_of_packets; - frame_index++) { - struct ehci_sitd *sitd; - unsigned uframe; - - // FIXME: use real arguments, schedule this! - uframe = -1; - - sitd = sitd_make (ehci, urb, frame_index, - uframe, dma, mem_flags); - - if (sitd) { - /* - if (first_sitd) - list_add_tail (&sitd->sitd_list, - &first_sitd->sitd_list); - else - first_sitd = sitd; - */ - } else { - // FIXME: clean everything up - } - } - - // if we have a first sitd, then - // store them all into the periodic schedule! - // urb->hcpriv = first sitd in sitd_list - - return -ENOSYS; -} #endif /* have_split_iso */ /*-------------------------------------------------------------------------*/ -static void scan_periodic (struct ehci_hcd *ehci) +static void +scan_periodic (struct ehci_hcd *ehci) { unsigned frame, clock, now_uframe, mod; - unsigned long flags; + unsigned count = 0; mod = ehci->periodic_size << 3; - spin_lock_irqsave (&ehci->lock, flags); /* * When running, scan from last scan point up to "now" @@ -1122,6 +999,14 @@ u32 type, *hw_p; unsigned uframes; + /* keep latencies down: let any irqs in */ + if (count > max_completions) { + spin_unlock_irq (&ehci->lock); + cpu_relax (); + count = 0; + spin_lock_irq (&ehci->lock); + } + restart: /* scan schedule to _before_ current frame index */ if (frame == clock) @@ -1145,8 +1030,8 @@ last = (q.qh->hw_next == EHCI_LIST_END); temp = q.qh->qh_next; type = Q_NEXT_TYPE (q.qh->hw_next); - flags = intr_complete (ehci, frame, - qh_get (q.qh), flags); + count += intr_complete (ehci, frame, + qh_get (q.qh)); qh_put (ehci, q.qh); q = temp; break; @@ -1178,8 +1063,8 @@ type = Q_NEXT_TYPE (*hw_p); /* might free q.itd ... */ - flags = itd_complete (ehci, - temp.itd, uf, flags); + count += itd_complete (ehci, + temp.itd, uf); break; } } @@ -1195,7 +1080,7 @@ #ifdef have_split_iso case Q_TYPE_SITD: last = (q.sitd->hw_next == EHCI_LIST_END); - flags = sitd_complete (ehci, q.sitd, flags); + sitd_complete (ehci, q.sitd); type = Q_NEXT_TYPE (q.sitd->hw_next); // FIXME unlink SITD after split completes @@ -1241,5 +1126,4 @@ } else frame = (frame + 1) % ehci->periodic_size; } - spin_unlock_irqrestore (&ehci->lock, flags); } diff -Nru a/drivers/usb/hcd/ehci.h b/drivers/usb/hcd/ehci.h --- a/drivers/usb/hcd/ehci.h Mon Oct 21 22:14:31 2002 +++ b/drivers/usb/hcd/ehci.h Mon Oct 21 22:14:31 2002 @@ -21,6 +21,23 @@ /* definitions used for the EHCI driver */ +/* statistics can be kept for for tuning/monitoring */ +struct ehci_stats { + /* irq usage */ + unsigned long normal; + unsigned long error; + unsigned long reclaim; + + /* termination of urbs from core */ + unsigned long complete; + unsigned long unlink; + + /* qhs patched to recover from td queueing race + * (can avoid by using 'dummy td', allowing fewer irqs) + */ + unsigned long qpatch; +}; + /* ehci_hcd->lock guards shared data against other CPUs: * ehci_hcd: async, reclaim, periodic (and shadow), ... * hcd_dev: ep[] @@ -39,7 +56,8 @@ /* async schedule support */ struct ehci_qh *async; struct ehci_qh *reclaim; - int reclaim_ready; + int reclaim_ready : 1, + async_idle : 1; /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 /* some HCs can do less */ @@ -50,7 +68,7 @@ union ehci_shadow *pshadow; /* mirror hw periodic table */ int next_uframe; /* scan periodic, start here */ - unsigned periodic_urbs; /* how many urbs scheduled? */ + unsigned periodic_sched; /* periodic activity count */ /* deferred work from IRQ, etc */ struct tasklet_struct tasklet; @@ -69,10 +87,19 @@ struct pci_pool *qtd_pool; /* one or more per qh */ struct pci_pool *itd_pool; /* itd per iso urb */ struct pci_pool *sitd_pool; /* sitd per split iso urb */ + + struct timer_list watchdog; + +#ifdef EHCI_STATS + struct ehci_stats stats; +# define COUNT(x) do { (x)++; } while (0) +#else +# define COUNT(x) do {} while (0) +#endif }; /* unwrap an HCD pointer to get an EHCI_HCD pointer */ -#define hcd_to_ehci(hcd_ptr) list_entry(hcd_ptr, struct ehci_hcd, hcd) +#define hcd_to_ehci(hcd_ptr) container_of(hcd_ptr, struct ehci_hcd, hcd) /* NOTE: urb->transfer_flags expected to not use this bit !!! */ #define EHCI_STATE_UNLINK 0x8000 /* urb being unlinked */ @@ -219,7 +246,6 @@ /* dma same in urb's qtds, except 1st control qtd (setup buffer) */ struct urb *urb; /* qtd's urb */ - dma_addr_t buf_dma; /* buffer address */ size_t length; /* length of buffer */ } __attribute__ ((aligned (32))); @@ -287,15 +313,20 @@ struct list_head qtd_list; /* sw qtd list */ atomic_t refcount; - unsigned short usecs; /* intr bandwidth */ - short qh_state; + + u8 qh_state; #define QH_STATE_LINKED 1 /* HC sees this */ #define QH_STATE_UNLINK 2 /* HC may still see this */ #define QH_STATE_IDLE 3 /* HC doesn't see this */ -#ifdef EHCI_SOFT_RETRIES - int retries; -#endif + /* periodic schedule info */ + u8 usecs; /* intr bandwidth */ + u8 gap_uf; /* uframes split/csplit gap */ + u8 c_usecs; /* ... split completion bw */ + unsigned short period; /* polling interval */ + unsigned short start; /* where polling starts */ +#define NO_FRAME ((unsigned short)~0) /* pick new start */ + } __attribute__ ((aligned (32))); /*-------------------------------------------------------------------------*/ @@ -360,6 +391,9 @@ union ehci_shadow sitd_next; /* ptr to periodic q entry */ struct urb *urb; dma_addr_t buf_dma; /* buffer address */ + + unsigned short usecs; /* start bandwidth */ + unsigned short c_usecs; /* completion bandwidth */ } __attribute__ ((aligned (32))); /*-------------------------------------------------------------------------*/ @@ -381,5 +415,40 @@ dma_addr_t fstn_dma; union ehci_shadow fstn_next; /* ptr to periodic q entry */ } __attribute__ ((aligned (32))); + +/*-------------------------------------------------------------------------*/ + +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32) + +#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb) +#define STUB_DEBUG_FILES + +static inline int hcd_register_root (struct usb_hcd *hcd) +{ + return usb_new_device (hcd_to_bus (hcd)->root_hub); +} + +#else /* LINUX_VERSION_CODE */ + +// hcd_to_bus() eventually moves to hcd.h on 2.5 too +static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd) + { return &hcd->self; } +// ... as does hcd_register_root() +static inline int hcd_register_root (struct usb_hcd *hcd) +{ + return usb_register_root_hub ( + hcd_to_bus (hcd)->root_hub, &hcd->pdev->dev); +} + +#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb,mem_flags) + +#ifndef DEBUG +#define STUB_DEBUG_FILES +#endif /* DEBUG */ + +#endif /* LINUX_VERSION_CODE */ + +/*-------------------------------------------------------------------------*/ #endif /* __LINUX_EHCI_HCD_H */ diff -Nru a/drivers/usb/hcd.c b/drivers/usb/hcd.c --- a/drivers/usb/hcd.c Mon Oct 21 22:14:31 2002 +++ b/drivers/usb/hcd.c Mon Oct 21 22:14:31 2002 @@ -296,7 +296,7 @@ // serial number } else if (id == 1) { - strcpy (buf, hcd->bus_name); + strcpy (buf, hcd->bus->bus_name); // product description } else if (id == 2) { @@ -392,7 +392,7 @@ case DeviceOutRequest | USB_REQ_SET_ADDRESS: // wValue == urb->dev->devaddr dbg ("%s root hub device address %d", - hcd->bus_name, wValue); + hcd->bus->bus_name, wValue); break; /* INTERFACE REQUESTS (no defined feature/status flags) */ @@ -506,7 +506,7 @@ && rh_status_urb (hcd, urb) != 0) { /* another driver snuck in? */ dbg ("%s, can't resubmit roothub status urb?", - hcd->bus_name); + hcd->bus->bus_name); spin_unlock_irqrestore (&hcd_data_lock, flags); BUG (); } @@ -687,6 +687,7 @@ base); // FIXME simpler: make "bus" be that data, not pointer to it. +// (fixed in 2.5) bus = usb_alloc_bus (&hcd_operations); if (bus == NULL) { dbg ("usb_alloc_bus fail"); @@ -695,7 +696,6 @@ goto clean_3; } hcd->bus = bus; - hcd->bus_name = dev->slot_name; /* prefer bus->bus_name */ bus->bus_name = dev->slot_name; hcd->product_desc = dev->name; bus->hcpriv = (void *) hcd; @@ -739,14 +739,14 @@ hcd = pci_get_drvdata(dev); if (!hcd) return; - info ("remove: %s, state %x", hcd->bus_name, hcd->state); + info ("remove: %s, state %x", hcd->bus->bus_name, hcd->state); if (in_interrupt ()) BUG (); hub = hcd->bus->root_hub; hcd->state = USB_STATE_QUIESCING; - dbg ("%s: roothub graceful disconnect", hcd->bus_name); + dbg ("%s: roothub graceful disconnect", hcd->bus->bus_name); usb_disconnect (&hub); // usb_disconnect (&hcd->bus->root_hub); @@ -817,7 +817,7 @@ int retval; hcd = pci_get_drvdata(dev); - info ("suspend %s to state %d", hcd->bus_name, state); + info ("suspend %s to state %d", hcd->bus->bus_name, state); pci_save_state (dev, hcd->pci_state); @@ -846,12 +846,12 @@ int retval; hcd = pci_get_drvdata(dev); - info ("resume %s", hcd->bus_name); + info ("resume %s", hcd->bus->bus_name); /* guard against multiple resumes (APM bug?) */ atomic_inc (&hcd->resume_count); if (atomic_read (&hcd->resume_count) != 1) { - err ("concurrent PCI resumes for %s", hcd->bus_name); + err ("concurrent PCI resumes for %s", hcd->bus->bus_name); retval = 0; goto done; } @@ -868,7 +868,8 @@ retval = hcd->driver->resume (hcd); if (!HCD_IS_RUNNING (hcd->state)) { - dbg ("resume %s failure, retval %d", hcd->bus_name, retval); + dbg ("resume %s failure, retval %d", + hcd->bus->bus_name, retval); hc_died (hcd); // FIXME: recover, reset etc. } else { @@ -943,7 +944,8 @@ list_for_each (urblist, &dev->urb_list) { urb = list_entry (urblist, struct urb, urb_list); dbg ("shutdown %s urb %p pipe %x, current status %d", - hcd->bus_name, urb, urb->pipe, urb->status); + hcd->bus->bus_name, + urb, urb->pipe, urb->status); if (urb->status == -EINPROGRESS) urb->status = -ESHUTDOWN; } @@ -1067,8 +1069,6 @@ if (urb->transfer_buffer_length < 0) return -EINVAL; - // FIXME set urb->transfer_dma and/or setup_dma - if (urb->next) { warn ("use explicit queuing not urb->next"); return -EINVAL; @@ -1186,16 +1186,26 @@ if (status) return status; + // NOTE: 2.5 does this if !URB_NO_DMA_MAP transfer flag + if (usb_pipecontrol (urb->pipe)) + urb->setup_dma = pci_map_single ( + hcd->pdev, + urb->setup_packet, + sizeof (struct usb_ctrlrequest), + PCI_DMA_TODEVICE); + if (urb->transfer_buffer_length != 0) + urb->transfer_dma = pci_map_single ( + hcd->pdev, + urb->transfer_buffer, + urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? PCI_DMA_FROMDEVICE + : PCI_DMA_TODEVICE); + if (urb->dev == hcd->bus->root_hub) status = rh_urb_enqueue (hcd, urb); else status = hcd->driver->urb_enqueue (hcd, urb, mem_flags); - /* urb->dev got nulled if hcd called giveback for us - * NOTE: ref to urb->dev is a race without (2.5) refcounting, - * unless driver only returns status when it didn't giveback - */ - if (status && urb->dev) - urb_unlink (urb); return status; } @@ -1282,25 +1292,25 @@ goto done; } - /* For non-periodic transfers, any status except -EINPROGRESS means - * the HCD has already started to unlink this URB from the hardware. - * In that case, there's no more work to do. + /* Any status except -EINPROGRESS means the HCD has already started + * to return this URB to the driver. In that case, there's no + * more work for us to do. * - * For periodic transfers, this is the only way to trigger unlinking - * from the hardware. Since we (currently) overload urb->status to - * tell the driver to unlink, error status might get clobbered ... - * unless that transfer hasn't yet restarted. One such case is when - * the URB gets unlinked from its completion handler. + * There's much magic because of "automagic resubmit" of interrupt + * transfers, stopped only by explicit unlinking. We won't issue + * an "it's unlinked" callback more than once, but device drivers + * can need to retry (SMP, -EAGAIN) an unlink request as well as + * fake out the "not yet completed" state (set -EINPROGRESS) if + * unlinking from complete(). Automagic eventually vanishes. * * FIXME use an URB_UNLINKED flag to match URB_TIMEOUT_KILLED */ - switch (usb_pipetype (urb->pipe)) { - case PIPE_CONTROL: - case PIPE_BULK: - if (urb->status != -EINPROGRESS) { + if (urb->status != -EINPROGRESS) { + if (usb_pipetype (urb->pipe) == PIPE_INTERRUPT) + retval = -EAGAIN; + else retval = -EINVAL; - goto done; - } + goto done; } /* maybe set up to block on completion notification */ @@ -1340,7 +1350,7 @@ && HCD_IS_RUNNING (hcd->state) && !retval) { dbg ("%s: wait for giveback urb %p", - hcd->bus_name, urb); + hcd->bus->bus_name, urb); wait_for_completion (&splice.done); } else if ((urb->transfer_flags & USB_ASYNC_UNLINK) && retval == 0) { return -EINPROGRESS; @@ -1352,7 +1362,7 @@ bye: if (retval) dbg ("%s: hcd_unlink_urb fail %d", - hcd ? hcd->bus_name : "(no bus?)", + hcd ? hcd->bus->bus_name : "(no bus?)", retval); return retval; } @@ -1385,7 +1395,7 @@ /* device driver problem with refcounts? */ if (!list_empty (&dev->urb_list)) { dbg ("free busy dev, %s devnum %d (bug!)", - hcd->bus_name, udev->devnum); + hcd->bus->bus_name, udev->devnum); return -EINVAL; } @@ -1460,7 +1470,17 @@ dbg ("giveback urb %p status %d len %d", urb, urb->status, urb->actual_length); - // FIXME unmap urb->transfer_dma and/or setup_dma + // NOTE: 2.5 does this if !URB_NO_DMA_MAP transfer flag + if (usb_pipecontrol (urb->pipe)) + pci_unmap_single (hcd->pdev, urb->setup_dma, + sizeof (struct usb_ctrlrequest), + PCI_DMA_TODEVICE); + if (urb->transfer_buffer_length != 0) + pci_unmap_single (hcd->pdev, urb->transfer_dma, + urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? PCI_DMA_FROMDEVICE + : PCI_DMA_TODEVICE); /* pass ownership to the completion handler */ urb->complete (urb); diff -Nru a/drivers/usb/hcd.h b/drivers/usb/hcd.h --- a/drivers/usb/hcd.h Mon Oct 21 22:14:31 2002 +++ b/drivers/usb/hcd.h Mon Oct 21 22:14:31 2002 @@ -36,7 +36,6 @@ struct usb_bus *bus; /* hcd is-a bus */ struct list_head hcd_list; - const char *bus_name; const char *product_desc; const char *description; /* "ehci-hcd" etc */