diff -r 000000000000 -r 1e05558e2206 usbclasses/usbphoneasmodem/classimplementation/mscfileserver/src/bulkonlytransport.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usbclasses/usbphoneasmodem/classimplementation/mscfileserver/src/bulkonlytransport.cpp Thu Dec 17 09:14:30 2009 +0200 @@ -0,0 +1,1493 @@ +// 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: +// +// +/** + @file + */ +#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 (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(aBuffer[1]); + iValue = static_cast(aBuffer[2] + (aBuffer[3] << 8)); + iIndex = static_cast(aBuffer[4] + (aBuffer[5] << 8)); + iLength = static_cast(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(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 (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() + { + 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(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 (&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(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 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(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 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(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 (iCbwBuf[13] & 0x0f); + + iCbwTag = static_cast(iCbwBuf[KCbwTagOffset]) | + static_cast(iCbwBuf[KCbwTagOffset+1]) <<8 | + static_cast(iCbwBuf[KCbwTagOffset+2]) <<16| + static_cast(iCbwBuf[KCbwTagOffset+3]) <<24; + + TInt i = KCbwDataTransferLengthOffset; + TUint hostDataLength = static_cast(iCbwBuf[i ]) | + static_cast(iCbwBuf[i+1]) <<8 | + static_cast(iCbwBuf[i+2]) <<16 | + static_cast(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(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 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 ((aTag & 0x000000FF)); + csw[i + 1] = static_cast ((aTag & 0x0000FF00) >> 8); + csw[i + 2] = static_cast ((aTag & 0x00FF0000) >> 16); + csw[i + 3] = static_cast ((aTag & 0xFF000000) >> 24); + + i = KCswDataResidueOffset; + csw[i] = static_cast ((aDataResidue & 0x000000FF)); + csw[i + 1] = static_cast ((aDataResidue & 0x0000FF00) >> 8); + csw[i + 2] = static_cast ((aDataResidue & 0x00FF0000) >> 16); + csw[i + 3] = static_cast ((aDataResidue & 0xFF000000) >> 24); + + csw[KCswStatusOffset] = static_cast (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(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(); + } +