aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/storage
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2014-10-31 14:49:47 -0400
committerBen Hutchings <ben@decadent.org.uk>2014-12-14 16:23:54 +0000
commit85e66060d38a271c05546231d9f6a800533c6f3f (patch)
tree44f2f24bf9993a6c3201dec3ca1f5f3619a9803d /drivers/usb/storage
parent4fee2090c23113ecc613a1c0d0fce3f69076cf70 (diff)
downloadkernel_samsung_smdk4412-85e66060d38a271c05546231d9f6a800533c6f3f.zip
kernel_samsung_smdk4412-85e66060d38a271c05546231d9f6a800533c6f3f.tar.gz
kernel_samsung_smdk4412-85e66060d38a271c05546231d9f6a800533c6f3f.tar.bz2
usb-storage: handle a skipped data phase
commit 93c9bf4d1838d5851a18ca398b0ad66397f05056 upstream. Sometimes mass-storage devices using the Bulk-only transport will mistakenly skip the data phase of a command. Rather than sending the data expected by the host or sending a zero-length packet, they go directly to the status phase and send the CSW. This causes problems for usb-storage, for obvious reasons. The driver will interpret the CSW as a short data transfer and will wait to receive a CSW. The device won't have anything left to send, so the command eventually times out. The SCSI layer doesn't retry commands after they time out (this is a relatively recent change). Therefore we should do our best to detect a skipped data phase and handle it promptly. This patch adds code to do that. If usb-storage receives a short 13-byte data transfer from the device, and if the first four bytes of the data match the CSW signature, the driver will set the residue to the full transfer length and interpret the data as a CSW. This fixes Bugzilla #86611. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> CC: Matthew Dharm <mdharm-usb@one-eyed-alien.net> Tested-by: Paul Osmialowski <newchief@king.net.pl> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> [bwh: Backported to 3.2: use US_DEBUGP() not usb_stor_dbg()] Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Diffstat (limited to 'drivers/usb/storage')
-rw-r--r--drivers/usb/storage/transport.c26
1 files changed, 26 insertions, 0 deletions
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index 0e5c91c..366395c 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -1119,6 +1119,31 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
*/
if (result == USB_STOR_XFER_LONG)
fake_sense = 1;
+
+ /*
+ * Sometimes a device will mistakenly skip the data phase
+ * and go directly to the status phase without sending a
+ * zero-length packet. If we get a 13-byte response here,
+ * check whether it really is a CSW.
+ */
+ if (result == USB_STOR_XFER_SHORT &&
+ srb->sc_data_direction == DMA_FROM_DEVICE &&
+ transfer_length - scsi_get_resid(srb) ==
+ US_BULK_CS_WRAP_LEN) {
+ struct scatterlist *sg = NULL;
+ unsigned int offset = 0;
+
+ if (usb_stor_access_xfer_buf((unsigned char *) bcs,
+ US_BULK_CS_WRAP_LEN, srb, &sg,
+ &offset, FROM_XFER_BUF) ==
+ US_BULK_CS_WRAP_LEN &&
+ bcs->Signature ==
+ cpu_to_le32(US_BULK_CS_SIGN)) {
+ US_DEBUGP("Device skipped data phase\n");
+ scsi_set_resid(srb, transfer_length);
+ goto skipped_data_phase;
+ }
+ }
}
/* See flow chart on pg 15 of the Bulk Only Transport spec for
@@ -1154,6 +1179,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
+ skipped_data_phase:
/* check bulk status */
residue = le32_to_cpu(bcs->Residue);
US_DEBUGP("Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n",