Commit 85e66060 authored by Alan Stern's avatar Alan Stern Committed by Ben Hutchings

usb-storage: handle a skipped data phase

commit 93c9bf4d 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: default avatarAlan Stern <stern@rowland.harvard.edu>
CC: Matthew Dharm <mdharm-usb@one-eyed-alien.net>
Tested-by: default avatarPaul Osmialowski <newchief@king.net.pl>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
[bwh: Backported to 3.2: use US_DEBUGP() not usb_stor_dbg()]
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent 4fee2090
...@@ -1119,6 +1119,31 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) ...@@ -1119,6 +1119,31 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
*/ */
if (result == USB_STOR_XFER_LONG) if (result == USB_STOR_XFER_LONG)
fake_sense = 1; 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 /* 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) ...@@ -1154,6 +1179,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
if (result != USB_STOR_XFER_GOOD) if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR; return USB_STOR_TRANSPORT_ERROR;
skipped_data_phase:
/* check bulk status */ /* check bulk status */
residue = le32_to_cpu(bcs->Residue); residue = le32_to_cpu(bcs->Residue);
US_DEBUGP("Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n", US_DEBUGP("Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment