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