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