[RFC PATCH 1/4] usb: introduce hub->resume_bits and HCD_FLAG_RESET_RESUME

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

 



Preparation for making reset_resume a port event.  End goal is to
trigger port recovery actions like warm reset when recovering a port
power session.  A usb_device may not be attached to the port, so the
flag for requesting reset service can not be contained within the
usb_device.

Signed-off-by: Dan Williams <dan.j.williams@xxxxxxxxx>
---
 drivers/usb/core/driver.c |   40 +++++++++++++++++++++++++++++++++++-----
 drivers/usb/core/hub.c    |   29 ++++++++++++++++-------------
 drivers/usb/core/hub.h    |    2 ++
 include/linux/usb.h       |    1 -
 include/linux/usb/hcd.h   |    1 +
 5 files changed, 54 insertions(+), 19 deletions(-)

diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 8d989b1d3dc5..6447bfc3e4c1 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -29,7 +29,7 @@
 #include <linux/usb/quirks.h>
 #include <linux/usb/hcd.h>
 
-#include "usb.h"
+#include "hub.h"
 
 
 /*
@@ -1088,6 +1088,36 @@ static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)
 	return status;
 }
 
+#define clear_reset_resume(udev) op_reset_resume(udev, 0)
+#define set_reset_resume(udev) op_reset_resume(udev, 1)
+#define test_reset_resume(udev) op_reset_resume(udev, 2)
+static int op_reset_resume(struct usb_device *udev, int op)
+{
+	unsigned long *addr;
+	int nr, rc = 0;
+
+	if (udev->parent) {
+		struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
+
+		addr = hub->resume_bits;
+		nr = udev->portnum;
+	} else {
+		struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+		addr = &hcd->flags;
+		nr = HCD_FLAG_RESET_RESUME;
+	}
+
+	if (op == 0)
+		clear_bit(nr, addr);
+	else if (op == 1)
+		set_bit(nr, addr);
+	else
+		rc = test_bit(nr, addr);
+
+	return rc;
+}
+
 static int usb_resume_device(struct usb_device *udev, pm_message_t msg)
 {
 	struct usb_device_driver	*udriver;
@@ -1110,7 +1140,7 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg)
 				&udev->bus->hs_companion->root_hub->dev);
 
 	if (udev->quirks & USB_QUIRK_RESET_RESUME)
-		udev->reset_resume = 1;
+		set_reset_resume(udev);
 
 	udriver = to_usb_device_driver(udev->dev.driver);
 	status = udriver->resume(udev, msg);
@@ -1318,7 +1348,7 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
 	udev->can_submit = 1;
 
 	/* Resume the device */
-	if (udev->state == USB_STATE_SUSPENDED || udev->reset_resume)
+	if (udev->state == USB_STATE_SUSPENDED || test_reset_resume(udev))
 		status = usb_resume_device(udev, msg);
 
 	/* Resume the interfaces */
@@ -1326,7 +1356,7 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
 		for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
 			intf = udev->actconfig->interface[i];
 			usb_resume_interface(udev, intf, msg,
-					udev->reset_resume);
+					     test_reset_resume(udev));
 		}
 	}
 	usb_mark_last_busy(udev);
@@ -1334,7 +1364,7 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
  done:
 	dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
 	if (!status)
-		udev->reset_resume = 0;
+		clear_reset_resume(udev);
 	return status;
 }
 
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 162e94dbed53..48d5c8fff77f 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1176,9 +1176,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
 		} else if (udev->persist_enabled) {
 			struct usb_port *port_dev = hub->ports[port1 - 1];
 
-#ifdef CONFIG_PM
-			udev->reset_resume = 1;
-#endif
+			set_bit(port1, hub->resume_bits);
 			/* Don't set the change_bits when the device
 			 * was powered off.
 			 */
@@ -2784,9 +2782,10 @@ static int check_port_resume_type(struct usb_device *udev,
 	/* Can't do a normal resume if the port isn't enabled,
 	 * so try a reset-resume instead.
 	 */
-	else if (!(portstatus & USB_PORT_STAT_ENABLE) && !udev->reset_resume) {
+	else if (!(portstatus & USB_PORT_STAT_ENABLE)
+		 && !test_bit(port1, hub->resume_bits)) {
 		if (udev->persist_enabled)
-			udev->reset_resume = 1;
+			set_bit(port1, hub->resume_bits);
 		else
 			status = -ENODEV;
 	}
@@ -2795,7 +2794,7 @@ static int check_port_resume_type(struct usb_device *udev,
 		dev_dbg(hub->intfdev,
 				"port %d status %04x.%04x after resume, %d\n",
 				port1, portchange, portstatus, status);
-	} else if (udev->reset_resume) {
+	} else if (test_bit(port1, hub->resume_bits)) {
 
 		/* Late port handoff can set status-change bits */
 		if (portchange & USB_PORT_STAT_C_CONNECTION)
@@ -3081,10 +3080,12 @@ static int finish_port_resume(struct usb_device *udev)
 {
 	int	status = 0;
 	u16	devstatus = 0;
+	int	port1 = udev->portnum;
+	struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
 
 	/* caller owns the udev device lock */
-	dev_dbg(&udev->dev, "%s\n",
-		udev->reset_resume ? "finish reset-resume" : "finish resume");
+	dev_dbg(&udev->dev, "%s\n", test_bit(port1, hub->resume_bits)
+		? "finish reset-resume" : "finish resume");
 
 	/* usb ch9 identifies four variants of SUSPENDED, based on what
 	 * state the device resumes to.  Linux currently won't see the
@@ -3100,7 +3101,7 @@ static int finish_port_resume(struct usb_device *udev)
 	 * operation is carried out here, after the port has been
 	 * resumed.
 	 */
-	if (udev->reset_resume)
+	if (test_bit(port1, hub->resume_bits))
  retry_reset_resume:
 		status = usb_reset_and_verify_device(udev);
 
@@ -3113,9 +3114,9 @@ static int finish_port_resume(struct usb_device *udev)
 		status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
 
 		/* If a normal resume failed, try doing a reset-resume */
-		if (status && !udev->reset_resume && udev->persist_enabled) {
+		if (status && udev->persist_enabled
+		    && !test_and_set_bit(port1, hub->resume_bits)) {
 			dev_dbg(&udev->dev, "retry with reset-resume\n");
-			udev->reset_resume = 1;
 			goto retry_reset_resume;
 		}
 	}
@@ -3129,7 +3130,7 @@ static int finish_port_resume(struct usb_device *udev)
 	 * which crash if the feature is cleared, hence check for
 	 * udev->reset_resume
 	 */
-	} else if (udev->actconfig && !udev->reset_resume) {
+	} else if (udev->actconfig && !test_bit(port1, hub->resume_bits)) {
 		if (udev->speed < USB_SPEED_SUPER) {
 			if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
 				status = usb_disable_remote_wakeup(udev);
@@ -3392,8 +3393,10 @@ static int hub_reset_resume(struct usb_interface *intf)
  */
 void usb_root_hub_lost_power(struct usb_device *rhdev)
 {
+	struct usb_hcd *hcd = bus_to_hcd(rhdev->bus);
+
 	dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
-	rhdev->reset_resume = 1;
+	set_bit(HCD_FLAG_RESET_RESUME, &hcd->flags);
 }
 EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
 
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 4e4790dea343..419e7ac51c12 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -51,6 +51,8 @@ struct usb_hub {
 							device present */
 	unsigned long		wakeup_bits[1];	/* ports that have signaled
 							remote wakeup */
+	unsigned long		resume_bits[1]; /* ports that have seen a
+							resume event */
 #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
 #error event_bits[] is too short!
 #endif
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 512ab162832c..851917571a9e 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -571,7 +571,6 @@ struct usb_device {
 	unsigned long connect_time;
 
 	unsigned do_remote_wakeup:1;
-	unsigned reset_resume:1;
 	unsigned port_is_suspended:1;
 #endif
 	struct wusb_dev *wusb_dev;
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index efe8d8a7c7ad..b6781f4f0d9c 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -119,6 +119,7 @@ struct usb_hcd {
 #define HCD_FLAG_WAKEUP_PENDING		4	/* root hub is resuming? */
 #define HCD_FLAG_RH_RUNNING		5	/* root hub is running? */
 #define HCD_FLAG_DEAD			6	/* controller has died? */
+#define HCD_FLAG_RESET_RESUME		7	/* reset resume root hub */
 
 	/* The flags can be tested using these macros; they are likely to
 	 * be slightly faster than test_bit().

--
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