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