aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/core
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/config.c6
-rw-r--r--drivers/usb/core/driver.c3
-rw-r--r--drivers/usb/core/generic.c2
-rw-r--r--drivers/usb/core/hcd.c11
-rw-r--r--drivers/usb/core/hub.c61
-rw-r--r--drivers/usb/core/port.c32
-rw-r--r--drivers/usb/core/quirks.c6
-rw-r--r--drivers/usb/core/usb.c46
8 files changed, 98 insertions, 69 deletions
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 16b1fd9dc60c..48bc8a4814ac 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -406,7 +406,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
* the USB-2 spec requires such endpoints to have wMaxPacketSize = 0
* (see the end of section 5.6.3), so don't warn about them.
*/
- maxp = usb_endpoint_maxp(&endpoint->desc);
+ maxp = le16_to_cpu(endpoint->desc.wMaxPacketSize);
if (maxp == 0 && !(usb_endpoint_xfer_isoc(d) && asnum == 0)) {
dev_warn(ddev, "config %d interface %d altsetting %d endpoint 0x%X has invalid wMaxPacketSize 0\n",
cfgno, inum, asnum, d->bEndpointAddress);
@@ -422,9 +422,9 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
maxpacket_maxes = full_speed_maxpacket_maxes;
break;
case USB_SPEED_HIGH:
- /* Bits 12..11 are allowed only for HS periodic endpoints */
+ /* Multiple-transactions bits are allowed only for HS periodic endpoints */
if (usb_endpoint_xfer_int(d) || usb_endpoint_xfer_isoc(d)) {
- i = maxp & (BIT(12) | BIT(11));
+ i = maxp & USB_EP_MAXP_MULT_MASK;
maxp &= ~i;
}
fallthrough;
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 072968c40ade..355ed33a2179 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/usb/driver.c - most of the driver model stuff for usb
+ * drivers/usb/core/driver.c - most of the driver model stuff for usb
*
* (C) Copyright 2005 Greg Kroah-Hartman <[email protected]>
*
@@ -834,6 +834,7 @@ const struct usb_device_id *usb_device_match_id(struct usb_device *udev,
return NULL;
}
+EXPORT_SYMBOL_GPL(usb_device_match_id);
bool usb_driver_applicable(struct usb_device *udev,
struct usb_device_driver *udrv)
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index 26f9fb9f67ca..740342a2812a 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/usb/generic.c - generic driver for USB devices (not interfaces)
+ * drivers/usb/core/generic.c - generic driver for USB devices (not interfaces)
*
* (C) Copyright 2005 Greg Kroah-Hartman <[email protected]>
*
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 4d326ee12c36..3e01dd6e509b 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -753,6 +753,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
{
struct urb *urb;
int length;
+ int status;
unsigned long flags;
char buffer[6]; /* Any root hubs with > 31 ports? */
@@ -770,11 +771,17 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
if (urb) {
clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
hcd->status_urb = NULL;
+ if (urb->transfer_buffer_length >= length) {
+ status = 0;
+ } else {
+ status = -EOVERFLOW;
+ length = urb->transfer_buffer_length;
+ }
urb->actual_length = length;
memcpy(urb->transfer_buffer, buffer, length);
usb_hcd_unlink_urb_from_ep(hcd, urb);
- usb_hcd_giveback_urb(hcd, urb, 0);
+ usb_hcd_giveback_urb(hcd, urb, status);
} else {
length = 0;
set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
@@ -1281,7 +1288,7 @@ static int hcd_alloc_coherent(struct usb_bus *bus,
return -EFAULT;
}
- vaddr = hcd_buffer_alloc(bus, size + sizeof(vaddr),
+ vaddr = hcd_buffer_alloc(bus, size + sizeof(unsigned long),
mem_flags, dma_handle);
if (!vaddr)
return -ENOMEM;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 86658a81d284..47a1c8bddf86 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1110,7 +1110,10 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
} else {
hub_power_on(hub, true);
}
- }
+ /* Give some time on remote wakeup to let links to transit to U0 */
+ } else if (hub_is_superspeed(hub->hdev))
+ msleep(20);
+
init2:
/*
@@ -1225,7 +1228,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
*/
if (portchange || (hub_is_superspeed(hub->hdev) &&
port_resumed))
- set_bit(port1, hub->change_bits);
+ set_bit(port1, hub->event_bits);
} else if (udev->persist_enabled) {
#ifdef CONFIG_PM
@@ -2777,6 +2780,8 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
#define PORT_INIT_TRIES 4
#endif /* CONFIG_USB_FEW_INIT_RETRIES */
+#define DETECT_DISCONNECT_TRIES 5
+
#define HUB_ROOT_RESET_TIME 60 /* times are in msec */
#define HUB_SHORT_RESET_TIME 10
#define HUB_BH_RESET_TIME 50
@@ -3570,7 +3575,7 @@ static int finish_port_resume(struct usb_device *udev)
* This routine should only be called when persist is enabled.
*/
static int wait_for_connected(struct usb_device *udev,
- struct usb_hub *hub, int *port1,
+ struct usb_hub *hub, int port1,
u16 *portchange, u16 *portstatus)
{
int status = 0, delay_ms = 0;
@@ -3584,7 +3589,7 @@ static int wait_for_connected(struct usb_device *udev,
}
msleep(20);
delay_ms += 20;
- status = hub_port_status(hub, *port1, portstatus, portchange);
+ status = hub_port_status(hub, port1, portstatus, portchange);
}
dev_dbg(&udev->dev, "Waited %dms for CONNECT\n", delay_ms);
return status;
@@ -3690,7 +3695,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
}
if (udev->persist_enabled)
- status = wait_for_connected(udev, hub, &port1, &portchange,
+ status = wait_for_connected(udev, hub, port1, &portchange,
&portstatus);
status = check_port_resume_type(udev,
@@ -4700,8 +4705,6 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
if (oldspeed == USB_SPEED_LOW)
delay = HUB_LONG_RESET_TIME;
- mutex_lock(hcd->address0_mutex);
-
/* Reset the device; full speed may morph to high speed */
/* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
retval = hub_port_reset(hub, port1, udev, delay, false);
@@ -5016,7 +5019,6 @@ fail:
hub_port_disable(hub, port1, 0);
update_devnum(udev, devnum); /* for disconnect processing */
}
- mutex_unlock(hcd->address0_mutex);
return retval;
}
@@ -5191,6 +5193,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
struct usb_port *port_dev = hub->ports[port1 - 1];
struct usb_device *udev = port_dev->child;
static int unreliable_port = -1;
+ bool retry_locked;
/* Disconnect any existing devices under this port */
if (udev) {
@@ -5246,8 +5249,11 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
unit_load = 100;
status = 0;
- for (i = 0; i < PORT_INIT_TRIES; i++) {
+ for (i = 0; i < PORT_INIT_TRIES; i++) {
+ usb_lock_port(port_dev);
+ mutex_lock(hcd->address0_mutex);
+ retry_locked = true;
/* reallocate for each attempt, since references
* to the previous one can escape in various ways
*/
@@ -5255,6 +5261,8 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
if (!udev) {
dev_err(&port_dev->dev,
"couldn't allocate usb_device\n");
+ mutex_unlock(hcd->address0_mutex);
+ usb_unlock_port(port_dev);
goto done;
}
@@ -5276,12 +5284,14 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
}
/* reset (non-USB 3.0 devices) and get descriptor */
- usb_lock_port(port_dev);
status = hub_port_init(hub, udev, port1, i);
- usb_unlock_port(port_dev);
if (status < 0)
goto loop;
+ mutex_unlock(hcd->address0_mutex);
+ usb_unlock_port(port_dev);
+ retry_locked = false;
+
if (udev->quirks & USB_QUIRK_DELAY_INIT)
msleep(2000);
@@ -5374,6 +5384,10 @@ loop:
usb_ep0_reinit(udev);
release_devnum(udev);
hub_free_dev(udev);
+ if (retry_locked) {
+ mutex_unlock(hcd->address0_mutex);
+ usb_unlock_port(port_dev);
+ }
usb_put_dev(udev);
if ((status == -ENOTCONN) || (status == -ENOTSUPP))
break;
@@ -5534,6 +5548,7 @@ static void port_event(struct usb_hub *hub, int port1)
struct usb_device *udev = port_dev->child;
struct usb_device *hdev = hub->hdev;
u16 portstatus, portchange;
+ int i = 0;
connect_change = test_bit(port1, hub->change_bits);
clear_bit(port1, hub->event_bits);
@@ -5610,17 +5625,27 @@ static void port_event(struct usb_hub *hub, int port1)
connect_change = 1;
/*
- * Warm reset a USB3 protocol port if it's in
- * SS.Inactive state.
+ * Avoid trying to recover a USB3 SS.Inactive port with a warm reset if
+ * the device was disconnected. A 12ms disconnect detect timer in
+ * SS.Inactive state transitions the port to RxDetect automatically.
+ * SS.Inactive link error state is common during device disconnect.
*/
- if (hub_port_warm_reset_required(hub, port1, portstatus)) {
- dev_dbg(&port_dev->dev, "do warm reset\n");
- if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)
+ while (hub_port_warm_reset_required(hub, port1, portstatus)) {
+ if ((i++ < DETECT_DISCONNECT_TRIES) && udev) {
+ u16 unused;
+
+ msleep(20);
+ hub_port_status(hub, port1, &portstatus, &unused);
+ dev_dbg(&port_dev->dev, "Wait for inactive link disconnect detect\n");
+ continue;
+ } else if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)
|| udev->state == USB_STATE_NOTATTACHED) {
+ dev_dbg(&port_dev->dev, "do warm reset, port only\n");
if (hub_port_reset(hub, port1, NULL,
HUB_BH_RESET_TIME, true) < 0)
hub_port_disable(hub, port1, 1);
} else {
+ dev_dbg(&port_dev->dev, "do warm reset, full device\n");
usb_unlock_port(port_dev);
usb_lock_device(udev);
usb_reset_device(udev);
@@ -5628,6 +5653,7 @@ static void port_event(struct usb_hub *hub, int port1)
usb_lock_port(port_dev);
connect_change = 0;
}
+ break;
}
if (connect_change)
@@ -5915,6 +5941,8 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
bos = udev->bos;
udev->bos = NULL;
+ mutex_lock(hcd->address0_mutex);
+
for (i = 0; i < PORT_INIT_TRIES; ++i) {
/* ep0 maxpacket size may change; let the HCD know about it.
@@ -5924,6 +5952,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV)
break;
}
+ mutex_unlock(hcd->address0_mutex);
if (ret < 0)
goto re_enumerate;
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index dfcca9c876c7..c2bbf97a79be 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -9,6 +9,7 @@
#include <linux/slab.h>
#include <linux/pm_qos.h>
+#include <linux/component.h>
#include "hub.h"
@@ -528,6 +529,32 @@ static void find_and_link_peer(struct usb_hub *hub, int port1)
link_peers_report(port_dev, peer);
}
+static int connector_bind(struct device *dev, struct device *connector, void *data)
+{
+ int ret;
+
+ ret = sysfs_create_link(&dev->kobj, &connector->kobj, "connector");
+ if (ret)
+ return ret;
+
+ ret = sysfs_create_link(&connector->kobj, &dev->kobj, dev_name(dev));
+ if (ret)
+ sysfs_remove_link(&dev->kobj, "connector");
+
+ return ret;
+}
+
+static void connector_unbind(struct device *dev, struct device *connector, void *data)
+{
+ sysfs_remove_link(&connector->kobj, dev_name(dev));
+ sysfs_remove_link(&dev->kobj, "connector");
+}
+
+static const struct component_ops connector_ops = {
+ .bind = connector_bind,
+ .unbind = connector_unbind,
+};
+
int usb_hub_create_port_device(struct usb_hub *hub, int port1)
{
struct usb_port *port_dev;
@@ -577,6 +604,10 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
find_and_link_peer(hub, port1);
+ retval = component_add(&port_dev->dev, &connector_ops);
+ if (retval)
+ dev_warn(&port_dev->dev, "failed to add component\n");
+
/*
* Enable runtime pm and hold a refernce that hub_configure()
* will drop once the PM_QOS_NO_POWER_OFF flag state has been set
@@ -619,5 +650,6 @@ void usb_hub_remove_port_device(struct usb_hub *hub, int port1)
peer = port_dev->peer;
if (peer)
unlink_peers(port_dev, peer);
+ component_del(&port_dev->dev, &connector_ops);
device_unregister(&port_dev->dev);
}
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 8239fe7129dd..d3c14b5ed4a1 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -434,6 +434,12 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x1532, 0x0116), .driver_info =
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
+ /* Lenovo USB-C to Ethernet Adapter RTL8153-04 */
+ { USB_DEVICE(0x17ef, 0x720c), .driver_info = USB_QUIRK_NO_LPM },
+
+ /* Lenovo Powered USB-C Travel Hub (4X90S92381, RTL8153 GigE) */
+ { USB_DEVICE(0x17ef, 0x721e), .driver_info = USB_QUIRK_NO_LPM },
+
/* Lenovo ThinkCenter A630Z TI024Gen3 usb-audio */
{ USB_DEVICE(0x17ef, 0xa012), .driver_info =
USB_QUIRK_DISCONNECT_SUSPEND },
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 62368c4ed37a..2ce3667ec6fa 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -398,52 +398,6 @@ int usb_for_each_dev(void *data, int (*fn)(struct usb_device *, void *))
}
EXPORT_SYMBOL_GPL(usb_for_each_dev);
-struct each_hub_arg {
- void *data;
- int (*fn)(struct device *, void *);
-};
-
-static int __each_hub(struct usb_device *hdev, void *data)
-{
- struct each_hub_arg *arg = (struct each_hub_arg *)data;
- struct usb_hub *hub;
- int ret = 0;
- int i;
-
- hub = usb_hub_to_struct_hub(hdev);
- if (!hub)
- return 0;
-
- mutex_lock(&usb_port_peer_mutex);
-
- for (i = 0; i < hdev->maxchild; i++) {
- ret = arg->fn(&hub->ports[i]->dev, arg->data);
- if (ret)
- break;
- }
-
- mutex_unlock(&usb_port_peer_mutex);
-
- return ret;
-}
-
-/**
- * usb_for_each_port - interate over all USB ports in the system
- * @data: data pointer that will be handed to the callback function
- * @fn: callback function to be called for each USB port
- *
- * Iterate over all USB ports and call @fn for each, passing it @data. If it
- * returns anything other than 0, we break the iteration prematurely and return
- * that value.
- */
-int usb_for_each_port(void *data, int (*fn)(struct device *, void *))
-{
- struct each_hub_arg arg = {data, fn};
-
- return usb_for_each_dev(&arg, __each_hub);
-}
-EXPORT_SYMBOL_GPL(usb_for_each_port);
-
/**
* usb_release_dev - free a usb device structure when all users of it are finished.
* @dev: device that's been disconnected