#!/bin/bash
#
# Copyright (C) 2024 MOXA Inc. All rights reserved.
# This software is distributed under the terms of the MOXA SOFTWARE NOTICE.
# See the file LICENSE for details.
#
# Authors:
#       2024    Elvis Yao <ElvisCW.Yao@moxa.com>
# Description:
#       Disk hotswap daemon

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

LED_STATE=("off" "on")
OS_ID="$(grep -oP '(?<=^ID=).+' /etc/os-release | tr -d '"')"

function V3400::profile() {
        GPIO_BTN_PIN=(2 4)
        GPIO_LED_PIN=(3 5)
        GPIO_CHIP="gpiochip0"
        TIME_INTER_BTN_1=""
        TIME_INTER_BTN_2=""
        TIME_PRESSED_BTN_1=""
        TIME_PRESSED_BTN_2=""
        TIME_INTERVAL_REMOVE_DISK="3" # pressed over 3 sec to unmount and remove disk
}

function V3400::daemon() {
        gpiomon --num-events=0 -c $GPIO_CHIP ${GPIO_BTN_PIN[@]} | while read -r event
        do
                V3400::do_action "$event"
        done
}

function V3400::do_action() {
        local event=( $1 )
        local timestamp
        local edge_type
        local chip
        local gpio_pin

        timestamp=${event[0]}
        edge_type=${event[1]}
        chip=${event[2]}
        gpio_pin=${event[3]}

        if [[ $gpio_pin -eq ${GPIO_BTN_PIN[0]} ]]; then
                if [[ "$edge_type" == "falling" ]]; then
                        TIME_PRESSED_BTN_1=${timestamp%.*}
                elif [[ "$edge_type" == "rising" ]]; then
                        if [ ! -z "${TIME_PRESSED_BTN_1}" ]; then
                                TIME_RELEASE_BTN_1=${timestamp%.*}
                                TIME_INTER_BTN_1="$((TIME_RELEASE_BTN_1 - TIME_PRESSED_BTN_1))"
                                if [[ $TIME_INTER_BTN_1 -ge $TIME_INTERVAL_REMOVE_DISK ]]; then
                                        V3400::remove_disk 0
                                elif [[ $TIME_INTER_BTN_1 -lt $TIME_INTERVAL_REMOVE_DISK ]]; then
                                        V3400::scan_disk 0
                                fi
                        fi
                else
                        echo "Wrong BTN Edge Type: $edge_type"
                        exit 1
                fi
        elif [[ $gpio_pin -eq ${GPIO_BTN_PIN[1]} ]]; then
                if [[ "$edge_type" == "falling" ]]; then
                        TIME_PRESSED_BTN_2=${timestamp%.*}
                elif [[ "$edge_type" == "rising" ]]; then
                        if [ ! -z "${TIME_PRESSED_BTN_2}" ]; then
                                TIME_RELEASE_BTN_2=${timestamp%.*}
                                TIME_INTER_BTN_2="$((TIME_RELEASE_BTN_2 - TIME_PRESSED_BTN_2))"
                                if [[ $TIME_INTER_BTN_2 -ge $TIME_INTERVAL_REMOVE_DISK ]]; then
                                        V3400::remove_disk 1
                                elif [[ $TIME_INTER_BTN_2 -lt $TIME_INTERVAL_REMOVE_DISK ]]; then
                                        V3400::scan_disk 1
                                fi
                        fi
                else
                        echo "Wrong BTN Edge Type: $edge_type"
                        exit 1
                fi
        else
                echo "Wrong BTN GPIO PIN: $gpio_pin"
                exit 1
        fi
}

function V3400::set_led_state() {
        local port
        local state
        port=${1}
        state=${2}

        set_cp2112_led_state_libgpiod "$port" "$state"
}

function V3400::remove_disk() {
        local disk_index=${1}
        local host="host${disk_index}"
        local target="target${disk_index}:0:0"
        local path="${disk_index}:0:0:0"
        local field="\$1"

        # check partitions
        partitions=`mount | awk -F' ' "/media\/disk${disk_index}/{ print $field }" | xargs -n 10`
        if [ ! -z "$partitions" ] ; then
                echo "WARNING! /media/disk${disk_index}pX should be unmounted before disk is removed"
                echo "Force to unmount them"
                for i in $partitions; do
                        umount $i 2> /dev/null
                done
        fi

        # remove disks
        if [ ! -f /sys/class/scsi_host/${host}/device/${target}/${path}/delete ] ; then
                echo "WARNING! can't remove disk ${disk_index}. It may has been removed before"
                exit 0
        else
                echo 1 > /sys/class/scsi_host/${host}/device/${target}/${path}/delete
        fi

        for i in 1 2 3; do
                V3400::set_led_state $disk_index 1
                sleep 1
                V3400::set_led_state $disk_index 0
                sleep 1
        done
}

function V3400::scan_disk() {
        local disk_index=${1}
        local host="host${disk_index}"
        local target="target${disk_index}:0:0"
        local timeout_sec=10
        local count_sec=0

        echo "scan_disk $disk_index"

        # rescan bus
        dev_host="/sys/class/scsi_host/${host}"
        if [ -d "$dev_host" ]; then
                echo "- - -" > ${dev_host}/scan
                udevadm trigger --action=add --property-match=DEVNAME=/dev/sd*
        else
                echo "Disk host $host is not found"
                exit 1
        fi

        # check disk device is existed
        dev_target="${dev_host}/device/${target}"
        while [ ${count_sec} -lt ${timeout_sec} ]; do
                if [ -d "${dev_target}" ]; then
                        echo "Disk slot $disk_index was detected."
                        break
                fi
                count_sec=$((count_sec + 1))
                sleep 1
        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
}

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
}

function set_cp2112_led_state_libgpiod() {
        local idx=$1
        local state=$2
        local check_state
        local ret

        gpio_set_value_libgpiod ${GPIO_LED_PIN[$idx]} $state $GPIO_CHIP
        [[ $? -ne 0 ]] && ret=1

        check_state=$(gpio_get_value_libgpiod ${GPIO_LED_PIN[$idx]} $GPIO_CHIP)
        [[ $? -ne 0 ]] && ret=1
        if [[ $ret -ne 0 || "$state" -ne "$check_state" ]]; then
                echo "Set State Failed"
                exit $ret
        fi

        echo "Set Led#$idx state: ${LED_STATE[$state]}"
}

function main() {
        script_init
        "${MODEL_NAME}"::daemon
}

main $@
