/*
 * @brief Ethernet MAC1 interface
 *
 */

#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include "netif/ethernet.h"
#include "lwip/def.h"
#include "lwip/sys.h"
#include "lwip/pbuf.h"
#include "lwip/prot/ethernet.h"
#include "enet_ethernetif.h"

#include "enet_mac_if1.h"

#include "ptpd_dep.h"

#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET
#include "fsl_memory.h"
#endif
// #include "pin_mux.h"
// #include "clock_config.h"
#include "fsl_gpio.h"
#include "fsl_iomuxc.h"

#include "fsl_debug_console.h"

#include "fsl_enet.h"

#if USE_RTOS && defined(FSL_RTOS_FREE_RTOS)
#include "FreeRTOS.h"
#include "event_groups.h"
#endif

#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL
    #if defined(FSL_FEATURE_L2CACHE_LINESIZE_BYTE) \
        && ((!defined(FSL_SDK_DISBLE_L2CACHE_PRESENT)) || (FSL_SDK_DISBLE_L2CACHE_PRESENT == 0))
        #if defined(FSL_FEATURE_L1DCACHE_LINESIZE_BYTE)
            #define FSL_CACHE_LINESIZE_MAX MAX(FSL_FEATURE_L1DCACHE_LINESIZE_BYTE, FSL_FEATURE_L2CACHE_LINESIZE_BYTE)
            #define FSL_ENET_BUFF_ALIGNMENT MAX(ENET_BUFF_ALIGNMENT, FSL_CACHE_LINESIZE_MAX)
        #else
            #define FSL_ENET_BUFF_ALIGNMENT MAX(ENET_BUFF_ALIGNMENT, FSL_FEATURE_L2CACHE_LINESIZE_BYTE)
        #endif
    #elif defined(FSL_FEATURE_L1DCACHE_LINESIZE_BYTE)
        #define FSL_ENET_BUFF_ALIGNMENT MAX(ENET_BUFF_ALIGNMENT, FSL_FEATURE_L1DCACHE_LINESIZE_BYTE)
    #else
        #define FSL_ENET_BUFF_ALIGNMENT ENET_BUFF_ALIGNMENT
    #endif
#else
    #define FSL_ENET_BUFF_ALIGNMENT ENET_BUFF_ALIGNMENT
#endif

#if defined(FSL_FEATURE_SOC_LPC_ENET_COUNT) && (FSL_FEATURE_SOC_LPC_ENET_COUNT > 0)
#define kENET_RxEvent kENET_RxIntEvent
#define kENET_TxEvent kENET_TxIntEvent
#endif

typedef uint8_t rx_buffer_t[SDK_SIZEALIGN(ENET_RXBUFF_SIZE, FSL_ENET_BUFF_ALIGNMENT)];
typedef uint8_t tx_buffer_t[SDK_SIZEALIGN(ENET_TXBUFF_SIZE, FSL_ENET_BUFF_ALIGNMENT)];



struct ethernetif
{
    ENET_Type *base;
#if (defined(FSL_FEATURE_SOC_ENET_COUNT) && (FSL_FEATURE_SOC_ENET_COUNT > 0)) || \
    (USE_RTOS && defined(FSL_RTOS_FREE_RTOS))
    enet_handle_t handle;
#endif
#if USE_RTOS && defined(FSL_RTOS_FREE_RTOS)
    EventGroupHandle_t enetTransmitAccessEvent;
    EventBits_t txFlag;
#endif
    enet_rx_bd_struct_t *RxBuffDescrip;
    enet_tx_bd_struct_t *TxBuffDescrip;
    rx_buffer_t *RxDataBuff;
    tx_buffer_t *TxDataBuff;


#if defined(FSL_FEATURE_SOC_LPC_ENET_COUNT) && (FSL_FEATURE_SOC_LPC_ENET_COUNT > 0)
    uint8_t txIdx;
#if !(USE_RTOS && defined(FSL_RTOS_FREE_RTOS))
    uint8_t rxIdx;
#endif
#endif
};

#if defined(FSL_FEATURE_SOC_LPC_ENET_COUNT) && (FSL_FEATURE_SOC_LPC_ENET_COUNT > 0)
#define kENET_RxEvent kENET_RxIntEvent
#define kENET_TxEvent kENET_TxIntEvent
#endif

const uint8_t configMAC1_ADDR[] = {configMAC1_ADDR0, configMAC1_ADDR1, configMAC1_ADDR2, configMAC1_ADDR3, configMAC1_ADDR4, configMAC1_ADDR5};

/*
typedef uint8_t rx_buffer_t[SDK_SIZEALIGN(ENET_RXBUFF_SIZE, FSL_ENET_BUFF_ALIGNMENT)];
typedef uint8_t tx_buffer_t[SDK_SIZEALIGN(ENET_TXBUFF_SIZE, FSL_ENET_BUFF_ALIGNMENT)];
*/

static struct ethernet_savif * ethernet_sav_interface  = NULL;

#if PTP_ENABLED

/*******************************************************************************
 * PTP-related interfaces to support ptpd protocal
 ******************************************************************************/
static struct ethernetif * ptp_ethernetif  = NULL;


// FIXME: NickP: should these two addresses be configurable? Define the interface!
static uint8_t ptpDefaultHwAddress[] = {0x01, 0x1b, 0x19, 0x00, 0x00, 0x00};
static uint8_t ptpPeerHwAddress[] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e};

/* Buffers for store receive and transmit timestamp. */
//static sys_mutex_t ptp_mutex;
enet_ptp_time_data_t g_rxPtpTsBuff[ENET_RXBD_NUM];
enet_ptp_time_data_t g_txPtpTsBuff[ENET_TXBD_NUM];

// extern err_t netGetPtpMailboxHandle(sys_mbox_t*);


void set_sav_interface(struct ethernet_savif *sav_intf)
{
	ethernet_sav_interface = sav_intf;
}



int lwIP_HandleUnknownEthernetProtocol(struct pbuf *p)
{

    if (p == NULL)
    {
    	return ERR_ARG;
    }

    struct eth_hdr* ethhdr = (struct eth_hdr *)p->payload;

    if (ethhdr->type == PP_HTONS(ETHTYPE_PTP))
    {
        sys_mbox_t handle;

        // check destination mac addr is ptp type? Should be:-
        // 01-1B-19-00-00-00 or
        // 01-80-C2-00-00-0E

        // put ptr to the pbuf that contains the message onto the ptp application incoming msg queue.
        if (netGetPtpMailboxHandle(&handle) == ERR_OK)
        {
            return sys_mbox_trypost (&handle, p);
        }
    }

    else if (ethhdr->type == PP_HTONS(ETHTYPE_IEC61850_SMV))
    {
    	ethernet_sav_interface->process_rx_buffer(p);
    }

    return ERR_VAL;
}



static void ethernet_ptptime_enablepps(struct ethernetif *ethernetif, enet_ptp_timer_channel_t tmr_ch)
{
    uint32_t next_counter = 0;
    uint32_t tmp_val = 0;

 //   sys_mutex_lock(&ptp_mutex);

    /* clear capture or output compare interrupt status if have.
     */
    ENET_Ptp1588ClearChannelStatus(ethernetif->base, tmr_ch);

    /* It is recommended to double check the TMODE field in the
     * TCSR register to be cleared before the first compare counter
     * is written into TCCR register. Just add a double check.
     */
    tmp_val = ethernetif->base->CHANNEL[tmr_ch].TCSR;
    do {
        tmp_val &= ~(ENET_TCSR_TMODE_MASK);
        ethernetif->base->CHANNEL[tmr_ch].TCSR = tmp_val;
        tmp_val = ethernetif->base->CHANNEL[tmr_ch].TCSR;
    } while (tmp_val & ENET_TCSR_TMODE_MASK);

    tmp_val = (ENET_NANOSECOND_ONE_SECOND >> 1);

    ENET_Ptp1588SetChannelCmpValue(ethernetif->base, tmr_ch, tmp_val);

    /* Calculate the second the compare event timestamp */
    next_counter = tmp_val;

    /* Compare channel setting. */
    ENET_Ptp1588ClearChannelStatus(ethernetif->base, tmr_ch);

    ENET_Ptp1588SetChannelOutputPulseWidth(ethernetif->base, tmr_ch, false, 4, true);

    /* Write the second compare event timestamp and calculate
     * the third timestamp. Refer the TCCR register detail in the spec.
     */
    ENET_Ptp1588SetChannelCmpValue(ethernetif->base, tmr_ch, next_counter);
    /* Update next counter */
    //next_counter = (next_counter + ETH_PTP_NSEC_PER_SEC) & ENET_TCCR_TCC_MASK;
    ethernetif->handle.ptpNextCounter = next_counter;

//    sys_mutex_unlock(&ptp_mutex);
}

static void ethernet_ptptime_init(struct ethernetif *ethernetif, uint32_t ptp_clk_freq,
                                   bool pps_en, enet_ptp_timer_channel_t tmr_ch)
{
    enet_ptp_config_t ptp_cfg;

    assert(ethernetif);
    ptp_ethernetif = ethernetif;

    /* Config 1588 */
    memset(&ptp_cfg, 0, sizeof(enet_ptp_config_t));
    ptp_cfg.channel = tmr_ch;
    ptp_cfg.ptpTsRxBuffNum = ENET_RXBD_NUM;
    ptp_cfg.ptpTsTxBuffNum = ENET_TXBD_NUM;
    ptp_cfg.rxPtpTsData = &g_rxPtpTsBuff[0];
    ptp_cfg.txPtpTsData = &g_txPtpTsBuff[0];
    ptp_cfg.ptp1588ClockSrc_Hz = ptp_clk_freq;
    ENET_Ptp1588Configure(ptp_ethernetif->base, &ptp_ethernetif->handle, &ptp_cfg);

    if (true == pps_en)
    {
        ethernet_ptptime_enablepps(ptp_ethernetif, tmr_ch);
    }
    else
    {
        ENET_Ptp1588SetChannelMode(ptp_ethernetif->base, tmr_ch, kENET_PtpChannelDisable, false);
    }
}



/*******************************************************************************
* Function Name  : ethernet_ptptime_adjfreq
* Description    : Updates timer correction Register and correction increment value
* Input          : Correction value in thousandth of ppm (incps*10^9)
* Output         : None
* Return         : None
*******************************************************************************/
void ethernet_ptptime_adjfreq(int32_t incps)
{
    int32_t neg_adj = 0;
    uint32_t corr_inc, corr_period;

    assert(ptp_ethernetif);

    /*
     * incps means the increment rate (nanseconds per second)by which to
     * slow down or speed up the slave timer.
     * Positive ppb need to speed up and negative value need to slow down.
    */

    if (0 == incps)
    {
        ptp_ethernetif->base->ATCOR &= ~ENET_ATCOR_COR_MASK; /* Reset PTP frequency */
        return;
    }

    if (incps < 0)
    {
        incps = -incps;
        neg_adj = 1;
    }

    corr_period = (uint32_t)PTP_CLOCK_FRE_RT/incps;

    /* neg_adj = 1, slow down timer, neg_adj = 0, speed up timer */
    corr_inc = (neg_adj) ? (PTP_AT_INC - 1) : (PTP_AT_INC + 1);

    ENET_Ptp1588AdjustTimer(ptp_ethernetif->base, corr_inc, corr_period);

}

void ethernet_ptptime_adjtime(int32_t second, int32_t nano_second)
{
    enet_ptp_time_t tmp_ts;
    enet_ptp_time_t current_ts;
    int32_t tmp_adj_sec = 0, tmp_adj_nsec = 0;
    int32_t carry_flag = 0;

    assert(nano_second < ENET_NANOSECOND_ONE_SECOND);
    assert(nano_second > -ENET_NANOSECOND_ONE_SECOND);

    ethernet_ptptime_gettime(&current_ts);
    tmp_adj_nsec = current_ts.nanosecond - nano_second;
	tmp_adj_sec = current_ts.second - second;

    if (tmp_adj_nsec > ENET_NANOSECOND_ONE_SECOND)
    {
        tmp_adj_sec += 1;
        tmp_adj_nsec -= ENET_NANOSECOND_ONE_SECOND;
    }

	if (tmp_adj_sec > 0  && tmp_adj_nsec < 0)
    {
        carry_flag = - 1;
        tmp_adj_nsec += ENET_NANOSECOND_ONE_SECOND;
    } else if (tmp_adj_sec < 0  && tmp_adj_nsec > 0)
    {
        carry_flag = 1;
		tmp_adj_nsec -= ENET_NANOSECOND_ONE_SECOND;
    }

    tmp_adj_sec = current_ts.second + carry_flag;
    assert(tmp_adj_sec >= 0);
    assert(tmp_adj_nsec >= 0);

    tmp_ts.second = tmp_adj_sec;
    tmp_ts.nanosecond = tmp_adj_nsec;
    ethernet_ptptime_settime(&tmp_ts);
}

/*******************************************************************************
* Function Name  : ethernet_ptptime_settime
* Description    : Initialize time base
* Input          : Time with sign
* Output         : None
* Return         : None
*******************************************************************************/
void ethernet_ptptime_settime(enet_ptp_time_t *timestamp)
{
    assert(ptp_ethernetif);

    ENET_Ptp1588SetTimer(ptp_ethernetif->base, &(ptp_ethernetif->handle), timestamp);

//    ethernet_ptptime_enablepps(ptp_ethernetif, ETH_PTP_DEFAULT_PPS_CHANNEL);
}

void ethernet_ptptime_gettime(enet_ptp_time_t *timestamp)
{
    assert(ptp_ethernetif);

    ENET_Ptp1588GetTimer(ptp_ethernetif->base, &ptp_ethernetif->handle, timestamp);
}

err_t enet_get_rxframe_time(enet_ptp_time_data_t *ptpTimeData)
{
	if(ENET_GetRxFrameTime(&ptp_ethernetif->handle, ptpTimeData) == kStatus_Success)
	{
		return ERR_OK;
	}

	return ERR_ARG;
}
err_t enet_get_txframe_time(enet_ptp_time_data_t *ptpTimeData)
{
	if(ENET_GetTxFrameTime(&ptp_ethernetif->handle, ptpTimeData) == kStatus_Success)
	{
		return ERR_OK;
	}

	return ERR_ARG;
}

#endif /* PTP_ENABLED */

const uint8_t* netGetHwAddr(void)
{
	return(configMAC1_ADDR);
}



#if USE_RTOS && defined(FSL_RTOS_FREE_RTOS)
#if defined(FSL_FEATURE_SOC_ENET_COUNT) && (FSL_FEATURE_SOC_ENET_COUNT > 0)
static void ethernet_callback(ENET_Type *base, enet_handle_t *handle, enet_event_t event, void *param)
#endif /* FSL_FEATURE_SOC_*_ENET_COUNT */
{
//    struct netif *netif = (struct netif *)param;
//    struct ethernetif *ethernetif = ptp_ethernetif;

    switch (event)
    {
        case kENET_RxEvent:
        	enet_mac1_input();
            break;
        case kENET_TxEvent:
/*
        	{
        		portBASE_TYPE taskToWake = pdFALSE;

        		if (__get_IPSR())
        		{
        			xEventGroupSetBitsFromISR(ethernetif->enetTransmitAccessEvent, ethernetif->txFlag, &taskToWake);
        			if (pdTRUE == taskToWake)
        			{
        				portYIELD_FROM_ISR(taskToWake);
        			}
        		}
        		else
        		{
        			xEventGroupSetBits(ethernetif->enetTransmitAccessEvent, ethernetif->txFlag);
        		}
        	}
*/
        	break;

        default:
        	break;
    }
}
#endif




/**
 * Initializes ENET driver.
 */
uint8_t sav_mac_addr[6];
static void enet1_init(struct ethernetif *ethernetif)
{
    enet_config_t config;
    uint32_t sysClock;

    enet_buffer_config_t buffCfg;

//    uint8_t sav_mac_addr[6];

    /* prepare the buffer configuration. */
    buffCfg.rxBdNumber = ENET_RXBD_NUM;                      /* Receive buffer descriptor number. */
    buffCfg.txBdNumber = ENET_TXBD_NUM;                      /* Transmit buffer descriptor number. */
    buffCfg.rxBuffSizeAlign = sizeof(rx_buffer_t);           /* Aligned receive data buffer size. */
    buffCfg.txBuffSizeAlign = sizeof(tx_buffer_t);           /* Aligned transmit data buffer size. */
    buffCfg.rxBdStartAddrAlign = &(ethernetif->RxBuffDescrip[0]); /* Aligned receive buffer descriptor start address. */
    buffCfg.txBdStartAddrAlign = &(ethernetif->TxBuffDescrip[0]); /* Aligned transmit buffer descriptor start address. */
    buffCfg.rxBufferAlign = &(ethernetif->RxDataBuff[0][0]); /* Receive data buffer start address. */
    buffCfg.txBufferAlign = &(ethernetif->TxDataBuff[0][0]); /* Transmit data buffer start address. */

//    sysClock = CLOCK_GetFreq(ethernetifConfig->clockName);
    sysClock = CLOCK_GetFreq(kCLOCK_AhbClk);

    ENET_GetDefaultConfig(&config);

    config.txAccelerConfig |= kENET_TxAccelIpCheckEnabled; // jem - Configure MAC to generate the IP header checksum in Tx frames
    config.miiSpeed = kENET_MiiSpeed100M;
    config.miiDuplex = kENET_MiiFullDuplex;

#if USE_RTOS && defined(FSL_RTOS_FREE_RTOS)
    uint32_t instance;
    static ENET_Type *const enetBases[] = ENET_BASE_PTRS;
    static const IRQn_Type enetTxIrqId[] = ENET_Transmit_IRQS;
    /*! @brief Pointers to enet receive IRQ number for each instance. */
    static const IRQn_Type enetRxIrqId[] = ENET_Receive_IRQS;
// #if defined(ENET_ENHANCEDBUFFERDESCRIPTOR_MODE) && ENET_ENHANCEDBUFFERDESCRIPTOR_MODE
#if defined(ENET_ENHANCEDBUFFERDESCRIPTOR_MODE)
    /*! @brief Pointers to enet timestamp IRQ number for each instance. */
    static const IRQn_Type enetTsIrqId[] = ENET_1588_Timer_IRQS;
#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */

    /* Create the Event for transmit busy release trigger. */
    ethernetif->enetTransmitAccessEvent = xEventGroupCreate();
    ethernetif->txFlag = 0x1;

    config.interrupt |= kENET_RxFrameInterrupt | kENET_TxFrameInterrupt | kENET_TxBufferInterrupt;

    for (instance = 0; instance < ARRAY_SIZE(enetBases); instance++)
    {
        if (enetBases[instance] == ethernetif->base)
        {

            NVIC_SetPriority(enetRxIrqId[instance], ENET_PRIORITY);
            NVIC_SetPriority(enetTxIrqId[instance], ENET_PRIORITY);
// #if defined(ENET_ENHANCEDBUFFERDESCRIPTOR_MODE) && ENET_ENHANCEDBUFFERDESCRIPTOR_MODE
#if defined(ENET_ENHANCEDBUFFERDESCRIPTOR_MODE)
            NVIC_SetPriority(enetTsIrqId[instance], ENET_1588_PRIORITY);
#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */
            break;
        }
    }

    LWIP_ASSERT("Input Ethernet base error!", (instance != ARRAY_SIZE(enetBases)));
#endif /* USE_RTOS */

    /* Initialize the ENET module.*/
    ENET_Init(ethernetif->base, &ethernetif->handle, &config, &buffCfg, configMAC1_ADDR, sysClock);

#if PTP_ENABLED
    ENET_AddMulticastGroup(ethernetif->base, ptpDefaultHwAddress);  // LX: Enable general MAC address
    ENET_AddMulticastGroup(ethernetif->base, ptpPeerHwAddress);     // LX: Enable peer MAC address
#endif

    if(ethernet_sav_interface->sav_rx_enabled(sav_mac_addr))
    {
    	ENET_AddMulticastGroup(ethernetif->base, sav_mac_addr); // LX: Added for receiving process MV data
    }

#if USE_RTOS && defined(FSL_RTOS_FREE_RTOS)
    ENET_SetCallback(&ethernetif->handle, ethernet_callback, NULL);
#endif

#if PTP_ENABLED
    /* It's time to initialize the IE1588 function of ethernet */
    ethernet_ptptime_init(ethernetif, PTP_CLOCK_FRE_RT, PTP_TEST_APP_ENABLE, PTP_TEST_APP_CHANNEL);
#endif

    ENET_ActiveRead(ethernetif->base);
}

/*
struct ethernetif *enet_get_ethenetif(void)
{
	struct ethernetif *ethernetif = ptp_ethernetif;

	return ethernetif;
}
*/


struct pbuf *mac1_linkinput(void)
{
    struct ethernetif *ethernetif = ptp_ethernetif;
    struct pbuf *p = NULL;
    uint32_t len;
    status_t status;

    /* Obtain the size of the packet and put it into the "len"
       variable. */
    status = ENET_GetRxFrameSize(&ethernetif->handle, &len);

    if (kStatus_ENET_RxFrameEmpty != status)
    {
        /* Call ENET_ReadFrame when there is a received frame. */
        if (len != 0)
        {
            /* We allocate a pbuf chain of pbufs from the pool. */
            p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);

            if (p != NULL)
            {
#if RX_FRAME_FITS_INTO_PBUF
                LWIP_ASSERT("Frame does not fit into a single pbuf", p->next == 0);
                ENET_ReadFrame(ethernetif->base, &ethernetif->handle, p->payload, p->len);
#else
                if (p->next == 0) /* One-chain buffer.*/
                {
                    ENET_ReadFrame(ethernetif->base, &ethernetif->handle, p->payload, p->len);
                }
                else    /* Multi-chain buffer.*/
                {
                    uint8_t data_tmp[ENET_FRAME_MAX_FRAMELEN];
                    uint32_t data_tmp_len = 0;
                    struct pbuf *q;

                    ENET_ReadFrame(ethernetif->base, &ethernetif->handle, data_tmp, p->tot_len);

                    /* We iterate over the pbuf chain until we have read the entire
                    * packet into the pbuf. */
                    for (q = p; (q != NULL) && ((data_tmp_len + q->len) <= sizeof(data_tmp)); q = q->next)
                    {
                        /* Read enough bytes to fill this pbuf in the chain. The
                        * available data in the pbuf is given by the q->len
                        * variable. */
                        memcpy(q->payload,  &data_tmp[data_tmp_len], q->len);
                        data_tmp_len += q->len;
                    }
                }
#endif /* RX_FRAME_FITS_INTO_PBUF */

            }
            else
            {
                /* drop packet*/
                ENET_ReadFrame(ethernetif->base, &ethernetif->handle, NULL, 0U);
            }
        }
        else
        {
            /* Update the received buffer when error happened. */
            if (status == kStatus_ENET_RxFrameError)
            {
#if 0 && defined(FSL_FEATURE_SOC_ENET_COUNT) && (FSL_FEATURE_SOC_ENET_COUNT > 0) /* Error statisctics */
            	enet_data_error_stats_t eErrStatic;
            	/* Get the error information of the received g_frame. */
            	ENET_GetRxErrBeforeReadFrame(&ethernetif->handle, &eErrStatic);
#endif

                /* Update the receive buffer. */
                ENET_ReadFrame(ethernetif->base, &ethernetif->handle, NULL, 0U);
            }
        }
    }
    return p;
}




/**
 * Sends frame via ENET.
 */
err_t enet1_send_frame(unsigned char *data, const uint16_t length)
{
    struct ethernetif *ethernetif = ptp_ethernetif;

#if USE_RTOS && defined(FSL_RTOS_FREE_RTOS)
    {
        status_t result;

        do
        {
            result = ENET_SendFrame(ethernetif->base, &ethernetif->handle, data, length);

            if (result == kStatus_ENET_TxFrameBusy)
            {
                xEventGroupWaitBits(ethernetif->enetTransmitAccessEvent, ethernetif->txFlag, pdTRUE, (BaseType_t) false,
                                    portMAX_DELAY);
            }

        } while (result == kStatus_ENET_TxFrameBusy);
        return ERR_OK;
    }
#elif defined(FSL_FEATURE_SOC_ENET_COUNT) && (FSL_FEATURE_SOC_ENET_COUNT > 0)
    {
        uint32_t counter;

        for (counter = ENET_TIMEOUT; counter != 0U; counter--)
        {
            if (ENET_SendFrame(ethernetif->base, &ethernetif->handle, data, length) != kStatus_ENET_TxFrameBusy)
            {
                return ERR_OK;
            }
        }

        return ERR_TIMEOUT;
    }
#elif defined(FSL_FEATURE_SOC_LPC_ENET_COUNT) && (FSL_FEATURE_SOC_LPC_ENET_COUNT > 0)
    {
        uint32_t tail;
        enet_tx_bd_struct_t *txBuffDesc = get_tx_desc(ethernetif, ethernetif->txIdx);

        ENET_SetupTxDescriptor(txBuffDesc, data, length, NULL, 0U, length, false, false, kENET_FirstLastFlag, 0U);
        ethernetif->txIdx = (ethernetif->txIdx + 1U) % ENET_TXBD_NUM;

        /* Update the transmit tail address. */
        if (ethernetif->txIdx == 0U)
        {
            tail = (uint32_t)get_tx_desc(ethernetif, ENET_TXBD_NUM);
        }
        else
        {
            tail = (uint32_t)get_tx_desc(ethernetif, ethernetif->txIdx);
        }
        ENET_UpdateTxDescriptorTail(ethernetif->base, 0U, tail);

        return ERR_OK;
    }
#endif
}




/**
 * This function should be called when a packet is ready to be read
 * from the interface. It uses the function ethernetif_linkinput() that
 * should handle the actual reception of bytes from the network
 * interface. Then the type of the received packet is determined and
 * the appropriate input function is called.
 *
 * @param netif the lwip network interface structure for this ethernetif
 */
void enet_mac1_input(void)
{
    struct pbuf *p;

    /* move received packet into a new pbuf */
    while ((p = mac1_linkinput()) != NULL)
    {
    	// Bypass the TCP/IP stack
        if(lwIP_HandleUnknownEthernetProtocol(p) != ERR_OK) {
            LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
            pbuf_free(p);
            p = NULL;
        }
    }
}





/**
 * Should be called at the beginning of the program to set up the
 * second network interface. It calls the function low_level_init() to do the
 * actual setup of the hardware.
 *
 * This function should be passed as a parameter to netif_add().
 *
 * @param netif the lwip network interface structure for this ethernetif
 * @return ERR_OK if the loopif is initialized
 *         ERR_MEM if private data couldn't be allocated
 *         any other err_t on error
 */
void enetif0_init(void)
{
    static struct ethernetif ethernetif_0;
    AT_NONCACHEABLE_SECTION_ALIGN(static enet_rx_bd_struct_t rxBuffDescrip_0[ENET_RXBD_NUM], FSL_ENET_BUFF_ALIGNMENT);
    AT_NONCACHEABLE_SECTION_ALIGN(static enet_tx_bd_struct_t txBuffDescrip_0[ENET_TXBD_NUM], FSL_ENET_BUFF_ALIGNMENT);
    SDK_ALIGN(static rx_buffer_t rxDataBuff_0[ENET_RXBD_NUM], FSL_ENET_BUFF_ALIGNMENT);
    SDK_ALIGN(static tx_buffer_t txDataBuff_0[ENET_TXBD_NUM], FSL_ENET_BUFF_ALIGNMENT);

    ethernetif_0.RxBuffDescrip = &(rxBuffDescrip_0[0]);
    ethernetif_0.TxBuffDescrip = &(txBuffDescrip_0[0]);
    ethernetif_0.RxDataBuff = &(rxDataBuff_0[0]);
    ethernetif_0.TxDataBuff = &(txDataBuff_0[0]);

    ethernetif_0.base = ENET;

    // ENET driver initialization.
    enet1_init(&ethernetif_0);
}





