// 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;
	}






