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)
if (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 */
if (!(urb->transfer_flags & URB_NO_DMA_MAP)) {
if (usb_pipecontrol (urb->pipe))
......@@ -1038,16 +1048,7 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags)
: PCI_DMA_TODEVICE);
}
/* 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)
status = rh_urb_enqueue (hcd, urb);
else
status = hcd->driver->urb_enqueue (hcd, urb, mem_flags);
return status;
return hcd->driver->urb_enqueue (hcd, urb, mem_flags);
}
/*-------------------------------------------------------------------------*/
......
......@@ -27,7 +27,6 @@
/*****************************************************************************/
#define __NO_VERSION__
#include <linux/config.h>
#include <linux/module.h>
#include <linux/fs.h>
......@@ -561,35 +560,57 @@ static void put_mount (struct vfsmount **mount)
static int create_special_files (void)
{
struct dentry *parent;
int retval;
int retval = 0;
/* create the devices special file */
retval = get_mount (&usbdevice_fs_type, &usbdevfs_mount);
if (retval)
return retval;
if (retval) {
err ("Unable to get usbdevfs mount");
goto exit;
}
retval = get_mount (&usb_fs_type, &usbfs_mount);
if (retval) {
put_mount (&usbfs_mount);
return retval;
err ("Unable to get usbfs mount");
goto error_clean_usbdevfs_mount;
}
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,
listuid, listgid);
if (devices_usbfs_dentry == NULL) {
err ("Unable to create devices usbfs file");
return -ENODEV;
retval = -ENODEV;
goto error_clean_mounts;
}
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,
listuid, listgid);
if (devices_usbdevfs_dentry == NULL) {
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)
......
......@@ -57,7 +57,7 @@ static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
strcat(buf, tmp);
}
dbg ("%s: %s portroute %s",
ehci->hcd.self.bus_name, label,
hcd_to_bus (&ehci->hcd)->bus_name, label,
buf);
}
}
......@@ -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,
"%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)
);
}
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,
"%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)
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 */
......@@ -248,7 +250,16 @@ dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status)
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; \
switch (info1 & (3 << 12)) { \
......@@ -258,6 +269,49 @@ dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status)
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)
{
......@@ -284,49 +338,21 @@ show_async (struct device *dev, char *buf, size_t count, loff_t off)
if (ehci->async) {
qh = ehci->async;
do {
u32 scratch;
struct list_head *entry;
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;
qh_lines (qh, &next, &size);
} while ((qh = qh->qh_next.qh) != ehci->async);
}
temp = snprintf (next, size, "\n");
if (ehci->reclaim) {
temp = snprintf (next, size, "\nreclaim =\n");
size -= temp;
next += temp;
} while ((qh = qh->qh_next.qh) != ehci->async);
qh_lines (ehci->reclaim, &next, &size);
}
spin_unlock_irqrestore (&ehci->lock, flags);
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
......@@ -373,7 +399,7 @@ show_periodic (struct device *dev, char *buf, size_t count, loff_t off)
do {
switch (tag) {
case Q_TYPE_QH:
temp = snprintf (next, size, " intr-%d %p",
temp = snprintf (next, size, " qh%d/%p",
p.qh->period, p.qh);
size -= temp;
next += temp;
......@@ -387,12 +413,14 @@ show_periodic (struct device *dev, char *buf, size_t count, loff_t off)
&p.qh->hw_info1);
temp = snprintf (next, size,
" (%cs dev%d ep%d)",
" (%cs dev%d ep%d [%d/%d] %d)",
speed_char (scratch),
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)
seen [seen_count++].qh = p.qh;
......@@ -434,7 +462,7 @@ show_periodic (struct device *dev, char *buf, size_t count, loff_t off)
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
......@@ -522,7 +550,7 @@ show_registers (struct device *dev, char *buf, size_t count, loff_t off)
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)
{
......@@ -538,14 +566,5 @@ static inline void remove_debug_files (struct ehci_hcd *bus)
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 @@
#endif
#include <linux/usb.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32)
#include "../hcd.h"
#else
#include "../core/hcd.h"
#endif
#include <asm/byteorder.h>
#include <asm/io.h>
......@@ -87,7 +92,7 @@
* 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_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
......@@ -104,6 +109,8 @@
#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 */
/* Initial IRQ latency: lower than default */
static int log2_irq_thresh = 0; // 0 to 6
MODULE_PARM (log2_irq_thresh, "i");
......@@ -232,6 +239,19 @@ static void ehci_ready (struct ehci_hcd *ehci)
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/...
* off the controller (maybe it can boot from highspeed USB disks).
*/
......@@ -267,6 +287,7 @@ static int ehci_start (struct usb_hcd *hcd)
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;
......@@ -372,16 +393,19 @@ static int ehci_start (struct usb_hcd *hcd)
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->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) {
done2:
ehci_mem_cleanup (ehci);
return -ENOMEM;
}
create_debug_files (ehci);
/*
* Start, enabling full USB 2.0 functionality ... usb 1.1 devices
* are explicitly handed to companion controller(s), so no TT is
......@@ -394,7 +418,7 @@ static int ehci_start (struct usb_hcd *hcd)
/* 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",
info ("USB %x.%x support enabled, EHCI rev %x.%02x",
((tempbyte & 0xf0)>>4),
(tempbyte & 0x0f),
temp >> 8,
......@@ -409,16 +433,22 @@ static int ehci_start (struct usb_hcd *hcd)
*/
usb_connect (udev);
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) {
#endif
if (hcd->state == USB_STATE_RUNNING)
ehci_ready (ehci);
ehci_reset (ehci);
hcd->self.root_hub = 0;
bus->root_hub = 0;
usb_free_dev (udev);
retval = -ENODEV;
goto done2;
}
create_debug_files (ehci);
return 0;
}
......@@ -428,13 +458,20 @@ static void ehci_stop (struct usb_hcd *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 ... */
if (hcd->state == USB_STATE_RUNNING)
ehci_ready (ehci);
if (in_interrupt ()) /* should not happen!! */
err ("stopped %s!", RUN_CONTEXT);
else
del_timer_sync (&ehci->watchdog);
ehci_reset (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) */
......@@ -463,7 +500,7 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state)
int ports;
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);
......@@ -480,7 +517,7 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state)
if ((temp & PORT_PE) == 0
|| (temp & PORT_OWNER) != 0)
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;
writel (temp, &ehci->regs->port_status [i]);
}
......@@ -502,7 +539,7 @@ static int ehci_resume (struct usb_hcd *hcd)
int ports;
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);
......@@ -522,7 +559,7 @@ static int ehci_resume (struct usb_hcd *hcd)
if ((temp & PORT_PE) == 0
|| (temp & PORT_SUSPEND) != 0)
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;
writel (temp, &ehci->regs->port_status [i]);
readl (&ehci->regs->command); /* unblock posted writes */
......@@ -546,12 +583,17 @@ dbg ("%s: resume port %d", hcd->self.bus_name, i);
static void ehci_tasklet (unsigned long param)
{
struct ehci_hcd *ehci = (struct ehci_hcd *) param;
unsigned long flags;
spin_lock_irqsave (&ehci->lock, flags);
if (ehci->reclaim_ready)
end_unlink_async (ehci);
scan_async (ehci);
flags = end_unlink_async (ehci, flags);
flags = scan_async (ehci, flags);
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)
/* e.g. cardbus physical eject */
if (status == ~(u32) 0) {
dbg ("%s: device removed!", hcd->self.bus_name);
dbg ("%s: device removed!", hcd_to_bus (hcd)->bus_name);
goto dead;
}
......@@ -597,7 +639,7 @@ static void ehci_irq (struct usb_hcd *hcd)
/* PCI errors [4.15.2.4] */
if (unlikely ((status & STS_FATAL) != 0)) {
err ("%s: fatal error, state %x",
hcd->self.bus_name, hcd->state);
hcd_to_bus (hcd)->bus_name, hcd->state);
dead:
ehci_reset (ehci);
/* generic layer kills/unlinks all urbs, then
......@@ -673,7 +715,7 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
unsigned long flags;
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)) {
// case PIPE_CONTROL:
......@@ -681,7 +723,13 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
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;
......@@ -702,19 +750,19 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
break;
case PIPE_INTERRUPT:
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);
flags = intr_deschedule (ehci, qh, 1, flags);
/* qh_state == IDLE */
}
qh_completions (ehci, qh);
flags = qh_completions (ehci, qh, flags);
/* reschedule QH iff another request is queued */
if (!list_empty (&qh->qtd_list)
&& HCD_IS_RUNNING (ehci->hcd.state)) {
int status;
spin_lock_irqsave (&ehci->lock, flags);
status = qh_schedule (ehci, qh);
spin_unlock_irqrestore (&ehci->lock, flags);
......@@ -726,7 +774,7 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
}
return status;
}
spin_unlock_irqrestore (&ehci->lock, flags);
break;
case PIPE_ISOCHRONOUS:
......@@ -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: 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);
for (i = 0; i < 32; i++) {
......@@ -775,7 +824,8 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
why = 0;
if (why) {
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",
why);
BUG ();
......@@ -805,8 +855,7 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
start_unlink_async (ehci, qh);
while (qh->qh_state != QH_STATE_IDLE
&& ehci->hcd.state != USB_STATE_HALT) {
spin_unlock_irqrestore (&ehci->lock,
flags);
spin_unlock_irqrestore (&ehci->lock, flags);
wait_ms (1);
spin_lock_irqsave (&ehci->lock, flags);
}
......
......@@ -41,14 +41,17 @@ static int check_reset_complete (
/* 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.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 ?
port_status |= PORT_OWNER;
writel (port_status, &ehci->regs->port_status [index]);
} 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;
}
......@@ -310,11 +313,13 @@ static int ehci_hub_control (
if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
&& PORT_USB11 (temp)) {
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;
} else {
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_PE;
......
......@@ -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) */
static void ehci_urb_done (
static unsigned long ehci_urb_done (
struct ehci_hcd *ehci,
struct urb *urb
struct urb *urb,
unsigned long flags
) {
#ifdef INTR_AUTOMAGIC
struct urb *resubmit = 0;
......@@ -177,7 +178,7 @@ static void ehci_urb_done (
if ((qh->hw_info2 & cpu_to_le32 (0x00ff)) != 0) {
/* ... update hc-wide periodic stats (for usbfs) */
ehci->hcd.self.bandwidth_int_reqs--;
hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs--;
#ifdef INTR_AUTOMAGIC
if (!((urb->status == -ENOENT)
......@@ -199,6 +200,8 @@ static void ehci_urb_done (
urb->status = 0;
}
/* complete() can reenter this HCD */
spin_unlock_irqrestore (&ehci->lock, flags);
usb_hcd_giveback_urb (&ehci->hcd, urb);
#ifdef INTR_AUTOMAGIC
......@@ -212,34 +215,32 @@ static void ehci_urb_done (
int status;
resubmit->dev = dev;
status = usb_submit_urb (resubmit, SLAB_KERNEL);
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_irqsave (&ehci->lock, flags);
return flags;
}
/*
* Process completed qtds for a qh, issuing completions if needed.
* Frees qtds, unmaps buf, returns URB to driver.
* 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 irqsave flags (maybe modified).
*/
static void
qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
static unsigned long
qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, unsigned long flags)
{
struct ehci_qtd *qtd, *last;
struct list_head *next, *qtd_list = &qh->qtd_list;
int unlink = 0, halted = 0;
unsigned long flags;
spin_lock_irqsave (&ehci->lock, flags);
if (unlikely (list_empty (qtd_list))) {
spin_unlock_irqrestore (&ehci->lock, flags);
return;
}
if (unlikely (list_empty (qtd_list)))
return flags;
/* scan QTDs till end of list, or we reach an active one */
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)
/* 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);
ehci_urb_done (ehci, last->urb);
spin_lock_irqsave (&ehci->lock, flags);
}
if (likely (last->urb != urb))
flags = ehci_urb_done (ehci, last->urb, flags);
/* qh overlays can have HC's old cached copies of
* next qtd ptrs, if an URB was queued afterwards.
......@@ -283,6 +280,9 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|| (ehci->hcd.state == USB_STATE_HALT)
|| (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? */
if (unlikely ((token & QTD_STS_HALT) != 0)) {
unlink = 1;
......@@ -341,18 +341,19 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
#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))) {
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)) {
ehci_urb_done (ehci, last->urb);
ehci_qtd_free (ehci, last);
}
return flags;
}
/*-------------------------------------------------------------------------*/
......@@ -367,31 +368,12 @@ static void qtd_list_free (
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++;
}
}
ehci_qtd_free (ehci, qtd);
}
}
......@@ -670,8 +652,8 @@ ehci_qh_make (
info2 |= hb_mult (maxp) << 30;
}
break;
#ifdef DEBUG
default:
#ifdef DEBUG
BUG ();
#endif
}
......@@ -859,7 +841,8 @@ submit_async (
epnum |= 0x10;
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",
qtd, dev ? dev->ep [epnum] : (void *)~0);
......@@ -886,25 +869,28 @@ submit_async (
/* the async qh for the qtds being reclaimed are now unlinked from the HC */
/* 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;
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);
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)
&& HCD_IS_RUNNING (ehci->hcd.state))
qh_link_async (ehci, qh);
else
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)
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 unsigned long
scan_async (struct ehci_hcd *ehci, unsigned long flags)
{
struct ehci_qh *qh;
unsigned long flags;
spin_lock_irqsave (&ehci->lock, flags);
rescan:
qh = ehci->async;
if (likely (qh != 0)) {
......@@ -993,12 +980,9 @@ static void scan_async (struct ehci_hcd *ehci)
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);
spin_lock_irqsave (&ehci->lock, flags);
flags = qh_completions (ehci, qh, flags);
qh_put (ehci, qh);
}
......@@ -1020,6 +1004,5 @@ static void scan_async (struct ehci_hcd *ehci)
goto rescan;
} while (qh != ehci->async);
}
spin_unlock_irqrestore (&ehci->lock, flags);
return flags;
}
......@@ -222,17 +222,15 @@ static int disable_periodic (struct ehci_hcd *ehci)
// FIXME microframe periods not yet handled
static void intr_deschedule (
static unsigned long intr_deschedule (
struct ehci_hcd *ehci,
struct ehci_qh *qh,
int wait
int wait,
unsigned long flags
) {
unsigned long flags;
int status;
unsigned frame = qh->start;
spin_lock_irqsave (&ehci->lock, flags);
do {
periodic_unlink (ehci, frame, qh);
qh_put (ehci, qh);
......@@ -251,8 +249,6 @@ static void intr_deschedule (
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.
......@@ -260,8 +256,10 @@ static void intr_deschedule (
*/
if (((ehci_get_frame (&ehci->hcd) - frame) % qh->period) == 0) {
if (wait) {
spin_unlock_irqrestore (&ehci->lock, flags);
udelay (125);
qh->hw_next = EHCI_LIST_END;
spin_lock_irqsave (&ehci->lock, flags);
} else {
/* we may not be IDLE yet, but if the qh is empty
* the race is very short. then if qh also isn't
......@@ -275,12 +273,13 @@ static void intr_deschedule (
qh->qh_state = QH_STATE_IDLE;
/* 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;
vdbg ("descheduled qh %p, per = %d frame = %d count = %d, urbs = %d",
qh, qh->period, frame,
atomic_read (&qh->refcount), ehci->periodic_sched);
return flags;
}
static int check_period (
......@@ -436,7 +435,7 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
} while (frame < ehci->periodic_size);
/* 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;
/* maybe enable periodic schedule processing */
......@@ -486,7 +485,7 @@ static int intr_submit (
BUG_ON (qh == 0);
/* ... update usbfs periodic stats */
ehci->hcd.self.bandwidth_int_reqs++;
hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs++;
done:
spin_unlock_irqrestore (&ehci->lock, flags);
......@@ -513,12 +512,10 @@ intr_complete (
}
/* handle any completions */
spin_unlock_irqrestore (&ehci->lock, flags);
qh_completions (ehci, qh);
spin_lock_irqsave (&ehci->lock, flags);
flags = qh_completions (ehci, qh, flags);
if (unlikely (list_empty (&qh->qtd_list)))
intr_deschedule (ehci, qh, 0);
flags = intr_deschedule (ehci, qh, 0, flags);
return 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 long flags;
mod = ehci->periodic_size << 3;
spin_lock_irqsave (&ehci->lock, flags);
/*
* When running, scan from last scan point up to "now"
......@@ -1237,5 +1233,5 @@ static void scan_periodic (struct ehci_hcd *ehci)
} else
frame = (frame + 1) % ehci->periodic_size;
}
spin_unlock_irqrestore (&ehci->lock, flags);
return flags;
}
......@@ -69,6 +69,8 @@ struct ehci_hcd { /* one per controller */
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;
};
/* unwrap an HCD pointer to get an EHCI_HCD pointer */
......@@ -389,4 +391,26 @@ struct ehci_fstn {
union ehci_shadow fstn_next; /* ptr to periodic q entry */
} __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 */
......@@ -72,37 +72,6 @@ static void urb_print (struct urb * urb, char * str, int small)
#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)
{
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)
if (verbose)
ohci_dump_periodic (controller, "hcca");
#endif
if (controller->hcca)
dbg ("hcca frame #%04x", controller->hcca->frame_no);
ohci_dump_roothub (controller, 1);
}
......@@ -316,8 +286,9 @@ ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose)
case ED_IN: type = "-IN"; break;
/* else from TDs ... control */
}
dbg (" info %08x MAX=%d%s%s%s EP=%d%s DEV=%d", le32_to_cpu (tmp),
0x0fff & (le32_to_cpu (tmp) >> 16),
dbg (" info %08x MAX=%d%s%s%s%s EP=%d%s DEV=%d", le32_to_cpu (tmp),
0x03ff & (le32_to_cpu (tmp) >> 16),
(tmp & ED_DEQUEUE) ? " DQ" : "",
(tmp & ED_ISO) ? " ISO" : "",
(tmp & ED_SKIP) ? " SKIP" : "",
(tmp & ED_LOWSPEED) ? " LOW" : "",
......@@ -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 /* 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 @@
*
* 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/06/09 SA-1111 support (Christopher Hoover)
* 2002/06/01 remember frame when HC won't see EDs any more; use that info
......@@ -66,7 +68,6 @@
* v1.0 1999/04/27 initial release
*
* 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>
......@@ -107,8 +108,8 @@
* - lots more testing!!
*/
#define DRIVER_VERSION "2002-Jul-19"
#define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell"
#define DRIVER_VERSION "2002-Sep-03"
#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
/*-------------------------------------------------------------------------*/
......@@ -152,7 +153,6 @@ static int ohci_urb_enqueue (
unsigned int pipe = urb->pipe;
int i, size = 0;
unsigned long flags;
int bustime = 0;
int retval = 0;
#ifdef OHCI_VERBOSE_DEBUG
......@@ -230,43 +230,32 @@ static int ohci_urb_enqueue (
}
}
// FIXME: much of this switch should be generic, move to hcd code ...
// ... and what's not generic can't really be handled this way.
// need to consider periodicity for both types!
/* 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;
/* schedule the ed if needed */
if (ed->state == ED_IDLE) {
retval = ed_schedule (ohci, ed);
if (retval < 0)
goto fail;
}
usb_claim_bandwidth (urb->dev, urb,
bustime, usb_pipeisoc (urb->pipe));
}
if (ed->type == PIPE_ISOCHRONOUS) {
u16 frame = le16_to_cpu (ohci->hcca->frame_no);
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 */
if (ed->state == ED_IDLE)
ed_schedule (ohci, ed);
/* yes, only USB_ISO_ASAP is supported, and
* urb->start_frame is never used as input.
*/
}
} else if (ed->type == PIPE_ISOCHRONOUS)
urb->start_frame = ed->last_iso + ed->interval;
/* fill the TDs and link them to the ed; and
* enable that part of the schedule, if needed
* and update count of queued periodic urbs
*/
urb->hcpriv = urb_priv;
td_submit_urb (ohci, urb);
fail:
......@@ -531,12 +520,14 @@ static int hc_start (struct ohci_hcd *ohci)
udev->speed = USB_SPEED_FULL;
if (usb_register_root_hub (udev, ohci->parent_dev) != 0) {
usb_free_dev (udev);
ohci->hcd.self.root_hub = NULL;
disable (ohci);
ohci->hc_control &= ~OHCI_CTRL_HCFS;
writel (ohci->hc_control, &ohci->regs->control);
return -ENODEV;
}
create_debug_files (ohci);
return 0;
}
......@@ -571,7 +562,8 @@ static void ohci_irq (struct usb_hcd *hcd)
if (ints & OHCI_INTR_UE) {
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
#ifdef DEBUG
......@@ -620,10 +612,14 @@ static void ohci_stop (struct usb_hcd *hcd)
if (!ohci->disabled)
hc_reset (ohci);
remove_debug_files (ohci);
ohci_mem_cleanup (ohci);
if (ohci->hcca) {
pci_free_consistent (ohci->hcd.pdev, sizeof *ohci->hcca,
ohci->hcca, ohci->hcca_dma);
ohci->hcca = NULL;
ohci->hcca_dma = 0;
}
}
/*-------------------------------------------------------------------------*/
......@@ -646,14 +642,13 @@ static int hc_restart (struct ohci_hcd *ohci)
usb_disconnect (&ohci->hcd.self.root_hub);
/* 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;
/* no EDs to remove */
ohci->ed_rm_list = NULL;
/* empty control and bulk lists */
ohci->ed_isotail = NULL;
ohci->ed_controltail = NULL;
ohci->ed_bulktail = NULL;
......
......@@ -5,7 +5,6 @@
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* 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)
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 *
dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma)
{
......@@ -97,13 +89,6 @@ hash_add_ed_td (
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
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)
}
}
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
hash_free_td (struct ohci_hcd *hc, struct td * td)
{
......@@ -223,11 +202,6 @@ ed_alloc (struct ohci_hcd *hc, int mem_flags)
memset (ed, 0, sizeof (*ed));
INIT_LIST_HEAD (&ed->td_list);
ed->dma = dma;
/* hash it for later reverse mapping */
if (!hash_add_ed (hc, ed, mem_flags)) {
pci_pool_free (hc->ed_cache, ed, dma);
return NULL;
}
}
return ed;
}
......@@ -235,7 +209,6 @@ ed_alloc (struct ohci_hcd *hc, int mem_flags)
static void
ed_free (struct ohci_hcd *hc, struct ed *ed)
{
hash_free_ed (hc, ed);
pci_pool_free (hc->ed_cache, ed, ed->dma);
}
This diff is collapsed.
......@@ -5,7 +5,6 @@
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* 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 @@
struct ed {
/* first fields are hardware-specified, le32 */
__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_SKIP __constant_cpu_to_le32(1 << 14)
#define ED_LOWSPEED __constant_cpu_to_le32(1 << 13)
#define ED_OUT __constant_cpu_to_le32(0x01 << 11)
#define ED_IN __constant_cpu_to_le32(0x02 << 11)
__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_H __constant_cpu_to_le32(0x01) /* halted */
__u32 hwNextED; /* next ED in list */
......@@ -48,14 +50,12 @@ struct ed {
#define ED_OPER 0x02 /* IS linked to hc */
u8 type; /* PIPE_{BULK,...} */
u16 interval; /* interrupt, isochronous */
union {
struct intr_info { /* interrupt */
u8 int_branch;
u8 int_load;
} intr_info;
u16 last_iso; /* isochronous */
} intriso;
/* periodic scheduling params (for intr and iso) */
u8 branch;
u16 interval;
u16 load;
u16 last_iso; /* iso only */
/* HC may see EDs on rm_list until next frame (frame_no == tick) */
u16 tick;
......@@ -335,10 +335,8 @@ struct hash_list_t {
};
#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 ED_HASH_FUNC(ed_dma) ((ed_dma ^ (ed_dma >> 5)) % ED_HASH_SIZE)
/*
......@@ -373,7 +371,7 @@ struct ohci_hcd {
struct ed *ed_bulktail; /* last in bulk 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
......@@ -381,14 +379,13 @@ struct ohci_hcd {
struct pci_pool *td_cache;
struct pci_pool *ed_cache;
struct hash_list_t td_hash [TD_HASH_SIZE];
struct hash_list_t ed_hash [ED_HASH_SIZE];
/*
* driver state
*/
int disabled; /* e.g. got a UE, we're hung */
int sleeping;
int ohci_int_load [NUM_INTS];
int load [NUM_INTS];
u32 hc_control; /* copy of hc control reg */
unsigned long flags; /* for HC bugs */
......
This diff is collapsed.
......@@ -65,6 +65,7 @@
#define UHCI_PTR_TERM cpu_to_le32(0x0001)
#define UHCI_PTR_QH cpu_to_le32(0x0002)
#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_MAX_SOF_NUMBER 2047 /* in an SOF packet */
......@@ -80,6 +81,19 @@ struct uhci_frame_list {
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 {
/* Hardware fields */
__u32 link; /* Next queue */
......@@ -156,6 +170,9 @@ struct uhci_qh {
*
* Alas, not anymore, we have more than 4 words for software, woops.
* 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 {
/* Hardware fields */
......@@ -172,7 +189,7 @@ struct uhci_td {
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 */
} __attribute__((aligned(16)));
......@@ -217,6 +234,22 @@ struct uhci_td {
*
* To keep with Linus' nomenclature, this is called the QH skeleton. These
* 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
......@@ -275,7 +308,7 @@ static inline int __interval_to_skel(int interval)
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.
......@@ -286,18 +319,13 @@ static inline int __interval_to_skel(int interval)
struct uhci_hcd {
struct usb_hcd hcd;
struct pci_dev *dev;
#ifdef CONFIG_PROC_FS
/* procfs */
int num;
struct proc_dir_entry *proc_entry;
#endif
/* Grabbed from PCI */
int irq;
unsigned int io_addr;
unsigned int io_size;
struct pci_pool *qh_pool;
struct pci_pool *td_pool;
......@@ -329,7 +357,6 @@ struct uhci_hcd {
spinlock_t complete_list_lock;
struct list_head complete_list; /* P: uhci->complete_list_lock */
struct usb_device *rh_dev; /* Root hub */
int rh_numports;
struct timer_list stall_timer;
......
......@@ -191,6 +191,8 @@ PEGASUS_DEV( "Billionton USBE-100", VENDOR_BILLIONTON, 0x8511,
DEFAULT_GPIO_RESET | PEGASUS_II )
PEGASUS_DEV( "Corega FEter USB-TX", VENDOR_COREGA, 0x0004,
DEFAULT_GPIO_RESET )
PEGASUS_DEV( "Corega FEter", VENDOR_COREGA, 0x0004,
DEFAULT_GPIO_RESET | PEGASUS_II )
PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4001,
LINKSYS_GPIO_RESET )
PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4002,
......@@ -205,7 +207,7 @@ PEGASUS_DEV( "D-Link DSB-650TX(PNA)", VENDOR_DLINK, 0x4003,
DEFAULT_GPIO_RESET | HAS_HOME_PNA )
PEGASUS_DEV( "D-Link DSB-650", VENDOR_DLINK, 0xabc1,
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 )
PEGASUS_DEV( "Elsa Micolink USB2Ethernet", VENDOR_ELSA, 0x3000,
DEFAULT_GPIO_RESET )
......@@ -254,7 +256,7 @@ PEGASUS_DEV( "SOHOware NUB100 Ethernet", VENDOR_SOHOWARE, 0x9100,
PEGASUS_DEV( "SOHOware NUB110 Ethernet", VENDOR_SOHOWARE, 0x9110,
DEFAULT_GPIO_RESET | PEGASUS_II )
PEGASUS_DEV( "SpeedStream USB 10/100 Ethernet", VENDOR_SIEMENS, 0x1001,
DEFAULT_GPIO_RESET )
DEFAULT_GPIO_RESET | PEGASUS_II )
#endif /* PEGASUS_DEV */
......@@ -310,6 +310,13 @@ UNUSUAL_DEV( 0x05dc, 0x0001, 0x0000, 0x0001,
US_FL_MODE_XLATE ),
#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>
* This device needs an INQUIRY of exactly 36-bytes to function.
* That is the only reason this entry is needed.
......
......@@ -440,8 +440,14 @@ static int usb_stor_control_thread(void * __us)
/* Most USB devices can't handle START_STOP. But we
* need something for media-change, so we'll use TUR
* 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 */
__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