kerneltest/e32test/iic/iic_psl/iic_slaveclient.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 0 a41df078684a
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_slaveclient.cpp
// Simulated client of IIC Platform Independent Layer (PIL) Slave functionality
//

#include <kernel/kern_priv.h>		// for DThread, TDfc
#ifdef STANDALONE_CHANNEL
#include <drivers/iic_transaction.h>
#else
#include <drivers/iic.h>
#endif
#include "../t_iic.h"

#ifdef STANDALONE_CHANNEL
#include <drivers/iic_channel.h>
#include "i2c.h"
#endif
#ifdef LOG_SLAVECLIENT
#define CLIENT_PRINT(str) Kern::Printf str
#else
#define CLIENT_PRINT(str)
#endif

//For channel creation
#ifdef STANDALONE_CHANNEL
#define NUM_CHANNELS 3 // Arbitrary

#if defined(MASTER_MODE) && !defined(SLAVE_MODE)
const TInt KChannelTypeArray[NUM_CHANNELS] = {DIicBusChannel::EMaster, DIicBusChannel::EMaster, DIicBusChannel::EMaster};
#elif defined(MASTER_MODE) && defined(SLAVE_MODE)
const TInt KChannelTypeArray[NUM_CHANNELS] = {DIicBusChannel::EMaster, DIicBusChannel::ESlave, DIicBusChannel::EMasterSlave};
#else
const TInt KChannelTypeArray[NUM_CHANNELS] = {DIicBusChannel::ESlave, DIicBusChannel::ESlave, DIicBusChannel::ESlave};
#endif
#define CHANNEL_TYPE(n) (KChannelTypeArray[n])
#define CHANNEL_DUPLEX(n) (DIicBusChannel::EHalfDuplex)
#define BUS_TYPE (DIicBusChannel::EI2c)

#if defined(MASTER_MODE)
const TInt8 KI2cChannelNumBase = 10;	// Arbitrary, real platform may consult the Configuration Repository
										// Note limit of 5 bit representation (0-31)

#else
const TInt8 KI2cChannelNumBase = 10 + NUM_CHANNELS;	// For Slave mode, want to provide different response
													// If client assumes Master mode, should be informed not available
#endif/*MASTER_MODE*/

LOCAL_C TInt8 AssignChanNumI2c()
	{
	static TInt8 iBaseChanNumI2c = KI2cChannelNumBase;
	CLIENT_PRINT(("I2C AssignChanNum - on entry, iBaseChanNum = 0x%x\n",iBaseChanNumI2c));
	return iBaseChanNumI2c++; // Arbitrary, for illustration
	}

class DIicSlaveClientChan : public DBase
	{
public:
	DIicSlaveClientChan(DIicBusChannel* aChan, TInt8 aChanNum, TInt aChanType):iChan(aChan),iChanNumber(aChanNum),iChanType(aChanType){};
	~DIicSlaveClientChan();
	TInt GetChanNum()const {return iChanNumber;};
	TInt GetChanType()const {return iChanType;};
	DIicBusChannel* GetChannelPtr(){return iChan;};
private:
	TInt8 iChanNumber;
	TInt iChanType;
	DIicBusChannel* iChan;
	};

DIicSlaveClientChan::~DIicSlaveClientChan()
	{
	if(iChan)
		delete iChan;
	}

#endif/*STANDALONE_CHANNEL*/

const TInt KIicSlaveClientThreadPriority = 24;

const TInt KIicSlaveClientDfcPriority = 3; // 0 to 7, 7 is highest

const TInt KMaxNumChannels = 2;	// 1 "true" slave, one "dummy"

#ifdef STANDALONE_CHANNEL
_LIT(KLddRootName,"iic_slaveclient_ctrless");
#else
_LIT(KLddRootName,"iic_slaveclient");
#endif
_LIT(KIicSlaveClientThreadName,"IicSlaveClientLddThread");


struct TCapsIicSlaveClient
    {
    TVersion version;
    };


class DDeviceIicSlaveClient : public DLogicalDevice
    {
    public:
    /**
     * The constructor
     */
    DDeviceIicSlaveClient();
    /**
     * The destructor
     */
    ~DDeviceIicSlaveClient();
    /**
     * Second stage constructor - install the device
     */
    virtual TInt Install();
    /**
     * Get the Capabilites of the device
     * @param aDes descriptor that will contain the returned capibilites
     */
    virtual void GetCaps(TDes8 &aDes) const;
    /**
     * Create a logical channel to the device
     */
    virtual TInt Create(DLogicalChannelBase*& aChannel);
    };


class DChannelIicSlaveClient : public DLogicalChannel
    {
    public:
    DChannelIicSlaveClient();
    ~DChannelIicSlaveClient();

    virtual TInt DoCreate(TInt aUnit, const TDesC8* aInfo, const TVersion& aVer);

	void RequestComplete(TInt r);
	TInt CheckDataWritten();
	TInt CheckDataRead();

	static void SlaveClientCallbackFunc(TInt aChannelId, TInt aReturn, TInt aTrigger, TInt16 aRxWords, TInt16 aTxWords, TAny*	aParam);
#ifdef STANDALONE_CHANNEL
	static TInt OrderEntries(const DIicSlaveClientChan& aMatch, const DIicSlaveClientChan& aEntry);
#endif
    protected:
    virtual void HandleMsg(TMessageBase* aMsg);	// Note: this is a pure virtual in DLogicalChannel

	TInt DoControl(TInt aId, TAny* a1, TAny* a2); // Name for convenience!
    TInt DoRequest(TInt aId, TRequestStatus* aStatus, TAny* a1, TAny* a2); // Name for convenience!

	private:
	TInt InitSlaveClient();
	TInt CbProcessOverUnderRunRxTx();
	TInt RegisterRxBuffer(TInt aChannelId, TPtr8 aRxBuffer, TInt8 aBufGranularity, TInt8 aNumWords, TInt8 aOffset);
	TInt SetNotificationTrigger(TInt aChannelId, TInt aTrigger);
	TInt StaticExtension(TUint aId, TUint aFunction, TAny* aParam1, TAny* aParam2);
	TInt RegisterTxBuffer(TInt aChannelId, TPtr8 aTxBuffer, TInt8 aBufGranularity, TInt8 aNumWords, TInt8 aOffset);
	TInt CaptureChannel(TInt aBusId, TDes8* aConfigHdr, TIicBusSlaveCallback* aCallback, TInt& aChannelId, TBool aAsynch=NULL);
	TInt ReleaseChannel(TInt aChannelId);
	private:
	TDynamicDfcQue* iDfcQue;
	TIicBusSlaveCallback* iNotif;

	HBuf8* iConfigHdr;
	TRequestStatus* iStatus;

	TUint8* iRxBuf;
	TUint8* iTxBuf;

	TUint8* iBusTxCheckBuf;

	TInt8 iNumRegRxWords;
	TInt8 iNumRegTxWords;

	TInt8 iTxRegGranularity;
	TInt8 iTxRegOffset;
	TInt8 iTxReqNumWords;

	TInt8 iRxRegGranularity;
	TInt8 iRxRegOffset;
	TInt8 iRxReqNumWords;

	TUint iBusId;
#ifdef STANDALONE_CHANNEL
	// For some slave-channel-only functions,e.g. ReleaseChannel, RegisterRxBuffer, etc.
	// the client needs to remember the slave channel that has been captured
	struct TCapturedChannel
		{
		DIicSlaveClientChan* iChannel;
		TInt iChannelId;
		};
	TCapturedChannel iCapturedChan;
#endif
	public:
	DThread* iClient;
	TInt iChannelId;	// public to be accessible by callback
	TInt* iClientChanId;

	TInt iExpectedTrigger;
	TInt iFullDuplexReq;
	TInt iBlockedTrigger;

	typedef enum TTestOverUnderState
		{
		EStartState  = 0x1,
		ERxOverrun_1,
		ERxOverrun_2,
		ETxUnderrun_1,
		ETxUnderrun_2
		};
	TTestOverUnderState iTestOverUnderState;
	};

DDeviceIicSlaveClient::DDeviceIicSlaveClient()
// Constructor
    {
	CLIENT_PRINT(("> DDeviceIicSlaveClient::DDeviceIicSlaveClient()"));
    iParseMask=0;		// No info, no PDD, no Units
    iUnitsMask=0;
    iVersion=TVersion(KIicClientMajorVersionNumber,
		      KIicClientMinorVersionNumber,
		      KIicClientBuildVersionNumber);
    }

#ifdef STANDALONE_CHANNEL
//Store all the channels created by the client into an array
RPointerArray<DIicSlaveClientChan> ChannelArray;
#endif
DDeviceIicSlaveClient::~DDeviceIicSlaveClient()
// Destructor
    {
	CLIENT_PRINT(("> DDeviceIicSlaveClient::~DDeviceIicSlaveClient()"));
#ifdef STANDALONE_CHANNEL
	//The client is reponsible for channel destroy in STANDALONE_CHANNEL mode
    ChannelArray.ResetAndDestroy();
#endif
	}

TInt DDeviceIicSlaveClient::Install()
// Install the device driver.
    {
	CLIENT_PRINT(("> DDeviceIicSlaveClient::Install()"));

    return(SetName(&KLddRootName));
    }


void DDeviceIicSlaveClient::GetCaps(TDes8& aDes) const
// Return the IicClient capabilities.
    {
	CLIENT_PRINT(("> DDeviceIicSlaveClient::GetCaps(TDes8& aDes) const"));
    TPckgBuf<TCapsIicSlaveClient> b;
    b().version=TVersion(KIicClientMajorVersionNumber,
			 KIicClientMinorVersionNumber,
			 KIicClientBuildVersionNumber);
    Kern::InfoCopy(aDes,b);
    }


TInt DDeviceIicSlaveClient::Create(DLogicalChannelBase*& aChannel)
// Create a channel on the device.
    {
	CLIENT_PRINT(("> DDeviceIicSlaveClient::Create(DLogicalChannelBase*& aChannel)"));
	if(iOpenChannels>=KMaxNumChannels)
		return KErrOverflow;
    aChannel=new DChannelIicSlaveClient;
    return aChannel?KErrNone:KErrNoMemory;
    }

#ifdef STANDALONE_CHANNEL
//  auxiliary function for ordering entries in the array of channels
TInt DChannelIicSlaveClient::OrderEntries(const DIicSlaveClientChan& aMatch, const DIicSlaveClientChan& aEntry)
	{
	TUint8 l=(TUint8)aMatch.GetChanNum();
	TUint8 r=(TUint8)aEntry.GetChanNum();
	if(l>r)
		return -1;
	else if(l<r)
		return 1;
	else
		return 0;
	}

// global ordering object to be passed to RPointerArray InsertInOrderXXX and FindInOrder
TLinearOrder<DIicSlaveClientChan> EntryOrder(DChannelIicSlaveClient::OrderEntries);

TInt GetChanPtr(const TInt aBusId, DIicSlaveClientChan*& aChan)
	{
    __KTRACE_OPT(KIIC, 	Kern::Printf("GetChanPtr, aBusId=0x%x\n",aBusId));
	TInt32 chanId;
	chanId = GET_CHAN_NUM(aBusId);
    __KTRACE_OPT(KIIC, 	Kern::Printf("GetChanPtr, chanId=0x%x\n",chanId));
	DIicSlaveClientChan chanEntry(NULL,(TInt8)chanId, DIicBusChannel::EMasterSlave);

	TInt r = KErrNotFound;
	TInt aIndex = ChannelArray.FindInOrder(&chanEntry, EntryOrder);

	if(aIndex >= 0)
		{
		aChan = ChannelArray[aIndex];
		r = KErrNone;
		}

	return r;
	}
#endif/*STANDALONE_CHANNEL*/

DECLARE_STANDARD_LDD()
	{
#ifdef STANDALONE_CHANNEL
	TInt r = KErrNone;
	DIicBusChannel *chan = NULL, *chanM = NULL, *chanS = NULL;
	DIicSlaveClientChan* aSlaveClientChan;
	for(TInt i=0; i<NUM_CHANNELS; i++)
			{
			CLIENT_PRINT(("\n"));
	#if defined(MASTER_MODE)
			if(CHANNEL_TYPE(i) == (DIicBusChannel::EMaster))
				{
				chan=new DSimulatedIicBusChannelMasterI2c(BUS_TYPE,CHANNEL_DUPLEX(i));
				if(!chan)
					{
					CLIENT_PRINT(("\n\nI2C: Channel of type (%d) not created for index %d\n\n",CHANNEL_TYPE(i),i));
					return NULL;
					}
				CLIENT_PRINT(("I2C chan created at 0x%x\n",chan));
				if(((DSimulatedIicBusChannelMasterI2c*)chan)->Create()!=KErrNone)
					{
					delete chan;
					return NULL;
					}
				aSlaveClientChan = new DIicSlaveClientChan(chan,AssignChanNumI2c(),DIicBusChannel::EMaster);
				if(!aSlaveClientChan)
					{
					delete chan;
					return NULL;
					}
				r = ChannelArray.InsertInOrder(aSlaveClientChan,EntryOrder);
				if(r!=KErrNone)
					{
					delete chan;
					delete aSlaveClientChan;
					break;
					}
				}
	#endif
	#if defined(MASTER_MODE) && defined(SLAVE_MODE)
			if(CHANNEL_TYPE(i) == DIicBusChannel::EMasterSlave)
				{
				chanM=new DSimulatedIicBusChannelMasterI2c(BUS_TYPE,CHANNEL_DUPLEX(i));
				if(!chanM)
					return NULL;
				chanS=new DSimulatedIicBusChannelSlaveI2c(BUS_TYPE,CHANNEL_DUPLEX(i));
				if(!chanS)
					{
					delete chanM;
					return NULL;
					}
				chan=new DSimulatedIicBusChannelMasterSlaveI2c(BUS_TYPE,CHANNEL_DUPLEX(i),(DSimulatedIicBusChannelMasterI2c*)chanM,(DSimulatedIicBusChannelSlaveI2c*)chanS); // Generic implementation
				if(!chan)
					{
					CLIENT_PRINT(("\n\nI2C: Channel of type (%d) not created for index %d\n\n",CHANNEL_TYPE(i),i));
					delete chanM;
					delete chanS;
					return NULL;
					}
				CLIENT_PRINT(("I2C chan created at 0x%x\n",chan));
				if(((DIicBusChannelMasterSlave*)chan)->DoCreate()!=KErrNone)
					{
					delete chanM;
					delete chanS;
					delete chan;
					return NULL;
					}
				aSlaveClientChan = new DIicSlaveClientChan(chan,AssignChanNumI2c(),DIicBusChannel::EMasterSlave);
				if(!aSlaveClientChan)
					{
					delete chanM;
					delete chanS;
					delete chan;
					return NULL;
					}
				r = ChannelArray.InsertInOrder(aSlaveClientChan,EntryOrder);
				if(r!=KErrNone)
					{
					delete chanM;
					delete chanS;
					delete chan;
					delete aSlaveClientChan;
					break;
					}
				}
	#endif
	#if defined(SLAVE_MODE)
			if(CHANNEL_TYPE(i) == (DIicBusChannel::ESlave))
				{
				chan=new DSimulatedIicBusChannelSlaveI2c(BUS_TYPE,CHANNEL_DUPLEX(i));
				if(!chan)
					{
					CLIENT_PRINT(("\n\nI2C: Channel of type (%d) not created for index %d\n\n",CHANNEL_TYPE(i),i));
					return NULL;
					}
				CLIENT_PRINT(("I2C chan created at 0x%x\n",chan));
				if(((DSimulatedIicBusChannelSlaveI2c*)chan)->Create()!=KErrNone)
					return NULL;
				aSlaveClientChan = new DIicSlaveClientChan(chan,AssignChanNumI2c(),DIicBusChannel::ESlave);
				if(!aSlaveClientChan)
					{
					delete chan;
					return NULL;
					}
				r = ChannelArray.InsertInOrder(aSlaveClientChan,EntryOrder);
				if(r!=KErrNone)
					{
					delete chan;
					delete aSlaveClientChan;
					break;
					}
				}
	#endif
	#if !defined(MASTER_MODE) && !defined(SLAVE_MODE)
	#error I2C mode not defined as Master, Slave nor Master-Slave
	#endif
			}
#endif
	return new DDeviceIicSlaveClient;
	}



DChannelIicSlaveClient::DChannelIicSlaveClient()
// Constructor
    {
	iFullDuplexReq=iBlockedTrigger=iExpectedTrigger=0;
	CLIENT_PRINT(("> DChannelIicSlaveClient::DChannelIicSlaveClient()"));
    iClient=&Kern::CurrentThread();
	// Increase the DThread's ref count so that it does not close without us
	iClient->Open();
	iTestOverUnderState = EStartState;
    }

DChannelIicSlaveClient::~DChannelIicSlaveClient()
// Destructor
    {
	CLIENT_PRINT(("> DChannelIicSlaveClient::~DChannelIicSlaveClient()"));
	iDfcQue->Destroy();
	delete iNotif;
	delete iRxBuf;
	delete iTxBuf;
	delete iBusTxCheckBuf;
	// decrement the DThread's reference count
	Kern::SafeClose((DObject*&)iClient, NULL);
	}

void DChannelIicSlaveClient::RequestComplete(TInt r)
	{
	Kern::RequestComplete(iClient, iStatus, r);
	}

TInt DChannelIicSlaveClient::RegisterRxBuffer(TInt aChannelId, TPtr8 aRxBuffer, TInt8 aBufGranularity, TInt8 aNumWords, TInt8 aOffset)
	{
	TInt r = KErrNone;
#ifdef STANDALONE_CHANNEL
	DIicSlaveClientChan* aChanPtr = NULL;
	if(iCapturedChan.iChannelId == aChannelId)
		aChanPtr = iCapturedChan.iChannel;
	if(!aChanPtr)
		return KErrArgument;
	if(aChanPtr->GetChanType() == DIicBusChannel::EMasterSlave)
		r = ((DIicBusChannelMasterSlave*)(aChanPtr->GetChannelPtr()))->RegisterRxBuffer(aRxBuffer, aBufGranularity, aNumWords, aOffset);
	else if(aChanPtr->GetChanType() == DIicBusChannel::ESlave)
		r = ((DIicBusChannelSlave*)(aChanPtr->GetChannelPtr()))->RegisterRxBuffer(aRxBuffer, aBufGranularity, aNumWords, aOffset);
#else
	r = IicBus::RegisterRxBuffer(aChannelId, aRxBuffer, aBufGranularity, aNumWords, aOffset);
#endif
	return r;
	}

TInt DChannelIicSlaveClient::SetNotificationTrigger(TInt aChannelId, TInt aTrigger)
	{
	TInt r = KErrNone;
#ifdef STANDALONE_CHANNEL
	DIicSlaveClientChan* aChanPtr = NULL;
	if(iCapturedChan.iChannelId == aChannelId)
		aChanPtr = iCapturedChan.iChannel;
	if(!aChanPtr)
		return KErrArgument;
	if(aChanPtr->GetChanType() == DIicBusChannel::EMasterSlave)
		r = ((DIicBusChannelMasterSlave*)(aChanPtr->GetChannelPtr()))->SetNotificationTrigger(aTrigger);
	else if(aChanPtr->GetChanType() == DIicBusChannel::ESlave)
		r = ((DIicBusChannelSlave*)(aChanPtr->GetChannelPtr()))->SetNotificationTrigger(aTrigger);
#else
	r = IicBus::SetNotificationTrigger(aChannelId, aTrigger);
#endif
	return r;
	}

TInt DChannelIicSlaveClient::StaticExtension(TUint aId, TUint aFunction, TAny* aParam1, TAny* aParam2)
	{
	TInt r = KErrNone;
#ifdef STANDALONE_CHANNEL
	DIicSlaveClientChan* aChanPtr;
	r = GetChanPtr(aId, aChanPtr);
	if(r != KErrNone)
		return r;
	if(!aChanPtr)
		return KErrArgument;
	if(aChanPtr->GetChanType() == DIicBusChannel::EMasterSlave)
		r = ((DIicBusChannelMasterSlave*)(aChanPtr->GetChannelPtr()))->StaticExtension(aFunction, aParam1, aParam2);
	else if(aChanPtr->GetChanType() == DIicBusChannel::ESlave)
	r = ((DIicBusChannelSlave*)(aChanPtr->GetChannelPtr()))->StaticExtension(aFunction, aParam1, aParam2);
#else
	r = IicBus::StaticExtension(aId, aFunction, aParam1, aParam2);
#endif
	return r;
	}

TInt DChannelIicSlaveClient::RegisterTxBuffer(TInt aChannelId, TPtr8 aTxBuffer, TInt8 aBufGranularity, TInt8 aNumWords, TInt8 aOffset)
	{
	TInt r = KErrNone;
#ifdef STANDALONE_CHANNEL
	DIicSlaveClientChan* aChanPtr = NULL;
	if(iCapturedChan.iChannelId == aChannelId)
		aChanPtr = iCapturedChan.iChannel;
	if(!aChanPtr)
		return KErrArgument;
	if(aChanPtr->GetChanType() == DIicBusChannel::EMasterSlave)
		r = ((DIicBusChannelMasterSlave*)(aChanPtr->GetChannelPtr()))->RegisterTxBuffer(aTxBuffer, aBufGranularity, aNumWords, aOffset);
	else if(aChanPtr->GetChanType() == DIicBusChannel::ESlave)
		r = ((DIicBusChannelSlave*)(aChanPtr->GetChannelPtr()))->RegisterTxBuffer(aTxBuffer, aBufGranularity, aNumWords, aOffset);
#else
	r = IicBus::RegisterTxBuffer(aChannelId, aTxBuffer, aBufGranularity, aNumWords, aOffset);
#endif
	return r;
	}

TInt DChannelIicSlaveClient::CaptureChannel(TInt aBusId, TDes8* aConfigHdr, TIicBusSlaveCallback* aCallback, TInt& aChannelId, TBool aAsynch)
	{
	TInt r = KErrNone;
#ifndef STANDALONE_CHANNEL
	r = IicBus::CaptureChannel(aBusId, aConfigHdr, aCallback, aChannelId, aAsynch);
#else
	// Check that that aCallback!=NULL and aConfigHdr!=NULL - if not, return KErrArgument
	if(!aCallback || !aConfigHdr)
		{
		return KErrArgument;
		}

	// Get the channel
	DIicSlaveClientChan* chanPtr = NULL;
	if(r == KErrNone)
		{
		r = GetChanPtr(aBusId, chanPtr);
		if(r == KErrNone)
			{
			if(!chanPtr)
				{
				r = KErrArgument;
				}
			else
				{
				switch(chanPtr->GetChanType())
					{
					// CaptureChannel requests are only supported by channels in Slave mode.
					case DIicBusChannel::EMaster:
						{
						r = KErrNotSupported;
						break;
						}
					case DIicBusChannel::EMasterSlave:
						{
						r = ((DIicBusChannelMasterSlave*) (chanPtr->GetChannelPtr()))->CaptureChannel(aConfigHdr, aCallback, aChannelId, aAsynch);
						break;
						}
					case DIicBusChannel::ESlave:
						{
						r = ((DIicBusChannelSlave*) (chanPtr->GetChannelPtr()))->CaptureChannel(aConfigHdr, aCallback, aChannelId, aAsynch);
						break;
						}
					default:
						{
						r = KErrArgument;
						}
					}
				// For synchronous capture, if successful then install the channel
				if(r == KErrNone)
					{
					if(!aAsynch)
						{
						 iCapturedChan.iChannel = chanPtr;
						 iCapturedChan.iChannelId = aChannelId;
						}
					else
						//For asynchronous capture, record slaveChanPtr, if later failed capture,
						//clean iCapturedChannel in client's callback.
						iCapturedChan.iChannel = chanPtr;
					}
				}
			}
		}
#endif
	return r;
	}

TInt DChannelIicSlaveClient::ReleaseChannel(TInt aChannelId)
	{
	TInt r = KErrNone;
#ifndef STANDALONE_CHANNEL
	r = IicBus::ReleaseChannel(aChannelId);
#else
    __KTRACE_OPT(KIIC, Kern::Printf("DChannelIicSlaveClient::ReleaseChannel, channelID = 0x%x \n",aChannelId));
    // Acquire the pointer to the Slave Channel
    if(iCapturedChan.iChannelId != aChannelId)
		return KErrNotFound;

	if((iCapturedChan.iChannel)->GetChanType() == DIicBusChannel::EMasterSlave)
		r = ((DIicBusChannelMasterSlave*)((iCapturedChan.iChannel)->GetChannelPtr()))->ReleaseChannel();
	else if((iCapturedChan.iChannel)->GetChanType() == DIicBusChannel::ESlave)
		r = ((DIicBusChannelSlave*)((iCapturedChan.iChannel)->GetChannelPtr()))->ReleaseChannel();
	//After release channel, reset iCapturedChan
	iCapturedChan.iChannel = NULL;
	iCapturedChan.iChannelId = 0;
#endif
	return r;
	}
TInt DChannelIicSlaveClient::CbProcessOverUnderRunRxTx()
	{
	CLIENT_PRINT(("> DChannelIicSlaveClient::CbProcessOverUnderRunRxTx(), iTestOverUnderState=%d\n",iTestOverUnderState));
	TInt r = KErrNone;
	switch (iTestOverUnderState)
		{
		case(EStartState):
			{
			// In this state, no action is required
			break;
			};
		case(ERxOverrun_1):
			{
			CLIENT_PRINT(("CbProcessOverUnderRunRxTx: entry state = ERxOverrun_1\n"));
			// At this point, the outstanding request trigger should be ETxAllBytes | ETxUnderrun
			// and the flag to indicate duplex transfers should be cleared
			if((iExpectedTrigger != (ETxAllBytes | ETxUnderrun)) || (iFullDuplexReq != ETxAllBytes))
				{
				CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx, iExpectedTrigger=0x%x, iFullDuplexReq=%d\n",iExpectedTrigger,iFullDuplexReq));
				r=KErrGeneral;
				}
			else
				{
				// Simulate providing a new buffer (actually, re-use existing buffer)
				CLIENT_PRINT(("CbProcessOverUnderRunRxTx: invoking RegisterRxBuffer\n"));
				TPtr8 rxPtr(iRxBuf,KRxBufSizeInBytes);
				r = RegisterRxBuffer(iChannelId, rxPtr, iRxRegGranularity, iNumRegRxWords, iRxRegOffset);

				if(r != KErrNone)
					{
					CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - RegisterRxBuffer returned %d\n",r));
					}
				else
					{
					// For the next step, just specify the new Rx triggers (do not specify Tx triggers)
					r = SetNotificationTrigger(iChannelId, (ERxAllBytes | ERxOverrun));
					if(r != KErrNone)
						{
						CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - SetNotificationTrigger returned %d\n",r));
						}
					else
						{
						iExpectedTrigger = ERxAllBytes | ERxOverrun | ETxAllBytes | ETxUnderrun;
						iFullDuplexReq |= (ERxAllBytes|ETxAllBytes);
						iTestOverUnderState = ERxOverrun_2;	// Prepare for callback
						// The requested number of words when the buffer was registered was 8, so simulate 10
						// to provoke an RxOverrun event.
						TInt numWords=10;
						// To support testing, any values of aId for StaticExtension must be shifted left one place
						// and for a Slave, the two msbs must be zero
						TInt ctrlIoVal = RBusDevIicClient::ECtrlIoRxWords;
						ctrlIoVal = (ctrlIoVal << 1) & 0x3FFFFFFF;
						r = StaticExtension(iBusId, ctrlIoVal, (TAny*)iChannelId, (TAny*)numWords);
						}
					}
				}
			break;
			};
		case(ERxOverrun_2):
			{
			CLIENT_PRINT(("CbProcessOverUnderRunRxTx: entry state = ERxOverrun_2\n"));
			// At this point, the outstanding request trigger should be ETxAllBytes | ETxUnderrun
			// and the flag to indicate duplex transfers should be cleared
			if((iExpectedTrigger != (ETxAllBytes | ETxUnderrun)) || (iFullDuplexReq != ETxAllBytes))
				{
				CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx, iExpectedTrigger=0x%x, iFullDuplexReq=%d\n",iExpectedTrigger,iFullDuplexReq));
				r=KErrGeneral;
				}
			else
				{
				// Simulate providing a new buffer (actually, re-use existing buffer)
				CLIENT_PRINT(("CbProcessOverUnderRunRxTx: invoking RegisterRxBuffer\n"));
				TPtr8 rxPtr(iRxBuf,KRxBufSizeInBytes);
				r = RegisterRxBuffer(iChannelId, rxPtr, iRxRegGranularity, iNumRegRxWords, iRxRegOffset);
				if(r != KErrNone)
					{
					CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - RegisterRxBuffer returned %d\n",r));
					}
				else
					{
					// Test that an attempt to modify existing Tx notification requests is rejected
					r = SetNotificationTrigger(iChannelId, (ERxAllBytes | ERxOverrun | ETxAllBytes | ETxOverrun));
					if(r != KErrInUse)
						{
						CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - SetNotificationTrigger returned %d, expected KErrInUse\n",r));
						}
					else
						{
						// For the next step, specify the new Rx triggers and the Tx triggers
						r = SetNotificationTrigger(iChannelId, (ERxAllBytes | ERxOverrun | ETxAllBytes | ETxUnderrun));
						if(r != KErrNone)
							{
							CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - SetNotificationTrigger returned %d, expected KErrNone\n",r));
							}
						else
							{
							iExpectedTrigger = ERxAllBytes | ERxOverrun | ETxAllBytes | ETxUnderrun;
							iFullDuplexReq |= (ERxAllBytes|ETxAllBytes);
							iTestOverUnderState = ETxUnderrun_1;	// Prepare for callback
							// The requested number of words when the buffer was registered was 12, so simulate 14
							// to provoke an TxUnderrun event.
							TInt numWords=14;
							// To support testing, any values of aId for StaticExtension must be shifted left one place
							// and for a Slave, the two msbs must be zero
							TInt ctrlIoVal = RBusDevIicClient::ECtrlIoTxWords;
							ctrlIoVal = (ctrlIoVal << 1) & 0x3FFFFFFF;
							r = StaticExtension(iBusId, ctrlIoVal, (TAny*)iChannelId, (TAny*)numWords);
							}
						}
					}
				}
			break;
			};
		case(ETxUnderrun_1):
			{
			CLIENT_PRINT(("CbProcessOverUnderRunRxTx: entry state = ETxOverrun_1\n"));
			// At this point, the outstanding request trigger should be ERxAllBytes | ERxOverrun
			// and the flag to indicate duplex transfers should be cleared
			if((iExpectedTrigger != (ERxAllBytes | ERxOverrun)) || (iFullDuplexReq != ERxAllBytes))
				{
				CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx, iExpectedTrigger=0x%x, iFullDuplexReq=%d\n",iExpectedTrigger,iFullDuplexReq));
				r=KErrGeneral;
				}
			else
				{
				// Simulate providing a new buffer (actually, re-use existing buffer)
				CLIENT_PRINT(("CbProcessOverUnderRunRxTx: invoking RegisterTxBuffer\n"));
				TPtr8 rxPtr(iTxBuf,KTxBufSizeInBytes);
				r = RegisterTxBuffer(iChannelId, rxPtr, iTxRegGranularity, iNumRegTxWords, iTxRegOffset);
				if(r != KErrNone)
					{
					CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - RegisterTxBuffer returned %d\n",r));
					}
				else
					{
					// For the next step, just specify the new Tx triggers (do not specify Rx triggers)
					r = SetNotificationTrigger(iChannelId, (ETxAllBytes | ETxUnderrun));
					if(r != KErrNone)
						{
						CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - SetNotificationTrigger returned %d\n",r));
						}
					else
						{
						iExpectedTrigger = ERxAllBytes | ERxOverrun | ETxAllBytes | ETxUnderrun;
						iFullDuplexReq |= (ERxAllBytes|ETxAllBytes);
						iTestOverUnderState = ETxUnderrun_2;	// Prepare for callback
						// The requested number of words when the buffer was registered was 12, so simulate 14
						// to provoke an TxUnderrun event.
						TInt numWords=14;
						// To support testing, any values of aId for StaticExtension must be shifted left one place
						// and for a Slave, the two msbs must be zero
						TInt ctrlIoVal = RBusDevIicClient::ECtrlIoTxWords;
						ctrlIoVal = (ctrlIoVal << 1) & 0x3FFFFFFF;
						r = StaticExtension(iBusId, ctrlIoVal, (TAny*)iChannelId, (TAny*)numWords);
						}
					}
				}
			break;
			};
		case(ETxUnderrun_2):
			{
			CLIENT_PRINT(("CbProcessOverUnderRunRxTx: entry state = ETxUnderrun_2\n"));
			// At this point, the outstanding request trigger should be ERxAllBytes | ERxOverrun
			// and the flag to indicate duplex transfers should be cleared
			if((iExpectedTrigger != (ERxAllBytes | ERxOverrun)) || (iFullDuplexReq != ERxAllBytes))
				{
				CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx, iExpectedTrigger=0x%x, iFullDuplexReq=%d\n",iExpectedTrigger,iFullDuplexReq));
				r=KErrGeneral;
				}
			else
				{
				// Simulate providing a new buffer (actually, re-use existing buffer)
				CLIENT_PRINT(("CbProcessOverUnderRunRxTx: invoking RegisterRxBuffer\n"));
				TPtr8 rxPtr(iTxBuf,KTxBufSizeInBytes);
				r = RegisterTxBuffer(iChannelId, rxPtr, iTxRegGranularity, iNumRegTxWords, iTxRegOffset);
				if(r != KErrNone)
					{
					CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - RegisterTxBuffer returned %d\n",r));
					}
				else
					{
					// Test that an attempt to modify existing Rx notification requests is rejected
					r = SetNotificationTrigger(iChannelId, (ERxAllBytes | ERxUnderrun | ETxAllBytes | ETxUnderrun));
					if(r != KErrInUse)
						{
						CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - SetNotificationTrigger returned %d, expected KErrInUse\n",r));
						}
					else
						{
						// For the next step, specify the new Rx triggers and the Tx triggers
						r = SetNotificationTrigger(iChannelId, (ERxAllBytes | ERxOverrun | ETxAllBytes | ETxUnderrun));

						if(r != KErrNone)
							{
							CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - SetNotificationTrigger returned %d, expected KErrNone\n",r));
							}
						else
							{
							// Simulate a simultaneous ERxAllBytes and ETxAllBytes event.
							iExpectedTrigger = ERxAllBytes | ETxAllBytes;
							iFullDuplexReq |= (ERxAllBytes|ETxAllBytes);
							iTestOverUnderState = EStartState;	// Prepare for callback - return to normal operation
							// Need to pass the number of words in an array, for use by StaticExtension
							TInt parms[2];
							parms[0]= 8;	// Number of Rx Words
							parms[1]=12;	// Number of Tx Words
							// To support testing, any values of aId for StaticExtension must be shifted left one place
							// and for a Slave, the two msbs must be zero
							TInt ctrlIoVal = RBusDevIicClient::ECtrlIoRxTxWords;
							ctrlIoVal = (ctrlIoVal << 1) & 0x3FFFFFFF;
							r = StaticExtension(iBusId, ctrlIoVal, (TAny*)iChannelId, (TAny*)(&parms[0]));
							}
						}
					}
				}
			break;
			};
		default:
			{
			r = KErrGeneral;
			break;
			};
		}
	return r;
	}

void DChannelIicSlaveClient::SlaveClientCallbackFunc(TInt aChannelId, TInt aReturn, TInt aTrigger, TInt16 aRxWords, TInt16 aTxWords, TAny*	aParam)
	{
	CLIENT_PRINT(("> SlaveClientCallbackFunc() - aChannelId=0x%x,aReturn=%d,aTrigger=0x%x,aRxWords=0x%x,aTxWords=0x%x,aParam=0x%x\n",aChannelId,aReturn,aTrigger,aRxWords,aTxWords,aParam));
	(void)aTxWords; // Unused if CLIENT_PRINT is undefined
	(void)aRxWords; // Unused if CLIENT_PRINT is undefined
	DChannelIicSlaveClient* channel = (DChannelIicSlaveClient*)aParam;

	// Ensure only the valid bits of aTrigger are processed
	aTrigger &= 0xff;

	CLIENT_PRINT(("SlaveClientCallbackFunc() - channel=0x%x\n",channel));
	if(aTrigger == EAsyncCaptChan)
		{
		CLIENT_PRINT(("SlaveClientCallbackFunc: capture channel completed\n"));
		// Set iChannelId, and write to user-side variable.
		channel->iChannelId=aChannelId;
		TInt r=Kern::ThreadRawWrite(channel->iClient,channel->iClientChanId,&aChannelId,sizeof(TInt));
		if(r == KErrNone)
			r=aReturn;
#ifdef STANDALONE_CHANNEL
		// Set the captured channel's iChannelId if the capture succeeds.
		if(r != KErrCompletion)
			(channel->iCapturedChan).iChannel = NULL;
		else
			(channel->iCapturedChan).iChannelId = aChannelId;
#endif/*STANDALONE_CHANNEL*/
	    channel->RequestComplete(r);	// Inform user of error
		return;
		}
	else
		{
		if(aTrigger&ERxAllBytes)
			{
			CLIENT_PRINT(("SlaveClientCallbackFunc() - ERxAllBytes\n"));
			aTrigger&= ~ERxAllBytes;
			channel->iExpectedTrigger&=~ERxAllBytes;
			channel->iFullDuplexReq&=~ERxAllBytes;
			aReturn=channel->CheckDataRead();
			// Check underrun
			if(aTrigger&ERxUnderrun)
				{
				if(channel->iExpectedTrigger&ERxUnderrun)
					{
					CLIENT_PRINT(("\nSlaveClientCallbackFunc() - expected ERxUnderrun found OK\n\n"));
					channel->iExpectedTrigger&=~ERxUnderrun;
					}
				else
					{
					CLIENT_PRINT(("\nSlaveClientCallbackFunc() - unexpected ERxUnderrun indicated\n\n"));
					aReturn = KErrGeneral;
					}
				}
			else
				{
				if(channel->iExpectedTrigger&ERxUnderrun)
					{
					CLIENT_PRINT(("\nSlaveClientCallbackFunc() - expected ERxUnderrun not (yet) seen\n\n"));
					aReturn = KErrGeneral;
					}
				}
			// Check overrun
			if(aTrigger&ERxOverrun)
				{
				if(channel->iExpectedTrigger&ERxOverrun)
					{
					CLIENT_PRINT(("\nSlaveClientCallbackFunc() - expected ERxOverrun found OK\n\n"));
					channel->iExpectedTrigger&=~ERxOverrun;
					}
				else
					{
					CLIENT_PRINT(("\nSlaveClientCallbackFunc() - unexpected ERxOverrun indicated\n\n"));
					aReturn = KErrGeneral;
					}
				}
			else
				{
				if(channel->iExpectedTrigger&ERxOverrun)
					{
					CLIENT_PRINT(("\nSlaveClientCallbackFunc() - expected ERxOverrun not (yet) seen\n\n"));
					aReturn = KErrGeneral;
					}
				}
			}

		if(aTrigger&ETxAllBytes)
			{
			CLIENT_PRINT(("SlaveClientCallbackFunc() - ETxAllBytes\n"));
			aTrigger&= ~ETxAllBytes;
			channel->iExpectedTrigger&=~ETxAllBytes;
			channel->iFullDuplexReq&=~ETxAllBytes;
			aReturn=channel->CheckDataWritten();
			// Check underrun
			if(aTrigger&ETxUnderrun)
				{
				if(channel->iExpectedTrigger&ETxUnderrun)
					{
					CLIENT_PRINT(("\nSlaveClientCallbackFunc() - expected ETxUnderrun found OK\n\n"));
					channel->iExpectedTrigger&=~ETxUnderrun;
					}
				else
					{
					CLIENT_PRINT(("\nSlaveClientCallbackFunc() - unexpected ETxUnderrun indicated\n\n"));
					aReturn = KErrGeneral;
					}
				}
			else
				{
				if(channel->iExpectedTrigger&ETxUnderrun)
					{
					CLIENT_PRINT(("\nSlaveClientCallbackFunc() - expected ETxUnderrun not (yet) seen\n\n"));
					aReturn = KErrGeneral;
					}
				}
			// Check overrun
			if(aTrigger&ETxOverrun)
				{
				if(channel->iExpectedTrigger&ETxOverrun)
					{
					CLIENT_PRINT(("\nSlaveClientCallbackFunc() - expected ETxOverrun found OK\n\n"));
					channel->iExpectedTrigger&=~ETxOverrun;
					}
				else
					{
					CLIENT_PRINT(("\nSlaveClientCallbackFunc() - unexpected ETxOverrun indicated\n\n"));
					aReturn = KErrGeneral;
					}
				}
			else
				{
				if(channel->iExpectedTrigger&ETxOverrun)
					{
					CLIENT_PRINT(("\nSlaveClientCallbackFunc() - expected ETxOverrun not (yet) seen\n\n"));
					aReturn = KErrGeneral;
					}
				}
			}


		if(aTrigger&EGeneralBusError)
			{
				if(channel->iExpectedTrigger&EGeneralBusError)
					{
					CLIENT_PRINT(("\nSlaveClientCallbackFunc: EGeneralBusError - as expected\n\n"));
					channel->iExpectedTrigger&=~EGeneralBusError;
					if(aReturn == KErrGeneral)
						{
						aReturn=KErrNone; // If aReturn==KErrGeneral, set to KErrNone so t_iic knows test was successful
						channel->iFullDuplexReq = 0; // The transaction is considered terminated, so don't wait for more triggers
						}
					else
						{
						CLIENT_PRINT(("\nSlaveClientCallbackFunc: aReturn not EGeneralBusError, =0x%x \n\n",aReturn));
						aReturn=KErrGeneral;
						}

					}
				else
					{
					CLIENT_PRINT(("\nSlaveClientCallbackFunc() - unexpected EGeneralBusError indicated\n\n"));
					aReturn = KErrGeneral;
					}
			}

		if((aTrigger < 0)||(aTrigger & (~0xFF)))
			{
			CLIENT_PRINT(("\nSlaveClientCallbackFunc: trigger condition 0x%x is not recognised \n\n",aTrigger));
			}

		// For simulataneous Rx,Tx triggers with ERxOverrun or TxUnderrun testing, need to call the following
		if(aReturn == KErrNone)
			{
			aReturn = channel->CbProcessOverUnderRunRxTx();
			}

		if((channel->iExpectedTrigger == 0)&&(channel->iFullDuplexReq == 0))
			channel->RequestComplete(aReturn);	// Complete user-side request only if all the triggers have been satisfied

		} // if(aTrigger == EAsyncCaptChan)
	}


TInt DChannelIicSlaveClient::CheckDataRead()
	{
	TInt r=KErrNone;
	// This channel will have provided a buffer for writing to, with a specified offset and number of words
	// Bytes in the buffer before the offset should be set to zero
	// Bytes written at and beyond the offset should exhibit an incrementing count
	// Bytes beyond the offset that were not written to should be set to zero
	TInt8 numWords=(iRxReqNumWords>iNumRegRxWords)?iNumRegRxWords:iRxReqNumWords;
	TInt8 currVal=0;
	TInt8 index = 0;
	while(index<iRxRegOffset)
		{
		currVal=*(iRxBuf+index);
		if(currVal != 0)
			{
			CLIENT_PRINT(("DChannelIicSlaveClient::CheckDataRead, index=%d, value =0x%x, expected 0",index,currVal));
			r=KErrCorrupt;
			}
		++index;
		}

	TInt8 checkVal=0x10; // first value written by simulated bus channel
	TInt8 endOfData= (TInt8)(iRxRegOffset+(numWords*iRxRegGranularity));
	TInt8 wordCount=0;
	while(index<endOfData)
		{
		currVal = *(iRxBuf+index);
		if(checkVal != currVal)
			{
			CLIENT_PRINT(("DChannelIicSlaveClient::CheckDataRead, index=%d, value =0x%x, expected 0x%x",index,currVal,checkVal));
			r=KErrCorrupt;
			}
		if(++wordCount == iRxRegGranularity)
			{
			wordCount=0;
			checkVal++;
			}
		++index;
		}

	while(index<KRxBufSizeInBytes)
		{
		currVal=*(iRxBuf+index);
		if(currVal != 0)
			{
			CLIENT_PRINT(("DChannelIicSlaveClient::CheckDataRead, index=%d, value =0x%x, expected 0",index,currVal);)
			r=KErrCorrupt;
			}
		++index;
		}
	return r;
	}


TInt DChannelIicSlaveClient::CheckDataWritten()
	{
	TInt r=KErrNone;
	// The pattern in the transmit buffer used by the simulated bus channel contains a incrementing count
	// from 0 to (KTxBufSizeInBytes-1), therefore the first value to be present in the check buffer will be
	// represented by the required offset.

	// The bytes "transmitted" are stored in the simulated bus' iTxCheckBuf
	// Since the simulated bus channel is also in the kernel process it shares the same address space
	// Get the address of the buffer
	TUint testId = (((TUint)(RBusDevIicClient::ECtrlIoTxChkBuf))<<1)&0x3FFFFFFF;
	CLIENT_PRINT(("DChannelIicSlaveClient::CheckDataWritten invoking StaticExtension ECtrlIoTxChkBuf with iBusId=0x%x, testId=0x%x, iBusTxCheckBuf=0x%x\n",iBusId,testId,iBusTxCheckBuf));
	r = StaticExtension(iBusId, testId, &iBusTxCheckBuf, NULL);
	if(r!=KErrNone)
		{
		CLIENT_PRINT(("DChannelIicSlaveClient::CheckDataWritten StaticExtension ECtrlIoTxChkBuf returned %d\n",r));
		return r;
		}

	// Check that the values in the check buffer increment for the
	// required number of words, and that any remaining bytes in the check buffer are set to zero.
	TInt8 firstValue = iTxRegOffset;
	TInt8 currVal = 0;
	TInt8 wordsWritten = 0;
	wordsWritten=(iTxReqNumWords>iNumRegTxWords)?iNumRegTxWords:iTxReqNumWords;
	TInt8 index=0;
	while(index<(wordsWritten*iTxRegGranularity))
		{
		currVal=*(iBusTxCheckBuf+index);
		if(currVal != (TInt8)(firstValue+index))
			{
			CLIENT_PRINT(("DChannelIicSlaveClient::CheckDataWritten, index=%d, value =0x%x, expected 0x%x",index,currVal,(TInt8)(firstValue+index)));
			r=KErrCorrupt;
			}
		++index;
		}
	while(index<(iNumRegTxWords*iTxRegGranularity))
		{
		currVal=*(iBusTxCheckBuf+index);
		if(currVal != 0)
			{
			CLIENT_PRINT(("DChannelIicSlaveClient::CheckDataWritten, index=%d, value =0x%x, expected 0",index,currVal));
			r=KErrCorrupt;
			}
		++index;
		}
	return r;
	}


TInt DChannelIicSlaveClient::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& /*aVer*/)
	{
	CLIENT_PRINT(("> DChannelIicSlaveClient::DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion &aVer)"));

	TInt r = Kern::DynamicDfcQCreate(iDfcQue,KIicSlaveClientThreadPriority,KIicSlaveClientThreadName);
	if(r!=KErrNone)
		return r;
	SetDfcQ(iDfcQue);

	// Allocate buffers for Rx, Tx operations
	iRxBuf = new TUint8[KRxBufSizeInBytes];
	iTxBuf = new TUint8[KTxBufSizeInBytes];
	if((iRxBuf == NULL)||(iTxBuf == NULL))
		return KErrNoMemory;
	// Start receiving messages
	iMsgQ.Receive();

	return r;
	}

TInt DChannelIicSlaveClient::InitSlaveClient()
	{
	iNotif = new TIicBusSlaveCallback(DChannelIicSlaveClient::SlaveClientCallbackFunc, (TAny*)this, iDfcQue, KIicSlaveClientDfcPriority);
	if(iNotif == NULL)
		{
		CLIENT_PRINT(("> DChannelIicSlaveClient::InitSlaveClient ERROR unable to allocate space TIicBusSlaveCallback* iNotif \n"));
		return KErrNoMemory;
		}
	return KErrNone;
	}

void DChannelIicSlaveClient::HandleMsg(TMessageBase* aMsg)
	{
    TThreadMessage& m=*(TThreadMessage*)aMsg;
    TInt id=m.iValue;

	CLIENT_PRINT((" >ldd: DChannelIicSlaveClient::HandleMsg(TMessageBase* aMsg) id=%d\n", id));

	if (id == (TInt)ECloseMsg)
		{
	    iMsgQ.iMessage->Complete(KErrNone,EFalse);
		return;
		}

    if (id<0)
		{
		TRequestStatus* pS=(TRequestStatus*)m.Ptr0();
		TInt r=DoRequest(~id, pS, m.Ptr1(), m.Ptr2());
		if (r!=KErrNone)
			{
	    	Kern::RequestComplete(iClient, pS, r);
			}
		m.Complete(KErrNone,ETrue);
		}
    else
	if((id>=0)&&(id!=KMaxTInt))
		{
		TInt r=DoControl(id,m.Ptr0(),m.Ptr1());
		m.Complete(r,ETrue);
		}
	}


TInt DChannelIicSlaveClient::DoControl(TInt aId, TAny* a1, TAny* a2)
	{
	CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoked with aId=0x%x, a1=0x%x, a2=0x%x\n", aId,a1,a2));
	TInt r=KErrNone;
	// To support testing, any values of aId for StaticExtension must be shifted left one place
	// and for a Slave, the two msbs must be zero
	TInt ctrlIoVal = 0;
	if((aId & KTestSlaveControlIo) == KTestSlaveControlIo)
		ctrlIoVal = (aId << 1) & 0x3FFFFFFF;

	switch(aId)
		{
		case(RBusDevIicClient::EInitSlaveClient):
			{
			r=InitSlaveClient();
			break;
			}

		case(RBusDevIicClient::ECaptureChanSync):
			{
			// a1 is a pointer to the TDes8* aConfigHdr
			// a2 is a pointer to TInt* parms[2], where:
			// parms[0]=(TInt*)aBusId;
			// parms[1]=&aChannelId;
			//
			TInt* parms[2];
			r=Kern::ThreadRawRead(iClient,a2,&(parms[0]),2*sizeof(TInt*));
			if(r!=KErrNone)
				break;	// Can't proceed if can't access request parameters
			//
		  	TInt hdrSize = Kern::ThreadGetDesLength(iClient,a1);
			CLIENT_PRINT(("DChannelIicSlaveClient::DoControl hdrSize = 0x%x\n",hdrSize));
  			if (hdrSize<=0)
				{
				CLIENT_PRINT(("DChannelIicSlaveClient::DoControl ERROR, hdrSize is invalid\n"));
 				r = KErrArgument;
 				break;
				}
			if((iConfigHdr = HBuf8::New(hdrSize)) == NULL)
				return KErrNoMemory;
			r = Kern::ThreadDesRead(iClient,a1,*iConfigHdr,0);
			if(r!=KErrNone)
				{
				delete iConfigHdr;
				break;
				}
			// Store the address of the user-side variable to update with the ChannelId
			iClientChanId=parms[1];

			CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking (synchronous) CaptureChannel\n"));
			r = CaptureChannel((TInt)(parms[0]), iConfigHdr, iNotif, iChannelId);
			if(r != KErrNone)
			    {
			    delete iConfigHdr;
			    break;
			    }
			CLIENT_PRINT(("DChannelIicSlaveClient::DoControl CaptureChannelgave iChannelId=0x%x\n",iChannelId));

			r = Kern::ThreadRawWrite(iClient,iClientChanId,&iChannelId,sizeof(TInt));
			if(r != KErrNone)
				delete iConfigHdr;
			break;
			}

		case(RBusDevIicClient::EReleaseChan):
			{
			// a1 represents TInt aChannelId
			CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking ReleaseChannel\n"));
			r = ReleaseChannel((TInt)a1);
			delete iConfigHdr;
			break;
			}

		case(RBusDevIicClient::ERegisterRxBuffer):
			{
			// a1 represents TInt aChannelId
			// a2 represents (TAny*) of TInt8 parms[3] where parms[0]=aBufGranularity; parms[1]=aNumWords; parms[2]=aOffset
			TInt8 parms[3];
			r=Kern::ThreadRawRead(iClient,a2,&(parms[0]),3*sizeof(TInt8));
			if(r!=KErrNone)
				break;	// Can't proceed if can't access request parameters
			// Store parameters for checking in the callback
			iRxRegGranularity = parms[0];
			iRxRegOffset= parms[2];
			iNumRegRxWords=parms[1];

			CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking RegisterRxBuffer\n"));
			TPtr8 rxPtr(iRxBuf,KRxBufSizeInBytes);
			r = RegisterRxBuffer((TInt)a1, rxPtr, parms[0], parms[1], parms[2]);
			break;
			}

		case(RBusDevIicClient::ERegisterTxBuffer):
			{
			// a1 represents TInt aChannelId
			// a2 represents (TAny*) of TInt8 parms[3] where parms[0]=aBufGranularity; parms[1]=aNumWords; parms[2]=aOffset
			TInt8 parms[3];
			r=Kern::ThreadRawRead(iClient,a2,&(parms[0]),3*sizeof(TInt8));
			if(r!=KErrNone)
				break;	// Can't proceed if can't access request parameters
			// Store parameters for checking in the callback
			iTxRegGranularity = parms[0];
			iTxRegOffset= parms[2];
			iNumRegTxWords=parms[1];

			CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking RegisterTxBuffer\n"));
			TPtr8 txPtr(iTxBuf,KTxBufSizeInBytes);
			r = RegisterTxBuffer((TInt)a1, txPtr, parms[0], parms[1], parms[2]);
			break;
			}

		case(RBusDevIicClient::ESetNotifTrigger):
			{
			// a1 represents (TAny*) of TRequestStatus* aStatus
			// a2 represents (TAny*) of TInt parms[2] where parms[0]=aChannelId; parms[1]=aTrigger
			TInt parms[2];
			r=Kern::ThreadRawRead(iClient,a2,&(parms[0]),2*sizeof(TInt));
			if(r!=KErrNone)
				break;	// Can't proceed if can't access request parameters
			CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking SetNotificationTrigger with aStatus=0x%x, aChannelId=0x%x, aTrigger=0x%x\n",a1,parms[0],parms[1]));
			if(a1 == NULL)
				{
				r = KErrArgument;
				break;
				}
			iStatus=(TRequestStatus*)a1;
			// Set the flags for duplex processing
			if((parms[1]&ERxAllBytes)&&(parms[1]&ETxAllBytes))
				iFullDuplexReq |= (ERxAllBytes|ETxAllBytes);
			r = SetNotificationTrigger(parms[0],parms[1]);
			if(r == KErrTimedOut)
				r=KErrNone;	// KErrTimedOut is returned if the Client has not interacted with IIC for a while
			break;
			}

		case(RBusDevIicClient::ECtrlIoNotifNoTrigger):
			{
			// a1 represents (TAny*) of aChannelId
			// a2 represents (TAny*) of aTrigger
			TInt chanId = (TInt)a1;
			TInt trigger = (TInt)a2;
			// No TRequestStatus is accessed because the call to SetNotificationTrigger
			// is either with zero (when it is valid to do so), or it is being called with a
			// trigger value that is expected to be rejected.
			r = SetNotificationTrigger(chanId,trigger);

			if(r == KErrNone)
				{
				if((trigger&ERxAllBytes)&&(trigger&ETxAllBytes))
					iFullDuplexReq |= (ERxAllBytes|ETxAllBytes);
				}
			if(r == KErrTimedOut)
				r=KErrNone;	// KErrTimedOut is returned if the Client has not interacted with IIC for a while
			break;
			}

		case(RBusDevIicClient::ECtrlIoRxWords):
			{
			// a1 represents TInt aBusId
			// a2 represents (TAny*) of TInt parms[2] where parms[0]=aChannelId; parms[1]=aNumWords
			TInt parms[2];
			r=Kern::ThreadRawRead(iClient,a2,&(parms[0]),2*sizeof(TInt));
			if(r!=KErrNone)
				break;	// Can't proceed if can't access request parameters
			// Prepare iRxBuf
			memset(iRxBuf,0,KRxBufSizeInBytes);
			// Store the number of words for checking in the callback
			iRxReqNumWords=(TInt8)(parms[1]);

			TInt tempTrigger=0;
			// Set the expected result
			tempTrigger |= ERxAllBytes;
			if(parms[1] < iNumRegRxWords)
				tempTrigger |= ERxUnderrun;
			if(parms[1] > iNumRegRxWords)
				tempTrigger |= ERxOverrun;
			if(iExpectedTrigger != EGeneralBusError)
				iExpectedTrigger |= tempTrigger;
			else
				iBlockedTrigger |= tempTrigger;
			CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking StaticExtension (ECtrlIoRxWords) with aBusId=0x%x, aChannelId=0x%x, aNumBytes=0x%x\n",(TInt)a1,parms[0],parms[1]));
			r = StaticExtension((TUint)a1, (TUint)ctrlIoVal, (TAny*)(parms[0]), (TAny*)(parms[1]));
			if(r == KErrTimedOut)
				r=KErrNone;	// KErrTimedOut is returned if the Client has not interacted with IIC for a while
			break;
			}

		case(RBusDevIicClient::ECtrlIoTxWords):
			{
			// a1 represents TInt aBusId
			// a2 represents (TAny*) of TInt parms[2] where parms[0]=aChannelId; parms[1]=aNumWords
			TInt parms[2];
			r=Kern::ThreadRawRead(iClient,a2,&(parms[0]),2*sizeof(TInt));
			if(r!=KErrNone)
				break;	// Can't proceed if can't access request parameters
			// Prepare iTxBuf
			TUint8* ptr=iTxBuf;
			for(TInt offset=0; offset<KRxBufSizeInBytes; ++offset)
				*ptr++=(TUint8)offset;
			// Store the number of words for checking in the callback
			iTxReqNumWords=(TInt8)(parms[1]);
			TInt tempTrigger=0;
			// Set the expected result
			tempTrigger |= ETxAllBytes;
			if(parms[1] < iNumRegTxWords)
				tempTrigger |= ETxOverrun;
			if(parms[1] > iNumRegTxWords)
				tempTrigger |= ETxUnderrun;
			if(iExpectedTrigger != EGeneralBusError)
				iExpectedTrigger |= tempTrigger;
			else
				iBlockedTrigger |= tempTrigger;

			// The bytes "transmitted" are stored in the simulated bus' iTxCheckBuf
			// Since the simulated bus channel is also in the kernel process it shares the same address space
			// Get the address of the buffer
			// As part of the callback invoked by IIC, this client will check the data stored by the simulated
			// bus. Since the simulated bus channel is also in the kernel process it shares the same address space,
			// so the buffer can be accessed directly - but the buffer is not created until it receives the following
			// StaticExtension command, so the bus identifier represented by a1 is stored to allow accessing the buffer
			// adddress from the callback.
			iBusId=(TUint)a1;
			CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking StaticExtension (ECtrlIoTxWords) with aBusId=0x%x, aChannelId=0x%x, aNumBytes=0x%x\n",(TInt)a1,parms[0],parms[1]));
			aId<<=1;
			r = StaticExtension((TUint)a1, (TUint)ctrlIoVal, (TAny*)(parms[0]), (TAny*)(parms[1]));
			break;
			}

		case(RBusDevIicClient::ECtrlIoRxTxWords):
			{
			// a1 represents TInt aBusId
			// a2 represents (TAny*) of TInt parms[3] where parms[0]=aChannelId; parms[1]=aNumRxWords; parms[2]=aNumTxWords
			TInt parms[3];
			r=Kern::ThreadRawRead(iClient,a2,&(parms[0]),3*sizeof(TInt));
			if(r!=KErrNone)
				break;	// Can't proceed if can't access request parameters
			// Prepare iRxBuf, iTxBuf
			memset(iRxBuf,0,KRxBufSizeInBytes);
			TUint8* ptr=iTxBuf;
			for(TInt offset=0; offset<KRxBufSizeInBytes; ++offset)
				*ptr++=(TUint8)offset;

			// Store the number of words for checking in the callback
			iRxReqNumWords=(TInt8)(parms[1]);
			iTxReqNumWords=(TInt8)(parms[2]);

			TInt tempTrigger=0;
			// Set the expected result
			tempTrigger |= (ERxAllBytes|ETxAllBytes);

			if(parms[1] < iNumRegRxWords)
				tempTrigger |= ERxUnderrun;
			if(parms[1] > iNumRegRxWords)
				tempTrigger |= ERxOverrun;

			if(parms[2] < iNumRegTxWords)
				tempTrigger |= ETxOverrun;
			if(parms[2] > iNumRegTxWords)
				tempTrigger |= ETxUnderrun;

			if(iExpectedTrigger != EGeneralBusError)
				iExpectedTrigger |= tempTrigger;
			else
				iBlockedTrigger |= tempTrigger;

			// The bytes "transmitted" are stored in the simulated bus' iTxCheckBuf
			// Since the simulated bus channel is also in the kernel process it shares the same address space
			// Get the address of the buffer
			// As part of the callback invoked by IIC, this client will check the data stored by the simulated
			// bus. Since the simulated bus channel is also in the kernel process it shares the same address space,
			// so the buffer can be accessed directly - but the buffer is not created until it receives the following
			// StaticExtension command, so the bus identifier represented by a1 is stored to allow accessing the buffer
			// adddress from the callback.
			iBusId=(TUint)a1;

			CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking StaticExtension (ECtrlIoRxTxWords) with aBusId=0x%x, aChannelId=0x%x, aNumRxBytes=0x%x, aNumTxBytes=0x%x\n",(TInt)a1,parms[0],parms[1],parms[2]));
			r = StaticExtension((TUint)a1, (TUint)ctrlIoVal, (TAny*)(parms[0]), (TAny*)(&(parms[1])));
			if(r == KErrTimedOut)
				r=KErrNone;	// KErrTimedOut is returned if the Client has not interacted with IIC for a while
			break;
			}

		case(RBusDevIicClient::ECtlIoBusError):
			{
			// a1 represents TInt aBusId
			// a2 represents TInt aChannelId
			// Set the expected result
			iExpectedTrigger |= EGeneralBusError;
			CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking StaticExtension (ECtlIoBusError) \n"));
			r = StaticExtension((TUint)a1, (TUint)ctrlIoVal, (TAny*)a2, NULL);
			break;
			}

		case(RBusDevIicClient::ECtrlIoUnblockNotification):
			{
			// a1 represents TInt aBusId
			// a2 represents TInt aChannelId
			iExpectedTrigger = iBlockedTrigger;
			iBlockedTrigger=0;
			CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking StaticExtension (ECtrlIoUnblockNotification) \n"));
			r = StaticExtension((TUint)a1, (TUint)ctrlIoVal, (TAny*)a2, NULL);
			break;
			}

		case(RBusDevIicClient::ECtrlIoBlockNotification):
			{
			// a1 represents TInt aBusId
			// a2 represents TInt aChannelId
			iBlockedTrigger = iExpectedTrigger;
			iExpectedTrigger = EGeneralBusError;	// For this test, just interested in if the timeout is detected
													// iExpectedTrigger will be reinstated prior to unblocking
			CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking StaticExtension (ECtrlIoBlockNotification) \n"));
			r = StaticExtension((TUint)a1, (TUint)ctrlIoVal, (TAny*)a2, NULL);
			break;
			}

		case(RBusDevIicClient::ECtrlIoUpdTimeout):
			{
			// a1 represents TInt aBusId
			// a2 represents TInt aChannelId

			// For this test, instruct the simulated bus to do the following for the Master and Client timeout values:
			// (1) Read the current timeout value and check that it is set to the default
			// (2) Set it to different value
			// (3) Read it back to check success
			// (4) Return to the original value, and readback to confirm
			CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking StaticExtension (ECtrlIoBlockNotification) \n"));
			r = StaticExtension((TUint)a1, (TUint)ctrlIoVal, NULL, NULL);
			break;
			}

		default:
			{
			CLIENT_PRINT(("DChannelIicSlaveClient::DoControl - unrecognised value for aId=0x%x\n",aId));
			r=KErrArgument;
			break;
			}

		}
	return r;
	}

TInt DChannelIicSlaveClient::DoRequest(TInt aId, TRequestStatus* aStatus, TAny* a1, TAny* a2)
	{
	CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest invoked with aId=0x%x, aStatus=0x%x, a1=0x%x, a2=0x%x\n", aId,aStatus,a1,a2));

	TInt r=KErrNone;
	switch(aId)
		{
		case(RBusDevIicClient::ECaptureChanAsync):
			{
			// a1 is a pointer to the TDes8* aConfigHdr
			// a2 is a pointer to TInt* parms[2], where:
			// parms[0]=(TInt*)aBusId;
			// parms[1]=&aChannelId;
			//
			TInt* parms[2];
			r=Kern::ThreadRawRead(iClient,a2,&(parms[0]),2*sizeof(TInt*));
			if(r!=KErrNone)
				break;	// Can't proceed if can't access request parameters
			//
		  	TInt hdrSize = Kern::ThreadGetDesLength(iClient,a1);
			CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest hdrSize = 0x%x\n",hdrSize));
  			if (hdrSize<=0)
				{
				CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest ERROR, hdrSize is invalid\n"));
 				return KErrArgument;
				}
			if((iConfigHdr = HBuf8::New(hdrSize)) == NULL)
				return KErrNoMemory;
			if((r = Kern::ThreadDesRead(iClient,a1,*iConfigHdr,0))!=KErrNone)
				{
				delete iConfigHdr;
				return r;
				}
			iStatus=aStatus;
			// Store the address of the user-side variable to update with the ChannelId
			iClientChanId=parms[1];
			// Invoke the IIC API
			CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest invoking (asynchronous) CaptureChannel\n"));
			r = CaptureChannel((TInt)(parms[0]), iConfigHdr, iNotif, iChannelId, ETrue);
			if(r != KErrNone)
			    delete iConfigHdr;
			CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest (asynchronous) CaptureChannel returned %d\n",r));
			break;
			}

		case(RBusDevIicClient::ECtrlIoOvUndRunRxTx):
			{
			iBusId = (TInt)a1;
			iChannelId = (TInt)a2;
			iStatus=aStatus;
			CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest status = 0x%x, busId = 0x%x, chanId =0x%x\n",iStatus,iBusId,iChannelId));

			// This test is state-machine driven. It is instigated from this point, then subsequent steps
			// are handled in the callback funciton SlaveClientCallbackFunc
			//
			// Check we in the appropriate state to start
			if(iTestOverUnderState == EStartState)
				{
				// Re-use the previously-provided buffers. Just request the initial notification triggers,
				// the simulate the first event (RxOverrun).
				r = SetNotificationTrigger(iChannelId, (ERxAllBytes | ERxOverrun | ETxAllBytes | ETxUnderrun));
				if(r != KErrNone)
					{
					CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest SetNotificationTrigger returned %d\n",r));
					}
				else
					{
					// Trigger now set, so simulate the required event
					iExpectedTrigger = ERxAllBytes | ERxOverrun | ETxAllBytes | ETxUnderrun;
					iFullDuplexReq |= (ERxAllBytes|ETxAllBytes);
					iTestOverUnderState = ERxOverrun_1;	// Prepare for callback
					// The requested number of words when the buffer was registered was 8, so simulate 10
					// to provoke an RxOverrun event.
					TInt numWords=10;
					// To support testing, any values of aId for StaticExtension must be shifted left one place
					// and for a Slave, the two msbs must be zero
					TInt ctrlIoVal = RBusDevIicClient::ECtrlIoRxWords;
					ctrlIoVal = (ctrlIoVal << 1) & 0x3FFFFFFF;
					r = StaticExtension(iBusId, ctrlIoVal, (TAny*)iChannelId, (TAny*)numWords);
					}
				}
			else
				{
				CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest ECtrlIoOvUndRunRxTx, iTestOverUnderState = 0x%x\n",iTestOverUnderState));
				r=KErrGeneral;
				}
			break;
			}

		default:
			{
			CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest - unrecognised value for aId=0x%x\n",aId));
			r=KErrArgument;
			break;
			}
		}
	return r;
	}