summaryrefslogtreecommitdiffstats
path: root/libk3bdevice
diff options
context:
space:
mode:
Diffstat (limited to 'libk3bdevice')
-rw-r--r--libk3bdevice/Makefile.am34
-rw-r--r--libk3bdevice/configure.in.bot20
-rw-r--r--libk3bdevice/configure.in.in212
-rw-r--r--libk3bdevice/k3bcdtext.cpp685
-rw-r--r--libk3bdevice/k3bcdtext.h201
-rw-r--r--libk3bdevice/k3bcrc.cpp80
-rw-r--r--libk3bdevice/k3bcrc.h35
-rw-r--r--libk3bdevice/k3bdebug.cpp137
-rw-r--r--libk3bdevice/k3bdebug.h63
-rw-r--r--libk3bdevice/k3bdevice.cpp3650
-rw-r--r--libk3bdevice/k3bdevice.h836
-rw-r--r--libk3bdevice/k3bdevice_export.h33
-rw-r--r--libk3bdevice/k3bdevice_mmc.cpp947
-rw-r--r--libk3bdevice/k3bdeviceglobals.cpp247
-rw-r--r--libk3bdevice/k3bdeviceglobals.h54
-rw-r--r--libk3bdevice/k3bdevicemanager.cpp903
-rw-r--r--libk3bdevice/k3bdevicemanager.h247
-rw-r--r--libk3bdevice/k3bdevicetypes.h266
-rw-r--r--libk3bdevice/k3bdiskinfo.cpp266
-rw-r--r--libk3bdevice/k3bdiskinfo.h182
-rw-r--r--libk3bdevice/k3bhalconnection.cpp610
-rw-r--r--libk3bdevice/k3bhalconnection.h223
-rw-r--r--libk3bdevice/k3bmmc.h697
-rw-r--r--libk3bdevice/k3bmsf.cpp335
-rw-r--r--libk3bdevice/k3bmsf.h118
-rw-r--r--libk3bdevice/k3bscsicommand.cpp218
-rw-r--r--libk3bdevice/k3bscsicommand.h142
-rw-r--r--libk3bdevice/k3bscsicommand_bsd.cpp208
-rw-r--r--libk3bdevice/k3bscsicommand_linux.cpp177
-rw-r--r--libk3bdevice/k3bscsicommand_netbsd.cpp111
-rw-r--r--libk3bdevice/k3btoc.cpp163
-rw-r--r--libk3bdevice/k3btoc.h101
-rw-r--r--libk3bdevice/k3btrack.cpp123
-rw-r--r--libk3bdevice/k3btrack.h151
-rw-r--r--libk3bdevice/libk3bdevice.doxy214
35 files changed, 12689 insertions, 0 deletions
diff --git a/libk3bdevice/Makefile.am b/libk3bdevice/Makefile.am
new file mode 100644
index 0000000..bd6aaf8
--- /dev/null
+++ b/libk3bdevice/Makefile.am
@@ -0,0 +1,34 @@
+AM_CPPFLAGS= -I$(srcdir)/libk3bdevice -I$(srcdir) $(all_includes) $(HAL_INCS) $(DBUS_INCS) $(DBUSQT_INCS)
+
+KDE_CXXFLAGS = $(ENABLE_PERMISSIVE_FLAG)
+
+METASOURCES = AUTO
+
+lib_LTLIBRARIES = libk3bdevice.la
+
+libk3bdevice_la_LIBADD = $(LIB_KIO) $(RESMGR_LIB) $(CAM_LIB) $(HAL_DBUS_LIBS)
+
+# lib version 5 for K3b 1.0
+libk3bdevice_la_LDFLAGS = $(all_libraries) -version-info 5:0:0 -no-undefined
+
+if include_HAL
+libk3bdevice_la_SOURCES = k3bdevice.cpp k3bdevice_mmc.cpp k3bscsicommand.cpp \
+k3btrack.cpp k3btoc.cpp k3bdevicemanager.cpp k3bmsf.cpp k3bdiskinfo.cpp \
+k3bdeviceglobals.cpp k3bcrc.cpp k3bcdtext.cpp k3bhalconnection.cpp \
+k3bdebug.cpp
+
+include_HEADERS = k3bdevicemanager.h k3bdevice.h k3btoc.h k3btrack.h \
+k3bdeviceglobals.h k3bdiskinfo.h k3bcdtext.h k3bmsf.h k3bdevicetypes.h \
+k3bdevice_export.h k3bhalconnection.h k3bdebug.h
+else
+libk3bdevice_la_SOURCES = k3bdevice.cpp k3bdevice_mmc.cpp k3bscsicommand.cpp \
+k3btrack.cpp k3btoc.cpp k3bdevicemanager.cpp k3bmsf.cpp k3bdiskinfo.cpp \
+k3bdeviceglobals.cpp k3bcrc.cpp k3bcdtext.cpp k3bdebug.cpp
+
+include_HEADERS = k3bdevicemanager.h k3bdevice.h k3btoc.h k3btrack.h \
+k3bdeviceglobals.h k3bdiskinfo.h k3bcdtext.h k3bmsf.h k3bdevicetypes.h \
+k3bdevice_export.h k3bdebug.h
+endif
+
+messages: rc.cpp
+ $(XGETTEXT) `find -name "*.cpp" -or -name "*.h"` -o $(podir)/libk3bdevice.pot
diff --git a/libk3bdevice/configure.in.bot b/libk3bdevice/configure.in.bot
new file mode 100644
index 0000000..fbb068d
--- /dev/null
+++ b/libk3bdevice/configure.in.bot
@@ -0,0 +1,20 @@
+echo ""
+
+if test -n "$RESMGR_LIB"; then
+ echo "K3b - Resmgr support: yes"
+else
+ echo "K3b - Resmgr support: no"
+fi
+
+echo ""
+
+
+if test x$have_hal = xyes; then
+ echo "K3b - Compile HAL support yes"
+else
+ echo "K3b - Compile HAL support no"
+if test "x$ac_cv_use_hal" = "xyes" ; then
+ echo "K3b - You are missing the HAL >= 0.5 headers and libraries"
+ echo "K3b - or the DBus Qt bindings."
+fi
+fi
diff --git a/libk3bdevice/configure.in.in b/libk3bdevice/configure.in.in
new file mode 100644
index 0000000..5cabeec
--- /dev/null
+++ b/libk3bdevice/configure.in.in
@@ -0,0 +1,212 @@
+dnl FIXME: only make the linux header check on linux systems.
+
+linux_scsi=no
+AC_MSG_CHECKING(for linux scsi headers)
+AC_LANG_SAVE
+AC_LANG_CPLUSPLUS
+AC_TRY_COMPILE([
+ #include <linux/version.h>
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,50)
+ typedef unsigned char u8;
+ #endif
+ #include <sys/types.h>
+ #include <linux/../scsi/scsi.h> /* cope with silly includes */
+ ],
+ [],
+ [linux_scsi=yes])
+AC_MSG_RESULT($linux_scsi)
+
+case "$host_os" in
+freebsd*|dragonfly*)
+ # I'll be damned if lousy coding prevents us from running
+ # this application.
+ linux_scsi=yes
+ ;;
+esac
+
+if test "x$linux_scsi" = "xno" ; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE k3b"
+fi
+AC_LANG_RESTORE
+
+dnl - find the cam_* functions
+AC_CHECK_FUNC(cam_close_device,
+ [CAM_LIB=""],
+ [AC_CHECK_LIB(cam, cam_close_device, [CAM_LIB=-lcam], [CAM_LIB=""])]
+ )
+AC_SUBST(CAM_LIB)
+
+
+
+dnl === check for resmgr - begin ============
+AC_ARG_WITH(
+ resmgr,
+ AS_HELP_STRING([--without-resmgr], [build K3b without ResMgr support (default=no)]),
+ [ac_cv_use_resmgr=$withval],
+ [ac_cv_use_resmgr=yes]
+)
+
+if test "$ac_cv_use_resmgr" = "yes"; then
+ RESMGR_LIB=""
+ KDE_CHECK_HEADERS(resmgr.h, [
+ KDE_CHECK_LIB(resmgr,rsm_open_device,[
+ RESMGR_LIB="-lresmgr"
+ AC_DEFINE(HAVE_RESMGR,1,[defined if you have resmgr libraries and headers])
+ ])
+ ])
+ AC_SUBST(RESMGR_LIB)
+fi
+dnl === check for resmgr - end ============
+
+
+
+
+
+# HAL check from kdebase/kioslave/media
+
+AC_ARG_WITH(
+ hal,
+ AS_HELP_STRING(
+ [--without-hal],
+ [build K3b without HAL support (default=no)]),
+ [ac_cv_use_hal=$withval],
+ [ac_cv_use_hal=yes]
+)
+
+if test "x$ac_cv_use_hal" = "xyes" ; then
+
+########### Check for the HAL
+
+ AC_MSG_CHECKING(for the HAL)
+
+ hal_inc=NOTFOUND
+ hal_lib=NOTFOUND
+ hal=NOTFOUND
+
+ search_incs="$kde_includes /usr/include /usr/include/hal /usr/local/include /usr/local/include/hal"
+ AC_FIND_FILE(libhal.h libhal-storage.h, $search_incs, hal_incdir)
+
+ if [test -r $hal_incdir/libhal.h] ; then
+ HAL_INCS="-I$hal_incdir"
+ hal_inc=FOUND
+ fi
+
+ if test -r $hal_incdir/libhal-storage.h ; then
+ hal_storage_version=4
+ grep LibHalVolume $hal_incdir/libhal-storage.h \
+ > /dev/null 2>&1 && hal_storage_version=5
+ if test $hal_storage_version = 4 ; then
+ AC_DEFINE(HAL_0_4, , [HAL API version 0.4])
+ fi
+ fi
+
+ search_libs="$kde_libraries /usr/lib64 /usr/lib /usr/local/lib /lib /lib64"
+ AC_FIND_FILE(libhal.so, $search_libs, hal_libdir)
+
+ if [test -r $hal_libdir/libhal.so] ; then
+ HAL_LIBS="-L$hal_libdir -lhal"
+ hal_lib=FOUND
+ fi
+
+
+ if [test $hal_inc = FOUND] && [test $hal_lib = FOUND] ; then
+ AC_MSG_RESULT(headers $hal_incdir libraries $hal_libdir)
+ hal=FOUND
+ else
+ AC_MSG_RESULT(searched but not found)
+ fi
+
+ AC_SUBST(HAL_INCS)
+ AC_SUBST(HAL_LIBS)
+
+
+########### Check for DBus
+
+ AC_MSG_CHECKING(for DBus)
+
+ dbus_inc=NOTFOUND
+ dbus_lib=NOTFOUND
+ dbus=NOTFOUND
+
+ search_incs="$kde_includes /usr/include /usr/include/dbus-1.0 /usr/local/include /usr/local/include/dbus-1.0"
+ AC_FIND_FILE(dbus/dbus.h, $search_incs, dbus_incdir)
+
+ search_incs_arch_deps="$kde_includes /usr/lib64/dbus-1.0/include /usr/lib/dbus-1.0/include /usr/local/lib/dbus-1.0/include"
+ AC_FIND_FILE(dbus/dbus-arch-deps.h, $search_incs_arch_deps, dbus_incdir_arch_deps)
+
+ if [test -r $dbus_incdir/dbus/dbus.h] && [test -r $dbus_incdir_arch_deps/dbus/dbus-arch-deps.h] ; then
+ DBUS_INCS="-I$dbus_incdir -I$dbus_incdir_arch_deps"
+ dbus_inc=FOUND
+ fi
+
+ search_libs="$kde_libraries /usr/lib64 /usr/lib /usr/local/lib /lib /lib64"
+ AC_FIND_FILE(libdbus-1.so, $search_libs, dbus_libdir)
+
+ if test -r $dbus_libdir/libdbus-1.so ; then
+ DBUS_LIBS="-L$dbus_libdir -ldbus-1"
+ dbus_lib=FOUND
+ fi
+
+ if [test $dbus_inc = FOUND] && [test $dbus_lib = FOUND] ; then
+ AC_MSG_RESULT(headers $dbus_incdir $dbus_incdir_arch_deps libraries $dbus_libdir)
+ dbus=FOUND
+ else
+ AC_MSG_RESULT(searched but not found)
+ fi
+
+ AC_SUBST(DBUS_INCS)
+ AC_SUBST(DBUS_LIBS)
+
+########### Check for DBus-Qt bindings
+
+ AC_MSG_CHECKING(for DBus-Qt bindings)
+
+ dbusqt_inc=NOTFOUND
+ dbusqt_lib=NOTFOUND
+ dbusqt=NOTFOUND
+
+ search_incs="$kde_includes /usr/include /usr/include/dbus-1.0 /usr/local/include /usr/local/include/dbus-1.0"
+ AC_FIND_FILE(dbus/connection.h, $search_incs, dbusqt_incdir)
+
+ if test -r $dbusqt_incdir/dbus/connection.h ; then
+ have_qt_patch=0
+ grep dbus_connection_setup_with_qt_main $dbusqt_incdir/dbus/connection.h \
+ > /dev/null 2>&1 && have_qt_patch=1
+ if test $have_qt_patch = 1 ; then
+ DBUSQT_INCS="-I$dbusqt_incdir"
+ dbusqt_inc=FOUND
+ fi
+ fi
+
+ search_libs="$kde_libraries /usr/lib /usr/lib64 /usr/local/lib"
+ AC_FIND_FILE(libdbus-qt-1.so, $search_libs, dbusqt_libdir)
+
+ if test -r $dbusqt_libdir/libdbus-qt-1.so ; then
+ DBUSQT_LIBS="-L$dbusqt_libdir -ldbus-qt-1"
+ dbusqt_lib=FOUND
+ fi
+
+ if [test $dbusqt_inc = FOUND] && [test $dbusqt_lib = FOUND] ; then
+ AC_MSG_RESULT(headers $dbusqt_incdir libraries $dbusqt_libdir)
+ dbusqt=FOUND
+ else
+ AC_MSG_RESULT(searched but not found)
+ fi
+
+ AC_SUBST(DBUSQT_INCS)
+ AC_SUBST(DBUSQT_LIBS)
+fi
+
+########### Check if media HAL backend sould be compiled
+
+have_hal=no
+HAL_DBUS_LIBS=""
+if [test "x$hal" = "xFOUND"] && [test "x$dbus" = "xFOUND"] && [test "x$dbusqt" = "xFOUND"] && [ test $hal_storage_version = 5 ] ; then
+ AC_DEFINE(HAVE_HAL, , [compile in HAL support])
+ have_hal=yes
+ HAL_DBUS_LIBS="$HAL_LIBS $DBUS_LIBS $DBUSQT_LIBS"
+fi
+
+AM_CONDITIONAL(include_HAL, [test x$have_hal = xyes])
+AC_SUBST(HAL_DBUS_LIBS)
+
diff --git a/libk3bdevice/k3bcdtext.cpp b/libk3bdevice/k3bcdtext.cpp
new file mode 100644
index 0000000..713f7dd
--- /dev/null
+++ b/libk3bdevice/k3bcdtext.cpp
@@ -0,0 +1,685 @@
+/*
+ *
+ * $Id: k3bcdtext.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include <config.h>
+
+#include "k3bcdtext.h"
+#include "k3bcrc.h"
+
+#include <k3bdebug.h>
+
+#include <qtextcodec.h>
+
+#include <string.h>
+
+
+namespace K3bDevice {
+
+ struct cdtext_pack {
+ unsigned char id1;
+ unsigned char id2;
+ unsigned char id3;
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char dbcc: 1;
+ unsigned char blocknum: 3;
+ unsigned char charpos: 4;
+#else
+ unsigned char charpos: 4;
+ unsigned char blocknum: 3;
+ unsigned char dbcc: 1;
+#endif
+ unsigned char data[12];
+ unsigned char crc[2];
+ };
+
+ /**
+ * This one is taken from cdrecord
+ */
+ struct text_size_block {
+ char charcode;
+ char first_track;
+ char last_track;
+ char copyr_flags;
+ char pack_count[16];
+ char last_seqnum[8];
+ char language_codes[8];
+ };
+
+ void debugRawTextPackData( const unsigned char* data, int dataLen )
+ {
+ k3bDebug() << endl << " id1 | id2 | id3 | charps | blockn | dbcc | data | crc |" << endl;
+
+ cdtext_pack* pack = (cdtext_pack*)data;
+
+ for( int i = 0; i < dataLen/18; ++i ) {
+ QString s;
+ s += QString( " %1 |" ).arg( pack[i].id1, 6, 16 );
+ s += QString( " %1 |" ).arg( pack[i].id2, 6 );
+ s += QString( " %1 |" ).arg( pack[i].id3, 6 );
+ s += QString( " %1 |" ).arg( pack[i].charpos, 6 );
+ s += QString( " %1 |" ).arg( pack[i].blocknum, 6 );
+ s += QString( " %1 |" ).arg( pack[i].dbcc, 4 );
+// char str[12];
+// sprintf( str, "%c%c%c%c%c%c%c%c%c%c%c%c",
+// pack[i].data[0] == '\0' ? '°' : pack[i].data[0],
+// pack[i].data[1] == '\0' ? '°' : pack[i].data[1],
+// pack[i].data[2] == '\0' ? '°' : pack[i].data[2],
+// pack[i].data[3] == '\0' ? '°' : pack[i].data[3],
+// pack[i].data[4] == '\0' ? '°' : pack[i].data[4],
+// pack[i].data[5] == '\0' ? '°' : pack[i].data[5],
+// pack[i].data[6] == '\0' ? '°' : pack[i].data[6],
+// pack[i].data[7] == '\0' ? '°' : pack[i].data[7],
+// pack[i].data[8] == '\0' ? '°' : pack[i].data[8],
+// pack[i].data[9] == '\0' ? '°' : pack[i].data[9],
+// pack[i].data[10] == '\0' ? '°' : pack[i].data[10],
+// pack[i].data[11] == '\0' ? '°' : pack[i].data[11] );
+// s += QString( " %1 |" ).arg( "'" + QCString(str,13) + "'", 14 );
+// Q_UINT16 crc = pack[i].crc[0]<<8|pack[i].crc[1];
+// s += QString( " %1 |" ).arg( crc );
+ k3bDebug() << s << endl;
+ }
+ }
+
+}
+
+
+
+K3bDevice::CdText::CdText()
+{
+}
+
+
+K3bDevice::CdText::CdText( const K3bDevice::CdText& text )
+ : QValueVector<K3bDevice::TrackCdText>( text ),
+ m_title( text.title() ),
+ m_performer( text.performer() ),
+ m_songwriter( text.songwriter() ),
+ m_composer( text.composer() ),
+ m_arranger( text.arranger() ),
+ m_message( text.message() ),
+ m_discId( text.discId() ),
+ m_upcEan( text.upcEan() )
+{
+}
+
+
+K3bDevice::CdText::CdText( const unsigned char* data, int len )
+{
+ setRawPackData( data, len );
+}
+
+
+K3bDevice::CdText::CdText( const QByteArray& b )
+{
+ setRawPackData( b );
+}
+
+
+K3bDevice::CdText::CdText( int size )
+{
+ resize( size );
+}
+
+
+void K3bDevice::CdText::clear()
+{
+ QValueVector<TrackCdText>::clear();
+
+ m_title.setLength(0);
+ m_performer.setLength(0);
+ m_songwriter.setLength(0);
+ m_composer.setLength(0);
+ m_arranger.setLength(0);
+ m_message.setLength(0);
+ m_discId.setLength(0);
+ m_upcEan.setLength(0);
+}
+
+
+void K3bDevice::CdText::setRawPackData( const unsigned char* data, int len )
+{
+ clear();
+
+ int r = len%18;
+ if( r > 0 && r != 4 ) {
+ k3bDebug() << "(K3bDevice::CdText) invalid cdtext size: " << len << endl;
+ }
+ else if( len-r > 0 ) {
+ debugRawTextPackData( &data[r], len-r );
+
+ cdtext_pack* pack = (cdtext_pack*)&data[r];
+
+
+ for( int i = 0; i < (len-r)/18; ++i ) {
+
+ if( pack[i].dbcc ) {
+ k3bDebug() << "(K3bDevice::CdText) Double byte code not supported" << endl;
+ return;
+ }
+
+ //
+ // For some reason all crc bits are inverted.
+ //
+ pack[i].crc[0] ^= 0xff;
+ pack[i].crc[1] ^= 0xff;
+
+ Q_UINT16 crc = calcX25( reinterpret_cast<unsigned char*>(&pack[i]), 18 );
+
+ pack[i].crc[0] ^= 0xff;
+ pack[i].crc[1] ^= 0xff;
+
+ if( crc != 0x0000 )
+ k3bDebug() << "(K3bDevice::CdText) CRC invalid!" << endl;
+
+
+ //
+ // pack.data has a length of 12
+ //
+ // id1 tells us the tracknumber of the data (0 for global)
+ // data may contain multiple \0. In that case after every \0 the track number increases 1
+ //
+
+ char* nullPos = (char*)pack[i].data - 1;
+
+ unsigned int trackNo = pack[i].id2;
+
+ while( nullPos ) {
+ if( count() < trackNo )
+ resize( trackNo );
+
+ char* nextNullPos = (char*)::memchr( nullPos+1, '\0', 11 - (nullPos - (char*)pack[i].data) );
+ QString txtstr;
+ if( nextNullPos ) // take all chars up to the next null
+ txtstr = QString::fromLatin1( (char*)nullPos+1, nextNullPos - nullPos - 1 );
+ else // take all chars to the end of the pack data (12 bytes)
+ txtstr = QString::fromLatin1( (char*)nullPos+1, 11 - (nullPos - (char*)pack[i].data) );
+
+ //
+ // a tab character means to use the same as for the previous track
+ //
+ if( txtstr == "\t" )
+ txtstr = textForPackType( pack[i].id1, trackNo-1 );
+
+ switch( pack[i].id1 ) {
+ case 0x80: // Title
+ if( trackNo == 0 )
+ m_title.append( txtstr );
+ else
+ at(trackNo-1).m_title.append( txtstr );
+ break;
+
+ case 0x81: // Performer
+ if( trackNo == 0 )
+ m_performer.append( txtstr );
+ else
+ at(trackNo-1).m_performer.append( txtstr );
+ break;
+
+ case 0x82: // Writer
+ if( trackNo == 0 )
+ m_songwriter.append( txtstr );
+ else
+ at(trackNo-1).m_songwriter.append( txtstr );
+ break;
+
+ case 0x83: // Composer
+ if( trackNo == 0 )
+ m_composer.append( txtstr );
+ else
+ at(trackNo-1).m_composer.append( txtstr );
+ break;
+
+ case 0x84: // Arranger
+ if( trackNo == 0 )
+ m_arranger.append( txtstr );
+ else
+ at(trackNo-1).m_arranger.append( txtstr );
+ break;
+
+ case 0x85: // Message
+ if( trackNo == 0 )
+ m_message.append( txtstr );
+ else
+ at(trackNo-1).m_message.append( txtstr );
+ break;
+
+ case 0x86: // Disc identification
+ // only global
+ if( trackNo == 0 )
+ m_discId.append( txtstr );
+ break;
+
+ case 0x8e: // Upc or isrc
+ if( trackNo == 0 )
+ m_upcEan.append( txtstr );
+ else
+ at(trackNo-1).m_isrc.append( txtstr );
+ break;
+
+ // TODO: support for binary data
+ // 0x88: TOC
+ // 0x89: second TOC
+ // 0x8f: Size information
+
+ default:
+ break;
+ }
+
+ trackNo++;
+ nullPos = nextNullPos;
+ }
+ }
+
+ // remove empty fields at the end
+ unsigned int i = count();
+ while( i > 0 && at(i-1).isEmpty() )
+ --i;
+ resize( i );
+ }
+ else
+ k3bDebug() << "(K3bDevice::CdText) zero-sized CD-TEXT: " << len << endl;
+}
+
+
+void K3bDevice::CdText::setRawPackData( const QByteArray& b )
+{
+ setRawPackData( reinterpret_cast<const unsigned char*>(b.data()), b.size() );
+}
+
+QByteArray K3bDevice::CdText::rawPackData() const
+{
+ // FIXME: every pack block may only consist of up to 255 packs.
+
+ unsigned int pc = 0;
+ unsigned int alreadyCountedPacks = 0;
+
+
+ //
+ // prepare the size information block
+ //
+ text_size_block tsize;
+ ::memset( &tsize, 0, sizeof(text_size_block) );
+ tsize.charcode = 0; // ISO 8859-1
+ tsize.first_track = 1;
+ tsize.last_track = count();
+ tsize.pack_count[0xF] = 3;
+ tsize.language_codes[0] = 0x09; // English (from cdrecord)
+
+
+ //
+ // create the CD-Text packs
+ //
+ QByteArray data(0);
+ for( int i = 0; i <= 6; ++i ) {
+ if( textLengthForPackType( 0x80 | i ) ) {
+ appendByteArray( data, createPackData( 0x80 | i, pc ) );
+ tsize.pack_count[i] = pc - alreadyCountedPacks;
+ alreadyCountedPacks = pc;
+ }
+ }
+ if( textLengthForPackType( 0x8E ) ) {
+ appendByteArray( data, createPackData( 0x8E, pc ) );
+ tsize.pack_count[0xE] = pc - alreadyCountedPacks;
+ alreadyCountedPacks = pc;
+ }
+
+
+ // pc is the number of the next pack and we add 3 size packs
+ tsize.last_seqnum[0] = pc + 2;
+
+
+ //
+ // create the size info packs
+ //
+ unsigned int dataFill = data.size();
+ data.resize( data.size() + 3 * sizeof(cdtext_pack) );
+ for( int i = 0; i < 3; ++i ) {
+ cdtext_pack pack;
+ ::memset( &pack, 0, sizeof(cdtext_pack) );
+ pack.id1 = 0x8F;
+ pack.id2 = i;
+ pack.id3 = pc+i;
+ ::memcpy( pack.data, &reinterpret_cast<char*>(&tsize)[i*12], 12 );
+ savePack( &pack, data, dataFill );
+ }
+
+ //
+ // add MMC header
+ //
+ QByteArray a( 4 );
+ a[0] = (data.size()+2)>>8 & 0xff;
+ a[1] = (data.size()+2) & 0xff;
+ a[2] = a[3] = 0;
+ appendByteArray( a, data );
+
+ return a;
+}
+
+
+void K3bDevice::CdText::appendByteArray( QByteArray& a, const QByteArray& b ) const
+{
+ unsigned int oldSize = a.size();
+ a.resize( oldSize + b.size() );
+ ::memcpy( &a.data()[oldSize], b.data(), b.size() );
+}
+
+
+// this method also creates completely empty packs
+QByteArray K3bDevice::CdText::createPackData( int packType, unsigned int& packCount ) const
+{
+ QByteArray data;
+ unsigned int dataFill = 0;
+ QCString text = encodeCdText( textForPackType( packType, 0 ) );
+ unsigned int currentTrack = 0;
+ unsigned int textPos = 0;
+ unsigned int packPos = 0;
+
+ //
+ // initialize the first pack
+ //
+ cdtext_pack pack;
+ ::memset( &pack, 0, sizeof(cdtext_pack) );
+ pack.id1 = packType;
+ pack.id3 = packCount;
+
+ //
+ // We break this loop when all texts have been packed
+ //
+ while( 1 ) {
+ //
+ // Copy as many bytes as possible into the pack
+ //
+ int copyBytes = QMIN( 12-packPos, text.length()-textPos );
+ ::memcpy( reinterpret_cast<char*>(&pack.data[packPos]), &text.data()[textPos], copyBytes );
+ textPos += copyBytes;
+ packPos += copyBytes;
+
+
+ //
+ // Check if the packdata is full
+ //
+ if( packPos > 11 ) {
+
+ savePack( &pack, data, dataFill );
+ ++packCount;
+
+ //
+ // reset the pack
+ //
+ ::memset( &pack, 0, sizeof(cdtext_pack) );
+ pack.id1 = packType;
+ pack.id2 = currentTrack;
+ pack.id3 = packCount;
+ packPos = 0;
+
+ // update the charpos in case we continue a text in the next pack
+ if( textPos <= text.length() )
+ pack.charpos = ( textPos > 15 ? 15 : textPos );
+ }
+
+
+ //
+ // Check if we have no text data left
+ //
+ if( textPos >= text.length() ) {
+
+ // add one zero spacer byte
+ ++packPos;
+
+ ++currentTrack;
+
+ // Check if all texts have been packed
+ if( currentTrack > count() ) {
+ savePack( &pack, data, dataFill );
+ ++packCount;
+
+ data.resize( dataFill );
+ return data;
+ }
+
+ // next text block
+ text = encodeCdText( textForPackType( packType, currentTrack ) );
+ textPos = 0;
+ }
+ }
+}
+
+
+void K3bDevice::CdText::savePack( cdtext_pack* pack, QByteArray& data, unsigned int& dataFill ) const
+{
+ // create CRC
+ Q_UINT16 crc = calcX25( reinterpret_cast<unsigned char*>(pack), sizeof(cdtext_pack)-2 );
+
+ // invert for Redbook compliance
+ crc ^= 0xffff;
+
+ pack->crc[0] = (crc>>8) & 0xff;
+ pack->crc[1] = crc & 0xff;
+
+
+ // append the pack to data
+ if( data.size() < dataFill + sizeof(cdtext_pack) )
+ data.resize( dataFill + sizeof(cdtext_pack), QGArray::SpeedOptim );
+
+ ::memcpy( &data.data()[dataFill], reinterpret_cast<char*>( pack ), sizeof(cdtext_pack) );
+
+ dataFill += sizeof(cdtext_pack);
+}
+
+
+// track 0 means global cdtext
+const QString& K3bDevice::CdText::textForPackType( int packType, unsigned int track ) const
+{
+ switch( packType ) {
+ default:
+ case 0x80:
+ if( track == 0 )
+ return title();
+ else
+ return at(track-1).title();
+
+ case 0x81:
+ if( track == 0 )
+ return performer();
+ else
+ return at(track-1).performer();
+
+ case 0x82:
+ if( track == 0 )
+ return songwriter();
+ else
+ return at(track-1).songwriter();
+
+ case 0x83:
+ if( track == 0 )
+ return composer();
+ else
+ return at(track-1).composer();
+
+ case 0x84:
+ if( track == 0 )
+ return arranger();
+ else
+ return at(track-1).arranger();
+
+ case 0x85:
+ if( track == 0 )
+ return message();
+ else
+ return at(track-1).message();
+
+ case 0x86:
+ if( track == 0 )
+ return discId();
+ else
+ return QString::null;
+
+// case 0x87:
+// if( track == 0 )
+// return genre();
+// else
+// return at(track-1).title();
+
+ case 0x8E:
+ if( track == 0 )
+ return upcEan();
+ else
+ return at(track-1).isrc();
+ }
+}
+
+
+// count the overall length of a certain packtype texts
+unsigned int K3bDevice::CdText::textLengthForPackType( int packType ) const
+{
+ unsigned int len = 0;
+ for( unsigned int i = 0; i <= count(); ++i )
+ len += encodeCdText( textForPackType( packType, i ) ).length();
+ return len;
+}
+
+
+QCString K3bDevice::encodeCdText( const QString& s, bool* illegalChars )
+{
+ if( illegalChars )
+ *illegalChars = false;
+
+ // TODO: do this without QT
+ QTextCodec* codec = QTextCodec::codecForName("ISO8859-1");
+ if( codec ) {
+ QCString encoded = codec->fromUnicode( s );
+ return encoded;
+ }
+ else {
+ QCString r(s.length()+1);
+
+ for( unsigned int i = 0; i < s.length(); ++i ) {
+ if( s[i].latin1() == 0 ) { // non-ASCII character
+ r[i] = ' ';
+ if( illegalChars )
+ *illegalChars = true;
+ }
+ else
+ r[i] = s[i].latin1();
+ }
+
+ return r;
+ }
+}
+
+
+bool K3bDevice::CdText::checkCrc( const QByteArray& rawData )
+{
+ return checkCrc( reinterpret_cast<const unsigned char*>(rawData.data()), rawData.size() );
+}
+
+
+bool K3bDevice::CdText::checkCrc( const unsigned char* data, int len )
+{
+ int r = len%18;
+ if( r > 0 && r != 4 ) {
+ k3bDebug() << "(K3bDevice::CdText) invalid cdtext size: " << len << endl;
+ return false;
+ }
+ else {
+ len -= r;
+
+ // TODO: what if the crc field is not used? All zeros?
+
+ for( int i = 0; i < (len-r)/18; ++i ) {
+ cdtext_pack* pack = (cdtext_pack*)&data[r];
+
+ //
+ // For some reason all crc bits are inverted.
+ //
+ pack[i].crc[0] ^= 0xff;
+ pack[i].crc[1] ^= 0xff;
+
+ int crc = calcX25( reinterpret_cast<unsigned char*>(&pack[i]), 18 );
+
+ pack[i].crc[0] ^= 0xff;
+ pack[i].crc[1] ^= 0xff;
+
+ if( crc != 0x0000 )
+ return false;
+ }
+
+ return true;
+ }
+}
+
+
+void K3bDevice::CdText::debug() const
+{
+ // debug the stuff
+ k3bDebug() << "CD-TEXT data:" << endl
+ << "Global:" << endl
+ << " Title: '" << title() << "'" << endl
+ << " Performer: '" << performer() << "'" << endl
+ << " Songwriter: '" << songwriter() << "'" << endl
+ << " Composer: '" << composer() << "'" << endl
+ << " Arranger: '" << arranger() << "'" << endl
+ << " Message: '" << message() << "'" << endl
+ << " Disc ID: '" << discId() << "'" << endl
+ << " Upc Ean: '" << upcEan() << "'" << endl;
+ for( unsigned int i = 0; i < count(); ++i ) {
+ k3bDebug() << "Track " << (i+1) << ":" << endl
+ << " Title: '" << at(i).title() << "'" << endl
+ << " Performer: '" << at(i).performer() << "'" << endl
+ << " Songwriter: '" << at(i).songwriter() << "'" << endl
+ << " Composer: '" << at(i).composer() << "'" << endl
+ << " Arranger: '" << at(i).arranger() << "'" << endl
+ << " Message: '" << at(i).message() << "'" << endl
+ << " Isrc: '" << at(i).isrc() << "'" << endl;
+ }
+}
+
+
+bool K3bDevice::TrackCdText::operator==( const K3bDevice::TrackCdText& other ) const
+{
+ return( m_title == other.m_title &&
+ m_performer == other.m_performer &&
+ m_songwriter == other.m_songwriter &&
+ m_composer == other.m_composer &&
+ m_arranger == other.m_arranger &&
+ m_message == other.m_message &&
+ m_isrc == other.m_isrc );
+}
+
+
+bool K3bDevice::TrackCdText::operator!=( const K3bDevice::TrackCdText& other ) const
+{
+ return !operator==( other );
+}
+
+
+bool K3bDevice::CdText::operator==( const K3bDevice::CdText& other ) const
+{
+ return( m_title == other.m_title &&
+ m_performer == other.m_performer &&
+ m_songwriter == other.m_songwriter &&
+ m_composer == other.m_composer &&
+ m_arranger == other.m_arranger &&
+ m_message == other.m_message &&
+ m_discId == other.m_discId &&
+ m_upcEan == other.m_upcEan &&
+ QValueVector<TrackCdText>::operator==( other ) );
+}
+
+
+bool K3bDevice::CdText::operator!=( const K3bDevice::CdText& other ) const
+{
+ return !operator==( other );
+}
diff --git a/libk3bdevice/k3bcdtext.h b/libk3bdevice/k3bcdtext.h
new file mode 100644
index 0000000..10f6c82
--- /dev/null
+++ b/libk3bdevice/k3bcdtext.h
@@ -0,0 +1,201 @@
+/*
+ *
+ * $Id: k3bcdtext.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_CDTEXT_H_
+#define _K3B_CDTEXT_H_
+
+#include <qstring.h>
+#include <qvaluevector.h>
+#include "k3bdevice_export.h"
+
+namespace K3bDevice
+{
+ struct cdtext_pack;
+
+ class TrackCdText
+ {
+ friend class Device;
+
+ public:
+ TrackCdText() {
+ }
+
+ void clear() {
+ m_title.truncate(0);
+ m_performer.truncate(0);
+ m_songwriter.truncate(0);
+ m_composer.truncate(0);
+ m_arranger.truncate(0);
+ m_message.truncate(0);
+ m_isrc.truncate(0);
+ }
+
+ const QString& title() const { return m_title; }
+ const QString& performer() const { return m_performer; }
+ const QString& songwriter() const { return m_songwriter; }
+ const QString& composer() const { return m_composer; }
+ const QString& arranger() const { return m_arranger; }
+ const QString& message() const { return m_message; }
+ const QString& isrc() const { return m_isrc; }
+
+ // TODO: use the real CD-TEXT charset (a modified ISO8859-1)
+ void setTitle( const QString& s ) { m_title = s; fixup(m_title); }
+ void setPerformer( const QString& s ) { m_performer = s; fixup(m_performer); }
+ void setSongwriter( const QString& s ) { m_songwriter = s; fixup(m_songwriter); }
+ void setComposer( const QString& s ) { m_composer = s; fixup(m_composer); }
+ void setArranger( const QString& s ) { m_arranger = s; fixup(m_arranger); }
+ void setMessage( const QString& s ) { m_message = s; fixup(m_message); }
+ void setIsrc( const QString& s ) { m_isrc = s; fixup(m_isrc); }
+
+ bool isEmpty() const {
+ if( !m_title.isEmpty() )
+ return false;
+ if( !m_performer.isEmpty() )
+ return false;
+ if( !m_songwriter.isEmpty() )
+ return false;
+ if( !m_composer.isEmpty() )
+ return false;
+ if( !m_arranger.isEmpty() )
+ return false;
+ if( !m_message.isEmpty() )
+ return false;
+ if( !m_isrc.isEmpty() )
+ return false;
+
+ return true;
+ }
+
+ bool operator==( const TrackCdText& ) const;
+ bool operator!=( const TrackCdText& ) const;
+
+ private:
+ // TODO: remove this (see above)
+ void fixup( QString& s ) { s.replace( '/', "_" ); s.replace( '\"', "_" ); }
+
+ QString m_title;
+ QString m_performer;
+ QString m_songwriter;
+ QString m_composer;
+ QString m_arranger;
+ QString m_message;
+ QString m_isrc;
+
+ friend class CdText;
+ };
+
+ class LIBK3BDEVICE_EXPORT CdText : public QValueVector<TrackCdText>
+ {
+ friend class Device;
+
+ public:
+ CdText();
+ CdText( const unsigned char* data, int len );
+ CdText( const QByteArray& );
+ CdText( int size );
+ CdText( const CdText& );
+
+ void setRawPackData( const unsigned char*, int );
+ void setRawPackData( const QByteArray& );
+
+ QByteArray rawPackData() const;
+
+ bool empty() const {
+ if( !m_title.isEmpty() )
+ return false;
+ if( !m_performer.isEmpty() )
+ return false;
+ if( !m_songwriter.isEmpty() )
+ return false;
+ if( !m_composer.isEmpty() )
+ return false;
+ if( !m_arranger.isEmpty() )
+ return false;
+ if( !m_message.isEmpty() )
+ return false;
+ if( !m_discId.isEmpty() )
+ return false;
+ if( !m_upcEan.isEmpty() )
+ return false;
+
+ for( unsigned int i = 0; i < count(); ++i )
+ if( !at(i).isEmpty() )
+ return false;
+
+ return true;
+ }
+
+ bool isEmpty() const {
+ return empty();
+ }
+
+ void clear();
+
+ const QString& title() const { return m_title; }
+ const QString& performer() const { return m_performer; }
+ const QString& songwriter() const { return m_songwriter; }
+ const QString& composer() const { return m_composer; }
+ const QString& arranger() const { return m_arranger; }
+ const QString& message() const { return m_message; }
+ const QString& discId() const { return m_discId; }
+ const QString& upcEan() const { return m_upcEan; }
+
+ // TODO: use the real CD-TEXT charset (a modified ISO8859-1)
+ void setTitle( const QString& s ) { m_title = s; fixup(m_title); }
+ void setPerformer( const QString& s ) { m_performer = s; fixup(m_performer); }
+ void setSongwriter( const QString& s ) { m_songwriter = s; fixup(m_songwriter); }
+ void setComposer( const QString& s ) { m_composer = s; fixup(m_composer); }
+ void setArranger( const QString& s ) { m_arranger = s; fixup(m_arranger); }
+ void setMessage( const QString& s ) { m_message = s; fixup(m_message); }
+ void setDiscId( const QString& s ) { m_discId = s; fixup(m_discId); }
+ void setUpcEan( const QString& s ) { m_upcEan = s; fixup(m_upcEan); }
+
+ void debug() const;
+
+ /**
+ * Returns false if found a crc error in the raw cdtext block or it has a
+ * wrong length.
+ */
+ static bool checkCrc( const unsigned char*, int );
+ static bool checkCrc( const QByteArray& );
+
+ bool operator==( const CdText& ) const;
+ bool operator!=( const CdText& ) const;
+
+ private:
+ // TODO: remove this (see above)
+ void fixup( QString& s ) { s.replace( '/', "_" ); s.replace( '\"', "_" ); }
+
+ const QString& textForPackType( int packType, unsigned int track ) const;
+ unsigned int textLengthForPackType( int packType ) const;
+ QByteArray createPackData( int packType, unsigned int& ) const;
+ void savePack( cdtext_pack* pack, QByteArray& data, unsigned int& dataFill ) const;
+ void appendByteArray( QByteArray& a, const QByteArray& b ) const;
+
+ QString m_title;
+ QString m_performer;
+ QString m_songwriter;
+ QString m_composer;
+ QString m_arranger;
+ QString m_message;
+ QString m_discId;
+ QString m_upcEan;
+ };
+
+ QCString encodeCdText( const QString& s, bool* illegalChars = 0 );
+}
+
+#endif
diff --git a/libk3bdevice/k3bcrc.cpp b/libk3bdevice/k3bcrc.cpp
new file mode 100644
index 0000000..f533ae7
--- /dev/null
+++ b/libk3bdevice/k3bcrc.cpp
@@ -0,0 +1,80 @@
+/*
+ *
+ * $Id: k3bcrc.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bcrc.h"
+
+#include <k3bdebug.h>
+
+
+static Q_UINT16 g_x25Table[1<<8] = {
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
+ 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
+ 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
+ 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
+ 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+ 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
+ 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
+ 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
+ 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
+ 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
+ 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
+ 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
+ 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
+ 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
+ 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
+ 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
+ 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+ 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
+ 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
+ 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
+ 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
+ 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
+ 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
+ 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
+ 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
+ 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,
+};
+
+
+Q_UINT16 K3bDevice::calcX25( unsigned char* message, unsigned int len, Q_UINT16 crc )
+{
+ while( len-- ) {
+ crc = (crc<<8) ^ g_x25Table[(crc>>8) ^ (*message++)];
+ }
+
+ return crc;
+}
+
+
+bool K3bDevice::checkQCrc( unsigned char* subdata )
+{
+ // Red Book for some reason inverts the CRC bytes
+ subdata[10] ^= 0xff;
+ subdata[11] ^= 0xff;
+
+ Q_UINT16 crc = calcX25( subdata, 12 );
+
+ // correct the data
+ subdata[10] ^= 0xff;
+ subdata[11] ^= 0xff;
+
+ return( crc == 0x0000 );
+}
diff --git a/libk3bdevice/k3bcrc.h b/libk3bdevice/k3bcrc.h
new file mode 100644
index 0000000..ee7d6a6
--- /dev/null
+++ b/libk3bdevice/k3bcrc.h
@@ -0,0 +1,35 @@
+/*
+ *
+ * $Id: k3bcrc.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_CRC_H_
+#define _K3B_CRC_H_
+
+#include <qglobal.h>
+
+namespace K3bDevice
+{
+ // static K3bCrc* x25();
+
+ // bool check( unsigned char* message, unsigned int len, unsigned char* crc, unsigned int crcLen );
+
+ Q_UINT16 calcX25( unsigned char* message, unsigned int len, Q_UINT16 start = 0x0000 );
+
+ /**
+ * subdata is 12 bytes in long.
+ */
+ bool checkQCrc( unsigned char* subdata );
+};
+
+#endif
diff --git a/libk3bdevice/k3bdebug.cpp b/libk3bdevice/k3bdebug.cpp
new file mode 100644
index 0000000..40774aa
--- /dev/null
+++ b/libk3bdevice/k3bdebug.cpp
@@ -0,0 +1,137 @@
+/*
+ *
+ * $Id: k3bemptydiscwaiter.cpp 606691 2006-11-21 12:15:21Z trueg $
+ * Copyright (C) 2006 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bdebug.h"
+
+#include <stdio.h>
+
+
+K3bDebug::K3bDebug()
+{
+}
+
+
+K3bDebug::~K3bDebug()
+{
+}
+
+
+K3bDebug& K3bDebug::operator<<( int i )
+{
+ fprintf( stderr, "%i", i );
+ return *this;
+}
+
+
+K3bDebug& K3bDebug::operator<<( long l )
+{
+ fprintf( stderr, "%li", l );
+ return *this;
+}
+
+
+K3bDebug& K3bDebug::operator<<( unsigned int i )
+{
+ fprintf( stderr, "%u", i );
+ return *this;
+}
+
+
+K3bDebug& K3bDebug::operator<<( unsigned long l )
+{
+ fprintf( stderr, "%lu", l );
+ return *this;
+}
+
+
+K3bDebug& K3bDebug::operator<<( unsigned long long l )
+{
+ fprintf( stderr, "%llu", l );
+ return *this;
+}
+
+
+K3bDebug& K3bDebug::operator<<( char c )
+{
+ fprintf( stderr, "%c", c );
+ return *this;
+}
+
+
+K3bDebug& K3bDebug::operator<<( float f )
+{
+ fprintf( stderr, "%f", f );
+ return *this;
+}
+
+
+K3bDebug& K3bDebug::operator<<( double d )
+{
+ fprintf( stderr, "%f", d );
+ return *this;
+}
+
+
+K3bDebug& K3bDebug::operator<<( const QString& s )
+{
+ fprintf( stderr, "%s", s.utf8().data() );
+ return *this;
+}
+
+
+K3bDebug& K3bDebug::operator<<( const QCString& s )
+{
+ fprintf( stderr, "%s", s.data() );
+ return *this;
+}
+
+
+K3bDebug& K3bDebug::operator<<( const char* s )
+{
+ fprintf( stderr, "%s", s );
+ return *this;
+}
+
+
+K3bDebug& K3bDebug::operator<<( const K3b::Msf& msf )
+{
+ return *this << msf.toString();
+}
+
+
+K3bDebug& K3bDebug::operator<<( K3BDBGFUNC f )
+{
+ return f( *this );
+}
+
+
+K3bDebug& K3bDebug::k3bDebug()
+{
+ static K3bDebug s_debug;
+ return s_debug;
+}
+
+
+
+K3bDebug& k3bDebug()
+{
+ return K3bDebug::k3bDebug();
+}
+
+
+K3bDebug& endl( K3bDebug& s )
+{
+ return s << '\n';
+}
diff --git a/libk3bdevice/k3bdebug.h b/libk3bdevice/k3bdebug.h
new file mode 100644
index 0000000..8bdd374
--- /dev/null
+++ b/libk3bdevice/k3bdebug.h
@@ -0,0 +1,63 @@
+/*
+ *
+ * $Id: k3bemptydiscwaiter.cpp 606691 2006-11-21 12:15:21Z trueg $
+ * Copyright (C) 2006 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_DEBUG_H_
+#define _K3B_DEBUG_H_
+
+#include <qstring.h>
+
+#include <k3bmsf.h>
+#include <k3bdevice_export.h>
+
+class K3bDebug;
+
+typedef K3bDebug& (*K3BDBGFUNC)( K3bDebug& );
+
+/**
+ * K3bDebug as compared to KDebug does not need anything. No KInstance or whatever
+ * and does not use anything except fprintf.
+ * Thus, K3bDebug is fully thread-safe and safe in general
+ */
+class LIBK3BDEVICE_EXPORT K3bDebug
+{
+ public:
+ ~K3bDebug();
+
+ K3bDebug& operator<<( int );
+ K3bDebug& operator<<( long );
+ K3bDebug& operator<<( unsigned int );
+ K3bDebug& operator<<( unsigned long );
+ K3bDebug& operator<<( unsigned long long );
+ K3bDebug& operator<<( char );
+ K3bDebug& operator<<( float );
+ K3bDebug& operator<<( double );
+ K3bDebug& operator<<( const QString& );
+ K3bDebug& operator<<( const QCString& );
+ K3bDebug& operator<<( const char* );
+ K3bDebug& operator<<( const K3b::Msf& );
+
+ K3bDebug& operator<<( K3BDBGFUNC );
+
+ static K3bDebug& k3bDebug();
+
+ private:
+ K3bDebug();
+};
+
+LIBK3BDEVICE_EXPORT K3bDebug& k3bDebug();
+LIBK3BDEVICE_EXPORT K3bDebug& endl( K3bDebug& );
+
+#endif
diff --git a/libk3bdevice/k3bdevice.cpp b/libk3bdevice/k3bdevice.cpp
new file mode 100644
index 0000000..45ba7bf
--- /dev/null
+++ b/libk3bdevice/k3bdevice.cpp
@@ -0,0 +1,3650 @@
+/*
+ *
+ * $Id: k3bdevice.cpp 732002 2007-11-02 14:13:14Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include <config.h>
+
+
+#include "k3bdevice.h"
+#include "k3bdeviceglobals.h"
+#include "k3btrack.h"
+#include "k3btoc.h"
+#include "k3bdiskinfo.h"
+#include "k3bmmc.h"
+#include "k3bscsicommand.h"
+#include "k3bcrc.h"
+
+#include <qstringlist.h>
+#include <qfile.h>
+#include <qglobal.h>
+#include <qvaluevector.h>
+#include <qmutex.h>
+
+#include <k3bdebug.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <math.h>
+#include <stdarg.h>
+
+
+#ifdef Q_OS_LINUX
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,70)
+typedef unsigned char u8;
+#endif
+
+#undef __STRICT_ANSI__
+#include <linux/cdrom.h>
+#define __STRICT_ANSI__
+
+#endif // Q_OS_LINUX
+
+#ifdef Q_OS_FREEBSD
+#include <stdio.h>
+#include <camlib.h>
+#define CD_FRAMESIZE_RAW 2352
+#endif
+
+#ifdef Q_OS_NETBSD
+#include <sys/cdio.h>
+#endif
+
+#ifdef HAVE_RESMGR
+extern "C" {
+#include <resmgr.h>
+}
+#endif
+
+
+//
+// Very evil hacking: force the speed values to be acurate
+// as long as "they" do not introduce other "broken" DVD
+// speeds like 2.4 this works fine
+//
+static int fixupDvdWritingSpeed( int speed )
+{
+ //
+ // Some writers report their speeds in 1000 bytes per second instead of 1024.
+ //
+ if( speed % 1385 == 0 )
+ return speed;
+
+ else if( speed % 1352 == 0 )
+ return speed*1385/1352;
+
+ // has to be 2.4x speed
+ else
+ return 3324;
+}
+
+
+const char* K3bDevice::Device::cdrdao_drivers[] =
+ { "auto", "plextor", "plextor-scan", "cdd2600", "generic-mmc",
+ "generic-mmc-raw", "ricoh-mp6200", "sony-cdu920",
+ "sony-cdu948", "taiyo-yuden", "teac-cdr55", "toshiba",
+ "yamaha-cdr10x", 0
+ };
+
+
+#if defined(Q_OS_LINUX) || defined(Q_OS_NETBSD)
+int K3bDevice::openDevice( const char* name, bool write )
+{
+ int fd = -1;
+ int flags = O_NONBLOCK;
+ if( write )
+ flags |= O_RDWR;
+ else
+ flags |= O_RDONLY;
+
+#ifdef HAVE_RESMGR
+ // first try resmgr
+ fd = ::rsm_open_device( name, flags );
+ // k3bDebug() << "(K3bDevice::Device) resmgr open: " << fd << endl;
+#endif
+
+ if( fd < 0 )
+ fd = ::open( name, flags );
+
+ if( fd < 0 ) {
+ k3bDebug() << "(K3bDevice::Device) could not open device "
+ << name << ( write ? " for writing" : " for reading" ) << endl;
+ k3bDebug() << " (" << strerror(errno) << ")" << endl;
+ fd = -1;
+
+ // at least open it read-only (which is sufficient for kernels < 2.6.8 anyway)
+ if( write )
+ return openDevice( name, false );
+ }
+
+ return fd;
+}
+#endif
+
+
+class K3bDevice::Device::Private
+{
+public:
+ Private()
+ : supportedProfiles(0),
+#ifdef Q_OS_LINUX
+ deviceFd(-1),
+#endif
+#ifdef Q_OS_NETBSD
+ deviceFd(-1),
+#endif
+#ifdef Q_OS_FREEBSD
+ cam(0),
+#endif
+ openedReadWrite(false),
+ burnfree(false) {
+ }
+
+ int readCapabilities;
+ int writeCapabilities;
+ int supportedProfiles;
+ QStringList allNodes;
+#ifdef Q_OS_LINUX
+ int deviceFd;
+#endif
+#ifdef Q_OS_NETBSD
+ int deviceFd;
+#endif
+#ifdef Q_OS_FREEBSD
+ struct cam_device *cam;
+#endif
+ bool openedReadWrite;
+ bool burnfree;
+
+ QMutex mutex;
+ QMutex openCloseMutex;
+};
+
+
+K3bDevice::Device::Device( const QString& devname )
+ : m_bus(-1),
+ m_target(-1),
+ m_lun(-1),
+ m_writeModes(0)
+{
+ d = new Private;
+
+ m_blockDevice = devname;
+ d->allNodes.append(devname);
+
+ m_cdrdaoDriver = "auto";
+ m_cdTextCapable = 0;
+ m_maxWriteSpeed = 0;
+ m_maxReadSpeed = 0;
+ d->burnfree = false;
+ m_dvdMinusTestwrite = true;
+ m_bufferSize = 0;
+}
+
+
+K3bDevice::Device::~Device()
+{
+ close();
+ delete d;
+}
+
+
+bool K3bDevice::Device::init( bool bCheckWritingModes )
+{
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": init()" << endl;
+
+ //
+ // they all should read CD-ROM.
+ //
+ d->readCapabilities = MEDIA_CD_ROM;
+ d->writeCapabilities = 0;
+ d->supportedProfiles = 0;
+
+ if( !open() )
+ return false;
+
+ //
+ // inquiry
+ // use a 36 bytes buffer since not all devices return the full inquiry struct
+ //
+ ScsiCommand cmd( this );
+ unsigned char buf[36];
+ cmd.clear();
+ ::memset( buf, 0, sizeof(buf) );
+ struct inquiry* inq = (struct inquiry*)buf;
+ cmd[0] = MMC_INQUIRY;
+ cmd[4] = sizeof(buf);
+ cmd[5] = 0;
+ if( cmd.transport( TR_DIR_READ, buf, sizeof(buf) ) ) {
+ kdError() << "(K3bDevice::Device) Unable to do inquiry." << endl;
+ close();
+ return false;
+ }
+ else {
+ m_vendor = QString::fromLatin1( (char*)(inq->vendor), 8 ).stripWhiteSpace();
+ m_description = QString::fromLatin1( (char*)(inq->product), 16 ).stripWhiteSpace();
+ m_version = QString::fromLatin1( (char*)(inq->revision), 4 ).stripWhiteSpace();
+ }
+
+ if( m_vendor.isEmpty() )
+ m_vendor = "UNKNOWN";
+ if( m_description.isEmpty() )
+ m_description = "UNKNOWN";
+
+ //
+ // We probe all features of the device. Since not all devices support the GET CONFIGURATION command
+ // we also query the mode page 2A and use the cdrom.h stuff to get as much information as possible
+ //
+ checkFeatures();
+
+ //
+ // Check the supported write modes (WRITINGMODE_TAO, WRITINGMODE_SAO, WRITINGMODE_RAW) by trying to set them
+ // We do this before checking mode page 2A in case some readers allow changin
+ // the write parameter page
+ //
+ if( bCheckWritingModes )
+ checkWritingModes();
+
+ //
+ // Most current drives support the 2A mode page
+ // Here we can get some more information (cdrecord -prcap does exactly this)
+ //
+ checkFor2AFeatures();
+
+ m_maxWriteSpeed = determineMaximalWriteSpeed();
+
+ //
+ // Check Just-Link via Ricoh mode page 0x30
+ //
+ if( !d->burnfree )
+ checkForJustLink();
+
+ //
+ // Support for some very old drives
+ //
+ checkForAncientWriters();
+
+ //
+ // If it can be written it can also be read
+ //
+ d->readCapabilities |= d->writeCapabilities;
+
+ close();
+
+ return furtherInit();
+}
+
+
+bool K3bDevice::Device::furtherInit()
+{
+#ifdef Q_OS_LINUX
+
+ //
+ // Since all CDR drives at least support WRITINGMODE_TAO, all CDRW drives should support
+ // mode page 2a and all DVD writer should support mode page 2a or the GET CONFIGURATION
+ // command this is redundant and may be removed for BSD ports or even completely
+ //
+ // We just keep it here because of the "should" in the sentence above. If someone can tell me
+ // that the linux driver does nothing more we can remove it completely.
+ //
+ open();
+ int drivetype = ::ioctl( handle(), CDROM_GET_CAPABILITY, CDSL_CURRENT );
+ if( drivetype < 0 ) {
+ k3bDebug() << "Error while retrieving capabilities." << endl;
+ close();
+ return false;
+ }
+
+ d->readCapabilities |= DEVICE_CD_ROM;
+
+ if( drivetype & CDC_CD_R )
+ d->writeCapabilities |= MEDIA_CD_R;
+ if( drivetype & CDC_CD_RW )
+ d->writeCapabilities |= MEDIA_CD_RW;
+ if( drivetype & CDC_DVD_R )
+ d->writeCapabilities |= MEDIA_DVD_R;
+ if( drivetype & CDC_DVD )
+ d->readCapabilities |= MEDIA_DVD_ROM;
+
+ close();
+
+#endif // Q_OS_LINUX
+ return true;
+}
+
+
+void K3bDevice::Device::checkForAncientWriters()
+{
+ // TODO: add a boolean which determines if this device is non-MMC so we may warn the user at K3b startup about it
+
+ //
+ // There are a lot writers out there which behave like the TEAC R5XS
+ //
+ if( ( vendor().startsWith("TEAC") && ( description().startsWith("CD-R50S") ||
+ description().startsWith("CD-R55S") ) )
+ ||
+ ( vendor().startsWith("SAF") && ( description().startsWith("CD-R2006PLUS") ||
+ description().startsWith("CD-RW226") ||
+ description().startsWith("CD-R4012") ) )
+ ||
+ ( vendor().startsWith("JVC") && ( description().startsWith("XR-W2001") ||
+ description().startsWith("XR-W2010") ||
+ description().startsWith("R2626") ) )
+ ||
+ ( vendor().startsWith("PINNACLE") && ( description().startsWith("RCD-1000") ||
+ description().startsWith("RCD5020") ||
+ description().startsWith("RCD5040") ||
+ description().startsWith("RCD 4X4") ) )
+ ||
+ ( vendor().startsWith("Traxdata") && description().startsWith("CDR4120") ) ) {
+ m_writeModes = WRITINGMODE_TAO;
+ d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
+ d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
+ m_maxWriteSpeed = 4;
+ m_maxReadSpeed = 12;
+ m_bufferSize = 1024;
+ d->burnfree = false;
+ }
+ else if( vendor().startsWith("TEAC") ) {
+ if( description().startsWith("CD-R56S") ) {
+ m_writeModes |= TAO;
+ d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
+ d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
+ m_maxWriteSpeed = 6;
+ m_maxReadSpeed = 24;
+ m_bufferSize = 1302;
+ d->burnfree = false;
+ }
+ if( description().startsWith("CD-R58S") ) {
+ m_writeModes |= TAO;
+ d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
+ d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
+ m_maxWriteSpeed = 8;
+ m_maxReadSpeed = 24;
+ m_bufferSize = 4096;
+ d->burnfree = false;
+ }
+ }
+ else if( vendor().startsWith("MATSHITA") ) {
+ if( description().startsWith("CD-R CW-7501") ) {
+ m_writeModes = WRITINGMODE_TAO|WRITINGMODE_SAO;
+ d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
+ d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
+ m_maxWriteSpeed = 2;
+ m_maxReadSpeed = 4;
+ m_bufferSize = 1024;
+ d->burnfree = false;
+ }
+ if( description().startsWith("CD-R CW-7502") ) {
+ m_writeModes = WRITINGMODE_TAO|WRITINGMODE_SAO;
+ d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
+ d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
+ m_maxWriteSpeed = 4;
+ m_maxReadSpeed = 8;
+ m_bufferSize = 1024;
+ d->burnfree = false;
+ }
+ else if( description().startsWith("CD-R56S") ) {
+ m_writeModes |= WRITINGMODE_TAO;
+ d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
+ d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
+ m_maxWriteSpeed = 6;
+ m_maxReadSpeed = 24;
+ m_bufferSize = 1302;
+ d->burnfree = false;
+ }
+ }
+ else if( vendor().startsWith("HP") ) {
+ if( description().startsWith("CD-Writer 6020") ) {
+ m_writeModes = WRITINGMODE_TAO;
+ d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
+ d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
+ m_maxWriteSpeed = 2;
+ m_maxReadSpeed = 6;
+ m_bufferSize = 1024;
+ d->burnfree = false;
+ }
+ }
+ else if( vendor().startsWith( "PHILIPS" ) ) {
+ if( description().startsWith( "CDD2600" ) ) {
+ m_writeModes = WRITINGMODE_TAO|WRITINGMODE_SAO;
+ d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
+ d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
+ m_maxWriteSpeed = 2;
+ m_maxReadSpeed = 6;
+ m_bufferSize = 1024;
+ d->burnfree = false;
+ }
+ }
+}
+
+
+K3bDevice::Interface K3bDevice::Device::interfaceType() const
+{
+ if( m_bus != -1 && m_target != -1 && m_lun != -1 )
+ return SCSI;
+ else
+ return IDE;
+}
+
+
+bool K3bDevice::Device::dao() const
+{
+ return m_writeModes & WRITINGMODE_SAO;
+}
+
+
+bool K3bDevice::Device::supportsRawWriting() const
+{
+ return( writingModes() & (WRITINGMODE_RAW|WRITINGMODE_RAW_R16|WRITINGMODE_RAW_R96P|WRITINGMODE_RAW_R96R) );
+}
+
+
+bool K3bDevice::Device::writesCd() const
+{
+ return ( d->writeCapabilities & MEDIA_CD_R ) && ( m_writeModes & WRITINGMODE_TAO );
+}
+
+
+bool K3bDevice::Device::burner() const
+{
+ return ( writesCd() || writesDvd() );
+}
+
+
+bool K3bDevice::Device::writesCdrw() const
+{
+ return d->writeCapabilities & MEDIA_CD_RW;
+}
+
+
+bool K3bDevice::Device::writesDvd() const
+{
+ return ( writesDvdPlus() || writesDvdMinus() );
+}
+
+
+bool K3bDevice::Device::writesDvdPlus() const
+{
+ return d->writeCapabilities & (MEDIA_DVD_PLUS_R|MEDIA_DVD_PLUS_RW);
+}
+
+
+bool K3bDevice::Device::writesDvdMinus() const
+{
+ return d->writeCapabilities & (MEDIA_DVD_R|MEDIA_DVD_RW);
+}
+
+
+bool K3bDevice::Device::readsDvd() const
+{
+ return d->readCapabilities & MEDIA_DVD_ROM;
+}
+
+
+int K3bDevice::Device::type() const
+{
+ int r = 0;
+ if( readCapabilities() & MEDIA_CD_ROM )
+ r |= DEVICE_CD_ROM;
+ if( writeCapabilities() & MEDIA_CD_R )
+ r |= DEVICE_CD_R;
+ if( writeCapabilities() & MEDIA_CD_RW )
+ r |= DEVICE_CD_RW;
+ if( readCapabilities() & MEDIA_DVD_ROM )
+ r |= DEVICE_DVD_ROM;
+ if( writeCapabilities() & MEDIA_DVD_RAM )
+ r |= DEVICE_DVD_RAM;
+ if( writeCapabilities() & MEDIA_DVD_R )
+ r |= DEVICE_DVD_R;
+ if( writeCapabilities() & MEDIA_DVD_RW )
+ r |= DEVICE_DVD_RW;
+ if( writeCapabilities() & MEDIA_DVD_R_DL )
+ r |= DEVICE_DVD_R_DL;
+ if( writeCapabilities() & MEDIA_DVD_PLUS_R )
+ r |= DEVICE_DVD_PLUS_R;
+ if( writeCapabilities() & MEDIA_DVD_PLUS_RW )
+ r |= DEVICE_DVD_PLUS_RW;
+ if( writeCapabilities() & MEDIA_DVD_PLUS_R_DL )
+ r |= DEVICE_DVD_PLUS_R_DL;
+ if( readCapabilities() & MEDIA_HD_DVD_ROM )
+ r |= DEVICE_HD_DVD_ROM;
+ if( writeCapabilities() & MEDIA_HD_DVD_R )
+ r |= DEVICE_HD_DVD_R;
+ if( writeCapabilities() & MEDIA_HD_DVD_RAM )
+ r |= DEVICE_HD_DVD_RAM;
+ if( readCapabilities() & MEDIA_BD_ROM )
+ r |= DEVICE_BD_ROM;
+ if( writeCapabilities() & MEDIA_BD_R )
+ r |= DEVICE_BD_R;
+ if( writeCapabilities() & MEDIA_BD_RE )
+ r |= DEVICE_BD_RE;
+
+ return r;
+}
+
+
+int K3bDevice::Device::readCapabilities() const
+{
+ return d->readCapabilities;
+}
+
+
+int K3bDevice::Device::writeCapabilities() const
+{
+ return d->writeCapabilities;
+}
+
+
+const QString& K3bDevice::Device::devicename() const
+{
+ return blockDeviceName();
+}
+
+
+QString K3bDevice::Device::busTargetLun() const
+{
+ return QString("%1,%2,%3").arg(m_bus).arg(m_target).arg(m_lun);
+}
+
+
+int K3bDevice::Device::cdTextCapable() const
+{
+ if( cdrdaoDriver() == "auto" )
+ return 0;
+ else
+ return m_cdTextCapable;
+}
+
+
+void K3bDevice::Device::setCdTextCapability( bool b )
+{
+ m_cdTextCapable = ( b ? 1 : 2 );
+}
+
+
+bool K3bDevice::Device::burnproof() const
+{
+ return burnfree();
+}
+
+
+bool K3bDevice::Device::burnfree() const
+{
+ return d->burnfree;
+}
+
+
+bool K3bDevice::Device::isDVD() const
+{
+ if( readsDvd() )
+ return( mediaType() & MEDIA_DVD_ALL );
+ else
+ return false;
+}
+
+
+int K3bDevice::Device::isEmpty() const
+{
+ // if the device is already opened we do not close it
+ // to allow fast multiple method calls in a row
+ bool needToClose = !isOpen();
+
+ int ret = STATE_UNKNOWN;
+ if( !open() )
+ return STATE_UNKNOWN;
+
+ if( !testUnitReady() )
+ return STATE_NO_MEDIA;
+
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+
+ if( readDiscInformation( &data, dataLen ) ) {
+ disc_info_t* inf = (disc_info_t*)data;
+ switch( inf->status ) {
+ case 0:
+ ret = STATE_EMPTY;
+ break;
+ case 1:
+ ret = STATE_INCOMPLETE;
+ break;
+ case 2:
+ ret = STATE_COMPLETE;
+ break;
+ default:
+ ret = STATE_UNKNOWN;
+ break;
+ }
+
+ delete [] data;
+ }
+
+ if( needToClose )
+ close();
+
+ return ret;
+}
+
+
+int K3bDevice::Device::numSessions() const
+{
+ //
+ // Session Info
+ // ============
+ // Byte 0-1: Data Length
+ // Byte 2: First Complete Session Number (Hex) - always 1
+ // Byte 3: Last Complete Session Number (Hex)
+ //
+
+ int ret = -1;
+
+ unsigned char* data = 0;
+ unsigned int len = 0;
+
+ if( mediaType() & MEDIA_CD_ALL ) {
+ //
+ // Althought disk_info should get the real value without ide-scsi
+ // I keep getting wrong values (the value is too high. I think the leadout
+ // gets counted as session sometimes :()
+ //
+ if( readTocPmaAtip( &data, len, 1, 0, 0 ) ) {
+ ret = data[3];
+
+ delete [] data;
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": could not get session info !" << endl;
+ }
+ }
+ else {
+ if( readDiscInformation( &data, len ) ) {
+ ret = (int)( data[9]<<8 | data[4] );
+
+ // do only count complete sessions
+ if( (data[2]>>2) != 3 )
+ ret--;
+
+ delete [] data;
+ }
+ }
+
+ return ret;
+}
+
+
+int K3bDevice::Device::getDataMode( const K3b::Msf& sector ) const
+{
+ bool needToClose = !isOpen();
+
+ int ret = Track::UNKNOWN;
+
+ if( !open() )
+ return ret;
+
+ // we use readCdMsf here since it's defined mandatory in MMC1 and
+ // we only use this method for CDs anyway
+ unsigned char data[2352];
+ bool readSuccess = readCdMsf( data, 2352,
+ 0, // all sector types
+ false, // no dap
+ sector,
+ sector+1,
+ true, // SYNC
+ true, // HEADER
+ true, // SUBHEADER
+ true, // USER DATA
+ true, // EDC/ECC
+ 0, // no c2 info
+ 0 );
+
+ if( readSuccess ) {
+ if ( data[15] == 0x1 )
+ ret = Track::MODE1;
+ else if ( data[15] == 0x2 )
+ ret = Track::MODE2;
+ if ( ret == Track::MODE2 ) {
+ if ( data[16] == data[20] &&
+ data[17] == data[21] &&
+ data[18] == data[22] &&
+ data[19] == data[23] ) {
+ if ( data[18] & 0x20 )
+ ret = Track::XA_FORM2;
+ else
+ ret = Track::XA_FORM1;
+ }
+ }
+ }
+
+ if( needToClose )
+ close();
+
+ return ret;
+}
+
+
+
+int K3bDevice::Device::getTrackDataMode( const K3bDevice::Track& track ) const
+{
+ return getDataMode( track.firstSector() );
+}
+
+
+K3bDevice::Toc K3bDevice::Device::readToc() const
+{
+ // if the device is already opened we do not close it
+ // to allow fast multiple method calls in a row
+ bool needToClose = !isOpen();
+
+ Toc toc;
+
+ if( !open() )
+ return toc;
+
+ int mt = mediaType();
+
+ //
+ // Use the profile if available because DVD-ROM units need to treat DVD+-R(W) media as DVD-ROM
+ // if supported at all
+ //
+ if( currentProfile() == MEDIA_DVD_ROM )
+ mt = MEDIA_DVD_ROM;
+
+ if( mt & (MEDIA_DVD_MINUS_ALL|MEDIA_DVD_PLUS_RW|MEDIA_DVD_ROM) ) {
+ if( !readFormattedToc( toc, mt ) ) {
+ K3b::Msf size;
+ if( readCapacity( size ) ) {
+ Track track;
+ track.m_firstSector = 0;
+ track.m_lastSector = size.lba();
+ track.m_session = 1;
+ track.m_type = Track::DATA;
+ track.m_mode = Track::DVD;
+ track.m_copyPermitted = ( mt != MEDIA_DVD_ROM );
+ track.m_preEmphasis = ( mt != MEDIA_DVD_ROM );
+
+ toc.append( track );
+ }
+ else
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << "READ CAPACITY for toc failed." << endl;
+ }
+ }
+
+ else if( mt & (MEDIA_DVD_PLUS_R|MEDIA_DVD_PLUS_R_DL) ) {
+ //
+ // a DVD+R disk may have multiple sessions
+ // every session may contain up to 16 fragments
+ // if the disk is open there is one open session
+ // every closed session is viewed as a track whereas
+ // every fragment of the open session is viewed as a track
+ //
+ // We may use
+ // READ DISK INFORMATION
+ // READ TRACK INFORMATION: track number FFh, however, does not refer to the invisible track
+ // READ TOC/PMA/ATIP: form 0 refers to all closed sessions
+ // form 1 refers to the last closed session
+ //
+ readFormattedToc( toc, mt );
+ }
+
+ else if( mt & MEDIA_BD_ALL ) {
+ readFormattedToc( toc, mt );
+ }
+
+ else if( mt == MEDIA_DVD_RAM ) {
+ k3bDebug() << "(K3bDevice::readDvdToc) no dvdram support" << endl;
+ }
+
+
+ else if( mt & MEDIA_CD_ALL ) {
+ bool success = readRawToc( toc );
+ if( !success ) {
+ success = readFormattedToc( toc, mt );
+
+#ifdef Q_OS_LINUX
+ if( !success ) {
+ k3bDebug() << "(K3bDevice::Device) MMC READ TOC failed. falling back to cdrom.h." << endl;
+ readTocLinux(toc);
+ }
+#endif
+
+ if( success )
+ fixupToc( toc );
+ }
+ }
+
+ if( needToClose )
+ close();
+
+ return toc;
+}
+
+
+void K3bDevice::Device::readIsrcMcn( K3bDevice::Toc& toc ) const
+{
+ // read MCN and ISRC of all tracks
+ QCString mcn;
+ if( readMcn( mcn ) ) {
+ toc.setMcn( mcn );
+ k3bDebug() << "(K3bDevice::Device) found MCN: " << mcn << endl;
+ }
+ else
+ k3bDebug() << "(K3bDevice::Device) no MCN found." << endl;
+
+ for( unsigned int i = 1; i <= toc.count(); ++i ) {
+ QCString isrc;
+ if( toc[i-1].type() == Track::AUDIO ) {
+ if( readIsrc( i, isrc ) ) {
+ k3bDebug() << "(K3bDevice::Device) found ISRC for track " << i << ": " << isrc << endl;
+ toc[i-1].setIsrc( isrc );
+ }
+ else
+ k3bDebug() << "(K3bDevice::Device) no ISRC found for track " << i << endl;
+ }
+ }
+}
+
+
+bool K3bDevice::Device::readFormattedToc( K3bDevice::Toc& toc, int mt ) const
+{
+ // if the device is already opened we do not close it
+ // to allow fast multiple method calls in a row
+ bool needToClose = !isOpen();
+
+ bool success = false;
+
+ toc.clear();
+
+ unsigned int lastTrack = 0;
+
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+ if( !(mt & MEDIA_CD_ALL) ) {
+ //
+ // on DVD-R(W) multisession disks only two sessions are represented as tracks in the readTocPmaAtip
+ // response (fabricated TOC). Thus, we use readDiscInformation for DVD media to get the proper number of tracks
+ //
+ if( readDiscInformation( &data, dataLen ) ) {
+ lastTrack = (int)( data[11]<<8 | data[6] );
+
+ delete [] data;
+
+ if( readTrackInformation( &data, dataLen, 1, lastTrack ) ) {
+ track_info_t* trackInfo = (track_info_t*)data;
+
+ if( trackInfo->blank ) {
+ lastTrack--;
+ }
+
+ delete [] data;
+
+ success = true;
+ }
+ else
+ return false;
+ }
+ else
+ return false;
+ }
+ else {
+ if( readTocPmaAtip( &data, dataLen, 0, 0, 1 ) ) {
+
+ if( dataLen < 4 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": formatted toc data too small." << endl;
+ }
+ else if( dataLen != ( (unsigned int)sizeof(toc_track_descriptor) * ((unsigned int)data[3]+1) ) + 4 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": invalid formatted toc data length: "
+ << (dataLen-2) << endl;
+ }
+ else {
+ lastTrack = data[3];
+ toc_track_descriptor* td = (toc_track_descriptor*)&data[4];
+ for( unsigned int i = 0; i < lastTrack; ++i ) {
+
+ Track track;
+ unsigned int control = 0;
+
+ //
+ // In case READ TRACK INFORMATION fails:
+ // no session number info
+ // no track length and thus possibly incorrect last sector for
+ // multisession disks
+ //
+ track.m_firstSector = from4Byte( td[i].start_adr );
+ track.m_lastSector = from4Byte( td[i+1].start_adr ) - 1;
+ control = td[i].control;
+
+ track.m_type = (control & 0x4) ? Track::DATA : Track::AUDIO;
+ track.m_mode = getTrackDataMode( track );
+ track.m_copyPermitted = ( control & 0x2 );
+ track.m_preEmphasis = ( control & 0x1 );
+
+ toc.append( track );
+ }
+
+ success = true;
+ }
+
+ delete [] data;
+ }
+ }
+
+
+ //
+ // Try to get information for all the tracks
+ //
+ for( unsigned int i = 0; i < lastTrack; ++i ) {
+ if( toc.count() < i+1 )
+ toc.append( Track() );
+
+ unsigned char* trackData = 0;
+ unsigned int trackDataLen = 0;
+ if( readTrackInformation( &trackData, trackDataLen, 1, i+1 ) ) {
+ track_info_t* trackInfo = (track_info_t*)trackData;
+
+ toc[i].m_firstSector = from4Byte( trackInfo->track_start );
+
+ if( i > 0 && toc[i-1].m_lastSector == 0 )
+ toc[i-1].m_lastSector = toc[i].m_firstSector - 1;
+
+ // There are drives that return 0 track length here!
+ // Some drives even return an invalid length here. :(
+ if( from4Byte( trackInfo->track_size ) > 0 )
+ toc[i].m_lastSector = toc[i].m_firstSector + from4Byte( trackInfo->track_size ) - 1;
+
+ if( trackInfo->nwa_v ) {
+ toc[i].m_nextWritableAddress = from4Byte( trackInfo->next_writable );
+ toc[i].m_freeBlocks = from4Byte( trackInfo->free_blocks );
+ }
+
+ toc[i].m_session = (int)(trackInfo->session_number_m<<8 & 0xf0 |
+ trackInfo->session_number_l & 0x0f); //FIXME: is this BCD?
+
+ int control = trackInfo->track_mode;
+
+ if( mt & MEDIA_CD_ALL ) {
+ toc[i].m_type = (control & 0x4) ? Track::DATA : Track::AUDIO;
+ toc[i].m_mode = getTrackDataMode( toc[i] );
+ }
+ else {
+ toc[i].m_type = Track::DATA;
+ toc[i].m_mode = Track::DVD;
+ }
+ toc[i].m_copyPermitted = ( control & 0x2 );
+ toc[i].m_preEmphasis = ( control & 0x1 );
+
+ delete [] trackData;
+ }
+ else if( !(mt & MEDIA_CD_ALL) ) {
+ success = false;
+ }
+ }
+
+ // this can only happen with DVD media
+ if( !toc.isEmpty() && toc.last().lastSector() == 0 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " no track length for the last non-empty track." << endl;
+ unsigned char* trackData = 0;
+ unsigned int trackDataLen = 0;
+ if( readTrackInformation( &trackData, trackDataLen, 1, lastTrack+1 ) ) {
+ track_info_t* trackInfo = (track_info_t*)trackData;
+
+ toc.last().m_lastSector = from4Byte( trackInfo->track_start ) - 1;
+
+ delete [] trackData;
+ }
+ }
+
+
+ if( needToClose )
+ close();
+
+ return success;
+}
+
+
+bool K3bDevice::Device::readRawToc( K3bDevice::Toc& toc ) const
+{
+ // if the device is already opened we do not close it
+ // to allow fast multiple method calls in a row
+ bool needToClose = !isOpen();
+
+ bool success = false;
+
+ toc.clear();
+
+ if( open() ) {
+ //
+ // Read Raw TOC (format: 0010b)
+ //
+ // For POINT from 01h-63h we get all the tracks
+ // POINT a1h gices us the last track number in the session in PMIN
+ // POINT a2h gives the start of the session lead-out in PMIN,PSEC,PFRAME
+ //
+
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+
+ if( readTocPmaAtip( &data, dataLen, 2, false, 1 ) ) {
+ if( dataLen > 4 ) {
+ success = true;
+
+ toc_raw_track_descriptor* tr = (toc_raw_track_descriptor*)&data[4];
+
+ //
+ // debug the raw toc data
+ //
+ k3bDebug() << "Session | ADR | CONTROL| TNO | POINT | Min | Sec | Frame | Zero | PMIN | PSEC | PFRAME |" << endl;
+ for( unsigned int i = 0; i < (dataLen-4)/(int)sizeof(toc_raw_track_descriptor); ++i ) {
+ QString s;
+ s += QString( " %1 |" ).arg( (int)tr[i].session_number, 6 );
+ s += QString( " %1 |" ).arg( (int)tr[i].adr, 6 );
+ s += QString( " %1 |" ).arg( (int)tr[i].control, 6 );
+ s += QString( " %1 |" ).arg( (int)tr[i].tno, 6 );
+ s += QString( " %1 |" ).arg( (int)tr[i].point, 6, 16 );
+ s += QString( " %1 |" ).arg( (int)tr[i].min, 6 );
+ s += QString( " %1 |" ).arg( (int)tr[i].sec, 6 );
+ s += QString( " %1 |" ).arg( (int)tr[i].frame, 6 );
+ s += QString( " %1 |" ).arg( (int)tr[i].zero, 6, 16 );
+ s += QString( " %1 |" ).arg( (int)tr[i].p_min, 6 );
+ s += QString( " %1 |" ).arg( (int)tr[i].p_sec, 6 );
+ s += QString( " %1 |" ).arg( (int)tr[i].p_frame, 6 );
+ k3bDebug() << s << endl;
+ }
+
+ //
+ // First we try to determine if the raw toc data uses BCD values
+ //
+ int isBcd = rawTocDataWithBcdValues( data, dataLen );
+ if( isBcd == -1 ) {
+ delete [] data;
+ return false;
+ }
+
+ K3b::Msf sessionLeadOut;
+
+ for( unsigned int i = 0; i < (dataLen-4)/(unsigned int)sizeof(toc_raw_track_descriptor); ++i ) {
+ if( tr[i].adr == 1 && tr[i].point <= 0x63 ) {
+ // track
+ K3bTrack track;
+ track.m_session = tr[i].session_number;
+
+ // :( We use 00:00:00 == 0 lba)
+ if( isBcd )
+ track.m_firstSector = K3b::Msf( K3bDevice::fromBcd(tr[i].p_min),
+ K3bDevice::fromBcd(tr[i].p_sec),
+ K3bDevice::fromBcd(tr[i].p_frame) ) - 150;
+ else
+ track.m_firstSector = K3b::Msf( tr[i].p_min, tr[i].p_sec, tr[i].p_frame ) - 150;
+
+ track.m_type = ( tr[i].control & 0x4 ? Track::DATA : Track::AUDIO );
+ track.m_mode = ( track.type() == Track::DATA ? getTrackDataMode(track) : Track::UNKNOWN );
+ track.m_copyPermitted = ( tr[i].control & 0x2 );
+ track.m_preEmphasis = ( tr[i].control & 0x1 );
+
+ //
+ // only do this within a session because otherwise we already set the last sector with the session leadout
+ //
+ if( !toc.isEmpty() )
+ if( toc[toc.count()-1].session() == track.session() )
+ toc[toc.count()-1].m_lastSector = track.firstSector() - 1;
+
+ toc.append(track);
+ }
+ else if( tr[i].point == 0xa2 ) {
+ //
+ // since the session is always reported before the tracks this is where we do this:
+ // set the previous session's last tracks's last sector to the first sector of the
+ // session leadout (which was reported before the tracks)
+ //
+ // This only happens on multisession CDs
+ //
+ if( !toc.isEmpty() )
+ toc[toc.count()-1].m_lastSector = sessionLeadOut - 1;
+
+ // this is save since the descriptors are reported in ascending order of the session number
+ // :( We use 00:00:00 == 0 lba)
+ if( isBcd )
+ sessionLeadOut = K3b::Msf( K3bDevice::fromBcd(tr[i].p_min),
+ K3bDevice::fromBcd(tr[i].p_sec),
+ K3bDevice::fromBcd(tr[i].p_frame) ) - 150;
+ else
+ sessionLeadOut = K3b::Msf( tr[i].p_min, tr[i].p_sec, tr[i].p_frame ) - 150;
+ }
+ }
+
+ k3bDebug() << blockDeviceName() << ": setting last sector of last track to " << (sessionLeadOut-1).lba() << endl;
+
+ // set the last track's last sector
+ if( !toc.isEmpty() )
+ toc[toc.count()-1].m_lastSector = sessionLeadOut - 1;
+ }
+ else
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " empty raw toc." << endl;
+
+ delete [] data;
+ }
+ }
+
+ if( needToClose )
+ close();
+
+ return success;
+}
+
+
+int K3bDevice::Device::rawTocDataWithBcdValues( unsigned char* data, unsigned int dataLen ) const
+{
+ toc_raw_track_descriptor* tr = (toc_raw_track_descriptor*)&data[4];
+
+ bool notBcd = false;
+ bool notHex = false;
+
+ //
+ // in most cases this will already tell us if a drive does not provide bcd numbers
+ // (which should be all newer MMC drives)
+ //
+ for( unsigned int i = 0; i < (dataLen-4)/(unsigned int)sizeof(toc_raw_track_descriptor); ++i ) {
+ if( tr[i].adr == 1 && tr[i].point <= 0xa2) {
+ if( !K3bDevice::isValidBcd(tr[i].p_min) ||
+ !K3bDevice::isValidBcd(tr[i].p_sec) ||
+ !K3bDevice::isValidBcd(tr[i].p_frame) ) {
+ notBcd = true;
+ break;
+ }
+
+ // we only need to check sec and frame since min needs to be <= 99
+ // and bcd values are never bigger than 99.
+ else if( (int)K3bDevice::fromBcd(tr[i].p_sec) >= 60 ||
+ (int)K3bDevice::fromBcd(tr[i].p_frame) >= 75 ) {
+ notBcd = true;
+ break;
+ }
+ }
+ }
+
+
+ //
+ // all values are valid bcd values but we still don't know for sure if they are really
+ // used as bcd. So we also check the HEX values.
+ //
+ for( unsigned int i = 0; i < (dataLen-4)/(unsigned int)sizeof(toc_raw_track_descriptor); ++i ) {
+ if( tr[i].adr == 1 && tr[i].point <= 0xa2 ) {
+ if( (int)tr[i].p_min > 99 ||
+ (int)tr[i].p_sec >= 60 ||
+ (int)tr[i].p_frame >= 75 ) {
+ notHex = true;
+ break;
+ }
+ }
+ }
+
+
+ //
+ // If all values are valid bcd and valid hex we check the start sectors of the tracks.
+ //
+ if( !notHex || !notBcd ) {
+ K3b::Msf sessionLeadOutHex, sessionLeadOutBcd;
+ K3b::Msf lastTrackHex, lastTrackBcd;
+
+ for( unsigned int i = 0; i < (dataLen-4)/(unsigned int)sizeof(toc_raw_track_descriptor); ++i ) {
+
+ if( tr[i].adr == 1 ) {
+ if( tr[i].point < 0x64 ) {
+
+ // check hex values
+ if( K3b::Msf( tr[i].p_min, tr[i].p_sec, tr[i].p_frame ) <
+ lastTrackHex )
+ notHex = true;
+
+ // check bcd values
+ if( K3b::Msf( K3bDevice::fromBcd(tr[i].p_min), K3bDevice::fromBcd(tr[i].p_sec), K3bDevice::fromBcd(tr[i].p_frame) ) <
+ lastTrackBcd )
+ notBcd = true;
+
+ lastTrackBcd = K3b::Msf( K3bDevice::fromBcd(tr[i].p_min), K3bDevice::fromBcd(tr[i].p_sec), K3bDevice::fromBcd(tr[i].p_frame) );
+ lastTrackHex = K3b::Msf( tr[i].p_min, tr[i].p_sec, tr[i].p_frame );
+ }
+ else if( tr[i].point == 0xa2 ) {
+ if( sessionLeadOutHex < lastTrackHex )
+ notHex = true;
+ if( sessionLeadOutBcd < lastTrackBcd )
+ notBcd = true;
+
+ sessionLeadOutHex = K3b::Msf( tr[i].p_min, tr[i].p_sec, tr[i].p_frame );
+ sessionLeadOutBcd = K3b::Msf( K3bDevice::fromBcd(tr[i].p_min), K3bDevice::fromBcd(tr[i].p_sec), K3bDevice::fromBcd(tr[i].p_frame) );
+ }
+ }
+ }
+
+ // check the last track
+ if( sessionLeadOutHex < lastTrackHex )
+ notHex = true;
+ if( sessionLeadOutBcd < lastTrackBcd )
+ notBcd = true;
+ }
+
+
+ if( !notBcd && !notHex ) {
+ k3bDebug() << "(K3bDevice::Device) need to compare raw toc to formatted toc. :(" << endl;
+ //
+ // All values are valid bcd and valid HEX values so we compare with the formatted toc.
+ // This slows us down a lot but in most cases this should not be reached anyway.
+ //
+ // TODO: also check the bcd values
+ //
+ K3bDevice::Toc formattedToc;
+ if( readFormattedToc( formattedToc, MEDIA_CD_ROM ) ) {
+ for( unsigned int i = 0; i < (dataLen-4)/(unsigned int)sizeof(toc_raw_track_descriptor); ++i ) {
+ if( tr[i].adr == 1 && tr[i].point < 0x64 ) {
+ unsigned int track = (int)tr[i].point;
+
+ // FIXME: do bcd drive also encode the track number in bcd? If so test it, too.
+
+ if( track > formattedToc.count() ) {
+ notHex = true;
+ break;
+ }
+
+ K3b::Msf posHex( tr[i].p_min,
+ tr[i].p_sec,
+ tr[i].p_frame );
+ K3b::Msf posBcd( K3bDevice::fromBcd(tr[i].p_min),
+ K3bDevice::fromBcd(tr[i].p_sec),
+ K3bDevice::fromBcd(tr[i].p_frame) );
+ posHex -= 150;
+ posBcd -= 150;
+ if( posHex != formattedToc[track-1].firstSector() )
+ notHex = true;
+ if( posBcd != formattedToc[track-1].firstSector() )
+ notBcd = true;
+ }
+ }
+ }
+ }
+
+ if( notBcd )
+ k3bDebug() << "(K3bDevice::Device) found invalid bcd values. No bcd toc." << endl;
+ if( notHex )
+ k3bDebug() << "(K3bDevice::Device) found invalid hex values. No hex toc." << endl;
+
+ if( notBcd == notHex ) {
+ k3bDebug() << "(K3bDevice::Device) unable to determine if hex (" << notHex << ") or bcd (" << notBcd << ")." << endl;
+ if( !notHex ) {
+ k3bDebug() << "Assuming hex encoding in favor of newer drives and the more reliable raw toc." << endl;
+ return 0;
+ }
+ return -1;
+ }
+ else if( notBcd )
+ return 0;
+ else
+ return 1;
+}
+
+
+K3bDevice::CdText K3bDevice::Device::readCdText() const
+{
+ // if the device is already opened we do not close it
+ // to allow fast multiple method calls in a row
+ bool needToClose = !isOpen();
+
+ K3bDevice::CdText textData;
+
+ if( open() ) {
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+
+ if( readTocPmaAtip( &data, dataLen, 5, false, 0 ) ) {
+ textData.setRawPackData( data, dataLen );
+
+ delete [] data;
+ }
+
+ if( needToClose )
+ close();
+ }
+
+ return textData;
+}
+
+
+#ifdef Q_OS_LINUX
+// fallback
+bool K3bDevice::Device::readTocLinux( K3bDevice::Toc& toc ) const
+{
+ // if the device is already opened we do not close it
+ // to allow fast multiple method calls in a row
+ bool needToClose = !isOpen();
+
+ bool success = true;
+
+ toc.clear();
+
+ struct cdrom_tochdr tochdr;
+ struct cdrom_tocentry tocentry;
+
+ usageLock();
+ if( open() ) {
+ //
+ // CDROMREADTOCHDR ioctl returns:
+ // cdth_trk0: First Track Number
+ // cdth_trk1: Last Track Number
+ //
+ if( ::ioctl( d->deviceFd, CDROMREADTOCHDR, &tochdr ) ) {
+ k3bDebug() << "(K3bDevice::Device) could not get toc header !" << endl;
+ success = false;
+ }
+ else {
+ Track lastTrack;
+ for (int i = tochdr.cdth_trk0; i <= tochdr.cdth_trk1 + 1; i++) {
+ ::memset(&tocentry,0,sizeof (struct cdrom_tocentry));
+ // get Lead-Out Information too
+ tocentry.cdte_track = (i<=tochdr.cdth_trk1) ? i : CDROM_LEADOUT;
+ tocentry.cdte_format = CDROM_LBA;
+ //
+ // CDROMREADTOCENTRY ioctl returns:
+ // cdte_addr.lba: Start Sector Number (LBA Format requested)
+ // cdte_ctrl: 4 ctrl bits
+ // 00x0b: 2 audio Channels(no pre-emphasis)
+ // 00x1b: 2 audio Channels(pre-emphasis)
+ // 10x0b: audio Channels(no pre-emphasis),reserved in cd-rw
+ // 10x1b: audio Channels(pre-emphasis),reserved in cd-rw
+ // 01x0b: data track, recorded uninterrupted
+ // 01x1b: data track, recorded incremental
+ // 11xxb: reserved
+ // xx0xb: digital copy prohibited
+ // xx1xb: digital copy permitted
+ // cdte_addr: 4 addr bits (type of Q-Subchannel data)
+ // 0000b: no Information
+ // 0001b: current position data
+ // 0010b: MCN
+ // 0011b: ISRC
+ // 0100b-1111b: reserved
+ // cdte_datamode: 0: Data Mode1
+ // 1: CD-I
+ // 2: CD-XA Mode2
+ //
+
+ if( ::ioctl( d->deviceFd, CDROMREADTOCENTRY, &tocentry ) ) {
+ k3bDebug() << "(K3bDevice::Device) error reading tocentry " << i << endl;
+ success = false;
+ break;
+ }
+
+ int startSec = tocentry.cdte_addr.lba;
+ int control = tocentry.cdte_ctrl & 0x0f;
+ int mode = tocentry.cdte_datamode;
+ if( i > tochdr.cdth_trk0 ) {
+ Track track( lastTrack.firstSector(), startSec-1, lastTrack.type(), lastTrack.mode() );
+ track.m_preEmphasis = control & 0x1;
+ track.m_copyPermitted = control & 0x2;
+ toc.append( track );
+ }
+ int trackType = 0;
+ int trackMode = Track::UNKNOWN;
+ if( (control & 0x04 ) && (tocentry.cdte_track != CDROM_LEADOUT) ) {
+ trackType = Track::DATA;
+ if( mode == 1 )
+ trackMode = Track::MODE1;
+ else if( mode == 2 )
+ trackMode = Track::MODE2;
+
+ mode = getDataMode(startSec);
+ if( mode != Track::UNKNOWN )
+ trackMode = mode;
+ }
+ else
+ trackType = Track::AUDIO;
+
+ lastTrack = Track( startSec, startSec, trackType, trackMode );
+ }
+ }
+
+ if( needToClose )
+ close();
+ }
+ else
+ success = false;
+
+ usageUnlock();
+
+ return success;
+}
+#endif // Q_OS_LINUX
+
+
+bool K3bDevice::Device::fixupToc( K3bDevice::Toc& toc ) const
+{
+ bool success = false;
+
+ //
+ // This is a very lame method of fixing the TOC of an Advanced Audio CD
+ // (a CD with two sessions: one with audio tracks and one with the data track)
+ // If a drive does not support reading raw toc or reading track info we only
+ // get every track's first sector. But between sessions there is a gap which is used
+ // for ms stuff. In this case it's 11400 sectors in size. When ripping ausio we would
+ // include these 11400 sectors which would result in a strange ending audio file.
+ //
+ if( numSessions() > 1 || toc.contentType() == MIXED ) {
+ k3bDebug() << "(K3bDevice::Device) fixup multisession toc..." << endl;
+
+ //
+ // we need to update the last sector of every last track in every session
+ // for now we only update the track before the last session...
+ // This is the most often case: Advanced Audio CD
+ //
+
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+ if( readTocPmaAtip( &data, dataLen, 1, false, 0 ) ) {
+
+ //
+ // data[6] - first track number in last complete session
+ // data[8-11] - start address of first track in last session
+ //
+
+ toc[(unsigned int)data[6]-2].m_lastSector = from4Byte( &data[8] ) - 11400 - 1;
+
+ delete [] data;
+ success = true;
+ }
+ else
+ k3bDebug() << "(K3bDevice::Device) FIXUP TOC failed." << endl;
+ }
+
+ return success;
+}
+
+
+bool K3bDevice::Device::block( bool b ) const
+{
+ //
+ // For some reason the Scsi Command does not work here.
+ // So we use the ioctl on Linux systems
+ //
+#if defined(Q_OS_LINUX)
+ bool success = false;
+ bool needToClose = !isOpen();
+ usageLock();
+ if( open() ) {
+ success = ( ::ioctl( d->deviceFd, CDROM_LOCKDOOR, b ? 1 : 0 ) == 0 );
+ if( needToClose )
+ close();
+ }
+ usageUnlock();
+ if ( success )
+ return success;
+#elif defined(Q_OS_NETBSD)
+ bool success = false;
+ bool needToClose = !isOpen();
+ int arg = b ? 1 : 0;
+ usageLock();
+ if( open() ) {
+ success = ( ::ioctl( d->deviceFd, DIOCLOCK, &arg ) == 0 );
+ if( needToClose )
+ close();
+ }
+ usageUnlock();
+ if ( success )
+ return success;
+#endif
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_PREVENT_ALLOW_MEDIUM_REMOVAL;
+ cmd[5] = 0; // Necessary to set the proper command length
+ if( b )
+ cmd[4] = 0x01;
+ int r = cmd.transport( TR_DIR_WRITE );
+
+ if( r )
+ k3bDebug() << "(K3bDevice::Device) MMC ALLOW MEDIA REMOVAL failed." << endl;
+
+ return ( r == 0 );
+}
+
+bool K3bDevice::Device::rewritable() const
+{
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+
+ if( readDiscInformation( &data, dataLen ) ) {
+ disc_info_t* inf = (disc_info_t*)data;
+ bool e = inf->erasable;
+
+ delete [] data;
+
+ return e;
+ }
+ else
+ return false;
+}
+
+
+bool K3bDevice::Device::eject() const
+{
+#ifdef Q_OS_NETBSD
+ bool success = false;
+ bool needToClose = !isOpen();
+ int arg = 0;
+
+ usageLock();
+ if( open() ) {
+ if ( ::ioctl( d->deviceFd, DIOCEJECT, &arg ) >= 0)
+ success = true;
+ if( needToClose )
+ close();
+ }
+ usageUnlock();
+ if ( success )
+ return success;
+#elif defined(Q_OS_LINUX)
+ bool success = false;
+ bool needToClose = !isOpen();
+
+ usageLock();
+ if( open() ) {
+ if( ::ioctl( d->deviceFd, CDROMEJECT ) >= 0 )
+ success = true;
+ if( needToClose )
+ close();
+ }
+ usageUnlock();
+ if ( success )
+ return success;
+#endif
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_PREVENT_ALLOW_MEDIUM_REMOVAL;
+ cmd[5] = 0; // Necessary to set the proper command length
+ cmd.transport();
+
+ cmd[0] = MMC_START_STOP_UNIT;
+ cmd[5] = 0; // Necessary to set the proper command length
+ cmd[4] = 0x1; // Start unit
+ cmd.transport();
+
+ cmd[4] = 0x2; // LoEj = 1, Start = 0
+
+ return !cmd.transport();
+}
+
+
+bool K3bDevice::Device::load() const
+{
+#ifdef Q_OS_NETBSD
+ bool success = false;
+ bool needToClose = !isOpen();
+ int arg = 0;
+
+ usageLock();
+ if( open() ) {
+ if ( ::ioctl( d->deviceFd, CDIOCCLOSE, &arg ) >= 0)
+ success = true;
+ if( needToClose )
+ close();
+ }
+ usageUnlock();
+ if ( success )
+ return success;
+#elif defined(Q_OS_LINUX)
+ bool success = false;
+ bool needToClose = !isOpen();
+
+ usageLock();
+ if( open() ) {
+ if( ::ioctl( d->deviceFd, CDROMCLOSETRAY ) >= 0 )
+ success = true;
+ if( needToClose )
+ close();
+ }
+ usageUnlock();
+ if ( success )
+ return success;
+#endif
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_START_STOP_UNIT;
+ cmd[4] = 0x3; // LoEj = 1, Start = 1
+ cmd[5] = 0; // Necessary to set the proper command length
+ return !cmd.transport();
+}
+
+
+bool K3bDevice::Device::setAutoEjectEnabled( bool enabled ) const
+{
+ bool success = false;
+#ifdef Q_OS_LINUX
+
+ bool needToClose = !isOpen();
+ usageLock();
+ if ( open() ) {
+ success = ( ::ioctl( d->deviceFd, CDROMEJECT_SW, enabled ? 1 : 0 ) == 0 );
+ if ( needToClose ) {
+ close();
+ }
+ }
+ usageUnlock();
+#endif
+ return success;
+}
+
+
+void K3bDevice::Device::addDeviceNode( const QString& n )
+{
+ if( !d->allNodes.contains( n ) )
+ d->allNodes.append( n );
+}
+
+
+const QStringList& K3bDevice::Device::deviceNodes() const
+{
+ return d->allNodes;
+}
+
+
+K3bDevice::Device::Handle K3bDevice::Device::handle() const
+{
+#ifdef Q_OS_FREEBSD
+ return d->cam;
+#else
+ return d->deviceFd;
+#endif
+}
+
+
+bool K3bDevice::Device::open( bool write ) const
+{
+ if( d->openedReadWrite != write )
+ close();
+
+ QMutexLocker ml( &d->openCloseMutex );
+
+ d->openedReadWrite = write;
+
+#ifdef Q_OS_FREEBSD
+ if( !d->cam ) {
+ d->cam = cam_open_pass (m_passDevice.latin1(), O_RDWR,0 /* NULL */);
+ k3bDebug() << "(K3bDevice::openDevice) open device " << m_passDevice
+ << ((d->cam)?" succeeded.":" failed.") << endl;
+ }
+
+ return (d->cam != 0);
+#endif
+#if defined(Q_OS_LINUX) || defined(Q_OS_NETBSD)
+ if( d->deviceFd == -1 )
+ d->deviceFd = openDevice( QFile::encodeName(devicename()), write );
+
+ return ( d->deviceFd != -1 );
+#endif
+}
+
+
+void K3bDevice::Device::close() const
+{
+ QMutexLocker ml( &d->openCloseMutex );
+
+#ifdef Q_OS_FREEBSD
+ if( d->cam ) {
+ cam_close_device(d->cam);
+ d->cam = 0;
+ }
+#endif
+#if defined(Q_OS_LINUX) || defined(Q_OS_NETBSD)
+ if( d->deviceFd != -1 ) {
+ ::close( d->deviceFd );
+ d->deviceFd = -1;
+ }
+#endif
+}
+
+
+bool K3bDevice::Device::isOpen() const
+{
+#ifdef Q_OS_FREEBSD
+ return d->cam;
+#endif
+#if defined(Q_OS_LINUX) || defined(Q_OS_NETBSD)
+ return ( d->deviceFd != -1 );
+#endif
+}
+
+
+int K3bDevice::Device::supportedProfiles() const
+{
+ return d->supportedProfiles;
+}
+
+
+int K3bDevice::Device::currentProfile() const
+{
+ unsigned char profileBuf[8];
+ ::memset( profileBuf, 0, 8 );
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_GET_CONFIGURATION;
+ cmd[1] = 1;
+ cmd[8] = 8;
+ cmd[9] = 0; // Necessary to set the proper command length
+
+ if( cmd.transport( TR_DIR_READ, profileBuf, 8 ) ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << " GET_CONFIGURATION failed." << endl;
+ return MEDIA_UNKNOWN;
+ }
+ else {
+ short profile = from2Byte( &profileBuf[6] );
+
+ //
+ // Plextor drives might not set a current profile
+ // In that case we get the list of all current profiles
+ // and simply use the first one in that list.
+ //
+ if( profile == 0x00 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << " current profile 0. Checking current profile list instead." << endl;
+ unsigned char* data;
+ unsigned int len = 0;
+ if( getFeature( &data, len, FEATURE_PROFILE_LIST ) ) {
+ int featureLen( data[11] );
+ for( int j = 0; j < featureLen; j+=4 ) {
+ // use the first current profile we encounter
+ if( data[12+j+2] & 0x1 ) {
+ profile = from2Byte( &data[12+j] );
+ break;
+ }
+ }
+
+ delete[] data;
+ }
+ }
+
+ switch (profile) {
+ case 0x00: return MEDIA_NONE;
+ case 0x08: return MEDIA_CD_ROM;
+ case 0x09: return MEDIA_CD_R;
+ case 0x0A: return MEDIA_CD_RW;
+ case 0x10: return MEDIA_DVD_ROM;
+ case 0x11: return MEDIA_DVD_R_SEQ;
+ case 0x12: return MEDIA_DVD_RAM;
+ case 0x13: return MEDIA_DVD_RW_OVWR;
+ case 0x14: return MEDIA_DVD_RW_SEQ;
+ case 0x15: return MEDIA_DVD_R_DL_SEQ;
+ case 0x16: return MEDIA_DVD_R_DL_JUMP;
+ case 0x1A: return MEDIA_DVD_PLUS_RW;
+ case 0x1B: return MEDIA_DVD_PLUS_R;
+ case 0x2B: return MEDIA_DVD_PLUS_R_DL;
+ case 0x40: return MEDIA_BD_ROM;
+ case 0x41: {
+ if( featureCurrent( FEATURE_BD_PSEUDO_OVERWRITE ) == 1 )
+ return MEDIA_BD_R_SRM_POW;
+ else
+ return MEDIA_BD_R_SRM;
+ }
+ case 0x42: return MEDIA_BD_R_RRM;
+ case 0x43: return MEDIA_BD_RE;
+ case 0x50: return MEDIA_HD_DVD_ROM;
+ case 0x51: return MEDIA_HD_DVD_R;
+ case 0x52: return MEDIA_HD_DVD_RAM;
+ default: return MEDIA_UNKNOWN;
+ }
+ }
+}
+
+
+K3bDevice::DiskInfo K3bDevice::Device::diskInfo() const
+{
+ DiskInfo inf;
+
+ // if the device is already opened we do not close it
+ // to allow fast multiple method calls in a row
+ bool needToClose = !isOpen();
+
+ if( open() ) {
+
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+
+ //
+ // The first thing to do should be: checking if a media is loaded
+ // We cannot rely on the profile here since at least some Plextor
+ // drives return the NO MEDIUM profile for CD media
+ //
+ if( !testUnitReady() ) {
+ // no disk or tray open
+ inf.m_diskState = STATE_NO_MEDIA;
+ inf.m_mediaType = MEDIA_NONE;
+ inf.m_currentProfile = MEDIA_NONE;
+ }
+ else
+ inf.m_currentProfile = currentProfile();
+
+ if( inf.diskState() != STATE_NO_MEDIA ) {
+
+ if( readDiscInformation( &data, dataLen ) ) {
+ disc_info_t* dInf = (disc_info_t*)data;
+ //
+ // Copy the needed values from the disk_info struct
+ //
+ switch( dInf->status ) {
+ case 0:
+ inf.m_diskState = STATE_EMPTY;
+ break;
+ case 1:
+ inf.m_diskState = STATE_INCOMPLETE;
+ break;
+ case 2:
+ inf.m_diskState = STATE_COMPLETE;
+ break;
+ default:
+ inf.m_diskState = STATE_UNKNOWN;
+ break;
+ }
+
+ switch( dInf->border ) {
+ case 0:
+ inf.m_lastSessionState = STATE_EMPTY;
+ break;
+ case 1:
+ inf.m_lastSessionState = STATE_INCOMPLETE;
+ break;
+ case 2:
+ inf.m_lastSessionState = STATE_COMPLETE;
+ break;
+ default:
+ inf.m_lastSessionState = STATE_UNKNOWN;
+ break;
+ }
+
+ switch( dInf->bg_f_status&0x3 ) {
+ case 0x0:
+ inf.m_bgFormatState = BG_FORMAT_NONE;
+ break;
+ case 0x1:
+ inf.m_bgFormatState = BG_FORMAT_INCOMPLETE;
+ break;
+ case 0x2:
+ inf.m_bgFormatState = BG_FORMAT_IN_PROGRESS;
+ break;
+ case 0x3:
+ inf.m_bgFormatState = BG_FORMAT_COMPLETE;
+ break;
+ }
+
+ inf.m_numTracks = (dInf->last_track_l & 0xff) | (dInf->last_track_m<<8 & 0xff00);
+ if( inf.diskState() == STATE_EMPTY )
+ inf.m_numTracks = 0;
+
+ // FIXME: I am not sure if this is accurate. Better test the last track's RT field
+ else if( inf.diskState() == STATE_INCOMPLETE )
+ inf.m_numTracks--; // do not count the invisible track
+
+ inf.m_rewritable = dInf->erasable;
+
+ //
+ // This is the Last Possible Lead-Out Start Address in HMSF format
+ // This is only valid for CD-R(W) and DVD+R media.
+ // For complete media this shall be filled with 0xff
+ //
+ if( dInf->lead_out_m != 0xff &&
+ dInf->lead_out_r != 0xff &&
+ dInf->lead_out_s != 0xff &&
+ dInf->lead_out_f != 0xff )
+ inf.m_capacity = K3b::Msf( dInf->lead_out_m + dInf->lead_out_r*60,
+ dInf->lead_out_s,
+ dInf->lead_out_f ) - 150;
+
+ //
+ // This is the position where the next Session shall be recorded in HMSF format
+ // This is only valid for CD-R(W) and DVD+R media.
+ // For complete media this shall be filled with 0xff
+ //
+ if( dInf->lead_in_m != 0xff &&
+ dInf->lead_in_r != 0xff &&
+ dInf->lead_in_s != 0xff &&
+ dInf->lead_in_f != 0xff )
+ inf.m_usedCapacity = K3b::Msf( dInf->lead_in_m + dInf->lead_in_r*60,
+ dInf->lead_in_s,
+ dInf->lead_in_f ) - 4500;
+
+ delete [] data;
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << " fabricating disk information for a stupid device." << endl;
+ Toc toc = readToc();
+ if( !toc.isEmpty() ) {
+ inf.m_diskState = STATE_COMPLETE;
+ inf.m_lastSessionState = STATE_COMPLETE;
+ inf.m_numTracks = toc.count();
+ inf.m_capacity = inf.m_usedCapacity = toc.length();
+ }
+ }
+
+
+ //
+ // The mediatype needs to be set
+ //
+ inf.m_mediaType = mediaType();
+
+ // At least some Plextor drives return profile NONE for CD media
+ // or CD_ROM for writable media
+ if( inf.m_mediaType & (MEDIA_UNKNOWN|MEDIA_NONE|MEDIA_CD_ROM) ) {
+ // probably it is a CD
+ if( inf.rewritable() )
+ inf.m_mediaType = MEDIA_CD_RW;
+ else if( inf.empty() || inf.appendable() )
+ inf.m_mediaType = MEDIA_CD_R;
+ else
+ inf.m_mediaType = MEDIA_CD_ROM;
+ }
+
+ if( inf.m_mediaType & MEDIA_DVD_ALL ) {
+ if( readDvdStructure( &data, dataLen ) ) {
+ // some debugging stuff
+ K3b::Msf sda, eda, ea0;
+ sda = ( data[4+5]<<16 | data[4+6] << 8 | data[4+7] );
+ eda = ( data[4+9]<<16 | data[4+10] << 8 | data[4+11] );
+ ea0 = ( data[4+13]<<16 | data[4+14] << 8 | data[4+15] );
+
+ k3bDebug() << "First sec data area: " << sda.toString()
+ << " (LBA " << QString::number(sda.lba())
+ << ") (" << QString::number(sda.mode1Bytes()) << endl;
+ k3bDebug() << "Last sec data area: " << eda.toString()
+ << " (LBA " << QString::number(eda.lba())
+ << ") (" << QString::number(eda.mode1Bytes()) << " Bytes)" << endl;
+ k3bDebug() << "Last sec layer 1: " << ea0.toString()
+ << " (LBA " << QString::number(ea0.lba())
+ << ") (" << QString::number(ea0.mode1Bytes()) << " Bytes)" << endl;
+
+
+ K3b::Msf da0 = ea0 - sda + 1;
+ K3b::Msf da1 = eda - ea0;
+ k3bDebug() << "Layer 1 length: " << da0.toString()
+ << " (LBA " << QString::number(da0.lba())
+ << ") (" << QString::number(da0.mode1Bytes()) << " Bytes)" << endl;
+ k3bDebug() << "Layer 2 length: " << da1.toString()
+ << " (LBA " << QString::number(da1.lba())
+ << ") (" << QString::number(da1.mode1Bytes()) << " Bytes)" << endl;
+
+ inf.m_numLayers = ((data[6]&0x60) == 0 ? 1 : 2);
+
+ bool otp = (data[4+2] & 0xF);
+
+ // ea0 is 0 if the medium does not use Opposite track path
+ if( otp && ea0 > 0 )
+ inf.m_firstLayerSize = da0;
+ else
+ inf.m_firstLayerSize = 0;
+
+ delete [] data;
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) Unable to read DVD structure for num of layers." << endl;
+ inf.m_numLayers = ( (inf.m_mediaType & MEDIA_WRITABLE_DVD_DL) ? 2 : 1 );
+ }
+ }
+
+
+ //
+ // Number of sessions for non-empty disks
+ //
+ if( inf.diskState() != STATE_EMPTY ) {
+ int sessions = numSessions();
+ if( sessions >= 0 )
+ inf.m_numSessions = sessions;
+ else
+ k3bDebug() << "(K3bDevice::Device) could not get session info via READ TOC/PMA/ATIP." << endl;
+ }
+ else
+ inf.m_numSessions = 0;
+
+ inf.m_mediaId = mediaId( inf.mediaType() );
+
+ //
+ // Now we determine the size:
+
+ // for all empty and appendable media READ FORMAT CAPACITIES should return the proper unformatted size
+ // for complete disks we may use the READ_CAPACITY command or the start sector from the leadout
+ //
+ int media = inf.mediaType();
+ //
+ // Use the profile if available because DVD-ROM units need to treat DVD+-R(W) media as DVD-ROM
+ // if supported at all
+ //
+ if( inf.currentProfile() == MEDIA_DVD_ROM )
+ media = MEDIA_DVD_ROM;
+
+ switch( media ) {
+ case MEDIA_CD_R:
+ case MEDIA_CD_RW:
+ if( inf.m_capacity == 0 ) {
+ if( readTocPmaAtip( &data, dataLen, 0x4, true, 0 ) ) {
+
+ struct atip_descriptor* atip = (struct atip_descriptor*)data;
+
+ if( dataLen >= 11 ) {
+ inf.m_capacity = K3b::Msf( atip->lead_out_m, atip->lead_out_s, atip->lead_out_f ) - 150;
+ debugBitfield( &atip->lead_out_m, 3 );
+ k3bDebug() << blockDeviceName() << ": ATIP capacity: " << inf.m_capacity.toString() << endl;
+ }
+
+ delete [] data;
+ }
+ }
+
+ //
+ // for empty and appendable media capacity and usedCapacity should be filled in from
+ // diskinfo above. If not they are both still 0
+ //
+ if( inf.m_capacity != 0 &&
+ ( inf.diskState() == STATE_EMPTY || inf.m_usedCapacity != 0 ) ) {
+ // done.
+ break;
+ }
+
+ default:
+ case MEDIA_CD_ROM:
+ if( inf.m_capacity > 0 && inf.m_usedCapacity == 0 )
+ inf.m_usedCapacity = inf.m_capacity;
+
+ if( inf.m_usedCapacity == 0 ) {
+ K3b::Msf readCap;
+ if( readCapacity( readCap ) ) {
+ k3bDebug() << "(K3bDevice::Device) READ CAPACITY: " << readCap.toString()
+ << " other capacity: " << inf.m_capacity.toString() << endl;
+ //
+ // READ CAPACITY returns the last written sector
+ // that means the size is actually readCap + 1
+ //
+ inf.m_usedCapacity = readCap + 1;
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << " Falling back to readToc for capacity." << endl;
+ inf.m_usedCapacity = readToc().length();
+ }
+ }
+
+ case MEDIA_DVD_ROM: {
+ K3b::Msf readCap;
+ if( readCapacity( readCap ) ) {
+ k3bDebug() << "(K3bDevice::Device) READ CAPACITY: " << readCap.toString()
+ << " other capacity: " << inf.m_capacity.toString() << endl;
+ //
+ // READ CAPACITY returns the last written sector
+ // that means the size is actually readCap + 1
+ //
+ inf.m_usedCapacity = readCap + 1;
+ }
+ else {
+ //
+ // Only one track, use it's size
+ //
+ if( readTrackInformation( &data, dataLen, 0x1, 0x1 ) ) {
+ track_info_t* trackInfo = (track_info_t*)data;
+ inf.m_usedCapacity = from4Byte( trackInfo->track_size );
+ delete [] data;
+ }
+ else
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << "READ TRACK INFORMATION for DVD-ROM failed." << endl;
+ }
+
+ break;
+ }
+
+ case MEDIA_DVD_PLUS_R:
+ case MEDIA_DVD_PLUS_R_DL:
+ if( inf.appendable() || inf.empty() ) {
+ //
+ // get remaining space via the invisible track
+ //
+ if( readTrackInformation( &data, dataLen, 0x1, /*0xff*/ inf.numTracks()+1 ) ) {
+ track_info_t* trackInfo = (track_info_t*)data;
+ inf.m_usedCapacity = from4Byte( trackInfo->track_start );
+ inf.m_capacity = from4Byte( trackInfo->track_start ) + from4Byte( trackInfo->track_size );
+ delete [] data;
+ }
+ }
+ else {
+ if( readTrackInformation( &data, dataLen, 0x1, inf.numTracks() ) ) {
+ track_info_t* trackInfo = (track_info_t*)data;
+ inf.m_capacity = inf.m_usedCapacity
+ = from4Byte( trackInfo->track_start ) + from4Byte( trackInfo->track_size );
+ delete [] data;
+ }
+ }
+ break;
+
+ case MEDIA_DVD_R:
+ case MEDIA_DVD_R_SEQ:
+ case MEDIA_DVD_R_DL:
+ case MEDIA_DVD_R_DL_JUMP:
+ case MEDIA_DVD_R_DL_SEQ:
+ //
+ // get data from the incomplete track (which is NOT the invisible track 0xff)
+ // This will fail in case the media is complete!
+ //
+ if( readTrackInformation( &data, dataLen, 0x1, inf.numTracks()+1 ) ) {
+ track_info_t* trackInfo = (track_info_t*)data;
+ inf.m_usedCapacity = from4Byte( trackInfo->track_start );
+ inf.m_capacity = from4Byte( trackInfo->free_blocks ) + from4Byte( trackInfo->track_start );
+ delete [] data;
+ }
+
+ //
+ // Get the "really" used space without border-out
+ //
+ if( !inf.empty() ) {
+ K3b::Msf readCap;
+ if( readCapacity( readCap ) ) {
+ //
+ // READ CAPACITY returns the last written sector
+ // that means the size is actually readCap + 1
+ //
+ inf.m_usedCapacity = readCap + 1;
+ }
+ else
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << " READ CAPACITY for DVD-R failed." << endl;
+ }
+
+ break;
+
+ case MEDIA_DVD_RW_OVWR:
+ inf.m_numSessions = 1;
+ case MEDIA_DVD_RW:
+ case MEDIA_DVD_RW_SEQ:
+ // only one track on a DVD-RW media
+ if( readTrackInformation( &data, dataLen, 0x1, 0x1 ) ) {
+ track_info_t* trackInfo = (track_info_t*)data;
+ inf.m_capacity = from4Byte( trackInfo->track_size );
+ if( !inf.empty() ) {
+ if( readFormatCapacity( 0x10, inf.m_capacity ) )
+ k3bDebug() << blockDeviceName() << ": Format capacity 0x10: " << inf.m_capacity.toString() << endl;
+
+ inf.m_usedCapacity = from4Byte( trackInfo->track_size );
+ }
+
+ delete [] data;
+ }
+ break;
+
+ case MEDIA_DVD_PLUS_RW: {
+ K3b::Msf currentMax;
+ int currentMaxFormat = 0;
+ if( readFormatCapacity( 0x26, inf.m_capacity, &currentMax, &currentMaxFormat ) ) {
+ if( currentMaxFormat == 0x1 ) { // unformatted or blank media
+ inf.m_usedCapacity = 0;
+ inf.m_capacity = currentMax;
+ }
+ else {
+ inf.m_usedCapacity = currentMax;
+ // Plextor drives tend to screw things up and report invalid values
+ // for the max format capacity of 1.4 GB DVD media
+ if ( inf.bgFormatState() == BG_FORMAT_COMPLETE ) {
+ inf.m_capacity = currentMax;
+ }
+ }
+ }
+ else
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << " READ FORMAT CAPACITIES for DVD+RW failed." << endl;
+
+ break;
+ }
+
+ case MEDIA_BD_R:
+ case MEDIA_BD_R_SRM:
+ case MEDIA_BD_R_SRM_POW:
+ case MEDIA_BD_R_RRM:
+ case MEDIA_BD_RE:
+ //
+ // get the invisible track's first sector
+ // or the next writable address of the last open track
+ //
+ if( readDiscInformation( &data, dataLen ) ) {
+ int lastTrack = (int)( data[11]<<8 | data[6] );
+ delete [] data;
+
+ if( readTrackInformation( &data, dataLen, 1, lastTrack ) ) {
+
+ // capacity: last track's start address + last track's size
+ inf.m_capacity = from4Byte( data+8 ) + from4Byte( data+24 );
+
+ if( data[6] & 0x80 )
+ inf.m_usedCapacity = from4Byte( data+8 );
+ else if( data[7] & 0x1 )
+ inf.m_usedCapacity = from4Byte( data+12 );
+ delete [] data;
+ }
+ }
+ break;
+
+ case MEDIA_BD_ROM: {
+ K3b::Msf readCap;
+ if( readCapacity( readCap ) ) {
+ //
+ // READ CAPACITY returns the last written sector
+ // that means the size is actually readCap + 1
+ //
+ inf.m_usedCapacity = readCap + 1;
+ }
+
+ break;
+ }
+ }
+ }
+
+ if( needToClose )
+ close();
+ }
+
+ return inf;
+}
+
+
+int K3bDevice::Device::mediaType() const
+{
+ int m = MEDIA_UNKNOWN;
+
+ if( testUnitReady() ) {
+
+ m = currentProfile();
+
+ if( m & (MEDIA_UNKNOWN|MEDIA_DVD_ROM|MEDIA_HD_DVD_ROM) ) {
+ //
+ // We prefere the mediatype as reported by the media since this way
+ // even ROM drives may report the correct type of writable media.
+ //
+
+ // 4 bytes header + 2048 bytes layer descriptor
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+ if( readDvdStructure( &data, dataLen ) ) {
+ switch( data[4]&0xF0 ) {
+ case 0x00: m = MEDIA_DVD_ROM; break;
+ case 0x10: m = MEDIA_DVD_RAM; break;
+ case 0x20: m = MEDIA_DVD_R; break; // there seems to be no value for DVD-R DL, it reports DVD-R
+ case 0x30: m = MEDIA_DVD_RW; break;
+ case 0x40: m = MEDIA_HD_DVD_ROM; break;
+ case 0x50: m = MEDIA_HD_DVD_R; break;
+ case 0x60: m = MEDIA_HD_DVD_RAM; break;
+ case 0x90: m = MEDIA_DVD_PLUS_RW; break;
+ case 0xA0: m = MEDIA_DVD_PLUS_R; break;
+ case 0xE0: m = MEDIA_DVD_PLUS_R_DL; break;
+ default:
+ k3bDebug() << "(K3bDevice::Device) unknown dvd media type: " << QString::number(data[4]&0xF0, 8) << endl;
+ break; // unknown
+ }
+
+ delete [] data;
+ }
+ }
+
+ if( m & (MEDIA_UNKNOWN|MEDIA_BD_ROM) ) {
+ //
+ // We prefere the mediatype as reported by the media since this way
+ // even ROM drives may report the correct type of writable media.
+ //
+
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+ if( readDiscStructure( &data, dataLen, 1, 0 ) ) {
+ if( dataLen > 4+12 &&
+ data[4+8] == 'B' && data[4+9] == 'D' ) {
+ switch( data[4+10] ) {
+ case 'O': m = MEDIA_BD_ROM; break;
+ case 'W': m = MEDIA_BD_RE; break;
+ case 'R': m = MEDIA_BD_R; break;
+ }
+ }
+
+ delete [] data;
+ }
+ }
+
+ //
+ // Only old CD or DVD devices do not report a current profile
+ // or report CD-ROM profile for all CD types
+ //
+ if( m & (MEDIA_UNKNOWN|MEDIA_CD_ROM) ) {
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+ if( readTocPmaAtip( &data, dataLen, 4, false, 0 ) ) {
+ if( (data[6]>>6)&1 )
+ m = MEDIA_CD_RW;
+ else
+ m = MEDIA_CD_R;
+
+ delete [] data;
+ }
+ else
+ m = MEDIA_CD_ROM;
+ }
+ }
+
+ return m;
+}
+
+
+bool K3bDevice::Device::readSectorsRaw( unsigned char *buf, int start, int count ) const
+{
+ return readCd( buf, count*2352,
+ 0, // all sector types
+ false, // no dap
+ start,
+ count,
+ true, // SYNC
+ true, // HEADER
+ true, // SUBHEADER
+ true, // USER DATA
+ true, // EDC/ECC
+ 0, // no c2 info
+ 0 );
+}
+
+
+
+void K3bDevice::Device::checkForJustLink()
+{
+ unsigned char* ricoh = 0;
+ unsigned int ricohLen = 0;
+ if( modeSense( &ricoh, ricohLen, 0x30 ) ) {
+
+ //
+ // 8 byte mode header + 6 byte page data
+ //
+
+ if( ricohLen >= 14 ) {
+ ricoh_mode_page_30* rp = (ricoh_mode_page_30*)(ricoh+8);
+ d->burnfree = rp->BUEFS;
+ }
+
+ delete [] ricoh;
+ }
+}
+
+
+void K3bDevice::Device::checkFeatures()
+{
+ unsigned char header[1024];
+ ::memset( header, 0, 1024 );
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_GET_CONFIGURATION;
+ cmd[1] = 2;
+ cmd[9] = 0; // Necessary to set the proper command length
+
+
+ //
+ // CD writing features
+ //
+ cmd[2] = FEATURE_CD_MASTERING>>8;
+ cmd[3] = FEATURE_CD_MASTERING;
+ cmd[8] = 8+8;
+ if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
+ unsigned int len = from4Byte( header );
+ if( len >= 12 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "CD Mastering" << endl;
+#ifdef WORDS_BIGENDIAN
+ struct cd_mastering_feature {
+ unsigned char reserved1 : 1;
+ unsigned char BUF : 1; // Burnfree
+ unsigned char SAO : 1; // Session At Once writing
+ unsigned char raw_ms : 1; // Writing Multisession in Raw Writing Mode
+ unsigned char raw : 1; // Writing in WRITINGMODE_RAW mode
+ unsigned char testwrite : 1; // Simulation write support
+ unsigned char cd_rw : 1; // CD-RW support
+ unsigned char rw_sub : 1; // Write R-W sub channels with user data
+ unsigned char max_cue_length[3];
+ };
+#else
+ struct cd_mastering_feature {
+ unsigned char rw_sub : 1; // Write R-W sub channels with user data
+ unsigned char cd_rw : 1; // CD-RW support
+ unsigned char testwrite : 1; // Simulation write support
+ unsigned char raw : 1; // Writing in WRITINGMODE_RAW mode
+ unsigned char raw_ms : 1; // Writing Multisession in Raw Writing Mode
+ unsigned char SAO : 1; // Session At Once writing
+ unsigned char BUF : 1; // Burnfree
+ unsigned char reserved1 : 1;
+ unsigned char max_cue_length[3];
+ };
+#endif
+
+ struct cd_mastering_feature* p = (struct cd_mastering_feature*)&header[12];
+ if( p->BUF ) d->burnfree = true;
+ d->writeCapabilities |= MEDIA_CD_R;
+ if( p->cd_rw )
+ d->writeCapabilities |= MEDIA_CD_RW;
+// if( p->WRITINGMODE_SAO ) m_writeModes |= WRITINGMODE_SAO;
+// if( p->raw || p->raw_ms ) m_writeModes |= WRITINGMODE_RAW; // WRITINGMODE_RAW16 always supported when raw is supported?
+ }
+ }
+
+ cmd[2] = FEATURE_CD_TRACK_AT_ONCE>>8;
+ cmd[3] = FEATURE_CD_TRACK_AT_ONCE;
+ cmd[8] = 8+8;
+ if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
+ unsigned int len = from4Byte( header );
+ if( len >= 12 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "CD Track At Once" << endl;
+#ifdef WORDS_BIGENDIAN
+ struct cd_track_at_once_feature {
+ unsigned char reserved1 : 1;
+ unsigned char BUF : 1; // Burnfree
+ unsigned char reserved2 : 1;
+ unsigned char rw_raw : 1; // Writing R-W subcode in Raw mode
+ unsigned char rw_pack : 1; // Writing R-W subcode in Packet mode
+ unsigned char testwrite : 1; // Simulation write support
+ unsigned char cd_rw : 1; // CD-RW support
+ unsigned char rw_sub : 1; // Write R-W sub channels with user data
+ unsigned char reserved3;
+ unsigned char data_type[2];
+ };
+#else
+ struct cd_track_at_once_feature {
+ unsigned char rw_sub : 1; // Write R-W sub channels with user data
+ unsigned char cd_rw : 1; // CD-RW support
+ unsigned char testwrite : 1; // Simulation write support
+ unsigned char rw_pack : 1; // Writing R-W subcode in Packet mode
+ unsigned char rw_raw : 1; // Writing R-W subcode in Raw mode
+ unsigned char reserved2 : 1;
+ unsigned char BUF : 1; // Burnfree
+ unsigned char reserved1 : 1;
+ unsigned char reserved3;
+ unsigned char data_type[2];
+ };
+#endif
+
+ struct cd_track_at_once_feature* p = (struct cd_track_at_once_feature*)&header[12];
+ m_writeModes |= WRITINGMODE_TAO;
+ if( p->BUF ) d->burnfree = true;
+ d->writeCapabilities |= MEDIA_CD_R;
+ if( p->cd_rw )
+ d->writeCapabilities |= MEDIA_CD_RW;
+
+ // is the following correct? What exactly does rw_sub tell us?
+// if( m_writeModes & WRITINGMODE_RAW ) {
+// if( p->rw_raw ) m_writeModes |= WRITINGMODE_RAW_R96R;
+// if( p->rw_pack ) m_writeModes |= WRITINGMODE_RAW_R96P;
+// }
+
+// // check the data types for 1, 2, and 3 (raw16, raw96p, and raw96r)
+// debugBitfield( p->data_type, 2 );
+// if( m_writeModes & WRITINGMODE_RAW ) {
+// if( p->data_type[1] & 0x20 ) m_writeModes |= WRITINGMODE_RAW_R16;
+// if( p->data_type[1] & 0x40 ) m_writeModes |= WRITINGMODE_RAW_R96P;
+// if( p->data_type[1] & 0x80 ) m_writeModes |= WRITINGMODE_RAW_R96R;
+// }
+ }
+ }
+
+ cmd[2] = FEATURE_CD_RW_MEDIA_WRITE_SUPPORT>>8;
+ cmd[3] = FEATURE_CD_RW_MEDIA_WRITE_SUPPORT;
+ cmd[8] = 8+8;
+ if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
+ unsigned int len = from4Byte( header );
+ if( len >= 12 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "CD-RW Media Write Support" << endl;
+ d->writeCapabilities |= (MEDIA_CD_R|MEDIA_CD_RW);
+ }
+ }
+
+
+ //
+ // DVD-ROM
+ //
+ // FIXME: since MMC5 the feature descr. is 8 bytes in length including a dvd dl read bit at byte 6
+ cmd[2] = FEATURE_DVD_READ>>8;
+ cmd[3] = FEATURE_DVD_READ;
+ cmd[8] = 8+8;
+ if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
+ unsigned int len = from4Byte( header );
+ if( len >= 12 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "DVD Read (MMC5)" << endl;
+ d->readCapabilities |= MEDIA_DVD_ROM;
+ if( header[8+6] & 0x1 )
+ d->readCapabilities |= MEDIA_WRITABLE_DVD_DL;
+ }
+ }
+ else {
+ // retry with pre-MMC5 length
+ cmd[8] = 8+4;
+ if( !cmd.transport( TR_DIR_READ, header, 12 ) ) {
+ unsigned int len = from4Byte( header );
+ if( len >= 8 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "DVD Read (pre-MMC5)" << endl;
+ d->readCapabilities |= MEDIA_DVD_ROM;
+ }
+ }
+ }
+
+ //
+ // DVD+R(W) writing features
+ //
+ cmd[2] = FEATURE_DVD_PLUS_R>>8;
+ cmd[3] = FEATURE_DVD_PLUS_R;
+ cmd[8] = 8+8;
+ if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
+ unsigned int len = from4Byte( header );
+ if( len >= 12 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "DVD+R" << endl;
+ d->readCapabilities |= MEDIA_DVD_PLUS_R;
+ if( header[12] & 0x1 )
+ d->writeCapabilities |= MEDIA_DVD_PLUS_R;
+ }
+ }
+
+ cmd[2] = FEATURE_DVD_PLUS_RW>>8;
+ cmd[3] = FEATURE_DVD_PLUS_RW;
+ cmd[8] = 8+8;
+ if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
+ unsigned int len = from4Byte( header );
+ if( len >= 12 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "DVD+RW" << endl;
+#ifdef WORDS_BIGENDIAN
+ struct dvd_plus_rw_feature {
+ unsigned char reserved1 : 7;
+ unsigned char write : 1;
+ unsigned char reserved2 : 6;
+ unsigned char quick_start : 1;
+ unsigned char close_only : 1;
+ // and some stuff we do not use here...
+ };
+#else
+ struct dvd_plus_rw_feature {
+ unsigned char write : 1;
+ unsigned char reserved1 : 7;
+ unsigned char close_only : 1;
+ unsigned char quick_start : 1;
+ unsigned char reserved2 : 6;
+ // and some stuff we do not use here...
+ };
+#endif
+
+ struct dvd_plus_rw_feature* p = (struct dvd_plus_rw_feature*)&header[12];
+ d->readCapabilities |= MEDIA_DVD_PLUS_RW;
+ if( p->write )
+ d->writeCapabilities |= MEDIA_DVD_PLUS_RW;
+ }
+ }
+
+
+ // some older DVD-ROM drives claim to support DVD+R DL
+ if( d->writeCapabilities & MEDIA_DVD_PLUS_R ) {
+ cmd[2] = FEATURE_DVD_PLUS_RW_DUAL_LAYER>>8;
+ cmd[3] = FEATURE_DVD_PLUS_RW_DUAL_LAYER;
+ cmd[8] = 8+8;
+ if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
+ unsigned int len = from4Byte( header );
+ if( len >= 12 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "DVD+RW Double Layer" << endl;
+ d->readCapabilities |= MEDIA_DVD_PLUS_RW_DL;
+ if( header[12] & 0x1 )
+ d->writeCapabilities |= MEDIA_DVD_PLUS_RW_DL;
+ }
+ }
+
+ cmd[2] = FEATURE_DVD_PLUS_R_DUAL_LAYER>>8;
+ cmd[3] = FEATURE_DVD_PLUS_R_DUAL_LAYER;
+ cmd[8] = 8+8;
+ if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
+ unsigned int len = from4Byte( header );
+ if( len >= 12 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "DVD+R Double Layer" << endl;
+ d->readCapabilities |= MEDIA_DVD_PLUS_R_DL;
+ if( header[12] & 0x1 )
+ d->writeCapabilities |= MEDIA_DVD_PLUS_R_DL;
+ }
+ }
+ }
+
+
+ //
+ // Blue Ray
+ //
+ // We do not care for the different BD classes and versions here
+ //
+ cmd[2] = FEATURE_BD_READ>>8;
+ cmd[3] = FEATURE_BD_READ;
+ cmd[8] = 8+32;
+ if( !cmd.transport( TR_DIR_READ, header, 40 ) ) {
+ unsigned int len = from4Byte( header );
+ if( len >= 36 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "BD Read" << endl;
+ if( header[8+8] || header[8+9] || header[8+10] || header[8+11] || header[8+12] || header[8+13] || header[8+14] || header[8+15] )
+ d->readCapabilities |= MEDIA_BD_RE;
+ if( header[8+16] || header[8+17] || header[8+18] || header[8+19] || header[8+20] || header[8+21] || header[8+22] || header[8+23] )
+ d->readCapabilities |= MEDIA_BD_R;
+ if( header[8+24] || header[8+25] || header[8+26] || header[8+27] || header[8+28] || header[8+29] || header[8+30] || header[8+31] )
+ d->readCapabilities |= MEDIA_BD_ROM;
+ }
+ }
+
+ cmd[2] = FEATURE_BD_WRITE>>8;
+ cmd[3] = FEATURE_BD_WRITE;
+ cmd[8] = 8+24;
+ if( !cmd.transport( TR_DIR_READ, header, 32 ) ) {
+ unsigned int len = from4Byte( header );
+ if( len >= 28 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "BD Write" << endl;
+ if( header[8+8] || header[8+9] || header[8+10] || header[8+11] || header[8+12] || header[8+13] || header[8+14] || header[8+15] )
+ d->writeCapabilities |= MEDIA_BD_RE;
+ if( header[8+16] || header[8+17] || header[8+18] || header[8+19] || header[8+20] || header[8+21] || header[8+22] || header[8+23] ) {
+ d->writeCapabilities |= MEDIA_BD_R;
+ m_writeModes |= WRITINGMODE_SRM;
+
+ cmd[2] = FEATURE_BD_PSEUDO_OVERWRITE>>8;
+ cmd[3] = FEATURE_BD_PSEUDO_OVERWRITE;
+ cmd[8] = 8+8;
+ if( !cmd.transport( TR_DIR_READ, header, 8+8 ) ) {
+ unsigned int len = from4Byte( header );
+ if( len >= 4+8 ) {
+ m_writeModes |= WRITINGMODE_SRM_POW;
+ }
+ }
+
+ cmd[2] = FEATURE_RANDOM_WRITABLE>>8;
+ cmd[3] = FEATURE_RANDOM_WRITABLE;
+ cmd[8] = 8+16;
+ if( !cmd.transport( TR_DIR_READ, header, 8+16 ) ) {
+ unsigned int len = from4Byte( header );
+ if( len >= 4+16 ) {
+ m_writeModes |= WRITINGMODE_RRM;
+ }
+ }
+ }
+ }
+ }
+
+
+
+ //
+ // DVD-R(W)
+ //
+ cmd[2] = FEATURE_DVD_R_RW_WRITE>>8;
+ cmd[3] = FEATURE_DVD_R_RW_WRITE;
+ cmd[8] = 16;
+ if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
+ unsigned int len = from4Byte( header );
+ if( len >= 12 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "DVD-R/-RW Write" << endl;
+#ifdef WORDS_BIGENDIAN
+ struct dvd_r_rw_write_feature {
+ unsigned char reserved1 : 1;
+ unsigned char BUF : 1; // Burnfree
+ unsigned char reserved2 : 2;
+ unsigned char RDL : 1;
+ unsigned char testwrite : 1; // Simulation write support
+ unsigned char dvd_rw : 1; // DVD-RW Writing
+ unsigned char reserved3 : 1;
+ unsigned char reserved4[3];
+ };
+#else
+ struct dvd_r_rw_write_feature {
+ unsigned char reserved3 : 1;
+ unsigned char dvd_rw : 1; // DVD-RW Writing
+ unsigned char testwrite : 1; // Simulation write support
+ unsigned char RDL : 1;
+ unsigned char reserved2 : 2;
+ unsigned char BUF : 1; // Burnfree
+ unsigned char reserved1 : 1;
+ unsigned char reserved4[3];
+ };
+#endif
+
+ struct dvd_r_rw_write_feature* p = (struct dvd_r_rw_write_feature*)&header[12];
+ if( p->BUF ) d->burnfree = true;
+ d->writeCapabilities |= (MEDIA_DVD_R|MEDIA_DVD_R_SEQ);
+ if( p->dvd_rw )
+ d->writeCapabilities |= (MEDIA_DVD_RW|MEDIA_DVD_RW_SEQ);
+ if( p->RDL )
+ d->writeCapabilities |= (MEDIA_DVD_R_DL|MEDIA_DVD_R_DL_SEQ);
+
+ m_dvdMinusTestwrite = p->testwrite;
+ }
+ }
+
+
+ //
+ // DVD-RW restricted overwrite check
+ //
+ cmd[2] = FEATURE_RIGID_RESTRICTED_OVERWRITE>>8;
+ cmd[3] = FEATURE_RIGID_RESTRICTED_OVERWRITE;
+ cmd[8] = 16;
+ if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
+ unsigned int len = from4Byte( header );
+ if( len >= 12 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "Rigid Restricted Overwrite" << endl;
+ m_writeModes |= WRITINGMODE_RES_OVWR;
+ d->writeCapabilities |= (MEDIA_DVD_RW|MEDIA_DVD_RW_OVWR);
+ }
+ }
+
+
+ //
+ // DVD-R Dual Layer Layer
+ //
+ cmd[2] = FEATURE_LAYER_JUMP_RECORDING>>8;
+ cmd[3] = FEATURE_LAYER_JUMP_RECORDING;
+ cmd[8] = 12;
+ if( !cmd.transport( TR_DIR_READ, header, 12 ) ) {
+ // Now the jump feature is longer than 4 bytes but we don't need the link sizes.
+ unsigned int len = from4Byte( header );
+ if( len >= 8 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "Layer Jump Recording" << endl;
+ d->writeCapabilities |= (MEDIA_DVD_R_DL|MEDIA_DVD_R_DL_JUMP);
+ m_writeModes |= WRITINGMODE_LAYER_JUMP;
+ }
+ }
+
+
+ //
+ // HD-DVD-ROM
+ //
+ cmd[2] = FEATURE_HD_DVD_READ>>8;
+ cmd[3] = FEATURE_HD_DVD_READ;
+ cmd[8] = 16;
+ if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
+ unsigned int len = from4Byte( header );
+ if( len >= 12 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "HD-DVD Read" << endl;
+ d->readCapabilities |= MEDIA_HD_DVD_ROM;
+ if( header[8+4] & 0x1 )
+ d->readCapabilities |= MEDIA_HD_DVD_R;
+ if( header[8+6] & 0x1 )
+ d->readCapabilities |= MEDIA_HD_DVD_RAM;
+ }
+ }
+
+
+ //
+ // HD-DVD-R(AM)
+ //
+ cmd[2] = FEATURE_HD_DVD_WRITE>>8;
+ cmd[3] = FEATURE_HD_DVD_WRITE;
+ cmd[8] = 16;
+ if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
+ unsigned int len = from4Byte( header );
+ if( len >= 12 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "HD-DVD Write" << endl;
+ if( header[8+4] & 0x1 )
+ d->writeCapabilities |= MEDIA_HD_DVD_R;
+ if( header[8+6] & 0x1 )
+ d->writeCapabilities |= MEDIA_HD_DVD_RAM;
+ }
+ }
+
+
+
+ //
+ // Get the profiles
+ //
+ // the max len of the returned data is 8 (header) + 4 (feature) + 255 (additional length)
+ //
+ cmd[2] = FEATURE_PROFILE_LIST>>8;
+ cmd[3] = FEATURE_PROFILE_LIST;
+ cmd[8] = 12; // get the number of returned profiles first
+ if( !cmd.transport( TR_DIR_READ, header, 12 ) ) {
+ unsigned int len = from4Byte( header ) + 4;
+ if( len >= 12 ) {
+ cmd[7] = len>>8;
+ cmd[8] = len;
+ if( !cmd.transport( TR_DIR_READ, header, len ) ) {
+ int featureLen( header[11] );
+ for( int j = 0; j < featureLen; j+=4 ) {
+ short profile = from2Byte( &header[12+j] );
+
+ switch (profile) {
+ case 0x08:
+ d->supportedProfiles |= MEDIA_CD_ROM;
+ break;
+ case 0x09:
+ d->supportedProfiles |= MEDIA_CD_R;
+ break;
+ case 0x0A:
+ d->supportedProfiles |= MEDIA_CD_RW;
+ break;
+ case 0x10:
+ d->supportedProfiles |= MEDIA_DVD_ROM;
+ // d->readCapabilities |= MEDIA_DVD_ROM;
+ break;
+ case 0x11:
+ d->supportedProfiles |= MEDIA_DVD_R_SEQ;
+ // d->writeCapabilities |= (MEDIA_DVD_R|MEDIA_DVD_R_SEQ);
+ break;
+ case 0x12:
+ d->supportedProfiles |= MEDIA_DVD_RAM;
+// d->readCapabilities |= (MEDIA_DVD_RAM|MEDIA_DVD_ROM);
+// d->writeCapabilities |= MEDIA_DVD_RAM;
+ break;
+ case 0x13:
+ d->supportedProfiles |= MEDIA_DVD_RW_OVWR;
+ // d->writeCapabilities |= (MEDIA_DVD_RW|MEDIA_DVD_RW_OVWR);
+ break;
+ case 0x14:
+ d->supportedProfiles |= MEDIA_DVD_RW_SEQ;
+ // d->writeCapabilities |= (MEDIA_DVD_RW|MEDIA_DVD_R|MEDIA_DVD_RW_SEQ|MEDIA_DVD_R_SEQ);
+ break;
+ case 0x15:
+ d->supportedProfiles |= MEDIA_DVD_R_DL_SEQ;
+ // d->writeCapabilities |= (MEDIA_DVD_R|MEDIA_DVD_R_DL|MEDIA_DVD_R_SEQ|MEDIA_DVD_R_DL_SEQ);
+ break;
+ case 0x16:
+ d->supportedProfiles |= MEDIA_DVD_R_DL_JUMP;
+ // d->writeCapabilities |= (MEDIA_DVD_R|MEDIA_DVD_R_DL||MEDIA_DVD_R_DL_JUMP);
+ break;
+ case 0x1A:
+ d->supportedProfiles |= MEDIA_DVD_PLUS_RW;
+ // d->writeCapabilities |= MEDIA_DVD_PLUS_RW;
+ break;
+ case 0x1B:
+ d->supportedProfiles |= MEDIA_DVD_PLUS_R;
+ // d->writeCapabilities |= MEDIA_DVD_PLUS_R;
+ break;
+ case 0x2A:
+ d->supportedProfiles |= MEDIA_DVD_PLUS_RW_DL;
+ // d->writeCapabilities |= MEDIA_DVD_PLUS_RW_DL;
+ break;
+ case 0x2B:
+ d->supportedProfiles |= MEDIA_DVD_PLUS_R_DL;
+ // d->writeCapabilities |= MEDIA_DVD_PLUS_R_DL;
+ break;
+ case 0x40:
+ d->supportedProfiles |= MEDIA_BD_ROM;
+ break;
+ case 0x41:
+ d->supportedProfiles |= MEDIA_BD_R_SRM;
+ break;
+ case 0x42:
+ d->supportedProfiles |= MEDIA_BD_R_RRM;
+ break;
+ case 0x43:
+ d->supportedProfiles |= MEDIA_BD_RE;
+ break;
+ case 0x50:
+ d->supportedProfiles |= MEDIA_HD_DVD_ROM;
+ break;
+ case 0x51:
+ d->supportedProfiles |= MEDIA_HD_DVD_R;
+ break;
+ case 0x52:
+ d->supportedProfiles |= MEDIA_HD_DVD_RAM;
+ break;
+ default:
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " unknown profile: "
+ << profile << endl;
+ }
+ }
+
+ // some older DVD-ROM drives claim to support DVD+R DL
+ if( !(d->supportedProfiles & MEDIA_DVD_PLUS_R) ) {
+ // remove DVD+R DL capability
+ // d->writeCapabilities &= ~MEDIA_DVD_PLUS_R_DL;
+ d->supportedProfiles &= ~MEDIA_DVD_PLUS_R_DL;
+ }
+ }
+ }
+ }
+}
+
+
+void K3bDevice::Device::checkFor2AFeatures()
+{
+ unsigned char* mm_cap_buffer = 0;
+ unsigned int mm_cap_len = 0;
+
+ if( modeSense( &mm_cap_buffer, mm_cap_len, 0x2A ) ) {
+ mm_cap_page_2A* mm_p = (mm_cap_page_2A*)(mm_cap_buffer+8);
+ if( mm_p->BUF )
+ d->burnfree = true;
+
+ if( mm_p->cd_r_write )
+ d->writeCapabilities |= MEDIA_CD_R;
+ else
+ d->writeCapabilities &= ~MEDIA_CD_R;
+
+ if( mm_p->cd_rw_write )
+ d->writeCapabilities |= MEDIA_CD_RW;
+ else
+ d->writeCapabilities &= ~MEDIA_CD_RW;
+
+ if( mm_p->dvd_r_write )
+ d->writeCapabilities |= MEDIA_DVD_R;
+ else
+ d->writeCapabilities &= ~MEDIA_DVD_R;
+
+ if( mm_p->dvd_rom_read || mm_p->dvd_r_read )
+ d->readCapabilities |= MEDIA_DVD_ROM;
+
+ m_maxReadSpeed = from2Byte(mm_p->max_read_speed);
+ m_bufferSize = from2Byte( mm_p->buffer_size );
+
+ delete [] mm_cap_buffer;
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": read mode page 2A failed!" << endl;
+ }
+}
+
+
+void K3bDevice::Device::checkWritingModes()
+{
+ // if the device is already opened we do not close it
+ // to allow fast multiple method calls in a row
+ bool needToClose = !isOpen();
+
+ if( !open() )
+ return;
+
+ // header size is 8
+ unsigned char* buffer = 0;
+ unsigned int dataLen = 0;
+
+ if( !modeSense( &buffer, dataLen, 0x05 ) ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": modeSense 0x05 failed!" << endl
+ << "(K3bDevice::Device) " << blockDeviceName() << ": Cannot check write modes." << endl;
+ }
+ else if( dataLen < 18 ) { // 8 bytes header + 10 bytes used modepage
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": Missing modepage 0x05 data." << endl
+ << "(K3bDevice::Device) " << blockDeviceName() << ": Cannot check write modes." << endl;
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": dataLen: " << dataLen << endl;
+
+ wr_param_page_05* mp = (struct wr_param_page_05*)(buffer+8);
+
+ // reset some stuff to be on the safe side
+ mp->PS = 0;
+ mp->BUFE = 0;
+ mp->multi_session = 0;
+ mp->test_write = 0;
+ mp->LS_V = 0;
+ mp->copy = 0;
+ mp->fp = 0;
+ mp->host_appl_code= 0;
+ mp->session_format = 0;
+ mp->audio_pause_len[0] = 0;
+ mp->audio_pause_len[1] = 150;
+
+ // WRITINGMODE_TAO
+ mp->write_type = 0x01; // Track-at-once
+ mp->track_mode = 4; // MMC-4 says: 5, cdrecord uses 4 ?
+ mp->dbtype = 8; // Mode 1
+
+ // k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": modeselect WRITINGMODE_TAO data: " << endl;
+ // debugBitfield( buffer, dataLen );
+
+
+ //
+ // if a writer does not support WRITINGMODE_TAO it surely does not support WRITINGMODE_SAO or WRITINGMODE_RAW writing since WRITINGMODE_TAO is the minimal
+ // requirement
+ //
+
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": checking for TAO" << endl;
+ if( modeSelect( buffer, dataLen, 1, 0 ) ) {
+ m_writeModes |= WRITINGMODE_TAO;
+ d->writeCapabilities |= MEDIA_CD_R;
+
+ // WRITINGMODE_SAO
+ mp->write_type = 0x02; // Session-at-once
+
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": checking for SAO" << endl;
+ if( modeSelect( buffer, dataLen, 1, 0 ) )
+ m_writeModes |= WRITINGMODE_SAO;
+
+// mp->dbtype = 1; // Raw data with P and Q Sub-channel (2368 bytes)
+// if( modeSelect( buffer, dataLen, 1, 0 ) ) {
+// m_writeModes |= WRITINGMODE_RAW_R16;
+// }
+
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": checking for SAO_R96P" << endl;
+ mp->dbtype = 2; // Raw data with P-W Sub-channel (2448 bytes)
+ if( modeSelect( buffer, dataLen, 1, 0 ) ) {
+ m_writeModes |= WRITINGMODE_SAO_R96P;
+ }
+
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": checking for SAO_R96R" << endl;
+ mp->dbtype = 3; // Raw data with P-W raw Sub-channel (2448 bytes)
+ if( modeSelect( buffer, dataLen, 1, 0 ) ) {
+ m_writeModes |= WRITINGMODE_SAO_R96R;
+ }
+
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": checking for RAW_R16" << endl;
+ // WRITINGMODE_RAW
+ mp->write_type = 0x03; // WRITINGMODE_RAW
+ mp->dbtype = 1; // Raw data with P and Q Sub-channel (2368 bytes)
+ if( modeSelect( buffer, dataLen, 1, 0 ) ) {
+ m_writeModes |= WRITINGMODE_RAW;
+ m_writeModes |= WRITINGMODE_RAW_R16;
+ }
+
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": checking for RAW_R96P" << endl;
+ mp->dbtype = 2; // Raw data with P-W Sub-channel (2448 bytes)
+ if( modeSelect( buffer, dataLen, 1, 0 ) ) {
+ m_writeModes |= WRITINGMODE_RAW;
+ m_writeModes |= WRITINGMODE_RAW_R96P;
+ }
+
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": checking for RAW_R96R" << endl;
+ mp->dbtype = 3; // Raw data with P-W raw Sub-channel (2448 bytes)
+ if( modeSelect( buffer, dataLen, 1, 0 ) ) {
+ m_writeModes |= WRITINGMODE_RAW;
+ m_writeModes |= WRITINGMODE_RAW_R96R;
+ }
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": modeSelect with WRITINGMODE_TAO failed. No writer" << endl;
+ }
+
+
+ delete [] buffer;
+ }
+
+ if( needToClose )
+ close();
+}
+
+
+int K3bDevice::Device::determineMaximalWriteSpeed() const
+{
+ int ret = 0;
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+
+ if( mediaType() & MEDIA_CD_ALL ) {
+ if( modeSense( &data, dataLen, 0x2A ) ) {
+ mm_cap_page_2A* mm = (mm_cap_page_2A*)&data[8];
+
+ // MMC1 used byte 18 and 19 for the max write speed
+ if( dataLen > 19 )
+ ret = from2Byte( mm->max_write_speed );
+
+ delete [] data;
+
+ if( ret > 0 )
+ return ret;
+ }
+ }
+
+ QValueList<int> list = determineSupportedWriteSpeeds();
+ if( !list.isEmpty() ) {
+ for( QValueList<int>::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
+ ret = QMAX( ret, *it );
+ }
+
+ if( ret > 0 )
+ return ret;
+ else
+ return m_maxWriteSpeed;
+}
+
+
+QValueList<int> K3bDevice::Device::determineSupportedWriteSpeeds() const
+{
+ QValueList<int> ret;
+
+ if( burner() ) {
+ //
+ // Tests with all my drives resulted in 2A for CD and GET PERFORMANCE for DVD media
+ // as the valid method of speed detection.
+ //
+ if( mediaType() & MEDIA_CD_ALL ) {
+ if( !getSupportedWriteSpeedsVia2A( ret, false ) )
+ getSupportedWriteSpeedsViaGP( ret, false );
+
+ // restrict to max speed, although deprecated in MMC3 is still used everywhere and
+ // cdrecord also uses it as the max writing speed.
+ int max = 0;
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+ if( modeSense( &data, dataLen, 0x2A ) ) {
+ mm_cap_page_2A* mm = (mm_cap_page_2A*)&data[8];
+
+ // MMC1 used byte 18 and 19 for the max write speed
+ if( dataLen > 19 )
+ max = from2Byte( mm->max_write_speed );
+
+ delete [] data;
+
+ if( max > 0 ) {
+ while( !ret.isEmpty() && ret.last() > max ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << " writing speed " << ret.last() << " higher than max " << max << endl;
+ ret.pop_back();
+ }
+ }
+ }
+ }
+ else {
+ if( !getSupportedWriteSpeedsViaGP( ret, true ) )
+ getSupportedWriteSpeedsVia2A( ret, true );
+ }
+ }
+
+ return ret;
+}
+
+
+bool K3bDevice::Device::getSupportedWriteSpeedsVia2A( QValueList<int>& list, bool dvd ) const
+{
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+ if( modeSense( &data, dataLen, 0x2A ) ) {
+ mm_cap_page_2A* mm = (mm_cap_page_2A*)&data[8];
+
+ if( dataLen > 32 ) {
+ // we have descriptors
+ unsigned int numDesc = from2Byte( mm->num_wr_speed_des );
+
+ // Some CDs writer returns the number of bytes that contain
+ // the descriptors rather than the number of descriptors
+ // Ensure number of descriptors claimed actually fits in the data
+ // returned by the mode sense command.
+ if( numDesc > ((dataLen - 32 - 8) / 4) )
+ numDesc = (dataLen - 32 - 8) / 4;
+
+ cd_wr_speed_performance* wr = (cd_wr_speed_performance*)mm->wr_speed_des;
+
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << ": Number of supported write speeds via 2A: "
+ << numDesc << endl;
+
+
+ for( unsigned int i = 0; i < numDesc; ++i ) {
+ int s = (int)from2Byte( wr[i].wr_speed_supp );
+ //
+ // some DVD writers report CD writing speeds here
+ // If that is the case we cannot rely on the reported speeds
+ // and need to use the values gained from GET PERFORMANCE.
+ //
+ if( dvd && s < 1352 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << " Invalid DVD speed: " << s << " KB/s" << endl;
+ list.clear();
+ break;
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << " : " << s << " KB/s" << endl;
+
+ if( dvd )
+ s = fixupDvdWritingSpeed( s );
+
+ // sort the list
+ QValueList<int>::iterator it = list.begin();
+ while( it != list.end() && *it < s )
+ ++it;
+ list.insert( it, s );
+ }
+ }
+ }
+
+ delete [] data;
+ }
+
+ return !list.isEmpty();
+}
+
+
+bool K3bDevice::Device::getSupportedWriteSpeedsViaGP( QValueList<int>& list, bool dvd ) const
+{
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+ if( getPerformance( &data, dataLen, 0x3, 0x0 ) ) {
+ int numDesc = (dataLen - 8)/16;
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << ": Number of supported write speeds via GET PERFORMANCE: "
+ << numDesc << endl;
+
+ for( int i = 0; i < numDesc; ++i ) {
+ int s = from4Byte( &data[20+i*16] );
+
+ // Looks as if the code below does not make sense with most drives
+// if( !( data[4+i*16] & 0x2 ) ) {
+// k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+// << " No write speed: " << s << " KB/s" << endl;
+// continue;
+// }
+
+ if( dvd && s < 1352 ) {
+ //
+ // Does this ever happen?
+ //
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << " Invalid DVD speed: " << s << " KB/s" << endl;
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << " : " << s << " KB/s" << endl;
+
+ if( dvd )
+ s = fixupDvdWritingSpeed( s );
+
+ QValueList<int>::iterator it = list.begin();
+ while( it != list.end() && *it < s )
+ ++it;
+ // the speed might already have been found in the 2a modepage
+ if( it == list.end() || *it != s )
+ list.insert( it, s );
+ }
+ }
+
+ delete [] data;
+ }
+
+ return !list.isEmpty();
+}
+
+
+int K3bDevice::Device::getIndex( unsigned long lba ) const
+{
+ // if the device is already opened we do not close it
+ // to allow fast multiple method calls in a row
+ bool needToClose = !isOpen();
+
+ if( !open() )
+ return -1;
+
+ int ret = -1;
+
+ //
+ // first try readCd
+ //
+ unsigned char readData[16];
+ ::memset( readData, 0, 16 );
+
+ //
+ // The index is found in the Mode-1 Q which occupies at least 9 out of 10 successive CD frames
+ // It can be indentified by ADR == 1
+ //
+ // So if the current sector does not provide Mode-1 Q subchannel we try the previous.
+ //
+
+ if( readCd( readData,
+ 16,
+ 1, // CD-DA
+ 0, // no DAP
+ lba,
+ 1,
+ false,
+ false,
+ false,
+ false,
+ false,
+ 0,
+ 2 // Q-Subchannel
+ ) ) {
+ // byte 0: 4 bits CONTROL (MSB) + 4 bits ADR (LSB)
+ if( (readData[0]&0x0f) == 0x1 )
+ ret = readData[2];
+
+ // search previous sector for Mode1 Q Subchannel
+ else if( readCd( readData,
+ 16,
+ 1, // CD-DA
+ 0, // no DAP
+ lba-1,
+ 1,
+ false,
+ false,
+ false,
+ false,
+ false,
+ 0,
+ 2 // Q-Subchannel
+ ) ) {
+ if( (readData[0]&0x0f) == 0x1 )
+ ret = readData[2];
+ else
+ ret = -2;
+ }
+ }
+
+ else {
+ k3bDebug() << "(K3bDevice::Device::getIndex) readCd failed. Trying seek." << endl;
+
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+ if( seek( lba ) && readSubChannel( &data, dataLen, 1, 0 ) ) {
+ // byte 5: 4 bits ADR (MSB) + 4 bits CONTROL (LSB)
+ if( dataLen > 7 && (data[5]>>4 & 0x0F) == 0x1 ) {
+ ret = data[7];
+ }
+ else if( seek( lba-1 ) && readSubChannel( &data, dataLen, 1, 0 ) ) {
+ if( dataLen > 7 && (data[5]>>4 & 0x0F) == 0x1 )
+ ret = data[7];
+ else
+ ret = -2;
+ }
+
+ delete [] data;
+ }
+ else
+ k3bDebug() << "(K3bDevice::Device::getIndex) seek or readSubChannel failed." << endl;
+ }
+
+ if( needToClose )
+ close();
+
+ return ret;
+}
+
+
+bool K3bDevice::Device::searchIndex0( unsigned long startSec,
+ unsigned long endSec,
+ long& pregapStart ) const
+{
+ // if the device is already opened we do not close it
+ // to allow fast multiple method calls in a row
+ bool needToClose = !isOpen();
+
+ if( !open() )
+ return false;
+
+ bool ret = false;
+
+ int lastIndex = getIndex( endSec );
+ if( lastIndex == 0 ) {
+ // there is a pregap
+ // let's find the position where the index turns to 0
+ // we jump in 1 sec steps backwards until we find an index > 0
+ unsigned long sector = endSec;
+ while( lastIndex == 0 && sector > startSec ) {
+ sector -= 75;
+ if( sector < startSec )
+ sector = startSec;
+ lastIndex = getIndex(sector);
+ }
+
+ if( lastIndex == 0 ) {
+ k3bDebug() << "(K3bDevice::Device) warning: no index != 0 found." << endl;
+ }
+ else {
+ // search forward to the first index = 0
+ while( getIndex( sector ) != 0 && sector < endSec )
+ sector++;
+
+ pregapStart = sector;
+ ret = true;
+ }
+ }
+ else if( lastIndex > 0 ) {
+ // no pregap
+ pregapStart = -1;
+ ret = true;
+ }
+
+ if( needToClose )
+ close();
+
+ return ret;
+}
+
+
+bool K3bDevice::Device::indexScan( K3bDevice::Toc& toc ) const
+{
+ // if the device is already opened we do not close it
+ // to allow fast multiple method calls in a row
+ bool needToClose = !isOpen();
+
+ if( !open() )
+ return false;
+
+ bool ret = true;
+
+ for( Toc::iterator it = toc.begin(); it != toc.end(); ++it ) {
+ Track& track = *it;
+ if( track.type() == Track::AUDIO ) {
+ track.m_indices.clear();
+ long index0 = -1;
+ if( searchIndex0( track.firstSector().lba(), track.lastSector().lba(), index0 ) ) {
+ k3bDebug() << "(K3bDevice::Device) found index 0: " << index0 << endl;
+ }
+ if( index0 > 0 )
+ track.m_index0 = K3b::Msf( index0 - track.firstSector().lba() );
+ else
+ track.m_index0 = 0;
+
+ if( index0 > 0 )
+ searchIndexTransitions( track.firstSector().lba(), index0-1, track );
+ else
+ searchIndexTransitions( track.firstSector().lba(), track.lastSector().lba(), track );
+ }
+ }
+
+ if( needToClose )
+ close();
+
+ return ret;
+}
+
+
+void K3bDevice::Device::searchIndexTransitions( long start, long end, K3bDevice::Track& track ) const
+{
+ k3bDebug() << "(K3bDevice::Device) searching for index transitions between "
+ << start << " and " << end << endl;
+ int startIndex = getIndex( start );
+ int endIndex = getIndex( end );
+
+ if( startIndex < 0 || endIndex < 0 ) {
+ k3bDebug() << "(K3bDevice::Device) could not retrieve index values." << endl;
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) indices: " << start << " - " << startIndex
+ << " and " << end << " - " << endIndex << endl;
+
+ if( startIndex != endIndex ) {
+ if( start+1 == end ) {
+ k3bDebug() << "(K3bDevice::Device) found index transition: " << endIndex << " " << end << endl;
+ track.m_indices.resize( endIndex );
+ // we save the index relative to the first sector
+ track.m_indices[endIndex-1] = K3b::Msf( end ) - track.firstSector();
+ }
+ else {
+ searchIndexTransitions( start, start+(end-start)/2, track );
+ searchIndexTransitions( start+(end-start)/2, end, track );
+ }
+ }
+ }
+}
+
+
+int K3bDevice::Device::copyrightProtectionSystemType() const
+{
+ unsigned char* dvdheader = 0;
+ unsigned int dataLen = 0;
+ if( readDvdStructure( &dvdheader, dataLen, 0x1 ) ) {
+ int ret = -1;
+ if( dataLen >= 6 )
+ ret = dvdheader[4];
+ delete [] dvdheader;
+ return ret;
+ }
+ else
+ return -1;
+}
+
+
+bool K3bDevice::Device::getNextWritableAdress( unsigned int& lastSessionStart, unsigned int& nextWritableAdress ) const
+{
+ bool success = false;
+
+ // FIXME: add CD media handling
+ int m = mediaType();
+ if( m & MEDIA_DVD_ALL ) {
+ // DVD+RW always returns complete
+ if( m & (K3bDevice::MEDIA_DVD_PLUS_RW|K3bDevice::MEDIA_DVD_RW_OVWR) )
+ return false;
+
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+
+ if( readDiscInformation( &data, dataLen ) ) {
+ disc_info_t* inf = (disc_info_t*)data;
+
+ //
+ // The state of the last session has to be "empty" (0x0) or "incomplete" (0x1)
+ // The procedure here is taken from the dvd+rw-tools
+ //
+ if( !(inf->border & 0x2) ) {
+ // the incomplete track number is the first track in the last session (the empty session)
+ int nextTrack = inf->first_track_l|inf->first_track_m<<8;
+
+ unsigned char* trackData = 0;
+ unsigned int trackDataLen = 0;
+
+ // Read start address of the incomplete track
+ if( readTrackInformation( &trackData, trackDataLen, 0x1, nextTrack ) ) {
+ nextWritableAdress = from4Byte( &trackData[8] );
+ delete [] trackData;
+
+ // Read start address of the first track in the last session
+ if( readTocPmaAtip( &trackData, trackDataLen, 0x1, false, 0x0 ) ) {
+ lastSessionStart = from4Byte( &trackData[8] );
+ delete [] trackData;
+ success = true;
+ }
+ }
+ }
+ }
+
+ delete [] data;
+ }
+
+ return success;
+}
+
+
+int K3bDevice::Device::nextWritableAddress() const
+{
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+ int nwa = -1;
+
+ if( readDiscInformation( &data, dataLen ) ) {
+ disc_info_t* inf = (disc_info_t*)data;
+
+ //
+ // The state of the last session has to be "empty" (0x0) or "incomplete" (0x1)
+ // The procedure here is taken from the dvd+rw-tools and wodim
+ //
+ if( !(inf->border & 0x2) ) {
+ // the incomplete track number is the first track in the last session (the empty session)
+ int nextTrack = inf->first_track_l|inf->first_track_m<<8;
+
+ unsigned char* trackData = 0;
+ unsigned int trackDataLen = 0;
+
+ // Read start address of the incomplete track
+ if( readTrackInformation( &trackData, trackDataLen, 0x1, nextTrack ) ) {
+ nwa = from4Byte( &trackData[8] );
+ delete [] trackData;
+ }
+
+ // Read start address of the invisible track
+ else if ( readTrackInformation( &trackData, trackDataLen, 0x1, 0xff ) ) {
+ nwa = from4Byte( &trackData[8] );
+ delete [] trackData;
+ }
+ }
+
+ delete [] data;
+ }
+
+ return nwa;
+}
+
+
+QCString K3bDevice::Device::mediaId( int mediaType ) const
+{
+ QCString id;
+
+ if( mediaType & MEDIA_CD_ALL ) {
+ // FIXME:
+ }
+
+ else if( mediaType & MEDIA_DVD_MINUS_ALL ) {
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+ if( readDvdStructure( &data, dataLen, 0x0E ) ) {
+ if( data[4+16] == 3 && data[4+24] == 4 ) {
+ id.sprintf( "%6.6s%-6.6s", data+4+17, data+4+25 );
+ }
+ delete [] data;
+ }
+ }
+
+ else if( mediaType & MEDIA_DVD_PLUS_ALL ) {
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+ if( readDvdStructure( &data, dataLen, 0x11 ) ||
+ readDvdStructure( &data, dataLen, 0x0 ) ) {
+ id.sprintf( "%8.8s/%3.3s", data+23, data+31 );
+ delete [] data;
+ }
+ }
+
+ else if( mediaType & MEDIA_BD_ALL ) {
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+ if( readDiscStructure( &data, dataLen, 1, 0 ) ) {
+ if( data[4+0] == 'D' && data[4+1] == 'I' )
+ id.sprintf ("%6.6s/%-3.3s", data+4+100, data+4+106 );
+ delete [] data;
+ }
+ }
+
+ return id;
+}
+
+
+// int K3bDevice::Device::ioctl( int request, ... ) const
+// {
+// int r = -1;
+// #if defined(Q_OS_LINUX) || defined(Q_OS_NETBSD)
+// d->mutex.lock();
+
+// va_list ap;
+// va_start( ap, request );
+// r = ::ioctl( d->deviceFd, request, ap );
+// va_end( ap );
+
+// d->mutex.unlock();
+// #endif
+// return r;
+// }
+
+
+void K3bDevice::Device::usageLock() const
+{
+ d->mutex.lock();
+}
+
+
+void K3bDevice::Device::usageUnlock() const
+{
+ d->mutex.unlock();
+}
diff --git a/libk3bdevice/k3bdevice.h b/libk3bdevice/k3bdevice.h
new file mode 100644
index 0000000..1dcd863
--- /dev/null
+++ b/libk3bdevice/k3bdevice.h
@@ -0,0 +1,836 @@
+/*
+ *
+ * $Id: k3bdevice.h 679274 2007-06-23 13:23:58Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BDEVICE_H
+#define K3BDEVICE_H
+
+#include <qstringlist.h>
+#include <qvaluelist.h>
+#include <qglobal.h>
+
+#include <k3bdevicetypes.h>
+#include <k3bdiskinfo.h>
+#include <k3bcdtext.h>
+#include <k3bmsf.h>
+#include <k3bdevice_export.h>
+
+#ifdef Q_OS_FREEBSD
+struct cam_device;
+#endif
+
+namespace K3bDevice
+{
+ class Toc;
+
+ /**
+ * \brief The main class representing a device.
+ *
+ * Devices are constructed by the DeviceManager.
+ *
+ * All methods except for open and close in Device are thread-safe which basicly means that
+ * no two commands are sent to the device at the same time.
+ */
+ // FIXME: all methods are const which makes no sense at all!
+ class LIBK3BDEVICE_EXPORT Device
+ {
+ public:
+#ifdef Q_OS_FREEBSD
+ typedef struct cam_device* Handle;
+#else
+ // file descriptor
+ typedef int Handle;
+#endif
+
+ /**
+ * The available cdrdao drivers
+ * \deprecated This will be moved to libk3b
+ */
+ static const char* cdrdao_drivers[];
+
+ // FIXME: make this protected
+ ~Device();
+
+ /**
+ * The interface type.
+ *
+ * \return K3bDevice::SCSI or K3bDevice::IDE.
+ */
+ Interface interfaceType() const;
+
+ /**
+ * \deprecated use readCapabilities() and writeCapabilities()
+ * The device type.
+ *
+ * @return A bitwise or of K3bDevice::DeviceType.
+ */
+ int type() const;
+
+ /**
+ * The mediatypes this device is able to read.
+ *
+ * \return A bitwise or of K3bDevice::MediaType
+ */
+ int readCapabilities() const;
+
+ /**
+ * The media types this device is able to write.
+ *
+ * \return A bitwise or of K3bDevice::MediaType
+ */
+ int writeCapabilities() const;
+
+ /**
+ * \return Vendor string as reported by the device's firmware.
+ */
+ const QString& vendor() const { return m_vendor; }
+
+ /**
+ * \return Description string as reported by the device's firmware.
+ */
+ const QString& description() const { return m_description; }
+
+ /**
+ * \return Version string as reported by the device's firmware.
+ */
+ const QString& version() const { return m_version; }
+
+ /**
+ * Shortcut for \code writesCd() || writesDvd() \endcode
+ *
+ * \return true if the device is able to burn media.
+ */
+ bool burner() const;
+
+ /**
+ * Shortcut for \code type() & DEVICE_CD_R \endcode
+ *
+ * \return true if the device is able to burn CD-R media.
+ */
+ bool writesCd() const;
+
+ /**
+ * Shortcut for \code type() & DEVICE_CD_RW \endcode
+ *
+ * \return true if the device is able to burn CD-RW media.
+ */
+ bool writesCdrw() const;
+
+ /**
+ * Shortcut for \code writesDvdMinus() || writesDvdPlus() \endcode
+ *
+ * \return true if the device is able to burn DVD media.
+ */
+ bool writesDvd() const;
+
+
+ /**
+ * Shortcut for \code type() & (DEVICE_DVD_PLUS_R|DEVICE_DVD_PLUS_RW) \endcode
+ *
+ * \return true if the device is able to burn DVD+R or DVD+RW media.
+ */
+ bool writesDvdPlus() const;
+
+ /**
+ * Shortcut for \code type() & (DEVICE_DVD_R|DEVICE_DVD_RW) \endcode
+ *
+ * \return true if the device is able to burn DVD-R or DVD-RW media.
+ */
+ bool writesDvdMinus() const;
+
+ /**
+ * Shortcut for \code type() & DEVICE_DVD_ROM \endcode
+ *
+ * \return true if the device is able to read DVD media.
+ */
+ bool readsDvd() const;
+
+ /**
+ * @deprecated Use burnfree()
+ */
+ bool burnproof() const;
+
+ /**
+ * @return true is the device is a writer and supports buffer underrun free recording (BURNFREE)
+ */
+ bool burnfree() const;
+
+ /**
+ * Shortcut for \code writingModes() & WRITINGMODE_SAO \endcode
+ *
+ * \deprecated use supportsWritingMode()
+ */
+ bool dao() const;
+
+ /**
+ * Check if the device supports a certain writing mode.
+ *
+ * \return true if the device supports the requested writing mode or false otherwise.
+ */
+ bool supportsWritingMode( WritingMode mode ) const { return (m_writeModes & mode); }
+
+ /**
+ * Shortcut for
+ * \code
+ * writingModes() & (WRITINGMODE_RAW|WRITINGMODE_RAW_R16|WRITINGMODE_RAW_R96P|WRITINGMODE_RAW_R96R)
+ * \endcode
+ */
+ bool supportsRawWriting() const;
+
+ /**
+ * @return true if the device is a DVD-R(W) writer which supports test writing.
+ */
+ bool dvdMinusTestwrite() const { return m_dvdMinusTestwrite; }
+
+ int maxReadSpeed() const { return m_maxReadSpeed; }
+ int currentWriteSpeed() const { return m_currentWriteSpeed; }
+
+ /**
+ * Size of the device's internal writing buffer.
+ *
+ * \return The size of the buffer in KB.
+ */
+ int bufferSize() const { return m_bufferSize; }
+
+ /**
+ * @return the corresponding device name.
+ */
+ const QString& devicename() const;
+
+ /**
+ * for SCSI devices this should be something like /dev/scd0 or /dev/sr0
+ * for IDE device this should be something like /dev/hdb1
+ */
+ const QString& blockDeviceName() const { return m_blockDevice; }
+
+ /**
+ * This is only valid for SCSI devices. Without devfs it's something
+ * like /dev/sg0. Otherwise something like /dev/scsi/host0/bus0/target0/lun0/generic.
+ *
+ * This is not needed in K3b at all. But cdrecord and cdrdao use the sg devices and
+ * we need it to fixup it's permissions in K3bSetup.
+ */
+ const QString& genericDevice() const { return m_genericDevice; }
+
+ /**
+ * \return All device nodes for this drive.
+ */
+ const QStringList& deviceNodes() const;
+
+ /**
+ * \see K3bDevice::Device::deviceNodes()
+ */
+ void addDeviceNode( const QString& );
+
+ /**
+ * Makes only sense to use with scsi devices
+ * @return a string for use with the cdrtools
+ * @deprecated
+ */
+ QString busTargetLun() const;
+
+ int scsiBus() const { return m_bus; }
+ int scsiId() const { return m_target; }
+ int scsiLun() const { return m_lun; }
+
+ int maxWriteSpeed() const { return m_maxWriteSpeed; }
+
+ /**
+ * \deprecated the cdrdao driver has no place in this library. It will be removed.
+ */
+ const QString& cdrdaoDriver() const { return m_cdrdaoDriver; }
+
+ /**
+ * returns: 0 auto (no cdrdao-driver selected)
+ * 1 yes
+ * 2 no
+ *
+ * \deprecated cdrdao specific stuff has no place in this library. It will be removed.
+ */
+ int cdTextCapable() const;
+
+ /**
+ * internal K3b value.
+ * \deprecated This should not be handled here.
+ */
+ void setCurrentWriteSpeed( int s ) { m_currentWriteSpeed = s; }
+
+ /**
+ * Use this if the speed was not detected correctly.
+ */
+ void setMaxReadSpeed( int s ) { m_maxReadSpeed = s; }
+
+ /**
+ * Use this if the speed was not detected correctly.
+ */
+ void setMaxWriteSpeed( int s ) { m_maxWriteSpeed = s; }
+
+ /**
+ * Use this if cdrdao is not able to autodetect the nessessary driver.
+ * \deprecated the cdrdao driver has no place in this library. It will be removed.
+ */
+ void setCdrdaoDriver( const QString& d ) { m_cdrdaoDriver = d; }
+
+ /**
+ * Only used if the cdrdao-driver is NOT set to "auto".
+ * In that case it must be manually set because there
+ * is no way to autosense the cd-text capability.
+ *
+ * \deprecated the cdrdao driver has no place in this library. It will be removed.
+ */
+ void setCdTextCapability( bool );
+
+ /**
+ * checks if unit is ready (medium inserted and ready for command)
+ *
+ * Refers to the MMC command: TEST UNIT READY
+ */
+ bool testUnitReady() const;
+
+ /**
+ * checks if disk is empty, returns @p K3bDevice::State
+ */
+ int isEmpty() const;
+
+ /**
+ * @return true if inserted media is rewritable.
+ */
+ bool rewritable() const;
+
+ /**
+ * Check if the inserted media is a DVD.
+ *
+ * \return true if the inserted media is a DVD.
+ */
+ bool isDVD() const;
+
+ /**
+ * @return The number of sessions on the media.
+ */
+ int numSessions() const;
+
+ /**
+ * @return The toc of the media or an empty (invalid) K3bDevice::Toc if
+ * no or an empty media is inserted.
+ */
+ Toc readToc() const;
+
+ /**
+ * Append ISRC and MCN to the TOC if found
+ * This has been moved to a separate method since it can take a very long time
+ * to scan for all ISRCs.
+ */
+ void readIsrcMcn( Toc& toc ) const;
+
+ /**
+ * Read the CD-TEXT of an audio or mixed-mode CD.
+ *
+ * \return A CdText object filled with the CD-TEXT values or an empty one in case of
+ * pure data media or if the CD does not contain CD-TEXT.
+ */
+ CdText readCdText() const;
+
+ /**
+ * @return The K3bDevice::Track::DataMode of the track.
+ * @see K3bDevice::Track
+ */
+ int getTrackDataMode( const Track& track ) const;
+
+ /**
+ * @return the mode of a data track. K3bDevice::Track::MODE1, K3bDevice::Track::MODE2,
+ * K3bDevice::Track::XA_FORM1, or K3bDevice::Track::XA_FORM2.
+ */
+ int getDataMode( const K3b::Msf& sector ) const;
+
+ /**
+ * block or unblock the drive's tray
+ * \return true on success and false on error.
+ * \see eject()
+ */
+ bool block( bool ) const;
+
+ /**
+ * Eject the media.
+ * \return true on success and false on error.
+ * \see load()
+ */
+ bool eject() const;
+
+ /**
+ * Load the media.
+ * @return true on success and false on error.
+ */
+ bool load() const;
+
+ /**
+ * Enable or disable auto-ejecting. For now this is a no-op on non-Linux systems.
+ * \param enabled if true auto-ejecting will be enabled, otherwise disabled.
+ * \return true if the operation was successful, false otherwise
+ */
+ bool setAutoEjectEnabled( bool enabled ) const;
+
+ /**
+ * The supported writing modes.
+ *
+ * \return A bitwise or of K3bDevice::WritingMode or 0 in case of a read-only device.
+ */
+ int writingModes() const { return m_writeModes; }
+
+ bool readSectorsRaw(unsigned char *buf, int start, int count) const;
+
+ /**
+ * Get a list of supported profiles. See enumeration MediaType.
+ */
+ int supportedProfiles() const;
+
+ /**
+ * Tries to get the current profile from the drive.
+ * @returns -1 on error (command failed or unknown profile)
+ * MediaType otherwise (MEDIA_NONE means: no current profile)
+ */
+ int currentProfile() const;
+
+ /**
+ * Check if a certain feature is current.
+ * \see k3bdevicetypes.h for feature constants.
+ * \return 1 if the feature is current, 0 if not, -1 on error
+ */
+ int featureCurrent( unsigned int feature ) const;
+
+ /**
+ * This is the method to use!
+ */
+ DiskInfo diskInfo() const;
+
+ /**
+ * Refers to MMC command READ CAPACITY
+ */
+ bool readCapacity( K3b::Msf& ) const;
+
+ /**
+ * Refers to MMC command READ FORMAT CAPACITY
+ *
+ * @param wantedFormat The requested format type.
+ * @param result If true is returned this contains the requested value.
+ * @param currentMax If not 0 this will be filled with the Current/Maximum Descriptor value.
+ * @param currentMax If not 0 this will be filled with the Current/Maximum Format Type.
+ */
+ bool readFormatCapacity( int wantedFormat, K3b::Msf& result,
+ K3b::Msf* currentMax = 0, int* currentMaxFormat = 0 ) const;
+
+ /**
+ * Determine the type of the currently mounted medium
+ *
+ * @returns K3bDevice::MediaType
+ */
+ int mediaType() const;
+
+ /**
+ * Returnes the list of supported writing speeds as reported by
+ * mode page 2Ah.
+ *
+ * This only works with MMC3 compliant drives.
+ */
+ QValueList<int> determineSupportedWriteSpeeds() const;
+
+ /**
+ * @returnes the speed in kb/s or 0 on failure.
+ */
+ int determineMaximalWriteSpeed() const;
+
+ /**
+ * Open the device for access via a file descriptor.
+ * @return true on success or if the device is already open.
+ * @see close()
+ *
+ * Be aware that this method is not thread-safe.
+ */
+ bool open( bool write = false ) const;
+
+ /**
+ * Close the files descriptor.
+ * @see open()
+ *
+ * Be aware that this method is not thread-safe.
+ */
+ void close() const;
+
+ /**
+ * @return true if the device was successfully opened via @p open()
+ */
+ bool isOpen() const;
+
+ /**
+ * fd on linux, cam on bsd
+ */
+ Handle handle() const;
+
+ /**
+ * \return \li -1 on error (no DVD)
+ * \li 1 (CSS/CPPM)
+ * \li 2 (CPRM) if scrambled
+ * \li 0 otherwise
+ */
+ int copyrightProtectionSystemType() const;
+
+ // MMC commands
+
+ /**
+ * SET SPEED command
+ *
+ * @param readingSpeed The preferred reading speed (0x0000-0xFFFE). 0xFFFF requests
+ * fot the logical unit to select the optimal speed.
+ * @param writingSpeed The preferred writing speed (0x0000-0xFFFE). 0xFFFF requests
+ * fot the logical unit to select the optimal speed.
+ * @param cav Is the speed pure CAV?
+ */
+ bool setSpeed( unsigned int readingSpeed,
+ unsigned int writingSpeed,
+ bool cav = false ) const;
+
+ /**
+ * if true is returned dataLen specifies the actual length of *data which needs to be
+ * deleted after using.
+ */
+ bool readDiscInformation( unsigned char** data, unsigned int& dataLen ) const;
+
+ /**
+ * @param pf If false all fields in the descriptor data is vendor specific. Default should be true.
+ */
+ bool modeSelect( unsigned char* page, unsigned int pageLen, bool pf, bool sp ) const;
+
+ /**
+ * if true is returned pageLen specifies the actual length of *pageData which needs to be
+ * deleted after using.
+ */
+ bool modeSense( unsigned char** pageData, unsigned int& pageLen, int page ) const;
+
+ /**
+ * if true is returned dataLen specifies the actual length of *data which needs to be
+ * deleted after using.
+ */
+ bool readTocPmaAtip( unsigned char** data, unsigned int& dataLen, int format, bool msf, int track ) const;
+
+ /**
+ * @param type specifies what value means:
+ * \li 00b - value refers to a logical block address
+ * \li 01b - value refers to a track number where 0 will treat the lead-in as if it
+ * were a logical track and ffh will read the invisible or incomplete track.
+ * \li 10b - value refers to a session number
+ *
+ */
+ bool readTrackInformation( unsigned char** data, unsigned int& dataLen, int type, int value ) const;
+
+ /**
+ * if true is returned dataLen specifies the actual length of *data which needs to be
+ * deleted after using.
+ */
+ bool readDiscStructure( unsigned char** data, unsigned int& dataLen,
+ unsigned int mediaType = 0x0,
+ unsigned int format = 0x0,
+ unsigned int layer = 0x0,
+ unsigned long adress = 0,
+ unsigned int agid = 0x0 ) const;
+
+ /**
+ * In MMC5 readDvdStructure was renamed to readDiscStructure. This method does the same
+ * like the above.
+ */
+ bool readDvdStructure( unsigned char** data, unsigned int& dataLen,
+ unsigned int format = 0x0,
+ unsigned int layer = 0x0,
+ unsigned long adress = 0,
+ unsigned int agid = 0x0 ) const;
+
+ /**
+ * if true is returned dataLen specifies the actual length of *data which needs to be
+ * deleted after using.
+ */
+ bool mechanismStatus( unsigned char** data, unsigned int& dataLen ) const;
+
+ /**
+ * Read a single feature.
+ * data will be filled with the feature header and the descriptor
+ */
+ bool getFeature( unsigned char** data, unsigned int& dataLen, unsigned int feature ) const;
+
+
+ /**
+ * if true is returned dataLen specifies the actual length of *data which needs to be
+ * deleted after using.
+ */
+ bool getPerformance( unsigned char** data, unsigned int& dataLen,
+ unsigned int type,
+ unsigned int dataType,
+ unsigned int lba = 0 ) const;
+
+ /**
+ * @param sectorType: \li 000b - all types
+ * \li 001b - CD-DA
+ * \li 010b - Mode 1
+ * \li 011b - Mode 2 formless
+ * \li 100b - Mode 2 form 1
+ * \li 101b - Mode 2 form 2
+ *
+ * @param startAdress Lba 0 is mapped to msf 00:00:00 so this method uses
+ * startAdress+150 as the starting msf.
+ *
+ * @param endAdress This is the ending address which is NOT included in the read operation.
+ * Lba 0 is mapped to msf 00:00:00 so this method uses
+ * endAdress+150 as the ending msf.
+ *
+ * @param c2: \li 00b - No error info
+ * \li 01b - 294 bytes, one bit for every byte of the 2352 bytes
+ * \li 10b - 296 bytes, xor of all c2 bits, zero pad bit, 294 c2 bits
+ *
+ * @param subChannel: \li 000b - No Sub-channel data
+ * \li 001b - RAW P-W Sub-channel (96 bytes)
+ * \li 010b - Formatted Q Sub-channel (16 bytes)
+ * \li 100b - Corrected and de-interleaved R-W Sub-channel (96 bytes)
+ */
+ bool readCdMsf( unsigned char* data,
+ unsigned int dataLen,
+ int sectorType,
+ bool dap,
+ const K3b::Msf& startAdress,
+ const K3b::Msf& endAdress,
+ bool sync,
+ bool header,
+ bool subHeader,
+ bool userData,
+ bool edcEcc,
+ int c2,
+ int subChannel ) const;
+
+ /**
+ * @param sectorType: \li 000b - all types
+ * \li 001b - CD-DA
+ * \li 010b - Mode 1
+ * \li 011b - Mode 2 formless
+ * \li 100b - Mode 2 form 1
+ * \li 101b - Mode 2 form 2
+ *
+ * @param c2: \li 00b - No error info
+ * \li 01b - 294 bytes, one bit for every byte of the 2352 bytes
+ * \li 10b - 296 bytes, xor of all c2 bits, zero pad bit, 294 c2 bits
+ *
+ * @param subChannel: \li 000b - No Sub-channel data
+ * \li 001b - RAW P-W Sub-channel (96 bytes)
+ * \li 010b - Formatted Q Sub-channel (16 bytes)
+ * \li 100b - Corrected and de-interleaved R-W Sub-channel (96 bytes)
+ */
+ bool readCd( unsigned char* data,
+ unsigned int dataLen,
+ int sectorType,
+ bool dap,
+ unsigned long startAdress,
+ unsigned long length,
+ bool sync,
+ bool header,
+ bool subHeader,
+ bool userData,
+ bool edcEcc,
+ int c2,
+ int subChannel ) const;
+
+ bool read10( unsigned char* data,
+ unsigned int dataLen,
+ unsigned long startAdress,
+ unsigned int length,
+ bool fua = false ) const;
+
+ bool read12( unsigned char* data,
+ unsigned int dataLen,
+ unsigned long startAdress,
+ unsigned long length,
+ bool streaming = false,
+ bool fua = false ) const;
+
+ /**
+ * @param subchannelParam: 01h - CD current position
+ * 02h - Media Catalog number (UPC/bar code)
+ * 03h - ISRC
+ * @param trackNumber only valid if subchannelParam == 03h
+ */
+ bool readSubChannel( unsigned char** data,
+ unsigned int& dataLen,
+ unsigned int subchannelParam,
+ unsigned int trackNumber ) const;
+
+ bool readIsrc( unsigned int track, QCString& isrc ) const;
+
+ bool readMcn( QCString& mcn ) const;
+
+ /**
+ * MMC command Read Buffer Capacity
+ *
+ * \return \see K3bScsiCommand::transport()
+ */
+ int readBufferCapacity( long long& bufferLength, long long& bufferAvail ) const;
+
+ /**
+ * @returns the index number on success
+ * -1 on general error
+ * and -2 if there is no index info in that frame
+ */
+ int getIndex( unsigned long lba ) const;
+
+ bool searchIndex0( unsigned long startSec, unsigned long endSec, long& pregapStart ) const;
+
+ /**
+ * For now this just searches index 0 for all tracks and sets
+ * the value in the tracks.
+ * In the future this should scan for all indices.
+ */
+ bool indexScan( K3bDevice::Toc& toc ) const;
+
+ /**
+ * Seek to the specified sector.
+ */
+ bool seek( unsigned long lba ) const;
+
+ bool getNextWritableAdress( unsigned int& lastSessionStart, unsigned int& nextWritableAdress ) const;
+
+ /**
+ * Retrieve the next writable address from the currently mounted writable medium.
+ * \return The next writable address if the medium is empty or appendable or -1
+ * if an error occured.
+ */
+ int nextWritableAddress() const;
+
+ /**
+ * Locks the device for usage. This means that no MMC command can be performed
+ * until usageUnlock is called.
+ *
+ * Locking a device is useful when an external application or library is called
+ * that opens the device itself.
+ *
+ * \sa usageUnlock
+ */
+ void usageLock() const;
+
+ /**
+ * Unlock the device after a call to usageLock.
+ */
+ void usageUnlock() const;
+
+ /**
+ * Thread-safe ioctl call for this device for Linux and Net-BSD systems.
+ * Be aware that so far this does not include opening the device
+ */
+// int ioctl( int request, ... ) const;
+
+ protected:
+ bool furtherInit();
+
+#ifdef Q_OS_LINUX
+ /**
+ * Fallback method that uses the evil cdrom.h stuff
+ */
+ bool readTocLinux( Toc& ) const;
+#endif
+
+ /**
+ * The preferred toc reading method for all CDs. Also reads session info.
+ * undefined for DVDs.
+ */
+ bool readRawToc( Toc& ) const;
+ bool readFormattedToc( Toc&, int mediaType ) const;
+
+ /**
+ * Fixes the last block on CD-Extra disks. This is needed if the readRawToc failed since
+ * in that case the first sector of the last session's first track is used as the previous
+ * session's last track's last sector which is wrong. There is a 11400 block session lead-in
+ * between them. This method fixes this only for the last session and only on linux.
+ */
+ bool fixupToc( Toc& ) const;
+
+ private:
+ /**
+ * A Device can only be constructed the the DeviceManager.
+ */
+ Device( const QString& devname );
+
+ /**
+ * Determines the device's capabilities. This needs to be called once before
+ * using the device.
+ *
+ * Should only be used by the DeviceManager.
+ *
+ * @param checkWritingModes if true the CD writing modes will be checked using
+ * MMC_MODE_SELECT.
+ */
+ bool init( bool checkWritingModes = true );
+
+ void searchIndexTransitions( long start, long end, K3bDevice::Track& track ) const;
+ void checkWritingModes();
+ void checkFeatures();
+ void checkForJustLink();
+ void checkFor2AFeatures();
+ void checkForAncientWriters();
+
+ /**
+ * Internal method which checks if the raw toc data has bcd values or hex.
+ * @return 0 if hex, 1 if bcd, -1 if none
+ */
+ int rawTocDataWithBcdValues( unsigned char* data, unsigned int dataLen ) const;
+
+ bool getSupportedWriteSpeedsVia2A( QValueList<int>& list, bool dvd ) const;
+ bool getSupportedWriteSpeedsViaGP( QValueList<int>& list, bool dvd ) const;
+
+ QCString mediaId( int mediaType ) const;
+
+ QString m_vendor;
+ QString m_description;
+ QString m_version;
+ QString m_cdrdaoDriver;
+ int m_cdTextCapable;
+ int m_maxReadSpeed;
+ int m_maxWriteSpeed;
+ int m_currentWriteSpeed;
+
+ bool m_dvdMinusTestwrite;
+
+ // only needed for scsi devices
+ int m_bus;
+ int m_target;
+ int m_lun;
+
+ int m_bufferSize;
+
+ int m_writeModes;
+
+ // only needed on FreeBSD
+ QString m_passDevice;
+ QString m_blockDevice;
+ QString m_genericDevice;
+
+ class Private;
+ Private* d;
+ friend class DeviceManager;
+ };
+
+#if defined(Q_OS_LINUX) || defined(Q_OS_NETBSD)
+ /**
+ * This should always be used to open a device since it
+ * uses the resmgr
+ *
+ * @internal
+ */
+ int openDevice( const char* name, bool write = false );
+#endif
+}
+
+#endif
diff --git a/libk3bdevice/k3bdevice_export.h b/libk3bdevice/k3bdevice_export.h
new file mode 100644
index 0000000..6c43716
--- /dev/null
+++ b/libk3bdevice/k3bdevice_export.h
@@ -0,0 +1,33 @@
+/*
+ *
+ * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $
+ * Copyright (c) 2005 Laurent Montel <montel@kde.org>
+ * Copyright (C) 2005-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3BDEVICE_EXPORT_H_
+#define _K3BDEVICE_EXPORT_H_
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef __KDE_HAVE_GCC_VISIBILITY
+#define LIBK3BDEVICE_NO_EXPORT __attribute__ ((visibility("hidden")))
+#define LIBK3BDEVICE_EXPORT __attribute__ ((visibility("default")))
+#else
+#define LIBK3BDEVICE_NO_EXPORT
+#define LIBK3BDEVICE_EXPORT
+#endif
+
+#endif
+
diff --git a/libk3bdevice/k3bdevice_mmc.cpp b/libk3bdevice/k3bdevice_mmc.cpp
new file mode 100644
index 0000000..77db530
--- /dev/null
+++ b/libk3bdevice/k3bdevice_mmc.cpp
@@ -0,0 +1,947 @@
+/*
+ *
+ * $Id: k3bdevice_mmc.cpp 690628 2007-07-21 16:05:08Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+/**
+This file contains all the MMC command implementations of the K3b device class
+to make the code more readable.
+**/
+
+
+#include "k3bdevice.h"
+#include "k3bscsicommand.h"
+#include "k3bdeviceglobals.h"
+#include "k3bdebug.h"
+
+#include <string.h>
+
+
+bool K3bDevice::Device::testUnitReady() const
+{
+ ScsiCommand cmd( this );
+ cmd.enableErrorMessages( false );
+ cmd[0] = MMC_TEST_UNIT_READY;
+ cmd[5] = 0; // Necessary to set the proper command length
+ return( cmd.transport() == 0 );
+}
+
+
+bool K3bDevice::Device::getFeature( unsigned char** data, unsigned int& dataLen, unsigned int feature ) const
+{
+ unsigned char header[2048];
+ ::memset( header, 0, 2048 );
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_GET_CONFIGURATION;
+ cmd[1] = 2; // read only specified feature
+ cmd[2] = feature>>8;
+ cmd[3] = feature;
+ cmd[8] = 8; // we only read the data length first
+ cmd[9] = 0; // Necessary to set the proper command length
+
+ // we only read the data length first
+ dataLen = 8;
+ if( cmd.transport( TR_DIR_READ, header, 8 ) )
+ dataLen = from4Byte( header ) + 4;
+ else
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": GET CONFIGURATION length det failed." << endl;
+
+ //
+ // Some buggy firmwares do not return the size of the available data
+ // but the returned data or something invalid altogether.
+ // So we simply use the maximum possible value to be on the safe side
+ // with these buggy drives.
+ // We cannot use this as default since many firmwares fail with a too high data length.
+ //
+ if( (dataLen-8) % 8 || dataLen <= 8 )
+ dataLen = 0xFFFF;
+
+ // again with real length
+ *data = new unsigned char[dataLen];
+ ::memset( *data, 0, dataLen );
+
+ cmd[7] = dataLen>>8;
+ cmd[8] = dataLen;
+ if( cmd.transport( TR_DIR_READ, *data, dataLen ) == 0 ) {
+ dataLen = QMIN( dataLen, from4Byte( *data ) + 4 );
+ return true;
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": GET CONFIGURATION with real length "
+ << dataLen << " failed." << endl;
+ delete [] *data;
+ }
+
+ return false;
+}
+
+
+int K3bDevice::Device::featureCurrent( unsigned int feature ) const
+{
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+ if( getFeature( &data, dataLen, feature ) ) {
+ int ret = -1;
+ if( dataLen >= 11 )
+ ret = ( data[8+2]&1 ? 1 : 0 ); // check the current flag
+
+ delete [] data;
+
+ return ret;
+ }
+ else
+ return -1;
+}
+
+
+bool K3bDevice::Device::readIsrc( unsigned int track, QCString& isrc ) const
+{
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+
+ if( readSubChannel( &data, dataLen, 0x3, track ) ) {
+ bool isrcValid = false;
+
+ if( dataLen >= 8+18 ) {
+ isrcValid = (data[8+4]>>7 & 0x1);
+
+ if( isrcValid ) {
+ isrc = QCString( reinterpret_cast<char*>(data[8+5]), 13 );
+
+ // TODO: check the range of the chars
+
+ }
+ }
+
+ delete [] data;
+
+ return isrcValid;
+ }
+ else
+ return false;
+}
+
+
+bool K3bDevice::Device::readMcn( QCString& mcn ) const
+{
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+
+ if( readSubChannel( &data, dataLen, 0x2, 0 ) ) {
+ bool mcnValid = false;
+
+ if( dataLen >= 8+18 ) {
+ mcnValid = (data[8+4]>>7 & 0x1);
+
+ if( mcnValid )
+ mcn = QCString( reinterpret_cast<char*>(data[8+5]), 14 );
+ }
+
+ delete [] data;
+
+ return mcnValid;
+ }
+ else
+ return false;
+}
+
+
+bool K3bDevice::Device::getPerformance( unsigned char** data, unsigned int& dataLen,
+ unsigned int type,
+ unsigned int dataType,
+ unsigned int lba ) const
+{
+ unsigned int descLen = 0;
+ switch( type ) {
+ case 0x0:
+ descLen = 16;
+ break;
+ case 0x1:
+ descLen = 8;
+ break;
+ case 0x2:
+ descLen = 2048;
+ break;
+ case 0x3:
+ descLen = 16;
+ break;
+ case 0x4:
+ descLen = 8;
+ break;
+ case 0x5:
+ descLen = 8; // FIXME: ??
+ break;
+ }
+
+ unsigned char header[8];
+ ::memset( header, 0, 8 );
+ dataLen = 8;
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_GET_PERFORMANCE;
+ cmd[1] = dataType;
+ cmd[2] = lba >> 24;
+ cmd[3] = lba >> 16;
+ cmd[4] = lba >> 8;
+ cmd[5] = lba;
+ cmd[9] = 1; // first we read one descriptor
+ cmd[10] = type;
+ cmd[11] = 0; // Necessary to set the proper command length
+ if( cmd.transport( TR_DIR_READ, header, 8 ) ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << ": GET PERFORMANCE length det failed." << endl;
+ return false;
+ }
+
+ dataLen = from4Byte( header ) + 4;
+
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << ": GET PERFORMANCE dataLen = " << dataLen << endl;
+
+ if( (dataLen-8) % descLen ||
+ dataLen <= 8 ||
+ dataLen > 2048 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << ": GET PERFORMANCE reports bogus dataLen: " << dataLen << endl;
+ return false;
+ }
+
+ *data = new unsigned char[dataLen];
+ ::memset( *data, 0, dataLen );
+
+ unsigned int numDesc = (dataLen-8)/descLen;
+
+ cmd[8] = numDesc>>8;
+ cmd[9] = numDesc;
+ if( cmd.transport( TR_DIR_READ, *data, dataLen ) == 0 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << ": GET PERFORMANCE successful with reported length: " << from4Byte( *data ) << endl;
+ dataLen = QMIN( dataLen, from4Byte( *data ) + 4 );
+ return true;
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << ": GET PERFORMANCE with real length "
+ << dataLen << " failed." << endl;
+ delete [] *data;
+ return false;
+ }
+}
+
+
+bool K3bDevice::Device::setSpeed( unsigned int readingSpeed,
+ unsigned int writingSpeed,
+ bool cav ) const
+{
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_SET_SPEED;
+ cmd[1] = ( cav ? 0x1 : 0x0 );
+ cmd[2] = readingSpeed >> 8;
+ cmd[3] = readingSpeed;
+ cmd[4] = writingSpeed >> 8;
+ cmd[5] = writingSpeed;
+ cmd[11] = 0; // Necessary to set the proper command length
+ return ( cmd.transport( TR_DIR_WRITE ) == 0 );
+}
+
+
+bool K3bDevice::Device::seek( unsigned long lba ) const
+{
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_SEEK_10;
+ cmd[2] = lba>>24;
+ cmd[3] = lba>>16;
+ cmd[4] = lba>>8;
+ cmd[5] = lba;
+ cmd[9] = 0; // Necessary to set the proper command length
+ return !cmd.transport();
+}
+
+
+bool K3bDevice::Device::readTrackInformation( unsigned char** data, unsigned int& dataLen, int type, int value ) const
+{
+ unsigned char header[2048];
+ ::memset( header, 0, 2048 );
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_READ_TRACK_INFORMATION;
+ cmd[9] = 0; // Necessary to set the proper command length
+
+ switch( type ) {
+ case 0:
+ case 1:
+ case 2:
+ cmd[1] = type & 0x3;
+ cmd[2] = value>>24;
+ cmd[3] = value>>16;
+ cmd[4] = value>>8;
+ cmd[5] = value;
+ break;
+ default:
+ k3bDebug() << "(K3bDevice::readTrackInformation) wrong type parameter: " << type << endl;
+ return false;
+ }
+
+ // first we read the header
+ dataLen = 4;
+ cmd[8] = 4;
+ if( cmd.transport( TR_DIR_READ, header, 4 ) == 0 )
+ dataLen = from2Byte( header ) + 2;
+ else
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": READ TRACK INFORMATION length det failed." << endl;
+
+ //
+ // Some buggy firmwares do not return the size of the available data
+ // but the returned data.
+ // So we try to determine the correct size based on the medium type
+ // DVD+R: 40 (MMC4)
+ // DVD-DL: 48 (MMC5)
+ // CD: 36 (MMC2)
+ //
+ if( dataLen <= 4 ) {
+ int m = mediaType();
+ if( m & (MEDIA_DVD_R_DL|MEDIA_DVD_R_DL_SEQ|MEDIA_DVD_R_DL_JUMP) )
+ dataLen = 48;
+ else if( m & (MEDIA_DVD_PLUS_R|MEDIA_DVD_PLUS_R_DL) )
+ dataLen = 40;
+ else
+ dataLen = 36;
+ }
+
+ // again with real length
+ *data = new unsigned char[dataLen];
+ ::memset( *data, 0, dataLen );
+
+ cmd[7] = dataLen>>8;
+ cmd[8] = dataLen;
+ if( cmd.transport( TR_DIR_READ, *data, dataLen ) == 0 ) {
+ dataLen = QMIN( dataLen, from2Byte( *data ) + 2u );
+ return true;
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": READ TRACK INFORMATION with real length "
+ << dataLen << " failed." << endl;
+ delete [] *data;
+ }
+
+ return false;
+}
+
+
+
+bool K3bDevice::Device::read10( unsigned char* data,
+ unsigned int dataLen,
+ unsigned long startAdress,
+ unsigned int length,
+ bool fua ) const
+{
+ ::memset( data, 0, dataLen );
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_READ_10;
+ cmd[1] = ( fua ? 0x8 : 0x0 );
+ cmd[2] = startAdress>>24;
+ cmd[3] = startAdress>>16;
+ cmd[4] = startAdress>>8;
+ cmd[5] = startAdress;
+ cmd[7] = length>>8;
+ cmd[8] = length;
+ cmd[9] = 0; // Necessary to set the proper command length
+
+ if( cmd.transport( TR_DIR_READ, data, dataLen ) ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": READ 10 failed!" << endl;
+ return false;
+ }
+ else
+ return true;
+}
+
+
+bool K3bDevice::Device::read12( unsigned char* data,
+ unsigned int dataLen,
+ unsigned long startAdress,
+ unsigned long length,
+ bool streaming,
+ bool fua ) const
+{
+ ::memset( data, 0, dataLen );
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_READ_12;
+ cmd[1] = ( fua ? 0x8 : 0x0 );
+ cmd[2] = startAdress>>24;
+ cmd[3] = startAdress>>16;
+ cmd[4] = startAdress>>8;
+ cmd[5] = startAdress;
+ cmd[6] = length>>24;
+ cmd[7] = length>>16;
+ cmd[8] = length>>8;
+ cmd[9] = length;
+ cmd[10] = (streaming ? 0x80 : 0 );
+ cmd[11] = 0; // Necessary to set the proper command length
+
+ if( cmd.transport( TR_DIR_READ, data, dataLen ) ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": READ 12 failed!" << endl;
+ return false;
+ }
+ else
+ return true;
+}
+
+
+bool K3bDevice::Device::readCd( unsigned char* data,
+ unsigned int dataLen,
+ int sectorType,
+ bool dap,
+ unsigned long startAdress,
+ unsigned long length,
+ bool sync,
+ bool header,
+ bool subHeader,
+ bool userData,
+ bool edcEcc,
+ int c2,
+ int subChannel ) const
+{
+ ::memset( data, 0, dataLen );
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_READ_CD;
+ cmd[1] = (sectorType<<2 & 0x1c) | ( dap ? 0x2 : 0x0 );
+ cmd[2] = startAdress>>24;
+ cmd[3] = startAdress>>16;
+ cmd[4] = startAdress>>8;
+ cmd[5] = startAdress;
+ cmd[6] = length>>16;
+ cmd[7] = length>>8;
+ cmd[8] = length;
+ cmd[9] = ( ( sync ? 0x80 : 0x0 ) |
+ ( subHeader ? 0x40 : 0x0 ) |
+ ( header ? 0x20 : 0x0 ) |
+ ( userData ? 0x10 : 0x0 ) |
+ ( edcEcc ? 0x8 : 0x0 ) |
+ ( c2<<1 & 0x6 ) );
+ cmd[10] = subChannel & 0x7;
+ cmd[11] = 0; // Necessary to set the proper command length
+
+ if( cmd.transport( TR_DIR_READ, data, dataLen ) ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": READ CD failed!" << endl;
+ return false;
+ }
+ else {
+ return true;
+ }
+}
+
+
+bool K3bDevice::Device::readCdMsf( unsigned char* data,
+ unsigned int dataLen,
+ int sectorType,
+ bool dap,
+ const K3b::Msf& startAdress,
+ const K3b::Msf& endAdress,
+ bool sync,
+ bool header,
+ bool subHeader,
+ bool userData,
+ bool edcEcc,
+ int c2,
+ int subChannel ) const
+{
+ ::memset( data, 0, dataLen );
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_READ_CD_MSF;
+ cmd[1] = (sectorType<<2 & 0x1c) | ( dap ? 0x2 : 0x0 );
+ cmd[3] = (startAdress+150).minutes();
+ cmd[4] = (startAdress+150).seconds();
+ cmd[5] = (startAdress+150).frames();
+ cmd[6] = (endAdress+150).minutes();
+ cmd[7] = (endAdress+150).seconds();
+ cmd[8] = (endAdress+150).frames();
+ cmd[9] = ( ( sync ? 0x80 : 0x0 ) |
+ ( subHeader ? 0x40 : 0x0 ) |
+ ( header ? 0x20 : 0x0 ) |
+ ( userData ? 0x10 : 0x0 ) |
+ ( edcEcc ? 0x8 : 0x0 ) |
+ ( c2<<1 & 0x6 ) );
+ cmd[10] = subChannel & 0x7;
+ cmd[11] = 0; // Necessary to set the proper command length
+
+ if( cmd.transport( TR_DIR_READ, data, dataLen ) ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": READ CD MSF failed!" << endl;
+ return false;
+ }
+ else
+ return true;
+}
+
+
+bool K3bDevice::Device::readSubChannel( unsigned char** data, unsigned int& dataLen,
+ unsigned int subchannelParam,
+ unsigned int trackNumber ) const
+{
+ unsigned char header[2048];
+ ::memset( header, 0, 2048 );
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_READ_SUB_CHANNEL;
+ cmd[2] = 0x40; // SUBQ
+ cmd[3] = subchannelParam;
+ cmd[6] = trackNumber; // only used when subchannelParam == 03h (ISRC)
+ cmd[8] = 4;
+ cmd[9] = 0; // Necessary to set the proper command length
+
+ // first we read the header
+ dataLen = 4;
+ if( cmd.transport( TR_DIR_READ, header, 4 ) == 0 )
+ dataLen = from2Byte( &header[2] ) + 4;
+ else
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": READ SUB-CHANNEL length det failed." << endl;
+
+ //
+ // Some buggy firmwares do not return the size of the available data
+ // but the returned data. So we simply use the maximum possible value to be on the safe side
+ // with these buggy drives.
+ // We cannot use this as default since many firmwares fail with a too high data length.
+ //
+ if( dataLen <= 4 )
+ dataLen = 0xFFFF;
+
+ // again with real length
+ *data = new unsigned char[dataLen];
+ ::memset( *data, 0, dataLen );
+
+ cmd[7] = dataLen>>8;
+ cmd[8] = dataLen;
+ if( cmd.transport( TR_DIR_READ, *data, dataLen ) == 0 ) {
+ dataLen = QMIN( dataLen, from2Byte( (*data)+2 ) + 4u );
+ return true;
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": READ SUB-CHANNEL with real length "
+ << dataLen << " failed." << endl;
+ delete [] *data;
+ }
+
+ return false;
+}
+
+
+bool K3bDevice::Device::readTocPmaAtip( unsigned char** data, unsigned int& dataLen, int format, bool time, int track ) const
+{
+ unsigned int descLen = 0;
+
+ switch( format ) {
+ case 0x0:
+ descLen = 8;
+ break;
+ case 0x1:
+ descLen = 8;
+ break;
+ case 0x2:
+ descLen = 11;
+ break;
+ case 0x3:
+ descLen = 11;
+ break;
+ case 0x4:
+ descLen = 4; // MMC2: 24 and MMC4: 28, so we use the highest common factor
+ break;
+ case 0x5:
+ descLen = 18;
+ break;
+ }
+
+ unsigned char header[2048];
+ ::memset( header, 0, 2048 );
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_READ_TOC_PMA_ATIP;
+ cmd[1] = ( time ? 0x2 : 0x0 );
+ cmd[2] = format & 0x0F;
+ cmd[6] = track;
+ cmd[8] = 4;
+ cmd[9] = 0; // Necessary to set the proper command length
+
+ // we only read the header
+ dataLen = 4;
+ if( cmd.transport( TR_DIR_READ, header, 4 ) == 0 )
+ dataLen = from2Byte( header ) + 2;
+ else
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": READ TOC/PMA/ATIP length det failed." << endl;
+
+ //
+ // Some buggy firmwares return an invalid size here
+ // So we simply use the maximum possible value to be on the safe side
+ // with these buggy drives.
+ // We cannot use this as default since many firmwares fail with a too high data length.
+ //
+ if( (dataLen-4) % descLen || dataLen < 4+descLen ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": READ TOC/PMA/ATIP invalid length returned: " << dataLen << endl;
+ dataLen = 0xFFFF;
+ }
+
+ //
+ // Not all drives like uneven numbers
+ //
+ if( dataLen%2 )
+ ++dataLen;
+
+ // again with real length
+ *data = new unsigned char[dataLen];
+ ::memset( *data, 0, dataLen );
+
+ cmd[7] = dataLen>>8;
+ cmd[8] = dataLen;
+ if( cmd.transport( TR_DIR_READ, *data, dataLen ) == 0 ) {
+ dataLen = QMIN( dataLen, from2Byte( *data ) + 2u );
+ if( (dataLen-4) % descLen || dataLen < 4+descLen ) {
+ // useless length
+ delete [] *data;
+ return false;
+ }
+ else
+ return true;
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": READ TOC/PMA/ATIP format "
+ << format << " with real length "
+ << dataLen << " failed." << endl;
+ delete [] *data;
+ }
+
+ return false;
+}
+
+
+bool K3bDevice::Device::mechanismStatus( unsigned char** data, unsigned int& dataLen ) const
+{
+ unsigned char header[2048];
+ ::memset( header, 0, 2048 );
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_MECHANISM_STATUS;
+ cmd[9] = 8;
+ cmd[11] = 0; // Necessary to set the proper command length
+
+ // first we read the header
+ dataLen = 8;
+ if( cmd.transport( TR_DIR_READ, header, 8 ) == 0 )
+ dataLen = from4Byte( &header[6] ) + 8;
+ else
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": MECHANISM STATUS length det failed." << endl;
+
+ //
+ // Some buggy firmwares do not return the size of the available data
+ // but the returned data or something invalid altogether.
+ // So we simply use the maximum possible value to be on the safe side
+ // with these buggy drives.
+ // We cannot use this as default since many firmwares fail with a too high data length.
+ //
+ if( (dataLen-8) % 4 || dataLen <= 8 )
+ dataLen = 0xFFFF;
+
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": MECHANISM STATUS "
+ << (int)header[5] << " slots." << endl;
+
+ // again with real length
+ *data = new unsigned char[dataLen];
+ ::memset( *data, 0, dataLen );
+
+ cmd[8] = dataLen>>8;
+ cmd[9] = dataLen;
+ if( cmd.transport( TR_DIR_READ, *data, dataLen ) == 0 ) {
+ dataLen = QMIN( dataLen, from4Byte( (*data)+6 ) + 8 );
+ return true;
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": MECHANISM STATUS with real length "
+ << dataLen << " failed." << endl;
+ delete [] *data;
+ }
+
+ return false;
+}
+
+
+
+bool K3bDevice::Device::modeSense( unsigned char** pageData, unsigned int& pageLen, int page ) const
+{
+ unsigned char header[2048];
+ ::memset( header, 0, 2048 );
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_MODE_SENSE;
+ cmd[1] = 0x8; // Disable Block Descriptors
+ cmd[2] = page & 0x3F;
+ cmd[8] = 8;
+ cmd[9] = 0; // Necessary to set the proper command length
+
+ // first we determine the data length
+ pageLen = 8;
+ if( cmd.transport( TR_DIR_READ, header, 8 ) == 0 )
+ pageLen = from2Byte( header ) + 2;
+ else
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": MODE SENSE length det failed." << endl;
+
+ //
+ // Some buggy firmwares do not return the size of the available data
+ // but the returned data. So we simply use the maximum possible value to be on the safe side
+ // with these buggy drives.
+ // We cannot use this as default since many firmwares fail with a too high data length.
+ //
+ if( pageLen == 8 )
+ pageLen = 0xFFFF;
+
+ // again with real length
+ *pageData = new unsigned char[pageLen];
+ ::memset( *pageData, 0, pageLen );
+
+ cmd[7] = pageLen>>8;
+ cmd[8] = pageLen;
+ if( cmd.transport( TR_DIR_READ, *pageData, pageLen ) == 0 ) {
+ pageLen = QMIN( pageLen, from2Byte( *pageData ) + 2u );
+ return true;
+ }
+ else {
+ delete [] *pageData;
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": MODE SENSE with real length "
+ << pageLen << " failed." << endl;
+ }
+
+ return false;
+}
+
+
+bool K3bDevice::Device::modeSelect( unsigned char* page, unsigned int pageLen, bool pf, bool sp ) const
+{
+ page[0] = 0;
+ page[1] = 0;
+ page[4] = 0;
+ page[5] = 0;
+
+ // we do not support Block Descriptors here
+ page[6] = 0;
+ page[7] = 0;
+
+ // PS bit reserved
+ page[8] &= 0x3F;
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_MODE_SELECT;
+ cmd[1] = ( sp ? 1 : 0 ) | ( pf ? 0x10 : 0 );
+ cmd[7] = pageLen>>8;
+ cmd[8] = pageLen;
+ cmd[9] = 0;
+ return( cmd.transport( TR_DIR_WRITE, page, pageLen ) == 0 );
+}
+
+
+// does only make sense for complete media
+bool K3bDevice::Device::readCapacity( K3b::Msf& r ) const
+{
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_READ_CAPACITY;
+ cmd[9] = 0; // Necessary to set the proper command length
+ unsigned char buf[8];
+ ::memset( buf, 0, 8 );
+ if( cmd.transport( TR_DIR_READ, buf, 8 ) == 0 ) {
+ r = from4Byte( buf );
+ return true;
+ }
+ else
+ return false;
+}
+
+
+bool K3bDevice::Device::readFormatCapacity( int wantedFormat, K3b::Msf& r,
+ K3b::Msf* currentMax, int* currentMaxFormat ) const
+{
+ bool success = false;
+
+ // the maximal length as stated in MMC4
+ static const unsigned int maxLen = 4 + (8*32);
+
+ unsigned char buffer[maxLen];
+ ::memset( buffer, 0, maxLen );
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_READ_FORMAT_CAPACITIES;
+ cmd[7] = maxLen >> 8;
+ cmd[8] = maxLen & 0xFF;
+ cmd[9] = 0; // Necessary to set the proper command length
+ if( cmd.transport( TR_DIR_READ, buffer, maxLen ) == 0 ) {
+
+ unsigned int realLength = buffer[3] + 4;
+
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " READ FORMAT CAPACITY: Current/Max "
+ << (int)(buffer[8]&0x3) << " " << from4Byte( &buffer[4] ) << endl;
+
+ if( currentMax )
+ *currentMax = from4Byte( &buffer[4] );
+ if( currentMaxFormat )
+ *currentMaxFormat = (int)(buffer[8]&0x3);
+
+ //
+ // Descriptor Type:
+ // 0 - reserved
+ // 1 - unformatted :)
+ // 2 - formatted. Here we get the used capacity (lead-in to last lead-out/border-out)
+ // 3 - No media present
+ //
+ for( unsigned int i = 12; i < realLength-4; i+=8 ) {
+ int format = (int)((buffer[i+4]>>2)&0x3f);
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " READ FORMAT CAPACITY: "
+ << format << " " << from4Byte( &buffer[i] )
+ << " " << (int)( buffer[i+5] << 16 & 0xFF0000 |
+ buffer[i+6] << 8 & 0xFF00 |
+ buffer[i+7] & 0xFF ) << endl;
+
+ if( format == wantedFormat ) {
+ // found the descriptor
+ r = QMAX( (int)from4Byte( &buffer[i] ), r.lba() );
+ success = true;
+ }
+ }
+ }
+
+ return success;
+}
+
+
+bool K3bDevice::Device::readDiscInformation( unsigned char** data, unsigned int& dataLen ) const
+{
+ unsigned char header[2];
+ ::memset( header, 0, 2 );
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_READ_DISC_INFORMATION;
+ cmd[8] = 2;
+ cmd[9] = 0; // Necessary to set the proper command length
+
+ if( cmd.transport( TR_DIR_READ, header, 2 ) == 0 )
+ dataLen = from2Byte( header ) + 2;
+ else
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << ": READ DISC INFORMATION length det failed" << endl;
+
+ if( dataLen < 32 ) {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
+ << ": Device reports bogus disc information length of " << dataLen << endl;
+ dataLen = 32;
+ }
+
+ *data = new unsigned char[dataLen];
+ ::memset( *data, 0, dataLen );
+
+ cmd[7] = dataLen>>8;
+ cmd[8] = dataLen;
+ if( cmd.transport( TR_DIR_READ, *data, dataLen ) == 0 ) {
+ dataLen = QMIN( dataLen, from2Byte( *data ) + 2u );
+ return true;
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": READ DISC INFORMATION with real length "
+ << dataLen << " failed." << endl;
+ delete [] *data;
+ }
+
+ return false;
+}
+
+
+bool K3bDevice::Device::readDvdStructure( unsigned char** data, unsigned int& dataLen,
+ unsigned int format,
+ unsigned int layer,
+ unsigned long adress,
+ unsigned int agid ) const
+{
+ return readDiscStructure( data, dataLen, 0x0, format, layer, adress, agid );
+}
+
+
+bool K3bDevice::Device::readDiscStructure( unsigned char** data, unsigned int& dataLen,
+ unsigned int mediaType,
+ unsigned int format,
+ unsigned int layer,
+ unsigned long adress,
+ unsigned int agid ) const
+{
+ unsigned char header[4];
+ ::memset( header, 0, 4 );
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_READ_DVD_STRUCTURE;
+ cmd[1] = mediaType & 0xF;
+ cmd[2] = adress>>24;
+ cmd[3] = adress>>16;
+ cmd[4] = adress>>8;
+ cmd[5] = adress;
+ cmd[6] = layer;
+ cmd[7] = format;
+ cmd[10] = (agid<<6);
+ cmd[11] = 0; // Necessary to set the proper command length
+
+ cmd[9] = 4;
+ if( cmd.transport( TR_DIR_READ, header, 4 ) == 0 ) {
+ // again with real length
+ dataLen = from2Byte( header ) + 2;
+
+ *data = new unsigned char[dataLen];
+ ::memset( *data, 0, dataLen );
+
+ cmd[8] = dataLen>>8;
+ cmd[9] = dataLen;
+ if( cmd.transport( TR_DIR_READ, *data, dataLen ) == 0 ) {
+ dataLen = QMIN( dataLen, from2Byte( *data ) + 2u );
+ return true;
+ }
+ else {
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": READ DVD STRUCTURE with real length failed." << endl;
+ delete [] *data;
+ }
+ }
+ else
+ k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": READ DVD STRUCTURE length det failed" << endl;
+
+ return false;
+}
+
+
+int K3bDevice::Device::readBufferCapacity( long long& bufferLength, long long& bufferAvail ) const
+{
+ unsigned char data[12];
+ ::memset( data, 0, 12 );
+
+ ScsiCommand cmd( this );
+ cmd[0] = MMC_READ_BUFFER_CAPACITY;
+ cmd[8] = 12;
+ cmd[9] = 0; // Necessary to set the proper command length
+ int r = cmd.transport( TR_DIR_READ, data, 12 );
+ if( r )
+ return r;
+
+ unsigned int dataLength = from2Byte( data );
+
+ if( dataLength >= 10 ) {
+ bufferLength = from4Byte( &data[4] );
+ bufferAvail = from4Byte( &data[8] );
+ }
+ else {
+ bufferAvail = bufferLength = 0;
+ }
+
+ return 0;
+}
diff --git a/libk3bdevice/k3bdeviceglobals.cpp b/libk3bdevice/k3bdeviceglobals.cpp
new file mode 100644
index 0000000..c778130
--- /dev/null
+++ b/libk3bdevice/k3bdeviceglobals.cpp
@@ -0,0 +1,247 @@
+/*
+ *
+ * $Id: k3bdeviceglobals.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bdeviceglobals.h"
+#include "k3bdiskinfo.h"
+#include "k3bdevice.h"
+
+#include <klocale.h>
+#include <k3bdebug.h>
+
+#include <qstringlist.h>
+
+
+QString K3bDevice::deviceTypeString( int t )
+{
+ QStringList s;
+ if( t & K3bDevice::DEVICE_CD_R )
+ s += i18n("CD-R");
+ if( t & K3bDevice::DEVICE_CD_RW )
+ s += i18n("CD-RW");
+ if( t & K3bDevice::DEVICE_CD_ROM )
+ s += i18n("CD-ROM");
+ if( t & K3bDevice::DEVICE_DVD_ROM )
+ s += i18n("DVD-ROM");
+ if( t & K3bDevice::DEVICE_DVD_RAM )
+ s += i18n("DVD-RAM");
+ if( t & K3bDevice::DEVICE_DVD_R )
+ s += i18n("DVD-R");
+ if( t & K3bDevice::DEVICE_DVD_RW )
+ s += i18n("DVD-RW");
+ if( t & K3bDevice::DEVICE_DVD_R_DL )
+ s += i18n("DVD-R DL");
+ if( t & DEVICE_HD_DVD_ROM )
+ s += i18n("HD DVD-ROM");
+ if( t & DEVICE_HD_DVD_R )
+ s += i18n("HD DVD-R");
+ if( t & DEVICE_HD_DVD_RAM )
+ s += i18n("HD DVD-RAM");
+ if( t & DEVICE_BD_ROM )
+ s += i18n("BD-ROM");
+ if( t & DEVICE_BD_R )
+ s += i18n("BD-R");
+ if( t & DEVICE_BD_RE )
+ s += i18n("BD-RE");
+ if( t & K3bDevice::DEVICE_DVD_PLUS_R )
+ s += i18n("DVD+R");
+ if( t & K3bDevice::DEVICE_DVD_PLUS_RW )
+ s += i18n("DVD+RW");
+ if( t & K3bDevice::DEVICE_DVD_PLUS_R_DL )
+ s += i18n("DVD+R DL");
+
+ if( s.isEmpty() )
+ return i18n("Error");
+ else
+ return s.join( ", " );
+}
+
+
+QString K3bDevice::writingModeString( int m )
+{
+ QStringList s;
+ if( m & K3bDevice::WRITINGMODE_SAO )
+ s += i18n("SAO");
+ if( m & K3bDevice::WRITINGMODE_TAO )
+ s += i18n("TAO");
+ if( m & K3bDevice::WRITINGMODE_RAW )
+ s += i18n("RAW");
+ if( m & K3bDevice::WRITINGMODE_SAO_R96P )
+ s += i18n("SAO/R96P");
+ if( m & K3bDevice::WRITINGMODE_SAO_R96R )
+ s += i18n("SAO/R96R");
+ if( m & K3bDevice::WRITINGMODE_RAW_R16 )
+ s += i18n("RAW/R16");
+ if( m & K3bDevice::WRITINGMODE_RAW_R96P )
+ s += i18n("RAW/R96P");
+ if( m & K3bDevice::WRITINGMODE_RAW_R96R )
+ s += i18n("RAW/R96R");
+ if( m & K3bDevice::WRITINGMODE_INCR_SEQ )
+ s += i18n("Incremental Sequential");
+ if( m & K3bDevice::WRITINGMODE_RES_OVWR )
+ s += i18n("Restricted Overwrite");
+ if( m & K3bDevice::WRITINGMODE_LAYER_JUMP )
+ s += i18n("Layer Jump");
+
+ if( m & K3bDevice::WRITINGMODE_RRM )
+ s += i18n("Random Recording");
+ if( m & K3bDevice::WRITINGMODE_SRM )
+ s += i18n("Sequential Recording");
+ if( m & K3bDevice::WRITINGMODE_SRM_POW )
+ s += i18n("Sequential Recording + POW");
+
+ if( s.isEmpty() )
+ return i18n("None");
+ else
+ return s.join( ", " );
+}
+
+
+QString K3bDevice::mediaTypeString( int m, bool simple )
+{
+ if( m == K3bDevice::MEDIA_UNKNOWN )
+ return i18n("Unknown");
+
+ QStringList s;
+ if( m & MEDIA_NONE )
+ s += i18n("No media");
+ if( m & MEDIA_DVD_ROM )
+ s += i18n("DVD-ROM");
+ if( m & MEDIA_DVD_R ||
+ (simple && (m & MEDIA_DVD_R_SEQ)) )
+ s += i18n("DVD-R");
+ if( m & MEDIA_DVD_R_SEQ && !simple )
+ s += i18n("DVD-R Sequential");
+ if( m & MEDIA_DVD_R_DL ||
+ (simple && (m & (MEDIA_DVD_R_DL_SEQ|MEDIA_DVD_R_DL_JUMP))) )
+ s += i18n("DVD-R Dual Layer");
+ if( m & MEDIA_DVD_R_DL_SEQ && !simple )
+ s += i18n("DVD-R Dual Layer Sequential");
+ if( m & MEDIA_DVD_R_DL_JUMP && !simple )
+ s += i18n("DVD-R Dual Layer Jump");
+ if( m & MEDIA_DVD_RAM )
+ s += i18n("DVD-RAM");
+ if( m & MEDIA_DVD_RW ||
+ (simple && (m & (MEDIA_DVD_RW_OVWR|MEDIA_DVD_RW_SEQ))) )
+ s += i18n("DVD-RW");
+ if( m & MEDIA_DVD_RW_OVWR && !simple )
+ s += i18n("DVD-RW Restricted Overwrite");
+ if( m & MEDIA_DVD_RW_SEQ && !simple )
+ s += i18n("DVD-RW Sequential");
+ if( m & MEDIA_DVD_PLUS_RW )
+ s += i18n("DVD+RW");
+ if( m & MEDIA_DVD_PLUS_R )
+ s += i18n("DVD+R");
+ if( m & MEDIA_DVD_PLUS_RW_DL )
+ s += i18n("DVD+RW Dual Layer");
+ if( m & MEDIA_DVD_PLUS_R_DL )
+ s += i18n("DVD+R Dual Layer");
+ if( m & MEDIA_CD_ROM )
+ s += i18n("CD-ROM");
+ if( m & MEDIA_CD_R )
+ s += i18n("CD-R");
+ if( m & MEDIA_CD_RW )
+ s += i18n("CD-RW");
+ if( m & MEDIA_HD_DVD_ROM )
+ s += i18n("HD DVD-ROM");
+ if( m & MEDIA_HD_DVD_R )
+ s += i18n("HD DVD-R");
+ if( m & MEDIA_HD_DVD_RAM )
+ s += i18n("HD DVD-RAM");
+ if( m & MEDIA_BD_ROM )
+ s += i18n("BD-ROM");
+ if( m & MEDIA_BD_R ||
+ (simple && (m & (MEDIA_BD_R_SRM|MEDIA_BD_R_RRM))) )
+ s += i18n("BD-R");
+ if( m & MEDIA_BD_R_SRM && !simple )
+ s += i18n("BD-R Sequential (SRM)");
+ if( m & MEDIA_BD_R_SRM_POW && !simple )
+ s += i18n("BD-R Sequential Pseudo Overwrite (SRM+POW)");
+ if( m & MEDIA_BD_R_RRM && !simple )
+ s += i18n("BD-R Random (RRM)");
+ if( m & MEDIA_BD_RE )
+ s += i18n("BD-RE");
+
+ if( s.isEmpty() )
+ return i18n("Error");
+ else
+ return s.join( ", " );
+}
+
+
+void K3bDevice::debugBitfield( unsigned char* data, long len )
+{
+ for( int i = 0; i < len; ++i ) {
+ QString index, bitString;
+ index.sprintf( "%4i", i );
+ for( int bp = 7; bp >= 0; --bp )
+ bitString[7-bp] = ( data[i] & (1<<bp) ? '1' : '0' );
+ k3bDebug() << index << " - " << bitString << " - " << (int)data[i] << endl;
+ }
+}
+
+
+K3bDevice::uint16 K3bDevice::from2Byte( unsigned char* d )
+{
+ return ( d[0] << 8 & 0xFF00 |
+ d[1] & 0xFF );
+}
+
+
+K3bDevice::uint32 K3bDevice::from4Byte( unsigned char* d )
+{
+ return ( d[0] << 24 & 0xFF000000 |
+ d[1] << 16 & 0xFF0000 |
+ d[2] << 8 & 0xFF00 |
+ d[3] & 0xFF );
+}
+
+
+char K3bDevice::fromBcd( const char& i )
+{
+ return (i & 0x0f) + 10 * ( (i >> 4) & 0x0f );
+}
+
+
+char K3bDevice::toBcd( const char& i )
+{
+ return ( i % 10 ) | ( ( (( i / 10 ) % 10) << 4 ) & 0xf0 );
+}
+
+
+bool K3bDevice::isValidBcd( const char& i )
+{
+ return ( i & 0x0f ) <= 0x09 && ( i & 0xf0 ) <= 0x90;
+}
+
+
+int K3bDevice::determineMaxReadingBufferSize( K3bDevice::Device* dev, const K3b::Msf& firstSector )
+{
+ //
+ // As long as we do not know how to determine the max read buffer properly we simply determine it
+ // by trying. :)
+ //
+
+ int bufferSizeSectors = 128;
+ unsigned char buffer[2048*128];
+ while( !dev->read10( buffer, 2048*bufferSizeSectors, firstSector.lba(), bufferSizeSectors ) ) {
+ k3bDebug() << "(K3bDataTrackReader) determine max read sectors: "
+ << bufferSizeSectors << " too high." << endl;
+ bufferSizeSectors--;
+ }
+ k3bDebug() << "(K3bDataTrackReader) determine max read sectors: "
+ << bufferSizeSectors << " is max." << endl;
+
+ return bufferSizeSectors;
+}
diff --git a/libk3bdevice/k3bdeviceglobals.h b/libk3bdevice/k3bdeviceglobals.h
new file mode 100644
index 0000000..5e167ad
--- /dev/null
+++ b/libk3bdevice/k3bdeviceglobals.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * $Id: k3bdeviceglobals.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_DEVICE_GLOBALS_H_
+#define _K3B_DEVICE_GLOBALS_H_
+
+#include <qstring.h>
+#include <k3bmsf.h>
+#include "k3bdevice_export.h"
+
+namespace K3bDevice
+{
+ typedef Q_UINT8 uint8;
+ typedef Q_UINT16 uint16;
+ typedef Q_UINT32 uint32;
+
+ class Device;
+
+ LIBK3BDEVICE_EXPORT QString deviceTypeString( int );
+ LIBK3BDEVICE_EXPORT QString writingModeString( int );
+ /**
+ * @param simplyfied if true the formatting state of DVD media is left out.
+ */
+ LIBK3BDEVICE_EXPORT QString mediaTypeString( int, bool simplyfied = false );
+ LIBK3BDEVICE_EXPORT void debugBitfield( unsigned char* data, long len );
+
+ LIBK3BDEVICE_EXPORT uint16 from2Byte( unsigned char* );
+ LIBK3BDEVICE_EXPORT uint32 from4Byte( unsigned char* );
+
+ LIBK3BDEVICE_EXPORT char fromBcd( const char& );
+ LIBK3BDEVICE_EXPORT char toBcd( const char& );
+ LIBK3BDEVICE_EXPORT bool isValidBcd( const char& );
+
+ /**
+ * @return the maximum nuber of sectors that can be read from device @p dev starting
+ * at sector @p firstSector.
+ */
+ int determineMaxReadingBufferSize( Device* dev, const K3b::Msf& firstSector );
+}
+
+#endif
diff --git a/libk3bdevice/k3bdevicemanager.cpp b/libk3bdevice/k3bdevicemanager.cpp
new file mode 100644
index 0000000..b7c04d0
--- /dev/null
+++ b/libk3bdevice/k3bdevicemanager.cpp
@@ -0,0 +1,903 @@
+/*
+ *
+ * $Id: k3bdevicemanager.cpp 676188 2007-06-16 08:55:00Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include <config.h>
+
+#include "k3bdevicemanager.h"
+#include "k3bdevice.h"
+#include "k3bdeviceglobals.h"
+#include "k3bscsicommand.h"
+#include "k3bmmc.h"
+#include "k3bdebug.h"
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qptrlist.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qregexp.h>
+
+#include <kprocess.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <ktempfile.h>
+
+#include <iostream>
+#include <limits.h>
+#include <assert.h>
+
+#ifdef Q_OS_FREEBSD
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <osreldate.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_RESMGR
+#include <resmgr.h>
+#endif
+
+#ifdef Q_OS_LINUX
+
+/* Fix definitions for 2.5 kernels */
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,70)
+typedef unsigned char u8;
+#endif
+
+#undef __STRICT_ANSI__
+#include <asm/types.h>
+#define __STRICT_ANSI__
+
+#include <scsi/scsi.h>
+#include <linux/major.h>
+
+
+#ifndef SCSI_DISK_MAJOR
+#define SCSI_DISK_MAJOR(M) ((M) == SCSI_DISK0_MAJOR || \
+ ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR) || \
+ ((M) >= SCSI_DISK8_MAJOR && (M) <= SCSI_DISK15_MAJOR))
+#endif
+
+#ifndef SCSI_BLK_MAJOR
+#define SCSI_BLK_MAJOR(M) \
+ (SCSI_DISK_MAJOR(M) \
+ || (M) == SCSI_CDROM_MAJOR)
+#endif
+
+#ifndef SCSI_GENERIC_MAJOR
+#define SCSI_GENERIC_MAJOR 21
+#endif
+
+#endif // Q_OS_LINUX
+
+
+#ifdef Q_OS_FREEBSD
+#include <cam/cam.h>
+#include <cam/scsi/scsi_pass.h>
+#include <camlib.h>
+#endif
+
+#ifdef Q_OS_NETBSD
+#include <sys/scsiio.h>
+#endif
+
+
+
+class K3bDevice::DeviceManager::Private
+{
+public:
+ QPtrList<K3bDevice::Device> allDevices;
+ QPtrList<K3bDevice::Device> cdReader;
+ QPtrList<K3bDevice::Device> cdWriter;
+ QPtrList<K3bDevice::Device> dvdReader;
+ QPtrList<K3bDevice::Device> dvdWriter;
+ QPtrList<K3bDevice::Device> bdReader;
+ QPtrList<K3bDevice::Device> bdWriter;
+
+ bool checkWritingModes;
+};
+
+
+
+K3bDevice::DeviceManager::DeviceManager( QObject* parent, const char* name )
+ : QObject( parent, name )
+{
+ d = new Private;
+}
+
+
+K3bDevice::DeviceManager::~DeviceManager()
+{
+ d->allDevices.setAutoDelete( true );
+ delete d;
+}
+
+
+void K3bDevice::DeviceManager::setCheckWritingModes( bool b )
+{
+ d->checkWritingModes = b;
+}
+
+
+K3bDevice::Device* K3bDevice::DeviceManager::deviceByName( const QString& name )
+{
+ return findDevice( name );
+}
+
+
+K3bDevice::Device* K3bDevice::DeviceManager::findDevice( int bus, int id, int lun )
+{
+ QPtrListIterator<K3bDevice::Device> it( d->allDevices );
+ while( it.current() )
+ {
+ if( it.current()->scsiBus() == bus &&
+ it.current()->scsiId() == id &&
+ it.current()->scsiLun() == lun )
+ return it.current();
+
+ ++it;
+ }
+
+ return 0;
+}
+
+
+K3bDevice::Device* K3bDevice::DeviceManager::findDevice( const QString& devicename )
+{
+ if( devicename.isEmpty() ) {
+ k3bDebug() << "(K3bDevice::DeviceManager) request for empty device!" << endl;
+ return 0;
+ }
+ QPtrListIterator<K3bDevice::Device> it( d->allDevices );
+ while( it.current() ) {
+ if( it.current()->deviceNodes().contains(devicename) )
+ return it.current();
+
+ ++it;
+ }
+
+ return 0;
+}
+
+
+const QPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::cdWriter() const
+{
+ return d->cdWriter;
+}
+
+const QPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::cdReader() const
+{
+ return d->cdReader;
+}
+
+const QPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::dvdWriter() const
+{
+ return d->dvdWriter;
+}
+
+const QPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::dvdReader() const
+{
+ return d->dvdReader;
+}
+
+const QPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::blueRayReader() const
+{
+ return d->bdReader;
+}
+
+const QPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::blueRayWriters() const
+{
+ return d->bdWriter;
+}
+
+const QPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::burningDevices() const
+{
+ return cdWriter();
+}
+
+
+const QPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::readingDevices() const
+{
+ return cdReader();
+}
+
+
+const QPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::allDevices() const
+{
+ return d->allDevices;
+}
+
+
+int K3bDevice::DeviceManager::scanBus()
+{
+ unsigned int numDevs = d->allDevices.count();
+
+#ifdef Q_OS_LINUX
+ LinuxDeviceScan();
+#endif
+#ifdef Q_OS_FREEBSD
+ BSDDeviceScan();
+#endif
+#ifdef Q_OS_NETBSD
+ NetBSDDeviceScan();
+#endif
+
+ return d->allDevices.count() - numDevs;
+}
+
+
+void K3bDevice::DeviceManager::LinuxDeviceScan()
+{
+#ifdef HAVE_RESMGR
+ //
+ // Resmgr device scan
+ //
+ char** resmgrDevices = rsm_list_devices( 0 );
+ if( resmgrDevices ) {
+ for( int i = 0; resmgrDevices[i]; ++i ) {
+ addDevice( resmgrDevices[i] );
+ free( resmgrDevices[i] );
+ }
+
+ free( resmgrDevices );
+ }
+#endif
+
+ QFile info("/proc/sys/dev/cdrom/info");
+ QString line,devstring;
+ if( info.open(IO_ReadOnly) ) {
+ info.readLine(line,80); // CD-ROM information, Id: cdrom.c 3.12 2000/10/18
+ info.readLine(line,80); //
+
+ QRegExp re("[\t\n:]+");
+ while( info.readLine( line, 80 ) > 0 ) {
+ if( line.contains("drive name") > 0 ) {
+ int i = 1;
+ QString dev;
+ while( !(dev = line.section(re, i, i)).isEmpty() ) {
+ if( addDevice( QString("/dev/%1").arg(dev) ) ) {
+ devstring += dev + "|";
+ }
+ // according to the LINUX ALLOCATED DEVICES document (http://www.lanana.org/docs/device-list/),
+ // the official device names for SCSI-CDROM's (block major 11) are /dev/sr*, the
+ // prefix /dev/scd instead of /dev/sr has been used as well, and might make more sense.
+ // Since there should be one and only one device node (and maybe several symbolic links) for
+ // each physical device the next line should be better
+ // else if ( dev.startsWith("sr") )
+ if ( dev.startsWith("sr") ) {
+ if( addDevice(QString("/dev/%1").arg(dev.replace(QRegExp("r"),"cd"))) )
+ devstring += dev + "|";
+ }
+ ++i;
+ }
+ }
+ break;
+ }
+ info.close();
+ }
+ else {
+ kdError() << "(K3bDevice::DeviceManager) could not open /proc/sys/dev/cdrom/info" << endl;
+ }
+
+ //
+ // Scan the generic devices if we have scsi devices
+ //
+ k3bDebug() << "(K3bDevice::DeviceManager) SCANNING FOR GENERIC DEVICES." << endl;
+ for( int i = 0; i < 16; i++ ) {
+ QString sgDev = resolveSymLink( QString("/dev/sg%1").arg(i) );
+ int bus = -1, id = -1, lun = -1;
+ if( determineBusIdLun( sgDev, bus, id, lun ) ) {
+ if( Device* dev = findDevice( bus, id, lun ) ) {
+ dev->m_genericDevice = sgDev;
+ }
+ }
+ }
+ // FIXME: also scan /dev/scsi/hostX.... for devfs without symlinks
+}
+
+
+void K3bDevice::DeviceManager::NetBSDDeviceScan()
+{
+ // Generate entries for /dev/cd* devices
+ // Note: As there are only 10 possible /dev/(r)cd devices,
+ // only these will be found.
+
+ int i;
+
+ // Whole disk mask (According to cd(4), the AMD64, i386 and BeBox ports use
+ // 'd' as whole-disk partition, the rest uses 'c'.)
+
+#if defined(__i386__) || defined (__amd64__) || defined (__bebox__)
+ static const char slicename = 'd';
+#else
+ static const char slicename = 'c';
+#endif
+
+ char devicename[11]; // /dev/rcdXd + trailing zero
+
+ for (i = 0; i < 10; i++ ) // cd(4) claims there are max. 10 CD devices.
+ {
+ snprintf(devicename,11,"/dev/rcd%d%c",i, slicename);
+ addDevice(QString(devicename));
+ }
+}
+
+
+void K3bDevice::DeviceManager::BSDDeviceScan()
+{
+ // Unfortunately uses lots of FBSD-specific data structures
+#ifndef Q_OS_FREEBSD
+ // bool bsdspecificcode = false;
+ // assert(bsdspecificcode);
+#endif
+
+#ifdef Q_OS_FREEBSD
+ union ccb ccb;
+ int fd;
+ int need_close = 0;
+ int skip_device = 0;
+ int bus, target, lun;
+ QString dev1, dev2;
+
+ if ((fd = open(XPT_DEVICE, O_RDWR)) == -1)
+ {
+ k3bDebug() << "couldn't open %s " << XPT_DEVICE << endl;
+ return;
+ }
+
+ memset(&ccb, 0, sizeof(ccb));
+
+ ccb.ccb_h.func_code = XPT_DEV_MATCH;
+ char buffer[100*sizeof(struct dev_match_result)];
+ ccb.cdm.match_buf_len = 100*sizeof(struct dev_match_result);
+ ccb.cdm.matches = (struct dev_match_result *)buffer;
+ ccb.cdm.num_matches = 0;
+ ccb.cdm.num_patterns = 0;
+ ccb.cdm.pattern_buf_len = 0;
+ do {
+ if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
+ k3bDebug() << "(BSDDeviceScan) error sending CAMIOCOMMAND ioctl: " << errno << endl;
+ break;
+ }
+
+ if ((ccb.ccb_h.status != CAM_REQ_CMP)
+ || ((ccb.cdm.status != CAM_DEV_MATCH_LAST) && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) {
+ k3bDebug() << "(BSDDeviceScan) got CAM error " << ccb.ccb_h.status << ", CDM error %d" << ccb.cdm.status << endl;
+ break;
+ }
+ k3bDebug() << "(BSDDeviceScan) number of matches " << (int)ccb.cdm.num_matches << endl;
+ for (int i = 0; i < (int)ccb.cdm.num_matches; i++) {
+ switch (ccb.cdm.matches[i].type) {
+ case DEV_MATCH_DEVICE: {
+ struct device_match_result *dev_result = &ccb.cdm.matches[i].result.device_result;
+
+ if (dev_result->flags & DEV_RESULT_UNCONFIGURED)
+ {
+ skip_device = 1;
+ break;
+ }
+ else
+ skip_device = 0;
+ if (need_close)
+ {
+ QString pass = dev1;
+ QString dev = "/dev/" + dev2;
+ if (dev2.startsWith("pass"))
+ {
+ pass = dev2;
+ dev = "/dev/" + dev1;
+ }
+#if __FreeBSD_version < 500100
+ dev += "c";
+#endif
+ if (!dev1.isEmpty() && !dev2.isEmpty() && dev.startsWith("/dev/cd"))
+ {
+ Device* device = new Device(dev.latin1());
+ device->m_bus = bus;
+ device->m_target = target;
+ device->m_lun = lun;
+ device->m_passDevice = "/dev/" + pass;
+ k3bDebug() << "(BSDDeviceScan) add device " << dev << ":" << bus << ":" << target << ":" << lun << endl;
+ addDevice(device);
+ }
+ need_close = 0;
+ dev1="";
+ dev2="";
+ }
+ bus = dev_result->path_id;
+ target = dev_result->target_id;
+ lun = dev_result->target_lun;
+
+ need_close = 1;
+
+ break;
+ }
+ case DEV_MATCH_PERIPH: {
+ struct periph_match_result *periph_result = &ccb.cdm.matches[i].result.periph_result;
+
+ if (skip_device != 0)
+ break;
+
+ if (need_close > 1)
+ dev1 = periph_result->periph_name + QString::number(periph_result->unit_number);
+ else
+ dev2 = periph_result->periph_name + QString::number(periph_result->unit_number);
+
+ need_close++;
+ break;
+ }
+ case DEV_MATCH_BUS : {
+ // bool cannotmatchbus = false;
+ // assert(cannotmatchbus);
+ break;
+ }
+ }
+ }
+
+ } while ((ccb.ccb_h.status == CAM_REQ_CMP)
+ && (ccb.cdm.status == CAM_DEV_MATCH_MORE));
+
+ if (need_close)
+ {
+ QString pass = dev1;
+ QString dev = "/dev/" + dev2;
+ if (dev2.startsWith("pass"))
+ {
+ pass = dev2;
+ dev = "/dev/" + dev1;
+ }
+#if __FreeBSD_version < 500100
+ dev += "c";
+#endif
+ if (!dev1.isEmpty() && !dev2.isEmpty() && dev.startsWith("/dev/cd"))
+ {
+ Device* device = new Device(dev.latin1());
+ device->m_bus = bus;
+ device->m_target = target;
+ device->m_lun = lun;
+ device->m_passDevice = "/dev/" + pass;
+ k3bDebug() << "(BSDDeviceScan) add device " << dev << ":" << bus << ":" << target << ":" << lun << endl;
+ addDevice(device);
+ }
+ }
+ close(fd);
+#endif
+}
+
+
+void K3bDevice::DeviceManager::printDevices()
+{
+ k3bDebug() << "Devices:" << endl
+ << "------------------------------" << endl;
+ QPtrListIterator<Device> it( allDevices() );
+ for( ; *it; ++it ) {
+ Device* dev = *it;
+ k3bDebug() << "Blockdevice: " << dev->blockDeviceName() << endl
+ << "Generic device: " << dev->genericDevice() << endl
+ << "Vendor: " << dev->vendor() << endl
+ << "Description: " << dev->description() << endl
+ << "Version: " << dev->version() << endl
+ << "Write speed: " << dev->maxWriteSpeed() << endl
+ << "Profiles: " << mediaTypeString( dev->supportedProfiles() ) << endl
+ << "Read Cap: " << mediaTypeString( dev->readCapabilities() ) << endl
+ << "Write Cap: " << mediaTypeString( dev->writeCapabilities() ) << endl
+ << "Writing modes: " << writingModeString( dev->writingModes() ) << endl
+ << "Reader aliases: " << dev->deviceNodes().join(", ") << endl
+ << "------------------------------" << endl;
+ }
+}
+
+
+void K3bDevice::DeviceManager::clear()
+{
+ // clear current devices
+ d->cdReader.clear();
+ d->cdWriter.clear();
+ d->dvdReader.clear();
+ d->dvdWriter.clear();
+ d->bdReader.clear();
+ d->bdWriter.clear();
+
+ // to make sure no one crashes lets keep the devices around until the changed
+ // signals return
+ QPtrList<K3bDevice::Device> tmp = d->allDevices;
+ tmp.setAutoDelete( true );
+
+ d->allDevices.clear();
+
+ emit changed( this );
+ emit changed();
+}
+
+
+bool K3bDevice::DeviceManager::readConfig( KConfig* c )
+{
+ //
+ // New configuration format since K3b 0.11.94
+ // for details see saveConfig()
+ //
+
+ if( !c->hasGroup( "Devices" ) )
+ return false;
+
+ c->setGroup( "Devices" );
+
+ QStringList deviceSearchPath = c->readListEntry( "device_search_path" );
+ for( QStringList::const_iterator it = deviceSearchPath.constBegin();
+ it != deviceSearchPath.constEnd(); ++it )
+ addDevice( *it );
+
+ //
+ // Iterate over all devices and check if we have a config entry
+ //
+ for( QPtrListIterator<K3bDevice::Device> it( d->allDevices ); *it; ++it ) {
+ K3bDevice::Device* dev = *it;
+
+ QString configEntryName = dev->vendor() + " " + dev->description();
+ QStringList list = c->readListEntry( configEntryName );
+ if( !list.isEmpty() ) {
+ k3bDebug() << "(K3bDevice::DeviceManager) found config entry for devicetype: " << configEntryName << endl;
+
+ dev->setMaxReadSpeed( list[0].toInt() );
+ if( list.count() > 1 )
+ dev->setMaxWriteSpeed( list[1].toInt() );
+ if( list.count() > 2 )
+ dev->setCdrdaoDriver( list[2] );
+ if( list.count() > 3 )
+ dev->setCdTextCapability( list[3] == "yes" );
+ }
+ }
+
+ return true;
+}
+
+
+bool K3bDevice::DeviceManager::saveConfig( KConfig* c )
+{
+ //
+ // New configuration format since K3b 0.11.94
+ //
+ // We save a device search path which contains all device nodes
+ // where devices could be found including the old search path.
+ // This way also for example a manually added USB device will be
+ // found between sessions.
+ // Then we do not save the device settings (writing speed, cdrdao driver)
+ // for every single device but for every device type.
+ // This also makes sure device settings are kept between sessions
+ //
+
+ c->setGroup( "Devices" );
+ QStringList deviceSearchPath = c->readListEntry( "device_search_path" );
+ // remove duplicate entries (caused by buggy old implementations)
+ QStringList saveDeviceSearchPath;
+ for( QStringList::const_iterator it = deviceSearchPath.constBegin(); it != deviceSearchPath.constEnd(); ++it )
+ if( !saveDeviceSearchPath.contains( *it ) )
+ saveDeviceSearchPath.append( *it );
+
+ for( QPtrListIterator<K3bDevice::Device> it( d->allDevices ); *it; ++it ) {
+ K3bDevice::Device* dev = *it;
+
+ // update device search path
+ if( !saveDeviceSearchPath.contains( dev->blockDeviceName() ) )
+ saveDeviceSearchPath.append( dev->blockDeviceName() );
+
+ // save the device type settings
+ QString configEntryName = dev->vendor() + " " + dev->description();
+ QStringList list;
+ list << QString::number(dev->maxReadSpeed())
+ << QString::number(dev->maxWriteSpeed())
+ << dev->cdrdaoDriver();
+
+ if( dev->cdrdaoDriver() != "auto" )
+ list << ( dev->cdTextCapable() == 1 ? "yes" : "no" );
+ else
+ list << "auto";
+
+ c->writeEntry( configEntryName, list );
+ }
+
+ c->writeEntry( "device_search_path", saveDeviceSearchPath );
+
+ c->sync();
+
+ return true;
+}
+
+
+bool K3bDevice::DeviceManager::testForCdrom( const QString& devicename )
+{
+#ifdef Q_OS_FREEBSD
+ Q_UNUSED(devicename);
+ return true;
+#endif
+#if defined(Q_OS_LINUX) || defined(Q_OS_NETBSD)
+ bool ret = false;
+ int cdromfd = K3bDevice::openDevice( devicename.ascii() );
+ if (cdromfd < 0) {
+ k3bDebug() << "could not open device " << devicename << " (" << strerror(errno) << ")" << endl;
+ return ret;
+ }
+
+ // stat the device
+ struct stat cdromStat;
+ if( ::fstat( cdromfd, &cdromStat ) )
+ return false;
+
+ if( !S_ISBLK( cdromStat.st_mode) ) {
+ k3bDebug() << devicename << " is no block device" << endl;
+ }
+ else {
+ k3bDebug() << devicename << " is block device (" << (int)(cdromStat.st_rdev & 0xFF) << ")" << endl;
+#if defined(Q_OS_NETBSD)
+ }
+ {
+#endif
+ // inquiry
+ // use a 36 bytes buffer since not all devices return the full inquiry struct
+ unsigned char buf[36];
+ struct inquiry* inq = (struct inquiry*)buf;
+ ::memset( buf, 0, sizeof(buf) );
+
+ ScsiCommand cmd( cdromfd );
+ cmd[0] = MMC_INQUIRY;
+ cmd[4] = sizeof(buf);
+ cmd[5] = 0;
+
+ if( cmd.transport( TR_DIR_READ, buf, sizeof(buf) ) ) {
+ k3bDebug() << "(K3bDevice::Device) Unable to do inquiry. " << devicename << " is not a cdrom device" << endl;
+ }
+ else if( (inq->p_device_type&0x1f) != 0x5 ) {
+ k3bDebug() << devicename << " seems not to be a cdrom device: " << strerror(errno) << endl;
+ }
+ else {
+ ret = true;
+ k3bDebug() << devicename << " seems to be cdrom" << endl;
+ }
+ }
+
+ ::close( cdromfd );
+ return ret;
+#endif
+}
+
+K3bDevice::Device* K3bDevice::DeviceManager::addDevice( const QString& devicename )
+{
+#ifdef Q_OS_FREEBSD
+ return 0;
+#endif
+
+ K3bDevice::Device* device = 0;
+
+ // resolve all symlinks
+ QString resolved = resolveSymLink( devicename );
+ k3bDebug() << devicename << " resolved to " << resolved << endl;
+
+ if ( K3bDevice::Device* oldDev = findDevice(resolved) ) {
+ k3bDebug() << "(K3bDevice::DeviceManager) dev " << resolved << " already found" << endl;
+ oldDev->addDeviceNode( devicename );
+ return 0;
+ }
+
+ if( !testForCdrom(resolved) ) {
+#ifdef HAVE_RESMGR
+ // With resmgr we might only be able to open the symlink name.
+ if( testForCdrom(devicename) ) {
+ resolved = devicename;
+ }
+ else {
+ return 0;
+ }
+#else
+ return 0;
+#endif
+ }
+
+ int bus = -1, target = -1, lun = -1;
+ bool scsi = determineBusIdLun( resolved, bus, target, lun );
+ if(scsi) {
+ if ( K3bDevice::Device* oldDev = findDevice(bus, target, lun) ) {
+ k3bDebug() << "(K3bDevice::DeviceManager) dev " << resolved << " already found" << endl;
+ oldDev->addDeviceNode( devicename );
+ return 0;
+ }
+ }
+
+ device = new K3bDevice::Device(resolved);
+ if( scsi ) {
+ device->m_bus = bus;
+ device->m_target = target;
+ device->m_lun = lun;
+ }
+
+ return addDevice(device);
+}
+
+
+K3bDevice::Device* K3bDevice::DeviceManager::addDevice( K3bDevice::Device* device )
+{
+ const QString devicename = device->devicename();
+
+ if( !device->init() ) {
+ k3bDebug() << "Could not initialize device " << devicename << endl;
+ delete device;
+ return 0;
+ }
+
+ if( device ) {
+ d->allDevices.append( device );
+
+ // not every drive is able to read CDs
+ // there are some 1st generation DVD writer that cannot
+ if( device->type() & K3bDevice::DEVICE_CD_ROM )
+ d->cdReader.append( device );
+ if( device->readsDvd() )
+ d->dvdReader.append( device );
+ if( device->writesCd() )
+ d->cdWriter.append( device );
+ if( device->writesDvd() )
+ d->dvdWriter.append( device );
+ if( device->readCapabilities() & MEDIA_BD_ALL )
+ d->bdReader.append( device );
+ if( device->writeCapabilities() & MEDIA_BD_ALL )
+ d->bdWriter.append( device );
+
+ if( device->writesCd() ) {
+ // default to max write speed
+ k3bDebug() << "(K3bDevice::DeviceManager) setting current write speed of device "
+ << device->blockDeviceName()
+ << " to " << device->maxWriteSpeed() << endl;
+ device->setCurrentWriteSpeed( device->maxWriteSpeed() );
+ }
+
+ emit changed( this );
+ emit changed();
+ }
+
+ return device;
+}
+
+
+void K3bDevice::DeviceManager::removeDevice( const QString& dev )
+{
+ if( Device* device = findDevice( dev ) ) {
+ d->cdReader.removeRef( device );
+ d->dvdReader.removeRef( device );
+ d->bdReader.removeRef( device );
+ d->cdWriter.removeRef( device );
+ d->dvdWriter.removeRef( device );
+ d->bdWriter.removeRef( device );
+ d->allDevices.removeRef( device );
+
+ emit changed( this );
+ emit changed();
+
+ delete device;
+ }
+}
+
+
+bool K3bDevice::DeviceManager::determineBusIdLun( const QString& dev, int& bus, int& id, int& lun )
+{
+#ifdef Q_OS_FREEBSD
+ Q_UNUSED(dev);
+ Q_UNUSED(bus);
+ Q_UNUSED(id);
+ Q_UNUSED(lun);
+ return false;
+ /* NOTREACHED */
+#endif
+
+#ifdef Q_OS_NETBSD
+ int cdromfd = K3bDevice::openDevice ( dev.ascii() );
+ if (cdromfd < 0) {
+ int local_errno = errno; // For all we know, k3bDebug() destroys errno
+ k3bDebug() << "could not open device " << dev << " (" << strerror(local_errno) << ")" << endl;
+ return false;
+ }
+
+ struct scsi_addr my_addr;
+
+ if (::ioctl(cdromfd, SCIOCIDENTIFY, &my_addr))
+ {
+ int local_errno = errno; // For all we know, k3bDebug() destroys errno
+ k3bDebug() << "ioctl(SCIOCIDENTIFY) failed on device " << dev << " (" << strerror(local_errno) << ")" << endl;
+
+ ::close(cdromfd);
+ return false;
+ }
+
+ if (my_addr.type == TYPE_ATAPI)
+ {
+ // XXX Re-map atapibus, so it doesn't conflict with "real" scsi
+ // busses
+
+ bus = 15;
+ id = my_addr.addr.atapi.drive + 2 * my_addr.addr.atapi.atbus;
+ lun = 0;
+ }
+ else
+ {
+ bus = my_addr.addr.scsi.scbus;
+ id = my_addr.addr.scsi.target;
+ lun = my_addr.addr.scsi.lun;
+ }
+
+ ::close(cdromfd);
+
+ return true;
+#endif
+
+#ifdef Q_OS_LINUX
+ int ret = false;
+ int cdromfd = K3bDevice::openDevice( dev.ascii() );
+ if (cdromfd < 0) {
+ return false;
+ }
+
+ struct stat cdromStat;
+ if ( ::fstat( cdromfd, &cdromStat ) )
+ return false;
+
+ if( SCSI_BLK_MAJOR( cdromStat.st_rdev>>8 ) ||
+ SCSI_GENERIC_MAJOR == (cdromStat.st_rdev>>8) ) {
+ struct ScsiIdLun
+ {
+ int id;
+ int lun;
+ };
+ ScsiIdLun idLun;
+
+ // in kernel 2.2 SCSI_IOCTL_GET_IDLUN does not contain the bus id
+ if ( (::ioctl( cdromfd, SCSI_IOCTL_GET_IDLUN, &idLun ) < 0) ||
+ (::ioctl( cdromfd, SCSI_IOCTL_GET_BUS_NUMBER, &bus ) < 0) ) {
+ k3bDebug() << "Need a filename that resolves to a SCSI device" << endl;
+ ret = false;
+ }
+ else {
+ id = idLun.id & 0xff;
+ lun = (idLun.id >> 8) & 0xff;
+ k3bDebug() << "bus: " << bus << ", id: " << id << ", lun: " << lun << endl;
+ ret = true;
+ }
+ }
+
+ ::close(cdromfd);
+ return ret;
+#endif
+}
+
+
+QString K3bDevice::DeviceManager::resolveSymLink( const QString& path )
+{
+ char resolved[PATH_MAX];
+ if( !realpath( QFile::encodeName(path), resolved ) )
+ {
+ k3bDebug() << "Could not resolve " << path << endl;
+ return path;
+ }
+
+ return QString::fromLatin1( resolved );
+}
+
+
+#include "k3bdevicemanager.moc"
diff --git a/libk3bdevice/k3bdevicemanager.h b/libk3bdevice/k3bdevicemanager.h
new file mode 100644
index 0000000..4656538
--- /dev/null
+++ b/libk3bdevice/k3bdevicemanager.h
@@ -0,0 +1,247 @@
+/*
+ *
+ * $Id: k3bdevicemanager.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BDEVICEMANAGER_H
+#define K3BDEVICEMANAGER_H
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qmemarray.h>
+#include <qptrlist.h>
+
+#include "k3bdevice_export.h"
+#include <kdebug.h>
+
+class KProcess;
+class KConfig;
+class K3bExternalBin;
+
+
+namespace K3bDevice {
+
+ class Device;
+
+ /**
+ * \brief Manages all devices.
+ *
+ * Searches the system for devices and maintains lists of them.
+ *
+ * <b>Basic usage:</b>
+ * \code
+ * K3bDevice::DeviceManager* manager = new K3bDevice::DeviceManager( this );
+ * manager->scanBus();
+ * K3bDevice::Device* dev = manager->findDevice( "/dev/cdrom" );
+ * \endcode
+ */
+ class LIBK3BDEVICE_EXPORT DeviceManager : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ /**
+ * Creates a new DeviceManager
+ */
+ DeviceManager( QObject* parent = 0, const char* name = 0 );
+ virtual ~DeviceManager();
+
+ /**
+ * By default the DeviceManager makes the Devices check their writing modes.
+ * This includes commands to be sent which require writing permissions on the
+ * devices and might take some time.
+ *
+ * So if you don't need the information about the writing modes use this method
+ * to speed up the device detection procedure.
+ *
+ * Be aware that this only refers to CD writing modes. If you only want to handle
+ * DVD devices it's always save to set this to false.
+ */
+ void setCheckWritingModes( bool b );
+
+ /**
+ * \deprecated use findDevice( const QString& )
+ */
+ Device* deviceByName( const QString& );
+
+ /**
+ * Search an SCSI device by SCSI bus, id, and lun.
+ *
+ * \note This method does not initialize new devices.
+ * Devices cannot be found until they have been added via addDevice(const QString&)
+ * or scanBus().
+ *
+ * \return The corresponding device or 0 if there is no such device.
+ */
+ Device* findDevice( int bus, int id, int lun );
+
+ /**
+ * Search a device by blockdevice name.
+ *
+ * \note This method does not initialize new devices.
+ * Devices cannot be found until they have been added via addDevice(const QString&)
+ * or scanBus().
+ *
+ * \return The corresponding device or 0 if there is no such device.
+ */
+ Device* findDevice( const QString& devicename );
+
+ /**
+ * Before getting the devices do a @ref scanBus().
+ * \return List of all cd writer devices.
+ * \deprecated use cdWriter()
+ */
+ const QPtrList<Device>& burningDevices() const;
+
+ /**
+ * \return List of all reader devices without writer devices.
+ * \deprecated use cdReader()
+ **/
+ const QPtrList<Device>& readingDevices() const;
+
+ /**
+ * Before getting the devices do a @ref scanBus() or add
+ * devices via addDevice( const QString& ).
+ *
+ * \return List of all devices.
+ */
+ const QPtrList<Device>& allDevices() const;
+
+ /**
+ * Before getting the devices do a @ref scanBus() or add
+ * devices via addDevice( const QString& ).
+ *
+ * \return List of all cd writer devices.
+ */
+ const QPtrList<Device>& cdWriter() const;
+
+ /**
+ * Before getting the devices do a @ref scanBus() or add
+ * devices via addDevice( const QString& ).
+ *
+ * \return List of all cd reader devices.
+ */
+ const QPtrList<Device>& cdReader() const;
+
+ /**
+ * Before getting the devices do a @ref scanBus() or add
+ * devices via addDevice( const QString& ).
+ *
+ * \return List of all DVD writer devices.
+ */
+ const QPtrList<Device>& dvdWriter() const;
+
+ /**
+ * Before getting the devices do a @ref scanBus() or add
+ * devices via addDevice( const QString& ).
+ *
+ * \return List of all DVD reader devices.
+ */
+ const QPtrList<Device>& dvdReader() const;
+
+ /**
+ * Before getting the devices do a @ref scanBus() or add
+ * devices via addDevice( const QString& ).
+ *
+ * \return List of all Blue Ray reader devices.
+ */
+ const QPtrList<Device>& blueRayReader() const;
+
+ /**
+ * Before getting the devices do a @ref scanBus() or add
+ * devices via addDevice( const QString& ).
+ *
+ * \return List of all Blue Ray writer devices.
+ */
+ const QPtrList<Device>& blueRayWriters() const;
+
+ /**
+ * Reads the device information from the config file.
+ */
+ virtual bool readConfig( KConfig* );
+
+ virtual bool saveConfig( KConfig* );
+
+
+ public slots:
+ /**
+ * Writes a list of all devices to stderr.
+ */
+ void printDevices();
+
+ /**
+ * Scan the system for devices. Call this to initialize all devices.
+ *
+ * If the system uses the HAL device deamon it is possible to use
+ * HalConnection instead of calling this method.
+ *
+ * \return Number of found devices.
+ **/
+ virtual int scanBus();
+
+ /**
+ * Clears the writers and readers list of devices.
+ */
+ virtual void clear();
+
+ /**
+ * Add a new device.
+ *
+ * \param dev Name of a block device or link to a block device. If the
+ * corresponding device has already been detected it will simply
+ * be returned. Otherwise if a device is found it will be initialized
+ * and added to the internal lists (meaning it can be accessed through
+ * emthods like cdReader()).
+ *
+ * Called by scanBus()
+ *
+ * \return The device if it could be found or 0 otherwise.
+ */
+ virtual Device* addDevice( const QString& dev );
+
+ /**
+ * Remove a device from the device manager. Basicly this method
+ * only makes sense in combination with the HalConnection. Connect
+ * it to the deviceRemoved signal.
+ */
+ virtual void removeDevice( const QString& dev );
+
+ signals:
+ /**
+ * Emitted if the device configuration changed, i.e. a device was added or removed.
+ */
+ void changed( K3bDevice::DeviceManager* );
+ void changed();
+
+ private:
+ bool testForCdrom( const QString& );
+ bool determineBusIdLun( const QString &dev, int& bus, int& id, int& lun );
+ QString resolveSymLink( const QString& path );
+
+ class Private;
+ Private* d;
+
+ /**
+ * Add a device to the managers device lists and initialize the device.
+ */
+ Device *addDevice( Device* );
+
+ void BSDDeviceScan();
+ void NetBSDDeviceScan();
+ void LinuxDeviceScan();
+ };
+}
+
+#endif
diff --git a/libk3bdevice/k3bdevicetypes.h b/libk3bdevice/k3bdevicetypes.h
new file mode 100644
index 0000000..feb9a4e
--- /dev/null
+++ b/libk3bdevice/k3bdevicetypes.h
@@ -0,0 +1,266 @@
+/*
+ *
+ * $Id: k3bdevicetypes.h 654649 2007-04-16 17:55:50Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_DEVICE_TYPES_H_
+#define _K3B_DEVICE_TYPES_H_
+
+namespace K3bDevice {
+ const unsigned short FEATURE_PROFILE_LIST = 0x000;
+ const unsigned short FEATURE_CORE = 0x001;
+ const unsigned short FEATURE_MORPHING = 0x002;
+ const unsigned short FEATURE_REMOVABLE_MEDIA = 0x003;
+ const unsigned short FEATURE_WRITE_PROTECT = 0x004;
+ const unsigned short FEATURE_RANDOM_READABLE = 0x010;
+ const unsigned short FEATURE_MULTI_READ = 0x01D;
+ const unsigned short FEATURE_CD_READ = 0x01E;
+ const unsigned short FEATURE_DVD_READ = 0x01F;
+ const unsigned short FEATURE_RANDOM_WRITABLE = 0x020;
+ const unsigned short FEATURE_INCREMENTAL_STREAMING_WRITABLE = 0x021;
+ const unsigned short FEATURE_SECTOR_ERASABLE = 0x022;
+ const unsigned short FEATURE_FORMATTABLE = 0x023;
+ const unsigned short FEATURE_DEFECT_MANAGEMENT = 0x024;
+ const unsigned short FEATURE_WRITE_ONCE = 0x025;
+ const unsigned short FEATURE_RESTRICTED_OVERWRITE = 0x026;
+ const unsigned short FEATURE_CD_RW_CAV_WRITE = 0x027;
+ const unsigned short FEATURE_MRW = 0x028;
+ const unsigned short FEATURE_ENHANCED_DEFECT_REPORTING = 0x029;
+ const unsigned short FEATURE_DVD_PLUS_RW = 0x02A;
+ const unsigned short FEATURE_DVD_PLUS_R = 0x02B;
+ const unsigned short FEATURE_RIGID_RESTRICTED_OVERWRITE = 0x02C;
+ const unsigned short FEATURE_CD_TRACK_AT_ONCE = 0x02D;
+ const unsigned short FEATURE_CD_MASTERING = 0x02E;
+ const unsigned short FEATURE_DVD_R_RW_WRITE = 0x02F;
+ const unsigned short FEATURE_DDCD_READ = 0x030;
+ const unsigned short FEATURE_DDCD_R_WRITE = 0x031;
+ const unsigned short FEATURE_DDCD_RW_WRITE = 0x032;
+ const unsigned short FEATURE_LAYER_JUMP_RECORDING = 0x033;
+ const unsigned short FEATURE_CD_RW_MEDIA_WRITE_SUPPORT = 0x037;
+ const unsigned short FEATURE_BD_PSEUDO_OVERWRITE = 0x038;
+ const unsigned short FEATURE_DVD_PLUS_RW_DUAL_LAYER = 0x03A; /**< since MMC5 revision 3 */
+ const unsigned short FEATURE_DVD_PLUS_R_DUAL_LAYER = 0x03B;
+ const unsigned short FEATURE_BD_READ = 0x040;
+ const unsigned short FEATURE_BD_WRITE = 0x041;
+ const unsigned short FEATURE_HD_DVD_READ = 0x050;
+ const unsigned short FEATURE_HD_DVD_WRITE = 0x051;
+ const unsigned short FEATURE_POWER_MANAGEMENT = 0x100;
+ const unsigned short FEATURE_EMBEDDED_CHANGER = 0x102;
+ const unsigned short FEATURE_CD_AUDIO_ANALOG_PLAY = 0x103;
+ const unsigned short FEATURE_MICROCODE_UPGRADE = 0x104;
+ const unsigned short FEATURE_TIMEOUT = 0x105;
+ const unsigned short FEATURE_DVD_CSS = 0x106;
+ const unsigned short FEATURE_REAL_TIME_STREAMING = 0x107;
+ const unsigned short FEATURE_LOGICAL_UNIT_SERIAL_NUMBER = 0x108;
+ const unsigned short FEATURE_DISC_CONTROL_BLOCKS = 0x10A;
+ const unsigned short FEATURE_DVD_CPRM = 0x10B;
+ const unsigned short FEATURE_FIRMWARE_DATE = 0x10C;
+
+ enum Interface {
+ SCSI, /**< The device is accessed through the SCSI subsystem. */
+ IDE, /**< The device is accessed through the IDE (ATAPI) interface. */
+ OTHER /**< Unknown interface (this is not used as the DeviceManager does only handle SCSI and IDE anyway). */
+ };
+
+ /**
+ * Specifies the device type. Device::type() returns a bitwise or
+ * of device types.
+ */
+ enum DeviceType {
+ DEVICE_CD_ROM = 0x1, /**< Device reads CD-ROM media (every device in K3b supports this.) */
+ DEVICE_CD_R = 0x2, /**< Device writes CD-R media */
+ DEVICE_CD_RW = 0x4, /**< Device writes CD-RW media */
+ DEVICE_DVD_ROM = 0x8, /**< Device reads DVD-ROM media */
+ DEVICE_DVD_RAM = 0x10, /**< Device writes DVD-RAM media */
+ DEVICE_DVD_R = 0x20, /**< Device writes DVD-R media */
+ DEVICE_DVD_RW = 0x40, /**< Device writes DVD-RW media */
+ DEVICE_DVD_R_DL = 0x80, /**< Device writes DVD-R Dual Layer media */
+ DEVICE_DVD_PLUS_R = 0x100, /**< Device writes DVD+R media */
+ DEVICE_DVD_PLUS_RW = 0x200, /**< Device writes DVD+RW media */
+ DEVICE_DVD_PLUS_R_DL = 0x400, /**< Device writes DVD+R Double Layer media */
+ DEVICE_HD_DVD_ROM = 0x800, /**< Device reads HD DVD-ROM media */
+ DEVICE_HD_DVD_R = 0x1000, /**< Device writes HD DVD-R media */
+ DEVICE_HD_DVD_RAM = 0x2000, /**< Device writes HD DVD-RAM media */
+ DEVICE_BD_ROM = 0x4000, /**< Device reads BD-ROM media */
+ DEVICE_BD_R = 0x8000, /**< Device writes BD-R media */
+ DEVICE_BD_RE = 0x10000, /**< Device writes BD-RE media */
+ CDR = DEVICE_CD_R, /**< \deprecated {use DEVICE_CD_R instead) */
+ CDRW = DEVICE_CD_RW, /**< \deprecated {use DEVICE_CD_RW instead) */
+ CDROM = DEVICE_CD_ROM, /**< \deprecated {use DEVICE_CD_ROM instead) */
+ DVD = DEVICE_DVD_ROM, /**< \deprecated {use DEVICE_DVD_ROM instead) */
+ DVDRAM = DEVICE_DVD_RAM, /**< \deprecated {use DEVICE_DVD_RAM instead) */
+ DVDR = DEVICE_DVD_R, /**< \deprecated {use DEVICE_DVD_R instead) */
+ DVDRW = DEVICE_DVD_RW, /**< \deprecated {use DEVICE_DVD_RW instead) */
+ DVDPR = DEVICE_DVD_PLUS_R, /**< \deprecated {use DEVICE_DVD_PLUS_R instead) */
+ DVDPRW = DEVICE_DVD_PLUS_RW /**< \deprecated {use DEVICE_DVD_PLUS_RW instead) */
+ };
+
+
+ /**
+ * The different writing modes. Device::writingModes() returns a bitwise or of writing modes.
+ */
+ enum WritingMode {
+ WRITINGMODE_SAO = 0x1, /**< Device writes CD or DVD-R media in Session at once (also known as DAO) writing mode */
+ WRITINGMODE_SAO_R96P = 0x2, /**< Device writes CD media with packed R-W subchannels Session at once writing mode */
+ WRITINGMODE_SAO_R96R = 0x4, /**< Device writes CD media with raw R-W subchannels Session at once writing mode */
+ WRITINGMODE_TAO = 0x8, /**< Device writes CD media in Track at once writing mode */
+ WRITINGMODE_RAW = 0x10, /**< Device writes CD media in Raw writing mode */
+ WRITINGMODE_RAW_R16 = 0x20, /**< Device writes CD media with P/Q subchannels in Raw writing mode */
+ WRITINGMODE_RAW_R96P = 0x40, /**< Device writes CD media with packed R-W subchannels Raw writing mode */
+ WRITINGMODE_RAW_R96R = 0x80, /**< Device writes CD media with raw R-W subchannels Raw writing mode */
+ WRITINGMODE_INCR_SEQ = 0x100, /**< Device writes DVD-R(W) media in incremental sequential writing mode */
+ WRITINGMODE_RES_OVWR = 0x200, /**< Device writes DVD-RW media in restricted overwrite mode */
+ WRITINGMODE_LAYER_JUMP = 0x400, /**< Device writes DVD-R Dual layer media in Layer Jump writing mode */
+ WRITINGMODE_RRM = 0x800, /**< Device writes BD-R media in Random Recording Mode */
+ WRITINGMODE_SRM = 0x1000, /**< Device writes BD-R media in Sequential recording mode */
+ WRITINGMODE_SRM_POW = 0x2000, /**< Device writes BD-R media in Pseudo overwrite Sequential recording mode */
+ SAO = WRITINGMODE_SAO, /**< \deprecated {use WRITINGMODE_SAO instead) */
+ TAO = WRITINGMODE_TAO, /**< \deprecated {use WRITINGMODE_TAO instead) */
+ RAW = WRITINGMODE_RAW, /**< \deprecated {use WRITINGMODE_RAW instead) */
+ SAO_R96P = WRITINGMODE_SAO_R96P, /**< \deprecated {use WRITINGMODE_SAO_R96P instead) */
+ SAO_R96R = WRITINGMODE_SAO_R96R, /**< \deprecated {use WRITINGMODE_SAO_R96R instead) */
+ RAW_R16 = WRITINGMODE_RAW_R16, /**< \deprecated {use WRITINGMODE_RAW_R16 instead) */
+ RAW_R96P = WRITINGMODE_RAW_R96P, /**< \deprecated {use WRITINGMODE_RAW_R96P instead) */
+ RAW_R96R = WRITINGMODE_RAW_R96R /**< \deprecated {use WRITINGMODE_RAW_R96R instead) */
+ };
+
+
+ enum MediaState {
+ STATE_UNKNOWN = 0x1, /**< Media state is unknown (when an error occurred or the device is unable to determine the media state). */
+ STATE_NO_MEDIA = 0x2, /**< No media inserted. */
+ STATE_COMPLETE = 0x4, /**< The inserted media is complete. */
+ STATE_INCOMPLETE = 0x8, /**< The inserted media is incomplete/appendable. */
+ STATE_EMPTY = 0x10 /**< The inserted media is empty. */
+ };
+
+ enum BackGroundFormattingState {
+ BG_FORMAT_NONE = 0x1,
+ BG_FORMAT_INCOMPLETE = 0x2,
+ BG_FORMAT_IN_PROGRESS = 0x4,
+ BG_FORMAT_COMPLETE = 0x8
+ };
+
+ /**
+ * Defines the different media types as retured by
+ * Device::cdMediaType() and Device::dvdMediaType()
+ */
+ enum MediaType {
+ MEDIA_UNKNOWN = 0x1, /**< Represents an unknown media type (when an error occurred) */
+ MEDIA_NONE = 0x2, /**< No medium is inserted */
+ MEDIA_DVD_ROM = 0x4, /**< */
+ MEDIA_DVD_R = 0x8, /**< */
+ MEDIA_DVD_R_SEQ = 0x10, /**< */
+ MEDIA_DVD_R_DL = 0x20, /**< Dual Layer DVD-R media. */
+ MEDIA_DVD_R_DL_SEQ = 0x40, /**< */
+ MEDIA_DVD_R_DL_JUMP = 0x80, /**< */
+ MEDIA_DVD_RAM = 0x100, /**< */
+ MEDIA_DVD_RW = 0x200, /**< */
+ MEDIA_DVD_RW_OVWR = 0x400, /**< DVD-RW media formatted in Restricted Overwrite mode. */
+ MEDIA_DVD_RW_SEQ = 0x800, /**< DVD-RW media formatted in Incremental Sequential mode. */
+ MEDIA_DVD_PLUS_RW = 0x1000, /**< */
+ MEDIA_DVD_PLUS_R = 0x2000, /**< */
+ MEDIA_DVD_PLUS_R_DL = 0x4000, /**< Double Layer DVD+R media. */
+ MEDIA_DVD_PLUS_RW_DL = 0x8000, /**< Double Layer DVD+RW media. */
+ MEDIA_CD_ROM = 0x10000, /**< */
+ MEDIA_CD_R = 0x20000, /**< */
+ MEDIA_CD_RW = 0x40000, /**< */
+ MEDIA_HD_DVD_ROM = 0x80000, /**< */
+ MEDIA_HD_DVD_R = 0x100000, /**< */
+ MEDIA_HD_DVD_RAM = 0x200000, /**< */
+ MEDIA_BD_ROM = 0x400000, /**< Read-only Blu-ray Disc (BD) */
+ MEDIA_BD_R = 0x800000, /**< Writable Blu-ray Disc (BD-R) */
+ MEDIA_BD_R_SRM = 0x1000000, /**< Writable Blu-ray Disc (BD-R) */
+ MEDIA_BD_R_SRM_POW = 0x2000000, /**< Writable Blu-ray Disc (BD-R) */
+ MEDIA_BD_R_RRM = 0x4000000, /**< Writable Blu-ray Disc (BD-R) */
+ MEDIA_BD_RE = 0x8000000, /**< Rewritable Blu-ray Disc (BD-RE) */
+ MEDIA_WRITABLE_CD = MEDIA_CD_R | /**< This is a bitwise or of media types representing all writable CD media.*/
+ MEDIA_CD_RW,
+ MEDIA_CD_ALL = MEDIA_WRITABLE_CD |
+ MEDIA_CD_ROM,
+ MEDIA_WRITABLE_DVD_SL = MEDIA_DVD_R | /**< This is a bitwise or of media types representing all writable single layer DVD media.*/
+ MEDIA_DVD_R_SEQ |
+ MEDIA_DVD_RW |
+ MEDIA_DVD_RW_OVWR |
+ MEDIA_DVD_RW_SEQ |
+ MEDIA_DVD_PLUS_RW |
+ MEDIA_DVD_PLUS_R,
+ MEDIA_WRITABLE_DVD_DL = MEDIA_DVD_R_DL | /**< This is a bitwise or of media types representing all writable double layer DVD media.*/
+ MEDIA_DVD_R_DL_SEQ |
+ MEDIA_DVD_R_DL_JUMP |
+ MEDIA_DVD_PLUS_R_DL |
+ MEDIA_DVD_PLUS_RW_DL,
+ MEDIA_WRITABLE_DVD = MEDIA_WRITABLE_DVD_SL | /**< This is a bitwise or of media types representing all writable DVD media.*/
+ MEDIA_WRITABLE_DVD_DL,
+ MEDIA_REWRITABLE_DVD = MEDIA_DVD_RW |
+ MEDIA_DVD_RW_OVWR |
+ MEDIA_DVD_RW_SEQ |
+ MEDIA_DVD_PLUS_RW_DL |
+ MEDIA_DVD_PLUS_RW,
+ MEDIA_WRITABLE_BD = MEDIA_BD_R | /**< This is a bitwise or of media types representing all writable BD media.*/
+ MEDIA_BD_R_SRM |
+ MEDIA_BD_R_SRM_POW |
+ MEDIA_BD_R_RRM |
+ MEDIA_BD_RE,
+ MEDIA_WRITABLE = MEDIA_WRITABLE_CD | /**< This is a bitwise or of media types representing all writable media.*/
+ MEDIA_WRITABLE_DVD |
+ MEDIA_WRITABLE_BD,
+ MEDIA_REWRITABLE = MEDIA_CD_RW |
+ MEDIA_REWRITABLE_DVD |
+ MEDIA_BD_RE,
+ MEDIA_DVD_MINUS_ALL = MEDIA_DVD_R | /**< This is a bitwise or of media types representing all DVD-R/W media.*/
+ MEDIA_DVD_R_SEQ |
+ MEDIA_DVD_RW |
+ MEDIA_DVD_RW_OVWR |
+ MEDIA_DVD_RW_SEQ |
+ MEDIA_DVD_R_DL |
+ MEDIA_DVD_R_DL_SEQ |
+ MEDIA_DVD_R_DL_JUMP,
+ MEDIA_DVD_PLUS_ALL = MEDIA_DVD_PLUS_RW | /**< This is a bitwise or of media types representing all DVD+R/W media.*/
+ MEDIA_DVD_PLUS_R |
+ MEDIA_DVD_PLUS_R_DL |
+ MEDIA_DVD_PLUS_RW_DL,
+ MEDIA_DVD_ALL = MEDIA_WRITABLE_DVD |
+ MEDIA_DVD_ROM,
+ MEDIA_BD_ALL = MEDIA_WRITABLE_BD |
+ MEDIA_BD_ROM,
+ MEDIA_ALL = MEDIA_CD_ALL |
+ MEDIA_DVD_ALL |
+ MEDIA_BD_ALL
+ };
+
+ inline bool isDvdMedia( int mediaType ) {
+ return ( mediaType == MEDIA_DVD_ROM ||
+ mediaType == MEDIA_DVD_R ||
+ mediaType == MEDIA_DVD_R_SEQ ||
+ mediaType == MEDIA_DVD_R_DL ||
+ mediaType == MEDIA_DVD_R_DL_SEQ ||
+ mediaType == MEDIA_DVD_R_DL_JUMP ||
+ mediaType == MEDIA_DVD_RW ||
+ mediaType == MEDIA_DVD_RW_OVWR ||
+ mediaType == MEDIA_DVD_RW_SEQ ||
+ mediaType == MEDIA_DVD_PLUS_RW ||
+ mediaType == MEDIA_DVD_PLUS_R ||
+ mediaType == MEDIA_DVD_PLUS_R_DL );
+ }
+
+ inline bool isRewritableMedia( int mediaType ) {
+ return ( mediaType == MEDIA_DVD_RW ||
+ mediaType == MEDIA_DVD_RW_OVWR ||
+ mediaType == MEDIA_DVD_RW_SEQ ||
+ mediaType == MEDIA_DVD_PLUS_RW ||
+ mediaType == MEDIA_CD_RW );
+ }
+}
+
+#endif
diff --git a/libk3bdevice/k3bdiskinfo.cpp b/libk3bdevice/k3bdiskinfo.cpp
new file mode 100644
index 0000000..6c91d19
--- /dev/null
+++ b/libk3bdevice/k3bdiskinfo.cpp
@@ -0,0 +1,266 @@
+/*
+ *
+ * $Id: k3bdiskinfo.cpp 654649 2007-04-16 17:55:50Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bdiskinfo.h"
+#include "k3bdeviceglobals.h"
+
+#include <k3bmsf.h>
+
+#include <klocale.h>
+#include <k3bdebug.h>
+#include <kio/global.h>
+
+#include <qstringlist.h>
+
+
+K3bDevice::DiskInfo::DiskInfo()
+ : m_mediaType(MEDIA_UNKNOWN),
+ m_currentProfile(MEDIA_UNKNOWN),
+ m_diskState(STATE_UNKNOWN),
+ m_lastSessionState(STATE_UNKNOWN),
+ m_bgFormatState(0),
+ m_numSessions(0),
+ m_numTracks(0),
+ m_rewritable(false)
+{
+}
+
+
+K3bDevice::DiskInfo::~DiskInfo()
+{
+}
+
+
+int K3bDevice::DiskInfo::diskState() const
+{
+ return m_diskState;
+}
+
+
+int K3bDevice::DiskInfo::lastSessionState() const
+{
+ return m_lastSessionState;
+}
+
+
+int K3bDevice::DiskInfo::bgFormatState() const
+{
+ return m_bgFormatState;
+}
+
+
+bool K3bDevice::DiskInfo::empty() const
+{
+ return diskState() == STATE_EMPTY;
+}
+
+
+bool K3bDevice::DiskInfo::rewritable() const
+{
+ return m_rewritable;
+}
+
+
+bool K3bDevice::DiskInfo::appendable() const
+{
+ return diskState() == STATE_INCOMPLETE;
+}
+
+
+int K3bDevice::DiskInfo::mediaType() const
+{
+ return m_mediaType;
+}
+
+
+bool K3bDevice::DiskInfo::isDvdMedia() const
+{
+ return K3bDevice::isDvdMedia( mediaType() );
+}
+
+
+int K3bDevice::DiskInfo::numSessions() const
+{
+ if( empty() )
+ return 0;
+ else
+ return m_numSessions;
+}
+
+
+int K3bDevice::DiskInfo::numTracks() const
+{
+ if( empty() )
+ return 0;
+ else
+ return m_numTracks;
+}
+
+
+int K3bDevice::DiskInfo::numLayers() const
+{
+ if( isDvdMedia() )
+ return m_numLayers;
+ else
+ return 1;
+}
+
+
+K3b::Msf K3bDevice::DiskInfo::remainingSize() const
+{
+ if( empty() )
+ return capacity();
+
+ //
+ // There is no way to properly determine the used size on an overwrite media
+ // without having a look at the filesystem (or is there?)
+ //
+ else if( appendable() ||
+ mediaType() & (MEDIA_DVD_PLUS_RW|MEDIA_DVD_RW_OVWR) )
+ return capacity() - m_usedCapacity;
+
+ else
+ return 0;
+}
+
+
+K3b::Msf K3bDevice::DiskInfo::capacity() const
+{
+ return (m_capacity == 0 ? size() : m_capacity);
+}
+
+
+K3b::Msf K3bDevice::DiskInfo::size() const
+{
+ if( empty() )
+ return 0;
+ else
+ return m_usedCapacity;
+}
+
+
+K3b::Msf K3bDevice::DiskInfo::firstLayerSize() const
+{
+ if( numLayers() > 1 )
+ return m_firstLayerSize;
+ else
+ return size();
+}
+
+
+void K3bDevice::DiskInfo::debug() const
+{
+ k3bDebug() << "DiskInfo:" << endl
+ << "Mediatype: " << K3bDevice::mediaTypeString( mediaType() ) << endl
+ << "Current Profile: " << K3bDevice::mediaTypeString( currentProfile() ) << endl
+ << "Disk state: " << ( diskState() == K3bDevice::STATE_EMPTY ?
+ "empty" :
+ ( diskState() == K3bDevice::STATE_INCOMPLETE ?
+ "incomplete" :
+ ( diskState() == K3bDevice::STATE_COMPLETE ?
+ "complete" :
+ ( diskState() == K3bDevice::STATE_NO_MEDIA ?
+ "no media" :
+ "unknown" ) ) ) ) << endl
+ << "Empty: " << empty() << endl
+ << "Rewritable: " << rewritable() << endl
+ << "Appendable: " << appendable() << endl
+ << "Sessions: " << numSessions() << endl
+ << "Tracks: " << numTracks() << endl
+ << "Layers: " << numLayers() << endl
+ << "Capacity: " << capacity()
+ << " (LBA " << capacity().lba()
+ << ") (" << capacity().mode1Bytes() << " Bytes)" << endl
+
+ << "Remaining size: " << remainingSize()
+ << " (LBA " << remainingSize().lba()
+ << ") (" << remainingSize().mode1Bytes() << " Bytes)" << endl
+
+ << "Used Size: " << size()
+ << " (LBA " << size().lba()
+ << ") (" << size().mode1Bytes() << " Bytes)" << endl;
+
+ if( mediaType() == K3bDevice::MEDIA_DVD_PLUS_RW )
+ k3bDebug() << "Bg Format: " << ( bgFormatState() == BG_FORMAT_NONE ?
+ "none" :
+ ( bgFormatState() == BG_FORMAT_INCOMPLETE ?
+ "incomplete" :
+ ( bgFormatState() == BG_FORMAT_IN_PROGRESS ?
+ "in progress" :
+ ( bgFormatState() == BG_FORMAT_COMPLETE ?
+ "complete" : "unknown" ) ) ) ) << endl;
+}
+
+
+bool K3bDevice::DiskInfo::operator==( const K3bDevice::DiskInfo& other ) const
+{
+ return( m_mediaType == other.m_mediaType &&
+ m_currentProfile == other.m_currentProfile &&
+ m_diskState == other.m_diskState &&
+ m_lastSessionState == other.m_lastSessionState &&
+ m_bgFormatState == other.m_bgFormatState &&
+ m_numSessions == other.m_numSessions &&
+ m_numTracks == other.m_numTracks &&
+ m_numLayers == other.m_numLayers &&
+ m_rewritable == other.m_rewritable &&
+ m_capacity == other.m_capacity &&
+ m_usedCapacity == other.m_usedCapacity &&
+ m_firstLayerSize == other.m_firstLayerSize &&
+ m_mediaId == other.m_mediaId );
+}
+
+
+bool K3bDevice::DiskInfo::operator!=( const K3bDevice::DiskInfo& other ) const
+{
+ return( m_mediaType != other.m_mediaType ||
+ m_currentProfile != other.m_currentProfile ||
+ m_diskState != other.m_diskState ||
+ m_lastSessionState != other.m_lastSessionState ||
+ m_bgFormatState != other.m_bgFormatState ||
+ m_numSessions != other.m_numSessions ||
+ m_numTracks != other.m_numTracks ||
+ m_numLayers != other.m_numLayers ||
+ m_rewritable != other.m_rewritable ||
+ m_capacity != other.m_capacity ||
+ m_usedCapacity != other.m_usedCapacity ||
+ m_firstLayerSize != other.m_firstLayerSize ||
+ m_mediaId != other.m_mediaId );
+}
+
+
+// kdbgstream& K3bDevice::operator<<( kdbgstream& s, const K3bDevice::DiskInfo& ngInf )
+// {
+// s << "DiskInfo:" << endl
+// << "Mediatype: " << K3bDevice::mediaTypeString( ngInf.mediaType() ) << endl
+// << "Current Profile: " << K3bDevice::mediaTypeString( ngInf.currentProfile() ) << endl
+// << "Disk state: " << ( ngInf.diskState() == K3bDevice::STATE_EMPTY ?
+// "empty" :
+// ( ngInf.diskState() == K3bDevice::STATE_INCOMPLETE ?
+// "incomplete" :
+// ( ngInf.diskState() == K3bDevice::STATE_COMPLETE ?
+// "complete" :
+// ( ngInf.diskState() == K3bDevice::STATE_NO_MEDIA ?
+// "no media" :
+// "unknown" ) ) ) ) << endl
+// << "Empty: " << ngInf.empty() << endl
+// << "Rewritable: " << ngInf.rewritable() << endl
+// << "Appendable: " << ngInf.appendable() << endl
+// << "Sessions: " << ngInf.numSessions() << endl
+// << "Tracks: " << ngInf.numTracks() << endl
+// << "Size: " << ngInf.capacity().toString() << endl
+// << "Remaining size: " << ngInf.remainingSize().toString() << endl;
+
+// return s;
+// }
diff --git a/libk3bdevice/k3bdiskinfo.h b/libk3bdevice/k3bdiskinfo.h
new file mode 100644
index 0000000..ed7c382
--- /dev/null
+++ b/libk3bdevice/k3bdiskinfo.h
@@ -0,0 +1,182 @@
+/*
+ *
+ * $Id: k3bdiskinfo.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_DISKINFO_H_
+#define _K3B_DISKINFO_H_
+
+#include <k3bdevicetypes.h>
+
+#include <k3btoc.h>
+#include <k3bmsf.h>
+#include "k3bdevice_export.h"
+
+#include <qcstring.h>
+
+
+class kdbgstream;
+
+
+namespace K3bDevice
+{
+ /**
+ * This class is directly accociated to a strcuture from
+ * the MMC draft READ_DISK_INFO.
+ * It also holds some additional data.
+ * This class' data will be retrieved by K3bDevice::Device.
+ *
+ * Before using any values one should check diskState != STATE_UNKNOWN or
+ * diskState == STATE_NO_MEDIA.
+ * That may mean that no disk is in the drive or an error occurred.
+ * Writers should never give the STATE_UNKNOWN state. CD-ROM or DVD-ROM
+ * drives on the other hand may have trouble determining the state of the disk.
+ */
+ class LIBK3BDEVICE_EXPORT DiskInfo
+ {
+ public:
+ DiskInfo();
+ ~DiskInfo();
+
+ /**
+ * Returnes the state of the disk.
+ * See enum State.
+ */
+ int diskState() const;
+
+ /**
+ * Returnes the state of the last session.
+ * See enum State.
+ */
+ int lastSessionState() const;
+
+ /**
+ * Returnes the state of the background formatting. This does
+ * only make sense for DVD+RW (and MRW which is not yet supported)
+ */
+ int bgFormatState() const;
+
+ /**
+ * returnes true if diskState() == STATE_EMPTY
+ */
+ bool empty() const;
+
+ /**
+ * Is this a rewritable media (e.g. a CD-RW, DVD-RW, or DVD+RW)
+ */
+ bool rewritable() const;
+
+ /**
+ * Is this disk appendable
+ * returnes true if diskState() == STATE_INCOMPLETE
+ */
+ bool appendable() const;
+
+ /**
+ * The type of the disk:
+ * CD-ROM, CD-R, CD-RW, DVD-ROM, DVD-R(W), DVD+R(W)
+ */
+ int mediaType() const;
+
+ /**
+ * This is the current profile of the drive. That means it may differ
+ * from drive to drive.
+ * -1 means no info.
+ * Mainly interesting for the distiction of DVD-R(W) modes:
+ * Sequential and Restricted Overwrite.
+ */
+ int currentProfile() const { return m_currentProfile; }
+
+ /**
+ * Just for easy implementation since there are so many
+ * different DVD formats.
+ */
+ bool isDvdMedia() const;
+
+ /**
+ * The number of sessions on the disk.
+ * This does not include any leadout or the last empty session
+ * on a DVD+-R(W)
+ */
+ int numSessions() const;
+
+ /**
+ * The number of finished tracks.
+ * This does not include the empty track.
+ */
+ int numTracks() const;
+
+ /**
+ * Number of layers on a DVD media. For CD media this is always 1.
+ */
+ int numLayers() const;
+
+ /**
+ * Does only make sense for appendable disks.
+ */
+ K3b::Msf remainingSize() const;
+
+ /**
+ * The capacity of the disk.
+ * For complete disks this may be the same as size()
+ */
+ K3b::Msf capacity() const;
+
+ /**
+ * Returns the size of the used part.
+ * For appendable media this equals capacity() - remainingSize()
+ */
+ K3b::Msf size() const;
+
+ /**
+ * Returns the size of Data area in the first layer for DL DVD media.
+ * Otherwise size() is returned.
+ *
+ * This does not specify the layer change sector as the data area on DVD media does
+ * not start at sector 0 but at sector 30000h or 31000h depending on the type.
+ */
+ K3b::Msf firstLayerSize() const;
+
+ const QCString& mediaId() const { return m_mediaId; }
+
+ void debug() const;
+
+ bool operator==( const DiskInfo& ) const;
+ bool operator!=( const DiskInfo& ) const;
+
+ private:
+ int m_mediaType;
+ int m_currentProfile;
+
+ int m_diskState;
+ int m_lastSessionState;
+ int m_bgFormatState;
+ int m_numSessions;
+ int m_numTracks;
+ int m_numLayers; // only for DVD media
+ int m_rewritable;
+
+ K3b::Msf m_capacity;
+ K3b::Msf m_usedCapacity;
+ K3b::Msf m_firstLayerSize;
+
+ QCString m_mediaId;
+
+ friend class Device;
+ };
+
+ // kdbgstream& operator<<( kdbgstream& s, const DiskInfo& ngInf );
+}
+
+#endif
diff --git a/libk3bdevice/k3bhalconnection.cpp b/libk3bdevice/k3bhalconnection.cpp
new file mode 100644
index 0000000..2b0877b
--- /dev/null
+++ b/libk3bdevice/k3bhalconnection.cpp
@@ -0,0 +1,610 @@
+/*
+ *
+ * $Id: sourceheader,v 1.3 2005/01/19 13:03:46 trueg Exp $
+ * Copyright (C) 2005-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bhalconnection.h"
+#include "k3bdevice.h"
+
+#include <k3bdebug.h>
+#include <klocale.h>
+
+#include <qtimer.h>
+
+// We acknowledge the the dbus API is unstable
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/connection.h>
+#include <dbus/dbus.h>
+#include <hal/libhal.h>
+
+
+static char** qstringListToArray( const QStringList& s )
+{
+ char** a = new char*[s.count()];
+ for( unsigned int i = 0; i < s.count(); ++i ) {
+ a[i] = new char[s[i].length()+1];
+ ::strncpy( a[i], s[i].local8Bit().data(), s[i].length() );
+ a[s[i].length()] = '\0';
+ }
+ return a;
+}
+
+static void freeArray( char** a, unsigned int length )
+{
+ for( unsigned int i = 0; i < length; ++i )
+ delete [] a[i];
+ delete a;
+}
+
+
+// CALLBACKS
+void halDeviceAdded( LibHalContext* ctx, const char* udi )
+{
+ Q_UNUSED( ctx );
+ k3bDebug() << "adding udi " << udi << endl;
+ K3bDevice::HalConnection::instance()->addDevice( udi );
+}
+
+
+void halDeviceRemoved( LibHalContext* ctx, const char* udi )
+{
+ Q_UNUSED( ctx );
+ k3bDebug() << "removing udi " << udi << endl;
+ K3bDevice::HalConnection::instance()->removeDevice( udi );
+}
+
+
+K3bDevice::HalConnection* K3bDevice::HalConnection::s_instance = 0;
+
+
+class K3bDevice::HalConnection::Private
+{
+public:
+ Private()
+ : halContext(0),
+ dBusQtConnection(0),
+ bOpen(false) {
+ }
+
+ LibHalContext* halContext;
+ DBusConnection* connection;
+ DBusQt::Connection* dBusQtConnection;
+
+ bool bOpen;
+
+ QMap<QCString, QString> udiDeviceMap;
+ QMap<QString, QCString> deviceUdiMap;
+
+ QMap<QCString, QCString> deviceMediumUdiMap;
+};
+
+
+K3bDevice::HalConnection* K3bDevice::HalConnection::instance()
+{
+ if( s_instance == 0 )
+ s_instance = new HalConnection( 0 );
+
+ if( !s_instance->isConnected() && !s_instance->open() )
+ k3bDebug() << "(K3bDevice::HalConnection) failed to open connection to HAL." << endl;
+
+ return s_instance;
+}
+
+
+K3bDevice::HalConnection::HalConnection( QObject* parent, const char* name )
+ : QObject( parent, name )
+{
+ d = new Private();
+}
+
+
+K3bDevice::HalConnection::~HalConnection()
+{
+ s_instance = 0;
+ close();
+ delete d;
+}
+
+
+bool K3bDevice::HalConnection::isConnected() const
+{
+ return d->bOpen;
+}
+
+
+bool K3bDevice::HalConnection::open()
+{
+ close();
+
+ k3bDebug() << "(K3bDevice::HalConnection) initializing HAL >= 0.5" << endl;
+
+ d->halContext = libhal_ctx_new();
+ if( !d->halContext ) {
+ k3bDebug() << "(K3bDevice::HalConnection) unable to create HAL context." << endl;
+ return false;
+ }
+
+ DBusError error;
+ dbus_error_init( &error );
+ d->connection = dbus_bus_get( DBUS_BUS_SYSTEM, &error );
+ if( dbus_error_is_set(&error) ) {
+ k3bDebug() << "(K3bDevice::HalConnection) unable to connect to DBUS: " << error.message << endl;
+ return false;
+ }
+
+ setupDBusQtConnection( d->connection );
+
+ libhal_ctx_set_dbus_connection( d->halContext, d->connection );
+
+ libhal_ctx_set_device_added( d->halContext, halDeviceAdded );
+ libhal_ctx_set_device_removed( d->halContext, halDeviceRemoved );
+ libhal_ctx_set_device_new_capability( d->halContext, 0 );
+ libhal_ctx_set_device_lost_capability( d->halContext, 0 );
+ libhal_ctx_set_device_property_modified( d->halContext, 0 );
+ libhal_ctx_set_device_condition( d->halContext, 0 );
+
+ if( !libhal_ctx_init( d->halContext, 0 ) ) {
+ k3bDebug() << "(K3bDevice::HalConnection) Failed to init HAL context!" << endl;
+ return false;
+ }
+
+ d->bOpen = true;
+
+ //
+ // report all devices
+ //
+ int numDevices;
+ char** halDeviceList = libhal_get_all_devices( d->halContext, &numDevices, 0 );
+ for( int i = 0; i < numDevices; ++i )
+ addDevice( halDeviceList[i] );
+
+ return true;
+}
+
+
+void K3bDevice::HalConnection::close()
+{
+ if( d->halContext ) {
+ // clear the context
+ if( isConnected() )
+ libhal_ctx_shutdown( d->halContext, 0 );
+ libhal_ctx_free( d->halContext );
+
+ // delete the connection (may be 0 if open() failed)
+ delete d->dBusQtConnection;
+
+ d->halContext = 0;
+ d->dBusQtConnection = 0;
+ d->bOpen = false;
+ }
+}
+
+
+QStringList K3bDevice::HalConnection::devices() const
+{
+ return QStringList( d->udiDeviceMap.values() );
+}
+
+
+void K3bDevice::HalConnection::addDevice( const char* udi )
+{
+ // ignore devices that have no property "info.capabilities" to suppress error messages
+ if( !libhal_device_property_exists( d->halContext, udi, "info.capabilities", 0 ) )
+ return;
+
+ if( libhal_device_query_capability( d->halContext, udi, "storage.cdrom", 0 ) ) {
+ char* dev = libhal_device_get_property_string( d->halContext, udi, "block.device", 0 );
+ if( dev ) {
+ QString s( dev );
+ libhal_free_string( dev );
+
+ if( !s.isEmpty() ) {
+ k3bDebug() << "Mapping udi " << udi << " to device " << s << endl;
+ d->udiDeviceMap[udi] = s;
+ d->deviceUdiMap[s] = udi;
+ emit deviceAdded( s );
+ }
+ }
+ }
+ else {
+ if( libhal_device_property_exists( d->halContext, udi, "block.storage_device", 0 ) ) {
+ char* deviceUdi = libhal_device_get_property_string( d->halContext, udi, "block.storage_device", 0 );
+ if( deviceUdi ) {
+ QCString du( deviceUdi );
+ libhal_free_string( deviceUdi );
+
+ if( d->udiDeviceMap.contains( du ) ) {
+ //
+ // A new medium has been inserted. Save this medium's udi so we can reuse it later
+ // on for the mount/unmount/eject methods
+ //
+ d->deviceMediumUdiMap[du] = QCString( udi );
+ emit mediumChanged( d->udiDeviceMap[du] );
+ }
+ }
+ }
+ }
+}
+
+
+void K3bDevice::HalConnection::removeDevice( const char* udi )
+{
+ QMapIterator<QCString, QString> it = d->udiDeviceMap.find( udi );
+ if( it != d->udiDeviceMap.end() ) {
+ k3bDebug() << "Unmapping udi " << udi << " from device " << it.data() << endl;
+ emit deviceRemoved( it.data() );
+ d->udiDeviceMap.erase( it );
+ d->deviceUdiMap.erase( it.data() );
+ }
+ else {
+ if( libhal_device_property_exists( d->halContext, udi, "block.storage_device", 0 ) ) {
+ char* deviceUdi = libhal_device_get_property_string( d->halContext, udi, "block.storage_device", 0 );
+ if( deviceUdi ) {
+ QCString du( deviceUdi );
+ libhal_free_string( deviceUdi );
+
+ if( d->udiDeviceMap.contains( du ) ) {
+ //
+ // A medium has been removed/ejected.
+ //
+ d->deviceMediumUdiMap[du] = 0;
+ emit mediumChanged( d->udiDeviceMap[du] );
+ }
+ }
+ }
+ }
+}
+
+
+int K3bDevice::HalConnection::lock( Device* dev )
+{
+ //
+ // The code below is based on the code from kioslave/media/mediamanager/halbackend.cpp in the kdebase package
+ // Copyright (c) 2004-2005 Jérôme Lodewyck <jerome dot lodewyck at normalesup dot org>
+ //
+ DBusMessage* dmesg = 0;
+ DBusMessage* reply = 0;
+ DBusError error;
+
+ if( !d->deviceUdiMap.contains( dev->blockDeviceName() ) ) {
+ return org_freedesktop_Hal_Device_Volume_NoSuchDevice;
+ }
+
+ QCString udi = d->deviceUdiMap[dev->blockDeviceName()];
+
+ if( !( dmesg = dbus_message_new_method_call( "org.freedesktop.Hal", udi.data(),
+ "org.freedesktop.Hal.Device",
+ "Lock" ) ) ) {
+ k3bDebug() << "(K3bDevice::HalConnection) lock failed for " << udi << ": could not create dbus message\n";
+ return org_freedesktop_Hal_CommunicationError;
+ }
+
+ const char* lockComment = "Locked by the K3b libraries";
+
+ if( !dbus_message_append_args( dmesg,
+ DBUS_TYPE_STRING, &lockComment,
+ DBUS_TYPE_INVALID ) ) {
+ k3bDebug() << "(K3bDevice::HalConnection) lock failed for " << udi << ": could not append args to dbus message\n";
+ dbus_message_unref( dmesg );
+ return org_freedesktop_Hal_CommunicationError;
+ }
+
+ int ret = org_freedesktop_Hal_Success;
+
+ dbus_error_init( &error );
+ reply = dbus_connection_send_with_reply_and_block( d->connection, dmesg, -1, &error );
+ if( dbus_error_is_set( &error ) ) {
+ kdError() << "(K3bDevice::HalConnection) lock failed for " << udi << ": " << error.name << " - " << error.message << endl;
+ if( !strcmp(error.name, "org.freedesktop.Hal.NoSuchDevice" ) )
+ ret = org_freedesktop_Hal_NoSuchDevice;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.DeviceAlreadyLocked" ) )
+ ret = org_freedesktop_Hal_DeviceAlreadyLocked;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.PermissionDenied" ) )
+ ret = org_freedesktop_Hal_PermissionDenied;
+
+ dbus_error_free( &error );
+ }
+ else
+ k3bDebug() << "(K3bDevice::HalConnection) lock queued for " << udi << endl;
+
+ dbus_message_unref( dmesg );
+ if( reply )
+ dbus_message_unref( reply );
+
+ return ret;
+}
+
+
+int K3bDevice::HalConnection::unlock( Device* dev )
+{
+ //
+ // The code below is based on the code from kioslave/media/mediamanager/halbackend.cpp in the kdebase package
+ // Copyright (c) 2004-2005 Jérôme Lodewyck <jerome dot lodewyck at normalesup dot org>
+ //
+ DBusMessage* dmesg = 0;
+ DBusMessage* reply = 0;
+ DBusError error;
+
+ if( !d->deviceUdiMap.contains( dev->blockDeviceName() ) ) {
+ return org_freedesktop_Hal_Device_Volume_NoSuchDevice;
+ }
+
+ QCString udi = d->deviceUdiMap[dev->blockDeviceName()];
+
+ if( !( dmesg = dbus_message_new_method_call( "org.freedesktop.Hal", udi.data(),
+ "org.freedesktop.Hal.Device",
+ "Unlock" ) ) ) {
+ k3bDebug() << "(K3bDevice::HalConnection) unlock failed for " << udi << ": could not create dbus message\n";
+ return org_freedesktop_Hal_CommunicationError;
+ }
+
+ if( !dbus_message_append_args( dmesg,
+ DBUS_TYPE_INVALID ) ) {
+ k3bDebug() << "(K3bDevice::HalConnection) unlock failed for " << udi << ": could not append args to dbus message\n";
+ dbus_message_unref( dmesg );
+ return org_freedesktop_Hal_CommunicationError;
+ }
+
+ int ret = org_freedesktop_Hal_Success;
+
+ dbus_error_init( &error );
+ reply = dbus_connection_send_with_reply_and_block( d->connection, dmesg, -1, &error );
+ if( dbus_error_is_set( &error ) ) {
+ kdError() << "(K3bDevice::HalConnection) unlock failed for " << udi << ": " << error.name << " - " << error.message << endl;
+ if( !strcmp(error.name, "org.freedesktop.Hal.NoSuchDevice" ) )
+ ret = org_freedesktop_Hal_NoSuchDevice;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.DeviceAlreadyLocked" ) )
+ ret = org_freedesktop_Hal_DeviceAlreadyLocked;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.PermissionDenied" ) )
+ ret = org_freedesktop_Hal_PermissionDenied;
+
+ dbus_error_free( &error );
+ }
+ else
+ k3bDebug() << "(K3bDevice::HalConnection) unlock queued for " << udi << endl;
+
+ dbus_message_unref( dmesg );
+ if( reply )
+ dbus_message_unref( reply );
+
+ return ret;
+}
+
+
+int K3bDevice::HalConnection::mount( K3bDevice::Device* dev,
+ const QString& mountPoint,
+ const QString& fstype,
+ const QStringList& options )
+{
+ //
+ // The code below is based on the code from kioslave/media/mediamanager/halbackend.cpp in the kdebase package
+ // Copyright (c) 2004-2005 Jérôme Lodewyck <jerome dot lodewyck at normalesup dot org>
+ //
+ DBusMessage* dmesg = 0;
+ DBusMessage* reply = 0;
+ DBusError error;
+
+ if( !d->deviceUdiMap.contains( dev->blockDeviceName() ) )
+ return org_freedesktop_Hal_NoSuchDevice;
+
+ if( !d->deviceMediumUdiMap.contains( d->deviceUdiMap[dev->blockDeviceName()] ) )
+ return org_freedesktop_Hal_Device_Volume_NoSuchDevice;
+
+ QCString mediumUdi = d->deviceMediumUdiMap[d->deviceUdiMap[dev->blockDeviceName()]];
+
+ if( !( dmesg = dbus_message_new_method_call( "org.freedesktop.Hal", mediumUdi.data(),
+ "org.freedesktop.Hal.Device.Volume",
+ "Mount" ) ) ) {
+ k3bDebug() << "(K3bDevice::HalConnection) mount failed for " << mediumUdi << ": could not create dbus message\n";
+ return org_freedesktop_Hal_CommunicationError;
+ }
+
+ char** poptions = qstringListToArray( options );
+
+ QByteArray strMountPoint = mountPoint.local8Bit();
+ QByteArray strFstype = fstype.local8Bit();
+
+ if( !dbus_message_append_args( dmesg,
+ DBUS_TYPE_STRING, strMountPoint.data(),
+ DBUS_TYPE_STRING, strFstype.data(),
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &poptions, options.count(),
+ DBUS_TYPE_INVALID ) ) {
+ k3bDebug() << "(K3bDevice::HalConnection) mount failed for " << mediumUdi << ": could not append args to dbus message\n";
+ dbus_message_unref( dmesg );
+ freeArray( poptions, options.count() );
+ return org_freedesktop_Hal_CommunicationError;
+ }
+
+ freeArray( poptions, options.count() );
+
+ int ret = org_freedesktop_Hal_Success;
+
+ dbus_error_init( &error );
+ reply = dbus_connection_send_with_reply_and_block( d->connection, dmesg, -1, &error );
+ if( dbus_error_is_set( &error ) ) {
+ kdError() << "(K3bDevice::HalConnection) mount failed for " << mediumUdi << ": " << error.name << " - " << error.message << endl;
+ if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.NoSuchDevice" ) )
+ ret = org_freedesktop_Hal_Device_Volume_NoSuchDevice;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.PermissionDenied" ) )
+ ret = org_freedesktop_Hal_Device_Volume_PermissionDenied;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.UnknownFilesystemType" ) )
+ ret = org_freedesktop_Hal_Device_Volume_UnknownFilesystemType;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.MountPointNotAvailable" ) )
+ ret = org_freedesktop_Hal_Device_Volume_MountPointNotAvailable;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.AlreadyMounted" ) )
+ ret = org_freedesktop_Hal_Device_Volume_AlreadyMounted;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.InvalidMountpoint" ) )
+ ret = org_freedesktop_Hal_Device_Volume_InvalidMountpoint;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.InvalidMountOption" ) )
+ ret = org_freedesktop_Hal_Device_Volume_InvalidMountOption;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.PermissionDeniedByPolicy" ) )
+ ret = org_freedesktop_Hal_Device_Volume_PermissionDeniedByPolicy;
+
+ dbus_error_free( &error );
+ }
+ else
+ k3bDebug() << "(K3bDevice::HalConnection) mount queued for " << mediumUdi << endl;
+
+ dbus_message_unref( dmesg );
+ if( reply )
+ dbus_message_unref( reply );
+
+ return ret;
+}
+
+
+int K3bDevice::HalConnection::unmount( K3bDevice::Device* dev,
+ const QStringList& options )
+{
+ //
+ // The code below is based on the code from kioslave/media/mediamanager/halbackend.cpp in the kdebase package
+ // Copyright (c) 2004-2005 Jérôme Lodewyck <jerome dot lodewyck at normalesup dot org>
+ //
+ DBusMessage* dmesg = 0;
+ DBusMessage* reply = 0;
+ DBusError error;
+
+ if( !d->deviceUdiMap.contains( dev->blockDeviceName() ) )
+ return org_freedesktop_Hal_NoSuchDevice;
+
+ if( !d->deviceMediumUdiMap.contains( d->deviceUdiMap[dev->blockDeviceName()] ) )
+ return org_freedesktop_Hal_Device_Volume_NoSuchDevice;
+
+ QCString mediumUdi = d->deviceMediumUdiMap[d->deviceUdiMap[dev->blockDeviceName()]];
+
+ if( !( dmesg = dbus_message_new_method_call( "org.freedesktop.Hal", mediumUdi.data(),
+ "org.freedesktop.Hal.Device.Volume",
+ "Unmount" ) ) ) {
+ k3bDebug() << "(K3bDevice::HalConnection) unmount failed for " << mediumUdi << ": could not create dbus message\n";
+ return org_freedesktop_Hal_CommunicationError;
+ }
+
+ char** poptions = qstringListToArray( options );
+
+ if( !dbus_message_append_args( dmesg,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &poptions, options.count(),
+ DBUS_TYPE_INVALID ) ) {
+ k3bDebug() << "(K3bDevice::HalConnection) unmount failed for " << mediumUdi << ": could not append args to dbus message\n";
+ dbus_message_unref( dmesg );
+ freeArray( poptions, options.count() );
+ return org_freedesktop_Hal_CommunicationError;
+ }
+
+ freeArray( poptions, options.count() );
+
+ int ret = org_freedesktop_Hal_Success;
+
+ dbus_error_init( &error );
+ reply = dbus_connection_send_with_reply_and_block( d->connection, dmesg, -1, &error );
+ if( dbus_error_is_set( &error ) ) {
+ kdError() << "(K3bDevice::HalConnection) unmount failed for " << mediumUdi << ": " << error.name << " - " << error.message << endl;
+ if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.NoSuchDevice" ) )
+ ret = org_freedesktop_Hal_Device_Volume_NoSuchDevice;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.PermissionDenied" ) )
+ ret = org_freedesktop_Hal_Device_Volume_PermissionDenied;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.MountPointNotAvailable" ) )
+ ret = org_freedesktop_Hal_Device_Volume_MountPointNotAvailable;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.InvalidUnmountOption" ) )
+ ret = org_freedesktop_Hal_Device_Volume_InvalidUnmountOption;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.InvalidMountpoint" ) )
+ ret = org_freedesktop_Hal_Device_Volume_InvalidMountpoint;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.PermissionDeniedByPolicy" ) )
+ ret = org_freedesktop_Hal_Device_Volume_PermissionDeniedByPolicy;
+
+ dbus_error_free( &error );
+ }
+ else
+ k3bDebug() << "(K3bDevice::HalConnection) unmount queued for " << mediumUdi << endl;
+
+ dbus_message_unref( dmesg );
+ if( reply )
+ dbus_message_unref( reply );
+
+ return ret;
+}
+
+
+int K3bDevice::HalConnection::eject( K3bDevice::Device* dev,
+ const QStringList& options )
+{
+ //
+ // The code below is based on the code from kioslave/media/mediamanager/halbackend.cpp in the kdebase package
+ // Copyright (c) 2004-2005 Jérôme Lodewyck <jerome dot lodewyck at normalesup dot org>
+ //
+ DBusMessage* dmesg = 0;
+ DBusMessage* reply = 0;
+ DBusError error;
+
+ if( !d->deviceUdiMap.contains( dev->blockDeviceName() ) )
+ return org_freedesktop_Hal_NoSuchDevice;
+
+ if( !d->deviceMediumUdiMap.contains( d->deviceUdiMap[dev->blockDeviceName()] ) )
+ return org_freedesktop_Hal_Device_Volume_NoSuchDevice;
+
+ QCString mediumUdi = d->deviceMediumUdiMap[d->deviceUdiMap[dev->blockDeviceName()]];
+
+ if( !( dmesg = dbus_message_new_method_call( "org.freedesktop.Hal", mediumUdi.data(),
+ "org.freedesktop.Hal.Device.Volume",
+ "Eject" ) ) ) {
+ k3bDebug() << "(K3bDevice::HalConnection) eject failed for " << mediumUdi << ": could not create dbus message\n";
+ return org_freedesktop_Hal_CommunicationError;
+ }
+
+ char** poptions = qstringListToArray( options );
+
+ if( !dbus_message_append_args( dmesg,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &poptions, options.count(),
+ DBUS_TYPE_INVALID ) ) {
+ k3bDebug() << "(K3bDevice::HalConnection) eject failed for " << mediumUdi << ": could not append args to dbus message\n";
+ dbus_message_unref( dmesg );
+ freeArray( poptions, options.count() );
+ return org_freedesktop_Hal_CommunicationError;
+ }
+
+ freeArray( poptions, options.count() );
+
+ int ret = org_freedesktop_Hal_Success;
+
+ dbus_error_init( &error );
+ reply = dbus_connection_send_with_reply_and_block( d->connection, dmesg, -1, &error );
+ if( dbus_error_is_set( &error ) ) {
+ kdError() << "(K3bDevice::HalConnection) eject failed for " << mediumUdi << ": " << error.name << " - " << error.message << endl;
+ if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.NoSuchDevice" ) )
+ ret = org_freedesktop_Hal_Device_Volume_NoSuchDevice;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.PermissionDenied" ) )
+ ret = org_freedesktop_Hal_Device_Volume_PermissionDenied;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.InvalidEjectOption" ) )
+ ret = org_freedesktop_Hal_Device_Volume_InvalidEjectOption;
+ else if( !strcmp(error.name, "org.freedesktop.Hal.Device.Volume.PermissionDeniedByPolicy" ) )
+ ret = org_freedesktop_Hal_Device_Volume_PermissionDeniedByPolicy;
+
+ dbus_error_free( &error );
+ }
+ else
+ k3bDebug() << "(K3bDevice::HalConnection) eject queued for " << mediumUdi << endl;
+
+ dbus_message_unref( dmesg );
+ if( reply )
+ dbus_message_unref( reply );
+
+ return ret;
+}
+
+
+void K3bDevice::HalConnection::setupDBusQtConnection( DBusConnection* dbusConnection )
+{
+ d->dBusQtConnection = new DBusQt::Connection( this );
+ d->dBusQtConnection->dbus_connection_setup_with_qt_main( dbusConnection );
+}
+
+#include "k3bhalconnection.moc"
diff --git a/libk3bdevice/k3bhalconnection.h b/libk3bdevice/k3bhalconnection.h
new file mode 100644
index 0000000..583bbf2
--- /dev/null
+++ b/libk3bdevice/k3bhalconnection.h
@@ -0,0 +1,223 @@
+/*
+ *
+ * $Id: sourceheader,v 1.3 2005/01/19 13:03:46 trueg Exp $
+ * Copyright (C) 2005-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_HAL_CONNECTION_H_
+#define _K3B_HAL_CONNECTION_H_
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "k3bdevice_export.h"
+
+#include <qobject.h>
+#include <qmap.h>
+#include <qstringlist.h>
+
+class DBusConnection;
+
+
+namespace K3bDevice {
+
+ class Device;
+
+ /**
+ * This is a simple HAL/DBUS wrapper which creates QT signals whenever a new optical
+ * drive is plugged into the system or one is unplugged.
+ *
+ * The HalConnection class also handles media changes. Whenever a new medium is inserted
+ * into a drive or a medium is removed (i.e. ejected) a signal is emitted. This way it
+ * is easy to keep track of the inserted media.
+ *
+ * This class does not deal with K3b devices but with system device names
+ * such as /dev/cdrom. These device names can be used in DeviceManager::findDevice().
+ */
+ class LIBK3BDEVICE_EXPORT HalConnection : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ ~HalConnection();
+
+ /**
+ * Creates a new singleton HalConnection object or returns the already existing one.
+ * A newly created HalConnection will emit newDevice signals for all devices in the HAL
+ * manager. However, since one cannot be sure if this is the first time the HalConnection
+ * is created it is recommended to connect to the signals and query the list of current
+ * devices.
+ *
+ * \return An instance of the singleton HalConnection object.
+ */
+ static HalConnection* instance();
+
+ /**
+ * \return true if a connection to the HAL deamon could be established and
+ * communication has been set up.
+ */
+ bool isConnected() const;
+
+ /**
+ * \return a list of optical devices as reported by HAL.
+ */
+ QStringList devices() const;
+
+ /**
+ * \internal
+ */
+ void addDevice( const char* udi );
+
+ /**
+ * \internal
+ */
+ void removeDevice( const char* udi );
+
+ /**
+ * Error codes named as the HAL deamon raises them
+ */
+ enum ErrorCodes {
+ org_freedesktop_Hal_Success = 0, //*< The operation was successful. This code does not match any in HAL
+ org_freedesktop_Hal_CommunicationError, //*< DBus communication error. This code does not match any in HAL
+ org_freedesktop_Hal_NoSuchDevice,
+ org_freedesktop_Hal_DeviceAlreadyLocked,
+ org_freedesktop_Hal_PermissionDenied,
+ org_freedesktop_Hal_Device_Volume_NoSuchDevice,
+ org_freedesktop_Hal_Device_Volume_PermissionDenied,
+ org_freedesktop_Hal_Device_Volume_AlreadyMounted,
+ org_freedesktop_Hal_Device_Volume_InvalidMountOption,
+ org_freedesktop_Hal_Device_Volume_UnknownFilesystemType,
+ org_freedesktop_Hal_Device_Volume_InvalidMountpoint,
+ org_freedesktop_Hal_Device_Volume_MountPointNotAvailable,
+ org_freedesktop_Hal_Device_Volume_PermissionDeniedByPolicy,
+ org_freedesktop_Hal_Device_Volume_InvalidUnmountOption,
+ org_freedesktop_Hal_Device_Volume_InvalidEjectOption
+ };
+
+ public slots:
+ /**
+ * Lock the device in HAL
+ *
+ * Be aware that once the method returns the HAL deamon has not necessarily
+ * finished the procedure yet.
+ *
+ * \param dev The device to lock
+ * \return An error code
+ *
+ * \see ErrorCode
+ */
+ int lock( Device* );
+
+ /**
+ * Unlock a previously locked device in HAL
+ *
+ * Be aware that once the method returns the HAL deamon has not necessarily
+ * finished the procedure yet.
+ *
+ * \param dev The device to lock
+ * \return An error code
+ *
+ * \see ErrorCode
+ */
+ int unlock( Device* );
+
+ /**
+ * Mounts a device via HAL
+ *
+ * Be aware that once the method returns the HAL deamon has not necessarily
+ * finished the procedure yet.
+ *
+ * \param dev The device to lock
+ * \return An error code
+ *
+ * \see ErrorCode
+ */
+ int mount( Device*,
+ const QString& mountPoint = QString::null,
+ const QString& fstype = QString::null,
+ const QStringList& options = QStringList() );
+
+ /**
+ * Unmounts a device via HAL
+ *
+ * Be aware that once the method returns the HAL deamon has not necessarily
+ * finished the procedure yet.
+ *
+ * \param dev The device to lock
+ * \return An error code
+ *
+ * \see ErrorCode
+ */
+ int unmount( Device*,
+ const QStringList& options = QStringList() );
+
+ /**
+ * Unmounts a device via HAL
+ *
+ * Be aware that once the method returns the HAL deamon has not necessarily
+ * finished the procedure yet.
+ *
+ * \param dev The device to lock
+ * \return An error code
+ *
+ * \see ErrorCode
+ */
+ int eject( Device*,
+ const QStringList& options = QStringList() );
+
+ signals:
+ /**
+ * This signal gets emitted whenever HAL finds a new optical drive.
+ *
+ * \param dev The block device name of the new drive.
+ */
+ void deviceAdded( const QString& dev );
+
+ /**
+ * This signal gets emitted whenever HAL detects that an optical drive
+ * has been unplugged.
+ *
+ * \param dev The block device name of the drive.
+ */
+ void deviceRemoved( const QString& dev );
+
+ /**
+ * This signal gets emitted whenever a new medium is inserted into a
+ * device or an inserted is removed (i.e. ejected)
+ *
+ * \param dev The block device name of the drive the medium is or was inserted into.
+ */
+ void mediumChanged( const QString& dev );
+
+ private:
+ /**
+ * HalConnection is a signelton class. Use the instance() method to create it.
+ */
+ HalConnection( QObject* parent = 0, const char* name = 0 );
+
+ /**
+ * Tries to open a connection to HAL.
+ */
+ bool open();
+ void close();
+
+ static HalConnection* s_instance;
+
+ class Private;
+ Private* d;
+
+ void setupDBusQtConnection( DBusConnection* dbusConnection );
+ };
+}
+
+#endif
diff --git a/libk3bdevice/k3bmmc.h b/libk3bdevice/k3bmmc.h
new file mode 100644
index 0000000..ebe2171
--- /dev/null
+++ b/libk3bdevice/k3bmmc.h
@@ -0,0 +1,697 @@
+/*
+ *
+ * $Id: k3bmmc.h 630384 2007-02-05 09:33:17Z mlaurent $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_MMC_H_
+#define _K3B_MMC_H_
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+namespace K3bDevice
+{
+ /*
+ * struct disc_info taken from cdrwtool.h
+ *
+ *
+ * disc status (status):
+ * 00b - empty disc
+ * 01b - incomplete disc (appendable)
+ * 10b - Complete disc
+ * 11b - Others
+ *
+ * State of last session (border)
+ * 00b - Empty session
+ * 01b - Incomplete session
+ * 10b - Reseverd
+ * 11b - Complete session (only possible when disc status is complete)
+ */
+
+ typedef struct disc_info {
+ Q_UINT16 length;
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char reserved1 : 3;
+ unsigned char erasable : 1;
+ unsigned char border : 2;
+ unsigned char status : 2;
+#else
+ unsigned char status : 2;
+ unsigned char border : 2;
+ unsigned char erasable : 1;
+ unsigned char reserved1 : 3;
+#endif
+ unsigned char n_first_track;
+ unsigned char n_sessions_l;
+ unsigned char first_track_l;
+ unsigned char last_track_l;
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char did_v : 1;
+ unsigned char dbc_v : 1;
+ unsigned char uru : 1;
+ unsigned char reserved2 : 2;
+ unsigned char dbit : 1;
+ unsigned char bg_f_status : 1;
+#else
+ unsigned char bg_f_status : 1;
+ unsigned char dbit : 1;
+ unsigned char reserved2 : 2;
+ unsigned char uru : 1;
+ unsigned char dbc_v : 1;
+ unsigned char did_v : 1;
+#endif
+
+ /*
+ * disc type
+ * 00h - CD-DA of CDROM
+ * 10h - CD-I
+ * 20h - CD-ROM XA
+ * FFh - Undefined
+ * All other values are reserved
+ */
+ unsigned char disc_type;
+ unsigned char n_sessions_m;
+ unsigned char first_track_m;
+ unsigned char last_track_m;
+ Q_UINT32 disc_id;
+
+ /*
+ * Last session lead-in start time
+ * if the disc is complete this shall be FF/FF/FF
+ */
+ unsigned char lead_in_r;
+ unsigned char lead_in_m;
+ unsigned char lead_in_s;
+ unsigned char lead_in_f;
+
+ /*
+ * Last possible start time for start of lead-in
+ * if the disc is complete this shall be FF/FF/FF
+ */
+ unsigned char lead_out_r;
+ unsigned char lead_out_m;
+ unsigned char lead_out_s;
+ unsigned char lead_out_f;
+
+ unsigned char disc_bar_code[8];
+
+ //
+ // We need to make sure the structure has a proper size
+ // I think it needs to be a power of 2.
+ // With ide-scsi there is no problem. But without the
+ // GPCMD_READ_DISC_INFO command failes if the size is 34
+ //
+
+/* unsigned char reserved3; */
+/* unsigned char opc_entries; */
+ } disc_info_t;
+
+
+
+ /*
+ * struct track_info taken from cdrwtool.h
+ */
+ typedef struct track_info {
+ unsigned char data_length[2];
+ unsigned char track_number_l;
+ unsigned char session_number_l;
+ unsigned char reserved1;
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char reserved2 : 2;
+ unsigned char damage : 1;
+ unsigned char copy : 1;
+ unsigned char track_mode : 4;
+ unsigned char rt : 1;
+ unsigned char blank : 1;
+ unsigned char packet : 1;
+ unsigned char fp : 1;
+ unsigned char data_mode : 4;
+ unsigned char reserved3 : 6;
+ unsigned char lra_v : 1;
+ unsigned char nwa_v : 1;
+#else
+ unsigned char track_mode : 4;
+ unsigned char copy : 1;
+ unsigned char damage : 1;
+ unsigned char reserved2 : 2;
+ unsigned char data_mode : 4;
+ unsigned char fp : 1;
+ unsigned char packet : 1;
+ unsigned char blank : 1;
+ unsigned char rt : 1;
+ unsigned char nwa_v : 1;
+ unsigned char lra_v : 1;
+ unsigned char reserved3 : 6;
+#endif
+ unsigned char track_start[4];
+ unsigned char next_writable[4];
+ unsigned char free_blocks[4];
+ unsigned char packet_size[4];
+ unsigned char track_size[4];
+ unsigned char last_recorded[4];
+ unsigned char track_number_m;
+ unsigned char session_number_m;
+ unsigned char reserved4;
+ unsigned char reserved5;
+ unsigned char read_compatibility[4];
+ } track_info_t;
+
+
+ /*
+ * Use this with the GPCMD_READ_TOC_PMA_ATIP command
+ * where format is set to 2 (Full TOC)
+ */
+ struct toc_raw_track_descriptor {
+ unsigned char session_number;
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char adr : 4;
+ unsigned char control : 4;
+#else
+ unsigned char control : 4;
+ unsigned char adr : 4;
+#endif
+ unsigned char tno;
+ unsigned char point;
+ unsigned char min;
+ unsigned char sec;
+ unsigned char frame;
+ unsigned char zero;
+ unsigned char p_min;
+ unsigned char p_sec;
+ unsigned char p_frame;
+ };
+
+
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ struct cd_wr_speed_performance {
+ unsigned char res0; /* Reserved */
+ unsigned char res_1_27 : 6; /* Reserved */
+ unsigned char rot_ctl_sel : 2; /* Rotational control selected */
+ unsigned char wr_speed_supp[2]; /* Supported write speed */
+ };
+#else
+ struct cd_wr_speed_performance {
+ unsigned char res0; /* Reserved */
+ unsigned char rot_ctl_sel : 2; /* Rotational control selected */
+ unsigned char res_1_27 : 6; /* Reserved */
+ unsigned char wr_speed_supp[2]; /* Supported write speed */
+ };
+#endif
+
+
+ /**
+ * Based on the cdrecord struct cd_mode_page_2A
+ * MM Capabilities and Mechanical Status Page
+ */
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+
+ struct mm_cap_page_2A {
+ unsigned char PS : 1;
+ unsigned char res_1 : 1;
+ unsigned char page_code : 6;
+ unsigned char page_len; /* 0x14 = 20 Bytes (MMC) */
+ /* 0x18 = 24 Bytes (MMC-2) */
+ /* 0x1C >= 28 Bytes (MMC-3) */
+ unsigned char res_2_67 : 2; /* Reserved */
+ unsigned char dvd_ram_read : 1; /* Reads DVD-RAM media */
+ unsigned char dvd_r_read : 1; /* Reads DVD-R media */
+ unsigned char dvd_rom_read : 1; /* Reads DVD ROM media */
+ unsigned char method2 : 1; /* Reads fixed packet method2 media */
+ unsigned char cd_rw_read : 1; /* Reads CD-RW media */
+ unsigned char cd_r_read : 1; /* Reads CD-R media */
+ unsigned char res_3_67 : 2; /* Reserved */
+ unsigned char dvd_ram_write : 1; /* Supports writing DVD-RAM media */
+ unsigned char dvd_r_write : 1; /* Supports writing DVD-R media */
+ unsigned char res_3_3 : 1; /* Reserved */
+ unsigned char test_write : 1; /* Supports emulation write */
+ unsigned char cd_rw_write : 1; /* Supports writing CD-RW media */
+ unsigned char cd_r_write : 1; /* Supports writing CD-R media */
+ unsigned char BUF : 1; /* Supports Buffer under. free rec. */
+ unsigned char multi_session : 1; /* Reads multi-session media */
+ unsigned char mode_2_form_2 : 1; /* Reads Mode-2 form 2 media */
+ unsigned char mode_2_form_1 : 1; /* Reads Mode-2 form 1 media (XA) */
+ unsigned char digital_port_1 : 1; /* Supports digital output on port 1 */
+ unsigned char digital_port_2 : 1; /* Supports digital output on port 2 */
+ unsigned char composite : 1; /* Deliveres composite A/V stream */
+ unsigned char audio_play : 1; /* Supports Audio play operation */
+ unsigned char read_bar_code : 1; /* Supports reading bar codes */
+ unsigned char UPC : 1; /* Reads media catalog number (UPC) */
+ unsigned char ISRC : 1; /* Reads ISRC information */
+ unsigned char c2_pointers : 1; /* Supports C2 error pointers */
+ unsigned char rw_deint_corr : 1; /* Reads de-interleved R-W sub chan */
+ unsigned char rw_supported : 1; /* Reads R-W sub channel information */
+ unsigned char cd_da_accurate : 1; /* READ CD data stream is accurate */
+ unsigned char cd_da_supported : 1; /* Reads audio data with READ CD cmd */
+ unsigned char loading_type : 3; /* Loading mechanism type */
+ unsigned char res_6_4 : 1; /* Reserved */
+ unsigned char eject : 1; /* Ejects disc/cartr with STOP LoEj */
+ unsigned char prevent_jumper : 1; /* State of prev/allow jumper 0=pres */
+ unsigned char lock_state : 1; /* Lock state 0=unlocked 1=locked */
+ unsigned char lock : 1; /* PREVENT/ALLOW may lock media */
+ unsigned char res_7 : 2; /* Reserved */
+ unsigned char rw_in_lead_in : 1; /* Reads raw R-W subcode from lead in */
+ unsigned char side_change : 1; /* Side change capable */
+ unsigned char sw_slot_sel : 1; /* Load empty slot in changer */
+ unsigned char disk_present_rep : 1; /* Changer supports disk present rep */
+ unsigned char sep_chan_mute : 1; /* Mute controls each channel separat*/
+ unsigned char sep_chan_vol : 1; /* Vol controls each channel separat */
+ unsigned char max_read_speed[2]; /* Max. read speed in KB/s */
+ /* obsolete in MMC-4 */
+ unsigned char num_vol_levels[2]; /* # of supported volume levels */
+ unsigned char buffer_size[2]; /* Buffer size for the data in KB */
+ unsigned char cur_read_speed[2]; /* Current read speed in KB/s */
+ /* obsolete in MMC-4 */
+ unsigned char res_16; /* Reserved */
+ unsigned char res_17 : 2; /* Reserved */
+ unsigned char length : 2; /* 0=32BCKs 1=16BCKs 2=24BCKs 3=24I2c*/
+ unsigned char LSBF : 1; /* Set: LSB first Clear: MSB first */
+ unsigned char RCK : 1; /* Set: HIGH high LRCK=left channel */
+ unsigned char BCK : 1; /* Data valid on falling edge of BCK */
+ unsigned char res_17_0 : 1; /* Reserved */
+ unsigned char max_write_speed[2]; /* Max. write speed supported in KB/s*/
+ /* obsolete in MMC-4 */
+ unsigned char cur_write_speed[2]; /* Current write speed in KB/s */
+ /* obsolete in MMC-4 */
+
+ /* Byte 22 ... Only in MMC-2 */
+ unsigned char copy_man_rev[2]; /* Copy management revision supported*/
+ unsigned char res_24; /* Reserved */
+ unsigned char res_25; /* Reserved */
+
+ /* Byte 26 ... Only in MMC-3 */
+ unsigned char res_26; /* Reserved */
+ unsigned char res_27_27 : 6; /* Reserved */
+ unsigned char rot_ctl_sel : 2; /* Rotational control selected */
+ unsigned char v3_cur_write_speed[2]; /* Current write speed in KB/s */
+ unsigned char num_wr_speed_des[2]; /* # of wr speed perf descr. tables */
+ struct cd_wr_speed_performance
+ wr_speed_des[1]; /* wr speed performance descriptor */
+ /* Actually more (num_wr_speed_des) */
+ };
+
+#else // LITTLE_ENDIAN
+
+ struct mm_cap_page_2A {
+ unsigned char page_code : 6;
+ unsigned char res_1 : 1;
+ unsigned char PS : 1;
+ unsigned char page_len; /* 0x14 = 20 Bytes (MMC) */
+ /* 0x18 = 24 Bytes (MMC-2) */
+ /* 0x1C >= 28 Bytes (MMC-3) */
+ unsigned char cd_r_read : 1; /* Reads CD-R media */
+ unsigned char cd_rw_read : 1; /* Reads CD-RW media */
+ unsigned char method2 : 1; /* Reads fixed packet method2 media */
+ unsigned char dvd_rom_read : 1; /* Reads DVD ROM media */
+ unsigned char dvd_r_read : 1; /* Reads DVD-R media */
+ unsigned char dvd_ram_read : 1; /* Reads DVD-RAM media */
+ unsigned char res_2_67 : 2; /* Reserved */
+ unsigned char cd_r_write : 1; /* Supports writing CD-R media */
+ unsigned char cd_rw_write : 1; /* Supports writing CD-RW media */
+ unsigned char test_write : 1; /* Supports emulation write */
+ unsigned char res_3_3 : 1; /* Reserved */
+ unsigned char dvd_r_write : 1; /* Supports writing DVD-R media */
+ unsigned char dvd_ram_write : 1; /* Supports writing DVD-RAM media */
+ unsigned char res_3_67 : 2; /* Reserved */
+ unsigned char audio_play : 1; /* Supports Audio play operation */
+ unsigned char composite : 1; /* Deliveres composite A/V stream */
+ unsigned char digital_port_2 : 1; /* Supports digital output on port 2 */
+ unsigned char digital_port_1 : 1; /* Supports digital output on port 1 */
+ unsigned char mode_2_form_1 : 1; /* Reads Mode-2 form 1 media (XA) */
+ unsigned char mode_2_form_2 : 1; /* Reads Mode-2 form 2 media */
+ unsigned char multi_session : 1; /* Reads multi-session media */
+ unsigned char BUF : 1; /* Supports Buffer under. free rec. */
+ unsigned char cd_da_supported : 1; /* Reads audio data with READ CD cmd */
+ unsigned char cd_da_accurate : 1; /* READ CD data stream is accurate */
+ unsigned char rw_supported : 1; /* Reads R-W sub channel information */
+ unsigned char rw_deint_corr : 1; /* Reads de-interleved R-W sub chan */
+ unsigned char c2_pointers : 1; /* Supports C2 error pointers */
+ unsigned char ISRC : 1; /* Reads ISRC information */
+ unsigned char UPC : 1; /* Reads media catalog number (UPC) */
+ unsigned char read_bar_code : 1; /* Supports reading bar codes */
+ unsigned char lock : 1; /* PREVENT/ALLOW may lock media */
+ unsigned char lock_state : 1; /* Lock state 0=unlocked 1=locked */
+ unsigned char prevent_jumper : 1; /* State of prev/allow jumper 0=pres */
+ unsigned char eject : 1; /* Ejects disc/cartr with STOP LoEj */
+ unsigned char res_6_4 : 1; /* Reserved */
+ unsigned char loading_type : 3; /* Loading mechanism type */
+ unsigned char sep_chan_vol : 1; /* Vol controls each channel separat */
+ unsigned char sep_chan_mute : 1; /* Mute controls each channel separat*/
+ unsigned char disk_present_rep : 1; /* Changer supports disk present rep */
+ unsigned char sw_slot_sel : 1; /* Load empty slot in changer */
+ unsigned char side_change : 1; /* Side change capable */
+ unsigned char rw_in_lead_in : 1; /* Reads raw R-W subcode from lead in */
+ unsigned char res_7 : 2; /* Reserved */
+ unsigned char max_read_speed[2]; /* Max. read speed in KB/s */
+ /* obsolete in MMC-4 */
+ unsigned char num_vol_levels[2]; /* # of supported volume levels */
+ unsigned char buffer_size[2]; /* Buffer size for the data in KB */
+ unsigned char cur_read_speed[2]; /* Current read speed in KB/s */
+ /* obsolete in MMC-4 */
+ unsigned char res_16; /* Reserved */
+ unsigned char res_17_0 : 1; /* Reserved */
+ unsigned char BCK : 1; /* Data valid on falling edge of BCK */
+ unsigned char RCK : 1; /* Set: HIGH high LRCK=left channel */
+ unsigned char LSBF : 1; /* Set: LSB first Clear: MSB first */
+ unsigned char length : 2; /* 0=32BCKs 1=16BCKs 2=24BCKs 3=24I2c*/
+ unsigned char res_17 : 2; /* Reserved */
+ unsigned char max_write_speed[2]; /* Max. write speed supported in KB/s*/
+ /* obsolete in MMC-4 */
+ unsigned char cur_write_speed[2]; /* Current write speed in KB/s */
+ /* obsolete in MMC-4 */
+
+ /* Byte 22 ... Only in MMC-2 */
+ unsigned char copy_man_rev[2]; /* Copy management revision supported*/
+ unsigned char res_24; /* Reserved */
+ unsigned char res_25; /* Reserved */
+
+ /* Byte 26 ... Only in MMC-3 */
+ unsigned char res_26; /* Reserved */
+ unsigned char rot_ctl_sel : 2; /* Rotational control selected */
+ unsigned char res_27_27 : 6; /* Reserved */
+ unsigned char v3_cur_write_speed[2]; /* Current write speed in KB/s */
+ unsigned char num_wr_speed_des[2]; /* # of wr speed perf descr. tables */
+ struct cd_wr_speed_performance
+ wr_speed_des[1]; /* wr speed performance descriptor */
+ /* Actually more (num_wr_speed_des) */
+ };
+#endif
+
+ /**
+ * Based on the cdrecord struct cd_mode_page_05
+ * Write Parameters Mode Page
+ */
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ struct wr_param_page_05 { /* write parameters */
+ unsigned char PS : 1;
+ unsigned char res_1 : 1;
+ unsigned char page_code : 6;
+ unsigned char page_len; /* 0x32 = 50 Bytes */
+ unsigned char res_2_7 : 1;
+ unsigned char BUFE : 1; /* Enable Bufunderrun free rec. */
+ unsigned char LS_V : 1; /* Link size valid */
+ unsigned char test_write : 1; /* Do not actually write data */
+ unsigned char write_type : 4; /* Session write type (PACKET/TAO...)*/
+ unsigned char multi_session : 2; /* Multi session write type */
+ unsigned char fp : 1; /* Fixed packed (if in packet mode) */
+ unsigned char copy : 1; /* 1st higher gen of copy prot track */
+ unsigned char track_mode : 4; /* Track mode (Q-sub control nibble) */
+ unsigned char res_4 : 4; /* Reserved */
+ unsigned char dbtype : 4; /* Data block type */
+ unsigned char link_size; /* Link Size (default is 7) */
+ unsigned char res_6; /* Reserved */
+ unsigned char res_7 : 2; /* Reserved */
+ unsigned char host_appl_code : 6; /* Host application code of disk */
+ unsigned char session_format; /* Session format (DA/CDI/XA) */
+ unsigned char res_9; /* Reserved */
+ unsigned char packet_size[4]; /* # of user datablocks/fixed packet */
+ unsigned char audio_pause_len[2]; /* # of blocks where index is zero */
+ unsigned char media_cat_number[16]; /* Media catalog Number (MCN) */
+ unsigned char ISRC[14]; /* ISRC for this track */
+ unsigned char sub_header[4];
+ unsigned char vendor_uniq[4];
+ };
+
+#else // __LITTLE_ENDIAN
+ struct wr_param_page_05 { /* write parameters */
+ unsigned char page_code : 6;
+ unsigned char res_1 : 1;
+ unsigned char PS : 1;
+ unsigned char p_len; /* 0x32 = 50 Bytes */
+ unsigned char write_type : 4; /* Session write type (PACKET/TAO...)*/
+ unsigned char test_write : 1; /* Do not actually write data */
+ unsigned char LS_V : 1; /* Link size valid */
+ unsigned char BUFE : 1; /* Enable Bufunderrun free rec. */
+ unsigned char res_2_7 : 1;
+ unsigned char track_mode : 4; /* Track mode (Q-sub control nibble) */
+ unsigned char copy : 1; /* 1st higher gen of copy prot track ~*/
+ unsigned char fp : 1; /* Fixed packed (if in packet mode) */
+ unsigned char multi_session : 2; /* Multi session write type */
+ unsigned char dbtype : 4; /* Data block type */
+ unsigned char res_4 : 4; /* Reserved */
+ unsigned char link_size; /* Link Size (default is 7) */
+ unsigned char res_6; /* Reserved */
+ unsigned char host_appl_code : 6; /* Host application code of disk */
+ unsigned char res_7 : 2; /* Reserved */
+ unsigned char session_format; /* Session format (DA/CDI/XA) */
+ unsigned char res_9; /* Reserved */
+ unsigned char packet_size[4]; /* # of user datablocks/fixed packet */
+ unsigned char audio_pause_len[2]; /* # of blocks where index is zero */
+ unsigned char media_cat_number[16]; /* Media catalog Number (MCN) */
+ unsigned char ISRC[14]; /* ISRC for this track */
+ unsigned char sub_header[4];
+ unsigned char vendor_uniq[4];
+ };
+#endif
+
+
+ struct toc_track_descriptor {
+ unsigned char res1;
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char adr : 4;
+ unsigned char control : 4;
+#else
+ unsigned char control : 4;
+ unsigned char adr : 4;
+#endif
+ unsigned char track_no;
+ unsigned char res2;
+ unsigned char start_adr[4];
+ };
+
+
+ struct atip_descriptor {
+ unsigned char dataLength[2];
+ unsigned char res1;
+ unsigned char res2;
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char ind_wr_power : 4; // indicated writing power
+ unsigned char ddcd : 1; // DDCD
+ unsigned char ref_speed : 3; // reference Speed
+ unsigned char zero : 1; // 0
+ unsigned char uru : 1; // Uru
+ unsigned char res3 : 6;
+ unsigned char one : 1; // 1
+ unsigned char disc_type : 1; // Disc Type
+ unsigned char disc_subtype : 3; // Disc Sub-Type
+ unsigned char a1_valid : 1;
+ unsigned char a2_valid : 1;
+ unsigned char a3_valid : 1;
+#else
+ unsigned char ref_speed : 3; // reference Speed
+ unsigned char ddcd : 1; // DDCD
+ unsigned char ind_wr_power : 4; // indicated writing power
+ unsigned char res3 : 6;
+ unsigned char uru : 1; // Uru
+ unsigned char zero : 1; // 0
+ unsigned char a3_valid : 1;
+ unsigned char a2_valid : 1;
+ unsigned char a1_valid : 1;
+ unsigned char disc_subtype : 3; // Disc Sub-Type
+ unsigned char disc_type : 1; // Disc Type
+ unsigned char one : 1; // 1
+#endif
+ unsigned char res4;
+ unsigned char lead_in_m;
+ unsigned char lead_in_s;
+ unsigned char lead_in_f;
+ unsigned char res5;
+ unsigned char lead_out_m;
+ unsigned char lead_out_s;
+ unsigned char lead_out_f;
+ unsigned char res6;
+ unsigned char a1[3];
+ unsigned char res7;
+ unsigned char a2[3];
+ unsigned char res8;
+ unsigned char a3[3];
+ unsigned char res9;
+ unsigned char s4[3];
+ unsigned char res10;
+ };
+
+
+ struct mechanism_status_header {
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char fault : 1;
+ unsigned char changer_state : 2;
+ unsigned char slot_low : 5;
+#else
+ unsigned char slot_low : 5;
+ unsigned char changer_state : 2;
+ unsigned char fault : 1;
+#endif
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char mech_state : 3;
+ unsigned char door_open : 1;
+ unsigned char res1 : 1;
+ unsigned char slot_high : 3;
+#else
+ unsigned char slot_high : 3;
+ unsigned char res1 : 1;
+ unsigned char door_open : 1;
+ unsigned char mech_state : 3;
+#endif
+ unsigned char current_lba[3];
+ unsigned char num_slots;
+ unsigned char slot_len[2];
+ };
+
+ struct mechanism_status_slot {
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char disc_present : 1;
+ unsigned char res1 : 6;
+ unsigned char change : 1;
+#else
+ unsigned char change : 1;
+ unsigned char res1 : 6;
+ unsigned char disc_present : 1;
+#endif
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char res2 : 6;
+ unsigned char cwp_v : 1;
+ unsigned char cwp : 1;
+#else
+ unsigned char cwp : 1;
+ unsigned char cwp_v : 1;
+ unsigned char res2 : 6;
+#endif
+ unsigned char res3;
+ unsigned char res4;
+ };
+
+
+ struct inquiry {
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char p_qualifier : 3;
+ unsigned char p_device_type : 5;
+ unsigned char rmb : 1;
+ unsigned char reserved1 : 7;
+#else
+ unsigned char p_device_type : 5;
+ unsigned char p_qualifier : 3;
+ unsigned char reserved1 : 7;
+ unsigned char rmb : 1;
+#endif
+ unsigned char version;
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char interface_dep : 4;
+ unsigned char data_format : 4;
+#else
+ unsigned char data_format : 4;
+ unsigned char interface_dep : 4;
+#endif
+ unsigned char add_length;
+ unsigned char reserved2;
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char bque : 1;
+ unsigned char enc_serv : 1;
+ unsigned char vs1 : 1;
+ unsigned char multi_p : 1;
+ unsigned char m_chngr : 1;
+ unsigned char reserved3 : 1;
+ unsigned char reserved4 : 1;
+ unsigned char addr_16 : 1;
+ unsigned char rel_adr : 1;
+ unsigned char reserved5 : 1;
+ unsigned char w_bus_16 : 1;
+ unsigned char sync : 1;
+ unsigned char linked : 1;
+ unsigned char reserved6 : 1;
+ unsigned char cmd_que : 1;
+ unsigned char vs2 : 1;
+#else
+ unsigned char addr_16 : 1;
+ unsigned char reserved4 : 1;
+ unsigned char reserved3 : 1;
+ unsigned char m_chngr : 1;
+ unsigned char multi_p : 1;
+ unsigned char vs1 : 1;
+ unsigned char enc_serv : 1;
+ unsigned char bque : 1;
+ unsigned char vs2 : 1;
+ unsigned char cmd_que : 1;
+ unsigned char reserved6 : 1;
+ unsigned char linked : 1;
+ unsigned char sync : 1;
+ unsigned char w_bus_16 : 1;
+ unsigned char reserved5 : 1;
+ unsigned char rel_adr : 1;
+#endif
+ unsigned char vendor[8];
+ unsigned char product[16];
+ unsigned char revision[4];
+ unsigned char vendor_specific[20];
+ unsigned char reserved7[2];
+ unsigned char version1[2];
+ unsigned char version2[2];
+ unsigned char version3[2];
+ unsigned char version4[2];
+ unsigned char version5[2];
+ unsigned char version6[2];
+ unsigned char version7[2];
+ unsigned char version8[2];
+
+ // bytes 74-95: reserved
+ // bytes 96-n: vendor specific
+ };
+
+
+ struct ricoh_mode_page_30 {
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char PS : 1;
+ unsigned char res_1 : 1;
+ unsigned char page_code : 6;
+#else
+ unsigned char page_code : 6;
+ unsigned char res_1 : 1;
+ unsigned char PS : 1;
+#endif
+ unsigned char page_len; /* 0xE = 14 Bytes */
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char res_2_67 :2;
+ unsigned char AWSCS :1; /* Auto write speed control supp. */
+ unsigned char ARSCS :1; /* Auto read speed control supp. */
+ unsigned char res_2_23 :2;
+ unsigned char TWBFS :1; /* Test Burn-Free sup. */
+ unsigned char BUEFS :1; /* Burn-Free supported */
+#else
+ unsigned char BUEFS :1; /* Burn-Free supported */
+ unsigned char TWBFS :1; /* Test Burn-Free sup. */
+ unsigned char res_2_23 :2;
+ unsigned char ARSCS :1; /* Auto read speed control supp. */
+ unsigned char AWSCS :1; /* Auto write speed control supp. */
+ unsigned char res_2_67 :2;
+#endif
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char res_3_67 :2;
+ unsigned char AWSCD :1; /* Auto write speed control disabled */
+ unsigned char ARSCE :1; /* Auto read speed control enabled */
+ unsigned char res_2_13 :3;
+ unsigned char BUEFE :1; /* Burn-Free enabled */
+#else
+ unsigned char BUEFE :1; /* Burn-Free enabled */
+ unsigned char res_2_13 :3;
+ unsigned char ARSCE :1; /* Auto read speed control enabled */
+ unsigned char AWSCD :1; /* Auto write speed control disabled */
+ unsigned char res_3_67 :2;
+#endif
+ unsigned char link_counter[2]; /* Burn-Free link counter */
+ unsigned char res[10]; /* Padding up to 16 bytes */
+ };
+}
+
+
+#endif
diff --git a/libk3bdevice/k3bmsf.cpp b/libk3bdevice/k3bmsf.cpp
new file mode 100644
index 0000000..c7257a1
--- /dev/null
+++ b/libk3bdevice/k3bmsf.cpp
@@ -0,0 +1,335 @@
+/*
+ *
+ * $Id: k3bmsf.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bmsf.h"
+#include <qregexp.h>
+
+#include <math.h>
+
+
+K3b::Msf::Msf()
+ : m_minutes(0),
+ m_seconds(0),
+ m_frames(0)
+{
+}
+
+K3b::Msf::Msf( const K3b::Msf& m )
+ : m_minutes(m.minutes()),
+ m_seconds(m.seconds()),
+ m_frames(m.frames())
+{
+}
+
+K3b::Msf::Msf( int m, int s, int f )
+ : m_minutes(m),
+ m_seconds(s),
+ m_frames(f)
+{
+ makeValid();
+}
+
+K3b::Msf::Msf( int i )
+ : m_minutes(0),
+ m_seconds(0),
+ m_frames(i)
+{
+ makeValid();
+}
+
+
+void K3b::Msf::setValue( int m, int s, int f )
+{
+ m_minutes = m;
+ m_seconds = s;
+ m_frames = f;
+ makeValid();
+}
+
+
+void K3b::Msf::addMinutes( int m )
+{
+ m_minutes += m;
+ makeValid();
+}
+
+void K3b::Msf::addSeconds( int s )
+{
+ m_seconds += s;
+ makeValid();
+}
+
+void K3b::Msf::addFrames( int f )
+{
+ m_frames += f;
+ makeValid();
+}
+
+K3b::Msf& K3b::Msf::operator=( const K3b::Msf& m )
+{
+ m_frames = m.frames();
+ m_seconds = m.seconds();
+ m_minutes = m.minutes();
+ return *this;
+}
+
+K3b::Msf& K3b::Msf::operator=( int i )
+{
+ m_frames = i;
+ m_seconds = 0;
+ m_minutes = 0;
+ makeValid();
+ return *this;
+}
+
+K3b::Msf& K3b::Msf::operator+=( const K3b::Msf& m )
+{
+ m_frames += m.frames();
+ m_seconds += m.seconds();
+ m_minutes += m.minutes();
+ makeValid();
+ return *this;
+}
+
+K3b::Msf& K3b::Msf::operator+=( int i )
+{
+ addFrames(i);
+ return *this;
+}
+
+K3b::Msf& K3b::Msf::operator-=( const K3b::Msf& m )
+{
+ m_frames -= m.frames();
+ m_seconds -= m.seconds();
+ m_minutes -= m.minutes();
+ makeValid();
+ return *this;
+}
+
+K3b::Msf& K3b::Msf::operator-=( int i )
+{
+ m_frames -= i;
+ makeValid();
+ return *this;
+}
+
+
+const K3b::Msf K3b::Msf::operator++( int )
+{
+ Msf old = *this;
+ ++(*this);
+ return old;
+}
+
+
+K3b::Msf& K3b::Msf::operator++()
+{
+ (*this) += 1;
+ return *this;
+}
+
+
+const K3b::Msf K3b::Msf::operator--( int )
+{
+ Msf old = *this;
+ --(*this);
+ return old;
+}
+
+
+K3b::Msf& K3b::Msf::operator--()
+{
+ (*this) -= 1;
+ return *this;
+}
+
+
+QString K3b::Msf::toString( bool showFrames ) const
+{
+ QString str;
+
+ if( showFrames )
+ str.sprintf( "%.2i:%.2i:%.2i", m_minutes, m_seconds, m_frames );
+ else
+ str.sprintf( "%.2i:%.2i", m_minutes, m_seconds );
+
+ return str;
+}
+
+
+KIO::filesize_t K3b::Msf::mode1Bytes() const
+{
+ return (KIO::filesize_t)2048 * ( (KIO::filesize_t)lba() );
+}
+
+KIO::filesize_t K3b::Msf::mode2Form1Bytes() const
+{
+ return (KIO::filesize_t)2048 * ( (KIO::filesize_t)lba() );
+}
+
+KIO::filesize_t K3b::Msf::mode2Form2Bytes() const
+{
+ return (KIO::filesize_t)2324 * ( (KIO::filesize_t)lba() );
+}
+
+KIO::filesize_t K3b::Msf::audioBytes() const
+{
+ return (KIO::filesize_t)2352 * ( (KIO::filesize_t)lba() );
+}
+
+KIO::filesize_t K3b::Msf::rawBytes() const
+{
+ return (KIO::filesize_t)2448 * ( (KIO::filesize_t)lba() );
+}
+
+void K3b::Msf::makeValid()
+{
+ if( m_frames < 0 ) {
+ int newFrames = m_frames/-75 + 1;
+ m_seconds -= newFrames;
+ m_frames += 75*newFrames;
+ }
+ m_seconds += m_frames/75;
+ m_frames = m_frames % 75;
+ if( m_seconds < 0 ) {
+ int newSecs = m_seconds/-60 + 1;
+ m_minutes -= newSecs;
+ m_seconds += 60*newSecs;
+ }
+ m_minutes += m_seconds/60;
+ m_seconds = m_seconds % 60;
+ if( m_minutes < 0 ) {
+ m_minutes = 0;
+ m_seconds = 0;
+ m_frames = 0;
+ }
+}
+
+
+
+QRegExp K3b::Msf::regExp()
+{
+ //
+ // An MSF can have the following formats:
+ // 100 (treated as frames)
+ // 100:23 (minutes:seconds)
+ // 100:23:72 (minutes:seconds:frames)
+ // 100:23.72 (minutes:seconds.frames)
+ //
+ static QRegExp rx( "(\\d+)(?::([0-5]?\\d)(?:[:\\.]((?:[0-6]?\\d)|(?:7[0-4])))?)?" );
+ return rx;
+}
+
+
+K3b::Msf K3b::Msf::fromSeconds( double ms )
+{
+ return K3b::Msf( static_cast<int>( ::ceil(ms*75.0) ) );
+}
+
+
+K3b::Msf K3b::Msf::fromString( const QString& s, bool* ok )
+{
+ QRegExp rx = regExp();
+
+ K3b::Msf msf;
+
+ if( rx.exactMatch( s ) ) {
+ //
+ // first number - cap(1)
+ // second number - cap(2)
+ // third number - cap(3)
+ //
+ if( rx.cap(2).isEmpty() ) {
+ msf.m_frames = rx.cap(1).toInt();
+ }
+ else {
+ msf.m_minutes = rx.cap(1).toInt();
+ msf.m_seconds = rx.cap(2).toInt();
+ msf.m_frames = rx.cap(3).toInt();
+ }
+
+ if( ok )
+ *ok = true;
+ }
+ else if( ok )
+ *ok = false;
+
+ msf.makeValid();
+
+ return msf;
+}
+
+
+
+K3b::Msf K3b::operator+( const K3b::Msf& m1, const K3b::Msf& m2 )
+{
+ K3b::Msf msf(m1);
+ return msf += m2;
+}
+
+K3b::Msf K3b::operator+( const K3b::Msf& m, int i )
+{
+ K3b::Msf msf(m);
+ return msf += i;
+}
+
+K3b::Msf K3b::operator-( const K3b::Msf& m1, const K3b::Msf& m2 )
+{
+ K3b::Msf msf(m1);
+ return msf -= m2;
+}
+
+K3b::Msf K3b::operator-( const K3b::Msf& m, int i )
+{
+ K3b::Msf msf(m);
+ return msf -= i;
+}
+
+bool K3b::operator==( const K3b::Msf& m1, const K3b::Msf& m2 )
+{
+ return ( m1.minutes() == m2.minutes() &&
+ m1.seconds() == m2.seconds() &&
+ m1.frames() == m2.frames() );
+}
+
+bool K3b::operator!=( const K3b::Msf& m1, const K3b::Msf& m2 )
+{
+ return !operator==( m1, m2 );
+}
+
+bool K3b::operator<( const K3b::Msf& m1, const K3b::Msf& m2 )
+{
+ return ( m1.lba() < m2.lba() );
+}
+
+bool K3b::operator>( const K3b::Msf& m1, const K3b::Msf& m2 )
+{
+ return ( m1.lba() > m2.lba() );
+}
+
+bool K3b::operator<=( const K3b::Msf& m1, const K3b::Msf& m2 )
+{
+ return ( m1.lba() <= m2.lba() );
+}
+
+bool K3b::operator>=( const K3b::Msf& m1, const K3b::Msf& m2 )
+{
+ return ( m1.lba() >= m2.lba() );
+}
+
+kdbgstream& K3b::operator<<( kdbgstream& s, const Msf& m )
+{
+ return s << m.toString();
+}
diff --git a/libk3bdevice/k3bmsf.h b/libk3bdevice/k3bmsf.h
new file mode 100644
index 0000000..d2209c3
--- /dev/null
+++ b/libk3bdevice/k3bmsf.h
@@ -0,0 +1,118 @@
+/*
+ *
+ * $Id: k3bmsf.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_MSF_H_
+#define _K3B_MSF_H_
+
+#include <qstring.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kio/global.h>
+#include "k3bdevice_export.h"
+
+namespace K3b
+{
+ /**
+ * int values are always treated as frames
+ * except in the set methods
+ * A MSF is never < 0.
+ */
+ class LIBK3BDEVICE_EXPORT Msf
+ {
+ public:
+ Msf();
+ Msf( const Msf& );
+ Msf( int, int, int );
+ Msf( int );
+
+ Msf& operator=( const Msf& );
+ Msf& operator=( int );
+ Msf& operator+=( const Msf& );
+ Msf& operator+=( int );
+ Msf& operator-=( const Msf& );
+ Msf& operator-=( int );
+ const Msf operator++( int );
+ Msf& operator++();
+ const Msf operator--( int );
+ Msf& operator--();
+
+ int minutes() const { return m_minutes; }
+ int seconds() const { return m_seconds; }
+ int frames() const { return m_frames; }
+
+ int totalFrames() const { return ( m_minutes*60 + m_seconds )*75 + m_frames; }
+ int lba() const { return totalFrames(); }
+
+ // operator int () const { return lba(); }
+
+ void setValue( int m, int s, int f );
+
+ void addMinutes( int m );
+ void addSeconds( int s );
+ void addFrames( int f );
+
+ QString toString( bool showFrames = true ) const;
+
+ KIO::filesize_t mode1Bytes() const;
+ KIO::filesize_t mode2Form1Bytes() const;
+ KIO::filesize_t mode2Form2Bytes() const;
+ KIO::filesize_t audioBytes() const;
+ KIO::filesize_t rawBytes() const;
+ unsigned long long pcmSamples() const { return lba()*588; }
+
+ /**
+ * Convert a string representation into an Msf object.
+ *
+ * Valid strings include:
+ * \li 100 - treated as 100 frames
+ * \li 100:23 - treated as 100 minutes and 23 seconds
+ * \li 100:23:57 - treated as 100 minutes, 23 seconds, and 57 frames
+ * \li 100:23.57 - treated as 100 minutes, 23 seconds, and 57 frames
+ */
+ static Msf fromString( const QString&, bool* ok = 0 );
+
+ /**
+ * @param ms seconds
+ * frames will be rounded up
+ */
+ static Msf fromSeconds( double ms );
+
+ static QRegExp regExp();
+
+ private:
+ void makeValid();
+ int m_minutes;
+ int m_seconds;
+ int m_frames;
+ };
+
+ LIBK3BDEVICE_EXPORT Msf operator+( const Msf&, const Msf& );
+ LIBK3BDEVICE_EXPORT Msf operator+( const Msf&, int );
+ LIBK3BDEVICE_EXPORT Msf operator-( const Msf&, const Msf& );
+ LIBK3BDEVICE_EXPORT Msf operator-( const Msf&, int );
+ LIBK3BDEVICE_EXPORT bool operator==( const Msf&, const Msf& );
+ LIBK3BDEVICE_EXPORT bool operator!=( const Msf&, const Msf& );
+ LIBK3BDEVICE_EXPORT bool operator<( const Msf&, const Msf& );
+ LIBK3BDEVICE_EXPORT bool operator>( const Msf&, const Msf& );
+ LIBK3BDEVICE_EXPORT bool operator<=( const Msf&, const Msf& );
+ LIBK3BDEVICE_EXPORT bool operator>=( const Msf&, const Msf& );
+
+ LIBK3BDEVICE_EXPORT kdbgstream& operator<<( kdbgstream&, const Msf& );
+ LIBK3BDEVICE_EXPORT inline kndbgstream& operator<<( kndbgstream &stream, const Msf& ) { return stream; }
+}
+
+#endif
diff --git a/libk3bdevice/k3bscsicommand.cpp b/libk3bdevice/k3bscsicommand.cpp
new file mode 100644
index 0000000..2b34217
--- /dev/null
+++ b/libk3bdevice/k3bscsicommand.cpp
@@ -0,0 +1,218 @@
+/*
+ *
+ * $Id: k3bscsicommand.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bscsicommand.h"
+#include "k3bdevice.h"
+
+#include <k3bdebug.h>
+
+
+QString K3bDevice::commandString( const unsigned char& command )
+{
+ if( command == MMC_BLANK )
+ return "BLANK";
+ if( command == MMC_CLOSE_TRACK_SESSION )
+ return "CLOSE TRACK/SESSION";
+ if( command == MMC_ERASE )
+ return "ERASE";
+ if( command == MMC_FORMAT_UNIT )
+ return "FORMAT UNIT";
+ if( command == MMC_GET_CONFIGURATION )
+ return "GET CONFIGURATION";
+ if( command == MMC_GET_EVENT_STATUS_NOTIFICATION )
+ return "GET EVENT STATUS NOTIFICATION";
+ if( command == MMC_GET_PERFORMANCE )
+ return "GET PERFORMANCE";
+ if( command == MMC_INQUIRY )
+ return "INQUIRY";
+ if( command == MMC_LOAD_UNLOAD_MEDIUM )
+ return "LOAD/UNLOAD MEDIUM";
+ if( command == MMC_MECHANISM_STATUS )
+ return "MECHANISM STATUS";
+ if( command == MMC_MODE_SELECT )
+ return "MODE SELECT";
+ if( command == MMC_MODE_SENSE )
+ return "MODE SENSE";
+ if( command == MMC_PAUSE_RESUME )
+ return "PAUSE/RESUME";
+ if( command == MMC_PLAY_AUDIO_10 )
+ return "PLAY AUDIO (10)";
+ if( command == MMC_PLAY_AUDIO_12 )
+ return "PLAY AUDIO (12)";
+ if( command == MMC_PLAY_AUDIO_MSF )
+ return "PLAY AUDIO (MSF)";
+ if( command == MMC_PREVENT_ALLOW_MEDIUM_REMOVAL )
+ return "PREVENT ALLOW MEDIUM REMOVAL";
+ if( command == MMC_READ_10 )
+ return "READ (10)";
+ if( command == MMC_READ_12 )
+ return "READ (12)";
+ if( command == MMC_READ_BUFFER )
+ return "READ BUFFER";
+ if( command == MMC_READ_BUFFER_CAPACITY )
+ return "READ BUFFER CAPACITY";
+ if( command == MMC_READ_CAPACITY )
+ return "READ CAPACITY";
+ if( command == MMC_READ_CD )
+ return "READ CD";
+ if( command == MMC_READ_CD_MSF )
+ return "READ CD MSF";
+ if( command == MMC_READ_DISC_INFORMATION )
+ return "READ DISC INFORMATION";
+ if( command == MMC_READ_DVD_STRUCTURE )
+ return "READ DVD STRUCTURE";
+ if( command == MMC_READ_FORMAT_CAPACITIES )
+ return "READ FORMAT CAPACITIES";
+ if( command == MMC_READ_SUB_CHANNEL )
+ return "READ SUB-CHANNEL";
+ if( command == MMC_READ_TOC_PMA_ATIP )
+ return "READ TOC/PMA/ATIP";
+ if( command == MMC_READ_TRACK_INFORMATION )
+ return "READ TRACK INFORMATION";
+ if( command == MMC_REPAIR_TRACK )
+ return "REPAIR TRACK";
+ if( command == MMC_REPORT_KEY )
+ return "REPORT KEY";
+ if( command == MMC_REQUEST_SENSE )
+ return "REQUEST SENSE";
+ if( command == MMC_RESERVE_TRACK )
+ return "RESERVE TRACK";
+ if( command == MMC_SCAN )
+ return "SCAN";
+ if( command == MMC_SEEK_10 )
+ return "SEEK (10)";
+ if( command == MMC_SEND_CUE_SHEET )
+ return "SEND CUE SHEET";
+ if( command == MMC_SEND_DVD_STRUCTURE )
+ return "SEND DVD STRUCTURE";
+ if( command == MMC_SEND_KEY )
+ return "SEND KEY";
+ if( command == MMC_SEND_OPC_INFORMATION )
+ return "SEND OPC INFORMATION";
+ if( command == MMC_SET_SPEED )
+ return "SET SPEED";
+ if( command == MMC_SET_READ_AHEAD )
+ return "SET READ AHEAD";
+ if( command == MMC_SET_STREAMING )
+ return "SET STREAMING";
+ if( command == MMC_START_STOP_UNIT )
+ return "START STOP UNIT";
+ if( command == MMC_STOP_PLAY_SCAN )
+ return "STOP PLAY/SCAN";
+ if( command == MMC_SYNCHRONIZE_CACHE )
+ return "SYNCHRONIZE CACHE";
+ if( command == MMC_TEST_UNIT_READY )
+ return "TEST UNIT READY";
+ if( command == MMC_VERIFY_10 )
+ return "VERIFY (10)";
+ if( command == MMC_WRITE_10 )
+ return "WRITE (10)";
+ if( command == MMC_WRITE_12 )
+ return "WRITE (12)";
+ if( command == MMC_WRITE_AND_VERIFY_10 )
+ return "WRITE AND VERIFY (10)";
+ if( command == MMC_WRITE_BUFFER )
+ return "WRITE BUFFER";
+
+ return "unknown";
+}
+
+
+QString K3bDevice::ScsiCommand::senseKeyToString( int key )
+{
+ switch( key ) {
+ case 0x0:
+ return "NO SENSE (2)";
+ case 0x1:
+ return "RECOVERED ERROR (1)";
+ case 0x2:
+ return "NOT READY (2)";
+ case 0x3:
+ return "MEDIUM ERROR (3)";
+ case 0x4:
+ return "HARDWARE ERROR (4)";
+ case 0x5:
+ return "ILLEGAL REQUEST (5)";
+ case 0x6:
+ return "UNIT ATTENTION (6)";
+ case 0x7:
+ return "DATA PROTECT (7)";
+ case 0x8:
+ return "BLANK CHECK (8)";
+ case 0x9:
+ return "VENDOR SPECIFIC (9)";
+ case 0xA:
+ return "COPY ABORTED (A)";
+ case 0xB:
+ return "ABORTED COMMAND (B)";
+ case 0xC:
+ return "0xC is obsolete... ??";
+ }
+
+ return "unknown";
+}
+
+
+void K3bDevice::ScsiCommand::debugError( int command, int errorCode, int senseKey, int asc, int ascq ) {
+ if( m_printErrors ) {
+ k3bDebug() << "(K3bDevice::ScsiCommand) failed: " << endl
+ << " command: " << QString("%1 (%2)")
+ .arg( K3bDevice::commandString( command ) )
+ .arg( QString::number(command, 16) ) << endl
+ << " errorcode: " << QString::number(errorCode, 16) << endl
+ << " sense key: " << senseKeyToString(senseKey) << endl
+ << " asc: " << QString::number(asc, 16) << endl
+ << " ascq: " << QString::number(ascq, 16) << endl;
+ }
+}
+
+
+
+#ifdef Q_OS_LINUX
+#include "k3bscsicommand_linux.cpp"
+#endif
+#ifdef Q_OS_FREEBSD
+#include "k3bscsicommand_bsd.cpp"
+#endif
+#ifdef Q_OS_NETBSD
+#include "k3bscsicommand_netbsd.cpp"
+#endif
+
+
+
+K3bDevice::ScsiCommand::ScsiCommand( K3bDevice::Device::Handle handle )
+ : d(new Private),
+ m_device(0),
+ m_printErrors(true)
+{
+ m_deviceHandle = handle;
+ clear();
+}
+
+
+K3bDevice::ScsiCommand::ScsiCommand( const K3bDevice::Device* dev )
+ : d(new Private),
+ m_device(dev),
+ m_printErrors(true)
+{
+ clear();
+}
+
+
+K3bDevice::ScsiCommand::~ScsiCommand()
+{
+ delete d;
+}
+
diff --git a/libk3bdevice/k3bscsicommand.h b/libk3bdevice/k3bscsicommand.h
new file mode 100644
index 0000000..11a9860
--- /dev/null
+++ b/libk3bdevice/k3bscsicommand.h
@@ -0,0 +1,142 @@
+/*
+ *
+ * $Id: k3bscsicommand.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_SCSI_COMMAND_H_
+#define _K3B_SCSI_COMMAND_H_
+
+#include <qglobal.h>
+#include <qstring.h>
+
+#include "k3bdevice.h"
+
+
+namespace K3bDevice
+{
+ const unsigned char MMC_BLANK = 0xA1;
+ const unsigned char MMC_CLOSE_TRACK_SESSION = 0x5B;
+ const unsigned char MMC_ERASE = 0x2C;
+ const unsigned char MMC_FORMAT_UNIT = 0x04;
+ const unsigned char MMC_GET_CONFIGURATION = 0x46;
+ const unsigned char MMC_GET_EVENT_STATUS_NOTIFICATION = 0x4A;
+ const unsigned char MMC_GET_PERFORMANCE = 0xAC;
+ const unsigned char MMC_INQUIRY = 0x12;
+ const unsigned char MMC_LOAD_UNLOAD_MEDIUM = 0xA6;
+ const unsigned char MMC_MECHANISM_STATUS = 0xBD;
+ const unsigned char MMC_MODE_SELECT = 0x55;
+ const unsigned char MMC_MODE_SENSE = 0x5A;
+ const unsigned char MMC_PAUSE_RESUME = 0x4B;
+ const unsigned char MMC_PLAY_AUDIO_10 = 0x45;
+ const unsigned char MMC_PLAY_AUDIO_12 = 0xA5;
+ const unsigned char MMC_PLAY_AUDIO_MSF = 0x47;
+ const unsigned char MMC_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1E;
+ const unsigned char MMC_READ_10 = 0x28;
+ const unsigned char MMC_READ_12 = 0xA8;
+ const unsigned char MMC_READ_BUFFER = 0x3C;
+ const unsigned char MMC_READ_BUFFER_CAPACITY = 0x5C;
+ const unsigned char MMC_READ_CAPACITY = 0x25;
+ const unsigned char MMC_READ_CD = 0xBE;
+ const unsigned char MMC_READ_CD_MSF = 0xB9;
+ const unsigned char MMC_READ_DISC_INFORMATION = 0x51;
+ const unsigned char MMC_READ_DVD_STRUCTURE = 0xAD;
+ const unsigned char MMC_READ_DISC_STRUCTURE = 0xAD; /**< READ DVD STRUCTURE has been renamed to READ DISC STRUCTURE in MMC5 */
+ const unsigned char MMC_READ_FORMAT_CAPACITIES = 0x23;
+ const unsigned char MMC_READ_SUB_CHANNEL = 0x42;
+ const unsigned char MMC_READ_TOC_PMA_ATIP = 0x43;
+ const unsigned char MMC_READ_TRACK_INFORMATION = 0x52;
+ const unsigned char MMC_REPAIR_TRACK = 0x58;
+ const unsigned char MMC_REPORT_KEY = 0xA4;
+ const unsigned char MMC_REQUEST_SENSE = 0x03;
+ const unsigned char MMC_RESERVE_TRACK = 0x53;
+ const unsigned char MMC_SCAN = 0xBA;
+ const unsigned char MMC_SEEK_10 = 0x2B;
+ const unsigned char MMC_SEND_CUE_SHEET = 0x5D;
+ const unsigned char MMC_SEND_DVD_STRUCTURE = 0xBF;
+ const unsigned char MMC_SEND_KEY = 0xA3;
+ const unsigned char MMC_SEND_OPC_INFORMATION = 0x54;
+ const unsigned char MMC_SET_SPEED = 0xBB;
+ const unsigned char MMC_SET_READ_AHEAD = 0xA7;
+ const unsigned char MMC_SET_STREAMING = 0xB6;
+ const unsigned char MMC_START_STOP_UNIT = 0x1B;
+ const unsigned char MMC_STOP_PLAY_SCAN = 0x4E;
+ const unsigned char MMC_SYNCHRONIZE_CACHE = 0x35;
+ const unsigned char MMC_TEST_UNIT_READY = 0x00;
+ const unsigned char MMC_VERIFY_10 = 0x2F;
+ const unsigned char MMC_WRITE_10 = 0x2A;
+ const unsigned char MMC_WRITE_12 = 0xAA;
+ const unsigned char MMC_WRITE_AND_VERIFY_10 = 0x2E;
+ const unsigned char MMC_WRITE_BUFFER = 0x3B;
+
+ QString commandString( const unsigned char& command );
+
+ enum TransportDirection {
+ TR_DIR_NONE,
+ TR_DIR_READ,
+ TR_DIR_WRITE
+ };
+
+ class ScsiCommand
+ {
+ public:
+ ScsiCommand( K3bDevice::Device::Handle handle );
+ ScsiCommand( const Device* );
+ ~ScsiCommand();
+
+ /**
+ * Enales or disables printing of debugging messages for failed
+ * commands.
+ *
+ * Default is enabled.
+ */
+ void enableErrorMessages( bool b ) { m_printErrors = b; }
+
+ void clear();
+
+ unsigned char& operator[]( size_t );
+
+ // TODO: use this
+/* union ErrorCode { */
+/* K3bDevice::uint32 code; */
+/* struct { */
+/* K3bDevice::uint8 errorCode; */
+/* K3bDevice::uint8 senseKey; */
+/* K3bDevice::uint8 asc; */
+/* K3bDevice::uint8 ascq; */
+/* } details; */
+/* }; */
+
+ /**
+ * \return 0 on success, -1 if the device could not be opened, and
+ * an error code otherwise. The error code is constructed from
+ * the scsi error code, the sense key, asc, and ascq. These four values are
+ * combined into the lower 32 bit of an integer in the order used above.
+ */
+ int transport( TransportDirection dir = TR_DIR_NONE,
+ void* = 0,
+ size_t len = 0 );
+
+ private:
+ static QString senseKeyToString( int key );
+ void debugError( int command, int errorCode, int senseKey, int asc, int ascq );
+
+ class Private;
+ Private *d;
+ Device::Handle m_deviceHandle;
+ const Device* m_device;
+
+ bool m_printErrors;
+ };
+}
+
+#endif
diff --git a/libk3bdevice/k3bscsicommand_bsd.cpp b/libk3bdevice/k3bscsicommand_bsd.cpp
new file mode 100644
index 0000000..84e12f9
--- /dev/null
+++ b/libk3bdevice/k3bscsicommand_bsd.cpp
@@ -0,0 +1,208 @@
+/*
+ *
+ * $Id: k3bscsicommand_bsd.cpp 679320 2007-06-23 15:57:22Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bscsicommand.h"
+#include "k3bdevice.h"
+
+#include <k3bdebug.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <camlib.h>
+#include <cam/scsi/scsi_message.h>
+#include <cam/scsi/scsi_pass.h>
+
+#define ERRCODE(s) ((((s)[2]&0x0F)<<16)|((s)[12]<<8)|((s)[13]))
+#define EMEDIUMTYPE EINVAL
+#define ENOMEDIUM ENODEV
+#define CREAM_ON_ERRNO(s) do { \
+ switch ((s)[12]) \
+ { case 0x04: errno=EAGAIN; break; \
+ case 0x20: errno=ENODEV; break; \
+ case 0x21: if ((s)[13]==0) errno=ENOSPC; \
+ else errno=EINVAL; \
+ break; \
+ case 0x30: errno=EMEDIUMTYPE; break; \
+ case 0x3A: errno=ENOMEDIUM; break; \
+ } \
+} while(0)
+
+
+
+class K3bDevice::ScsiCommand::Private
+{
+public:
+ union ccb ccb;
+};
+
+
+void K3bDevice::ScsiCommand::clear()
+{
+ memset (&d->ccb,0,sizeof(ccb));
+}
+
+
+unsigned char& K3bDevice::ScsiCommand::operator[]( size_t i )
+{
+ if( d->ccb.csio.cdb_len < i+1 )
+ d->ccb.csio.cdb_len = i+1;
+ return d->ccb.csio.cdb_io.cdb_bytes[i];
+}
+
+int K3bDevice::ScsiCommand::transport( TransportDirection dir,
+ void* data,
+ size_t len )
+{
+ if( !m_device )
+ return -1;
+
+ m_device->usageLock();
+
+ bool needToClose = false;
+ if( !m_device->isOpen() ) {
+ needToClose = true;
+ }
+
+ if( !m_device->open( true ) ) {
+ m_device->usageUnlock();
+ return -1;
+ }
+ d->ccb.ccb_h.path_id = m_device->handle()->path_id;
+ d->ccb.ccb_h.target_id = m_device->handle()->target_id;
+ d->ccb.ccb_h.target_lun = m_device->handle()->target_lun;
+
+ k3bDebug() << "(K3bDevice::ScsiCommand) transport command " << QString::number((int)d->ccb.csio.cdb_io.cdb_bytes[0], 16) << ", length: " << (int)d->ccb.csio.cdb_len << endl;
+ int ret=0;
+ int direction = CAM_DEV_QFRZDIS;
+ if (!len)
+ direction |= CAM_DIR_NONE;
+ else
+ direction |= (dir & TR_DIR_READ)?CAM_DIR_IN : CAM_DIR_OUT;
+ cam_fill_csio (&(d->ccb.csio), 1, 0 /* NULL */, direction, MSG_SIMPLE_Q_TAG, (u_int8_t *)data, len, sizeof(d->ccb.csio.sense_data), d->ccb.csio.cdb_len, 30*1000);
+ unsigned char * sense = (unsigned char *)&d->ccb.csio.sense_data;
+
+ ret = cam_send_ccb(m_device->handle(), &d->ccb);
+
+ if (ret < 0) {
+ k3bDebug() << "(K3bDevice::ScsiCommand) transport failed: " << ret << endl;
+
+ if( needToClose )
+ m_device->close();
+
+ m_device->usageUnlock();
+
+ struct scsi_sense_data* senset = (struct scsi_sense_data*)sense;
+ debugError( d->ccb.csio.cdb_io.cdb_bytes[0],
+ senset->error_code & SSD_ERRCODE,
+ senset->flags & SSD_KEY,
+ senset->add_sense_code,
+ senset->add_sense_code_qual );
+
+ int result = (((senset->error_code & SSD_ERRCODE)<<24) & 0xF000 |
+ ((senset->flags & SSD_KEY)<<16) & 0x0F00 |
+ (senset->add_sense_code<<8) & 0x00F0 |
+ (senset->add_sense_code_qual) & 0x000F );
+
+ return result ? result : ret;
+ }
+
+ else if ((d->ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
+ if( needToClose )
+ m_device->close();
+ m_device->usageUnlock();
+ return 0;
+ }
+
+ errno = EIO;
+ // FreeBSD 5-CURRENT since 2003-08-24, including 5.2 fails to
+ // pull sense data automatically, at least for ATAPI transport,
+ // so I reach for it myself...
+ if ((d->ccb.csio.scsi_status==SCSI_STATUS_CHECK_COND) &&
+ !(d->ccb.ccb_h.status&CAM_AUTOSNS_VALID))
+ {
+ u_int8_t _sense[18];
+ u_int32_t resid=d->ccb.csio.resid;
+
+ memset(_sense,0,sizeof(_sense));
+
+ operator[](0) = 0x03; // REQUEST SENSE
+ d->ccb.csio.cdb_io.cdb_bytes[4] = sizeof(_sense);
+ d->ccb.csio.cdb_len = 6;
+ d->ccb.csio.ccb_h.flags |= CAM_DIR_IN|CAM_DIS_AUTOSENSE;
+ d->ccb.csio.data_ptr = _sense;
+ d->ccb.csio.dxfer_len = sizeof(_sense);
+ d->ccb.csio.sense_len = 0;
+
+ ret = cam_send_ccb(m_device->handle(), &d->ccb);
+
+ d->ccb.csio.resid = resid;
+ if (ret<0)
+ {
+ k3bDebug() << "(K3bDevice::ScsiCommand) transport failed (2): " << ret << endl;
+ ret = -1;
+ struct scsi_sense_data* senset = (struct scsi_sense_data*)sense;
+ debugError( d->ccb.csio.cdb_io.cdb_bytes[0],
+ senset->error_code & SSD_ERRCODE,
+ senset->flags & SSD_KEY,
+ senset->add_sense_code,
+ senset->add_sense_code_qual );
+
+ if( needToClose )
+ m_device->close();
+ m_device->usageUnlock();
+
+ return -1;
+ }
+ if ((d->ccb.ccb_h.status&CAM_STATUS_MASK) != CAM_REQ_CMP)
+ {
+ k3bDebug() << "(K3bDevice::ScsiCommand) transport failed (3): " << ret << endl;
+ errno=EIO,-1;
+ ret = -1;
+ struct scsi_sense_data* senset = (struct scsi_sense_data*)sense;
+ debugError( d->ccb.csio.cdb_io.cdb_bytes[0],
+ senset->error_code & SSD_ERRCODE,
+ senset->flags & SSD_KEY,
+ senset->add_sense_code,
+ senset->add_sense_code_qual );
+
+ if( needToClose )
+ m_device->close();
+ m_device->usageUnlock();
+
+ return -1;
+ }
+
+ memcpy(sense,_sense,sizeof(_sense));
+ }
+
+ ret = ERRCODE(sense);
+ k3bDebug() << "(K3bDevice::ScsiCommand) transport failed (4): " << ret << endl;
+ if (ret == 0)
+ ret = -1;
+ else
+ CREAM_ON_ERRNO(((unsigned char *)&d->ccb.csio.sense_data));
+ struct scsi_sense_data* senset = (struct scsi_sense_data*)sense;
+ debugError( d->ccb.csio.cdb_io.cdb_bytes[0],
+ senset->error_code & SSD_ERRCODE,
+ senset->flags & SSD_KEY,
+ senset->add_sense_code,
+ senset->add_sense_code_qual );
+
+ if( needToClose )
+ m_device->close();
+ m_device->usageUnlock();
+
+ return ret;
+}
diff --git a/libk3bdevice/k3bscsicommand_linux.cpp b/libk3bdevice/k3bscsicommand_linux.cpp
new file mode 100644
index 0000000..774da06
--- /dev/null
+++ b/libk3bdevice/k3bscsicommand_linux.cpp
@@ -0,0 +1,177 @@
+/*
+ *
+ * $Id: k3bscsicommand_linux.cpp 679274 2007-06-23 13:23:58Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * Parts of this file are inspired (and copied) from transport.hxx
+ * from the dvd+rw-tools (C) Andy Polyakov <appro@fy.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bscsicommand.h"
+#include "k3bdevice.h"
+
+#include <k3bdebug.h>
+
+#include <sys/ioctl.h>
+#undef __STRICT_ANSI__
+#include <linux/cdrom.h>
+#define __STRICT_ANSI__
+#include <scsi/sg.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+
+
+#if !defined(SG_FLAG_LUN_INHIBIT)
+# if defined(SG_FLAG_UNUSED_LUN_INHIBIT)
+# define SG_FLAG_LUN_INHIBIT SG_FLAG_UNUSED_LUN_INHIBIT
+# else
+# define SG_FLAG_LUN_INHIBIT 0
+# endif
+#endif
+
+#ifdef SG_IO
+static bool useSgIo()
+{
+ struct utsname buf;
+ uname( &buf );
+ // was CDROM_SEND_PACKET declared dead in 2.5?
+ return ( strcmp( buf.release, "2.5.43" ) >=0 );
+}
+#endif
+
+
+class K3bDevice::ScsiCommand::Private
+{
+public:
+ struct cdrom_generic_command cmd;
+ struct request_sense sense;
+
+#ifdef SG_IO
+ bool useSgIo;
+ struct sg_io_hdr sgIo;
+#endif
+};
+
+
+void K3bDevice::ScsiCommand::clear()
+{
+ ::memset( &d->cmd, 0, sizeof(struct cdrom_generic_command) );
+ ::memset( &d->sense, 0, sizeof(struct request_sense) );
+
+ d->cmd.quiet = 1;
+ d->cmd.sense = &d->sense;
+
+#ifdef SG_IO
+ d->useSgIo = useSgIo();
+ ::memset( &d->sgIo, 0, sizeof(struct sg_io_hdr) );
+#endif
+}
+
+
+unsigned char& K3bDevice::ScsiCommand::operator[]( size_t i )
+{
+#ifdef SG_IO
+ if( d->sgIo.cmd_len < i+1 )
+ d->sgIo.cmd_len = i+1;
+#endif
+ return d->cmd.cmd[i];
+}
+
+
+int K3bDevice::ScsiCommand::transport( TransportDirection dir,
+ void* data,
+ size_t len )
+{
+ bool needToClose = false;
+ if( m_device ) {
+ m_device->usageLock();
+ if( !m_device->isOpen() ) {
+ needToClose = true;
+ }
+ if ( !m_device->open( dir == TR_DIR_WRITE ) ) {
+ m_device->usageUnlock();
+ return -1;
+ }
+ m_deviceHandle = m_device->handle();
+ }
+
+ if( m_deviceHandle == -1 ) {
+ return -1;
+ }
+
+ int i = -1;
+
+#ifdef SG_IO
+ if( d->useSgIo ) {
+ d->sgIo.interface_id= 'S';
+ d->sgIo.mx_sb_len = sizeof( struct request_sense );
+ d->sgIo.cmdp = d->cmd.cmd;
+ d->sgIo.sbp = (unsigned char*)&d->sense;
+ d->sgIo.flags = SG_FLAG_LUN_INHIBIT|SG_FLAG_DIRECT_IO;
+ d->sgIo.dxferp = data;
+ d->sgIo.dxfer_len = len;
+ d->sgIo.timeout = 5000;
+ if( dir == TR_DIR_READ )
+ d->sgIo.dxfer_direction = SG_DXFER_FROM_DEV;
+ else if( dir == TR_DIR_WRITE )
+ d->sgIo.dxfer_direction = SG_DXFER_TO_DEV;
+ else
+ d->sgIo.dxfer_direction = SG_DXFER_NONE;
+
+ i = ::ioctl( m_deviceHandle, SG_IO, &d->sgIo );
+
+ if( ( d->sgIo.info&SG_INFO_OK_MASK ) != SG_INFO_OK )
+ i = -1;
+ }
+ else {
+#endif
+ d->cmd.buffer = (unsigned char*)data;
+ d->cmd.buflen = len;
+ if( dir == TR_DIR_READ )
+ d->cmd.data_direction = CGC_DATA_READ;
+ else if( dir == TR_DIR_WRITE )
+ d->cmd.data_direction = CGC_DATA_WRITE;
+ else
+ d->cmd.data_direction = CGC_DATA_NONE;
+
+ i = ::ioctl( m_deviceHandle, CDROM_SEND_PACKET, &d->cmd );
+#ifdef SG_IO
+ }
+#endif
+
+ if( needToClose )
+ m_device->close();
+
+ if ( m_device ) {
+ m_device->usageUnlock();
+ }
+
+ if( i ) {
+ debugError( d->cmd.cmd[0],
+ d->sense.error_code,
+ d->sense.sense_key,
+ d->sense.asc,
+ d->sense.ascq );
+
+ int errCode =
+ (d->sense.error_code<<24) & 0xF000 |
+ (d->sense.sense_key<<16) & 0x0F00 |
+ (d->sense.asc<<8) & 0x00F0 |
+ (d->sense.ascq) & 0x000F;
+
+ return( errCode != 0 ? errCode : 1 );
+ }
+ else
+ return 0;
+}
diff --git a/libk3bdevice/k3bscsicommand_netbsd.cpp b/libk3bdevice/k3bscsicommand_netbsd.cpp
new file mode 100644
index 0000000..bd09e7a
--- /dev/null
+++ b/libk3bdevice/k3bscsicommand_netbsd.cpp
@@ -0,0 +1,111 @@
+/*
+ *
+ * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $
+ * Copyright (C) 2006 Mark Davies <mark@mcs.vuw.ac.nz>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bscsicommand.h"
+#include "k3bdevice.h"
+
+#include <k3bdebug.h>
+
+#include <sys/ioctl.h>
+#include <sys/scsiio.h>
+// #include <sys/cdio.h>
+// #include <sys/dkio.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+
+
+class K3bDevice::ScsiCommand::Private
+{
+public:
+ struct scsireq cmd;
+};
+
+
+void K3bDevice::ScsiCommand::clear()
+{
+ ::memset( &d->cmd, 0, sizeof(struct scsireq ) );
+}
+
+
+unsigned char& K3bDevice::ScsiCommand::operator[]( size_t i )
+{
+ if( d->cmd.cmdlen < i+1 )
+ d->cmd.cmdlen = i+1;
+ return d->cmd.cmd[i];
+}
+
+
+int K3bDevice::ScsiCommand::transport( TransportDirection dir,
+ void* data,
+ size_t len )
+{
+ bool needToClose = false;
+ if( m_device ) {
+ m_device->usageLock();
+ if( !m_device->isOpen() ) {
+ needToClose = true;
+ }
+ m_device->open( dir == TR_DIR_WRITE );
+ m_deviceHandle = m_device->handle();
+ }
+
+ if( m_deviceHandle == -1 ) {
+ if ( m_device ) {
+ m_device->usageUnlock();
+ }
+
+ return -1;
+ }
+
+ d->cmd.timeout = 10000;
+ d->cmd.databuf = (caddr_t) data;
+ d->cmd.datalen = len;
+ // d->cmd.datalen_used = len;
+ d->cmd.senselen = SENSEBUFLEN;
+ switch (dir)
+ {
+ case TR_DIR_READ:
+ d->cmd.flags = SCCMD_READ;
+ break;
+ case TR_DIR_WRITE:
+ d->cmd.flags = SCCMD_WRITE;
+ break;
+ default:
+ d->cmd.flags = SCCMD_READ;
+ break;
+ }
+
+ int i = ::ioctl( m_deviceHandle, SCIOCCOMMAND, &d->cmd );
+
+ if ( m_device ) {
+ if( needToClose )
+ m_device->close();
+ m_device->usageUnlock();
+ }
+
+ if( i || (d->cmd.retsts != SCCMD_OK)) {
+ debugError( d->cmd.cmd[0],
+ d->cmd.retsts,
+ d->cmd.sense[2],
+ d->cmd.sense[12],
+ d->cmd.sense[13] );
+
+ return 1;
+ }
+ else
+ return 0;
+}
diff --git a/libk3bdevice/k3btoc.cpp b/libk3bdevice/k3btoc.cpp
new file mode 100644
index 0000000..570bc9e
--- /dev/null
+++ b/libk3bdevice/k3btoc.cpp
@@ -0,0 +1,163 @@
+/*
+ *
+ * $Id: k3btoc.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3btoc.h"
+#include "k3bdebug.h"
+
+#include <qstring.h>
+
+
+K3bDevice::Toc::Toc()
+ : QValueList<K3bDevice::Track>()
+{
+}
+
+
+K3bDevice::Toc::Toc( const Toc& toc )
+ : QValueList<K3bDevice::Track>( toc )
+{
+ m_firstSector = toc.firstSector();
+}
+
+
+K3bDevice::Toc::~Toc()
+{
+}
+
+
+K3bDevice::Toc& K3bDevice::Toc::operator=( const Toc& toc )
+{
+ if( &toc == this ) return *this;
+
+ m_firstSector = toc.firstSector();
+
+ QValueList<K3bDevice::Track>::operator=( toc );
+
+ return *this;
+}
+
+
+const K3b::Msf& K3bDevice::Toc::firstSector() const
+{
+ return m_firstSector;
+}
+
+
+K3b::Msf K3bDevice::Toc::lastSector() const
+{
+ if( isEmpty() )
+ return 0;
+ // the last track's last sector should be the last sector of the entire cd
+ return last().lastSector();
+}
+
+
+K3b::Msf K3bDevice::Toc::length() const
+{
+ // +1 since the last sector is included
+ return lastSector() - firstSector() + 1;
+}
+
+
+unsigned int K3bDevice::Toc::discId() const
+{
+ // calculate cddb-id
+ unsigned int id = 0;
+ for( Toc::const_iterator it = constBegin(); it != constEnd(); ++it ) {
+ unsigned int n = (*it).firstSector().lba() + 150;
+ n /= 75;
+ while( n > 0 ) {
+ id += n % 10;
+ n /= 10;
+ }
+ }
+ unsigned int l = length().lba();
+ l /= 75;
+ id = ( ( id % 0xff ) << 24 ) | ( l << 8 ) | count();
+
+ return id;
+}
+
+
+int K3bDevice::Toc::contentType() const
+{
+ int audioCnt = 0, dataCnt = 0;
+ for( Toc::const_iterator it = constBegin(); it != constEnd(); ++it ) {
+ if( (*it).type() == K3bDevice::Track::AUDIO )
+ audioCnt++;
+ else
+ dataCnt++;
+ }
+
+ if( audioCnt + dataCnt == 0 )
+ return K3bDevice::NONE;
+ if( audioCnt == 0 )
+ return K3bDevice::DATA;
+ if( dataCnt == 0 )
+ return K3bDevice::AUDIO;
+ return K3bDevice::MIXED;
+}
+
+
+int K3bDevice::Toc::sessions() const
+{
+ if( isEmpty() )
+ return 0;
+ else if( last().session() == 0 )
+ return 1; // default if unknown
+ else
+ return last().session();
+}
+
+
+void K3bDevice::Toc::clear()
+{
+ QValueList<Track>::clear();
+ m_mcn.resize( 0 );
+ m_firstSector = 0;
+}
+
+
+void K3bDevice::Toc::debug() const
+{
+ k3bDebug() << count() << " in " << sessions() << " sessions" << endl;
+ int sessionN = 0;
+ int trackN = 0;
+ for( Toc::const_iterator it = begin(); it != end(); ++it ) {
+ ++trackN;
+ if( sessionN != (*it).session() ) {
+ sessionN = (*it).session();
+ k3bDebug() << "Session Number " << sessionN << endl;
+ }
+ k3bDebug() << " Track " << trackN << ( (*it).type() == Track::AUDIO ? " AUDIO" : " DATA" )
+ << " " << (*it).firstSector().lba() << " - " << (*it).lastSector().lba()
+ << " (" << (*it).length().lba() << ")" << endl;
+ }
+}
+
+
+bool K3bDevice::Toc::operator==( const Toc& other ) const
+{
+ return( m_firstSector == other.m_firstSector &&
+ QValueList<Track>::operator==( other ) );
+}
+
+
+bool K3bDevice::Toc::operator!=( const Toc& other ) const
+{
+ return( m_firstSector != other.m_firstSector ||
+ QValueList<Track>::operator!=( other ) );
+}
diff --git a/libk3bdevice/k3btoc.h b/libk3bdevice/k3btoc.h
new file mode 100644
index 0000000..adfba74
--- /dev/null
+++ b/libk3bdevice/k3btoc.h
@@ -0,0 +1,101 @@
+/*
+ *
+ * $Id: k3btoc.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BTOC_H
+#define K3BTOC_H
+
+#include <qvaluelist.h>
+#include <qcstring.h>
+
+#include <k3bmsf.h>
+
+#include "k3btrack.h"
+#include "k3bdevice_export.h"
+class QString;
+
+namespace K3bDevice
+{
+
+ enum ContentsType {
+ DATA,
+ AUDIO,
+ MIXED,
+ NONE // no tracks
+ };
+
+ /**
+ * A list of K3bTracks that represents the contents
+ * of a cd.
+ * The Toc deletes all its tracks when it is deleted and
+ * deletes removed tracks.
+ */
+ class LIBK3BDEVICE_EXPORT Toc : public QValueList<K3bTrack>
+ {
+ public:
+ Toc();
+ /** deep copy */
+ Toc( const Toc& );
+ /** deletes all tracks */
+ ~Toc();
+ /** deep copy */
+ Toc& operator=( const Toc& );
+
+ /**
+ * CDDB disc Id
+ */
+ unsigned int discId() const;
+
+ const QCString& mcn() const { return m_mcn; }
+
+ /**
+ * determine the contents type based on the tracks' types.
+ * Audio, Data, or Mixed
+ */
+ int contentType() const;
+
+ /**
+ * \return the number of sessions in this TOC.
+ */
+ int sessions() const;
+
+ /**
+ * The first track's first sector could differ from the disc's
+ * first sector if there is a pregap before index 1
+ */
+ const K3b::Msf& firstSector() const;
+ K3b::Msf lastSector() const;
+ K3b::Msf length() const;
+
+ void setFirstSector( int i ) { m_firstSector = i; }
+
+ void setMcn( const QCString& mcn ) { m_mcn = mcn; }
+
+ void clear();
+
+ void debug() const;
+
+ bool operator==( const Toc& ) const;
+ bool operator!=( const Toc& ) const;
+
+ private:
+ unsigned int m_discId;
+ K3b::Msf m_firstSector;
+
+ QCString m_mcn;
+ };
+}
+
+#endif
diff --git a/libk3bdevice/k3btrack.cpp b/libk3bdevice/k3btrack.cpp
new file mode 100644
index 0000000..6efcdcb
--- /dev/null
+++ b/libk3bdevice/k3btrack.cpp
@@ -0,0 +1,123 @@
+/*
+ *
+ * $Id: k3btrack.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3btrack.h"
+
+
+K3bDevice::Track::Track()
+ : m_type(-1),
+ m_mode(-1),
+ m_copyPermitted(true),
+ m_preEmphasis(false),
+ m_session(0)
+{
+}
+
+
+K3bDevice::Track::Track( const Track& track )
+ : m_firstSector( track.firstSector() ),
+ m_lastSector( track.lastSector() ),
+ m_index0( track.index0() ),
+ m_type( track.type() ),
+ m_mode( track.mode() ),
+ m_copyPermitted( track.copyPermitted() ),
+ m_preEmphasis( track.preEmphasis() ),
+ m_session( track.session() ),
+ m_indices( track.indices() )
+{
+}
+
+
+K3bDevice::Track::Track( const K3b::Msf& firstSector,
+ const K3b::Msf& lastSector,
+ int type,
+ int mode )
+ : m_firstSector( firstSector ),
+ m_lastSector( lastSector ),
+ m_type( type ),
+ m_mode( mode ),
+ m_copyPermitted(true),
+ m_preEmphasis(false),
+ m_session(0)
+{
+}
+
+
+K3bDevice::Track& K3bDevice::Track::operator=( const K3bTrack& track )
+{
+ if( this != &track ) {
+ m_firstSector = track.firstSector();
+ m_lastSector = track.lastSector();
+ m_index0 = track.index0();
+ m_type = track.type();
+ m_mode = track.mode();
+ m_indices = track.indices();
+ }
+
+ return *this;
+}
+
+
+K3b::Msf K3bDevice::Track::length() const
+{
+ // +1 since the last sector is included
+ return m_lastSector - m_firstSector + 1;
+}
+
+
+K3b::Msf K3bDevice::Track::realAudioLength() const
+{
+ if( index0() > 0 )
+ return index0();
+ else
+ return length();
+}
+
+
+void K3bDevice::Track::setIndex0( const K3b::Msf& msf )
+{
+ if( msf <= m_lastSector-m_firstSector )
+ m_index0 = msf;
+}
+
+
+int K3bDevice::Track::indexCount() const
+{
+ return m_indices.count()-1;
+}
+
+
+bool K3bDevice::Track::operator==( const Track& other ) const
+{
+ return( m_firstSector == other.m_firstSector &&
+ m_lastSector == other.m_lastSector &&
+ m_index0 == other.m_index0 &&
+ m_nextWritableAddress == other.m_nextWritableAddress &&
+ m_freeBlocks == other.m_freeBlocks &&
+ m_type == other.m_type &&
+ m_mode == other.m_mode &&
+ m_copyPermitted == other.m_copyPermitted &&
+ m_preEmphasis == other.m_preEmphasis &&
+ m_session == other.m_session &&
+ m_indices == other.m_indices &&
+ m_isrc == other.m_isrc );
+}
+
+
+bool K3bDevice::Track::operator!=( const Track& other ) const
+{
+ return !operator==( other );
+}
diff --git a/libk3bdevice/k3btrack.h b/libk3bdevice/k3btrack.h
new file mode 100644
index 0000000..f68cc86
--- /dev/null
+++ b/libk3bdevice/k3btrack.h
@@ -0,0 +1,151 @@
+/*
+ *
+ * $Id: k3btrack.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+
+#ifndef K3BTRACK_H
+#define K3BTRACK_H
+
+#include <qcstring.h>
+#include <qvaluevector.h>
+
+#include <k3bmsf.h>
+#include "k3bdevice_export.h"
+
+namespace K3bDevice
+{
+ class LIBK3BDEVICE_EXPORT Track
+ {
+ friend class Device;
+
+ public:
+ enum TrackType {
+ AUDIO,
+ DATA
+ };
+
+ enum DataMode {
+ MODE1,
+ MODE2,
+ XA_FORM1,
+ XA_FORM2,
+ DVD,
+ UNKNOWN
+ };
+
+ Track();
+ Track( const Track& );
+ Track( const K3b::Msf& firstSector,
+ const K3b::Msf& lastSector,
+ int type,
+ int mode = UNKNOWN );
+ Track& operator=( const Track& );
+
+ int type() const { return m_type; }
+
+ /**
+ * Invalid for DVDs and Audio CDs
+ */
+ int mode() const { return m_mode; }
+
+ /**
+ * Invalid for DVDs
+ */
+ bool copyPermitted() const { return m_copyPermitted; }
+ void setCopyPermitted( bool b ) { m_copyPermitted = b; }
+
+ /**
+ * Only valid for audio tracks
+ */
+ bool preEmphasis() const { return m_preEmphasis; }
+ void setPreEmphasis( bool b ) { m_preEmphasis = b; }
+
+ bool recordedIncremental() const { return m_preEmphasis; }
+ bool recordedUninterrupted() const { return !recordedIncremental(); }
+
+ const QCString& isrc() const { return m_isrc; }
+ void setIsrc( const QCString& s ) { m_isrc = s; }
+
+ const K3b::Msf& firstSector() const { return m_firstSector; }
+ const K3b::Msf& lastSector() const { return m_lastSector; }
+ void setFirstSector( const K3b::Msf& msf ) { m_firstSector = msf; }
+ void setLastSector( const K3b::Msf& msf ) { m_lastSector = msf; }
+
+ const K3b::Msf& nextWritableAddress() const { return m_nextWritableAddress; }
+ const K3b::Msf& freeBlocks() const { return m_freeBlocks; }
+
+ K3b::Msf length() const;
+
+ /**
+ * This takes index0 into account
+ */
+ K3b::Msf realAudioLength() const;
+
+ /**
+ * 0 if unknown
+ */
+ int session() const { return m_session; }
+ void setSession( int s ) { m_session = s; }
+
+ /**
+ * @return number of indices. This does not include index 0.
+ */
+ int indexCount() const;
+
+ /**
+ * Returns the index relative to the track's start.
+ * If it is zero there is no index0.
+ */
+ const K3b::Msf& index0() const { return m_index0; }
+
+ /**
+ * Set the track's index0 value.
+ * @param msf offset relative to track start.
+ */
+ void setIndex0( const K3b::Msf& msf );
+
+ /**
+ * All indices. Normally this list is empty as indices are rarely used.
+ * Starts with index 2 (since index 1 are all other sectors FIXME)
+ */
+ const QValueVector<K3b::Msf>& indices() const { return m_indices; }
+
+ bool operator==( const Track& ) const;
+ bool operator!=( const Track& ) const;
+
+ private:
+ K3b::Msf m_firstSector;
+ K3b::Msf m_lastSector;
+ K3b::Msf m_index0;
+
+ K3b::Msf m_nextWritableAddress;
+ K3b::Msf m_freeBlocks;
+
+ int m_type;
+ int m_mode;
+ bool m_copyPermitted;
+ bool m_preEmphasis;
+
+ int m_session;
+
+ QValueVector<K3b::Msf> m_indices;
+
+ QCString m_isrc;
+ };
+}
+
+typedef K3bDevice::Track K3bTrack;
+
+#endif
diff --git a/libk3bdevice/libk3bdevice.doxy b/libk3bdevice/libk3bdevice.doxy
new file mode 100644
index 0000000..cb7325a
--- /dev/null
+++ b/libk3bdevice/libk3bdevice.doxy
@@ -0,0 +1,214 @@
+# Doxyfile 1.3.9.1
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME = libk3bdevice
+PROJECT_NUMBER = 1.0
+OUTPUT_DIRECTORY = docs/
+CREATE_SUBDIRS = NO
+OUTPUT_LANGUAGE = English
+USE_WINDOWS_ENCODING = NO
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ABBREVIATE_BRIEF =
+ALWAYS_DETAILED_SEC = NO
+INLINE_INHERITED_MEMB = NO
+FULL_PATH_NAMES = YES
+STRIP_FROM_PATH =
+STRIP_FROM_INC_PATH =
+SHORT_NAMES = NO
+JAVADOC_AUTOBRIEF = NO
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP = NO
+INHERIT_DOCS = YES
+DISTRIBUTE_GROUP_DOC = NO
+TAB_SIZE = 8
+ALIASES =
+OPTIMIZE_OUTPUT_FOR_C = NO
+OPTIMIZE_OUTPUT_JAVA = NO
+SUBGROUPING = YES
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL = YES
+EXTRACT_PRIVATE = NO
+EXTRACT_STATIC = YES
+EXTRACT_LOCAL_CLASSES = YES
+EXTRACT_LOCAL_METHODS = NO
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = NO
+HIDE_IN_BODY_DOCS = NO
+INTERNAL_DOCS = NO
+CASE_SENSE_NAMES = YES
+HIDE_SCOPE_NAMES = NO
+SHOW_INCLUDE_FILES = YES
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = YES
+SORT_BRIEF_DOCS = NO
+SORT_BY_SCOPE_NAME = NO
+GENERATE_TODOLIST = YES
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+SHOW_USED_FILES = YES
+SHOW_DIRECTORIES = YES
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = NO
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT =
+FILE_PATTERNS = *.h
+RECURSIVE = NO
+EXCLUDE = k3bmmc.h
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS =
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS =
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_PATTERNS =
+FILTER_SOURCE_FILES = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER = NO
+INLINE_SOURCES = NO
+STRIP_CODE_COMMENTS = YES
+REFERENCED_BY_RELATION = NO
+REFERENCES_RELATION = NO
+VERBATIM_HEADERS = NO
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = NO
+COLS_IN_ALPHA_INDEX = 5
+IGNORE_PREFIX =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+HTML_FILE_EXTENSION = .html
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_ALIGN_MEMBERS = YES
+GENERATE_HTMLHELP = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = NO
+ENUM_VALUES_PER_LINE = 4
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX = NO
+LATEX_OUTPUT = latex
+LATEX_CMD_NAME = latex
+MAKEINDEX_CMD_NAME = makeindex
+COMPACT_LATEX = NO
+PAPER_TYPE = a4wide
+EXTRA_PACKAGES =
+LATEX_HEADER =
+PDF_HYPERLINKS = NO
+USE_PDFLATEX = NO
+LATEX_BATCHMODE = NO
+LATEX_HIDE_INDICES = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+MAN_LINKS = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML = NO
+XML_OUTPUT = xml
+XML_SCHEMA =
+XML_DTD =
+XML_PROGRAMLISTING = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD = NO
+PERLMOD_LATEX = NO
+PERLMOD_PRETTY = YES
+PERLMOD_MAKEVAR_PREFIX =
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED =
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE =
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = YES
+PERL_PATH = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS = NO
+HIDE_UNDOC_RELATIONS = YES
+HAVE_DOT = NO
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+UML_LOOK = YES
+TEMPLATE_RELATIONS = NO
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+CALL_GRAPH = NO
+GRAPHICAL_HIERARCHY = YES
+DOT_IMAGE_FORMAT = png
+DOT_PATH =
+DOTFILE_DIRS =
+MAX_DOT_GRAPH_WIDTH = 1024
+MAX_DOT_GRAPH_HEIGHT = 1024
+MAX_DOT_GRAPH_DEPTH = 0
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO