dbgagents/trkagent/engine/TrkFramingLayer.cpp
author bdonegan
Thu, 30 Sep 2010 16:15:48 +0100
changeset 3 b667e5204120
parent 0 c6b0df440bee
permissions -rw-r--r--
Remove reference to cinidata in order to allow TrkEngine to build

/*
* Copyright (c) 2004 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: 
*
*/


#include "TrkFramingLayer.h"

#ifdef __USE_NEW_DEBUG_API__
#include "trkdispatchlayer2.h"
#else
#include "TrkDispatchLayer.h"
#endif

#include "serframe.h"

_LIT8(kPPP_FLAG, "~");
_LIT8(kPPP_ESCAPE, "}");


//
//
// CTrkFramingLayer implementation
//
//

//
// CTrkFramingLayer constructor
//
CTrkFramingLayer::CTrkFramingLayer(CTrkCommPort* aPort)
	: iPort(aPort),
	  iFramingState(CTrkFramingLayer::eWaiting),
	  iEscape(0),
	  iFCS(0),
	  iSequenceId(0),
	  iOutSequenceId(1)
{
	// check the target endianness
	TUint32	test;
	TUint8 *byte_alias = (TUint8 *)&test;
	
	// Write a specific 4-byte sequence and then read it as
	// a 32-bit word.  Big-endian systems yield one value and
	// little-endian systems yield another.
	
	byte_alias[ 0 ] = 0x12;
	byte_alias[ 1 ] = 0x34;
	byte_alias[ 2 ] = 0x56;
	byte_alias[ 3 ] = 0x78;
	
	if (test == 0x12345678)
		iIsBigEndian = ETrue;
	else
		iIsBigEndian = EFalse;
}

//
// CTrkFramingLayer destructor
//
CTrkFramingLayer::~CTrkFramingLayer()
{
	if (iPort)
	{
		iPort->ClosePort();
		SafeDelete(iPort);
	}
	
	SafeDelete(iLastReply);
}

//
// CTrkFramingLayer::Listen
//
// Start listening for incoming messages from the host debugger
//
void CTrkFramingLayer::Listen(CTrkDispatchLayer *aDispatchLayer)
{
	// Start listening asynchronously for incoming messages
	iDispatchLayer = aDispatchLayer;
	
	iPort->Listen(this);
}

//
// CTrkFramingLayer::StopListening
//
// Stop listening for incoming messages from the host debugger
//
void CTrkFramingLayer::StopListening()
{
	iDispatchLayer = 0;
	iPort->StopListening();
}

//
// CTrkFramingLayer::HandleByte
//
// Handle a single incoming byte with a state machine 
//
void CTrkFramingLayer::HandleByte(TUint8 aByte)
{
	// discard existing escape if needed
	if (iFramingState != eInFrame)
		iEscape = EFalse;

	// handle state machine
	switch (iFramingState)
	{
		case eWaiting:
		{
			// Start of a new packet
			if (PPP_FLAG == aByte)
			{
				iFCS = PPPINITFCS;
				iFramingState = eFound;	// next state
			}
			break;
		}
			
		case eFound:
		{
			if (PPP_FLAG == aByte)  // discard multiple flags
				break;
	
			iFramingState = eInFrame;

			// fall through to eInFrame, receive first char
		}
					
		case eInFrame:
		{
			if (PPP_FLAG == aByte)
			{
				if (iEscape)
				{
					// [..., escape, flag] is bad frame; drop it
					// send a NAK with the error.
					RespondErr(kDSReplyNAK, kDSReplyEscapeError);
					
					// clear the buffer
					DiscardFrame();
				}
				else
				{
					// normal case, good packet
					if (ProcessFrame())
					{
						iDispatchLayer->HandleMsg(iBuffer);
					}
						
					// now start all over again
					DiscardFrame();
				}
				break;
			}
			else if (iEscape)
			{
				aByte ^= PPP_TRANS;		// change character to new code
				iEscape = EFalse;		// no longer escaped character
			}
			else if (PPP_ESCAPE == aByte)
			{
				iEscape = ETrue;		// next character is escaped
				break;
			}

			// make sure our buffer is not full
			if (iBuffer.Length() == iBuffer.MaxLength())
			{
				// overflow.  send out a NAK immediately.  go into an overflow state
				// which will wait for the end of the packet.
				RespondErr(kDSReplyNAK, kDSReplyOverflow);

				iFramingState = eFrameOverflow;
				break;
			}
							
			// append byte to buffer and compute running FCS (checksum)
			iBuffer.Append(aByte);

			iFCS = PPPFCS(iFCS, aByte);
			break;
		}
		
		case eFrameOverflow:
		{
			// overflow occurred.  throw away all characters and wait for a flag
			if (PPP_FLAG == aByte)
			{
				DiscardFrame();
			}
			break;
		}
			
		default:
		{
			User::Panic(_L("Invalid Framing State"), __LINE__);
			break;
		}
	}
}

//
// CTrkFramingLayer::RespondOkL
//
// Send an ACK to the host debugger
//
void CTrkFramingLayer::RespondOkL(const TDesC8& aMsg)
{
	// compose the standard ACK message
	TBuf8<3> reply;
	reply.Zero();
	
	reply.Append(kDSReplyACK);
	reply.Append(iSequenceId);
	reply.Append(kDSReplyNoError);

	// now append the rest of the response if necessary
	HBufC8 *ack = HBufC8::NewLC(aMsg.Length() + 3);
	ack->Des().Copy(aMsg.Ptr(), aMsg.Length());

	TPtr8 ptr(ack->Des());
	ptr.Insert(0, reply);

	// send it out
	TUint ackTries = 3;
	TInt leaveCode = KErrGeneral;
	
	do
	{
		TRAP(leaveCode, SendMsgL(*ack));
		ackTries--;
	} while ((leaveCode != KErrNone) && ackTries > 0);
	
	// now increment the sequence id
	IncrementSequenceId();

	// save this in case we need to resend it
	SafeDelete(iLastReply);
	iLastReply = ack->Des().AllocL();
	
	CleanupStack::PopAndDestroy(ack);
}

//
// CTrkFramingLayer::RespondErr
//
// Send an NAK to the host debugger
//
void CTrkFramingLayer::RespondErr(TUint8 aType, TUint8 aErr)
{
	// compose the standard NAK message
	TBuf8<3> ack;
	TUint ackTries = 3;
	TInt leaveCode = KErrGeneral;
	
	ack.Zero();
	ack.Append(aType);	
	ack.Append(iSequenceId);
	ack.Append(aErr);

	// send it out
	do
	{
		TRAP(leaveCode, SendMsgL(ack));
		ackTries--;
	} while ((leaveCode != KErrNone) && ackTries > 0);

	if (kDSReplySequenceMissing != aErr)
	{
		// now increment the sequence id
		IncrementSequenceId();
	}
	
	// save this in case we need to resend it
	SafeDelete(iLastReply);
	iLastReply = ack.Alloc();
	
	if (!iLastReply)
		User::Panic(_L("Failed to allocate reply buffer"), __LINE__);
}

//
// CTrkFramingLayer::InformEventL
//
// Queue up an event on the target
//
void CTrkFramingLayer::InformEventL(const TDesC8& aMsg)
{
	// compose the message
	TBuf8<1> id;
	id.Zero();
	id.Append(iOutSequenceId);
	IncrementOutSequenceId();

	HBufC8 *msg = HBufC8::NewLC(aMsg.Length() + 1);
	msg->Des().Copy(aMsg.Ptr(), aMsg.Length());

	TPtr8 ptr(msg->Des());
	ptr.Insert(1, id);

	// now send it out
	TUint ackTries = 3;
	TInt leaveCode = KErrGeneral;

	do
	{
		TRAP(leaveCode, SendMsgL(*msg));
		ackTries--;
	} while ((leaveCode != KErrNone) && ackTries > 0);

	CleanupStack::PopAndDestroy(msg);
	
	if (leaveCode != KErrNone)
		User::Leave(leaveCode);
}

//
// CTrkFramingLayer::SendRawMsgL
//
// Frames a message and sends it back to the host debugger
//
void CTrkFramingLayer::SendRawMsgL(const TDesC8& aMsg)
{
	iPort->SendDataL(aMsg);
}


//
// CTrkFramingLayer::SendMsgL
//
// Frames a message and sends it back to the host debugger
//
void CTrkFramingLayer::SendMsgL(const TDesC8& aMsg)
{
	FCSType fcs;
	FCSType acc;

	// allocate room for this message with ample space for escape sequences and framing
	HBufC8 *msg = HBufC8::NewLC(aMsg.Length() + 100);

	TPtr8 ptr(msg->Des());

	// walk through buffer, computing frame check sequence
	// (checksum).
	
	fcs = PPPINITFCS;
	
	for (TInt i=0; i<aMsg.Length(); i++)
		fcs = PPPFCS(fcs, aMsg[i]);

	fcs = fcs ^ PPPCOMPFCS;		// finish by complementing
	
	// send bytes out the comm channel:
	//	[0x7E (frame flag)] [...data...] [FCS:8 or FCS:16 or FCS:32] [0x7E (frame flag)]
	 
	// FLAG
	ptr.Append(kPPP_FLAG);
					
	// DATA
	TBuf8<1> byte;
	
	for (TInt i=0; i<aMsg.Length(); i++)
	{
		byte.Zero();
		byte.Append(aMsg[i]);
		
		// escape if necessary
		if (byte[0] == PPP_FLAG || byte[0] == PPP_ESCAPE)
		{
			ptr.Append(kPPP_ESCAPE);
			byte[0] ^= PPP_TRANS;
		}
		
		ptr.Append(byte);
	}
	
	// FCS always goes out in little endian (LSB, ... , MSB) order	
	acc = fcs;		// accumulator
	
	TInt fcsSize = sizeof(FCSType);
	
	for (TInt i=0; i<fcsSize; i++)
	{
		byte.Zero();
		byte.Append(acc & 0xFF);

		acc >>= 8;

		// escape if necessary
		if (byte[0] == PPP_FLAG || byte[0] == PPP_ESCAPE)
		{
			ptr.Append(kPPP_ESCAPE);
			byte[0] ^= PPP_TRANS;
		}
		
		ptr.Append(byte);
	}
	
	// FLAG	
	ptr.Append(kPPP_FLAG);
	
	iPort->SendDataL(*msg);
	
	CleanupStack::PopAndDestroy(msg);
}

//
// CTrkFramingLayer::DiscardFrame
//
// Trash the existing message and reset the state machine 
//
void CTrkFramingLayer::DiscardFrame()
{
	iBuffer.Zero();
	iFramingState = eWaiting;
}

//
// CTrkFramingLayer::ProcessFrame
//
// Checks a complete message for correct format
//
TBool CTrkFramingLayer::ProcessFrame()
{
	// the buffer contains data in the following form:
	//		[data] [FCS8]
	//	or:
	//		[data] [FCS16]
	//	or:
	//		[data] [FCS32]
	//	
	//	where data is arbitrary length and FCSxx is 1, 2 or 4 bytes

	TInt minSize = 2 + sizeof(FCSType);
	
	if (iBuffer.Length() < minSize)
	{
		// data received is too short
		RespondErr(kDSReplyNAK, kDSReplyPacketSizeError);
		DiscardFrame();
		return EFalse;
	}
	
	// check for FCS
	if (PPPGOODFCS != iFCS)
	{
		// bad checksum (FCS)		
		RespondErr(kDSReplyNAK, kDSReplyBadFCS);
		DiscardFrame();
		return EFalse;
	}
	
	// make sure the sequence id is correct, then increment
	if (iBuffer[TRK_MSG_SID_INDEX] == 0)
	{
		// special case - resets both sequence ids
		iSequenceId = 0;
		iOutSequenceId = 1;		
	}
	else
	{
		// see which id we're dealing with
		if ((iBuffer[0] != kDSReplyACK) && (iBuffer[0] != kDSReplyNAK))
		{
			// a command from the host 
			if (iBuffer[TRK_MSG_SID_INDEX] == iSequenceId)
			{
				// got the expected sequence id - do nothing
			}
			else if (iBuffer[TRK_MSG_SID_INDEX] == (iSequenceId - 1))
			{
				// we need to resend the last reply
				TUint ackTries = 3;
				TInt leaveCode = KErrGeneral;

				do
				{
					TRAP(leaveCode, SendMsgL(*iLastReply));
					ackTries--;
				} while ((leaveCode != KErrNone) && ackTries > 0);

				return EFalse;
			}
			else
			{
				// bad sequence id
				RespondErr(kDSReplyNAK, kDSReplySequenceMissing);
				DiscardFrame();
				return EFalse;
			}
		}
	}

	// trim back packet, eliminating FCS
	iBuffer.Delete(iBuffer.Length()-1, 1);

	// remove sequence id
	iBuffer.Delete(1, 1);
	
	return ETrue;
}