/*
* 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 "dppn2rx.h"         // moves data from isc api to rx buffer
#include "dppif.h"           // pipe interface (pif)
#include "dpflowctrl.h"      // flow control handling
#include "dpdataconfig.h"    // configuration store
#include "dpparityhandler.h" // parity bit handling in 7-bit data mode
#include "dplog.h"           // dataport logging
#include "OstTraceDefinitions.h"
#ifdef OST_TRACE_COMPILER_IN_USE
#include "dppn2rxTraces.h"
#endif


// EXTERNAL DATA STRUCTURES
// none

// EXTERNAL FUNCTION PROTOTYPES
// none

// CONSTANTS
// none

// MACROS
// none

// LOCAL CONSTANTS AND MACROS
// none

// MODULE DATA STRUCTURES
// none

// LOCAL FUNCTION PROTOTYPES
// none

// FORWARD DECLARATIONS
// none

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

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

// -----------------------------------------------------------------------------
// CDpPn2Rx::CDpPn2Rx
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CDpPn2Rx::CDpPn2Rx(
    CDpDataPort& aDataPort ) :
    CActive( KDpPn2RxPriority ),
    iDataPort( aDataPort ),
    iBufferRx( iDataPort.Rx() ),
    iPifDcs( iDataPort.Pif() ),
    iDataConfig( iDataPort.DataConfig() ),
    iFlowCtrl( iDataPort.FlowCtrl() ),
    iParityHandler( iDataPort.ParityHandler() ),
    iRx( 0, 0 ),
    iState( EElementWait ),
    iInBufHasDataNotify( EFalse ),
    iRequestActive( EFalse ),
    iRole( MDpDataClient::EDbDataClientWriter )
    {
    OstTrace0( TRACE_NORMAL, CDPPN2RX_CDPPN2RX, "CDpPn2Rx::CDpPn2Rx" );
    LOGM(" CDpPn2Rx::CDpPn2Rx");
    }

// -----------------------------------------------------------------------------
// CDpPn2Rx::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CDpPn2Rx::ConstructL()
    {
    OstTrace0( TRACE_NORMAL, CDPPN2RX_CONSTRUCTL, "CDpPn2Rx::ConstructL" );
    LOGM("CDpPn2Rx::ConstructL");

    //  initialise inner observer object
    iDpPn2RxObserver.Init( this );
    // register to PIF as observer
    iPifDcs.Attach( iDpPn2RxObserver );
    // If registering fails => can't do nothing here => must leave
    User::LeaveIfError( iBufferRx.RegisterDataClient( *this ) );

    CActiveScheduler::Add( this );
    }

// -----------------------------------------------------------------------------
// CDpPn2Rx::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CDpPn2Rx* CDpPn2Rx::NewL(
    CDpDataPort& aDataPort )
    {
    OstTrace0( TRACE_NORMAL, CDPPN2RX_NEWL, "CDpPn2Rx::NewL" );
    LOGM("CDpPn2Rx::NewL");

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

    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );

    return self;
    }

// -----------------------------------------------------------------------------
// CDpPn2Rx::~CDpPn2Rx
// Destructor
// -----------------------------------------------------------------------------
//
CDpPn2Rx::~CDpPn2Rx()
    {
    OstTrace0( TRACE_NORMAL, DUP1_CDPPN2RX_CDPPN2RX, "CDpPn2Rx::~CDpPn2Rx" );
    LOGM("CDpPn2Rx::~CDpPn2Rx");
    // unregister from PIF as observer
    iPifDcs.Detach( iDpPn2RxObserver );
    }

// -----------------------------------------------------------------------------
// CDpPn2Rx::RunL
// This active object is signalled by Isc Api.
// We will renew read to Isc Api.
// -----------------------------------------------------------------------------
//
void CDpPn2Rx::RunL()
    {
    OstTrace0( TRACE_NORMAL, CDPPN2RX_RUNL, "CDpPn2Rx::RunL" );
    OstTraceExt2( TRACE_NORMAL, DUP1_CDPPN2RX_RUNL, "CDpPn2Rx:: Port: %u, Status: %d", iDataPort.PortUnit(), iStatus.Int() );

    LOGM2("CDpPn2Rx::RunL - Port %d, Status: %d ",
        iDataPort.PortUnit(), iStatus.Int() );

    iRequestActive = EFalse;
    User::LeaveIfError( Read() );

    // don't set iStatus nor set_active here as it is better done in functions
    }

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

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

    return aError;
    }

// -----------------------------------------------------------------------------
// CDpPn2Rx::DoCancel
// Cancels this active object and cancels pipe receive.
// -----------------------------------------------------------------------------
//
void CDpPn2Rx::DoCancel()
    {
    OstTrace0( TRACE_NORMAL, CDPPN2RX_DOCANCEL, "CDpPn2Rx::DoCancel" );
    LOGM("CDpPn2Rx::DoCancel");

    switch ( iState )
        {
        case EDpReadWait:
            {
            if ( iRequestActive )
                {
                // cancel pipe
                iDataPort.ISAHandle().DataReceiveCancel();

                LOG("  CDpPn2Rx::DoCancel, Rx element release");
                OstTrace0( TRACE_NORMAL, DUP1_CDPPN2RX_DOCANCEL, "CDpPn2Rx:: Rx element release" );

                TInt ret = iBufferRx.WriteElement().Release( iRx.Length() );

                if ( KErrNone == ret )
                    {
#ifdef USE_FILE_SERIAL_TRUST_DEBUG
                    iDataPort.DebugBytesFromPn( iRx.Length() );
#endif
                    if ( 0 < iRx.Length() )
                        {
                        // 7-bit data extract parity bits
                        if ( EData7 == iDataConfig.DataBits() )
                            {
                            iParityHandler.ExtractParityBits( iRx );
                            }
                        //no else

                        if ( iDataPort.IsRx2DteReadPending() )
                            {
                            // Signal RX2DTE
                            iDataPort.SignalRx2Dte();
                            }
                        //no else

                        // New data is available. is client interested about that
                        if ( iInBufHasDataNotify )
                            {
                            // We should notify client
                            iInBufHasDataNotify = EFalse;
                            iDataPort.NotifyDataAvailableCompleted( KErrNone );
                            }
                        //no else
                        }
                    //no else
                    }
                else
                    {
                    // Element release couldn't be done. Can't leave here.
                    // Element remains reserved and something has gone totally
                    // wrong before release.
                    LOG("  ERROR, CDpPn2Rx::DoCancel, Element release failed");
                    OstTrace0( TRACE_NORMAL, DUP2_CDPPN2RX_DOCANCEL, "CDpPn2Rx:: Element release failed" );
                    }
                //no else

                // reset the state
                iState = EDefault;
                iRequestActive = EFalse;
                }
            //no else
            break;
            }
        case EElementWait:
            {
            // Do not complete request if pipe is closed
            if ( CDpPif::EDpPipeDisabled != iPifDcs.PipeState() &&
                CDpPif::EDpPipeDisconnected != iPifDcs.PipeState() )
                {
                // do the required signaling
                if ( iStatus == KRequestPending )
                    {
                    if ( iRequestActive )
                        {
                        // do the required signaling
                        iRequestActive = EFalse;
                        iState = EDefault;
                        TRequestStatus* s = &iStatus;
                        User::RequestComplete( s, KErrCancel );
                        }
                    //no else
                    }
                //no else
                }
            //no else
            break;
            }
        case EDefault:
            {
            break;
            }
        default:
            {
            LOG("  ERROR, CDpPn2Rx::DoCancel, Unknown iState");
            OstTrace0( TRACE_NORMAL, DUP3_CDPPN2RX_DOCANCEL, "ERROR, CDpPn2Rx::DoCancel, Unknown iState" );
            break;
            }
        }
    }

// -----------------------------------------------------------------------------
// CDpPn2Rx::Read
// Reserves space from rx buffer and makes new PipeReceive
// to Isc Api. Checks if flow control high water mark is reached.
// Extracts paritybits if 7-bit data.Signals Rx2Dte.
// -----------------------------------------------------------------------------
//
TInt CDpPn2Rx::Read()
    {
    OstTrace0( TRACE_NORMAL, CDPPN2RX_READ, "CDpPn2Rx::Read" );
    OstTrace1( TRACE_NORMAL, DUP1_CDPPN2RX_READ, "CDpPn2Rx::Read %x", &iDataPort );
    OstTraceExt2( TRACE_NORMAL, DUP2_CDPPN2RX_READ, "CDpPn2Rx:: (Read from ISC) - Port: %u, iRx.Length: %d", iDataPort.PortUnit(), iRx.Length() );

    LOGM1("CDpPn2Rx::Read %x", &iDataPort);
    LOGM2("CDpPn2Rx::Read (Read from ISC) - Port %d, iRx.Length: %d",
     iDataPort.PortUnit(), iRx.Length() );

    TInt ret( KErrNone );

    // We've been listening pn_pipe
    if ( EDpReadWait == iState )
        {
        LOGHEX(iRx, EFalse);
        OstTraceExt1( TRACE_NORMAL, DUP10_CDPPN2RX_READ, "CDpPn2Rx:: RX = %s", iRx );

        ret = iBufferRx.WriteElement().Release( iRx.Length() );

        if ( KErrNone == ret )
            {
#ifdef USE_FILE_SERIAL_TRUST_DEBUG
            iDataPort.DebugBytesFromPn( iRx.Length() );
#endif
            if ( 0 < iRx.Length() )
                {
                // 7-bit data extract parity bits
                if ( EData7 == iDataConfig.DataBits() )
                    {
                    iParityHandler.ExtractParityBits( iRx );
                    }
                //no else

                if ( iDataPort.IsRx2DteReadPending() )
                    {
                    // Signal RX2DTE
                    iDataPort.SignalRx2Dte();
                    }
                //no else

                // New data is available. is client interested about that
                if ( iInBufHasDataNotify )
                    {
                    // We should notify client
                    iInBufHasDataNotify = EFalse;
                    iDataPort.NotifyDataAvailableCompleted( KErrNone );
                    }
                //no else
                }
            //no else
            }
        //no else
        }
    //no else

    // Check pipe state
    if ( KErrNone == ret && CDpPif::EDpPipeEnabled != iPifDcs.PipeState() )
        {
        ret = KErrNotReady;
        }
    else if ( KErrNone == ret )
        {
        // Try reserve element for writing
        TPtr8 nullPtr( 0, 0 );

        // Check if reserved buffer was too small
        // => override MaxReservationSize for next read request.
        if ( KErrNoMemory == iStatus.Int() &&
            iLen > iBufferRx.MaxReservationSize() )
            {
            OstTrace0( TRACE_NORMAL, DUP4_CDPPN2RX_READ, "CDpPn2Rx::KErrNoMemory from ISC =>" );
            OstTraceExt1( TRACE_NORMAL, DUP5_CDPPN2RX_READ, "CDpPn2Rx:: -- Try to read more than MaxReservationSize, %hu --", iLen );

            LOG("  CDpPn2Rx::Read, KErrNoMemory from ISC =>");
            LOG1("  -- Try to read more than MaxReservationSize, %d --", iLen );

            ret = iBufferRx.WriteElement().Reserve(
                iLen, iRx, nullPtr, ETrue );
            // Set iLen to zero (required buffer lenght for read,
            // received from ISC if buffer was too small)
            iLen = 0;
            }
        else
            {
            ret = iBufferRx.WriteElement().Reserve(
                iBufferRx.MaxReservationSize(), iRx, nullPtr );
            }

        // Is flowctrl off and rx "high watermark" reached
        if ( EFlowControlOff == iFlowCtrl.FlowCtrlDcs2Dp() )
            {
            if ( IsHighWaterMarkReached() )
                {
                // Buffer is almost full set flowcontrol on
                LOG("  CDpPn2Rx::Read, Rx High WaterMarkReached");
                OstTrace0( TRACE_NORMAL, DUP6_CDPPN2RX_READ, "CDpPn2Rx:: Rx High WaterMark reached" );

                iFlowCtrl.WaterMarkHighReached();
                }
            //no else
            }
        //no else

        switch ( ret )
            {
            // Reservation successed
            case KErrNone:
                {
                LOG1("  CDpPn2Rx::Read, ISAHandle Pipe Receive. iRx.MaxLength: %d",
                    iRx.MaxLength() );
                OstTrace1( TRACE_NORMAL, DUP7_CDPPN2RX_READ, "CDpPn2Rx:: ISAHandle Pipe Receive. iRx.MaxLength: %d", iRx.MaxLength() );

                // Set pipe receive
                if ( !IsActive() )
                    {
                    iDataPort.ISAHandle().DataReceive( iStatus, iRx, iLen );

                    iRequestActive = ETrue;
                    // Change state
                    iState = EDpReadWait;

                    SetActive();
                    }
                //no else
                break;
                }
            // Reservation failed
            case KErrNotReady:
                {
                LOG("  ERROR, CDpPn2Rx::Read, Element reservation failed (KErrNotReady).");
                OstTrace0( TRACE_NORMAL, DUP8_CDPPN2RX_READ, "ERROR, CDpPn2Rx::Read, Element reservation failed (KErrNotReady)" );

                // Change state
                iState = EElementWait;
                // Better done here
                if ( !IsActive() )
                    {
                    iStatus = KRequestPending;
                    SetActive();
                    }
                //no else
                iRequestActive = ETrue;

                ret = KErrNone;

                break;
                }
            default:
                {
                // An error value returned
                LOG1("  ERROR, CDpPn2Rx::Read, Fail to reserve Element, error code: %d",
                    ret );
                OstTrace1( TRACE_NORMAL, DUP9_CDPPN2RX_READ, "CDpPn2Rx:: ERROR, CDpPn2Rx::Read, Fail to reserve Element, error code: %d", ret );
                }
            }
        }
    //no else

    return ret;
    }

// -----------------------------------------------------------------------------
// CDpPn2Rx::UpDate
// Pipe state has been changed.
// -----------------------------------------------------------------------------
//
void CDpPn2Rx::UpDate(
    CDpSubject* /*aChangedSubject*/ )
    {
    OstTrace0( TRACE_NORMAL, CDPPN2RX_UPDATE, "CDpPn2Rx::UpDate" );
    LOGM("CDpPn2Rx::UpDate");

    TInt r( KErrNone );
    // DCS PIF state changed
    switch ( iPifDcs.PipeState() )
        {
        case CDpPif::EDpPipeEnabled:
            {
            // start read but beware double read
            if ( iState == EElementWait )
                {
                r = Read();
                if ( r != KErrNone )
                    {
                    LOG("  ERROR, CDpPn2Rx::UpDate, Read() failed");
                    OstTrace0( TRACE_NORMAL, DUP1_CDPPN2RX_UPDATE, "ERROR, CDpPn2Rx::UpDate, Read() failed" );
                    }
                //no else
                }
            //no else
            break;
            }
        case CDpPif::EDpPipeDisabled: // Fall through
        case CDpPif::EDpPipeDisconnected:
            {
            if ( IsActive() )
                {
                Cancel();
                }
            //no else
            break;
            }
        default:
            {
            // Pipe is in state that is not regonized
            LOG1("  ERROR, CDpPn2Rx::UpDate, Unrecognized pipe state: %d",
                iPifDcs.PipeState() );
            OstTraceExt1( TRACE_NORMAL, DUP2_CDPPN2RX_UPDATE, "ERROR, CDpPn2Rx::UpDate, Unrecognized pipe state: %s", iPifDcs.PipeState() );

            Cancel();
            break;
            }
        }
    }

// -----------------------------------------------------------------------------
// CDpPn2Rx::ReleaseNotify
// This method signals ourselves.
// -----------------------------------------------------------------------------
//
void CDpPn2Rx::ReleaseNotify()
    {
    OstTrace0( TRACE_NORMAL, CDPPN2RX_RELEASENOTIFY, "CDpPn2Rx::ReleaseNotify" );
    LOGM("CDpPn2Rx::ReleaseNotify");

    // This is done because receive data might me also active
    DoCancel();
    }

// -----------------------------------------------------------------------------
// CDpPn2Rx::FlushNotify
// 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.
// -----------------------------------------------------------------------------
//
void CDpPn2Rx::FlushNotify()
    {
    OstTrace0( TRACE_NORMAL, CDPPN2RX_FLUSHNOTIFY, "CDpPn2Rx::FlushNotify" );
    LOGM("CDpPn2Rx::FlushNotify()");

    ReleaseNotify();
    }

// -----------------------------------------------------------------------------
// CDpPn2Rx::Role
// This method gets the role (reader/writer) of data client.
// -----------------------------------------------------------------------------
//
TUint8 CDpPn2Rx::Role()
    {
    OstTrace0( TRACE_NORMAL, CDPPN2RX_ROLE, "CDpPn2Rx::Role" );

    return iRole;
    }

// -----------------------------------------------------------------------------
// CDpPn2Rx::SetDataAvailableNotification
// Set flag that indicates are requested to notify the C32
// client, if there is data available in rx buffer.
// -----------------------------------------------------------------------------
//
TInt CDpPn2Rx::SetDataAvailableNotification(
    const TBool aUsedOrNot )
    {
    OstTrace0( TRACE_NORMAL, CDPPN2RX_SETDATAAVAILABLENOTIFICATION, "CDpPn2Rx::SetDataAvailableNotification" );
    LOGM(" CDpPn2Rx::SetDataAvailableNotification");

    iInBufHasDataNotify = aUsedOrNot;
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CDpPn2Rx::IsHighWaterMarkReached
// Is high water mark reached
// -----------------------------------------------------------------------------
//
TBool CDpPn2Rx::IsHighWaterMarkReached()
    {
    OstTrace0( TRACE_NORMAL, CDPPN2RX_ISHIGHWATERMARKREACHED, "CDpPn2Rx::IsHighWaterMarkReached" );
    TBool waterMarkReached( EFalse );

    if ( iBufferRx.UsedBytes() >= iDataConfig.WaterMarkHighSize( iBufferRx ) )
        {
        waterMarkReached = ETrue;
        }
    else
        {
        waterMarkReached = EFalse;
        }

    return waterMarkReached;
    }

//==============================================================================

// -----------------------------------------------------------------------------
// CDpPn2Rx::CDpPn2RxObserver::Init
// This method initializes Pn2Rx observer.
// -----------------------------------------------------------------------------
//
void CDpPn2Rx::CDpPn2RxObserver::Init(
    CDpPn2Rx* aDpPn2Rx )
    {
    iDpPn2Rx = aDpPn2Rx;
    }

// -----------------------------------------------------------------------------
// CDpPn2Rx::CDpPn2RxObserver::UpDate
// This method updates Pn2Rx.
// -----------------------------------------------------------------------------
//
void CDpPn2Rx::CDpPn2RxObserver::UpDate(
    CDpSubject* aChangedSubject )
    {
    LOGM("CDpPn2Rx::CDpPn2RxObserver::UpDate");

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

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

//  End of File
