kernel/eka/drivers/iic/iic_channel.cpp
changeset 9 96e5fb8b040d
child 43 c1f20ce4abcf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/drivers/iic/iic_channel.cpp	Thu Dec 17 09:24:54 2009 +0200
@@ -0,0 +1,1145 @@
+// 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:
+// e32\drivers\iic_channel.cpp
+// IIC Channel Platform Independent Layer (PIL)
+//
+
+#include <drivers/iic_channel.h>
+#ifdef IIC_INSTRUMENTATION_MACRO
+#include <drivers/iic_trace.h>
+#endif
+
+// The timer call back function which calls the PSL's HandleSlaveTimeout()
+// Note that this assumes that the channel thread has been unblocked - if this
+// is not the case, the callback will never get to run
+
+TInt DIicBusChannelMaster::DoCreate()
+    {return KErrNone;}
+
+void DIicBusChannelMaster::Lock()
+    {NKern::FMWait(&iTransactionQLock);}
+
+void DIicBusChannelMaster::Unlock()
+    {NKern::FMSignal(&iTransactionQLock);}
+
+TIicBusTransaction* DIicBusChannelMaster::NextTrans(TIicBusTransaction* aTrans)
+    {
+    // call multi-transaction call back function to get next transaction
+    if((aTrans->iFlags&KTransactionWithPreamble)&&(aTrans->iFlags&KTransactionWithMultiTransc))
+        return ((TIicBusTransactionPreambleExt*)aTrans)->iMultiTransc(aTrans, ((TIicBusTransactionPreambleExt*)aTrans)->iMultiTranscArg);
+    else if(aTrans->iFlags&KTransactionWithMultiTransc)
+        return ((TIicBusTransactionMultiTransc*)aTrans)->iMultiTransc(aTrans, ((TIicBusTransactionMultiTransc*)aTrans)->iMultiTranscArg);
+    else
+        return NULL;
+    }
+
+void DIicBusChannelMaster::UnlockAndKick()
+    {iTransQDfc.Enque(&iTransactionQLock);}
+
+void DIicBusChannelMaster::SlaveTimeoutCallback(TAny* aPtr)
+	{
+
+	DIicBusChannelMaster* aChanMaster=(DIicBusChannelMaster* )aPtr;
+	TInt r = aChanMaster->HandleSlaveTimeout();
+	aChanMaster->CompleteRequest(r);
+	}
+
+TInt DIicBusChannelMaster::TransFlow(TIicBusTransaction* aTransaction)
+    {
+    if(aTransaction->iHalfDuplexTrans == NULL)
+        return KErrArgument;
+    else if(aTransaction->iFullDuplexTrans == NULL)
+        return DIicBusChannel::EHalfDuplex;
+    else return DIicBusChannel::EFullDuplex;
+    }
+
+TInt8 DIicBusChannelMaster::IsMasterBusy()
+    {
+    if((iTransCount&~KTransCountMsBit) == 0)
+        return 0;
+    else return 1;
+    }
+
+DIicBusChannelMaster::DIicBusChannelMaster(TBusType aBusType, TChannelDuplex aChanDuplex)
+		: DIicBusChannel(DIicBusChannel::EMaster, aBusType, aChanDuplex),
+		iTransQDfc(DIicBusChannelMaster::MsgQFunc, this, NULL, 1), iChannelReady(EFalse)
+	{
+	new(&iTimeoutTimer) NTimer(SlaveTimeoutCallback,this);
+	}
+
+DIicBusChannelMaster::~DIicBusChannelMaster()
+	{
+	delete iSlaveTimeoutDfc;
+	}
+
+TInt DIicBusChannelMaster::Init()
+	{
+	iSlaveTimeoutDfc = new TDfc(SlaveTimeoutCallback,(TAny*)this, 7);	// Highest Dfc priority
+	if(!iSlaveTimeoutDfc)
+		return KErrNoMemory;
+	else
+		return KErrNone;
+	}
+
+// Function to used to indicate if the Slave response has exceeded
+// an expected time
+TInt DIicBusChannelMaster::StartSlaveTimeOutTimer(TInt aTime)
+	{
+	TInt r = iTimeoutTimer.OneShot(NKern::TimerTicks(aTime),(*iSlaveTimeoutDfc));
+	return r;
+	}
+
+void DIicBusChannelMaster::SetDfcQ(TDfcQue* aDfcQue)
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::SetDfcQ 0x%x\n",aDfcQue));
+	__ASSERT_DEBUG(aDfcQue!=NULL, Kern::Fault(KIicChannelPanic,__LINE__));
+	iDfcQ=aDfcQue;
+	iTransQDfc.SetDfcQ(iDfcQ);
+	iSlaveTimeoutDfc->SetDfcQ(iDfcQ);
+	Lock();
+	__ASSERT_DEBUG(!iChannelReady, Kern::Fault(KIicChannelPanic,__LINE__));
+	if (!iTransactionQ.IsEmpty())
+		{
+		iTransaction=(TIicBusTransaction*)(iTransactionQ.First()->Deque());
+		__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::SetDfcQ got %08x",iTransaction));
+		iTransaction->iState=TIicBusTransaction::EAccepted;
+		iCurrentTransaction = iTransaction;
+		UnlockAndKick();
+		}
+	else
+		{
+		__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::SetDfcQ"));
+		iChannelReady=ETrue;
+		iTransaction=NULL;
+		iCurrentTransaction = NULL;
+		Unlock();
+		}
+	}
+
+void DIicBusChannelMaster::CompleteRequest(TInt aResult)
+	{
+	TIicBusTransaction* nextTrans=NextTrans(iCurrentTransaction);
+
+	if((aResult != KErrNone)||(nextTrans == NULL))
+		EndTransaction(iTransaction,aResult,iTransaction->iCallback);
+	else
+		{
+		nextTrans->iBusId = iCurrentTransaction->iBusId; // Pass the bus configuration info to the PSL
+		iCurrentTransaction = nextTrans;
+		DoRequest(nextTrans);
+		}
+	}
+
+#ifdef MASTER_MODE
+
+/*
+For Master-side transaction queuing APIs
+the Channel implementation sends the transaction as a message to the Channel's message queue,
+optionally blocking the client thread on the message's semaphore (synchronous APIs).
+*/
+TInt DIicBusChannelMaster::QueueTransaction(TIicBusTransaction* aTransaction)
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::QueueTransaction, aTransaction=0x%x\n",aTransaction));
+	// Send the transaction as a message to the Channel's message queue
+	// Synchronous API, so block the calling thread during the processing
+	TInt r = QueueTransaction(aTransaction, NULL);
+	if(r!=KErrNone)
+		return r;	// Transaction was not queued - so don't wait for a notification that it completed.
+
+	__KTRACE_OPT(KIIC, Kern::Printf("<DIicBusChannelMaster::QueueTransaction ret %d",aTransaction->iResult));
+	return aTransaction->iResult;
+	}
+
+TInt DIicBusChannelMaster::QueueTransaction(TIicBusTransaction* aTransaction, TIicBusCallback* aCallback)
+	{
+	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::QueueTransaction, aTransaction=0x%x, aCallback=0x%x\n",aTransaction,aCallback));
+
+	// Check aTransaction is non-NULL (aCallback may be NULL if the synchronous operation is required).
+	if(aTransaction == NULL)
+		{
+		return KErrArgument;
+		}
+
+	// Send the transaction as a message to the Channel's message queue and return
+	aTransaction->iCallback = aCallback;
+	if(aCallback != NULL)
+		{
+		aCallback->iTransaction = aTransaction;
+		}
+
+	// Call the PSL implementation to check that the header is valid for this channel
+	TInt r = CheckHdr(aTransaction->iHeader);
+	if(r!=KErrNone)
+		{
+		return r;
+		}
+
+	// Duplex operation is indicated in the transaction object
+	if((TransFlow((TIicBusTransaction*)aTransaction) == DIicBusChannel::EFullDuplex) &&
+	   (ChannelDuplex()                              != DIicBusChannel::EFullDuplex))
+		{
+		return KErrNotSupported;
+		}
+
+	DThread* pC =& Kern::CurrentThread();
+	Lock();
+	__ASSERT_DEBUG(aTransaction->iState == TIicBusTransaction::EFree, Kern::Fault(KIicChannelPanic,__LINE__));
+	if(!(iTransCount & KTransCountMsBit))
+		{
+		if(iTransCount < ~KTransCountMsBit)
+			{
+			++iTransCount;
+			}
+		else
+			{
+			Unlock();
+			return KErrOverflow;
+			}
+		}
+
+	aTransaction->iSyncNotification.iCount = 0;
+	aTransaction->iSyncNotification.iOwningThread = &pC->iNThread;
+	pC->Open();
+	if (iChannelReady)
+		{
+		aTransaction->iState = TIicBusTransaction::EAccepted;
+		iTransaction = aTransaction;
+		iCurrentTransaction = aTransaction;
+		iChannelReady = EFalse;
+		UnlockAndKick();
+		}
+	else
+		{
+		iTransactionQ.Add(aTransaction);
+		aTransaction->iState = TIicBusTransaction::EDelivered;
+		Unlock();
+		}
+
+	// Wait on a semaphore if called from synchronous version
+	if(aCallback == NULL)
+		{
+		NKern::FSWait(&aTransaction->iSyncNotification);
+		}
+
+	return KErrNone;
+	}
+
+TInt DIicBusChannelMaster::CancelTransaction(TIicBusTransaction* aTransaction)
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::CancelTransaction, aTransaction=0x%x\n",aTransaction));
+
+	// Check aTransaction is non-NULL
+	if(aTransaction == NULL)
+		{
+		return KErrArgument;
+		}
+	// If the method is called on a synchronous transaction return KErrNotSupported
+	if(aTransaction->iCallback == NULL)
+		{
+		return KErrNotSupported;
+		}
+	DThread* pT = NULL;
+	Lock();
+
+	TInt r = KErrNone;
+	switch(aTransaction->iState)
+		{
+		case TIicBusTransaction::EDelivered:
+			{
+			aTransaction->Deque();
+			pT=_LOFF(aTransaction->iSyncNotification.iOwningThread,DThread,iNThread);
+			aTransaction->iState=TIicBusTransaction::EFree;
+			--iTransCount; // Count must be greater than zero if the transaction is in this state
+			r = KErrCancel;
+			break;
+			}
+
+		case TIicBusTransaction::EAccepted:
+			{
+			r = KErrInUse;
+			break;
+			}
+
+		case TIicBusTransaction::EFree:
+			{
+			r = KErrCancel;
+			break;
+			}
+		}
+	Unlock();
+	if (pT)
+		{
+		pT->AsyncClose();
+		}
+
+	return r;
+	}
+
+#else /*MASTER_MODE*/
+
+TInt DIicBusChannelMaster::QueueTransaction(TIicBusTransaction* /*aTransaction*/)
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::QueueTransaction invoked when not in MASTER_MODE!\n"));
+	return KErrNotSupported;
+	}
+
+TInt DIicBusChannelMaster::QueueTransaction(TIicBusTransaction* /*aTransaction*/, TIicBusCallback* /*aCallback*/)
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::QueueTransaction invoked when not in MASTER_MODE!\n"));
+	return KErrNotSupported;
+	}
+
+TInt DIicBusChannelMaster::CancelTransaction(TIicBusTransaction* /*aTransaction*/)
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::CancelTransaction invoked when not in MASTER_MODE!\n"));
+	return KErrNotSupported;
+	}
+#endif/*MASTER_MODE*/
+
+// Invoked in response to receiving a message
+// Function argument is a pointer to the required channel object
+// Invoke the channel's PSL implementation of the DoRequest method with a pointer to the transaction object
+//
+void DIicBusChannelMaster::MsgQFunc(TAny* aPtr)
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::MsgQFunc, aPtr=0x%x\n",aPtr));
+	DIicBusChannelMaster* channel=(DIicBusChannelMaster*)aPtr;
+	TIicBusTransaction* trans = channel->iTransaction;
+
+#ifdef IIC_INSTRUMENTATION_MACRO
+	IIC_MPROCESSTRANS_START_PIL_TRACE;
+#endif
+
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::MsgQFunc trans->iHeader=0x%x\n",trans->iHeader));
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::MsgQFunc trans->iHalfDuplexTrans=0x%x\n",trans->iHalfDuplexTrans));
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::MsgQFunc trans->iFullDuplexTrans=0x%x\n",trans->iFullDuplexTrans));
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::MsgQFunc trans->iCallback=0x%x\n",trans->iCallback));
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::MsgQFunc trans->iFlags=0x%x\n",trans->iFlags));
+
+	TInt r = KErrNone;
+	// invoke the preamble callback function supplied by the client of the IIC if there is any
+	if(GetTransFlags(trans) & KTransactionWithPreamble)
+		{
+		TIicBusTransactionPreamble* transPreamble = (TIicBusTransactionPreamble*)trans;
+		TIicBusPreamble funcPtr=NULL;
+		funcPtr=(GetPreambleFuncPtr(transPreamble));
+		funcPtr(transPreamble,GetPreambleFuncArg(transPreamble));
+		}
+	r = channel->DoRequest(trans);	// Instigate processing in the PSL
+	if(r!=KErrNone)
+		channel->EndTransaction(trans, r, trans->iCallback);
+	}
+
+void DIicBusChannelMaster::EndTransaction(TIicBusTransaction* aTrans, TInt aResult, TIicBusCallback* aCb)
+	{
+#ifdef IIC_INSTRUMENTATION_MACRO
+	IIC_MPROCESSTRANS_END_PIL_TRACE;
+#endif
+	Complete(aResult,aTrans);
+	if(aCb != NULL)
+		{
+		aCb->iResult = aResult;
+		aCb->Enque();
+		}
+	}
+
+void DIicBusChannelMaster::CancelTimeOut()
+	{
+	iTimeoutTimer.Cancel();
+	}
+
+void DIicBusChannelMaster::Complete(TInt aResult, TIicBusTransaction* aTransaction) //Completes a kernel message and receive the next one
+	{
+	__KTRACE_OPT(KIIC, Kern::Printf("MsgB::Complete %08x, %d",this,aResult));
+	Lock();
+	__ASSERT_DEBUG(aTransaction->iState == TIicBusTransaction::EAccepted, Kern::Fault(KIicChannelPanic,__LINE__));
+	aTransaction->iResult=aResult;
+	aTransaction->iState=TIicBusTransaction::EFree;
+	--iTransCount;
+	DThread* pT=_LOFF(aTransaction->iSyncNotification.iOwningThread,DThread,iNThread);
+	__ASSERT_DEBUG(!iChannelReady, Kern::Fault(KIicChannelPanic,__LINE__));
+	if (!iTransactionQ.IsEmpty())
+		{
+		TIicBusTransaction* pM=(TIicBusTransaction*)iTransactionQ.First()->Deque();
+		__KTRACE_OPT(KIIC, Kern::Printf("rxnext: got %08x",pM));
+		pM->iState=TIicBusTransaction::EAccepted;
+		iTransaction = pM;
+		iCurrentTransaction = pM;
+		iTransQDfc.Enque();
+		}
+	else
+		{
+		__KTRACE_OPT(KIIC, Kern::Printf("rxnext"));
+		iChannelReady=ETrue;
+		iTransaction=NULL;
+		iCurrentTransaction = NULL;
+		}
+	NKern::FSSignal(&aTransaction->iSyncNotification,&iTransactionQLock);
+	pT->AsyncClose();
+	}
+
+TInt DIicBusChannelMaster::StaticExtension(TUint /*aFunction*/, TAny* /*aParam1*/, TAny* /*aParam*/)
+ 	{
+ 	return KErrNotSupported;
+ 	}
+
+TInt DIicBusChannelMaster::Spare1(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
+	{
+	return KErrNotSupported;
+	}
+
+#ifdef SLAVE_MODE
+
+TInt DIicBusChannelSlave::CaptureChannel(TDes8* aConfigHdr, TIicBusSlaveCallback* aCallback, TInt& aChannelId, TBool aAsynch)
+	{
+	// Only one client can have access to the Slave channel at any one time. Any subsequent attempts to capture the
+	// same channel should return an error.
+	//
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::CaptureChannel\n"));
+	if((aConfigHdr == NULL) || (aCallback == NULL))
+		{
+	    __KTRACE_OPT(KIIC, Kern::Printf("ERROR: non-NULL argument aConfigHdr=0x%x, aCallback=0x%x\n",aConfigHdr,aCallback));
+		return KErrArgument;
+		}
+	//
+	// Check the header is valid for the channel
+	TInt r = CheckHdr(aConfigHdr);
+	if(r == KErrNone)
+		{
+		// Check Slave channel is available for capture
+		// If iChannelInUse is not set, capture should succeed
+		// If this Slave channel is part of a MasterSlave channel iChannelInUse will already be set
+		// but iClient will still be NULL. In this case, the capture should succeed.
+		TInt intState=__SPIN_LOCK_IRQSAVE(iSpinLock);
+		DThread* pT=&(Kern::CurrentThread());
+		if((iChannelInUse)&&(iClient!=NULL))
+			r=KErrInUse;
+		else
+			{
+			iChannelInUse=1;
+			iClient=pT;
+			}
+		__SPIN_UNLOCK_IRQRESTORE(iSpinLock,intState);
+
+		if(r == KErrNone)
+			{
+			iClient->Open();
+			aCallback->iChannel=this;
+			iNotif = aCallback;
+			iConfigHeader=aConfigHdr;	// Header alread checked, so just assign it
+
+			// Invoke the PSL processing
+			if(aAsynch)
+				{
+				aChannelId = 0; // the client should read iChannelId from the callback object.
+				r=DoRequest(EAsyncConfigPwrUp);
+				}
+			else
+				r=DoRequest(ESyncConfigPwrUp);
+
+			if(r == KErrNone)
+				{
+				if(!aAsynch)	// For asynchronous version there is nothing more to do until the callback is invoked
+					{
+					SetChannelId(aChannelId);
+					iClientTimeoutDfc->SetDfcQ(iNotif->iDfcQ);
+					}
+				}
+			else
+				{
+				// PSL encountered an error
+				ReleaseChannel();
+				if(aAsynch)
+					CompleteAsynchCapture(r);	// Queue the client callback for execution
+				}
+			}
+		}
+	return r;
+	}
+
+TInt DIicBusChannelSlave::ReleaseChannel()
+	{
+	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::ReleaseChannel\n"));
+	 // Release a previously-captured channel.
+	TInt r=KErrNone;;
+	// Ensure that only the channel's client may release the channel
+	DThread* pT=&(Kern::CurrentThread());
+	if(iClient!=pT)			// Direct access since iClient can't be modified while channel is still captured
+		return KErrAccessDenied;
+
+	r=SetNotificationTrigger(0);			// Attempt to clear notification requests
+	if((r!=KErrNone)&&(r!=KErrTimedOut))	// KErrTimedOut refers to an earlier transaction, and is for information only
+		return r;
+	iTimeoutTimer.Cancel();
+	r=DoRequest(EPowerDown);
+	if(r == KErrNone)
+		{
+		TInt intState=__SPIN_LOCK_IRQSAVE(iSpinLock);
+		iClient=NULL;
+		iChannelInUse=0;	// Channel now available for capture by other clients
+		__SPIN_UNLOCK_IRQRESTORE(iSpinLock,intState);
+		pT->AsyncClose();	// Allow Client thread to close now channel has been released
+		}
+	else
+		{
+		// PSL error when releasing the channel - have to assume the hardware has a problem.
+		// The channel is no longer considered "captured" by the controller, i.e. will not accept commands
+		// But not having cleared the busy flag means that it can not be used for master channel
+		// operations if it is part of a MasterSlave channel
+		// Must Fault the Kernel.
+		__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::ReleaseChannel - PSL returned error code %d\n",r));
+		__ASSERT_ALWAYS(EFalse, Kern::Fault(KIicChannelPanic,__LINE__));
+		}
+	return r;
+	}
+
+TInt DIicBusChannelSlave::RegisterRxBuffer(TPtr8 aRxBuffer, TInt8 aBufGranularity, TInt8 aNumWords, TInt8 aOffset)
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::RegisterRxBuffer\n"));
+#ifdef IIC_INSTRUMENTATION_MACRO
+	IIC_SREGRXBUF_START_PIL_TRACE;
+#endif
+	TInt r=KErrNone;
+	// Ensure that only the channel's client may perform this operation
+	DThread* pT=&(Kern::CurrentThread());
+	if(iClient!=pT)			// Direct access since iClient can't be modified while channel is still captured
+		return KErrAccessDenied;
+	//If the buffer pointer is NULL, return KErrArgument
+	if(aRxBuffer.Ptr() == NULL)
+		return KErrArgument;
+	// If a buffer is already registered, a subsequent request to do the same should return KErrAlreadyExists
+	// This will be the case if SetNotificationTrigger has been invoked with any of ERxAllBytes, ERxUnderrun or ERxOverrun
+	if(iReqTrig&(ERxAllBytes|ERxUnderrun|ERxOverrun))
+		r=KErrAlreadyExists;
+	else
+		{
+		iRxBuf=(TInt8*)(aRxBuffer.Ptr());
+		iRxGranularity=aBufGranularity;
+		iNumRxWords=aNumWords;
+		iRxOffset=aOffset;
+		}
+#ifdef IIC_INSTRUMENTATION_MACRO
+	IIC_SREGRXBUF_END_PIL_TRACE;
+#endif
+	 return r;
+	}
+
+TInt DIicBusChannelSlave::RegisterTxBuffer(TPtr8 aTxBuffer, TInt8 aBufGranularity, TInt8 aNumWords, TInt8 aOffset)
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::RegisterTxBuffer	- default implementation\n"));
+#ifdef IIC_INSTRUMENTATION_MACRO
+	IIC_SREGTXBUF_START_PIL_TRACE;
+#endif
+	TInt r=KErrNone;
+	// Ensure that only the channel's client may perform this operation
+	DThread* pT=&(Kern::CurrentThread());
+	if(iClient!=pT)			// Direct access since iClient can't be modified while channel is still captured
+		return KErrAccessDenied;
+	//If the buffer pointer is NULL, return KErrArgument
+	if(aTxBuffer.Ptr() == NULL)
+		return KErrArgument;
+	// If a buffer is already registered and a request is pending, a subsequent request to register a buffer should return
+	// KErrAlreadyExists
+	// This will be the case if SetNotificationTrigger has been invoked with any of ETxAllBytes, ETxUnderrun or ETxOverrun
+	if(iReqTrig&(ETxAllBytes|ETxUnderrun|ETxOverrun))
+		r=KErrAlreadyExists;
+	else
+		{
+		iTxBuf=(TInt8*)(aTxBuffer.Ptr());
+		iTxGranularity=aBufGranularity;
+		iNumTxWords=aNumWords;
+		iTxOffset=aOffset;
+		}
+#ifdef IIC_INSTRUMENTATION_MACRO
+	IIC_SREGTXBUF_END_PIL_TRACE;
+#endif
+	return r;
+	}
+
+TInt DIicBusChannelSlave::SetNotificationTrigger(TInt aTrigger)
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::SetNotificationTrigger invoked with aTrigger=0x%x\n",aTrigger));
+	// Ensure that only the channel's client may perform this operation
+	DThread* pT=&(Kern::CurrentThread());
+	if(iClient!=pT)			// Direct access since iClient can't be modified while channel is still captured
+		return KErrAccessDenied;
+
+	TInt retVal = KErrNone;
+	TInt trigger = aTrigger;
+	switch (iTimerState)	// Handle existing timer conditions
+		{
+		case DIicBusChannelSlave::EInactive:
+			{
+			// In this state no timers have been started - so no action required
+			break;
+			}
+		case DIicBusChannelSlave::EWaitForClient:
+			{
+			// Client has responded within the given time period, so stop the timer.
+			StopTimer();
+			break;
+			}
+		case DIicBusChannelSlave::EWaitForMaster:
+			{
+			// If both Rx and Tx events had been requested, and if ERxOverrun had occurred, the Client
+			// may have called this function with new requests for Rx notifications, in order to
+			// continue reading data sent by the Master. At this point, all Rx request flags in iReqTrig
+			// will have been cleared.
+			// If both Rx and Tx events had been requested, and if ETxUnderrun had occurred, the Client
+			// may have called this function with new requests for Tx notifications, in order to
+			// continue sending data to the Master. At this point, all Tx request flags in iReqTrig will
+			// have been cleared.
+			//
+			// To handle the ERxOverrun situation, aTrigger may specify only the new Rx trigger settings,
+			// or it may also re-specify the exisiting Tx settings. Similarly for the ETxUnderrun, aTrigger
+			// may specify only the new Tx triggers, or both the new Tx triggers and the existing Rx triggers.
+			//
+			// However, if Rx flags are still set in iReqTrig, a request to change the Rx settings
+			// will be rejected (similarly, for Tx).
+			//
+			// If the requested notification is zero, which would represent an attempt to clear all triggers
+			// while the Master may have commenced a transfer, the request will be rejected.
+			__ASSERT_DEBUG(iReqTrig != 0, Kern::Fault(KIicChannelPanic,__LINE__));
+			if(trigger == 0)
+				{
+				return KErrInUse;
+				}
+			TInt allRxFlags = ERxAllBytes | ERxOverrun | ERxUnderrun;
+			TInt allTxFlags = ETxAllBytes | ETxOverrun | ETxUnderrun;
+			// Check the Rx flags
+			TInt rxTrig = iReqTrig & allRxFlags;
+			TInt reqRxTrig = trigger & allRxFlags;
+			if(rxTrig == 0)
+				{
+				rxTrig = reqRxTrig;
+				}
+			else if(reqRxTrig != 0)
+				{
+				// New Rx triggers specified - check that Client is not attempting to modify the existing
+				// settings
+				if(rxTrig ^ reqRxTrig)
+					{
+					// Attempting to change the trigger settings - so reject the request
+					return KErrInUse;
+					}
+				}
+			// Check the Tx flags
+			TInt txTrig = iReqTrig & allTxFlags;
+			TInt reqTxTrig = trigger & allTxFlags;
+			if(txTrig == 0)
+				{
+				txTrig = reqTxTrig;
+				}
+			else if(reqTxTrig != 0)
+				{
+				// New Tx triggers specified - check that Client is not attempting to modify the existing
+				// settings
+				if(txTrig ^ reqTxTrig)
+					{
+					// Attempting to change the trigger settings - so reject the request
+					return KErrInUse;
+					}
+				}
+			// Udate iReqTrig for the new requested trigger
+			// and cancel the timer - since we are now starting a new transfer, we should
+			// allow the Master the time to perform it
+			trigger = rxTrig | txTrig;
+			StopTimer();
+			break;
+			}
+		case DIicBusChannelSlave::EClientTimeout:
+			{
+			// The Client did not respond within the expected time for the previous transfer. As a result,
+			// the transaction will have been terminated for the Client.
+			// Set the return value to inform the Client that it previously exceeded the expected response time
+			retVal = KErrTimedOut;
+			break;
+			}
+		default:
+			{
+			__ASSERT_DEBUG(0, Kern::Fault(KIicChannelPanic,__LINE__));
+			break;
+			}
+		}
+	// Ensure that requests for notification of asynchronous capture of channel is removed, since this
+	// is not a valid event to request (the channel will already have been captured to get this far).
+	// Also ensure that requests for EGeneralBusError are removed, since they are redundant (a notification
+	// for a bus error is unconditional) and just represent overhead.
+	trigger &= ~(EAsyncCaptChan | EGeneralBusError);
+
+	iReqTrig = (TInt8)trigger; 				// Not atomic access since only client thread modifies iReqTrig
+	iAccumTrig = 0;						// New transfer, so initialise accumulated event record
+	TInt reqFlags=0;
+	// Overrun and/or underrun may be requested if Client is unsure how much data is to follow,
+	// so need to instigate Rx/Tx operation for any such request
+	if(iReqTrig & (ERxOverrun|ERxUnderrun|ERxAllBytes))
+		{
+		reqFlags |= EReceive;
+		}
+	if(iReqTrig & (ETxOverrun|ETxUnderrun|ETxAllBytes))
+		{
+		reqFlags |= ETransmit;
+		}
+	TInt r = DoRequest(reqFlags);
+	if(r != KErrNone)
+		{
+		// PSL encountered an error in intiating the requested trigger. Set the return value accordingly.
+		// Assume triggers have been cancelled - if they have not, the client-provided callback will still
+		// be invoked, but it will have been warned to expecte erroneous behaviour by the value assigned to retVal.
+		iReqTrig = 0;
+		retVal = KErrGeneral;
+		}
+	else	// PSL accepted the request, so update timer and state information
+		{
+		switch (iTimerState)
+			{
+			case DIicBusChannelSlave::EInactive:
+				{
+				// Do not start the timer. Must wait for the Master to access a Slave buffer before considering
+				// a transaction as started.
+				break;
+				}
+			case DIicBusChannelSlave::EWaitForClient:
+				{
+				// Client has responded within the given time period. The next state is
+				// dependent on the requested trigger - if set to zero, the Client is explicitly
+				// ending the transaction, so the next state is EInactive; otherwise, the
+				// Client has indicated the next action expected from the Master and so the
+				// timer is started and next state is EWaitForMaster
+				if(iReqTrig == 0)
+					{
+					iTimerState = DIicBusChannelSlave::EInactive;
+					}
+				else
+					{
+					iTimerState = DIicBusChannelSlave::EWaitForMaster;
+					StartTimerByState();
+					}
+				break;
+				}
+			case DIicBusChannelSlave::EClientTimeout:
+				{
+				// For the previous transfer, the Client failed to respond within the required time - and
+				// the PSL will have been instructed to indicate a bus error (so the Master
+				// will have been informed). The error code returned by this function will be KErrTimedOut
+				// so the Client will be informed of what has happened.
+				// A transaction is considered to start when the Slave is addressed by the Master
+				// (as indicated by the PSL invoking NotifyClient) - which has not yet happened -
+				// so the next state is EInactive.
+				iTimerState=DIicBusChannelSlave::EInactive;
+				break;
+				}
+			case DIicBusChannelSlave::EWaitForMaster:
+				{
+				// In this case we are handling a new requested trigger from the client to handle ERxOverrun or
+				// ETxUnderrun. The PSL has accepted the new trigger, so must allow the Master sufficient time
+				// to perform the newly-requested transfer; the timer has already been stopped, so just start it again..
+				StartTimerByState();
+				break;
+				}
+			default:
+				{
+				__ASSERT_DEBUG(0, Kern::Fault(KIicChannelPanic,__LINE__));
+				break;
+				}
+			}
+		}
+
+	return retVal;
+	}
+#else /*SLAVE_MODE*/
+
+TInt DIicBusChannelSlave::CaptureChannel(TDes8* /*aConfigHdr*/, TIicBusSlaveCallback* /*aCallback*/, TInt& /*aChannelId*/, TBool /*aAsynch*/)
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::CaptureChannel invoked when not in SLAVE_MODE!\n"));
+	 return KErrNotSupported;
+	}
+
+TInt DIicBusChannelSlave::ReleaseChannel()
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::ReleaseChannel invoked when not in SLAVE_MODE!\n"));
+	 return KErrNotSupported;
+	}
+
+TInt DIicBusChannelSlave::RegisterRxBuffer(TPtr8 /*aRxBuffer*/, TInt8 /*aBufGranularity*/, TInt8 /*aNumWords*/, TInt8 /*aOffset*/)
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::RegisterRxBuffer invoked when not in SLAVE_MODE!\n"));
+	 return KErrNotSupported;
+	}
+
+TInt DIicBusChannelSlave::RegisterTxBuffer(TPtr8 /*aTxBuffer*/, TInt8 /*aBufGranularity*/, TInt8 /*aNumWords*/, TInt8 /*aOffset*/)
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::RegisterTxBuffer invoked when not in SLAVE_MODE!\n"));
+	 return KErrNotSupported;
+	}
+
+TInt DIicBusChannelSlave::SetNotificationTrigger(TInt /*aTrigger*/)
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::SetNotificationTrigger invoked when not in SLAVE_MODE!\n"));
+	 return KErrNotSupported;
+	}
+#endif/*SLAVE_MODE*/
+
+DIicBusChannelSlave::DIicBusChannelSlave(TBusType aBusType, TChannelDuplex aChanDuplex, TInt16 aChannelId)
+	: DIicBusChannel(DIicBusChannel::ESlave, aBusType, aChanDuplex),
+	iChannelId(aChannelId), iTimerState(EInactive),
+	iMasterWaitTime(KSlaveDefMWaitTime), iClientWaitTime(KSlaveDefCWaitTime),
+	iSpinLock(TSpinLock::EOrderGenericIrqLow2)  // Semi-arbitrary, low priority value
+	{
+#ifndef STANDALONE_CHANNEL
+	iController = NULL;
+#endif
+	}
+
+DIicBusChannelSlave::~DIicBusChannelSlave()
+    {
+    delete iClientTimeoutDfc;
+    }
+
+void DIicBusChannelSlave::SlaveStaticCB(TAny* aPtr)
+	{
+	DIicBusChannelSlave* chan = (DIicBusChannelSlave*)aPtr;
+	chan->SlaveTimerCallBack();
+	return;
+	}
+
+TInt DIicBusChannelSlave::Init()
+	{
+	iClientTimeoutDfc = new TDfc(SlaveStaticCB,(TAny*)this, 7);	// Highest Dfc priority
+	if(!iClientTimeoutDfc)
+		return KErrNoMemory;
+	else
+		return KErrNone;
+	}
+
+void DIicBusChannelSlave::ChanCaptureCallback(TInt aResult)
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("ChanCaptureCallback: aChannel=0x%x, aResult=%d\n",this,aResult));
+
+	TInt r=aResult;
+	TInt channelId = 0;
+	if(aResult == KErrNone)
+		{
+		SetChannelId(channelId);
+#ifndef STANDALONE_CHANNEL
+		__ASSERT_DEBUG(iController, Kern::Fault(KIicChannelPanic,__LINE__));
+		iController->InstallCapturedChannel(channelId, this);
+#endif
+		iClientTimeoutDfc->SetDfcQ(iNotif->iDfcQ);
+		r=KErrCompletion;
+		}
+	else
+		ReleaseChannel();
+
+#ifdef IIC_INSTRUMENTATION_MACRO
+	IIC_SCAPTCHANASYNC_END_PIL_TRACE;
+#endif
+	CompleteAsynchCapture(r);	// Queue the client callback for execution
+	}
+
+void DIicBusChannelSlave::SlaveTimerCallBack()
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("SlaveTimerCallBack"));
+	if(iTimerState == DIicBusChannelSlave::EWaitForMaster)
+		{
+		// Master timeout. Consider the transaction terminated - call NotifyClient
+		// to inform both the Client and the PSL, and update the state machine
+		NotifyClient(EGeneralBusError);
+		}
+	else if(iTimerState == DIicBusChannelSlave::EWaitForClient)
+		{
+		// Client timeout. Instigate the PSL-specific bus error indication
+		iTimerState=DIicBusChannelSlave::EClientTimeout;
+		SendBusErrorAndReturn();
+		}
+	else
+		{
+		__ASSERT_DEBUG(0, Kern::Fault(KIicChannelPanic,__LINE__));
+		}
+	}
+
+
+void DIicBusChannelSlave::StartTimerByState()
+	{
+	if(iTimerState == DIicBusChannelSlave::EWaitForMaster)
+		{
+		iTimeoutTimer.OneShot(NKern::TimerTicks(iMasterWaitTime),(*iClientTimeoutDfc));
+		}
+	else if(iTimerState == DIicBusChannelSlave::EWaitForClient)
+		{
+		iTimeoutTimer.OneShot(NKern::TimerTicks(iClientWaitTime),(*iClientTimeoutDfc));
+		}
+	else
+		{
+		__ASSERT_DEBUG(NULL, Kern::Fault(KIicChannelPanic,__LINE__));
+		}
+	}
+
+void DIicBusChannelSlave::StopTimer()
+	{
+	iTimeoutTimer.Cancel();
+	}
+
+TInt DIicBusChannelSlave::UpdateReqTrig(TInt8& aCbTrigVal, TInt& aCallbackRet)
+	{
+    __KTRACE_OPT(KIIC, Kern::Printf("UpdateReqTrig"));
+
+	TInt nextSteps = 0;
+	iAccumTrig |= iNotif->iTrigger;	// Update the accumulated event history, regardless of if the trigger was requested
+
+	if(iNotif->iTrigger & EGeneralBusError)
+		{
+		// In the event of a bus error, always cancel the timer and call the Client callback
+		nextSteps |= (EStopTimer | EInvokeCb);
+		iTimerState = EInactive;
+		aCallbackRet = KErrGeneral;
+		}
+	else if(iNotif->iTrigger == EAsyncCaptChan)
+		{
+		// For asynchronous channel capture, no timers are involved - just call the Client callback
+		nextSteps |= EInvokeCb;
+		aCallbackRet = KErrCompletion;
+		}
+	else if((iNotif->iTrigger & iReqTrig) != 0)
+		{
+		// If a requested Rx event has occurred, clear all Rx flags from the requested triggers (similarly for Tx)
+		if(iNotif->iTrigger & (ERxAllBytes | ERxUnderrun | ERxOverrun))
+			{
+			iReqTrig &= ~(ERxAllBytes | ERxUnderrun | ERxOverrun);
+			}
+		if(iNotif->iTrigger & (ETxAllBytes | ETxUnderrun | ETxOverrun))
+			{
+			iReqTrig &= ~(ETxAllBytes | ETxUnderrun | ETxOverrun);
+			}
+
+		if(iTimerState == EInactive)
+			{
+			nextSteps |= (EStartTimer | EInvokeCb);
+			// The next state in the state machine depends on if all the requested events have occurred
+			if(iReqTrig == 0)
+				{
+				// All triggers required have occurred, so transition to state EWaitForClient
+				iTimerState = EWaitForClient;
+				}
+			else
+				{
+				// The Client can request both Rx an Tx triggers; if only one has occurred, must wait for
+				// the Master to generate the other
+				iTimerState = EWaitForMaster;
+				}
+			aCallbackRet = KErrNone;
+			}
+		else if(iTimerState == EWaitForMaster)
+			{
+			// The next state in the state machine depends on if all the requested events have occurred
+			if(iReqTrig == 0)
+				{
+				// All triggers required have occurred, so transition to state EWaitForClient
+				iTimerState = EWaitForClient;
+				nextSteps |= (EStopTimer | EInvokeCb | EStartTimer);
+				}
+			else
+				{
+				// The Client can request both Rx an Tx triggers; if only one has occurred, must wait for
+				// the Master to generate the other - so remain in this state, do not cancel the timer or
+				// re-start it with a new timeout period. Still invoke the callback to notify the client
+				// that at least one of the requested triggers has occurred.
+				nextSteps |= EInvokeCb;
+				}
+			aCallbackRet = KErrNone;
+			}
+		else if((iTimerState == EWaitForClient) || (iTimerState == EClientTimeout))
+			{
+			// No triggers are expected in these states (iReqTrig==0).
+			__ASSERT_DEBUG(NULL, Kern::Fault(KIicChannelPanic,__LINE__));
+			}
+		}
+	aCbTrigVal = iAccumTrig;
+	return nextSteps;
+	}
+
+
+void DIicBusChannelSlave::NotifyClient(TInt aTrigger)
+	{
+	TIicBusSlaveCallback* notif = iNotif;
+	notif->iTrigger = aTrigger;	// Ensure ProcessData is provided with the trigger
+
+	if(NKern::CurrentContext() == NKern::EThread && &(Kern::CurrentThread()) == iClient)
+		{
+		// PSL will update notif to represent the events that have occurred
+		ProcessData(aTrigger, notif);
+		// Only invoke the client's callback (and update the state machine) if one of the requested triggers has
+		// occurred or if a bus error has been witnessed
+		TInt8 callbackTrig=0;
+		TInt callbackRet=0;
+		TInt nextSteps = UpdateReqTrig(callbackTrig, callbackRet);
+		if(nextSteps & EStopTimer)
+			{
+			iTimeoutTimer.Cancel();
+			}
+		if(nextSteps & EInvokeCb)
+			{
+			(notif->iCallback)(notif->iChannelId, (TInt)callbackRet, callbackTrig, notif->iRxWords, notif->iTxWords, notif->iParam);
+			// Callback now processed, so re-initialise callback object members
+			notif->iTrigger=0;
+			notif->iReturn=KErrNone;
+			notif->iRxWords=0;
+			notif->iTxWords=0;
+			iAccumTrig = 0;	// and re-initialise the accumulated history as the transaction is considered terminated
+			}
+		if(nextSteps & EStartTimer)
+			{
+			StartTimerByState();
+			}
+		}
+	else if(NKern::CurrentContext() == NKern::EInterrupt)
+			notif->Add();
+	else
+		notif->Enque();
+	}
+
+TInt DIicBusChannelSlave::SetMasterWaitTime(TInt8 aWaitTime)
+	{
+	if((aWaitTime<0)||(aWaitTime>KMaxWaitTime))
+		return KErrArgument;
+	iMasterWaitTime=aWaitTime;
+	return KErrNone;
+	}
+
+TInt DIicBusChannelSlave::SetClientWaitTime(TInt8 aWaitTime)
+	{
+	if((aWaitTime<0)||(aWaitTime>KMaxWaitTime))
+		return KErrArgument;
+	iClientWaitTime=aWaitTime;
+	return KErrNone;
+	}
+
+void DIicBusChannelSlave::SendBusErrorAndReturn()
+	{
+	DoRequest(EAbort);
+	}
+
+void DIicBusChannelSlave::SetChannelId(TInt& aChannelId)
+    {
+    ++iInstanceCount;
+    aChannelId = (iInstanceCount<<16);
+    //
+    // The PSL-specific channel identifier was stored in this generic class' member iChannelId at registration time
+    aChannelId |= iChannelId;
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::SetChannelId: iInstanceCount=0x%x, iChannelId=0x%x returned aChannelId=0x%x\n",iInstanceCount,iChannelId,aChannelId));
+    iNotif->iChannelId=aChannelId;
+    }
+
+void DIicBusChannelSlave::CompleteAsynchCapture(TInt aResult)
+    {
+    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::CompleteAsynchCapture aResult = %d",aResult));
+    if(NKern::CurrentContext() == NKern::EThread && &Kern::CurrentThread() == iClient)
+        {
+        iNotif->iCallback(iNotif->iChannelId, aResult, EAsyncCaptChan, NULL, NULL, iNotif->iParam);
+        return;
+        }
+    else
+        {
+        iNotif->iReturn=aResult;
+        iNotif->iTrigger=EAsyncCaptChan;
+        iNotif->iTxWords=NULL;
+        iNotif->iRxWords=NULL;
+        }
+    if(NKern::CurrentContext() == NKern::EInterrupt)
+        iNotif->Add();
+    else
+        iNotif->Enque();
+    }
+
+TInt DIicBusChannelSlave::StaticExtension(TUint /*aFunction*/, TAny* /*aParam1*/, TAny* /*aParam*/)
+ 	{
+ 	return KErrNotSupported;
+ 	}
+
+TInt DIicBusChannelSlave::Spare1(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
+	{
+	return KErrNotSupported;
+	}
+
+TInt DIicBusChannelMasterSlave::QueueTransaction(TIicBusTransaction* aTransaction)
+	{
+	return QueueTransaction(aTransaction,NULL);
+	};
+
+TInt DIicBusChannelMasterSlave::QueueTransaction(TIicBusTransaction* aTransaction, TIicBusCallback* aCallback)
+	{
+	TInt r=KErrNone;
+	iMasterChannel->Lock();
+	if(iSlaveChannel->iChannelInUse)
+		r=KErrInUse;
+	else
+		{
+		TInt16 count=(TInt16)((iMasterChannel->iTransCount)&~KTransCountMsBit);
+		if(count<~KTransCountMsBit)
+			{
+			++count;
+			count|=KTransCountMsBit;
+			}
+		else
+			r=KErrInUse;
+		}
+	iMasterChannel->Unlock();
+	if(r == KErrNone)
+		r=(iMasterChannel->QueueTransaction(aTransaction, aCallback));
+	return r;
+	};
+
+TInt DIicBusChannelMasterSlave::CaptureChannel(TDes8* aConfigHdr, TIicBusSlaveCallback* aCallback, TInt& aChannelId, TBool aAsynch)
+	{
+	iMasterChannel->Lock();
+	TInt r=KErrNone;
+	if(iSlaveChannel->iChannelInUse)
+		r=KErrInUse;
+	else
+		{
+		if(iMasterChannel->IsMasterBusy())
+			r=KErrInUse;
+		else
+			iSlaveChannel->iChannelInUse = 1;
+		}
+	iMasterChannel->Unlock();
+	if(r == KErrNone)
+		r=iSlaveChannel->CaptureChannel(aConfigHdr, aCallback, aChannelId, aAsynch);
+	return r;
+	};
+
+
+TInt DIicBusChannelMasterSlave::ReleaseChannel()
+    {
+	iMasterChannel->Lock();
+	TInt r=iSlaveChannel->ReleaseChannel();
+	iMasterChannel->Unlock();
+	return r;
+	};
+
+TInt DIicBusChannelMasterSlave::StaticExtension(TUint /*aFunction*/, TAny* /*aParam1*/, TAny* /*aParam*/)
+ 	{
+ 	return KErrNotSupported;
+ 	}
+
+#ifdef STANDALONE_CHANNEL
+EXPORT_C DIicBusChannelMasterSlave::DIicBusChannelMasterSlave(TBusType aBusType, TChannelDuplex aChanDuplex, DIicBusChannelMaster* aMasterChan, DIicBusChannelSlave* aSlaveChan)
+    : DIicBusChannel(DIicBusChannel::EMasterSlave, aBusType, aChanDuplex),
+    iMasterChannel(aMasterChan),
+    iSlaveChannel(aSlaveChan)
+    {
+    //If in stand-alone channel mode, the client assigns a channel number to the MasterSlave channel it creates.
+    }
+#endif
+