#!/bin/sh
# SPDX-License-Identifier: MIT

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

orig_port="/dev/ttymxc3"
port="/dev/ttyMux0"
reset_interval_sec=3

at_scfg="AT^SCFG"

scfg_mode_ring0="\"GPIO/mode/RING0\""
scfg_mode_ring0_std="$scfg_mode_ring0,\"std\""

scfg_mode_asc1="\"GPIO/mode/ASC1\""
scfg_mode_asc1_rsv="$scfg_mode_asc1,\"rsv\""

scfg_urc_ringline="\"URC/Ringline\""
scfg_urc_ringline_asc0="$scfg_urc_ringline,\"asc0\""

scfg_urc_ringline_activetime="\"URC/Ringline/ActiveTime\""
scfg_urc_ringline_activetime_2="$scfg_urc_ringline_activetime,\"2\""

scfg_meopmode_powermgmt_suspend="\"MEopMode/PowerMgmt/Suspend\""

scfg_meopmode_prov_autoselect="\"MEopMode/Prov/AutoSelect\""
scfg_meopmode_prov_autoselect_on="$scfg_meopmode_prov_autoselect,\"on\""
scfg_meopmode_prov_autoselect_off="$scfg_meopmode_prov_autoselect,\"off\""

scfg_meopmode_prov_cfg="\"MEopMode/Prov/Cfg\""
scfg_profile_docomojp="docomojp"
scfg_profile_softbank="sbmjp"
scfg_profile_kddijp="kddijp"

at_sgpicfg="AT^SGPICFG"
sgpicfg_login_ring0="\"Logic/Ring0\""
sgpicfg_login_ring0_low="$sgpicfg_login_ring0,\"low\""
sgpicfg_port_wakein="\"port/WakeIn\""
sgpicfg_port_wakein_gpio25="$sgpicfg_port_wakein,\"GPIO25\""

plus_cpsms="+CPSMS"
at_cpsms="AT$plus_cpsms"

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_act_time_2sec="000"
psm_act_time_1min="001"
psm_act_time_6min="010"

hat_sedrxs="^SEDRXS"
at_sedrxs="AT$hat_sedrxs"

psm_edrx_disable="disable"

gpio25_path="/sys/devices/platform/ems31-reset/lte_gpio25/value"

needs_restart_ems31=""

# store value on ems31-boot.conf
conf_suspend=""
conf_prov_autoselect=""
conf_profile=""
conf_psm=""
conf_edrx=""

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

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

parse_config_file() {

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

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

    case "$fix_profile" in
    "\"$scfg_profile_docomojp\""|"\"$scfg_profile_softbank\""|"\"$scfg_profile_kddijp\"")
        conf_profile="$fix_profile"
        conf_prov_autoselect="$scfg_meopmode_prov_autoselect_off";;
    "$scfg_profile_docomojp"|"$scfg_profile_softbank"|"$scfg_profile_kddijp")
        conf_profile="$fix_profile"
        conf_profile="\"$conf_profile"\"
        conf_prov_autoselect="$scfg_meopmode_prov_autoselect_off";;
    "auto"|"\"auto\"")
        conf_prov_autoselect="$scfg_meopmode_prov_autoselect_on";;
    *)
        if [ -n "$fix_profile" ]; then
            put_log "invalid parameter for fix_profile: $fix_profile"
        fi;;
    esac

    case "$suspend" in
    "\"enable\""|"enable")
        conf_suspend="\"1\"";;
    "\"disable\""|"disable")
        conf_suspend="\"0\"";;
    *)
        if [ -n "$suspend" ]; then
            put_log "invalid parameter for suspend: $suspend"
        fi;
    esac

    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
            pcl_orig=$(echo "$edrx" | cut -d ',' -f 1)
            ptw_orig=$(echo "$edrx" | cut -d ',' -f 2)
            if [ -n "$pcl_orig" ] && [ -n "$ptw_orig" ]; then
                temp_edrx_pcl=$(convert_edrx_pcl "$pcl_orig")
                temp_edrx_ptw=$(convert_edrx_ptw "$ptw_orig")
                if [ -n "$temp_edrx_pcl" ] && [ -n "$temp_edrx_ptw" ]; then
                    conf_edrx="$temp_edrx_pcl,$temp_edrx_ptw"
                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" -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" -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
        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" -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_pcl() {
    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\"";;
    esac
}

convert_edrx_ptw() {
    case $1 in
    "1.28")  echo "\"0000\"";;
    "2.56")  echo "\"0001\"";;
    "3.84")  echo "\"0010\"";;
    "5.12")  echo "\"0011\"";;
    "6.40")  echo "\"0100\"";;
    "7.68")  echo "\"0101\"";;
    "8.96")  echo "\"0110\"";;
    "10.24") echo "\"0111\"";;
    "11.52") echo "\"1000\"";;
    "12.80") echo "\"1001\"";;
    "14.08") echo "\"1010\"";;
    "15.36") echo "\"1011\"";;
    "16.64") echo "\"1100\"";;
    "17.92") echo "\"1101\"";;
    "19.20") echo "\"1110\"";;
    "20.48") echo "\"1111\"";;
    esac
}

ems31_poweron() {
    ems31-power-ctrl on
}

ems31_poweroff() {
    ems31-power-ctrl off
}

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

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

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

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

is_at_ok()
{
    i=0
    while ! send_at "AT"
    do
        i=$((i+1))
        if [ $i -ge 15 ]; then
            put_log "timed out: after update profile"
            return
        fi
        sleep 2
    done
}

remove_pdp_contexts() {
    send_at "AT+CFUN=0"
    for cid in $(seq 2 8)
    do
        send_at "AT+CGDCONT=$cid"
    done
    send_at "AT+CFUN=1"
}

set_scfg() {
    update_prov_autoselect=0
    update_prov_cfg=0

    cache="$(send_at_echo $at_scfg? 2>&1 > /dev/null)"

while read -r line
do
    case "$line" in
    *"$scfg_mode_ring0"*)
        if line_not_set "$scfg_mode_ring0_std"; then
            send_at "$at_scfg=$scfg_mode_ring0_std"
        fi;;
    *"$scfg_mode_asc1"*)
        if line_not_set "$scfg_mode_asc1_rsv"; then
            send_at "$at_scfg=$scfg_mode_asc1_rsv"
        fi;;
    *"$scfg_urc_ringline_activetime"*)
        if line_not_set "$scfg_urc_ringline_activetime_2"; then
            send_at "$at_scfg=$scfg_urc_ringline_activetime_2"
        fi;;
    *"$scfg_urc_ringline"*)
        if line_not_set "$scfg_urc_ringline_asc0"; then
            send_at "$at_scfg=$scfg_urc_ringline_asc0"
        fi;;
    *"$scfg_meopmode_powermgmt_suspend"*)
        if [ -n "$conf_suspend" ]; then
            if line_not_set "$scfg_meopmode_powermgmt_suspend,$conf_suspend"; then
                send_at "$at_scfg=$scfg_meopmode_powermgmt_suspend,$conf_suspend,\"1\""
            fi
        fi;;
    *"$scfg_meopmode_prov_autoselect"*)
        if [ -n "$conf_prov_autoselect" ]; then
            if line_not_set "$conf_prov_autoselect"; then
                update_prov_autoselect=1
            fi
        fi;;
    *"$scfg_meopmode_prov_cfg"*)
        if [ -n "$conf_profile" ]; then
            if line_not_set "$scfg_meopmode_prov_cfg,$conf_profile";then
                update_prov_cfg=1
            fi
        fi
    esac
done <<END
$cache
END
    if [ "$update_prov_autoselect" -eq 1 ] || [ "$update_prov_cfg" -eq 1 ]; then
        remove_pdp_contexts
    fi

    if [ "$update_prov_autoselect" -eq 1 ]; then
        send_at "$at_scfg=$conf_prov_autoselect"
        put_log "update autoselect provider profile, needs restart ems31."
        needs_restart_ems31="true"
    fi

    if [ "$update_prov_cfg" -eq 1 ]; then
        send_at "$at_scfg=$scfg_meopmode_prov_cfg,$conf_profile"
        put_log "update provider configuration, needs restart ems31."
        needs_restart_ems31="true"
    fi
}

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

while read -r line
do
    case "$line" in
    *"$sgpicfg_login_ring0"*)
        if line_not_set "$sgpicfg_login_ring0_low"; then
            send_at "$at_sgpicfg=$sgpicfg_login_ring0_low"
        fi;;
    *"$sgpicfg_port_wakein"*)
        if line_not_set "$sgpicfg_port_wakein_gpio25"; then
            send_at "$at_sgpicfg=$sgpicfg_port_wakein_gpio25"
        fi;;
    esac
done <<END
$cache
END
}

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

    cache="$(send_at_echo $at_cpsms? 2>&1 > /dev/null)"

while read -r line
do
    case "$line" in
    *"$plus_cpsms"*)
        if [ "$conf_psm" = "$psm_edrx_disable" ]; then
            if line_not_set "$plus_cpsms: 0"; then
                send_at "$at_cpsms=0"
            fi
        else
            if line_not_set "1,,,$conf_psm"; then
                send_at "$at_cpsms=1,,,$conf_psm"
            fi
        fi;;
    esac
done <<END
$cache
END
}

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

    cache="$(send_at_echo $at_sedrxs? 2>&1 > /dev/null)"

while read -r line
do
    case "$line" in
    *"$hat_sedrxs"*)
        if [ "$conf_edrx" = "$psm_edrx_disable" ]; then
            if line_not_set "$hat_sedrxs: 0"; then
                send_at "$at_sedrxs=0"
            fi
        else
            if line_not_set "4,$conf_edrx"; then
                send_at "$at_sedrxs=2,4,$conf_edrx"
            fi
        fi
    esac
done <<END
$cache
END
}

restart_ems31() {
    ems31_poweroff
    sleep $reset_interval_sec
    ems31_poweron
    sleep $reset_interval_sec
    echo 1 > "$gpio25_path"
    ems31-mux "$orig_port"
    while ! send_at "AT"; do
        put_log "retry mux check"
        sleep 1
    done
}

parse_config_file
restart_ems31
set_scfg
if [ "$needs_restart_ems31" = "true" ]; then
    restart_ems31
fi
set_sgpicfg
set_cpsms
set_sedrxs
put_log "start modemmanager"
rc-service modemmanager start
