diff -r 59aa7d6e3e0f -r 089413cdde3c usbdrv/peripheral/pdd/pil/src/controltransfersm.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usbdrv/peripheral/pdd/pil/src/controltransfersm.cpp Fri Jul 23 15:54:47 2010 +0800 @@ -0,0 +1,604 @@ +/* + Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). + All rights reserved. + + This program and the accompanying materials are made available + under the terms of the Eclipse Public License v1.0 which accompanies + this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html + + Initial Contributors: + Nokia Corporation - initial contribution. + + Contributors: +*/ + +#include "controltransfersm.h" + +// Bitmap of setup packet +/* +Offset 0, bmRequestType, 1 bytes + 1 Bit-Map + D7 Data Phase Transfer Direction + 0 = Host to Device + 1 = Device to Host + + D6..5 Type + 0 = Standard + 1 = Class + 2 = Vendor + 3 = Reserved + + D4..0 Recipient + 0 = Device + 1 = Interface + 2 = Endpoint + 3 = Other + 4..31 = Reserved + +Offset 1, bRequest + +Offset 6, Count, 2 bytes + Number of bytes to transfer if there is a data phase +*/ + +#if defined(_DEBUG) + +#define CTSM_ID "ControlTransferSM " + +char* DebugName[] = + { + "Setup", + "Data Out", + "Status In", + "Data In", + "Status Out" + }; + +#endif + +// Static data instance +TUsbcSetup TSetupPkgParser::iSetupPkt; + +TSetupPkgParser::TSetupPkgParser() + { + iSetupPkt.iRequestType = 0; + iSetupPkt.iRequest = 0; + + iSetupPkt.iValue = 0; + iSetupPkt.iIndex = 0; + iSetupPkt.iLength = 0; + } + +// Code for TSetupPkgParser +// we do a bitwise copy here. +void TSetupPkgParser::Set(const TUint8* aSetupBuf) + { + // TUint8 index + iSetupPkt.iRequestType = static_cast(aSetupBuf)[0]; + iSetupPkt.iRequest = static_cast(aSetupBuf)[1]; + // TUint16 index from here! + iSetupPkt.iValue = SWAP_BYTES_16((reinterpret_cast(aSetupBuf))[1]); + iSetupPkt.iIndex = SWAP_BYTES_16((reinterpret_cast(aSetupBuf))[2]); + iSetupPkt.iLength = SWAP_BYTES_16((reinterpret_cast(aSetupBuf))[3]); + + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "TSetupPkgParser::Set")); + } + +// return the next stage by decoding the setup packet +// the possible stage followed by a setup packet are: +// StatusIn (no data stage) +// DataOut (host sent to peripheral) +// DataIn (peripheral to host) +UsbShai::TControlStage TSetupPkgParser::NextStage() + { + UsbShai::TControlStage ret = UsbShai::EControlTransferStageMax; + + // Take the data length out, 0 length means no data stage + if (iSetupPkt.iLength == 0) + { + ret = UsbShai::EControlTransferStageStatusIn; + } + else if ((iSetupPkt.iRequestType & KUsbRequestType_DirMask) == KUsbRequestType_DirToDev) + { + // Dir to device means host will send data out + ret = UsbShai::EControlTransferStageDataOut; + } + else + { + // Otherwise, there must be a datain stage follows + ret = UsbShai::EControlTransferStageDataIn; + } + + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "TSetupPkgParser::NextStage %d", ret)); + + return ret; + } + +// Base class of stage sm +TControlStageSm::TControlStageSm(DControlTransferManager& aTransferMgr): + iTransferMgr(aTransferMgr) + { + } + +void TControlStageSm::ChangeToStage(UsbShai::TControlStage aToStage) + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "<> TControlStageSm::ChangeToStage: %s",DebugName[aToStage])); + iTransferMgr.iCurrentStage = aToStage; + } + +void TControlStageSm::ClearPendingRead() + { + iTransferMgr.iReadPending = EFalse; + } + +// Code for DControlTransferManager +// + +DControlTransferManager::DControlTransferManager(MControlTransferIf& aCtrTransIf): + iCtrTransferIf(aCtrTransIf) + { + for(int i=0; iIsRequstAllowed(TControlTransferRequestRead)) + { + if(!iReadPending) + { + iReadPending = ETrue; + return CtrTransferIf().ProcessSetupEndpointZeroRead(); + } + else + { + // A read operation already on going, ignore this request + return KErrNone; + } + } + else + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID " !! SetupEndpointZeroRead discard")); + return KErrNotReady; + } + } + +TInt DControlTransferManager::SetupEndpointZeroWrite(const TUint8* aBuffer, TInt aLength, TBool aZlpReqd) + { + if(iState[iCurrentStage]->IsRequstAllowed(TControlTransferRequestWrite)) + { + return CtrTransferIf().ProcessSetupEndpointZeroWrite(aBuffer,aLength,aZlpReqd); + } + else + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID " !! SetupEndpointZeroWrite discard")); + return KErrNotReady; + } + } + +TInt DControlTransferManager::SendEp0ZeroByteStatusPacket() + { + if(iState[iCurrentStage]->IsRequstAllowed(TControlTransferRequestSendStatus)) + { + iCurrentStage = UsbShai::EControlTransferStageSetup; + return CtrTransferIf().ProcessSendEp0ZeroByteStatusPacket(); + } + else + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID " !! SendEp0ZeroByteStatusPacket discard")); + return KErrNotReady; + } + } + +TInt DControlTransferManager::StallEndpoint(TInt aRealEndpoint) + { + // Endpoint is stalled, we need to reset our state machine. + Reset(); + return CtrTransferIf().ProcessStallEndpoint(aRealEndpoint); + } + +void DControlTransferManager::Ep0SetupPacketProceed() + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID " !! Missed setup packet processed")); + CtrTransferIf().ProcessEp0SetupPacketProceed(); + } + +void DControlTransferManager::Ep0DataPacketProceed() + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID " !! Missed data packet processed")); + CtrTransferIf().ProcessEp0DataPacketProceed(); + } + +void DControlTransferManager::Reset() + { + iCurrentStage = UsbShai::EControlTransferStageSetup; + iReadPending = EFalse; + } + +void DControlTransferManager::Ep0RequestComplete(TUint8* aBuf, + TInt aCount, + TInt aError, + UsbShai::TControlPacketType aPktType) + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "> DControlTransferManager::Ep0RequestComplete, packet type: %s", DebugName[aPktType])); + // If a setup packet comes, update our local setup packet buffer first + if(aPktType == UsbShai::EControlPacketTypeSetup) + { + iPacketParser.Set(aBuf); + // This is the only place this variable to be reset. + iDataTransfered = 0; + } + + // RequestComplete will return ETrue if it can not handle a packet + // And it knows that some other sm can handle it. + // It will update the state to the one which can hanlde that packet first. + TBool furtherProcessNeeded = ETrue; + while(furtherProcessNeeded) + { + __KTRACE_OPT(KUSB, Kern::Printf(" We're at Stage: %s", DebugName[iCurrentStage])); + furtherProcessNeeded = iState[iCurrentStage]->RequestComplete(aCount,aError,aPktType); + __KTRACE_OPT(KUSB, Kern::Printf(" We're moved to stage: %s", DebugName[iCurrentStage])); + } + + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "< DControlTransferManager::Ep0RequestComplete")); + } + +// setup the state machine for a state +void DControlTransferManager::AddState(UsbShai::TControlStage aStage,TControlStageSm& aStageSm) + { + if( (aStage >= UsbShai::EControlTransferStageSetup) && (aStage < UsbShai::EControlTransferStageMax)) + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID " AddState(), Stage: %s", DebugName[aStage])); + iState[aStage] = &aStageSm; + } + } + +// *************** Code for SETUP state machines ************************************** +// +DSetupStageSm::DSetupStageSm(DControlTransferManager& aTransferMgr): + TControlStageSm(aTransferMgr) + { + } + +// WE are waiting a SETUP packet +TBool DSetupStageSm::RequestComplete(TInt aPktSize, TInt aError, UsbShai::TControlPacketType aPktType) + { + TBool ret = EFalse; + + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "DSetupStageSm::RequestComplete")); + + if(aPktType != UsbShai::EControlPacketTypeSetup) + { + // we just discard any non-setup packet + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "ALERT: DSetupStageSm - Non-Setup recieved")); + return ret; + } + + // change state to whatever returned from this call. + ChangeToStage(iTransferMgr.PktParser().NextStage()); + + // We're going to complete User's read request, consume the previous + // read operation + ClearPendingRead(); + + // Setup packet are always need to be processed + iTransferMgr.CtrTransferIf().ProcessSetupPacket(aPktSize,aError); + + return EFalse; + } + +TBool DSetupStageSm::IsRequstAllowed(TControlTransferRequest aRequest) + { + // Allow user to read, No other operation is allowed + TBool ret = (aRequest == TControlTransferRequestRead)?ETrue:EFalse; + + if( ! ret) + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "Warning: request %d was blocked at DSetupStageSm",aRequest)); + } + + return ret; + } + + +// *************** Code for DATA IN state machines ************************************** + +DDataInStageSm::DDataInStageSm(DControlTransferManager& aTransferMgr): + TControlStageSm(aTransferMgr) + { + } + +// We are waiting for a competion of DATA IN packet +TBool DDataInStageSm::RequestComplete(TInt aPktSize, TInt aError, UsbShai::TControlPacketType aPktType) + { + TInt completionCode = aError; + TBool furtherRequest = EFalse; + + switch(aPktType) + { + case UsbShai::EControlPacketTypeSetup: + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "ALERT: DDataInStageSm - Setup recieved")); + // Something goes wrong, host is abandoning the unfinished control transfer + completionCode = KErrGeneral; + + // Force SM restart at setup stage + ChangeToStage(UsbShai::EControlTransferStageSetup); + + // this packet is partially processed here + // need another SM to continue + furtherRequest = ETrue; + } + break; + + case UsbShai::EControlPacketTypeDataIn: + { + // PSL notifing us that the data had been sent to host + // next step is to wait for the status from Host + ChangeToStage(UsbShai::EControlTransferStageStatusOut); + + // In USB spec, a compete control transfer must inclue a status stage + // which is not case in reality,some PSL/Hardware will swallow the + // Status out report, so, we just complete client normally. + } + break; + + default: + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "ALERT: DDataInStageSm - %s recieved",DebugName[aPktType])); + + // Unexpected packet will be discard, and lead us reset state machine + // so that we can wait for next SETUP packet. + // Of course error will be report to any client if any there. + ChangeToStage(UsbShai::EControlTransferStageSetup); + completionCode = KErrGeneral; + } + break; + } + + iTransferMgr.CtrTransferIf().ProcessDataInPacket(aPktSize,completionCode); + + return furtherRequest; + } + +TBool DDataInStageSm::IsRequstAllowed(TControlTransferRequest aRequest) + { + // Only write is possible because host is waiting for data from us + TBool ret = (aRequest == TControlTransferRequestWrite)?ETrue:EFalse; + + if( ! ret) + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "Warning: request %d was blocked at DDataInStageSm",aRequest)); + } + + return ret; + }; + +// *************** Code for STATUS OUT state machines ************************************** +DStatusOutStageSm::DStatusOutStageSm(DControlTransferManager& aTransferMgr): + TControlStageSm(aTransferMgr) + { + } + +// We are waiting for a competion of STATUS OUT or a SETUP packet if PSL or hardware don't +// complete a status in packet +TBool DStatusOutStageSm::RequestComplete(TInt aPktSize, TInt aError, UsbShai::TControlPacketType aPktType) + { + TBool furtherRequest = EFalse; + TInt completionCode = aError; + + switch(aPktType) + { + case UsbShai::EControlPacketTypeSetup: + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "ALERT: DStatusOutStageSm - Setup recieved")); + // hw or PSL may not send back the status packet for a DATA OUT + // and we're ok for this, just back to EControlTransferStageSetup stage + + // Force SM restart at setup stage + ChangeToStage(UsbShai::EControlTransferStageSetup); + + // this packet is partially processed here + // need another SM to continue + furtherRequest = ETrue; + } + break; + + case UsbShai::EControlPacketTypeStatusOut: + { + // Force SM restart at setup stage + ChangeToStage(UsbShai::EControlTransferStageSetup); + } + break; + + default: + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "ALERT: DStatusOutStageSm - %s recieved",DebugName[aPktType])); + + // Unexpected packet will be discard, and lead us reset state machine + // so that we can wait for next SETUP packet. + // Of course error will be report to any client if any there. + ChangeToStage(UsbShai::EControlTransferStageSetup); + completionCode = KErrGeneral; + } + break; + } + + iTransferMgr.CtrTransferIf().ProcessStatusOutPacket(completionCode); + + return furtherRequest; + + } + +TBool DStatusOutStageSm::IsRequstAllowed(TControlTransferRequest aRequest) + { + // Read is ok since client don't care the status out stage. + // and this lead no hurt to anybody. + TBool ret = (aRequest == TControlTransferRequestRead)?ETrue:EFalse; + + if( ! ret) + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "Warning: request %d was blocked at DStatusOutStageSm",aRequest)); + } + + return ret; + }; + +// *************** Code for DATA OUT state machines ************************************** +// +DDataOutStageSm::DDataOutStageSm(DControlTransferManager& aTransferMgr): + TControlStageSm(aTransferMgr) + { + } + +TBool DDataOutStageSm::RequestComplete(TInt aPktSize, TInt aError, UsbShai::TControlPacketType aPktType) + { + TBool furtherRequest = EFalse; + TInt completionCode = aError; + + switch(aPktType) + { + case UsbShai::EControlPacketTypeSetup: + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "ALERT: DDataOutStageSm - Setup recieved")); + // Host is abandon the previous Transfer + completionCode = KErrGeneral; + + // Force SM restart at setup stage + ChangeToStage(UsbShai::EControlTransferStageSetup); + + // this packet is partially processed here + // need another SM to continue + furtherRequest = ETrue; + } + break; + + case UsbShai::EControlPacketTypeDataOut: + { + iTransferMgr.DataReceived(aPktSize); + + if(!iTransferMgr.IsMoreBytesNeeded()) + { + // We had recieved enough bytes as indicated by the setup + // packet, Data stage is finished. enter STATUS IN state + ChangeToStage(UsbShai::EControlTransferStageStatusIn); + } + } + break; + + case UsbShai::EControlPacketTypeStatusIn: + { + // Status in had been sent to host + // return and waiting for new SETUP + ChangeToStage(UsbShai::EControlTransferStageSetup); + } + break; + + default: + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "ALERT: DDataOutStageSm - %s recieved",DebugName[aPktType])); + + // Unexpected packet will be discard, and lead us reset state machine + // so that we can wait for next SETUP packet. + // Of course error will be report to any client if any there. + ChangeToStage(UsbShai::EControlTransferStageSetup); + completionCode = KErrGeneral; + } + break; + } + + ClearPendingRead(); + iTransferMgr.CtrTransferIf().ProcessDataOutPacket(aPktSize,completionCode); + + return furtherRequest; + } + +TBool DDataOutStageSm::IsRequstAllowed(TControlTransferRequest aRequest) + { + // only read operation is allowed in data out stage. + TBool ret = (aRequest == TControlTransferRequestRead)?ETrue:EFalse; + + if( ! ret) + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "Warning: request %d was blocked at DDataOutStageSm",aRequest)); + } + + return ret; + }; + +// *************** Code for DATA OUT state machines ************************************** +// +DStatusInStageSm::DStatusInStageSm(DControlTransferManager& aTransferMgr): + TControlStageSm(aTransferMgr) + { + } + +TBool DStatusInStageSm::RequestComplete(TInt aPktSize, TInt aError, UsbShai::TControlPacketType aPktType) + { + TBool furtherRequest = EFalse; + TInt completionCode = KErrNone; + + switch(aPktType) + { + case UsbShai::EControlPacketTypeSetup: + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "ALERT: DStatusInStageSm - Setup recieved")); + // Status in is an optional for PSL + // If we received a setup packet here, we assume the previous + // status in packet had been successfully sent to host. + + // Force SM restart at setup stage + ChangeToStage(UsbShai::EControlTransferStageSetup); + + // this packet is partially processed here + // need another SM to continue + furtherRequest = ETrue; + } + break; + + case UsbShai::EControlPacketTypeStatusIn: + { + // Status in had been recieved, monitor setup packet then. + ChangeToStage(UsbShai::EControlTransferStageSetup); + } + break; + + default: + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "ALERT: DStatusInStageSm - %s recieved",DebugName[aPktType])); + + // Unexpected packet will be discard, and lead us reset state machine + // so that we can wait for next SETUP packet. + // Of course error will be report to any client if any there. + ChangeToStage(UsbShai::EControlTransferStageSetup); + completionCode = KErrGeneral; + } + break; + } + + iTransferMgr.CtrTransferIf().ProcessStatusInPacket(completionCode); + + return furtherRequest; + } + +TBool DStatusInStageSm::IsRequstAllowed(TControlTransferRequest aRequest) + { + // Read is ok even we are wait for the client to send a zero status packet + TBool ret = ((aRequest == TControlTransferRequestSendStatus) || + (aRequest == TControlTransferRequestRead))?ETrue:EFalse; + + if( ! ret) + { + __KTRACE_OPT(KUSB, Kern::Printf(CTSM_ID "Warning: request %d was blocked at DStatusInStageSm",aRequest)); + } + + return ret; + } + +// End of file +