// 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 <kernel/kern_priv.h> // for DThread, TDfc
#ifdef STANDALONE_CHANNEL
#include <drivers/iic_transaction.h>
#else
#include <drivers/iic.h>
#endif
#include "../t_iic.h"
#ifdef STANDALONE_CHANNEL
#include <drivers/iic_channel.h>
#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<DIicSlaveClientChan> 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<TCapsIicSlaveClient> 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<r)
return 1;
else
return 0;
}
// global ordering object to be passed to RPointerArray InsertInOrderXXX and FindInOrder
TLinearOrder<DIicSlaveClientChan> 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; i<NUM_CHANNELS; i++)
{
CLIENT_PRINT(("\n"));
#if defined(MASTER_MODE)
if(CHANNEL_TYPE(i) == (DIicBusChannel::EMaster))
{
chan=new DSimulatedIicBusChannelMasterI2c(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(((DSimulatedIicBusChannelMasterI2c*)chan)->Create()!=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(index<iRxRegOffset)
{
currVal=*(iRxBuf+index);
if(currVal != 0)
{
CLIENT_PRINT(("DChannelIicSlaveClient::CheckDataRead, index=%d, value =0x%x, expected 0",index,currVal));
r=KErrCorrupt;
}
++index;
}
TInt8 checkVal=0x10; // first value written by simulated bus channel
TInt8 endOfData= (TInt8)(iRxRegOffset+(numWords*iRxRegGranularity));
TInt8 wordCount=0;
while(index<endOfData)
{
currVal = *(iRxBuf+index);
if(checkVal != currVal)
{
CLIENT_PRINT(("DChannelIicSlaveClient::CheckDataRead, index=%d, value =0x%x, expected 0x%x",index,currVal,checkVal));
r=KErrCorrupt;
}
if(++wordCount == iRxRegGranularity)
{
wordCount=0;
checkVal++;
}
++index;
}
while(index<KRxBufSizeInBytes)
{
currVal=*(iRxBuf+index);
if(currVal != 0)
{
CLIENT_PRINT(("DChannelIicSlaveClient::CheckDataRead, index=%d, value =0x%x, expected 0",index,currVal);)
r=KErrCorrupt;
}
++index;
}
return r;
}
TInt DChannelIicSlaveClient::CheckDataWritten()
{
TInt r=KErrNone;
// The pattern in the transmit buffer used by the simulated bus channel contains a incrementing count
// from 0 to (KTxBufSizeInBytes-1), therefore the first value to be present in the check buffer will be
// represented by the required offset.
// 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
TUint testId = (((TUint)(RBusDevIicClient::ECtrlIoTxChkBuf))<<1)&0x3FFFFFFF;
CLIENT_PRINT(("DChannelIicSlaveClient::CheckDataWritten invoking StaticExtension ECtrlIoTxChkBuf with iBusId=0x%x, testId=0x%x, iBusTxCheckBuf=0x%x\n",iBusId,testId,iBusTxCheckBuf));
r = StaticExtension(iBusId, testId, &iBusTxCheckBuf, NULL);
if(r!=KErrNone)
{
CLIENT_PRINT(("DChannelIicSlaveClient::CheckDataWritten StaticExtension ECtrlIoTxChkBuf returned %d\n",r));
return r;
}
// Check that the values in the check buffer increment for the
// required number of words, and that any remaining bytes in the check buffer are set to zero.
TInt8 firstValue = iTxRegOffset;
TInt8 currVal = 0;
TInt8 wordsWritten = 0;
wordsWritten=(iTxReqNumWords>iNumRegTxWords)?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<KRxBufSizeInBytes; ++offset)
*ptr++=(TUint8)offset;
// Store the number of words for checking in the callback
iTxReqNumWords=(TInt8)(parms[1]);
TInt tempTrigger=0;
// Set the expected result
tempTrigger |= ETxAllBytes;
if(parms[1] < iNumRegTxWords)
tempTrigger |= ETxOverrun;
if(parms[1] > 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<KRxBufSizeInBytes; ++offset)
*ptr++=(TUint8)offset;
// Store the number of words for checking in the callback
iRxReqNumWords=(TInt8)(parms[1]);
iTxReqNumWords=(TInt8)(parms[2]);
TInt tempTrigger=0;
// Set the expected result
tempTrigger |= (ERxAllBytes|ETxAllBytes);
if(parms[1] < iNumRegRxWords)
tempTrigger |= ERxUnderrun;
if(parms[1] > 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;
}