/*
* Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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: 
*
*/



// INCLUDE FILES
#include "dpdef.h"        // dataport definitions
#include "dpdataport.h"   // dataport main and c32 interface
#include "dpdte2tx.h"     // moves data from client to tx buffer
#include "dpflowctrl.h"   // flow control handling
#include "dpdataconfig.h" // configuration store
#include "dpescdetect.h"  // escape sequence detection
#include "dprx2dte.h"     // moves data from rx buffer to client
#include "dplog.h"        // dataport logging
#include "OstTraceDefinitions.h"
#ifdef OST_TRACE_COMPILER_IN_USE
#include "dpdte2txTraces.h"
#endif

// LOCAL FUNCTION PROTOTYPES
// none

// ==================== LOCAL FUNCTIONS ====================

// ================= MEMBER FUNCTIONS =======================

// ---------------------------------------------------------
// CDpDte2Tx::CDpDte2Tx
// C++ default constructor.
// ---------------------------------------------------------
CDpDte2Tx::CDpDte2Tx(
    CDpDataPort& aDataPort ) :
    CActive( KDpDte2TxPriority ),
    iDataPort( aDataPort ),
    iBufferTx( iDataPort.Tx() ),
    iFlowCtrl( iDataPort.FlowCtrl() ),
    iEscDetect( iDataPort.EscDetect() ),
    iDataConfig( iDataPort.DataConfig() ),
    iRx2Dte( iDataPort.Rx2Dte() ),
    iTx( 0, 0 ),
    iRequestActive( EFalse ),
    iRole( MDpDataClient::EDbDataClientWriter )
    {
    OstTrace0( TRACE_NORMAL, CDPDTE2TX_CDPDTE2TX, "CDpDte2Tx::CDpDte2Tx" );
    LOGM(" CDpDte2Tx::CDpDte2Tx");
    }

// ---------------------------------------------------------
// CDpDte2Tx::ConstructL
// Symbian 2nd phase constructor.
// ---------------------------------------------------------
void CDpDte2Tx::ConstructL()
    {
    OstTrace0( TRACE_NORMAL, CDPDTE2TX_CONSTRUCTL, "CDpDte2Tx::ConstructL" );
    LOGM(" CDpDte2Tx::ConstructL");

    //  initialise inner observer object
    iDpDte2TxFcObserver.Init(this);

    // register to FlowCtrl as observer
    iFlowCtrl.Attach( iDpDte2TxFcObserver );

    // If registering fails => can't do nothing here => must leave
    User::LeaveIfError( iBufferTx.RegisterDataClient( *this ) );

    CActiveScheduler::Add( this );
    }

// ---------------------------------------------------------
// CDpDte2Tx::NewL
// Static constructor.
// ---------------------------------------------------------
CDpDte2Tx* CDpDte2Tx::NewL(
    CDpDataPort& aDataPort )
    {
    OstTrace0( TRACE_NORMAL, CDPDTE2TX_NEWL, "CDpDte2Tx::NewL" );
    LOGM(" CDpDte2Tx::NewL");

    CDpDte2Tx* self = new ( ELeave ) CDpDte2Tx( aDataPort );

    CleanupStack::PushL( reinterpret_cast< CActive* > ( self ) );
    self->ConstructL();
    CleanupStack::Pop( self );

    return self;
    }

// ---------------------------------------------------------
// CDpDte2Tx::~CDpDte2Tx
// Destructor
// ---------------------------------------------------------
CDpDte2Tx::~CDpDte2Tx()
    {
    OstTrace0( TRACE_NORMAL, DUP1_CDPDTE2TX_CDPDTE2TX, "CDpDte2Tx::~CDpDte2Tx" );
    LOGM(" CDpDte2Tx::~CDpDte2Tx");

    iPendingClientBuffer = 0;

    // unregister from FlowCtrl as observer
    iFlowCtrl.Detach( iDpDte2TxFcObserver );
    }

// ---------------------------------------------------------
// CDpDte2Tx::WriteTx
// This method puts write pending and signals itself
// (Dte2Tx).
// ---------------------------------------------------------
//
TInt CDpDte2Tx::WriteTx(
    const TAny* aClientBuffer,
    const TInt aLength)
    {
    OstTrace0( TRACE_NORMAL, CDPDTE2TX_WRITETX, "CDpDte2Tx::WriteTx" );
    OstTrace1( TRACE_NORMAL, DUP1_CDPDTE2TX_WRITETX, "CDpDte2Tx:: Port: %u", iDataPort.PortUnit() );

    LOGM1("CDpDte2Tx::WriteTx - Port %d", iDataPort.PortUnit() );

    TInt ret( KErrNone );

    if ( iWritePending )
        {
        //double write
        ret = KErrWrite;
        }
    else if ( 0 > aLength  )
        {
        //underflow
        ret = KErrArgument;
        }
    else if ( 0 != aLength )
        {
        // store pointer of client buffer
        iPendingClientBuffer = ( TAny* ) aClientBuffer;
        iPendingLength = aLength;

        iWritePending = ETrue;

        if ( !IsActive() )
            {
            iRequestActive = ETrue;
            iStatus = KRequestPending;
            SetActive();
            }
        //no else

        iDataPort.SignalDte2Tx();
        ret = KErrNotReady;
        }

    return ret;
    }

// ---------------------------------------------------------
// CDpDte2Tx::RunL
// This method handles write operation after signalling.
// ---------------------------------------------------------
//
void CDpDte2Tx::RunL()
    {
    OstTrace0( TRACE_NORMAL, CDPDTE2TX_RUNL, "CDpDte2Tx::RunL" );
    OstTrace1( TRACE_NORMAL, DUP1_CDPDTE2TX_RUNL, "CDpDte2Tx::RunL %x", &iDataPort );
    OstTrace1( TRACE_NORMAL, DUP2_CDPDTE2TX_RUNL, "CDpDte2Tx:: Port: %u", iDataPort.PortUnit() );

    LOGM1("CDpDte2Tx::RunL %x", &iDataPort);
    LOGM1("CDpDte2Tx::RunL - Port %d", iDataPort.PortUnit() );

    if ( iWritePending && iStatus != KErrCancel )
        {
        // check linefail
        TCommRole role;
        iDataPort.GetRole( role );
        if ( iDataConfig.IsLineFail( role ) )
            {
            LOG(" ERROR, Dte2Tx::RunL, Dte write completed with LINE FAIL.");
            OstTrace0( TRACE_NORMAL, DUP3_CDPDTE2TX_RUNL, "ERROR, Dte2Tx::RunL, Dte write completed with LINE FAIL." );

            iDataPort.WriteCompleted( KErrCommsLineFail );
            iWritePending = EFalse;
            iPendingClientBuffer = 0;
            iPendingLength = 0;
            }
        else if ( iDataConfig.IsEchoDte() &&
            ( iPendingLength - iIPCReadOffset ) > iRx2Dte.EchoBytesFreeSpace() )
            {
            if ( !IsActive() )
                {
                iRequestActive = ETrue;
                iStatus = KRequestPending;
                SetActive();
                }
            //no else
            }
        else
            {
            // first take minimum of three elements
            TInt clientLength( iPendingLength - iIPCReadOffset );
            TInt freeBytes( iBufferTx.FreeBytes() );
            TInt len( 0 );

            if ( clientLength >= iBufferTx.MaxReservationSize() )
                {
                len = iBufferTx.MaxReservationSize();
                }
            else
                {
                len = clientLength;
                }

            if ( freeBytes > 0 )
                {
                if (len >= freeBytes)
                    {
                    len = freeBytes;
                    }
                //no else
                }
            //no else

            TPtr8 nullPtr( 0, 0 );
            CDpDataElement& elem = iBufferTx.WriteElement();

            TInt ret( elem.Reserve( len, iTx, nullPtr ) );

            if ( ret != KErrNone)
                {
                if ( ret == KErrNotReady )
                    {
                    if ( !IsActive() )
                        {
                        iRequestActive = ETrue;
                        iStatus = KRequestPending;
                        SetActive();
                        }
                    //no else
                    }
                else
                    {
                    LOG1(" ErrorDte2Tx Tx element reservation failure! Id: %d",
                        ret );
                    OstTrace1( TRACE_NORMAL, DUP4_CDPDTE2TX_RUNL, "CDpDte2Tx:: ErrorDte2Tx Tx element reservation failure! Id: %d", ret );

                    User::Leave( ret );
                    }
                }
            else
                {
                // we suppose it's always either KErrNone or KErrNotReady ...
                User::LeaveIfError( iDataPort.IPCRead(
                    iPendingClientBuffer, iTx, iIPCReadOffset ) );

                if ( iDataConfig.IsEchoDte() && 0 < iTx.Length() )
                    {
                    // configured to echo tx bytes
                    iRx2Dte.EchoTx( iTx );
                    }
                //no else

                iEscDetect.Scan( iTx );

                // WasReaderWaiting is used to check if the reader was waiting
                // for more data until it is being able to read. Otherwise it
                // should signal to reader (Tx2Pn) that it can then read
                TBool WasReaderWaiting = iBufferTx.IsReaderWaiting();

                User::LeaveIfError( elem.Release( iTx.Length() ) );

                iIPCReadOffset += iTx.Length();

                // If reader was waiting for more data and got the enough,
                // DPElement from Release will notify Reader using ReleaseNotify().
                // WasReaderWaiting checks if Reader was waiting, if it is not,
                // it should signal the reader to read the buffer.
                if ( !WasReaderWaiting )
                    {
                    iDataPort.SignalTx2Pn();
                    }
                //no else

                if ( iIPCReadOffset >= iPendingLength )
                    {
                    LOG(" Dte2Tx::RunL, Dte write completed.");
                    OstTrace0( TRACE_NORMAL, DUP5_CDPDTE2TX_RUNL, "CDpDte2Tx:: Dte write completed" );

                    iIPCReadOffset = 0;
                    iDataPort.WriteCompleted( KErrNone );
                    iWritePending = EFalse;
                    iPendingClientBuffer = 0;
                    iPendingLength = 0;
                    }
                else
                    {
                    if ( !IsActive() )
                        {
                        iRequestActive = ETrue;
                        iStatus = KRequestPending;
                        SetActive();
                        }
                    //no else
                    iDataPort.SignalDte2Tx();
                    }
                }
            }
        }
        //no else
    }

// ---------------------------------------------------------
// CDpDte2Tx::RunError
// Leave in RunL() is handled here. Error code is returned,
// when internal error has occured.
// ---------------------------------------------------------
//
TInt CDpDte2Tx::RunError(
    TInt aError )
    {
    OstTrace0( TRACE_NORMAL, CDPDTE2TX_RUNERROR, "CDpDte2Tx::RunError" );
    OstTraceExt2( TRACE_NORMAL, DUP1_CDPDTE2TX_RUNERROR, "CDpDte2Tx:: Port: %u, error code: %d", iDataPort.PortUnit(), aError );

    LOGM2("CDpDte2Tx::RunError - Port %d, error code: %d",
        iDataPort.PortUnit(), aError );

    return aError;
    }

// ---------------------------------------------------------
// CDpDte2Tx::UpDate
// We are observer of FlowCtrl. E.g. when flow control
// status changes, we are called.
// ---------------------------------------------------------
//
void CDpDte2Tx::UpDate(
    CDpSubject* /*aChangedSubject*/)
    {
    OstTrace0( TRACE_NORMAL, CDPDTE2TX_UPDATE, "CDpDte2Tx::UpDate" );
    LOGM(" CDpDte2Tx::UpDate");

    ReleaseNotify();
    }

// ---------------------------------------------------------
// CDpDte2Tx::DoCancel
// This method cancels this active object by
// RequestComplete(). This method must be called directly
// (not through Cancel()), when RunL() should be completed
// with KErrCancel.
// ---------------------------------------------------------
//
void CDpDte2Tx::DoCancel()
    {
    OstTrace0( TRACE_NORMAL, CDPDTE2TX_DOCANCEL, "CDpDte2Tx::DoCancel" );
    LOGM(" CDpDte2Tx::DoCancel");

    if ( iRequestActive )
        {
        iRequestActive = EFalse;
        // do the required signaling
        TRequestStatus* s=&iStatus;
        User::RequestComplete( s, KErrCancel );
        }
    //no else
    }

// ---------------------------------------------------------
// CDpDte2Tx::ReleaseNotify
// This method signals ourselves (Dte2Tx).
// ---------------------------------------------------------
//
void CDpDte2Tx::ReleaseNotify()
    {
    OstTrace0( TRACE_NORMAL, CDPDTE2TX_RELEASENOTIFY, "CDpDte2Tx::ReleaseNotify" );
    LOGM(" CDpDte2Tx::ReleaseNotify");

    // signal ourselves
    if ( iRequestActive )
        {
        iRequestActive = EFalse;
        TRequestStatus* status = &iStatus;

        User::RequestComplete( status, KErrNone );
        }
    //no else
    }

// ---------------------------------------------------------
// This method notifies that buffer is flushed. Classes which have access
// to buffers are derived from this class. Derived class could override
// this method, otherwise this default method will be used.
// release this cactive object
// ---------------------------------------------------------
//
void CDpDte2Tx::FlushNotify()
    {
    OstTrace0( TRACE_NORMAL, CDPDTE2TX_FLUSHNOTIFY, "CDpDte2Tx::FlushNotify" );
    LOGM(" CDpDte2Tx::FlushNotify()");

    ReleaseNotify();
    }

// ---------------------------------------------------------
// CDpDte2Tx::Role
// This method gets the role (reader/writer) of data client.
// ---------------------------------------------------------
//
TUint8 CDpDte2Tx::Role()
    {
    OstTrace0( TRACE_NORMAL, CDPDTE2TX_ROLE, "CDpDte2Tx::Role" );
    return iRole;
    }

// ---------------------------------------------------------
// CDpDte2Tx::CancelWrite
// This method is called by client via
// CDpDataPort::WriteCancel(). Client want to cancel pending
// write operation. C32 makes WriteCompleted to DTE -->
// don't make it here at all.
// Status : Proposal
// ---------------------------------------------------------
//
void CDpDte2Tx::CancelWrite()
    {
    OstTrace0( TRACE_NORMAL, CDPDTE2TX_CANCELWRITE, "CDpDte2Tx::CancelWrite" );
    LOGM(" CDpDte2Tx::CancelWrite");

    if ( iWritePending )
        {
        iPendingClientBuffer = 0;
        iPendingLength = 0;
        iWritePending = EFalse;
        iIPCReadOffset = 0;
        iDataPort.SignalDte2Tx();
        }
    //no else
    }

// ---------------------------------------------------------
// CDpDte2Tx::CDpDte2TxObserver::Init
// This method initializes Dte2Tx observer.
// ---------------------------------------------------------
//
void CDpDte2Tx::CDpDte2TxObserver::Init(
    CDpDte2Tx* aDpDte2Tx )
    {
    iDpDte2Tx = aDpDte2Tx;
    }

// ---------------------------------------------------------
// CDpDte2Tx::CDpDte2TxObserver::UpDate
// This method updates Dte2Tx.
// ---------------------------------------------------------
//
void CDpDte2Tx::CDpDte2TxObserver::UpDate(
    CDpSubject* aChangedSubject )
    {
    LOG(" CDpDte2Tx::CDpDte2TxObserver::UpDate");

    if ( iDpDte2Tx )
        {
        iDpDte2Tx->UpDate( aChangedSubject );
        }
    //no else
    }

// ================= OTHER EXPORTED FUNCTIONS ==============
// none

//  End of File
