/** * \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 ) { Gmac *pHw = pGmacd->pHw; sGmacTxDescriptor *pTxTd; volatile fGmacdTransferCallback *pfTxCb; TRACE_DEBUG("GMAC_Send\n\r"); /* Check parameter */ if (size > GMAC_TX_UNITSIZE) { TRACE_ERROR("GMAC driver does not split send packets."); return GMACD_PARAM; } /* Pointers to the current TxTd */ pTxTd = &pGmacd->pTxD[pGmacd->wTxHead]; /* If no free TxTd, buffer can't be sent */ if( GCIRC_SPACE(pGmacd->wTxHead, pGmacd->wTxTail, pGmacd->wTxListSize) == 0) return GMACD_TX_BUSY; /* Pointers to the current Tx Callback */ pfTxCb = &pGmacd->fTxCbList[pGmacd->wTxHead]; /* Sanity check */ /* 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 (pGmacd->wTxHead == pGmacd->wTxListSize-1) { pTxTd->status.val = (size & GMAC_LENGTH_FRAME) | GMAC_TX_LAST_BUFFER_BIT | GMAC_TX_WRAP_BIT; } else { pTxTd->status.val = (size & GMAC_LENGTH_FRAME) | GMAC_TX_LAST_BUFFER_BIT; } GCIRC_INC(pGmacd->wTxHead, pGmacd->wTxListSize); //CP15_flush_dcache_for_dma ((uint32_t)(pTxTd), ((uint32_t)(pTxTd) + sizeof(pTxTd))); /* Tx packets count */ /* Now start to transmit if it is not already done */ GMAC_TransmissionStart(pHw); return GMACD_OK; }
/** * \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 */ GMAC_CACHE_COHERENCE /* 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 write back. */ 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]; memory_sync(); } /* 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 successful. */ 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 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 GMAC Interrupt handler * \param pGmacd Pointer to GMAC Driver instance. */ void GMACD_Handler(sGmacd *pGmacd ) { Gmac *pHw = pGmacd->pHw; sGmacTxDescriptor *pTxTd; fGmacdTransferCallback *pTxCb = NULL; uint32_t isr; uint32_t rsr; uint32_t tsr; uint32_t rxStatusFlag; uint32_t txStatusFlag; isr = GMAC_GetItStatus(pHw); rsr = GMAC_GetRxStatus(pHw); tsr = GMAC_GetTxStatus(pHw); isr &= ~(GMAC_GetItMask(pHw)| 0xF8030300); /* RX packet */ if ((isr & GMAC_ISR_RCOMP) || (rsr & GMAC_RSR_REC)) { asm("nop"); rxStatusFlag = GMAC_RSR_REC; /* Frame received */ /* Check OVR */ if (rsr & GMAC_RSR_RXOVR) { rxStatusFlag |= GMAC_RSR_RXOVR; } /* Check BNA */ if (rsr & GMAC_RSR_BNA) { rxStatusFlag |= GMAC_RSR_BNA; } /* Check HNO */ if (rsr & GMAC_RSR_HNO) { rxStatusFlag |= GMAC_RSR_HNO; } /* Clear status */ GMAC_ClearRxStatus(pHw, rxStatusFlag); /* Invoke callbacks */ if (pGmacd->fRxCb) { pGmacd->fRxCb(rxStatusFlag); } } /* TX packet */ if ((isr & GMAC_ISR_TCOMP) || (tsr & GMAC_TSR_TXCOMP)) { asm("nop"); txStatusFlag = GMAC_TSR_TXCOMP; /* A frame transmitted Check RLE */ if (tsr & GMAC_TSR_RLE) { /* Status RLE & Number of discarded buffers */ txStatusFlag = GMAC_TSR_RLE | GCIRC_CNT(pGmacd->wTxHead, pGmacd->wTxTail, pGmacd->wTxListSize); pTxCb = &pGmacd->fTxCbList[pGmacd->wTxTail]; GMACD_ResetTx(pGmacd); TRACE_INFO("Tx RLE!!\n\r"); GMAC_TransmitEnable(pHw, 1); } /* Check COL */ if (tsr & GMAC_TSR_COL) { txStatusFlag |= GMAC_TSR_COL; } /* Check TFC */ if (tsr & GMAC_TSR_TFC) { txStatusFlag |= GMAC_TSR_TFC; } /* Check UND */ if (tsr & GMAC_TSR_UND) { txStatusFlag |= GMAC_TSR_UND; } /* Check HRESP */ if (tsr & GMAC_TSR_HRESP) { txStatusFlag |= GMAC_TSR_HRESP; } /* Check LCO */ if (tsr & GMAC_TSR_LCO) { txStatusFlag |= GMAC_TSR_LCO; } /* Clear status */ GMAC_ClearTxStatus(pHw, txStatusFlag); if (!GCIRC_EMPTY(pGmacd->wTxHead, pGmacd->wTxTail)) { /* Check the buffers */ do { pTxTd = &pGmacd->pTxD[pGmacd->wTxTail]; pTxCb = &pGmacd->fTxCbList[pGmacd->wTxTail]; /* Exit if buffer has not been sent yet */ if ((pTxTd->status.val & (uint32_t)GMAC_TX_USED_BIT) == 0) { break; } /* Notify upper layer that a packet has been sent */ if (*pTxCb) { (*pTxCb)(txStatusFlag); } GCIRC_INC( pGmacd->wTxTail, pGmacd->wTxListSize ); } while (GCIRC_CNT(pGmacd->wTxHead, pGmacd->wTxTail, pGmacd->wTxListSize)); } if (tsr & GMAC_TSR_RLE) { /* 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((GCIRC_SPACE(pGmacd->wTxHead, pGmacd->wTxTail, pGmacd->wTxListSize) >= pGmacd->bWakeupThreshold) && pGmacd->fWakupCb) { pGmacd->fWakupCb(); } } /* PAUSE Frame */ if (isr & GMAC_ISR_PFNZ) TRACE_INFO("Pause!\n\r"); if (isr & GMAC_ISR_PTZ) TRACE_INFO("Pause TO!\n\r"); }