diff options
-rw-r--r-- | drivers/usb/core/driver.c | 23 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 43 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 41 | ||||
-rw-r--r-- | include/linux/usb.h | 8 |
4 files changed, 113 insertions, 2 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 23b3c7e79d4b..7e26fb3c2759 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
@@ -184,6 +184,20 @@ static int usb_unbind_device(struct device *dev) | |||
184 | return 0; | 184 | return 0; |
185 | } | 185 | } |
186 | 186 | ||
187 | /* | ||
188 | * Cancel any pending scheduled resets | ||
189 | * | ||
190 | * [see usb_queue_reset_device()] | ||
191 | * | ||
192 | * Called after unconfiguring / when releasing interfaces. See | ||
193 | * comments in __usb_queue_reset_device() regarding | ||
194 | * udev->reset_running. | ||
195 | */ | ||
196 | static void usb_cancel_queued_reset(struct usb_interface *iface) | ||
197 | { | ||
198 | if (iface->reset_running == 0) | ||
199 | cancel_work_sync(&iface->reset_ws); | ||
200 | } | ||
187 | 201 | ||
188 | /* called from driver core with dev locked */ | 202 | /* called from driver core with dev locked */ |
189 | static int usb_probe_interface(struct device *dev) | 203 | static int usb_probe_interface(struct device *dev) |
@@ -242,6 +256,7 @@ static int usb_probe_interface(struct device *dev) | |||
242 | mark_quiesced(intf); | 256 | mark_quiesced(intf); |
243 | intf->needs_remote_wakeup = 0; | 257 | intf->needs_remote_wakeup = 0; |
244 | intf->condition = USB_INTERFACE_UNBOUND; | 258 | intf->condition = USB_INTERFACE_UNBOUND; |
259 | usb_cancel_queued_reset(intf); | ||
245 | } else | 260 | } else |
246 | intf->condition = USB_INTERFACE_BOUND; | 261 | intf->condition = USB_INTERFACE_BOUND; |
247 | 262 | ||
@@ -272,6 +287,7 @@ static int usb_unbind_interface(struct device *dev) | |||
272 | usb_disable_interface(udev, intf); | 287 | usb_disable_interface(udev, intf); |
273 | 288 | ||
274 | driver->disconnect(intf); | 289 | driver->disconnect(intf); |
290 | usb_cancel_queued_reset(intf); | ||
275 | 291 | ||
276 | /* Reset other interface state. | 292 | /* Reset other interface state. |
277 | * We cannot do a Set-Interface if the device is suspended or | 293 | * We cannot do a Set-Interface if the device is suspended or |
@@ -380,8 +396,10 @@ void usb_driver_release_interface(struct usb_driver *driver, | |||
380 | if (device_is_registered(dev)) { | 396 | if (device_is_registered(dev)) { |
381 | iface->condition = USB_INTERFACE_UNBINDING; | 397 | iface->condition = USB_INTERFACE_UNBINDING; |
382 | device_release_driver(dev); | 398 | device_release_driver(dev); |
399 | } else { | ||
400 | iface->condition = USB_INTERFACE_UNBOUND; | ||
401 | usb_cancel_queued_reset(iface); | ||
383 | } | 402 | } |
384 | |||
385 | dev->driver = NULL; | 403 | dev->driver = NULL; |
386 | usb_set_intfdata(iface, NULL); | 404 | usb_set_intfdata(iface, NULL); |
387 | 405 | ||
@@ -942,7 +960,8 @@ static int usb_suspend_interface(struct usb_device *udev, | |||
942 | if (udev->state == USB_STATE_NOTATTACHED || !is_active(intf)) | 960 | if (udev->state == USB_STATE_NOTATTACHED || !is_active(intf)) |
943 | goto done; | 961 | goto done; |
944 | 962 | ||
945 | if (intf->condition == USB_INTERFACE_UNBOUND) /* This can't happen */ | 963 | /* This can happen; see usb_driver_release_interface() */ |
964 | if (intf->condition == USB_INTERFACE_UNBOUND) | ||
946 | goto done; | 965 | goto done; |
947 | driver = to_usb_driver(intf->dev.driver); | 966 | driver = to_usb_driver(intf->dev.driver); |
948 | 967 | ||
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 95fb3104ba4f..e65881899c8f 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -3518,3 +3518,46 @@ int usb_reset_device(struct usb_device *udev) | |||
3518 | return ret; | 3518 | return ret; |
3519 | } | 3519 | } |
3520 | EXPORT_SYMBOL_GPL(usb_reset_device); | 3520 | EXPORT_SYMBOL_GPL(usb_reset_device); |
3521 | |||
3522 | |||
3523 | /** | ||
3524 | * usb_queue_reset_device - Reset a USB device from an atomic context | ||
3525 | * @iface: USB interface belonging to the device to reset | ||
3526 | * | ||
3527 | * This function can be used to reset a USB device from an atomic | ||
3528 | * context, where usb_reset_device() won't work (as it blocks). | ||
3529 | * | ||
3530 | * Doing a reset via this method is functionally equivalent to calling | ||
3531 | * usb_reset_device(), except for the fact that it is delayed to a | ||
3532 | * workqueue. This means that any drivers bound to other interfaces | ||
3533 | * might be unbound, as well as users from usbfs in user space. | ||
3534 | * | ||
3535 | * Corner cases: | ||
3536 | * | ||
3537 | * - Scheduling two resets at the same time from two different drivers | ||
3538 | * attached to two different interfaces of the same device is | ||
3539 | * possible; depending on how the driver attached to each interface | ||
3540 | * handles ->pre_reset(), the second reset might happen or not. | ||
3541 | * | ||
3542 | * - If a driver is unbound and it had a pending reset, the reset will | ||
3543 | * be cancelled. | ||
3544 | * | ||
3545 | * - This function can be called during .probe() or .disconnect() | ||
3546 | * times. On return from .disconnect(), any pending resets will be | ||
3547 | * cancelled. | ||
3548 | * | ||
3549 | * There is no no need to lock/unlock the @reset_ws as schedule_work() | ||
3550 | * does its own. | ||
3551 | * | ||
3552 | * NOTE: We don't do any reference count tracking because it is not | ||
3553 | * needed. The lifecycle of the work_struct is tied to the | ||
3554 | * usb_interface. Before destroying the interface we cancel the | ||
3555 | * work_struct, so the fact that work_struct is queued and or | ||
3556 | * running means the interface (and thus, the device) exist and | ||
3557 | * are referenced. | ||
3558 | */ | ||
3559 | void usb_queue_reset_device(struct usb_interface *iface) | ||
3560 | { | ||
3561 | schedule_work(&iface->reset_ws); | ||
3562 | } | ||
3563 | EXPORT_SYMBOL_GPL(usb_queue_reset_device); | ||
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index cc47d36798b1..aadf29f09c45 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c | |||
@@ -1441,6 +1441,46 @@ static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev, | |||
1441 | return retval; | 1441 | return retval; |
1442 | } | 1442 | } |
1443 | 1443 | ||
1444 | |||
1445 | /* | ||
1446 | * Internal function to queue a device reset | ||
1447 | * | ||
1448 | * This is initialized into the workstruct in 'struct | ||
1449 | * usb_device->reset_ws' that is launched by | ||
1450 | * message.c:usb_set_configuration() when initializing each 'struct | ||
1451 | * usb_interface'. | ||
1452 | * | ||
1453 | * It is safe to get the USB device without reference counts because | ||
1454 | * the life cycle of @iface is bound to the life cycle of @udev. Then, | ||
1455 | * this function will be ran only if @iface is alive (and before | ||
1456 | * freeing it any scheduled instances of it will have been cancelled). | ||
1457 | * | ||
1458 | * We need to set a flag (usb_dev->reset_running) because when we call | ||
1459 | * the reset, the interfaces might be unbound. The current interface | ||
1460 | * cannot try to remove the queued work as it would cause a deadlock | ||
1461 | * (you cannot remove your work from within your executing | ||
1462 | * workqueue). This flag lets it know, so that | ||
1463 | * usb_cancel_queued_reset() doesn't try to do it. | ||
1464 | * | ||
1465 | * See usb_queue_reset_device() for more details | ||
1466 | */ | ||
1467 | void __usb_queue_reset_device(struct work_struct *ws) | ||
1468 | { | ||
1469 | int rc; | ||
1470 | struct usb_interface *iface = | ||
1471 | container_of(ws, struct usb_interface, reset_ws); | ||
1472 | struct usb_device *udev = interface_to_usbdev(iface); | ||
1473 | |||
1474 | rc = usb_lock_device_for_reset(udev, iface); | ||
1475 | if (rc >= 0) { | ||
1476 | iface->reset_running = 1; | ||
1477 | usb_reset_device(udev); | ||
1478 | iface->reset_running = 0; | ||
1479 | usb_unlock_device(udev); | ||
1480 | } | ||
1481 | } | ||
1482 | |||
1483 | |||
1444 | /* | 1484 | /* |
1445 | * usb_set_configuration - Makes a particular device setting be current | 1485 | * usb_set_configuration - Makes a particular device setting be current |
1446 | * @dev: the device whose configuration is being updated | 1486 | * @dev: the device whose configuration is being updated |
@@ -1611,6 +1651,7 @@ free_interfaces: | |||
1611 | intf->dev.type = &usb_if_device_type; | 1651 | intf->dev.type = &usb_if_device_type; |
1612 | intf->dev.groups = usb_interface_groups; | 1652 | intf->dev.groups = usb_interface_groups; |
1613 | intf->dev.dma_mask = dev->dev.dma_mask; | 1653 | intf->dev.dma_mask = dev->dev.dma_mask; |
1654 | INIT_WORK(&intf->reset_ws, __usb_queue_reset_device); | ||
1614 | device_initialize(&intf->dev); | 1655 | device_initialize(&intf->dev); |
1615 | mark_quiesced(intf); | 1656 | mark_quiesced(intf); |
1616 | dev_set_name(&intf->dev, "%d-%s:%d.%d", | 1657 | dev_set_name(&intf->dev, "%d-%s:%d.%d", |
diff --git a/include/linux/usb.h b/include/linux/usb.h index 859a88e6ce9c..c8e55aa979de 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -120,6 +120,11 @@ enum usb_interface_condition { | |||
120 | * to the sysfs representation for that device. | 120 | * to the sysfs representation for that device. |
121 | * @pm_usage_cnt: PM usage counter for this interface; autosuspend is not | 121 | * @pm_usage_cnt: PM usage counter for this interface; autosuspend is not |
122 | * allowed unless the counter is 0. | 122 | * allowed unless the counter is 0. |
123 | * @reset_ws: Used for scheduling resets from atomic context. | ||
124 | * @reset_running: set to 1 if the interface is currently running a | ||
125 | * queued reset so that usb_cancel_queued_reset() doesn't try to | ||
126 | * remove from the workqueue when running inside the worker | ||
127 | * thread. See __usb_queue_reset_device(). | ||
123 | * | 128 | * |
124 | * USB device drivers attach to interfaces on a physical device. Each | 129 | * USB device drivers attach to interfaces on a physical device. Each |
125 | * interface encapsulates a single high level function, such as feeding | 130 | * interface encapsulates a single high level function, such as feeding |
@@ -168,10 +173,12 @@ struct usb_interface { | |||
168 | unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ | 173 | unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ |
169 | unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */ | 174 | unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */ |
170 | unsigned needs_binding:1; /* needs delayed unbind/rebind */ | 175 | unsigned needs_binding:1; /* needs delayed unbind/rebind */ |
176 | unsigned reset_running:1; | ||
171 | 177 | ||
172 | struct device dev; /* interface specific device info */ | 178 | struct device dev; /* interface specific device info */ |
173 | struct device *usb_dev; | 179 | struct device *usb_dev; |
174 | int pm_usage_cnt; /* usage counter for autosuspend */ | 180 | int pm_usage_cnt; /* usage counter for autosuspend */ |
181 | struct work_struct reset_ws; /* for resets in atomic context */ | ||
175 | }; | 182 | }; |
176 | #define to_usb_interface(d) container_of(d, struct usb_interface, dev) | 183 | #define to_usb_interface(d) container_of(d, struct usb_interface, dev) |
177 | #define interface_to_usbdev(intf) \ | 184 | #define interface_to_usbdev(intf) \ |
@@ -507,6 +514,7 @@ extern int usb_lock_device_for_reset(struct usb_device *udev, | |||
507 | 514 | ||
508 | /* USB port reset for device reinitialization */ | 515 | /* USB port reset for device reinitialization */ |
509 | extern int usb_reset_device(struct usb_device *dev); | 516 | extern int usb_reset_device(struct usb_device *dev); |
517 | extern void usb_queue_reset_device(struct usb_interface *dev); | ||
510 | 518 | ||
511 | extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id); | 519 | extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id); |
512 | 520 | ||