#!/bin/sh
##########################
# History:
#	2016/06/20 - [Tao Wu] Create file
#
# Copyright (c) 2016 Ambarella International LP
#
# This file and its contents ("Software") are protected by intellectual
# property rights including, without limitation, U.S. and/or foreign
# copyrights. This Software is also the confidential and proprietary
# information of Ambarella International LP and its licensors. You may not use, reproduce,
# disclose, distribute, modify, or otherwise prepare derivative works of this
# Software or any portion thereof except pursuant to a signed license agreement
# or nondisclosure agreement with Ambarella International LP or its authorized affiliates.
# In the absence of such an agreement, you agree to promptly notify and return
# this Software to Ambarella International LP.
#
# This file includes sample code and is only for internal testing and evaluation.  If you
# distribute this sample code (whether in source, object, or binary code form), it will be
# without any warranty or indemnity protection from Ambarella International LP or its affiliates.
#
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
# MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL AMBARELLA INTERNATIONAL LP OR ITS AFFILIATES BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; COMPUTER FAILURE OR MALFUNCTION; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
###############
WL_SETUP_VERSION="0.1.9"

mode=$1

############# Configuration it ###############
DEVICE=wlan0
DIR_CONFIG=/tmp/config
IP_CONFIG=$DIR_CONFIG/ip.conf

## use wpa_passphrase generate pmk is faster, enable by default
WPA_PASSPHRASE_GEN_PMK=1

############# Exit Error Number ###############
ERRNO_OK=0
ERRNO_PARAM=1
ERRNO_ENV=2
ERRNO_SSID_NOT_FOUND=3
#ERRNO_PASSWORD_WRONG=4
#ERRNO_SSID_REPEATED=5
############# Broadcom WiFi module bcmdhd CONFIG ###############
BCMDHD_CONFIG=$DIR_CONFIG/dhd.conf
#########################################

#############  Basic Function ##################
usages()
{
	echo "Version: ${WL_SETUP_VERSION}"
	echo "This script used to Setup/Stop WiFi STA/AP mode with wl tools which from Broadcom"
	echo "usage:                      $0 [sta|ap] <SSID> <Password|0> [open|wpa2|wpa|wpawpa2|wep|0] <Channel>"
	echo ""
	echo "Example:"
	echo "Connect To AP[open]:        $0 sta <SSID>"
	echo "Connect To AP[encrypt]:     $0 sta <SSID> <Password>"
	echo "Connect To AP[WEP]:         $0 sta <SSID> <Password>   0                <Index>"
	echo "Connect To Hidden[open]:    $0 sta <SSID>"
	echo "Connect To Hidden[encrypt]: $0 sta <SSID> <Password>"
	echo "Setup AP[open]:             $0 ap  <SSID>     0       open              <Channel>"
	echo "Setup AP[encrypt]:          $0 ap  <SSID> <Password> [wpa2|wpa|wpawpa2] <Channel>"
	echo ""
	echo "Stop STA/AP mode:           $0 stop"

	exit $ERRNO_OK
}

do_cmd()
{
	local command="$1"
	echo "CMD=[${command}]"
	${command}
}

check_env()
{
	if [ "$(which wl)" = "" ]; then
		echo "Not Found wl tools"
		exit $ERRNO_ENV
	fi
}

check_iface()
{
	iface=$1
	if [ "$(ifconfig "$iface")" = "" ]; then
		echo "Not Found Interface $iface"
		exit $ERRNO_ENV
	fi
}

kill_apps()
{
	#the max times to kill app
	local KILL_NUM_MAX=10

	for app in "$@"
	do
		local kill_num=0
		while [ "$(pgrep "${app}")" != "" ]
		do
			if [ $kill_num -ge $KILL_NUM_MAX ]; then
				echo "Please try execute \"killall ${app}\" by yourself"
				exit $ERRNO_ENV
			else
				killall -9 "${app}"
				sleep 1
			fi
			kill_num=$((kill_num + 1));
		done
	done
}
################ Driver #####################

get_device_id()
{
	MODULE_ID=UNKNOWN
	if [ -e /sys/module/bcmdhd ]; then
		MODULE_ID=BCMDHD
	fi
}

generate_bcmdhd_conf()
{
	local local_ssid=$1
	local local_channel=$2

	if [ -f ${BCMDHD_CONFIG} ]; then
		grep -v 'roam_cache_ssid' ${BCMDHD_CONFIG} | grep -v 'roam_cache_channel' > ${BCMDHD_CONFIG}.tmp
		mv ${BCMDHD_CONFIG}.tmp ${BCMDHD_CONFIG}
	fi
	echo "roam_cache_ssid=${local_ssid}" >> ${BCMDHD_CONFIG}
	echo "roam_cache_channel=${local_channel}" >> ${BCMDHD_CONFIG}
}

generate_driver_conf()
{
	if [ "${MODULE_ID}" = "BCMDHD" ]; then
		generate_bcmdhd_conf "$1" "$2"
	fi
}

################   IP address / DNS  ###################
generate_dns_conf()
{
	echo "#  DO NOT EDIT THIS FILE BY HAND " > /etc/resolv.conf
	for i in "$@"; do
		echo "nameserver $i" >> /etc/resolv.conf
	done
}

generate_ip_conf()
{
# Generate ip.conf
#ip_addr:192.168.1.28
#Bcast:192.168.1.255
#Mask:255.255.255.0
#Gateway:192.168.1.1
	echo "$2"
	echo "ip_$2" > $IP_CONFIG
	echo "$3"
	echo "$3" >> $IP_CONFIG
	echo "$4"
	echo "$4" >> $IP_CONFIG
	gw=$(route -n |grep ${DEVICE} |awk 'NR==1{print $2}')
	echo "Gateway:$gw" >> $IP_CONFIG
}

get_ip_address()
{
# Get IP Address, 1. DHCP 2. Static IP
	if [ -f $IP_CONFIG ]; then
		LOCAL_IP=$(grep ip_addr ${IP_CONFIG})
		LOCAL_IP=${LOCAL_IP##*:}
		BCAST=$(grep Bcast ${IP_CONFIG})
		BCAST=${BCAST##*:}
		MASK=$(grep Mask ${IP_CONFIG})
		MASK=${MASK##*:}
		GATEWAY=$(grep Gateway ${IP_CONFIG})
		GATEWAY=${GATEWAY##*:}
		echo "Static IP: $LOCAL_IP "
		echo "Broad Cast: $BCAST "
		echo "Net Mask: $MASK "
		echo "Gate Way: $GATEWAY "
		ifconfig ${DEVICE} "${LOCAL_IP}" broadcast "${BCAST}" netmask "${MASK}"
		route add default gw "${GATEWAY}" ${DEVICE}
	else
		dns_server=$(udhcpc -i${DEVICE} -A 1 -b 2>&1 |grep dns|awk '{print $3}')
		generate_dns_conf "${dns_server}"
		local_ip_config=$(ifconfig ${DEVICE} | grep "inet addr" | cut -c 11-)
		generate_ip_conf ${local_ip_config}
	fi
}
################  WL Utility ###################

start_wl()
{
	ifconfig ${DEVICE} up
	do_cmd "wl up"

	# Set CAM(constantly awake) mode to avoid packet loss.
	do_cmd "wl PM 0"
}

stop_wl()
{
	kill_apps udhcpc
	do_cmd "wl disassoc"
	do_cmd "wl down"
	ifconfig ${DEVICE} down
}

################  WL STA ###################
## Global variable ##
wsec=0
auth=0
sup_wpa=0
wpa_auth=0
amode="open"

scan_result=""
scan_entry=""
#############

wl_scan_results()
{
	local	local_ssid="$1"

	echo "CMD=[wl scan \"${local_ssid}\"]"
	wl scan "${local_ssid}"

	#sleep 6

	#the max times to wait scan result
	local SCAN_CHECK_MAX=10
	local SCAN_CHECK_INTERVAL=0.5
	local scan_check=0

	while [ $scan_check -lt $SCAN_CHECK_MAX ]
	do
		scan_result=$(wl scanresults)

		scan_not_ready=$(echo "${scan_result}" | grep 'Not Ready')
		if [ "${scan_not_ready}" = "" ]; then
			#scan_entry=$(echo "${scan_result}" | sed -n "/^SSID.*${local_ssid}/,/^$/p")
			scan_entry=$(echo "${scan_result}")
			if [ "${scan_entry}" != "" ]; then
				break
			fi
		fi

		scan_check=$((scan_check + 1))
		sleep 0.5
	done

	echo "=============================================="
	echo "Scan [ $scan_check x $SCAN_CHECK_INTERVAL] seconds"
	if [ "${scan_entry}" != "" ]; then
		echo "${scan_entry}"
	else
		echo "${scan_result}" | grep ^SSID
		echo "Can not find SSID [${local_ssid}]"
		exit $ERRNO_SSID_NOT_FOUND
	fi
	echo "=============================================="
}

wl_get_value_CCMP_TKIP()
{
	local local_auth_info="$1"

	local_unicast_cipher=$(echo "${local_auth_info}" | awk -F: '/.*unicast.*cipher.*:/{print $2}' | tr -d '[:space:]')

	TKIP=$(echo "${local_unicast_cipher}" | grep TKIP)
	CCMP=$(echo "${local_unicast_cipher}" | grep CCMP)

	if [ "${TKIP}" != "" ]; then
		wsec=0x2
	fi
	if [ "${CCMP}" != "" ]; then
		wsec=$((wsec + 0x4))
	fi
}

wl_connect_ap()
{
	local	local_ssid="$1"
	local	local_passwd="$2"
#	local	local_encrypt="$3"
	local	local_index=$4

	if [ "${local_ssid}" = "" ]; then
		echo "Please input SSID"
		exit $ERRNO_PARAM
	fi
	mkdir -p $DIR_CONFIG
	start_wl
	do_cmd "wl disassoc"

	##  Scan SSID ##
	echo "STA: SSID[${local_ssid}]"
	wl_scan_results "${local_ssid}"

	#local local_bssid=$(echo "${scan_entry}" | awk '/^BSSID/{print $2}')
	#local local_multi_cipher=$(echo "${scan_entry}" | awk -F: '/.*multi.*cipher.*:/{print $2}' | tr -d '[[:space:]]')
	#local local_unicast_cipher=$(echo "${scan_entry}" | awk -F: '/.*unicast.*cipher.*:/{print $2}' | tr -d '[[:space:]]')
	#local local_akm_suite=$(echo "${scan_entry}" | awk -F: '/.*AKM.*Suites.*:/{print $2}' | tr -d '[[:space:]]')

	is_wpa=$(echo "${scan_entry}" | grep ^WPA )
	is_wpa2=$(echo "${scan_entry}" | grep ^RSN )

	if [ "${is_wpa}" != "" ]; then
		local_wpa_info=$(echo "${scan_entry}" | sed -n "/^WPA.*/,/.*Capabilities.*/p")
		#echo "${local_wpa_info}"
		wl_get_value_CCMP_TKIP "${local_wpa_info}"
		sup_wpa=1
		wpa_auth=0x4
		amode="wpapsk"
	fi
	if [ "${is_wpa2}" != "" ]; then
		local_wpa2_info=$(echo "${scan_entry}" | sed -n "/^RSN.*/,/.*Capabilities.*/p")
		#echo "${local_wpa2_info}"
		wl_get_value_CCMP_TKIP "${local_wpa2_info}"
		sup_wpa=1
		wpa_auth=$((wpa_auth + 0x80))
		amode="wpa2psk"
	fi
	if [ ${wsec} -eq 8 ]; then
		wsec=6;
	fi
	if [ $sup_wpa -eq 1 ]; then
		if [ "$local_passwd" = "" ]; then
			echo "Please input password in encrypt ap"
			exit $ERRNO_PARAM
		fi
	fi
	if [ $sup_wpa -eq 0 ]; then
		local_is_wep=$(echo "${scan_entry}" | grep Capability | grep WEP )
		if [ "${local_is_wep}" != "" ]; then
			wsec=1
			#auth=2
		fi
	fi

	do_cmd "wl -i ${DEVICE} wsec ${wsec}"
	if [ ${auth} -ne 0 ]; then
		do_cmd "wl -i ${DEVICE} auth ${auth}"
	fi
	if [ ${sup_wpa} -ne 0 ]; then
		do_cmd "wl -i ${DEVICE} sup_wpa ${sup_wpa}"
	fi
	if [ ${wpa_auth} -ne 0 ]; then
		do_cmd "wl -i ${DEVICE} wpa_auth ${wpa_auth}"
	fi
	if [ "${local_passwd}" != "" ] && [ $sup_wpa -eq 1 ]; then
		if [ ${WPA_PASSPHRASE_GEN_PMK} -eq 1 ] && [ "$(which wpa_passphrase)" != "" ]; then
			gen_pmk_str=$(wpa_passphrase ${local_ssid} ${local_passwd} | grep -v '#psk' | grep psk)
			gen_pmk_hex=${gen_pmk_str#*psk=}
			do_cmd "wl -i ${DEVICE} set_pmk ${gen_pmk_hex}"
		else
			do_cmd "wl -i ${DEVICE} set_pmk ${local_passwd}"
		fi
	fi
	if [ "${local_is_wep}" != "" ]; then
		do_cmd "wl addwep ${local_index} ${local_passwd}"
	fi
	if [ "${amode}" != "" ]; then
		echo "CMD=[wl -i ${DEVICE} join \"${local_ssid}\" imode bss amode ${amode}]"
		wl -i ${DEVICE} join "${local_ssid}" imode bss amode ${amode}
	fi
}

wl_check_status()
{
	local local_ssid="$1"
	## the max times to check wl status
	local CONNECT_NUM_MAX=80
	local CHECK_INTERVAL=0.5

	local connect_num=0
	local connect_sleep_num=$((CONNECT_NUM_MAX/2))

	#sleep 5
	while [ $connect_num -lt $CONNECT_NUM_MAX ]
	do
		wl_status=$(wl status)
		wl_no_ass=$(echo "${wl_status}" | grep 'Not associated')
		if [ "${wl_no_ass}" = "" ]; then
			break
		fi

		connect_num=$((connect_num + 1));
		if [ $connect_num -gt $connect_sleep_num ]; then
			printf "%s" '.'
			sleep $CHECK_INTERVAL
		fi
	done

	local local_channel=${wl_status#*Channel:}
	generate_driver_conf "${local_ssid}" "${local_channel}"

	echo "Check time [${connect_num}/${CONNECT_NUM_MAX} x ${CHECK_INTERVAL}], wl_status:"
	echo "${wl_status}"
	echo "=============================================="

	get_ip_address

	# Set Fast Power Save
	do_cmd "wl PM 2"

	echo "wl_setup Finished"
}

clear_config()
{
	## Not remove this if use the previous ip as static ip
	rm -rf ${IP_CONFIG}
}
################   Main  ###################

## Show usage when no parameter
if [ $# -eq 0 ]; then
	usages
fi

## Setup new config before clear old one
if [ $# -gt 1 ]; then
	clear_config
fi

check_env
get_device_id

case ${mode} in
	"stop")
		stop_wl
		;;
	sta)
		sta_ssid=$2
		sta_password=$3
		sta_encrypt=$4
		sta_index=$5
		check_iface "${DEVICE}"
		wl_connect_ap "${sta_ssid}" "${sta_password}" "${sta_encrypt}" "${sta_index}"
		wl_check_status "${sta_ssid}"
		;;
	ap)
		echo "Not implement yet, exit."
		exit $ERRNO_PARAM
		;;
	*)
		echo "Please Select Mode [sta|ap] or stop"
		exit $ERRNO_PARAM
		;;
esac

########################################