hti/HtiFramework/src/HtiCommAdapter.cpp
branchRCL_3
changeset 59 8ad140f3dd41
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hti/HtiFramework/src/HtiCommAdapter.cpp	Wed Oct 13 16:17:58 2010 +0300
@@ -0,0 +1,556 @@
+/*
+* 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:  CHtiCommAdapter implementation
+*
+*/
+
+
+#include "HtiCommAdapter.h"
+#include "HtiDispatcher.h"
+#include "HtiCommPluginInterface.h"
+#include "HtiMessage.h"
+#include "HtiNotifier.h"
+
+#include "HtiLogging.h"
+
+//default value for max message size for incoming messages
+//used if value in constructor is not valid (<0)
+const static TInt KMaxMessageSize = 10 * 1024; // 10 KB
+
+const static TInt KHtiCommPriority = 2;
+
+_LIT( KCommPanic, "HtiCommAdapter" );
+
+CHtiCommAdapter* CHtiCommAdapter::NewLC( CHTICommPluginInterface* aCommPlugin,
+    CHtiDispatcher* aDispatcher, TInt aMaxMsgSize )
+    {
+    CHtiCommAdapter* obj = new (ELeave) CHtiCommAdapter( aDispatcher,
+                                        aCommPlugin, aMaxMsgSize );
+    CleanupStack::PushL( obj );
+    obj->ConstructL();
+    return obj;
+    }
+
+CHtiCommAdapter* CHtiCommAdapter::NewL( CHTICommPluginInterface* aCommPlugin,
+    CHtiDispatcher* aDispatcher, TInt aMaxMsgSize )
+    {
+    CHtiCommAdapter* obj = NewLC( aCommPlugin, aDispatcher, aMaxMsgSize );
+    CleanupStack::Pop();
+    return obj;
+    }
+
+CHtiCommAdapter::CHtiCommAdapter( CHtiDispatcher* aDispatcher,
+                           CHTICommPluginInterface* aCommPlugin,
+                           TInt aMaxMsgSize )
+    :CActive( KHtiCommPriority ),
+    iState( EIdle ),
+    iDispatcher( aDispatcher ),
+    iCommPlugin( aCommPlugin ),
+    iBufferPtr( NULL, 0 ),
+    iMsgToReceive( NULL ),
+    iMsgToSend( NULL ),
+    iMsgToSendPtr( NULL, 0 ),
+    iMsgToSendOffset( 0 ),
+    iSkipLength( 0 ),
+    iMaxMessageSize( aMaxMsgSize > 0 ? aMaxMsgSize : KMaxMessageSize )
+    {
+    HTI_LOG_FORMAT( "MaxMsgSize %d", iMaxMessageSize );
+    };
+
+CHtiCommAdapter::~CHtiCommAdapter()
+    {
+    HTI_LOG_FUNC_IN( "CHtiCommAdapter::~CHtiCommAdapter" );
+    Cancel();
+
+    delete iBuffer;
+    delete iLeftovers;
+
+    delete iMsgToReceive;
+    delete iMsgToSend;
+
+    HTI_LOG_FUNC_OUT( "CHtiCommAdapter::~CHtiCommAdapter" );
+    }
+
+void CHtiCommAdapter::ConstructL()
+    {
+    HTI_LOG_FUNC_IN( "CHtiCommAdapter::ConstructL" );
+
+    //allocate recommended buffer plus some extra space
+    //needed to handle msg parsing
+    iBuffer = HBufC8::NewL( iCommPlugin->GetReceiveBufferSize() +
+                CHtiMessage::MinHeaderSize() );
+
+    iBuffer->Des().SetLength( iCommPlugin->GetReceiveBufferSize() );
+    iBufferPtr.Set( iBuffer->Des().LeftTPtr(
+            iCommPlugin->GetReceiveBufferSize() ) );
+
+    iLeftovers = HBufC8::NewL( CHtiMessage::MinHeaderSize() );
+
+    CActiveScheduler::Add( this );
+    HTI_LOG_FUNC_OUT( "CHtiCommAdapter::ConstructL" );
+    }
+
+void CHtiCommAdapter::ReceiveMessage()
+    {
+    ReceiveMessage( EFalse );
+    }
+
+void CHtiCommAdapter::ReceiveMessage( TBool aContinue )
+    {
+    HTI_LOG_FUNC_IN( "CHtiCommAdapter::ReceiveMessage" );
+
+    iState = aContinue?EReceivingCont:EReceiving;
+    iCommPlugin->Receive( iBufferPtr, iStatus );
+    SetActive();
+
+    HTI_LOG_FUNC_OUT( "CHtiCommAdapter::ReceiveMessage" );
+    }
+
+void CHtiCommAdapter::SendMessage( CHtiMessage* aMessage )
+    {
+    HTI_LOG_FUNC_IN( "CCommAdapter::SendMessage" );
+    iState = ESending;
+    if ( aMessage != NULL )
+        {
+        HTI_LOG_TEXT( "send first packet" );
+        iMsgToSend = aMessage;
+        iMsgToSendOffset = 0;
+
+        //first send header
+        iMsgToSendPtr.Set( iMsgToSend->Header() );
+        }
+    else if ( iMsgToSend != NULL )
+        {
+        //send next chunk
+        if ( iMsgToSend->BodySize() <=
+            ( iMsgToSendOffset + iCommPlugin->GetSendBufferSize() ) )
+            {
+            HTI_LOG_TEXT( "send remainder" );
+            iMsgToSendPtr.Set( iMsgToSend->Body().Mid( iMsgToSendOffset ) );
+            iMsgToSendOffset = iMsgToSend->BodySize(); //last sending
+            }
+        else
+            {
+            HTI_LOG_TEXT( "send packet" );
+            iMsgToSendPtr.Set( iMsgToSend->Body().Mid( iMsgToSendOffset,
+                                    iCommPlugin->GetSendBufferSize() ) );
+            iMsgToSendOffset += iCommPlugin->GetSendBufferSize();
+            }
+        }
+    else
+        {
+        User::Panic( KCommPanic, KErrGeneral );
+        }
+
+    iCommPlugin->Send( iMsgToSendPtr, iStatus );
+    SetActive();
+
+    HTI_LOG_FUNC_OUT( "CCommAdapter::SendMessage" );
+    }
+
+void CHtiCommAdapter::Reset()
+    {
+    HTI_LOG_FUNC_IN( "CHtiCommAdapter::Reset" );
+    Cancel();
+    //receiving msg
+    delete iMsgToReceive;
+    iMsgToReceive = NULL;
+
+    //send msg
+    delete iMsgToSend;
+    iMsgToSend = NULL;
+    iMsgToSendOffset = 0;
+
+    //empty buffers
+    iLeftovers->Des().Zero();
+    iBuffer->Des().Zero();
+    iSkipLength = 0;
+
+    //set idle
+    iState = EIdle;
+
+    HTI_LOG_FUNC_OUT( "CHtiCommAdapter::Reset" );
+    }
+
+void CHtiCommAdapter::RunL()
+    {
+    HTI_LOG_FUNC_IN( "CHtiCommAdapter::RunL" );
+    HTI_LOG_FORMAT( "iStatus %d", iStatus.Int() );
+
+    //handle reset code common for all states
+    if ( iStatus == KErrComModuleReset )
+        {
+        HTI_LOG_TEXT( "Reset received from comm module" );
+        //reset dispatcher
+        iDispatcher->Reset();
+        //dispatcher will call Reset() for comm adapters and reissue requests
+        return;
+        }
+
+    //check for critical (unrecoverable) communication errors
+    else if ( iStatus == KErrServerTerminated )
+        {
+        if ( iDispatcher->GetShowErrorDialogs() )
+            {
+            User::Panic( KCommPanic, KErrServerTerminated );
+            }
+        else
+            {
+            User::Exit( KErrServerTerminated );
+            }
+        }
+    // USB errors from d32usbc.h
+    else if ( -6700 > iStatus.Int() && iStatus.Int() > -6712 )
+        {
+        if(iDispatcher->CommReconnect())
+            {
+            return;
+            }
+
+        if ( iDispatcher->GetShowErrorDialogs() )
+            {
+            TBuf<48> errorText;
+            errorText.Append( _L( "USB connection lost (" ) );
+            errorText.AppendNum( iStatus.Int() );
+            errorText.Append( _L( "). HTI stopped." ) );
+            CHtiNotifier::ShowErrorL( errorText );
+            }
+        User::Exit( KErrDisconnected );
+        }
+    else if ( iStatus == KErrDisconnected )
+        {
+        // This happens if Bluetooth connection is lost.
+        if(iDispatcher->CommReconnect())
+            {
+            return;
+            }
+        
+        if ( iDispatcher->GetShowErrorDialogs() )
+            {
+            CHtiNotifier::ShowErrorL(
+                    _L( "Connection lost. HTI stopped." ), KErrDisconnected );
+            }
+        User::Exit( KErrDisconnected );
+        }
+
+    switch ( iState )
+        {
+        case EReceiving:
+            //process
+            HTI_LOG_TEXT( "EReceiving" );
+            if ( iStatus == KErrNone )
+                {
+                iBuffer->Des().SetLength( iBufferPtr.Length() );
+                TRAPD( err, HandleReceiveL() );
+                if ( err != KErrNone )
+                    {
+                    HTI_LOG_FORMAT( "Error in HandleReceiveL %d", err );
+
+                    delete iMsgToReceive;
+                    iMsgToReceive = NULL;
+
+                    iDispatcher->Notify( err );
+
+                    ReceiveMessage();
+                    }
+                }
+            else
+                {
+                HTI_LOG_FORMAT( "Error %d, reissue request", iStatus.Int() );
+                iDispatcher->Notify( iStatus.Int() );
+                User::After(2000000);
+                ReceiveMessage();
+                }
+            break;
+        case EReceivingCont:
+            //process
+            HTI_LOG_TEXT( "EReceivingCont" );
+            if ( iStatus == KErrNone )
+                {
+                iBuffer->Des().SetLength( iBufferPtr.Length() );
+                TRAPD( err, HandleReceiveContL() );
+                if ( err != KErrNone )
+                    {
+                    HTI_LOG_FORMAT( "Error in HandleReceiveContL %d", err );
+
+                    delete iMsgToReceive;
+                    iMsgToReceive = NULL;
+
+                    iDispatcher->Notify( err );
+
+                    ReceiveMessage();
+                    }
+                }
+            else
+                {
+                HTI_LOG_FORMAT( "Error %d, reissue request", iStatus.Int() );
+                HTI_LOG_TEXT( "and dismiss received HTI msg beginning" );
+                delete iMsgToReceive;
+                iMsgToReceive = NULL;
+
+                iDispatcher->Notify( iStatus.Int() );
+                User::After(2000000);
+                ReceiveMessage();
+                }
+            break;
+        case ESending:
+            HTI_LOG_TEXT( "ESending" );
+            if ( iStatus == KErrNone )
+                {
+                HandleSend();
+                }
+            else
+                {
+                HTI_LOG_FORMAT( "Error %d, stop sending, go to EIdle", iStatus.Int() );
+
+                delete iMsgToSend;
+                iMsgToSend = NULL;
+                iMsgToSendOffset = 0;
+                iState = EIdle;
+
+                iDispatcher->Notify( iStatus.Int() );
+                }
+            break;
+        case EIdle:
+            //ERROR
+            HTI_LOG_TEXT( "EIdle" );
+            User::Panic( KCommPanic, KErrGeneral );
+            break;
+        default:
+            //ERROR
+            HTI_LOG_TEXT( "default" );
+            User::Panic( KCommPanic, KErrGeneral );
+        };
+
+    HTI_LOG_FUNC_OUT( "CHtiCommAdapter::RunL" );
+    }
+
+void CHtiCommAdapter::HandleReceiveL()
+    {
+    TBool toContinue = EFalse; //for reissuing state
+
+    if ( iLeftovers->Length() > 0 )
+        {
+        HTI_LOG_TEXT( "handle leftovers" );
+        //if something left from previous time
+        //insert it to the buffer beggining
+        //check that there is enough space (should be)
+        if ( iBuffer->Length() + iLeftovers->Length() <=
+             iBuffer->Des().MaxLength() )
+            {
+            iBuffer->Des().Insert( 0, *iLeftovers );
+            }
+        else
+            {
+            //error
+            HTI_LOG_TEXT( "iMsgToReceive contain too much leftovers, drop them" );
+            }
+
+        iLeftovers->Des().Zero();
+        }
+
+    //use loop cause can be several HTI messages in iBuffer
+    while ( iBuffer->Length() >= CHtiMessage::MinHeaderSize() )
+        {
+        HTI_LOG_FORMAT( "iBuffer.Length() %d", iBuffer->Length() );
+        HTI_LOG_TEXT( "check header" );
+
+        if ( CHtiMessage::CheckValidHtiHeader( *iBuffer ) )
+            {
+            HTI_LOG_TEXT( "valid header" );
+            TInt msgSize = CHtiMessage::Size( *iBuffer );
+            HTI_LOG_FORMAT( "msgSize %d", msgSize );
+            HTI_LOG_HEX( iBuffer->Des().Ptr(), 14 );
+
+            __ASSERT_ALWAYS( iMsgToReceive == NULL,
+                            User::Panic( KCommPanic, KErrGeneral ) );
+
+            //check msgSize is acceptable
+            if ( msgSize > iMaxMessageSize )
+                {
+                //send err message
+                iDispatcher->DispatchOutgoingErrorMessage( EHtiErrBigMessage );
+                iSkipLength = msgSize;
+                }
+            else if ( msgSize > iDispatcher->GetFreeMemory() )
+                {
+                //send err message
+                iDispatcher->DispatchOutgoingErrorMessage( EHtiErrNoMemory );
+                iSkipLength = msgSize;
+                }
+            else
+                {
+                iMsgToReceive = CHtiMessage::NewL( *iBuffer );
+                }
+
+            if ( iMsgToReceive == NULL )
+                {
+                HTI_LOG_TEXT( "skip message" );
+                if ( iSkipLength > iBuffer->Length() )
+                    {
+                    iSkipLength -= iBuffer->Length();
+                    iBuffer->Des().Zero();
+                    // and continue to receive HTI message
+                    //switch state to EReceiveCont
+                    toContinue = ETrue;
+                    }
+                else
+                    {
+                    //should not be here, but just in case
+                    iBuffer->Des().Delete( 0, iSkipLength );
+                    iSkipLength = 0;
+                    }
+                }
+            else if ( iMsgToReceive->IsBodyComplete() )
+                {
+                HTI_LOG_TEXT( "handle small message" );
+                //iMsgToReceive is already contain whole HTI message
+                iDispatcher->DispatchIncomingMessage( iMsgToReceive );
+
+                //delete sent msg from buffer (and procces the rest)
+                iBuffer->Des().Delete( 0, iMsgToReceive->Size() );
+                iMsgToReceive = NULL;
+                }
+            else
+                {
+                HTI_LOG_TEXT( "start receive big message" );
+                //clear it to leave loop
+                iBuffer->Des().Zero();
+                // and continue to receive HTI message
+                //switch state to EReceiveCont
+                toContinue = ETrue;
+                }
+            }
+        else
+            {
+            HTI_LOG_TEXT( "invalid header, dismiss buffer and reissue request" );
+            HTI_LOG_HEX( iBuffer->Des().Ptr(), 14 );
+            // clear all buffer cause don't know where is valid header starts
+            // and wait for the next packet
+            iBuffer->Des().Zero(); //to leave loop
+            }
+        }
+
+    if ( iBuffer->Length() > 0 )
+        {
+        HTI_LOG_FORMAT( "copy leftovers %d", iBuffer->Length() );
+        //header beggining left in the buffer
+        //copy it to iLeftovers
+        ( *iLeftovers ) = ( *iBuffer );
+        }
+    //reissue request
+    ReceiveMessage( toContinue );
+    }
+
+void CHtiCommAdapter::HandleReceiveContL()
+    {
+    HTI_LOG_FORMAT( "In buffer: %d", iBuffer->Length() );
+    TBool toContinue = ETrue;
+
+    if ( iMsgToReceive != NULL && iSkipLength == 0 )
+        {
+        //add new packet to a message
+        TInt copyLen = iMsgToReceive->AddToBody( *iBuffer );
+
+        //delete what we copied to the message
+        iBuffer->Des().Delete( 0, copyLen );
+
+        if ( iMsgToReceive->IsBodyComplete() )
+            {
+            HTI_LOG_FORMAT( "body complete: %d", iMsgToReceive->BodySize() );
+
+            iDispatcher->DispatchIncomingMessage( iMsgToReceive );
+            iMsgToReceive = NULL;
+
+            //message complete, go to state EReceive
+            toContinue = EFalse;
+            }
+        }
+    else if ( iSkipLength > 0 )
+        {
+        HTI_LOG_FORMAT( "continue skipping, remains %d", iSkipLength );
+
+        if ( iSkipLength > iBuffer->Length() )
+            {
+            iSkipLength -= iBuffer->Length();
+            iBuffer->Des().Zero();
+            }
+        else
+            {
+            //skip last bytes
+            iBuffer->Des().Delete( 0, iSkipLength );
+            iSkipLength = 0;
+
+            toContinue = EFalse;
+            }
+        }
+
+    if ( iBuffer->Length() > 0 )
+        {
+        //process remainder and reissue in HandleReceiveL()
+        HandleReceiveL();
+        }
+    else
+        {
+        //reissue
+        ReceiveMessage(toContinue);
+        }
+    }
+
+void CHtiCommAdapter::HandleSend()
+    {
+    if ( iMsgToSendOffset < iMsgToSend->BodySize() )
+        {
+        //reissuse sending
+        SendMessage( NULL );
+        }
+    else
+        {
+        HTI_LOG_TEXT( "finish send, go to idle" );
+        iMsgToSendOffset = 0;
+        delete iMsgToSend;
+        iMsgToSend = NULL;
+        iState = EIdle;
+        iDispatcher->Notify( KErrNone );
+        }
+    }
+
+TInt CHtiCommAdapter::RunError( TInt aError )
+    {
+    TInt err = aError;
+    switch ( aError )
+        {
+        case KErrNoMemory:
+            break;
+        case KErrNotFound:
+            break;
+        }
+    return err;
+    }
+
+void CHtiCommAdapter::DoCancel()
+    {
+    switch ( iState )
+        {
+        case EReceiving:
+        case EReceivingCont:
+            iCommPlugin->CancelReceive();
+            break;
+        case ESending:
+            iCommPlugin->CancelSend();
+            break;
+        default:
+            break;
+        }
+    }