// Copyright (c) 2004-2010 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:
//

/**
 @file
 @internalTechnology
*/
#include "cbulkonlytransport.h"
#include "cbulkonlytransportusbcldd.h"
#include "cbulkonlytransportusbcscldd.h"
#include "usbmsshared.h"
#include "massstoragedebug.h"
#include "cusbmassstorageserver.h"


//CBW offsets
LOCAL_D const TInt KCbwSignatureOffset 			= 0;
LOCAL_D const TInt KCbwTagOffset 				= 4;
LOCAL_D const TInt KCbwDataTransferLengthOffset = 8;
LOCAL_D const TInt KCbwFlagOffset 				= 12;
LOCAL_D const TInt KCbwLunOffset				= 13;
LOCAL_D const TInt KCbwCbLengthOffset  			= 14;

LOCAL_D const TInt KMaxCbwcbLength 				= 16;

// CSW offsets
LOCAL_D const TInt KCswSingnatureOffset 		= 0;
LOCAL_D const TInt KCswTagOffset 				= 4;
LOCAL_D const TInt KCswDataResidueOffset 		= 8;
LOCAL_D const TInt KCswStatusOffset 			= 12;



/**
 This function unpacks into the TUsbRequestHdr class from a descriptor with
 the alignment that would be introduced on the USB bus.

 @param aBuffer Input buffer
 @param aTarget Unpacked header.
 @return Error.
 */
TInt TUsbRequestHdr::Decode(const TDesC8& aBuffer)

	{
	if (aBuffer.Length() < static_cast<TInt>(KRequestHdrSize))
		{
        __PRINT1(_L("TUsbRequestHdr::Decode buffer invalid length %d"), aBuffer.Length());
		return KErrGeneral;
		}

	iRequestType = aBuffer[0];
	iRequest = static_cast<TEp0Request>(aBuffer[1]);
	iValue	 = static_cast<TUint16>(aBuffer[2] + (aBuffer[3] << 8));
	iIndex	 = static_cast<TUint16>(aBuffer[4] + (aBuffer[5] << 8));
	iLength  = static_cast<TUint16>(aBuffer[6] + (aBuffer[7] << 8));
    __PRINT5(_L("type=%d request=%d value=%d index=%d length=%d"), iRequestType,iRequest,iValue,iIndex,iLength);

	return KErrNone;
	}


/**
This function determines whether data is required by the host in response
to a message header.

@return TBool	Flag indicating whether a data response required.
*/
TBool TUsbRequestHdr::IsDataResponseRequired() const

	{
	return (iRequestType & 0x80) ? (TBool)ETrue : (TBool)EFalse;
	}

//-------------------------------------
/**
Create an object of a class derived from CBulkOnlyTransport (default to CBulkOnlyTransportUsbcLdd object)
@param aNumDrives - The number of drives available for MS
@param aController - reference to the parent
@return pointer to newly created derived class object
*/
CBulkOnlyTransport* CBulkOnlyTransport::NewL(TInt aNumDrives,CUsbMassStorageController& aController)
	{
	__FNLOG("CBulkOnlyTransport::NewL()");
	
	return NewL(aNumDrives,aController, (CUsbMassStorageController::TTransportldd) 1);
	}

/**
Create an object of a class derived from CBulkOnlyTransport 
@param aNumDrives - The number of drives available for MS
@param aController - reference to the parent
@param aTransportLddFlag - Type of usb client ldd
@return pointer to newly created derived class object
*/
CBulkOnlyTransport* CBulkOnlyTransport::NewL(TInt aNumDrives,CUsbMassStorageController& aController, CUsbMassStorageController::TTransportldd aTransportLddFlag)
	{
	__FNLOG("CBulkOnlyTransport::NewL()");

	if (aNumDrives <=0 || static_cast<TUint>(aNumDrives) > KUsbMsMaxDrives)
		{
		User::Leave(KErrArgument);
		}

	CBulkOnlyTransportUsbcScLdd* scTransport;
	CBulkOnlyTransportUsbcLdd* nonscTransport;
	switch (aTransportLddFlag)
		{
		case 1: 
				nonscTransport = new(ELeave) CBulkOnlyTransportUsbcLdd(aNumDrives, aController);
				return nonscTransport;

		case 2: 
				scTransport = new(ELeave) CBulkOnlyTransportUsbcScLdd(aNumDrives, aController);
				return scTransport;
		default:
				return NULL;

		}
	}


TInt CBulkOnlyTransport::InitialiseTransportL(TInt aTransportLddFlag)
	{
	__FNLOG("CBulkOnlyTransportUsbcScLdd::InitialiseTransportL()");
	TInt ret = KErrNone;
	MTransportBase* transport;
	iController.GetTransport(transport);
	switch (aTransportLddFlag)
		{
		case 2: 
				ret = ((CBulkOnlyTransportUsbcScLdd*) transport)->Ldd().Open(0);
				if (ret != KErrNone)
					{
					return ret;
					}
				else
					{
					((CBulkOnlyTransportUsbcScLdd*) transport)->Ldd().Close();		
					CleanupStack::PushL(transport);
					((CBulkOnlyTransportUsbcScLdd*) transport)->ConstructL();
					CleanupStack::Pop(transport);
					return ret;
					}
		case 1:
				ret = ((CBulkOnlyTransportUsbcLdd*) transport)->Ldd().Open(0);
				if (ret != KErrNone)
					{
					return ret;
					}
				else
					{
					((CBulkOnlyTransportUsbcLdd*) transport)->Ldd().Close();
					CleanupStack::PushL(transport);
					((CBulkOnlyTransportUsbcLdd*) transport)->ConstructL();
					CleanupStack::Pop(transport);
					return ret;
					}
		default:
				return KErrNotFound;
		}
	}	

/**
c'tor
@param aNumDrives - The number of drives available for MS
@param aController - reference to the parent
*/
CBulkOnlyTransport::CBulkOnlyTransport(TInt aNumDrives,CUsbMassStorageController& aController):
	CActive(EPriorityStandard),
	iMaxLun(aNumDrives-1),
	iController(aController),
	iStallAllowed(ETrue),
	iInterfaceConfigured(EFalse),
	iCommandBufPtr(NULL,0),
	iDataBufPtr(NULL,0),
	iCswBufPtr(NULL,0),
	iPaddingBufPtr(NULL,0),
	iWriteBufPtr(NULL,0),
	iReadBufPtr(NULL, 0),
	iCbwBufPtr(NULL,0)
	{
	__FNLOG("CBulkOnlyTransport::CBulkOnlyTransport");
	}

/**
Destructor
*/
CBulkOnlyTransport::~CBulkOnlyTransport()
	{
	}


/**
Called by the protocol after processing the packet to indicate that more data is required.

@param aData reference to the data buffer.
*/
void CBulkOnlyTransport::SetupReadData(TUint aLength)
	{
	__FNLOG("CBulkOnlyTransport::SetupReadData");
	__PRINT1(_L("Length = %d  (bytes)\n"), aLength);
	iBufSize = aLength;
	iReadSetUp = ETrue;
	}


/**
Called by the protocol after processing the packet to indicate that data should be written to the host.

@param aData reference to the data buffer.
*/
void CBulkOnlyTransport::SetupWriteData(TPtrC8& aData)
	{
	__FNLOG("CBulkOnlyTransport::SetupWriteData");
	__PRINT1(_L("Length = %d  (bytes)\n"), aData.Length());
	iWriteBufPtr.Set(aData);
	iWriteSetUp = ETrue;
	}


TInt CBulkOnlyTransport::Start()
	{
	__FNLOG("CBulkOnlyTransport::Start");

	TInt err = KErrNone;

	if (!iProtocol)
		{
		return KErrBadHandle;   //protocol should be set up before start
		}

	if (IsActive())
		{
		__PRINT(_L("CBulkOnlyTransport::Start  - active before start!\n"));
		return KErrInUse;
		}

	if ((err = SetupConfigurationDescriptor()) 	!= KErrNone ||
		(err = SetupInterfaceDescriptors())		!= KErrNone )
		{
		__PRINT(_L("CBulkOnlyTransport::Start  - Error during descriptors setup!\n"));
		return err;
		}

	AllocateEndpointResources();
	ActivateDeviceStateNotifier();  // activate notifier wich will wait until USB became configured
	TUsbcDeviceState deviceStatus = EUsbcDeviceStateDefault;
	err = GetDeviceStatus(deviceStatus);
	__PRINT1(_L("CBulkOnlyTransport::Start - Device status = %d\n"), deviceStatus);
	if (err == KErrNone && deviceStatus == EUsbcDeviceStateConfigured)
		{
		__PRINT(_L("CBulkOnlyTransport::Start  - Starting bulk only transport\n"));
		err = HwStart();
		}

#ifdef MSDC_MULTITHREADED
	TPtr8 aDes1(NULL,0);
	TPtr8 aDes2(NULL,0);
	GetBufferPointers(aDes1, aDes2);
	iProtocol->InitializeBufferPointers(aDes1, aDes2); // have to pass pointer to memory not offsets to initialise TPtr, and lengths
#endif

	iInterfaceConfigured = ETrue;
	return err;
	}

TInt CBulkOnlyTransport::HwStart(TBool aDiscard)
	{
	__FNLOG("CBulkOnlyTransport::HwStart");

    TInt lun = MaxLun();
    do
        {
        Controller().DriveManager().Connect(lun);
        }
    while(--lun >= 0);

	TInt res = StartControlInterface();

	iCurrentState = ENone;
	iWriteSetUp=EFalse;
	iReadSetUp=EFalse;
	iStarted = ETrue;

    if (aDiscard)
		{
		FlushData();
		}

	ReadCBW();
	return res;
	}


TInt CBulkOnlyTransport::HwStop()
	{
	__FNLOG("CBulkOnlyTransport::HwStop");
	if (iStarted)
		{
        StopBulkOnlyEndpoint();
		CancelControlInterface();
		iStarted = EFalse;
		}
	return KErrNone;
	}


void CBulkOnlyTransport::StopBulkOnlyEndpoint()
	{
	__FNLOG("CBulkOnlyTransport::StopBulkOnlyEndpoint");

    TInt lun = MaxLun();
    do
        {
        Controller().DriveManager().Disconnect(lun);
        }
    while(--lun >= 0);
	Cancel();
	iProtocol->Cancel();
	}


TInt CBulkOnlyTransport::HwSuspend()
	{
	__FNLOG("CBulkOnlyTransport::HwSuspend");

	TInt lun = MaxLun();
	do
		{
		Controller().DriveManager().Disconnect(lun);
		}
	while(--lun >= 0);

	return KErrNone;
	}


TInt CBulkOnlyTransport::HwResume()
	{
	__FNLOG("CBulkOnlyTransport::HwResume");

    TInt lun = MaxLun();
    do
        {
        Controller().DriveManager().Connect(lun);
        }
    while(--lun >= 0);

	return KErrNone;
	}

/**
Stops the Bulk Only Transport
*/
TInt CBulkOnlyTransport::Stop()
	{
	__FNLOG("CBulkOnlyTransport::Stop");
	CancelControlInterface();
	CancelDeviceStateNotifier();
	Cancel();
	if  (iInterfaceConfigured)
		{
		ReleaseInterface();
		SetupConfigurationDescriptor(ETrue);
		}
	iCurrentState = ENone;
	iInterfaceConfigured = EFalse;

	return KErrNone;
	}



void CBulkOnlyTransport::DoCancel()
	{
	__FNLOG("CBulkOnlyTransport::DoCancel");
	CancelReadWriteRequests();
	}


void CBulkOnlyTransport::Activate(TInt aReason)
    {
    SetActive();
    TRequestStatus* r = &iStatus;
    User::RequestComplete(r, aReason);
    }


void CBulkOnlyTransport::RunL()
	{
	__FNLOG("CBulkOnlyTransport::RunL");
	if (iStatus != KErrNone)
		{
		__PRINT1(_L("Error %d in RunL, halt endpoints \n"), iStatus.Int());
		SetPermError(); //halt endpoints for reset recovery
		return;
		}
	switch (iCurrentState)
		{
		case EWaitForCBW:
			__PRINT(_L("EWaitForCBW"));
			ProcessCbwEvent();
			break;

		case EWritingData:
            __PRINT(_L("EWritingData"));
			iWriteSetUp = EFalse;  //the buffer was used

			if (iDataResidue && iStallAllowed)
				{
				StallEndpointAndWaitForClear();
				}

			SendCSW(iCbwTag, iDataResidue, iCmdStatus);
			break;

		case EReadingData:
			{
			__PRINT(_L("EReadingData"));

			ProcessReadingDataEvent();
			}
			break;

		case ESendingCSW:
			__PRINT(_L("ESendingCSW"));
			ReadCBW();
			break;

        case EPermErr:
			__PRINT(_L("EPermErr"));
			StallEndpointAndWaitForClear();
            break;

        default:
			SetPermError();		// unexpected state
		}
	}


/**
Decode the CBW received from the host via OutEndpoint

- If the header is valid, the data content is passed to the parser.
- Depending on the command, more data may be transmitted/received.
- ...or the CSW is sent (if not a data command).

*/
void CBulkOnlyTransport::DecodeCBW()
	{
	__FNLOG("CBulkOnlyTransport::DecodeCBW");

	SetCbwPtr();

	if (!CheckCBW())  //check if CBW valid and meaningful
		{
        // CBW not valid or meaningful
        // Specification says: "If the CBW is not valid, the device shall STALL
        // the Bulk-In pipe. Also, the device shall either STALL the Bulk-Out pipe,
        // or the device shall accept and discard any Bulk-Out data. The device
        // shall maintain this state until a Reset Recovery."
        // Here we keep bulk-in ep stalled and ignore bulk-out ep.
		SetPermError();
		ExpireData((TAny*) (iCbwBufPtr.Ptr()));
		return;
		}

	TPtrC8 aData;
	aData.Set(&iCbwBufPtr[KCbwCbLengthOffset], KMaxCbwcbLength+1);    //prepare data for protocol starting form Length
	TUint8 lun = static_cast<TUint8>(iCbwBufPtr[13] & 0x0f);

	iCbwTag  =	static_cast<TUint32>(iCbwBufPtr[KCbwTagOffset])		|
				static_cast<TUint32>(iCbwBufPtr[KCbwTagOffset+1])	<<8 |
				static_cast<TUint32>(iCbwBufPtr[KCbwTagOffset+2])	<<16|
				static_cast<TUint32>(iCbwBufPtr[KCbwTagOffset+3])	<<24;

	TInt i = KCbwDataTransferLengthOffset;
	TUint hostDataLength = 	static_cast<TUint32>(iCbwBufPtr[i  ])		|
							static_cast<TUint32>(iCbwBufPtr[i+1]) <<8 	|
							static_cast<TUint32>(iCbwBufPtr[i+2]) <<16	|
							static_cast<TUint32>(iCbwBufPtr[i+3]) <<24;

	TBool dataToHost = iCbwBufPtr[KCbwFlagOffset] & 0x80;

	__PRINT4(_L("lun =%d, hostDataLength=%d, CBWtag = 0x%x, dataToHost=%d\n"), lun, hostDataLength, iCbwTag, dataToHost);
	//////////////////////////////////////////////
	TBool ret = iProtocol->DecodePacket(aData, lun);
	//////////////////////////////////////////////
	ExpireData((TAny*) (iCbwBufPtr.Ptr()));


	iStallAllowed = ETrue;

	if (!ret)
		{
		__PRINT(_L("Command Failed\n"));
		iCmdStatus = ECommandFailed;
		}
	else
		{
		__PRINT(_L("Command Passed\n"));
		iCmdStatus = ECommandPassed;
		}

	if (hostDataLength)    // Host  expected data transfer
		{
		if (dataToHost)  // send data to host
			{
			if (!iWriteSetUp) //write buffer was not set up
				{
				__PRINT(_L("Write buffer was not setup\n"));
				iDataResidue =hostDataLength;
				__PRINT1(_L("DataResidue (write to host)=%d\n"),iDataResidue);

//------------------------------------
				if (hostDataLength <= KBOTMaxBufSize)
					{
					__PRINT(_L("Case 4 or 8\n"));
					SetPaddingBufPtr(hostDataLength);
					iPaddingBufPtr.FillZ(hostDataLength);
					TPtrC8 ptr(NULL, 0);
					ptr.Set((TUint8*)iPaddingBufPtr.Ptr(), hostDataLength);
					WriteData(iStatus, ptr, hostDataLength, EFalse);
					iStallAllowed = EFalse;
					if (iReadSetUp)  //read buffer WAS set up - case (8)
						{
						__PRINT(_L("It is Case 8\n"));
						iCmdStatus = EPhaseError;
						}
					return;
					}
				else
//------------------------------------
//					Use next block instead of StallEndpointAndWaitForClear(InEndpoint);
					{
					SetPaddingBufPtr(hostDataLength);
					iPaddingBufPtr.FillZ(KBOTMaxBufSize);
					TUint c =0;
					TRequestStatus status;
					while (c<hostDataLength)
						{
						TInt len;
						if (hostDataLength - c >  KBOTMaxBufSize)
							{
							len = KBOTMaxBufSize;
							}
						else
							{
							len = hostDataLength - c;
							}

							TPtrC8 ptr(NULL, 0);
							ptr.Set((TUint8*)iPaddingBufPtr.Ptr(), len);
							WriteUsb(status, ptr, len);
							User::WaitForRequest(status);
							c +=  KBOTMaxBufSize;
						}
					}

				if (iReadSetUp)  //read buffer WAS set up - case (8)
					{
					__PRINT(_L("Case 8\n"));
					SendCSW(iCbwTag, hostDataLength, EPhaseError);
					  //don't care to reset any flag - should get reset recovery
					}
				else   // case (4)
					{
					__PRINT(_L("Case 4\n"));
					SendCSW(iCbwTag, hostDataLength, iCmdStatus);
					}
				return;
				}	// if (!iWriteSetUp)

//==================
			TUint deviceDataLength = static_cast<TUint>(iWriteBufPtr.Length());
			iDataResidue =hostDataLength - deviceDataLength ;
			__PRINT2(_L("Device data length = %d, DataResidue (write to host)=%d\n"), deviceDataLength, iDataResidue);

			if (deviceDataLength < hostDataLength  &&
				hostDataLength < KBOTMaxBufSize )
					{
					__PRINT(_L("Case 5 (padding)\n"));
					SetPaddingBufPtr(hostDataLength);
					iPaddingBufPtr.Zero();
					iPaddingBufPtr.Append(iWriteBufPtr);
					iStallAllowed = EFalse;
					__PRINT1(_L("iPaddingBufPtr.Length = %d\n"),iPaddingBufPtr.Length());
					TPtrC8 ptr(NULL, 0);
					ptr.Set((TUint8*)iPaddingBufPtr.Ptr(), hostDataLength);
					WriteData(iStatus, ptr, hostDataLength, EFalse);
					return;
					}

//===================

			if (deviceDataLength == hostDataLength)  	//case (6)[==]
				{
				__PRINT(_L("Case 6\n"));
				WriteData(iStatus, iWriteBufPtr, deviceDataLength);
				return;
				}
			else if (deviceDataLength < hostDataLength)	//case (5)[<]
				{
				__PRINT(_L("Case 5\n"));
				WriteData(iStatus, iWriteBufPtr, deviceDataLength, ETrue);		// Send ZLP
				return;
				}
			else 										// deviceDataLength > hostDataLength - case (7)
				{
				__PRINT(_L("Case 7\n"));
				iCmdStatus = EPhaseError;
				iDataResidue = 0;
				WriteData(iStatus, iWriteBufPtr, hostDataLength);
				return;
				}
			}
		else  //read data from host
			{
			if (!iReadSetUp)
				{
				iDataResidue = hostDataLength;
				__PRINT(_L("Read buffer was not setup\n"));
//				Use next block instead of StallEndpointAndWaitForClear(OutEndpoint);
				DiscardData(hostDataLength);

				if (iWriteSetUp) //case (10)
					{
					__PRINT(_L("case 10\n"));
					SendCSW(iCbwTag, hostDataLength, EPhaseError);
					}
				else // case (9)
					{
					__PRINT(_L("Case 9\n"));
					SendCSW(iCbwTag, hostDataLength, iCmdStatus);
					}

				return;
				}

			TUint deviceDataLength = iBufSize;
			iDataResidue = hostDataLength;  // calculate residue later

			__PRINT2(_L("deviceDataLength = iBufSize = %d, DataResidue = HDL for now (read from host) =%d\n"),deviceDataLength,iDataResidue);

			if (deviceDataLength <= hostDataLength)  // case (11) and (12)
				{
				__PRINT(_L("Case 11 or 12\n"));
				ReadData(deviceDataLength);
				return;
				}
			if (deviceDataLength > hostDataLength) // case  (13)
				{
				__PRINT(_L("Case 13\n"));
                /**
                 * Comment following line in order to pass compliant test.
                 * As spec said in case 13:"The device may receive data up to a
                 * total of dCBWDataTransferLength."
                 * Here we choose to ignore incoming data.
                 */
				//StallEndpointAndWaitForClear(OutEndpoint); //Stall Out endpoint
                if (iReadSetUp)
                    {
					WriteToClient(hostDataLength);
                    iReadSetUp = EFalse;
                    }
                SendCSW(iCbwTag, hostDataLength, EPhaseError);
				return;
				}
			}
		}
	else  // Host expected no data transfer
		{
		__PRINT(_L("No data transfer expected\n"));
		iDataResidue = 0;
		if (iWriteSetUp || iReadSetUp)   // case (2) and (3)
			{
			__PRINT(_L("Case 2 or 3\n"));
			SendCSW(iCbwTag, 0, EPhaseError);
			}
		else
			{
			__PRINT(_L("Case 1\n"));
			SendCSW(iCbwTag, 0, iCmdStatus);  //case (1)
			}
		}
	}


/**
Check if CBW Valid and Meaningful.

@return ETrue if CBW is Valid and Meaningful, EFalse otherwise
*/
TBool CBulkOnlyTransport::CheckCBW()
	{
	__FNLOG("CBulkOnlyTransport::CheckCBW");

    //
    // Check valid
    //

    // Check length
    if ((TUint) (iCbwBufPtr.Length()) != KCbwLength)
        {
		__PRINT2(_L("Bad length: %d != KCbwLength"), iCbwBufPtr.Length(), KCbwLength);
		return EFalse;
        }

    // Check signature
	TInt i = KCbwSignatureOffset;
	if (iCbwBufPtr[i  ] != 0x55 ||         // CBW Singature from USB Bulk-Only Transport spec
		iCbwBufPtr[i+1] != 0x53 ||
		iCbwBufPtr[i+2] != 0x42 ||
		iCbwBufPtr[i+3] != 0x43)
		{
		__PRINT(_L("Bad signature"));
		__PRINT4(_L(" 0x%x, 0x%x, 0x%x, 0x%x \n"), iCbwBufPtr[i], iCbwBufPtr[i+1], iCbwBufPtr[i+2],iCbwBufPtr[i+3])
		return EFalse;
		}

    //
    // Check meaningful
    //

    // Check reserved bits ( must be zero )
    if ((iCbwBufPtr[KCbwLunOffset] & 0xF0) || (iCbwBufPtr[KCbwCbLengthOffset] & 0xE0))
		{
		__PRINT(_L("Reserved bits not zero\n"));
		return EFalse;
		}

	// check command block length
	TInt cbwcbLength = iCbwBufPtr[KCbwCbLengthOffset] & 0x1F;
	if (cbwcbLength >KMaxCbwcbLength)
		{
		__PRINT(_L("Incorrect block length\n"));
		return EFalse;
		}

	//check LUN
	TInt8 lun = static_cast<TUint8>(iCbwBufPtr[KCbwLunOffset] & 0x0f);
	if (iMaxLun < lun)
		{
		__PRINT1(_L("bad lun: %d"), lun);
		return EFalse;
		}

	return ETrue;
	}


/**
Initiate stalling of bulk IN endpoint.
Used when protocol wants to force host to initiate a reset recovery.
*/
void CBulkOnlyTransport::SetPermError()
	{
	__FNLOG("CBulkOnlyTransport::SetPermError");
    iCurrentState = EPermErr;
    Activate(KErrNone);
	}


/**
Send data provided by protocol to the host

@param aLength amount of data (in bytes) to be send to host
*/
void CBulkOnlyTransport::WriteData(TRequestStatus& aStatus, TPtrC8& aDes, TUint aLength, TBool aZlpRequired)
	{
	__FNLOG("CBulkOnlyTransport::WriteData");

	if (IsActive())
		{
		__PRINT(_L("Still active\n"));
		__ASSERT_DEBUG(EFalse, User::Panic(KUsbMsSvrPncCat, EMsBulkOnlyStillActive));
		return;
		}
	WriteUsb(aStatus, aDes, aLength, aZlpRequired);
	iCurrentState = EWritingData;
	SetActive();
	}


/**
Send Command Status Wrapper to the host

@param aTag Echo of Command Block Tag sent by the host.
@param aDataResidue the difference between the amount of data expected by the
       host, and the actual amount of data processed by the device.
@param aStatus indicates the success or failure of the command.
*/
void CBulkOnlyTransport::SendCSW(TUint aTag, TUint aDataResidue, TCswStatus aStatus)
	{
	__FNLOG("CBulkOnlyTransport::SendCSW");
	__PRINT2(_L("DataResidue = %d, Status = %d \n"), aDataResidue, aStatus);

	if (IsActive())
		{
		__PRINT(_L("Still active\n"));
		__ASSERT_DEBUG(EFalse, User::Panic(KUsbMsSvrPncCat, EMsBulkOnlyStillActive));
		return;
		}

	SetCswBufPtr(KCswLength);
	TInt i = KCswSingnatureOffset;
	iCswBufPtr[i  ] = 0x55;   // CSW Singature from USB Bulk-Only Transport spec
	iCswBufPtr[i+1] = 0x53;
	iCswBufPtr[i+2] = 0x42;
	iCswBufPtr[i+3] = 0x53;

	i = KCswTagOffset;

	iCswBufPtr[i  ] = static_cast<TUint8>((aTag & 0x000000FF));
	iCswBufPtr[i+1] = static_cast<TUint8>((aTag & 0x0000FF00) >> 8);
	iCswBufPtr[i+2] = static_cast<TUint8>((aTag & 0x00FF0000) >> 16);
	iCswBufPtr[i+3] = static_cast<TUint8>((aTag & 0xFF000000) >> 24);

	i = KCswDataResidueOffset;
	iCswBufPtr[i  ] = static_cast<TUint8>((aDataResidue & 0x000000FF));
	iCswBufPtr[i+1] = static_cast<TUint8>((aDataResidue & 0x0000FF00) >> 8);
	iCswBufPtr[i+2] = static_cast<TUint8>((aDataResidue & 0x00FF0000) >> 16);
	iCswBufPtr[i+3] = static_cast<TUint8>((aDataResidue & 0xFF000000) >> 24);

	iCswBufPtr[KCswStatusOffset] = static_cast<TUint8>(aStatus);

	TPtrC8 ptr(NULL, 0);
	ptr.Set((const TUint8*)iCswBufPtr.Ptr(), KCswLength);

	WriteUsb(iStatus, ptr, KCswLength);

	iCurrentState = ESendingCSW;

	SetActive();
	}


/**
Associates the transport with the protocol.  Called during initialization of the controller.

@param aProtocol reference to the protocol
*/
void CBulkOnlyTransport::RegisterProtocol(MProtocolBase& aProtocol)
	{
	__FNLOG("CBulkOnlyTransport::RegisterProtocol");
	iProtocol = &aProtocol;
	}


/**
Used by CControlInterface

@return reference to the controller which instantiate the CBulkOnlyTransport
*/
CUsbMassStorageController& CBulkOnlyTransport::Controller()
	{
	return iController;
	}


/**
@return the number of logical units supported by the device.
Logical Unit Numbers on the device shall be numbered contiguously starting from LUN
0 to a maximum LUN of 15 (Fh).
*/
TInt CBulkOnlyTransport::MaxLun()
	{
	return iMaxLun;
	}


void CBulkOnlyTransport::GetCommandBufPtr(TPtr8& aDes, TUint aLength) // Set pointer to buffer of specified aLength for command
	{
	aDes.Set(SetCommandBufPtr(aLength));
	}

void CBulkOnlyTransport::GetReadDataBufPtr(TPtr8& aDes) // Set pointer to buffer into which data is to be read from drive (Read10)
	{
	aDes.Set(SetDataBufPtr());
	}


void CBulkOnlyTransport::GetWriteDataBufPtr(TPtrC8& aDes) // Set pointer to buffer from which data is to be written to drive (Write10)
	{
	aDes.Set(iReadBufPtr);
	}

#ifdef MSDC_MULTITHREADED
void CBulkOnlyTransport::ProcessReadData(TAny* aAddress)
	{
	ExpireData(aAddress);
	}
#endif








