Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 72937e1e authored by Sarah Sharp's avatar Sarah Sharp
Browse files

USB: Set wakeup bits for all children hubs.



This patch takes care of the race condition between the Function Wake
Device Notification and the auto-suspend timeout for this situation:

Roothub
  | (U3)
hub A
  | (U3)
hub B
  | (U3)
device C

When device C signals a resume, the xHCI driver will set the wakeup_bits
for the roothub port that hub A is attached to.  However, since USB 3.0
hubs do not set a link state change bit on device-initiated resume, hub
A will not indicate a port event when polled.  Without this patch, khubd
will notice the wakeup-bits are set for the roothub port, it will resume
hub A, and then it will poll the events bits for hub A and notice that
nothing has changed.  Then it will be suspended after 2 seconds.

Change hub_activate() to look at the port link state for each USB 3.0
hub port, and set hub->change_bits if the link state is U0, indicating
the device has finished resume.  Change the resume function called by
hub_events(), hub_handle_remote_wakeup(), to check the link status
for resume instead of just the port's wakeup_bits.

Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
parent 4ee823b8
Loading
Loading
Loading
Loading
+15 −7
Original line number Diff line number Diff line
@@ -853,12 +853,19 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
				set_bit(port1, hub->change_bits);

		} else if (portstatus & USB_PORT_STAT_ENABLE) {
			bool port_resumed = (portstatus &
					USB_PORT_STAT_LINK_STATE) ==
				USB_SS_PORT_LS_U0;
			/* The power session apparently survived the resume.
			 * If there was an overcurrent or suspend change
			 * (i.e., remote wakeup request), have khubd
			 * take care of it.
			 * take care of it.  Look at the port link state
			 * for USB 3.0 hubs, since they don't have a suspend
			 * change bit, and they don't set the port link change
			 * bit on device-initiated resume.
			 */
			if (portchange)
			if (portchange || (hub_is_superspeed(hub->hdev) &&
						port_resumed))
				set_bit(port1, hub->change_bits);

		} else if (udev->persist_enabled) {
@@ -3509,7 +3516,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,

/* Returns 1 if there was a remote wakeup and a connect status change. */
static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
		u16 portchange)
		u16 portstatus, u16 portchange)
{
	struct usb_device *hdev;
	struct usb_device *udev;
@@ -3524,8 +3531,8 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
		clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND);
	} else {
		if (!udev || udev->state != USB_STATE_SUSPENDED ||
				!test_and_clear_bit(udev->portnum,
					hub->wakeup_bits))
				 (portstatus & USB_PORT_STAT_LINK_STATE) !=
				 USB_SS_PORT_LS_U0)
			return 0;
	}

@@ -3638,7 +3645,7 @@ static void hub_events(void)
			if (test_bit(i, hub->busy_bits))
				continue;
			connect_change = test_bit(i, hub->change_bits);
			wakeup_change = test_bit(i, hub->wakeup_bits);
			wakeup_change = test_and_clear_bit(i, hub->wakeup_bits);
			if (!test_and_clear_bit(i, hub->event_bits) &&
					!connect_change && !wakeup_change)
				continue;
@@ -3681,7 +3688,8 @@ static void hub_events(void)
				}
			}

			if (hub_handle_remote_wakeup(hub, i, portchange))
			if (hub_handle_remote_wakeup(hub, i,
						portstatus, portchange))
				connect_change = 1;

			if (portchange & USB_PORT_STAT_C_OVERCURRENT) {