Introduce a new utility function to be able to reset a device from interrupt context using a workqueue. If a reset is already pending, it won't schedule another one.
Signed-off-by: Inaky Perez-Gonzalez <[EMAIL PROTECTED]> --- drivers/usb/core/hub.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/usb.h | 2 + 2 files changed, 94 insertions(+) Index: linux.hg/include/linux/usb.h =================================================================== --- linux.hg.orig/include/linux/usb.h 2007-07-18 10:05:50.000000000 -0700 +++ linux.hg/include/linux/usb.h 2007-07-18 10:05:50.000000000 -0700 @@ -374,6 +374,7 @@ unsigned authorized:1; /* Policy has determined we can use it */ unsigned authenticated:1; /* Crypto authentication passed */ unsigned wusb:1; /* Device is Wireless USB */ + unsigned delayed_reset:1; /* Delayed reset in progress */ int string_langid; /* language ID for strings */ /* static strings from the device */ @@ -431,6 +432,7 @@ extern int usb_reset_device(struct usb_device *dev); extern int usb_reset_composite_device(struct usb_device *dev, struct usb_interface *iface); +extern void usb_dev_reset_delayed(struct usb_device *usb_dev); extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id); Index: linux.hg/drivers/usb/core/hub.c =================================================================== --- linux.hg.orig/drivers/usb/core/hub.c 2007-07-18 10:05:50.000000000 -0700 +++ linux.hg/drivers/usb/core/hub.c 2007-07-18 10:05:50.000000000 -0700 @@ -3258,3 +3258,95 @@ return ret; } EXPORT_SYMBOL(usb_reset_composite_device); + + +/* Support for delayed resets */ +struct usb_dev_reset_ctx { + struct work_struct ws; + struct usb_device *usb_dev; +}; + + +/* + * We need to lock the device before resetting, but we may already have the + * lock. So we try to lock the device, if it fails (returns < 0) then we + * cannot proceed. If it returned 1 then we acquired the lock here and need + * to release the lock. If it returned 0 then we already have the lock and + * we leave it to the piece that acquired the lock to release it. + */ +static +void usb_dev_reset_delayed_task(struct work_struct *ws) +{ + struct usb_dev_reset_ctx *reset_ctx = + container_of(ws, struct usb_dev_reset_ctx, ws); + struct usb_device *usb_dev = reset_ctx->usb_dev; + struct device *dev = &usb_dev->dev; + int had_to_lock; + int result = 0; + + if (usb_dev == NULL) { + WARN_ON(1); + goto release; + } + had_to_lock = usb_lock_device_for_reset(usb_dev, NULL); + if (had_to_lock < 0) { + if (had_to_lock != -ENODEV) // ignore dissapearance + dev_err(dev, "Cannot lock device for reset: %d\n", + had_to_lock); + } else { + result = usb_reset_device(usb_dev); + if (result < 0 && result != -ENODEV) + dev_err(dev, "Unable to reset device: %d\n", result); + if (had_to_lock) + usb_unlock_device(usb_dev); + } + usb_dev->delayed_reset = 0; + usb_put_dev(usb_dev); +release: + kfree(reset_ctx); + module_put(THIS_MODULE); +} + + +/** + * Schedule a delayed USB device reset + * + * @usb_dev: USB device that needs to be reset. We assume you have a + * valid reference on it (so it won't dissapear) until we + * take another one that we'll own. + * + * Allocates a context structure containing a workqueue struct with + * all the pertinent info; gets a reference to @usb_dev and schedules + * the call that will be executed later on. + * + * NOTE: for use in atomic contexts + */ +void usb_dev_reset_delayed(struct usb_device *usb_dev) +{ + struct usb_dev_reset_ctx *reset_ctx; + struct device *dev = &usb_dev->dev; + reset_ctx = kmalloc(sizeof(*reset_ctx), GFP_ATOMIC); + if (reset_ctx == NULL) { + if (printk_ratelimit()) + dev_err(dev, "USB: cannot allocate memory for " + "delayed device reset\n"); + return; + } + if (try_module_get(THIS_MODULE) == 0) + goto error_module_get; + usb_get_dev(usb_dev); + if (usb_dev->delayed_reset) + goto error_pending; + usb_dev->delayed_reset = 1; + reset_ctx->usb_dev = usb_dev; + INIT_WORK(&reset_ctx->ws, usb_dev_reset_delayed_task); + schedule_work(&reset_ctx->ws); + return; + +error_pending: + usb_put_dev(usb_dev); +error_module_get: + kfree(reset_ctx); + return; +} +EXPORT_SYMBOL_GPL(usb_dev_reset_delayed); -- Inaky ------------------------------------------------------------------------- This SF.net email is sponsored by DB2 Express Download DB2 Express C - the FREE version of DB2 express and take control of your XML. No limits. Just data. Click to get it now. http://sourceforge.net/powerbar/db2/ _______________________________________________ linux-usb-devel@lists.sourceforge.net To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel