|
|
|
/*
|
|
|
|
*
|
|
|
|
* $Id: k3bdevice_mmc.cpp 690628 2007-07-21 16:05:08Z trueg $
|
|
|
|
* Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
|
|
|
|
*
|
|
|
|
* This file is part of the K3b project.
|
|
|
|
* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
* See the file "COPYING" for the exact licensing terms.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
This file contains all the MMC command implementations of the K3b device class
|
|
|
|
to make the code more readable.
|
|
|
|
**/
|
|
|
|
|
|
|
|
|
|
|
|
#include "k3bdevice.h"
|
|
|
|
#include "k3bscsicommand.h"
|
|
|
|
#include "k3bdeviceglobals.h"
|
|
|
|
#include "k3bdebug.h"
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
|
|
bool K3bDevice::Device::testUnitReady() const
|
|
|
|
{
|
|
|
|
ScsiCommand cmd( this );
|
|
|
|
cmd.enableErrorMessages( false );
|
|
|
|
cmd[0] = MMC_TEST_UNIT_READY;
|
|
|
|
cmd[5] = 0; // Necessary to set the proper command length
|
|
|
|
return( cmd.transport() == 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool K3bDevice::Device::getFeature( unsigned char** data, unsigned int& dataLen, unsigned int feature ) const
|
|
|
|
{
|
|
|
|
unsigned char header[2048];
|
|
|
|
::memset( header, 0, 2048 );
|
|
|
|
|
|
|
|
ScsiCommand cmd( this );
|
|
|
|
cmd[0] = MMC_GET_CONFIGURATION;
|
|
|
|
cmd[1] = 2; // read only specified feature
|
|
|
|
cmd[2] = feature>>8;
|
|
|
|
cmd[3] = feature;
|
|
|
|
cmd[8] = 8; // we only read the data length first
|
|
|
|
cmd[9] = 0; // Necessary to set the proper command length
|
|
|
|
|
|
|
|
// we only read the data length first
|
|
|
|
dataLen = 8;
|
|
|
|
if( cmd.transport( TR_DIR_READ, header, 8 ) )
|
|
|
|
dataLen = from4Byte( header ) + 4;
|
|
|
|
else
|
|
|
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": GET CONFIGURATION length det failed." << endl;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Some buggy firmwares do not return the size of the available data
|
|
|
|
// but the returned data or something invalid altogether.
|
|
|
|
// So we simply use the maximum possible value to be on the safe side
|
|
|
|
// with these buggy drives.
|
|
|
|
// We cannot use this as default since many firmwares fail with a too high data length.
|
|
|
|
//
|
|
|
|
if( (dataLen-8) % 8 || dataLen <= 8 )
|
|
|
|
dataLen = 0xFFFF;
|
|
|
|
|
|
|
|
// again with real length
|
|
|
|
*data = new unsigned char[dataLen];
|
|
|
|
::memset( *data, 0, dataLen );
|
|
|
|
|
|
|
|
cmd[7] = dataLen>>8;
|
|
|
|
cmd[8] = dataLen;
|
|
|
|
if( cmd.transport( TR_DIR_READ, *data, dataLen ) == 0 ) {
|
|
|
|
dataLen = TQMIN( 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, TQCString& 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 = TQCString( 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( TQCString& 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 = TQCString( 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 = TQMIN( 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 = TQMIN( 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 = TQMIN( 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 = TQMIN( 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::mechanismtqStatus( 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 = TQMIN( 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 = TQMIN( 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 = TQMAX( (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 = TQMIN( 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 = TQMIN( 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;
|
|
|
|
}
|