/*
 *  Copyright (C) 2021-24 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.
 */


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

#include <kernel/dpl/DebugP.h>
#include <drivers/soc.h>

#if defined(SOC_AM243X) || defined(SOC_AM64X)
#include <drivers/sciclient.h>
#endif

#include <kernel/dpl/TaskP.h>
#include <drivers/pinmux.h>
#include <drivers/hw_include/hw_types.h>
#include "ti_drivers_open_close.h"
#include "ti_board_open_close.h"
#include <position_sense/endat/include/endat_drv.h>

#include "endat_periodic_trigger.h"

/* Size of the PRU instruction memory in bytes.*/
#define PRU_IRAM_SIZE   ( 12 * 1024 )    /* 12KB */

/* Size of the RTU PRU instruction memory in bytes. */
#define RTUPRU_IRAM_SIZE   ( 8 * 1024 )    /* 8KB */

/* Size of the TX PRU instruction memory in bytes.*/
#define TXPRU_IRAM_SIZE   ( 6 * 1024 )    /* 6KB */

#define PRUICSS_SLICEx CONFIG_ENDAT0_PRUICSS_PRUx

#if (PRUICSS_SLICEx == 1)
#define PRUICSS_PRUx PRUICSS_PRU1
#ifndef PRUICSSM
#define PRUICSS_TXPRUx PRUICSS_TX_PRU1
#define PRUICSS_RTUPRUx PRUICSS_RTU_PRU1
#endif
#else
#define PRUICSS_PRUx PRUICSS_PRU0
#ifndef PRUICSSM
#define PRUICSS_TXPRUx PRUICSS_TX_PRU0
#define PRUICSS_RTUPRUx PRUICSS_RTU_PRU0
#endif
#endif



#if CONFIG_ENDAT0_MODE == ENDAT_MODE_MULTI_CHANNEL_SINGLE_PRU
#if PRUICSS_SLICEx == 1
#include <endat_receiver_multi_pru1_bin.h>
#else
#include <endat_receiver_multi_pru0_bin.h>
#endif
#endif

#if (CONFIG_ENDAT0_MODE == ENDAT_MODE_MULTI_CHANNEL_MULTI_PRU)
#if PRUICSS_SLICEx == 1
#include <endat_receiver_multi_rtu_pru1_bin.h>
#include <endat_receiver_multi_pru1_bin.h>
#include <endat_receiver_multi_tx_pru1_bin.h>
#else
#include <endat_receiver_multi_rtu_pru0_bin.h>
#include <endat_receiver_multi_pru0_bin.h>
#include <endat_receiver_multi_tx_pru0_bin.h>
#endif
#endif

#if CONFIG_ENDAT0_MODE == ENDAT_MODE_SINGLE_CHANNEL_SINGLE_PRU
#if PRUICSS_SLICEx == 1
#include <endat_receiver_pru1_bin.h>
#else
#include <endat_receiver_pru0_bin.h>
#endif
#endif

#define WAIT_5_SECOND  (5000)
#define TASK_STACK_SIZE (4096)
#define TASK_PRIORITY   (6)

#define ENDAT_RX_SAMPLE_SIZE    7
#define ENDAT_RX_SESQUI_DIV (1 << 15)

#define MRS_POS_VAL2_WORD1  0x42
#define MRS_POS_VAL2_WORD2  0x43
#define MRS_POS_VAL2_WORD3  0x44

#if defined(SOC_AM261X) || defined(SOC_AM263X) || defined(SOC_AM263PX)
/* Translate the TCM local view addr to SoC view addr */
#define CPU0_BTCM_SOCVIEW(x) (CSL_R5SS0_CORE0_TCMB_U_BASE+(x - CSL_MSS_TCMB_RAM_BASE))
#else
/* Translate the TCM local view addr to SoC view addr */
#define CPU0_ATCM_SOCVIEW(x) (CSL_R5FSS0_CORE0_ATCM_BASE+(x))
#define CPU1_ATCM_SOCVIEW(x) (CSL_R5FSS1_CORE0_ATCM_BASE+(x))
#define CPU0_BTCM_SOCVIEW(x) (CSL_R5FSS0_CORE0_BTCM_BASE+(x - CSL_R5FSS0_BTCM_BASE))
#define CPU1_BTCM_SOCVIEW(x) (CSL_R5FSS1_CORE0_BTCM_BASE+(x - CSL_R5FSS1_BTCM_BASE))
#endif

/*Use soc driver instead it when available */
#if SOC_AM263PX
/**
 *  \anchor TCA6416_Mode
 *  \name IO pin mode - Input or Output
 *  @{
 */
/** \brief Configure IO pin as input */
#define TCA6416_MODE_INPUT              (0U)
/** \brief Configure IO pin as output */
#define TCA6416_MODE_OUTPUT             (1U)
/** @} */

/**
 *  \anchor TCA6416_OutState
 *  \name IO pin output state - HIGH or LOW
 *  @{
 */
/** \brief Configure IO pin output as LOW */
#define TCA6416_OUT_STATE_LOW           (0U)
/** \brief Configure IO pin output as HIGH */
#define TCA6416_OUT_STATE_HIGH          (1U)
/** @} */


#define TCA6416_REG_INPUT_PORT_0        (0x00U)
#define TCA6416_REG_INPUT_PORT_1        (0x01U)
#define TCA6416_REG_OUTPUT_PORT_0       (0x02U)
#define TCA6416_REG_OUTPUT_PORT_1       (0x03U)
#define TCA6416_REG_POL_INV_PORT_0      (0x04U)
#define TCA6416_REG_POL_INV_PORT_1      (0x05U)
#define TCA6416_REG_CONFIG_PORT_0       (0x06U)
#define TCA6416_REG_CONFIG_PORT_1       (0x07U)
#endif

static union endat_format_data gEndat_format_data_mtrctrl[3];
static uint32_t gEndat_mtrctrl_crc_err[3];
static uint32_t gEndat_2_2_crc_position_err_cnt[3];
static uint32_t gEndat_2_2_crc_addinfo1_err_cnt[3];
static uint64_t gEndat_2_2_pos_val2[3];
static int32_t gEndat_2_2_loop_mrs;
static void (*endat_fn_position_loop)(unsigned int);

uint32_t gTaskFxnStack[TASK_STACK_SIZE/sizeof(uint32_t)] __attribute__((aligned(32)));
TaskP_Object gTaskObject;
#define VALID_PERIODIC_CMD(x) ((x) == 200)

#define VALID_HOST_CMD(x) ((x == 100) || ((x) == 101) || ((x) == 102) || ((x) == 103) || ((x) == 104) || ((x) == 105) || \
                           ((x) == 106) || ((x) == 107) || ((x) == 108) || ((x) == 109) || ((x) == 110) || ((x) == 111) ||((x)== 112))

#define HAVE_COMMAND_SUPPLEMENT(x) (((x) == 2) || ((x) == 3) || ((x) == 4) || ((x) == 7) || \
                                    ((x) == 9) || ((x) == 10) || ((x) == 11) || ((x) == 13) || ((x) == 14) || \
                                    ((x) == 100) || ((x) == 101) || ((x)== 103) || ((x) == 105) || ((x) == 106) || ((x) == 107) || ((x) == 108) || ((x) == 109)  || ((x) == 200) || ((x) == 112))


#define ICSS_PRU_CORE_CLOCK CONFIG_PRU_ICSS0_CORE_CLK_FREQ_HZ
#define ENDAT_INPUT_CLOCK_UART_FREQUENCY CONFIG_PRU_ICSS0_UART_CLK_FREQ_HZ

#if CONFIG_ENDAT0_TX_RX_FIFO_CLOCK_SOURCE == 1
#define ENDAT_RX_INPUT_CLOCK_FREQUENCY ICSS_PRU_CORE_CLOCK
#define ENDAT_TX_INPUT_CLOCK_FREQUENCY ICSS_PRU_CORE_CLOCK
#else
#define ENDAT_RX_INPUT_CLOCK_FREQUENCY ENDAT_INPUT_CLOCK_UART_FREQUENCY
#define ENDAT_TX_INPUT_CLOCK_FREQUENCY ENDAT_INPUT_CLOCK_UART_FREQUENCY
#endif

#define ENDAT_POSITION_LOOP_STOP    0
#define ENDAT_POSITION_LOOP_START   1


union position
{
    float angle;
    uint64_t length;
};

struct endat_priv *priv;

#define PRUSS_PRU1_CTRL_CTPPR1      (CSL_ICSS_G_PR1_PDSP1_IRAM_REGS_BASE + 0x2C)

#define ENDAT_MULTI_CH0 (1 << 0)
#define ENDAT_MULTI_CH1 (1 << 1)
#define ENDAT_MULTI_CH2 (1 << 2)
static uint32_t gEndat_prop_delay[3];
static uint32_t gEndat_prop_delay_max;
static uint8_t gEndat_is_multi_ch;
static uint8_t gEndat_multi_ch_mask;
static uint8_t  gEndat_is_load_share_mode;
static char gUart_buffer[256];

/** \brief Global Structure pointer holding PRUSS1 memory Map. */
PRUICSS_Handle gPruIcssXHandle;

/* buffer to handle long long printf */
char gPrintf_dump_buffer[21];

/* This function is used to convert uint64_t
 * variable tp a string as printf doesn't support
 * printing 64-bit variables
 */

/* EnDat channel Info, written by PRU cores */
__attribute__((section(".gEnDatChInfo"))) struct endatChRxInfo gEndatChInfo;
char * uint64_to_str (uint64_t x)
{
    char *b = gPrintf_dump_buffer + sizeof(gPrintf_dump_buffer);
    *(--b) = '\0';
    do
    {
        *(--b) = '0' + (x % 10);
        x /= 10;
    } while (x);

    return b;
}

#if defined(SOC_AM263PX)
I2C_Handle          i2cHandle;

int32_t TCA6416_open()
{
    int32_t status = SystemP_SUCCESS;

    i2cHandle = I2C_getHandle(CONFIG_I2C0);

    return (status);
}

int32_t TCA6416_config(uint32_t ioIndex, uint32_t mode)
{

    int32_t         status = SystemP_SUCCESS;
    I2C_Transaction i2cTransaction;
    uint32_t        port, portPin, i2cAddress;
    uint8_t         buffer[2U] = {0};

    i2cAddress  = 0x20;

    if(status == SystemP_SUCCESS)
    {
        /* Each port contains 8 IOs */
        port        = 0;
        portPin     = ioIndex;

        /* Set config register address - needed for next read */
        I2C_Transaction_init(&i2cTransaction);
        buffer[0] = TCA6416_REG_CONFIG_PORT_0 + port;
        i2cTransaction.writeBuf     = buffer;
        i2cTransaction.writeCount   = 1U;
        i2cTransaction.targetAddress = i2cAddress;
        status += I2C_transfer(i2cHandle, &i2cTransaction);

        /* Read config register value */
        I2C_Transaction_init(&i2cTransaction);
        i2cTransaction.readBuf      = buffer;
        i2cTransaction.readCount    = 1;
        i2cTransaction.targetAddress = i2cAddress;
        status += I2C_transfer(i2cHandle, &i2cTransaction);

        /* Set output or input mode to particular IO pin - read/modify/write */
        I2C_Transaction_init(&i2cTransaction);
        if(TCA6416_MODE_INPUT == mode)
        {
            buffer[1] = buffer[0] | (0x01 << portPin);
        }
        else
        {
            buffer[1] = buffer[0] & ~(0x01 << portPin);
        }
        buffer[0] = TCA6416_REG_CONFIG_PORT_0 + port;
        i2cTransaction.writeBuf     = buffer;
        i2cTransaction.writeCount   = 2;
        i2cTransaction.targetAddress = i2cAddress;
        status += I2C_transfer(i2cHandle, &i2cTransaction);
    }

    return (status);
}

int32_t TCA6416_setOutput(uint32_t ioIndex, uint32_t state)
{
    int32_t         status = SystemP_SUCCESS;
    I2C_Transaction i2cTransaction;
    uint32_t        port, portPin, i2cAddress;
    uint8_t         buffer[2U] = {0};

    i2cAddress  = 0x20;

    if(status == SystemP_SUCCESS)
    {
        /* Each port contains 8 IOs */
        port        = 0;
        portPin     = ioIndex;

        /* Set output prt register address - needed for next read */
        I2C_Transaction_init(&i2cTransaction);
        buffer[0] = TCA6416_REG_OUTPUT_PORT_0 + port;
        i2cTransaction.writeBuf     = buffer;
        i2cTransaction.writeCount   = 1U;
        i2cTransaction.targetAddress = i2cAddress;
        status += I2C_transfer(i2cHandle, &i2cTransaction);

        /* Read config register value */
        I2C_Transaction_init(&i2cTransaction);
        i2cTransaction.readBuf      = buffer;
        i2cTransaction.readCount    = 1;
        i2cTransaction.targetAddress = i2cAddress;
        status += I2C_transfer(i2cHandle, &i2cTransaction);

        /* Set output or input mode to particular IO pin - read/modify/write */
        I2C_Transaction_init(&i2cTransaction);
        if(TCA6416_OUT_STATE_HIGH == state)
        {
            buffer[1] = buffer[0] | (0x01 << portPin);
        }
        else
        {
            buffer[1] = buffer[0] & ~(0x01 << portPin);
        }
        buffer[0] = TCA6416_REG_OUTPUT_PORT_0 + port;
        i2cTransaction.writeBuf     = buffer;
        i2cTransaction.writeCount   = 2;
        i2cTransaction.targetAddress = i2cAddress;
        status += I2C_transfer(i2cHandle, &i2cTransaction);
    }

    return (status);
}

void lp_bp_mux_mode_config()
{
    int32_t status = SystemP_FAILURE;
    status = TCA6416_open();
    DebugP_assert(status == SystemP_SUCCESS);

    /* Configure pins 6 and 7 as outputs */
    status = TCA6416_config(6, TCA6416_MODE_OUTPUT);
    DebugP_assert(status == SystemP_SUCCESS);
    status = TCA6416_config(7, TCA6416_MODE_OUTPUT);
    DebugP_assert(status == SystemP_SUCCESS);

    /* Set value 1 in pin 7 - BP Mux 0 */
    status = TCA6416_setOutput(7, TCA6416_OUT_STATE_HIGH);
    DebugP_assert(status == SystemP_SUCCESS);

     /* Set value 1 in pin 6 - BP Mux 1 */
    status = TCA6416_setOutput(6, TCA6416_OUT_STATE_HIGH);
    DebugP_assert(status == SystemP_SUCCESS);
}
#endif

static void endat_pruicss_init(void)
{

    gPruIcssXHandle = PRUICSS_open(CONFIG_PRU_ICSS0);
     /* Configure g_mux_en to 1 in ICSSG_SA_MX_REG Register. */
#ifdef CONFIG_ENDAT0_G_MUX_EN
    PRUICSS_setSaMuxMode(gPruIcssXHandle, PRUICSS_SA_MUX_MODE_SD_ENDAT);
#endif
    /* Set in constant table C30 to shared RAM 0x40300000 */
    PRUICSS_setConstantTblEntry(gPruIcssXHandle, PRUICSS_PRUx, PRUICSS_CONST_TBL_ENTRY_C30, ((0x40300000 & 0x00FFFF00) >> 8));
#ifdef CONFIG_ENDAT0_LOAD_SHARE_MODE

        PRUICSS_setConstantTblEntry(gPruIcssXHandle, PRUICSS_TXPRUx, PRUICSS_CONST_TBL_ENTRY_C30, ((0x40300000 & 0x00FFFF00) >> 8));
        PRUICSS_setConstantTblEntry(gPruIcssXHandle, PRUICSS_RTUPRUx, PRUICSS_CONST_TBL_ENTRY_C30, ((0x40300000 & 0x00FFFF00) >> 8));
    /*
    * Set the constant table C28 for tx pru
    * configuring the constant table C28 to point to the TX counter
    * register (CNTR). The counter is needed in firmware for adding waits and time stemps.
    */
#if CONFIG_ENDAT0_PRUICSSx == 1
#if PRUICSS_SLICEx == 1
    PRUICSS_setConstantTblEntry(gPruIcssXHandle, PRUICSS_TXPRUx, PRUICSS_CONST_TBL_ENTRY_C28, 0xA58);
#else
    PRUICSS_setConstantTblEntry(gPruIcssXHandle, PRUICSS_TXPRUx, PRUICSS_CONST_TBL_ENTRY_C28, 0xA50);
#endif /* PRUICSS_SLICEx == 1 */
#else
#if PRUICSS_SLICEx == 1
    PRUICSS_setConstantTblEntry(gPruIcssXHandle, PRUICSS_TXPRUx, PRUICSS_CONST_TBL_ENTRY_C28, 0x258);
#else
    PRUICSS_setConstantTblEntry(gPruIcssXHandle, PRUICSS_TXPRUx, PRUICSS_CONST_TBL_ENTRY_C28, 0x250);
#endif /* PRUICSS_SLICEx == 1 */
#endif /* CONFIG_ENDAT0_PRUICSSx == 1 */
#endif

     /* clear ICSS0 PRU1 data RAM */
    PRUICSS_initMemory(gPruIcssXHandle, PRUICSS_DATARAM(PRUICSS_SLICEx));
#ifdef CONFIG_ENDAT0_LOAD_SHARE_MODE
        PRUICSS_disableCore(gPruIcssXHandle, PRUICSS_RTUPRUx);
        PRUICSS_disableCore(gPruIcssXHandle, PRUICSS_TXPRUx);
#endif
    PRUICSS_disableCore(gPruIcssXHandle, PRUICSS_PRUx);


}

void endat_pre_init(void)
{
    endat_pruicss_init();
#if defined(SOC_AM263PX)
    lp_bp_mux_mode_config();
#endif
}

uint32_t endat_pruicss_load_run_fw(struct endat_priv *priv)
{

    uint32_t status = SystemP_FAILURE;

#if CONFIG_ENDAT0_MODE == ENDAT_MODE_MULTI_CHANNEL_MULTI_PRU /*enable loadshare mode*/


           /*validate binary size*/
           if((sizeof(EnDatFirmwareMultiMakeRTU_0)) > RTUPRU_IRAM_SIZE)
           {
               DebugP_log("ERROR: Firmware binary size (%d) exceeds available IRAM size (%d)\n", sizeof(EnDatFirmwareMultiMakeRTU_0), RTUPRU_IRAM_SIZE);
           }
            status = PRUICSS_disableCore(gPruIcssXHandle, PRUICSS_RTUPRUx);
            DebugP_assert(SystemP_SUCCESS == status);
            status=PRUICSS_writeMemory(gPruIcssXHandle, PRUICSS_IRAM_RTU_PRU(PRUICSS_SLICEx),
                                                        0, (uint32_t *) EnDatFirmwareMultiMakeRTU_0,
                                                        sizeof(EnDatFirmwareMultiMakeRTU_0));
            DebugP_assert(0 != status);
            status = PRUICSS_resetCore(gPruIcssXHandle, PRUICSS_RTUPRUx);
            DebugP_assert(SystemP_SUCCESS == status);
            status = PRUICSS_enableCore(gPruIcssXHandle, PRUICSS_RTUPRUx);
            DebugP_assert(SystemP_SUCCESS == status);

            /*validate binary size*/
           if((sizeof(EnDatFirmwareMultiMakePRU_0)) > PRU_IRAM_SIZE)
           {
               DebugP_log("ERROR: Firmware binary size (%d) exceeds available IRAM size (%d)\n", sizeof(EnDatFirmwareMultiMakePRU_0), PRU_IRAM_SIZE);
           }

            status=PRUICSS_disableCore(gPruIcssXHandle, PRUICSS_PRUx );
            DebugP_assert(SystemP_SUCCESS == status);
            status = PRUICSS_writeMemory(gPruIcssXHandle, PRUICSS_IRAM_PRU(PRUICSS_SLICEx),
                                                      0, (uint32_t *) EnDatFirmwareMultiMakePRU_0,
                                                      sizeof(EnDatFirmwareMultiMakePRU_0));
            DebugP_assert(0 != status);
            status = PRUICSS_resetCore(gPruIcssXHandle, PRUICSS_PRUx);
            DebugP_assert(SystemP_SUCCESS == status);
            status = PRUICSS_enableCore(gPruIcssXHandle, PRUICSS_PRUx);
            DebugP_assert(SystemP_SUCCESS == status);

           /*validate binary size*/
           if((sizeof(EnDatFirmwareMultiMakeTXPRU_0)) > TXPRU_IRAM_SIZE)
           {
               DebugP_log("ERROR: Firmware binary size (%d) exceeds available IRAM size (%d)\n", sizeof(EnDatFirmwareMultiMakeTXPRU_0), TXPRU_IRAM_SIZE);
           }
           status = PRUICSS_disableCore(gPruIcssXHandle, PRUICSS_TXPRUx);
             DebugP_assert(SystemP_SUCCESS == status);
            status = PRUICSS_writeMemory(gPruIcssXHandle,  PRUICSS_IRAM_TX_PRU(PRUICSS_SLICEx),
                                                        0, (uint32_t *) EnDatFirmwareMultiMakeTXPRU_0,
                                                        sizeof(EnDatFirmwareMultiMakeTXPRU_0));
            DebugP_assert(0 != status);
            status = PRUICSS_resetCore(gPruIcssXHandle, PRUICSS_TXPRUx);
            DebugP_assert(SystemP_SUCCESS == status);
            status = PRUICSS_enableCore(gPruIcssXHandle, PRUICSS_TXPRUx);
            DebugP_assert(SystemP_SUCCESS == status);


        status=endat_wait_initialization(priv, WAIT_5_SECOND, gEndat_multi_ch_mask);


#else

        status = PRUICSS_disableCore(gPruIcssXHandle, PRUICSS_PRUx);
        DebugP_assert(SystemP_SUCCESS == status);

#if(CONFIG_ENDAT0_MODE == ENDAT_MODE_MULTI_CHANNEL_SINGLE_PRU)
           
           
            /*validate binary size*/
           if((sizeof(EnDatFirmwareMulti_0)) > PRU_IRAM_SIZE)
           {
               DebugP_log("ERROR: Firmware binary size (%d) exceeds available IRAM size (%d)\n", sizeof(EnDatFirmwareMulti_0), PRU_IRAM_SIZE);
           }

            status = PRUICSS_writeMemory(gPruIcssXHandle, PRUICSS_IRAM_PRU(PRUICSS_SLICEx),
                                0, (uint32_t *) EnDatFirmwareMulti_0,
                                sizeof(EnDatFirmwareMulti_0));

#else
           
           /*validate binary size*/
           if((sizeof(EnDatFirmware_0)) > PRU_IRAM_SIZE)
           {
               DebugP_log("ERROR: Firmware binary size (%d) exceeds available IRAM size (%d)\n", sizeof(EnDatFirmware_0), PRU_IRAM_SIZE);
           }
            status = PRUICSS_writeMemory(gPruIcssXHandle, PRUICSS_IRAM_PRU(PRUICSS_SLICEx),
                                0, (uint32_t *) EnDatFirmware_0,
                                sizeof(EnDatFirmware_0));
#endif
        DebugP_assert(0 != status);

        status = PRUICSS_resetCore(gPruIcssXHandle, PRUICSS_PRUx);
        DebugP_assert(SystemP_SUCCESS == status);

         /*Run firmware */
        status = PRUICSS_enableCore(gPruIcssXHandle, PRUICSS_PRUx);
        DebugP_assert(SystemP_SUCCESS == status);

        /* check initialization ack from firmware, with a timeout of 5 second */
        status = endat_wait_initialization(priv, WAIT_5_SECOND, gEndat_multi_ch_mask);
#endif

    return status;
}

uint64_t endat_get_fw_version(void)
{
#if CONFIG_ENDAT0_MODE == ENDAT_MODE_MULTI_CHANNEL_SINGLE_PRU
    return *((unsigned long *)EnDatFirmwareMulti_0 + 2);
#endif

#if (CONFIG_ENDAT0_CHANNEL0) && (CONFIG_ENDAT0_MODE == ENDAT_MODE_MULTI_CHANNEL_MULTI_PRU)
    return *((unsigned long *)EnDatFirmwareMultiMakeRTU_0 + 2);
#endif

#if (CONFIG_ENDAT0_CHANNEL1) && (CONFIG_ENDAT0_MODE == ENDAT_MODE_MULTI_CHANNEL_MULTI_PRU)
    return *((unsigned long *)EnDatFirmwareMultiMakePRU_0 + 2);
#endif

#if (CONFIG_ENDAT0_CHANNEL2) && (CONFIG_ENDAT0_MODE == ENDAT_MODE_MULTI_CHANNEL_MULTI_PRU)
    return *((unsigned long *)EnDatFirmwareMultiMakeTXPRU_0 + 2);
#endif

#if CONFIG_ENDAT0_MODE == ENDAT_MODE_SINGLE_CHANNEL_SINGLE_PRU
    return *((unsigned long *)EnDatFirmware_0 + 2);
#endif

}

static void endat_print_menu(void)
{
    DebugP_log("\r|------------------------------------------------------------------------------|\n");
    DebugP_log("\r|              Select value for the encoder command from following             |\n");
    DebugP_log("\r|------------------------------------------------------------------------------|\n");
    DebugP_log("\r| 1 : Encoder send position values                                             |\n");
    DebugP_log("\r| 2 : Selection of memory area                                                 |\n");
    DebugP_log("\r| 3 : Encoder receive parameter                                                |\n");
    DebugP_log("\r| 4 : Encoder send parameter                                                   |\n");
    DebugP_log("\r| 5 : Encoder receive reset                                                    |\n");
    DebugP_log("\r| 6 : Encoder send test values                                                 |\n");
    DebugP_log("\r| 7 : Encoder receive test command                                             |\n");

    if(priv->cmd_set_2_2)
    {
        DebugP_log("\r| 8 : Encoder to send position + AI(s)                                         |\n");
        DebugP_log("\r| 9 : Encoder to send position + AI(s) and receive selection of memory area    |\n");
        DebugP_log("\r| 10: Encoder to send position + AI(s) and receive parameter                   |\n");
        DebugP_log("\r| 11: Encoder to send position + AI(s) and send parameter                      |\n");
        DebugP_log("\r| 12: Encoder to send position + AI(s) and receive error reset                 |\n");
        DebugP_log("\r| 13: Encoder to send position + AI(s) and receive test command                |\n");
        DebugP_log("\r| 14: Encoder receive communication command                                    |\n");
    }

    DebugP_log("\r|                                                                              |\n");
    DebugP_log("\r|100: Configure clock                                                          |\n");
    DebugP_log("\r|101: Simulate motor control 2.1 position loop                                 |\n");
    DebugP_log("\r|102: Toggle raw data display                                                  |\n");
    DebugP_log("\r|103: Configure tST delay                                                      |\n");
    DebugP_log("\r|104: Start continuous mode                                                    |\n");


    DebugP_log("\r|105: Configure rx arm counter (account tD)                                    |\n");
    DebugP_log("\r|106: Configure rx clock disable time (for tD)                                 |\n");

    if(priv->cmd_set_2_2)
    {
        DebugP_log("\r|107: Simulate motor control 2.2 position loop %s                        |\n",
                    gEndat_is_multi_ch || gEndat_is_load_share_mode ? "        " : "(safety)");
    }

    DebugP_log("\r|108: Configure propagation delay (tD)                                         |\n");

    if((gEndat_is_multi_ch) || (gEndat_is_load_share_mode))
    {
        DebugP_log("\r|109: Configure wire delay                                                     |\n");
    }

    DebugP_log("\r|110: Recovery Time (RT)                                                       |\n");
    DebugP_log("\r|111: Simulate motor control 2.1 position loop for long time                   |\n");
    DebugP_log("\r|112: Start/Stop Recovery Time measurement                                     |\n");
    DebugP_log("\r|200: Start periodic continuous mode                                           |\n");

    DebugP_log("\r|------------------------------------------------------------------------------|\n\r|\n");
    DebugP_log("\r| enter value: ");
}

static inline int32_t endat_get_command(void)
{
    volatile int32_t cmd = -1;


    if(DebugP_scanf("%d", &cmd) < 0)
    {
        cmd = -1;
    }

    if(VALID_2_1_CMD(cmd) || (priv->cmd_set_2_2 && VALID_2_2_CMD(cmd))
            || VALID_HOST_CMD(cmd) || VALID_PERIODIC_CMD(cmd))
    {
        return cmd;
    }

    DebugP_log("\r| WARNING: invalid command, EnDat 2.1 send position values command will be sent\n");
    return 1;
}

static void endat_recvd_print(int32_t cmd, struct endat_priv *priv,
                              union endat_format_data *u, int32_t crc)
{
    uint32_t addinfo, byte1;
    uint64_t max = pow(2, priv->single_turn_res[priv->current_channel]);
    union position position;

    /* this would give wrong values if cmd is not position related, but that is okay as then this value won't be used */
    if(priv->type[priv->current_channel] == rotary)
    {
        position.angle = ((float) u->position_addinfo.position.position) /
                         (float)max * (float)360;
    }

    else
    {
        position.length = u->position_addinfo.position.position * priv->step[priv->current_channel];
    }


    DebugP_log("\r|\n\r|\n");

    switch(cmd)
    {
        case 2:
        case 3:
        case 5:
        case 7:
        case 14:
            DebugP_log("\r| crc: %s\n", crc & 0x1 ? "success" : "failure");
            DebugP_logInfo("| crc: %x\n", u->addr_params.crc);
            break;

        case 4:
            DebugP_log("\r| parameter: 0x%x, crc: %s\n", u->addr_params.params,
                        crc & 0x1 ? "success" : "failure");
            DebugP_logInfo("\r| crc: %x\n", u->addr_params.crc);
            break;

        case 6:
            DebugP_log("\r| test value: 0x%02x%08x, crc: %s\n",
                        (uint32_t)((u->test.value & 0xFF00000000) >> 32),
                        (uint32_t)(u->test.value & 0xFFFFFFFF), crc & 0x1 ? "success" : "failure");
            DebugP_logInfo("\r| crc: %x\n", u->test.crc);
            break;

        case 1:
            if(priv->multi_turn_res[priv->current_channel])
            {
                sprintf(gUart_buffer, "\r| position: %.12f, revolution: %s, ",
                        position.angle, uint64_to_str(u->position_addinfo.position.revolution));
            }
            else
            {
                if(priv->type[priv->current_channel] == rotary)
                {
                    sprintf(gUart_buffer, "\r| position: %.12f ", position.angle);
                }
                else
                {
                    sprintf(gUart_buffer, "\r| position: %s ", uint64_to_str(position.length));
                }
            }

            DebugP_log("%s", gUart_buffer);

            DebugP_log("f1: %x, crc: %s\n", u->position_addinfo.position.f1,
                        crc & 0x1 ? "success" : "failure");

            DebugP_logInfo("| crc: %x\n", u->position_addinfo.position.crc);
            break;

        case 8:
        case 9:
        case 10:
        case 11:
        case 12:
        case 13:
            if(priv->multi_turn_res[priv->current_channel])
            {
                sprintf(gUart_buffer, "\r| position: %.12f, revolution: %s, ",
                        position.angle, uint64_to_str(u->position_addinfo.position.revolution));
            }
            else
            {
                if(priv->type[priv->current_channel] == rotary)
                {
                    sprintf(gUart_buffer, "\r| position: %.12f ", position.angle);
                }
                else
                {
                    sprintf(gUart_buffer, "\r| position: %s ", uint64_to_str(position.length));
                }
            }

            DebugP_log("%s", gUart_buffer);

            DebugP_log("f1/f2: %x/%x, crc: %s\n", u->position_addinfo.position.f1,
                        u->position_addinfo.position.f2,
                        crc & 0x1 ? "success" : "failure");

            DebugP_logInfo("| crc: %x\n", u->position_addinfo.position.crc);

            if(priv->flags.info1)
            {
                addinfo = u->position_addinfo.addinfo1.addinfo;
                byte1 = (addinfo >> 16) & ((1 << 8) - 1);

                DebugP_log("\r|\n| WRN: %x  RM: %x  Busy: %x  I4-I0: %x\n",
                            (byte1 & ENDAT_STATUS_WARN_MASK) >> ENDAT_STATUS_WARN_SHIFT,
                            (byte1 & ENDAT_STATUS_RM_MASK) >> ENDAT_STATUS_RM_SHIFT,
                            (byte1 & ENDAT_STATUS_BUSY_MASK) >> ENDAT_STATUS_BUSY_SHIFT,
                            byte1 & ENDAT_INFORMATION_MASK);
                DebugP_log("\r|\n| Additional Information 1: 0x%x, crc: %s\n", addinfo,
                            crc & 0x2 ? "success" : "failure");
                DebugP_logInfo("| addinfo1 crc: %x\n",
                                u->position_addinfo.addinfo1.crc);
            }

            if(priv->flags.info2)
            {
                addinfo = u->position_addinfo.addinfo2.addinfo;
                byte1 = (addinfo >> 16) & ((1 << 8) - 1);

                DebugP_log("\r|\n| Additional Information 2: 0x%x, crc: %s\n", addinfo,
                            crc & 0x4 ? "success" : "failure");
                DebugP_logInfo("| addinfo2 crc: %x\n",
                                u->position_addinfo.addinfo2.crc);
            }

            break;

        default:
            DebugP_log("\r|\n| ERROR: print requested for invalid command\n");
            break;
    }

    DebugP_log("\r|\n\r|\n");
}

static void endat_display_raw_data(int32_t cmd, struct endat_priv *priv)
{
    int32_t ch = priv->current_channel;
    struct endatChRxInfo *endatChRxInfo = priv->endatChRxInfo;

    switch(cmd)
    {
        case 1:
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
        case 7:
        case 8:
        case 9:
        case 10:
        case 11:
        case 12:
        case 13:
        case 14:
            DebugP_log("\r|\n| raw data: %x %x %x %x\n|\n",
                        endatChRxInfo->ch[ch].posWord0, endatChRxInfo->ch[ch].posWord1,
                        endatChRxInfo->ch[ch].posWord2, endatChRxInfo->ch[ch].posWord3);
            break;

        default:
            DebugP_log("\r|\n| Nothing raw to display - this is not a valid command\n|\n");
            break;
    }
}

/*
 * check 2.2 command case with 2.2 capability in encoder, can live w/o as endat_get_command
 * will handle and it is assumed that this function will be called after endat_get_command
 */
static int32_t endat_get_command_supplement(int32_t cmd,
                                        struct cmd_supplement *cmd_supplement)
{
    /* erase previous cmd supplements */
    memset(cmd_supplement, 0, sizeof(*cmd_supplement));

    switch(cmd)
    {
        case 2:
            DebugP_log("\r| enter MRS code (hex value): ");

            if(DebugP_scanf("%x\n", &cmd_supplement->address) < 0)
            {
                DebugP_log("\r| ERROR: invalid MRS code\n|\n|\n|\n");
                return -EINVAL;
            }

            if(cmd_supplement->address > 0xFF)
            {
                DebugP_log("\r| ERROR: invalid MRS code\n|\n|\n|\n");
                return -EINVAL;
            }

            break;

        case 9:
            DebugP_log("\r| enter MRS code (hex value): ");

            if(DebugP_scanf("%x\n", &cmd_supplement->address) < 0)
            {
                DebugP_log("\r| ERROR: invalid MRS code\n|\n|\n|\n");
                return -EINVAL;
            }

            if(cmd_supplement->address > 0xFF)
            {
                DebugP_log("\r| ERROR: invalid MRS code\n|\n|\n|\n");
                return -EINVAL;
            }

            if(cmd_supplement->address == ENDAT_SECTION2_MEMORY)
            {
                DebugP_log("\r| enter block address (hex value): ");

                if(DebugP_scanf("%x\n", &cmd_supplement->block) < 0)
                {
                    DebugP_log("\r| ERROR: invalid block address\n|\n|\n|\n");
                    return -EINVAL;
                }

                /* better compare it with number of blocks information available in eeprom */
                if(cmd_supplement->block > 0xFF)
                {
                    DebugP_log("\r| ERROR: invalid block address\n|\n|\n|\n");
                    return -EINVAL;
                }

                cmd_supplement->has_block_address = TRUE;
            }

            break;

        case 3:
        case 10:
            DebugP_log("\r| enter parameter address (hex value): ");

            if(DebugP_scanf("%x\n", &cmd_supplement->address) < 0)
            {
                DebugP_log("\r| ERROR: invalid parameter address\n|\n|\n|\n");
                return -EINVAL;
            }

            if(cmd_supplement->address > 0xFF)
            {
                DebugP_log("\r| ERROR: invalid parameter address\n|\n|\n|\n");
                return -EINVAL;
            }

            DebugP_log("\r| enter parameter (hex value): ");

            if(DebugP_scanf("%x\n", &cmd_supplement->data) < 0)
            {
                DebugP_log("\r| ERROR: invalid parameter\n|\n|\n|\n");
                return -EINVAL;
            }

            if(cmd_supplement->data > 0xFFFF)
            {
                DebugP_log("\r| ERROR: invalid parameter\n|\n|\n|\n");
                return -EINVAL;
            }

            break;

        case 4:
        case 11:
            DebugP_log("\r| enter parameter address (hex value): ");

            if(DebugP_scanf("%x\n", &cmd_supplement->address) < 0)
            {
                DebugP_log("\r| ERROR: invalid parameter address\n|\n|\n|\n");
                return -EINVAL;
            }

            if(cmd_supplement->address > 0xFF)
            {
                DebugP_log("\r| ERROR: invalid parameter address\n|\n|\n|\n");
                return -EINVAL;
            }

            break;

        case 7:
        case 13:
            DebugP_log("\r| enter port address (hex value):");

            if(DebugP_scanf("%x\n", &cmd_supplement->address) < 0)
            {
                DebugP_log("\r| ERROR: invalid port address\n|\n|\n|\n");
                return -EINVAL;
            }

            if(cmd_supplement->address > 0xFF)
            {
                DebugP_log("| ERROR: invalid port address\n|\n|\n|\n");
                return -EINVAL;
            }

            break;

        case 14:
            DebugP_log("\r| enter encoder address (hex value): ");

            if(DebugP_scanf("%x\n", &cmd_supplement->address) < 0)
            {
                DebugP_log("\r| ERROR: invalid encoder address\n|\n|\n|\n");
                return -EINVAL;
            }

            if(cmd_supplement->address > 0xFF)
            {
                DebugP_log("\r| ERROR: invalid encoder address\n|\n|\n|\n");
                return -EINVAL;
            }

            DebugP_log("\r| enter instruction (hex value): ");

            if(DebugP_scanf("%x\n", &cmd_supplement->data) < 0)
            {
                DebugP_log("\r| ERROR: invalid instruction\n|\n|\n|\n");
                return -EINVAL;
            }

            if(cmd_supplement->data > 0xFFFF)
            {
                DebugP_log("\r| ERROR: invalid instruction\n|\n|\n|\n");
                return -EINVAL;
            }

            break;

        case 100:
        case 101:
        case 107:
            DebugP_log("\r| enter frequency in Hz: ");

            if(DebugP_scanf("%u\n", &cmd_supplement->frequency) < 0)
            {
                DebugP_log("\r| ERROR: invalid frequency\n|\n|\n|\n");
                return -EINVAL;
            }

            break;

        case 103:
            DebugP_log("\r| enter tST delay counter value in ns: ");

            if(DebugP_scanf("%u\n", &cmd_supplement->frequency) < 0)
            {
                DebugP_log("\r| ERROR: invalid value\n|\n|\n|\n");
                return -EINVAL;
            }
            if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
            {
                DebugP_log("\r| Select Channel: ");
                if(DebugP_scanf("%u\n", &priv->current_channel) < 0)
                {
                    DebugP_log("\r| ERROR: invalid channel\n|\n|\n|\n");
                    return -EINVAL;
                }

                if(!((gEndat_multi_ch_mask) & (1<<priv->current_channel)))
                {
                    DebugP_log("\r| ERROR: invalid channel\n|\n|\n|\n");
                    return -EINVAL;
                }
            }
            break;

        case 105:
            DebugP_log("\r| enter rx arm counter in ns: ");

            if(DebugP_scanf("%u\n", &cmd_supplement->frequency) < 0)
            {
                DebugP_log("\r| ERROR: invalid value\n|\n|\n|\n");
                return -EINVAL;
            }
            if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
            {
                DebugP_log("\r| Select Channel: ");
                if(DebugP_scanf("%u\n", &priv->current_channel) < 0)
                {
                    DebugP_log("\r| ERROR: invalid channel\n|\n|\n|\n");
                    return -EINVAL;
                }

                if(!((gEndat_multi_ch_mask) & (1<<priv->current_channel)))
                {
                    DebugP_log("\r| ERROR: invalid channel\n|\n|\n|\n");
                    return -EINVAL;
                }
            }
            break;


        case 106:
            DebugP_log("\r| enter rx clock disable time (for tD) in ns: ");

            if(DebugP_scanf("%u\n", &cmd_supplement->frequency) < 0)
            {
                DebugP_log("\r| ERROR: invalid value\n|\n|\n|\n");
                return -EINVAL;
            }
            if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
            {
                DebugP_log("\r| Select Channel: ");
                if(DebugP_scanf("%u\n", &priv->current_channel) < 0)
                {
                    DebugP_log("\r| ERROR: invalid channel\n|\n|\n|\n");
                    return -EINVAL;
                }

                if(!((gEndat_multi_ch_mask) & (1<<priv->current_channel)))
                {
                    DebugP_log("\r| ERROR: invalid channel\n|\n|\n|\n");
                    return -EINVAL;
                }
            }
            break;

        case 108:
            DebugP_log("\r| enter propagation delay in ns: ");

            if(DebugP_scanf("%u\n", &cmd_supplement->frequency) < 0)
            {
                DebugP_log("\r| ERROR: invalid value\n|\n|\n|\n");
                return -EINVAL;
            }
            if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
            {
                DebugP_log("\r| Select Channel: ");
                if(DebugP_scanf("%u\n", &priv->current_channel) < 0)
                {
                    DebugP_log("\r| ERROR: invalid channel\n|\n|\n|\n");
                    return -EINVAL;
                }

                if(!((gEndat_multi_ch_mask) & (1<<priv->current_channel)))
                {
                    DebugP_log("\r| ERROR: invalid channel\n|\n|\n|\n");
                    return -EINVAL;
                }
            }

            break;

        case 109:
            DebugP_log("\r| enter channel number: ");

            if(DebugP_scanf("%x\n", &cmd_supplement->address) < 0)
            {
                DebugP_log("\r| ERROR: invalid channel\n|\n|\n|\n");
                return -EINVAL;
            }

            if(!(gEndat_multi_ch_mask & 1 << cmd_supplement->address))
            {
                DebugP_log("\r| ERROR: invalid channel\n|\n|\n|\n");
                return -EINVAL;
            }

            DebugP_log("\r| enter wire delay in ns: ");

            if(DebugP_scanf("%u\n", &cmd_supplement->frequency) < 0)
            {
                DebugP_log("\r| ERROR: invalid value\n|\n|\n|\n");
                return -EINVAL;
            }

            break;
        case 112:
            DebugP_log("\r| enter 1 to enable recovery time measurement and 0 to disable recovery time measurement: ");

            if(DebugP_scanf("%u\n", &cmd_supplement->frequency) < 0)
            {
                DebugP_log("\r| ERROR: invalid value\n|\n|\n|\n");
                return -EINVAL;
            }
            if(cmd_supplement->frequency > 1)
            {
                DebugP_log("\r| ERROR: invalid value\n|\n|\n|\n");
                               return -EINVAL;
            }
            if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
            {
                DebugP_log("\r| Select Channel: ");
                if(DebugP_scanf("%u\n", &priv->current_channel) < 0)
                {
                    DebugP_log("\r| ERROR: invalid channel\n|\n|\n|\n");
                    return -EINVAL;
                }

                if(!((gEndat_multi_ch_mask) & (1<<priv->current_channel)))
                {
                    DebugP_log("\r| ERROR: invalid channel\n|\n|\n|\n");
                    return -EINVAL;
                }
            }
            break;
        case 200:


            DebugP_log("\r| Enter IEP reset cycle count (must be greater than EnDat cycle time including timeout period, in IEP cycles): ");
            if(DebugP_scanf("%u\n", &cmd_supplement->iep_reset_count))
            {
                DebugP_log("\r| ERROR: invalid value\n|\n|\n|\n");
                return -EINVAL;
            }

            if(gEndat_is_load_share_mode)
            {

                if(gEndat_multi_ch_mask & (1<<0))
                {
                    DebugP_log("\r| Enter IEP trigger time (must be less than or equal to IEP reset cycle, in IEP cycles) Channel0: \n");
                    if(DebugP_scanf("%u\n", &cmd_supplement->ch0_trigger_count) < 0)
                    {
                        DebugP_log("\r| ERROR: invalid value\n|\n|\n|\n");
                        return -EINVAL;
                    }
                }

                if(gEndat_multi_ch_mask & (1<<1))
                {
                    DebugP_log("\r| Enter IEP trigger time (must be less than or equal to IEP reset cycle, in IEP cycles) Channel1: \n");
                    if(DebugP_scanf("%u\n", &cmd_supplement->ch1_trigger_count) < 0)
                    {
                        DebugP_log("\r| ERROR: invalid value\n|\n|\n|\n");
                        return -EINVAL;
                    }
                }
                if(gEndat_multi_ch_mask & (1<<2))
                {
                    DebugP_log("\r| Enter IEP trigger time (must be less than or equal to IEP reset cycle, in IEP cycles) Channel2: \n");
                    if(DebugP_scanf("%u\n", &cmd_supplement->ch2_trigger_count) < 0)
                    {
                        DebugP_log("\r| ERROR: invalid value\n|\n|\n|\n");
                        return -EINVAL;
                    }
                }

            }
            else
            {
                DebugP_log("\r| Enter IEP trigger time (must be less than or equal to IEP reset cycle, in IEP cycles): ");
                if(DebugP_scanf("%u\n", &cmd_supplement->ch0_trigger_count) < 0)
                {
                    DebugP_log("\r| ERROR: invalid value\n|\n|\n|\n");
                    return -EINVAL;
                }
            }
            break;

        default:
            cmd = -EINVAL;
            DebugP_log("\r| ERROR: no command data required for the command\n");
            break;
    }

    return cmd;
}

static int32_t endat_handle_user(struct cmd_supplement *cmd_supplement)
{
    int32_t cmd;

    endat_print_menu();
    cmd = endat_get_command();

    if(HAVE_COMMAND_SUPPLEMENT(cmd))
    {
        return endat_get_command_supplement(cmd, cmd_supplement);
    }

    return cmd;
}

static int32_t endat_calc_clock(uint32_t freq, struct endat_clk_cfg *clk_cfg)
{
    uint32_t ns;

    if(freq > 16000000 || (freq > 12000000 && freq < 16000000))
    {
        DebugP_log("\r| ERROR: frequency above 16MHz, between 12 & 16MHz not allowed\n|\n|\n");
        return -1;
    }

    if((freq != 16000000) && (ENDAT_RX_INPUT_CLOCK_FREQUENCY % (freq * 8))&&(ENDAT_TX_INPUT_CLOCK_FREQUENCY % (freq)))
        DebugP_log("\r| WARNING: exact clock divider is not possible, frequencies set would be tx: %u\trx: %u\n",
                    ENDAT_TX_INPUT_CLOCK_FREQUENCY / (ENDAT_TX_INPUT_CLOCK_FREQUENCY / freq),
                    ENDAT_RX_INPUT_CLOCK_FREQUENCY / (ENDAT_RX_INPUT_CLOCK_FREQUENCY / (freq * 8)));

    ns = ENDAT_DELAY_COUNTER_INCREMENT*(2*ICSS_PRU_CORE_CLOCK/freq); /* rx arm >= 2 clock */

    /* should be divisible by 5 */
    if(ns % 5)
    {
        ns /= 5, ns += 1,  ns *= 5;
    }

    clk_cfg->tx_div = ENDAT_TX_INPUT_CLOCK_FREQUENCY / freq - 1;
    clk_cfg->rx_div = ENDAT_RX_INPUT_CLOCK_FREQUENCY / (freq * 8) - 1;
    clk_cfg->rx_en_cnt = ns;
    clk_cfg->rx_div_attr = ENDAT_RX_SAMPLE_SIZE;

    if(freq == 16000000)
    {
        clk_cfg->rx_div_attr |= ENDAT_RX_SESQUI_DIV;
    }

    DebugP_logInfo("\r| clock config values - tx_div: %u\trx_div: %u\trx_en_cnt: %u\trx_div_attr: %x\n",
                    clk_cfg->tx_div, clk_cfg->rx_div, clk_cfg->rx_en_cnt, clk_cfg->rx_div_attr);

    return 0;
}

static uint32_t endat_do_sanity_tst_delay(uint32_t delay)
{
    /* (unsigned short)~0 is also a multiple of 5 */
    if(delay > (unsigned short)~0)
    {
        DebugP_log("\r| ERROR: delay greater than %uns, enter lesser value\n|\n|\n",
                    (unsigned short)~0);
        return delay;
    }

    if(delay % 5)
    {
        delay += 5, delay /= 5, delay *= 5;
        DebugP_log("\r| WARNING: delay not multiple of 5ns, rounding to %uns\n|\n|\n",
                    delay);
    }

    return delay;
}

static int32_t endat_position_loop_status;

/* position period in microsecond */
static int32_t endat_calc_position_period(uint32_t freq)
{
    /* 16KHz limitation due to the timer */
    if(freq > 16000)
    {
        DebugP_log("\r| ERROR: enter frequency less than or equal 16KHz\n|\n|\n");
        return -1;
    }
    else if((gEndat_is_multi_ch || gEndat_is_load_share_mode)&& freq > 8000)
    {
        DebugP_log("\r| ERROR: enter frequency less than or equal 8KHz in multi channel configuration\n|\n|\n");
        return -1;
    }

    return 1000000 / freq;
}

static void endat_position_loop_decide_termination(void *args)
{
    char c;

    while(1)
    {
        DebugP_scanf("%c", &c);
        endat_position_loop_status = ENDAT_POSITION_LOOP_STOP;
        break;
    }
    TaskP_exit();
}

void endat_process_position_command(uint32_t a0)
{
    uint32_t crc;

    endat_command_process(priv, 1, NULL);
    endat_recvd_process(priv, 1, &gEndat_format_data_mtrctrl[a0]);
    crc = endat_recvd_validate(priv, 1, &gEndat_format_data_mtrctrl[a0]);

    if(!(crc & 0x1))
    {
        gEndat_mtrctrl_crc_err[a0]++;
    }
}

uint16_t _endat_process_2_2_position_command(int32_t cmd,
        struct cmd_supplement *cmd_supplement, uint32_t a0)
{
    uint32_t crc;

    endat_command_process(priv, cmd, cmd_supplement);
    endat_recvd_process(priv, cmd, &gEndat_format_data_mtrctrl[a0]);
    crc = endat_recvd_validate(priv, cmd, &gEndat_format_data_mtrctrl[a0]);

    if(!(crc & 0x1))
    {
        gEndat_2_2_crc_position_err_cnt[a0]++;
    }

    if(priv->flags.info1 && !(crc & 0x2))
    {
        gEndat_2_2_crc_addinfo1_err_cnt[a0]++;
    }

    endat_addinfo_track(priv, cmd, cmd_supplement);

    return gEndat_format_data_mtrctrl[a0].position_addinfo.addinfo1.addinfo & 0xFFFF;
}

void endat_process_2_2_position_command(uint32_t a0)
{
    uint32_t cmd;
    struct cmd_supplement cmd_supplement;
    uint16_t pos_word;

    if(((!gEndat_is_multi_ch || !gEndat_is_load_share_mode) && priv->has_safety[priv->current_channel]) )
    {
        cmd = 9, cmd_supplement.address = gEndat_2_2_loop_mrs;
    }
    else
    {
        cmd = 8;
    }

    pos_word = _endat_process_2_2_position_command(cmd, &cmd_supplement, a0);

    if((gEndat_is_multi_ch) || (!priv->has_safety[priv->current_channel]) || (gEndat_is_load_share_mode))
    {
        return;
    }

    /* WORD3 in addinfo1 */
    if(gEndat_2_2_loop_mrs == MRS_POS_VAL2_WORD1)
    {
        gEndat_2_2_pos_val2[a0] &= 0xFFFF0000FFFFFFFF;
        gEndat_2_2_pos_val2[a0] |= (unsigned long long)pos_word << 32;
        gEndat_2_2_loop_mrs = MRS_POS_VAL2_WORD2;
        /* WORD1 in addinfo1 */
    }
    else if(gEndat_2_2_loop_mrs == MRS_POS_VAL2_WORD2)
    {
        gEndat_2_2_pos_val2[a0] &= 0xFFFFFFFFFFFF0000;
        gEndat_2_2_pos_val2[a0] |= (uint64_t)pos_word;
        gEndat_2_2_loop_mrs = MRS_POS_VAL2_WORD3;
        /* WORD2 in addinfo1 */
    }
    else if(gEndat_2_2_loop_mrs == MRS_POS_VAL2_WORD3)
    {
        gEndat_2_2_pos_val2[a0] &= 0xFFFFFFFF0000FFFF;
        gEndat_2_2_pos_val2[a0] |= (unsigned long long)pos_word << 16;
        gEndat_2_2_loop_mrs = MRS_POS_VAL2_WORD1;
    }
}

void endat_position_loop(uint32_t a0)
{
    if(endat_fn_position_loop != NULL)
    {
        if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
        {
            int32_t j;

            for(j = 0; j < 3; j++)
            {
                if(gEndat_multi_ch_mask & 1 << j)
                {
                    endat_multi_channel_set_cur(priv, j);
                    endat_fn_position_loop(j);
                }
            }
        }
        else
        {
            endat_fn_position_loop(0);
        }
    }
}

static int32_t endat_loop_task_create(void)
{
    uint32_t status;
    TaskP_Params taskParams;

    TaskP_Params_init(&taskParams);
    taskParams.name = "endat_position_loop_decide_termination";
    taskParams.stackSize = TASK_STACK_SIZE;
    taskParams.stack = (uint8_t *)gTaskFxnStack;
    taskParams.priority = TASK_PRIORITY;
    taskParams.taskMain = (TaskP_FxnMain)endat_position_loop_decide_termination;
    status = TaskP_construct(&gTaskObject, &taskParams);

    if(status != SystemP_SUCCESS)
    {
        DebugP_log("\rTask2 creation failed\n");
    }

    return status ;
}

static void endat_loop_timer_create(int32_t us)
{
    TimerP_Params timerParams;

    TimerP_Params_init(&timerParams);
    timerParams.inputPreScaler = CONFIG_TIMER0_INPUT_PRE_SCALER;
    timerParams.inputClkHz     = CONFIG_TIMER0_INPUT_CLK_HZ;
    timerParams.periodInUsec   = us;
    timerParams.oneshotMode    = 0;
    timerParams.enableOverflowInt = 1;
    TimerP_setup(gTimerBaseAddr[CONFIG_TIMER0], &timerParams);

    return ;
}

static int32_t endat_get_position_loop_chars(struct endat_priv *priv,
        int32_t continuous, int32_t is_2_2)
{
    int32_t i;

    if(priv->multi_turn_res[priv->current_channel])
    {
        i = 34;
    }
    else
    {
        i = 16;
    }

    if(!continuous)
    {
        i += 12;
    }

    i += 4;

    if(!continuous && is_2_2)
    {
        i += 4;
    }

    return i;
}

static void endat_print_position_loop(struct endat_priv *priv, int32_t continuous,
                                      int32_t is_2_2, int32_t ch)
{
    uint64_t max = pow(2, priv->single_turn_res[priv->current_channel]);
    union position position;

    if(priv->type[priv->current_channel] == rotary)
    {
        position.angle = ((float)
                          gEndat_format_data_mtrctrl[ch].position_addinfo.position.position) /
                         (float)max * (float)360;
    }
    else
    {
        position.length =
            gEndat_format_data_mtrctrl[ch].position_addinfo.position.position * priv->step[priv->current_channel];
    }

    /* max value is 2x48, has 15 digits, so 16 is safe */
    if(priv->multi_turn_res[priv->current_channel])
    {
        sprintf(gUart_buffer, "%16.12f, %16s", position.angle,
                uint64_to_str(gEndat_format_data_mtrctrl[ch].position_addinfo.position.revolution));
    }
    else
    {
        if(priv->type[priv->current_channel] == rotary)
        {
            sprintf(gUart_buffer, "%16.12f", position.angle);
        }
        else
        {
            sprintf(gUart_buffer, "%16s", uint64_to_str(position.length));
        }
    }

    DebugP_log("\r%s", gUart_buffer);

    if(!continuous)
    {
        if(is_2_2)
        {
            DebugP_log(", %10u", gEndat_2_2_crc_position_err_cnt[ch]);
        }
        else
        {
            DebugP_log(", %10u", gEndat_mtrctrl_crc_err[ch]);
        }
    }

    DebugP_log(",%3u",
                gEndat_format_data_mtrctrl[ch].position_addinfo.position.f1);

    if(!continuous && is_2_2)
    {
        DebugP_log(",%3u",
                    gEndat_format_data_mtrctrl[ch].position_addinfo.position.f2);
    }
}

static void endat_print_position_loop_channel_info(struct endat_priv *priv,
        int32_t is_2_2)
{
    int32_t k, j, i = endat_get_position_loop_chars(priv, 0, is_2_2);

    /* add 3 extra to account for spacing b/n channels */
    i += 3;
    /* find mid point */
    i /= 2;
    /* account for "CHANNEL x" display */
    i -= 4;

    for(k = i, j = 0; j < 3; j++, k = i)
        if(gEndat_multi_ch_mask & 1 << j)
        {
            while(k--)
            {
                DebugP_log("%c", ' ');
            }

            DebugP_log("\rCHANNEL %d", j);

            /* 3 - extra to account for spacing b/n channels, 9 - "CHANNEL x" length */
            k = endat_get_position_loop_chars(priv, 0, is_2_2) + 3 - i - 9;

            while(k--)
            {
                DebugP_log("%c", ' ');
            }
        }
}

static void endat_handle_prop_delay(struct endat_priv *priv,
                                    uint16_t prop_delay)
{
    /*convert rx_en_cnt into ns */
    float ct = ((priv->rx_en_cnt/ENDAT_DELAY_COUNTER_INCREMENT)*((float)1000000000/priv->pru_clock))/2; /*one endat clock cycle time = 1/endat frequency = 2*rx_en_cnt*/
    /* if propagation delay is more than half clock cycle time (2/endat frequency) then we have to reduce clock cycles for rx*/
    if(prop_delay > (ct/2))
    {
        uint16_t dis = floor(prop_delay/ct);
        /* convert propagation delay into rx arm counts */
        uint16_t temp = ((uint16_t)(((float)prop_delay * priv->pru_clock )/1000000000)) * ENDAT_DELAY_COUNTER_INCREMENT;
        endat_config_rx_arm_cnt(priv, temp);
        /* propagation delay/cycle_time */
        endat_config_rx_clock_disable(priv, dis);
    }
    else
    {
        endat_config_rx_arm_cnt(priv, priv->rx_en_cnt);
        endat_config_rx_clock_disable(priv, 0);
    }
}

static void endat_process_periodic_command(int32_t cmd,
                                       struct cmd_supplement *cmd_supplement, struct endat_priv *priv)
{
    if(cmd == 200)
    {
        endat_config_periodic_trigger(priv);
        int32_t status;
        int32_t pos_cmd = 9;
        cmd_supplement->address = 0;
        DebugP_assert(endat_command_process(priv, pos_cmd, cmd_supplement) >= 0);
        priv->iep_reset_count = cmd_supplement->iep_reset_count;
        if(gEndat_is_load_share_mode)
        {
            priv->ch0_trigger_count = gEndat_multi_ch_mask & (1<<0) ? cmd_supplement->ch0_trigger_count : 0;
            priv->ch1_trigger_count = gEndat_multi_ch_mask & (1<<1) ? cmd_supplement->ch1_trigger_count : 0;
            priv->ch2_trigger_count = gEndat_multi_ch_mask & (1<<2) ? cmd_supplement->ch2_trigger_count : 0;
        }
        else
        {
            priv->ch0_trigger_count = cmd_supplement->ch0_trigger_count;
        }

        if(endat_loop_task_create() != SystemP_SUCCESS)
        {
            DebugP_log("\r| ERROR: OS not allowing continuous mode as related Task creation failed\r\n|\r\n|\n");
            DebugP_log("Task_create() failed!\n");
            return;
        }

        struct endat_periodic_interface endat_periodic_interface;
        endat_periodic_interface.pruicss_iep = priv->pruss_iep;
        endat_periodic_interface.pruicss_dmem = priv->pruss_xchg;
        endat_periodic_interface.load_share = priv->load_share;
        endat_periodic_interface.ch0_trigger_count = priv->ch0_trigger_count;
        endat_periodic_interface.ch1_trigger_count = priv->ch1_trigger_count;
        endat_periodic_interface.ch2_trigger_count = priv->ch2_trigger_count;
        endat_periodic_interface.cmp0_count = priv->iep_reset_count;

        status = endat_config_periodic_mode(&endat_periodic_interface, gPruIcssXHandle);
        DebugP_assert(0 != status);
        endat_position_loop_status = ENDAT_POSITION_LOOP_START;

        if(priv->multi_turn_res[priv->current_channel])
        {
            DebugP_log("\r|\n\r| press enter to stop the continuous mode\r\n|\r\n|         position,       revolution, f1\r\n| ");
        }
        else
        {
            DebugP_log("\r|\n\r| press enter to stop the continuous mode\r\n|\r\n|         position, f1\r\n| ");
        }

        while(1)
            if(endat_position_loop_status == ENDAT_POSITION_LOOP_STOP)
            {
                endat_stop_periodic_continuous_mode(&endat_periodic_interface);
                endat_config_host_trigger(priv);
                return;
            }
            else
            {
                int32_t i;

                if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
                {
                    int32_t j;
                    for(i = 0, j = 0; j < 3; j++)
                    {
                        if(gEndat_multi_ch_mask & 1 << j)
                        {
                            endat_multi_channel_set_cur(priv, j);
                            endat_recvd_process(priv, 9, &gEndat_format_data_mtrctrl[j]);
                            i += endat_get_position_loop_chars(priv, 0, 1);
                            DebugP_log("| ");
                            DebugP_log("\r|\n\r| Channel: %10u\r\n| ", j);
                            endat_print_position_loop(priv, 1, 1, j);
                            DebugP_log(" ");
                            DebugP_log("\n");
                            i += 3;
                        }
                    }
                }
                else
                {
                    endat_recvd_process(priv, 1, &gEndat_format_data_mtrctrl[0]);

                    i = endat_get_position_loop_chars(priv, 1, 0);
                    endat_print_position_loop(priv, 1, 0, 0);
                }
                /* increase sleep value if glitches in display to be prevented (and would result in slower position display freq) */
                ClockP_usleep(100);

                while(i--)
                {
                    DebugP_log("%c", 8);
                }
            }


    }
}
static void endat_process_host_command(int32_t cmd,
                                       struct cmd_supplement *cmd_supplement, struct endat_priv *priv)
{
    struct endat_clk_cfg clk_cfg;
    static int32_t timer_init;

    /* clock configuration */
    if(cmd == 100)
    {
        if(endat_calc_clock(cmd_supplement->frequency, &clk_cfg) < 0)
        {
            return;
        }

        endat_config_clock(priv, &clk_cfg);

        priv->rx_en_cnt = clk_cfg.rx_en_cnt;

        if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
        {
            int32_t j;
            uint16_t d;

            for(j = 0; j < 3; j++)
            {
                if(gEndat_multi_ch_mask & 1 << j)
                {
                    endat_multi_channel_set_cur(priv, j);
                    endat_handle_prop_delay(priv, gEndat_prop_delay[priv->current_channel]);
                    d = gEndat_prop_delay_max - gEndat_prop_delay[j];
                    endat_config_wire_delay(priv, d);
                }
            }
        }
        else
        {
            endat_handle_prop_delay(priv, gEndat_prop_delay[priv->current_channel]);
        }

        /* set tST to 2us if frequency > 1MHz, else turn it off */
        if(cmd_supplement->frequency >= 1000000)
        {
            cmd_supplement->frequency = 2000;
        }
        else
        {
            cmd_supplement->frequency = 0;
        }

        /* control loop */
        if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
        {
            int32_t j;
            for(j = 0; j < 3; j++)
            {
                if(gEndat_multi_ch_mask & 1 << j)
                {
                    endat_multi_channel_set_cur(priv, j);
                    endat_process_host_command(103, cmd_supplement, priv);
                }
           }
        }
        else
        {
            endat_process_host_command(103, cmd_supplement, priv);
        }

    }
    else if(cmd == 101)
    {
        int32_t us = endat_calc_position_period(cmd_supplement->frequency);

        if(us < 0)
        {
            return;
        }

        if(endat_loop_task_create() != SystemP_SUCCESS)
        {
            DebugP_log("\r| ERROR: OS not allowing position loop as related Task creation failed\n|\n|\n");
            DebugP_log("\rTask_create() failed!\n");
            return;
        }

        endat_fn_position_loop = endat_process_position_command;

        if(!timer_init)
        {
            endat_loop_timer_create(us);
            timer_init = 1;
        }

        TimerP_start(gTimerBaseAddr[CONFIG_TIMER0]);
        endat_position_loop_status = ENDAT_POSITION_LOOP_START;

        if(priv->multi_turn_res[priv->current_channel])
        {
            DebugP_log("\r|\r\n| press enter to stop the position display|\n");

            if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
            {
                int32_t j;

                DebugP_log("\r|\n\r|");
                endat_print_position_loop_channel_info(priv, 0);
                DebugP_log("\r\n|\n");

                for(j = 0; j < 3; j++)
                {
                    if(gEndat_multi_ch_mask & 1 << j)
                    {
                        DebugP_log("| ");
                        DebugP_log("        position,       revolution, crc errors, f1\n");
                        DebugP_log(" ");
                    }
                }
                DebugP_log("\n");
            }
            else
            {
                DebugP_log("\r|         position,       revolution, crc errors, f1|\r\n ");
            }
        }
        else
        {
            DebugP_log("|\n| press enter to stop the position display\n|\n");

            if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
            {
                int32_t j;

                DebugP_log("\r|\n\r|");
                endat_print_position_loop_channel_info(priv, 0);
                DebugP_log("\r\n|\n");

                for(j = 0; j < 3; j++)
                {
                    if(gEndat_multi_ch_mask & 1 << j)
                    {
                        DebugP_log("| ");
                        DebugP_log("        position, crc errors, f1\n");
                        DebugP_log(" ");
                    }
                }
                DebugP_log("\n");
            }
            else
            {
                DebugP_log("|         position, crc errors, f1\n| ");
            }
        }

        while(1)
            if(endat_position_loop_status == ENDAT_POSITION_LOOP_STOP)
            {
                TimerP_stop(gTimerBaseAddr[CONFIG_TIMER0]);
                return;
            }
            else
            {
                int32_t i;

                if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
                {
                    int32_t j;

                    for(i = 0, j = 0; j < 3; j++)
                    {
                        if(gEndat_multi_ch_mask & 1 << j)
                        {
                            endat_multi_channel_set_cur(priv, j);
                            i += endat_get_position_loop_chars(priv, 0, 0);
                            DebugP_log("| ");
                            endat_print_position_loop(priv, 0, 0, j);
                            DebugP_log(" ");
                            DebugP_log("\n");
                            i += 3;
                        }
                    }
                }
                else
                {
                    i = endat_get_position_loop_chars(priv, 0, 0);
                    endat_print_position_loop(priv, 0, 0, 0);
                }

                /* increase sleep value if glitches in display to be prevented (and would result in slower position display freq) */
                ClockP_usleep(500);

                while(i--)
                {
                    DebugP_log("%c", 8);
                }
            }
    }

    else if(cmd == 102)
    {
        priv->raw_data ^= 1;
    }
    else if(cmd == 103)
    {
        uint32_t delay;
 
        /* convert tst delay from ns to tst counts*/
        cmd_supplement->frequency = ENDAT_DELAY_COUNTER_INCREMENT*((uint16_t)(((float)cmd_supplement->frequency * priv->pru_clock)/1000000000));

        delay = endat_do_sanity_tst_delay(cmd_supplement->frequency);

        if(delay <= (uint16_t)~0)
        {
            endat_config_tst_delay(priv, (uint16_t) delay);
        }
    }
    else if(cmd == 104)
    {

        if(endat_loop_task_create() != SystemP_SUCCESS)
        {
            DebugP_log("\r| ERROR: OS not allowing continuous mode as related Task creation failed\r\n|\r\n|\n");
            DebugP_log("Task_create() failed!\n");
            return;
        }

        endat_start_continuous_mode(priv);
        endat_position_loop_status = ENDAT_POSITION_LOOP_START;

        if(priv->multi_turn_res[priv->current_channel])
        {
            DebugP_log("\r|\n\r| press enter to stop the continuous mode\r\n|\r\n|         position,       revolution, f1\r\n| ");
        }
        else
        {
            DebugP_log("\r|\n\r| press enter to stop the continuous mode\r\n|\r\n|         position, f1\r\n| ");
        }

        while(1)
            if(endat_position_loop_status == ENDAT_POSITION_LOOP_STOP)
            {
                endat_stop_continuous_mode(priv);
                return;
            }
            else
            {
                int32_t i;

                if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
                {
                    int32_t j;
                    for(i = 0, j = 0; j < 3; j++)
                    {
                        if(gEndat_multi_ch_mask & 1 << j)
                        {
                            endat_multi_channel_set_cur(priv, j);
                            endat_recvd_process(priv, 1, &gEndat_format_data_mtrctrl[j]);
                            i += endat_get_position_loop_chars(priv, 0, 0);
                            DebugP_log("| ");
                            DebugP_log("\r|\n\r| Channel: %10u\r\n| ", j);
                            endat_print_position_loop(priv, 1, 0, j);
                            DebugP_log(" ");
                            DebugP_log("\n");
                            i += 3;
                        }
                    }
                }
                else
                {
                    endat_recvd_process(priv, 1, &gEndat_format_data_mtrctrl[0]);

                    i = endat_get_position_loop_chars(priv, 1, 0);
                    endat_print_position_loop(priv, 1, 0, 0);
                }
                /* increase sleep value if glitches in display to be prevented (and would result in slower position display freq) */
                ClockP_usleep(100);

                while(i--)
                {
                    DebugP_log("%c", 8);
                }
            }
    }
    else if(cmd == 105)
    {
        uint32_t val;
        /* convert rx arm delay from ns to rx arm count*/
        cmd_supplement->frequency = ENDAT_DELAY_COUNTER_INCREMENT*((uint16_t)(((float)cmd_supplement->frequency * priv->pru_clock)/1000000000));

        /* reuse tST delay sanity check */
        val = endat_do_sanity_tst_delay(cmd_supplement->frequency);

        if(val <= (uint16_t)~0)
        {
            endat_config_rx_arm_cnt(priv, (uint16_t)val);
        }
    }
    else if(cmd == 106)
    {
        
        /*convert rx_en_cnt into 1 enadt clock cycle period */ 
        float ct = ((priv->rx_en_cnt/ENDAT_DELAY_COUNTER_INCREMENT)*((float)1000000000/priv->pru_clock))/2;
        uint16_t dis = floor(cmd_supplement->frequency / ct);

        endat_config_rx_clock_disable(priv, dis);
    }
    else if(cmd == 108)
    {
        /* reuse tST delay sanity check */
        uint32_t val = endat_do_sanity_tst_delay(cmd_supplement->frequency);

        if(val > (uint16_t)~0)
        {
            return;
        }
        endat_handle_prop_delay(priv, (uint16_t)val);

    }
    else if(cmd == 109)
    {
        /* convert from ns to wire delay count*/
        cmd_supplement->frequency = ENDAT_DELAY_COUNTER_INCREMENT*((uint16_t)(((float)cmd_supplement->frequency * priv->pru_clock)/1000000000));
        /* reuse tST delay sanity check */
        uint32_t val = endat_do_sanity_tst_delay(cmd_supplement->frequency);

        endat_multi_channel_set_cur(priv, cmd_supplement->address);
        endat_config_wire_delay(priv, val);
    }
    else if(cmd == 107)
    {
        int32_t us = endat_calc_position_period(cmd_supplement->frequency);

        if(us < 0)
        {
            return;
        }
        if(endat_loop_task_create() != SystemP_SUCCESS)
        {
            DebugP_log("\r| ERROR: OS not allowing position loop as related Task creation failed\n|\n|\n");
            DebugP_log("\rTask_create() failed!\n");
            return;
        }

        endat_fn_position_loop = endat_process_2_2_position_command;

        if(!timer_init)
        {
            endat_loop_timer_create(us);
            timer_init = 1;
        }

        /* reset additional info's if present */
        endat_command_process(priv, 5, NULL);
        endat_addinfo_track(priv, 5, NULL);

        gEndat_2_2_loop_mrs = MRS_POS_VAL2_WORD1;

        TimerP_start(gTimerBaseAddr[CONFIG_TIMER0]);
        endat_position_loop_status = ENDAT_POSITION_LOOP_START;

        /* so that proper position value 2 is displayed from the begining */
        ClockP_usleep(us * 3);

        if((!gEndat_is_multi_ch && !priv->has_safety[priv->current_channel]) || (!gEndat_is_load_share_mode && !priv->has_safety[priv->current_channel]))
        {
            DebugP_log("\r|\n| encoder does not support safety, position value 2 would not be displayed\n|\n");
        }

        DebugP_log("\r|\n\r| press enter to stop the position display\n\r|\n");

        if(priv->multi_turn_res[priv->current_channel])
        {
            if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
            {
                int32_t j;

                DebugP_log("\r|\n\r|");
                endat_print_position_loop_channel_info(priv, 1);
                DebugP_log("\n\r|\n");

                for(j = 0; j < 3; j++)
                {
                    if(gEndat_multi_ch_mask & 1 << j)
                    {
                        DebugP_log("\r| ");
                        DebugP_log("        position,       revolution, crc errors, f1, f2");
                        DebugP_log(" ");
                    }
                }
                DebugP_log("\r\n");
            }
            else
            {
                DebugP_log("\r|         position,       revolution, crc errors, f1, f2");

                if(priv->has_safety[priv->current_channel])
                {
                    DebugP_log(",      position(2),    revolution(2), crc errors(2)");
                }
                DebugP_log("\r\n| ");
            }
        }
        else
        {
            if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
            {
                int32_t j;

                DebugP_log("\r|\n\r|");
                endat_print_position_loop_channel_info(priv, 1);
                DebugP_log("\r\n|\r\n");

                for(j = 0; j < 3; j++)
                {
                    if(gEndat_multi_ch_mask & 1 << j)
                    {
                        DebugP_log("| ");
                        DebugP_log("        position, crc errors, f1");
                        DebugP_log(" ");
                    }
                }
                DebugP_log("\n");
            }
            else
            {
                DebugP_log("|         position, crc errors, f1, f2 ");

                if(priv->has_safety[priv->current_channel])
                {
                    DebugP_log(",      position(2), crc errors(2)");
                }
                DebugP_log("\r\n| ");
            }
        }

        while(1)
            if(endat_position_loop_status == ENDAT_POSITION_LOOP_STOP)
            {
                TimerP_stop(gTimerBaseAddr[CONFIG_TIMER0]);

                /* reset additional info1 */
                endat_command_process(priv, 5, NULL);
                endat_addinfo_track(priv, 5, NULL);
                return;
            }
            else
            {
                int32_t i;

                if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
                {
                    int32_t j;

                    for(i = 0, j = 0; j < 3; j++)
                    {
                        if(gEndat_multi_ch_mask & 1 << j)
                        {
                            endat_multi_channel_set_cur(priv, j);
                            i += endat_get_position_loop_chars(priv, 0, 1);
                            DebugP_log("| ");
                            endat_print_position_loop(priv, 0, 1, j);
                            DebugP_log(" ");
                            DebugP_log("\n");
                            i += 3;
                        }
                    }
                }
                else
                {
                    i = endat_get_position_loop_chars(priv, 0, 1);
                    endat_print_position_loop(priv, 0, 1, 0);
                }

                if((!gEndat_is_multi_ch && priv->has_safety[priv->current_channel]) || (!gEndat_is_load_share_mode && priv->has_safety[priv->current_channel]))
                {
                    uint64_t multi_turn, single_turn;
                    union position position2;
                    uint64_t max = pow(2, priv->single_turn_res[priv->current_channel]);

                    multi_turn = ENDAT_GET_POS_MULTI_TURN(gEndat_2_2_pos_val2[0], priv);
                    single_turn = ENDAT_GET_POS_SINGLE_TURN(gEndat_2_2_pos_val2[0], priv);

                    if(priv->type[priv->current_channel] == rotary)
                    {
                        position2.angle = (float)single_turn / (float)max * (float)360;
                    }
                    else
                    {
                        position2.length = single_turn * priv->step[priv->current_channel];
                    }

                    DebugP_log(", ");

                    if(priv->multi_turn_res[priv->current_channel])
                    {
                        sprintf(gUart_buffer, "%16.12f, %16s", position2.angle, uint64_to_str(multi_turn));
                    }
                    else
                    {
                        if(priv->type[priv->current_channel] == rotary)
                        {
                            sprintf(gUart_buffer, "%16.12f", position2.angle);
                        }
                        else
                        {
                            sprintf(gUart_buffer, "%16s", uint64_to_str(position2.length));
                        }
                    }

                    DebugP_log("%s", gUart_buffer);

                    DebugP_log(",    %10u", gEndat_2_2_crc_addinfo1_err_cnt[0]);
                }

                /* increase sleep value if glitches in display to be prevented (and would result in slower position display freq) */
                ClockP_usleep(100);

                if((!gEndat_is_multi_ch && priv->has_safety[priv->current_channel]) || (!gEndat_is_load_share_mode && priv->has_safety[priv->current_channel]))
                {
                    if(priv->multi_turn_res[priv->current_channel])
                    {
                        i += 2 + 46 + 3;
                    }
                    else
                    {
                        i += 2 + 28 + 3;
                    }
                }

                while(i--)
                {
                    DebugP_log("%c", 8);
                }
            }
    }
    else if(cmd == 110)
    {
        uint32_t recovery_time;
        if(gEndat_is_multi_ch||gEndat_is_load_share_mode)
        {
            int32_t j;
            for( j = 0; j < 3; j++)
            {
                if(gEndat_multi_ch_mask & 1 << j)
                {
                    endat_multi_channel_set_cur(priv, j);
                    DebugP_log("channel: %d",priv->current_channel);
                    DebugP_log("\t");

                    recovery_time = endat_get_recovery_time(priv);
                    DebugP_log("\r Recovery Time: %10u ns \n", recovery_time);
                    DebugP_log("\r Current value of RT counter: %10u \n", priv->endatChRxInfo->ch[priv->current_channel].recoveryTimeParms.currentCounterValue);
                    DebugP_log("\r Previous value of RT counter: %10u \n", priv->endatChRxInfo->ch[priv->current_channel].recoveryTimeParms.lastCounterValue);
                    DebugP_log("\r Starting value of RT counter: %10u \n", priv->endatChRxInfo->ch[priv->current_channel].recoveryTimeParms.startingValue);
                }
            }
        }
        else
        {
            recovery_time = endat_get_recovery_time(priv);
            DebugP_log("\r Recovery Time: %10u ns \n", recovery_time);
            DebugP_log("\r Current value of RT counter: %10u \n", priv->endatChRxInfo->ch[priv->current_channel].recoveryTimeParms.currentCounterValue);
            DebugP_log("\r Previous value of RT counter: %10u \n", priv->endatChRxInfo->ch[priv->current_channel].recoveryTimeParms.lastCounterValue);
            DebugP_log("\r Starting value of RT counter: %10u \n", priv->endatChRxInfo->ch[priv->current_channel].recoveryTimeParms.startingValue);
        }

    }
    else if(cmd == 111)
    {
        DebugP_log("\r|press enter to stop the long time continuous mode\n|");
        if(endat_loop_task_create() != SystemP_SUCCESS)
        {
            DebugP_log("\r| ERROR: OS not allowing position loop as related Task creation failed\n|\n|\n");
            DebugP_log("\rTask_create() failed!\n");
            return;
        }

        endat_position_loop_status = ENDAT_POSITION_LOOP_START;

        uint64_t poaition_read = 0;
        /*clear CRC error count */
        memset(gEndat_mtrctrl_crc_err, 0, 3);
        while(1)
        {
            if(endat_position_loop_status == ENDAT_POSITION_LOOP_STOP)
            {

                if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
                {
                    int32_t j;

                    for(j = 0; j < 3; j++)
                    {
                        if(gEndat_multi_ch_mask & 1 << j)
                        {
                           DebugP_log("\r -------Channel %u ------\n", j);
                           DebugP_log("\r position command sent = %u \n", poaition_read);
                           DebugP_log("\r CRC failures encountered = %u \n", gEndat_mtrctrl_crc_err[j]);
                           DebugP_log(" ");
                           DebugP_log("\r");
                        }
                    }
                }
                else
                {
                    DebugP_log("\r position command sent = %u \n", poaition_read);
                    DebugP_log("\r CRC failures encountered = %u \n", gEndat_mtrctrl_crc_err[0]);
                    DebugP_log(" ");
                    DebugP_log("\r");
                }

                return;
            }
            else
            {
                poaition_read++;
                if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
                {
                    int32_t j;

                    for(j = 0; j < 3; j++)
                    {
                        if(gEndat_multi_ch_mask & 1 << j)
                        {
                            endat_multi_channel_set_cur(priv, j);
                            endat_process_position_command(j);
                        }
                    }
                }
                else
                {
                    endat_process_position_command(0);
                }

                /* increase sleep value if glitches in display to be prevented (and would result in slower position display freq) */
                ClockP_usleep(500);

            }
        }
    }
    else if(cmd == 112)
    {
        if(cmd_supplement->frequency == 1)
        {
            endat_enable_rt_measurement(priv);
        }
        else
        {
            endat_disable_rt_measurement(priv);
        }
    }
    else
    {
        DebugP_log("\r| ERROR: non host command being requested to be handled as host command\n|\n|\n");
    }
}

static void endat_handle_rx(struct endat_priv *priv, int32_t cmd)
{
    uint32_t crc;
    union endat_format_data endat_format_data;

    if(priv->raw_data)
    {
        endat_display_raw_data(cmd, priv);
    }

    endat_recvd_process(priv, cmd,  &endat_format_data);
    crc = endat_recvd_validate(priv, cmd, &endat_format_data);
    endat_recvd_print(cmd, priv, &endat_format_data, crc);

    return;
}

static void endat_print_encoder_info(struct endat_priv *priv)
{
    DebugP_log("EnDat 2.%d %s encoder\tID: %u %s\tSN: %c %u %c\n\n",
                priv->cmd_set_2_2 ? 2 : 1,
                (priv->type[priv->current_channel] == rotary) ? "rotary" : "linear",
                priv->id.binary, (char *)&priv->id.ascii,
                (char)priv->sn.ascii_msb, priv->sn.binary, (char)priv->sn.ascii_lsb);
    DebugP_log("\rPosition: %d bits ", priv->pos_res);

    if(priv->type[priv->current_channel] == rotary)
    {
        DebugP_log("(singleturn: %d, multiturn: %d) ", priv->single_turn_res[priv->current_channel],
                    priv->multi_turn_res[priv->current_channel]);
    }

    DebugP_log("[resolution: %d %s]", priv->step[priv->current_channel],
                priv->type[priv->current_channel] == rotary ? "M/rev" : "nm");
    DebugP_log("\r\n\nPropagation delay: %dns",
                gEndat_prop_delay[priv->current_channel]);
    DebugP_log("\n\n\n");
}

void endat_main(void *args)
{
    int32_t i;
    struct cmd_supplement cmd_supplement;

    uint64_t icssClk;

    void *pruicss_cfg;
    void *pruicss_iep;

    endat_clock_config endat_clk_config;

    /* Open drivers to open the UART driver for console */
    Drivers_open();
    Board_driversOpen();

/*C16 pin High for Enabling ch0 in booster pack */
#if(CONFIG_ENDAT0_BOOSTER_PACK && CONFIG_ENDAT0_CHANNEL0)
    GPIO_setDirMode(ENC1_EN_BASE_ADDR, ENC1_EN_PIN, ENC1_EN_DIR);
    GPIO_pinWriteHigh(ENC1_EN_BASE_ADDR, ENC1_EN_PIN);
#endif
/*B17 pin High for Enabling ch2 in booster pack */
#if(CONFIG_ENDAT0_BOOSTER_PACK && CONFIG_ENDAT0_CHANNEL2)
    GPIO_setDirMode(ENC2_EN_BASE_ADDR, ENC2_EN_PIN, ENC2_EN_DIR);
    GPIO_pinWriteHigh(ENC2_EN_BASE_ADDR, ENC2_EN_PIN);
#endif

    i = endat_get_fw_version();

    DebugP_log("\n\n\n");
    DebugP_log("EnDat firmware \t: %x.%x.%x (%s)\n\n", (i >> 24) & 0x7F,
                (i >> 16) & 0xFF, i & 0xFFFF, i & (1 << 31) ? "internal" : "release");

    gEndat_is_multi_ch = CONFIG_ENDAT0_MODE & 1;
    gEndat_is_load_share_mode = CONFIG_ENDAT0_MODE & 2;
    endat_pre_init();


    if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
    {

        gEndat_multi_ch_mask=(CONFIG_ENDAT0_CHANNEL0<<0|CONFIG_ENDAT0_CHANNEL1<<1|CONFIG_ENDAT0_CHANNEL2<<2);

        DebugP_log("\r\nchannels %s %s %s selected\n",
                    gEndat_multi_ch_mask & ENDAT_MULTI_CH0 ? "0" : "",
                    gEndat_multi_ch_mask & ENDAT_MULTI_CH1 ? "1" : "",
                    gEndat_multi_ch_mask & ENDAT_MULTI_CH2 ? "2" : "");

        if(!gEndat_multi_ch_mask)
        {
            DebugP_log("\r\nERROR: please select channels to be used in multi channel configuration -\n\n");
            DebugP_log("\rexit %s as no channel selected in multichannel configuration\n",
                          __func__);
            return;
        }
    }
    else
    {

        i = CONFIG_ENDAT0_CHANNEL0 & 0;

        i += CONFIG_ENDAT0_CHANNEL1;

        i += CONFIG_ENDAT0_CHANNEL2<<1;

        if(i < 0 || i > 2)
        {
           DebugP_log("\r\nWARNING: invalid channel selected, defaulting to Channel 0\n");
           i = 0;
        }
    }

    DebugP_log("\r\n\n");

    /*Translate the TCM local view addr to globel view addr */
    uint64_t gEndatChInfoGlobalAddr = CPU0_BTCM_SOCVIEW((uint64_t)&gEndatChInfo);

    pruicss_cfg = (void *)(((PRUICSS_HwAttrs *)(gPruIcssXHandle->hwAttrs))->cfgRegBase);
    pruicss_iep  = (void *)(((PRUICSS_HwAttrs *)(gPruIcssXHandle->hwAttrs))->iep0RegBase);

    icssClk = ICSS_PRU_CORE_CLOCK;

    /*3 channel pheripheral clock configuration*/
    endat_clk_config.pru_clock = icssClk;
    endat_clk_config.pru_uart_clock = ENDAT_INPUT_CLOCK_UART_FREQUENCY;
    endat_clk_config.rx_clock_source = CONFIG_ENDAT0_TX_RX_FIFO_CLOCK_SOURCE;
    endat_clk_config.tx_clock_source = CONFIG_ENDAT0_TX_RX_FIFO_CLOCK_SOURCE;


    #if (PRUICSS_SLICEx == 1)
        priv = endat_init((struct endat_pruss_xchg *)((PRUICSS_HwAttrs *)(
                          gPruIcssXHandle->hwAttrs))->pru1DramBase, &gEndatChInfo, gEndatChInfoGlobalAddr, pruicss_cfg, pruicss_iep, PRUICSS_SLICEx, &endat_clk_config);

    #else
        priv = endat_init((struct endat_pruss_xchg *)((PRUICSS_HwAttrs *)(
                          gPruIcssXHandle->hwAttrs))->pru0DramBase, &gEndatChInfo, gEndatChInfoGlobalAddr,  pruicss_cfg, pruicss_iep, PRUICSS_SLICEx, &endat_clk_config);
    #endif




    if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
    {
        endat_config_multi_channel_mask(priv, gEndat_multi_ch_mask, gEndat_is_load_share_mode);
    }
    else
    {
        endat_config_channel(priv, i);
    }

    endat_config_host_trigger(priv);

    /* Configure Delays based on the ICSSG frequency*/
    /* Count = ((required delay * icssClk)/1000) */
    priv->pruss_xchg->endat_delay_125ns = ((icssClk*125)/1000000000);
    priv->pruss_xchg->endat_delay_51us = ((icssClk*51)/1000000 );
    priv->pruss_xchg->endat_delay_5us = ((icssClk*5)/1000000);
    priv->pruss_xchg->endat_delay_1ms = ((icssClk/1000) * 1);
    priv->pruss_xchg->endat_delay_2ms = ((icssClk/1000) * 2);
    priv->pruss_xchg->endat_delay_12ms = ((icssClk/1000) * 12);
    priv->pruss_xchg->endat_delay_50ms = ((icssClk/1000) * 50);
    priv->pruss_xchg->endat_delay_380ms = ((icssClk/1000) * 380);
    priv->pruss_xchg->endat_delay_900ms = ((icssClk/1000) * 900);
    priv->pruss_xchg->icssg_clk = icssClk;


    i = endat_pruicss_load_run_fw(priv);

    if(i < 0)
    {
        DebugP_log("\rERROR: EnDat initialization failed -\n\n");

        if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
        {
            uint8_t tmp;

            tmp = endat_multi_channel_detected(priv) & gEndat_multi_ch_mask;
            tmp ^= gEndat_multi_ch_mask;
            DebugP_log("\r\tunable to detect encoder in channel %s %s %s\n",
                        tmp & ENDAT_MULTI_CH0 ? "0" : "",
                        tmp & ENDAT_MULTI_CH1 ? "1" : "",
                        tmp & ENDAT_MULTI_CH2 ? "2" : "");
        }
        else
        {
            DebugP_log("\r\tcheck whether encoder is connected and ensure proper connections\n");
        }

        DebugP_log("\rexit %s due to failed firmware initialization\n", __func__);
        return;
    }

    /* read encoder info at low frequency so that cable length won't affect */
    cmd_supplement.frequency = 200 * 1000;
    endat_process_host_command(100, &cmd_supplement, priv);

    if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
    {
        int32_t j;

        for(j = 0; j < 3; j++)
        {
            if(gEndat_multi_ch_mask & 1 << j)
            {
                endat_multi_channel_set_cur(priv, j);
                /*Initialization of RT parameters*/
                endat_init_rt_measurement(priv);
                if(endat_get_encoder_info(priv) < 0)
                {
                    DebugP_log("\rEnDat initialization channel %d failed\n", j);
                    DebugP_log("\rexit %s due to failed initialization\n", __func__);
                    return;
                }
                /*convert cnt to time in ns ((cnt*1000000000)/icssClk) before use*/
                gEndat_prop_delay[priv->current_channel] = endat_get_prop_delay(priv)*((float)(1000000000)/icssClk);
                DebugP_log("\n\t\t\t\tCHANNEL %d\n\n", j);
                endat_print_encoder_info(priv);
            }
        }

        gEndat_prop_delay_max = gEndat_prop_delay[0] > gEndat_prop_delay[1] ?
                               gEndat_prop_delay[0] : gEndat_prop_delay[1];
        gEndat_prop_delay_max = gEndat_prop_delay_max > gEndat_prop_delay[2] ?
                               gEndat_prop_delay_max : gEndat_prop_delay[2];

    }
    else
    {
        /*Initialization of RT parameters*/
        endat_init_rt_measurement(priv);
        if(endat_get_encoder_info(priv) < 0)
        {
            DebugP_log("\rEnDat initialization failed\n");
            DebugP_log("\rexit %s due to failed initialization\n", __func__);
            return;
        }
        /*convert cnt to time in ns ((cnt*1000000000)/icssClk) before use*/
        gEndat_prop_delay[priv->current_channel] = endat_get_prop_delay(priv)*((float)(1000000000)/icssClk);

        endat_print_encoder_info(priv);
    }

    /* default frequency - 8MHz for 2.2 encoders, 1MHz for 2.1 encoders */
    if(priv->cmd_set_2_2)
    {
    #if (ENDAT_INPUT_CLOCK_UART_FREQUENCY == 160000000) || (ENDAT_INPUT_CLOCK_FREQUENCY == 200000000)
        cmd_supplement.frequency = 5 * 1000 * 1000;
    #else
        cmd_supplement.frequency = 8 * 1000 * 1000;
    #endif

    }
    else
    {
        cmd_supplement.frequency = 1 * 1000 * 1000;
    }

    endat_process_host_command(100, &cmd_supplement, priv);

    while(1)
    {
        int32_t cmd;

        cmd = endat_handle_user(&cmd_supplement);

        if(cmd < 0)
        {
            continue;
        }

        if(VALID_HOST_CMD(cmd))
        {
            endat_process_host_command(cmd, &cmd_supplement, priv);
            DebugP_log("\r|\n\r|\n");
            continue;
        }

        if(VALID_PERIODIC_CMD(cmd))
        {
            endat_process_periodic_command(cmd, &cmd_supplement, priv);
            DebugP_log("\r|\n\r|\n");
            continue;
        }

        if(endat_command_process(priv, cmd, &cmd_supplement) < 0)
        {
            continue;
        }

        if(gEndat_is_multi_ch || gEndat_is_load_share_mode)
        {
            int32_t j;
            int8_t rt_error;

            DebugP_log("\r|\n");

            for(j = 0; j < 3; j++)
            {
                if(gEndat_multi_ch_mask & 1 << j)
                {
                    endat_multi_channel_set_cur(priv, j);
                    DebugP_log("\r|\n|\t\t\t\tCHANNEL %d\n", j);
                    endat_handle_rx(priv, cmd);
                    /* Recovery Time validation */
                    if(endat_status_rt_measurement(priv) == 1)
                    {
                        rt_error =  endat_check_rt_error(priv);
                        if(rt_error != RT_NO_ERROR)
                        {
                            DebugP_log("\r Error: Channel %d - Recovery time out of expected range. \n", priv->current_channel);
                            if(rt_error == RT_COUNTER_STUCK_ERROR)
                            {
                                DebugP_log("\r Error: Counter for Channel %d is stuck.\n", priv->current_channel);
                            }
                        }
                    }
                }
            }
        }
        else
        {
            int8_t rt_error;
            endat_handle_rx(priv, cmd);
            /* Recovery Time validation */
            if(endat_status_rt_measurement(priv) == 1)
            {
                rt_error =  endat_check_rt_error(priv);
                if(rt_error == RT_COUNTER_STUCK_ERROR)
                {
                    DebugP_log("\r Error: Channel %d - Recovery time out of expected range. \n", priv->current_channel);
                    if(priv->endatChRxInfo->ch[priv->current_channel].recoveryTimeParms.isCounterStuck == 1)
                    {
                        DebugP_log("\r Error: Counter for Channel %d is stuck. \n", priv->current_channel);
                    }
                }
            }
        }

        /* this cannot be done except as last in loop; additional info becomes applicable from next command onwards only */
        endat_addinfo_track(priv, cmd, &cmd_supplement);
    }

    Board_driversClose();
    Drivers_close();
}

