diff -r 000000000000 -r a41df078684a kerneltest/f32test/shostmassstorage/testclient/usbtestmsclient/transport/cbulkonlytransport.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/f32test/shostmassstorage/testclient/usbtestmsclient/transport/cbulkonlytransport.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,1330 @@ +// 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 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 +#include + +#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 "msdebug.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) + { + __MSFNSLOG + if (aNumDrives <=0 || static_cast(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) + { + __MSFNSLOG + if (aNumDrives <=0 || static_cast(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) + { + __MSFNLOG + } + +#ifdef MSDC_TESTMODE +CBulkOnlyTransport::CBulkOnlyTransport(TInt aNumDrives, + CUsbMassStorageController& aController, + TTestParser* aTestParser) +: CActive(EPriorityStandard), + iMaxLun(aNumDrives-1), + iController(aController), + iStallAllowed(ETrue), + iTestParser(aTestParser) + { + __MSFNLOG + } +#endif + + +/** +Constructs the CBulkOnlyTranspor object +*/ +void CBulkOnlyTransport::ConstructL() + { + __MSFNLOG + iBotControlInterface = CBotControlInterface::NewL(*this); + iDeviceStateNotifier = CActiveDeviceStateNotifier::NewL(*this); + CActiveScheduler::Add(this); + } + + +/** +Destructor +*/ +CBulkOnlyTransport::~CBulkOnlyTransport() + { + __MSFNLOG + 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) + { + __MSFNLOG + TInt ret(KErrNone); + TInt configDescriptorSize(0); + + iLdd.GetConfigurationDescriptorSize(configDescriptorSize); + if (static_cast(configDescriptorSize) != KUsbDescSize_Config) + { + return KErrCorrupt; + } + + TBuf8 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() + { + __MSFNLOG + // 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(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; + 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; + 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(&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) + { + __MSFNLOG + 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() + { + __MSFNLOG + 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) + { + __MSFNLOG + + TInt lun = MaxLun(); + do + { + Controller().DriveManager().Connect(lun); + } + while(--lun >= 0); + + TInt res = iBotControlInterface->Start() ; + + iCurrentState = ENone; + iDataTransferMan.Init(); + iStarted = ETrue; + + // 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() + { + __MSFNLOG + if (iStarted) + { + iController.DriveManager().Disconnect(); + Cancel(); + iBotControlInterface->Cancel(); + iProtocol->Cancel(); + iStarted = EFalse; + } + } + + +void CBulkOnlyTransport::HwSuspend() + { + __MSFNLOG + iController.DriveManager().Disconnect(); + } + + +void CBulkOnlyTransport::HwResume() + { + __MSFNLOG + iController.DriveManager().Connect(); + } + + +/** +Stops the Bulk Only Transport +*/ +TInt CBulkOnlyTransport::Stop() + { + __MSFNLOG + 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() + { + __MSFNLOG + if (IsActive()) + { + __PRINT(_L("Still active\n")); + __ASSERT_DEBUG(EFalse, User::Panic(KUsbMsClientPanicCat, EMsBulkOnlyStillActive)); + return; + } + + ReadCbw(); + } + + +void CBulkOnlyTransport::ReadCbw() + { + __MSFNLOG + iCbwBuf.SetMax(); + iLdd.ReadUntilShort(iStatus, KOutEndpoint, iCbwBuf, iCbwBuf.Length()); + iCurrentState = EWaitForCBW; + SetActive(); + } + + +void CBulkOnlyTransport::DoCancel() + { + __MSFNLOG + iLdd.WriteCancel(KInEndpoint); + iLdd.ReadCancel(KOutEndpoint); + } + + +void CBulkOnlyTransport::Activate(TInt aReason) + { + __MSFNLOG + SetActive(); + TRequestStatus* r = &iStatus; + User::RequestComplete(r, aReason); + } + + +void CBulkOnlyTransport::RunL() + { + __MSFNLOG + 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() + { + __MSFNLOG + 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 + DataOutRead(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() + { + __MSFNLOG + 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) + { + __MSFNLOG + 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) + { + __MSFNLOG + 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) + { + __MSFNLOG + __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) + { + __MSFNLOG + 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() + { + __MSFNLOG + return iMaxLun; + } + + +/** +Used by CControlInterface +@return reference to USB logical driver +*/ +RDevUsbcClient& CBulkOnlyTransport::Ldd() + { + return iLdd; + } + + +void CBulkOnlyTransport::StallEndpointAndWaitForClear(TEndpointNumber aEndpoint) + { + __MSFNLOG + __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() + { + __MSFNLOG + 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) + { + __MSFNLOG + 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) + { + __MSFNLOG + } + + +CActiveDeviceStateNotifier* CActiveDeviceStateNotifier::NewL(CBulkOnlyTransport& aParent) + { + __MSFNSLOG + CActiveDeviceStateNotifier* self = new (ELeave) CActiveDeviceStateNotifier(aParent); + CleanupStack::PushL(self); + self->ConstructL(); + CActiveScheduler::Add(self); + CleanupStack::Pop(); // self + return (self); + } + + +void CActiveDeviceStateNotifier::ConstructL() + { + __MSFNLOG + } + + +CActiveDeviceStateNotifier::~CActiveDeviceStateNotifier() + { + __MSFNLOG + Cancel(); // base class + } + + +void CActiveDeviceStateNotifier::DoCancel() +/** + * + */ + { + __MSFNLOG + iParent.Ldd().AlternateDeviceStatusNotifyCancel(); + } + + +void CActiveDeviceStateNotifier::RunL() +/** + * + */ + { + __MSFNLOG + // 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() +/** + * + */ + { + __MSFNLOG + if (IsActive()) + { + __PRINT(_L("Still active\n")); + return; + } + iParent.Ldd().AlternateDeviceStatusNotify(iStatus, iDeviceState); + SetActive(); + }