--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/dmav2/d_dma2.cpp Mon Jan 18 21:31:10 2010 +0200
@@ -0,0 +1,1351 @@
+// Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "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:
+// Test driver for DMA V2 framework
+//
+//
+
+#include <kernel/kern_priv.h>
+#include <drivers/dma.h>
+#include "d_dma2.h"
+
+_LIT(KClientPanicCat, "D_DMA2");
+_LIT(KDFCThreadName,"D_DMA_DFC_THREAD");
+_LIT(KIsrCbDfcThreadName,"D_DMA_IsrCb_thread");
+const TInt KDFCThreadPriority=26;
+
+class TStopwatch
+ {
+public:
+ TStopwatch()
+ :iStart(0), iStop(0)
+ {}
+
+ void Start()
+ {iStart = NKern::FastCounter();}
+
+ void Stop()
+ {
+ iStop = NKern::FastCounter();
+
+ __KTRACE_OPT(KDMA, Kern::Printf(">TStopwatch::Stop FastCounter ticks: iStart=0x%lx iStop=0x%lx", iStart, iStop));
+ }
+
+ TUint64 ReadMicroSecs() const
+ {
+#ifndef __SMP__
+ TUint64 diff = 0;
+ if(iStart > iStop)
+ {
+ diff = (KMaxTUint64 - iStart) + iStop;
+ }
+ else
+ {
+ diff = iStop - iStart;
+ }
+ return FastCountToMicroSecs(diff);
+#else
+ //TODO On SMP it is possible for the value returned from
+ //NKern::FastCounter to depend on the current CPU (ie.
+ //NaviEngine)
+ //
+ //One solution would be to tie DFC's and ISR's to the same
+ //core as the client, but this would reduce the usefulness of
+ //SMP testing.
+ return 0;
+#endif
+ }
+private:
+
+ TUint64 FastCountToMicroSecs(TUint64 aCount) const
+ {
+ const TUint64 countsPerS = NKern::FastCounterFrequency();
+
+ TUint64 timeuS = (aCount*1000000)/countsPerS;
+ __KTRACE_OPT(KDMA, Kern::Printf(">TStopwatch::FastCountToMicroSecs FastCounter ticks: aCount=0x%lx countsPerS=0x%lx time=0x%lx", aCount, countsPerS, timeuS));
+ return timeuS;
+ }
+
+ TUint64 iStart;
+ TUint64 iStop;
+ };
+
+//////////////////////////////////////////////////////////////////////////////
+
+class DClientDmaRequest;
+/**
+Driver channel. Only accessible by a single client thread
+*/
+class DDmaTestSession : public DLogicalChannelBase
+ {
+public:
+ DDmaTestSession();
+ virtual ~DDmaTestSession();
+protected:
+ // from DLogicalChannelBase
+ virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
+ virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2);
+ virtual TInt RequestUserHandle(DThread* aThread, TOwnerType aType);
+private:
+ TInt DoGetInfo(TAny* aInfo);
+
+ TInt OpenDmaChannel(TUint aPslCookie, TUint& aDriverCookie);
+ TInt CloseDmaChannelByCookie(TUint aDriverCookie);
+ TInt PauseDmaChannelByCookie(TUint aDriverCookie);
+ TInt ResumeDmaChannelByCookie(TUint aDriverCookie);
+ TInt GetChannelCapsByCookie(TUint aDriverCookie, SDmacCaps& aChannelCaps);
+ TInt GetChannelCapsByCookie(TUint aDriverCookie, TDmacTestCaps& aChannelCaps);
+ TInt CancelAllByCookie(TUint aDriverCookie);
+ TInt IsrRedoRequestByCookie(TUint aDriverCookie,TUint32 aSrcAddr,TUint32 aDstAddr,TInt aTransferCount,TUint32 aPslRequestInfo,TBool aIsrCb);
+ TInt IsQueueEmptyByCookie(TUint aDriverCookie, TBool& aQueueEmpty);
+ TInt ChannelIsOpenedByCookie(TUint aDriverCookie, TBool& aChannelOpen);
+ void CloseDmaChannelByIndex(TInt aIndex);
+ void CancelAllByIndex(TInt aIndex);
+ TInt PauseDmaChannelByIndex(TInt aIndex);
+ TInt ResumeDmaChannelByIndex(TInt aIndex);
+ TInt IsrRedoRequestByIndex(TInt aIndex,TUint32 aSrcAddr,TUint32 aDstAddr,TInt aTransferCount,TUint32 aPslRequestInfo,TBool aIsrCb);
+ TInt CreateSharedChunk();
+ TUint OpenSharedChunkHandle();
+
+ /**
+ Creates a new kernel-side DMA request object, associated with a previously
+ opened channel
+
+ @param aChannelCookie - A channel cookie as returned by OpenDmaChannel
+ @param aRequestCookie - On success will be a cookie by which the dma request can be referred to
+ @param aNewCallback - If true, then a new style DMA callback will be used
+ */
+ TInt CreateDmaRequest(TUint aChannelCookie, TUint& aRequestCookie, TBool aNewCallback = EFalse, TInt aMaxFragmentSizeBytes=0);
+
+ //TODO what happens if a client closes a channel that
+ //it still has dma requests associated with?
+
+ /**
+ Destroys a previously created dma request object
+ */
+ TInt DestroyDmaRequestByCookie(TUint aRequestCookie);
+
+ void DestroyDmaRequestByIndex(TInt aIndex);
+
+
+ TInt CookieToChannelIndex(TUint aDriverCookie) const;
+ TInt CookieToRequestIndex(TUint aRequestCookie) const;
+
+ void FixupTransferArgs(TDmaTransferArgs& aTransferArgs) const;
+ TInt FragmentRequest(TUint aRequestCookie, const TDmaTransferArgs& aTransferArgs, TBool aLegacy=ETrue);
+
+ TInt QueueRequest(TUint aRequestCookie, TRequestStatus* aStatus, TCallbackRecord* aRecord, TUint64* aDurationMicroSecs);
+ DClientDmaRequest* RequestFromCookie(TUint aRequestCookie) const;
+ TInt RequestFragmentCount(TUint aRequestCookie);
+
+ TDmaV2TestInfo ConvertTestInfo(const TDmaTestInfo& aOldInfo) const;
+private:
+ DThread* iClient;
+ TDynamicDfcQue* iDfcQ;
+ TDynamicDfcQue* iIsrCallbackDfcQ; // Will be used by requests which complete with an ISR callback
+ static const TInt KMaxChunkSize = 8 * KMega;
+ TLinAddr iChunkBase;
+ DChunk* iChunk;
+
+ RPointerArray<TDmaChannel> iChannels;
+ RPointerArray<DClientDmaRequest> iClientDmaReqs;
+ };
+
+
+/**
+Allows a TClientRequest to be associated with a DDmaRequest
+*/
+class DClientDmaRequest : public DDmaRequest
+ {
+public:
+ static DClientDmaRequest* Construct(DThread* aClient, TDfcQue* const aDfcQ, TDmaChannel& aChannel, TBool aNewStyle=EFalse, TInt aMaxTransferSize=0);
+ ~DClientDmaRequest();
+
+ TInt Queue(TRequestStatus* aRequestStatus, TCallbackRecord* aRecord, TUint64* aDurationMicroSecs);
+ void AddRequeArgs(const TIsrRequeArgsSet& aRequeArgSet);
+
+ TUint64 GetDuration()
+ {return iStopwatch.ReadMicroSecs();}
+
+protected:
+ TInt Create();
+ /** Construct with old style callback */
+ DClientDmaRequest(DThread* aClient, TDfcQue* const aDfcQ, TDmaChannel& aChannel, TInt aMaxTransferSize);
+
+ /** Construct with new style callback */
+ DClientDmaRequest(DThread* aClient, TDfcQue* const aDfcQ, TDmaChannel& aChannel, TBool aNewStyle, TInt aMaxTransferSize);
+
+private:
+ static void CallbackOldStyle(TResult aResult, TAny* aRequest);
+ static void Callback(TUint, TDmaResult, TAny*, SDmaDesHdr*);
+ static void CompleteCallback(TAny* aRequest);
+
+ void DoCallback(TUint, TDmaResult);
+ TBool RedoRequest();
+
+ //!< Used to return a TCallbackRecord and transfer time
+ TClientDataRequest2<TCallbackRecord, TUint64>* iClientDataRequest;
+
+ DThread* const iClient;
+ TDfcQue* const iDfcQ; //!< Use the DDmaTestSession's dfc queue
+ TDfc iDfc;
+
+ TStopwatch iStopwatch;
+ TIsrRequeArgsSet iIsrRequeArgSet;
+ };
+
+DClientDmaRequest* DClientDmaRequest::Construct(DThread* aClient, TDfcQue* const aDfcQ, TDmaChannel& aChannel, TBool aNewStyle, TInt aMaxTransferSize)
+ {
+ DClientDmaRequest* dmaRequest = NULL;
+ if(aNewStyle)
+ {
+#ifdef DMA_APIV2
+ dmaRequest = new DClientDmaRequest(aClient, aDfcQ, aChannel, aNewStyle, aMaxTransferSize);
+#else
+ TEST_FAULT; // if a new style dma request was requested it should have been caught earlier
+#endif
+ }
+ else
+ {
+ dmaRequest = new DClientDmaRequest(aClient, aDfcQ, aChannel, aMaxTransferSize);
+ }
+
+ if(dmaRequest == NULL)
+ {
+ return dmaRequest;
+ }
+
+ const TInt r = dmaRequest->Create();
+ if(r != KErrNone)
+ {
+ delete dmaRequest;
+ dmaRequest = NULL;
+ }
+ return dmaRequest;
+ }
+
+DClientDmaRequest::DClientDmaRequest(DThread* aClient, TDfcQue* const aDfcQ, TDmaChannel& aChannel, TInt aMaxFragmentSize)
+ :DDmaRequest(aChannel, &CallbackOldStyle, this, aMaxFragmentSize),
+ iClientDataRequest(NULL),
+ iClient(aClient),
+ iDfcQ(aDfcQ),
+ iDfc(CompleteCallback,NULL, iDfcQ, KMaxDfcPriority)
+ {
+ }
+#ifdef DMA_APIV2
+DClientDmaRequest::DClientDmaRequest(DThread* aClient, TDfcQue* const aDfcQ, TDmaChannel& aChannel, TBool /*aNewStyle*/, TInt aMaxFragmentSize)
+ :DDmaRequest(aChannel, &Callback, this, aMaxFragmentSize),
+ iClientDataRequest(NULL),
+ iClient(aClient),
+ iDfcQ(aDfcQ),
+ iDfc(CompleteCallback,NULL, iDfcQ, KMaxDfcPriority)
+ {
+ }
+#endif
+
+TInt DClientDmaRequest::Create()
+ {
+ return Kern::CreateClientDataRequest2(iClientDataRequest);
+ }
+
+DClientDmaRequest::~DClientDmaRequest()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf(">DClientDmaRequest::~DClientDmaRequest"));
+ if(iClientDataRequest)
+ {
+ Kern::DestroyClientRequest(iClientDataRequest);
+ }
+ }
+
+/**
+Queue the DClientDmaRequest.
+
+@param aRequestStatus Pointer to the client's request status
+@param aRecord Pointer to the user's TCallbackRecord, may be null
+@return
+ -KErrInUse The client request is in use
+ -KErrNone success
+*/
+TInt DClientDmaRequest::Queue(TRequestStatus* aRequestStatus, TCallbackRecord* aRecord, TUint64* aDurationMicroSecs)
+ {
+ __NK_ASSERT_ALWAYS(aRecord);
+ __NK_ASSERT_ALWAYS(aDurationMicroSecs);
+
+ //erase results from last transfer
+ iClientDataRequest->Data1().Reset();
+ iClientDataRequest->SetDestPtr1(aRecord);
+
+ iClientDataRequest->SetDestPtr2(aDurationMicroSecs);
+
+
+ TInt r = iClientDataRequest->SetStatus(aRequestStatus);
+ if(r != KErrNone)
+ {
+ return r;
+ }
+
+ iStopwatch.Start();
+#ifdef DMA_APIV2
+ r = DDmaRequest::Queue();
+#else
+ // old version of queue did not return an error code
+ DDmaRequest::Queue();
+ r = KErrNone;
+#endif
+
+ return r;
+ }
+
+void DClientDmaRequest::AddRequeArgs(const TIsrRequeArgsSet& aRequeArgSet)
+ {
+ iIsrRequeArgSet = aRequeArgSet;
+ }
+
+/**
+If a transfer complete callback in ISR context s received this will be
+called to redo the request with the first entry in the array
+
+@return ETrue If the redo was successful - indicates that another callback is comming
+*/
+TBool DClientDmaRequest::RedoRequest()
+ {
+ TIsrRequeArgs args = iIsrRequeArgSet.GetArgs();
+ const TInt r = args.Call(iChannel);
+ TCallbackRecord& record = iClientDataRequest->Data1();
+ record.IsrRedoResult(r);
+ return (r == KErrNone);
+ }
+
+
+/**
+Calls TDmaChannel::IsrRedoRequest on aChannel
+with this object's parameters
+*/
+TInt TIsrRequeArgs::Call(TDmaChannel& aChannel)
+ {
+#ifdef DMA_APIV2
+ return aChannel.IsrRedoRequest(iSrcAddr, iDstAddr, iTransferCount, iPslRequestInfo, iIsrCb);
+#else
+ TEST_FAULT;
+ return KErrNotSupported;
+#endif
+ }
+
+/**
+Check that both source and destination of ISR reque args will
+lie within the range specified by aStart and aSize.
+
+@param aStart The linear base address of the region
+@param aSize The size of the region
+*/
+TBool TIsrRequeArgs::CheckRange(TLinAddr aStart, TUint aSize) const
+ {
+ TUint physStart = Epoc::LinearToPhysical(aStart);
+ TEST_ASSERT(physStart != KPhysAddrInvalid);
+
+ TAddrRange chunk(physStart, aSize);
+ TBool sourceOk = (iSrcAddr == KPhysAddrInvalid) ? ETrue : chunk.Contains(SourceRange());
+
+ TBool destOk = (iDstAddr == KPhysAddrInvalid) ? ETrue : chunk.Contains(DestRange());
+
+ return sourceOk && destOk;
+ }
+
+TBool TIsrRequeArgsSet::CheckRange(TLinAddr aAddr, TUint aSize) const
+ {
+ for(TInt i=0; i<iCount; i++)
+ {
+ if(!iRequeArgs[i].CheckRange(aAddr, aSize))
+ return EFalse;
+ }
+ return ETrue;
+ }
+
+/**
+Translate an old style dma callback to a new-style one
+*/
+void DClientDmaRequest::CallbackOldStyle(TResult aResult, TAny* aArg)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf(">DClientDmaRequest::CallBackOldStyle: TResult result=%d", aResult));
+ TEST_ASSERT(aResult != EBadResult);
+ //translate result code
+ const TDmaResult result = (aResult == EOk) ? EDmaResultOK : EDmaResultError;
+
+ //call the new-style callback
+ Callback(EDmaCallbackRequestCompletion, result, aArg, NULL);
+ }
+
+
+/**
+The new style callback called by the DMA framework
+may be called in either thread or ISR context
+*/
+void DClientDmaRequest::Callback(TUint aCallbackType, TDmaResult aResult, TAny* aArg, SDmaDesHdr* aHdr)
+ {
+ const TInt context = NKern::CurrentContext();
+ __KTRACE_OPT(KDMA, Kern::Printf(">DClientDmaRequest::CallBack: TDmaResult result = %d, NKern::TContext context = %d", aResult, context));
+
+ DClientDmaRequest& self = *reinterpret_cast<DClientDmaRequest*>(aArg);
+ self.DoCallback(aCallbackType, aResult);
+
+ // decide if callback is complete
+ const TBool transferComplete = aCallbackType & EDmaCallbackRequestCompletion;
+ if(!transferComplete)
+ {
+ return;
+ }
+
+ // If there are reque args then redo this request
+ // another callback would then be expected.
+ // Requests can only be re-queued in ISR context, but we
+ // do not check that here as it is up to the client to get
+ // it right - also, we want to test that the PIL catches this
+ // error
+ if(!self.iIsrRequeArgSet.IsEmpty())
+ {
+ // If redo call was succesful, return and wait for next call back
+ if(self.RedoRequest())
+ return;
+ }
+
+ switch(context)
+ {
+ case NKern::EThread:
+ {
+ CompleteCallback(aArg);
+ break;
+ }
+ case NKern::EInterrupt:
+ {
+ self.iDfc.iPtr = aArg;
+ self.iDfc.Add();
+ break;
+ }
+ case NKern::EIDFC: //fall-through
+ case NKern::EEscaped:
+ default:
+ TEST_FAULT;
+ }
+ }
+
+/**
+Log results of callback. May be called in either thread or ISR context
+*/
+void DClientDmaRequest::DoCallback(TUint aCallbackType, TDmaResult aResult)
+ {
+ iStopwatch.Stop(); //sucessive calls will simply over write the stop time
+
+ // This will always be done whether the client requested a
+ // callback record or not
+ TCallbackRecord& record = iClientDataRequest->Data1();
+ record.ProcessCallback(aCallbackType, aResult);
+ }
+
+/**
+This function may either be called directly or queued as a DFC
+*/
+void DClientDmaRequest::CompleteCallback(TAny* aArg)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf(">DClientDmaRequest::CompleteCallBack thread %O", &Kern::CurrentThread()));
+ __ASSERT_NOT_ISR;
+
+ DClientDmaRequest& self = *reinterpret_cast<DClientDmaRequest*>(aArg);
+
+ self.iClientDataRequest->Data2() = self.iStopwatch.ReadMicroSecs();
+
+ //Assert that we called SetRequestStatus on this object before
+ //queueing
+ __NK_ASSERT_DEBUG(self.iClientDataRequest->IsReady());
+
+ // This is an inelegant, temporary, solution to the following problem:
+ //
+ // If a dma request completes with an ISR callback the test
+ // framework will queue this function as a DFC which
+ // will then signal the user-side client. As a consequence of
+ // this the user side client may then decide to destroy this
+ // request. However, untill the DMA framework's DFC has run
+ // and called OnDeque() on this request, it is still considered as
+ // queued. Since it is possible that this DFC could run
+ // before the DMA fw's DFC, this request could get destroyed while
+ // it is stil queued, triggering a PIL assertion.
+ //
+ // The real fix is likely be for the PIL to call the callback
+ // twice, but with different arguments, once to annonunce the
+ // ISR and again to announce the dequeue.
+ //
+ // Here we poll and wait for this request to be dequeued. Note,
+ // this DFC is currently run on a separate DFC queue, otherwise
+ // it could get deadlocked. An alternative to polling would be
+ // to use DCondVar, but that would require PIL modification
+
+ if(NKern::CurrentThread() == self.iDfcQ->iThread)
+ {
+ // Only need to poll if we aren't on the channel's DFC queue
+ for(;;)
+ {
+ // once the request has been unqueued it
+ // can only be queued again by the client
+ const TBool queued = __e32_atomic_load_acq32(&self.iQueued);
+ if(!queued)
+ break;
+ __KTRACE_OPT(KDMA, Kern::Printf("Waiting for requeuest to be dequeued"));
+ NKern::Sleep(10);
+ }
+ }
+ else
+ {
+ // If we are on the channel's DFCQ we should be dequeued
+ // already
+ __NK_ASSERT_DEBUG(!__e32_atomic_load_acq32(&self.iQueued));
+ }
+
+ // We can always complete with KErrNone, the actual DMA result is
+ // logged in the TCallbackRecord
+ Kern::QueueRequestComplete(self.iClient, self.iClientDataRequest, KErrNone);
+ }
+
+const TInt DDmaTestSession::KMaxChunkSize;
+
+TInt DDmaTestSession::RequestUserHandle(DThread* aThread, TOwnerType aType)
+ {
+ if (aType!=EOwnerThread || aThread!=iClient)
+ return KErrAccessDenied;
+ return KErrNone;
+ }
+
+DDmaTestSession::DDmaTestSession()
+ : iClient(NULL), iDfcQ(NULL), iIsrCallbackDfcQ(NULL), iChunkBase(0), iChunk(NULL)
+ {}
+
+// called in thread critical section
+TInt DDmaTestSession::DoCreate(TInt /*aUnit*/, const TDesC8* aInfo, const TVersion& /*aVer*/)
+ {
+ __NK_ASSERT_ALWAYS(iDfcQ == NULL);
+ __NK_ASSERT_ALWAYS(iIsrCallbackDfcQ == NULL);
+
+ TInt r = Kern::DynamicDfcQCreate(iDfcQ, KDFCThreadPriority, KDFCThreadName);
+ if (r != KErrNone)
+ return r;
+ NKern::ThreadSetCpuAffinity((NThread*)(iDfcQ->iThread), KCpuAffinityAny);
+
+ r = Kern::DynamicDfcQCreate(iIsrCallbackDfcQ, KDFCThreadPriority, KIsrCbDfcThreadName);
+ if (r != KErrNone)
+ return r;
+ NKern::ThreadSetCpuAffinity((NThread*)(iIsrCallbackDfcQ->iThread), KCpuAffinityAny);
+
+ iClient = &Kern::CurrentThread();
+
+ r = CreateSharedChunk();
+ return r;
+ }
+
+DDmaTestSession::~DDmaTestSession()
+ {
+ //Destroy requests before channels
+ //or we will trigger an assertion
+ while(iClientDmaReqs.Count())
+ {
+ DestroyDmaRequestByIndex(0);
+ }
+ iClientDmaReqs.Close();
+
+ while(iChannels.Count())
+ {
+ CloseDmaChannelByIndex(0);
+ }
+ iChannels.Close();
+
+
+ if (iDfcQ)
+ {
+ iDfcQ->Destroy();
+ }
+
+ if (iIsrCallbackDfcQ)
+ {
+ iIsrCallbackDfcQ->Destroy();
+ }
+
+ if(iChunk)
+ {
+ Kern::ChunkClose(iChunk);
+ iChunk = NULL;
+ }
+ }
+
+TInt DDmaTestSession::Request(TInt aFunction, TAny* a1, TAny* a2)
+ {
+ __NK_ASSERT_DEBUG(&Kern::CurrentThread() == iClient);
+
+ switch (aFunction)
+ {
+ case RDmaSession::EOpenChannel:
+ {
+ TUint pslCookie = (TUint)a1;
+ TUint driverCookie = 0;
+ TInt r = OpenDmaChannel(pslCookie, driverCookie);
+ umemput32(a2, &driverCookie, sizeof(TAny*));
+ return r;
+ }
+ case RDmaSession::ECloseChannel:
+ {
+ TUint driverCookie = reinterpret_cast<TUint>(a1);
+ TInt r = CloseDmaChannelByCookie(driverCookie);
+ return r;
+ }
+ case RDmaSession::EChannelCaps:
+ {
+ TUint driverCookie = reinterpret_cast<TUint>(a1);
+ TPckgBuf<TDmacTestCaps> capsBuf;
+ TInt r = GetChannelCapsByCookie(driverCookie, capsBuf());
+ Kern::KUDesPut(*reinterpret_cast<TDes8*>(a2), capsBuf);
+ return r;
+ }
+ case RDmaSession::EPauseChannel:
+ {
+ TUint driverCookie = reinterpret_cast<TUint>(a1);
+ TInt r = PauseDmaChannelByCookie(driverCookie);
+ return r;
+ }
+ case RDmaSession::EResumeChannel:
+ {
+ TUint driverCookie = reinterpret_cast<TUint>(a1);
+ TInt r = ResumeDmaChannelByCookie(driverCookie);
+ return r;
+ }
+ case RDmaSession::EFragmentCount:
+ {
+ TUint requestCookie = reinterpret_cast<TUint>(a1);
+ TInt r = RequestFragmentCount(requestCookie);
+ return r;
+ }
+ case RDmaSession::ERequestOpen:
+ {
+ RDmaSession::TRequestCreateArgs createArgs(0, EFalse, 0);
+ TPckg<RDmaSession::TRequestCreateArgs> package(createArgs);
+ Kern::KUDesGet(package, *reinterpret_cast<TDes8*>(a1));
+
+ const TUint channelCookie = createArgs.iChannelCookie;
+ TUint requestCookie = 0;
+
+ TInt r = CreateDmaRequest(channelCookie, requestCookie, createArgs.iNewStyle, createArgs.iMaxFragmentSize);
+
+ umemput32(a2, &requestCookie, sizeof(TAny*));
+ return r;
+ }
+ case RDmaSession::ERequestClose:
+ {
+ const TUint requestCookie = reinterpret_cast<TUint>(a1);
+ return DestroyDmaRequestByCookie(requestCookie);
+ }
+ case RDmaSession::EFragmentLegacy:
+ case RDmaSession::EFragment:
+ {
+ TPckgBuf<RDmaSession::TFragmentArgs> argsBuff;
+ Kern::KUDesGet(argsBuff, *reinterpret_cast<TDes8*>(a1));
+ const TUint requestCookie = argsBuff().iRequestCookie;
+
+ //must remove constness as we actually need to
+ //convert the src and dst offsets to addresses
+ TDmaTransferArgs& transferArgs = const_cast<TDmaTransferArgs&>(argsBuff().iTransferArgs);
+
+ //convert address offsets in to kernel virtual addresses
+ FixupTransferArgs(transferArgs);
+
+ TEST_ASSERT((TAddressParms(transferArgs).CheckRange(iChunkBase, iChunk->Size())));
+
+ TInt r = KErrGeneral;
+
+ TStopwatch clock;
+ clock.Start();
+ switch (aFunction)
+ {
+ case RDmaSession::EFragmentLegacy:
+ r = FragmentRequest(requestCookie, transferArgs, ETrue); break;
+ case RDmaSession::EFragment:
+ r = FragmentRequest(requestCookie, transferArgs, EFalse); break;
+ default:
+ TEST_FAULT;
+ }
+ clock.Stop();
+
+ const TUint64 time = clock.ReadMicroSecs();
+
+ TUint64* const timePtr = argsBuff().iDurationMicroSecs;
+ if(timePtr)
+ {
+ umemput(timePtr, &time, sizeof(time));
+ }
+ return r;
+ }
+ case RDmaSession::EQueueRequest:
+ {
+ TPckgBuf<RDmaSession::TQueueArgs> argsBuff;
+ Kern::KUDesGet(argsBuff, *reinterpret_cast<TDes8*>(a1));
+
+ //this is an Asynchronous request
+ const TUint requestCookie = argsBuff().iRequestCookie;
+ TRequestStatus* requestStatus = argsBuff().iStatus;
+ TCallbackRecord* callbackRec = argsBuff().iCallbackRecord;
+ TUint64* duration = argsBuff().iDurationMicroSecs;
+
+ TInt r = QueueRequest(requestCookie, requestStatus, callbackRec, duration);
+ if(r != KErrNone)
+ {
+ Kern::RequestComplete(requestStatus, r);
+ }
+ return r;
+ }
+ case RDmaSession::EQueueRequestWithReque:
+ {
+ //TODO can common code with EQueueRequest be extracted?
+ TPckgBuf<RDmaSession::TQueueArgsWithReque> argsBuff;
+ Kern::KUDesGet(argsBuff, *reinterpret_cast<TDes8*>(a1));
+
+ //this is an Asynchronous request
+ const TUint requestCookie = argsBuff().iRequestCookie;
+ TRequestStatus* requestStatus = argsBuff().iStatus;
+ TCallbackRecord* callbackRec = argsBuff().iCallbackRecord;
+ TUint64* duration = argsBuff().iDurationMicroSecs;
+
+ TInt r = KErrNotFound;
+
+ DClientDmaRequest* const request = RequestFromCookie(requestCookie);
+ if(request != NULL)
+ {
+ argsBuff().iRequeSet.Fixup(iChunkBase);
+ //TODO reque args must be substituted in order to
+ //check the range. The original transfer args are not
+ //available when queue is called, they could
+ //however be stored within DClientDmaRequest
+ //TEST_ASSERT((argsBuff().iRequeSet.CheckRange(iChunkBase, iChunk->Size())));
+ request->AddRequeArgs(argsBuff().iRequeSet);
+
+ r = QueueRequest(requestCookie, requestStatus, callbackRec, duration);
+ }
+
+ if(r != KErrNone)
+ {
+ Kern::RequestComplete(requestStatus, r);
+ }
+ return r;
+ }
+ case RDmaSession::EIsrRedoRequest:
+ {
+ TPckgBuf<RDmaSession::TIsrRedoReqArgs> argsBuff;
+ Kern::KUDesGet(argsBuff, *reinterpret_cast<TDes8*>(a1));
+
+ const TUint driverCookie = argsBuff().iDriverCookie;
+ const TUint32 srcAddr = argsBuff().iSrcAddr;
+ const TUint32 dstAddr = argsBuff().iDstAddr;
+ const TInt transferCount = argsBuff().iTransferCount;
+ const TUint32 pslRequestInfo = argsBuff().iPslRequestInfo;
+ const TBool isrCb = argsBuff().iIsrCb;
+
+ TInt r = IsrRedoRequestByCookie(driverCookie,srcAddr,dstAddr,transferCount,pslRequestInfo,isrCb);
+ return r;
+ }
+ case RDmaSession::EIsOpened:
+ {
+ TUint driverCookie = (TUint)a1;
+ TBool channelOpen = EFalse;;
+ TInt r = ChannelIsOpenedByCookie(driverCookie,channelOpen);
+ umemput32(a2, &channelOpen, sizeof(TAny*));
+ return r;
+ }
+ case RDmaSession::EIsQueueEmpty:
+ {
+ TUint driverCookie = (TUint)a1;
+ TBool queueEmpty = EFalse;;
+ TInt r = IsQueueEmptyByCookie(driverCookie,queueEmpty);
+ umemput32(a2, &queueEmpty, sizeof(TAny*));
+ return r;
+ }
+ case RDmaSession::ECancelAllChannel:
+ {
+ TUint driverCookie = reinterpret_cast<TUint>(a1);
+ TInt r = CancelAllByCookie(driverCookie);
+ return r;
+ }
+ case RDmaSession::EOpenSharedChunk:
+ {
+ return OpenSharedChunkHandle();
+ }
+ case RDmaSession::EGetTestInfo:
+ {
+#ifdef DMA_APIV2
+ TPckgC<TDmaV2TestInfo> package(DmaTestInfoV2());
+#else
+ TPckgC<TDmaV2TestInfo> package(ConvertTestInfo(DmaTestInfo()));
+#endif
+ Kern::KUDesPut(*reinterpret_cast<TDes8*>(a1), package);
+ return KErrNone;
+ }
+ default:
+ Kern::PanicCurrentThread(KClientPanicCat, __LINE__);
+ return KErrGeneral;
+ }
+ }
+
+TInt DDmaTestSession::OpenDmaChannel(TUint aPslCookie, TUint& aDriverCookie )
+ {
+ TDmaChannel::SCreateInfo info;
+ info.iCookie = aPslCookie;
+ info.iDfcQ = iDfcQ;
+ info.iDfcPriority = 3;
+ info.iDesCount = 128;
+
+ TDmaChannel* channel = NULL;
+
+ //cs so thread can't be killed between
+ //opening channel and adding to array
+ NKern::ThreadEnterCS();
+ TInt r = TDmaChannel::Open(info, channel);
+ if(KErrNone == r)
+ {
+ __NK_ASSERT_ALWAYS(channel);
+
+ __KTRACE_OPT(KDMA, Kern::Printf("OpenDmaChannel: channel@ 0x%08x", channel));
+
+
+ TInt err = iChannels.Append(channel);
+ if(KErrNone == err)
+ {
+ aDriverCookie = reinterpret_cast<TUint>(channel);
+ }
+ else
+ {
+ channel->Close();
+ r = KErrNoMemory;
+ }
+ }
+ NKern::ThreadLeaveCS();
+
+ return r;
+ }
+
+TInt DDmaTestSession::CookieToChannelIndex(TUint aDriverCookie) const
+ {
+ const TInt r = iChannels.Find(reinterpret_cast<TDmaChannel*>(aDriverCookie));
+
+ if(r < 0)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("CookieToChannelIndex: cookie 0x%08x not found!", aDriverCookie));
+ }
+ return r;
+ }
+
+TInt DDmaTestSession::CookieToRequestIndex(TUint aRequestCookie) const
+ {
+ const TInt r = iClientDmaReqs.Find(reinterpret_cast<DClientDmaRequest*>(aRequestCookie));
+
+ if(r < 0)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("CookieToRequestIndex: cookie 0x%08x not found!", aRequestCookie));
+ }
+ return r;
+ }
+
+void DDmaTestSession::CloseDmaChannelByIndex(TInt aIndex)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("CloseDmaChannelByIndex: %d", aIndex));
+ __NK_ASSERT_DEBUG(aIndex < iChannels.Count());
+ // cs so client thread can't be killed between removing channel from
+ // array and closing it.
+ NKern::ThreadEnterCS();
+ TDmaChannel* channel = iChannels[aIndex];
+ iChannels.Remove(aIndex);
+ channel->Close();
+ NKern::ThreadLeaveCS();
+ }
+
+TInt DDmaTestSession::CloseDmaChannelByCookie(TUint aDriverCookie)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("CloseDmaChannelByCookie: 0x%08x", aDriverCookie));
+ const TInt index = CookieToChannelIndex(aDriverCookie);
+
+ if(index >= 0)
+ {
+ CloseDmaChannelByIndex(index);
+ return KErrNone;
+ }
+ else
+ {
+ return KErrNotFound;
+ }
+ }
+
+TInt DDmaTestSession::CancelAllByCookie(TUint aDriverCookie)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("CancelAllByCookie: 0x%08x", aDriverCookie));
+ const TInt index = CookieToChannelIndex(aDriverCookie);
+
+ if(index >= 0)
+ {
+ CancelAllByIndex(index);
+ return KErrNone;
+ }
+ else
+ {
+ return KErrNotFound;
+ }
+ }
+
+void DDmaTestSession::CancelAllByIndex(TInt aIndex)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("CancelAllByIndex: %d", aIndex));
+ __NK_ASSERT_DEBUG(aIndex < iChannels.Count());
+
+ TDmaChannel* channel = iChannels[aIndex];
+ iChannels.Remove(aIndex);
+ channel->CancelAll();
+ }
+
+TInt DDmaTestSession::PauseDmaChannelByIndex(TInt aIndex)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("PauseDmaChannelByIndex: %d", aIndex));
+ __NK_ASSERT_DEBUG(aIndex < iChannels.Count());
+
+#ifdef DMA_APIV2
+ TDmaChannel* channel = iChannels[aIndex];
+ return channel->Pause();
+#else
+ return KErrNotSupported;
+#endif
+ }
+
+TInt DDmaTestSession::PauseDmaChannelByCookie(TUint aDriverCookie)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("PauseDmaChannelByCookie: 0x%08x", aDriverCookie));
+ const TInt index = CookieToChannelIndex(aDriverCookie);
+
+ if(index >= 0)
+ {
+ TInt r = PauseDmaChannelByIndex(index);
+ return r;
+ }
+ else
+ {
+ return KErrNotFound;
+ }
+ }
+
+TInt DDmaTestSession::ResumeDmaChannelByIndex(TInt aIndex)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("ResumeDmaChannelByIndex: %d", aIndex));
+ __NK_ASSERT_DEBUG(aIndex < iChannels.Count());
+
+#ifdef DMA_APIV2
+ TDmaChannel* channel = iChannels[aIndex];
+ return channel->Resume();
+#else
+ return KErrNotSupported;
+#endif
+ }
+
+TInt DDmaTestSession::ResumeDmaChannelByCookie(TUint aDriverCookie)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("ResumeDmaChannelByCookie: 0x%08x", aDriverCookie));
+ const TInt index = CookieToChannelIndex(aDriverCookie);
+
+ if(index >= 0)
+ {
+ TInt r = ResumeDmaChannelByIndex(index);
+ return r;
+ }
+ else
+ {
+ return KErrNotFound;
+ }
+ }
+
+TInt DDmaTestSession::IsrRedoRequestByCookie(TUint aDriverCookie,TUint32 aSrcAddr,TUint32 aDstAddr,TInt aTransferCount,TUint32 aPslRequestInfo,TBool aIsrCb)
+{
+ __KTRACE_OPT(KDMA, Kern::Printf("IsrRedoRequestByCookie: 0x%08x", aDriverCookie));
+ const TInt index = CookieToChannelIndex(aDriverCookie);
+
+ if(index >= 0)
+ {
+ TInt r = IsrRedoRequestByIndex(index,aSrcAddr,aDstAddr,aTransferCount,aPslRequestInfo,aIsrCb);
+ return r;
+ }
+ else
+ {
+ return KErrNotFound;
+ }
+}
+
+TInt DDmaTestSession::IsrRedoRequestByIndex(TInt aIndex,TUint32 aSrcAddr,TUint32 aDstAddr,TInt aTransferCount,TUint32 aPslRequestInfo,TBool aIsrCb)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("IsrRedoRequestByIndex: %d", aIndex));
+ __NK_ASSERT_DEBUG(aIndex < iChannels.Count());
+
+#ifdef DMA_APIV2
+ TDmaChannel* channel = iChannels[aIndex];
+ return channel->IsrRedoRequest(aSrcAddr,aDstAddr,aTransferCount,aPslRequestInfo,aIsrCb);
+#else
+ return KErrNotSupported;
+#endif
+ }
+
+/**
+aChannelCaps will be set to "NULL" values
+*/
+TInt DDmaTestSession::GetChannelCapsByCookie(TUint aDriverCookie, TDmacTestCaps& aChannelCaps)
+ {
+ SDmacCaps caps = {0,}; //initialise with NULL values
+ TInt r = GetChannelCapsByCookie(aDriverCookie, caps);
+
+ if(r == KErrNotSupported)
+ {
+ //If we can not query caps it means
+ //that we are using the v1 driver
+ //we construct a empty TDmacTestCaps
+ //but with an iPILVersion of 1
+ const TDmacTestCaps nullCapsV1(caps, 1);
+ aChannelCaps = nullCapsV1;
+ r = KErrNone;
+ }
+ else if(r == KErrNone)
+ {
+ const TDmacTestCaps capsV2(caps, 2);
+ aChannelCaps = capsV2;
+ }
+
+ return r;
+ }
+
+/**
+Will return the capabilities of the DMA channel.
+Querying SDmacCaps is not possible on V1 of the DMA framework.
+In that case an error of KErrNotSupported will be returned
+*/
+TInt DDmaTestSession::GetChannelCapsByCookie(TUint aDriverCookie, SDmacCaps& aChannelCaps)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("GetChannelCapsByCookie: 0x%08x", aDriverCookie));
+ const TInt index = CookieToChannelIndex(aDriverCookie);
+ if(index >= 0)
+ {
+#ifdef DMA_APIV2
+ aChannelCaps = iChannels[index]->DmacCaps();
+ return KErrNone;
+#else
+ return KErrNotSupported;
+#endif
+ }
+ else
+ {
+ return KErrNotFound;
+ }
+ }
+
+TInt DDmaTestSession::IsQueueEmptyByCookie(TUint aDriverCookie, TBool& aQueueEmpty)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("IsQueueEmptyByCookie: 0x%08x", aDriverCookie));
+ const TInt index = CookieToChannelIndex(aDriverCookie);
+
+ if(index >= 0)
+ {
+ aQueueEmpty=iChannels[index]->IsQueueEmpty();
+ return KErrNone;
+ }
+ else
+ {
+ return KErrNotFound;
+ }
+ }
+
+TInt DDmaTestSession::ChannelIsOpenedByCookie(TUint aDriverCookie, TBool& aChannelOpen)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("ChannelIsOpenedByCookie: 0x%08x", aDriverCookie));
+ const TInt index = CookieToChannelIndex(aDriverCookie);
+
+ if(index >= 0)
+ {
+ aChannelOpen=iChannels[index]->IsOpened();
+ return KErrNone;
+ }
+ else
+ {
+ return KErrNotFound;
+ }
+ }
+
+TInt DDmaTestSession::CreateDmaRequest(TUint aChannelCookie, TUint& aRequestCookie, TBool aNewCallback, TInt aMaxFragmentSizeBytes)
+ {
+#ifndef DMA_APIV2
+ if(aNewCallback)
+ return KErrNotSupported;
+#endif
+
+ TInt channelIndex = CookieToChannelIndex(aChannelCookie);
+ if(channelIndex < 0)
+ return channelIndex;
+
+ NKern::ThreadEnterCS();
+ DClientDmaRequest* request = DClientDmaRequest::Construct(iClient, iIsrCallbackDfcQ, *iChannels[channelIndex], aNewCallback, aMaxFragmentSizeBytes);
+ if(request == NULL)
+ {
+ NKern::ThreadLeaveCS();
+ return KErrNoMemory;
+ }
+
+ TInt r = iClientDmaReqs.Append(request);
+ if(r == KErrNone)
+ {
+ aRequestCookie = reinterpret_cast<TUint>(request);
+ }
+ else
+ {
+ delete request;
+ }
+ NKern::ThreadLeaveCS();
+
+ return r;
+ }
+
+TInt DDmaTestSession::DestroyDmaRequestByCookie(TUint aRequestCookie)
+ {
+ TInt requestIndex = CookieToRequestIndex(aRequestCookie);
+ if(requestIndex < 0)
+ return requestIndex;
+
+ DestroyDmaRequestByIndex(requestIndex);
+
+ return KErrNone;
+ }
+
+void DDmaTestSession::DestroyDmaRequestByIndex(TInt aIndex)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DestroyDmaRequestByIndex: %d", aIndex));
+ __NK_ASSERT_DEBUG(aIndex < iClientDmaReqs.Count());
+ NKern::ThreadEnterCS();
+
+ DClientDmaRequest* request = iClientDmaReqs[aIndex];
+ iClientDmaReqs.Remove(aIndex);
+ delete request;
+
+ NKern::ThreadLeaveCS();
+ }
+
+TInt DDmaTestSession::CreateSharedChunk()
+ {
+ // Enter critical section so we can't die and leak the objects we are creating
+ // I.e. the TChunkCleanup and DChunk (Shared Chunk)
+ NKern::ThreadEnterCS();
+
+ // Create the chunk
+ TChunkCreateInfo info;
+ info.iType = TChunkCreateInfo::ESharedKernelSingle;
+ info.iMaxSize = KMaxChunkSize;
+ info.iMapAttr = EMapAttrFullyBlocking | EMapAttrUserRw;
+ info.iOwnsMemory = ETrue;
+ info.iDestroyedDfc = NULL;
+
+ DChunk* chunk;
+ TUint32 mapAttr;
+ TInt r = Kern::ChunkCreate(info, chunk, iChunkBase, mapAttr);
+ if(r!=KErrNone)
+ {
+ NKern::ThreadLeaveCS();
+ return r;
+ }
+
+ // Map our device's memory into the chunk (at offset 0)
+ TUint32 physicalAddr;
+ r = Kern::ChunkCommitContiguous(chunk,0,KMaxChunkSize, physicalAddr);
+ if(r!=KErrNone)
+ {
+ // Commit failed so tidy-up...
+ Kern::ChunkClose(chunk);
+ }
+ else
+ {
+ iChunk = chunk;
+ }
+
+ // Can leave critical section now that we have saved pointers to created objects
+ NKern::ThreadLeaveCS();
+
+ return r;
+ }
+
+TUint DDmaTestSession::OpenSharedChunkHandle()
+ {
+ NKern::ThreadEnterCS();
+ const TInt r = Kern::MakeHandleAndOpen(NULL, iChunk);
+ NKern::ThreadLeaveCS();
+ return r;
+ }
+
+void DDmaTestSession::FixupTransferArgs(TDmaTransferArgs& aTransferArgs) const
+ {
+ aTransferArgs.iSrcConfig.iAddr += iChunkBase;
+ aTransferArgs.iDstConfig.iAddr += iChunkBase;
+ }
+
+#ifndef DMA_APIV2
+static TInt FragmentCount(DDmaRequest* aRequest)
+ {
+ TInt count = 0;
+ for (SDmaDesHdr* pH = aRequest->iFirstHdr; pH != NULL; pH = pH->iNext)
+ count++;
+ return count;
+ }
+#endif
+
+TInt DDmaTestSession::RequestFragmentCount(TUint aRequestCookie)
+ {
+ TInt requestIndex = CookieToRequestIndex(aRequestCookie);
+ if(requestIndex < 0)
+ return requestIndex;
+#ifdef DMA_APIV2
+ TInt r = iClientDmaReqs[requestIndex]->FragmentCount();
+#else
+ TInt r = FragmentCount(iClientDmaReqs[requestIndex]);
+#endif
+
+ return r;
+ }
+
+TInt DDmaTestSession::FragmentRequest(TUint aRequestCookie, const TDmaTransferArgs& aTransferArgs, TBool aLegacy)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf(">FragmentRequest: cookie=0x%08x, legacy=%d", aRequestCookie, aLegacy));
+ TInt requestIndex = CookieToRequestIndex(aRequestCookie);
+ if(requestIndex < 0)
+ return requestIndex;
+
+ TInt r = KErrNotSupported;
+ if(aLegacy)
+ {
+ // TODO we can extract the required info from the struct to
+ // set flags
+ TUint flags = KDmaMemSrc | KDmaIncSrc | KDmaMemDest | KDmaIncDest;
+
+ const TUint src = aTransferArgs.iSrcConfig.iAddr;
+ const TUint dst = aTransferArgs.iDstConfig.iAddr;
+ r = iClientDmaReqs[requestIndex]->Fragment(src, dst, aTransferArgs.iTransferCount, flags, NULL);
+ }
+ else
+ {
+#ifdef DMA_APIV2
+ r = iClientDmaReqs[requestIndex]->Fragment(aTransferArgs);
+#else
+ r = KErrNotSupported;
+#endif
+ }
+ return r;
+ }
+
+/**
+Queue the request refered to by aRequestCookie
+
+@param aRequestCookie Client identifier for the DDmaRequest
+@param aStatus Pointer to the client's TRequestStatus
+@param aRecord Pointer to the client's TCallbackRecord
+@return
+ - KErrNotFound - aRequestCookie was invalid
+ - KErrNone - Success
+*/
+TInt DDmaTestSession::QueueRequest(TUint aRequestCookie, TRequestStatus* aStatus, TCallbackRecord* aRecord, TUint64* aDurationMicroSecs)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf(">QueueRequest: 0x%08x", aRequestCookie));
+
+ DClientDmaRequest* request = RequestFromCookie(aRequestCookie);
+ if(request == NULL)
+ return KErrNotFound;
+
+ return request->Queue(aStatus, aRecord, aDurationMicroSecs);
+ }
+
+DClientDmaRequest* DDmaTestSession::RequestFromCookie(TUint aRequestCookie) const
+ {
+ TInt requestIndex = CookieToRequestIndex(aRequestCookie);
+ if(requestIndex < 0)
+ return NULL;
+
+ return (iClientDmaReqs[requestIndex]);
+ }
+
+TDmaV2TestInfo DDmaTestSession::ConvertTestInfo(const TDmaTestInfo& aOldInfo) const
+ {
+ TDmaV2TestInfo newInfo;
+ newInfo.iMaxTransferSize = aOldInfo.iMaxTransferSize;
+ newInfo.iMemAlignMask = aOldInfo.iMemAlignMask;
+ newInfo.iMemMemPslInfo = aOldInfo.iMemMemPslInfo;
+
+ newInfo.iMaxSbChannels = aOldInfo.iMaxSbChannels;
+ for(TInt i=0; i<aOldInfo.iMaxSbChannels; i++)
+ newInfo.iSbChannels[i] = aOldInfo.iSbChannels[i];
+
+ newInfo.iMaxDbChannels = aOldInfo.iMaxDbChannels;
+ for(TInt i=0; i<aOldInfo.iMaxDbChannels; i++)
+ newInfo.iDbChannels[i] = aOldInfo.iDbChannels[i];
+
+ newInfo.iMaxSgChannels = aOldInfo.iMaxSgChannels;
+ for(TInt i=0; i<aOldInfo.iMaxSgChannels; i++)
+ newInfo.iSgChannels[i] = aOldInfo.iSgChannels[i];
+
+ //TODO will want to add initialisation for Asym channels
+ //when these are available
+
+ return newInfo;
+ }
+//////////////////////////////////////////////////////////////////////////////
+
+class DDmaTestFactory : public DLogicalDevice
+ {
+public:
+ DDmaTestFactory();
+ // from DLogicalDevice
+ virtual ~DDmaTestFactory()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf(">DDmaTestFactory::~DDmaTestFactory"));
+ }
+ virtual TInt Install();
+ virtual void GetCaps(TDes8& aDes) const;
+ virtual TInt Create(DLogicalChannelBase*& aChannel);
+ };
+
+
+DDmaTestFactory::DDmaTestFactory()
+ {
+ iVersion = TestDmaLddVersion();
+ iParseMask = KDeviceAllowUnit; // no info, no PDD
+ // iUnitsMask = 0; // Only one thing
+ }
+
+
+TInt DDmaTestFactory::Create(DLogicalChannelBase*& aChannel)
+ {
+ aChannel=new DDmaTestSession;
+ return aChannel ? KErrNone : KErrNoMemory;
+ }
+
+
+TInt DDmaTestFactory::Install()
+ {
+ return SetName(&KTestDmaLddName);
+ }
+
+
+void DDmaTestFactory::GetCaps(TDes8& /*aDes*/) const
+ {
+ }
+
+//////////////////////////////////////////////////////////////////////////////
+
+DECLARE_STANDARD_LDD()
+ {
+ return new DDmaTestFactory;
+ }