I have written a patch based on the companion patch that will allow me to disable arbitrary ports on my EHCI root hubs for a product we are working on (The system has a very naughty built on compact flash card reader that cannot be disabled in bios) and I want to make sure this is the best way to do it. Also, it seems to have one simple issue. If there is something plugged into the port, and I disable it, khubd never seems to notice the event, so it will only see the port is disabled after I try to access the device. Is there any way to fake a port disconnect event so that I can get it to rescan the ports? The same applies to me re-enabling a port that already has something pluged into it.

I guess basically this patch seems to work, except I need to figure out a way to fake port disconnect/connect events. Is this possible? The reason I would need this is all of our USB devices are pretty much hardwired and always exist, so there is only one plug event at boot time.

Thanks,
Mike


--- kernel-2.6.16.19/linux-2.6.16.19/drivers/usb/host/ehci-hcd.c        
2006-09-26 15:53:34.000000000 -0400
+++ kernel-2.6.16.19-mike/linux-2.6.16.19/drivers/usb/host/ehci-hcd.c   
2006-09-26 15:33:05.000000000 -0400
@@ -385,6 +385,7 @@
        unregister_reboot_notifier (&ehci->reboot_notifier);
 
        remove_companion_file(ehci);
+       remove_enable_file(ehci);
        remove_debug_files (ehci);
 
        /* root hub is shut down separately (first, when possible) */
@@ -413,6 +414,8 @@
        int                     retval;
        u32                     hcc_params;
 
+       ehci->enabled_ports = 0xFFFFFFFF & ~(3 << 4);  // Enable all ports 
except 5 and 6
+
        spin_lock_init(&ehci->lock);
 
        init_timer(&ehci->watchdog);
@@ -562,6 +565,7 @@
         */
        create_debug_files(ehci);
        create_companion_file(ehci);
+       create_enable_file(ehci);
 
        return 0;
 }
--- kernel-2.6.16.19/linux-2.6.16.19/drivers/usb/host/ehci-hub.c        
2006-09-26 15:53:34.000000000 -0400
+++ kernel-2.6.16.19-mike/linux-2.6.16.19/drivers/usb/host/ehci-hub.c   
2006-09-26 15:00:44.000000000 -0400
@@ -197,6 +197,30 @@
        return ptr - buf;
 }
 
+/* Display the ports dedicated to the companion controller */
+static ssize_t
+show_pestatus (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->enabled_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
@@ -253,6 +277,53 @@
        }
        return count;
 }
+/*
+ * Enable or disable a port.
+ * Syntax is "[-]portnum", where a leading '-' sign means
+ * disable the port on the EHCI controller.
+ */
+static ssize_t
+store_pestatus (struct class_device *class_dev, const char *buf, size_t count)
+{
+       struct usb_bus          *bus;
+       struct ehci_hcd         *ehci;
+       int                     portnum, enabled, try;
+       u32 __iomem             *status_reg;
+       u32                     port_status;
+
+       bus = class_get_devdata(class_dev);
+       ehci = hcd_to_ehci (bus->hcpriv);
+
+       enabled = PORT_PE;
+       if (sscanf (buf, "%d", &portnum) != 1)
+               return -EINVAL;
+       if (portnum < 0) {
+               portnum = - portnum;
+               enabled = 0;            /* Port disabled. */
+       }
+       if (portnum <= 0 || portnum > HCS_N_PORTS (ehci->hcs_params))
+               return -ENOENT;
+       status_reg = &ehci->regs->port_status [--portnum];
+       if (enabled)
+               set_bit (portnum, &ehci->enabled_ports);
+       else
+               clear_bit (portnum, &ehci->enabled_ports);
+
+       spin_lock_irq (&ehci->lock);
+       port_status = readl (status_reg);
+       if (enabled) {  // Request to enable port, we cant directly enable it, 
we have to reset it
+               port_status |= PORT_RESET;
+               port_status &= ~PORT_PE;
+       } else {
+               port_status &= ~PORT_PE;
+       }
+       port_status &= ~(PORT_RWC_BITS);
+       writel (port_status, status_reg);
+       spin_unlock_irq (&ehci->lock);
+
+       return count;
+}
+
 static CLASS_DEVICE_ATTR (companion, S_IRUGO | S_IWUSR,
                show_companion, store_companion);
 
@@ -274,6 +345,23 @@
                class_device_remove_file(cldev, &class_device_attr_companion);
 }
 
+static CLASS_DEVICE_ATTR (enable, S_IRUGO | S_IWUSR,
+               show_pestatus, store_pestatus);
+
+static inline void create_enable_file (struct ehci_hcd *ehci)
+{
+       struct class_device *cldev = ehci_to_hcd(ehci)->self.class_dev;
+
+       class_device_create_file(cldev, &class_device_attr_enable);
+}
+
+static inline void remove_enable_file (struct ehci_hcd *ehci)
+{
+       struct class_device *cldev = ehci_to_hcd(ehci)->self.class_dev;
+
+       class_device_remove_file(cldev, &class_device_attr_enable);
+}
+
 
 /*-------------------------------------------------------------------------*/
 
@@ -431,11 +519,11 @@
         * (track current state ourselves) ... blink for diagnostics,
         * power, "this is the one", etc.  EHCI spec supports this.
         */
-//     printk("%s: Called for port %d\n", __FUNCTION__, wIndex);
-       if ((wIndex == 5) || (wIndex == 6)) {
+       printk("%s: Called for port %d\n", __FUNCTION__, wIndex);
+//     if ((wIndex == 5) || (wIndex == 6)) {
 //             printk("%s: Ignoring dead port %d.\n", __FUNCTION__, wIndex);
-               return -EPIPE;
-       }
+//             return -EPIPE;
+//     }
    
        status_reg = &ehci->regs->port_status [wIndex - 1];   
        spin_lock_irqsave (&ehci->lock, flags);
@@ -555,6 +643,7 @@
                }
 
                /* whoever resets must GetPortStatus to complete it!! */
+               //if ((temp & PORT_RESET) && test_bit(wIndex, 
&ehci->enabled_ports)
                if ((temp & PORT_RESET)
                                && time_after (jiffies,
                                        ehci->reset_done [wIndex])) {
@@ -661,26 +750,31 @@
                         * which can be fine if this root hub has a
                         * transaction translator built in.
                         */
-                       if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
-                                       && !ehci_is_TDI(ehci)
-                                       && PORT_USB11 (temp)) {
-                               ehci_dbg (ehci,
-                                       "port %d low speed --> companion\n",
-                                       wIndex + 1);
-                               temp |= PORT_OWNER;
+                       if (test_bit(wIndex, &ehci->enabled_ports)) { 
+                               printk("Port reset requested, and port enabled. 
(port %d)\n", wIndex + 1);
+                               if ((temp & (PORT_PE|PORT_CONNECT)) == 
PORT_CONNECT
+                                               && !ehci_is_TDI(ehci)
+                                               && PORT_USB11 (temp)) {
+                                       ehci_dbg (ehci,
+                                               "port %d low speed --> 
companion\n",
+                                               wIndex + 1);
+                                       temp |= PORT_OWNER;
+                               } else {
+                                       ehci_vdbg (ehci, "port %d reset\n", 
wIndex + 1);
+                                       temp |= PORT_RESET;
+                                       temp &= ~PORT_PE;
+
+                                       /*
+                                        * caller must wait, then call 
GetPortStatus
+                                        * usb 2.0 spec says 50 ms resets on 
root
+                                        */
+                                       ehci->reset_done [wIndex] = jiffies
+                                                       + msecs_to_jiffies (50);
+                               }
+                               writel (temp, status_reg);
                        } else {
-                               ehci_vdbg (ehci, "port %d reset\n", wIndex + 1);
-                               temp |= PORT_RESET;
-                               temp &= ~PORT_PE;
-
-                               /*
-                                * caller must wait, then call GetPortStatus
-                                * usb 2.0 spec says 50 ms resets on root
-                                */
-                               ehci->reset_done [wIndex] = jiffies
-                                               + msecs_to_jiffies (50);
+                               printk("Port reset requested, and port 
disabled. (port %d)\n", wIndex + 1);
                        }
-                       writel (temp, status_reg);
                        break;
                default:
                        goto error;
--- kernel-2.6.16.19/linux-2.6.16.19/drivers/usb/host/ehci.h    2006-09-26 
15:54:49.000000000 -0400
+++ kernel-2.6.16.19-mike/linux-2.6.16.19/drivers/usb/host/ehci.h       
2006-09-26 11:38:22.000000000 -0400
@@ -75,6 +75,7 @@
        /* per root hub port */
        unsigned long           reset_done [EHCI_MAX_ROOT_PORTS];
        unsigned long           companion_ports;        /* bitmask */
+       unsigned long           enabled_ports;  /* bitmask */
 
        /* per-HC memory pools (could be per-bus, but ...) */
        struct dma_pool         *qh_pool;       /* qh per active urb */
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to