/*!
 *  \file appTask.c
 *
 *  \brief
 *  EtherNet/IP&trade; Adapter Example Application profile common functions.
 *
 *  \author
 *  Texas Instruments Incorporated
 *
 *  \copyright
 *  Copyright (C) 2021 Texas Instruments Incorporated
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *    Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *    Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the
 *    distribution.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#if (!(defined FBTLPROVIDER) || (0 == FBTLPROVIDER)) && (!(defined FBTL_REMOTE) || (0 == FBTL_REMOTE))
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdarg.h>

#include "EI_API.h"
#include "EI_API_def.h"
#include "Version.h"

#include "appUart.h"
#include "drivers/uart/v0/uart.h"
#include "appLed.h"
#include "appNV.h"
#include "appCfg.h"

#include <osal.h>
#include <osal_error.h>
#include <hwal.h>
#include <board.h>

#include <CMN_board.h>
#include <CMN_os.h>
#include <CMN_app.h>
#include <CMN_mem.h>
#include <CMN_CPU_api.h>

#include <drivers/CUST_drivers.h>
#include <drivers/gpio.h>
#include "ti_drivers_config.h"

#include <appWebServer.h>
#include"task.h"
#include "app.h"
#include "appTask.h"
#include "appCfg.h"
#include "appRst.h"
#include "appMutex.h"
#include <device_profiles/app_device_profile.h>

#include "ti_board_open_close.h"
#include "ti_drivers_open_close.h"
#include "ti_drivers_config.h" // For CONFIG_ADC0_BASE_ADDR
#include "drivers/adc/v0/adc.h"

#include "FreeRTOS.h"
#include "semphr.h"
#include "timers.h"

// DAC process semaphore
static SemaphoreHandle_t dacSem = NULL;
extern volatile bool     g_cfgNvWritePending;

// UART RX monitor task handles

// Timer callback for fixed interval (e.g., 80ms)
void vDacTimerCallback(TimerHandle_t xTimer)
{
    xSemaphoreGive(dacSem);
}

#if defined(SOC_AM64X) || defined(SOC_AM243X) || defined (SOC_AM263PX) || defined(SOC_AM261X)
extern PRUICSS_Handle prusshandle;
#else
Board_IDInfo boardInfo;
#endif

// Static variables and pointers used in this example.

static EI_API_ADP_T*           adapter_s  = NULL;
static EI_API_CIP_NODE_T*      cipNode_s  = NULL;

static int                     EI_APP_TASK_cleanup             (EI_API_ADP_T      *pAdapter, EI_API_CIP_NODE_T *pCipNode);
static void                    EI_APP_TASK_stackInit           (APP_SParams_t     *pParam);
static bool                    EI_APP_TASK_init                (APP_SParams_t     *pParam);
static void                    EI_APP_TASK_adpInit             (EI_API_ADP_T      *pAadapter);
static void                    EI_APP_TASK_run                 (EI_API_CIP_NODE_T *pCipNode);
static uint8_t*                EI_APP_TASK_getMacAddr          (void);

static void                    EI_APP_TASK_stackErrorHandlerCb (uint32_t errorCode,   uint8_t fatal, uint8_t numOfPara, va_list argPtr);
static bool                    EI_APP_TASK_cipCreateCallback   (EI_API_CIP_NODE_T *pCipNode);
extern void createDACSpeedTask(void);
// Global variables and pointers used in this example.
// has to stay, used in lib_eip_lwip_ip :-(
static uint8_t EI_APP_TASK_macAddress[] = {0x88, 0x0C, 0xE0, 0x5C, 0x46, 0xE9};
extern IP_ConfigState_t g_ipConfigState;

uint32_t globalError = 0;
I2C_Handle i2cHandle;
I2C_Transaction i2cTransaction;
uint8_t txBuffer[1] = {0};
/*!
 *  <!-- Description: -->
 *
 *  \brief
 *  This function deinitializes / cleans the objects created
 *  before an exit.
 *
 *  \details
 *  This function deinitializes CIP node and ADP objects. It also
 *  deinitializes the IDK board GPIOs before an exit.
 *
 *  <!-- Parameters and return values: -->
 *
 *  \return     int               Exit code.
 *
 *  \retval     0                 Clean up success.
 *  \retval     1                 Clean up failed.
 *
 */
static int EI_APP_TASK_cleanup (EI_API_ADP_T* adapter, EI_API_CIP_NODE_T* cipNode)
{
    int exit_code = 0;

    // Try to delete the CIP node.
    if (EI_API_CIP_NODE_delete(cipNode))
    {
        // Fail.
        exit_code = 1;
    }

    // Try to delete the ADP.
    if (EI_API_ADP_delete(adapter))
    {
        // Fail.
        exit_code = 1;
    }

    return exit_code;
}

/*!
 *  <!-- Description: -->
 *
 *  \brief
 *  Initialize EtherNet/IP&trade; Adapter data.
 *
 *  \details
 *  Enable the device implementer to override default data, such as for example
 *  Vendor ID, Device Type, Product Code, Revision, Product Name, etc.
 *  and read non-volatile configuration data.
 */
static void EI_APP_TASK_adpInit(EI_API_ADP_T* pAdapter)
{
    EI_APP_CFG_apply(pAdapter);
}

/*!
 *  <!-- Description: -->
 *
 *  \brief
 *  Creates several callbacks.
 *
 *  \details
 *  Creates and registers callbacks which are needed for program flow.
 *  - Store non-volatile data callback.
 *  - Set_Attribute_Single service callback.
 *  - Reset service callback.
 *
 */
static bool EI_APP_TASK_cipCreateCallback(EI_API_CIP_NODE_T* cipNode)
{
    bool    res = false;

    // Your callback function which stores your data when triggered.
    EI_API_CIP_CBService ptr_my_config_cb = EI_APP_CFG_callback;

    // Register callbacks for Set_Attribute_Single service.
    EI_API_CIP_SService_t srvc = { EI_API_CIP_eSC_SETATTRSINGLE, 0, NULL, ptr_my_config_cb };

    if (EI_API_CIP_eERR_OK != EI_API_CIP_setInstanceServiceFunc(cipNode, 0x00F5, 0x0001, &srvc))
    {
        goto laError;
    }

    if (EI_API_CIP_eERR_OK != EI_API_CIP_setInstanceServiceFunc(cipNode, 0x00F6, 0x0001, &srvc))
    {
        goto laError;
    }

    if (EI_API_CIP_eERR_OK != EI_API_CIP_setInstanceServiceFunc(cipNode, 0x00F6, 0x0002, &srvc))
    {
        goto laError;
    }

    if (EI_API_CIP_eERR_OK != EI_API_CIP_setInstanceServiceFunc(cipNode, 0x0043, 0x0001, &srvc))
    {
        goto laError;
    }

    if (EI_API_CIP_eERR_OK != EI_API_CIP_setInstanceServiceFunc(cipNode, 0x0048, 0x0001, &srvc))
    {
        goto laError;
    }

    if (EI_API_CIP_eERR_OK != EI_API_CIP_setInstanceServiceFunc(cipNode, 0x0109, 0x0001, &srvc))
    {
        goto laError;
    }

    // Add callback for Reset service of class ID 0x01.
    EI_API_CIP_SService_t srvcReset = { EI_API_CIP_eSC_RESET, 0, NULL, EI_APP_RST_service };

    if (EI_API_CIP_eERR_OK != EI_API_CIP_setInstanceServiceFunc(cipNode, 0x01, 0x01, &srvcReset))
    {
        goto laError;
    }

    res = true;

laError:

    return res;
}


/*!
 *  <!-- Description: -->
 *
 *  \brief
 *  Basic initialization function.
 *
 *  \details
 *  Creates a new EtherNet/IP&trade; Adapter.<br>
 *  Initializes data structures from non-volatile storage.<br>
 *  Registers stack error handler.<br>
 *  Initializes the Adapter.<br>
 *  Create a CIP&trade; node.<br>
 *
 */
static bool EI_APP_TASK_init(APP_SParams_t* pParam)
{
    bool result = true;

    EI_API_ADP_SParam_t macAddr = {0};

    EI_API_ADP_SInit_t initParams = {0};

    // Initialize adapter for 1 (one) interface.
    adapter_s = EI_API_ADP_new(1);
//    DebugP_log("run1\r\n");
    EI_APP_CFG_init(adapter_s, &pParam->config);
//    DebugP_log("run2\r\n");
#if defined(EIP_TIME_SYNC) && (EIP_TIME_SYNC == 1)
    EI_API_ADP_setTimeSyncSupported(adapter_s);
#endif

    // Setup error handler for the EtherNet/IP stack.
    EI_API_ADP_setErrorHandlerFunc(EI_APP_TASK_stackErrorHandlerCb);
//    DebugP_log("run3\r\n");
    EI_APP_TASK_getMacAddr();

    // Init module for non-volatile data.
    EI_APP_NV_init(adapter_s, &pParam->nv);
//    DebugP_log("run4\r\n");
    // Read non-volatile data
    EI_APP_CFG_read();
//    DebugP_log("run5\r\n");
    // Initialize data for the adapter.
    EI_APP_TASK_adpInit(adapter_s);
//    DebugP_log("run6\r\n");
#if defined(EIP_QUICK_CONNECT) && (EIP_QUICK_CONNECT == 1)
    // Enable QuickConnect
    EI_API_ADP_setQuickConnectSupported(adapter_s);
#endif

    EI_APP_TASK_stackInit(pParam);
//    DebugP_log("run7\r\n");
    // Create a CIP node.
    cipNode_s = EI_API_CIP_NODE_new(&pParam->cip);

    if (NULL == cipNode_s)
    {
        return false;
    }

    // Create vendor specific classes.
    EI_APP_DEVICE_PROFILE_TASK_init(adapter_s, cipNode_s);

    // Finally apply.
    initParams.taskPrioCyclicIo  = pParam->adapter.taskPrioCyclicIo;
    initParams.taskPrioPacket    = pParam->adapter.taskPrioPacket;
    initParams.taskPrioStatistic = pParam->adapter.taskPrioStatistic;

    initParams.dll.ptp.taskPrioTsDelayRqTx   = pParam->adapter.taskPrioPtpDelayRqTx;   /* Task priority for TX delay request. */
    initParams.dll.ptp.taskPrioTxTimeStamp   = pParam->adapter.taskPrioPtpTxTimeStamp; /* Task priority for TX time stamp P1 and P2. */
    initParams.dll.ptp.taskPrioNRT           = pParam->adapter.taskPrioPtpNRT;         /* Task priority for NRT. */
    initParams.dll.ptp.taskPrioBackground    = pParam->adapter.taskPrioPtpBackground;  /* Task priority for background thread. */
    initParams.dll.ptp.timeSyncRxPhyLatency  = pParam->adapter.timeSyncRxPhyLatency;   /* TimeSync RX PHY latency. */
    initParams.dll.ptp.timeSyncTxPhyLatency  = pParam->adapter.timeSyncTxPhyLatency;   /* TimeSync TX PHY latency. */

    initParams.dll.lldp.taskPrioReceive      = pParam->adapter.taskPrioLldpReceive;    /* Task priority for receive thread. */
    initParams.dll.lldp.maxNeighborDevices   = pParam->adapter.lldpMaxNeighborDevices; /* LLDP maximum neighbor devices */


//    OSAL_printf("before entering the ADP init with maxNeighborDevices %d \r\n", initParams.dll.lldp.maxNeighborDevices);

    EI_API_ADP_init(adapter_s, initParams);

//    OSAL_printf("after the ADP init with maxNeighborDevices %d \r\n",initParams.dll.lldp.maxNeighborDevices);

    // Create callbacks for changed values.
    EI_APP_TASK_cipCreateCallback(cipNode_s);

    EI_API_ADP_setAcdDelay(adapter_s, pParam->acd.initialDelay);

    EI_API_ADP_getMacAddr(adapter_s, &macAddr);

    if (macAddr.data != NULL)
    {
//        OSAL_printf("EI_API_ADP_getMacAddr:  %02x:%02x:%02x:%02x:%02x:%02x\r\n",
//            macAddr.data[0],
//            macAddr.data[1],
//            macAddr.data[2],
//            macAddr.data[3],
//            macAddr.data[4],
//            macAddr.data[5]);
        DebugP_log("EI_API_ADP_getMacAddr:  %02x:%02x:%02x:%02x:%02x:%02x\r",
                   macAddr.data[0],
                   macAddr.data[1],
                   macAddr.data[2],
                   macAddr.data[3],
                   macAddr.data[4],
                   macAddr.data[5]);
    }
      LED_ClrVal(CONNECTIVITY_LED_BASE_ADDR,CONNECTIVITY_LED_PIN,CONNECTIVITY_LED_DIR);   //surabhi : connection establishment LED on
      DebugP_log("EIP Module Firmware Version %s \r\n",SOFTWARE_VERSION);   //surabhi : reading firmware version

    return result;
}

/*!
 *  <!-- Description: -->
 *
 *  \brief
 *  Cyclically called run function.
 *
 *  \details
 *  Cyclically called run function, handles the EtherNet/IP stack and hardware
 *  specific functionality, such as reading switches, reading inputs, setting outputs
 *  and LEDs.
 *
 */
static void EI_APP_TASK_run(EI_API_CIP_NODE_T* cipNode)
{
    EI_API_ADP_run();

    EI_APP_DEVICE_PROFILE_TASK_run(cipNode);
}



#if 0
uint32_t read_speed_in_mv(void)
{
    uint32_t baseAddr = CONFIG_ADC0_BASE_ADDR;
    adcStepConfig_t adcConfig;
    uint32_t fifoData, voltageLvl;

    // 1. Power up and initialize ADC
    ADCPowerUp(baseAddr, TRUE);
    ClockP_usleep(5U);
    ADCInit(baseAddr, FALSE, 0U, 0U);

    // 2. Configure step for AIN0 (Channel 0)
    adcConfig.mode             = ADC_OPERATION_MODE_SINGLE_SHOT;
    adcConfig.openDelay        = 0x1U;
    adcConfig.sampleDelay      = 0U;
    adcConfig.rangeCheckEnable = 0U;
    adcConfig.averaging        = ADC_AVERAGING_16_SAMPLES;
    adcConfig.fifoNum          = ADC_FIFO_NUM_0;
    adcConfig.channel          = ADC_CHANNEL_1; // AIN0
    ADCSetStepParams(baseAddr, ADC_STEP_1, &adcConfig);
    ADCStepEnable(baseAddr, ADC_STEP_1, TRUE);

    // 3. Start conversion
    ADCStart(baseAddr, TRUE);

    // 4. Wait for conversion to complete (poll FIFO count)
    while (ADCGetFIFOWordCount(baseAddr, ADC_FIFO_NUM_0) == 0);

    // 5. Read result
    fifoData = ADCGetFIFOData(baseAddr, ADC_FIFO_NUM_0);
    fifoData = ((fifoData & ADC_FIFODATA_ADCDATA_MASK) >> ADC_FIFODATA_ADCDATA_SHIFT);

    // 6. Convert to mV (assuming 12-bit ADC, 1.8V ref)
    voltageLvl  = fifoData * 1800U;
    voltageLvl /= 4095U;

        // Print the measured voltage value
        OSAL_printf("Measured SPEED_IN voltage: %u mV\r\n", voltageLvl);

    // 7. Power down ADC
    ADCStepEnable(baseAddr, ADC_STEP_1, FALSE);
    ADCPowerUp(baseAddr, FALSE);

    return voltageLvl; // mV
}
#endif
void dac_app_task(void *pvTaskArg_p)
{
    // Create semaphore for DAC process
    dacSem = xSemaphoreCreateBinary();
    configASSERT(dacSem != NULL);

    // Create FreeRTOS timer for 40ms interval
    TimerHandle_t dacTimer = xTimerCreate("DacTimer", pdMS_TO_TICKS(200), pdTRUE, NULL, vDacTimerCallback);
    configASSERT(dacTimer != NULL);
    xTimerStart(dacTimer, 0);

    // Initialize DAC I2C transaction before entering the loop
    SOC_dac_init(&i2cHandle, &i2cTransaction, txBuffer);

    while (1)
    {
        // Wait for timer signal
        if (xSemaphoreTake(dacSem, portMAX_DELAY) == pdTRUE)
        {
            uint8_t data_transmit[8]= {0xAA,0X55,0x04,0x11,0x22,0x33,0x44,0x55};
             EI_APP_UART_send_string(1,data_transmit,8);
//         DebugP_log("Test uart console\r\n");
            SOC_dac_process(speed, accelTime, decelTime, run, i2cHandle, &i2cTransaction, txBuffer);

            OSAL_SCHED_yield();
        }
    }
}

// UART RX monitor task for UART1
//static void uart_rx_monitor_task_1(void *pvTaskArg)
//{
//    UART_Handle handle = EI_APP_UART_getHandle(1);
//    uint8_t rxBuf[128];
//    UART_Transaction transaction;
//    UART_Transaction_init(&transaction);
//    transaction.buf = rxBuf;
//    transaction.count = sizeof(rxBuf);
//    transaction.args = NULL;
//    while (1)
//    {
//        if (UART_read(handle, &transaction) == UART_STATUS_SUCCESS && transaction.count > 0)
//        {
//            // Process or print received data
//            OSAL_printf("UART1 RX: %.*s\r\n", transaction.count, rxBuf);
//        }
//        OSAL_SCHED_yield();
//    }
//}

/*!
 *  <!-- Description: -->
 *
 *  \brief
 *  Cyclically calls run functions of EtherNet/IP Adapter and device profile.
 *
 *  \details
 *  Cyclically calls run function, handles the EtherNet/IP stack and hardware
 *  specific functionality, such as reading inputs, setting outputs
 *  and LEDs. It evaluates reset request and executes it when required.
 *
 *
 */
void EI_APP_TASK_main(void* pvTaskArg_p)
{
    uint32_t err = OSAL_NO_ERROR;
    int16_t  resetServiceFlag = 0;

    APP_SInstance_t* pAppInstance = (APP_SInstance_t*) pvTaskArg_p;


    CMN_BOARD_init();

    if(EI_APP_MUTEX_eERR_NOERROR != EI_APP_Mutex_init())
    {
        //Fatal error
        return;
    }

    CUST_DRIVERS_init(&pAppInstance->config.customDrivers);

    EI_APP_UART_init(&pAppInstance->config.uart);
    EI_APP_UART_init(&pAppInstance->config.uartRxTi);

    DebugP_log("UART main inst = %d\n", pAppInstance->config.uart.uartInst);
    DebugP_log("UART PCB  inst = %d\n", pAppInstance->config.uartRxTi.uartInst);
//    EI_APP_LED_init(&pAppInstance->config.led);

//    OSAL_registerPrintOut(NULL, EI_APP_UART_printf);

    err = HWAL_init ();
    if (err != OSAL_NO_ERROR)
    {
        goto laError;
    }


    err = EI_APP_DEVICE_PROFILE_init();

    if (EI_APP_DEVICE_PROFILE_ERR_OK != err)
    {
        OSAL_printf("Fatal error: Device profile initialization failed\n");
        return;
    }

    if (false == EI_APP_TASK_init(&pAppInstance->config))
    {
        OSAL_printf("Fatal error: Application initialization failed\n");
        return;
    }
#if (defined CPU_LOAD_MONITOR) && (1==CPU_LOAD_MONITOR)

    if (false == APP_startWebServerTask(&pAppInstance->config.webServer))
    {
        OSAL_printf("Fatal error: Failed to start Webserver task. \n");
        return;
    }

    CMN_CPU_API_startMonitor(&pAppInstance->config.cpuLoad);
#elif  (defined UART_CPU_LOAD_MONITOR) && (1==UART_CPU_LOAD_MONITOR)
    CMN_CPU_API_startMonitor(&pAppInstance->config.cpuLoad);
#endif

//    SOC_dac_init(&i2cHandle, &i2cTransaction, txBuffer);
    for (;;)
    {
        EI_APP_TASK_run(cipNode_s);

        resetServiceFlag = EI_APP_RST_getServiceFlag();
        if (resetServiceFlag  != -1)
        {
            break;
        }

        if((true == EI_APP_CFG_isChanged()) || (true == g_cfgNvWritePending))
        {
            g_cfgNvWritePending = false;
            EI_APP_CFG_write(false);
        }
//        EI_APP_UART_printf(1,NULL, "Test TX PCB UART\r\n", NULL);

       // OSAL_printf("test code\n");
        OSAL_SCHED_yield();

    }

laError:
    OSAL_printf("resetting device\n");

    CMN_OS_reset();
}


/*!
 *  <!-- Description: -->
 *
 *  \brief
 *  General stack error handler.
 *
 *  \details
 *  General stack error handler. Reporting of stack internal errors.
 *
 */
void EI_APP_TASK_stackErrorHandlerCb (uint32_t errorCode,           //!< [in] Error code
                                      uint8_t  fatal,               //!< [in] Is Error fatal
                                      uint8_t  numOfPara,           //!< [in] Number of parameter
                                      va_list  argPtr)              //!< [in] Error arguments
{
    OSAL_printf("###### Stack Error: 0x%08x, %s ######\n", errorCode, fatal == 0 ? "non fatal" : "fatal");

    if (fatal)
    {
        // TODO: handle the app_cleanup return codes.
        EI_APP_TASK_cleanup(adapter_s, cipNode_s);
        // TODO: Return an appropriate exit code in this case.
        while(1);
    }

}

/*!
 *  <!-- Description: -->
 *
 *  \brief
 *  General OS error handler.
 *
 *  \details
 *  General OS error handler. Reporting of OS errors.
 *
 */
void EI_APP_TASK_osErrorHandlerCb (uint32_t errorCode,      //!< [in] Error code
                                      bool     fatal,          //!< [in] Is Error fatal
                                      uint8_t  paraCnt,        //!< [in] Parameter counter
                                      va_list  argptr)         //!< [in] Error arguments
{
    globalError = errorCode;

    OSAL_printf ("\nError: 0x%8.8x, Fatal: %s", errorCode, fatal ? "yes" : "no");

#if (defined CMN_MEM_TRACE) && (1==CMN_MEM_TRACE)
        extern uint32_t __HEAP_START;
        extern uint32_t __HEAP_END;

        uint32_t heapStart = (uint32_t) &__HEAP_START;
        uint32_t heapSize  = (uint32_t) &(__HEAP_END) - (uint32_t) &(__HEAP_START);

        CMN_MEM_traceHeapCheck(heapStart, heapSize);

        void* ptr = OSAL_MEMORY_calloc(1078, 1);

        if (ptr != NULL)
        {
            OSAL_MEMORY_free(ptr);
        }

        while(1);
#endif

    if (fatal == true)
    {
        while(1);
    }
}

static void EI_APP_TASK_stackInit(APP_SParams_t* pParam)
{
    uint32_t err;
    EIP_SLoadParameter tParam;

    CUST_PRUICSS_ConfigParams_t* pPruIcssCfgParams = CUST_PRUICSS_getCfgParams();

    OSAL_MEMORY_memset (&tParam, 0, sizeof (EIP_SLoadParameter));
    OSAL_MEMORY_memcpy (tParam.ai8uMacAddr, EI_APP_TASK_macAddress, 6);

    tParam.pruIcssCfgId                 = pPruIcssCfgParams->sysConfigId;
    tParam.pPruIcssCfg                  = (PRUICSS_ConfigPtr) pPruIcssCfgParams->pPruIcssCfg;                  /* Pointer to PRU-ICSS block used by stack. */
    tParam.pPru0Firmware                = pPruIcssCfgParams->pru[0].pFirmware;                                 /* Pointer to PRU-ICSS firmware of PRU 0. */
    tParam.pPru0FirmwareLength          = pPruIcssCfgParams->pru[0].frmLength;                                 /* Length of PRU 0 firmware */
    tParam.pPru1Firmware                = pPruIcssCfgParams->pru[1].pFirmware;                                 /* Pointer to PRU-ICSS firmware of PRU 1. */
    tParam.pPru1FirmwareLength          = pPruIcssCfgParams->pru[1].frmLength;                                 /* Length of PRU 1 firmware */
    tParam.pEthPhyCfg[0]                = (ETHPHY_ConfigPtr)  pPruIcssCfgParams->pru[0].ethPhy.pConfig;        /* Pointer to PRU-ICSS ETHPHY0 configuration used by stack. */
    tParam.pEthPhyCfg[1]                = (ETHPHY_ConfigPtr)  pPruIcssCfgParams->pru[1].ethPhy.pConfig;        /* Pointer to PRU-ICSS ETHPHY1 configuration used by stack. */
    tParam.ethPhyHandle[0]              = pPruIcssCfgParams->pru[0].ethPhy.handle;                             /* PRU-ICSS ETHPHY0 handle used by stack. */
    tParam.ethPhyHandle[1]              = pPruIcssCfgParams->pru[1].ethPhy.handle;                             /* PRU-ICSS ETHPHY1 handle used by stack. */
    tParam.mdioManualMode               = pPruIcssCfgParams->manualMdioMode.isEnabled;                         /* MDIO Manual Mode */
    tParam.mdioManualModeBaseAddress    = pPruIcssCfgParams->manualMdioMode.baseAddress;                       /* MDIO Manual Mode Base Address */
    tParam.pMdioManualModeFirmware      = pPruIcssCfgParams->manualMdioMode.pFirmware;                         /* Pointer to MDIO Manual Mode firmware */
    tParam.mdioManualModeFirmwareLength = pPruIcssCfgParams->manualMdioMode.frmLength;                         /* Size of MDIO Manual Mode firmware in bytes */
    tParam.mdioManualModeFirmwareConfig = pPruIcssCfgParams->manualMdioMode.frmConfig;                         /* Configuration of MDIO Manual Mode firmware */
    tParam.taskPrioPhyMdixTask          = pParam->customDrivers.pruIcss.ethPhy.taskPrioPhyMdixTask;            /* Task priority for PHY MDIX task. */

    err = EI_API_ADP_pruicssInit (&tParam);
    if (err)
    {
        goto laError;
    }

    EI_API_ADP_pruicssStart();

    return;

//-------------------------------------------------------------------------------------------------
laError:

    printf ("\nStack Init Error: 0x%8.8x", err);
    return;
}

/*!
*  <!-- Description: -->
*
*  \brief
*  Get MAC address.
*
*  \details
*  By default, retrieve the MAC address used by the EtherNet/IP adapter from the SoC.
*
*/
static uint8_t* EI_APP_TASK_getMacAddr (void)
{
#ifndef _DEBUG_USE_CUSTOM_MAC_ADDRESS
    static uint8_t mac_addr[6];
    const uint32_t mac_address_upper_16_bits = *REG_MAC_ID1;
    const uint32_t mac_address_lower_32_bits = *REG_MAC_ID0;

    mac_addr[0] = (uint8_t) (mac_address_upper_16_bits >> 8);
    mac_addr[1] = (uint8_t) (mac_address_upper_16_bits);
    mac_addr[2] = (uint8_t) (mac_address_lower_32_bits >> 24);
    mac_addr[3] = (uint8_t) (mac_address_lower_32_bits >> 16);
    mac_addr[4] = (uint8_t) (mac_address_lower_32_bits >> 8);
    mac_addr[5] = (uint8_t) (mac_address_lower_32_bits);

    // EI_APP_aMacAddr_g is directly used in lwip
    OSAL_MEMORY_memcpy(EI_APP_TASK_macAddress, mac_addr, 6);

    return mac_addr;
#else
    return EI_APP_TASK_macAddress;
#endif
}

#endif  // (!(defined FBTLPROVIDER) || (0 == FBTLPROVIDER)) && (!(defined FBTL_REMOTE) || (0 == FBTL_REMOTE))
