2 * Copyright 2003-2011, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
6 * Tyler Akidau, haiku@akidau.net
12 Disc class implementation, used to enumerate the CD/DVD sessions.
14 The protocols followed in this module are based on information
15 taken from the "SCSI-3 Multimedia Commands" draft, revision 10A.
17 The SCSI command of interest is "READ TOC/PMA/ATIP", command
20 The format of interest for said command is "Full TOC", format
27 #include <DiskDeviceDefs.h>
28 #include <DiskDeviceTypes.h>
33 DBG(static const char* kModuleDebugName
= "session");
36 /*! \brief An item that can be stored in a List object.
40 list_item(uint32 index
, list_item
* next
= NULL
)
52 /*! \brief A simple, singly linked list.
59 list_item
* Find(int32 index
) const;
60 void Add(list_item
* item
);
62 void SortAndRemoveDuplicates();
64 list_item
* First() const;
65 list_item
* Last() const;
73 /*! \brief Keeps track of track information.
75 struct track
: public list_item
{
77 track(uint32 index
, off_t startLBA
, uint8 control
, uint8 adr
,
80 list_item(index
, next
),
89 // Used to check for Yellow/Red Book mixed-mode CDs.
91 // only used to give what are probably useless warnings
95 /*! \brief Keeps track of session information.
97 struct session
: public list_item
{
99 session(uint32 index
, session
* next
= NULL
);
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
107 int8 first_track_hint
;
108 int8 last_track_hint
;
117 // #pragma mark - Helper functions
123 dump_scsi_command(raw_device_command* cmd)
127 scsi_table_of_contents_command* scsi_command
128 = (scsi_table_of_contents_command*)(&(cmd->command));
130 for (i = 0; i < cmd->command_length; i++)
131 TRACE(("%.2x,", cmd->command[i]));
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];
158 if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
159 || ('0' <= c && c <= '9'))
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'))
182 dump_full_table_of_contents(uchar
* data
, uint16 dataLength
)
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
);
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
;
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
));
203 int count
= (headerLength
- 2) / sizeof(cdrom_full_table_of_contents_entry
);
205 TRACE(("entry count = %d\n", count
));
207 for (int i
= 0; i
< count
; i
++) {
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
,
219 TRACE((" minutes = %d\n", entries
[i
].minutes
));
220 TRACE((" frames = %d\n", entries
[i
].seconds
));
221 TRACE((" seconds = %d\n", entries
[i
].frames
));
222 TRACE((" zero = %d\n", entries
[i
].zero
));
223 TRACE((" pminutes = %d\n", entries
[i
].pminutes
));
224 TRACE((" pseconds = %d\n", entries
[i
].pseconds
));
225 TRACE((" pframes = %d\n", entries
[i
].pframes
));
226 TRACE((" lba = %" B_PRId64
"\n",
227 msf_to_lba(make_msf_address(entries
[i
].pminutes
,
228 entries
[i
].pseconds
, entries
[i
].pframes
))));
230 TRACE(("--------------------------------------------------\n"));
236 read_table_of_contents(int deviceFD
, uint32 first_session
, uchar
* buffer
,
237 uint16 buffer_length
, bool msf
)
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
;
245 DEBUG_INIT_ETC(NULL
, ("fd: %d, buffer: %p, buffer_length: %d",
246 deviceFD
, buffer
, buffer_length
));
251 // This does not always work on the first try, so do it twice just in case.
252 for (int attempt
= 0; attempt
< 2; attempt
++) {
253 // Init the scsi command and copy it into the "raw scsi command"
255 memset(raw_command
.command
, 0, 16);
256 scsi_command
.command
= 0x43;
257 scsi_command
.msf
= 1;
258 scsi_command
.format
= kFullTableOfContentsFormat
;
259 scsi_command
.number
= first_session
;
260 scsi_command
.length
= B_HOST_TO_BENDIAN_INT16(buffer_length
);
261 scsi_command
.control
= 0;
262 scsi_command
.reserved0
= scsi_command
.reserved1
= scsi_command
.reserved2
263 = scsi_command
.reserved3
= scsi_command
.reserved4
264 = scsi_command
.reserved5
= scsi_command
.reserved6
= 0;
265 memcpy(raw_command
.command
, &scsi_command
, sizeof(scsi_command
));
267 // Init the rest of the raw command
268 raw_command
.command_length
= 10;
269 raw_command
.flags
= kScsiFlags
;
270 raw_command
.scsi_status
= 0;
271 raw_command
.cam_status
= 0;
272 raw_command
.data
= buffer
;
273 raw_command
.data_length
= buffer_length
;
274 memset(raw_command
.data
, 0, raw_command
.data_length
);
275 raw_command
.sense_data
= sense_data
;
276 raw_command
.sense_data_length
= sense_data_length
;
277 memset(raw_command
.sense_data
, 0, raw_command
.sense_data_length
);
278 raw_command
.timeout
= kScsiTimeout
;
280 if (ioctl(deviceFD
, B_RAW_DEVICE_COMMAND
, &raw_command
) == 0) {
281 if (raw_command
.scsi_status
== 0 && raw_command
.cam_status
== 1) {
283 DBG(dump_full_table_of_contents(buffer
, buffer_length
));
286 error
= B_FILE_ERROR
;
287 TRACE(("%s: scsi ioctl succeeded, but scsi command failed\n",
292 TRACE(("%s: scsi command failed with error 0x%" B_PRIx32
"\n",
293 kModuleDebugName
, error
));
301 // #pragma mark - List
302 // TODO: get rid of this, and use the standard DoublyLinkedList
305 /*! \brief Creates an empty list.
321 /*! \brief Returns the ListItem with the given index, or NULL if not found.
324 List::Find(int32 index
) const
326 // TRACE(("%s: List::Find(%ld)\n", kModuleDebugName, index));
327 list_item
* item
= fFirst
;
328 while (item
&& item
->index
!= index
) {
335 /*! \brief Adds the given item to the end of the list.
337 \param item The item to add (may not be NULL)
340 List::Add(list_item
* item
)
342 // TRACE(("%s: List::Add(%p)\n", kModuleDebugName, item));
349 fFirst
= fLast
= item
;
352 TRACE(("%s: List::Add(): NULL item parameter\n", kModuleDebugName
));
357 /*! \brief Clears the list.
362 list_item
* item
= fFirst
;
364 list_item
* next
= item
->next
;
368 fFirst
= fLast
= NULL
;
372 /*! \brief Bubble sorts the list by index, removing any duplicates
373 (the first instance is kept).
375 \todo I believe duplicate removal is actually unnecessary, but
376 I need to verify that.
379 List::SortAndRemoveDuplicates()
385 list_item
* prev
= NULL
;
386 list_item
* item
= fFirst
;
387 list_item
* next
= NULL
;
388 while (item
&& item
->next
) {
390 // dprintf("List::Sort: %ld -> %ld\n", item->index, next->index);
391 if (item
->index
> next
->index
) {
394 // Keep fLast up to date
400 // item is not fFirst
402 item
->next
= next
->next
;
405 // item must be fFirst
407 item
->next
= next
->next
;
410 } else if (item
->index
== next
->index
) {
411 // Duplicate indicies
412 TRACE(("%s: List::SortAndRemoveDuplicates: duplicate indicies "
413 "found (#%" B_PRId32
"); keeping first instance\n",
414 kModuleDebugName
, item
->index
));
415 item
->next
= next
->next
;
427 /*! \brief Returns the first item in the list, or NULL if empty
436 /*! \brief Returns the last item in the list, or NULL if empty
445 // #pragma mark - session
448 /*! \brief Creates an unitialized session object
450 session::session(uint32 index
, session
* next
)
452 list_item(index
, next
),
453 first_track_hint(-1),
462 /*! \brief Returns true if the \a first_track_hint member has not been
463 set to a legal value yet.
466 session::first_track_hint_is_set()
468 return 1 <= first_track_hint
&& first_track_hint
<= 99;
472 /*! \brief Returns true if the \a last_track_hint member has not been
473 set to a legal value yet.
476 session::last_track_hint_is_set()
478 return 1 <= last_track_hint
&& last_track_hint
<= 99;
482 /*! \brief Returns true if the \a end_lba member has not been
483 set to a legal value yet.
485 The result of this function also signals that the \a control
486 and \a adr members have or have not been set, since they are
487 set at the same time as \a end_lba.
490 session::end_lba_is_set()
496 /*! \brief Returns true if the session is flagged as being audio.
498 If the \c control value for the session has not been set, returns
504 return end_lba_is_set() && !(control
& kControlDataTrack
);
508 // #pragma mark - Disc
511 /*! \brief Creates a new Disc object by parsing the given table of contents
512 entries and checking the resultant data structure for errors and
515 If successful, subsequent calls to InitCheck() will return \c B_OK,
516 elsewise they will return an error code.
520 fInitStatus(B_NO_INIT
),
521 fSessionList(new List
)
523 DEBUG_INIT_ETC("Disc", ("fd: %d", fd
));
525 uchar data
[kBlockSize
];
528 error = sessionInfo && index >= 0 ? B_OK : B_BAD_VALUE;
529 int32 session = index+1;
530 // Check for a valid session index
531 if (session < 1 || session > 99)
532 error = B_ENTRY_NOT_FOUND;
535 status_t error
= fSessionList
? B_OK
: B_NO_MEMORY
;
537 // Attempt to read the table of contents, first in lba mode, then in msf
540 error
= read_table_of_contents(fd
, 1, data
, kBlockSize
, false);
542 TRACE(("%s: lba read_toc failed, trying msf instead\n",
544 error
= read_table_of_contents(fd
, 1, data
, kBlockSize
, true);
547 // Interpret the data returned, if successful
549 cdrom_table_of_contents_header
* header
;
550 cdrom_full_table_of_contents_entry
* entries
;
553 header
= (cdrom_table_of_contents_header
*)data
;
554 entries
= (cdrom_full_table_of_contents_entry
*)(data
+ 4);
555 header
->length
= B_BENDIAN_TO_HOST_INT16(header
->length
);
557 count
= (header
->length
- 2)
558 / sizeof(cdrom_full_table_of_contents_entry
);
560 count
= _AdjustForYellowBook(entries
, count
);
561 error
= _ParseTableOfContents(entries
, count
);
564 _SortAndRemoveDuplicates();
565 error
= _CheckForErrorsAndWarnings();
569 PRINT(("Setting init status to 0x%" B_PRIx32
", `%s'\n", error
,
575 /*! \brief Destroys the Disc's internal list.
583 /*! \brief Returns \c B_OK if the object was successfully initialized, or
584 an error code if not.
593 /*! \brief Stores the info for the given session (using 0 based indicies) in the
594 struct pointed to by \a sessionInfo.
596 Returns \c B_ENTRY_NOT_FOUND if no such session exists.
599 Disc::GetSession(int32 index
)
601 DEBUG_INIT_ETC("Disc", ("index: %" B_PRId32
, index
));
603 for (session
* session
= (struct session
*)fSessionList
->First(); session
;
604 session
= (struct session
*)session
->next
) {
605 if (session
->is_audio()) {
607 // only one session per audio session
608 if (counter
== index
) {
609 // Found an audio session. Take the start of the first
610 // track with the end of session.
611 track
* track
= (struct track
*)session
->track_list
.First();
613 PRINT(("found session #%" B_PRId32
" info (audio session)"
616 off_t startLBA
= track
->start_lba
;
617 off_t endLBA
= session
->end_lba
;
619 off_t offset
= startLBA
* kBlockSize
;
620 off_t size
= (endLBA
- startLBA
) * kBlockSize
;
622 Session
* result
= new Session(offset
, size
, kBlockSize
,
623 index
, B_PARTITION_READ_ONLY
,
624 kPartitionTypeAudioSession
);
625 if (result
== NULL
) {
626 PRINT(("Error allocating new Session object; out of "
631 PRINT(("Error: session #%" B_PRId32
" is an audio session "
632 "with no tracks!\n", index
));
637 for (track
* track
= (struct track
*)session
->track_list
.First();
638 track
; track
= (struct track
*)track
->next
) {
640 if (counter
== index
) {
641 PRINT(("found session #%" B_PRId32
" info (data session)\n",
644 off_t startLBA
= track
->start_lba
;
646 WARN(("%s: warning: invalid negative start LBA of %"
647 B_PRId64
" for data track assuming 0\n",
648 kModuleDebugName
, startLBA
));
652 off_t endLBA
= track
->next
653 ? ((struct track
*)track
->next
)->start_lba
656 off_t offset
= startLBA
* kBlockSize
;
657 off_t size
= (endLBA
- startLBA
) * kBlockSize
;
659 Session
* result
= new Session(offset
, size
, kBlockSize
,
660 index
, B_PARTITION_READ_ONLY
,
661 kPartitionTypeDataSession
);
662 if (result
== NULL
) {
663 PRINT(("Error allocating new Session object; out of "
672 PRINT(("no session #%" B_PRId32
" found!\n", index
));
677 /*! \brief Dumps a printout of the disc using TRACE.
682 TRACE(("%s: Disc dump:\n", kModuleDebugName
));
683 session
* session
= (struct session
*)fSessionList
->First();
684 while (session
!= NULL
) {
685 TRACE(("session %" B_PRId32
":\n", session
->index
));
686 TRACE((" first track hint: %d\n", session
->first_track_hint
));
687 TRACE((" last track hint: %d\n", session
->last_track_hint
));
688 TRACE((" end_lba: %" B_PRId64
"\n", session
->end_lba
));
689 TRACE((" control: %d (%s session, copy %s)\n",
690 session
->control
, (session
->control
& kControlDataTrack
692 (session
->control
& kControlCopyPermitted
693 ? "permitted" : "prohibited")));
694 TRACE((" adr: %d\n", session
->adr
));
695 track
* track
= (struct track
*)session
->track_list
.First();
696 while (track
!= NULL
) {
697 TRACE((" track %" B_PRId32
":\n", track
->index
));
698 TRACE((" start_lba: %" B_PRId64
"\n", track
->start_lba
));
699 track
= (struct track
*)track
->next
;
701 session
= (struct session
*)session
->next
;
706 /*! \brief Checks for Yellow Book data tracks in audio sessions and if found
707 inserts them as a new data session.
710 Disc::_AdjustForYellowBook(cdrom_full_table_of_contents_entry entries
[],
713 uint8 foundCount
= 0;
714 uint8 endLBAEntry
= 0;
717 // Make sure TOC has only one session and that it is audio.
718 bool sessionIsAudio
= true;
719 for (uint32 i
= 0; i
< count
; i
++) {
720 if (entries
[i
].point
== 0xa2) {
721 if ((entries
[i
].control
& kControlDataTrack
) != 0) {
722 sessionIsAudio
= false;
729 if (!sessionIsAudio
|| foundCount
!= 1)
732 TRACE(("%s: Single audio session, checking for data track\n",
735 // See if there are any data tracks.
736 for (uint32 i
= 0; i
< count
; i
++) {
737 if (entries
[i
].point
> 0 && entries
[i
].point
< 100
738 && (entries
[i
].control
& kControlDataTrack
) != 0) {
739 if (entries
[i
].point
== 1) {
740 // Create a new endLBA point for session one.
741 entries
[count
] = entries
[endLBAEntry
];
742 entries
[count
].control
= entries
[i
].control
;
744 // Get track two and use it's start as
745 // the end of our new session.
746 for (uint8 j
= 0; j
< count
; j
++) {
747 if (entries
[j
].point
== 2) {
752 entries
[count
].pminutes
= entries
[trackTwo
].pminutes
;
753 entries
[count
].pseconds
= entries
[trackTwo
].pseconds
;
754 entries
[count
].pframes
= entries
[trackTwo
].pframes
;
756 // Change the other points to session two.
757 for (uint32 j
= 0; j
< count
; j
++) {
758 entries
[j
].session
= 2;
760 entries
[i
].session
= 1;
763 TRACE(("%s: first track is data, adjusted TOC\n",
767 // Change the track to session two.
768 entries
[i
].session
= 2;
770 // Create a new endLBA point for session two.
771 entries
[count
] = entries
[endLBAEntry
];
772 entries
[count
].session
= 2;
773 entries
[count
].control
= entries
[i
].control
;
775 // Use the beginning of the data track as the
776 // end of the previous session.
777 entries
[endLBAEntry
].pminutes
= entries
[i
].pminutes
;
778 entries
[endLBAEntry
].pseconds
= entries
[i
].pseconds
;
779 entries
[endLBAEntry
].pframes
= entries
[i
].pframes
;
782 TRACE(("%s: last track is data, adjusted TOC\n",
792 /*! \brief Reads through the given table of contents data and creates an
793 unsorted, unverified (i.e. non-error-checked) list of sessions and tracks.
796 Disc::_ParseTableOfContents(cdrom_full_table_of_contents_entry entries
[],
799 DEBUG_INIT_ETC("Disc", ("entries: %p, count: %" B_PRIu32
, entries
, count
));
801 for (uint32 i
= 0; i
< count
; i
++) {
802 // Find or create the appropriate session
803 uint8 sessionIndex
= entries
[i
].session
;
804 session
* session
= (struct session
*)fSessionList
->Find(sessionIndex
);
805 if (session
== NULL
) {
806 session
= new struct session(sessionIndex
);
810 fSessionList
->Add(session
);
813 uint8 point
= entries
[i
].point
;
818 if (!session
->first_track_hint_is_set()) {
819 int8 firstTrackHint
= entries
[i
].pminutes
;
820 if (1 <= firstTrackHint
&& firstTrackHint
<= 99) {
821 session
->first_track_hint
= firstTrackHint
;
823 WARN(("%s: warning: illegal first track hint %d found "
824 "for session %d\n", kModuleDebugName
,
825 firstTrackHint
, sessionIndex
));
828 WARN(("%s: warning: duplicated first track hint values "
829 "found for session %d; using first value "
830 "encountered: %d", kModuleDebugName
, sessionIndex
,
831 session
->first_track_hint
));
837 if (!session
->last_track_hint_is_set()) {
838 int8 lastTrackHint
= entries
[i
].pminutes
;
839 if (1 <= lastTrackHint
&& lastTrackHint
<= 99) {
840 session
->last_track_hint
= lastTrackHint
;
842 WARN(("%s: warning: illegal last track hint %d found "
843 "for session %d\n", kModuleDebugName
,
844 lastTrackHint
, sessionIndex
));
847 WARN(("%s: warning: duplicate last track hint values found "
848 "for session %d; using first value encountered: %d",
849 kModuleDebugName
, sessionIndex
,
850 session
->last_track_hint
));
854 // end of session address
856 if (!session
->end_lba_is_set()) {
857 off_t endLBA
= msf_to_lba(make_msf_address(
858 entries
[i
].pminutes
, entries
[i
].pseconds
,
859 entries
[i
].pframes
));
861 session
->end_lba
= endLBA
;
862 // We also grab the session's control and adr values
864 session
->control
= entries
[i
].control
;
865 session
->adr
= entries
[i
].adr
;
867 WARN(("%s: warning: illegal end lba %" B_PRId64
" found"
868 " for session %d\n", kModuleDebugName
, endLBA
,
872 WARN(("%s: warning: duplicate end lba values found for "
873 "session %d; using first value encountered: %" B_PRId64
,
874 kModuleDebugName
, sessionIndex
, session
->end_lba
));
878 // Valid, but uninteresting, points
889 // Anything else had better be a valid track number,
890 // or it's an invalid point
891 if (1 <= point
&& point
<= 99) {
892 // Create and add the track. We'll weed out any duplicates
894 uint8 trackIndex
= point
;
895 off_t startLBA
= msf_to_lba(make_msf_address(
896 entries
[i
].pminutes
, entries
[i
].pseconds
,
897 entries
[i
].pframes
));
898 // The control and adr values grabbed here are only used
899 // later on to signal a warning if they don't match the
900 // corresponding values of the parent session.
901 track
* track
= new(std::nothrow
) struct track(trackIndex
,
902 startLBA
, entries
[i
].control
, entries
[i
].adr
);
906 session
->track_list
.Add(track
);
908 WARN(("%s: warning: illegal point 0x%2x found in table of "
909 "contents\n", kModuleDebugName
, point
));
918 /*! \brief Bubble sorts the session list and each session's track lists,
919 removing all but the first of any duplicates (by index) found along
923 Disc::_SortAndRemoveDuplicates()
925 fSessionList
->SortAndRemoveDuplicates();
926 session
* session
= (struct session
*)fSessionList
->First();
927 while (session
!= NULL
) {
928 session
->track_list
.SortAndRemoveDuplicates();
929 session
= (struct session
*)session
->next
;
934 /* \brief Checks the sessions and tracks for any anomalies.
936 Errors will return an error code, warnings will return B_OK.
937 Both will print a notification using TRACE.
939 Anomalies that result in errors:
940 - Sessions with no end_lba set
941 - Sessions with no tracks
943 Anomalies that result in warnings:
944 - Inaccurate first_track_hint and/or last_track_hint values
945 - Sequences of sessions or tracks that do not start at 1,
946 do not end at or before 99, or are not strictly ascending.
947 (all tracks are checked as a single sequence, since track
948 numbering does not restart with each session).
949 - Tracks with different control and/or adr values than their
952 Anomalies that are currently *not* checked:
953 - First Track Hint or Last Track Hint control and adr values
954 that do not match the values for their session; Ingo's copy
955 of the BeOS R5 CD is like this, but I don't believe it's
956 a matter we need to worry about. This could certainly be
957 changed in the future if needed.
960 Disc::_CheckForErrorsAndWarnings() {
961 int32 lastSessionIndex
= 0;
962 int32 lastTrackIndex
= 0;
964 for (session
* session
= (struct session
*)fSessionList
->First(); session
;
965 session
= (struct session
*)session
->next
) {
969 if (!session
->end_lba_is_set()) {
970 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: error: no end of "
971 "session address for session #%" B_PRId32
"\n",
972 kModuleDebugName
, session
->index
));
977 track
* track
= (struct track
*)session
->track_list
.First();
979 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: error: session #%"
980 B_PRId32
"has no tracks\n", kModuleDebugName
, session
->index
));
984 // Check for warnings
986 // incorrect first track hint
987 if (session
->first_track_hint_is_set()
988 && session
->first_track_hint
!= track
->index
) {
989 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: session "
990 "#%" B_PRId32
": first track hint (%d) doesn't match actual "
991 "first track (%" B_PRId32
")\n", kModuleDebugName
,
992 session
->index
, session
->first_track_hint
, track
->index
));
995 // incorrect last track hint
996 struct track
* last
= (struct track
*)session
->track_list
.Last();
997 if (session
->last_track_hint_is_set() && last
998 && session
->last_track_hint
!= last
->index
) {
999 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: session "
1000 "#%" B_PRId32
": last track hint (%d) doesn't match actual "
1001 "last track (%" B_PRId32
")\n", kModuleDebugName
,
1002 session
->index
, session
->last_track_hint
, last
->index
));
1005 // invalid session sequence
1006 if (lastSessionIndex
+ 1 != session
->index
) {
1007 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: index for "
1008 "session #%" B_PRId32
" is out of sequence (should have been #%"
1009 B_PRId32
")\n", kModuleDebugName
, session
->index
,
1012 lastSessionIndex
= session
->index
;
1014 for (; track
; track
= (struct track
*)track
->next
) {
1015 // invalid track sequence
1016 if (lastTrackIndex
+ 1 != track
->index
) {
1017 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: index "
1018 "for track #%" B_PRId32
" is out of sequence (should have "
1019 "been #%" B_PRId32
")\n", kModuleDebugName
, track
->index
,
1022 lastTrackIndex
= track
->index
;
1024 // mismatched control
1025 if (track
->control
!= session
->control
) {
1026 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: control "
1027 "for track #%" B_PRId32
" (%d, %s track, copy %s) does not "
1028 "match control for parent session #%" B_PRId32
" (%d, %s "
1029 "session, copy %s)\n", kModuleDebugName
, track
->index
,
1031 (track
->control
& kControlDataTrack
? "data" : "audio"),
1032 (track
->control
& kControlCopyPermitted
1033 ? "permitted" : "prohibited"),
1034 session
->index
, session
->control
,
1035 (session
->control
& kControlDataTrack
? "data" : "audio"),
1036 (session
->control
& kControlCopyPermitted
1037 ? "permitted" : "prohibited")));
1041 if (track
->adr
!= session
->adr
) {
1042 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: adr "
1043 "for track #%" B_PRId32
" (adr = %d) does not match adr "
1044 "for parent session #%" B_PRId32
" (adr = %d)\n",
1045 kModuleDebugName
, track
->index
, track
->adr
, session
->index
,
1055 // #pragma mark - Session
1058 Session::Session(off_t offset
, off_t size
, uint32 blockSize
, int32 index
,
1059 uint32 flags
, const char* type
)
1063 fBlockSize(blockSize
),