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