changeset 936:bb51223d93d7

6226041 "cdrw -p" command doesn't display the default speed 6327126 cdrw incorrectly interprets "1X" for DVD
author arutz
date Wed, 16 Nov 2005 10:49:09 -0800
parents 375bf7616dfc
children e808c6c8cf8e
files usr/src/cmd/cdrw/dae.c usr/src/cmd/cdrw/device.c usr/src/cmd/cdrw/device.h usr/src/cmd/cdrw/misc_scsi.c usr/src/cmd/cdrw/misc_scsi.h usr/src/cmd/cdrw/mmc.h
diffstat 6 files changed, 274 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/cdrw/dae.c	Wed Nov 16 09:13:23 2005 -0800
+++ b/usr/src/cmd/cdrw/dae.c	Wed Nov 16 10:49:09 2005 -0800
@@ -184,12 +184,13 @@
 			if (speed == requested_speed) {
 				(void) printf(gettext("Speed set to %dX.\n"),
 				    speed);
+			} else if (speed == 0) {
+				(void) printf(gettext("Could not obtain "
+				    "current Read Speed.\n"));
 			} else {
-				(void) printf(gettext(
-				    "Speed set to closest approximation "));
-
-				(void) printf(gettext(
-				    "of %dX allowed by device (%dX).\n"),
+				(void) printf(gettext("Speed set to "
+				    "closest approximation of %dX allowed "
+				    "by device (%dX).\n"),
 				    requested_speed, speed);
 			}
 		}
--- a/usr/src/cmd/cdrw/device.c	Wed Nov 16 09:13:23 2005 -0800
+++ b/usr/src/cmd/cdrw/device.c	Wed Nov 16 10:49:09 2005 -0800
@@ -864,3 +864,35 @@
 		}
 	}
 }
+
+/* Translate a transfer rate (eg, KB/s) into a Speed (eg, "2X") */
+uint_t
+cdrw_bandwidth_to_x(uint_t rate)
+{
+	switch (device_type) {
+	case DVD_PLUS_W:
+	case DVD_MINUS:
+	case DVD_PLUS:
+		return (DVD_RATE_TO_X(rate));
+
+	default:
+	case CD_RW:
+		return (CD_RATE_TO_X(rate));
+	}
+}
+
+/* Translate a Speed (eg, "2X") into a transfer rate (eg, KB/s) */
+uint_t
+cdrw_x_to_bandwidth(uint_t x)
+{
+	switch (device_type) {
+	case DVD_PLUS_W:
+	case DVD_MINUS:
+	case DVD_PLUS:
+		return (DVD_X_TO_RATE(x));
+
+	default:
+	case CD_RW:
+		return (CD_X_TO_RATE(x));
+	}
+}
--- a/usr/src/cmd/cdrw/device.h	Wed Nov 16 09:13:23 2005 -0800
+++ b/usr/src/cmd/cdrw/device.h	Wed Nov 16 10:49:09 2005 -0800
@@ -20,7 +20,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -39,23 +39,24 @@
 
 #define	DEFAULT_CAPACITY	(74*60*75)
 
-#define	DEV_CAP_EXTRACT_CDDA			1
-#define	DEV_CAP_ACCURATE_CDDA			2
-#define	DEV_CAP_SETTING_SPEED_NOT_ALLOWED	4
-
 typedef struct _device {
 	char		*d_node;
 	char		*d_name;
 	int		d_fd;
 	uint_t		d_blksize;
-	uchar_t		*d_inq;
-	uint32_t	d_cap;
+	uchar_t		*d_inq;		/* INQUIRY response data */
+	uint32_t	d_cap;		/* capabilities */
 	int		(*d_read_audio)(struct _device *dev, uint_t start_blk,
 			    uint_t nblks, uchar_t *buf);
 	int		(*d_speed_ctrl)(struct _device *dev, int cmd,
 			    int speed);
 } cd_device;
 
+/* values for d_cap */
+#define	DEV_CAP_EXTRACT_CDDA			1
+#define	DEV_CAP_ACCURATE_CDDA			2
+#define	DEV_CAP_SETTING_SPEED_NOT_ALLOWED	4
+
 /*
  * Speed commands
  */
@@ -77,6 +78,8 @@
 void write_next_track(int mode, bstreamhandle h);
 int check_device(cd_device *dev, int cond);
 void get_media_type(int fd);
+uint_t cdrw_bandwidth_to_x(uint_t rate);
+uint_t cdrw_x_to_bandwidth(uint_t x);
 
 #ifdef	__cplusplus
 }
--- a/usr/src/cmd/cdrw/misc_scsi.c	Wed Nov 16 09:13:23 2005 -0800
+++ b/usr/src/cmd/cdrw/misc_scsi.c	Wed Nov 16 10:49:09 2005 -0800
@@ -43,6 +43,7 @@
 #include "main.h"
 #include "toshiba.h"
 #include "msgs.h"
+#include "device.h"
 
 uint32_t
 read_scsi32(void *addr)
@@ -597,104 +598,246 @@
 }
 
 /*
- * Get CD speed from Page code 2A. since GET PERFORMANCE is not supported
- * (which is already checked before) this mode page *will* have the speed.
+ * Get current Read or Write Speed from Mode Page 0x2a.
+ *
+ * Use the size of the Page to determine which Multimedia Command
+ * set (MMC) is present.  Based on the MMC version, get the
+ * specified Read/Write Speed.
+ *
+ * Note that some MMC versions do not necessarily support a
+ * (current) Read or Write Speed.  As a result, this function
+ * _can_ return a value of zero.
+ *
+ * The newer standards (reserve and) mark the field(s) as Obsolete,
+ * yet many vendors populate the Obsolete fields with valid values
+ * (assumedly for backward compatibility).  This is important, as
+ * a command like GET PERFORMANCE cannot return _the_ speed; it can
+ * only return a Logical-Block-Address-dependent (LBA) speed.  Such
+ * values can vary widely between the innermost and outermost Track.
+ * Mode Page 0x2a is the best solution identifying "the current
+ * (nominal) speed".
  */
 static uint16_t
-i_cd_speed_read(cd_device *dev, int cmd)
+cd_speed_get(cd_device *dev, int cmd)
 {
-	uchar_t *mp2a;
-	uint16_t rate;
+	uchar_t		*mp2a;
+	uint16_t	rate = 0;
+	int		offset;
+	uint_t		buflen = 254;
+
+	/*
+	 * Allocate a buffer acceptably larger than any nominal
+	 * Page for Page Code 0x2A.
+	 */
+	mp2a = (uchar_t *)my_zalloc(buflen);
+	if (get_mode_page(dev->d_fd, 0x2A, 0, buflen, mp2a) == 0)
+		goto end;
+
+	/* Determine MMC version based on 'Page Length' field */
+	switch (mp2a[1]) {
+	case 0x14:  /* MMC-1 */
+		if (debug)
+			(void) printf("Mode Page 2A: MMC-1\n");
+
+		offset = (cmd == GET_READ_SPEED) ? 14 : 20;
+		rate = read_scsi16(&mp2a[offset]);
+		break;
+
+
+	case 0x18: /* MMC-2 */
+		if (debug)
+			(void) printf("Mode Page 2A: MMC-2;"
+			    " Read and Write Speeds are "
+			    "obsolete\n");
 
-	mp2a = (uchar_t *)my_zalloc(PAGE_CODE_2A_SIZE);
-	if (get_mode_page(dev->d_fd, 0x2A, 0, PAGE_CODE_2A_SIZE,
-	    mp2a) == 0) {
-		rate = 0;
-	} else {
+		/* see if "Obsolete" values are valid: */
+		offset = (cmd == GET_READ_SPEED) ? 14 : 20;
+		rate = read_scsi16(&mp2a[offset]);
+		break;
+
+	default: /* MMC-3 or newer */
+		if (debug)
+			(void) printf("Mode Page 2A: MMC-3 or"
+			    " newer; Read Speed is obsolete.\n");
+
 		if (cmd == GET_READ_SPEED) {
-			rate = ((uint16_t)mp2a[14] << 8) | mp2a[15];
+			/* this is Obsolete, but try it */
+			offset = 14;
+			rate = read_scsi16(&mp2a[offset]);
 		} else {
-			rate = ((uint16_t)mp2a[20] << 8) | mp2a[21];
+			/* Write Speed is not obsolete */
+			offset = 28;
+			rate = read_scsi16(&mp2a[offset]);
+
+			if (rate == 0) {
+				/*
+				 * then try an Obsolete field
+				 * (but this shouldn't happen!)
+				 */
+				offset = 20;
+				rate = read_scsi16(&mp2a[offset]);
+			}
 		}
+		break;
 	}
+end:
 	free(mp2a);
+
+	if (debug)
+		(void) printf("cd_speed_get: %s Speed is "
+		    "%uX\n", (cmd == GET_READ_SPEED) ?
+		    "Read" : "Write", cdrw_bandwidth_to_x(rate));
 	return (rate);
 }
 
 /*
  * CD speed related functions (ioctl style) for drives which do not support
  * real time streaming.
+ *
+ * For the SET operations, the SET CD SPEED command needs
+ * both the Read Speed and the Write Speed.  Eg, if
+ * we're trying to set the Write Speed (SET_WRITE_SPEED),
+ * then we first need to obtain the current Read Speed.
+ * That speed is specified along with the chosen_speed (the
+ * Write Speed in this case) in the SET CD SPEED command.
  */
 int
 cd_speed_ctrl(cd_device *dev, int cmd, int speed)
 {
 	uint16_t rate;
 
-	if ((cmd == GET_READ_SPEED) || (cmd == GET_WRITE_SPEED))
-		return (XFER_RATE_TO_SPEED(i_cd_speed_read(dev, cmd)));
-	if (cmd == SET_READ_SPEED) {
-		rate = i_cd_speed_read(dev, GET_WRITE_SPEED);
-		return (set_cd_speed(dev->d_fd, SPEED_TO_XFER_RATE(speed),
-		    rate));
+	switch (cmd) {
+	case GET_READ_SPEED:
+		rate = cd_speed_get(dev, GET_READ_SPEED);
+		return (cdrw_bandwidth_to_x(rate));
+
+	case GET_WRITE_SPEED:
+		rate = cd_speed_get(dev, GET_WRITE_SPEED);
+		return (cdrw_bandwidth_to_x(rate));
+
+	case SET_READ_SPEED:
+		rate = cd_speed_get(dev, GET_WRITE_SPEED);
+		return (set_cd_speed(dev->d_fd,
+		    cdrw_x_to_bandwidth(speed), rate));
+		break;
+
+	case SET_WRITE_SPEED:
+		rate = cd_speed_get(dev, GET_READ_SPEED);
+		return (set_cd_speed(dev->d_fd, rate,
+		    cdrw_x_to_bandwidth(speed)));
+		break;
+
+	default:
+		return (0);
 	}
-	if (cmd == SET_WRITE_SPEED) {
-		rate = i_cd_speed_read(dev, GET_READ_SPEED);
-		return (set_cd_speed(dev->d_fd, rate,
-		    SPEED_TO_XFER_RATE(speed)));
-	}
-	return (0);
 }
 
 /*
- * cd speed related functions for drives which support RT-streaming
+ * Manage sending of SET STREAMING command using the specified
+ * read_speed and write_speed.
+ *
+ * This function allocates and initializes a Performance
+ * Descriptor, which is sent as part of the SET STREAMING
+ * command.  The descriptor is deallocated before function
+ * exit.
+ */
+static int
+do_set_streaming(cd_device *dev, uint_t read_speed,
+	uint_t write_speed)
+{
+	int ret;
+	uchar_t *str;
+
+	/* Allocate and initialize the Performance Descriptor */
+	str = (uchar_t *)my_zalloc(SET_STREAM_DATA_LEN);
+
+	/* Read Time (in milliseconds) */
+	load_scsi32(&str[16], 1000);
+	/* Write Time (in milliseconds) */
+	load_scsi32(&str[24], 1000);
+
+	/* Read Speed */
+	load_scsi32(&str[12], (uint32_t)read_speed);
+	/* Write Speed */
+	load_scsi32(&str[20], (uint32_t)write_speed);
+
+	/* issue SET STREAMING command */
+	ret = set_streaming(dev->d_fd, str);
+	free(str);
+
+	return (ret);
+}
+
+/*
+ * cd speed related functions for drives which support
+ * Real-Time Streaming Feature.
+ *
+ * For the SET operations, the SET STREAMING command needs
+ * both the Read Speed and the Write Speed.  Eg, if
+ * we're trying to set the Write Speed (SET_WRITE_SPEED),
+ * then we first need to obtain the current Read Speed.
+ * That speed is specified along with the chosen_speed (the
+ * Write Speed in this case) in the SET STREAMING command.
  */
 int
 rt_streaming_ctrl(cd_device *dev, int cmd, int speed)
 {
-	uchar_t *perf, *str;
-	int write_perf;
-	int ret;
-	uint16_t perf_got;
+	int ret = 0;
+	uint_t rate;
+
+	switch (cmd) {
+	case GET_WRITE_SPEED:
+		rate = cd_speed_get(dev, GET_WRITE_SPEED);
+		ret = (int)cdrw_bandwidth_to_x(rate);
+		break;
+
+	case GET_READ_SPEED:
+		rate = cd_speed_get(dev, GET_READ_SPEED);
+		ret = (int)cdrw_bandwidth_to_x(rate);
+		break;
 
-	write_perf = 0;
-	if ((cmd == GET_WRITE_SPEED) || (cmd == SET_READ_SPEED))
-		write_perf = 1;
-	perf = (uchar_t *)my_zalloc(GET_PERF_DATA_LEN);
-	if (!get_performance(dev->d_fd, write_perf, perf)) {
-		ret = 0;
-		goto end_rsc;
-	}
-	perf_got = (uint16_t)read_scsi32(&perf[20]);
-	if ((cmd == GET_READ_SPEED) || (cmd == GET_WRITE_SPEED)) {
-		ret = XFER_RATE_TO_SPEED(perf_got);
-		goto end_rsc;
-	}
-	str = (uchar_t *)my_zalloc(SET_STREAM_DATA_LEN);
-	(void) memcpy(&str[8], &perf[16], 4);
-	load_scsi32(&str[16], 1000);
-	load_scsi32(&str[24], 1000);
-	if (cmd == SET_WRITE_SPEED) {
-		load_scsi32(&str[12], (uint32_t)perf_got);
-		load_scsi32(&str[20], (uint32_t)SPEED_TO_XFER_RATE(speed));
-	} else {
-		load_scsi32(&str[20], (uint32_t)perf_got);
-		load_scsi32(&str[12], (uint32_t)SPEED_TO_XFER_RATE(speed));
-	}
-	ret = set_streaming(dev->d_fd, str);
-	free(str);
+	case SET_READ_SPEED: {
+		uint_t write_speed = cd_speed_get(dev, GET_WRITE_SPEED);
+
+		/* set Read Speed using SET STREAMING */
+		ret = do_set_streaming(dev,
+		    cdrw_x_to_bandwidth(speed), write_speed);
 
-	/* If rt_speed_ctrl fails for any reason use cd_speed_ctrl */
-	if (ret == 0) {
-		if (debug)
-			(void) printf(" real time speed control"
-			    " failed, using CD speed control\n");
+		/* If rt_speed_ctrl fails for any reason use cd_speed_ctrl */
+		if (ret == 0) {
+			if (debug)
+				(void) printf(" real time speed control"
+				    " failed, using CD speed control\n");
 
-		dev->d_speed_ctrl = cd_speed_ctrl;
-		ret = dev->d_speed_ctrl(dev, cmd, speed);
+			dev->d_speed_ctrl = cd_speed_ctrl;
+			ret = dev->d_speed_ctrl(dev, cmd, speed);
+		}
+		break;
 	}
 
-end_rsc:
-	free(perf);
+	case SET_WRITE_SPEED: {
+		uint_t read_speed = cd_speed_get(dev, GET_READ_SPEED);
+
+		/* set Write Speed using SET STREAMING */
+		ret = do_set_streaming(dev, read_speed,
+		    cdrw_x_to_bandwidth(speed));
+
+		/* If rt_speed_ctrl fails for any reason use cd_speed_ctrl */
+		if (ret == 0) {
+			if (debug)
+				(void) printf(" real time speed control"
+				    " failed, using CD speed control\n");
+
+			dev->d_speed_ctrl = cd_speed_ctrl;
+			ret = dev->d_speed_ctrl(dev, cmd, speed);
+		}
+		break;
+	}
+
+	default:
+		break;
+	}
+
 	return (ret);
 }
 
@@ -780,6 +923,9 @@
 			if (speed == requested_speed) {
 				(void) printf(gettext("Speed set to %dX.\n"),
 				    speed);
+			} else if (speed == 0) {
+				(void) printf(gettext("Could not obtain "
+				    "current Write Speed.\n"));
 			} else {
 				(void) printf(
 				gettext("Speed set to closest approximation "
--- a/usr/src/cmd/cdrw/misc_scsi.h	Wed Nov 16 09:13:23 2005 -0800
+++ b/usr/src/cmd/cdrw/misc_scsi.h	Wed Nov 16 10:49:09 2005 -0800
@@ -20,7 +20,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -74,11 +74,22 @@
 #define	MAX_DVD_BLKS		2295100
 
 /*
+ * Macros to translate between a bandwidth ("RATE") and a Speed ("X")
+ * for CDs.  Eg, "1X == 176,400 bytes/second".
+ *
  * Some devices just multiply speed by 176. But more accurate ones
  * multiply speed by 176.4.
  */
-#define	XFER_RATE_TO_SPEED(r)	((r) % 176 ? ((((r)*10)+5)/1764) : (r) / 176)
-#define	SPEED_TO_XFER_RATE(s)	((((s)*1764)+5)/10)
+#define	CD_RATE_TO_X(r)	((r) % 176 ? ((((r)*10)+5)/1764) : (r) / 176)
+#define	CD_X_TO_RATE(s)	((((s)*1764)+5)/10)
+
+/*
+ * Macros to translate between a bandwidth ("RATE") and a Speed ("X")
+ * for DVDs. Eg, "1X == 1,385,000 bytes/second".
+ */
+#define	DVD_RATE_TO_X(r)	(((ulong_t)(r)*1000)/1385000)
+#define	DVD_X_TO_RATE(s)	(((s)*1385000)/1000)
+
 
 #define	FINALIZE_TIMEOUT		(6 * 12)	/* Six minutes */
 
--- a/usr/src/cmd/cdrw/mmc.h	Wed Nov 16 09:13:23 2005 -0800
+++ b/usr/src/cmd/cdrw/mmc.h	Wed Nov 16 10:49:09 2005 -0800
@@ -20,7 +20,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -33,8 +33,8 @@
 extern "C" {
 #endif
 
+/* bytelengths for some SCSI data structures */
 #define	SENSE_DATA_SIZE		16
-#define	PAGE_CODE_2A_SIZE	26
 #define	TRACK_INFO_SIZE		36
 #define	DISC_INFO_BLOCK_SIZE	32
 #define	INQUIRY_DATA_LENGTH	96