1 /* 2 * Copyright 2003-2011, Haiku, Inc. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * Tyler Akidau, haiku@akidau.net 7 */ 8 9 10 /*! \file Disc.cpp 11 12 Disc class implementation, used to enumerate the CD/DVD sessions. 13 14 The protocols followed in this module are based on information 15 taken from the "SCSI-3 Multimedia Commands" draft, revision 10A. 16 17 The SCSI command of interest is "READ TOC/PMA/ATIP", command 18 number \c 0x43. 19 20 The format of interest for said command is "Full TOC", format 21 number \c 0x2. 22 */ 23 24 25 #include "Disc.h" 26 27 #include <DiskDeviceDefs.h> 28 #include <DiskDeviceTypes.h> 29 30 #include "Debug.h" 31 32 33 DBG(static const char* kModuleDebugName = "session"); 34 35 36 /*! \brief An item that can be stored in a List object. 37 */ 38 struct list_item { 39 public: 40 list_item(uint32 index, list_item* next = NULL) 41 : 42 index(index), 43 next(next) 44 { 45 } 46 47 int32 index; 48 list_item* next; 49 }; 50 51 52 /*! \brief A simple, singly linked list. 53 */ 54 class List { 55 public: 56 List(); 57 ~List(); 58 59 list_item* Find(int32 index) const; 60 void Add(list_item* item); 61 void Clear(); 62 void SortAndRemoveDuplicates(); 63 64 list_item* First() const; 65 list_item* Last() const; 66 67 private: 68 list_item* fFirst; 69 list_item* fLast; 70 }; 71 72 73 /*! \brief Keeps track of track information. 74 */ 75 struct track : public list_item { 76 public: 77 track(uint32 index, off_t startLBA, uint8 control, uint8 adr, 78 track* next = NULL) 79 : 80 list_item(index, next), 81 start_lba(startLBA), 82 control(control), 83 adr(adr) 84 { 85 } 86 87 off_t start_lba; 88 uint8 control; 89 // Used to check for Yellow/Red Book mixed-mode CDs. 90 uint8 adr; 91 // only used to give what are probably useless warnings 92 }; 93 94 95 /*! \brief Keeps track of session information. 96 */ 97 struct session : public list_item { 98 public: 99 session(uint32 index, session* next = NULL); 100 101 bool first_track_hint_is_set(); 102 bool last_track_hint_is_set(); 103 bool end_lba_is_set(); // also implies control and adr are set 104 105 bool is_audio(); 106 107 int8 first_track_hint; 108 int8 last_track_hint; 109 int8 control; 110 int8 adr; 111 off_t end_lba; 112 113 List track_list; 114 }; 115 116 117 // #pragma mark - Helper functions 118 119 120 #ifdef DEBUG 121 /* 122 static void 123 dump_scsi_command(raw_device_command* cmd) 124 { 125 int i; 126 uint j; 127 scsi_table_of_contents_command* scsi_command 128 = (scsi_table_of_contents_command*)(&(cmd->command)); 129 130 for (i = 0; i < cmd->command_length; i++) 131 TRACE(("%.2x,", cmd->command[i])); 132 TRACE(("\n")); 133 134 TRACE(("raw_device_command:\n")); 135 TRACE((" command:\n")); 136 TRACE((" command = %d (0x%.2x)\n", scsi_command->command, 137 scsi_command->command)); 138 TRACE((" msf = %d\n", scsi_command->msf)); 139 TRACE((" format = %d (0x%.2x)\n", scsi_command->format, 140 scsi_command->format)); 141 TRACE((" number = %d\n", scsi_command->number)); 142 TRACE((" length = %d\n", 143 B_BENDIAN_TO_HOST_INT16(scsi_command->length))); 144 TRACE((" control = %d\n", scsi_command->control)); 145 TRACE((" command_length = %d\n", cmd->command_length)); 146 TRACE((" flags = %d\n", cmd->flags)); 147 TRACE((" scsi_status = 0x%x\n", cmd->scsi_status)); 148 TRACE((" cam_status = 0x%x\n", cmd->cam_status)); 149 TRACE((" data = %p\n", cmd->data)); 150 TRACE((" data_length = %ld\n", cmd->data_length)); 151 TRACE((" sense_data = %p\n", cmd->sense_data)); 152 TRACE((" sense_data_length = %ld\n", cmd->sense_data_length)); 153 TRACE((" timeout = %lld\n", cmd->timeout)); 154 TRACE(("data dump:\n")); 155 for (j = 0; j < 2048; j++) {//cmd->data_length; j++) { 156 uchar c = ((uchar*)cmd->data)[j]; 157 158 if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') 159 || ('0' <= c && c <= '9')) 160 TRACE(("\\%c,", c)); 161 else 162 TRACE(("%.2x,", c)); 163 } 164 TRACE(("\n")); 165 TRACE(("sense_data dump:\n")); 166 for (j = 0; j < cmd->sense_data_length; j++) { 167 uchar c = ((uchar*)cmd->sense_data)[j]; 168 if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') 169 || ('0' <= c && c <= '9')) 170 TRACE(("%c", c)); 171 else if (c == 0) 172 TRACE(("_")); 173 else 174 TRACE(("-")); 175 } 176 TRACE(("\n")); 177 } 178 */ 179 180 181 static void 182 dump_full_table_of_contents(uchar* data, uint16 dataLength) 183 { 184 cdrom_table_of_contents_header* header 185 = (cdrom_table_of_contents_header*)data; 186 cdrom_full_table_of_contents_entry* entries 187 = (cdrom_full_table_of_contents_entry*)(data + 4); 188 int headerLength = B_BENDIAN_TO_HOST_INT16(header->length); 189 190 if (dataLength < headerLength) { 191 TRACE(("dump_full_table_of_contents: warning, data buffer not large " 192 "enough (%d < %d)\n", dataLength, headerLength)); 193 headerLength = dataLength; 194 } 195 196 TRACE(("%s: table of contents dump:\n", kModuleDebugName)); 197 TRACE(("--------------------------------------------------\n")); 198 TRACE(("header:\n")); 199 TRACE((" length = %d\n", headerLength)); 200 TRACE((" first = %d\n", header->first)); 201 TRACE((" last = %d\n", header->last)); 202 203 int count = (headerLength - 2) / sizeof(cdrom_full_table_of_contents_entry); 204 TRACE(("\n")); 205 TRACE(("entry count = %d\n", count)); 206 207 for (int i = 0; i < count; i++) { 208 TRACE(("\n")); 209 TRACE(("entry #%d:\n", i)); 210 TRACE((" session = %d\n", entries[i].session)); 211 TRACE((" adr = %d\n", entries[i].adr)); 212 TRACE((" control = %d (%s track, copy %s)\n", entries[i].control, 213 (entries[i].control & kControlDataTrack ? "data" : "audio"), 214 (entries[i].control & kControlCopyPermitted 215 ? "permitted" : "prohibited"))); 216 TRACE((" tno = %d\n", entries[i].tno)); 217 TRACE((" point = %d (0x%.2x)\n", entries[i].point, 218 entries[i].point)); 219 TRACE((" minutes = %d\n", entries[i].minutes)); 220 TRACE((" frames = %d\n", entries[i].seconds)); 221 TRACE((" seconds = %d\n", entries[i].frames)); 222 TRACE((" zero = %d\n", entries[i].zero)); 223 TRACE((" pminutes = %d\n", entries[i].pminutes)); 224 TRACE((" pseconds = %d\n", entries[i].pseconds)); 225 TRACE((" pframes = %d\n", entries[i].pframes)); 226 TRACE((" lba = %" B_PRId64 "\n", 227 msf_to_lba(make_msf_address(entries[i].pminutes, 228 entries[i].pseconds, entries[i].pframes)))); 229 } 230 TRACE(("--------------------------------------------------\n")); 231 } 232 #endif // DEBUG 233 234 235 static status_t 236 read_table_of_contents(int deviceFD, uint32 first_session, uchar* buffer, 237 uint16 buffer_length, bool msf) 238 { 239 scsi_table_of_contents_command scsi_command; 240 raw_device_command raw_command; 241 const uint32 sense_data_length = 1024; 242 uchar sense_data[sense_data_length]; 243 status_t error = buffer ? B_OK : B_BAD_VALUE; 244 245 DEBUG_INIT_ETC(NULL, ("fd: %d, buffer: %p, buffer_length: %d", 246 deviceFD, buffer, buffer_length)); 247 248 if (error) 249 return error; 250 251 // This does not always work on the first try, so do it twice just in case. 252 for (int attempt = 0; attempt < 2; attempt++) { 253 // Init the scsi command and copy it into the "raw scsi command" 254 // ioctl struct 255 memset(raw_command.command, 0, 16); 256 scsi_command.command = 0x43; 257 scsi_command.msf = 1; 258 scsi_command.format = kFullTableOfContentsFormat; 259 scsi_command.number = first_session; 260 scsi_command.length = B_HOST_TO_BENDIAN_INT16(buffer_length); 261 scsi_command.control = 0; 262 scsi_command.reserved0 = scsi_command.reserved1 = scsi_command.reserved2 263 = scsi_command.reserved3 = scsi_command.reserved4 264 = scsi_command.reserved5 = scsi_command.reserved6 = 0; 265 memcpy(raw_command.command, &scsi_command, sizeof(scsi_command)); 266 267 // Init the rest of the raw command 268 raw_command.command_length = 10; 269 raw_command.flags = kScsiFlags; 270 raw_command.scsi_status = 0; 271 raw_command.cam_status = 0; 272 raw_command.data = buffer; 273 raw_command.data_length = buffer_length; 274 memset(raw_command.data, 0, raw_command.data_length); 275 raw_command.sense_data = sense_data; 276 raw_command.sense_data_length = sense_data_length; 277 memset(raw_command.sense_data, 0, raw_command.sense_data_length); 278 raw_command.timeout = kScsiTimeout; 279 280 if (ioctl(deviceFD, B_RAW_DEVICE_COMMAND, &raw_command) == 0) { 281 if (raw_command.scsi_status == 0 && raw_command.cam_status == 1) { 282 // SUCCESS!!! 283 DBG(dump_full_table_of_contents(buffer, buffer_length)); 284 return B_OK; 285 } else { 286 error = B_FILE_ERROR; 287 TRACE(("%s: scsi ioctl succeeded, but scsi command failed\n", 288 kModuleDebugName)); 289 } 290 } else { 291 error = errno; 292 TRACE(("%s: scsi command failed with error 0x%" B_PRIx32 "\n", 293 kModuleDebugName, error)); 294 } 295 } 296 297 return error; 298 } 299 300 301 // #pragma mark - List 302 // TODO: get rid of this, and use the standard DoublyLinkedList 303 304 305 /*! \brief Creates an empty list. 306 */ 307 List::List() 308 : 309 fFirst(NULL), 310 fLast(NULL) 311 { 312 } 313 314 315 List::~List() 316 { 317 Clear(); 318 } 319 320 321 /*! \brief Returns the ListItem with the given index, or NULL if not found. 322 */ 323 list_item* 324 List::Find(int32 index) const 325 { 326 // TRACE(("%s: List::Find(%ld)\n", kModuleDebugName, index)); 327 list_item* item = fFirst; 328 while (item && item->index != index) { 329 item = item->next; 330 } 331 return item; 332 } 333 334 335 /*! \brief Adds the given item to the end of the list. 336 337 \param item The item to add (may not be NULL) 338 */ 339 void 340 List::Add(list_item* item) 341 { 342 // TRACE(("%s: List::Add(%p)\n", kModuleDebugName, item)); 343 if (item) { 344 item->next = NULL; 345 if (fLast) { 346 fLast->next = item; 347 fLast = item; 348 } else { 349 fFirst = fLast = item; 350 } 351 } else { 352 TRACE(("%s: List::Add(): NULL item parameter\n", kModuleDebugName)); 353 } 354 } 355 356 357 /*! \brief Clears the list. 358 */ 359 void 360 List::Clear() 361 { 362 list_item* item = fFirst; 363 while (item) { 364 list_item* next = item->next; 365 delete item; 366 item = next; 367 } 368 fFirst = fLast = NULL; 369 } 370 371 372 /*! \brief Bubble sorts the list by index, removing any duplicates 373 (the first instance is kept). 374 375 \todo I believe duplicate removal is actually unnecessary, but 376 I need to verify that. 377 */ 378 void 379 List::SortAndRemoveDuplicates() 380 { 381 bool sorted = false; 382 while (!sorted) { 383 sorted = true; 384 385 list_item* prev = NULL; 386 list_item* item = fFirst; 387 list_item* next = NULL; 388 while (item && item->next) { 389 next = item->next; 390 // dprintf("List::Sort: %ld -> %ld\n", item->index, next->index); 391 if (item->index > next->index) { 392 sorted = false; 393 394 // Keep fLast up to date 395 if (next == fLast) 396 fLast = item; 397 398 // Swap 399 if (prev) { 400 // item is not fFirst 401 prev->next = next; 402 item->next = next->next; 403 next->next = item; 404 } else { 405 // item must be fFirst 406 fFirst = next; 407 item->next = next->next; 408 next->next = item; 409 } 410 } else if (item->index == next->index) { 411 // Duplicate indicies 412 TRACE(("%s: List::SortAndRemoveDuplicates: duplicate indicies " 413 "found (#%" B_PRId32 "); keeping first instance\n", 414 kModuleDebugName, item->index)); 415 item->next = next->next; 416 delete next; 417 next = item->next; 418 continue; 419 } 420 prev = item; 421 item = next; 422 } 423 } 424 } 425 426 427 /*! \brief Returns the first item in the list, or NULL if empty 428 */ 429 list_item* 430 List::First() const 431 { 432 return fFirst; 433 } 434 435 436 /*! \brief Returns the last item in the list, or NULL if empty 437 */ 438 list_item* 439 List::Last() const 440 { 441 return fLast; 442 } 443 444 445 // #pragma mark - session 446 447 448 /*! \brief Creates an unitialized session object 449 */ 450 session::session(uint32 index, session* next) 451 : 452 list_item(index, next), 453 first_track_hint(-1), 454 last_track_hint(-1), 455 control(-1), 456 adr(-1), 457 end_lba(0) 458 { 459 } 460 461 462 /*! \brief Returns true if the \a first_track_hint member has not been 463 set to a legal value yet. 464 */ 465 bool 466 session::first_track_hint_is_set() 467 { 468 return 1 <= first_track_hint && first_track_hint <= 99; 469 } 470 471 472 /*! \brief Returns true if the \a last_track_hint member has not been 473 set to a legal value yet. 474 */ 475 bool 476 session::last_track_hint_is_set() 477 { 478 return 1 <= last_track_hint && last_track_hint <= 99; 479 } 480 481 482 /*! \brief Returns true if the \a end_lba member has not been 483 set to a legal value yet. 484 485 The result of this function also signals that the \a control 486 and \a adr members have or have not been set, since they are 487 set at the same time as \a end_lba. 488 */ 489 bool 490 session::end_lba_is_set() 491 { 492 return end_lba > 0; 493 } 494 495 496 /*! \brief Returns true if the session is flagged as being audio. 497 498 If the \c control value for the session has not been set, returns 499 false. 500 */ 501 bool 502 session::is_audio() 503 { 504 return end_lba_is_set() && !(control & kControlDataTrack); 505 } 506 507 508 // #pragma mark - Disc 509 510 511 /*! \brief Creates a new Disc object by parsing the given table of contents 512 entries and checking the resultant data structure for errors and 513 warnings. 514 515 If successful, subsequent calls to InitCheck() will return \c B_OK, 516 elsewise they will return an error code. 517 */ 518 Disc::Disc(int fd) 519 : 520 fInitStatus(B_NO_INIT), 521 fSessionList(new List) 522 { 523 DEBUG_INIT_ETC("Disc", ("fd: %d", fd)); 524 525 uchar data[kBlockSize]; 526 /* 527 if (!error) 528 error = sessionInfo && index >= 0 ? B_OK : B_BAD_VALUE; 529 int32 session = index+1; 530 // Check for a valid session index 531 if (session < 1 || session > 99) 532 error = B_ENTRY_NOT_FOUND; 533 */ 534 535 status_t error = fSessionList ? B_OK : B_NO_MEMORY; 536 537 // Attempt to read the table of contents, first in lba mode, then in msf 538 // mode 539 if (!error) 540 error = read_table_of_contents(fd, 1, data, kBlockSize, false); 541 if (error) { 542 TRACE(("%s: lba read_toc failed, trying msf instead\n", 543 kModuleDebugName)); 544 error = read_table_of_contents(fd, 1, data, kBlockSize, true); 545 } 546 547 // Interpret the data returned, if successful 548 if (!error) { 549 cdrom_table_of_contents_header* header; 550 cdrom_full_table_of_contents_entry* entries; 551 int count; 552 553 header = (cdrom_table_of_contents_header*)data; 554 entries = (cdrom_full_table_of_contents_entry*)(data + 4); 555 header->length = B_BENDIAN_TO_HOST_INT16(header->length); 556 557 count = (header->length - 2) 558 / sizeof(cdrom_full_table_of_contents_entry); 559 560 count = _AdjustForYellowBook(entries, count); 561 error = _ParseTableOfContents(entries, count); 562 // Dump(); 563 if (!error) { 564 _SortAndRemoveDuplicates(); 565 error = _CheckForErrorsAndWarnings(); 566 } 567 } 568 569 PRINT(("Setting init status to 0x%" B_PRIx32 ", `%s'\n", error, 570 strerror(error))); 571 fInitStatus = error; 572 } 573 574 575 /*! \brief Destroys the Disc's internal list. 576 */ 577 Disc::~Disc() 578 { 579 delete fSessionList; 580 } 581 582 583 /*! \brief Returns \c B_OK if the object was successfully initialized, or 584 an error code if not. 585 */ 586 status_t 587 Disc::InitCheck() 588 { 589 return fInitStatus; 590 } 591 592 593 /*! \brief Stores the info for the given session (using 0 based indicies) in the 594 struct pointed to by \a sessionInfo. 595 596 Returns \c B_ENTRY_NOT_FOUND if no such session exists. 597 */ 598 Session* 599 Disc::GetSession(int32 index) 600 { 601 DEBUG_INIT_ETC("Disc", ("index: %" B_PRId32, index)); 602 int32 counter = -1; 603 for (session* session = (struct session*)fSessionList->First(); session; 604 session = (struct session*)session->next) { 605 if (session->is_audio()) { 606 counter++; 607 // only one session per audio session 608 if (counter == index) { 609 // Found an audio session. Take the start of the first 610 // track with the end of session. 611 track* track = (struct track*)session->track_list.First(); 612 if (track != NULL) { 613 PRINT(("found session #%" B_PRId32 " info (audio session)" 614 "\n", index)); 615 616 off_t startLBA = track->start_lba; 617 off_t endLBA = session->end_lba; 618 619 off_t offset = startLBA * kBlockSize; 620 off_t size = (endLBA - startLBA) * kBlockSize; 621 622 Session* result = new Session(offset, size, kBlockSize, 623 index, B_PARTITION_READ_ONLY, 624 kPartitionTypeAudioSession); 625 if (result == NULL) { 626 PRINT(("Error allocating new Session object; out of " 627 "memory!\n")); 628 } 629 return result; 630 } else { 631 PRINT(("Error: session #%" B_PRId32 " is an audio session " 632 "with no tracks!\n", index)); 633 return NULL; 634 } 635 } 636 } else { 637 for (track* track = (struct track*)session->track_list.First(); 638 track; track = (struct track*)track->next) { 639 counter++; 640 if (counter == index) { 641 PRINT(("found session #%" B_PRId32 " info (data session)\n", 642 index)); 643 644 off_t startLBA = track->start_lba; 645 if (startLBA < 0) { 646 WARN(("%s: warning: invalid negative start LBA of %" 647 B_PRId64 " for data track assuming 0\n", 648 kModuleDebugName, startLBA)); 649 startLBA = 0; 650 } 651 652 off_t endLBA = track->next 653 ? ((struct track*)track->next)->start_lba 654 : session->end_lba; 655 656 off_t offset = startLBA * kBlockSize; 657 off_t size = (endLBA - startLBA) * kBlockSize; 658 659 Session* result = new Session(offset, size, kBlockSize, 660 index, B_PARTITION_READ_ONLY, 661 kPartitionTypeDataSession); 662 if (result == NULL) { 663 PRINT(("Error allocating new Session object; out of " 664 "memory!\n")); 665 } 666 return result; 667 } 668 } 669 } 670 } 671 672 PRINT(("no session #%" B_PRId32 " found!\n", index)); 673 return NULL; 674 } 675 676 677 /*! \brief Dumps a printout of the disc using TRACE. 678 */ 679 void 680 Disc::Dump() 681 { 682 TRACE(("%s: Disc dump:\n", kModuleDebugName)); 683 session* session = (struct session*)fSessionList->First(); 684 while (session != NULL) { 685 TRACE(("session %" B_PRId32 ":\n", session->index)); 686 TRACE((" first track hint: %d\n", session->first_track_hint)); 687 TRACE((" last track hint: %d\n", session->last_track_hint)); 688 TRACE((" end_lba: %" B_PRId64 "\n", session->end_lba)); 689 TRACE((" control: %d (%s session, copy %s)\n", 690 session->control, (session->control & kControlDataTrack 691 ? "data" : "audio"), 692 (session->control & kControlCopyPermitted 693 ? "permitted" : "prohibited"))); 694 TRACE((" adr: %d\n", session->adr)); 695 track* track = (struct track*)session->track_list.First(); 696 while (track != NULL) { 697 TRACE((" track %" B_PRId32 ":\n", track->index)); 698 TRACE((" start_lba: %" B_PRId64 "\n", track->start_lba)); 699 track = (struct track*)track->next; 700 } 701 session = (struct session*)session->next; 702 } 703 } 704 705 706 /*! \brief Checks for Yellow Book data tracks in audio sessions and if found 707 inserts them as a new data session. 708 */ 709 uint32 710 Disc::_AdjustForYellowBook(cdrom_full_table_of_contents_entry entries[], 711 uint32 count) 712 { 713 uint8 foundCount = 0; 714 uint8 endLBAEntry = 0; 715 uint8 trackTwo = 0; 716 717 // Make sure TOC has only one session and that it is audio. 718 bool sessionIsAudio = true; 719 for (uint32 i = 0; i < count; i++) { 720 if (entries[i].point == 0xa2) { 721 if ((entries[i].control & kControlDataTrack) != 0) { 722 sessionIsAudio = false; 723 break; 724 } 725 foundCount++; 726 endLBAEntry = i; 727 } 728 } 729 if (!sessionIsAudio || foundCount != 1) 730 return count; 731 732 TRACE(("%s: Single audio session, checking for data track\n", 733 kModuleDebugName)); 734 735 // See if there are any data tracks. 736 for (uint32 i = 0; i < count; i++) { 737 if (entries[i].point > 0 && entries[i].point < 100 738 && (entries[i].control & kControlDataTrack) != 0) { 739 if (entries[i].point == 1) { 740 // Create a new endLBA point for session one. 741 entries[count] = entries[endLBAEntry]; 742 entries[count].control = entries[i].control; 743 744 // Get track two and use it's start as 745 // the end of our new session. 746 for (uint8 j = 0; j < count; j++) { 747 if (entries[j].point == 2) { 748 trackTwo = j; 749 break; 750 } 751 } 752 entries[count].pminutes = entries[trackTwo].pminutes; 753 entries[count].pseconds = entries[trackTwo].pseconds; 754 entries[count].pframes = entries[trackTwo].pframes; 755 756 // Change the other points to session two. 757 for (uint32 j = 0; j < count; j++) { 758 entries[j].session = 2; 759 } 760 entries[i].session = 1; 761 762 count++; 763 TRACE(("%s: first track is data, adjusted TOC\n", 764 kModuleDebugName)); 765 break; 766 } else { 767 // Change the track to session two. 768 entries[i].session = 2; 769 770 // Create a new endLBA point for session two. 771 entries[count] = entries[endLBAEntry]; 772 entries[count].session = 2; 773 entries[count].control = entries[i].control; 774 775 // Use the beginning of the data track as the 776 // end of the previous session. 777 entries[endLBAEntry].pminutes = entries[i].pminutes; 778 entries[endLBAEntry].pseconds = entries[i].pseconds; 779 entries[endLBAEntry].pframes = entries[i].pframes; 780 781 count++; 782 TRACE(("%s: last track is data, adjusted TOC\n", 783 kModuleDebugName)); 784 break; 785 } 786 } 787 } 788 return count; 789 } 790 791 792 /*! \brief Reads through the given table of contents data and creates an 793 unsorted, unverified (i.e. non-error-checked) list of sessions and tracks. 794 */ 795 status_t 796 Disc::_ParseTableOfContents(cdrom_full_table_of_contents_entry entries[], 797 uint32 count) 798 { 799 DEBUG_INIT_ETC("Disc", ("entries: %p, count: %" B_PRIu32, entries, count)); 800 801 for (uint32 i = 0; i < count; i++) { 802 // Find or create the appropriate session 803 uint8 sessionIndex = entries[i].session; 804 session* session = (struct session*)fSessionList->Find(sessionIndex); 805 if (session == NULL) { 806 session = new struct session(sessionIndex); 807 if (session == NULL) 808 return B_NO_MEMORY; 809 810 fSessionList->Add(session); 811 } 812 813 uint8 point = entries[i].point; 814 815 switch (point) { 816 // first track hint 817 case 0xA0: 818 if (!session->first_track_hint_is_set()) { 819 int8 firstTrackHint = entries[i].pminutes; 820 if (1 <= firstTrackHint && firstTrackHint <= 99) { 821 session->first_track_hint = firstTrackHint; 822 } else { 823 WARN(("%s: warning: illegal first track hint %d found " 824 "for session %d\n", kModuleDebugName, 825 firstTrackHint, sessionIndex)); 826 } 827 } else { 828 WARN(("%s: warning: duplicated first track hint values " 829 "found for session %d; using first value " 830 "encountered: %d", kModuleDebugName, sessionIndex, 831 session->first_track_hint)); 832 } 833 break; 834 835 // last track hint 836 case 0xA1: 837 if (!session->last_track_hint_is_set()) { 838 int8 lastTrackHint = entries[i].pminutes; 839 if (1 <= lastTrackHint && lastTrackHint <= 99) { 840 session->last_track_hint = lastTrackHint; 841 } else { 842 WARN(("%s: warning: illegal last track hint %d found " 843 "for session %d\n", kModuleDebugName, 844 lastTrackHint, sessionIndex)); 845 } 846 } else { 847 WARN(("%s: warning: duplicate last track hint values found " 848 "for session %d; using first value encountered: %d", 849 kModuleDebugName, sessionIndex, 850 session->last_track_hint)); 851 } 852 break; 853 854 // end of session address 855 case 0xA2: 856 if (!session->end_lba_is_set()) { 857 off_t endLBA = msf_to_lba(make_msf_address( 858 entries[i].pminutes, entries[i].pseconds, 859 entries[i].pframes)); 860 if (endLBA > 0) { 861 session->end_lba = endLBA; 862 // We also grab the session's control and adr values 863 // from this entry 864 session->control = entries[i].control; 865 session->adr = entries[i].adr; 866 } else { 867 WARN(("%s: warning: illegal end lba %" B_PRId64 " found" 868 " for session %d\n", kModuleDebugName, endLBA, 869 sessionIndex)); 870 } 871 } else { 872 WARN(("%s: warning: duplicate end lba values found for " 873 "session %d; using first value encountered: %" B_PRId64, 874 kModuleDebugName, sessionIndex, session->end_lba)); 875 } 876 break; 877 878 // Valid, but uninteresting, points 879 case 0xB0: 880 case 0xB1: 881 case 0xB2: 882 case 0xB3: 883 case 0xB4: 884 case 0xC0: 885 case 0xC1: 886 break; 887 888 default: 889 // Anything else had better be a valid track number, 890 // or it's an invalid point 891 if (1 <= point && point <= 99) { 892 // Create and add the track. We'll weed out any duplicates 893 // later. 894 uint8 trackIndex = point; 895 off_t startLBA = msf_to_lba(make_msf_address( 896 entries[i].pminutes, entries[i].pseconds, 897 entries[i].pframes)); 898 // The control and adr values grabbed here are only used 899 // later on to signal a warning if they don't match the 900 // corresponding values of the parent session. 901 track* track = new(std::nothrow) struct track(trackIndex, 902 startLBA, entries[i].control, entries[i].adr); 903 if (track == NULL) 904 return B_NO_MEMORY; 905 906 session->track_list.Add(track); 907 } else { 908 WARN(("%s: warning: illegal point 0x%2x found in table of " 909 "contents\n", kModuleDebugName, point)); 910 } 911 break; 912 } 913 } 914 return B_OK; 915 } 916 917 918 /*! \brief Bubble sorts the session list and each session's track lists, 919 removing all but the first of any duplicates (by index) found along 920 the way. 921 */ 922 void 923 Disc::_SortAndRemoveDuplicates() 924 { 925 fSessionList->SortAndRemoveDuplicates(); 926 session* session = (struct session*)fSessionList->First(); 927 while (session != NULL) { 928 session->track_list.SortAndRemoveDuplicates(); 929 session = (struct session*)session->next; 930 } 931 } 932 933 934 /* \brief Checks the sessions and tracks for any anomalies. 935 936 Errors will return an error code, warnings will return B_OK. 937 Both will print a notification using TRACE. 938 939 Anomalies that result in errors: 940 - Sessions with no end_lba set 941 - Sessions with no tracks 942 943 Anomalies that result in warnings: 944 - Inaccurate first_track_hint and/or last_track_hint values 945 - Sequences of sessions or tracks that do not start at 1, 946 do not end at or before 99, or are not strictly ascending. 947 (all tracks are checked as a single sequence, since track 948 numbering does not restart with each session). 949 - Tracks with different control and/or adr values than their 950 parent session 951 952 Anomalies that are currently *not* checked: 953 - First Track Hint or Last Track Hint control and adr values 954 that do not match the values for their session; Ingo's copy 955 of the BeOS R5 CD is like this, but I don't believe it's 956 a matter we need to worry about. This could certainly be 957 changed in the future if needed. 958 */ 959 status_t 960 Disc::_CheckForErrorsAndWarnings() { 961 int32 lastSessionIndex = 0; 962 int32 lastTrackIndex = 0; 963 964 for (session* session = (struct session*)fSessionList->First(); session; 965 session = (struct session*)session->next) { 966 // Check for errors 967 968 // missing end lba 969 if (!session->end_lba_is_set()) { 970 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: error: no end of " 971 "session address for session #%" B_PRId32 "\n", 972 kModuleDebugName, session->index)); 973 return B_ERROR; 974 } 975 976 // empty track list 977 track* track = (struct track*)session->track_list.First(); 978 if (track == NULL) { 979 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: error: session #%" 980 B_PRId32 "has no tracks\n", kModuleDebugName, session->index)); 981 return B_ERROR; 982 } 983 984 // Check for warnings 985 986 // incorrect first track hint 987 if (session->first_track_hint_is_set() 988 && session->first_track_hint != track->index) { 989 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: session " 990 "#%" B_PRId32 ": first track hint (%d) doesn't match actual " 991 "first track (%" B_PRId32 ")\n", kModuleDebugName, 992 session->index, session->first_track_hint, track->index)); 993 } 994 995 // incorrect last track hint 996 struct track* last = (struct track*)session->track_list.Last(); 997 if (session->last_track_hint_is_set() && last 998 && session->last_track_hint != last->index) { 999 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: session " 1000 "#%" B_PRId32 ": last track hint (%d) doesn't match actual " 1001 "last track (%" B_PRId32 ")\n", kModuleDebugName, 1002 session->index, session->last_track_hint, last->index)); 1003 } 1004 1005 // invalid session sequence 1006 if (lastSessionIndex + 1 != session->index) { 1007 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: index for " 1008 "session #%" B_PRId32 " is out of sequence (should have been #%" 1009 B_PRId32 ")\n", kModuleDebugName, session->index, 1010 lastSessionIndex)); 1011 } 1012 lastSessionIndex = session->index; 1013 1014 for (; track; track = (struct track*)track->next) { 1015 // invalid track sequence 1016 if (lastTrackIndex + 1 != track->index) { 1017 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: index " 1018 "for track #%" B_PRId32 " is out of sequence (should have " 1019 "been #%" B_PRId32 ")\n", kModuleDebugName, track->index, 1020 lastTrackIndex)); 1021 } 1022 lastTrackIndex = track->index; 1023 1024 // mismatched control 1025 if (track->control != session->control) { 1026 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: control " 1027 "for track #%" B_PRId32 " (%d, %s track, copy %s) does not " 1028 "match control for parent session #%" B_PRId32 " (%d, %s " 1029 "session, copy %s)\n", kModuleDebugName, track->index, 1030 track->control, 1031 (track->control & kControlDataTrack ? "data" : "audio"), 1032 (track->control & kControlCopyPermitted 1033 ? "permitted" : "prohibited"), 1034 session->index, session->control, 1035 (session->control & kControlDataTrack ? "data" : "audio"), 1036 (session->control & kControlCopyPermitted 1037 ? "permitted" : "prohibited"))); 1038 } 1039 1040 // mismatched adr 1041 if (track->adr != session->adr) { 1042 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: adr " 1043 "for track #%" B_PRId32 " (adr = %d) does not match adr " 1044 "for parent session #%" B_PRId32 " (adr = %d)\n", 1045 kModuleDebugName, track->index, track->adr, session->index, 1046 session->adr)); 1047 } 1048 } 1049 } 1050 1051 return B_OK; 1052 } 1053 1054 1055 // #pragma mark - Session 1056 1057 1058 Session::Session(off_t offset, off_t size, uint32 blockSize, int32 index, 1059 uint32 flags, const char* type) 1060 : 1061 fOffset(offset), 1062 fSize(size), 1063 fBlockSize(blockSize), 1064 fIndex(index), 1065 fFlags(flags), 1066 fType(strdup(type)) 1067 { 1068 } 1069 1070 1071 Session::~Session() 1072 { 1073 free(fType); 1074 } 1075