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