]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/core/message.c
USB: Introduce usb_queue_reset() to do resets from atomic contexts
[linux-2.6-omap-h63xx.git] / drivers / usb / core / message.c
index cc47d36798b1418accd1500b2cb803eea5400871..aadf29f09c45307c0d21ab5917df665e3dadabb1 100644 (file)
@@ -1441,6 +1441,46 @@ static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev,
        return retval;
 }
 
+
+/*
+ * Internal function to queue a device reset
+ *
+ * This is initialized into the workstruct in 'struct
+ * usb_device->reset_ws' that is launched by
+ * message.c:usb_set_configuration() when initializing each 'struct
+ * usb_interface'.
+ *
+ * It is safe to get the USB device without reference counts because
+ * the life cycle of @iface is bound to the life cycle of @udev. Then,
+ * this function will be ran only if @iface is alive (and before
+ * freeing it any scheduled instances of it will have been cancelled).
+ *
+ * We need to set a flag (usb_dev->reset_running) because when we call
+ * the reset, the interfaces might be unbound. The current interface
+ * cannot try to remove the queued work as it would cause a deadlock
+ * (you cannot remove your work from within your executing
+ * workqueue). This flag lets it know, so that
+ * usb_cancel_queued_reset() doesn't try to do it.
+ *
+ * See usb_queue_reset_device() for more details
+ */
+void __usb_queue_reset_device(struct work_struct *ws)
+{
+       int rc;
+       struct usb_interface *iface =
+               container_of(ws, struct usb_interface, reset_ws);
+       struct usb_device *udev = interface_to_usbdev(iface);
+
+       rc = usb_lock_device_for_reset(udev, iface);
+       if (rc >= 0) {
+               iface->reset_running = 1;
+               usb_reset_device(udev);
+               iface->reset_running = 0;
+               usb_unlock_device(udev);
+       }
+}
+
+
 /*
  * usb_set_configuration - Makes a particular device setting be current
  * @dev: the device whose configuration is being updated
@@ -1611,6 +1651,7 @@ free_interfaces:
                intf->dev.type = &usb_if_device_type;
                intf->dev.groups = usb_interface_groups;
                intf->dev.dma_mask = dev->dev.dma_mask;
+               INIT_WORK(&intf->reset_ws, __usb_queue_reset_device);
                device_initialize(&intf->dev);
                mark_quiesced(intf);
                dev_set_name(&intf->dev, "%d-%s:%d.%d",