kerneltest/f32test/shostmassstorage/testclient/usbtestmsclient/transport/cbulkonlytransport.cpp
author hgs
Tue, 26 Oct 2010 12:49:20 +0100
changeset 297 b2826f67641f
parent 90 947f0dc9f7a8
permissions -rw-r--r--
201043_03

// Copyright (c) 2009-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 <e32base.h>
#include <d32usbc.h>

#include "mstypes.h"
#include "msctypes.h"
#include "usbmsclientpanic.h"
#include "botmsctypes.h"
#include "testman.h"
#include "botmscserver.h"

#include "mserverprotocol.h"
#include "mdevicetransport.h"

#include "mstypes.h"
#include "botcontrolinterface.h"
#include "debug.h"
#include "cusbmassstorageserver.h"
#include "cusbmassstoragecontroller.h"
#include "drivemanager.h"
#include "cbulkonlytransport.h"

#define KInEndpoint EEndpoint1
#define KOutEndpoint EEndpoint2


//-------------------------------------
/**
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,
                                             CUsbMassStorageController& aController)
    {
    if (aNumDrives <=0 || static_cast<TUint>(aNumDrives) > KUsbMsMaxDrives)
        {
        User::Leave(KErrArgument);
        }
    CBulkOnlyTransport* self = new(ELeave) CBulkOnlyTransport(aNumDrives, aController);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

#ifdef MSDC_TESTMODE
CBulkOnlyTransport* CBulkOnlyTransport::NewL(TInt aNumDrives,
                                             CUsbMassStorageController& aController,
                                             TTestParser* aTestParser)
    {
    if (aNumDrives <=0 || static_cast<TUint>(aNumDrives) > KUsbMsMaxDrives)
        {
        User::Leave(KErrArgument);
        }
    CBulkOnlyTransport* self = new(ELeave) CBulkOnlyTransport(aNumDrives,
                                                              aController,
                                                              aTestParser);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }
#endif


/**
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)
    {
    }

#ifdef MSDC_TESTMODE
CBulkOnlyTransport::CBulkOnlyTransport(TInt aNumDrives,
                                       CUsbMassStorageController& aController,
                                       TTestParser* aTestParser)
:   CActive(EPriorityStandard),
    iMaxLun(aNumDrives-1),
    iController(aController),
    iStallAllowed(ETrue),
    iTestParser(aTestParser)
    {
    }
#endif


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


/**
Destructor
*/
CBulkOnlyTransport::~CBulkOnlyTransport()
    {
    if (iInterfaceConfigured)
        {
        Stop();
        }
    delete iBotControlInterface;
    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)
    {
    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()
    {
    // 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))
            {
            // KInEndpoint is going to be our TX (IN, write) endpoint
            ifc().iEndpointData[0].iType = KUsbEpTypeBulk;
            if((d_caps().iFeatureWord1 & KUsbDevCapsFeatureWord1_EndpointResourceAllocV2) == KUsbDevCapsFeatureWord1_EndpointResourceAllocV2)
                ifc().iEndpointData[0].iFeatureWord1  = KUsbcEndpointInfoFeatureWord1_DMA|KUsbcEndpointInfoFeatureWord1_DoubleBuffering;
            ifc().iEndpointData[0].iDir  = KUsbEpDirIn;
            ifc().iEndpointData[0].iSize = maxPacketSize;
            ifc().iEndpointData[0].iInterval_Hs = 0;
            foundBulkIN = ETrue;
            if (++ep_found == KRequiredNumberOfEndpoints)
                {
                break;
                }
            continue;
            }
        if (!foundBulkOUT &&
            (caps->iTypesAndDir & (KUsbEpTypeBulk | KUsbEpDirOut)) == (KUsbEpTypeBulk | KUsbEpDirOut))
            {
            // KOutEndpoint is going to be our RX (OUT, read) endpoint
            ifc().iEndpointData[1].iType = KUsbEpTypeBulk;
            if((d_caps().iFeatureWord1 & KUsbDevCapsFeatureWord1_EndpointResourceAllocV2) == KUsbDevCapsFeatureWord1_EndpointResourceAllocV2)
                ifc().iEndpointData[1].iFeatureWord1  = KUsbcEndpointInfoFeatureWord1_DMA|KUsbcEndpointInfoFeatureWord1_DoubleBuffering;
            ifc().iEndpointData[1].iDir  = KUsbEpDirOut;
            ifc().iEndpointData[1].iSize = maxPacketSize;
            ifc().iEndpointData[1].iInterval_Hs = 0;
            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::SetupDataOut(TPtr8& aData)
    {
    iDataTransferMan.SetModeDataOut(aData);
    }

/**
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::SetupDataIn(TPtrC8& aData)
    {
    iDataTransferMan.SetModeDataIn(aData);
    }


TInt 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 = iLdd.Open(0))                    != KErrNone ||
        (err = SetupConfigurationDescriptor())  != KErrNone ||
        (err = SetupInterfaceDescriptors())     != KErrNone )
        {
        __PRINT(_L("CBulkOnlyTransport::Start  - Error during descriptors setup!\n"));
        return err;
        }

    iDeviceStateNotifier->Activate();  // activate notifier wich will wait until USB became configured
    TUsbcDeviceState deviceStatus = EUsbcDeviceStateDefault;
    err = iLdd.DeviceStatus(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();
        }
    iInterfaceConfigured = ETrue;
    return err;
    }

TInt CBulkOnlyTransport::HwStart(TBool aDiscard)
    {
    TInt lun = MaxLun();
    do
        {
        Controller().DriveManager().Connect(lun);
        }
    while(--lun >= 0);

    TInt res = iBotControlInterface->Start() ;

    iCurrentState = ENone;
    iDataTransferMan.Init();
    iStarted = ETrue;

    TUsbDeviceCaps d_caps;
    TInt ret = iLdd.DeviceCaps(d_caps);
    if (ret == KErrNone)
        {
        if((d_caps().iFeatureWord1 & KUsbDevCapsFeatureWord1_EndpointResourceAllocV2) != KUsbDevCapsFeatureWord1_EndpointResourceAllocV2)
            {
            // Set up DMA if possible (errors are non-critical)
            TInt err = iLdd.AllocateEndpointResource(KOutEndpoint, EUsbcEndpointResourceDMA);
            if (err != KErrNone)
                {
                __PRINT1(_L("Set DMA on OUT endpoint failed with error code: %d"), err);
                }
            err = iLdd.AllocateEndpointResource(KInEndpoint, EUsbcEndpointResourceDMA);
            if (err != KErrNone)
                {
                __PRINT1(_L("Set DMA on IN endpoint failed with error code: %d"), err);
                }

            // Set up Double Buffering if possible (errors are non-critical)
            err = iLdd.AllocateEndpointResource(KOutEndpoint, EUsbcEndpointResourceDoubleBuffering);
            if (err != KErrNone)
                {
                __PRINT1(_L("Set Double Buffering on OUT endpoint failed with error code: %d"), err);
                }
            err = iLdd.AllocateEndpointResource(KInEndpoint, EUsbcEndpointResourceDoubleBuffering);
            if (err != KErrNone)
                {
                __PRINT1(_L("Set Double Buffering on IN endpoint failed with error code: %d"), err);
                }
            }
        }

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

    ClientReadCbw();
    return res;
    }


void CBulkOnlyTransport::HwStop()
    {
    if (iStarted)
        {
        iController.DriveManager().Disconnect();
        Cancel();
        iBotControlInterface->Cancel();
        iProtocol->Cancel();
        iStarted = EFalse;
        }
    }


void CBulkOnlyTransport::HwSuspend()
    {
    iController.DriveManager().Disconnect();
    }


void CBulkOnlyTransport::HwResume()
    {
    iController.DriveManager().Connect();
    }


/**
Stops the Bulk Only Transport
*/
TInt CBulkOnlyTransport::Stop()
    {
    iBotControlInterface->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::ClientReadCbw()
    {
    if (IsActive())
        {
        __PRINT(_L("Still active\n"));
        __ASSERT_DEBUG(EFalse, User::Panic(KUsbMsClientPanicCat, EMsBulkOnlyStillActive));
        return;
        }

    ReadCbw();
    }


void CBulkOnlyTransport::ReadCbw()
    {
    iCbwBuf.SetMax();
    iLdd.ReadUntilShort(iStatus, KOutEndpoint, iCbwBuf, iCbwBuf.Length());
    iCurrentState = EWaitForCBW;
    SetActive();
    }


void CBulkOnlyTransport::DoCancel()
    {
    iLdd.WriteCancel(KInEndpoint);
    iLdd.ReadCancel(KOutEndpoint);
    }


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


void 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"));
            TRAPD(err, DecodeCbwL());
            if (err)
                {
                // 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;
                }
            break;

        case EHandleDataIn:
            {
            __PRINT(_L("EHandleDataIn"));
            iDataTransferMan.SetModeNoData();

            if (iDataTransferMan.iDataResidue && iStallAllowed)
                {
                StallEndpointAndWaitForClear(KInEndpoint);
                }
#ifdef MSDC_TESTMODE
            if (iTestParser && iTestParser->Enabled())
                {
                if (iTestParser->TestCase() == TTestParser::ETestCaseDiStallCsw)
                    {
                    iTestParser->ClrTestCase();
                    __TESTMODEPRINT1("CBW Tag=0x%x: Stalling CSW Data-In", iCbwTag);
                    StallEndpointAndWaitForClear(KInEndpoint);
                    }
                }
#endif
            SendCsw(iCbwTag, iDataTransferMan.iDataResidue, iCmdStatus);
            }
            break;

        case EHandleDataOut:
            {
            TUint bytesWritten;
            TInt ret = iProtocol->MediaWritePacket(bytesWritten);
            iDataTransferMan.iDataResidue -= bytesWritten;

            switch(ret)
                {
            case KErrCompletion:
                {
                // The protocol has indicated with KErrCompletion that
                // sufficient data is available in the buffer to process the
                // transfer immediately.

                TInt deviceDataLength = iDataTransferMan.iReadBuf.Length();
                iLdd.Read(iStatus, KOutEndpoint, iDataTransferMan.iReadBuf, deviceDataLength);
                SetActive();
                }
                break;
            case KErrNotReady:
                {
                // The protocol has indicated with KErrNotReady that
                // insufficient data is available in the buffer, so should wait
                // for it to arrive.
                TInt deviceDataLength = iDataTransferMan.iReadBuf.Length();
                iLdd.Read(iStatus, KOutEndpoint, iDataTransferMan.iReadBuf, deviceDataLength);
                SetActive();
                }
                break;
            case KErrAbort:
                {
                iDataTransferMan.SetModeNoData();
                iCmdStatus = TBotCsw::ECommandFailed;
                if (iDataTransferMan.iDataResidue)
                    {
                    __PRINT(_L("Discarding residue"));
                    FlushDataOut(iDataTransferMan.iTransportResidue);
                    }
                SendCsw(iCbwTag, iDataTransferMan.iDataResidue, iCmdStatus);
                }
                break;

            case KErrNone:
            default:
                {
                // The protocol has indicated that transfer is complete, so
                // send the CSW response to the host.
                iDataTransferMan.SetModeNoData();
#ifdef MSDC_TESTMODE
                if (iDataTransferMan.iDataResidue)
                    {
                    TBool done = EFalse;
                    if (iTestParser && iTestParser->Enabled())
                        {
                        TInt testCase = iTestParser->TestCase();
                        if (testCase == TTestParser::ETestCaseDoStallCsw)
                            {
                            iTestParser->ClrTestCase();
                            __TESTMODEPRINT1("CBW Tag=0x%x: Stalling CSW Data-Out", iCbwTag);
                            StallEndpointAndWaitForClear(KInEndpoint);
                            done = ETrue;
                            }
                        }
                    if (!done)
                        {
                        __PRINT(_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.
                        FlushDataOut(iDataTransferMan.iDataResidue);
                        __TESTMODEPRINT2("CBW Tag=0x%x: Residue = x%x",
                                         iCbwTag, iDataTransferMan.iDataResidue);
                        }
                    }
                else
                    {
                    if (iTestParser && iTestParser->Enabled())
                        {
                        TInt testCase = iTestParser->TestCase();
                        if (testCase == TTestParser::ETestCaseDoStallCsw)
                            {
                            iTestParser->ClrTestCase();
                            __TESTMODEPRINT1("CBW Tag=0x%x: Stalling CSW Data-Out", iCbwTag);
                            StallEndpointAndWaitForClear(KInEndpoint);
                            }
                        }
                    }

#else
                    if (iDataTransferMan.iDataResidue)
                        {
                        __PRINT(_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.
                        FlushDataOut(iDataTransferMan.iDataResidue);
                        }
#endif
                SendCsw(iCbwTag, iDataTransferMan.iDataResidue, iCmdStatus);
                }
                break;  // KErrNone
                } // switch
            }
            break;  // EReadingData

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

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

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


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

- 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::DecodeCbwL()
    {
    TBotServerReq req;
    req.DecodeL(iCbwBuf);

    if (!(req.IsValidCbw() && req.IsMeaningfulCbw(iMaxLun)))
        {
        User::Leave(KErrGeneral);
        }

    TPtrC8 aData(&iCbwBuf[TBotCbw::KCbwCbOffset], TBotCbw::KMaxCbwcbLength);
    //aData.Set(&iCbwBuf[TBotCbw::KCbwCbOffset], TBotCbw::KMaxCbwcbLength);
    TUint8 lun = req.Lun();

    iCbwTag  =  req.Tag();
    TUint32 hostDataLength = req.DataTransferLength();
    iDataTransferMan.SetHostDataLen(hostDataLength);

    TBotServerReq::TCbwDirection dataToHost = req.Direction();

    __PRINT4(_L("lun =%d, hostDataLength=%d, CBWtag = 0x%X\n, dataToHost=%d\n"),
             lun, hostDataLength, iCbwTag, dataToHost);

    //////////////////////////////////////////////
    TBool ret = iProtocol->DecodePacket(aData, lun);
    //////////////////////////////////////////////

    iStallAllowed = ETrue;

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

    if (hostDataLength)    // Host expected data transfer
        {
        if (dataToHost == TBotServerReq::EDataIn)  // send data to host
            {
            if (!iDataTransferMan.IsModeDataIn())
                {
                __PRINT(_L("Write buffer was not setup\n"));

                if (hostDataLength <= KBOTMaxBufSize)
                    {
                    // Case 4 or 8"
                    iBuf.FillZ(hostDataLength);
                    iLdd.Write(iStatus, KInEndpoint, iBuf, hostDataLength);
                    SetActive();
                    iCurrentState = EHandleDataIn;
                    iStallAllowed = EFalse;
                    if (iDataTransferMan.IsModeDataOut())
                        {
                        //read buffer WAS set up - case (8)
                        iCmdStatus = TBotCsw::EPhaseError;
                        }
                    return;
                    }
                else
                    {
                    // Use next block instead of StallEndpointAndWaitForClear(KInEndpoint);
                    FlushDataIn(hostDataLength);
                    }

                if (iDataTransferMan.IsModeDataOut())
                    {
                    //read buffer WAS set up - case (8)
                    SendCsw(iCbwTag, hostDataLength, TBotCsw::EPhaseError);
                    //don't care to reset any flag - should get reset recovery
                    }
                else
                    {
                    // case (4)
                    SendCsw(iCbwTag, hostDataLength, iCmdStatus);
                    }
                return;
                }

//==================
            TInt deviceDataLength = iDataTransferMan.iWriteBuf.Length();
            iDataTransferMan.iDataResidue = hostDataLength - deviceDataLength;

#ifdef MSDC_TESTMODE
            if (iTestParser && iTestParser->Enabled())
                {
                TInt testCase = iTestParser->TestCase();
                if (testCase == TTestParser::ETestCaseDiResidue)
                    {
                    __TESTMODEPRINT2("DataIN (Data Residue) %x %x", iTestParser->TestCounter(), hostDataLength);

                    const TInt KTransferLength = 0x100;
                    const TInt KResidueA = KTransferLength/4 -10;
                    const TInt KResidueB = KTransferLength/4 - 10;
                    if (hostDataLength > KTransferLength)
                        {
                        TInt residue = 0;
                        TInt i = iTestParser->TestCounter();
                        switch (i)
                            {
                            case 0:
                                iTestParser->ClrTestCase();
                                break;
                            case 1:
                                residue = KResidueA;
                                break;
                            case 2:
                                residue = KResidueB;
                                break;

                            default:
                                break;
                            }

                        //deviceDataLength -= residue;
                        iDataTransferMan.iDataResidue = residue;
                        iStallAllowed = EFalse;
                        iTestParser->DecTestCounter();

                        __TESTMODEPRINT4("CBW Tag=0x%x: Setting Residue (Data-In) hdl=%x ddl=%x residue=%x",
                                         iCbwTag, hostDataLength, deviceDataLength, residue);
                        }
                    }
                }
#endif
            if (deviceDataLength < hostDataLength && hostDataLength <= KBOTMaxBufSize)
                {
                // do not pad
                // iStallAllowed = ETrue;
                // __PRINT1(_L("iBuf.Length=%d\n"),iBuf.Length());
                // iLdd.Write(iStatus, KInEndpoint, iBuf, deviceDataLength);
                // SetActive();
                //iCurrentState = EWritingData;

                // pad
                iBuf.Zero();
                iBuf.Append(iDataTransferMan.iWriteBuf);
                iBuf.SetLength(hostDataLength);
                iStallAllowed = EFalse;
                __PRINT1(_L("iBuf.Length=%d\n"),iBuf.Length());
                iLdd.Write(iStatus, KInEndpoint, iBuf, hostDataLength);
                SetActive();
                iCurrentState = EHandleDataIn;

                return;
                }

            if (deviceDataLength == hostDataLength)
                {
                //case (6)[==]
#ifdef MSDC_TESTMODE
                if (iTestParser && iTestParser->Enabled())
                    {
                    if (iTestParser->TestCase() == TTestParser::ETestCaseDiStallData)
                        {
                        iTestParser->ClrTestCase();
                        __TESTMODEPRINT1("CBW Tag=0x%x: Stalling CSW Data-In", iCbwTag);
                        StallEndpointAndWaitForClear(KInEndpoint);
                        }
                    }
#endif

                DataInWriteRequest(deviceDataLength);
                return;
                }
            else if (deviceDataLength < hostDataLength)
                {
                //case (5)[<]
                DataInWriteRequest(deviceDataLength, ETrue);        // Send ZLP
                return;
                }
            else
                {
                // deviceDataLength > hostDataLength - case (7)
                iCmdStatus = TBotCsw::EPhaseError;
                iDataTransferMan.iDataResidue = 0;
                DataInWriteRequest(hostDataLength);
                return;
                }
            }
        else  // read data from host
            {
            if (!iDataTransferMan.IsModeDataOut())
                {
                __PRINT(_L("Read buffer was not setup\n"));

                // Use next block instead of StallEndpointAndWaitForClear(KOutEndpoint);
                FlushDataOut(hostDataLength);
                if (iDataTransferMan.IsModeDataIn())
                    {
                    //case (10)
                    SendCsw(iCbwTag, hostDataLength, TBotCsw::EPhaseError);
                    }
                else
                    {
                    // case (9)
                    SendCsw(iCbwTag, hostDataLength, iCmdStatus);
                    }
                return;
                }

            TInt deviceDataLength = iDataTransferMan.iReadBuf.Length();

            if (deviceDataLength <= hostDataLength)
                {
                // case (11) and (12)
#ifdef MSDC_TESTMODE
                TBool done = EFalse;
                if (iTestParser && iTestParser->Enabled())
                    {
                    TInt testCase = iTestParser->TestCase();

                    if (testCase == TTestParser::ETestCaseDoStallData)
                        {
                        iTestParser->ClrTestCase();
                        // Stall Data Out
                        __TESTMODEPRINT2("CBW Tag=0x%x:  Stalling Data (Data-Out) Residue = x%x",
                                         iCbwTag, iDataTransferMan.iDataResidue);
                        StallEndpointAndWaitForClear(KOutEndpoint);
                        TInt bytes;
                        const TInt err = iLdd.QueryReceiveBuffer(KOutEndpoint, bytes);
                        __PRINT1(_L("Error: err=%d bytes=%d"), bytes);
                        FlushDataOut(bytes);
                        }
                    }
                if (!done)
                    {
                    DataOutReadRequest(deviceDataLength);
                    }
#else
                DataOutReadRequest(deviceDataLength);
#endif
                return;
                }
            else
                {
                // case  (13)
                /**
                 * 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(KOutEndpoint); //Stall Out endpoint
                if (iDataTransferMan.IsModeDataOut())
                    {
                    iDataTransferMan.SetModeNoData();
                    iLdd.Read(iStatus, KOutEndpoint, iDataTransferMan.iReadBuf, hostDataLength);
                    User::WaitForRequest(iStatus);
                    iProtocol->MediaWriteAbort();
                    }
                SendCsw(iCbwTag, hostDataLength, TBotCsw::EPhaseError);
                return;
                }
            }
        }
    else  // Host expected no data transfer
        {
        __PRINT(_L("No data transfer expected\n"));
        iDataTransferMan.iDataResidue = 0;
        if (!iDataTransferMan.IsModeNoData())
            {
            // case (2) and (3)
            SendCsw(iCbwTag, 0, TBotCsw::EPhaseError);
            }
        else
            {
            //case (1)
#ifdef MSDC_TESTMODE
            if (iTestParser && iTestParser->Enabled())
                {
                TInt testCase = iTestParser->TestCase();
                if (testCase == TTestParser::ETestCaseNoDataStallCsw)
                    {
                    __TESTMODEPRINT1("CBW Tag=0x%x: Stalling CSW for Case 1 (Hn=Dn)", iCbwTag);
                    iTestParser->ClrTestCase();
                    StallEndpointAndWaitForClear(KInEndpoint);
                    }

                if (testCase == TTestParser::ETestCaseNoDataPhaseError)
                    {
                    __TESTMODEPRINT1("CBW Tag=0x%x: Enabling phase error (no data)", iCbwTag);
                    iTestParser->SetPhaseError();
                    }
                }
#endif
            SendCsw(iCbwTag, 0, iCmdStatus);
            }
        }
    }


/**
Initiate stalling of bulk IN endpoint.
Used when protocol wants to force host to initiate a reset recovery.
*/
void 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::DataInWriteRequest(TUint aLength, TBool aZlpRequired)
    {
    iLdd.Write(iStatus, KInEndpoint, iDataTransferMan.iWriteBuf, aLength, aZlpRequired);
    iDataTransferMan.iTransportResidue -= aLength;
    iCurrentState = EHandleDataIn;
    SetActive();
    }


/**
Request data form the host for the protocol

@param aLength amount of data (in bytes) to be received from the host
*/
void CBulkOnlyTransport::DataOutReadRequest(TUint aLength)
    {
    iLdd.Read(iStatus, KOutEndpoint, iDataTransferMan.iReadBuf, aLength);
    iDataTransferMan.iTransportResidue -= aLength;
    iCurrentState = EHandleDataOut;
    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, TUint32 aDataResidue, TBotCsw::TCswStatus aStatus)
    {
    __PRINT2(_L("DataResidue = %d, Status = %d \n"), aDataResidue, aStatus);
    iCsw.SetLength(TBotCsw::KCswLength);
    TBotServerResp resp(iCbwTag, aDataResidue, aStatus);
#ifdef MSDC_TESTMODE
    resp.EncodeL(iCsw, iTestParser);
#else
    resp.EncodeL(iCsw);
#endif
    iLdd.Write(iStatus, KInEndpoint, iCsw, TBotCsw::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(MServerProtocol& aProtocol)
    {
    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;
    }


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


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

    // Now stall this endpoint
    __PRINT1(_L("Stalling endpoint %d"), aEndpoint);
    __TESTMODEPRINT2("CBW Tag=0x%x: Stalling endpoint %d", iCbwTag, aEndpoint);
    TInt r = iLdd.HaltEndpoint(aEndpoint);
    if (r != KErrNone)
        {
        __PRINT2(_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);

        if (aEndpoint == KInEndpoint)
            {
            // Discard BULK-OUT data
            TInt bytes;
            const TInt err = iLdd.QueryReceiveBuffer(KOutEndpoint, bytes);
            if (err != KErrNone || bytes <= 0)
                {
                __PRINT1(_L("Error: err=%d bytes=%d"), bytes);
                }
            else
                {
                __PRINT1(_L("RxBuffer has %d bytes"), bytes);
                FlushDataOut(bytes);
                }
            }

        iLdd.EndpointStatus(aEndpoint, ep_state);
        if (++i >= 550)
            {
            // 5.5 secs should be enough (see 9.2.6.1 Request Processing Timing)
            __PRINT1(_L("Error: Checked for ep %d de-stall for 5.5s - giving up now"), aEndpoint);
            __TESTMODEPRINT1("Error: Checked for ep %d de-stall for 5.5s - giving up now", aEndpoint);
            // We can now only hope for a Reset Recovery
            return;
            }
        } while ((ep_state == EEndpointStateStalled) && iStarted);
    __PRINT2(_L("Checked for ep %d de-stall: %d time(s)"), aEndpoint, i);
    __TESTMODEPRINT2("Checked for ep %d de-stall: %d time(s)", aEndpoint, i);
    }



/**
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(KOutEndpoint, bytes);
    if (err != KErrNone)
        bytes = 0;
    return bytes;
    }


/**
 * Read out rest data from KOutEndpoint and discard them
 */
void CBulkOnlyTransport::FlushDataOut(TInt aLength)
    {
    iBuf.SetMax();
    TRequestStatus status;
    while (aLength > 0)
        {
        iLdd.ReadOneOrMore(status, KOutEndpoint, iBuf, iBuf.Length());
        User::WaitForRequest(status);
        TInt err = status.Int();
        if (err != KErrNone)
            {
            // Bad.
            break;
            }
        aLength -= iBuf.Length();
        }
    }


void CBulkOnlyTransport::FlushDataIn(TInt aLength)
    {
    iBuf.SetMax();
    TInt c = 0;
    TInt len;
    TRequestStatus status;
    while (c < aLength)
        {
        len = aLength - c;
        if (len >  KBOTMaxBufSize)
            {
            len = KBOTMaxBufSize;
            }
        iLdd.Write(status, KInEndpoint, iBuf, len);
        User::WaitForRequest(status);
        c +=  KBOTMaxBufSize;
        }
    }

//
// --- 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()
    {
    }


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


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


void CActiveDeviceStateNotifier::RunL()
/**
 *
 */
    {
    // 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:
            __PRINT(_L("Device State notifier: Undefined\n"));
            iParent.HwStop();
            break;
        case EUsbcDeviceStateAttached:
            __PRINT(_L("Device State notifier: Attached\n"));
            iParent.HwStop();
            break;
        case EUsbcDeviceStatePowered:
            __PRINT(_L("Device State notifier: Powered\n"));
            iParent.HwStop();
            break;
        case EUsbcDeviceStateDefault:
            __PRINT(_L("Device State notifier: Default\n"));
            iParent.HwStop();
            break;
        case EUsbcDeviceStateAddress:
            __PRINT(_L("Device State notifier: Address\n"));
            iParent.HwStop();
            break;
        case EUsbcDeviceStateConfigured:
            __PRINT(_L("Device State notifier: Configured\n"));
            if (iOldDeviceState == EUsbcDeviceStateSuspended)
                {
                iParent.HwResume();
                }
            else
                {
                iParent.HwStart();
                }
            break;
        case EUsbcDeviceStateSuspended:
            __PRINT(_L("Device State notifier: Suspended\n"));
            if (iOldDeviceState == EUsbcDeviceStateConfigured)
                {
                iParent.HwSuspend();
                }
            break;
        default:
            __PRINT(_L("Device State notifier: ***BAD***\n"));
            iParent.HwStop();
            break;
            }
        iOldDeviceState = iDeviceState;
        }
    else if (iDeviceState & KUsbAlternateSetting)
        {
        __PRINT1(_L("Device State notifier: Alternate interface setting has changed: now %d\n"), iDeviceState & ~KUsbAlternateSetting);
        }
    Activate();
    }


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