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