xref: /haiku/src/add-ons/kernel/partitioning_systems/session/Disc.cpp (revision 529cd177b573aaba391c8adc9c9f5ad76a14bf81)
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 	// 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));
266 
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;
279 
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));
289 			}
290 		} else {
291 			error = errno;
292 			TRACE(("%s: scsi command failed with error 0x%" B_PRIx32 "\n",
293 				kModuleDebugName, error));
294 		}
295 	}
296 
297 	return error;
298 }
299 
300 
301 //	#pragma mark - List
302 // TODO: get rid of this, and use the standard DoublyLinkedList
303 
304 
305 /*! \brief Creates an empty list.
306 */
307 List::List()
308 	:
309 	fFirst(NULL),
310 	fLast(NULL)
311 {
312 }
313 
314 
315 List::~List()
316 {
317 	Clear();
318 }
319 
320 
321 /*! \brief Returns the ListItem with the given index, or NULL if not found.
322 */
323 list_item*
324 List::Find(int32 index) const
325 {
326 //	TRACE(("%s: List::Find(%ld)\n", kModuleDebugName, index));
327 	list_item* item = fFirst;
328 	while (item && item->index != index) {
329 		item = item->next;
330 	}
331 	return item;
332 }
333 
334 
335 /*! \brief Adds the given item to the end of the list.
336 
337 	\param item The item to add (may not be NULL)
338 */
339 void
340 List::Add(list_item* item)
341 {
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;
350 		}
351 	} else {
352 		TRACE(("%s: List::Add(): NULL item parameter\n", kModuleDebugName));
353 	}
354 }
355 
356 
357 /*! \brief Clears the list.
358 */
359 void
360 List::Clear()
361 {
362 	list_item* item = fFirst;
363 	while (item) {
364 		list_item* next = item->next;
365 		delete item;
366 		item = next;
367 	}
368 	fFirst = fLast = NULL;
369 }
370 
371 
372 /*! \brief Bubble sorts the list by index, removing any duplicates
373 	(the first instance is kept).
374 
375 	\todo I believe duplicate removal is actually unnecessary, but
376 		I need to verify that.
377 */
378 void
379 List::SortAndRemoveDuplicates()
380 {
381 	bool sorted = false;
382 	while (!sorted) {
383 		sorted = true;
384 
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;
393 
394 				// Keep fLast up to date
395 				if (next == fLast)
396 					fLast = item;
397 
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;
409 				}
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;
419 			}
420 			prev = item;
421 			item = next;
422 		}
423 	}
424 }
425 
426 
427 /*! \brief Returns the first item in the list, or NULL if empty
428 */
429 list_item*
430 List::First() const
431 {
432 	return fFirst;
433 }
434 
435 
436 /*! \brief Returns the last item in the list, or NULL if empty
437 */
438 list_item*
439 List::Last() const
440 {
441 	return fLast;
442 }
443 
444 
445 //	#pragma mark - session
446 
447 
448 /*! \brief Creates an unitialized session object
449 */
450 session::session(uint32 index, session* next)
451 	:
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)
458 {
459 }
460 
461 
462 /*! \brief Returns true if the \a first_track_hint member has not been
463 	set to a legal value yet.
464 */
465 bool
466 session::first_track_hint_is_set()
467 {
468 	return 1 <= first_track_hint && first_track_hint <= 99;
469 }
470 
471 
472 /*! \brief Returns true if the \a last_track_hint member has not been
473 	set to a legal value yet.
474 */
475 bool
476 session::last_track_hint_is_set()
477 {
478 	return 1 <= last_track_hint && last_track_hint <= 99;
479 }
480 
481 
482 /*! \brief Returns true if the \a end_lba member has not been
483 	set to a legal value yet.
484 
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.
488 */
489 bool
490 session::end_lba_is_set()
491 {
492 	return end_lba > 0;
493 }
494 
495 
496 /*! \brief Returns true if the session is flagged as being audio.
497 
498 	If the \c control value for the session has not been set, returns
499 	false.
500 */
501 bool
502 session::is_audio()
503 {
504 	return end_lba_is_set() && !(control & kControlDataTrack);
505 }
506 
507 
508 //	#pragma mark - Disc
509 
510 
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.
514 
515 	If successful, subsequent calls to InitCheck() will return \c B_OK,
516 	elsewise they will return an error code.
517 */
518 Disc::Disc(int fd)
519 	:
520 	fInitStatus(B_NO_INIT),
521 	fSessionList(new List)
522 {
523 	DEBUG_INIT_ETC("Disc", ("fd: %d", fd));
524 
525 	uchar data[kBlockSize];
526 /*
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;
533 */
534 
535 	status_t error = fSessionList ? B_OK : B_NO_MEMORY;
536 
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);
545 	}
546 
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;
552 
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);
556 
557 		count = (header->length - 2)
558 			/ sizeof(cdrom_full_table_of_contents_entry);
559 
560 		count = _AdjustForYellowBook(entries, count);
561 		error = _ParseTableOfContents(entries, count);
562 //		Dump();
563 		if (!error) {
564 			_SortAndRemoveDuplicates();
565 			error = _CheckForErrorsAndWarnings();
566 		}
567 	}
568 
569 	PRINT(("Setting init status to 0x%" B_PRIx32 ", `%s'\n", error,
570 		strerror(error)));
571 	fInitStatus = error;
572 }
573 
574 
575 /*! \brief Destroys the Disc's internal list.
576 */
577 Disc::~Disc()
578 {
579 	delete fSessionList;
580 }
581 
582 
583 /*! \brief Returns \c B_OK if the object was successfully initialized, or
584 	an error code if not.
585 */
586 status_t
587 Disc::InitCheck()
588 {
589 	return fInitStatus;
590 }
591 
592 
593 /*! \brief Stores the info for the given session (using 0 based indicies) in the
594 	struct pointed to by \a sessionInfo.
595 
596 	Returns \c B_ENTRY_NOT_FOUND if no such session exists.
597 */
598 Session*
599 Disc::GetSession(int32 index)
600 {
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));
615 
616 					off_t startLBA = track->start_lba;
617 					off_t endLBA = session->end_lba;
618 
619 					off_t offset = startLBA * kBlockSize;
620 					off_t size = (endLBA - startLBA) * kBlockSize;
621 
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"));
628 					}
629 					return result;
630 				} else {
631 					PRINT(("Error: session #%" B_PRId32 " is an audio session "
632 						"with no tracks!\n", index));
633 					return NULL;
634 				}
635 			}
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));
643 
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;
650 					}
651 
652 					off_t endLBA = track->next
653 						? ((struct track*)track->next)->start_lba
654 						: session->end_lba;
655 
656 					off_t offset = startLBA * kBlockSize;
657 					off_t size = (endLBA - startLBA) * kBlockSize;
658 
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"));
665 					}
666 					return result;
667 				}
668 			}
669 		}
670 	}
671 
672 	PRINT(("no session #%" B_PRId32 " found!\n", index));
673 	return NULL;
674 }
675 
676 
677 /*! \brief Dumps a printout of the disc using TRACE.
678 */
679 void
680 Disc::Dump()
681 {
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;
700 		}
701 		session = (struct session*)session->next;
702 	}
703 }
704 
705 
706 /*! \brief Checks for Yellow Book data tracks in audio sessions and if found
707 	inserts them as a new data session.
708 */
709 uint32
710 Disc::_AdjustForYellowBook(cdrom_full_table_of_contents_entry entries[],
711 	uint32 count)
712 {
713 	uint8 foundCount = 0;
714 	uint8 endLBAEntry = 0;
715 	uint8 trackTwo = 0;
716 
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;
724 			}
725 			foundCount++;
726 			endLBAEntry = i;
727 		}
728 	}
729 	if (!sessionIsAudio || foundCount != 1)
730 		return count;
731 
732 	TRACE(("%s: Single audio session, checking for data track\n",
733 		kModuleDebugName));
734 
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;
743 
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;
750 					}
751 				}
752 				entries[count].pminutes = entries[trackTwo].pminutes;
753 				entries[count].pseconds = entries[trackTwo].pseconds;
754 				entries[count].pframes = entries[trackTwo].pframes;
755 
756 				// Change the other points to session two.
757 				for (uint32 j = 0; j < count; j++) {
758 					entries[j].session = 2;
759 				}
760 				entries[i].session = 1;
761 
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;
769 
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;
774 
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;
780 
781 				count++;
782 				TRACE(("%s: last track is data, adjusted TOC\n",
783 					kModuleDebugName));
784 				break;
785 			}
786 		}
787 	}
788 	return count;
789 }
790 
791 
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.
794 */
795 status_t
796 Disc::_ParseTableOfContents(cdrom_full_table_of_contents_entry entries[],
797 	uint32 count)
798 {
799 	DEBUG_INIT_ETC("Disc", ("entries: %p, count: %" B_PRIu32, entries, count));
800 
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;
809 
810 			fSessionList->Add(session);
811 		}
812 
813 		uint8 point = entries[i].point;
814 
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));
826 					}
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));
832 				}
833 				break;
834 
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));
845 					}
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));
851 				}
852 				break;
853 
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));
870 					}
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));
875 				}
876 				break;
877 
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;
887 
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;
905 
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));
910 				}
911 				break;
912 		}
913 	}
914 	return B_OK;
915 }
916 
917 
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.
921 */
922 void
923 Disc::_SortAndRemoveDuplicates()
924 {
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;
930 	}
931 }
932 
933 
934 /*	\brief Checks the sessions and tracks for any anomalies.
935 
936 	Errors will return an error code, warnings will return B_OK.
937 	Both will print a notification using TRACE.
938 
939 	Anomalies that result in errors:
940 	- Sessions with no end_lba set
941 	- Sessions with no tracks
942 
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
951 
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.
958 */
959 status_t
960 Disc::_CheckForErrorsAndWarnings() {
961 	int32 lastSessionIndex = 0;
962 	int32 lastTrackIndex = 0;
963 
964 	for (session* session = (struct session*)fSessionList->First(); session;
965 			session = (struct session*)session->next) {
966 		// Check for errors
967 
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;
974 		}
975 
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;
982 		}
983 
984 		// Check for warnings
985 
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));
993 		}
994 
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));
1003 		}
1004 
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));
1011 		}
1012 		lastSessionIndex = session->index;
1013 
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));
1021 			}
1022 			lastTrackIndex = track->index;
1023 
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")));
1038 			}
1039 
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));
1047 			}
1048 		}
1049 	}
1050 
1051 	return B_OK;
1052 }
1053 
1054 
1055 //	#pragma mark - Session
1056 
1057 
1058 Session::Session(off_t offset, off_t size, uint32 blockSize, int32 index,
1059 		uint32 flags, const char* type)
1060 	:
1061 	fOffset(offset),
1062 	fSize(size),
1063 	fBlockSize(blockSize),
1064 	fIndex(index),
1065 	fFlags(flags),
1066 	fType(strdup(type))
1067 {
1068 }
1069 
1070 
1071 Session::~Session()
1072 {
1073 	free(fType);
1074 }
1075