xref: /onnv-gate/usr/src/cmd/hal/utils/cdutils.c (revision 6597)
12912Sartem /***************************************************************************
22912Sartem  *
34215Sartem  * cdutils.c : CD/DVD utilities
42912Sartem  *
54215Sartem  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
62912Sartem  * Use is subject to license terms.
72912Sartem  *
82912Sartem  * Licensed under the Academic Free License version 2.1
92912Sartem  *
102912Sartem  **************************************************************************/
112912Sartem 
122916Sartem #pragma ident	"%Z%%M%	%I%	%E% SMI"
132912Sartem 
142912Sartem #ifdef HAVE_CONFIG_H
152912Sartem #  include <config.h>
162912Sartem #endif
172912Sartem 
182912Sartem #include <stdio.h>
192912Sartem #include <sys/types.h>
202912Sartem #include <sys/scsi/impl/uscsi.h>
212912Sartem #include <string.h>
222912Sartem #include <strings.h>
232912Sartem #include <unistd.h>
242912Sartem #include <stdlib.h>
252912Sartem #include <errno.h>
262912Sartem #include <fcntl.h>
272912Sartem #include <sys/dkio.h>
282912Sartem #include <libintl.h>
292912Sartem 
302912Sartem #include <logger.h>
312912Sartem 
322912Sartem #include "cdutils.h"
332912Sartem 
342912Sartem #define	RQLEN	32
352912Sartem #define SENSE_KEY(rqbuf)        (rqbuf[2])      /* scsi error category */
362912Sartem #define ASC(rqbuf)              (rqbuf[12])     /* additional sense code */
372912Sartem #define ASCQ(rqbuf)             (rqbuf[13])     /* ASC qualifier */
382912Sartem 
392912Sartem #define	GET16(a) (((a)[0] << 8) | (a)[1])
402912Sartem #define	GET32(a) (((a)[0] << 24) | ((a)[1] << 16) | ((a)[2] << 8) | (a)[3])
412912Sartem 
422912Sartem #define	CD_USCSI_TIMEOUT	60
432912Sartem 
442912Sartem void
452912Sartem uscsi_cmd_init(struct uscsi_cmd *scmd, char *cdb, int cdblen)
462912Sartem {
472912Sartem 	bzero(scmd, sizeof (*scmd));
482912Sartem 	bzero(cdb, cdblen);
492912Sartem 	scmd->uscsi_cdb = cdb;
502912Sartem }
512912Sartem 
522912Sartem int
532912Sartem uscsi(int fd, struct uscsi_cmd *scmd)
542912Sartem {
552912Sartem 	char		rqbuf[RQLEN];
562912Sartem 	int		ret;
572912Sartem 	int		i, retries, total_retries;
582912Sartem 	int		max_retries = 20;
592912Sartem 
602912Sartem 	scmd->uscsi_flags |= USCSI_RQENABLE;
612912Sartem 	scmd->uscsi_rqlen = RQLEN;
622912Sartem 	scmd->uscsi_rqbuf = rqbuf;
632912Sartem 
642912Sartem 	for (retries = 0; retries < max_retries; retries++) {
652912Sartem 		scmd->uscsi_status = 0;
662912Sartem 		memset(rqbuf, 0, RQLEN);
672912Sartem 
682912Sartem 		ret = ioctl(fd, USCSICMD, scmd);
692912Sartem 
702912Sartem 		if ((ret == 0) && (scmd->uscsi_status == 2)) {
712912Sartem 			ret = -1;
722912Sartem 			errno = EIO;
732912Sartem 		}
742912Sartem 		if ((ret < 0) && (scmd->uscsi_status == 2)) {
752912Sartem 			/*
762912Sartem 			 * The drive is not ready to recieve commands but
772912Sartem 			 * may be in the process of becoming ready.
782912Sartem 			 * sleep for a short time then retry command.
792912Sartem 			 * SENSE/ASC = 2/4 : not ready
802912Sartem 			 * ASCQ = 0  Not Reportable.
812912Sartem 			 * ASCQ = 1  Becoming ready.
822912Sartem 			 * ASCQ = 4  FORMAT in progress.
832912Sartem 			 * ASCQ = 7  Operation in progress.
842912Sartem 			 */
852912Sartem 			if ((SENSE_KEY(rqbuf) == 2) && (ASC(rqbuf) == 4) &&
862912Sartem 			    ((ASCQ(rqbuf) == 0) || (ASCQ(rqbuf) == 1) ||
872912Sartem 			    (ASCQ(rqbuf) == 4)) || (ASCQ(rqbuf) == 7)) {
882912Sartem 				total_retries++;
892912Sartem 				sleep(1);
902912Sartem 				continue;
912912Sartem 			}
922912Sartem 
932912Sartem 			/*
942912Sartem 			 * Device is not ready to transmit or a device reset
952912Sartem 			 * has occurred. wait for a short period of time then
962912Sartem 			 * retry the command.
972912Sartem 			 */
982912Sartem 			if ((SENSE_KEY(rqbuf) == 6) && ((ASC(rqbuf) == 0x28) ||
992912Sartem 			    (ASC(rqbuf) == 0x29))) {
1002912Sartem 				sleep(1);
1012912Sartem 				total_retries++;
1022912Sartem 				continue;
1032912Sartem 			}
1042912Sartem 			/*
1052912Sartem 			 * Blank Sense, we don't know what the error is or if
1062912Sartem 			 * the command succeeded, Hope for the best. Some
1072912Sartem 			 * drives return blank sense periodically and will
1082912Sartem 			 * fail if this is removed.
1092912Sartem 			 */
1102912Sartem 			if ((SENSE_KEY(rqbuf) == 0) && (ASC(rqbuf) == 0) &&
1112912Sartem 			    (ASCQ(rqbuf) == 0)) {
1122912Sartem 				ret = 0;
1132912Sartem 				break;
1142912Sartem 			}
1152912Sartem 
1162912Sartem 			HAL_DEBUG (("cmd: 0x%02x ret:%i status:%02x "
1172912Sartem 			    " sense: %02x ASC: %02x ASCQ:%02x\n",
1182912Sartem 			    (uchar_t)scmd->uscsi_cdb[0], ret,
1192912Sartem 			    scmd->uscsi_status,
1202912Sartem 			    (uchar_t)SENSE_KEY(rqbuf),
1212912Sartem 			    (uchar_t)ASC(rqbuf), (uchar_t)ASCQ(rqbuf)));
1222912Sartem 		}
1232912Sartem 
1242912Sartem 		break;
1252912Sartem 	}
1262912Sartem 
1272912Sartem 	if (retries) {
1282912Sartem 		HAL_DEBUG (("total retries: %d\n", total_retries));
1292912Sartem 	}
1302912Sartem 
1312912Sartem 	return (ret);
1322912Sartem }
1332912Sartem 
1342912Sartem int
1352912Sartem mode_sense(int fd, uchar_t pc, int dbd, int page_len, uchar_t *buffer)
1362912Sartem {
1372912Sartem 	struct uscsi_cmd scmd;
1382912Sartem 	char cdb[16];
1392912Sartem 
1402912Sartem 	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
1412912Sartem 	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
1422912Sartem 	scmd.uscsi_buflen = page_len;
1432912Sartem 	scmd.uscsi_bufaddr = (char *)buffer;
1442912Sartem 	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
1452912Sartem 	scmd.uscsi_cdblen = 0xa;
1462912Sartem 	scmd.uscsi_cdb[0] = 0x5a; /* MODE SENSE 10 */
1472912Sartem 	if (dbd) {
1482912Sartem 		scmd.uscsi_cdb[1] = 0x8; /* no block descriptors */
1492912Sartem 	}
1502912Sartem 	scmd.uscsi_cdb[2] = pc;
1512912Sartem 	scmd.uscsi_cdb[7] = (page_len >> 8) & 0xff;
1522912Sartem 	scmd.uscsi_cdb[8] = page_len & 0xff;
1532912Sartem 
1542912Sartem 	return (uscsi(fd, &scmd) == 0);
1552912Sartem }
1562912Sartem 
1572912Sartem /*
1582912Sartem  * will get the mode page only i.e. will strip off the header.
1592912Sartem  */
1602912Sartem int
1612912Sartem get_mode_page(int fd, int page_no, int pc, int buf_len, uchar_t *buffer, int *plen)
1622912Sartem {
1632912Sartem 	int ret;
1642912Sartem 	uchar_t byte2;
1652912Sartem 	uchar_t buf[256];
1662912Sartem 	uint_t header_len, page_len, copy_cnt;
1672912Sartem 
1682912Sartem 	byte2 = (uchar_t)(((pc << 6) & 0xC0) | (page_no & 0x3f));
1692912Sartem 
1702912Sartem 	/* Ask 254 bytes only to make our IDE driver happy */
1712912Sartem 	if ((ret = mode_sense(fd, byte2, 1, 254, buf)) == 0) {
1722912Sartem 		return (0);
1732912Sartem 	}
1742912Sartem 
1752912Sartem 	header_len = 8 + GET16(&buf[6]);
1762912Sartem 	page_len = buf[header_len + 1] + 2;
1772912Sartem 
1782912Sartem 	copy_cnt = (page_len > buf_len) ? buf_len : page_len;
1792912Sartem 	(void) memcpy(buffer, &buf[header_len], copy_cnt);
1802912Sartem 
1812912Sartem 	if (plen) {
1822912Sartem 		*plen = page_len;
1832912Sartem 	}
1842912Sartem 
1852912Sartem 	return (1);
1862912Sartem }
1872912Sartem 
1882912Sartem /* Get information about the Logical Unit's capabilities */
1892912Sartem int
1902912Sartem get_configuration(int fd, uint16_t feature, int bufsize, uchar_t *buf)
1912912Sartem {
1922912Sartem 	struct uscsi_cmd scmd;
1932912Sartem 	char cdb[16];
1942912Sartem 
1952912Sartem 	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
1962912Sartem 	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
1972912Sartem 	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
1982912Sartem 	scmd.uscsi_cdb[0] = 0x46; /* GET CONFIGURATION */
1992912Sartem 	scmd.uscsi_cdb[1] = 0x2; /* request type */
2002912Sartem 	scmd.uscsi_cdb[2] = (feature >> 8) & 0xff; /* starting feature # */
2012912Sartem 	scmd.uscsi_cdb[3] = feature & 0xff;
2022912Sartem 	scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */
2032912Sartem 	scmd.uscsi_cdb[8] = bufsize & 0xff;
2042912Sartem 	scmd.uscsi_cdblen = 10;
2052912Sartem 	scmd.uscsi_bufaddr = (char *)buf;
2062912Sartem 	scmd.uscsi_buflen = bufsize;
2072912Sartem 
2082912Sartem 	return (uscsi(fd, &scmd) == 0);
2092912Sartem }
2102912Sartem 
2112912Sartem boolean_t
2122912Sartem get_current_profile(int fd, int *profile)
2132912Sartem {
2142912Sartem 	size_t i;
2152912Sartem 	uchar_t smallbuf[4];
2162912Sartem 	size_t buflen;
2172912Sartem 	uchar_t *bufp;
2182912Sartem 	int ret = B_FALSE;
2192912Sartem 
2202912Sartem 	/* first determine amount of memory needed to hold all profiles */
2212912Sartem 	if (get_configuration(fd, 0, 4, &smallbuf[0])) {
2222912Sartem 		buflen = GET32(smallbuf) + 4;
2232912Sartem 		bufp = (uchar_t *)malloc(buflen);
2242912Sartem 
2252912Sartem 	 	/* now get all profiles */
2262912Sartem 		if (get_configuration(fd, 0, buflen, bufp)) {
2272912Sartem 			*profile = GET16(&bufp[6]);
2282912Sartem 			ret = B_TRUE;
2292912Sartem 		}
2302912Sartem 		free(bufp);
2312912Sartem 	}
2322912Sartem 
2332912Sartem 	return (ret);
2342912Sartem }
2352912Sartem 
2362912Sartem void
2372912Sartem walk_profiles(int fd, int (*f)(void *, int, boolean_t), void *arg)
2382912Sartem {
2392912Sartem 	size_t i;
2402912Sartem 	uint16_t profile, current_profile;
2412912Sartem 	uchar_t smallbuf[4];
2422912Sartem 	size_t buflen;
2432912Sartem 	uchar_t *bufp;
2442912Sartem 	int ret;
2452912Sartem 
2462912Sartem 	/* first determine amount of memory needed to hold all profiles */
2472912Sartem 	if (get_configuration(fd, 0, 4, &smallbuf[0])) {
2482912Sartem 		buflen = GET32(smallbuf) + 4;
2492912Sartem 		bufp = (uchar_t *)malloc(buflen);
2502912Sartem 
2512912Sartem 	 	/* now get all profiles */
2522912Sartem 		if (get_configuration(fd, 0, buflen, bufp)) {
2532912Sartem 			current_profile = GET16(&bufp[6]);
2542912Sartem 			for (i = 8 + 4;  i < buflen; i += 4) {
2552912Sartem 				profile = GET16(&bufp[i]);
2562912Sartem 				ret = f(arg, profile, (profile == current_profile));
2572912Sartem 				if (ret == CDUTIL_WALK_STOP) {
2582912Sartem 					break;
2592912Sartem 				}
2602912Sartem 			}
2612912Sartem 		}
2622912Sartem 
2632912Sartem 		free(bufp);
2642912Sartem 	}
2652912Sartem }
2662912Sartem 
2672912Sartem /* retrieve speed list from the Write Speed Performance Descriptor Blocks
2682912Sartem  */
2692912Sartem void
2702912Sartem get_write_speeds(uchar_t *page, int n, intlist_t **speeds, int *n_speeds, intlist_t **speeds_mem)
2712912Sartem {
2722912Sartem 	uchar_t	*p = page + 2;
2732912Sartem 	int	i;
2742912Sartem 	intlist_t **nextp;
2752912Sartem 	intlist_t *current;
2762912Sartem 	boolean_t skip;
2772912Sartem 
2782912Sartem 	*n_speeds = 0;
2792912Sartem 	*speeds = NULL;
2802912Sartem 	*speeds_mem = (intlist_t *)calloc(n, sizeof (intlist_t));
2812912Sartem 	if (*speeds_mem == NULL) {
2822912Sartem 		return;
2832912Sartem 	}
2842912Sartem 
2852912Sartem 	for (i = 0; i < n; i++, p += 4) {
2862912Sartem 		current = &(*speeds_mem)[i];
2872912Sartem 		current->val = GET16(p);
2882912Sartem 
2892912Sartem 		/* keep the list sorted */
2902912Sartem 		skip = B_FALSE;
2912912Sartem 		for (nextp = speeds; *nextp != NULL; nextp = &((*nextp)->next)) {
2922912Sartem 			if (current->val == (*nextp)->val) {
2932912Sartem 				skip = B_TRUE; /* skip duplicates */
2942912Sartem 				break;
2952912Sartem 			} else if (current->val > (*nextp)->val) {
2962912Sartem 				break;
2972912Sartem 			}
2982912Sartem 		}
2992912Sartem 		if (!skip) {
3002912Sartem 			current->next = *nextp;
3012912Sartem 			*nextp = current;
3022912Sartem 			*n_speeds++;
3032912Sartem 		}
3042912Sartem 	}
3052912Sartem }
3062912Sartem 
3072912Sartem void
3082912Sartem get_read_write_speeds(int fd, int *read_speed, int *write_speed,
3092912Sartem     intlist_t **speeds, int *n_speeds, intlist_t **speeds_mem)
3102912Sartem {
3112912Sartem 	int page_len;
3122912Sartem 	uchar_t	p[254];
3132912Sartem 	int n; /* number of write speed performance descriptor blocks */
3142912Sartem 
3152912Sartem 	*read_speed = *write_speed = 0;
3162912Sartem 	*speeds = *speeds_mem = NULL;
3172912Sartem 
3182912Sartem 	if (!get_mode_page(fd, 0x2A, 0, sizeof (p), p, &page_len)) {
3192912Sartem 		return;
3202912Sartem 	}
3212912Sartem 
3222912Sartem 	if (page_len > 8) {
3232912Sartem 		*read_speed = GET16(&p[8]);
3242912Sartem 	}
3252912Sartem 	if (page_len > 18) {
3262912Sartem 		*write_speed = GET16(&p[18]);
3272912Sartem 	}
3282912Sartem 	if (page_len < 28) {
3292912Sartem 		printf("MMC-2\n");
3302912Sartem 		return;
3312912Sartem 	} else {
3322912Sartem 		printf("MMC-3\n");
3332912Sartem 	}
3342912Sartem 
3352912Sartem 	*write_speed = GET16(&p[28]);
3362912Sartem 
3372912Sartem 	if (page_len < 30) {
3382912Sartem 		return;
3392912Sartem 	}
3402912Sartem 
3412912Sartem 	/* retrieve speed list */
3422912Sartem 	n = GET16(&p[30]);
3432912Sartem 	n = min(n, (sizeof (p) - 32) / 4);
3442912Sartem 
3452912Sartem 	get_write_speeds(&p[32], n, speeds, n_speeds, speeds_mem);
3462912Sartem 
3472912Sartem 	if (*speeds != NULL) {
3482912Sartem 		*write_speed = max(*write_speed, (*speeds)[0].val);
3492912Sartem 	}
3502912Sartem }
3512912Sartem 
3522912Sartem boolean_t
3532912Sartem get_disc_info(int fd, disc_info_t *di)
3542912Sartem {
3552912Sartem 	struct uscsi_cmd scmd;
3562912Sartem 	char cdb[16];
3572912Sartem 	uint8_t	buf[32];
3582912Sartem 	int bufsize = sizeof (buf);
3592912Sartem 
3602912Sartem 	bzero(buf, bufsize);
3612912Sartem 	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
3622912Sartem 	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
3632912Sartem 	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
3642912Sartem 	scmd.uscsi_cdb[0] = 0x51; /* READ DISC INFORMATION */
3652912Sartem 	scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */
3662912Sartem 	scmd.uscsi_cdb[8] = bufsize & 0xff;
3672912Sartem 	scmd.uscsi_cdblen = 10;
3682912Sartem 	scmd.uscsi_bufaddr = (char *)buf;
3692912Sartem 	scmd.uscsi_buflen = bufsize;
3702912Sartem 
3712912Sartem 	if ((uscsi(fd, &scmd)) != 0) {
3722912Sartem 		return (B_FALSE);
3732912Sartem 	}
3742912Sartem 
3754215Sartem 	/*
3764215Sartem 	 * According to MMC-5 6.22.3.2, the Disc Information Length should be
3774215Sartem 	 * 32+8*(Number of OPC Tables). Some devices, like U3 sticks, return 0.
378*6597Sartem 	 * Yet some drives can return less than 32. We only need the first 22.
3794215Sartem 	 */
380*6597Sartem 	if (GET16(&buf[0]) < 22) {
3814215Sartem 		return (B_FALSE);
3824215Sartem 	}
3834215Sartem 
3842912Sartem 	di->disc_status = buf[2] & 0x03;
3852912Sartem 	di->erasable = buf[2] & 0x10;
3862912Sartem 	if ((buf[21] != 0) && (buf[21] != 0xff)) {
3872912Sartem 		di->capacity = ((buf[21] * 60) + buf[22]) * 75;
3882912Sartem 	} else {
3892912Sartem 		di->capacity = 0;
3904215Sartem 	}
3912912Sartem 
3922912Sartem 	return (B_TRUE);
3932912Sartem }
3942912Sartem 
3952912Sartem /*
3962912Sartem  * returns current/maximum format capacity in bytes
3972912Sartem  */
3982912Sartem boolean_t
3992912Sartem read_format_capacity(int fd, uint64_t *capacity)
4002912Sartem {
4012912Sartem 	struct uscsi_cmd scmd;
4022912Sartem 	char cdb[16];
4032912Sartem 	uint8_t	buf[32];
4042912Sartem 	int bufsize = sizeof (buf);
4052912Sartem 	uint32_t num_blocks;
4062912Sartem 	uint32_t block_len;
4072912Sartem 
4082912Sartem 	bzero(buf, bufsize);
4092912Sartem 	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
4102912Sartem 	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
4112912Sartem 	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
4122912Sartem 	scmd.uscsi_cdb[0] = 0x23; /* READ FORMAT CAPACITIRES */
4132912Sartem 	scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */
4142912Sartem 	scmd.uscsi_cdb[8] = bufsize & 0xff;
4152912Sartem 	scmd.uscsi_cdblen = 12;
4162912Sartem 	scmd.uscsi_bufaddr = (char *)buf;
4172912Sartem 	scmd.uscsi_buflen = bufsize;
4182912Sartem 
4192912Sartem 	if ((uscsi(fd, &scmd)) != 0) {
4202912Sartem 		return (B_FALSE);
4212912Sartem 	}
4222912Sartem 
4232912Sartem 	num_blocks = (uint32_t)(buf[4] << 24) + (buf[5] << 16) + (buf[6] << 8) + buf[7];
4242912Sartem 	block_len = (uint32_t)(buf[9] << 16) + (buf[10] << 8) + buf[11];
4252912Sartem 	*capacity = (uint64_t)num_blocks * block_len;
4262912Sartem 
4272912Sartem 	return (B_TRUE);
4282912Sartem }
4292912Sartem 
4302912Sartem boolean_t
4312912Sartem get_media_info(int fd, struct dk_minfo *minfop)
4322912Sartem {
4332912Sartem 	return (ioctl(fd, DKIOCGMEDIAINFO, minfop) != -1);
4342912Sartem }
4352912Sartem 
4362912Sartem /*
4372912Sartem  * given current profile, use the best method for determining
4382912Sartem  * disc capacity (in bytes)
4392912Sartem  */
4402912Sartem boolean_t
4412912Sartem get_disc_capacity_for_profile(int fd, int profile, uint64_t *capacity)
4422912Sartem {
4432912Sartem 	struct dk_minfo	mi;
4442912Sartem 	disc_info_t	di;
4452912Sartem 	boolean_t	ret = B_FALSE;
4462912Sartem 
4472912Sartem 	switch (profile) {
4482912Sartem 	case 0x08: /* CD-ROM */
4492912Sartem 	case 0x10: /* DVD-ROM */
4502912Sartem 		if (get_media_info(fd, &mi) && (mi.dki_capacity > 1)) {
4512912Sartem 			*capacity = mi.dki_capacity * mi.dki_lbsize;
4522912Sartem 			ret = B_TRUE;
4532912Sartem 		}
4542912Sartem 		break;
4552912Sartem 	default:
4562912Sartem 		if (read_format_capacity(fd, capacity) && (*capacity > 0)) {
4572912Sartem 			ret = B_TRUE;
4582912Sartem 		} else if (get_disc_info(fd, &di) && (di.capacity > 0)) {
4592912Sartem 			if (get_media_info(fd, &mi)) {
4602912Sartem 				*capacity = di.capacity * mi.dki_lbsize;
4612912Sartem 				ret = B_TRUE;
4622912Sartem 			}
4632912Sartem 		}
4642912Sartem 	}
4652912Sartem 
4662912Sartem 	return (ret);
4672912Sartem }
4682912Sartem 
4692912Sartem boolean_t
4702912Sartem read_toc(int fd, int format, int trackno, int buflen, uchar_t *buf)
4712912Sartem {
4722912Sartem 	struct uscsi_cmd scmd;
4732912Sartem 	char cdb[16];
4742912Sartem 
4752912Sartem 	bzero(buf, buflen);
4762912Sartem 	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
4772912Sartem 	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
4782912Sartem 	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
4792912Sartem 	scmd.uscsi_cdb[0] = 0x43 /* READ_TOC_CMD */;
4802912Sartem 	scmd.uscsi_cdb[2] = format & 0xf;
4812912Sartem 	scmd.uscsi_cdb[6] = trackno;
4822912Sartem 	scmd.uscsi_cdb[8] = buflen & 0xff;
4832912Sartem 	scmd.uscsi_cdb[7] = (buflen >> 8) & 0xff;
4842912Sartem 	scmd.uscsi_cdblen = 10;
4852912Sartem 	scmd.uscsi_bufaddr = (char *)buf;
4862912Sartem 	scmd.uscsi_buflen = buflen;
4872912Sartem 
4882912Sartem 	if ((uscsi(fd, &scmd)) != 0) {
4892912Sartem         	return (B_FALSE);
4902912Sartem 	}
4912912Sartem 
4922912Sartem 	return (B_TRUE);
4932912Sartem }
494