usbclasses/usbphoneasmodem/classimplementation/mscfileserver/src/bulkonlytransport.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 18 Jan 2010 21:13:23 +0200
changeset 2 468cfcb53fd1
parent 0 1e05558e2206
permissions -rw-r--r--
Revision: 201001 Kit: 201003

// Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "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 "bulkonlytransport.h"
#include "mscfileserver.h"
#include "usbmscfileshared.h"
#include "debug.h"
#define InEndpoint EEndpoint1
#define OutEndpoint EEndpoint2
//This value defined in USB Mass Storage Bulk Only Transrt spec and not supposed to be changed
LOCAL_D const TInt KRequiredNumberOfEndpoints = 2; // in addition to endpoint 0. 


//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;
LOCAL_D const TInt KCswLength = 13;
LOCAL_D const TInt KUsbNumInterfacesOffset = 4;

/**
 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))
        {
        TRACE_ERROR((_L("TUsbRequestHdr::Decode buffer invalid length %d"),aBuffer.Length()))
        return KErrGeneral;
        }

    // Decode as SPEC 1.3.3
    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));
    TRACE_INFO((_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) ? ETrue : EFalse;
    }

//
/**
 Called by CBulkOnlyTransport to create an instance of CControlInterface

 @param aParent reference to the CBulkOnlyTransport
 */
CControlInterface* CControlInterface::NewL(CBulkOnlyTransport& aParent)
    {
    CControlInterface* self = new (ELeave) CControlInterface(aParent);
    CleanupStack::PushL(self);
    self->ConstructL();
    CActiveScheduler::Add(self);
    CleanupStack::Pop();
    return self;
    }

void CControlInterface::ConstructL()
    {
    }

/**
 c'tor

 @param aParent reference to the CBulkOnlyTransport
 */
CControlInterface::CControlInterface(CBulkOnlyTransport& aParent) :
	CActive(EPriorityStandard),
	iParent(aParent),
	iCurrentState(ENone)
    {
    }

/**
 d'tor
 */
CControlInterface::~CControlInterface()
    {
    TRACE_FUNC
	Cancel();
    }

/**
 Called by  CBulkOnlyTransportStart to start control interface
 */
TInt CControlInterface::Start()
    {
    TRACE_FUNC
    TInt res = ReadEp0Data();
    return (res);
    }

/**
 Called by CBulkOnlyTransportStart to stop control interface
 */
void CControlInterface::Stop()
    {
    TRACE_FUNC
    // Don't need to be any thing if we're not in active state
    if (!IsActive())
        {
        TRACE_INFO((_L("Not active")))
        return;
        }

    TRACE_INFO((_L("Stopping...")))

    // Stop by cancel
    Cancel();
    iCurrentState = ENone;
    }

/**
 Cancel outstanding request (if any)
 */
void CControlInterface::DoCancel()
    {
    TRACE_FUNC
    switch(iCurrentState)
        {
        case EReadEp0Data:
        iParent.Ldd().ReadCancel(EEndpoint0);
        break;
        case ESendMaxLun:
        iParent.Ldd().WriteCancel(EEndpoint0);
        break;
        default:
        TRACE_ERROR((_L("\nWrong state !")))
        __ASSERT_DEBUG(EFalse, User::Panic(KUsbMsSvrPncCat, EMsControlInterfaceBadState));
        }
    }

/**
 Implement CControlInterface state machine
 */
void CControlInterface::RunL()
    {
    TRACE_FUNC
    if (iStatus != KErrNone)
        {
        TRACE_ERROR(( _L( "Error %d in RunL" ), iStatus.Int() ))

        //read EP0  again 
        ReadEp0Data();
        return;
        }

    switch (iCurrentState)
        {
        case ESendMaxLun:
            ReadEp0Data();
            break;

        case EReadEp0Data:
            DecodeEp0Data();
            break;

        default:
            TRACE_ERROR(( _L( "error: (Shouldn't end up here...)" ) ))
            __ASSERT_DEBUG(EFalse, User::Panic(KUsbMsSvrPncCat, EMsControlInterfaceBadState));
            break;
        }
    return;
    }

/**
 Post a read request to EEndpoint0 to read request header
 */
TInt CControlInterface::ReadEp0Data()
    {
    TRACE_FUNC
    if ( IsActive() )
        {
        TRACE_ERROR(( _L( "Still active" ) ))
        return KErrServerBusy;
        }

    iParent.Ldd().Read(iStatus, EEndpoint0, iData, KRequestHdrSize);

    iCurrentState = EReadEp0Data;

    SetActive();
    return KErrNone;
    }

/**
 Decode request header and do appropriate action - get max LUN info or post a reset request
 */
void CControlInterface::DecodeEp0Data()
    {
    TRACE_FUNC
    if ( IsActive() )
        {
        TRACE_ERROR(( _L( "Still active" ) ))
        __ASSERT_DEBUG(EFalse, User::Panic(KUsbMsSvrPncCat, EMsControlInterfaceStillActive));
        return;
        }

    TInt err = iRequestHeader.Decode(iData);

    if (err != KErrNone)
        {
        TRACE_WARNING((_L("Decode header error : err=%d"), err))
        return;
        }

    switch(iRequestHeader.iRequest)
        {
        // GET MAX LUN (0xFE)
        case TUsbRequestHdr::EReqGetMaxLun:
            {
            TRACE_INFO((_L("DecodeEp0Data : 'Get Max LUN' Request MaxLun = %d" ),
                            iParent.MaxLun() ))

            if ( iRequestHeader.iRequestType != 0xA1 //value from USB MS BOT spec
                    || iRequestHeader.iIndex> 15 || iRequestHeader.iValue != 0 || iRequestHeader.iLength != 1)
                {
                TRACE_ERROR((_L("GetMaxLun command packet check error")))
                iParent.Ldd().EndpointZeroRequestError();
                break;
                }

            iData.FillZ(1); //Return only 1 byte to host 
            iData[0] = static_cast<TUint8>(iParent.MaxLun()); // Supported Units
            iParent.Ldd().Write(iStatus, EEndpoint0, iData, 1);

            iCurrentState = ESendMaxLun;
            SetActive();

            return;
            }

        // RESET (0xFF)
        case TUsbRequestHdr::EReqReset:
            {
            TRACE_INFO((_L("DecodeEp0Data : 'Mass Storage Reset' Request")))

            if ( iRequestHeader.iRequestType != 0x21 //value from USB MS BOT spec
                    || iRequestHeader.iIndex> 15 || iRequestHeader.iValue != 0 || iRequestHeader.iLength != 0)
                {
                TRACE_ERROR((_L("MSC Reset command packet check error")))
                iParent.Ldd().EndpointZeroRequestError();
                break;
                }

            iParent.HwStop();
            iParent.Controller().Reset();
            iParent.HwStart(ETrue);

            err = iParent.Ldd().SendEp0StatusPacket();
            TRACE_INFO((_L("SendEp0StatusPacket: err=%d"), err))

            return;
            }
        // Unknown?
        default:
            {
            TRACE_ERROR((_L("DecodeEp0Data : Unknown Request")))
            }
        }
    ReadEp0Data(); //try to get another request 
    }

//-------------------------------------
/**
 Create CBulkOnlyTransport object
 @param aNumDrives - The number of drives available for MS
 @param aController - reference to the parent
 @return pointer to newly created object
 */
CBulkOnlyTransport* CBulkOnlyTransport::NewL(TInt aNumDrives,CMscFileController& aController)
    {
	CBulkOnlyTransport* self = new(ELeave) CBulkOnlyTransport(aNumDrives, aController);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

/**
 c'tor 
 @param aNumDrives - The number of drives available for MS
 @param aController - reference to the parent
 */
CBulkOnlyTransport::CBulkOnlyTransport(TInt aNumDrives,CMscFileController& aController):
	CActive(EPriorityStandard),
	iMaxLun(aNumDrives-1),
	iController(aController),
	iReadBuf(NULL,0),
	iWriteBuf(NULL,0),
	iStallAllowed(ETrue)
    {
    }

/**
 Constructs the CBulkOnlyTranspor object
 */
void CBulkOnlyTransport::ConstructL()
    {
    TRACE_FUNC
    iControlInterface = CControlInterface::NewL(*this);
    iDeviceStateNotifier = CActiveDeviceStateNotifier::NewL(*this);
    CActiveScheduler::Add(this);
    }

/**
 Destructor
 */
CBulkOnlyTransport::~CBulkOnlyTransport()
    {
    TRACE_FUNC
    if (iInterfaceConfigured)
        {
        Stop();
        }
    delete iControlInterface;
    delete iDeviceStateNotifier;
    }

/**
 Set or unset configuration descriptor for USB MassStorage Bulk Only transport

 @param aUnset indicate whether set or unset descriptor
 @return KErrNone if operation was completed successfully, errorcode otherwise
 */
TInt CBulkOnlyTransport::SetupConfigurationDescriptor(TBool aUnset)
    {
    TRACE_FUNC
    TInt ret(KErrNone);
    TInt configDescriptorSize(0);

    iLdd.GetConfigurationDescriptorSize(configDescriptorSize);
    if (static_cast<TUint> (configDescriptorSize) != KUsbDescSize_Config)
        {
        return KErrCorrupt;
        }

    TBuf8<KUsbDescSize_Config> configDescriptor;
    ret = iLdd.GetConfigurationDescriptor(configDescriptor);
    if (ret != KErrNone)
        {
        return ret;
        }

    // I beleive that other fields setted up during LDD initialisation
    if (aUnset)
        {
        --configDescriptor[KUsbNumInterfacesOffset];
        }
    else
        {
        ++configDescriptor[KUsbNumInterfacesOffset];
        }
    ret = iLdd.SetConfigurationDescriptor(configDescriptor);

    return ret;
    }

/**
 Set up interface descriptor

 @return KErrNone if operation was completed successfully, errorcode otherwise
 */
TInt CBulkOnlyTransport::SetupInterfaceDescriptors()
    {
    TRACE_FUNC
    // Device caps
    TUsbDeviceCaps d_caps;
    TInt ret = iLdd.DeviceCaps(d_caps);
    if (ret != KErrNone)
        {
        return ret;
        }
    TInt totalEndpoints = d_caps().iTotalEndpoints;
    if (totalEndpoints < KRequiredNumberOfEndpoints)
        {
        return KErrHardwareNotAvailable;
        }

    // Endpoint caps
    TUsbcEndpointData data[KUsbcMaxEndpoints];
	TPtr8 dataptr(reinterpret_cast<TUint8*>(data), sizeof(data), sizeof(data));
    ret = iLdd.EndpointCaps(dataptr);
    if (ret != KErrNone)
        {
        return ret;
        }

    // Set the active interface
    TUsbcInterfaceInfoBuf ifc;
    TInt ep_found = 0;
    TBool foundBulkIN = EFalse;
    TBool foundBulkOUT = EFalse;

    for (TInt i = 0; i < totalEndpoints; i++)
        {
        const TUsbcEndpointCaps* caps = &data[i].iCaps;
        const TInt maxPacketSize = caps->MaxPacketSize();
		if (!foundBulkIN &&
			(caps->iTypesAndDir & (KUsbEpTypeBulk | KUsbEpDirIn)) == (KUsbEpTypeBulk | KUsbEpDirIn))
            {
            // InEndpoint is going to be our TX (IN, write) endpoint
            ifc().iEndpointData[0].iType = KUsbEpTypeBulk;
            ifc().iEndpointData[0].iDir = KUsbEpDirIn;
            ifc().iEndpointData[0].iSize = maxPacketSize;
            ifc().iEndpointData[0].iInterval_Hs = 0;
			ifc().iEndpointData[0].iFeatureWord1 = KUsbcEndpointInfoFeatureWord1_DMA;
            foundBulkIN = ETrue;
            if (++ep_found == KRequiredNumberOfEndpoints)
                {
                break;
                }
            continue;
            }
		if (!foundBulkOUT &&
			(caps->iTypesAndDir & (KUsbEpTypeBulk | KUsbEpDirOut)) == (KUsbEpTypeBulk | KUsbEpDirOut))
            {
            // OutEndpoint is going to be our RX (OUT, read) endpoint
            ifc().iEndpointData[1].iType = KUsbEpTypeBulk;
            ifc().iEndpointData[1].iDir = KUsbEpDirOut;
            ifc().iEndpointData[1].iSize = maxPacketSize;
            ifc().iEndpointData[1].iInterval_Hs = 0;
			ifc().iEndpointData[1].iFeatureWord1 = KUsbcEndpointInfoFeatureWord1_DMA;
            foundBulkOUT = ETrue;
            if (++ep_found == KRequiredNumberOfEndpoints)
                {
                break;
                }
            continue;
            }
        }
    if (ep_found != KRequiredNumberOfEndpoints)
        {
        return KErrHardwareNotAvailable;
        }
    _LIT16(string, "USB Mass Storage Interface");
    ifc().iString = const_cast<TDesC16*> (&string);
    ifc().iTotalEndpointsUsed = KRequiredNumberOfEndpoints;
    ifc().iClass.iClassNum = 0x08; // Mass Storage
    ifc().iClass.iSubClassNum = 0x06; // SCSI Transparent Command Set
    ifc().iClass.iProtocolNum = 0x50; // Bulk Only Transport

	TUint bandwidth_priority = (EUsbcBandwidthOUTDefault | EUsbcBandwidthINDefault);
    if (d_caps().iHighSpeed)
        {
        // If this device supports USB High-speed, then we request 64KB buffers
        // (otherwise the default 4KB ones will do).
        bandwidth_priority = (EUsbcBandwidthOUTPlus2 | EUsbcBandwidthINPlus2);
        // Also, tell the Protocol about it, because it might want to do some
        // optimizing too.
        iProtocol->ReportHighSpeedDevice();
        }
    ret = iLdd.SetInterface(0, ifc, bandwidth_priority);
    return ret;
    }

/**
 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(TPtr8& aData)
    {
    TRACE_FUNC
    TRACE_INFO((_L("Length = %d  (bytes)"), aData.Length()))
    iReadBuf.Set(aData);
    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)
    {
    TRACE_FUNC
    TRACE_INFO((_L("Length = %d  (bytes)"), aData.Length()))
    iWriteBuf.Set(aData);
    iWriteSetUp = ETrue;
    }

TInt CBulkOnlyTransport::Start()
    {
    TRACE_FUNC_ENTRY

    TInt err = KErrNone;

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

    if (IsActive())
        {
        TRACE_ERROR((_L("Active before start!")))
        return KErrInUse;
        }

    if ((err = iLdd.Open(0)) != KErrNone )
        {
        TRACE_ERROR((_L("Error during open ldd!")))
        return err;
        }

    if ((err = SetupConfigurationDescriptor()) != KErrNone ||
            (err = SetupInterfaceDescriptors()) != KErrNone )
        {
        iLdd.Close();
        TRACE_ERROR((_L("Error during descriptors setup!")))
        return err;
        }

    iDeviceStateNotifier->Activate(); // activate notifier wich will wait until USB became configured
    TUsbcDeviceState deviceStatus = EUsbcDeviceStateDefault;
    err = iLdd.DeviceStatus(deviceStatus);
    TRACE_INFO((_L("Device status = %d"), deviceStatus))
    if (err == KErrNone && deviceStatus == EUsbcDeviceStateConfigured)
        {
        TRACE_INFO(_L("Starting bulk only transport\n"));
        err = HwStart();
        }
    iInterfaceConfigured = ETrue;
    TRACE_FUNC_EXIT
    return err;
    }

TInt CBulkOnlyTransport::HwStart(TBool aDiscard)
    {
    TRACE_FUNC_ENTRY

    TInt res = iControlInterface->Start();

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

    if (aDiscard)
        {
        TInt bytes;
        const TInt err = iLdd.QueryReceiveBuffer(OutEndpoint, bytes);
        if (err != KErrNone || bytes <= 0)
            {
            TRACE_ERROR((_L("Error: err=%d bytes=%d"), bytes))
            }
        else
            {
            TRACE_ERROR((_L("RxBuffer has %d bytes"), bytes))
            ReadAndDiscardData(bytes);
            }
        }

    ReadCBW();
    TRACE_FUNC_EXIT
    return res;
    }

TInt CBulkOnlyTransport::HwStop()
    {
    TRACE_FUNC

    if (iStarted)
        {
        Cancel();
        iControlInterface->Cancel();
        iProtocol->Cancel();
        iStarted = EFalse;
        }
    return KErrNone;
    }

TInt CBulkOnlyTransport::HwSuspend()
    {
    TRACE_FUNC
    return KErrNone;
    }

TInt CBulkOnlyTransport::HwResume()
    {
    TRACE_FUNC
    return KErrNone;
    }

/**
 Stops the Bulk Only Transport
 */
TInt CBulkOnlyTransport::Stop()
    {
    TRACE_FUNC
    iControlInterface->Cancel();
    iDeviceStateNotifier->Cancel();
    Cancel();
    if (iInterfaceConfigured)
        {
        iLdd.ReleaseInterface(0);
        SetupConfigurationDescriptor(ETrue);
        iLdd.Close();
        }
    iCurrentState = ENone;
    iInterfaceConfigured = EFalse;

    return KErrNone;
    }

/**
 Read aLength bytes of data from the host into the read buffer.
 @param aLength The number of bytes to read from the host.
 */
void CBulkOnlyTransport::ReadCBW()
    {
    TRACE_FUNC
    if (IsActive())
        {
        TRACE_ERROR(( _L( "Still active" ) ))
        __ASSERT_DEBUG(EFalse, User::Panic(KUsbMsSvrPncCat, EMsBulkOnlyStillActive));
        return;
        }

    iCbwBuf.SetMax();
    iLdd.ReadUntilShort(iStatus, OutEndpoint, iCbwBuf, iCbwBuf.Length());

    iCurrentState = EWaitForCBW;
    SetActive();
    }

void CBulkOnlyTransport::DoCancel()
    {
    TRACE_FUNC
    iLdd.WriteCancel(InEndpoint);
    iLdd.ReadCancel(OutEndpoint);
    }

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

void CBulkOnlyTransport::RunL()
    {
    TRACE_FUNC
    if (iStatus != KErrNone)
        {
        TRACE_ERROR((_L("Error %d in RunL, halt endpoints \n"), iStatus.Int()))
        SetPermError(); //halt endpoints for reset recovery
        return;
        }

    switch (iCurrentState)
        {
        case EWaitForCBW:
            TRACE_INFO((_L("EWaitForCBW")))
            DecodeCBW();
            break;

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

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

            SendCSW(iCbwTag, iDataResidue, iCmdStatus);
            break;

        case EReadingData:
            {
            TRACE_INFO((_L("EReadingData")))

            TInt ret = KErrNone;
            FOREVER
                {
                if (iReadSetUp)
                    {
                    ret = iProtocol->ReadComplete(KErrNone);
                    }

                TUint deviceDataLength = static_cast<TUint>(iReadBuf.Length());
                if(ret == KErrCompletion)
                    {
                    // The protocol has indicated with KErrCompletion that sufficient
                    // data is available in the buffer to process the transfer immediately.
                    iDataResidue -= deviceDataLength;
                    iLdd.Read(iStatus, OutEndpoint, iReadBuf, deviceDataLength);
                    User::WaitForRequest(iStatus);
                    if (iStatus != KErrNone)
                        {
                        // An error occurred - halt endpoints for reset recovery
                        TRACE_ERROR((_L("Error %d in EReadingData, halt endpoints"),
                                        iStatus.Int()))
                        SetPermError();
                        return;
                        }
                    }
                else if(ret == KErrNotReady)
                    {
                    // The protocol has indicated with KErrNotReady that insufficient
                    // data is available in the buffer, so should wait for it to arrive
                    ReadData(deviceDataLength);
                    break;
                    }
                else
                    {
                    // The protocol has indicated that transfer is 
                    // complete, so send the CSW response to the host.
                    iReadSetUp = EFalse;

                    if (ret != KErrNone)
                        {
                        iCmdStatus = ECommandFailed;
                        }

                    if (iDataResidue)
                        {
                        TRACE_INFO((_L("Discarding residue")))
                        // we have to read as much data as available that host PC sends;
                        // otherwise, bulk-out endpoint will need to keep sending NAK back.
                        ReadAndDiscardData(iDataResidue);
                        }
                    SendCSW(iCbwTag, iDataResidue, iCmdStatus);
                    break;
                    }
                }
            }
        break;

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

        case EPermErr:
        TRACE_INFO((_L("EPermErr")))
        StallEndpointAndWaitForClear(InEndpoint);
        break;

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

/**
 Prepare incase we need to read data from host
 Called in DecodeCBW(...)
 @param aHostDataLength The number of bytes to read from the host.
 */
void CBulkOnlyTransport::PrepareReadFromHost(TUint aHostDataLength)
    {
    if (!iReadSetUp)
        {
			iDataResidue =aHostDataLength;
			TRACE_INFO((_L("Read buffer was not setup\n")))
            //Use next block instead of StallEndpointAndWaitForClear(OutEndpoint); 
            {
            iBuf.SetLength(KBOTMaxBufSize);
            TUint c =0;
            TRequestStatus status;
            while (c<aHostDataLength)
                {
                TInt len;
                if (aHostDataLength - c> KBOTMaxBufSize)
                    {
                    len = KBOTMaxBufSize;
                    }
                else
                    {
                    len = aHostDataLength - c;
                    }

                iLdd.Read(status, OutEndpoint, iBuf, len);
                User::WaitForRequest(status);
                c += KBOTMaxBufSize;
                }
            }

        if (iWriteSetUp) //case (10)
            {
            TRACE_INFO((_L("case 10\n")))
            SendCSW(iCbwTag, aHostDataLength, EPhaseError);
            }
        else // case (9)

            {
            TRACE_INFO((_L("Case 9\n")))
            SendCSW(iCbwTag, aHostDataLength, iCmdStatus);
            }

        return;
        }
    else
        {
        TUint deviceDataLength = static_cast<TUint>(iReadBuf.Length());
        iDataResidue =aHostDataLength - deviceDataLength;
        TRACE_INFO((_L("deviceDataLength=%d, DataResidue (read from host) =%d\n"),
                        deviceDataLength,iDataResidue))

        if (deviceDataLength <= aHostDataLength) // case (11) and (12)

            {
            TRACE_INFO((_L("Case 11 or 12\n")))
            ReadData(deviceDataLength);
            return;
            }

        if (deviceDataLength> aHostDataLength) // case  (13)

            {
            TRACE_INFO((_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)
                {
                iLdd.Read(iStatus, OutEndpoint, iReadBuf, aHostDataLength);
                User::WaitForRequest(iStatus);
                iProtocol->ReadComplete(KErrGeneral);
                iReadSetUp = EFalse;
                }
            SendCSW(iCbwTag, aHostDataLength, EPhaseError);
            return;
            }
        }
    }

/**
 Prepare incase we need to read data from host
 Called in DecodeCBW(...)
 @param aHostDataLength The number of bytes to write to the host.
 */
void CBulkOnlyTransport::PrepareWriteToHost(TUint aHostDataLength)
    {
    if (!iWriteSetUp) //write buffer was not set up 
        {
        TRACE_INFO((_L("Write buffer was not setup")))
        iDataResidue =aHostDataLength;
        TRACE_INFO((_L("DataResidue (write to host)=%d"),iDataResidue))

        //------------------------------------
        if (aHostDataLength <= KBOTMaxBufSize)
            {
            TRACE_INFO((_L("Case 4 or 8\n")))
            iBuf.FillZ(aHostDataLength);
            iLdd.Write(iStatus, InEndpoint, iBuf, aHostDataLength);
            SetActive();
            iCurrentState = EWritingData;
            iStallAllowed = EFalse;
            if (iReadSetUp) //read buffer WAS set up - case (8)

                {
                TRACE_INFO((_L("It is Case 8")))
                iCmdStatus = EPhaseError;
                }
            return;
            }
        else
        //------------------------------------
        //					Use next block instead of StallEndpointAndWaitForClear(InEndpoint);

            {
            iBuf.FillZ(KBOTMaxBufSize);
            TUint c =0;
            TRequestStatus status;
            while (c<aHostDataLength)
                {
                TInt len;
                if (aHostDataLength - c> KBOTMaxBufSize)
                    {
                    len = KBOTMaxBufSize;
                    }
                else
                    {
                    len = aHostDataLength - c;
                    }

                iLdd.Write(status, InEndpoint, iBuf, len);
                User::WaitForRequest(status);
                c += KBOTMaxBufSize;
                }
            }

        if (iReadSetUp) //read buffer WAS set up - case (8)
            {
            TRACE_INFO(_L("Case 8"));
            SendCSW(iCbwTag, aHostDataLength, EPhaseError);
            //don't care to reset any flag - should get reset recovery
            }
        else // case (4)
            {
            TRACE_INFO((_L("Case 4")))
            SendCSW(iCbwTag, aHostDataLength, iCmdStatus);
            }
        return;
        }
    else
        {
        //================== 
        TUint deviceDataLength = static_cast<TUint>(iWriteBuf.Length());
        iDataResidue =aHostDataLength - deviceDataLength;
        TRACE_INFO((_L("Device data length = %d, DataResidue (write to host)=%d"),
                        deviceDataLength, iDataResidue))

        if (deviceDataLength < aHostDataLength &&
                aHostDataLength < KBOTMaxBufSize )
            {
            TRACE_INFO((_L("Case 5 (padding)\n")))
            iBuf.Zero();
            iBuf.Append(iWriteBuf);
            iBuf.SetLength(aHostDataLength);
            iStallAllowed = EFalse;
            TRACE_INFO((_L("iBuf.Length=%d\n"),iBuf.Length()))
            iLdd.Write(iStatus, InEndpoint, iBuf, aHostDataLength);
            SetActive();
            iCurrentState = EWritingData;
            return;
            }

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

        if (deviceDataLength == aHostDataLength) //case (6)[==]

            {
            TRACE_INFO((_L("Case 6\n")))
            WriteData(deviceDataLength);
            return;
            }
        else if (deviceDataLength < aHostDataLength) //case (5)[<]

            {
            TRACE_INFO((_L("Case 5\n")))
            WriteData(deviceDataLength, ETrue); // Send ZLP
            return;
            }
        else // deviceDataLength > aHostDataLength - case (7)

            {
            TRACE_INFO((_L("Case 7\n")))
            iCmdStatus = EPhaseError;
            iDataResidue = 0;
            WriteData(aHostDataLength);
            return;
            }
        }
    }

/**
 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()
    {
    TRACE_FUNC
    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();
        return;
        }

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

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

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

    TBool dataToHost = iCbwBuf[KCbwFlagOffset] & 0x80;

    TRACE_INFO((_L("lun =%d, hostDataLength=%d, CBWtag = 0x%X\n, dataToHost=%d"),
                    lun, hostDataLength, iCbwTag, dataToHost))
    //
    TBool ret = iProtocol->DecodePacket(aData, lun);
    //

    iStallAllowed = ETrue;

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

    if (hostDataLength == 0)
        {
        TRACE_INFO((_L("No data transfer expected\n")))
        iDataResidue = 0;
        if (iWriteSetUp || iReadSetUp) // case (2) and (3)

            {
            TRACE_INFO((_L("Case 2 or 3\n")))
            SendCSW(iCbwTag, 0, EPhaseError);
            }
        else
            {
            TRACE_INFO((_L("Case 1\n")))
            SendCSW(iCbwTag, 0, iCmdStatus); //case (1)
            }

        return;
        }
    else
        {
        // Data Direction, To or From Host
        if (dataToHost)
            {
            PrepareWriteToHost(hostDataLength);
            }
        else
            {
            PrepareReadFromHost(hostDataLength);
            }
        }
    }

/**
 Check if CBW Valid and Meaningful.  

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

    // Check valid

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

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

    // Check meaningful

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

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

    //check LUN
    TInt8 lun = static_cast<TUint8>(iCbwBuf[KCbwLunOffset] & 0x0f);
    if (iMaxLun < lun)
        {
        TRACE_INFO((_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()
    {
    TRACE_FUNC
    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(TUint aLength, TBool aZlpRequired)
    {
    TRACE_FUNC

    if (IsActive())
        {
        TRACE_INFO((_L("Still active")))
        __ASSERT_DEBUG(EFalse, User::Panic(KUsbMsSvrPncCat, EMsBulkOnlyStillActive));
        return;
        }
    iLdd.Write(iStatus, InEndpoint, iWriteBuf, aLength, aZlpRequired);
    iCurrentState = EWritingData;
    SetActive();
    }

/**
 Request data form the host for the protocol

 @param aLength amount of data (in bytes) to be received from the host
 */
void CBulkOnlyTransport::ReadData(TUint aLength)
    {
    TRACE_FUNC
    if (IsActive())
        {
        TRACE_INFO((_L("Still active")))
        __ASSERT_DEBUG(EFalse, User::Panic(KUsbMsSvrPncCat, EMsBulkOnlyStillActive));
        return;
        }
    iLdd.Read(iStatus, OutEndpoint, iReadBuf, aLength);
    SetActive();
    iCurrentState = EReadingData;
    }

/**
 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)
    {
    TRACE_FUNC
    TRACE_INFO((_L("DataResidue = %d, Status = %d \n"), aDataResidue, aStatus))

    if (IsActive())
        {
        TRACE_INFO((_L("Still active")))
        __ASSERT_DEBUG(EFalse, User::Panic(KUsbMsSvrPncCat, EMsBulkOnlyStillActive));
        return;
        }
    TBuf8<KCswLength> csw(KCswLength);

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

    i = KCswTagOffset;

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

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

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

    iLdd.Write(iStatus, InEndpoint, csw, 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)
    {
    TRACE_FUNC
    iProtocol = &aProtocol;
    }

/**
 Used by CControlInterface

 @return reference to the controller which instantiate the CBulkOnlyTransport
 */
CMscFileController& 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;
    }

/**
 Used by CControlInterface
 @return reference to USB logical driver
 */
RDevUsbcClient& CBulkOnlyTransport::Ldd()
    {
    return iLdd;
    }


void CBulkOnlyTransport::StallEndpointAndWaitForClear(TEndpointNumber aEndpoint)
    {
    TRACE_FUNC
    __ASSERT_DEBUG(aEndpoint != EEndpoint0, User::Panic(KUsbMsSvrPncCat, EMsWrongEndpoint));

    // Now stall this endpoint
    TRACE_INFO((_L("Stalling endpoint %d"), aEndpoint))
    TInt r = iLdd.HaltEndpoint(aEndpoint);
    if (r != KErrNone)
        {
        TRACE_ERROR((_L("Error: stalling ep %d failed: %d"), aEndpoint, r))
        }
    TEndpointState ep_state;
    TInt i = 0;
    do
        {
        // Wait for 10ms before checking the ep status
        User::After(10000);
        iLdd.EndpointStatus(aEndpoint, ep_state);
        if (++i >= 1000)
            {
            // 10 secs should be enough
            TRACE_ERROR((_L("Error: Checked for ep %d de-stall %d seconds - giving up now..."),
                            aEndpoint, i / 100))
            // We can only hope for a Reset Recovery
            return;
            }
        }while ((ep_state == EEndpointStateStalled) && iStarted);
    TRACE_INFO((_L("Checked for ep %d de-stall: %d time(s)"), aEndpoint, i))
    }

/**
 * Read out rest data from OutEndpoint and discard them
 */
void CBulkOnlyTransport::ReadAndDiscardData(TInt aBytes)
    {
    TRACE_FUNC
    const TUint bufsize = static_cast<TUint>(iReadBuf.Length());
    __ASSERT_ALWAYS(bufsize> 0, User::Panic(_L("Buffer size is zero"), bufsize));
    TRequestStatus status;
    while (aBytes> 0)
        {
        TRACE_INFO((_L("Bytes still to be read: %d\n"), aBytes))
        iLdd.ReadOneOrMore(status, OutEndpoint, iReadBuf, bufsize);
        User::WaitForRequest(status);
        TInt err = status.Int();
        if (err != KErrNone)
            {
            // Bad.
            break;
            }
        aBytes -= iReadBuf.Length();
        }
    iReadBuf.SetLength(bufsize);
    }

/**
 Called by the protocol to determine how many bytes of data are available in the read buffer. 

 @return The number of bytes available in the read buffer
 */
TInt CBulkOnlyTransport::BytesAvailable()
    {
    TInt bytes = 0;
    TInt err = iLdd.QueryReceiveBuffer(OutEndpoint, bytes);
    if (err != KErrNone)
        bytes = 0;
    return bytes;
    }

//
// --- class CActiveDeviceStateNotifier ---------------------------------------------------------
//
CActiveDeviceStateNotifier::CActiveDeviceStateNotifier(CBulkOnlyTransport& aParent)
/**
 *
 */
	: CActive(EPriorityStandard),
	  iParent(aParent),
	  iDeviceState(EUsbcNoState),
            iOldDeviceState(EUsbcNoState)
    {

    }


CActiveDeviceStateNotifier* CActiveDeviceStateNotifier::NewL(CBulkOnlyTransport& aParent)
/**
 *
 */
    {
	CActiveDeviceStateNotifier* self = new (ELeave) CActiveDeviceStateNotifier(aParent);
    CleanupStack::PushL(self);
    self->ConstructL();
    CActiveScheduler::Add(self);
    CleanupStack::Pop(); // self
    return (self);
    }

void CActiveDeviceStateNotifier::ConstructL()
/**
 *
 */
    {
    TRACE_FUNC
	}

CActiveDeviceStateNotifier::~CActiveDeviceStateNotifier()
/**
 *
 */
    {
    TRACE_FUNC
    Cancel();												// base class
    }

void CActiveDeviceStateNotifier::DoCancel()
/**
 *
 */
    {
    TRACE_FUNC
    iParent.Ldd().AlternateDeviceStatusNotifyCancel();
    }

void CActiveDeviceStateNotifier::RunL()
/**
 *
 */
    {
    TRACE_FUNC
    // This displays the device state.
    // In a real world program, the user could take here appropriate action (cancel a
    // transfer request or whatever).
    if (!(iDeviceState & KUsbAlternateSetting))
        {

        switch (iDeviceState)
            {
            case EUsbcDeviceStateUndefined:
            TRACE_INFO((_L("Device State notifier: Undefined\n")))
            iParent.HwStop();
            break;
            case EUsbcDeviceStateAttached:
            TRACE_INFO((_L("Device State notifier: Attached\n")))
            iParent.HwStop();
            break;
            case EUsbcDeviceStatePowered:
            TRACE_INFO((_L("Device State notifier: Powered\n")))
            iParent.HwStop();
            break;
            case EUsbcDeviceStateDefault:
            TRACE_INFO((_L("Device State notifier: Default\n")))
            iParent.HwStop();
            break;
            case EUsbcDeviceStateAddress:
            TRACE_INFO((_L("Device State notifier: Address\n")))
            iParent.HwStop();
            break;
            case EUsbcDeviceStateConfigured:
            TRACE_INFO((_L("Device State notifier: Configured\n")))
            if (iOldDeviceState == EUsbcDeviceStateSuspended)
                {
                iParent.HwResume();
                }
            else
                {
                iParent.HwStart();
                }
            break;
            case EUsbcDeviceStateSuspended:
            TRACE_INFO((_L("Device State notifier: Suspended\n")))
            if (iOldDeviceState == EUsbcDeviceStateConfigured)
                {
                iParent.HwSuspend();
                }
            break;
            default:
            TRACE_INFO((_L("Device State notifier: ***BAD***\n")))
            iParent.HwStop();
            break;
            }
        iOldDeviceState = iDeviceState;
        }
    else if (iDeviceState & KUsbAlternateSetting)
        {
        TRACE_INFO((_L("Device State notifier: Alternate interface setting has changed: now %d\n"),
                        iDeviceState & ~KUsbAlternateSetting))
        }
    Activate();
    }

void CActiveDeviceStateNotifier::Activate()
/**
 *
 */
    {
    TRACE_FUNC
    if (IsActive())
        {
        TRACE_INFO((_L("Still active")))
        return;
        }
    iParent.Ldd().AlternateDeviceStatusNotify(iStatus, iDeviceState);
    SetActive();
    }