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

Reply via email to