xref: /onnv-gate/usr/src/cmd/hal/utils/cdutils.c (revision 2912)
1*2912Sartem /***************************************************************************
2*2912Sartem  *
3*2912Sartem  * cdutils.h : CD/DVD utilities
4*2912Sartem  *
5*2912Sartem  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
6*2912Sartem  * Use is subject to license terms.
7*2912Sartem  *
8*2912Sartem  * Licensed under the Academic Free License version 2.1
9*2912Sartem  *
10*2912Sartem  **************************************************************************/
11*2912Sartem 
12*2912Sartem #pragma	ident	"%Z%%M%	%I%	%E% SMI"
13*2912Sartem 
14*2912Sartem #ifdef HAVE_CONFIG_H
15*2912Sartem #  include <config.h>
16*2912Sartem #endif
17*2912Sartem 
18*2912Sartem #include <stdio.h>
19*2912Sartem #include <sys/types.h>
20*2912Sartem #include <sys/scsi/impl/uscsi.h>
21*2912Sartem #include <string.h>
22*2912Sartem #include <strings.h>
23*2912Sartem #include <unistd.h>
24*2912Sartem #include <stdlib.h>
25*2912Sartem #include <errno.h>
26*2912Sartem #include <fcntl.h>
27*2912Sartem #include <sys/dkio.h>
28*2912Sartem #include <libintl.h>
29*2912Sartem 
30*2912Sartem #include <logger.h>
31*2912Sartem 
32*2912Sartem #include "cdutils.h"
33*2912Sartem 
34*2912Sartem #define	RQLEN	32
35*2912Sartem #define SENSE_KEY(rqbuf)        (rqbuf[2])      /* scsi error category */
36*2912Sartem #define ASC(rqbuf)              (rqbuf[12])     /* additional sense code */
37*2912Sartem #define ASCQ(rqbuf)             (rqbuf[13])     /* ASC qualifier */
38*2912Sartem 
39*2912Sartem #define	GET16(a) (((a)[0] << 8) | (a)[1])
40*2912Sartem #define	GET32(a) (((a)[0] << 24) | ((a)[1] << 16) | ((a)[2] << 8) | (a)[3])
41*2912Sartem 
42*2912Sartem #define	CD_USCSI_TIMEOUT	60
43*2912Sartem 
44*2912Sartem void
45*2912Sartem uscsi_cmd_init(struct uscsi_cmd *scmd, char *cdb, int cdblen)
46*2912Sartem {
47*2912Sartem 	bzero(scmd, sizeof (*scmd));
48*2912Sartem 	bzero(cdb, cdblen);
49*2912Sartem 	scmd->uscsi_cdb = cdb;
50*2912Sartem }
51*2912Sartem 
52*2912Sartem int
53*2912Sartem uscsi(int fd, struct uscsi_cmd *scmd)
54*2912Sartem {
55*2912Sartem 	char		rqbuf[RQLEN];
56*2912Sartem 	int		ret;
57*2912Sartem 	int		i, retries, total_retries;
58*2912Sartem 	int		max_retries = 20;
59*2912Sartem 
60*2912Sartem 	scmd->uscsi_flags |= USCSI_RQENABLE;
61*2912Sartem 	scmd->uscsi_rqlen = RQLEN;
62*2912Sartem 	scmd->uscsi_rqbuf = rqbuf;
63*2912Sartem 
64*2912Sartem 	for (retries = 0; retries < max_retries; retries++) {
65*2912Sartem 		scmd->uscsi_status = 0;
66*2912Sartem 		memset(rqbuf, 0, RQLEN);
67*2912Sartem 
68*2912Sartem 		ret = ioctl(fd, USCSICMD, scmd);
69*2912Sartem 
70*2912Sartem 		if ((ret == 0) && (scmd->uscsi_status == 2)) {
71*2912Sartem 			ret = -1;
72*2912Sartem 			errno = EIO;
73*2912Sartem 		}
74*2912Sartem 		if ((ret < 0) && (scmd->uscsi_status == 2)) {
75*2912Sartem 			/*
76*2912Sartem 			 * The drive is not ready to recieve commands but
77*2912Sartem 			 * may be in the process of becoming ready.
78*2912Sartem 			 * sleep for a short time then retry command.
79*2912Sartem 			 * SENSE/ASC = 2/4 : not ready
80*2912Sartem 			 * ASCQ = 0  Not Reportable.
81*2912Sartem 			 * ASCQ = 1  Becoming ready.
82*2912Sartem 			 * ASCQ = 4  FORMAT in progress.
83*2912Sartem 			 * ASCQ = 7  Operation in progress.
84*2912Sartem 			 */
85*2912Sartem 			if ((SENSE_KEY(rqbuf) == 2) && (ASC(rqbuf) == 4) &&
86*2912Sartem 			    ((ASCQ(rqbuf) == 0) || (ASCQ(rqbuf) == 1) ||
87*2912Sartem 			    (ASCQ(rqbuf) == 4)) || (ASCQ(rqbuf) == 7)) {
88*2912Sartem 				total_retries++;
89*2912Sartem 				sleep(1);
90*2912Sartem 				continue;
91*2912Sartem 			}
92*2912Sartem 
93*2912Sartem 			/*
94*2912Sartem 			 * Device is not ready to transmit or a device reset
95*2912Sartem 			 * has occurred. wait for a short period of time then
96*2912Sartem 			 * retry the command.
97*2912Sartem 			 */
98*2912Sartem 			if ((SENSE_KEY(rqbuf) == 6) && ((ASC(rqbuf) == 0x28) ||
99*2912Sartem 			    (ASC(rqbuf) == 0x29))) {
100*2912Sartem 				sleep(1);
101*2912Sartem 				total_retries++;
102*2912Sartem 				continue;
103*2912Sartem 			}
104*2912Sartem 			/*
105*2912Sartem 			 * Blank Sense, we don't know what the error is or if
106*2912Sartem 			 * the command succeeded, Hope for the best. Some
107*2912Sartem 			 * drives return blank sense periodically and will
108*2912Sartem 			 * fail if this is removed.
109*2912Sartem 			 */
110*2912Sartem 			if ((SENSE_KEY(rqbuf) == 0) && (ASC(rqbuf) == 0) &&
111*2912Sartem 			    (ASCQ(rqbuf) == 0)) {
112*2912Sartem 				ret = 0;
113*2912Sartem 				break;
114*2912Sartem 			}
115*2912Sartem 
116*2912Sartem 			HAL_DEBUG (("cmd: 0x%02x ret:%i status:%02x "
117*2912Sartem 			    " sense: %02x ASC: %02x ASCQ:%02x\n",
118*2912Sartem 			    (uchar_t)scmd->uscsi_cdb[0], ret,
119*2912Sartem 			    scmd->uscsi_status,
120*2912Sartem 			    (uchar_t)SENSE_KEY(rqbuf),
121*2912Sartem 			    (uchar_t)ASC(rqbuf), (uchar_t)ASCQ(rqbuf)));
122*2912Sartem 		}
123*2912Sartem 
124*2912Sartem 		break;
125*2912Sartem 	}
126*2912Sartem 
127*2912Sartem 	if (retries) {
128*2912Sartem 		HAL_DEBUG (("total retries: %d\n", total_retries));
129*2912Sartem 	}
130*2912Sartem 
131*2912Sartem 	return (ret);
132*2912Sartem }
133*2912Sartem 
134*2912Sartem int
135*2912Sartem mode_sense(int fd, uchar_t pc, int dbd, int page_len, uchar_t *buffer)
136*2912Sartem {
137*2912Sartem 	struct uscsi_cmd scmd;
138*2912Sartem 	char cdb[16];
139*2912Sartem 
140*2912Sartem 	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
141*2912Sartem 	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
142*2912Sartem 	scmd.uscsi_buflen = page_len;
143*2912Sartem 	scmd.uscsi_bufaddr = (char *)buffer;
144*2912Sartem 	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
145*2912Sartem 	scmd.uscsi_cdblen = 0xa;
146*2912Sartem 	scmd.uscsi_cdb[0] = 0x5a; /* MODE SENSE 10 */
147*2912Sartem 	if (dbd) {
148*2912Sartem 		scmd.uscsi_cdb[1] = 0x8; /* no block descriptors */
149*2912Sartem 	}
150*2912Sartem 	scmd.uscsi_cdb[2] = pc;
151*2912Sartem 	scmd.uscsi_cdb[7] = (page_len >> 8) & 0xff;
152*2912Sartem 	scmd.uscsi_cdb[8] = page_len & 0xff;
153*2912Sartem 
154*2912Sartem 	return (uscsi(fd, &scmd) == 0);
155*2912Sartem }
156*2912Sartem 
157*2912Sartem /*
158*2912Sartem  * will get the mode page only i.e. will strip off the header.
159*2912Sartem  */
160*2912Sartem int
161*2912Sartem get_mode_page(int fd, int page_no, int pc, int buf_len, uchar_t *buffer, int *plen)
162*2912Sartem {
163*2912Sartem 	int ret;
164*2912Sartem 	uchar_t byte2;
165*2912Sartem 	uchar_t buf[256];
166*2912Sartem 	uint_t header_len, page_len, copy_cnt;
167*2912Sartem 
168*2912Sartem 	byte2 = (uchar_t)(((pc << 6) & 0xC0) | (page_no & 0x3f));
169*2912Sartem 
170*2912Sartem 	/* Ask 254 bytes only to make our IDE driver happy */
171*2912Sartem 	if ((ret = mode_sense(fd, byte2, 1, 254, buf)) == 0) {
172*2912Sartem 		return (0);
173*2912Sartem 	}
174*2912Sartem 
175*2912Sartem 	header_len = 8 + GET16(&buf[6]);
176*2912Sartem 	page_len = buf[header_len + 1] + 2;
177*2912Sartem 
178*2912Sartem 	copy_cnt = (page_len > buf_len) ? buf_len : page_len;
179*2912Sartem 	(void) memcpy(buffer, &buf[header_len], copy_cnt);
180*2912Sartem 
181*2912Sartem 	if (plen) {
182*2912Sartem 		*plen = page_len;
183*2912Sartem 	}
184*2912Sartem 
185*2912Sartem 	return (1);
186*2912Sartem }
187*2912Sartem 
188*2912Sartem /* Get information about the Logical Unit's capabilities */
189*2912Sartem int
190*2912Sartem get_configuration(int fd, uint16_t feature, int bufsize, uchar_t *buf)
191*2912Sartem {
192*2912Sartem 	struct uscsi_cmd scmd;
193*2912Sartem 	char cdb[16];
194*2912Sartem 
195*2912Sartem 	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
196*2912Sartem 	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
197*2912Sartem 	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
198*2912Sartem 	scmd.uscsi_cdb[0] = 0x46; /* GET CONFIGURATION */
199*2912Sartem 	scmd.uscsi_cdb[1] = 0x2; /* request type */
200*2912Sartem 	scmd.uscsi_cdb[2] = (feature >> 8) & 0xff; /* starting feature # */
201*2912Sartem 	scmd.uscsi_cdb[3] = feature & 0xff;
202*2912Sartem 	scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */
203*2912Sartem 	scmd.uscsi_cdb[8] = bufsize & 0xff;
204*2912Sartem 	scmd.uscsi_cdblen = 10;
205*2912Sartem 	scmd.uscsi_bufaddr = (char *)buf;
206*2912Sartem 	scmd.uscsi_buflen = bufsize;
207*2912Sartem 
208*2912Sartem 	return (uscsi(fd, &scmd) == 0);
209*2912Sartem }
210*2912Sartem 
211*2912Sartem boolean_t
212*2912Sartem get_current_profile(int fd, int *profile)
213*2912Sartem {
214*2912Sartem 	size_t i;
215*2912Sartem 	uchar_t smallbuf[4];
216*2912Sartem 	size_t buflen;
217*2912Sartem 	uchar_t *bufp;
218*2912Sartem 	int ret = B_FALSE;
219*2912Sartem 
220*2912Sartem 	/* first determine amount of memory needed to hold all profiles */
221*2912Sartem 	if (get_configuration(fd, 0, 4, &smallbuf[0])) {
222*2912Sartem 		buflen = GET32(smallbuf) + 4;
223*2912Sartem 		bufp = (uchar_t *)malloc(buflen);
224*2912Sartem 
225*2912Sartem 	 	/* now get all profiles */
226*2912Sartem 		if (get_configuration(fd, 0, buflen, bufp)) {
227*2912Sartem 			*profile = GET16(&bufp[6]);
228*2912Sartem 			ret = B_TRUE;
229*2912Sartem 		}
230*2912Sartem 		free(bufp);
231*2912Sartem 	}
232*2912Sartem 
233*2912Sartem 	return (ret);
234*2912Sartem }
235*2912Sartem 
236*2912Sartem void
237*2912Sartem walk_profiles(int fd, int (*f)(void *, int, boolean_t), void *arg)
238*2912Sartem {
239*2912Sartem 	size_t i;
240*2912Sartem 	uint16_t profile, current_profile;
241*2912Sartem 	uchar_t smallbuf[4];
242*2912Sartem 	size_t buflen;
243*2912Sartem 	uchar_t *bufp;
244*2912Sartem 	int ret;
245*2912Sartem 
246*2912Sartem 	/* first determine amount of memory needed to hold all profiles */
247*2912Sartem 	if (get_configuration(fd, 0, 4, &smallbuf[0])) {
248*2912Sartem 		buflen = GET32(smallbuf) + 4;
249*2912Sartem 		bufp = (uchar_t *)malloc(buflen);
250*2912Sartem 
251*2912Sartem 	 	/* now get all profiles */
252*2912Sartem 		if (get_configuration(fd, 0, buflen, bufp)) {
253*2912Sartem 			current_profile = GET16(&bufp[6]);
254*2912Sartem 			for (i = 8 + 4;  i < buflen; i += 4) {
255*2912Sartem 				profile = GET16(&bufp[i]);
256*2912Sartem 				ret = f(arg, profile, (profile == current_profile));
257*2912Sartem 				if (ret == CDUTIL_WALK_STOP) {
258*2912Sartem 					break;
259*2912Sartem 				}
260*2912Sartem 			}
261*2912Sartem 		}
262*2912Sartem 
263*2912Sartem 		free(bufp);
264*2912Sartem 	}
265*2912Sartem }
266*2912Sartem 
267*2912Sartem /* retrieve speed list from the Write Speed Performance Descriptor Blocks
268*2912Sartem  */
269*2912Sartem void
270*2912Sartem get_write_speeds(uchar_t *page, int n, intlist_t **speeds, int *n_speeds, intlist_t **speeds_mem)
271*2912Sartem {
272*2912Sartem 	uchar_t	*p = page + 2;
273*2912Sartem 	int	i;
274*2912Sartem 	intlist_t **nextp;
275*2912Sartem 	intlist_t *current;
276*2912Sartem 	boolean_t skip;
277*2912Sartem 
278*2912Sartem 	*n_speeds = 0;
279*2912Sartem 	*speeds = NULL;
280*2912Sartem 	*speeds_mem = (intlist_t *)calloc(n, sizeof (intlist_t));
281*2912Sartem 	if (*speeds_mem == NULL) {
282*2912Sartem 		return;
283*2912Sartem 	}
284*2912Sartem 
285*2912Sartem 	for (i = 0; i < n; i++, p += 4) {
286*2912Sartem 		current = &(*speeds_mem)[i];
287*2912Sartem 		current->val = GET16(p);
288*2912Sartem 
289*2912Sartem 		/* keep the list sorted */
290*2912Sartem 		skip = B_FALSE;
291*2912Sartem 		for (nextp = speeds; *nextp != NULL; nextp = &((*nextp)->next)) {
292*2912Sartem 			if (current->val == (*nextp)->val) {
293*2912Sartem 				skip = B_TRUE; /* skip duplicates */
294*2912Sartem 				break;
295*2912Sartem 			} else if (current->val > (*nextp)->val) {
296*2912Sartem 				break;
297*2912Sartem 			}
298*2912Sartem 		}
299*2912Sartem 		if (!skip) {
300*2912Sartem 			current->next = *nextp;
301*2912Sartem 			*nextp = current;
302*2912Sartem 			*n_speeds++;
303*2912Sartem 		}
304*2912Sartem 	}
305*2912Sartem }
306*2912Sartem 
307*2912Sartem void
308*2912Sartem get_read_write_speeds(int fd, int *read_speed, int *write_speed,
309*2912Sartem     intlist_t **speeds, int *n_speeds, intlist_t **speeds_mem)
310*2912Sartem {
311*2912Sartem 	int page_len;
312*2912Sartem 	uchar_t	p[254];
313*2912Sartem 	int n; /* number of write speed performance descriptor blocks */
314*2912Sartem 
315*2912Sartem 	*read_speed = *write_speed = 0;
316*2912Sartem 	*speeds = *speeds_mem = NULL;
317*2912Sartem 
318*2912Sartem 	if (!get_mode_page(fd, 0x2A, 0, sizeof (p), p, &page_len)) {
319*2912Sartem 		return;
320*2912Sartem 	}
321*2912Sartem 
322*2912Sartem 	if (page_len > 8) {
323*2912Sartem 		*read_speed = GET16(&p[8]);
324*2912Sartem 	}
325*2912Sartem 	if (page_len > 18) {
326*2912Sartem 		*write_speed = GET16(&p[18]);
327*2912Sartem 	}
328*2912Sartem 	if (page_len < 28) {
329*2912Sartem 		printf("MMC-2\n");
330*2912Sartem 		return;
331*2912Sartem 	} else {
332*2912Sartem 		printf("MMC-3\n");
333*2912Sartem 	}
334*2912Sartem 
335*2912Sartem 	*write_speed = GET16(&p[28]);
336*2912Sartem 
337*2912Sartem 	if (page_len < 30) {
338*2912Sartem 		return;
339*2912Sartem 	}
340*2912Sartem 
341*2912Sartem 	/* retrieve speed list */
342*2912Sartem 	n = GET16(&p[30]);
343*2912Sartem 	n = min(n, (sizeof (p) - 32) / 4);
344*2912Sartem 
345*2912Sartem 	get_write_speeds(&p[32], n, speeds, n_speeds, speeds_mem);
346*2912Sartem 
347*2912Sartem 	if (*speeds != NULL) {
348*2912Sartem 		*write_speed = max(*write_speed, (*speeds)[0].val);
349*2912Sartem 	}
350*2912Sartem }
351*2912Sartem 
352*2912Sartem boolean_t
353*2912Sartem get_disc_info(int fd, disc_info_t *di)
354*2912Sartem {
355*2912Sartem 	struct uscsi_cmd scmd;
356*2912Sartem 	char cdb[16];
357*2912Sartem 	uint8_t	buf[32];
358*2912Sartem 	int bufsize = sizeof (buf);
359*2912Sartem 
360*2912Sartem 	bzero(buf, bufsize);
361*2912Sartem 	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
362*2912Sartem 	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
363*2912Sartem 	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
364*2912Sartem 	scmd.uscsi_cdb[0] = 0x51; /* READ DISC INFORMATION */
365*2912Sartem 	scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */
366*2912Sartem 	scmd.uscsi_cdb[8] = bufsize & 0xff;
367*2912Sartem 	scmd.uscsi_cdblen = 10;
368*2912Sartem 	scmd.uscsi_bufaddr = (char *)buf;
369*2912Sartem 	scmd.uscsi_buflen = bufsize;
370*2912Sartem 
371*2912Sartem 	if ((uscsi(fd, &scmd)) != 0) {
372*2912Sartem 		return (B_FALSE);
373*2912Sartem 	}
374*2912Sartem 
375*2912Sartem 	di->disc_status = buf[2] & 0x03;
376*2912Sartem 	di->erasable = buf[2] & 0x10;
377*2912Sartem 	if ((buf[21] != 0) && (buf[21] != 0xff)) {
378*2912Sartem 		di->capacity = ((buf[21] * 60) + buf[22]) * 75;
379*2912Sartem 	} else {
380*2912Sartem 		di->capacity = 0;
381*2912Sartem         }
382*2912Sartem 
383*2912Sartem 	return (B_TRUE);
384*2912Sartem }
385*2912Sartem 
386*2912Sartem /*
387*2912Sartem  * returns current/maximum format capacity in bytes
388*2912Sartem  */
389*2912Sartem boolean_t
390*2912Sartem read_format_capacity(int fd, uint64_t *capacity)
391*2912Sartem {
392*2912Sartem 	struct uscsi_cmd scmd;
393*2912Sartem 	char cdb[16];
394*2912Sartem 	uint8_t	buf[32];
395*2912Sartem 	int bufsize = sizeof (buf);
396*2912Sartem 	uint32_t num_blocks;
397*2912Sartem 	uint32_t block_len;
398*2912Sartem 
399*2912Sartem 	bzero(buf, bufsize);
400*2912Sartem 	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
401*2912Sartem 	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
402*2912Sartem 	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
403*2912Sartem 	scmd.uscsi_cdb[0] = 0x23; /* READ FORMAT CAPACITIRES */
404*2912Sartem 	scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */
405*2912Sartem 	scmd.uscsi_cdb[8] = bufsize & 0xff;
406*2912Sartem 	scmd.uscsi_cdblen = 12;
407*2912Sartem 	scmd.uscsi_bufaddr = (char *)buf;
408*2912Sartem 	scmd.uscsi_buflen = bufsize;
409*2912Sartem 
410*2912Sartem 	if ((uscsi(fd, &scmd)) != 0) {
411*2912Sartem 		return (B_FALSE);
412*2912Sartem 	}
413*2912Sartem 
414*2912Sartem 	num_blocks = (uint32_t)(buf[4] << 24) + (buf[5] << 16) + (buf[6] << 8) + buf[7];
415*2912Sartem 	block_len = (uint32_t)(buf[9] << 16) + (buf[10] << 8) + buf[11];
416*2912Sartem 	*capacity = (uint64_t)num_blocks * block_len;
417*2912Sartem 
418*2912Sartem 	return (B_TRUE);
419*2912Sartem }
420*2912Sartem 
421*2912Sartem boolean_t
422*2912Sartem get_media_info(int fd, struct dk_minfo *minfop)
423*2912Sartem {
424*2912Sartem 	return (ioctl(fd, DKIOCGMEDIAINFO, minfop) != -1);
425*2912Sartem }
426*2912Sartem 
427*2912Sartem /*
428*2912Sartem  * given current profile, use the best method for determining
429*2912Sartem  * disc capacity (in bytes)
430*2912Sartem  */
431*2912Sartem boolean_t
432*2912Sartem get_disc_capacity_for_profile(int fd, int profile, uint64_t *capacity)
433*2912Sartem {
434*2912Sartem 	struct dk_minfo	mi;
435*2912Sartem 	disc_info_t	di;
436*2912Sartem 	boolean_t	ret = B_FALSE;
437*2912Sartem 
438*2912Sartem 	switch (profile) {
439*2912Sartem 	case 0x08: /* CD-ROM */
440*2912Sartem 	case 0x10: /* DVD-ROM */
441*2912Sartem 		if (get_media_info(fd, &mi) && (mi.dki_capacity > 1)) {
442*2912Sartem 			*capacity = mi.dki_capacity * mi.dki_lbsize;
443*2912Sartem 			ret = B_TRUE;
444*2912Sartem 		}
445*2912Sartem 		break;
446*2912Sartem 	default:
447*2912Sartem 		if (read_format_capacity(fd, capacity) && (*capacity > 0)) {
448*2912Sartem 			ret = B_TRUE;
449*2912Sartem 		} else if (get_disc_info(fd, &di) && (di.capacity > 0)) {
450*2912Sartem 			if (get_media_info(fd, &mi)) {
451*2912Sartem 				*capacity = di.capacity * mi.dki_lbsize;
452*2912Sartem 				ret = B_TRUE;
453*2912Sartem 			}
454*2912Sartem 		}
455*2912Sartem 	}
456*2912Sartem 
457*2912Sartem 	return (ret);
458*2912Sartem }
459*2912Sartem 
460*2912Sartem boolean_t
461*2912Sartem read_toc(int fd, int format, int trackno, int buflen, uchar_t *buf)
462*2912Sartem {
463*2912Sartem 	struct uscsi_cmd scmd;
464*2912Sartem 	char cdb[16];
465*2912Sartem 
466*2912Sartem 	bzero(buf, buflen);
467*2912Sartem 	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
468*2912Sartem 	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
469*2912Sartem 	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
470*2912Sartem 	scmd.uscsi_cdb[0] = 0x43 /* READ_TOC_CMD */;
471*2912Sartem 	scmd.uscsi_cdb[2] = format & 0xf;
472*2912Sartem 	scmd.uscsi_cdb[6] = trackno;
473*2912Sartem 	scmd.uscsi_cdb[8] = buflen & 0xff;
474*2912Sartem 	scmd.uscsi_cdb[7] = (buflen >> 8) & 0xff;
475*2912Sartem 	scmd.uscsi_cdblen = 10;
476*2912Sartem 	scmd.uscsi_bufaddr = (char *)buf;
477*2912Sartem 	scmd.uscsi_buflen = buflen;
478*2912Sartem 
479*2912Sartem 	if ((uscsi(fd, &scmd)) != 0) {
480*2912Sartem         	return (B_FALSE);
481*2912Sartem 	}
482*2912Sartem 
483*2912Sartem 	return (B_TRUE);
484*2912Sartem }
485