#!/bin/sh
# BG95 Cellular Watchdog with Exponential Backoff
# Handles USB TTY recovery, PDP reactivation, modem reset

MODEM_TTY="/dev/ttyUSB2"
LOCK="/var/lock/modem.lock"
MWAN3_LOCK="/tmp/mwan3-modem.lock"
MAX_AT_ERRORS=3
MAX_AT_ATTEMPTS=5
CHECK_INTERVAL=60  # periodic check interval (seconds)

# --- Backoff configuration ---
INITIAL_DELAY=5       # initial delay between PDP attempts (seconds)
MAX_DELAY=300         # maximum delay between attempts (seconds)
BACKOFF_FACTOR=2      # exponential factor

# --- AT command function with retries ---
send_at() {
    local cmd="$1"
    local max_errors=${2:-$MAX_AT_ERRORS}
    local delay=${3:-1}
    local attempts=0
    local errors=0
    local resp=""

    while true; do
        resp=$(echo -e "$cmd\r" | socat - $MODEM_TTY,raw,echo=0,crnl,waitlock=$LOCK 2>/dev/null)
        resp=$(echo "$resp" | grep "[A-Za-z0-9]" | sed -e 's/^\s*//;s/\s*$//')

        if echo "$resp" | grep -q "^OK$"; then
            echo "$resp"
            return 0
        elif echo "$resp" | grep -q "^ERROR$"; then
            errors=$((errors+1))
            if [ $errors -ge $max_errors ]; then
                echo "$resp"
                return 1
            fi
            sleep $delay
        else
            attempts=$((attempts+1))
            if [ $attempts -ge $MAX_AT_ATTEMPTS ]; then
                echo "$resp"
                return 1
            fi
            sleep $delay
        fi
    done
}

# --- Recover USB TTY if missing or invalid state ---
recover_usb_tty() {
    if [ ! -c "$MODEM_TTY" ]; then
        logger -t bg95-watchdog "$MODEM_TTY missing or stale, recovering USB..."
        rm -f "$MODEM_TTY"

        # reset cell modem
        gpioset --mode=time --sec=2 `gpiofind PD7`=1 ; gpioset `gpiofind PD7`=0
        sleep 10
    fi
}

# --- Activate PDP with exponential backoff ---
activate_pdp_backoff() {
    local delay=$INITIAL_DELAY

    while true; do
        # check if PDP is currently active
        cgact=$(send_at "AT+CGACT?" $MAX_AT_ERRORS 1)
        if echo "$cgact" | grep -q "+CGACT: 1,1"; then
            logger -t bg95-watchdog "PDP context active"
            return 0
        fi

        # activate PDP context
        logger -t bg95-watchdog "PDP not active, attempting activation..."
        send_at "AT+CGACT=1,1" $MAX_AT_ERRORS 1

        sleep $delay
        # exponential backoff
        delay=$((delay*BACKOFF_FACTOR))
        [ $delay -gt $MAX_DELAY ] && delay=$MAX_DELAY

        # software reset modem if retry delay exceeds threshold
        if [ $delay -ge $MAX_DELAY ]; then
            logger -t bg95-watchdog "Max backoff reached, resetting modem..."
            send_at "AT+CFUN=1,1" $MAX_AT_ERRORS 1
            sleep 10
            delay=$INITIAL_DELAY
        fi
    done
}

cleanup() {
    rm -f "$LOCK" "$MWAN3_LOCK"
    exit 0
}
trap cleanup INT TERM

# --- MWAN3 hook mode ---
if [ -n "$ACTION" ]; then
    case "$ACTION" in
        offline)
            if [ "$INTERFACE" = "wwan0" ]; then
                echo $$ > "$MWAN3_LOCK"
                logger -t bg95-watchdog "MWAN3 detected wwan0 offline"
                recover_usb_tty
                cereg=$(send_at "AT+CEREG?" $MAX_AT_ERRORS 1)
                logger -t bg95-watchdog "CEREG: $cereg"
                activate_pdp_backoff
                rm -f "$MWAN3_LOCK"
            fi
            exit 0
            ;;
    esac
fi

# --- Standalone periodic watchdog ---
while true; do
    [ -f "$MWAN3_LOCK" ] && continue  # Skip this cycle
    recover_usb_tty

    cereg=$(send_at "AT+CEREG?" $MAX_AT_ERRORS 1)
    if echo "$cereg" | grep -q "+CEREG: 0,1\|+CEREG: 0,5"; then
        activate_pdp_backoff
    fi

    sleep $CHECK_INTERVAL
done
