summaryrefslogtreecommitdiff
path: root/include/linux/usb.h
diff options
context:
space:
mode:
authorInaky Perez-Gonzalez <inaky@linux.intel.com>2008-11-13 10:31:35 -0800
committerGreg Kroah-Hartman <gregkh@suse.de>2009-01-07 09:59:53 -0800
commitdc023dceec861c60bc1d1a17a2c6496ddac26ee7 (patch)
tree908529b3eb975acb2582f679d20a81bfadc95bf4 /include/linux/usb.h
parent9ac39f28b5237a629e41ccfc1f73d3a55723045c (diff)
downloadlwn-dc023dceec861c60bc1d1a17a2c6496ddac26ee7.tar.gz
lwn-dc023dceec861c60bc1d1a17a2c6496ddac26ee7.zip
USB: Introduce usb_queue_reset() to do resets from atomic contexts
This patch introduces a new call to be able to do a USB reset from an atomic contect. This is quite helpful in USB callbacks to handle errors (when the only thing that can be done is to do a device reset). It is done queuing a work struct that will do the actual reset. The struct is "attached" to an interface so pending requests from an interface are removed when said interface is unbound from the driver. The call flow then becomes: usb_queue_reset_device() __usb_queue_reset_device() [workqueue] usb_reset_device() usb_probe_interface() usb_cancel_queue_reset() [error path] usb_unbind_interface() usb_cancel_queue_reset() usb_driver_release_interface() usb_cancel_queue_reset() Note usb_cancel_queue_reset() needs smarts to try not to unqueue when it is actually being executed. This happens when we run the reset from the workqueue: usb_reset_device() is called and on interface unbind time, usb_cancel_queue_reset() would be called. That would deadlock on cancel_work_sync(). To avoid that, we set (before running usb_reset_device()) usb_intf->reset_running and clear it inmediately after returning. Patch is against 2.6.28-rc2 and depends on http://marc.info/?l=linux-usb&m=122581634925308&w=2 (as submitted by Alan Stern). Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com> Cc: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'include/linux/usb.h')
-rw-r--r--include/linux/usb.h8
1 files changed, 8 insertions, 0 deletions
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 {
* to the sysfs representation for that device.
* @pm_usage_cnt: PM usage counter for this interface; autosuspend is not
* allowed unless the counter is 0.
+ * @reset_ws: Used for scheduling resets from atomic context.
+ * @reset_running: set to 1 if the interface is currently running a
+ * queued reset so that usb_cancel_queued_reset() doesn't try to
+ * remove from the workqueue when running inside the worker
+ * thread. See __usb_queue_reset_device().
*
* USB device drivers attach to interfaces on a physical device. Each
* interface encapsulates a single high level function, such as feeding
@@ -168,10 +173,12 @@ struct usb_interface {
unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */
unsigned needs_binding:1; /* needs delayed unbind/rebind */
+ unsigned reset_running:1;
struct device dev; /* interface specific device info */
struct device *usb_dev;
int pm_usage_cnt; /* usage counter for autosuspend */
+ struct work_struct reset_ws; /* for resets in atomic context */
};
#define to_usb_interface(d) container_of(d, struct usb_interface, dev)
#define interface_to_usbdev(intf) \
@@ -507,6 +514,7 @@ extern int usb_lock_device_for_reset(struct usb_device *udev,
/* USB port reset for device reinitialization */
extern int usb_reset_device(struct usb_device *dev);
+extern void usb_queue_reset_device(struct usb_interface *dev);
extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id);