#!/bin/bash
#
# Copyright (C) 2023 MOXA Inc. All rights reserved.
# This software is distributed under the terms of the MOXA SOFTWARE NOTICE.
# See the file LICENSE for details.
#
# Authors:
#	2022	Elvis Yao <ElvisCW.Yao@moxa.com>
#	2022    Wilson Huang <WilsonYS.Huang@moxa.com>
#	2024    Henry LC Chen <HenryLC.Chen@moxa.com>
# Description:
#	For controlling gpio-sysfs value from Super IO LED
#

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

MODEL_NAME=""
LED_STATE=("on" "off")
NUM_OF_LED=""
NUM_OF_LED_STATE=${#LED_STATE[@]}
TARGET_LED_PORT=""
TARGET_STATE=0
TARGET_OPCODE=0
TARGET_GET_LED_STATE_OPCODE=1
TARGET_SET_LED_STATE_OPCODE=3

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

function DA820E::profile() {
	LED_IT87_GPIO_TBL=(70 71 72 73 74 75 76 77)
}

function DA820E::init() {
	NUM_OF_LED=${#LED_IT87_GPIO_TBL[@]}
}

function DA820E::get_led_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		get_led_state_it87_sysfs "$1"
	else
		get_led_state_libgpiod "$1"
	fi
}

function DA820E::set_led_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		set_led_state_it87_sysfs "$1" "$2"
	else
		set_led_state_libgpiod "$1" "$2"
	fi
}

function DA820C::profile() {
	LED_IT87_GPIO_TBL=(70 71 72 73 74 75 76 77)
}

function DA820C::init() {
	NUM_OF_LED=${#LED_IT87_GPIO_TBL[@]}
}

function DA820C::get_led_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		get_led_state_it87_sysfs "$1"
	else
		get_led_state_libgpiod "$1"
	fi
}

function DA820C::set_led_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		set_led_state_it87_sysfs "$1" "$2"
	else
		set_led_state_libgpiod "$1" "$2"
	fi
}

function DA682C::profile() {
	LED_IT87_GPIO_TBL=(70 71 72 73 74 75 76 77)
}

function DA682C::init() {
	NUM_OF_LED=${#LED_IT87_GPIO_TBL[@]}
}

function DA682C::get_led_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		get_led_state_it87_sysfs "$1"
	else
		get_led_state_libgpiod "$1"
	fi
}

function DA682C::set_led_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		set_led_state_it87_sysfs "$1" "$2"
	else
		set_led_state_libgpiod "$1" "$2"
	fi
}

function DA681C::profile() {
	LED_IT87_GPIO_TBL=(70 71 72 73 74 75 76 77)
}

function DA681C::init() {
	NUM_OF_LED=${#LED_IT87_GPIO_TBL[@]}
}

function DA681C::get_led_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		get_led_state_it87_sysfs "$1"
	else
		get_led_state_libgpiod "$1"
	fi
}

function DA681C::set_led_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		set_led_state_it87_sysfs "$1" "$2"
	else
		set_led_state_libgpiod "$1" "$2"
	fi
}

function DA680::profile() {
	LED_IT87_GPIO_TBL=(70 71 72 73 74 75 76 77)
}

function DA680::init() {
	NUM_OF_LED=${#LED_IT87_GPIO_TBL[@]}
}

function DA680::get_led_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		get_led_state_it87_sysfs "$1"
	else
		get_led_state_libgpiod "$1"
	fi
}

function DA680::set_led_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		set_led_state_it87_sysfs "$1" "$2"
	else
		set_led_state_libgpiod "$1" "$2"
	fi
}

function V3000::profile() {
	LED_IT87_GPIO_TBL=(77 76 75)
}

function V3000::init() {
	NUM_OF_LED=${#LED_IT87_GPIO_TBL[@]}
}

function V3000::get_led_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		get_led_state_it87_sysfs "$1"
	else
		get_led_state_libgpiod "$1"
	fi
}

function V3000::set_led_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		set_led_state_it87_sysfs "$1" "$2"
	else
		set_led_state_libgpiod "$1" "$2"
	fi
}

function V3400::profile() {
	LED_IT87_GPIO_TBL=(77 76 75)
	LED_PCA953X_GPIO_TBL=(0 1 2 3 4 5 6 7 8 9 10 11)
        PCA953X_CHIP_ADDRESS="0x25"
}

function V3400::init() {
        local ret=0
        local cp2112_module_num_str=$(get_i2c_device_minor_number_list "CP2112")
        local cp2112_module_num_array=($cp2112_module_num_str)
        local cp2112_module_num=${#cp2112_module_num_array[@]}

        NUM_OF_IT87_LED=${#LED_IT87_GPIO_TBL[@]}
        NUM_OF_PCA953X_LED=${#LED_PCA953X_GPIO_TBL[@]}
        NUM_OF_LED=$((NUM_OF_IT87_LED + NUM_OF_PCA953X_LED))

        ret=$(bind_i2c_driver "CP2112" "pca9535" $PCA953X_CHIP_ADDRESS)
        if [[ $ret -ne 0 ]]; then
                echo "Bind i2c device fail"
                exit $ret
        fi

        PCA953X_GPIOCHIP_NAME=$(format_as_i2c_directory_name ${cp2112_module_num_array[0]} ${PCA953X_CHIP_ADDRESS})
}

function V3400::get_led_state() {
        local port
        port=${1}

        if [[ $port -ge 0 ]] && [[ $port -le 2 ]]; then
                if [[ "${OS_ID}" == "centos" ]]; then
                        get_led_state_it87_sysfs "$port"
                else
                        get_led_state_libgpiod "$port"
                fi
        elif [[ $port -ge 3 ]] && [[ $port -le $NUM_OF_LED ]]; then
                if [[ "${OS_ID}" == "centos" ]]; then
                        get_led_state_pca953x_sysfs "$PCA953X_GPIOCHIP_NAME" "$port"
                else
                        get_led_state_pca953x_libgpiod "$PCA953X_GPIOCHIP_NAME" "$port"
                fi
        fi
}

function V3400::set_led_state() {
        local port
        local state
        port=${1}
        state=${2}

        if [[ $port -ge 0 ]] && [[ $port -le 2 ]]; then
                if [[ "${OS_ID}" == "centos" ]]; then
                        set_led_state_it87_sysfs "$port" "$state"
                else
                        set_led_state_libgpiod "$port" "$state"
                fi
        elif [[ $port -ge 3 ]] && [[ $port -le $NUM_OF_LED ]]; then
                if [[ "${OS_ID}" == "centos" ]]; then
                        set_led_state_pca953x_sysfs "$PCA953X_GPIOCHIP_NAME" "$port" "$state"
                else
                        set_led_state_pca953x_libgpiod "$PCA953X_GPIOCHIP_NAME" "$port" "$state"
                fi
        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
}

## led

function get_led_state_libgpiod() {
	local idx=$1
        local sio_gpio_pin
        local gpc_name
        local ret

        sio_gpio_pin=$(gpc_it8786_remap ${LED_IT87_GPIO_TBL[$idx]})
	gpc_name=$(gpio_get_gpiochip_name gpio_it87)
	local state=$(gpio_get_value_libgpiod $sio_gpio_pin $gpc_name)

	echo "Get Led#$idx state: ${LED_STATE[$state]}"
}

function set_led_state_libgpiod() {
	local idx=$1
	local state=$2
	local check_state
        local sio_gpio_pin
        local gpc_name
        local ret

        sio_gpio_pin=$(gpc_it8786_remap ${LED_IT87_GPIO_TBL[$idx]})
	gpc_name=$(gpio_get_gpiochip_name gpio_it87)
	gpio_set_value_libgpiod $sio_gpio_pin $state $gpc_name
        [[ $? -ne 0 ]] && ret=1

        check_state=$(gpio_get_value_libgpiod $sio_gpio_pin $gpc_name)
        [[ $? -ne 0 ]] && ret=1
        if [[ $ret -ne 0 || "$state" -ne "$check_state" ]]; then
                echo "Set State Failed"
                exit $ret
        fi

	echo "Set Led#$idx state: ${LED_STATE[$state]}"
}

function get_led_state_it87_sysfs() {
	local idx=$1
	local num=${LED_IT87_GPIO_TBL[$idx]}
	local state=$(gpio_get_value_sysfs $(gpc_it8786 $num))

	echo "Get Led#$idx state: ${LED_STATE[$state]}"
}

function set_led_state_it87_sysfs() {
	local idx=$1
	local state=$2
	local num=${LED_IT87_GPIO_TBL[$idx]}

	gpio_set_value_sysfs $(gpc_it8786 $num) $state

	echo "Set Led#$idx state: ${LED_STATE[$state]}"
}

function get_led_state_pca953x_libgpiod() {
        local gpiochip_name=$1
        local idx=$2
        local sio_gpio_pin
        local gpc_name
        local ret

        idx=$((idx - NUM_OF_IT87_LED))
        pca953x_gpio_pin=${LED_PCA953X_GPIO_TBL[$idx]}
        gpc_name=$(gpio_get_gpiochip_name $gpiochip_name)
        local state=$(gpio_get_value_libgpiod $pca953x_gpio_pin $gpc_name)

        echo "Get Led#$((idx + NUM_OF_IT87_LED)) state: ${LED_STATE[$state]}"
}

function set_led_state_pca953x_libgpiod() {
        local gpiochip_name=$1
        local idx=$2
        local state=$3
        local check_state
        local sio_gpio_pin
        local gpc_name
        local ret

        idx=$((idx - NUM_OF_IT87_LED))
        pca953x_gpio_pin=${LED_PCA953X_GPIO_TBL[$idx]}
        gpc_name=$(gpio_get_gpiochip_name $gpiochip_name)
        gpio_set_value_libgpiod $pca953x_gpio_pin $state $gpc_name
        [[ $? -ne 0 ]] && ret=1

        check_state=$(gpio_get_value_libgpiod $pca953x_gpio_pin $gpc_name)
        [[ $? -ne 0 ]] && ret=1
        if [[ $ret -ne 0 || "$state" -ne "$check_state" ]]; then
                echo "Set State Failed"
                exit $ret
        fi

        echo "Set Led#$((idx + NUM_OF_IT87_LED)) state: ${LED_STATE[$state]}"
}

function get_led_state_pca953x_sysfs() {
        local gpiochip_name=$1
        local idx=$2
        local pca953x_gpio_pin
        local state

        idx=$((idx - NUM_OF_IT87_LED))
        pca953x_gpio_pin=${LED_PCA953X_GPIO_TBL[$idx]}
        state=$(gpio_get_value_sysfs $(gpc_pca953x $pca953x_gpio_pin))
        echo "Get Led#$((idx + NUM_OF_IT87_LED)) state: ${LED_STATE[$state]}"
}

function set_led_state_pca953x_sysfs() {
        local gpiochip_name=$1
        local idx=$2
        local state=$3
        local pca953x_gpio_pin

        idx=$((idx - NUM_OF_IT87_LED))
        pca953x_gpio_pin=${LED_PCA953X_GPIO_TBL[$idx]}
        gpio_set_value_sysfs $(gpc_pca953x $pca953x_gpio_pin) $state
        echo "Set Led#$((idx + NUM_OF_IT87_LED)) state: ${LED_STATE[$state]}"
}

function script_usage() {
cat << EOF
Usage:
		mx-led-ctl -i <led_index> [on|off]

OPTIONS:
		-i <led_index>
				Set LED index.

Example:
		Get state from index 1
		# mx-led-ctl -i 1

		Set index 1 to on
		# mx-led-ctl -i 1 on
EOF
}

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

        while getopts "hi:" opt; do
                case "${opt}" in
                i)
                        TARGET_LED_PORT="$OPTARG"
			TARGET_LED_SET_STATE="${arr[$((OPTIND - 1))]}"
                        TARGET_OPCODE=$((TARGET_OPCODE += 1))

			if [[ "$TARGET_LED_SET_STATE" == "on" ]]; then
				TARGET_OPCODE=$((TARGET_OPCODE += 2))
				TARGET_STATE=0
				break
			elif [[ "$TARGET_LED_SET_STATE" == "off" ]]; then
				TARGET_OPCODE=$((TARGET_OPCODE += 2))
				TARGET_STATE=1
				break
			elif [[ "$TARGET_LED_SET_STATE" == "" ]]; then
				break
			else
				echo "only accept 'on' or 'off' in set state"
				script_usage
				exit 22
			fi
                        ;;
                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_led_port() {
        if ! check_leading_zero_digit $TARGET_LED_PORT; then
                echo "Invaild LED port format."
                exit 1
        fi

        if [[ $TARGET_LED_PORT -lt 0 || $TARGET_LED_PORT -ge $NUM_OF_LED ]]; then
                echo "Invalid LED index."
                exit 1
        fi
}

function main() {
        script_params "$@"
        script_init

        case $TARGET_OPCODE in
        $TARGET_GET_LED_STATE_OPCODE)
                verify_led_port

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

                "${MODEL_NAME}"::get_led_state $TARGET_LED_PORT
                ;;
	$TARGET_SET_LED_STATE_OPCODE)
				verify_led_port

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

                "${MODEL_NAME}"::set_led_state $TARGET_LED_PORT $TARGET_STATE
                ;;
        *)
                script_usage
                exit 1
                ;;
        esac
}

main $@
