usbengines/usbdevcon/src/cstatemachine.cpp
changeset 0 1e05558e2206
child 87 18fe5224f0dc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usbengines/usbdevcon/src/cstatemachine.cpp	Thu Dec 17 09:14:30 2009 +0200
@@ -0,0 +1,384 @@
+/*
+* Copyright (c) 2007 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:  usbdevcon state machine
+*
+*/
+
+
+#include "cusbdevcon.h"
+#include "cstatemachine.h"
+#include "cep0reader.h"
+#include "cep0writer.h"
+#include "crequestshandler.h"
+#include "debug.h"
+
+const TUint KOneByte = 8; // for shifting data to one byte
+
+// binary constant for checking if bit 7 is set
+const TUint KUsbDevConBit7 = 0x80;
+
+const TUint KLengthLoByte = 6;
+const TUint KLengthHiByte = 7;
+        
+// ---------------------------------------------------------------------------
+// Two-phase construction
+// ---------------------------------------------------------------------------
+//
+CStateMachine* CStateMachine::NewL(CRequestsHandler& aRequestsHandler, RDevUsbcClient& iLdd)
+    {
+    FLOG( _L( "[USBDEVCON]\tCStateMachine::NewL" ) );
+    
+    CStateMachine* self = new (ELeave) CStateMachine(aRequestsHandler, iLdd);
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    CleanupStack::Pop(self);
+    return self;    
+    }
+    
+// ---------------------------------------------------------------------------
+// Two-phase constructon
+// ---------------------------------------------------------------------------
+//
+void CStateMachine::ConstructL()
+    {
+    
+    FLOG( _L( "[USBDEVCON]\tCStateMachine::ConstructL" ) );
+    
+    iBuffer.CreateL(0); // later will be reallocated with required size
+    
+    // reader
+    iEP0Reader = CEP0Reader::NewL(*this,iLdd);
+    
+    // writer
+    iEP0Writer = CEP0Writer::NewL(*this, iLdd);
+    
+    }
+    
+// ---------------------------------------------------------------------------
+// Default construction
+// ---------------------------------------------------------------------------
+//
+CStateMachine::CStateMachine(CRequestsHandler& aRequestsHandler, RDevUsbcClient& aLdd) : 
+                            iRequestsHandler(aRequestsHandler),
+                            iLdd (aLdd),
+                            iState(ENoneState)
+    {
+    }
+
+// ---------------------------------------------------------------------------
+// Destruction
+// ---------------------------------------------------------------------------
+//
+CStateMachine::~CStateMachine()
+    {
+    delete iEP0Writer;
+    delete iEP0Reader;
+    iBuffer.Close();    
+    }   
+
+// ---------------------------------------------------------------------------
+// Starts state machine
+// ---------------------------------------------------------------------------
+//
+void CStateMachine::Start()
+    {
+    
+    FLOG( _L( "[USBDEVCON]\tCStateMachine::Start" ) );
+    
+    // resetting, if it's been already started
+    if(IsStarted())
+        {
+        Stop();
+        }
+    
+    iState = ESetupStage;   
+    iEP0Reader->ReadSetupPacket();
+                
+    }
+    
+// ---------------------------------------------------------------------------
+// Stops machine
+// ---------------------------------------------------------------------------
+//
+void CStateMachine::Stop()
+    {
+    
+    FLOG( _L( "[USBDEVCON]\tCStateMachine::Stop" ) );
+        
+    iEP0Reader->Cancel();
+    iEP0Writer->Cancel();
+    
+    iState = ENoneState;
+    
+    }
+    
+// ---------------------------------------------------------------------------
+// ETrue if state machine is started
+// ---------------------------------------------------------------------------
+//
+TInt CStateMachine::IsStarted() const
+    {
+    
+    if(ENoneState == iState)
+        {
+        FLOG( _L( "[USBDEVCON]\tCStateMachine::IsStarted == EFalse" ) );
+        return EFalse;
+        }
+        else
+        {
+        FLOG( _L( "[USBDEVCON]\tCStateMachine::IsStarted == ETrue" ) );
+        }
+    return ETrue;
+    }
+    
+// ---------------------------------------------------------------------------
+// Something has been read from EP0 
+// ---------------------------------------------------------------------------
+//
+void CStateMachine::ReadEP0(RBuf8& aBuffer, const TRequestStatus& aStatus)
+    {
+    
+    FTRACE(FPrint(
+            _L("[USBDEVCON]\tCStateMachine::ReadEP0: BufferLength = %d, aStatus = %d" ),aBuffer.Length(), aStatus.Int()));
+    
+    // all errors while reading data lead to idle state (ESetupStage in our case)
+    if(KErrNone != aStatus.Int())
+        {
+        // restart
+        Start();
+        return;
+        }
+    
+    TInt err(KErrNone);
+    
+    switch(iState)
+        {
+        case ESetupStage: // setup transaction received
+            {
+            
+            FLOG( _L( "[USBDEVCON]\tCStateMachine::ReadEP0 processing ESetupStage" ) );
+            
+            if(aBuffer.Length() != KSetupPacketLength) // SetupPacket is always 8 bytes
+                {
+                Start();
+                return;
+                }
+            
+            ProcessSetupPacket(aBuffer);
+                                
+            break;
+            }
+        case EDataStage:  // some data received from host. This data is required for the request, received on setup stage
+            {
+            
+            FLOG( _L( "[USBDEVCON]\tCStateMachine::ReadEP0 processing EDataStage" ) );
+
+            // handle request, providing data together with request descriptor
+            // iBuffer == request, aBuffer == data from host
+            err = iRequestsHandler.Handle(iBuffer, aBuffer);
+            
+            if(KErrNone != err) // some error happened while handling request
+                {
+                iLdd.EndpointZeroRequestError(); // stall EP0
+                }
+                else
+                {
+                // send Status Packet, indicating that we received request, data, and handled request. OK
+                iLdd.SendEp0StatusPacket();
+                }
+            
+            // all done, go to idle state
+            iState = ESetupStage;
+            iEP0Reader->ReadSetupPacket();
+            
+            break;
+            
+            }
+        default:
+            {
+            FLOG( _L( "[USBDEVCON]\tCStateMachine::ReadEP0 processing ***NOT_DEFINED state***" ) );
+            }
+            
+        }
+    
+    }
+
+// ---------------------------------------------------------------------------
+// Processing setup packet 
+// ---------------------------------------------------------------------------
+//
+void CStateMachine::ProcessSetupPacket(RBuf8& aSetupPacket)
+    {
+    
+    FLOG( _L( "[USBDEVCON]\tCStateMachine::ProcessSetupPacket" ) );
+    
+    TUint datalength(0); // data length, to be received from host
+            
+    if(IsDataFromHostRequired(aSetupPacket, datalength)) // then goes to data stage
+        {
+        
+        FTRACE(FPrint(
+            _L("[USBDEVCON]\tCStateMachine::ProcessSetupPacket. Data from host is required: %d bytes" ),datalength));
+
+        // save request, until receiving following data
+        iBuffer.Close();
+        iBuffer.Create(aSetupPacket);
+                
+        // switch to Data state
+        iState = EDataStage;
+        iEP0Reader->Read(datalength);
+    
+        return;
+        
+        }
+            
+    TInt err(KErrNone); 
+                        
+    // Handle request. It does not require data from host   
+    // aSetupPacket == request, iBuffer = result of the request
+    err = iRequestsHandler.Handle(aSetupPacket, iBuffer);
+        
+    if(KErrNone != err) // some error happened while handling request
+        {
+        
+        FTRACE(FPrint(
+            _L("[USBDEVCON]\tCStateMachine::ProcessSetupPacket. Error while handling request, errcode: %d" ), err));
+
+        iLdd.EndpointZeroRequestError(); // stall EP0
+            
+        // listen to EP0
+        iState = ESetupStage;
+        iEP0Reader->ReadSetupPacket();
+        
+        return;
+        }
+            
+    // send response, size of datalength
+    if(IsDataFromDeviceRequired(aSetupPacket, datalength))
+        {
+        
+        FTRACE(FPrint(
+          _L("[USBDEVCON]\tCStateMachine::ProcessSetupPacket. Data from device is required: %d bytes" ),datalength));
+
+        iState = EStatusStage;
+        iEP0Writer->Write(iBuffer, datalength);
+        
+        return;
+                
+        }
+    
+    // status stage 
+    iLdd.SendEp0StatusPacket();
+                
+    // all is done, listen to EP0, in setup stage
+    iState = ESetupStage;
+    iEP0Reader->ReadSetupPacket();
+    
+    }
+    
+
+// ---------------------------------------------------------------------------
+// Something has been written to EP0 
+// ---------------------------------------------------------------------------
+//
+void CStateMachine::WroteEP0(const TRequestStatus& aStatus)
+    {
+    
+    FTRACE(FPrint(
+            _L("[USBDEVCON]\tCStateMachine::WroteEP0: iStatus = %d" ), aStatus.Int()));
+
+    // all errors while writing data lead to idle state (ESetupStage in our case)
+    if(KErrNone != aStatus.Int())
+        {
+        // restart
+        Start();
+        }
+        
+    switch(iState)
+        {
+        case EStatusStage:
+            {
+            
+            FLOG( _L( "[USBDEVCON]\tCStateMachine::WroteEP0 EStatusStage -> ESetupStage" ) );
+            
+            // successfully wrote data to EP0
+            // go to idle
+            iState = ESetupStage;
+            iEP0Reader->ReadSetupPacket();
+            
+            break;
+            }
+        default:
+            {
+            FLOG( _L( "[USBDEVCON]\tCStateMachine::WroteEP0 ***ENoneState***" ) );
+            }
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// ETrue if data required to be send from host 
+// ---------------------------------------------------------------------------
+//
+TBool CStateMachine::IsDataFromHostRequired(const RBuf8& aSetupPacket, TUint& aDataLength) const
+    {
+        // bits 6 and 7 of SetupPacket contain wLength - length of data in Data stage  
+        aDataLength = static_cast<TUint16>(aSetupPacket[KLengthLoByte] |
+                                            (aSetupPacket[KLengthHiByte] << KOneByte));
+        if(0 == aDataLength)
+            {
+            // no data required in any direction
+            return EFalse;
+            }
+            
+        // direction of data
+        // bit 7 of byte 0 of aSetupPacket (means bmRequestType one) contains:
+        // 0, if no data or data goes from host to device
+        // 1 means data goes from device to host
+        if(KUsbDevConBit7 & aSetupPacket[0])
+            { // bit 7 is set => data flow is from device to host
+            return EFalse;
+            }
+            
+        return ETrue;
+        
+    }
+
+// ---------------------------------------------------------------------------
+// ETrue if data required to be send to host
+// ---------------------------------------------------------------------------
+//
+TBool CStateMachine::IsDataFromDeviceRequired(const RBuf8& aSetupPacket, TUint& aDataLength) const
+    {
+        // bytes 6 and 7 of SetupPacket contain wLength - length of data in Data stage
+        aDataLength = static_cast<TUint16>(aSetupPacket[KLengthLoByte] |
+                                            (aSetupPacket[KLengthHiByte] << KOneByte));
+        if(0 == aDataLength)
+            {
+            // no data required in any direction
+            return EFalse;
+            }
+            
+        // direction of data
+        // bit 7 of byte 0 of aSetupPacket (means bmRequestType one) contains:
+        // 0, if no data or data goes from host to device
+        // 1 means data goes from device to host
+        if(KUsbDevConBit7 & aSetupPacket[0])
+            { // bit 7 is set => data flow is from device to host
+            return ETrue;
+            }
+            
+        return EFalse;
+    }
+
+