#!/bin/sh
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: Copyright 2024 Atmark Techno Inc.

sim7672_power_path="/sys/devices/platform/sim7672-reset/reset/power"
sim7672_status_path="/sys/devices/platform/sim7672-reset/reset/status"

config_file_path="/etc/atmark/sim7672-boot.conf"

port="/dev/ttyCommModem"
port_fwupd="/dev/ttySIM7672FWUPD"
port_other="/dev/ttyACM0"
port_uart="/dev/ttymxc3"
port_lpuart="/dev/ttyLP1"
at_port=""
reset_interval_sec=3

psm_tau_2sec="011"
psm_tau_30sec="100"
psm_tau_1min="101"
psm_tau_10min="000"
psm_tau_1hour="001"
psm_tau_10hour="010"
psm_tau_320hour="110"

psm_act_time_2sec="000"
psm_act_time_1min="001"
psm_act_time_6min="010"

psm_edrx_disable="disable"

needs_restart_sim7672=""

psm=""
edrx=""

# store value on sim7672-boot.conf
conf_psm=""
conf_edrx=""

put_log()
{
	echo "$1"
	logger -t "sim7672-boot" "$1"
}

error()
{
	put_log "$1"
	exit 1
}

parse_config_file() {

	if [ ! -e "$config_file_path" ]; then
		return
	fi

	# shellcheck source=/etc/atmark/sim7672-boot.conf
	. "$config_file_path"

	if [ -n "$psm" ]; then
		if [ "$psm" = "disable" ] || [ "$psm" = "\"disable\"" ]; then
			conf_psm="$psm_edrx_disable"
		else
			tau_orig=$(echo "$psm" | cut -d ',' -f 1)
			act_time_orig=$(echo "$psm" | cut -d ',' -f 2)
			if [ -n "$tau_orig" ] && [ -n "$act_time_orig" ]; then
				temp_psm_tau=$(convert_psm_tau "$tau_orig")
				temp_psm_act_time=$(convert_psm_act_time "$act_time_orig")
				if [ -n "$temp_psm_tau" ] && [ -n "$temp_psm_act_time" ]; then
					conf_psm="\"$temp_psm_tau\",\"$temp_psm_act_time\""
				fi
			fi
			if [ -z "$conf_psm" ]; then
				put_log "invalid parameter for psm: $psm"
			fi
		fi
	fi

	if [ -n "$edrx" ]; then
		if [ "$edrx" = "disable" ] || [ "$edrx" = "\"disable\"" ]; then
			conf_edrx="$psm_edrx_disable"
		else
			edrx_orig=$(echo "$edrx" | cut -d ',' -f 1)
			if [ -n "$edrx_orig" ]; then
				temp_edrx=$(convert_edrx "$edrx_orig")
				if [ -n "$temp_edrx" ]; then
					conf_edrx="$temp_edrx"
				fi
			fi
			if [ -z "$conf_edrx" ]; then
				put_log "invalid parameter for edrx: $edrx"
			fi
		fi
	fi
}

psm_lower5bit() {
	val=$1
	bin=""

	for i in 16 8 4 2 1
	do
		if [ "$val" -ge "$i" ]; then
			val=$((val-i))
			bin=$bin"1"
		else
			bin=$bin"0"
		fi
	done

	echo $bin
}

convert_psm_tau() {
	len=${#1}
	num_last=$((len - 1))
	unit=$(echo "$1" | cut -b "$len")
	num=$(echo "$1" | cut -b 1-"$num_last")
	tau=""

	if echo "$num" | grep -q "[^0-9]\\+"; then
		# not number
		return
	fi

	if [ "$unit" = 's' ]; then
		if [ "$num" -le 62 ]; then
			# 2 seconds  011
			if [ "$(( num%2 ))" -eq 0 ]; then
				tau=$psm_tau_2sec
				num=$(( num/2 ))
			fi
		elif [ "$num" -le 930 ]; then
			# 30 seconds 100
			if [ "$(( num%30 ))" -eq 0 ]; then
				tau=$psm_tau_30sec
				num=$(( num/30 ))
			fi
		fi
	elif [ "$unit" = 'm' ]; then
		if [ "$num" -ge 16 ] && [ "$num" -le 31 ]; then
			# 1 minute   101
			tau=$psm_tau_1min
		elif [ "$num" -le 310 ]; then
			# 10 minutes 000
			if [ "$(( num%10 ))" -eq 0 ]; then
				tau=$psm_tau_10min
				num=$(( num/10 ))
			fi
		fi
	elif [ "$unit" = 'h' ]; then
		if [ "$num" -ge 6 ] && [ "$num" -le 31 ]; then
			# 1 hour     001
			tau=$psm_tau_1hour
		elif [ "$num" -le 310 ]; then
			# 10 hours   010
			if [ "$(( num%10 ))" -eq 0 ]; then
				tau=$psm_tau_10hour
				num=$(( num/10 ))
			fi
		elif [ "$num" -le 9920 ]; then
			# 320 hours   110
			if [ "$(( num%320 ))" -eq 0 ]; then
				tau=$psm_tau_320hour
				num=$(( num/320 ))
			fi
		fi
	fi

	if [ -n "$tau" ]; then
		lower5bit="$(psm_lower5bit "$num")"
		echo "$tau""$lower5bit"
	fi
}

convert_psm_act_time() {
	len=${#1}
	num_last=$((len - 1))
	unit=$(echo "$1" | cut -b "$len")
	num=$(echo "$1" | cut -b 1-"$num_last")
	act_time=""

	if echo "$num" | grep -q "[^0-9]\\+$"; then
		# not number
		return
	fi

	if [ "$unit" = 's' ]; then
		if [ "$num" -le 62 ]; then
			if [ "$(( num%2 ))" -eq 0 ]; then
				# 2 seconds 000
				act_time=$psm_act_time_2sec
				num=$(( num/2 ))
			fi
		fi
	elif [ "$unit" = 'm' ]; then
		if [ "$num" -ge 2 ] && [ "$num" -le 31 ]; then
			# 1 minute  001
			act_time=$psm_act_time_1min
		elif [ "$num" -le 186 ]; then
			if [ "$(( num%6 ))" -eq 0 ]; then
				# 6 minutes 010
				act_time=$psm_act_time_6min
				num=$(( num/6 ))
			fi
		fi
	fi

	if [ -n "$act_time" ]; then
		lower5bit="$(psm_lower5bit "$num")"
		echo "$act_time""$lower5bit"
	fi
}

convert_edrx() {
	case $1 in
	"5.12")     echo "\"0000\"";;
	"10.24")    echo "\"0001\"";;
	"20.48")    echo "\"0010\"";;
	"40.96")    echo "\"0011\"";;
	"61.44")    echo "\"0100\"";;
	"81.92")    echo "\"0101\"";;
	"102.4")    echo "\"0110\"";;
	"122.88")   echo "\"0111\"";;
	"143.36")   echo "\"1000\"";;
	"163.84")   echo "\"1001\"";;
	"327.68")   echo "\"1010\"";;
	"655.36")   echo "\"1011\"";;
	"1310.72")  echo "\"1100\"";;
	"2621.44")  echo "\"1101\"";;
	"5242.88")  echo "\"1110\"";;
	"10485.76") echo "\"1111\"";;
	esac
}

send_at() {
	send-at "$at_port" "$1" > /dev/null 2>&1
	rc="$?"
	[ "$rc" = 143 ] && error "send-at killed"
	return "$rc"
}

send_at_echo() {
	send-at "$at_port" "$1" echo
	rc="$?"
	[ "$rc" = 143 ] && error "send-at killed"
	return "$rc"
}

line_not_set()
{
	case "$line" in
		*"$1"*)
			return 1;;
	esac
	return 0
}

set_value() {
	cache="$(send_at_echo "AT$1?" 2>&1 > /dev/null)"

while read -r line
do
	case "$line" in
	"$1"*)
		if line_not_set "$1: $2"; then
			send_at "AT$1=$2"
			if [ "$3" = "true" ]; then
				needs_restart_sim7672="true"
			fi
			break
		fi;;
	esac
done <<END
$cache
END
}

set_value_with_sub() {
	cache="$(send_at_echo "AT$1?" 2>&1 > /dev/null)"

while read -r line
do
	case "$line" in
	*"$2:"*)
		if line_not_set "$2:$3"; then
			send_at "AT$1=$2,$3"
			if [ "$4" = "true" ]; then
				needs_restart_sim7672="true"
			fi
			break
		fi;;
	esac
done <<END
$cache
END
}

sim7672_poweron() {
	echo 1 > "$sim7672_power_path"
}

sim7672_poweroff() {
	if [ "$(cat "$sim7672_status_path")" = running ]; then
		send_at AT+CPOF
		local i=0
		while [ "$(cat "$sim7672_status_path")" != off ]; do
			i=$((i+1))
			if [ $i -ge 120 ]; then
				put_log "error: sim7672_poweroff disconnect wait timeout"
				exit 1
			fi
			sleep 0.5
		done
	fi
	echo 0 2>/dev/null > "$sim7672_power_path"
}

first_settings() {
	set_value "+CFUN" "1" "false"
	# AT+DIALMODE=1  Disable USBNET networks
	set_value "+DIALMODE" "1" "true"
	# AT$QCPCFG="usbCtrl",1  use ppp only
	set_value_with_sub "\$QCPCFG" "\"usbCtrl\"" "1" "true"
	# AT+CATR=1  use USB1 port to output URCs
	set_value "+CATR" "1" "false"
	# AT+CFGRI=1,60,120  Configure RI pin
	set_value "+CFGRI" "1,60,120" "false"
	# AT$QCPMUCFG=1,1 set sleep depth IDLE
	set_value "\$QCPMUCFG" "1,1" "false"
	if [ "$needs_restart_sim7672" = "true" ]; then
		send_at "ATV1"
		send_at "AT&W"
	fi
}

set_cpsms() {
	if [ -z "$conf_psm" ]; then
		return
	fi

	if [ "$conf_psm" = "$psm_edrx_disable" ]; then
		set_value "+CPSMS" "0" "false"
	else
		set_value "+CPSMS" "1,,,$conf_psm" "false"
	fi
}

set_cedrxs() {
	if [ -z "$conf_edrx" ]; then
		return
	fi

	if [ "$conf_edrx" = "$psm_edrx_disable" ]; then
		set_value "+CEDRXS" "0" "false"
	else
		set_value "+CEDRXS" "2,4,$conf_edrx" "false"
	fi
}

select_port() {
	for i in $(seq 1 30)
	do
		if [ -e $port ]; then
			at_port=$port
			return 0
		elif [ -e $port_fwupd ]; then
			at_port=$port_fwupd
			return 0
		elif [ -e $port_other ]; then
			at_port=$port_other
			return 0
		fi
		sleep 1
	done
	put_log "ERROR: not found a AT port"
	exit 1
}

restart_sim7672() {
	sim7672_poweroff
	sleep $reset_interval_sec
	sim7672_poweron
	sleep $reset_interval_sec
	select_port
	while ! send_at "AT"; do
		put_log "retry to check wake the lte module up"
		sleep 1
	done
}

# Sometimes the response to an AT command will be a number such as "0"
# instead of "OK", and if this happens modemmanager will not operate
# correctly, so reconfigure it so that the response is different from "OK".
check_response_is_not_ok() {
	# use ATZ to check saved config, not reset config.
	if ! send_at "ATZ"; then
		# make sure it does return '0' instead
		if send-at "$at_port" AT urc 0 3; then
			send_at "ATV1"
			send_at "AT&W"
		fi
	fi
}

parse_config_file
sim7672_poweron
select_port
check_response_is_not_ok
first_settings
if [ "$needs_restart_sim7672" = "true" ]; then
	restart_sim7672
fi
set_cpsms
set_cedrxs
put_log "start modemmanager"
rc-service modemmanager start
