#!/bin/bash
# Copyright (C) 2024 MOXA Inc. All rights reserved.
# This software is distributed under the terms of the MOXA SOFTWARE NOTICE.
# See the file LICENSE for details.
#
# Name:
#       MOXA Serial Mode Control Utility
#
# Description:
#       For controlling gpio-sysfs value from Super IO UART mode.
#
# Authors:
#       2024    Wilson Huang <WilsonYS.Huang@moxa.com>
#       2024    Elvis Yao <ElvisCW.Yao@moxa.com>
#

source "/usr/lib/mx-gpio-lib"
source "/usr/lib/mx-common-lib"

SETSETIAL="/usr/bin/setserial"

MODEL_NAME=""
NUM_OF_UART=""
NUM_OF_UART_MODE=4
TARGET_UART_PORT=""
TARGET_UART_MODE=""
TARGET_OPCODE=0
TARGET_GET_MODE_OPCODE=1
TARGET_INVALID_OPCODE=2
TARGET_SET_MODE_OPCODE=3

OS_ID="$(awk -v opt="ID" -F= '$1==opt { print $2 ;}' /etc/os-release | tr -d '"')"

function MPC3000::profile() {
        UART_PORT_TBL=""
        UART_IT87_GPIO_TBL_PORT0=(13 11 12 1)
        UART_IT87_GPIO_TBL_PORT1=(16 14 15 2)
        NUM_OF_IT87_UART=2

        UART_MODE_TBL=""
        UART_IT87_GPIO_VAL_TBL_MODE0=(1 0 0 0)
        UART_IT87_GPIO_VAL_TBL_MODE1=(0 1 0 1)
        UART_IT87_GPIO_VAL_TBL_MODE2=(0 0 1 1)
        UART_IT87_GPIO_VAL_TBL_MODE3=(0 0 1 1)

        UART_MODE_STR=""
        UART_MODE_STR1=("RS-232" "RS-485-2W" "RS-422/RS-485-4W")
}

function MPC3000::init() {
        # Determine number of uart
        NUM_OF_UART=$((NUM_OF_UART += NUM_OF_IT87_UART))

        # Determine uart port and uart mode table
        UART_PORT_TBL="UART_IT87_GPIO_TBL_PORT${TARGET_UART_PORT}[@]"
        UART_PORT_TBL=("${!UART_PORT_TBL}")

        if [[ -n $TARGET_UART_MODE ]]; then
                UART_MODE_TBL="UART_IT87_GPIO_VAL_TBL_MODE${TARGET_UART_MODE}[@]"
                UART_MODE_TBL=("${!UART_MODE_TBL}")
        fi

        UART_MODE_STR=("${UART_MODE_STR1[@]}")
}

function MPC3000::get_uart_mode() {
        get_uart_mode_it87
}

function MPC3000::set_uart_mode() {
        set_uart_mode_it87
}

function DRPA100::profile() {
        DRP100_series_profile
}

function DRPA100::init() {
        DRP100_series_init
}

function DRPA100::get_uart_mode() {
        get_uart_mode_it87_and_mxuport "$@"
}

function DRPA100::set_uart_mode() {
        set_uart_mode_it87_and_mxuport "$@"
}

function DRPC100::profile() {
        DRP100_series_profile
}

function DRPC100::init() {
        DRP100_series_init
}

function DRPC100::get_uart_mode() {
        get_uart_mode_it87_and_mxuport "$@"
}

function DRPC100::set_uart_mode() {
        set_uart_mode_it87_and_mxuport "$@"
}

function DRP100_series_profile() {
        IO_BOARD_GPIO_TBL=(33 34 36 37)
        # Refer to main circuit diagram P.37
        IO_BOARD_4_COM_2_LAN_GPIO=(0 0 0 1)
        IO_BOARD_6_COM_GPIO=(1 1 0 1)

        UART_PORT_TBL=""
        UART_IT87_GPIO_TBL_PORT0=(13 11 12 1)
        UART_IT87_GPIO_TBL_PORT1=(16 14 15 2)
        NUM_OF_IT87_UART=2
        UART_IO_BOARD_4_PORT_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2" "ttyUSB3")
        NUM_OF_IO_BOARD_4_PORT_UART=${#UART_IO_BOARD_4_PORT_TBL[@]}
        UART_IO_BOARD_6_PORT_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2" "ttyUSB3" "ttyUSB4" "ttyUSB5")
        NUM_OF_IO_BOARD_6_PORT_UART=${#UART_IO_BOARD_6_PORT_TBL[@]}

        UART_MODE_TBL=""
        UART_IT87_GPIO_VAL_TBL_MODE0=(1 0 0 0)
        UART_IT87_GPIO_VAL_TBL_MODE1=(0 1 0 1)
        UART_IT87_GPIO_VAL_TBL_MODE2=(0 0 1 1)
        UART_IT87_GPIO_VAL_TBL_MODE3=(0 0 1 1)
        UART_IO_BOARD_MODE_TBL=("0x0000" "0x0001" "0x0002" "0x0003")

        UART_MODE_STR=""
        UART_MODE_STR1=("RS-232" "RS-485-2W" "RS-422/RS-485-4W")
        UART_MODE_STR2=("RS-232" "RS-485-2W" "RS-422" "RS-485-4W")
}

function DRP100_series_init() {
        local io_board_gpio_pins
        local io_board_gpio_val
        local gpc_name
        local ret

        ret=0

        if ! is_module_loaded gpio_it87; then
                echo "gpio_it87 driver is not loaded"
                exit 1
        fi

        if [[ "${OS_ID}" == "centos" ]]; then
                io_board_gpio_pins[0]=$(gpc_it8786 ${IO_BOARD_GPIO_TBL[0]})
                [[ $? -ne 0 ]] && ret=1
                io_board_gpio_pins[1]=$(gpc_it8786 ${IO_BOARD_GPIO_TBL[1]})
                [[ $? -ne 0 ]] && ret=1
                io_board_gpio_pins[2]=$(gpc_it8786 ${IO_BOARD_GPIO_TBL[2]})
                [[ $? -ne 0 ]] && ret=1
                io_board_gpio_pins[3]=$(gpc_it8786 ${IO_BOARD_GPIO_TBL[3]})
                [[ $? -ne 0 ]] && ret=1

                if [[ $ret -ne 0 ]]; then
                        echo "Initialization Failed"
                        exit $ret
                fi

                io_board_gpio_val[0]=$(gpio_get_value_sysfs ${io_board_gpio_pins[0]})
                [[ $? -ne 0 ]] && ret=2
                io_board_gpio_val[1]=$(gpio_get_value_sysfs ${io_board_gpio_pins[1]})
                [[ $? -ne 0 ]] && ret=2
                io_board_gpio_val[2]=$(gpio_get_value_sysfs ${io_board_gpio_pins[2]})
                [[ $? -ne 0 ]] && ret=2
                io_board_gpio_val[3]=$(gpio_get_value_sysfs ${io_board_gpio_pins[3]})
                [[ $? -ne 0 ]] && ret=2

                if [[ $ret -ne 0 ]]; then
                        echo "Initialization Failed"
                        exit $ret
                fi
        else
                io_board_gpio_pins=(
                        $(gpc_it8786_remap ${IO_BOARD_GPIO_TBL[0]})
                        $(gpc_it8786_remap ${IO_BOARD_GPIO_TBL[1]})
                        $(gpc_it8786_remap ${IO_BOARD_GPIO_TBL[2]})
                        $(gpc_it8786_remap ${IO_BOARD_GPIO_TBL[3]})
                )

                gpc_name=$(gpio_get_gpiochip_name gpio_it87)
                [[ $? -ne 0 ]] && ret=1
                io_board_gpio_val[0]=$(gpio_get_value_libgpiod ${io_board_gpio_pins[0]} $gpc_name)
                [[ $? -ne 0 ]] && ret=1
                io_board_gpio_val[1]=$(gpio_get_value_libgpiod ${io_board_gpio_pins[1]} $gpc_name)
                [[ $? -ne 0 ]] && ret=1
                io_board_gpio_val[2]=$(gpio_get_value_libgpiod ${io_board_gpio_pins[2]} $gpc_name)
                [[ $? -ne 0 ]] && ret=1
                io_board_gpio_val[3]=$(gpio_get_value_libgpiod ${io_board_gpio_pins[3]} $gpc_name)
                [[ $? -ne 0 ]] && ret=1

                if [[ $ret -ne 0 ]]; then
                        echo "Initialization Failed"
                        exit $ret
                fi
        fi

        # Determine number of uart
        NUM_OF_UART=$((NUM_OF_UART += NUM_OF_IT87_UART))

        if [[ "${IO_BOARD_4_COM_2_LAN_GPIO[*]}" == "${io_board_gpio_val[*]}" ]]; then
                NUM_OF_UART=$((NUM_OF_UART += NUM_OF_IO_BOARD_4_PORT_UART))
        elif [[ "${IO_BOARD_6_COM_GPIO[*]}" == "${io_board_gpio_val[*]}" ]]; then
                NUM_OF_UART=$((NUM_OF_UART += NUM_OF_IO_BOARD_6_PORT_UART))
        fi

        # Determine uart port and uart mode table
        if [[ $TARGET_UART_PORT -ge 0 ]] && [[ $TARGET_UART_PORT -lt $NUM_OF_IT87_UART ]]; then
                UART_PORT_TBL="UART_IT87_GPIO_TBL_PORT${TARGET_UART_PORT}[@]"
                UART_PORT_TBL=("${!UART_PORT_TBL}")

                if [[ -n $TARGET_UART_MODE ]]; then
                        UART_MODE_TBL="UART_IT87_GPIO_VAL_TBL_MODE${TARGET_UART_MODE}[@]"
                        UART_MODE_TBL=("${!UART_MODE_TBL}")
                fi

                UART_MODE_STR=("${UART_MODE_STR1[@]}")
        elif [[ $TARGET_UART_PORT -ge $NUM_OF_IT87_UART ]] && [[ $TARGET_UART_PORT -le $NUM_OF_UART ]]; then
                if [[ $NUM_OF_UART -eq 6 ]]; then
                        UART_PORT_TBL=("${UART_IO_BOARD_4_PORT_TBL[@]}")
                elif [[ $NUM_OF_UART -eq 8 ]]; then
                        UART_PORT_TBL=("${UART_IO_BOARD_6_PORT_TBL[@]}")
                fi

                UART_MODE_TBL=("${UART_IO_BOARD_MODE_TBL[@]}")
                UART_MODE_STR=("${UART_MODE_STR2[@]}")
        fi
}

function BXPA100::profile() {
        IO_BOARD_GPIO_TBL=(91 92 93)
        # Refer to circuit diagram P.3
        IO_BOARD_3_COM_2_LAN_GPIO=(0 1 0)
        IO_BOARD_8_COM_GPIO=(0 0 0)

        UART_PORT_TBL=""
        UART_IT87_GPIO_TBL_PORT0=(13 11 12 1)
        UART_IT87_GPIO_TBL_PORT1=(16 14 15 2)
        NUM_OF_IT87_UART=2
        UART_IO_BOARD_3_PORT_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2")
        NUM_OF_IO_BOARD_3_PORT_UART=${#UART_IO_BOARD_3_PORT_TBL[@]}
        UART_IO_BOARD_8_PORT_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2" "ttyUSB3" "ttyUSB4" "ttyUSB5" "ttyUSB6" "ttyUSB7")
        NUM_OF_IO_BOARD_8_PORT_UART=${#UART_IO_BOARD_8_PORT_TBL[@]}

        UART_MODE_TBL=""
        UART_IT87_GPIO_VAL_TBL_MODE0=(1 0 0 0)
        UART_IT87_GPIO_VAL_TBL_MODE1=(0 1 0 1)
        UART_IT87_GPIO_VAL_TBL_MODE2=(0 0 1 1)
        UART_IT87_GPIO_VAL_TBL_MODE3=(0 0 1 1)
        UART_IO_BOARD_MODE_TBL=("0x0000" "0x0001" "0x0002" "0x0003")

        UART_MODE_STR=""
        UART_MODE_STR1=("RS-232" "RS-485-2W" "RS-422/RS-485-4W")
        UART_MODE_STR2=("RS-232" "RS-485-2W" "RS-422" "RS-485-4W")
}

function BXPA100::init() {
        BXP100_series_init
}

function BXPA100::get_uart_mode() {
        get_uart_mode_it87_and_mxuport "$@"
}

function BXPA100::set_uart_mode() {
        set_uart_mode_it87_and_mxuport "$@"
}

function BXPC100::profile() {
        IO_BOARD_GPIO_TBL=(80 81 82)
        # Refer to circuit diagram P.3
        IO_BOARD_3_COM_2_LAN_GPIO=(0 1 0)
        IO_BOARD_8_COM_GPIO=(0 0 0)

        UART_PORT_TBL=""
        UART_IT87_GPIO_TBL_PORT0=(13 11 12 1)
        UART_IT87_GPIO_TBL_PORT1=(16 14 15 2)
        NUM_OF_IT87_UART=2
        UART_IO_BOARD_3_PORT_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2")
        NUM_OF_IO_BOARD_3_PORT_UART=${#UART_IO_BOARD_3_PORT_TBL[@]}
        UART_IO_BOARD_8_PORT_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2" "ttyUSB3" "ttyUSB4" "ttyUSB5" "ttyUSB6" "ttyUSB7")
        NUM_OF_IO_BOARD_8_PORT_UART=${#UART_IO_BOARD_8_PORT_TBL[@]}

        UART_MODE_TBL=""
        UART_IT87_GPIO_VAL_TBL_MODE0=(1 0 0 0)
        UART_IT87_GPIO_VAL_TBL_MODE1=(0 1 0 1)
        UART_IT87_GPIO_VAL_TBL_MODE2=(0 0 1 1)
        UART_IT87_GPIO_VAL_TBL_MODE3=(0 0 1 1)
        UART_IO_BOARD_MODE_TBL=("0x0000" "0x0001" "0x0002" "0x0003")

        UART_MODE_STR=""
        UART_MODE_STR1=("RS-232" "RS-485-2W" "RS-422/RS-485-4W")
        UART_MODE_STR2=("RS-232" "RS-485-2W" "RS-422" "RS-485-4W")
}

function BXPC100::init() {
        BXP100_series_init
}

function BXPC100::get_uart_mode() {
        get_uart_mode_it87_and_mxuport "$@"
}

function BXPC100::set_uart_mode() {
        set_uart_mode_it87_and_mxuport "$@"
}

function BXP100_series_init() {
        local io_board_gpio_pins
        local io_board_gpio_val
        local gpc_name
        local ret

        ret=0

        if ! is_module_loaded gpio_it87; then
                echo "gpio_it87 driver is not loaded"
                exit 1
        fi

        if [[ "${OS_ID}" == "centos" ]]; then
                io_board_gpio_pins[0]=$(gpc_it8786 ${IO_BOARD_GPIO_TBL[0]})
                [[ $? -ne 0 ]] && ret=1
                io_board_gpio_pins[1]=$(gpc_it8786 ${IO_BOARD_GPIO_TBL[1]})
                [[ $? -ne 0 ]] && ret=1
                io_board_gpio_pins[2]=$(gpc_it8786 ${IO_BOARD_GPIO_TBL[2]})
                [[ $? -ne 0 ]] && ret=1

                if [[ $ret -ne 0 ]]; then
                        echo "Initialization Failed"
                        exit $ret
                fi

                io_board_gpio_val[0]=$(gpio_get_value_sysfs ${io_board_gpio_pins[0]})
                [[ $? -ne 0 ]] && ret=2
                io_board_gpio_val[1]=$(gpio_get_value_sysfs ${io_board_gpio_pins[1]})
                [[ $? -ne 0 ]] && ret=2
                io_board_gpio_val[2]=$(gpio_get_value_sysfs ${io_board_gpio_pins[2]})
                [[ $? -ne 0 ]] && ret=2

                if [[ $ret -ne 0 ]]; then
                        echo "Initialization Failed"
                        exit $ret
                fi
        else
                io_board_gpio_pins=(
                        $(gpc_it8786_remap ${IO_BOARD_GPIO_TBL[0]})
                        $(gpc_it8786_remap ${IO_BOARD_GPIO_TBL[1]})
                        $(gpc_it8786_remap ${IO_BOARD_GPIO_TBL[2]})
                )

                gpc_name=$(gpio_get_gpiochip_name gpio_it87)
                [[ $? -ne 0 ]] && ret=1
                io_board_gpio_val[0]=$(gpio_get_value_libgpiod ${io_board_gpio_pins[0]} $gpc_name)
                [[ $? -ne 0 ]] && ret=1
                io_board_gpio_val[1]=$(gpio_get_value_libgpiod ${io_board_gpio_pins[1]} $gpc_name)
                [[ $? -ne 0 ]] && ret=1
                io_board_gpio_val[2]=$(gpio_get_value_libgpiod ${io_board_gpio_pins[2]} $gpc_name)
                [[ $? -ne 0 ]] && ret=1

                if [[ $ret -ne 0 ]]; then
                        echo "Initialization Failed"
                        exit $ret
                fi
        fi

        # Determine number of uart
        NUM_OF_UART=$((NUM_OF_UART += NUM_OF_IT87_UART))

        if [[ "${IO_BOARD_3_COM_2_LAN_GPIO[*]}" == "${io_board_gpio_val[*]}" ]]; then
                NUM_OF_UART=$((NUM_OF_UART += NUM_OF_IO_BOARD_3_PORT_UART))
        elif [[ "${IO_BOARD_8_COM_GPIO[*]}" == "${io_board_gpio_val[*]}" ]]; then
                NUM_OF_UART=$((NUM_OF_UART += NUM_OF_IO_BOARD_8_PORT_UART))
        fi

        # Determine uart port and uart mode table
        if [[ $TARGET_UART_PORT -ge 0 ]] && [[ $TARGET_UART_PORT -lt $NUM_OF_IT87_UART ]]; then
                UART_PORT_TBL="UART_IT87_GPIO_TBL_PORT${TARGET_UART_PORT}[@]"
                UART_PORT_TBL=("${!UART_PORT_TBL}")

                if [[ -n $TARGET_UART_MODE ]]; then
                        UART_MODE_TBL="UART_IT87_GPIO_VAL_TBL_MODE${TARGET_UART_MODE}[@]"
                        UART_MODE_TBL=("${!UART_MODE_TBL}")
                fi

                UART_MODE_STR=("${UART_MODE_STR1[@]}")
        elif [[ $TARGET_UART_PORT -ge $NUM_OF_IT87_UART ]] && [[ $TARGET_UART_PORT -le $NUM_OF_UART ]]; then
                if [[ $NUM_OF_UART -eq 5 ]]; then
                        UART_PORT_TBL=("${UART_IO_BOARD_3_PORT_TBL[@]}")
                elif [[ $NUM_OF_UART -eq 10 ]]; then
                        UART_PORT_TBL=("${UART_IO_BOARD_8_PORT_TBL[@]}")
                fi

                UART_MODE_TBL=("${UART_IO_BOARD_MODE_TBL[@]}")
                UART_MODE_STR=("${UART_MODE_STR2[@]}")
        fi
}

function RKPA110::profile() {
        RKP110_series_profile
}

function RKPA110::init() {
        RKP110_series_init
}

function RKPA110::get_uart_mode() {
        get_uart_mode_it87_and_mxuport "$@"
}

function RKPA110::set_uart_mode() {
        set_uart_mode_it87_and_mxuport "$@"
}

function RKPC110::profile() {
        RKP110_series_profile
}

function RKPC110::init() {
        RKP110_series_init
}

function RKPC110::get_uart_mode() {
        get_uart_mode_it87_and_mxuport "$@"
}

function RKPC110::set_uart_mode() {
        set_uart_mode_it87_and_mxuport "$@"
}

function RKP110_series_profile() {
        IO_BOARD_GPIO_TBL=(50 47 63)
        # Refer to main circuit diagram P.57
        IO_BOARD_4_COM_2_LAN_GPIO=(0 0 1)
        IO_BOARD_8_COM_GPIO=(0 1 1)

        UART_PORT_TBL=""
        UART_IT87_GPIO_TBL_PORT0=(13 11 12 1)
        UART_IT87_GPIO_TBL_PORT1=(16 14 15 2)
        NUM_OF_IT87_UART=2
        UART_IO_BOARD_4_PORT_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2" "ttyUSB3")
        NUM_OF_IO_BOARD_4_PORT_UART=${#UART_IO_BOARD_4_PORT_TBL[@]}
        UART_IO_BOARD_8_PORT_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2" "ttyUSB3" "ttyUSB4" "ttyUSB5" "ttyUSB6" "ttyUSB7")
        NUM_OF_IO_BOARD_8_PORT_UART=${#UART_IO_BOARD_8_PORT_TBL[@]}

        UART_MODE_TBL=""
        UART_IT87_GPIO_VAL_TBL_MODE0=(1 0 0 0)
        UART_IT87_GPIO_VAL_TBL_MODE1=(0 1 0 1)
        UART_IT87_GPIO_VAL_TBL_MODE2=(0 0 1 1)
        UART_IT87_GPIO_VAL_TBL_MODE3=(0 0 1 1)
        UART_IO_BOARD_MODE_TBL=("0x0000" "0x0001" "0x0002" "0x0003")

        UART_MODE_STR=""
        UART_MODE_STR1=("RS-232" "RS-485-2W" "RS-422/RS-485-4W")
        UART_MODE_STR2=("RS-232" "RS-485-2W" "RS-422" "RS-485-4W")
}

function RKP110_series_init() {
        local io_board_gpio_pins
        local io_board_gpio_val
        local gpc_name
        local ret

        ret=0

        if ! is_module_loaded gpio_it87; then
                echo "gpio_it87 driver is not loaded"
                exit 1
        fi

        if [[ "${OS_ID}" == "centos" ]]; then
                io_board_gpio_pins[0]=$(gpc_it8786 ${IO_BOARD_GPIO_TBL[0]})
                [[ $? -ne 0 ]] && ret=1
                io_board_gpio_pins[1]=$(gpc_it8786 ${IO_BOARD_GPIO_TBL[1]})
                [[ $? -ne 0 ]] && ret=1
                io_board_gpio_pins[2]=$(gpc_it8786 ${IO_BOARD_GPIO_TBL[2]})
                [[ $? -ne 0 ]] && ret=1

                if [[ $ret -ne 0 ]]; then
                        echo "Initialization Failed"
                        exit $ret
                fi

                io_board_gpio_val[0]=$(gpio_get_value_sysfs ${io_board_gpio_pins[0]})
                [[ $? -ne 0 ]] && ret=2
                io_board_gpio_val[1]=$(gpio_get_value_sysfs ${io_board_gpio_pins[1]})
                [[ $? -ne 0 ]] && ret=2
                io_board_gpio_val[2]=$(gpio_get_value_sysfs ${io_board_gpio_pins[2]})
                [[ $? -ne 0 ]] && ret=2

                if [[ $ret -ne 0 ]]; then
                        echo "Initialization Failed"
                        exit $ret
                fi
        else
                io_board_gpio_pins=(
                        $(gpc_it8786_remap ${IO_BOARD_GPIO_TBL[0]})
                        $(gpc_it8786_remap ${IO_BOARD_GPIO_TBL[1]})
                        $(gpc_it8786_remap ${IO_BOARD_GPIO_TBL[2]})
                )

                gpc_name=$(gpio_get_gpiochip_name gpio_it87)
                [[ $? -ne 0 ]] && ret=1
                io_board_gpio_val[0]=$(gpio_get_value_libgpiod ${io_board_gpio_pins[0]} $gpc_name)
                [[ $? -ne 0 ]] && ret=1
                io_board_gpio_val[1]=$(gpio_get_value_libgpiod ${io_board_gpio_pins[1]} $gpc_name)
                [[ $? -ne 0 ]] && ret=1
                io_board_gpio_val[2]=$(gpio_get_value_libgpiod ${io_board_gpio_pins[2]} $gpc_name)
                [[ $? -ne 0 ]] && ret=1

                if [[ $ret -ne 0 ]]; then
                        echo "Initialization Failed"
                        exit $ret
                fi
        fi

        # Determine number of uart
        NUM_OF_UART=$((NUM_OF_UART += NUM_OF_IT87_UART))

        if [[ "${IO_BOARD_4_COM_2_LAN_GPIO[*]}" == "${io_board_gpio_val[*]}" ]]; then
                NUM_OF_UART=$((NUM_OF_UART += NUM_OF_IO_BOARD_4_PORT_UART))
        elif [[ "${IO_BOARD_8_COM_GPIO[*]}" == "${io_board_gpio_val[*]}" ]]; then
                NUM_OF_UART=$((NUM_OF_UART += NUM_OF_IO_BOARD_8_PORT_UART))
        fi

        # Determine uart port and uart mode table
        if [[ $TARGET_UART_PORT -ge 0 ]] && [[ $TARGET_UART_PORT -lt $NUM_OF_IT87_UART ]]; then
                UART_PORT_TBL="UART_IT87_GPIO_TBL_PORT${TARGET_UART_PORT}[@]"
                UART_PORT_TBL=("${!UART_PORT_TBL}")

                if [[ -n $TARGET_UART_MODE ]]; then
                        UART_MODE_TBL="UART_IT87_GPIO_VAL_TBL_MODE${TARGET_UART_MODE}[@]"
                        UART_MODE_TBL=("${!UART_MODE_TBL}")
                fi

                UART_MODE_STR=("${UART_MODE_STR1[@]}")
        elif [[ $TARGET_UART_PORT -ge $NUM_OF_IT87_UART ]] && [[ $TARGET_UART_PORT -le $NUM_OF_UART ]]; then
                if [[ $NUM_OF_UART -eq 6 ]]; then
                        UART_PORT_TBL=("${UART_IO_BOARD_4_PORT_TBL[@]}")
                elif [[ $NUM_OF_UART -eq 10 ]]; then
                        UART_PORT_TBL=("${UART_IO_BOARD_8_PORT_TBL[@]}")
                fi

                UART_MODE_TBL=("${UART_IO_BOARD_MODE_TBL[@]}")
                UART_MODE_STR=("${UART_MODE_STR2[@]}")
        fi
}

function get_uart_mode_it87_and_mxuport() {
        local port

        port=${1}

        if [[ $port -ge 0 ]] && [[ $port -le 1 ]]; then
                get_uart_mode_it87
        elif [[ $port -ge 2 ]] && [[ $port -le $NUM_OF_UART ]]; then
                port=$((port -= NUM_OF_IT87_UART))
                get_uart_mode_it87_mxuport $port
        fi
}

function set_uart_mode_it87_and_mxuport() {
        local port
        local mode

        port=${1}
        mode=${2}

        if [[ $port -ge 0 ]] && [[ $port -lt $NUM_OF_IT87_UART ]]; then
                set_uart_mode_it87
        elif [[ $port -ge $NUM_OF_IT87_UART ]] && [[ $port -le $NUM_OF_UART ]]; then
                port=$((port -= NUM_OF_IT87_UART))
                set_uart_mode_it87_mxuport $port $mode
        fi
}

function get_uart_mode_it87() {
        if [[ "${OS_ID}" == "centos" ]]; then
                get_uart_mode_it87_via_sysfs
        else
                get_uart_mode_it87_via_libgpiod
        fi
}

function get_uart_mode_it87_mxuport() {
        local port

        port=${1}

        if ! is_module_loaded mxuport; then
                echo "mxuport driver is not loaded"
                exit 1
        fi

        get_uart_mode_via_setserial $port
}

function set_uart_mode_it87() {
        if [[ "${OS_ID}" == "centos" ]]; then
                set_uart_mode_it87_via_sysfs
        else
                set_uart_mode_it87_via_libgpiod
        fi
}

function set_uart_mode_it87_mxuport() {
        local port
        local mode

        port=${1}
        mode=${2}

        if ! is_module_loaded mxuport; then
                echo "mxuport driver is not loaded"
                exit 1
        fi

        set_uart_mode_via_setserial $port $mode
}

function get_uart_mode_it87_via_sysfs() {
        local sio_gpio_pins
        local sio_values
        local mode_value_tbl
        local ret

        ret=0

        if ! is_module_loaded it87_serial; then
                echo "it87_serial driver is not loaded"
                exit 1
        fi

        sio_gpio_pins[0]=$(gpc_it8786 ${UART_PORT_TBL[0]})
        [[ $? -ne 0 ]] && ret=1
        sio_gpio_pins[1]=$(gpc_it8786 ${UART_PORT_TBL[1]})
        [[ $? -ne 0 ]] && ret=1
        sio_gpio_pins[2]=$(gpc_it8786 ${UART_PORT_TBL[2]})
        [[ $? -ne 0 ]] && ret=1

        if [[ $ret -ne 0 ]]; then
                echo "Get Mode Failed"
                exit $ret
        fi

        sio_values[0]=$(gpio_get_value_sysfs ${sio_gpio_pins[0]})
        [[ $? -ne 0 ]] && ret=2
        sio_values[1]=$(gpio_get_value_sysfs ${sio_gpio_pins[1]})
        [[ $? -ne 0 ]] && ret=2
        sio_values[2]=$(gpio_get_value_sysfs ${sio_gpio_pins[2]})
        [[ $? -ne 0 ]] && ret=2
        sio_values[3]=$(cat /sys/class/misc/it87_serial/serial${UART_PORT_TBL[3]}/serial${UART_PORT_TBL[3]}_rs485 2>/dev/null)
        [[ $? -ne 0 ]] && ret=2

        if [[ $ret -ne 0 ]]; then
                echo "Get Mode Failed"
                exit $ret
        fi

        for ((i = 0; i < $NUM_OF_UART_MODE; ++i)); do
                mode_value_tbl="UART_IT87_GPIO_VAL_TBL_MODE${i}[@]"
                mode_value_tbl=("${!mode_value_tbl}")
                if [[ "${sio_values[*]}" == "${mode_value_tbl[*]}" ]]; then
                        echo "${UART_MODE_STR[$i]}"
                        return 0
                fi
        done

        echo "Unknown Mode"
        return 1
}

function get_uart_mode_it87_via_libgpiod() {
        local gpc_name
        local sio_gpio_pins
        local sio_values
        local mode_value_tbl
        local ret

        ret=0

        if ! is_module_loaded gpio_it87; then
                echo "gpio_it87 driver is not loaded"
                exit 1
        fi

        if ! is_module_loaded it87_serial; then
                echo "it87_serial driver is not loaded"
                exit 1
        fi

        gpc_name=$(gpio_get_gpiochip_name gpio_it87)

        sio_gpio_pins=(
                $(gpc_it8786_remap ${UART_PORT_TBL[0]})
                $(gpc_it8786_remap ${UART_PORT_TBL[1]})
                $(gpc_it8786_remap ${UART_PORT_TBL[2]})
        )

        sio_values[0]=$(gpio_get_value_libgpiod ${sio_gpio_pins[0]} $gpc_name)
        [[ $? -ne 0 ]] && ret=1
        sio_values[1]=$(gpio_get_value_libgpiod ${sio_gpio_pins[1]} $gpc_name)
        [[ $? -ne 0 ]] && ret=1
        sio_values[2]=$(gpio_get_value_libgpiod ${sio_gpio_pins[2]} $gpc_name)
        [[ $? -ne 0 ]] && ret=1
        sio_values[3]=$(cat /sys/class/misc/it87_serial/serial${UART_PORT_TBL[3]}/serial${UART_PORT_TBL[3]}_rs485 2>/dev/null)
        [[ $? -ne 0 ]] && ret=1

        if [[ $ret -ne 0 ]]; then
                echo "Get Mode Failed"
                exit $ret
        fi

        for ((i = 0; i < $NUM_OF_UART_MODE; ++i)); do
                mode_value_tbl="UART_IT87_GPIO_VAL_TBL_MODE${i}[@]"
                mode_value_tbl=("${!mode_value_tbl}")
                if [[ "${sio_values[*]}" == "${mode_value_tbl[*]}" ]]; then
                        echo "${UART_MODE_STR[$i]}"
                        return 0
                fi
        done

        echo "Unknown Mode"
        return 1
}

function get_uart_mode_via_setserial() {
        local port
        local port_name
        local pattern
        local mode_value_tbl
        local ret

        ret=0

        if [[ ! -e "$SETSETIAL" ]]; then
                echo "setserial is not installed"
                exit 1
        fi

        port=${1}
        port_name=${UART_PORT_TBL[$port]}
        pattern='port (0x[0-9]+)'
        result=$($SETSETIAL -G "/dev/$port_name")
        [[ $? -ne 0 ]] && ret=1

        if [[ $ret -ne 0 ]]; then
                echo "Get Mode Failed"
                exit $ret
        fi

        if [[ "$result" =~ $pattern ]]; then
                for ((i = 0; i < $NUM_OF_UART_MODE; ++i)); do
                        if [[ "${BASH_REMATCH[1]}" == "${UART_MODE_TBL[$i]}" ]]; then
                                echo "${UART_MODE_STR[$i]}"
                                return 0
                        fi
                done
        fi

        echo "Unknown"
        return 1
}

function set_uart_mode_it87_via_sysfs() {
        local sio_pins
        local sio_values
        local ret

        ret=0

        sio_pins[0]=$(gpc_it8786 ${UART_PORT_TBL[0]})
        [[ $? -ne 0 ]] && ret=1
        sio_pins[1]=$(gpc_it8786 ${UART_PORT_TBL[1]})
        [[ $? -ne 0 ]] && ret=1
        sio_pins[2]=$(gpc_it8786 ${UART_PORT_TBL[2]})
        [[ $? -ne 0 ]] && ret=1
        sio_pins[3]="/sys/class/misc/it87_serial/serial${UART_PORT_TBL[3]}/serial${UART_PORT_TBL[3]}_rs485"

        if [[ $ret -ne 0 ]]; then
                echo "Set Mode Failed"
                exit $ret
        fi

        gpio_set_value_sysfs ${sio_pins[0]} ${UART_MODE_TBL[0]}
        [[ $? -ne 0 ]] && ret=2
        gpio_set_value_sysfs ${sio_pins[1]} ${UART_MODE_TBL[1]}
        [[ $? -ne 0 ]] && ret=2
        gpio_set_value_sysfs ${sio_pins[2]} ${UART_MODE_TBL[2]}
        [[ $? -ne 0 ]] && ret=2
        echo "${UART_MODE_TBL[3]}" 2>/dev/null >"${sio_pins[3]}"
        [[ $? -ne 0 ]] && ret=2

        if [[ $ret -ne 0 ]]; then
                echo "Set Mode Failed"
                exit $ret
        fi
}

function set_uart_mode_it87_via_libgpiod() {
        local gpc_name
        local sio_pins
        local sio_values
        local ret

        ret=0

        if ! is_module_loaded gpio_it87; then
                echo "gpio_it87 driver is not loaded"
                exit 1
        fi

        if ! is_module_loaded it87_serial; then
                echo "it87_serial driver is not loaded"
                exit 1
        fi

        sio_pins=(
                $(gpc_it8786_remap ${UART_PORT_TBL[0]})
                $(gpc_it8786_remap ${UART_PORT_TBL[1]})
                $(gpc_it8786_remap ${UART_PORT_TBL[2]})
                "/sys/class/misc/it87_serial/serial${UART_PORT_TBL[3]}/serial${UART_PORT_TBL[3]}_rs485"
        )

        gpc_name=$(gpio_get_gpiochip_name gpio_it87)
        [[ $? -ne 0 ]] && ret=1
        gpio_set_value_libgpiod ${sio_pins[0]} ${UART_MODE_TBL[0]} $gpc_name
        [[ $? -ne 0 ]] && ret=1
        gpio_set_value_libgpiod ${sio_pins[1]} ${UART_MODE_TBL[1]} $gpc_name
        [[ $? -ne 0 ]] && ret=1
        gpio_set_value_libgpiod ${sio_pins[2]} ${UART_MODE_TBL[2]} $gpc_name
        [[ $? -ne 0 ]] && ret=1
        echo "${UART_MODE_TBL[3]}" 2>/dev/null >"${sio_pins[3]}"
        [[ $? -ne 0 ]] && ret=1

        if [[ $ret -ne 0 ]]; then
                echo "Set Mode Failed"
                exit $ret
        fi
}

function set_uart_mode_via_setserial() {
        local port
        local port_name
        local mode
        local ret

        ret=0

        if [[ ! -e "$SETSETIAL" ]]; then
                echo "setserial is not installed"
                exit 1
        fi

        port=${1}
        port_name=${UART_PORT_TBL[$port]}
        mode=${2}

        $SETSETIAL "/dev/$port_name" port "${UART_MODE_TBL[$mode]}" >/dev/null 2>&1
        [[ $? -ne 0 ]] && ret=1

        if [[ $ret -ne 0 ]]; then
                echo "Get Mode Failed"
                exit $ret
        fi
}

function load_model_name() {
        for name in $(get_model_name_from_dmi_type12); do
                if [[ "$(type -t "${name}::profile")" = 'function' ]]; then
                        MODEL_NAME="${name}"
                        break
                fi
        done

        if [[ -z "${MODEL_NAME}" ]]; then
                for name in $(get_model_name_from_dmi_type1); do
                        if [[ "$(type -t "${name}::profile")" = 'function' ]]; then
                                MODEL_NAME="${name}"
                                break
                        fi
                done
        fi

        if [[ -z "${MODEL_NAME}" ]]; then
                echo "Unsupported model"
                exit 38
        fi
}

function script_usage() {
        cat <<EOF
USAGE:
	mx-uart-ctl -p <port> [-m <mode>]

OPTIONS:
	-p <port>
		(Required) Determine uart port. Without option '-m', it will get uart mode

	-m <uart_mode>
                (Optional) Set uart mode
			0: RS-232
			1: RS-485-2W
			2: RS-422
                        3: RS-485-4W

EXAMPLE:
	Get port 0 mode
	mx-uart-ctl -p 0

	Set port 1 to mode RS-232
	mx-uart-ctl -p 1 -m 0

EOF
}

function script_params() {
        if [[ $# -eq 0 ]]; then
                script_usage
                exit 1
        fi

        while getopts "hp:m:" opt; do
                case "${opt}" in
                p)
                        TARGET_UART_PORT="$OPTARG"
                        TARGET_OPCODE=$((TARGET_OPCODE += 1))
                        ;;

                m)
                        TARGET_UART_MODE="$OPTARG"
                        TARGET_OPCODE=$((TARGET_OPCODE += 2))
                        ;;
                h)
                        script_usage
                        exit 0
                        ;;
                \?)
                        script_usage
                        exit 22
                        ;;
                :)
                        echo "Option -$OPTARG requires an argument." >&2
                        script_usage
                        exit 22
                        ;;
                esac
        done
}

function script_init() {
        load_model_name

        if [[ ! $(type -t "${MODEL_NAME}"::profile) == function ]]; then
                echo "${MODEL_NAME} profile function is not define"
                exit 1
        fi

        "${MODEL_NAME}"::profile

        if [[ ! $(type -t "${MODEL_NAME}"::init) == function ]]; then
                echo "${MODEL_NAME} init function is not define"
                exit 1
        fi

        "${MODEL_NAME}"::init
}

function verify_uart_port() {
        if ! check_leading_zero_digit $TARGET_UART_PORT; then
                echo "Invaild uart port format"
                exit 1
        fi

        if [[ $TARGET_UART_PORT -lt 0 || $TARGET_UART_PORT -ge $NUM_OF_UART ]]; then
                echo "Invaild uart port"
                exit 1
        fi
}

function verift_uart_mode() {
        if ! check_leading_zero_digit $TARGET_UART_MODE; then
                echo "Invaild uart port format"
                exit 1
        fi

        if [[ $TARGET_UART_MODE -lt 0 || $TARGET_UART_MODE -ge $NUM_OF_UART_MODE ]]; then
                echo "Invaild uart mode"
                exit 1
        fi
}

function main() {
        script_params "$@"
        script_init

        verify_uart_port

        case $TARGET_OPCODE in
        $TARGET_GET_MODE_OPCODE)
                if [[ ! $(type -t "${MODEL_NAME}"::get_uart_mode) == function ]]; then
                        echo "${MODEL_NAME} get_uart_mode function is not define"
                        exit 1
                fi

                "${MODEL_NAME}"::get_uart_mode $TARGET_UART_PORT
                ;;
        $TARGET_SET_MODE_OPCODE)
                verift_uart_mode

                if [[ ! $(type -t "${MODEL_NAME}"::set_uart_mode) == function ]]; then
                        echo "${MODEL_NAME} set_uart_mode function is not define"
                        exit 1
                fi

                "${MODEL_NAME}"::set_uart_mode $TARGET_UART_PORT $TARGET_UART_MODE
                ;;
        $TARGET_INVALID_OPCODE)
                echo "Require -p argument for port number"
                script_usage
                exit 1
                ;;
        *)
                script_usage
                exit 1
                ;;
        esac

        exit 0
}

main $@
