From 8ba0b6e187f087c181fbecc61fa00509ac8e6050 Mon Sep 17 00:00:00 2001 From: Tyler Dauwalder Date: Sat, 15 Nov 2003 00:54:59 +0000 Subject: [PATCH] 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 --- .../partitioning_systems/session/Debug.cpp | 343 +++++++ .../partitioning_systems/session/Debug.h | 233 +++++ .../partitioning_systems/session/Disc.cpp | 962 ++++++++++++++++++ .../partitioning_systems/session/Disc.h | 59 ++ .../partitioning_systems/session/Jamfile | 24 + .../partitioning_systems/session/scsi-mmc.h | 165 +++ .../partitioning_systems/session/session.cpp | 190 ++++ 7 files changed, 1976 insertions(+) create mode 100644 src/add-ons/kernel/partitioning_systems/session/Debug.cpp create mode 100644 src/add-ons/kernel/partitioning_systems/session/Debug.h create mode 100644 src/add-ons/kernel/partitioning_systems/session/Disc.cpp create mode 100644 src/add-ons/kernel/partitioning_systems/session/Disc.h create mode 100644 src/add-ons/kernel/partitioning_systems/session/Jamfile create mode 100644 src/add-ons/kernel/partitioning_systems/session/scsi-mmc.h create mode 100644 src/add-ons/kernel/partitioning_systems/session/session.cpp diff --git a/src/add-ons/kernel/partitioning_systems/session/Debug.cpp b/src/add-ons/kernel/partitioning_systems/session/Debug.cpp new file mode 100644 index 0000000000..fc1897b078 --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/session/Debug.cpp @@ -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, tyler@dauwalder.net +// Initial version copyright (c) 2002 Axel Dörfler, axeld@pinc-software.de +//---------------------------------------------------------------------- + +/*! \file Debug.cpp + + Support code for handy debugging macros. +*/ + +#include "Debug.h" + +#include +#include + +//---------------------------------------------------------------------- +// 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); +} + diff --git a/src/add-ons/kernel/partitioning_systems/session/Debug.h b/src/add-ons/kernel/partitioning_systems/session/Debug.h new file mode 100644 index 0000000000..fcec3776ce --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/session/Debug.h @@ -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, tyler@dauwalder.net +// Initial version copyright (c) 2002 Axel Dörfler, axeld@pinc-software.de +// dbg_printf() function copyright (c) 2003 Ingo Weinhold, bonefish@cs.tu-berlin.edu +//---------------------------------------------------------------------- +#ifndef MY_DEBUG_H +#define MY_DEBUG_H + +/*! \file Debug.h + + Handy debugging macros. +*/ + +#include + +#include +#ifdef DEBUG +# include +#endif +#include + +#define DEBUG_TO_FILE 0 + +#if DEBUG_TO_FILE +# include +# include +# define __out dbg_printf + void dbg_printf(const char *format,...); + void initialize_debugger(const char *filename); +#else +# ifdef USER +# include +# define __out printf +# else +# include +# 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 diff --git a/src/add-ons/kernel/partitioning_systems/session/Disc.cpp b/src/add-ons/kernel/partitioning_systems/session/Disc.cpp new file mode 100644 index 0000000000..e45ce1cd23 --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/session/Disc.cpp @@ -0,0 +1,962 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- +/*! + \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; +} +*/ diff --git a/src/add-ons/kernel/partitioning_systems/session/Disc.h b/src/add-ons/kernel/partitioning_systems/session/Disc.h new file mode 100644 index 0000000000..496ef1f3fa --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/session/Disc.h @@ -0,0 +1,59 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- +/*! + \file Disc.cpp + + Disc class, used to enumerate the CD/DVD sessions. +*/ + +#ifndef _DISC_H +#define _DISC_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 diff --git a/src/add-ons/kernel/partitioning_systems/session/Jamfile b/src/add-ons/kernel/partitioning_systems/session/Jamfile new file mode 100644 index 0000000000..f59f5bad36 --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/session/Jamfile @@ -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 session : userland partitioning_systems : + Debug.cpp + Disc.cpp + session.cpp +; + +LinkSharedOSLibs session : + libkernelland_emu.so + libdisk_device_manager.so +; diff --git a/src/add-ons/kernel/partitioning_systems/session/scsi-mmc.h b/src/add-ons/kernel/partitioning_systems/session/scsi-mmc.h new file mode 100644 index 0000000000..4230a23f67 --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/session/scsi-mmc.h @@ -0,0 +1,165 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- +#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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 diff --git a/src/add-ons/kernel/partitioning_systems/session/session.cpp b/src/add-ons/kernel/partitioning_systems/session/session.cpp new file mode 100644 index 0000000000..db8b4df5d0 --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/session/session.cpp @@ -0,0 +1,190 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- +/*! + \file session.cpp + \brief Disk device manager partition module for CD/DVD sessions. +*/ + +#include +#include +#include +#include + +#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(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(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); +}