Commit e23525cd authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge bk://linuxusb@bkbits.net/linus-2.5

into kroah.com:/home/greg/linux/BK/gregkh-2.5
parents e42e97d6 681cb8eb
...@@ -1020,6 +1020,16 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags) ...@@ -1020,6 +1020,16 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags)
if (status) if (status)
return status; return status;
/* increment urb's reference count as part of giving it to the HCD
* (which now controls it). HCD guarantees that it either returns
* an error or calls giveback(), but not both.
*/
urb = usb_get_urb (urb);
if (urb->dev == hcd->self.root_hub) {
urb->transfer_flags |= URB_NO_DMA_MAP;
return rh_urb_enqueue (hcd, urb);
}
/* lower level hcd code should use *_dma exclusively */ /* lower level hcd code should use *_dma exclusively */
if (!(urb->transfer_flags & URB_NO_DMA_MAP)) { if (!(urb->transfer_flags & URB_NO_DMA_MAP)) {
if (usb_pipecontrol (urb->pipe)) if (usb_pipecontrol (urb->pipe))
...@@ -1038,16 +1048,7 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags) ...@@ -1038,16 +1048,7 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags)
: PCI_DMA_TODEVICE); : PCI_DMA_TODEVICE);
} }
/* increment urb's reference count as part of giving it to the HCD return hcd->driver->urb_enqueue (hcd, urb, mem_flags);
* (which now controls it). HCD guarantees that it either returns
* an error or calls giveback(), but not both.
*/
urb = usb_get_urb (urb);
if (urb->dev == hcd->self.root_hub)
status = rh_urb_enqueue (hcd, urb);
else
status = hcd->driver->urb_enqueue (hcd, urb, mem_flags);
return status;
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
......
...@@ -27,7 +27,6 @@ ...@@ -27,7 +27,6 @@
/*****************************************************************************/ /*****************************************************************************/
#define __NO_VERSION__
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/fs.h> #include <linux/fs.h>
...@@ -561,35 +560,57 @@ static void put_mount (struct vfsmount **mount) ...@@ -561,35 +560,57 @@ static void put_mount (struct vfsmount **mount)
static int create_special_files (void) static int create_special_files (void)
{ {
struct dentry *parent; struct dentry *parent;
int retval; int retval = 0;
/* create the devices special file */ /* create the devices special file */
retval = get_mount (&usbdevice_fs_type, &usbdevfs_mount); retval = get_mount (&usbdevice_fs_type, &usbdevfs_mount);
if (retval) if (retval) {
return retval; err ("Unable to get usbdevfs mount");
goto exit;
}
retval = get_mount (&usb_fs_type, &usbfs_mount); retval = get_mount (&usb_fs_type, &usbfs_mount);
if (retval) { if (retval) {
put_mount (&usbfs_mount); err ("Unable to get usbfs mount");
return retval; goto error_clean_usbdevfs_mount;
} }
parent = usbfs_mount->mnt_sb->s_root; parent = usbfs_mount->mnt_sb->s_root;
devices_usbfs_dentry = fs_create_file ("devices", listmode | S_IFREG, parent, devices_usbfs_dentry = fs_create_file ("devices",
listmode | S_IFREG, parent,
NULL, &usbdevfs_devices_fops, NULL, &usbdevfs_devices_fops,
listuid, listgid); listuid, listgid);
if (devices_usbfs_dentry == NULL) { if (devices_usbfs_dentry == NULL) {
err ("Unable to create devices usbfs file"); err ("Unable to create devices usbfs file");
return -ENODEV; retval = -ENODEV;
goto error_clean_mounts;
} }
parent = usbdevfs_mount->mnt_sb->s_root; parent = usbdevfs_mount->mnt_sb->s_root;
devices_usbdevfs_dentry = fs_create_file ("devices", listmode | S_IFREG, parent, devices_usbdevfs_dentry = fs_create_file ("devices",
listmode | S_IFREG, parent,
NULL, &usbdevfs_devices_fops, NULL, &usbdevfs_devices_fops,
listuid, listgid); listuid, listgid);
if (devices_usbdevfs_dentry == NULL) { if (devices_usbdevfs_dentry == NULL) {
err ("Unable to create devices usbfs file"); err ("Unable to create devices usbfs file");
return -ENODEV; retval = -ENODEV;
goto error_remove_file;
} }
return 0; goto exit;
error_remove_file:
fs_remove_file (devices_usbfs_dentry);
devices_usbfs_dentry = NULL;
error_clean_mounts:
put_mount (&usbfs_mount);
error_clean_usbdevfs_mount:
put_mount (&usbdevfs_mount);
exit:
return retval;
} }
static void remove_special_files (void) static void remove_special_files (void)
......
...@@ -57,7 +57,7 @@ static void dbg_hcs_params (struct ehci_hcd *ehci, char *label) ...@@ -57,7 +57,7 @@ static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
strcat(buf, tmp); strcat(buf, tmp);
} }
dbg ("%s: %s portroute %s", dbg ("%s: %s portroute %s",
ehci->hcd.self.bus_name, label, hcd_to_bus (&ehci->hcd)->bus_name, label,
buf); buf);
} }
} }
...@@ -122,7 +122,8 @@ dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -122,7 +122,8 @@ dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
} }
} }
static int dbg_status_buf (char *buf, unsigned len, char *label, u32 status) static int __attribute__((__unused__))
dbg_status_buf (char *buf, unsigned len, char *label, u32 status)
{ {
return snprintf (buf, len, return snprintf (buf, len,
"%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s",
...@@ -140,7 +141,8 @@ static int dbg_status_buf (char *buf, unsigned len, char *label, u32 status) ...@@ -140,7 +141,8 @@ static int dbg_status_buf (char *buf, unsigned len, char *label, u32 status)
); );
} }
static int dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable) static int __attribute__((__unused__))
dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable)
{ {
return snprintf (buf, len, return snprintf (buf, len,
"%s%sintrenable %02x%s%s%s%s%s%s", "%s%sintrenable %02x%s%s%s%s%s%s",
...@@ -213,19 +215,19 @@ dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -213,19 +215,19 @@ dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
static inline int __attribute__((__unused__)) static inline int __attribute__((__unused__))
dbg_status_buf (char *buf, unsigned len, char *label, u32 status) dbg_status_buf (char *buf, unsigned len, char *label, u32 status)
{} { return 0; }
static inline int __attribute__((__unused__)) static inline int __attribute__((__unused__))
dbg_command_buf (char *buf, unsigned len, char *label, u32 command) dbg_command_buf (char *buf, unsigned len, char *label, u32 command)
{} { return 0; }
static inline int __attribute__((__unused__)) static inline int __attribute__((__unused__))
dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable) dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable)
{} { return 0; }
static inline int __attribute__((__unused__)) static inline int __attribute__((__unused__))
dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status) dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status)
{} { return 0; }
#endif /* DEBUG */ #endif /* DEBUG */
...@@ -248,7 +250,16 @@ dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status) ...@@ -248,7 +250,16 @@ dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status)
dbg ("%s", _buf); \ dbg ("%s", _buf); \
} }
#ifdef DEBUG /*-------------------------------------------------------------------------*/
#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; \ #define speed_char(info1) ({ char tmp; \
switch (info1 & (3 << 12)) { \ switch (info1 & (3 << 12)) { \
...@@ -258,6 +269,49 @@ dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status) ...@@ -258,6 +269,49 @@ dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status)
default: tmp = '?'; break; \ default: tmp = '?'; break; \
}; tmp; }) }; 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 static ssize_t
show_async (struct device *dev, char *buf, size_t count, loff_t off) show_async (struct device *dev, char *buf, size_t count, loff_t off)
{ {
...@@ -284,49 +338,21 @@ show_async (struct device *dev, char *buf, size_t count, loff_t off) ...@@ -284,49 +338,21 @@ show_async (struct device *dev, char *buf, size_t count, loff_t off)
if (ehci->async) { if (ehci->async) {
qh = ehci->async; qh = ehci->async;
do { do {
u32 scratch; qh_lines (qh, &next, &size);
struct list_head *entry; } while ((qh = qh->qh_next.qh) != ehci->async);
struct ehci_qtd *td;
scratch = cpu_to_le32p (&qh->hw_info1);
temp = snprintf (next, size, "qh %p dev%d %cs ep%d",
qh, scratch & 0x007f,
speed_char (scratch),
(scratch >> 8) & 0x000f);
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,
", td %p len=%d tok %04x %s",
td, scratch >> 16,
scratch & 0xffff,
({ 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;})
);
size -= temp;
next += temp;
} }
if (ehci->reclaim) {
temp = snprintf (next, size, "\n"); temp = snprintf (next, size, "\nreclaim =\n");
size -= temp; size -= temp;
next += temp; next += temp;
} while ((qh = qh->qh_next.qh) != ehci->async); qh_lines (ehci->reclaim, &next, &size);
} }
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
return count - size; return count - size;
} }
static DEVICE_ATTR (async, S_IRUSR, show_async, NULL); static DEVICE_ATTR (async, S_IRUGO, show_async, NULL);
#define DBG_SCHED_LIMIT 64 #define DBG_SCHED_LIMIT 64
...@@ -373,7 +399,7 @@ show_periodic (struct device *dev, char *buf, size_t count, loff_t off) ...@@ -373,7 +399,7 @@ show_periodic (struct device *dev, char *buf, size_t count, loff_t off)
do { do {
switch (tag) { switch (tag) {
case Q_TYPE_QH: case Q_TYPE_QH:
temp = snprintf (next, size, " intr-%d %p", temp = snprintf (next, size, " qh%d/%p",
p.qh->period, p.qh); p.qh->period, p.qh);
size -= temp; size -= temp;
next += temp; next += temp;
...@@ -387,12 +413,14 @@ show_periodic (struct device *dev, char *buf, size_t count, loff_t off) ...@@ -387,12 +413,14 @@ show_periodic (struct device *dev, char *buf, size_t count, loff_t off)
&p.qh->hw_info1); &p.qh->hw_info1);
temp = snprintf (next, size, temp = snprintf (next, size,
" (%cs dev%d ep%d)", " (%cs dev%d ep%d [%d/%d] %d)",
speed_char (scratch), speed_char (scratch),
scratch & 0x007f, scratch & 0x007f,
(scratch >> 8) & 0x000f); (scratch >> 8) & 0x000f,
p.qh->usecs, p.qh->c_usecs,
scratch >> 16);
/* FIXME TDs too */ /* FIXME TD info too */
if (seen_count < DBG_SCHED_LIMIT) if (seen_count < DBG_SCHED_LIMIT)
seen [seen_count++].qh = p.qh; seen [seen_count++].qh = p.qh;
...@@ -434,7 +462,7 @@ show_periodic (struct device *dev, char *buf, size_t count, loff_t off) ...@@ -434,7 +462,7 @@ show_periodic (struct device *dev, char *buf, size_t count, loff_t off)
return count - size; return count - size;
} }
static DEVICE_ATTR (periodic, S_IRUSR, show_periodic, NULL); static DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL);
#undef DBG_SCHED_LIMIT #undef DBG_SCHED_LIMIT
...@@ -522,7 +550,7 @@ show_registers (struct device *dev, char *buf, size_t count, loff_t off) ...@@ -522,7 +550,7 @@ show_registers (struct device *dev, char *buf, size_t count, loff_t off)
return count - size; return count - size;
} }
static DEVICE_ATTR (registers, S_IRUSR, show_registers, NULL); static DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
static inline void create_debug_files (struct ehci_hcd *bus) static inline void create_debug_files (struct ehci_hcd *bus)
{ {
...@@ -538,14 +566,5 @@ static inline void remove_debug_files (struct ehci_hcd *bus) ...@@ -538,14 +566,5 @@ static inline void remove_debug_files (struct ehci_hcd *bus)
device_remove_file (&bus->hcd.pdev->dev, &dev_attr_registers); device_remove_file (&bus->hcd.pdev->dev, &dev_attr_registers);
} }
#else /* DEBUG */ #endif /* STUB_DEBUG_FILES */
static inline void create_debug_files (struct ehci_hcd *bus)
{
}
static inline void remove_debug_files (struct ehci_hcd *bus)
{
}
#endif /* DEBUG */
...@@ -38,7 +38,12 @@ ...@@ -38,7 +38,12 @@
#endif #endif
#include <linux/usb.h> #include <linux/usb.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32)
#include "../hcd.h"
#else
#include "../core/hcd.h" #include "../core/hcd.h"
#endif
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -87,7 +92,7 @@ ...@@ -87,7 +92,7 @@
* 2001-June Works with usb-storage and NEC EHCI on 2.4 * 2001-June Works with usb-storage and NEC EHCI on 2.4
*/ */
#define DRIVER_VERSION "2002-Aug-06" #define DRIVER_VERSION "2002-Aug-28"
#define DRIVER_AUTHOR "David Brownell" #define DRIVER_AUTHOR "David Brownell"
#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
...@@ -104,6 +109,8 @@ ...@@ -104,6 +109,8 @@
#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ #define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
#define EHCI_TUNE_MULT_TT 1 #define EHCI_TUNE_MULT_TT 1
#define EHCI_WATCHDOG_JIFFIES (HZ/100) /* arbitrary; ~10 msec */
/* Initial IRQ latency: lower than default */ /* Initial IRQ latency: lower than default */
static int log2_irq_thresh = 0; // 0 to 6 static int log2_irq_thresh = 0; // 0 to 6
MODULE_PARM (log2_irq_thresh, "i"); MODULE_PARM (log2_irq_thresh, "i");
...@@ -232,6 +239,19 @@ static void ehci_ready (struct ehci_hcd *ehci) ...@@ -232,6 +239,19 @@ static void ehci_ready (struct ehci_hcd *ehci)
static void ehci_tasklet (unsigned long param); 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;
/* guard against lost IAA, which wedges everything */
spin_lock_irqsave (&ehci->lock, flags);
ehci_irq (&ehci->hcd);
spin_unlock_irqrestore (&ehci->lock, flags);
}
/* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/... /* 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). * off the controller (maybe it can boot from highspeed USB disks).
*/ */
...@@ -267,6 +287,7 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -267,6 +287,7 @@ static int ehci_start (struct usb_hcd *hcd)
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 temp; u32 temp;
struct usb_device *udev; struct usb_device *udev;
struct usb_bus *bus;
int retval; int retval;
u32 hcc_params; u32 hcc_params;
u8 tempbyte; u8 tempbyte;
...@@ -372,16 +393,19 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -372,16 +393,19 @@ static int ehci_start (struct usb_hcd *hcd)
ehci->tasklet.func = ehci_tasklet; ehci->tasklet.func = ehci_tasklet;
ehci->tasklet.data = (unsigned long) ehci; 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 */ /* wire up the root hub */
hcd->self.root_hub = udev = usb_alloc_dev (NULL, &hcd->self); bus = hcd_to_bus (hcd);
bus->root_hub = udev = usb_alloc_dev (NULL, bus);
if (!udev) { if (!udev) {
done2: done2:
ehci_mem_cleanup (ehci); ehci_mem_cleanup (ehci);
return -ENOMEM; return -ENOMEM;
} }
create_debug_files (ehci);
/* /*
* Start, enabling full USB 2.0 functionality ... usb 1.1 devices * Start, enabling full USB 2.0 functionality ... usb 1.1 devices
* are explicitly handed to companion controller(s), so no TT is * are explicitly handed to companion controller(s), so no TT is
...@@ -394,7 +418,7 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -394,7 +418,7 @@ static int ehci_start (struct usb_hcd *hcd)
/* PCI Serial Bus Release Number is at 0x60 offset */ /* PCI Serial Bus Release Number is at 0x60 offset */
pci_read_config_byte (hcd->pdev, 0x60, &tempbyte); pci_read_config_byte (hcd->pdev, 0x60, &tempbyte);
temp = readw (&ehci->caps->hci_version); temp = readw (&ehci->caps->hci_version);
info ("USB %x.%x support enabled, EHCI rev %x.%2x", info ("USB %x.%x support enabled, EHCI rev %x.%02x",
((tempbyte & 0xf0)>>4), ((tempbyte & 0xf0)>>4),
(tempbyte & 0x0f), (tempbyte & 0x0f),
temp >> 8, temp >> 8,
...@@ -409,16 +433,22 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -409,16 +433,22 @@ static int ehci_start (struct usb_hcd *hcd)
*/ */
usb_connect (udev); usb_connect (udev);
udev->speed = USB_SPEED_HIGH; udev->speed = USB_SPEED_HIGH;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32)
if (usb_new_device (udev) != 0) {
#else
if (usb_register_root_hub (udev, &ehci->hcd.pdev->dev) != 0) { if (usb_register_root_hub (udev, &ehci->hcd.pdev->dev) != 0) {
#endif
if (hcd->state == USB_STATE_RUNNING) if (hcd->state == USB_STATE_RUNNING)
ehci_ready (ehci); ehci_ready (ehci);
ehci_reset (ehci); ehci_reset (ehci);
hcd->self.root_hub = 0; bus->root_hub = 0;
usb_free_dev (udev); usb_free_dev (udev);
retval = -ENODEV; retval = -ENODEV;
goto done2; goto done2;
} }
create_debug_files (ehci);
return 0; return 0;
} }
...@@ -428,13 +458,20 @@ static void ehci_stop (struct usb_hcd *hcd) ...@@ -428,13 +458,20 @@ static void ehci_stop (struct usb_hcd *hcd)
{ {
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
dbg ("%s: stop", hcd->self.bus_name); dbg ("%s: stop", hcd_to_bus (hcd)->bus_name);
/* no more interrupts ... */ /* no more interrupts ... */
if (hcd->state == USB_STATE_RUNNING) if (hcd->state == USB_STATE_RUNNING)
ehci_ready (ehci); ehci_ready (ehci);
if (in_interrupt ()) /* should not happen!! */
err ("stopped %s!", RUN_CONTEXT);
else
del_timer_sync (&ehci->watchdog);
ehci_reset (ehci); ehci_reset (ehci);
/* let companion controllers work when we aren't */
writel (0, &ehci->regs->configured_flag);
remove_debug_files (ehci); remove_debug_files (ehci);
/* root hub is shut down separately (first, when possible) */ /* root hub is shut down separately (first, when possible) */
...@@ -463,7 +500,7 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state) ...@@ -463,7 +500,7 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state)
int ports; int ports;
int i; int i;
dbg ("%s: suspend to %d", hcd->self.bus_name, state); dbg ("%s: suspend to %d", hcd_to_bus (hcd)->bus_name, state);
ports = HCS_N_PORTS (ehci->hcs_params); ports = HCS_N_PORTS (ehci->hcs_params);
...@@ -480,7 +517,7 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state) ...@@ -480,7 +517,7 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state)
if ((temp & PORT_PE) == 0 if ((temp & PORT_PE) == 0
|| (temp & PORT_OWNER) != 0) || (temp & PORT_OWNER) != 0)
continue; continue;
dbg ("%s: suspend port %d", hcd->self.bus_name, i); dbg ("%s: suspend port %d", hcd_to_bus (hcd)->bus_name, i);
temp |= PORT_SUSPEND; temp |= PORT_SUSPEND;
writel (temp, &ehci->regs->port_status [i]); writel (temp, &ehci->regs->port_status [i]);
} }
...@@ -502,7 +539,7 @@ static int ehci_resume (struct usb_hcd *hcd) ...@@ -502,7 +539,7 @@ static int ehci_resume (struct usb_hcd *hcd)
int ports; int ports;
int i; int i;
dbg ("%s: resume", hcd->self.bus_name); dbg ("%s: resume", hcd_to_bus (hcd)->bus_name);
ports = HCS_N_PORTS (ehci->hcs_params); ports = HCS_N_PORTS (ehci->hcs_params);
...@@ -522,7 +559,7 @@ static int ehci_resume (struct usb_hcd *hcd) ...@@ -522,7 +559,7 @@ static int ehci_resume (struct usb_hcd *hcd)
if ((temp & PORT_PE) == 0 if ((temp & PORT_PE) == 0
|| (temp & PORT_SUSPEND) != 0) || (temp & PORT_SUSPEND) != 0)
continue; continue;
dbg ("%s: resume port %d", hcd->self.bus_name, i); dbg ("%s: resume port %d", hcd_to_bus (hcd)->bus_name, i);
temp |= PORT_RESUME; temp |= PORT_RESUME;
writel (temp, &ehci->regs->port_status [i]); writel (temp, &ehci->regs->port_status [i]);
readl (&ehci->regs->command); /* unblock posted writes */ readl (&ehci->regs->command); /* unblock posted writes */
...@@ -546,12 +583,17 @@ dbg ("%s: resume port %d", hcd->self.bus_name, i); ...@@ -546,12 +583,17 @@ dbg ("%s: resume port %d", hcd->self.bus_name, i);
static void ehci_tasklet (unsigned long param) static void ehci_tasklet (unsigned long param)
{ {
struct ehci_hcd *ehci = (struct ehci_hcd *) param; struct ehci_hcd *ehci = (struct ehci_hcd *) param;
unsigned long flags;
spin_lock_irqsave (&ehci->lock, flags);
if (ehci->reclaim_ready) if (ehci->reclaim_ready)
end_unlink_async (ehci); flags = end_unlink_async (ehci, flags);
scan_async (ehci); flags = scan_async (ehci, flags);
if (ehci->next_uframe != -1) if (ehci->next_uframe != -1)
scan_periodic (ehci); flags = scan_periodic (ehci, flags);
spin_unlock_irqrestore (&ehci->lock, flags);
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -564,7 +606,7 @@ static void ehci_irq (struct usb_hcd *hcd) ...@@ -564,7 +606,7 @@ static void ehci_irq (struct usb_hcd *hcd)
/* e.g. cardbus physical eject */ /* e.g. cardbus physical eject */
if (status == ~(u32) 0) { if (status == ~(u32) 0) {
dbg ("%s: device removed!", hcd->self.bus_name); dbg ("%s: device removed!", hcd_to_bus (hcd)->bus_name);
goto dead; goto dead;
} }
...@@ -597,7 +639,7 @@ static void ehci_irq (struct usb_hcd *hcd) ...@@ -597,7 +639,7 @@ static void ehci_irq (struct usb_hcd *hcd)
/* PCI errors [4.15.2.4] */ /* PCI errors [4.15.2.4] */
if (unlikely ((status & STS_FATAL) != 0)) { if (unlikely ((status & STS_FATAL) != 0)) {
err ("%s: fatal error, state %x", err ("%s: fatal error, state %x",
hcd->self.bus_name, hcd->state); hcd_to_bus (hcd)->bus_name, hcd->state);
dead: dead:
ehci_reset (ehci); ehci_reset (ehci);
/* generic layer kills/unlinks all urbs, then /* generic layer kills/unlinks all urbs, then
...@@ -673,7 +715,7 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) ...@@ -673,7 +715,7 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
unsigned long flags; unsigned long flags;
dbg ("%s urb_dequeue %p qh %p state %d", dbg ("%s urb_dequeue %p qh %p state %d",
hcd->self.bus_name, urb, qh, qh->qh_state); hcd_to_bus (hcd)->bus_name, urb, qh, qh->qh_state);
switch (usb_pipetype (urb->pipe)) { switch (usb_pipetype (urb->pipe)) {
// case PIPE_CONTROL: // case PIPE_CONTROL:
...@@ -681,7 +723,13 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) ...@@ -681,7 +723,13 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
default: default:
spin_lock_irqsave (&ehci->lock, flags); spin_lock_irqsave (&ehci->lock, flags);
if (ehci->reclaim) { 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 ()) { if (in_interrupt ()) {
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
return -EAGAIN; return -EAGAIN;
...@@ -702,19 +750,19 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) ...@@ -702,19 +750,19 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
break; break;
case PIPE_INTERRUPT: case PIPE_INTERRUPT:
spin_lock_irqsave (&ehci->lock, flags);
if (qh->qh_state == QH_STATE_LINKED) { if (qh->qh_state == QH_STATE_LINKED) {
/* messy, can spin or block a microframe ... */ /* messy, can spin or block a microframe ... */
intr_deschedule (ehci, qh, 1); flags = intr_deschedule (ehci, qh, 1, flags);
/* qh_state == IDLE */ /* qh_state == IDLE */
} }
qh_completions (ehci, qh); flags = qh_completions (ehci, qh, flags);
/* reschedule QH iff another request is queued */ /* reschedule QH iff another request is queued */
if (!list_empty (&qh->qtd_list) if (!list_empty (&qh->qtd_list)
&& HCD_IS_RUNNING (ehci->hcd.state)) { && HCD_IS_RUNNING (ehci->hcd.state)) {
int status; int status;
spin_lock_irqsave (&ehci->lock, flags);
status = qh_schedule (ehci, qh); status = qh_schedule (ehci, qh);
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
...@@ -726,7 +774,7 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) ...@@ -726,7 +774,7 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
} }
return status; return status;
} }
spin_unlock_irqrestore (&ehci->lock, flags);
break; break;
case PIPE_ISOCHRONOUS: case PIPE_ISOCHRONOUS:
...@@ -754,7 +802,8 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev) ...@@ -754,7 +802,8 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
/* ASSERT: no requests/urbs are still linked (so no TDs) */ /* ASSERT: no requests/urbs are still linked (so no TDs) */
/* ASSERT: nobody can be submitting urbs for this any more */ /* ASSERT: nobody can be submitting urbs for this any more */
dbg ("%s: free_config devnum %d", hcd->self.bus_name, udev->devnum); dbg ("%s: free_config devnum %d",
hcd_to_bus (hcd)->bus_name, udev->devnum);
spin_lock_irqsave (&ehci->lock, flags); spin_lock_irqsave (&ehci->lock, flags);
for (i = 0; i < 32; i++) { for (i = 0; i < 32; i++) {
...@@ -775,7 +824,8 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev) ...@@ -775,7 +824,8 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
why = 0; why = 0;
if (why) { if (why) {
err ("dev %s-%s ep %d-%s error: %s", err ("dev %s-%s ep %d-%s error: %s",
hcd->self.bus_name, udev->devpath, hcd_to_bus (hcd)->bus_name,
udev->devpath,
i & 0xf, (i & 0x10) ? "IN" : "OUT", i & 0xf, (i & 0x10) ? "IN" : "OUT",
why); why);
BUG (); BUG ();
...@@ -805,8 +855,7 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev) ...@@ -805,8 +855,7 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
start_unlink_async (ehci, qh); start_unlink_async (ehci, qh);
while (qh->qh_state != QH_STATE_IDLE while (qh->qh_state != QH_STATE_IDLE
&& ehci->hcd.state != USB_STATE_HALT) { && ehci->hcd.state != USB_STATE_HALT) {
spin_unlock_irqrestore (&ehci->lock, spin_unlock_irqrestore (&ehci->lock, flags);
flags);
wait_ms (1); wait_ms (1);
spin_lock_irqsave (&ehci->lock, flags); spin_lock_irqsave (&ehci->lock, flags);
} }
......
...@@ -41,14 +41,17 @@ static int check_reset_complete ( ...@@ -41,14 +41,17 @@ static int check_reset_complete (
/* if reset finished and it's still not enabled -- handoff */ /* if reset finished and it's still not enabled -- handoff */
if (!(port_status & PORT_PE)) { if (!(port_status & PORT_PE)) {
dbg ("%s port %d full speed, give to companion, 0x%x", dbg ("%s port %d full speed, give to companion, 0x%x",
ehci->hcd.self.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 ? // what happens if HCS_N_CC(params) == 0 ?
port_status |= PORT_OWNER; port_status |= PORT_OWNER;
writel (port_status, &ehci->regs->port_status [index]); writel (port_status, &ehci->regs->port_status [index]);
} else } else
dbg ("%s port %d high speed", ehci->hcd.self.bus_name, index + 1); dbg ("%s port %d high speed",
hcd_to_bus (&ehci->hcd)->bus_name,
index + 1);
return port_status; return port_status;
} }
...@@ -310,11 +313,13 @@ static int ehci_hub_control ( ...@@ -310,11 +313,13 @@ static int ehci_hub_control (
if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
&& PORT_USB11 (temp)) { && PORT_USB11 (temp)) {
dbg ("%s port %d low speed, give to companion", dbg ("%s port %d low speed, give to companion",
hcd->self.bus_name, wIndex + 1); hcd_to_bus (&ehci->hcd)->bus_name,
wIndex + 1);
temp |= PORT_OWNER; temp |= PORT_OWNER;
} else { } else {
vdbg ("%s port %d reset", vdbg ("%s port %d reset",
hcd->self.bus_name, wIndex + 1); hcd_to_bus (&ehci->hcd)->bus_name,
wIndex + 1);
temp |= PORT_RESET; temp |= PORT_RESET;
temp &= ~PORT_PE; temp &= ~PORT_PE;
......
...@@ -161,9 +161,10 @@ static inline void qtd_copy_status (struct urb *urb, size_t length, u32 token) ...@@ -161,9 +161,10 @@ static inline void qtd_copy_status (struct urb *urb, size_t length, u32 token)
/* urb->lock ignored from here on (hcd is done with urb) */ /* urb->lock ignored from here on (hcd is done with urb) */
static void ehci_urb_done ( static unsigned long ehci_urb_done (
struct ehci_hcd *ehci, struct ehci_hcd *ehci,
struct urb *urb struct urb *urb,
unsigned long flags
) { ) {
#ifdef INTR_AUTOMAGIC #ifdef INTR_AUTOMAGIC
struct urb *resubmit = 0; struct urb *resubmit = 0;
...@@ -177,7 +178,7 @@ static void ehci_urb_done ( ...@@ -177,7 +178,7 @@ static void ehci_urb_done (
if ((qh->hw_info2 & cpu_to_le32 (0x00ff)) != 0) { if ((qh->hw_info2 & cpu_to_le32 (0x00ff)) != 0) {
/* ... update hc-wide periodic stats (for usbfs) */ /* ... update hc-wide periodic stats (for usbfs) */
ehci->hcd.self.bandwidth_int_reqs--; hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs--;
#ifdef INTR_AUTOMAGIC #ifdef INTR_AUTOMAGIC
if (!((urb->status == -ENOENT) if (!((urb->status == -ENOENT)
...@@ -199,6 +200,8 @@ static void ehci_urb_done ( ...@@ -199,6 +200,8 @@ static void ehci_urb_done (
urb->status = 0; urb->status = 0;
} }
/* complete() can reenter this HCD */
spin_unlock_irqrestore (&ehci->lock, flags);
usb_hcd_giveback_urb (&ehci->hcd, urb); usb_hcd_giveback_urb (&ehci->hcd, urb);
#ifdef INTR_AUTOMAGIC #ifdef INTR_AUTOMAGIC
...@@ -212,34 +215,32 @@ static void ehci_urb_done ( ...@@ -212,34 +215,32 @@ static void ehci_urb_done (
int status; int status;
resubmit->dev = dev; resubmit->dev = dev;
status = usb_submit_urb (resubmit, SLAB_KERNEL); status = SUBMIT_URB (resubmit, SLAB_KERNEL);
if (status != 0) if (status != 0)
err ("can't resubmit interrupt urb %p: status %d", err ("can't resubmit interrupt urb %p: status %d",
resubmit, status); resubmit, status);
usb_put_urb (resubmit); usb_put_urb (resubmit);
} }
#endif #endif
spin_lock_irqsave (&ehci->lock, flags);
return flags;
} }
/* /*
* Process completed qtds for a qh, issuing completions if needed. * Process and free completed qtds for a qh, returning URBs to drivers.
* Frees qtds, unmaps buf, returns URB to driver. * Chases up to qh->hw_current, returns irqsave flags (maybe modified).
* Races up to qh->hw_current; returns number of urb completions.
*/ */
static void static unsigned long
qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, unsigned long flags)
{ {
struct ehci_qtd *qtd, *last; struct ehci_qtd *qtd, *last;
struct list_head *next, *qtd_list = &qh->qtd_list; struct list_head *next, *qtd_list = &qh->qtd_list;
int unlink = 0, halted = 0; int unlink = 0, halted = 0;
unsigned long flags;
spin_lock_irqsave (&ehci->lock, flags); if (unlikely (list_empty (qtd_list)))
if (unlikely (list_empty (qtd_list))) { return flags;
spin_unlock_irqrestore (&ehci->lock, flags);
return;
}
/* scan QTDs till end of list, or we reach an active one */ /* scan QTDs till end of list, or we reach an active one */
for (qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list), for (qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list),
...@@ -252,12 +253,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -252,12 +253,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
/* clean up any state from previous QTD ...*/ /* clean up any state from previous QTD ...*/
if (last) { if (last) {
if (likely (last->urb != urb)) { if (likely (last->urb != urb))
/* complete() can reenter this HCD */ flags = ehci_urb_done (ehci, last->urb, flags);
spin_unlock_irqrestore (&ehci->lock, flags);
ehci_urb_done (ehci, last->urb);
spin_lock_irqsave (&ehci->lock, flags);
}
/* qh overlays can have HC's old cached copies of /* qh overlays can have HC's old cached copies of
* next qtd ptrs, if an URB was queued afterwards. * next qtd ptrs, if an URB was queued afterwards.
...@@ -283,6 +280,9 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -283,6 +280,9 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|| (ehci->hcd.state == USB_STATE_HALT) || (ehci->hcd.state == USB_STATE_HALT)
|| (qh->qh_state == QH_STATE_IDLE); || (qh->qh_state == QH_STATE_IDLE);
// FIXME Remove the automagic unlink mode.
// Drivers can now clean up safely; its' their job.
/* fault: unlink the rest, since this qtd saw an error? */ /* fault: unlink the rest, since this qtd saw an error? */
if (unlikely ((token & QTD_STS_HALT) != 0)) { if (unlikely ((token & QTD_STS_HALT) != 0)) {
unlink = 1; unlink = 1;
...@@ -341,18 +341,19 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -341,18 +341,19 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
#endif #endif
} }
/* patch up list head? */ /* last urb's completion might still need calling */
if (likely (last != 0)) {
flags = ehci_urb_done (ehci, last->urb, flags);
ehci_qtd_free (ehci, last);
}
/* reactivate queue after error and driver's cleanup */
if (unlikely (halted && !list_empty (qtd_list))) { if (unlikely (halted && !list_empty (qtd_list))) {
qh_update (qh, list_entry (qtd_list->next, qh_update (qh, list_entry (qtd_list->next,
struct ehci_qtd, qtd_list)); struct ehci_qtd, qtd_list));
} }
spin_unlock_irqrestore (&ehci->lock, flags);
/* last urb's completion might still need calling */ return flags;
if (likely (last != 0)) {
ehci_urb_done (ehci, last->urb);
ehci_qtd_free (ehci, last);
}
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -367,31 +368,12 @@ static void qtd_list_free ( ...@@ -367,31 +368,12 @@ static void qtd_list_free (
struct list_head *qtd_list struct list_head *qtd_list
) { ) {
struct list_head *entry, *temp; struct list_head *entry, *temp;
int unmapped = 0;
list_for_each_safe (entry, temp, qtd_list) { list_for_each_safe (entry, temp, qtd_list) {
struct ehci_qtd *qtd; struct ehci_qtd *qtd;
qtd = list_entry (entry, struct ehci_qtd, qtd_list); qtd = list_entry (entry, struct ehci_qtd, qtd_list);
list_del (&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++;
}
}
ehci_qtd_free (ehci, qtd); ehci_qtd_free (ehci, qtd);
} }
} }
...@@ -670,8 +652,8 @@ ehci_qh_make ( ...@@ -670,8 +652,8 @@ ehci_qh_make (
info2 |= hb_mult (maxp) << 30; info2 |= hb_mult (maxp) << 30;
} }
break; break;
#ifdef DEBUG
default: default:
#ifdef DEBUG
BUG (); BUG ();
#endif #endif
} }
...@@ -859,7 +841,8 @@ submit_async ( ...@@ -859,7 +841,8 @@ submit_async (
epnum |= 0x10; epnum |= 0x10;
vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]", vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]",
ehci->hcd.self.bus_name, urb, urb->transfer_buffer_length, hcd_to_bus (&ehci->hcd)->bus_name,
urb, urb->transfer_buffer_length,
epnum & 0x0f, (epnum & 0x10) ? "in" : "out", epnum & 0x0f, (epnum & 0x10) ? "in" : "out",
qtd, dev ? dev->ep [epnum] : (void *)~0); qtd, dev ? dev->ep [epnum] : (void *)~0);
...@@ -886,25 +869,28 @@ submit_async ( ...@@ -886,25 +869,28 @@ submit_async (
/* the async qh for the qtds being reclaimed are now unlinked from the HC */ /* the async qh for the qtds being reclaimed are now unlinked from the HC */
/* caller must not own ehci->lock */ /* caller must not own ehci->lock */
static void end_unlink_async (struct ehci_hcd *ehci) static unsigned long
end_unlink_async (struct ehci_hcd *ehci, unsigned long flags)
{ {
struct ehci_qh *qh = ehci->reclaim; struct ehci_qh *qh = ehci->reclaim;
del_timer (&ehci->watchdog);
qh->qh_state = QH_STATE_IDLE; qh->qh_state = QH_STATE_IDLE;
qh->qh_next.qh = 0; qh->qh_next.qh = 0;
qh_put (ehci, qh); // refcount from reclaim qh_put (ehci, qh); // refcount from reclaim
ehci->reclaim = 0; ehci->reclaim = 0;
ehci->reclaim_ready = 0; ehci->reclaim_ready = 0;
qh_completions (ehci, qh); flags = qh_completions (ehci, qh, flags);
// 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) if (!list_empty (&qh->qtd_list)
&& HCD_IS_RUNNING (ehci->hcd.state)) && HCD_IS_RUNNING (ehci->hcd.state))
qh_link_async (ehci, qh); qh_link_async (ehci, qh);
else else
qh_put (ehci, qh); // refcount from async list qh_put (ehci, qh); // refcount from async list
return flags;
} }
...@@ -975,16 +961,17 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -975,16 +961,17 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
cmd |= CMD_IAAD; cmd |= CMD_IAAD;
writel (cmd, &ehci->regs->command); writel (cmd, &ehci->regs->command);
/* posted write need not be known to HC yet ... */ /* 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 unsigned long
scan_async (struct ehci_hcd *ehci, unsigned long flags)
{ {
struct ehci_qh *qh; struct ehci_qh *qh;
unsigned long flags;
spin_lock_irqsave (&ehci->lock, flags);
rescan: rescan:
qh = ehci->async; qh = ehci->async;
if (likely (qh != 0)) { if (likely (qh != 0)) {
...@@ -993,12 +980,9 @@ static void scan_async (struct ehci_hcd *ehci) ...@@ -993,12 +980,9 @@ static void scan_async (struct ehci_hcd *ehci)
if (!list_empty (&qh->qtd_list)) { if (!list_empty (&qh->qtd_list)) {
// dbg_qh ("scan_async", ehci, qh); // dbg_qh ("scan_async", ehci, qh);
qh = qh_get (qh); qh = qh_get (qh);
spin_unlock_irqrestore (&ehci->lock, flags);
/* concurrent unlink could happen here */ /* concurrent unlink could happen here */
qh_completions (ehci, qh); flags = qh_completions (ehci, qh, flags);
spin_lock_irqsave (&ehci->lock, flags);
qh_put (ehci, qh); qh_put (ehci, qh);
} }
...@@ -1020,6 +1004,5 @@ static void scan_async (struct ehci_hcd *ehci) ...@@ -1020,6 +1004,5 @@ static void scan_async (struct ehci_hcd *ehci)
goto rescan; goto rescan;
} while (qh != ehci->async); } while (qh != ehci->async);
} }
return flags;
spin_unlock_irqrestore (&ehci->lock, flags);
} }
...@@ -222,17 +222,15 @@ static int disable_periodic (struct ehci_hcd *ehci) ...@@ -222,17 +222,15 @@ static int disable_periodic (struct ehci_hcd *ehci)
// FIXME microframe periods not yet handled // FIXME microframe periods not yet handled
static void intr_deschedule ( static unsigned long intr_deschedule (
struct ehci_hcd *ehci, struct ehci_hcd *ehci,
struct ehci_qh *qh, struct ehci_qh *qh,
int wait int wait,
unsigned long flags
) { ) {
unsigned long flags;
int status; int status;
unsigned frame = qh->start; unsigned frame = qh->start;
spin_lock_irqsave (&ehci->lock, flags);
do { do {
periodic_unlink (ehci, frame, qh); periodic_unlink (ehci, frame, qh);
qh_put (ehci, qh); qh_put (ehci, qh);
...@@ -251,8 +249,6 @@ static void intr_deschedule ( ...@@ -251,8 +249,6 @@ static void intr_deschedule (
vdbg ("periodic schedule still enabled"); vdbg ("periodic schedule still enabled");
} }
spin_unlock_irqrestore (&ehci->lock, flags);
/* /*
* If the hc may be looking at this qh, then delay a uframe * If the hc may be looking at this qh, then delay a uframe
* (yeech!) to be sure it's done. * (yeech!) to be sure it's done.
...@@ -260,8 +256,10 @@ static void intr_deschedule ( ...@@ -260,8 +256,10 @@ static void intr_deschedule (
*/ */
if (((ehci_get_frame (&ehci->hcd) - frame) % qh->period) == 0) { if (((ehci_get_frame (&ehci->hcd) - frame) % qh->period) == 0) {
if (wait) { if (wait) {
spin_unlock_irqrestore (&ehci->lock, flags);
udelay (125); udelay (125);
qh->hw_next = EHCI_LIST_END; qh->hw_next = EHCI_LIST_END;
spin_lock_irqsave (&ehci->lock, flags);
} else { } else {
/* we may not be IDLE yet, but if the qh is empty /* we may not be IDLE yet, but if the qh is empty
* the race is very short. then if qh also isn't * the race is very short. then if qh also isn't
...@@ -275,12 +273,13 @@ static void intr_deschedule ( ...@@ -275,12 +273,13 @@ static void intr_deschedule (
qh->qh_state = QH_STATE_IDLE; qh->qh_state = QH_STATE_IDLE;
/* update per-qh bandwidth utilization (for usbfs) */ /* update per-qh bandwidth utilization (for usbfs) */
ehci->hcd.self.bandwidth_allocated -= hcd_to_bus (&ehci->hcd)->bandwidth_allocated -=
(qh->usecs + qh->c_usecs) / qh->period; (qh->usecs + qh->c_usecs) / qh->period;
vdbg ("descheduled qh %p, per = %d frame = %d count = %d, urbs = %d", vdbg ("descheduled qh %p, per = %d frame = %d count = %d, urbs = %d",
qh, qh->period, frame, qh, qh->period, frame,
atomic_read (&qh->refcount), ehci->periodic_sched); atomic_read (&qh->refcount), ehci->periodic_sched);
return flags;
} }
static int check_period ( static int check_period (
...@@ -436,7 +435,7 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -436,7 +435,7 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
} while (frame < ehci->periodic_size); } while (frame < ehci->periodic_size);
/* update per-qh bandwidth for usbfs */ /* update per-qh bandwidth for usbfs */
ehci->hcd.self.bandwidth_allocated += hcd_to_bus (&ehci->hcd)->bandwidth_allocated +=
(qh->usecs + qh->c_usecs) / qh->period; (qh->usecs + qh->c_usecs) / qh->period;
/* maybe enable periodic schedule processing */ /* maybe enable periodic schedule processing */
...@@ -486,7 +485,7 @@ static int intr_submit ( ...@@ -486,7 +485,7 @@ static int intr_submit (
BUG_ON (qh == 0); BUG_ON (qh == 0);
/* ... update usbfs periodic stats */ /* ... update usbfs periodic stats */
ehci->hcd.self.bandwidth_int_reqs++; hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs++;
done: done:
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
...@@ -513,12 +512,10 @@ intr_complete ( ...@@ -513,12 +512,10 @@ intr_complete (
} }
/* handle any completions */ /* handle any completions */
spin_unlock_irqrestore (&ehci->lock, flags); flags = qh_completions (ehci, qh, flags);
qh_completions (ehci, qh);
spin_lock_irqsave (&ehci->lock, flags);
if (unlikely (list_empty (&qh->qtd_list))) if (unlikely (list_empty (&qh->qtd_list)))
intr_deschedule (ehci, qh, 0); flags = intr_deschedule (ehci, qh, 0, flags);
return flags; return flags;
} }
...@@ -1091,13 +1088,12 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) ...@@ -1091,13 +1088,12 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static void scan_periodic (struct ehci_hcd *ehci) static unsigned long
scan_periodic (struct ehci_hcd *ehci, unsigned long flags)
{ {
unsigned frame, clock, now_uframe, mod; unsigned frame, clock, now_uframe, mod;
unsigned long flags;
mod = ehci->periodic_size << 3; mod = ehci->periodic_size << 3;
spin_lock_irqsave (&ehci->lock, flags);
/* /*
* When running, scan from last scan point up to "now" * When running, scan from last scan point up to "now"
...@@ -1237,5 +1233,5 @@ static void scan_periodic (struct ehci_hcd *ehci) ...@@ -1237,5 +1233,5 @@ static void scan_periodic (struct ehci_hcd *ehci)
} else } else
frame = (frame + 1) % ehci->periodic_size; frame = (frame + 1) % ehci->periodic_size;
} }
spin_unlock_irqrestore (&ehci->lock, flags); return flags;
} }
...@@ -69,6 +69,8 @@ struct ehci_hcd { /* one per controller */ ...@@ -69,6 +69,8 @@ struct ehci_hcd { /* one per controller */
struct pci_pool *qtd_pool; /* one or more per qh */ struct pci_pool *qtd_pool; /* one or more per qh */
struct pci_pool *itd_pool; /* itd per iso urb */ struct pci_pool *itd_pool; /* itd per iso urb */
struct pci_pool *sitd_pool; /* sitd per split iso urb */ struct pci_pool *sitd_pool; /* sitd per split iso urb */
struct timer_list watchdog;
}; };
/* unwrap an HCD pointer to get an EHCI_HCD pointer */ /* unwrap an HCD pointer to get an EHCI_HCD pointer */
...@@ -389,4 +391,26 @@ struct ehci_fstn { ...@@ -389,4 +391,26 @@ struct ehci_fstn {
union ehci_shadow fstn_next; /* ptr to periodic q entry */ union ehci_shadow fstn_next; /* ptr to periodic q entry */
} __attribute__ ((aligned (32))); } __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32)
#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb)
#define STUB_DEBUG_FILES
#else /* LINUX_VERSION_CODE */
static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd)
{ return &hcd->self; }
#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 */ #endif /* __LINUX_EHCI_HCD_H */
...@@ -72,37 +72,6 @@ static void urb_print (struct urb * urb, char * str, int small) ...@@ -72,37 +72,6 @@ static void urb_print (struct urb * urb, char * str, int small)
#endif #endif
} }
static inline struct ed *
dma_to_ed (struct ohci_hcd *hc, dma_addr_t ed_dma);
/* print non-empty branches of the periodic ed tree */
static void __attribute__ ((unused))
ohci_dump_periodic (struct ohci_hcd *ohci, char *label)
{
int i, j;
u32 *ed_p;
int printed = 0;
for (i= 0; i < 32; i++) {
j = 5;
ed_p = &(ohci->hcca->int_table [i]);
if (*ed_p == 0)
continue;
printed = 1;
printk (KERN_DEBUG "%s, ohci %s frame %2d:",
label, ohci->hcd.self.bus_name, i);
while (*ed_p != 0 && j--) {
struct ed *ed = dma_to_ed (ohci, le32_to_cpup(ed_p));
printk (" %p/%08x;", ed, ed->hwINFO);
ed_p = &ed->hwNextED;
}
printk ("\n");
}
if (!printed)
printk (KERN_DEBUG "%s, ohci %s, empty periodic schedule\n",
label, ohci->hcd.self.bus_name);
}
static void ohci_dump_intr_mask (char *label, __u32 mask) static void ohci_dump_intr_mask (char *label, __u32 mask)
{ {
dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s",
...@@ -239,6 +208,7 @@ static void ohci_dump (struct ohci_hcd *controller, int verbose) ...@@ -239,6 +208,7 @@ static void ohci_dump (struct ohci_hcd *controller, int verbose)
if (verbose) if (verbose)
ohci_dump_periodic (controller, "hcca"); ohci_dump_periodic (controller, "hcca");
#endif #endif
if (controller->hcca)
dbg ("hcca frame #%04x", controller->hcca->frame_no); dbg ("hcca frame #%04x", controller->hcca->frame_no);
ohci_dump_roothub (controller, 1); ohci_dump_roothub (controller, 1);
} }
...@@ -316,8 +286,9 @@ ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose) ...@@ -316,8 +286,9 @@ ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose)
case ED_IN: type = "-IN"; break; case ED_IN: type = "-IN"; break;
/* else from TDs ... control */ /* else from TDs ... control */
} }
dbg (" info %08x MAX=%d%s%s%s EP=%d%s DEV=%d", le32_to_cpu (tmp), dbg (" info %08x MAX=%d%s%s%s%s EP=%d%s DEV=%d", le32_to_cpu (tmp),
0x0fff & (le32_to_cpu (tmp) >> 16), 0x03ff & (le32_to_cpu (tmp) >> 16),
(tmp & ED_DEQUEUE) ? " DQ" : "",
(tmp & ED_ISO) ? " ISO" : "", (tmp & ED_ISO) ? " ISO" : "",
(tmp & ED_SKIP) ? " SKIP" : "", (tmp & ED_SKIP) ? " SKIP" : "",
(tmp & ED_LOWSPEED) ? " LOW" : "", (tmp & ED_LOWSPEED) ? " LOW" : "",
...@@ -344,5 +315,222 @@ ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose) ...@@ -344,5 +315,222 @@ ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose)
} }
} }
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,32)
# define DRIVERFS_DEBUG_FILES
#endif #endif
#endif /* DEBUG */
/*-------------------------------------------------------------------------*/
#ifdef DRIVERFS_DEBUG_FILES
static ssize_t
show_list (struct ohci_hcd *ohci, char *buf, size_t count, struct ed *ed)
{
unsigned temp, size = count;
if (!ed)
return 0;
/* print first --> last */
while (ed->ed_prev)
ed = ed->ed_prev;
/* dump a snapshot of the bulk or control schedule */
while (ed) {
u32 info = ed->hwINFO;
u32 scratch = cpu_to_le32p (&ed->hwINFO);
struct list_head *entry;
struct td *td;
temp = snprintf (buf, size,
"ed/%p %cs dev%d ep%d-%s max %d %08x%s%s %s",
ed,
(info & ED_LOWSPEED) ? 'l' : 'f',
scratch & 0x7f,
(scratch >> 7) & 0xf,
(info & ED_IN) ? "in" : "out",
0x03ff & (scratch >> 16),
scratch,
(info & ED_SKIP) ? " s" : "",
(ed->hwHeadP & ED_H) ? " H" : "",
(ed->hwHeadP & ED_C) ? data1 : data0);
size -= temp;
buf += temp;
list_for_each (entry, &ed->td_list) {
u32 cbp, be;
td = list_entry (entry, struct td, td_list);
scratch = cpu_to_le32p (&td->hwINFO);
cbp = le32_to_cpup (&td->hwCBP);
be = le32_to_cpup (&td->hwBE);
temp = snprintf (buf, size,
"\n\ttd %p %s %d cc=%x urb %p (%08x)",
td,
({ char *pid;
switch (scratch & TD_DP) {
case TD_DP_SETUP: pid = "setup"; break;
case TD_DP_IN: pid = "in"; break;
case TD_DP_OUT: pid = "out"; break;
default: pid = "(?)"; break;
} pid;}),
cbp ? (be + 1 - cbp) : 0,
TD_CC_GET (scratch), td->urb, scratch);
size -= temp;
buf += temp;
}
temp = snprintf (buf, size, "\n");
size -= temp;
buf += temp;
ed = ed->ed_next;
}
return count - size;
}
static ssize_t
show_async (struct device *dev, char *buf, size_t count, loff_t off)
{
struct pci_dev *pdev;
struct ohci_hcd *ohci;
size_t temp;
unsigned long flags;
if (off != 0)
return 0;
pdev = container_of (dev, struct pci_dev, dev);
ohci = container_of (pci_get_drvdata (pdev), struct ohci_hcd, hcd);
/* display control and bulk lists together, for simplicity */
spin_lock_irqsave (&ohci->lock, flags);
temp = show_list (ohci, buf, count, ohci->ed_controltail);
count = show_list (ohci, buf + temp, count - temp, ohci->ed_bulktail);
spin_unlock_irqrestore (&ohci->lock, flags);
return temp + count;
}
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 ohci_hcd *ohci;
struct ed **seen, *ed;
unsigned long flags;
unsigned temp, size, seen_count;
char *next;
unsigned i;
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);
ohci = container_of (pci_get_drvdata (pdev), struct ohci_hcd, hcd);
next = buf;
size = count;
temp = snprintf (next, size, "size = %d\n", NUM_INTS);
size -= temp;
next += temp;
/* dump a snapshot of the periodic schedule (and load) */
spin_lock_irqsave (&ohci->lock, flags);
for (i = 0; i < NUM_INTS; i++) {
if (!(ed = ohci->periodic [i]))
continue;
temp = snprintf (next, size, "%2d [%3d]:", i, ohci->load [i]);
size -= temp;
next += temp;
do {
temp = snprintf (next, size, " ed%d/%p",
ed->interval, ed);
size -= temp;
next += temp;
for (temp = 0; temp < seen_count; temp++) {
if (seen [temp] == ed)
break;
}
/* show more info the first time around */
if (temp == seen_count) {
u32 info = ed->hwINFO;
u32 scratch = cpu_to_le32p (&ed->hwINFO);
temp = snprintf (next, size,
" (%cs dev%d%s ep%d-%s"
" max %d %08x%s%s)",
(info & ED_LOWSPEED) ? 'l' : 'f',
scratch & 0x7f,
(info & ED_ISO) ? " iso" : "",
(scratch >> 7) & 0xf,
(info & ED_IN) ? "in" : "out",
0x03ff & (scratch >> 16),
scratch,
(info & ED_SKIP) ? " s" : "",
(ed->hwHeadP & ED_H) ? " H" : "");
size -= temp;
next += temp;
// FIXME some TD info too
if (seen_count < DBG_SCHED_LIMIT)
seen [seen_count++] = ed;
ed = ed->ed_next;
} else {
/* we've seen it and what's after */
temp = 0;
ed = 0;
}
} while (ed);
temp = snprintf (next, size, "\n");
size -= temp;
next += temp;
}
spin_unlock_irqrestore (&ohci->lock, flags);
kfree (seen);
return count - size;
}
static DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL);
#undef DBG_SCHED_LIMIT
static inline void create_debug_files (struct ohci_hcd *bus)
{
device_create_file (&bus->hcd.pdev->dev, &dev_attr_async);
device_create_file (&bus->hcd.pdev->dev, &dev_attr_periodic);
// registers
dbg ("%s: created debug files", bus->hcd.self.bus_name);
}
static inline void remove_debug_files (struct ohci_hcd *bus)
{
device_remove_file (&bus->hcd.pdev->dev, &dev_attr_async);
device_remove_file (&bus->hcd.pdev->dev, &dev_attr_periodic);
}
#else /* empty stubs for creating those files */
static inline void create_debug_files (struct ohci_hcd *bus) { }
static inline void remove_debug_files (struct ohci_hcd *bus) { }
#endif /* DRIVERFS_DEBUG_FILES */
/*-------------------------------------------------------------------------*/
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
* *
* History: * History:
* *
* 2002/09/03 get rid of ed hashtables, rework periodic scheduling and
* bandwidth accounting; if debugging, show schedules in driverfs
* 2002/07/19 fixes to management of ED and schedule state. * 2002/07/19 fixes to management of ED and schedule state.
* 2002/06/09 SA-1111 support (Christopher Hoover) * 2002/06/09 SA-1111 support (Christopher Hoover)
* 2002/06/01 remember frame when HC won't see EDs any more; use that info * 2002/06/01 remember frame when HC won't see EDs any more; use that info
...@@ -66,7 +68,6 @@ ...@@ -66,7 +68,6 @@
* v1.0 1999/04/27 initial release * v1.0 1999/04/27 initial release
* *
* This file is licenced under the GPL. * This file is licenced under the GPL.
* $Id: ohci-hcd.c,v 1.9 2002/03/27 20:41:57 dbrownell Exp $
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -107,8 +108,8 @@ ...@@ -107,8 +108,8 @@
* - lots more testing!! * - lots more testing!!
*/ */
#define DRIVER_VERSION "2002-Jul-19" #define DRIVER_VERSION "2002-Sep-03"
#define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell" #define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver" #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -152,7 +153,6 @@ static int ohci_urb_enqueue ( ...@@ -152,7 +153,6 @@ static int ohci_urb_enqueue (
unsigned int pipe = urb->pipe; unsigned int pipe = urb->pipe;
int i, size = 0; int i, size = 0;
unsigned long flags; unsigned long flags;
int bustime = 0;
int retval = 0; int retval = 0;
#ifdef OHCI_VERBOSE_DEBUG #ifdef OHCI_VERBOSE_DEBUG
...@@ -230,43 +230,32 @@ static int ohci_urb_enqueue ( ...@@ -230,43 +230,32 @@ static int ohci_urb_enqueue (
} }
} }
// FIXME: much of this switch should be generic, move to hcd code ... /* schedule the ed if needed */
// ... and what's not generic can't really be handled this way. if (ed->state == ED_IDLE) {
// need to consider periodicity for both types! retval = ed_schedule (ohci, ed);
if (retval < 0)
/* allocate and claim bandwidth if needed; ISO
* needs start frame index if it was't provided.
*/
switch (usb_pipetype (pipe)) {
case PIPE_ISOCHRONOUS:
if (urb->transfer_flags & USB_ISO_ASAP) {
urb->start_frame = ((ed->state != ED_IDLE)
? (ed->intriso.last_iso + 1)
: (le16_to_cpu (ohci->hcca->frame_no)
+ 10)) & 0xffff;
}
/* FALLTHROUGH */
case PIPE_INTERRUPT:
if (urb->bandwidth == 0) {
bustime = usb_check_bandwidth (urb->dev, urb);
}
if (bustime < 0) {
retval = bustime;
goto fail; goto fail;
} if (ed->type == PIPE_ISOCHRONOUS) {
usb_claim_bandwidth (urb->dev, urb, u16 frame = le16_to_cpu (ohci->hcca->frame_no);
bustime, usb_pipeisoc (urb->pipe));
}
urb->hcpriv = urb_priv; /* delay a few frames before the first TD */
frame += max_t (u16, 8, ed->interval);
frame &= ~(ed->interval - 1);
frame |= ed->branch;
urb->start_frame = frame;
/* schedule the ed if needed */ /* yes, only USB_ISO_ASAP is supported, and
if (ed->state == ED_IDLE) * urb->start_frame is never used as input.
ed_schedule (ohci, ed); */
}
} else if (ed->type == PIPE_ISOCHRONOUS)
urb->start_frame = ed->last_iso + ed->interval;
/* fill the TDs and link them to the ed; and /* fill the TDs and link them to the ed; and
* enable that part of the schedule, if needed * enable that part of the schedule, if needed
* and update count of queued periodic urbs
*/ */
urb->hcpriv = urb_priv;
td_submit_urb (ohci, urb); td_submit_urb (ohci, urb);
fail: fail:
...@@ -531,12 +520,14 @@ static int hc_start (struct ohci_hcd *ohci) ...@@ -531,12 +520,14 @@ static int hc_start (struct ohci_hcd *ohci)
udev->speed = USB_SPEED_FULL; udev->speed = USB_SPEED_FULL;
if (usb_register_root_hub (udev, ohci->parent_dev) != 0) { if (usb_register_root_hub (udev, ohci->parent_dev) != 0) {
usb_free_dev (udev); usb_free_dev (udev);
ohci->hcd.self.root_hub = NULL;
disable (ohci); disable (ohci);
ohci->hc_control &= ~OHCI_CTRL_HCFS; ohci->hc_control &= ~OHCI_CTRL_HCFS;
writel (ohci->hc_control, &ohci->regs->control); writel (ohci->hc_control, &ohci->regs->control);
return -ENODEV; return -ENODEV;
} }
create_debug_files (ohci);
return 0; return 0;
} }
...@@ -571,7 +562,8 @@ static void ohci_irq (struct usb_hcd *hcd) ...@@ -571,7 +562,8 @@ static void ohci_irq (struct usb_hcd *hcd)
if (ints & OHCI_INTR_UE) { if (ints & OHCI_INTR_UE) {
disable (ohci); disable (ohci);
err ("OHCI Unrecoverable Error, %s disabled", hcd->self.bus_name); err ("OHCI Unrecoverable Error, %s disabled",
hcd->self.bus_name);
// e.g. due to PCI Master/Target Abort // e.g. due to PCI Master/Target Abort
#ifdef DEBUG #ifdef DEBUG
...@@ -620,10 +612,14 @@ static void ohci_stop (struct usb_hcd *hcd) ...@@ -620,10 +612,14 @@ static void ohci_stop (struct usb_hcd *hcd)
if (!ohci->disabled) if (!ohci->disabled)
hc_reset (ohci); hc_reset (ohci);
remove_debug_files (ohci);
ohci_mem_cleanup (ohci); ohci_mem_cleanup (ohci);
if (ohci->hcca) {
pci_free_consistent (ohci->hcd.pdev, sizeof *ohci->hcca, pci_free_consistent (ohci->hcd.pdev, sizeof *ohci->hcca,
ohci->hcca, ohci->hcca_dma); ohci->hcca, ohci->hcca_dma);
ohci->hcca = NULL;
ohci->hcca_dma = 0;
}
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -646,14 +642,13 @@ static int hc_restart (struct ohci_hcd *ohci) ...@@ -646,14 +642,13 @@ static int hc_restart (struct ohci_hcd *ohci)
usb_disconnect (&ohci->hcd.self.root_hub); usb_disconnect (&ohci->hcd.self.root_hub);
/* empty the interrupt branches */ /* empty the interrupt branches */
for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load [i] = 0; for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0;
for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0; for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0;
/* no EDs to remove */ /* no EDs to remove */
ohci->ed_rm_list = NULL; ohci->ed_rm_list = NULL;
/* empty control and bulk lists */ /* empty control and bulk lists */
ohci->ed_isotail = NULL;
ohci->ed_controltail = NULL; ohci->ed_controltail = NULL;
ohci->ed_bulktail = NULL; ohci->ed_bulktail = NULL;
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
* *
* This file is licenced under the GPL. * This file is licenced under the GPL.
* $Id: ohci-mem.c,v 1.3 2002/03/22 16:04:54 dbrownell Exp $
*/ */
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -52,13 +51,6 @@ dma_to_ed_td (struct hash_list_t * entry, dma_addr_t dma) ...@@ -52,13 +51,6 @@ dma_to_ed_td (struct hash_list_t * entry, dma_addr_t dma)
return scan->virt; return scan->virt;
} }
static struct ed *
dma_to_ed (struct ohci_hcd *hc, dma_addr_t ed_dma)
{
return (struct ed *) dma_to_ed_td(&(hc->ed_hash [ED_HASH_FUNC(ed_dma)]),
ed_dma);
}
static struct td * static struct td *
dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma) dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma)
{ {
...@@ -97,13 +89,6 @@ hash_add_ed_td ( ...@@ -97,13 +89,6 @@ hash_add_ed_td (
return 1; return 1;
} }
static inline int
hash_add_ed (struct ohci_hcd *hc, struct ed *ed, int mem_flags)
{
return hash_add_ed_td (&(hc->ed_hash [ED_HASH_FUNC (ed->dma)]),
ed, ed->dma, mem_flags);
}
static inline int static inline int
hash_add_td (struct ohci_hcd *hc, struct td *td, int mem_flags) hash_add_td (struct ohci_hcd *hc, struct td *td, int mem_flags)
{ {
...@@ -138,12 +123,6 @@ hash_free_ed_td (struct hash_list_t *entry, void *virt) ...@@ -138,12 +123,6 @@ hash_free_ed_td (struct hash_list_t *entry, void *virt)
} }
} }
static inline void
hash_free_ed (struct ohci_hcd *hc, struct ed * ed)
{
hash_free_ed_td (&(hc->ed_hash[ED_HASH_FUNC(ed->dma)]), ed);
}
static inline void static inline void
hash_free_td (struct ohci_hcd *hc, struct td * td) hash_free_td (struct ohci_hcd *hc, struct td * td)
{ {
...@@ -223,11 +202,6 @@ ed_alloc (struct ohci_hcd *hc, int mem_flags) ...@@ -223,11 +202,6 @@ ed_alloc (struct ohci_hcd *hc, int mem_flags)
memset (ed, 0, sizeof (*ed)); memset (ed, 0, sizeof (*ed));
INIT_LIST_HEAD (&ed->td_list); INIT_LIST_HEAD (&ed->td_list);
ed->dma = dma; ed->dma = dma;
/* hash it for later reverse mapping */
if (!hash_add_ed (hc, ed, mem_flags)) {
pci_pool_free (hc->ed_cache, ed, dma);
return NULL;
}
} }
return ed; return ed;
} }
...@@ -235,7 +209,6 @@ ed_alloc (struct ohci_hcd *hc, int mem_flags) ...@@ -235,7 +209,6 @@ ed_alloc (struct ohci_hcd *hc, int mem_flags)
static void static void
ed_free (struct ohci_hcd *hc, struct ed *ed) ed_free (struct ohci_hcd *hc, struct ed *ed)
{ {
hash_free_ed (hc, ed);
pci_pool_free (hc->ed_cache, ed, ed->dma); pci_pool_free (hc->ed_cache, ed, ed->dma);
} }
This diff is collapsed.
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
* *
* This file is licenced under the GPL. * This file is licenced under the GPL.
* $Id: ohci.h,v 1.6 2002/03/22 16:04:54 dbrownell Exp $
*/ */
/* /*
...@@ -18,13 +17,16 @@ ...@@ -18,13 +17,16 @@
struct ed { struct ed {
/* first fields are hardware-specified, le32 */ /* first fields are hardware-specified, le32 */
__u32 hwINFO; /* endpoint config bitmap */ __u32 hwINFO; /* endpoint config bitmap */
/* info bits defined by hcd */
#define ED_DEQUEUE __constant_cpu_to_le32(1 << 27)
/* info bits defined by the hardware */
#define ED_ISO __constant_cpu_to_le32(1 << 15) #define ED_ISO __constant_cpu_to_le32(1 << 15)
#define ED_SKIP __constant_cpu_to_le32(1 << 14) #define ED_SKIP __constant_cpu_to_le32(1 << 14)
#define ED_LOWSPEED __constant_cpu_to_le32(1 << 13) #define ED_LOWSPEED __constant_cpu_to_le32(1 << 13)
#define ED_OUT __constant_cpu_to_le32(0x01 << 11) #define ED_OUT __constant_cpu_to_le32(0x01 << 11)
#define ED_IN __constant_cpu_to_le32(0x02 << 11) #define ED_IN __constant_cpu_to_le32(0x02 << 11)
__u32 hwTailP; /* tail of TD list */ __u32 hwTailP; /* tail of TD list */
__u32 hwHeadP; /* head of TD list */ __u32 hwHeadP; /* head of TD list (hc r/w) */
#define ED_C __constant_cpu_to_le32(0x02) /* toggle carry */ #define ED_C __constant_cpu_to_le32(0x02) /* toggle carry */
#define ED_H __constant_cpu_to_le32(0x01) /* halted */ #define ED_H __constant_cpu_to_le32(0x01) /* halted */
__u32 hwNextED; /* next ED in list */ __u32 hwNextED; /* next ED in list */
...@@ -48,14 +50,12 @@ struct ed { ...@@ -48,14 +50,12 @@ struct ed {
#define ED_OPER 0x02 /* IS linked to hc */ #define ED_OPER 0x02 /* IS linked to hc */
u8 type; /* PIPE_{BULK,...} */ u8 type; /* PIPE_{BULK,...} */
u16 interval; /* interrupt, isochronous */
union { /* periodic scheduling params (for intr and iso) */
struct intr_info { /* interrupt */ u8 branch;
u8 int_branch; u16 interval;
u8 int_load; u16 load;
} intr_info; u16 last_iso; /* iso only */
u16 last_iso; /* isochronous */
} intriso;
/* HC may see EDs on rm_list until next frame (frame_no == tick) */ /* HC may see EDs on rm_list until next frame (frame_no == tick) */
u16 tick; u16 tick;
...@@ -335,10 +335,8 @@ struct hash_list_t { ...@@ -335,10 +335,8 @@ struct hash_list_t {
}; };
#define TD_HASH_SIZE 64 /* power'o'two */ #define TD_HASH_SIZE 64 /* power'o'two */
#define ED_HASH_SIZE 64 /* power'o'two */
#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 5)) % TD_HASH_SIZE) #define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 5)) % TD_HASH_SIZE)
#define ED_HASH_FUNC(ed_dma) ((ed_dma ^ (ed_dma >> 5)) % ED_HASH_SIZE)
/* /*
...@@ -373,7 +371,7 @@ struct ohci_hcd { ...@@ -373,7 +371,7 @@ struct ohci_hcd {
struct ed *ed_bulktail; /* last in bulk list */ struct ed *ed_bulktail; /* last in bulk list */
struct ed *ed_controltail; /* last in ctrl list */ struct ed *ed_controltail; /* last in ctrl list */
struct ed *ed_isotail; /* last in iso list */ struct ed *periodic [NUM_INTS]; /* shadow int_table */
/* /*
* memory management for queue data structures * memory management for queue data structures
...@@ -381,14 +379,13 @@ struct ohci_hcd { ...@@ -381,14 +379,13 @@ struct ohci_hcd {
struct pci_pool *td_cache; struct pci_pool *td_cache;
struct pci_pool *ed_cache; struct pci_pool *ed_cache;
struct hash_list_t td_hash [TD_HASH_SIZE]; struct hash_list_t td_hash [TD_HASH_SIZE];
struct hash_list_t ed_hash [ED_HASH_SIZE];
/* /*
* driver state * driver state
*/ */
int disabled; /* e.g. got a UE, we're hung */ int disabled; /* e.g. got a UE, we're hung */
int sleeping; int sleeping;
int ohci_int_load [NUM_INTS]; int load [NUM_INTS];
u32 hc_control; /* copy of hc control reg */ u32 hc_control; /* copy of hc control reg */
unsigned long flags; /* for HC bugs */ unsigned long flags; /* for HC bugs */
......
This diff is collapsed.
...@@ -65,6 +65,7 @@ ...@@ -65,6 +65,7 @@
#define UHCI_PTR_TERM cpu_to_le32(0x0001) #define UHCI_PTR_TERM cpu_to_le32(0x0001)
#define UHCI_PTR_QH cpu_to_le32(0x0002) #define UHCI_PTR_QH cpu_to_le32(0x0002)
#define UHCI_PTR_DEPTH cpu_to_le32(0x0004) #define UHCI_PTR_DEPTH cpu_to_le32(0x0004)
#define UHCI_PTR_BREADTH cpu_to_le32(0x0000)
#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */ #define UHCI_NUMFRAMES 1024 /* in the frame list [array] */
#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */ #define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */
...@@ -80,6 +81,19 @@ struct uhci_frame_list { ...@@ -80,6 +81,19 @@ struct uhci_frame_list {
struct urb_priv; struct urb_priv;
/* One role of a QH is to hold a queue of TDs for some endpoint. Each QH is
* used with one URB, and qh->element (updated by the HC) is either:
* - the next unprocessed TD for the URB, or
* - UHCI_PTR_TERM (when there's no more traffic for this endpoint), or
* - the QH for the next URB queued to the same endpoint.
*
* The other role of a QH is to serve as a "skeleton" framelist entry, so we
* can easily splice a QH for some endpoint into the schedule at the right
* place. Then qh->element is UHCI_PTR_TERM.
*
* In the frame list, qh->link maintains a list of QHs seen by the HC:
* skel1 --> ep1-qh --> ep2-qh --> ... --> skel2 --> ...
*/
struct uhci_qh { struct uhci_qh {
/* Hardware fields */ /* Hardware fields */
__u32 link; /* Next queue */ __u32 link; /* Next queue */
...@@ -156,6 +170,9 @@ struct uhci_qh { ...@@ -156,6 +170,9 @@ struct uhci_qh {
* *
* Alas, not anymore, we have more than 4 words for software, woops. * Alas, not anymore, we have more than 4 words for software, woops.
* Everything still works tho, surprise! -jerdfelt * Everything still works tho, surprise! -jerdfelt
*
* td->link points to either another TD (not necessarily for the same urb or
* even the same endpoint), or nothing (PTR_TERM), or a QH (for queued urbs)
*/ */
struct uhci_td { struct uhci_td {
/* Hardware fields */ /* Hardware fields */
...@@ -172,7 +189,7 @@ struct uhci_td { ...@@ -172,7 +189,7 @@ struct uhci_td {
struct list_head list; /* P: urb->lock */ struct list_head list; /* P: urb->lock */
int frame; int frame; /* for iso: what frame? */
struct list_head fl_list; /* P: uhci->frame_list_lock */ struct list_head fl_list; /* P: uhci->frame_list_lock */
} __attribute__((aligned(16))); } __attribute__((aligned(16)));
...@@ -217,6 +234,22 @@ struct uhci_td { ...@@ -217,6 +234,22 @@ struct uhci_td {
* *
* To keep with Linus' nomenclature, this is called the QH skeleton. These * To keep with Linus' nomenclature, this is called the QH skeleton. These
* labels (below) are only signficant to the root hub's QH's * labels (below) are only signficant to the root hub's QH's
*
*
* NOTE: That ASCII art doesn't match the current (August 2002) code, in
* more ways than just not using QHs for ISO.
*
* NOTE: Another way to look at the UHCI schedules is to compare them to what
* other host controller interfaces use. EHCI, OHCI, and UHCI all have tables
* of transfers that the controller scans, frame by frame, and which hold the
* scheduled periodic transfers. The key differences are that UHCI
*
* (a) puts control and bulk transfers into that same table; the others
* have separate data structures for non-periodic transfers.
* (b) lets QHs be linked from TDs, not just other QHs, since they don't
* hold endpoint data. this driver chooses to use one QH per URB.
* (c) needs more TDs, since it uses one per packet. the data toggle
* is stored in those TDs, along with all other endpoint state.
*/ */
#define UHCI_NUM_SKELTD 10 #define UHCI_NUM_SKELTD 10
...@@ -275,7 +308,7 @@ static inline int __interval_to_skel(int interval) ...@@ -275,7 +308,7 @@ static inline int __interval_to_skel(int interval)
return 7; /* int128 for 128-255 ms (Max.) */ return 7; /* int128 for 128-255 ms (Max.) */
} }
#define hcd_to_uhci(hcd_ptr) list_entry(hcd_ptr, struct uhci_hcd, hcd) #define hcd_to_uhci(hcd_ptr) container_of(hcd_ptr, struct uhci_hcd, hcd)
/* /*
* This describes the full uhci information. * This describes the full uhci information.
...@@ -286,18 +319,13 @@ static inline int __interval_to_skel(int interval) ...@@ -286,18 +319,13 @@ static inline int __interval_to_skel(int interval)
struct uhci_hcd { struct uhci_hcd {
struct usb_hcd hcd; struct usb_hcd hcd;
struct pci_dev *dev;
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
/* procfs */ /* procfs */
int num;
struct proc_dir_entry *proc_entry; struct proc_dir_entry *proc_entry;
#endif #endif
/* Grabbed from PCI */ /* Grabbed from PCI */
int irq;
unsigned int io_addr; unsigned int io_addr;
unsigned int io_size;
struct pci_pool *qh_pool; struct pci_pool *qh_pool;
struct pci_pool *td_pool; struct pci_pool *td_pool;
...@@ -329,7 +357,6 @@ struct uhci_hcd { ...@@ -329,7 +357,6 @@ struct uhci_hcd {
spinlock_t complete_list_lock; spinlock_t complete_list_lock;
struct list_head complete_list; /* P: uhci->complete_list_lock */ struct list_head complete_list; /* P: uhci->complete_list_lock */
struct usb_device *rh_dev; /* Root hub */
int rh_numports; int rh_numports;
struct timer_list stall_timer; struct timer_list stall_timer;
......
...@@ -191,6 +191,8 @@ PEGASUS_DEV( "Billionton USBE-100", VENDOR_BILLIONTON, 0x8511, ...@@ -191,6 +191,8 @@ PEGASUS_DEV( "Billionton USBE-100", VENDOR_BILLIONTON, 0x8511,
DEFAULT_GPIO_RESET | PEGASUS_II ) DEFAULT_GPIO_RESET | PEGASUS_II )
PEGASUS_DEV( "Corega FEter USB-TX", VENDOR_COREGA, 0x0004, PEGASUS_DEV( "Corega FEter USB-TX", VENDOR_COREGA, 0x0004,
DEFAULT_GPIO_RESET ) DEFAULT_GPIO_RESET )
PEGASUS_DEV( "Corega FEter", VENDOR_COREGA, 0x0004,
DEFAULT_GPIO_RESET | PEGASUS_II )
PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4001, PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4001,
LINKSYS_GPIO_RESET ) LINKSYS_GPIO_RESET )
PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4002, PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4002,
...@@ -205,7 +207,7 @@ PEGASUS_DEV( "D-Link DSB-650TX(PNA)", VENDOR_DLINK, 0x4003, ...@@ -205,7 +207,7 @@ PEGASUS_DEV( "D-Link DSB-650TX(PNA)", VENDOR_DLINK, 0x4003,
DEFAULT_GPIO_RESET | HAS_HOME_PNA ) DEFAULT_GPIO_RESET | HAS_HOME_PNA )
PEGASUS_DEV( "D-Link DSB-650", VENDOR_DLINK, 0xabc1, PEGASUS_DEV( "D-Link DSB-650", VENDOR_DLINK, 0xabc1,
DEFAULT_GPIO_RESET ) DEFAULT_GPIO_RESET )
PEGASUS_DEV( "ELCON EPLC10Mi USB to Powerline Adapter", VENDOR_ELCON, 0x0002, PEGASUS_DEV( "GOLDPFEIL USB Adapter", VENDOR_ELCON, 0x0002,
DEFAULT_GPIO_RESET | PEGASUS_II | HAS_HOME_PNA ) DEFAULT_GPIO_RESET | PEGASUS_II | HAS_HOME_PNA )
PEGASUS_DEV( "Elsa Micolink USB2Ethernet", VENDOR_ELSA, 0x3000, PEGASUS_DEV( "Elsa Micolink USB2Ethernet", VENDOR_ELSA, 0x3000,
DEFAULT_GPIO_RESET ) DEFAULT_GPIO_RESET )
...@@ -254,7 +256,7 @@ PEGASUS_DEV( "SOHOware NUB100 Ethernet", VENDOR_SOHOWARE, 0x9100, ...@@ -254,7 +256,7 @@ PEGASUS_DEV( "SOHOware NUB100 Ethernet", VENDOR_SOHOWARE, 0x9100,
PEGASUS_DEV( "SOHOware NUB110 Ethernet", VENDOR_SOHOWARE, 0x9110, PEGASUS_DEV( "SOHOware NUB110 Ethernet", VENDOR_SOHOWARE, 0x9110,
DEFAULT_GPIO_RESET | PEGASUS_II ) DEFAULT_GPIO_RESET | PEGASUS_II )
PEGASUS_DEV( "SpeedStream USB 10/100 Ethernet", VENDOR_SIEMENS, 0x1001, PEGASUS_DEV( "SpeedStream USB 10/100 Ethernet", VENDOR_SIEMENS, 0x1001,
DEFAULT_GPIO_RESET ) DEFAULT_GPIO_RESET | PEGASUS_II )
#endif /* PEGASUS_DEV */ #endif /* PEGASUS_DEV */
...@@ -310,6 +310,13 @@ UNUSUAL_DEV( 0x05dc, 0x0001, 0x0000, 0x0001, ...@@ -310,6 +310,13 @@ UNUSUAL_DEV( 0x05dc, 0x0001, 0x0000, 0x0001,
US_FL_MODE_XLATE ), US_FL_MODE_XLATE ),
#endif #endif
/* Reported by Blake Matheny <bmatheny@purdue.edu> */
UNUSUAL_DEV( 0x05dc, 0xb002, 0x0000, 0x0113,
"Lexar",
"USB CF Reader",
US_SC_SCSI, US_PR_BULK, NULL,
US_FL_FIX_INQUIRY ),
/* Reported by Carlos Villegas <cav@uniscope.co.jp> /* Reported by Carlos Villegas <cav@uniscope.co.jp>
* This device needs an INQUIRY of exactly 36-bytes to function. * This device needs an INQUIRY of exactly 36-bytes to function.
* That is the only reason this entry is needed. * That is the only reason this entry is needed.
......
...@@ -440,8 +440,14 @@ static int usb_stor_control_thread(void * __us) ...@@ -440,8 +440,14 @@ static int usb_stor_control_thread(void * __us)
/* Most USB devices can't handle START_STOP. But we /* Most USB devices can't handle START_STOP. But we
* need something for media-change, so we'll use TUR * need something for media-change, so we'll use TUR
* instead. * instead.
*
* We specifically allow this command through if either:
* (a) it's a load/eject command (cmnd[4] & 2)
* (b) it's a multi-target unit (i.e. legacy SCSI adaptor)
*/ */
else if (us->srb->cmnd[0] == START_STOP) { else if (us->srb->cmnd[0] == START_STOP &&
!(us->srb->cmnd[4] & 2) &&
!(us->flags & US_FL_SCM_MULT_TARG)) {
unsigned char saved_cdb[16]; /* largest SCSI-III cmd */ unsigned char saved_cdb[16]; /* largest SCSI-III cmd */
__u8 old_cmd_len; __u8 old_cmd_len;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment