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