--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/drivers/iic/iic.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1105 @@
+// 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
+
+
+