localconnectivityservice/dun/plugins/src/bt/DunBtPlugin.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 31 Aug 2010 16:03:15 +0300
branchRCL_3
changeset 19 0aa8cc770c8a
permissions -rw-r--r--
Revision: 201032 Kit: 201035

/*
* Copyright (c) 2006-2008 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:  DUN Bluetooth plugin
*
*/


#include <bt_sock.h>
#include <c32comm.h>
#include "DunPlugin.h"
#include "DunBtListen.h"
#include "DunBtPlugin.h"
#include "DunDebug.h"
#include "DunTransporter.h"

_LIT( KBtChannelName, "DUNBT::"  );

const TInt KCharactersInTInt = 10;  // For "2147483648"

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

// ---------------------------------------------------------------------------
// CDunBtPlugin::CDunBtPlugin()
// ---------------------------------------------------------------------------
//
CDunBtPlugin::CDunBtPlugin() :
    iServer( NULL ),
    iBTListen( NULL ),
    iTransporter( NULL )
    {
    }

// ---------------------------------------------------------------------------
// Destructor.
// ---------------------------------------------------------------------------
//
CDunBtPlugin::~CDunBtPlugin()
    {
    FTRACE(FPrint( _L( "CDunBtPlugin::~CDunBtPlugin()" ) ));
    Uninitialize();
    FTRACE(FPrint( _L( "CDunBtPlugin::~CDunBtPlugin() complete" ) ));
    }

// ---------------------------------------------------------------------------
// State of this plugin
// ---------------------------------------------------------------------------
//
TDunPluginState CDunBtPlugin::PluginState()
    {
    return iServer->GetPluginStateByUid( KDunBtPluginUid );
    }

// ---------------------------------------------------------------------------
// Constructs a listener object for this plugin
// ---------------------------------------------------------------------------
//
void CDunBtPlugin::ConstructListenerL()
    {
    FTRACE(FPrint(_L("CDunBtPlugin::ConstructListenerL()")));
    if ( PluginState() != EDunStateLoaded )
        {
        FTRACE(FPrint(_L("CDunBtPlugin::ConstructListenerL() (not ready) complete")));
        User::Leave( KErrNotReady );
        }
    ReportStateChangeUp( EDunStateTryListen );
    if ( iBTListen )
        {
        FTRACE(FPrint(_L("CDunBtPlugin::ConstructListenerL() (already exists) complete")));
        User::Leave( KErrAlreadyExists );
        }
    CDunBtListen* listen = CDunBtListen::NewL( iServer,
                                               this,
                                               iTransporter,
                                               iEntity );
    CleanupStack::PushL( listen );
    listen->IssueRequestL();
    CleanupStack::Pop( listen );
    iBTListen = listen;
    ReportStateChangeUp( EDunStateListening );
    FTRACE(FPrint(_L("CDunBtPlugin::ConstructListenerL() complete")));
    }

// ---------------------------------------------------------------------------
// Sets new state
// New state must be one more than the old state
// ---------------------------------------------------------------------------
//
TInt CDunBtPlugin::ReportStateChangeUp( TDunPluginState aPluginState )
    {
    FTRACE(FPrint(_L("CDunBtPlugin::ReportStateChangeUp()")));
    TInt retTemp = iServer->NotifyPluginStateChangeUp( aPluginState,
                                                       KDunBtPluginUid );
    if ( retTemp != KErrNone )
        {
        FTRACE(FPrint(_L("CDunBtPlugin::ReportStateChangeUp() (ERROR) complete")));
        return retTemp;
        }
    FTRACE(FPrint(_L("CDunBtPlugin::ReportStateChangeUp() complete")));
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Sets new state
// New state must be one less than the old state
// ---------------------------------------------------------------------------
//
TInt CDunBtPlugin::ReportStateChangeDown( TDunPluginState aPluginState )
    {
    FTRACE(FPrint(_L("CDunBtPlugin::ReportStateChangeDown()")));
    TInt retTemp = iServer->NotifyPluginStateChangeDown( aPluginState,
                                                         KDunBtPluginUid );
    if ( retTemp != KErrNone )
        {
        FTRACE(FPrint(_L("CDunBtPlugin::ReportStateChangeDown() (ERROR) complete")));
        return retTemp;
        }
    FTRACE(FPrint(_L("CDunBtPlugin::ReportStateChangeDown() complete")));
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Allocates a free channel
// ---------------------------------------------------------------------------
//
void CDunBtPlugin::AllocateChannelL( TBool& aNoFreeChans,
                                     TBtCleanupInfo& aCleanupInfo )
    {
    FTRACE(FPrint(_L("CDunBtPlugin::AllocateChannelL()")));
    // iDataSocket has new data socket information so copy it to iBTPorts
    TBtPortEntity* foundEntity = NULL;
    TInt foundIndex = GetFirstFreePort( foundEntity );
    if ( !foundEntity )  // free not found so add new
        {
        TBtPortEntity newEntity;
        iBTPorts.AppendL( newEntity );
        aCleanupInfo.iNewEntity = ETrue;
        aCleanupInfo.iEntityIndex = iBTPorts.Count() - 1;
        foundEntity = &iBTPorts[ aCleanupInfo.iEntityIndex ];
        }
    else  // free found so change array
        {
        aCleanupInfo.iNewEntity = EFalse;
        aCleanupInfo.iEntityIndex = foundIndex;
        foundEntity = &iBTPorts[ foundIndex ];
        }
    foundEntity->iChannelNum = iEntity.iChannelNum;
    foundEntity->iBTPort = iEntity.iBTPort;
    RSocket* socket = &foundEntity->iBTPort;
    HBufC8* channelName = HBufC8::NewMaxLC( KBtChannelName().Length() +
                          KCharactersInTInt );
    TPtr8 channelNamePtr = channelName->Des();
    channelNamePtr.Copy( KBtChannelName );
    channelNamePtr.AppendNum( iEntity.iChannelNum );
    iTransporter->AllocateChannelL( socket,
                                    KDunBtPluginUid,
                                    channelNamePtr,
                                    EFalse,
                                    aNoFreeChans );
    iTransporter->AddConnMonCallbackL( socket,
                                       this,
                                       EDunReaderUpstream,
                                       EFalse );
    iTransporter->AddConnMonCallbackL( socket,
                                       this,
                                       EDunWriterUpstream,
                                       EFalse );
    iTransporter->AddConnMonCallbackL( socket,
                                       this,
                                       EDunReaderDownstream,
                                       ETrue );
    iTransporter->AddConnMonCallbackL( socket,
                                       this,
                                       EDunWriterDownstream,
                                       EFalse );
    iTransporter->IssueTransferRequestsL( socket );
    CleanupStack::PopAndDestroy( channelName );
    FTRACE(FPrint(_L("CDunBtPlugin::AllocateChannelL() complete")));
    }

// ---------------------------------------------------------------------------
// Frees an existing channel
// ---------------------------------------------------------------------------
//
TInt CDunBtPlugin::FreeChannels()
    {
    FTRACE(FPrint(_L("CDunBtPlugin::FreeChannels()")));
    if ( PluginState() != EDunStateTryUninitialize )
        {
        FTRACE(FPrint(_L("CDunBtPlugin::FreeChannels() (not ready) complete")));
        return KErrNotReady;
        }
    TInt i;
    TInt count = iBTPorts.Count();
    for ( i=0; i<count; i++ )
        {
        if ( iBTPorts[i].iBTPort.SubSessionHandle() )
            {
            iTransporter->FreeChannel( &iBTPorts[i].iBTPort );
            iBTPorts[i].iBTPort.Close();
            // All channels freed and this is for Uninitialize() so don't touch
            // advertisement monitor here!
            }
        iBTPorts[i].iChannelNum = KErrNotFound;
        }
    iBTPorts.Close();
    FTRACE(FPrint(_L("CDunBtPlugin::FreeChannels() complete")));
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Uninitializes this plugin
// ---------------------------------------------------------------------------
//
TInt CDunBtPlugin::Uninitialize()
    {
    FTRACE(FPrint( _L( "CDunBtPlugin::Uninitialize()" ) ));
    ReportStateChangeDown( EDunStateTryUninitialize );
    // Free channels (ignore errors)
    FreeChannels();
    // Delete listening object (also advertisement monitor)
    delete iBTListen;
    iBTListen = NULL;
    // Set state back to loaded
    ReportStateChangeUp( EDunStateUninitialized );
    ReportStateChangeUp( EDunStateTryLoad );
    ReportStateChangeUp( EDunStateLoaded );
    FTRACE(FPrint( _L( "CDunBtPlugin::Uninitialize() complete" ) ));
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Gets port's index and entity by connection ID
// ---------------------------------------------------------------------------
//
TInt CDunBtPlugin::GetPortByConnId( TConnId aConnId, TBtPortEntity*& aEntity )
    {
    FTRACE(FPrint( _L( "CDunBtPlugin::GetPortByConnId()")) );
    TInt i;
    TInt count = iBTPorts.Count();
    for ( i=0; i<count; i++ )
        {
        if ( &iBTPorts[i].iBTPort == aConnId )
            {
            aEntity = &iBTPorts[i];
            FTRACE(FPrint( _L( "CDunBtPlugin::GetPortByConnId() complete")) );
            return i;
            }
        }
    aEntity = NULL;
    FTRACE(FPrint( _L( "CDunBtPlugin::GetPortByConnId() (not found) complete")) );
    return KErrNotFound;
    }

// ---------------------------------------------------------------------------
// Gets first free port's index and entity
// ---------------------------------------------------------------------------
//
TInt CDunBtPlugin::GetFirstFreePort( TBtPortEntity*& aEntity )
    {
    FTRACE(FPrint( _L( "CDunBtPlugin::GetFirstFreePort()")) );
    TInt i;
    TInt count = iBTPorts.Count();
    for ( i=0; i<count; i++ )
        {
        if ( !iBTPorts[i].iBTPort.SubSessionHandle() )
            {
            aEntity = &iBTPorts[i];
            FTRACE(FPrint( _L( "CDunBtPlugin::GetFirstFreePort() complete")) );
            return i;
            }
        }
    aEntity = NULL;
    FTRACE(FPrint( _L( "CDunBtPlugin::GetFirstFreePort() (not found) complete")) );
    return KErrNotFound;
    }

// ---------------------------------------------------------------------------
// Sets modem's MSC (Modem Status Command)
// ---------------------------------------------------------------------------
//
TInt CDunBtPlugin::SetRFCOMMStatusCommand( TBtPortEntity& aEntity,
                                           TUint8 aSignal,
                                           TBool aSignalOn )
    {
    FTRACE(FPrint( _L( "CDunBtPlugin::SetRFCOMMStatusCommand()" ) ));
    // Get existing Modem Status Command (MSC)
    // Ref.: 3GPP TS 07.10 V7.2.0 (2002-03)
    // Table 6,7, (5.4.6.3.7)
    TUint8 modemStatus = 0;
    TPckgBuf<TUint8> pkg( modemStatus );
    TInt retTemp = aEntity.iBTPort.GetOpt( KRFCOMMLocalModemStatus,
                                           KSolBtRFCOMM,
                                           pkg );
    if ( retTemp != KErrNone )
        {
        FTRACE(FPrint( _L( "CDunBtPlugin::SetRFCOMMStatusCommand() (GetOpt failed!) complete" ) ));
        return retTemp;
        }
    modemStatus = pkg();
    FTRACE(FPrint( _L( "CDunBtPlugin::SetRFCOMMStatusCommand() signals are: 0x%02X" ), modemStatus));
    TBool changed = EFalse;
    TUint8 signal = modemStatus & aSignal;
    if ( aSignalOn )
        {
        if ( !signal )
            {
            modemStatus |= aSignal;
            changed = ETrue;
            }
        }
    else
        {
        if ( signal )
            {
            modemStatus &= ( ~aSignal );
            changed = ETrue;
            }
        }
    if ( changed )
        {
        pkg = modemStatus;
        retTemp = aEntity.iBTPort.SetOpt( KRFCOMMLocalModemStatus,
                                          KSolBtRFCOMM,
                                          pkg );
        if ( retTemp != KErrNone )
            {
            FTRACE(FPrint( _L( "CDunBtPlugin::SetRFCOMMStatusCommand() (SetOpt failed!) complete" ) ));
            return retTemp;
            }
        }
    FTRACE(FPrint( _L( "CDunBtPlugin::SetRFCOMMStatusCommand() complete" ) ));
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Manages advertiser for channel free operation
// ---------------------------------------------------------------------------
//
void CDunBtPlugin::ManageAdvertiserFreeOperationL()
    {
    FTRACE(FPrint(_L("CDunBtPlugin::ManageAdvertiserFreeOperationL()")));
    TInt numOfChans = iTransporter->NumberOfAllocatedChannels();
    // Remove of last CDunTransporter channel removes also the
    // advertisement monitor so set it now if necessary
    if ( numOfChans == 0 )
        {
        iTransporter->SetAdvertisementMonitorL( KDunBtPluginUid, iBTListen );
        }
    FTRACE(FPrint(_L("CDunBtPlugin::ManageAdvertiserFreeOperationL() complete")));
    }

// ---------------------------------------------------------------------------
// Cleans partial created channel data based on TATExtCleanupInfo
// ---------------------------------------------------------------------------
//
void CDunBtPlugin::CleanPartialChanneldata( TBtCleanupInfo& aCleanupInfo )
    {
    FTRACE(FPrint(_L("CDunBtPlugin::CleanPartialChanneldata()")));
    RSocket* socket = &iBTPorts[aCleanupInfo.iEntityIndex].iBTPort;
    iTransporter->FreeChannel( socket );
    iBTPorts[aCleanupInfo.iEntityIndex].iChannelNum = KErrNotFound;
    socket->Close();
    if ( aCleanupInfo.iNewEntity )
        {
        iBTPorts.Remove( aCleanupInfo.iEntityIndex );
        }
    FTRACE(FPrint(_L("CDunBtPlugin::CleanPartialChanneldata() complete")));
    }

// ---------------------------------------------------------------------------
// From class MDunLocalMediaPlugin.
// CDunBtPlugin::ConstructL
// ---------------------------------------------------------------------------
//
void CDunBtPlugin::ConstructL( MDunServerCallback* aServer,
                               CDunTransporter* aTransporter )
    {
    FTRACE(FPrint( _L( "CDunBtPlugin::ConstructL()" ) ));
    if ( !aServer || !aTransporter )
        {
        FTRACE(FPrint(_L("CDunBtPlugin::ConstructL() not initialized!")));
        User::Leave( KErrBadHandle );
        }
    iServer = aServer;
    iTransporter = aTransporter;
    FTRACE(FPrint( _L( "CDunBtPlugin::ConstructL() complete" ) ));
    }

// ---------------------------------------------------------------------------
// From class MDunLocalMediaPlugin.
// Gets called when server changes a plugin's state
// ---------------------------------------------------------------------------
//
TInt CDunBtPlugin::NotifyServerStateChange( TDunPluginState aPluginState )
    {
    FTRACE(FPrint(_L("CDunBtPlugin::NotifyServerStateChange()")));
    TInt retTemp;
    switch ( aPluginState )
        {
        case EDunStateTryListen:
            if ( PluginState() != EDunStateLoaded )
                {
                FTRACE(FPrint(_L("CDunBtPlugin::NotifyServerStateChange() (not ready) complete")));
                return KErrNotReady;
                }
            // Change to listening mode
            TRAPD( retTrap, ConstructListenerL() );
            if ( retTrap != KErrNone )
                {
                FTRACE(FPrint(_L("CDunBtPlugin::NotifyServerStateChange() (ERROR) complete (%d)"), retTrap));
                return retTrap;
                }
            break;
        case EDunStateTryUninitialize:
            if ( PluginState() == EDunStateUninitialized )
                {
                FTRACE(FPrint(_L("CDunBtPlugin::NotifyServerStateChange() (not ready) complete")));
                return KErrNotReady;
                }
            // Uninitialize
            retTemp = Uninitialize();
            if ( retTemp != KErrNone )
                {
                FTRACE(FPrint(_L("CDunBtPlugin::NotifyServerStateChange() (not ready) complete (%d)"), retTemp));
                return KErrNotReady;
                }
            break;
        default:
            FTRACE(FPrint(_L("CDunBtPlugin::NotifyServerStateChange() (unknown state) complete")));
            return KErrNotSupported;
        }
    FTRACE(FPrint(_L("CDunBtPlugin::NotifyServerStateChange() complete")));
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// From class MDunLocalMediaPlugin.
// Gets called when server needs to know the active connection
// ---------------------------------------------------------------------------
//
TConnId CDunBtPlugin::ActiveConnection()
    {
    FTRACE(FPrint(_L("CDunBtPlugin::ActiveConnection()")));
    if ( iBTPorts.Count() >= 1 )
        {
        FTRACE(FPrint(_L("CDunBtPlugin::ActiveConnection() complete")));
        return &iBTPorts[0];
        }
    FTRACE(FPrint(_L("CDunBtPlugin::ActiveConnection() (not found) complete")));
    return NULL;
    }

// ---------------------------------------------------------------------------
// From class MDunListenCallback.
// Gets called when new channel must be created
// ---------------------------------------------------------------------------
//
TInt CDunBtPlugin::NotifyChannelAllocate( TBool& aNoFreeChans )
    {
    // Now state can be either EDunStateListening (no channels) or
    // EDunStateChanneled (one or more channels). Support both states
    TDunPluginState startState = PluginState();
    if ( startState!=EDunStateListening && startState!=EDunStateChanneled )
        {
        FTRACE(FPrint(_L("CDunBtPlugin::NotifyChannelAllocate() (not ready) complete")));
        return KErrNotReady;
        }
    if ( startState == EDunStateListening )
        {
        ReportStateChangeUp( EDunStateTryChannel );
        }
    TBtCleanupInfo cleanupInfo;
    TRAPD( retTrap, AllocateChannelL(aNoFreeChans,cleanupInfo) );
    if ( retTrap != KErrNone )
        {
        CleanPartialChanneldata( cleanupInfo );
        FTRACE(FPrint(_L("CDunBtPlugin::NotifyChannelAllocate() (trapped!) complete")));
        return retTrap;
        }
    if ( startState == EDunStateListening )
        {
        ReportStateChangeUp( EDunStateChanneled );
        }
    FTRACE(FPrint(_L("CDunBtPlugin::NotifyChannelAllocate() complete")));
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// From class MDunListenCallback.
// Gets called when an existing channel must be freed
// ---------------------------------------------------------------------------
//
TInt CDunBtPlugin::NotifyChannelFree()
    {
    // No implementation needed here
    return KErrNotSupported;
    }

// ---------------------------------------------------------------------------
// From class MDunConnMon.
// Gets called when line status changes or when any type of error is detected
// ---------------------------------------------------------------------------
//
void CDunBtPlugin::NotifyProgressChangeL( TConnId aConnId,
                                          TDunConnectionReason aConnReason )
    {
    FTRACE(FPrint( _L( "CDunBtPlugin::NotifyProgressChangeL()" ) ));
    // Find matching failed ID
    TBtPortEntity* foundEntity = NULL;
    TInt foundIndex = GetPortByConnId( aConnId, foundEntity );
    if ( !foundEntity )
        {
        FTRACE(FPrint( _L( "CDunBtPlugin::NotifyProgressChangeL() (not found) complete")) );
        User::Leave( KErrNotFound );
        }
    if ( aConnReason.iReasonType == EDunReasonTypeSignal )
        {
        if ( aConnReason.iContext != EDunMediaContextNetwork )
            {
            // Should never come here as other signals are for RComm
            FTRACE(FPrint( _L( "CDunBtPlugin::NotifyProgressChangeL() (ERROR) complete")) );
            User::Leave( KErrGeneral );
            }
        // Signal change detected on network side -> process change
        if ( aConnReason.iSignalType == KSignalDCD )
            {
            SetRFCOMMStatusCommand( *foundEntity,
                                    KModemSignalDV,
                                    aConnReason.iSignalHigh );
            FTRACE(FPrint( _L( "CDunBtPlugin::NotifyProgressChangeL() DV changed")) );
            }
        else if ( aConnReason.iSignalType == KSignalRNG )
            {
            SetRFCOMMStatusCommand( *foundEntity,
                                    KModemSignalIC,
                                    aConnReason.iSignalHigh );
            FTRACE(FPrint( _L( "CDunBtPlugin::NotifyProgressChangeL() IC changed")) );
            }
        // Omit other signals
        }
    else
        {
        // All other cases are down indications from local media side
        if ( foundEntity->iBTPort.SubSessionHandle() )
            {
            iTransporter->FreeChannel( &foundEntity->iBTPort );
            // CDunTransporter will notify the listener about advertisement
            // status change after FreeChannel() so no need to do
            // IssueRequestL() for CDunBtListen here after this.
            foundEntity->iBTPort.Close();
            }
        ManageAdvertiserFreeOperationL();
        // Now resources are freed so command server to reopen possibly
        // existing queued plugins
        iServer->NotifyPluginReopenRequest();
        }
    FTRACE(FPrint( _L( "CDunBtPlugin::NotifyProgressChangeL() complete")) );
    }

// ======== GLOBAL FUNCTIONS ========

// ---------------------------------------------------------------------------
// NewLocalPluginL implements factory construction for
// the class CDunBtPlugin.
// The function is exported at ordinal 1.
// ---------------------------------------------------------------------------
//
EXPORT_C MDunLocalMediaPlugin* NewLocalPluginL()
    {
    return new (ELeave) CDunBtPlugin;
    }