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 = %lld\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 // Init the scsi command and copy it into the "raw scsi command" 252 // ioctl struct 253 memset(raw_command.command, 0, 16); 254 scsi_command.command = 0x43; 255 scsi_command.msf = 1; 256 scsi_command.format = kFullTableOfContentsFormat; 257 scsi_command.number = first_session; 258 scsi_command.length = B_HOST_TO_BENDIAN_INT16(buffer_length); 259 scsi_command.control = 0; 260 scsi_command.reserved0 = scsi_command.reserved1 = scsi_command.reserved2 261 = scsi_command.reserved3 = scsi_command.reserved4 262 = scsi_command.reserved5 = scsi_command.reserved6 = 0; 263 memcpy(raw_command.command, &scsi_command, sizeof(scsi_command)); 264 265 // Init the rest of the raw command 266 raw_command.command_length = 10; 267 raw_command.flags = kScsiFlags; 268 raw_command.scsi_status = 0; 269 raw_command.cam_status = 0; 270 raw_command.data = buffer; 271 raw_command.data_length = buffer_length; 272 memset(raw_command.data, 0, raw_command.data_length); 273 raw_command.sense_data = sense_data; 274 raw_command.sense_data_length = sense_data_length; 275 memset(raw_command.sense_data, 0, raw_command.sense_data_length); 276 raw_command.timeout = kScsiTimeout; 277 278 if (ioctl(deviceFD, B_RAW_DEVICE_COMMAND, &raw_command) == 0) { 279 if (raw_command.scsi_status == 0 && raw_command.cam_status == 1) { 280 // SUCCESS!!! 281 DBG(dump_full_table_of_contents(buffer, buffer_length)); 282 } else { 283 error = B_FILE_ERROR; 284 TRACE(("%s: scsi ioctl succeeded, but scsi command failed\n", 285 kModuleDebugName)); 286 } 287 } else { 288 error = errno; 289 TRACE(("%s: scsi command failed with error 0x%lx\n", kModuleDebugName, 290 error)); 291 } 292 293 return error; 294 295 } 296 297 298 // #pragma mark - List 299 // TODO: get rid of this, and use the standard DoublyLinkedList 300 301 302 /*! \brief Creates an empty list. 303 */ 304 List::List() 305 : 306 fFirst(NULL), 307 fLast(NULL) 308 { 309 } 310 311 312 List::~List() 313 { 314 Clear(); 315 } 316 317 318 /*! \brief Returns the ListItem with the given index, or NULL if not found. 319 */ 320 list_item* 321 List::Find(int32 index) const 322 { 323 // TRACE(("%s: List::Find(%ld)\n", kModuleDebugName, index)); 324 list_item* item = fFirst; 325 while (item && item->index != index) { 326 item = item->next; 327 } 328 return item; 329 } 330 331 332 /*! \brief Adds the given item to the end of the list. 333 334 \param item The item to add (may not be NULL) 335 */ 336 void 337 List::Add(list_item* item) 338 { 339 // TRACE(("%s: List::Add(%p)\n", kModuleDebugName, item)); 340 if (item) { 341 item->next = NULL; 342 if (fLast) { 343 fLast->next = item; 344 fLast = item; 345 } else { 346 fFirst = fLast = item; 347 } 348 } else { 349 TRACE(("%s: List::Add(): NULL item parameter\n", kModuleDebugName)); 350 } 351 } 352 353 354 /*! \brief Clears the list. 355 */ 356 void 357 List::Clear() 358 { 359 list_item* item = fFirst; 360 while (item) { 361 list_item* next = item->next; 362 delete item; 363 item = next; 364 } 365 fFirst = fLast = NULL; 366 } 367 368 369 /*! \brief Bubble sorts the list by index, removing any duplicates 370 (the first instance is kept). 371 372 \todo I believe duplicate removal is actually unnecessary, but 373 I need to verify that. 374 */ 375 void 376 List::SortAndRemoveDuplicates() 377 { 378 bool sorted = false; 379 while (!sorted) { 380 sorted = true; 381 382 list_item* prev = NULL; 383 list_item* item = fFirst; 384 list_item* next = NULL; 385 while (item && item->next) { 386 next = item->next; 387 // dprintf("List::Sort: %ld -> %ld\n", item->index, next->index); 388 if (item->index > next->index) { 389 sorted = false; 390 391 // Keep fLast up to date 392 if (next == fLast) 393 fLast = item; 394 395 // Swap 396 if (prev) { 397 // item is not fFirst 398 prev->next = next; 399 item->next = next->next; 400 next->next = item; 401 } else { 402 // item must be fFirst 403 fFirst = next; 404 item->next = next->next; 405 next->next = item; 406 } 407 } else if (item->index == next->index) { 408 // Duplicate indicies 409 TRACE(("%s: List::SortAndRemoveDuplicates: duplicate indicies " 410 "found (#%ld); keeping first instance\n", kModuleDebugName, 411 item->index)); 412 item->next = next->next; 413 delete next; 414 next = item->next; 415 continue; 416 } 417 prev = item; 418 item = next; 419 } 420 } 421 } 422 423 424 /*! \brief Returns the first item in the list, or NULL if empty 425 */ 426 list_item* 427 List::First() const 428 { 429 return fFirst; 430 } 431 432 433 /*! \brief Returns the last item in the list, or NULL if empty 434 */ 435 list_item* 436 List::Last() const 437 { 438 return fLast; 439 } 440 441 442 // #pragma mark - session 443 444 445 /*! \brief Creates an unitialized session object 446 */ 447 session::session(uint32 index, session* next) 448 : 449 list_item(index, next), 450 first_track_hint(-1), 451 last_track_hint(-1), 452 control(-1), 453 adr(-1), 454 end_lba(0) 455 { 456 } 457 458 459 /*! \brief Returns true if the \a first_track_hint member has not been 460 set to a legal value yet. 461 */ 462 bool 463 session::first_track_hint_is_set() 464 { 465 return 1 <= first_track_hint && first_track_hint <= 99; 466 } 467 468 469 /*! \brief Returns true if the \a last_track_hint member has not been 470 set to a legal value yet. 471 */ 472 bool 473 session::last_track_hint_is_set() 474 { 475 return 1 <= last_track_hint && last_track_hint <= 99; 476 } 477 478 479 /*! \brief Returns true if the \a end_lba member has not been 480 set to a legal value yet. 481 482 The result of this function also signals that the \a control 483 and \a adr members have or have not been set, since they are 484 set at the same time as \a end_lba. 485 */ 486 bool 487 session::end_lba_is_set() 488 { 489 return end_lba > 0; 490 } 491 492 493 /*! \brief Returns true if the session is flagged as being audio. 494 495 If the \c control value for the session has not been set, returns 496 false. 497 */ 498 bool 499 session::is_audio() 500 { 501 return end_lba_is_set() && !(control & kControlDataTrack); 502 } 503 504 505 // #pragma mark - Disc 506 507 508 /*! \brief Creates a new Disc object by parsing the given table of contents 509 entries and checking the resultant data structure for errors and 510 warnings. 511 512 If successful, subsequent calls to InitCheck() will return \c B_OK, 513 elsewise they will return an error code. 514 */ 515 Disc::Disc(int fd) 516 : 517 fInitStatus(B_NO_INIT), 518 fSessionList(new List) 519 { 520 DEBUG_INIT_ETC("Disc", ("fd: %d", fd)); 521 522 uchar data[kBlockSize]; 523 /* 524 if (!error) 525 error = sessionInfo && index >= 0 ? B_OK : B_BAD_VALUE; 526 int32 session = index+1; 527 // Check for a valid session index 528 if (session < 1 || session > 99) 529 error = B_ENTRY_NOT_FOUND; 530 */ 531 532 status_t error = fSessionList ? B_OK : B_NO_MEMORY; 533 534 // Attempt to read the table of contents, first in lba mode, then in msf 535 // mode 536 if (!error) 537 error = read_table_of_contents(fd, 1, data, kBlockSize, false); 538 if (error) { 539 TRACE(("%s: lba read_toc failed, trying msf instead\n", 540 kModuleDebugName)); 541 error = read_table_of_contents(fd, 1, data, kBlockSize, true); 542 } 543 544 // Interpret the data returned, if successful 545 if (!error) { 546 cdrom_table_of_contents_header* header; 547 cdrom_full_table_of_contents_entry* entries; 548 int count; 549 550 header = (cdrom_table_of_contents_header*)data; 551 entries = (cdrom_full_table_of_contents_entry*)(data + 4); 552 header->length = B_BENDIAN_TO_HOST_INT16(header->length); 553 554 count = (header->length - 2) 555 / sizeof(cdrom_full_table_of_contents_entry); 556 557 count = _AdjustForYellowBook(entries, count); 558 error = _ParseTableOfContents(entries, count); 559 // Dump(); 560 if (!error) { 561 _SortAndRemoveDuplicates(); 562 error = _CheckForErrorsAndWarnings(); 563 } 564 } 565 566 PRINT(("Setting init status to 0x%lx, `%s'\n", error, strerror(error))); 567 fInitStatus = error; 568 } 569 570 571 /*! \brief Destroys the Disc's internal list. 572 */ 573 Disc::~Disc() 574 { 575 delete fSessionList; 576 } 577 578 579 /*! \brief Returns \c B_OK if the object was successfully initialized, or 580 an error code if not. 581 */ 582 status_t 583 Disc::InitCheck() 584 { 585 return fInitStatus; 586 } 587 588 589 /*! \brief Stores the info for the given session (using 0 based indicies) in the 590 struct pointed to by \a sessionInfo. 591 592 Returns \c B_ENTRY_NOT_FOUND if no such session exists. 593 */ 594 Session* 595 Disc::GetSession(int32 index) 596 { 597 DEBUG_INIT_ETC("Disc", ("index: %ld", index)); 598 int32 counter = -1; 599 for (session* session = (struct session*)fSessionList->First(); session; 600 session = (struct session*)session->next) { 601 if (session->is_audio()) { 602 counter++; 603 // only one session per audio session 604 if (counter == index) { 605 // Found an audio session. Take the start of the first 606 // track with the end of session. 607 track* track = (struct track*)session->track_list.First(); 608 if (track != NULL) { 609 PRINT(("found session #%ld info (audio session)\n", index)); 610 611 off_t startLBA = track->start_lba; 612 off_t endLBA = session->end_lba; 613 614 off_t offset = startLBA * kBlockSize; 615 off_t size = (endLBA - startLBA) * kBlockSize; 616 617 Session* result = new Session(offset, size, kBlockSize, 618 index, B_PARTITION_READ_ONLY, 619 kPartitionTypeAudioSession); 620 if (result == NULL) { 621 PRINT(("Error allocating new Session object; out of " 622 "memory!\n")); 623 } 624 return result; 625 } else { 626 PRINT(("Error: session #%ld is an audio session with no " 627 "tracks!\n", index)); 628 return NULL; 629 } 630 } 631 } else { 632 for (track* track = (struct track*)session->track_list.First(); 633 track; track = (struct track*)track->next) { 634 counter++; 635 if (counter == index) { 636 PRINT(("found session #%ld info (data session)\n", index)); 637 638 off_t startLBA = track->start_lba; 639 if (startLBA < 0) { 640 WARN(("%s: warning: invalid negative start LBA of %lld" 641 " for data track assuming 0\n", kModuleDebugName, 642 startLBA)); 643 startLBA = 0; 644 } 645 646 off_t endLBA = track->next 647 ? ((struct track*)track->next)->start_lba 648 : session->end_lba; 649 650 off_t offset = startLBA * kBlockSize; 651 off_t size = (endLBA - startLBA) * kBlockSize; 652 653 Session* result = new Session(offset, size, kBlockSize, 654 index, B_PARTITION_READ_ONLY, 655 kPartitionTypeDataSession); 656 if (result == NULL) { 657 PRINT(("Error allocating new Session object; out of " 658 "memory!\n")); 659 } 660 return result; 661 } 662 } 663 } 664 } 665 666 PRINT(("no session #%ld found!\n", index)); 667 return NULL; 668 } 669 670 671 /*! \brief Dumps a printout of the disc using TRACE. 672 */ 673 void 674 Disc::Dump() 675 { 676 TRACE(("%s: Disc dump:\n", kModuleDebugName)); 677 session* session = (struct session*)fSessionList->First(); 678 while (session != NULL) { 679 TRACE(("session %ld:\n", session->index)); 680 TRACE((" first track hint: %d\n", session->first_track_hint)); 681 TRACE((" last track hint: %d\n", session->last_track_hint)); 682 TRACE((" end_lba: %lld\n", session->end_lba)); 683 TRACE((" control: %d (%s session, copy %s)\n", 684 session->control, (session->control & kControlDataTrack 685 ? "data" : "audio"), 686 (session->control & kControlCopyPermitted 687 ? "permitted" : "prohibited"))); 688 TRACE((" adr: %d\n", session->adr)); 689 track* track = (struct track*)session->track_list.First(); 690 while (track != NULL) { 691 TRACE((" track %ld:\n", track->index)); 692 TRACE((" start_lba: %lld\n", track->start_lba)); 693 track = (struct track*)track->next; 694 } 695 session = (struct session*)session->next; 696 } 697 } 698 699 700 /*! \brief Checks for Yellow Book data tracks in audio sessions and if found 701 inserts them as a new data session. 702 */ 703 uint32 704 Disc::_AdjustForYellowBook(cdrom_full_table_of_contents_entry entries[], 705 uint32 count) 706 { 707 uint8 foundCount = 0; 708 uint8 endLBAEntry = 0; 709 uint8 trackTwo = 0; 710 711 // Make sure TOC has only one session and that it is audio. 712 bool sessionIsAudio = true; 713 for (uint32 i = 0; i < count; i++) { 714 if (entries[i].point == 0xa2) { 715 if ((entries[i].control & kControlDataTrack) != 0) { 716 sessionIsAudio = false; 717 break; 718 } 719 foundCount++; 720 endLBAEntry = i; 721 } 722 } 723 if (!sessionIsAudio || foundCount != 1) 724 return count; 725 726 TRACE(("%s: Single audio session, checking for data track\n", 727 kModuleDebugName)); 728 729 // See if there are any data tracks. 730 for (uint32 i = 0; i < count; i++) { 731 if (entries[i].point > 0 && entries[i].point < 100 732 && (entries[i].control & kControlDataTrack) != 0) { 733 if (entries[i].point == 1) { 734 // Create a new endLBA point for session one. 735 entries[count] = entries[endLBAEntry]; 736 entries[count].control = entries[i].control; 737 738 // Get track two and use it's start as 739 // the end of our new session. 740 for (uint8 j = 0; j < count; j++) { 741 if (entries[j].point == 2) { 742 trackTwo = j; 743 break; 744 } 745 } 746 entries[count].pminutes = entries[trackTwo].pminutes; 747 entries[count].pseconds = entries[trackTwo].pseconds; 748 entries[count].pframes = entries[trackTwo].pframes; 749 750 // Change the other points to session two. 751 for (uint32 j = 0; j < count; j++) { 752 entries[j].session = 2; 753 } 754 entries[i].session = 1; 755 756 count++; 757 TRACE(("%s: first track is data, adjusted TOC\n", 758 kModuleDebugName)); 759 break; 760 } else { 761 // Change the track to session two. 762 entries[i].session = 2; 763 764 // Create a new endLBA point for session two. 765 entries[count] = entries[endLBAEntry]; 766 entries[count].session = 2; 767 entries[count].control = entries[i].control; 768 769 // Use the beginning of the data track as the 770 // end of the previous session. 771 entries[endLBAEntry].pminutes = entries[i].pminutes; 772 entries[endLBAEntry].pseconds = entries[i].pseconds; 773 entries[endLBAEntry].pframes = entries[i].pframes; 774 775 count++; 776 TRACE(("%s: last track is data, adjusted TOC\n", 777 kModuleDebugName)); 778 break; 779 } 780 } 781 } 782 return count; 783 } 784 785 786 /*! \brief Reads through the given table of contents data and creates an 787 unsorted, unverified (i.e. non-error-checked) list of sessions and tracks. 788 */ 789 status_t 790 Disc::_ParseTableOfContents(cdrom_full_table_of_contents_entry entries[], 791 uint32 count) 792 { 793 DEBUG_INIT_ETC("Disc", ("entries: %p, count: %ld", entries, count)); 794 795 for (uint32 i = 0; i < count; i++) { 796 // Find or create the appropriate session 797 uint8 sessionIndex = entries[i].session; 798 session* session = (struct session*)fSessionList->Find(sessionIndex); 799 if (session == NULL) { 800 session = new struct session(sessionIndex); 801 if (session == NULL) 802 return B_NO_MEMORY; 803 804 fSessionList->Add(session); 805 } 806 807 uint8 point = entries[i].point; 808 809 switch (point) { 810 // first track hint 811 case 0xA0: 812 if (!session->first_track_hint_is_set()) { 813 int8 firstTrackHint = entries[i].pminutes; 814 if (1 <= firstTrackHint && firstTrackHint <= 99) { 815 session->first_track_hint = firstTrackHint; 816 } else { 817 WARN(("%s: warning: illegal first track hint %d found " 818 "for session %d\n", kModuleDebugName, 819 firstTrackHint, sessionIndex)); 820 } 821 } else { 822 WARN(("%s: warning: duplicated first track hint values " 823 "found for session %d; using first value " 824 "encountered: %d", kModuleDebugName, sessionIndex, 825 session->first_track_hint)); 826 } 827 break; 828 829 // last track hint 830 case 0xA1: 831 if (!session->last_track_hint_is_set()) { 832 int8 lastTrackHint = entries[i].pminutes; 833 if (1 <= lastTrackHint && lastTrackHint <= 99) { 834 session->last_track_hint = lastTrackHint; 835 } else { 836 WARN(("%s: warning: illegal last track hint %d found " 837 "for session %d\n", kModuleDebugName, 838 lastTrackHint, sessionIndex)); 839 } 840 } else { 841 WARN(("%s: warning: duplicate last track hint values found " 842 "for session %d; using first value encountered: %d", 843 kModuleDebugName, sessionIndex, 844 session->last_track_hint)); 845 } 846 break; 847 848 // end of session address 849 case 0xA2: 850 if (!session->end_lba_is_set()) { 851 off_t endLBA = msf_to_lba(make_msf_address( 852 entries[i].pminutes, entries[i].pseconds, 853 entries[i].pframes)); 854 if (endLBA > 0) { 855 session->end_lba = endLBA; 856 // We also grab the session's control and adr values 857 // from this entry 858 session->control = entries[i].control; 859 session->adr = entries[i].adr; 860 } else { 861 WARN(("%s: warning: illegal end lba %lld found for " 862 "session %d\n", kModuleDebugName, endLBA, 863 sessionIndex)); 864 } 865 } else { 866 WARN(("%s: warning: duplicate end lba values found for " 867 "session %d; using first value encountered: %lld", 868 kModuleDebugName, sessionIndex, session->end_lba)); 869 } 870 break; 871 872 // Valid, but uninteresting, points 873 case 0xB0: 874 case 0xB1: 875 case 0xB2: 876 case 0xB3: 877 case 0xB4: 878 case 0xC0: 879 case 0xC1: 880 break; 881 882 default: 883 // Anything else had better be a valid track number, 884 // or it's an invalid point 885 if (1 <= point && point <= 99) { 886 // Create and add the track. We'll weed out any duplicates 887 // later. 888 uint8 trackIndex = point; 889 off_t startLBA = msf_to_lba(make_msf_address( 890 entries[i].pminutes, entries[i].pseconds, 891 entries[i].pframes)); 892 // The control and adr values grabbed here are only used 893 // later on to signal a warning if they don't match the 894 // corresponding values of the parent session. 895 track* track = new(std::nothrow) struct track(trackIndex, 896 startLBA, entries[i].control, entries[i].adr); 897 if (track == NULL) 898 return B_NO_MEMORY; 899 900 session->track_list.Add(track); 901 } else { 902 WARN(("%s: warning: illegal point 0x%2x found in table of " 903 "contents\n", kModuleDebugName, point)); 904 } 905 break; 906 } 907 } 908 return B_OK; 909 } 910 911 912 /*! \brief Bubble sorts the session list and each session's track lists, 913 removing all but the first of any duplicates (by index) found along 914 the way. 915 */ 916 void 917 Disc::_SortAndRemoveDuplicates() 918 { 919 fSessionList->SortAndRemoveDuplicates(); 920 session* session = (struct session*)fSessionList->First(); 921 while (session != NULL) { 922 session->track_list.SortAndRemoveDuplicates(); 923 session = (struct session*)session->next; 924 } 925 } 926 927 928 /* \brief Checks the sessions and tracks for any anomalies. 929 930 Errors will return an error code, warnings will return B_OK. 931 Both will print a notification using TRACE. 932 933 Anomalies that result in errors: 934 - Sessions with no end_lba set 935 - Sessions with no tracks 936 937 Anomalies that result in warnings: 938 - Inaccurate first_track_hint and/or last_track_hint values 939 - Sequences of sessions or tracks that do not start at 1, 940 do not end at or before 99, or are not strictly ascending. 941 (all tracks are checked as a single sequence, since track 942 numbering does not restart with each session). 943 - Tracks with different control and/or adr values than their 944 parent session 945 946 Anomalies that are currently *not* checked: 947 - First Track Hint or Last Track Hint control and adr values 948 that do not match the values for their session; Ingo's copy 949 of the BeOS R5 CD is like this, but I don't believe it's 950 a matter we need to worry about. This could certainly be 951 changed in the future if needed. 952 */ 953 status_t 954 Disc::_CheckForErrorsAndWarnings() { 955 int32 lastSessionIndex = 0; 956 int32 lastTrackIndex = 0; 957 958 for (session* session = (struct session*)fSessionList->First(); session; 959 session = (struct session*)session->next) { 960 // Check for errors 961 962 // missing end lba 963 if (!session->end_lba_is_set()) { 964 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: error: no end of " 965 "session address for session #%ld\n", kModuleDebugName, 966 session->index)); 967 return B_ERROR; 968 } 969 970 // empty track list 971 track* track = (struct track*)session->track_list.First(); 972 if (track == NULL) { 973 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: error: session #%ld " 974 "has no tracks\n", kModuleDebugName, session->index)); 975 return B_ERROR; 976 } 977 978 // Check for warnings 979 980 // incorrect first track hint 981 if (session->first_track_hint_is_set() 982 && session->first_track_hint != track->index) { 983 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: session " 984 "#%ld: first track hint (%d) doesn't match actual first track " 985 "(%ld)\n", kModuleDebugName, session->index, 986 session->first_track_hint, track->index)); 987 } 988 989 // incorrect last track hint 990 struct track* last = (struct track*)session->track_list.Last(); 991 if (session->last_track_hint_is_set() && last 992 && session->last_track_hint != last->index) { 993 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: session " 994 "#%ld: last track hint (%d) doesn't match actual last track " 995 "(%ld)\n", kModuleDebugName, session->index, 996 session->last_track_hint, last->index)); 997 } 998 999 // invalid session sequence 1000 if (lastSessionIndex + 1 != session->index) { 1001 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: index for " 1002 "session #%ld is out of sequence (should have been #%ld)\n", 1003 kModuleDebugName, session->index, lastSessionIndex)); 1004 } 1005 lastSessionIndex = session->index; 1006 1007 for (; track; track = (struct track*)track->next) { 1008 // invalid track sequence 1009 if (lastTrackIndex + 1 != track->index) { 1010 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: index " 1011 "for track #%ld is out of sequence (should have been " 1012 "#%ld)\n", kModuleDebugName, track->index, lastTrackIndex)); 1013 } 1014 lastTrackIndex = track->index; 1015 1016 // mismatched control 1017 if (track->control != session->control) { 1018 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: control " 1019 "for track #%ld (%d, %s track, copy %s) does not match " 1020 "control for parent session #%ld (%d, %s session, copy " 1021 "%s)\n", kModuleDebugName, track->index, track->control, 1022 (track->control & kControlDataTrack ? "data" : "audio"), 1023 (track->control & kControlCopyPermitted 1024 ? "permitted" : "prohibited"), 1025 session->index, session->control, 1026 (session->control & kControlDataTrack ? "data" : "audio"), 1027 (session->control & kControlCopyPermitted 1028 ? "permitted" : "prohibited"))); 1029 } 1030 1031 // mismatched adr 1032 if (track->adr != session->adr) { 1033 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: adr " 1034 "for track #%ld (adr = %d) does not match adr for parent " 1035 "session #%ld (adr = %d)\n", kModuleDebugName, track->index, 1036 track->adr, session->index, session->adr)); 1037 } 1038 } 1039 } 1040 1041 return B_OK; 1042 } 1043 1044 1045 // #pragma mark - Session 1046 1047 1048 Session::Session(off_t offset, off_t size, uint32 blockSize, int32 index, 1049 uint32 flags, const char* type) 1050 : 1051 fOffset(offset), 1052 fSize(size), 1053 fBlockSize(blockSize), 1054 fIndex(index), 1055 fFlags(flags), 1056 fType(strdup(type)) 1057 { 1058 } 1059 1060 1061 Session::~Session() 1062 { 1063 free(fType); 1064 } 1065