dbgagents/trkagent/dccdriver/TrkDccDriver.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) 2006 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: 
*
*/


#ifdef __WINS__
#error - this driver cannot be built for emulation
#endif

#include <e32def.h>
#include <e32cmn.h>
#include <kernel.h>
#include <kern_priv.h>
#include <arm.h>

#include "TrkDccComm.h"
#include "TrkDccDriver.h"

//This macro turns on DCC3 mode, this is always turned on.
//To test DCC1 mode, comment this out and make sure to configure T32 to use DCC1 as well.
#define DCC3

//currenly both start and end bytes for the packet are the same
#define PKT_STRT_BYTE 0x7E
#define PKT_END_BYTE 0x7E

#define KTrkAppSecurUid 0x200170BB
#define KTrkExeSecurUid 0x200159E2
//
// Static function definitions
//


//
//
// DTrkDccDriverFactory implementation
//
//

//
// DTrkDccDriverFactory constructor
//
DTrkDccDriverFactory::DTrkDccDriverFactory()
{
    iVersion = TVersion(KMajorDccVersionNumber, KMinorDccVersionNumber, KBuildDccVersionNumber);    
}

//
// DTrkDccDriverFactory::Create
//
TInt DTrkDccDriverFactory::Create(DLogicalChannelBase*& aChannel)
{
	if (iOpenChannels != 0)
		return KErrInUse; // a channel is already open
	
	aChannel = new DTrkDccChannel(this);
	
	return aChannel ? KErrNone : KErrNoMemory;
}

//
// DTrkDccDriverFactory::Install
//
TInt DTrkDccDriverFactory::Install()
{
    return(SetName(&KTrkDccDriverName));
}

//
// DTrkDccDriverFactory::Install
//
void DTrkDccDriverFactory::GetCaps(TDes8& aDes) const
{
    TCapsTrkDccDriver b;
    b.iVersion = TVersion(KMajorDccVersionNumber, KMinorDccVersionNumber, KBuildDccVersionNumber);
    
    Kern::InfoCopy(aDes,(TUint8*)&b, sizeof(b));
}


//
//
// DTrkDccChannel implementation
//
//

//
// DTrkDccChannel constructor
//
DTrkDccChannel::DTrkDccChannel(DLogicalDevice* aLogicalDevice)
				: iRxTimer(DTrkDccChannel::RxTimerCallBack, this),
				  iRxBufSize(MAXMESSAGESIZE*2),
				  iRxGetIndx(0),
				  iRxPutIndx(0),
				  iRxCompleteDfc(DTrkDccChannel::RxTimerDfc, this, 2),
				  iInterruptId(76),
				  iPacketStarted(EFalse),
				  iPacketEnded(EFalse)


{
	LOG_MSG("DTrkDccChannel::DTrkDccChannel()");

	iDevice = aLogicalDevice;
	
	iClientThread = &Kern::CurrentThread();
	TInt err = iClientThread->Open();	
	
	iRxTimeOut = NKern::TimerTicks(1); 
}

//
// DTrkDccChannel destructor
//
DTrkDccChannel::~DTrkDccChannel()
{
	LOG_MSG("DTrkDccChannel::~DTrkDccChannel()");

	iRxTimer.Cancel();
	
	if (iRxBuffer)
	{
		delete iRxBuffer;
		iRxBuffer = NULL;
	}
	if (iLock)
		iLock->Close(NULL);

	Kern::SafeClose((DObject*&)iClientThread, NULL);	
}

//
// DTrkDccChannel::DoCreate
//
TInt DTrkDccChannel::DoCreate(TInt /*aUnit*/, const TDesC* /*anInfo*/, const TVersion& aVer)
{
	LOG_MSG("DTrkDccChannel::DoCreate()");

  	if (!Kern::QueryVersionSupported(TVersion(KMajorDccVersionNumber, KMinorDccVersionNumber, KBuildDccVersionNumber), aVer))
		return KErrNotSupported; 

	//Setup the driver for receiving client messages
	iDFCQue = NULL;
	TBuf8<KMaxInfoName> dccDFC = _L8("DCC DFC");;
	
	TInt err = Kern::DfcQCreate(iDFCQue, 27, &dccDFC);
	if (err == KErrNone)
	{
		SetDfcQ(iDFCQue);
	}
	else
	{
		SetDfcQ(Kern::DfcQue0());
	}

	iMsgQ.Receive();  	
	
	iRxCompleteDfc.SetDfcQ(Kern::TimerDfcQ());
	
	iRxBuffer=(TUint8*)Kern::Alloc(iRxBufSize);

	err = Kern::SemaphoreCreate(iLock, _L("TrkDccDriverWriteLock"), 1 /* Initial count */);
	if (err != KErrNone)
		return err;

	return KErrNone;
}

//
// DTrkDccChannel::DoCancel
//
void DTrkDccChannel::DoCancel(TInt aReqNo)
{
	LOG_MSG("DTrkDccChannel::DoCancel()");
	
	switch(aReqNo)
	{
		case RTrkDccDriver::ERequestReadCancel:
		{
			Kern::RequestComplete(iClientThread, iRxRequestStatus, KErrCancel);
			iRxClientBuffer = NULL;
			iRxRequestStatus = NULL;
			iRxClientBufferLength = 0;
			
			iRxTimer.Cancel();
		}
		break;
	}

}

//
// DTrkDccChannel::DoRequest
//
void DTrkDccChannel::DoRequest(TInt aReqNo, TRequestStatus* aStatus, TAny* a1, TAny* a2)
{
	LOG_MSG("DTrkDccChannel::DoRequest()");
		
	switch(aReqNo)
	{
		case RTrkDccDriver::ERequestRead:
		{
			iRxRequestStatus = aStatus;
			iRxClientBuffer = a1;
			iRxClientBufferLength = (TUint32)a2;
			//start the polling timer...
			iRxTimer.OneShot(iRxTimeOut);
			break;
		}		
		default:
			Kern::RequestComplete(iClientThread, aStatus, KErrNotSupported);
	}
}

//
// DTrkDccChannel::DoControl
//
TInt DTrkDccChannel::DoControl(TInt aFunction, TAny* a1, TAny* a2)
{
	LOG_MSG("DTrkDccChannel::DoControl()");
	
	TInt err = KErrNone;
	
	switch(aFunction)
	{
		case RTrkDccDriver::EControlWrite:
		{
			//lock the Semaphore so that write doesn't happen when a write is already in progress.
			Kern::SemaphoreWait(*iLock);
			err = DoWrite((TUint32)a1, a2);			
			Kern::SemaphoreSignal(*iLock);			
			break;
		}
		default:
		{
			return KErrGeneral;
		}

	}
	if (KErrNone != err)
	{
		LOG_MSG2("Error %d from control function", err);
	}
	return err;
}

//
// DTrkDccChannel::HandleMsg
//
void DTrkDccChannel::HandleMsg(TMessageBase* aMsg)
{
	LOG_MSG("DTrkDccChannel::HandleMsg()");
	
	TThreadMessage& m = *(TThreadMessage*)aMsg;
	TInt id = m.iValue;


	if (id == (TInt)ECloseMsg)
	{
		m.Complete(KErrNone, EFalse);
		return;
	}

	if (id == KMaxTInt)
	{
		// DoCancel
		DoCancel(m.Int0());
		m.Complete(KErrNone, ETrue); 
		return;
	}

	if (id < 0)
	{
		// DoRequest
		TRequestStatus* pStatus = (TRequestStatus*)m.Ptr0();
		DoRequest(~id, pStatus, m.Ptr1(), m.Ptr2());
		m.Complete(KErrNone, ETrue);
	}
	else
	{
		// DoControl
		TInt err = DoControl(id, m.Ptr0(), m.Ptr1());
		m.Complete(err, ETrue);
	}
}

//
// DTrkDccChannel::DoWrite
//
TInt DTrkDccChannel::DoWrite(TUint32 aLength, TAny* a2)
{
	LOG_MSG("DTrkDccChannel::DoWrite()");

	TInt err = KErrNone;

	err = Kern::ThreadDesRead(iClientThread, a2, iTxBuffer, 0, KChunkShiftBy0);

	if (err == KErrNone)
	{	
		err = WriteDccChannel(iTxBuffer);
	}
	return err;
}

//
// DTrkDccChannel::DoRead
//
TInt DTrkDccChannel::DoRead()
{	
	TInt err = KErrNone;
	
	ReadDccChannel();

	if (iRxPutIndx != iRxGetIndx)
	{
		if (iPacketEnded)
		{
			iPacketStarted = EFalse;
			iPacketEnded = EFalse;
		}
		//there is some data, complete the read request if there is one pending
		DoCompleteRx();
		
		if (iRxPutIndx == iRxGetIndx)
		{
			iRxPutIndx = iRxGetIndx = 0;
		}
	}
	else
	{
		err = -1;
		//start the timer again since we didn't complete the request
		iRxTimer.OneShot(iRxTimeOut);
	}
	
	return err;			
}

//
// DTrkDccChannel::WriteDccChannel
//
// This function writes all the data in the descriptor word by word.
// DCC3 mode is used for transferring the data to the DCC channel.
// With DCC3, the number of bytes in the word is encoded in its MSB.
// DCC3 mode can be turned of by commenting the DCC3 macro at the 
// beginning of this file.
//
TInt DTrkDccChannel::WriteDccChannel(const TDesC8& aDes)
{
	TInt err = KErrNone;
	
	const TText8* pStart = aDes.Ptr();
	const TText8* pEnd = pStart + aDes.Length();
	if (pStart != pEnd)
	{
		while (pStart < pEnd)
		{			
			TUint retries = 10;

		#ifdef DCC3
			TUint32 data = 0;
			//for DCC3 support
			if ((pEnd-pStart) >= 3)
			{
				data = ((TUint32)(*pStart++)) | ((TUint32)(*pStart++))<<8 | ((TUint32)(*pStart++))<<16 | (0x02<<24);
			}
			else if ((pEnd-pStart) == 2)
			{
				data = ((TUint32)(*pStart++)) | ((TUint32)(*pStart++))<<8 |  (0x01<<24);
			}
			else if ((pEnd-pStart) == 1)
			{
				data = (TUint32)(*pStart++);
			}
		#endif
			
			while (retries--)
			{
			#ifdef DCC3
				err = Arm::DebugOutJTAG(data);		
			#else
				err = Arm::DebugOutJTAG(*pStart);		
			#endif
				
				//if we get timeout error, try three times sending the same byte
				if (err == KErrTimedOut)
				{
					LOG_MSG("Write timed out, trying again\n");
					continue;
				}
				else
					break;
			}
			if (err)
			{
				LOG_MSG2("WriteDccChannel() failed %d", err);
				return err;
			}
			
		#ifndef DCC3
			pStart++;
		#endif
		
		}
	}
	return err;
}

//
// DTrkDccChannel::ReadDccChannel
//
// Strictly reads packet by packets since TRK expects the host debugger 
// to wait for the response for the cmd sent. 
// This function will return if 
// 	- the packet is completely read
//  - if out of band data, i.e. data not conforming to TRK protocol packets
//	- if an error other than KErrNotReady happens in while in the middle of 
//    reading a packet.
// Also the function uses DCC3 mode, where the no of bytes sent in the word
// is encoded in the MSB and the actual data bytes in the remaining bytes.
// Like for sending three bytes, the word will be 0x02CCBBAA.
// For sending three bytes, the MSB should be 2.
// For two bytes, the MSB should be 1.
// For one bytes, the MSB should be 0.
//
void DTrkDccChannel::ReadDccChannel()
{
	TInt err = KErrNone;
	TInt err1 = KErrNone;
	
	TUint i = iRxPutIndx;
	TUint32 data;
	
	err = err1 = Arm::DebugInJTAG(data);	
	while (KErrNone == err)
	{	
		if (KErrNone == err1)
		{	
			TUint8 nBytes = 0;
			nBytes = TUint8(data>>24);
			
			for (int j=0; j<=nBytes; j++)
			{									
				iRxBuffer[i] = (TUint8)(data>>(j*8));	
				if (iRxBuffer[i] == PKT_STRT_BYTE)	
				{
					if (!iPacketStarted)
					{
						iPacketStarted = ETrue;
					}
					else
					{
						iPacketEnded = ETrue;
					}
				}
				i++; //increment i here so that put index can be appriopriately set later 
			}
			
			if (i == iRxBufSize)
			{
				i = 0;
				break;
			}
			//if cmd packet ended, send the data to the engine.
			if (iPacketEnded)
			{
				break;
			}
		}

		//read again to see if there is data already available
		err = err1 = Arm::DebugInJTAG(data);	
		
		//if the packet is started, try to read the whole packet
		//so set the err value to KErrNone so that we can continue reading
		if ((err == KErrNotReady) && iPacketStarted && !iPacketEnded)
			err = KErrNone; 				
			
		if (err != KErrNone && err != KErrNotReady)
			LOG_MSG2("Read Error %d\n", err);
	}

	iRxPutIndx=i;	
}

//
// DTrkDccChannel::RxTimerCallBack
//
void DTrkDccChannel::RxTimerCallBack(TAny* aPtr)
{
	// called from ISR when timer completes
	DTrkDccChannel *pChannel = (DTrkDccChannel*)aPtr;
	pChannel->iRxCompleteDfc.Add();
}

//
// DTrkDccChannel::RxTimerDfc
//
void DTrkDccChannel::RxTimerDfc(TAny* aPtr)
{
	DTrkDccChannel *pChannel = (DTrkDccChannel*)aPtr;	
	pChannel->DoRead();
}

//
// DTrkDccChannel::DoCompleteRx
//
void DTrkDccChannel::DoCompleteRx()
{
	LOG_MSG("DTrkDccChannel::DoCompleteRx()");

	if (iRxRequestStatus)
	{
		TInt avail = 0;
		if (iRxPutIndx > iRxGetIndx) 
			avail = iRxPutIndx-iRxGetIndx;
		else
			avail = iRxBufSize-iRxGetIndx;
			
		TInt len = Min(avail, iRxClientBufferLength);
		
		TPtrC8 des(iRxBuffer+iRxGetIndx, len);
		
		TInt err = Kern::ThreadDesWrite(iClientThread, iRxClientBuffer, des, 0, KChunkShiftBy0, iClientThread);

		Kern::RequestComplete(iClientThread, iRxRequestStatus, err);
		
		iRxRequestStatus = NULL;
		iRxGetIndx += len;
	}
			
	if (iRxGetIndx >= iRxBufSize)
		iRxGetIndx = 0;
}


DECLARE_STANDARD_LDD()
{
    return new DTrkDccDriverFactory;
}