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

Reply via email to