linklayerprotocols/pppnif/SPPP/PPPHDLC.CPP
changeset 0 af10295192d8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linklayerprotocols/pppnif/SPPP/PPPHDLC.CPP	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,2411 @@
+// Copyright (c) 1997-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:
+// See RFC 1662 and "PPP Design and Debugging" by James Carlson (ISBN 0201185393)
+// PPP's HDLC support
+// 
+//
+
+/**
+ @file
+*/
+
+#include <cdblen.h>
+#include <es_ini.h>
+#include "PPPHDLC.H"
+#include "PPPLOG.H"
+#include "ncpip.h"
+#include "PPPConfig.h"
+
+#include <commsdattypeinfov1_1.h>
+
+// Receive buffer size for the BCA layer. HDLC has a separate Rx buffer.
+static const TInt KReceiveBufferSize=4096; 
+
+// Internal flag bits (iFlags)
+static const TUint KPppHdlcSendFcs16 = 0x00000001;
+static const TUint KPppHdlcSendFcs32 = 0x00000002;
+static const TUint KPppHdlcSendCompProt = 0x00000004;
+static const TUint KPppHdlcSendCompAddrCtrl = 0x00000008;
+static const TUint KPppHdlcRecvFcs16 = 0x00000010;
+static const TUint KPppHdlcRecvFcs32 = 0x00000020;
+static const TUint KPppHdlcRecvCompProt = 0x00000040;
+static const TUint KPppHdlcRecvCompAddrCtrl = 0x00000080;
+//static const TUint KPppHdlcRecvEscSeen = 0x00000100;
+//static const TUint KPppHdlcSendXonXoff = 0x00000400;
+//static const TUint KPppHdlcRecvXonXoff = 0x00000800;
+static const TUint KPppHdlcSendBusy = 0x00001000;
+static const TUint KPppHdlcRecvEscPending = 0x00002000;
+static const TUint KPppHdlcCommConfigOk = 0x00004000;
+static const TUint KPppHdlcApplyPending = 0x00010000;
+static const TUint KPppHdlcAtApplyMark = 0x00020000;
+static const TUint KPppHdlcOrigConfig = 0x000040000;
+
+/*
+#if defined(_DEBUG) && defined (__WINS__)
+#include <f32file.h>
+#endif
+*/
+
+//
+// HDLC Support
+//
+
+static const TInt KPppLoWat = 16;
+static const TInt KPppHiWat = 32;
+
+static const TInt KOptionListLen = 6;
+static const TUint8 OptionList[KOptionListLen] =
+	{
+	KPppLcpOptEscapeCharMap,
+	KPppLcpOptProtocolCompress,
+	KPppLcpOptAddrCtrlCompress,
+	KPppLcpOptFcsType,
+	KPppLcpOptPadding,
+	KPppLcpOptCompoundFrames
+	};
+
+CPppHdlcLink::CPppHdlcLink(CPppLcp* aLcp)
+	: CPppLinkBase(aLcp),
+	  iError(KErrNone),
+	  iLinkDown(ETrue) // By default
+	{
+	OptRegister(iPppLcp, OptionList, KOptionListLen);
+	iSendLoWat = KPppLoWat;
+	iSendHiWat = KPppHiWat;
+	}
+
+CPppHdlcLink::~CPppHdlcLink()
+	{
+	iSendPkt.Free();
+	iSendQ.Free();
+	iRecvPkt.Free();
+	if (iRecvMBuf)
+		{
+		iRecvMBuf->Free();
+		}
+	iRecvQ.Free();
+
+	delete iBcaReader;
+	delete iBcaWriter;
+	delete iBcaControl; // Must be deleted last, as it makes sure the BCA is closed.
+	
+	if(iBca) // Remove the BCA itself, if it was created.
+		{
+		iBca->Release();
+		}
+	}
+	
+/**
+Allocates the link resources, associated with BCA 
+ 
+@leave If the link cannot be created.
+*/
+void CPppHdlcLink::CreateL()
+    {
+    if (iPppLcp->GetBCAProvision() == NULL)
+        {
+        User::Leave(KErrNotReady);
+        }
+        
+	LoadBcaL(); 
+	__ASSERT_ALWAYS(iBca, User::Panic(_L("PPP Panic"), EPppPanic_NullBca));
+	
+	iBcaControl = new (ELeave)CBcaControl(*this, *iBca); 
+	iBcaReader  = new (ELeave)CBcaReader( *this, *iBca, KPppHdlcCommReadPriority);
+	iBcaWriter  = new (ELeave)CBcaWriter( *this, *iBca, KPppHdlcCommWritePriority);	
+    }
+    
+/** 
+Loads the BCA based on Comms settings 
+
+@leave if the BCA cannot be loaded
+*/
+void CPppHdlcLink::LoadBcaL()
+	{
+	LOG( Logger().Printf(_L("CPppHdlcLink::LoadBcaL: Loading BCA.[%S]"), GetBCAName().Ptr()); )
+		
+	User::LeaveIfError(iBcaDll.iObj.Load(iPppLcp->GetBCAProvision()->GetBCAName()));
+	
+	TNewBcaFactoryL newBcaFactoryProcL = (TNewBcaFactoryL)iBcaDll.iObj.Lookup(1);
+	if (!newBcaFactoryProcL)
+		{
+		LOG(Logger().Printf(_L("CPppHdlcLink::LoadBcaL: BCA Factory method lookup failed. ")); )
+		User::Leave(KErrBadLibraryEntryPoint);	
+		}
+	
+	MBcaFactory* bcaFactory = (*newBcaFactoryProcL)();
+	__ASSERT_ALWAYS(bcaFactory, User::Panic(_L("PPP Panic"), EPppPanic_NullBcaFactory));
+	
+	CleanupReleasePushL(*bcaFactory);
+	
+	iBca = bcaFactory->NewBcaL(); 
+	__ASSERT_ALWAYS(iBca, User::Panic(_L("PPP Panic"), EPppPanic_NullBca));
+	
+	CleanupStack::PopAndDestroy(bcaFactory); // We only need once BCA per PPP lifetime.
+	LOG(Logger().Printf(_L("CPppHdlcLink::LoadBcaL: BCA successfully loaded. ")); )
+	}
+
+
+
+void CPppHdlcLink::InitEscMap()
+/**
+Initializes the send escape map to a conservative value.
+iRecvEscMap is not set here, since Carlson says that can cause problems on
+LCP renegotiation.
+
+@post iSendEscMap is initialized
+*/
+	{
+	// Always Escape bytes 0..31 initially
+	Mem::FillZ(iSendEscMap, sizeof(iSendEscMap));
+	iSendEscMap[0] = 0xffffffff;
+	// Also escape the two special bytes
+	SetEscMapBit(KPppHdlcFlagChar);
+	SetEscMapBit(KPppHdlcEscChar);
+	}
+
+void CPppHdlcLink::SetEscMapBit(TUint8 aChar)
+/**
+Marks this character to be escaped when sending.
+
+@param aChar Character
+*/
+	{
+	iSendEscMap[aChar>>5] |= 1<<(aChar&0x1f);
+	}
+
+void CPppHdlcLink::DeferredApplyOptions()
+/**
+Starts using the negotiated PPP options.
+Called once LCP has completed.
+*/
+	{
+//	LOG( iPppLcp->iLogger->Printf(_L("PPP Link DeferredApplyOptions()")); )
+	iFlags &= ~(KPppHdlcApplyPending|KPppHdlcAtApplyMark);
+	iFlags = (iFlags & ~iPendingMask) | iPendingFlags;
+	iSendEscMap[0] = iPendingEscMap;
+	}
+
+void CPppHdlcLink::GetSendRecvSize(TInt& aMaxRecvSize, TInt& aMaxSendSize)
+/**
+Returns the link's idea of the maximum receive and send unit sizes.
+
+@param aMaxRecvSize Returns the MRU, or 0 for default
+@param aMaxSendSize Returns the MTU, or 0 for default
+*/
+	{
+	aMaxRecvSize = iPppLcp->MaxReceiveSize();
+	aMaxSendSize = iPppLcp->MaxTransferSize();
+	}
+
+void CPppHdlcLink::OptNegotiationStarted()
+	{
+//	LOG( iPppLcp->iLogger->Printf(_L("PPP Link OptNegotiationStarted()")); )
+	InitEscMap();
+
+// Initialize (most) flags
+	iFlags = (iFlags & (KPppHdlcCommConfigOk|KPppHdlcSendBusy|KPppHdlcOrigConfig))
+				| (KPppHdlcSendFcs16 | KPppHdlcRecvFcs16);
+
+	iPendingMask = 0;
+	iPendingFlags = 0;
+	iPendingEscMap = iSendEscMap[0];
+	iApplyConfigMarker = NULL;
+	}
+
+void CPppHdlcLink::OptNegotiationAborted()
+	{
+//	LOG( iPppLcp->iLogger->Printf(_L("PPP Link OptNegotiationAborted()")); )
+	}
+
+void CPppHdlcLink::OptNegotiationComplete()
+	{
+//	LOG( iPppLcp->iLogger->Printf(_L("PPP Link OptNegotiationComplete()")); )
+	iFlags |= KPppHdlcApplyPending;
+/*
+	if (!iSendQ.IsEmpty())
+		{
+		RMBufChain pkt = iSendQ.Last();
+		iApplyConfigMarker = pkt.First();
+		}
+	else
+		{
+		iFlags |= KPppHdlcAtApplyMark;
+		if (iSendPkt.IsEmpty())
+			DeferredApplyOptions();
+		}
+*/
+	}
+
+void CPppHdlcLink::OptFillinConfigRequestL(RPppOptionList& aRequestList)
+	{
+	// Add XON/XOFF bits to the ACCM read from ppp.ini
+	TUint32 map = iDesiredRecvEscMap;
+	if ((iOrigConfig().iHandshake & KConfigObeyXoff)!=0 ||
+		(iOrigConfig().iHandshake & KConfigSendXoff)!=0)
+		{
+		if (iOrigConfig().iXonChar<32)
+			map |= 1<<iOrigConfig().iXonChar;
+		if (iOrigConfig().iXoffChar<32)
+			map |= 1<<iOrigConfig().iXoffChar;
+		}
+	aRequestList.CreateAndAddL(KPppLcpOptEscapeCharMap,map);
+	aRequestList.CreateAndAddL(KPppLcpOptProtocolCompress);
+	aRequestList.CreateAndAddL(KPppLcpOptAddrCtrlCompress);
+	}
+
+TPppOptResponse CPppHdlcLink::OptCheckConfigRequest(RPppOption& aOption)
+/**
+Checks options in a received config request.
+Called once for each option.
+
+@param aOption LCP option
+
+@return EPppOptAck, EPppOptNak, or EPppOptReject
+*/
+	{
+	const TInt KBooleanOptDataLen = 0;
+	const TInt KOptEscapeCharMapLen = 4;
+	
+	switch (aOption.OptType())
+		{
+	case KPppLcpOptEscapeCharMap:
+		{		
+		TUint32 remoteRecvAccm = 0;
+		TBool forceNak = EFalse; // Whether the ACCM must be NAKed regardless of value.
+		
+		if(KOptEscapeCharMapLen != aOption.ValueLength()) // Invalid length: NAK 
+			{
+			aOption.SetValueLength(KOptEscapeCharMapLen); // Fix length. (value = 0x0)
+			forceNak = ETrue; 
+			}
+		else // Valid length. Extract the ACCM and negotiate.
+			{
+			remoteRecvAccm = BigEndian::Get32(aOption.ValuePtr());
+			}
+
+		// Create the sender ACCM
+		TUint32 localSendAccm = 0; // We want to escape as little as possible.
+		// Add XON/XOFF bits to the sender ACCM 
+		if ((iOrigConfig().iHandshake & KConfigObeyXoff)!=0 ||
+				(iOrigConfig().iHandshake & KConfigSendXoff)!=0)
+				{
+				if (iOrigConfig().iXonChar<32)
+					localSendAccm |= 1<<iOrigConfig().iXonChar;
+				if (iOrigConfig().iXoffChar<32)
+					localSendAccm |= 1<<iOrigConfig().iXoffChar;
+				}
+	
+		
+		// Compare the ACCMs and check if peer's proposal must be NAKed:
+		// It is NAKed if there is a disagreement on ACCM, or if the option is invalid
+		if ((~remoteRecvAccm & localSendAccm) || forceNak) 
+		    {
+			BigEndian::Put32( aOption.ValuePtr(), localSendAccm | remoteRecvAccm);
+			return EPppOptNak;
+			}
+		
+		
+		return EPppOptAck;
+		}
+
+	case KPppLcpOptProtocolCompress:
+		if(KBooleanOptDataLen == aOption.ValueLength())
+			{
+			return EPppOptAck;
+			}
+		else
+			{
+			aOption.SetValueLength(KBooleanOptDataLen);
+			return EPppOptNak;
+			}		
+	case KPppLcpOptAddrCtrlCompress:
+		if(KBooleanOptDataLen == aOption.ValueLength())
+			{
+			return EPppOptAck;
+			}
+		else
+			{
+			aOption.SetValueLength(KBooleanOptDataLen);
+			return EPppOptNak;
+			}
+
+	case KPppLcpOptFcsType:
+		{
+		if (aOption.ValueLength()<1)
+			return EPppOptReject;
+		TUint8 fcs = *aOption.ValuePtr();
+		// This only allows one FCS method at a time.
+		if (fcs==KPppHdlcFcs0Flag || fcs==KPppHdlcFcs16Flag
+#ifdef __LCP_EXTENSION_FCS_32
+		    || fcs==KPppHdlcFcs32Flag
+#endif
+	    )
+			{
+			return EPppOptAck;
+			}
+		*aOption.ValuePtr() = KPppHdlcFcs16Flag;
+		return EPppOptNak;
+		}
+
+	case KPppLcpOptPadding:
+	case KPppLcpOptCompoundFrames:
+//		if (iPppLcp->DoLcpExts())
+//			{
+			// handle the options
+//			}
+		// else fall though and reject
+	default:
+		return EPppOptReject;
+		}
+	}
+
+
+void CPppHdlcLink::CallBackOptRequestGranted()
+	{
+
+	iPppLcp->CallbackRequestGranted();
+	}
+
+
+void CPppHdlcLink::OptApplyConfigRequest(RPppOption& aOption)
+/**
+Applies options in a received config request (that was ACK'd).
+Called once for each option.
+
+@param aOption LCP option
+*/
+	{
+	
+//	__LOGTEXT2_DEBUG(_L8("CPppHdlcLink::OptApplyConfigRequest() Option = %d\n"),aOption.OptType());
+	
+	switch (aOption.OptType())
+		{
+	case KPppLcpOptEscapeCharMap:
+		iPendingEscMap = BigEndian::Get32(aOption.ValuePtr());
+		break;
+	case KPppLcpOptProtocolCompress:
+		iPendingMask |= KPppHdlcSendCompProt;
+		iPendingFlags |= KPppHdlcSendCompProt;
+		break;
+	case KPppLcpOptAddrCtrlCompress:
+		iPendingMask |= KPppHdlcSendCompAddrCtrl;
+		iPendingFlags |= KPppHdlcSendCompAddrCtrl;
+		break;
+	case KPppLcpOptFcsType:
+		{
+		TUint8 fcs = *aOption.ValuePtr();
+		iPendingMask |= (KPppHdlcSendFcs16|KPppHdlcSendFcs32);
+		iPendingFlags &= ~(KPppHdlcSendFcs16|KPppHdlcSendFcs32);
+		if (fcs==KPppHdlcFcs16Flag)
+			iPendingFlags |= KPppHdlcSendFcs16;
+
+#ifdef __LCP_EXTENSION_FCS_32
+		if (fcs==KPppHdlcFcs32Flag)
+			iFlags |= KPppHdlcSendFcs32;
+#endif
+		}
+		CallBackOptRequestGranted();
+		break;
+	case KPppLcpOptCallback:
+		CallBackOptRequestGranted();
+		break;
+	case KPppLcpOptPadding:
+	case KPppLcpOptCompoundFrames:
+	default:
+		break;
+		}
+	}
+
+
+void CPppHdlcLink::OptRecvConfigAck(RPppOption& aOption)
+/**
+Receives a Config Ack - apply the option.
+Called once for each option.
+
+@param aOption LCP option
+*/
+	{
+//	__LOGTEXT2_DEBUG(_L8("CPppHdlcLink::OptRecvConfigAck() Option = %d\n"),aOption.OptType());
+	switch (aOption.OptType())
+		{
+	case KPppLcpOptEscapeCharMap:
+		// Start using the new map immediately.
+		// This goes against the RFC, but is recommended by Carlson
+		iRecvEscMap = BigEndian::Get32(aOption.ValuePtr());
+		break;
+	case KPppLcpOptProtocolCompress:
+		// Start using the flag immediately
+		iFlags |= KPppHdlcRecvCompProt;
+		break;
+	case KPppLcpOptAddrCtrlCompress:
+		// Start using the flag immediately
+		iFlags |= KPppHdlcRecvCompAddrCtrl;
+		break;
+	case KPppLcpOptFcsType:
+		{
+		TUint8 fcs = *aOption.ValuePtr();
+		iPendingMask |= (KPppHdlcRecvFcs16|KPppHdlcRecvFcs32);
+		iPendingFlags &= ~(KPppHdlcRecvFcs16|KPppHdlcRecvFcs32);
+		if (fcs==KPppHdlcFcs16Flag)
+			iPendingFlags |= KPppHdlcRecvFcs16;
+
+#ifdef __LCP_EXTENSION_FCS_32
+		if (fcs==KPppHdlcFcs32Flag)
+			iPendingFlags |= KPppHdlcRecvFcs32;
+#endif		
+		}
+		break;
+	case KPppLcpOptPadding:
+	case KPppLcpOptCompoundFrames:
+	default:
+		break;
+		}
+	}
+
+void CPppHdlcLink::OptRecvConfigNak(RPppOption& aOption, RPppOptionList& aReqList)
+/**
+Modifies request after receiving a Config Nak.
+Called once for each option.
+
+@param aOption LCP option
+@param aReqList The associated original request to be modified
+*/
+	{
+	switch (aOption.OptType())
+		{
+	case KPppLcpOptEscapeCharMap:
+		// Carlson says to OR this with our original request, but this will
+		// work as-is with a correctly-functioning peer
+		aReqList.ReplaceOption(aOption);
+		break;
+	case KPppLcpOptProtocolCompress:
+		aReqList.ReplaceOption(aOption);
+		break;
+	case KPppLcpOptAddrCtrlCompress:
+		aReqList.ReplaceOption(aOption);
+		break;
+	case KPppLcpOptFcsType:
+		aReqList.ReplaceOption(aOption);
+		break;
+	case KPppLcpOptPadding:
+	case KPppLcpOptCompoundFrames:
+	default:
+		break;
+		}
+	}
+
+void CPppHdlcLink::OptRecvConfigReject(RPppOption& aOption, RPppOptionList& aReqList)
+/**
+Modifies request after receiving a Config Reject
+Called once for each option.
+
+@param aOption LCP option
+@param aReqList The associated original request to be modified
+*/
+	{
+//	__LOGTEXT2_DEBUG(_L8("CPppHdlcLink::OptRecvConfigReject() Option = %d\n"),aOption.OptType());
+	switch (aOption.OptType())
+		{
+	case KPppLcpOptEscapeCharMap:
+		aReqList.RemoveOption(aOption);
+		break;
+	case KPppLcpOptProtocolCompress:
+		aReqList.RemoveOption(aOption);
+		break;
+	case KPppLcpOptAddrCtrlCompress:
+		aReqList.RemoveOption(aOption);
+		break;
+	case KPppLcpOptFcsType:
+		aReqList.RemoveOption(aOption);
+		break;
+	case KPppLcpOptPadding:
+	case KPppLcpOptCompoundFrames:
+	default:
+		break;
+		}
+	}
+
+void CPppHdlcLink::OpenL()
+/**
+Starts connect establishment
+*/
+	{
+	switch (iState)
+		{
+	case EPppHdlcConnecting:
+	case EPppHdlcOpen:
+		return;
+	case EPppHdlcClosed:
+		iState = EPppHdlcConnecting;
+		break;
+	case EPppHdlcDisconnecting:
+		LOG(Logger().Printf(_L("CPppHdlcLink::OpenL: Link is disconnecting. Leaving with KErrNotReady[%d]"), KErrNotReady); )
+		User::Leave(KErrNotReady);
+		break;
+	default:
+		return;
+		}
+	LOG( Logger().Printf(_L("PPP Link OpenL")); )
+	}
+
+  
+/**
+Drops connection
+*/
+void CPppHdlcLink::Close()
+	{
+	LOG( Logger().Printf(_L("CPppHdlcLink::Close(): Link Close requested: ")); )
+	iError = KErrNone;
+	switch (iState)
+		{
+	case EPppHdlcConnecting:		
+	case EPppHdlcOpen:
+		iState = EPppHdlcDisconnecting;
+		PacketModeOff();
+		break;
+	
+	case EPppHdlcClosed:
+	case EPppHdlcDisconnecting:
+		break;
+		
+	case EPppHdlcReconnecting: // This is not used anywhere in PPP.
+	default:
+		__ASSERT_DEBUG(EFalse, User::Panic(_L("PPP Panic"), EPppPanic_IllegalHdlcState));
+		break;
+		}
+	}
+
+
+TUint16 CPppHdlcLink::DecodeProtocolID(TUint8*& aPtr) const
+/**
+Decodes the PPP protocol ID from a buffer.
+The ID may be compressed.
+
+@param aPtr Pointer to PPP frame buffer; returns pointing one past protocol ID
+
+@return PPP protocol ID
+*/
+	{
+	// Extract the protocol information - this can be either one or two bytes, but
+	// the last byte must be odd, and the first byte (if present) must be even
+	TUint16 prot = *aPtr++;
+	if(!(prot & 1))
+		{
+		prot = (TUint16) ((prot << 8) | *aPtr++);
+		}
+	return prot;
+	}
+
+TBool CPppHdlcLink::UnescapeChar(TUint8*& ptr, TUint8*& end, TUint8*& mptr, TUint8*& mend)
+/**
+Unescape the next character in the buffer. Ignore characters in the receive ACCM.
+
+@param ptr Pointer to input buffer
+@param end Pointer to one past the end of the input buffer
+@param mptr Pointer to the output buffer
+@param mend Pointer to one past the end of the output buffer (must be > mptr on entry)
+
+@return ETrue on error (error will be logged)
+
+@post KPppHdlcRecvEscPending is appropriately set or cleared in iFlags
+*/
+	{
+	// See whether there's still more data in the incoming buffer
+	while (ptr<end)
+		{
+		// Check for flag sequence and silently discard the frame if it is
+		if ((*ptr)==KPppHdlcFlagChar)
+			{
+			LOG( Logger().Printf(_L("PPP: HDLC: Bad PPP frame - flag char received directly after escaping sequence")));
+			// Delete the iRecvMBuf
+			DoBadRecv();
+			return ETrue;
+			}
+
+		// If the character is not in the receive ACCM, it's good--stop searching
+		if (!IsInRecvEscMap(*ptr))
+			{
+			break;
+			}
+
+		++ptr;
+		}
+		
+	if (ptr >= end)
+		{
+		// We haven't got the data yet, so set the flag and sort it out next time DoRecv() is called
+		iFlags |= KPppHdlcRecvEscPending;
+		}
+
+	else {
+		// Successfully processing the escaped character
+		iFlags &= ~KPppHdlcRecvEscPending;
+
+		// Decode the escaped char and copy it across
+		if (DecodeChar(mptr, mend, (TUint8)(*ptr++ ^ KPppHdlcEscBit)))
+			{
+			return ETrue;	// error
+			}
+		}
+	return EFalse;
+	}
+
+
+TBool CPppHdlcLink::AppendRecvMbuf(TUint8*& aMptr, TUint8*& aMend)
+/**
+Appends the current Mbuf to the packet and allocate a new one to replace it.
+
+@param aMptr Returns a pointer to the beginning of the new buffer
+@param aMend Returns a pointer to the end of the new buffer
+
+@return ETrue when a new buffer could not be allocated
+*/
+	{
+	// Queue this MBuf
+	if (iRecvMBuf != NULL)
+		{
+		iRecvPkt.Append(iRecvMBuf);
+		}
+
+	// Allocate a new one
+	TRAPD(err, iRecvMBuf = RMBuf::AllocL());
+	if (err!=KErrNone)
+		{
+		// Nuke the whole packet if we fail to allocate a new MBuf
+		iRecvMBuf = NULL;
+		iRecvPkt.Free();
+		LOG( iPppLcp->iLogger->Printf(_L("PPP: couldn't allocate MBuf")));
+		return ETrue;
+		}
+	aMptr = iRecvMBuf->Ptr();
+	aMend = iRecvMBuf->EndPtr();
+	__ASSERT_DEBUG((aMend - aMptr) == iRecvMBuf->Size(), User::Panic(_L("PPP Panic"), 0));
+	return EFalse;
+	}
+	
+void CPppHdlcLink::DoBadRecv()
+/**
+Adds a special packet to the receive queue with the meaning that
+a bad PPP frame was received. Any data in the current MBuf is
+used as the data portion of the special packet (and ignored).
+*/
+	{
+	// Need at least one MBuf in the packet to create an info header.
+	// Use the current MBuf if available, or create a new one if not.
+	if (iRecvPkt.IsEmpty())
+		{
+		TUint8* ptr, * end;
+		if (AppendRecvMbuf(ptr, end))
+			{
+			return;	// error has been logged; exit
+			}
+		iRecvMBuf->SetLength(0);
+		
+		if (iRecvPkt.IsEmpty())
+			{
+			// There wasn't an MBuf available a moment ago;
+			// there is now...
+			if (AppendRecvMbuf(ptr, end))
+				{
+				return;	// error has been logged; exit
+				}
+			iRecvMBuf->SetLength(0);
+			}
+		}
+	else if (iRecvMBuf != NULL)
+		{
+		// The MBuf waiting for use may be partially filled.  Since this is
+		// DoBadRecv() we know it contains garbage, so clear it out so it's
+		// ready for reuse the next time through DoRecv().
+		iRecvMBuf->SetLength(0);
+		}
+
+	RMBufPacket pkt;
+	RMBufPktInfo* info = NULL;
+	TRAPD(err, info=pkt.CreateL(iRecvPkt, 0);)
+	if(err!=KErrNone)
+		{
+		// We can't allocate an info header, so log a message and give up.
+		LOG( iPppLcp->iLogger->Printf(_L("PPP: couldn't allocate packet")));
+		return;
+		}
+	TPppAddr::Cast(info->iDstAddr).SetCRCError(ETrue);
+	pkt.Pack();
+	iRecvQ.Append(pkt);
+	}
+
+void CPppHdlcLink::DoRecv()
+/**
+Comm Read has completed - decodes and passes to LCP.
+
+We're copying the frame from the iRecvBuf into a series of mbufs - iRecvMBuf -
+which are chained together into a RMBufPacket, which is then
+added onto the iRecvQ 
+
+@see DoBadRecv()
+*/
+	{
+	// This buffer contains one frame, as we instructed the COMM port to
+	// stop reading when it encounters a KPppHdlcFlagChar
+	TUint8* ptr = (TUint8*)iRecvBuf.Ptr();
+	TUint8* end = ptr+iRecvBuf.Length();
+	TUint8* mptr = NULL;
+	TUint8* mend = NULL;
+
+	LOG( if (iLogLevel>=5) iPppLcp->iLogger->HexDump(_S("COMM Recv    "), _S("    "), ptr, end-ptr); )
+	LOG( if (iLogFormat == EpppDumpLogFormat) iPppLcp->iLogger->DumpFrame(EpppDirectionReceive,iRecvBuf); )
+
+	if (iRecvMBuf==NULL)
+		{
+		// Allocate a new MBuf
+		if (AppendRecvMbuf(mptr, mend))
+			{
+			return;	// error has been logged; exit
+			}
+		}
+	else
+		{
+		// Append to the existing MBuf
+		// Set the buffer length to the maximum to make room for received data.
+		// The correct length is set at the end of this method.
+		mptr = iRecvMBuf->EndPtr();
+		iRecvMBuf->SetLength(iRecvMBuf->Size());
+		mend = iRecvMBuf->EndPtr();
+		__ASSERT_DEBUG(mptr < mend, User::Panic(_L("PPP Panic"), 0));
+		}
+	
+	// Deal with pending escape sequence - this happens when part of a frame received, and it ends with an
+ 	// escaping sequence, but the char that is being escaped will be the first char in the next block of data
+ 	// to be received.
+	if (iFlags & KPppHdlcRecvEscPending)
+		{
+		if (UnescapeChar(ptr,end,mptr,mend))
+			{
+			return;	// error has been logged; exit
+			}
+		}
+	
+	while (ptr<end)
+		{
+		TUint8 ch = *ptr++;
+
+		// If this is the beginning or end of a frame...
+		if (ch==KPppHdlcFlagChar)
+			{
+			if (mptr!=iRecvMBuf->Ptr())
+				{
+				// If we have a partially full MBuf, set the length appropriately
+	 			// and stick it onto the receive queue for LCP
+				iRecvMBuf->SetLength(mptr - iRecvMBuf->Ptr());
+				if (AppendRecvMbuf(mptr, mend))
+					{
+					return;	// error has been logged; exit
+					}
+				}
+
+			// This is the end of the frame - so check and remove CRC
+			if (!iRecvPkt.IsEmpty())
+				{
+				// End of frame - check and remove CRC
+				TBool crcok = ETrue;
+
+				// Check the CRC for 16-bit FCS
+				if (iFlags & KPppHdlcRecvFcs16)
+					{
+					crcok = iRecvFcs16.IsGood();
+
+					// Initialize the CRC for a new packet
+					iRecvFcs16.Init();
+
+					RMBuf* m = iRecvPkt.Last();
+
+					// Attempt to remove 16 bit CRC
+					// adjend is not being used until after passing to a Decompressor
+					// hence decompressors were getting the CRC that should have been removed.
+					// 
+
+					// Replacement code 
+					if (m->Length()>2)
+						{
+						// Remove the CRC; must be at least one data byte in the last MBuf
+
+						m->AdjustEnd(-2);
+						}
+					else
+						// Special case where the CRC is the last byte/s in the buffer
+						// There is no way to remove just the last MBuf in the queue,
+						// so we must copy each MBuf except the last into a brand new
+						// RMBufQ, then copy them all back into the original one.
+						//
+						// TODO: See if it's possible to just set the length of the
+						// last MBuf to 0 and avoid all the copying.
+						{
+						// Get the odd length 0 -> 2
+						TInt oddLen = m->Length();
+						// Set up a temporary MBuf Queue and mbuf pointer
+						RMBufQ tmpQ;
+						tmpQ.Init();
+						RMBuf* tmpBuf;
+						// Loop removing all mbufs from the member queue
+						while((tmpBuf = iRecvPkt.Remove()) != NULL)
+							{
+							// Is it the last one? m points to last
+							if(tmpBuf == m)
+								// It is, so free it as it's only got CRC bytes
+								m->Free();
+							else
+								// put it on the temporary queue
+								tmpQ.Append(tmpBuf);
+							}
+						// Loop replacing the mbufs on the member queue
+						// NB : The last one was FREE'd in the previous loop
+						while((tmpBuf = tmpQ.Remove()) != NULL)
+							{
+							iRecvPkt.Append(tmpBuf);
+							}
+						m = iRecvPkt.Last();
+						if(m==NULL)
+							{
+							LOG( iPppLcp->iLogger->Printf(_L("PPP: HDLC: empty packet after CRC removal")));
+							return; // invalid frame has been discarded
+							}
+						// Subtract the CRC len from the NEW last buffer (Could be 0 -> 2)
+						m->AdjustEnd(oddLen - 2);
+						}
+					
+					}
+
+#ifdef __LCP_EXTENSION_FCS_32
+				// or calculate the CRC code for 32-bit FCSs
+                // TODO This feature is not tested
+				if (iFlags & KPppHdlcRecvFcs32)
+					{
+					crcok = iRecvFcs32.IsGood();
+
+					// Initialize the CRC for a new packet
+					if (iFlags & KPppHdlcRecvFcs32)
+						{
+						iRecvFcs32.Init();
+						}
+
+					// This doesn't handle the case of a part of the CRC sitting
+					// in a separate mbuf, which the CRC-16 code above handles.
+					// There are likely other problems with switching from
+					// 16 to 32 bit CRCs, and with sending 48 bits of CRCs
+					// (see RFC 1570).  --danfa
+					RMBuf* m = iRecvPkt.Last();
+					if (m->Length()>4)
+						m->AdjustEnd(-4);
+					else
+						adjend = 4;
+					}
+#endif
+				// We've calculated the CRC now, so continue processing
+				if (!crcok)
+					{
+					// The CRC is bad; queue a metapacket indicating so
+					LOG( iPppLcp->iLogger->Printf(_L("PPP: HDLC: Silently discarding frame due to bad CRC")));
+		 			DoBadRecv();
+		 			return;
+					}
+
+				// The CRC's OK, so sort out the other HDLC framing...
+				else
+					{
+					// Remove link header
+					RMBuf* m = iRecvPkt.First();
+
+					// Check that we have a valid frame length (<2 after FCS removal invalid according to 
+ 					// RFC1662)
+ 					// If not, silently discard the frame
+ 					if (m->Length() < 2)
+ 						{
+ 						LOG( iPppLcp->iLogger->Printf(_L("PPP: HDLC: Silently discarding frame due to bad length (<2 bytes after FCS removed)")));
+						DoBadRecv();
+						return;
+ 						}
+ 
+					TUint8* p = m->Ptr();
+					TUint addr=0, ctrl=0;
+ 					// Check to see if we've got address and control field compression active
+					if (iFlags & KPppHdlcRecvCompAddrCtrl)
+						{
+						// If we have, but they're still present anyway, adjust the start pointers appropriately
+						if (p[0]==KPppHdlcAddrByte && p[1]==KPppHdlcCtrlUIByte)
+							p += 2;
+						addr = KPppHdlcAddrByte;
+						ctrl = KPppHdlcCtrlUIByte;
+						}
+					else
+						{
+						// Get the address from the frame (RFC1662 - 1 byte - extensions may be defined later,
+ 						// and then we'll have to change this code)
+ 						addr = *p++;
+ 
+ 						// Check that the address wasn't even (all addresses must be odd - RFC1662)
+ 						//
+ 						// For full RFC compliance, we should == with 0xff here...
+ 						if (!(addr&0x01)) 
+ 							{
+ 							// Discard the frame
+ 							LOG( iPppLcp->iLogger->Printf(_L("PPP: HDLC: Silently discarding frame due to bad address field")));
+							DoBadRecv();
+ 							return;
+ 							}
+
+						// Get control byte from the frame (RFC1662 - 1 byte - extensions may be defined later,
+ 						// and then we'll have to change this code)
+ 						ctrl = *p++;
+ 						
+ 						// Check that the control field wasn't even (all control fields must be odd - RFC1662)
+ 						// For full RFC compliance, we should == with KPppHdlcCtrlUIByte here...
+ 						if (!(ctrl&0x01)) 
+ 							{
+ 							// Discard the frame
+ 							LOG( iPppLcp->iLogger->Printf(_L("PPP: HDLC: Silently discarding frame due to bad control field")));
+ 							DoBadRecv();
+ 							return;
+ 							}
+ 						}
+
+					// Extract the protocol information
+					TUint prot = DecodeProtocolID(p);
+
+					// Ensure that the header we just parsed doesn't extend beyond the end of the frame.
+					TInt hdrlen = p - m->Ptr();
+					if (hdrlen <= m->Length())
+						{
+						// Adjust start point to compensate for removal of address, control and protocol fields
+						m->AdjustStart(hdrlen);
+						}
+					else
+						{
+						// Catch-all safety check... looks like we've been passed
+						// a badly-framed packet.
+						LOG( iPppLcp->iLogger->Printf(_L("PPP: HDLC: Discarding badly framed packet")));
+						DoBadRecv();
+ 						return;
+						}
+
+					// Check to see if we're running any compression
+					if (prot == KPppIdCompressed)
+						{
+						// If we are, try and decompress the frame
+						if (iPppDecompressor)
+							{
+							if (iPppDecompressor->Decompress(iRecvPkt))
+								{
+								m = iRecvPkt.First();
+								p = m->Ptr();
+								// Decompress OK so...
+								// extract the protocol information
+								prot = DecodeProtocolID(p);
+								
+								LOG(iPppLcp->iLogger->Printf(_L("Frame Prot %x"),prot);)
+								m->AdjustStart(p-m->Ptr());
+								}
+							else
+								{
+								// Otherwise we couldn't decompress it, so throw away the frame
+ 								LOG( iPppLcp->iLogger->Printf(_L("PPP: HDLC: Could not decode compressed packet - discarding")));
+								DoBadRecv();
+								return;
+								}
+							}
+						}
+					
+					// Prepend and fill in info header and deliver to LCP
+					RMBufPacket pkt;
+					RMBufPktInfo* info = NULL;
+					TRAPD(err, info=pkt.CreateL(iRecvPkt, 0);)
+					if(err!=KErrNone)
+						{
+						// This is assumed to mean that we're OOM, but there might be other situations...
+						LOG( iPppLcp->iLogger->Printf(_L("PPP: HDLC: Could not create info packet error %d"), err));
+						//__DEBUGGER();
+						return;
+						}
+
+					TPppAddr::Cast(info->iDstAddr).SetAddress(addr);
+					TPppAddr::Cast(info->iDstAddr).SetControl(ctrl);
+					TPppAddr::Cast(info->iDstAddr).SetProtocol(prot);
+
+					TPppAddr::Cast(info->iDstAddr).SetCRCError(EFalse);
+
+					pkt.Pack();
+					iRecvQ.Append(pkt);
+					}
+				}
+			}
+			
+		// Check for escaping sequence
+		else if (ch==KPppHdlcEscChar)
+			{
+			if (UnescapeChar(ptr,end,mptr,mend))
+				{
+				return;	// error has been logged; exit
+				}
+			}
+		// Otherwise copy the char across to the iRecvMBuf - *** most executed case ***
+		else
+			{
+			// If the character is in the receive ACCM, it's bogus--drop it on the floor.
+			// Otherwise, XON/XOFF from the local DCE would be erroneously included in the packet.
+			if (!IsInRecvEscMap(ch) &&
+				DecodeChar(mptr, mend, ch))
+				{
+				return;	// error has been logged; exit
+				}
+			}
+		}
+		
+	iRecvMBuf->SetLength(mptr - iRecvMBuf->Ptr());
+	__ASSERT_DEBUG(mptr < mend, User::Panic(_L("PPP Panic"), 0));
+	}
+
+
+void CPppHdlcLink::BcaReadComplete(TInt aStatus)
+/**
+Comm Read has completed - decodes and passes to LCP
+
+@param aStatus Error code from CommRead()
+*/
+	{
+	switch (aStatus)
+		{
+	case KErrCommsOverrun:
+	case KErrCommsFrame:
+	case KErrCommsParity:
+	case KErrNone:
+		{
+		if (aStatus == KErrNone)
+			{
+			// Unframe and queue the received packet
+			DoRecv();
+			}
+		else
+			{
+			// Queue an error packet
+			LOG( Logger().Printf(_L("CPppHdlcLink::BcaReadComplete: error %d. Doing Bad Receive."), aStatus); )
+			DoBadRecv();
+			}
+			
+		if (iState == EPppHdlcOpen)
+			{
+			iRecvBuf.SetMax();
+			// Initiate another asynchronous read
+			iBcaReader->Read(iRecvBuf);
+			}
+
+		// Pass any received packets up the protocol chain
+		RMBufChain pkt;
+		while (iRecvQ.Remove(pkt))
+			{
+			// Parse and dump the PPP frame to the log
+			LOG( if(iLogLevel>=4)  iPppLcp->iLogger->Dump(pkt, KPppHdlcRecvChannel); )
+			LogUserData(pkt,KPppHdlcRecvChannel);	// MS
+			DeliverToLcp(pkt);
+			}
+		}
+		break;
+
+	case KErrCommsLineFail: // Usually means that the serial link was broken. (DCD/DTR down)
+	default:
+		LOG( Logger().Printf(_L("CPppHdlcLink::BcaReadComplete: Read Error %d"), aStatus); )
+			
+		LinkDown(aStatus);
+		break;
+		}
+	}
+
+
+/**
+Sends more data (if the link is still open).
+Called when CBcaWriter::Write() has completed.
+
+@param aStatus (ignored)
+*/
+void CPppHdlcLink::BcaWriteComplete(TInt /*aStatus*/)
+	{
+	iFlags &= ~KPppHdlcSendBusy;
+	//if (aStatus!=KErrNone)
+	//	{
+	//	LinkDown(aStatus);
+	//	return;
+	//	}
+	if (iState == EPppHdlcOpen)
+		{
+		DoSend(EFalse);
+		}		
+	}
+
+TBool CPppHdlcLink::DecodeChar(TUint8*& aPtr, TUint8*& aPtrEnd, TUint8 aChar)
+/**
+Copies an unescaped character into a receive buffer and updates the CRC at the
+same time. Allocates a new buffer if the current one fills up.
+
+@param aPtr Pointer to buffer; on return, points one past the last location used
+@param aPtrEnd Pointer to one past the end of buffer (must be > aPtr on entry)
+@param aChar Unescaped character
+
+@return ETrue when a new buffer could not be allocated
+*/
+	{
+	__ASSERT_DEBUG(aPtr < aPtrEnd, User::Panic(_L("PPP Panic"), 0));
+	*aPtr++ = aChar;
+	if (iFlags & KPppHdlcRecvFcs16)
+		{
+		iRecvFcs16.CalcByte(aChar);
+		}
+#ifdef __LCP_EXTENSION_FCS_32
+	if (iFlags & KPppHdlcRecvFcs32)
+		{
+		iRecvFcs32.CalcByte(aChar);
+		}
+#endif
+	if (aPtr>=aPtrEnd)
+		{
+		// Queue this MBuf and allocate another
+		return AppendRecvMbuf(aPtr, aPtrEnd);
+		}
+	return EFalse;
+	}
+
+void CPppHdlcLink::EncodeChar(TUint8*& aPtr, TUint8 aChar)
+/**
+Copies a character into a transmit buffer and escapes if necessary.
+Updates the CRC at the same time.
+
+@param aPtr Pointer to buffer; on return, points one past the last location used
+@param aChar Character to encode
+*/
+	{
+	if (IsEscapedChar(aChar))
+		{
+		*aPtr++=KPppHdlcEscChar;
+		*aPtr++=(TUint8)(aChar^KPppHdlcEscBit);
+		}
+	else
+		*aPtr++=aChar;
+
+	if (iFlags & KPppHdlcSendFcs16)
+		{
+		iSendFcs16.CalcByte(aChar);
+		}
+#ifdef __LCP_EXTENSION_FCS_32
+	if (iFlags & KPppHdlcSendFcs32)
+		{
+		iSendFcs32.CalcByte(aChar);
+		}
+#endif
+	}
+
+void CPppHdlcLink::CreateHdlcHeader(TUint8*& aPtr, TBool aRestart, TUint16 aProt)
+/**
+Creates and encodes an HDLC header into the output buffer and updates the CRC.
+There must be sufficient space available in the output buffer.
+
+@param aPtr Pointer into buffer (must have at least 9 bytes free);
+       on return, points one past the last location used
+@param aRestart EFalse if this frame immediately follows a previous frame
+@param aProt PPP protocol ID
+*/
+	{
+	// Only send flag byte if not immediately following previous frame
+	if (aRestart)
+		*aPtr++ = KPppHdlcFlagChar;
+
+	// never apply to LCP
+	if (aProt==KPppIdLcp || !(iFlags & KPppHdlcSendCompAddrCtrl))
+		{
+		EncodeChar(aPtr, KPppHdlcAddrByte);
+		EncodeChar(aPtr, KPppHdlcCtrlUIByte);
+		}
+
+	// never apply to LCP (LCP > 255 anyway!)
+	if (aProt>0xff || !(iFlags & KPppHdlcSendCompProt))
+		EncodeChar(aPtr, (TUint8)(aProt>>8));
+	EncodeChar(aPtr, (TUint8)(aProt&0xff));
+	}		
+
+TInt CPppHdlcLink::Send(RMBufChain& aPacket, TUint aPppId/*=KPppIdAsIs*/)
+/**
+Queues outgoing packet and ensures writer is active.
+If PPP compression is enabled, the compresses the frame first then reinserts
+it into the queue.
+
+@param aPacket MBuf chain containing packet
+@param aPppId PPP protocol number
+
+@return KErrNoMemory when out of memory, or 1 if packet was queued,
+        or KErrNone if queue is above high water mark or HDLC is not open
+*/
+	{
+	RMBufPktInfo* info = RMBufPacket::PeekInfoInChain(aPacket);
+	TPppAddr::Cast(info->iDstAddr).SetAddress(KPppHdlcAddrByte);
+	TPppAddr::Cast(info->iDstAddr).SetControl(KPppHdlcCtrlUIByte);
+
+	if ((aPppId >= 0x0021) &&
+		(aPppId <= 0x00fa))
+		{
+		if (iPppCompressor)
+			{
+			// Remove the Info Header before compressing
+			RMBuf* Temp = aPacket.Remove();
+
+			//__DEBUGGER();
+			TPppCompressReturnValue ret = iPppCompressor->Compress(aPacket, (TUint16)aPppId);
+			
+			if (ret == EPppCompressedOK)
+				{
+				// Set packet length to the new, compressed length
+				info->iLength = aPacket.Length();
+				
+				aPacket.Prepend(Temp);
+				aPppId = KPppIdCompressed;
+				}
+
+			else if(ret ==EPppCompressedFrameExpanded)
+				{
+				// The frame expanded during compression; send original frame
+				aPacket.Prepend(Temp);
+				}
+			
+			else //(ret == EPppCompressedNotOK)
+				{
+				// Throw everything away
+				//__DEBUGGER();
+				aPacket.Free();
+				Temp->Free();
+				return KErrNoMemory;
+				}			
+			}
+
+		
+/*++		
+		if (iPppCompressor)
+			{
+			RMBuf* Temp;
+
+			Temp = aPacket.Remove();
+
+			//
+			// Remove the Info Header
+			//
+			//__DEBUGGER();
+			if (iPppCompressor->Compress(aPacket, (TUint16)aPppId))
+				{
+				//
+				// Set the length to the new length
+				//
+				info->iLength = aPacket.Length();
+				
+				}
+			else
+				{
+				//
+				// Throw everything away
+				//
+				//__DEBUGGER();
+				aPacket.Free();
+				Temp->Free();
+				return KErrNoMemory;
+				}
+			aPacket.Prepend(Temp);
+			aPppId = KPppIdCompressed;
+			}
+--*/
+
+		}
+
+	if (aPppId!=KPppIdAsIs && aPppId!=KPppIdUnknown && aPppId!=KPppIdCopyAll)
+		{
+		TPppAddr::Cast(info->iDstAddr).SetProtocol(aPppId);
+		}
+	iSendNumBufs += aPacket.NumBufs();
+	iSendQ.Append(aPacket);
+
+	switch (iState)
+		{
+	case EPppHdlcConnecting:
+		break;
+	case EPppHdlcOpen:
+		if (iSendFlowOn && iSendNumBufs>=iSendHiWat)
+			iSendFlowOn = EFalse;
+		if (!(iFlags & KPppHdlcSendBusy))
+			DoSend(ETrue);
+		return iSendFlowOn ? 1 : 0;
+	case EPppHdlcClosed:
+		{
+		 TRAPD(err, OpenL());
+		 if (KErrNone != err) 
+			{
+			LOG( iPppLcp->iLogger->Printf(_L("PPP Link Open Failure with error %d"), err); )
+			}
+		}
+		break;
+	default:
+		break;
+		}
+	return 0;
+	}
+
+void CPppHdlcLink::DoSend(TBool aRestart)
+/**
+Writes more data to the comm port.
+The send buffer is filled with as much of the encoded PPP frame as will
+fit, then is transmitted to the comm port. The next call will send the
+remaining data (if any), or start with the next PPP frame from the queue.
+
+@param aRestart EFalse if this call immediately follows a previous frame
+*/
+	{
+	__ASSERT_DEBUG(!(iFlags & KPppHdlcSendBusy), User::Panic(_L("PPP Panic"), 0));
+
+	TUint8* ptr = (TUint8*)(iSendBuf.Ptr());
+	TUint8* end = ptr+(iSendBuf.MaxLength()-1);	// -1 allows safe encoding
+	
+	// Starting a new packet?
+	if (iSendPkt.IsEmpty())
+		{
+		// Get next packet from send queue
+		RMBufPacket pkt;
+		if (!iSendQ.Remove(pkt))
+			{
+			iSendNumBufs=0; // Must be so
+			return;
+			}
+
+		// Parse and dump the PPP frame to the log
+		LOG(if(iLogLevel>=4)  iPppLcp->iLogger->Dump(pkt, KPppHdlcSendChannel); )
+		LogUserData(pkt,KPppHdlcSendChannel);	//MS
+		
+		if (iFlags & KPppHdlcSendFcs16)
+			{
+			iSendFcs16.Init();
+			}
+#ifdef __LCP_EXTENSION_FCS_32
+		if (iFlags & KPppHdlcSendFcs32)
+			{
+			iSendFcs32.Init();
+			}
+#endif
+		
+/*		
+		// Check if have reached the point after which new
+		// link config options have to be applied
+		if (iFlags & KPppHdlcApplyPending)
+			{
+			if (pkt.First()==iApplyConfigMarker)
+				iFlags |= KPppHdlcAtApplyMark;
+			}
+*/		
+		RMBufPktInfo* info = pkt.Unpack();
+		TUint16 prot = (TUint16) TPppAddr::Cast(info->iDstAddr).GetProtocol();
+
+/*
+		// Apply new configuration if a non LCP frame
+		if (prot!=KPppIdLcp && (iFlags & KPppHdlcAtApplyMark))
+			DeferredApplyOptions();
+*/
+		// Apply new configuration if a non LCP frame
+		// Note: This might pose a problem if a defective Cisco router sends
+		// an ACP packet too early.
+		if (prot!=KPppIdLcp)
+			{
+			if (iFlags & KPppHdlcApplyPending)
+				DeferredApplyOptions();
+			}
+
+		// Create and encode the frame header
+		CreateHdlcHeader(ptr, aRestart, prot);
+		__ASSERT_DEBUG(ptr <= end, User::Panic(_L("PPP Panic"), 0));
+		
+		iSendPkt.Assign(pkt);
+		pkt.FreeInfo();
+		--iSendNumBufs;
+		}		
+
+	// Sending Packet data...
+	TInt ensure = KMBufSmallSize<<1; // worst-case is every byte is escaped
+	if (iFlags & KPppHdlcSendFcs16)
+		ensure += 4;	// enough space for 2 escaped bytes
+#ifdef __LCP_EXTENSION_FCS_32
+	if (iFlags & KPppHdlcSendFcs32)
+		ensure += 8;	// enough space for 4 escaped bytes
+#endif
+    // Encode all buffers in the chain (that will fit) into the send buffer
+	while ((ptr+ensure)<end)
+		{
+		RMBuf *m = iSendPkt.First();
+		TUint8* mptr = m->Ptr();
+		TUint8* mend = m->EndPtr();
+		// Encode frame data into send buffer & calculate CRC
+		while (mptr<mend)
+			{
+			EncodeChar(ptr, *mptr++);
+			}
+		__ASSERT_DEBUG(ptr <= end, User::Panic(_L("PPP Panic"), 0));
+		iSendPkt.Remove();
+		m->Free();
+		--iSendNumBufs;
+		if (!iSendFlowOn && iSendNumBufs<iSendLoWat)
+			{
+			// The backlog of buffers is clearing, so allow more to come
+			iSendFlowOn = ETrue;
+			iPppLcp->LinkFlowOn();
+			}
+		if (iSendPkt.IsEmpty())
+			break;
+		}
+
+    // If the mbuf chain is empty, we've encoded the entire packet, so end it
+	// by appending the CRC and flag character to the send buffer.
+	// We left ourselves enough space in the buffer for this in the loop above.
+	if (iSendPkt.IsEmpty())
+		{
+		if (iFlags & KPppHdlcSendFcs16)
+			{
+			TUint16 fcs = iSendFcs16.Fcs();	// save FCS before it changes
+			EncodeChar(ptr, (TUint8)(fcs & 0xff));
+			EncodeChar(ptr, (TUint8)(fcs >> 8));
+			}
+#ifdef __LCP_EXTENSION_FCS_32
+		if (iFlags & KPppHdlcSendFcs32)
+			{
+			TUint32 fcs = iSendFcs32.Fcs();	// save FCS before it changes
+			EncodeChar(ptr, (TUint8)(fcs & 0xff));
+			EncodeChar(ptr, (TUint8)((fcs>>8) & 0xff));
+			EncodeChar(ptr, (TUint8)((fcs>>16) & 0xff));
+			EncodeChar(ptr, (TUint8)((fcs>>24) & 0xff));
+			}
+#endif
+		// Terminating flag byte
+		*ptr++ = KPppHdlcFlagChar;
+		__ASSERT_DEBUG(ptr <= end, User::Panic(_L("PPP Panic"), 0));
+		}
+	iSendBuf.SetLength(ptr - iSendBuf.Ptr());
+
+	// Log after HDLC framing and escaping
+	// DoSend() might be called several times to send one (long) frame.
+	// This is supported by EpppDumpLogFormat but not necessarily by other formats.
+	LOG ( if (iLogLevel>=5) iPppLcp->iLogger->HexDump(_S("COMM Send    "), _S("    "), iSendBuf.Ptr(), iSendBuf.Length()); )
+	LOG ( if (iLogFormat == EpppDumpLogFormat) iPppLcp->iLogger->DumpFrame(EpppDirectionSend,iSendBuf); )
+
+	iFlags |= KPppHdlcSendBusy;	// indicate that we have an outstanding write
+	iBcaWriter->Write(iSendBuf);
+	}
+
+
+/**
+Configures the comm port for our needs.  Also initializes flags
+in preparation for receiving the first packet.
+
+*/
+void CPppHdlcLink::PacketModeOnL()
+	{
+	if (iFlags & KPppHdlcCommConfigOk) // Already configured.
+		{
+		return;
+		}
+
+	// Open the BCA channel now:
+	iBcaControl->StartStartupSequence(); // Next we are in CBcaControl::RunL	
+	}
+
+/** Saves original serial port configuration.
+
+@param aHdlcConfig the config to save */
+void CPppHdlcLink::SetOriginalSerialPortConfig(TCommConfig& aHdlcConfig)
+	{
+	if(iFlags & KPppHdlcOrigConfig) // We already have the original port settings
+		{
+		return; // What we have may not be original (could have been set by us)
+		// This can happen if the link is being restarted 
+		// It's easier to protect the settings from here, than from the BCA control
+		}
+	
+	iFlags |= KPppHdlcOrigConfig;
+	iOrigConfig = aHdlcConfig;
+	}
+	
+/** Configures the BCA channel serial parameters for use by HDLC.
+
+@param aHdlcConfig the desirable serial configuration.
+*/
+void CPppHdlcLink::GetSerialPortConfigForHdlc(TCommConfig& aHdlcConfig) const
+	{
+	LOG( iPppLcp->iLogger->Printf(_L("CPppHdlcLink::GetSerialPortConfigForHdlc()")); )
+
+	aHdlcConfig = iOrigConfig;
+
+	aHdlcConfig().iTerminatorCount = 1;
+	aHdlcConfig().iTerminator[0] = KPppHdlcFlagChar;
+
+	// if 7 + parity, correct to 8N (for ISPs such as CompuServe)
+	if ( aHdlcConfig().iDataBits == EData7 && aHdlcConfig().iParity != EParityNone )
+		{
+		aHdlcConfig().iDataBits = EData8;
+		aHdlcConfig().iParity   = EParityNone;
+		}
+
+	aHdlcConfig().iHandshake = GetHandShaking();
+	}
+
+/**
+Returns original serial configuration of the BCA channel
+
+@return the original serial port configuration.
+ */
+TCommConfig CPppHdlcLink::OriginalSerialPortConfig() const
+	{
+	return iOrigConfig;
+	}
+
+	
+void CPppHdlcLink::StartPPP()
+/** Brings up PPP link and read a packet.*/
+	{
+	InitEscMap();
+		
+	// Always expect bytes 0..31 to be escaped initially
+	iRecvEscMap = 0xffffffff;
+
+	iFlags = KPppHdlcSendFcs16 | KPppHdlcRecvFcs16;
+
+	iFlags |= KPppHdlcCommConfigOk;
+	
+	iState = EPppHdlcOpen;
+	iSendFlowOn = ETrue;
+	iPppLcp->LinkLayerUp();
+	
+	iRecvBuf = GetExcessData();
+    if(iRecvBuf.Length())
+		{
+		// Simulate a completed read in order to force processing of excess data received from the Agent.
+		iBcaReader->ReadReady();
+		}
+	else
+		{
+		iRecvBuf.SetMax();
+  		iBcaReader->Read(iRecvBuf);
+		} 
+	}
+
+/**
+Triggers BCA shutdown, if applicable. This restores the original Comm port config.
+Note: If the BCA was not opened, it will not be shut down. In this case, there is no
+need to restore original config, because config can be set on open BCA only.
+
+We can be called twice: Once because PPP shuts down the link, the second time when BCA
+reports that it has gone down. In this case, we guard against shutting down the BCA for 
+the second time, and report link down to LCP. 
+*/
+void CPppHdlcLink::PacketModeOff()
+	{
+	LOG( iPppLcp->iLogger->Printf(_L("CPppHdlcLink::PacketModeOff(): Releasing the link.")); )
+
+	iBcaReader->Cancel();
+	iBcaWriter->Cancel();
+	iBcaControl->Cancel();
+
+	if (iFlags & KPppHdlcCommConfigOk)
+		{
+		iFlags &= ~KPppHdlcCommConfigOk;
+		}
+			
+	if(iBcaControl->BcaIsOpen())
+		{
+		LOG( iPppLcp->iLogger->Printf(_L("CPppHdlcLink::PacketModeOff(): BCA channel is open. Shutting it down.")); )
+
+		iBcaControl->StartShutdownSequence(); // Next we are in CBcaControl::RunL
+		// which will call LinkTerminationComplete eventually.	
+		}
+	else // Nothing to shutdown, the link is finished now.
+		{
+		LinkTerminationComplete(); 
+		}		
+	}
+
+/**
+Called when the HDLC link termination is complete. 
+Releases the send buffer & notifies upper layers 
+
+This call MUST be made after HDLC is finished, else LCP may wait for link to report termination forever.
+*/
+void CPppHdlcLink::LinkTerminationComplete()
+	{
+	LOG( iPppLcp->iLogger->Printf(_L("CPppHdlcLink::LinkTerminationComplete: HDLC Link is finished, iLinkDown[%d]: 1 Notifying upper layer."), iLinkDown); )
+
+	iState = EPppHdlcClosed;
+	FreeBuf();
+	if(iLinkDown) // LCP can ask us not to notify it.
+		{
+		iPppLcp->LinkLayerDown(iError);		// This is essentially PPP 'Down' event on LCP
+		}
+	iLinkDown = ETrue; // reset to default
+	}
+
+/**
+Does the HDLC link require graceful shutdown (as opposed to an immediate close) of the BCA channel?
+
+@return ETrue if yes.
+*/ 
+TBool CPppHdlcLink::DoShutdownBcaGracefully() const
+	{
+	return KErrConnectionTerminated != iError;
+	}
+	
+
+/** Frees the send buffering resources of the BCA.*/
+void CPppHdlcLink::FreeBuf()
+	{
+	LOG( iPppLcp->iLogger->Printf(_L("CPppHdlcLink::FreeBuf()")); )
+	iSendPkt.Free();
+	iSendQ.Free();
+	iSendNumBufs=0;
+	}
+
+
+void CPppHdlcLink::LinkDown(TInt aStatus)
+/**
+Link down - drops out of packet mode and notifies with reason.
+
+@param aStatus Error code indicating the reason the link is going down
+*/
+	{
+	iError = (KErrNone == iError) ? aStatus : iError; // Try to get an actual error code instead of KErrNone
+	// We can stay with KErrNone if the link closure was requested by the upper layer. This is OK, the upper layer
+	// will take care of the error code.
+	PacketModeOff(); // Disconnect from the link. 
+	}
+
+void CPppHdlcLink::ReadIniFileL()
+/**
+Reads settings from the ini file.
+Default values are assumed if the file does not exist.
+
+@leave Error if the ini file is found but cannot be read
+*/
+	{
+	// Set up defaults in case they aren't set in the .ini file
+	iDesiredRecvEscMap = 0x00000000;
+	iPppLcp->SetMaxReceiveSize(0);
+
+	// Read the correct .ini file depending on whether we're configured for server mode
+	CESockIniData* ini = NULL;
+	TRAPD(res,
+		if (iPppLcp->PppLinkMode() == CPppLcpConfig::EPppLinkIsServer)
+			{
+			ini = CESockIniData::NewL(PPP_SERVER_INI_FILE);
+			}
+		else
+			{
+			ini = CESockIniData::NewL(PPP_INI_FILE);
+			}
+		)
+	if(res!=KErrNone)
+		{
+		if(res==KErrNotFound)
+			{
+			return;
+			}
+		User::Leave(res);
+		}
+
+	CleanupStack::PushL(ini);
+
+	// Each port can have its own high and low watermark settings by putting
+	// them in a section like [link::comm::0]
+    TPortName port(iPppLcp->GetBCAProvision()->GetPortName());
+	_LIT(KLink, "link");
+	TName name(KLink);
+	port.LowerCase();
+	name.AppendFormat(_L("::%S"), &port);
+	
+	TInt lo, hi;
+   _LIT(KLowMark, "LoMark");
+   _LIT(KHighMark, "HiMark");
+
+	if (ini->FindVar(name, KLowMark, lo) && ini->FindVar(name, KHighMark, hi))
+		{
+		iSendLoWat = lo;
+		iSendHiWat = hi;
+		}
+	else if (ini->FindVar(KLink, KLowMark, lo) && ini->FindVar(KLink, KHighMark, hi))
+		{
+		iSendLoWat = lo;
+		iSendHiWat = hi;
+		}
+
+#if defined (_DEBUG) 
+//Log level
+//
+//Options are:
+//n=0 Finite State Machine transition are logged.
+//n=1 Reserved for future use.
+//n=2 Reserved for future use.
+//n=3 Reserved for future use.
+//n=4 Log parsed PPP packets *SLOW* (superseded by TcpLog.log).
+//n=5 HexDump of PPP packets *VERY SLOW* (superseded by TcpLog.log).
+//
+//		A new log is generated if the \logs\tcpdump folder is created.
+//		TcpDump.log is a log created in a binary format,
+//		hence non readable with a text editor.
+//		It can be used as an input file for Ethereal..
+//	Ethereal is a general purpose packet analyser, that can parse a wide variety of protocols.
+//	It is freeware and can be fetched from: www.ethereal.com.
+	_LIT(KLogSection, "log");
+	_LIT(KLogLevel, "level");
+	TInt logLevel;
+	if (ini->FindVar(KLogSection, KLogLevel, logLevel) && (logLevel>=0 ))
+		{
+		iLogLevel = logLevel;
+		}
+	else
+		{
+		iLogLevel = 1;
+		}
+
+//Log File Format
+//
+//Options are:
+//n=0 PPPdump format
+   _LIT(KlogFormat, "logFormat");
+	ini->FindVar(KLogSection, KlogFormat, iLogFormat);
+	if(iLogFormat<0 || iLogFormat>=ElastLogFormat)
+		{
+		iLogFormat = EpppDumpLogFormat;
+		}
+
+//
+//Link format
+//
+//Options are:
+//n=0 Entire PPP frame is logged (for debugging - incompatible with tcptrace)
+//n=1 Only IP frames are logged (for performance analysis with tcptrace)
+   _LIT(KlogLinkFormat, "linkFormat");
+	ini->FindVar(KLogSection, KlogLinkFormat, iLogLinkFormat);
+	if(iLogLinkFormat<0 || iLogLinkFormat>=ElastLogLinkFormat)
+		{
+		iLogLinkFormat = EpppLogLinkFormat;
+		}
+#endif
+
+//
+	// Added for ACCM configuration
+	// These two strings are the ones in ppp.ini :-
+	// The example below would enable accm config and negate the need to escape values 4 - 7 inclusive
+	// [link]
+	// PPPEnableAccm= 1
+	// PPPAccm= FFFFFF0F
+	// XON and XOFF characters are added no matter what
+	_LIT(KEnableACCM, "PPPEnableAccm");
+	_LIT(KACCMValue, "PPPAccm");
+	TInt enable;
+	// Check ACCM configuration is enabled first
+	if (ini->FindVar(KLink,KEnableACCM, enable) && enable != 0)
+		{
+		TPtrC hexStr;
+		// We are enabled, read the 8 Digit ASCII HEX value
+		if (ini->FindVar(KLink,KACCMValue,hexStr))
+			{
+			// Convert it to a 32 bit integer
+			TLex lex(hexStr);
+			lex.Val(iDesiredRecvEscMap,EHex);
+			}
+		}
+
+//
+	// Added for MRU configuration
+	// These two strings are the ones in ppp.ini :-
+	// [link]
+	// PPPEnableMru= 1
+	// PPPMru= 1500
+	_LIT(KEnableMRU, "PPPEnableMru");
+	_LIT(KMRUValue, "PPPMru");
+	if (ini->FindVar(KLink,KEnableMRU, enable) && enable != 0)
+		{
+		TInt mru;
+		// We are enabled, read a 32 Bit value
+		if (ini->FindVar(KLink,KMRUValue,mru))
+			{
+			iPppLcp->SetMaxReceiveSize(mru);
+			}
+		}
+//
+
+	CleanupStack::PopAndDestroy();
+	}
+
+
+void CPppHdlcLink::StartL()
+/**
+Starts the PPP link.
+
+@leave Error if comm port cannot be initialized
+*/
+	{
+	LOG( iPppLcp->iLogger->Printf(_L("PPP Link StartL")); )
+    LOG( iPppLcp->iLogger->DumpFrameFileHeader(iLogFormat, iLogLinkFormat); );
+
+	ReadIniFileL();
+
+	// Configure the Link & BCA.
+	PacketModeOnL();
+	}
+	
+
+void CPppHdlcLink::Stop(TInt aReason, TBool aLinkDown)
+/**
+Shuts down the PPP link.
+
+@param aReason Error code indicating the reason the link has gone down
+@param aLinkDown ETrue if the link is actually going down,
+or EFalse if it is just being reset
+*/
+	{
+	LOG( iPppLcp->iLogger->Printf(_L("CPppHdlcLink::Stop: Stopping HDLC Link.")); )
+	iError = aReason;
+	iLinkDown = aLinkDown;
+	PacketModeOff(); 
+	}
+
+TInt CPppHdlcLink::SpeedMetric()
+/**
+Determines the speed of the communications port.
+
+@pre PacketModeOnL() must have been called
+
+@return Speed in bps or 0 if unknown
+*/
+	{
+	switch (iOrigConfig().iRate)
+		{
+	case EBps50:
+		return 50;
+	case EBps75:
+		return 75;
+	case EBps110:
+		return 110;
+	case EBps134:
+		return 134;
+	case EBps150:
+		return 150;
+	case EBps300:
+		return 300;
+	case EBps600:
+		return 600;
+	case EBps1200:
+		return 1200;
+	case EBps1800:
+		return 1800;
+	case EBps2000:
+		return 2000;
+	case EBps2400:
+		return 2400;
+	case EBps3600:
+		return 3600;
+	case EBps4800:
+		return 4800;
+	case EBps7200:
+		return 7200;
+	case EBps9600:
+		return 9600;
+	case EBps19200:
+		return 19200;
+	case EBps38400:
+		return 38400;
+	case EBps57600:
+		return 57600;
+	case EBps115200:
+		return 115200;
+	case EBps230400:
+		return 230400;
+	case EBps460800:
+		return 460800;
+	case EBps576000:
+		return 576000;
+	case EBps1152000:
+		return 1152000;
+	case EBps4000000:
+		return 4000000;
+	case EBpsSpecial:
+		return iOrigConfig().iSpecialRate;
+	default:
+		;	// unknown speed
+		}
+	return 0;
+	}
+
+//=======================================
+// JGG PPP CHANGE
+void CPppHdlcLink::GetDataTransfer(RPacketContext::TDataVolume& aData)
+//void CPppHdlcLink::GetDataTransfer(RGprsContext::TDataVolume& aData)
+//=======================================
+/**
+Returns the current total amount of IP data transferred.
+
+@param aData Receives the data count
+*/
+	{
+	aData.iBytesSent = iSentData;
+	aData.iOverflowCounterSent = 0;
+	aData.iBytesReceived = iRecvdData;
+	aData.iOverflowCounterReceived = 0;
+	}
+
+void CPppHdlcLink::LogUserData(RMBufChain& aPacket, TInt aChannel)
+/**
+Updates the current total of PPP payload bytes transferred.
+Separate counts are maintained for bytes received and transmitted.
+
+@param aPacket MBuf chain containing packet
+@param aChannel KPppHdlcRecvChannel for the receiving channel
+or KPppHdlcSendChannel for the sending channel
+
+@see GetDataTransfer()
+*/
+	{
+	RMBufPktInfo* info = RMBufPacket::PeekInfoInChain(aPacket);
+	const TInt len = info->iLength;
+    const TBool CrcError = TPppAddr::Cast(info->iDstAddr).CRCError();
+    if (CrcError)
+    	{
+    	// This is a metapacket containing no useful payload
+    	}
+	else if (aChannel==KPppHdlcSendChannel)
+		{
+		iSentData += len;
+		}
+	else if (aChannel==KPppHdlcRecvChannel)
+		{
+		iRecvdData += len;
+		}
+	}
+
+/**
+ * Constructor. Performs standard active object initialisation.
+ *
+ * @param aObserver Reference to the observer of this PPP HdlcLink.
+ * 
+ */
+CBcaControl::CBcaControl(CPppHdlcLink& aUser, MBca& aBca)
+	: CActive(EPriorityStandard), 
+	  iUser(aUser), 
+	  iBca(aBca),
+	  iControlStep(ENone)
+	{
+	CActiveScheduler::Add(this);
+	}
+	
+/**
+ * Destructor.
+ */
+CBcaControl::~CBcaControl()
+	{
+	Cancel();
+	//Make sure the BCA is closed. This is not strictly necessary, as BCA destructor should do it.
+	if(iBcaIsOpen)
+		{
+		iBca.Close();
+		}
+	}
+
+/** 
+This is the central control routine of the BCA. It is called in 2 cases:
+1. An outstanding control request on the BCA has completed.
+2. A control sequence (startup or shutdown) has started. In this case, it is triggered by our User.
+
+This way, all the control actions on the BCA are dispatched from here only. 
+*/	
+void CBcaControl::RunL()
+	{
+	LOG(iUser.Logger().Printf(_L("CBcaControl::RunL(): Step[%d] completed with error[%d]."), iControlStep, iStatus.Int()); )
+	 		
+	if(KErrNone != iStatus.Int())
+		{
+		// Note: For clarity, the logic below is not optimized. It called only during BCA startup /shutdown,
+		// so the performance penalty is minimal.
+		const TBool KOpening       = EOpeningChannel   == iControlStep;
+		const TBool KIoctlError  = (!IsShuttingDown() && !KOpening ) && // not opening, & not shutting down.
+						(iStatus.Int() != KErrNotSupported && // BCA may not support this Ioctl, this is OK. 
+					     iStatus.Int() != KErrAlreadyExists); // This ioctl may not be necessary.
+	
+		if(KOpening || // Any error on open is fatal, 
+		   			  // All errors on shutdown are ignored. 
+		   KIoctlError ) // Ioctl errored out, i.e. BCA tried to do it and failed, rather than refuse to do it.
+		    {
+		    LOG( iUser.Logger().Printf(_L("CBcaControl: operation failed. Link down with [%d]."), iStatus.Int());)
+			iControlStep = ENone; // We are not doing anything.
+			iUser.LinkDown(iStatus.Int());
+			return;
+			}
+		}
+				
+	// Our state: the previous operation has completed successfully, or we don't care about failure.
+	
+	// Execute the specified control action. The switch statement is structured such that a control sequence
+	// can be read from top to bottom. (I.e. case = step, on completion go to the case below).
+	switch(iControlStep) // Which step did just complete?
+		{
+		//
+		// The startup sequence.
+		//
+		case EStartingStartup:
+			{
+			LOG(iUser.Logger().Printf(_L("CBcaControl: Bca Ioctl: Setting Iap Id[%d]."), iUser.GetIAPid()); )
+			
+			iControlStep = ESettingIap;
+			TPckg<TUint32> aOpt(iUser.GetIAPid());
+			iBca.Ioctl(iStatus,KBcaOptLevelGeneric, KBCASetIapId, aOpt);
+			break;
+			}
+			
+		case ESettingIap:
+			{
+			iControlStep  = ESettingBcaStack;
+			TPtrC bcaStack(iUser.GetBCAStack());
+			if(bcaStack.Length())
+				{
+				LOG(iUser.Logger().Printf(_L("CBcaControl: Bca Ioctl: Setting Bca Stack [%S]."), &bcaStack); )
+				
+				TBuf8<KMaxName> remainingBcaStack8;
+				remainingBcaStack8.Copy(bcaStack);
+				iBca.Ioctl(iStatus, KBcaOptLevelGeneric,KBCASetBcaStack,remainingBcaStack8);
+				}
+			else
+				{
+				TRequestStatus* statusPtr=&iStatus;
+				User::RequestComplete(statusPtr,KErrNone);
+				}
+			break;
+			}
+		
+		case ESettingBcaStack:
+			{
+			LOG(TPtrC portName = iUser.GetPortName(); iUser.Logger().Printf(_L("CBcaControl: Opening BCA channel [%S]."), &portName); )
+
+            iControlStep = EOpeningChannel;
+			__ASSERT_DEBUG(!iBcaIsOpen, User::Panic(_L("PPP Panic"), EPppPanic_UnexpectedBcaOpen));
+			iBca.Open(iStatus,iUser.GetPortName());
+			break;	
+			}
+					
+		case EOpeningChannel:
+			{
+			iBcaIsOpen = ETrue;
+			
+			LOG(iUser.Logger().Printf(_L("CBcaControl: Bca Ioctl: Enabling link monitoring for control line failure.")); )
+			
+			iControlStep = EnablingLinkMonitoring;
+			TPckgBuf<TUint32> argPckg(KFailBcaSpecificOnly);
+			iBca.Ioctl(iStatus, KBcaOptLevelExtSerial, KSerialMonitorControlLines, argPckg);
+			break;
+			// We won't have to disable it when we are shutting down, BCA will do it for us.
+			}
+		
+		case EnablingLinkMonitoring:
+			{
+			LOG(iUser.Logger().Printf(_L("CBcaControl: Bca Ioctl: Getting Serial Port Config")); )
+			
+			iControlStep = EGettingSerialConfig;
+
+			TPckgBuf<TCommConfig> argPckg(iSerialConfig);
+			iBca.Ioctl(iStatus, KBcaOptLevelExtSerial, KSerialConfig, argPckg);
+			iSerialConfig = argPckg();
+			break;
+			}			
+			
+		case EGettingSerialConfig:
+			{
+			iUser.SetOriginalSerialPortConfig(iSerialConfig); // Save our existing serial port settings, retrieved in the previous step.
+			// N.B. If we are being restarted, we could have modified the port settings already, so what get now
+			// would NOT be the original settings. It's users job to keep track of that, otherwise the logic here
+			// gets too complicated. If this is not the original settings, user will not set them, i.e. the call above
+			// should have not effects.
+	
+			LOG(iUser.Logger().Printf(_L("CBcaControl: Bca Ioctl: Configuring serial port for HDLC.")); )
+						
+			iControlStep = ESettingSerialConfigForHdlc;
+			iUser.GetSerialPortConfigForHdlc(iSerialConfig);
+
+			LOG(iUser.Logger().Printf(_L("CBcaControl: Bca Ioctl: Setting Serial Config for HDLC, handshaking [%x]."), iSerialConfig().iHandshake); )
+
+			TPckgBuf<TCommConfig> argPckg(iSerialConfig);
+			iBca.Ioctl(iStatus, KBcaOptLevelExtSerial, KSerialSetConfig, argPckg);
+			break;
+			}
+					
+		case ESettingSerialConfigForHdlc:
+			{
+			LOG(iUser.Logger().Printf(_L("CBcaControl: Bca Ioctl: Setting Rx Tx Buffer size to %d."), KReceiveBufferSize); )
+			
+			iControlStep = ESettingBufferSize;
+			TPckg<TInt> bufSizeOpt(KReceiveBufferSize);
+			iBca.Ioctl(iStatus,KBcaOptLevelExtSerial, KSerialSetTxRxBufferSize, bufSizeOpt);
+			break;
+			}
+			
+		case ESettingBufferSize:
+			{
+			LOG(iUser.Logger().Printf(_L("CBcaControl: Bca Ioctl: Resetting Buffers.")); )
+			
+			iControlStep = EResettingBuffers;
+			TPckg<TInt> resetMask(KResetRxBuf);
+			iBca.Ioctl(iStatus, KBcaOptLevelGeneric, KBCAResetBuffers, resetMask);
+			break;	
+			}
+			
+		case EResettingBuffers:
+			{
+			iControlStep = ENone; // We are finished with the startup sequence.
+			iUser.StartPPP();
+			break;
+			}
+			
+		//
+		// The shutdown sequence		
+		// Some operations may fail. If we are shutting down as a result of link failure, we have no way of knowing what
+		// exactly has failed. Errors will be ignored, so the shutdown sequence runs fully always. 
+		
+		case EStartingShutdown:
+			{
+			LOG(iUser.Logger().Printf(_L("CBcaControl: Bca Ioctl: Restoring original Serial port config.")); )
+			
+			iControlStep = ERestoringOrigSerialConfig;
+			iSerialConfig = iUser.OriginalSerialPortConfig(); // In case user modifies the config before request completion.
+
+			TPckgBuf<TCommConfig> argPckg(iSerialConfig);
+			iBca.Ioctl(iStatus, KBcaOptLevelExtSerial, KSerialSetConfig,  argPckg);			
+			// This may fail, because the underlying channel provider may have caused the shutdown to start 
+			// in the first case. Error will be ignored, we'll always execute the next step.
+			break;
+			}
+			
+		case ERestoringOrigSerialConfig:
+			{
+			iControlStep = EShuttingDownChannel;
+			if(iUser.DoShutdownBcaGracefully()) 
+				{
+				LOG(iUser.Logger().Printf(_L("CBcaControl: Shutting down BCA.")); )
+			
+				iBca.Shutdown(iStatus);
+				// This may fail. If the link is shutting down because of channel provider failure, it may not be possible
+				// to shut it down cleanly. Error will be ignored, we'll always execute the next step.
+				}
+			else // No need to shutdown, BCA is closed
+				{
+				LOG(iUser.Logger().Printf(_L("CBcaControl: Closing BCA.")); )
+
+				iBca.Close(); // This is a synchronous call, so we need to complete the request ourselves to move on.
+				TRequestStatus* statusPtr = &iStatus;
+				User::RequestComplete(statusPtr, KErrNone);
+				}
+			break;
+			}
+			
+		case EShuttingDownChannel:
+			{
+			iBcaIsOpen = EFalse;
+			
+			iControlStep = ENone; // We are finished with the shutdown sequence.
+			iUser.LinkTerminationComplete(); // We don't pass error code, because erros on termination are ignored.
+			break;
+			}
+			
+		default: // Unrecognized step. RFC1661 says we SHOULD not freeze or reset if internal FSM transition is invalid.
+			{
+			LOG(iUser.Logger().Printf(_L("CBcaControl: ERROR: Illegal control step [%d]"), iControlStep); )
+			__ASSERT_DEBUG(EFalse, User::Panic(_L("PPP Panic"), EPppPanic_IllegalBcaControlStep));
+			break;
+			}
+			
+		}
+		// Complete the step.
+		if(ENone != iControlStep) // ENone means we have no step to complete.
+			{
+			SetActive();
+			}
+	}
+
+	
+/**
+ *	Cancels outstanding Control action. 
+ */
+void CBcaControl::DoCancel()
+	{
+	LOG(iUser.Logger().Printf(_L("CBcaControl::DoCancel: Cancelling Ioctl. iControlStep[%d]."), iControlStep); )
+	
+	if(EOpeningChannel == iControlStep ||  EShuttingDownChannel== iControlStep)
+		{
+		iBca.Close(); // "Cancels" Open & graceful shutdown.
+		}
+	else if(ENone != iControlStep) // We have an Ioctl request outstanding.
+		{
+		iBca.CancelIoctl(); 
+		// Some Ioctl states may not have an Ioctl outstanding. Canceling is OK, BCA ignores it.		
+		}
+	iControlStep = ENone;
+		
+	// N.B. IMPORTANT ASSUMPTION:
+	// We are only going to be called if things went wrong, and 
+	// the link is being terminated. I.e., after this call, the BCA is closed. 
+		
+	// We can't always execute an Ioctl more than once an opened BCA. I.e, we can't
+	// always set different parameters, because the BCA may have been opened with them.
+	
+	// If the BCA is closed, its internal state is reset, so it is safe to restart the startup sequence.
+	}
+	
+
+/**
+Launches the BCA channel startup sequence */	
+void CBcaControl::StartStartupSequence()
+	{
+	__ASSERT_DEBUG(!IsActive(), User::Panic(_L("PPP Panic"), EPppPanic_BcaStartupAlreadyActive));
+		
+	// Jump to startup.
+	iControlStep = EStartingStartup;
+	TRequestStatus* stat = &iStatus;
+	User::RequestComplete(stat, KErrNone);	
+	SetActive();
+	LOG(iUser.Logger().Printf(_L("CBcaControl: Startup sequence started.")); )
+	}
+	
+/** 
+Launches the BCA channel shutdown sequence */	
+void CBcaControl::StartShutdownSequence()
+	{
+	if(IsShuttingDown())
+		{
+		LOG(iUser.Logger().Printf(_L("CBcaControl: Warning: Shutdown sequence already in progress.")); )
+		return; // We are already shutting down.
+		}
+		
+	Cancel(); // stop whatever we are doing and shut ourselves down.
+	// Jump to shutdown.
+	iControlStep = EStartingShutdown;
+	TRequestStatus* stat = &iStatus;
+	User::RequestComplete(stat, KErrNone);	
+	SetActive();
+	LOG(iUser.Logger().Printf(_L("CBcaControl: Shutdown sequence started.")); )
+	}
+
+/**
+Is the controller executing the shutdown sequence on the BCA? 
+
+@return ETrue if Yes.
+*/
+TBool CBcaControl::IsShuttingDown()const
+	{
+	return (
+		iControlStep == EStartingShutdown ||
+		iControlStep == ERestoringOrigSerialConfig ||
+		iControlStep == EShuttingDownChannel);
+	}
+
+/**
+  C++ Constructor. Performs standard active object initialisation.
+ 
+  @param aUser Our user: PPP HdlcLink.
+  @param aBca the BCA we read from on behalf of the user
+  @param aPriority the AO priority 
+*/
+CBcaReader::CBcaReader(CPppHdlcLink& aUser, MBca& aBca, TInt aPriority):
+	CActive(aPriority), 
+	iUser(aUser),
+	iBca(aBca)	  
+	{
+	CActiveScheduler::Add(this);
+	}
+
+/** Destructor */
+CBcaReader::~CBcaReader()
+	{
+	Cancel();
+	}
+	
+/** Called when Read request completes, i.e. BCA can process another read.*/
+void CBcaReader::RunL()
+	{
+	iUser.BcaReadComplete(iStatus.Int());		
+	}
+	
+/** Cancels an outstanding read request.*/
+void CBcaReader::DoCancel()
+	{
+	iBca.CancelRead();
+	}
+
+/**
+Triggers completion of the read */	
+void CBcaReader::ReadReady()
+	{
+	TRequestStatus* statusPtr = &iStatus;
+	User::RequestComplete(statusPtr,KErrNone);
+	SetActive();
+	}
+	
+/** Queues a read on the BCA.*/
+void CBcaReader::Read(TDes8& aDes)
+	{
+	iBca.Read(iStatus,aDes);
+	SetActive();
+	}
+	
+
+/**
+C++ constructor 
+
+@param aUser: the BCA user - the HDLC link
+@param aBca: the BCA to use for writes
+@param aPriority write AO priority 
+*/	
+CBcaWriter::CBcaWriter(CPppHdlcLink& aUser, MBca& aBca, TInt aPriority)
+	: CActive(aPriority), 
+	  iUser(aUser),
+	  iBca(aBca)	  
+	{
+	CActiveScheduler::Add(this);
+	}
+
+/** C++ Destructor */
+CBcaWriter::~CBcaWriter()
+	{
+	Cancel();
+	}
+	
+/** Called on Write completion, indicating a readiness to handle another write.*/
+void CBcaWriter::RunL()
+	{
+	iUser.BcaWriteComplete(iStatus.Int());		
+	}
+	
+/** Cancels an outstanding write request.*/
+void CBcaWriter::DoCancel()
+	{
+	iBca.CancelWrite();
+	}
+	
+/** Writes on the BCA channel.
+
+@param aDes the buffer to write.
+*/	
+void CBcaWriter::Write(const TDesC8& aDes)
+	{
+	iBca.Write(iStatus,aDes);
+	SetActive();
+	}