First public contribution.
1 // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of the License "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // e32\drivers\iic_channel.cpp
15 // IIC Channel Platform Independent Layer (PIL)
18 #include <drivers/iic_channel.h>
19 #ifdef IIC_INSTRUMENTATION_MACRO
20 #include <drivers/iic_trace.h>
23 // The timer call back function which calls the PSL's HandleSlaveTimeout()
24 // Note that this assumes that the channel thread has been unblocked - if this
25 // is not the case, the callback will never get to run
27 TInt DIicBusChannelMaster::DoCreate()
30 void DIicBusChannelMaster::Lock()
31 {NKern::FMWait(&iTransactionQLock);}
33 void DIicBusChannelMaster::Unlock()
34 {NKern::FMSignal(&iTransactionQLock);}
36 TIicBusTransaction* DIicBusChannelMaster::NextTrans(TIicBusTransaction* aTrans)
38 // call multi-transaction call back function to get next transaction
39 if((aTrans->iFlags&KTransactionWithPreamble)&&(aTrans->iFlags&KTransactionWithMultiTransc))
40 return ((TIicBusTransactionPreambleExt*)aTrans)->iMultiTransc(aTrans, ((TIicBusTransactionPreambleExt*)aTrans)->iMultiTranscArg);
41 else if(aTrans->iFlags&KTransactionWithMultiTransc)
42 return ((TIicBusTransactionMultiTransc*)aTrans)->iMultiTransc(aTrans, ((TIicBusTransactionMultiTransc*)aTrans)->iMultiTranscArg);
47 void DIicBusChannelMaster::UnlockAndKick()
48 {iTransQDfc.Enque(&iTransactionQLock);}
50 void DIicBusChannelMaster::SlaveTimeoutCallback(TAny* aPtr)
53 DIicBusChannelMaster* aChanMaster=(DIicBusChannelMaster* )aPtr;
54 TInt r = aChanMaster->HandleSlaveTimeout();
55 aChanMaster->CompleteRequest(r);
58 TInt DIicBusChannelMaster::TransFlow(TIicBusTransaction* aTransaction)
60 if(aTransaction->iHalfDuplexTrans == NULL)
62 else if(aTransaction->iFullDuplexTrans == NULL)
63 return DIicBusChannel::EHalfDuplex;
64 else return DIicBusChannel::EFullDuplex;
67 TInt8 DIicBusChannelMaster::IsMasterBusy()
69 if((iTransCount&~KTransCountMsBit) == 0)
74 DIicBusChannelMaster::DIicBusChannelMaster(TBusType aBusType, TChannelDuplex aChanDuplex)
75 : DIicBusChannel(DIicBusChannel::EMaster, aBusType, aChanDuplex),
76 iTransQDfc(DIicBusChannelMaster::MsgQFunc, this, NULL, 1), iChannelReady(EFalse)
78 new(&iTimeoutTimer) NTimer(SlaveTimeoutCallback,this);
81 DIicBusChannelMaster::~DIicBusChannelMaster()
83 delete iSlaveTimeoutDfc;
86 TInt DIicBusChannelMaster::Init()
88 iSlaveTimeoutDfc = new TDfc(SlaveTimeoutCallback,(TAny*)this, 7); // Highest Dfc priority
95 // Function to used to indicate if the Slave response has exceeded
97 TInt DIicBusChannelMaster::StartSlaveTimeOutTimer(TInt aTime)
99 TInt r = iTimeoutTimer.OneShot(NKern::TimerTicks(aTime),(*iSlaveTimeoutDfc));
103 void DIicBusChannelMaster::SetDfcQ(TDfcQue* aDfcQue)
105 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::SetDfcQ 0x%x\n",aDfcQue));
106 __ASSERT_DEBUG(aDfcQue!=NULL, Kern::Fault(KIicChannelPanic,__LINE__));
108 iTransQDfc.SetDfcQ(iDfcQ);
109 iSlaveTimeoutDfc->SetDfcQ(iDfcQ);
111 __ASSERT_DEBUG(!iChannelReady, Kern::Fault(KIicChannelPanic,__LINE__));
112 if (!iTransactionQ.IsEmpty())
114 iTransaction=(TIicBusTransaction*)(iTransactionQ.First()->Deque());
115 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::SetDfcQ got %08x",iTransaction));
116 iTransaction->iState=TIicBusTransaction::EAccepted;
117 iCurrentTransaction = iTransaction;
122 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::SetDfcQ"));
125 iCurrentTransaction = NULL;
130 void DIicBusChannelMaster::CompleteRequest(TInt aResult)
132 TIicBusTransaction* nextTrans=NextTrans(iCurrentTransaction);
134 if((aResult != KErrNone)||(nextTrans == NULL))
135 EndTransaction(iTransaction,aResult,iTransaction->iCallback);
138 nextTrans->iBusId = iCurrentTransaction->iBusId; // Pass the bus configuration info to the PSL
139 iCurrentTransaction = nextTrans;
140 DoRequest(nextTrans);
147 For Master-side transaction queuing APIs
148 the Channel implementation sends the transaction as a message to the Channel's message queue,
149 optionally blocking the client thread on the message's semaphore (synchronous APIs).
151 TInt DIicBusChannelMaster::QueueTransaction(TIicBusTransaction* aTransaction)
153 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::QueueTransaction, aTransaction=0x%x\n",aTransaction));
154 // Send the transaction as a message to the Channel's message queue
155 // Synchronous API, so block the calling thread during the processing
156 TInt r = QueueTransaction(aTransaction, NULL);
158 return r; // Transaction was not queued - so don't wait for a notification that it completed.
160 __KTRACE_OPT(KIIC, Kern::Printf("<DIicBusChannelMaster::QueueTransaction ret %d",aTransaction->iResult));
161 return aTransaction->iResult;
164 TInt DIicBusChannelMaster::QueueTransaction(TIicBusTransaction* aTransaction, TIicBusCallback* aCallback)
166 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::QueueTransaction, aTransaction=0x%x, aCallback=0x%x\n",aTransaction,aCallback));
168 // Check aTransaction is non-NULL (aCallback may be NULL if the synchronous operation is required).
169 if(aTransaction == NULL)
174 // Send the transaction as a message to the Channel's message queue and return
175 aTransaction->iCallback = aCallback;
176 if(aCallback != NULL)
178 aCallback->iTransaction = aTransaction;
181 // Call the PSL implementation to check that the header is valid for this channel
182 TInt r = CheckHdr(aTransaction->iHeader);
188 // Duplex operation is indicated in the transaction object
189 if((TransFlow((TIicBusTransaction*)aTransaction) == DIicBusChannel::EFullDuplex) &&
190 (ChannelDuplex() != DIicBusChannel::EFullDuplex))
192 return KErrNotSupported;
195 DThread* pC =& Kern::CurrentThread();
197 __ASSERT_DEBUG(aTransaction->iState == TIicBusTransaction::EFree, Kern::Fault(KIicChannelPanic,__LINE__));
198 if(!(iTransCount & KTransCountMsBit))
200 if(iTransCount < ~KTransCountMsBit)
211 aTransaction->iSyncNotification.iCount = 0;
212 aTransaction->iSyncNotification.iOwningThread = &pC->iNThread;
216 aTransaction->iState = TIicBusTransaction::EAccepted;
217 iTransaction = aTransaction;
218 iCurrentTransaction = aTransaction;
219 iChannelReady = EFalse;
224 iTransactionQ.Add(aTransaction);
225 aTransaction->iState = TIicBusTransaction::EDelivered;
229 // Wait on a semaphore if called from synchronous version
230 if(aCallback == NULL)
232 NKern::FSWait(&aTransaction->iSyncNotification);
238 TInt DIicBusChannelMaster::CancelTransaction(TIicBusTransaction* aTransaction)
240 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::CancelTransaction, aTransaction=0x%x\n",aTransaction));
242 // Check aTransaction is non-NULL
243 if(aTransaction == NULL)
247 // If the method is called on a synchronous transaction return KErrNotSupported
248 if(aTransaction->iCallback == NULL)
250 return KErrNotSupported;
256 switch(aTransaction->iState)
258 case TIicBusTransaction::EDelivered:
260 aTransaction->Deque();
261 pT=_LOFF(aTransaction->iSyncNotification.iOwningThread,DThread,iNThread);
262 aTransaction->iState=TIicBusTransaction::EFree;
263 --iTransCount; // Count must be greater than zero if the transaction is in this state
268 case TIicBusTransaction::EAccepted:
274 case TIicBusTransaction::EFree:
289 #else /*MASTER_MODE*/
291 TInt DIicBusChannelMaster::QueueTransaction(TIicBusTransaction* /*aTransaction*/)
293 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::QueueTransaction invoked when not in MASTER_MODE!\n"));
294 return KErrNotSupported;
297 TInt DIicBusChannelMaster::QueueTransaction(TIicBusTransaction* /*aTransaction*/, TIicBusCallback* /*aCallback*/)
299 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::QueueTransaction invoked when not in MASTER_MODE!\n"));
300 return KErrNotSupported;
303 TInt DIicBusChannelMaster::CancelTransaction(TIicBusTransaction* /*aTransaction*/)
305 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::CancelTransaction invoked when not in MASTER_MODE!\n"));
306 return KErrNotSupported;
308 #endif/*MASTER_MODE*/
310 // Invoked in response to receiving a message
311 // Function argument is a pointer to the required channel object
312 // Invoke the channel's PSL implementation of the DoRequest method with a pointer to the transaction object
314 void DIicBusChannelMaster::MsgQFunc(TAny* aPtr)
316 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::MsgQFunc, aPtr=0x%x\n",aPtr));
317 DIicBusChannelMaster* channel=(DIicBusChannelMaster*)aPtr;
318 TIicBusTransaction* trans = channel->iTransaction;
320 #ifdef IIC_INSTRUMENTATION_MACRO
321 IIC_MPROCESSTRANS_START_PIL_TRACE;
324 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::MsgQFunc trans->iHeader=0x%x\n",trans->iHeader));
325 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::MsgQFunc trans->iHalfDuplexTrans=0x%x\n",trans->iHalfDuplexTrans));
326 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::MsgQFunc trans->iFullDuplexTrans=0x%x\n",trans->iFullDuplexTrans));
327 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::MsgQFunc trans->iCallback=0x%x\n",trans->iCallback));
328 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMaster::MsgQFunc trans->iFlags=0x%x\n",trans->iFlags));
331 // invoke the preamble callback function supplied by the client of the IIC if there is any
332 if(GetTransFlags(trans) & KTransactionWithPreamble)
334 TIicBusTransactionPreamble* transPreamble = (TIicBusTransactionPreamble*)trans;
335 TIicBusPreamble funcPtr=NULL;
336 funcPtr=(GetPreambleFuncPtr(transPreamble));
337 funcPtr(transPreamble,GetPreambleFuncArg(transPreamble));
339 r = channel->DoRequest(trans); // Instigate processing in the PSL
341 channel->EndTransaction(trans, r, trans->iCallback);
344 void DIicBusChannelMaster::EndTransaction(TIicBusTransaction* aTrans, TInt aResult, TIicBusCallback* aCb)
346 #ifdef IIC_INSTRUMENTATION_MACRO
347 IIC_MPROCESSTRANS_END_PIL_TRACE;
349 Complete(aResult,aTrans);
352 aCb->iResult = aResult;
357 void DIicBusChannelMaster::CancelTimeOut()
359 iTimeoutTimer.Cancel();
362 void DIicBusChannelMaster::Complete(TInt aResult, TIicBusTransaction* aTransaction) //Completes a kernel message and receive the next one
364 __KTRACE_OPT(KIIC, Kern::Printf("MsgB::Complete %08x, %d",this,aResult));
366 __ASSERT_DEBUG(aTransaction->iState == TIicBusTransaction::EAccepted, Kern::Fault(KIicChannelPanic,__LINE__));
367 aTransaction->iResult=aResult;
368 aTransaction->iState=TIicBusTransaction::EFree;
370 DThread* pT=_LOFF(aTransaction->iSyncNotification.iOwningThread,DThread,iNThread);
371 __ASSERT_DEBUG(!iChannelReady, Kern::Fault(KIicChannelPanic,__LINE__));
372 if (!iTransactionQ.IsEmpty())
374 TIicBusTransaction* pM=(TIicBusTransaction*)iTransactionQ.First()->Deque();
375 __KTRACE_OPT(KIIC, Kern::Printf("rxnext: got %08x",pM));
376 pM->iState=TIicBusTransaction::EAccepted;
378 iCurrentTransaction = pM;
383 __KTRACE_OPT(KIIC, Kern::Printf("rxnext"));
386 iCurrentTransaction = NULL;
388 NKern::FSSignal(&aTransaction->iSyncNotification,&iTransactionQLock);
392 TInt DIicBusChannelMaster::StaticExtension(TUint /*aFunction*/, TAny* /*aParam1*/, TAny* /*aParam*/)
394 return KErrNotSupported;
397 TInt DIicBusChannelMaster::Spare1(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
399 return KErrNotSupported;
404 TInt DIicBusChannelSlave::CaptureChannel(TDes8* aConfigHdr, TIicBusSlaveCallback* aCallback, TInt& aChannelId, TBool aAsynch)
406 // Only one client can have access to the Slave channel at any one time. Any subsequent attempts to capture the
407 // same channel should return an error.
409 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::CaptureChannel\n"));
410 if((aConfigHdr == NULL) || (aCallback == NULL))
412 __KTRACE_OPT(KIIC, Kern::Printf("ERROR: non-NULL argument aConfigHdr=0x%x, aCallback=0x%x\n",aConfigHdr,aCallback));
416 // Check the header is valid for the channel
417 TInt r = CheckHdr(aConfigHdr);
420 // Check Slave channel is available for capture
421 // If iChannelInUse is not set, capture should succeed
422 // If this Slave channel is part of a MasterSlave channel iChannelInUse will already be set
423 // but iClient will still be NULL. In this case, the capture should succeed.
424 TInt intState=__SPIN_LOCK_IRQSAVE(iSpinLock);
425 DThread* pT=&(Kern::CurrentThread());
426 if((iChannelInUse)&&(iClient!=NULL))
433 __SPIN_UNLOCK_IRQRESTORE(iSpinLock,intState);
438 aCallback->iChannel=this;
440 iConfigHeader=aConfigHdr; // Header alread checked, so just assign it
442 // Invoke the PSL processing
445 aChannelId = 0; // the client should read iChannelId from the callback object.
446 r=DoRequest(EAsyncConfigPwrUp);
449 r=DoRequest(ESyncConfigPwrUp);
453 if(!aAsynch) // For asynchronous version there is nothing more to do until the callback is invoked
455 SetChannelId(aChannelId);
456 iClientTimeoutDfc->SetDfcQ(iNotif->iDfcQ);
461 // PSL encountered an error
464 CompleteAsynchCapture(r); // Queue the client callback for execution
471 TInt DIicBusChannelSlave::ReleaseChannel()
473 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::ReleaseChannel\n"));
474 // Release a previously-captured channel.
476 // Ensure that only the channel's client may release the channel
477 DThread* pT=&(Kern::CurrentThread());
478 if(iClient!=pT) // Direct access since iClient can't be modified while channel is still captured
479 return KErrAccessDenied;
481 r=SetNotificationTrigger(0); // Attempt to clear notification requests
482 if((r!=KErrNone)&&(r!=KErrTimedOut)) // KErrTimedOut refers to an earlier transaction, and is for information only
484 iTimeoutTimer.Cancel();
485 r=DoRequest(EPowerDown);
488 TInt intState=__SPIN_LOCK_IRQSAVE(iSpinLock);
490 iChannelInUse=0; // Channel now available for capture by other clients
491 __SPIN_UNLOCK_IRQRESTORE(iSpinLock,intState);
492 pT->AsyncClose(); // Allow Client thread to close now channel has been released
496 // PSL error when releasing the channel - have to assume the hardware has a problem.
497 // The channel is no longer considered "captured" by the controller, i.e. will not accept commands
498 // But not having cleared the busy flag means that it can not be used for master channel
499 // operations if it is part of a MasterSlave channel
500 // Must Fault the Kernel.
501 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::ReleaseChannel - PSL returned error code %d\n",r));
502 __ASSERT_ALWAYS(EFalse, Kern::Fault(KIicChannelPanic,__LINE__));
507 TInt DIicBusChannelSlave::RegisterRxBuffer(TPtr8 aRxBuffer, TInt8 aBufGranularity, TInt8 aNumWords, TInt8 aOffset)
509 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::RegisterRxBuffer\n"));
510 #ifdef IIC_INSTRUMENTATION_MACRO
511 IIC_SREGRXBUF_START_PIL_TRACE;
514 // Ensure that only the channel's client may perform this operation
515 DThread* pT=&(Kern::CurrentThread());
516 if(iClient!=pT) // Direct access since iClient can't be modified while channel is still captured
517 return KErrAccessDenied;
518 //If the buffer pointer is NULL, return KErrArgument
519 if(aRxBuffer.Ptr() == NULL)
521 // If a buffer is already registered, a subsequent request to do the same should return KErrAlreadyExists
522 // This will be the case if SetNotificationTrigger has been invoked with any of ERxAllBytes, ERxUnderrun or ERxOverrun
523 if(iReqTrig&(ERxAllBytes|ERxUnderrun|ERxOverrun))
527 iRxBuf=(TInt8*)(aRxBuffer.Ptr());
528 iRxGranularity=aBufGranularity;
529 iNumRxWords=aNumWords;
532 #ifdef IIC_INSTRUMENTATION_MACRO
533 IIC_SREGRXBUF_END_PIL_TRACE;
538 TInt DIicBusChannelSlave::RegisterTxBuffer(TPtr8 aTxBuffer, TInt8 aBufGranularity, TInt8 aNumWords, TInt8 aOffset)
540 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::RegisterTxBuffer - default implementation\n"));
541 #ifdef IIC_INSTRUMENTATION_MACRO
542 IIC_SREGTXBUF_START_PIL_TRACE;
545 // Ensure that only the channel's client may perform this operation
546 DThread* pT=&(Kern::CurrentThread());
547 if(iClient!=pT) // Direct access since iClient can't be modified while channel is still captured
548 return KErrAccessDenied;
549 //If the buffer pointer is NULL, return KErrArgument
550 if(aTxBuffer.Ptr() == NULL)
552 // If a buffer is already registered and a request is pending, a subsequent request to register a buffer should return
554 // This will be the case if SetNotificationTrigger has been invoked with any of ETxAllBytes, ETxUnderrun or ETxOverrun
555 if(iReqTrig&(ETxAllBytes|ETxUnderrun|ETxOverrun))
559 iTxBuf=(TInt8*)(aTxBuffer.Ptr());
560 iTxGranularity=aBufGranularity;
561 iNumTxWords=aNumWords;
564 #ifdef IIC_INSTRUMENTATION_MACRO
565 IIC_SREGTXBUF_END_PIL_TRACE;
570 TInt DIicBusChannelSlave::SetNotificationTrigger(TInt aTrigger)
572 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::SetNotificationTrigger invoked with aTrigger=0x%x\n",aTrigger));
573 // Ensure that only the channel's client may perform this operation
574 DThread* pT=&(Kern::CurrentThread());
575 if(iClient!=pT) // Direct access since iClient can't be modified while channel is still captured
576 return KErrAccessDenied;
578 TInt retVal = KErrNone;
579 TInt trigger = aTrigger;
580 switch (iTimerState) // Handle existing timer conditions
582 case DIicBusChannelSlave::EInactive:
584 // In this state no timers have been started - so no action required
587 case DIicBusChannelSlave::EWaitForClient:
589 // Client has responded within the given time period, so stop the timer.
593 case DIicBusChannelSlave::EWaitForMaster:
595 // If both Rx and Tx events had been requested, and if ERxOverrun had occurred, the Client
596 // may have called this function with new requests for Rx notifications, in order to
597 // continue reading data sent by the Master. At this point, all Rx request flags in iReqTrig
598 // will have been cleared.
599 // If both Rx and Tx events had been requested, and if ETxUnderrun had occurred, the Client
600 // may have called this function with new requests for Tx notifications, in order to
601 // continue sending data to the Master. At this point, all Tx request flags in iReqTrig will
602 // have been cleared.
604 // To handle the ERxOverrun situation, aTrigger may specify only the new Rx trigger settings,
605 // or it may also re-specify the exisiting Tx settings. Similarly for the ETxUnderrun, aTrigger
606 // may specify only the new Tx triggers, or both the new Tx triggers and the existing Rx triggers.
608 // However, if Rx flags are still set in iReqTrig, a request to change the Rx settings
609 // will be rejected (similarly, for Tx).
611 // If the requested notification is zero, which would represent an attempt to clear all triggers
612 // while the Master may have commenced a transfer, the request will be rejected.
613 __ASSERT_DEBUG(iReqTrig != 0, Kern::Fault(KIicChannelPanic,__LINE__));
618 TInt allRxFlags = ERxAllBytes | ERxOverrun | ERxUnderrun;
619 TInt allTxFlags = ETxAllBytes | ETxOverrun | ETxUnderrun;
620 // Check the Rx flags
621 TInt rxTrig = iReqTrig & allRxFlags;
622 TInt reqRxTrig = trigger & allRxFlags;
627 else if(reqRxTrig != 0)
629 // New Rx triggers specified - check that Client is not attempting to modify the existing
631 if(rxTrig ^ reqRxTrig)
633 // Attempting to change the trigger settings - so reject the request
637 // Check the Tx flags
638 TInt txTrig = iReqTrig & allTxFlags;
639 TInt reqTxTrig = trigger & allTxFlags;
644 else if(reqTxTrig != 0)
646 // New Tx triggers specified - check that Client is not attempting to modify the existing
648 if(txTrig ^ reqTxTrig)
650 // Attempting to change the trigger settings - so reject the request
654 // Udate iReqTrig for the new requested trigger
655 // and cancel the timer - since we are now starting a new transfer, we should
656 // allow the Master the time to perform it
657 trigger = rxTrig | txTrig;
661 case DIicBusChannelSlave::EClientTimeout:
663 // The Client did not respond within the expected time for the previous transfer. As a result,
664 // the transaction will have been terminated for the Client.
665 // Set the return value to inform the Client that it previously exceeded the expected response time
666 retVal = KErrTimedOut;
671 __ASSERT_DEBUG(0, Kern::Fault(KIicChannelPanic,__LINE__));
675 // Ensure that requests for notification of asynchronous capture of channel is removed, since this
676 // is not a valid event to request (the channel will already have been captured to get this far).
677 // Also ensure that requests for EGeneralBusError are removed, since they are redundant (a notification
678 // for a bus error is unconditional) and just represent overhead.
679 trigger &= ~(EAsyncCaptChan | EGeneralBusError);
681 iReqTrig = (TInt8)trigger; // Not atomic access since only client thread modifies iReqTrig
682 iAccumTrig = 0; // New transfer, so initialise accumulated event record
684 // Overrun and/or underrun may be requested if Client is unsure how much data is to follow,
685 // so need to instigate Rx/Tx operation for any such request
686 if(iReqTrig & (ERxOverrun|ERxUnderrun|ERxAllBytes))
688 reqFlags |= EReceive;
690 if(iReqTrig & (ETxOverrun|ETxUnderrun|ETxAllBytes))
692 reqFlags |= ETransmit;
694 TInt r = DoRequest(reqFlags);
697 // PSL encountered an error in intiating the requested trigger. Set the return value accordingly.
698 // Assume triggers have been cancelled - if they have not, the client-provided callback will still
699 // be invoked, but it will have been warned to expecte erroneous behaviour by the value assigned to retVal.
701 retVal = KErrGeneral;
703 else // PSL accepted the request, so update timer and state information
707 case DIicBusChannelSlave::EInactive:
709 // Do not start the timer. Must wait for the Master to access a Slave buffer before considering
710 // a transaction as started.
713 case DIicBusChannelSlave::EWaitForClient:
715 // Client has responded within the given time period. The next state is
716 // dependent on the requested trigger - if set to zero, the Client is explicitly
717 // ending the transaction, so the next state is EInactive; otherwise, the
718 // Client has indicated the next action expected from the Master and so the
719 // timer is started and next state is EWaitForMaster
722 iTimerState = DIicBusChannelSlave::EInactive;
726 iTimerState = DIicBusChannelSlave::EWaitForMaster;
731 case DIicBusChannelSlave::EClientTimeout:
733 // For the previous transfer, the Client failed to respond within the required time - and
734 // the PSL will have been instructed to indicate a bus error (so the Master
735 // will have been informed). The error code returned by this function will be KErrTimedOut
736 // so the Client will be informed of what has happened.
737 // A transaction is considered to start when the Slave is addressed by the Master
738 // (as indicated by the PSL invoking NotifyClient) - which has not yet happened -
739 // so the next state is EInactive.
740 iTimerState=DIicBusChannelSlave::EInactive;
743 case DIicBusChannelSlave::EWaitForMaster:
745 // In this case we are handling a new requested trigger from the client to handle ERxOverrun or
746 // ETxUnderrun. The PSL has accepted the new trigger, so must allow the Master sufficient time
747 // to perform the newly-requested transfer; the timer has already been stopped, so just start it again..
753 __ASSERT_DEBUG(0, Kern::Fault(KIicChannelPanic,__LINE__));
763 TInt DIicBusChannelSlave::CaptureChannel(TDes8* /*aConfigHdr*/, TIicBusSlaveCallback* /*aCallback*/, TInt& /*aChannelId*/, TBool /*aAsynch*/)
765 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::CaptureChannel invoked when not in SLAVE_MODE!\n"));
766 return KErrNotSupported;
769 TInt DIicBusChannelSlave::ReleaseChannel()
771 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::ReleaseChannel invoked when not in SLAVE_MODE!\n"));
772 return KErrNotSupported;
775 TInt DIicBusChannelSlave::RegisterRxBuffer(TPtr8 /*aRxBuffer*/, TInt8 /*aBufGranularity*/, TInt8 /*aNumWords*/, TInt8 /*aOffset*/)
777 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::RegisterRxBuffer invoked when not in SLAVE_MODE!\n"));
778 return KErrNotSupported;
781 TInt DIicBusChannelSlave::RegisterTxBuffer(TPtr8 /*aTxBuffer*/, TInt8 /*aBufGranularity*/, TInt8 /*aNumWords*/, TInt8 /*aOffset*/)
783 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::RegisterTxBuffer invoked when not in SLAVE_MODE!\n"));
784 return KErrNotSupported;
787 TInt DIicBusChannelSlave::SetNotificationTrigger(TInt /*aTrigger*/)
789 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::SetNotificationTrigger invoked when not in SLAVE_MODE!\n"));
790 return KErrNotSupported;
794 DIicBusChannelSlave::DIicBusChannelSlave(TBusType aBusType, TChannelDuplex aChanDuplex, TInt16 aChannelId)
795 : DIicBusChannel(DIicBusChannel::ESlave, aBusType, aChanDuplex),
796 iChannelId(aChannelId), iTimerState(EInactive),
797 iMasterWaitTime(KSlaveDefMWaitTime), iClientWaitTime(KSlaveDefCWaitTime),
798 iSpinLock(TSpinLock::EOrderGenericIrqLow2) // Semi-arbitrary, low priority value
800 #ifndef STANDALONE_CHANNEL
805 DIicBusChannelSlave::~DIicBusChannelSlave()
807 delete iClientTimeoutDfc;
810 void DIicBusChannelSlave::SlaveStaticCB(TAny* aPtr)
812 DIicBusChannelSlave* chan = (DIicBusChannelSlave*)aPtr;
813 chan->SlaveTimerCallBack();
817 TInt DIicBusChannelSlave::Init()
819 iClientTimeoutDfc = new TDfc(SlaveStaticCB,(TAny*)this, 7); // Highest Dfc priority
820 if(!iClientTimeoutDfc)
826 void DIicBusChannelSlave::ChanCaptureCallback(TInt aResult)
828 __KTRACE_OPT(KIIC, Kern::Printf("ChanCaptureCallback: aChannel=0x%x, aResult=%d\n",this,aResult));
832 if(aResult == KErrNone)
834 SetChannelId(channelId);
835 #ifndef STANDALONE_CHANNEL
836 __ASSERT_DEBUG(iController, Kern::Fault(KIicChannelPanic,__LINE__));
837 iController->InstallCapturedChannel(channelId, this);
839 iClientTimeoutDfc->SetDfcQ(iNotif->iDfcQ);
845 #ifdef IIC_INSTRUMENTATION_MACRO
846 IIC_SCAPTCHANASYNC_END_PIL_TRACE;
848 CompleteAsynchCapture(r); // Queue the client callback for execution
851 void DIicBusChannelSlave::SlaveTimerCallBack()
853 __KTRACE_OPT(KIIC, Kern::Printf("SlaveTimerCallBack"));
854 if(iTimerState == DIicBusChannelSlave::EWaitForMaster)
856 // Master timeout. Consider the transaction terminated - call NotifyClient
857 // to inform both the Client and the PSL, and update the state machine
858 NotifyClient(EGeneralBusError);
860 else if(iTimerState == DIicBusChannelSlave::EWaitForClient)
862 // Client timeout. Instigate the PSL-specific bus error indication
863 iTimerState=DIicBusChannelSlave::EClientTimeout;
864 SendBusErrorAndReturn();
868 __ASSERT_DEBUG(0, Kern::Fault(KIicChannelPanic,__LINE__));
873 void DIicBusChannelSlave::StartTimerByState()
875 if(iTimerState == DIicBusChannelSlave::EWaitForMaster)
877 iTimeoutTimer.OneShot(NKern::TimerTicks(iMasterWaitTime),(*iClientTimeoutDfc));
879 else if(iTimerState == DIicBusChannelSlave::EWaitForClient)
881 iTimeoutTimer.OneShot(NKern::TimerTicks(iClientWaitTime),(*iClientTimeoutDfc));
885 __ASSERT_DEBUG(NULL, Kern::Fault(KIicChannelPanic,__LINE__));
889 void DIicBusChannelSlave::StopTimer()
891 iTimeoutTimer.Cancel();
894 TInt DIicBusChannelSlave::UpdateReqTrig(TInt8& aCbTrigVal, TInt& aCallbackRet)
896 __KTRACE_OPT(KIIC, Kern::Printf("UpdateReqTrig"));
899 iAccumTrig |= iNotif->iTrigger; // Update the accumulated event history, regardless of if the trigger was requested
901 if(iNotif->iTrigger & EGeneralBusError)
903 // In the event of a bus error, always cancel the timer and call the Client callback
904 nextSteps |= (EStopTimer | EInvokeCb);
905 iTimerState = EInactive;
906 aCallbackRet = KErrGeneral;
908 else if(iNotif->iTrigger == EAsyncCaptChan)
910 // For asynchronous channel capture, no timers are involved - just call the Client callback
911 nextSteps |= EInvokeCb;
912 aCallbackRet = KErrCompletion;
914 else if((iNotif->iTrigger & iReqTrig) != 0)
916 // If a requested Rx event has occurred, clear all Rx flags from the requested triggers (similarly for Tx)
917 if(iNotif->iTrigger & (ERxAllBytes | ERxUnderrun | ERxOverrun))
919 iReqTrig &= ~(ERxAllBytes | ERxUnderrun | ERxOverrun);
921 if(iNotif->iTrigger & (ETxAllBytes | ETxUnderrun | ETxOverrun))
923 iReqTrig &= ~(ETxAllBytes | ETxUnderrun | ETxOverrun);
926 if(iTimerState == EInactive)
928 nextSteps |= (EStartTimer | EInvokeCb);
929 // The next state in the state machine depends on if all the requested events have occurred
932 // All triggers required have occurred, so transition to state EWaitForClient
933 iTimerState = EWaitForClient;
937 // The Client can request both Rx an Tx triggers; if only one has occurred, must wait for
938 // the Master to generate the other
939 iTimerState = EWaitForMaster;
941 aCallbackRet = KErrNone;
943 else if(iTimerState == EWaitForMaster)
945 // The next state in the state machine depends on if all the requested events have occurred
948 // All triggers required have occurred, so transition to state EWaitForClient
949 iTimerState = EWaitForClient;
950 nextSteps |= (EStopTimer | EInvokeCb | EStartTimer);
954 // The Client can request both Rx an Tx triggers; if only one has occurred, must wait for
955 // the Master to generate the other - so remain in this state, do not cancel the timer or
956 // re-start it with a new timeout period. Still invoke the callback to notify the client
957 // that at least one of the requested triggers has occurred.
958 nextSteps |= EInvokeCb;
960 aCallbackRet = KErrNone;
962 else if((iTimerState == EWaitForClient) || (iTimerState == EClientTimeout))
964 // No triggers are expected in these states (iReqTrig==0).
965 __ASSERT_DEBUG(NULL, Kern::Fault(KIicChannelPanic,__LINE__));
968 aCbTrigVal = iAccumTrig;
973 void DIicBusChannelSlave::NotifyClient(TInt aTrigger)
975 TIicBusSlaveCallback* notif = iNotif;
976 notif->iTrigger = aTrigger; // Ensure ProcessData is provided with the trigger
978 if(NKern::CurrentContext() == NKern::EThread && &(Kern::CurrentThread()) == iClient)
980 // PSL will update notif to represent the events that have occurred
981 ProcessData(aTrigger, notif);
982 // Only invoke the client's callback (and update the state machine) if one of the requested triggers has
983 // occurred or if a bus error has been witnessed
984 TInt8 callbackTrig=0;
986 TInt nextSteps = UpdateReqTrig(callbackTrig, callbackRet);
987 if(nextSteps & EStopTimer)
989 iTimeoutTimer.Cancel();
991 if(nextSteps & EInvokeCb)
993 (notif->iCallback)(notif->iChannelId, (TInt)callbackRet, callbackTrig, notif->iRxWords, notif->iTxWords, notif->iParam);
994 // Callback now processed, so re-initialise callback object members
996 notif->iReturn=KErrNone;
999 iAccumTrig = 0; // and re-initialise the accumulated history as the transaction is considered terminated
1001 if(nextSteps & EStartTimer)
1003 StartTimerByState();
1006 else if(NKern::CurrentContext() == NKern::EInterrupt)
1012 TInt DIicBusChannelSlave::SetMasterWaitTime(TInt8 aWaitTime)
1014 if((aWaitTime<0)||(aWaitTime>KMaxWaitTime))
1015 return KErrArgument;
1016 iMasterWaitTime=aWaitTime;
1020 TInt DIicBusChannelSlave::SetClientWaitTime(TInt8 aWaitTime)
1022 if((aWaitTime<0)||(aWaitTime>KMaxWaitTime))
1023 return KErrArgument;
1024 iClientWaitTime=aWaitTime;
1028 void DIicBusChannelSlave::SendBusErrorAndReturn()
1033 void DIicBusChannelSlave::SetChannelId(TInt& aChannelId)
1036 aChannelId = (iInstanceCount<<16);
1038 // The PSL-specific channel identifier was stored in this generic class' member iChannelId at registration time
1039 aChannelId |= iChannelId;
1040 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::SetChannelId: iInstanceCount=0x%x, iChannelId=0x%x returned aChannelId=0x%x\n",iInstanceCount,iChannelId,aChannelId));
1041 iNotif->iChannelId=aChannelId;
1044 void DIicBusChannelSlave::CompleteAsynchCapture(TInt aResult)
1046 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlave::CompleteAsynchCapture aResult = %d",aResult));
1047 if(NKern::CurrentContext() == NKern::EThread && &Kern::CurrentThread() == iClient)
1049 iNotif->iCallback(iNotif->iChannelId, aResult, EAsyncCaptChan, NULL, NULL, iNotif->iParam);
1054 iNotif->iReturn=aResult;
1055 iNotif->iTrigger=EAsyncCaptChan;
1056 iNotif->iTxWords=NULL;
1057 iNotif->iRxWords=NULL;
1059 if(NKern::CurrentContext() == NKern::EInterrupt)
1065 TInt DIicBusChannelSlave::StaticExtension(TUint /*aFunction*/, TAny* /*aParam1*/, TAny* /*aParam*/)
1067 return KErrNotSupported;
1070 TInt DIicBusChannelSlave::Spare1(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
1072 return KErrNotSupported;
1075 TInt DIicBusChannelMasterSlave::QueueTransaction(TIicBusTransaction* aTransaction)
1077 return QueueTransaction(aTransaction,NULL);
1080 TInt DIicBusChannelMasterSlave::QueueTransaction(TIicBusTransaction* aTransaction, TIicBusCallback* aCallback)
1083 iMasterChannel->Lock();
1084 if(iSlaveChannel->iChannelInUse)
1088 TInt16 count=(TInt16)((iMasterChannel->iTransCount)&~KTransCountMsBit);
1089 if(count<~KTransCountMsBit)
1092 count|=KTransCountMsBit;
1097 iMasterChannel->Unlock();
1099 r=(iMasterChannel->QueueTransaction(aTransaction, aCallback));
1103 TInt DIicBusChannelMasterSlave::CaptureChannel(TDes8* aConfigHdr, TIicBusSlaveCallback* aCallback, TInt& aChannelId, TBool aAsynch)
1105 iMasterChannel->Lock();
1107 if(iSlaveChannel->iChannelInUse)
1111 if(iMasterChannel->IsMasterBusy())
1114 iSlaveChannel->iChannelInUse = 1;
1116 iMasterChannel->Unlock();
1118 r=iSlaveChannel->CaptureChannel(aConfigHdr, aCallback, aChannelId, aAsynch);
1123 TInt DIicBusChannelMasterSlave::ReleaseChannel()
1125 iMasterChannel->Lock();
1126 TInt r=iSlaveChannel->ReleaseChannel();
1127 iMasterChannel->Unlock();
1131 TInt DIicBusChannelMasterSlave::StaticExtension(TUint /*aFunction*/, TAny* /*aParam1*/, TAny* /*aParam*/)
1133 return KErrNotSupported;
1136 #ifdef STANDALONE_CHANNEL
1137 EXPORT_C DIicBusChannelMasterSlave::DIicBusChannelMasterSlave(TBusType aBusType, TChannelDuplex aChanDuplex, DIicBusChannelMaster* aMasterChan, DIicBusChannelSlave* aSlaveChan)
1138 : DIicBusChannel(DIicBusChannel::EMasterSlave, aBusType, aChanDuplex),
1139 iMasterChannel(aMasterChan),
1140 iSlaveChannel(aSlaveChan)
1142 //If in stand-alone channel mode, the client assigns a channel number to the MasterSlave channel it creates.