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