BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / partitioning_systems / session / Disc.cpp
blobb42254f7e2b6ea63b200b5d1640f4849d2e46bfa
1 /*
2 * Copyright 2003-2011, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
5 * Authors:
6 * Tyler Akidau, haiku@akidau.net
7 */
10 /*! \file Disc.cpp
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
18 number \c 0x43.
20 The format of interest for said command is "Full TOC", format
21 number \c 0x2.
25 #include "Disc.h"
27 #include <DiskDeviceDefs.h>
28 #include <DiskDeviceTypes.h>
30 #include "Debug.h"
33 DBG(static const char* kModuleDebugName = "session");
36 /*! \brief An item that can be stored in a List object.
38 struct list_item {
39 public:
40 list_item(uint32 index, list_item* next = NULL)
42 index(index),
43 next(next)
47 int32 index;
48 list_item* next;
52 /*! \brief A simple, singly linked list.
54 class List {
55 public:
56 List();
57 ~List();
59 list_item* Find(int32 index) const;
60 void Add(list_item* item);
61 void Clear();
62 void SortAndRemoveDuplicates();
64 list_item* First() const;
65 list_item* Last() const;
67 private:
68 list_item* fFirst;
69 list_item* fLast;
73 /*! \brief Keeps track of track information.
75 struct track : public list_item {
76 public:
77 track(uint32 index, off_t startLBA, uint8 control, uint8 adr,
78 track* next = NULL)
80 list_item(index, next),
81 start_lba(startLBA),
82 control(control),
83 adr(adr)
87 off_t start_lba;
88 uint8 control;
89 // Used to check for Yellow/Red Book mixed-mode CDs.
90 uint8 adr;
91 // only used to give what are probably useless warnings
95 /*! \brief Keeps track of session information.
97 struct session : public list_item {
98 public:
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
105 bool is_audio();
107 int8 first_track_hint;
108 int8 last_track_hint;
109 int8 control;
110 int8 adr;
111 off_t end_lba;
113 List track_list;
117 // #pragma mark - Helper functions
120 #ifdef DEBUG
122 static void
123 dump_scsi_command(raw_device_command* cmd)
125 int i;
126 uint j;
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]));
132 TRACE(("\n"));
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'))
160 TRACE(("\\%c,", c));
161 else
162 TRACE(("%.2x,", c));
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(("-"));
176 TRACE(("\n"));
181 static void
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);
204 TRACE(("\n"));
205 TRACE(("entry count = %d\n", count));
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 = %" B_PRId64 "\n",
227 msf_to_lba(make_msf_address(entries[i].pminutes,
228 entries[i].pseconds, entries[i].pframes))));
230 TRACE(("--------------------------------------------------\n"));
232 #endif // DEBUG
235 static status_t
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));
248 if (error)
249 return error;
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"
254 // ioctl struct
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) {
282 // SUCCESS!!!
283 DBG(dump_full_table_of_contents(buffer, buffer_length));
284 return B_OK;
285 } else {
286 error = B_FILE_ERROR;
287 TRACE(("%s: scsi ioctl succeeded, but scsi command failed\n",
288 kModuleDebugName));
290 } else {
291 error = errno;
292 TRACE(("%s: scsi command failed with error 0x%" B_PRIx32 "\n",
293 kModuleDebugName, error));
297 return error;
301 // #pragma mark - List
302 // TODO: get rid of this, and use the standard DoublyLinkedList
305 /*! \brief Creates an empty list.
307 List::List()
309 fFirst(NULL),
310 fLast(NULL)
315 List::~List()
317 Clear();
321 /*! \brief Returns the ListItem with the given index, or NULL if not found.
323 list_item*
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) {
329 item = item->next;
331 return item;
335 /*! \brief Adds the given item to the end of the list.
337 \param item The item to add (may not be NULL)
339 void
340 List::Add(list_item* item)
342 // TRACE(("%s: List::Add(%p)\n", kModuleDebugName, item));
343 if (item) {
344 item->next = NULL;
345 if (fLast) {
346 fLast->next = item;
347 fLast = item;
348 } else {
349 fFirst = fLast = item;
351 } else {
352 TRACE(("%s: List::Add(): NULL item parameter\n", kModuleDebugName));
357 /*! \brief Clears the list.
359 void
360 List::Clear()
362 list_item* item = fFirst;
363 while (item) {
364 list_item* next = item->next;
365 delete item;
366 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.
378 void
379 List::SortAndRemoveDuplicates()
381 bool sorted = false;
382 while (!sorted) {
383 sorted = true;
385 list_item* prev = NULL;
386 list_item* item = fFirst;
387 list_item* next = NULL;
388 while (item && item->next) {
389 next = item->next;
390 // dprintf("List::Sort: %ld -> %ld\n", item->index, next->index);
391 if (item->index > next->index) {
392 sorted = false;
394 // Keep fLast up to date
395 if (next == fLast)
396 fLast = item;
398 // Swap
399 if (prev) {
400 // item is not fFirst
401 prev->next = next;
402 item->next = next->next;
403 next->next = item;
404 } else {
405 // item must be fFirst
406 fFirst = next;
407 item->next = next->next;
408 next->next = item;
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;
416 delete next;
417 next = item->next;
418 continue;
420 prev = item;
421 item = next;
427 /*! \brief Returns the first item in the list, or NULL if empty
429 list_item*
430 List::First() const
432 return fFirst;
436 /*! \brief Returns the last item in the list, or NULL if empty
438 list_item*
439 List::Last() const
441 return fLast;
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),
454 last_track_hint(-1),
455 control(-1),
456 adr(-1),
457 end_lba(0)
462 /*! \brief Returns true if the \a first_track_hint member has not been
463 set to a legal value yet.
465 bool
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.
475 bool
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.
489 bool
490 session::end_lba_is_set()
492 return end_lba > 0;
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
499 false.
501 bool
502 session::is_audio()
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
513 warnings.
515 If successful, subsequent calls to InitCheck() will return \c B_OK,
516 elsewise they will return an error code.
518 Disc::Disc(int fd)
520 fInitStatus(B_NO_INIT),
521 fSessionList(new List)
523 DEBUG_INIT_ETC("Disc", ("fd: %d", fd));
525 uchar data[kBlockSize];
527 if (!error)
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
538 // mode
539 if (!error)
540 error = read_table_of_contents(fd, 1, data, kBlockSize, false);
541 if (error) {
542 TRACE(("%s: lba read_toc failed, trying msf instead\n",
543 kModuleDebugName));
544 error = read_table_of_contents(fd, 1, data, kBlockSize, true);
547 // Interpret the data returned, if successful
548 if (!error) {
549 cdrom_table_of_contents_header* header;
550 cdrom_full_table_of_contents_entry* entries;
551 int count;
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);
562 // Dump();
563 if (!error) {
564 _SortAndRemoveDuplicates();
565 error = _CheckForErrorsAndWarnings();
569 PRINT(("Setting init status to 0x%" B_PRIx32 ", `%s'\n", error,
570 strerror(error)));
571 fInitStatus = error;
575 /*! \brief Destroys the Disc's internal list.
577 Disc::~Disc()
579 delete fSessionList;
583 /*! \brief Returns \c B_OK if the object was successfully initialized, or
584 an error code if not.
586 status_t
587 Disc::InitCheck()
589 return fInitStatus;
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.
598 Session*
599 Disc::GetSession(int32 index)
601 DEBUG_INIT_ETC("Disc", ("index: %" B_PRId32, index));
602 int32 counter = -1;
603 for (session* session = (struct session*)fSessionList->First(); session;
604 session = (struct session*)session->next) {
605 if (session->is_audio()) {
606 counter++;
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();
612 if (track != NULL) {
613 PRINT(("found session #%" B_PRId32 " info (audio session)"
614 "\n", index));
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 "
627 "memory!\n"));
629 return result;
630 } else {
631 PRINT(("Error: session #%" B_PRId32 " is an audio session "
632 "with no tracks!\n", index));
633 return NULL;
636 } else {
637 for (track* track = (struct track*)session->track_list.First();
638 track; track = (struct track*)track->next) {
639 counter++;
640 if (counter == index) {
641 PRINT(("found session #%" B_PRId32 " info (data session)\n",
642 index));
644 off_t startLBA = track->start_lba;
645 if (startLBA < 0) {
646 WARN(("%s: warning: invalid negative start LBA of %"
647 B_PRId64 " for data track assuming 0\n",
648 kModuleDebugName, startLBA));
649 startLBA = 0;
652 off_t endLBA = track->next
653 ? ((struct track*)track->next)->start_lba
654 : session->end_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 "
664 "memory!\n"));
666 return result;
672 PRINT(("no session #%" B_PRId32 " found!\n", index));
673 return NULL;
677 /*! \brief Dumps a printout of the disc using TRACE.
679 void
680 Disc::Dump()
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
691 ? "data" : "audio"),
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.
709 uint32
710 Disc::_AdjustForYellowBook(cdrom_full_table_of_contents_entry entries[],
711 uint32 count)
713 uint8 foundCount = 0;
714 uint8 endLBAEntry = 0;
715 uint8 trackTwo = 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;
723 break;
725 foundCount++;
726 endLBAEntry = i;
729 if (!sessionIsAudio || foundCount != 1)
730 return count;
732 TRACE(("%s: Single audio session, checking for data track\n",
733 kModuleDebugName));
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) {
748 trackTwo = j;
749 break;
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;
762 count++;
763 TRACE(("%s: first track is data, adjusted TOC\n",
764 kModuleDebugName));
765 break;
766 } else {
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;
781 count++;
782 TRACE(("%s: last track is data, adjusted TOC\n",
783 kModuleDebugName));
784 break;
788 return count;
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.
795 status_t
796 Disc::_ParseTableOfContents(cdrom_full_table_of_contents_entry entries[],
797 uint32 count)
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);
807 if (session == NULL)
808 return B_NO_MEMORY;
810 fSessionList->Add(session);
813 uint8 point = entries[i].point;
815 switch (point) {
816 // first track hint
817 case 0xA0:
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;
822 } else {
823 WARN(("%s: warning: illegal first track hint %d found "
824 "for session %d\n", kModuleDebugName,
825 firstTrackHint, sessionIndex));
827 } else {
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));
833 break;
835 // last track hint
836 case 0xA1:
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;
841 } else {
842 WARN(("%s: warning: illegal last track hint %d found "
843 "for session %d\n", kModuleDebugName,
844 lastTrackHint, sessionIndex));
846 } else {
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));
852 break;
854 // end of session address
855 case 0xA2:
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));
860 if (endLBA > 0) {
861 session->end_lba = endLBA;
862 // We also grab the session's control and adr values
863 // from this entry
864 session->control = entries[i].control;
865 session->adr = entries[i].adr;
866 } else {
867 WARN(("%s: warning: illegal end lba %" B_PRId64 " found"
868 " for session %d\n", kModuleDebugName, endLBA,
869 sessionIndex));
871 } else {
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));
876 break;
878 // Valid, but uninteresting, points
879 case 0xB0:
880 case 0xB1:
881 case 0xB2:
882 case 0xB3:
883 case 0xB4:
884 case 0xC0:
885 case 0xC1:
886 break;
888 default:
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
893 // later.
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);
903 if (track == NULL)
904 return B_NO_MEMORY;
906 session->track_list.Add(track);
907 } else {
908 WARN(("%s: warning: illegal point 0x%2x found in table of "
909 "contents\n", kModuleDebugName, point));
911 break;
914 return B_OK;
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
920 the way.
922 void
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
950 parent session
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.
959 status_t
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) {
966 // Check for errors
968 // missing end lba
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));
973 return B_ERROR;
976 // empty track list
977 track* track = (struct track*)session->track_list.First();
978 if (track == NULL) {
979 TRACE(("%s: Disc::_CheckForErrorsAndWarnings: error: session #%"
980 B_PRId32 "has no tracks\n", kModuleDebugName, session->index));
981 return B_ERROR;
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,
1010 lastSessionIndex));
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,
1020 lastTrackIndex));
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,
1030 track->control,
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")));
1040 // mismatched adr
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,
1046 session->adr));
1051 return B_OK;
1055 // #pragma mark - Session
1058 Session::Session(off_t offset, off_t size, uint32 blockSize, int32 index,
1059 uint32 flags, const char* type)
1061 fOffset(offset),
1062 fSize(size),
1063 fBlockSize(blockSize),
1064 fIndex(index),
1065 fFlags(flags),
1066 fType(strdup(type))
1071 Session::~Session()
1073 free(fType);