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