Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:pzskc383
kernel
linux-2.6.33-usb-storage-suspend.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File linux-2.6.33-usb-storage-suspend.patch of Package kernel
From: Sarah Sharp <sarah.a.sharp@linux.intel.com> Subject: [PATCH] usb: storage: Auto-suspend when media is removed. Patch-mainline: 2.6.35? Oliver Neukum recently made a patch to suspend mass storage devices between SCSI commands. This patch uses some of the work from Oliver's patch. Unfortunately, Oliver's patch made SD readers lose their minds. After a suspend, all previous internal state is lost and they can't tell whether a card had been recently inserted or removed. After a suspend, the first TEST_UNIT_READY command sent to an SD card reader will report "Media may have changed", and the next TEST_UNIT_READY command will report actual media presence or removal. If the SD readers are suspended between TEST_UNIT_READY commands, they will continually report "Media may have changed", and no media change will ever be detected. Disable autosuspend for a mass storage device until we know medium is not present, by peeking into the results of a TEST_UNIT_READY command. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> --- drivers/usb/storage/scsiglue.c | 11 +++++++++- drivers/usb/storage/transport.c | 42 +++++++++++++++++++++++++++++++++++++++ drivers/usb/storage/usb.c | 38 ++++++++++++++++++++++++++++++++-- drivers/usb/storage/usb.h | 7 ++++++ 4 files changed, 94 insertions(+), 4 deletions(-) diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index cfa26d5..fe3e8b6 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -351,14 +351,23 @@ static int command_abort(struct scsi_cmnd *srb) static int device_reset(struct scsi_cmnd *srb) { struct us_data *us = host_to_us(srb->device->host); - int result; + int result, resumption; US_DEBUGP("%s called\n", __func__); + resumption = usb_autopm_get_interface(us->pusb_intf); + /* + * in the error case we go on anyway + * as this is a measure of last resort + */ + /* lock the device pointers and do the reset */ mutex_lock(&(us->dev_mutex)); result = us->transport_reset(us); mutex_unlock(&us->dev_mutex); + if (resumption == 0) + usb_autopm_put_interface(us->pusb_intf); + return result < 0 ? FAILED : SUCCESS; } diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 589f6b4..522fd8c 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -810,6 +810,48 @@ Retry_Sense: srb->sense_buffer[2] = HARDWARE_ERROR; } } + + /* See if new media is present or tracked media is removed, + * so that we can disable auto-suspend or enable auto-suspend. + * + * We conservatively say new media is present when an auto-sense + * after a TEST_UNIT_READY command fails with the "Media may + * have changed" status, because SD card readers are confused + * about the state of media when they first come out of suspend. + * The first TEST_UNIT_READY command will say "Media may have + * changed", and the next TEST_UNIT_READY command will succeed + * to say medium is present or fail and the auto-sense will say + * "No medium present". If we auto-suspend the device between + * the two TEST_UNIT_READY commands, the device will always + * report "Medium may have changed." + * + * Disable auto-suspend until we really know the state of the + * media. + */ + /* Media may have changed, key = 0x6, asc = 0x28, ascq = 0x0 */ + if (!us->media_present && + srb->cmnd[0] == TEST_UNIT_READY && + (srb->sense_buffer[2] & 0xf) == 0x6 && + srb->sense_buffer[12] == 0x28 && + srb->sense_buffer[13] == 0x0) { + us->media_present = 1; + usb_autopm_get_interface_async(us->pusb_intf); + } + /* Media not present, key = 0x2, asc = 0x3a, ascq = 0x0 */ + if (us->media_present && + srb->cmnd[0] == TEST_UNIT_READY && + (srb->sense_buffer[2] & 0xf) == 0x2 && + srb->sense_buffer[12] == 0x3a && + srb->sense_buffer[13] == 0x0) { + us->media_present = 0; + usb_autopm_put_interface_async(us->pusb_intf); + } + } else { + /* TEST_UNIT_READY succeeded, media is now present. */ + if (!us->media_present && srb->cmnd[0] == TEST_UNIT_READY) { + us->media_present = 1; + usb_autopm_get_interface_async(us->pusb_intf); + } } /* Did we transfer less than the minimum amount required? */ diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 8060b85..407352d 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -139,9 +139,6 @@ int usb_stor_suspend(struct usb_interface *iface, pm_message_t message) 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; } @@ -260,6 +257,7 @@ static int usb_stor_control_thread(void * __us) { struct us_data *us = (struct us_data *)__us; struct Scsi_Host *host = us_to_host(us); + int autopm_rc; for(;;) { US_DEBUGP("*** thread sleeping.\n"); @@ -268,6 +266,9 @@ static int usb_stor_control_thread(void * __us) US_DEBUGP("*** thread awakened.\n"); + /* Autoresume the device */ + autopm_rc = usb_autopm_get_interface(us->pusb_intf); + /* lock the device pointers */ mutex_lock(&(us->dev_mutex)); @@ -327,6 +328,12 @@ static int usb_stor_control_thread(void * __us) us->srb->result = SAM_STAT_GOOD; } + /* Did the autoresume fail? */ + else if (autopm_rc < 0) { + US_DEBUGP("Could not wake device\n"); + us->srb->result = DID_ERROR << 16; + } + /* we've got a command, let's do it! */ else { US_DEBUG(usb_stor_show_command(us->srb)); @@ -365,6 +372,10 @@ SkipForAbort: /* unlock the device pointers */ mutex_unlock(&us->dev_mutex); + + /* Start an autosuspend if media was removed. */ + if (autopm_rc == 0) + usb_autopm_put_interface(us->pusb_intf); } /* for (;;) */ /* Wait until we are told to stop */ @@ -741,6 +752,20 @@ static void usb_stor_release_resources(struct us_data *us) /* Free the extra data and the URB */ kfree(us->extra); usb_free_urb(us->current_urb); + + /* Balance calls to usb_autopm_get_interface() and + * usb_autopm_put_interface(). + * + * If we've disabled auto-suspend because medium is present, then + * re-enable it when the driver is being disconnected. + * + * us->media_present is zeroed on allocation. If we never grabbed the + * interface (meaning probe failed early), this variable will be zeroed, + * the device's autopm counter will remain unchanged, and + * auto-suspend will be enabled. + */ + if (us->media_present) + usb_autopm_put_interface(us->pusb_intf); } /* Dissociate from the USB device */ @@ -947,6 +972,12 @@ int usb_stor_probe2(struct us_data *us) goto BadDevice; } + /* + * Prevent the device from suspending until we know + * if it has removable media that is not present. + */ + us->media_present = 1; + usb_autopm_get_interface(us->pusb_intf); wake_up_process(th); return 0; @@ -1020,6 +1051,7 @@ static struct usb_driver usb_storage_driver = { .post_reset = usb_stor_post_reset, .id_table = usb_storage_usb_ids, .soft_unbind = 1, + .supports_autosuspend = 1, }; static int __init usb_stor_init(void) diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 2609efb..e993b02 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -153,6 +153,13 @@ struct us_data { extra_data_destructor extra_destructor;/* extra data destructor */ #ifdef CONFIG_PM pm_hook suspend_resume_hook; + + /* Cleared when a TEST_UNIT_READY command fails and the auto-sense + * indicates there is no media present. This means a device with + * removable media is safe to auto-suspend (until the next + * TEST_UNIT_READY command). + */ + int media_present:1; #endif /* hacks for READ CAPACITY bug handling */ -- 1.6.3.3
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor