kernel/eka/drivers/iic/iic.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:26:05 +0100
branchRCL_3
changeset 136 743008598095
parent 0 a41df078684a
child 199 189ece41fa29
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:
// e32\drivers\iic.cpp
// IIC Controller and public API Implementation
//

#include <drivers/iic.h>
#include <drivers/iic_channel.h>
#include "iic_priv.h"

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

// Global Controller pointer
static DIicBusController* TheController = NULL;

//
//		Implementation of generic IicBus API for client interface
//
EXPORT_C TInt IicBus::QueueTransaction(TInt aBusId, TIicBusTransaction* aTransaction)
	{
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_MQTRANSSYNC_START_PIL_TRACE;
#endif
	TInt r=TheController->QueueTransaction(aBusId, aTransaction);

#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_MQTRANSSYNC_END_PIL_TRACE;
#endif
	return r;
	}

EXPORT_C TInt IicBus::QueueTransaction(TInt aBusId, TIicBusTransaction* aTransaction, TIicBusCallback* aCallback)
	{
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_MQTRANSASYNC_START_PIL_TRACE;
#endif
	TInt r=TheController->QueueTransaction(aBusId, aTransaction, aCallback);

#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_MQTRANSASYNC_END_PIL_TRACE;
#endif
	return r;
	}

EXPORT_C TInt IicBus::CancelTransaction(TInt aBusId, TIicBusTransaction* aTransaction)
	{
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_MCANCELTRANS_START_PIL_TRACE;
#endif
	TInt r=TheController->CancelTransaction(aBusId, aTransaction);

#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_MCANCELTRANS_END_PIL_TRACE;
#endif
	return r;
	}

EXPORT_C TInt IicBus::CaptureChannel(TInt aBusId, TDes8* aConfigHdr, TIicBusSlaveCallback* aCallback, TInt& aChannelId, TBool aAsynch)
	{
#ifdef IIC_INSTRUMENTATION_MACRO
	if(!aAsynch)
		{
		IIC_SCAPTCHANSYNC_START_PIL_TRACE;
		}
	else
		{
		IIC_SCAPTCHANASYNC_START_PIL_TRACE;
		}
#endif
	TInt r=TheController->CaptureChannel(aBusId, aConfigHdr, aCallback, aChannelId, aAsynch);

#ifdef IIC_INSTRUMENTATION_MACRO
	if(!aAsynch)
		{
		IIC_SCAPTCHANSYNC_END_PIL_TRACE;
		}
#endif
	return r;
	}

EXPORT_C TInt IicBus::ReleaseChannel(TInt aChannelId)
	{
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_SRELCHAN_START_PIL_TRACE;
#endif
	TInt r=TheController->ReleaseChannel(aChannelId);

#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_SRELCHAN_END_PIL_TRACE;
#endif
	return r;
	}

EXPORT_C TInt IicBus::RegisterRxBuffer(TInt aChannelId, TPtr8 aRxBuffer, TInt8 aBufGranularity, TInt8 aNumWords, TInt8 aOffset)
	{
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_SREGRXBUF_START_PIL_TRACE;
#endif
	TInt r=TheController->RegisterRxBuffer(aChannelId, aRxBuffer, aBufGranularity, aNumWords, aOffset);

#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_SREGRXBUF_END_PIL_TRACE;
#endif
	return r;
	}

EXPORT_C TInt IicBus::RegisterTxBuffer(TInt aChannelId, TPtr8 aTxBuffer, TInt8 aBufGranularity, TInt8 aNumWords, TInt8 aOffset)
	{
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_SREGTXBUF_START_PIL_TRACE;
#endif
	TInt r=TheController->RegisterTxBuffer(aChannelId, aTxBuffer, aBufGranularity, aNumWords, aOffset);

#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_SREGTXBUF_END_PIL_TRACE;
#endif
	return r;
	}

EXPORT_C TInt IicBus::SetNotificationTrigger(TInt aChannelId, TInt aTrigger)
	{
#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_SNOTIFTRIG_START_PIL_TRACE;
#endif
	TInt r=TheController->SetNotificationTrigger(aChannelId, aTrigger);

#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_SNOTIFTRIG_END_PIL_TRACE;
#endif
	return r;
	}

EXPORT_C TInt IicBus::StaticExtension(TUint aId, TUint aFunction, TAny* aParam1, TAny* aParam2)
	{
	return(TheController->StaticExtension(aId, aFunction, aParam1, aParam2));
	}


//
//		Bus Controller
//

//  auxiliary function for ordering entries in the array of channels
TInt DIicBusController::OrderEntries(const DIicBusChannel& aMatch, const DIicBusChannel& aEntry)
	{
	TUint8 l=(TUint8)aMatch.ChannelNumber();
	TUint8 r=(TUint8)aEntry.ChannelNumber();
	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<DIicBusChannel> EntryOrder(DIicBusController::OrderEntries);

// Implementation for DIicBusController
//

TInt DIicBusController::Create()
	{
	TInt r=KErrNone;
	iChanLock = new TSpinLock(TSpinLock::EOrderGenericIrqLow2);  // Semi-arbitrary, low priority value
	iCaptLock = new TSpinLock(TSpinLock::EOrderGenericIrqLow2);  // Semi-arbitrary, low priority value
	if((iChanLock == NULL)||(iCaptLock == NULL))
		{
		delete iChanLock;
		delete iCaptLock;
		r=KErrNoMemory;
		}
	return r;
	}

DIicBusController::~DIicBusController()
	{
#ifdef IIC_SIMULATED_PSL
	for(TInt i=0; i<iChannelArray.Count(); i++)
		{
		DIicBusChannel* ptr=iChannelArray[i];
		// Remove the channel from the array
		iChannelArray.Remove(i);
		// Delete the channel object
		delete ptr;
		};

	iChannelArray.Reset();
	delete iChanLock;
	delete iCaptLock;
#endif
	}

TInt DIicBusController::GetChanWriteAccess()
	{
	// Can only have one insertion or removal active at any one time
	// Can not perform an insertion or removal while a read is in progress
	// If either of the two above conditions exist, return KErrInUse
	// Otherwise, set the flag to indicate that a write is in progress
	// and return KErrNone.
	TInt chanIntState=0;
	chanIntState=__SPIN_LOCK_IRQSAVE(*iChanLock);
	if(iChanRwFlags != 0)
		{
		__SPIN_UNLOCK_IRQRESTORE(*iChanLock,chanIntState);
		return KErrInUse;
		}
	iChanRwFlags |= EWriteInProgress;
	__SPIN_UNLOCK_IRQRESTORE(*iChanLock,chanIntState);
	return KErrNone;
	}

void DIicBusController::FreeChanWriteAccess()
	{
	// If an insertion or removal is in progress, no other modifying operation
	// can be active. Reads are also not permitted - so iChanRwFlags can only be
	// EWriteInProgress.
	__ASSERT_DEBUG(iChanRwFlags == EWriteInProgress, Kern::Fault(KIicPanic,__LINE__));
	TInt chanIntState=0;
 	chanIntState=__SPIN_LOCK_IRQSAVE(*iChanLock);
 	iChanRwFlags &= ~EWriteInProgress;
 	__SPIN_UNLOCK_IRQRESTORE(*iChanLock,chanIntState);
	}

TInt DIicBusController::GetChanReadAccess()
	{
	// No reads are permitted while an insertion or removal is in progress
	// If one of the above operations is in progress return KErrInUse
	// Can have several concurrent reads at any one time - so increment
	// the count of such operations as well as ensuring the flag is set to indicate
	// a read is in progress
	TInt chanIntState=0;
	chanIntState=__SPIN_LOCK_IRQSAVE(*iChanLock);
	if(iChanRwFlags == EWriteInProgress)
		{
		__SPIN_UNLOCK_IRQRESTORE(*iChanLock,chanIntState);
		return KErrInUse;
		}
	__ASSERT_DEBUG(iChanRdCount!=0xFFFFFFFF, Kern::Fault(KIicPanic,__LINE__)); // Overflow
	iChanRdCount++;
	iChanRwFlags |= EReadInProgress;
	__SPIN_UNLOCK_IRQRESTORE(*iChanLock,chanIntState);
	return KErrNone;
	}

void DIicBusController::FreeChanReadAccess()
	{
	// No insertions or removals are permitted while a read is in progress
	// so iChanRwFlags can only be EReadInProgress
	// Multiple reads can be in progress concurrently, so the count must be decremented
	TInt chanIntState=0;
	chanIntState=__SPIN_LOCK_IRQSAVE(*iChanLock);
	__ASSERT_DEBUG(iChanRwFlags == EReadInProgress, Kern::Fault(KIicPanic,__LINE__));
	__ASSERT_DEBUG(iChanRdCount>0, Kern::Fault(KIicPanic,__LINE__));
	iChanRdCount--;
	if(iChanRdCount == 0)
		iChanRwFlags &= ~EReadInProgress;
	__SPIN_UNLOCK_IRQRESTORE(*iChanLock,chanIntState);
	}

TInt DIicBusController::RequestTypeSupported(const TInt aBusId, DIicBusChannelMaster* const aChannel)
	{
	TInt32 reqBusType;
	reqBusType = GET_BUS_TYPE(aBusId);
	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::RequestTypeSupported, BusType=0x%x\n", reqBusType));

	if(reqBusType != aChannel->BusType())
		{
		return KErrNotSupported;
		}

	return KErrNone;
	}


EXPORT_C TInt DIicBusController::RegisterChannels(DIicBusChannel** aListChannels, TInt aNumberChannels)
	{
// To be used by Channel implementations to register a list of supported channels
    __KTRACE_OPT(KIIC, Kern::Printf("\nDIicBusController::RegisterChannels, aListChannels=0x%x, aNumberChannels=%d\n",aListChannels,aNumberChannels));
	__ASSERT_DEBUG(aListChannels!=NULL, Kern::Fault(KIicPanic,__LINE__));

	RPointerArray<DIicBusChannel>* chanArray = TheController->ChannelArray();

#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_REGISTERCHANS_START_PIL_TRACE;
#endif
	// Get access to the channel pointer array - exit if it is currently being modfied
	TInt r=KErrNone;
	if((r=TheController->GetChanWriteAccess()) == KErrNone)
		{
#ifdef _DEBUG
		__KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::RegisterChannels - On entry, iChannelArray ...\n"));
		TheController->DumpChannelArray();
#endif
		// Loop for aNumberChannels	and write directly to the channel array
		DIicBusChannel** chanIterator = aListChannels;
		for(TInt iteration = 0; iteration < aNumberChannels; ++iteration, ++chanIterator)
			{
			DIicBusChannel* chanPtr = *chanIterator;
			__KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::RegisterChannels - adding channel number %d\n",chanPtr->ChannelNumber()));
			TInt r = chanArray->InsertInOrder(chanPtr,EntryOrder);
			if(r!=KErrNone)
				break;
			}

#ifdef _DEBUG
		 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::RegisterChannels - On exit, iChannelArray ...\n"));
		TheController->DumpChannelArray();
#endif
		TheController->FreeChanWriteAccess();
		}
	else
		{
		__KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::GetChanWriteAccess returned %d\n",r));
		}

#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_REGISTERCHANS_END_PIL_TRACE;
#endif
	return r;
	}


EXPORT_C TInt DIicBusController::DeRegisterChannel(DIicBusChannel* aChannel)
	{
// To be used by Channel implementations to deregister a channel
    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::DeRegisterChannel, aChannel=0x%x\n",aChannel));
	if(aChannel == NULL)
		return KErrArgument;

	RPointerArray<DIicBusChannel>* chanArray = TheController->ChannelArray();

#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_DEREGISTERCHAN_START_PIL_TRACE;
#endif
	TInt r=KErrNone;
	// Get access to the channel pointer array - exit if it is currently unavailable
	// Gaining write access will prevent a client of a Master Channel from instigating a new QueueTransaction
	// (or CancelTransaction), and it will obstruct a client of a Slave Channel in CaptureChannel.
	if((r=TheController->GetChanWriteAccess())!=KErrNone)
		return r;

	// Check channel is registered
	TInt chanIndex = chanArray->FindInOrder(aChannel,EntryOrder);
	if(chanIndex<0)
		{
		TheController->FreeChanWriteAccess();
		return KErrNotFound;
		}

#ifdef _DEBUG
    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::DeRegisterChannel - On entry, iChannelArray ...\n"));
	TheController->DumpChannelArray();
#endif

	// Remove the channel from the array
	// Note that this does not delete the channel object
	chanArray->Remove(chanIndex);

#ifdef _DEBUG
    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::DeRegisterChannel - On exit, iChannelArray ...\n"));
	TheController->DumpChannelArray();
#endif
	TheController->FreeChanWriteAccess();

#ifdef IIC_INSTRUMENTATION_MACRO
	IIC_DEREGISTERCHAN_END_PIL_TRACE;
#endif
	return KErrNone;
	}

TInt DIicBusController::FindCapturedChanById(TCapturedChannel aCapturedChan, TInt& aIndex)
	{
	TInt index=0;
	TInt r=KErrNotFound;
	do
		{
		if(iCapturedChannels[index].iChannelId == aCapturedChan.iChannelId)
			{
			aIndex=index;
			r=KErrNone;
			}
		index++;
		} while ((index < KMaxNumCapturedChannels)&&(r == KErrNotFound));
	return r;
	}

TInt DIicBusController::FindCapturedChan(TCapturedChannel aCapturedChan, TInt& aIndex)
	{
	TInt index=0;
	TInt r=KErrNotFound;
	do
		{
		if(iCapturedChannels[index] == aCapturedChan)
			{
			aIndex=index;
			r=KErrNone;
			}
		index++;
		} while ((index < KMaxNumCapturedChannels)&&(r == KErrNotFound));
	return r;
	}

TInt DIicBusController::InsertCaptChanInArray(TCapturedChannel aCapturedChan)
	{
	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::InsertCaptChanInArray \n"));
	// Ensure the channel hasn't already been inserted in the array
	// If found, fault the Kernel
	TInt dumInt = 0;
	TInt r=FindCapturedChan(aCapturedChan,dumInt);
	__ASSERT_DEBUG(r!=KErrNone, Kern::Fault(KIicPanic,__LINE__));

	// Loop the array and insert in the first available slot
	// If no slots are available return KErrNotReady
	TInt index=0;
	TCapturedChannel emptyChan;
	for(;index<KMaxNumCapturedChannels;++index)
		{
		if(iCapturedChannels[index] == emptyChan)
			{
			// Found a space
			iCapturedChannels[index]=aCapturedChan;
			break;
			}
		}
	if(index>=KMaxNumCapturedChannels)
		r = KErrNotReady;
	return r;
	}

TInt DIicBusController::RemoveCaptChanFromArray(TCapturedChannel aCapturedChan)
	{
	// Remove the entry from the array
	// If the entry is not present return KErrArgument
	TInt index=-1;
	TInt r=FindCapturedChan(aCapturedChan,index);
	if((r!=KErrNone)||(index>=KMaxNumCapturedChannels))
		return KErrArgument;
	iCapturedChannels[index].iChanPtr=NULL;
	iCapturedChannels[index].iChannelId=0;
	return KErrNone;
	}


TInt DIicBusController::InstallCapturedChannel(const TInt aChannelId, const DIicBusChannelSlave* aChanPtr)
	{
#ifdef _DEBUG
    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::InstallCapturedChannel - On entry, iCapturedChannels ...\n"));
	DumpCapturedChannels();
#endif
	TInt r=KErrNone;
	TCapturedChannel capturedChan((TInt)aChannelId,(DIicBusChannelSlave*)aChanPtr);
	// Because insertions are bounded by the size of the array and do not involve allocating
	// or freeing memory, simply take the spinlock at the start of the operation and release at the end
	TInt captIntState=__SPIN_LOCK_IRQSAVE(*iCaptLock);
	r=InsertCaptChanInArray(capturedChan);
	__SPIN_UNLOCK_IRQRESTORE(*iCaptLock,captIntState);
	if(r!=KErrNone)
		return r;

#ifdef _DEBUG
    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::InstallCapturedChannel - On exit, iCapturedChannels ...\n"));
	DumpCapturedChannels();
#endif
	return KErrNone;
	}

TInt DIicBusController::DeInstallCapturedChannel(const TInt aChannelId, const DIicBusChannelSlave* aChanPtr)
	{
#ifdef _DEBUG
    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::DeInstallCapturedChannel - On entry, iCapturedChannels ...\n"));
	DumpCapturedChannels();
#endif
	TInt r = KErrNone;
	TCapturedChannel capturedChan((TInt) aChannelId, (DIicBusChannelSlave*) aChanPtr);
	// Because removals are bounded by the size of the array and do not involve allocating
	// or freeing memory, simply take the spinlock at the start of the operation and release at the end
	TInt captIntState = __SPIN_LOCK_IRQSAVE(*iCaptLock);
	r = RemoveCaptChanFromArray(capturedChan);
	__SPIN_UNLOCK_IRQRESTORE(*iCaptLock, captIntState);
	if(r != KErrNone)
		return r;

#ifdef _DEBUG
    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::DeInstallCapturedChannel - On exit, iCapturedChannels ...\n"));
	DumpCapturedChannels();
#endif
	return KErrNone;
	}

	// Master-side API
TInt DIicBusController::QueueTransaction(TInt aBusId, TIicBusTransaction* aTransaction)
	{
	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::QueueTransaction, aBusId=0x%x,aTransaction=0x%x\n", aBusId, aTransaction));
	if(!aTransaction)
		{
		return KErrArgument;
		}

	// Get a pointer to the channel
	TInt dumInt = 0;
	DIicBusChannel* chanPtr = NULL;
	// Can only read the channel array if it is not currently being modified
	TInt r = GetChanReadAccess();
	if(r != KErrNone)
		{
		return r;
		}
	r = GetChanPtr(aBusId, dumInt, chanPtr);
	if(r == KErrNone)
		{
		if(!chanPtr)
			{
			r = KErrArgument;
			}
		else
			{
			switch(chanPtr->ChannelType())
				{
				// QueueTransaction requests are only supported by channels in Master mode.
				case DIicBusChannel::ESlave:
					{
					r = KErrNotSupported;
					break;
					}
				// If the request is supported by the Master channel, send it to the channel for processing in its thread
				case DIicBusChannel::EMasterSlave:
					{
					r = RequestTypeSupported(aBusId, ((DIicBusChannelMasterSlave*)chanPtr)->iMasterChannel);
					if(r == KErrNone)
						{
						aTransaction->iBusId = aBusId;
						r = (((DIicBusChannelMasterSlave*) chanPtr)->QueueTransaction(aTransaction));
						}
					break;
					}
				case DIicBusChannel::EMaster:
					{
					r = RequestTypeSupported(aBusId, (DIicBusChannelMaster*)chanPtr);
					if(r == KErrNone)
						{
						aTransaction->iBusId = aBusId;
						r = (((DIicBusChannelMaster*) chanPtr)->QueueTransaction(aTransaction));
						}
					break;
					}
				default:
					{
					r = KErrGeneral;
					}
				}
			}
		}
	FreeChanReadAccess();
	return r;
	}

TInt DIicBusController::QueueTransaction(TInt aBusId, TIicBusTransaction* aTransaction, TIicBusCallback* aCallback)
	{
    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::QueueTransaction, aBusId=0x%x,aTransaction=0x%x,aCallback=0x%x\n",aBusId,aTransaction,aCallback));
	if(!aTransaction || !aCallback)
		{
		return KErrArgument;
		}

	// Get a pointer to the channel
	TInt dumInt = 0;
	DIicBusChannel* chanPtr = NULL;
	// Can only read the channel array if it is not currently being modified
	TInt r = GetChanReadAccess();
	if(r == KErrNone)
		{
		r = GetChanPtr(aBusId, dumInt, chanPtr);
		if(r == KErrNone)
			{
			if(!chanPtr)
				{
				r = KErrArgument;
				}
			else
				{
				switch(chanPtr->ChannelType())
					{
					// QueueTransaction requests are only supported by channels in Master mode.
					case DIicBusChannel::ESlave:
						{
						r = KErrNotSupported;
						break;
						}
					// If the request is supported by the Master channel, send it to the channel for processing in its thread
					case DIicBusChannel::EMasterSlave:
						{
						r = RequestTypeSupported(aBusId, ((DIicBusChannelMasterSlave*)chanPtr)->iMasterChannel);
						if(r == KErrNone)
							{
							aTransaction->iBusId = aBusId;
							r = (((DIicBusChannelMasterSlave*) chanPtr)->QueueTransaction(aTransaction, aCallback));
							}
						break;
						}
					case DIicBusChannel::EMaster:
						{
						r = RequestTypeSupported(aBusId, (DIicBusChannelMaster*)chanPtr);
						if(r == KErrNone)
							{
							aTransaction->iBusId = aBusId;
							r = (((DIicBusChannelMaster*) chanPtr)->QueueTransaction(aTransaction, aCallback));
							}
						break;
						}
					default:
						{
						r = KErrGeneral;
						}
					}
				}
			}
		}
	FreeChanReadAccess();
	return r;
	}


TInt DIicBusController::GetChanPtr(const TInt aBusId, TInt &aIndex, DIicBusChannel*& aChan)
	{
    __KTRACE_OPT(KIIC, 	Kern::Printf("DIicBusController::GetChanPtr, aBusId=0x%x\n",aBusId));

	TInt32 chanId;
	chanId = GET_CHAN_NUM(aBusId);

	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::GetChanPtr, chanId=0x%x\n", chanId));
	DIicBusChannelSearcher searchChannel(DIicBusChannel::EMasterSlave, DIicBusChannel::ESccb, DIicBusChannel::EFullDuplex);
	searchChannel.SetChannelNumber((TInt8)chanId);

	TInt r = KErrNotFound;
	aIndex = iChannelArray.FindInOrder(&searchChannel, EntryOrder);
	if(aIndex >= 0)
		{
		aChan = iChannelArray[aIndex];
		r = KErrNone;
		}

	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::GetChanPtr, chanPtr=0x%x, index=%d\n", aChan, aIndex));
	return r;
	}


TInt DIicBusController::CancelTransaction(TInt aBusId, TIicBusTransaction* aTransaction)
	{
    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::CancelTransaction, aBusId=0x%x,aTransaction=0x%x\n", aBusId, aTransaction));
	if(!aTransaction)
		{
		return KErrArgument;
		}

	// Get the channel
	TInt dumInt = 0;
	DIicBusChannel* chanPtr = NULL;

	// Can only read the channel array if it is not currently being modified
	TInt r = GetChanReadAccess();
	if(r == KErrNone)
		{
		r = GetChanPtr(aBusId, dumInt, chanPtr);
		if(r == KErrNone)
			{
			if(!chanPtr)
				{
				r = KErrArgument;
				}
			else
				{
				// QueueTransaction requests are only supported by channels in Master mode.
				switch(chanPtr->ChannelType())
					{
					case DIicBusChannel::ESlave:
						{
						r = KErrNotSupported;
						break;
						}
					case DIicBusChannel::EMasterSlave:
						{
						r = RequestTypeSupported(aBusId, ((DIicBusChannelMasterSlave*)chanPtr)->iMasterChannel);
						if(r == KErrNone)
							{
							r = (((DIicBusChannelMasterSlave*) chanPtr)->CancelTransaction(aTransaction));
							}
						break;
						}
					case DIicBusChannel::EMaster:
						{
						r = RequestTypeSupported(aBusId, (DIicBusChannelMaster*)chanPtr);
						if(r == KErrNone)
							{
							r = (((DIicBusChannelMaster*) chanPtr)->CancelTransaction(aTransaction));
							}
						break;
						}
					default:
						{
						r = KErrGeneral;
						}
					}
				}
			}
		}
	FreeChanReadAccess();
	return r;
	}

	// Slave-side API
TInt DIicBusController::CaptureChannel(TInt aBusId, TDes8* aConfigHdr, TIicBusSlaveCallback* aCallback, TInt& aChannelId, TBool aAsynch)
	{
	// Check that that aCallback!=NULL and aConfigHdr!=NULL - if not, return KErrArgument
	if(!aCallback || !aConfigHdr)
		{
		return KErrArgument;
		}

	// Get the channel
	TInt chanIndex = 0;
	DIicBusChannel* chanPtr = NULL;

	// Can only read the channel array if it is not currently being modified
	TInt r = GetChanReadAccess();
	if(r == KErrNone)
		{
		r = GetChanPtr(aBusId, chanIndex, chanPtr);
		if(r == KErrNone)
			{
			if(!chanPtr)
				{
				r = KErrArgument;
				}
			else
				{
				DIicBusChannelSlave* slaveChanPtr = NULL;
				switch(chanPtr->ChannelType())
					{
					// CaptureChannel requests are only supported by channels in Slave mode.
					case DIicBusChannel::EMaster:
						{
						r = KErrNotSupported;
						break;
						}
					case DIicBusChannel::EMasterSlave:
						{
						slaveChanPtr = ((DIicBusChannelMasterSlave*) chanPtr)->iSlaveChannel;
						__ASSERT_DEBUG(slaveChanPtr!=NULL, Kern::Fault(KIicPanic,__LINE__)); // MasterSlave channel should have a valid Slave channel
						// Send the request to the channel
						slaveChanPtr->iController = this;
						r = ((DIicBusChannelMasterSlave*) chanPtr)->CaptureChannel(aConfigHdr, aCallback, aChannelId, aAsynch);
						break;
						}
					case DIicBusChannel::ESlave:
						{
						slaveChanPtr = (DIicBusChannelSlave*) chanPtr; // chanPtr is non-NULL
						// Send the request to the channel
						slaveChanPtr->iController = this;
						r = (slaveChanPtr->CaptureChannel(aConfigHdr, aCallback, aChannelId, aAsynch));
						break;
						}
					default:
						{
						r = KErrArgument;
						}
					}
				// For synchronous capture, if successful then install the channel
				if(r == KErrNone && slaveChanPtr)
					{
					if(!aAsynch)
						{
						InstallCapturedChannel(aChannelId, slaveChanPtr);
						}
					}
				}
			}
		}
	FreeChanReadAccess();
	return r;
	}


TInt DIicBusController::GetSlaveChanPtr(TInt aChannelId, DIicBusChannelSlave*& aSlaveChanPtr)
	{
	TInt r=KErrNone;
	// Check that the channelID is recognised
	TCapturedChannel capturedChan(aChannelId,NULL);
	TInt chanIndex=-1;
	// Ensure the array of captured channels will not be modified before it has been searched
	// Because searches are bounded by the size of the array and do not involve allocating
	// or freeing memory, simply take the spinlock at the start of the operation and release at the end
	TInt captIntState=__SPIN_LOCK_IRQSAVE(*iCaptLock);
	r=FindCapturedChanById(capturedChan, chanIndex);
	if((chanIndex < 0)||(r == KErrNotFound))
		r=KErrArgument;
	else
		aSlaveChanPtr = (DIicBusChannelSlave*)(iCapturedChannels[chanIndex].iChanPtr);
	__SPIN_UNLOCK_IRQRESTORE(*iCaptLock,captIntState);

	__ASSERT_DEBUG(aSlaveChanPtr!=NULL, Kern::Fault(KIicPanic,__LINE__));
	return r;
	}


TInt DIicBusController::ReleaseChannel(TInt aChannelId)
	{
    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::ReleaseChannel, channelID = 0x%x \n",aChannelId));
	TInt r = KErrNone;
	DIicBusChannel* chanPtr = NULL;
	
	// Get the pointer to the Slave Channel
	DIicBusChannelSlave* slaveChanPtr = NULL;
	if((r = GetSlaveChanPtr(aChannelId, slaveChanPtr)) != KErrNone)
		return r;
		
	DIicBusChannelSearcher searchChannel(DIicBusChannel::EMasterSlave, DIicBusChannel::ESccb, DIicBusChannel::EFullDuplex);
	searchChannel.SetChannelNumber(slaveChanPtr->ChannelNumber());

	TInt dumIndex = iChannelArray.FindInOrder(&searchChannel, EntryOrder);
	if(dumIndex < 0)
		{
		return KErrNotFound;
		}
	chanPtr = iChannelArray[dumIndex];

	__ASSERT_DEBUG(chanPtr!=NULL, Kern::Fault(KIicPanic,__LINE__));

	//if it is the masterslave channel, then call the masterslave's RelaseChannel
	// which will call the slave channel's ReleaseChannel internally
	if(chanPtr->ChannelType() == DIicBusChannel::EMasterSlave)
		r = ((DIicBusChannelMasterSlave*)chanPtr)->ReleaseChannel();
	else // Call the slave only ReleaseChannel
		r = slaveChanPtr->ReleaseChannel();
	
	// In either case de-install the captured slave channel
	if(r == KErrNone)
		{
		r = DeInstallCapturedChannel(aChannelId, slaveChanPtr);
		}

	// No need to unset iController - there is only one IIC Controller
	return r;
	}


TInt DIicBusController::RegisterRxBuffer(TInt aChannelId, TPtr8 aRxBuffer, TInt8 aBufGranularity, TInt8 aNumWords, TInt8 aOffset)
	{
    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::RegisterRxBuffer, channelID=0x%x,aRxBuffer=0x%x,aBufGranularity=0x%x,aNumWords=0x%x,aOffset=0x%x \n",aChannelId,(TInt)&aRxBuffer,aBufGranularity,aNumWords,aOffset));

    // Acquire the pointer to the Slave Channel
	DIicBusChannelSlave* slaveChanPtr = NULL;
	TInt r = GetSlaveChanPtr(aChannelId, slaveChanPtr);
	if(r != KErrNone)
		{
		return r;
		}

	// Instigate the channel functionality
	return(slaveChanPtr->RegisterRxBuffer(aRxBuffer,aBufGranularity,aNumWords,aOffset));
	}

TInt DIicBusController::RegisterTxBuffer(TInt aChannelId, TPtr8 aTxBuffer, TInt8 aBufGranularity, TInt8 aNumWords, TInt8 aOffset)
	{
    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::RegisterTxBuffer, channelID=0x%x,aTxBuffer=0x%x,aBufGranularity=0x%x,aNumWords=0x%x,aOffset=0x%x \n",aChannelId,(TInt)&aTxBuffer,aBufGranularity,aNumWords,aOffset));

	// Acquire the pointer to the Slave Channel
	DIicBusChannelSlave* slaveChanPtr = NULL;
	TInt r = GetSlaveChanPtr(aChannelId, slaveChanPtr);
	if(r != KErrNone)
		{
		return r;
		}

	// Instigate the channel functionality
	return (slaveChanPtr->RegisterTxBuffer(aTxBuffer, aBufGranularity, aNumWords, aOffset));
	}


TInt DIicBusController::SetNotificationTrigger(TInt aChannelId, TInt aTrigger)
	{
    __KTRACE_OPT(KIIC, Kern::Printf("DIicBusController::SetNotificationTrigger - for aChannelId=0x%x, aTrigger=0x%x\n",aChannelId,aTrigger));
	// Acquire the pointer to the Slave Channel
	DIicBusChannelSlave* slaveChanPtr = NULL;
	TInt r = GetSlaveChanPtr(aChannelId, slaveChanPtr);
	if( r != KErrNone)
		{
		return r;
		}

	// Instigate the channel functionality
	return(slaveChanPtr->SetNotificationTrigger(aTrigger));
	}


TInt DIicBusController::StaticExtension(TUint aId, TUint aFunction, TAny* aParam1, TAny* aParam2)
	{
//		The IIC controller and channel classes are generic, and can serve many differing client and
//		bus implementations. If a client and bus make use of specific functionality that is not
//		common to other bus types, it makes sense to provide only the minimum-required support in the
//		generic code. Here, the channel identifier is checked but all other parameters are passed
//		directly to the bus implementation channel for processing; if the channel does not provide
//		StaticExtension implementation, the generic DIicBusChannel::StaticExtension method is invoked.

#ifdef IIC_INSTRUMENTATION_MACRO
	if((aFunction & KControlIoMask) == KMasterSlaveControlIo)
		{
		IIC_MSSTATEXT_START_PIL_TRACE
		}
	else if((aFunction & KControlIoMask) == KMasterControlIo)
		{
		IIC_MSTATEXT_START_PIL_TRACE
		}
	else if((aFunction & KControlIoMask) == KSlaveControlIo)
		{
		IIC_SSTATEXT_START_PIL_TRACE
		}
//	else - Unexpected value - just pass silently to the PSL ...
#endif

	// Get the channel
	TInt dumInt = 0;
	DIicBusChannel* chanPtr = NULL;
	// Can only read the channel array if it is not currently being modified
	TInt r = GetChanReadAccess();
	if(r == KErrNone)
		{
		r = GetChanPtr(aId, dumInt, chanPtr);
		if(r == KErrNone)
			{
			if(!chanPtr)
				{
				r = KErrArgument;
				}
			else
				{
				r = chanPtr->StaticExtension(aFunction, aParam1, aParam2);
				}
			}
		}

#ifdef IIC_INSTRUMENTATION_MACRO
	if((aFunction & KControlIoMask) == KMasterSlaveControlIo)
		{
		IIC_MSSTATEXT_START_PIL_TRACE
		}
	else if((aFunction & KControlIoMask) == KMasterControlIo)
		{
		IIC_MSTATEXT_START_PIL_TRACE
		}
	else if((aFunction & KControlIoMask) == KSlaveControlIo)
		{
		IIC_SSTATEXT_START_PIL_TRACE
		}
//	else	... do nothing
#endif
	FreeChanReadAccess();
	return r;
	}


#ifdef _DEBUG

void DIicBusController::DumpCapturedChannels()
	{
	// Print iCapturedChannels ...
	TInt count=0;
	TInt i=0;
	TCapturedChannel emptyChan;
	for(;i<KMaxNumCapturedChannels;++i)
		{
		if(iCapturedChannels[i] == emptyChan)
			continue;
		++count;
		}

	i = 0;
    __KTRACE_OPT(KIIC, Kern::Printf("	- Count gave %d\n",count));
	for(;i<KMaxNumCapturedChannels;++i)
		{
		if(iCapturedChannels[i] == emptyChan)
			continue;
		DIicBusChannel* ptr=(DIicBusChannel*)(iCapturedChannels[i]).iChanPtr;
	    __KTRACE_OPT(KIIC, Kern::Printf("	- ptr %d=0x%x\n",i,ptr));
		ptr->StaticExtension(KCtrlIoDumpChan,0,0);
		};
	}

void DIicBusController::DumpChannelArray()
	{
	TInt i = 0;
	__KTRACE_OPT(KIIC, Kern::Printf("\nDIicBusController::DumpChannelArray\n"));
    __KTRACE_OPT(KIIC, Kern::Printf("	- Count gave %d\n",iChannelArray.Count()));
	for(i=0; i<iChannelArray.Count(); i++)
		{
		DIicBusChannel* ptr=iChannelArray[i];
	    __KTRACE_OPT(KIIC, Kern::Printf("	- ptr %d=0x%x\n",i,ptr));
		ptr->StaticExtension(KCtrlIoDumpChan,0,0);
		};
	}

#endif

#ifdef IIC_SIMULATED_PSL
TVersion DIicPdd::VersionRequired()
	{
	const TInt KIicMajorVersionNumber=1;
	const TInt KIicMinorVersionNumber=0;
	const TInt KIicBuildVersionNumber=KE32BuildVersionNumber;
	return TVersion(KIicMajorVersionNumber,KIicMinorVersionNumber,KIicBuildVersionNumber);
	}

/** Factory class constructor */
DIicPdd::DIicPdd()
	{
    iVersion = DIicPdd::VersionRequired();
	}

DIicPdd::~DIicPdd()
	{
	delete TheController;
	}

TInt DIicPdd::Install()
    {
    return(SetName(&KPddName));
    }

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

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

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

#ifndef IIC_SIMULATED_PSL
// Client interface entry point
DECLARE_EXTENSION_WITH_PRIORITY(KExtensionMaximumPriority-1)	// highest priority after Resource Manager
	{
	TheController = new DIicBusController;
	if(!TheController)
		return KErrNoMemory;
	TInt r=TheController->Create();
	return r;
	}
#else
static DIicPdd* TheIicPdd;

DECLARE_STANDARD_PDD()
	{
	TheController = new DIicBusController;
	if(!TheController)
		return NULL;
	TInt r = TheController->Create();
	if(r == KErrNone)
		{
		TheIicPdd = new DIicPdd;
		if(TheIicPdd)
			return TheIicPdd;
		}
	
	delete TheController; 
	return NULL;
	}
#endif