/* ---------------------------------------------------------------------------- | |
* SAM Software Package License | |
* ---------------------------------------------------------------------------- | |
* Copyright (c) 2014, Atmel Corporation | |
* | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* | |
* - Redistributions of source code must retain the above copyright notice, | |
* this list of conditions and the disclaimer below. | |
* | |
* Atmel's name may not be used to endorse or promote products derived from | |
* this software without specific prior written permission. | |
* | |
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR | |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE | |
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, | |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, | |
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | |
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
* ---------------------------------------------------------------------------- | |
*/ | |
/** \addtogroup xdmad_module | |
* | |
* \section Xdma xDma Configuration Usage | |
* | |
* To configure a XDMA channel, the user has to follow these few steps : | |
* <ul> | |
* <li> Initialize a XDMA driver instance by XDMAD_Initialize().</li> | |
* <li> choose an available (disabled) channel using XDMAD_AllocateChannel().</li> | |
* <li> After the XDMAC selected channel has been programmed, | |
* XDMAD_PrepareChannel() is to enable clock and dma peripheral of the DMA, and | |
* set Configuration register to set up the transfer type (memory or non-memory | |
* peripheral for source and destination) and flow control device.</li> | |
* <li> Invoke XDMAD_StartTransfer() to start DMA transfer or | |
* XDMAD_StopTransfer() to force stop DMA transfer.</li> | |
* <li> Once the buffer of data is transferred, XDMAD_IsTransferDone() | |
* checks if DMA transfer is finished.</li> | |
* <li> XDMAD_Handler() handles XDMA interrupt, and invoking XDMAD_SetCallback() | |
* if provided.</li> | |
* </ul> | |
* | |
* Related files:\n | |
* \ref xdmad.h\n | |
* \ref xdmad.c.\n | |
*/ | |
/** \file */ | |
/** \addtogroup dmad_functions | |
@{*/ | |
/*---------------------------------------------------------------------------- | |
* Includes | |
*----------------------------------------------------------------------------*/ | |
#include "chip.h" | |
#include <assert.h> | |
static uint8_t xDmad_Initialized = 0; | |
/*---------------------------------------------------------------------------- | |
* Local functions | |
*----------------------------------------------------------------------------*/ | |
/** | |
* \brief Try to allocate a DMA channel for on given controller. | |
* \param pDmad Pointer to DMA driver instance. | |
* \param bSrcID Source peripheral ID, 0xFF for memory. | |
* \param bDstID Destination peripheral ID, 0xFF for memory. | |
* \return Channel number if allocation successful, return | |
* DMAD_ALLOC_FAILED if allocation failed. | |
*/ | |
static uint32_t XDMAD_AllocateXdmacChannel( sXdmad *pXdmad, | |
uint8_t bSrcID, | |
uint8_t bDstID) | |
{ | |
uint32_t i; | |
/* Can't support peripheral to peripheral */ | |
if ((( bSrcID != XDMAD_TRANSFER_MEMORY ) | |
&& ( bDstID != XDMAD_TRANSFER_MEMORY ))) { | |
return XDMAD_ALLOC_FAILED; | |
} | |
/* dma transfer from peripheral to memory */ | |
if ( bDstID == XDMAD_TRANSFER_MEMORY) { | |
if( (!XDMAIF_IsValidatedPeripherOnDma(bSrcID)) ) { | |
TRACE_ERROR("%s:: Allocation failed", __FUNCTION__); | |
return XDMAD_ALLOC_FAILED; | |
} | |
} | |
/* dma transfer from memory to peripheral */ | |
if ( bSrcID == XDMAD_TRANSFER_MEMORY ) { | |
if( (!XDMAIF_IsValidatedPeripherOnDma(bDstID)) ) { | |
TRACE_ERROR("%s:: Allocation failed", __FUNCTION__); | |
return XDMAD_ALLOC_FAILED; | |
} | |
} | |
for (i = 0; i < pXdmad->numChannels; i ++) { | |
if ( pXdmad->XdmaChannels[i].state == XDMAD_STATE_FREE ) { | |
/* Allocate the channel */ | |
pXdmad->XdmaChannels[i].state = XDMAD_STATE_ALLOCATED; | |
/* Get general informations */ | |
pXdmad->XdmaChannels[i].bSrcPeriphID = bSrcID; | |
pXdmad->XdmaChannels[i].bDstPeriphID = bDstID; | |
pXdmad->XdmaChannels[i].bSrcTxIfID = | |
XDMAIF_Get_ChannelNumber(bSrcID, 0); | |
pXdmad->XdmaChannels[i].bSrcRxIfID = | |
XDMAIF_Get_ChannelNumber(bSrcID, 1); | |
pXdmad->XdmaChannels[i].bDstTxIfID = | |
XDMAIF_Get_ChannelNumber(bDstID, 0); | |
pXdmad->XdmaChannels[i].bDstRxIfID = | |
XDMAIF_Get_ChannelNumber(bDstID, 1); | |
return ((i) & 0xFF); | |
} | |
} | |
TRACE_ERROR("%s:: Allocation failed, all channels are occupied", __FUNCTION__); | |
return XDMAD_ALLOC_FAILED; | |
} | |
/*---------------------------------------------------------------------------- | |
* Exported functions | |
*----------------------------------------------------------------------------*/ | |
/** | |
* \brief Initialize xDMA driver instance. | |
* \param pXdmad Pointer to xDMA driver instance. | |
* \param bPollingMode Polling DMA transfer: | |
* 1. Via XDMAD_IsTransferDone(); or | |
* 2. Via XDMAD_Handler(). | |
*/ | |
void XDMAD_Initialize( sXdmad *pXdmad, uint8_t bPollingMode ) | |
{ | |
uint32_t j; | |
uint32_t volatile timer=0x7FF; | |
assert( pXdmad) ; | |
LockMutex(pXdmad->xdmaMutex, timer); | |
if (xDmad_Initialized) { | |
ReleaseMutex(pXdmad->xdmaMutex); | |
return; | |
} | |
pXdmad->pXdmacs = XDMAC; | |
pXdmad->pollingMode = bPollingMode; | |
pXdmad->numControllers = XDMAC_CONTROLLER_NUM; | |
pXdmad->numChannels = (XDMAC_GTYPE_NB_CH( XDMAC_GetType(XDMAC) ) + 1); | |
for (j = 0; j < pXdmad->numChannels; j ++) { | |
pXdmad->XdmaChannels[j].fCallback = 0; | |
pXdmad->XdmaChannels[j].pArg = 0; | |
pXdmad->XdmaChannels[j].bIrqOwner = 0; | |
pXdmad->XdmaChannels[j].bSrcPeriphID = 0; | |
pXdmad->XdmaChannels[j].bDstPeriphID = 0; | |
pXdmad->XdmaChannels[j].bSrcTxIfID = 0; | |
pXdmad->XdmaChannels[j].bSrcRxIfID = 0; | |
pXdmad->XdmaChannels[j].bDstTxIfID = 0; | |
pXdmad->XdmaChannels[j].bDstRxIfID = 0; | |
pXdmad->XdmaChannels[j].state = XDMAD_STATE_FREE; | |
} | |
xDmad_Initialized = 1; | |
ReleaseMutex(pXdmad->xdmaMutex); | |
} | |
/** | |
* \brief Allocate a XDMA channel for upper layer. | |
* \param pXdmad Pointer to xDMA driver instance. | |
* \param bSrcID Source peripheral ID, 0xFF for memory. | |
* \param bDstID Destination peripheral ID, 0xFF for memory. | |
* \return Channel number if allocation successful, return | |
* XDMAD_ALLOC_FAILED if allocation failed. | |
*/ | |
uint32_t XDMAD_AllocateChannel( sXdmad *pXdmad, | |
uint8_t bSrcID, | |
uint8_t bDstID) | |
{ | |
uint32_t dwChannel = XDMAD_ALLOC_FAILED; | |
uint32_t volatile timer=0x7FF; | |
LockMutex(pXdmad->xdmaMutex, timer); | |
dwChannel = XDMAD_AllocateXdmacChannel( pXdmad, bSrcID, bDstID ); | |
ReleaseMutex(pXdmad->xdmaMutex); | |
return dwChannel; | |
} | |
/** | |
* \brief Free the specified xDMA channel. | |
* \param pXdmad Pointer to xDMA driver instance. | |
* \param dwChannel ControllerNumber << 8 | ChannelNumber. | |
*/ | |
eXdmadRC XDMAD_FreeChannel( sXdmad *pXdmad, | |
uint32_t dwChannel ) | |
{ | |
uint8_t iChannel = (dwChannel) & 0xFF; | |
assert( pXdmad != NULL ) ; | |
if (iChannel >= pXdmad->numChannels) return XDMAD_ERROR; | |
switch ( pXdmad->XdmaChannels[iChannel].state ) { | |
case XDMAD_STATE_ALLOCATED: | |
case XDMAD_STATE_START: | |
case XDMAD_STATE_IN_XFR: | |
return XDMAD_BUSY; | |
case XDMAD_STATE_DONE: | |
case XDMAD_STATE_HALTED: | |
pXdmad->XdmaChannels[iChannel].state = XDMAD_STATE_FREE; | |
break; | |
} | |
return XDMAD_OK; | |
} | |
/** | |
* \brief Set the callback function for xDMA channel transfer. | |
* \param pXdmad Pointer to xDMA driver instance. | |
* \param dwChannel ControllerNumber << 8 | ChannelNumber. | |
* \param fCallback Pointer to callback function. | |
* \param pArg Pointer to optional argument for callback. | |
*/ | |
eXdmadRC XDMAD_SetCallback( sXdmad *pXdmad, | |
uint32_t dwChannel, | |
XdmadTransferCallback fCallback, | |
void* pArg ) | |
{ | |
uint8_t iChannel = (dwChannel) & 0xFF; | |
assert( pXdmad != NULL ) ; | |
if (iChannel >= pXdmad->numChannels) return XDMAD_ERROR; | |
if ( pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_FREE ) | |
return XDMAD_ERROR; | |
else if ( pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_START ) | |
return XDMAD_BUSY; | |
pXdmad->XdmaChannels[iChannel].fCallback = fCallback; | |
pXdmad->XdmaChannels[iChannel].pArg = pArg; | |
return XDMAD_OK; | |
} | |
/** | |
* \brief Enable clock of the xDMA peripheral, Enable the dma peripheral, | |
* configure configuration register for xDMA transfer. | |
* \param pXdmad Pointer to xDMA driver instance. | |
* \param dwChannel ControllerNumber << 8 | ChannelNumber. | |
* \param dwCfg Configuration value. | |
*/ | |
eXdmadRC XDMAD_PrepareChannel( sXdmad *pXdmad, uint32_t dwChannel) | |
{ | |
uint8_t iChannel = (dwChannel) & 0xFF; | |
Xdmac *pXdmac = pXdmad->pXdmacs; | |
assert( pXdmad != NULL ) ; | |
if (iChannel >= pXdmad->numChannels) return XDMAD_ERROR; | |
if ( pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_FREE ) | |
return XDMAD_ERROR; | |
else if ( ( pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_START ) | |
|| ( pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_IN_XFR ) ) | |
return XDMAD_BUSY; | |
/* Enable clock of the DMA peripheral */ | |
if (!PMC_IsPeriphEnabled( ID_XDMAC )) { | |
PMC_EnablePeripheral( ID_XDMAC ); | |
} | |
/* Clear dummy status */ | |
XDMAC_GetChannelIsr( pXdmac,iChannel ); | |
/* Disables XDMAC interrupt for the given channel. */ | |
XDMAC_DisableGIt (pXdmac, iChannel); | |
XDMAC_DisableChannelIt (pXdmac, iChannel, 0xFF); | |
/* Disable the given dma channel. */ | |
XDMAC_DisableChannel( pXdmac, iChannel ); | |
XDMAC_SetSourceAddr(pXdmac, iChannel, 0); | |
XDMAC_SetDestinationAddr(pXdmac, iChannel, 0); | |
XDMAC_SetBlockControl(pXdmac, iChannel, 0); | |
XDMAC_SetChannelConfig( pXdmac, iChannel, 0x20); | |
XDMAC_SetDescriptorAddr(pXdmac, iChannel, 0, 0); | |
XDMAC_SetDescriptorControl(pXdmac, iChannel, 0); | |
return XDMAD_OK; | |
} | |
/** | |
* \brief xDMA interrupt handler | |
* \param pxDmad Pointer to DMA driver instance. | |
*/ | |
void XDMAD_Handler( sXdmad *pDmad) | |
{ | |
Xdmac *pXdmac; | |
sXdmadChannel *pCh; | |
uint32_t xdmaChannelIntStatus, xdmaGlobaIntStatus,xdmaGlobalChStatus; | |
uint8_t bExec = 0; | |
uint8_t _iChannel; | |
assert( pDmad != NULL ) ; | |
pXdmac = pDmad->pXdmacs; | |
xdmaGlobaIntStatus = XDMAC_GetGIsr(pXdmac); | |
if ((xdmaGlobaIntStatus & 0xFFFFFF) != 0) { | |
xdmaGlobalChStatus = XDMAC_GetGlobalChStatus(pXdmac); | |
for (_iChannel = 0; _iChannel < pDmad->numChannels; _iChannel ++) { | |
if (!(xdmaGlobaIntStatus & (1<<_iChannel))) continue; | |
pCh = &pDmad->XdmaChannels[_iChannel]; | |
if ( pCh->state == XDMAD_STATE_FREE) return ; | |
if ((xdmaGlobalChStatus & ( XDMAC_GS_ST0 << _iChannel)) == 0) { | |
bExec = 0; | |
xdmaChannelIntStatus = XDMAC_GetMaskChannelIsr( pXdmac, _iChannel); | |
if (xdmaChannelIntStatus & XDMAC_CIS_BIS) { | |
if((XDMAC_GetChannelItMask(pXdmac, _iChannel) & XDMAC_CIM_LIM) | |
== 0 ) { | |
pCh->state = XDMAD_STATE_DONE ; | |
bExec = 1; | |
} | |
TRACE_DEBUG("XDMAC_CIS_BIS\n\r"); | |
} | |
if (xdmaChannelIntStatus & XDMAC_CIS_FIS) { | |
TRACE_DEBUG("XDMAC_CIS_FIS\n\r"); | |
} | |
if (xdmaChannelIntStatus & XDMAC_CIS_RBEIS) { | |
TRACE_DEBUG("XDMAC_CIS_RBEIS\n\r"); | |
} | |
if (xdmaChannelIntStatus & XDMAC_CIS_WBEIS) { | |
TRACE_DEBUG("XDMAC_CIS_WBEIS\n\r"); | |
} | |
if (xdmaChannelIntStatus & XDMAC_CIS_ROIS) { | |
TRACE_DEBUG("XDMAC_CIS_ROIS\n\r"); | |
} | |
if (xdmaChannelIntStatus & XDMAC_CIS_LIS) { | |
TRACE_DEBUG("XDMAC_CIS_LIS\n\r"); | |
pCh->state = XDMAD_STATE_DONE ; | |
bExec = 1; | |
} | |
if (xdmaChannelIntStatus & XDMAC_CIS_DIS ) | |
{ | |
pCh->state = XDMAD_STATE_DONE ; | |
bExec = 1; | |
} | |
SCB_CleanInvalidateDCache(); | |
} else { | |
SCB_CleanInvalidateDCache(); | |
/* Block end interrupt for LLI dma mode */ | |
if( XDMAC_GetChannelIsr( pXdmac, _iChannel) & XDMAC_CIS_BIS) { | |
/* Execute callback */ | |
pCh->fCallback(_iChannel, pCh->pArg); | |
} | |
} | |
/* Execute callback */ | |
if (bExec && pCh->fCallback) { | |
pCh->fCallback(_iChannel, pCh->pArg); | |
} | |
} | |
} | |
} | |
/** | |
* \brief Check if DMA transfer is finished. | |
* In polling mode XDMAD_Handler() is polled. | |
* \param pDmad Pointer to DMA driver instance. | |
* \param dwChannel ControllerNumber << 8 | ChannelNumber. | |
*/ | |
eXdmadRC XDMAD_IsTransferDone( sXdmad *pXdmad, uint32_t dwChannel ) | |
{ | |
uint8_t iChannel = (dwChannel) & 0xFF; | |
uint8_t state; | |
assert( pXdmad != NULL ) ; | |
if (iChannel >= pXdmad->numChannels) | |
return XDMAD_ERROR; | |
SCB_CleanInvalidateDCache(); | |
state = pXdmad->XdmaChannels[iChannel].state; | |
if ( state == XDMAD_STATE_ALLOCATED ) return XDMAD_OK; | |
if ( state == XDMAD_STATE_FREE ) return XDMAD_ERROR; | |
else if ( state != XDMAD_STATE_DONE ) { | |
if(pXdmad->pollingMode) XDMAD_Handler( pXdmad); | |
return XDMAD_BUSY; | |
} | |
return XDMAD_OK; | |
} | |
/** | |
* \brief Configure DMA for a single transfer. | |
* \param pXdmad Pointer to xDMA driver instance. | |
* \param dwChannel ControllerNumber << 8 | ChannelNumber. | |
*/ | |
eXdmadRC XDMAD_ConfigureTransfer( sXdmad *pXdmad, | |
uint32_t dwChannel, | |
sXdmadCfg *pXdmaParam, | |
uint32_t dwXdmaDescCfg, | |
uint32_t dwXdmaDescAddr, | |
uint32_t dwXdmaIntEn) | |
{ | |
uint8_t iChannel = (dwChannel) & 0xFF; | |
assert( pXdmad != NULL ) ; | |
if (iChannel >= pXdmad->numChannels) | |
return XDMAD_ERROR; | |
Xdmac *pXdmac = pXdmad->pXdmacs; | |
XDMAC_GetChannelIsr( pXdmac, iChannel); | |
if ( pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_FREE ) | |
return XDMAD_ERROR; | |
if ( pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_START ) | |
return XDMAD_BUSY; | |
/* Linked List is enabled */ | |
if ((dwXdmaDescCfg & XDMAC_CNDC_NDE) == XDMAC_CNDC_NDE_DSCR_FETCH_EN) { | |
if ((dwXdmaDescCfg & XDMAC_CNDC_NDVIEW_Msk) == XDMAC_CNDC_NDVIEW_NDV0) { | |
XDMAC_SetChannelConfig( pXdmac, iChannel, pXdmaParam->mbr_cfg ); | |
XDMAC_SetSourceAddr(pXdmac, iChannel, pXdmaParam->mbr_sa); | |
XDMAC_SetDestinationAddr(pXdmac, iChannel, pXdmaParam->mbr_da); | |
} | |
if ((dwXdmaDescCfg & XDMAC_CNDC_NDVIEW_Msk) == XDMAC_CNDC_NDVIEW_NDV1) { | |
XDMAC_SetChannelConfig( pXdmac, iChannel, pXdmaParam->mbr_cfg ); | |
} | |
XDMAC_SetDescriptorAddr(pXdmac, iChannel, dwXdmaDescAddr, 0); | |
XDMAC_SetDescriptorControl(pXdmac, iChannel, dwXdmaDescCfg); | |
XDMAC_DisableChannelIt (pXdmac, iChannel, 0xFF); | |
XDMAC_EnableChannelIt (pXdmac,iChannel, dwXdmaIntEn ); | |
} else { | |
/* LLI is disabled. */ | |
XDMAC_SetSourceAddr(pXdmac, iChannel, pXdmaParam->mbr_sa); | |
XDMAC_SetDestinationAddr(pXdmac, iChannel, pXdmaParam->mbr_da); | |
XDMAC_SetMicroblockControl(pXdmac, iChannel, pXdmaParam->mbr_ubc); | |
XDMAC_SetBlockControl(pXdmac, iChannel, pXdmaParam->mbr_bc); | |
XDMAC_SetDataStride_MemPattern(pXdmac, iChannel, pXdmaParam->mbr_ds); | |
XDMAC_SetSourceMicroBlockStride(pXdmac, iChannel, pXdmaParam->mbr_sus); | |
XDMAC_SetDestinationMicroBlockStride(pXdmac, iChannel, pXdmaParam->mbr_dus); | |
XDMAC_SetChannelConfig( pXdmac, iChannel, pXdmaParam->mbr_cfg ); | |
XDMAC_SetDescriptorAddr(pXdmac, iChannel, 0, 0); | |
XDMAC_SetDescriptorControl(pXdmac, iChannel, 0); | |
XDMAC_EnableChannelIt (pXdmac,iChannel,dwXdmaIntEn); | |
} | |
return XDMAD_OK; | |
} | |
/** | |
* \brief Start xDMA transfer. | |
* \param pXdmad Pointer to XDMA driver instance. | |
* \param dwChannel ControllerNumber << 8 | ChannelNumber. | |
*/ | |
eXdmadRC XDMAD_StartTransfer( sXdmad *pXdmad, uint32_t dwChannel ) | |
{ | |
uint8_t iChannel = (dwChannel) & 0xFF; | |
assert( pXdmad != NULL ) ; | |
if (iChannel >= pXdmad->numChannels) return XDMAD_ERROR; | |
Xdmac *pXdmac = pXdmad->pXdmacs; | |
if ( pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_FREE ) { | |
TRACE_ERROR("%s:: XDMAD_STATE_FREE \n\r", __FUNCTION__); | |
return XDMAD_ERROR; | |
} else if ( pXdmad->XdmaChannels[iChannel].state == XDMAD_STATE_START ) { | |
TRACE_ERROR("%s:: XDMAD_STATE_START \n\r", __FUNCTION__) | |
return XDMAD_BUSY; | |
} | |
/* Change state to transferring */ | |
pXdmad->XdmaChannels[iChannel].state = XDMAD_STATE_START; | |
XDMAC_EnableChannel(pXdmac, iChannel); | |
if ( pXdmad->pollingMode == 0 ) { | |
XDMAC_EnableGIt( pXdmac, iChannel); | |
} | |
return XDMAD_OK; | |
} | |
/** | |
* \brief Stop DMA transfer. | |
* \param pDmad Pointer to DMA driver instance. | |
* \param dwChannel ControllerNumber << 8 | ChannelNumber. | |
*/ | |
eXdmadRC XDMAD_StopTransfer( sXdmad *pXdmad, uint32_t dwChannel ) | |
{ | |
uint8_t _iChannel = (dwChannel) & 0xFF; | |
assert( pXdmad != NULL ) ; | |
if (_iChannel >= pXdmad->numChannels) return XDMAD_ERROR; | |
Xdmac *pXdmac = pXdmad->pXdmacs; | |
pXdmad->XdmaChannels[_iChannel].state = XDMAD_STATE_HALTED; | |
/* Disable channel */ | |
XDMAC_DisableChannel(pXdmac, _iChannel); | |
/* Disable interrupts */ | |
XDMAC_DisableChannelIt(pXdmac, _iChannel, 0xFF); | |
/* Clear pending status */ | |
XDMAC_GetChannelIsr( pXdmac, _iChannel); | |
XDMAC_GetGlobalChStatus(pXdmac); | |
return XDMAD_OK; | |
} | |
/**@}*/ | |