#!/bin/sh
# helper functions sourced from other scripts

: ${GPIO:=/usr/local/bin/telem/gpio.sh}
: ${rev:=$(cat /tmp/telem/board/rev)}

modem_is_alive(){
    local USB_MODEM="nothing"
    [ -f /var/local/telem/usb-modem-vidpid ] && source /var/local/telem/usb-modem-vidpid
    lsusb | grep -q -- "$USB_MODEM"
}

telit_modem_rev4_turnOFF(){
    # Do not call outside this file.
    if $GPIO --is-on MODEM_PWR_MON; then
        # MODEM_PWR is inverted
        $GPIO -s MODEM_PWR; sleep 4; $GPIO -c MODEM_PWR; sleep 1;
        # wait for modem to shut down
        i="0"
        while $GPIO --is-on MODEM_PWR_MON && [ "$i" -lt "15" ]; do
            i=$((i+1))
            sleep 1
        done
        # HW SHUTDOWN Unconditional: if modem is still on
        $GPIO --is-on MODEM_PWR_MON && $GPIO -t RST_MODEM
    fi
}

telit_modem_rev4_reset(){
    # Do not call outside this file.
    # From HE910 Hardware User Guide
    # MODEM_PWR = ON_OFF pin
    # RST_MODEM = HW_SHUTDOWN pin ?
    lsmod | grep -q -- "cdc.*acm" || modprobe cdc-acm
    # turn off modem if it is on
    telit_modem_rev4_turnOFF
    # turn back on
    if $GPIO --is-off MODEM_PWR_MON; then
        i="0"
        while $GPIO --is-off MODEM_PWR_MON && [ "$i" -lt "2" ]; do
            i=$((i+1))
            $GPIO -s MODEM_PWR; sleep 6; $GPIO -c MODEM_PWR
            sleep 1
            # HW SHUTDOWN Unconditional: if modem is still off
            $GPIO --is-off MODEM_PWR_MON && $GPIO -t RST_MODEM
        done
        # Start AT CMD part not needed here
    fi
    $GPIO -s MODEM_USB_PWR
}

huawei_modem_reset(){
    lsmod | grep -q -- "option"   || modprobe option
    lsmod | grep -q -- "cdc.*acm" || modprobe cdc-acm

    if modem_is_alive; then
        # Modem is alive, lets try reset pin.

        # HUAWEI Reset pulse timing:
        # * 50-100ms
        # RESIN_N pin must not be pulled down for more than 1s.
        $GPIO -s RST_MODEM
        usleep 55000 # sleep 55ms
        $GPIO -c RST_MODEM
    else
        # modem is not alive, lets try to reset power

        # HUAWEI Power on timing sequence:
        # MU609: 3-5s
        # MU709: 7s
        # ME909: 14s
        # Do not toggle RESIN_N pin during the power on sequence.
        # Pulling RESIN_N pin low will extend time for module startup.
        if $GPIO --is-on MODEM_PWR; then
            # Modem is not really turning off?
            # Wait for capacitor to discharge?
            # TESTED: It is a hardware issue
            #   gpio.sh -c MODEM_PWR; sleep 1; gpio.sh -s MODEM_PWR
            #   command works without long sleep if power pin has a resistor to pull down power faster.
            $GPIO -c MODEM_PWR
            logg "Will sleep for 45 seconds now"
            sleep 25 # additional 20 seconds outside this if
        fi
        # keep it off for 20s, because we dont know if it has been off long enough.
        sleep 20
        $GPIO -s MODEM_PWR
        sleep 14
    fi
}

quectel_modem_reset(){
    lsmod | grep -q -- "option"   || modprobe option

    if modem_is_alive; then
        # Modem is alive, lets try reset pin.

        # QUECTEL Reset pulse timing:
        # * 150-460ms
        $GPIO -s RST_MODEM
        usleep 230000 # sleep 230ms
        $GPIO -c RST_MODEM
        sleep 1
    else
        # modem is not alive, lets try to reset power

        # There is no clear information for module power on timings!
        # Each manual gives different information!
        # Fig 12 of "Quectel_EC21_Hardware_Design_V1.4.pdf"
        # QUECTEL Power on timing sequence:
        # EC21: 13s+ for usb
        # Before turning on RESET_N (PERST#) must be high?
        if $GPIO --is-on MODEM_PWR; then
            # Fig 13 of "Quectel_EC21_Hardware_Design_V1.4.pdf"
            # Power down procedure lasts 30s+
            # There is no access to PWRKEY on PCIe module?
            # TODO use AT comand
            $GPIO -c MODEM_PWR
            logg "Will sleep for 45 seconds now"
            sleep 25 # additional 20 seconds outside this if
        fi
        # keep it off for 20s, because we dont know if it has been off long enough.
        sleep 20
        $GPIO -c RST_MODEM
        $GPIO -s MODEM_PWR
        sleep 15
    fi
}

usingPCI_module(){
    # Telem-GWM rev8 (two prototypes) is first board with PCI module
    # Telem-GWM rev8 is excluded for simplicity
    test "$rev" -ge "9" && return

    # Old board with onboard Telit modem
    false
}

modem_reset(){
    # Do not call outside this file.
    if usingPCI_module; then
        # TODO FIX?: enabling MODEM_PWR with 'gpio.sh -e' will set this gpio to '0' (OFF)
        $GPIO -e MODEM_PWR
        $GPIO -e RST_MODEM
        # currently we support only huawei & quectel modems
        if $GPIO --is-on MODEM_PWR; then
            # do modem specific reset
            local USB_MODEM="nothing"
            [ -f /var/local/telem/usb-modem-vidpid ] && source /var/local/telem/usb-modem-vidpid
            case "$USB_MODEM" in
                12d1*)
                    huawei_modem_reset
                    ;;
                2c7c*)
                    quectel_modem_reset
                    ;;
                *)
                    huawei_modem_reset
                    ;;
            esac
        else
            # just turn on
            lsmod | grep -q -- "option"   || modprobe option
            lsmod | grep -q -- "cdc.*acm" || modprobe cdc-acm
            $GPIO -c RST_MODEM
            $GPIO -s MODEM_PWR
            sleep 7 # extra sleep
        fi
        sleep 7
    else
        $GPIO -e MODEM_USB_PWR
        $GPIO -e MODEM_PWR_MON
        $GPIO -e MODEM_PWR
        $GPIO -e RST_MODEM

        $GPIO -c MODEM_USB_PWR
        telit_modem_rev4_reset
        $GPIO -s MODEM_USB_PWR
    fi
}

# You have to wait for modem at least +10 seconds after modem device appears or chat script will fail
wait_for_modem()
{
    ret=0
    reset_lockFile="/tmp/modem_reset_2min_lock"  # file is touched before reset
    tty_cmdFile="/var/local/telem/modem_tty_cmd" # file is modified after device is created by mdev

    # find -mmin -0.2 will always be true if file epoch is < 12 seconds (current workaround: S01hwclock should set clock at lest to year 2000)
    # -mmin +0 avoids files whose date is in the future or to close to epoch 0

    # wait for 18 seconds after reset command
    while [ "$(find "$reset_lockFile" -mmin -0.3 -mmin +0 2>/dev/null)" == "$reset_lockFile" ]; do sleep 2; ret=1; done
    # wait for 12 seconds after modem device was created
    while [ "$(find -L "$tty_cmdFile" -mmin -0.2 -mmin +0 2>/dev/null)" == "$tty_cmdFile"    ]; do sleep 2; ret=1; done
    return $ret
}

# there are at least three scripts where modem reseting is initiated
# so it would be good idea to use modem_reset_lock
modem_reset_lock()
{
    reset_lockFile="/tmp/modem_reset_2min_lock"
    # Reset modem if there is no lock file newer than ~1.5 minutes
    if [ "$(find "$reset_lockFile" -mmin -1.5 -mmin +0 2>/dev/null)" != "$reset_lockFile" ] ; then
        touch "$reset_lockFile"
        logg "Resetting modem"
        modem_reset
    else
        # just wait
        logg "Can't reset modem, it is already being reset"
    fi
    wait_for_modem
}

modem_turnOFF()
{
    if usingPCI_module; then
        lsmod | grep -q -- "option"   || modprobe option
        lsmod | grep -q -- "cdc.*acm" || modprobe cdc-acm
        $GPIO -e MODEM_PWR; $GPIO -c MODEM_PWR
        # TODO: turn off with AT command
    else
        $GPIO -e MODEM_USB_PWR
        $GPIO -e MODEM_PWR_MON
        $GPIO -e MODEM_PWR
        $GPIO -e RST_MODEM

        $GPIO -c MODEM_USB_PWR
        telit_modem_rev4_turnOFF
    fi
}
