/***************************************************************************//**
 * @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 *********************************/

