aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core/hub.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r--drivers/usb/core/hub.c184
1 files changed, 112 insertions, 72 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index e6b682c6c236..a7c04e24ca48 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -120,7 +120,7 @@ static inline char *portspeed(struct usb_hub *hub, int portstatus)
if (hub_is_superspeed(hub->hdev))
return "5.0 Gb/s";
if (portstatus & USB_PORT_STAT_HIGH_SPEED)
- return "480 Mb/s";
+ return "480 Mb/s";
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
return "1.5 Mb/s";
else
@@ -135,7 +135,7 @@ struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev)
return usb_get_intfdata(hdev->actconfig->interface[0]);
}
-static int usb_device_supports_lpm(struct usb_device *udev)
+int usb_device_supports_lpm(struct usb_device *udev)
{
/* USB 2.1 (and greater) devices indicate LPM support through
* their USB 2.0 Extended Capabilities BOS descriptor.
@@ -156,6 +156,11 @@ static int usb_device_supports_lpm(struct usb_device *udev)
"Power management will be impacted.\n");
return 0;
}
+
+ /* udev is root hub */
+ if (!udev->parent)
+ return 1;
+
if (udev->parent->lpm_capable)
return 1;
@@ -310,9 +315,9 @@ static void usb_set_lpm_parameters(struct usb_device *udev)
return;
udev_u1_del = udev->bos->ss_cap->bU1devExitLat;
- udev_u2_del = udev->bos->ss_cap->bU2DevExitLat;
+ udev_u2_del = le16_to_cpu(udev->bos->ss_cap->bU2DevExitLat);
hub_u1_del = udev->parent->bos->ss_cap->bU1devExitLat;
- hub_u2_del = udev->parent->bos->ss_cap->bU2DevExitLat;
+ hub_u2_del = le16_to_cpu(udev->parent->bos->ss_cap->bU2DevExitLat);
usb_set_lpm_mel(udev, &udev->u1_params, udev_u1_del,
hub, &udev->parent->u1_params, hub_u1_del);
@@ -433,7 +438,7 @@ static void set_port_led(
case HUB_LED_OFF: s = "off"; break;
case HUB_LED_AUTO: s = "auto"; break;
default: s = "??"; break;
- }; s; }),
+ } s; }),
status);
}
@@ -857,7 +862,7 @@ static int hub_hub_status(struct usb_hub *hub,
"%s failed (err = %d)\n", __func__, ret);
} else {
*status = le16_to_cpu(hub->status->hub.wHubStatus);
- *change = le16_to_cpu(hub->status->hub.wHubChange);
+ *change = le16_to_cpu(hub->status->hub.wHubChange);
ret = 0;
}
mutex_unlock(&hub->status_mutex);
@@ -956,7 +961,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
*/
set_bit(port1, hub->change_bits);
- kick_khubd(hub);
+ kick_khubd(hub);
}
/**
@@ -1107,16 +1112,13 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
/*
* USB3 protocol ports will automatically transition
* to Enabled state when detect an USB3.0 device attach.
- * Do not disable USB3 protocol ports.
+ * Do not disable USB3 protocol ports, just pretend
+ * power was lost
*/
- if (!hub_is_superspeed(hdev)) {
+ portstatus &= ~USB_PORT_STAT_ENABLE;
+ if (!hub_is_superspeed(hdev))
usb_clear_port_feature(hdev, port1,
USB_PORT_FEAT_ENABLE);
- portstatus &= ~USB_PORT_STAT_ENABLE;
- } else {
- /* Pretend that power was lost for USB3 devs */
- portstatus &= ~USB_PORT_STAT_ENABLE;
- }
}
/* Clear status-change flags; we'll debounce later */
@@ -1130,6 +1132,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_ENABLE);
}
+ if (portchange & USB_PORT_STAT_C_RESET) {
+ need_debounce_delay = true;
+ usb_clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_RESET);
+ }
if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
hub_is_superspeed(hub->hdev)) {
need_debounce_delay = true;
@@ -1361,7 +1368,7 @@ static int hub_configure(struct usb_hub *hub,
if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&
!(hub_is_superspeed(hdev))) {
int i;
- char portstr [USB_MAXCHILDREN + 1];
+ char portstr[USB_MAXCHILDREN + 1];
for (i = 0; i < hdev->maxchild; i++)
portstr[i] = hub->descriptor->u.hs.DeviceRemovable
@@ -1429,32 +1436,32 @@ static int hub_configure(struct usb_hub *hub,
/* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
switch (wHubCharacteristics & HUB_CHAR_TTTT) {
- case HUB_TTTT_8_BITS:
- if (hdev->descriptor.bDeviceProtocol != 0) {
- hub->tt.think_time = 666;
- dev_dbg(hub_dev, "TT requires at most %d "
- "FS bit times (%d ns)\n",
- 8, hub->tt.think_time);
- }
- break;
- case HUB_TTTT_16_BITS:
- hub->tt.think_time = 666 * 2;
- dev_dbg(hub_dev, "TT requires at most %d "
- "FS bit times (%d ns)\n",
- 16, hub->tt.think_time);
- break;
- case HUB_TTTT_24_BITS:
- hub->tt.think_time = 666 * 3;
+ case HUB_TTTT_8_BITS:
+ if (hdev->descriptor.bDeviceProtocol != 0) {
+ hub->tt.think_time = 666;
dev_dbg(hub_dev, "TT requires at most %d "
"FS bit times (%d ns)\n",
- 24, hub->tt.think_time);
- break;
- case HUB_TTTT_32_BITS:
- hub->tt.think_time = 666 * 4;
- dev_dbg(hub_dev, "TT requires at most %d "
- "FS bit times (%d ns)\n",
- 32, hub->tt.think_time);
- break;
+ 8, hub->tt.think_time);
+ }
+ break;
+ case HUB_TTTT_16_BITS:
+ hub->tt.think_time = 666 * 2;
+ dev_dbg(hub_dev, "TT requires at most %d "
+ "FS bit times (%d ns)\n",
+ 16, hub->tt.think_time);
+ break;
+ case HUB_TTTT_24_BITS:
+ hub->tt.think_time = 666 * 3;
+ dev_dbg(hub_dev, "TT requires at most %d "
+ "FS bit times (%d ns)\n",
+ 24, hub->tt.think_time);
+ break;
+ case HUB_TTTT_32_BITS:
+ hub->tt.think_time = 666 * 4;
+ dev_dbg(hub_dev, "TT requires at most %d "
+ "FS bit times (%d ns)\n",
+ 32, hub->tt.think_time);
+ break;
}
/* probe() zeroes hub->indicator[] */
@@ -1560,7 +1567,7 @@ static int hub_configure(struct usb_hub *hub,
/* maybe cycle the hub leds */
if (hub->has_indicators && blinkenlights)
- hub->indicator [0] = INDICATOR_CYCLE;
+ hub->indicator[0] = INDICATOR_CYCLE;
for (i = 0; i < hdev->maxchild; i++) {
ret = usb_hub_create_port_device(hub, i + 1);
@@ -1978,7 +1985,7 @@ static void choose_devnum(struct usb_device *udev)
if (devnum >= 128)
devnum = find_next_zero_bit(bus->devmap.devicemap,
128, 1);
- bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
+ bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1);
}
if (devnum < 128) {
set_bit(devnum, bus->devmap.devicemap);
@@ -2018,8 +2025,8 @@ static void hub_free_dev(struct usb_device *udev)
* Something got disconnected. Get rid of it and all of its children.
*
* If *pdev is a normal device then the parent hub must already be locked.
- * If *pdev is a root hub then this routine will acquire the
- * usb_bus_list_lock on behalf of the caller.
+ * If *pdev is a root hub then the caller must hold the usb_bus_list_lock,
+ * which protects the set of root hubs as well as the list of buses.
*
* Only hub drivers (including virtual root hub drivers for host
* controllers) should ever call this.
@@ -2232,8 +2239,7 @@ static int usb_enumerate_device(struct usb_device *udev)
udev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
udev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
udev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
- }
- else {
+ } else {
/* read the standard strings and cache them if present */
udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
udev->manufacturer = usb_cache_string(udev,
@@ -2489,7 +2495,7 @@ error_device_descriptor:
usb_autosuspend_device(usb_dev);
error_autoresume:
out_authorized:
- usb_unlock_device(usb_dev); // complements locktree
+ usb_unlock_device(usb_dev); /* complements locktree */
return result;
}
@@ -3108,8 +3114,8 @@ static int finish_port_resume(struct usb_device *udev)
retry_reset_resume:
status = usb_reset_and_verify_device(udev);
- /* 10.5.4.5 says be sure devices in the tree are still there.
- * For now let's assume the device didn't go crazy on resume,
+ /* 10.5.4.5 says be sure devices in the tree are still there.
+ * For now let's assume the device didn't go crazy on resume,
* and device drivers will know about any resume quirks.
*/
if (status == 0) {
@@ -3211,7 +3217,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
if (status == 0 && !port_is_suspended(hub, portstatus))
goto SuspendCleared;
- // dev_dbg(hub->intfdev, "resume port %d\n", port1);
+ /* dev_dbg(hub->intfdev, "resume port %d\n", port1); */
set_bit(port1, hub->busy_bits);
@@ -3855,7 +3861,7 @@ EXPORT_SYMBOL_GPL(usb_enable_ltm);
* Between connect detection and reset signaling there must be a delay
* of 100ms at least for debounce and power-settling. The corresponding
* timer shall restart whenever the downstream port detects a disconnect.
- *
+ *
* Apparently there are some bluetooth and irda-dongles and a number of
* low-speed devices for which this debounce period may last over a second.
* Not covered by the spec - but easy to deal with.
@@ -3949,6 +3955,32 @@ static int hub_set_address(struct usb_device *udev, int devnum)
return retval;
}
+/*
+ * There are reports of USB 3.0 devices that say they support USB 2.0 Link PM
+ * when they're plugged into a USB 2.0 port, but they don't work when LPM is
+ * enabled.
+ *
+ * Only enable USB 2.0 Link PM if the port is internal (hardwired), or the
+ * device says it supports the new USB 2.0 Link PM errata by setting the BESL
+ * support bit in the BOS descriptor.
+ */
+static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev)
+{
+ int connect_type;
+
+ if (!udev->usb2_hw_lpm_capable)
+ return;
+
+ connect_type = usb_get_hub_port_connect_type(udev->parent,
+ udev->portnum);
+
+ if ((udev->bos->ext_cap->bmAttributes & USB_BESL_SUPPORT) ||
+ connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) {
+ udev->usb2_hw_lpm_allowed = 1;
+ usb_set_usb2_hardware_lpm(udev, 1);
+ }
+}
+
/* Reset device, (re)assign address, get device descriptor.
* Device connection must be stable, no more debouncing needed.
* Returns device in USB_STATE_ADDRESS, except on error.
@@ -4055,7 +4087,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
udev->tt = &hub->tt;
udev->ttport = port1;
}
-
+
/* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
* Because device hardware and firmware is sometimes buggy in
* this area, and this is how Linux has done it for ages.
@@ -4130,11 +4162,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
#undef GET_DESCRIPTOR_BUFSIZE
}
- /*
- * If device is WUSB, we already assigned an
- * unauthorized address in the Connect Ack sequence;
- * authorization will assign the final address.
- */
+ /*
+ * If device is WUSB, we already assigned an
+ * unauthorized address in the Connect Ack sequence;
+ * authorization will assign the final address.
+ */
if (udev->wusb == 0) {
for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
retval = hub_set_address(udev, devnum);
@@ -4163,7 +4195,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
msleep(10);
if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3))
break;
- }
+ }
retval = usb_get_device_descriptor(udev, 8);
if (retval < 8) {
@@ -4219,7 +4251,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
usb_ep0_reinit(udev);
}
-
+
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
if (retval < (signed)sizeof(udev->descriptor)) {
if (retval != -ENODEV)
@@ -4242,6 +4274,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
/* notify HCD that we have a device connected and addressed */
if (hcd->driver->update_device)
hcd->driver->update_device(hcd, udev);
+ hub_set_initial_usb2_lpm_policy(udev);
fail:
if (retval) {
hub_port_disable(hub, port1, 0);
@@ -4316,7 +4349,7 @@ hub_power_remaining (struct usb_hub *hub)
}
if (remaining < 0) {
dev_warn(hub->intfdev, "%dmA over power budget!\n",
- - remaining);
+ -remaining);
remaining = 0;
}
return remaining;
@@ -4427,7 +4460,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
if (portstatus & USB_PORT_STAT_ENABLE)
- goto done;
+ goto done;
return;
}
if (hub_is_superspeed(hub->hdev))
@@ -4450,7 +4483,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
}
usb_set_device_state(udev, USB_STATE_POWERED);
- udev->bus_mA = hub->mA_per_port;
+ udev->bus_mA = hub->mA_per_port;
udev->level = hdev->level + 1;
udev->wusb = hub_is_wusb(hub);
@@ -4504,7 +4537,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
goto loop_disable;
}
}
-
+
/* check for devices running slower than they could */
if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
&& udev->speed == USB_SPEED_FULL
@@ -4564,7 +4597,7 @@ loop:
dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
port1);
}
-
+
done:
hub_port_disable(hub, port1, 1);
if (hcd->driver->relinquish_port && !hub->hdev->parent)
@@ -4729,7 +4762,7 @@ static void hub_events(void)
* EM interference sometimes causes badly
* shielded USB devices to be shutdown by
* the hub, this hack enables them again.
- * Works at least with mouse driver.
+ * Works at least with mouse driver.
*/
if (!(portstatus & USB_PORT_STAT_ENABLE)
&& !connect_change
@@ -4841,7 +4874,7 @@ static void hub_events(void)
dev_dbg(hub_dev, "over-current change\n");
clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
msleep(500); /* Cool down */
- hub_power_on(hub, true);
+ hub_power_on(hub, true);
hub_hub_status(hub, &status, &unused);
if (status & HUB_STATUS_OVERCURRENT)
dev_err(hub_dev, "over-current "
@@ -4861,7 +4894,7 @@ static void hub_events(void)
usb_unlock_device(hdev);
kref_put(&hub->kref, hub_release);
- } /* end while (1) */
+ } /* end while (1) */
}
static int hub_thread(void *__unused)
@@ -4886,7 +4919,7 @@ static int hub_thread(void *__unused)
static const struct usb_device_id hub_id_table[] = {
{ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
- | USB_DEVICE_ID_MATCH_INT_CLASS,
+ | USB_DEVICE_ID_MATCH_INT_CLASS,
.idVendor = USB_VENDOR_GENESYS_LOGIC,
.bInterfaceClass = USB_CLASS_HUB,
.driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},
@@ -5086,6 +5119,12 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
}
parent_hub = usb_hub_to_struct_hub(parent_hdev);
+ /* Disable USB2 hardware LPM.
+ * It will be re-enabled by the enumeration process.
+ */
+ if (udev->usb2_hw_lpm_enabled == 1)
+ usb_set_usb2_hardware_lpm(udev, 0);
+
bos = udev->bos;
udev->bos = NULL;
@@ -5120,13 +5159,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
if (ret < 0)
goto re_enumerate;
-
+
/* Device might have changed firmware (DFU or similar) */
if (descriptors_changed(udev, &descriptor, bos)) {
dev_info(&udev->dev, "device firmware changed\n");
udev->descriptor = descriptor; /* for disconnect() calls */
goto re_enumerate;
- }
+ }
/* Restore the device's previous configuration */
if (!udev->actconfig)
@@ -5151,7 +5190,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
udev->actconfig->desc.bConfigurationValue, ret);
mutex_unlock(hcd->bandwidth_mutex);
goto re_enumerate;
- }
+ }
mutex_unlock(hcd->bandwidth_mutex);
usb_set_device_state(udev, USB_STATE_CONFIGURED);
@@ -5193,12 +5232,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
done:
/* Now that the alt settings are re-installed, enable LTM and LPM. */
+ usb_set_usb2_hardware_lpm(udev, 1);
usb_unlocked_enable_lpm(udev);
usb_enable_ltm(udev);
usb_release_bos_descriptor(udev);
udev->bos = bos;
return 0;
-
+
re_enumerate:
/* LPM state doesn't matter when we're about to destroy the device. */
hub_port_logical_disconnect(parent_hub, port1);
@@ -5461,6 +5501,6 @@ acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev,
if (!hub)
return NULL;
- return DEVICE_ACPI_HANDLE(&hub->ports[port1 - 1]->dev);
+ return ACPI_HANDLE(&hub->ports[port1 - 1]->dev);
}
#endif