This patch implements autosuspend and autoresume for usb-storage. It is based on the SCSI Dynamic PM patch just posted on linux-scsi and linux-pm. Note: For testing, you will have to set the power/level attribute (and possibly also power/autosuspend) for both the SCSI device and the USB device. Alan Stern Index: usb-2.6/drivers/usb/storage/usb.c =================================================================== --- usb-2.6.orig/drivers/usb/storage/usb.c +++ usb-2.6/drivers/usb/storage/usb.c @@ -184,16 +184,14 @@ static int storage_suspend(struct usb_in { struct us_data *us = usb_get_intfdata(iface); + US_DEBUGP("%s\n", __FUNCTION__); + /* Wait until no command is running */ mutex_lock(&us->dev_mutex); - US_DEBUGP("%s\n", __FUNCTION__); if (us->suspend_resume_hook) (us->suspend_resume_hook)(us, US_SUSPEND); - /* When runtime PM is working, we'll set a flag to indicate - * whether we should autoresume when a SCSI request arrives. */ - mutex_unlock(&us->dev_mutex); return 0; } @@ -202,13 +200,10 @@ static int storage_resume(struct usb_int { struct us_data *us = usb_get_intfdata(iface); - mutex_lock(&us->dev_mutex); - US_DEBUGP("%s\n", __FUNCTION__); if (us->suspend_resume_hook) (us->suspend_resume_hook)(us, US_RESUME); - mutex_unlock(&us->dev_mutex); return 0; } @@ -928,6 +923,7 @@ static int usb_stor_scan_thread(void * _ /* Should we unbind if no devices were detected? */ } + usb_autopm_put_interface(us->pusb_intf); complete_and_exit(&us->scanning_done, 0); } @@ -957,6 +953,9 @@ static int storage_probe(struct usb_inte return -ENOMEM; } + /* Don't autosuspend until the SCSI core tells us */ + usb_autopm_get_interface(intf); + /* * Allow 16-byte CDBs and thus > 2TB */ @@ -1017,6 +1016,7 @@ static int storage_probe(struct usb_inte goto BadDevice; } + usb_autopm_get_interface(intf); /* dropped in the scanning thread */ wake_up_process(th); return 0; @@ -1054,6 +1054,7 @@ static struct usb_driver usb_storage_dri .pre_reset = storage_pre_reset, .post_reset = storage_post_reset, .id_table = storage_usb_ids, + .supports_autosuspend = 1, }; static int __init usb_stor_init(void) Index: usb-2.6/drivers/usb/storage/scsiglue.c =================================================================== --- usb-2.6.orig/drivers/usb/storage/scsiglue.c +++ usb-2.6/drivers/usb/storage/scsiglue.c @@ -295,10 +295,15 @@ static int device_reset(struct scsi_cmnd US_DEBUGP("%s called\n", __FUNCTION__); - /* lock the device pointers and do the reset */ - mutex_lock(&(us->dev_mutex)); - result = us->transport_reset(us); - mutex_unlock(&us->dev_mutex); + result = usb_autopm_get_interface(us->pusb_intf); + if (result == 0) { + + /* lock the device pointers and do the reset */ + mutex_lock(&(us->dev_mutex)); + result = us->transport_reset(us); + mutex_unlock(&us->dev_mutex); + usb_autopm_put_interface(us->pusb_intf); + } return result < 0 ? FAILED : SUCCESS; } @@ -341,6 +346,24 @@ void usb_stor_report_bus_reset(struct us scsi_unlock(host); } +/* The host and its devices are all idle so we can autosuspend */ +static int autosuspend(struct Scsi_Host *host) +{ + struct us_data *us = host_to_us(host); + + usb_autopm_put_interface(us->pusb_intf); + return 0; +} + +/* The host needs to be autoresumed */ +static int autoresume(struct Scsi_Host *host) +{ + struct us_data *us = host_to_us(host); + + return usb_autopm_get_interface(us->pusb_intf); +} + + /*********************************************************************** * /proc/scsi/ functions ***********************************************************************/ @@ -467,6 +490,10 @@ struct scsi_host_template usb_stor_host_ .eh_device_reset_handler = device_reset, .eh_bus_reset_handler = bus_reset, + /* dynamic power management */ + .autosuspend = autosuspend, + .autoresume = autoresume, + /* queue commands only, only one command per LUN */ .can_queue = 1, .cmd_per_lun = 1, - To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html