--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/smassstorage/scsiprot.cpp Thu Dec 17 09:24:54 2009 +0200
@@ -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<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());
+ }
+
+