コード例 #1
0
ファイル: gmacd.c プロジェクト: AlexShiLucky/freertos
/**
 * \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;
}
コード例 #2
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;
}