diff -r b3a1d9898418 -r 95f71bcdcdb7 bsptemplate/asspandvariant/template_assp/dmapsl_v2.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bsptemplate/asspandvariant/template_assp/dmapsl_v2.cpp Thu May 27 14:17:14 2010 +0300 @@ -0,0 +1,480 @@ +// Copyright (c) 2004-2010 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: +// bsptemplate/asspvariant/template_assp/dmapsl_v2.cpp +// Template DMA Platform Specific Layer (PSL). +// +// + + +#include +#include // /assp/template_assp/ + +#include +#include + + +// Debug support +static const char KDmaPanicCat[] = "DMA PSL - " __FILE__; + +static const TInt KMaxTransferLen = 0x1FE0; // max transfer length for this DMAC +static const TInt KMemAlignMask = 7; // memory addresses passed to DMAC must be multiple of 8 +static const TInt KChannelCount = 16; // we got 16 channels +static const TInt KDesCount = 160; // Initial DMA descriptor count + + +class TDmaDesc +// +// Hardware DMA descriptor +// + { +public: + enum {KStopBitMask = 1}; +public: + TPhysAddr iDescAddr; + TPhysAddr iSrcAddr; + TPhysAddr iDestAddr; + TUint32 iCmd; + }; + + +////////////////////////////////////////////////////////////////////////////// +// Test Support +////////////////////////////////////////////////////////////////////////////// + +/** +TO DO: Fill in to provide information to the V1 test harness (t_dma.exe) +*/ +TDmaTestInfo TestInfo = + { + 0, + 0, + 0, + 0, + NULL, + 0, + NULL, + 0, + NULL + }; + + +EXPORT_C const TDmaTestInfo& DmaTestInfo() +// +// +// + { + return TestInfo; + } + +/** +TO DO: Fill in to provide information to the V2 test harness (t_dma2.exe) +*/ +TDmaV2TestInfo TestInfov2 = + { + 0, + 0, + 0, + 0, + {0}, + 0, + {0}, + 0, + {0} + }; + +EXPORT_C const TDmaV2TestInfo& DmaTestInfoV2() + { + return TestInfov2; + } + +////////////////////////////////////////////////////////////////////////////// +// Helper Functions +////////////////////////////////////////////////////////////////////////////// + +inline TBool IsHwDesAligned(TAny* aDes) +// +// Checks whether given hardware descriptor is 16-bytes aligned. +// + { + return ((TLinAddr)aDes & 0xF) == 0; + } + + +static TUint32 DmaCmdReg(TUint aCount, TUint aFlags, TUint32 aSrcPslInfo, TUint32 aDstPslInfo) +// +// Returns value to set in DMA command register or in descriptor command field. +// + { + // TO DO: Construct CMD word from input values. + // The return value should reflect the actual control word. + return (aCount | aFlags | aSrcPslInfo | aDstPslInfo); + } + + +////////////////////////////////////////////////////////////////////////////// +// Derived Channel (Scatter/Gather) +////////////////////////////////////////////////////////////////////////////// + +class TTemplateSgChannel : public TDmaSgChannel + { +public: + TDmaDesc* iTmpDes; + TPhysAddr iTmpDesPhysAddr; + }; + + +////////////////////////////////////////////////////////////////////////////// +// Derived Controller Class +////////////////////////////////////////////////////////////////////////////// + +class TTemplateDmac : public TDmac + { +public: + TTemplateDmac(); + TInt Create(); +private: + // from TDmac (PIL pure virtual) + virtual void StopTransfer(const TDmaChannel& aChannel); + virtual TBool IsIdle(const TDmaChannel& aChannel); + virtual TUint MaxTransferLength(TDmaChannel& aChannel, TUint aSrcFlags, + TUint aDstFlags, TUint32 aPslInfo); + virtual TUint AddressAlignMask(TDmaChannel& aChannel, TUint aSrcFlags, + TUint aDstFlags, TUint32 aPslInfo); + // from TDmac (PIL virtual) + virtual void Transfer(const TDmaChannel& aChannel, const SDmaDesHdr& aHdr); + virtual TInt InitHwDes(const SDmaDesHdr& aHdr, const TDmaTransferArgs& aTransferArgs); + virtual void ChainHwDes(const SDmaDesHdr& aHdr, const SDmaDesHdr& aNextHdr); + virtual void AppendHwDes(const TDmaChannel& aChannel, const SDmaDesHdr& aLastHdr, + const SDmaDesHdr& aNewHdr); + virtual void UnlinkHwDes(const TDmaChannel& aChannel, SDmaDesHdr& aHdr); + // other + static void Isr(TAny* aThis); + inline TDmaDesc* HdrToHwDes(const SDmaDesHdr& aHdr); +private: + static const SCreateInfo KInfo; +public: + TTemplateSgChannel iChannels[KChannelCount]; + }; + + +static TTemplateDmac Controller; + + +const TDmac::SCreateInfo TTemplateDmac::KInfo = + { + ETrue, // iCapsHwDes + KDesCount, // iDesCount + sizeof(TDmaDesc), // iDesSize + EMapAttrSupRw | EMapAttrFullyBlocking // iDesChunkAttribs + }; + + +TTemplateDmac::TTemplateDmac() +// +// Constructor. +// + : TDmac(KInfo) + {} + + +TInt TTemplateDmac::Create() +// +// Second phase construction. +// + { + TInt r = TDmac::Create(KInfo); // Base class Create() + if (r == KErrNone) + { + __DMA_ASSERTA(ReserveSetOfDes(KChannelCount) == KErrNone); + for (TInt i=0; i < KChannelCount; ++i) + { + TDmaDesc* pD = HdrToHwDes(*iFreeHdr); + iChannels[i].iTmpDes = pD; + iChannels[i].iTmpDesPhysAddr = HwDesLinToPhys(pD); + iFreeHdr = iFreeHdr->iNext; + } + r = Interrupt::Bind(EAsspIntIdDma, Isr, this); + if (r == KErrNone) + { + // TO DO: Map DMA clients (requests) to DMA channels here. + + r = Interrupt::Enable(EAsspIntIdDma); + } + } + return r; + } + + +void TTemplateDmac::Transfer(const TDmaChannel& aChannel, const SDmaDesHdr& aHdr) +// +// Initiates a (previously constructed) request on a specific channel. +// + { + const TUint8 i = static_cast(aChannel.PslId()); + TDmaDesc* pD = HdrToHwDes(aHdr); + + __KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::Transfer channel=%d des=0x%08X", i, pD)); + + // TO DO (for instance): Load the first descriptor address into the DMAC and start it + // by setting the RUN bit. + (void) *pD, (void) i; + + } + + +void TTemplateDmac::StopTransfer(const TDmaChannel& aChannel) +// +// Stops a running channel. +// + { + const TUint8 i = static_cast(aChannel.PslId()); + + __KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::StopTransfer channel=%d", i)); + + // TO DO (for instance): Clear the RUN bit of the channel. + (void) i; + + } + + +TBool TTemplateDmac::IsIdle(const TDmaChannel& aChannel) +// +// Returns the state of a given channel. +// + { + const TUint8 i = static_cast(aChannel.PslId()); + + __KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::IsIdle channel=%d", i)); + + // TO DO (for instance): Return the state of the RUN bit of the channel. + // The return value should reflect the actual state. + (void) i; + + return ETrue; + } + + +TUint TTemplateDmac::MaxTransferLength(TDmaChannel& /*aChannel*/, TUint /*aSrcFlags*/, + TUint /*aDstFlags*/, TUint32 /*aPslInfo*/) +// +// Returns the maximum transfer length in bytes for a given transfer. +// + { + // TO DO: Determine the proper return value, based on the arguments. + + // For instance: + return KMaxTransferLen; + } + + +TUint TTemplateDmac::AddressAlignMask(TDmaChannel& aChannel, TUint /*aSrcFlags*/, + TUint /*aDstFlags*/, TUint32 /*aPslInfo*/) +// +// Returns the memory buffer alignment restrictions mask for a given transfer. +// + { + // TO DO: Determine the proper return value, based on the arguments. + + // For instance: + return KMemAlignMask; + } + + +TInt TTemplateDmac::InitHwDes(const SDmaDesHdr& aHdr, const TDmaTransferArgs& aTransferArgs) +// +// Sets up (from a passed in request) the descriptor with that fragment's +// source and destination address, the fragment size, and the (driver/DMA +// controller) specific transfer parameters (mem/peripheral, burst size, +// transfer width). +// + { + TDmaDesc* pD = HdrToHwDes(aHdr); + + __KTRACE_OPT(KDMA, Kern::Printf("TTemplateDmac::InitHwDes 0x%08X", pD)); + + // Unaligned descriptor? Bug in generic layer! + __DMA_ASSERTD(IsHwDesAligned(pD)); + + const TDmaTransferConfig& src = aTransferArgs.iSrcConfig; + const TDmaTransferConfig& dst = aTransferArgs.iDstConfig; + pD->iSrcAddr = (src.iFlags & KDmaPhysAddr) ? src.iAddr : Epoc::LinearToPhysical(src.iAddr); + __DMA_ASSERTD(pD->iSrcAddr != KPhysAddrInvalid); + pD->iDestAddr = (dst.iFlags & KDmaPhysAddr) ? dst.iAddr : Epoc::LinearToPhysical(dst.iAddr); + __DMA_ASSERTD(pD->iDestAddr != KPhysAddrInvalid); + pD->iCmd = DmaCmdReg(aTransferArgs.iTransferCount, aTransferArgs.iFlags, + src.iPslTargetInfo, dst.iPslTargetInfo); + pD->iDescAddr = TDmaDesc::KStopBitMask; + + return KErrNone; + } + + +void TTemplateDmac::ChainHwDes(const SDmaDesHdr& aHdr, const SDmaDesHdr& aNextHdr) +// +// Chains hardware descriptors together by setting the next pointer of the original descriptor +// to the physical address of the descriptor to be chained. +// + { + TDmaDesc* pD = HdrToHwDes(aHdr); + TDmaDesc* pN = HdrToHwDes(aNextHdr); + + __KTRACE_OPT(KDMA, Kern::Printf("TTemplateDmac::ChainHwDes des=0x%08X next des=0x%08X", pD, pN)); + + // Unaligned descriptor? Bug in generic layer! + __DMA_ASSERTD(IsHwDesAligned(pD) && IsHwDesAligned(pN)); + + // TO DO: Modify pD->iCmd so that no end-of-transfer interrupt gets raised any longer. + + pD->iDescAddr = HwDesLinToPhys(pN); + } + + +void TTemplateDmac::AppendHwDes(const TDmaChannel& aChannel, const SDmaDesHdr& aLastHdr, + const SDmaDesHdr& aNewHdr) +// +// Appends a descriptor to the chain while the channel is running. +// + { + const TUint8 i = static_cast(aChannel.PslId()); + + TDmaDesc* pL = HdrToHwDes(aLastHdr); + TDmaDesc* pN = HdrToHwDes(aNewHdr); + + __KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::AppendHwDes channel=%d last des=0x%08X new des=0x%08X", + i, pL, pN)); + // Unaligned descriptor? Bug in generic layer! + __DMA_ASSERTD(IsHwDesAligned(pL) && IsHwDesAligned(pN)); + + TPhysAddr newPhys = HwDesLinToPhys(pN); + + const TInt irq = NKern::DisableAllInterrupts(); + StopTransfer(aChannel); + + pL->iDescAddr = newPhys; + const TTemplateSgChannel& channel = static_cast(aChannel); + TDmaDesc* pD = channel.iTmpDes; + + // TO DO: Implement the appropriate algorithm for appending a descriptor here. + (void) *pD, (void) i; + + NKern::RestoreInterrupts(irq); + + __KTRACE_OPT(KDMA, Kern::Printf("TTemplateDmac::UnlinkHwDes")); + TDmaDesc* pD = HdrToHwDes(aHdr); + pD->iDescAddr = TDmaDesc::KStopBitMask; + + // TO DO: Modify pD->iCmd so that an end-of-transfer interrupt will get raised. + + } + + +void TTemplateDmac::Isr(TAny* aThis) +// +// This ISR reads the interrupt identification and calls back into the base class +// interrupt service handler with the channel identifier and an indication whether the +// transfer completed correctly or with an error. +// + { + TTemplateDmac& me = *static_cast(aThis); + + // TO DO: Implement the behaviour described above, call HandleIsr(). + + HandleIsr(me.iChannels[5], EDmaCallbackRequestCompletion, ETrue); // Example + + } + + +inline TDmaDesc* TTemplateDmac::HdrToHwDes(const SDmaDesHdr& aHdr) +// +// Changes return type of base class call. +// + { + return static_cast(TDmac::HdrToHwDes(aHdr)); + } + + +////////////////////////////////////////////////////////////////////////////// +// Channel Opening/Closing (Channel Allocator) +////////////////////////////////////////////////////////////////////////////// + +TDmaChannel* DmaChannelMgr::Open(TUint32 aOpenId, TBool /*aDynChannel*/, TUint /*aPriority*/) +// +// +// + { + __KTRACE_OPT(KDMA, Kern::Printf(">DmaChannelMgr::Open aOpenId=%d", aOpenId)); + + __DMA_ASSERTA(aOpenId < static_cast(KChannelCount)); + + TDmaChannel* pC = Controller.iChannels + aOpenId; + if (pC->IsOpened()) + { + pC = NULL; + } + else + { + pC->iController = &Controller; + pC->iPslId = aOpenId; + } + + return pC; + } + + +void DmaChannelMgr::Close(TDmaChannel* /*aChannel*/) +// +// +// + { + // NOP + } + + +TInt DmaChannelMgr::StaticExtension(TInt /*aCmd*/, TAny* /*aArg*/) +// +// +// + { + return KErrNotSupported; + } + + +////////////////////////////////////////////////////////////////////////////// +// DLL Exported Function +////////////////////////////////////////////////////////////////////////////// + +DECLARE_STANDARD_EXTENSION() +// +// Creates and initializes a new DMA controller object on the kernel heap. +// + { + __KTRACE_OPT2(KBOOT, KDMA, Kern::Printf("Starting DMA Extension")); + + const TInt r = DmaChannelMgr::Initialise(); + if (r != KErrNone) + { + return r; + } + return Controller.Create(); + }