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