diff -r 000000000000 -r a41df078684a userlibandfileserver/fileserver/smassstorage/scsiprot.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/userlibandfileserver/fileserver/smassstorage/scsiprot.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,1281 @@ +// 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((x[3] << 24) | (x[4] << 16) | (x[5] << 8) | x[6]) +#define LEN(x) static_cast((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(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(aSenseCode); + iAdditional = static_cast(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(aSenseCode); + iAdditional = static_cast(aAdditional); + iQualifier = static_cast(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(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((numBlocks & 0xFF000000) >> 24); // Number of blocks + writeBuf[5] = static_cast((numBlocks & 0x00FF0000) >> 16); // + writeBuf[6] = static_cast((numBlocks & 0x0000FF00) >> 8); // + writeBuf[7] = static_cast((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((numBlocks & 0xFF000000) >> 24); // Number of blocks + writeBuf[1] = static_cast((numBlocks & 0x00FF0000) >> 16); + writeBuf[2] = static_cast((numBlocks & 0x0000FF00) >> 8); + writeBuf[3] = static_cast((numBlocks & 0x000000FF)); + } + else + { + writeBuf[0] = writeBuf[1] = writeBuf[2] = writeBuf[3] = 0xFF; // indicate that size more then )0xFFFFFFFF + } + + writeBuf[4] = static_cast((KDefaultBlockSize & 0xFF000000) >> 24); // Block Size + writeBuf[5] = static_cast((KDefaultBlockSize & 0x00FF0000) >> 16); + writeBuf[6] = static_cast((KDefaultBlockSize & 0x0000FF00) >> 8); + writeBuf[7] = static_cast((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(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()); + } + +