mirror of
				https://github.com/digistump/DigistumpArduino.git
				synced 2025-11-03 13:04:48 -08:00 
			
		
		
		
	switch to setup for Arduino Boards Manager
This commit is contained in:
		
							
								
								
									
										802
									
								
								digistump-sam/system/libsam/source/emac.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										802
									
								
								digistump-sam/system/libsam/source/emac.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,802 @@
 | 
			
		||||
 /**
 | 
			
		||||
 * \file
 | 
			
		||||
 *
 | 
			
		||||
 * \brief EMAC (Ethernet MAC) driver for SAM.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2011-2012 Atmel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * \asf_license_start
 | 
			
		||||
 *
 | 
			
		||||
 * \page License
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions are met:
 | 
			
		||||
 *
 | 
			
		||||
 * 1. Redistributions of source code must retain the above copyright notice,
 | 
			
		||||
 *    this list of conditions and the following disclaimer.
 | 
			
		||||
 *
 | 
			
		||||
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 | 
			
		||||
 *    this list of conditions and the following disclaimer in the documentation
 | 
			
		||||
 *    and/or other materials provided with the distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * 3. The name of Atmel may not be used to endorse or promote products derived
 | 
			
		||||
 *    from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * 4. This software may only be redistributed and used in connection with an
 | 
			
		||||
 *    Atmel microcontroller product.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 | 
			
		||||
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 | 
			
		||||
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
 | 
			
		||||
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
			
		||||
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
			
		||||
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
			
		||||
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 | 
			
		||||
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 | 
			
		||||
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | 
			
		||||
 * POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 * \asf_license_stop
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "../chip.h"
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
/// @cond 0
 | 
			
		||||
/**INDENT-OFF**/
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
/**INDENT-ON**/
 | 
			
		||||
/// @endcond
 | 
			
		||||
 | 
			
		||||
#if SAM3XA_SERIES
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \defgroup emac_group Ethernet Media Access Controller
 | 
			
		||||
 *
 | 
			
		||||
 * See \ref emac_quickstart.
 | 
			
		||||
 *
 | 
			
		||||
 * Driver for the EMAC (Ethernet Media Access Controller).
 | 
			
		||||
 * This file contains basic functions for the EMAC, with support for all modes, settings
 | 
			
		||||
 * and clock speeds.
 | 
			
		||||
 *
 | 
			
		||||
 * \section dependencies Dependencies
 | 
			
		||||
 * This driver does not depend on other modules.
 | 
			
		||||
 *
 | 
			
		||||
 * @{
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define EMAC_RX_BUFFERS                               16
 | 
			
		||||
#define EMAC_TX_BUFFERS                               8
 | 
			
		||||
#define MAC_PHY_RETRY_MAX                             1000000
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/** TX descriptor lists */
 | 
			
		||||
#ifdef __ICCARM__ /* IAR */
 | 
			
		||||
#pragma data_alignment=8
 | 
			
		||||
#endif
 | 
			
		||||
static emac_tx_descriptor_t gs_tx_desc[EMAC_TX_BUFFERS];
 | 
			
		||||
/** TX callback lists */
 | 
			
		||||
static emac_dev_tx_cb_t gs_tx_callback[EMAC_TX_BUFFERS];
 | 
			
		||||
/** RX descriptors lists */
 | 
			
		||||
#ifdef __ICCARM__ /* IAR */
 | 
			
		||||
#pragma data_alignment=8
 | 
			
		||||
#endif
 | 
			
		||||
static emac_rx_descriptor_t gs_rx_desc[EMAC_RX_BUFFERS];
 | 
			
		||||
/** Send Buffer. Section 3.6 of AMBA 2.0 spec states that burst should not cross the
 | 
			
		||||
 * 1K Boundaries. Receive buffer manager write operations are burst of 2 words => 3 lsb bits
 | 
			
		||||
 * of the address shall be set to 0.
 | 
			
		||||
 */
 | 
			
		||||
#ifdef __ICCARM__ /* IAR */
 | 
			
		||||
#pragma data_alignment=8
 | 
			
		||||
#endif
 | 
			
		||||
static uint8_t gs_uc_tx_buffer[EMAC_TX_BUFFERS * EMAC_TX_UNITSIZE]
 | 
			
		||||
		__attribute__ ((aligned(8)));
 | 
			
		||||
 | 
			
		||||
#ifdef __ICCARM__ /* IAR */
 | 
			
		||||
#pragma data_alignment=8
 | 
			
		||||
#endif
 | 
			
		||||
/** Receive Buffer */
 | 
			
		||||
static uint8_t gs_uc_rx_buffer[EMAC_RX_BUFFERS * EMAC_RX_UNITSIZE]
 | 
			
		||||
		__attribute__ ((aligned(8)));
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * EMAC device memory management struct.
 | 
			
		||||
 */
 | 
			
		||||
typedef struct emac_dev_mem {
 | 
			
		||||
	/* Pointer to allocated buffer for RX. The address should be 8-byte aligned
 | 
			
		||||
	and the size should be EMAC_RX_UNITSIZE * wRxSize. */
 | 
			
		||||
	uint8_t *p_rx_buffer;
 | 
			
		||||
	/* Pointer to allocated RX descriptor list. */
 | 
			
		||||
	emac_rx_descriptor_t *p_rx_dscr;
 | 
			
		||||
	/* RX size, in number of registered units (RX descriptors). */
 | 
			
		||||
	uint16_t us_rx_size;
 | 
			
		||||
	/* Pointer to allocated buffer for TX. The address should be 8-byte aligned
 | 
			
		||||
	and the size should be EMAC_TX_UNITSIZE * wTxSize. */
 | 
			
		||||
	uint8_t *p_tx_buffer;
 | 
			
		||||
	/* Pointer to allocated TX descriptor list. */
 | 
			
		||||
	emac_tx_descriptor_t *p_tx_dscr;
 | 
			
		||||
	/* TX size, in number of registered units (TX descriptors). */
 | 
			
		||||
	uint16_t us_tx_size;
 | 
			
		||||
} emac_dev_mem_t;
 | 
			
		||||
 | 
			
		||||
/** Return count in buffer */
 | 
			
		||||
#define CIRC_CNT(head,tail,size) (((head) - (tail)) % (size))
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Return space available, from 0 to size-1.
 | 
			
		||||
 * Always leave one free char as a completely full buffer that has (head == tail),
 | 
			
		||||
 * which is the same as empty.
 | 
			
		||||
 */
 | 
			
		||||
#define CIRC_SPACE(head,tail,size) CIRC_CNT((tail),((head)+1),(size))
 | 
			
		||||
 | 
			
		||||
/** Circular buffer is empty ? */
 | 
			
		||||
#define CIRC_EMPTY(head, tail)     (head == tail)
 | 
			
		||||
/** Clear circular buffer */
 | 
			
		||||
#define CIRC_CLEAR(head, tail)     (head = tail = 0)
 | 
			
		||||
 | 
			
		||||
/** Increment head or tail */
 | 
			
		||||
static void circ_inc(uint16_t *headortail, uint32_t size)
 | 
			
		||||
{
 | 
			
		||||
        (*headortail)++;
 | 
			
		||||
        if((*headortail) >= size) {
 | 
			
		||||
            (*headortail) = 0;
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Wait PHY operation to be completed.
 | 
			
		||||
 *
 | 
			
		||||
 * \param p_emac HW controller address.
 | 
			
		||||
 * \param ul_retry The retry times, 0 to wait forever until completeness.
 | 
			
		||||
 *
 | 
			
		||||
 * Return EMAC_OK if the operation is completed successfully.
 | 
			
		||||
 */
 | 
			
		||||
static uint8_t emac_wait_phy(Emac* p_emac, const uint32_t ul_retry)
 | 
			
		||||
{
 | 
			
		||||
	volatile uint32_t ul_retry_count = 0;
 | 
			
		||||
 | 
			
		||||
	while (!emac_is_phy_idle(p_emac)) {
 | 
			
		||||
		if (ul_retry == 0) {
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ul_retry_count++;
 | 
			
		||||
 | 
			
		||||
		if (ul_retry_count >= ul_retry) {
 | 
			
		||||
			return EMAC_TIMEOUT;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return EMAC_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Disable transfer, reset registers and descriptor lists.
 | 
			
		||||
 *
 | 
			
		||||
 * \param p_dev Pointer to EMAC driver instance.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
static void emac_reset_tx_mem(emac_device_t* p_dev)
 | 
			
		||||
{
 | 
			
		||||
	Emac *p_hw = p_dev->p_hw;
 | 
			
		||||
	uint8_t *p_tx_buff = p_dev->p_tx_buffer;
 | 
			
		||||
	emac_tx_descriptor_t *p_td = p_dev->p_tx_dscr;
 | 
			
		||||
 | 
			
		||||
	uint32_t ul_index;
 | 
			
		||||
	uint32_t ul_address;
 | 
			
		||||
 | 
			
		||||
	/* Disable TX */
 | 
			
		||||
	emac_enable_transmit(p_hw, 0);
 | 
			
		||||
 | 
			
		||||
	/* Set up the TX descriptors */
 | 
			
		||||
	CIRC_CLEAR(p_dev->us_tx_head, p_dev->us_tx_tail);
 | 
			
		||||
	for (ul_index = 0; ul_index < p_dev->us_tx_list_size; ul_index++) {
 | 
			
		||||
		ul_address = (uint32_t) (&(p_tx_buff[ul_index * EMAC_TX_UNITSIZE]));
 | 
			
		||||
		p_td[ul_index].addr = ul_address;
 | 
			
		||||
		p_td[ul_index].status.val = EMAC_TXD_USED;
 | 
			
		||||
	}
 | 
			
		||||
	p_td[p_dev->us_tx_list_size - 1].status.val =
 | 
			
		||||
			EMAC_TXD_USED | EMAC_TXD_WRAP;
 | 
			
		||||
 | 
			
		||||
	/* Set transmit buffer queue */
 | 
			
		||||
	emac_set_tx_queue(p_hw, (uint32_t) p_td);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Disable receiver, reset registers and descriptor list.
 | 
			
		||||
 *
 | 
			
		||||
 * \param p_drv Pointer to EMAC Driver instance.
 | 
			
		||||
 */
 | 
			
		||||
static void emac_reset_rx_mem(emac_device_t* p_dev)
 | 
			
		||||
{
 | 
			
		||||
	Emac *p_hw = p_dev->p_hw;
 | 
			
		||||
	uint8_t *p_rx_buff = p_dev->p_rx_buffer;
 | 
			
		||||
	emac_rx_descriptor_t *pRd = p_dev->p_rx_dscr;
 | 
			
		||||
 | 
			
		||||
	uint32_t ul_index;
 | 
			
		||||
	uint32_t ul_address;
 | 
			
		||||
 | 
			
		||||
	/* Disable RX */
 | 
			
		||||
	emac_enable_receive(p_hw, 0);
 | 
			
		||||
 | 
			
		||||
	/* Set up the RX descriptors */
 | 
			
		||||
	p_dev->us_rx_idx = 0;
 | 
			
		||||
	for (ul_index = 0; ul_index < p_dev->us_rx_list_size; ul_index++) {
 | 
			
		||||
		ul_address = (uint32_t) (&(p_rx_buff[ul_index * EMAC_RX_UNITSIZE]));
 | 
			
		||||
		pRd[ul_index].addr.val = ul_address & EMAC_RXD_ADDR_MASK;
 | 
			
		||||
		pRd[ul_index].status.val = 0;
 | 
			
		||||
	}
 | 
			
		||||
	pRd[p_dev->us_rx_list_size - 1].addr.val |= EMAC_RXD_WRAP;
 | 
			
		||||
 | 
			
		||||
	/* Set receive buffer queue */
 | 
			
		||||
	emac_set_rx_queue(p_hw, (uint32_t) pRd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Initialize the allocated buffer lists for EMAC driver to transfer data.
 | 
			
		||||
 * Must be invoked after emac_dev_init() but before RX/TX starts.
 | 
			
		||||
 *
 | 
			
		||||
 * \note If input address is not 8-byte aligned, the address is automatically
 | 
			
		||||
 *       adjusted and the list size is reduced by one.
 | 
			
		||||
 *
 | 
			
		||||
 * \param p_emac Pointer to EMAC instance.
 | 
			
		||||
 * \param p_emac_dev Pointer to EMAC device instance.
 | 
			
		||||
 * \param p_dev_mm Pointer to the EMAC memory management control block.
 | 
			
		||||
 * \param p_tx_cb Pointer to allocated TX callback list.
 | 
			
		||||
 *
 | 
			
		||||
 * \return EMAC_OK or EMAC_PARAM.
 | 
			
		||||
 */
 | 
			
		||||
static uint8_t emac_init_mem(Emac* p_emac, emac_device_t* p_emac_dev,
 | 
			
		||||
		emac_dev_mem_t* p_dev_mm,
 | 
			
		||||
		emac_dev_tx_cb_t* p_tx_cb)
 | 
			
		||||
{
 | 
			
		||||
	if (p_dev_mm->us_rx_size <= 1 || p_dev_mm->us_tx_size <= 1 || p_tx_cb == NULL) {
 | 
			
		||||
		return EMAC_PARAM;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Assign RX buffers */
 | 
			
		||||
	if (((uint32_t) p_dev_mm->p_rx_buffer & 0x7)
 | 
			
		||||
			|| ((uint32_t) p_dev_mm->p_rx_dscr & 0x7)) {
 | 
			
		||||
		p_dev_mm->us_rx_size--;
 | 
			
		||||
	}
 | 
			
		||||
	p_emac_dev->p_rx_buffer =
 | 
			
		||||
			(uint8_t *) ((uint32_t) p_dev_mm->p_rx_buffer & 0xFFFFFFF8);
 | 
			
		||||
	p_emac_dev->p_rx_dscr =
 | 
			
		||||
			(emac_rx_descriptor_t *) ((uint32_t) p_dev_mm->p_rx_dscr
 | 
			
		||||
			& 0xFFFFFFF8);
 | 
			
		||||
	p_emac_dev->us_rx_list_size = p_dev_mm->us_rx_size;
 | 
			
		||||
 | 
			
		||||
	/* Assign TX buffers */
 | 
			
		||||
	if (((uint32_t) p_dev_mm->p_tx_buffer & 0x7)
 | 
			
		||||
			|| ((uint32_t) p_dev_mm->p_tx_dscr & 0x7)) {
 | 
			
		||||
		p_dev_mm->us_tx_size--;
 | 
			
		||||
	}
 | 
			
		||||
	p_emac_dev->p_tx_buffer =
 | 
			
		||||
			(uint8_t *) ((uint32_t) p_dev_mm->p_tx_buffer & 0xFFFFFFF8);
 | 
			
		||||
	p_emac_dev->p_tx_dscr =
 | 
			
		||||
			(emac_tx_descriptor_t *) ((uint32_t) p_dev_mm->p_tx_dscr
 | 
			
		||||
			& 0xFFFFFFF8);
 | 
			
		||||
	p_emac_dev->us_tx_list_size = p_dev_mm->us_tx_size;
 | 
			
		||||
	p_emac_dev->func_tx_cb_list = p_tx_cb;
 | 
			
		||||
 | 
			
		||||
	/* Reset TX & RX */
 | 
			
		||||
	emac_reset_rx_mem(p_emac_dev);
 | 
			
		||||
	emac_reset_tx_mem(p_emac_dev);
 | 
			
		||||
 | 
			
		||||
	/* Enable Rx and Tx, plus the statistics register */
 | 
			
		||||
	emac_enable_transmit(p_emac, 1);
 | 
			
		||||
	emac_enable_receive(p_emac, 1);
 | 
			
		||||
	emac_enable_statistics_write(p_emac, 1);
 | 
			
		||||
 | 
			
		||||
	/* Set up the interrupts for transmission and errors */
 | 
			
		||||
	emac_enable_interrupt(p_emac,
 | 
			
		||||
			EMAC_IER_RXUBR | /* Enable receive used bit read interrupt. */
 | 
			
		||||
			EMAC_IER_TUND  | /* Enable transmit underrun interrupt. */
 | 
			
		||||
			EMAC_IER_RLE   | /* Enable retry limit  exceeded interrupt. */
 | 
			
		||||
			EMAC_IER_TXERR | /* Enable transmit buffers exhausted in mid-frame interrupt. */
 | 
			
		||||
			EMAC_IER_TCOMP | /* Enable transmit complete interrupt. */
 | 
			
		||||
			EMAC_IER_ROVR  | /* Enable receive overrun interrupt. */
 | 
			
		||||
			EMAC_IER_HRESP | /* Enable Hresp not OK interrupt. */
 | 
			
		||||
			EMAC_IER_PFR   | /* Enable pause frame received interrupt. */
 | 
			
		||||
			EMAC_IER_PTZ);   /* Enable pause time zero interrupt. */
 | 
			
		||||
 | 
			
		||||
	return EMAC_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Read the PHY register.
 | 
			
		||||
 *
 | 
			
		||||
 * \param p_emac   Pointer to the EMAC instance.
 | 
			
		||||
 * \param uc_phy_address PHY address.
 | 
			
		||||
 * \param uc_address Register address.
 | 
			
		||||
 * \param p_value Pointer to a 32-bit location to store read data.
 | 
			
		||||
 *
 | 
			
		||||
 * \Return EMAC_OK if successfully, EMAC_TIMEOUT if timeout.
 | 
			
		||||
 */
 | 
			
		||||
uint8_t emac_phy_read(Emac* p_emac, uint8_t uc_phy_address, uint8_t uc_address,
 | 
			
		||||
		uint32_t* p_value)
 | 
			
		||||
{
 | 
			
		||||
	emac_maintain_phy(p_emac, uc_phy_address, uc_address, 1, 0);
 | 
			
		||||
 | 
			
		||||
	if (emac_wait_phy(p_emac, MAC_PHY_RETRY_MAX) == EMAC_TIMEOUT) {
 | 
			
		||||
		return EMAC_TIMEOUT;
 | 
			
		||||
	}
 | 
			
		||||
	*p_value = emac_get_phy_data(p_emac);
 | 
			
		||||
	return EMAC_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Write the PHY register.
 | 
			
		||||
 *
 | 
			
		||||
 * \param p_emac   Pointer to the EMAC instance.
 | 
			
		||||
 * \param uc_phy_address PHY Address.
 | 
			
		||||
 * \param uc_address Register Address.
 | 
			
		||||
 * \param ul_value Data to write, actually 16-bit data.
 | 
			
		||||
 *
 | 
			
		||||
 * \Return EMAC_OK if successfully, EMAC_TIMEOUT if timeout.
 | 
			
		||||
 */
 | 
			
		||||
uint8_t emac_phy_write(Emac* p_emac, uint8_t uc_phy_address,
 | 
			
		||||
		uint8_t uc_address, uint32_t ul_value)
 | 
			
		||||
{
 | 
			
		||||
	emac_maintain_phy(p_emac, uc_phy_address, uc_address, 0, ul_value);
 | 
			
		||||
 | 
			
		||||
	if (emac_wait_phy(p_emac, MAC_PHY_RETRY_MAX) == EMAC_TIMEOUT) {
 | 
			
		||||
		return EMAC_TIMEOUT;
 | 
			
		||||
	}
 | 
			
		||||
	return EMAC_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Initialize the EMAC driver.
 | 
			
		||||
 *
 | 
			
		||||
 * \param p_emac   Pointer to the EMAC instance.
 | 
			
		||||
 * \param p_emac_dev Pointer to the EMAC device instance.
 | 
			
		||||
 * \param p_opt EMAC configure options.
 | 
			
		||||
 */
 | 
			
		||||
void emac_dev_init(Emac* p_emac, emac_device_t* p_emac_dev,
 | 
			
		||||
		emac_options_t* p_opt)
 | 
			
		||||
{
 | 
			
		||||
	emac_dev_mem_t emac_dev_mm;
 | 
			
		||||
 | 
			
		||||
	/* Disable TX & RX and more */
 | 
			
		||||
	emac_network_control(p_emac, 0);
 | 
			
		||||
	emac_disable_interrupt(p_emac, ~0u);
 | 
			
		||||
 | 
			
		||||
	emac_clear_statistics(p_emac);
 | 
			
		||||
 | 
			
		||||
	/* Clear all status bits in the receive status register. */
 | 
			
		||||
	emac_clear_rx_status(p_emac, EMAC_RSR_OVR | EMAC_RSR_REC | EMAC_RSR_BNA);
 | 
			
		||||
 | 
			
		||||
	/* Clear all status bits in the transmit status register */
 | 
			
		||||
	emac_clear_tx_status(p_emac, EMAC_TSR_UBR | EMAC_TSR_COL | EMAC_TSR_RLES
 | 
			
		||||
			| EMAC_TSR_BEX | EMAC_TSR_COMP | EMAC_TSR_UND);
 | 
			
		||||
 | 
			
		||||
	/* Clear interrupts */
 | 
			
		||||
	emac_get_interrupt_status(p_emac);
 | 
			
		||||
 | 
			
		||||
	/* Enable the copy of data into the buffers
 | 
			
		||||
	   ignore broadcasts, and not copy FCS. */
 | 
			
		||||
	emac_set_configure(p_emac,
 | 
			
		||||
			emac_get_configure(p_emac) | EMAC_NCFGR_DRFCS | EMAC_NCFGR_PAE);
 | 
			
		||||
 | 
			
		||||
	emac_enable_copy_all(p_emac, p_opt->uc_copy_all_frame);
 | 
			
		||||
	emac_disable_broadcast(p_emac, p_opt->uc_no_boardcast);
 | 
			
		||||
 | 
			
		||||
	/* Fill in EMAC device memory management */
 | 
			
		||||
	emac_dev_mm.p_rx_buffer = gs_uc_rx_buffer;
 | 
			
		||||
	emac_dev_mm.p_rx_dscr = gs_rx_desc;
 | 
			
		||||
	emac_dev_mm.us_rx_size = EMAC_RX_BUFFERS;
 | 
			
		||||
 | 
			
		||||
	emac_dev_mm.p_tx_buffer = gs_uc_tx_buffer;
 | 
			
		||||
	emac_dev_mm.p_tx_dscr = gs_tx_desc;
 | 
			
		||||
	emac_dev_mm.us_tx_size = EMAC_TX_BUFFERS;
 | 
			
		||||
 | 
			
		||||
	emac_init_mem(p_emac, p_emac_dev, &emac_dev_mm, gs_tx_callback);
 | 
			
		||||
 | 
			
		||||
	emac_set_address(p_emac, 0, p_opt->uc_mac_addr);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Frames can be read from the EMAC in multiple sections.
 | 
			
		||||
 * Read ul_frame_size bytes from the EMAC receive buffers to pcTo.
 | 
			
		||||
 * p_rcv_size is the size of the entire frame.  Generally emac_read
 | 
			
		||||
 * will be repeatedly called until the sum of all the ul_frame_size equals
 | 
			
		||||
 * the value of p_rcv_size.
 | 
			
		||||
 *
 | 
			
		||||
 * \param p_emac_dev Pointer to the EMAC device instance.
 | 
			
		||||
 * \param p_frame Address of the frame buffer.
 | 
			
		||||
 * \param ul_frame_size  Length of the frame.
 | 
			
		||||
 * \param p_rcv_size   Received frame size.
 | 
			
		||||
 *
 | 
			
		||||
 * \return EMAC_OK if receiving frame successfully, otherwise failed.
 | 
			
		||||
 */
 | 
			
		||||
uint32_t emac_dev_read(emac_device_t* p_emac_dev, uint8_t* p_frame,
 | 
			
		||||
		uint32_t ul_frame_size, uint32_t* p_rcv_size)
 | 
			
		||||
{
 | 
			
		||||
	uint16_t us_buffer_length;
 | 
			
		||||
	uint32_t tmp_ul_frame_size = 0;
 | 
			
		||||
	uint8_t *p_tmp_frame = 0;
 | 
			
		||||
	uint16_t us_tmp_idx = p_emac_dev->us_rx_idx;
 | 
			
		||||
	emac_rx_descriptor_t *p_rx_td =
 | 
			
		||||
			&p_emac_dev->p_rx_dscr[p_emac_dev->us_rx_idx];
 | 
			
		||||
	int8_t c_is_frame = 0;
 | 
			
		||||
 | 
			
		||||
	if (p_frame == NULL)
 | 
			
		||||
		return EMAC_PARAM;
 | 
			
		||||
 | 
			
		||||
	/* Set the default return value */
 | 
			
		||||
	*p_rcv_size = 0;
 | 
			
		||||
 | 
			
		||||
	/* Process received RX descriptor */
 | 
			
		||||
	while ((p_rx_td->addr.val & EMAC_RXD_OWNERSHIP) == EMAC_RXD_OWNERSHIP) {
 | 
			
		||||
		/* A start of frame has been received, discard previous fragments */
 | 
			
		||||
		if ((p_rx_td->status.val & EMAC_RXD_SOF) == EMAC_RXD_SOF) {
 | 
			
		||||
			/* Skip previous fragment */
 | 
			
		||||
			while (us_tmp_idx != p_emac_dev->us_rx_idx) {
 | 
			
		||||
				p_rx_td = &p_emac_dev->p_rx_dscr[p_emac_dev->us_rx_idx];
 | 
			
		||||
				p_rx_td->addr.val &= ~(EMAC_RXD_OWNERSHIP);
 | 
			
		||||
 | 
			
		||||
				circ_inc(&p_emac_dev->us_rx_idx, p_emac_dev->us_rx_list_size);
 | 
			
		||||
			}
 | 
			
		||||
			/* Reset the temporary frame pointer */
 | 
			
		||||
			p_tmp_frame = p_frame;
 | 
			
		||||
			tmp_ul_frame_size = 0;
 | 
			
		||||
			/* Start to gather buffers in a frame */
 | 
			
		||||
			c_is_frame = 1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Increment the pointer */
 | 
			
		||||
		circ_inc(&us_tmp_idx, p_emac_dev->us_rx_list_size);
 | 
			
		||||
 | 
			
		||||
		/* Copy data in the frame buffer */
 | 
			
		||||
		if (c_is_frame) {
 | 
			
		||||
			if (us_tmp_idx == p_emac_dev->us_rx_idx) {
 | 
			
		||||
				do {
 | 
			
		||||
					p_rx_td = &p_emac_dev->p_rx_dscr[p_emac_dev->us_rx_idx];
 | 
			
		||||
					p_rx_td->addr.val &= ~(EMAC_RXD_OWNERSHIP);
 | 
			
		||||
					circ_inc(&p_emac_dev->us_rx_idx, p_emac_dev->us_rx_list_size);
 | 
			
		||||
 | 
			
		||||
				} while (us_tmp_idx != p_emac_dev->us_rx_idx);
 | 
			
		||||
 | 
			
		||||
				return EMAC_RX_NULL;
 | 
			
		||||
			}
 | 
			
		||||
			/* Copy the buffer into the application frame */
 | 
			
		||||
			us_buffer_length = EMAC_RX_UNITSIZE;
 | 
			
		||||
			if ((tmp_ul_frame_size + us_buffer_length) > ul_frame_size) {
 | 
			
		||||
				us_buffer_length = ul_frame_size - tmp_ul_frame_size;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			memcpy(p_tmp_frame,
 | 
			
		||||
					(void *)(p_rx_td->addr.val & EMAC_RXD_ADDR_MASK),
 | 
			
		||||
					us_buffer_length);
 | 
			
		||||
			p_tmp_frame += us_buffer_length;
 | 
			
		||||
			tmp_ul_frame_size += us_buffer_length;
 | 
			
		||||
 | 
			
		||||
			/* An end of frame has been received, return the data */
 | 
			
		||||
			if ((p_rx_td->status.val & EMAC_RXD_EOF) == EMAC_RXD_EOF) {
 | 
			
		||||
				/* Frame size from the EMAC */
 | 
			
		||||
				*p_rcv_size = (p_rx_td->status.val & EMAC_RXD_LEN_MASK);
 | 
			
		||||
 | 
			
		||||
				/* All data have been copied in the application frame buffer => release TD */
 | 
			
		||||
				while (p_emac_dev->us_rx_idx != us_tmp_idx) {
 | 
			
		||||
					p_rx_td = &p_emac_dev->p_rx_dscr[p_emac_dev->us_rx_idx];
 | 
			
		||||
					p_rx_td->addr.val &= ~(EMAC_RXD_OWNERSHIP);
 | 
			
		||||
					circ_inc(&p_emac_dev->us_rx_idx, p_emac_dev->us_rx_list_size);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				/* Application frame buffer is too small so that all data have not been copied */
 | 
			
		||||
				if (tmp_ul_frame_size < *p_rcv_size) {
 | 
			
		||||
					return EMAC_SIZE_TOO_SMALL;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return EMAC_OK;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		/* SOF has not been detected, skip the fragment */
 | 
			
		||||
		else {
 | 
			
		||||
			p_rx_td->addr.val &= ~(EMAC_RXD_OWNERSHIP);
 | 
			
		||||
			p_emac_dev->us_rx_idx = us_tmp_idx;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Process the next buffer */
 | 
			
		||||
		p_rx_td = &p_emac_dev->p_rx_dscr[us_tmp_idx];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return EMAC_RX_NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Send ulLength bytes from pcFrom. This copies the buffer to one of the
 | 
			
		||||
 * EMAC Tx buffers, and then indicates to the EMAC 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_emac_dev Pointer to the EMAC 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 emac_dev_write(emac_device_t* p_emac_dev, void *p_buffer,
 | 
			
		||||
		uint32_t ul_size, emac_dev_tx_cb_t func_tx_cb)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	volatile emac_tx_descriptor_t *p_tx_td;
 | 
			
		||||
	volatile emac_dev_tx_cb_t *p_func_tx_cb;
 | 
			
		||||
 | 
			
		||||
	Emac *p_hw = p_emac_dev->p_hw;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/* Check parameter */
 | 
			
		||||
	if (ul_size > EMAC_TX_UNITSIZE) {
 | 
			
		||||
		return EMAC_PARAM;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Pointers to the current transmit descriptor */
 | 
			
		||||
	p_tx_td = &p_emac_dev->p_tx_dscr[p_emac_dev->us_tx_head];
 | 
			
		||||
 | 
			
		||||
	/* If no free TxTd, buffer can't be sent, schedule the wakeup callback */
 | 
			
		||||
	if (CIRC_SPACE(p_emac_dev->us_tx_head, p_emac_dev->us_tx_tail,
 | 
			
		||||
					p_emac_dev->us_tx_list_size) == 0) {
 | 
			
		||||
		return EMAC_TX_BUSY;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Pointers to the current Tx callback */
 | 
			
		||||
	p_func_tx_cb = &p_emac_dev->func_tx_cb_list[p_emac_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_emac_dev->us_tx_head == p_emac_dev->us_tx_list_size - 1) {
 | 
			
		||||
		p_tx_td->status.val =
 | 
			
		||||
				(ul_size & EMAC_TXD_LEN_MASK) | EMAC_TXD_LAST
 | 
			
		||||
				| EMAC_TXD_WRAP;
 | 
			
		||||
	} else {
 | 
			
		||||
		p_tx_td->status.val =
 | 
			
		||||
				(ul_size & EMAC_TXD_LEN_MASK) | EMAC_TXD_LAST;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	circ_inc(&p_emac_dev->us_tx_head, p_emac_dev->us_tx_list_size);
 | 
			
		||||
 | 
			
		||||
	/* Now start to transmit if it is still not done */
 | 
			
		||||
	emac_start_transmission(p_hw);
 | 
			
		||||
 | 
			
		||||
	return EMAC_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Get current load of transmit.
 | 
			
		||||
 *
 | 
			
		||||
 * \param p_emac_dev Pointer to the EMAC device instance.
 | 
			
		||||
 *
 | 
			
		||||
 * \return Current load of transmit.
 | 
			
		||||
 */
 | 
			
		||||
uint32_t emac_dev_get_tx_load(emac_device_t* p_emac_dev)
 | 
			
		||||
{
 | 
			
		||||
	uint16_t us_head = p_emac_dev->us_tx_head;
 | 
			
		||||
	uint16_t us_tail = p_emac_dev->us_tx_tail;
 | 
			
		||||
	return CIRC_CNT(us_head, us_tail, p_emac_dev->us_tx_list_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Register/Clear RX callback. Callback will be invoked after the next received
 | 
			
		||||
 * frame.
 | 
			
		||||
 *
 | 
			
		||||
 * When emac_dev_read() returns EMAC_RX_NULL, the application task calls
 | 
			
		||||
 * emac_dev_set_rx_callback() to register func_rx_cb() callback and enters suspend state.
 | 
			
		||||
 * The callback is in charge to resume the task once a new frame has been
 | 
			
		||||
 * received. The next time emac_dev_read() is called, it will be successful.
 | 
			
		||||
 *
 | 
			
		||||
 * This function is usually invoked from the RX callback itself with NULL
 | 
			
		||||
 * callback, to unregister. Once the callback has resumed the application task,
 | 
			
		||||
 * there is no need to invoke the callback again.
 | 
			
		||||
 *
 | 
			
		||||
 * \param p_emac_dev Pointer to the EMAC device instance.
 | 
			
		||||
 * \param func_tx_cb  Receive callback function.
 | 
			
		||||
 */
 | 
			
		||||
void emac_dev_set_rx_callback(emac_device_t* p_emac_dev,
 | 
			
		||||
		emac_dev_tx_cb_t func_rx_cb)
 | 
			
		||||
{
 | 
			
		||||
	Emac *p_hw = p_emac_dev->p_hw;
 | 
			
		||||
 | 
			
		||||
	if (func_rx_cb == NULL) {
 | 
			
		||||
		emac_disable_interrupt(p_hw, EMAC_IDR_RCOMP);
 | 
			
		||||
		p_emac_dev->func_rx_cb = NULL;
 | 
			
		||||
	} else {
 | 
			
		||||
		p_emac_dev->func_rx_cb = func_rx_cb;
 | 
			
		||||
		emac_enable_interrupt(p_hw, EMAC_IER_RCOMP);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *  \brief Register/Clear TX wakeup callback.
 | 
			
		||||
 *
 | 
			
		||||
 * When emac_dev_write() returns EMAC_TX_BUSY (all transmit descriptor busy), the application
 | 
			
		||||
 * task calls emac_dev_set_tx_wakeup_callback() to register func_wakeup() callback and
 | 
			
		||||
 * enters suspend state. The callback is in charge to resume the task once
 | 
			
		||||
 * several transmit descriptors have been released. The next time emac_dev_write() will be called,
 | 
			
		||||
 * it shall be successful.
 | 
			
		||||
 *
 | 
			
		||||
 * This function is usually invoked with NULL callback from the TX wakeup
 | 
			
		||||
 * callback itself, to unregister. Once the callback has resumed the
 | 
			
		||||
 * application task, there is no need to invoke the callback again.
 | 
			
		||||
 *
 | 
			
		||||
 * \param p_emac_dev   Pointer to EMAC device instance.
 | 
			
		||||
 * \param func_wakeup    Pointer to wakeup callback function.
 | 
			
		||||
 * \param uc_threshold Number of free transmit descriptor before wakeup callback invoked.
 | 
			
		||||
 *
 | 
			
		||||
 * \return EMAC_OK, EMAC_PARAM on parameter error.
 | 
			
		||||
 */
 | 
			
		||||
uint8_t emac_dev_set_tx_wakeup_callback(emac_device_t* p_emac_dev,
 | 
			
		||||
		emac_dev_wakeup_cb_t func_wakeup_cb, uint8_t uc_threshold)
 | 
			
		||||
{
 | 
			
		||||
	if (func_wakeup_cb == NULL) {
 | 
			
		||||
		p_emac_dev->func_wakeup_cb = NULL;
 | 
			
		||||
	} else {
 | 
			
		||||
		if (uc_threshold <= p_emac_dev->us_tx_list_size) {
 | 
			
		||||
			p_emac_dev->func_wakeup_cb = func_wakeup_cb;
 | 
			
		||||
			p_emac_dev->uc_wakeup_threshold = uc_threshold;
 | 
			
		||||
		} else {
 | 
			
		||||
			return EMAC_PARAM;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return EMAC_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Reset TX & RX queue & statistics.
 | 
			
		||||
 *
 | 
			
		||||
 * \param p_emac_dev   Pointer to EMAC device instance.
 | 
			
		||||
 */
 | 
			
		||||
void emac_dev_reset(emac_device_t* p_emac_dev)
 | 
			
		||||
{
 | 
			
		||||
	Emac *p_hw = p_emac_dev->p_hw;
 | 
			
		||||
 | 
			
		||||
	emac_reset_rx_mem(p_emac_dev);
 | 
			
		||||
	emac_reset_tx_mem(p_emac_dev);
 | 
			
		||||
	emac_network_control(p_hw, EMAC_NCR_TE | EMAC_NCR_RE
 | 
			
		||||
			| EMAC_NCR_WESTAT | EMAC_NCR_CLRSTAT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief EMAC Interrupt handler.
 | 
			
		||||
 *
 | 
			
		||||
 * \param p_emac_dev   Pointer to EMAC device instance.
 | 
			
		||||
 */
 | 
			
		||||
void emac_handler(emac_device_t* p_emac_dev)
 | 
			
		||||
{
 | 
			
		||||
	Emac *p_hw = p_emac_dev->p_hw;
 | 
			
		||||
 | 
			
		||||
	emac_tx_descriptor_t *p_tx_td;
 | 
			
		||||
	emac_dev_tx_cb_t *p_tx_cb;
 | 
			
		||||
	volatile uint32_t ul_isr;
 | 
			
		||||
	volatile uint32_t ul_rsr;
 | 
			
		||||
	volatile uint32_t ul_tsr;
 | 
			
		||||
	uint32_t ul_rx_status_flag;
 | 
			
		||||
	uint32_t ul_tx_status_flag;
 | 
			
		||||
 | 
			
		||||
	ul_isr = emac_get_interrupt_status(p_hw);
 | 
			
		||||
	ul_rsr = emac_get_rx_status(p_hw);
 | 
			
		||||
	ul_tsr = emac_get_tx_status(p_hw);
 | 
			
		||||
 | 
			
		||||
	ul_isr &= ~(emac_get_interrupt_mask(p_hw) | 0xFFC300);
 | 
			
		||||
 | 
			
		||||
	/* RX packet */
 | 
			
		||||
	if ((ul_isr & EMAC_ISR_RCOMP) || (ul_rsr & EMAC_RSR_REC)) {
 | 
			
		||||
		ul_rx_status_flag = EMAC_RSR_REC;
 | 
			
		||||
 | 
			
		||||
		/* Check OVR */
 | 
			
		||||
		if (ul_rsr & EMAC_RSR_OVR) {
 | 
			
		||||
			ul_rx_status_flag |= EMAC_RSR_OVR;
 | 
			
		||||
		}
 | 
			
		||||
		/* Check BNA */
 | 
			
		||||
		if (ul_rsr & EMAC_RSR_BNA) {
 | 
			
		||||
			ul_rx_status_flag |= EMAC_RSR_BNA;
 | 
			
		||||
		}
 | 
			
		||||
		/* Clear status */
 | 
			
		||||
		emac_clear_rx_status(p_hw, ul_rx_status_flag);
 | 
			
		||||
 | 
			
		||||
		/* Invoke callbacks */
 | 
			
		||||
		if (p_emac_dev->func_rx_cb) {
 | 
			
		||||
			p_emac_dev->func_rx_cb(ul_rx_status_flag);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* TX packet */
 | 
			
		||||
	if ((ul_isr & EMAC_ISR_TCOMP) || (ul_tsr & EMAC_TSR_COMP)) {
 | 
			
		||||
 | 
			
		||||
		ul_tx_status_flag = EMAC_TSR_COMP;
 | 
			
		||||
 | 
			
		||||
		/* A frame transmitted */
 | 
			
		||||
 | 
			
		||||
		/* Check RLE */
 | 
			
		||||
		if (ul_tsr & EMAC_TSR_RLES) {
 | 
			
		||||
			/* Status RLE & Number of discarded buffers */
 | 
			
		||||
			ul_tx_status_flag = EMAC_TSR_RLES | CIRC_CNT(p_emac_dev->us_tx_head,
 | 
			
		||||
					p_emac_dev->us_tx_tail, p_emac_dev->us_tx_list_size);
 | 
			
		||||
			p_tx_cb = &p_emac_dev->func_tx_cb_list[p_emac_dev->us_tx_tail];
 | 
			
		||||
			emac_reset_tx_mem(p_emac_dev);
 | 
			
		||||
			emac_enable_transmit(p_hw, 1);
 | 
			
		||||
		}
 | 
			
		||||
		/* Check COL */
 | 
			
		||||
		if (ul_tsr & EMAC_TSR_COL) {
 | 
			
		||||
			ul_tx_status_flag |= EMAC_TSR_COL;
 | 
			
		||||
		}
 | 
			
		||||
		/* Check BEX */
 | 
			
		||||
		if (ul_tsr & EMAC_TSR_BEX) {
 | 
			
		||||
			ul_tx_status_flag |= EMAC_TSR_BEX;
 | 
			
		||||
		}
 | 
			
		||||
		/* Check UND */
 | 
			
		||||
		if (ul_tsr & EMAC_TSR_UND) {
 | 
			
		||||
			ul_tx_status_flag |= EMAC_TSR_UND;
 | 
			
		||||
		}
 | 
			
		||||
		/* Clear status */
 | 
			
		||||
		emac_clear_tx_status(p_hw, ul_tx_status_flag);
 | 
			
		||||
 | 
			
		||||
		if (!CIRC_EMPTY(p_emac_dev->us_tx_head, p_emac_dev->us_tx_tail)) {
 | 
			
		||||
			/* Check the buffers */
 | 
			
		||||
			do {
 | 
			
		||||
				p_tx_td = &p_emac_dev->p_tx_dscr[p_emac_dev->us_tx_tail];
 | 
			
		||||
				p_tx_cb = &p_emac_dev->func_tx_cb_list[p_emac_dev->us_tx_tail];
 | 
			
		||||
				/* Any error? Exit if buffer has not been sent yet */
 | 
			
		||||
				if ((p_tx_td->status.val & EMAC_TXD_USED) == 0) {
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				/* Notify upper layer that a packet has been sent */
 | 
			
		||||
				if (*p_tx_cb) {
 | 
			
		||||
					(*p_tx_cb) (ul_tx_status_flag);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				circ_inc(&p_emac_dev->us_tx_tail, p_emac_dev->us_tx_list_size);
 | 
			
		||||
			} while (CIRC_CNT(p_emac_dev->us_tx_head, p_emac_dev->us_tx_tail,
 | 
			
		||||
							p_emac_dev->us_tx_list_size));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (ul_tsr & EMAC_TSR_RLES) {
 | 
			
		||||
			/* Notify upper layer RLE */
 | 
			
		||||
			if (*p_tx_cb) {
 | 
			
		||||
				(*p_tx_cb) (ul_tx_status_flag);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* If a wakeup has been scheduled, notify upper layer that it can
 | 
			
		||||
		   send other packets, and the sending will be successful. */
 | 
			
		||||
		if ((CIRC_SPACE(p_emac_dev->us_tx_head, p_emac_dev->us_tx_tail,
 | 
			
		||||
				p_emac_dev->us_tx_list_size) >= p_emac_dev->uc_wakeup_threshold)
 | 
			
		||||
				&& p_emac_dev->func_wakeup_cb) {
 | 
			
		||||
			p_emac_dev->func_wakeup_cb();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//@}
 | 
			
		||||
 | 
			
		||||
#endif // SAM3XA_SERIES
 | 
			
		||||
 | 
			
		||||
/// @cond 0
 | 
			
		||||
/**INDENT-OFF**/
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
/**INDENT-ON**/
 | 
			
		||||
/// @endcond
 | 
			
		||||
		Reference in New Issue
	
	Block a user