* device reset. */
Handle_Errors:
- /* Let the SCSI layer know we are doing a reset, set the
- * RESETTING bit, and clear the ABORTING bit so that the reset
- * may proceed. */
+ /* Set the RESETTING bit, and clear the ABORTING bit so that
+ * the reset may proceed. */
scsi_lock(us_to_host(us));
- usb_stor_report_bus_reset(us);
set_bit(US_FLIDX_RESETTING, &us->flags);
clear_bit(US_FLIDX_ABORTING, &us->flags);
scsi_unlock(us_to_host(us));
+ /* We must release the device lock because the pre_reset routine
+ * will want to acquire it. */
+ mutex_unlock(&us->dev_mutex);
result = usb_stor_port_reset(us);
+ mutex_lock(&us->dev_mutex);
+
if (result < 0) {
scsi_lock(us_to_host(us));
usb_stor_report_device_reset(us);
0, us->ifnum, NULL, 0);
}
-/* Issue a USB port reset to the device. But don't do anything if
- * there's more than one interface in the device, so that other users
- * are not affected. */
+/* Issue a USB port reset to the device. The caller must not hold
+ * us->dev_mutex.
+ */
int usb_stor_port_reset(struct us_data *us)
{
- int result, rc;
+ int result, rc_lock;
- if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
- result = -EIO;
- US_DEBUGP("No reset during disconnect\n");
- } else if (us->pusb_dev->actconfig->desc.bNumInterfaces != 1) {
- result = -EBUSY;
- US_DEBUGP("Refusing to reset a multi-interface device\n");
- } else {
- result = rc =
- usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
- if (result < 0) {
- US_DEBUGP("unable to lock device for reset: %d\n",
- result);
+ result = rc_lock =
+ usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
+ if (result < 0)
+ US_DEBUGP("unable to lock device for reset: %d\n", result);
+ else {
+ /* Were we disconnected while waiting for the lock? */
+ if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+ result = -EIO;
+ US_DEBUGP("No reset during disconnect\n");
} else {
- result = usb_reset_device(us->pusb_dev);
- if (rc)
- usb_unlock_device(us->pusb_dev);
- US_DEBUGP("usb_reset_device returns %d\n", result);
+ result = usb_reset_composite_device(
+ us->pusb_dev, us->pusb_intf);
+ US_DEBUGP("usb_reset_composite_device returns %d\n",
+ result);
}
+ if (rc_lock)
+ usb_unlock_device(us->pusb_dev);
}
return result;
}
#endif /* CONFIG_PM */
+/*
+ * The next two routines get called just before and just after
+ * a USB port reset, whether from this driver or a different one.
+ */
+
+static void storage_pre_reset(struct usb_interface *iface)
+{
+ struct us_data *us = usb_get_intfdata(iface);
+
+ US_DEBUGP("%s\n", __FUNCTION__);
+
+ /* Make sure no command runs during the reset */
+ mutex_lock(&us->dev_mutex);
+}
+
+static void storage_post_reset(struct usb_interface *iface)
+{
+ struct us_data *us = usb_get_intfdata(iface);
+
+ US_DEBUGP("%s\n", __FUNCTION__);
+
+ /* Report the reset to the SCSI core */
+ scsi_lock(us_to_host(us));
+ usb_stor_report_bus_reset(us);
+ scsi_unlock(us_to_host(us));
+
+ /* FIXME: Notify the subdrivers that they need to reinitialize
+ * the device */
+ mutex_unlock(&us->dev_mutex);
+}
+
/*
* fill_inquiry_response takes an unsigned char array (which must
* be at least 36 characters) and populates the vendor name,
.suspend = storage_suspend,
.resume = storage_resume,
#endif
+ .pre_reset = storage_pre_reset,
+ .post_reset = storage_post_reset,
.id_table = storage_usb_ids,
};