/***************************************************************************//** | |
* @file | |
* SmartFusion MSS Ethernet MAC driver implementation. | |
* | |
* (c) Copyright 2007 Actel Corporation | |
* | |
* SVN $Revision: 2369 $ | |
* SVN $Date: 2010-03-01 18:31:45 +0000 (Mon, 01 Mar 2010) $ | |
* | |
******************************************************************************/ | |
/* | |
* | |
* | |
* NOTE: This driver has been modified specifically for use with the* uIP stack. | |
* It is no longer a generic driver. | |
* | |
* | |
*/ | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
#include "FreeRTOS.h" | |
#include "task.h" | |
#include "crc32.h" | |
#include "mss_ethernet_mac.h" | |
#include "mss_ethernet_mac_regs.h" | |
#include "mss_ethernet_mac_desc.h" | |
#include "mss_ethernet_mac_conf.h" | |
#include "../../CMSIS/mss_assert.h" | |
#include "phy.h" | |
/**************************** INTERNAL DEFINES ********************************/ | |
#define MAC_CHECK(CHECK,ERRNO) \ | |
{if(!(CHECK)){g_mss_mac.last_error=(ERRNO); configASSERT((CHECK));}} | |
/* | |
* Flags | |
*/ | |
#define FLAG_MAC_INIT_DONE 1u | |
#define FLAG_PERFECT_FILTERING 2u | |
#define FLAG_CRC_DISABLE 4u | |
#define FLAG_EXCEED_LIMIT 8u | |
/* | |
* Return types | |
*/ | |
#define MAC_OK 0 | |
#define MAC_FAIL (-1) | |
#define MAC_WRONG_PARAMETER (-2) | |
#define MAC_TOO_BIG_PACKET (-3) | |
#define MAC_BUFFER_IS_FULL (-4) | |
#define MAC_NOT_ENOUGH_SPACE (-5) | |
#define MAC_TIME_OUT (-6) | |
#define MAC_TOO_SMALL_PACKET (-7) | |
/* Allocating this many buffers will always ensure there is one free as, even | |
though TX_RING_SIZE is set to two, the two Tx descriptors will only ever point | |
to the same buffer. */ | |
#define macNUM_BUFFERS RX_RING_SIZE + TX_RING_SIZE | |
#define macBUFFER_SIZE 1488 | |
/***************************************************************/ | |
MAC_instance_t g_mss_mac; | |
/**************************** INTERNAL DATA ***********************************/ | |
#define ERROR_MESSAGE_COUNT 8 | |
#define MAX_ERROR_MESSAGE_WIDTH 40 | |
static const int8_t unknown_error[] = "Unknown error"; | |
static const int8_t ErrorMessages[][MAX_ERROR_MESSAGE_WIDTH] = { | |
"No error occured", | |
"Method failed", | |
"Wrong parameter pased to function", | |
"Frame is too long", | |
"Not enough space in buffer", | |
"Not enough space in buffer", | |
"Timed out", | |
"Frame is too small" | |
}; | |
/* | |
* Null variables | |
*/ | |
static uint8_t* NULL_buffer; | |
static MSS_MAC_callback_t NULL_callback; | |
/* Declare the uip_buf as a pointer, rather than the traditional array, as this | |
is a zero copy driver. uip_buf just gets set to whichever buffer is being | |
processed. */ | |
unsigned char *uip_buf = NULL; | |
/**************************** INTERNAL FUNCTIONS ******************************/ | |
static int32_t MAC_dismiss_bad_frames( void ); | |
static int32_t MAC_send_setup_frame( void ); | |
static int32_t MAC_stop_transmission( void ); | |
static void MAC_start_transmission( void ); | |
static int32_t MAC_stop_receiving( void ); | |
static void MAC_start_receiving( void ); | |
static void MAC_set_time_out( uint32_t time_out ); | |
static uint32_t MAC_get_time_out( void ); | |
static void MAC_memset(uint8_t *s, uint8_t c, uint32_t n); | |
static void MAC_memcpy(uint8_t *dest, const uint8_t *src, uint32_t n); | |
static void MAC_memset_All(MAC_instance_t *s, uint32_t c); | |
static unsigned char *MAC_obtain_buffer( void ); | |
static void MAC_release_buffer( unsigned char *pcBufferToRelease ); | |
#if( TX_RING_SIZE != 2 ) | |
#error This uIP Ethernet driver required TX_RING_SIZE to be set to 2 | |
#endif | |
/* Buffers that will dynamically be allocated to/from the Tx and Rx descriptors. | |
The union is used for alignment only. */ | |
static union xMAC_BUFFERS | |
{ | |
unsigned long ulAlignmentVariable; /* For alignment only, not used anywhere. */ | |
unsigned char ucBuffer[ macNUM_BUFFERS ][ macBUFFER_SIZE ]; | |
} xMACBuffers; | |
/* Each array position indicates whether or not the buffer of the same index | |
is currently allocated to a descriptor (pdTRUE) or is free for use (pdFALSE). */ | |
static unsigned char ucMACBufferInUse[ macNUM_BUFFERS ] = { 0 }; | |
/***************************************************************************//** | |
* Initializes the Ethernet Controller. | |
* This function will prepare the Ethernet Controller for first time use in a | |
* given hardware/software configuration. This function should be called before | |
* any other Ethernet API functions are called. | |
* | |
* Initialization of registers - config registers, enable Tx/Rx interrupts, | |
* enable Tx/Rx, initialize MAC addr, init PHY, autonegotiation, MAC address | |
* filter table (unicats/multicast)/hash init | |
*/ | |
void | |
MSS_MAC_init | |
( | |
uint8_t phy_address | |
) | |
{ | |
const uint8_t mac_address[6] = { DEFAULT_MAC_ADDRESS }; | |
int32_t a; | |
/* To start with all buffers are free. */ | |
for( a = 0; a < macNUM_BUFFERS; a++ ) | |
{ | |
ucMACBufferInUse[ a ] = pdFALSE; | |
} | |
/* Try to reset chip */ | |
MAC_BITBAND->CSR0_SWR = 1u; | |
do | |
{ | |
vTaskDelay( 10 ); | |
} while ( 1u == MAC_BITBAND->CSR0_SWR ); | |
/* Check reset values of some registers to constrol | |
* base address validity */ | |
configASSERT( MAC->CSR0 == 0xFE000000uL ); | |
configASSERT( MAC->CSR5 == 0xF0000000uL ); | |
configASSERT( MAC->CSR6 == 0x32000040uL ); | |
/* Instance setup */ | |
MAC_memset_All( &g_mss_mac, 0u ); | |
g_mss_mac.base_address = MAC_BASE; | |
g_mss_mac.phy_address = phy_address; | |
for( a=0; a<RX_RING_SIZE; a++ ) | |
{ | |
/* Give the ownership to the MAC */ | |
g_mss_mac.rx_descriptors[a].descriptor_0 = RDES0_OWN; | |
g_mss_mac.rx_descriptors[a].descriptor_1 = (MSS_RX_BUFF_SIZE << RDES1_RBS1_OFFSET); | |
/* Allocate a buffer to the descriptor, then mark the buffer as in use | |
(not free). */ | |
g_mss_mac.rx_descriptors[a].buffer_1 = ( unsigned long ) &( xMACBuffers.ucBuffer[ a ][ 0 ] ); | |
ucMACBufferInUse[ a ] = pdTRUE; | |
} | |
g_mss_mac.rx_descriptors[RX_RING_SIZE-1].descriptor_1 |= RDES1_RER; | |
for( a = 0; a < TX_RING_SIZE; a++ ) | |
{ | |
/* Buffers only get allocated to the Tx buffers when something is | |
actually tranmitted. */ | |
g_mss_mac.tx_descriptors[a].buffer_1 = ( unsigned long ) NULL; | |
} | |
g_mss_mac.tx_descriptors[TX_RING_SIZE - 1].descriptor_1 |= TDES1_TER; | |
/* Configurable settings */ | |
MAC_BITBAND->CSR0_DBO = DESCRIPTOR_BYTE_ORDERING_MODE; | |
MAC->CSR0 = (MAC->CSR0 & ~CSR0_PBL_MASK) | ((uint32_t)PROGRAMMABLE_BURST_LENGTH << CSR0_PBL_SHIFT); | |
MAC_BITBAND->CSR0_BLE = BUFFER_BYTE_ORDERING_MODE; | |
MAC_BITBAND->CSR0_BAR = (uint32_t)BUS_ARBITRATION_SCHEME; | |
/* Fixed settings */ | |
/* No space between descriptors */ | |
MAC->CSR0 = MAC->CSR0 &~ CSR0_DSL_MASK; | |
/* General-purpose timer works in continuous mode */ | |
MAC_BITBAND->CSR11_CON = 1u; | |
/* Start general-purpose */ | |
MAC->CSR11 = (MAC->CSR11 & ~CSR11_TIM_MASK) | (0x0000FFFFuL << CSR11_TIM_SHIFT); | |
/* Ensure promiscous mode is off (it should be by default anyway). */ | |
MAC_BITBAND->CSR6_PR = 0; | |
/* Perfect filter. */ | |
MAC_BITBAND->CSR6_HP = 1; | |
/* Pass multcast. */ | |
MAC_BITBAND->CSR6_PM = 1; | |
/* Set descriptors */ | |
MAC->CSR3 = (uint32_t)&(g_mss_mac.rx_descriptors[0].descriptor_0); | |
MAC->CSR4 = (uint32_t)&(g_mss_mac.tx_descriptors[0].descriptor_0); | |
/* enable normal interrupts */ | |
MAC_BITBAND->CSR7_NIE = 1u; | |
/* Set default MAC address and reset mac filters */ | |
MAC_memcpy( g_mss_mac.mac_address, mac_address, 6u ); | |
MSS_MAC_set_mac_address((uint8_t *)mac_address); | |
/* Detect PHY */ | |
if( g_mss_mac.phy_address > MSS_PHY_ADDRESS_MAX ) | |
{ | |
PHY_probe(); | |
configASSERT( g_mss_mac.phy_address <= MSS_PHY_ADDRESS_MAX ); | |
} | |
/* Reset PHY */ | |
PHY_reset(); | |
/* Configure chip according to PHY status */ | |
MSS_MAC_auto_setup_link(); | |
/* Ensure uip_buf starts by pointing somewhere. */ | |
uip_buf = MAC_obtain_buffer(); | |
} | |
/***************************************************************************//** | |
* Sets the configuration of the Ethernet Controller. | |
* After the EthernetInit function has been called, this API function can be | |
* used to configure the various features of the Ethernet Controller. | |
* | |
* @param instance Pointer to a MAC_instance_t structure | |
* @param config The logical OR of the following values: | |
* - #MSS_MAC_CFG_RECEIVE_ALL | |
* - #MSS_MAC_CFG_TRANSMIT_THRESHOLD_MODE | |
* - #MSS_MSS_MAC_CFG_STORE_AND_FORWARD | |
* - #MAC_CFG_THRESHOLD_CONTROL_[00,01,10,11] | |
* - #MSS_MAC_CFG_FULL_DUPLEX_MODE | |
* - #MSS_MAC_CFG_PASS_ALL_MULTICAST | |
* - #MSS_MAC_CFG_PROMISCUOUS_MODE | |
* - #MSS_MAC_CFG_PASS_BAD_FRAMES | |
* @see MAC_get_configuration() | |
*/ | |
void | |
MSS_MAC_configure | |
( | |
uint32_t configuration | |
) | |
{ | |
int32_t ret; | |
ret = MAC_stop_transmission(); | |
configASSERT( ret == MAC_OK ); | |
ret = MAC_stop_receiving(); | |
configASSERT( ret == MAC_OK ); | |
MAC_BITBAND->CSR6_RA = (uint32_t)(((configuration & MSS_MAC_CFG_RECEIVE_ALL) != 0u) ? 1u : 0u ); | |
MAC_BITBAND->CSR6_TTM = (((configuration & MSS_MAC_CFG_TRANSMIT_THRESHOLD_MODE) != 0u) ? 1u : 0u ); | |
MAC_BITBAND->CSR6_SF = (uint32_t)(((configuration & MSS_MAC_CFG_STORE_AND_FORWARD) != 0u) ? 1u : 0u ); | |
switch( configuration & MSS_MAC_CFG_THRESHOLD_CONTROL_11 ) { | |
case MSS_MAC_CFG_THRESHOLD_CONTROL_00: | |
MAC->CSR6 = MAC->CSR6 & ~CSR6_TR_MASK; | |
break; | |
case MSS_MAC_CFG_THRESHOLD_CONTROL_01: | |
MAC->CSR6 = (MAC->CSR6 & ~CSR6_TR_MASK) | ((uint32_t)1 << CSR6_TR_SHIFT ); | |
break; | |
case MSS_MAC_CFG_THRESHOLD_CONTROL_10: | |
MAC->CSR6 = (MAC->CSR6 & ~CSR6_TR_MASK) | ((uint32_t)2 << CSR6_TR_SHIFT ); | |
break; | |
case MSS_MAC_CFG_THRESHOLD_CONTROL_11: | |
MAC->CSR6 = (MAC->CSR6 & ~CSR6_TR_MASK) | ((uint32_t)3 << CSR6_TR_SHIFT ); | |
break; | |
default: | |
break; | |
} | |
MAC_BITBAND->CSR6_FD = (uint32_t)(((configuration & MSS_MAC_CFG_FULL_DUPLEX_MODE) != 0u) ? 1u : 0u ); | |
MAC_BITBAND->CSR6_PM = (uint32_t)(((configuration & MSS_MAC_CFG_PASS_ALL_MULTICAST) != 0u) ? 1u : 0u ); | |
MAC_BITBAND->CSR6_PR = (uint32_t)(((configuration & MSS_MAC_CFG_PROMISCUOUS_MODE) != 0u) ? 1u : 0u ); | |
MAC_BITBAND->CSR6_PB = (uint32_t)(((configuration & MSS_MAC_CFG_PASS_BAD_FRAMES) != 0u) ? 1u : 0u ); | |
PHY_set_link_type( (uint8_t) | |
((((configuration & MSS_MAC_CFG_TRANSMIT_THRESHOLD_MODE) != 0u) ? MSS_MAC_LINK_STATUS_100MB : 0u ) | | |
(((configuration & MSS_MAC_CFG_FULL_DUPLEX_MODE) != 0u) ? MSS_MAC_LINK_STATUS_FDX : 0u )) ); | |
MSS_MAC_auto_setup_link(); | |
} | |
/***************************************************************************//** | |
* Returns the configuration of the Ethernet Controller. | |
* | |
* @param instance Pointer to a MAC_instance_t structure | |
* @return The logical OR of the following values: | |
* - #MSS_MAC_CFG_RECEIVE_ALL | |
* - #MSS_MAC_CFG_TRANSMIT_THRESHOLD_MODE | |
* - #MSS_MAC_CFG_STORE_AND_FORWARD | |
* - #MAC_CFG_THRESHOLD_CONTROL_[00,01,10,11] | |
* - #MSS_MAC_CFG_FULL_DUPLEX_MODE | |
* - #MSS_MAC_CFG_PASS_ALL_MULTICAST | |
* - #MSS_MAC_CFG_PROMISCUOUS_MODE | |
* - #MSS_MAC_CFG_INVERSE_FILTERING | |
* - #MSS_MAC_CFG_PASS_BAD_FRAMES | |
* - #MSS_MAC_CFG_HASH_ONLY_FILTERING_MODE | |
* - #MSS_MAC_CFG_HASH_PERFECT_RECEIVE_FILTERING_MODE | |
* @see MAC_configure() | |
*/ | |
int32_t | |
MSS_MAC_get_configuration( void ) | |
{ | |
uint32_t configuration; | |
configuration = 0u; | |
if( MAC_BITBAND->CSR6_RA != 0u ) { | |
configuration |= MSS_MAC_CFG_RECEIVE_ALL; | |
} | |
if( MAC_BITBAND->CSR6_TTM != 0u ) { | |
configuration |= MSS_MAC_CFG_TRANSMIT_THRESHOLD_MODE; | |
} | |
if( MAC_BITBAND->CSR6_SF != 0u ) { | |
configuration |= MSS_MAC_CFG_STORE_AND_FORWARD; | |
} | |
switch( (MAC->CSR6 & CSR6_TR_MASK) >> CSR6_TR_SHIFT ) { | |
case 1: configuration |= MSS_MAC_CFG_THRESHOLD_CONTROL_01; break; | |
case 2: configuration |= MSS_MAC_CFG_THRESHOLD_CONTROL_10; break; | |
case 3: configuration |= MSS_MAC_CFG_THRESHOLD_CONTROL_11; break; | |
default: break; | |
} | |
if( MAC_BITBAND->CSR6_FD != 0u ) { | |
configuration |= MSS_MAC_CFG_FULL_DUPLEX_MODE; | |
} | |
if( MAC_BITBAND->CSR6_PM != 0u ) { | |
configuration |= MSS_MAC_CFG_PASS_ALL_MULTICAST; | |
} | |
if( MAC_BITBAND->CSR6_PR != 0u ) { | |
configuration |= MSS_MAC_CFG_PROMISCUOUS_MODE; | |
} | |
if( MAC_BITBAND->CSR6_IF != 0u ) { | |
configuration |= MSS_MAC_CFG_INVERSE_FILTERING; | |
} | |
if( MAC_BITBAND->CSR6_PB != 0u ) { | |
configuration |= MSS_MAC_CFG_PASS_BAD_FRAMES; | |
} | |
if( MAC_BITBAND->CSR6_HO != 0u ) { | |
configuration |= MSS_MAC_CFG_HASH_ONLY_FILTERING_MODE; | |
} | |
if( MAC_BITBAND->CSR6_HP != 0u ) { | |
configuration |= MSS_MAC_CFG_HASH_PERFECT_RECEIVE_FILTERING_MODE; | |
} | |
return (int32_t)configuration; | |
} | |
/***************************************************************************//** | |
Sends a packet from the uIP stack to the Ethernet Controller. | |
The MSS_MAC_tx_packet() function is used to send a packet to the MSS Ethernet | |
MAC. This function writes uip_len bytes of the packet contained in uip_buf into | |
the transmit FIFO and then activates the transmitter for this packet. If space | |
is available in the FIFO, the function will return once pac_len bytes of the | |
packet have been placed into the FIFO and the transmitter has been started. | |
This function will not wait for the transmission to complete. | |
@return | |
The function returns zero if a timeout occurs otherwise it returns size of the packet. | |
@see MAC_rx_packet() | |
*/ | |
int32_t | |
MSS_MAC_tx_packet | |
( | |
unsigned short usLength | |
) | |
{ | |
uint32_t desc; | |
unsigned long ulDescriptor; | |
int32_t error = MAC_OK; | |
configASSERT( uip_buf != NULL_buffer ); | |
configASSERT( usLength >= 12 ); | |
if( (g_mss_mac.flags & FLAG_EXCEED_LIMIT) == 0u ) | |
{ | |
configASSERT( usLength <= MSS_MAX_PACKET_SIZE ); | |
} | |
taskENTER_CRITICAL(); | |
{ | |
/* Check both Tx descriptors are free, meaning the double send has completed. */ | |
if( ( ( (g_mss_mac.tx_descriptors[ 0 ].descriptor_0) & TDES0_OWN) == TDES0_OWN ) || ( ( (g_mss_mac.tx_descriptors[ 1 ].descriptor_0) & TDES0_OWN) == TDES0_OWN ) ) | |
{ | |
error = MAC_BUFFER_IS_FULL; | |
} | |
} | |
taskEXIT_CRITICAL(); | |
configASSERT( ( g_mss_mac.tx_desc_index == 0 ) ); | |
if( error == MAC_OK ) | |
{ | |
/* Ensure nothing is going to get sent until both descriptors are ready. | |
This is done to prevent a Tx end occurring prior to the second descriptor | |
being ready. */ | |
MAC_BITBAND->CSR6_ST = 0u; | |
/* Assumed TX_RING_SIZE == 2. A #error directive checks this is the | |
case. */ | |
taskENTER_CRITICAL(); | |
{ | |
for( ulDescriptor = 0; ulDescriptor < TX_RING_SIZE; ulDescriptor++ ) | |
{ | |
g_mss_mac.tx_descriptors[ g_mss_mac.tx_desc_index ].descriptor_1 = 0u; | |
if( (g_mss_mac.flags & FLAG_CRC_DISABLE) != 0u ) { | |
g_mss_mac.tx_descriptors[ g_mss_mac.tx_desc_index ].descriptor_1 |= TDES1_AC; | |
} | |
/* Every buffer can hold a full frame so they are always first and last | |
descriptor */ | |
g_mss_mac.tx_descriptors[ g_mss_mac.tx_desc_index ].descriptor_1 |= TDES1_LS | TDES1_FS; | |
/* set data size */ | |
g_mss_mac.tx_descriptors[ g_mss_mac.tx_desc_index ].descriptor_1 |= usLength; | |
/* reset end of ring */ | |
g_mss_mac.tx_descriptors[TX_RING_SIZE-1].descriptor_1 |= TDES1_TER; | |
if( usLength > MSS_TX_BUFF_SIZE ) /* FLAG_EXCEED_LIMIT */ | |
{ | |
usLength = (uint16_t)MSS_TX_BUFF_SIZE; | |
} | |
/* The data buffer is assigned to the Tx descriptor. */ | |
g_mss_mac.tx_descriptors[ g_mss_mac.tx_desc_index ].buffer_1 = ( unsigned long ) uip_buf; | |
/* update counters */ | |
desc = g_mss_mac.tx_descriptors[ g_mss_mac.tx_desc_index ].descriptor_0; | |
if( (desc & TDES0_LO) != 0u ) { | |
g_mss_mac.statistics.tx_loss_of_carrier++; | |
} | |
if( (desc & TDES0_NC) != 0u ) { | |
g_mss_mac.statistics.tx_no_carrier++; | |
} | |
if( (desc & TDES0_LC) != 0u ) { | |
g_mss_mac.statistics.tx_late_collision++; | |
} | |
if( (desc & TDES0_EC) != 0u ) { | |
g_mss_mac.statistics.tx_excessive_collision++; | |
} | |
if( (desc & TDES0_UF) != 0u ) { | |
g_mss_mac.statistics.tx_underflow_error++; | |
} | |
g_mss_mac.statistics.tx_collision_count += | |
(desc >> TDES0_CC_OFFSET) & TDES0_CC_MASK; | |
/* Give ownership of descriptor to the MAC */ | |
g_mss_mac.tx_descriptors[ g_mss_mac.tx_desc_index ].descriptor_0 = TDES0_OWN; | |
g_mss_mac.tx_desc_index = (g_mss_mac.tx_desc_index + 1u) % (uint32_t)TX_RING_SIZE; | |
} | |
} | |
taskEXIT_CRITICAL(); | |
} | |
if (error == MAC_OK) | |
{ | |
error = (int32_t)usLength; | |
/* Start sending now both descriptors are set up. This is done to | |
prevent a Tx end occurring prior to the second descriptor being | |
ready. */ | |
MAC_BITBAND->CSR6_ST = 1u; | |
MAC->CSR1 = 1u; | |
/* The buffer pointed to by uip_buf is now assigned to a Tx descriptor. | |
Find anothere free buffer for uip_buf. */ | |
uip_buf = MAC_obtain_buffer(); | |
} | |
else | |
{ | |
error = 0; | |
} | |
return ( error ); | |
} | |
/***************************************************************************//** | |
* Returns available packet size. | |
* | |
* @param instance Pointer to a MAC_instance_t structure | |
* @return size of packet, bigger than 0, if a packet is available. | |
* If not, returns 0. | |
* @see MAC_rx_packet() | |
*/ | |
int32_t | |
MSS_MAC_rx_pckt_size | |
( | |
void | |
) | |
{ | |
int32_t retval; | |
MAC_dismiss_bad_frames(); | |
if( (g_mss_mac.rx_descriptors[ g_mss_mac.rx_desc_index ].descriptor_0 & RDES0_OWN) != 0u ) | |
{ | |
/* Current descriptor is empty */ | |
retval = 0; | |
} | |
else | |
{ | |
uint32_t frame_length; | |
frame_length = ( g_mss_mac.rx_descriptors[ g_mss_mac.rx_desc_index ].descriptor_0 >> RDES0_FL_OFFSET ) & RDES0_FL_MASK; | |
retval = (int32_t)( frame_length ); | |
} | |
return retval; | |
} | |
/***************************************************************************//** | |
* Receives a packet from the Ethernet Controller into the uIP stack. | |
* This function reads a packet from the receive FIFO of the controller and | |
* places it into uip_buf. | |
* @return Size of packet if packet fits in uip_buf. | |
* 0 if there is no received packet. | |
* @see MAC_rx_pckt_size() | |
* @see MAC_tx_packet() | |
*/ | |
int32_t | |
MSS_MAC_rx_packet | |
( | |
void | |
) | |
{ | |
uint16_t frame_length=0u; | |
MAC_dismiss_bad_frames(); | |
if( (g_mss_mac.rx_descriptors[ g_mss_mac.rx_desc_index ].descriptor_0 & RDES0_OWN) == 0u ) | |
{ | |
frame_length = ( ( | |
g_mss_mac.rx_descriptors[ g_mss_mac.rx_desc_index ].descriptor_0 >> | |
RDES0_FL_OFFSET ) & RDES0_FL_MASK ); | |
/* strip crc */ | |
frame_length -= 4u; | |
if( frame_length > macBUFFER_SIZE ) { | |
return MAC_NOT_ENOUGH_SPACE; | |
} | |
/* uip_buf is about to point to the buffer that contains the received | |
data, mark the buffer that uip_buf is currently pointing to as free | |
again. */ | |
MAC_release_buffer( uip_buf ); | |
uip_buf = ( unsigned char * ) g_mss_mac.rx_descriptors[ g_mss_mac.rx_desc_index ].buffer_1; | |
/* The buffer the Rx descriptor was pointing to is now in use by the | |
uIP stack - allocate a new buffer to the Rx descriptor. */ | |
g_mss_mac.rx_descriptors[ g_mss_mac.rx_desc_index ].buffer_1 = ( unsigned long ) MAC_obtain_buffer(); | |
MSS_MAC_prepare_rx_descriptor(); | |
} | |
return ((int32_t)frame_length); | |
} | |
/***************************************************************************//** | |
* Receives a packet from the Ethernet Controller. | |
* This function reads a packet from the receive FIFO of the controller and | |
* sets the address of pacData to the received data. | |
* If time_out parameter is zero the function will return | |
* immediately (after the copy operation if data is available. Otherwise the function | |
* will keep trying to read till time_out expires or data is read, if MSS_MAC_BLOCKING | |
* value is given as time_out, function will wait for the reception to complete. | |
* | |
* @param pacData The pointer to the packet data. | |
* @param time_out Time out value in milli seconds for receiving. | |
* if value is #MSS_MAC_BLOCKING, there will be no time out. | |
* if value is #MSS_MAC_NONBLOCKING, function will return immediately | |
* if there is no packet waiting. | |
* Otherwise value must be greater than 0 and smaller than | |
* 0x01000000. | |
* @return Size of packet if packet fits in pacData. | |
* 0 if there is no received packet. | |
* @see MAC_rx_pckt_size() | |
* @see MAC_tx_packet() | |
*/ | |
int32_t | |
MSS_MAC_rx_packet_ptrset | |
( | |
uint8_t **pacData, | |
uint32_t time_out | |
) | |
{ | |
uint16_t frame_length = 0u; | |
int8_t exit = 0; | |
configASSERT( (time_out == MSS_MAC_BLOCKING) || | |
(time_out == MSS_MAC_NONBLOCKING) || | |
((time_out >= 1) && (time_out <= 0x01000000UL)) ); | |
MAC_dismiss_bad_frames(); | |
/* wait for a packet */ | |
if( time_out != MSS_MAC_BLOCKING ) { | |
if( time_out == MSS_MAC_NONBLOCKING ) { | |
MAC_set_time_out( 0u ); | |
} else { | |
MAC_set_time_out( time_out ); | |
} | |
} | |
while( ((g_mss_mac.rx_descriptors[ g_mss_mac.rx_desc_index ].descriptor_0 & | |
RDES0_OWN) != 0u) && (exit == 0) ) | |
{ | |
if( time_out != MSS_MAC_BLOCKING ) | |
{ | |
if( MAC_get_time_out() == 0u ) { | |
exit = 1; | |
} | |
} | |
} | |
if(exit == 0) | |
{ | |
frame_length = ( ( | |
g_mss_mac.rx_descriptors[ g_mss_mac.rx_desc_index ].descriptor_0 >> | |
RDES0_FL_OFFSET ) & RDES0_FL_MASK ); | |
/* strip crc */ | |
frame_length -= 4u; | |
/* Here we are setting the buffer 'pacData' address to the address | |
RX descriptor address. After this is called, the following function | |
must be called 'MAC_prepare_rx_descriptor' | |
to prepare the current rx descriptor for receiving the next packet. | |
*/ | |
*pacData = (uint8_t *)g_mss_mac.rx_descriptors[ g_mss_mac.rx_desc_index ].buffer_1 ; | |
} | |
return ((int32_t)frame_length); | |
} | |
/***************************************************************************//** | |
* Returns the status of connection. | |
* | |
* @return the logical OR of the following values: | |
* #MSS_MAC_LINK_STATUS_LINK - Link up/down | |
* #MSS_MAC_LINK_STATUS_100MB - Connection is 100Mb/10Mb | |
* #MSS_MAC_LINK_STATUS_FDX - Connection is full/half duplex | |
* @see MAC_auto_setup_link() | |
*/ | |
int32_t | |
MSS_MAC_link_status | |
( | |
void | |
) | |
{ | |
uint32_t link; | |
link = PHY_link_status(); | |
if( link == MSS_MAC_LINK_STATUS_LINK ) { | |
link |= PHY_link_type(); | |
} | |
return ((int32_t)link); | |
} | |
/***************************************************************************//** | |
* Setups the link between PHY and MAC and returns the status of connection. | |
* | |
* @return the logical OR of the following values: | |
* #MSS_MAC_LINK_STATUS_LINK - Link up/down | |
* #MSS_MAC_LINK_STATUS_100MB - Connection is 100Mb/10Mb | |
* #MSS_MAC_LINK_STATUS_FDX - Connection is full/half duplex | |
* @see MAC_link_status() | |
*/ | |
int32_t | |
MSS_MAC_auto_setup_link | |
( | |
void | |
) | |
{ | |
int32_t link; | |
PHY_auto_negotiate(); | |
link = MSS_MAC_link_status(); | |
if( (link & MSS_MAC_LINK_STATUS_LINK) != 0u ) { | |
int32_t ret; | |
ret = MAC_stop_transmission(); | |
MAC_CHECK( ret == MAC_OK, ret ); | |
ret = MAC_stop_receiving(); | |
MAC_CHECK( ret == MAC_OK, ret ); | |
MAC_BITBAND->CSR6_TTM = (uint32_t)((((uint32_t)link & MSS_MAC_LINK_STATUS_100MB) != 0u) ? 1u : 0u ); | |
MAC_BITBAND->CSR6_FD = (uint32_t)((((uint32_t)link & MSS_MAC_LINK_STATUS_FDX) != 0u) ? 1u : 1u ); | |
MAC_start_transmission(); | |
MAC_start_receiving(); | |
} | |
return link; | |
} | |
/***************************************************************************//** | |
* Sets mac address. New address must be unicast. | |
* | |
* @param new_address Pointer to a MAC_instance_t structure | |
* @see MAC_get_mac_address() | |
*/ | |
void | |
MSS_MAC_set_mac_address | |
( | |
const uint8_t *new_address | |
) | |
{ | |
/* Check if the new address is unicast */ | |
configASSERT( (new_address[0]&1) == 0 ); | |
MAC_memcpy( g_mss_mac.mac_address, new_address, 6u ); | |
if((g_mss_mac.flags & FLAG_PERFECT_FILTERING) != 0u ) { | |
int32_t a; | |
/* set unused filters to the new mac address */ | |
for( a=14*6; a>=0; a-=6 ) { | |
if( (g_mss_mac.mac_filter_data[a] & 1u) != 0u ) { | |
/* Filters with multicast addresses are used */ | |
a = -1; | |
} else { | |
MAC_memcpy( &(g_mss_mac.mac_filter_data[a]), | |
g_mss_mac.mac_address, 6u ); | |
} | |
} | |
} | |
MAC_send_setup_frame(); | |
} | |
/***************************************************************************//** | |
* Returns mac address. | |
* | |
* @param address Pointer to receive the MAC address | |
* @see MAC_set_mac_address() | |
*/ | |
void | |
MSS_MAC_get_mac_address | |
( | |
uint8_t *address | |
) | |
{ | |
MAC_memcpy( address, g_mss_mac.mac_address, 6u ); | |
} | |
/***************************************************************************//** | |
* Sets mac address filters. Addresses must be multicast. | |
* | |
* @param filter_count number of addresses | |
* @param filters Pointer to addresses to be filtered | |
*/ | |
void | |
MSS_MAC_set_mac_filters | |
( | |
uint16_t filter_count, | |
const uint8_t *filters | |
) | |
{ | |
configASSERT( (filter_count==0) || (filters != NULL_buffer) ); | |
/* Check if the mac addresses is multicast */ | |
{ | |
int32_t a; | |
for( a = 0u; a < filter_count; a++ ) { | |
configASSERT( (filters[a*6]&1) == 1 ); | |
} | |
} | |
if( filter_count <= 15 ){ | |
int32_t a; | |
g_mss_mac.flags |= FLAG_PERFECT_FILTERING; | |
/* copy new filters */ | |
MAC_memcpy( g_mss_mac.mac_filter_data, filters, (uint32_t)(filter_count*6)); | |
/* set unused filters to our mac address */ | |
for( a=filter_count; a<15; a++ ) { | |
MAC_memcpy( &(g_mss_mac.mac_filter_data[a*6]), | |
g_mss_mac.mac_address, 6 ); | |
} | |
} else { | |
int32_t a,b; | |
uint32_t hash; | |
g_mss_mac.flags &= ~FLAG_PERFECT_FILTERING; | |
/* reset hash table */ | |
MAC_memset( g_mss_mac.mac_filter_data, 0u, 64u ); | |
for( a=0, b=0; a<filter_count; a++, b+=6 ) { | |
hash = mss_ethernet_crc( &(filters[b]), 6 ) & 0x1FF; | |
g_mss_mac.mac_filter_data[ hash / 8 ] |= 1 << (hash & 0x7); | |
} | |
} | |
MAC_send_setup_frame(); | |
} | |
/***************************************************************************//** | |
* MAC interrupt service routine. | |
* | |
* @param instance Pointer to a MAC_instance_t structure | |
* @see MAC_set_callback() | |
*/ | |
#if defined(__GNUC__) | |
__attribute__((__interrupt__)) void EthernetMAC_IRQHandler( void ) | |
#else | |
void EthernetMAC_IRQHandler( void ) | |
#endif | |
{ | |
uint32_t events; | |
uint32_t intr_status; | |
events = 0u; | |
intr_status = MAC->CSR5; | |
if( (intr_status & CSR5_NIS_MASK) != 0u ) { | |
if( (intr_status & CSR5_TI_MASK) != 0u ) { /* Transmit */ | |
g_mss_mac.statistics.tx_interrupts++; | |
events |= MSS_MAC_EVENT_PACKET_SEND; | |
} | |
if( (intr_status & CSR5_RI_MASK) != 0u ) { /* Receive */ | |
g_mss_mac.statistics.rx_interrupts++; | |
events |= MSS_MAC_EVENT_PACKET_RECEIVED; | |
} | |
} | |
/* Clear interrupts */ | |
MAC->CSR5 = CSR5_INT_BITS; | |
if( (events != 0u) && (g_mss_mac.listener != NULL_callback) ) { | |
g_mss_mac.listener( events ); | |
} | |
} | |
/***************************************************************************//** | |
* Sets MAC event listener. | |
* Sets the given event listener function to be triggered inside MAC_isr(). | |
* Assigning NULL pointer as the listener function will disable it. | |
* | |
* @param instance Pointer to a MAC_instance_t structure | |
* @param listener function pointer to a MAC_callback_t function | |
* @return #MAC_OK if everything is OK | |
* #MAC_WRONG_PARAMETER if instance is null or hasn't been | |
* initialized. | |
* @see MAC_isr() | |
*/ | |
void | |
MSS_MAC_set_callback | |
( | |
MSS_MAC_callback_t listener | |
) | |
{ | |
/* disable tx and rx interrupts */ | |
MAC_BITBAND->CSR7_RIE = 0u; | |
MAC_BITBAND->CSR7_TIE = 0u; | |
g_mss_mac.listener = listener; | |
if( listener != NULL_callback ) { | |
/* enable tx and rx interrupts */ | |
MAC_BITBAND->CSR7_RIE = 1u; | |
MAC_BITBAND->CSR7_TIE = 1u; | |
} | |
} | |
/***************************************************************************//** | |
* Returns description of last error. | |
* | |
* @param instance Pointer to a MAC_instance_t structure | |
* @return A string describing the error. This string must not be | |
* modified by the application. | |
* #MAC_WRONG_PARAMETER if instance is null or hasn't been | |
* initialized. | |
*/ | |
const int8_t* | |
MSS_MAC_last_error | |
( | |
void | |
) | |
{ | |
int8_t error_msg_nb; | |
const int8_t* returnvalue; | |
error_msg_nb = -(g_mss_mac.last_error); | |
if( error_msg_nb >= ERROR_MESSAGE_COUNT ) { | |
returnvalue = unknown_error; | |
} else { | |
returnvalue = ErrorMessages[error_msg_nb]; | |
} | |
return returnvalue; | |
} | |
/***************************************************************************//** | |
* Returns statistics counter of stat_id identifier. | |
* | |
* @param instance Pointer to a MAC_instance_t structure | |
* @param stat_id Identifier of statistics counter. | |
* @return Statistics counter of stat_id identifier. | |
* On error returns 0. | |
*/ | |
uint32_t | |
MSS_MAC_get_statistics | |
( | |
mss_mac_statistics_id_t stat_id | |
) | |
{ | |
uint32_t returnval = 0u; | |
switch( stat_id ) { | |
case MSS_MAC_RX_INTERRUPTS: | |
returnval = g_mss_mac.statistics.rx_interrupts; | |
break; | |
case MSS_MAC_RX_FILTERING_FAIL: | |
returnval = g_mss_mac.statistics.rx_filtering_fail; | |
break; | |
case MSS_MAC_RX_DESCRIPTOR_ERROR: | |
returnval = g_mss_mac.statistics.rx_descriptor_error; | |
break; | |
case MSS_MAC_RX_RUNT_FRAME: | |
returnval = g_mss_mac.statistics.rx_runt_frame; | |
break; | |
case MSS_MAC_RX_NOT_FIRST: | |
returnval = g_mss_mac.statistics.rx_not_first; | |
break; | |
case MSS_MAC_RX_NOT_LAST: | |
returnval = g_mss_mac.statistics.rx_not_last; | |
break; | |
case MSS_MAC_RX_FRAME_TOO_LONG: | |
returnval = g_mss_mac.statistics.rx_frame_too_long; | |
break; | |
case MSS_MAC_RX_COLLISION_SEEN: | |
returnval = g_mss_mac.statistics.rx_collision_seen; | |
break; | |
case MSS_MAC_RX_CRC_ERROR: | |
returnval = g_mss_mac.statistics.rx_crc_error; | |
break; | |
case MSS_MAC_RX_FIFO_OVERFLOW: | |
returnval = g_mss_mac.statistics.rx_fifo_overflow; | |
break; | |
case MSS_MAC_RX_MISSED_FRAME: | |
returnval = g_mss_mac.statistics.rx_missed_frame; | |
break; | |
case MSS_MAC_TX_INTERRUPTS: | |
returnval = g_mss_mac.statistics.tx_interrupts; | |
break; | |
case MSS_MAC_TX_LOSS_OF_CARRIER: | |
returnval = g_mss_mac.statistics.tx_loss_of_carrier; | |
break; | |
case MSS_MAC_TX_NO_CARRIER: | |
returnval = g_mss_mac.statistics.tx_no_carrier; | |
break; | |
case MSS_MAC_TX_LATE_COLLISION: | |
returnval = g_mss_mac.statistics.tx_late_collision; | |
break; | |
case MSS_MAC_TX_EXCESSIVE_COLLISION: | |
returnval = g_mss_mac.statistics.tx_excessive_collision; | |
break; | |
case MSS_MAC_TX_COLLISION_COUNT: | |
returnval = g_mss_mac.statistics.tx_collision_count; | |
break; | |
case MSS_MAC_TX_UNDERFLOW_ERROR: | |
returnval = g_mss_mac.statistics.tx_underflow_error; | |
break; | |
default: | |
break; | |
} | |
return returnval; | |
} | |
/**************************** INTERNAL FUNCTIONS ******************************/ | |
/***************************************************************************//** | |
* Prepares current rx descriptor for receiving. | |
*/ | |
void | |
MSS_MAC_prepare_rx_descriptor | |
( | |
void | |
) | |
{ | |
uint32_t desc; | |
/* update counters */ | |
desc = g_mss_mac.rx_descriptors[ g_mss_mac.rx_desc_index ].descriptor_0; | |
if( (desc & RDES0_FF) != 0u ) { | |
g_mss_mac.statistics.rx_filtering_fail++; | |
} | |
if( (desc & RDES0_DE) != 0u ) { | |
g_mss_mac.statistics.rx_descriptor_error++; | |
} | |
if( (desc & RDES0_RF) != 0u ) { | |
g_mss_mac.statistics.rx_runt_frame++; | |
} | |
if( (desc & RDES0_FS) == 0u ) { | |
g_mss_mac.statistics.rx_not_first++; | |
} | |
if( (desc & RDES0_LS) == 0u ) { | |
g_mss_mac.statistics.rx_not_last++; | |
} | |
if( (desc & RDES0_TL) != 0u ) { | |
g_mss_mac.statistics.rx_frame_too_long++; | |
} | |
if( (desc & RDES0_CS) != 0u ) { | |
g_mss_mac.statistics.rx_collision_seen++; | |
} | |
if( (desc & RDES0_CE) != 0u ) { | |
g_mss_mac.statistics.rx_crc_error++; | |
} | |
desc = MAC->CSR8; | |
g_mss_mac.statistics.rx_fifo_overflow += | |
(desc & (CSR8_OCO_MASK|CSR8_FOC_MASK)) >> CSR8_FOC_SHIFT; | |
g_mss_mac.statistics.rx_missed_frame += | |
(desc & (CSR8_MFO_MASK|CSR8_MFC_MASK)); | |
/* Give ownership of descriptor to the MAC */ | |
g_mss_mac.rx_descriptors[ g_mss_mac.rx_desc_index ].descriptor_0 = RDES0_OWN; | |
g_mss_mac.rx_desc_index = (g_mss_mac.rx_desc_index + 1u) % RX_RING_SIZE; | |
/* Start receive */ | |
MAC_start_receiving(); | |
} | |
/***************************************************************************//** | |
* Prepares a setup frame and sends it to MAC. | |
* This function is blocking. | |
* @return #MAC_OK if everything is ok. | |
* #MAC_TIME_OUT if timed out before packet send. | |
*/ | |
static int32_t | |
MAC_send_setup_frame | |
( | |
void | |
) | |
{ | |
volatile MAC_descriptor_t descriptor; | |
uint8_t frame_data[192]; | |
uint8_t *data; | |
int32_t a,b,c,d; | |
int32_t ret; | |
/* prepare descriptor */ | |
descriptor.descriptor_0 = TDES0_OWN; | |
descriptor.descriptor_1 = TDES1_SET | TDES1_TER | | |
(sizeof(frame_data) << TDES1_TBS1_OFFSET); | |
if( (g_mss_mac.flags & FLAG_PERFECT_FILTERING) == 0u ) { | |
descriptor.descriptor_1 |= TDES1_FT0; | |
} | |
descriptor.buffer_1 = (uint32_t)frame_data; | |
descriptor.buffer_2 = 0u; | |
/* prepare frame */ | |
if( (g_mss_mac.flags & FLAG_PERFECT_FILTERING) != 0u ) { | |
b = 0; | |
d = 12; | |
c = 90; | |
} else { | |
b = 156; | |
d = 0; | |
c = 64; | |
} | |
data = g_mss_mac.mac_address; | |
frame_data[b] = data[0]; | |
frame_data[b+1] = data[1]; | |
frame_data[b+4] = data[2]; | |
frame_data[b+5] = data[3]; | |
frame_data[b+8] = data[4]; | |
frame_data[b+9] = data[5]; | |
data = g_mss_mac.mac_filter_data; | |
for( a = 0; a < c; ) { | |
frame_data[d] = data[a++]; | |
frame_data[d+1] = data[a++]; | |
frame_data[d+4] = data[a++]; | |
frame_data[d+5] = data[a++]; | |
frame_data[d+8] = data[a++]; | |
frame_data[d+9] = data[a++]; | |
d += 12; | |
} | |
/* Stop transmission */ | |
ret = MAC_stop_transmission(); | |
configASSERT( ret == MAC_OK ); | |
ret = MAC_stop_receiving(); | |
configASSERT( ret == MAC_OK ); | |
/* Set descriptor */ | |
MAC->CSR4 = (uint32_t)&descriptor; | |
/* Start transmission */ | |
MAC_start_transmission(); | |
/* Wait until transmission over */ | |
ret = MAC_OK; | |
MAC_set_time_out( (uint32_t)SETUP_FRAME_TIME_OUT ); | |
while( (((MAC->CSR5 & CSR5_TS_MASK) >> CSR5_TS_SHIFT) != | |
CSR5_TS_SUSPENDED) && (MAC_OK == ret) ) | |
{ | |
/* transmit poll demand */ | |
MAC->CSR1 = 1u; | |
if( MAC_get_time_out() == 0u ) { | |
ret = MAC_TIME_OUT; | |
} | |
} | |
MAC_CHECK( MAC_stop_transmission() == MAC_OK, MAC_FAIL ); | |
/* Set tx descriptor */ | |
MAC->CSR4 = (uint32_t)g_mss_mac.tx_descriptors; | |
/* Start receiving and transmission */ | |
MAC_start_receiving(); | |
MAC_start_transmission(); | |
return ret; | |
} | |
/***************************************************************************//** | |
* Stops transmission. | |
* Function will wait until transmit operation enters stop state. | |
* | |
* @return #MAC_OK if everything is ok. | |
* #MAC_TIME_OUT if timed out. | |
*/ | |
static int32_t | |
MAC_stop_transmission | |
( | |
void | |
) | |
{ | |
int32_t retval = MAC_OK; | |
MAC_set_time_out( (uint16_t)STATE_CHANGE_TIME_OUT ); | |
while( (((MAC->CSR5 & CSR5_TS_MASK) >> CSR5_TS_SHIFT) != | |
CSR5_TS_STOPPED) && (retval == MAC_OK) ) | |
{ | |
MAC_BITBAND->CSR6_ST = 0u; | |
if( MAC_get_time_out() == 0u ) { | |
retval = MAC_TIME_OUT; | |
} | |
} | |
return retval; | |
} | |
/***************************************************************************//** | |
* Starts transmission. | |
*/ | |
static void | |
MAC_start_transmission | |
( | |
void | |
) | |
{ | |
MAC_BITBAND->CSR6_ST = 1u; | |
} | |
/***************************************************************************//** | |
* Stops transmission. | |
* Function will wait until transmit operation enters stop state. | |
* | |
* @return #MAC_OK if everything is ok. | |
* #MAC_TIME_OUT if timed out. | |
*/ | |
static int32_t | |
MAC_stop_receiving | |
( | |
void | |
) | |
{ | |
int32_t retval = MAC_OK; | |
MAC_set_time_out( (uint16_t)STATE_CHANGE_TIME_OUT ); | |
while( (((MAC->CSR5 & CSR5_RS_MASK) >> CSR5_RS_SHIFT) != CSR5_RS_STOPPED) | |
&& (retval == MAC_OK) ) | |
{ | |
MAC_BITBAND->CSR6_SR = 0u; | |
if( MAC_get_time_out() == 0u ) { | |
retval = MAC_TIME_OUT; | |
} | |
} | |
return retval; | |
} | |
/***************************************************************************//** | |
* Starts transmission. | |
*/ | |
static void | |
MAC_start_receiving | |
( | |
void | |
) | |
{ | |
MAC_BITBAND->CSR6_SR = 1u; | |
} | |
/***************************************************************************//** | |
* Dismisses bad frames. | |
* | |
* @return dismissed frame count. | |
*/ | |
static int32_t | |
MAC_dismiss_bad_frames | |
( | |
void | |
) | |
{ | |
int32_t dc = 0; | |
int8_t cont = 1; | |
if( MAC_BITBAND->CSR6_PB != 0u ) { | |
/* User wants bad frames too, don't dismiss anything */ | |
cont = 0; | |
} | |
while( ( (g_mss_mac.rx_descriptors[ g_mss_mac.rx_desc_index ].descriptor_0 & | |
RDES0_OWN) == 0u) && (cont == 1) ) /* Host owns this descriptor */ | |
{ | |
/* check error summary */ | |
if( (g_mss_mac.rx_descriptors[ g_mss_mac.rx_desc_index ].descriptor_0 & | |
(RDES0_ES | RDES0_LS | RDES0_FS)) != (RDES0_LS | RDES0_FS) ) | |
{ | |
MSS_MAC_prepare_rx_descriptor(); | |
dc++; | |
} | |
else | |
{ | |
cont = 0; | |
} | |
} | |
return dc; | |
} | |
/***************************************************************************//** | |
* Sets time out value. | |
* #MAC_get_time_out must be called frequently to make time out value updated. | |
* Because of user may not be using ISR, we can not update time out in ISR. | |
* | |
* @time_out time out value in milli seconds. | |
* Must be smaller than 0x01000000. | |
*/ | |
static void | |
MAC_set_time_out | |
( | |
uint32_t time_out | |
) | |
{ | |
g_mss_mac.time_out_value = (time_out * 122u) / 10u; | |
g_mss_mac.last_timer_value = (uint16_t)( MAC->CSR11 & CSR11_TIM_MASK ); | |
} | |
/***************************************************************************//** | |
* Returns time out value. | |
* | |
* @return timer out value in milli seconds. | |
*/ | |
static uint32_t | |
MAC_get_time_out | |
( | |
void | |
) | |
{ | |
uint32_t timer; | |
uint32_t time = 0u; | |
timer = ( MAC->CSR11 & CSR11_TIM_MASK ); | |
if( timer > g_mss_mac.last_timer_value ) { | |
time = 0x0000ffffUL; | |
} | |
time += g_mss_mac.last_timer_value - timer; | |
if( MAC_BITBAND->CSR6_TTM == 0u ) { | |
time *= 10u; | |
} | |
if( g_mss_mac.time_out_value <= time ){ | |
g_mss_mac.time_out_value = 0u; | |
} else { | |
g_mss_mac.time_out_value -= time; | |
} | |
g_mss_mac.last_timer_value = (uint16_t)timer; | |
return ((g_mss_mac.time_out_value * 10u) / 122u); | |
} | |
/***************************************************************************//** | |
* Fills the first n bytes of the memory area pointed to by s with the constant | |
* byte c. | |
*/ | |
static void MAC_memset(uint8_t *s, uint8_t c, uint32_t n) | |
{ | |
uint8_t *sb = s; | |
while( n > 0u ) { | |
n--; | |
sb[n] = c; | |
} | |
} | |
/***************************************************************************//** | |
* Fills all fields of MAC_instance_t with c. | |
* | |
* @return a pointer to the given MAC_instance_t s. | |
*/ | |
static void MAC_memset_All(MAC_instance_t *s, uint32_t c) | |
{ | |
int32_t count; | |
s->base_address = (addr_t)c; | |
s->flags = (uint8_t)c; | |
s->last_error = (int8_t)c; | |
s->last_timer_value = (uint16_t)c; | |
s->listener = NULL_callback; | |
MAC_memset( s->mac_address, (uint8_t)c, 6u ); | |
MAC_memset( s->mac_filter_data, (uint8_t)c, 90u ); | |
s->phy_address = (uint8_t)c; | |
s->rx_desc_index =c; | |
for(count = 0; count<RX_RING_SIZE ;count++) | |
{ | |
s->rx_descriptors[count].buffer_1 = c; | |
s->rx_descriptors[count].buffer_2 = c; | |
s->rx_descriptors[count].descriptor_0 = c; | |
s->rx_descriptors[count].descriptor_1 = c; | |
} | |
s->statistics.rx_collision_seen =c; | |
s->statistics.rx_crc_error = c; | |
s->statistics.rx_descriptor_error = c; | |
s->statistics.rx_fifo_overflow = c; | |
s->statistics.rx_filtering_fail = c; | |
s->statistics.rx_frame_too_long = c; | |
s->statistics.rx_interrupts = c; | |
s->statistics.rx_missed_frame = c; | |
s->statistics.rx_not_first = c; | |
s->statistics.rx_not_last = c; | |
s->statistics.rx_runt_frame = c; | |
s->statistics.tx_collision_count = c; | |
s->statistics.tx_excessive_collision = c; | |
s->statistics.tx_interrupts = c; | |
s->statistics.tx_late_collision = c; | |
s->statistics.tx_loss_of_carrier = c; | |
s->statistics.tx_no_carrier = c; | |
s->statistics.tx_underflow_error = c; | |
s->time_out_value = c; | |
s->tx_desc_index = c; | |
for(count = 0; count < TX_RING_SIZE ;count++) | |
{ | |
s->tx_descriptors[count].buffer_1 = c; | |
s->tx_descriptors[count].buffer_2 = c; | |
s->tx_descriptors[count].descriptor_0 = c; | |
s->tx_descriptors[count].descriptor_1 = c; | |
} | |
} | |
/***************************************************************************//** | |
* Copies n bytes from memory area src to memory area dest. | |
* The memory areas should not overlap. | |
* | |
* @return a pointer to the memory area dest. | |
*/ | |
static void MAC_memcpy(uint8_t *dest, const uint8_t *src, uint32_t n) | |
{ | |
uint8_t *d = dest; | |
while( n > 0u ) { | |
n--; | |
d[n] = src[n]; | |
} | |
} | |
/***************************************************************************//** | |
* Tx has completed, mark the buffers that were assigned to the Tx descriptors | |
* as free again. | |
* | |
*/ | |
void MSS_MAC_FreeTxBuffers( void ) | |
{ | |
/* Check the buffers have not already been freed in the first of the | |
two Tx interrupts - which could potentially happen if the second Tx completed | |
during the interrupt for the first Tx. */ | |
if( g_mss_mac.tx_descriptors[ 0 ].buffer_1 != ( uint32_t ) NULL ) | |
{ | |
if( ( ( (g_mss_mac.tx_descriptors[ 0 ].descriptor_0) & TDES0_OWN) == 0 ) && ( ( (g_mss_mac.tx_descriptors[ 1 ].descriptor_0) & TDES0_OWN) == 0 ) ) | |
{ | |
configASSERT( g_mss_mac.tx_descriptors[ 0 ].buffer_1 == g_mss_mac.tx_descriptors[ 1 ].buffer_1 ); | |
MAC_release_buffer( ( unsigned char * ) g_mss_mac.tx_descriptors[ 0 ].buffer_1 ); | |
/* Just to mark the fact that the buffer has already been released. */ | |
g_mss_mac.tx_descriptors[ 0 ].buffer_1 = ( uint32_t ) NULL; | |
} | |
} | |
} | |
/***************************************************************************//** | |
* Look through the array of buffers until one is found that is free for use - | |
* that is, not currently assigned to an Rx or a Tx descriptor. Mark the buffer | |
* as in use, then return its address. | |
* | |
* @return a pointer to a free buffer. | |
*/ | |
unsigned char *MAC_obtain_buffer( void ) | |
{ | |
long lIndex, lAttempt = 0, lDescriptor, lBufferIsInUse; | |
unsigned char *pcReturn = NULL; | |
unsigned char *pcBufferAddress; | |
/* Find and return the address of a buffer that is not being used. Mark | |
the buffer as now in use. */ | |
while( ( lAttempt <= 1 ) && ( pcReturn == NULL ) ) | |
{ | |
for( lIndex = 0; lIndex < macNUM_BUFFERS; lIndex++ ) | |
{ | |
if( ucMACBufferInUse[ lIndex ] == pdFALSE ) | |
{ | |
pcReturn = &( xMACBuffers.ucBuffer[ lIndex ][ 0 ] ); | |
ucMACBufferInUse[ lIndex ] = pdTRUE; | |
break; | |
} | |
} | |
if( pcReturn == NULL ) | |
{ | |
/* Did not find a buffer. That should not really happen, but could if | |
an interrupt was missed. See if any buffers are marked as in use, but | |
are not actually in use. */ | |
for( lIndex = 0; lIndex < macNUM_BUFFERS; lIndex++ ) | |
{ | |
pcBufferAddress = &( xMACBuffers.ucBuffer[ lIndex ][ 0 ] ); | |
lBufferIsInUse = pdFALSE; | |
/* Is the buffer used by an Rx descriptor? */ | |
for( lDescriptor = 0; lDescriptor < RX_RING_SIZE; lDescriptor++ ) | |
{ | |
if( g_mss_mac.rx_descriptors[ lDescriptor ].buffer_1 == ( uint32_t ) pcBufferAddress ) | |
{ | |
/* The buffer is in use by an Rx descriptor. */ | |
lBufferIsInUse = pdTRUE; | |
break; | |
} | |
} | |
if( lBufferIsInUse != pdTRUE ) | |
{ | |
/* Is the buffer used by an Tx descriptor? */ | |
for( lDescriptor = 0; lDescriptor < TX_RING_SIZE; lDescriptor++ ) | |
{ | |
if( g_mss_mac.tx_descriptors[ lDescriptor ].buffer_1 == ( uint32_t ) pcBufferAddress ) | |
{ | |
/* The buffer is in use by an Tx descriptor. */ | |
lBufferIsInUse = pdTRUE; | |
break; | |
} | |
} | |
} | |
/* If the buffer was not found to be in use by either a Tx or an | |
Rx descriptor, but the buffer is marked as in use, then mark the | |
buffer to be in it's correct state of "not in use". */ | |
if( ( lBufferIsInUse == pdFALSE ) && ( ucMACBufferInUse[ lIndex ] == pdTRUE ) ) | |
{ | |
ucMACBufferInUse[ lIndex ] = pdFALSE; | |
} | |
} | |
} | |
/* If any buffer states were changed it might be that a buffer can now | |
be obtained. Try again, but only one more time. */ | |
lAttempt++; | |
} | |
configASSERT( pcReturn ); | |
return pcReturn; | |
} | |
/***************************************************************************//** | |
* Return a buffer to the list of free buffers, it was in use, but is not now. | |
* | |
*/ | |
void MAC_release_buffer( unsigned char *pucBufferToRelease ) | |
{ | |
long lIndex; | |
/* uip_buf is going to point to a different buffer - first ensure the buffer | |
it is currently pointing to is marked as being free again. */ | |
for( lIndex = 0; lIndex < macNUM_BUFFERS; lIndex++ ) | |
{ | |
if( pucBufferToRelease == &( xMACBuffers.ucBuffer[ lIndex ][ 0 ] ) ) | |
{ | |
/* This is the buffer in use, mark it as being free. */ | |
ucMACBufferInUse[ lIndex ] = pdFALSE; | |
break; | |
} | |
} | |
configASSERT( lIndex < macNUM_BUFFERS ); | |
} | |
#ifdef __cplusplus | |
} | |
#endif | |
/******************************** END OF FILE *********************************/ | |