#!/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.
#
# Name:
#       MOXA USB Power Control Utility
#
# Authors:
#       2024    Elvis Yao <ElvisCW.Yao@moxa.com>
#

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

MODEL_NAME=""
USB_PWR_STATE_STR=("off" "on")
USB_PORT_STR=("front" "rear" "internal")
NUM_OF_USB=""
NUM_OF_PWR_STATE=${#USB_PWR_STATE_STR[@]}
TARGET_STATE=""
TARGET_OPCODE=0
TARGET_GET_USB_STATE_OPCODE=1
TARGET_SET_USB_PWR_STATE_OPCODE=3

OS_ID="$(awk -v opt="ID" -F= '$1==opt { print $2 ;}' /etc/os-release | tr -d '"')"

# DA-681C settings
function DA681C::profile() {
        # "front: GP46", "rear: GP27", "internal: GP41"
        USB_IT87_GPIO_TBL=(46 27 41)
}

function DA681C::init() {
        NUM_OF_USB=${#USB_IT87_GPIO_TBL[@]}
}

function DA681C::get_usb_pwr_state() {
        get_usb_pwr_state_it87 "$1"
}

function DA681C::set_usb_pwr_state() {
        set_usb_pwr_state_it87 "$1" "$2"
}

# DA-682C settings
function DA682C::profile() {
        # "front: GP46", "rear: GP27", "internal: GP41"
        USB_IT87_GPIO_TBL=(46 27 41)
}

function DA682C::init() {
        NUM_OF_USB=${#USB_IT87_GPIO_TBL[@]}
}

function DA682C::get_usb_pwr_state() {
        get_usb_pwr_state_it87 "$1"
}

function DA682C::set_usb_pwr_state() {
        set_usb_pwr_state_it87 "$1" "$2"
}

# DA-820C settings
function DA820C::profile() {
        # "front: GP46", "rear: GP27", "internal: GP41"
        USB_IT87_GPIO_TBL=(46 27 41)
}

function DA820C::init() {
        NUM_OF_USB=${#USB_IT87_GPIO_TBL[@]}
}

function DA820C::get_usb_pwr_state() {
        get_usb_pwr_state_it87 "$1"
}

function DA820C::set_usb_pwr_state() {
        set_usb_pwr_state_it87 "$1" "$2"
}

# DA-820E settings
function DA820E::profile() {
        # "front: GP46", "rear: GP41", "internal: GP66"
        USB_IT87_GPIO_TBL=(46 41 66)
}

function DA820E::init() {
        NUM_OF_USB=${#USB_IT87_GPIO_TBL[@]}
}

function DA820E::get_usb_pwr_state() {
        get_usb_pwr_state_it87 "$1"
}

function DA820E::set_usb_pwr_state() {
        set_usb_pwr_state_it87 "$1" "$2"
}

# main functions
function get_usb_pwr_state_it87() {
        local usb_port

        usb_port=${1}

        if [[ "${OS_ID}" == "centos" ]]; then
                get_usb_pwr_state_it87_via_sysfs $usb_port
        else
                get_usb_pwr_state_it87_via_libgpiod $usb_port
        fi
}

function set_usb_pwr_state_it87() {
        local usb_port
        local usb_pwr_state

        usb_port=${1}
        usb_pwr_state=${2}

        if [[ "${OS_ID}" == "centos" ]]; then
                set_usb_pwr_state_it87_via_sysfs $usb_port $usb_pwr_state
        else
                set_usb_pwr_state_it87_via_libgpiod $usb_port $usb_pwr_state
        fi
}

function get_usb_pwr_state_it87() {
        local usb_port

        usb_port=${1}

        if [[ "${OS_ID}" == "centos" ]]; then
                get_usb_pwr_state_it87_via_sysfs $usb_port
        else
                get_usb_pwr_state_it87_via_libgpiod $usb_port
        fi
}

function set_usb_pwr_state_it87() {
        local usb_port
        local usb_pwr_state

        usb_port=${1}
        usb_pwr_state=${2}

        if [[ "${OS_ID}" == "centos" ]]; then
                set_usb_pwr_state_it87_via_sysfs $usb_port $usb_pwr_state
        else
                set_usb_pwr_state_it87_via_libgpiod $usb_port $usb_pwr_state
        fi
}

function get_usb_pwr_state_it87() {
        local usb_port

        usb_port=${1}

        if [[ "${OS_ID}" == "centos" ]]; then
                get_usb_pwr_state_it87_via_sysfs $usb_port
        else
                get_usb_pwr_state_it87_via_libgpiod $usb_port
        fi
}

function set_usb_pwr_state_it87() {
        local usb_port
        local usb_pwr_state

        usb_port=${1}
        usb_pwr_state=${2}

        if [[ "${OS_ID}" == "centos" ]]; then
                set_usb_pwr_state_it87_via_sysfs $usb_port $usb_pwr_state
        else
                set_usb_pwr_state_it87_via_libgpiod $usb_port $usb_pwr_state
        fi
}

function get_usb_pwr_state_it87_via_sysfs() {
        local usb_port
        local usb_pwr_state
        local sio_gpio_pin
        local ret

        ret=0

        if ! is_module_loaded gpio_it87; then
                echo "gpio_it87 driver is not loaded"
                exit 1
        fi

        usb_port=${1}
        sio_gpio_pin=$(gpc_it8786 ${USB_IT87_GPIO_TBL[$usb_port]})
        [[ $? -ne 0 ]] && ret=1
        usb_pwr_state=$(gpio_get_value_sysfs $sio_gpio_pin)
        [[ $? -ne 0 ]] && ret=1

        if [[ $ret -ne 0 ]]; then
                echo "Get USB power state Failed"
                exit $ret
        fi

        echo "Current [${USB_PORT_STR[$usb_port]}] USB power state is ${USB_PWR_STATE_STR[$usb_pwr_state]}."
}

function get_usb_pwr_state_it87_via_libgpiod() {
        local usb_port
        local usb_pwr_state
        local sio_gpio_pin
        local gpc_name
        local ret

        ret=0

        if ! is_module_loaded gpio_it87; then
                echo "gpio_it87 driver is not loaded"
                exit 1
        fi

        usb_port=${1}
        sio_gpio_pin=$(gpc_it8786_remap ${USB_IT87_GPIO_TBL[$usb_port]})
        gpc_name=$(gpio_get_gpiochip_name gpio_it87)
        [[ $? -ne 0 ]] && ret=1
        usb_pwr_state=$(gpio_get_value_libgpiod $sio_gpio_pin $gpc_name)
        [[ $? -ne 0 ]] && ret=1

        if [[ $ret -ne 0 ]]; then
                echo "Get State Failed"
                exit $ret
        fi

        echo "Current [${USB_PORT_STR[$usb_port]}] USB power state is ${USB_PWR_STATE_STR[$usb_pwr_state]}."
}

function set_usb_pwr_state_it87_via_sysfs() {
        local usb_port
        local usb_pwr_state
        local check_state
        local sio_gpio_pin

        if ! is_module_loaded gpio_it87; then
                echo "gpio_it87 driver is not loaded"
                exit 1
        fi

        usb_port=${1}
        usb_pwr_state=${2}
        sio_gpio_pin=$(gpc_it8786 ${USB_IT87_GPIO_TBL[$usb_port]})
        [[ $? -ne 0 ]] && ret=1

        gpio_set_value_sysfs $sio_gpio_pin $usb_pwr_state
        [[ $? -ne 0 ]] && ret=1

        if [[ $ret -ne 0 ]]; then
                echo "Set State Failed"
                exit $ret
        fi

        check_state=$(gpio_get_value_sysfs $sio_gpio_pin)
        [[ $? -ne 0 ]] && ret=1
        if [[ $ret -ne 0 || "$usb_pwr_state" -ne "$check_state" ]]; then
                echo "Set State Failed"
                exit $ret
        fi

        echo "Set OK."
}

function set_usb_pwr_state_it87_via_libgpiod() {
        local usb_port
        local usb_pwr_state
        local check_state
        local sio_gpio_pin
        local gpc_name
        local ret

        ret=0

        if ! is_module_loaded gpio_it87; then
                echo "gpio_it87 driver is not loaded"
                exit 1
        fi

        usb_port=${1}
        usb_pwr_state=${2}
        sio_gpio_pin=$(gpc_it8786_remap ${USB_IT87_GPIO_TBL[$usb_port]})
        gpc_name=$(gpio_get_gpiochip_name gpio_it87)
        [[ $? -ne 0 ]] && ret=1

        gpio_set_value_libgpiod $sio_gpio_pin $usb_pwr_state $gpc_name
        [[ $? -ne 0 ]] && ret=1

        check_state=$(gpio_get_value_libgpiod $sio_gpio_pin $gpc_name)
        [[ $? -ne 0 ]] && ret=1
        if [[ $ret -ne 0 || "$usb_pwr_state" -ne "$check_state" ]]; then
                echo "Set State Failed"
                exit $ret
        fi

        echo "Set OK."
}

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 script_usage() {
        cat <<EOF
USAGE:
	mx-usb-power-ctl -i <usb_port> [-s <state>]

OPTIONS:
	-i <usb_port>
                Get USB port power state
                        0: front
                        1: rear
                        2: internal

        -s <state>
                Set USB port power state
                        0: off
                        1: on

EXAMPLE:
	Get USB front port power state
	mx-usb-power-ctl -i 0

	Get USB rear port power state
	mx-usb-power-ctl -i 1

	Set USB front port power state to off
	mx-usb-power-ctl -i 0 -s 0

	Set USB internal port power state to on
	mx-usb-power-ctl -i 2 -s 1

EOF
}

function script_params() {
        if [[ $# -eq 0 ]]; then
                script_usage
                exit 1
        fi

        while getopts "hi:s:" opt; do
                case "${opt}" in
                i)
                        TARGET_USB_PORT="$OPTARG"
                        TARGET_OPCODE=$((TARGET_OPCODE += 1))
                        ;;
                s)
                        TARGET_STATE="$OPTARG"
                        TARGET_OPCODE=$((TARGET_OPCODE += 2))
                        ;;

                h)
                        script_usage
                        exit 0
                        ;;
                \?)
                        script_usage
                        exit 22
                        ;;
                :)
                        echo "Option -$OPTARG requires an argument." >&2
                        script_usage
                        exit 22
                        ;;
                esac
        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

        if [[ ! $(type -t "${MODEL_NAME}"::init) == function ]]; then
                echo "${MODEL_NAME} init function is not define"
                exit 1
        fi

        "${MODEL_NAME}"::init
}

function verify_usb_port() {
        if ! check_leading_zero_digit $TARGET_USB_PORT; then
                echo "Invaild USB port format."
                exit 1
        fi

        if [[ $TARGET_USB_PORT -lt 0 || $TARGET_USB_PORT -ge $NUM_OF_USB ]]; then
                echo "Invalid USB port."
                exit 1
        fi
}

function verify_state() {
        if ! check_leading_zero_digit $TARGET_STATE; then
                echo "Invalid power state format."
                exit 1
        fi

        if [[ $TARGET_STATE -lt 0 || $TARGET_STATE -ge $NUM_OF_PWR_STATE ]]; then
                echo "Invalid power state."
                exit 1
        fi
}

function main() {
        script_params "$@"
        script_init

        case $TARGET_OPCODE in
        $TARGET_GET_USB_STATE_OPCODE)
                verify_usb_port

                if [[ ! $(type -t "${MODEL_NAME}"::get_usb_pwr_state) == function ]]; then
                        echo "${MODEL_NAME} get_usb_pwr_state function is not define"
                        exit 1
                fi

                "${MODEL_NAME}"::get_usb_pwr_state $TARGET_USB_PORT
                ;;
        $TARGET_SET_USB_PWR_STATE_OPCODE)
                verify_usb_port
                verify_state

                if [[ ! $(type -t "${MODEL_NAME}"::set_usb_pwr_state) == function ]]; then
                        echo "${MODEL_NAME} set_usb_pwr_state function is not define"
                        exit 1
                fi

                "${MODEL_NAME}"::set_usb_pwr_state $TARGET_USB_PORT $TARGET_STATE
                ;;
        *)
                script_usage
                exit 1
                ;;
        esac
}

main $@
