/** * \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 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; }