diff -r 000000000000 -r bde4ae8d615e os/kernelhwsrv/kernel/eka/drivers/iic/iic_channel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/kernelhwsrv/kernel/eka/drivers/iic/iic_channel.cpp Fri Jun 15 03:10:57 2012 +0200 @@ -0,0 +1,1145 @@ +// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// e32\drivers\iic_channel.cpp +// IIC Channel Platform Independent Layer (PIL) +// + +#include +#ifdef IIC_INSTRUMENTATION_MACRO +#include +#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("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 +