diff options
| author | James Bottomley <jejb@raven.il.steeleye.com> | 2003-06-25 04:36:58 -0500 |
|---|---|---|
| committer | James Bottomley <jejb@raven.il.steeleye.com> | 2003-06-25 04:36:58 -0500 |
| commit | 5c8dfe47db6bcc3473576860645547af072c176e (patch) | |
| tree | 0e5a22bfca9c44fe4f3a37e541dd4592742c7a29 | |
| parent | 1d8c1ffb63346237de365aff8f01c2bfc699f61a (diff) | |
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.
| -rw-r--r-- | drivers/scsi/scsi_lib.c | 124 | ||||
| -rw-r--r-- | drivers/scsi/scsi_syms.c | 3 | ||||
| -rw-r--r-- | drivers/scsi/sd.c | 81 | ||||
| -rw-r--r-- | drivers/scsi/sr.c | 20 | ||||
| -rw-r--r-- | include/scsi/scsi_device.h | 5 | ||||
| -rw-r--r-- | include/scsi/scsi_request.h | 15 |
6 files changed, 181 insertions, 67 deletions
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 6d17684726e1..4606fa0a4c3c 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1336,3 +1336,127 @@ void scsi_exit_queue(void) 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; +} diff --git a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c index 77cfb5ad322e..7c3a7ecd7a69 100644 --- a/drivers/scsi/scsi_syms.c +++ b/drivers/scsi/scsi_syms.c @@ -86,6 +86,9 @@ EXPORT_SYMBOL(scsi_add_device); EXPORT_SYMBOL(scsi_remove_device); 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. */ diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 60e26ebd3742..797f2c5a09d7 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1062,40 +1062,12 @@ got_data: } /* called with buffer of length 512 */ -static int +static inline int sd_do_mode_sense(struct scsi_request *SRpnt, int dbd, int modepage, - unsigned char *buffer, int len) { - unsigned char cmd[12]; - - memset((void *) &cmd[0], 0, 12); - 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; + unsigned char *buffer, int len, struct scsi_mode_data *data) +{ + return __scsi_mode_sense(SRpnt, dbd, modepage, buffer, len, + SD_TIMEOUT, SD_MAX_RETRIES, data); } /* @@ -1106,33 +1078,34 @@ static void sd_read_write_protect_flag(struct scsi_disk *sdkp, char *diskname, struct scsi_request *SRpnt, unsigned char *buffer) { int res; + struct scsi_mode_data data; /* * First attempt: ask for all pages (0x3F), but only 4 bytes. * 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); + res = sd_do_mode_sense(SRpnt, 0, 0x3F, buffer, 4, &data); /* * Second attempt: ask for page 0 * 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. */ - if (res) - res = sd_do_mode_sense(SRpnt, 0, 0, buffer, 4); + if (!scsi_status_is_good(res)) + res = sd_do_mode_sense(SRpnt, 0, 0, buffer, 4, &data); /* * Third attempt: ask 255 bytes, as we did earlier. */ - if (res) - res = sd_do_mode_sense(SRpnt, 0, 0x3F, buffer, 255); + if (!scsi_status_is_good(res)) + res = sd_do_mode_sense(SRpnt, 0, 0x3F, buffer, 255, &data); - if (res) { + if (!scsi_status_is_good(res)) { printk(KERN_WARNING "%s: test WP failed, assume Write Enabled\n", diskname); } 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, sdkp->write_prot ? "on" : "off"); 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, struct scsi_request *SRpnt, unsigned char *buffer) { int len = 0, res; - const int dbd = 0x08; /* DBD */ + const int dbd = 0; /* DBD */ const int modepage = 0x08; /* current values, cache page */ + struct scsi_mode_data data; + /* 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 */ - len = buffer[0] + 1; + len = data.length; if (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[] = { "write through", "none", "write back", "write back, no read (daft)" }; 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->RCD = ((buffer[offset + 2] & 0x01) != 0); + sdkp->WCE = ((buffer[offset] & 0x04) != 0); + sdkp->RCD = ((buffer[offset] & 0x01) != 0); ct = sdkp->RCD + 2*sdkp->WCE; printk(KERN_NOTICE "SCSI device %s: drive cache: %s\n", diskname, types[ct]); } else { - if (res == 0 || - (status_byte(res) == CHECK_CONDITION - && (SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 + if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 && (SRpnt->sr_sense_buffer[2] & 0x0f) == ILLEGAL_REQUEST /* ASC 0x24 ASCQ 0x00: Invalid field in CDB */ && 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", diskname); } else { diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index df7e1b4964c2..f031db4abee8 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -660,9 +660,9 @@ Enomem: static void get_capabilities(struct scsi_cd *cd) { - struct cdrom_generic_command cgc; unsigned char *buffer; int rc, n; + struct scsi_mode_data data; static char *loadmech[] = { @@ -681,18 +681,10 @@ static void get_capabilities(struct scsi_cd *cd) printk(KERN_ERR "sr: out of memory.\n"); return; } - memset(&cgc, 0, sizeof(struct cdrom_generic_command)); - cgc.cmd[0] = MODE_SENSE; - cgc.cmd[2] = 0x2a; - cgc.cmd[4] = 128; - 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) { + rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, 128, + SR_TIMEOUT, 3, &data); + + if (!scsi_status_is_good(rc)) { /* failed, drive doesn't have capabilities mode page */ cd->cdi.speed = 1; cd->cdi.mask |= (CDC_CD_R | CDC_CD_RW | CDC_DVD_R | @@ -702,7 +694,7 @@ static void get_capabilities(struct scsi_cd *cd) printk("%s: scsi-1 drive\n", cd->cdi.name); 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->readcd_known = 1; cd->readcd_cdda = buffer[n + 5] & 0x01; diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index d19cd9797544..c49c124fde74 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -7,6 +7,7 @@ struct request_queue; struct scsi_cmnd; +struct scsi_mode_data; struct scsi_device { @@ -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_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 */ diff --git a/include/scsi/scsi_request.h b/include/scsi/scsi_request.h index 17cfe3f71d15..e1c9fb7a760e 100644 --- a/include/scsi/scsi_request.h +++ b/include/scsi/scsi_request.h @@ -55,4 +55,19 @@ extern void scsi_do_req(struct scsi_request *, const void *cmnd, void (*done) (struct scsi_cmnd *), 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 */ |
