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