On Fri, 26 May 2006, Ezequiel Valenzuela wrote:

> Alan,
> 
> I'm pleased to tell you that the patch you created seems to be working.
> I've improved it slightly to take a "mode" parameter, and it's
> apparently working well.
> 
> However, I'm having a problem now that may or may not exactly fall into
> your area of expertise, but I'll tell you anyway, just in case: as
> you've told me, the port is being reset to the "default" once I unplug
> the device. If I send the command to switch to the companion controller
> (uhci in my case), it seems that the port is not being switched at that
> point yet (or maybe it's being reset once I connect the device). That
> is, If I plug the device, it's still being connected to the "ehci hub".
> It's not until I send the switching command *after* the device has been
> plugged in that I see the device connected to the right (uhci, the
> "companion") controller.
> 
> When I plug the device, I get this:
> 
> May 26 21:17:07 [kernel] [17182057.596000] usb 4-2: new high speed USB
> device using ehci_hcd and address 6
> 
> After I send my command
>  (echo 4-2:1 > /sys/class/usb_host/usb_host4/companion):
> 
> May 26 21:17:36 [kernel] [17182086.168000] usb 1-2: new full speed USB
> device using uhci_hcd and address 6
> May 26 21:17:36 [kernel] [17182086.348000] usb 1-2: not running at top
> speed; connect to a high speed hub
> 
> Then I loaded the ndiswrapper module by hand:
> 
> May 26 21:19:49 [kernel] [17182219.804000] ndiswrapper version 1.15
> loaded (preempt=no,smp=no)
> May 26 21:19:51 [kernel] [17182221.220000] wlan0: vendor: 'Ralink
> Technology Inc.'
> May 26 21:19:51 [kernel] [17182221.232000] wlan0: encryption modes
> supported: WEP; TKIP with WPA, WPA2, WPA2PSK; AES/CCMP with WPA, WPA2,
> WPA2PSK
> May 26 21:19:51 [kernel] [17182221.256000] usbcore: registered new
> driver ndiswrapper
> May 26 21:19:52 [kernel] [17182221.856000] ndiswrapper
> (iw_set_tx_power:458): setting tx_power failed (C0010015)
> May 26 21:19:55 [kernel] [17182224.936000] ndiswrapper
> (set_infra_mode:199): setting operating mode failed (C0010015)
> 
> The problem is obvious now: how can I intercept this device being
> plugged in, so that I can send the command to set the companion
> controller *before* the actual higher level module (ndiswrapper in my
> case) takes over? The idea is that I don't want ndiswrapper "seeing" the
> device connected to the ehci controller, and I think the easier/more
> correct way of achieving this is to have some control (maybe via
> hotplug?) *before* ndiswrapper is being used to control the device to
> set the companion controller, then letting things go as normal
> (hopefully ndiswrapper will then "see" the device connected to the uhci
> controller).
> 
> Any ideas will be much appreciated. Thanks again.

Here is a new patch that works more the way you want.  Entries in the 
"companion" file are persistent; they stay until you remove them.  An 
entry is removed by writing the port number (or bus-port numbers) with a 
'-' sign in front.

Alan Stern


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,123 @@ 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-%d\n",
+                                       bus->busnum, index + 1);
+                       count -= n;
+                       ptr += n;
+               }
+       }
+       return ptr - buf;
+}
+
+/* Dedicate or undedicate a port to the companion controller.
+ * Syntax is "[+|-][busnum-]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                     busnum, 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 */
+       switch (sscanf (buf, "%d-%d.%d", &busnum, &portnum, &try)) {
+       case 1:
+               portnum = busnum;
+               if (portnum < 0) {
+                       portnum = - portnum;
+                       new_owner = 0;          /* Owned by EHCI */
+               }
+               break;
+       case 2:
+               if (busnum < 0) {
+                       busnum = - busnum;
+                       new_owner = 0;          /* Owned by EHCI */
+               }
+               if (busnum != bus->busnum)
+                       return -ENODEV;
+               break;
+       default:
+               return -EINVAL;
+       }
+       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 PORT_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 the 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,
@@ -202,6 +319,10 @@ static int check_reset_complete (
                port_status &= ~PORT_RWC_BITS;
                writel (port_status, &ehci->regs->port_status [index]);
 
+               /* prevent another connect-change status */
+               writel (port_status | PORT_CSC,
+                               &ehci->regs->port_status [index]);
+
        } else
                ehci_dbg (ehci, "port %d high speed\n", index + 1);
 
@@ -239,15 +360,6 @@ 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;
-               }
                if (!(temp & PORT_CONNECT))
                        ehci->reset_done [i] = 0;
                if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0
@@ -316,6 +428,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;
@@ -342,19 +455,15 @@ static int ehci_hub_control (
        case ClearPortFeature:
                if (!wIndex || wIndex > ports)
                        goto error;
-               wIndex--;
-               temp = readl (&ehci->regs->port_status [wIndex]);
-               if (temp & PORT_OWNER)
-                       break;
+               status_reg = &ehci->regs->port_status [--wIndex];
+               temp = readl (status_reg);
 
                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 +475,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 +486,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 */
@@ -408,9 +514,9 @@ static int ehci_hub_control (
        case GetPortStatus:
                if (!wIndex || wIndex > ports)
                        goto error;
-               wIndex--;
+               status_reg = &ehci->regs->port_status [--wIndex];
                status = 0;
-               temp = readl (&ehci->regs->port_status [wIndex]);
+               temp = readl (status_reg);
 
                // wPortChange bits
                if (temp & PORT_CSC)
@@ -428,11 +534,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 +556,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",
@@ -466,11 +570,23 @@ static int ehci_hub_control (
 
                        /* see what we found out */
                        temp = check_reset_complete (ehci, wIndex,
-                               readl (&ehci->regs->port_status [wIndex]));
+                               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);
+
+                       /* prevent another connect-change status */
+                       status &= ~(1 << USB_PORT_FEAT_C_CONNECTION);
+                       writel (temp | PORT_CSC, status_reg);
+                       ehci_dbg (ehci, "port %d --> companion\n", wIndex + 1);
+
                // don't show wPortStatus if it's owned by a companion hc
-               if (!(temp & PORT_OWNER)) {
+               } else if (!(temp & PORT_OWNER)) {
                        if (temp & PORT_CONNECT) {
                                status |= 1 << USB_PORT_FEAT_CONNECTION;
                                // status may be from integrated TT
@@ -508,8 +624,8 @@ static int ehci_hub_control (
        case SetPortFeature:
                if (!wIndex || wIndex > ports)
                        goto error;
-               wIndex--;
-               temp = readl (&ehci->regs->port_status [wIndex]);
+               status_reg = &ehci->regs->port_status [--wIndex];
+               temp = readl (status_reg);
                if (temp & PORT_OWNER)
                        break;
 
@@ -523,13 +639,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 +671,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.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 */



-------------------------------------------------------
All the advantages of Linux Managed Hosting--Without the Cost and Risk!
Fully trained technicians. The highest number of Red Hat certifications in
the hosting industry. Fanatical Support. Click to learn more
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=107521&bid=248729&dat=121642
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-users

Reply via email to