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:
@@ -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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user