[PATCH v2 13/14] USB: ratelimit port power transitions

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Prevent a recovery result of:

  "hub 3-0:1.0: Cannot enable port 1.  Maybe the USB cable is bad?"

Once a populated port has been resumed, do not allow it to be suspended
again until it has gone through a recovery cycle (reset_resume).  It has
been observed that bouncing power without an intervening recovery
results in the endpoint device failing to reconnect.

Force an autosuspend timeout and a recovery of the port before allowing
power to be removed again.

Signed-off-by: Dan Williams <dan.j.williams@xxxxxxxxx>
---
 drivers/usb/core/hub.h  |    1 +
 drivers/usb/core/port.c |   23 ++++++++++++++++++++++-
 2 files changed, 23 insertions(+), 1 deletions(-)

diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 733d4ef53683..64958ff59ad4 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -87,6 +87,7 @@ struct usb_port {
 	struct usb_device *child;
 	struct device dev;
 	struct dev_state *port_owner;
+	struct work_struct ratelimit_work;
 	enum usb_port_connect_type connect_type;
 	u8 portnum;
 };
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 977e5b137b79..4f63c49df162 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -97,9 +97,24 @@ static void usb_port_device_release(struct device *dev)
 {
 	struct usb_port *port_dev = to_usb_port(dev);
 
+	cancel_work_sync(&port_dev->ratelimit_work);
 	kfree(port_dev);
 }
 
+static void pm_ping_child(struct work_struct *w)
+{
+	struct usb_port *port_dev;
+	struct usb_device *udev;
+
+	port_dev = container_of(w, typeof(*port_dev), ratelimit_work);
+	udev = usb_port_get_child(port_dev);
+	if (udev) {
+		pm_runtime_get_sync(&udev->dev);
+		pm_runtime_put_autosuspend(&udev->dev);
+	}
+	usb_port_put_child(udev);
+}
+
 #ifdef CONFIG_PM_RUNTIME
 static int usb_port_runtime_resume(struct device *dev)
 {
@@ -117,9 +132,13 @@ static int usb_port_runtime_resume(struct device *dev)
 	if (test_bit(port1, hub->poweroff_bits))
 		retval = usb_set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 
-	/* no child? we're done recovering this port */
+	/* no child? we're done recovering this port, otherwise try to
+	 * recover the device connection to rate limit power toggling
+	 */
 	if (!port_dev->child)
 		usb_clear_port_poweroff(hub, port1);
+	else
+		schedule_work(&port_dev->ratelimit_work);
 	usb_autopm_put_interface(intf);
 
 	return retval;
@@ -156,6 +175,7 @@ static int usb_port_runtime_suspend(struct device *dev)
 	int port1 = port_dev->portnum;
 	int retval;
 
+	flush_work(&port_dev->ratelimit_work);
 	if (!hub)
 		return -EINVAL;
 
@@ -251,6 +271,7 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
 	port_dev->dev.parent = hub->intfdev;
 	port_dev->dev.groups = port_dev_group;
 	port_dev->dev.type = &usb_port_device_type;
+	INIT_WORK(&port_dev->ratelimit_work, pm_ping_child);
 	dev_set_name(&port_dev->dev, "port%d", port1);
 
 	retval = device_register(&port_dev->dev);

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




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux