/* ---------------------------------------------------------------------------- | |
* SAM Software Package License | |
* ---------------------------------------------------------------------------- | |
* Copyright (c) 2012, 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 "chip.h" | |
#include <string.h> | |
/** \addtogroup gmacd_defines | |
@{*/ | |
/*---------------------------------------------------------------------------- | |
* Macro | |
*----------------------------------------------------------------------------*/ | |
/** ISO/IEC 14882:2003(E) - 5.6 Multiplicative operators: | |
* The binary / operator yields the quotient, and the binary % operator yields the remainder | |
* from the division of the first expression by the second. | |
* If the second operand of / or % is zero the behavior is undefined; otherwise (a/b)*b + a%b is equal to a. | |
* If both operands are nonnegative then the remainder is nonnegative; | |
* if not, the sign of the remainder is implementation-defined 74). | |
*/ | |
static inline int fixed_mod(int a, int b) | |
{ | |
int rem = a % b; | |
while (rem < 0) | |
rem += b; | |
return rem; | |
} | |
/** Return count in buffer */ | |
#define GCIRC_CNT(head,tail,size) fixed_mod((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 GCIRC_SPACE(head,tail,size) GCIRC_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 GCIRC_CNT_TO_END(head,tail,size) \ | |
({int end = (size) - (tail); \ | |
int n = fixed_mod((head) + end, (size)); \ | |
n < end ? n : end;}) | |
/** Return space available up to the end of the buffer */ | |
#define GCIRC_SPACE_TO_END(head,tail,size) \ | |
({int end = (size) - 1 - (head); \ | |
int n = fixed_mod(end + (tail), (size)); \ | |
n <= end ? n : end+1;}) | |
/** Increment head or tail */ | |
#define GCIRC_INC(headortail,size) \ | |
headortail++; \ | |
if(headortail >= size) { \ | |
headortail = 0; \ | |
} | |
/** Circular buffer is empty ? */ | |
#define GCIRC_EMPTY(head, tail) (head == tail) | |
/** Clear circular buffer */ | |
#define GCIRC_CLEAR(head, tail) (head = tail = 0) | |
/*--------------------------------------------------------------------------- | |
* Local functions | |
*---------------------------------------------------------------------------*/ | |
/** | |
* \brief Disable TX & reset registers and descriptor list | |
* \param pDrv Pointer to GMAC Driver instance. | |
*/ | |
static void GMACD_ResetTx(sGmacd *pDrv, gmacQueList_t queIdx) | |
{ | |
Gmac *pHw = pDrv->pHw; | |
uint8_t *pTxBuffer = pDrv->queueList[queIdx].pTxBuffer; | |
sGmacTxDescriptor *pTd = pDrv->queueList[queIdx].pTxD; | |
uint32_t Index; | |
uint32_t Address; | |
/* Disable TX */ | |
GMAC_TransmitEnable(pHw, 0); | |
/* Setup the TX descriptors. */ | |
GCIRC_CLEAR(pDrv->queueList[queIdx].wTxHead, pDrv->queueList[queIdx].wTxTail); | |
for(Index = 0; Index < pDrv->queueList[queIdx].wTxListSize; Index++) { | |
Address = (uint32_t)(&(pTxBuffer[Index * GMAC_TX_UNITSIZE])); | |
pTd[Index].addr = Address; | |
pTd[Index].status.val = (uint32_t)GMAC_TX_USED_BIT; | |
} | |
pTd[pDrv->queueList[queIdx].wTxListSize - 1].status.val = GMAC_TX_USED_BIT | GMAC_TX_WRAP_BIT; | |
/* Transmit Buffer Queue Pointer Register */ | |
GMAC_SetTxQueue(pHw, (uint32_t)pTd, queIdx); | |
} | |
/** | |
* \brief Disable RX & reset registers and descriptor list | |
* \param pDrv Pointer to GMAC Driver instance. | |
*/ | |
static void GMACD_ResetRx(sGmacd *pDrv, gmacQueList_t queIdx ) | |
{ | |
Gmac *pHw = pDrv->pHw; | |
uint8_t *pRxBuffer = pDrv->queueList[queIdx].pRxBuffer; | |
sGmacRxDescriptor *pRd = pDrv->queueList[queIdx].pRxD; | |
uint32_t Index; | |
uint32_t Address; | |
/* Disable RX */ | |
GMAC_ReceiveEnable(pHw, 0); | |
/* Setup the RX descriptors. */ | |
pDrv->queueList[queIdx].wRxI = 0; | |
for(Index = 0; Index < pDrv->queueList[queIdx].wRxListSize; Index++) | |
{ | |
Address = (uint32_t)(&(pRxBuffer[Index * GMAC_RX_UNITSIZE])); | |
/* Remove GMAC_RXD_bmOWNERSHIP and GMAC_RXD_bmWRAP */ | |
pRd[Index].addr.val = Address & GMAC_ADDRESS_MASK; | |
pRd[Index].status.val = 0; | |
} | |
pRd[pDrv->queueList[queIdx].wRxListSize - 1].addr.val |= GMAC_RX_WRAP_BIT; | |
/* Receive Buffer Queue Pointer Register */ | |
GMAC_SetRxQueue(pHw, (uint32_t) pRd, queIdx); | |
} | |
/** | |
* \brief Process successfully sent packets | |
* \param pGmacd Pointer to GMAC Driver instance. | |
*/ | |
static void GMACD_TxCompleteHandler(sGmacd *pGmacd, gmacQueList_t qId) | |
{ | |
Gmac *pHw = pGmacd->pHw; | |
sGmacTxDescriptor *pTxTd; | |
fGmacdTransferCallback fTxCb; | |
uint32_t tsr; | |
/* Clear status */ | |
tsr = GMAC_GetTxStatus(pHw); | |
GMAC_ClearTxStatus(pHw, tsr); | |
while (!GCIRC_EMPTY(pGmacd->queueList[qId].wTxHead, pGmacd->queueList[qId].wTxTail)) { | |
pTxTd = &pGmacd->queueList[qId].pTxD[pGmacd->queueList[qId].wTxTail]; | |
/* Make hw descriptor updates visible to CPU */ | |
memory_barrier(); | |
/* Exit if frame has not been sent yet: | |
* On TX completion, the GMAC set the USED bit only into the | |
* very first buffer descriptor of the sent frame. | |
* Otherwise it updates this descriptor with status error bits. | |
* This is the descriptor writeback. | |
*/ | |
if ((pTxTd->status.val & GMAC_TX_USED_BIT) == 0) | |
break; | |
/* Process all buffers of the current transmitted frame */ | |
while ((pTxTd->status.val & GMAC_TX_LAST_BUFFER_BIT) == 0) { | |
GCIRC_INC(pGmacd->queueList[qId].wTxTail, pGmacd->queueList[qId].wTxListSize); | |
pTxTd = &pGmacd->queueList[qId].pTxD[pGmacd->queueList[qId].wTxTail]; | |
} | |
/* Notify upper layer that a frame has been sent */ | |
fTxCb = pGmacd->queueList[qId].fTxCbList[pGmacd->queueList[qId].wTxTail]; | |
if (fTxCb) | |
fTxCb(tsr); | |
/* Go to next frame */ | |
GCIRC_INC(pGmacd->queueList[qId].wTxTail, pGmacd->queueList[qId].wTxListSize); | |
} | |
/* If a wakeup has been scheduled, notify upper layer that it can | |
send other packets, send will be successfull. */ | |
if (pGmacd->queueList[qId].fWakupCb && | |
GCIRC_SPACE(pGmacd->queueList[qId].wTxHead, | |
pGmacd->queueList[qId].wTxTail, | |
pGmacd->queueList[qId].wTxListSize) >= pGmacd->queueList[qId].bWakeupThreshold) | |
pGmacd->queueList[qId].fWakupCb(); | |
} | |
/** | |
* \brief Reset TX queue when errors are detected | |
* \param pGmacd Pointer to GMAC Driver instance. | |
*/ | |
static void GMACD_TxErrorHandler(sGmacd *pGmacd, gmacQueList_t qId) | |
{ | |
Gmac *pHw = pGmacd->pHw; | |
sGmacTxDescriptor *pTxTd; | |
fGmacdTransferCallback fTxCb; | |
uint32_t tsr; | |
/* Clear TXEN bit into the Network Configuration Register: | |
* this is a workaround to recover from TX lockups that | |
* occur on sama5d3 gmac (r1p24f2) when using scatter-gather. | |
* This issue has never been seen on sama5d4 gmac (r1p31). | |
*/ | |
GMAC_TransmitEnable(pHw, 0); | |
/* The following step should be optional since this function is called | |
* directly by the IRQ handler. Indeed, according to Cadence | |
* documentation, the transmission is halted on errors such as | |
* too many retries or transmit under run. | |
* However it would become mandatory if the call of this function | |
* were scheduled as a task by the IRQ handler (this is how Linux | |
* driver works). Then this function might compete with GMACD_Send(). | |
* | |
* Setting bit 10, tx_halt, of the Network Control Register is not enough: | |
* We should wait for bit 3, tx_go, of the Transmit Status Register to | |
* be cleared at transmit completion if a frame is being transmitted. | |
*/ | |
GMAC_TransmissionHalt(pHw); | |
while (GMAC_GetTxStatus(pHw) & GMAC_TSR_TXGO); | |
/* Treat frames in TX queue including the ones that caused the error. */ | |
while (!GCIRC_EMPTY(pGmacd->queueList[qId].wTxHead, pGmacd->queueList[qId].wTxTail)) { | |
int tx_completed = 0; | |
pTxTd = &pGmacd->queueList[qId].pTxD[pGmacd->queueList[qId].wTxTail]; | |
/* Make hw descriptor updates visible to CPU */ | |
memory_barrier(); | |
/* Check USED bit on the very first buffer descriptor to validate | |
* TX completion. | |
*/ | |
if (pTxTd->status.val & GMAC_TX_USED_BIT) | |
tx_completed = 1; | |
/* Go to the last buffer descriptor of the frame */ | |
while ((pTxTd->status.val & GMAC_TX_LAST_BUFFER_BIT) == 0) { | |
GCIRC_INC(pGmacd->queueList[qId].wTxTail, pGmacd->queueList[qId].wTxListSize); | |
pTxTd = &pGmacd->queueList[qId].pTxD[pGmacd->queueList[qId].wTxTail]; | |
} | |
/* Notify upper layer that a frame status */ | |
fTxCb = pGmacd->queueList[qId].fTxCbList[pGmacd->queueList[qId].wTxTail]; | |
if (fTxCb) | |
fTxCb(tx_completed ? GMAC_TSR_TXCOMP : 0); // TODO: which error to notify? | |
/* Go to next frame */ | |
GCIRC_INC(pGmacd->queueList[qId].wTxTail, pGmacd->queueList[qId].wTxListSize); | |
} | |
/* Reset TX queue */ | |
GMACD_ResetTx(pGmacd, qId); | |
/* Clear status */ | |
tsr = GMAC_GetTxStatus(pHw); | |
GMAC_ClearTxStatus(pHw, tsr); | |
/* Now we are ready to start transmission again */ | |
GMAC_TransmitEnable(pHw, 1); | |
if (pGmacd->queueList[qId].fWakupCb) | |
pGmacd->queueList[qId].fWakupCb(); | |
} | |
/*--------------------------------------------------------------------------- | |
* Exported functions | |
*---------------------------------------------------------------------------*/ | |
/** | |
* \brief GMAC Interrupt handler | |
* \param pGmacd Pointer to GMAC Driver instance. | |
*/ | |
void GMACD_Handler(sGmacd *pGmacd, gmacQueList_t queIdx) | |
{ | |
Gmac *pHw = pGmacd->pHw; | |
uint32_t isr; | |
uint32_t rsr; | |
/* Interrupt Status Register is cleared on read */ | |
while ( (isr = GMAC_GetItStatus(pHw, queIdx)) !=0) { | |
/* RX packet */ | |
if (isr & GMAC_INT_RX_BITS) { | |
/* Clear status */ | |
rsr = GMAC_GetRxStatus(pHw); | |
GMAC_ClearRxStatus(pHw, rsr); | |
/* Invoke callback */ | |
if (pGmacd->queueList[queIdx].fRxCb) | |
pGmacd->queueList[queIdx].fRxCb(rsr); | |
} | |
/* TX error */ | |
if (isr & GMAC_INT_TX_ERR_BITS) { | |
GMACD_TxErrorHandler(pGmacd, queIdx); | |
break; | |
} | |
/* TX packet */ | |
if (isr & GMAC_IER_TCOMP) | |
GMACD_TxCompleteHandler(pGmacd, queIdx); | |
if (isr & GMAC_IER_HRESP) { | |
TRACE_ERROR("HRESP\n\r"); | |
} | |
} | |
} | |
/** | |
* \brief Initialize the GMAC with the Gmac controller address | |
* \param pGmacd Pointer to GMAC Driver instance. | |
* \param pHw Pointer to HW address for registers. | |
* \param bID HW ID for power management | |
* \param enableCAF Enable/Disable CopyAllFrame. | |
* \param enableNBC Enable/Disable NoBroadCast. | |
*/ | |
void GMACD_Init(sGmacd *pGmacd, | |
Gmac *pHw, | |
uint8_t bID, | |
uint8_t enableCAF, | |
uint8_t enableNBC ) | |
{ | |
uint32_t dwNcfgr, dwDcfgr; | |
/* Check parameters */ | |
// assert(GRX_BUFFERS * GMAC_RX_UNITSIZE > GMAC_FRAME_LENTGH_MAX); | |
TRACE_DEBUG("GMAC_Init\n\r"); | |
/* Initialize struct */ | |
pGmacd->pHw = pHw; | |
pGmacd->bId = bID; | |
/* Power ON */ | |
PMC_EnablePeripheral(bID); | |
/* Disable TX & RX and more */ | |
GMAC_NetworkControl(pHw, 0); | |
GMAC_DisableAllQueueIt(pHw, ~0u); | |
GMAC_ClearStatistics(pHw); | |
/* Clear all status bits in the receive status register. */ | |
GMAC_ClearRxStatus(pHw, GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA |GMAC_RSR_HNO); | |
/* Clear all status bits in the transmit status register */ | |
GMAC_ClearTxStatus(pHw, GMAC_TSR_UBR | GMAC_TSR_COL | GMAC_TSR_RLE | |
| GMAC_TSR_TXGO | GMAC_TSR_TFC | GMAC_TSR_TXCOMP | |
| GMAC_TSR_UND | GMAC_TSR_HRESP ); | |
/* Clear All interrupts */ | |
GMAC_GetItStatus(pHw, GMAC_QUE_0); | |
GMAC_GetItStatus(pHw, GMAC_QUE_1); | |
GMAC_GetItStatus(pHw, GMAC_QUE_2); | |
/* Enable the copy of data into the buffers | |
ignore broadcasts, and don't copy FCS. */ | |
dwNcfgr = GMAC_NCFGR_FD | GMAC_NCFGR_DBW(0) | GMAC_NCFGR_CLK_MCK_64; | |
/* enable 1536 buffer */ | |
// dwNcfgr |= GMAC_NCFGR_MAXFS; | |
if( enableCAF ) { | |
dwNcfgr |= GMAC_NCFGR_CAF; | |
} | |
if( enableNBC ) { | |
dwNcfgr |= GMAC_NCFGR_NBC; | |
} | |
dwDcfgr = (GMAC_DCFGR_DRBS(8) | (0<<8) | (0<<10) ); | |
GMAC_Configure(pHw, dwNcfgr); | |
GMAC_DmaConfigure(pHw, dwDcfgr); | |
} | |
/** | |
* Initialize necessary allocated buffer lists for GMAC Driver to transfer data. | |
* Must be invoked after GMACD_Init() but before RX/TX start. | |
* \param pGmacd Pointer to GMAC Driver instance. | |
* \param pRxBuffer Pointer to allocated buffer for RX. The address should | |
* be 8-byte aligned and the size should be | |
* GMAC_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 | |
* GMAC_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 GMACD_OK or GMACD_PARAM. | |
* \note If input address is not 8-byte aligned the address is automatically | |
* adjusted and the list size is reduced by one. | |
*/ | |
uint8_t GMACD_InitTransfer( sGmacd *pGmacd, | |
uint8_t *pRxBuffer, sGmacRxDescriptor *pRxD, | |
uint16_t wRxSize, | |
uint8_t *pTxBuffer, sGmacTxDescriptor *pTxD, fGmacdTransferCallback *pTxCb, | |
uint16_t wTxSize, | |
gmacQueList_t queIdx) | |
{ | |
Gmac *pHw = pGmacd->pHw; | |
if (wRxSize <= 1 || wTxSize <= 1 || pTxCb == NULL) return GMACD_PARAM; | |
/* Assign RX buffers */ | |
if ( ((uint32_t)pRxBuffer & 0x7) | |
|| ((uint32_t)pRxD & 0x7) ) | |
{ | |
wRxSize --; | |
TRACE_DEBUG("RX list address adjusted\n\r"); | |
} | |
pGmacd->queueList[queIdx].pRxBuffer = (uint8_t*)((uint32_t)pRxBuffer & 0xFFFFFFF8); | |
pGmacd->queueList[queIdx].pRxD = (sGmacRxDescriptor*)((uint32_t)pRxD & 0xFFFFFFF8); | |
pGmacd->queueList[queIdx].wRxListSize = wRxSize; | |
/* Assign TX buffers */ | |
if ( ((uint32_t)pTxBuffer & 0x7) | |
|| ((uint32_t)pTxD & 0x7) ) | |
{ | |
wTxSize --; | |
TRACE_DEBUG("TX list address adjusted\n\r"); | |
} | |
pGmacd->queueList[queIdx].pTxBuffer = (uint8_t*)((uint32_t)pTxBuffer & 0xFFFFFFF8); | |
pGmacd->queueList[queIdx].pTxD = (sGmacTxDescriptor*)((uint32_t)pTxD & 0xFFFFFFF8); | |
pGmacd->queueList[queIdx].wTxListSize = wTxSize; | |
pGmacd->queueList[queIdx].fTxCbList = pTxCb; | |
/* Reset TX & RX */ | |
GMACD_ResetRx(pGmacd, queIdx); | |
GMACD_ResetTx(pGmacd, queIdx); | |
/* Setup the interrupts for RX/TX completion (and errors) */ | |
switch(queIdx) | |
{ | |
case GMAC_QUE_0: | |
/* YBP: Que 0 should be configured last so as to enable transmit and Receive in the NCR register */ | |
/* Enable Rx and Tx, plus the stats register. */ | |
GMAC_TransmitEnable(pHw, 1); | |
GMAC_ReceiveEnable(pHw, 1); | |
GMAC_StatisticsWriteEnable(pHw, 1); | |
GMAC_EnableIt(pHw, | |
GMAC_INT_RX_BITS | | |
GMAC_INT_TX_BITS | | |
GMAC_INT_TX_ERR_BITS, GMAC_QUE_0); | |
break; | |
case GMAC_QUE_1: | |
GMAC_EnableIt(pHw, | |
GMAC_INT_RX_BITS | | |
GMAC_INT_TX_BITS | | |
GMAC_INT_TX_ERR_BITS, GMAC_QUE_1); | |
break; | |
case GMAC_QUE_2: | |
GMAC_EnableIt(pHw, | |
GMAC_INT_RX_BITS | | |
GMAC_INT_TX_BITS | | |
GMAC_INT_TX_ERR_BITS, GMAC_QUE_2); | |
break; | |
}; | |
return GMACD_OK; | |
} | |
/** | |
* Reset TX & RX queue & statistics | |
* \param pGmacd Pointer to GMAC Driver instance. | |
*/ | |
void GMACD_Reset(sGmacd *pGmacd) | |
{ | |
Gmac *pHw = pGmacd->pHw; | |
GMACD_ResetRx(pGmacd, GMAC_QUE_0); | |
GMACD_ResetRx(pGmacd, GMAC_QUE_1); | |
GMACD_ResetRx(pGmacd, GMAC_QUE_2); | |
GMACD_ResetTx(pGmacd, GMAC_QUE_0); | |
GMACD_ResetTx(pGmacd, GMAC_QUE_1); | |
GMACD_ResetTx(pGmacd, GMAC_QUE_2); | |
//memset((void*)&GmacStatistics, 0x00, sizeof(GmacStats)); | |
GMAC_NetworkControl(pHw, GMAC_NCR_TXEN | GMAC_NCR_RXEN | |
| GMAC_NCR_WESTAT | GMAC_NCR_CLRSTAT); | |
} | |
/** | |
* \brief Send a frame splitted into buffers. If the frame size is larger than transfer buffer size | |
* error returned. If frame transfer status is monitored, specify callback for each frame. | |
* \param pGmacd Pointer to GMAC Driver instance. | |
* \param sgl Pointer to a scatter-gather list describing the buffers of the ethernet frame. | |
*/ | |
uint8_t GMACD_SendSG(sGmacd *pGmacd, | |
const sGmacSGList *sgl, | |
fGmacdTransferCallback fTxCb, | |
gmacQueList_t queIdx) | |
{ | |
Gmac *pHw = pGmacd->pHw; | |
sGmacTxDescriptor *pTd = pGmacd->queueList[queIdx].pTxD; | |
sGmacTxDescriptor *pTxTd; | |
uint16_t wTxPos, wTxHead; | |
int i; | |
TRACE_DEBUG("%s\n\r", __FUNCTION__); | |
/* Check parameter */ | |
if (!sgl->len) { | |
TRACE_ERROR("%s:: ethernet frame is empty.\r\n", __FUNCTION__); | |
return GMACD_PARAM; | |
} | |
if (sgl->len >= pGmacd->queueList[queIdx].wTxListSize) { | |
TRACE_ERROR("%s: ethernet frame has too many buffers.\r\n", __FUNCTION__); | |
return GMACD_PARAM; | |
} | |
/* Check available space */ | |
if (GCIRC_SPACE(pGmacd->queueList[queIdx].wTxHead, pGmacd->queueList[queIdx].wTxTail, pGmacd->queueList[queIdx].wTxListSize) < (int)sgl->len) | |
return GMACD_TX_BUSY; | |
/* Tag end of TX queue */ | |
wTxHead = fixed_mod(pGmacd->queueList[queIdx].wTxHead + sgl->len, pGmacd->queueList[queIdx].wTxListSize); | |
wTxPos = wTxHead; | |
pGmacd->queueList[queIdx].fTxCbList[wTxPos] = NULL; | |
pTxTd = &pTd[wTxPos]; | |
pTxTd->status.val = GMAC_TX_USED_BIT; | |
/* Update buffer descriptors in reverse order to avoid a race | |
* condition with hardware. | |
*/ | |
for (i = (int)(sgl->len-1); i >= 0; --i) { | |
const sGmacSG *sg = &sgl->sg[i]; | |
uint32_t status; | |
if (sg->size > GMAC_TX_UNITSIZE) { | |
TRACE_ERROR("%s: buffer size is too big.\r\n", __FUNCTION__); | |
return GMACD_PARAM; | |
} | |
if (wTxPos == 0) | |
wTxPos = pGmacd->queueList[queIdx].wTxListSize-1; | |
else | |
wTxPos--; | |
/* Reset TX callback */ | |
pGmacd->queueList[queIdx].fTxCbList[wTxPos] = NULL; | |
pTxTd = &pTd[wTxPos]; | |
#ifdef GMAC_ZERO_COPY | |
/** Update buffer descriptor address word: | |
* MUST be done before status word to avoid a race condition. | |
*/ | |
pTxTd->addr = (uint32_t)sg->pBuffer; | |
#else | |
/* Copy data into transmittion buffer */ | |
if (sg->pBuffer && sg->size) | |
memcpy((void *)pTxTd->addr, sg->pBuffer, sg->size); | |
#endif | |
/* Compute buffer descriptor status word */ | |
status = sg->size & GMAC_LENGTH_FRAME; | |
if (i == (int)(sgl->len-1)) { | |
status |= GMAC_TX_LAST_BUFFER_BIT; | |
pGmacd->queueList[queIdx].fTxCbList[wTxPos] = fTxCb; | |
} | |
if (wTxPos == pGmacd->queueList[queIdx].wTxListSize-1) | |
status |= GMAC_TX_WRAP_BIT; | |
/* Update buffer descriptor status word: clear USED bit */ | |
pTxTd->status.val = status; | |
/* Make newly initialized descriptor visible to hardware */ | |
memory_barrier(); | |
} | |
/* Update TX ring buffer pointers */ | |
pGmacd->queueList[queIdx].wTxHead = wTxHead; | |
memory_barrier(); | |
/* Now start to transmit if it is not already done */ | |
GMAC_TransmissionStart(pHw); | |
return GMACD_OK; | |
} | |
/** | |
* \brief Send a packet with GMAC. If the packet size is larger than transfer buffer size | |
* error returned. If packet transfer status is monitored, specify callback for each packet. | |
* \param pGmacd Pointer to GMAC Driver instance. | |
* \param buffer The buffer to be send | |
* \param size The size of buffer to be send | |
* \param fGMAC_TxCallback Threshold Wakeup callback | |
* \param fWakeUpCb TX Wakeup | |
* \return OK, Busy or invalid packet | |
*/ | |
uint8_t GMACD_Send(sGmacd *pGmacd, | |
void *pBuffer, | |
uint32_t size, | |
fGmacdTransferCallback fTxCb, | |
gmacQueList_t queIdx) | |
{ | |
sGmacSGList sgl; | |
sGmacSG sg; | |
/* Init single entry scatter-gather list */ | |
sg.size = size; | |
sg.pBuffer = pBuffer; | |
sgl.len = 1; | |
sgl.sg = &sg; | |
return GMACD_SendSG(pGmacd, &sgl, fTxCb, queIdx); | |
} | |
/** | |
* Return current load of TX. | |
* \param pGmacd Pointer to GMAC Driver instance. | |
*/ | |
uint32_t GMACD_TxLoad(sGmacd *pGmacd, gmacQueList_t queIdx) | |
{ | |
uint16_t head = pGmacd->queueList[queIdx].wTxHead; | |
uint16_t tail = pGmacd->queueList[queIdx].wTxTail; | |
return GCIRC_CNT(head, tail, pGmacd->queueList[queIdx].wTxListSize); | |
} | |
/** | |
* \brief Receive a packet with GMAC. | |
* If not enough buffer for the packet, the remaining data is lost but right | |
* frame length is returned. | |
* \param pGmacd Pointer to GMAC 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 GMACD_Poll(sGmacd * pGmacd, | |
uint8_t *pFrame, | |
uint32_t frameSize, | |
uint32_t *pRcvSize, | |
gmacQueList_t queIdx) | |
{ | |
uint16_t bufferLength; | |
uint32_t tmpFrameSize = 0; | |
uint8_t *pTmpFrame = 0; | |
uint32_t tmpIdx = pGmacd->queueList[queIdx].wRxI; | |
volatile sGmacRxDescriptor *pRxTd = &pGmacd->queueList[queIdx].pRxD[pGmacd->queueList[queIdx].wRxI]; | |
uint8_t isFrame = 0; | |
if (pFrame == NULL) return GMACD_PARAM; | |
/* Set the default return value */ | |
*pRcvSize = 0; | |
/* Process received RxTd */ | |
while ((pRxTd->addr.val & GMAC_RX_OWNERSHIP_BIT) == GMAC_RX_OWNERSHIP_BIT) | |
{ | |
/* A start of frame has been received, discard previous fragments */ | |
if ((pRxTd->status.val & GMAC_RX_SOF_BIT) == GMAC_RX_SOF_BIT) | |
{ | |
/* Skip previous fragment */ | |
while (tmpIdx != pGmacd->queueList[queIdx].wRxI) | |
{ | |
pRxTd = &pGmacd->queueList[queIdx].pRxD[pGmacd->queueList[queIdx].wRxI]; | |
pRxTd->addr.val &= ~(GMAC_RX_OWNERSHIP_BIT); | |
GCIRC_INC(pGmacd->queueList[queIdx].wRxI, pGmacd->queueList[queIdx].wRxListSize); | |
} | |
pTmpFrame = pFrame; | |
tmpFrameSize = 0; | |
/* Start to gather buffers in a frame */ | |
isFrame = 1; | |
} | |
/* Increment the pointer */ | |
GCIRC_INC(tmpIdx, pGmacd->queueList[queIdx].wRxListSize); | |
/* Copy data in the frame buffer */ | |
if (isFrame) { | |
if (tmpIdx == pGmacd->queueList[queIdx].wRxI) | |
{ | |
TRACE_INFO("no EOF (Invalid of buffers too small)\n\r"); | |
do { | |
pRxTd = &pGmacd->queueList[queIdx].pRxD[pGmacd->queueList[queIdx].wRxI]; | |
pRxTd->addr.val &= ~(GMAC_RX_OWNERSHIP_BIT); | |
GCIRC_INC(pGmacd->queueList[queIdx].wRxI, pGmacd->queueList[queIdx].wRxListSize); | |
} while(tmpIdx != pGmacd->queueList[queIdx].wRxI); | |
return GMACD_RX_NULL; | |
} | |
/* Copy the buffer into the application frame */ | |
bufferLength = GMAC_RX_UNITSIZE; | |
if ((tmpFrameSize + bufferLength) > frameSize) | |
{ | |
bufferLength = frameSize - tmpFrameSize; | |
} | |
memcpy(pTmpFrame, (void*)(pRxTd->addr.val & GMAC_ADDRESS_MASK), bufferLength); | |
pTmpFrame += bufferLength; | |
tmpFrameSize += bufferLength; | |
/* An end of frame has been received, return the data */ | |
if ((pRxTd->status.val & GMAC_RX_EOF_BIT) == GMAC_RX_EOF_BIT) | |
{ | |
/* Frame size from the GMAC */ | |
*pRcvSize = (pRxTd->status.val & GMAC_LENGTH_FRAME); | |
/* Application frame buffer is too small all data have not been copied */ | |
if (tmpFrameSize < *pRcvSize) { | |
return GMACD_SIZE_TOO_SMALL; | |
} | |
TRACE_DEBUG("packet %d-%d (%d)\n\r", pGmacd->queueList[queIdx].wRxI, tmpIdx, *pRcvSize); | |
/* All data have been copied in the application frame buffer => release TD */ | |
while (pGmacd->queueList[queIdx].wRxI != tmpIdx) | |
{ | |
pRxTd = &pGmacd->queueList[queIdx].pRxD[pGmacd->queueList[queIdx].wRxI]; | |
pRxTd->addr.val &= ~(GMAC_RX_OWNERSHIP_BIT); | |
GCIRC_INC(pGmacd->queueList[queIdx].wRxI, pGmacd->queueList[queIdx].wRxListSize); | |
} | |
return GMACD_OK; | |
} | |
} | |
/* SOF has not been detected, skip the fragment */ | |
else { | |
pRxTd->addr.val &= ~(GMAC_RX_OWNERSHIP_BIT); | |
pGmacd->queueList[queIdx].wRxI = tmpIdx; | |
} | |
/* Process the next buffer */ | |
pRxTd = &pGmacd->queueList[queIdx].pRxD[tmpIdx]; | |
memory_barrier(); | |
} | |
return GMACD_RX_NULL; | |
} | |
/** | |
* \brief Registers pRxCb callback. Callback will be invoked after the next received | |
* frame. When GMAC_Poll() returns GMAC_RX_NO_DATA the application task call GMAC_Set_RxCb() | |
* to register pRxCb() callback and enters suspend state. The callback is in charge | |
* to resume the task once a new frame has been received. The next time GMAC_Poll() | |
* is called, it will be successfull. | |
* \param pGmacd Pointer to GMAC Driver instance. | |
* \param pRxCb Pointer to callback function | |
* \return OK, no data, or frame too small | |
*/ | |
void GMACD_SetRxCallback(sGmacd * pGmacd, fGmacdTransferCallback fRxCb, gmacQueList_t queIdx) | |
{ | |
Gmac *pHw = pGmacd->pHw; | |
if (fRxCb == NULL) | |
{ | |
GMAC_DisableIt(pHw, GMAC_IDR_RCOMP, queIdx); | |
pGmacd->queueList[queIdx].fRxCb = NULL; | |
} | |
else | |
{ | |
pGmacd->queueList[queIdx].fRxCb = fRxCb; | |
GMAC_EnableIt(pHw, GMAC_IER_RCOMP, queIdx); | |
} | |
} | |
/** | |
* Register/Clear TX wakeup callback. | |
* | |
* When GMACD_Send() returns GMACD_TX_BUSY (all TD busy) the application | |
* task calls GMACD_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 GMACD_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 pGmacd Pointer to GMAC Driver instance. | |
* \param fWakeup Wakeup callback. | |
* \param bThreshould Number of free TD before wakeup callback invoked. | |
* \return GMACD_OK, GMACD_PARAM on parameter error. | |
*/ | |
uint8_t GMACD_SetTxWakeupCallback(sGmacd * pGmacd, | |
fGmacdWakeupCallback fWakeup, | |
uint8_t bThreshold, | |
gmacQueList_t queIdx) | |
{ | |
if (fWakeup == NULL) | |
{ | |
pGmacd->queueList[queIdx].fWakupCb = NULL; | |
} | |
else | |
{ | |
if (bThreshold <= pGmacd->queueList[queIdx].wTxListSize) | |
{ | |
pGmacd->queueList[queIdx].fWakupCb = fWakeup; | |
pGmacd->queueList[queIdx].bWakeupThreshold = bThreshold; | |
} | |
else | |
{ | |
return GMACD_PARAM; | |
} | |
} | |
return GMACD_OK; | |
} | |
//uint32_t GMACD_GetQ0BuffBaseAddr (uint8_t ** baseAddrPtr) | |
//{ | |
// *baseAddrPtr = &gs_uc_rx_buffer[0u]; | |
// return sizeof(gs_uc_rx_buffer); | |
//} | |
// | |
//uint32_t GMACD_GetQ1BuffBaseAddr (uint8_t ** baseAddrPtr) | |
//{ | |
// *baseAddrPtr = &gs_uc_q1_rx_buffer[0u]; | |
// return sizeof(gs_uc_q1_rx_buffer); | |
//} | |
// | |
//uint32_t GMACD_GetQ2BuffBaseAddr (uint8_t ** baseAddrPtr) | |
//{ | |
// *baseAddrPtr = &gs_uc_q2_rx_buffer[0u]; | |
// return sizeof(gs_uc_q2_rx_buffer); | |
//} |