diff -r 000000000000 -r af10295192d8 linklayerprotocols/pppnif/SPPP/PPPHDLC.CPP --- /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 +#include +#include "PPPHDLC.H" +#include "PPPLOG.H" +#include "ncpip.h" +#include "PPPConfig.h" + +#include + +// 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 +#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<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) + { + // 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 (ptrPtr()) + { + // 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)Ptr(); + TUint8* mend = m->EndPtr(); + // Encode frame data into send buffer & calculate CRC + while (mptrFree(); + --iSendNumBufs; + if (!iSendFlowOn && iSendNumBufsLinkFlowOn(); + } + 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 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 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 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 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 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 bufSizeOpt(KReceiveBufferSize); + iBca.Ioctl(iStatus,KBcaOptLevelExtSerial, KSerialSetTxRxBufferSize, bufSizeOpt); + break; + } + + case ESettingBufferSize: + { + LOG(iUser.Logger().Printf(_L("CBcaControl: Bca Ioctl: Resetting Buffers.")); ) + + iControlStep = EResettingBuffers; + TPckg 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 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(); + }