1 /* 2 * Copyright 2003-2009, 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 //!< only used to give what are probably useless warnings 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 error = _ParseTableOfContents(entries, count); 558 // Dump(); 559 if (!error) { 560 _SortAndRemoveDuplicates(); 561 error = _CheckForErrorsAndWarnings(); 562 } 563 } 564 565 PRINT(("Setting init status to 0x%lx, `%s'\n", error, strerror(error))); 566 fInitStatus = error; 567 } 568 569 570 /*! \brief Destroys the Disc's internal list. 571 */ 572 Disc::~Disc() 573 { 574 delete fSessionList; 575 } 576 577 578 /*! \brief Returns \c B_OK if the object was successfully initialized, or 579 an error code if not. 580 */ 581 status_t 582 Disc::InitCheck() 583 { 584 return fInitStatus; 585 } 586 587 588 /*! \brief Stores the info for the given session (using 0 based indicies) in the 589 struct pointed to by \a sessionInfo. 590 591 Returns \c B_ENTRY_NOT_FOUND if no such session exists. 592 */ 593 Session* 594 Disc::GetSession(int32 index) 595 { 596 DEBUG_INIT_ETC("Disc", ("index: %ld", index)); 597 int32 counter = -1; 598 for (session* session = (struct session*)fSessionList->First(); session; 599 session = (struct session*)session->next) { 600 if (session->is_audio()) { 601 counter++; 602 // only one session per audio session 603 if (counter == index) { 604 // Found an audio session. Take the start of the first 605 // track with the end of session. 606 track* track = (struct track*)session->track_list.First(); 607 if (track != NULL) { 608 PRINT(("found session #%ld info (audio session)\n", index)); 609 610 off_t startLBA = track->start_lba; 611 off_t endLBA = session->end_lba; 612 613 off_t offset = startLBA * kBlockSize; 614 off_t size = (endLBA - startLBA) * kBlockSize; 615 616 Session* result = new Session(offset, size, kBlockSize, 617 index, B_PARTITION_READ_ONLY, 618 kPartitionTypeAudioSession); 619 if (result == NULL) { 620 PRINT(("Error allocating new Session object; out of " 621 "memory!\n")); 622 } 623 return result; 624 } else { 625 PRINT(("Error: session #%ld is an audio session with no " 626 "tracks!\n", index)); 627 return NULL; 628 } 629 } 630 } else { 631 for (track* track = (struct track*)session->track_list.First(); 632 track; track = (struct track*)track->next) { 633 counter++; 634 if (counter == index) { 635 PRINT(("found session #%ld info (data session)\n", index)); 636 637 off_t startLBA = track->start_lba; 638 if (startLBA < 0) { 639 WARN(("%s: warning: invalid negative start LBA of %lld" 640 " for data track assuming 0\n", kModuleDebugName, 641 startLBA)); 642 startLBA = 0; 643 } 644 645 off_t endLBA = track->next 646 ? ((struct track*)track->next)->start_lba 647 : session->end_lba; 648 649 off_t offset = startLBA * kBlockSize; 650 off_t size = (endLBA - startLBA) * kBlockSize; 651 652 Session* result = new Session(offset, size, kBlockSize, 653 index, B_PARTITION_READ_ONLY, 654 kPartitionTypeDataSession); 655 if (result == NULL) { 656 PRINT(("Error allocating new Session object; out of " 657 "memory!\n")); 658 } 659 return result; 660 } 661 } 662 } 663 } 664 665 PRINT(("no session #%ld found!\n", index)); 666 return NULL; 667 } 668 669 670 /*! \brief Dumps a printout of the disc using TRACE. 671 */ 672 void 673 Disc::Dump() 674 { 675 TRACE(("%s: Disc dump:\n", kModuleDebugName)); 676 session* session = (struct session*)fSessionList->First(); 677 while (session != NULL) { 678 TRACE(("session %ld:\n", session->index)); 679 TRACE((" first track hint: %d\n", session->first_track_hint)); 680 TRACE((" last track hint: %d\n", session->last_track_hint)); 681 TRACE((" end_lba: %lld\n", session->end_lba)); 682 TRACE((" control: %d (%s session, copy %s)\n", 683 session->control, (session->control & kControlDataTrack 684 ? "data" : "audio"), 685 (session->control & kControlCopyPermitted 686 ? "permitted" : "prohibited"))); 687 TRACE((" adr: %d\n", session->adr)); 688 track* track = (struct track*)session->track_list.First(); 689 while (track != NULL) { 690 TRACE((" track %ld:\n", track->index)); 691 TRACE((" start_lba: %lld\n", track->start_lba)); 692 track = (struct track*)track->next; 693 } 694 session = (struct session*)session->next; 695 } 696 } 697 698 699 /*! \brief Reads through the given table of contents data and creates an 700 unsorted, unverified (i.e. non-error-checked) list of sessions and tracks. 701 */ 702 status_t 703 Disc::_ParseTableOfContents(cdrom_full_table_of_contents_entry entries[], 704 uint32 count) 705 { 706 DEBUG_INIT_ETC("Disc", ("entries: %p, count: %ld", entries, count)); 707 708 for (uint32 i = 0; i < count; i++) { 709 // Find or create the appropriate session 710 uint8 sessionIndex = entries[i].session; 711 session* session = (struct session*)fSessionList->Find(sessionIndex); 712 if (session == NULL) { 713 session = new struct session(sessionIndex); 714 if (session == NULL) 715 return B_NO_MEMORY; 716 717 fSessionList->Add(session); 718 } 719 720 uint8 point = entries[i].point; 721 722 switch (point) { 723 // first track hint 724 case 0xA0: 725 if (!session->first_track_hint_is_set()) { 726 int8 firstTrackHint = entries[i].pminutes; 727 if (1 <= firstTrackHint && firstTrackHint <= 99) { 728 session->first_track_hint = firstTrackHint; 729 } else { 730 WARN(("%s: warning: illegal first track hint %d found " 731 "for session %d\n", kModuleDebugName, 732 firstTrackHint, sessionIndex)); 733 } 734 } else { 735 WARN(("%s: warning: duplicated first track hint values " 736 "found for session %d; using first value " 737 "encountered: %d", kModuleDebugName, sessionIndex, 738 session->first_track_hint)); 739 } 740 break; 741 742 // last track hint 743 case 0xA1: 744 if (!session->last_track_hint_is_set()) { 745 int8 lastTrackHint = entries[i].pminutes; 746 if (1 <= lastTrackHint && lastTrackHint <= 99) { 747 session->last_track_hint = lastTrackHint; 748 } else { 749 WARN(("%s: warning: illegal last track hint %d found " 750 "for session %d\n", kModuleDebugName, 751 lastTrackHint, sessionIndex)); 752 } 753 } else { 754 WARN(("%s: warning: duplicate last track hint values found " 755 "for session %d; using first value encountered: %d", 756 kModuleDebugName, sessionIndex, 757 session->last_track_hint)); 758 } 759 break; 760 761 // end of session address 762 case 0xA2: 763 if (!session->end_lba_is_set()) { 764 off_t endLBA = msf_to_lba(make_msf_address( 765 entries[i].pminutes, entries[i].pseconds, 766 entries[i].pframes)); 767 if (endLBA > 0) { 768 session->end_lba = endLBA; 769 // We also grab the session's control and adr values 770 // from this entry 771 session->control = entries[i].control; 772 session->adr = entries[i].adr; 773 } else { 774 WARN(("%s: warning: illegal end lba %lld found for " 775 "session %d\n", kModuleDebugName, endLBA, 776 sessionIndex)); 777 } 778 } else { 779 WARN(("%s: warning: duplicate end lba values found for " 780 "session %d; using first value encountered: %lld", 781 kModuleDebugName, sessionIndex, session->end_lba)); 782 } 783 break; 784 785 // Valid, but uninteresting, points 786 case 0xB0: 787 case 0xB1: 788 case 0xB2: 789 case 0xB3: 790 case 0xB4: 791 case 0xC0: 792 case 0xC1: 793 break; 794 795 default: 796 // Anything else had better be a valid track number, 797 // or it's an invalid point 798 if (1 <= point && point <= 99) { 799 // Create and add the track. We'll weed out any duplicates 800 // later. 801 uint8 trackIndex = point; 802 off_t startLBA = msf_to_lba(make_msf_address( 803 entries[i].pminutes, entries[i].pseconds, 804 entries[i].pframes)); 805 // The control and adr values grabbed here are only used 806 // later on to signal a warning if they don't match the 807 // corresponding values of the parent session. 808 track* track = new(std::nothrow) struct track(trackIndex, 809 startLBA, entries[i].control, entries[i].adr); 810 if (track == NULL) 811 return B_NO_MEMORY; 812 813 session->track_list.Add(track); 814 } else { 815 WARN(("%s: warning: illegal point 0x%2x found in table of " 816 "contents\n", kModuleDebugName, entries[i].point)); 817 } 818 break; 819 } 820 } 821 return B_OK; 822 } 823 824 825 /*! \brief Bubble sorts the session list and each session's track lists, 826 removing all but the first of any duplicates (by index) found along 827 the way. 828 */ 829 void 830 Disc::_SortAndRemoveDuplicates() 831 { 832 fSessionList->SortAndRemoveDuplicates(); 833 session* session = (struct session*)fSessionList->First(); 834 while (session != NULL) { 835 session->track_list.SortAndRemoveDuplicates(); 836 session = (struct session*)session->next; 837 } 838 } 839 840 841 /* \brief Checks the sessions and tracks for any anomalies. 842 843 Errors will return an error code, warnings will return B_OK. 844 Both will print a notification using TRACE. 845 846 Anomalies that result in errors: 847 - Sessions with no end_lba set 848 - Sessions with no tracks 849 850 Anomalies that result in warnings: 851 - Inaccurate first_track_hint and/or last_track_hint values 852 - Sequences of sessions or tracks that do not start at 1, 853 do not end at or before 99, or are not strictly ascending. 854 (all tracks are checked as a single sequence, since track 855 numbering does not restart with each session). 856 - Tracks with different control and/or adr values than their 857 parent session 858 859 Anomalies that are currently *not* checked: 860 - First Track Hint or Last Track Hint control and adr values 861 that do not match the values for their session; Ingo's copy 862 of the BeOS R5 CD is like this, but I don't believe it's 863 a matter we need to worry about. This could certainly be 864 changed in the future if needed. 865 */ 866 status_t 867 Disc::_CheckForErrorsAndWarnings() { 868 int32 lastSessionIndex = 0; 869 int32 lastTrackIndex = 0; 870 871 for (session* session = (struct session*)fSessionList->First(); session; 872 session = (struct session*)session->next) { 873 // Check for errors 874 875 // missing end lba 876 if (!session->end_lba_is_set()) { 877 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: error: no end of " 878 "session address for session #%ld\n", kModuleDebugName, 879 session->index)); 880 return B_ERROR; 881 } 882 883 // empty track list 884 track* track = (struct track*)session->track_list.First(); 885 if (track == NULL) { 886 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: error: session #%ld " 887 "has no tracks\n", kModuleDebugName, session->index)); 888 return B_ERROR; 889 } 890 891 // Check for warnings 892 893 // incorrect first track hint 894 if (session->first_track_hint_is_set() 895 && session->first_track_hint != track->index) { 896 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: session " 897 "#%ld: first track hint (%d) doesn't match actual first track " 898 "(%ld)\n", kModuleDebugName, session->index, 899 session->first_track_hint, track->index)); 900 } 901 902 // incorrect last track hint 903 struct track* last = (struct track*)session->track_list.Last(); 904 if (session->last_track_hint_is_set() && last 905 && session->last_track_hint != last->index) { 906 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: session " 907 "#%ld: last track hint (%d) doesn't match actual last track " 908 "(%ld)\n", kModuleDebugName, session->index, 909 session->last_track_hint, last->index)); 910 } 911 912 // invalid session sequence 913 if (lastSessionIndex + 1 != session->index) { 914 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: index for " 915 "session #%ld is out of sequence (should have been #%ld)\n", 916 kModuleDebugName, session->index, lastSessionIndex)); 917 } 918 lastSessionIndex = session->index; 919 920 for (; track; track = (struct track*)track->next) { 921 // invalid track sequence 922 if (lastTrackIndex + 1 != track->index) { 923 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: index " 924 "for track #%ld is out of sequence (should have been " 925 "#%ld)\n", kModuleDebugName, track->index, lastTrackIndex)); 926 } 927 lastTrackIndex = track->index; 928 929 // mismatched control 930 if (track->control != session->control) { 931 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: control " 932 "for track #%ld (%d, %s track, copy %s) does not match " 933 "control for parent session #%ld (%d, %s session, copy " 934 "%s)\n", kModuleDebugName, track->index, track->control, 935 (track->control & kControlDataTrack ? "data" : "audio"), 936 (track->control & kControlCopyPermitted 937 ? "permitted" : "prohibited"), 938 session->index, session->control, 939 (session->control & kControlDataTrack ? "data" : "audio"), 940 (session->control & kControlCopyPermitted 941 ? "permitted" : "prohibited"))); 942 } 943 944 // mismatched adr 945 if (track->adr != session->adr) { 946 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: adr " 947 "for track #%ld (adr = %d) does not match adr for parent " 948 "session #%ld (adr = %d)\n", kModuleDebugName, track->index, 949 track->adr, session->index, session->adr)); 950 } 951 } 952 } 953 954 return B_OK; 955 } 956 957 958 // #pragma mark - Session 959 960 961 Session::Session(off_t offset, off_t size, uint32 blockSize, int32 index, 962 uint32 flags, const char* type) 963 : 964 fOffset(offset), 965 fSize(size), 966 fBlockSize(blockSize), 967 fIndex(index), 968 fFlags(flags), 969 fType(strdup(type)) 970 { 971 } 972 973 974 Session::~Session() 975 { 976 free(fType); 977 } 978