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