This patch (as1043) implements autosuspend for usb-storage. It relies on an underlying SCSI dynamic PM implementation for generating autosuspend and autoresume requests. Signed-off-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> --- I'm submitting this through the SCSI tree rather than the USB tree, because it depends so heavily on the previous SCSI dynamic PM patch. 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,22 @@ static int storage_suspend(struct usb_in { struct us_data *us = usb_get_intfdata(iface); + US_DEBUGP("%s\n", __FUNCTION__); + +#ifdef CONFIG_SCSI_DYNAMIC_PM + /* Don't suspend if the host is still active */ + if (iface->pm_usage_cnt > 0) { + US_DEBUGP("host is not suspended!\n"); + return -EBUSY; + } +#endif + /* 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 +208,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; } @@ -957,6 +960,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 */ @@ -1054,6 +1060,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 @@ -299,10 +299,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; } @@ -345,6 +350,32 @@ void usb_stor_report_bus_reset(struct us scsi_unlock(host); } +#ifdef CONFIG_SCSI_DYNAMIC_PM + +/* The host and its devices are all idle so we can autosuspend */ +static int us_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 us_autoresume(struct Scsi_Host *host) +{ + struct us_data *us = host_to_us(host); + + return usb_autopm_get_interface(us->pusb_intf); +} + +#else +#define us_autosuspend NULL +#define us_autoresume NULL + +#endif /* CONFIG_SCSI_DYNAMIC_PM */ + + /*********************************************************************** * /proc/scsi/ functions ***********************************************************************/ @@ -471,6 +502,10 @@ struct scsi_host_template usb_stor_host_ .eh_device_reset_handler = device_reset, .eh_bus_reset_handler = bus_reset, + /* dynamic power management */ + .autosuspend = us_autosuspend, + .autoresume = us_autoresume, + /* queue commands only, only one command per LUN */ .can_queue = 1, .cmd_per_lun = 1, _______________________________________________ linux-pm mailing list linux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linux-foundation.org/mailman/listinfo/linux-pm