// Copyright (c) 2004-2010 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:
// Implements the interface to BCA.
// 
//

/**
 @file BcaIoController.cpp
*/


#include "OstTraceDefinitions.h"
#ifdef OST_TRACE_COMPILER_IN_USE
#include "BcaIoControllerTraces.h"
#endif

#include <e32uid.h>
#include <nifmbuf.h>
#include <e32svr.h>
#include <u32hal.h>

#include "Constants.h"
#include "BcaIoController.h"
#include "Sender.h"
#include "Receiver.h"

const TUint KDefaultSendQueueSize=5;

#ifdef __EABI__
// Patch data is used and KMaxTxIPPacketSize and KMaxRxIPPacketSize can be modified to a different value in RawIpNif.iby file
extern const TInt KMaxSendQueueLen = KDefaultSendQueueSize;
extern const TInt KMaxTxIPPacketSize = KMaxIPPacket + KIPTagHeaderLength;
extern const TInt KMaxRxIPPacketSize = KMaxIPPacket + KIPTagHeaderLength;
#endif

CBcaIoController::CBcaIoController(MControllerObserver& aObserver)
/**
 * Constructor. 
 *
 * @param aObserver Reference to the observer of this state machine
 */
    : iSendState(EIdle),
      iFlowBlocked(EFalse),
      iNumPacketsInSendQueue(0),
      iObserver(aObserver),
      iMBca(NULL),
      iSender(NULL),
      iReceiver(NULL),
      iLoader(NULL)	  
    {
    }

CBcaIoController* CBcaIoController::NewL(MControllerObserver& aObserver)
/**
 * Two-phase constructor. Creates a new CBcaIoController object, performs 
 * second-phase construction, then returns it.
 *
 * @param aObserver The observer, to which events will be reported
 * @return A newly constructed CBcaIoController object
 */
	{
	CBcaIoController* self = new (ELeave) CBcaIoController(aObserver);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

void CBcaIoController::ConstructL()
/**
 * Second-phase constructor. Creates all the state objects it owns.
 */
    {
    OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_CONSTRUCTL_1, "CBcaIoController::ConstructL");

#ifdef RAWIP_HEADER_APPENDED_TO_PACKETS
    iIPTagHeader = new (ELeave) CIPTagHeader(iTheLogger);
#endif // RAWIP_HEADER_APPENDED_TO_PACKETS

#if defined __EABI__
    // Default value for queue length
    iMaxSendQueueLen = KMaxSendQueueLen;
    // Default value for Tx and Rx packet size
    iMaxTxPacketSize = KMaxTxIPPacketSize;
    iMaxRxPacketSize = KMaxRxIPPacketSize;
#else // WINS
    // Set default values in case patch is not present in epocrawip.ini
    iMaxSendQueueLen = KDefaultSendQueueSize;    
    iMaxTxPacketSize = KMaxIPPacket + KIPTagHeaderLength;
    iMaxRxPacketSize = KMaxIPPacket + KIPTagHeaderLength;
           
    // for the emulator process is patched via the epocrawip.ini file
    UserSvr::HalFunction(EHalGroupEmulator,EEmulatorHalIntProperty,(TAny*)"rawip_KMaxSendQueueLen",&iMaxSendQueueLen);
    UserSvr::HalFunction(EHalGroupEmulator,EEmulatorHalIntProperty,(TAny*)"rawip_KMaxTxIPPacketSize",&iMaxTxPacketSize);
    UserSvr::HalFunction(EHalGroupEmulator,EEmulatorHalIntProperty,(TAny*)"rawip_KMaxRxIPPacketSize",&iMaxRxPacketSize);
#endif
    
    // end note
    
    iSender = CSender::NewL(*this, iMaxTxPacketSize);
    iReceiver = CReceiver::NewL(*this, iMaxRxPacketSize);
    iLoader = new (ELeave) CBcaControl(*this);
    }
	


CBcaIoController::~CBcaIoController()
/**
 * Destructor.
 */
	{
    iSendQueue.Free();
    iNumPacketsInSendQueue = 0;
    
	delete iReceiver;
	delete iSender;
	delete iLoader;
	
#ifdef RAWIP_HEADER_APPENDED_TO_PACKETS
    delete iIPTagHeader;
#endif // RAWIP_HEADER_APPENDED_TO_PACKETS
	}

/** sets the BCA Stack name

* @param aBcaStack Text composed of bca stack and next bca names
*/	
void CBcaIoController::SetBcaStackAndName(const TDesC& aBcaStack, const TDesC& aBcaName)
	{
	iBcaName.Set(aBcaName);
	iBcaStack.Set(aBcaStack);
	}
	

void CBcaIoController::StartL()
/**
 *  Used to kick off the initialisation for this module
 */
	{
	OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_STARTL_1, "CBcaIoController::StartL is called.");

    iLoader->StartLoadL();
	}

void CBcaIoController::Stop(TInt aError)
/**
 *  Used to shutdown this module. This will cancel all the outstanding 
 *  requests on the active objects owned by this module and shutdown.
 * @param aError the passed in error code as to why Stop has been called
 */
	{
	OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_STOP_1, "CBcaIoController::Stop is called.");

	//Stop all the active objects
	iReceiver->Cancel();

	if (iSendState == ESending)
		{
		iSender->Cancel();
		}

	// Update module state
	SendState(EShuttingDown);
	
    iLoader->ShutdownBca(aError);  
	}


ESock::MLowerDataSender::TSendResult CBcaIoController::Send(RMBufChain& aPdu)
/**
 *  This method is called by CRawIPFlow in order to send a packet down
 *  to the BCA. 
 *
 *  @param aPdu a data packet
 */
    {
    OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_SEND_1, "<<CBcaIoController::Send");

    // Check if flow is shutting down
    if (iSendState == EShuttingDown)
        {
        OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_SEND_2, "    ERROR: Nif is shutting down");
        
        // when the flow is destroyed the memory for this packet will be 
        // cleaned up - just tell the layers above to stop sending.
        aPdu.Free();
        
        return ESock::MLowerDataSender::ESendBlocked;
        }
    
    // check that this packet isnt too big - If it is, we dont want to send it or
    // add it to our queue
    if ((aPdu.Length() - aPdu.First()->Length()) > iMaxTxPacketSize)
        {
        OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_SEND_3, "Packet is too large - discarding");
        OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_SEND_4, "<<CSender::Send -> Error");

        // in debug panic - this should not happen, MTU on the uplink should
        // be strictly enforced
        __ASSERT_DEBUG(ETrue,Panic(KFlowInvalidULPacketSize));
        
        aPdu.Free();
       
        // may be counter intuitive, however the only options here are either 
        // send accepted or blocked (MLowerDataSender).
        
        OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_SEND_5, "<<CBcaIoController::Send - return ContinueSending");
        return ESock::MLowerDataSender::ESendAccepted;
        }
    
    // transmit flow control.
    if (iFlowBlocked)
        {
        // Transmit is off for this flow - we must have received a block
        // message from our control.  append this message to the queue
        // and tell the layer above it to kindly stop sending.
        OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_SEND_6, "    Sender blocked, appending packet to queue");
        
        AppendToSendQueue(aPdu);
        
        // may be a bit counter-intuitive, however, even if the flow is blocked
        // there is no reason not to ask for more data as long as our send
        // queue isn't full.
        
        if (IsSendQueueFull())
            {
            OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_SEND_7, "<<CBcaIoController::Send - return StopSending");
            return ESock::MLowerDataSender::ESendBlocked;
            }
        else
            {
            OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_SEND_8, "<<CBcaIoController::Send - return ContinueSending");
            return ESock::MLowerDataSender::ESendAccepted;       
            }
        }
    
    // transmit is on for this flow.  send a packet or queue it
    // if we're already sending one.

    if (iSendState == ESending)
        // If this happens, it means that TCP/IP has sent us an IP packet
        // while we're still sending the previous one. 
        {    
        OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_SEND_9, "    Sender busy, appending packet to queue");
        AppendToSendQueue(aPdu);
        
        if (IsSendQueueFull())
            {
            OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_SEND_10, "<<CBcaIoController::Send - return StopSending");
            return ESock::MLowerDataSender::ESendBlocked;
            }
        }
    else
        {
        // if we're idle, then we must not be sending.  If we're not idle
        // then the SendComplete() will handle sending any additional 
        // packets that might have been queued onto the send queue.
    
        // Update module state
        OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_SEND_11, "     set State to ESending");
        iSendState = ESending;
         
        iSender->Send(aPdu);
        }

    // if our send queue isn't full, then the send is accepted
    // otherwise, block this flow until we have room for the next
    // packet
    
    OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_SEND_12, "<<CBcaIoController::Send - return ContinueSending");

    return ESock::MLowerDataSender::ESendAccepted;
    }
	
void CBcaIoController::SendComplete()
/**
 *  A packet has finished sending - check to see if we need
 *  to process more packets.
 */
    {
    OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_SENDCOMPLETE_1, ">>CBcaIoController::SendComplete");

    // if we've been blocked while in the middle of a 
    // send - don't continue sending, this will happen
    // when the flow is resumed.
	
    iSendState = EIdle;

    // are we available to transmit?
    
    if (iFlowBlocked == EFalse)
        {
        // flow transmit is on
    
        TBool resumeSending = EFalse;
    
        // if the queue is full, it means that while a packet was
        // outstanding in BCA, we've filled up our send queue and
        // informed the upper layers to stop sending.
        
        if (IsSendQueueFull())
            {
            resumeSending = ETrue;
            }
        
        // if the queue has anything inside it, we need to send the 
        // first packet in the queue.  note: the resumeSending check is to 
        // short circuit this so we don't check the queue length twice
        // in the case that it is full
        
        if ((resumeSending) || (!IsSendQueueEmpty()))
            {
            iSendState = ESending;
            
            RMBufChain tmpPdu;
            OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_SENDCOMPLETE_2, "    Packet removed from queue to send");
            RemoveFromSendQueue(tmpPdu);
            
            // Update module state
            OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_SENDCOMPLETE_3, "     set State to ESending");
          
            iSender->Send(tmpPdu);
            
            // if the queue was full, again, we told the upper layers
            // to stop sending, we need to resume this flow.  order is
            // important as we don't want to get a packet before we've 
            // given one to BCA to send.
            if (resumeSending)
                {
                iObserver.ResumeSending();
                }
            }
        }
    
    OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_SENDCOMPLETE_4, "<<CBcaIoController::SendComplete");
    }


void CBcaIoController::ResumeSending()
/**
 *  Flow is being unblocked this will resume sending.
 */
    {
    OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_RESUMESENDING_1, ">>CBcaIoController::ResumeSending");

    // allows for normal SendComplete behaviour if there is
    // a packet outstanding with BCA
    iFlowBlocked = EFalse;
    
    // If the send state is idle, then there isn't a packet
    // outstanding, check the send queue to see if there are
    // packets to send.
    
    if (iSendState == EIdle)
        {
        // flow transmit is on
    
        TBool resumeSending = EFalse; 
        
        if (IsSendQueueFull())
            {
            resumeSending = ETrue;
            }
    
        // if the queue has anything inside it, we need to send the 
        // first packet in the queue.  note: the resumeSending check is to 
        // short circuit this so we don't check the queue length twice
        // in the case that it is full
        
        if ((resumeSending) || (!IsSendQueueEmpty()))
            {
            RMBufChain tmpPdu;
            OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_RESUMESENDING_2, "    Packet removed from queue to send");
            RemoveFromSendQueue(tmpPdu);
            
            // Update module state
            OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_RESUMESENDING_3, "     set State to ESending");
            iSendState = ESending;
          
            iSender->Send(tmpPdu);
            
            // if the queue was full, again, we told the upper layers
            // to stop sending, we need to resume this flow.  order is
            // important as we don't want to get a packet before we've 
            // given one to BCA to send.
            if (resumeSending)
                {
                iObserver.ResumeSending();
                }
            }
        }
    
    OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_RESUMESENDING_4, "<<CBcaIoController::ResumeSending");
    }

#ifdef RAWIP_HEADER_APPENDED_TO_PACKETS
void CBcaIoController::SetType(TUint16 aType)
    {
/**
 *  Used to specify the type of the IP header.
 */
    OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_SETTYPE_1, "CBcaController::SetType");
    
    iIPTagHeader->SetType(aType);   
    }

void CBcaIoController::AddHeader(TDes8& aDes)
/**
 *  Used to add the IP header to the packet before sending to the BCA.
 */
    {
    OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_ADDHEADER_1, "CBcaController::AddHeader");

    iIPTagHeader->AddHeader(aDes);
    }

TUint16 CBcaIoController::RemoveHeader(RMBufChain& aPdu)
/**
 *  Used to remove the IP header from the received the packet before sending to the 
 *  TCP/IP layer.  
 * @return The IP header that has been removed from the packet
 */
    {
    OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCAIOCONTROLLER_REMOVEHEADER_1, "CBcaController::RemoveHeader");

    return (iIPTagHeader->RemoveHeader(aPdu));
    }   
#endif // RAWIP_HEADER_APPENDED_TO_PACKETS


CBcaControl::CBcaControl(CBcaIoController& aObserver)
/**
 * Constructor. Performs standard active object initialisation.
 *
 * @param aObserver Reference to the observer of this state machine
 */
	: CActive(EPriorityStandard), 
	  iObserver(aObserver), 
	  iMBca(NULL),
	  iState(EIdling),
	  iError(KErrNone)
	  
	{
	CActiveScheduler::Add(this);
	}
	
CBcaControl::~CBcaControl()
/**
 * Destructor.
 */
	{
	Cancel();
	if(iMBca)
		{
        //If the Bca is still open, close it
        if(EBcaOpened == iState)
            {
            iMBca->Close();
            }
        //delete the BCA instance
		iMBca->Release();	
		}
	// Library will be Closed when iBcaDll is destroyed.
	}

void CBcaControl::RunL()
/**
 *  Called after request is completed. 
 *  
 */
	{
	OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_RUNL_1, "CBcaControl::RunL() called");
	switch (iState)
		{
		//in this state, Ioctl is called to set IAP ID, check the result of
		// Ioctl, then either set the BCA stack with another Ioctl call, 
		// open the BCA (if there's no BCA stack to set), or stop the NIF.
		case EIdling:
			{
			if(iStatus == KErrNone || iStatus == KErrNotSupported)
				{
				if(iStatus == KErrNotSupported)
					{
					OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_RUNL_2, "This BCA does not support IAPID set");
					}
				else
					{
					OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_RUNL_3, "This BCA supports IAPID set");
					}
				
				TPtrC bcaStack = iObserver.BcaStack();
				if(bcaStack.Length())
					{
					TBuf8<KMaxName> remainingBcaStack8;
					remainingBcaStack8.Copy(bcaStack);
					iMBca->Ioctl(iStatus, KBcaOptLevelGeneric,KBCASetBcaStack,remainingBcaStack8);
					}
				else
					{
					TRequestStatus* statusPtr=&iStatus;
					User::RequestComplete(statusPtr,KErrNone);
					}
				iState = EIAPSet;
				SetActive();	
				}
			else
				{
				OstTraceDef1(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_RUNL_4, "ERROR in BCA IAPID set = %d", iStatus.Int());
				iObserver.Stop(iStatus.Int());
				}
			
			break;
			}
			
		//in this case, we receive the result of Ioctl call to set Bca Stack.
		// Check the result of Ioctl, then Open the Bca or stop the NIF
		case EIAPSet:
			{
			if(iStatus == KErrNotSupported || iStatus == KErrNone)
				{
				if(iStatus == KErrNotSupported)
					{
					OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_RUNL_5, "This BCA does not support BCA stacking");
					}
				else
					{
					OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_RUNL_6, "This BCA supports BCA stacking");
					}
				iMBca->Open(iStatus, iObserver.Port());
				iState = EBcaStackSet;
				SetActive();	
				}
			else
				{
				OstTraceDef1(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_RUNL_7, "ERROR in BCA stack set = %d", iStatus.Int());
				iObserver.Stop(iStatus.Int());
				}
			break;
			}
		
		//in this state, BCA Open is called. Checks the result of Open.
		// If it is successful,then start the NIF. Otherwise stops the NIF.
		case EBcaStackSet:
			{
			if(iStatus != KErrNone && iStatus !=  KErrAlreadyExists)
				{
				OstTraceDef1(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_RUNL_8, "ERROR in BCA Open = %d", iStatus.Int());
				iObserver.Stop(iStatus.Int());
				}
			else
				{
                iState = EBcaOpened;
                //Activate the receiver Active Object
				iObserver.Receiver().StartListening();
				OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_RUNL_9, "CBcaIoController Is Initialised");
				TRAPD(err, iObserver.GetObserver().InitialiseL(MRawIPObserverBase::EBcaController,KErrNone));
				if(err != KErrNone)
					{
					OstTraceDef1(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_RUNL_10, "ERROR in BCA Open Initialise observer = %d", err);
					iObserver.Stop(err);
					}
				}
			break;
			}

		//in this state, BCA is Shutdown, shutdown the NIF.
		case EClosing:
			{
			// linklayer shutdown
			iState = EIdling;
			iObserver.GetObserver().ShutDown(MControllerObserver::EBcaController, iError);
			break;
			}
		// Wrong state.
		default:
			{
			OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_RUNL_11, "ERROR CBcaControl::RunL(): Unknown state");
	        OstTraceDefExt2(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_RUNL_12, "PANIC: %S %d", KNifName, KBcaUnkownState);
	        User::Panic(KNifName, KBcaUnkownState);
			break;
			}
		}

	}
	
void CBcaControl::DoCancel()
/**
 *	cancel active request. 
 */
	{
	OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_DOCANCEL_1, "CBcaControl::DoCancel called.");
	OstTraceDef1(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_DOCANCEL_2, "iState value is %d", iState);
	switch (iState)
		{
		case EIdling:
		case EIAPSet:
	    case EBcaStackSet:
			if(iMBca)
				{
				iMBca->CancelIoctl();
				}
			iState = EIdling;
			break;
		case EClosing:
            iState = EIdling;		    
            break;    
		default:
			OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_DOCANCEL_3, "ERROR CBcaControl::DoCancel(): Unknown state");
	        OstTraceDefExt2(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_DOCANCEL_4, "PANIC: %S %d", KNifName, KBcaUnkownState);
	        User::Panic(KNifName, KBcaUnkownState);
			break;
		}
	}
	
void CBcaControl::StartLoadL()
/**
 *  This method loads the C32BCA library and uses Ioctl to set the Bca iIapId. 
 */
	{
	OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_STARTLOADL_1, "CBcaControl::StartLoad");
	
	//iMBca should not be initialized at this point
	__ASSERT_DEBUG(!iMBca,Panic(KBcaAlreadyExists));
	
	//We don't expect iMBca here, but if it occurs, we delete previous BCA Instance
	if(iMBca)
	    {
        //If the state is still "open", close it first
        if(EBcaOpened == iState)
            {
            iMBca->Close();
            }
        iMBca->Release();
        }

	// Loads Bca Dll and creates a Bca instance;
	User::LeaveIfError(iBcaDll.iObj.Load(iObserver.BcaName()));
	
	TNewBcaFactoryL newBcaFactoryProcL = (TNewBcaFactoryL)iBcaDll.iObj.Lookup(1);
	if (NULL == newBcaFactoryProcL)
		{
		OstTraceDef1(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_STARTLOADL_2, "Library entry point found error %d", KErrBadLibraryEntryPoint);
		User::Leave(KErrBadLibraryEntryPoint);	
		}
	
	MBcaFactory* bcaFactory = (*newBcaFactoryProcL)();

	if(!bcaFactory)
		{
		OstTraceDef1(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_STARTLOADL_3, "BcaFactory creation error %d", KErrCompletion);
		User::Leave(KErrCompletion);	
		}
	CleanupReleasePushL(*bcaFactory);
	
	iMBca = bcaFactory->NewBcaL();
	CleanupStack::PopAndDestroy(bcaFactory);
	
	iObserver.SetBca(iMBca); //Pass BCA pointer.

	TPckg<TUint32> aOpt(iObserver.IapId());
	iMBca->Ioctl(iStatus,KBcaOptLevelGeneric,KBCASetIapId,aOpt);
	
	iState = EIdling;
	SetActive();
	}

	
void CBcaControl::ShutdownBca(TInt aError)
/**
 *  Bca Shutdown.
 
 *  @param aError the error code to shutdown the NIF. 
 */
	{
	__ASSERT_DEBUG(iMBca,Panic(KBcaNotExist));
	Cancel();
        
    //We should only call shutdown or close if we have successfully opened a BCA Channel
    if((iMBca) && (EBcaOpened == iState))
        {
        if(aError == KErrConnectionTerminated )
            {
            OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_SHUTDOWNBCA_1, "This is an emergency shutdown, it kills the NIF immediately.");
            // It is a emergency shutdown, it kills the NIF immediately.
            iMBca->Close();
            iState = EIdling;
            iObserver.GetObserver().ShutDown(MControllerObserver::EBcaController, aError);
            }
        else
            {
            OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_SHUTDOWNBCA_2, "This is a graceful termination which takes a while.");
            //It is a graceful termination which takes a while.
            iError = aError;
            iState = EClosing;
            iMBca->Shutdown(iStatus);
            SetActive();    
            }
        }
    else //nothing to shutdown, just notify linklayer down.
        {
        OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_INTERNALS, CBCACONTROL_SHUTDOWNBCA_3, "Bca is not initialized or opened, bring the linklayer down");
        iState = EIdling;
        iObserver.GetObserver().ShutDown(MControllerObserver::EBcaController, aError);
        }
  
	}

/** Panic function for RawIpNif 

* @param aPanic panic code */
void Panic(TRawIPNifPanic aPanic)
	{
	User::Panic(KNifName,aPanic);
	}

