#include <linux/etherdevice.h>
#include <linux/netdevice.h>
#include <net/netlink.h>
#include <linux/wireless.h>
#include <linux/nl80211.h>
#include <net/iw_handler.h>
#include <uapi/linux/if_arp.h>
#include <linux/vmalloc.h>
#include "aicwf_debug.h"
#include "rwnx_msg_tx.h"
#include "aicwf_wext_linux.h"
#include "rwnx_defs.h"

#define WLAN_CAPABILITY_ESS		(1<<0)
#define WLAN_CAPABILITY_IBSS	(1<<1)
#define WLAN_CAPABILITY_PRIVACY (1<<4)

#define IEEE80211_HT_CAP_SGI_20			0x0020
#define IEEE80211_HT_CAP_SGI_40			0x0040

#define IEEE80211_HE_CH_BW_SET_160_80P80 		1 << 4
#define IEEE80211_HE_CH_BW_SET_160_IN_5G 		1 << 3
#define IEEE80211_HE_CH_BW_SET_40_AND_80_IN_5G 	1 << 2
#define IEEE80211_HE_CH_BW_SET_40_IN_2_4G 		1 << 1

#if WIRELESS_EXT < 17
	#define IW_QUAL_QUAL_INVALID   0x10
	#define IW_QUAL_LEVEL_INVALID  0x20
	#define IW_QUAL_NOISE_INVALID  0x40
	#define IW_QUAL_QUAL_UPDATED   0x1
	#define IW_QUAL_LEVEL_UPDATED  0x2
	#define IW_QUAL_NOISE_UPDATED  0x4
#endif

#define MAX_WPA_IE_LEN (256)


/*				20/40/80,	ShortGI,	MCS Rate  */
const u16 VHT_MCS_DATA_RATE[3][2][30] = {
	{	
		{
			13, 26, 39, 52, 78, 104, 117, 130, 156, 156,
			26, 52, 78, 104, 156, 208, 234, 260, 312, 312,
			39, 78, 117, 156, 234, 312, 351, 390, 468, 520
		},	/* Long GI, 20MHz */
		{
			14, 29, 43, 58, 87, 116, 130, 144, 173, 173,
			29, 58, 87, 116, 173, 231, 260, 289, 347, 347,
			43,	87, 130, 173, 260, 347, 390,	433,	520, 578
		}
	},		/* Short GI, 20MHz */
	{	
		{
			27, 54, 81, 108, 162, 216, 243, 270, 324, 360,
			54, 108, 162, 216, 324, 432, 486, 540, 648, 720,
			81, 162, 243, 324, 486, 648, 729, 810, 972, 1080
		}, 	/* Long GI, 40MHz */
		{
			30, 60, 90, 120, 180, 240, 270, 300, 360, 400,
			60, 120, 180, 240, 360, 480, 540, 600, 720, 800,
			90, 180, 270, 360, 540, 720, 810, 900, 1080, 1200
		}
	},		/* Short GI, 40MHz */
	{	
		{
			59, 117,  176, 234, 351, 468, 527, 585, 702, 780,
			117, 234, 351, 468, 702, 936, 1053, 1170, 1404, 1560,
			176, 351, 527, 702, 1053, 1404, 1580, 1755, 2106, 2340
		},	/* Long GI, 80MHz */
		{
			65, 130, 195, 260, 390, 520, 585, 650, 780, 867,
			130, 260, 390, 520, 780, 1040, 1170, 1300, 1560, 1734,
			195, 390, 585, 780, 1170, 1560, 1755, 1950, 2340, 2600
		}	/* Short GI, 80MHz */
	}	
};


/*HE 20/40/80,MCS Rate  */
const u16 HE_MCS_DATA_RATE[3][30] = {
	{
		9, 17, 26, 34, 52, 69, 77, 86, 103, 115,
		129, 143, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0
	},	/* 20MHz */
	{
		17, 34, 52, 69, 103, 138, 155, 172, 207, 229,
		258, 286, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0
	}, 	/* 40MHz */
	{
		36, 72,  108, 144, 216, 288, 324, 360, 432, 480,
		540, 601, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0
	},	/* 80MHz */
	
};


#if WIRELESS_EXT >= 17
struct iw_statistics iwstats;

static struct iw_statistics *aicwf_get_wireless_stats(struct net_device *dev)
{
	int tmp_level = -100;
	int tmp_qual = 0;
	int tmp_noise = 0;

	struct rwnx_vif* rwnx_vif = netdev_priv(dev);
	struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw;
	struct rwnx_sta *sta = NULL; 

	union rwnx_rate_ctrl_info *rate_info;
	struct mm_get_sta_info_cfm cfm;

	AICWFDBG(LOGTRACE, "%s Enter", __func__);

	if(rwnx_vif->sta.ap){
		sta = rwnx_vif->sta.ap;
		rwnx_send_get_sta_info_req(rwnx_hw, sta->sta_idx, &cfm);
			rate_info = (union rwnx_rate_ctrl_info *)&cfm.rate_info;
			tmp_level = cfm.rssi;
	}


	iwstats.qual.level = tmp_level;
	iwstats.qual.qual = tmp_qual;
	iwstats.qual.noise = tmp_noise;
	iwstats.qual.updated = 0x07;
	iwstats.qual.updated = iwstats.qual.updated | IW_QUAL_DBM;

	return &iwstats;
}
#endif


static int aicwf_get_name(struct net_device *dev,
			   struct iw_request_info *info,
			   union iwreq_data *wrqu, char *extra)
{
	struct rwnx_vif* rwnx_vif = netdev_priv(dev);
	struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw;
	struct rwnx_sta *sta = NULL; 

	union rwnx_rate_ctrl_info *rate_info;
	struct mm_get_sta_info_cfm cfm;

	AICWFDBG(LOGTRACE, "%s Enter", __func__);

	if(rwnx_vif->sta.ap){
		sta = rwnx_vif->sta.ap;
		rwnx_send_get_sta_info_req(rwnx_hw, sta->sta_idx, &cfm);
			rate_info = (union rwnx_rate_ctrl_info *)&cfm.rate_info;
		
			switch (rate_info->formatModTx) {
			case FORMATMOD_NON_HT:
			case FORMATMOD_NON_HT_DUP_OFDM:
				snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11bg");
				break;
			case FORMATMOD_HT_MF:
			case FORMATMOD_HT_GF:
				snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11n");
				break;
			case FORMATMOD_VHT:
				snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11ac");
				break;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
			case FORMATMOD_HE_MU:
			case FORMATMOD_HE_SU:
			case FORMATMOD_HE_ER:
				snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11ax");
				break;
#else
			//kernel not support he
			case FORMATMOD_HE_MU:
			case FORMATMOD_HE_SU:
			case FORMATMOD_HE_ER:
				snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11ax");
				break;
#endif
			}

		
	}else{
		snprintf(wrqu->name, IFNAMSIZ, "unassociated");
	}


	return 0;
}

static int aicwf_get_freq(struct net_device *dev,
			   struct iw_request_info *info,
			   union iwreq_data *wrqu, char *extra)
{
	struct rwnx_vif* rwnx_vif = netdev_priv(dev);


	AICWFDBG(LOGTRACE, "%s Enter", __func__);

	if(rwnx_vif->sta.ap){
		wrqu->freq.m = rwnx_vif->sta.ap->center_freq * 100000;
		wrqu->freq.e = 1;
		wrqu->freq.i = ieee80211_frequency_to_channel(rwnx_vif->sta.ap->center_freq);
	}else{
		wrqu->freq.m = 2412 * 100000;
		wrqu->freq.e = 1;
		wrqu->freq.i = 1;
	}

	return 0;
}

static int aicwf_get_mode(struct net_device *dev, struct iw_request_info *a,
			   union iwreq_data *wrqu, char *b)
{

	AICWFDBG(LOGTRACE, "%s Enter", __func__);

	wrqu->mode = IW_MODE_AUTO;
#if 0
	if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == _TRUE)
		wrqu->mode = IW_MODE_INFRA;
	else if ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == _TRUE) ||
		 (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == _TRUE))

		wrqu->mode = IW_MODE_ADHOC;
	else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == _TRUE)
		wrqu->mode = IW_MODE_MASTER;
	else if (check_fwstate(pmlmepriv, WIFI_MONITOR_STATE) == _TRUE)
		wrqu->mode = IW_MODE_MONITOR;
	else
		wrqu->mode = IW_MODE_AUTO;
#endif

	return 0;

}


static int aicwf_get_range(struct net_device *dev,
			    struct iw_request_info *info,
			    union iwreq_data *wrqu, char *extra)
{
	struct iw_range *range = (struct iw_range *)extra;
	u16 val = 0;

	AICWFDBG(LOGTRACE, "%s Enter", __func__);


	wrqu->data.length = sizeof(*range);
	memset(range, 0, sizeof(*range));

	/* Let's try to keep this struct in the same order as in
	 * linux/include/wireless.h
	 */

	/* TODO: See what values we can set, and remove the ones we can't
	 * set, or fill them with some default data.
	 */

	/* ~5 Mb/s real (802.11b) */
	range->throughput = 5 * 1000 * 1000;

	/* TODO: Not used in 802.11b?
	*	range->min_nwid;	 Minimal NWID we are able to set  */
	/* TODO: Not used in 802.11b?
	*	range->max_nwid;	 Maximal NWID we are able to set  */

	/* Old Frequency (backward compat - moved lower ) */
	/*	range->old_num_channels;
	 *	range->old_num_frequency;
	 * 	range->old_freq[6];  Filler to keep "version" at the same offset  */

	/* signal level threshold range */

	/* Quality of link & SNR stuff */
	/* Quality range (link, level, noise)
	 * If the quality is absolute, it will be in the range [0 ; max_qual],
	 * if the quality is dBm, it will be in the range [max_qual ; 0].
	 * Don't forget that we use 8 bit arithmetics...
	 *
	 * If percentage range is 0~100
	 * Signal strength dbm range logical is -100 ~ 0
	 * but usually value is -90 ~ -20
	 */
	range->max_qual.qual = 100;
#ifdef CONFIG_SIGNAL_DISPLAY_DBM
	range->max_qual.level = (u8)-100;
	range->max_qual.noise = (u8)-100;
	range->max_qual.updated = IW_QUAL_ALL_UPDATED; /* Updated all three */
	range->max_qual.updated |= IW_QUAL_DBM;
#else /* !CONFIG_SIGNAL_DISPLAY_DBM */
	/* percent values between 0 and 100. */
	range->max_qual.level = 100;
	range->max_qual.noise = 100;
	range->max_qual.updated = IW_QUAL_ALL_UPDATED; /* Updated all three */
#endif /* !CONFIG_SIGNAL_DISPLAY_DBM */

	/* This should contain the average/typical values of the quality
	 * indicator. This should be the threshold between a "good" and
	 * a "bad" link (example : monitor going from green to orange).
	 * Currently, user space apps like quality monitors don't have any
	 * way to calibrate the measurement. With this, they can split
	 * the range between 0 and max_qual in different quality level
	 * (using a geometric subdivision centered on the average).
	 * I expect that people doing the user space apps will feedback
	 * us on which value we need to put in each driver... */
	range->avg_qual.qual = 92; /* > 8% missed beacons is 'bad' */
#ifdef CONFIG_SIGNAL_DISPLAY_DBM
	/* TODO: Find real 'good' to 'bad' threshold value for RSSI */
	range->avg_qual.level = (u8)-70;
	range->avg_qual.noise = 0;
	range->avg_qual.updated = IW_QUAL_ALL_UPDATED; /* Updated all three */
	range->avg_qual.updated |= IW_QUAL_DBM;
#else /* !CONFIG_SIGNAL_DISPLAY_DBM */
	/* TODO: Find real 'good' to 'bad' threshol value for RSSI */
	range->avg_qual.level = 30;
	range->avg_qual.noise = 100;
	range->avg_qual.updated = IW_QUAL_ALL_UPDATED; /* Updated all three */
#endif /* !CONFIG_SIGNAL_DISPLAY_DBM */
#if 0
	range->num_bitrates = RATE_COUNT;

	for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++)
		range->bitrate[i] = rtw_rates[i];

	range->min_frag = MIN_FRAG_THRESHOLD;
	range->max_frag = MAX_FRAG_THRESHOLD;
#endif

	range->pm_capa = 0;

	range->we_version_compiled = WIRELESS_EXT;
	range->we_version_source = 16;

	/*	range->retry_capa;	 What retry options are supported
	 *	range->retry_flags;	 How to decode max/min retry limit
	 *	range->r_time_flags;	 How to decode max/min retry life
	 *	range->min_retry;	 Minimal number of retries
	 *	range->max_retry;	 Maximal number of retries
	 *	range->min_r_time;	 Minimal retry lifetime
	 *	range->max_r_time;	 Maximal retry lifetime  */
#if 0 
	for (i = 0, val = 0; i < rfctl->max_chan_nums; i++) {

		/* Include only legal frequencies for some countries */
		if (rfctl->channel_set[i].ChannelNum != 0) {
			range->freq[val].i = rfctl->channel_set[i].ChannelNum;
			range->freq[val].m = rtw_ch2freq(rfctl->channel_set[i].ChannelNum) * 100000;
			range->freq[val].e = 1;
			val++;
		}

		if (val == IW_MAX_FREQUENCIES)
			break;
	}
#endif
	range->num_channels = val;
	range->num_frequency = val;

	/* Commented by Albert 2009/10/13
	 * The following code will proivde the security capability to network manager.
	 * If the driver doesn't provide this capability to network manager,
	 * the WPA/WPA2 routers can't be choosen in the network manager. */

	/*
	#define IW_SCAN_CAPA_NONE		0x00
	#define IW_SCAN_CAPA_ESSID		0x01
	#define IW_SCAN_CAPA_BSSID		0x02
	#define IW_SCAN_CAPA_CHANNEL	0x04
	#define IW_SCAN_CAPA_MODE		0x08
	#define IW_SCAN_CAPA_RATE		0x10
	#define IW_SCAN_CAPA_TYPE		0x20
	#define IW_SCAN_CAPA_TIME		0x40
	*/

#if WIRELESS_EXT > 17
	range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
			  IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
#endif

#ifdef IW_SCAN_CAPA_ESSID /* WIRELESS_EXT > 21 */
	range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_TYPE | IW_SCAN_CAPA_BSSID |
		   IW_SCAN_CAPA_CHANNEL | IW_SCAN_CAPA_MODE | IW_SCAN_CAPA_RATE;
#endif



	return 0;

}


static char *aicwf_get_iwe_stream_mac_addr(struct rwnx_hw* rwnx_hw,
		struct iw_request_info *info, struct scanu_result_wext *scan_re,
		char *start, char *stop, struct iw_event *iwe)
{
	/*  AP MAC address */
	iwe->cmd = SIOCGIWAP;
	iwe->u.ap_addr.sa_family = ARPHRD_ETHER;

	if(scan_re->bss && &scan_re->bss->bssid[0]){
	memcpy(iwe->u.ap_addr.sa_data, scan_re->bss->bssid, ETH_ALEN);
	}

	start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_ADDR_LEN);
	return start;
}


static inline char *aicwf_get_iwe_stream_essid(struct rwnx_hw* rwnx_hw,
		struct iw_request_info *info, struct scanu_result_wext *scan_re,
		char *start, char *stop, struct iw_event *iwe)
{
	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload;
	const u8 *ie = mgmt->u.beacon.variable;
	u8 *ssid;
	int ssid_len = 0;

	//get ssid len form ie
	ssid_len = ie[1];

	//get ssid form ie
	ssid = (u8*)vmalloc(sizeof(char)* (ssid_len + 1));
	memset(ssid, 0, (ssid_len + 1));
	memcpy(ssid, &ie[2], ssid_len);

	
	//AICWFDBG(LOGDEBUG, "%s len:%d ssid:%s\r\n", __func__, ssid_len, ssid);

	/* Add the ESSID */
	iwe->cmd = SIOCGIWESSID;
	iwe->u.data.flags = 1;
	iwe->u.data.length = min((u16)ssid_len, (u16)32);
	start = iwe_stream_add_point(info, start, stop, iwe, ssid);

	vfree(ssid);

	return start;
}



static inline char *aicwf_get_iwe_stream_protocol(struct rwnx_hw* rwnx_hw,
		struct iw_request_info *info, struct scanu_result_wext *scan_re,
		char *start, char *stop, struct iw_event *iwe)
{
	u16 ht_cap = false; 
	u16 vht_cap = false;
	u16 he_cap = false;
	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload;
	u8 *payload = mgmt->u.beacon.variable;
	const u8 *ie_content;

	/* parsing HT_CAP_IE	 */
	ie_content = NULL;
	ie_content = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, payload, scan_re->ind->length);
	if (ie_content != NULL){
		ht_cap = true;
	}

	/* parsing VHT_CAP_IE	 */
	ie_content = NULL;
	ie_content = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, payload, scan_re->ind->length);
	if (ie_content != NULL){
		vht_cap = true;
	}
	
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)|| defined(CONFIG_HE_FOR_OLD_KERNEL)
	/* parsing HE_CAP_IE	 */
	ie_content = NULL;
	ie_content = cfg80211_find_ie(WLAN_EID_EXTENSION, payload, scan_re->ind->length);
	if (ie_content != NULL && ie_content[2] == WLAN_EID_EXT_HE_CAPABILITY){
		he_cap = true;
	}
#endif

	/* Add the protocol name */
	iwe->cmd = SIOCGIWNAME;

	if (ieee80211_frequency_to_channel(scan_re->ind->center_freq) > 14) {
		if (he_cap == true){
			snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11ax");
		}else if(vht_cap == true){
			snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11ac");
		}else{
			if (ht_cap == true)
				snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11an");
			else
				snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11a");
		}
	} else {
		if(he_cap == true){
			snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11ax");
		}else if (ht_cap == true){
			snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11bgn");
		}else{
			snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11bg");
		}
	}
	
	start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_CHAR_LEN);
	return start;
}



static inline char *aicwf_get_iwe_stream_rssi(struct rwnx_hw* rwnx_hw,
		struct iw_request_info *info, struct scanu_result_wext *scan_re,
		char *start, char *stop, struct iw_event *iwe)
{
	iwe->cmd = IWEVQUAL;

	iwe->u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED
		| IW_QUAL_NOISE_INVALID
		| IW_QUAL_DBM;

	iwe->u.qual.level = (u8)scan_re->ind->rssi;
	iwe->u.qual.qual = 100;//scan_re->bss->signal;
	iwe->u.qual.noise = 0;
	
	start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_QUAL_LEN);
	
	return start;
}

static inline char *aicwf_get_iwe_stream_chan(struct rwnx_hw* rwnx_hw,
		struct iw_request_info *info, struct scanu_result_wext *scan_re,
		char *start, char *stop, struct iw_event *iwe)
{
	/* Add frequency/channel */
	iwe->cmd = SIOCGIWFREQ;
	iwe->u.freq.m = scan_re->ind->center_freq * 100000;
	iwe->u.freq.e = 1;
	iwe->u.freq.i = ieee80211_frequency_to_channel(scan_re->ind->center_freq);
	start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_FREQ_LEN);

	return start;
}


static inline char *aicwf_get_iwe_stream_mode(struct rwnx_hw* rwnx_hw,
		struct iw_request_info *info, struct scanu_result_wext *scan_re,
		char *start, char *stop, struct iw_event *iwe)
{

	u16 cap = scan_re->bss->capability;
	/* Add mode */
	if (cap & (WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_ESS)) {
		iwe->cmd = SIOCGIWMODE;
		if (cap & WLAN_CAPABILITY_ESS)
			iwe->u.mode = IW_MODE_MASTER;
		else
			iwe->u.mode = IW_MODE_ADHOC;

		start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_UINT_LEN);
	}
	return start;

}

static inline char *aicwf_get_iwe_stream_encryption(struct rwnx_hw* rwnx_hw,
		struct iw_request_info *info, struct scanu_result_wext *scan_re,
		char *start, char *stop, struct iw_event *iwe)
{
	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload;
	const u8 *ie = mgmt->u.beacon.variable;
	u16 cap = scan_re->bss->capability;
	u8 *ssid;
	int ssid_len = 0;

	//get ssid len form ie
	ssid_len = ie[1];

	//get ssid form ie
	ssid = (u8*)vmalloc(sizeof(char)* (ssid_len + 1));
	memset(ssid, 0, (ssid_len + 1));
	memcpy(ssid, &ie[2], ssid_len);

	
	/* Add encryption capability */
	iwe->cmd = SIOCGIWENCODE;
	if (cap & WLAN_CAPABILITY_PRIVACY)
		iwe->u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
	else
		iwe->u.data.flags = IW_ENCODE_DISABLED;
	iwe->u.data.length = 0;
	start = iwe_stream_add_point(info, start, stop, iwe, ssid);
	vfree(ssid);
	
	return start;

}

static inline char *aicwf_get_iwe_stream_rate(struct rwnx_hw* rwnx_hw,
		struct iw_request_info *info, struct scanu_result_wext *scan_re,
		char *start, char *stop, struct iw_event *iwe)
{
	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload;
	u8 *payload = mgmt->u.beacon.variable;
	const u8 *ie_content;
	
	u16 mcs_rate = 0;
	u8 bw_40MHz = 0;
	u8 short_GI = 0;
	u16 max_rate = 0;
	u8 bw_160MHz = 0;
	
	u16 ht_cap = false;
	struct ieee80211_ht_cap *ht_capie;
	
	u16 vht_cap = false;
	u8 tx_mcs_map[2];
	u8 tx_mcs_index = 0;
	u16 vht_data_rate = 0;

	u16 he_cap = false;
	u8 he_ch_width_set = 0;
	u8 he_bw = 0;

	/* parsing HT_CAP_IE	 */
	ie_content = NULL;
	ie_content = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, payload, scan_re->ind->length);
	if (ie_content != NULL){
		ht_cap = true;
		ht_capie = (struct ieee80211_ht_cap *)(ie_content + 2);
		bw_40MHz = (ht_capie->cap_info & NL80211_CHAN_WIDTH_40) ? 1 : 0;
		short_GI = (ht_capie->cap_info & (IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40)) ? 1 : 0;
		memcpy(&mcs_rate, ht_capie->mcs.rx_mask, 2);
	}



	/* parsing VHT_CAP_IE	 */
	ie_content = NULL;
	ie_content = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, payload, scan_re->ind->length);
	if (ie_content != NULL){
		
		vht_cap = true;
		bw_160MHz = ((*(u8*)(ie_content + 2)) >> 2) & ((u8)(0xFF >> (8 - (2))));
		if (bw_160MHz){
			short_GI = ((*(u8*)(ie_content + 2)) >> 6) & ((u8)(0xFF >> (8 - (1))));
		}else{
			short_GI = ((*(u8*)(ie_content + 2)) >> 5) & ((u8)(0xFF >> (8 - (1))));
		}

		memcpy(tx_mcs_map, ((ie_content + 2)+8), 2);

		tx_mcs_index = (tx_mcs_map[0] & 0x0F) - 1;
		if(ieee80211_frequency_to_channel(scan_re->ind->center_freq) > 14){
			vht_data_rate = VHT_MCS_DATA_RATE[2][short_GI][tx_mcs_index];
		}else{
			vht_data_rate = VHT_MCS_DATA_RATE[1][short_GI][tx_mcs_index];
		}
		//TO DO:
		//need to counter antenna number for AC
	}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)|| defined(CONFIG_HE_FOR_OLD_KERNEL)
	/* parsing HE_CAP_IE	 */
	ie_content = NULL;
	/*0xFF+len+WLAN_EID_EXTENSION+playload */
	ie_content = cfg80211_find_ie(WLAN_EID_EXTENSION, payload, scan_re->ind->length);
	if (ie_content != NULL && ie_content[2] == WLAN_EID_EXT_HE_CAPABILITY){
		he_cap = true;
		he_ch_width_set = ie_content[8];
		if(he_ch_width_set & IEEE80211_HE_CH_BW_SET_160_80P80){
			he_bw = NL80211_CHAN_WIDTH_80;
		}else if(he_ch_width_set & IEEE80211_HE_CH_BW_SET_160_IN_5G){
			he_bw = NL80211_CHAN_WIDTH_160;
		}else if(he_ch_width_set & IEEE80211_HE_CH_BW_SET_40_AND_80_IN_5G){
			he_bw = NL80211_CHAN_WIDTH_40;
		}else if(he_ch_width_set & IEEE80211_HE_CH_BW_SET_40_IN_2_4G){
			he_bw = NL80211_CHAN_WIDTH_40;
		}else{
			he_bw = NL80211_CHAN_WIDTH_20;
		}
		//TO DO:
		//need to counter antenna number for AX
	}
#endif

	if(he_cap == true){
		if(he_bw == NL80211_CHAN_WIDTH_20){
			max_rate = 144;
		}else if(he_bw == NL80211_CHAN_WIDTH_40){
			max_rate = 287;
		}else if(he_bw == NL80211_CHAN_WIDTH_80){
			max_rate = 601;
		}else if(he_bw == NL80211_CHAN_WIDTH_160){
			max_rate = 1147;
		}
		max_rate = max_rate * 2;
	}else if(vht_cap == true){
		max_rate = vht_data_rate;
	}else if (ht_cap == true) {
		if (mcs_rate & 0x8000) /* MCS15 */
			max_rate = (bw_40MHz) ? ((short_GI) ? 300 : 270) : ((short_GI) ? 144 : 130);
	
		else if (mcs_rate & 0x0080) /* MCS7 */
			max_rate = (bw_40MHz) ? ((short_GI) ? 150 : 135) : ((short_GI) ? 72 : 65);
		else { /* default MCS7 */
			max_rate = (bw_40MHz) ? ((short_GI) ? 150 : 135) : ((short_GI) ? 72 : 65);
		}
	}

	iwe->cmd = SIOCGIWRATE;
	iwe->u.bitrate.fixed = iwe->u.bitrate.disabled = 0;
	iwe->u.bitrate.value = max_rate * 1000000;
	start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_PARAM_LEN);
	return start ;
}



int aic_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, u8 *wpa_ie, u16 *wpa_len)
{

	u8 authmode;
	u8 sec_idx;
	u8 wpa_oui[4] = {0x00, 0x50, 0xf2, 0x01};
	uint cnt = 0;

	/* Search required WPA or WPA2 IE and copy to sec_ie[ ] */

	cnt = 0;

	sec_idx = 0;

	while (cnt < in_len) {
		authmode = in_ie[cnt];

		if ((authmode == 0xdd/*_WPA_IE_ID_*/) && (memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4) == 0)) {
			if (wpa_ie)
				memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);

			*wpa_len = in_ie[cnt + 1] + 2;
			cnt += in_ie[cnt + 1] + 2; /* get next */
		} else {
			if (authmode == 0x30/*_WPA2_IE_ID_*/) {
				if (rsn_ie)
					memcpy(rsn_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);

				*rsn_len = in_ie[cnt + 1] + 2;
				cnt += in_ie[cnt + 1] + 2; /* get next */
			} else {
				cnt += in_ie[cnt + 1] + 2; /* get next */
			}
		}

	}


	return *rsn_len + *wpa_len;

}


static inline char *aicwf_get_iwe_stream_wpa_wpa2(struct rwnx_hw* rwnx_hw,
		struct iw_request_info *info, struct scanu_result_wext *scan_re,
		char *start, char *stop, struct iw_event *iwe)
{
	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload;
	u8 *payload = mgmt->u.beacon.variable;
	int buf_size = MAX_WPA_IE_LEN * 2;
	u8 *pbuf;

	u8 wpa_ie[255] = {0}, rsn_ie[255] = {0};
	u16 i, wpa_len = 0, rsn_len = 0;
	u8 *p;
	int out_len = 0;

	pbuf = (u8*)vmalloc(sizeof(u8) * buf_size);

	if (pbuf) {
		p = pbuf;

		/* parsing WPA/WPA2 IE */
			out_len = aic_get_sec_ie(payload , scan_re->ind->length, rsn_ie, &rsn_len, wpa_ie, &wpa_len);

			if (wpa_len > 0) {

				memset(pbuf, 0, buf_size);
				p += sprintf(p, "wpa_ie=");
				for (i = 0; i < wpa_len; i++)
					p += sprintf(p, "%02x", wpa_ie[i]);

				if (wpa_len > 100) {
					printk("-----------------Len %d----------------\n", wpa_len);
					for (i = 0; i < wpa_len; i++)
						printk("%02x ", wpa_ie[i]);
					printk("\n");
					printk("-----------------Len %d----------------\n", wpa_len);
				}

				memset(iwe, 0, sizeof(*iwe));
				iwe->cmd = IWEVCUSTOM;
				iwe->u.data.length = strlen(pbuf);
				start = iwe_stream_add_point(info, start, stop, iwe, pbuf);

				memset(iwe, 0, sizeof(*iwe));
				iwe->cmd = IWEVGENIE;
				iwe->u.data.length = wpa_len;
				start = iwe_stream_add_point(info, start, stop, iwe, wpa_ie);
			}
			if (rsn_len > 0) {

				memset(pbuf, 0, buf_size);
				p += sprintf(p, "rsn_ie=");
				for (i = 0; i < rsn_len; i++)
					p += sprintf(p, "%02x", rsn_ie[i]);
				memset(iwe, 0, sizeof(*iwe));
				iwe->cmd = IWEVCUSTOM;
				iwe->u.data.length = strlen(pbuf);
				start = iwe_stream_add_point(info, start, stop, iwe, pbuf);

				memset(iwe, 0, sizeof(*iwe));
				iwe->cmd = IWEVGENIE;
				iwe->u.data.length = rsn_len;
				start = iwe_stream_add_point(info, start, stop, iwe, rsn_ie);
			}

		vfree(pbuf);
	}
	return start;
}


u8 aicwf_get_is_wps_ie(u8 *ie_ptr, uint *wps_ielen)
{
	u8 match = false;
	u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04};

	if (ie_ptr == NULL)
		return match;

	eid = ie_ptr[0];

	if ((eid == 0xdd/*_WPA_IE_ID_*/) && (memcmp(&ie_ptr[2], wps_oui, 4) == 0)) {
		*wps_ielen = ie_ptr[1] + 2;
		match = true;
	}
	return match;
}


static inline char *aicwf_get_iwe_stream_wps(struct rwnx_hw* rwnx_hw,
		struct iw_request_info *info, struct scanu_result_wext *scan_re,
		char *start, char *stop, struct iw_event *iwe)
{

	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload;
	u8 *payload = mgmt->u.beacon.variable;

	/* parsing WPS IE */
	uint cnt = 0, total_ielen;
	u8 *wpsie_ptr = NULL;
	uint wps_ielen = 0;
	u8 *ie_ptr = payload;
	
	total_ielen = scan_re->ind->length;
	
	while (cnt < total_ielen) {
		if (aicwf_get_is_wps_ie(&ie_ptr[cnt], &wps_ielen) && (wps_ielen > 2)) {
			wpsie_ptr = &ie_ptr[cnt];
			iwe->cmd = IWEVGENIE;
			iwe->u.data.length = (u16)wps_ielen;
			start = iwe_stream_add_point(info, start, stop, iwe, wpsie_ptr);
		}
		cnt += ie_ptr[cnt + 1] + 2; /* goto next */
	}
	return start;
}


static char *translate_scan(struct rwnx_hw* rwnx_hw,
		struct iw_request_info *info, struct scanu_result_wext *scan_re,
		char *start, char *stop)
{
	struct iw_event iwe;
	memset(&iwe, 0, sizeof(iwe));

	if(scan_re->bss){
	    	start = aicwf_get_iwe_stream_mac_addr(rwnx_hw, info, scan_re, start, stop, &iwe);
	    	start = aicwf_get_iwe_stream_essid(rwnx_hw, info, scan_re, start, stop, &iwe);
	    	start = aicwf_get_iwe_stream_protocol(rwnx_hw, info, scan_re, start, stop, &iwe);
	    	start = aicwf_get_iwe_stream_chan(rwnx_hw, info, scan_re, start, stop, &iwe);
	    	start = aicwf_get_iwe_stream_mode(rwnx_hw, info, scan_re, start, stop, &iwe);
	    	start = aicwf_get_iwe_stream_encryption(rwnx_hw, info, scan_re, start, stop, &iwe);
	    	start = aicwf_get_iwe_stream_rate(rwnx_hw, info, scan_re, start, stop, &iwe);
	    	start = aicwf_get_iwe_stream_wpa_wpa2(rwnx_hw, info, scan_re, start, stop, &iwe);
	    	start = aicwf_get_iwe_stream_wps(rwnx_hw, info, scan_re, start, stop, &iwe);
	    	start = aicwf_get_iwe_stream_rssi(rwnx_hw, info, scan_re, start, stop, &iwe);
		return start;
	} else{
		return start;
	}	
}



static int aicwf_get_wap(struct net_device *dev,
			  struct iw_request_info *info,
			  union iwreq_data *wrqu, char *extra)
{

	struct rwnx_vif* rwnx_vif = netdev_priv(dev);


	AICWFDBG(LOGTRACE, "%s Enter", __func__);
	
	wrqu->ap_addr.sa_family = ARPHRD_ETHER;

	memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);

	if(rwnx_vif->sta.ap){
		memcpy(wrqu->ap_addr.sa_data, rwnx_vif->sta.ap->mac_addr, ETH_ALEN);
	}else{
		memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
	}

	return 0;

}


static int aicwf_set_scan(struct net_device *dev, struct iw_request_info *a,
			   union iwreq_data *wrqu, char *extra)
{
	int ret = 0;
	struct rwnx_vif* rwnx_vif = netdev_priv(dev);
	struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw;
	struct cfg80211_scan_request *request;
	int index = 0;
	struct wiphy *wiphy = priv_to_wiphy(rwnx_hw);
	unsigned long wext_scan_timeout;

	AICWFDBG(LOGTRACE, "%s Enter", __func__);

	if(wiphy == NULL){
		printk("aic_wiphy error \r\n");

	}

    if (rwnx_hw->wext_scan || rwnx_hw->scanning) {
        AICWFDBG(LOGINFO, "is scanning, abort\n");
	ret =  rwnx_send_scanu_cancel_req(rwnx_hw, NULL);
	if (ret)
            return ret;
	msleep(150);
    }
	
	rwnx_hw->wext_scan = 1;

	request = (struct cfg80211_scan_request *)vmalloc(sizeof(struct cfg80211_scan_request));

	request->n_channels = rwnx_hw->support_freqs_number;
	request->n_ssids = 0;
	request->no_cck = false;
	request->ie = NULL;
	request->ie_len = 0;

	for(index = 0;index < rwnx_hw->support_freqs_number; index++){
		request->channels[index] = ieee80211_get_channel(wiphy, 
			rwnx_hw->support_freqs[index]);
		if(request->channels[index] == NULL){
			AICWFDBG(LOGERROR, "%s ERROR!!! channels is NULL", __func__);
			continue;
		}
	}

#if WIRELESS_EXT >= 17
		if (wrqu->data.length == sizeof(struct iw_scan_req)) {
			struct iw_scan_req *req = (struct iw_scan_req *)extra;

			if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
				int len = min((int)req->essid_len, 32);
				request->ssids = kmalloc(sizeof(struct cfg80211_ssid), GFP_KERNEL);
				if(!request->ssids){
					AICWFDBG(LOGERROR, "%s Failied to alloc memory for ssids", __func__);
					return -ENOMEM;
				}
				memset(request->ssids, 0, sizeof(struct cfg80211_ssid));
				memcpy(request->ssids[0].ssid, req->essid, len);
				request->ssids[0].ssid_len = len;
				request->n_ssids = 1;
				AICWFDBG(LOGDEBUG,"IW_SCAN_THIS_ESSID, ssid=%s, len=%d\n", req->essid, req->essid_len);
			} else if (req->scan_type == IW_SCAN_TYPE_PASSIVE)
				AICWFDBG(LOGDEBUG,"aic_set_scan, req->scan_type == IW_SCAN_TYPE_PASSIVE\n");
		}
#endif
	if ((ret = rwnx_send_scanu_req(rwnx_hw, rwnx_vif, request))){
        return ret;
	}

	rwnx_vif->rwnx_hw->scan_request = request;
	wext_scan_timeout = msecs_to_jiffies(5000);

	if (!wait_for_completion_killable_timeout(&rwnx_hw->wext_scan_com, wext_scan_timeout)) {
		AICWFDBG(LOGERROR, "%s WEXT scan timeout", __func__);
	}
	return 0;
}

static int aicwf_get_scan(struct net_device *dev, struct iw_request_info *a,
			   union iwreq_data *wrqu, char *extra)
{
	int ret = 0;
	struct rwnx_vif* rwnx_vif = netdev_priv(dev);
	struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw;
	struct scanu_result_wext *scan_re;
	struct scanu_result_wext *tmp;
	char *start = extra;
	char *stop = start + wrqu->data.length;
	
	AICWFDBG(LOGDEBUG, "%s Enter %p %p len:%d \r\n", __func__, start, stop, wrqu->data.length);

	//TODO: spinlock
	list_for_each_entry_safe(scan_re, tmp, &rwnx_hw->wext_scanre_list, scanu_re_list) {
		start = translate_scan(rwnx_hw, a, scan_re, start, stop);
		if ((stop - start) < 768) {
			return -E2BIG;
		}
		
	}

	list_for_each_entry_safe(scan_re, tmp, &rwnx_hw->wext_scanre_list, scanu_re_list) {
		list_del(&scan_re->scanu_re_list);
		vfree(scan_re->payload);
		vfree(scan_re->ind);
		vfree(scan_re);
		scan_re = NULL;
	}

	
	wrqu->data.length = start - extra;
	wrqu->data.flags = 0;

	return ret ;

}


static int aicwf_get_essid(struct net_device *dev,
			    struct iw_request_info *a,
			    union iwreq_data *wrqu, char *extra)
{
	int ret = 0;
	struct rwnx_vif* rwnx_vif = netdev_priv(dev);
	struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw;
	
	AICWFDBG(LOGTRACE, "%s Enter", __func__);

	wrqu->essid.length = strlen(rwnx_hw->wext_essid);
	memcpy(extra, rwnx_hw->wext_essid, strlen(rwnx_hw->wext_essid));
	wrqu->essid.flags = 1;


	return ret;

}


static int aicwf_get_nick(struct net_device *dev,
			   struct iw_request_info *info,
			   union iwreq_data *wrqu, char *extra)
{
			   
	AICWFDBG(LOGTRACE, "%s Enter", __func__);

	if (extra) {
		wrqu->data.length = 8;
		wrqu->data.flags = 1;
		memcpy(extra, "AIC@8800", 8);
	}

	return 0;

}

static int aicwf_get_rate(struct net_device *dev,
			   struct iw_request_info *info,
			   union iwreq_data *wrqu, char *extra)
{
	u16 max_rate = 0;
	struct rwnx_vif* rwnx_vif = netdev_priv(dev);
	struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw;
	struct rwnx_sta *sta = NULL; 
	
	union rwnx_rate_ctrl_info *rate_info;
	struct mm_get_sta_info_cfm cfm;
	
	AICWFDBG(LOGTRACE, "%s Enter", __func__);
	
	if(rwnx_vif->sta.ap){
		sta = rwnx_vif->sta.ap;
		rwnx_send_get_sta_info_req(rwnx_hw, sta->sta_idx, &cfm);
			rate_info = (union rwnx_rate_ctrl_info *)&cfm.rate_info;
			
			switch (rate_info->formatModTx) {
			case FORMATMOD_NON_HT:
			case FORMATMOD_NON_HT_DUP_OFDM:
				//get bg mode datarate
				max_rate = tx_legrates_lut_rate[rate_info->mcsIndexTx]/10;
				break;
			case FORMATMOD_HT_MF:
			case FORMATMOD_HT_GF:
			case FORMATMOD_VHT:
				//get bg mode MCS index
				max_rate = VHT_MCS_DATA_RATE[rate_info->bwTx][1][rate_info->mcsIndexTx]/2;
				break;
			case FORMATMOD_HE_MU:
			case FORMATMOD_HE_SU:
			case FORMATMOD_HE_ER:
				//get bg mode MCS index
				max_rate = HE_MCS_DATA_RATE[rate_info->bwTx][rate_info->mcsIndexTx];
				break;
			}
	}

	wrqu->bitrate.fixed = 0;	/* no auto select */
	wrqu->bitrate.value = max_rate * 1000000;

	return 0;
}


static int aicwf_get_enc(struct net_device *dev,
			  struct iw_request_info *info,
			  union iwreq_data *wrqu, char *keybuf)
{
	int ret = 0;
	struct iw_point *erq = &(wrqu->encoding);

	AICWFDBG(LOGTRACE, "%s Enter", __func__);

	erq->length = 0;
	erq->flags |= IW_ENCODE_ENABLED;


	return ret;

}


static iw_handler aic_handlers[] = {
	NULL,					/* SIOCSIWCOMMIT */
	aicwf_get_name,			/* SIOCGIWNAME */
	NULL,					/* SIOCSIWNWID */
	NULL,					/* SIOCGIWNWID */
	NULL,					/* SIOCSIWFREQ */
	aicwf_get_freq,			/* SIOCGIWFREQ */
	NULL,					/* SIOCSIWMODE */
	aicwf_get_mode,			/* SIOCGIWMODE */
	NULL,					/* SIOCSIWSENS */
	NULL,					/* SIOCGIWSENS */
	NULL,					/* SIOCSIWRANGE */
	aicwf_get_range,		/* SIOCGIWRANGE */
	NULL,					/* SIOCSIWPRIV */
	NULL,					/* SIOCGIWPRIV */
	NULL,					/* SIOCSIWSTATS */
	NULL,					/* SIOCGIWSTATS */
	NULL,					/* SIOCSIWSPY */
	NULL,					/* SIOCGIWSPY */
	NULL,					/* SIOCGIWTHRSPY */
	NULL,					/* SIOCWIWTHRSPY */
	NULL,					/* SIOCSIWAP */
	aicwf_get_wap,			/* SIOCGIWAP */
	NULL,					/* request MLME operation; uses struct iw_mlme */
	NULL,					/* SIOCGIWAPLIST -- depricated */
	aicwf_set_scan,			/* SIOCSIWSCAN */
	aicwf_get_scan,			/* SIOCGIWSCAN */
	NULL,					/* SIOCSIWESSID */
	aicwf_get_essid,		/* SIOCGIWESSID */
	NULL,					/* SIOCSIWNICKN */
	aicwf_get_nick,			/* SIOCGIWNICKN */
	NULL,					/* -- hole -- */
	NULL,					/* -- hole -- */
	NULL,					/* SIOCSIWRATE */
	aicwf_get_rate,			/* SIOCGIWRATE */
	NULL,					/* SIOCSIWRTS */
	NULL,					/* SIOCGIWRTS */
	NULL,					/* SIOCSIWFRAG */
	NULL,					/* SIOCGIWFRAG */
	NULL,					/* SIOCSIWTXPOW */
	NULL,					/* SIOCGIWTXPOW */
	NULL,					/* SIOCSIWRETRY */
	NULL,					/* SIOCGIWRETRY */
	NULL,					/* SIOCSIWENCODE */
	aicwf_get_enc,			/* SIOCGIWENCODE */
	NULL,					/* SIOCSIWPOWER */
	NULL,					/* SIOCGIWPOWER */
	NULL,					/*---hole---*/
	NULL,					/*---hole---*/
	NULL,					/* SIOCSIWGENIE */
	NULL,					/* SIOCGWGENIE */
	NULL,					/* SIOCSIWAUTH */
	NULL,					/* SIOCGIWAUTH */
	NULL,					/* SIOCSIWENCODEEXT */
	NULL,					/* SIOCGIWENCODEEXT */
	NULL,					/* SIOCSIWPMKSA */
	NULL,					/*---hole---*/
};

struct iw_handler_def aic_handlers_def = {
		.standard = aic_handlers,
		.num_standard = sizeof(aic_handlers) / sizeof(iw_handler),
#if WIRELESS_EXT >= 17
		.get_wireless_stats = aicwf_get_wireless_stats,
#endif
};

void aicwf_set_wireless_ext( struct net_device *ndev, struct rwnx_hw *rwnx_hw){

	AICWFDBG(LOGINFO, "%s Enter", __func__);
	
	init_completion(&rwnx_hw->wext_scan_com);
	INIT_LIST_HEAD(&rwnx_hw->wext_scanre_list);
	
	ndev->wireless_handlers = (struct iw_handler_def *)&aic_handlers_def;
}


