// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//
#include "scsiprot.h"
#ifdef MSDC_MULTITHREADED
#include "rwdrivethread.h"
#endif // MSDC_MULTITHREADED
#include "massstoragedebug.h"
// Helper macros
#define LBA(x) static_cast<TUint32>((x[3] << 24) | (x[4] << 16) | (x[5] << 8) | x[6])
#define LEN(x) static_cast<TUint16>((x[8] << 8) | x[9])
LOCAL_D const TUint KDefaultBlockSize = 0x200; //default block size for FAT
LOCAL_D const TUint KUndefinedLun = 0xFFFF;
LOCAL_D const TUint8 KAllPages = 0x3F;
LOCAL_D const TUint8 KChangeableValues = 0x1;
LOCAL_D const TUint8 KDefaultValues = 0x2;
/**
Default constructor for TSenseInfo
*/
TSenseInfo::TSenseInfo()
: iSenseCode(ENoSense),
iAdditional(EAscNull),
iQualifier(EAscqNull)
{}
/**
Set sense with no additional info.
@param aSenseCode sense key
*/
void TSenseInfo::SetSense(TSenseCode aSenseCode)
{
iSenseCode = static_cast<TUint8>(aSenseCode);
iAdditional = EAscNull;
iQualifier = EAscqNull;
}
/**
Set sense with additional info.
@param aSenseCode sense key
@param aAdditional additional sense code (ASC)
*/
void TSenseInfo::SetSense(TSenseCode aSenseCode, TAdditionalCode aAdditional)
{
iSenseCode = static_cast<TUint8>(aSenseCode);
iAdditional = static_cast<TUint8>(aAdditional);
iQualifier = EAscqNull;
}
/**
Set sense with additional info and qualifier.
@param aSenseCode sense key
@param aAdditional additional sense code (ASC)
@param aQualifier additional sense code qualifier (ASCQ)
*/
void TSenseInfo::SetSense(TSenseCode aSenseCode,
TAdditionalCode aAdditional,
TAdditionalSenseCodeQualifier aQualifier)
{
iSenseCode = static_cast<TUint8>(aSenseCode);
iAdditional = static_cast<TUint8>(aAdditional);
iQualifier = static_cast<TUint8>(aQualifier);
}
//-----------------------------------------------
/**
Creates the CScsiProtocol object. Called during controller initialisation.
@param aDriveManager reference to the drive manager object
*/
CScsiProtocol* CScsiProtocol::NewL(CDriveManager& aDriveManager)
{
CScsiProtocol* self = new (ELeave) CScsiProtocol(aDriveManager);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop();
return self;
}
/**
c'tor
@param aDriveManager reference to the drive manager object
*/
CScsiProtocol::CScsiProtocol(CDriveManager& aDriveManager):
iDriveManager(aDriveManager),
iLastCommand(EUndefinedCommand),
iLastLun(KUndefinedLun),
iMediaWriteSize(KDefaultMediaWriteSize)
{
__FNLOG("CScsiProtocol::CScsiProtocol");
#ifdef USB_TRANSFER_PUBLISHER
iWriteTransferPublisher = CUsbWriteTransferPublisher::NewL(iBytesWritten);
iReadTransferPublisher = CUsbReadTransferPublisher::NewL(iBytesRead);
for (TUint i = 0; i < KUsbMsMaxDrives; i++)
{
iBytesRead[i] = 0;
iBytesWritten[i] = 0;
}
#else
iWriteTransferPublisher = CDriveWriteTransferPublisher::NewL(aDriveManager.iDrives);
iReadTransferPublisher = CDriveReadTransferPublisher::NewL(aDriveManager.iDrives);
#endif
}
CScsiProtocol::~CScsiProtocol()
{
__FNLOG("CScsiProtocol::~CScsiProtocol");
#ifdef MSDC_MULTITHREADED
__PRINT(_L("Deleting WriteDrive Thread"));
delete iWriteDriveThread;
__PRINT(_L("Deleting ReadDrive Thread"));
delete iReadDriveThread;
#endif // MSDC_MULTITHREADED
delete iWriteTransferPublisher;
delete iReadTransferPublisher;
}
void CScsiProtocol::ConstructL()
{
__FNLOG("CScsiProtocol::ConstructL");
#ifdef MSDC_MULTITHREADED
__PRINT(_L("Creating WriteDrive Thread"));
iWriteDriveThread = CWriteDriveThread::NewL();
__PRINT(_L("Creating ReadDrive Thread"));
iReadDriveThread = CReadDriveThread::NewL();
#endif // MSDC_MULTITHREADED
}
/**
Associates the transport with the protocol. Called during initialisation of the controller.
@param aTransport pointer to the transport object
*/
void CScsiProtocol::RegisterTransport(MTransportBase* aTransport)
{
__FNLOG("CScsiProtocol::RegisterTransport");
iTransport = aTransport;
}
/**
Called by the Transport when it detects that the USB device is either running
at High Speed or is at least capable of HS operation. The Protocol can use this
information (for instance) to select the optimal write block size to use.
This function is preferably called before actual MS data transfer operation
starts, and usually only once.
*/
void CScsiProtocol::ReportHighSpeedDevice()
{
__FNLOG("CScsiProtocol::ReportHighSpeedDevice");
iMediaWriteSize = KHsMediaWriteSize;
__PRINT1(_L("HS Device reported: SCSI will use %d bytes disk write size"), iMediaWriteSize);
}
TInt CScsiProtocol::SetScsiParameters(TMassStorageConfig aConfig)
{
__FNLOG("CScsiProtocol::SetScsiParameters");
iConfig = aConfig;
return KErrNone;
}
#ifdef MSDC_MULTITHREADED
void CScsiProtocol::ProcessWriteComplete (TUint8* aAddress, TAny* aPtr)
{
((CScsiProtocol*)aPtr)->iTransport->ProcessReadData(aAddress);
}
void CScsiProtocol::InitializeBufferPointers(TPtr8& aDes1, TPtr8& aDes2) // Todo Change name later - InitializeReadBufferSomething
{
iReadDriveThread->iThreadContext->iBuffer.SetUpReadBuf(aDes1, aDes2);
}
#endif
/**
Called by the transport layer when a packet is available for decoding.
If an error occurs, the sense code is updated and EFalse is returned.
@param aData
@return ETrue if command was decoded and executed successfully
*/
TBool CScsiProtocol::DecodePacket(TPtrC8& aData, TUint aLun)
{
__FNLOG("CScsiProtocol::DecodePacket");
TUint8 command = aData[1];
if (command != ERequestSense)
{
iSenseInfo.SetSense(TSenseInfo::ENoSense);
}
__PRINT2(_L("command = 0x%x lun=%d"), command, aLun);
switch (command)
{
case ETestUnitReady:
HandleUnitReady(aLun);
break;
case ERequestSense:
HandleRequestSense(aData);
break;
case EInquiry:
HandleInquiry(aData, aLun);
break;
case EModeSense:
HandleModeSense(aData, aLun);
break;
case EStartStopUnit:
HandleStartStopUnit(aData, aLun);
break;
case EPreventMediaRemoval:
HandlePreventMediaRemoval(aData, aLun);
break;
case EReadCapacity:
HandleReadCapacity(aData, aLun);
break;
case ERead10:
HandleRead10(aData, aLun);
break;
case EWrite10:
HandleWrite10(aData,aLun);
break;
case EVerify10:
HandleVerify10(aData, aLun);
break;
case EReadFormatCapacities:
HandleReadFormatCapacities(aLun);
break;
default:
iSenseInfo.SetSense(TSenseInfo::EIllegalRequest, TSenseInfo::EInvalidCmdCode);
}
__PRINT1(_L("DecodePacket result = %d"), iSenseInfo.SenseOk());
return(iSenseInfo.SenseOk());
}
/**
Checks if drive ready
@param aLun Logic unit number
@return pointer to drive correspondent to LUN if drive mounted and ready, NULL otherwise
*/
CMassStorageDrive* CScsiProtocol::GetCheckDrive(TUint aLun)
{
__FNLOG("CScsiProtocol::GetCheckDrive");
TInt err=KErrNone;
#ifdef MSDC_MULTITHREADED
// check for deferred errors
if (iWriteDriveThread->DeferredError())
{
iWriteDriveThread->ClearDeferredError();
iDeferredSenseInfo.SetSense(TSenseInfo::EMediumError);
return NULL;
}
#endif
CMassStorageDrive* drive= iDriveManager.Drive(aLun, err);
if (err !=KErrNone || drive == NULL)
{
__PRINT(_L("No drive available\n"));
iSenseInfo.SetSense(TSenseInfo::EIllegalRequest, TSenseInfo::ELuNotSupported);
return NULL;
}
CMassStorageDrive::TMountState mountState = drive->MountState();
if (mountState == CMassStorageDrive::EDisconnected || mountState == CMassStorageDrive::EConnecting)
{
__PRINT(_L("Drive disconnected\n"));
iSenseInfo.SetSense(TSenseInfo::ENotReady,
TSenseInfo::EMediaNotPresent);
return NULL;
}
CMassStorageDrive::TDriveState state = drive->CheckDriveState();
if (state == CMassStorageDrive::EMediaNotPresent || state == CMassStorageDrive::ELocked)
{
__PRINT1(_L("Media not present or locked. (state =0x%X)\n"),state);
iSenseInfo.SetSense(TSenseInfo::ENotReady, TSenseInfo::EMediaNotPresent);
return NULL;
}
if (drive->IsMediaChanged(ETrue)) //reset "media changed" status
{
__PRINT(_L("Media was changed\n"));
// SAM-2 Section 5.9.5 Unit Attention Condition
iSenseInfo.SetSense(TSenseInfo::EUnitAttention, TSenseInfo::ENotReadyToReadyChange);
iDriveManager.Connect(aLun); //publish event to USB app
return NULL;
}
if (mountState == CMassStorageDrive::EDisconnecting)
{
__PRINT(_L("Drive disconnecting\n"));
iSenseInfo.SetSense(TSenseInfo::ENotReady,
TSenseInfo::EMediaNotPresent);
return NULL;
}
return drive;
}
/**
Command Parser for the UNIT READY command (0x00)
@param aLun Logic unit number
@return ETrue if successful,
*/
TBool CScsiProtocol::HandleUnitReady(TUint aLun)
{
__FNLOG("CScsiProtocol::HandleUnitReady");
#ifdef MSDC_MULTITHREADED
iWriteDriveThread->WaitForWriteEmpty();
#endif
return GetCheckDrive(aLun) ? (TBool)ETrue : (TBool)EFalse;
}
/**
Command Parser for the REQUEST SENSE command (0x03)
@return ETrue if successful,
*/
TBool CScsiProtocol::HandleRequestSense(TPtrC8& aData)
{
__FNLOG("CScsiProtocol::HandleRequestSense");
TUint length = aData[5];
__PRINT1(_L("length = %d\n"), length);
TPtr8 writeBuf(NULL, 0);
iTransport->GetCommandBufPtr(writeBuf, KRequestSenseCommandLength);
writeBuf.FillZ(KRequestSenseCommandLength);
TSenseInfo* senseInfo;
#ifdef MSDC_MULTITHREADED
if (!iDeferredSenseInfo.SenseOk())
{
writeBuf[00] = 0x71; //(deferred errors)
senseInfo = &iDeferredSenseInfo;
}
else
{
writeBuf[00] = 0x70; //(current errors)
senseInfo = &iSenseInfo;
}
#else
senseInfo = &iSenseInfo;
writeBuf[00] = 0x70; //(current errors)
#endif
writeBuf[02] = static_cast<TUint8>(senseInfo->iSenseCode & 0x0F);
writeBuf[12] = senseInfo->iAdditional;
writeBuf[13] = senseInfo->iQualifier;
if (length<18 && length >=8)
{
writeBuf.SetLength(length); //length of response code data
writeBuf[07] = TUint8(length - 8); //additional sence length
}
else if (length >= KRequestSenseCommandLength)
{
writeBuf[07] = KRequestSenseCommandLength - 8; // we have max 18 byte to send
}
__PRINT4(_L("Response=0x%x Sense=0x%x, Additional=0x%x, Qualifier=0x%x\n"),
writeBuf[0], writeBuf[02], writeBuf[12], writeBuf[13]);
TPtrC8 writeBuf1 = writeBuf.Left(length);
iTransport->SetupWriteData(writeBuf1);
// clear the sense info
iSenseInfo.SetSense(TSenseInfo::ENoSense);
#ifdef MSDC_MULTITHREADED
iDeferredSenseInfo.SetSense(TSenseInfo::ENoSense);
#endif
return ETrue;
}
/**
Command Parser for the INQUIRY command (0x12)
@param aLun Logic unit number
@return ETrue if successful,
*/
TBool CScsiProtocol::HandleInquiry(TPtrC8& aData, TUint aLun )
{
__FNLOG("CScsiProtocol::HandleInquiry");
TBool cmdDt = aData[2] & 0x2;
TBool evpd = aData[2] & 0x1;
TUint8 page = aData[3];
if (cmdDt || evpd || page || aLun >= KUsbMsMaxDrives)
{
iSenseInfo.SetSense(TSenseInfo::EIllegalRequest, TSenseInfo::EInvalidFieldInCdb);
return EFalse;
}
TPtr8 writeBuf(NULL, 0);
iTransport->GetCommandBufPtr(writeBuf, KInquiryCommandLength);
writeBuf.FillZ(KInquiryCommandLength);
writeBuf[1] = 0x80; // MSB: RMB : Removable
writeBuf[3] = 0x02; // AERC, TrmTsk, NormACA, Response Data Format
writeBuf[4] = 0x1F; // Additional Length
TPtr8 vendorId(&writeBuf[8], 8, 8); // Vendor ID (Vendor Specific/Logged by T10)
vendorId.Fill(' ', 8);
vendorId.Copy(iConfig.iVendorId);
TPtr8 productId(&writeBuf[16], 16, 16); // Product ID (Vendor Specific)
productId.Fill(' ', 16);
productId.Copy(iConfig.iProductId);
TPtr8 productRev(&writeBuf[32], 4, 4); // Product Revision Level (Vendor Specific)
productRev.Fill(' ', 4);
productRev.Copy(iConfig.iProductRev);
TUint length = aData[5];
TPtrC8 writeBuf1 = writeBuf.Left(length);
iTransport->SetupWriteData(writeBuf1);
iSenseInfo.SetSense(TSenseInfo::ENoSense);
return ETrue;
}
/**
Command Parser for the START STOP UNIT command (0x1B)
@param aData command data (started form position 1)
@param aLun Logic unit number
@return ETrue if successful, TFalse otherwise
*/
TBool CScsiProtocol::HandleStartStopUnit(TPtrC8& aData, TUint aLun)
{
__FNLOG("CScsiProtocol::HandleStartStopUnit");
const TUint8 KStartMask = 0x01;
const TUint8 KImmedMask = 0x01;
const TUint8 KLoejMask = 0x02;
TBool immed = aData[2] & KImmedMask ? (TBool)ETrue : (TBool)EFalse;
TBool start = aData[5] & KStartMask ? (TBool)ETrue : (TBool)EFalse;
TBool loej = aData[5] & KLoejMask ? (TBool)ETrue : (TBool)EFalse;
__PRINT2(_L("Data %X %X\n"), aData[2], aData[5]);
__PRINT1(_L("IMMED = %d\n"), immed);
__PRINT1(_L("START = %d\n"), start);
__PRINT1(_L("LOEJ = %d\n"), loej);
TInt err(KErrNone);
if (loej)
{
if(start) //Start unit
{
err = iDriveManager.Connect(aLun);
__PRINT(_L("Load media\n"));
#ifdef USB_TRANSFER_PUBLISHER
iBytesRead[aLun] = 0;
iBytesWritten[aLun] = 0;
#endif
// publish the initial values
iWriteTransferPublisher->DoPublishDataTransferredEvent();
iReadTransferPublisher->DoPublishDataTransferredEvent();
}
else //Stop unit
{
iDriveManager.SetCritical(aLun, EFalse);
err = iDriveManager.Disconnect(aLun);
__PRINT(_L("Unload media\n"));
}
}
if (err !=KErrNone) //actually we have error here only if the LUN is incorrect
{
iSenseInfo.SetSense(TSenseInfo::EIllegalRequest, TSenseInfo::ELuNotSupported);
return EFalse;
}
if (immed)
{
return ETrue;
}
CMassStorageDrive* drive= iDriveManager.Drive(aLun, err);
if (err !=KErrNone || drive == NULL)
{
iSenseInfo.SetSense(TSenseInfo::EIllegalRequest, TSenseInfo::ELuNotSupported);
return EFalse;
}
TInt timeLeft (20); // 1 sec timeout
CMassStorageDrive::TMountState mountState;
do
{
User::After(1000 * 50); // 50 mSec
--timeLeft;
mountState = drive->MountState();
if ((!start && mountState != CMassStorageDrive::EConnected)
||
(start &&
(mountState == CMassStorageDrive::EDisconnecting ||
mountState == CMassStorageDrive::EConnected))
)
{
return ETrue;
}
} while (timeLeft>0);
//timeout happend
iSenseInfo.SetSense(TSenseInfo::ENotReady,
TSenseInfo::EAscLogicalUnitDoesNotRespondToSelection);
return EFalse;
}
/**
Command Parser for the PREVENT/ALLOW MEDIA REMOVAL command (0x1E)
@param aData command data (started form position 1)
@param aLun Logic unit number
@return ETrue if successful.
*/
TBool CScsiProtocol::HandlePreventMediaRemoval(TPtrC8& aData, TUint aLun)
{
__FNLOG("CScsiProtocol::HandlePreventMediaRemoval");
CMassStorageDrive* drive=GetCheckDrive(aLun);
if (drive == NULL)
{
return EFalse;
}
TInt prevent = aData[5] & 0x01;
__PRINT1(_L("prevent = %d\n"), prevent);
iDriveManager.SetCritical(aLun, prevent);
return ETrue;
}
/** Cancel active state, Invoked by transnport when it stops */
TInt CScsiProtocol::Cancel()
{
iDriveManager.SetCritical(CDriveManager::KAllLuns, EFalse);
return KErrNone;
}
TBool CScsiProtocol::HandleReadFormatCapacities(TUint aLun)
/**
* Command Parser for the READ FORMAT CAPACITIES command (0x23)
*
* @return ETrue if successful, else a standard Symbian OS error code.
*/
{
__FNLOG("CScsiProtocol::HandleReadFormatCapacities");
CMassStorageDrive* drive=GetCheckDrive(aLun);
if (drive == NULL)
{
return EFalse;
}
TLocalDriveCapsV4 driveInfo;
TInt err = drive->Caps(driveInfo);
if(err != KErrNone)
{
__PRINT1(_L("Can't obtain drive Caps. Err=%d \n"),err);
iSenseInfo.SetSense(TSenseInfo::ENotReady, TSenseInfo::EMediaNotPresent);
return EFalse;
}
TInt64 driveBlocks = (driveInfo.iDriveAtt & KDriveAttLogicallyRemovable) ? driveInfo.iSize : driveInfo.MediaSizeInBytes();
driveBlocks /= MAKE_TINT64(0, KDefaultBlockSize);
TPtr8 writeBuf(NULL, 0);
iTransport->GetCommandBufPtr(writeBuf, KReadFormatCapacitiesCommandLength);
writeBuf.FillZ(KReadFormatCapacitiesCommandLength);
writeBuf[3] = 0x08; // Capacity List Length
TUint32 numBlocks = I64LOW(driveBlocks);
writeBuf[4] = static_cast<TUint8>((numBlocks & 0xFF000000) >> 24); // Number of blocks
writeBuf[5] = static_cast<TUint8>((numBlocks & 0x00FF0000) >> 16); //
writeBuf[6] = static_cast<TUint8>((numBlocks & 0x0000FF00) >> 8); //
writeBuf[7] = static_cast<TUint8>((numBlocks & 0x000000FF)); //
writeBuf[8] = 0x02; // Formatted size
writeBuf[9] = 0x00; // 512 Byte Blocks
writeBuf[10] = 0x02; //
writeBuf[11] = 0x00; //
TPtrC8 writeBuf1 = writeBuf;
iTransport->SetupWriteData(writeBuf1);
return ETrue;
}
/**
Command Parser for the READ CAPACITY(10) command (0x25)
@param aData command data (started form position 1)
@param aLun Logic unit number
@return ETrue if successful.
*/
TBool CScsiProtocol::HandleReadCapacity(TPtrC8& aData, TUint aLun)
{
__FNLOG("CScsiProtocol::HandleReadCapacity");
CMassStorageDrive* drive=GetCheckDrive(aLun);
if (drive == NULL)
{
return EFalse;
}
TInt pmi = aData[9] & 0x01;
TInt lba = aData[3] | aData[4] | aData[5] | aData[6];
if (pmi || lba) //do not support partial medium indicator
{
iSenseInfo.SetSense(TSenseInfo::EIllegalRequest, TSenseInfo::EInvalidFieldInCdb);
return EFalse;
}
TLocalDriveCapsV4 driveInfo;
TInt err = drive->Caps(driveInfo);
if(err != KErrNone)
{
__PRINT1(_L("Can't obtain drive Caps. Err=%d \n"),err);
iSenseInfo.SetSense(TSenseInfo::ENotReady, TSenseInfo::EMediaNotPresent);
return EFalse;
}
TInt64 driveBlocks = 0;
if (driveInfo.iDriveAtt & KDriveAttLogicallyRemovable)
{
// Partition Access only
driveBlocks = driveInfo.iSize / MAKE_TINT64(0, KDefaultBlockSize);
}
else
{
// whole Media Access
driveBlocks = driveInfo.MediaSizeInBytes() / MAKE_TINT64(0, KDefaultBlockSize) - 1;
}
TPtr8 writeBuf(NULL, 0);
iTransport->GetCommandBufPtr(writeBuf, KReadCapacityCommandLength);
writeBuf.FillZ(KReadCapacityCommandLength);
if (I64HIGH(driveBlocks) == 0)
{
TUint32 numBlocks = I64LOW(driveBlocks);
__PRINT2(_L("Block size=%d, NumBlocks=%d\n"), KDefaultBlockSize, numBlocks);
writeBuf[0] = static_cast<TUint8>((numBlocks & 0xFF000000) >> 24); // Number of blocks
writeBuf[1] = static_cast<TUint8>((numBlocks & 0x00FF0000) >> 16);
writeBuf[2] = static_cast<TUint8>((numBlocks & 0x0000FF00) >> 8);
writeBuf[3] = static_cast<TUint8>((numBlocks & 0x000000FF));
}
else
{
writeBuf[0] = writeBuf[1] = writeBuf[2] = writeBuf[3] = 0xFF; // indicate that size more then )0xFFFFFFFF
}
writeBuf[4] = static_cast<TUint8>((KDefaultBlockSize & 0xFF000000) >> 24); // Block Size
writeBuf[5] = static_cast<TUint8>((KDefaultBlockSize & 0x00FF0000) >> 16);
writeBuf[6] = static_cast<TUint8>((KDefaultBlockSize & 0x0000FF00) >> 8);
writeBuf[7] = static_cast<TUint8>((KDefaultBlockSize & 0x000000FF));
TPtrC8 writeBuf1 = writeBuf;
iTransport->SetupWriteData(writeBuf1);
return KErrNone;
}
/**
Command Parser for the READ10 command (0x28)
@param aData command data (started form position 1)
@param aLun Logic unit number
@return ETrue if successful.
*/
TBool CScsiProtocol::HandleRead10(TPtrC8& aData, TUint aLun)
{
__FNLOG("CScsiProtocol::HandleRead10");
CMassStorageDrive* drive = GetCheckDrive(aLun);
if (drive == NULL)
{
return EFalse;
}
TInt rdProtect = aData[2] >> 5;
if (rdProtect)
{
iSenseInfo.SetSense(TSenseInfo::EIllegalRequest, TSenseInfo::EInvalidFieldInCdb);
return EFalse;
}
const TUint32 lba = LBA(aData);
const TUint16 len = LEN(aData);
__PRINT2(_L("READ(10) : LBA = 0x%x, Length = %d (blocks)\n"), lba, len);
if (!len)
{
return ETrue; // do nothing - this is not an error
}
TLocalDriveCapsV4 driveInfo;
TInt err = drive->Caps(driveInfo);
if (err != KErrNone)
{
__PRINT1(_L("Can't obtain drive Caps. Err=%d \n"), err);
iSenseInfo.SetSense(TSenseInfo::ENotReady, TSenseInfo::EMediaNotPresent);
return EFalse;
}
const TInt64 bOffset = MAKE_TINT64(0, lba) * KDefaultBlockSize;
const TInt bLength = len * KDefaultBlockSize;
const TInt64 theEnd = bOffset + MAKE_TINT64(0, bLength);
const TInt64 mediaSize = (driveInfo.iDriveAtt & KDriveAttLogicallyRemovable) ? driveInfo.iSize : driveInfo.MediaSizeInBytes() ;
if (theEnd > mediaSize) //check if media big enough for this request
{
__PRINT(_L("err - Request ends out of media\n"));
iSenseInfo.SetSense(TSenseInfo::EIllegalRequest, TSenseInfo::ELbaOutOfRange);
return EFalse;
}
#ifdef MSDC_MULTITHREADED
iWriteDriveThread->WaitForWriteEmpty();
// check if our buffer can hold requested data
if (iReadDriveThread->iThreadContext->MaxBufferLength() < bLength)
{
__PRINT(_L("err - Buffer too small\n"));
iSenseInfo.SetSense(TSenseInfo::EIllegalRequest, TSenseInfo::EInvalidFieldInCdb);
return EFalse;
}
// Optimisation note : If the host is reading from sectors it just wrote to,
// then we have to force a cache-miss so that the real data is read from the
// drive. It would be possible to service the read from the write buffers,
// but as the host is probably trying to verify the write data, we don't do
// that for now.
if (!iReadDriveThread->ReadDriveData(drive, bOffset, bLength, iWriteDriveThread->IsRecentlyWritten(bOffset,bLength)))
{
iSenseInfo.SetSense(TSenseInfo::ENotReady, TSenseInfo::EMediaNotPresent);
return EFalse;
}
iWriteDriveThread->SetCommandWrite10(EFalse);
TBlockDesc* &desc = iReadDriveThread->iThreadContext->iBuffer.iDescReadPtr;
TPtrC8 writeBuf1 = desc->iBuf;
#else
TPtr8 writeBuf(NULL, 0);
iTransport->GetReadDataBufPtr(writeBuf);
// check if our buffer can hold requested data
if (writeBuf.MaxLength() < bLength)
{
__PRINT(_L("err - Buffer too small\n"));
iSenseInfo.SetSense(TSenseInfo::EIllegalRequest, TSenseInfo::EInvalidFieldInCdb);
return EFalse;
}
err = drive->Read(bOffset, bLength, writeBuf, drive->IsWholeMediaAccess());
if (err != KErrNone)
{
__PRINT1(_L("Read failed, err=%d\n"), err);
iSenseInfo.SetSense(TSenseInfo::ENotReady, TSenseInfo::EMediaNotPresent);
return EFalse;
}
TPtrC8 writeBuf1 = writeBuf;
#endif // MSDC_MULTITHREADED
#ifdef USB_TRANSFER_PUBLISHER
iBytesRead[aLun] += writeBuf1.Length();
#endif
iReadTransferPublisher->StartTimer();
// Set up data write to the host
iTransport->SetupWriteData(writeBuf1);
return ETrue;
}
/**
Command Parser for the WRITE(10) command (0x2A)
@param aData command data (started form position 1)
@param aLun Logic unit number
@return ETrue if successful.
*/
TBool CScsiProtocol::HandleWrite10(TPtrC8& aData, TUint aLun)
{
__FNLOG("CScsiProtocol::HandleWrite10");
CMassStorageDrive* drive = GetCheckDrive(aLun);
if (drive == NULL)
{
return EFalse;
}
TInt wrProtect = aData[2] >> 5;
if (wrProtect)
{
iSenseInfo.SetSense(TSenseInfo::EIllegalRequest, TSenseInfo::EInvalidFieldInCdb);
return EFalse;
}
const TUint32 lba = LBA(aData);
const TUint16 len = LEN(aData);
__PRINT2(_L("WRITE(10) : LBA = 0x%x, Length = %d (blocks)\n"), lba, len);
if (!len)
{
return ETrue; // do nothing - this is not an error
}
TLocalDriveCapsV4 driveInfo;
TInt err = drive->Caps(driveInfo);
if (err != KErrNone)
{
__PRINT1(_L("Can't obtain drive Caps. Err=%d \n"), err);
iSenseInfo.SetSense(TSenseInfo::ENotReady, TSenseInfo::EMediaNotPresent);
return EFalse;
}
if (driveInfo.iMediaAtt & KMediaAttWriteProtected ||
driveInfo.iMediaAtt & KMediaAttLocked)
{
iSenseInfo.SetSense(TSenseInfo::EDataProtection, TSenseInfo::EWriteProtected);
return EFalse;
}
const TInt64 bOffset = MAKE_TINT64(0, lba) * KDefaultBlockSize;
iBytesRemain = len * KDefaultBlockSize;
const TInt64 theEnd = bOffset + MAKE_TINT64(0, iBytesRemain);
const TInt64 mediaSize = (driveInfo.iDriveAtt & KDriveAttLogicallyRemovable) ? driveInfo.iSize : driveInfo.MediaSizeInBytes() ;
if (theEnd > mediaSize) //check if media big enough for this request
{
__PRINT(_L("err - Request ends out of media\n"));
iSenseInfo.SetSense(TSenseInfo::EIllegalRequest, TSenseInfo::ELbaOutOfRange);
return EFalse;
}
#ifdef MSDC_MULTITHREADED
iWriteDriveThread->SetCommandWrite10(ETrue);
#endif
// Set up the first request for data from the host - either
// KMaxBufSize or the entire transfer length, whichever is smallest.
TUint thisLength = (iBytesRemain > KMaxBufSize) ? KMaxBufSize : iBytesRemain;
thisLength = (thisLength > iMediaWriteSize) ? iMediaWriteSize : thisLength;
iOffset = bOffset;
iLastCommand = EWrite10;
iLastLun = aLun;
iWriteTransferPublisher->StartTimer();
iTransport->SetupReadData(thisLength);
return ETrue;
}
/**
Command Parser for the VERIFY(10) command (0x2F)
@param aData command data (started form position 1)
@param aLun Logic unit number
@return ETrue if successful.
*/
TBool CScsiProtocol::HandleVerify10(TPtrC8& aData, TUint aLun)
{
__FNLOG("CScsiProtocol::HandleVerify10");
CMassStorageDrive* drive = GetCheckDrive(aLun);
if (drive == NULL)
{
return EFalse;
}
TInt vrProtect = aData[2] >> 5;
if (vrProtect)
{
iSenseInfo.SetSense(TSenseInfo::EIllegalRequest, TSenseInfo::EInvalidFieldInCdb);
return EFalse;
}
const TUint32 lba = LBA(aData);
const TUint16 len = LEN(aData);
__PRINT2(_L("VERIFY(10) : LBA = %d, Length = %d (blocks)\n"), lba, len);
TInt bytChk = aData[2] & 0x02;
if (!len)
{
return ETrue; // do nothing - this is not an error
}
TLocalDriveCapsV4 driveInfo;
TInt err = drive->Caps(driveInfo);
if (err != KErrNone)
{
__PRINT1(_L("Can't obtain drive Caps. Err=%d \n"), err);
iSenseInfo.SetSense(TSenseInfo::ENotReady, TSenseInfo::EMediaNotPresent);
return EFalse;
}
const TInt64 bOffset = MAKE_TINT64(0, lba) * KDefaultBlockSize;
const TInt bLength = len * KDefaultBlockSize;
const TInt64 theEnd = bOffset + MAKE_TINT64(0, bLength);
const TInt64 mediaSize = (driveInfo.iDriveAtt & KDriveAttLogicallyRemovable) ? driveInfo.iSize : driveInfo.MediaSizeInBytes() ;
// check if media big enough for this request
if (theEnd > mediaSize)
{
iSenseInfo.SetSense(TSenseInfo::EIllegalRequest, TSenseInfo::ELbaOutOfRange);
return EFalse;
}
// check if our buffer can hold requested data
#ifdef MSDC_MULTITHREADED
if (iWriteDriveThread->iThreadContext->MaxBufferLength() < bLength)
#else
TPtr8 writeBuf(NULL, 0);
iTransport->GetReadDataBufPtr(writeBuf);
if (writeBuf.MaxLength() < bLength)
#endif
{
iSenseInfo.SetSense(TSenseInfo::EIllegalRequest, TSenseInfo::EInvalidFieldInCdb);
return EFalse;
}
if (!bytChk)
{
// BYTCHK==0 : Perform a medium verification with no data comparison and not transfer any data from the application client data-out buffer.
// The device should attempt to read from the specified locations
#ifdef MSDC_MULTITHREADED
TPtr8 writeBuf = iWriteDriveThread->iThreadContext->GetReadBuffer(bLength);
#else
writeBuf.SetLength(bLength);
#endif
err = drive->Read(bOffset, bLength, writeBuf, drive->IsWholeMediaAccess());
if (err != KErrNone)
{
iSenseInfo.SetSense(TSenseInfo::EMisCompare);
return EFalse;
}
return ETrue;
}
// BYTCHK==1 : perform a byte-by-byte comparison of user data read from the medium & user data transferred from the application client data-out buffer.
// The host sends data in the data-transport phase, and the device should verify that the received data matches what is stored in the device.
iOffset = bOffset;
iLastCommand = EVerify10;
iLastLun = aLun;
iTransport->SetupReadData(bLength);
return ETrue;
}
/**
Called by the transport when the requested data has been read or an error has
occurred during the read.
@param aError Indicate if an error occurs during reading data by transport.
@return KErrAbort if command processing is complete but has failed,
KErrCompletion if sufficient data is available in the buffer to process
the transfer immediately, KErrNotReady if insufficient data is
available in the buffer so the transport should wait for it to arrive,
KErrNone if command processing is complete and was successful.
*/
TInt CScsiProtocol::ReadComplete(TInt aError)
{
__FNLOG("CScsiProtocol::ReadComplete");
__PRINT1(_L("Error = 0x%X \n"), aError);
const TInt64 bOffset = iOffset;
TUint8 lastCommand = iLastCommand;
TUint lastLun = iLastLun;
iOffset = 0;
iLastCommand = EUndefinedCommand;
iLastLun = KUndefinedLun;
__PRINT1(_L("Last command was: %s\n"),
(lastCommand == EUndefinedCommand) ? _S("Undefined") :
((lastCommand == EWrite10) ? _S("EWrite10") :
((lastCommand == EVerify10) ? _S("EVerify10") :
_S("Unknown"))));
if (aError != KErrNone ||
lastCommand == EUndefinedCommand ||
lastLun == KUndefinedLun)
{
iSenseInfo.SetSense(TSenseInfo::EAbortedCommand);
return KErrAbort;
}
CMassStorageDrive* drive = GetCheckDrive(lastLun);
if (drive == NULL)
{
return KErrAbort;
}
if (lastCommand == EWrite10)
{
TPtrC8 writeBuf(NULL, 0);
iTransport->GetWriteDataBufPtr(writeBuf);
#ifdef USB_TRANSFER_PUBLISHER
iBytesWritten[lastLun] += writeBuf.Length();
#endif
#ifdef MSDC_MULTITHREADED
TInt err = iWriteDriveThread->WriteDriveData(drive, bOffset, writeBuf, ProcessWriteComplete, this);
if (err != KErrNone)
{
iDeferredSenseInfo.SetSense(TSenseInfo::EMediumError);
}
TUint thisLength = iWriteDriveThread->WriteBufferLength();
#else
#ifdef MEASURE_AND_DISPLAY_WRITE_TIME
RDebug::Print(_L("SCSI: writing %d bytes\n"), writeBuf.Length());
TTime t0, t1;
t0.HomeTime();
#else
__PRINT1(_L("SCSI: writing %d bytes\n"), writeBuf.Length());
#endif
#ifdef INJECT_ERROR
if (writeBuf[0] == '2')
{
writeBuf[0] = 'x';
RDebug::Printf("Injecting error");
}
RDebug::Printf("%08lx %x [%x] [%x]", bOffset,writeBuf.Length(),
writeBuf[0],
writeBuf[writeBuf.Length()-1]);
#endif
TInt err = drive->Write(bOffset, writeBuf, drive->IsWholeMediaAccess());
#ifdef INJECT_ERROR
if (writeBuf[0] == 'x')
{
err = KErrUnknown;
}
#endif
#ifdef MEASURE_AND_DISPLAY_WRITE_TIME
t1.HomeTime();
const TTimeIntervalMicroSeconds time = t1.MicroSecondsFrom(t0);
const TUint time_ms = I64LOW(time.Int64() / 1000);
RDebug::Print(_L("SCSI: write took %d ms\n"), time_ms);
#endif
if (err != KErrNone)
{
__PRINT1(_L("Error after write = 0x%X \n"), err);
iSenseInfo.SetSense(TSenseInfo::EAbortedCommand);
return KErrAbort;
}
TUint thisLength = writeBuf.Length();
#endif // MSDC_MULTITHREADED
iOffset = bOffset + MAKE_TINT64(0, thisLength);
iBytesRemain -= thisLength;
if ((TInt)iBytesRemain > 0)
{
// More data is expected - set up another request to read from the host
iLastCommand = EWrite10;
iLastLun = lastLun;
TUint minLength = (iBytesRemain < iMediaWriteSize) ? iBytesRemain : iMediaWriteSize;
TUint bytesAvail = iTransport->BytesAvailable() & ~(KDefaultBlockSize-1);
TBool wait = EFalse;
thisLength = bytesAvail ? bytesAvail : minLength;
if (thisLength < minLength)
{
// Not enough data is available at the transport to satisfy the request,
// so return KErrNotReady to indicate that the transport should wait.
thisLength = minLength;
wait = ETrue;
}
thisLength = (thisLength > KMaxBufSize) ? KMaxBufSize : thisLength;
iTransport->SetupReadData(thisLength);
return wait ? KErrNotReady : KErrCompletion;
}
}
else if (lastCommand == EVerify10)
{
HBufC8* hostData = NULL;
TPtrC8 writeBuf(NULL, 0);
iTransport->GetWriteDataBufPtr(writeBuf);
#ifdef MSDC_MULTITHREADED
TRAPD(err, hostData = HBufC8::NewL(writeBuf.Length()));
#else
TRAPD(err, hostData = HBufC8::NewL(writeBuf.Length()));
#endif
if (err != KErrNone || hostData == NULL)
{
iSenseInfo.SetSense(TSenseInfo::EAbortedCommand, TSenseInfo::EInsufficientRes);
return KErrAbort;
}
#ifdef MSDC_MULTITHREADED
// copy the data
*hostData = writeBuf;
TPtr8 readBuf = iWriteDriveThread->iThreadContext->GetReadBuffer();
err = drive->Read(bOffset, writeBuf.Length(), readBuf, drive->IsWholeMediaAccess());
if (err == KErrNone)
{
err = (hostData->Compare(readBuf) == 0) ? KErrNone : KErrCorrupt;
}
#else
*hostData = writeBuf;
TPtr8 readBuf((TUint8*) writeBuf.Ptr(), writeBuf.Length());
err = drive->Read(bOffset, writeBuf.Length(), readBuf, drive->IsWholeMediaAccess());
if (err == KErrNone)
{
err = (hostData->Compare(readBuf) == 0) ? KErrNone : KErrCorrupt;
}
#endif
if (err != KErrNone)
{
iSenseInfo.SetSense(TSenseInfo::EMisCompare);
}
delete hostData;
}
else // unknown command
{
iSenseInfo.SetSense(TSenseInfo::EAbortedCommand);
}
return iSenseInfo.SenseOk() ? KErrNone : KErrAbort;
}
/**
Command Parser for the MODE SENSE(06) command (0x1A)
@return ETrue if successful.
*/
TBool CScsiProtocol::HandleModeSense(TPtrC8& aData, TUint aLun)
{
__FNLOG("CScsiProtocol::HandleModeSense");
TInt pageCode = aData[3] & 0x3F;
TUint8 pageControl= static_cast<TUint8>(aData[3] >>6);
// reserve 4 bytes for Length, Media type, Device-specific parameter and Block descriptor length
if (pageCode != KAllPages || pageControl == KChangeableValues)
{
__PRINT(_L("TSenseInfo::EIllegalRequest,TSenseInfo::EInvalidFieldInCdb"));
iSenseInfo.SetSense(TSenseInfo::EIllegalRequest,TSenseInfo::EInvalidFieldInCdb);
return EFalse;
}
TPtr8 writeBuf(NULL, 0);
iTransport->GetCommandBufPtr(writeBuf, KModeSenseCommandLength);
writeBuf.FillZ(KModeSenseCommandLength);
if (pageControl != KDefaultValues)
{
//check if drive write protected
CMassStorageDrive* drive=GetCheckDrive(aLun);
if (drive == NULL)
{
__PRINT(_L("drive == null"));
return EFalse;
}
TLocalDriveCapsV4 driveInfo;
TInt err = drive->Caps(driveInfo);
if (err != KErrNone)
{
__PRINT(_L("TSenseInfo::ENotReady"));
iSenseInfo.SetSense(TSenseInfo::ENotReady, TSenseInfo::EMediaNotPresent);
return EFalse ;
}
if (driveInfo.iMediaAtt & KMediaAttWriteProtected)
{
writeBuf[2] = 1<<7; // set SWP bit at the Device Specific parameters
}
}
writeBuf[0]=3; //Sending only Mode parameter header
TPtrC8 writeBuf1 = writeBuf;
iTransport->SetupWriteData(writeBuf1);
return (iSenseInfo.SenseOk());
}