USB: force handover port to companion when hub_port_connect_change fails
authorBalaji Rao <balajirrao@gmail.com>
Wed, 21 Nov 2007 20:28:14 +0000 (01:58 +0530)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 1 Feb 2008 22:34:52 +0000 (14:34 -0800)
This patch hands over the port to the companion when the
hub_port_connect_change fails.

Signed-off-by: Balaji Rao <balajirrao@gmail.com>
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/core/hcd.h
drivers/usb/core/hub.c
drivers/usb/host/ehci-au1xxx.c
drivers/usb/host/ehci-fsl.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci-pci.c
drivers/usb/host/ehci-ppc-soc.c
drivers/usb/host/ehci-ps3.c

index 98e2419..080298a 100644 (file)
@@ -210,6 +210,9 @@ struct hc_driver {
        int             (*start_port_reset)(struct usb_hcd *, unsigned port_num);
        void            (*hub_irq_enable)(struct usb_hcd *);
                /* Needed only if port-change IRQs are level-triggered */
+
+               /* force handover of high-speed port to full-speed companion */
+       void            (*relinquish_port)(struct usb_hcd *, int);
 };
 
 extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
index 1b17f63..24c4116 100644 (file)
@@ -2482,6 +2482,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
 {
        struct usb_device *hdev = hub->hdev;
        struct device *hub_dev = hub->intfdev;
+       struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
        u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
        int status, i;
  
@@ -2645,6 +2646,8 @@ loop:
  
 done:
        hub_port_disable(hub, port1, 1);
+       if (hcd->driver->relinquish_port && !hub->hdev->parent)
+               hcd->driver->relinquish_port(hcd, port1);
 }
 
 static void hub_events(void)
index 766ef68..da7532d 100644 (file)
@@ -222,6 +222,7 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
        .hub_control = ehci_hub_control,
        .bus_suspend = ehci_bus_suspend,
        .bus_resume = ehci_bus_resume,
+       .relinquish_port = ehci_relinquish_port,
 };
 
 /*-------------------------------------------------------------------------*/
index 430821c..f35795f 100644 (file)
@@ -323,6 +323,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
        .hub_control = ehci_hub_control,
        .bus_suspend = ehci_bus_suspend,
        .bus_resume = ehci_bus_resume,
+       .relinquish_port = ehci_relinquish_port,
 };
 
 static int ehci_fsl_drv_probe(struct platform_device *pdev)
index 1ad7a65..a165e0a 100644 (file)
@@ -314,41 +314,21 @@ static ssize_t show_companion(struct device *dev,
 }
 
 /*
- * 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.
+ * Sets the owner of a port
  */
-static ssize_t store_companion(struct device *dev,
-                              struct device_attribute *attr,
-                              const char *buf, size_t count)
+static void set_owner(struct ehci_hcd *ehci, int portnum, int new_owner)
 {
-       struct ehci_hcd         *ehci;
-       int                     portnum, new_owner, try;
        u32 __iomem             *status_reg;
        u32                     port_status;
+       int                     try;
 
-       ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev)));
-       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);
+       status_reg = &ehci->regs->port_status[portnum];
 
        /*
         * 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 = ehci_readl(ehci, status_reg);
@@ -365,6 +345,36 @@ static ssize_t store_companion(struct device *dev,
                if (try > 1)
                        msleep(5);
        }
+}
+
+/*
+ * 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 device *dev,
+                              struct device_attribute *attr,
+                              const char *buf, size_t count)
+{
+       struct ehci_hcd         *ehci;
+       int                     portnum, new_owner;
+
+       ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev)));
+       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;
+       portnum--;
+       if (new_owner)
+               set_bit(portnum, &ehci->companion_ports);
+       else
+               clear_bit(portnum, &ehci->companion_ports);
+       set_owner(ehci, portnum, new_owner);
        return count;
 }
 static DEVICE_ATTR(companion, 0644, show_companion, store_companion);
@@ -867,3 +877,13 @@ error:
        spin_unlock_irqrestore (&ehci->lock, flags);
        return retval;
 }
+
+static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum)
+{
+       struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
+
+       if (ehci_is_TDI(ehci))
+               return;
+       set_owner(ehci, --portnum, PORT_OWNER);
+}
+
index ad0d496..45e0400 100644 (file)
@@ -364,6 +364,7 @@ static const struct hc_driver ehci_pci_hc_driver = {
        .hub_control =          ehci_hub_control,
        .bus_suspend =          ehci_bus_suspend,
        .bus_resume =           ehci_bus_resume,
+       .relinquish_port =      ehci_relinquish_port,
 };
 
 /*-------------------------------------------------------------------------*/
index 452d4b1..a324907 100644 (file)
@@ -162,6 +162,7 @@ static const struct hc_driver ehci_ppc_soc_hc_driver = {
        .hub_control = ehci_hub_control,
        .bus_suspend = ehci_bus_suspend,
        .bus_resume = ehci_bus_resume,
+       .relinquish_port = ehci_relinquish_port,
 };
 
 static int ehci_hcd_ppc_soc_drv_probe(struct platform_device *pdev)
index 03a6b2f..bbda58e 100644 (file)
@@ -72,6 +72,7 @@ static const struct hc_driver ps3_ehci_hc_driver = {
        .bus_suspend            = ehci_bus_suspend,
        .bus_resume             = ehci_bus_resume,
 #endif
+       .relinquish_port        = ehci_relinquish_port,
 };
 
 static int ps3_ehci_probe(struct ps3_system_bus_device *dev)