blob: 69eaa8afa750770666cd9626025b15432e6fa6a2 [file] [log] [blame]
/* ----------------------------------------------------------------------------
* SAM Software Package License
* ----------------------------------------------------------------------------
* Copyright (c) 2011, 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.
* ----------------------------------------------------------------------------
*/
/** \file */
/*---------------------------------------------------------------------------
* Headers
*---------------------------------------------------------------------------*/
#include <board.h>
#include <string.h>
/** \addtogroup EMACD_defines
@{*/
/*----------------------------------------------------------------------------
* Definitions
*----------------------------------------------------------------------------*/
/** Error bits for TX */
#define EMAC_TX_ERR_BITS \
(EMAC_TXD_bmERROR | EMAC_TXD_bmUNDERRUN | EMAC_TXD_bmEXHAUSTED)
/*---------------------------------------------------------------------------
* Circular buffer management
*---------------------------------------------------------------------------*/
/** Return count in buffer */
#define CIRC_CNT(head,tail,size) (((head) - (tail)) % (size))
/** Return space available, 0..size-1. always leave one free char as a completely full buffer
has head == tail, which is the same as empty */
#define CIRC_SPACE(head,tail,size) CIRC_CNT((tail),((head)+1),(size))
/** Return count up to the end of the buffer. Carefully avoid accessing head and tail more than once,
so they can change underneath us without returning inconsistent results */
#define CIRC_CNT_TO_END(head,tail,size) \
({int end = (size) - (tail); \
int n = ((head) + end) % (size); \
n < end ? n : end;})
/** Return space available up to the end of the buffer */
#define CIRC_SPACE_TO_END(head,tail,size) \
({int end = (size) - 1 - (head); \
int n = (end + (tail)) % (size); \
n <= end ? n : end+1;})
/** Increment head or tail */
#define CIRC_INC(headortail,size) \
headortail++; \
if(headortail >= size) { \
headortail = 0; \
}
/** Circular buffer is empty ? */
#define CIRC_EMPTY(head, tail) (head == tail)
/** Clear circular buffer */
#define CIRC_CLEAR(head, tail) (head = tail = 0)
/** @}*/
/*----------------------------------------------------------------------------
* Internal variables
*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
* Internal functions
*----------------------------------------------------------------------------*/
/**
* Disable TX & reset registers and descriptor list
* \param pDrv Pointer to EMAC Driver instance.
*/
static void EMACD_ResetTx(sEmacd *pDrv)
{
Emac *pHw = pDrv->pHw;
uint8_t *pTxBuffer = pDrv->pTxBuffer;
sEmacTxDescriptor *pTd = pDrv->pTxD;
uint32_t Index;
uint32_t Address;
/* Disable TX */
EMAC_TransmitEnable(pHw, 0);
/* Setup the TX descriptors. */
CIRC_CLEAR(pDrv->wTxHead, pDrv->wTxTail);
for(Index = 0; Index < pDrv->wTxListSize; Index++)
{
Address = (uint32_t)(&(pTxBuffer[Index * EMAC_TX_UNITSIZE]));
pTd[Index].addr = Address;
pTd[Index].status.val = EMAC_TXD_bmUSED;
}
pTd[pDrv->wTxListSize - 1].status.val = EMAC_TXD_bmUSED | EMAC_TXD_bmWRAP;
/* Transmit Buffer Queue Pointer Register */
EMAC_SetTxQueue(pHw, (uint32_t)pTd);
}
/**
* Disable RX & reset registers and descriptor list
* \param pDrv Pointer to EMAC Driver instance.
*/
static void EMACD_ResetRx(sEmacd *pDrv)
{
Emac *pHw = pDrv->pHw;
uint8_t *pRxBuffer = pDrv->pRxBuffer;
sEmacRxDescriptor *pRd = pDrv->pRxD;
uint32_t Index;
uint32_t Address;
/* Disable RX */
EMAC_ReceiveEnable(pHw, 0);
/* Setup the RX descriptors. */
pDrv->wRxI = 0;
for(Index = 0; Index < pDrv->wRxListSize; Index++)
{
Address = (uint32_t)(&(pRxBuffer[Index * EMAC_RX_UNITSIZE]));
/* Remove EMAC_RXD_bmOWNERSHIP and EMAC_RXD_bmWRAP */
pRd[Index].addr.val = Address & EMAC_RXD_ADDR_MASK;
pRd[Index].status.val = 0;
}
pRd[pDrv->wRxListSize - 1].addr.val |= EMAC_RXD_bmWRAP;
/* Receive Buffer Queue Pointer Register */
EMAC_SetRxQueue(pHw, (uint32_t) pRd);
}
/*---------------------------------------------------------------------------
* Exported functions
*---------------------------------------------------------------------------*/
/**
* EMAC Interrupt handler
*/
void EMACD_Handler( sEmacd *pEmacd )
{
Emac *pHw = pEmacd->pHw;
uint32_t isr;
uint32_t rsr;
uint32_t tsr;
sEmacTxDescriptor *pTxTd;
fEmacdTransferCallback *pTxCb = NULL;
uint32_t rxStatusFlag;
uint32_t txStatusFlag;
isr = EMAC_GetItStatus(pHw);
rsr = EMAC_GetRxStatus(pHw);
tsr = EMAC_GetTxStatus(pHw);
isr &= ~(EMAC_GetItMask(pHw) | 0xFFC300);
/* RX packet */
if ((isr & EMAC_ISR_RCOMP) || (rsr & EMAC_RSR_REC))
{
asm("nop");
rxStatusFlag = EMAC_RSR_REC;
/* Check OVR */
if (rsr & EMAC_RSR_OVR)
{
rxStatusFlag |= EMAC_RSR_OVR;
}
/* Check BNA */
if (rsr & EMAC_RSR_BNA)
{
rxStatusFlag |= EMAC_RSR_BNA;
}
/* Clear status */
EMAC_ClearRxStatus(pHw, rxStatusFlag);
/* Invoke callbacks */
if (pEmacd->fRxCb)
{
pEmacd->fRxCb(rxStatusFlag);
}
}
/* TX packet */
if ((isr & EMAC_ISR_TCOMP) || (tsr & EMAC_TSR_COMP)) {
asm("nop");
txStatusFlag = EMAC_TSR_COMP;
/* A frame transmitted */
/* Check RLE */
if (tsr & EMAC_TSR_RLES)
{
/* Status RLE & Number of discarded buffers */
txStatusFlag = EMAC_TSR_RLES
| CIRC_CNT(pEmacd->wTxHead,
pEmacd->wTxTail,
pEmacd->wTxListSize)
;
pTxCb = &pEmacd->fTxCbList[pEmacd->wTxTail];
EMACD_ResetTx(pEmacd);
TRACE_INFO("Tx RLE!!\n\r");
EMAC_TransmitEnable(pHw, 1);
}
/* Check COL */
if (tsr & EMAC_TSR_COL)
{
txStatusFlag |= EMAC_TSR_COL;
}
/* Check BEX */
if (tsr & EMAC_TSR_BEX)
{
txStatusFlag |= EMAC_TSR_BEX;
}
/* Check UND */
if (tsr & EMAC_TSR_UND)
{
txStatusFlag |= EMAC_TSR_UND;
}
/* Clear status */
EMAC_ClearTxStatus(pHw, txStatusFlag);
if (!CIRC_EMPTY(pEmacd->wTxHead, pEmacd->wTxTail))
{
// Check the buffers
do {
pTxTd = &pEmacd->pTxD[pEmacd->wTxTail];
pTxCb = &pEmacd->fTxCbList[pEmacd->wTxTail];
/* Any error?
Exit if buffer has not been sent yet */
if ((pTxTd->status.val & EMAC_TXD_bmUSED) == 0)
{
break;
}
/* Notify upper layer that a packet has been sent */
if (*pTxCb)
{
(*pTxCb)(txStatusFlag);
}
CIRC_INC( pEmacd->wTxTail, pEmacd->wTxListSize );
} while (CIRC_CNT(pEmacd->wTxHead, pEmacd->wTxTail, pEmacd->wTxListSize));
}
if (tsr & EMAC_TSR_RLES)
{
/* Notify upper layer RLE */
if (*pTxCb)
{
(*pTxCb)(txStatusFlag);
}
}
/* If a wakeup has been scheduled, notify upper layer that it can
send other packets, send will be successfull. */
if( (CIRC_SPACE(pEmacd->wTxHead,
pEmacd->wTxTail,
pEmacd->wTxListSize) >= pEmacd->bWakeupThreshold)
&& pEmacd->fWakupCb)
{
pEmacd->fWakupCb();
}
}
/* PAUSE Frame */
if (isr & EMAC_ISR_PFRE)
{
TRACE_INFO("Pause!\n\r");
}
if (isr & EMAC_ISR_PTZ)
{
TRACE_INFO("Pause TO!\n\r");
}
}
/**
* Initialize the EMAC Driver with HW settings.
* \param pEmacd Pointer to EMAC Driver instance.
* \param pHw Pointer to HW address for registers.
* \param bID HW ID for power management.
* \param bCAF Enable/Disable CopyAllFrame.
* \param bNBC Enable/Disable NoBroadCast.
*/
void EMACD_Init(sEmacd *pEmacd,
Emac *pHw, uint8_t bID,
uint8_t bCAF, uint8_t bNBC )
{
TRACE_DEBUG("EMACD_Init\n\r");
/* Initialize struct */
pEmacd->pHw = pHw;
pEmacd->bId = bID;
/* Power ON */
PMC_EnablePeripheral(bID);
/* Disable TX & RX and more */
EMAC_NetworkControl(pHw, 0);
EMAC_DisableIt(pHw, ~0u);
EMAC_ClearStatistics(pHw);
/* Clear all status bits in the receive status register. */
EMAC_ClearRxStatus(pHw, EMAC_RSR_OVR | EMAC_RSR_REC | EMAC_RSR_BNA);
/* Clear all status bits in the transmit status register */
EMAC_ClearTxStatus(pHw, EMAC_TSR_UBR | EMAC_TSR_COL | EMAC_TSR_RLES
| EMAC_TSR_BEX | EMAC_TSR_COMP | EMAC_TSR_UND);
/* Clear interrupts */
EMAC_GetItStatus(pHw);
/* Enable the copy of data into the buffers
ignore broadcasts, and don't copy FCS. */
EMAC_Configure(pHw, EMAC_GetConfigure(pHw) | EMAC_NCFGR_DRFCS | EMAC_NCFGR_PAE);
EMAC_CpyAllEnable(pHw, bCAF);
EMAC_BroadcastDisable(pHw, bNBC);
}
/**
* Initialize necessary allocated buffer lists for EMAC Driver to transfer data.
* Must be invoked after EMACD_Init() but before RX/TX start.
* \param pEmacd Pointer to EMAC Driver instance.
* \param pRxBuffer Pointer to allocated buffer for RX. The address should
* be 8-byte aligned and the size should be
* EMAC_RX_UNITSIZE * wRxSize.
* \param pRxD Pointer to allocated RX descriptor list.
* \param wRxSize RX size, in number of registered units (RX descriptors).
* \param pTxBuffer Pointer to allocated buffer for TX. The address should
* be 8-byte aligned and the size should be
* EMAC_TX_UNITSIZE * wTxSize.
* \param pTxD Pointer to allocated TX descriptor list.
* \param pTxCb Pointer to allocated TX callback list.
* \param wTxSize TX size, in number of registered units (TX descriptors).
* \return EMACD_OK or EMACD_PARAM.
* \note If input address is not 8-byte aligned the address is automatically
* adjusted and the list size is reduced by one.
*/
extern uint8_t EMACD_InitTransfer( sEmacd *pEmacd,
uint8_t *pRxBuffer, sEmacRxDescriptor *pRxD,
uint16_t wRxSize,
uint8_t *pTxBuffer, sEmacTxDescriptor *pTxD, fEmacdTransferCallback *pTxCb,
uint16_t wTxSize)
{
Emac *pHw = pEmacd->pHw;
if (wRxSize <= 1 || wTxSize <= 1 || pTxCb == NULL) return EMACD_PARAM;
/* Assign RX buffers */
if ( ((uint32_t)pRxBuffer & 0x7)
|| ((uint32_t)pRxD & 0x7) )
{
wRxSize --;
TRACE_DEBUG("RX list address adjusted\n\r");
}
pEmacd->pRxBuffer = (uint8_t*)((uint32_t)pRxBuffer & 0xFFFFFFF8);
pEmacd->pRxD = (sEmacRxDescriptor*)((uint32_t)pRxD & 0xFFFFFFF8);
pEmacd->wRxListSize = wRxSize;
/* Assign TX buffers */
if ( ((uint32_t)pTxBuffer & 0x7)
|| ((uint32_t)pTxD & 0x7) )
{
wTxSize --;
TRACE_DEBUG("TX list address adjusted\n\r");
}
pEmacd->pTxBuffer = (uint8_t*)((uint32_t)pTxBuffer & 0xFFFFFFF8);
pEmacd->pTxD = (sEmacTxDescriptor*)((uint32_t)pTxD & 0xFFFFFFF8);
pEmacd->wTxListSize = wTxSize;
pEmacd->fTxCbList = pTxCb;
/* Reset TX & RX */
EMACD_ResetRx(pEmacd);
EMACD_ResetTx(pEmacd);
/* Enable Rx and Tx, plus the stats register. */
EMAC_TransmitEnable(pHw, 1);
EMAC_ReceiveEnable(pHw, 1);
EMAC_StatisticsWriteEnable(pHw, 1);
/* Setup the interrupts for TX (and errors) */
EMAC_EnableIt(pHw, EMAC_IER_RXUBR
| EMAC_IER_TUND
| EMAC_IER_RLE
| EMAC_IER_TXERR
| EMAC_IER_TCOMP
| EMAC_IER_ROVR
| EMAC_IER_HRESP
| EMAC_IER_PFR
| EMAC_IER_PTZ);
return EMACD_OK;
}
/**
* Reset TX & RX queue & statistics
* \param pEmacd Pointer to EMAC Driver instance.
*/
void EMACD_Reset(sEmacd *pEmacd)
{
Emac *pHw = pEmacd->pHw;
EMACD_ResetRx(pEmacd);
EMACD_ResetTx(pEmacd);
EMAC_NetworkControl(pHw, EMAC_NCR_TE | EMAC_NCR_RE
| EMAC_NCR_WESTAT | EMAC_NCR_CLRSTAT);
}
/**
* Send a packet with EMAC.
* If the packet size is larger than transfer buffer size error returned.
* If packet transfer status is monitored, specify callback for each packet.
* \param pEmacd Pointer to EMAC Driver instance.
* \param buffer The buffer to be send
* \param size The size of buffer to be send
* \param fTxCb TX callback.
* \return EMACD_OK, EMACD_PARAM or EMACD_TX_BUSY.
*/
uint8_t EMACD_Send( sEmacd *pEmacd,
void *pBuffer,
uint32_t size,
fEmacdTransferCallback fTxCb )
{
Emac *pHw = pEmacd->pHw;
volatile sEmacTxDescriptor *pTxTd;
volatile fEmacdTransferCallback *pfTxCb;
TRACE_DEBUG("EMAC_Send\n\r");
/* Check parameter */
if (size > EMAC_TX_UNITSIZE) {
TRACE_ERROR("EMAC driver does not split send packets.");
TRACE_ERROR("%d bytes max in one packet (%u bytes requested)\n\r",
EMAC_TX_UNITSIZE, (unsigned int)size);
return EMACD_PARAM;
}
/* Pointers to the current TxTd */
pTxTd = &pEmacd->pTxD[pEmacd->wTxHead];
/* If no free TxTd, buffer can't be sent, schedule the wakeup callback */
if( CIRC_SPACE(pEmacd->wTxHead, pEmacd->wTxTail, pEmacd->wTxListSize) == 0)
{
//if ((pTxTd->status & EMAC_TXD_bmUSED) != 0)
{
//EMAC_ResetTx();
//TRACE_WARNING("Circ Full but FREE TD found\n\r");
//AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TE;
}
//else
{
return EMACD_TX_BUSY;
}
}
/* Pointers to the current Tx Callback */
pfTxCb = &pEmacd->fTxCbList[pEmacd->wTxHead];
/* Setup/Copy data to transmition buffer */
if (pBuffer && size)
{
/* Driver manage the ring buffer */
memcpy((void *)pTxTd->addr, pBuffer, size);
}
/* Tx Callback */
*pfTxCb = fTxCb;
/* Update TD status */
/* The buffer size defined is length of ethernet frame
so it's always the last buffer of the frame. */
if (pEmacd->wTxHead == pEmacd->wTxListSize-1)
{
pTxTd->status.val =
(size & EMAC_TXD_LEN_MASK) | EMAC_TXD_bmLAST | EMAC_TXD_bmWRAP;
}
else
{
pTxTd->status.val = (size & EMAC_TXD_LEN_MASK) | EMAC_TXD_bmLAST;
}
CIRC_INC(pEmacd->wTxHead, pEmacd->wTxListSize);
/* Now start to transmit if it is not already done */
EMAC_TransmissionStart(pHw);
return EMACD_OK;
}
/**
* Return current load of TX.
* \param pEmacd Pointer to EMAC Driver instance.
*/
uint32_t EMACD_TxLoad(sEmacd *pEmacd)
{
uint16_t head = pEmacd->wTxHead;
uint16_t tail = pEmacd->wTxTail;
return CIRC_CNT(head, tail, pEmacd->wTxListSize);
}
/**
* Receive a packet with EMAC
* If not enough buffer for the packet, the remaining data is lost but right
* frame length is returned.
* \param pEmacd Pointer to EMAC Driver instance.
* \param pFrame Buffer to store the frame
* \param frameSize Size of the frame
* \param pRcvSize Received size
* \return OK, no data, or frame too small
*/
uint8_t EMACD_Poll( sEmacd * pEmacd,
uint8_t *pFrame,
uint32_t frameSize,
uint32_t *pRcvSize)
{
uint16_t bufferLength;
uint32_t tmpFrameSize=0;
uint8_t *pTmpFrame=0;
uint32_t tmpIdx = pEmacd->wRxI;
volatile sEmacRxDescriptor *pRxTd = &pEmacd->pRxD[pEmacd->wRxI];
char isFrame = 0;
if (pFrame == NULL) return EMACD_PARAM;
/* Set the default return value */
*pRcvSize = 0;
/* Process received RxTd */
while ((pRxTd->addr.val & EMAC_RXD_bmOWNERSHIP) == EMAC_RXD_bmOWNERSHIP)
{
/* A start of frame has been received, discard previous fragments */
if ((pRxTd->status.val & EMAC_RXD_bmSOF) == EMAC_RXD_bmSOF)
{
/* Skip previous fragment */
while (tmpIdx != pEmacd->wRxI)
{
pRxTd = &pEmacd->pRxD[pEmacd->wRxI];
pRxTd->addr.val &= ~(EMAC_RXD_bmOWNERSHIP);
CIRC_INC(pEmacd->wRxI, pEmacd->wRxListSize);
}
/* Reset the temporary frame pointer */
pTmpFrame = pFrame;
tmpFrameSize = 0;
/* Start to gather buffers in a frame */
isFrame = 1;
}
/* Increment the pointer */
CIRC_INC(tmpIdx, pEmacd->wRxListSize);
asm("nop");
/* Copy data in the frame buffer */
if (isFrame)
{
if (tmpIdx == pEmacd->wRxI)
{
TRACE_INFO("no EOF (Invalid of buffers too small)\n\r");
do
{
pRxTd = &pEmacd->pRxD[pEmacd->wRxI];
pRxTd->addr.val &= ~(EMAC_RXD_bmOWNERSHIP);
CIRC_INC(pEmacd->wRxI, pEmacd->wRxListSize);
} while(tmpIdx != pEmacd->wRxI);
return EMACD_RX_NULL;
}
/* Copy the buffer into the application frame */
bufferLength = EMAC_RX_UNITSIZE;
if ((tmpFrameSize + bufferLength) > frameSize)
{
bufferLength = frameSize - tmpFrameSize;
}
memcpy(pTmpFrame, (void*)(pRxTd->addr.val & EMAC_RXD_ADDR_MASK), bufferLength);
pTmpFrame += bufferLength;
tmpFrameSize += bufferLength;
/* An end of frame has been received, return the data */
if ((pRxTd->status.val & EMAC_RXD_bmEOF) == EMAC_RXD_bmEOF)
{
/* Frame size from the EMAC */
*pRcvSize = (pRxTd->status.val & EMAC_RXD_LEN_MASK);
TRACE_INFO("packet %d-%d (%d)\n\r", pEmacd->wRxI, tmpIdx, *pRcvSize);
/* All data have been copied in the application frame buffer => release TD */
while (pEmacd->wRxI != tmpIdx)
{
pRxTd = &pEmacd->pRxD[pEmacd->wRxI];
pRxTd->addr.val &= ~(EMAC_RXD_bmOWNERSHIP);
CIRC_INC(pEmacd->wRxI, pEmacd->wRxListSize);
}
/* Application frame buffer is too small all data have not been copied */
if (tmpFrameSize < *pRcvSize)
{
TRACE_INFO("size req %u size allocated %u\n\r", (unsigned int)(*pRcvSize), (unsigned int)frameSize);
return EMACD_SIZE_TOO_SMALL;
}
return EMACD_OK;
}
}
/* SOF has not been detected, skip the fragment */
else
{
pRxTd->addr.val &= ~(EMAC_RXD_bmOWNERSHIP);
pEmacd->wRxI = tmpIdx;
}
/* Process the next buffer */
pRxTd = &pEmacd->pRxD[tmpIdx];
}
return EMACD_RX_NULL;
}
/**
* Register/Clear RX callback. Callback will be invoked after the next received
* frame.
*
* When EMACD_Poll() returns EMACD_RX_NULL the application task call
* EMACD_SetRxCallback() to register fRxCb() callback and enters suspend state.
* The callback is in charge to resume the task once a new frame has been
* received. The next time EMACD_Poll() is called, it will be successfull.
*
* This function is usually invoked from the RX callback itself with NULL
* callback, to unregister. Once the callback has resumed the application task,
* there is no need to invoke the callback again.
*
* \param pEmacd Pointer to EMAC Driver instance.
* \param fRxCb RX callback.
*/
void EMACD_SetRxCallback(sEmacd * pEmacd, fEmacdTransferCallback fRxCb)
{
Emac *pHw = pEmacd->pHw;
if (fRxCb == NULL)
{
EMAC_DisableIt(pHw, EMAC_IDR_RCOMP);
pEmacd->fRxCb = NULL;
}
else
{
pEmacd->fRxCb = fRxCb;
EMAC_EnableIt(pHw, EMAC_IER_RCOMP);
}
}
/**
* Register/Clear TX wakeup callback.
*
* When EMACD_Send() returns EMACD_TX_BUSY (all TD busy) the application
* task calls EMACD_SetTxWakeupCallback() to register fWakeup() callback and
* enters suspend state. The callback is in charge to resume the task once
* several TD have been released. The next time EMACD_Send() will be called,
* it shall be successfull.
*
* This function is usually invoked with NULL callback from the TX wakeup
* callback itself, to unregister. Once the callback has resumed the
* application task, there is no need to invoke the callback again.
*
* \param pEmacd Pointer to EMAC Driver instance.
* \param fWakeup Wakeup callback.
* \param bThreshould Number of free TD before wakeup callback invoked.
* \return EMACD_OK, EMACD_PARAM on parameter error.
*/
uint8_t EMACD_SetTxWakeupCallback(sEmacd * pEmacd,
fEmacdWakeupCallback fWakeup,
uint8_t bThreshold)
{
if (fWakeup == NULL)
{
pEmacd->fWakupCb = NULL;
}
else
{
if (bThreshold <= pEmacd->wTxListSize)
{
pEmacd->fWakupCb = fWakeup;
pEmacd->bWakeupThreshold = bThreshold;
}
else
{
return EMACD_PARAM;
}
}
return EMACD_OK;
}