public inbox for linux-scsi@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] definitive abstraction of the mode_sense commands
@ 2003-06-24  4:31 James Bottomley
  2003-06-24  8:10 ` Douglas Gilbert
  0 siblings, 1 reply; 4+ messages in thread
From: James Bottomley @ 2003-06-24  4:31 UTC (permalink / raw)
  To: SCSI Mailing List, Matthew Dharm

[-- Attachment #1: Type: text/plain, Size: 282 bytes --]

The attached is an extension of the previous abstraction patch so that
one of the returns is now a command size independent view of the mode
sense header.  Thus it should now be easy to process even non DBD mode
sense commands whatever command size is chosen by the device.

James


[-- Attachment #2: tmp.diff --]
[-- Type: text/plain, Size: 12781 bytes --]

# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.1408  -> 1.1409 
#	include/scsi/scsi_device.h	1.1     -> 1.2    
#	   drivers/scsi/sr.c	1.82    -> 1.83   
#	drivers/scsi/scsi_syms.c	1.40    -> 1.41   
#	drivers/scsi/scsi_lib.c	1.95    -> 1.96   
#	   drivers/scsi/sd.c	1.123   -> 1.124  
#	include/scsi/scsi_request.h	1.1     -> 1.2    
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 03/06/23	jejb@raven.il.steeleye.com	1.1409
# 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.
# 
# Note: Requestors of the Block Descriptors now must pay attention
# to the longlba flag
# --------------------------------------------
#
diff -Nru a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
--- a/drivers/scsi/scsi_lib.c	Mon Jun 23 23:30:02 2003
+++ b/drivers/scsi/scsi_lib.c	Mon Jun 23 23:30:02 2003
@@ -1336,3 +1336,126 @@
 		kmem_cache_destroy(sgp->slab);
 	}
 }
+/**	scsi_do_mode_sense - issue a mode sense, falling back from 10 to 
+ *		six bytes if necessary.
+ *	@SRpnt:	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_do_mode_sense(struct scsi_request *SRpnt, 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 ? 0x08: 0;
+	cmd[2] = modepage;
+
+ retry:
+	use_10_for_ms = SRpnt->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;
+	}
+
+	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, 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(SRpnt->sr_result) &&
+	    (driver_byte(SRpnt->sr_result) & DRIVER_SENSE) &&
+	    SRpnt->sr_sense_buffer[2] == ILLEGAL_REQUEST &&
+	    (SRpnt->sr_sense_buffer[4] & 0x40) == 0x40 &&
+	    SRpnt->sr_sense_buffer[5] == 0 &&
+	    SRpnt->sr_sense_buffer[6] == 0 ) {
+		SRpnt->sr_device->use_10_for_ms = 0;
+		goto retry;
+	}
+
+	if(scsi_status_is_good(SRpnt->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 SRpnt->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 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
+ *
+ *	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 0;
+
+	ret = scsi_do_mode_sense(sreq, dbd, modepage, buffer, len,
+				 timeout, retries, data);
+
+	scsi_release_request(sreq);
+
+	return ret;
+}
diff -Nru a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c
--- a/drivers/scsi/scsi_syms.c	Mon Jun 23 23:30:02 2003
+++ b/drivers/scsi/scsi_syms.c	Mon Jun 23 23:30:02 2003
@@ -86,6 +86,9 @@
 EXPORT_SYMBOL(scsi_remove_device);
 EXPORT_SYMBOL(scsi_set_device_offline);
 
+EXPORT_SYMBOL(scsi_do_mode_sense);
+EXPORT_SYMBOL(scsi_mode_sense);
+
 /*
  * This symbol is for the highlevel drivers (e.g. sg) only.
  */
diff -Nru a/drivers/scsi/sd.c b/drivers/scsi/sd.c
--- a/drivers/scsi/sd.c	Mon Jun 23 23:30:02 2003
+++ b/drivers/scsi/sd.c	Mon Jun 23 23:30:02 2003
@@ -1062,40 +1062,12 @@
 }
 
 /* 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_do_mode_sense(SRpnt, dbd, modepage, buffer, len,
+				  SD_TIMEOUT, SD_MAX_RETRIES, data);
 }
 
 /*
@@ -1106,33 +1078,34 @@
 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",
@@ -1151,41 +1124,43 @@
 
 	const int dbd = 0x08;	   /* 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 -Nru a/drivers/scsi/sr.c b/drivers/scsi/sr.c
--- a/drivers/scsi/sr.c	Mon Jun 23 23:30:02 2003
+++ b/drivers/scsi/sr.c	Mon Jun 23 23:30:02 2003
@@ -660,9 +660,9 @@
 
 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 @@
 		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);
+	rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, 128,
+			     SR_TIMEOUT, 3, &data);
 
-	if (rc) {
+	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 @@
 		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 -Nru a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
--- a/include/scsi/scsi_device.h	Mon Jun 23 23:30:02 2003
+++ b/include/scsi/scsi_device.h	Mon Jun 23 23:30:02 2003
@@ -103,4 +103,9 @@
 
 extern int scsi_set_medium_removal(struct scsi_device *, char);
 
+struct scsi_mode_data;
+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 -Nru a/include/scsi/scsi_request.h b/include/scsi/scsi_request.h
--- a/include/scsi/scsi_request.h	Mon Jun 23 23:30:02 2003
+++ b/include/scsi/scsi_request.h	Mon Jun 23 23:30:02 2003
@@ -55,4 +55,19 @@
 			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_do_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 */

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] definitive abstraction of the mode_sense commands
  2003-06-24  4:31 [PATCH] definitive abstraction of the mode_sense commands James Bottomley
@ 2003-06-24  8:10 ` Douglas Gilbert
  2003-06-24  8:17   ` Matthew Dharm
  2003-06-24 17:58   ` James Bottomley
  0 siblings, 2 replies; 4+ messages in thread
From: Douglas Gilbert @ 2003-06-24  8:10 UTC (permalink / raw)
  To: James Bottomley; +Cc: SCSI Mailing List

James Bottomley wrote:

> +/**	scsi_do_mode_sense - issue a mode sense, falling back from 10 to 
> + *		six bytes if necessary.
> + *	@SRpnt:	SCSI request to fill in with the MODE_SENSE
> + *	@dbd:	set if mode sense will allow block descriptors to be returned

"A DBD bit of one specifies that the device server shall
not return any block descriptors in the returned MODE SENSE
data" [SPC-3 rev 13]. So that description of @dbd seems
inverted.

 > # Note: Requestors of the Block Descriptors now must pay attention
 > # to the longlba flag

The longlba flag can only be set in the "mode parameter
header(10)" if the LLBAA bit is set in the corresponding
MODE SENSE(10) command [ref: SPC-3 rev 13 MODE SENSE(10)
command (section 6.10)]. Your code doesn't set LLBAA so
there should be no reason to check the longlba flag.


For really depraved devices it can be useful to send a
MODE SENSE(6 or 10) that only asks for a 4 byte response. From
this response the length of the respone that can be supplied
is deduced. Then a second MODE SENSE can be
issued for the now known response length. Yes, it's
horrible, but that is precisely what Joerg Schilling
does in cdrecord.
Your code seems to have this technique in mind but requires
a MODE SENSE(10) response buffer to be at least 8 bytes long.

Doug Gilbert


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] definitive abstraction of the mode_sense commands
  2003-06-24  8:10 ` Douglas Gilbert
@ 2003-06-24  8:17   ` Matthew Dharm
  2003-06-24 17:58   ` James Bottomley
  1 sibling, 0 replies; 4+ messages in thread
From: Matthew Dharm @ 2003-06-24  8:17 UTC (permalink / raw)
  To: Douglas Gilbert; +Cc: James Bottomley, SCSI Mailing List

[-- Attachment #1: Type: text/plain, Size: 1056 bytes --]

On Tue, Jun 24, 2003 at 06:10:29PM +1000, Douglas Gilbert wrote:
> For really depraved devices it can be useful to send a
> MODE SENSE(6 or 10) that only asks for a 4 byte response. From
> this response the length of the respone that can be supplied
> is deduced. Then a second MODE SENSE can be
> issued for the now known response length. Yes, it's
> horrible, but that is precisely what Joerg Schilling
> does in cdrecord.
> Your code seems to have this technique in mind but requires
> a MODE SENSE(10) response buffer to be at least 8 bytes long.

The code does it this way because more devices seem to work that way.  That
is, always ask for the 'header' size as a minimum, and header size depends
on which command you send.

Lots of devices can't return less than the header size.

Matt

-- 
Matthew Dharm                              Home: mdharm-usb@one-eyed-alien.net 
Maintainer, Linux USB Mass Storage Driver

What, are you one of those Microsoft-bashing Linux freaks?
					-- Customer to Greg
User Friendly, 2/10/1999

[-- Attachment #2: Type: application/pgp-signature, Size: 232 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] definitive abstraction of the mode_sense commands
  2003-06-24  8:10 ` Douglas Gilbert
  2003-06-24  8:17   ` Matthew Dharm
@ 2003-06-24 17:58   ` James Bottomley
  1 sibling, 0 replies; 4+ messages in thread
From: James Bottomley @ 2003-06-24 17:58 UTC (permalink / raw)
  To: dougg; +Cc: SCSI Mailing List

[-- Attachment #1: Type: text/plain, Size: 252 bytes --]

I fixed your comments (I did have DBD's meaning reversed when I first
made the patch---I fixed it all except for the comment).

I also added the ability to pass LLBAA in the dbd part.

Plus some coding style fixes that certain persons noticed.

James


[-- Attachment #2: tmp.diff --]
[-- Type: text/plain, Size: 13586 bytes --]

# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.1408  -> 1.1414 
#	include/scsi/scsi_device.h	1.1     -> 1.3    
#	   drivers/scsi/sr.c	1.82    -> 1.83   
#	drivers/scsi/scsi_syms.c	1.40    -> 1.42   
#	drivers/scsi/scsi_lib.c	1.95    -> 1.99   
#	   drivers/scsi/sd.c	1.123   -> 1.126  
#	include/scsi/scsi_request.h	1.1     -> 1.3    
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 03/06/23	jejb@raven.il.steeleye.com	1.1409
# 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.
# 
# Note: Requestors of the Block Descriptors now must pay attention
# to the longlba flag
# --------------------------------------------
# 03/06/24	jejb@raven.il.steeleye.com	1.1410
# Fix hch comments
# --------------------------------------------
# 03/06/24	jejb@raven.il.steeleye.com	1.1411
# Fix up export symbols
# --------------------------------------------
# 03/06/24	jejb@raven.il.steeleye.com	1.1412
# More fix ups
# --------------------------------------------
# 03/06/24	jejb@raven.il.steeleye.com	1.1413
# Minor fixes
# --------------------------------------------
# 03/06/24	jejb@raven.il.steeleye.com	1.1414
# More fixes
# --------------------------------------------
#
diff -Nru a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
--- a/drivers/scsi/scsi_lib.c	Tue Jun 24 12:55:16 2003
+++ b/drivers/scsi/scsi_lib.c	Tue Jun 24 12:55:16 2003
@@ -1336,3 +1336,127 @@
 		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 -Nru a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c
--- a/drivers/scsi/scsi_syms.c	Tue Jun 24 12:55:16 2003
+++ b/drivers/scsi/scsi_syms.c	Tue Jun 24 12:55:16 2003
@@ -86,6 +86,9 @@
 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 -Nru a/drivers/scsi/sd.c b/drivers/scsi/sd.c
--- a/drivers/scsi/sd.c	Tue Jun 24 12:55:16 2003
+++ b/drivers/scsi/sd.c	Tue Jun 24 12:55:16 2003
@@ -1062,40 +1062,12 @@
 }
 
 /* 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 @@
 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 @@
 		   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 -Nru a/drivers/scsi/sr.c b/drivers/scsi/sr.c
--- a/drivers/scsi/sr.c	Tue Jun 24 12:55:16 2003
+++ b/drivers/scsi/sr.c	Tue Jun 24 12:55:16 2003
@@ -660,9 +660,9 @@
 
 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 @@
 		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);
+	rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, 128,
+			     SR_TIMEOUT, 3, &data);
 
-	if (rc) {
+	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 @@
 		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 -Nru a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
--- a/include/scsi/scsi_device.h	Tue Jun 24 12:55:15 2003
+++ b/include/scsi/scsi_device.h	Tue Jun 24 12:55:15 2003
@@ -7,6 +7,7 @@
 
 struct request_queue;
 struct scsi_cmnd;
+struct scsi_mode_data;
 
 
 struct scsi_device {
@@ -102,5 +103,9 @@
 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 -Nru a/include/scsi/scsi_request.h b/include/scsi/scsi_request.h
--- a/include/scsi/scsi_request.h	Tue Jun 24 12:55:16 2003
+++ b/include/scsi/scsi_request.h	Tue Jun 24 12:55:16 2003
@@ -55,4 +55,19 @@
 			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 */

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2003-06-24 17:45 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-06-24  4:31 [PATCH] definitive abstraction of the mode_sense commands James Bottomley
2003-06-24  8:10 ` Douglas Gilbert
2003-06-24  8:17   ` Matthew Dharm
2003-06-24 17:58   ` James Bottomley

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox