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