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