usbdrv/peripheral/pdd/pil/src/controltransfersm.cpp
changeset 33 089413cdde3c
child 59 bbdce6bffaad
--- /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<const TUint8*>(aSetupBuf)[0];
+    iSetupPkt.iRequest = static_cast<const TUint8*>(aSetupBuf)[1];
+    // TUint16 index from here!
+    iSetupPkt.iValue = SWAP_BYTES_16((reinterpret_cast<const TUint16*>(aSetupBuf))[1]);
+    iSetupPkt.iIndex = SWAP_BYTES_16((reinterpret_cast<const TUint16*>(aSetupBuf))[2]);
+    iSetupPkt.iLength = SWAP_BYTES_16((reinterpret_cast<const TUint16*>(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; i<UsbShai::EControlTransferStageMax; i++)
+        {
+        iState[i] = NULL;
+        }
+    
+    Reset();
+    }
+
+TInt DControlTransferManager::SetupEndpointZeroRead()
+    {
+    if(iState[iCurrentStage]->IsRequstAllowed(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
+