summaryrefslogtreecommitdiff
path: root/drivers/usb/core
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/Makefile6
-rw-r--r--drivers/usb/core/config.c128
-rw-r--r--drivers/usb/core/devio.c17
-rw-r--r--drivers/usb/core/driver.c77
-rw-r--r--drivers/usb/core/endpoint.c10
-rw-r--r--drivers/usb/core/generic.c2
-rw-r--r--drivers/usb/core/hcd-pci.c2
-rw-r--r--drivers/usb/core/hcd.c95
-rw-r--r--drivers/usb/core/hub.c196
-rw-r--r--drivers/usb/core/hub.h1
-rw-r--r--drivers/usb/core/ledtrig-usbport.c4
-rw-r--r--drivers/usb/core/message.c138
-rw-r--r--drivers/usb/core/of.c8
-rw-r--r--drivers/usb/core/offload.c152
-rw-r--r--drivers/usb/core/phy.c20
-rw-r--r--drivers/usb/core/port.c25
-rw-r--r--drivers/usb/core/quirks.c54
-rw-r--r--drivers/usb/core/sysfs.c2
-rw-r--r--drivers/usb/core/trace.c6
-rw-r--r--drivers/usb/core/trace.h61
-rw-r--r--drivers/usb/core/urb.c50
-rw-r--r--drivers/usb/core/usb-acpi.c6
-rw-r--r--drivers/usb/core/usb.c150
23 files changed, 907 insertions, 303 deletions
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile
index ac006abd13b3..60ea76160122 100644
--- a/drivers/usb/core/Makefile
+++ b/drivers/usb/core/Makefile
@@ -3,12 +3,16 @@
# Makefile for USB Core files and filesystem
#
+# define_trace.h needs to know how to find our header
+CFLAGS_trace.o := -I$(src)
+
usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o
usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o
usbcore-y += devio.o notify.o generic.o quirks.o devices.o
-usbcore-y += phy.o port.o
+usbcore-y += phy.o port.o trace.o
usbcore-$(CONFIG_OF) += of.o
+usbcore-$(CONFIG_USB_XHCI_SIDEBAND) += offload.o
usbcore-$(CONFIG_USB_PCI) += hcd-pci.o
usbcore-$(CONFIG_ACPI) += usb-acpi.o
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index f7bf8d1de3ad..45e20c6d76c0 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -54,16 +54,46 @@ static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev,
* follows the SuperSpeed Endpoint Companion descriptor
*/
desc = (struct usb_ssp_isoc_ep_comp_descriptor *) buffer;
- if (desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP ||
- size < USB_DT_SSP_ISOC_EP_COMP_SIZE) {
- dev_notice(ddev, "Invalid SuperSpeedPlus isoc endpoint companion"
- "for config %d interface %d altsetting %d ep %d.\n",
+ if (size < USB_DT_SSP_ISOC_EP_COMP_SIZE ||
+ desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP) {
+ dev_notice(ddev, "Invalid SuperSpeedPlus isoc endpoint companion for config %d interface %d altsetting %d ep 0x%X.\n",
cfgno, inum, asnum, ep->desc.bEndpointAddress);
return;
}
memcpy(&ep->ssp_isoc_ep_comp, desc, USB_DT_SSP_ISOC_EP_COMP_SIZE);
}
+static void usb_parse_eusb2_isoc_endpoint_companion(struct device *ddev,
+ int cfgno, int inum, int asnum, struct usb_host_endpoint *ep,
+ unsigned char *buffer, int size)
+{
+ struct usb_eusb2_isoc_ep_comp_descriptor *desc;
+ struct usb_descriptor_header *h;
+
+ /*
+ * eUSB2 isochronous endpoint companion descriptor for this endpoint
+ * shall be declared before the next endpoint or interface descriptor
+ */
+ while (size >= USB_DT_EUSB2_ISOC_EP_COMP_SIZE) {
+ h = (struct usb_descriptor_header *)buffer;
+
+ if (h->bDescriptorType == USB_DT_EUSB2_ISOC_ENDPOINT_COMP) {
+ desc = (struct usb_eusb2_isoc_ep_comp_descriptor *)buffer;
+ ep->eusb2_isoc_ep_comp = *desc;
+ return;
+ }
+ if (h->bDescriptorType == USB_DT_ENDPOINT ||
+ h->bDescriptorType == USB_DT_INTERFACE)
+ break;
+
+ buffer += h->bLength;
+ size -= h->bLength;
+ }
+
+ dev_notice(ddev, "No eUSB2 isoc ep 0x%X companion for config %d interface %d altsetting %d\n",
+ ep->desc.bEndpointAddress, cfgno, inum, asnum);
+}
+
static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
int inum, int asnum, struct usb_host_endpoint *ep,
unsigned char *buffer, int size)
@@ -76,11 +106,15 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
*/
desc = (struct usb_ss_ep_comp_descriptor *) buffer;
- if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP ||
- size < USB_DT_SS_EP_COMP_SIZE) {
- dev_notice(ddev, "No SuperSpeed endpoint companion for config %d "
- " interface %d altsetting %d ep %d: "
- "using minimum values\n",
+ if (size < USB_DT_SS_EP_COMP_SIZE) {
+ dev_notice(ddev,
+ "invalid SuperSpeed endpoint companion descriptor "
+ "of length %d, skipping\n", size);
+ return;
+ }
+
+ if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) {
+ dev_notice(ddev, "No SuperSpeed endpoint companion for config %d interface %d altsetting %d ep 0x%X: using minimum values\n",
cfgno, inum, asnum, ep->desc.bEndpointAddress);
/* Fill in some default values.
@@ -104,42 +138,32 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
/* Check the various values */
if (usb_endpoint_xfer_control(&ep->desc) && desc->bMaxBurst != 0) {
- dev_notice(ddev, "Control endpoint with bMaxBurst = %d in "
- "config %d interface %d altsetting %d ep %d: "
- "setting to zero\n", desc->bMaxBurst,
- cfgno, inum, asnum, ep->desc.bEndpointAddress);
+ dev_notice(ddev, "Control endpoint with bMaxBurst = %d in config %d interface %d altsetting %d ep 0x%X: setting to zero\n",
+ desc->bMaxBurst, cfgno, inum, asnum, ep->desc.bEndpointAddress);
ep->ss_ep_comp.bMaxBurst = 0;
} else if (desc->bMaxBurst > 15) {
- dev_notice(ddev, "Endpoint with bMaxBurst = %d in "
- "config %d interface %d altsetting %d ep %d: "
- "setting to 15\n", desc->bMaxBurst,
- cfgno, inum, asnum, ep->desc.bEndpointAddress);
+ dev_notice(ddev, "Endpoint with bMaxBurst = %d in config %d interface %d altsetting %d ep 0x%X: setting to 15\n",
+ desc->bMaxBurst, cfgno, inum, asnum, ep->desc.bEndpointAddress);
ep->ss_ep_comp.bMaxBurst = 15;
}
if ((usb_endpoint_xfer_control(&ep->desc) ||
usb_endpoint_xfer_int(&ep->desc)) &&
desc->bmAttributes != 0) {
- dev_notice(ddev, "%s endpoint with bmAttributes = %d in "
- "config %d interface %d altsetting %d ep %d: "
- "setting to zero\n",
+ dev_notice(ddev, "%s endpoint with bmAttributes = %d in config %d interface %d altsetting %d ep 0x%X: setting to zero\n",
usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk",
desc->bmAttributes,
cfgno, inum, asnum, ep->desc.bEndpointAddress);
ep->ss_ep_comp.bmAttributes = 0;
} else if (usb_endpoint_xfer_bulk(&ep->desc) &&
desc->bmAttributes > 16) {
- dev_notice(ddev, "Bulk endpoint with more than 65536 streams in "
- "config %d interface %d altsetting %d ep %d: "
- "setting to max\n",
+ dev_notice(ddev, "Bulk endpoint with more than 65536 streams in config %d interface %d altsetting %d ep 0x%X: setting to max\n",
cfgno, inum, asnum, ep->desc.bEndpointAddress);
ep->ss_ep_comp.bmAttributes = 16;
} else if (usb_endpoint_xfer_isoc(&ep->desc) &&
!USB_SS_SSP_ISOC_COMP(desc->bmAttributes) &&
USB_SS_MULT(desc->bmAttributes) > 3) {
- dev_notice(ddev, "Isoc endpoint has Mult of %d in "
- "config %d interface %d altsetting %d ep %d: "
- "setting to 3\n",
+ dev_notice(ddev, "Isoc endpoint has Mult of %d in config %d interface %d altsetting %d ep 0x%X: setting to 3\n",
USB_SS_MULT(desc->bmAttributes),
cfgno, inum, asnum, ep->desc.bEndpointAddress);
ep->ss_ep_comp.bmAttributes = 2;
@@ -154,10 +178,15 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
(desc->bMaxBurst + 1);
else
max_tx = 999999;
- if (le16_to_cpu(desc->wBytesPerInterval) > max_tx) {
- dev_notice(ddev, "%s endpoint with wBytesPerInterval of %d in "
- "config %d interface %d altsetting %d ep %d: "
- "setting to %d\n",
+ /*
+ * wBytesPerInterval > max_tx is bogus, but USB3 spec doesn't forbid the opposite.
+ * Experience shows that wBytesPerInterval < wMaxPacketSize on common interrupt IN
+ * endpoints is usually bogus too, and recent HCs enforce interrupt BW limits.
+ */
+ if (le16_to_cpu(desc->wBytesPerInterval) > max_tx ||
+ (le16_to_cpu(desc->wBytesPerInterval) < usb_endpoint_maxp(&ep->desc) &&
+ usb_endpoint_is_int_in(&ep->desc))) {
+ dev_notice(ddev, "%s endpoint with wBytesPerInterval of %d in config %d interface %d altsetting %d ep 0x%X: setting to %d\n",
usb_endpoint_xfer_isoc(&ep->desc) ? "Isoc" : "Int",
le16_to_cpu(desc->wBytesPerInterval),
cfgno, inum, asnum, ep->desc.bEndpointAddress,
@@ -258,8 +287,10 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
int n, i, j, retval;
unsigned int maxp;
const unsigned short *maxpacket_maxes;
+ u16 bcdUSB;
d = (struct usb_endpoint_descriptor *) buffer;
+ bcdUSB = le16_to_cpu(udev->descriptor.bcdUSB);
buffer += d->bLength;
size -= d->bLength;
@@ -274,7 +305,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
goto skip_to_next_endpoint_or_interface_descriptor;
}
- i = d->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ i = usb_endpoint_num(d);
if (i == 0) {
dev_notice(ddev, "config %d interface %d altsetting %d has an "
"invalid descriptor for endpoint zero, skipping\n",
@@ -409,15 +440,17 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
/*
* Validate the wMaxPacketSize field.
- * Some devices have isochronous endpoints in altsetting 0;
- * 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.
+ * eUSB2 devices (see USB 2.0 Double Isochronous IN ECN 9.6.6 Endpoint)
+ * and devices with isochronous endpoints in altsetting 0 (see USB 2.0
+ * end of section 5.6.3) have wMaxPacketSize = 0.
+ * So don't warn about those.
*/
maxp = le16_to_cpu(endpoint->desc.wMaxPacketSize);
- if (maxp == 0 && !(usb_endpoint_xfer_isoc(d) && asnum == 0)) {
+
+ if (maxp == 0 && bcdUSB != 0x0220 &&
+ !(usb_endpoint_xfer_isoc(d) && asnum == 0))
dev_notice(ddev, "config %d interface %d altsetting %d endpoint 0x%X has invalid wMaxPacketSize 0\n",
cfgno, inum, asnum, d->bEndpointAddress);
- }
/* Find the highest legal maxpacket size for this endpoint */
i = 0; /* additional transactions per microframe */
@@ -465,6 +498,12 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
maxp);
}
+ /* Parse a possible eUSB2 periodic endpoint companion descriptor */
+ if (udev->speed == USB_SPEED_HIGH && bcdUSB == 0x0220 &&
+ !le16_to_cpu(d->wMaxPacketSize) && usb_endpoint_is_isoc_in(d))
+ usb_parse_eusb2_isoc_endpoint_companion(ddev, cfgno, inum, asnum,
+ endpoint, buffer, size);
+
/* Parse a possible SuperSpeed endpoint companion descriptor */
if (udev->speed >= USB_SPEED_SUPER)
usb_parse_ss_endpoint_companion(ddev, cfgno,
@@ -776,7 +815,7 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
nalts[i] = j = USB_MAXALTSETTING;
}
- intfc = kzalloc(struct_size(intfc, altsetting, j), GFP_KERNEL);
+ intfc = kzalloc_flex(*intfc, altsetting, j);
config->intf_cache[i] = intfc;
if (!intfc)
return -ENOMEM;
@@ -880,7 +919,11 @@ int usb_get_configuration(struct usb_device *dev)
dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
}
- if (ncfg < 1) {
+ if (ncfg < 1 && dev->quirks & USB_QUIRK_FORCE_ONE_CONFIG) {
+ dev_info(ddev, "Device claims zero configurations, forcing to 1\n");
+ dev->descriptor.bNumConfigurations = 1;
+ ncfg = 1;
+ } else if (ncfg < 1) {
dev_err(ddev, "no configurations\n");
return -EINVAL;
}
@@ -993,7 +1036,12 @@ int usb_get_bos_descriptor(struct usb_device *dev)
__u8 cap_type;
int ret;
- bos = kzalloc(sizeof(*bos), GFP_KERNEL);
+ if (dev->quirks & USB_QUIRK_NO_BOS) {
+ dev_dbg(ddev, "skipping BOS descriptor\n");
+ return -ENOMSG;
+ }
+
+ bos = kzalloc_obj(*bos);
if (!bos)
return -ENOMEM;
@@ -1014,7 +1062,7 @@ int usb_get_bos_descriptor(struct usb_device *dev)
if (total_len < length)
return -EINVAL;
- dev->bos = kzalloc(sizeof(*dev->bos), GFP_KERNEL);
+ dev->bos = kzalloc_obj(*dev->bos);
if (!dev->bos)
return -ENOMEM;
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index f6ce6e26e0d4..e191934623c7 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -245,7 +245,7 @@ static int usbdev_mmap(struct file *file, struct vm_area_struct *vma)
if (ret)
goto error;
- usbm = kzalloc(sizeof(struct usb_memory), GFP_KERNEL);
+ usbm = kzalloc_obj(struct usb_memory);
if (!usbm) {
ret = -ENOMEM;
goto error_decrease_mem;
@@ -402,7 +402,7 @@ static struct async *alloc_async(unsigned int numisoframes)
{
struct async *as;
- as = kzalloc(sizeof(struct async), GFP_KERNEL);
+ as = kzalloc_obj(struct async);
if (!as)
return NULL;
as->urb = usb_alloc_urb(numisoframes, GFP_KERNEL);
@@ -970,7 +970,7 @@ static int parse_usbdevfs_streams(struct usb_dev_state *ps,
if (num_streams_ret && (num_streams < 2 || num_streams > 65536))
return -EINVAL;
- eps = kmalloc_array(num_eps, sizeof(*eps), GFP_KERNEL);
+ eps = kmalloc_objs(*eps, num_eps);
if (!eps)
return -ENOMEM;
@@ -1039,7 +1039,7 @@ static int usbdev_open(struct inode *inode, struct file *file)
int ret;
ret = -ENOMEM;
- ps = kzalloc(sizeof(struct usb_dev_state), GFP_KERNEL);
+ ps = kzalloc_obj(struct usb_dev_state);
if (!ps)
goto out_free_ps;
@@ -1196,7 +1196,7 @@ static int do_proc_control(struct usb_dev_state *ps,
urb = usb_alloc_urb(0, GFP_NOIO);
if (!urb)
goto done;
- dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
+ dr = kmalloc_obj(struct usb_ctrlrequest, GFP_NOIO);
if (!dr)
goto done;
@@ -1670,7 +1670,7 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
/* min 8 byte setup packet */
if (uurb->buffer_length < 8)
return -EINVAL;
- dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+ dr = kmalloc_obj(struct usb_ctrlrequest);
if (!dr)
return -ENOMEM;
if (copy_from_user(dr, uurb->buffer, 8)) {
@@ -1805,9 +1805,8 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
as->mem_usage = u;
if (num_sgs) {
- as->urb->sg = kmalloc_array(num_sgs,
- sizeof(struct scatterlist),
- GFP_KERNEL | __GFP_NOWARN);
+ as->urb->sg = kmalloc_objs(struct scatterlist, num_sgs,
+ GFP_KERNEL | __GFP_NOWARN);
if (!as->urb->sg) {
ret = -ENOMEM;
goto error;
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 460d4dde5994..f63004417058 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -57,7 +57,7 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids,
if (fields < 2)
return -EINVAL;
- dynid = kzalloc(sizeof(*dynid), GFP_KERNEL);
+ dynid = kzalloc_obj(*dynid);
if (!dynid)
return -ENOMEM;
@@ -119,11 +119,11 @@ ssize_t usb_show_dynids(struct usb_dynids *dynids, char *buf)
guard(mutex)(&usb_dynids_lock);
list_for_each_entry(dynid, &dynids->list, node)
if (dynid->id.bInterfaceClass != 0)
- count += scnprintf(&buf[count], PAGE_SIZE - count, "%04x %04x %02x\n",
+ count += sysfs_emit_at(buf, count, "%04x %04x %02x\n",
dynid->id.idVendor, dynid->id.idProduct,
dynid->id.bInterfaceClass);
else
- count += scnprintf(&buf[count], PAGE_SIZE - count, "%04x %04x\n",
+ count += sysfs_emit_at(buf, count, "%04x %04x\n",
dynid->id.idVendor, dynid->id.idProduct);
return count;
}
@@ -332,10 +332,10 @@ static int usb_probe_interface(struct device *dev)
return error;
if (udev->authorized == 0) {
- dev_err(&intf->dev, "Device is not authorized for usage\n");
+ dev_info(&intf->dev, "Device is not authorized for usage\n");
return error;
} else if (intf->authorized == 0) {
- dev_err(&intf->dev, "Interface %d is not authorized for usage\n",
+ dev_info(&intf->dev, "Interface %d is not authorized for usage\n",
intf->altsetting->desc.bInterfaceNumber);
return error;
}
@@ -1415,16 +1415,34 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
int status = 0;
int i = 0, n = 0;
struct usb_interface *intf;
+ bool offload_active = false;
if (udev->state == USB_STATE_NOTATTACHED ||
udev->state == USB_STATE_SUSPENDED)
goto done;
+ usb_offload_set_pm_locked(udev, true);
+ if (msg.event == PM_EVENT_SUSPEND && usb_offload_check(udev)) {
+ dev_dbg(&udev->dev, "device offloaded, skip suspend.\n");
+ offload_active = true;
+ }
+
/* Suspend all the interfaces and then udev itself */
if (udev->actconfig) {
n = udev->actconfig->desc.bNumInterfaces;
for (i = n - 1; i >= 0; --i) {
intf = udev->actconfig->interface[i];
+ /*
+ * Don't suspend interfaces with remote wakeup while
+ * the controller is active. This preserves pending
+ * interrupt urbs, allowing interrupt events to be
+ * handled during system suspend.
+ */
+ if (offload_active && intf->needs_remote_wakeup) {
+ dev_dbg(&intf->dev,
+ "device offloaded, skip suspend.\n");
+ continue;
+ }
status = usb_suspend_interface(udev, intf, msg);
/* Ignore errors during system sleep transitions */
@@ -1435,7 +1453,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
}
}
if (status == 0) {
- status = usb_suspend_device(udev, msg);
+ if (!offload_active)
+ status = usb_suspend_device(udev, msg);
/*
* Ignore errors from non-root-hub devices during
@@ -1480,13 +1499,17 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
*/
} else {
udev->can_submit = 0;
- for (i = 0; i < 16; ++i) {
- usb_hcd_flush_endpoint(udev, udev->ep_out[i]);
- usb_hcd_flush_endpoint(udev, udev->ep_in[i]);
+ if (!offload_active) {
+ for (i = 0; i < 16; ++i) {
+ usb_hcd_flush_endpoint(udev, udev->ep_out[i]);
+ usb_hcd_flush_endpoint(udev, udev->ep_in[i]);
+ }
}
}
done:
+ if (status != 0)
+ usb_offload_set_pm_locked(udev, false);
dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
return status;
}
@@ -1516,21 +1539,40 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
int status = 0;
int i;
struct usb_interface *intf;
+ bool offload_active = false;
if (udev->state == USB_STATE_NOTATTACHED) {
status = -ENODEV;
goto done;
}
udev->can_submit = 1;
+ if (msg.event == PM_EVENT_RESUME)
+ offload_active = usb_offload_check(udev);
/* Resume the device */
- if (udev->state == USB_STATE_SUSPENDED || udev->reset_resume)
- status = usb_resume_device(udev, msg);
+ if (udev->state == USB_STATE_SUSPENDED || udev->reset_resume) {
+ if (!offload_active)
+ status = usb_resume_device(udev, msg);
+ else
+ dev_dbg(&udev->dev,
+ "device offloaded, skip resume.\n");
+ }
/* Resume the interfaces */
if (status == 0 && udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
intf = udev->actconfig->interface[i];
+ /*
+ * Interfaces with remote wakeup aren't suspended
+ * while the controller is active. This preserves
+ * pending interrupt urbs, allowing interrupt events
+ * to be handled during system suspend.
+ */
+ if (offload_active && intf->needs_remote_wakeup) {
+ dev_dbg(&intf->dev,
+ "device offloaded, skip resume.\n");
+ continue;
+ }
usb_resume_interface(udev, intf, msg,
udev->reset_resume);
}
@@ -1539,6 +1581,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);
+ usb_offload_set_pm_locked(udev, false);
if (!status)
udev->reset_resume = 0;
return status;
@@ -1723,8 +1766,6 @@ int usb_autoresume_device(struct usb_device *udev)
dev_vdbg(&udev->dev, "%s: cnt %d -> %d\n",
__func__, atomic_read(&udev->dev.power.usage_count),
status);
- if (status > 0)
- status = 0;
return status;
}
@@ -1774,13 +1815,11 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
void usb_autopm_put_interface_async(struct usb_interface *intf)
{
struct usb_device *udev = interface_to_usbdev(intf);
- int status;
usb_mark_last_busy(udev);
- status = pm_runtime_put(&intf->dev);
- dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n",
- __func__, atomic_read(&intf->dev.power.usage_count),
- status);
+ pm_runtime_put(&intf->dev);
+ dev_vdbg(&intf->dev, "%s: cnt %d\n",
+ __func__, atomic_read(&intf->dev.power.usage_count));
}
EXPORT_SYMBOL_GPL(usb_autopm_put_interface_async);
@@ -1829,8 +1868,6 @@ int usb_autopm_get_interface(struct usb_interface *intf)
dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n",
__func__, atomic_read(&intf->dev.power.usage_count),
status);
- if (status > 0)
- status = 0;
return status;
}
EXPORT_SYMBOL_GPL(usb_autopm_get_interface);
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
index e48399401608..e00eaf9e22cd 100644
--- a/drivers/usb/core/endpoint.c
+++ b/drivers/usb/core/endpoint.c
@@ -26,14 +26,6 @@ struct ep_device {
#define to_ep_device(_dev) \
container_of(_dev, struct ep_device, dev)
-struct ep_attribute {
- struct attribute attr;
- ssize_t (*show)(struct usb_device *,
- struct usb_endpoint_descriptor *, char *);
-};
-#define to_ep_attribute(_attr) \
- container_of(_attr, struct ep_attribute, attr)
-
#define usb_ep_attr(field, format_string) \
static ssize_t field##_show(struct device *dev, \
struct device_attribute *attr, \
@@ -154,7 +146,7 @@ int usb_create_ep_devs(struct device *parent,
struct ep_device *ep_dev;
int retval;
- ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL);
+ ep_dev = kzalloc_obj(*ep_dev);
if (!ep_dev) {
retval = -ENOMEM;
goto exit;
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index 9c6ae5e1198b..a48994e11ef3 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -243,7 +243,7 @@ int usb_generic_driver_probe(struct usb_device *udev)
* with the driver core and lets interface drivers bind to them.
*/
if (udev->authorized == 0)
- dev_err(&udev->dev, "Device is not authorized for usage\n");
+ dev_info(&udev->dev, "Device is not authorized for usage\n");
else {
c = usb_choose_configuration(udev);
if (c >= 0) {
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 56b534f59907..cd223475917e 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -210,7 +210,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct hc_driver *driver)
hcd->amd_resume_bug = usb_hcd_amd_resume_bug(dev, driver);
if (driver->flags & HCD_MEMORY) {
- /* EHCI, OHCI */
+ /* XHCI, EHCI, OHCI */
hcd->rsrc_start = pci_resource_start(dev, 0);
hcd->rsrc_len = pci_resource_len(dev, 0);
if (!devm_request_mem_region(&dev->dev, hcd->rsrc_start,
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index a75cf1f6d741..b181b43a35dc 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -77,10 +77,6 @@
/*-------------------------------------------------------------------------*/
-/* Keep track of which host controller drivers are loaded */
-unsigned long usb_hcds_loaded;
-EXPORT_SYMBOL_GPL(usb_hcds_loaded);
-
/* host controllers we manage */
DEFINE_IDR (usb_bus_idr);
EXPORT_SYMBOL_GPL (usb_bus_idr);
@@ -332,9 +328,7 @@ static const u8 ss_rh_config_descriptor[] = {
USB_DT_ENDPOINT, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* __u8 ep_bmAttributes; Interrupt */
- /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
- * see hub.c:hub_configure() for details. */
- (USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
+ 0x02, 0x00, /* __le16 ep_wMaxPacketSize; 2 bytes per USB3 10.15.1 */
0x0c, /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */
/* one SuperSpeed endpoint companion descriptor */
@@ -775,7 +769,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status);
/* timer callback */
static void rh_timer_func (struct timer_list *t)
{
- struct usb_hcd *_hcd = from_timer(_hcd, t, rh_timer);
+ struct usb_hcd *_hcd = timer_container_of(_hcd, t, rh_timer);
usb_hcd_poll_rh_status(_hcd);
}
@@ -842,7 +836,7 @@ static int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
} else { /* Status URB */
if (!hcd->uses_new_polling)
- del_timer (&hcd->rh_timer);
+ timer_delete(&hcd->rh_timer);
if (urb == hcd->status_urb) {
hcd->status_urb = NULL;
usb_hcd_unlink_urb_from_ep(hcd, urb);
@@ -1342,29 +1336,35 @@ void usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
if (IS_ENABLED(CONFIG_HAS_DMA) &&
- (urb->transfer_flags & URB_DMA_MAP_SG))
+ (urb->transfer_flags & URB_DMA_MAP_SG)) {
dma_unmap_sg(hcd->self.sysdev,
urb->sg,
urb->num_sgs,
dir);
- else if (IS_ENABLED(CONFIG_HAS_DMA) &&
- (urb->transfer_flags & URB_DMA_MAP_PAGE))
+ } else if (IS_ENABLED(CONFIG_HAS_DMA) &&
+ (urb->transfer_flags & URB_DMA_MAP_PAGE)) {
dma_unmap_page(hcd->self.sysdev,
urb->transfer_dma,
urb->transfer_buffer_length,
dir);
- else if (IS_ENABLED(CONFIG_HAS_DMA) &&
- (urb->transfer_flags & URB_DMA_MAP_SINGLE))
+ } else if (IS_ENABLED(CONFIG_HAS_DMA) &&
+ (urb->transfer_flags & URB_DMA_MAP_SINGLE)) {
dma_unmap_single(hcd->self.sysdev,
urb->transfer_dma,
urb->transfer_buffer_length,
dir);
- else if (urb->transfer_flags & URB_MAP_LOCAL)
+ } else if (urb->transfer_flags & URB_MAP_LOCAL) {
hcd_free_coherent(urb->dev->bus,
&urb->transfer_dma,
&urb->transfer_buffer,
urb->transfer_buffer_length,
dir);
+ } else if ((urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) && urb->sgt) {
+ dma_sync_sgtable_for_cpu(hcd->self.sysdev, urb->sgt, dir);
+ if (dir == DMA_FROM_DEVICE)
+ invalidate_kernel_vmap_range(urb->transfer_buffer,
+ urb->transfer_buffer_length);
+ }
/* Make it safe to call this routine more than once */
urb->transfer_flags &= ~(URB_DMA_MAP_SG | URB_DMA_MAP_PAGE |
@@ -1425,8 +1425,15 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
}
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
- if (urb->transfer_buffer_length != 0
- && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
+ if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) {
+ if (!urb->sgt)
+ return 0;
+
+ if (dir == DMA_TO_DEVICE)
+ flush_kernel_vmap_range(urb->transfer_buffer,
+ urb->transfer_buffer_length);
+ dma_sync_sgtable_for_device(hcd->self.sysdev, urb->sgt, dir);
+ } else if (urb->transfer_buffer_length != 0) {
if (hcd->localmem_pool) {
ret = hcd_alloc_coherent(
urb->dev->bus, mem_flags,
@@ -1609,7 +1616,7 @@ int usb_hcd_unlink_urb (struct urb *urb, int status)
if (retval == 0)
retval = -EINPROGRESS;
else if (retval != -EIDRM && retval != -EBUSY)
- dev_dbg(&udev->dev, "hcd_unlink_urb %pK fail %d\n",
+ dev_dbg(&udev->dev, "hcd_unlink_urb %p fail %d\n",
urb, retval);
usb_put_dev(udev);
}
@@ -1623,7 +1630,6 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
struct usb_anchor *anchor = urb->anchor;
int status = urb->unlinked;
- unsigned long flags;
urb->hcpriv = NULL;
if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
@@ -1641,14 +1647,13 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
/* pass ownership to the completion handler */
urb->status = status;
/*
- * Only collect coverage in the softirq context and disable interrupts
- * to avoid scenarios with nested remote coverage collection sections
- * that KCOV does not support.
- * See the comment next to kcov_remote_start_usb_softirq() for details.
+ * This function can be called in task context inside another remote
+ * coverage collection section, but kcov doesn't support that kind of
+ * recursion yet. Only collect coverage in softirq context for now.
*/
- flags = kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum);
+ kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum);
urb->complete(urb);
- kcov_remote_stop_softirq(flags);
+ kcov_remote_stop_softirq();
usb_anchor_resume_wakeups(anchor);
atomic_dec(&urb->use_count);
@@ -1706,10 +1711,10 @@ static void usb_giveback_urb_bh(struct work_struct *work)
* @urb: urb being returned to the USB device driver.
* @status: completion status code for the URB.
*
- * Context: atomic. The completion callback is invoked in caller's context.
- * For HCDs with HCD_BH flag set, the completion callback is invoked in BH
- * context (except for URBs submitted to the root hub which always complete in
- * caller's context).
+ * Context: atomic. The completion callback is invoked either in a work queue
+ * (BH) context or in the caller's context, depending on whether the HCD_BH
+ * flag is set in the @hcd structure, except that URBs submitted to the
+ * root hub always complete in BH context.
*
* This hands the URB from HCD to its USB device driver, using its
* completion function. The HCD has freed all per-urb resources
@@ -1786,7 +1791,7 @@ rescan:
/* kick hcd */
unlink1(hcd, urb, -ESHUTDOWN);
dev_dbg (hcd->self.controller,
- "shutdown urb %pK ep%d%s-%s\n",
+ "shutdown urb %p ep%d%s-%s\n",
urb, usb_endpoint_num(&ep->desc),
is_in ? "in" : "out",
usb_ep_type_string(usb_endpoint_type(&ep->desc)));
@@ -2153,7 +2158,7 @@ static struct urb *request_single_step_set_feature_urb(
urb->complete = usb_ehset_completion;
urb->status = -EINPROGRESS;
urb->actual_length = 0;
- urb->transfer_flags = URB_DIR_IN;
+ urb->transfer_flags = URB_DIR_IN | URB_NO_TRANSFER_DMA_MAP;
usb_get_urb(urb);
atomic_inc(&urb->use_count);
atomic_inc(&urb->dev->urbnum);
@@ -2186,7 +2191,7 @@ int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
if (!buf)
return -ENOMEM;
- dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+ dr = kmalloc_obj(struct usb_ctrlrequest);
if (!dr) {
kfree(buf);
return -ENOMEM;
@@ -2217,9 +2222,15 @@ int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
/* Complete remaining DATA and STATUS stages using the same URB */
urb->status = -EINPROGRESS;
+ urb->transfer_flags &= ~URB_NO_TRANSFER_DMA_MAP;
usb_get_urb(urb);
atomic_inc(&urb->use_count);
atomic_inc(&urb->dev->urbnum);
+ if (map_urb_for_dma(hcd, urb, GFP_KERNEL)) {
+ usb_put_urb(urb);
+ goto out1;
+ }
+
retval = hcd->driver->submit_single_step_set_feature(hcd, urb, 0);
if (!retval && !wait_for_completion_timeout(&done,
msecs_to_jiffies(2000))) {
@@ -2390,7 +2401,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
if (hcd->rh_registered) {
pm_wakeup_event(&hcd->self.root_hub->dev, 0);
set_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
- queue_work(pm_wq, &hcd->wakeup_work);
+ queue_work(system_freezable_wq, &hcd->wakeup_work);
}
spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
}
@@ -2554,16 +2565,14 @@ struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver,
if (!hcd)
return NULL;
if (primary_hcd == NULL) {
- hcd->address0_mutex = kmalloc(sizeof(*hcd->address0_mutex),
- GFP_KERNEL);
+ hcd->address0_mutex = kmalloc_obj(*hcd->address0_mutex);
if (!hcd->address0_mutex) {
kfree(hcd);
dev_dbg(dev, "hcd address0 mutex alloc failed\n");
return NULL;
}
mutex_init(hcd->address0_mutex);
- hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex),
- GFP_KERNEL);
+ hcd->bandwidth_mutex = kmalloc_obj(*hcd->bandwidth_mutex);
if (!hcd->bandwidth_mutex) {
kfree(hcd->address0_mutex);
kfree(hcd);
@@ -2679,18 +2688,18 @@ static void hcd_release(struct kref *kref)
kfree(hcd);
}
-struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd)
+struct usb_hcd *usb_get_hcd(struct usb_hcd *hcd)
{
if (hcd)
- kref_get (&hcd->kref);
+ kref_get(&hcd->kref);
return hcd;
}
EXPORT_SYMBOL_GPL(usb_get_hcd);
-void usb_put_hcd (struct usb_hcd *hcd)
+void usb_put_hcd(struct usb_hcd *hcd)
{
if (hcd)
- kref_put (&hcd->kref, hcd_release);
+ kref_put(&hcd->kref, hcd_release);
}
EXPORT_SYMBOL_GPL(usb_put_hcd);
@@ -2768,14 +2777,14 @@ static void usb_stop_hcd(struct usb_hcd *hcd)
{
hcd->rh_pollable = 0;
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
- del_timer_sync(&hcd->rh_timer);
+ timer_delete_sync(&hcd->rh_timer);
hcd->driver->stop(hcd);
hcd->state = HC_STATE_HALT;
/* In case the HCD restarted the timer, stop it again. */
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
- del_timer_sync(&hcd->rh_timer);
+ timer_delete_sync(&hcd->rh_timer);
}
/**
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index dcba4281ea48..24960ba9caa9 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -28,6 +28,7 @@
#include <linux/usb/otg.h>
#include <linux/usb/quirks.h>
#include <linux/workqueue.h>
+#include <linux/minmax.h>
#include <linux/mutex.h>
#include <linux/random.h>
#include <linux/pm_qos.h>
@@ -40,6 +41,7 @@
#include "hub.h"
#include "phy.h"
#include "otg_productlist.h"
+#include "trace.h"
#define USB_VENDOR_GENESYS_LOGIC 0x05e3
#define USB_VENDOR_SMSC 0x0424
@@ -68,6 +70,12 @@
*/
#define USB_SHORT_SET_ADDRESS_REQ_TIMEOUT 500 /* ms */
+/*
+ * Give SS hubs 200ms time after wake to train downstream links before
+ * assuming no port activity and allowing hub to runtime suspend back.
+ */
+#define USB_SS_PORT_U0_WAKE_TIME 200 /* ms */
+
/* Protect struct usb_device->state and ->children members
* Note: Both are also protected by ->dev.sem, except that ->state can
* change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */
@@ -271,10 +279,7 @@ static void usb_set_lpm_pel(struct usb_device *udev,
* device and the parent hub into U0. The exit latency is the bigger of
* the device exit latency or the hub exit latency.
*/
- if (udev_exit_latency > hub_exit_latency)
- first_link_pel = udev_exit_latency * 1000;
- else
- first_link_pel = hub_exit_latency * 1000;
+ first_link_pel = max(udev_exit_latency, hub_exit_latency) * 1000;
/*
* When the hub starts to receive the LFPS, there is a slight delay for
@@ -288,10 +293,7 @@ static void usb_set_lpm_pel(struct usb_device *udev,
* According to figure C-7 in the USB 3.0 spec, the PEL for this device
* is the greater of the two exit latencies.
*/
- if (first_link_pel > hub_pel)
- udev_lpm_params->pel = first_link_pel;
- else
- udev_lpm_params->pel = hub_pel;
+ udev_lpm_params->pel = max(first_link_pel, hub_pel);
}
/*
@@ -697,7 +699,7 @@ static void hub_resubmit_irq_urb(struct usb_hub *hub)
static void hub_retry_irq_urb(struct timer_list *t)
{
- struct usb_hub *hub = from_timer(hub, t, irq_urb_retry);
+ struct usb_hub *hub = timer_container_of(hub, t, irq_urb_retry);
hub_resubmit_irq_urb(hub);
}
@@ -927,7 +929,7 @@ int usb_hub_clear_tt_buffer(struct urb *urb)
* since each TT has "at least two" buffers that can need it (and
* there can be many TTs per hub). even if they're uncommon.
*/
- clear = kmalloc(sizeof *clear, GFP_ATOMIC);
+ clear = kmalloc_obj(*clear, GFP_ATOMIC);
if (clear == NULL) {
dev_err(&udev->dev, "can't save CLEAR_TT_BUFFER state\n");
/* FIXME recover somehow ... RESET_TT? */
@@ -1095,6 +1097,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
goto init2;
goto init3;
}
+
hub_get(hub);
/* The superspeed hub except for root hub has to use Hub Depth
@@ -1343,6 +1346,17 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
device_unlock(&hdev->dev);
}
+ if (type == HUB_RESUME && hub_is_superspeed(hub->hdev)) {
+ /* give usb3 downstream links training time after hub resume */
+ usb_autopm_get_interface_no_resume(
+ to_usb_interface(hub->intfdev));
+
+ queue_delayed_work(system_power_efficient_wq,
+ &hub->post_resume_work,
+ msecs_to_jiffies(USB_SS_PORT_U0_WAKE_TIME));
+ return;
+ }
+
hub_put(hub);
}
@@ -1361,6 +1375,14 @@ static void hub_init_func3(struct work_struct *ws)
hub_activate(hub, HUB_INIT3);
}
+static void hub_post_resume(struct work_struct *ws)
+{
+ struct usb_hub *hub = container_of(ws, struct usb_hub, post_resume_work.work);
+
+ usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
+ hub_put(hub);
+}
+
enum hub_quiescing_type {
HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND
};
@@ -1385,7 +1407,8 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
}
/* Stop hub_wq and related activity */
- del_timer_sync(&hub->irq_urb_retry);
+ timer_delete_sync(&hub->irq_urb_retry);
+ flush_delayed_work(&hub->post_resume_work);
usb_kill_urb(hub->urb);
if (hub->has_indicators)
cancel_delayed_work_sync(&hub->leds);
@@ -1438,20 +1461,20 @@ static int hub_configure(struct usb_hub *hub,
unsigned full_load;
unsigned maxchild;
- hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
+ hub->buffer = kmalloc_obj(*hub->buffer);
if (!hub->buffer) {
ret = -ENOMEM;
goto fail;
}
- hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);
+ hub->status = kmalloc_obj(*hub->status);
if (!hub->status) {
ret = -ENOMEM;
goto fail;
}
mutex_init(&hub->status_mutex);
- hub->descriptor = kzalloc(sizeof(*hub->descriptor), GFP_KERNEL);
+ hub->descriptor = kzalloc_obj(*hub->descriptor);
if (!hub->descriptor) {
ret = -ENOMEM;
goto fail;
@@ -1499,7 +1522,7 @@ static int hub_configure(struct usb_hub *hub,
dev_info(hub_dev, "%d port%s detected\n", maxchild,
str_plural(maxchild));
- hub->ports = kcalloc(maxchild, sizeof(struct usb_port *), GFP_KERNEL);
+ hub->ports = kzalloc_objs(struct usb_port *, maxchild);
if (!hub->ports) {
ret = -ENOMEM;
goto fail;
@@ -1935,7 +1958,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
/* We found a hub */
dev_info(&intf->dev, "USB hub found\n");
- hub = kzalloc(sizeof(*hub), GFP_KERNEL);
+ hub = kzalloc_obj(*hub);
if (!hub)
return -ENOMEM;
@@ -1944,6 +1967,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
hub->hdev = hdev;
INIT_DELAYED_WORK(&hub->leds, led_work);
INIT_DELAYED_WORK(&hub->init_work, NULL);
+ INIT_DELAYED_WORK(&hub->post_resume_work, hub_post_resume);
INIT_WORK(&hub->events, hub_event);
INIT_LIST_HEAD(&hub->onboard_devs);
spin_lock_init(&hub->irq_urb_lock);
@@ -2119,6 +2143,21 @@ static void update_port_device_state(struct usb_device *udev)
}
}
+static void update_usb_device_state(struct usb_device *udev,
+ enum usb_device_state new_state)
+{
+ if (udev->state == USB_STATE_SUSPENDED &&
+ new_state != USB_STATE_SUSPENDED)
+ udev->active_duration -= jiffies;
+ else if (new_state == USB_STATE_SUSPENDED &&
+ udev->state != USB_STATE_SUSPENDED)
+ udev->active_duration += jiffies;
+
+ udev->state = new_state;
+ update_port_device_state(udev);
+ trace_usb_set_device_state(udev);
+}
+
static void recursively_mark_NOTATTACHED(struct usb_device *udev)
{
struct usb_hub *hub = usb_hub_to_struct_hub(udev);
@@ -2128,10 +2167,7 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev)
if (hub->ports[i]->child)
recursively_mark_NOTATTACHED(hub->ports[i]->child);
}
- if (udev->state == USB_STATE_SUSPENDED)
- udev->active_duration -= jiffies;
- udev->state = USB_STATE_NOTATTACHED;
- update_port_device_state(udev);
+ update_usb_device_state(udev, USB_STATE_NOTATTACHED);
}
/**
@@ -2181,14 +2217,7 @@ void usb_set_device_state(struct usb_device *udev,
else
wakeup = 0;
}
- if (udev->state == USB_STATE_SUSPENDED &&
- new_state != USB_STATE_SUSPENDED)
- udev->active_duration -= jiffies;
- else if (new_state == USB_STATE_SUSPENDED &&
- udev->state != USB_STATE_SUSPENDED)
- udev->active_duration += jiffies;
- udev->state = new_state;
- update_port_device_state(udev);
+ update_usb_device_state(udev, new_state);
} else
recursively_mark_NOTATTACHED(udev);
spin_unlock_irqrestore(&device_state_lock, flags);
@@ -2337,6 +2366,9 @@ void usb_disconnect(struct usb_device **pdev)
usb_remove_ep_devs(&udev->ep0);
usb_unlock_device(udev);
+ if (udev->usb4_link)
+ device_link_del(udev->usb4_link);
+
/* Unregister the device. The device driver is responsible
* for de-configuring the device and invoking the remove-device
* notifier chain (used by usbfs and possibly others).
@@ -4110,7 +4142,7 @@ static int usb_req_set_sel(struct usb_device *udev)
* which may be initiated by an error path of a mass storage driver.
* Therefore, use GFP_NOIO.
*/
- sel_values = kmalloc(sizeof *(sel_values), GFP_NOIO);
+ sel_values = kmalloc_obj(*(sel_values), GFP_NOIO);
if (!sel_values)
return -ENOMEM;
@@ -4160,7 +4192,7 @@ static int usb_set_device_initiated_lpm(struct usb_device *udev,
"for unconfigured device.\n",
__func__, str_enable_disable(enable),
usb3_lpm_names[state]);
- return 0;
+ return -EINVAL;
}
if (enable) {
@@ -4234,9 +4266,9 @@ static int usb_set_lpm_timeout(struct usb_device *udev,
}
/*
- * Don't allow device intiated U1/U2 if the system exit latency + one bus
- * interval is greater than the minimum service interval of any active
- * periodic endpoint. See USB 3.2 section 9.4.9
+ * Don't allow device intiated U1/U2 if device isn't in the configured state,
+ * or the system exit latency + one bus interval is greater than the minimum
+ * service interval of any active periodic endpoint. See USB 3.2 section 9.4.9
*/
static bool usb_device_may_initiate_lpm(struct usb_device *udev,
enum usb3_link_state state)
@@ -4244,7 +4276,7 @@ static bool usb_device_may_initiate_lpm(struct usb_device *udev,
unsigned int sel; /* us */
int i, j;
- if (!udev->lpm_devinit_allow)
+ if (!udev->lpm_devinit_allow || !udev->actconfig)
return false;
if (state == USB3_LPM_U1)
@@ -4292,7 +4324,7 @@ static bool usb_device_may_initiate_lpm(struct usb_device *udev,
* driver know about it. If that call fails, it should be harmless, and just
* take up more slightly more bus bandwidth for unnecessary U1/U2 exit latency.
*/
-static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
+static int usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
enum usb3_link_state state)
{
int timeout;
@@ -4301,7 +4333,7 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
/* Skip if the device BOS descriptor couldn't be read */
if (!udev->bos)
- return;
+ return -EINVAL;
u1_mel = udev->bos->ss_cap->bU1devExitLat;
u2_mel = udev->bos->ss_cap->bU2DevExitLat;
@@ -4312,7 +4344,7 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
*/
if ((state == USB3_LPM_U1 && u1_mel == 0) ||
(state == USB3_LPM_U2 && u2_mel == 0))
- return;
+ return -EINVAL;
/* We allow the host controller to set the U1/U2 timeout internally
* first, so that it can change its schedule to account for the
@@ -4323,13 +4355,13 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
/* xHCI host controller doesn't want to enable this LPM state. */
if (timeout == 0)
- return;
+ return -EINVAL;
if (timeout < 0) {
dev_warn(&udev->dev, "Could not enable %s link state, "
"xHCI error %i.\n", usb3_lpm_names[state],
timeout);
- return;
+ return timeout;
}
if (usb_set_lpm_timeout(udev, state, timeout)) {
@@ -4338,29 +4370,15 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
* host know that this link state won't be enabled.
*/
hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
- return;
- }
-
- /* Only a configured device will accept the Set Feature
- * U1/U2_ENABLE
- */
- if (udev->actconfig &&
- usb_device_may_initiate_lpm(udev, state)) {
- if (usb_set_device_initiated_lpm(udev, state, true)) {
- /*
- * Request to enable device initiated U1/U2 failed,
- * better to turn off lpm in this case.
- */
- usb_set_lpm_timeout(udev, state, 0);
- hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
- return;
- }
+ return -EBUSY;
}
if (state == USB3_LPM_U1)
udev->usb3_lpm_u1_enabled = 1;
else if (state == USB3_LPM_U2)
udev->usb3_lpm_u2_enabled = 1;
+
+ return 0;
}
/*
* Disable the hub-initiated U1/U2 idle timeouts, and disable device-initiated
@@ -4393,8 +4411,6 @@ static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
if (usb_set_lpm_timeout(udev, state, 0))
return -EBUSY;
- usb_set_device_initiated_lpm(udev, state, false);
-
if (hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state))
dev_warn(&udev->dev, "Could not disable xHCI %s timeout, "
"bus schedule bandwidth may be impacted.\n",
@@ -4424,6 +4440,7 @@ static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
int usb_disable_lpm(struct usb_device *udev)
{
struct usb_hcd *hcd;
+ int err;
if (!udev || !udev->parent ||
udev->speed < USB_SPEED_SUPER ||
@@ -4441,14 +4458,19 @@ int usb_disable_lpm(struct usb_device *udev)
/* If LPM is enabled, attempt to disable it. */
if (usb_disable_link_state(hcd, udev, USB3_LPM_U1))
- goto enable_lpm;
+ goto disable_failed;
if (usb_disable_link_state(hcd, udev, USB3_LPM_U2))
- goto enable_lpm;
+ goto disable_failed;
+
+ err = usb_set_device_initiated_lpm(udev, USB3_LPM_U1, false);
+ if (!err)
+ usb_set_device_initiated_lpm(udev, USB3_LPM_U2, false);
return 0;
-enable_lpm:
- usb_enable_lpm(udev);
+disable_failed:
+ udev->lpm_disable_count--;
+
return -EBUSY;
}
EXPORT_SYMBOL_GPL(usb_disable_lpm);
@@ -4509,10 +4531,24 @@ void usb_enable_lpm(struct usb_device *udev)
port_dev = hub->ports[udev->portnum - 1];
if (port_dev->usb3_lpm_u1_permit)
- usb_enable_link_state(hcd, udev, USB3_LPM_U1);
+ if (usb_enable_link_state(hcd, udev, USB3_LPM_U1))
+ return;
if (port_dev->usb3_lpm_u2_permit)
- usb_enable_link_state(hcd, udev, USB3_LPM_U2);
+ if (usb_enable_link_state(hcd, udev, USB3_LPM_U2))
+ return;
+
+ /*
+ * Enable device initiated U1/U2 with a SetFeature(U1/U2_ENABLE) request
+ * if system exit latency is short enough and device is configured
+ */
+ if (usb_device_may_initiate_lpm(udev, USB3_LPM_U1)) {
+ if (usb_set_device_initiated_lpm(udev, USB3_LPM_U1, true))
+ return;
+
+ if (usb_device_may_initiate_lpm(udev, USB3_LPM_U2))
+ usb_set_device_initiated_lpm(udev, USB3_LPM_U2, true);
+ }
}
EXPORT_SYMBOL_GPL(usb_enable_lpm);
@@ -4708,8 +4744,6 @@ void usb_ep0_reinit(struct usb_device *udev)
}
EXPORT_SYMBOL_GPL(usb_ep0_reinit);
-#define usb_sndaddr0pipe() (PIPE_CONTROL << 30)
-
static int hub_set_address(struct usb_device *udev, int devnum)
{
int retval;
@@ -4733,7 +4767,7 @@ static int hub_set_address(struct usb_device *udev, int devnum)
if (hcd->driver->address_device)
retval = hcd->driver->address_device(hcd, udev, timeout_ms);
else
- retval = usb_control_msg(udev, usb_sndaddr0pipe(),
+ retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_SET_ADDRESS, 0, devnum, 0,
NULL, 0, timeout_ms);
if (retval == 0) {
@@ -5202,7 +5236,7 @@ check_highspeed(struct usb_hub *hub, struct usb_device *udev, int port1)
if (udev->quirks & USB_QUIRK_DEVICE_QUALIFIER)
return;
- qual = kmalloc(sizeof *qual, GFP_KERNEL);
+ qual = kmalloc_obj(*qual);
if (qual == NULL)
return;
@@ -5718,6 +5752,7 @@ static void port_event(struct usb_hub *hub, int port1)
struct usb_device *hdev = hub->hdev;
u16 portstatus, portchange;
int i = 0;
+ int err;
connect_change = test_bit(port1, hub->change_bits);
clear_bit(port1, hub->event_bits);
@@ -5814,8 +5849,11 @@ static void port_event(struct usb_hub *hub, int port1)
} 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)
+ err = hub_port_reset(hub, port1, NULL,
+ HUB_BH_RESET_TIME, true);
+ if (!udev && err == -ENOTCONN)
+ connect_change = 0;
+ else if (err < 0)
hub_port_disable(hub, port1, 1);
} else {
dev_dbg(&port_dev->dev, "do warm reset, full device\n");
@@ -6040,7 +6078,7 @@ int usb_hub_init(void)
* device was gone before the EHCI controller had handed its port
* over to the companion full-speed controller.
*/
- hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0);
+ hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE | WQ_PERCPU, 0);
if (hub_wq)
return 0;
@@ -6135,6 +6173,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
struct usb_hub *parent_hub;
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
struct usb_device_descriptor descriptor;
+ struct usb_interface *intf;
struct usb_host_bos *bos;
int i, j, ret = 0;
int port1 = udev->portnum;
@@ -6192,6 +6231,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
if (!udev->actconfig)
goto done;
+ /*
+ * Some devices can't handle setting default altsetting 0 with a
+ * Set-Interface request. Disable host-side endpoints of those
+ * interfaces here. Enable and reset them back after host has set
+ * its internal endpoint structures during usb_hcd_alloc_bandwith()
+ */
+ for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+ intf = udev->actconfig->interface[i];
+ if (intf->cur_altsetting->desc.bAlternateSetting == 0)
+ usb_disable_interface(udev, intf, true);
+ }
+
mutex_lock(hcd->bandwidth_mutex);
ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
if (ret < 0) {
@@ -6223,12 +6274,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
*/
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
struct usb_host_config *config = udev->actconfig;
- struct usb_interface *intf = config->interface[i];
struct usb_interface_descriptor *desc;
+ intf = config->interface[i];
desc = &intf->cur_altsetting->desc;
if (desc->bAlternateSetting == 0) {
- usb_disable_interface(udev, intf, true);
usb_enable_interface(udev, intf, true);
ret = 0;
} else {
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index e6ae73f8a95d..9ebc5ef54a32 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -70,6 +70,7 @@ struct usb_hub {
u8 indicator[USB_MAXCHILDREN];
struct delayed_work leds;
struct delayed_work init_work;
+ struct delayed_work post_resume_work;
struct work_struct events;
spinlock_t irq_urb_lock;
struct timer_list irq_urb_retry;
diff --git a/drivers/usb/core/ledtrig-usbport.c b/drivers/usb/core/ledtrig-usbport.c
index 5e3c515991f3..8881644777a7 100644
--- a/drivers/usb/core/ledtrig-usbport.c
+++ b/drivers/usb/core/ledtrig-usbport.c
@@ -190,7 +190,7 @@ static int usbport_trig_add_port(struct usbport_trig_data *usbport_data,
size_t len;
int err;
- port = kzalloc(sizeof(*port), GFP_KERNEL);
+ port = kzalloc_obj(*port);
if (!port) {
err = -ENOMEM;
goto err_out;
@@ -305,7 +305,7 @@ static int usbport_trig_activate(struct led_classdev *led_cdev)
struct usbport_trig_data *usbport_data;
int err;
- usbport_data = kzalloc(sizeof(*usbport_data), GFP_KERNEL);
+ usbport_data = kzalloc_obj(*usbport_data);
if (!usbport_data)
return -ENOMEM;
usbport_data->led_cdev = led_cdev;
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index d2b2787be409..75e2bfd744a9 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -42,16 +42,19 @@ static void usb_api_blocking_completion(struct urb *urb)
/*
- * Starts urb and waits for completion or timeout. Note that this call
- * is NOT interruptible. Many device driver i/o requests should be
- * interruptible and therefore these drivers should implement their
- * own interruptible routines.
+ * Starts urb and waits for completion or timeout.
+ * Whether or not the wait is killable depends on the flag passed in.
+ * For example, compare usb_bulk_msg() and usb_bulk_msg_killable().
+ *
+ * For non-killable waits, we enforce a maximum limit on the timeout value.
*/
-static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
+static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length,
+ bool killable)
{
struct api_context ctx;
unsigned long expire;
int retval;
+ long rc;
init_completion(&ctx.done);
urb->context = &ctx;
@@ -60,13 +63,24 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
if (unlikely(retval))
goto out;
- expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
- if (!wait_for_completion_timeout(&ctx.done, expire)) {
+ if (!killable && (timeout <= 0 || timeout > USB_MAX_SYNCHRONOUS_TIMEOUT))
+ timeout = USB_MAX_SYNCHRONOUS_TIMEOUT;
+ expire = (timeout > 0) ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
+ if (killable)
+ rc = wait_for_completion_killable_timeout(&ctx.done, expire);
+ else
+ rc = wait_for_completion_timeout(&ctx.done, expire);
+ if (rc <= 0) {
usb_kill_urb(urb);
- retval = (ctx.status == -ENOENT ? -ETIMEDOUT : ctx.status);
+ if (ctx.status != -ENOENT)
+ retval = ctx.status;
+ else if (rc == 0)
+ retval = -ETIMEDOUT;
+ else
+ retval = rc;
dev_dbg(&urb->dev->dev,
- "%s timed out on ep%d%s len=%u/%u\n",
+ "%s timed out or killed on ep%d%s len=%u/%u\n",
current->comm,
usb_endpoint_num(&urb->ep->desc),
usb_urb_dir_in(urb) ? "in" : "out",
@@ -100,7 +114,7 @@ static int usb_internal_control_msg(struct usb_device *usb_dev,
usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data,
len, usb_api_blocking_completion, NULL);
- retv = usb_start_wait_urb(urb, timeout, &length);
+ retv = usb_start_wait_urb(urb, timeout, &length, false);
if (retv < 0)
return retv;
else
@@ -117,8 +131,7 @@ static int usb_internal_control_msg(struct usb_device *usb_dev,
* @index: USB message index value
* @data: pointer to the data to send
* @size: length in bytes of the data to send
- * @timeout: time in msecs to wait for the message to complete before timing
- * out (if 0 the wait is forever)
+ * @timeout: time in msecs to wait for the message to complete before timing out
*
* Context: task context, might sleep.
*
@@ -141,7 +154,7 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
struct usb_ctrlrequest *dr;
int ret;
- dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
+ dr = kmalloc_obj(struct usb_ctrlrequest, GFP_NOIO);
if (!dr)
return -ENOMEM;
@@ -173,8 +186,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg);
* @index: USB message index value
* @driver_data: pointer to the data to send
* @size: length in bytes of the data to send
- * @timeout: time in msecs to wait for the message to complete before timing
- * out (if 0 the wait is forever)
+ * @timeout: time in msecs to wait for the message to complete before timing out
* @memflags: the flags for memory allocation for buffers
*
* Context: !in_interrupt ()
@@ -232,8 +244,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg_send);
* @index: USB message index value
* @driver_data: pointer to the data to be filled in by the message
* @size: length in bytes of the data to be received
- * @timeout: time in msecs to wait for the message to complete before timing
- * out (if 0 the wait is forever)
+ * @timeout: time in msecs to wait for the message to complete before timing out
* @memflags: the flags for memory allocation for buffers
*
* Context: !in_interrupt ()
@@ -304,8 +315,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg_recv);
* @len: length in bytes of the data to send
* @actual_length: pointer to a location to put the actual length transferred
* in bytes
- * @timeout: time in msecs to wait for the message to complete before
- * timing out (if 0 the wait is forever)
+ * @timeout: time in msecs to wait for the message to complete before timing out
*
* Context: task context, might sleep.
*
@@ -337,8 +347,7 @@ EXPORT_SYMBOL_GPL(usb_interrupt_msg);
* @len: length in bytes of the data to send
* @actual_length: pointer to a location to put the actual length transferred
* in bytes
- * @timeout: time in msecs to wait for the message to complete before
- * timing out (if 0 the wait is forever)
+ * @timeout: time in msecs to wait for the message to complete before timing out
*
* Context: task context, might sleep.
*
@@ -385,10 +394,59 @@ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
usb_api_blocking_completion, NULL);
- return usb_start_wait_urb(urb, timeout, actual_length);
+ return usb_start_wait_urb(urb, timeout, actual_length, false);
}
EXPORT_SYMBOL_GPL(usb_bulk_msg);
+/**
+ * usb_bulk_msg_killable - Builds a bulk urb, sends it off and waits for completion in a killable state
+ * @usb_dev: pointer to the usb device to send the message to
+ * @pipe: endpoint "pipe" to send the message to
+ * @data: pointer to the data to send
+ * @len: length in bytes of the data to send
+ * @actual_length: pointer to a location to put the actual length transferred
+ * in bytes
+ * @timeout: time in msecs to wait for the message to complete before
+ * timing out (if <= 0, the wait is as long as possible)
+ *
+ * Context: task context, might sleep.
+ *
+ * This function is just like usb_blk_msg(), except that it waits in a
+ * killable state and there is no limit on the timeout length.
+ *
+ * Return:
+ * If successful, 0. Otherwise a negative error number. The number of actual
+ * bytes transferred will be stored in the @actual_length parameter.
+ *
+ */
+int usb_bulk_msg_killable(struct usb_device *usb_dev, unsigned int pipe,
+ void *data, int len, int *actual_length, int timeout)
+{
+ struct urb *urb;
+ struct usb_host_endpoint *ep;
+
+ ep = usb_pipe_endpoint(usb_dev, pipe);
+ if (!ep || len < 0)
+ return -EINVAL;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+
+ if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_INT) {
+ pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
+ usb_fill_int_urb(urb, usb_dev, pipe, data, len,
+ usb_api_blocking_completion, NULL,
+ ep->desc.bInterval);
+ } else
+ usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
+ usb_api_blocking_completion, NULL);
+
+ return usb_start_wait_urb(urb, timeout, actual_length, true);
+}
+EXPORT_SYMBOL_GPL(usb_bulk_msg_killable);
+
/*-------------------------------------------------------------------*/
static void sg_clean(struct usb_sg_request *io)
@@ -526,7 +584,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
}
/* initialize all the urbs we'll use */
- io->urbs = kmalloc_array(io->entries, sizeof(*io->urbs), mem_flags);
+ io->urbs = kmalloc_objs(*io->urbs, io->entries, mem_flags);
if (!io->urbs)
goto nomem;
@@ -1005,7 +1063,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
}
EXPORT_SYMBOL_GPL(usb_string);
-/* one UTF-8-encoded 16-bit character has at most three bytes */
+/* one 16-bit character, when UTF-8-encoded, has at most three bytes */
#define MAX_USB_STRING_SIZE (127 * 3 + 1)
/**
@@ -1026,16 +1084,18 @@ char *usb_cache_string(struct usb_device *udev, int index)
return NULL;
buf = kmalloc(MAX_USB_STRING_SIZE, GFP_NOIO);
- if (buf) {
- len = usb_string(udev, index, buf, MAX_USB_STRING_SIZE);
- if (len > 0) {
- smallbuf = kmalloc(++len, GFP_NOIO);
- if (!smallbuf)
- return buf;
- memcpy(smallbuf, buf, len);
- }
+ if (!buf)
+ return NULL;
+
+ len = usb_string(udev, index, buf, MAX_USB_STRING_SIZE);
+ if (len <= 0) {
kfree(buf);
+ return NULL;
}
+
+ smallbuf = krealloc(buf, len + 1, GFP_NOIO);
+ if (unlikely(!smallbuf))
+ return buf;
return smallbuf;
}
EXPORT_SYMBOL_GPL(usb_cache_string);
@@ -1058,7 +1118,7 @@ struct usb_device_descriptor *usb_get_device_descriptor(struct usb_device *udev)
struct usb_device_descriptor *desc;
int ret;
- desc = kmalloc(sizeof(*desc), GFP_NOIO);
+ desc = kmalloc_obj(*desc, GFP_NOIO);
if (!desc)
return ERR_PTR(-ENOMEM);
@@ -2028,15 +2088,13 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
n = nintf = 0;
if (cp) {
nintf = cp->desc.bNumInterfaces;
- new_interfaces = kmalloc_array(nintf, sizeof(*new_interfaces),
- GFP_NOIO);
+ new_interfaces = kmalloc_objs(*new_interfaces, nintf, GFP_NOIO);
if (!new_interfaces)
return -ENOMEM;
for (; n < nintf; ++n) {
- new_interfaces[n] = kzalloc(
- sizeof(struct usb_interface),
- GFP_NOIO);
+ new_interfaces[n] = kzalloc_obj(struct usb_interface,
+ GFP_NOIO);
if (!new_interfaces[n]) {
ret = -ENOMEM;
free_interfaces:
@@ -2289,7 +2347,7 @@ int usb_driver_set_configuration(struct usb_device *udev, int config)
{
struct set_config_request *req;
- req = kmalloc(sizeof(*req), GFP_KERNEL);
+ req = kmalloc_obj(*req);
if (!req)
return -ENOMEM;
req->udev = udev;
@@ -2431,7 +2489,7 @@ int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr,
break;
case USB_CDC_MBIM_EXTENDED_TYPE:
if (elength < sizeof(struct usb_cdc_mbim_extended_desc))
- break;
+ goto next_desc;
hdr->usb_cdc_mbim_extended_desc =
(struct usb_cdc_mbim_extended_desc *)buffer;
break;
diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c
index 763e4122ed5b..a6ee20d8774e 100644
--- a/drivers/usb/core/of.c
+++ b/drivers/usb/core/of.c
@@ -79,17 +79,13 @@ EXPORT_SYMBOL_GPL(usb_of_has_combined_node);
static bool usb_of_has_devices_or_graph(const struct usb_device *hub)
{
const struct device_node *np = hub->dev.of_node;
- struct device_node *child;
if (of_graph_is_present(np))
return true;
- for_each_child_of_node(np, child) {
- if (of_property_present(child, "reg")) {
- of_node_put(child);
+ for_each_child_of_node_scoped(np, child)
+ if (of_property_present(child, "reg"))
return true;
- }
- }
return false;
}
diff --git a/drivers/usb/core/offload.c b/drivers/usb/core/offload.c
new file mode 100644
index 000000000000..9db3cfedd29c
--- /dev/null
+++ b/drivers/usb/core/offload.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * offload.c - USB offload related functions
+ *
+ * Copyright (c) 2025, Google LLC.
+ *
+ * Author: Guan-Yu Lin
+ */
+
+#include <linux/usb.h>
+
+#include "usb.h"
+
+/**
+ * usb_offload_get - increment the offload_usage of a USB device
+ * @udev: the USB device to increment its offload_usage
+ *
+ * Incrementing the offload_usage of a usb_device indicates that offload is
+ * enabled on this usb_device; that is, another entity is actively handling USB
+ * transfers. This information allows the USB driver to adjust its power
+ * management policy based on offload activity.
+ *
+ * Return: 0 on success. A negative error code otherwise.
+ */
+int usb_offload_get(struct usb_device *udev)
+{
+ int ret = 0;
+
+ if (!usb_get_dev(udev))
+ return -ENODEV;
+
+ if (pm_runtime_get_if_active(&udev->dev) != 1) {
+ ret = -EBUSY;
+ goto err_rpm;
+ }
+
+ spin_lock(&udev->offload_lock);
+
+ if (udev->offload_pm_locked) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ udev->offload_usage++;
+
+err:
+ spin_unlock(&udev->offload_lock);
+ pm_runtime_put_autosuspend(&udev->dev);
+err_rpm:
+ usb_put_dev(udev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_offload_get);
+
+/**
+ * usb_offload_put - drop the offload_usage of a USB device
+ * @udev: the USB device to drop its offload_usage
+ *
+ * The inverse operation of usb_offload_get, which drops the offload_usage of
+ * a USB device. This information allows the USB driver to adjust its power
+ * management policy based on offload activity.
+ *
+ * Return: 0 on success. A negative error code otherwise.
+ */
+int usb_offload_put(struct usb_device *udev)
+{
+ int ret = 0;
+
+ if (!usb_get_dev(udev))
+ return -ENODEV;
+
+ if (pm_runtime_get_if_active(&udev->dev) != 1) {
+ ret = -EBUSY;
+ goto err_rpm;
+ }
+
+ spin_lock(&udev->offload_lock);
+
+ if (udev->offload_pm_locked) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ /* Drop the count when it wasn't 0, ignore the operation otherwise. */
+ if (udev->offload_usage)
+ udev->offload_usage--;
+
+err:
+ spin_unlock(&udev->offload_lock);
+ pm_runtime_put_autosuspend(&udev->dev);
+err_rpm:
+ usb_put_dev(udev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_offload_put);
+
+/**
+ * usb_offload_check - check offload activities on a USB device
+ * @udev: the USB device to check its offload activity.
+ *
+ * Check if there are any offload activity on the USB device right now. This
+ * information could be used for power management or other forms of resource
+ * management.
+ *
+ * The caller must hold @udev's device lock. In addition, the caller should
+ * ensure the device itself and the downstream usb devices are all marked as
+ * "offload_pm_locked" to ensure the correctness of the return value.
+ *
+ * Returns true on any offload activity, false otherwise.
+ */
+bool usb_offload_check(struct usb_device *udev) __must_hold(&udev->dev->mutex)
+{
+ struct usb_device *child;
+ bool active = false;
+ int port1;
+
+ if (udev->offload_usage)
+ return true;
+
+ usb_hub_for_each_child(udev, port1, child) {
+ usb_lock_device(child);
+ active = usb_offload_check(child);
+ usb_unlock_device(child);
+
+ if (active)
+ break;
+ }
+
+ return active;
+}
+EXPORT_SYMBOL_GPL(usb_offload_check);
+
+/**
+ * usb_offload_set_pm_locked - set the PM lock state of a USB device
+ * @udev: the USB device to modify
+ * @locked: the new lock state
+ *
+ * Setting @locked to true prevents offload_usage from being modified. This
+ * ensures that offload activities cannot be started or stopped during critical
+ * power management transitions, maintaining a stable state for the duration
+ * of the transition.
+ */
+void usb_offload_set_pm_locked(struct usb_device *udev, bool locked)
+{
+ spin_lock(&udev->offload_lock);
+ udev->offload_pm_locked = locked;
+ spin_unlock(&udev->offload_lock);
+}
+EXPORT_SYMBOL_GPL(usb_offload_set_pm_locked);
diff --git a/drivers/usb/core/phy.c b/drivers/usb/core/phy.c
index faa20054ad5a..4d966cc9cdc9 100644
--- a/drivers/usb/core/phy.c
+++ b/drivers/usb/core/phy.c
@@ -114,7 +114,7 @@ EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc);
struct usb_phy_roothub *usb_phy_roothub_alloc_usb3_phy(struct device *dev)
{
struct usb_phy_roothub *phy_roothub;
- int num_phys;
+ int num_phys, usb2_phy_index;
if (!IS_ENABLED(CONFIG_GENERIC_PHY))
return NULL;
@@ -124,6 +124,16 @@ struct usb_phy_roothub *usb_phy_roothub_alloc_usb3_phy(struct device *dev)
if (num_phys <= 0)
return NULL;
+ /*
+ * If 'usb2-phy' is not present, usb_phy_roothub_alloc() added
+ * all PHYs to the primary HCD's phy_roothub already, so skip
+ * adding 'usb3-phy' here to avoid double use of that.
+ */
+ usb2_phy_index = of_property_match_string(dev->of_node, "phy-names",
+ "usb2-phy");
+ if (usb2_phy_index < 0)
+ return NULL;
+
phy_roothub = devm_kzalloc(dev, sizeof(*phy_roothub), GFP_KERNEL);
if (!phy_roothub)
return ERR_PTR(-ENOMEM);
@@ -200,16 +210,10 @@ int usb_phy_roothub_set_mode(struct usb_phy_roothub *phy_roothub,
list_for_each_entry(roothub_entry, head, list) {
err = phy_set_mode(roothub_entry->phy, mode);
if (err)
- goto err_out;
+ return err;
}
return 0;
-
-err_out:
- list_for_each_entry_continue_reverse(roothub_entry, head, list)
- phy_power_off(roothub_entry->phy);
-
- return err;
}
EXPORT_SYMBOL_GPL(usb_phy_roothub_set_mode);
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index f54198171b6a..b1364f0c384c 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -21,6 +21,20 @@ static int usb_port_block_power_off;
static const struct attribute_group *port_dev_group[];
+static bool usb_port_allow_power_off(struct usb_device *hdev,
+ struct usb_hub *hub,
+ struct usb_port *port_dev)
+{
+ if (hub_is_port_power_switchable(hub))
+ return true;
+
+ if (!IS_ENABLED(CONFIG_ACPI))
+ return false;
+
+ return port_dev->connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED &&
+ usb_acpi_power_manageable(hdev, port_dev->portnum - 1);
+}
+
static ssize_t early_stop_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -141,6 +155,7 @@ static ssize_t disable_store(struct device *dev, struct device_attribute *attr,
usb_disconnect(&port_dev->child);
rc = usb_hub_set_port_power(hdev, hub, port1, !disabled);
+ msleep(2 * hub_power_on_good_delay(hub));
if (disabled) {
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
@@ -739,11 +754,11 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
struct usb_device *hdev = hub->hdev;
int retval;
- port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
+ port_dev = kzalloc_obj(*port_dev);
if (!port_dev)
return -ENOMEM;
- port_dev->req = kzalloc(sizeof(*(port_dev->req)), GFP_KERNEL);
+ port_dev->req = kzalloc_obj(*(port_dev->req));
if (!port_dev->req) {
kfree(port_dev);
return -ENOMEM;
@@ -805,10 +820,10 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
device_enable_async_suspend(&port_dev->dev);
/*
- * Keep hidden the ability to enable port-poweroff if the hub
- * does not support power switching.
+ * Keep hidden the ability to enable port-poweroff if neither the
+ * USB hub nor platform firmware can manage downstream port power.
*/
- if (!hub_is_port_power_switchable(hub))
+ if (!usb_port_allow_power_off(hdev, hub, port_dev))
return 0;
/* Attempt to let userspace take over the policy. */
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 8efbacc5bc34..87810eff974e 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -61,8 +61,7 @@ static int quirks_param_set(const char *value, const struct kernel_param *kp)
quirk_list = NULL;
}
- quirk_list = kcalloc(quirk_count, sizeof(struct quirk_entry),
- GFP_KERNEL);
+ quirk_list = kzalloc_objs(struct quirk_entry, quirk_count);
if (!quirk_list) {
quirk_count = 0;
mutex_unlock(&quirk_mutex);
@@ -141,6 +140,8 @@ static int quirks_param_set(const char *value, const struct kernel_param *kp)
case 'p':
flags |= USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT;
break;
+ case 'q':
+ flags |= USB_QUIRK_FORCE_ONE_CONFIG;
/* Ignore unrecognized flag characters */
}
}
@@ -208,6 +209,10 @@ static const struct usb_device_id usb_quirk_list[] = {
/* HP v222w 16GB Mini USB Drive */
{ USB_DEVICE(0x03f0, 0x3f40), .driver_info = USB_QUIRK_DELAY_INIT },
+ /* Huawei 4G LTE module ME906S */
+ { USB_DEVICE(0x03f0, 0xa31d), .driver_info =
+ USB_QUIRK_DISCONNECT_SUSPEND },
+
/* Creative SB Audigy 2 NX */
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -227,7 +232,8 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x046a, 0x0023), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech HD Webcam C270 */
- { USB_DEVICE(0x046d, 0x0825), .driver_info = USB_QUIRK_RESET_RESUME },
+ { USB_DEVICE(0x046d, 0x0825), .driver_info = USB_QUIRK_RESET_RESUME |
+ USB_QUIRK_NO_LPM},
/* Logitech HD Pro Webcams C920, C920-C, C922, C925e and C930e */
{ USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT },
@@ -369,6 +375,16 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x0781, 0x5583), .driver_info = USB_QUIRK_NO_LPM },
{ USB_DEVICE(0x0781, 0x5591), .driver_info = USB_QUIRK_NO_LPM },
+ /* SanDisk Corp. SanDisk 3.2Gen1 */
+ { USB_DEVICE(0x0781, 0x5596), .driver_info = USB_QUIRK_DELAY_INIT },
+ { USB_DEVICE(0x0781, 0x55a3), .driver_info = USB_QUIRK_DELAY_INIT },
+
+ /* SanDisk Extreme 55AE */
+ { USB_DEVICE(0x0781, 0x55ae), .driver_info = USB_QUIRK_NO_LPM },
+
+ /* Avermedia Live Gamer Ultra 2.1 (GC553G2) - BOS descriptor fetch hangs at SuperSpeed Plus */
+ { USB_DEVICE(0x07ca, 0x2553), .driver_info = USB_QUIRK_NO_BOS },
+
/* Realforce 87U Keyboard */
{ USB_DEVICE(0x0853, 0x011b), .driver_info = USB_QUIRK_NO_LPM },
@@ -383,6 +399,10 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x0904, 0x6103), .driver_info =
USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL },
+ /* Silicon Motion Flash Drive */
+ { USB_DEVICE(0x090c, 0x1000), .driver_info = USB_QUIRK_DELAY_INIT },
+ { USB_DEVICE(0x090c, 0x2000), .driver_info = USB_QUIRK_DELAY_INIT },
+
/* Sound Devices USBPre2 */
{ USB_DEVICE(0x0926, 0x0202), .driver_info =
USB_QUIRK_ENDPOINT_IGNORE },
@@ -426,6 +446,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x0b05, 0x17e0), .driver_info =
USB_QUIRK_IGNORE_REMOTE_WAKEUP },
+ /* ASUS TUF 4K PRO - BOS descriptor fetch hangs at SuperSpeed Plus */
+ { USB_DEVICE(0x0b05, 0x1ab9), .driver_info = USB_QUIRK_NO_BOS },
+
/* Realtek Semiconductor Corp. Mass Storage Device (Multicard Reader)*/
{ USB_DEVICE(0x0bda, 0x0151), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS },
@@ -439,6 +462,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x0c45, 0x7056), .driver_info =
USB_QUIRK_IGNORE_REMOTE_WAKEUP },
+ /* Elgato 4K X - BOS descriptor fetch hangs at SuperSpeed Plus */
+ { USB_DEVICE(0x0fd9, 0x009b), .driver_info = USB_QUIRK_NO_BOS },
+
/* Sony Xperia XZ1 Compact (lilac) smartphone in fastboot mode */
{ USB_DEVICE(0x0fce, 0x0dde), .driver_info = USB_QUIRK_NO_LPM },
@@ -456,6 +482,8 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Huawei 4G LTE module */
{ USB_DEVICE(0x12d1, 0x15bb), .driver_info =
USB_QUIRK_DISCONNECT_SUSPEND },
+ { USB_DEVICE(0x12d1, 0x15c1), .driver_info =
+ USB_QUIRK_DISCONNECT_SUSPEND },
{ USB_DEVICE(0x12d1, 0x15c3), .driver_info =
USB_QUIRK_DISCONNECT_SUSPEND },
@@ -465,6 +493,8 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Razer - Razer Blade Keyboard */
{ USB_DEVICE(0x1532, 0x0116), .driver_info =
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
+ /* Razer - Razer Kiyo Pro Webcam */
+ { USB_DEVICE(0x1532, 0x0e05), .driver_info = USB_QUIRK_NO_LPM },
/* Lenovo ThinkPad OneLink+ Dock twin hub controllers (VIA Labs VL812) */
{ USB_DEVICE(0x17ef, 0x1018), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -483,6 +513,10 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Lenovo ThinkPad USB-C Dock Gen2 Ethernet (RTL8153 GigE) */
{ USB_DEVICE(0x17ef, 0xa387), .driver_info = USB_QUIRK_NO_LPM },
+ /* Lenovo ThinkPad USB-C Dock Gen2 USB 3.1 and USB 2.0 hub controllers */
+ { USB_DEVICE(0x17ef, 0xa391), .driver_info = USB_QUIRK_NO_LPM },
+ { USB_DEVICE(0x17ef, 0xa392), .driver_info = USB_QUIRK_NO_LPM },
+
/* BUILDWIN Photo Frame */
{ USB_DEVICE(0x1908, 0x1315), .driver_info =
USB_QUIRK_HONOR_BNUMINTERFACES },
@@ -539,6 +573,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x2040, 0x7200), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
+ /* VLI disk */
+ { USB_DEVICE(0x2109, 0x0711), .driver_info = USB_QUIRK_NO_LPM },
+
/* Raydium Touchscreen */
{ USB_DEVICE(0x2386, 0x3114), .driver_info = USB_QUIRK_NO_LPM },
@@ -546,6 +583,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x2386, 0x350e), .driver_info = USB_QUIRK_NO_LPM },
+ /* UGREEN 35871 - BOS descriptor fetch hangs at SuperSpeed Plus */
+ { USB_DEVICE(0x2b89, 0x5871), .driver_info = USB_QUIRK_NO_BOS },
+
/* APTIV AUTOMOTIVE HUB */
{ USB_DEVICE(0x2c48, 0x0132), .driver_info =
USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT },
@@ -556,12 +596,18 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Alcor Link AK9563 SC Reader used in 2022 Lenovo ThinkPads */
{ USB_DEVICE(0x2ce3, 0x9563), .driver_info = USB_QUIRK_NO_LPM },
+ /* ezcap401 - BOS descriptor fetch hangs at SuperSpeed Plus */
+ { USB_DEVICE(0x32ed, 0x0401), .driver_info = USB_QUIRK_NO_BOS },
+
/* DELL USB GEN2 */
{ USB_DEVICE(0x413c, 0xb062), .driver_info = USB_QUIRK_NO_LPM | USB_QUIRK_RESET_RESUME },
/* VCOM device */
{ USB_DEVICE(0x4296, 0x7570), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS },
+ /* Noji-MCS SmartCard Reader */
+ { USB_DEVICE(0x5131, 0x2007), .driver_info = USB_QUIRK_FORCE_ONE_CONFIG },
+
/* INTEL VALUE SSD */
{ USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -723,7 +769,7 @@ void usb_detect_quirks(struct usb_device *udev)
udev->quirks ^= usb_detect_dynamic_quirks(udev);
if (udev->quirks)
- dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
+ dev_dbg(&udev->dev, "USB quirks for this device: 0x%x\n",
udev->quirks);
#ifdef CONFIG_USB_DEFAULT_PERSIST
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 23f3cb1989f4..a07866f1060c 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -944,7 +944,7 @@ static umode_t dev_bin_attrs_are_visible(struct kobject *kobj,
}
static const struct attribute_group dev_bin_attr_grp = {
- .bin_attrs_new = dev_bin_attrs,
+ .bin_attrs = dev_bin_attrs,
.is_bin_visible = dev_bin_attrs_are_visible,
};
diff --git a/drivers/usb/core/trace.c b/drivers/usb/core/trace.c
new file mode 100644
index 000000000000..607bcf639d27
--- /dev/null
+++ b/drivers/usb/core/trace.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 Google LLC
+ */
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/drivers/usb/core/trace.h b/drivers/usb/core/trace.h
new file mode 100644
index 000000000000..903e57dc273a
--- /dev/null
+++ b/drivers/usb/core/trace.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2025 Google LLC
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM usbcore
+
+#if !defined(_USB_CORE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _USB_CORE_TRACE_H
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+#include <linux/usb.h>
+
+DECLARE_EVENT_CLASS(usb_core_log_usb_device,
+ TP_PROTO(struct usb_device *udev),
+ TP_ARGS(udev),
+ TP_STRUCT__entry(
+ __string(name, dev_name(&udev->dev))
+ __field(enum usb_device_speed, speed)
+ __field(enum usb_device_state, state)
+ __field(unsigned short, bus_mA)
+ __field(unsigned, authorized)
+ ),
+ TP_fast_assign(
+ __assign_str(name);
+ __entry->speed = udev->speed;
+ __entry->state = udev->state;
+ __entry->bus_mA = udev->bus_mA;
+ __entry->authorized = udev->authorized;
+ ),
+ TP_printk("usb %s speed %s state %s %dmA [%s]",
+ __get_str(name),
+ usb_speed_string(__entry->speed),
+ usb_state_string(__entry->state),
+ __entry->bus_mA,
+ __entry->authorized ? "authorized" : "unauthorized")
+);
+
+DEFINE_EVENT(usb_core_log_usb_device, usb_set_device_state,
+ TP_PROTO(struct usb_device *udev),
+ TP_ARGS(udev)
+);
+
+DEFINE_EVENT(usb_core_log_usb_device, usb_alloc_dev,
+ TP_PROTO(struct usb_device *udev),
+ TP_ARGS(udev)
+);
+
+
+#endif /* _USB_CORE_TRACE_H */
+
+/* this part has to be here */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+#include <trace/define_trace.h>
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 7576920e2d5a..c06b44ca507b 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -72,8 +72,7 @@ struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
{
struct urb *urb;
- urb = kmalloc(struct_size(urb, iso_frame_desc, iso_packets),
- mem_flags);
+ urb = kmalloc_flex(*urb, iso_frame_desc, iso_packets, mem_flags);
if (!urb)
return NULL;
usb_init_urb(urb);
@@ -372,11 +371,12 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
struct usb_host_endpoint *ep;
int is_out;
unsigned int allowed;
+ bool is_eusb2_isoch_double;
if (!urb || !urb->complete)
return -EINVAL;
if (urb->hcpriv) {
- WARN_ONCE(1, "URB %pK submitted while active\n", urb);
+ WARN_ONCE(1, "URB %p submitted while active\n", urb);
return -EBUSY;
}
@@ -434,7 +434,8 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
return -ENODEV;
max = usb_endpoint_maxp(&ep->desc);
- if (max <= 0) {
+ is_eusb2_isoch_double = usb_endpoint_is_hs_isoc_double(dev, ep);
+ if (!max && !is_eusb2_isoch_double) {
dev_dbg(&dev->dev,
"bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
usb_endpoint_num(&ep->desc), is_out ? "out" : "in",
@@ -467,9 +468,13 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
max = le32_to_cpu(isoc_ep_comp->dwBytesPerInterval);
}
- /* "high bandwidth" mode, 1-3 packets/uframe? */
- if (dev->speed == USB_SPEED_HIGH)
- max *= usb_endpoint_maxp_mult(&ep->desc);
+ /* High speed, 1-3 packets/uframe, max 6 for eUSB2 double bw */
+ if (dev->speed == USB_SPEED_HIGH) {
+ if (is_eusb2_isoch_double)
+ max = le32_to_cpu(ep->eusb2_isoc_ep_comp.dwBytesPerInterval);
+ else
+ max *= usb_endpoint_maxp_mult(&ep->desc);
+ }
if (urb->number_of_packets <= 0)
return -EINVAL;
@@ -500,7 +505,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
/* Check that the pipe's type matches the endpoint's type */
if (usb_pipe_type_check(urb->dev, urb->pipe))
- dev_WARN(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
+ dev_warn_once(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
usb_pipetype(urb->pipe), pipetypes[xfertype]);
/* Check against a simple/standard policy */
@@ -597,10 +602,9 @@ EXPORT_SYMBOL_GPL(usb_submit_urb);
* code).
*
* Drivers should not call this routine or related routines, such as
- * usb_kill_urb() or usb_unlink_anchored_urbs(), after their disconnect
- * method has returned. The disconnect function should synchronize with
- * a driver's I/O routines to insure that all URB-related activity has
- * completed before it returns.
+ * usb_kill_urb(), after their disconnect method has returned. The
+ * disconnect function should synchronize with a driver's I/O routines
+ * to insure that all URB-related activity has completed before it returns.
*
* This request is asynchronous, however the HCD might call the ->complete()
* callback during unlink. Therefore when drivers call usb_unlink_urb(), they
@@ -890,28 +894,6 @@ void usb_unpoison_anchored_urbs(struct usb_anchor *anchor)
spin_unlock_irqrestore(&anchor->lock, flags);
}
EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs);
-/**
- * usb_unlink_anchored_urbs - asynchronously cancel transfer requests en masse
- * @anchor: anchor the requests are bound to
- *
- * this allows all outstanding URBs to be unlinked starting
- * from the back of the queue. This function is asynchronous.
- * The unlinking is just triggered. It may happen after this
- * function has returned.
- *
- * This routine should not be called by a driver after its disconnect
- * method has returned.
- */
-void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
-{
- struct urb *victim;
-
- while ((victim = usb_get_from_anchor(anchor)) != NULL) {
- usb_unlink_urb(victim);
- usb_put_urb(victim);
- }
-}
-EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
/**
* usb_anchor_suspend_wakeups
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
index 935c0efea0b6..489dbdc96f94 100644
--- a/drivers/usb/core/usb-acpi.c
+++ b/drivers/usb/core/usb-acpi.c
@@ -157,7 +157,7 @@ EXPORT_SYMBOL_GPL(usb_acpi_set_power_state);
*/
static int usb_acpi_add_usb4_devlink(struct usb_device *udev)
{
- const struct device_link *link;
+ struct device_link *link;
struct usb_port *port_dev;
struct usb_hub *hub;
@@ -165,6 +165,8 @@ static int usb_acpi_add_usb4_devlink(struct usb_device *udev)
return 0;
hub = usb_hub_to_struct_hub(udev->parent);
+ if (!hub)
+ return 0;
port_dev = hub->ports[udev->portnum - 1];
struct fwnode_handle *nhi_fwnode __free(fwnode_handle) =
@@ -186,6 +188,8 @@ static int usb_acpi_add_usb4_devlink(struct usb_device *udev)
dev_dbg(&port_dev->dev, "Created device link from %s to %s\n",
dev_name(&port_dev->child->dev), dev_name(nhi_fwnode->dev));
+ udev->usb4_link = link;
+
return 0;
}
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 0b4685aad2d5..df166cafe106 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -46,6 +46,7 @@
#include <linux/dma-mapping.h>
#include "hub.h"
+#include "trace.h"
const char *usbcore_name = "usbcore";
@@ -647,7 +648,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
struct usb_hcd *usb_hcd = bus_to_hcd(bus);
unsigned raw_port = port1;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc_obj(*dev);
if (!dev)
return NULL;
@@ -670,6 +671,8 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
set_dev_node(&dev->dev, dev_to_node(bus->sysdev));
dev->state = USB_STATE_ATTACHED;
dev->lpm_disable_count = 1;
+ spin_lock_init(&dev->offload_lock);
+ dev->offload_usage = 0;
atomic_set(&dev->urbnum, 0);
INIT_LIST_HEAD(&dev->ep0.urb_list);
@@ -695,15 +698,16 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
device_set_of_node_from_dev(&dev->dev, bus->sysdev);
dev_set_name(&dev->dev, "usb%d", bus->busnum);
} else {
+ int n;
+
/* match any labeling on the hubs; it's one-based */
if (parent->devpath[0] == '0') {
- snprintf(dev->devpath, sizeof dev->devpath,
- "%d", port1);
+ n = snprintf(dev->devpath, sizeof(dev->devpath), "%d", port1);
/* Root ports are not counted in route string */
dev->route = 0;
} else {
- snprintf(dev->devpath, sizeof dev->devpath,
- "%s.%d", parent->devpath, port1);
+ n = snprintf(dev->devpath, sizeof(dev->devpath), "%s.%d",
+ parent->devpath, port1);
/* Route string assumes hubs have less than 16 ports */
if (port1 < 15)
dev->route = parent->route +
@@ -712,6 +716,11 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->route = parent->route +
(15 << ((parent->level - 1)*4));
}
+ if (n >= sizeof(dev->devpath)) {
+ usb_put_hcd(bus_to_hcd(bus));
+ usb_put_dev(dev);
+ return NULL;
+ }
dev->dev.parent = &parent->dev;
dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);
@@ -739,6 +748,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
#endif
dev->authorized = usb_dev_authorized(dev, usb_hcd);
+ trace_usb_alloc_dev(dev);
return dev;
}
EXPORT_SYMBOL_GPL(usb_alloc_dev);
@@ -1024,6 +1034,136 @@ void usb_free_coherent(struct usb_device *dev, size_t size, void *addr,
}
EXPORT_SYMBOL_GPL(usb_free_coherent);
+/**
+ * usb_alloc_noncoherent - allocate dma-noncoherent buffer for URB_NO_xxx_DMA_MAP
+ * @dev: device the buffer will be used with
+ * @size: requested buffer size
+ * @mem_flags: affect whether allocation may block
+ * @dma: used to return DMA address of buffer
+ * @dir: DMA transfer direction
+ * @table: used to return sg_table of allocated memory
+ *
+ * To explicit manage the memory ownership for the kernel vs the device by
+ * USB core, the user needs save sg_table to urb->sgt. Then USB core will
+ * do DMA sync for CPU and device properly.
+ *
+ * When the buffer is no longer used, free it with usb_free_noncoherent().
+ *
+ * Return: Either null (indicating no buffer could be allocated), or the
+ * cpu-space pointer to a buffer that may be used to perform DMA to the
+ * specified device. Such cpu-space buffers are returned along with the DMA
+ * address (through the pointer provided).
+ */
+void *usb_alloc_noncoherent(struct usb_device *dev, size_t size,
+ gfp_t mem_flags, dma_addr_t *dma,
+ enum dma_data_direction dir,
+ struct sg_table **table)
+{
+ struct device *dmadev;
+ struct sg_table *sgt;
+ void *buffer;
+
+ if (!dev || !dev->bus)
+ return NULL;
+
+ dmadev = bus_to_hcd(dev->bus)->self.sysdev;
+
+ sgt = dma_alloc_noncontiguous(dmadev, size, dir, mem_flags, 0);
+ if (!sgt)
+ return NULL;
+
+ buffer = dma_vmap_noncontiguous(dmadev, size, sgt);
+ if (!buffer) {
+ dma_free_noncontiguous(dmadev, size, sgt, dir);
+ return NULL;
+ }
+
+ *table = sgt;
+ *dma = sg_dma_address(sgt->sgl);
+
+ return buffer;
+}
+EXPORT_SYMBOL_GPL(usb_alloc_noncoherent);
+
+/**
+ * usb_free_noncoherent - free memory allocated with usb_alloc_noncoherent()
+ * @dev: device the buffer was used with
+ * @size: requested buffer size
+ * @addr: CPU address of buffer
+ * @dir: DMA transfer direction
+ * @table: describe the allocated and DMA mapped memory,
+ *
+ * This reclaims an I/O buffer, letting it be reused. The memory must have
+ * been allocated using usb_alloc_noncoherent(), and the parameters must match
+ * those provided in that allocation request.
+ */
+void usb_free_noncoherent(struct usb_device *dev, size_t size,
+ void *addr, enum dma_data_direction dir,
+ struct sg_table *table)
+{
+ struct device *dmadev;
+
+ if (!dev || !dev->bus)
+ return;
+ if (!addr)
+ return;
+
+ dmadev = bus_to_hcd(dev->bus)->self.sysdev;
+ dma_vunmap_noncontiguous(dmadev, addr);
+ dma_free_noncontiguous(dmadev, size, table, dir);
+}
+EXPORT_SYMBOL_GPL(usb_free_noncoherent);
+
+/**
+ * usb_endpoint_max_periodic_payload - Get maximum payload bytes per service
+ * interval
+ * @udev: The USB device
+ * @ep: The endpoint
+ *
+ * Returns: the maximum number of bytes isochronous or interrupt endpoint @ep
+ * can transfer during a service interval, or 0 for other endpoints.
+ */
+u32 usb_endpoint_max_periodic_payload(struct usb_device *udev,
+ const struct usb_host_endpoint *ep)
+{
+ if (!usb_endpoint_xfer_isoc(&ep->desc) &&
+ !usb_endpoint_xfer_int(&ep->desc))
+ return 0;
+
+ switch (udev->speed) {
+ case USB_SPEED_SUPER_PLUS:
+ if (USB_SS_SSP_ISOC_COMP(ep->ss_ep_comp.bmAttributes))
+ return le32_to_cpu(ep->ssp_isoc_ep_comp.dwBytesPerInterval);
+ fallthrough;
+ case USB_SPEED_SUPER:
+ return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval);
+ default:
+ if (usb_endpoint_is_hs_isoc_double(udev, ep))
+ return le32_to_cpu(ep->eusb2_isoc_ep_comp.dwBytesPerInterval);
+ return usb_endpoint_maxp(&ep->desc) * usb_endpoint_maxp_mult(&ep->desc);
+ }
+}
+EXPORT_SYMBOL_GPL(usb_endpoint_max_periodic_payload);
+
+/**
+ * usb_endpoint_is_hs_isoc_double - Tell whether an endpoint uses USB 2
+ * Isochronous Double IN Bandwidth
+ * @udev: The USB device
+ * @ep: The endpoint
+ *
+ * Returns: true if an endpoint @ep conforms to USB 2 Isochronous Double IN
+ * Bandwidth ECN, false otherwise.
+ */
+bool usb_endpoint_is_hs_isoc_double(struct usb_device *udev,
+ const struct usb_host_endpoint *ep)
+{
+ return ep->eusb2_isoc_ep_comp.bDescriptorType &&
+ le16_to_cpu(udev->descriptor.bcdUSB) == 0x220 &&
+ usb_endpoint_is_isoc_in(&ep->desc) &&
+ !le16_to_cpu(ep->desc.wMaxPacketSize);
+}
+EXPORT_SYMBOL_GPL(usb_endpoint_is_hs_isoc_double);
+
/*
* Notifications of device and interface registration
*/