summaryrefslogtreecommitdiff
path: root/drivers/usb/core/driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core/driver.c')
-rw-r--r--drivers/usb/core/driver.c189
1 files changed, 117 insertions, 72 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 7e7e119c253f..d29edc7c616a 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -95,9 +95,9 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids,
}
}
- spin_lock(&dynids->lock);
+ mutex_lock(&usb_dynids_lock);
list_add_tail(&dynid->node, &dynids->list);
- spin_unlock(&dynids->lock);
+ mutex_unlock(&usb_dynids_lock);
retval = driver_attach(driver);
@@ -116,13 +116,14 @@ ssize_t usb_show_dynids(struct usb_dynids *dynids, char *buf)
struct usb_dynid *dynid;
size_t count = 0;
+ 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;
}
@@ -160,7 +161,7 @@ static ssize_t remove_id_store(struct device_driver *driver, const char *buf,
if (fields < 2)
return -EINVAL;
- spin_lock(&usb_driver->dynids.lock);
+ guard(mutex)(&usb_dynids_lock);
list_for_each_entry_safe(dynid, n, &usb_driver->dynids.list, node) {
struct usb_device_id *id = &dynid->id;
@@ -171,7 +172,6 @@ static ssize_t remove_id_store(struct device_driver *driver, const char *buf,
break;
}
}
- spin_unlock(&usb_driver->dynids.lock);
return count;
}
@@ -189,13 +189,13 @@ static int usb_create_newid_files(struct usb_driver *usb_drv)
goto exit;
if (usb_drv->probe != NULL) {
- error = driver_create_file(&usb_drv->drvwrap.driver,
+ error = driver_create_file(&usb_drv->driver,
&driver_attr_new_id);
if (error == 0) {
- error = driver_create_file(&usb_drv->drvwrap.driver,
+ error = driver_create_file(&usb_drv->driver,
&driver_attr_remove_id);
if (error)
- driver_remove_file(&usb_drv->drvwrap.driver,
+ driver_remove_file(&usb_drv->driver,
&driver_attr_new_id);
}
}
@@ -209,9 +209,9 @@ static void usb_remove_newid_files(struct usb_driver *usb_drv)
return;
if (usb_drv->probe != NULL) {
- driver_remove_file(&usb_drv->drvwrap.driver,
+ driver_remove_file(&usb_drv->driver,
&driver_attr_remove_id);
- driver_remove_file(&usb_drv->drvwrap.driver,
+ driver_remove_file(&usb_drv->driver,
&driver_attr_new_id);
}
}
@@ -220,27 +220,24 @@ static void usb_free_dynids(struct usb_driver *usb_drv)
{
struct usb_dynid *dynid, *n;
- spin_lock(&usb_drv->dynids.lock);
+ guard(mutex)(&usb_dynids_lock);
list_for_each_entry_safe(dynid, n, &usb_drv->dynids.list, node) {
list_del(&dynid->node);
kfree(dynid);
}
- spin_unlock(&usb_drv->dynids.lock);
}
static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *intf,
- struct usb_driver *drv)
+ const struct usb_driver *drv)
{
struct usb_dynid *dynid;
- spin_lock(&drv->dynids.lock);
+ guard(mutex)(&usb_dynids_lock);
list_for_each_entry(dynid, &drv->dynids.list, node) {
if (usb_match_one_id(intf, &dynid->id)) {
- spin_unlock(&drv->dynids.lock);
return &dynid->id;
}
}
- spin_unlock(&drv->dynids.lock);
return NULL;
}
@@ -290,7 +287,10 @@ static int usb_probe_device(struct device *dev)
* specialised device drivers prior to setting the
* use_generic_driver bit.
*/
- error = udriver->probe(udev);
+ if (udriver->probe)
+ error = udriver->probe(udev);
+ else if (!udriver->generic_subclass)
+ error = -EINVAL;
if (error == -ENODEV && udriver != &usb_generic_driver &&
(udriver->id_table || udriver->match)) {
udev->use_generic_driver = 1;
@@ -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;
}
@@ -514,6 +514,19 @@ static int usb_unbind_interface(struct device *dev)
return 0;
}
+static void usb_shutdown_interface(struct device *dev)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usb_driver *driver;
+
+ if (!dev->driver)
+ return;
+
+ driver = to_usb_driver(dev->driver);
+ if (driver->shutdown)
+ driver->shutdown(intf);
+}
+
/**
* usb_driver_claim_interface - bind a driver to an interface
* @driver: the driver to be bound
@@ -549,7 +562,7 @@ int usb_driver_claim_interface(struct usb_driver *driver,
if (!iface->authorized)
return -ENODEV;
- dev->driver = &driver->drvwrap.driver;
+ dev->driver = &driver->driver;
usb_set_intfdata(iface, data);
iface->needs_binding = 0;
@@ -612,7 +625,7 @@ void usb_driver_release_interface(struct usb_driver *driver,
struct device *dev = &iface->dev;
/* this should never happen, don't release something that's not ours */
- if (!dev->driver || dev->driver != &driver->drvwrap.driver)
+ if (!dev->driver || dev->driver != &driver->driver)
return;
/* don't release from within disconnect() */
@@ -837,7 +850,7 @@ const struct usb_device_id *usb_device_match_id(struct usb_device *udev,
EXPORT_SYMBOL_GPL(usb_device_match_id);
bool usb_driver_applicable(struct usb_device *udev,
- struct usb_device_driver *udrv)
+ const struct usb_device_driver *udrv)
{
if (udrv->id_table && udrv->match)
return usb_device_match_id(udev, udrv->id_table) != NULL &&
@@ -852,12 +865,12 @@ bool usb_driver_applicable(struct usb_device *udev,
return false;
}
-static int usb_device_match(struct device *dev, struct device_driver *drv)
+static int usb_device_match(struct device *dev, const struct device_driver *drv)
{
/* devices and interfaces are handled separately */
if (is_usb_device(dev)) {
struct usb_device *udev;
- struct usb_device_driver *udrv;
+ const struct usb_device_driver *udrv;
/* interface drivers never match devices */
if (!is_usb_device_driver(drv))
@@ -877,7 +890,7 @@ static int usb_device_match(struct device *dev, struct device_driver *drv)
} else if (is_usb_interface(dev)) {
struct usb_interface *intf;
- struct usb_driver *usb_drv;
+ const struct usb_driver *usb_drv;
const struct usb_device_id *id;
/* device drivers never match interfaces */
@@ -899,14 +912,14 @@ static int usb_device_match(struct device *dev, struct device_driver *drv)
return 0;
}
-static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int usb_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
- struct usb_device *usb_dev;
+ const struct usb_device *usb_dev;
if (is_usb_device(dev)) {
usb_dev = to_usb_device(dev);
} else if (is_usb_interface(dev)) {
- struct usb_interface *intf = to_usb_interface(dev);
+ const struct usb_interface *intf = to_usb_interface(dev);
usb_dev = interface_to_usbdev(intf);
} else {
@@ -947,7 +960,7 @@ static int __usb_bus_reprobe_drivers(struct device *dev, void *data)
int ret;
/* Don't reprobe if current driver isn't usb_generic_driver */
- if (dev->driver != &usb_generic_driver.drvwrap.driver)
+ if (dev->driver != &usb_generic_driver.driver)
return 0;
udev = to_usb_device(dev);
@@ -961,6 +974,11 @@ static int __usb_bus_reprobe_drivers(struct device *dev, void *data)
return 0;
}
+bool is_usb_device_driver(const struct device_driver *drv)
+{
+ return drv->probe == usb_probe_device;
+}
+
/**
* usb_register_device_driver - register a USB device (not interface) driver
* @new_udriver: USB operations for the device driver
@@ -980,15 +998,14 @@ int usb_register_device_driver(struct usb_device_driver *new_udriver,
if (usb_disabled())
return -ENODEV;
- new_udriver->drvwrap.for_devices = 1;
- new_udriver->drvwrap.driver.name = new_udriver->name;
- new_udriver->drvwrap.driver.bus = &usb_bus_type;
- new_udriver->drvwrap.driver.probe = usb_probe_device;
- new_udriver->drvwrap.driver.remove = usb_unbind_device;
- new_udriver->drvwrap.driver.owner = owner;
- new_udriver->drvwrap.driver.dev_groups = new_udriver->dev_groups;
+ new_udriver->driver.name = new_udriver->name;
+ new_udriver->driver.bus = &usb_bus_type;
+ new_udriver->driver.probe = usb_probe_device;
+ new_udriver->driver.remove = usb_unbind_device;
+ new_udriver->driver.owner = owner;
+ new_udriver->driver.dev_groups = new_udriver->dev_groups;
- retval = driver_register(&new_udriver->drvwrap.driver);
+ retval = driver_register(&new_udriver->driver);
if (!retval) {
pr_info("%s: registered new device driver %s\n",
@@ -1020,7 +1037,7 @@ void usb_deregister_device_driver(struct usb_device_driver *udriver)
pr_info("%s: deregistering device driver %s\n",
usbcore_name, udriver->name);
- driver_unregister(&udriver->drvwrap.driver);
+ driver_unregister(&udriver->driver);
}
EXPORT_SYMBOL_GPL(usb_deregister_device_driver);
@@ -1048,18 +1065,17 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
if (usb_disabled())
return -ENODEV;
- new_driver->drvwrap.for_devices = 0;
- new_driver->drvwrap.driver.name = new_driver->name;
- new_driver->drvwrap.driver.bus = &usb_bus_type;
- new_driver->drvwrap.driver.probe = usb_probe_interface;
- new_driver->drvwrap.driver.remove = usb_unbind_interface;
- new_driver->drvwrap.driver.owner = owner;
- new_driver->drvwrap.driver.mod_name = mod_name;
- new_driver->drvwrap.driver.dev_groups = new_driver->dev_groups;
- spin_lock_init(&new_driver->dynids.lock);
+ new_driver->driver.name = new_driver->name;
+ new_driver->driver.bus = &usb_bus_type;
+ new_driver->driver.probe = usb_probe_interface;
+ new_driver->driver.remove = usb_unbind_interface;
+ new_driver->driver.shutdown = usb_shutdown_interface;
+ new_driver->driver.owner = owner;
+ new_driver->driver.mod_name = mod_name;
+ new_driver->driver.dev_groups = new_driver->dev_groups;
INIT_LIST_HEAD(&new_driver->dynids.list);
- retval = driver_register(&new_driver->drvwrap.driver);
+ retval = driver_register(&new_driver->driver);
if (retval)
goto out;
@@ -1070,15 +1086,14 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
pr_info("%s: registered new interface driver %s\n",
usbcore_name, new_driver->name);
-out:
- return retval;
+ return 0;
out_newid:
- driver_unregister(&new_driver->drvwrap.driver);
-
+ driver_unregister(&new_driver->driver);
+out:
pr_err("%s: error %d registering interface driver %s\n",
usbcore_name, retval, new_driver->name);
- goto out;
+ return retval;
}
EXPORT_SYMBOL_GPL(usb_register_driver);
@@ -1099,7 +1114,7 @@ void usb_deregister(struct usb_driver *driver)
usbcore_name, driver->name);
usb_remove_newid_files(driver);
- driver_unregister(&driver->drvwrap.driver);
+ driver_unregister(&driver->driver);
usb_free_dynids(driver);
}
EXPORT_SYMBOL_GPL(usb_deregister);
@@ -1405,11 +1420,28 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
udev->state == USB_STATE_SUSPENDED)
goto done;
+ if (msg.event == PM_EVENT_SUSPEND && usb_offload_check(udev)) {
+ dev_dbg(&udev->dev, "device offloaded, skip suspend.\n");
+ udev->offload_at_suspend = 1;
+ }
+
/* 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 (udev->offload_at_suspend &&
+ 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 */
@@ -1420,7 +1452,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
}
}
if (status == 0) {
- status = usb_suspend_device(udev, msg);
+ if (!udev->offload_at_suspend)
+ status = usb_suspend_device(udev, msg);
/*
* Ignore errors from non-root-hub devices during
@@ -1465,9 +1498,11 @@ 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 (!udev->offload_at_suspend) {
+ for (i = 0; i < 16; ++i) {
+ usb_hcd_flush_endpoint(udev, udev->ep_out[i]);
+ usb_hcd_flush_endpoint(udev, udev->ep_in[i]);
+ }
}
}
@@ -1509,17 +1544,35 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
udev->can_submit = 1;
/* 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 (!udev->offload_at_suspend)
+ 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 (udev->offload_at_suspend &&
+ intf->needs_remote_wakeup) {
+ dev_dbg(&intf->dev,
+ "device offloaded, skip resume.\n");
+ continue;
+ }
usb_resume_interface(udev, intf, msg,
udev->reset_resume);
}
}
+ udev->offload_at_suspend = 0;
usb_mark_last_busy(udev);
done:
@@ -1704,14 +1757,10 @@ int usb_autoresume_device(struct usb_device *udev)
{
int status;
- status = pm_runtime_get_sync(&udev->dev);
- if (status < 0)
- pm_runtime_put_sync(&udev->dev);
+ status = pm_runtime_resume_and_get(&udev->dev);
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;
}
@@ -1812,14 +1861,10 @@ int usb_autopm_get_interface(struct usb_interface *intf)
{
int status;
- status = pm_runtime_get_sync(&intf->dev);
- if (status < 0)
- pm_runtime_put_sync(&intf->dev);
+ status = pm_runtime_resume_and_get(&intf->dev);
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);
@@ -2025,7 +2070,7 @@ int usb_disable_usb2_hardware_lpm(struct usb_device *udev)
#endif /* CONFIG_PM */
-struct bus_type usb_bus_type = {
+const struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,