xref: /titanic_51/usr/src/cmd/hal/utils/cdutils.c (revision 8cd4c22636ceaa8d2260f00cb5cedfd1d249fab9)
118c2aff7Sartem /***************************************************************************
218c2aff7Sartem  *
3e6996a4dSartem  * cdutils.c : CD/DVD utilities
418c2aff7Sartem  *
5*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
618c2aff7Sartem  * Use is subject to license terms.
718c2aff7Sartem  *
818c2aff7Sartem  * Licensed under the Academic Free License version 2.1
918c2aff7Sartem  *
1018c2aff7Sartem  **************************************************************************/
1118c2aff7Sartem 
1218c2aff7Sartem 
1318c2aff7Sartem #ifdef HAVE_CONFIG_H
1418c2aff7Sartem #  include <config.h>
1518c2aff7Sartem #endif
1618c2aff7Sartem 
1718c2aff7Sartem #include <stdio.h>
1818c2aff7Sartem #include <sys/types.h>
1918c2aff7Sartem #include <sys/scsi/impl/uscsi.h>
2018c2aff7Sartem #include <string.h>
2118c2aff7Sartem #include <strings.h>
2218c2aff7Sartem #include <unistd.h>
2318c2aff7Sartem #include <stdlib.h>
2418c2aff7Sartem #include <errno.h>
2518c2aff7Sartem #include <fcntl.h>
2618c2aff7Sartem #include <sys/dkio.h>
2718c2aff7Sartem #include <libintl.h>
2818c2aff7Sartem 
2918c2aff7Sartem #include <logger.h>
3018c2aff7Sartem 
3118c2aff7Sartem #include "cdutils.h"
3218c2aff7Sartem 
3318c2aff7Sartem #define	RQLEN	32
3418c2aff7Sartem #define SENSE_KEY(rqbuf)        (rqbuf[2])      /* scsi error category */
3518c2aff7Sartem #define ASC(rqbuf)              (rqbuf[12])     /* additional sense code */
3618c2aff7Sartem #define ASCQ(rqbuf)             (rqbuf[13])     /* ASC qualifier */
3718c2aff7Sartem 
3818c2aff7Sartem #define	GET16(a) (((a)[0] << 8) | (a)[1])
3918c2aff7Sartem #define	GET32(a) (((a)[0] << 24) | ((a)[1] << 16) | ((a)[2] << 8) | (a)[3])
4018c2aff7Sartem 
4118c2aff7Sartem #define	CD_USCSI_TIMEOUT	60
4218c2aff7Sartem 
4318c2aff7Sartem void
4418c2aff7Sartem uscsi_cmd_init(struct uscsi_cmd *scmd, char *cdb, int cdblen)
4518c2aff7Sartem {
4618c2aff7Sartem 	bzero(scmd, sizeof (*scmd));
4718c2aff7Sartem 	bzero(cdb, cdblen);
4818c2aff7Sartem 	scmd->uscsi_cdb = cdb;
4918c2aff7Sartem }
5018c2aff7Sartem 
5118c2aff7Sartem int
5218c2aff7Sartem uscsi(int fd, struct uscsi_cmd *scmd)
5318c2aff7Sartem {
5418c2aff7Sartem 	char		rqbuf[RQLEN];
5518c2aff7Sartem 	int		ret;
5618c2aff7Sartem 	int		i, retries, total_retries;
5718c2aff7Sartem 	int		max_retries = 20;
5818c2aff7Sartem 
5918c2aff7Sartem 	scmd->uscsi_flags |= USCSI_RQENABLE;
6018c2aff7Sartem 	scmd->uscsi_rqlen = RQLEN;
6118c2aff7Sartem 	scmd->uscsi_rqbuf = rqbuf;
6218c2aff7Sartem 
6318c2aff7Sartem 	for (retries = 0; retries < max_retries; retries++) {
6418c2aff7Sartem 		scmd->uscsi_status = 0;
6518c2aff7Sartem 		memset(rqbuf, 0, RQLEN);
6618c2aff7Sartem 
6718c2aff7Sartem 		ret = ioctl(fd, USCSICMD, scmd);
6818c2aff7Sartem 
6918c2aff7Sartem 		if ((ret == 0) && (scmd->uscsi_status == 2)) {
7018c2aff7Sartem 			ret = -1;
7118c2aff7Sartem 			errno = EIO;
7218c2aff7Sartem 		}
7318c2aff7Sartem 		if ((ret < 0) && (scmd->uscsi_status == 2)) {
7418c2aff7Sartem 			/*
7518c2aff7Sartem 			 * The drive is not ready to recieve commands but
7618c2aff7Sartem 			 * may be in the process of becoming ready.
7718c2aff7Sartem 			 * sleep for a short time then retry command.
7818c2aff7Sartem 			 * SENSE/ASC = 2/4 : not ready
7918c2aff7Sartem 			 * ASCQ = 0  Not Reportable.
8018c2aff7Sartem 			 * ASCQ = 1  Becoming ready.
8118c2aff7Sartem 			 * ASCQ = 4  FORMAT in progress.
8218c2aff7Sartem 			 * ASCQ = 7  Operation in progress.
8318c2aff7Sartem 			 */
8418c2aff7Sartem 			if ((SENSE_KEY(rqbuf) == 2) && (ASC(rqbuf) == 4) &&
8518c2aff7Sartem 			    ((ASCQ(rqbuf) == 0) || (ASCQ(rqbuf) == 1) ||
8618c2aff7Sartem 			    (ASCQ(rqbuf) == 4)) || (ASCQ(rqbuf) == 7)) {
8718c2aff7Sartem 				total_retries++;
8818c2aff7Sartem 				sleep(1);
8918c2aff7Sartem 				continue;
9018c2aff7Sartem 			}
9118c2aff7Sartem 
9218c2aff7Sartem 			/*
9318c2aff7Sartem 			 * Device is not ready to transmit or a device reset
9418c2aff7Sartem 			 * has occurred. wait for a short period of time then
9518c2aff7Sartem 			 * retry the command.
9618c2aff7Sartem 			 */
9718c2aff7Sartem 			if ((SENSE_KEY(rqbuf) == 6) && ((ASC(rqbuf) == 0x28) ||
9818c2aff7Sartem 			    (ASC(rqbuf) == 0x29))) {
9918c2aff7Sartem 				sleep(1);
10018c2aff7Sartem 				total_retries++;
10118c2aff7Sartem 				continue;
10218c2aff7Sartem 			}
10318c2aff7Sartem 			/*
10418c2aff7Sartem 			 * Blank Sense, we don't know what the error is or if
10518c2aff7Sartem 			 * the command succeeded, Hope for the best. Some
10618c2aff7Sartem 			 * drives return blank sense periodically and will
10718c2aff7Sartem 			 * fail if this is removed.
10818c2aff7Sartem 			 */
10918c2aff7Sartem 			if ((SENSE_KEY(rqbuf) == 0) && (ASC(rqbuf) == 0) &&
11018c2aff7Sartem 			    (ASCQ(rqbuf) == 0)) {
11118c2aff7Sartem 				ret = 0;
11218c2aff7Sartem 				break;
11318c2aff7Sartem 			}
11418c2aff7Sartem 
11518c2aff7Sartem 			HAL_DEBUG (("cmd: 0x%02x ret:%i status:%02x "
11618c2aff7Sartem 			    " sense: %02x ASC: %02x ASCQ:%02x\n",
11718c2aff7Sartem 			    (uchar_t)scmd->uscsi_cdb[0], ret,
11818c2aff7Sartem 			    scmd->uscsi_status,
11918c2aff7Sartem 			    (uchar_t)SENSE_KEY(rqbuf),
12018c2aff7Sartem 			    (uchar_t)ASC(rqbuf), (uchar_t)ASCQ(rqbuf)));
12118c2aff7Sartem 		}
12218c2aff7Sartem 
12318c2aff7Sartem 		break;
12418c2aff7Sartem 	}
12518c2aff7Sartem 
12618c2aff7Sartem 	if (retries) {
12718c2aff7Sartem 		HAL_DEBUG (("total retries: %d\n", total_retries));
12818c2aff7Sartem 	}
12918c2aff7Sartem 
13018c2aff7Sartem 	return (ret);
13118c2aff7Sartem }
13218c2aff7Sartem 
13318c2aff7Sartem int
13418c2aff7Sartem mode_sense(int fd, uchar_t pc, int dbd, int page_len, uchar_t *buffer)
13518c2aff7Sartem {
13618c2aff7Sartem 	struct uscsi_cmd scmd;
13718c2aff7Sartem 	char cdb[16];
13818c2aff7Sartem 
13918c2aff7Sartem 	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
14018c2aff7Sartem 	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
14118c2aff7Sartem 	scmd.uscsi_buflen = page_len;
14218c2aff7Sartem 	scmd.uscsi_bufaddr = (char *)buffer;
14318c2aff7Sartem 	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
14418c2aff7Sartem 	scmd.uscsi_cdblen = 0xa;
14518c2aff7Sartem 	scmd.uscsi_cdb[0] = 0x5a; /* MODE SENSE 10 */
14618c2aff7Sartem 	if (dbd) {
14718c2aff7Sartem 		scmd.uscsi_cdb[1] = 0x8; /* no block descriptors */
14818c2aff7Sartem 	}
14918c2aff7Sartem 	scmd.uscsi_cdb[2] = pc;
15018c2aff7Sartem 	scmd.uscsi_cdb[7] = (page_len >> 8) & 0xff;
15118c2aff7Sartem 	scmd.uscsi_cdb[8] = page_len & 0xff;
15218c2aff7Sartem 
15318c2aff7Sartem 	return (uscsi(fd, &scmd) == 0);
15418c2aff7Sartem }
15518c2aff7Sartem 
15618c2aff7Sartem /*
15718c2aff7Sartem  * will get the mode page only i.e. will strip off the header.
15818c2aff7Sartem  */
15918c2aff7Sartem int
16018c2aff7Sartem get_mode_page(int fd, int page_no, int pc, int buf_len, uchar_t *buffer, int *plen)
16118c2aff7Sartem {
16218c2aff7Sartem 	int ret;
16318c2aff7Sartem 	uchar_t byte2;
16418c2aff7Sartem 	uchar_t buf[256];
16518c2aff7Sartem 	uint_t header_len, page_len, copy_cnt;
16618c2aff7Sartem 
16718c2aff7Sartem 	byte2 = (uchar_t)(((pc << 6) & 0xC0) | (page_no & 0x3f));
16818c2aff7Sartem 
16918c2aff7Sartem 	/* Ask 254 bytes only to make our IDE driver happy */
17018c2aff7Sartem 	if ((ret = mode_sense(fd, byte2, 1, 254, buf)) == 0) {
17118c2aff7Sartem 		return (0);
17218c2aff7Sartem 	}
17318c2aff7Sartem 
17418c2aff7Sartem 	header_len = 8 + GET16(&buf[6]);
17518c2aff7Sartem 	page_len = buf[header_len + 1] + 2;
17618c2aff7Sartem 
17718c2aff7Sartem 	copy_cnt = (page_len > buf_len) ? buf_len : page_len;
17818c2aff7Sartem 	(void) memcpy(buffer, &buf[header_len], copy_cnt);
17918c2aff7Sartem 
18018c2aff7Sartem 	if (plen) {
18118c2aff7Sartem 		*plen = page_len;
18218c2aff7Sartem 	}
18318c2aff7Sartem 
18418c2aff7Sartem 	return (1);
18518c2aff7Sartem }
18618c2aff7Sartem 
18718c2aff7Sartem /* Get information about the Logical Unit's capabilities */
18818c2aff7Sartem int
18918c2aff7Sartem get_configuration(int fd, uint16_t feature, int bufsize, uchar_t *buf)
19018c2aff7Sartem {
19118c2aff7Sartem 	struct uscsi_cmd scmd;
19218c2aff7Sartem 	char cdb[16];
19318c2aff7Sartem 
19418c2aff7Sartem 	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
19518c2aff7Sartem 	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
19618c2aff7Sartem 	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
19718c2aff7Sartem 	scmd.uscsi_cdb[0] = 0x46; /* GET CONFIGURATION */
19818c2aff7Sartem 	scmd.uscsi_cdb[1] = 0x2; /* request type */
19918c2aff7Sartem 	scmd.uscsi_cdb[2] = (feature >> 8) & 0xff; /* starting feature # */
20018c2aff7Sartem 	scmd.uscsi_cdb[3] = feature & 0xff;
20118c2aff7Sartem 	scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */
20218c2aff7Sartem 	scmd.uscsi_cdb[8] = bufsize & 0xff;
20318c2aff7Sartem 	scmd.uscsi_cdblen = 10;
20418c2aff7Sartem 	scmd.uscsi_bufaddr = (char *)buf;
20518c2aff7Sartem 	scmd.uscsi_buflen = bufsize;
20618c2aff7Sartem 
20718c2aff7Sartem 	return (uscsi(fd, &scmd) == 0);
20818c2aff7Sartem }
20918c2aff7Sartem 
21018c2aff7Sartem boolean_t
21118c2aff7Sartem get_current_profile(int fd, int *profile)
21218c2aff7Sartem {
21318c2aff7Sartem 	size_t i;
214*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	uchar_t smallbuf[8];
21518c2aff7Sartem 	size_t buflen;
21618c2aff7Sartem 	uchar_t *bufp;
21718c2aff7Sartem 	int ret = B_FALSE;
21818c2aff7Sartem 
219*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	/*
220*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	 * first determine amount of memory needed to hold all profiles.
221*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	 * The first four bytes of smallbuf concatenated tell us the
222*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	 * number of bytes of memory we need but do not take themselves
223*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	 * into account. Therefore, add four to allocate that number
224*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	 * of bytes.
225*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	 */
226*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	if (get_configuration(fd, 0, 8, &smallbuf[0])) {
22718c2aff7Sartem 		buflen = GET32(smallbuf) + 4;
22818c2aff7Sartem 		bufp = (uchar_t *)malloc(buflen);
22918c2aff7Sartem 
23018c2aff7Sartem 	 	/* now get all profiles */
23118c2aff7Sartem 		if (get_configuration(fd, 0, buflen, bufp)) {
23218c2aff7Sartem 			*profile = GET16(&bufp[6]);
23318c2aff7Sartem 			ret = B_TRUE;
23418c2aff7Sartem 		}
23518c2aff7Sartem 		free(bufp);
23618c2aff7Sartem 	}
23718c2aff7Sartem 
23818c2aff7Sartem 	return (ret);
23918c2aff7Sartem }
24018c2aff7Sartem 
24118c2aff7Sartem void
24218c2aff7Sartem walk_profiles(int fd, int (*f)(void *, int, boolean_t), void *arg)
24318c2aff7Sartem {
24418c2aff7Sartem 	size_t i;
24518c2aff7Sartem 	uint16_t profile, current_profile;
246*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	uchar_t smallbuf[8];
24718c2aff7Sartem 	size_t buflen;
24818c2aff7Sartem 	uchar_t *bufp;
24918c2aff7Sartem 	int ret;
25018c2aff7Sartem 
251*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	/*
252*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	 * first determine amount of memory needed to hold all profiles.
253*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	 * The first four bytes of smallbuf concatenated tell us the
254*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	 * number of bytes of memory we need but do not take themselves
255*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	 * into account. Therefore, add four to allocate that number
256*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	 * of bytes.
257*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	 */
258*8cd4c226SXiaolin Zhang - Sun Microsystems - Beijing China 	if (get_configuration(fd, 0, 8, &smallbuf[0])) {
25918c2aff7Sartem 		buflen = GET32(smallbuf) + 4;
26018c2aff7Sartem 		bufp = (uchar_t *)malloc(buflen);
26118c2aff7Sartem 
26218c2aff7Sartem 	 	/* now get all profiles */
26318c2aff7Sartem 		if (get_configuration(fd, 0, buflen, bufp)) {
26418c2aff7Sartem 			current_profile = GET16(&bufp[6]);
26518c2aff7Sartem 			for (i = 8 + 4;  i < buflen; i += 4) {
26618c2aff7Sartem 				profile = GET16(&bufp[i]);
26718c2aff7Sartem 				ret = f(arg, profile, (profile == current_profile));
26818c2aff7Sartem 				if (ret == CDUTIL_WALK_STOP) {
26918c2aff7Sartem 					break;
27018c2aff7Sartem 				}
27118c2aff7Sartem 			}
27218c2aff7Sartem 		}
27318c2aff7Sartem 
27418c2aff7Sartem 		free(bufp);
27518c2aff7Sartem 	}
27618c2aff7Sartem }
27718c2aff7Sartem 
27818c2aff7Sartem /* retrieve speed list from the Write Speed Performance Descriptor Blocks
27918c2aff7Sartem  */
28018c2aff7Sartem void
28118c2aff7Sartem get_write_speeds(uchar_t *page, int n, intlist_t **speeds, int *n_speeds, intlist_t **speeds_mem)
28218c2aff7Sartem {
28318c2aff7Sartem 	uchar_t	*p = page + 2;
28418c2aff7Sartem 	int	i;
28518c2aff7Sartem 	intlist_t **nextp;
28618c2aff7Sartem 	intlist_t *current;
28718c2aff7Sartem 	boolean_t skip;
28818c2aff7Sartem 
28918c2aff7Sartem 	*n_speeds = 0;
29018c2aff7Sartem 	*speeds = NULL;
29118c2aff7Sartem 	*speeds_mem = (intlist_t *)calloc(n, sizeof (intlist_t));
29218c2aff7Sartem 	if (*speeds_mem == NULL) {
29318c2aff7Sartem 		return;
29418c2aff7Sartem 	}
29518c2aff7Sartem 
29618c2aff7Sartem 	for (i = 0; i < n; i++, p += 4) {
29718c2aff7Sartem 		current = &(*speeds_mem)[i];
29818c2aff7Sartem 		current->val = GET16(p);
29918c2aff7Sartem 
30018c2aff7Sartem 		/* keep the list sorted */
30118c2aff7Sartem 		skip = B_FALSE;
30218c2aff7Sartem 		for (nextp = speeds; *nextp != NULL; nextp = &((*nextp)->next)) {
30318c2aff7Sartem 			if (current->val == (*nextp)->val) {
30418c2aff7Sartem 				skip = B_TRUE; /* skip duplicates */
30518c2aff7Sartem 				break;
30618c2aff7Sartem 			} else if (current->val > (*nextp)->val) {
30718c2aff7Sartem 				break;
30818c2aff7Sartem 			}
30918c2aff7Sartem 		}
31018c2aff7Sartem 		if (!skip) {
31118c2aff7Sartem 			current->next = *nextp;
31218c2aff7Sartem 			*nextp = current;
31318c2aff7Sartem 			*n_speeds++;
31418c2aff7Sartem 		}
31518c2aff7Sartem 	}
31618c2aff7Sartem }
31718c2aff7Sartem 
31818c2aff7Sartem void
31918c2aff7Sartem get_read_write_speeds(int fd, int *read_speed, int *write_speed,
32018c2aff7Sartem     intlist_t **speeds, int *n_speeds, intlist_t **speeds_mem)
32118c2aff7Sartem {
32218c2aff7Sartem 	int page_len;
32318c2aff7Sartem 	uchar_t	p[254];
32418c2aff7Sartem 	int n; /* number of write speed performance descriptor blocks */
32518c2aff7Sartem 
32618c2aff7Sartem 	*read_speed = *write_speed = 0;
32718c2aff7Sartem 	*speeds = *speeds_mem = NULL;
32818c2aff7Sartem 
32918c2aff7Sartem 	if (!get_mode_page(fd, 0x2A, 0, sizeof (p), p, &page_len)) {
33018c2aff7Sartem 		return;
33118c2aff7Sartem 	}
33218c2aff7Sartem 
33318c2aff7Sartem 	if (page_len > 8) {
33418c2aff7Sartem 		*read_speed = GET16(&p[8]);
33518c2aff7Sartem 	}
33618c2aff7Sartem 	if (page_len > 18) {
33718c2aff7Sartem 		*write_speed = GET16(&p[18]);
33818c2aff7Sartem 	}
33918c2aff7Sartem 	if (page_len < 28) {
34018c2aff7Sartem 		printf("MMC-2\n");
34118c2aff7Sartem 		return;
34218c2aff7Sartem 	} else {
34318c2aff7Sartem 		printf("MMC-3\n");
34418c2aff7Sartem 	}
34518c2aff7Sartem 
34618c2aff7Sartem 	*write_speed = GET16(&p[28]);
34718c2aff7Sartem 
34818c2aff7Sartem 	if (page_len < 30) {
34918c2aff7Sartem 		return;
35018c2aff7Sartem 	}
35118c2aff7Sartem 
35218c2aff7Sartem 	/* retrieve speed list */
35318c2aff7Sartem 	n = GET16(&p[30]);
35418c2aff7Sartem 	n = min(n, (sizeof (p) - 32) / 4);
35518c2aff7Sartem 
35618c2aff7Sartem 	get_write_speeds(&p[32], n, speeds, n_speeds, speeds_mem);
35718c2aff7Sartem 
35818c2aff7Sartem 	if (*speeds != NULL) {
35918c2aff7Sartem 		*write_speed = max(*write_speed, (*speeds)[0].val);
36018c2aff7Sartem 	}
36118c2aff7Sartem }
36218c2aff7Sartem 
36318c2aff7Sartem boolean_t
36418c2aff7Sartem get_disc_info(int fd, disc_info_t *di)
36518c2aff7Sartem {
36618c2aff7Sartem 	struct uscsi_cmd scmd;
36718c2aff7Sartem 	char cdb[16];
36818c2aff7Sartem 	uint8_t	buf[32];
36918c2aff7Sartem 	int bufsize = sizeof (buf);
37018c2aff7Sartem 
37118c2aff7Sartem 	bzero(buf, bufsize);
37218c2aff7Sartem 	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
37318c2aff7Sartem 	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
37418c2aff7Sartem 	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
37518c2aff7Sartem 	scmd.uscsi_cdb[0] = 0x51; /* READ DISC INFORMATION */
37618c2aff7Sartem 	scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */
37718c2aff7Sartem 	scmd.uscsi_cdb[8] = bufsize & 0xff;
37818c2aff7Sartem 	scmd.uscsi_cdblen = 10;
37918c2aff7Sartem 	scmd.uscsi_bufaddr = (char *)buf;
38018c2aff7Sartem 	scmd.uscsi_buflen = bufsize;
38118c2aff7Sartem 
38218c2aff7Sartem 	if ((uscsi(fd, &scmd)) != 0) {
38318c2aff7Sartem 		return (B_FALSE);
38418c2aff7Sartem 	}
38518c2aff7Sartem 
386e6996a4dSartem 	/*
387e6996a4dSartem 	 * According to MMC-5 6.22.3.2, the Disc Information Length should be
388e6996a4dSartem 	 * 32+8*(Number of OPC Tables). Some devices, like U3 sticks, return 0.
389771e4261Sartem 	 * Yet some drives can return less than 32. We only need the first 22.
390e6996a4dSartem 	 */
391771e4261Sartem 	if (GET16(&buf[0]) < 22) {
392e6996a4dSartem 		return (B_FALSE);
393e6996a4dSartem 	}
394e6996a4dSartem 
39518c2aff7Sartem 	di->disc_status = buf[2] & 0x03;
39618c2aff7Sartem 	di->erasable = buf[2] & 0x10;
39718c2aff7Sartem 	if ((buf[21] != 0) && (buf[21] != 0xff)) {
39818c2aff7Sartem 		di->capacity = ((buf[21] * 60) + buf[22]) * 75;
39918c2aff7Sartem 	} else {
40018c2aff7Sartem 		di->capacity = 0;
40118c2aff7Sartem 	}
40218c2aff7Sartem 
40318c2aff7Sartem 	return (B_TRUE);
40418c2aff7Sartem }
40518c2aff7Sartem 
40618c2aff7Sartem /*
40718c2aff7Sartem  * returns current/maximum format capacity in bytes
40818c2aff7Sartem  */
40918c2aff7Sartem boolean_t
41018c2aff7Sartem read_format_capacity(int fd, uint64_t *capacity)
41118c2aff7Sartem {
41218c2aff7Sartem 	struct uscsi_cmd scmd;
41318c2aff7Sartem 	char cdb[16];
41418c2aff7Sartem 	uint8_t	buf[32];
41518c2aff7Sartem 	int bufsize = sizeof (buf);
41618c2aff7Sartem 	uint32_t num_blocks;
41718c2aff7Sartem 	uint32_t block_len;
41818c2aff7Sartem 
41918c2aff7Sartem 	bzero(buf, bufsize);
42018c2aff7Sartem 	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
42118c2aff7Sartem 	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
42218c2aff7Sartem 	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
42318c2aff7Sartem 	scmd.uscsi_cdb[0] = 0x23; /* READ FORMAT CAPACITIRES */
42418c2aff7Sartem 	scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */
42518c2aff7Sartem 	scmd.uscsi_cdb[8] = bufsize & 0xff;
42618c2aff7Sartem 	scmd.uscsi_cdblen = 12;
42718c2aff7Sartem 	scmd.uscsi_bufaddr = (char *)buf;
42818c2aff7Sartem 	scmd.uscsi_buflen = bufsize;
42918c2aff7Sartem 
43018c2aff7Sartem 	if ((uscsi(fd, &scmd)) != 0) {
43118c2aff7Sartem 		return (B_FALSE);
43218c2aff7Sartem 	}
43318c2aff7Sartem 
43418c2aff7Sartem 	num_blocks = (uint32_t)(buf[4] << 24) + (buf[5] << 16) + (buf[6] << 8) + buf[7];
43518c2aff7Sartem 	block_len = (uint32_t)(buf[9] << 16) + (buf[10] << 8) + buf[11];
43618c2aff7Sartem 	*capacity = (uint64_t)num_blocks * block_len;
43718c2aff7Sartem 
43818c2aff7Sartem 	return (B_TRUE);
43918c2aff7Sartem }
44018c2aff7Sartem 
44118c2aff7Sartem boolean_t
44218c2aff7Sartem get_media_info(int fd, struct dk_minfo *minfop)
44318c2aff7Sartem {
44418c2aff7Sartem 	return (ioctl(fd, DKIOCGMEDIAINFO, minfop) != -1);
44518c2aff7Sartem }
44618c2aff7Sartem 
44718c2aff7Sartem /*
44818c2aff7Sartem  * given current profile, use the best method for determining
44918c2aff7Sartem  * disc capacity (in bytes)
45018c2aff7Sartem  */
45118c2aff7Sartem boolean_t
45218c2aff7Sartem get_disc_capacity_for_profile(int fd, int profile, uint64_t *capacity)
45318c2aff7Sartem {
45418c2aff7Sartem 	struct dk_minfo	mi;
45518c2aff7Sartem 	disc_info_t	di;
45618c2aff7Sartem 	boolean_t	ret = B_FALSE;
45718c2aff7Sartem 
45818c2aff7Sartem 	switch (profile) {
45918c2aff7Sartem 	case 0x08: /* CD-ROM */
46018c2aff7Sartem 	case 0x10: /* DVD-ROM */
46118c2aff7Sartem 		if (get_media_info(fd, &mi) && (mi.dki_capacity > 1)) {
46218c2aff7Sartem 			*capacity = mi.dki_capacity * mi.dki_lbsize;
46318c2aff7Sartem 			ret = B_TRUE;
46418c2aff7Sartem 		}
46518c2aff7Sartem 		break;
46618c2aff7Sartem 	default:
46718c2aff7Sartem 		if (read_format_capacity(fd, capacity) && (*capacity > 0)) {
46818c2aff7Sartem 			ret = B_TRUE;
46918c2aff7Sartem 		} else if (get_disc_info(fd, &di) && (di.capacity > 0)) {
47018c2aff7Sartem 			if (get_media_info(fd, &mi)) {
47118c2aff7Sartem 				*capacity = di.capacity * mi.dki_lbsize;
47218c2aff7Sartem 				ret = B_TRUE;
47318c2aff7Sartem 			}
47418c2aff7Sartem 		}
47518c2aff7Sartem 	}
47618c2aff7Sartem 
47718c2aff7Sartem 	return (ret);
47818c2aff7Sartem }
47918c2aff7Sartem 
48018c2aff7Sartem boolean_t
48118c2aff7Sartem read_toc(int fd, int format, int trackno, int buflen, uchar_t *buf)
48218c2aff7Sartem {
48318c2aff7Sartem 	struct uscsi_cmd scmd;
48418c2aff7Sartem 	char cdb[16];
48518c2aff7Sartem 
48618c2aff7Sartem 	bzero(buf, buflen);
48718c2aff7Sartem 	uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
48818c2aff7Sartem 	scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
48918c2aff7Sartem 	scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
49018c2aff7Sartem 	scmd.uscsi_cdb[0] = 0x43 /* READ_TOC_CMD */;
49118c2aff7Sartem 	scmd.uscsi_cdb[2] = format & 0xf;
49218c2aff7Sartem 	scmd.uscsi_cdb[6] = trackno;
49318c2aff7Sartem 	scmd.uscsi_cdb[8] = buflen & 0xff;
49418c2aff7Sartem 	scmd.uscsi_cdb[7] = (buflen >> 8) & 0xff;
49518c2aff7Sartem 	scmd.uscsi_cdblen = 10;
49618c2aff7Sartem 	scmd.uscsi_bufaddr = (char *)buf;
49718c2aff7Sartem 	scmd.uscsi_buflen = buflen;
49818c2aff7Sartem 
49918c2aff7Sartem 	if ((uscsi(fd, &scmd)) != 0) {
50018c2aff7Sartem         	return (B_FALSE);
50118c2aff7Sartem 	}
50218c2aff7Sartem 
50318c2aff7Sartem 	return (B_TRUE);
50418c2aff7Sartem }
505