示例#1
0
/**
 * \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;
}
示例#2
0
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;
}
示例#3
0
/**
 * \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;
}