The reset_resume implementation is in place to recover lost power
sessions.  Testing has already shown that simply waiting for
USB_PORT_STAT_C_CONNECTION is not sufficient for recovering a powered
down port.  Replace recovery actions in usb_port_runtime_resume() with a
deferral to recovery via usb_port_resume().

>From the point the port is powered down to the point that the port is
recovered we do not want khubd or hub_activate() to take action on the
disabled port.  Introduce hub->poweroff_bits to communicate that the
port is not to be serviced until it has been through recovery.

The existing ->power_is_on flag gates hub_activate() from setting change
bits.  Placing the evalaution of the power state there is insufficient
for guaranteeing that khubd does not receive a connection change event.
Also, hub_activate() in this case is only running on hub_resume() we
want to manage change events as individual ports resume.  ->power_is_on
will be removed in a subsequent patch.

Signed-off-by: Dan Williams <dan.j.willi...@intel.com>
---
 drivers/usb/core/hub.c  |   22 +++++++++++++++++-----
 drivers/usb/core/hub.h  |   16 +---------------
 drivers/usb/core/port.c |   29 +++++++++--------------------
 3 files changed, 27 insertions(+), 40 deletions(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 06cec635e703..6f2cf4672942 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1092,6 +1092,9 @@ static void hub_activate(struct usb_hub *hub, enum 
hub_activation_type type)
                struct usb_device *udev = hub->ports[port1 - 1]->child;
                u16 portstatus, portchange;
 
+               if (test_bit(port1, hub->poweroff_bits))
+                       continue;
+
                portstatus = portchange = 0;
                status = hub_port_status(hub, port1, &portstatus, &portchange);
                if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
@@ -2783,8 +2786,14 @@ static int check_port_resume_type(struct usb_device 
*udev,
                struct usb_hub *hub, int port1,
                int status, unsigned portchange, unsigned portstatus)
 {
+       /* port power recovery, proceed to resetting the device */
+       if (test_bit(port1, hub->poweroff_bits)) {
+               status = 0;
+               udev->reset_resume = 1;
+       }
+
        /* Is the device still present? */
-       if (status || port_is_suspended(hub, portstatus) ||
+       else if (status || port_is_suspended(hub, portstatus) ||
                        !port_is_power_on(hub, portstatus) ||
                        !(portstatus & USB_PORT_STAT_CONNECTION)) {
                if (status >= 0)
@@ -3871,7 +3880,7 @@ EXPORT_SYMBOL_GPL(usb_enable_ltm);
  * every 25ms for transient disconnects.  When the port status has been
  * unchanged for 100ms it returns the port status.
  */
-int hub_port_debounce(struct usb_hub *hub, int port1, bool must_be_connected)
+int hub_port_debounce(struct usb_hub *hub, int port1)
 {
        int ret;
        int total_time, stable_time = 0;
@@ -3885,8 +3894,7 @@ int hub_port_debounce(struct usb_hub *hub, int port1, 
bool must_be_connected)
 
                if (!(portchange & USB_PORT_STAT_C_CONNECTION) &&
                     (portstatus & USB_PORT_STAT_CONNECTION) == connection) {
-                       if (!must_be_connected ||
-                            (connection == USB_PORT_STAT_CONNECTION))
+                       if (connection == USB_PORT_STAT_CONNECTION)
                                stable_time += HUB_DEBOUNCE_STEP;
                        if (stable_time >= HUB_DEBOUNCE_STABLE)
                                break;
@@ -4437,7 +4445,7 @@ static void hub_port_connect_change(struct usb_hub *hub, 
int port1,
 
        if (portchange & (USB_PORT_STAT_C_CONNECTION |
                                USB_PORT_STAT_C_ENABLE)) {
-               status = hub_port_debounce_be_stable(hub, port1);
+               status = hub_port_debounce(hub, port1);
                if (status < 0) {
                        if (status != -ENODEV && printk_ratelimit())
                                dev_err(hub_dev, "connect-debounce failed, "
@@ -4749,6 +4757,9 @@ static void hub_events(void)
                                connect_change = 1;
                        }
 
+                       if (test_bit(i, hub->poweroff_bits))
+                               continue;
+
                        if (portchange & USB_PORT_STAT_C_ENABLE) {
                                if (!connect_change)
                                        dev_dbg (hub_dev,
@@ -5155,6 +5166,7 @@ static int usb_reset_and_verify_device(struct usb_device 
*udev)
                if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV)
                        break;
        }
+       clear_bit(port1, parent_hub->poweroff_bits);
        clear_bit(port1, parent_hub->busy_bits);
 
        if (ret < 0)
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 4e4790dea343..0b9bde67ff8b 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -47,6 +47,7 @@ struct usb_hub {
                                                        status change */
        unsigned long           busy_bits[1];   /* ports being reset or
                                                        resumed */
+       unsigned long           poweroff_bits[1]; /* ports logically off */
        unsigned long           removed_bits[1]; /* ports with a "removed"
                                                        device present */
        unsigned long           wakeup_bits[1]; /* ports that have signaled
@@ -106,20 +107,5 @@ extern void usb_hub_remove_port_device(struct usb_hub *hub,
 extern int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub,
                int port1, bool set);
 extern struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev);
-extern int hub_port_debounce(struct usb_hub *hub, int port1,
-               bool must_be_connected);
 extern int usb_clear_port_feature(struct usb_device *hdev,
                int port1, int feature);
-
-static inline int hub_port_debounce_be_connected(struct usb_hub *hub,
-               int port1)
-{
-       return hub_port_debounce(hub, port1, true);
-}
-
-static inline int hub_port_debounce_be_stable(struct usb_hub *hub,
-               int port1)
-{
-       return hub_port_debounce(hub, port1, false);
-}
-
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 51542f852393..0ecff7d85739 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -83,26 +83,13 @@ static int usb_port_runtime_resume(struct device *dev)
                return -EINVAL;
 
        usb_autopm_get_interface(intf);
-       set_bit(port1, hub->busy_bits);
-
        retval = usb_hub_set_port_power(hdev, hub, port1, true);
-       if (port_dev->child && !retval) {
-               /*
-                * Attempt to wait for usb hub port to be reconnected in order
-                * to make the resume procedure successful.  The device may have
-                * disconnected while the port was powered off, so ignore the
-                * return status.
-                */
-               retval = hub_port_debounce_be_connected(hub, port1);
-               if (retval < 0)
-                       dev_dbg(&port_dev->dev, "can't get reconnection after 
setting port  power on, status %d\n",
-                                       retval);
-               usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
-               retval = 0;
-       }
-
-       clear_bit(port1, hub->busy_bits);
        usb_autopm_put_interface(intf);
+
+       /* no child? we're done recovering this port */
+       if (!port_dev->child)
+               clear_bit(port1, hub->poweroff_bits);
+
        return retval;
 }
 
@@ -122,13 +109,15 @@ static int usb_port_runtime_suspend(struct device *dev)
                        == PM_QOS_FLAGS_ALL)
                return -EAGAIN;
 
+       set_bit(port1, hub->poweroff_bits);
        usb_autopm_get_interface(intf);
-       set_bit(port1, hub->busy_bits);
        retval = usb_hub_set_port_power(hdev, hub, port1, false);
+       if (retval)
+               clear_bit(port1, hub->poweroff_bits);
        usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
        usb_clear_port_feature(hdev, port1,     USB_PORT_FEAT_C_ENABLE);
-       clear_bit(port1, hub->busy_bits);
        usb_autopm_put_interface(intf);
+
        return retval;
 }
 #endif

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to