text:
/*
** $Id: //Department/DaVinci/BRANCHES/MT6620_WIFI_DRIVER_V2_3/os/linux/gl_wext.c#3 $
*/

/*! \file gl_wext.c
    \brief  ioctl() (mostly Linux Wireless Extensions) routines for STA driver.
*/

/*
** $Log: gl_wext.c $
 *
 * 06 13 2012 yuche.tsai
 * NULL
 * Update maintrunk driver.
 * Add support for driver compose assoc request frame.
 *
 * 01 16 2012 wh.su
 * [WCXRP00001170] [MT6620 Wi-Fi][Driver] Adding the related code for set/get band ioctl
 * Adding the template code for set / get band IOCTL (with ICS supplicant_6)..
 *
 * 01 05 2012 wh.su
 * [WCXRP00001153] [MT6620 Wi-Fi][Driver] Adding the get_ch_list and set_tx_power proto type function
 * Adding the related ioctl / wlan oid function to set the Tx power cfg.
 *
 * 01 02 2012 wh.su
 * [WCXRP00001153] [MT6620 Wi-Fi][Driver] Adding the get_ch_list and set_tx_power proto type function
 * Adding the proto type function for set_int set_tx_power and get int get_ch_list.
 *
 * 11 10 2011 cp.wu
 * [WCXRP00001098] [MT6620 Wi-Fi][Driver] Replace printk by DBG LOG macros in linux porting layer
 * 1. eliminaite direct calls to printk in porting layer.
 * 2. replaced by DBGLOG, which would be XLOG on ALPS platforms.
 *
 * 10 12 2011 wh.su
 * [WCXRP00001036] [MT6620 Wi-Fi][Driver][FW] Adding the 802.11w code for MFP
 * adding the 802.11w related function and define .
 *
 * 09 23 2011 tsaiyuan.hsu
 * [WCXRP00000979] [MT6620 Wi-Fi][DRV]] stop attempting to connect to config AP after D3 state
 * avoid entering D3 state after deep sleep.
 *
 * 07 28 2011 chinghwa.yu
 * [WCXRP00000063] Update BCM CoEx design and settings
 * Add BWCS cmd and event.
 *
 * 07 27 2011 wh.su
 * [WCXRP00000877] [MT6620 Wi-Fi][Driver] Remove the netif_carry_ok check for avoid the wpa_supplicant fail to query the ap address
 * Remove the netif check while query bssid and ssid
 *
 * 07 26 2011 chinglan.wang
 * NULL
 * [MT6620][WiFi Driver] Do not include the WSC IE in the association info packet when not do the wps connection..
 *
 * 07 18 2011 chinghwa.yu
 * [WCXRP00000063] Update BCM CoEx design and settings[WCXRP00000612] [MT6620 Wi-Fi] [FW] CSD update SWRDD algorithm
 * Add CMD/Event for RDD and BWCS.
 *
 * 05 17 2011 eddie.chen
 * [WCXRP00000603] [MT6620 Wi-Fi][DRV] Fix Klocwork warning
 * Initilize the vairlabes.
 *
 * 05 11 2011 jeffrey.chang
 * [WCXRP00000718] [MT6620 Wi-Fi] modify the behavior of setting tx power
 * modify set_tx_pow ioctl
 *
 * 03 29 2011 terry.wu
 * [WCXRP00000610] [MT 6620 Wi-Fi][Driver] Fix klocwork waring
 * [MT6620 Wi-Fi][Driver] Fix klocwork warning. Add Null pointer check on wext_get_essid. Limit the upper bound of essid storage array.
 *
 * 03 21 2011 cp.wu
 * [WCXRP00000540] [MT5931][Driver] Add eHPI8/eHPI16 support to Linux Glue Layer
 * improve portability for awareness of early version of linux kernel and wireless extension.
 *
 * 03 17 2011 chinglan.wang
 * [WCXRP00000570] [MT6620 Wi-Fi][Driver] Add Wi-Fi Protected Setup v2.0 feature
 * .
 *
 * 03 07 2011 terry.wu
 * [WCXRP00000521] [MT6620 Wi-Fi][Driver] Remove non-standard debug message
 * Toggle non-standard debug messages to comments.
 *
 * 02 21 2011 wh.su
 * [WCXRP00000483] [MT6620 Wi-Fi][Driver] Check the kalIoctl return value before doing the memory copy at linux get essid
 * fixed the potential error to do a larget memory copy while wlanoid get essid not actually running.
 *
 * 02 08 2011 george.huang
 * [WCXRP00000422] [MT6620 Wi-Fi][Driver] support query power mode OID handler
 * Support querying power mode OID.
 *
 * 01 29 2011 wh.su
 * [WCXRP00000408] [MT6620 Wi-Fi][Driver] Not doing memory alloc while ioctl set ie with length 0
 * not doing mem alloc. while set ie length already 0
 *
 * 01 20 2011 eddie.chen
 * [WCXRP00000374] [MT6620 Wi-Fi][DRV] SW debug control
 * Remove debug text.
 *
 * 01 20 2011 eddie.chen
 * [WCXRP00000374] [MT6620 Wi-Fi][DRV] SW debug control
 * Adjust OID order.
 *
 * 01 20 2011 eddie.chen
 * [WCXRP00000374] [MT6620 Wi-Fi][DRV] SW debug control
 * Add Oid for sw control debug command
 *
 * 01 11 2011 chinglan.wang
 * NULL
 * Modify to reslove the CR :[ALPS00028994] Use WEP security to connect Marvell 11N AP.  Connection establish successfully.
 * Use the WPS function to connect AP, the privacy bit always is set to 1. .
 *
 * 01 07 2011 cm.chang
 * [WCXRP00000336] [MT6620 Wi-Fi][Driver] Add test mode commands in normal phone operation
 * Add a new compiling option to control if MCR read/write is permitted
 *
 * 01 04 2011 cp.wu
 * [WCXRP00000338] [MT6620 Wi-Fi][Driver] Separate kalMemAlloc into kmalloc and vmalloc implementations to ease physically continous memory demands
 * separate kalMemAlloc() into virtually-continous and physically-continous types
 * to ease slab system pressure
 *
 * 01 04 2011 cp.wu
 * [WCXRP00000338] [MT6620 Wi-Fi][Driver] Separate kalMemAlloc into kmalloc and vmalloc implementations to ease physically continous memory demands
 * separate kalMemAlloc() into virtually-continous and physically-continous type to ease slab system pressure
 *
 * 12 31 2010 cm.chang
 * [WCXRP00000336] [MT6620 Wi-Fi][Driver] Add test mode commands in normal phone operation
 * Add some iwpriv commands to support test mode operation
 *
 * 12 15 2010 george.huang
 * [WCXRP00000152] [MT6620 Wi-Fi] AP mode power saving function
 * Support set PS profile and set WMM-PS related iwpriv.
 *
 * 12 15 2010 george.huang
 * [WCXRP00000152] [MT6620 Wi-Fi] AP mode power saving function
 * Allow change PS profile function (throught wext_set_power()).
 *
 * 12 14 2010 jeffrey.chang
 * [WCXRP00000262] [MT6620 Wi-Fi][Driver] modify the scan request ioctl to handle hidden SSID
 * handle hidden SSID
 *
 * 12 13 2010 chinglan.wang
 * NULL
 * Add WPS 1.0 feature flag to enable the WPS 1.0 function.
 *
 * 12 07 2010 cm.chang
 * [WCXRP00000238] MT6620 Wi-Fi][Driver][FW] Support regulation domain setting from NVRAM and supplicant
 * Fix compiling error
 *
 * 12 07 2010 cm.chang
 * [WCXRP00000238] MT6620 Wi-Fi][Driver][FW] Support regulation domain setting from NVRAM and supplicant
 * 1. Country code is from NVRAM or supplicant
 * 2. Change band definition in CMD/EVENT.
 *
 * 11 30 2010 cp.wu
 * [WCXRP00000213] [MT6620 Wi-Fi][Driver] Implement scanning with specified SSID for wpa_supplicant with ap_scan=1
 * .
 *
 * 11 08 2010 wh.su
 * [WCXRP00000171] [MT6620 Wi-Fi][Driver] Add message check code same behavior as mt5921
 * add the message check code from mt5921.
 *
 * 10 19 2010 jeffrey.chang
 * [WCXRP00000121] [MT6620 Wi-Fi][Driver] Temporarily disable set power mode ioctl which may cause 6620 to enter power saving
 * Temporarily disable set power mode ioctl which may cause MT6620 to enter power saving
 *
 * 10 18 2010 jeffrey.chang
 * [WCXRP00000116] [MT6620 Wi-Fi][Driver] Refine the set_scan ioctl to resolve the Android UI hanging issue
 * refine the scan ioctl to prevent hanging of Android UI
 *
 * 10 01 2010 wh.su
 * [WCXRP00000067] [MT6620 Wi-Fi][Driver] Support the android+ WAPI function
 * add the scan result with wapi ie.
 *
 * 09 30 2010 wh.su
 * [WCXRP00000072] [MT6620 Wi-Fi][Driver] Fix TKIP Counter Measure EAPoL callback register issue
 * fixed the wapi ie assigned issue.
 *
 * 09 27 2010 wh.su
 * NULL
 * [WCXRP00000067][MT6620 Wi-Fi][Driver] Support the android+ WAPI function.
 *
 * 09 21 2010 kevin.huang
 * [WCXRP00000052] [MT6620 Wi-Fi][Driver] Eliminate Linux Compile Warning
 * Eliminate Linux Compile Warning
 *
 * 09 09 2010 cp.wu
 * NULL
 * add WPS/WPA/RSN IE for Wi-Fi Direct scanning result.
 *
 * 09 06 2010 cp.wu
 * NULL
 * Androi/Linux: return current operating channel information
 *
 * 09 01 2010 wh.su
 * NULL
 * adding the wapi support for integration test.
 *
 * 08 02 2010 jeffrey.chang
 * NULL
 * enable remove key ioctl
 *
 * 08 02 2010 jeffrey.chang
 * NULL
 * 1) modify tx service thread to avoid busy looping
 * 2) add spin lock declartion for linux build
 *
 * 07 28 2010 jeffrey.chang
 * NULL
 * 1) enable encyption ioctls
 * 2) temporarily disable remove keys ioctl to prevent  TX1 busy
 *
 * 07 28 2010 jeffrey.chang
 * NULL
 * 1) remove unused spinlocks
 * 2) enable encyption ioctls
 * 3) fix scan ioctl which may cause supplicant to hang
 *
 * 07 19 2010 jeffrey.chang
 *
 * add kal api for scanning done
 *
 * 07 19 2010 jeffrey.chang
 *
 * for linux driver migration
 *
 * 07 19 2010 jeffrey.chang
 *
 * Linux port modification
 *
 * 07 08 2010 cp.wu
 *
 * [WPD00003833] [MT6620 and MT5931] Driver migration - move to new repository.
 *
 * 06 06 2010 kevin.huang
 * [WPD00003832][MT6620 5931] Create driver base
 * [MT6620 5931] Create driver base
 *
 * 05 28 2010 jeffrey.chang
 * [WPD00003826]Initial import for Linux port
 * remove unused macro and debug messages
 *
 * 05 14 2010 jeffrey.chang
 * [WPD00003826]Initial import for Linux port
 * Add dissassoication support for wpa supplicant
 *
 * 04 22 2010 jeffrey.chang
 * [WPD00003826]Initial import for Linux port
 *
 * 1) modify rx path code for supporting Wi-Fi direct
 * 2) modify config.h since Linux dont need to consider retaining packet
 *
 * 04 21 2010 jeffrey.chang
 * [WPD00003826]Initial import for Linux port
 * add for private ioctl support
 *
 * 04 19 2010 jeffrey.chang
 * [WPD00003826]Initial import for Linux port
 * Add ioctl of power management
 *
 * 04 19 2010 jeffrey.chang
 * [WPD00003826]Initial import for Linux port
 * remove debug message
 *
 * 04 14 2010 jeffrey.chang
 * [WPD00003826]Initial import for Linux port
 * 1) prGlueInfo->pvInformationBuffer and prGlueInfo->u4InformationBufferLength are no longer used
 *  * 2) fix ioctl
 *
 * 04 12 2010 jeffrey.chang
 * [WPD00003826]Initial import for Linux port
 * remove debug messages for pre-release
 *
 * 04 07 2010 cp.wu
 * [WPD00001943]Create WiFi test driver framework on WinXP
 * rWlanInfo should be placed at adapter rather than glue due to most operations
 *  *  *  *  *  *  *  * are done in adapter layer.
 *
 * 04 02 2010 jeffrey.chang
 * [WPD00003826]Initial import for Linux port
 * fix ioctl type
 *
 * 04 01 2010 jeffrey.chang
 * [WPD00003826]Initial import for Linux port
 * enable pmksa cache operation
 *
 * 03 31 2010 jeffrey.chang
 * [WPD00003826]Initial import for Linux port
 * fix ioctl which may cause cmdinfo memory leak
 *
 * 03 31 2010 wh.su
 * [WPD00003816][MT6620 Wi-Fi] Adding the security support
 * modify the wapi related code for new driver's design.
 *
 * 03 30 2010 jeffrey.chang
 * [WPD00003826]Initial import for Linux port
 * emulate NDIS Pending OID facility
 *
 * 03 24 2010 jeffrey.chang
 * [WPD00003826]Initial import for Linux port
 * initial import for Linux port
**  \main\maintrunk.MT5921\38 2009-10-08 10:33:22 GMT mtk01090
**  Avoid accessing private data of net_device directly. Replace with netdev_priv(). Add more checking for input parameters and pointers.
**  \main\maintrunk.MT5921\37 2009-09-29 16:49:48 GMT mtk01090
**  Remove unused variables
**  \main\maintrunk.MT5921\36 2009-09-28 20:19:11 GMT mtk01090
**  Add private ioctl to carry OID structures. Restructure public/private ioctl interfaces to Linux kernel.
**  \main\maintrunk.MT5921\35 2009-09-03 11:42:30 GMT mtk01088
**  adding the wapi ioctl support
**  \main\maintrunk.MT5921\34 2009-08-18 22:56:50 GMT mtk01090
**  Add Linux SDIO (with mmc core) support.
**  Add Linux 2.6.21, 2.6.25, 2.6.26.
**  Fix compile warning in Linux.
**  \main\maintrunk.MT5921\33 2009-05-14 22:43:47 GMT mtk01089
**  fix compiling warning
**  \main\maintrunk.MT5921\32 2009-05-07 22:26:18 GMT mtk01089
**  Add mandatory and private IO control for Linux BWCS
**  \main\maintrunk.MT5921\31 2009-02-07 15:11:14 GMT mtk01088
**  fixed the compiling error
**  \main\maintrunk.MT5921\30 2009-02-07 14:46:51 GMT mtk01088
**  add the privacy setting from linux supplicant ap selection
**  \main\maintrunk.MT5921\29 2008-11-19 15:18:50 GMT mtk01088
**  fixed the compling error
**  \main\maintrunk.MT5921\28 2008-11-19 11:56:18 GMT mtk01088
**  rename some variable with pre-fix to avoid the misunderstanding
**  \main\maintrunk.MT5921\27 2008-08-29 16:59:43 GMT mtk01088
**  fixed compiling error
**  \main\maintrunk.MT5921\26 2008-08-29 14:55:53 GMT mtk01088
**  adjust the code for meet the coding style, and add assert check
**  \main\maintrunk.MT5921\25 2008-06-02 11:15:19 GMT mtk01461
**  Update after wlanoidSetPowerMode changed
**  \main\maintrunk.MT5921\24 2008-05-30 15:13:12 GMT mtk01084
**  rename wlanoid
**  \main\maintrunk.MT5921\23 2008-03-28 10:40:28 GMT mtk01461
**  Add set desired rate in Linux STD IOCTL
**  \main\maintrunk.MT5921\22 2008-03-18 10:31:24 GMT mtk01088
**  add pmkid ioctl and indicate
**  \main\maintrunk.MT5921\21 2008-03-11 15:21:24 GMT mtk01461
**  \main\maintrunk.MT5921\20 2008-03-11 14:50:55 GMT mtk01461
**  Refine WPS related priv ioctl for unified interface
**
**  \main\maintrunk.MT5921\19 2008-03-06 16:30:41 GMT mtk01088
**  move the configuration code from set essid function,
**  remove the non-used code
**  \main\maintrunk.MT5921\18 2008-02-21 15:47:09 GMT mtk01461
**  Fix CR[489]
**  \main\maintrunk.MT5921\17 2008-02-12 23:38:31 GMT mtk01461
**  Add Set Frequency & Channel oid support for Linux
**  \main\maintrunk.MT5921\16 2008-01-24 12:07:34 GMT mtk01461
**  \main\maintrunk.MT5921\15 2008-01-24 12:00:10 GMT mtk01461
**  Modify the wext_essid for set up correct information for IBSS, and fix the wrong input ptr for prAdapter
**  \main\maintrunk.MT5921\14 2007-12-06 09:30:12 GMT mtk01425
**  1. Branch Test
**  \main\maintrunk.MT5921\13 2007-12-04 18:07:59 GMT mtk01461
**  fix typo
**  \main\maintrunk.MT5921\12 2007-11-30 17:10:21 GMT mtk01425
**  1. Fix compiling erros
**
**  \main\maintrunk.MT5921\11 2007-11-27 10:43:22 GMT mtk01425
**  1. Add WMM-PS setting
**  \main\maintrunk.MT5921\10 2007-11-06 20:33:32 GMT mtk01088
**  fixed the compiler error
**  \main\maintrunk.MT5921\9 2007-11-06 19:33:15 GMT mtk01088
**  add WPS code
**  \main\maintrunk.MT5921\8 2007-10-30 12:00:44 GMT MTK01425
**  1. Update wlanQueryInformation
*/

/*******************************************************************************
*                         C O M P I L E R   F L A G S
********************************************************************************
*/

/*******************************************************************************
*                    E X T E R N A L   R E F E R E N C E S
********************************************************************************
*/

#include "gl_os.h"

#include "config.h"
#include "wlan_oid.h"

#include "gl_wext.h"
#include "gl_wext_priv.h"

#include "precomp.h"

#if CFG_SUPPORT_WAPI
#include "gl_sec.h"
#endif

/* compatibility to wireless extensions */
#ifdef WIRELESS_EXT

/*******************************************************************************
*                              C O N S T A N T S
********************************************************************************
*/
const long channel_freq[] = {
	2412, 2417, 2422, 2427, 2432, 2437, 2442,
	2447, 2452, 2457, 2462, 2467, 2472, 2484
};

#define     MAP_CHANNEL_ID_TO_KHZ(ch, khz)  {               \
		switch (ch)                                 \
		{                                           \
		    case 1:     khz = 2412000;   break;     \
		    case 2:     khz = 2417000;   break;     \
		    case 3:     khz = 2422000;   break;     \
		    case 4:     khz = 2427000;   break;     \
		    case 5:     khz = 2432000;   break;     \
		    case 6:     khz = 2437000;   break;     \
		    case 7:     khz = 2442000;   break;     \
		    case 8:     khz = 2447000;   break;     \
		    case 9:     khz = 2452000;   break;     \
		    case 10:    khz = 2457000;   break;     \
		    case 11:    khz = 2462000;   break;     \
		    case 12:    khz = 2467000;   break;     \
		    case 13:    khz = 2472000;   break;     \
		    case 14:    khz = 2484000;   break;     \
		    case 36:  /* UNII */  khz = 5180000;   break;     \
		    case 40:  /* UNII */  khz = 5200000;   break;     \
		    case 44:  /* UNII */  khz = 5220000;   break;     \
		    case 48:  /* UNII */  khz = 5240000;   break;     \
		    case 52:  /* UNII */  khz = 5260000;   break;     \
		    case 56:  /* UNII */  khz = 5280000;   break;     \
		    case 60:  /* UNII */  khz = 5300000;   break;     \
		    case 64:  /* UNII */  khz = 5320000;   break;     \
		    case 149: /* UNII */  khz = 5745000;   break;     \
		    case 153: /* UNII */  khz = 5765000;   break;     \
		    case 157: /* UNII */  khz = 5785000;   break;     \
		    case 161: /* UNII */  khz = 5805000;   break;     \
		    case 165: /* UNII */  khz = 5825000;   break;     \
		    case 100: /* HiperLAN2 */  khz = 5500000;   break;     \
		    case 104: /* HiperLAN2 */  khz = 5520000;   break;     \
		    case 108: /* HiperLAN2 */  khz = 5540000;   break;     \
		    case 112: /* HiperLAN2 */  khz = 5560000;   break;     \
		    case 116: /* HiperLAN2 */  khz = 5580000;   break;     \
		    case 120: /* HiperLAN2 */  khz = 5600000;   break;     \
		    case 124: /* HiperLAN2 */  khz = 5620000;   break;     \
		    case 128: /* HiperLAN2 */  khz = 5640000;   break;     \
		    case 132: /* HiperLAN2 */  khz = 5660000;   break;     \
		    case 136: /* HiperLAN2 */  khz = 5680000;   break;     \
		    case 140: /* HiperLAN2 */  khz = 5700000;   break;     \
		    case 34:  /* Japan MMAC */   khz = 5170000;   break;   \
		    case 38:  /* Japan MMAC */   khz = 5190000;   break;   \
		    case 42:  /* Japan MMAC */   khz = 5210000;   break;   \
		    case 46:  /* Japan MMAC */   khz = 5230000;   break;   \
		    case 184: /* Japan */   khz = 4920000;   break;   \
		    case 188: /* Japan */   khz = 4940000;   break;   \
		    case 192: /* Japan */   khz = 4960000;   break;   \
		    case 196: /* Japan */   khz = 4980000;   break;   \
		    case 208: /* Japan, means J08 */   khz = 5040000;   break;   \
		    case 212: /* Japan, means J12 */   khz = 5060000;   break;   \
		    case 216: /* Japan, means J16 */   khz = 5080000;   break;   \
		    default:    khz = 2412000;   break;     \
		}                                           \
	    }

#define NUM_CHANNELS (sizeof(channel_freq) / sizeof(channel_freq[0]))

#define MAX_SSID_LEN    32

/*******************************************************************************
*                             D A T A   T Y P E S
********************************************************************************
*/

/*******************************************************************************
*                            P U B L I C   D A T A
********************************************************************************
*/
/* NOTE: name in iwpriv_args only have 16 bytes */
static const struct iw_priv_args rIwPrivTable[] = {
	{IOCTL_SET_INT, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, ""},
	{IOCTL_GET_INT, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, ""},
	{IOCTL_SET_INT, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, 0, ""},
	{IOCTL_GET_INT, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, ""},
	{IOCTL_SET_INT, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, ""},

	{IOCTL_GET_INT, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2,
	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, ""},
	{IOCTL_GET_INT, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, ""},

	{IOCTL_SET_INTS, IW_PRIV_TYPE_INT | 4, 0, ""},
	{IOCTL_GET_INT, 0, IW_PRIV_TYPE_INT | 50, ""},

	/* added for set_oid and get_oid */
	{IOCTL_SET_STRUCT, 256, 0, ""},
	{IOCTL_GET_STRUCT, 0, 256, ""},

	/* sub-ioctl definitions */
#if 0
	{PRIV_CMD_REG_DOMAIN, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_reg_domain"},
	{PRIV_CMD_REG_DOMAIN, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_reg_domain"},
#endif

#if CFG_TCP_IP_CHKSUM_OFFLOAD
	{PRIV_CMD_CSUM_OFFLOAD, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_tcp_csum"},
#endif				/* CFG_TCP_IP_CHKSUM_OFFLOAD */

	{PRIV_CMD_POWER_MODE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_power_mode"},
	{PRIV_CMD_POWER_MODE, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_power_mode"},

	{PRIV_CMD_WMM_PS, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, 0, "set_wmm_ps"},

	{PRIV_CMD_TEST_MODE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_test_mode"},
	{PRIV_CMD_TEST_CMD, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "set_test_cmd"},
	{PRIV_CMD_TEST_CMD, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2,
	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_test_result"},
#if CFG_SUPPORT_PRIV_MCR_RW
	{PRIV_CMD_ACCESS_MCR, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "set_mcr"},
	{PRIV_CMD_ACCESS_MCR, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_mcr"},
#endif
	{PRIV_CMD_SW_CTRL, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "set_sw_ctrl"},
	{PRIV_CMD_SW_CTRL, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_sw_ctrl"},

#if CFG_SUPPORT_BCM && CFG_SUPPORT_BCM_BWCS
	{PRIV_CUSTOM_BWCS_CMD, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_bwcs"},
	/* GET STRUCT sub-ioctls commands */
	{PRIV_CUSTOM_BWCS_CMD, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bwcs"},
#endif

	/* SET STRUCT sub-ioctls commands */
	{PRIV_CMD_OID, 256, 0, "set_oid"},
	/* GET STRUCT sub-ioctls commands */
	{PRIV_CMD_OID, 0, 256, "get_oid"},

	{PRIV_CMD_BAND_CONFIG, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_band"},
	{PRIV_CMD_BAND_CONFIG, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_band"},

	{PRIV_CMD_SET_TX_POWER, IW_PRIV_TYPE_INT | 4, 0, "set_txpower"},
	{PRIV_CMD_GET_CH_LIST, 0, IW_PRIV_TYPE_INT | 50, "get_ch_list"},
	{PRIV_CMD_DUMP_MEM, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2,
	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_mem"},

#if CFG_ENABLE_WIFI_DIRECT
	{PRIV_CMD_P2P_MODE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "set_p2p_mode"},
#endif
};

static const iw_handler rIwPrivHandler[] = {
	[IOCTL_SET_INT - SIOCIWFIRSTPRIV] = priv_set_int,
	[IOCTL_GET_INT - SIOCIWFIRSTPRIV] = priv_get_int,
	[IOCTL_SET_ADDRESS - SIOCIWFIRSTPRIV] = NULL,
	[IOCTL_GET_ADDRESS - SIOCIWFIRSTPRIV] = NULL,
	[IOCTL_SET_STR - SIOCIWFIRSTPRIV] = NULL,
	[IOCTL_GET_STR - SIOCIWFIRSTPRIV] = NULL,
	[IOCTL_SET_KEY - SIOCIWFIRSTPRIV] = NULL,
	[IOCTL_GET_KEY - SIOCIWFIRSTPRIV] = NULL,
	[IOCTL_SET_STRUCT - SIOCIWFIRSTPRIV] = priv_set_struct,
	[IOCTL_GET_STRUCT - SIOCIWFIRSTPRIV] = priv_get_struct,
	[IOCTL_SET_STRUCT_FOR_EM - SIOCIWFIRSTPRIV] = priv_set_struct,
	[IOCTL_SET_INTS - SIOCIWFIRSTPRIV] = priv_set_ints,
	[IOCTL_GET_INTS - SIOCIWFIRSTPRIV] = priv_get_ints,
};

const struct iw_handler_def wext_handler_def = {
	.num_standard = 0,
	.num_private = (__u16) sizeof(rIwPrivHandler) / sizeof(iw_handler),
	.num_private_args = (__u16) sizeof(rIwPrivTable) / sizeof(struct iw_priv_args),
	.standard = (iw_handler *) NULL,
	.private = rIwPrivHandler,
	.private_args = rIwPrivTable,
	.get_wireless_stats = wext_get_wireless_stats,
};

/*******************************************************************************
*                           P R I V A T E   D A T A
********************************************************************************
*/

/*******************************************************************************
*                                 M A C R O S
********************************************************************************
*/

/*******************************************************************************
*                  F U N C T I O N   D E C L A R A T I O N S
********************************************************************************
*/

/*******************************************************************************
*                              F U N C T I O N S
********************************************************************************
*/

/*----------------------------------------------------------------------------*/
/*!
* \brief Find the desired WPA/RSN Information Element according to desiredElemID.
*
* \param[in] pucIEStart IE starting address.
* \param[in] i4TotalIeLen Total length of all the IE.
* \param[in] ucDesiredElemId Desired element ID.
* \param[out] ppucDesiredIE Pointer to the desired IE.
*
* \retval TRUE Find the desired IE.
* \retval FALSE Desired IE not found.
*
* \note
*/
/*----------------------------------------------------------------------------*/
BOOLEAN
wextSrchDesiredWPAIE(IN PUINT_8 pucIEStart,
		     IN INT_32 i4TotalIeLen, IN UINT_8 ucDesiredElemId, OUT PUINT_8 * ppucDesiredIE)
{
	INT_32 i4InfoElemLen;

	ASSERT(pucIEStart);
	ASSERT(ppucDesiredIE);

	while (i4TotalIeLen >= 2) {
		i4InfoElemLen = (INT_32) pucIEStart[1] + 2;

		if (pucIEStart[0] == ucDesiredElemId && i4InfoElemLen <= i4TotalIeLen) {
			if (ucDesiredElemId != 0xDD) {
				/* Non 0xDD, OK! */
				*ppucDesiredIE = &pucIEStart[0];
				return TRUE;
			} else {
				/* EID == 0xDD, check WPA IE */
				if (pucIEStart[1] >= 4) {
					if (memcmp(&pucIEStart[2], "\x00\x50\xf2\x01", 4) == 0) {
						*ppucDesiredIE = &pucIEStart[0];
						return TRUE;
					}
				}	/* check WPA IE length */
			}	/* check EID == 0xDD */
		}

		/* check desired EID */
		/* Select next information element. */
		i4TotalIeLen -= i4InfoElemLen;
		pucIEStart += i4InfoElemLen;
	}

	return FALSE;
}				/* parseSearchDesiredWPAIE */

#if CFG_SUPPORT_WAPI
/*----------------------------------------------------------------------------*/
/*!
* \brief Find the desired WAPI Information Element .
*
* \param[in] pucIEStart IE starting address.
* \param[in] i4TotalIeLen Total length of all the IE.
* \param[out] ppucDesiredIE Pointer to the desired IE.
*
* \retval TRUE Find the desired IE.
* \retval FALSE Desired IE not found.
*
* \note
*/
/*----------------------------------------------------------------------------*/
BOOLEAN
wextSrchDesiredWAPIIE(IN PUINT_8 pucIEStart, IN INT_32 i4TotalIeLen, OUT PUINT_8 * ppucDesiredIE)
{
	INT_32 i4InfoElemLen;

	ASSERT(pucIEStart);
	ASSERT(ppucDesiredIE);

	while (i4TotalIeLen >= 2) {
		i4InfoElemLen = (INT_32) pucIEStart[1] + 2;

		if (pucIEStart[0] == ELEM_ID_WAPI && i4InfoElemLen <= i4TotalIeLen) {
			*ppucDesiredIE = &pucIEStart[0];
			return TRUE;
		}

		/* check desired EID */
		/* Select next information element. */
		i4TotalIeLen -= i4InfoElemLen;
		pucIEStart += i4InfoElemLen;
	}

	return FALSE;
}				/* wextSrchDesiredWAPIIE */
#endif

#if CFG_SUPPORT_WPS
/*----------------------------------------------------------------------------*/
/*!
* \brief Find the desired WPS Information Element according to desiredElemID.
*
* \param[in] pucIEStart IE starting address.
* \param[in] i4TotalIeLen Total length of all the IE.
* \param[in] ucDesiredElemId Desired element ID.
* \param[out] ppucDesiredIE Pointer to the desired IE.
*
* \retval TRUE Find the desired IE.
* \retval FALSE Desired IE not found.
*
* \note
*/
/*----------------------------------------------------------------------------*/
BOOLEAN
wextSrchDesiredWPSIE(IN PUINT_8 pucIEStart,
		     IN INT_32 i4TotalIeLen, IN UINT_8 ucDesiredElemId, OUT PUINT_8 * ppucDesiredIE)
{
	INT_32 i4InfoElemLen;

	ASSERT(pucIEStart);
	ASSERT(ppucDesiredIE);

	while (i4TotalIeLen >= 2) {
		i4InfoElemLen = (INT_32) pucIEStart[1] + 2;

		if (pucIEStart[0] == ucDesiredElemId && i4InfoElemLen <= i4TotalIeLen) {
			if (ucDesiredElemId != 0xDD) {
				/* Non 0xDD, OK! */
				*ppucDesiredIE = &pucIEStart[0];
				return TRUE;
			} else {
				/* EID == 0xDD, check WPS IE */
				if (pucIEStart[1] >= 4) {
					if (memcmp(&pucIEStart[2], "\x00\x50\xf2\x04", 4) == 0) {
						*ppucDesiredIE = &pucIEStart[0];
						return TRUE;
					}
				}	/* check WPS IE length */
			}	/* check EID == 0xDD */
		}

		/* check desired EID */
		/* Select next information element. */
		i4TotalIeLen -= i4InfoElemLen;
		pucIEStart += i4InfoElemLen;
	}

	return FALSE;
}				/* parseSearchDesiredWPSIE */
#endif

/*----------------------------------------------------------------------------*/
/*!
* \brief Get the name of the protocol used on the air.
*
* \param[in]  prDev Net device requested.
* \param[in]  prIwrInfo NULL.
* \param[out] pcName Buffer to store protocol name string
* \param[in]  pcExtra NULL.
*
* \retval 0 For success.
*
* \note If netif_carrier_ok, protocol name is returned;
*       otherwise, "disconnected" is returned.
*/
/*----------------------------------------------------------------------------*/
static int
wext_get_name(IN struct net_device *prNetDev,
	      IN struct iw_request_info *prIwrInfo, OUT char *pcName, IN char *pcExtra)
{
	ENUM_PARAM_NETWORK_TYPE_T eNetWorkType;

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(pcName);
	if (FALSE == GLUE_CHK_PR2(prNetDev, pcName)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	if (netif_carrier_ok(prNetDev)) {

		rStatus = kalIoctl(prGlueInfo,
				   wlanoidQueryNetworkTypeInUse,
				   &eNetWorkType,
				   sizeof(eNetWorkType), TRUE, FALSE, FALSE, FALSE, &u4BufLen);

		switch (eNetWorkType) {
		case PARAM_NETWORK_TYPE_DS:
			strcpy(pcName, "IEEE 802.11b");
			break;
		case PARAM_NETWORK_TYPE_OFDM24:
			strcpy(pcName, "IEEE 802.11bgn");
			break;
		case PARAM_NETWORK_TYPE_AUTOMODE:
		case PARAM_NETWORK_TYPE_OFDM5:
			strcpy(pcName, "IEEE 802.11abgn");
			break;
		case PARAM_NETWORK_TYPE_FH:
		default:
			strcpy(pcName, "IEEE 802.11");
			break;
		}
	} else {
		strcpy(pcName, "Disconnected");
	}

	return 0;
}				/* wext_get_name */

/*----------------------------------------------------------------------------*/
/*!
* \brief To set the operating channel in the wireless device.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL
* \param[in] prFreq Buffer to store frequency information
* \param[in] pcExtra NULL
*
* \retval 0 For success.
* \retval -EOPNOTSUPP If infrastructure mode is not NET NET_TYPE_IBSS.
* \retval -EINVAL Invalid channel frequency.
*
* \note If infrastructure mode is IBSS, new channel frequency is set to device.
*      The range of channel number depends on different regulatory domain.
*/
/*----------------------------------------------------------------------------*/
static int
wext_set_freq(IN struct net_device *prNetDev,
	      IN struct iw_request_info *prIwReqInfo, IN struct iw_freq *prIwFreq, IN char *pcExtra)
{

#if 0
	UINT_32 u4ChnlFreq;	/* Store channel or frequency information */

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(prIwFreq);
	if (FALSE == GLUE_CHK_PR2(prNetDev, prIwFreq)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	/*
	   printk("set m:%d, e:%d, i:%d, flags:%d\n",
	   prIwFreq->m, prIwFreq->e, prIwFreq->i, prIwFreq->flags);
	 */

	/* If setting by frequency, convert to a channel */
	if ((prIwFreq->e == 1) && (prIwFreq->m >= (int)2.412e8) && (prIwFreq->m <= (int)2.484e8)) {

		/* Change to KHz format */
		u4ChnlFreq = (UINT_32) (prIwFreq->m / (KILO / 10));

		rStatus = kalIoctl(prGlueInfo,
				   wlanoidSetFrequency,
				   &u4ChnlFreq, sizeof(u4ChnlFreq), FALSE, FALSE, FALSE, &u4BufLen);

		if (WLAN_STATUS_SUCCESS != rStatus) {
			return -EINVAL;
		}
	}
	/* Setting by channel number */
	else if ((prIwFreq->m > KILO) || (prIwFreq->e > 0)) {
		return -EOPNOTSUPP;
	} else {
		/* Change to channel number format */
		u4ChnlFreq = (UINT_32) prIwFreq->m;

		rStatus = kalIoctl(prGlueInfo,
				   wlanoidSetChannel,
				   &u4ChnlFreq, sizeof(u4ChnlFreq), FALSE, FALSE, FALSE, &u4BufLen);

		if (WLAN_STATUS_SUCCESS != rStatus) {
			return -EINVAL;
		}
	}

#endif

	return 0;

}				/* wext_set_freq */

/*----------------------------------------------------------------------------*/
/*!
* \brief To get the operating channel in the wireless device.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[out] prFreq Buffer to store frequency information.
* \param[in] pcExtra NULL.
*
* \retval 0 If netif_carrier_ok.
* \retval -ENOTCONN Otherwise
*
* \note If netif_carrier_ok, channel frequency information is stored in pFreq.
*/
/*----------------------------------------------------------------------------*/
static int
wext_get_freq(IN struct net_device *prNetDev,
	      IN struct iw_request_info *prIwrInfo, OUT struct iw_freq *prIwFreq, IN char *pcExtra)
{
	UINT_32 u4Channel = 0;

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(prIwFreq);
	if (FALSE == GLUE_CHK_PR2(prNetDev, prIwFreq)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	/* GeorgeKuo: TODO skip checking in IBSS mode */
	if (!netif_carrier_ok(prNetDev)) {
		return -ENOTCONN;
	}

	rStatus = kalIoctl(prGlueInfo,
			   wlanoidQueryFrequency,
			   &u4Channel, sizeof(u4Channel), TRUE, FALSE, FALSE, FALSE, &u4BufLen);

	prIwFreq->m = (int)u4Channel;	/* freq in KHz */
	prIwFreq->e = 3;

	return 0;

}				/* wext_get_freq */

/*----------------------------------------------------------------------------*/
/*!
* \brief To set operating mode.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[in] pu4Mode Pointer to new operation mode.
* \param[in] pcExtra NULL.
*
* \retval 0 For success.
* \retval -EOPNOTSUPP If new mode is not supported.
*
* \note Device will run in new operation mode if it is valid.
*/
/*----------------------------------------------------------------------------*/
static int
wext_set_mode(IN struct net_device *prNetDev,
	      IN struct iw_request_info *prIwReqInfo, IN unsigned int *pu4Mode, IN char *pcExtra)
{
	ENUM_PARAM_OP_MODE_T eOpMode;

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(pu4Mode);
	if (FALSE == GLUE_CHK_PR2(prNetDev, pu4Mode)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	switch (*pu4Mode) {
	case IW_MODE_AUTO:
		eOpMode = NET_TYPE_AUTO_SWITCH;
		break;

	case IW_MODE_ADHOC:
		eOpMode = NET_TYPE_IBSS;
		break;

	case IW_MODE_INFRA:
		eOpMode = NET_TYPE_INFRA;
		break;

	default:
		DBGLOG(INIT, INFO, ("%s(): Set UNSUPPORTED Mode = %d.\n", __func__, *pu4Mode));
		return -EOPNOTSUPP;
	}

	/* printk("%s(): Set Mode = %d\n", __FUNCTION__, *pu4Mode); */

	rStatus = kalIoctl(prGlueInfo,
			   wlanoidSetInfrastructureMode,
			   &eOpMode, sizeof(eOpMode), FALSE, FALSE, TRUE, FALSE, &u4BufLen);

	/* after set operation mode, key table are cleared */

	/* reset wpa info */
	prGlueInfo->rWpaInfo.u4WpaVersion = IW_AUTH_WPA_VERSION_DISABLED;
	prGlueInfo->rWpaInfo.u4KeyMgmt = 0;
	prGlueInfo->rWpaInfo.u4CipherGroup = IW_AUTH_CIPHER_NONE;
	prGlueInfo->rWpaInfo.u4CipherPairwise = IW_AUTH_CIPHER_NONE;
	prGlueInfo->rWpaInfo.u4AuthAlg = IW_AUTH_ALG_OPEN_SYSTEM;
#if CFG_SUPPORT_802_11W
	prGlueInfo->rWpaInfo.u4Mfp = IW_AUTH_MFP_DISABLED;
#endif

	return 0;
}				/* wext_set_mode */

/*----------------------------------------------------------------------------*/
/*!
* \brief To get operating mode.
*
* \param[in] prNetDev Net device requested.
* \param[in] prIwReqInfo NULL.
* \param[out] pu4Mode Buffer to store operating mode information.
* \param[in] pcExtra NULL.
*
* \retval 0 If data is valid.
* \retval -EINVAL Otherwise.
*
* \note If netif_carrier_ok, operating mode information is stored in pu4Mode.
*/
/*----------------------------------------------------------------------------*/
static int
wext_get_mode(IN struct net_device *prNetDev,
	      IN struct iw_request_info *prIwReqInfo, OUT unsigned int *pu4Mode, IN char *pcExtra)
{
	ENUM_PARAM_OP_MODE_T eOpMode;

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(pu4Mode);
	if (FALSE == GLUE_CHK_PR2(prNetDev, pu4Mode)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	rStatus = kalIoctl(prGlueInfo,
			   wlanoidQueryInfrastructureMode,
			   &eOpMode, sizeof(eOpMode), TRUE, FALSE, FALSE, FALSE, &u4BufLen);

	switch (eOpMode) {
	case NET_TYPE_IBSS:
		*pu4Mode = IW_MODE_ADHOC;
		break;

	case NET_TYPE_INFRA:
		*pu4Mode = IW_MODE_INFRA;
		break;

	case NET_TYPE_AUTO_SWITCH:
		*pu4Mode = IW_MODE_AUTO;
		break;

	default:
		DBGLOG(INIT, INFO, ("%s(): Get UNKNOWN Mode.\n", __func__));
		return -EINVAL;
	}

	return 0;
}				/* wext_get_mode */

/*----------------------------------------------------------------------------*/
/*!
* \brief To get the valid range for each configurable STA setting value.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[in] prData Pointer to iw_point structure, not used.
* \param[out] pcExtra Pointer to buffer which is allocated by caller of this
*                     function, wext_support_ioctl() or ioctl_standard_call() in
*                     wireless.c.
*
* \retval 0 If data is valid.
*
* \note The extra buffer (pcExtra) is filled with information from driver.
*/
/*----------------------------------------------------------------------------*/
static int
wext_get_range(IN struct net_device *prNetDev,
	       IN struct iw_request_info *prIwrInfo, IN struct iw_point *prData, OUT char *pcExtra)
{
	struct iw_range *prRange = NULL;
	PARAM_RATES_EX aucSuppRate = { 0 };	/* data buffers */
	int i = 0;

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(pcExtra);
	if (FALSE == GLUE_CHK_PR2(prNetDev, pcExtra)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	prRange = (struct iw_range *)pcExtra;

	memset(prRange, 0, sizeof(*prRange));
	prRange->throughput = 20000000;	/* 20Mbps */
	prRange->min_nwid = 0;	/* not used */
	prRange->max_nwid = 0;	/* not used */

	/* scan_capa not implemented */

	/* event_capa[6]: kernel + driver capabilities */
	prRange->event_capa[0] = (IW_EVENT_CAPA_K_0 | IW_EVENT_CAPA_MASK(SIOCGIWAP)
				  | IW_EVENT_CAPA_MASK(SIOCGIWSCAN)
				  /* can't display meaningful string in iwlist
				     | IW_EVENT_CAPA_MASK(SIOCGIWTXPOW)
				     | IW_EVENT_CAPA_MASK(IWEVMICHAELMICFAILURE)
				     | IW_EVENT_CAPA_MASK(IWEVASSOCREQIE)
				     | IW_EVENT_CAPA_MASK(IWEVPMKIDCAND)
				   */
	    );
	prRange->event_capa[1] = IW_EVENT_CAPA_K_1;

	/* report 2.4G channel and frequency only */
	prRange->num_channels = (__u16) NUM_CHANNELS;
	prRange->num_frequency = (__u8) NUM_CHANNELS;
	for (i = 0; i < NUM_CHANNELS; i++) {
		/* iwlib takes this number as channel number */
		prRange->freq[i].i = i + 1;
		prRange->freq[i].m = channel_freq[i];
		prRange->freq[i].e = 6;	/* Values in table in MHz */
	}

	rStatus = kalIoctl(prGlueInfo,
			   wlanoidQuerySupportedRates,
			   &aucSuppRate, sizeof(aucSuppRate), TRUE, FALSE, FALSE, FALSE, &u4BufLen);

	for (i = 0; i < IW_MAX_BITRATES && i < PARAM_MAX_LEN_RATES_EX; i++) {
		if (aucSuppRate[i] == 0) {
			break;
		}
		prRange->bitrate[i] = (aucSuppRate[i] & 0x7F) * 500000;	/* 0.5Mbps */
	}
	prRange->num_bitrates = i;

	prRange->min_rts = 0;
	prRange->max_rts = 2347;
	prRange->min_frag = 256;
	prRange->max_frag = 2346;

	prRange->min_pmp = 0;	/* power management by driver */
	prRange->max_pmp = 0;	/* power management by driver */
	prRange->min_pmt = 0;	/* power management by driver */
	prRange->max_pmt = 0;	/* power management by driver */
	prRange->pmp_flags = IW_POWER_RELATIVE;	/* pm default flag */
	prRange->pmt_flags = IW_POWER_ON;	/* pm timeout flag */
	prRange->pm_capa = IW_POWER_ON;	/* power management by driver */

	prRange->encoding_size[0] = 5;	/* wep40 */
	prRange->encoding_size[1] = 16;	/* tkip */
	prRange->encoding_size[2] = 16;	/* ckip */
	prRange->encoding_size[3] = 16;	/* ccmp */
	prRange->encoding_size[4] = 13;	/* wep104 */
	prRange->encoding_size[5] = 16;	/* wep128 */
	prRange->num_encoding_sizes = 6;
	prRange->max_encoding_tokens = 6;	/* token? */

#if WIRELESS_EXT < 17
	prRange->txpower_capa = 0x0002;	/* IW_TXPOW_RELATIVE */
#else
	prRange->txpower_capa = IW_TXPOW_RELATIVE;
#endif
	prRange->num_txpower = 5;
	prRange->txpower[0] = 0;	/* minimum */
	prRange->txpower[1] = 25;	/* 25% */
	prRange->txpower[2] = 50;	/* 50% */
	prRange->txpower[3] = 100;	/* 100% */

	prRange->we_version_compiled = WIRELESS_EXT;
	prRange->we_version_source = WIRELESS_EXT;

	prRange->retry_capa = IW_RETRY_LIMIT;
	prRange->retry_flags = IW_RETRY_LIMIT;
	prRange->min_retry = 7;
	prRange->max_retry = 7;
	prRange->r_time_flags = IW_RETRY_ON;
	prRange->min_r_time = 0;
	prRange->max_r_time = 0;

	/* signal strength and link quality */
	/* Just define range here, reporting value moved to wext_get_stats() */
	prRange->sensitivity = -83;	/* fixed value */
	prRange->max_qual.qual = 100;	/* max 100% */
	prRange->max_qual.level = (__u8) (0x100 - 0);	/* max 0 dbm */
	prRange->max_qual.noise = (__u8) (0x100 - 0);	/* max 0 dbm */

	/* enc_capa */
#if WIRELESS_EXT > 17
	prRange->enc_capa = IW_ENC_CAPA_WPA |
	    IW_ENC_CAPA_WPA2 | IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
#endif

	/* min_pms; Minimal PM saving */
	/* max_pms; Maximal PM saving */
	/* pms_flags; How to decode max/min PM saving */

	/* modul_capa; IW_MODUL_* bit field */
	/* bitrate_capa; Types of bitrates supported */

	return 0;
}				/* wext_get_range */

/*----------------------------------------------------------------------------*/
/*!
* \brief To set BSSID of AP to connect.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[in] prAddr Pointer to struct sockaddr structure containing AP's BSSID.
* \param[in] pcExtra NULL.
*
* \retval 0 For success.
*
* \note Desired AP's BSSID is set to driver.
*/
/*----------------------------------------------------------------------------*/
static int
wext_set_ap(IN struct net_device *prDev,
	    IN struct iw_request_info *prIwrInfo, IN struct sockaddr *prAddr, IN char *pcExtra)
{
	return 0;
}				/* wext_set_ap */

/*----------------------------------------------------------------------------*/
/*!
* \brief To get AP MAC address.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[out] prAddr Pointer to struct sockaddr structure storing AP's BSSID.
* \param[in] pcExtra NULL.
*
* \retval 0 If netif_carrier_ok.
* \retval -ENOTCONN Otherwise.
*
* \note If netif_carrier_ok, AP's mac address is stored in pAddr->sa_data.
*/
/*----------------------------------------------------------------------------*/
static int
wext_get_ap(IN struct net_device *prNetDev,
	    IN struct iw_request_info *prIwrInfo, OUT struct sockaddr *prAddr, IN char *pcExtra)
{
	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(prAddr);
	if (FALSE == GLUE_CHK_PR2(prNetDev, prAddr)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	/* if (!netif_carrier_ok(prNetDev)) { */
	/* return -ENOTCONN; */
	/* } */

	if (prGlueInfo->eParamMediaStateIndicated == PARAM_MEDIA_STATE_DISCONNECTED) {
		memset(prAddr, 0, 6);
		return 0;
	}

	rStatus = kalIoctl(prGlueInfo,
			   wlanoidQueryBssid,
			   prAddr->sa_data, ETH_ALEN, TRUE, FALSE, FALSE, FALSE, &u4BufLen);

	return 0;
}				/* wext_get_ap */

/*----------------------------------------------------------------------------*/
/*!
* \brief To set mlme operation request.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[in] prData Pointer of iw_point header.
* \param[in] pcExtra Pointer to iw_mlme structure mlme request information.
*
* \retval 0 For success.
* \retval -EOPNOTSUPP unsupported IW_MLME_ command.
* \retval -EINVAL Set MLME Fail, different bssid.
*
* \note Driver will start mlme operation if valid.
*/
/*----------------------------------------------------------------------------*/
static int
wext_set_mlme(IN struct net_device *prNetDev,
	      IN struct iw_request_info *prIwrInfo, IN struct iw_point *prData, IN char *pcExtra)
{
	struct iw_mlme *prMlme = NULL;

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(pcExtra);
	if (FALSE == GLUE_CHK_PR2(prNetDev, pcExtra)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	prMlme = (struct iw_mlme *)pcExtra;
	if (prMlme->cmd == IW_MLME_DEAUTH || prMlme->cmd == IW_MLME_DISASSOC) {
		if (!netif_carrier_ok(prNetDev)) {
			DBGLOG(INIT, INFO,
			       ("[wifi] Set MLME Deauth/Disassoc, but netif_carrier_off\n"));
			return 0;
		}

		rStatus = kalIoctl(prGlueInfo,
				   wlanoidSetDisassociate,
				   NULL, 0, FALSE, FALSE, TRUE, FALSE, &u4BufLen);
		return 0;
	} else {
		DBGLOG(INIT, INFO, ("[wifi] unsupported IW_MLME_ command :%d\n", prMlme->cmd));
		return -EOPNOTSUPP;
	}
}				/* wext_set_mlme */

/*----------------------------------------------------------------------------*/
/*!
* \brief To issue scan request.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[in] prData NULL.
* \param[in] pcExtra NULL.
*
* \retval 0 For success.
* \retval -EFAULT Tx power is off.
*
* \note Device will start scanning.
*/
/*----------------------------------------------------------------------------*/
static int
wext_set_scan(IN struct net_device *prNetDev,
	      IN struct iw_request_info *prIwrInfo, IN union iwreq_data *prData, IN char *pcExtra)
{
	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;
	int essid_len = 0;

	ASSERT(prNetDev);
	if (FALSE == GLUE_CHK_DEV(prNetDev)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

#if WIRELESS_EXT > 17
	/* retrieve SSID */
	if (prData) {
		essid_len =
		    ((struct iw_scan_req *)(((struct iw_point *)prData)->pointer))->essid_len;
	}
#endif

	init_completion(&prGlueInfo->rScanComp);

	/* TODO:  parse flags and issue different scan requests? */

	rStatus = kalIoctl(prGlueInfo,
			   wlanoidSetBssidListScan,
			   pcExtra, essid_len, FALSE, FALSE, FALSE, FALSE, &u4BufLen);

	/* wait_for_completion_interruptible_timeout(&prGlueInfo->rScanComp, 2 * KAL_HZ); */
	/* kalIndicateStatusAndComplete(prGlueInfo, WLAN_STATUS_SCAN_COMPLETE, NULL, 0); */

	return 0;
}				/* wext_set_scan */

/*----------------------------------------------------------------------------*/
/*!
* \brief To write the ie to buffer
*
*/
/*----------------------------------------------------------------------------*/
static inline int snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len)
{
	size_t i;
	char *pos = buf, *end = buf + buf_size;
	int ret;

	if (buf_size == 0)
		return 0;

	for (i = 0; i < len; i++) {
		ret = snprintf(pos, end - pos, "%02x", data[i]);
		if (ret < 0 || ret >= end - pos) {
			end[-1] = '\0';
			return pos - buf;
		}
		pos += ret;
	}
	end[-1] = '\0';
	return pos - buf;
}

/*----------------------------------------------------------------------------*/
/*!
* \brief To get scan results, transform results from driver's format to WE's.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[out] prData Pointer to iw_point structure, pData->length is the size of
*               pcExtra buffer before used, and is updated after filling scan
*               results.
* \param[out] pcExtra Pointer to buffer which is allocated by caller of this
*                     function, wext_support_ioctl() or ioctl_standard_call() in
*                     wireless.c.
*
* \retval 0 For success.
* \retval -ENOMEM If dynamic memory allocation fail.
* \retval -E2BIG Invalid length.
*
* \note Scan results is filled into pcExtra buffer, data size is updated in
*       pData->length.
*/
/*----------------------------------------------------------------------------*/
static int
wext_get_scan(IN struct net_device *prNetDev,
	      IN struct iw_request_info *prIwrInfo,
	      IN OUT struct iw_point *prData, IN char *pcExtra)
{
	UINT_32 i = 0;
	UINT_32 j = 0;
	P_PARAM_BSSID_LIST_EX_T prList = NULL;
	P_PARAM_BSSID_EX_T prBss = NULL;
	P_PARAM_VARIABLE_IE_T prDesiredIE = NULL;
	struct iw_event iwEvent;	/* local iw_event buffer */

	/* write pointer of extra buffer */
	char *pcCur = NULL;
	/* pointer to the end of  last full entry in extra buffer */
	char *pcValidEntryEnd = NULL;
	char *pcEnd = NULL;	/* end of extra buffer */

	UINT_32 u4AllocBufLen = 0;

	/* arrange rate information */
	UINT_32 u4HighestRate = 0;
	char aucRatesBuf[64];
	UINT_32 u4BufIndex;

	/* return value */
	int ret = 0;

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(prData);
	ASSERT(pcExtra);
	if (FALSE == GLUE_CHK_PR3(prNetDev, prData, pcExtra)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	/* Initialize local variables */
	pcCur = pcExtra;
	pcValidEntryEnd = pcExtra;
	pcEnd = pcExtra + prData->length;	/* end of extra buffer */

	/* Allocate another query buffer with the same size of extra buffer */
	u4AllocBufLen = prData->length;
	prList = kalMemAlloc(u4AllocBufLen, VIR_MEM_TYPE);
	if (prList == NULL) {
		DBGLOG(INIT, INFO, ("[wifi] no memory for scan list:%d\n", prData->length));
		ret = -ENOMEM;
		goto error;
	}
	prList->u4NumberOfItems = 0;

	/* wait scan done */
	/* printk ("wait for scan results\n"); */
	/* wait_for_completion_interruptible_timeout(&prGlueInfo->rScanComp, 4 * KAL_HZ); */

	rStatus = kalIoctl(prGlueInfo,
			   wlanoidQueryBssidList,
			   prList, u4AllocBufLen, TRUE, FALSE, FALSE, FALSE, &u4BufLen);

	if (rStatus == WLAN_STATUS_INVALID_LENGTH) {
		/* Buffer length is not large enough. */
		/* printk(KERN_INFO "[wifi] buf:%d result:%ld\n", pData->length, u4BufLen); */

#if WIRELESS_EXT >= 17
		/* This feature is supported in WE-17 or above, limited by iwlist.
		 ** Return -E2BIG and iwlist will request again with a larger buffer.
		 */
		ret = -E2BIG;
		/* Update length to give application a hint on result length */
		prData->length = (__u16) u4BufLen;
		goto error;
#else
		/* Realloc a larger query buffer here, but don't write too much to extra
		 ** buffer when filling it later.
		 */
		kalMemFree(prList, VIR_MEM_TYPE, u4AllocBufLen);

		u4AllocBufLen = u4BufLen;
		prList = kalMemAlloc(u4AllocBufLen, VIR_MEM_TYPE);
		if (prList == NULL) {
			DBGLOG(INIT, INFO,
			       ("[wifi] no memory for larger scan list :%ld\n", u4BufLen));
			ret = -ENOMEM;
			goto error;
		}
		prList->NumberOfItems = 0;

		rStatus = kalIoctl(prGlueInfo,
				   wlanoidQueryBssidList,
				   prList, u4AllocBufLen, TRUE, FALSE, FALSE, FALSE, &u4BufLen);

		if (rStatus == WLAN_STATUS_INVALID_LENGTH) {
			DBGLOG(INIT, INFO,
			       ("[wifi] larger buf:%d result:%ld\n", u4AllocBufLen, u4BufLen));
			ret = -E2BIG;
			prData->length = (__u16) u4BufLen;
			goto error;
		}
#endif				/* WIRELESS_EXT >= 17 */

	}

	if (prList->u4NumberOfItems > CFG_MAX_NUM_BSS_LIST) {
		DBGLOG(INIT, INFO, ("[wifi] strange scan result count:%ld\n",
				    prList->u4NumberOfItems));
		goto error;
	}

	/* Copy required data from pList to pcExtra */
	prBss = &prList->arBssid[0];	/* set to the first entry */
	for (i = 0; i < prList->u4NumberOfItems; ++i) {
		/* BSSID */
		iwEvent.cmd = SIOCGIWAP;
		iwEvent.len = IW_EV_ADDR_LEN;
		if ((pcCur + iwEvent.len) > pcEnd)
			break;
		iwEvent.u.ap_addr.sa_family = ARPHRD_ETHER;
		memcpy(iwEvent.u.ap_addr.sa_data, prBss->arMacAddress, ETH_ALEN);
		memcpy(pcCur, &iwEvent, IW_EV_ADDR_LEN);
		pcCur += IW_EV_ADDR_LEN;

		/* SSID */
		iwEvent.cmd = SIOCGIWESSID;
		/* Modification to user space pointer(essid.pointer) is not needed. */
		iwEvent.u.essid.length = (__u16) prBss->rSsid.u4SsidLen;
		iwEvent.len = IW_EV_POINT_LEN + iwEvent.u.essid.length;

		if ((pcCur + iwEvent.len) > pcEnd)
			break;
		iwEvent.u.essid.flags = 1;
		iwEvent.u.essid.pointer = NULL;

#if WIRELESS_EXT <= 18
		memcpy(pcCur, &iwEvent, iwEvent.len);
#else
		memcpy(pcCur, &iwEvent, IW_EV_LCP_LEN);
		memcpy(pcCur + IW_EV_LCP_LEN,
		       &iwEvent.u.data.length, sizeof(struct iw_point) - IW_EV_POINT_OFF);
#endif
		memcpy(pcCur + IW_EV_POINT_LEN, prBss->rSsid.aucSsid, iwEvent.u.essid.length);
		pcCur += iwEvent.len;
		/* Frequency */
		iwEvent.cmd = SIOCGIWFREQ;
		iwEvent.len = IW_EV_FREQ_LEN;
		if ((pcCur + iwEvent.len) > pcEnd)
			break;
		iwEvent.u.freq.m = prBss->rConfiguration.u4DSConfig;
		iwEvent.u.freq.e = 3;	/* (in KHz) */
		iwEvent.u.freq.i = 0;
		memcpy(pcCur, &iwEvent, IW_EV_FREQ_LEN);
		pcCur += IW_EV_FREQ_LEN;

		/* Operation Mode */
		iwEvent.cmd = SIOCGIWMODE;
		iwEvent.len = IW_EV_UINT_LEN;
		if ((pcCur + iwEvent.len) > pcEnd)
			break;
		if (prBss->eOpMode == NET_TYPE_IBSS) {
			iwEvent.u.mode = IW_MODE_ADHOC;
		} else if (prBss->eOpMode == NET_TYPE_INFRA) {
			iwEvent.u.mode = IW_MODE_INFRA;
		} else {
			iwEvent.u.mode = IW_MODE_AUTO;
		}
		memcpy(pcCur, &iwEvent, IW_EV_UINT_LEN);
		pcCur += IW_EV_UINT_LEN;

		/* Quality */
		iwEvent.cmd = IWEVQUAL;
		iwEvent.len = IW_EV_QUAL_LEN;
		if ((pcCur + iwEvent.len) > pcEnd)
			break;
		iwEvent.u.qual.qual = 0;	/* Quality not available now */
		/* -100 < Rssi < -10, normalized by adding 0x100 */
		iwEvent.u.qual.level = 0x100 + prBss->rRssi;
		iwEvent.u.qual.noise = 0;	/* Noise not available now */
		iwEvent.u.qual.updated = IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_UPDATED
		    | IW_QUAL_NOISE_INVALID;
		memcpy(pcCur, &iwEvent, IW_EV_QUAL_LEN);
		pcCur += IW_EV_QUAL_LEN;

		/* Security Mode */
		iwEvent.cmd = SIOCGIWENCODE;
		iwEvent.len = IW_EV_POINT_LEN;
		if ((pcCur + iwEvent.len) > pcEnd)
			break;
		iwEvent.u.data.pointer = NULL;
		iwEvent.u.data.flags = 0;
		iwEvent.u.data.length = 0;
		if (!prBss->u4Privacy) {
			iwEvent.u.data.flags |= IW_ENCODE_DISABLED;
		}
#if WIRELESS_EXT <= 18
		memcpy(pcCur, &iwEvent, IW_EV_POINT_LEN);
#else
		memcpy(pcCur, &iwEvent, IW_EV_LCP_LEN);
		memcpy(pcCur + IW_EV_LCP_LEN,
		       &iwEvent.u.data.length, sizeof(struct iw_point) - IW_EV_POINT_OFF);
#endif
		pcCur += IW_EV_POINT_LEN;

		/* rearrange rate information */
		u4BufIndex = sprintf(aucRatesBuf, "Rates (Mb/s):");
		u4HighestRate = 0;
		for (j = 0; j < PARAM_MAX_LEN_RATES_EX; ++j) {
			UINT_8 curRate = prBss->rSupportedRates[j] & 0x7F;
			if (curRate == 0) {
				break;
			}

			if (curRate > u4HighestRate) {
				u4HighestRate = curRate;
			}

			if (curRate == RATE_5_5M) {
				u4BufIndex += sprintf(aucRatesBuf + u4BufIndex, " 5.5");
			} else {
				u4BufIndex += sprintf(aucRatesBuf + u4BufIndex, " %d", curRate / 2);
			}
#if DBG
			if (u4BufIndex > sizeof(aucRatesBuf)) {
				/* printk("rate info too long\n"); */
				break;
			}
#endif
		}
		/* Report Highest Rates */
		iwEvent.cmd = SIOCGIWRATE;
		iwEvent.len = IW_EV_PARAM_LEN;
		if ((pcCur + iwEvent.len) > pcEnd)
			break;
		iwEvent.u.bitrate.value = u4HighestRate * 500000;
		iwEvent.u.bitrate.fixed = 0;
		iwEvent.u.bitrate.disabled = 0;
		iwEvent.u.bitrate.flags = 0;
		memcpy(pcCur, &iwEvent, iwEvent.len);
		pcCur += iwEvent.len;

#if WIRELESS_EXT >= 15		/* IWEVCUSTOM is available in WE-15 or above */
		/* Report Residual Rates */
		iwEvent.cmd = IWEVCUSTOM;
		iwEvent.u.data.length = u4BufIndex;
		iwEvent.len = IW_EV_POINT_LEN + iwEvent.u.data.length;
		if ((pcCur + iwEvent.len) > pcEnd)
			break;
		iwEvent.u.data.flags = 0;
#if WIRELESS_EXT <= 18
		memcpy(pcCur, &iwEvent, IW_EV_POINT_LEN);
#else
		memcpy(pcCur, &iwEvent, IW_EV_LCP_LEN);
		memcpy(pcCur + IW_EV_LCP_LEN,
		       &iwEvent.u.data.length, sizeof(struct iw_point) - IW_EV_POINT_OFF);
#endif
		memcpy(pcCur + IW_EV_POINT_LEN, aucRatesBuf, u4BufIndex);
		pcCur += iwEvent.len;
#endif				/* WIRELESS_EXT >= 15 */

		if (wextSrchDesiredWPAIE(&prBss->aucIEs[sizeof(PARAM_FIXED_IEs)],
					 prBss->u4IELength - sizeof(PARAM_FIXED_IEs),
					 0xDD, (PUINT_8 *) &prDesiredIE)) {
			iwEvent.cmd = IWEVGENIE;
			iwEvent.u.data.flags = 1;
			iwEvent.u.data.length = 2 + (__u16) prDesiredIE->ucLength;
			iwEvent.len = IW_EV_POINT_LEN + iwEvent.u.data.length;
			if ((pcCur + iwEvent.len) > pcEnd)
				break;
#if WIRELESS_EXT <= 18
			memcpy(pcCur, &iwEvent, IW_EV_POINT_LEN);
#else
			memcpy(pcCur, &iwEvent, IW_EV_LCP_LEN);
			memcpy(pcCur + IW_EV_LCP_LEN,
			       &iwEvent.u.data.length, sizeof(struct iw_point) - IW_EV_POINT_OFF);
#endif
			memcpy(pcCur + IW_EV_POINT_LEN, prDesiredIE, 2 + prDesiredIE->ucLength);
			pcCur += iwEvent.len;
		}
#if CFG_SUPPORT_WPS		/* search WPS IE (0xDD, 221, OUI: 0x0050f204 ) */
		if (wextSrchDesiredWPSIE(&prBss->aucIEs[sizeof(PARAM_FIXED_IEs)],
					 prBss->u4IELength - sizeof(PARAM_FIXED_IEs),
					 0xDD, (PUINT_8 *) &prDesiredIE)) {
			iwEvent.cmd = IWEVGENIE;
			iwEvent.u.data.flags = 1;
			iwEvent.u.data.length = 2 + (__u16) prDesiredIE->ucLength;
			iwEvent.len = IW_EV_POINT_LEN + iwEvent.u.data.length;
			if ((pcCur + iwEvent.len) > pcEnd)
				break;
#if WIRELESS_EXT <= 18
			memcpy(pcCur, &iwEvent, IW_EV_POINT_LEN);
#else
			memcpy(pcCur, &iwEvent, IW_EV_LCP_LEN);
			memcpy(pcCur + IW_EV_LCP_LEN,
			       &iwEvent.u.data.length, sizeof(struct iw_point) - IW_EV_POINT_OFF);
#endif
			memcpy(pcCur + IW_EV_POINT_LEN, prDesiredIE, 2 + prDesiredIE->ucLength);
			pcCur += iwEvent.len;
		}
#endif

		/* Search RSN IE (0x30, 48). pBss->IEs starts from timestamp. */
		/* pBss->IEs starts from timestamp */
		if (wextSrchDesiredWPAIE(&prBss->aucIEs[sizeof(PARAM_FIXED_IEs)],
					 prBss->u4IELength - sizeof(PARAM_FIXED_IEs),
					 0x30, (PUINT_8 *) &prDesiredIE)) {

			iwEvent.cmd = IWEVGENIE;
			iwEvent.u.data.flags = 1;
			iwEvent.u.data.length = 2 + (__u16) prDesiredIE->ucLength;
			iwEvent.len = IW_EV_POINT_LEN + iwEvent.u.data.length;
			if ((pcCur + iwEvent.len) > pcEnd)
				break;
#if WIRELESS_EXT <= 18
			memcpy(pcCur, &iwEvent, IW_EV_POINT_LEN);
#else
			memcpy(pcCur, &iwEvent, IW_EV_LCP_LEN);
			memcpy(pcCur + IW_EV_LCP_LEN,
			       &iwEvent.u.data.length, sizeof(struct iw_point) - IW_EV_POINT_OFF);
#endif
			memcpy(pcCur + IW_EV_POINT_LEN, prDesiredIE, 2 + prDesiredIE->ucLength);
			pcCur += iwEvent.len;
		}
#if CFG_SUPPORT_WAPI		/* Android+ */
		if (wextSrchDesiredWAPIIE(&prBss->aucIEs[sizeof(PARAM_FIXED_IEs)],
					  prBss->u4IELength - sizeof(PARAM_FIXED_IEs),
					  (PUINT_8 *) &prDesiredIE)) {

#if 0
			iwEvent.cmd = IWEVGENIE;
			iwEvent.u.data.flags = 1;
			iwEvent.u.data.length = 2 + (__u16) prDesiredIE->ucLength;
			iwEvent.len = IW_EV_POINT_LEN + iwEvent.u.data.length;
			if ((pcCur + iwEvent.len) > pcEnd)
				break;
#if WIRELESS_EXT <= 18
			memcpy(pcCur, &iwEvent, IW_EV_POINT_LEN);
#else
			memcpy(pcCur, &iwEvent, IW_EV_LCP_LEN);
			memcpy(pcCur + IW_EV_LCP_LEN,
			       &iwEvent.u.data.length, sizeof(struct iw_point) - IW_EV_POINT_OFF);
#endif
			memcpy(pcCur + IW_EV_POINT_LEN, prDesiredIE, 2 + prDesiredIE->ucLength);
			pcCur += iwEvent.len;
#else
			iwEvent.cmd = IWEVCUSTOM;
			iwEvent.u.data.length = (2 + prDesiredIE->ucLength) * 2 + 8 /* wapi_ie= */;
			iwEvent.len = IW_EV_POINT_LEN + iwEvent.u.data.length;
			if ((pcCur + iwEvent.len) > pcEnd)
				break;
			iwEvent.u.data.flags = 1;

			memcpy(pcCur, &iwEvent, IW_EV_LCP_LEN);
			memcpy(pcCur + IW_EV_LCP_LEN,
			       &iwEvent.u.data.length, sizeof(struct iw_point) - IW_EV_POINT_OFF);

			pcCur += (IW_EV_POINT_LEN);

			pcCur += sprintf(pcCur, "wapi_ie=");

			snprintf_hex(pcCur, pcEnd - pcCur, (UINT_8 *) prDesiredIE,
				     prDesiredIE->ucLength + 2);

			pcCur += (2 + prDesiredIE->ucLength) * 2 /* iwEvent.len */;
#endif
		}
#endif
		/* Complete an entry. Update end of valid entry */
		pcValidEntryEnd = pcCur;
		/* Extract next bss */
		prBss = (P_PARAM_BSSID_EX_T) ((char *)prBss + prBss->u4Length);
	}

	/* Update valid data length for caller function and upper layer
	 * applications.
	 */
	prData->length = (pcValidEntryEnd - pcExtra);
	/* printk(KERN_INFO "[wifi] buf:%d result:%ld\n", pData->length, u4BufLen); */

	/* kalIndicateStatusAndComplete(prGlueInfo, WLAN_STATUS_SCAN_COMPLETE, NULL, 0); */

 error :
	/* free local query buffer */
	if (prList) {
		kalMemFree(prList, VIR_MEM_TYPE, u4AllocBufLen);
	}

	return ret;
}				/* wext_get_scan */

/*----------------------------------------------------------------------------*/
/*!
* \brief To set desired network name ESSID.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[in] prEssid Pointer of iw_point header.
* \param[in] pcExtra Pointer to buffer srtoring essid string.
*
* \retval 0 If netif_carrier_ok.
* \retval -E2BIG Essid string length is too big.
* \retval -EINVAL pcExtra is null pointer.
* \retval -EFAULT Driver fail to set new essid.
*
* \note If string lengh is ok, device will try connecting to the new network.
*/
/*----------------------------------------------------------------------------*/
static int
wext_set_essid(IN struct net_device *prNetDev,
	       IN struct iw_request_info *prIwrInfo, IN struct iw_point *prEssid, IN char *pcExtra)
{
	PARAM_SSID_T rNewSsid;
	UINT_32 cipher;
	ENUM_PARAM_ENCRYPTION_STATUS_T eEncStatus;
	ENUM_PARAM_AUTH_MODE_T eAuthMode;

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(prEssid);
	ASSERT(pcExtra);
	if (FALSE == GLUE_CHK_PR3(prNetDev, prEssid, pcExtra)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	if (prEssid->length > IW_ESSID_MAX_SIZE) {
		return -E2BIG;
	}

	/* set auth mode */
	if (prGlueInfo->rWpaInfo.u4WpaVersion == IW_AUTH_WPA_VERSION_DISABLED) {
		eAuthMode = (prGlueInfo->rWpaInfo.u4AuthAlg == IW_AUTH_ALG_OPEN_SYSTEM) ?
		    AUTH_MODE_OPEN : AUTH_MODE_AUTO_SWITCH;
		/* printk(KERN_INFO "IW_AUTH_WPA_VERSION_DISABLED->Param_AuthMode%s\n", */
		/* (eAuthMode == AUTH_MODE_OPEN) ? "Open" : "Shared"); */
	} else {
		/* set auth mode */
		switch (prGlueInfo->rWpaInfo.u4KeyMgmt) {
		case IW_AUTH_KEY_MGMT_802_1X:
			eAuthMode =
			    (prGlueInfo->rWpaInfo.u4WpaVersion == IW_AUTH_WPA_VERSION_WPA) ?
			    AUTH_MODE_WPA : AUTH_MODE_WPA2;
			/* printk("IW_AUTH_KEY_MGMT_802_1X->AUTH_MODE_WPA%s\n", */
			/* (eAuthMode == AUTH_MODE_WPA) ? "" : "2"); */
			break;
		case IW_AUTH_KEY_MGMT_PSK:
			eAuthMode =
			    (prGlueInfo->rWpaInfo.u4WpaVersion == IW_AUTH_WPA_VERSION_WPA) ?
			    AUTH_MODE_WPA_PSK : AUTH_MODE_WPA2_PSK;
			/* printk("IW_AUTH_KEY_MGMT_PSK->AUTH_MODE_WPA%sPSK\n", */
			/* (eAuthMode == AUTH_MODE_WPA_PSK) ? "" : "2"); */
			break;
#if CFG_SUPPORT_WAPI		/* Android+ */
		case IW_AUTH_KEY_MGMT_WAPI_PSK:
			break;
		case IW_AUTH_KEY_MGMT_WAPI_CERT:
			break;
#endif

/* #if defined (IW_AUTH_KEY_MGMT_WPA_NONE) */
/* case IW_AUTH_KEY_MGMT_WPA_NONE: */
/* eAuthMode = AUTH_MODE_WPA_NONE; */
/* //printk("IW_AUTH_KEY_MGMT_WPA_NONE->AUTH_MODE_WPA_NONE\n"); */
/* break; */
/* #endif */
#if CFG_SUPPORT_802_11W
		case IW_AUTH_KEY_MGMT_802_1X_SHA256:
			eAuthMode = AUTH_MODE_WPA2;
			break;
		case IW_AUTH_KEY_MGMT_PSK_SHA256:
			eAuthMode = AUTH_MODE_WPA2_PSK;
			break;
#endif
		default:
			/* printk(KERN_INFO DRV_NAME"strange IW_AUTH_KEY_MGMT : %ld set auto switch\n", */
			/* prGlueInfo->rWpaInfo.u4KeyMgmt); */
			eAuthMode = AUTH_MODE_AUTO_SWITCH;
			break;
		}
	}

	rStatus = kalIoctl(prGlueInfo,
			   wlanoidSetAuthMode,
			   &eAuthMode, sizeof(eAuthMode), FALSE, FALSE, FALSE, FALSE, &u4BufLen);

	/* set encryption status */
	cipher = prGlueInfo->rWpaInfo.u4CipherGroup | prGlueInfo->rWpaInfo.u4CipherPairwise;
	if (cipher & IW_AUTH_CIPHER_CCMP) {
		/* printk("IW_AUTH_CIPHER_CCMP->ENUM_ENCRYPTION3_ENABLED\n"); */
		eEncStatus = ENUM_ENCRYPTION3_ENABLED;
	} else if (cipher & IW_AUTH_CIPHER_TKIP) {
		/* printk("IW_AUTH_CIPHER_TKIP->ENUM_ENCRYPTION2_ENABLED\n"); */
		eEncStatus = ENUM_ENCRYPTION2_ENABLED;
	} else if (cipher & (IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40)) {
		/* printk("IW_AUTH_CIPHER_WEPx->ENUM_ENCRYPTION1_ENABLED\n"); */
		eEncStatus = ENUM_ENCRYPTION1_ENABLED;
	} else if (cipher & IW_AUTH_CIPHER_NONE) {
		/* printk("IW_AUTH_CIPHER_NONE->ENUM_ENCRYPTION_DISABLED\n"); */
		if (prGlueInfo->rWpaInfo.fgPrivacyInvoke)
			eEncStatus = ENUM_ENCRYPTION1_ENABLED;
		else
			eEncStatus = ENUM_ENCRYPTION_DISABLED;
	} else {
		/* printk("unknown IW_AUTH_CIPHER->Param_EncryptionDisabled\n"); */
		eEncStatus = ENUM_ENCRYPTION_DISABLED;
	}

	rStatus = kalIoctl(prGlueInfo,
			   wlanoidSetEncryptionStatus,
			   &eEncStatus, sizeof(eEncStatus), FALSE, FALSE, FALSE, FALSE, &u4BufLen);

#if WIRELESS_EXT < 21
	/* GeorgeKuo: a length error bug exists in (WE < 21) cases, kernel before
	 ** 2.6.19. Cut the trailing '\0'.
	 */
	rNewSsid.u4SsidLen = (prEssid->length) ? prEssid->length - 1 : 0;
#else
	rNewSsid.u4SsidLen = prEssid->length;
#endif
	kalMemCopy(rNewSsid.aucSsid, pcExtra, rNewSsid.u4SsidLen);

	/*
	   rNewSsid.aucSsid[rNewSsid.u4SsidLen] = '\0';
	   printk("set ssid(%lu): %s\n", rNewSsid.u4SsidLen, rNewSsid.aucSsid);
	 */

	if (kalIoctl(prGlueInfo,
		     wlanoidSetSsid,
		     (PVOID) & rNewSsid,
		     sizeof(PARAM_SSID_T),
		     FALSE, FALSE, TRUE, FALSE, &u4BufLen) != WLAN_STATUS_SUCCESS) {
		/* printk(KERN_WARNING "Fail to set ssid\n"); */
		return -EFAULT;
	}

	return 0;
}				/* wext_set_essid */

/*----------------------------------------------------------------------------*/
/*!
* \brief To get current network name ESSID.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[in] prEssid Pointer to iw_point structure containing essid information.
* \param[out] pcExtra Pointer to buffer srtoring essid string.
*
* \retval 0 If netif_carrier_ok.
* \retval -ENOTCONN Otherwise.
*
* \note If netif_carrier_ok, network essid is stored in pcExtra.
*/
/*----------------------------------------------------------------------------*/
/* static PARAM_SSID_T ssid; */
static int
wext_get_essid(IN struct net_device *prNetDev,
	       IN struct iw_request_info *prIwrInfo, IN struct iw_point *prEssid, OUT char *pcExtra)
{
	/* PARAM_SSID_T ssid; */

	P_PARAM_SSID_T prSsid;
	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(prEssid);
	ASSERT(pcExtra);

	if (FALSE == GLUE_CHK_PR3(prNetDev, prEssid, pcExtra)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	/* if (!netif_carrier_ok(prNetDev)) { */
	/* return -ENOTCONN; */
	/* } */

	prSsid = kalMemAlloc(sizeof(PARAM_SSID_T), VIR_MEM_TYPE);

	if (!prSsid) {
		return -ENOMEM;
	}

	rStatus = kalIoctl(prGlueInfo,
			   wlanoidQuerySsid,
			   prSsid, sizeof(PARAM_SSID_T), TRUE, FALSE, FALSE, FALSE, &u4BufLen);

	if ((rStatus == WLAN_STATUS_SUCCESS) && (prSsid->u4SsidLen <= MAX_SSID_LEN)) {
		kalMemCopy(pcExtra, prSsid->aucSsid, prSsid->u4SsidLen);
		prEssid->length = prSsid->u4SsidLen;
		prEssid->flags = 1;
	}

	kalMemFree(prSsid, VIR_MEM_TYPE, sizeof(PARAM_SSID_T));

	return 0;
}				/* wext_get_essid */

#if 0

/*----------------------------------------------------------------------------*/
/*!
* \brief To set tx desired bit rate. Three cases here
*        iwconfig wlan0 auto -> Set to origianl supported rate set.
*        iwconfig wlan0 18M -> Imply "fixed" case, set to 18Mbps as desired rate.
*        iwconfig wlan0 18M auto -> Set to auto rate lower and equal to 18Mbps
*
* \param[in] prNetDev       Pointer to the net_device handler.
* \param[in] prIwReqInfo    Pointer to the Request Info.
* \param[in] prRate         Pointer to the Rate Parameter.
* \param[in] pcExtra        Pointer to the extra buffer.
*
* \retval 0         Update desired rate.
* \retval -EINVAL   Wrong parameter
*/
/*----------------------------------------------------------------------------*/
int
wext_set_rate(IN struct net_device *prNetDev,
	      IN struct iw_request_info *prIwReqInfo, IN struct iw_param *prRate, IN char *pcExtra)
{
	PARAM_RATES_EX aucSuppRate = { 0 };
	PARAM_RATES_EX aucNewRate = { 0 };
	UINT_32 u4NewRateLen = 0;
	UINT_32 i;

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(prRate);
	if (FALSE == GLUE_CHK_PR2(prNetDev, prRate)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	/*
	   printk("value = %d, fixed = %d, disable = %d, flags = %d\n",
	   prRate->value, prRate->fixed, prRate->disabled, prRate->flags);
	 */

	rStatus = wlanQueryInformation(prGlueInfo->prAdapter,
				       wlanoidQuerySupportedRates,
				       &aucSuppRate, sizeof(aucSuppRate), &u4BufLen);

	/* Case: AUTO */
	if (prRate->value < 0) {
		if (prRate->fixed == 0) {
			/* iwconfig wlan0 rate auto */

			/* set full supported rate to device */
			/* printk("wlanoidQuerySupportedRates():u4BufLen = %ld\n", u4BufLen); */
			rStatus = wlanSetInformation(prGlueInfo->prAdapter,
						     wlanoidSetDesiredRates,
						     &aucSuppRate, sizeof(aucSuppRate), &u4BufLen);
			return 0;
		} else {
			/* iwconfig wlan0 rate fixed */

			/* fix rate to what? DO NOTHING */
			return -EINVAL;
		}
	}

	aucNewRate[0] = prRate->value / 500000;	/* In unit of 500k */

	for (i = 0; i < PARAM_MAX_LEN_RATES_EX; i++) {
		/* check the given value is supported */
		if (aucSuppRate[i] == 0) {
			break;
		}

		if (aucNewRate[0] == aucSuppRate[i]) {
			u4NewRateLen = 1;
			break;
		}
	}

	if (u4NewRateLen == 0) {
		/* the given value is not supported */
		/* return error or use given rate as upper bound? */
		return -EINVAL;
	}

	if (prRate->fixed == 0) {
		/* add all rates lower than desired rate */
		for (i = 0; i < PARAM_MAX_LEN_RATES_EX; ++i) {
			if (aucSuppRate[i] == 0) {
				break;
			}

			if (aucSuppRate[i] < aucNewRate[0]) {
				aucNewRate[u4NewRateLen++] = aucSuppRate[i];
			}
		}
	}

	rStatus = wlanSetInformation(prGlueInfo->prAdapter,
				     wlanoidSetDesiredRates,
				     &aucNewRate, sizeof(aucNewRate), &u4BufLen);
	return 0;
}				/* wext_set_rate */

#endif

/*----------------------------------------------------------------------------*/
/*!
* \brief To get current tx bit rate.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[out] prRate Pointer to iw_param structure to store current tx rate.
* \param[in] pcExtra NULL.
*
* \retval 0 If netif_carrier_ok.
* \retval -ENOTCONN Otherwise.
*
* \note If netif_carrier_ok, current tx rate is stored in pRate.
*/
/*----------------------------------------------------------------------------*/
static int
wext_get_rate(IN struct net_device *prNetDev,
	      IN struct iw_request_info *prIwrInfo, OUT struct iw_param *prRate, IN char *pcExtra)
{
	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;
	UINT_32 u4Rate = 0;

	ASSERT(prNetDev);
	ASSERT(prRate);
	if (FALSE == GLUE_CHK_PR2(prNetDev, prRate)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	if (!netif_carrier_ok(prNetDev)) {
		return -ENOTCONN;
	}

	rStatus = kalIoctl(prGlueInfo,
			   wlanoidQueryLinkSpeed,
			   &u4Rate, sizeof(u4Rate), TRUE, FALSE, FALSE, FALSE, &u4BufLen);

	if (rStatus != WLAN_STATUS_SUCCESS) {
		return -EFAULT;
	}

	prRate->value = u4Rate * 100;	/* u4Rate is in unit of 100bps */
	prRate->fixed = 0;

	return 0;
}				/* wext_get_rate */

/*----------------------------------------------------------------------------*/
/*!
* \brief To set RTS/CTS theshold.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[in] prRts Pointer to iw_param structure containing rts threshold.
* \param[in] pcExtra NULL.
*
* \retval 0 For success.
* \retval -EINVAL Given value is out of range.
*
* \note If given value is valid, device will follow the new setting.
*/
/*----------------------------------------------------------------------------*/
static int
wext_set_rts(IN struct net_device *prNetDev,
	     IN struct iw_request_info *prIwrInfo, IN struct iw_param *prRts, IN char *pcExtra)
{
	PARAM_RTS_THRESHOLD u4RtsThresh;

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(prRts);
	if (FALSE == GLUE_CHK_PR2(prNetDev, prRts)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	if (prRts->disabled == 1) {
		u4RtsThresh = 2347;
	} else if (prRts->value < 0 || prRts->value > 2347) {
		return -EINVAL;
	} else {
		u4RtsThresh = (PARAM_RTS_THRESHOLD) prRts->value;
	}

	rStatus = kalIoctl(prGlueInfo,
			   wlanoidSetRtsThreshold,
			   &u4RtsThresh,
			   sizeof(u4RtsThresh), FALSE, FALSE, FALSE, FALSE, &u4BufLen);

	prRts->value = (typeof(prRts->value)) u4RtsThresh;
	prRts->disabled = (prRts->value > 2347) ? 1 : 0;
	prRts->fixed = 1;

	return 0;
}				/* wext_set_rts */

/*----------------------------------------------------------------------------*/
/*!
* \brief To get RTS/CTS theshold.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[out] prRts Pointer to iw_param structure containing rts threshold.
* \param[in] pcExtra NULL.
*
* \retval 0 Success.
*
* \note RTS threshold is stored in pRts.
*/
/*----------------------------------------------------------------------------*/
static int
wext_get_rts(IN struct net_device *prNetDev,
	     IN struct iw_request_info *prIwrInfo, OUT struct iw_param *prRts, IN char *pcExtra)
{
	PARAM_RTS_THRESHOLD u4RtsThresh;

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(prRts);
	if (FALSE == GLUE_CHK_PR2(prNetDev, prRts)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	rStatus = kalIoctl(prGlueInfo,
			   wlanoidQueryRtsThreshold,
			   &u4RtsThresh, sizeof(u4RtsThresh), TRUE, FALSE, FALSE, FALSE, &u4BufLen);

	prRts->value = (typeof(prRts->value)) u4RtsThresh;
	prRts->disabled = (prRts->value > 2347 || prRts->value < 0) ? 1 : 0;
	prRts->fixed = 1;

	return 0;
}				/* wext_get_rts */

/*----------------------------------------------------------------------------*/
/*!
* \brief To get fragmentation threshold.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[out] prFrag Pointer to iw_param structure containing frag threshold.
* \param[in] pcExtra NULL.
*
* \retval 0 Success.
*
* \note RTS threshold is stored in pFrag. Fragmentation is disabled.
*/
/*----------------------------------------------------------------------------*/
static int
wext_get_frag(IN struct net_device *prNetDev,
	      IN struct iw_request_info *prIwrInfo, OUT struct iw_param *prFrag, IN char *pcExtra)
{
	ASSERT(prFrag);

	prFrag->value = 2346;
	prFrag->fixed = 1;
	prFrag->disabled = 1;
	return 0;
}				/* wext_get_frag */

#if 1
/*----------------------------------------------------------------------------*/
/*!
* \brief To set TX power, or enable/disable the radio.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[in] prTxPow Pointer to iw_param structure containing tx power setting.
* \param[in] pcExtra NULL.
*
* \retval 0 Success.
*
* \note Tx power is stored in pTxPow. iwconfig wlan0 txpow on/off are used
*       to enable/disable the radio.
*/
/*----------------------------------------------------------------------------*/

static int
wext_set_txpow(IN struct net_device *prNetDev,
	       IN struct iw_request_info *prIwrInfo, IN struct iw_param *prTxPow, IN char *pcExtra)
{
	int ret = 0;
	/* PARAM_DEVICE_POWER_STATE ePowerState; */
	ENUM_ACPI_STATE_T ePowerState;

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(prTxPow);
	if (FALSE == GLUE_CHK_PR2(prNetDev, prTxPow)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	if (prTxPow->disabled) {
		/* <1> disconnect */
		rStatus = kalIoctl(prGlueInfo,
				   wlanoidSetDisassociate,
				   NULL, 0, FALSE, FALSE, TRUE, FALSE, &u4BufLen);
		if (rStatus != WLAN_STATUS_SUCCESS) {
			DBGLOG(INIT, INFO, ("######set disassoc failed\n"));
		} else {
			DBGLOG(INIT, INFO, ("######set assoc ok\n"));
		}

		/* <2> mark to power state flag */
		ePowerState = ACPI_STATE_D0;
		DBGLOG(INIT, INFO, ("set to acpi d3(0)\n"));
		wlanSetAcpiState(prGlueInfo->prAdapter, ePowerState);

	} else {
		ePowerState = ACPI_STATE_D0;
		DBGLOG(INIT, INFO, ("set to acpi d0\n"));
		wlanSetAcpiState(prGlueInfo->prAdapter, ePowerState);
	}

	prGlueInfo->ePowerState = ePowerState;

	return ret;
}				/* wext_set_txpow */

#endif

/*----------------------------------------------------------------------------*/
/*!
* \brief To get TX power.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[out] prTxPow Pointer to iw_param structure containing tx power setting.
* \param[in] pcExtra NULL.
*
* \retval 0 Success.
*
* \note Tx power is stored in pTxPow.
*/
/*----------------------------------------------------------------------------*/
static int
wext_get_txpow(IN struct net_device *prNetDev,
	       IN struct iw_request_info *prIwrInfo, OUT struct iw_param *prTxPow, IN char *pcExtra)
{
	/* PARAM_DEVICE_POWER_STATE ePowerState; */

	P_GLUE_INFO_T prGlueInfo = NULL;

	ASSERT(prNetDev);
	ASSERT(prTxPow);
	if (FALSE == GLUE_CHK_PR2(prNetDev, prTxPow)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	/* GeorgeKuo: wlanoidQueryAcpiDevicePowerState() reports capability, not
	 * current state. Use GLUE_INFO_T to store state.
	 */
	/* ePowerState = prGlueInfo->ePowerState; */

	/* TxPow parameters: Fixed at relative 100% */
#if WIRELESS_EXT < 17
	prTxPow->flags = 0x0002;	/* IW_TXPOW_RELATIVE */
#else
	prTxPow->flags = IW_TXPOW_RELATIVE;
#endif
	prTxPow->value = 100;
	prTxPow->fixed = 1;
	/* prTxPow->disabled = (ePowerState != ParamDeviceStateD3) ? FALSE : TRUE; */
	prTxPow->disabled = TRUE;

	return 0;
}				/* wext_get_txpow */

/*----------------------------------------------------------------------------*/
/*!
* \brief To get encryption cipher and key.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[out] prEnc Pointer to iw_point structure containing securiry information.
* \param[in] pcExtra Buffer to store key content.
*
* \retval 0 Success.
*
* \note Securiry information is stored in pEnc except key content.
*/
/*----------------------------------------------------------------------------*/
static int
wext_get_encode(IN struct net_device *prNetDev,
		IN struct iw_request_info *prIwrInfo, OUT struct iw_point *prEnc, IN char *pcExtra)
{
#if 1
	/* ENUM_ENCRYPTION_STATUS_T eEncMode; */
	ENUM_PARAM_ENCRYPTION_STATUS_T eEncMode;

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(prEnc);
	if (FALSE == GLUE_CHK_PR2(prNetDev, prEnc)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	rStatus = kalIoctl(prGlueInfo,
			   wlanoidQueryEncryptionStatus,
			   &eEncMode, sizeof(eEncMode), TRUE, FALSE, FALSE, FALSE, &u4BufLen);

	switch (eEncMode) {
	case ENUM_WEP_DISABLED:
		prEnc->flags = IW_ENCODE_DISABLED;
		break;
	case ENUM_WEP_ENABLED:
		prEnc->flags = IW_ENCODE_ENABLED;
		break;
	case ENUM_WEP_KEY_ABSENT:
		prEnc->flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
		break;
	default:
		prEnc->flags = IW_ENCODE_ENABLED;
		break;
	}

	/* Cipher, Key Content, Key ID can't be queried */
	prEnc->flags |= IW_ENCODE_NOKEY;
#endif
	return 0;
}				/* wext_get_encode */

/*----------------------------------------------------------------------------*/
/*!
* \brief To set encryption cipher and key.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[in] prEnc Pointer to iw_point structure containing securiry information.
* \param[in] pcExtra Pointer to key string buffer.
*
* \retval 0 Success.
* \retval -EINVAL Key ID error for WEP.
* \retval -EFAULT Setting parameters to driver fail.
* \retval -EOPNOTSUPP Key size not supported.
*
* \note Securiry information is stored in pEnc.
*/
/*----------------------------------------------------------------------------*/
static UINT_8 wepBuf[48];

static int
wext_set_encode(IN struct net_device *prNetDev,
		IN struct iw_request_info *prIwrInfo, IN struct iw_point *prEnc, IN char *pcExtra)
{
#if 1
	ENUM_PARAM_ENCRYPTION_STATUS_T eEncStatus;
	ENUM_PARAM_AUTH_MODE_T eAuthMode;
	/* UINT_8 wepBuf[48]; */
	P_PARAM_WEP_T prWepKey = (P_PARAM_WEP_T) wepBuf;

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(prEnc);
	ASSERT(pcExtra);
	if (FALSE == GLUE_CHK_PR3(prNetDev, prEnc, pcExtra)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	/* reset to default mode */
	prGlueInfo->rWpaInfo.u4WpaVersion = IW_AUTH_WPA_VERSION_DISABLED;
	prGlueInfo->rWpaInfo.u4KeyMgmt = 0;
	prGlueInfo->rWpaInfo.u4CipherPairwise = IW_AUTH_CIPHER_NONE;
	prGlueInfo->rWpaInfo.u4CipherGroup = IW_AUTH_CIPHER_NONE;
	prGlueInfo->rWpaInfo.u4AuthAlg = IW_AUTH_ALG_OPEN_SYSTEM;
#if CFG_SUPPORT_802_11W
	prGlueInfo->rWpaInfo.u4Mfp = IW_AUTH_MFP_DISABLED;
#endif

	/* iwconfig wlan0 key off */
	if ((prEnc->flags & IW_ENCODE_MODE) == IW_ENCODE_DISABLED) {
		eAuthMode = AUTH_MODE_OPEN;

		rStatus = kalIoctl(prGlueInfo,
				   wlanoidSetAuthMode,
				   &eAuthMode,
				   sizeof(eAuthMode), FALSE, FALSE, FALSE, FALSE, &u4BufLen);

		eEncStatus = ENUM_ENCRYPTION_DISABLED;

		rStatus = kalIoctl(prGlueInfo,
				   wlanoidSetEncryptionStatus,
				   &eEncStatus,
				   sizeof(eEncStatus), FALSE, FALSE, FALSE, FALSE, &u4BufLen);

		return 0;
	}

	/* iwconfig wlan0 key 0123456789 */
	/* iwconfig wlan0 key s:abcde */
	/* iwconfig wlan0 key 0123456789 [1] */
	/* iwconfig wlan0 key 01234567890123456789012345 [1] */
	/* check key size for WEP */
	if (prEnc->length == 5 || prEnc->length == 13 || prEnc->length == 16) {
		/* prepare PARAM_WEP key structure */
		prWepKey->u4KeyIndex = (prEnc->flags & IW_ENCODE_INDEX) ?
		    (prEnc->flags & IW_ENCODE_INDEX) - 1 : 0;
		if (prWepKey->u4KeyIndex > 3) {
			/* key id is out of range */
			return -EINVAL;
		}
		prWepKey->u4KeyIndex |= 0x80000000;
		prWepKey->u4Length = 12 + prEnc->length;
		prWepKey->u4KeyLength = prEnc->length;
		kalMemCopy(prWepKey->aucKeyMaterial, pcExtra, prEnc->length);

		rStatus = kalIoctl(prGlueInfo,
				   wlanoidSetAddWep,
				   prWepKey,
				   prWepKey->u4Length, FALSE, FALSE, TRUE, FALSE, &u4BufLen);

		if (rStatus != WLAN_STATUS_SUCCESS) {
			DBGLOG(INIT, INFO, ("wlanoidSetAddWep fail 0x%lx\n", rStatus));
			return -EFAULT;
		}

		/* change to auto switch */
		prGlueInfo->rWpaInfo.u4AuthAlg = IW_AUTH_ALG_SHARED_KEY | IW_AUTH_ALG_OPEN_SYSTEM;
		eAuthMode = AUTH_MODE_AUTO_SWITCH;

		rStatus = kalIoctl(prGlueInfo,
				   wlanoidSetAuthMode,
				   &eAuthMode,
				   sizeof(eAuthMode), FALSE, FALSE, FALSE, FALSE, &u4BufLen);

		if (rStatus != WLAN_STATUS_SUCCESS) {
			/* printk(KERN_INFO DRV_NAME"wlanoidSetAuthMode fail 0x%lx\n", rStatus); */
			return -EFAULT;
		}

		prGlueInfo->rWpaInfo.u4CipherPairwise =
		    IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40;
		prGlueInfo->rWpaInfo.u4CipherGroup = IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40;

		eEncStatus = ENUM_WEP_ENABLED;

		rStatus = kalIoctl(prGlueInfo,
				   wlanoidSetEncryptionStatus,
				   &eEncStatus,
				   sizeof(ENUM_PARAM_ENCRYPTION_STATUS_T),
				   FALSE, FALSE, FALSE, FALSE, &u4BufLen);

		if (rStatus != WLAN_STATUS_SUCCESS) {
			/* printk(KERN_INFO DRV_NAME"wlanoidSetEncryptionStatus fail 0x%lx\n", rStatus); */
			return -EFAULT;
		}

		return 0;
	}
#endif
	return -EOPNOTSUPP;
}				/* wext_set_encode */

/*----------------------------------------------------------------------------*/
/*!
* \brief To set power management.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[in] prPower Pointer to iw_param structure containing tx power setting.
* \param[in] pcExtra NULL.
*
* \retval 0 Success.
*
* \note New Power Management Mode is set to driver.
*/
/*----------------------------------------------------------------------------*/
static int
wext_set_power(IN struct net_device *prNetDev,
	       IN struct iw_request_info *prIwrInfo, IN struct iw_param *prPower, IN char *pcExtra)
{
#if 1
	PARAM_POWER_MODE ePowerMode;
	INT_32 i4PowerValue;

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(prPower);
	if (FALSE == GLUE_CHK_PR2(prNetDev, prPower)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	/* printk(KERN_INFO "wext_set_power value(%d) disabled(%d) flag(0x%x)\n", */
	/* prPower->value, prPower->disabled, prPower->flags); */

	if (prPower->disabled) {
		ePowerMode = Param_PowerModeCAM;
	} else {
		i4PowerValue = prPower->value;
#if WIRELESS_EXT < 21
		i4PowerValue /= 1000000;
#endif
		if (i4PowerValue == 0) {
			ePowerMode = Param_PowerModeCAM;
		} else if (i4PowerValue == 1) {
			ePowerMode = Param_PowerModeMAX_PSP;
		} else if (i4PowerValue == 2) {
			ePowerMode = Param_PowerModeFast_PSP;
		} else {
			DBGLOG(INIT, INFO, ("%s(): unsupported power management mode value = %d.\n",
					    __func__, prPower->value));

			return -EINVAL;
		}
	}

	rStatus = kalIoctl(prGlueInfo,
			   wlanoidSet802dot11PowerSaveProfile,
			   &ePowerMode, sizeof(ePowerMode), FALSE, FALSE, TRUE, FALSE, &u4BufLen);

	if (rStatus != WLAN_STATUS_SUCCESS) {
		/* printk(KERN_INFO DRV_NAME"wlanoidSet802dot11PowerSaveProfile fail 0x%lx\n", rStatus); */
		return -EFAULT;
	}
#endif
	return 0;
}				/* wext_set_power */

/*----------------------------------------------------------------------------*/
/*!
* \brief To get power management.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[out] prPower Pointer to iw_param structure containing tx power setting.
* \param[in] pcExtra NULL.
*
* \retval 0 Success.
*
* \note Power management mode is stored in pTxPow->value.
*/
/*----------------------------------------------------------------------------*/
static int
wext_get_power(IN struct net_device *prNetDev,
	       IN struct iw_request_info *prIwrInfo, OUT struct iw_param *prPower, IN char *pcExtra)
{

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;
	PARAM_POWER_MODE ePowerMode = Param_PowerModeCAM;

	ASSERT(prNetDev);
	ASSERT(prPower);
	if (FALSE == GLUE_CHK_PR2(prNetDev, prPower)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

#if 0
#if defined(_HIF_SDIO)
	rStatus = sdio_io_ctrl(prGlueInfo,
			       wlanoidQuery802dot11PowerSaveProfile,
			       &ePowerMode, sizeof(ePowerMode), TRUE, TRUE, &u4BufLen);
#else
	rStatus = wlanQueryInformation(prGlueInfo->prAdapter,
				       wlanoidQuery802dot11PowerSaveProfile,
				       &ePowerMode, sizeof(ePowerMode), &u4BufLen);
#endif
#else
	rStatus = wlanQueryInformation(prGlueInfo->prAdapter,
				       wlanoidQuery802dot11PowerSaveProfile,
				       &ePowerMode, sizeof(ePowerMode), &u4BufLen);
#endif

	if (rStatus != WLAN_STATUS_SUCCESS) {
		return -EFAULT;
	}

	prPower->value = 0;
	prPower->disabled = 1;

	if (Param_PowerModeCAM == ePowerMode) {
		prPower->value = 0;
		prPower->disabled = 1;
	} else if (Param_PowerModeMAX_PSP == ePowerMode) {
		prPower->value = 1;
		prPower->disabled = 0;
	} else if (Param_PowerModeFast_PSP == ePowerMode) {
		prPower->value = 2;
		prPower->disabled = 0;
	}

	prPower->flags = IW_POWER_PERIOD | IW_POWER_RELATIVE;
#if WIRELESS_EXT < 21
	prPower->value *= 1000000;
#endif

	/* printk(KERN_INFO "wext_get_power value(%d) disabled(%d) flag(0x%x)\n", */
	/* prPower->value, prPower->disabled, prPower->flags); */

	return 0;
}				/* wext_get_power */

/*----------------------------------------------------------------------------*/
/*!
* \brief To set authentication parameters.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[in] rpAuth Pointer to iw_param structure containing authentication information.
* \param[in] pcExtra Pointer to key string buffer.
*
* \retval 0 Success.
* \retval -EINVAL Key ID error for WEP.
* \retval -EFAULT Setting parameters to driver fail.
* \retval -EOPNOTSUPP Key size not supported.
*
* \note Securiry information is stored in pEnc.
*/
/*----------------------------------------------------------------------------*/
static int
wext_set_auth(IN struct net_device *prNetDev,
	      IN struct iw_request_info *prIwrInfo, IN struct iw_param *prAuth, IN char *pcExtra)
{
	P_GLUE_INFO_T prGlueInfo = NULL;

	ASSERT(prNetDev);
	ASSERT(prAuth);
	if (FALSE == GLUE_CHK_PR2(prNetDev, prAuth)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	/* Save information to glue info and process later when ssid is set. */
	switch (prAuth->flags & IW_AUTH_INDEX) {
	case IW_AUTH_WPA_VERSION:
#if CFG_SUPPORT_WAPI
		if (wlanQueryWapiMode(prGlueInfo->prAdapter)) {
			prGlueInfo->rWpaInfo.u4WpaVersion = IW_AUTH_WPA_VERSION_DISABLED;
			prGlueInfo->rWpaInfo.u4AuthAlg = IW_AUTH_ALG_OPEN_SYSTEM;
		} else {
			prGlueInfo->rWpaInfo.u4WpaVersion = prAuth->value;
		}
#else
		prGlueInfo->rWpaInfo.u4WpaVersion = prAuth->value;
#endif
		break;

	case IW_AUTH_CIPHER_PAIRWISE:
		prGlueInfo->rWpaInfo.u4CipherPairwise = prAuth->value;
		break;

	case IW_AUTH_CIPHER_GROUP:
		prGlueInfo->rWpaInfo.u4CipherGroup = prAuth->value;
		break;

	case IW_AUTH_KEY_MGMT:
		prGlueInfo->rWpaInfo.u4KeyMgmt = prAuth->value;
#if CFG_SUPPORT_WAPI
		if (prGlueInfo->rWpaInfo.u4KeyMgmt == IW_AUTH_KEY_MGMT_WAPI_PSK ||
		    prGlueInfo->rWpaInfo.u4KeyMgmt == IW_AUTH_KEY_MGMT_WAPI_CERT) {
			UINT_32 u4BufLen;
			WLAN_STATUS rStatus;

			rStatus = kalIoctl(prGlueInfo,
					   wlanoidSetWapiMode,
					   &prAuth->value,
					   sizeof(UINT_32), FALSE, FALSE, TRUE, FALSE, &u4BufLen);
			DBGLOG(INIT, INFO, ("IW_AUTH_WAPI_ENABLED :%d\n", prAuth->value));
		}
#endif
		if (prGlueInfo->rWpaInfo.u4KeyMgmt == IW_AUTH_KEY_MGMT_WPS)
			prGlueInfo->fgWpsActive = TRUE;
		else
			prGlueInfo->fgWpsActive = FALSE;
		break;

	case IW_AUTH_80211_AUTH_ALG:
		prGlueInfo->rWpaInfo.u4AuthAlg = prAuth->value;
		break;

	case IW_AUTH_PRIVACY_INVOKED:
		prGlueInfo->rWpaInfo.fgPrivacyInvoke = prAuth->value;
		break;
#if CFG_SUPPORT_802_11W
	case IW_AUTH_MFP:
		/* printk("wext_set_auth IW_AUTH_MFP=%d\n", prAuth->value); */
		prGlueInfo->rWpaInfo.u4Mfp = prAuth->value;
		break;
#endif
#if CFG_SUPPORT_WAPI
	case IW_AUTH_WAPI_ENABLED:
		{
			UINT_32 u4BufLen;
			WLAN_STATUS rStatus;

			rStatus = kalIoctl(prGlueInfo,
					   wlanoidSetWapiMode,
					   &prAuth->value,
					   sizeof(UINT_32), FALSE, FALSE, TRUE, FALSE, &u4BufLen);
		}
		DBGLOG(INIT, INFO, ("IW_AUTH_WAPI_ENABLED :%d\n", prAuth->value));
		break;
#endif
	default:
		/*
		   printk(KERN_INFO "[wifi] unsupported IW_AUTH_INDEX :%d\n", prAuth->flags);
		 */
		break;
	}
	return 0;
}				/* wext_set_auth */

/*----------------------------------------------------------------------------*/
/*!
* \brief To set encryption cipher and key.
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[in] prEnc Pointer to iw_point structure containing securiry information.
* \param[in] pcExtra Pointer to key string buffer.
*
* \retval 0 Success.
* \retval -EINVAL Key ID error for WEP.
* \retval -EFAULT Setting parameters to driver fail.
* \retval -EOPNOTSUPP Key size not supported.
*
* \note Securiry information is stored in pEnc.
*/
/*----------------------------------------------------------------------------*/
#if CFG_SUPPORT_WAPI
UINT_8 keyStructBuf[320];	/* add/remove key shared buffer */
#else
UINT_8 keyStructBuf[100];	/* add/remove key shared buffer */
#endif

static int
wext_set_encode_ext(IN struct net_device *prNetDev,
		    IN struct iw_request_info *prIwrInfo,
		    IN struct iw_point *prEnc, IN char *pcExtra)
{
	P_PARAM_REMOVE_KEY_T prRemoveKey = (P_PARAM_REMOVE_KEY_T) keyStructBuf;
	P_PARAM_KEY_T prKey = (P_PARAM_KEY_T) keyStructBuf;

	P_PARAM_WEP_T prWepKey = (P_PARAM_WEP_T) wepBuf;

	struct iw_encode_ext *prIWEncExt = (struct iw_encode_ext *)pcExtra;

	ENUM_PARAM_ENCRYPTION_STATUS_T eEncStatus;
	ENUM_PARAM_AUTH_MODE_T eAuthMode;
	/* ENUM_PARAM_OP_MODE_T eOpMode = NET_TYPE_AUTO_SWITCH; */

#if CFG_SUPPORT_WAPI
	P_PARAM_WPI_KEY_T prWpiKey = (P_PARAM_WPI_KEY_T) keyStructBuf;
#endif

	P_GLUE_INFO_T prGlueInfo = NULL;
	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
	UINT_32 u4BufLen = 0;

	ASSERT(prNetDev);
	ASSERT(prEnc);
	if (FALSE == GLUE_CHK_PR3(prNetDev, prEnc, pcExtra)) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	memset(keyStructBuf, 0, sizeof(keyStructBuf));

#if CFG_SUPPORT_WAPI
	if (prIWEncExt->alg == IW_ENCODE_ALG_SMS4) {
		if (prEnc->flags & IW_ENCODE_DISABLED) {
			/* printk(KERN_INFO "[wapi] IW_ENCODE_DISABLED\n"); */
			return 0;
		}
		/* KeyID */
		prWpiKey->ucKeyID = (prEnc->flags & IW_ENCODE_INDEX);
		prWpiKey->ucKeyID--;
		if (prWpiKey->ucKeyID > 1) {
			/* key id is out of range */
			/* printk(KERN_INFO "[wapi] add key error: key_id invalid %d\n", prWpiKey->ucKeyID); */
			return -EINVAL;
		}

		if (prIWEncExt->key_len != 32) {
			/* key length not valid */
			/* printk(KERN_INFO "[wapi] add key error: key_len invalid %d\n", prIWEncExt->key_len); */
			return -EINVAL;
		}
		/* printk(KERN_INFO "[wapi] %d ext_flags %d\n", prEnc->flags, prIWEncExt->ext_flags); */

		if (prIWEncExt->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
			prWpiKey->eKeyType = ENUM_WPI_GROUP_KEY;
			prWpiKey->eDirection = ENUM_WPI_RX;
		} else if (prIWEncExt->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
			prWpiKey->eKeyType = ENUM_WPI_PAIRWISE_KEY;
			prWpiKey->eDirection = ENUM_WPI_RX_TX;
		}

		/* PN */
		memcpy(prWpiKey->aucPN, prIWEncExt->tx_seq, IW_ENCODE_SEQ_MAX_SIZE * 2);

		/* BSSID */
		memcpy(prWpiKey->aucAddrIndex, prIWEncExt->addr.sa_data, 6);

		memcpy(prWpiKey->aucWPIEK, prIWEncExt->key, 16);
		prWpiKey->u4LenWPIEK = 16;

		memcpy(prWpiKey->aucWPICK, &prIWEncExt->key[16], 16);
		prWpiKey->u4LenWPICK = 16;

		rStatus = kalIoctl(prGlueInfo,
				   wlanoidSetWapiKey,
				   prWpiKey,
				   sizeof(PARAM_WPI_KEY_T), FALSE, FALSE, TRUE, FALSE, &u4BufLen);

		if (rStatus != WLAN_STATUS_SUCCESS) {
			/* printk(KERN_INFO "[wapi] add key error:%lx\n", rStatus); */
		}

	} else
#endif
	{

		if ((prEnc->flags & IW_ENCODE_MODE) == IW_ENCODE_DISABLED) {
			prRemoveKey->u4Length = sizeof(*prRemoveKey);
			memcpy(prRemoveKey->arBSSID, prIWEncExt->addr.sa_data, 6);
			/*
			   printk("IW_ENCODE_DISABLED: ID:%d, Addr:[" MACSTR "]\n",
			   prRemoveKey->KeyIndex, MAC2STR(prRemoveKey->BSSID));
			 */

			rStatus = kalIoctl(prGlueInfo,
					   wlanoidSetRemoveKey,
					   prRemoveKey,
					   prRemoveKey->u4Length,
					   FALSE, FALSE, TRUE, FALSE, &u4BufLen);

			if (rStatus != WLAN_STATUS_SUCCESS) {
				DBGLOG(INIT, INFO, ("remove key error:%lx\n", rStatus));
			}
			return 0;
		}
		/* return 0; */
		/* printk ("alg %x\n", prIWEncExt->alg); */

		switch (prIWEncExt->alg) {
		case IW_ENCODE_ALG_NONE:
			break;
		case IW_ENCODE_ALG_WEP:
			/* iwconfig wlan0 key 0123456789 */
			/* iwconfig wlan0 key s:abcde */
			/* iwconfig wlan0 key 0123456789 [1] */
			/* iwconfig wlan0 key 01234567890123456789012345 [1] */
			/* check key size for WEP */
			if (prIWEncExt->key_len == 5 || prIWEncExt->key_len == 13
			    || prIWEncExt->key_len == 16) {
				/* prepare PARAM_WEP key structure */
				prWepKey->u4KeyIndex = (prEnc->flags & IW_ENCODE_INDEX) ?
				    (prEnc->flags & IW_ENCODE_INDEX) - 1 : 0;
				if (prWepKey->u4KeyIndex > 3) {
					/* key id is out of range */
					return -EINVAL;
				}
				prWepKey->u4KeyIndex |= 0x80000000;
				prWepKey->u4Length = 12 + prIWEncExt->key_len;
				prWepKey->u4KeyLength = prIWEncExt->key_len;
				/* kalMemCopy(prWepKey->aucKeyMaterial, pcExtra, prIWEncExt->key_len); */
				kalMemCopy(prWepKey->aucKeyMaterial, prIWEncExt->key,
					   prIWEncExt->key_len);

				rStatus = kalIoctl(prGlueInfo,
						   wlanoidSetAddWep,
						   prWepKey,
						   prWepKey->u4Length,
						   FALSE, FALSE, TRUE, FALSE, &u4BufLen);

				if (rStatus != WLAN_STATUS_SUCCESS) {
					DBGLOG(INIT, INFO,
					       ("wlanoidSetAddWep fail 0x%lx\n", rStatus));
					return -EFAULT;
				}

				/* change to auto switch */
				prGlueInfo->rWpaInfo.u4AuthAlg = IW_AUTH_ALG_SHARED_KEY |
				    IW_AUTH_ALG_OPEN_SYSTEM;
				eAuthMode = AUTH_MODE_AUTO_SWITCH;

				rStatus = kalIoctl(prGlueInfo,
						   wlanoidSetAuthMode,
						   &eAuthMode,
						   sizeof(eAuthMode),
						   FALSE, FALSE, FALSE, FALSE, &u4BufLen);

				if (rStatus != WLAN_STATUS_SUCCESS) {
					DBGLOG(INIT, INFO,
					       ("wlanoidSetAuthMode fail 0x%lx\n", rStatus));
					return -EFAULT;
				}

				prGlueInfo->rWpaInfo.u4CipherPairwise =
				    IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40;
				prGlueInfo->rWpaInfo.u4CipherGroup =
				    IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40;

				eEncStatus = ENUM_WEP_ENABLED;

				rStatus = kalIoctl(prGlueInfo,
						   wlanoidSetEncryptionStatus,
						   &eEncStatus,
						   sizeof(ENUM_PARAM_ENCRYPTION_STATUS_T),
						   FALSE, FALSE, FALSE, FALSE, &u4BufLen);

				if (rStatus != WLAN_STATUS_SUCCESS) {
					DBGLOG(INIT, INFO,
					       ("wlanoidSetEncryptionStatus fail 0x%lx\n",
						rStatus));
					return -EFAULT;
				}

			} else {
				DBGLOG(INIT, INFO, ("key length %x\n", prIWEncExt->key_len));
				DBGLOG(INIT, INFO, ("key error\n"));
			}

			break;
		case IW_ENCODE_ALG_TKIP:
		case IW_ENCODE_ALG_CCMP:
#if CFG_SUPPORT_802_11W
		case IW_ENCODE_ALG_AES_CMAC:
#endif
			{

				/* KeyID */
				prKey->u4KeyIndex = (prEnc->flags & IW_ENCODE_INDEX) ?
				    (prEnc->flags & IW_ENCODE_INDEX) - 1 : 0;
#if CFG_SUPPORT_802_11W
				if (prKey->u4KeyIndex > 5)
#else
				if (prKey->u4KeyIndex > 3)
#endif
				{
					DBGLOG(INIT, INFO,
					       ("key index error:0x%lx\n", prKey->u4KeyIndex));
					/* key id is out of range */
					return -EINVAL;
				}

				/* bit(31) and bit(30) are shared by pKey and pRemoveKey */
				/* Tx Key Bit(31) */
				if (prIWEncExt->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
					prKey->u4KeyIndex |= 0x1UL << 31;
				}

				/* Pairwise Key Bit(30) */
				if (prIWEncExt->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
					/* group key */
				} else {
					/* pairwise key */
					prKey->u4KeyIndex |= 0x1UL << 30;
				}

			}
			/* Rx SC Bit(29) */
			if (prIWEncExt->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
				prKey->u4KeyIndex |= 0x1UL << 29;
				memcpy(&prKey->rKeyRSC, prIWEncExt->rx_seq, IW_ENCODE_SEQ_MAX_SIZE);
			}

			/* BSSID */
			memcpy(prKey->arBSSID, prIWEncExt->addr.sa_data, 6);

			/* switch tx/rx MIC key for sta */
			if (prIWEncExt->alg == IW_ENCODE_ALG_TKIP && prIWEncExt->key_len == 32) {
				memcpy(prKey->aucKeyMaterial, prIWEncExt->key, 16);
				memcpy(((PUINT_8) prKey->aucKeyMaterial) + 16, prIWEncExt->key + 24,
				       8);
				memcpy((prKey->aucKeyMaterial) + 24, prIWEncExt->key + 16, 8);
			} else {
				memcpy(prKey->aucKeyMaterial, prIWEncExt->key, prIWEncExt->key_len);
			}

			prKey->u4KeyLength = prIWEncExt->key_len;
			prKey->u4Length =
			    ((UINT_32) &(((P_PARAM_KEY_T) 0)->aucKeyMaterial)) +
			    prKey->u4KeyLength;

			rStatus = kalIoctl(prGlueInfo,
					   wlanoidSetAddKey,
					   prKey,
					   prKey->u4Length, FALSE, FALSE, TRUE, FALSE, &u4BufLen);

			if (rStatus != WLAN_STATUS_SUCCESS) {
				DBGLOG(INIT, INFO, ("add key error:%lx\n", rStatus));
				return -EFAULT;
			}
			break;
		}
	}

	return 0;
}				/* wext_set_encode_ext */

/*----------------------------------------------------------------------------*/
/*!
* \brief Set country code
*
* \param[in] prDev Net device requested.
* \param[in] prIwrInfo NULL.
* \param[in] pu4Mode Pointer to new operation mode.
* \param[in] pcExtra NULL.
*
* \retval 0 For success.
* \retval -EOPNOTSUPP If new mode is not supported.
*
* \note Device will run in new operation mode if it is valid.
*/
/*----------------------------------------------------------------------------*/
static int wext_set_country(IN struct net_device *prNetDev, IN struct iwreq *iwr)
{
	P_GLUE_INFO_T prGlueInfo;
	WLAN_STATUS rStatus;
	UINT_32 u4BufLen;
	UINT_8 aucCountry[2];

	ASSERT(prNetDev);

	/* iwr->u.data.pointer should be like "COUNTRY US", "COUNTRY EU"
	 * and "COUNTRY JP"
	 */
	if (FALSE == GLUE_CHK_PR2(prNetDev, iwr) || !iwr->u.data.pointer || iwr->u.data.length < 10) {
		return -EINVAL;
	}
	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));

	aucCountry[0] = *((PUINT_8) iwr->u.data.pointer + 8);
	aucCountry[1] = *((PUINT_8) iwr->u.data.pointer + 9);

	rStatus = kalIoctl(prGlueInfo,
			   wlanoidSetCountryCode,
			   &aucCountry[0], 2, FALSE, FALSE, TRUE, FALSE, &u4BufLen);

	return 0;
}

/*----------------------------------------------------------------------------*/
/*!
* \brief ioctl() (Linux Wireless Extensions) routines
*
* \param[in] prDev Net device requested.
* \param[in] ifr The ifreq structure for seeting the wireless extension.
* \param[in] i4Cmd The wireless extension ioctl command.
*
* \retval zero On success.
* \retval -EOPNOTSUPP If the cmd is not supported.
* \retval -EFAULT If copy_to_user goes wrong.
* \retval -EINVAL If any value's out of range.
*
* \note
*/
/*----------------------------------------------------------------------------*/
int wext_support_ioctl(IN struct net_device *prDev, IN struct ifreq *prIfReq, IN int i4Cmd)
{
	/* prIfReq is verified in the caller function wlanDoIOCTL() */
	struct iwreq *iwr = (struct iwreq *)prIfReq;
	struct iw_request_info rIwReqInfo;
	int ret = 0;
	char *prExtraBuf = NULL;
	UINT_32 u4ExtraSize = 0;

	/* prDev is verified in the caller function wlanDoIOCTL() */

	/* printk("%d CMD:0x%x\n", jiffies_to_msecs(jiffies), i4Cmd); */

	/* Prepare the call */
	rIwReqInfo.cmd = (__u16) i4Cmd;
	rIwReqInfo.flags = 0;

	switch (i4Cmd) {
	case SIOCGIWNAME:	/* 0x8B01, get wireless protocol name */
		ret = wext_get_name(prDev, &rIwReqInfo, (char *)&iwr->u.name, NULL);
		break;

		/* case SIOCSIWNWID: 0x8B02, deprecated */
		/* case SIOCGIWNWID: 0x8B03, deprecated */

	case SIOCSIWFREQ:	/* 0x8B04, set channel */
		ret = wext_set_freq(prDev, NULL, &iwr->u.freq, NULL);
		break;

	case SIOCGIWFREQ:	/* 0x8B05, get channel */
		ret = wext_get_freq(prDev, NULL, &iwr->u.freq, NULL);
		break;

	case SIOCSIWMODE:	/* 0x8B06, set operation mode */
		ret = wext_set_mode(prDev, NULL, &iwr->u.mode, NULL);
		/* ret = 0; */
		break;

	case SIOCGIWMODE:	/* 0x8B07, get operation mode */
		ret = wext_get_mode(prDev, NULL, &iwr->u.mode, NULL);
		break;

		/* case SIOCSIWSENS: 0x8B08, unsupported */
		/* case SIOCGIWSENS: 0x8B09, unsupported */

		/* case SIOCSIWRANGE: 0x8B0A, unused */
	case SIOCGIWRANGE:	/* 0x8B0B, get range of parameters */
		if (iwr->u.data.pointer != NULL) {
			/* Buffer size shoule be large enough */
			if (iwr->u.data.length < sizeof(struct iw_range)) {
				ret = -E2BIG;
				break;
			}

			prExtraBuf = kalMemAlloc(sizeof(struct iw_range), VIR_MEM_TYPE);
			if (!prExtraBuf) {
				ret = -ENOMEM;
				break;
			}

			/* reset all fields */
			memset(prExtraBuf, 0, sizeof(struct iw_range));
			iwr->u.data.length = sizeof(struct iw_range);

			ret = wext_get_range(prDev, NULL, &iwr->u.data, prExtraBuf);
			/* Push up to the caller */
			if (copy_to_user(iwr->u.data.pointer, prExtraBuf, iwr->u.data.length)) {
				ret = -EFAULT;
			}

			kalMemFree(prExtraBuf, VIR_MEM_TYPE, sizeof(struct iw_range));
			prExtraBuf = NULL;
		} else {
			ret = -EINVAL;
		}
		break;

	case SIOCSIWPRIV:	/* 0x8B0C, Country */
		ret = wext_set_country(prDev, iwr);
		break;

		/* case SIOCGIWPRIV: 0x8B0D, handled in wlan_do_ioctl() */
		/* caes SIOCSIWSTATS: 0x8B0E, unused */
		/* case SIOCGIWSTATS:
		   get statistics, intercepted by wireless_process_ioctl() in wireless.c,
		   redirected to dev_iwstats(), dev->get_wireless_stats().
		 */
		/* case SIOCSIWSPY: 0x8B10, unsupported */
		/* case SIOCGIWSPY: 0x8B11, unsupported */
		/* case SIOCSIWTHRSPY: 0x8B12, unsupported */
		/* case SIOCGIWTHRSPY: 0x8B13, unsupported */

	case SIOCSIWAP:	/* 0x8B14, set access point MAC addresses (BSSID) */
		if (iwr->u.ap_addr.sa_data[0] == 0 &&
		    iwr->u.ap_addr.sa_data[1] == 0 &&
		    iwr->u.ap_addr.sa_data[2] == 0 &&
		    iwr->u.ap_addr.sa_data[3] == 0 &&
		    iwr->u.ap_addr.sa_data[4] == 0 && iwr->u.ap_addr.sa_data[5] == 0) {
			/* WPA Supplicant will set 000000000000 in
			 ** wpa_driver_wext_deinit(), do nothing here or disassoc again?
			 */
			ret = 0;
			break;
		} else {
			ret = wext_set_ap(prDev, NULL, &iwr->u.ap_addr, NULL);
		}
		break;

	case SIOCGIWAP:	/* 0x8B15, get access point MAC addresses (BSSID) */
		ret = wext_get_ap(prDev, NULL, &iwr->u.ap_addr, NULL);
		break;

	case SIOCSIWMLME:	/* 0x8B16, request MLME operation */
		/* Fixed length structure */
		if (iwr->u.data.length != sizeof(struct iw_mlme)) {
			DBGLOG(INIT, INFO, ("MLME buffer strange:%d\n", iwr->u.data.length));
			ret = -EINVAL;
			break;
		}

		if (!iwr->u.data.pointer) {
			ret = -EINVAL;
			break;
		}

		prExtraBuf = kalMemAlloc(sizeof(struct iw_mlme), VIR_MEM_TYPE);
		if (!prExtraBuf) {
			ret = -ENOMEM;
			break;
		}

		if (copy_from_user(prExtraBuf, iwr->u.data.pointer, sizeof(struct iw_mlme))) {
			ret = -EFAULT;
		} else {
			ret = wext_set_mlme(prDev, NULL, &(iwr->u.data), prExtraBuf);
		}

		kalMemFree(prExtraBuf, VIR_MEM_TYPE, sizeof(struct iw_mlme));
		prExtraBuf = NULL;
		break;

		/* case SIOCGIWAPLIST: 0x8B17, deprecated */
	case SIOCSIWSCAN:	/* 0x8B18, scan request */
		if (iwr->u.data.pointer == NULL) {
			ret = wext_set_scan(prDev, NULL, NULL, NULL);
		}
#if WIRELESS_EXT > 17
		else if (iwr->u.data.length == sizeof(struct iw_scan_req)) {
			prExtraBuf = kalMemAlloc(MAX_SSID_LEN, VIR_MEM_TYPE);
			if (!prExtraBuf) {
				ret = -ENOMEM;
				break;
			}
			if (copy_from_user
			    (prExtraBuf, ((struct iw_scan_req *)(iwr->u.data.pointer))->essid,
			     ((struct iw_scan_req *)(iwr->u.data.pointer))->essid_len)) {
				ret = -EFAULT;
			} else {
				ret =
				    wext_set_scan(prDev, NULL, (union iwreq_data *)&(iwr->u.data),
						  prExtraBuf);
			}

			kalMemFree(prExtraBuf, VIR_MEM_TYPE, MAX_SSID_LEN);
			prExtraBuf = NULL;
		}
#endif
		else {
			ret = -EINVAL;
		}
		break;
#if 1
	case SIOCGIWSCAN:	/* 0x8B19, get scan results */
		if (!iwr->u.data.pointer || !iwr->u.essid.pointer) {
			ret = -EINVAL;
			break;
		}

		u4ExtraSize = iwr->u.data.length;
		/* allocate the same size of kernel buffer to store scan results. */
		prExtraBuf = kalMemAlloc(u4ExtraSize, VIR_MEM_TYPE);
		if (!prExtraBuf) {
			ret = -ENOMEM;
			break;
		}

		/* iwr->u.data.length may be updated by wext_get_scan() */
		ret = wext_get_scan(prDev, NULL, &iwr->u.data, prExtraBuf);
		if (ret != 0) {
			if (ret == -E2BIG) {
				DBGLOG(INIT, INFO, ("[wifi] wext_get_scan -E2BIG\n"));
			}
		} else {
			/* check updated length is valid */
			ASSERT(iwr->u.data.length <= u4ExtraSize);
			if (iwr->u.data.length > u4ExtraSize) {
				DBGLOG(INIT, INFO,
				       ("Updated result length is larger than allocated (%d > %ld)\n",
					iwr->u.data.length, u4ExtraSize));
				iwr->u.data.length = u4ExtraSize;
			}

			if (copy_to_user(iwr->u.data.pointer, prExtraBuf, iwr->u.data.length)) {
				ret = -EFAULT;
			}
		}

		kalMemFree(prExtraBuf, VIR_MEM_TYPE, u4ExtraSize);
		prExtraBuf = NULL;

		break;

#endif

#if 1
	case SIOCSIWESSID:	/* 0x8B1A, set SSID (network name) */
		if (iwr->u.essid.length > IW_ESSID_MAX_SIZE) {
			ret = -E2BIG;
			break;
		}
		if (!iwr->u.essid.pointer) {
			ret = -EINVAL;
			break;
		}

		prExtraBuf = kalMemAlloc(IW_ESSID_MAX_SIZE + 4, VIR_MEM_TYPE);
		if (!prExtraBuf) {
			ret = -ENOMEM;
			break;
		}

		if (copy_from_user(prExtraBuf, iwr->u.essid.pointer, iwr->u.essid.length)) {
			ret = -EFAULT;
		} else {
			/* Add trailing '\0' for printk */
			/* prExtraBuf[iwr->u.essid.length] = 0; */
			/* printk(KERN_INFO "wext_set_essid: %s (%d)\n", prExtraBuf, iwr->u.essid.length); */
			ret = wext_set_essid(prDev, NULL, &iwr->u.essid, prExtraBuf);
			/* printk ("set essid %d\n", ret); */
		}

		kalMemFree(prExtraBuf, VIR_MEM_TYPE, IW_ESSID_MAX_SIZE + 4);
		prExtraBuf = NULL;
		break;

#endif

	case SIOCGIWESSID:	/* 0x8B1B, get SSID */
		if (!iwr->u.essid.pointer) {
			ret = -EINVAL;
			break;
		}

		if (iwr->u.essid.length < IW_ESSID_MAX_SIZE) {
			DBGLOG(INIT, INFO, ("[wifi] iwr->u.essid.length:%d too small\n",
					    iwr->u.essid.length));
			ret = -E2BIG;	/* let caller try larger buffer */
			break;
		}

		prExtraBuf = kalMemAlloc(IW_ESSID_MAX_SIZE, VIR_MEM_TYPE);
		if (!prExtraBuf) {
			ret = -ENOMEM;
			break;
		}

		/* iwr->u.essid.length is updated by wext_get_essid() */

		ret = wext_get_essid(prDev, NULL, &iwr->u.essid, prExtraBuf);
		if (ret == 0) {
			if (copy_to_user(iwr->u.essid.pointer, prExtraBuf, iwr->u.essid.length)) {
				ret = -EFAULT;
			}
		}

		kalMemFree(prExtraBuf, VIR_MEM_TYPE, IW_ESSID_MAX_SIZE);
		prExtraBuf = NULL;

		break;

		/* case SIOCSIWNICKN: 0x8B1C, not supported */
		/* case SIOCGIWNICKN: 0x8B1D, not supported */

	case SIOCSIWRATE:	/* 0x8B20, set default bit rate (bps) */
		/* ret = wext_set_rate(prDev, &rIwReqInfo, &iwr->u.bitrate, NULL); */
		break;

	case SIOCGIWRATE:	/* 0x8B21, get current bit rate (bps) */
		ret = wext_get_rate(prDev, NULL, &iwr->u.bitrate, NULL);
		break;

	case SIOCSIWRTS:	/* 0x8B22, set rts/cts threshold */
		ret = wext_set_rts(prDev, NULL, &iwr->u.rts, NULL);
		break;

	case SIOCGIWRTS:	/* 0x8B23, get rts/cts threshold */
		ret = wext_get_rts(prDev, NULL, &iwr->u.rts, NULL);
		break;

		/* case SIOCSIWFRAG: 0x8B24, unsupported */
	case SIOCGIWFRAG:	/* 0x8B25, get frag threshold */
		ret = wext_get_frag(prDev, NULL, &iwr->u.frag, NULL);
		break;

	case SIOCSIWTXPOW:	/* 0x8B26, set relative tx power (in %) */
		ret = wext_set_txpow(prDev, NULL, &iwr->u.txpower, NULL);
		break;

	case SIOCGIWTXPOW:	/* 0x8B27, get relative tx power (in %) */
		ret = wext_get_txpow(prDev, NULL, &iwr->u.txpower, NULL);
		break;

		/* case SIOCSIWRETRY: 0x8B28, unsupported */
		/* case SIOCGIWRETRY: 0x8B29, unsupported */

#if 1
	case SIOCSIWENCODE:	/* 0x8B2A, set encoding token & mode */
		/* Only DISABLED case has NULL pointer and length == 0 */
		if (iwr->u.encoding.pointer) {
			if (iwr->u.encoding.length > 16) {
				ret = -E2BIG;
				break;
			}

			u4ExtraSize = iwr->u.encoding.length;
			prExtraBuf = kalMemAlloc(u4ExtraSize, VIR_MEM_TYPE);
			if (!prExtraBuf) {
				ret = -ENOMEM;
				break;
			}

			if (copy_from_user(prExtraBuf,
					   iwr->u.encoding.pointer, iwr->u.encoding.length)) {
				ret = -EFAULT;
			}
		} else if (iwr->u.encoding.length != 0) {
			ret = -EINVAL;
			break;
		}

		if (ret == 0) {
			ret = wext_set_encode(prDev, NULL, &iwr->u.encoding, prExtraBuf);
		}

		if (prExtraBuf) {
			kalMemFree(prExtraBuf, VIR_MEM_TYPE, u4ExtraSize);
			prExtraBuf = NULL;
		}
		break;

	case SIOCGIWENCODE:	/* 0x8B2B, get encoding token & mode */
		/* check pointer */
		ret = wext_get_encode(prDev, NULL, &iwr->u.encoding, NULL);
		break;

	case SIOCSIWPOWER:	/* 0x8B2C, set power management */
		ret = wext_set_power(prDev, NULL, &iwr->u.power, NULL);
		break;

	case SIOCGIWPOWER:	/* 0x8B2D, get power management */
		ret = wext_get_power(prDev, NULL, &iwr->u.power, NULL);
		break;

#if WIRELESS_EXT > 17
	case SIOCSIWGENIE:	/* 0x8B30, set gen ie */
		if (iwr->u.data.pointer) {
			P_GLUE_INFO_T prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prDev));
			if (1 /* wlanQueryWapiMode(prGlueInfo->prAdapter) */) {
				/* Fixed length structure */
#if CFG_SUPPORT_WAPI
				if (iwr->u.data.length > 42 /* The max wapi ie buffer */) {
					ret = -EINVAL;
					break;
				}
#endif
				u4ExtraSize = iwr->u.data.length;
				if (u4ExtraSize) {
					prExtraBuf = kalMemAlloc(u4ExtraSize, VIR_MEM_TYPE);
					if (!prExtraBuf) {
						ret = -ENOMEM;
						break;
					}
					if (copy_from_user(prExtraBuf,
							   iwr->u.data.pointer,
							   iwr->u.data.length)) {
						ret = -EFAULT;
					} else {
						WLAN_STATUS rStatus;
						UINT_32 u4BufLen;
#if CFG_SUPPORT_WAPI
						rStatus = kalIoctl(prGlueInfo,
								   wlanoidSetWapiAssocInfo,
								   prExtraBuf,
								   u4ExtraSize,
								   FALSE,
								   FALSE, TRUE, FALSE, &u4BufLen);

						if (rStatus != WLAN_STATUS_SUCCESS) {
							/* printk(KERN_INFO "[wapi] set wapi assoc info error:%lx\n", rStatus); */
#endif
#if CFG_SUPPORT_WPS2
							PUINT_8 prDesiredIE = NULL;
							if (wextSrchDesiredWPSIE(prExtraBuf,
										 u4ExtraSize,
										 0xDD,
										 (PUINT_8 *) &
										 prDesiredIE)) {
								rStatus =
								    kalIoctl(prGlueInfo,
									     wlanoidSetWSCAssocInfo,
									     prDesiredIE,
									     IE_SIZE(prDesiredIE),
									     FALSE, FALSE, TRUE,
									     FALSE, &u4BufLen);
								if (rStatus != WLAN_STATUS_SUCCESS) {
									/* printk(KERN_INFO "[WSC] set WSC assoc info error:%lx\n", rStatus); */
								}
							}
#endif
#if CFG_SUPPORT_WAPI
						}
#endif
					}
					kalMemFree(prExtraBuf, VIR_MEM_TYPE, u4ExtraSize);
					prExtraBuf = NULL;
				}
			}
		}
		break;

	case SIOCGIWGENIE:	/* 0x8B31, get gen ie, unused */
		break;

#endif

	case SIOCSIWAUTH:	/* 0x8B32, set auth mode params */
		ret = wext_set_auth(prDev, NULL, &iwr->u.param, NULL);
		break;

		/* case SIOCGIWAUTH: 0x8B33, unused? */
	case SIOCSIWENCODEEXT:	/* 0x8B34, set extended encoding token & mode */
		if (iwr->u.encoding.pointer) {
			u4ExtraSize = iwr->u.encoding.length;
			prExtraBuf = kalMemAlloc(u4ExtraSize, VIR_MEM_TYPE);
			if (!prExtraBuf) {
				ret = -ENOMEM;
				break;
			}

			if (copy_from_user(prExtraBuf,
					   iwr->u.encoding.pointer, iwr->u.encoding.length)) {
				ret = -EFAULT;
			}
		} else if (iwr->u.encoding.length != 0) {
			ret = -EINVAL;
			break;
		}

		if (ret == 0) {
			ret = wext_set_encode_ext(prDev, NULL, &iwr->u.encoding, prExtraBuf);
		}

		if (prExtraBuf) {
			kalMemFree(prExtraBuf, VIR_MEM_TYPE, u4ExtraSize);
			prExtraBuf = NULL;
		}
		break;

		/* case SIOCGIWENCODEEXT: 0x8B35, unused? */

	case SIOCSIWPMKSA:	/* 0x8B36, pmksa cache operation */
#if 1
		if (iwr->u.data.pointer) {
			/* Fixed length structure */
			if (iwr->u.data.length != sizeof(struct iw_pmksa)) {
				ret = -EINVAL;
				break;
			}

			u4ExtraSize = sizeof(struct iw_pmksa);
			prExtraBuf = kalMemAlloc(u4ExtraSize, VIR_MEM_TYPE);
			if (!prExtraBuf) {
				ret = -ENOMEM;
				break;
			}

			if (copy_from_user(prExtraBuf,
					   iwr->u.data.pointer, sizeof(struct iw_pmksa))) {
				ret = -EFAULT;
			} else {
				switch (((struct iw_pmksa *)prExtraBuf)->cmd) {
				case IW_PMKSA_ADD:
					/*
					   printk(KERN_INFO "IW_PMKSA_ADD [" MACSTR "]\n",
					   MAC2STR(((struct iw_pmksa *)pExtraBuf)->bssid.sa_data));
					 */
					{
						P_GLUE_INFO_T prGlueInfo =
						    *((P_GLUE_INFO_T *) netdev_priv(prDev));
						WLAN_STATUS rStatus;
						UINT_32 u4BufLen;
						P_PARAM_PMKID_T prPmkid;

						prPmkid =
						    (P_PARAM_PMKID_T) kalMemAlloc(8 +
										  sizeof
										  (PARAM_BSSID_INFO_T),
										  VIR_MEM_TYPE);
						if (!prPmkid) {
							DBGLOG(INIT, INFO,
							       ("Can not alloc memory for IW_PMKSA_ADD\n"));
							ret = -ENOMEM;
							break;
						}

						prPmkid->u4Length = 8 + sizeof(PARAM_BSSID_INFO_T);
						prPmkid->u4BSSIDInfoCount = 1;
						kalMemCopy(prPmkid->arBSSIDInfo->arBSSID,
							   ((struct iw_pmksa *)prExtraBuf)->bssid.
							   sa_data, 6);
						kalMemCopy(prPmkid->arBSSIDInfo->arPMKID,
							   ((struct iw_pmksa *)prExtraBuf)->pmkid,
							   IW_PMKID_LEN);

						rStatus = kalIoctl(prGlueInfo,
								   wlanoidSetPmkid,
								   prPmkid,
								   sizeof(PARAM_PMKID_T),
								   FALSE,
								   FALSE, TRUE, FALSE, &u4BufLen);

						if (rStatus != WLAN_STATUS_SUCCESS) {
							DBGLOG(INIT, INFO,
							       ("add pmkid error:%lx\n", rStatus));
						}
						kalMemFree(prPmkid, VIR_MEM_TYPE,
							   8 + sizeof(PARAM_BSSID_INFO_T));
					}
					break;
				case IW_PMKSA_REMOVE:
					/*
					   printk(KERN_INFO "IW_PMKSA_REMOVE [" MACSTR "]\n",
					   MAC2STR(((struct iw_pmksa *)buf)->bssid.sa_data));
					 */
					break;
				case IW_PMKSA_FLUSH:
					/*
					   printk(KERN_INFO "IW_PMKSA_FLUSH\n");
					 */
					{
						P_GLUE_INFO_T prGlueInfo =
						    *((P_GLUE_INFO_T *) netdev_priv(prDev));
						WLAN_STATUS rStatus;
						UINT_32 u4BufLen;
						P_PARAM_PMKID_T prPmkid;

						prPmkid =
						    (P_PARAM_PMKID_T) kalMemAlloc(8, VIR_MEM_TYPE);
						if (!prPmkid) {
							DBGLOG(INIT, INFO,
							       ("Can not alloc memory for IW_PMKSA_FLUSH\n"));
							ret = -ENOMEM;
							break;
						}

						prPmkid->u4Length = 8;
						prPmkid->u4BSSIDInfoCount = 0;

						rStatus = kalIoctl(prGlueInfo,
								   wlanoidSetPmkid,
								   prPmkid,
								   sizeof(PARAM_PMKID_T),
								   FALSE,
								   FALSE, TRUE, FALSE, &u4BufLen);

						if (rStatus != WLAN_STATUS_SUCCESS) {
							DBGLOG(INIT, INFO,
							       ("flush pmkid error:%lx\n",
								rStatus));
						}
						kalMemFree(prPmkid, VIR_MEM_TYPE, 8);
					}
					break;
				default:
					DBGLOG(INIT, INFO, ("UNKNOWN iw_pmksa command:%d\n",
							    ((struct iw_pmksa *)prExtraBuf)->cmd));
					ret = -EFAULT;
					break;
				}
			}

			if (prExtraBuf) {
				kalMemFree(prExtraBuf, VIR_MEM_TYPE, u4ExtraSize);
				prExtraBuf = NULL;
			}
		} else if (iwr->u.data.length != 0) {
			ret = -EINVAL;
			break;
		}
#endif
		break;

#endif

	default:
		/* printk(KERN_NOTICE "unsupported IOCTL: 0x%x\n", i4Cmd); */
		ret = -EOPNOTSUPP;
		break;
	}

	/* printk("%ld CMD:0x%x ret:%d\n", jiffies_to_msecs(jiffies), i4Cmd, ret); */

	return ret;
}				/* wext_support_ioctl */

/*----------------------------------------------------------------------------*/
/*!
* \brief To send an event (RAW socket pacekt) to user process actively.
*
* \param[in] prGlueInfo Glue layer info.
* \param[in] u4cmd Whcih event command we want to indicate to user process.
* \param[in] pData Data buffer to be indicated.
* \param[in] dataLen Available data size in pData.
*
* \return (none)
*
* \note Event is indicated to upper layer if cmd is supported and data is valid.
*       Using of kernel symbol wireless_send_event(), which is defined in
*      <net/iw_handler.h> after WE-14 (2.4.20).
*/
/*----------------------------------------------------------------------------*/
void
wext_indicate_wext_event(IN P_GLUE_INFO_T prGlueInfo,
			 IN unsigned int u4Cmd,
			 IN unsigned char *pucData, IN unsigned int u4dataLen)
{
	union iwreq_data wrqu;
	unsigned char *pucExtraInfo = NULL;
#if WIRELESS_EXT >= 15
	unsigned char *pucDesiredIE = NULL;
	unsigned char aucExtraInfoBuf[200];
#endif
#if WIRELESS_EXT < 18
	int i;
#endif

	memset(&wrqu, 0, sizeof(wrqu));

	switch (u4Cmd) {
	case SIOCGIWTXPOW:
		memcpy(&wrqu.power, pucData, u4dataLen);
		break;
	case SIOCGIWSCAN:
		complete_all(&prGlueInfo->rScanComp);
		break;

	case SIOCGIWAP:
		if (pucData) {
			memcpy(&wrqu.ap_addr.sa_data, pucData, ETH_ALEN);
		} else {
			memset(&wrqu.ap_addr.sa_data, 0, ETH_ALEN);
		}
		break;

	case IWEVASSOCREQIE:
#if WIRELESS_EXT < 15
		/* under WE-15, no suitable Event can be used */
		goto skip_indicate_event;
#else
		/* do supplicant a favor, parse to the start of WPA/RSN IE */
		if (wextSrchDesiredWPAIE(pucData, u4dataLen, 0x30, &pucDesiredIE)) {
			/* RSN IE found */
		}
#if 0
		else if (wextSrchDesiredWPSIE(pucData, u4dataLen, 0xDD, &pucDesiredIE)) {
			/* WPS IE found */
		}
#endif
		else if (wextSrchDesiredWPAIE(pucData, u4dataLen, 0xDD, &pucDesiredIE)) {
			/* WPA IE found */
		}
#if CFG_SUPPORT_WAPI		/* Android+ */
		else if (wextSrchDesiredWAPIIE(pucData, u4dataLen, &pucDesiredIE)) {
			/* printk("wextSrchDesiredWAPIIE!!\n"); */
			/* WAPI IE found */
		}
#endif
		else {
			/* no WPA/RSN IE found, skip this event */
			goto skip_indicate_event;
		}

#if WIRELESS_EXT < 18
		/* under WE-18, only IWEVCUSTOM can be used */
		u4Cmd = IWEVCUSTOM;
		pucExtraInfo = aucExtraInfoBuf;
		pucExtraInfo += sprintf(pucExtraInfo, "ASSOCINFO(ReqIEs=");
		/* printk(KERN_DEBUG "assoc info buffer size needed:%d\n", infoElemLen * 2 + 17); */
		/* translate binary string to hex string, requirement of IWEVCUSTOM */
		for (i = 0; i < pucDesiredIE[1] + 2; ++i) {
			pucExtraInfo += sprintf(pucExtraInfo, "%02x", pucDesiredIE[i]);
		}
		pucExtraInfo = aucExtraInfoBuf;
		wrqu.data.length = 17 + (pucDesiredIE[1] + 2) * 2;
#else
		/* IWEVASSOCREQIE, indicate binary string */
		pucExtraInfo = pucDesiredIE;
		wrqu.data.length = pucDesiredIE[1] + 2;
#endif
#endif				/* WIRELESS_EXT < 15 */
		break;

	case IWEVMICHAELMICFAILURE:
#if WIRELESS_EXT < 15
		/* under WE-15, no suitable Event can be used */
		goto skip_indicate_event;
#else
		if (pucData) {
			P_PARAM_AUTH_REQUEST_T pAuthReq = (P_PARAM_AUTH_REQUEST_T) pucData;
			/* under WE-18, only IWEVCUSTOM can be used */
			u4Cmd = IWEVCUSTOM;
			pucExtraInfo = aucExtraInfoBuf;
			pucExtraInfo += sprintf(pucExtraInfo, "MLME-MICHAELMICFAILURE.indication ");
			pucExtraInfo += sprintf(pucExtraInfo,
						"%s",
						(pAuthReq->u4Flags ==
						 PARAM_AUTH_REQUEST_GROUP_ERROR) ? "groupcast " :
						"unicast ");

			wrqu.data.length = pucExtraInfo - aucExtraInfoBuf;
			pucExtraInfo = aucExtraInfoBuf;
		}
#endif				/* WIRELESS_EXT < 15 */
		break;

	case IWEVPMKIDCAND:
		if (prGlueInfo->rWpaInfo.u4WpaVersion == IW_AUTH_WPA_VERSION_WPA2 &&
		    prGlueInfo->rWpaInfo.u4KeyMgmt == IW_AUTH_KEY_MGMT_802_1X) {

			/* only used in WPA2 */
#if WIRELESS_EXT >= 18
			P_PARAM_PMKID_CANDIDATE_T prPmkidCand = (P_PARAM_PMKID_CANDIDATE_T) pucData;

			struct iw_pmkid_cand rPmkidCand;
			pucExtraInfo = aucExtraInfoBuf;

			rPmkidCand.flags = prPmkidCand->u4Flags;
			rPmkidCand.index = 0;
			kalMemCopy(rPmkidCand.bssid.sa_data, prPmkidCand->arBSSID, 6);

			kalMemCopy(pucExtraInfo, (PUINT_8) &rPmkidCand,
				   sizeof(struct iw_pmkid_cand));
			wrqu.data.length = sizeof(struct iw_pmkid_cand);

			/* pmkid canadidate list is supported after WE-18 */
			/* indicate struct iw_pmkid_cand */
#else
			/* printk(KERN_INFO "IWEVPMKIDCAND event skipped, WE < 18\n"); */
			goto skip_indicate_event;
#endif
		} else {
			/* printk(KERN_INFO "IWEVPMKIDCAND event skipped, NOT WPA2\n"); */
			goto skip_indicate_event;
		}
		break;

	case IWEVCUSTOM:
		u4Cmd = IWEVCUSTOM;
		pucExtraInfo = aucExtraInfoBuf;
		kalMemCopy(pucExtraInfo, pucData, sizeof(PTA_IPC_T));
		wrqu.data.length = sizeof(PTA_IPC_T);
		break;

	default:
		/* printk(KERN_INFO "Unsupported wext event:%x\n", cmd); */
		goto skip_indicate_event;
	}

	/* Send event to user space */
	wireless_send_event(prGlueInfo->prDevHandler, u4Cmd, &wrqu, pucExtraInfo);

 skip_indicate_event:
	return;
}				/* wext_indicate_wext_event */

/*----------------------------------------------------------------------------*/
/*!
* \brief A method of struct net_device, to get the network interface statistical
*        information.
*
* Whenever an application needs to get statistics for the interface, this method
* is called. This happens, for example, when ifconfig or netstat -i is run.
*
* \param[in] pDev Pointer to struct net_device.
*
* \return net_device_stats buffer pointer.
*
*/
/*----------------------------------------------------------------------------*/
struct iw_statistics *wext_get_wireless_stats(struct net_device *prDev)
{

	WLAN_STATUS rStatus = WLAN_STATUS_FAILURE;
	P_GLUE_INFO_T prGlueInfo = NULL;
	struct iw_statistics *pStats = NULL;
	INT_32 i4Rssi;
	UINT_32 bufLen = 0;

	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prDev));
	ASSERT(prGlueInfo);
	if (!prGlueInfo) {
		goto stat_out;
	}

	pStats = (struct iw_statistics *)(&(prGlueInfo->rIwStats));

	if (!prDev || !netif_carrier_ok(prDev)) {
		/* network not connected */
		goto stat_out;
	}

	rStatus = kalIoctl(prGlueInfo,
			   wlanoidQueryRssi,
			   &i4Rssi, sizeof(i4Rssi), TRUE, TRUE, TRUE, FALSE, &bufLen);

 stat_out:
	return pStats;
}				/* wlan_get_wireless_stats */

/*----------------------------------------------------------------------------*/
/*!
* \brief To report the private supported IOCTLs table to user space.
*
* \param[in] prNetDev Net device requested.
* \param[out] prIfReq Pointer to ifreq structure, content is copied back to
*                  user space buffer in gl_iwpriv_table.
*
* \retval 0 For success.
* \retval -E2BIG For user's buffer size is too small.
* \retval -EFAULT For fail.
*
*/
/*----------------------------------------------------------------------------*/
int wext_get_priv(IN struct net_device *prNetDev, IN struct ifreq *prIfReq)
{
	/* prIfReq is verified in the caller function wlanDoIOCTL() */
	struct iwreq *prIwReq = (struct iwreq *)prIfReq;
	struct iw_point *prData = (struct iw_point *)&prIwReq->u.data;
	UINT_16 u2BufferSize = 0;

	u2BufferSize = prData->length;

	/* update our private table size */
	prData->length = (__u16) sizeof(rIwPrivTable) / sizeof(struct iw_priv_args);

	if (u2BufferSize < prData->length) {
		return -E2BIG;
	}

	if (prData->length) {
		if (copy_to_user(prData->pointer, rIwPrivTable, sizeof(rIwPrivTable))) {
			return -EFAULT;
		}
	}

	return 0;
}				/* wext_get_priv */

#endif				/* WIRELESS_EXT */