#!/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>
# Description:
#   For controlling gpio-sysfs value from Super IO UART mode.

TARGET_PRODUCT=""

ctrl=4
DMIDECODE="/usr/sbin/dmidecode"
SETSETIAL="/usr/bin/setserial"
LIBGPIOD_TOOLS_PATH="/usr/local/bin"

## Product Profile
#
# _product_<model name>() {
#   GPIO_MODULE=""
#   SERIAL_MODULE=""
#   SERIAL_GPIO_TBL=()
#   UART_MODE_TBL=()
# }
#

_product_MC1200() {
	GPIO_MODULE="gpio_it87"
	SERIAL_MODULE="it87_serial"

	SERIAL_GPIO_TBL=(13 11 12 1 \
			 16 14 15 2)

	UART_MODE_TBL=( 1 0 0 0 \
			0 1 0 1 \
			0 0 1 1)

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

_product_MC3201() {
	GPIO_MODULE="gpio_it87"
	SERIAL_MODULE="it87_serial"

	SERIAL_GPIO_TBL=(13 11 12 1 \
			 16 14 15 2)

	UART_MODE_TBL=( 1 0 0 0 \
			0 1 0 1 \
			0 0 1 1)

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

_product_V3000() {
	GPIO_MODULE="gpio_it87"
	SERIAL_MODULE="it87_serial"

	SERIAL_GPIO_TBL=(13 11 12 1 \
			 16 14 15 2)

	UART_MODE_TBL=( 1 0 0 0 \
			0 1 0 1 \
			0 0 1 1)

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

_product_V2406C() {
	GPIO_MODULE="gpio_it87"
	SERIAL_MODULE="it87_serial"

	SERIAL_GPIO_TBL=(14 11 12 1 \
			 30 15 16 2 \
			 36 32 33 3 \
			 50 37 47 4)

	UART_MODE_TBL=( 1 0 0 0 \
			0 1 0 1 \
			0 0 1 1)

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

_product_EXPCF2000() {
	GPIO_MODULE="gpio_pca953x"
	SERIAL_MODULE="cp2112"

	# Base on dynamic gpiochip base number
	SERIAL_GPIO_TBL=(0  1  2  3 \
			 4  5  6  7 \
			 8  9  10 11)

	UART_MODE_TBL=(1 1 0 0 \
		       0 0 0 1 \
		       0 0 1 0)

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


_product_DA681C() {
	GPIO_MODULE="gpio_pca953x"
	SERIAL_MODULE="hid_ft260"

	# Base on dynamic gpiochip base number
	SERIAL_GPIO_TBL=(32 33 34 35 \
			 36 37 38 39 \
			 40 41 42 43 \
			 44 45 46 47 \
			 16 17 18 19 \
			 20 21 22 23 \
			 24 25 26 27 \
			 28 29 30 31 \
			 0  1  2  3 \
			 4  5  6  7 \
			 8  9 10 11 \
			 12 13 14 15)

	UART_MODE_TBL=( 1 1 0 0 \
			0 0 0 1 \
			0 0 1 0)

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

_product_DA682C() {
	GPIO_MODULE="gpio_pca953x"
	SERIAL_MODULE="hid_ft260"

	# Base on dynamic gpiochip base number
	SERIAL_GPIO_TBL=(13 11 12 1 \
			 16 14 15 2)

	SERIAL_GPIO_MODULE_A_TBL=(16 17 18 19 \
				  20 21 22 23 \
				  24 25 26 27 \
				  28 29 30 31 \
				  0  1  2  3  \
				  4  5  6  7  \
				  8  9  10 11 \
				  12 13 14 15)

	SERIAL_GPIO_MODULE_B_TBL=(48 49 50 51 \
				  52 53 54 55 \
				  56 57 58 59 \
				  60 61 62 63 \
				  32 33 34 35 \
				  36 37 38 39 \
				  40 41 42 43 \
				  44 45 46 47)


	# TODO: How to change table according to different gpio chip?
	UART_MODE_TBL=( 1 1 0 0 \
			0 0 0 1 \
			0 0 1 0)

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

_product_DA820C() {
	SERIAL_GPIO_TBL=(13 11 12 1 \
			 16 14 15 2)

	SERIAL_GPIO_MODULE_A_TBL=(16 17 18 19 \
				  20 21 22 23 \
				  24 25 26 27 \
				  28 29 30 31 \
				  0  1  2  3 \
				  4  5  6  7 \
				  8  9 10 11 \
				  12 13 14 15)

	UART_MODE_TBL=( 1 0 0 0 \
			0 1 0 1 \
			0 0 1 1)

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

_product_V2403C() {
	GPIO_MODULE="gpio_it87"
	SERIAL_MODULE="it87_serial"

	SERIAL_GPIO_TBL=(14 11 12 1 \
			 30 15 16 2 \
			 36 32 33 3 \
			 50 37 47 4)

	UART_MODE_TBL=( 1 0 0 0 \
			0 1 0 1 \
			0 0 1 1)

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

_product_V2201() {
	GPIO_MODULE="gpio_it87"
	SERIAL_MODULE="it87_serial"

	SERIAL_GPIO_TBL=(51 52 53 1 \
			 55 56 57 2)

	UART_MODE_TBL=( 0 0 1 0 \
			1 0 0 1 \
			0 1 0 1)

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

_product_DA680() {
	SERIAL_GPIO_TBL=("ttyM0" "ttyM1"  "ttyM2"  "ttyM3"  \
			 "ttyM4" "ttyM5"  "ttyM6"  "ttyM7"  \
			 "ttyM8" "ttyM9"  "ttyM10" "ttyM11" \
			 "ttyM12" "ttyM13" "ttyM14" "ttyM15")

	UART_MODE_TBL=("0x0000" "0x0001" "0x0002" "0x0003")

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

_product_BXPC100() {
	GPIO_MODULE="gpio_it87"
	SERIAL_MODULE="it87_serial"

	IO_BOARD_GPIO_PIN=(80 81 82)

	# Refer to circuit diagram P.3
	IO_BOARD_3_COM_GPIO=(0 1 0)
	IO_BOARD_8_COM_GPIO=(0 0 0)

	SERIAL_GPIO_TBL=""
	SERIAL_SIO_GPIO_TBL=(13 11 12 1 \
			 	16 14 15 2)
	SERIAL_3_COM_DEVICE_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2")
	SERIAL_8_COM_DEVICE_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2" "ttyUSB3" \
				"ttyUSB4" "ttyUSB5" "ttyUSB6" "ttyUSB7")

	UART_MODE_TBL=""
	UART_MODE_TBL1=(1 0 0 0 \
			0 1 0 1 \
			0 0 1 1 \
			0 0 1 1)
	UART_MODE_TBL2=("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")
}

_product_DRPC100() {
	GPIO_MODULE="gpio_it87"
	SERIAL_MODULE="it87_serial"

	IO_BOARD_GPIO_PIN=(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)

	SERIAL_GPIO_TBL=""
	SERIAL_SIO_GPIO_TBL=(13 11 12 1 \
			     16 14 15 2)
	SERIAL_4_COM_DEVICE_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2" "ttyUSB3")
	SERIAL_6_COM_DEVICE_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2" "ttyUSB3" "ttyUSB4" "ttyUSB5")

	UART_MODE_TBL=""
	UART_MODE_TBL1=(1 0 0 0 \
			0 1 0 1 \
			0 0 1 1 \
			0 0 1 1)
	UART_MODE_TBL2=("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")
}

_product_RKPA110() {
	GPIO_MODULE="gpio_it87"
	SERIAL_MODULE="it87_serial"

	IO_BOARD_GPIO_PIN=(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)

	SERIAL_GPIO_TBL=""
	SERIAL_SIO_GPIO_TBL=(13 11 12 1 \
			 16 14 15 2)
	SERIAL_4_COM_DEVICE_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2" "ttyUSB3")
	SERIAL_8_COM_DEVICE_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2" "ttyUSB3" "ttyUSB4" "ttyUSB5" "ttyUSB6" "ttyUSB7")

	UART_MODE_TBL=""
	UART_MODE_TBL1=(1 0 0 0 \
			0 1 0 1 \
			0 0 1 1 \
		        0 0 1 1)
	UART_MODE_TBL2=("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")
}

_product_BXPA100() {
	GPIO_MODULE="gpio_it87"
	SERIAL_MODULE="it87_serial"

	IO_BOARD_GPIO_PIN=(91 92 93)

	# Refer to circuit diagram P.3
	IO_BOARD_3_COM_GPIO=(0 1 0)
	IO_BOARD_8_COM_GPIO=(0 0 0)

	SERIAL_GPIO_TBL=""
	SERIAL_SIO_GPIO_TBL=(13 11 12 1 \
			   	16 14 15 2)
	SERIAL_3_COM_DEVICE_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2")
	SERIAL_8_COM_DEVICE_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2" "ttyUSB3" \
				"ttyUSB4" "ttyUSB5" "ttyUSB6" "ttyUSB7")

	UART_MODE_TBL=""
	UART_MODE_TBL1=(1 0 0 0 \
			0 1 0 1 \
			0 0 1 1 \
			0 0 1 1)
	UART_MODE_TBL2=("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")
}

_product_DRPA100() {
	GPIO_MODULE="gpio_it87"
	SERIAL_MODULE="it87_serial"

	IO_BOARD_GPIO_PIN=(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)

	SERIAL_GPIO_TBL=""
	SERIAL_SIO_GPIO_TBL=(13 11 12 1 \
			     16 14 15 2)
	SERIAL_4_COM_DEVICE_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2" "ttyUSB3")
	SERIAL_6_COM_DEVICE_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2" "ttyUSB3" "ttyUSB4" "ttyUSB5")

	UART_MODE_TBL=""
	UART_MODE_TBL1=(1 0 0 0 \
			0 1 0 1 \
			0 0 1 1 \
			0 0 1 1)
	UART_MODE_TBL2=("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")
}

_product_RKPC110() {
	GPIO_MODULE="gpio_it87"
	SERIAL_MODULE="it87_serial"

	IO_BOARD_GPIO_PIN=(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)

	SERIAL_GPIO_TBL=""
	SERIAL_SIO_GPIO_TBL=(13 11 12 1 \
			 16 14 15 2)
	SERIAL_4_COM_DEVICE_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2" "ttyUSB3")
	SERIAL_8_COM_DEVICE_TBL=("ttyUSB0" "ttyUSB1" "ttyUSB2" "ttyUSB3" "ttyUSB4" "ttyUSB5" "ttyUSB6" "ttyUSB7")

	UART_MODE_TBL=""
	UART_MODE_TBL1=(1 0 0 0 \
			0 1 0 1 \
			0 0 1 1 \
			0 0 1 1)
	UART_MODE_TBL2=("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")
}

declare -A PRODUCT_PROFILE=(
	["MC1200"]=_product_MC1200
	["MC3201"]=_product_MC3201
	["V2406C"]=_product_V2406C
	["DA681C"]=_product_DA681C
	["DA682C"]=_product_DA682C
	["DA820C"]=_product_DA820C
	["EXPCF2000"]=_product_EXPCF2000
	["V3000"]=_product_V3000
	["V2403C"]=_product_V2403C
	["V2201"]=_product_V2201
	["DA680"]=_product_DA680
	["BXPC100"]=_product_BXPC100
	["DRPC100"]=_product_DRPC100
	["RKPA110"]=_product_RKPA110
	["BXPA100"]=_product_BXPA100
	["DRPA100"]=_product_DRPA100
	["RKPC110"]=_product_RKPC110
)

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 [[ ${PRODUCT_PROFILE[$m]} ]]; then
			TARGET_PRODUCT=$m
			${PRODUCT_PROFILE[$TARGET_PRODUCT]}
			ret=0
			break
		fi
	done

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

	return $ret
}

load_product_name_from_file() {
	echo "TBD"
}

load_product_config() {
	if load_product_name_from_dmi; then
		# Determine number of serial and mode
		case $TARGET_PRODUCT in
		DA680)
			NUM_OF_SERIAL=${#SERIAL_GPIO_TBL[@]}
			NUM_OF_UART_MODE=${#UART_MODE_TBL[@]}
			;;
		*)
			NUM_OF_SERIAL=$(( ${#SERIAL_GPIO_TBL[@]} / $ctrl ))
			NUM_OF_UART_MODE=$(( ${#UART_MODE_TBL[@]} / $ctrl ))
			;;
		esac
	else
		echo "Error: model profile not found."
		exit 1
	fi
}

## gpio
gpiochip_it8786_pin_remap() {
	local orig_pin
	local new_pin
	orig_pin=$1

	group=$(($orig_pin / 10 - 1))
	bit=$(($orig_pin % 10))
	new_pin=$((8 * group + bit))

	echo $new_pin
}

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
	)

	if ! $(is_module_loaded gpio_it87); then
		echo "gpio_it87 driver is not loaded."
		exit 1
	fi

	[[ ! ${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

	echo $(( base + $(gpiochip_it8786_pin_remap $gpio) ))
}

gpc_cp2112() {
	local gpio=$1
	local GPIO_TABLE=(0 1 2 3 4 5 6 7)

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

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

	echo $((base + $gpio))
}

gpc_pca953x() {
	local gpio=$1

	if ! $(is_module_loaded gpio_pca953x); then
		echo "gpio_pca953x driver is not loaded."
		exit 1
	fi

	for gpc in /sys/class/gpio/gpiochip*
	do
		if [ -f "${gpc}/device/name" ]; then
			if [[ $(cat $gpc/device/name) == "pca9535" ]]; then
				base=$(cat $gpc/base)
				break
			fi
		fi
	done

	echo $((base + $gpio))
}

declare -A GPIO_CHIP=(
	["gpio_it87"]=gpc_it8786
	["cp2112_gpio"]=gpc_cp2112
	["gpio_pca953x"]=gpc_pca953x
)

gpio_request_sysfs() {
	local gpio=${1}

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

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

	gpio_request_sysfs "${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_sysfs() {
	local gpio=${1}

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

get_gpiochip_name() {
	local driver
	driver=${1}

	$LIBGPIOD_TOOLS_PATH/gpiodetect | grep "${driver}" | awk -F " " '{print $1}'
}

gpio_set_value_libgpiod() {
	local gpio=${1}
	local value=${2}
	local gpc_name=${3}

	$LIBGPIOD_TOOLS_PATH/gpioset -p 20ms -t 0 -c $gpc_name "${gpio}=${value}"
}

gpio_get_value_libgpiod() {
	local gpio=${1}
	local gpc_name=${2}

	$LIBGPIOD_TOOLS_PATH/gpioget --numeric -c $gpc_name $gpio
}

_check_libgpiod_tools() {
	if [ ! -e "$LIBGPIOD_TOOLS_PATH/gpiodetect" ]
	then
		echo "Error: $LIBGPIOD_TOOLS_PATH/gpiodetect not found."
		exit 1
	fi
	if [ ! -e "$LIBGPIOD_TOOLS_PATH/gpioset" ]
	then
		echo "Error: $LIBGPIOD_TOOLS_PATH/gpioset not found."
		exit 1
	fi
	if [ ! -e "$LIBGPIOD_TOOLS_PATH/gpioget" ]
	then
		echo "Error: $LIBGPIOD_TOOLS_PATH/gpioget not found."
		exit 1
	fi
}

_set_uart_mode_it87_libgpiod() {
	local serial=$1
	local mode=$2
	local gpc_name

	_check_libgpiod_tools

	gpc_name=$(get_gpiochip_name gpio_it87)

	s=(${SERIAL_GPIO_TBL[@]:(($serial * $ctrl)):$ctrl})
	m=(${UART_MODE_TBL[@]:(($mode * $ctrl)):$ctrl})

	gpio_set_value_libgpiod $(gpiochip_it8786_pin_remap ${s[0]}) ${m[0]} $gpc_name
	gpio_set_value_libgpiod $(gpiochip_it8786_pin_remap ${s[1]}) ${m[1]} $gpc_name
	gpio_set_value_libgpiod $(gpiochip_it8786_pin_remap ${s[2]}) ${m[2]} $gpc_name
	echo ${m[3]} > /sys/class/misc/it87_serial/serial${s[3]}/serial${s[3]}_rs485
}

_set_uart_mode_it87() {
	local serial=$1
	local mode=$2
	local gpc=${GPIO_CHIP[gpio_it87]}

	s=(${SERIAL_GPIO_TBL[@]:(($serial * $ctrl)):$ctrl})
	m=(${UART_MODE_TBL[@]:(($mode * $ctrl)):$ctrl})

	gpio_set_value_sysfs $($gpc ${s[0]}) ${m[0]}
	gpio_set_value_sysfs $($gpc ${s[1]}) ${m[1]}
	gpio_set_value_sysfs $($gpc ${s[2]}) ${m[2]}
	echo ${m[3]} > /sys/class/misc/it87_serial/serial${s[3]}/serial${s[3]}_rs485
}

_set_uart_mode_pca953x() {
	local serial=$1
	local mode=$2
	local gpc=${GPIO_CHIP[gpio_pca953x]}

	s=(${SERIAL_GPIO_TBL[@]:(($serial * $ctrl)):$ctrl})
	m=(${UART_MODE_TBL[@]:(($mode * $ctrl)):$ctrl})

	gpio_set_value_sysfs $($gpc ${s[0]}) ${m[0]}
	gpio_set_value_sysfs $($gpc ${s[1]}) ${m[1]}
	gpio_set_value_sysfs $($gpc ${s[2]}) ${m[2]}
	gpio_set_value_sysfs $($gpc ${s[3]}) ${m[3]}
}

_set_uart_mode_via_setserial() {
	local serial=$1
	local mode=$2

	s=${SERIAL_GPIO_TBL[$serial]}
	m=${UART_MODE_TBL[$mode]}

	$SETSETIAL "/dev/$s" port $m > /dev/null 2>&1
	return $?
}

## uart
set_uart_mode() {
	local serial=$1
	local mode=$2

	case $TARGET_PRODUCT in
		MC1200 | MC3201 | V2406C | V3000 | V2403C | V2201)
			if ! $(is_module_loaded it87_serial); then
				echo "it87_serial driver is not loaded."
				exit 1
			fi
			_set_uart_mode_it87 $serial $mode
			;;
		DA681C | EXPCF2000)
			_set_uart_mode_pca953x $serial $mode
			;;
		DA682C|DA820C)
			if [[ $serial -ge 0 ]] && [[ $serial -le 1 ]]; then
				# TODO: How to change table according to different gpio chip?
				UART_MODE_TBL=(1 0 0 0 \
						0 1 0 1 \
						0 0 1 1)
				_set_uart_mode_it87 $serial $mode
			elif [[ $serial -ge 2 ]] && [[ $serial -le $NUM_OF_SERIAL ]]; then
				# TODO: How to change table according to different gpio chip?
				UART_MODE_TBL=(1 1 0 0 \
						0 0 0 1 \
						0 0 1 0)
				_set_uart_mode_pca953x $serial $mode
			else
				echo "Invalid serial port." && exit 1
			fi
			;;
		DA680)
			if ! is_module_loaded mxu11x0; then
				echo "mxu11x0 driver is not loaded."
				exit 1
			fi

			if ! _set_uart_mode_via_setserial $serial $mode; then
				echo "Not allow mode"
				exit 1
			fi
			;;
		BXPC100 | DRPC100 | RKPA110 | BXPA100 | DRPA100 | RKPC110)
			if [[ $serial -ge 0 ]] && [[ $serial -le 1 ]]; then
				_set_uart_mode_it87_libgpiod $serial $mode
			elif [[ $serial -ge 2 ]] && [[ $serial -le $NUM_OF_SERIAL ]]; then
				if ! _set_uart_mode_via_setserial $((serial - 2)) $mode; then
					echo "Not allow mode"
					exit 1
				fi
			fi
			;;
	esac

	echo "Set OK."
}

_get_uart_mode_it87_libgpiod() {
	local serial=$1
	local gpc_name

	_check_libgpiod_tools

	gpc_name=$(get_gpiochip_name gpio_it87)
	s=(${SERIAL_GPIO_TBL[@]:(($serial * $ctrl)):$ctrl})

	ctrl1=$(gpio_get_value_libgpiod $(gpiochip_it8786_pin_remap ${s[0]}) $gpc_name)
	ctrl2=$(gpio_get_value_libgpiod $(gpiochip_it8786_pin_remap ${s[1]}) $gpc_name)
	ctrl3=$(gpio_get_value_libgpiod $(gpiochip_it8786_pin_remap ${s[2]}) $gpc_name)
	ctrl4=$(cat /sys/class/misc/it87_serial/serial${s[3]}/serial${s[3]}_rs485)

	for ((i = 0; i < $ctrl; ++i)); do
		m=(${UART_MODE_TBL[@]:(($i * $ctrl)):$ctrl})
		if [[ ${m[0]} == $ctrl1 ]] && \
		   [[ ${m[1]} == $ctrl2 ]] && \
		   [[ ${m[2]} == $ctrl3 ]] && \
		   [[ ${m[3]} == $ctrl4 ]]; then

			echo "Current uart mode is ${UART_MODE_STR[$i]} interface."
			return 0
		fi
	done

	echo "Unknown UART mode"
	return 1
}

_get_uart_mode_it87() {
	local serial=$1
	local gpc=${GPIO_CHIP[gpio_it87]}

	s=(${SERIAL_GPIO_TBL[@]:(($serial * $ctrl)):$ctrl})

	ctrl1=$(gpio_get_value_sysfs $($gpc ${s[0]}))
	ctrl2=$(gpio_get_value_sysfs $($gpc ${s[1]}))
	ctrl3=$(gpio_get_value_sysfs $($gpc ${s[2]}))
	ctrl4=$(cat /sys/class/misc/it87_serial/serial${s[3]}/serial${s[3]}_rs485)

	for ((i = 0; i < $ctrl; ++i)); do
		m=(${UART_MODE_TBL[@]:(($i * $ctrl)):$ctrl})
		if [[ ${m[0]} == $ctrl1 ]] && \
		   [[ ${m[1]} == $ctrl2 ]] && \
		   [[ ${m[2]} == $ctrl3 ]] && \
		   [[ ${m[3]} == $ctrl4 ]]; then

			echo "Current uart mode is ${UART_MODE_STR[$i]} interface."
			return 0
		fi
	done

	echo "Unknown UART mode"
	return 1
}

_get_uart_mode_pca953x() {
	local serial=$1
	local gpc=${GPIO_CHIP[gpio_pca953x]}

	s=(${SERIAL_GPIO_TBL[@]:(($serial * $ctrl)):$ctrl})

	ctrl1=$(gpio_get_value_sysfs $($gpc ${s[0]}))
	ctrl2=$(gpio_get_value_sysfs $($gpc ${s[1]}))
	ctrl3=$(gpio_get_value_sysfs $($gpc ${s[2]}))
	ctrl4=$(gpio_get_value_sysfs $($gpc ${s[3]}))

	for ((i = 0; i < $ctrl; ++i)); do
		m=(${UART_MODE_TBL[@]:(($i * $ctrl)):$ctrl})
		if [[ ${m[0]} == $ctrl1 ]] && \
		   [[ ${m[1]} == $ctrl2 ]] && \
		   [[ ${m[2]} == $ctrl3 ]] && \
		   [[ ${m[3]} == $ctrl4 ]]; then

			echo "Current uart mode is ${UART_MODE_STR[$i]} interface."
			return 0
		fi
	done

	echo "Unknown UART mode"
	return 1
}

_get_uart_mode_via_setserial() {
	local serial=$1

	s=${SERIAL_GPIO_TBL[$serial]}
	result=$($SETSETIAL -G "/dev/$s")
	pat='port (0x[0-9]+)'

	if [[ "$result" =~ $pat ]]; then
		for ((i = 0; i < $NUM_OF_UART_MODE; i++)); do
			m=${UART_MODE_TBL[$i]}
			if [[ $m == ${BASH_REMATCH[1]} ]]; then
				echo "Current uart mode is ${UART_MODE_STR[$i]} interface."
				return 0
			fi
		done
	fi

	echo "Unknown UART mode"
	return 1
}

get_uart_mode() {
	local serial=$1

	case $TARGET_PRODUCT in
		MC1200 | MC3201 | V2406C | V3000 | V2403C | V2201)
			if ! $(is_module_loaded it87_serial); then
				echo "it87_serial driver is not loaded."
				exit 1
			fi
			_get_uart_mode_it87 $serial
			;;
		DA681C | EXPCF2000)
			_get_uart_mode_pca953x $serial
			;;
		DA682C|DA820C)
			if [[ $serial -ge 0 ]] && [[ $serial -le 1 ]]; then
				# TODO: How to change table according to different gpio chip?
				UART_MODE_TBL=(1 0 0 0 \
						0 1 0 1 \
						0 0 1 1)
				_get_uart_mode_it87 $serial
			elif [[ $serial -ge 2 ]] && [[ $serial -le $NUM_OF_SERIAL ]]; then
				# TODO: How to change table according to different gpio chip?
				UART_MODE_TBL=(1 1 0 0 \
						0 0 0 1 \
						0 0 1 0)

				_get_uart_mode_pca953x $serial
			else
				echo "Invalid serial port." && exit 1
			fi
			;;
		DA680)
			if ! is_module_loaded mxu11x0; then
				echo "mxu11x0 driver is not loaded."
				exit 1
			fi
			_get_uart_mode_via_setserial $serial
			;;
		BXPC100 | DRPC100 | RKPA110 | BXPA100 | DRPA100 | RKPC110)
			if [[ $serial -ge 0 ]] && [[ $serial -le 1 ]]; then
				_get_uart_mode_it87_libgpiod $serial
			elif [[ $serial -ge 2 ]] && [[ $serial -le $NUM_OF_SERIAL ]]; then
				_get_uart_mode_via_setserial $((serial - 2))
			fi
			;;
	esac
}

usage() {
cat << EOF
Usage:
		mx-uart-ctl -p <port_number> [-m <uart_mode>]

OPTIONS:
		-p <port_number>
				Set target port.
		-m <uart_mode>
				Set target port to uart_mode
				0 --> set to RS-232 mode
				1 --> set to RS-485-2W mode
				2 --> set to RS-422 mode
				3 --> set to RS-485-4W mode

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

		Set port 1 to mode RS232
		# mx-uart-ctl -p 1 -m 0
EOF
}

is_module_loaded() {
	local mod_name=$1
	if lsmod | grep -w $mod_name &> /dev/null; then
		return 0
	else
		return 1
	fi
}

is_number(){
	num=$1
	if [ -z "${num##*[!0-9]*}" ]; then
		echo "Input parameter '$num' is invalid. Exit."
		exit 1
	fi
}

# Bind FT260 USB-to-I2C driver for PCA9535
_bind_ft260_driver() {
	local reg_addr=$@
	local i2c_num=0
	local module_num=0

	if ! $(is_module_loaded hid_ft260); then
		echo "hid_ft260 driver is not loaded."
		exit 1
	fi

	if ! $(is_module_loaded gpio_pca953x); then
		echo "gpio_pca953x driver is not loaded."
		exit 1
	fi

	for filename in /sys/bus/i2c/devices/i2c-*/name; do
		i2c_devname=$(cat ${filename})
		if [[ $i2c_devname == *"FT260"* ]]; then
			((module_num++))
			i2c_devpath=$(echo ${filename%/*})
			for addr in $reg_addr; do
				if [[ ! -e "${i2c_devpath}/${i2c_num}-00${addr##0x}" ]]; then
					echo "pca9535 $addr" > ${i2c_devpath}/new_device
				fi
			done
		fi

		((i2c_num++))
	done

	echo $module_num
}

# Bind CP2112 USB-to-I2C driver for PCA9535
_bind_cp2112_driver() {
	local addr=$1

	if ! $(is_module_loaded hid_cp2112); then
		echo "hid_cp2112 driver is not loaded."
		exit 1
	fi

	if ! $(is_module_loaded gpio_pca953x); then
		echo "gpio_pca953x driver is not loaded."
		exit 1
	fi

	for filename in /sys/bus/i2c/devices/i2c-*/name; do
		i2c_devname=$(cat ${filename})
		if [[ $i2c_devname == *"CP2112"* ]]; then
			i2c_devpath=$(echo ${filename%/*})
			i2c_num=$(echo ${i2c_devpath} | cut -d '-' -f2)
			if [[ ! -e "${i2c_devpath}/${i2c_num}-00${addr##0x}" ]]; then
				echo "pca9535 $addr" > ${i2c_devpath}/new_device
			fi
		fi
	done
}

init_product() {
	case $TARGET_PRODUCT in
		DA681C)
			local ft260_module_num=$(_bind_ft260_driver 0x20 0x21 0x22)
			;;
		DA682C)
			local ft260_module_num=$(_bind_ft260_driver 0x26 0x27)

			# According to number of modules, it would change serial gpio offset table.
			if [[ $ft260_module_num -eq 1 ]]; then
				SERIAL_GPIO_TBL+=(${SERIAL_GPIO_MODULE_A_TBL[@]})
			elif [[ $ft260_module_num -eq 2 ]]; then
				SERIAL_GPIO_TBL+=(${SERIAL_GPIO_MODULE_B_TBL[@]} \
								  ${SERIAL_GPIO_MODULE_A_TBL[@]})
			fi

			NUM_OF_SERIAL=$(( ${#SERIAL_GPIO_TBL[@]} / $ctrl ))
			;;
		EXPCF2000)
			_bind_cp2112_driver 0x20
			;;
		DA820C)
			local ft260_module_num=$(_bind_ft260_driver 0x26 0x27)

			# According to number of modules, it would change serial gpio offset table.
			if [[ $ft260_module_num -eq 1 ]]; then
				SERIAL_GPIO_TBL+=(${SERIAL_GPIO_MODULE_A_TBL[@]})
			fi

			NUM_OF_SERIAL=$(( ${#SERIAL_GPIO_TBL[@]} / $ctrl ))
			;;
		BXPC100 | BXPA100)
			local io_board_gpio_val
			local gpc_name

			if ! $(is_module_loaded gpio_it87); then
				echo "gpio_it87 driver is not loaded."
				exit 1
			fi

			_check_libgpiod_tools

			gpc_name=$(get_gpiochip_name gpio_it87)

			io_board_gpio_val=(
				$(gpio_get_value_libgpiod $(gpiochip_it8786_pin_remap ${IO_BOARD_GPIO_PIN[0]}) $gpc_name)
				$(gpio_get_value_libgpiod $(gpiochip_it8786_pin_remap ${IO_BOARD_GPIO_PIN[1]}) $gpc_name)
				$(gpio_get_value_libgpiod $(gpiochip_it8786_pin_remap ${IO_BOARD_GPIO_PIN[2]}) $gpc_name)
			)

			if ! $(is_module_loaded it87_serial); then
				echo "it87_serial driver is not loaded."
				exit 1
			else
				NUM_OF_SERIAL=2
			fi

			if [[ "${IO_BOARD_3_COM_GPIO[*]}" == "${io_board_gpio_val[*]}" ]]; then
				if ! $(is_module_loaded mxuport); then
					echo "mxuport driver is not loaded."
					exit 1
				fi
				NUM_OF_SERIAL=$(( NUM_OF_SERIAL += 3 ))
			elif [[ "${IO_BOARD_8_COM_GPIO[*]}" == "${io_board_gpio_val[*]}" ]]; then
				if ! $(is_module_loaded mxuport); then
					echo "mxuport driver is not loaded."
					exit 1
				fi
				NUM_OF_SERIAL=$(( NUM_OF_SERIAL += 8 ))
			fi

			if [[ $TARGET_SERIAL_PORT -ge 0 ]] && [[ $TARGET_SERIAL_PORT -le 1 ]]; then
				SERIAL_GPIO_TBL=("${SERIAL_SIO_GPIO_TBL[@]}")
				UART_MODE_TBL=("${UART_MODE_TBL1[@]}")
				UART_MODE_STR=("${UART_MODE_STR1[@]}")
				NUM_OF_UART_MODE=$(( ${#UART_MODE_TBL[@]} / $ctrl ))
			elif [[ $TARGET_SERIAL_PORT -ge 2 ]] && [[ $TARGET_SERIAL_PORT -le $NUM_OF_SERIAL ]]; then
				if [[ $NUM_OF_SERIAL -eq 5 ]]; then
					SERIAL_GPIO_TBL=("${SERIAL_3_COM_DEVICE_TBL[@]}")
				elif [[ $NUM_OF_SERIAL -eq 10 ]]; then
					SERIAL_GPIO_TBL=("${SERIAL_8_COM_DEVICE_TBL[@]}")
				fi

				UART_MODE_TBL=("${UART_MODE_TBL2[@]}")
				UART_MODE_STR=("${UART_MODE_STR2[@]}")
				NUM_OF_UART_MODE=${#UART_MODE_TBL[@]}
			fi
			;;
		DRPC100 | DRPA100)
			local io_board_gpio_val
			local gpc_name

			if ! $(is_module_loaded gpio_it87); then
				echo "gpio_it87 driver is not loaded."
				exit 1
			fi

			_check_libgpiod_tools

			gpc_name=$(get_gpiochip_name gpio_it87)

			io_board_gpio_val=(
				$(gpio_get_value_libgpiod $(gpiochip_it8786_pin_remap ${IO_BOARD_GPIO_PIN[0]}) $gpc_name)
				$(gpio_get_value_libgpiod $(gpiochip_it8786_pin_remap ${IO_BOARD_GPIO_PIN[1]}) $gpc_name)
				$(gpio_get_value_libgpiod $(gpiochip_it8786_pin_remap ${IO_BOARD_GPIO_PIN[2]}) $gpc_name)
				$(gpio_get_value_libgpiod $(gpiochip_it8786_pin_remap ${IO_BOARD_GPIO_PIN[3]}) $gpc_name)
			)

			if ! $(is_module_loaded it87_serial); then
				echo "it87_serial driver is not loaded."
				exit 1
			else
				NUM_OF_SERIAL=2
			fi

			if [[ "${IO_BOARD_4_COM_2_LAN_GPIO[*]}" == "${io_board_gpio_val[*]}" ]]; then
				if ! $(is_module_loaded mxuport); then
					echo "mxuport driver is not loaded."
					exit 1
				fi
				NUM_OF_SERIAL=$(( NUM_OF_SERIAL += 4 ))

			elif [[ "${IO_BOARD_6_COM_GPIO[*]}" == "${io_board_gpio_val[*]}" ]]; then
				if ! $(is_module_loaded mxuport); then
					echo "mxuport driver is not loaded."
					exit 1
				fi
				NUM_OF_SERIAL=$(( NUM_OF_SERIAL += 6 ))
			fi

			if [[ $TARGET_SERIAL_PORT -ge 0 ]] && [[ $TARGET_SERIAL_PORT -le 1 ]]; then
				SERIAL_GPIO_TBL=("${SERIAL_SIO_GPIO_TBL[@]}")
				UART_MODE_TBL=("${UART_MODE_TBL1[@]}")
				UART_MODE_STR=("${UART_MODE_STR1[@]}")
				NUM_OF_UART_MODE=$(( ${#UART_MODE_TBL[@]} / $ctrl ))
			elif [[ $TARGET_SERIAL_PORT -ge 2 ]] && [[ $TARGET_SERIAL_PORT -le $NUM_OF_SERIAL ]]; then
				if [[ $NUM_OF_SERIAL -eq 6 ]]; then
					SERIAL_GPIO_TBL=("${SERIAL_4_COM_DEVICE_TBL[@]}")
				elif [[ $NUM_OF_SERIAL -eq 8 ]]; then
					SERIAL_GPIO_TBL=("${SERIAL_6_COM_DEVICE_TBL[@]}")
				fi

				UART_MODE_TBL=("${UART_MODE_TBL2[@]}")
				UART_MODE_STR=("${UART_MODE_STR2[@]}")
				NUM_OF_UART_MODE=${#UART_MODE_TBL[@]}
			fi
			;;
		RKPA110 | RKPC110)
			local b2b_gpio
			local io_board_gpio_val
			local gpc_name

			if ! $(is_module_loaded gpio_it87); then
				echo "gpio_it87 driver is not loaded."
				exit 1
			fi

			_check_libgpiod_tools

			gpc_name=$(get_gpiochip_name gpio_it87)
			io_board_gpio_val=(
				$(gpio_get_value_libgpiod $(gpiochip_it8786_pin_remap ${IO_BOARD_GPIO_PIN[0]}) $gpc_name)
				$(gpio_get_value_libgpiod $(gpiochip_it8786_pin_remap ${IO_BOARD_GPIO_PIN[1]}) $gpc_name)
				$(gpio_get_value_libgpiod $(gpiochip_it8786_pin_remap ${IO_BOARD_GPIO_PIN[2]}) $gpc_name)
			)

			if ! $(is_module_loaded it87_serial); then
				echo "it87_serial driver is not loaded."
				exit 1
			else
				NUM_OF_SERIAL=2
			fi

			if [[ "${IO_BOARD_4_COM_2_LAN_GPIO[*]}" == "${io_board_gpio_val[*]}" ]]; then
				if ! $(is_module_loaded mxuport); then
					echo "mxuport driver is not loaded."
					exit 1
				fi
				NUM_OF_SERIAL=$(( NUM_OF_SERIAL += 4 ))

			elif [[ "${IO_BOARD_8_COM_GPIO[*]}" == "${io_board_gpio_val[*]}" ]]; then
				if ! $(is_module_loaded mxuport); then
					echo "mxuport driver is not loaded."
					exit 1
				fi
				NUM_OF_SERIAL=$(( NUM_OF_SERIAL += 8 ))
			fi

			if [[ $TARGET_SERIAL_PORT -ge 0 ]] && [[ $TARGET_SERIAL_PORT -le 1 ]]; then
				SERIAL_GPIO_TBL=("${SERIAL_SIO_GPIO_TBL[@]}")
				UART_MODE_TBL=("${UART_MODE_TBL1[@]}")
				UART_MODE_STR=("${UART_MODE_STR1[@]}")
				NUM_OF_UART_MODE=$(( ${#UART_MODE_TBL[@]} / $ctrl ))
			elif [[ $TARGET_SERIAL_PORT -ge 2 ]] && [[ $TARGET_SERIAL_PORT -le $NUM_OF_SERIAL ]]; then
				if [[ $NUM_OF_SERIAL -eq 6 ]]; then
					SERIAL_GPIO_TBL=("${SERIAL_4_COM_DEVICE_TBL[@]}")
				elif [[ $NUM_OF_SERIAL -eq 10 ]]; then
					SERIAL_GPIO_TBL=("${SERIAL_8_COM_DEVICE_TBL[@]}")
				fi
				UART_MODE_TBL=("${UART_MODE_TBL2[@]}")
				UART_MODE_STR=("${UART_MODE_STR2[@]}")
				NUM_OF_UART_MODE=${#UART_MODE_TBL[@]}
			fi
			;;
	esac
}

load_product_config

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

while [[ "$#" -gt 0 ]]; do
	case $1 in
		-p)
			TARGET_SERIAL_PORT="$2"
			TARGET_OPT="get"
			is_number $TARGET_SERIAL_PORT
			shift
			;;
		-m)
			TARGET_SERIAL_MODE="$2"
			TARGET_OPT="set"
			is_number $TARGET_SERIAL_MODE
			shift
			;;
		*)
			echo "Unknown parameter passed"
			usage
			exit 1
			;;
		esac
		shift
done

init_product

[[ $TARGET_SERIAL_PORT -lt 0 || $TARGET_SERIAL_PORT -ge $NUM_OF_SERIAL ]] && \
	echo "Invalid serial port." && exit 1

case $TARGET_OPT in
	get)
		[[ -z $TARGET_SERIAL_PORT ]] && \
			echo "Invalid serial port." && exit 1

		get_uart_mode $TARGET_SERIAL_PORT
		;;
	set)
		[[ -z $TARGET_SERIAL_PORT || -z $TARGET_SERIAL_MODE ]] && \
			echo "Invalid serial port or UART mode." && exit 1

		[[ $TARGET_SERIAL_MODE -lt 0 || $TARGET_SERIAL_MODE -ge $NUM_OF_UART_MODE ]] && \
			echo "Invalid UART mode." && exit 1

		set_uart_mode $TARGET_SERIAL_PORT $TARGET_SERIAL_MODE
		get_uart_mode $TARGET_SERIAL_PORT
		;;
	*)
		exit 1
		;;
esac
