#!/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>
#	2024    Henry LC Chen <HenryLC.Chen@moxa.com>
# Description:
#	For controlling gpio-sysfs value from Relay mode
#

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

# Set GPIO Low = Normal Closed
# Set GPIO High = Normal Open
RELAY_MODE_STR=("NC" "NO")
MODEL_NAME=""
TARGET_RELAY_PORT=""
TARGET_RELAY_MODE=0
TARGET_OPCODE=0
TARGET_GET_RELAY_MODE_OPCODE=1
TARGET_SET_RELAY_MODE_OPCODE=3
IPMI_GET_OPT=0
IPMI_SET_OPT=1

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

function DA920E::profile() {
	# use BMC kcs to control relay
	# spv_ipmi raw 0x3E 0xC0 0x06 0xF8 [get(0)/set(1)] [status: 0(low)|1(high)]
	# 'spv_ipmi' means Insyde BMC tool
	# Notice:
	# The DA-920E relay contacts can be wired or controlled in different ways
	# Un-powered (Dry Contact), Powered (Wet Contact), and Event-Triggered
	# Thus only to provide low and high two state information to user
	# e.g.
	# set relay to high -> ipmitool raw 0x3E 0xC0 0x06 0xF8 1 1
	# set relay to low -> ipmitool raw 0x3E 0xC0 0x06 0xF8 1 0
	# get relay to low -> ipmitool raw 0x3E 0xC0 0x06 0xF8 0
	IPMI_CMD="spv_ipmi"
	IPMI_OPT="-I open raw"
	IPMI_RAW="0x3E 0xC0 0x06 0xF8"
}

function DA920E::init() {
	RELAY_MODE_STR=("low" "high")
	NUM_OF_RELAY=1
	RELAY_MODE_TBL=(0 1)
	NUM_OF_RELAY_MODE=${#RELAY_MODE_TBL[@]}
}

function DA920E::get_relay_state() {
	get_relay_state_ipmi "$1"
}

function DA920E::set_relay_state() {
	set_relay_state_ipmi "$1" "$2"
}

function DA820E::profile() {
	RELAY_IT87_GPIO_TBL=(47)
	RELAY_MODE_TBL=(0 1)
}

function DA820E::init() {
	NUM_OF_RELAY=${#RELAY_IT87_GPIO_TBL[@]}
	NUM_OF_RELAY_MODE=${#RELAY_MODE_TBL[@]}
}

function DA820E::get_relay_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		get_relay_mode_it87_via_sysfs "$1"
	else
		get_relay_mode_it87_via_libgpiod "$1"
	fi
}

function DA820E::set_relay_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		set_relay_mode_it87_via_sysfs "$1" "$2"
	else
		set_relay_mode_it87_via_libgpiod "$1" "$2"
	fi
}

function DA820C::profile() {
	RELAY_IT87_GPIO_TBL=(65)
	RELAY_MODE_TBL=(0 1)
}

function DA820C::init() {
	NUM_OF_RELAY=${#RELAY_IT87_GPIO_TBL[@]}
	NUM_OF_RELAY_MODE=${#RELAY_MODE_TBL[@]}
}

function DA820C::get_relay_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		get_relay_mode_it87_via_sysfs "$1"
	else
		get_relay_mode_it87_via_libgpiod "$1"
	fi
}

function DA820C::set_relay_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		set_relay_mode_it87_via_sysfs "$1" "$2"
	else
		set_relay_mode_it87_via_libgpiod "$1" "$2"
	fi
}

function DA682C::profile() {
	RELAY_IT87_GPIO_TBL=(65)
	RELAY_MODE_TBL=(0 1)
}

function DA682C::init() {
	NUM_OF_RELAY=${#RELAY_IT87_GPIO_TBL[@]}
	NUM_OF_RELAY_MODE=${#RELAY_MODE_TBL[@]}
}

function DA682C::get_relay_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		get_relay_mode_it87_via_sysfs "$1"
	else
		get_relay_mode_it87_via_libgpiod "$1"
	fi
}

function DA682C::set_relay_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		set_relay_mode_it87_via_sysfs "$1" "$2"
	else
		set_relay_mode_it87_via_libgpiod "$1" "$2"
	fi
}

function DA681C::profile() {
	RELAY_IT87_GPIO_TBL=(65)
	RELAY_MODE_TBL=(0 1)
}

function DA681C::init() {
	NUM_OF_RELAY=${#RELAY_IT87_GPIO_TBL[@]}
	NUM_OF_RELAY_MODE=${#RELAY_MODE_TBL[@]}
}

function DA681C::get_relay_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		get_relay_mode_it87_via_sysfs "$1"
	else
		get_relay_mode_it87_via_libgpiod "$1"
	fi
}

function DA681C::set_relay_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		set_relay_mode_it87_via_sysfs "$1" "$2"
	else
		set_relay_mode_it87_via_libgpiod "$1" "$2"
	fi
}

function DA680::profile() {
	RELAY_IT87_GPIO_TBL=(65)
	RELAY_MODE_TBL=(0 1)
}

function DA680::init() {
	NUM_OF_RELAY=${#RELAY_IT87_GPIO_TBL[@]}
	NUM_OF_RELAY_MODE=${#RELAY_MODE_TBL[@]}
}

function DA680::get_relay_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		get_relay_mode_it87_via_sysfs "$1"
	else
		get_relay_mode_it87_via_libgpiod "$1"
	fi
}

function DA680::set_relay_state() {
	if [[ "${OS_ID}" == "centos" ]]; then
		set_relay_mode_it87_via_sysfs "$1" "$2"
	else
		set_relay_mode_it87_via_libgpiod "$1" "$2"
	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
}

## relay
function get_relay_state_ipmi() {
	local idx=$1
	local state
	local ret

	ret=$(${IPMI_CMD} ${IPMI_OPT} ${IPMI_RAW} ${IPMI_GET_OPT})
	[[ $? -ne 0 ]] && exit 1

	# convert ipmi return string '00/01' to '0/1'
	state="$((ret))"

	echo "Current relay state is ${RELAY_MODE_STR[state]}"
}

function set_relay_state_ipmi() {
	local idx=$1
	local state=$2
	local ret
	local ret_state

	ret="$(${IPMI_CMD} ${IPMI_OPT} ${IPMI_RAW} ${IPMI_SET_OPT} ${state})"
	[[ $? -ne 0 ]] && exit 1

	# convert ipmi return string '00/01' to '0/1'
	ret_state="$((ret))"
	if [[ "${ret_state}" == "0" || "${ret_state}" == "1" ]]; then
		echo "Set OK."
	else
		echo "Set State Failed"
		exit 1
	fi
}

function get_relay_mode_it87_via_libgpiod() {
	local idx=$1
	local num=${RELAY_IT87_GPIO_TBL[$idx]}
	local gpc_name

	gpc_name=$(gpio_get_gpiochip_name gpio_it87)
	local mode=$(gpio_get_value_libgpiod $(gpc_it8786_remap $num) $gpc_name)

	echo "Current relay mode is ${RELAY_MODE_STR[mode]} interface."
}

function set_relay_mode_it87_via_libgpiod() {
	local idx=$1
	local mode=$2
	local num=${RELAY_IT87_GPIO_TBL[$idx]}
	local gpc_name

	gpc_name=$(gpio_get_gpiochip_name gpio_it87)
	gpio_set_value_libgpiod $(gpc_it8786_remap $num) $mode $gpc_name

	echo "Set OK."
}

function get_relay_mode_it87_via_sysfs() {
	local idx=$1
	local num=${RELAY_IT87_GPIO_TBL[$idx]}
	local mode=$(gpio_get_value_sysfs $(gpc_it8786 $num))

	echo "Current relay mode is ${RELAY_MODE_STR[mode]} interface."
}

function set_relay_mode_it87_via_sysfs() {
	local idx=$1
	local state=$2
	local num=${RELAY_IT87_GPIO_TBL[$idx]}

	gpio_set_value_sysfs $(gpc_it8786 $num) $state

	echo "Set OK."
}

function script_usage() {
cat << EOF
Usage:
		mx-relay-ctl -p <port_number> [-m <relay_mode>]

OPTIONS:
		-p <port_number>
				Set target port.
		-m <relay_mode>
				Set target port to relay_mode
				0 --> set to NC (Normal Closed) mode | Low state
				1 --> set to NO (Normal Open) mode | High state

Example:
		Get mode from port 0
		# mx-relay-ctl -p 0

		Set port 0 to mode NC | Low state
		# mx-relay-ctl -p 0 -m 0

		Set port 0 to mode NO | High state
		# mx-relay-ctl -p 0 -m 1
EOF
}

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

        while getopts "hp:m:" opt; do
                case "${opt}" in
                p)
                        TARGET_RELAY_PORT="$OPTARG"
                        TARGET_OPCODE=$((TARGET_OPCODE += 1))
                        ;;
                m)
                        TARGET_RELAY_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_relay_port() {
        if ! check_leading_zero_digit $TARGET_RELAY_PORT; then
                echo "Invaild relay port format."
                exit 1
        fi

        if [[ $TARGET_RELAY_PORT -lt 0 || $TARGET_RELAY_PORT -ge $NUM_OF_RELAY ]]; then
                echo "Invalid relay port."
                exit 1
        fi
}

function verify_mode() {
        if ! check_leading_zero_digit $TARGET_RELAY_MODE; then
                echo "Invalid relay mode format."
                exit 1
        fi

        if [[ $TARGET_RELAY_MODE -lt 0 || $TARGET_RELAY_MODE -ge $NUM_OF_RELAY_MODE ]]; then
                echo "Invalid relay mode."
                exit 1
        fi
}

function main() {
        script_params "$@"
        script_init

        case $TARGET_OPCODE in
        $TARGET_GET_RELAY_MODE_OPCODE)
                verify_relay_port
		verify_mode

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

                "${MODEL_NAME}"::get_relay_state $TARGET_RELAY_PORT
                ;;
	$TARGET_SET_RELAY_MODE_OPCODE)
		verify_relay_port
		verify_mode

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

                "${MODEL_NAME}"::set_relay_state $TARGET_RELAY_PORT $TARGET_RELAY_MODE
                "${MODEL_NAME}"::get_relay_state $TARGET_RELAY_PORT
                ;;
        *)
                script_usage
                exit 1
                ;;
        esac
}

main $@
