#!/bin/sh
# tlp - rf switch functions
#
# Copyright (c) 2012 Thomas Koch <linrunner at gmx.net>
# This software is licensed under the GPL v2.

# ----------------------------------------------------------------------------
# Constants
readonly RFKILL="$(which rfkill 2> /dev/null)"
readonly RFKD="/dev/rfkill"
readonly TEST="$(which test 2> /dev/null)"

readonly ALLDEV="bluetooth wifi wwan"
readonly STATEDIR="/var/lib/tlp"
readonly STATEFILE=$STATEDIR/rfkill-saved

# ----------------------------------------------------------------------------
# Functions
get_devc () { # $1: rftype; retval $devc
    local i 
    
    check_sysfs "get_devc" "/sys/class/rfkill"

    devc=""
    devs=254
    rfkdev="1"
    devon="1"
    devoff="0"
    
    case $1 in
        wwan|bluetooth)
            for i in /sys/class/rfkill/rfkill* ; do
                if [ -f $i/type ] && [ "$(cat $i/type)" = "$1" ]; then 
                    devc="$i/state"
                    echo_debug "rf" "get_devc($1) = $devc"
                    return 0
                fi
            done
            ;;

        wifi)
            for i in /sys/class/rfkill/rfkill* ; do
                if [ -f $i/type ] && [ "$(cat $i/type)" = "wlan" ]; then 
                    devc="$i/state"
                    echo_debug "rf" "get_devc($1) = $devc"
                    return 0
                fi
            done
 
            for i in /sys/bus/pci/drivers/ipw2?00/*/rf_kill; do
                if [ -f $i ]; then
                    devc="$i"
                    rfkdev="0"
                    devon="0"
                    devoff="1"
                    echo_debug "rf" "get_devc($1) = $devc"
                    return 0
                fi
            done
            ;;
        
        *)
            echo "Error: unknown device type \"$1\"" 1>&2
            echo_debug "rf" "get_devc($1).unknown_type"
            return 0
            ;;
    esac
    
    echo_debug "rf" "get_devc($1).not_present"
    
    return 0
}

err_no_root_priv () { 
    echo "Error: missing root privilege." 1>&2
    echo_debug "rf" "$1.missing_root_privilege"
    
    return 0
}

get_devs () { # $1: rftype; retval $devs: 0=off/1=on
    if [ -n "$devc" ]; then
        devs=$(cat $devc) 
        [ "$rfkdev" = "0" ] && devs=$(($devs ^ $devoff))
    fi

    echo_debug "rf" "get_devs($1) = $devs"
    
    return 0
}

device_state () { # $1: rftype; retval $devc, $devs: 0=off/1=on 
    echo_debug "rf" "device_state($1)"
    
    get_devc $1
    get_devs $1 
}


device_on () { # $1: rftype
    echo_debug "rf" "device_on($1)"

    get_devc $1

    # quit if no device
    [ -z "$devc" ] && return 0

    get_devs $1

    # quit if device is already on
    [ "$devs" = "1" ] && return 0

    if [ "$rfkdev" = "1" ] && [ -x "$RFKILL" ]; then
        if $TEST "$(id -u)" = "0" -o -w $RFKD ; then
            echo_debug "rf" "device_on($1).rfkill"
            $RFKILL unblock $1
        else
            err_no_root_priv "device_on($1).rfkill"
        fi
    else
        if [ "$(id -u)" = "0" ]; then
            echo_debug "rf" "device_on($1).devc"
            echo -n $devon > $devc
        else
            err_no_root_priv "device_on($1).devc"
        fi
    fi

    get_devs $1

    return 0
}

device_off () { # $1: rftype
    echo_debug "rf" "device_off($1)"

    get_devc $1

    # quit if no device
    [ -z "$devc" ] && return 0

    get_devs $1

    # quit if device is already off
    [ "$devs" = "0" ] && return 0

    if [ "$rfkdev" = "1" ] && [ -x "$RFKILL" ]; then
        if $TEST "$(id -u)" = "0" -o -w $RFKD ; then
            echo_debug "rf" "device_off($1).rfkill"
            $RFKILL block $1
        else
            err_no_root_priv "device_off($1).rfkill"
        fi
    else
        if [ "$(id -u)" = "0" ]; then
            echo_debug "rf" "device_off($1).devc"
            echo -n $devoff > $devc
        else
            err_no_root_priv "device_off($1).devc"
        fi
    fi

    get_devs $1

    return 0
}

device_toggle () { # $1: rftype
    echo_debug "rf" "device_toggle($1)"

    get_devc $1

    # quit if no device
    [ -z "$devc" ] && return 0

    get_devs $1

    if [ "$rfkdev" = "1" ] && [ -x "$RFKILL" ]; then
         if $TEST "$(id -u)" = "0" -o -w $RFKD ; then
            echo_debug "rf" "device_toggle($1).rfkill"
            case $devs in
                0) $RFKILL unblock $1 ;;
                1) $RFKILL block $1   ;;                
                *) ;;
            esac
        else
            err_no_root_priv "device_toogle($1).rfkill"
        fi
    else
        if [ "$(id -u)" = "0" ]; then
            echo_debug "rf" "device_toggle($1).devc"
            case $devs in
                0) echo -n $devon > $devc ;;
                1) echo -n $devoff > $devc ;;
                *) ;;
            esac
        else
            err_no_root_priv "device_toogle($1).devc"
        fi
    fi

    get_devs $1

    return 0
}

device_set_state () { # $1: rftype, $2: state 0=off/1=on
    echo_debug "rf" "device_set_state($1, $2)"

    get_devc $1

    # quit if no device
    [ -z "$devc" ] && return 0

    # quit if state not 0 nor 1
    [ "$2" = "0" ] || [ "$2" = "1" ] || return 0

    if [ "$rfkdev" = "1" ] && [ -x "$RFKILL" ]; then
        if $TEST "$(id -u)" = "0" -o -w $RFKD ; then
            echo_debug "rf" "device_set_state($1, $2).rfkill"
            case $2 in
                0) $RFKILL block $1 ;;
                1) $RFKILL unblock $1 ;;
            esac
        else
            err_no_root_priv "device_set_state($1).rfkill"
        fi
    else
        if [ "$(id -u)" = "0" ]; then
            echo_debug "rf" "device_set_state($1, $2).devc"
            case $2 in
                0) echo -n $devoff > $devc ;;
                1) echo -n $devon > $devc ;;
            esac
        else
            err_no_root_priv "device_set_state($1, $2).devc"
        fi
    fi

    get_devs $1

    return 0
}

echo_device_state () { # $1: rftype, $2: state
    case $1 in
        bluetooth)
            devstr="bluetooth"
            ;;
            
        wifi)
            devstr="wifi     "
            ;;
        
        wwan)
            devstr="wwan     "
            ;;
            
        *)
            devstr=$1
            ;;
    esac

    case $2 in
        0) 
            echo "$devstr = off (software)"
            ;;

        1) 
            echo "$devstr = on"
            ;;
            
        2) 
            echo "$devstr = off (hardware)"
            ;;
            
        254) 
            echo "$devstr = none (no device)"
            ;;

        *)
            echo "$devstr = unknown state \"$state\""
    esac

    return 0
}

save_device_states () {
    local dev

    echo_debug "rf" "save_device_states"

    # create empty state file
    mkdir -p $STATEDIR
    > $STATEFILE

    # iterate over all possible devices -> save state in file
    for dev in $ALLDEV; do
        device_state $dev
        echo "$dev $devs" >> $STATEFILE
    done
    
    return 0
}

restore_device_states () {
    local sline

    echo_debug "rf" "restore_device_states"
    
    if [ -f $STATEFILE ]; then
        set_run_flag $LOCK_RDW
        
        # read state file
        while read sline; do
            set -- $sline # read dev, state into $1, $2
            device_set_state $1 $2 
        done < $STATEFILE

        reset_run_flag $LOCK_RDW

        return 0
    else
        return 1
    fi
}

init_radio_devices () { # $1: start/stop - called from init scripts
    local dev devs2stop restore

    # save/restore mode is disabled by default
    restore=${RESTORE_DEVICE_STATE_ON_STARTUP:-0}

    echo_debug "rf" "init_radio_devices($1): restore=$restore"

    if [ "$restore" = "1" ]; then
        # "save/restore" mode
        case $1 in
            start)
                restore_device_states
                if [ $? = 0 ]; then
                    echo "Radio device states restored."
                else
                    echo "No saved radio device states found."
                fi
                ;;
                
            stop)
                save_device_states
                echo "Radio device states saved."
                ;; 
        esac
    else
        # "disable on startup/shutdown" mode
        case $1 in
            start) devs2stop=$DEVICES_TO_DISABLE_ON_STARTUP ;;
            stop)  devs2stop=$DEVICES_TO_DISABLE_ON_SHUTDOWN ;;
        esac

        if [ -n "$devs2stop" ]; then
            echo -n "Disabling radios:"
            for dev in $devs2stop; do
                echo -n " $dev"
                device_off $dev
            done
            echo "."
        fi

        # clean up: discard state file 
        rm -f $STATEFILE 2> /dev/null
    fi
    
    return 0
}

