Initial checkin. Beginnings of port of disk_scanner session module to

disk device manager. Actual session scanning code appears to work
a-okay, but it isn't hooked up to the partition scanning entry function
yet, so sessions are not yet actually enumerated when the ddm test
program is run.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@5366 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Tyler Dauwalder
2003-11-15 00:54:59 +00:00
parent 60e5e3a209
commit 8ba0b6e187
7 changed files with 1976 additions and 0 deletions
@@ -0,0 +1,343 @@
//----------------------------------------------------------------------
// This software is part of the OpenBeOS distribution and is covered
// by the OpenBeOS license.
//
// This version copyright (c) 2003 Tyler Dauwalder, [email protected]
// Initial version copyright (c) 2002 Axel Dörfler, [email protected]
//----------------------------------------------------------------------
/*! \file Debug.cpp
Support code for handy debugging macros.
*/
#include "Debug.h"
#include <KernelExport.h>
#include <TLS.h>
//----------------------------------------------------------------------
// Long-winded overview of the debug output macros:
//----------------------------------------------------------------------
/*! \def DEBUG_INIT()
\brief Increases the indentation level, prints out the enclosing function's
name, and creates a \c DebugHelper object on the stack to automatically
decrease the indentation level upon function exit.
This macro should be called at the very beginning of any function in
which you wish to use any of the other debugging macros.
If DEBUG is undefined, does nothing.
*/
//----------------------------------------------------------------------
/*! \def PRINT(x)
\brief Prints out the enclosing function's name followed by the contents
of \a x at the current indentation level.
\param x A printf-style format string enclosed in an extra set of parenteses,
e.g. PRINT(("%d\n", 0));
If DEBUG is undefined, does nothing.
*/
//----------------------------------------------------------------------
/*! \def LPRINT(x)
\brief Identical to \c PRINT(x), except that the line number in the source
file at which the macro is invoked is also printed.
\param x A printf-style format string enclosed in an extra set of parenteses,
e.g. PRINT(("%d\n", 0));
If DEBUG is undefined, does nothing.
*/
//----------------------------------------------------------------------
/*! \def SIMPLE_PRINT(x)
\brief Directly prints the contents of \a x with no extra formatting or
information included (just like a straight \c printf() call).
\param x A printf-style format string enclosed in an extra set of parenteses,
e.g. PRINT(("%d\n", 0));
If DEBUG is undefined, does nothing.
*/
//----------------------------------------------------------------------
/*! \def PRINT_INDENT()
\brief Prints out enough indentation characters to indent the current line
to the current indentation level (assuming the cursor was flush left to
begin with...).
This function is called by the other \c *PRINT* macros, and isn't really
intended for general consumption, but you might find it useful.
If DEBUG is undefined, does nothing.
*/
//----------------------------------------------------------------------
/*! \def REPORT_ERROR(error)
\brief Calls \c LPRINT(x) with a format string listing the error
code in \c error (assumed to be a \c status_t value) and the
corresponding text error code returned by a call to \c strerror().
This function is called by the \c RETURN* macros, and isn't really
intended for general consumption, but you might find it useful.
\param error A \c status_t error code to report.
If DEBUG is undefined, does nothing.
*/
//----------------------------------------------------------------------
/*! \def RETURN_ERROR(error)
\brief Calls \c REPORT_ERROR(error) if error is a an error code (i.e.
negative), otherwise remains silent. In either case, the enclosing
function is then exited with a call to \c "return error;".
\param error A \c status_t error code to report (if negative) and return.
If DEBUG is undefined, silently returns the value in \c error.
*/
//----------------------------------------------------------------------
/*! \def RETURN(error)
\brief Prints out a description of the error code being returned
(which, in this case, may be either "erroneous" or "successful")
and then exits the enclosing function with a call to \c "return error;".
\param error A \c status_t error code to report and return.
If DEBUG is undefined, silently returns the value in \c error.
*/
//----------------------------------------------------------------------
/*! \def FATAL(x)
\brief Prints out a fatal error message.
This one's still a work in progress...
\param x A printf-style format string enclosed in an extra set of parenteses,
e.g. PRINT(("%d\n", 0));
If DEBUG is undefined, does nothing.
*/
//----------------------------------------------------------------------
/*! \def INFORM(x)
\brief Directly prints the contents of \a x with no extra formatting or
information included (just like a straight \c printf() call). Does so
whether \c DEBUG is defined or not.
\param x A printf-style format string enclosed in an extra set of parenteses,
e.g. PRINT(("%d\n", 0));
I'll say it again: Prints its output regardless to DEBUG being defined or
undefined.
*/
//----------------------------------------------------------------------
/*! \def DBG(x)
\brief If debug is defined, \a x is passed along to the code and
executed unmodified. If \c DEBUG is undefined, the contents of
\a x disappear into the ether.
\param x Damn near anything resembling valid C\C++.
*/
//----------------------------------------------------------------------
/*! \def DIE(x)
\brief Drops the user into the appropriate debugger (user or kernel)
after printing out the handy message bundled in the parenthesee
enclosed printf-style format string found in \a x.
\param x A printf-style format string enclosed in an extra set of parenteses,
e.g. PRINT(("%d\n", 0));
*/
//----------------------------------------------------------------------
// declarations
//----------------------------------------------------------------------
static void indent(uint8 tabCount);
static void unindent(uint8 tabCount);
#ifdef USER
static int32 get_tls_handle();
#endif
//! Used to keep the tls handle from being allocated more than once.
vint32 tls_spinlock = 0;
/*! \brief Used to flag whether the tls handle has been allocated yet.
Not sure if this really needs to be \c volatile or not...
*/
volatile bool tls_handle_initialized = false;
//! The tls handle of the tls var used to store indentation info.
int32 tls_handle = 0;
//----------------------------------------------------------------------
// public functions
//----------------------------------------------------------------------
/*! \brief Returns the current debug indentation level for the
current thread.
NOTE: indentation is currently unsupported for R5::kernelland due
to lack of thread local storage support.
*/
int32
_get_debug_indent_level()
{
#ifdef USER
return (int32)tls_get(get_tls_handle());
#else
return 1;
#endif
}
//----------------------------------------------------------------------
// static functions
//----------------------------------------------------------------------
/*! \brief Increases the current debug indentation level for
the current thread by 1.
*/
void
indent(uint8 tabCount)
{
#ifdef USER
tls_set(get_tls_handle(), (void*)(_get_debug_indent_level()+tabCount));
#endif
}
/*! \brief Decreases the current debug indentation level for
the current thread by 1.
*/
void
unindent(uint8 tabCount)
{
#ifdef USER
tls_set(get_tls_handle(), (void*)(_get_debug_indent_level()-tabCount));
#endif
}
#ifdef USER
/*! \brief Returns the thread local storage handle used to store
indentation information, allocating the handle first if
necessary.
*/
int32
get_tls_handle()
{
// Init the tls handle if this is the first call.
if (!tls_handle_initialized) {
if (atomic_or(&tls_spinlock, 1) == 0) {
// First one in gets to init
tls_handle = tls_allocate();
tls_handle_initialized = true;
atomic_and(&tls_spinlock, 0);
} else {
// All others must wait patiently
while (!tls_handle_initialized) {
snooze(1);
}
}
}
return tls_handle;
}
#endif
/*! \brief Helper class for initializing the debugging output
file.
Note that this hummer isn't threadsafe, but it doesn't really
matter for our concerns, since the worst it'll result in is
a dangling file descriptor, and that would be in the case of
two or more volumes being mounted almost simultaneously...
not too big of a worry.
*/
class DebugOutputFile {
public:
DebugOutputFile(const char *filename = NULL)
: fFile(-1)
{
Init(filename);
}
~DebugOutputFile() {
if (fFile >= 0)
close(fFile);
}
void Init(const char *filename) {
if (fFile < 0 && filename)
fFile = open(filename, O_RDWR | O_CREAT | O_TRUNC);
}
int File() const { return fFile; }
private:
int fFile;
};
DebugOutputFile *out = NULL;
/*! \brief It doesn't appear that the constructor for the global
\c out variable is called when built as an R5 filesystem add-on,
so this function needs to be called in udf_mount to let the
magic happen.
*/
void initialize_debugger(const char *filename)
{
#if DEBUG_TO_FILE
if (!out) {
out = new DebugOutputFile(filename);
dbg_printf("out was NULL!\n");
} else {
DebugOutputFile *temp = out;
out = new DebugOutputFile(filename);
dbg_printf("out was %p!\n", temp);
}
#endif
}
// dbg_printf, stolen from Ingo's ReiserFS::Debug.cpp.
void
dbg_printf(const char *format,...)
{
#if DEBUG_TO_FILE
if (!out)
return;
char buffer[1024];
va_list args;
va_start(args, format);
// no vsnprintf() on PPC and in kernel
#if defined(__INTEL__) && USER
vsnprintf(buffer, sizeof(buffer) - 1, format, args);
#else
vsprintf(buffer, format, args);
#endif
va_end(args);
buffer[sizeof(buffer) - 1] = '\0';
write(out->File(), buffer, strlen(buffer));
#endif
}
//----------------------------------------------------------------------
// DebugHelper
//----------------------------------------------------------------------
/*! \brief Increases the current indentation level.
*/
DebugHelper::DebugHelper(const char *className, uint8 tabCount)
: fTabCount(tabCount)
, fClassName(NULL)
{
indent(fTabCount);
if (className) {
fClassName = (char*)malloc(strlen(className)+1);
if (fClassName)
strcpy(fClassName, className);
}
}
/*! \brief Decreases the current indentation level.
*/
DebugHelper::~DebugHelper()
{
unindent(fTabCount);
free(fClassName);
}
@@ -0,0 +1,233 @@
//----------------------------------------------------------------------
// This software is part of the OpenBeOS distribution and is covered
// by the OpenBeOS license.
//
// This version copyright (c) 2003 Tyler Dauwalder, [email protected]
// Initial version copyright (c) 2002 Axel Dörfler, [email protected]
// dbg_printf() function copyright (c) 2003 Ingo Weinhold, [email protected]
//----------------------------------------------------------------------
#ifndef MY_DEBUG_H
#define MY_DEBUG_H
/*! \file Debug.h
Handy debugging macros.
*/
#include <KernelExport.h>
#include <OS.h>
#ifdef DEBUG
# include <string.h>
#endif
#include <unistd.h>
#define DEBUG_TO_FILE 0
#if DEBUG_TO_FILE
# include <stdio.h>
# include <fcntl.h>
# define __out dbg_printf
void dbg_printf(const char *format,...);
void initialize_debugger(const char *filename);
#else
# ifdef USER
# include <stdio.h>
# define __out printf
# else
# include <null.h>
# define __out dprintf
# endif
#endif
#include "util/kernel_cpp.h"
class DebugHelper;
int32 _get_debug_indent_level();
/*! \brief Helper class that is allocated on the stack by
the \c DEBUG_INIT() macro. On creation, it increases the
current indentation level by the amount specified via its
constructor's \c tabCount parameter; on destruction, it
decreases it.
*/
class DebugHelper
{
public:
DebugHelper(const char *className = NULL, uint8 tabCount = 1);
~DebugHelper();
uint8 TabCount() const { return fTabCount; }
const char* ClassName() const { return fClassName; }
private:
uint8 fTabCount;
char *fClassName;
};
//----------------------------------------------------------------------
// NOTE: See Debug.cpp for complete descriptions of the following
// debug macros.
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// DEBUG-independent macros
//----------------------------------------------------------------------
#define INFORM(x) { __out("session: "); __out x; }
#ifdef USER
# define DIE(x) debugger x
#else
# define DIE(x) kernel_debugger x
#endif
//----------------------------------------------------------------------
// DEBUG-dependent macros
//----------------------------------------------------------------------
#ifdef DEBUG
#if DEBUG_TO_FILE
#define INITIALIZE_DEBUGGING_OUTPUT_FILE(filename) initialize_debugger(filename);
#else
#define INITIALIZE_DEBUGGING_OUTPUT_FILE(filename) ;
#endif
#define DEBUG_INIT_SILENT(className) \
DebugHelper _debugHelper(className, 2);
#define DEBUG_INIT(className) \
DEBUG_INIT_SILENT(className); \
PRINT(("\n"));
#define DEBUG_INIT_ETC(className, arguments) \
DEBUG_INIT_SILENT(className) \
{ \
PRINT_INDENT(); \
if (_debugHelper.ClassName()) { \
__out("session: %s::%s(", \
_debugHelper.ClassName(), __FUNCTION__); \
} else { \
__out("session: %s(", __FUNCTION__); \
} \
__out arguments; \
__out("):\n"); \
}
#define DUMP_INIT(categoryFlags, className) \
DEBUG_INIT_SILENT(className);
#define PRINT(x) { \
{ \
PRINT_INDENT(); \
if (_debugHelper.ClassName()) { \
__out("session: %s::%s(): ", \
_debugHelper.ClassName(), __FUNCTION__); \
} else { \
__out("session: %s(): ", __FUNCTION__); \
} \
__out x; \
} \
}
#define LPRINT(x) { \
{ \
PRINT_INDENT(); \
if (_debugHelper.ClassName()) { \
__out("session: %s::%s(): line %d: ", \
_debugHelper.ClassName(), __FUNCTION__, __LINE__); \
} else { \
__out("session: %s(): line %d: ", \
__FUNCTION__, __LINE__); \
} \
__out x; \
} \
}
#define SIMPLE_PRINT(x) { \
{ \
__out x; \
} \
}
#define PRINT_INDENT() { \
{ \
int32 _level = _get_debug_indent_level(); \
for (int32 i = 0; i < _level-_debugHelper.TabCount(); i++) { \
__out(" "); \
} \
} \
}
#define PRINT_DIVIDER() \
PRINT_INDENT(); \
SIMPLE_PRINT(("------------------------------------------------------------\n"));
#define DUMP(object) \
{ \
(object).dump(); \
}
#define PDUMP(objectPointer) \
{ \
(objectPointer)->dump(); \
}
#define REPORT_ERROR(error) { \
LPRINT(("returning error 0x%lx, `%s'\n", error, strerror(error))); \
}
#define RETURN_ERROR(error) { \
status_t _status = error; \
if (_status < (status_t)B_OK) \
REPORT_ERROR(_status); \
return _status; \
}
#define RETURN(error) { \
status_t _status = error; \
if (_status < (status_t)B_OK) { \
REPORT_ERROR(_status); \
} else if (_status == (status_t)B_OK) { \
LPRINT(("returning B_OK\n")); \
} else { \
LPRINT(("returning 0x%lx = %ld\n", _status, _status)); \
} \
return _status; \
}
#define FATAL(x) { \
PRINT(("fatal error: ")); SIMPLE_PRINT(x); \
}
#define DBG(x) x ;
#else // ifdef DEBUG
#define INITIALIZE_DEBUGGING_OUTPUT_FILE(filename) ;
#define DEBUG_INIT_SILENT(className) ;
#define DEBUG_INIT(className) ;
#define DEBUG_INIT_ETC(className, arguments) ;
#define DUMP_INIT(className) ;
#define PRINT(x) ;
#define LPRINT(x) ;
#define SIMPLE_PRINT(x) ;
#define PRINT_INDENT(x) ;
#define PRINT_DIVIDER() ;
#define DUMP(object) ;
#define PDUMP(objectPointer) ;
#define REPORT_ERROR(status) ;
#define RETURN_ERROR(status) return status;
#define RETURN(status) return status;
#define FATAL(x) { __out("session: fatal error: "); __out x; }
#define DBG(x) ;
#define DUMP(x) ;
#endif // ifdef DEBUG else
#define TRACE(x) DBG(dprintf x)
// These macros turn on or off extensive and generally unnecessary
// debugging output regarding table of contents parsing
//#define WARN(x) (dprintf x)
//#define WARN(x)
#define WARN(x) DBG(dprintf x)
#endif // MY_DEBUG_H
@@ -0,0 +1,962 @@
//----------------------------------------------------------------------
// This software is part of the OpenBeOS distribution and is covered
// by the OpenBeOS license.
//
// Copyright (c) 2003 Tyler Dauwalder, [email protected]
//---------------------------------------------------------------------
/*!
\file Disc.cpp
Disc class implementation, used to enumerate the CD/DVD sessions.
The protocols followed in this module are based on information
taken from the "SCSI-3 Multimedia Commands" draft, revision 10A.
The SCSI command of interest is "READ TOC/PMA/ATIP", command
number \c 0x43.
The format of interest for said command is "Full TOC", format
number \c 0x2.
*/
#include "Disc.h"
#include "Debug.h"
static const char *kModuleDebugName = "session";
//------------------------------------------------------------------------------
// Helper function declarations
//------------------------------------------------------------------------------
static
status_t
read_table_of_contents(int deviceFD, uint32 first_session, uchar *buffer,
uint16 buffer_length, bool msf);
//------------------------------------------------------------------------------
// Helper class declarations
//------------------------------------------------------------------------------
/*! \brief An item that can be stored in a List object.
*/
struct list_item {
public:
list_item(uint32 index, list_item *next = NULL)
: index(index)
, next(next)
{
};
int32 index;
list_item *next;
};
/*! \brief A simple, singly linked list.
*/
class List {
public:
List();
~List();
list_item* Find(int32 index) const;
void Add(list_item *item);
void Clear();
void SortAndRemoveDuplicates();
list_item* First() const;
list_item* Last() const;
private:
list_item *fFirst;
list_item *fLast;
};
/*! \brief Keeps track of track information.
Can be stored in a List object.
*/
struct track : public list_item {
public:
track(uint32 index, off_t startLBA, uint8 control, uint8 adr, track *next = NULL)
: list_item(index, next)
, start_lba(startLBA)
, control(control)
, adr(adr)
{
}
off_t start_lba;
uint8 control; //!< only used to give what are probably useless warnings
uint8 adr; //!< only used to give what are probably useless warnings
};
/*! \brief Keeps track of session information.
Can be stored in a List object.
*/
struct session : public list_item {
public:
session(uint32 index, session *next = NULL);
bool first_track_hint_is_set();
bool last_track_hint_is_set();
bool end_lba_is_set(); // also implies control and adr are set
bool is_audio();
int8 first_track_hint;
int8 last_track_hint;
int8 control;
int8 adr;
off_t end_lba;
List track_list;
};
//------------------------------------------------------------------------------
// List
//------------------------------------------------------------------------------
/*! \brief Creates an empty list.
*/
List::List()
: fFirst(NULL)
, fLast(NULL)
{
}
List::~List()
{
Clear();
}
/*! \brief Returns the ListItem with the given index, or NULL if not found.
*/
list_item*
List::Find(int32 index) const
{
// TRACE(("%s: List::Find(%ld)\n", kModuleDebugName, index));
list_item *item = fFirst;
while (item && item->index != index) {
item = item->next;
}
return item;
}
/*! \brief Adds the given item to the end of the list.
\param item The item to add (may not be NULL)
*/
void
List::Add(list_item *item)
{
// TRACE(("%s: List::Add(%p)\n", kModuleDebugName, item));
if (item) {
item->next = NULL;
if (fLast) {
fLast->next = item;
fLast = item;
} else {
fFirst = fLast = item;
}
} else {
TRACE(("%s: List::Add(): NULL item parameter\n", kModuleDebugName));
}
}
/*! \brief Clears the list.
*/
void
List::Clear()
{
list_item *item = fFirst;
while (item) {
list_item *next = item->next;
delete item;
item = next;
}
fFirst = fLast = NULL;
}
/*! \brief Bubble sorts the list by index, removing any duplicates
(the first instance is kept).
\todo I believe duplicate removal is actually unnecessary, but I need to verify that.
*/
void
List::SortAndRemoveDuplicates()
{
bool sorted = false;
while (!sorted) {
sorted = true;
list_item *prev = NULL;
list_item *item = fFirst;
list_item *next = NULL;
while (item && item->next) {
next = item->next;
// dprintf("List::Sort: %ld -> %ld\n", item->index, next->index);
if (item->index > next->index) {
sorted = false;
// Keep fLast up to date
if (next == fLast)
fLast = item;
// Swap
if (prev) {
// item is not fFirst
prev->next = next;
item->next = next->next;
next->next = item;
} else {
// item must be fFirst
fFirst = next;
item->next = next->next;
next->next = item;
}
} else if (item->index == next->index) {
// Duplicate indicies
TRACE(("%s: List::SortAndRemoveDuplicates: duplicate indicies found (#%ld); "
"keeping first instance\n", kModuleDebugName, item->index));
item->next = next->next;
delete next;
next = item->next;
continue;
}
prev = item;
item = next;
}
}
}
/*! \brief Returns the first item in the list, or NULL if empty
*/
list_item*
List::First() const {
return fFirst;
}
/*! \brief Returns the last item in the list, or NULL if empty
*/
list_item*
List::Last() const {
return fLast;
}
//------------------------------------------------------------------------------
// session
//------------------------------------------------------------------------------
/*! \brief Creates an unitialized session object
*/
session::session(uint32 index, session *next = NULL)
: list_item(index, next)
, first_track_hint(-1)
, last_track_hint(-1)
, control(-1)
, adr(-1)
, end_lba(0)
{
}
/*! \brief Returns true if the \a first_track_hint member has not been
set to a legal value yet.
*/
bool
session::first_track_hint_is_set()
{
return 1 <= first_track_hint && first_track_hint <= 99;
}
/*! \brief Returns true if the \a last_track_hint member has not been
set to a legal value yet.
*/
bool
session::last_track_hint_is_set()
{
return 1 <= last_track_hint && last_track_hint <= 99;
}
/*! \brief Returns true if the \a end_lba member has not been
set to a legal value yet.
The result of this function also signals that the \a control
and \a adr members have or have not been set, since they are
set at the same time as \a end_lba.
*/
bool
session::end_lba_is_set()
{
return end_lba > 0;
}
/*! \brief Returns true if the session is flagged as being audio.
If the \c control value for the session has not been set, returns
false.
*/
bool
session::is_audio()
{
return end_lba_is_set() && !(control & kControlDataTrack);
}
//------------------------------------------------------------------------------
// Disc
//------------------------------------------------------------------------------
/*! \brief Creates a new Disc object by parsing the given table of contents
entries and checking the resultant data structure for errors and
warnings.
If successful, subsequent calls to InitCheck() will return \c B_OK,
elsewise they will return an error code.
*/
Disc::Disc(int fd)
: fInitStatus(B_NO_INIT)
, fSessionList(new List)
{
DEBUG_INIT_ETC("Disc", ("fd: %d", fd));
uchar data[kBlockSize];
/*
if (!error)
error = sessionInfo && index >= 0 ? B_OK : B_BAD_VALUE;
int32 session = index+1;
// Check for a valid session index
if (session < 1 || session > 99)
error = B_ENTRY_NOT_FOUND;
*/
status_t error = fSessionList ? B_OK : B_NO_MEMORY;
// Attempt to read the table of contents, first in lba mode, then in msf mode
if (!error) {
error = read_table_of_contents(fd, 1, data, kBlockSize, false);
}
if (error) {
TRACE(("%s: lba read_toc failed, trying msf instead\n", kModuleDebugName));
error = read_table_of_contents(fd, 1, data, kBlockSize, true);
}
// Interpret the data returned, if successful
if (!error) {
cdrom_table_of_contents_header *header;
cdrom_full_table_of_contents_entry *entries;
int count;
header = (cdrom_table_of_contents_header*)data;
entries = (cdrom_full_table_of_contents_entry*)(data+4);
header->length = B_BENDIAN_TO_HOST_INT16(header->length);
count = (header->length-2) / sizeof(cdrom_full_table_of_contents_entry);
error = _ParseTableOfContents(entries, count);
Dump();
if (!error) {
_SortAndRemoveDuplicates();
error = _CheckForErrorsAndWarnings();
}
}
PRINT(("Setting init status to 0x%lx, `%s'\n", error, strerror(error)));
fInitStatus = error;
}
/*! \brief Destroys the Disc's internal list.
*/
Disc::~Disc() {
delete fSessionList;
}
/*! \brief Returns \c B_OK if the object was successfully initialized, or
an error code if not.
*/
status_t
Disc::InitCheck()
{
return fInitStatus;
}
/*! \brief Stores the info for the given session (using 0 based indicies) in the
struct pointed to by \a sessionInfo.
Returns \c B_ENTRY_NOT_FOUND if no such session exists.
*/
status_t
Disc::GetSessionInfo(int32 index)
{
// TRACE(("%s: Disc::GetSessionInfo(%ld, %ld, %p)\n", kModuleDebugName,
// index, blockSize, sessionInfo));
int32 counter = -1;
for (session *session = (struct session*)fSessionList->First();
session;
session = (struct session*)session->next)
{
if (session->is_audio()) {
counter++; // only one session per audio session
if (counter == index) {
// Found an audio session. Take the start of the first
// track with the end of session.
track *track = (struct track*)session->track_list.First();
if (track) {
TRACE(("%s: found session #%ld info (audio session)\n", kModuleDebugName, index));
off_t start_lba = track->start_lba;
off_t end_lba = session->end_lba;
/* sessionInfo->logical_block_size = blockSize;
sessionInfo->offset = start_lba * blockSize;
sessionInfo->size = (end_lba - start_lba) * blockSize;
sessionInfo->index = index;
sessionInfo->flags = 0;
*/
return B_OK;
} else {
TRACE(("%s: Disc::GetSessionInfo: session #%ld is an audio "
"session with no tracks\n", kModuleDebugName, index));
return B_ENTRY_NOT_FOUND;
}
}
} else {
for (track *track = (struct track*)session->track_list.First();
track;
track = (struct track*)track->next)
{
counter++;
if (counter == index) {
TRACE(("%s: found session #%ld info (data session)\n", kModuleDebugName, index));
off_t start_lba = track->start_lba;
off_t end_lba = track->next
? ((struct track*)track->next)->start_lba
: session->end_lba;
/*
sessionInfo->logical_block_size = blockSize;
sessionInfo->offset = start_lba * blockSize;
sessionInfo->size = (end_lba - start_lba) * blockSize;
sessionInfo->index = index;
sessionInfo->flags = B_DATA_SESSION;
*/
return B_OK;
}
}
}
}
return B_ENTRY_NOT_FOUND;
}
/*! \brief Dumps a printout of the disc using TRACE.
*/
void
Disc::Dump()
{
TRACE(("%s: Disc dump:\n", kModuleDebugName));
session *session = (struct session*)fSessionList->First();
while (session) {
TRACE(("session %ld:\n", session->index));
TRACE((" first track hint: %d\n", session->first_track_hint));
TRACE((" last track hint: %d\n", session->last_track_hint));
TRACE((" end_lba: %lld\n", session->end_lba));
TRACE((" control: %d (%s session, copy %s)\n", session->control,
(session->control & kControlDataTrack ? "data" : "audio"),
(session->control & kControlCopyPermitted ? "permitted" : "prohibited")));
TRACE((" adr: %d\n", session->adr));
track *track = (struct track*)session->track_list.First();
while (track) {
TRACE((" track %ld:\n", track->index));
TRACE((" start_lba: %lld\n", track->start_lba));
track = (struct track*)track->next;
}
session = (struct session*)session->next;
}
}
/*! \brief Reads through the given table of contents data and creates an unsorted,
unverified (i.e. non-error-checked) list of sessions and tracks.
*/
status_t
Disc::_ParseTableOfContents(cdrom_full_table_of_contents_entry entries[], uint32 count)
{
TRACE(("%s: Disc::ParseTableOfContents(%p, %ld)\n", kModuleDebugName, entries, count));
for (uint32 i = 0; i < count; i++) {
// Find or create the appropriate session
uint8 session_index = entries[i].session;
session *session = (struct session*)fSessionList->Find(session_index);
if (!session) {
session = new struct session(session_index);
if (!session) {
return B_NO_MEMORY;
} else {
fSessionList->Add(session);
}
}
uint8 point = entries[i].point;
switch (point) {
// first track hint
case 0xA0:
if (!session->first_track_hint_is_set()) {
int8 first_track_hint = entries[i].pminutes;
if (1 <= first_track_hint && first_track_hint <= 99) {
session->first_track_hint = first_track_hint;
} else {
WARN(("%s: warning: illegal first track hint %d found "
"for session %d\n", kModuleDebugName, first_track_hint,
session_index));
}
} else {
WARN(("%s: warning: duplicated first track hint values found for"
"session %d; using first value encountered: %d", kModuleDebugName,
session_index, session->first_track_hint));
}
break;
// last track hint
case 0xA1:
if (!session->last_track_hint_is_set()) {
int8 last_track_hint = entries[i].pminutes;
if (1 <= last_track_hint && last_track_hint <= 99) {
session->last_track_hint = last_track_hint;
} else {
WARN(("%s: warning: illegal last track hint %d found "
"for session %d\n", kModuleDebugName, last_track_hint,
session_index));
}
} else {
WARN(("%s: warning: duplicate last track hint values found for"
"session %d; using first value encountered: %d", kModuleDebugName,
session_index, session->last_track_hint));
}
break;
// end of session address
case 0xA2:
if (!session->end_lba_is_set()) {
off_t end_lba = msf_to_lba(make_msf_address(
entries[i].pminutes,
entries[i].pseconds,
entries[i].pframes));
if (end_lba > 0) {
session->end_lba = end_lba;
// We also grab the session's control and adr values
// from this entry
session->control = entries[i].control;
session->adr = entries[i].adr;
} else {
WARN(("%s: warning: illegal end lba %lld found "
"for session %d\n", kModuleDebugName, end_lba,
session_index));
}
} else {
WARN(("%s: warning: duplicate end lba values found for"
"session %d; using first value encountered: %lld",
kModuleDebugName, session_index, session->end_lba));
}
break;
// Valid, but uninteresting, points
case 0xB0:
case 0xB1:
case 0xB2:
case 0xB3:
case 0xB4:
case 0xC0:
case 0xC1:
break;
default:
// Anything else had better be a valid track number,
// or it's an invalid point
if (1 <= point && point <= 99) {
// Create and add the track. We'll weed out any duplicates later.
uint8 track_index = point;
off_t start_lba = msf_to_lba(make_msf_address(
entries[i].pminutes,
entries[i].pseconds,
entries[i].pframes));
// The control and adr values grabbed here are only used later on
// to signal a warning if they don't match the corresponding values
// of the parent session.
track *track = new(nothrow) struct track(track_index, start_lba,
entries[i].control, entries[i].adr);
if (!track) {
return B_NO_MEMORY;
} else {
session->track_list.Add(track);
}
} else {
WARN(("%s: warning: illegal point 0x%2x found in table of contents\n",
kModuleDebugName, entries[i].point));
}
break;
}
}
return B_OK;
}
/*! \brief Bubble sorts the session list and each session's track lists,
removing all but the first of any duplicates (by index) found along
the way.
*/
void
Disc::_SortAndRemoveDuplicates()
{
fSessionList->SortAndRemoveDuplicates();
session *session = (struct session*)fSessionList->First();
while (session) {
session->track_list.SortAndRemoveDuplicates();
session = (struct session*)session->next;
}
}
/* \brief Checks the sessions and tracks for any anomalies.
Errors will return an error code, warnings will return B_OK.
Both will print a notification using TRACE.
Anomalies that result in errors:
- Sessions with no end_lba set
- Sessions with no tracks
Anomalies that result in warnings:
- Inaccurate first_track_hint and/or last_track_hint values
- Sequences of sessions or tracks that do not start at 1,
do not end at or before 99, or are not strictly ascending.
(all tracks are checked as a single sequence, since track
numbering does not restart with each session).
- Tracks with different control and/or adr values than their
parent session
Anomalies that are currently *not* checked:
- First Track Hint or Last Track Hint control and adr values
that do not match the values for their session; Ingo's copy
of the BeOS R5 CD is like this, but I don't believe it's
a matter we need to worry about. This could certainly be
changed in the future if needed.
*/
status_t
Disc::_CheckForErrorsAndWarnings() {
int32 lastSessionIndex = 0;
int32 lastTrackIndex = 0;
for (session *session = (struct session*)fSessionList->First();
session;
session = (struct session*)session->next)
{
// Check for errors
//-----------------
// missing end lba
if (!session->end_lba_is_set()) {
TRACE(("%s: Disc::_CheckForErrorsAndWarnings: error: no end of session "
"address for session #%ld\n", kModuleDebugName, session->index));
return B_ERROR;
}
// empty track list
track *track = (struct track*)session->track_list.First();
if (!track) {
TRACE(("%s: Disc::_CheckForErrorsAndWarnings: error: session #%ld has no"
"tracks\n", kModuleDebugName, session->index));
return B_ERROR;
}
// Check for warnings
//-------------------
// incorrect first track hint
if (session->first_track_hint_is_set()
&& session->first_track_hint != track->index)
{
TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: session #%ld: first "
"track hint (%d) doesn't match actual first track (%ld)\n", kModuleDebugName,
session->index, session->first_track_hint, track->index));
}
// incorrect last track hint
struct track *last = (struct track*)session->track_list.Last();
if (session->last_track_hint_is_set() && last
&& session->last_track_hint != last->index)
{
TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: session #%ld: last "
"track hint (%d) doesn't match actual last track (%ld)\n", kModuleDebugName,
session->index, session->last_track_hint, last->index));
}
// invalid session sequence
if (lastSessionIndex+1 != session->index) {
TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: index for session #%ld "
"is out of sequence (should have been #%ld)\n", kModuleDebugName,
session->index, lastSessionIndex));
}
lastSessionIndex = session->index;
for ( ; track; track = (struct track*)track->next) {
// invalid track sequence
if (lastTrackIndex+1 != track->index) {
TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: index for track #%ld "
"is out of sequence (should have been #%ld)\n", kModuleDebugName,
track->index, lastTrackIndex));
}
lastTrackIndex = track->index;
// mismatched control
if (track->control != session->control) {
TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: control for track #%ld "
"(%d, %s track, copy %s) does not match control for parent session #%ld "
"(%d, %s session, copy %s)\n", kModuleDebugName, track->index, track->control,
(track->control & kControlDataTrack ? "data" : "audio"),
(track->control & kControlCopyPermitted ? "permitted" : "prohibited"),
session->index, session->control,
(session->control & kControlDataTrack ? "data" : "audio"),
(session->control & kControlCopyPermitted ? "permitted" : "prohibited")));
}
// mismatched adr
if (track->adr != session->adr) {
TRACE(("%s: Disc::_CheckForErrorsAndWarnings: warning: adr for track #%ld "
"(adr = %d) does not match adr for parent session #%ld (adr = %d)\n", kModuleDebugName,
track->index, track->adr, session->index, session->adr));
}
}
}
return B_OK;
}
//------------------------------------------------------------------------------
// C functions
//------------------------------------------------------------------------------
/*
static
void
dump_scsi_command(raw_device_command *cmd) {
int i;
uint j;
scsi_table_of_contents_command *scsi_command = (scsi_table_of_contents_command*)(&(cmd->command));
for (i = 0; i < cmd->command_length; i++)
TRACE(("%.2x,", cmd->command[i]));
TRACE(("\n"));
TRACE(("raw_device_command:\n"));
TRACE((" command:\n"));
TRACE((" command = %d (0x%.2x)\n", scsi_command->command, scsi_command->command));
TRACE((" msf = %d\n", scsi_command->msf));
TRACE((" format = %d (0x%.2x)\n", scsi_command->format, scsi_command->format));
TRACE((" number = %d\n", scsi_command->number));
TRACE((" length = %d\n", B_BENDIAN_TO_HOST_INT16(scsi_command->length)));
TRACE((" control = %d\n", scsi_command->control));
TRACE((" command_length = %d\n", cmd->command_length));
TRACE((" flags = %d\n", cmd->flags));
TRACE((" scsi_status = 0x%x\n", cmd->scsi_status));
TRACE((" cam_status = 0x%x\n", cmd->cam_status));
TRACE((" data = %p\n", cmd->data));
TRACE((" data_length = %ld\n", cmd->data_length));
TRACE((" sense_data = %p\n", cmd->sense_data));
TRACE((" sense_data_length = %ld\n", cmd->sense_data_length));
TRACE((" timeout = %lld\n", cmd->timeout));
TRACE(("data dump:\n"));
for (j = 0; j < 2048; j++) {//cmd->data_length; j++) {
uchar c = ((uchar*)cmd->data)[j];
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9'))
TRACE(("\\%c,", c));
else
TRACE(("%.2x,", c));
}
TRACE(("\n"));
TRACE(("sense_data dump:\n"));
for (j = 0; j < cmd->sense_data_length; j++) {
uchar c = ((uchar*)cmd->sense_data)[j];
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9'))
TRACE(("%c", c));
else if (c == 0)
TRACE(("_"));
else
TRACE(("-"));
}
TRACE(("\n"));
}
*/
static
void
dump_full_table_of_contents(uchar *data, uint16 data_length)
{
cdrom_table_of_contents_header *header;
cdrom_full_table_of_contents_entry *entries;
int i, count;
int header_length;
header = (cdrom_table_of_contents_header*)data;
entries = (cdrom_full_table_of_contents_entry*)(data+4);
header_length = B_BENDIAN_TO_HOST_INT16(header->length);
if (data_length < header_length) {
TRACE(("dump_full_table_of_contents: warning, data buffer not large enough (%d < %d)\n",
data_length, header_length));
header_length = data_length;
}
TRACE(("%s: table of contents dump:\n", kModuleDebugName));
TRACE(("--------------------------------------------------\n"));
TRACE(("header:\n"));
TRACE((" length = %d\n", header_length));
TRACE((" first = %d\n", header->first));
TRACE((" last = %d\n", header->last));
count = (header_length-2) / sizeof(cdrom_full_table_of_contents_entry);
TRACE(("\n"));
TRACE(("entry count = %d\n", count));
for (i = 0; i < count; i++) {
TRACE(("\n"));
TRACE(("entry #%d:\n", i));
TRACE((" session = %d\n", entries[i].session));
TRACE((" adr = %d\n", entries[i].adr));
TRACE((" control = %d (%s track, copy %s)\n", entries[i].control,
(entries[i].control & kControlDataTrack ? "data" : "audio"),
(entries[i].control & kControlCopyPermitted ? "permitted" : "prohibited")));
TRACE((" tno = %d\n", entries[i].tno));
TRACE((" point = %d (0x%.2x)\n", entries[i].point, entries[i].point));
TRACE((" minutes = %d\n", entries[i].minutes));
TRACE((" frames = %d\n", entries[i].seconds));
TRACE((" seconds = %d\n", entries[i].frames));
TRACE((" zero = %d\n", entries[i].zero));
TRACE((" pminutes = %d\n", entries[i].pminutes));
TRACE((" pseconds = %d\n", entries[i].pseconds));
TRACE((" pframes = %d\n", entries[i].pframes));
TRACE((" lba = %ld\n", msf_to_lba(make_msf_address(entries[i].pminutes,
entries[i].pseconds, entries[i].pframes))));
}
TRACE(("--------------------------------------------------\n"));
}
// read_table_of_contents
static
status_t
read_table_of_contents(int deviceFD, uint32 first_session, uchar *buffer,
uint16 buffer_length, bool msf)
{
scsi_table_of_contents_command scsi_command;
raw_device_command raw_command;
const uint32 sense_data_length = 1024;
uchar sense_data[sense_data_length];
status_t error = buffer ? B_OK : B_BAD_VALUE;
TRACE(("%s: read_table_of_contents: (%d, %p, %d)\n", kModuleDebugName,
deviceFD, buffer, buffer_length));
if (error)
return error;
// Init the scsi command and copy it into the BeOS "raw scsi command" ioctl struct
memset(raw_command.command, 0, 16);
scsi_command.command = 0x43;
scsi_command.msf = 0;
scsi_command.format = 2;
scsi_command.number = first_session;
scsi_command.length = B_HOST_TO_BENDIAN_INT16(buffer_length);
scsi_command.control = 0;
scsi_command.reserved0 = scsi_command.reserved1 = scsi_command.reserved2
= scsi_command.reserved3 = scsi_command.reserved4
= scsi_command.reserved5 = scsi_command.reserved6 = 0;
memcpy(raw_command.command, &scsi_command, sizeof(scsi_command));
// Init the rest of the raw command
raw_command.command_length = 10;
raw_command.flags = kScsiFlags;
raw_command.scsi_status = 0;
raw_command.cam_status = 0;
raw_command.data = buffer;
raw_command.data_length = buffer_length;
memset(raw_command.data, 0, raw_command.data_length);
raw_command.sense_data = sense_data;
raw_command.sense_data_length = sense_data_length;
memset(raw_command.sense_data, 0, raw_command.sense_data_length);
raw_command.timeout = kScsiTimeout;
if (ioctl(deviceFD, B_RAW_DEVICE_COMMAND, &raw_command) == 0) {
if (raw_command.scsi_status == 0 && raw_command.cam_status == 1) {
// SUCCESS!!!
DBG(dump_full_table_of_contents(buffer, buffer_length));
} else {
error = B_FILE_ERROR;
TRACE(("%s: scsi ioctl succeeded, but scsi command failed\n",
kModuleDebugName));
}
} else {
error = errno;
TRACE(("%s: scsi command failed with error 0x%lx\n", kModuleDebugName,
error));
}
return error;
}
// cdrom_session_get_nth_info
/*! \todo Check that read_toc buffer was large enough on success
*/
/*
static
status_t
cdrom_session_get_nth_info(int deviceFD, int32 index, off_t deviceSize,
int32 blockSize, struct session_info *sessionInfo)
{
status_t error = sessionInfo && index >= 0 ? B_OK : B_BAD_VALUE;
uchar data[2048];
int32 session = index+1;
TRACE(("%s: get_nth_info(%d, %ld, %lld, %ld, %p)\n", kModuleDebugName,
deviceFD, index, deviceSize, blockSize, sessionInfo));
// Attempt to read the table of contents, first in lba mode, then in msf mode
if (!error) {
error = read_table_of_contents(deviceFD, 1, data, 2048, false);
}
if (error) {
TRACE(("%s: lba read_toc failed, trying msf instead\n", kModuleDebugName));
error = read_table_of_contents(deviceFD, 1, data, 2048, true);
}
// Interpret the data returned, if successful
if (!error) {
cdrom_table_of_contents_header *header;
cdrom_full_table_of_contents_entry *entries;
int count;
header = (cdrom_table_of_contents_header*)data;
entries = (cdrom_full_table_of_contents_entry*)(data+4);
header->length = B_BENDIAN_TO_HOST_INT16(header->length);
count = (header->length-2) / sizeof(cdrom_full_table_of_contents_entry);
// Check for a valid session index
if (session < 1 || session > 99)
error = B_ENTRY_NOT_FOUND;
// Extract the data of interest
if (!error) {
Disc disc(entries, count);
error = disc.InitCheck();
if (!error)
error = disc.GetSessionInfo(index, blockSize, sessionInfo);
}
}
if (error)
TRACE(("%s: get_nth error 0x%lx\n", kModuleDebugName, error));
return error;
}
*/
@@ -0,0 +1,59 @@
//----------------------------------------------------------------------
// This software is part of the OpenBeOS distribution and is covered
// by the OpenBeOS license.
//
// Copyright (c) 2003 Tyler Dauwalder, [email protected]
//---------------------------------------------------------------------
/*!
\file Disc.cpp
Disc class, used to enumerate the CD/DVD sessions.
*/
#ifndef _DISC_H
#define _DISC_H
#include <errno.h>
#include <new>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ByteOrder.h>
#include <disk_scanner.h>
#include <KernelExport.h>
#include <scsi.h>
#include "Debug.h"
#include "scsi-mmc.h"
class List;
/*! \brief A class that encapsulates a complete, parsed, and error
checked table of contents for a CD-ROM.
*/
class Disc {
public:
Disc(int fd);
~Disc();
status_t InitCheck();
status_t GetSessionInfo(int32 index);
void Dump();
// CDs and DVDs are required to have a block size of 2K by
// the SCSI-3 standard
static const int kBlockSize = 2048;
private:
status_t _ParseTableOfContents(cdrom_full_table_of_contents_entry entries[],
uint32 count);
void _SortAndRemoveDuplicates();
status_t _CheckForErrorsAndWarnings();
status_t fInitStatus;
List *fSessionList;
};
#endif // _DISC_H
@@ -0,0 +1,24 @@
SubDir OBOS_TOP src add-ons kernel partitioning_systems session ;
#UsePrivateHeaders $(DOT) ;
UsePrivateHeaders [ FDirName kernel disk_device_manager ] ;
UsePrivateHeaders [ FDirName kernel ] ;
UsePrivateHeaders [ FDirName storage ] ;
{
local defines = [ FDefines USER ] ;
SubDirCcFlags $(defines) ;
SubDirC++Flags $(defines) ;
}
# For now build a userland version only.
Addon <partitioning_system>session : userland partitioning_systems :
Debug.cpp
Disc.cpp
session.cpp
;
LinkSharedOSLibs <partitioning_system>session :
libkernelland_emu.so
libdisk_device_manager.so
;
@@ -0,0 +1,165 @@
//----------------------------------------------------------------------
// This software is part of the OpenBeOS distribution and is covered
// by the OpenBeOS license.
//
// Copyright (c) 2003 Tyler Dauwalder, [email protected]
//---------------------------------------------------------------------
#ifndef _SCSI_MMC_H
#define _SCSI_MMC_H
/*!
\file scsi-mmc.h
SCSI-3 MMC support structures.
The protocols followed in this module are based on information
taken from the "SCSI-3 Multimedia Commands" draft, revision 10A.
The SCSI command of interest is "READ TOC/PMA/ATIP", command
number \c 0x43.
The format of interest for said command is "Full TOC", format
number \c 0x2.
*/
#include <errno.h>
#include <new>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ByteOrder.h>
#include <disk_scanner.h>
#include <KernelExport.h>
#include <scsi.h>
#include "Debug.h"
/*! \brief raw_device_command flags
This is the only combination of flags that works on my system. :-)
*/
const uint8 kScsiFlags = B_RAW_DEVICE_DATA_IN
| B_RAW_DEVICE_REPORT_RESIDUAL
| B_RAW_DEVICE_SHORT_READ_VALID;
/*! \brief Timeout value used when making scsi commands
I'm honestly not sure what value to use here. It's currently
1 second because I got sick of waiting for the timeout while
figuring out how to get the commands to work.
*/
const uint32 kScsiTimeout = 1000000;
//! SCSI "READ TOC/PMA/ATIP" command struct
typedef struct {
uint8 command; //!< 0x43 == READ TOC/PMA/ATIP
uint8 reserved2:1,
msf:1, //!< addressing format: 0 == LBA, 1 == MSF; try lba first, then msf if that fails
reserved1:3,
reserved0:3;
uint8 format:4, //!< sub-command: 0x0 == "TOC", 0x1 == "Session Info", 0x2 == "Full TOC", ...
reserved3:4;
uint8 reserved4;
uint8 reserved5;
uint8 reserved6;
uint8 number; //!< track/session number
uint16 length; //!< length of data buffer passed in raw_device_command.data; BIG-ENDIAN!!!
uint8 control; //!< control codes; 0x0 should be fine
} __attribute__((packed)) scsi_table_of_contents_command;
// Some of the possible "READ TOC/PMA/ATIP" formats
const uint8 kTableOfContentsFormat = 0x00;
const uint8 kSessionFormat = 0x01;
const uint8 kFullTableOfContentsFormat = 0x10; //!< "READ TOC/PMA/ATIP" format of interest
/*! \brief Minutes:Seconds:Frames format address
- Each msf frame corresponds to one logical block.
- Each msf second corresponds to 75 msf frames
- Each msf minute corresponds to 60 msf seconds
- Logical block 0 is at msf address 00:02:00
*/
typedef struct {
uint8 reserved;
uint8 minutes;
uint8 seconds;
uint8 frames;
} msf_address;
#define CDROM_FRAMES_PER_SECOND (75)
#define CDROM_FRAMES_PER_MINUTE (CDROM_FRAMES_PER_SECOND*60)
/*! \brief Returns an initialized \c msf_address struct
*/
static
inline
msf_address
make_msf_address(uint8 minutes, uint8 seconds, uint8 frames)
{
msf_address result;
result.reserved = 0;
result.minutes = minutes;
result.seconds = seconds;
result.frames = frames;
return result;
}
/*! \brief Converts the given msf address to lba format
*/
static
inline
uint32
msf_to_lba(msf_address msf)
{
return (CDROM_FRAMES_PER_MINUTE * msf.minutes)
+ (CDROM_FRAMES_PER_SECOND * msf.seconds)
+ msf.frames - 150;
}
/*! \brief Header for data returned by all formats of SCSI
"READ TOC/PMA/ATIP" command.
*/
typedef struct {
uint16 length; //!< Length of data in reply (not including these 2 bytes); BIG ENDIAN!!!
uint8 first; //!< First track/session in reply
uint8 last; //!< Last track/session in reply
} cdrom_table_of_contents_header;
/*! \brief Type of entries returned by "READ TOC/PMA/ATIP" when called with format
\c kTableOfContentsFormat == 0x00
*/
typedef struct {
uint8 reserved0;
uint8 control:4,
adr:4;
uint8 track_number;
uint8 reserved1;
uint32 address;
} cdrom_table_of_contents_entry;
/*! \brief Type of entries returned by "READ TOC/PMA/ATIP" when called with format
\c kFullTableOfContentsFormat == 0x10
*/
typedef struct {
uint8 session;
uint8 control:4,
adr:4;
uint8 tno;
uint8 point;
uint8 minutes;
uint8 seconds;
uint8 frames;
uint8 zero;
uint8 pminutes;
uint8 pseconds;
uint8 pframes;
} cdrom_full_table_of_contents_entry;
/*! \brief Bitflags for control entries
*/
enum {
kControlDataTrack = 0x4,
kControlCopyPermitted = 0x2,
};
#endif // _SCSI_MMC_H
@@ -0,0 +1,190 @@
//----------------------------------------------------------------------
// This software is part of the OpenBeOS distribution and is covered
// by the OpenBeOS license.
//
// Copyright (c) 2003 Tyler Dauwalder, [email protected]
//---------------------------------------------------------------------
/*!
\file session.cpp
\brief Disk device manager partition module for CD/DVD sessions.
*/
#include <ddm_modules.h>
#include <DiskDeviceTypes.h>
#include <KernelExport.h>
#include <unistd.h>
#include "Debug.h"
#include "Disc.h"
static const char *kSessionModuleName = "partitioning_systems/session/v1";
static status_t standard_operations(int32 op, ...);
static float identify_partition(int fd, partition_data *partition,
void **cookie);
static status_t scan_partition(int fd, partition_data *partition,
void *cookie);
static void free_identify_partition_cookie(partition_data *partition,
void *cookie);
static void free_partition_cookie(partition_data *partition);
static void free_partition_content_cookie(partition_data *partition);
static partition_module_info session_module = {
{
kSessionModuleName,
0,
standard_operations
},
kPartitionTypeMultisession, // pretty_name
0, // flags
// scanning
identify_partition, // identify_partition
scan_partition, // scan_partition
free_identify_partition_cookie, // free_identify_partition_cookie
free_partition_cookie, // free_partition_cookie
free_partition_content_cookie, // free_partition_content_cookie
// querying
NULL, // supports_repairing
NULL, // supports_resizing
NULL, // supports_resizing_child
NULL, // supports_moving
NULL, // supports_moving_child
NULL, // supports_setting_name
NULL, // supports_setting_content_name
NULL, // supports_setting_type
NULL, // supports_setting_parameters
NULL, // supports_setting_content_parameters
NULL, // supports_initializing
NULL, // supports_initializing_child
NULL, // supports_creating_child
NULL, // supports_deleting_child
NULL, // is_sub_system_for
NULL, // validate_resize
NULL, // validate_resize_child
NULL, // validate_move
NULL, // validate_move_child
NULL, // validate_set_name
NULL, // validate_set_content_name
NULL, // validate_set_type
NULL, // validate_set_parameters
NULL, // validate_set_content_parameters
NULL, // validate_initialize
NULL, // validate_create_child
NULL, // get_partitionable_spaces
NULL, // get_next_supported_type
NULL, // get_type_for_content_type
// shadow partition modification
NULL, // shadow_changed
// writing
NULL, // repair
NULL, // resize
NULL, // resize_child
NULL, // move
NULL, // move_child
NULL, // set_name
NULL, // set_content_name
NULL, // set_type
NULL, // set_parameters
NULL, // set_content_parameters
NULL, // initialize
NULL, // create_child
NULL, // delete_child
};
extern "C" partition_module_info *modules[];
_EXPORT partition_module_info *modules[] =
{
&session_module,
NULL
};
static
status_t
standard_operations(int32 op, ...)
{
status_t error = B_ERROR;
switch(op) {
case B_MODULE_INIT:
case B_MODULE_UNINIT:
error = B_OK;
break;
}
return error;
}
static
float
identify_partition(int fd, partition_data *partition, void **cookie)
{
DEBUG_INIT_ETC(NULL, ("fd: %d, id: %ld, offset: %Ld, "
"size: %Ld, block_size: %ld", fd,
partition->id, partition->offset, partition->size,
partition->block_size));
device_geometry geometry;
float result = -1;
if (partition->block_size == 2048
&& ioctl(fd, B_GET_GEOMETRY, &geometry) == 0
&& geometry.device_type == B_CD)
{
Disc *disc = new Disc(fd);
if (disc && disc->InitCheck() == B_OK) {
*cookie = static_cast<void*>(disc);
result = 0.5;
}
}
PRINT(("returning %4.2f\n", result));
return result;
}
static
status_t
scan_partition(int fd, partition_data *partition, void *cookie)
{
DEBUG_INIT_ETC(NULL, ("fd: %d, id: %ld, offset: %Ld, size: %Ld, "
"block_size: %ld, cookie: %p", fd, partition->id, partition->offset,
partition->size, partition->block_size, cookie));
status_t error = fd >= 0 && partition && cookie ? B_OK : B_BAD_VALUE;
if (!error) {
Disc *disc = static_cast<Disc*>(cookie);
partition->status = B_PARTITION_VALID;
partition->flags |= B_PARTITION_PARTITIONING_SYSTEM
| B_PARTITION_READ_ONLY;
partition->content_size = partition->size;
partition->content_cookie = disc;
for (int i = 1; disc->GetSessionInfo(i) == B_OK; i++) {
}
}
PRINT(("error: 0x%lx, `%s'\n", error, strerror(error)));
RETURN(B_ERROR);
}
static
void
free_identify_partition_cookie(partition_data */*partition*/, void *cookie)
{
if (cookie) {
DEBUG_INIT_ETC(NULL, ("cookie: %p", cookie));
}
}
static
void
free_partition_cookie(partition_data *partition)
{
DEBUG_INIT(NULL);
}
static
void
free_partition_content_cookie(partition_data *partition)
{
DEBUG_INIT(NULL);
}