/** * \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 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 gmacd Pointer to GMAC Driver instance. * \param sgl Pointer to a scatter-gather list describing the buffers of the ethernet frame. * \param fTxCb Pointer to callback function. */ uint8_t gmacd_send_sg(struct _gmacd* gmacd, uint8_t queue, const struct _gmac_sg_list* sgl, gmacd_callback_t callback) { Gmac* gmac = gmacd->gmac; struct _gmacd_queue* q = &gmacd->queues[queue]; struct _gmac_desc* desc; uint16_t idx, tx_head; int i; if (callback && !q->tx_callbacks) { trace_error("Cannot set send callback, no tx_callbacks "\ "buffer configured for queue %u", queue); } /* Check parameter */ if (!sgl->size) { trace_error("gmacd_send_sg: ethernet frame is empty.\r\n"); return GMACD_PARAM; } if (sgl->size >= q->tx_size) { trace_error("gmacd_send_sg: ethernet frame has too many buffers.\r\n"); return GMACD_PARAM; } /* Check available space */ if (RING_SPACE(q->tx_head, q->tx_tail, q->tx_size) < sgl->size) { trace_error("gmacd_send_sg: not enough free buffers in TX queue.\r\n"); return GMACD_TX_BUSY; } /* Tag end of TX queue */ tx_head = fixed_mod(q->tx_head + sgl->size, q->tx_size); idx = tx_head; if (q->tx_callbacks) q->tx_callbacks[idx] = NULL; desc = &q->tx_desc[idx]; desc->status |= GMAC_TX_STATUS_USED; /* Update buffer descriptors in reverse order to avoid a race * condition with hardware. */ for (i = sgl->size - 1; i >= 0; i--) { const struct _gmac_sg *sg = &sgl->entries[i]; uint32_t status; if (sg->size > GMAC_TX_UNITSIZE) { trace_error("gmacd_send_sg: buffer size is too big.\r\n"); return GMACD_PARAM; } RING_DEC(idx, q->tx_size); /* Reset TX callback */ if (q->tx_callbacks) q->tx_callbacks[idx] = NULL; desc = &q->tx_desc[idx]; /* Copy data into transmittion buffer */ if (sg->buffer && sg->size) { memcpy((void*)desc->addr, sg->buffer, sg->size); l2cc_clean_region(desc->addr, desc->addr + sg->size); } /* Compute buffer descriptor status word */ status = sg->size & GMAC_RX_STATUS_LENGTH_MASK; if (i == (sgl->size - 1)) { status |= GMAC_TX_STATUS_LASTBUF; if (q->tx_callbacks) q->tx_callbacks[idx] = callback; } if (idx == (q->tx_size - 1)) { status |= GMAC_TX_STATUS_WRAP; } /* Update buffer descriptor status word: clear USED bit */ desc->status = status; DSB(); } /* Update TX ring buffer pointers */ q->tx_head = tx_head; /* Now start to transmit if it is not already done */ gmac_start_transmission(gmac); return GMACD_OK; }