diff -r f497542af8e4 -r 538db54a451d kerneltest/e32test/dmav2/d_dma2.cpp --- /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 +#include +#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 iChannels; + RPointerArray 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* 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; iDClientDmaRequest::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(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(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(a1); + TInt r = CloseDmaChannelByCookie(driverCookie); + return r; + } + case RDmaSession::EChannelCaps: + { + TUint driverCookie = reinterpret_cast(a1); + TPckgBuf capsBuf; + TInt r = GetChannelCapsByCookie(driverCookie, capsBuf()); + Kern::KUDesPut(*reinterpret_cast(a2), capsBuf); + return r; + } + case RDmaSession::EPauseChannel: + { + TUint driverCookie = reinterpret_cast(a1); + TInt r = PauseDmaChannelByCookie(driverCookie); + return r; + } + case RDmaSession::EResumeChannel: + { + TUint driverCookie = reinterpret_cast(a1); + TInt r = ResumeDmaChannelByCookie(driverCookie); + return r; + } + case RDmaSession::EFragmentCount: + { + TUint requestCookie = reinterpret_cast(a1); + TInt r = RequestFragmentCount(requestCookie); + return r; + } + case RDmaSession::ERequestOpen: + { + RDmaSession::TRequestCreateArgs createArgs(0, EFalse, 0); + TPckg package(createArgs); + Kern::KUDesGet(package, *reinterpret_cast(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(a1); + return DestroyDmaRequestByCookie(requestCookie); + } + case RDmaSession::EFragmentLegacy: + case RDmaSession::EFragment: + { + TPckgBuf argsBuff; + Kern::KUDesGet(argsBuff, *reinterpret_cast(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(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 argsBuff; + Kern::KUDesGet(argsBuff, *reinterpret_cast(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 argsBuff; + Kern::KUDesGet(argsBuff, *reinterpret_cast(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 argsBuff; + Kern::KUDesGet(argsBuff, *reinterpret_cast(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(a1); + TInt r = CancelAllByCookie(driverCookie); + return r; + } + case RDmaSession::EOpenSharedChunk: + { + return OpenSharedChunkHandle(); + } + case RDmaSession::EGetTestInfo: + { +#ifdef DMA_APIV2 + TPckgC package(DmaTestInfoV2()); +#else + TPckgC package(ConvertTestInfo(DmaTestInfo())); +#endif + Kern::KUDesPut(*reinterpret_cast(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(channel); + } + else + { + channel->Close(); + r = KErrNoMemory; + } + } + NKern::ThreadLeaveCS(); + + return r; + } + +TInt DDmaTestSession::CookieToChannelIndex(TUint aDriverCookie) const + { + const TInt r = iChannels.Find(reinterpret_cast(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(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(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; iDDmaTestFactory::~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; + }