/** @file  moal_uap_wext.c
 *
 * @brief This file contains wireless extension standard ioctl functions
 *
 *
 * Copyright 2010-2021 NXP
 *
 * NXP CONFIDENTIAL
 * The source code contained or described herein and all documents related to
 * the source code (Materials) are owned by NXP, its
 * suppliers and/or its licensors. Title to the Materials remains with NXP,
 * its suppliers and/or its licensors. The Materials contain
 * trade secrets and proprietary and confidential information of NXP, its
 * suppliers and/or its licensors. The Materials are protected by worldwide
 * copyright and trade secret laws and treaty provisions. No part of the
 * Materials may be used, copied, reproduced, modified, published, uploaded,
 * posted, transmitted, distributed, or disclosed in any way without NXP's prior
 * express written permission.
 *
 * No license under any patent, copyright, trade secret or other intellectual
 * property right is granted to or conferred upon you by disclosure or delivery
 * of the Materials, either expressly, by implication, inducement, estoppel or
 * otherwise. Any license under such intellectual property rights must be
 * express and approved by NXP in writing.
 *
 *  Alternatively, this software may be distributed under the terms of GPL v2.
 *  SPDX-License-Identifier:    GPL-2.0
 *
 */

/************************************************************************
Change log:
    08/06/2010: initial version
************************************************************************/

#include "moal_main.h"
#include "moal_uap.h"
#include "moal_wext.h"
#include "moal_uap_priv.h"

/********************************************************
			Global Variables
********************************************************/
typedef struct _chan_to_freq_t {
	/** Channel */
	t_u16 channel;
	/** Frequency */
	t_u32 freq;
	/** Band */
	t_u8 band;
} chan_to_freq_t;

static const chan_to_freq_t chan_to_freq[] = {
	{1, 2412, 0},	{2, 2417, 0},	{3, 2422, 0},	{4, 2427, 0},
	{5, 2432, 0},	{6, 2437, 0},	{7, 2442, 0},	{8, 2447, 0},
	{9, 2452, 0},	{10, 2457, 0},	{11, 2462, 0},	{12, 2467, 0},
	{13, 2472, 0},	{14, 2484, 0},	{183, 4915, 1}, {184, 4920, 1},
	{185, 4925, 1}, {187, 4935, 1}, {188, 4940, 1}, {189, 4945, 1},
	{192, 4960, 1}, {196, 4980, 1}, {7, 5035, 1},	{8, 5040, 1},
	{9, 5045, 1},	{11, 5055, 1},	{12, 5060, 1},	{16, 5080, 1},
	{34, 5170, 1},	{36, 5180, 1},	{38, 5190, 1},	{40, 5200, 1},
	{42, 5210, 1},	{44, 5220, 1},	{46, 5230, 1},	{48, 5240, 1},
	{52, 5260, 1},	{56, 5280, 1},	{60, 5300, 1},	{64, 5320, 1},
	{100, 5500, 1}, {104, 5520, 1}, {108, 5540, 1}, {112, 5560, 1},
	{116, 5580, 1}, {120, 5600, 1}, {124, 5620, 1}, {128, 5640, 1},
	{132, 5660, 1}, {136, 5680, 1}, {140, 5700, 1}, {144, 5720, 1},
	{149, 5745, 1}, {153, 5765, 1}, {157, 5785, 1}, {161, 5805, 1},
	{165, 5825, 1}, {169, 5845, 1}, {173, 5865, 1}, {177, 5885, 1},
};

/**
 * iwpriv ioctl handlers
 */
static const struct iw_priv_args woal_uap_priv_args[] = {
	{WOAL_UAP_SETNONE_GETNONE, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_NONE, ""},
	{WOAL_UAP_START, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_NONE, "start"},
	{WOAL_UAP_STOP, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_NONE, "stop"},
	{WOAL_AP_BSS_START, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_NONE, "bssstart"},
	{WOAL_AP_BSS_STOP, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_NONE, "bssstop"},
	{WOAL_UAP_SETONEINT_GETWORDCHAR, IW_PRIV_TYPE_INT | 1,
	 IW_PRIV_TYPE_CHAR | 128, ""},
	{WOAL_UAP_VERSION, IW_PRIV_TYPE_INT | 1, IW_PRIV_TYPE_CHAR | 128,
	 "version"},
	{WOAL_UAP_VEREXT, IW_PRIV_TYPE_INT | 1, IW_PRIV_TYPE_CHAR | 128,
	 "verext"},
	{WOAL_UAP_SETONEINT_GETONEINT, IW_PRIV_TYPE_INT | 1,
	 IW_PRIV_TYPE_INT | 1, ""},
#ifdef WIFI_DIRECT_SUPPORT
#if defined(STA_SUPPORT) && defined(UAP_SUPPORT)
	{WOAL_UAP_SET_GET_BSS_ROLE, IW_PRIV_TYPE_INT | 1, IW_PRIV_TYPE_INT | 1,
	 "bssrole"},
#endif
#endif

	{WOAL_UAP_SET_GET_256_CHAR, IW_PRIV_TYPE_CHAR | 256,
	 IW_PRIV_TYPE_CHAR | 256, ""},
	{WOAL_WL_FW_RELOAD, IW_PRIV_TYPE_CHAR | 256, IW_PRIV_TYPE_CHAR | 256,
	 "fwreload"},
	{WOAL_AP_SET_CFG, IW_PRIV_TYPE_CHAR | 256, IW_PRIV_TYPE_CHAR | 256,
	 "apcfg"},
	{WOAL_UAP_HOST_CMD, IW_PRIV_TYPE_BYTE | 2047, IW_PRIV_TYPE_BYTE | 2047,
	 "hostcmd"},
	{WOAL_UAP_FROYO_START, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_NONE, "START"},
	{WOAL_UAP_FROYO_STOP, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_NONE, "STOP"},
	{WOAL_UAP_FROYO_AP_BSS_START, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_NONE,
	 "AP_BSS_START"},
	{WOAL_UAP_FROYO_AP_BSS_STOP, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_NONE,
	 "AP_BSS_STOP"},
	{WOAL_UAP_FROYO_WL_FW_RELOAD, IW_PRIV_TYPE_CHAR | 256,
	 IW_PRIV_TYPE_CHAR | 256, "WL_FW_RELOAD"},
	{WOAL_UAP_FROYO_AP_SET_CFG, IW_PRIV_TYPE_CHAR | 256,
	 IW_PRIV_TYPE_CHAR | 256, "AP_SET_CFG"},
};

/** Convertion from frequency to channel */
#define freq_to_chan(x) ((((x)-2412) / 5) + 1)

/********************************************************
			Local Functions
********************************************************/

/**
 *  @brief Sort Channels
 *
 *  @param freq                 A pointer to iw_freq structure
 *  @param num                  Number of Channels
 *
 *  @return                     N/A
 */
static inline void woal_sort_channels(struct iw_freq *freq, int num)
{
	int i, j;
	struct iw_freq temp;

	for (i = 0; i < num; i++)
		for (j = i + 1; j < num; j++)
			if (freq[i].i > freq[j].i) {
				temp.i = freq[i].i;
				temp.m = freq[i].m;

				freq[i].i = freq[j].i;
				freq[i].m = freq[j].m;

				freq[j].i = temp.i;
				freq[j].m = temp.m;
			}
}

/**
 *  @brief Get frequency for channel in given band
 *
 *  @param channel      channel
 *  @param band         band
 *
 *  @return             freq
 */
static int channel_to_frequency(t_u16 channel, t_u8 band)
{
	int i = 0;

	ENTER();
	for (i = 0; i < (int)ARRAY_SIZE(chan_to_freq); i++) {
		if (channel == chan_to_freq[i].channel &&
		    band == chan_to_freq[i].band) {
			LEAVE();
			return chan_to_freq[i].freq;
		}
	}
	LEAVE();
	return 0;
}

/**
 *  @brief Commit handler: called after a bunch of SET operations
 *
 *  @param dev          A pointer to net_device structure
 *  @param info         A pointer to iw_request_info structure
 *  @param cwrq         A pointer to char buffer
 *  @param extra        A pointer to extra data buf
 *
 *  @return             0 --success
 */
static int woal_config_commit(struct net_device *dev,
			      struct iw_request_info *info,
			      union iwreq_data *cwrq, char *extra)
{
	ENTER();

	LEAVE();
	return 0;
}

/**
 *  @brief Get name
 *
 *  @param dev          A pointer to net_device structure
 *  @param info         A pointer to iw_request_info structure
 *  @param cwrq         A pointer to char buffer
 *  @param extra        A pointer to extra data buf
 *
 *  @return             0 --success
 */
static int woal_get_name(struct net_device *dev, struct iw_request_info *info,
			 union iwreq_data *wrqu, char *extra)
{
	char *cwrq = wrqu->name;
	ENTER();
	strncpy(cwrq, "IEEE 802.11-DS", IFNAMSIZ);
	LEAVE();
	return 0;
}

/**
 *  @brief Get current BSSID
 *
 *  @param dev          A pointer to net_device structure
 *  @param info         A pointer to iw_request_info structure
 *  @param awrq         A pointer to sockaddr structure
 *  @param extra        A pointer to extra data buf
 *
 *  @return             0 --success
 */
static int woal_get_wap(struct net_device *dev, struct iw_request_info *info,
			union iwreq_data *wrqu, char *extra)
{
	moal_private *priv = (moal_private *)netdev_priv(dev);
	struct sockaddr *awrq = &wrqu->addr;
	int ret = 0;

	ENTER();

	if (priv->bss_started)
		moal_memcpy_ext(priv->phandle, awrq->sa_data,
				priv->current_addr, MLAN_MAC_ADDR_LENGTH,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0)
				sizeof(awrq->sa_data_min));
#else
				sizeof(awrq->sa_data));
#endif
	else
		memset(awrq->sa_data, 0, MLAN_MAC_ADDR_LENGTH);
	awrq->sa_family = ARPHRD_ETHER;

	LEAVE();
	return ret;
}

/**
 *  @brief Change the AP BSSID
 *
 *  @param dev          A pointer to net_device structure
 *  @param info         A pointer to iw_request_info structure
 *  @param awrq         A pointer to iw_param structure
 *  @param extra        A pointer to extra data buf
 *
 *  @return             0 --success, otherwise fail
 */
static int woal_set_wap(struct net_device *dev, struct iw_request_info *info,
			union iwreq_data *wrqu, char *extra)
{
	int ret = 0;
	moal_private *priv = (moal_private *)netdev_priv(dev);
	struct sockaddr *awrq = &wrqu->addr;
	const t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = {0, 0, 0, 0, 0, 0};

	ENTER();

	if (awrq->sa_family != ARPHRD_ETHER) {
		ret = -EINVAL;
		goto done;
	}

	PRINTM(MINFO, "ASSOC: WAP: uAP bss : " MACSTR "\n",
	       MAC2STR((t_u8 *)awrq->sa_data));

	/*
	 * Using this ioctl to start/stop the BSS, return if bss
	 * is already started/stopped.
	 */
	if (memcmp(zero_mac, awrq->sa_data, MLAN_MAC_ADDR_LENGTH)) {
		if (priv->bss_started == MFALSE)
			ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT,
						UAP_BSS_START);
		else
			PRINTM(MINFO, "BSS is already started.\n");
	} else {
		/* zero_mac means bss_stop */
		if (priv->bss_started == MTRUE)
			ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT,
						UAP_BSS_STOP);
		else
			PRINTM(MINFO, "BSS is already stopped.\n");
	}

done:
	LEAVE();
	return ret;
}

/**
 *  @brief Set frequency/channel
 *
 *  @param dev                  A pointer to net_device structure
 *  @param info                 A pointer to iw_request_info structure
 *  @param fwrq                 A pointer to iw_freq structure
 *  @param extra                A pointer to extra data buf
 *
 *  @return                     0 --success, otherwise fail
 */
static int woal_set_freq(struct net_device *dev, struct iw_request_info *info,
			 union iwreq_data *wrqu, char *extra)
{
	moal_private *priv = (moal_private *)netdev_priv(dev);
	struct iw_freq *fwrq = &wrqu->freq;
	mlan_uap_bss_param *sys_cfg = NULL, *ap_cfg = NULL;
	int ret = 0, chan = 0, i = 0;

	ENTER();

	ap_cfg = kmalloc(sizeof(mlan_uap_bss_param), GFP_KERNEL);
	if (ap_cfg == NULL) {
		ret = -ENOMEM;
		goto done;
	}

	sys_cfg = kmalloc(sizeof(mlan_uap_bss_param), GFP_KERNEL);
	if (sys_cfg == NULL) {
		ret = -ENOMEM;
		goto done;
	}

	if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET,
							   MOAL_IOCTL_WAIT,
							   ap_cfg)) {
		PRINTM(MERROR, "Error getting AP confiruration\n");
		ret = -EFAULT;
		goto done;
	}
	i = ap_cfg->num_of_chan;

	/* Initialize the invalid values so that the correct values
	 * below are downloaded to firmware */
	woal_set_sys_config_invalid_data(sys_cfg);

	/* If setting by frequency, convert to a channel */
	if (fwrq->e == 1)
		chan = freq_to_chan(fwrq->m / 100000);
	else
		chan = fwrq->m;
	if (chan > 0 && chan < MLAN_MAX_CHANNEL)
		sys_cfg->channel = chan;
	else {
		ret = -EINVAL;
		goto done;
	}
	for (i = 0; i < (int)ap_cfg->num_of_chan; i++)
		if (ap_cfg->chan_list[i].chan_number == chan)
			break;
	if (i == (int)ap_cfg->num_of_chan) {
		PRINTM(MERROR, "Channel %d is not supported\n", chan);
		ret = -EINVAL;
		goto done;
	}

	if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_SET,
							   MOAL_IOCTL_WAIT,
							   sys_cfg)) {
		PRINTM(MERROR, "Error setting AP confiruration\n");
		ret = -EFAULT;
		goto done;
	}

done:
	kfree(sys_cfg);
	kfree(ap_cfg);
	LEAVE();
	return ret;
}

/**
 *  @brief Get frequency and channel
 *
 *  @param dev                  A pointer to net_device structure
 *  @param info                 A pointer to iw_request_info structure
 *  @param fwrq                 A pointer to iw_freq structure
 *  @param extra                A pointer to extra data buf
 *
 *  @return                     0 --success, otherwise fail
 */
static int woal_get_freq(struct net_device *dev, struct iw_request_info *info,
			 union iwreq_data *wrqu, char *extra)
{
	moal_private *priv = (moal_private *)netdev_priv(dev);
	struct iw_freq *fwrq = &wrqu->freq;
	mlan_uap_bss_param *ap_cfg = NULL;
	t_u8 band = 0;
	int ret = 0;

	ENTER();

	ap_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC);
	if (!ap_cfg) {
		PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n");
		return -EFAULT;
	}

	if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET,
							   MOAL_IOCTL_WAIT,
							   ap_cfg)) {
		PRINTM(MERROR, "Error getting AP confiruration\n");
		kfree(ap_cfg);
		LEAVE();
		return -EFAULT;
	}

	band = (((ap_cfg->bandcfg.chanBand == BAND_5GHZ) ? 1 : 0));
	fwrq->m = (long)channel_to_frequency(ap_cfg->channel, band);
	fwrq->i = (long)ap_cfg->channel;
	fwrq->e = 6;

	kfree(ap_cfg);
	LEAVE();
	return ret;
}

/**
 *  @brief Set wlan mode
 *
 *  @param dev                  A pointer to net_device structure
 *  @param info                 A pointer to iw_request_info structure
 *  @param uwrq                 A pointer to t_u32 string
 *  @param extra                A pointer to extra data buf
 *
 *  @return                     0 --success, otherwise fail
 */
static int woal_set_bss_mode(struct net_device *dev,
			     struct iw_request_info *info,
			     union iwreq_data *wrqu, char *extra)
{
	int ret = 0;
	t_u32 *uwrq = &wrqu->mode;
	ENTER();

	switch (*uwrq) {
	case IW_MODE_AUTO:
	case IW_MODE_MASTER:
		PRINTM(MINFO, "This is correct mode in AP mode\n");
		break;
	default:
		PRINTM(MERROR, "Invalid mode for AP\n");
		ret = -EINVAL;
	}

	LEAVE();
	return ret;
}

/**
 *  @brief Get wlan mode
 *
 *  @param dev                  A pointer to net_device structure
 *  @param info                 A pointer to iw_request_info structure
 *  @param uwrq                 A pointer to t_u32 string
 *  @param extra                A pointer to extra data buf
 *
 *  @return                     0 --success
 */
static int woal_get_bss_mode(struct net_device *dev,
			     struct iw_request_info *info,
			     union iwreq_data *wrqu, char *extra)
{
	t_u32 *uwrq = &wrqu->mode;
	ENTER();

	*uwrq = IW_MODE_MASTER;

	LEAVE();
	return 0;
}

/**
 *  @brief Set encryption key
 *
 *  @param dev                  A pointer to net_device structure
 *  @param info                 A pointer to iw_request_info structure
 *  @param dwrq                 A pointer to iw_point structure
 *  @param extra                A pointer to extra data buf
 *
 *  @return                     0 --success, otherwise fail
 */
static int woal_set_encode(struct net_device *dev, struct iw_request_info *info,
			   struct iw_point *dwrq, char *extra)
{
	int ret = 0;
	moal_private *priv = (moal_private *)netdev_priv(dev);
	mlan_uap_bss_param *sys_cfg = NULL, *ap_cfg = NULL;
	wep_key *pkey = NULL;
	int key_index = 0;

	ENTER();

	/* Check index */
	key_index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
	if (key_index > 3) {
		PRINTM(MERROR, "Key index #%d out of range\n", key_index);
		ret = -EINVAL;
		goto done;
	}

	ap_cfg = kmalloc(sizeof(mlan_uap_bss_param), GFP_KERNEL);
	if (ap_cfg == NULL) {
		ret = -ENOMEM;
		goto done;
	}

	sys_cfg = kmalloc(sizeof(mlan_uap_bss_param), GFP_KERNEL);
	if (sys_cfg == NULL) {
		ret = -ENOMEM;
		goto done;
	}

	if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET,
							   MOAL_IOCTL_WAIT,
							   ap_cfg)) {
		PRINTM(MERROR, "Error getting AP confiruration\n");
		ret = -EFAULT;
		goto done;
	}

	/* Initialize the invalid values so that the correct values
	 * below are downloaded to firmware */
	woal_set_sys_config_invalid_data(sys_cfg);
	sys_cfg->wep_cfg.key0.key_index = 0;
	sys_cfg->wep_cfg.key1.key_index = 1;
	sys_cfg->wep_cfg.key2.key_index = 2;
	sys_cfg->wep_cfg.key3.key_index = 3;

	if (key_index >= 0 && key_index <= 3) {
		if (key_index == 0)
			pkey = &sys_cfg->wep_cfg.key0;
		else if (key_index == 1)
			pkey = &sys_cfg->wep_cfg.key1;
		else if (key_index == 2)
			pkey = &sys_cfg->wep_cfg.key2;
		else if (key_index == 3)
			pkey = &sys_cfg->wep_cfg.key3;
	}

	if (!(dwrq->flags & IW_ENCODE_NOKEY) && dwrq->length) {
		if (dwrq->length > MAX_WEP_KEY_SIZE) {
			PRINTM(MERROR, "Key length (%d) out of range\n",
			       dwrq->length);
			ret = -E2BIG;
			goto done;
		}
		if (key_index < 0) {
			/* Get current default key index */
			if (ap_cfg->wep_cfg.key0.is_default)
				pkey = &sys_cfg->wep_cfg.key0;
			if (ap_cfg->wep_cfg.key1.is_default)
				pkey = &sys_cfg->wep_cfg.key1;
			if (ap_cfg->wep_cfg.key2.is_default)
				pkey = &sys_cfg->wep_cfg.key2;
			if (ap_cfg->wep_cfg.key3.is_default)
				pkey = &sys_cfg->wep_cfg.key3;
			else { /* Something wrong, select first key as default
				*/
				PRINTM(MERROR,
				       "No default key set! Selecting first key.\n");
				pkey = &sys_cfg->wep_cfg.key0;
			}
		}

		sys_cfg->protocol = PROTOCOL_STATIC_WEP;
		if (extra)
			moal_memcpy_ext(priv->phandle, pkey->key, extra,
					dwrq->length, sizeof(pkey->key));
		/* Set the length */
		if (dwrq->length > MIN_WEP_KEY_SIZE)
			pkey->length = MAX_WEP_KEY_SIZE;
		else
			pkey->length = MIN_WEP_KEY_SIZE;
		/* Set current key index as default */
		pkey->is_default = MTRUE;
	} else {
		/*
		 * No key provided so it is either enable key,
		 * on or off
		 */
		if (dwrq->flags & IW_ENCODE_DISABLED) {
			PRINTM(MINFO, "*** iwconfig mlanX key off ***\n");
			sys_cfg->protocol = PROTOCOL_NO_SECURITY;
		} else {
			/*
			 * iwconfig mlanX key [n]
			 * iwconfig mlanX key on
			 * Do we want to just set the transmit key index ?
			 */
			if (key_index < 0) {
				PRINTM(MINFO,
				       "*** iwconfig mlanX key on ***\n");
			} else {
				/* Get current key configuration at key_index */
				if (key_index == 0)
					moal_memcpy_ext(priv->phandle, pkey,
							&ap_cfg->wep_cfg.key0,
							sizeof(wep_key),
							sizeof(wep_key));
				if (key_index == 1)
					moal_memcpy_ext(priv->phandle, pkey,
							&ap_cfg->wep_cfg.key1,
							sizeof(wep_key),
							sizeof(wep_key));
				if (key_index == 2)
					moal_memcpy_ext(priv->phandle, pkey,
							&ap_cfg->wep_cfg.key2,
							sizeof(wep_key),
							sizeof(wep_key));
				if (key_index == 3)
					moal_memcpy_ext(priv->phandle, pkey,
							&ap_cfg->wep_cfg.key3,
							sizeof(wep_key),
							sizeof(wep_key));
				/* Set current key index as default */
				pkey->is_default = MTRUE;
			}
		}
	}
	if (dwrq->flags & (IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN)) {
		switch (dwrq->flags & 0xf000) {
		case IW_ENCODE_RESTRICTED:
			/* iwconfig mlanX restricted key [1] */
			sys_cfg->auth_mode = MLAN_AUTH_MODE_SHARED;
			PRINTM(MINFO, "Auth mode restricted!\n");
			break;
		case IW_ENCODE_OPEN:
			/* iwconfig mlanX key [2] open */
			sys_cfg->auth_mode = MLAN_AUTH_MODE_OPEN;
			PRINTM(MINFO, "Auth mode open!\n");
			break;
		case IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN:
		default:
			/* iwconfig mlanX key [2] open restricted */
			PRINTM(MINFO, "Auth mode auto!\n");
			break;
		}
	}

	if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_SET,
							   MOAL_IOCTL_WAIT,
							   sys_cfg)) {
		PRINTM(MERROR, "Error setting AP confiruration\n");
		ret = -EFAULT;
		goto done;
	}

done:
	kfree(sys_cfg);
	kfree(ap_cfg);
	LEAVE();
	return ret;
}

/**
 *  @brief Get encryption key
 *
 *  @param dev                  A pointer to net_device structure
 *  @param info                 A pointer to iw_request_info structure
 *  @param dwrq                 A pointer to iw_point structure
 *  @param extra                A pointer to extra data buf
 *
 *  @return                     0 --success, otherwise fail
 */
static int woal_get_encode(struct net_device *dev, struct iw_request_info *info,
			   union iwreq_data *wrqu, char *extra)
{
	moal_private *priv = (moal_private *)netdev_priv(dev);
	struct iw_point *dwrq = &wrqu->data;
	int index = (dwrq->flags & IW_ENCODE_INDEX);
	wep_key *pkey = NULL;
	mlan_uap_bss_param *ap_cfg = NULL;
	int ret = 0;

	ENTER();
	if (index < 0 || index > 4) {
		PRINTM(MERROR, "Key index #%d out of range\n", index);
		ret = -EINVAL;
		goto done;
	}
	ap_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC);
	if (!ap_cfg) {
		PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n");
		ret = -EFAULT;
		goto done;
	}

	if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET,
							   MOAL_IOCTL_WAIT,
							   ap_cfg)) {
		PRINTM(MERROR, "Error getting AP confiruration\n");
		ret = -EFAULT;
		goto done;
	}

	dwrq->flags = 0;
	/*
	 * Check encryption mode
	 */
	switch (ap_cfg->auth_mode) {
	case MLAN_AUTH_MODE_OPEN:
		dwrq->flags = IW_ENCODE_OPEN;
		break;
	case MLAN_AUTH_MODE_SHARED:
	case MLAN_AUTH_MODE_NETWORKEAP:
		dwrq->flags = IW_ENCODE_RESTRICTED;
		break;
	default:
		dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN;
		break;
	}

	switch (ap_cfg->protocol) {
	case PROTOCOL_NO_SECURITY:
		dwrq->flags |= IW_ENCODE_DISABLED;
		break;
	case PROTOCOL_STATIC_WEP:
		if (ap_cfg->wep_cfg.key0.is_default)
			pkey = &ap_cfg->wep_cfg.key0;
		else if (ap_cfg->wep_cfg.key1.is_default)
			pkey = &ap_cfg->wep_cfg.key1;
		else if (ap_cfg->wep_cfg.key2.is_default)
			pkey = &ap_cfg->wep_cfg.key2;
		else if (ap_cfg->wep_cfg.key3.is_default)
			pkey = &ap_cfg->wep_cfg.key3;
		if (pkey) {
			dwrq->flags |= (pkey->key_index + 1);
			dwrq->length = pkey->length;
			moal_memcpy_ext(priv->phandle, extra, pkey->key,
					pkey->length, pkey->length);
			dwrq->flags &= ~IW_ENCODE_DISABLED;
		} else {
			ret = -EFAULT;
		}
		break;
	case PROTOCOL_WPA:
	case PROTOCOL_WPA2:
	case PROTOCOL_WPA2_MIXED:
		moal_memcpy_ext(priv->phandle, extra,
				ap_cfg->wpa_cfg.passphrase,
				ap_cfg->wpa_cfg.length, ap_cfg->wpa_cfg.length);
		if (ap_cfg->wpa_cfg.length)
			dwrq->length = ap_cfg->wpa_cfg.length;
		else
			dwrq->length = 16;
		dwrq->flags |= 1;
		dwrq->flags &= ~IW_ENCODE_DISABLED;
		break;
	default:
		dwrq->flags &= ~IW_ENCODE_DISABLED;
		break;
	}
	dwrq->flags |= IW_ENCODE_NOKEY;

done:
	kfree(ap_cfg);
	LEAVE();
	return ret;
}

#if (WIRELESS_EXT >= 18)
/**
 *  @brief Get IE
 *
 *  @param dev                  A pointer to net_device structure
 *  @param info                 A pointer to iw_request_info structure
 *  @param dwrq                 A pointer to iw_point structure
 *  @param extra                A pointer to extra data buf
 *
 *  @return                     -EOPNOTSUPP
 */
static int woal_get_gen_ie(struct net_device *dev, struct iw_request_info *info,
			   union iwreq_data *wrqu, char *extra)
{
	ENTER();
	LEAVE();
	return -EOPNOTSUPP;
}

/**
 *  @brief Set IE
 *
 *  Pass an opaque block of data, expected to be IEEE IEs, to the driver
 *    for eventual passthrough to the firmware in an associate/join
 *    (and potentially start) command.
 *
 *  @param dev                  A pointer to net_device structure
 *  @param info                 A pointer to iw_request_info structure
 *  @param dwrq                 A pointer to iw_point structure
 *  @param extra                A pointer to extra data buf
 *
 *  @return                     0 --success, otherwise fail
 */
static int woal_set_gen_ie(struct net_device *dev, struct iw_request_info *info,
			   union iwreq_data *wrqu, char *extra)
{
	moal_private *priv = (moal_private *)netdev_priv(dev);
	struct iw_point *dwrq = &wrqu->data;
	mlan_uap_bss_param *sys_cfg = NULL;
	IEEEtypes_Header_t *tlv = NULL;
	int tlv_hdr_len = sizeof(IEEEtypes_Header_t), tlv_buf_left = 0;
	int ret = 0;

	ENTER();

	sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC);
	if (!sys_cfg) {
		PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n");
		LEAVE();
		return -EFAULT;
	}

	/* Initialize the invalid values so that the correct values
	 * below are downloaded to firmware */
	woal_set_sys_config_invalid_data(sys_cfg);

	tlv_buf_left = dwrq->length;
	tlv = (IEEEtypes_Header_t *)extra;
	while (tlv_buf_left >= tlv_hdr_len) {
		if (tlv->element_id == WPA_IE) {
			sys_cfg->protocol |= PROTOCOL_WPA;
			if (priv->pairwise_cipher == CIPHER_TKIP) {
				sys_cfg->wpa_cfg.pairwise_cipher_wpa =
					CIPHER_TKIP;
				PRINTM(MINFO, "Set IE Cipher TKIP\n");
			}
			if (priv->pairwise_cipher == CIPHER_AES_CCMP) {
				sys_cfg->wpa_cfg.pairwise_cipher_wpa =
					CIPHER_AES_CCMP;
				PRINTM(MINFO, "Set IE Cipher CCMP\n");
			}
			if (priv->pairwise_cipher ==
			    (CIPHER_TKIP | CIPHER_AES_CCMP)) {
				sys_cfg->wpa_cfg.pairwise_cipher_wpa =
					CIPHER_TKIP | CIPHER_AES_CCMP;
				PRINTM(MINFO, "Set IE Cipher TKIP + CCMP\n");
			}
			moal_memcpy_ext(priv->phandle,
					priv->bcn_ie_buf + priv->bcn_ie_len,
					((t_u8 *)tlv),
					sizeof(IEEEtypes_Header_t) + tlv->len,
					sizeof(priv->bcn_ie_buf) -
						priv->bcn_ie_len);
			priv->bcn_ie_len +=
				sizeof(IEEEtypes_Header_t) + tlv->len;
		}
		if (tlv->element_id == RSN_IE) {
			sys_cfg->protocol |= PROTOCOL_WPA2;
			if (priv->pairwise_cipher == CIPHER_TKIP) {
				sys_cfg->wpa_cfg.pairwise_cipher_wpa2 =
					CIPHER_TKIP;
			}
			if (priv->pairwise_cipher == CIPHER_AES_CCMP) {
				sys_cfg->wpa_cfg.pairwise_cipher_wpa2 =
					CIPHER_AES_CCMP;
			}
			if (priv->pairwise_cipher ==
			    (CIPHER_TKIP | CIPHER_AES_CCMP)) {
				sys_cfg->wpa_cfg.pairwise_cipher_wpa2 =
					(CIPHER_TKIP | CIPHER_AES_CCMP);
			}
			moal_memcpy_ext(priv->phandle,
					priv->bcn_ie_buf + priv->bcn_ie_len,
					((t_u8 *)tlv),
					sizeof(IEEEtypes_Header_t) + tlv->len,
					sizeof(priv->bcn_ie_buf) -
						priv->bcn_ie_len);
			priv->bcn_ie_len +=
				sizeof(IEEEtypes_Header_t) + tlv->len;
		}
		if (priv->group_cipher == CIPHER_TKIP)
			sys_cfg->wpa_cfg.group_cipher = CIPHER_TKIP;
		if (priv->group_cipher == CIPHER_AES_CCMP)
			sys_cfg->wpa_cfg.group_cipher = CIPHER_AES_CCMP;
		tlv_buf_left -= (tlv_hdr_len + tlv->len);
		tlv = (IEEEtypes_Header_t *)((t_u8 *)tlv + tlv_hdr_len +
					     tlv->len);
	}
	sys_cfg->key_mgmt = priv->uap_key_mgmt;
	if (sys_cfg->key_mgmt & KEY_MGMT_PSK)
		sys_cfg->key_mgmt_operation |= 0x01;
	if (sys_cfg->key_mgmt & KEY_MGMT_EAP)
		sys_cfg->key_mgmt_operation |= 0x03;

	if (sys_cfg->protocol) {
		if (MLAN_STATUS_SUCCESS !=
		    woal_set_get_sys_config(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT,
					    sys_cfg)) {
			PRINTM(MERROR, "Error setting AP configuration\n");
			ret = -EFAULT;
			goto done;
		}
		priv->pairwise_cipher = 0;
		priv->group_cipher = 0;

		/* custom IE command to set priv->bcn_ie_buf */
		if (MLAN_STATUS_SUCCESS !=
#define UAP_RSN_MASK (BIT(8) | BIT(5) | BIT(1) | BIT(3))
		    woal_set_get_custom_ie(priv, UAP_RSN_MASK, priv->bcn_ie_buf,
					   priv->bcn_ie_len)) {
			PRINTM(MERROR, "Error setting wpa-rsn IE\n");
			ret = -EFAULT;
		}
	} else if (dwrq->length == 0) {
		/* custom IE command to re-set priv->bcn_ie_buf */
		if (MLAN_STATUS_SUCCESS !=
		    woal_set_get_custom_ie(priv, 0, priv->bcn_ie_buf,
					   priv->bcn_ie_len)) {
			PRINTM(MERROR, "Error resetting wpa-rsn IE\n");
			ret = -EFAULT;
		}
		priv->bcn_ie_len = 0;
	}

done:
	kfree(sys_cfg);
	LEAVE();
	return ret;
}

/**
 *  @brief  Extended version of encoding configuration
 *
 *  @param dev          A pointer to net_device structure
 *  @param info         A pointer to iw_request_info structure
 *  @param dwrq         A pointer to iw_point structure
 *  @param extra        A pointer to extra data buf
 *
 *  @return              0 --success, otherwise fail
 */
static int woal_set_encode_ext(struct net_device *dev,
			       struct iw_request_info *info,
			       union iwreq_data *wrqu, char *extra)
{
	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
	moal_private *priv = (moal_private *)netdev_priv(dev);
	struct iw_point *dwrq = &wrqu->data;
	int key_index;
	t_u8 *pkey_material = NULL;
	mlan_ioctl_req *req = NULL;
	mlan_ds_sec_cfg *sec = NULL;
	mlan_uap_bss_param *sys_cfg = NULL;
	wep_key *pwep_key = NULL;
	int ret = 0;
	mlan_status status = MLAN_STATUS_SUCCESS;

	ENTER();

	key_index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
	if (key_index < 0 || key_index > 5) {
		ret = -EINVAL;
		goto done;
	}
	if (ext->key_len > (dwrq->length - sizeof(struct iw_encode_ext))) {
		ret = -EINVAL;
		goto done;
	}
	sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC);
	if (!sys_cfg) {
		PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n");
		ret = -EFAULT;
		goto done;
	}

	/* Initialize the invalid values so that the correct values
	 * below are downloaded to firmware */
	woal_set_sys_config_invalid_data(sys_cfg);

	pkey_material = (t_u8 *)(ext + 1);
	/* Disable Key */
	if ((dwrq->flags & IW_ENCODE_DISABLED) && !ext->key_len) {
		sys_cfg->protocol = PROTOCOL_NO_SECURITY;
	} else if (ext->alg == IW_ENCODE_ALG_WEP) {
		sys_cfg->protocol = PROTOCOL_STATIC_WEP;
		/* Set WEP key */
		switch (key_index) {
		case 0:
			pwep_key = &sys_cfg->wep_cfg.key0;
			break;
		case 1:
			pwep_key = &sys_cfg->wep_cfg.key1;
			break;
		case 2:
			pwep_key = &sys_cfg->wep_cfg.key2;
			break;
		case 3:
			pwep_key = &sys_cfg->wep_cfg.key3;
			break;
		}
		if (pwep_key) {
			pwep_key->key_index = key_index;
			pwep_key->is_default = MTRUE;
			pwep_key->length = ext->key_len;
			moal_memcpy_ext(priv->phandle, pwep_key->key,
					pkey_material, ext->key_len,
					sizeof(pwep_key->key));
		}
	} else {
		/* Set GTK/PTK key */
		req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
		if (req == NULL) {
			ret = -ENOMEM;
			goto done;
		}
		sec = (mlan_ds_sec_cfg *)req->pbuf;
		sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY;
		req->req_id = MLAN_IOCTL_SEC_CFG;
		req->action = MLAN_ACT_SET;
		sec->param.encrypt_key.key_len = ext->key_len;
		sec->param.encrypt_key.key_index = key_index;
		moal_memcpy_ext(priv->phandle,
				sec->param.encrypt_key.key_material,
				pkey_material, ext->key_len,
				sizeof(sec->param.encrypt_key.key_material));
		moal_memcpy_ext(priv->phandle, sec->param.encrypt_key.mac_addr,
				ext->addr.sa_data, ETH_ALEN,
				sizeof(sec->param.encrypt_key.mac_addr));
		sec->param.encrypt_key.key_flags = ext->ext_flags;
		if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
			moal_memcpy_ext(priv->phandle,
					sec->param.encrypt_key.pn,
					(t_u8 *)ext->rx_seq, SEQ_MAX_SIZE,
					sizeof(sec->param.encrypt_key.pn));
			DBG_HEXDUMP(MCMD_D, "Uap Rx PN",
				    sec->param.encrypt_key.pn, SEQ_MAX_SIZE);
		}
		if (ext->ext_flags & IW_ENCODE_EXT_TX_SEQ_VALID) {
			moal_memcpy_ext(priv->phandle,
					sec->param.encrypt_key.pn,
					(t_u8 *)ext->tx_seq, SEQ_MAX_SIZE,
					sizeof(sec->param.encrypt_key.pn));
			DBG_HEXDUMP(MCMD_D, "Uap Tx PN",
				    sec->param.encrypt_key.pn, SEQ_MAX_SIZE);
		}
		PRINTM(MIOCTL,
		       "set uap wpa key key_index=%d, key_len=%d key_flags=0x%x " MACSTR
		       "\n",
		       key_index, ext->key_len,
		       sec->param.encrypt_key.key_flags,
		       MAC2STR(sec->param.encrypt_key.mac_addr));
		DBG_HEXDUMP(MCMD_D, "uap wpa key", pkey_material, ext->key_len);
#define IW_ENCODE_ALG_AES_CMAC 5

		if (ext->alg == IW_ENCODE_ALG_AES_CMAC)
			sec->param.encrypt_key.key_flags |=
				KEY_FLAG_AES_MCAST_IGTK;
		status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
		if (status != MLAN_STATUS_SUCCESS)
			ret = -EFAULT;
		/* Cipher set will be done in set generic IE */
		priv->pairwise_cipher = ext->alg;
		priv->group_cipher = ext->alg;
		goto done; /* No AP configuration */
	}
	if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_SET,
							   MOAL_IOCTL_WAIT,
							   sys_cfg)) {
		PRINTM(MERROR, "Error setting AP confiruration\n");
		ret = -EFAULT;
		goto done;
	}

done:
	kfree(sys_cfg);
	if (status != MLAN_STATUS_PENDING)
		kfree(req);
	LEAVE();
	return ret;
}

/**
 *  @brief  Extended version of encoding configuration
 *
 *  @param dev          A pointer to net_device structure
 *  @param info         A pointer to iw_request_info structure
 *  @param dwrq         A pointer to iw_point structure
 *  @param extra        A pointer to extra data buf
 *
 *  @return             -EOPNOTSUPP
 */
static int woal_get_encode_ext(struct net_device *dev,
			       struct iw_request_info *info,
			       union iwreq_data *wrqu, char *extra)
{
	ENTER();
	LEAVE();
	return -EOPNOTSUPP;
}

/**
 *  @brief  Request MLME operation
 *
 *  @param dev          A pointer to net_device structure
 *  @param info         A pointer to iw_request_info structure
 *  @param dwrq         A pointer to iw_point structure
 *  @param extra        A pointer to extra data buf
 *
 *  @return             0--success, otherwise fail
 */
static int woal_set_mlme(struct net_device *dev, struct iw_request_info *info,
			 union iwreq_data *wrqu, char *extra)
{
	struct iw_mlme *mlme = (struct iw_mlme *)extra;
	moal_private *priv = (moal_private *)netdev_priv(dev);
	mlan_ds_bss *bss = NULL;
	mlan_ds_get_info *pinfo = NULL;
	mlan_ioctl_req *req = NULL;
	mlan_ds_sta_list *sta_list = NULL;
	const t_u8 bc_addr[] = {0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF};
	t_u8 sta_addr[ETH_ALEN];
	int ret = 0, i;
	mlan_status status = MLAN_STATUS_SUCCESS;

	ENTER();

	memset(sta_addr, 0, ETH_ALEN);
	if ((mlme->cmd == IW_MLME_DEAUTH) || (mlme->cmd == IW_MLME_DISASSOC)) {
		moal_memcpy_ext(priv->phandle, sta_addr,
				(t_u8 *)mlme->addr.sa_data, ETH_ALEN,
				sizeof(sta_addr));
		PRINTM(MIOCTL, "Deauth station: " MACSTR ", reason=%d\n",
		       MAC2STR(sta_addr), mlme->reason_code);

		/* FIXME: For flushing all stations we need to use zero MAC,
		 * but right now the FW does not support this. So, manually
		 * delete each one individually.
		 */
		/* If deauth all station, get the connected STA list first */
		if (!memcmp(bc_addr, sta_addr, ETH_ALEN)) {
			PRINTM(MIOCTL, "Deauth all stations\n");
			req = woal_alloc_mlan_ioctl_req(
				sizeof(mlan_ds_get_info) +
				(MAX_STA_LIST_IE_SIZE * MAX_NUM_CLIENTS));
			if (req == NULL) {
				LEAVE();
				return -ENOMEM;
			}
			pinfo = (mlan_ds_get_info *)req->pbuf;
			pinfo->sub_command = MLAN_OID_UAP_STA_LIST;
			req->req_id = MLAN_IOCTL_GET_INFO;
			req->action = MLAN_ACT_GET;
			status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
			if (status != MLAN_STATUS_SUCCESS) {
				ret = -EFAULT;
				goto done;
			}
			sta_list =
				kmalloc(sizeof(mlan_ds_sta_list), GFP_KERNEL);
			if (sta_list == NULL) {
				PRINTM(MERROR, "Memory allocation failed!\n");
				ret = -ENOMEM;
				goto done;
			}
			moal_memcpy_ext(priv->phandle, sta_list,
					&pinfo->param.sta_list,
					sizeof(mlan_ds_sta_list),
					sizeof(mlan_ds_sta_list));
			kfree(req);
			req = NULL;
		}
		req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
		if (req == NULL) {
			ret = -ENOMEM;
			goto done;
		}
		bss = (mlan_ds_bss *)req->pbuf;
		bss->sub_command = MLAN_OID_UAP_DEAUTH_STA;
		req->req_id = MLAN_IOCTL_BSS;
		req->action = MLAN_ACT_SET;

		if (sta_list && !memcmp(bc_addr, sta_addr, ETH_ALEN)) {
			for (i = 0; i < sta_list->sta_count; i++) {
				moal_memcpy_ext(
					priv->phandle,
					bss->param.deauth_param.mac_addr,
					sta_list->info[i].mac_address, ETH_ALEN,
					sizeof(bss->param.deauth_param
						       .mac_addr));
				bss->param.deauth_param.reason_code =
					mlme->reason_code;

				status = woal_request_ioctl(priv, req,
							    MOAL_IOCTL_WAIT);
				if (status != MLAN_STATUS_SUCCESS) {
					ret = -EFAULT;
					goto done;
				}
			}
		} else {
			moal_memcpy_ext(
				priv->phandle, bss->param.deauth_param.mac_addr,
				sta_addr, ETH_ALEN,
				sizeof(bss->param.deauth_param.mac_addr));
			bss->param.deauth_param.reason_code = mlme->reason_code;

			status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
			if (status != MLAN_STATUS_SUCCESS) {
				ret = -EFAULT;
				goto done;
			}
		}
	}

done:
	if (status != MLAN_STATUS_PENDING)
		kfree(req);
	kfree(sta_list);
	LEAVE();
	return ret;
}

/** @brief Set authentication mode parameters
 *
 *  @param dev                  A pointer to net_device structure
 *  @param info                 A pointer to iw_request_info structure
 *  @param vwrq                 A pointer to iw_param structure
 *  @param extra                A pointer to extra data buf
 *
 *  @return                     0 --success, otherwise fail
 */
static int woal_set_auth(struct net_device *dev, struct iw_request_info *info,
			 union iwreq_data *wrqu, char *extra)
{
	moal_private *priv = (moal_private *)netdev_priv(dev);
	struct iw_param *vwrq = &wrqu->param;
	mlan_uap_bss_param *sys_cfg = NULL;

	ENTER();

	sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC);
	if (!sys_cfg) {
		PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n");
		LEAVE();
		return -EFAULT;
	}
	/* Initialize the invalid values so that the correct values
	 * below are downloaded to firmware */
	woal_set_sys_config_invalid_data(sys_cfg);

	switch (vwrq->flags & IW_AUTH_INDEX) {
	case IW_AUTH_CIPHER_PAIRWISE:
		/* Rest are not supported now */
		if (vwrq->value & IW_AUTH_CIPHER_NONE)
			/* XXX Do not delete no-operation line */
			;
		else if (vwrq->value & IW_AUTH_CIPHER_WEP40)
			/* XXX Do not delete no-operation line */
			;
		else if (vwrq->value & IW_AUTH_CIPHER_WEP104)
			/* XXX Do not delete no-operation line */
			;
		else if (vwrq->value == IW_AUTH_CIPHER_TKIP) {
			sys_cfg->wpa_cfg.pairwise_cipher_wpa = CIPHER_TKIP;
			sys_cfg->wpa_cfg.pairwise_cipher_wpa2 = CIPHER_TKIP;
			priv->pairwise_cipher = CIPHER_TKIP;
			if (!priv->uap_key_mgmt)
				priv->uap_key_mgmt = KEY_MGMT_PSK;
			PRINTM(MINFO, "Set Auth Cipher TKIP\n");
		} else if (vwrq->value == IW_AUTH_CIPHER_CCMP) {
			sys_cfg->wpa_cfg.pairwise_cipher_wpa = CIPHER_AES_CCMP;
			sys_cfg->wpa_cfg.pairwise_cipher_wpa2 = CIPHER_AES_CCMP;
			priv->pairwise_cipher = CIPHER_AES_CCMP;
			if (!priv->uap_key_mgmt)
				priv->uap_key_mgmt = KEY_MGMT_PSK;
			PRINTM(MINFO, "Set Auth Cipher CCMP\n");
		} else if (vwrq->value ==
			   (IW_AUTH_CIPHER_TKIP | IW_AUTH_CIPHER_CCMP)) {
			sys_cfg->wpa_cfg.pairwise_cipher_wpa =
				(CIPHER_TKIP | CIPHER_AES_CCMP);
			sys_cfg->wpa_cfg.pairwise_cipher_wpa2 =
				(CIPHER_TKIP | CIPHER_AES_CCMP);
			priv->pairwise_cipher = (CIPHER_TKIP | CIPHER_AES_CCMP);
			if (!priv->uap_key_mgmt)
				priv->uap_key_mgmt = KEY_MGMT_PSK;
			PRINTM(MINFO, "Set Auth Cipher TKIP + CCMP\n");
		}
		break;
	case IW_AUTH_CIPHER_GROUP:
		/* Rest are not supported now */
		if (vwrq->value & IW_AUTH_CIPHER_NONE)
			/* XXX Do not delete no-operation line */
			;
		else if (vwrq->value & IW_AUTH_CIPHER_WEP40)
			/* XXX Do not delete no-operation line */
			;
		else if (vwrq->value & IW_AUTH_CIPHER_WEP104)
			/* XXX Do not delete no-operation line */
			;
		else if (vwrq->value & IW_AUTH_CIPHER_TKIP) {
			sys_cfg->wpa_cfg.group_cipher = CIPHER_TKIP;
			priv->group_cipher = CIPHER_TKIP;
			if (!priv->uap_key_mgmt)
				priv->uap_key_mgmt = KEY_MGMT_PSK;
			PRINTM(MINFO, "Set Auth Cipher TKIP\n");
		} else if (vwrq->value & IW_AUTH_CIPHER_CCMP) {
			sys_cfg->wpa_cfg.group_cipher = CIPHER_AES_CCMP;
			priv->group_cipher = CIPHER_AES_CCMP;
			if (!priv->uap_key_mgmt)
				priv->uap_key_mgmt = KEY_MGMT_PSK;
			PRINTM(MINFO, "Set Auth Cipher CCMP\n");
		}
		break;
	case IW_AUTH_80211_AUTH_ALG:
		switch (vwrq->value) {
		case IW_AUTH_ALG_SHARED_KEY:
			PRINTM(MINFO, "Auth mode shared key!\n");
			sys_cfg->auth_mode = MLAN_AUTH_MODE_SHARED;
			break;
		case IW_AUTH_ALG_LEAP:
			break;
		case IW_AUTH_ALG_OPEN_SYSTEM:
			PRINTM(MINFO, "Auth mode open!\n");
			sys_cfg->auth_mode = MLAN_AUTH_MODE_OPEN;
			break;
		default:
			PRINTM(MINFO, "Auth mode auto!\n");
			break;
		}
		break;
	case IW_AUTH_WPA_VERSION:
		switch (vwrq->value) {
		case IW_AUTH_WPA_VERSION_DISABLED:
			sys_cfg->protocol = PROTOCOL_NO_SECURITY;
			break;
		case IW_AUTH_WPA_VERSION_WPA:
			sys_cfg->protocol = PROTOCOL_WPA;
			break;
		case IW_AUTH_WPA_VERSION_WPA2:
			sys_cfg->protocol = PROTOCOL_WPA2;
			break;
		case IW_AUTH_WPA_VERSION_WPA | IW_AUTH_WPA_VERSION_WPA2:
			sys_cfg->protocol = PROTOCOL_WPA2_MIXED;
			break;
		default:
			break;
		}
		priv->uap_protocol = sys_cfg->protocol;
		break;
	case IW_AUTH_KEY_MGMT:
		switch (vwrq->value) {
		case IW_AUTH_KEY_MGMT_802_1X:
			sys_cfg->key_mgmt |= KEY_MGMT_EAP;
			priv->uap_key_mgmt |= KEY_MGMT_EAP;
			break;
		case IW_AUTH_KEY_MGMT_PSK:
			sys_cfg->key_mgmt |= KEY_MGMT_PSK;
			priv->uap_key_mgmt |= KEY_MGMT_PSK;
			break;
		default:
			break;
		}
		break;
	case IW_AUTH_WPA_ENABLED:
	case IW_AUTH_TKIP_COUNTERMEASURES:
	case IW_AUTH_DROP_UNENCRYPTED:
	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
	case IW_AUTH_ROAMING_CONTROL:
	case IW_AUTH_PRIVACY_INVOKED:
	default:
		kfree(sys_cfg);
		LEAVE();
		return -EOPNOTSUPP; /* No AP configuration */
	}
	if (!sys_cfg->key_mgmt)
		sys_cfg->key_mgmt = priv->uap_key_mgmt;
	if (sys_cfg->key_mgmt & KEY_MGMT_PSK)
		sys_cfg->key_mgmt_operation |= 0x01;
	if (sys_cfg->key_mgmt & KEY_MGMT_EAP)
		sys_cfg->key_mgmt_operation |= 0x03;
	if (!sys_cfg->protocol)
		sys_cfg->protocol = priv->uap_protocol;

	/* Set AP configuration */
	if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_SET,
							   MOAL_IOCTL_WAIT,
							   sys_cfg)) {
		PRINTM(MERROR, "Error setting AP confiruration\n");
		goto done;
	}

done:
	kfree(sys_cfg);
	LEAVE();
	return 0;
}

/**
 *  @brief Get authentication mode parameters
 *
 *  @param dev                  A pointer to net_device structure
 *  @param info                 A pointer to iw_request_info structure
 *  @param vwrq                 A pointer to iw_param structure
 *  @param extra                A pointer to extra data buf
 *
 *  @return                     0 --success, otherwise fail
 */
static int woal_get_auth(struct net_device *dev, struct iw_request_info *info,
			 union iwreq_data *wrqu, char *extra)
{
	moal_private *priv = (moal_private *)netdev_priv(dev);
	struct iw_param *vwrq = &wrqu->param;
	mlan_uap_bss_param *ap_cfg = NULL;

	ENTER();

	ap_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC);
	if (!ap_cfg) {
		PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n");
		LEAVE();
		return -EFAULT;
	}

	if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET,
							   MOAL_IOCTL_WAIT,
							   ap_cfg)) {
		PRINTM(MERROR, "Error getting AP confiruration\n");
		kfree(ap_cfg);
		LEAVE();
		return -EFAULT;
	}
	switch (vwrq->flags & IW_AUTH_INDEX) {
	case IW_AUTH_CIPHER_PAIRWISE:
		if (ap_cfg->wpa_cfg.pairwise_cipher_wpa == CIPHER_TKIP ||
		    ap_cfg->wpa_cfg.pairwise_cipher_wpa2 == CIPHER_TKIP)
			vwrq->value = IW_AUTH_CIPHER_TKIP;
		else if (ap_cfg->wpa_cfg.pairwise_cipher_wpa ==
				 CIPHER_AES_CCMP ||
			 ap_cfg->wpa_cfg.pairwise_cipher_wpa2 ==
				 CIPHER_AES_CCMP)
			vwrq->value = IW_AUTH_CIPHER_CCMP;
		else
			vwrq->value = IW_AUTH_CIPHER_NONE;
		break;
	case IW_AUTH_CIPHER_GROUP:
		if (ap_cfg->wpa_cfg.group_cipher == CIPHER_TKIP)
			vwrq->value = IW_AUTH_CIPHER_TKIP;
		else if (ap_cfg->wpa_cfg.group_cipher == CIPHER_AES_CCMP)
			vwrq->value = IW_AUTH_CIPHER_CCMP;
		else
			vwrq->value = IW_AUTH_CIPHER_NONE;
		break;
	case IW_AUTH_80211_AUTH_ALG:
		if (ap_cfg->auth_mode == MLAN_AUTH_MODE_SHARED)
			vwrq->value = IW_AUTH_ALG_SHARED_KEY;
		else if (ap_cfg->auth_mode == MLAN_AUTH_MODE_NETWORKEAP)
			vwrq->value = IW_AUTH_ALG_LEAP;
		else
			vwrq->value = IW_AUTH_ALG_OPEN_SYSTEM;
		break;
	case IW_AUTH_WPA_ENABLED:
		if (ap_cfg->protocol == PROTOCOL_WPA ||
		    ap_cfg->protocol == PROTOCOL_WPA2 ||
		    ap_cfg->protocol == PROTOCOL_WPA2_MIXED)
			vwrq->value = 1;
		else
			vwrq->value = 0;
		break;
	case IW_AUTH_KEY_MGMT:
		if (ap_cfg->key_mgmt & KEY_MGMT_EAP)
			vwrq->value |= IW_AUTH_KEY_MGMT_802_1X;
		if (ap_cfg->key_mgmt & KEY_MGMT_PSK)
			vwrq->value |= IW_AUTH_KEY_MGMT_PSK;
		break;
	case IW_AUTH_WPA_VERSION:
	case IW_AUTH_TKIP_COUNTERMEASURES:
	case IW_AUTH_DROP_UNENCRYPTED:
	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
	case IW_AUTH_ROAMING_CONTROL:
	case IW_AUTH_PRIVACY_INVOKED:
	default:
		kfree(ap_cfg);
		LEAVE();
		return -EOPNOTSUPP;
	}
	kfree(ap_cfg);
	LEAVE();
	return 0;
}
#endif /* WE >= 18 */

/* Data rate listing
 *      MULTI_BANDS:
 *              abg             a       b       b/g
 *  Infra       G(12)           A(8)    B(4)    G(12)
 *  Adhoc       A+B(12)         A(8)    B(4)    B(4)
 *      non-MULTI_BANDS:
										b       b/g
 *  Infra                               B(4)    G(12)
 *  Adhoc                               B(4)    B(4)
 */
/**
 *  @brief Get Range Info
 *
 *  @param dev                  A pointer to net_device structure
 *  @param info                 A pointer to iw_request_info structure
 *  @param dwrq                 A pointer to iw_point structure
 *  @param extra                A pointer to extra data buf
 *
 *  @return                     0 --success, otherwise fail
 */
static int woal_get_range(struct net_device *dev, struct iw_request_info *info,
			  union iwreq_data *wrqu, char *extra)
{
	moal_private *priv = (moal_private *)netdev_priv(dev);
	struct iw_point *dwrq = &wrqu->data;
	mlan_uap_bss_param *ap_cfg = NULL;
	struct iw_range *range = (struct iw_range *)extra;
	t_u8 band = 0;
	int i;

	ENTER();

	ap_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC);
	if (!ap_cfg) {
		PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n");
		LEAVE();
		return -EFAULT;
	}

	if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET,
							   MOAL_IOCTL_WAIT,
							   ap_cfg)) {
		PRINTM(MERROR, "Error getting AP confiruration\n");
		kfree(ap_cfg);
		LEAVE();
		return -EFAULT;
	}
	dwrq->length = sizeof(struct iw_range);
	memset(range, 0, sizeof(struct iw_range));

	range->min_nwid = 0;
	range->max_nwid = 0;

	range->num_bitrates = MAX_DATA_RATES;
	for (i = 0;
	     i < MIN(MAX_DATA_RATES, IW_MAX_BITRATES) && ap_cfg->rates[i];
	     i++) {
		range->bitrate[i] = (ap_cfg->rates[i] & 0x7f) * 500000;
	}
	range->num_bitrates = i;
	PRINTM(MINFO, "IW_MAX_BITRATES=%d num_bitrates=%d\n", IW_MAX_BITRATES,
	       range->num_bitrates);

	range->num_frequency = MIN(ap_cfg->num_of_chan, IW_MAX_FREQUENCIES);

	for (i = 0; i < range->num_frequency; i++) {
		range->freq[i].i = (long)ap_cfg->chan_list[i].chan_number;
		band = (ap_cfg->chan_list[i].bandcfg.chanBand == BAND_5GHZ);
		range->freq[i].m =
			(long)channel_to_frequency(
				ap_cfg->chan_list[i].chan_number, band) *
			100000;
		range->freq[i].e = 1;
	}

	PRINTM(MINFO, "IW_MAX_FREQUENCIES=%d num_frequency=%d\n",
	       IW_MAX_FREQUENCIES, range->num_frequency);

	range->num_channels = range->num_frequency;

	woal_sort_channels(&range->freq[0], range->num_frequency);

	/*
	 * Set an indication of the max TCP throughput in bit/s that we can
	 * expect using this interface
	 */
	if (i > 2)
		range->throughput = 5000 * 1000;
	else
		range->throughput = 1500 * 1000;

	range->min_rts = MLAN_RTS_MIN_VALUE;
	range->max_rts = MLAN_RTS_MAX_VALUE;
	range->min_frag = MLAN_FRAG_MIN_VALUE;
	range->max_frag = MLAN_FRAG_MAX_VALUE;

	range->encoding_size[0] = 5;
	range->encoding_size[1] = 13;
	range->num_encoding_sizes = 2;
	range->max_encoding_tokens = 4;

/** Minimum power period */
#define IW_POWER_PERIOD_MIN 1000000 /* 1 sec */
/** Maximum power period */
#define IW_POWER_PERIOD_MAX 120000000 /* 2 min */
/** Minimum power timeout value */
#define IW_POWER_TIMEOUT_MIN 1000 /* 1 ms  */
/** Maximim power timeout value */
#define IW_POWER_TIMEOUT_MAX 1000000 /* 1 sec */

	/* Power Management duration & timeout */
	range->min_pmp = IW_POWER_PERIOD_MIN;
	range->max_pmp = IW_POWER_PERIOD_MAX;
	range->min_pmt = IW_POWER_TIMEOUT_MIN;
	range->max_pmt = IW_POWER_TIMEOUT_MAX;
	range->pmp_flags = IW_POWER_PERIOD;
	range->pmt_flags = IW_POWER_TIMEOUT;
	range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;

	/*
	 * Minimum version we recommend
	 */
	range->we_version_source = 15;

	/*
	 * Version we are compiled with
	 */
	range->we_version_compiled = WIRELESS_EXT;

	range->retry_capa = IW_RETRY_LIMIT;
	range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX;

	range->min_retry = MLAN_TX_RETRY_MIN;
	range->max_retry = MLAN_TX_RETRY_MAX;

#if (WIRELESS_EXT >= 18)
	if (ap_cfg->protocol & PROTOCOL_WPA)
		range->enc_capa |= IW_ENC_CAPA_WPA;
	if (ap_cfg->protocol & PROTOCOL_WPA2)
		range->enc_capa |= IW_ENC_CAPA_WPA2;
	if (ap_cfg->wpa_cfg.pairwise_cipher_wpa == CIPHER_TKIP ||
	    ap_cfg->wpa_cfg.pairwise_cipher_wpa2 == CIPHER_TKIP ||
	    ap_cfg->wpa_cfg.group_cipher == CIPHER_TKIP)
		range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP;
	if (ap_cfg->wpa_cfg.pairwise_cipher_wpa == CIPHER_AES_CCMP ||
	    ap_cfg->wpa_cfg.pairwise_cipher_wpa2 == CIPHER_AES_CCMP ||
	    ap_cfg->wpa_cfg.group_cipher == CIPHER_AES_CCMP)
		range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP;
#endif
	kfree(ap_cfg);
	LEAVE();
	return 0;
}

/**
 *  @brief Set priv command
 *
 *  @param dev          A pointer to net_device structure
 *  @param info         A pointer to iw_request_info structure
 *  @param dwrq         A pointer to iw_point structure
 *  @param extra        A pointer to extra data buf
 *
 *  @return                     0 --success, otherwise fail
 */
static int woal_set_priv(struct net_device *dev, struct iw_request_info *info,
			 union iwreq_data *wrqu, char *extra)
{
	ENTER();
	LEAVE();
	return -EOPNOTSUPP;
}

/**
 *  @brief Set essid
 *
 *  @param dev          A pointer to net_device structure
 *  @param info         A pointer to iw_request_info structure
 *  @param dwrq         A pointer to iw_point structure
 *  @param extra        A pointer to extra data buf
 *
 *  @return             0--success, otherwise fail
 */
static int woal_set_essid(struct net_device *dev, struct iw_request_info *info,
			  union iwreq_data *wrqu, char *extra)
{
	moal_private *priv = (moal_private *)netdev_priv(dev);
	struct iw_point *dwrq = &wrqu->data;
	mlan_uap_bss_param *sys_cfg = NULL;
	int ret = 0;

	ENTER();

	/* Check the size of the string */
	if (dwrq->length > IW_ESSID_MAX_SIZE + 1) {
		ret = -E2BIG;
		goto done;
	}
	sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC);
	if (!sys_cfg) {
		PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n");
		ret = -EFAULT;
		goto done;
	}

	/* Initialize the invalid values so that the correct values
	 * below are downloaded to firmware */
	woal_set_sys_config_invalid_data(sys_cfg);

	/* Set the SSID */
#if WIRELESS_EXT > 20
	sys_cfg->ssid.ssid_len = dwrq->length;
#else
	sys_cfg->ssid.ssid_len = dwrq->length - 1;
#endif

	moal_memcpy_ext(priv->phandle, sys_cfg->ssid.ssid, extra,
			sys_cfg->ssid.ssid_len, sizeof(sys_cfg->ssid.ssid));
	if (!sys_cfg->ssid.ssid_len || sys_cfg->ssid.ssid[0] < 0x20) {
		PRINTM(MERROR, "Invalid SSID - aborting set_essid\n");
		ret = -EINVAL;
		goto done;
	}
	PRINTM(MINFO, "Requested new SSID = %s\n",
	       (sys_cfg->ssid.ssid_len > 0) ? (char *)sys_cfg->ssid.ssid :
					      "NULL");

	/* Set AP configuration */
	if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_SET,
							   MOAL_IOCTL_WAIT,
							   sys_cfg)) {
		PRINTM(MERROR, "Error setting AP confiruration\n");
		ret = -EFAULT;
		goto done;
	}

done:
	kfree(sys_cfg);
	LEAVE();
	return ret;
}

/**
 *  @brief Get current essid
 *
 *  @param dev      A pointer to net_device structure
 *  @param info     A pointer to iw_request_info structure
 *  @param dwrq     A pointer to iw_point structure
 *  @param extra    A pointer to extra data buf
 *
 *  @return         0--success, otherwise fail
 */
static int woal_get_essid(struct net_device *dev, struct iw_request_info *info,
			  struct iw_point *dwrq, char *extra)
{
	moal_private *priv = (moal_private *)netdev_priv(dev);
	mlan_uap_bss_param *ap_cfg = NULL;

	ENTER();

	ap_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC);
	if (!ap_cfg) {
		PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n");
		return -EFAULT;
	}

	if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET,
							   MOAL_IOCTL_WAIT,
							   ap_cfg)) {
		PRINTM(MERROR, "Error getting AP confiruration\n");
		kfree(ap_cfg);
		LEAVE();
		return -EFAULT;
	}

	if (priv->bss_started) {
		dwrq->length = MIN(dwrq->length, ap_cfg->ssid.ssid_len);
		moal_memcpy_ext(priv->phandle, extra, ap_cfg->ssid.ssid,
				dwrq->length, dwrq->length);
	} else
		dwrq->length = 0;

	dwrq->flags = 1;
	kfree(ap_cfg);
	LEAVE();
	return 0;
}

/**
 * iwconfig settable callbacks
 */
static const iw_handler woal_handler[] = {
	(iw_handler)woal_config_commit, /* SIOCSIWCOMMIT */
	(iw_handler)woal_get_name, /* SIOCGIWNAME */
	(iw_handler)NULL, /* SIOCSIWNWID */
	(iw_handler)NULL, /* SIOCGIWNWID */
	(iw_handler)woal_set_freq, /* SIOCSIWFREQ */
	(iw_handler)woal_get_freq, /* SIOCGIWFREQ */
	(iw_handler)woal_set_bss_mode, /* SIOCSIWMODE */
	(iw_handler)woal_get_bss_mode, /* SIOCGIWMODE */
	(iw_handler)NULL, /* SIOCSIWSENS */
	(iw_handler)NULL, /* SIOCGIWSENS */
	(iw_handler)NULL, /* SIOCSIWRANGE */
	(iw_handler)woal_get_range, /* SIOCGIWRANGE */
	(iw_handler)woal_set_priv, /* SIOCSIWPRIV */
	(iw_handler)NULL, /* SIOCGIWPRIV */
	(iw_handler)NULL, /* SIOCSIWSTATS */
	(iw_handler)NULL, /* SIOCGIWSTATS */
#if WIRELESS_EXT > 15
#ifdef CONFIG_WEXT_SPY
	iw_handler_set_spy, /* SIOCSIWSPY */
	iw_handler_get_spy, /* SIOCGIWSPY */
	iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
	iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
#else
	(iw_handler)NULL, /* -- hole -- */
	(iw_handler)NULL, /* -- hole -- */
	(iw_handler)NULL, /* -- hole -- */
	(iw_handler)NULL, /* -- hole -- */
#endif
#else /* WIRELESS_EXT > 15 */
	(iw_handler)NULL, /* -- hole -- */
	(iw_handler)NULL, /* -- hole -- */
	(iw_handler)NULL, /* -- hole -- */
	(iw_handler)NULL, /* -- hole -- */
#endif /* WIRELESS_EXT > 15 */
	(iw_handler)woal_set_wap, /* SIOCSIWAP */
	(iw_handler)woal_get_wap, /* SIOCGIWAP */
#if WIRELESS_EXT >= 18
	(iw_handler)woal_set_mlme, /* SIOCSIWMLME  */
#else
	(iw_handler)NULL, /* -- hole -- */
#endif
	/* (iw_handler) wlan_get_aplist, */ /* SIOCGIWAPLIST */
	NULL, /* SIOCGIWAPLIST */
#if WIRELESS_EXT > 13
	(iw_handler)NULL, /* SIOCSIWSCAN */
	(iw_handler)NULL, /* SIOCGIWSCAN */
#else /* WIRELESS_EXT > 13 */
	(iw_handler)NULL, /* SIOCSIWSCAN */
	(iw_handler)NULL, /* SIOCGIWSCAN */
#endif /* WIRELESS_EXT > 13 */
	(iw_handler)woal_set_essid, /* SIOCSIWESSID */
	(iw_handler)woal_get_essid, /* SIOCGIWESSID */
	(iw_handler)NULL, /* SIOCSIWNICKN */
	(iw_handler)NULL, /* SIOCGIWNICKN */
	(iw_handler)NULL, /* -- hole -- */
	(iw_handler)NULL, /* -- hole -- */
	(iw_handler)NULL, /* SIOCSIWRATE */
	(iw_handler)NULL, /* SIOCGIWRATE */
	(iw_handler)NULL, /* SIOCSIWRTS */
	(iw_handler)NULL, /* SIOCGIWRTS */
	(iw_handler)NULL, /* SIOCSIWFRAG */
	(iw_handler)NULL, /* SIOCGIWFRAG */
	(iw_handler)NULL, /* SIOCSIWTXPOW */
	(iw_handler)NULL, /* SIOCGIWTXPOW */
	(iw_handler)NULL, /* SIOCSIWRETRY */
	(iw_handler)NULL, /* SIOCGIWRETRY */
	(iw_handler)woal_set_encode, /* SIOCSIWENCODE */
	(iw_handler)woal_get_encode, /* SIOCGIWENCODE */
	(iw_handler)NULL, /* SIOCSIWPOWER */
	(iw_handler)NULL, /* SIOCGIWPOWER */
#if (WIRELESS_EXT >= 18)
	(iw_handler)NULL, /* -- hole -- */
	(iw_handler)NULL, /* -- hole -- */
	(iw_handler)woal_set_gen_ie, /* SIOCSIWGENIE */
	(iw_handler)woal_get_gen_ie, /* SIOCGIWGENIE */
	(iw_handler)woal_set_auth, /* SIOCSIWAUTH  */
	(iw_handler)woal_get_auth, /* SIOCGIWAUTH  */
	(iw_handler)woal_set_encode_ext, /* SIOCSIWENCODEEXT */
	(iw_handler)woal_get_encode_ext, /* SIOCGIWENCODEEXT */
#endif /* WIRELESSS_EXT >= 18 */
};

/**
 * iwpriv settable callbacks
 */
static const iw_handler woal_private_handler[] = {
	NULL, /* SIOCIWFIRSTPRIV */
};

/********************************************************
			Global Functions
********************************************************/

// clang-format off
/** wlan_handler_def */
struct iw_handler_def woal_uap_handler_def = {
	.num_standard = ARRAY_SIZE(woal_handler),
	.num_private = ARRAY_SIZE(woal_private_handler),
	.num_private_args = ARRAY_SIZE(woal_uap_priv_args),
	.standard = (iw_handler *)woal_handler,
	.private = (iw_handler *)woal_private_handler,
	.private_args = (struct iw_priv_args *)woal_uap_priv_args,
#if WIRELESS_EXT > 20
	.get_wireless_stats = woal_get_uap_wireless_stats,
#endif
};
// clang-format on

/**
 *  @brief Get wireless statistics
 *
 *  @param dev          A pointer to net_device structure
 *
 *  @return             A pointer to iw_statistics buf
 */
struct iw_statistics *woal_get_uap_wireless_stats(struct net_device *dev)
{
	moal_private *priv = (moal_private *)netdev_priv(dev);
	t_u16 wait_option = MOAL_IOCTL_WAIT;

	ENTER();

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
	/*
	 * Since schedule() is not allowed from an atomic context
	 * such as when dev_base_lock for netdevices is acquired
	 * for reading/writing in kernel before this call, HostCmd
	 * is issued in non-blocking way in such contexts and
	 * blocking in other cases.
	 */
	if (in_atomic() || !write_can_lock(&dev_base_lock))
		wait_option = MOAL_NO_WAIT;
#endif

	priv->w_stats.qual.qual = 0;
	priv->w_stats.qual.level = 0;
	priv->w_stats.discard.code = 0;
	priv->w_stats.status = IW_MODE_MASTER;
	woal_uap_get_stats(priv, wait_option, NULL);

	LEAVE();
	return &priv->w_stats;
}
