/** * \brief Send ulLength bytes from pcFrom. This copies the buffer to one of the * GMAC Tx buffers, and then indicates to the GMAC that the buffer is ready. * If lEndOfFrame is true then the data being copied is the end of the frame * and the frame can be transmitted. * * \param p_gmac_dev Pointer to the GMAC device instance. * \param p_buffer Pointer to the data buffer. * \param ul_size Length of the frame. * \param func_tx_cb Transmit callback function. * * \return Length sent. */ uint32_t gmac_dev_write(gmac_device_t* p_gmac_dev, void *p_buffer, uint32_t ul_size, gmac_dev_tx_cb_t func_tx_cb) { volatile gmac_tx_descriptor_t *p_tx_td; volatile gmac_dev_tx_cb_t *p_func_tx_cb; Gmac *p_hw = p_gmac_dev->p_hw; /* Check parameter */ if (ul_size > GMAC_TX_UNITSIZE) { return GMAC_PARAM; } /* Pointers to the current transmit descriptor */ p_tx_td = &p_gmac_dev->p_tx_dscr[p_gmac_dev->us_tx_head]; /* If no free TxTd, buffer can't be sent, schedule the wakeup callback */ if (CIRC_SPACE(p_gmac_dev->us_tx_head, p_gmac_dev->us_tx_tail, p_gmac_dev->us_tx_list_size) == 0) { if (p_tx_td[p_gmac_dev->us_tx_head].status.val & GMAC_TXD_USED) return GMAC_TX_BUSY; } /* Pointers to the current Tx callback */ p_func_tx_cb = &p_gmac_dev->func_tx_cb_list[p_gmac_dev->us_tx_head]; /* Set up/copy data to transmission buffer */ if (p_buffer && ul_size) { /* Driver manages the ring buffer */ memcpy((void *)p_tx_td->addr, p_buffer, ul_size); } /* Tx callback */ *p_func_tx_cb = func_tx_cb; /* Update transmit descriptor status */ /* The buffer size defined is the length of ethernet frame, so it's always the last buffer of the frame. */ if (p_gmac_dev->us_tx_head == p_gmac_dev->us_tx_list_size - 1) { p_tx_td->status.val = (ul_size & GMAC_TXD_LEN_MASK) | GMAC_TXD_LAST | GMAC_TXD_WRAP; } else { p_tx_td->status.val = (ul_size & GMAC_TXD_LEN_MASK) | GMAC_TXD_LAST; } circ_inc(&p_gmac_dev->us_tx_head, p_gmac_dev->us_tx_list_size); /* Now start to transmit if it is still not done */ gmac_start_transmission(p_hw); return GMAC_OK; }
uint32_t gmac_dev_write_nocopy(gmac_device_t* p_gmac_dev, gmac_quelist_t queue_idx, uint32_t ul_size, gmac_dev_tx_cb_t func_tx_cb) { volatile gmac_tx_descriptor_t *p_tx_td; volatile gmac_dev_tx_cb_t *p_func_tx_cb; Gmac *p_hw = p_gmac_dev->p_hw; gmac_queue_t* p_gmac_queue = &p_gmac_dev->gmac_queue_list[queue_idx]; /* Check parameter */ if (ul_size > GMAC_TX_UNITSIZE) { return GMAC_PARAM; } /* Pointers to the current transmit descriptor */ p_tx_td = &p_gmac_queue->p_tx_dscr[p_gmac_queue->us_tx_head]; /* Pointers to the current Tx callback */ p_func_tx_cb = &p_gmac_queue->func_tx_cb_list[p_gmac_queue->us_tx_head]; /* Tx callback */ *p_func_tx_cb = func_tx_cb; /* Update transmit descriptor status */ /* The buffer size defined is the length of ethernet frame, so it's always the last buffer of the frame. */ if (p_gmac_queue->us_tx_head == p_gmac_queue->us_tx_list_size - 1) { p_tx_td->status.val = (ul_size & GMAC_TXD_LEN_MASK) | GMAC_TXD_LAST | GMAC_TXD_WRAP; } else { p_tx_td->status.val = (ul_size & GMAC_TXD_LEN_MASK) | GMAC_TXD_LAST; } circ_inc(&p_gmac_queue->us_tx_head, p_gmac_queue->us_tx_list_size); /* Now start to transmit if it is still not done */ gmac_start_transmission(p_hw); return GMAC_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; }