On Sun, 13 Aug 2006, Tony Smolar wrote:
> Hi,
> I recently upgraded to Fedora Core 5, from an old Red Hat 9
> installation, so I began using Linux 2.6 for the first time. I
> immediately started having USB problems. Ports that don't work,
> devices that stop working suddenly. I did searching of this list and
> found that cabling is often suspect. So I redid the cabling to my front
> USB ports. Now my front USB 2.0 ports work fine, but I'm still having
> trouble with a particular device. It's an internal 9-in-1 card
> reader. If you put a SD card in and read a few photos off of it, it's
> guaranteed to stop working with the following message under "dmesg":
>
> usb 6-3: reset high speed USB device using ehci_hcd and address 2
>
> If I remove the ehci_hcd driver, it works fine with the ohci_hcd
> driver. I think the device is just not completely USB 2.0
> compliant. It seems to flake out under Windows too. I suppose it
> still could be cabling, but it looks to have a good quality cable, and
> I've tried to isolate it from other sources as much as possible inside
> the case. The type of cable it uses isn't easy to come by, so I think
> I'm stuck with what I've got.
It could a problem not so much with the cable itself as with the
termination electronics at the point where the cable joins the device.
> So my question is, can I force ohci_hcd to drive this device? If I do
> "modprobe -r ehci_hcd", the device will work, but then I lose all USB
> 2.0 capability. I'd like to just prevent ehci_hcd from trying to drive
> that device. (It's Vendor=07cc ProdID=0350 BTW, It's sort of a
> generic/OEM device, I'm not sure who the manufacturer is)
I have attached a series of three patches. If you apply them in sequence
and boot the resulting kernel, you will find a file named
/sys/class/usb_host/usb_hostN/companion
where N is the bus number of the EHCI controller. Normally this file is
empty, but you can write various port numbers to it. For example:
echo 3 >/sys/class/usb_host/usb_host1/companion
echo 4 >/sys/class/usb_host/usb_host1/companion
The effect will be that a USB device attached to port 3 or 4 will
automatically be handed off from EHCI to the companion OHCI full-speed
controller. To erase a port number from the file, write its negative:
echo -3 >/sys/class/usb_host/usb_host1/companion
This works based only on the port number, not on the device ID, but it
should be good enough to solve your problem.
Alan Stern
Index: usb-2.6/drivers/usb/host/ehci-hub.c
===================================================================
--- usb-2.6.orig/drivers/usb/host/ehci-hub.c
+++ usb-2.6/drivers/usb/host/ehci-hub.c
@@ -176,6 +176,7 @@ static int ehci_bus_resume (struct usb_h
static int check_reset_complete (
struct ehci_hcd *ehci,
int index,
+ u32 __iomem *status_reg,
int port_status
) {
if (!(port_status & PORT_CONNECT)) {
@@ -200,7 +201,7 @@ static int check_reset_complete (
// what happens if HCS_N_CC(params) == 0 ?
port_status |= PORT_OWNER;
port_status &= ~PORT_RWC_BITS;
- writel (port_status, &ehci->regs->port_status [index]);
+ writel (port_status, status_reg);
} else
ehci_dbg (ehci, "port %d high speed\n", index + 1);
@@ -316,6 +317,7 @@ static int ehci_hub_control (
) {
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int ports = HCS_N_PORTS (ehci->hcs_params);
+ u32 __iomem *status_reg;
u32 temp, status;
unsigned long flags;
int retval = 0;
@@ -327,6 +329,7 @@ static int ehci_hub_control (
* power, "this is the one", etc. EHCI spec supports this.
*/
+ status_reg = &ehci->regs->port_status [wIndex - 1];
spin_lock_irqsave (&ehci->lock, flags);
switch (typeReq) {
case ClearHubFeature:
@@ -343,18 +346,16 @@ static int ehci_hub_control (
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
- temp = readl (&ehci->regs->port_status [wIndex]);
+ temp = readl (status_reg);
if (temp & PORT_OWNER)
break;
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
- writel (temp & ~PORT_PE,
- &ehci->regs->port_status [wIndex]);
+ writel (temp & ~PORT_PE, status_reg);
break;
case USB_PORT_FEAT_C_ENABLE:
- writel((temp & ~PORT_RWC_BITS) | PORT_PEC,
- &ehci->regs->port_status [wIndex]);
+ writel((temp & ~PORT_RWC_BITS) | PORT_PEC, status_reg);
break;
case USB_PORT_FEAT_SUSPEND:
if (temp & PORT_RESET)
@@ -366,8 +367,7 @@ static int ehci_hub_control (
goto error;
/* resume signaling for 20 msec */
temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
- writel (temp | PORT_RESUME,
- &ehci->regs->port_status [wIndex]);
+ writel (temp | PORT_RESUME, status_reg);
ehci->reset_done [wIndex] = jiffies
+ msecs_to_jiffies (20);
}
@@ -378,15 +378,13 @@ static int ehci_hub_control (
case USB_PORT_FEAT_POWER:
if (HCS_PPC (ehci->hcs_params))
writel (temp & ~(PORT_RWC_BITS | PORT_POWER),
- &ehci->regs->port_status [wIndex]);
+ status_reg);
break;
case USB_PORT_FEAT_C_CONNECTION:
- writel((temp & ~PORT_RWC_BITS) | PORT_CSC,
- &ehci->regs->port_status [wIndex]);
+ writel((temp & ~PORT_RWC_BITS) | PORT_CSC, status_reg);
break;
case USB_PORT_FEAT_C_OVER_CURRENT:
- writel((temp & ~PORT_RWC_BITS) | PORT_OCC,
- &ehci->regs->port_status [wIndex]);
+ writel((temp & ~PORT_RWC_BITS) | PORT_OCC, status_reg);
break;
case USB_PORT_FEAT_C_RESET:
/* GetPortStatus clears reset */
@@ -410,7 +408,7 @@ static int ehci_hub_control (
goto error;
wIndex--;
status = 0;
- temp = readl (&ehci->regs->port_status [wIndex]);
+ temp = readl (status_reg);
// wPortChange bits
if (temp & PORT_CSC)
@@ -428,11 +426,10 @@ static int ehci_hub_control (
ehci->reset_done [wIndex] = 0;
/* stop resume signaling */
- temp = readl (&ehci->regs->port_status [wIndex]);
+ temp = readl (status_reg);
writel (temp & ~(PORT_RWC_BITS | PORT_RESUME),
- &ehci->regs->port_status [wIndex]);
- retval = handshake (
- &ehci->regs->port_status [wIndex],
+ status_reg);
+ retval = handshake (status_reg,
PORT_RESUME, 0, 2000 /* 2msec */);
if (retval != 0) {
ehci_err (ehci, "port %d resume error %d\n",
@@ -451,12 +448,11 @@ static int ehci_hub_control (
/* force reset to complete */
writel (temp & ~(PORT_RWC_BITS | PORT_RESET),
- &ehci->regs->port_status [wIndex]);
+ status_reg);
/* REVISIT: some hardware needs 550+ usec to clear
* this bit; seems too long to spin routinely...
*/
- retval = handshake (
- &ehci->regs->port_status [wIndex],
+ retval = handshake (status_reg,
PORT_RESET, 0, 750);
if (retval != 0) {
ehci_err (ehci, "port %d reset error %d\n",
@@ -465,8 +461,8 @@ static int ehci_hub_control (
}
/* see what we found out */
- temp = check_reset_complete (ehci, wIndex,
- readl (&ehci->regs->port_status [wIndex]));
+ temp = check_reset_complete (ehci, wIndex, status_reg,
+ readl (status_reg));
}
// don't show wPortStatus if it's owned by a companion hc
@@ -509,7 +505,7 @@ static int ehci_hub_control (
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
- temp = readl (&ehci->regs->port_status [wIndex]);
+ temp = readl (status_reg);
if (temp & PORT_OWNER)
break;
@@ -523,13 +519,11 @@ static int ehci_hub_control (
goto error;
if (device_may_wakeup(&hcd->self.root_hub->dev))
temp |= PORT_WAKE_BITS;
- writel (temp | PORT_SUSPEND,
- &ehci->regs->port_status [wIndex]);
+ writel (temp | PORT_SUSPEND, status_reg);
break;
case USB_PORT_FEAT_POWER:
if (HCS_PPC (ehci->hcs_params))
- writel (temp | PORT_POWER,
- &ehci->regs->port_status [wIndex]);
+ writel (temp | PORT_POWER, status_reg);
break;
case USB_PORT_FEAT_RESET:
if (temp & PORT_RESUME)
@@ -557,7 +551,7 @@ static int ehci_hub_control (
ehci->reset_done [wIndex] = jiffies
+ msecs_to_jiffies (50);
}
- writel (temp, &ehci->regs->port_status [wIndex]);
+ writel (temp, status_reg);
break;
default:
goto error;
Index: usb-2.6/drivers/usb/host/ehci-hub.c
===================================================================
--- usb-2.6.orig/drivers/usb/host/ehci-hub.c
+++ usb-2.6/drivers/usb/host/ehci-hub.c
@@ -240,15 +240,14 @@ ehci_hub_status_data (struct usb_hcd *hc
spin_lock_irqsave (&ehci->lock, flags);
for (i = 0; i < ports; i++) {
temp = readl (&ehci->regs->port_status [i]);
- if (temp & PORT_OWNER) {
- /* don't report this in GetPortStatus */
- if (temp & PORT_CSC) {
- temp &= ~PORT_RWC_BITS;
- temp |= PORT_CSC;
- writel (temp, &ehci->regs->port_status [i]);
- }
- continue;
- }
+
+ /*
+ * Return status information even for ports with OWNER set.
+ * Otherwise khubd wouldn't see the disconnect event when a
+ * high-speed device is switched over to the companion
+ * controller by the user.
+ */
+
if (!(temp & PORT_CONNECT))
ehci->reset_done [i] = 0;
if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0
@@ -347,8 +346,13 @@ static int ehci_hub_control (
goto error;
wIndex--;
temp = readl (status_reg);
- if (temp & PORT_OWNER)
- break;
+
+ /*
+ * Even if OWNER is set, so the port is owned by the
+ * companion controller, khubd needs to be able to clear
+ * the port-change status bits (especially
+ * USB_PORT_FEAT_C_CONNECTION).
+ */
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
@@ -465,24 +469,27 @@ static int ehci_hub_control (
readl (status_reg));
}
- // don't show wPortStatus if it's owned by a companion hc
- if (!(temp & PORT_OWNER)) {
- if (temp & PORT_CONNECT) {
- status |= 1 << USB_PORT_FEAT_CONNECTION;
- // status may be from integrated TT
- status |= ehci_port_speed(ehci, temp);
- }
- if (temp & PORT_PE)
- status |= 1 << USB_PORT_FEAT_ENABLE;
- if (temp & (PORT_SUSPEND|PORT_RESUME))
- status |= 1 << USB_PORT_FEAT_SUSPEND;
- if (temp & PORT_OC)
- status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
- if (temp & PORT_RESET)
- status |= 1 << USB_PORT_FEAT_RESET;
- if (temp & PORT_POWER)
- status |= 1 << USB_PORT_FEAT_POWER;
+ /*
+ * Even if OWNER is set, there's no harm letting khubd
+ * see the wPortStatus values (they should all be 0 except
+ * for PORT_POWER anyway).
+ */
+
+ if (temp & PORT_CONNECT) {
+ status |= 1 << USB_PORT_FEAT_CONNECTION;
+ // status may be from integrated TT
+ status |= ehci_port_speed(ehci, temp);
}
+ if (temp & PORT_PE)
+ status |= 1 << USB_PORT_FEAT_ENABLE;
+ if (temp & (PORT_SUSPEND|PORT_RESUME))
+ status |= 1 << USB_PORT_FEAT_SUSPEND;
+ if (temp & PORT_OC)
+ status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
+ if (temp & PORT_RESET)
+ status |= 1 << USB_PORT_FEAT_RESET;
+ if (temp & PORT_POWER)
+ status |= 1 << USB_PORT_FEAT_POWER;
#ifndef EHCI_VERBOSE_DEBUG
if (status & ~0xffff) /* only if wPortChange is interesting */
Index: usb-2.6/drivers/usb/host/ehci-hcd.c
===================================================================
--- usb-2.6.orig/drivers/usb/host/ehci-hcd.c
+++ usb-2.6/drivers/usb/host/ehci-hcd.c
@@ -384,6 +384,7 @@ static void ehci_stop (struct usb_hcd *h
writel (0, &ehci->regs->configured_flag);
unregister_reboot_notifier (&ehci->reboot_notifier);
+ remove_companion_file(ehci);
remove_debug_files (ehci);
/* root hub is shut down separately (first, when possible) */
@@ -560,6 +561,7 @@ static int ehci_run (struct usb_hcd *hcd
* since the class device isn't created that early.
*/
create_debug_files(ehci);
+ create_companion_file(ehci);
return 0;
}
Index: usb-2.6/drivers/usb/host/ehci-hub.c
===================================================================
--- usb-2.6.orig/drivers/usb/host/ehci-hub.c
+++ usb-2.6/drivers/usb/host/ehci-hub.c
@@ -173,6 +173,110 @@ static int ehci_bus_resume (struct usb_h
/*-------------------------------------------------------------------------*/
+/* Display the ports dedicated to the companion controller */
+static ssize_t
+show_companion (struct class_device *class_dev, char *buf)
+{
+ struct usb_bus *bus;
+ struct ehci_hcd *ehci;
+ int count = PAGE_SIZE;
+ int index, nports, n;
+ char *ptr = buf;
+
+ bus = class_get_devdata(class_dev);
+ ehci = hcd_to_ehci (bus->hcpriv);
+ nports = HCS_N_PORTS (ehci->hcs_params);
+
+ for (index = 0; index < nports; ++index) {
+ if (test_bit(index, &ehci->companion_ports)) {
+ n = scnprintf(ptr, count, "%d\n", index + 1);
+ ptr += n;
+ count -= n;
+ }
+ }
+ return ptr - buf;
+}
+
+/*
+ * Dedicate or undedicate a port to the companion controller.
+ * Syntax is "[-]portnum", where a leading '-' sign means
+ * return control of the port to the EHCI controller.
+ */
+static ssize_t
+store_companion (struct class_device *class_dev, const char *buf, size_t count)
+{
+ struct usb_bus *bus;
+ struct ehci_hcd *ehci;
+ int portnum, new_owner, try;
+ u32 __iomem *status_reg;
+ u32 port_status;
+
+ bus = class_get_devdata(class_dev);
+ ehci = hcd_to_ehci (bus->hcpriv);
+
+ new_owner = PORT_OWNER; /* Owned by companion */
+ if (sscanf (buf, "%d", &portnum) != 1)
+ return -EINVAL;
+ if (portnum < 0) {
+ portnum = - portnum;
+ new_owner = 0; /* Owned by EHCI */
+ }
+ if (portnum <= 0 || portnum > HCS_N_PORTS (ehci->hcs_params))
+ return -ENOENT;
+ status_reg = &ehci->regs->port_status [--portnum];
+ if (new_owner)
+ set_bit (portnum, &ehci->companion_ports);
+ else
+ clear_bit (portnum, &ehci->companion_ports);
+
+ /*
+ * The controller won't set the OWNER bit if the port is
+ * enabled, so this loop will sometimes require at least two
+ * iterations: one to disable the port and one to set OWNER.
+ */
+
+ for (try = 4; try > 0; --try) {
+ spin_lock_irq (&ehci->lock);
+ port_status = readl (status_reg);
+ if ((port_status & PORT_OWNER) == new_owner
+ || (port_status & (PORT_OWNER | PORT_CONNECT))
+ == 0)
+ try = 0;
+ else {
+ port_status ^= PORT_OWNER;
+ port_status &= ~(PORT_PE | PORT_RWC_BITS);
+ writel (port_status, status_reg);
+ }
+ spin_unlock_irq (&ehci->lock);
+ if (try > 1)
+ msleep(5);
+ }
+ return count;
+}
+static CLASS_DEVICE_ATTR (companion, S_IRUGO | S_IWUSR,
+ show_companion, store_companion);
+
+static inline void create_companion_file (struct ehci_hcd *ehci)
+{
+ struct class_device *cldev = ehci_to_hcd(ehci)->self.class_dev;
+
+ /* with integrated TT there is no companion! */
+ if (!ehci_is_TDI(ehci))
+ class_device_create_file(cldev, &class_device_attr_companion);
+}
+
+static inline void remove_companion_file (struct ehci_hcd *ehci)
+{
+ struct class_device *cldev = ehci_to_hcd(ehci)->self.class_dev;
+
+ /* with integrated TT there is no companion! */
+ if (!ehci_is_TDI(ehci))
+ class_device_remove_file(cldev, &class_device_attr_companion);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
static int check_reset_complete (
struct ehci_hcd *ehci,
int index,
@@ -468,6 +572,15 @@ static int ehci_hub_control (
temp = check_reset_complete (ehci, wIndex, status_reg,
readl (status_reg));
}
+ /* transfer dedicated ports to the companion hc */
+ if ((temp & PORT_CONNECT) && test_bit (wIndex,
+ &ehci->companion_ports)) {
+ temp &= ~PORT_RWC_BITS;
+ temp |= PORT_OWNER;
+ writel (temp, status_reg);
+ ehci_dbg (ehci, "port %d --> companion\n", wIndex + 1);
+ temp = readl (status_reg);
+ }
/*
* Even if OWNER is set, there's no harm letting khubd
Index: usb-2.6/drivers/usb/host/ehci.h
===================================================================
--- usb-2.6.orig/drivers/usb/host/ehci.h
+++ usb-2.6/drivers/usb/host/ehci.h
@@ -74,6 +74,7 @@ struct ehci_hcd { /* one per controlle
/* per root hub port */
unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
+ unsigned long companion_ports; /* bitmask */
/* per-HC memory pools (could be per-bus, but ...) */
struct dma_pool *qh_pool; /* qh per active urb */
-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Linux-usb-users@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-users