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_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