kerneltest/e32test/iic/iic_psl/spi.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:26:05 +0100
branchRCL_3
changeset 29 743008598095
parent 6 0173bcd7697c
child 43 c1f20ce4abcf
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "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:
// e32test/iic/iic_psl/spi.cpp
//
#include "spi.h"

#ifdef IIC_INSTRUMENTATION_MACRO
#include <drivers/iic_trace.h>
#endif

#define NUM_CHANNELS 4 // Arbitrary

// Macros to be updated(?) with interaction with Configuration Repository
const TInt KChannelTypeArray[NUM_CHANNELS] = {DIicBusChannel::EMaster, DIicBusChannel::EMaster, DIicBusChannel::ESlave, DIicBusChannel::EMaster};
#define CHANNEL_TYPE(n) (KChannelTypeArray[n])	
const DIicBusChannel::TChannelDuplex KChannelDuplexArray[NUM_CHANNELS] = {DIicBusChannel::EHalfDuplex, DIicBusChannel::EHalfDuplex, DIicBusChannel::EHalfDuplex, DIicBusChannel::EFullDuplex};
#define CHANNEL_DUPLEX(n) (KChannelDuplexArray[n]) 

#ifdef LOG_SPI
#define SPI_PRINT(str) Kern::Printf str
#else
#define SPI_PRINT(str)
#endif

_LIT(KSpiThreadName,"SpiChannelThread");

const TInt KSpiThreadPriority = 5; // Arbitrary, can be 0-7, 7 highest

#ifdef STANDALONE_CHANNEL
_LIT(KPddNameSpi,"spi_ctrless.pdd");
#else
_LIT(KPddNameSpi,"spi.pdd");
#endif

#ifndef STANDALONE_CHANNEL
LOCAL_C TInt8 AssignChanNum()
	{
	static TInt8 iBaseChanNum = KSpiChannelNumBase;
	SPI_PRINT(("SPI AssignChanNum - on entry, iBaseCanNum = 0x%x\n",iBaseChanNum));
	return iBaseChanNum++; // Arbitrary, for illustration
	}
#endif

NONSHARABLE_CLASS(DSimulatedSpiDevice) : public DPhysicalDevice
	{
// Class to faciliate loading of the IIC classes
public:
	class TCaps
		{
	public:
		TVersion iVersion;
		};
public:
	DSimulatedSpiDevice();
	virtual TInt Install();
	virtual TInt Create(DBase*& aChannel, TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
	virtual TInt Validate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
	virtual void GetCaps(TDes8& aDes) const;
	inline static TVersion VersionRequired();
	};

TVersion DSimulatedSpiDevice::VersionRequired()
	{
	SPI_PRINT(("DSimulatedSpiDevice::VersionRequired\n"));
	return TVersion(KIicClientMajorVersionNumber,KIicClientMinorVersionNumber,KIicClientBuildVersionNumber);
	}

/** Factory class constructor */
DSimulatedSpiDevice::DSimulatedSpiDevice()
	{
	SPI_PRINT(("DSimulatedSpiDevice::DSimulatedSpiDevice\n"));
    iVersion = DSimulatedSpiDevice::VersionRequired();
	}

TInt DSimulatedSpiDevice::Install()
    {
	SPI_PRINT(("DSimulatedSpiDevice::Install\n"));
    return(SetName(&KPddNameSpi));
    }

/**  Called by the kernel's device driver framework to create a Physical Channel. */
TInt DSimulatedSpiDevice::Create(DBase*& /*aChannel*/, TInt /*aUint*/, const TDesC8* /*anInfo*/, const TVersion& /*aVer*/)
    {
	SPI_PRINT(("DSimulatedSpiDevice::Create\n"));
    return KErrNone;
    }

/**  Called by the kernel's device driver framework to check if this PDD is suitable for use with a Logical Channel.*/
TInt DSimulatedSpiDevice::Validate(TInt /*aUnit*/, const TDesC8* /*anInfo*/, const TVersion& aVer)
    {
	SPI_PRINT(("DSimulatedSpiDevice::Validate\n"));
   	if (!Kern::QueryVersionSupported(DSimulatedSpiDevice::VersionRequired(),aVer))
		return(KErrNotSupported);
    return KErrNone;
    }

/** Return the driver capabilities */
void DSimulatedSpiDevice::GetCaps(TDes8& aDes) const
    {
	SPI_PRINT(("DSimulatedSpiDevice::GetCaps\n"));
	// Create a capabilities object
	TCaps caps;
	caps.iVersion = iVersion;
	// Zero the buffer
	TInt maxLen = aDes.MaxLength();
	aDes.FillZ(maxLen);
	// Copy capabilities
	TInt size=sizeof(caps);
	if(size>maxLen)
	   size=maxLen;
	aDes.Copy((TUint8*)&caps,size);
    }

// supported channels for this implementation
static DIicBusChannel* ChannelPtrArray[NUM_CHANNELS];

//DECLARE_EXTENSION_WITH_PRIORITY(BUS_IMPLMENTATION_PRIORITY)	
DECLARE_STANDARD_PDD()		// SPI test driver to be explicitly loaded as an LDD, not kernel extension
	{
	SPI_PRINT(("\n\nSPI PDD, channel creation loop follows ...\n"));

#ifndef STANDALONE_CHANNEL
	DIicBusChannel* chan=NULL;
	for(TInt i=0; i<NUM_CHANNELS; i++)
		{
	SPI_PRINT(("\n"));
		if(CHANNEL_TYPE(i) == (DIicBusChannel::EMaster))
			{
			chan=new DSimulatedIicBusChannelMasterSpi(BUS_TYPE,CHANNEL_DUPLEX(i));
			if(!chan)
				return NULL;
			SPI_PRINT(("SPI chan created at 0x%x\n",chan));
			if(((DSimulatedIicBusChannelMasterSpi*)chan)->Create()!=KErrNone)
				return NULL;
			}
		else if(CHANNEL_TYPE(i) == DIicBusChannel::EMasterSlave)
			{
			DIicBusChannel* chanM=new DSimulatedIicBusChannelMasterSpi(BUS_TYPE,CHANNEL_DUPLEX(i));
			if(!chanM)
				return NULL;
			DIicBusChannel* chanS=new DSimulatedIicBusChannelSlaveSpi(BUS_TYPE,CHANNEL_DUPLEX(i));
			if(!chanS)
				return NULL;
			// For MasterSlave channel, the channel number for both the Master and Slave channels must be the same
			TInt8 msChanNum = ((DSimulatedIicBusChannelMasterSpi*)chanM)->GetChanNum();
			((DSimulatedIicBusChannelSlaveSpi*)chanS)->SetChanNum(msChanNum);

			chan=new DIicBusChannelMasterSlave(BUS_TYPE,CHANNEL_DUPLEX(i),(DIicBusChannelMaster*)chanM,(DIicBusChannelSlave*)chanS); // Generic implementation
			if(!chan)
				return NULL;
			SPI_PRINT(("SPI chan created at 0x%x\n",chan));
			if(((DIicBusChannelMasterSlave*)chan)->DoCreate()!=KErrNone)
				return NULL;
			}
		else
			{
			chan=new DSimulatedIicBusChannelSlaveSpi(BUS_TYPE,CHANNEL_DUPLEX(i));
			if(!chan)
				return NULL;
			SPI_PRINT(("SPI chan created at 0x%x\n",chan));
			if(((DSimulatedIicBusChannelSlaveSpi*)chan)->Create()!=KErrNone)
				return NULL;
			}
		ChannelPtrArray[i]=chan;
		}
	SPI_PRINT(("\nSPI PDD, channel creation loop done- about to invoke RegisterChannels\n\n"));
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_REGISTERCHANS_START_PSL_TRACE;
#endif

	TInt r = KErrNone;
	r=DIicBusController::RegisterChannels(ChannelPtrArray,NUM_CHANNELS);

#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_REGISTERCHANS_END_PSL_TRACE;
#endif
	SPI_PRINT(("\nSPI - returned from RegisterChannels with r=%d\n",r));
	if(r!=KErrNone)
		{
		delete chan;
		return NULL;
		}
#endif
	return new DSimulatedSpiDevice;
	}

#ifdef STANDALONE_CHANNEL
EXPORT_C
#endif
	DSimulatedIicBusChannelMasterSpi::DSimulatedIicBusChannelMasterSpi(const TBusType aBusType, const TChannelDuplex aChanDuplex)
	: DIicBusChannelMaster(aBusType,aChanDuplex)
	{
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::DSimulatedIicBusChannelMasterSpi, aBusType=%d,aChanDuplex=%d\n",aBusType,aChanDuplex));
#ifndef STANDALONE_CHANNEL
	iChannelNumber = AssignChanNum();
#endif
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::DSimulatedIicBusChannelMasterSpi, iChannelNumber=%d\n",iChannelNumber));
	iTestState = ETestNone;
	iChannelState = EIdle;
	}

// The time-out call back invoked when the Slave exeecds the allowed response time
TInt DSimulatedIicBusChannelMasterSpi::HandleSlaveTimeout()
	{
	SPI_PRINT(("HandleSlaveTimeout \n"));
	return AsynchStateMachine(ETimeExpired); 
	}

TInt DSimulatedIicBusChannelMasterSpi::DoCreate()
	{
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::DoCreate\n"));
	TInt r=Init();	// PIL Base class initialisation
	r=Kern::DynamicDfcQCreate(iDynamicDfcQ,KSpiThreadPriority,KSpiThreadName);
	if(r == KErrNone)
		SetDfcQ((TDfcQue*)iDynamicDfcQ);
	DSimulatedIicBusChannelMasterSpi::SetRequestDelayed(this,EFalse);
	return r;
	}

TInt DSimulatedIicBusChannelMasterSpi::CheckHdr(TDes8* aHdr)
	{
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CheckHdr\n"));

	TConfigSpiBufV01* spiBuf = (TConfigSpiBufV01*)aHdr;
	TConfigSpiV01* spiPtr = &((*spiBuf)());

	// Valid values for the device ID will depend on the bus configuration
	//
	// Check that the values for word width, clock speed and clock mode are recognised
	if((spiPtr->iWordWidth < 0) || (spiPtr->iWordWidth > ESpiWordWidth_16))
		{
		SPI_PRINT(("ERROR: DSimulatedIicBusChannelMasterSpi::CheckHdr unrecognised word width identifier %d\n",spiPtr->iWordWidth));
		return KErrArgument;
		}
	if(spiPtr->iClkSpeedHz < 0)
		{
		SPI_PRINT(("ERROR: DSimulatedIicBusChannelMasterSpi::CheckHdr negative clock speed specified %d\n",spiPtr->iClkSpeedHz));
		return KErrArgument;
		}
	if((spiPtr->iClkMode < 0) || (spiPtr->iClkMode > ESpiPolarityHighRisingEdge))
		{
		SPI_PRINT(("ERROR: DSimulatedIicBusChannelMasterSpi::CheckHdr unrecognised clock mode identifier %d\n",spiPtr->iClkMode));
		return KErrArgument;
		}
	// Values for the timeout period are arbitrary - can only check it is not a negative value
	if(spiPtr->iTimeoutPeriod < 0)
		{
		SPI_PRINT(("ERROR: DSimulatedIicBusChannelMasterSpi::CheckHdr negative timeout period %d\n",spiPtr->iTimeoutPeriod));
		return KErrArgument;
		}
	if((spiPtr->iEndianness < 0) || (spiPtr->iEndianness > ELittleEndian))
		{
		SPI_PRINT(("ERROR: DSimulatedIicBusChannelMasterSpi::CheckHdr unrecognised endianness identifier %d\n",spiPtr->iEndianness));
		return KErrArgument;
		}
	if((spiPtr->iBitOrder < 0) || (spiPtr->iBitOrder > EMsbFirst))
		{
		SPI_PRINT(("ERROR: DSimulatedIicBusChannelMasterSpi::CheckHdr unrecognised bit order identifier %d\n",spiPtr->iBitOrder));
		return KErrArgument;
		}
	if((spiPtr->iSSPinActiveMode < 0) || (spiPtr->iSSPinActiveMode > ESpiCSPinActiveHigh))
		{
		SPI_PRINT(("ERROR: DSimulatedIicBusChannelMasterSpi::CheckHdr unrecognised Slave select pin mode identifier %d\n",spiPtr->iSSPinActiveMode));
		return KErrArgument;
		}
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CheckHdr word width = %d\n",spiPtr->iWordWidth));
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CheckHdr clock speed = %d\n",spiPtr->iClkSpeedHz));
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CheckHdr clock mode = %d\n",spiPtr->iClkMode));
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CheckHdr timeout period = %d\n",spiPtr->iTimeoutPeriod));
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CheckHdr endianness = %d\n",spiPtr->iEndianness));
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CheckHdr bit order = %d\n",spiPtr->iBitOrder));
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CheckHdr transaction wait cycles = %d\n",spiPtr->iTransactionWaitCycles));
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CheckHdr slave select pin mode = %d\n",spiPtr->iSSPinActiveMode));

	// For the set of tests expecft the following values
	// ESpiWordWidth_8, 100kHz, ESpiPolarityLowRisingEdge,aTimeoutPeriod=100
	// EBigEndian, EMsbFirst, 10 wait cycles, Slave select active low
	if(	(spiPtr->iWordWidth != ESpiWordWidth_8)	||
		(spiPtr->iClkSpeedHz != 100000)	||
		(spiPtr->iClkMode != ESpiPolarityLowRisingEdge)	||
		(spiPtr->iTimeoutPeriod != 100) ||
		(spiPtr->iEndianness != ELittleEndian) ||
		(spiPtr->iBitOrder != EMsbFirst) ||
		(spiPtr->iTransactionWaitCycles != 10) ||
		(spiPtr->iSSPinActiveMode != ESpiCSPinActiveLow) )
		return KErrCorrupt;
	return KErrNone;
	}

TInt DSimulatedIicBusChannelMasterSpi::CompareTransactionOne(TIicBusTransaction* aTransaction)
// Compares the indicated TIicBusTransaction with the expected content
// Returns KErrNone if there is a match, KErrCorrupt otherwise.
	{
	TInt i;
	// Check the transaction header
	// Should contain the default header for SPI
	TDes8* bufPtr = GetTransactionHeader(aTransaction);
	if(bufPtr == NULL)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - NULL header\n"));
		return KErrCorrupt;
		}
	TConfigSpiV01 *buf = (TConfigSpiV01 *)(bufPtr->Ptr());
	if(buf->iWordWidth != ESpiWordWidth_8)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - Header wordwidth mis-match\n"));
		return KErrCorrupt;
		}
	if(buf->iClkSpeedHz !=100000)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - Header clockspeed mis-match\n"));
		return KErrCorrupt;
		}
	if(buf->iClkMode != ESpiPolarityLowRisingEdge)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - Header polarity mis-match\n"));
		return KErrCorrupt;
		}
	if(buf->iTimeoutPeriod != 100)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - Header timeout mis-match\n"));
		return KErrCorrupt;
		}

	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne header OK\n"));
	
	// Check the half-duplex transfer list
	TIicBusTransfer* tfer = GetTransHalfDuplexTferPtr(aTransaction);
	if(tfer == NULL)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - NULL half-duplex transfer\n"));
		return KErrCorrupt;
		}
	// Process each transfer in the list
	TInt8 dummy;

	// tfer1 = new TIicBusTransfer(TIicBusTransfer::EMasterWrite,8,buf1);
	// buf1 contains copy of TUint8 KTransOneTferOne[21] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
	dummy=GetTferType(tfer);
	if(dummy!=TIicBusTransfer::EMasterWrite)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - tfer1 type=%d\n"));
		return KErrCorrupt;
		}
	dummy=GetTferBufGranularity(tfer);
	if(dummy!=8)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - tfer1 granularity=%d\n",dummy));
		return KErrCorrupt;
		}
	if((bufPtr = (TDes8*)GetTferBuffer(tfer)) == NULL)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - tfer1 buffer NULL\n"));
		return KErrCorrupt;
		}
	for(i=0;i<21;++i)
		{
		if((*bufPtr)[i]!=i)
			{
			SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR tfer1 buffer element %d has 0x%x\n",i,(*bufPtr)[i]));
			return KErrCorrupt;
			}
		}
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne tfer1 OK\n"));


	tfer=GetTferNextTfer(tfer);
	// tfer2 = new TIicBusTransfer(TIicBusTransfer::EMasterRead,8,buf2);
	// buf2 contains copy of TUint8 KTransOneTferTwo[8] = {17,18,19,20,21,22,23,24};
	dummy=GetTferType(tfer);
	if(dummy!=TIicBusTransfer::EMasterRead)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - tfer2 type=%d\n",dummy));
		return KErrCorrupt;
		}
	dummy=GetTferBufGranularity(tfer);
	if(dummy!=8)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - tfer2 granularity=%d\n",dummy));
		return KErrCorrupt;
		}
	if((bufPtr = (TDes8*)GetTferBuffer(tfer)) == NULL)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - tfer2 buffer NULL\n"));
		return KErrCorrupt;
		}
	for(i=0;i<8;++i)
		{
		if((*bufPtr)[i]!=(17+i))
			{
			SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR tfer2 buffer element %d has 0x%x\n",i,(*bufPtr)[i]));
			return KErrCorrupt;
			}
		}
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne tfer2 OK\n"));

	tfer=GetTferNextTfer(tfer);
	// tfer3 = new TIicBusTransfer(TIicBusTransfer::EMasterWrite,8,buf3);
	// buf2 contains copy of TUint8 KTransOneTferThree[6] = {87,85,83,81,79,77};
	dummy=GetTferType(tfer);
	if(dummy!=TIicBusTransfer::EMasterWrite)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - tfer3 type=%d\n"));
		return KErrCorrupt;
		}
	dummy=GetTferBufGranularity(tfer);
	if(dummy!=8)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - tfer3 granularity=%d\n",dummy));
		return KErrCorrupt;
		}
	if((bufPtr = (TDes8*)GetTferBuffer(tfer)) == NULL)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - tfer3 buffer NULL\n"));
		return KErrCorrupt;
		}
	for(i=0;i<6;++i)
		{
		if((*bufPtr)[i]!=(87-(2*i)))
			{
			SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR tfer3 buffer element %d has 0x%x\n",i,(*bufPtr)[i]));
			return KErrCorrupt;
			}
		}
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne tfer3 OK\n"));

	// Shouldn't be any more transfers in the half duplex list
	if((tfer=GetTferNextTfer(tfer))!=NULL)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - tfer3 iNext=0x%x\n",tfer));
		return KErrCorrupt;
		}
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne half-duplex transfer OK\n"));

	// The full duplex transfer should be represented by a NULL pointer
	if((tfer=GetTransFullDuplexTferPtr(aTransaction))!=NULL)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - full duplex pointer=0x%x\n",tfer));
		return KErrCorrupt;
		}
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne full duplex pointer is NULL (OK)\n"));

	// Synchronous transaction, so check the callback pointer is NULL
	TIicBusCallback* dumCb = NULL;
	dumCb=GetTransCallback(aTransaction);
	if(dumCb!=NULL)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - callback pointer=0x%x\n",dumCb));
		return KErrCorrupt;
		}
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne callback pointer is NULL (OK)\n"));

	// Check the transaction flags are set to zero
	TUint8 dumFlags;
	dumFlags=GetTransFlags(aTransaction);
	if(dumFlags!=0)
		{
		SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne ERROR - flags=0x%x\n",dumFlags));
		return KErrCorrupt;
		}
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::CompareTransactionOne flags are zero (OK)\n"));

	return KErrNone;
	}


// The THwCallbackFunc gets called if the hardware preparation completes successfully.
void THwCallbackFunc(TAny* aPtr)
	{
	SPI_PRINT(("Hardware preparation completed, calling the callback function"));
	DSimulatedIicBusChannelMasterSpi* aChanMasterSpi=(DSimulatedIicBusChannelMasterSpi* )aPtr;
	TInt r = aChanMasterSpi->DoSimulatedTransaction();
	((DSimulatedIicBusChannelMasterSpi*)aChanMasterSpi)->CompleteReq(r);
	}

TInt DSimulatedIicBusChannelMasterSpi::DoSimulatedTransaction()
	{
	TInt r = AsynchStateMachine(EHwTransferDone);
	if(iTimeoutTimer.Cancel() == FALSE)
		{
		SPI_PRINT(("timer is not cancelled"));
		}
	return r;
	}

TInt DSimulatedIicBusChannelMasterSpi::DoHwPreparation()
	{
	SPI_PRINT(("Preparing hardware for the transaction"));
	
	TInt r = KErrNone;
	// The hardware preparation can either complete successfully or fail. 
	// If successful, invoke the callback function of THwDoneCallBack
	// if not, execute the timeout machanism
	// Currently DoHwPreparation is just a simple function. It should be more complicated 
	// for the real hardware.
	switch(iTestState)
		{
		case (ETestSlaveTimeOut):
			{
			// In the timeout test, do nothing until the timeout callback function is invoked.
			SPI_PRINT(("Simulating the timeout process."));
			break;
			}
		case (ETestNone):
			{
			// Pretend the hardware preparation's been done, and a callback function is invoked to call 
			// the Asynchronous State Machine
			SPI_PRINT(("Hardware preparing work is executing normally."));
			iCb->Enque();
			break;
			}
		default:
			{
			SPI_PRINT(("Not a valid test."));
			r = KErrNotSupported;
			break;
			}	
		}
	
	return r; 
	}

// Gateway function for PSL implementation, invoked for DFC processing
TInt DSimulatedIicBusChannelMasterSpi::DoRequest(TIicBusTransaction* aTransaction)
	{
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::DoRequest invoked with aTransaction=0x%x\n",aTransaction));
	
	TInt r = KErrNone;
	iCurrTrans=aTransaction;
	
	// Check if the Asynchronous State Machine is available. If not, then return KErrInUse. 
	// This is used to stop the second transaction executing if the machine has already been holding 
	// by a transaction. However, this situation cannot be tested because of the malfunction in PIL
	if(iChannelState!= EIdle)
		return KErrInUse;
	
	iChannelState = EBusy;
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_MPROCESSTRANS_START_PSL_TRACE;
#endif
	r = ProcessTrans(aTransaction);

	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::DoRequest - exiting\n"));
	return r;
	}

TInt DSimulatedIicBusChannelMasterSpi::ProcessTrans(TIicBusTransaction* aTransaction)
		{
		TInt r=KErrNone;
		
		switch(iTestState)
			{
			case(ETestWaitTransOne):
				{
				// The transaction received should exhibit the expected data
				// Return KErrArgument if this is not the case
				// The timer should be started at the beginning of every transaction
				// For simplicity, we omit the timer here. 
				r=CompareTransactionOne(iCurrTrans);
				iChannelState = EIdle;
				CompleteRequest(KErrNone);
				break;
				}
			case(ETestSlaveTimeOut):
				{
				// Test the timeout funciton
				SPI_PRINT(("Test the slave timeout function\n"));
				
				// Simulate a timeout 
				// Start a timer and then wait for the Slave response to timeout
				// A real bus would use its own means to identify a timeout
				TInt aTime=1000000/NKern::TickPeriod();
				r = StartSlaveTimeOutTimer(aTime);
				if(r != KErrNone)
					return r;
				r = DoHwPreparation();
				break;
				}
			case(ETestWaitPriorityTest):
				{	
				static TInt TranCount = 0;
				if(TranCount >= KPriorityTestNum) return KErrUnknown;
				// block the channel
				while(IsRequestDelayed(this))
					{
					SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::DoRequest - starting Sleep...\n"));
					NKern::Sleep(1000);	// 1000 is arbitrary
					SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::DoRequest - completed Sleep, check if still delayed\n"));
					}; 
				// get transaction header			
				TDes8* bufPtr = GetTransactionHeader(aTransaction);
				if(bufPtr == NULL)
				{
				SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::DoRequest ERROR - NULL header\n"));
				return KErrCorrupt;
				}

				if(TranCount == 0)
					{			
					// ignore the blocking transaction
					TranCount++;
					}
				else
					{
					// store transaction header
					iPriorityTestResult[TranCount++] = (*bufPtr)[0];
					SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::DoRequest Priority test transaction no.:%d Priority:%d", 
						(*bufPtr)[0], aTransaction->iKey));
					}
				iChannelState = EIdle;
				CompleteRequest(KErrNone);
				if(TranCount == KPriorityTestNum) iPriorityTestDone = ETrue;
				break;
				}



			case(ETestNone):
				{
				SPI_PRINT(("Nothing to be tested, just do the usual transaction"));
				
				// Start the timer on the Slave response. 
				// Since no timeout, this timer will be cancelled in the THwCallbackFunc
				r = StartSlaveTimeOutTimer(100000);
				if(r != KErrNone)
					return r;
				// Use a class THwDoneCallBack derived from TDfc to trigger the asynchronous state machine 
				// when the hardware preparation completes successfully. 
				// The priority is set with an arbitrary number 5
				iCb = new THwDoneCallBack(THwCallbackFunc, this, iDynamicDfcQ, 5);
				r = DoHwPreparation(); 
				break;
				}
			default:
				{
				SPI_PRINT(("Test status not matched"));
				return KErrNotSupported;
				}
			}

		return r;
		}
	
	
TInt DSimulatedIicBusChannelMasterSpi::AsynchStateMachine(TInt aReason)
	{
	TInt r=KErrNone; 
	
	// The asynchronous state machine has two states, it could either be idle or busy.
	// Initially, it's in idle state. When a user queues a transaction, the hardware preparation starts
	// and the state changes to busy. After the hardware preparation completes, either successfully or not, 
	// the state machine will do the corresponding work for the transaction and then goes back to the idle state.  
	switch(iChannelState)
		{
		case(EIdle):
			{
			 return KErrGeneral;
			}
		case (EBusy):
			{
			switch(aReason)
				{
				case(EHwTransferDone):
					{
								
					// Simulate processing - for now, do nothing!
					//
					SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine aTransaction->iHeader=0x%x\n",GetTransactionHeader(iCurrTrans)));
					SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine aTransaction->iHalfDuplexTrans=0x%x\n",GetTransHalfDuplexTferPtr(iCurrTrans)));
					SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine aTransaction->iFullDuplexTrans=0x%x\n",GetTransFullDuplexTferPtr(iCurrTrans)));
					SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine aTransaction->iCallback=0x%x\n",GetTransCallback(iCurrTrans)));
					SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine aTransaction->iFlags=0x%x\n",GetTransFlags(iCurrTrans)));

					SPI_PRINT(("\nDSimulatedIicBusChannelMasterSpi::AsynchStateMachine, iHeader info \n"));
					TDes8* bufPtr = GetTransactionHeader(iCurrTrans);
					if(bufPtr == NULL)
						{
						SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine ERROR - NULL header\n"));
						return KErrCorrupt;
						}
					TConfigSpiV01 *buf = (TConfigSpiV01 *)(bufPtr->Ptr());
					SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine, header word width=0x%x\n",buf->iWordWidth));
					SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine, header clock speed=0x%x\n",buf->iClkSpeedHz));
					SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine, header clock mode=0x%x\n",buf->iClkMode));
					SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine, header timeout period=0x%x\n",buf->iTimeoutPeriod));
					SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine, header endianness=0x%x\n",buf->iEndianness));
					SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine, header bit order=0x%x\n",buf->iBitOrder));
					SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine, header wait cycles=0x%x\n",buf->iTransactionWaitCycles));
					SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine, header Slave select pin mode=0x%x\n",buf->iSSPinActiveMode));
					(void)buf;	// Silence compiler when SPI_PRINT not used
							
					SPI_PRINT(("\nDSimulatedIicBusChannelMasterSpi::AsynchStateMachine, iHalfDuplexTrans info \n"));
					TIicBusTransfer* halfDuplexPtr=GetTransHalfDuplexTferPtr(iCurrTrans);
					while(halfDuplexPtr != NULL)
						{
						SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine transfer type=0x%x\n",GetTferType(halfDuplexPtr)));
						SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine granularity=0x%x\n",GetTferBufGranularity(halfDuplexPtr)));
						SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine transfer buffer=0x%x\n",GetTferBuffer(halfDuplexPtr)));
						SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine next transfer =0x%x\n",GetTferNextTfer(halfDuplexPtr)));
						halfDuplexPtr=GetTferNextTfer(halfDuplexPtr);
						}
					SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine - End of iHalfDuplexTrans info"));
					
					while(IsRequestDelayed(this))
						{
						SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine - starting Sleep...\n"));
						NKern::Sleep(1000);	// 1000 is arbitrary
						SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::AsynchStateMachine - completed Sleep, check if still delayed\n"));
						}; 
				
					iChannelState=EIdle; 
					delete iCb;
					break;
					}
				case(ETimeExpired):
					{
					SPI_PRINT(("Time expired, the Asynchrnous State Machine will be Idle again, and wait for the next request."));
					iChannelState=EIdle;
					r = KErrTimedOut; 
					break;
					}
				default:
					{
					SPI_PRINT(("Request can not be handled, return error code."));
					return KErrNotSupported;
					}
				}
			break;
			}
		default:
			{
			SPI_PRINT(("No matched state"));
			return KErrGeneral;
			}
		}
	return r; 
	}


TBool DSimulatedIicBusChannelMasterSpi::IsRequestDelayed(DSimulatedIicBusChannelMasterSpi* aChan)
	{
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::IsRequestDelayed invoked for aChan=0x%x\n",aChan));
	return aChan->iReqDelayed;
	}

void DSimulatedIicBusChannelMasterSpi::SetRequestDelayed(DSimulatedIicBusChannelMasterSpi* aChan,TBool aDelay) 
	{
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::SetRequestDelayed invoked for aChan=0x%x, with aDelay=0x%d\n",aChan,aDelay));
	aChan->iReqDelayed=aDelay;
	}

TInt DSimulatedIicBusChannelMasterSpi::StaticExtension(TUint aFunction, TAny* aParam1, TAny* aParam2)
	{
	SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::StaticExtension\n"));
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_MSTATEXT_START_PSL_TRACE;
#endif
	(void)aParam1;
	(void)aParam2;
	TInt r = KErrNone;
	// Test values of aFunction were shifted left one place by the (test) client driver
	// and for Slave values the two msb were cleared
	// Return to its original value.
	if(aFunction>KTestControlIoPilOffset)
		aFunction >>= 1;
	switch(aFunction)
		{
		case(RBusDevIicClient::ECtlIoDumpChan):
			{
#ifdef _DEBUG
			DumpChannel();
#endif
			break;
			}
		case(RBusDevIicClient::ECtlIoBlockReqCompletion):
			{
			SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::Blocking request completion\n"));
			SetRequestDelayed(this, ETrue);
			break;
			}
		case(RBusDevIicClient::ECtlIoUnblockReqCompletion):
			{
			SPI_PRINT(("DSimulatedIicBusChannelMasterSpi::Unlocking request completion\n"));
			SetRequestDelayed(this, EFalse);
			break;
			}
		case(RBusDevIicClient::ECtlIoDeRegChan):
			{
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_DEREGISTERCHAN_START_PSL_TRACE;
#endif
			SPI_PRINT(("DSimulatedIicBusChannelMasterSpi: deregister channel\n"));
#ifndef STANDALONE_CHANNEL
			r=DIicBusController::DeRegisterChannel(this);
#endif

#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_DEREGISTERCHAN_END_PSL_TRACE;
#endif
			break;
			}

		case(RBusDevIicClient::ECtlIoPriorityTest):
			{
			SPI_PRINT(("DSimulatedIicBusChannelMasterSpi: warned to expect priority test\n"));
			iPriorityTestDone = EFalse;
			iTestState=ETestWaitPriorityTest;
			break;
			}
		case(RBusDevIicClient::EGetTestResult):
			{
			if(!iPriorityTestDone) return KErrNotReady;
			
			SPI_PRINT(("DSimulatedIicBusChannelMasterSpi: get priority test order\n"));

			//iPriorityTestResult[0] is the blocking transaction, ignore it. start from entry 1.
			for(TInt i=1; i<KPriorityTestNum; i++)
				{
				if(iPriorityTestResult[i]!=(KPriorityTestNum-i-1))
					{
					r = KErrGeneral;
					break;
					}
				}
				r = KErrNone;
			break;
			}

		case(RBusDevIicClient::ECtlIoTracnOne):
			{
			SPI_PRINT(("DSimulatedIicBusChannelMasterSpi: warned to expect Transaction One\n"));
			iTestState=ETestWaitTransOne;
			break;
			}
		case(RBusDevIicClient::ECtlIoNone):
			{
			SPI_PRINT(("DSimulatedIicBusChannelMasterSpi: terminate ControlIO state\n"));
			iTestState=ETestNone;
			break;
			}
		case(RBusDevIicClient::ECtlIoSetTimeOutFlag):
			{
			SPI_PRINT(("DSimulatedIicBusChannelMasterSpi: test slave time out\n"));
			iTestState=ETestSlaveTimeOut;
			break;
			}
		default:
			{
			Kern::Printf("aFunction %d is not recognised \n",aFunction);
			r=KErrNotSupported;
			}
		}
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_MSTATEXT_END_PSL_TRACE;
#endif		
	return r;
	}

void DSimulatedIicBusChannelMasterSpi::CompleteReq(TInt aResult)
	{
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_MPROCESSTRANS_END_PSL_TRACE;
#endif
	CompleteRequest(aResult);
	}


void DSimulatedIicBusChannelSlaveSpi::SlaveAsyncSimCallback(TAny* aPtr)
	{
	SPI_PRINT(("SlaveAsyncSimCallback\n"));
	DSimulatedIicBusChannelSlaveSpi* channel = (DSimulatedIicBusChannelSlaveSpi*)aPtr;
	TInt r=KErrNone;	// Just simulate successfull capture
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_SCAPTCHANASYNC_END_PSL_TRACE;
#endif
	channel->ChanCaptureCb(r);
	}

#ifdef STANDALONE_CHANNEL
EXPORT_C
#endif
	DSimulatedIicBusChannelSlaveSpi::DSimulatedIicBusChannelSlaveSpi(const DIicBusChannel::TBusType aBusType, const DIicBusChannel::TChannelDuplex aChanDuplex)
	: DIicBusChannelSlave(aBusType,aChanDuplex,0), // 0 to be ignored by base class
	iSlaveTimer(SlaveAsyncSimCallback,this)
	{
	SPI_PRINT(("DSimulatedIicBusChannelSlaveSpi::DSimulatedIicBusChannelSlaveSpi, aBusType=%d,aChanDuplex=%d\n",aBusType,aChanDuplex));
#ifndef STANDALONE_CHANNEL
	iChannelNumber = AssignChanNum();
#endif
	SPI_PRINT(("DSimulatedIicBusChannelSlaveSpi::DSimulatedIicBusChannelSlaveSpi, iChannelNumber=%d\n",iChannelNumber));
	}

TInt DSimulatedIicBusChannelSlaveSpi::CaptureChannelPsl(TDes8* /*aConfigHdr*/, TBool aAsynch)
	{
	SPI_PRINT(("DSimulatedIicBusChannelSlaveSpi::CaptureChannelPsl, aAsynch=%d\n",aAsynch));
	TInt r = KErrNone;
	if(aAsynch)
		{
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_SCAPTCHANASYNC_START_PSL_TRACE;
#endif
		// To simulate an asynchronous operation, just set a timer to expire
		iSlaveTimer.OneShot(1000, ETrue); // Arbitrary timeout - expiry executes callback in context of DfcThread1
		}
	else
		{
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_SCAPTCHANSYNC_START_PSL_TRACE;
#endif
		// PSL processing would happen here ...
		// Expected to include implementation of the header configuration information 
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_SCAPTCHANSYNC_END_PSL_TRACE;
#endif
		}
	SPI_PRINT(("DSimulatedIicBusChannelSlaveI2c::CaptureChanSync ... no real processing to do \n"));

	return r;
	}

TInt DSimulatedIicBusChannelSlaveSpi::CheckHdr(TDes8* /*aHdr*/)
	{
	SPI_PRINT(("DSimulatedIicBusChannelSlaveSpi::CheckHdr\n"));
	return KErrNone;
	}

TInt DSimulatedIicBusChannelSlaveSpi::DoCreate()
	{
	SPI_PRINT(("DSimulatedIicBusChannelSlaveSpi::DoCreate\n"));
	TInt r=Init();	// PIL Base class initialisation
	return r;
	}

TInt DSimulatedIicBusChannelSlaveSpi::DoRequest(TInt /*aTrigger*/)
	{
	SPI_PRINT(("DSimulatedIicBusChannelSlaveSpi::DoRequest\n"));
	return KErrNotSupported; 
	}

void DSimulatedIicBusChannelSlaveSpi::ProcessData(TInt /*aTrigger*/, TIicBusSlaveCallback*  /*aCb*/)
	{
	SPI_PRINT(("DSimulatedIicBusChannelSlaveSpi::ProcessData\n"));
	}

TInt DSimulatedIicBusChannelSlaveSpi::StaticExtension(TUint aFunction, TAny* aParam1, TAny* aParam2)
	{
	SPI_PRINT(("DSimulatedIicBusChannelSlaveSpi::StaticExtension\n"));
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_SSTATEXT_START_PSL_TRACE;
#endif
	(void)aParam1;
	(void)aParam2;
	// Test values of aFunction were shifted left one place by the (test) client driver
	// and for Slave values the two msb were cleared
	// Return to its original value.
	if(aFunction>KTestControlIoPilOffset)
		{
		aFunction |= 0xC0000000;
		aFunction >>= 1;
		}
	TInt r = KErrNone;
	switch(aFunction)
		{
		case(RBusDevIicClient::ECtlIoDumpChan):
			{
#ifdef _DEBUG
			DumpChannel();
#endif
			break;
			}
		default:
			{
			Kern::Printf("aFunction %d is not recognised \n",aFunction);
			r=KErrNotSupported;
			}
		}

#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_SSTATEXT_START_PSL_TRACE;
#endif
	(void)aFunction;
	return r;
	}