diff -r 000000000000 -r 96e5fb8b040d kerneltest/e32test/iic/iic_psl/iic_slaveclient.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/e32test/iic/iic_psl/iic_slaveclient.cpp Thu Dec 17 09:24:54 2009 +0200 @@ -0,0 +1,1647 @@ +// 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: +// e32test/iic/iic_slaveclient.cpp +// Simulated client of IIC Platform Independent Layer (PIL) Slave functionality +// + +#include // for DThread, TDfc +#ifdef STANDALONE_CHANNEL +#include +#else +#include +#endif +#include "../t_iic.h" + +#ifdef STANDALONE_CHANNEL +#include +#include "i2c.h" +#endif +#ifdef LOG_SLAVECLIENT +#define CLIENT_PRINT(str) Kern::Printf str +#else +#define CLIENT_PRINT(str) +#endif + +//For channel creation +#ifdef STANDALONE_CHANNEL +#define NUM_CHANNELS 3 // Arbitrary + +#if defined(MASTER_MODE) && !defined(SLAVE_MODE) +const TInt KChannelTypeArray[NUM_CHANNELS] = {DIicBusChannel::EMaster, DIicBusChannel::EMaster, DIicBusChannel::EMaster}; +#elif defined(MASTER_MODE) && defined(SLAVE_MODE) +const TInt KChannelTypeArray[NUM_CHANNELS] = {DIicBusChannel::EMaster, DIicBusChannel::ESlave, DIicBusChannel::EMasterSlave}; +#else +const TInt KChannelTypeArray[NUM_CHANNELS] = {DIicBusChannel::ESlave, DIicBusChannel::ESlave, DIicBusChannel::ESlave}; +#endif +#define CHANNEL_TYPE(n) (KChannelTypeArray[n]) +#define CHANNEL_DUPLEX(n) (DIicBusChannel::EHalfDuplex) +#define BUS_TYPE (DIicBusChannel::EI2c) + +#if defined(MASTER_MODE) +const TInt8 KI2cChannelNumBase = 10; // Arbitrary, real platform may consult the Configuration Repository + // Note limit of 5 bit representation (0-31) + +#else +const TInt8 KI2cChannelNumBase = 10 + NUM_CHANNELS; // For Slave mode, want to provide different response + // If client assumes Master mode, should be informed not available +#endif/*MASTER_MODE*/ + +LOCAL_C TInt8 AssignChanNumI2c() + { + static TInt8 iBaseChanNumI2c = KI2cChannelNumBase; + CLIENT_PRINT(("I2C AssignChanNum - on entry, iBaseChanNum = 0x%x\n",iBaseChanNumI2c)); + return iBaseChanNumI2c++; // Arbitrary, for illustration + } + +class DIicSlaveClientChan : public DBase + { +public: + DIicSlaveClientChan(DIicBusChannel* aChan, TInt8 aChanNum, TInt aChanType):iChan(aChan),iChanNumber(aChanNum),iChanType(aChanType){}; + ~DIicSlaveClientChan(); + TInt GetChanNum()const {return iChanNumber;}; + TInt GetChanType()const {return iChanType;}; + DIicBusChannel* GetChannelPtr(){return iChan;}; +private: + TInt8 iChanNumber; + TInt iChanType; + DIicBusChannel* iChan; + }; + +DIicSlaveClientChan::~DIicSlaveClientChan() + { + if(iChan) + delete iChan; + } + +#endif/*STANDALONE_CHANNEL*/ + +const TInt KIicSlaveClientThreadPriority = 24; + +const TInt KIicSlaveClientDfcPriority = 3; // 0 to 7, 7 is highest + +const TInt KMaxNumChannels = 2; // 1 "true" slave, one "dummy" + +#ifdef STANDALONE_CHANNEL +_LIT(KLddRootName,"iic_slaveclient_ctrless"); +#else +_LIT(KLddRootName,"iic_slaveclient"); +#endif +_LIT(KIicSlaveClientThreadName,"IicSlaveClientLddThread"); + + +struct TCapsIicSlaveClient + { + TVersion version; + }; + + +class DDeviceIicSlaveClient : public DLogicalDevice + { + public: + /** + * The constructor + */ + DDeviceIicSlaveClient(); + /** + * The destructor + */ + ~DDeviceIicSlaveClient(); + /** + * Second stage constructor - install the device + */ + virtual TInt Install(); + /** + * Get the Capabilites of the device + * @param aDes descriptor that will contain the returned capibilites + */ + virtual void GetCaps(TDes8 &aDes) const; + /** + * Create a logical channel to the device + */ + virtual TInt Create(DLogicalChannelBase*& aChannel); + }; + + +class DChannelIicSlaveClient : public DLogicalChannel + { + public: + DChannelIicSlaveClient(); + ~DChannelIicSlaveClient(); + + virtual TInt DoCreate(TInt aUnit, const TDesC8* aInfo, const TVersion& aVer); + + void RequestComplete(TInt r); + TInt CheckDataWritten(); + TInt CheckDataRead(); + + static void SlaveClientCallbackFunc(TInt aChannelId, TInt aReturn, TInt aTrigger, TInt16 aRxWords, TInt16 aTxWords, TAny* aParam); +#ifdef STANDALONE_CHANNEL + static TInt OrderEntries(const DIicSlaveClientChan& aMatch, const DIicSlaveClientChan& aEntry); +#endif + protected: + virtual void HandleMsg(TMessageBase* aMsg); // Note: this is a pure virtual in DLogicalChannel + + TInt DoControl(TInt aId, TAny* a1, TAny* a2); // Name for convenience! + TInt DoRequest(TInt aId, TRequestStatus* aStatus, TAny* a1, TAny* a2); // Name for convenience! + + private: + TInt InitSlaveClient(); + TInt CbProcessOverUnderRunRxTx(); + TInt RegisterRxBuffer(TInt aChannelId, TPtr8 aRxBuffer, TInt8 aBufGranularity, TInt8 aNumWords, TInt8 aOffset); + TInt SetNotificationTrigger(TInt aChannelId, TInt aTrigger); + TInt StaticExtension(TUint aId, TUint aFunction, TAny* aParam1, TAny* aParam2); + TInt RegisterTxBuffer(TInt aChannelId, TPtr8 aTxBuffer, TInt8 aBufGranularity, TInt8 aNumWords, TInt8 aOffset); + TInt CaptureChannel(TInt aBusId, TDes8* aConfigHdr, TIicBusSlaveCallback* aCallback, TInt& aChannelId, TBool aAsynch=NULL); + TInt ReleaseChannel(TInt aChannelId); + private: + TDynamicDfcQue* iDfcQue; + TIicBusSlaveCallback* iNotif; + + HBuf8* iConfigHdr; + TRequestStatus* iStatus; + + TUint8* iRxBuf; + TUint8* iTxBuf; + + TUint8* iBusTxCheckBuf; + + TInt8 iNumRegRxWords; + TInt8 iNumRegTxWords; + + TInt8 iTxRegGranularity; + TInt8 iTxRegOffset; + TInt8 iTxReqNumWords; + + TInt8 iRxRegGranularity; + TInt8 iRxRegOffset; + TInt8 iRxReqNumWords; + + TUint iBusId; +#ifdef STANDALONE_CHANNEL + // For some slave-channel-only functions,e.g. ReleaseChannel, RegisterRxBuffer, etc. + // the client needs to remember the slave channel that has been captured + struct TCapturedChannel + { + DIicSlaveClientChan* iChannel; + TInt iChannelId; + }; + TCapturedChannel iCapturedChan; +#endif + public: + DThread* iClient; + TInt iChannelId; // public to be accessible by callback + TInt* iClientChanId; + + TInt iExpectedTrigger; + TInt iFullDuplexReq; + TInt iBlockedTrigger; + + typedef enum TTestOverUnderState + { + EStartState = 0x1, + ERxOverrun_1, + ERxOverrun_2, + ETxUnderrun_1, + ETxUnderrun_2 + }; + TTestOverUnderState iTestOverUnderState; + }; + +DDeviceIicSlaveClient::DDeviceIicSlaveClient() +// Constructor + { + CLIENT_PRINT(("> DDeviceIicSlaveClient::DDeviceIicSlaveClient()")); + iParseMask=0; // No info, no PDD, no Units + iUnitsMask=0; + iVersion=TVersion(KIicClientMajorVersionNumber, + KIicClientMinorVersionNumber, + KIicClientBuildVersionNumber); + } + +#ifdef STANDALONE_CHANNEL +//Store all the channels created by the client into an array +RPointerArray ChannelArray; +#endif +DDeviceIicSlaveClient::~DDeviceIicSlaveClient() +// Destructor + { + CLIENT_PRINT(("> DDeviceIicSlaveClient::~DDeviceIicSlaveClient()")); +#ifdef STANDALONE_CHANNEL + //The client is reponsible for channel destroy in STANDALONE_CHANNEL mode + ChannelArray.ResetAndDestroy(); +#endif + } + +TInt DDeviceIicSlaveClient::Install() +// Install the device driver. + { + CLIENT_PRINT(("> DDeviceIicSlaveClient::Install()")); + + return(SetName(&KLddRootName)); + } + + +void DDeviceIicSlaveClient::GetCaps(TDes8& aDes) const +// Return the IicClient capabilities. + { + CLIENT_PRINT(("> DDeviceIicSlaveClient::GetCaps(TDes8& aDes) const")); + TPckgBuf b; + b().version=TVersion(KIicClientMajorVersionNumber, + KIicClientMinorVersionNumber, + KIicClientBuildVersionNumber); + Kern::InfoCopy(aDes,b); + } + + +TInt DDeviceIicSlaveClient::Create(DLogicalChannelBase*& aChannel) +// Create a channel on the device. + { + CLIENT_PRINT(("> DDeviceIicSlaveClient::Create(DLogicalChannelBase*& aChannel)")); + if(iOpenChannels>=KMaxNumChannels) + return KErrOverflow; + aChannel=new DChannelIicSlaveClient; + return aChannel?KErrNone:KErrNoMemory; + } + +#ifdef STANDALONE_CHANNEL +// auxiliary function for ordering entries in the array of channels +TInt DChannelIicSlaveClient::OrderEntries(const DIicSlaveClientChan& aMatch, const DIicSlaveClientChan& aEntry) + { + TUint8 l=(TUint8)aMatch.GetChanNum(); + TUint8 r=(TUint8)aEntry.GetChanNum(); + if(l>r) + return -1; + else if(l EntryOrder(DChannelIicSlaveClient::OrderEntries); + +TInt GetChanPtr(const TInt aBusId, DIicSlaveClientChan*& aChan) + { + __KTRACE_OPT(KIIC, Kern::Printf("GetChanPtr, aBusId=0x%x\n",aBusId)); + TInt32 chanId; + chanId = GET_CHAN_NUM(aBusId); + __KTRACE_OPT(KIIC, Kern::Printf("GetChanPtr, chanId=0x%x\n",chanId)); + DIicSlaveClientChan chanEntry(NULL,(TInt8)chanId, DIicBusChannel::EMasterSlave); + + TInt r = KErrNotFound; + TInt aIndex = ChannelArray.FindInOrder(&chanEntry, EntryOrder); + + if(aIndex >= 0) + { + aChan = ChannelArray[aIndex]; + r = KErrNone; + } + + return r; + } +#endif/*STANDALONE_CHANNEL*/ + +DECLARE_STANDARD_LDD() + { +#ifdef STANDALONE_CHANNEL + TInt r = KErrNone; + DIicBusChannel *chan = NULL, *chanM = NULL, *chanS = NULL; + DIicSlaveClientChan* aSlaveClientChan; + for(TInt i=0; iCreate()!=KErrNone) + { + delete chan; + return NULL; + } + aSlaveClientChan = new DIicSlaveClientChan(chan,AssignChanNumI2c(),DIicBusChannel::EMaster); + if(!aSlaveClientChan) + { + delete chan; + return NULL; + } + r = ChannelArray.InsertInOrder(aSlaveClientChan,EntryOrder); + if(r!=KErrNone) + { + delete chan; + delete aSlaveClientChan; + break; + } + } + #endif + #if defined(MASTER_MODE) && defined(SLAVE_MODE) + if(CHANNEL_TYPE(i) == DIicBusChannel::EMasterSlave) + { + chanM=new DSimulatedIicBusChannelMasterI2c(BUS_TYPE,CHANNEL_DUPLEX(i)); + if(!chanM) + return NULL; + chanS=new DSimulatedIicBusChannelSlaveI2c(BUS_TYPE,CHANNEL_DUPLEX(i)); + if(!chanS) + { + delete chanM; + return NULL; + } + chan=new DSimulatedIicBusChannelMasterSlaveI2c(BUS_TYPE,CHANNEL_DUPLEX(i),(DSimulatedIicBusChannelMasterI2c*)chanM,(DSimulatedIicBusChannelSlaveI2c*)chanS); // Generic implementation + if(!chan) + { + CLIENT_PRINT(("\n\nI2C: Channel of type (%d) not created for index %d\n\n",CHANNEL_TYPE(i),i)); + delete chanM; + delete chanS; + return NULL; + } + CLIENT_PRINT(("I2C chan created at 0x%x\n",chan)); + if(((DIicBusChannelMasterSlave*)chan)->DoCreate()!=KErrNone) + { + delete chanM; + delete chanS; + delete chan; + return NULL; + } + aSlaveClientChan = new DIicSlaveClientChan(chan,AssignChanNumI2c(),DIicBusChannel::EMasterSlave); + if(!aSlaveClientChan) + { + delete chanM; + delete chanS; + delete chan; + return NULL; + } + r = ChannelArray.InsertInOrder(aSlaveClientChan,EntryOrder); + if(r!=KErrNone) + { + delete chanM; + delete chanS; + delete chan; + delete aSlaveClientChan; + break; + } + } + #endif + #if defined(SLAVE_MODE) + if(CHANNEL_TYPE(i) == (DIicBusChannel::ESlave)) + { + chan=new DSimulatedIicBusChannelSlaveI2c(BUS_TYPE,CHANNEL_DUPLEX(i)); + if(!chan) + { + CLIENT_PRINT(("\n\nI2C: Channel of type (%d) not created for index %d\n\n",CHANNEL_TYPE(i),i)); + return NULL; + } + CLIENT_PRINT(("I2C chan created at 0x%x\n",chan)); + if(((DSimulatedIicBusChannelSlaveI2c*)chan)->Create()!=KErrNone) + return NULL; + aSlaveClientChan = new DIicSlaveClientChan(chan,AssignChanNumI2c(),DIicBusChannel::ESlave); + if(!aSlaveClientChan) + { + delete chan; + return NULL; + } + r = ChannelArray.InsertInOrder(aSlaveClientChan,EntryOrder); + if(r!=KErrNone) + { + delete chan; + delete aSlaveClientChan; + break; + } + } + #endif + #if !defined(MASTER_MODE) && !defined(SLAVE_MODE) + #error I2C mode not defined as Master, Slave nor Master-Slave + #endif + } +#endif + return new DDeviceIicSlaveClient; + } + + + +DChannelIicSlaveClient::DChannelIicSlaveClient() +// Constructor + { + iFullDuplexReq=iBlockedTrigger=iExpectedTrigger=0; + CLIENT_PRINT(("> DChannelIicSlaveClient::DChannelIicSlaveClient()")); + iClient=&Kern::CurrentThread(); + // Increase the DThread's ref count so that it does not close without us + iClient->Open(); + iTestOverUnderState = EStartState; + } + +DChannelIicSlaveClient::~DChannelIicSlaveClient() +// Destructor + { + CLIENT_PRINT(("> DChannelIicSlaveClient::~DChannelIicSlaveClient()")); + iDfcQue->Destroy(); + delete iNotif; + delete iRxBuf; + delete iTxBuf; + delete iBusTxCheckBuf; + // decrement the DThread's reference count + Kern::SafeClose((DObject*&)iClient, NULL); + } + +void DChannelIicSlaveClient::RequestComplete(TInt r) + { + Kern::RequestComplete(iClient, iStatus, r); + } + +TInt DChannelIicSlaveClient::RegisterRxBuffer(TInt aChannelId, TPtr8 aRxBuffer, TInt8 aBufGranularity, TInt8 aNumWords, TInt8 aOffset) + { + TInt r = KErrNone; +#ifdef STANDALONE_CHANNEL + DIicSlaveClientChan* aChanPtr = NULL; + if(iCapturedChan.iChannelId == aChannelId) + aChanPtr = iCapturedChan.iChannel; + if(!aChanPtr) + return KErrArgument; + if(aChanPtr->GetChanType() == DIicBusChannel::EMasterSlave) + r = ((DIicBusChannelMasterSlave*)(aChanPtr->GetChannelPtr()))->RegisterRxBuffer(aRxBuffer, aBufGranularity, aNumWords, aOffset); + else if(aChanPtr->GetChanType() == DIicBusChannel::ESlave) + r = ((DIicBusChannelSlave*)(aChanPtr->GetChannelPtr()))->RegisterRxBuffer(aRxBuffer, aBufGranularity, aNumWords, aOffset); +#else + r = IicBus::RegisterRxBuffer(aChannelId, aRxBuffer, aBufGranularity, aNumWords, aOffset); +#endif + return r; + } + +TInt DChannelIicSlaveClient::SetNotificationTrigger(TInt aChannelId, TInt aTrigger) + { + TInt r = KErrNone; +#ifdef STANDALONE_CHANNEL + DIicSlaveClientChan* aChanPtr = NULL; + if(iCapturedChan.iChannelId == aChannelId) + aChanPtr = iCapturedChan.iChannel; + if(!aChanPtr) + return KErrArgument; + if(aChanPtr->GetChanType() == DIicBusChannel::EMasterSlave) + r = ((DIicBusChannelMasterSlave*)(aChanPtr->GetChannelPtr()))->SetNotificationTrigger(aTrigger); + else if(aChanPtr->GetChanType() == DIicBusChannel::ESlave) + r = ((DIicBusChannelSlave*)(aChanPtr->GetChannelPtr()))->SetNotificationTrigger(aTrigger); +#else + r = IicBus::SetNotificationTrigger(aChannelId, aTrigger); +#endif + return r; + } + +TInt DChannelIicSlaveClient::StaticExtension(TUint aId, TUint aFunction, TAny* aParam1, TAny* aParam2) + { + TInt r = KErrNone; +#ifdef STANDALONE_CHANNEL + DIicSlaveClientChan* aChanPtr; + r = GetChanPtr(aId, aChanPtr); + if(r != KErrNone) + return r; + if(!aChanPtr) + return KErrArgument; + if(aChanPtr->GetChanType() == DIicBusChannel::EMasterSlave) + r = ((DIicBusChannelMasterSlave*)(aChanPtr->GetChannelPtr()))->StaticExtension(aFunction, aParam1, aParam2); + else if(aChanPtr->GetChanType() == DIicBusChannel::ESlave) + r = ((DIicBusChannelSlave*)(aChanPtr->GetChannelPtr()))->StaticExtension(aFunction, aParam1, aParam2); +#else + r = IicBus::StaticExtension(aId, aFunction, aParam1, aParam2); +#endif + return r; + } + +TInt DChannelIicSlaveClient::RegisterTxBuffer(TInt aChannelId, TPtr8 aTxBuffer, TInt8 aBufGranularity, TInt8 aNumWords, TInt8 aOffset) + { + TInt r = KErrNone; +#ifdef STANDALONE_CHANNEL + DIicSlaveClientChan* aChanPtr = NULL; + if(iCapturedChan.iChannelId == aChannelId) + aChanPtr = iCapturedChan.iChannel; + if(!aChanPtr) + return KErrArgument; + if(aChanPtr->GetChanType() == DIicBusChannel::EMasterSlave) + r = ((DIicBusChannelMasterSlave*)(aChanPtr->GetChannelPtr()))->RegisterTxBuffer(aTxBuffer, aBufGranularity, aNumWords, aOffset); + else if(aChanPtr->GetChanType() == DIicBusChannel::ESlave) + r = ((DIicBusChannelSlave*)(aChanPtr->GetChannelPtr()))->RegisterTxBuffer(aTxBuffer, aBufGranularity, aNumWords, aOffset); +#else + r = IicBus::RegisterTxBuffer(aChannelId, aTxBuffer, aBufGranularity, aNumWords, aOffset); +#endif + return r; + } + +TInt DChannelIicSlaveClient::CaptureChannel(TInt aBusId, TDes8* aConfigHdr, TIicBusSlaveCallback* aCallback, TInt& aChannelId, TBool aAsynch) + { + TInt r = KErrNone; +#ifndef STANDALONE_CHANNEL + r = IicBus::CaptureChannel(aBusId, aConfigHdr, aCallback, aChannelId, aAsynch); +#else + // Check that that aCallback!=NULL and aConfigHdr!=NULL - if not, return KErrArgument + if(!aCallback || !aConfigHdr) + { + return KErrArgument; + } + + // Get the channel + DIicSlaveClientChan* chanPtr = NULL; + if(r == KErrNone) + { + r = GetChanPtr(aBusId, chanPtr); + if(r == KErrNone) + { + if(!chanPtr) + { + r = KErrArgument; + } + else + { + switch(chanPtr->GetChanType()) + { + // CaptureChannel requests are only supported by channels in Slave mode. + case DIicBusChannel::EMaster: + { + r = KErrNotSupported; + break; + } + case DIicBusChannel::EMasterSlave: + { + r = ((DIicBusChannelMasterSlave*) (chanPtr->GetChannelPtr()))->CaptureChannel(aConfigHdr, aCallback, aChannelId, aAsynch); + break; + } + case DIicBusChannel::ESlave: + { + r = ((DIicBusChannelSlave*) (chanPtr->GetChannelPtr()))->CaptureChannel(aConfigHdr, aCallback, aChannelId, aAsynch); + break; + } + default: + { + r = KErrArgument; + } + } + // For synchronous capture, if successful then install the channel + if(r == KErrNone) + { + if(!aAsynch) + { + iCapturedChan.iChannel = chanPtr; + iCapturedChan.iChannelId = aChannelId; + } + else + //For asynchronous capture, record slaveChanPtr, if later failed capture, + //clean iCapturedChannel in client's callback. + iCapturedChan.iChannel = chanPtr; + } + } + } + } +#endif + return r; + } + +TInt DChannelIicSlaveClient::ReleaseChannel(TInt aChannelId) + { + TInt r = KErrNone; +#ifndef STANDALONE_CHANNEL + r = IicBus::ReleaseChannel(aChannelId); +#else + __KTRACE_OPT(KIIC, Kern::Printf("DChannelIicSlaveClient::ReleaseChannel, channelID = 0x%x \n",aChannelId)); + // Acquire the pointer to the Slave Channel + if(iCapturedChan.iChannelId != aChannelId) + return KErrNotFound; + + if((iCapturedChan.iChannel)->GetChanType() == DIicBusChannel::EMasterSlave) + r = ((DIicBusChannelMasterSlave*)((iCapturedChan.iChannel)->GetChannelPtr()))->ReleaseChannel(); + else if((iCapturedChan.iChannel)->GetChanType() == DIicBusChannel::ESlave) + r = ((DIicBusChannelSlave*)((iCapturedChan.iChannel)->GetChannelPtr()))->ReleaseChannel(); + //After release channel, reset iCapturedChan + iCapturedChan.iChannel = NULL; + iCapturedChan.iChannelId = 0; +#endif + return r; + } +TInt DChannelIicSlaveClient::CbProcessOverUnderRunRxTx() + { + CLIENT_PRINT(("> DChannelIicSlaveClient::CbProcessOverUnderRunRxTx(), iTestOverUnderState=%d\n",iTestOverUnderState)); + TInt r = KErrNone; + switch (iTestOverUnderState) + { + case(EStartState): + { + // In this state, no action is required + break; + }; + case(ERxOverrun_1): + { + CLIENT_PRINT(("CbProcessOverUnderRunRxTx: entry state = ERxOverrun_1\n")); + // At this point, the outstanding request trigger should be ETxAllBytes | ETxUnderrun + // and the flag to indicate duplex transfers should be cleared + if((iExpectedTrigger != (ETxAllBytes | ETxUnderrun)) || (iFullDuplexReq != ETxAllBytes)) + { + CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx, iExpectedTrigger=0x%x, iFullDuplexReq=%d\n",iExpectedTrigger,iFullDuplexReq)); + r=KErrGeneral; + } + else + { + // Simulate providing a new buffer (actually, re-use existing buffer) + CLIENT_PRINT(("CbProcessOverUnderRunRxTx: invoking RegisterRxBuffer\n")); + TPtr8 rxPtr(iRxBuf,KRxBufSizeInBytes); + r = RegisterRxBuffer(iChannelId, rxPtr, iRxRegGranularity, iNumRegRxWords, iRxRegOffset); + + if(r != KErrNone) + { + CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - RegisterRxBuffer returned %d\n",r)); + } + else + { + // For the next step, just specify the new Rx triggers (do not specify Tx triggers) + r = SetNotificationTrigger(iChannelId, (ERxAllBytes | ERxOverrun)); + if(r != KErrNone) + { + CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - SetNotificationTrigger returned %d\n",r)); + } + else + { + iExpectedTrigger = ERxAllBytes | ERxOverrun | ETxAllBytes | ETxUnderrun; + iFullDuplexReq |= (ERxAllBytes|ETxAllBytes); + iTestOverUnderState = ERxOverrun_2; // Prepare for callback + // The requested number of words when the buffer was registered was 8, so simulate 10 + // to provoke an RxOverrun event. + TInt numWords=10; + // To support testing, any values of aId for StaticExtension must be shifted left one place + // and for a Slave, the two msbs must be zero + TInt ctrlIoVal = RBusDevIicClient::ECtrlIoRxWords; + ctrlIoVal = (ctrlIoVal << 1) & 0x3FFFFFFF; + r = StaticExtension(iBusId, ctrlIoVal, (TAny*)iChannelId, (TAny*)numWords); + } + } + } + break; + }; + case(ERxOverrun_2): + { + CLIENT_PRINT(("CbProcessOverUnderRunRxTx: entry state = ERxOverrun_2\n")); + // At this point, the outstanding request trigger should be ETxAllBytes | ETxUnderrun + // and the flag to indicate duplex transfers should be cleared + if((iExpectedTrigger != (ETxAllBytes | ETxUnderrun)) || (iFullDuplexReq != ETxAllBytes)) + { + CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx, iExpectedTrigger=0x%x, iFullDuplexReq=%d\n",iExpectedTrigger,iFullDuplexReq)); + r=KErrGeneral; + } + else + { + // Simulate providing a new buffer (actually, re-use existing buffer) + CLIENT_PRINT(("CbProcessOverUnderRunRxTx: invoking RegisterRxBuffer\n")); + TPtr8 rxPtr(iRxBuf,KRxBufSizeInBytes); + r = RegisterRxBuffer(iChannelId, rxPtr, iRxRegGranularity, iNumRegRxWords, iRxRegOffset); + if(r != KErrNone) + { + CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - RegisterRxBuffer returned %d\n",r)); + } + else + { + // Test that an attempt to modify existing Tx notification requests is rejected + r = SetNotificationTrigger(iChannelId, (ERxAllBytes | ERxOverrun | ETxAllBytes | ETxOverrun)); + if(r != KErrInUse) + { + CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - SetNotificationTrigger returned %d, expected KErrInUse\n",r)); + } + else + { + // For the next step, specify the new Rx triggers and the Tx triggers + r = SetNotificationTrigger(iChannelId, (ERxAllBytes | ERxOverrun | ETxAllBytes | ETxUnderrun)); + if(r != KErrNone) + { + CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - SetNotificationTrigger returned %d, expected KErrNone\n",r)); + } + else + { + iExpectedTrigger = ERxAllBytes | ERxOverrun | ETxAllBytes | ETxUnderrun; + iFullDuplexReq |= (ERxAllBytes|ETxAllBytes); + iTestOverUnderState = ETxUnderrun_1; // Prepare for callback + // The requested number of words when the buffer was registered was 12, so simulate 14 + // to provoke an TxUnderrun event. + TInt numWords=14; + // To support testing, any values of aId for StaticExtension must be shifted left one place + // and for a Slave, the two msbs must be zero + TInt ctrlIoVal = RBusDevIicClient::ECtrlIoTxWords; + ctrlIoVal = (ctrlIoVal << 1) & 0x3FFFFFFF; + r = StaticExtension(iBusId, ctrlIoVal, (TAny*)iChannelId, (TAny*)numWords); + } + } + } + } + break; + }; + case(ETxUnderrun_1): + { + CLIENT_PRINT(("CbProcessOverUnderRunRxTx: entry state = ETxOverrun_1\n")); + // At this point, the outstanding request trigger should be ERxAllBytes | ERxOverrun + // and the flag to indicate duplex transfers should be cleared + if((iExpectedTrigger != (ERxAllBytes | ERxOverrun)) || (iFullDuplexReq != ERxAllBytes)) + { + CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx, iExpectedTrigger=0x%x, iFullDuplexReq=%d\n",iExpectedTrigger,iFullDuplexReq)); + r=KErrGeneral; + } + else + { + // Simulate providing a new buffer (actually, re-use existing buffer) + CLIENT_PRINT(("CbProcessOverUnderRunRxTx: invoking RegisterTxBuffer\n")); + TPtr8 rxPtr(iTxBuf,KTxBufSizeInBytes); + r = RegisterTxBuffer(iChannelId, rxPtr, iTxRegGranularity, iNumRegTxWords, iTxRegOffset); + if(r != KErrNone) + { + CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - RegisterTxBuffer returned %d\n",r)); + } + else + { + // For the next step, just specify the new Tx triggers (do not specify Rx triggers) + r = SetNotificationTrigger(iChannelId, (ETxAllBytes | ETxUnderrun)); + if(r != KErrNone) + { + CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - SetNotificationTrigger returned %d\n",r)); + } + else + { + iExpectedTrigger = ERxAllBytes | ERxOverrun | ETxAllBytes | ETxUnderrun; + iFullDuplexReq |= (ERxAllBytes|ETxAllBytes); + iTestOverUnderState = ETxUnderrun_2; // Prepare for callback + // The requested number of words when the buffer was registered was 12, so simulate 14 + // to provoke an TxUnderrun event. + TInt numWords=14; + // To support testing, any values of aId for StaticExtension must be shifted left one place + // and for a Slave, the two msbs must be zero + TInt ctrlIoVal = RBusDevIicClient::ECtrlIoTxWords; + ctrlIoVal = (ctrlIoVal << 1) & 0x3FFFFFFF; + r = StaticExtension(iBusId, ctrlIoVal, (TAny*)iChannelId, (TAny*)numWords); + } + } + } + break; + }; + case(ETxUnderrun_2): + { + CLIENT_PRINT(("CbProcessOverUnderRunRxTx: entry state = ETxUnderrun_2\n")); + // At this point, the outstanding request trigger should be ERxAllBytes | ERxOverrun + // and the flag to indicate duplex transfers should be cleared + if((iExpectedTrigger != (ERxAllBytes | ERxOverrun)) || (iFullDuplexReq != ERxAllBytes)) + { + CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx, iExpectedTrigger=0x%x, iFullDuplexReq=%d\n",iExpectedTrigger,iFullDuplexReq)); + r=KErrGeneral; + } + else + { + // Simulate providing a new buffer (actually, re-use existing buffer) + CLIENT_PRINT(("CbProcessOverUnderRunRxTx: invoking RegisterRxBuffer\n")); + TPtr8 rxPtr(iTxBuf,KTxBufSizeInBytes); + r = RegisterTxBuffer(iChannelId, rxPtr, iTxRegGranularity, iNumRegTxWords, iTxRegOffset); + if(r != KErrNone) + { + CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - RegisterTxBuffer returned %d\n",r)); + } + else + { + // Test that an attempt to modify existing Rx notification requests is rejected + r = SetNotificationTrigger(iChannelId, (ERxAllBytes | ERxUnderrun | ETxAllBytes | ETxUnderrun)); + if(r != KErrInUse) + { + CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - SetNotificationTrigger returned %d, expected KErrInUse\n",r)); + } + else + { + // For the next step, specify the new Rx triggers and the Tx triggers + r = SetNotificationTrigger(iChannelId, (ERxAllBytes | ERxOverrun | ETxAllBytes | ETxUnderrun)); + + if(r != KErrNone) + { + CLIENT_PRINT(("Error: CbProcessOverUnderRunRxTx - SetNotificationTrigger returned %d, expected KErrNone\n",r)); + } + else + { + // Simulate a simultaneous ERxAllBytes and ETxAllBytes event. + iExpectedTrigger = ERxAllBytes | ETxAllBytes; + iFullDuplexReq |= (ERxAllBytes|ETxAllBytes); + iTestOverUnderState = EStartState; // Prepare for callback - return to normal operation + // Need to pass the number of words in an array, for use by StaticExtension + TInt parms[2]; + parms[0]= 8; // Number of Rx Words + parms[1]=12; // Number of Tx Words + // To support testing, any values of aId for StaticExtension must be shifted left one place + // and for a Slave, the two msbs must be zero + TInt ctrlIoVal = RBusDevIicClient::ECtrlIoRxTxWords; + ctrlIoVal = (ctrlIoVal << 1) & 0x3FFFFFFF; + r = StaticExtension(iBusId, ctrlIoVal, (TAny*)iChannelId, (TAny*)(&parms[0])); + } + } + } + } + break; + }; + default: + { + r = KErrGeneral; + break; + }; + } + return r; + } + +void DChannelIicSlaveClient::SlaveClientCallbackFunc(TInt aChannelId, TInt aReturn, TInt aTrigger, TInt16 aRxWords, TInt16 aTxWords, TAny* aParam) + { + CLIENT_PRINT(("> SlaveClientCallbackFunc() - aChannelId=0x%x,aReturn=%d,aTrigger=0x%x,aRxWords=0x%x,aTxWords=0x%x,aParam=0x%x\n",aChannelId,aReturn,aTrigger,aRxWords,aTxWords,aParam)); + (void)aTxWords; // Unused if CLIENT_PRINT is undefined + (void)aRxWords; // Unused if CLIENT_PRINT is undefined + DChannelIicSlaveClient* channel = (DChannelIicSlaveClient*)aParam; + + // Ensure only the valid bits of aTrigger are processed + aTrigger &= 0xff; + + CLIENT_PRINT(("SlaveClientCallbackFunc() - channel=0x%x\n",channel)); + if(aTrigger == EAsyncCaptChan) + { + CLIENT_PRINT(("SlaveClientCallbackFunc: capture channel completed\n")); + // Set iChannelId, and write to user-side variable. + channel->iChannelId=aChannelId; + TInt r=Kern::ThreadRawWrite(channel->iClient,channel->iClientChanId,&aChannelId,sizeof(TInt)); + if(r == KErrNone) + r=aReturn; +#ifdef STANDALONE_CHANNEL + // Set the captured channel's iChannelId if the capture succeeds. + if(r != KErrCompletion) + (channel->iCapturedChan).iChannel = NULL; + else + (channel->iCapturedChan).iChannelId = aChannelId; +#endif/*STANDALONE_CHANNEL*/ + channel->RequestComplete(r); // Inform user of error + return; + } + else + { + if(aTrigger&ERxAllBytes) + { + CLIENT_PRINT(("SlaveClientCallbackFunc() - ERxAllBytes\n")); + aTrigger&= ~ERxAllBytes; + channel->iExpectedTrigger&=~ERxAllBytes; + channel->iFullDuplexReq&=~ERxAllBytes; + aReturn=channel->CheckDataRead(); + // Check underrun + if(aTrigger&ERxUnderrun) + { + if(channel->iExpectedTrigger&ERxUnderrun) + { + CLIENT_PRINT(("\nSlaveClientCallbackFunc() - expected ERxUnderrun found OK\n\n")); + channel->iExpectedTrigger&=~ERxUnderrun; + } + else + { + CLIENT_PRINT(("\nSlaveClientCallbackFunc() - unexpected ERxUnderrun indicated\n\n")); + aReturn = KErrGeneral; + } + } + else + { + if(channel->iExpectedTrigger&ERxUnderrun) + { + CLIENT_PRINT(("\nSlaveClientCallbackFunc() - expected ERxUnderrun not (yet) seen\n\n")); + aReturn = KErrGeneral; + } + } + // Check overrun + if(aTrigger&ERxOverrun) + { + if(channel->iExpectedTrigger&ERxOverrun) + { + CLIENT_PRINT(("\nSlaveClientCallbackFunc() - expected ERxOverrun found OK\n\n")); + channel->iExpectedTrigger&=~ERxOverrun; + } + else + { + CLIENT_PRINT(("\nSlaveClientCallbackFunc() - unexpected ERxOverrun indicated\n\n")); + aReturn = KErrGeneral; + } + } + else + { + if(channel->iExpectedTrigger&ERxOverrun) + { + CLIENT_PRINT(("\nSlaveClientCallbackFunc() - expected ERxOverrun not (yet) seen\n\n")); + aReturn = KErrGeneral; + } + } + } + + if(aTrigger&ETxAllBytes) + { + CLIENT_PRINT(("SlaveClientCallbackFunc() - ETxAllBytes\n")); + aTrigger&= ~ETxAllBytes; + channel->iExpectedTrigger&=~ETxAllBytes; + channel->iFullDuplexReq&=~ETxAllBytes; + aReturn=channel->CheckDataWritten(); + // Check underrun + if(aTrigger&ETxUnderrun) + { + if(channel->iExpectedTrigger&ETxUnderrun) + { + CLIENT_PRINT(("\nSlaveClientCallbackFunc() - expected ETxUnderrun found OK\n\n")); + channel->iExpectedTrigger&=~ETxUnderrun; + } + else + { + CLIENT_PRINT(("\nSlaveClientCallbackFunc() - unexpected ETxUnderrun indicated\n\n")); + aReturn = KErrGeneral; + } + } + else + { + if(channel->iExpectedTrigger&ETxUnderrun) + { + CLIENT_PRINT(("\nSlaveClientCallbackFunc() - expected ETxUnderrun not (yet) seen\n\n")); + aReturn = KErrGeneral; + } + } + // Check overrun + if(aTrigger&ETxOverrun) + { + if(channel->iExpectedTrigger&ETxOverrun) + { + CLIENT_PRINT(("\nSlaveClientCallbackFunc() - expected ETxOverrun found OK\n\n")); + channel->iExpectedTrigger&=~ETxOverrun; + } + else + { + CLIENT_PRINT(("\nSlaveClientCallbackFunc() - unexpected ETxOverrun indicated\n\n")); + aReturn = KErrGeneral; + } + } + else + { + if(channel->iExpectedTrigger&ETxOverrun) + { + CLIENT_PRINT(("\nSlaveClientCallbackFunc() - expected ETxOverrun not (yet) seen\n\n")); + aReturn = KErrGeneral; + } + } + } + + + if(aTrigger&EGeneralBusError) + { + if(channel->iExpectedTrigger&EGeneralBusError) + { + CLIENT_PRINT(("\nSlaveClientCallbackFunc: EGeneralBusError - as expected\n\n")); + channel->iExpectedTrigger&=~EGeneralBusError; + if(aReturn == KErrGeneral) + { + aReturn=KErrNone; // If aReturn==KErrGeneral, set to KErrNone so t_iic knows test was successful + channel->iFullDuplexReq = 0; // The transaction is considered terminated, so don't wait for more triggers + } + else + { + CLIENT_PRINT(("\nSlaveClientCallbackFunc: aReturn not EGeneralBusError, =0x%x \n\n",aReturn)); + aReturn=KErrGeneral; + } + + } + else + { + CLIENT_PRINT(("\nSlaveClientCallbackFunc() - unexpected EGeneralBusError indicated\n\n")); + aReturn = KErrGeneral; + } + } + + if((aTrigger < 0)||(aTrigger & (~0xFF))) + { + CLIENT_PRINT(("\nSlaveClientCallbackFunc: trigger condition 0x%x is not recognised \n\n",aTrigger)); + } + + // For simulataneous Rx,Tx triggers with ERxOverrun or TxUnderrun testing, need to call the following + if(aReturn == KErrNone) + { + aReturn = channel->CbProcessOverUnderRunRxTx(); + } + + if((channel->iExpectedTrigger == 0)&&(channel->iFullDuplexReq == 0)) + channel->RequestComplete(aReturn); // Complete user-side request only if all the triggers have been satisfied + + } // if(aTrigger == EAsyncCaptChan) + } + + +TInt DChannelIicSlaveClient::CheckDataRead() + { + TInt r=KErrNone; + // This channel will have provided a buffer for writing to, with a specified offset and number of words + // Bytes in the buffer before the offset should be set to zero + // Bytes written at and beyond the offset should exhibit an incrementing count + // Bytes beyond the offset that were not written to should be set to zero + TInt8 numWords=(iRxReqNumWords>iNumRegRxWords)?iNumRegRxWords:iRxReqNumWords; + TInt8 currVal=0; + TInt8 index = 0; + while(indexiNumRegTxWords)?iNumRegTxWords:iTxReqNumWords; + TInt8 index=0; + while(index<(wordsWritten*iTxRegGranularity)) + { + currVal=*(iBusTxCheckBuf+index); + if(currVal != (TInt8)(firstValue+index)) + { + CLIENT_PRINT(("DChannelIicSlaveClient::CheckDataWritten, index=%d, value =0x%x, expected 0x%x",index,currVal,(TInt8)(firstValue+index))); + r=KErrCorrupt; + } + ++index; + } + while(index<(iNumRegTxWords*iTxRegGranularity)) + { + currVal=*(iBusTxCheckBuf+index); + if(currVal != 0) + { + CLIENT_PRINT(("DChannelIicSlaveClient::CheckDataWritten, index=%d, value =0x%x, expected 0",index,currVal)); + r=KErrCorrupt; + } + ++index; + } + return r; + } + + +TInt DChannelIicSlaveClient::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& /*aVer*/) + { + CLIENT_PRINT(("> DChannelIicSlaveClient::DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion &aVer)")); + + TInt r = Kern::DynamicDfcQCreate(iDfcQue,KIicSlaveClientThreadPriority,KIicSlaveClientThreadName); + if(r!=KErrNone) + return r; + SetDfcQ(iDfcQue); + + // Allocate buffers for Rx, Tx operations + iRxBuf = new TUint8[KRxBufSizeInBytes]; + iTxBuf = new TUint8[KTxBufSizeInBytes]; + if((iRxBuf == NULL)||(iTxBuf == NULL)) + return KErrNoMemory; + // Start receiving messages + iMsgQ.Receive(); + + return r; + } + +TInt DChannelIicSlaveClient::InitSlaveClient() + { + iNotif = new TIicBusSlaveCallback(DChannelIicSlaveClient::SlaveClientCallbackFunc, (TAny*)this, iDfcQue, KIicSlaveClientDfcPriority); + if(iNotif == NULL) + { + CLIENT_PRINT(("> DChannelIicSlaveClient::InitSlaveClient ERROR unable to allocate space TIicBusSlaveCallback* iNotif \n")); + return KErrNoMemory; + } + return KErrNone; + } + +void DChannelIicSlaveClient::HandleMsg(TMessageBase* aMsg) + { + TThreadMessage& m=*(TThreadMessage*)aMsg; + TInt id=m.iValue; + + CLIENT_PRINT((" >ldd: DChannelIicSlaveClient::HandleMsg(TMessageBase* aMsg) id=%d\n", id)); + + if (id == (TInt)ECloseMsg) + { + iMsgQ.iMessage->Complete(KErrNone,EFalse); + return; + } + + if (id<0) + { + TRequestStatus* pS=(TRequestStatus*)m.Ptr0(); + TInt r=DoRequest(~id, pS, m.Ptr1(), m.Ptr2()); + if (r!=KErrNone) + { + Kern::RequestComplete(iClient, pS, r); + } + m.Complete(KErrNone,ETrue); + } + else + if((id>=0)&&(id!=KMaxTInt)) + { + TInt r=DoControl(id,m.Ptr0(),m.Ptr1()); + m.Complete(r,ETrue); + } + } + + +TInt DChannelIicSlaveClient::DoControl(TInt aId, TAny* a1, TAny* a2) + { + CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoked with aId=0x%x, a1=0x%x, a2=0x%x\n", aId,a1,a2)); + TInt r=KErrNone; + // To support testing, any values of aId for StaticExtension must be shifted left one place + // and for a Slave, the two msbs must be zero + TInt ctrlIoVal = 0; + if((aId & KTestSlaveControlIo) == KTestSlaveControlIo) + ctrlIoVal = (aId << 1) & 0x3FFFFFFF; + + switch(aId) + { + case(RBusDevIicClient::EInitSlaveClient): + { + r=InitSlaveClient(); + break; + } + + case(RBusDevIicClient::ECaptureChanSync): + { + // a1 is a pointer to the TDes8* aConfigHdr + // a2 is a pointer to TInt* parms[2], where: + // parms[0]=(TInt*)aBusId; + // parms[1]=&aChannelId; + // + TInt* parms[2]; + r=Kern::ThreadRawRead(iClient,a2,&(parms[0]),2*sizeof(TInt*)); + if(r!=KErrNone) + break; // Can't proceed if can't access request parameters + // + TInt hdrSize = Kern::ThreadGetDesLength(iClient,a1); + CLIENT_PRINT(("DChannelIicSlaveClient::DoControl hdrSize = 0x%x\n",hdrSize)); + if (hdrSize<=0) + { + CLIENT_PRINT(("DChannelIicSlaveClient::DoControl ERROR, hdrSize is invalid\n")); + r = KErrArgument; + break; + } + if((iConfigHdr = HBuf8::New(hdrSize)) == NULL) + return KErrNoMemory; + r = Kern::ThreadDesRead(iClient,a1,*iConfigHdr,0); + if(r!=KErrNone) + { + delete iConfigHdr; + break; + } + // Store the address of the user-side variable to update with the ChannelId + iClientChanId=parms[1]; + + CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking (synchronous) CaptureChannel\n")); + r = CaptureChannel((TInt)(parms[0]), iConfigHdr, iNotif, iChannelId); + if(r != KErrNone) + { + delete iConfigHdr; + break; + } + CLIENT_PRINT(("DChannelIicSlaveClient::DoControl CaptureChannelgave iChannelId=0x%x\n",iChannelId)); + + r = Kern::ThreadRawWrite(iClient,iClientChanId,&iChannelId,sizeof(TInt)); + if(r != KErrNone) + delete iConfigHdr; + break; + } + + case(RBusDevIicClient::EReleaseChan): + { + // a1 represents TInt aChannelId + CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking ReleaseChannel\n")); + r = ReleaseChannel((TInt)a1); + delete iConfigHdr; + break; + } + + case(RBusDevIicClient::ERegisterRxBuffer): + { + // a1 represents TInt aChannelId + // a2 represents (TAny*) of TInt8 parms[3] where parms[0]=aBufGranularity; parms[1]=aNumWords; parms[2]=aOffset + TInt8 parms[3]; + r=Kern::ThreadRawRead(iClient,a2,&(parms[0]),3*sizeof(TInt8)); + if(r!=KErrNone) + break; // Can't proceed if can't access request parameters + // Store parameters for checking in the callback + iRxRegGranularity = parms[0]; + iRxRegOffset= parms[2]; + iNumRegRxWords=parms[1]; + + CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking RegisterRxBuffer\n")); + TPtr8 rxPtr(iRxBuf,KRxBufSizeInBytes); + r = RegisterRxBuffer((TInt)a1, rxPtr, parms[0], parms[1], parms[2]); + break; + } + + case(RBusDevIicClient::ERegisterTxBuffer): + { + // a1 represents TInt aChannelId + // a2 represents (TAny*) of TInt8 parms[3] where parms[0]=aBufGranularity; parms[1]=aNumWords; parms[2]=aOffset + TInt8 parms[3]; + r=Kern::ThreadRawRead(iClient,a2,&(parms[0]),3*sizeof(TInt8)); + if(r!=KErrNone) + break; // Can't proceed if can't access request parameters + // Store parameters for checking in the callback + iTxRegGranularity = parms[0]; + iTxRegOffset= parms[2]; + iNumRegTxWords=parms[1]; + + CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking RegisterTxBuffer\n")); + TPtr8 txPtr(iTxBuf,KTxBufSizeInBytes); + r = RegisterTxBuffer((TInt)a1, txPtr, parms[0], parms[1], parms[2]); + break; + } + + case(RBusDevIicClient::ESetNotifTrigger): + { + // a1 represents (TAny*) of TRequestStatus* aStatus + // a2 represents (TAny*) of TInt parms[2] where parms[0]=aChannelId; parms[1]=aTrigger + TInt parms[2]; + r=Kern::ThreadRawRead(iClient,a2,&(parms[0]),2*sizeof(TInt)); + if(r!=KErrNone) + break; // Can't proceed if can't access request parameters + CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking SetNotificationTrigger with aStatus=0x%x, aChannelId=0x%x, aTrigger=0x%x\n",a1,parms[0],parms[1])); + if(a1 == NULL) + { + r = KErrArgument; + break; + } + iStatus=(TRequestStatus*)a1; + // Set the flags for duplex processing + if((parms[1]&ERxAllBytes)&&(parms[1]&ETxAllBytes)) + iFullDuplexReq |= (ERxAllBytes|ETxAllBytes); + r = SetNotificationTrigger(parms[0],parms[1]); + if(r == KErrTimedOut) + r=KErrNone; // KErrTimedOut is returned if the Client has not interacted with IIC for a while + break; + } + + case(RBusDevIicClient::ECtrlIoNotifNoTrigger): + { + // a1 represents (TAny*) of aChannelId + // a2 represents (TAny*) of aTrigger + TInt chanId = (TInt)a1; + TInt trigger = (TInt)a2; + // No TRequestStatus is accessed because the call to SetNotificationTrigger + // is either with zero (when it is valid to do so), or it is being called with a + // trigger value that is expected to be rejected. + r = SetNotificationTrigger(chanId,trigger); + + if(r == KErrNone) + { + if((trigger&ERxAllBytes)&&(trigger&ETxAllBytes)) + iFullDuplexReq |= (ERxAllBytes|ETxAllBytes); + } + if(r == KErrTimedOut) + r=KErrNone; // KErrTimedOut is returned if the Client has not interacted with IIC for a while + break; + } + + case(RBusDevIicClient::ECtrlIoRxWords): + { + // a1 represents TInt aBusId + // a2 represents (TAny*) of TInt parms[2] where parms[0]=aChannelId; parms[1]=aNumWords + TInt parms[2]; + r=Kern::ThreadRawRead(iClient,a2,&(parms[0]),2*sizeof(TInt)); + if(r!=KErrNone) + break; // Can't proceed if can't access request parameters + // Prepare iRxBuf + memset(iRxBuf,0,KRxBufSizeInBytes); + // Store the number of words for checking in the callback + iRxReqNumWords=(TInt8)(parms[1]); + + TInt tempTrigger=0; + // Set the expected result + tempTrigger |= ERxAllBytes; + if(parms[1] < iNumRegRxWords) + tempTrigger |= ERxUnderrun; + if(parms[1] > iNumRegRxWords) + tempTrigger |= ERxOverrun; + if(iExpectedTrigger != EGeneralBusError) + iExpectedTrigger |= tempTrigger; + else + iBlockedTrigger |= tempTrigger; + CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking StaticExtension (ECtrlIoRxWords) with aBusId=0x%x, aChannelId=0x%x, aNumBytes=0x%x\n",(TInt)a1,parms[0],parms[1])); + r = StaticExtension((TUint)a1, (TUint)ctrlIoVal, (TAny*)(parms[0]), (TAny*)(parms[1])); + if(r == KErrTimedOut) + r=KErrNone; // KErrTimedOut is returned if the Client has not interacted with IIC for a while + break; + } + + case(RBusDevIicClient::ECtrlIoTxWords): + { + // a1 represents TInt aBusId + // a2 represents (TAny*) of TInt parms[2] where parms[0]=aChannelId; parms[1]=aNumWords + TInt parms[2]; + r=Kern::ThreadRawRead(iClient,a2,&(parms[0]),2*sizeof(TInt)); + if(r!=KErrNone) + break; // Can't proceed if can't access request parameters + // Prepare iTxBuf + TUint8* ptr=iTxBuf; + for(TInt offset=0; offset iNumRegTxWords) + tempTrigger |= ETxUnderrun; + if(iExpectedTrigger != EGeneralBusError) + iExpectedTrigger |= tempTrigger; + else + iBlockedTrigger |= tempTrigger; + + // The bytes "transmitted" are stored in the simulated bus' iTxCheckBuf + // Since the simulated bus channel is also in the kernel process it shares the same address space + // Get the address of the buffer + // As part of the callback invoked by IIC, this client will check the data stored by the simulated + // bus. Since the simulated bus channel is also in the kernel process it shares the same address space, + // so the buffer can be accessed directly - but the buffer is not created until it receives the following + // StaticExtension command, so the bus identifier represented by a1 is stored to allow accessing the buffer + // adddress from the callback. + iBusId=(TUint)a1; + CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking StaticExtension (ECtrlIoTxWords) with aBusId=0x%x, aChannelId=0x%x, aNumBytes=0x%x\n",(TInt)a1,parms[0],parms[1])); + aId<<=1; + r = StaticExtension((TUint)a1, (TUint)ctrlIoVal, (TAny*)(parms[0]), (TAny*)(parms[1])); + break; + } + + case(RBusDevIicClient::ECtrlIoRxTxWords): + { + // a1 represents TInt aBusId + // a2 represents (TAny*) of TInt parms[3] where parms[0]=aChannelId; parms[1]=aNumRxWords; parms[2]=aNumTxWords + TInt parms[3]; + r=Kern::ThreadRawRead(iClient,a2,&(parms[0]),3*sizeof(TInt)); + if(r!=KErrNone) + break; // Can't proceed if can't access request parameters + // Prepare iRxBuf, iTxBuf + memset(iRxBuf,0,KRxBufSizeInBytes); + TUint8* ptr=iTxBuf; + for(TInt offset=0; offset iNumRegRxWords) + tempTrigger |= ERxOverrun; + + if(parms[2] < iNumRegTxWords) + tempTrigger |= ETxOverrun; + if(parms[2] > iNumRegTxWords) + tempTrigger |= ETxUnderrun; + + if(iExpectedTrigger != EGeneralBusError) + iExpectedTrigger |= tempTrigger; + else + iBlockedTrigger |= tempTrigger; + + // The bytes "transmitted" are stored in the simulated bus' iTxCheckBuf + // Since the simulated bus channel is also in the kernel process it shares the same address space + // Get the address of the buffer + // As part of the callback invoked by IIC, this client will check the data stored by the simulated + // bus. Since the simulated bus channel is also in the kernel process it shares the same address space, + // so the buffer can be accessed directly - but the buffer is not created until it receives the following + // StaticExtension command, so the bus identifier represented by a1 is stored to allow accessing the buffer + // adddress from the callback. + iBusId=(TUint)a1; + + CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking StaticExtension (ECtrlIoRxTxWords) with aBusId=0x%x, aChannelId=0x%x, aNumRxBytes=0x%x, aNumTxBytes=0x%x\n",(TInt)a1,parms[0],parms[1],parms[2])); + r = StaticExtension((TUint)a1, (TUint)ctrlIoVal, (TAny*)(parms[0]), (TAny*)(&(parms[1]))); + if(r == KErrTimedOut) + r=KErrNone; // KErrTimedOut is returned if the Client has not interacted with IIC for a while + break; + } + + case(RBusDevIicClient::ECtlIoBusError): + { + // a1 represents TInt aBusId + // a2 represents TInt aChannelId + // Set the expected result + iExpectedTrigger |= EGeneralBusError; + CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking StaticExtension (ECtlIoBusError) \n")); + r = StaticExtension((TUint)a1, (TUint)ctrlIoVal, (TAny*)a2, NULL); + break; + } + + case(RBusDevIicClient::ECtrlIoUnblockNotification): + { + // a1 represents TInt aBusId + // a2 represents TInt aChannelId + iExpectedTrigger = iBlockedTrigger; + iBlockedTrigger=0; + CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking StaticExtension (ECtrlIoUnblockNotification) \n")); + r = StaticExtension((TUint)a1, (TUint)ctrlIoVal, (TAny*)a2, NULL); + break; + } + + case(RBusDevIicClient::ECtrlIoBlockNotification): + { + // a1 represents TInt aBusId + // a2 represents TInt aChannelId + iBlockedTrigger = iExpectedTrigger; + iExpectedTrigger = EGeneralBusError; // For this test, just interested in if the timeout is detected + // iExpectedTrigger will be reinstated prior to unblocking + CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking StaticExtension (ECtrlIoBlockNotification) \n")); + r = StaticExtension((TUint)a1, (TUint)ctrlIoVal, (TAny*)a2, NULL); + break; + } + + case(RBusDevIicClient::ECtrlIoUpdTimeout): + { + // a1 represents TInt aBusId + // a2 represents TInt aChannelId + + // For this test, instruct the simulated bus to do the following for the Master and Client timeout values: + // (1) Read the current timeout value and check that it is set to the default + // (2) Set it to different value + // (3) Read it back to check success + // (4) Return to the original value, and readback to confirm + CLIENT_PRINT(("DChannelIicSlaveClient::DoControl invoking StaticExtension (ECtrlIoBlockNotification) \n")); + r = StaticExtension((TUint)a1, (TUint)ctrlIoVal, NULL, NULL); + break; + } + + default: + { + CLIENT_PRINT(("DChannelIicSlaveClient::DoControl - unrecognised value for aId=0x%x\n",aId)); + r=KErrArgument; + break; + } + + } + return r; + } + +TInt DChannelIicSlaveClient::DoRequest(TInt aId, TRequestStatus* aStatus, TAny* a1, TAny* a2) + { + CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest invoked with aId=0x%x, aStatus=0x%x, a1=0x%x, a2=0x%x\n", aId,aStatus,a1,a2)); + + TInt r=KErrNone; + switch(aId) + { + case(RBusDevIicClient::ECaptureChanAsync): + { + // a1 is a pointer to the TDes8* aConfigHdr + // a2 is a pointer to TInt* parms[2], where: + // parms[0]=(TInt*)aBusId; + // parms[1]=&aChannelId; + // + TInt* parms[2]; + r=Kern::ThreadRawRead(iClient,a2,&(parms[0]),2*sizeof(TInt*)); + if(r!=KErrNone) + break; // Can't proceed if can't access request parameters + // + TInt hdrSize = Kern::ThreadGetDesLength(iClient,a1); + CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest hdrSize = 0x%x\n",hdrSize)); + if (hdrSize<=0) + { + CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest ERROR, hdrSize is invalid\n")); + return KErrArgument; + } + if((iConfigHdr = HBuf8::New(hdrSize)) == NULL) + return KErrNoMemory; + if((r = Kern::ThreadDesRead(iClient,a1,*iConfigHdr,0))!=KErrNone) + { + delete iConfigHdr; + return r; + } + iStatus=aStatus; + // Store the address of the user-side variable to update with the ChannelId + iClientChanId=parms[1]; + // Invoke the IIC API + CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest invoking (asynchronous) CaptureChannel\n")); + r = CaptureChannel((TInt)(parms[0]), iConfigHdr, iNotif, iChannelId, ETrue); + if(r != KErrNone) + delete iConfigHdr; + CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest (asynchronous) CaptureChannel returned %d\n",r)); + break; + } + + case(RBusDevIicClient::ECtrlIoOvUndRunRxTx): + { + iBusId = (TInt)a1; + iChannelId = (TInt)a2; + iStatus=aStatus; + CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest status = 0x%x, busId = 0x%x, chanId =0x%x\n",iStatus,iBusId,iChannelId)); + + // This test is state-machine driven. It is instigated from this point, then subsequent steps + // are handled in the callback funciton SlaveClientCallbackFunc + // + // Check we in the appropriate state to start + if(iTestOverUnderState == EStartState) + { + // Re-use the previously-provided buffers. Just request the initial notification triggers, + // the simulate the first event (RxOverrun). + r = SetNotificationTrigger(iChannelId, (ERxAllBytes | ERxOverrun | ETxAllBytes | ETxUnderrun)); + if(r != KErrNone) + { + CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest SetNotificationTrigger returned %d\n",r)); + } + else + { + // Trigger now set, so simulate the required event + iExpectedTrigger = ERxAllBytes | ERxOverrun | ETxAllBytes | ETxUnderrun; + iFullDuplexReq |= (ERxAllBytes|ETxAllBytes); + iTestOverUnderState = ERxOverrun_1; // Prepare for callback + // The requested number of words when the buffer was registered was 8, so simulate 10 + // to provoke an RxOverrun event. + TInt numWords=10; + // To support testing, any values of aId for StaticExtension must be shifted left one place + // and for a Slave, the two msbs must be zero + TInt ctrlIoVal = RBusDevIicClient::ECtrlIoRxWords; + ctrlIoVal = (ctrlIoVal << 1) & 0x3FFFFFFF; + r = StaticExtension(iBusId, ctrlIoVal, (TAny*)iChannelId, (TAny*)numWords); + } + } + else + { + CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest ECtrlIoOvUndRunRxTx, iTestOverUnderState = 0x%x\n",iTestOverUnderState)); + r=KErrGeneral; + } + break; + } + + default: + { + CLIENT_PRINT(("DChannelIicSlaveClient::DoRequest - unrecognised value for aId=0x%x\n",aId)); + r=KErrArgument; + break; + } + } + return r; + } +