userlibandfileserver/fileserver/smassstorage/scsiprot.cpp
changeset 0 a41df078684a
child 80 597aaf25e343
--- /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<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());
+	}
+
+