diff options
Diffstat (limited to 'libk3bdevice')
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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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, ¤tMax, ¤tMaxFormat ) ) { + 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * Copyright (C) 2005-2007 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * Parts of this file are inspired (and copied) from transport.hxx + * from the dvd+rw-tools (C) Andy Polyakov <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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 |