
WOWL_LOG="/nvram/wowl.log"
WIFI_CP_CACHED_FILE="/tmp/wifi_cp_cached"

is_product_eu() {
    echo "$1" | egrep -q '^(E6|EY|E4|D4|FJ|GK|H4|FK|06|23|2E|S00[4567]|S01[0W]|S07[FGHJ]|S08[2345])$'
}

is_product_latam() {
    echo "$1" | egrep -q '^(E5|E3|F2|07|24|2D|S066|S067|S011|S00R|S03N|S03M|S07X|S07W)$'
}

is_product_ph() {
    echo "$1" | egrep -q '^(FL)$'
}

get_device_pid() {
   local PID
   
   PID=$(pcedit -q pid 2>/dev/null | tail -1)
   
   if [ "$PID" == "(unset)" ]; then
       # Look for a cached file since reading from custom package is expensive
       if [ -f "$WIFI_CP_CACHED_FILE" ]; then
           var=$(cat $WIFI_CP_CACHED_FILE)
           set -- $var
           PID=$2
       else
           PID=$(cpiread -q "info/PID" 2> /dev/null)
       fi
    fi
    
    echo $PID
}

get_platform_type() {
    local PLATFORM_TYPE

    # Look for a cached file since reading from custom package is expensive
    if [ -f "$WIFI_CP_CACHED_FILE" ]; then
        var=$(cat $WIFI_CP_CACHED_FILE)
        set -- $var
        PLATFORM_TYPE=$4
    else
        PLATFORM_TYPE=$(cpiread -q "platform_type" 2> /dev/null)
    fi

   echo $PLATFORM_TYPE
}

set_wifi_mac() {

    if [ "$(get_platform_type)" == "TV" ]; then
        echo "TV products use Wi-Fi modules that come with pre-set MAC address"
        return
    fi
    
    ### Set Wifi MAC address:
    ### Selection order is: mfg-override, persist-config, wlan chip OTP, test default
    unset WMAC
    if [ -f /nvram/mfg_mac.txt ]; then
        WMAC=`cat /nvram/mfg_mac.txt`
    else
        PC_WMAC=$(pcedit -q wifimac 2>/dev/null | tail -1)
        if [ $PC_WMAC ] && [ $PC_WMAC != "(unset)" ]; then
            WMAC=$PC_WMAC
        else
            # sanity check for expected result, don't want bad WMAC
            OTP_WMAC=`cat /sys/class/net/$STAINTF/address`
            if [ $OTP_WMAC ]; then
                WMAC=`echo $OTP_WMAC | tr [a-z] [A-Z]`
                PREFIX=${WMAC:0:8}
                if [ "$PREFIX" == "00:E0:4C" ]; then
                    unset WMAC
                fi
            fi
        fi
    fi
    if [ ! $WMAC ]; then
        # Make it clear no valid MAC address has been provided!
        for shout in 1 2 3; do
            echo "!!!!!! WARNING: Wifi MAC address has not been configured !!!!!!"
            sleep 1
        done
        WMAC=b8:3e:59:ba:ba:01
    fi
    echo $WMAC >/tmp/wifimac
    ifconfig $STAINTF hw ether "$WMAC"
    ifconfig $STAINTF | grep '^$STAINTF'

    if [ $P2PINTF ]; then
        FIRST_BYTE_WMAC="0x${WMAC:0:2}"
        FIRST_BYTE_WMAC=`printf "%x\n" $((FIRST_BYTE_WMAC | 2))`
        WMAC2=$FIRST_BYTE_WMAC"${WMAC:2:15}"
        ifconfig $P2PINTF hw ether "$WMAC2"
        ifconfig $P2PINTF | grep '^$P2PINTF'
    fi
}


#
# Retrieves information about the region for the current product ID.
#
# Outputs:
#  $1 = country code
#  $2 = country name
#  $3 = channel plan
#
get_wlan_region_info() {

    local DEVICE_PID_ COUNTRY_CODE_ CHAN_PLAN_ FILE_COUNTRY_ CC_IS_LANGUAGE_

    DEVICE_PID_=$(get_device_pid)
 
    # Country Code and Channel Plan
    # TODO: Move this logic to custom package
    if is_product_eu $DEVICE_PID_; then
        # Per Atmel, EU country code should be DE
        COUNTRY_CODE_="DE"
        CHAN_PLAN_="0x42"
        FILE_COUNTRY_="DEFAULT"
    elif is_product_ph $DEVICE_PID_; then
        COUNTRY_CODE_="PH"
        CHAN_PLAN_="0x42"
        FILE_COUNTRY_="PHILIPPINES"
    elif is_product_latam $DEVICE_PID_; then
        COUNTRY_CODE_=`cat system.conf | grep CountryCode | cut  -d"=" -f2`

        # Someone decided that "country code" can actually be a language. Don't Ask...
        CC_IS_LANGUAGE_=false
        if [ $COUNTRY_CODE_ == "ES" ] || [ $COUNTRY_CODE_ == "US" ]; then
            CC_IS_LANGUAGE_=true
        fi

        if [ $COUNTRY_CODE_ == "BR" ]; then
            FILE_COUNTRY_="BRAZIL"
        else
            # If we don't know which LatAM country it is by now, set it to the lowest power (CHILE)
            if [ ! $COUNTRY_CODE_ ] || [ "$CC_IS_LANGUAGE_" = true ]; then
                FILE_COUNTRY_="CHILE"
            else
                FILE_COUNTRY_="MEXICO"
            fi

            # Other than Brazil, LatAm PID uses CC = "MX" for the driver parameter
            COUNTRY_CODE_="MX"
        fi

        # No matter what, LatAm channel plan is 0x29
        CHAN_PLAN_="0x29"
    else
        #For everyone else (US/Canada) CC=US
        COUNTRY_CODE_="US"
        CHAN_PLAN_="0x43"
        FILE_COUNTRY_="DEFAULT"
    fi

    # limit to only development box
    if [ $dev_mode_enabled = 1 ]
    then
        # Override CountryCode if /media/ext1\:/0vErR1DeC0untryC0de.txt file is present. 
        if [ -f /media/ext1\:/0vErR1DeC0untryC0de.txt ] && [ `cat /media/ext1\:/0vErR1DeC0untryC0de.txt | grep "CHAN_PLAN"` ]; then 
            CHAN_PLAN_=`cat /media/ext1\:/0vErR1DeC0untryC0de.txt | grep "CHAN_PLAN"  | cut -d  "=" -f2`
        fi
    fi

    eval $1=$COUNTRY_CODE_
    eval $2=$FILE_COUNTRY_
    eval $3=$CHAN_PLAN_
}

init_wlan_test_shell() {
    if [ -f /nvram/run_wifi_test_shell ]; then
        WiFiShell &> /dev/null &
        rm -f /nvram/auth_token
        rm -f /nvram/run_wifi_test_shell
    fi
    return 0
}

#
# Wait for wpa_supplicant to start.
#
# Arguments:
# $1 = interface name (e.g. wlan0)
#
wait_wlan_supplicant() {                                                             
    local TIMEOUT                                                                    
                                                                                     
    # Arg: interface name                                                            
    TIMEOUT=100                                                                      
    while ! wpa_cli -p/tmp/wpa_ctrl -i $1 ping 2>/dev/null | grep PONG >/dev/null; do
        echo "Supplicant: waiting for $1..."                      
        [ $TIMEOUT = "0" ] && return 1                            
        let TIMEOUT=$TIMEOUT-1                                   
        usleep 100000                                            
    done                                                        
    return 0                                                    
}

#
# Start wpa_supplicant
#
# Arguments:
#   $1 = interface name (e.g.wlan0)
#
init_wlan_supplicant() {
    local SUPP_CONF SUPP_DEBUG INTF ARGS PID

    INTF=$1
  
    echo "=== Wifi: starting wpa_supplicant"
    SUPP_CONF=/etc/wpa-supp.conf
    # Only try to load wpa-supp.conf from nvram if the device is in dev mode
    if [ "$roku_dev_mode" = 'dev=1' ]; then
        [ -f /nvram/wpa-supp.conf ] && SUPP_CONF=/nvram/wpa-supp.conf
        [ -f /nvram/supp-debug ] && SUPP_DEBUG="-df/dev/kmsg"
    fi

    ARGS="-Dnl80211 -i$INTF -c $SUPP_CONF -B $SUPP_DEBUG"
    echo "--- Wifi wpa_supplicant $ARGS"
    wpa_supplicant $ARGS
    PID=`ps -ef | grep wpa_supplicant | grep $INTF | tr -s ' ' | cut -d ' ' -f2`
    if wait_wlan_supplicant $INTF; then
        echo $PID >/tmp/wifi-supp-pid
        echo -16 >/proc/$PID/oom_adj
        chown -R app /tmp/wpa_ctrl
        # Test mode: launch DHCP client in background
        if [ -f /nvram/wifi-udhcpc ]; then
            killall -q udhcpc
            udhcpc -i $INTF -b -s /etc/udhcpc.sh
        fi

	# Be more aggressive with WPS enrollee sending EAPOL start
	# messages. The default is a 30 second timeout - change it
	# to 2 seconds.
	wpa_cli -p /tmp/wpa_ctrl -i$INTF SET EAPOL::startPeriod 2
	wpa_cli -p /tmp/wpa_ctrl -i$INTF SET EAPOL::maxStart 15
    else
        echo "Wifi: WPA supplicant failed to start!" > /dev/kmsg
        return 2
    fi
    return 0
}

kill_wlan_supplicant() {
    local PID

    echo "=== Wifi: killing wpa_supplicant"
    PID=$(cat /tmp/wifi-supp-pid)
    [ $PID ] && kill $PID && rm /tmp/wifi-supp-pid
}

check_wlan_interface() {
    ifconfig $1 > /dev/null 2>&1
    return $?
}

wowl_test() {
    echo "=== Wifi: enter WOWL test mode ==="
    iwpriv $1 wow_mode enable
}

wowl_reason() {
    REASON=$(cut -d" " -f3 /proc/net/rtl$1/$2/wow_wake_reason)
    echo "--- Wifi: WOWL wake reason $REASON"
    DAT=`date -Isec`
    echo "$DAT wowl-reason RT-$REASON" >> $WOWL_LOG
}

wowl_log() {
    if [ -f $WOWL_LOG ]; then
        tail -16 $WOWL_LOG >/tmp/wowl.log
        cp /tmp/wowl.log $WOWL_LOG
    fi
}

#
# Some SoCs require specific configuration outside of normal WiFi intialization.
#
# Arguments:
#   $1 = platform (e.g. longview)
#   $2 = module (e.g. 8812bu)
#
set_platform_config() {
    local PLATFORM MODULE

    PLATFORM=$1
    MODULE=$2
    
    if [ $PLATFORM == "longview" ] || [ $PLATFORM == "midland" ] || [ $PLATFORM == "elpaso2k" ]; then
        lsusb | grep 0bda | cut -d " " -f 2 > /sys/devices/platform/roku_wifi/wifi_set_port
    elif [ $PLATFORM == "malone" ] || [ $PLATFORM == "camden" ] || [ $PLATFORM == "camden2k" ]; then
        # Set fast_connect for the USB physical port that matches logical device.
        # Expects single digit hub numbers but allows multi-digit device numbers
        # (which can occur if multiple enumerations happen).
        if [ $MODULE == "8812bu" ]; then
            MODULE_ID="b812"
        elif [ $MODULE == "8812cu" ]; then
            MODULE_ID="c812"
        else
            echo "Wifi: set_platform_config: Unsupported module $MODULE"
            return # unsupported module
        fi
        for EHCI in `ls -d /sys/devices/Mstar-ehci-?`; do
            USB=`ls -d $EHCI/usb?`
            echo $USB
            for PORT in `ls -d $USB/?-*`; do
                if grep 0bda $PORT/idVendor && grep $MODULE_ID $PORT/idProduct; then
                    echo 1 > $EHCI/fast_connect
                    return
                fi
            done
        done
    fi
}

is_manu_image() {
    local manu_image
    
    manu_image=0
    if [ -b /dev/mtdblock_SWUPMAGIC ]; then
        # Running on MSU partitions; teeloader tells us if we're manufacturing
        if grep -wq "manuimage=1" /proc/cmdline; then
           manu_image=1
        fi
    else
        # Look for a file that's only in acramfs2
        if [ -f /etc/manufacturing ]; then
	   manu_image=1
        fi
    fi
    echo $manu_image
}    

is_rescue_image() {
    local rescue_image
    
    rescue_image=0
    if [ -b /dev/mtdblock_SWUPMAGIC ]; then
        if grep -wq "RESCUEMODE=yes" /etc/profile; then
	    rescue_image=1
        fi
    fi
    echo $rescue_image
}

# Checks whether the efuse for this module is programmed and valid.
#
# Arguments:
#   $1 = interface (e.g. wlan0)
#   $2 = module (e.g. 8812bu)
#
# Returns:
#   "valid" or "fail"
#
check_efuse() {
    local INTF MODULE

    INTF=$1
    MODULE=$2
    
    RESET_ENABLE_BYTE=`rtwpriv $INTF efuse_get rmap,8,01 | cut -d":" -f2`
    RESET_ENABLE_INVALID=$((($RESET_ENABLE_BYTE & 0x2) == 0))

    RFE_TYPE_BYTE=`rtwpriv $INTF efuse_get rmap,CA,01 | cut -d":" -f2`
    RFE_TYPE_INVALID=$(($RFE_TYPE_BYTE == 0xFF))

    EFUSE_UNPROGRAMMED=$(($RFE_TYPE_INVALID && $RESET_ENABLE_INVALID))
    if [ $EFUSE_UNPROGRAMMED -eq 1 ] && [ "$(is_manu_image)" = "1" ]; then
        # The wifi driver won't load if the efuse isn't programmed, which
        # can happen during manufacturing.  Try reloading the driver
        # using a 'fake' RFE type in order to program the efuse
        echo "efuse is unprogrammed. Reload driver with possibly incorrect RFE type"

        if [ -f /etc/wifi/${MODULE}_rfe_type ]; then
            RFE_TYPE=`cat /etc/wifi/${MODULE}_rfe_type | grep RFE_TYPE | cut -d"=" -f2`
        else
            # Efuse not programmed and there isn't a file containing a valid RFE value so fail hard
            # Rather than using default and failing in a subtle and hard to detect way
            RFE_TYPE="fail"
        fi
    elif [ $RFE_TYPE_INVALID -eq 1 ]; then
        # efuse is programmed, but RFE type is invalid (i.e. some 8812au modules).
        # Also, custom package did not have the value to use. Hard-code it to 0x3.
        if [ $MODULE == "8812au" ]; then
            RFE_TYPE=0x3
        else
            RFE_TYPE="valid"
        fi
    else
        RFE_TYPE="valid"
    fi

    # echo to be used as return value
    echo $RFE_TYPE
}

wait_proc_finished() {
    wait_pid=$1
    LIMIT=250
    while ps ${wait_pid} > /dev/null; do
        usleep 1000;
        let LIMIT=LIMIT-1
        if [ $LIMIT -le 0 ]; then
            echo "timed out waiting for process to finish ($wait_pid)"
            return 1
        fi
    done
    return 0
}

wlan_reset() {
    if [ -f /tmp/wifi_reset_pid ]; then
        read wifi_reset_pid < /tmp/wifi_reset_pid
        rm /tmp/wifi_reset_pid
        echo "waiting for wifi_reset ($wifi_reset_pid)"
        wait_proc_finished $wifi_reset_pid
    else
        # Hard reset the Wifi chip
        /bin/wifi_reset
    fi
}

wlan_detect() {
    modid=`lsusb | grep -oE "0bda:....|0a5c:...."`
    # Search the enumerated USB devices for a Realtek chip.  One trick here is
    # that the Fruitland platform actually has two chips.  Thus, we need to
    # iterate through the list, ignoring the second chip (8811cu) which is
    # intialized seperately in the S63wifi script (note that we can't ignore
    # the 8811cu entirely as other platforms it is the first chip and should be
    # initialized here).
    # This definitely is a hack - the right way to do this is to put this information
    # in custom package so we definitively know what chip(s) each platform has.
    for mod in $modid
    do
      case $mod in
          "0bda:f179") MODULE=8188fu ;;
          "0bda:881a") MODULE=8812au ;;
          "0bda:8812") MODULE=8812au ;;
          "0bda:b812") MODULE=8812bu ;;
          "0bda:b82c") MODULE=8822bu ;;
          "0bda:c811") [ $PLATFORM != "fruitland4k" ] && [ $PLATFORM != "galveston" ] && MODULE=8811cu ;;
          "0bda:c812") MODULE=8812cu ;;
          "0bda:c82c") MODULE=8822cu ;;
          0a5c:*) echo "Found Broadcom WiFi"; return 0;;
      esac
      if [ $MODULE ]; then
        break
      fi
    done
    if [ -z "$MODULE" ]; then
        return 1
    fi
    echo $MODULE > /tmp/wlan-module
    DRIVER=realtek

	cat <<-EOF > /tmp/wlan.conf
	type: rtk
	chip: $MODULE
	sta: $STAINTF
	p2p: $P2PINTF
	EOF

    ! echo $PATH | grep -q $DRIVER && export PATH=/lib/wlan/$DRIVER:$PATH
    return 0
}

wlan_wait_for_hardware() {
    echo "=== Wifi: waiting for chip..."
    TIMEOUT=60
    while ! wlan_detect ; do
        usleep 100000
        let TIMEOUT=TIMEOUT-1
        if [ $TIMEOUT -le 0 ]; then
	    echo "=== Failed to find chip"
            rm -f /tmp/wlan-driver
	    return 1
	fi
    done
    if [ -z "$MODULE" ]; then
        rm -f /tmp/wlan-driver
        return 1
    fi

    return 0
}

kill_async_start() {
    # Find processes that may have been started in a ( ) subshell and kill them.
    my_name=`basename $0`
    for pid in `pgrep $my_name` ; do
        if [ "$pid" != "$$" ] ; then
            kill $pid
        fi
    done
}

has_dual_wifi() {
       # Look for a cached file since reading from custom package is expensive
       if [ -f "$WIFI_CP_CACHED_FILE" ]; then
           var=$(cat $WIFI_CP_CACHED_FILE)
           set -- $var
           has_dual_wifi=$1
       else
           has_dual_wifi=$(cpiread -q "flags/MODEL_HAS_DUAL_WIFI" 2> /dev/null)
       fi
       echo $has_dual_wifi
}

get_realtek_wlan_module_params() {
    local DOPTS ADAPTIVITY ADAPTIVITY_EN SET_DEBUG TV_MODEL
    
    # Basic options: no-DFS + 20MHz BW
    DOPTS="rtw_channel_plan=$CHAN_PLAN rtw_bw_mode=0x00 rtw_go_country_string=$COUNTRY_CODE"

    if [ -f "$WIFI_CP_CACHED_FILE" ]; then
       var=$(cat $WIFI_CP_CACHED_FILE)
       set -- $var
       ADAPTIVITY=$3
    else
        ADAPTIVITY=$(cpiread -q "wifi/adaptivity" 2> /dev/null | tail -n1)        
    fi
    
    ADAPTIVITY=$(cpiread -q "wifi/adaptivity" 2> /dev/null | tail -n1)
    ADAPTIVITY_EN="0"
    [ $ADAPTIVITY == "enable" ] && ADAPTIVITY_EN="1"

    DOPTS="$DOPTS rtw_adaptivity_en=$ADAPTIVITY_EN"

    SET_DEBUG="rtw_drv_log_level=3"

    if [ $dev_mode_enabled = 1 ]; then
        [ -f /nvram/wlan-debug-info ] && SET_DEBUG="rtw_drv_log_level=4"
    fi
    DOPTS="$DOPTS $SET_DEBUG"

    # Limit speaker peripherals to a single spatial stream
    if [ "$PLATFORM" == "glaze" ] || [ "$PLATFORM" == "smiley" ]
    then
        if [ -f /nvram/allow-all-wifi-rates ]
        then 
            DOPTS="$DOPTS rtw_ht_rx_mcs_rate=0xff"
        else
            # Also limit Glaze/Smiley QAM-16 (MCS4 / OFDM_36M) or simpler
            DOPTS="$DOPTS rtw_ht_rx_mcs_rate=0x1f"
            DOPTS="$DOPTS rtw_sup_rates_override=0x3ff"
            DOPTS="$DOPTS rtw_vht_enable=0"
        fi
    fi

    echo $DOPTS
}

set_realtek_wlan_proc_parameters() {
    local IGNORE_GO_AND_LOW_RSSI MODULE INTF

    MODULE=$1
    INTF=$2
    
    # Reject all P2P GOs and APs with RSSI < -90
    IGNORE_GO_AND_LOW_RSSI=1
    if [ $PLATFORM == "glaze" ] || [ $PLATFORM == "smiley" ] || [ $PLATFORM == "galveston" ]; then
	# For peripherals, we need to discover GOs on primary device
        IGNORE_GO_AND_LOW_RSSI=0
    fi
    echo $IGNORE_GO_AND_LOW_RSSI 10 > /proc/net/rtl$MODULE/$INTF/ignore_go_and_low_rssi_in_scan

    # Modify scan for better P2P WRC response. This is only different from the
    # driver default by forcing return to home channel after every scanned channel.
    echo 50 255 255 100 1 > /proc/net/rtl$MODULE/$INTF/scan_param
}

set_wifi_remote_wowl_address() {
    # Get MAC used by Wifi remote for magic packet wake-up (WOWL):
    iw $1 info | grep addr | cut -d" " -f2 >/tmp/wifimac
}