diff options
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/config.c | 12 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 4 | ||||
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 31 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 131 | ||||
-rw-r--r-- | drivers/usb/core/hub.h | 1 | ||||
-rw-r--r-- | drivers/usb/core/quirks.c | 8 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/urb.c | 31 | ||||
-rw-r--r-- | drivers/usb/core/usb-acpi.c | 6 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 94 |
11 files changed, 232 insertions, 90 deletions
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 13bd4ec4ea5f..42468bbeffd2 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -107,8 +107,14 @@ 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) { + 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 %d: " "using minimum values\n", @@ -307,7 +313,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", diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 460d4dde5994..c3177034b779 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -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], 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], count, "%04x %04x\n", dynid->id.idVendor, dynid->id.idProduct); return count; } 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 a63c793bac21..03771bbc6c01 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -775,7 +775,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); } @@ -1342,29 +1342,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 +1431,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, diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 0e1dd6ef60a7..256fe8c86828 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -68,6 +68,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. */ @@ -697,7 +703,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); } @@ -1095,6 +1101,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 +1350,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 +1379,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 }; @@ -1386,6 +1412,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) /* Stop hub_wq and related activity */ 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); @@ -1944,6 +1971,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); @@ -2337,6 +2365,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). @@ -4160,7 +4191,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 +4265,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 +4275,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 +4323,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 +4332,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 +4343,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 +4354,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 +4369,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 +4410,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 +4439,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 +4457,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 +4530,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); @@ -5716,6 +5751,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); @@ -5812,8 +5848,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"); @@ -6133,6 +6172,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; @@ -6190,6 +6230,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) { @@ -6221,12 +6273,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/quirks.c b/drivers/usb/core/quirks.c index 36d3df7d040c..ff0ff95d5cca 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -227,7 +227,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 }, @@ -372,6 +373,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* SanDisk Corp. SanDisk 3.2Gen1 */ { USB_DEVICE(0x0781, 0x55a3), .driver_info = USB_QUIRK_DELAY_INIT }, + /* SanDisk Extreme 55AE */ + { USB_DEVICE(0x0781, 0x55ae), .driver_info = USB_QUIRK_NO_LPM }, + /* Realforce 87U Keyboard */ { USB_DEVICE(0x0853, 0x011b), .driver_info = USB_QUIRK_NO_LPM }, @@ -732,7 +736,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/urb.c b/drivers/usb/core/urb.c index 5e52a35486af..7a76d5a62db1 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -500,7 +500,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 +597,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 +889,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..fca7735fc660 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -695,15 +695,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 +713,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); @@ -1024,6 +1030,86 @@ 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); + /* * Notifications of device and interface registration */ |