Commit 5c8dfe47 authored by James Bottomley's avatar James Bottomley

SCSI: abstract mode_sense 6 and 10 out completely

Move the mode_sense request routines to a central location and make
all block device consumers use it.  Also abstract the header as
part of the return to hide the 6/10 differences.
parent 1d8c1ffb
...@@ -1336,3 +1336,127 @@ void scsi_exit_queue(void) ...@@ -1336,3 +1336,127 @@ void scsi_exit_queue(void)
kmem_cache_destroy(sgp->slab); kmem_cache_destroy(sgp->slab);
} }
} }
/**
* __scsi_mode_sense - issue a mode sense, falling back from 10 to
* six bytes if necessary.
* @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.
* @timeout: command timeout
* @retries: number of retries before failing
* @data: returns a structure abstracting the mode header data
*
* 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.
**/
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 cmd[12];
int use_10_for_ms;
int header_length;
memset(data, 0, sizeof(*data));
memset(&cmd[0], 0, 12);
cmd[1] = dbd & 0x18; /* allows DBD and LLBA bits */
cmd[2] = modepage;
retry:
use_10_for_ms = sreq->sr_device->use_10_for_ms;
if (use_10_for_ms) {
if (len < 8)
len = 8;
cmd[0] = MODE_SENSE_10;
cmd[8] = len;
header_length = 8;
} else {
if (len < 4)
len = 4;
cmd[0] = MODE_SENSE;
cmd[4] = len;
header_length = 4;
}
sreq->sr_cmd_len = 0;
sreq->sr_sense_buffer[0] = 0;
sreq->sr_sense_buffer[2] = 0;
sreq->sr_data_direction = SCSI_DATA_READ;
memset(buffer, 0, len);
scsi_wait_req(sreq, cmd, buffer, len, timeout, retries);
/* This code looks awful: what it's doing is making sure an
* ILLEGAL REQUEST sense return identifies the actual command
* byte as the problem. MODE_SENSE commands can return
* ILLEGAL REQUEST if the code page isn't supported */
if (use_10_for_ms && ! scsi_status_is_good(sreq->sr_result) &&
(driver_byte(sreq->sr_result) & DRIVER_SENSE) &&
sreq->sr_sense_buffer[2] == ILLEGAL_REQUEST &&
(sreq->sr_sense_buffer[4] & 0x40) == 0x40 &&
sreq->sr_sense_buffer[5] == 0 &&
sreq->sr_sense_buffer[6] == 0 ) {
sreq->sr_device->use_10_for_ms = 0;
goto retry;
}
if(scsi_status_is_good(sreq->sr_result)) {
data->header_length = header_length;
if(use_10_for_ms) {
data->length = buffer[0]*256 + buffer[1];
data->medium_type = buffer[2];
data->device_specific = buffer[3];
data->longlba = buffer[4] & 0x01;
data->block_descriptor_length = buffer[6]*256
+ buffer[7];
} else {
data->length = buffer[0];
data->medium_type = buffer[1];
data->device_specific = buffer[3];
data->block_descriptor_length = buffer[4];
}
}
return sreq->sr_result;
}
/**
* scsi_mode_sense - issue a mode sense, falling back from 10 to
* six bytes if necessary.
* @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.
* @timeout: command timeout
* @retries: number of retries before failing
*
* 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.
**/
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)
{
struct scsi_request *sreq = scsi_allocate_request(sdev);
int ret;
if (!sreq)
return -1;
ret = __scsi_mode_sense(sreq, dbd, modepage, buffer, len,
timeout, retries, data);
scsi_release_request(sreq);
return ret;
}
...@@ -86,6 +86,9 @@ EXPORT_SYMBOL(scsi_add_device); ...@@ -86,6 +86,9 @@ EXPORT_SYMBOL(scsi_add_device);
EXPORT_SYMBOL(scsi_remove_device); EXPORT_SYMBOL(scsi_remove_device);
EXPORT_SYMBOL(scsi_set_device_offline); EXPORT_SYMBOL(scsi_set_device_offline);
EXPORT_SYMBOL(__scsi_mode_sense);
EXPORT_SYMBOL(scsi_mode_sense);
/* /*
* This symbol is for the highlevel drivers (e.g. sg) only. * This symbol is for the highlevel drivers (e.g. sg) only.
*/ */
......
...@@ -1062,40 +1062,12 @@ sd_read_capacity(struct scsi_disk *sdkp, char *diskname, ...@@ -1062,40 +1062,12 @@ sd_read_capacity(struct scsi_disk *sdkp, char *diskname,
} }
/* called with buffer of length 512 */ /* called with buffer of length 512 */
static int static inline int
sd_do_mode_sense(struct scsi_request *SRpnt, int dbd, int modepage, sd_do_mode_sense(struct scsi_request *SRpnt, int dbd, int modepage,
unsigned char *buffer, int len) { unsigned char *buffer, int len, struct scsi_mode_data *data)
unsigned char cmd[12]; {
return __scsi_mode_sense(SRpnt, dbd, modepage, buffer, len,
memset((void *) &cmd[0], 0, 12); SD_TIMEOUT, SD_MAX_RETRIES, data);
cmd[1] = dbd;
cmd[2] = modepage;
if (SRpnt->sr_device->use_10_for_ms) {
if (len < 8)
len = 8;
cmd[0] = MODE_SENSE_10;
cmd[8] = len;
} else {
if (len < 4)
len = 4;
cmd[0] = MODE_SENSE;
cmd[4] = len;
}
SRpnt->sr_cmd_len = 0;
SRpnt->sr_sense_buffer[0] = 0;
SRpnt->sr_sense_buffer[2] = 0;
SRpnt->sr_data_direction = SCSI_DATA_READ;
memset((void *) buffer, 0, len);
scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
len, SD_TIMEOUT, SD_MAX_RETRIES);
return SRpnt->sr_result;
} }
/* /*
...@@ -1106,33 +1078,34 @@ static void ...@@ -1106,33 +1078,34 @@ static void
sd_read_write_protect_flag(struct scsi_disk *sdkp, char *diskname, 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 res; int res;
struct scsi_mode_data data;
/* /*
* First attempt: ask for all pages (0x3F), but only 4 bytes. * First attempt: ask for all pages (0x3F), but only 4 bytes.
* We have to start carefully: some devices hang if we ask * We have to start carefully: some devices hang if we ask
* for more than is available. * for more than is available.
*/ */
res = sd_do_mode_sense(SRpnt, 0, 0x3F, buffer, 4); res = sd_do_mode_sense(SRpnt, 0, 0x3F, buffer, 4, &data);
/* /*
* Second attempt: ask for page 0 * Second attempt: ask for page 0
* When only page 0 is implemented, a request for page 3F may return * When only page 0 is implemented, a request for page 3F may return
* Sense Key 5: Illegal Request, Sense Code 24: Invalid field in CDB. * Sense Key 5: Illegal Request, Sense Code 24: Invalid field in CDB.
*/ */
if (res) if (!scsi_status_is_good(res))
res = sd_do_mode_sense(SRpnt, 0, 0, buffer, 4); res = sd_do_mode_sense(SRpnt, 0, 0, buffer, 4, &data);
/* /*
* Third attempt: ask 255 bytes, as we did earlier. * Third attempt: ask 255 bytes, as we did earlier.
*/ */
if (res) if (!scsi_status_is_good(res))
res = sd_do_mode_sense(SRpnt, 0, 0x3F, buffer, 255); res = sd_do_mode_sense(SRpnt, 0, 0x3F, buffer, 255, &data);
if (res) { if (!scsi_status_is_good(res)) {
printk(KERN_WARNING printk(KERN_WARNING
"%s: test WP failed, assume Write Enabled\n", diskname); "%s: test WP failed, assume Write Enabled\n", diskname);
} else { } else {
sdkp->write_prot = ((buffer[2] & 0x80) != 0); sdkp->write_prot = ((data.device_specific & 0x80) != 0);
printk(KERN_NOTICE "%s: Write Protect is %s\n", diskname, printk(KERN_NOTICE "%s: Write Protect is %s\n", diskname,
sdkp->write_prot ? "on" : "off"); sdkp->write_prot ? "on" : "off");
printk(KERN_DEBUG "%s: Mode Sense: %02x %02x %02x %02x\n", printk(KERN_DEBUG "%s: Mode Sense: %02x %02x %02x %02x\n",
...@@ -1149,43 +1122,45 @@ sd_read_cache_type(struct scsi_disk *sdkp, char *diskname, ...@@ -1149,43 +1122,45 @@ 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 len = 0, res; int len = 0, res;
const int dbd = 0x08; /* DBD */ const int dbd = 0; /* DBD */
const int modepage = 0x08; /* current values, cache page */ const int modepage = 0x08; /* current values, cache page */
struct scsi_mode_data data;
/* cautiously ask */ /* cautiously ask */
res = sd_do_mode_sense(SRpnt, dbd, modepage, buffer, 4); res = sd_do_mode_sense(SRpnt, dbd, modepage, buffer, 4, &data);
if (res == 0) { if (scsi_status_is_good(res)) {
/* that went OK, now ask for the proper length */ /* that went OK, now ask for the proper length */
len = buffer[0] + 1; len = data.length;
if (len > 128) if (len > 128)
len = 128; len = 128;
res = sd_do_mode_sense(SRpnt, dbd, modepage, buffer, len); res = sd_do_mode_sense(SRpnt, dbd, modepage, buffer,
len, &data);
} }
if (res == 0 && buffer[3] + 6 < len) { if (scsi_status_is_good(res)) {
const char *types[] = { const char *types[] = {
"write through", "none", "write back", "write through", "none", "write back",
"write back, no read (daft)" "write back, no read (daft)"
}; };
int ct = 0; int ct = 0;
int offset = buffer[3] + 4; /* start of mode page */ int offset = data.header_length +
data.block_descriptor_length + 2;
sdkp->WCE = ((buffer[offset + 2] & 0x04) != 0); sdkp->WCE = ((buffer[offset] & 0x04) != 0);
sdkp->RCD = ((buffer[offset + 2] & 0x01) != 0); sdkp->RCD = ((buffer[offset] & 0x01) != 0);
ct = sdkp->RCD + 2*sdkp->WCE; ct = sdkp->RCD + 2*sdkp->WCE;
printk(KERN_NOTICE "SCSI device %s: drive cache: %s\n", printk(KERN_NOTICE "SCSI device %s: drive cache: %s\n",
diskname, types[ct]); diskname, types[ct]);
} else { } else {
if (res == 0 || if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70
(status_byte(res) == CHECK_CONDITION
&& (SRpnt->sr_sense_buffer[0] & 0x70) == 0x70
&& (SRpnt->sr_sense_buffer[2] & 0x0f) == ILLEGAL_REQUEST && (SRpnt->sr_sense_buffer[2] & 0x0f) == ILLEGAL_REQUEST
/* ASC 0x24 ASCQ 0x00: Invalid field in CDB */ /* ASC 0x24 ASCQ 0x00: Invalid field in CDB */
&& SRpnt->sr_sense_buffer[12] == 0x24 && SRpnt->sr_sense_buffer[12] == 0x24
&& SRpnt->sr_sense_buffer[13] == 0x00)) { && SRpnt->sr_sense_buffer[13] == 0x00) {
printk(KERN_NOTICE "%s: cache data unavailable\n", printk(KERN_NOTICE "%s: cache data unavailable\n",
diskname); diskname);
} else { } else {
......
...@@ -660,9 +660,9 @@ static void get_sectorsize(struct scsi_cd *cd) ...@@ -660,9 +660,9 @@ static void get_sectorsize(struct scsi_cd *cd)
static void get_capabilities(struct scsi_cd *cd) static void get_capabilities(struct scsi_cd *cd)
{ {
struct cdrom_generic_command cgc;
unsigned char *buffer; unsigned char *buffer;
int rc, n; int rc, n;
struct scsi_mode_data data;
static char *loadmech[] = static char *loadmech[] =
{ {
...@@ -681,18 +681,10 @@ static void get_capabilities(struct scsi_cd *cd) ...@@ -681,18 +681,10 @@ static void get_capabilities(struct scsi_cd *cd)
printk(KERN_ERR "sr: out of memory.\n"); printk(KERN_ERR "sr: out of memory.\n");
return; return;
} }
memset(&cgc, 0, sizeof(struct cdrom_generic_command)); rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, 128,
cgc.cmd[0] = MODE_SENSE; SR_TIMEOUT, 3, &data);
cgc.cmd[2] = 0x2a;
cgc.cmd[4] = 128; if (!scsi_status_is_good(rc)) {
cgc.buffer = buffer;
cgc.buflen = 128;
cgc.quiet = 1;
cgc.data_direction = SCSI_DATA_READ;
cgc.timeout = SR_TIMEOUT;
rc = sr_do_ioctl(cd, &cgc);
if (rc) {
/* failed, drive doesn't have capabilities mode page */ /* failed, drive doesn't have capabilities mode page */
cd->cdi.speed = 1; cd->cdi.speed = 1;
cd->cdi.mask |= (CDC_CD_R | CDC_CD_RW | CDC_DVD_R | cd->cdi.mask |= (CDC_CD_R | CDC_CD_RW | CDC_DVD_R |
...@@ -702,7 +694,7 @@ static void get_capabilities(struct scsi_cd *cd) ...@@ -702,7 +694,7 @@ static void get_capabilities(struct scsi_cd *cd)
printk("%s: scsi-1 drive\n", cd->cdi.name); printk("%s: scsi-1 drive\n", cd->cdi.name);
return; return;
} }
n = buffer[3] + 4; n = data.header_length + data.block_descriptor_length;
cd->cdi.speed = ((buffer[n + 8] << 8) + buffer[n + 9]) / 176; cd->cdi.speed = ((buffer[n + 8] << 8) + buffer[n + 9]) / 176;
cd->readcd_known = 1; cd->readcd_known = 1;
cd->readcd_cdda = buffer[n + 5] & 0x01; cd->readcd_cdda = buffer[n + 5] & 0x01;
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
struct request_queue; struct request_queue;
struct scsi_cmnd; struct scsi_cmnd;
struct scsi_mode_data;
struct scsi_device { struct scsi_device {
...@@ -103,4 +104,8 @@ extern int scsi_track_queue_full(struct scsi_device *, int); ...@@ -103,4 +104,8 @@ extern int scsi_track_queue_full(struct scsi_device *, int);
extern int scsi_set_medium_removal(struct scsi_device *, char); 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);
#endif /* _SCSI_SCSI_DEVICE_H */ #endif /* _SCSI_SCSI_DEVICE_H */
...@@ -55,4 +55,19 @@ extern void scsi_do_req(struct scsi_request *, const void *cmnd, ...@@ -55,4 +55,19 @@ extern void scsi_do_req(struct scsi_request *, const void *cmnd,
void (*done) (struct scsi_cmnd *), void (*done) (struct scsi_cmnd *),
int timeout, int retries); int timeout, int retries);
struct scsi_mode_data {
__u16 length;
__u16 block_descriptor_length;
__u8 medium_type;
__u8 device_specific;
__u8 header_length;
__u8 longlba:1;
};
extern int __scsi_mode_sense(struct scsi_request *SRpnt, int dbd,
int modepage, unsigned char *buffer, int len,
int timeout, int retries,
struct scsi_mode_data *data);
#endif /* _SCSI_SCSI_REQUEST_H */ #endif /* _SCSI_SCSI_REQUEST_H */
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