[PATCH] fix Sony USB mass storage - pass larger receive buffer

Patrick Mansfield
2003-11-12T23:51:29Z

James - can you please apply? This should not affect other devices since
we are not changing the requested length, just the size of the receive
buffer.

This patch against a recent 2.6 bk fixes problems with Sony USB mass
storage devices: they are sending back 10 bytes even when we ask for 8
bytes (USB babble).

Just use a larger receive buffer for MODE SENSE even though we are
requesting 8 bytes.

Tested by Ronald Kuetemei with his Sony camera, also tested with a USB CF
card reader.

diff -uprN -X /home/patman/dontdiff bl-25/drivers/scsi/scsi_lib.c modesense-bufflen-bk-2.5/drivers/scsi/scsi_lib.c
--- bl-25/drivers/scsi/scsi_lib.c Mon Sep 29 12:21:09 2003
+++ modesense-bufflen-bk-2.5/drivers/scsi/scsi_lib.c Mon Nov 10 15:42:03 2003
@@ -1348,8 +1348,9 @@ void scsi_exit_queue(void)
* @sreq: SCSI request to fill in with the MODE_SENSE
* @dbd: set if mode sense will allow block descriptors to be returned
* @modepage: mode page being requested
- * @buffer: request buffer (may not be smaller than eight bytes)
- * @len: length of request buffer.
+ * @buffer: request buffer
+ * @len: length passed in to scsi command
+ * @buf_len: length of request buffer (may not be smaller than eight)
* @timeout: command timeout
* @retries: number of retries before failing
* @data: returns a structure abstracting the mode header data
@@ -1357,15 +1358,24 @@ void scsi_exit_queue(void)
* Returns zero if unsuccessful, or the header offset (either 4
* or 8 depending on whether a six or ten byte command was
* issued) if successful.
+ *
+ * Note: buf_len is passed in as a separate value because some
+ * devices return more data than requested.
**/
int
__scsi_mode_sense(struct scsi_request *sreq, int dbd, int modepage,
- unsigned char *buffer, int len, int timeout, int retries,
- struct scsi_mode_data *data) {
+ unsigned char *buffer, int len, int buf_len, int timeout,
+ int retries, struct scsi_mode_data *data) {
unsigned char cmd[12];
int use_10_for_ms;
int header_length;

+ WARN_ON((buf_len < 8) || (buf_len < len));
+ if (buf_len > 255)
+ buf_len = 255;
+ if (len > 255)
+ len = 255;
+
memset(data, 0, sizeof(*data));
memset(&cmd[0], 0, 12);
cmd[1] = dbd & 0x18; /* allows DBD and LLBA bits */
@@ -1395,9 +1405,9 @@ __scsi_mode_sense(struct scsi_request *s
sreq->sr_sense_buffer[2] = 0;
sreq->sr_data_direction = DMA_FROM_DEVICE;

- memset(buffer, 0, len);
+ memset(buffer, 0, buf_len);

- scsi_wait_req(sreq, cmd, buffer, len, timeout, retries);
+ scsi_wait_req(sreq, cmd, buffer, buf_len, timeout, retries);

/* This code looks awful: what it's doing is making sure an
* ILLEGAL REQUEST sense return identifies the actual command
@@ -1439,8 +1449,9 @@ __scsi_mode_sense(struct scsi_request *s
* @sdev: scsi device to send command to.
* @dbd: set if mode sense will disable block descriptors in the return
* @modepage: mode page being requested
- * @buffer: request buffer (may not be smaller than eight bytes)
- * @len: length of request buffer.
+ * @buffer: request buffer
+ * @len: length passed in to scsi command
+ * @buf_len: length of request buffer (may not be smaller than eight)
* @timeout: command timeout
* @retries: number of retries before failing
*
@@ -1450,8 +1461,8 @@ __scsi_mode_sense(struct scsi_request *s
**/
int
scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
- unsigned char *buffer, int len, int timeout, int retries,
- struct scsi_mode_data *data)
+ unsigned char *buffer, int len, int buf_len, int timeout,
+ int retries, struct scsi_mode_data *data)
{
struct scsi_request *sreq = scsi_allocate_request(sdev, GFP_KERNEL);
int ret;
@@ -1459,7 +1470,7 @@ scsi_mode_sense(struct scsi_device *sdev
if (!sreq)
return -1;

- ret = __scsi_mode_sense(sreq, dbd, modepage, buffer, len,
+ ret = __scsi_mode_sense(sreq, dbd, modepage, buffer, len, buf_len,
timeout, retries, data);

scsi_release_request(sreq);
diff -uprN -X /home/patman/dontdiff bl-25/drivers/scsi/sd.c modesense-bufflen-bk-2.5/drivers/scsi/sd.c
--- bl-25/drivers/scsi/sd.c Mon Oct 27 14:28:18 2003
+++ modesense-bufflen-bk-2.5/drivers/scsi/sd.c Mon Nov 10 15:38:06 2003
@@ -1073,19 +1073,21 @@ got_data:
/* called with buffer of length 512 */
static inline int
sd_do_mode_sense(struct scsi_request *SRpnt, int dbd, int modepage,
- unsigned char *buffer, int len, struct scsi_mode_data *data)
+ unsigned char *buffer, int len, int buf_len,
+ struct scsi_mode_data *data)
{
- return __scsi_mode_sense(SRpnt, dbd, modepage, buffer, len,
+ return __scsi_mode_sense(SRpnt, dbd, modepage, buffer, len, buf_len,
SD_TIMEOUT, SD_MAX_RETRIES, data);
}

/*
* read write protect setting, if possible - called only in sd_revalidate_disk()
- * called with buffer of length 512
*/
static void
sd_read_write_protect_flag(struct scsi_disk *sdkp, char *diskname,
- struct scsi_request *SRpnt, unsigned char *buffer) {
+ struct scsi_request *SRpnt, unsigned char *buffer,
+ int buf_len)
+{
int res;
struct scsi_mode_data data;

@@ -1099,7 +1101,7 @@ sd_read_write_protect_flag(struct scsi_d
* We have to start carefully: some devices hang if we ask
* for more than is available.
*/
- res = sd_do_mode_sense(SRpnt, 0, 0x3F, buffer, 4, &data);
+ res = sd_do_mode_sense(SRpnt, 0, 0x3F, buffer, 4, buf_len, &data);

/*
* Second attempt: ask for page 0
@@ -1107,13 +1109,14 @@ sd_read_write_protect_flag(struct scsi_d
* Sense Key 5: Illegal Request, Sense Code 24: Invalid field in CDB.
*/
if (!scsi_status_is_good(res))
- res = sd_do_mode_sense(SRpnt, 0, 0, buffer, 4, &data);
+ res = sd_do_mode_sense(SRpnt, 0, 0, buffer, 4, buf_len, &data);

/*
* Third attempt: ask 255 bytes, as we did earlier.
*/
if (!scsi_status_is_good(res))
- res = sd_do_mode_sense(SRpnt, 0, 0x3F, buffer, 255, &data);
+ res = sd_do_mode_sense(SRpnt, 0, 0x3F, buffer, 255, buf_len,
+ &data);

if (!scsi_status_is_good(res)) {
printk(KERN_WARNING
@@ -1129,11 +1132,12 @@ sd_read_write_protect_flag(struct scsi_d

/*
* sd_read_cache_type - called only from sd_revalidate_disk()
- * called with buffer of length 512
*/
static void
sd_read_cache_type(struct scsi_disk *sdkp, char *diskname,
- struct scsi_request *SRpnt, unsigned char *buffer) {
+ struct scsi_request *SRpnt, unsigned char *buffer,
+ int buf_len)
+{
int len = 0, res;

const int dbd = 0; /* DBD */
@@ -1144,7 +1148,7 @@ sd_read_cache_type(struct scsi_disk *sdk
goto defaults;

/* cautiously ask */
- res = sd_do_mode_sense(SRpnt, dbd, modepage, buffer, 4, &data);
+ res = sd_do_mode_sense(SRpnt, dbd, modepage, buffer, 4, buf_len, &data);

if (!scsi_status_is_good(res))
goto bad_sense;
@@ -1165,7 +1169,8 @@ sd_read_cache_type(struct scsi_disk *sdk
len += data.header_length + data.block_descriptor_length;

/* Get the data */
- res = sd_do_mode_sense(SRpnt, dbd, modepage, buffer, len, &data);
+ res = sd_do_mode_sense(SRpnt, dbd, modepage, buffer, len, buf_len,
+ &data);

if (scsi_status_is_good(res)) {
const char *types[] = {
@@ -1260,8 +1265,8 @@ static int sd_revalidate_disk(struct gen
sd_read_capacity(sdkp, disk->disk_name, sreq, buffer);
if (sdp->removable)
sd_read_write_protect_flag(sdkp, disk->disk_name,
- sreq, buffer);
- sd_read_cache_type(sdkp, disk->disk_name, sreq, buffer);
+ sreq, buffer, 512);
+ sd_read_cache_type(sdkp, disk->disk_name, sreq, buffer, 512);
}

set_capacity(disk, sdkp->capacity);
diff -uprN -X /home/patman/dontdiff bl-25/drivers/scsi/sr.c modesense-bufflen-bk-2.5/drivers/scsi/sr.c
--- bl-25/drivers/scsi/sr.c Wed Oct 22 13:30:58 2003
+++ modesense-bufflen-bk-2.5/drivers/scsi/sr.c Mon Nov 10 15:32:16 2003
@@ -751,7 +751,7 @@ static void get_capabilities(struct scsi
SRpnt->sr_sense_buffer[2] == UNIT_ATTENTION)));

/* ask for mode page 0x2a */
- rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, 128,
+ rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, 128, 512,
SR_TIMEOUT, 3, &data);

if (!scsi_status_is_good(rc)) {
diff -uprN -X /home/patman/dontdiff bl-25/include/scsi/scsi_device.h modesense-bufflen-bk-2.5/include/scsi/scsi_device.h
--- bl-25/include/scsi/scsi_device.h Mon Oct 27 14:28:18 2003
+++ modesense-bufflen-bk-2.5/include/scsi/scsi_device.h Mon Nov 10 15:28:43 2003
@@ -123,6 +123,6 @@ extern int scsi_track_queue_full(struct
extern int scsi_set_medium_removal(struct scsi_device *, char);

extern int scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
- unsigned char *buffer, int len, int timeout,
- int retries, struct scsi_mode_data *data);
+ unsigned char *buffer, int len, int buf_len,
+ int timeout, int retries, struct scsi_mode_data *data);
#endif /* _SCSI_SCSI_DEVICE_H */
diff -uprN -X /home/patman/dontdiff bl-25/include/scsi/scsi_request.h modesense-bufflen-bk-2.5/include/scsi/scsi_request.h
--- bl-25/include/scsi/scsi_request.h Mon Sep 22 13:09:11 2003
+++ modesense-bufflen-bk-2.5/include/scsi/scsi_request.h Mon Nov 10 15:26:51 2003
@@ -66,7 +66,7 @@ struct scsi_mode_data {

extern int __scsi_mode_sense(struct scsi_request *SRpnt, int dbd,
int modepage, unsigned char *buffer, int len,
- int timeout, int retries,
+ int buf_len, int timeout, int retries,
struct scsi_mode_data *data);