#!/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>
# Description:
#	For Controlling Panel Multi function LED from gpio-sysfs value (EXPC-F2000 series)
#

DMIDECODE="/usr/sbin/dmidecode"

## Super IO GPIO value table ##
#- LED1
#  - Disable LAN LED1
#    - GP92=0
#  - Disable UART TX LED1
#    - GP71=1, GP70=1
#  - Disable UART RX LED1
#    - GP73=1, GP72=1
#  - LED1_LAN1
#    - GP94=1, GP93=1, GP92=1
#  - LED1_LAN2
#    - GP94=0, GP93=0, GP92=1
#  - LED1_LAN3
#    - GP94=0, GP93=1, GP92=1
#  - LED1_LAN4
#    - GP94=1, GP93=0, GP92=1
#  - LED1_UART1_TX
#    - GP71=0, GP70=0
#  - LED1_UART2_TX
#    - GP71=0, GP70=1
#  - LED1_UART3_TX
#    - GP71=1, GP70=0
#  - LED1_UART1_RX
#    - GP73=0, GP72=0
#  - LED1_UART2_RX
#    - GP73=0, GP72=1
#  - LED1_UART3_RX
#    - GP73=1, GP72=0
#
#- LED2
#  - Disable LAN LED2
#    - GP95=0
#  - Disable UART TX LED2
#    - GP33=1, GP32=1
#  - Disable UART RX LED2
#    - GP50=1, GP47=1
#  - LED2_LAN1
#    - GP97=1, GP96=1, GP95=1
#  - LED2_LAN2
#    - GP97=0, GP96=0, GP95=1
#  - LED2_LAN3
#    - GP97=0, GP96=1, GP95=1
#  - LED2_LAN4
#    - GP97=1, GP96=0, GP95=1
#  - LED2_UART1_TX
#    - GP33=0, GP32=0
#  - LED2_UART2_TX
#    - GP33=0, GP32=1
#  - LED2_UART3_TX
#    - GP33=1, GP32=0
#  - LED2_UART1_RX
#    - GP50=0, GP47=0
#  - LED2_UART2_RX
#    - GP50=0, GP47=1
#  - LED2_UART3_RX
#    - GP50=1, GP47=0

_product_EXPCF2000() {
	GPIO_MODULE="gpio_it87"

	# EFI VAR value table
	EFIVAR_NAME_UUID="OemConfig-e2f7aae2-180d-4de4-a0da-41c662db8172"
	EFIVAR_UUID_NAME="e2f7aae2-180d-4de4-a0da-41c662db8172-OemConfig"
	EFIVAR_TMP_FILE="/tmp/mx-expc-led-ctl.bin"
	EFIVAR_MODE_LIST=("DISABLED" "LAN1" "LAN2" "LAN3" "LAN4" "UART1_TX" "UART2_TX" "UART3_TX" "UART1_RX" "UART2_RX" "UART3_RX")
	EFIVAR_DISABLED=0
	EFIVAR_LAN1=1
	EFIVAR_LAN2=2
	EFIVAR_LAN3=3
	EFIVAR_LAN4=4
	EFIVAR_UART1_TX=5
	EFIVAR_UART2_TX=6
	EFIVAR_UART3_TX=7
	EFIVAR_UART1_RX=8
	EFIVAR_UART2_RX=9
	EFIVAR_UART3_RX=10

	# Remove immutable bit, allows modification
	chattr -i /sys/firmware/efi/efivars/${EFIVAR_NAME_UUID}

	# LED1
	LED1_LAN_EN=92
	LED1_LAN_A1=94
	LED1_LAN_A0=93

	LED1_UART_TX_A1=71
	LED1_UART_TX_A0=70

	LED1_UART_RX_A1=73
	LED1_UART_RX_A0=72

	# LED2
	LED2_LAN_EN=95
	LED2_LAN_A1=97
	LED2_LAN_A0=96

	LED2_UART_TX_A1=33
	LED2_UART_TX_A0=32

	LED2_UART_RX_A1=50
	LED2_UART_RX_A0=47
}

declare -A PRODUCT_PROFILE=(
	["EXPCF2000"]=_product_EXPCF2000
)

load_product_name_from_dmi() {
	local ret=1

	for m in $($DMIDECODE -t 12 | grep "Option " | awk -F ':' '{print substr($2,1,11)}' | sed 's/ //g');
	do
		if [ -v PRODUCT_PROFILE[$m] ]; then
			${PRODUCT_PROFILE[$m]}
			ret=0
			break
		fi
	done

	return $ret
}

load_product_config() {
	if load_product_name_from_dmi; then
		# Determine gpio chip profile
		gpc=${GPIO_CHIP[$GPIO_MODULE]}
	else
		echo "Error: model profile not found."
		exit 1
	fi
}

## gpio
gpc_it8786() {
	local gpio=$1
	local GPIO_TABLE=(
		10 11 12 13 14 15 16 17
		20 21 22 23 24 25 26 27
		30 31 32 33 34 35 36 37
		40 41 42 43 44 45 46 47
		50 51 52 53 54 55 56 57
		60 61 62 63 64 65 66 67
		70 71 72 73 74 75 76 77
		80 81 82 83 84 85 86 87
		90 91 92 93 94 95 96 97
                100 101 102 103 104 105
	)

	[[ ! ${GPIO_TABLE[*]} =~ $gpio ]] && \
			echo "Invalid gpio number." && exit 1

	for gpiochip in /sys/class/gpio/gpiochip*
	do
		if cat "${gpiochip}"/label | grep -q "gpio_it87"
		then
			base=$(cat "${gpiochip}"/base)
			break
		fi
	done

	group=$(($gpio / 10 - 1))
	bit=$(($gpio % 10))
	echo $((base + 8 * group + bit))
}

declare -A GPIO_CHIP=(
	["gpio_it87"]=gpc_it8786
)

gpio_request() {
	local gpio=${1}

	if [ ! -e "/sys/class/gpio/gpio${gpio}" ]
	then
		echo "${gpio}" > /sys/class/gpio/export
	fi
}

gpio_set_value() {
	local gpio=${1}
	local value=${2}

	gpio_request "${gpio}"
	case "${value}" in
	0)
		echo "low" > "/sys/class/gpio/gpio${gpio}/direction"
		;;
	1)
		echo "high" > "/sys/class/gpio/gpio${gpio}/direction"
		;;
	*)
		usage
	;;
	esac
}

gpio_get_value() {
	local gpio=${1}

	gpio_request "${gpio}"
	cat "/sys/class/gpio/gpio${gpio}/value"
}

disable_all_uart_led1() {
	disable_tx_uart_led1
	disable_rx_uart_led1
}

disable_all_uart_led2() {
	disable_tx_uart_led2
	disable_rx_uart_led2
}

disable_tx_uart_led1() {
	gpio_set_value $($gpc $LED1_UART_TX_A1) 1
	gpio_set_value $($gpc $LED1_UART_TX_A0) 1
}

disable_rx_uart_led1() {
	gpio_set_value $($gpc $LED1_UART_RX_A1) 1
	gpio_set_value $($gpc $LED1_UART_RX_A0) 1
}

disable_tx_uart_led2() {
	gpio_set_value $($gpc $LED2_UART_TX_A1) 1
	gpio_set_value $($gpc $LED2_UART_TX_A0) 1
}

disable_rx_uart_led2() {
	gpio_set_value $($gpc $LED2_UART_RX_A1) 1
	gpio_set_value $($gpc $LED2_UART_RX_A0) 1
}

disable_lan_led1() {
	gpio_set_value $($gpc $LED1_LAN_EN) 0
}

disable_lan_led2() {
	gpio_set_value $($gpc $LED2_LAN_EN) 0
}

set_efivar_value() {
	local index=${1}
	local mode=${2}

	# get current efivar status
	CUR_STATUS_LED1="$(efivar -d -n ${EFIVAR_UUID_NAME} | cut -d' ' -f1)"
	CUR_STATUS_LED1="$(printf "%x\n" $CUR_STATUS_LED1)"
	CUR_STATUS_LED2="$(efivar -d -n ${EFIVAR_UUID_NAME} | cut -d' ' -f2)"
	CUR_STATUS_LED2="$(printf "%x\n" $CUR_STATUS_LED2)"

	# convert to hex from dec
	mode=$(printf "%x\n" $mode)

	# set efivar from input var
	if [ "$index" == "0" ]; then
		printf "\x${mode}\x${CUR_STATUS_LED2}" > "$EFIVAR_TMP_FILE"
	elif [ "$index" == "1" ]; then
		printf "\x${CUR_STATUS_LED1}\x${mode}" > "$EFIVAR_TMP_FILE"
	fi

	# write to efivar
	efivar -w -n ${EFIVAR_UUID_NAME} -f "$EFIVAR_TMP_FILE"

	# cleanup tmp file
	if [ -f "$EFIVAR_TMP_FILE" ]; then
		rm "$EFIVAR_TMP_FILE"
	fi
}

get_efivar_value() {
	local index=${1}

	# get efivar from input var
	if [ "$index" == "0" ]; then
		CUR_STATUS="$(efivar -d -n ${EFIVAR_UUID_NAME} | cut -d' ' -f1)"
	elif [ "$index" == "1" ]; then
		CUR_STATUS="$(efivar -d -n ${EFIVAR_UUID_NAME} | cut -d' ' -f2)"
	fi

	echo $CUR_STATUS
}

debug() {
	# LED1
	echo "LED1_LAN_EN(GP$LED1_LAN_EN):$(gpio_get_value $($gpc $LED1_LAN_EN))"
	echo "LED1_LAN_A1(GP$LED1_LAN_A1):$(gpio_get_value $($gpc $LED1_LAN_A1))"
	echo "LED1_LAN_A0(GP$LED1_LAN_A0):$(gpio_get_value $($gpc $LED1_LAN_A0))"

	echo "LED1_UART_TX_A1(GP$LED1_UART_TX_A1):$(gpio_get_value $($gpc $LED1_UART_TX_A1))"
	echo "LED1_UART_TX_A0(GP$LED1_UART_TX_A0):$(gpio_get_value $($gpc $LED1_UART_TX_A0))"

	echo "LED1_UART_RX_A1(GP$LED1_UART_RX_A1):$(gpio_get_value $($gpc $LED1_UART_RX_A1))"
	echo "LED1_UART_RX_A0(GP$LED1_UART_RX_A0):$(gpio_get_value $($gpc $LED1_UART_RX_A0))"

	# LED2
	echo "LED2_LAN_EN(GP$LED2_LAN_EN): $(gpio_get_value $($gpc $LED2_LAN_EN))"
	echo "LED2_LAN_A1(GP$LED2_LAN_A1): $(gpio_get_value $($gpc $LED2_LAN_A1))"
	echo "LED2_LAN_A0(GP$LED2_LAN_A0): $(gpio_get_value $($gpc $LED2_LAN_A0))"

	echo "LED2_UART_TX_A1(GP$LED2_UART_TX_A1): $(gpio_get_value $($gpc $LED2_UART_TX_A1))"
	echo "LED2_UART_TX_A0(GP$LED2_UART_TX_A0): $(gpio_get_value $($gpc $LED2_UART_TX_A0))"

	echo "LED2_UART_RX_A1(GP$LED2_UART_RX_A1): $(gpio_get_value $($gpc $LED2_UART_RX_A1))"
	echo "LED2_UART_RX_A0(GP$LED2_UART_RX_A0): $(gpio_get_value $($gpc $LED2_UART_RX_A0))"
}

## led
get_led_state() {
	local idx=$1
	local ret

	ret=$(get_efivar_value $idx)
	echo "Get Led#$idx state: ${EFIVAR_MODE_LIST[$ret]}"
}

set_led_state() {
	local idx=$1
	local mode=$2

	case $idx in
		0)
			# LED1
			case $mode in
				LAN1)
					disable_all_uart_led1
					gpio_set_value $($gpc $LED1_LAN_EN) 1
					gpio_set_value $($gpc $LED1_LAN_A1) 1
					gpio_set_value $($gpc $LED1_LAN_A0) 1
					set_efivar_value $idx $EFIVAR_LAN1
					;;
				LAN2)
					disable_all_uart_led1
					gpio_set_value $($gpc $LED1_LAN_EN) 1
					gpio_set_value $($gpc $LED1_LAN_A1) 0
					gpio_set_value $($gpc $LED1_LAN_A0) 0
					set_efivar_value $idx $EFIVAR_LAN2
					;;
				LAN3)
					disable_all_uart_led1
					gpio_set_value $($gpc $LED1_LAN_EN) 1
					gpio_set_value $($gpc $LED1_LAN_A1) 0
					gpio_set_value $($gpc $LED1_LAN_A0) 1
					set_efivar_value $idx $EFIVAR_LAN3
					;;
				LAN4)
					disable_all_uart_led1
					gpio_set_value $($gpc $LED1_LAN_EN) 1
					gpio_set_value $($gpc $LED1_LAN_A1) 1
					gpio_set_value $($gpc $LED1_LAN_A0) 0
					set_efivar_value $idx $EFIVAR_LAN4
					;;
				UART1_TX)
					disable_lan_led1
					disable_rx_uart_led1
					gpio_set_value $($gpc $LED1_UART_TX_A1) 0
					gpio_set_value $($gpc $LED1_UART_TX_A0) 0
					set_efivar_value $idx $EFIVAR_UART1_TX
					;;
				UART2_TX)
					disable_lan_led1
					disable_rx_uart_led1
					gpio_set_value $($gpc $LED1_UART_TX_A1) 0
					gpio_set_value $($gpc $LED1_UART_TX_A0) 1
					set_efivar_value $idx $EFIVAR_UART2_TX
					;;
				UART3_TX)
					disable_lan_led1
					disable_rx_uart_led1
					gpio_set_value $($gpc $LED1_UART_TX_A1) 1
					gpio_set_value $($gpc $LED1_UART_TX_A0) 0
					set_efivar_value $idx $EFIVAR_UART3_TX
					;;
				UART1_RX)
					disable_lan_led1
					disable_tx_uart_led1
					gpio_set_value $($gpc $LED1_UART_RX_A1) 0
					gpio_set_value $($gpc $LED1_UART_RX_A0) 0
					set_efivar_value $idx $EFIVAR_UART1_RX
					;;
				UART2_RX)
					disable_lan_led1
					disable_tx_uart_led1
					gpio_set_value $($gpc $LED1_UART_RX_A1) 0
					gpio_set_value $($gpc $LED1_UART_RX_A0) 1
					set_efivar_value $idx $EFIVAR_UART2_RX
					;;
				UART3_RX)
					disable_lan_led1
					disable_tx_uart_led1
					gpio_set_value $($gpc $LED1_UART_RX_A1) 1
					gpio_set_value $($gpc $LED1_UART_RX_A0) 0
					set_efivar_value $idx $EFIVAR_UART3_RX
					;;
			esac
			;;
		1)
			case $mode in
				LAN1)
					disable_all_uart_led2
					gpio_set_value $($gpc $LED2_LAN_EN) 1
					gpio_set_value $($gpc $LED2_LAN_A1) 1
					gpio_set_value $($gpc $LED2_LAN_A0) 1
					set_efivar_value $idx $EFIVAR_LAN1
					;;
				LAN2)
					disable_all_uart_led2
					gpio_set_value $($gpc $LED2_LAN_EN) 1
					gpio_set_value $($gpc $LED2_LAN_A1) 0
					gpio_set_value $($gpc $LED2_LAN_A0) 0
					set_efivar_value $idx $EFIVAR_LAN2
					;;
				LAN3)
					disable_all_uart_led2
					gpio_set_value $($gpc $LED2_LAN_EN) 1
					gpio_set_value $($gpc $LED2_LAN_A1) 0
					gpio_set_value $($gpc $LED2_LAN_A0) 1
					set_efivar_value $idx $EFIVAR_LAN3
					;;
				LAN4)
					disable_all_uart_led2
					gpio_set_value $($gpc $LED2_LAN_EN) 1
					gpio_set_value $($gpc $LED2_LAN_A1) 1
					gpio_set_value $($gpc $LED2_LAN_A0) 0
					set_efivar_value $idx $EFIVAR_LAN4
					;;
				UART1_TX)
					disable_lan_led2
					disable_rx_uart_led2
					gpio_set_value $($gpc $LED2_UART_TX_A1) 0
					gpio_set_value $($gpc $LED2_UART_TX_A0) 0
					set_efivar_value $idx $EFIVAR_UART1_TX
					;;
				UART2_TX)
					disable_lan_led2
					disable_rx_uart_led2
					gpio_set_value $($gpc $LED2_UART_TX_A1) 0
					gpio_set_value $($gpc $LED2_UART_TX_A0) 1
					set_efivar_value $idx $EFIVAR_UART2_TX
					;;
				UART3_TX)
					disable_lan_led2
					disable_rx_uart_led2
					gpio_set_value $($gpc $LED2_UART_TX_A1) 1
					gpio_set_value $($gpc $LED2_UART_TX_A0) 0
					set_efivar_value $idx $EFIVAR_UART3_TX
					;;
				UART1_RX)
					disable_lan_led2
					disable_tx_uart_led2
					gpio_set_value $($gpc $LED2_UART_RX_A1) 0
					gpio_set_value $($gpc $LED2_UART_RX_A0) 0
					set_efivar_value $idx $EFIVAR_UART1_RX
					;;
				UART2_RX)
					disable_lan_led2
					disable_tx_uart_led2
					gpio_set_value $($gpc $LED2_UART_RX_A1) 0
					gpio_set_value $($gpc $LED2_UART_RX_A0) 1
					set_efivar_value $idx $EFIVAR_UART2_RX
					;;
				UART3_RX)
					disable_lan_led2
					disable_tx_uart_led2
					gpio_set_value $($gpc $LED2_UART_RX_A1) 1
					gpio_set_value $($gpc $LED2_UART_RX_A0) 0
					set_efivar_value $idx $EFIVAR_UART3_RX
					;;

			esac
			;;
	esac

	echo "Set LED#$idx mode: $mode"
}

usage() {
cat << EOF
Usage:
        mx-expc-led-ctl -i <led_index> [LAN1|LAN2|LAN3|LAN4|UART1_TX|UART2_TX|UART3_TX|UART1_RX|UART2_RX|UART3_RX]

OPTIONS:
        -i <led_index>
                Set LED index.

Example:
        Get mode from index 0
        # mx-expc-led-ctl -i 0

        Set LED index 0 to LAN1 mode
        # mx-expc-led-ctl -i 0 LAN1

        Set LED index 1 to UART3_TX mode
        # mx-expc-led-ctl -i 1 UART3_TX
EOF
}

check_efivar_cmd() {
	if ! command -v 'efivar' &> /dev/null
	then
		echo "Command 'efivar' could not be found."
		echo "Please install 'efivar' package on your host."
		exit 1
	fi
}

load_product_config
check_efivar_cmd

# Parameter check
if [[ $# -lt 2 ]]; then
	usage
	exit 1
fi

case $1 in
	-i)
		if [[ $2 -lt 0 || $2 -ge 2 ]]; then
			echo "Invalid LED index."
			exit 1
		fi

		TARGET_LED_INDEX="$2"
		TARGET_OPT="get"
		;;
	*)
		echo "Unknown parameter passed"
		usage
		exit 1
		;;
esac

case $3 in
	LAN1)
		TARGET_MODE="LAN1"
		TARGET_OPT="set"
		;;
	LAN2)
		TARGET_MODE="LAN2"
		TARGET_OPT="set"
		;;
	LAN3)
		TARGET_MODE="LAN3"
		TARGET_OPT="set"
		;;
	LAN4)
		TARGET_MODE="LAN4"
		TARGET_OPT="set"
		;;
	UART1_TX)
		TARGET_MODE="UART1_TX"
		TARGET_OPT="set"
		;;
	UART1_RX)
		TARGET_MODE="UART1_RX"
		TARGET_OPT="set"
		;;
	UART2_TX)
		TARGET_MODE="UART2_TX"
		TARGET_OPT="set"
		;;
	UART2_RX)
		TARGET_MODE="UART2_RX"
		TARGET_OPT="set"
		;;
	UART3_TX)
		TARGET_MODE="UART3_TX"
		TARGET_OPT="set"
		;;
	UART3_RX)
		TARGET_MODE="UART3_RX"
		TARGET_OPT="set"
		;;
esac

case $TARGET_OPT in
	get)
		get_led_state $TARGET_LED_INDEX
		;;
	set)
		set_led_state $TARGET_LED_INDEX $TARGET_MODE
		;;
	*)
		exit 1
		;;
esac

