xref: /OK3568_Linux_fs/external/rkwifibt/drivers/infineon/wl_iw.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Linux Wireless Extensions support
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Portions of this code are copyright (c) 2021 Cypress Semiconductor Corporation
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Copyright (C) 1999-2017, Broadcom Corporation
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  *      Unless you and Broadcom execute a separate written software license
9*4882a593Smuzhiyun  * agreement governing use of this software, this software is licensed to you
10*4882a593Smuzhiyun  * under the terms of the GNU General Public License version 2 (the "GPL"),
11*4882a593Smuzhiyun  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
12*4882a593Smuzhiyun  * following added to such license:
13*4882a593Smuzhiyun  *
14*4882a593Smuzhiyun  *      As a special exception, the copyright holders of this software give you
15*4882a593Smuzhiyun  * permission to link this software with independent modules, and to copy and
16*4882a593Smuzhiyun  * distribute the resulting executable under terms of your choice, provided that
17*4882a593Smuzhiyun  * you also meet, for each linked independent module, the terms and conditions of
18*4882a593Smuzhiyun  * the license of that module.  An independent module is a module which is not
19*4882a593Smuzhiyun  * derived from this software.  The special exception does not apply to any
20*4882a593Smuzhiyun  * modifications of the software.
21*4882a593Smuzhiyun  *
22*4882a593Smuzhiyun  *      Notwithstanding the above, under no circumstances may you combine this
23*4882a593Smuzhiyun  * software in any way with any other Broadcom software provided under a license
24*4882a593Smuzhiyun  * other than the GPL, without Broadcom's express prior written consent.
25*4882a593Smuzhiyun  *
26*4882a593Smuzhiyun  *
27*4882a593Smuzhiyun  * <<Broadcom-WL-IPTag/Open:>>
28*4882a593Smuzhiyun  *
29*4882a593Smuzhiyun  * $Id: wl_iw.c 693575 2017-04-04 06:45:00Z $
30*4882a593Smuzhiyun  */
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun #if defined(USE_IW)
33*4882a593Smuzhiyun #define LINUX_PORT
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun #include <typedefs.h>
36*4882a593Smuzhiyun #include <linuxver.h>
37*4882a593Smuzhiyun #include <osl.h>
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun #include <bcmutils.h>
40*4882a593Smuzhiyun #include <bcmendian.h>
41*4882a593Smuzhiyun #include <ethernet.h>
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun #include <linux/if_arp.h>
44*4882a593Smuzhiyun #include <asm/uaccess.h>
45*4882a593Smuzhiyun #include <linux/signal.h>
46*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0))
47*4882a593Smuzhiyun #include <linux/sched/signal.h>
48*4882a593Smuzhiyun #endif // endif
49*4882a593Smuzhiyun #include <wlioctl.h>
50*4882a593Smuzhiyun #include <wlioctl_utils.h>
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun typedef const struct si_pub	si_t;
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun #include <wl_dbg.h>
55*4882a593Smuzhiyun #include <wl_iw.h>
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun #ifdef BCMWAPI_WPI
58*4882a593Smuzhiyun /* these items should evetually go into wireless.h of the linux system headfile dir */
59*4882a593Smuzhiyun #ifndef IW_ENCODE_ALG_SM4
60*4882a593Smuzhiyun #define IW_ENCODE_ALG_SM4 0x20
61*4882a593Smuzhiyun #endif // endif
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun #ifndef IW_AUTH_WAPI_ENABLED
64*4882a593Smuzhiyun #define IW_AUTH_WAPI_ENABLED 0x20
65*4882a593Smuzhiyun #endif // endif
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun #ifndef IW_AUTH_WAPI_VERSION_1
68*4882a593Smuzhiyun #define IW_AUTH_WAPI_VERSION_1	0x00000008
69*4882a593Smuzhiyun #endif // endif
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun #ifndef IW_AUTH_CIPHER_SMS4
72*4882a593Smuzhiyun #define IW_AUTH_CIPHER_SMS4	0x00000020
73*4882a593Smuzhiyun #endif // endif
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun #ifndef IW_AUTH_KEY_MGMT_WAPI_PSK
76*4882a593Smuzhiyun #define IW_AUTH_KEY_MGMT_WAPI_PSK 4
77*4882a593Smuzhiyun #endif // endif
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun #ifndef IW_AUTH_KEY_MGMT_WAPI_CERT
80*4882a593Smuzhiyun #define IW_AUTH_KEY_MGMT_WAPI_CERT 8
81*4882a593Smuzhiyun #endif // endif
82*4882a593Smuzhiyun #endif /* BCMWAPI_WPI */
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun /* Broadcom extensions to WEXT, linux upstream has obsoleted WEXT */
85*4882a593Smuzhiyun #ifndef IW_AUTH_KEY_MGMT_FT_802_1X
86*4882a593Smuzhiyun #define IW_AUTH_KEY_MGMT_FT_802_1X 0x04
87*4882a593Smuzhiyun #endif // endif
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun #ifndef IW_AUTH_KEY_MGMT_FT_PSK
90*4882a593Smuzhiyun #define IW_AUTH_KEY_MGMT_FT_PSK 0x08
91*4882a593Smuzhiyun #endif // endif
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun #ifndef IW_ENC_CAPA_FW_ROAM_ENABLE
94*4882a593Smuzhiyun #define IW_ENC_CAPA_FW_ROAM_ENABLE	0x00000020
95*4882a593Smuzhiyun #endif // endif
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun /* FC9: wireless.h 2.6.25-14.fc9.i686 is missing these, even though WIRELESS_EXT is set to latest
98*4882a593Smuzhiyun  * version 22.
99*4882a593Smuzhiyun  */
100*4882a593Smuzhiyun #ifndef IW_ENCODE_ALG_PMK
101*4882a593Smuzhiyun #define IW_ENCODE_ALG_PMK 4
102*4882a593Smuzhiyun #endif // endif
103*4882a593Smuzhiyun #ifndef IW_ENC_CAPA_4WAY_HANDSHAKE
104*4882a593Smuzhiyun #define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010
105*4882a593Smuzhiyun #endif // endif
106*4882a593Smuzhiyun /* End FC9. */
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
109*4882a593Smuzhiyun #include <linux/rtnetlink.h>
110*4882a593Smuzhiyun #endif // endif
111*4882a593Smuzhiyun #if defined(SOFTAP)
112*4882a593Smuzhiyun struct net_device *ap_net_dev = NULL;
113*4882a593Smuzhiyun tsk_ctl_t ap_eth_ctl;  /* apsta AP netdev waiter thread */
114*4882a593Smuzhiyun #endif /* SOFTAP */
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun extern bool wl_iw_conn_status_str(uint32 event_type, uint32 status,
117*4882a593Smuzhiyun 	uint32 reason, char* stringBuf, uint buflen);
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun uint wl_msg_level = WL_ERROR_VAL;
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun #define MAX_WLIW_IOCTL_LEN WLC_IOCTL_MEDLEN
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun /* IOCTL swapping mode for Big Endian host with Little Endian dongle.  Default to off */
124*4882a593Smuzhiyun #define htod32(i) (i)
125*4882a593Smuzhiyun #define htod16(i) (i)
126*4882a593Smuzhiyun #define dtoh32(i) (i)
127*4882a593Smuzhiyun #define dtoh16(i) (i)
128*4882a593Smuzhiyun #define htodchanspec(i) (i)
129*4882a593Smuzhiyun #define dtohchanspec(i) (i)
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun extern struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev);
132*4882a593Smuzhiyun extern int dhd_wait_pend8021x(struct net_device *dev);
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun #if WIRELESS_EXT < 19
135*4882a593Smuzhiyun #define IW_IOCTL_IDX(cmd)	((cmd) - SIOCIWFIRST)
136*4882a593Smuzhiyun #define IW_EVENT_IDX(cmd)	((cmd) - IWEVFIRST)
137*4882a593Smuzhiyun #endif /* WIRELESS_EXT < 19 */
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
140*4882a593Smuzhiyun #define DAEMONIZE(a)	do { \
141*4882a593Smuzhiyun 		allow_signal(SIGKILL);	\
142*4882a593Smuzhiyun 		allow_signal(SIGTERM);	\
143*4882a593Smuzhiyun 	} while (0)
144*4882a593Smuzhiyun #elif ((LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)) && \
145*4882a593Smuzhiyun 	(LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)))
146*4882a593Smuzhiyun #define DAEMONIZE(a) daemonize(a); \
147*4882a593Smuzhiyun 	allow_signal(SIGKILL); \
148*4882a593Smuzhiyun 	allow_signal(SIGTERM);
149*4882a593Smuzhiyun #else /* Linux 2.4 (w/o preemption patch) */
150*4882a593Smuzhiyun #define RAISE_RX_SOFTIRQ() \
151*4882a593Smuzhiyun 	cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ)
152*4882a593Smuzhiyun #define DAEMONIZE(a) daemonize(); \
153*4882a593Smuzhiyun 	do { if (a) \
154*4882a593Smuzhiyun 		strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \
155*4882a593Smuzhiyun 	} while (0);
156*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE  */
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun #define ISCAN_STATE_IDLE   0
159*4882a593Smuzhiyun #define ISCAN_STATE_SCANING 1
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun /* the buf lengh can be WLC_IOCTL_MAXLEN (8K) to reduce iteration */
162*4882a593Smuzhiyun #define WLC_IW_ISCAN_MAXLEN   2048
163*4882a593Smuzhiyun typedef struct iscan_buf {
164*4882a593Smuzhiyun 	struct iscan_buf * next;
165*4882a593Smuzhiyun 	char   iscan_buf[WLC_IW_ISCAN_MAXLEN];
166*4882a593Smuzhiyun } iscan_buf_t;
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun typedef struct iscan_info {
169*4882a593Smuzhiyun 	struct net_device *dev;
170*4882a593Smuzhiyun 	timer_list_compat_t timer;
171*4882a593Smuzhiyun 	uint32 timer_ms;
172*4882a593Smuzhiyun 	uint32 timer_on;
173*4882a593Smuzhiyun 	int    iscan_state;
174*4882a593Smuzhiyun 	iscan_buf_t * list_hdr;
175*4882a593Smuzhiyun 	iscan_buf_t * list_cur;
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	/* Thread to work on iscan */
178*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
179*4882a593Smuzhiyun 	struct task_struct *kthread;
180*4882a593Smuzhiyun #endif // endif
181*4882a593Smuzhiyun 	long sysioc_pid;
182*4882a593Smuzhiyun 	struct semaphore sysioc_sem;
183*4882a593Smuzhiyun 	struct completion sysioc_exited;
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	char ioctlbuf[WLC_IOCTL_SMLEN];
186*4882a593Smuzhiyun } iscan_info_t;
187*4882a593Smuzhiyun iscan_info_t *g_iscan = NULL;
188*4882a593Smuzhiyun static void wl_iw_timerfunc(ulong data);
189*4882a593Smuzhiyun static void wl_iw_set_event_mask(struct net_device *dev);
190*4882a593Smuzhiyun static int wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, uint16 action);
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun /* priv_link becomes netdev->priv and is the link between netdev and wlif struct */
193*4882a593Smuzhiyun typedef struct priv_link {
194*4882a593Smuzhiyun 	wl_iw_t *wliw;
195*4882a593Smuzhiyun } priv_link_t;
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun /* dev to priv_link */
198*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24))
199*4882a593Smuzhiyun #define WL_DEV_LINK(dev)       (priv_link_t*)(dev->priv)
200*4882a593Smuzhiyun #else
201*4882a593Smuzhiyun #define WL_DEV_LINK(dev)       (priv_link_t*)netdev_priv(dev)
202*4882a593Smuzhiyun #endif // endif
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun /* dev to wl_iw_t */
205*4882a593Smuzhiyun #define IW_DEV_IF(dev)          ((wl_iw_t*)(WL_DEV_LINK(dev))->wliw)
206*4882a593Smuzhiyun 
swap_key_from_BE(wl_wsec_key_t * key)207*4882a593Smuzhiyun static void swap_key_from_BE(
208*4882a593Smuzhiyun 	        wl_wsec_key_t *key
209*4882a593Smuzhiyun )
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun 	key->index = htod32(key->index);
212*4882a593Smuzhiyun 	key->len = htod32(key->len);
213*4882a593Smuzhiyun 	key->algo = htod32(key->algo);
214*4882a593Smuzhiyun 	key->flags = htod32(key->flags);
215*4882a593Smuzhiyun 	key->rxiv.hi = htod32(key->rxiv.hi);
216*4882a593Smuzhiyun 	key->rxiv.lo = htod16(key->rxiv.lo);
217*4882a593Smuzhiyun 	key->iv_initialized = htod32(key->iv_initialized);
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun 
swap_key_to_BE(wl_wsec_key_t * key)220*4882a593Smuzhiyun static void swap_key_to_BE(
221*4882a593Smuzhiyun 	        wl_wsec_key_t *key
222*4882a593Smuzhiyun )
223*4882a593Smuzhiyun {
224*4882a593Smuzhiyun 	key->index = dtoh32(key->index);
225*4882a593Smuzhiyun 	key->len = dtoh32(key->len);
226*4882a593Smuzhiyun 	key->algo = dtoh32(key->algo);
227*4882a593Smuzhiyun 	key->flags = dtoh32(key->flags);
228*4882a593Smuzhiyun 	key->rxiv.hi = dtoh32(key->rxiv.hi);
229*4882a593Smuzhiyun 	key->rxiv.lo = dtoh16(key->rxiv.lo);
230*4882a593Smuzhiyun 	key->iv_initialized = dtoh32(key->iv_initialized);
231*4882a593Smuzhiyun }
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun static int
dev_wlc_ioctl(struct net_device * dev,int cmd,void * arg,int len)234*4882a593Smuzhiyun dev_wlc_ioctl(
235*4882a593Smuzhiyun 	struct net_device *dev,
236*4882a593Smuzhiyun 	int cmd,
237*4882a593Smuzhiyun 	void *arg,
238*4882a593Smuzhiyun 	int len
239*4882a593Smuzhiyun )
240*4882a593Smuzhiyun {
241*4882a593Smuzhiyun 	struct ifreq ifr;
242*4882a593Smuzhiyun 	wl_ioctl_t ioc;
243*4882a593Smuzhiyun 	mm_segment_t fs;
244*4882a593Smuzhiyun 	int ret;
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	memset(&ioc, 0, sizeof(ioc));
247*4882a593Smuzhiyun 	ioc.cmd = cmd;
248*4882a593Smuzhiyun 	ioc.buf = arg;
249*4882a593Smuzhiyun 	ioc.len = len;
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	strncpy(ifr.ifr_name, dev->name, sizeof(ifr.ifr_name));
252*4882a593Smuzhiyun 	ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
253*4882a593Smuzhiyun 	ifr.ifr_data = (caddr_t) &ioc;
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	fs = get_fs();
256*4882a593Smuzhiyun #if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 0, 21)
257*4882a593Smuzhiyun 	set_fs(get_ds());
258*4882a593Smuzhiyun #endif // endif
259*4882a593Smuzhiyun #if defined(WL_USE_NETDEV_OPS)
260*4882a593Smuzhiyun 	ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, SIOCDEVPRIVATE);
261*4882a593Smuzhiyun #else
262*4882a593Smuzhiyun 	ret = dev->do_ioctl(dev, &ifr, SIOCDEVPRIVATE);
263*4882a593Smuzhiyun #endif // endif
264*4882a593Smuzhiyun 	set_fs(fs);
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	return ret;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun /*
270*4882a593Smuzhiyun set named driver variable to int value and return error indication
271*4882a593Smuzhiyun calling example: dev_wlc_intvar_set(dev, "arate", rate)
272*4882a593Smuzhiyun */
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun static int
dev_wlc_intvar_set(struct net_device * dev,char * name,int val)275*4882a593Smuzhiyun dev_wlc_intvar_set(
276*4882a593Smuzhiyun 	struct net_device *dev,
277*4882a593Smuzhiyun 	char *name,
278*4882a593Smuzhiyun 	int val)
279*4882a593Smuzhiyun {
280*4882a593Smuzhiyun 	char buf[WLC_IOCTL_SMLEN];
281*4882a593Smuzhiyun 	uint len;
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	val = htod32(val);
284*4882a593Smuzhiyun 	len = bcm_mkiovar(name, (char *)(&val), sizeof(val), buf, sizeof(buf));
285*4882a593Smuzhiyun 	ASSERT(len);
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 	return (dev_wlc_ioctl(dev, WLC_SET_VAR, buf, len));
288*4882a593Smuzhiyun }
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun static int
dev_iw_iovar_setbuf(struct net_device * dev,char * iovar,void * param,int paramlen,void * bufptr,int buflen)291*4882a593Smuzhiyun dev_iw_iovar_setbuf(
292*4882a593Smuzhiyun 	struct net_device *dev,
293*4882a593Smuzhiyun 	char *iovar,
294*4882a593Smuzhiyun 	void *param,
295*4882a593Smuzhiyun 	int paramlen,
296*4882a593Smuzhiyun 	void *bufptr,
297*4882a593Smuzhiyun 	int buflen)
298*4882a593Smuzhiyun {
299*4882a593Smuzhiyun 	int iolen;
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen);
302*4882a593Smuzhiyun 	ASSERT(iolen);
303*4882a593Smuzhiyun 	BCM_REFERENCE(iolen);
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	return (dev_wlc_ioctl(dev, WLC_SET_VAR, bufptr, iolen));
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun static int
dev_iw_iovar_getbuf(struct net_device * dev,char * iovar,void * param,int paramlen,void * bufptr,int buflen)309*4882a593Smuzhiyun dev_iw_iovar_getbuf(
310*4882a593Smuzhiyun 	struct net_device *dev,
311*4882a593Smuzhiyun 	char *iovar,
312*4882a593Smuzhiyun 	void *param,
313*4882a593Smuzhiyun 	int paramlen,
314*4882a593Smuzhiyun 	void *bufptr,
315*4882a593Smuzhiyun 	int buflen)
316*4882a593Smuzhiyun {
317*4882a593Smuzhiyun 	int iolen;
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen);
320*4882a593Smuzhiyun 	ASSERT(iolen);
321*4882a593Smuzhiyun 	BCM_REFERENCE(iolen);
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	return (dev_wlc_ioctl(dev, WLC_GET_VAR, bufptr, buflen));
324*4882a593Smuzhiyun }
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun #if WIRELESS_EXT > 17
327*4882a593Smuzhiyun static int
dev_wlc_bufvar_set(struct net_device * dev,char * name,char * buf,int len)328*4882a593Smuzhiyun dev_wlc_bufvar_set(
329*4882a593Smuzhiyun 	struct net_device *dev,
330*4882a593Smuzhiyun 	char *name,
331*4882a593Smuzhiyun 	char *buf, int len)
332*4882a593Smuzhiyun {
333*4882a593Smuzhiyun 	char *ioctlbuf;
334*4882a593Smuzhiyun 	uint buflen;
335*4882a593Smuzhiyun 	int error;
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	ioctlbuf = kmalloc(MAX_WLIW_IOCTL_LEN, GFP_KERNEL);
338*4882a593Smuzhiyun 	if (!ioctlbuf)
339*4882a593Smuzhiyun 		return -ENOMEM;
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	buflen = bcm_mkiovar(name, buf, len, ioctlbuf, MAX_WLIW_IOCTL_LEN);
342*4882a593Smuzhiyun 	ASSERT(buflen);
343*4882a593Smuzhiyun 	error = dev_wlc_ioctl(dev, WLC_SET_VAR, ioctlbuf, buflen);
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	kfree(ioctlbuf);
346*4882a593Smuzhiyun 	return error;
347*4882a593Smuzhiyun }
348*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 17 */
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun /*
351*4882a593Smuzhiyun get named driver variable to int value and return error indication
352*4882a593Smuzhiyun calling example: dev_wlc_bufvar_get(dev, "arate", &rate)
353*4882a593Smuzhiyun */
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun static int
dev_wlc_bufvar_get(struct net_device * dev,char * name,char * buf,int buflen)356*4882a593Smuzhiyun dev_wlc_bufvar_get(
357*4882a593Smuzhiyun 	struct net_device *dev,
358*4882a593Smuzhiyun 	char *name,
359*4882a593Smuzhiyun 	char *buf, int buflen)
360*4882a593Smuzhiyun {
361*4882a593Smuzhiyun 	char *ioctlbuf;
362*4882a593Smuzhiyun 	int error;
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 	uint len;
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	ioctlbuf = kmalloc(MAX_WLIW_IOCTL_LEN, GFP_KERNEL);
367*4882a593Smuzhiyun 	if (!ioctlbuf)
368*4882a593Smuzhiyun 		return -ENOMEM;
369*4882a593Smuzhiyun 	len = bcm_mkiovar(name, NULL, 0, ioctlbuf, MAX_WLIW_IOCTL_LEN);
370*4882a593Smuzhiyun 	ASSERT(len);
371*4882a593Smuzhiyun 	BCM_REFERENCE(len);
372*4882a593Smuzhiyun 	error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)ioctlbuf, MAX_WLIW_IOCTL_LEN);
373*4882a593Smuzhiyun 	if (!error)
374*4882a593Smuzhiyun 		bcopy(ioctlbuf, buf, buflen);
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	kfree(ioctlbuf);
377*4882a593Smuzhiyun 	return (error);
378*4882a593Smuzhiyun }
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun /*
381*4882a593Smuzhiyun get named driver variable to int value and return error indication
382*4882a593Smuzhiyun calling example: dev_wlc_intvar_get(dev, "arate", &rate)
383*4882a593Smuzhiyun */
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun static int
dev_wlc_intvar_get(struct net_device * dev,char * name,int * retval)386*4882a593Smuzhiyun dev_wlc_intvar_get(
387*4882a593Smuzhiyun 	struct net_device *dev,
388*4882a593Smuzhiyun 	char *name,
389*4882a593Smuzhiyun 	int *retval)
390*4882a593Smuzhiyun {
391*4882a593Smuzhiyun 	union {
392*4882a593Smuzhiyun 		char buf[WLC_IOCTL_SMLEN];
393*4882a593Smuzhiyun 		int val;
394*4882a593Smuzhiyun 	} var;
395*4882a593Smuzhiyun 	int error;
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	uint len;
398*4882a593Smuzhiyun 	uint data_null;
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 	len = bcm_mkiovar(name, (char *)(&data_null), 0, (char *)(&var), sizeof(var.buf));
401*4882a593Smuzhiyun 	ASSERT(len);
402*4882a593Smuzhiyun 	error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)&var, len);
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 	*retval = dtoh32(var.val);
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 	return (error);
407*4882a593Smuzhiyun }
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun /* Maintain backward compatibility */
410*4882a593Smuzhiyun #if WIRELESS_EXT < 13
411*4882a593Smuzhiyun struct iw_request_info
412*4882a593Smuzhiyun {
413*4882a593Smuzhiyun 	__u16		cmd;		/* Wireless Extension command */
414*4882a593Smuzhiyun 	__u16		flags;		/* More to come ;-) */
415*4882a593Smuzhiyun };
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun typedef int (*iw_handler)(struct net_device *dev, struct iw_request_info *info,
418*4882a593Smuzhiyun 	void *wrqu, char *extra);
419*4882a593Smuzhiyun #endif /* WIRELESS_EXT < 13 */
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun #if WIRELESS_EXT > 12
422*4882a593Smuzhiyun static int
wl_iw_set_leddc(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)423*4882a593Smuzhiyun wl_iw_set_leddc(
424*4882a593Smuzhiyun 	struct net_device *dev,
425*4882a593Smuzhiyun 	struct iw_request_info *info,
426*4882a593Smuzhiyun 	union iwreq_data *wrqu,
427*4882a593Smuzhiyun 	char *extra
428*4882a593Smuzhiyun )
429*4882a593Smuzhiyun {
430*4882a593Smuzhiyun 	int dc = *(int *)extra;
431*4882a593Smuzhiyun 	int error;
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	error = dev_wlc_intvar_set(dev, "leddc", dc);
434*4882a593Smuzhiyun 	return error;
435*4882a593Smuzhiyun }
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun static int
wl_iw_set_vlanmode(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)438*4882a593Smuzhiyun wl_iw_set_vlanmode(
439*4882a593Smuzhiyun 	struct net_device *dev,
440*4882a593Smuzhiyun 	struct iw_request_info *info,
441*4882a593Smuzhiyun 	union iwreq_data *wrqu,
442*4882a593Smuzhiyun 	char *extra
443*4882a593Smuzhiyun )
444*4882a593Smuzhiyun {
445*4882a593Smuzhiyun 	int mode = *(int *)extra;
446*4882a593Smuzhiyun 	int error;
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	mode = htod32(mode);
449*4882a593Smuzhiyun 	error = dev_wlc_intvar_set(dev, "vlan_mode", mode);
450*4882a593Smuzhiyun 	return error;
451*4882a593Smuzhiyun }
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun static int
wl_iw_set_pm(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)454*4882a593Smuzhiyun wl_iw_set_pm(
455*4882a593Smuzhiyun 	struct net_device *dev,
456*4882a593Smuzhiyun 	struct iw_request_info *info,
457*4882a593Smuzhiyun 	union iwreq_data *wrqu,
458*4882a593Smuzhiyun 	char *extra
459*4882a593Smuzhiyun )
460*4882a593Smuzhiyun {
461*4882a593Smuzhiyun 	int pm = *(int *)extra;
462*4882a593Smuzhiyun 	int error;
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun 	pm = htod32(pm);
465*4882a593Smuzhiyun 	error = dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm));
466*4882a593Smuzhiyun 	return error;
467*4882a593Smuzhiyun }
468*4882a593Smuzhiyun 
469*4882a593Smuzhiyun #if WIRELESS_EXT > 17
470*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 17 */
471*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 12 */
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun int
wl_iw_send_priv_event(struct net_device * dev,char * flag)474*4882a593Smuzhiyun wl_iw_send_priv_event(
475*4882a593Smuzhiyun 	struct net_device *dev,
476*4882a593Smuzhiyun 	char *flag
477*4882a593Smuzhiyun )
478*4882a593Smuzhiyun {
479*4882a593Smuzhiyun 	union iwreq_data wrqu;
480*4882a593Smuzhiyun 	char extra[IW_CUSTOM_MAX + 1];
481*4882a593Smuzhiyun 	int cmd;
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 	cmd = IWEVCUSTOM;
484*4882a593Smuzhiyun 	memset(&wrqu, 0, sizeof(wrqu));
485*4882a593Smuzhiyun 	if (strlen(flag) > sizeof(extra))
486*4882a593Smuzhiyun 		return -1;
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun 	strncpy(extra, flag, sizeof(extra));
489*4882a593Smuzhiyun 	extra[sizeof(extra) - 1] = '\0';
490*4882a593Smuzhiyun 	wrqu.data.length = strlen(extra);
491*4882a593Smuzhiyun 	wireless_send_event(dev, cmd, &wrqu, extra);
492*4882a593Smuzhiyun 	WL_TRACE(("Send IWEVCUSTOM Event as %s\n", extra));
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 	return 0;
495*4882a593Smuzhiyun }
496*4882a593Smuzhiyun 
497*4882a593Smuzhiyun static int
wl_iw_config_commit(struct net_device * dev,struct iw_request_info * info,void * zwrq,char * extra)498*4882a593Smuzhiyun wl_iw_config_commit(
499*4882a593Smuzhiyun 	struct net_device *dev,
500*4882a593Smuzhiyun 	struct iw_request_info *info,
501*4882a593Smuzhiyun 	void *zwrq,
502*4882a593Smuzhiyun 	char *extra
503*4882a593Smuzhiyun )
504*4882a593Smuzhiyun {
505*4882a593Smuzhiyun 	wlc_ssid_t ssid;
506*4882a593Smuzhiyun 	int error;
507*4882a593Smuzhiyun 	struct sockaddr bssid;
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWCOMMIT\n", dev->name));
510*4882a593Smuzhiyun 
511*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid))))
512*4882a593Smuzhiyun 		return error;
513*4882a593Smuzhiyun 
514*4882a593Smuzhiyun 	ssid.SSID_len = dtoh32(ssid.SSID_len);
515*4882a593Smuzhiyun 
516*4882a593Smuzhiyun 	if (!ssid.SSID_len)
517*4882a593Smuzhiyun 		return 0;
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 	bzero(&bssid, sizeof(struct sockaddr));
520*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_REASSOC, &bssid, ETHER_ADDR_LEN))) {
521*4882a593Smuzhiyun 		WL_ERROR(("%s: WLC_REASSOC failed (%d)\n", __FUNCTION__, error));
522*4882a593Smuzhiyun 		return error;
523*4882a593Smuzhiyun 	}
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun 	return 0;
526*4882a593Smuzhiyun }
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun static int
wl_iw_get_name(struct net_device * dev,struct iw_request_info * info,union iwreq_data * cwrq,char * extra)529*4882a593Smuzhiyun wl_iw_get_name(
530*4882a593Smuzhiyun 	struct net_device *dev,
531*4882a593Smuzhiyun 	struct iw_request_info *info,
532*4882a593Smuzhiyun 	union iwreq_data *cwrq,
533*4882a593Smuzhiyun 	char *extra
534*4882a593Smuzhiyun )
535*4882a593Smuzhiyun {
536*4882a593Smuzhiyun 	int phytype, err;
537*4882a593Smuzhiyun 	uint band[3];
538*4882a593Smuzhiyun 	char cap[5];
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWNAME\n", dev->name));
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 	cap[0] = 0;
543*4882a593Smuzhiyun 	if ((err = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &phytype, sizeof(phytype))) < 0)
544*4882a593Smuzhiyun 		goto done;
545*4882a593Smuzhiyun 	if ((err = dev_wlc_ioctl(dev, WLC_GET_BANDLIST, band, sizeof(band))) < 0)
546*4882a593Smuzhiyun 		goto done;
547*4882a593Smuzhiyun 
548*4882a593Smuzhiyun 	band[0] = dtoh32(band[0]);
549*4882a593Smuzhiyun 	switch (phytype) {
550*4882a593Smuzhiyun 		case WLC_PHY_TYPE_A:
551*4882a593Smuzhiyun 			strncpy(cap, "a", sizeof(cap));
552*4882a593Smuzhiyun 			break;
553*4882a593Smuzhiyun 		case WLC_PHY_TYPE_B:
554*4882a593Smuzhiyun 			strncpy(cap, "b", sizeof(cap));
555*4882a593Smuzhiyun 			break;
556*4882a593Smuzhiyun 		case WLC_PHY_TYPE_G:
557*4882a593Smuzhiyun 			if (band[0] >= 2)
558*4882a593Smuzhiyun 				strncpy(cap, "abg", sizeof(cap));
559*4882a593Smuzhiyun 			else
560*4882a593Smuzhiyun 				strncpy(cap, "bg", sizeof(cap));
561*4882a593Smuzhiyun 			break;
562*4882a593Smuzhiyun 		case WLC_PHY_TYPE_N:
563*4882a593Smuzhiyun 			if (band[0] >= 2)
564*4882a593Smuzhiyun 				strncpy(cap, "abgn", sizeof(cap));
565*4882a593Smuzhiyun 			else
566*4882a593Smuzhiyun 				strncpy(cap, "bgn", sizeof(cap));
567*4882a593Smuzhiyun 			break;
568*4882a593Smuzhiyun 	}
569*4882a593Smuzhiyun done:
570*4882a593Smuzhiyun 	(void)snprintf(cwrq->name, IFNAMSIZ, "IEEE 802.11%s", cap);
571*4882a593Smuzhiyun 
572*4882a593Smuzhiyun 	return 0;
573*4882a593Smuzhiyun }
574*4882a593Smuzhiyun 
575*4882a593Smuzhiyun static int
wl_iw_set_freq(struct net_device * dev,struct iw_request_info * info,struct iw_freq * fwrq,char * extra)576*4882a593Smuzhiyun wl_iw_set_freq(
577*4882a593Smuzhiyun 	struct net_device *dev,
578*4882a593Smuzhiyun 	struct iw_request_info *info,
579*4882a593Smuzhiyun 	struct iw_freq *fwrq,
580*4882a593Smuzhiyun 	char *extra
581*4882a593Smuzhiyun )
582*4882a593Smuzhiyun {
583*4882a593Smuzhiyun 	int error, chan;
584*4882a593Smuzhiyun 	uint sf = 0;
585*4882a593Smuzhiyun 
586*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWFREQ\n", dev->name));
587*4882a593Smuzhiyun 
588*4882a593Smuzhiyun 	/* Setting by channel number */
589*4882a593Smuzhiyun 	if (fwrq->e == 0 && fwrq->m < MAXCHANNEL) {
590*4882a593Smuzhiyun 		chan = fwrq->m;
591*4882a593Smuzhiyun 	}
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun 	/* Setting by frequency */
594*4882a593Smuzhiyun 	else {
595*4882a593Smuzhiyun 		/* Convert to MHz as best we can */
596*4882a593Smuzhiyun 		if (fwrq->e >= 6) {
597*4882a593Smuzhiyun 			fwrq->e -= 6;
598*4882a593Smuzhiyun 			while (fwrq->e--)
599*4882a593Smuzhiyun 				fwrq->m *= 10;
600*4882a593Smuzhiyun 		} else if (fwrq->e < 6) {
601*4882a593Smuzhiyun 			while (fwrq->e++ < 6)
602*4882a593Smuzhiyun 				fwrq->m /= 10;
603*4882a593Smuzhiyun 		}
604*4882a593Smuzhiyun 		/* handle 4.9GHz frequencies as Japan 4 GHz based channelization */
605*4882a593Smuzhiyun 		if (fwrq->m > 4000 && fwrq->m < 5000) {
606*4882a593Smuzhiyun 		    sf = WF_CHAN_FACTOR_4_G; /* start factor for 4 GHz */
607*4882a593Smuzhiyun 		}
608*4882a593Smuzhiyun 
609*4882a593Smuzhiyun 		chan = wf_mhz2channel(fwrq->m, sf);
610*4882a593Smuzhiyun 	}
611*4882a593Smuzhiyun 	chan = htod32(chan);
612*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_SET_CHANNEL, &chan, sizeof(chan))))
613*4882a593Smuzhiyun 		return error;
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun 	/* -EINPROGRESS: Call commit handler */
616*4882a593Smuzhiyun 	return -EINPROGRESS;
617*4882a593Smuzhiyun }
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun static int
wl_iw_get_freq(struct net_device * dev,struct iw_request_info * info,struct iw_freq * fwrq,char * extra)620*4882a593Smuzhiyun wl_iw_get_freq(
621*4882a593Smuzhiyun 	struct net_device *dev,
622*4882a593Smuzhiyun 	struct iw_request_info *info,
623*4882a593Smuzhiyun 	struct iw_freq *fwrq,
624*4882a593Smuzhiyun 	char *extra
625*4882a593Smuzhiyun )
626*4882a593Smuzhiyun {
627*4882a593Smuzhiyun 	channel_info_t ci;
628*4882a593Smuzhiyun 	int error;
629*4882a593Smuzhiyun 
630*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWFREQ\n", dev->name));
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci))))
633*4882a593Smuzhiyun 		return error;
634*4882a593Smuzhiyun 
635*4882a593Smuzhiyun 	/* Return radio channel in channel form */
636*4882a593Smuzhiyun 	fwrq->m = dtoh32(ci.hw_channel);
637*4882a593Smuzhiyun 	fwrq->e = dtoh32(0);
638*4882a593Smuzhiyun 	return 0;
639*4882a593Smuzhiyun }
640*4882a593Smuzhiyun 
641*4882a593Smuzhiyun static int
wl_iw_set_mode(struct net_device * dev,struct iw_request_info * info,__u32 * uwrq,char * extra)642*4882a593Smuzhiyun wl_iw_set_mode(
643*4882a593Smuzhiyun 	struct net_device *dev,
644*4882a593Smuzhiyun 	struct iw_request_info *info,
645*4882a593Smuzhiyun 	__u32 *uwrq,
646*4882a593Smuzhiyun 	char *extra
647*4882a593Smuzhiyun )
648*4882a593Smuzhiyun {
649*4882a593Smuzhiyun 	int infra = 0, ap = 0, error = 0;
650*4882a593Smuzhiyun 
651*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWMODE\n", dev->name));
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 	switch (*uwrq) {
654*4882a593Smuzhiyun 	case IW_MODE_MASTER:
655*4882a593Smuzhiyun 		infra = ap = 1;
656*4882a593Smuzhiyun 		break;
657*4882a593Smuzhiyun 	case IW_MODE_ADHOC:
658*4882a593Smuzhiyun 	case IW_MODE_AUTO:
659*4882a593Smuzhiyun 		break;
660*4882a593Smuzhiyun 	case IW_MODE_INFRA:
661*4882a593Smuzhiyun 		infra = 1;
662*4882a593Smuzhiyun 		break;
663*4882a593Smuzhiyun 	default:
664*4882a593Smuzhiyun 		return -EINVAL;
665*4882a593Smuzhiyun 	}
666*4882a593Smuzhiyun 	infra = htod32(infra);
667*4882a593Smuzhiyun 	ap = htod32(ap);
668*4882a593Smuzhiyun 
669*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(infra))) ||
670*4882a593Smuzhiyun 	    (error = dev_wlc_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap))))
671*4882a593Smuzhiyun 		return error;
672*4882a593Smuzhiyun 
673*4882a593Smuzhiyun 	/* -EINPROGRESS: Call commit handler */
674*4882a593Smuzhiyun 	return -EINPROGRESS;
675*4882a593Smuzhiyun }
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun static int
wl_iw_get_mode(struct net_device * dev,struct iw_request_info * info,__u32 * uwrq,char * extra)678*4882a593Smuzhiyun wl_iw_get_mode(
679*4882a593Smuzhiyun 	struct net_device *dev,
680*4882a593Smuzhiyun 	struct iw_request_info *info,
681*4882a593Smuzhiyun 	__u32 *uwrq,
682*4882a593Smuzhiyun 	char *extra
683*4882a593Smuzhiyun )
684*4882a593Smuzhiyun {
685*4882a593Smuzhiyun 	int error, infra = 0, ap = 0;
686*4882a593Smuzhiyun 
687*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWMODE\n", dev->name));
688*4882a593Smuzhiyun 
689*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_GET_INFRA, &infra, sizeof(infra))) ||
690*4882a593Smuzhiyun 	    (error = dev_wlc_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap))))
691*4882a593Smuzhiyun 		return error;
692*4882a593Smuzhiyun 
693*4882a593Smuzhiyun 	infra = dtoh32(infra);
694*4882a593Smuzhiyun 	ap = dtoh32(ap);
695*4882a593Smuzhiyun 	*uwrq = infra ? ap ? IW_MODE_MASTER : IW_MODE_INFRA : IW_MODE_ADHOC;
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 	return 0;
698*4882a593Smuzhiyun }
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun static int
wl_iw_get_range(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)701*4882a593Smuzhiyun wl_iw_get_range(
702*4882a593Smuzhiyun 	struct net_device *dev,
703*4882a593Smuzhiyun 	struct iw_request_info *info,
704*4882a593Smuzhiyun 	struct iw_point *dwrq,
705*4882a593Smuzhiyun 	char *extra
706*4882a593Smuzhiyun )
707*4882a593Smuzhiyun {
708*4882a593Smuzhiyun 	struct iw_range *range = (struct iw_range *) extra;
709*4882a593Smuzhiyun 	static int channels[MAXCHANNEL+1];
710*4882a593Smuzhiyun 	wl_uint32_list_t *list = (wl_uint32_list_t *) channels;
711*4882a593Smuzhiyun 	wl_rateset_t rateset;
712*4882a593Smuzhiyun 	int error, i;
713*4882a593Smuzhiyun 	uint sf, ch;
714*4882a593Smuzhiyun 
715*4882a593Smuzhiyun 	int phytype;
716*4882a593Smuzhiyun 	int fbt_cap = 0;
717*4882a593Smuzhiyun 
718*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWRANGE\n", dev->name));
719*4882a593Smuzhiyun 
720*4882a593Smuzhiyun 	if (!extra)
721*4882a593Smuzhiyun 		return -EINVAL;
722*4882a593Smuzhiyun 
723*4882a593Smuzhiyun 	dwrq->length = sizeof(struct iw_range);
724*4882a593Smuzhiyun 	memset(range, 0, sizeof(*range));
725*4882a593Smuzhiyun 
726*4882a593Smuzhiyun 	/* We don't use nwids */
727*4882a593Smuzhiyun 	range->min_nwid = range->max_nwid = 0;
728*4882a593Smuzhiyun 
729*4882a593Smuzhiyun 	/* Set available channels/frequencies */
730*4882a593Smuzhiyun 	list->count = htod32(MAXCHANNEL);
731*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_GET_VALID_CHANNELS, channels, sizeof(channels))))
732*4882a593Smuzhiyun 		return error;
733*4882a593Smuzhiyun 	for (i = 0; i < dtoh32(list->count) && i < IW_MAX_FREQUENCIES; i++) {
734*4882a593Smuzhiyun 		range->freq[i].i = dtoh32(list->element[i]);
735*4882a593Smuzhiyun 
736*4882a593Smuzhiyun 		ch = dtoh32(list->element[i]);
737*4882a593Smuzhiyun 		if (ch <= CH_MAX_2G_CHANNEL)
738*4882a593Smuzhiyun 			sf = WF_CHAN_FACTOR_2_4_G;
739*4882a593Smuzhiyun 		else
740*4882a593Smuzhiyun 			sf = WF_CHAN_FACTOR_5_G;
741*4882a593Smuzhiyun 
742*4882a593Smuzhiyun 		range->freq[i].m = wf_channel2mhz(ch, sf);
743*4882a593Smuzhiyun 		range->freq[i].e = 6;
744*4882a593Smuzhiyun 	}
745*4882a593Smuzhiyun 	range->num_frequency = range->num_channels = i;
746*4882a593Smuzhiyun 
747*4882a593Smuzhiyun 	/* Link quality (use NDIS cutoffs) */
748*4882a593Smuzhiyun 	range->max_qual.qual = 5;
749*4882a593Smuzhiyun 	/* Signal level (use RSSI) */
750*4882a593Smuzhiyun 	range->max_qual.level = 0x100 - 200;	/* -200 dBm */
751*4882a593Smuzhiyun 	/* Noise level (use noise) */
752*4882a593Smuzhiyun 	range->max_qual.noise = 0x100 - 200;	/* -200 dBm */
753*4882a593Smuzhiyun 	/* Signal level threshold range (?) */
754*4882a593Smuzhiyun 	range->sensitivity = 65535;
755*4882a593Smuzhiyun 
756*4882a593Smuzhiyun #if WIRELESS_EXT > 11
757*4882a593Smuzhiyun 	/* Link quality (use NDIS cutoffs) */
758*4882a593Smuzhiyun 	range->avg_qual.qual = 3;
759*4882a593Smuzhiyun 	/* Signal level (use RSSI) */
760*4882a593Smuzhiyun 	range->avg_qual.level = 0x100 + WL_IW_RSSI_GOOD;
761*4882a593Smuzhiyun 	/* Noise level (use noise) */
762*4882a593Smuzhiyun 	range->avg_qual.noise = 0x100 - 75;	/* -75 dBm */
763*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 11 */
764*4882a593Smuzhiyun 
765*4882a593Smuzhiyun 	/* Set available bitrates */
766*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset))))
767*4882a593Smuzhiyun 		return error;
768*4882a593Smuzhiyun 	rateset.count = dtoh32(rateset.count);
769*4882a593Smuzhiyun 	range->num_bitrates = rateset.count;
770*4882a593Smuzhiyun 	for (i = 0; i < rateset.count && i < IW_MAX_BITRATES; i++)
771*4882a593Smuzhiyun 		range->bitrate[i] = (rateset.rates[i] & 0x7f) * 500000; /* convert to bps */
772*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &phytype, sizeof(phytype))))
773*4882a593Smuzhiyun 		return error;
774*4882a593Smuzhiyun 
775*4882a593Smuzhiyun 	/* Set an indication of the max TCP throughput
776*4882a593Smuzhiyun 	 * in bit/s that we can expect using this interface.
777*4882a593Smuzhiyun 	 * May be use for QoS stuff... Jean II
778*4882a593Smuzhiyun 	 */
779*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &i, sizeof(i))))
780*4882a593Smuzhiyun 		return error;
781*4882a593Smuzhiyun 	i = dtoh32(i);
782*4882a593Smuzhiyun 	if (i == WLC_PHY_TYPE_A)
783*4882a593Smuzhiyun 		range->throughput = 24000000;	/* 24 Mbits/s */
784*4882a593Smuzhiyun 	else
785*4882a593Smuzhiyun 		range->throughput = 1500000;	/* 1.5 Mbits/s */
786*4882a593Smuzhiyun 
787*4882a593Smuzhiyun 	/* RTS and fragmentation thresholds */
788*4882a593Smuzhiyun 	range->min_rts = 0;
789*4882a593Smuzhiyun 	range->max_rts = 2347;
790*4882a593Smuzhiyun 	range->min_frag = 256;
791*4882a593Smuzhiyun 	range->max_frag = 2346;
792*4882a593Smuzhiyun 
793*4882a593Smuzhiyun 	range->max_encoding_tokens = DOT11_MAX_DEFAULT_KEYS;
794*4882a593Smuzhiyun 	range->num_encoding_sizes = 4;
795*4882a593Smuzhiyun 	range->encoding_size[0] = WEP1_KEY_SIZE;
796*4882a593Smuzhiyun 	range->encoding_size[1] = WEP128_KEY_SIZE;
797*4882a593Smuzhiyun #if WIRELESS_EXT > 17
798*4882a593Smuzhiyun 	range->encoding_size[2] = TKIP_KEY_SIZE;
799*4882a593Smuzhiyun #else
800*4882a593Smuzhiyun 	range->encoding_size[2] = 0;
801*4882a593Smuzhiyun #endif // endif
802*4882a593Smuzhiyun 	range->encoding_size[3] = AES_KEY_SIZE;
803*4882a593Smuzhiyun 
804*4882a593Smuzhiyun 	/* Do not support power micro-management */
805*4882a593Smuzhiyun 	range->min_pmp = 0;
806*4882a593Smuzhiyun 	range->max_pmp = 0;
807*4882a593Smuzhiyun 	range->min_pmt = 0;
808*4882a593Smuzhiyun 	range->max_pmt = 0;
809*4882a593Smuzhiyun 	range->pmp_flags = 0;
810*4882a593Smuzhiyun 	range->pm_capa = 0;
811*4882a593Smuzhiyun 
812*4882a593Smuzhiyun 	/* Transmit Power - values are in mW */
813*4882a593Smuzhiyun 	range->num_txpower = 2;
814*4882a593Smuzhiyun 	range->txpower[0] = 1;
815*4882a593Smuzhiyun 	range->txpower[1] = 255;
816*4882a593Smuzhiyun 	range->txpower_capa = IW_TXPOW_MWATT;
817*4882a593Smuzhiyun 
818*4882a593Smuzhiyun #if WIRELESS_EXT > 10
819*4882a593Smuzhiyun 	range->we_version_compiled = WIRELESS_EXT;
820*4882a593Smuzhiyun 	range->we_version_source = 19;
821*4882a593Smuzhiyun 
822*4882a593Smuzhiyun 	/* Only support retry limits */
823*4882a593Smuzhiyun 	range->retry_capa = IW_RETRY_LIMIT;
824*4882a593Smuzhiyun 	range->retry_flags = IW_RETRY_LIMIT;
825*4882a593Smuzhiyun 	range->r_time_flags = 0;
826*4882a593Smuzhiyun 	/* SRL and LRL limits */
827*4882a593Smuzhiyun 	range->min_retry = 1;
828*4882a593Smuzhiyun 	range->max_retry = 255;
829*4882a593Smuzhiyun 	/* Retry lifetime limits unsupported */
830*4882a593Smuzhiyun 	range->min_r_time = 0;
831*4882a593Smuzhiyun 	range->max_r_time = 0;
832*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 10 */
833*4882a593Smuzhiyun 
834*4882a593Smuzhiyun #if WIRELESS_EXT > 17
835*4882a593Smuzhiyun 	range->enc_capa = IW_ENC_CAPA_WPA;
836*4882a593Smuzhiyun 	range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP;
837*4882a593Smuzhiyun 	range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP;
838*4882a593Smuzhiyun 	range->enc_capa |= IW_ENC_CAPA_WPA2;
839*4882a593Smuzhiyun 
840*4882a593Smuzhiyun 	/* Determine driver FBT capability. */
841*4882a593Smuzhiyun 	if (dev_wlc_intvar_get(dev, "fbt_cap", &fbt_cap) == 0) {
842*4882a593Smuzhiyun 		if (fbt_cap == WLC_FBT_CAP_DRV_4WAY_AND_REASSOC) {
843*4882a593Smuzhiyun 			/* Tell the host (e.g. wpa_supplicant) to let driver do the handshake */
844*4882a593Smuzhiyun 			range->enc_capa |= IW_ENC_CAPA_4WAY_HANDSHAKE;
845*4882a593Smuzhiyun 		}
846*4882a593Smuzhiyun 	}
847*4882a593Smuzhiyun 
848*4882a593Smuzhiyun #ifdef BCMFW_ROAM_ENABLE_WEXT
849*4882a593Smuzhiyun 	/* Advertise firmware roam capability to the external supplicant */
850*4882a593Smuzhiyun 	range->enc_capa |= IW_ENC_CAPA_FW_ROAM_ENABLE;
851*4882a593Smuzhiyun #endif /* BCMFW_ROAM_ENABLE_WEXT */
852*4882a593Smuzhiyun 
853*4882a593Smuzhiyun 	/* Event capability (kernel) */
854*4882a593Smuzhiyun 	IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
855*4882a593Smuzhiyun 	/* Event capability (driver) */
856*4882a593Smuzhiyun 	IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
857*4882a593Smuzhiyun 	IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
858*4882a593Smuzhiyun 	IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP);
859*4882a593Smuzhiyun 	IW_EVENT_CAPA_SET(range->event_capa, IWEVMICHAELMICFAILURE);
860*4882a593Smuzhiyun 	IW_EVENT_CAPA_SET(range->event_capa, IWEVASSOCREQIE);
861*4882a593Smuzhiyun 	IW_EVENT_CAPA_SET(range->event_capa, IWEVASSOCRESPIE);
862*4882a593Smuzhiyun 	IW_EVENT_CAPA_SET(range->event_capa, IWEVPMKIDCAND);
863*4882a593Smuzhiyun 
864*4882a593Smuzhiyun #if WIRELESS_EXT >= 22 && defined(IW_SCAN_CAPA_ESSID)
865*4882a593Smuzhiyun 	/* FC7 wireless.h defines EXT 22 but doesn't define scan_capa bits */
866*4882a593Smuzhiyun 	range->scan_capa = IW_SCAN_CAPA_ESSID;
867*4882a593Smuzhiyun #endif // endif
868*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 17 */
869*4882a593Smuzhiyun 
870*4882a593Smuzhiyun 	return 0;
871*4882a593Smuzhiyun }
872*4882a593Smuzhiyun 
873*4882a593Smuzhiyun static int
rssi_to_qual(int rssi)874*4882a593Smuzhiyun rssi_to_qual(int rssi)
875*4882a593Smuzhiyun {
876*4882a593Smuzhiyun 	if (rssi <= WL_IW_RSSI_NO_SIGNAL)
877*4882a593Smuzhiyun 		return 0;
878*4882a593Smuzhiyun 	else if (rssi <= WL_IW_RSSI_VERY_LOW)
879*4882a593Smuzhiyun 		return 1;
880*4882a593Smuzhiyun 	else if (rssi <= WL_IW_RSSI_LOW)
881*4882a593Smuzhiyun 		return 2;
882*4882a593Smuzhiyun 	else if (rssi <= WL_IW_RSSI_GOOD)
883*4882a593Smuzhiyun 		return 3;
884*4882a593Smuzhiyun 	else if (rssi <= WL_IW_RSSI_VERY_GOOD)
885*4882a593Smuzhiyun 		return 4;
886*4882a593Smuzhiyun 	else
887*4882a593Smuzhiyun 		return 5;
888*4882a593Smuzhiyun }
889*4882a593Smuzhiyun 
890*4882a593Smuzhiyun static int
wl_iw_set_spy(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)891*4882a593Smuzhiyun wl_iw_set_spy(
892*4882a593Smuzhiyun 	struct net_device *dev,
893*4882a593Smuzhiyun 	struct iw_request_info *info,
894*4882a593Smuzhiyun 	struct iw_point *dwrq,
895*4882a593Smuzhiyun 	char *extra
896*4882a593Smuzhiyun )
897*4882a593Smuzhiyun {
898*4882a593Smuzhiyun 	wl_iw_t *iw = IW_DEV_IF(dev);
899*4882a593Smuzhiyun 	struct sockaddr *addr = (struct sockaddr *) extra;
900*4882a593Smuzhiyun 	int i;
901*4882a593Smuzhiyun 
902*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWSPY\n", dev->name));
903*4882a593Smuzhiyun 
904*4882a593Smuzhiyun 	if (!extra)
905*4882a593Smuzhiyun 		return -EINVAL;
906*4882a593Smuzhiyun 
907*4882a593Smuzhiyun 	iw->spy_num = MIN(ARRAYSIZE(iw->spy_addr), dwrq->length);
908*4882a593Smuzhiyun 	for (i = 0; i < iw->spy_num; i++)
909*4882a593Smuzhiyun 		memcpy(&iw->spy_addr[i], addr[i].sa_data, ETHER_ADDR_LEN);
910*4882a593Smuzhiyun 	memset(iw->spy_qual, 0, sizeof(iw->spy_qual));
911*4882a593Smuzhiyun 
912*4882a593Smuzhiyun 	return 0;
913*4882a593Smuzhiyun }
914*4882a593Smuzhiyun 
915*4882a593Smuzhiyun static int
wl_iw_get_spy(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)916*4882a593Smuzhiyun wl_iw_get_spy(
917*4882a593Smuzhiyun 	struct net_device *dev,
918*4882a593Smuzhiyun 	struct iw_request_info *info,
919*4882a593Smuzhiyun 	struct iw_point *dwrq,
920*4882a593Smuzhiyun 	char *extra
921*4882a593Smuzhiyun )
922*4882a593Smuzhiyun {
923*4882a593Smuzhiyun 	wl_iw_t *iw = IW_DEV_IF(dev);
924*4882a593Smuzhiyun 	struct sockaddr *addr = (struct sockaddr *) extra;
925*4882a593Smuzhiyun 	struct iw_quality *qual = (struct iw_quality *) &addr[iw->spy_num];
926*4882a593Smuzhiyun 	int i;
927*4882a593Smuzhiyun 
928*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWSPY\n", dev->name));
929*4882a593Smuzhiyun 
930*4882a593Smuzhiyun 	if (!extra)
931*4882a593Smuzhiyun 		return -EINVAL;
932*4882a593Smuzhiyun 
933*4882a593Smuzhiyun 	dwrq->length = iw->spy_num;
934*4882a593Smuzhiyun 	for (i = 0; i < iw->spy_num; i++) {
935*4882a593Smuzhiyun 		memcpy(addr[i].sa_data, &iw->spy_addr[i], ETHER_ADDR_LEN);
936*4882a593Smuzhiyun 		addr[i].sa_family = AF_UNIX;
937*4882a593Smuzhiyun 		memcpy(&qual[i], &iw->spy_qual[i], sizeof(struct iw_quality));
938*4882a593Smuzhiyun 		iw->spy_qual[i].updated = 0;
939*4882a593Smuzhiyun 	}
940*4882a593Smuzhiyun 
941*4882a593Smuzhiyun 	return 0;
942*4882a593Smuzhiyun }
943*4882a593Smuzhiyun 
944*4882a593Smuzhiyun static int
wl_iw_set_wap(struct net_device * dev,struct iw_request_info * info,struct sockaddr * awrq,char * extra)945*4882a593Smuzhiyun wl_iw_set_wap(
946*4882a593Smuzhiyun 	struct net_device *dev,
947*4882a593Smuzhiyun 	struct iw_request_info *info,
948*4882a593Smuzhiyun 	struct sockaddr *awrq,
949*4882a593Smuzhiyun 	char *extra
950*4882a593Smuzhiyun )
951*4882a593Smuzhiyun {
952*4882a593Smuzhiyun 	int error = -EINVAL;
953*4882a593Smuzhiyun 
954*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWAP\n", dev->name));
955*4882a593Smuzhiyun 
956*4882a593Smuzhiyun 	if (awrq->sa_family != ARPHRD_ETHER) {
957*4882a593Smuzhiyun 		WL_ERROR(("%s: Invalid Header...sa_family\n", __FUNCTION__));
958*4882a593Smuzhiyun 		return -EINVAL;
959*4882a593Smuzhiyun 	}
960*4882a593Smuzhiyun 
961*4882a593Smuzhiyun 	/* Ignore "auto" or "off" */
962*4882a593Smuzhiyun 	if (ETHER_ISBCAST(awrq->sa_data) || ETHER_ISNULLADDR(awrq->sa_data)) {
963*4882a593Smuzhiyun 		scb_val_t scbval;
964*4882a593Smuzhiyun 		bzero(&scbval, sizeof(scb_val_t));
965*4882a593Smuzhiyun 		if ((error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t)))) {
966*4882a593Smuzhiyun 			WL_ERROR(("%s: WLC_DISASSOC failed (%d).\n", __FUNCTION__, error));
967*4882a593Smuzhiyun 		}
968*4882a593Smuzhiyun 		return 0;
969*4882a593Smuzhiyun 	}
970*4882a593Smuzhiyun 	/* WL_ASSOC(("Assoc to %s\n", bcm_ether_ntoa((struct ether_addr *)&(awrq->sa_data),
971*4882a593Smuzhiyun 	 * eabuf)));
972*4882a593Smuzhiyun 	 */
973*4882a593Smuzhiyun 	/* Reassociate to the specified AP */
974*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_REASSOC, awrq->sa_data, ETHER_ADDR_LEN))) {
975*4882a593Smuzhiyun 		WL_ERROR(("%s: WLC_REASSOC failed (%d).\n", __FUNCTION__, error));
976*4882a593Smuzhiyun 		return error;
977*4882a593Smuzhiyun 	}
978*4882a593Smuzhiyun 
979*4882a593Smuzhiyun 	return 0;
980*4882a593Smuzhiyun }
981*4882a593Smuzhiyun 
982*4882a593Smuzhiyun static int
wl_iw_get_wap(struct net_device * dev,struct iw_request_info * info,struct sockaddr * awrq,char * extra)983*4882a593Smuzhiyun wl_iw_get_wap(
984*4882a593Smuzhiyun 	struct net_device *dev,
985*4882a593Smuzhiyun 	struct iw_request_info *info,
986*4882a593Smuzhiyun 	struct sockaddr *awrq,
987*4882a593Smuzhiyun 	char *extra
988*4882a593Smuzhiyun )
989*4882a593Smuzhiyun {
990*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWAP\n", dev->name));
991*4882a593Smuzhiyun 
992*4882a593Smuzhiyun 	awrq->sa_family = ARPHRD_ETHER;
993*4882a593Smuzhiyun 	memset(awrq->sa_data, 0, ETHER_ADDR_LEN);
994*4882a593Smuzhiyun 
995*4882a593Smuzhiyun 	/* Ignore error (may be down or disassociated) */
996*4882a593Smuzhiyun 	(void) dev_wlc_ioctl(dev, WLC_GET_BSSID, awrq->sa_data, ETHER_ADDR_LEN);
997*4882a593Smuzhiyun 
998*4882a593Smuzhiyun 	return 0;
999*4882a593Smuzhiyun }
1000*4882a593Smuzhiyun 
1001*4882a593Smuzhiyun #if WIRELESS_EXT > 17
1002*4882a593Smuzhiyun static int
wl_iw_mlme(struct net_device * dev,struct iw_request_info * info,struct sockaddr * awrq,char * extra)1003*4882a593Smuzhiyun wl_iw_mlme(
1004*4882a593Smuzhiyun 	struct net_device *dev,
1005*4882a593Smuzhiyun 	struct iw_request_info *info,
1006*4882a593Smuzhiyun 	struct sockaddr *awrq,
1007*4882a593Smuzhiyun 	char *extra
1008*4882a593Smuzhiyun )
1009*4882a593Smuzhiyun {
1010*4882a593Smuzhiyun 	struct iw_mlme *mlme;
1011*4882a593Smuzhiyun 	scb_val_t scbval;
1012*4882a593Smuzhiyun 	int error  = -EINVAL;
1013*4882a593Smuzhiyun 
1014*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWMLME\n", dev->name));
1015*4882a593Smuzhiyun 
1016*4882a593Smuzhiyun 	mlme = (struct iw_mlme *)extra;
1017*4882a593Smuzhiyun 	if (mlme == NULL) {
1018*4882a593Smuzhiyun 		WL_ERROR(("Invalid ioctl data.\n"));
1019*4882a593Smuzhiyun 		return error;
1020*4882a593Smuzhiyun 	}
1021*4882a593Smuzhiyun 
1022*4882a593Smuzhiyun 	scbval.val = mlme->reason_code;
1023*4882a593Smuzhiyun 	bcopy(&mlme->addr.sa_data, &scbval.ea, ETHER_ADDR_LEN);
1024*4882a593Smuzhiyun 
1025*4882a593Smuzhiyun 	if (mlme->cmd == IW_MLME_DISASSOC) {
1026*4882a593Smuzhiyun 		scbval.val = htod32(scbval.val);
1027*4882a593Smuzhiyun 		error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t));
1028*4882a593Smuzhiyun 	}
1029*4882a593Smuzhiyun 	else if (mlme->cmd == IW_MLME_DEAUTH) {
1030*4882a593Smuzhiyun 		scbval.val = htod32(scbval.val);
1031*4882a593Smuzhiyun 		error = dev_wlc_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scbval,
1032*4882a593Smuzhiyun 			sizeof(scb_val_t));
1033*4882a593Smuzhiyun 	}
1034*4882a593Smuzhiyun 	else {
1035*4882a593Smuzhiyun 		WL_ERROR(("%s: Invalid ioctl data.\n", __FUNCTION__));
1036*4882a593Smuzhiyun 		return error;
1037*4882a593Smuzhiyun 	}
1038*4882a593Smuzhiyun 
1039*4882a593Smuzhiyun 	return error;
1040*4882a593Smuzhiyun }
1041*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 17 */
1042*4882a593Smuzhiyun 
1043*4882a593Smuzhiyun static int
wl_iw_get_aplist(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)1044*4882a593Smuzhiyun wl_iw_get_aplist(
1045*4882a593Smuzhiyun 	struct net_device *dev,
1046*4882a593Smuzhiyun 	struct iw_request_info *info,
1047*4882a593Smuzhiyun 	struct iw_point *dwrq,
1048*4882a593Smuzhiyun 	char *extra
1049*4882a593Smuzhiyun )
1050*4882a593Smuzhiyun {
1051*4882a593Smuzhiyun 	wl_scan_results_t *list;
1052*4882a593Smuzhiyun 	struct sockaddr *addr = (struct sockaddr *) extra;
1053*4882a593Smuzhiyun 	struct iw_quality qual[IW_MAX_AP];
1054*4882a593Smuzhiyun 	wl_bss_info_t *bi = NULL;
1055*4882a593Smuzhiyun 	int error, i;
1056*4882a593Smuzhiyun 	uint buflen = dwrq->length;
1057*4882a593Smuzhiyun 
1058*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWAPLIST\n", dev->name));
1059*4882a593Smuzhiyun 
1060*4882a593Smuzhiyun 	if (!extra)
1061*4882a593Smuzhiyun 		return -EINVAL;
1062*4882a593Smuzhiyun 
1063*4882a593Smuzhiyun 	/* Get scan results (too large to put on the stack) */
1064*4882a593Smuzhiyun 	list = kmalloc(buflen, GFP_KERNEL);
1065*4882a593Smuzhiyun 	if (!list)
1066*4882a593Smuzhiyun 		return -ENOMEM;
1067*4882a593Smuzhiyun 	memset(list, 0, buflen);
1068*4882a593Smuzhiyun 	list->buflen = htod32(buflen);
1069*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, buflen))) {
1070*4882a593Smuzhiyun 		WL_ERROR(("%d: Scan results error %d\n", __LINE__, error));
1071*4882a593Smuzhiyun 		kfree(list);
1072*4882a593Smuzhiyun 		return error;
1073*4882a593Smuzhiyun 	}
1074*4882a593Smuzhiyun 	list->buflen = dtoh32(list->buflen);
1075*4882a593Smuzhiyun 	list->version = dtoh32(list->version);
1076*4882a593Smuzhiyun 	list->count = dtoh32(list->count);
1077*4882a593Smuzhiyun 	ASSERT(list->version == WL_BSS_INFO_VERSION);
1078*4882a593Smuzhiyun 
1079*4882a593Smuzhiyun 	for (i = 0, dwrq->length = 0; i < list->count && dwrq->length < IW_MAX_AP; i++) {
1080*4882a593Smuzhiyun 		bi = (bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) :
1081*4882a593Smuzhiyun 			(wl_bss_info_t*)list->bss_info);
1082*4882a593Smuzhiyun 		ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list +
1083*4882a593Smuzhiyun 			buflen));
1084*4882a593Smuzhiyun 
1085*4882a593Smuzhiyun 		/* Infrastructure only */
1086*4882a593Smuzhiyun 		if (!(dtoh16(bi->capability) & DOT11_CAP_ESS))
1087*4882a593Smuzhiyun 			continue;
1088*4882a593Smuzhiyun 
1089*4882a593Smuzhiyun 		/* BSSID */
1090*4882a593Smuzhiyun 		memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN);
1091*4882a593Smuzhiyun 		addr[dwrq->length].sa_family = ARPHRD_ETHER;
1092*4882a593Smuzhiyun 		qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI));
1093*4882a593Smuzhiyun 		qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI);
1094*4882a593Smuzhiyun 		qual[dwrq->length].noise = 0x100 + bi->phy_noise;
1095*4882a593Smuzhiyun 
1096*4882a593Smuzhiyun 		/* Updated qual, level, and noise */
1097*4882a593Smuzhiyun #if WIRELESS_EXT > 18
1098*4882a593Smuzhiyun 		qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
1099*4882a593Smuzhiyun #else
1100*4882a593Smuzhiyun 		qual[dwrq->length].updated = 7;
1101*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 18 */
1102*4882a593Smuzhiyun 
1103*4882a593Smuzhiyun 		dwrq->length++;
1104*4882a593Smuzhiyun 	}
1105*4882a593Smuzhiyun 
1106*4882a593Smuzhiyun 	kfree(list);
1107*4882a593Smuzhiyun 
1108*4882a593Smuzhiyun 	if (dwrq->length) {
1109*4882a593Smuzhiyun 		memcpy(&addr[dwrq->length], qual, sizeof(struct iw_quality) * dwrq->length);
1110*4882a593Smuzhiyun 		/* Provided qual */
1111*4882a593Smuzhiyun 		dwrq->flags = 1;
1112*4882a593Smuzhiyun 	}
1113*4882a593Smuzhiyun 
1114*4882a593Smuzhiyun 	return 0;
1115*4882a593Smuzhiyun }
1116*4882a593Smuzhiyun 
1117*4882a593Smuzhiyun static int
wl_iw_iscan_get_aplist(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)1118*4882a593Smuzhiyun wl_iw_iscan_get_aplist(
1119*4882a593Smuzhiyun 	struct net_device *dev,
1120*4882a593Smuzhiyun 	struct iw_request_info *info,
1121*4882a593Smuzhiyun 	struct iw_point *dwrq,
1122*4882a593Smuzhiyun 	char *extra
1123*4882a593Smuzhiyun )
1124*4882a593Smuzhiyun {
1125*4882a593Smuzhiyun 	wl_scan_results_t *list;
1126*4882a593Smuzhiyun 	iscan_buf_t * buf;
1127*4882a593Smuzhiyun 	iscan_info_t *iscan = g_iscan;
1128*4882a593Smuzhiyun 
1129*4882a593Smuzhiyun 	struct sockaddr *addr = (struct sockaddr *) extra;
1130*4882a593Smuzhiyun 	struct iw_quality qual[IW_MAX_AP];
1131*4882a593Smuzhiyun 	wl_bss_info_t *bi = NULL;
1132*4882a593Smuzhiyun 	int i;
1133*4882a593Smuzhiyun 
1134*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWAPLIST\n", dev->name));
1135*4882a593Smuzhiyun 
1136*4882a593Smuzhiyun 	if (!extra)
1137*4882a593Smuzhiyun 		return -EINVAL;
1138*4882a593Smuzhiyun 
1139*4882a593Smuzhiyun 	if ((!iscan) || (iscan->sysioc_pid < 0)) {
1140*4882a593Smuzhiyun 		return wl_iw_get_aplist(dev, info, dwrq, extra);
1141*4882a593Smuzhiyun 	}
1142*4882a593Smuzhiyun 
1143*4882a593Smuzhiyun 	buf = iscan->list_hdr;
1144*4882a593Smuzhiyun 	/* Get scan results (too large to put on the stack) */
1145*4882a593Smuzhiyun 	while (buf) {
1146*4882a593Smuzhiyun 	    list = &((wl_iscan_results_t*)buf->iscan_buf)->results;
1147*4882a593Smuzhiyun 	    ASSERT(list->version == WL_BSS_INFO_VERSION);
1148*4882a593Smuzhiyun 
1149*4882a593Smuzhiyun 	    bi = NULL;
1150*4882a593Smuzhiyun 	for (i = 0, dwrq->length = 0; i < list->count && dwrq->length < IW_MAX_AP; i++) {
1151*4882a593Smuzhiyun 		bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info;
1152*4882a593Smuzhiyun 		ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list +
1153*4882a593Smuzhiyun 			WLC_IW_ISCAN_MAXLEN));
1154*4882a593Smuzhiyun 
1155*4882a593Smuzhiyun 		/* Infrastructure only */
1156*4882a593Smuzhiyun 		if (!(dtoh16(bi->capability) & DOT11_CAP_ESS))
1157*4882a593Smuzhiyun 			continue;
1158*4882a593Smuzhiyun 
1159*4882a593Smuzhiyun 		/* BSSID */
1160*4882a593Smuzhiyun 		memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN);
1161*4882a593Smuzhiyun 		addr[dwrq->length].sa_family = ARPHRD_ETHER;
1162*4882a593Smuzhiyun 		qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI));
1163*4882a593Smuzhiyun 		qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI);
1164*4882a593Smuzhiyun 		qual[dwrq->length].noise = 0x100 + bi->phy_noise;
1165*4882a593Smuzhiyun 
1166*4882a593Smuzhiyun 		/* Updated qual, level, and noise */
1167*4882a593Smuzhiyun #if WIRELESS_EXT > 18
1168*4882a593Smuzhiyun 		qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
1169*4882a593Smuzhiyun #else
1170*4882a593Smuzhiyun 		qual[dwrq->length].updated = 7;
1171*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 18 */
1172*4882a593Smuzhiyun 
1173*4882a593Smuzhiyun 		dwrq->length++;
1174*4882a593Smuzhiyun 	    }
1175*4882a593Smuzhiyun 	    buf = buf->next;
1176*4882a593Smuzhiyun 	}
1177*4882a593Smuzhiyun 	if (dwrq->length) {
1178*4882a593Smuzhiyun 		memcpy(&addr[dwrq->length], qual, sizeof(struct iw_quality) * dwrq->length);
1179*4882a593Smuzhiyun 		/* Provided qual */
1180*4882a593Smuzhiyun 		dwrq->flags = 1;
1181*4882a593Smuzhiyun 	}
1182*4882a593Smuzhiyun 
1183*4882a593Smuzhiyun 	return 0;
1184*4882a593Smuzhiyun }
1185*4882a593Smuzhiyun 
1186*4882a593Smuzhiyun #if WIRELESS_EXT > 13
1187*4882a593Smuzhiyun static int
wl_iw_set_scan(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)1188*4882a593Smuzhiyun wl_iw_set_scan(
1189*4882a593Smuzhiyun 	struct net_device *dev,
1190*4882a593Smuzhiyun 	struct iw_request_info *info,
1191*4882a593Smuzhiyun 	union iwreq_data *wrqu,
1192*4882a593Smuzhiyun 	char *extra
1193*4882a593Smuzhiyun )
1194*4882a593Smuzhiyun {
1195*4882a593Smuzhiyun 	wlc_ssid_t ssid;
1196*4882a593Smuzhiyun 
1197*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWSCAN\n", dev->name));
1198*4882a593Smuzhiyun 
1199*4882a593Smuzhiyun 	/* default Broadcast scan */
1200*4882a593Smuzhiyun 	memset(&ssid, 0, sizeof(ssid));
1201*4882a593Smuzhiyun 
1202*4882a593Smuzhiyun #if WIRELESS_EXT > 17
1203*4882a593Smuzhiyun 	/* check for given essid */
1204*4882a593Smuzhiyun 	if (wrqu->data.length == sizeof(struct iw_scan_req)) {
1205*4882a593Smuzhiyun 		if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
1206*4882a593Smuzhiyun 			struct iw_scan_req *req = (struct iw_scan_req *)extra;
1207*4882a593Smuzhiyun 			ssid.SSID_len = MIN(sizeof(ssid.SSID), req->essid_len);
1208*4882a593Smuzhiyun 			memcpy(ssid.SSID, req->essid, ssid.SSID_len);
1209*4882a593Smuzhiyun 			ssid.SSID_len = htod32(ssid.SSID_len);
1210*4882a593Smuzhiyun 		}
1211*4882a593Smuzhiyun 	}
1212*4882a593Smuzhiyun #endif // endif
1213*4882a593Smuzhiyun 	/* Ignore error (most likely scan in progress) */
1214*4882a593Smuzhiyun 	(void) dev_wlc_ioctl(dev, WLC_SCAN, &ssid, sizeof(ssid));
1215*4882a593Smuzhiyun 
1216*4882a593Smuzhiyun 	return 0;
1217*4882a593Smuzhiyun }
1218*4882a593Smuzhiyun 
1219*4882a593Smuzhiyun static int
wl_iw_iscan_set_scan(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)1220*4882a593Smuzhiyun wl_iw_iscan_set_scan(
1221*4882a593Smuzhiyun 	struct net_device *dev,
1222*4882a593Smuzhiyun 	struct iw_request_info *info,
1223*4882a593Smuzhiyun 	union iwreq_data *wrqu,
1224*4882a593Smuzhiyun 	char *extra
1225*4882a593Smuzhiyun )
1226*4882a593Smuzhiyun {
1227*4882a593Smuzhiyun 	wlc_ssid_t ssid;
1228*4882a593Smuzhiyun 	iscan_info_t *iscan = g_iscan;
1229*4882a593Smuzhiyun 
1230*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWSCAN\n", dev->name));
1231*4882a593Smuzhiyun 
1232*4882a593Smuzhiyun 	/* use backup if our thread is not successful */
1233*4882a593Smuzhiyun 	if ((!iscan) || (iscan->sysioc_pid < 0)) {
1234*4882a593Smuzhiyun 		return wl_iw_set_scan(dev, info, wrqu, extra);
1235*4882a593Smuzhiyun 	}
1236*4882a593Smuzhiyun 	if (iscan->iscan_state == ISCAN_STATE_SCANING) {
1237*4882a593Smuzhiyun 		return 0;
1238*4882a593Smuzhiyun 	}
1239*4882a593Smuzhiyun 
1240*4882a593Smuzhiyun 	/* default Broadcast scan */
1241*4882a593Smuzhiyun 	memset(&ssid, 0, sizeof(ssid));
1242*4882a593Smuzhiyun 
1243*4882a593Smuzhiyun #if WIRELESS_EXT > 17
1244*4882a593Smuzhiyun 	/* check for given essid */
1245*4882a593Smuzhiyun 	if (wrqu->data.length == sizeof(struct iw_scan_req)) {
1246*4882a593Smuzhiyun 		if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
1247*4882a593Smuzhiyun 			struct iw_scan_req *req = (struct iw_scan_req *)extra;
1248*4882a593Smuzhiyun 			ssid.SSID_len = MIN(sizeof(ssid.SSID), req->essid_len);
1249*4882a593Smuzhiyun 			memcpy(ssid.SSID, req->essid, ssid.SSID_len);
1250*4882a593Smuzhiyun 			ssid.SSID_len = htod32(ssid.SSID_len);
1251*4882a593Smuzhiyun 		}
1252*4882a593Smuzhiyun 	}
1253*4882a593Smuzhiyun #endif // endif
1254*4882a593Smuzhiyun 
1255*4882a593Smuzhiyun 	iscan->list_cur = iscan->list_hdr;
1256*4882a593Smuzhiyun 	iscan->iscan_state = ISCAN_STATE_SCANING;
1257*4882a593Smuzhiyun 
1258*4882a593Smuzhiyun 	wl_iw_set_event_mask(dev);
1259*4882a593Smuzhiyun 	wl_iw_iscan(iscan, &ssid, WL_SCAN_ACTION_START);
1260*4882a593Smuzhiyun #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
1261*4882a593Smuzhiyun 	iscan->timer.expires = jiffies + msecs_to_jiffies(iscan->timer_ms);
1262*4882a593Smuzhiyun #else
1263*4882a593Smuzhiyun 	iscan->timer.timer.expires = jiffies + msecs_to_jiffies(iscan->timer_ms);
1264*4882a593Smuzhiyun #endif // endif
1265*4882a593Smuzhiyun 	add_timer(&iscan->timer);
1266*4882a593Smuzhiyun 	iscan->timer_on = 1;
1267*4882a593Smuzhiyun 
1268*4882a593Smuzhiyun 	return 0;
1269*4882a593Smuzhiyun }
1270*4882a593Smuzhiyun 
1271*4882a593Smuzhiyun #if WIRELESS_EXT > 17
1272*4882a593Smuzhiyun static bool
ie_is_wpa_ie(uint8 ** wpaie,uint8 ** tlvs,int * tlvs_len)1273*4882a593Smuzhiyun ie_is_wpa_ie(uint8 **wpaie, uint8 **tlvs, int *tlvs_len)
1274*4882a593Smuzhiyun {
1275*4882a593Smuzhiyun /* Is this body of this tlvs entry a WPA entry? If */
1276*4882a593Smuzhiyun /* not update the tlvs buffer pointer/length */
1277*4882a593Smuzhiyun 	uint8 *ie = *wpaie;
1278*4882a593Smuzhiyun 
1279*4882a593Smuzhiyun 	/* If the contents match the WPA_OUI and type=1 */
1280*4882a593Smuzhiyun 	if ((ie[1] >= 6) &&
1281*4882a593Smuzhiyun 		!bcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x01"), 4)) {
1282*4882a593Smuzhiyun 		return TRUE;
1283*4882a593Smuzhiyun 	}
1284*4882a593Smuzhiyun 
1285*4882a593Smuzhiyun 	/* point to the next ie */
1286*4882a593Smuzhiyun 	ie += ie[1] + 2;
1287*4882a593Smuzhiyun 	/* calculate the length of the rest of the buffer */
1288*4882a593Smuzhiyun 	*tlvs_len -= (int)(ie - *tlvs);
1289*4882a593Smuzhiyun 	/* update the pointer to the start of the buffer */
1290*4882a593Smuzhiyun 	*tlvs = ie;
1291*4882a593Smuzhiyun 	return FALSE;
1292*4882a593Smuzhiyun }
1293*4882a593Smuzhiyun 
1294*4882a593Smuzhiyun static bool
ie_is_wps_ie(uint8 ** wpsie,uint8 ** tlvs,int * tlvs_len)1295*4882a593Smuzhiyun ie_is_wps_ie(uint8 **wpsie, uint8 **tlvs, int *tlvs_len)
1296*4882a593Smuzhiyun {
1297*4882a593Smuzhiyun /* Is this body of this tlvs entry a WPS entry? If */
1298*4882a593Smuzhiyun /* not update the tlvs buffer pointer/length */
1299*4882a593Smuzhiyun 	uint8 *ie = *wpsie;
1300*4882a593Smuzhiyun 
1301*4882a593Smuzhiyun 	/* If the contents match the WPA_OUI and type=4 */
1302*4882a593Smuzhiyun 	if ((ie[1] >= 4) &&
1303*4882a593Smuzhiyun 		!bcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x04"), 4)) {
1304*4882a593Smuzhiyun 		return TRUE;
1305*4882a593Smuzhiyun 	}
1306*4882a593Smuzhiyun 
1307*4882a593Smuzhiyun 	/* point to the next ie */
1308*4882a593Smuzhiyun 	ie += ie[1] + 2;
1309*4882a593Smuzhiyun 	/* calculate the length of the rest of the buffer */
1310*4882a593Smuzhiyun 	*tlvs_len -= (int)(ie - *tlvs);
1311*4882a593Smuzhiyun 	/* update the pointer to the start of the buffer */
1312*4882a593Smuzhiyun 	*tlvs = ie;
1313*4882a593Smuzhiyun 	return FALSE;
1314*4882a593Smuzhiyun }
1315*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 17 */
1316*4882a593Smuzhiyun 
1317*4882a593Smuzhiyun #ifdef BCMWAPI_WPI
_wpa_snprintf_hex(char * buf,size_t buf_size,const u8 * data,size_t len,int uppercase)1318*4882a593Smuzhiyun static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data,
1319*4882a593Smuzhiyun 	size_t len, int uppercase)
1320*4882a593Smuzhiyun {
1321*4882a593Smuzhiyun 	size_t i;
1322*4882a593Smuzhiyun 	char *pos = buf, *end = buf + buf_size;
1323*4882a593Smuzhiyun 	int ret;
1324*4882a593Smuzhiyun 	if (buf_size == 0)
1325*4882a593Smuzhiyun 		return 0;
1326*4882a593Smuzhiyun 	for (i = 0; i < len; i++) {
1327*4882a593Smuzhiyun 		ret = snprintf(pos, end - pos, uppercase ? "%02X" : "%02x",
1328*4882a593Smuzhiyun 			data[i]);
1329*4882a593Smuzhiyun 		if (ret < 0 || ret >= end - pos) {
1330*4882a593Smuzhiyun 			end[-1] = '\0';
1331*4882a593Smuzhiyun 			return pos - buf;
1332*4882a593Smuzhiyun 		}
1333*4882a593Smuzhiyun 		pos += ret;
1334*4882a593Smuzhiyun 	}
1335*4882a593Smuzhiyun 	end[-1] = '\0';
1336*4882a593Smuzhiyun 	return pos - buf;
1337*4882a593Smuzhiyun }
1338*4882a593Smuzhiyun 
1339*4882a593Smuzhiyun /**
1340*4882a593Smuzhiyun  * wpa_snprintf_hex - Print data as a hex string into a buffer
1341*4882a593Smuzhiyun  * @buf: Memory area to use as the output buffer
1342*4882a593Smuzhiyun  * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1)
1343*4882a593Smuzhiyun  * @data: Data to be printed
1344*4882a593Smuzhiyun  * @len: Length of data in bytes
1345*4882a593Smuzhiyun  * Returns: Number of bytes written
1346*4882a593Smuzhiyun  */
1347*4882a593Smuzhiyun static int
wpa_snprintf_hex(char * buf,size_t buf_size,const u8 * data,size_t len)1348*4882a593Smuzhiyun wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len)
1349*4882a593Smuzhiyun {
1350*4882a593Smuzhiyun 	return _wpa_snprintf_hex(buf, buf_size, data, len, 0);
1351*4882a593Smuzhiyun }
1352*4882a593Smuzhiyun #endif /* BCMWAPI_WPI */
1353*4882a593Smuzhiyun 
1354*4882a593Smuzhiyun static int
wl_iw_handle_scanresults_ies(char ** event_p,char * end,struct iw_request_info * info,wl_bss_info_t * bi)1355*4882a593Smuzhiyun wl_iw_handle_scanresults_ies(char **event_p, char *end,
1356*4882a593Smuzhiyun 	struct iw_request_info *info, wl_bss_info_t *bi)
1357*4882a593Smuzhiyun {
1358*4882a593Smuzhiyun #if WIRELESS_EXT > 17
1359*4882a593Smuzhiyun 	struct iw_event	iwe;
1360*4882a593Smuzhiyun 	char *event;
1361*4882a593Smuzhiyun #ifdef BCMWAPI_WPI
1362*4882a593Smuzhiyun 	char *buf;
1363*4882a593Smuzhiyun 	int custom_event_len;
1364*4882a593Smuzhiyun #endif // endif
1365*4882a593Smuzhiyun 
1366*4882a593Smuzhiyun 	event = *event_p;
1367*4882a593Smuzhiyun 	if (bi->ie_length) {
1368*4882a593Smuzhiyun 		/* look for wpa/rsn ies in the ie list... */
1369*4882a593Smuzhiyun 		bcm_tlv_t *ie;
1370*4882a593Smuzhiyun 		uint8 *ptr = ((uint8 *)bi) + bi->ie_offset;
1371*4882a593Smuzhiyun 		uint ptr_len = bi->ie_length;
1372*4882a593Smuzhiyun 
1373*4882a593Smuzhiyun 		/* OSEN IE */
1374*4882a593Smuzhiyun 		if ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_VS_ID)) &&
1375*4882a593Smuzhiyun 			ie->len > WFA_OUI_LEN + 1 &&
1376*4882a593Smuzhiyun 			!bcmp((const void *)&ie->data[0], (const void *)WFA_OUI, WFA_OUI_LEN) &&
1377*4882a593Smuzhiyun 			ie->data[WFA_OUI_LEN] == WFA_OUI_TYPE_OSEN) {
1378*4882a593Smuzhiyun 			iwe.cmd = IWEVGENIE;
1379*4882a593Smuzhiyun 			iwe.u.data.length = ie->len + 2;
1380*4882a593Smuzhiyun 			event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
1381*4882a593Smuzhiyun 		}
1382*4882a593Smuzhiyun 		ptr = ((uint8 *)bi) + bi->ie_offset;
1383*4882a593Smuzhiyun 
1384*4882a593Smuzhiyun 		if ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_RSN_ID))) {
1385*4882a593Smuzhiyun 			iwe.cmd = IWEVGENIE;
1386*4882a593Smuzhiyun 			iwe.u.data.length = ie->len + 2;
1387*4882a593Smuzhiyun 			event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
1388*4882a593Smuzhiyun 		}
1389*4882a593Smuzhiyun 		ptr = ((uint8 *)bi) + bi->ie_offset;
1390*4882a593Smuzhiyun 
1391*4882a593Smuzhiyun 		if ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_MDIE_ID))) {
1392*4882a593Smuzhiyun 			iwe.cmd = IWEVGENIE;
1393*4882a593Smuzhiyun 			iwe.u.data.length = ie->len + 2;
1394*4882a593Smuzhiyun 			event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
1395*4882a593Smuzhiyun 		}
1396*4882a593Smuzhiyun 		ptr = ((uint8 *)bi) + bi->ie_offset;
1397*4882a593Smuzhiyun 
1398*4882a593Smuzhiyun 		while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) {
1399*4882a593Smuzhiyun 			/* look for WPS IE */
1400*4882a593Smuzhiyun 			if (ie_is_wps_ie(((uint8 **)&ie), &ptr, &ptr_len)) {
1401*4882a593Smuzhiyun 				iwe.cmd = IWEVGENIE;
1402*4882a593Smuzhiyun 				iwe.u.data.length = ie->len + 2;
1403*4882a593Smuzhiyun 				event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
1404*4882a593Smuzhiyun 				break;
1405*4882a593Smuzhiyun 			}
1406*4882a593Smuzhiyun 		}
1407*4882a593Smuzhiyun 
1408*4882a593Smuzhiyun 		ptr = ((uint8 *)bi) + bi->ie_offset;
1409*4882a593Smuzhiyun 		ptr_len = bi->ie_length;
1410*4882a593Smuzhiyun 		while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) {
1411*4882a593Smuzhiyun 			if (ie_is_wpa_ie(((uint8 **)&ie), &ptr, &ptr_len)) {
1412*4882a593Smuzhiyun 				iwe.cmd = IWEVGENIE;
1413*4882a593Smuzhiyun 				iwe.u.data.length = ie->len + 2;
1414*4882a593Smuzhiyun 				event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
1415*4882a593Smuzhiyun 				break;
1416*4882a593Smuzhiyun 			}
1417*4882a593Smuzhiyun 		}
1418*4882a593Smuzhiyun 
1419*4882a593Smuzhiyun #ifdef BCMWAPI_WPI
1420*4882a593Smuzhiyun 		ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t);
1421*4882a593Smuzhiyun 		ptr_len = bi->ie_length;
1422*4882a593Smuzhiyun 
1423*4882a593Smuzhiyun 		while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WAPI_ID))) {
1424*4882a593Smuzhiyun 			WL_TRACE(("%s: found a WAPI IE...\n", __FUNCTION__));
1425*4882a593Smuzhiyun #ifdef WAPI_IE_USE_GENIE
1426*4882a593Smuzhiyun 			iwe.cmd = IWEVGENIE;
1427*4882a593Smuzhiyun 			iwe.u.data.length = ie->len + 2;
1428*4882a593Smuzhiyun 			event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
1429*4882a593Smuzhiyun #else /* using CUSTOM event */
1430*4882a593Smuzhiyun 			iwe.cmd = IWEVCUSTOM;
1431*4882a593Smuzhiyun 			custom_event_len = strlen("wapi_ie=") + 2*(ie->len + 2);
1432*4882a593Smuzhiyun 			iwe.u.data.length = custom_event_len;
1433*4882a593Smuzhiyun 
1434*4882a593Smuzhiyun 			buf = kmalloc(custom_event_len+1, GFP_KERNEL);
1435*4882a593Smuzhiyun 			if (buf == NULL)
1436*4882a593Smuzhiyun 			{
1437*4882a593Smuzhiyun 				WL_ERROR(("malloc(%d) returned NULL...\n", custom_event_len));
1438*4882a593Smuzhiyun 				break;
1439*4882a593Smuzhiyun 			}
1440*4882a593Smuzhiyun 
1441*4882a593Smuzhiyun 			memcpy(buf, "wapi_ie=", 8);
1442*4882a593Smuzhiyun 			wpa_snprintf_hex(buf + 8, 2+1, &(ie->id), 1);
1443*4882a593Smuzhiyun 			wpa_snprintf_hex(buf + 10, 2+1, &(ie->len), 1);
1444*4882a593Smuzhiyun 			wpa_snprintf_hex(buf + 12, 2*ie->len+1, ie->data, ie->len);
1445*4882a593Smuzhiyun 			event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, buf);
1446*4882a593Smuzhiyun 			kfree(buf);
1447*4882a593Smuzhiyun #endif /* WAPI_IE_USE_GENIE */
1448*4882a593Smuzhiyun 			break;
1449*4882a593Smuzhiyun 		}
1450*4882a593Smuzhiyun #endif /* BCMWAPI_WPI */
1451*4882a593Smuzhiyun 	*event_p = event;
1452*4882a593Smuzhiyun 	}
1453*4882a593Smuzhiyun 
1454*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 17 */
1455*4882a593Smuzhiyun 	return 0;
1456*4882a593Smuzhiyun }
1457*4882a593Smuzhiyun static int
wl_iw_get_scan(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)1458*4882a593Smuzhiyun wl_iw_get_scan(
1459*4882a593Smuzhiyun 	struct net_device *dev,
1460*4882a593Smuzhiyun 	struct iw_request_info *info,
1461*4882a593Smuzhiyun 	struct iw_point *dwrq,
1462*4882a593Smuzhiyun 	char *extra
1463*4882a593Smuzhiyun )
1464*4882a593Smuzhiyun {
1465*4882a593Smuzhiyun 	channel_info_t ci;
1466*4882a593Smuzhiyun 	wl_scan_results_t *list;
1467*4882a593Smuzhiyun 	struct iw_event	iwe;
1468*4882a593Smuzhiyun 	wl_bss_info_t *bi = NULL;
1469*4882a593Smuzhiyun 	int error, i, j;
1470*4882a593Smuzhiyun 	char *event = extra, *end = extra + dwrq->length, *value;
1471*4882a593Smuzhiyun 	uint buflen = dwrq->length;
1472*4882a593Smuzhiyun 
1473*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWSCAN\n", dev->name));
1474*4882a593Smuzhiyun 
1475*4882a593Smuzhiyun 	if (!extra)
1476*4882a593Smuzhiyun 		return -EINVAL;
1477*4882a593Smuzhiyun 
1478*4882a593Smuzhiyun 	/* Check for scan in progress */
1479*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci))))
1480*4882a593Smuzhiyun 		return error;
1481*4882a593Smuzhiyun 	ci.scan_channel = dtoh32(ci.scan_channel);
1482*4882a593Smuzhiyun 	if (ci.scan_channel)
1483*4882a593Smuzhiyun 		return -EAGAIN;
1484*4882a593Smuzhiyun 
1485*4882a593Smuzhiyun 	/* Get scan results (too large to put on the stack) */
1486*4882a593Smuzhiyun 	list = kmalloc(buflen, GFP_KERNEL);
1487*4882a593Smuzhiyun 	if (!list)
1488*4882a593Smuzhiyun 		return -ENOMEM;
1489*4882a593Smuzhiyun 	memset(list, 0, buflen);
1490*4882a593Smuzhiyun 	list->buflen = htod32(buflen);
1491*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, buflen))) {
1492*4882a593Smuzhiyun 		kfree(list);
1493*4882a593Smuzhiyun 		return error;
1494*4882a593Smuzhiyun 	}
1495*4882a593Smuzhiyun 	list->buflen = dtoh32(list->buflen);
1496*4882a593Smuzhiyun 	list->version = dtoh32(list->version);
1497*4882a593Smuzhiyun 	list->count = dtoh32(list->count);
1498*4882a593Smuzhiyun 
1499*4882a593Smuzhiyun 	ASSERT(list->version == WL_BSS_INFO_VERSION);
1500*4882a593Smuzhiyun 
1501*4882a593Smuzhiyun 	for (i = 0; i < list->count && i < IW_MAX_AP; i++) {
1502*4882a593Smuzhiyun 		bi = (bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) :
1503*4882a593Smuzhiyun 			(wl_bss_info_t *)list->bss_info);
1504*4882a593Smuzhiyun 		ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list +
1505*4882a593Smuzhiyun 			buflen));
1506*4882a593Smuzhiyun 
1507*4882a593Smuzhiyun 		/* First entry must be the BSSID */
1508*4882a593Smuzhiyun 		iwe.cmd = SIOCGIWAP;
1509*4882a593Smuzhiyun 		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
1510*4882a593Smuzhiyun 		memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN);
1511*4882a593Smuzhiyun 		event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN);
1512*4882a593Smuzhiyun 
1513*4882a593Smuzhiyun 		/* SSID */
1514*4882a593Smuzhiyun 		iwe.u.data.length = dtoh32(bi->SSID_len);
1515*4882a593Smuzhiyun 		iwe.cmd = SIOCGIWESSID;
1516*4882a593Smuzhiyun 		iwe.u.data.flags = 1;
1517*4882a593Smuzhiyun 		event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID);
1518*4882a593Smuzhiyun 
1519*4882a593Smuzhiyun 		/* Mode */
1520*4882a593Smuzhiyun 		if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) {
1521*4882a593Smuzhiyun 			iwe.cmd = SIOCGIWMODE;
1522*4882a593Smuzhiyun 			if (dtoh16(bi->capability) & DOT11_CAP_ESS)
1523*4882a593Smuzhiyun 				iwe.u.mode = IW_MODE_INFRA;
1524*4882a593Smuzhiyun 			else
1525*4882a593Smuzhiyun 				iwe.u.mode = IW_MODE_ADHOC;
1526*4882a593Smuzhiyun 			event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN);
1527*4882a593Smuzhiyun 		}
1528*4882a593Smuzhiyun 
1529*4882a593Smuzhiyun 		/* Channel */
1530*4882a593Smuzhiyun 		iwe.cmd = SIOCGIWFREQ;
1531*4882a593Smuzhiyun 
1532*4882a593Smuzhiyun 		iwe.u.freq.m = wf_channel2mhz(CHSPEC_CHANNEL(bi->chanspec),
1533*4882a593Smuzhiyun 			(CHSPEC_IS2G(bi->chanspec)) ?
1534*4882a593Smuzhiyun 			WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G);
1535*4882a593Smuzhiyun 		iwe.u.freq.e = 6;
1536*4882a593Smuzhiyun 		event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN);
1537*4882a593Smuzhiyun 
1538*4882a593Smuzhiyun 		/* Channel quality */
1539*4882a593Smuzhiyun 		iwe.cmd = IWEVQUAL;
1540*4882a593Smuzhiyun 		iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI));
1541*4882a593Smuzhiyun 		iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI);
1542*4882a593Smuzhiyun 		iwe.u.qual.noise = 0x100 + bi->phy_noise;
1543*4882a593Smuzhiyun 		event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN);
1544*4882a593Smuzhiyun 
1545*4882a593Smuzhiyun 		 wl_iw_handle_scanresults_ies(&event, end, info, bi);
1546*4882a593Smuzhiyun 
1547*4882a593Smuzhiyun 		/* Encryption */
1548*4882a593Smuzhiyun 		iwe.cmd = SIOCGIWENCODE;
1549*4882a593Smuzhiyun 		if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY)
1550*4882a593Smuzhiyun 			iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
1551*4882a593Smuzhiyun 		else
1552*4882a593Smuzhiyun 			iwe.u.data.flags = IW_ENCODE_DISABLED;
1553*4882a593Smuzhiyun 		iwe.u.data.length = 0;
1554*4882a593Smuzhiyun 		event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event);
1555*4882a593Smuzhiyun 
1556*4882a593Smuzhiyun 		/* Rates */
1557*4882a593Smuzhiyun 		if (bi->rateset.count) {
1558*4882a593Smuzhiyun 			value = event + IW_EV_LCP_LEN;
1559*4882a593Smuzhiyun 			iwe.cmd = SIOCGIWRATE;
1560*4882a593Smuzhiyun 			/* Those two flags are ignored... */
1561*4882a593Smuzhiyun 			iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
1562*4882a593Smuzhiyun 			for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) {
1563*4882a593Smuzhiyun 				iwe.u.bitrate.value = (bi->rateset.rates[j] & 0x7f) * 500000;
1564*4882a593Smuzhiyun 				value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe,
1565*4882a593Smuzhiyun 					IW_EV_PARAM_LEN);
1566*4882a593Smuzhiyun 			}
1567*4882a593Smuzhiyun 			event = value;
1568*4882a593Smuzhiyun 		}
1569*4882a593Smuzhiyun 	}
1570*4882a593Smuzhiyun 
1571*4882a593Smuzhiyun 	kfree(list);
1572*4882a593Smuzhiyun 
1573*4882a593Smuzhiyun 	dwrq->length = event - extra;
1574*4882a593Smuzhiyun 	dwrq->flags = 0;	/* todo */
1575*4882a593Smuzhiyun 
1576*4882a593Smuzhiyun 	return 0;
1577*4882a593Smuzhiyun }
1578*4882a593Smuzhiyun 
1579*4882a593Smuzhiyun static int
wl_iw_iscan_get_scan(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)1580*4882a593Smuzhiyun wl_iw_iscan_get_scan(
1581*4882a593Smuzhiyun 	struct net_device *dev,
1582*4882a593Smuzhiyun 	struct iw_request_info *info,
1583*4882a593Smuzhiyun 	struct iw_point *dwrq,
1584*4882a593Smuzhiyun 	char *extra
1585*4882a593Smuzhiyun )
1586*4882a593Smuzhiyun {
1587*4882a593Smuzhiyun 	wl_scan_results_t *list;
1588*4882a593Smuzhiyun 	struct iw_event	iwe;
1589*4882a593Smuzhiyun 	wl_bss_info_t *bi = NULL;
1590*4882a593Smuzhiyun 	int ii, j;
1591*4882a593Smuzhiyun 	int apcnt;
1592*4882a593Smuzhiyun 	char *event = extra, *end = extra + dwrq->length, *value;
1593*4882a593Smuzhiyun 	iscan_info_t *iscan = g_iscan;
1594*4882a593Smuzhiyun 	iscan_buf_t * p_buf;
1595*4882a593Smuzhiyun 
1596*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWSCAN\n", dev->name));
1597*4882a593Smuzhiyun 
1598*4882a593Smuzhiyun 	if (!extra)
1599*4882a593Smuzhiyun 		return -EINVAL;
1600*4882a593Smuzhiyun 
1601*4882a593Smuzhiyun 	/* use backup if our thread is not successful */
1602*4882a593Smuzhiyun 	if ((!iscan) || (iscan->sysioc_pid < 0)) {
1603*4882a593Smuzhiyun 		return wl_iw_get_scan(dev, info, dwrq, extra);
1604*4882a593Smuzhiyun 	}
1605*4882a593Smuzhiyun 
1606*4882a593Smuzhiyun 	/* Check for scan in progress */
1607*4882a593Smuzhiyun 	if (iscan->iscan_state == ISCAN_STATE_SCANING)
1608*4882a593Smuzhiyun 		return -EAGAIN;
1609*4882a593Smuzhiyun 
1610*4882a593Smuzhiyun 	apcnt = 0;
1611*4882a593Smuzhiyun 	p_buf = iscan->list_hdr;
1612*4882a593Smuzhiyun 	/* Get scan results */
1613*4882a593Smuzhiyun 	while (p_buf != iscan->list_cur) {
1614*4882a593Smuzhiyun 	    list = &((wl_iscan_results_t*)p_buf->iscan_buf)->results;
1615*4882a593Smuzhiyun 
1616*4882a593Smuzhiyun 	    if (list->version != WL_BSS_INFO_VERSION) {
1617*4882a593Smuzhiyun 		WL_ERROR(("list->version %d != WL_BSS_INFO_VERSION\n", list->version));
1618*4882a593Smuzhiyun 	    }
1619*4882a593Smuzhiyun 
1620*4882a593Smuzhiyun 	    bi = NULL;
1621*4882a593Smuzhiyun 	    for (ii = 0; ii < list->count && apcnt < IW_MAX_AP; apcnt++, ii++) {
1622*4882a593Smuzhiyun 		bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info;
1623*4882a593Smuzhiyun 		ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list +
1624*4882a593Smuzhiyun 			WLC_IW_ISCAN_MAXLEN));
1625*4882a593Smuzhiyun 
1626*4882a593Smuzhiyun 		/* overflow check cover fields before wpa IEs */
1627*4882a593Smuzhiyun 		if (event + ETHER_ADDR_LEN + bi->SSID_len + IW_EV_UINT_LEN + IW_EV_FREQ_LEN +
1628*4882a593Smuzhiyun 			IW_EV_QUAL_LEN >= end)
1629*4882a593Smuzhiyun 			return -E2BIG;
1630*4882a593Smuzhiyun 		/* First entry must be the BSSID */
1631*4882a593Smuzhiyun 		iwe.cmd = SIOCGIWAP;
1632*4882a593Smuzhiyun 		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
1633*4882a593Smuzhiyun 		memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN);
1634*4882a593Smuzhiyun 		event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN);
1635*4882a593Smuzhiyun 
1636*4882a593Smuzhiyun 		/* SSID */
1637*4882a593Smuzhiyun 		iwe.u.data.length = dtoh32(bi->SSID_len);
1638*4882a593Smuzhiyun 		iwe.cmd = SIOCGIWESSID;
1639*4882a593Smuzhiyun 		iwe.u.data.flags = 1;
1640*4882a593Smuzhiyun 		event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID);
1641*4882a593Smuzhiyun 
1642*4882a593Smuzhiyun 		/* Mode */
1643*4882a593Smuzhiyun 		if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) {
1644*4882a593Smuzhiyun 			iwe.cmd = SIOCGIWMODE;
1645*4882a593Smuzhiyun 			if (dtoh16(bi->capability) & DOT11_CAP_ESS)
1646*4882a593Smuzhiyun 				iwe.u.mode = IW_MODE_INFRA;
1647*4882a593Smuzhiyun 			else
1648*4882a593Smuzhiyun 				iwe.u.mode = IW_MODE_ADHOC;
1649*4882a593Smuzhiyun 			event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN);
1650*4882a593Smuzhiyun 		}
1651*4882a593Smuzhiyun 
1652*4882a593Smuzhiyun 		/* Channel */
1653*4882a593Smuzhiyun 		iwe.cmd = SIOCGIWFREQ;
1654*4882a593Smuzhiyun 
1655*4882a593Smuzhiyun 		iwe.u.freq.m = wf_channel2mhz(CHSPEC_CHANNEL(bi->chanspec),
1656*4882a593Smuzhiyun 			(CHSPEC_IS2G(bi->chanspec)) ?
1657*4882a593Smuzhiyun 			WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G);
1658*4882a593Smuzhiyun 		iwe.u.freq.e = 6;
1659*4882a593Smuzhiyun 		event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN);
1660*4882a593Smuzhiyun 
1661*4882a593Smuzhiyun 		/* Channel quality */
1662*4882a593Smuzhiyun 		iwe.cmd = IWEVQUAL;
1663*4882a593Smuzhiyun 		iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI));
1664*4882a593Smuzhiyun 		iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI);
1665*4882a593Smuzhiyun 		iwe.u.qual.noise = 0x100 + bi->phy_noise;
1666*4882a593Smuzhiyun 		event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN);
1667*4882a593Smuzhiyun 
1668*4882a593Smuzhiyun 		wl_iw_handle_scanresults_ies(&event, end, info, bi);
1669*4882a593Smuzhiyun 
1670*4882a593Smuzhiyun 		/* Encryption */
1671*4882a593Smuzhiyun 		iwe.cmd = SIOCGIWENCODE;
1672*4882a593Smuzhiyun 		if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY)
1673*4882a593Smuzhiyun 			iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
1674*4882a593Smuzhiyun 		else
1675*4882a593Smuzhiyun 			iwe.u.data.flags = IW_ENCODE_DISABLED;
1676*4882a593Smuzhiyun 		iwe.u.data.length = 0;
1677*4882a593Smuzhiyun 		event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event);
1678*4882a593Smuzhiyun 
1679*4882a593Smuzhiyun 		/* Rates */
1680*4882a593Smuzhiyun 		if (bi->rateset.count <= sizeof(bi->rateset.rates)) {
1681*4882a593Smuzhiyun 			if (event + IW_MAX_BITRATES*IW_EV_PARAM_LEN >= end)
1682*4882a593Smuzhiyun 				return -E2BIG;
1683*4882a593Smuzhiyun 
1684*4882a593Smuzhiyun 			value = event + IW_EV_LCP_LEN;
1685*4882a593Smuzhiyun 			iwe.cmd = SIOCGIWRATE;
1686*4882a593Smuzhiyun 			/* Those two flags are ignored... */
1687*4882a593Smuzhiyun 			iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
1688*4882a593Smuzhiyun 			for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) {
1689*4882a593Smuzhiyun 				iwe.u.bitrate.value = (bi->rateset.rates[j] & 0x7f) * 500000;
1690*4882a593Smuzhiyun 				value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe,
1691*4882a593Smuzhiyun 					IW_EV_PARAM_LEN);
1692*4882a593Smuzhiyun 			}
1693*4882a593Smuzhiyun 			event = value;
1694*4882a593Smuzhiyun 		}
1695*4882a593Smuzhiyun 	    }
1696*4882a593Smuzhiyun 	    p_buf = p_buf->next;
1697*4882a593Smuzhiyun 	} /* while (p_buf) */
1698*4882a593Smuzhiyun 
1699*4882a593Smuzhiyun 	dwrq->length = event - extra;
1700*4882a593Smuzhiyun 	dwrq->flags = 0;	/* todo */
1701*4882a593Smuzhiyun 
1702*4882a593Smuzhiyun 	return 0;
1703*4882a593Smuzhiyun }
1704*4882a593Smuzhiyun 
1705*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 13 */
1706*4882a593Smuzhiyun 
1707*4882a593Smuzhiyun static int
wl_iw_set_essid(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)1708*4882a593Smuzhiyun wl_iw_set_essid(
1709*4882a593Smuzhiyun 	struct net_device *dev,
1710*4882a593Smuzhiyun 	struct iw_request_info *info,
1711*4882a593Smuzhiyun 	struct iw_point *dwrq,
1712*4882a593Smuzhiyun 	char *extra
1713*4882a593Smuzhiyun )
1714*4882a593Smuzhiyun {
1715*4882a593Smuzhiyun 	wlc_ssid_t ssid;
1716*4882a593Smuzhiyun 	int error;
1717*4882a593Smuzhiyun 
1718*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWESSID\n", dev->name));
1719*4882a593Smuzhiyun 
1720*4882a593Smuzhiyun 	/* default Broadcast SSID */
1721*4882a593Smuzhiyun 	memset(&ssid, 0, sizeof(ssid));
1722*4882a593Smuzhiyun 	if (dwrq->length && extra) {
1723*4882a593Smuzhiyun #if WIRELESS_EXT > 20
1724*4882a593Smuzhiyun 		ssid.SSID_len = MIN(sizeof(ssid.SSID), dwrq->length);
1725*4882a593Smuzhiyun #else
1726*4882a593Smuzhiyun 		ssid.SSID_len = MIN(sizeof(ssid.SSID), dwrq->length-1);
1727*4882a593Smuzhiyun #endif // endif
1728*4882a593Smuzhiyun 		memcpy(ssid.SSID, extra, ssid.SSID_len);
1729*4882a593Smuzhiyun 		ssid.SSID_len = htod32(ssid.SSID_len);
1730*4882a593Smuzhiyun 
1731*4882a593Smuzhiyun 		if ((error = dev_wlc_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid))))
1732*4882a593Smuzhiyun 			return error;
1733*4882a593Smuzhiyun 	}
1734*4882a593Smuzhiyun 	/* If essid null then it is "iwconfig <interface> essid off" command */
1735*4882a593Smuzhiyun 	else {
1736*4882a593Smuzhiyun 		scb_val_t scbval;
1737*4882a593Smuzhiyun 		bzero(&scbval, sizeof(scb_val_t));
1738*4882a593Smuzhiyun 		if ((error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t))))
1739*4882a593Smuzhiyun 			return error;
1740*4882a593Smuzhiyun 	}
1741*4882a593Smuzhiyun 	return 0;
1742*4882a593Smuzhiyun }
1743*4882a593Smuzhiyun 
1744*4882a593Smuzhiyun static int
wl_iw_get_essid(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)1745*4882a593Smuzhiyun wl_iw_get_essid(
1746*4882a593Smuzhiyun 	struct net_device *dev,
1747*4882a593Smuzhiyun 	struct iw_request_info *info,
1748*4882a593Smuzhiyun 	struct iw_point *dwrq,
1749*4882a593Smuzhiyun 	char *extra
1750*4882a593Smuzhiyun )
1751*4882a593Smuzhiyun {
1752*4882a593Smuzhiyun 	wlc_ssid_t ssid;
1753*4882a593Smuzhiyun 	int error;
1754*4882a593Smuzhiyun 
1755*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWESSID\n", dev->name));
1756*4882a593Smuzhiyun 
1757*4882a593Smuzhiyun 	if (!extra)
1758*4882a593Smuzhiyun 		return -EINVAL;
1759*4882a593Smuzhiyun 
1760*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid)))) {
1761*4882a593Smuzhiyun 		WL_ERROR(("Error getting the SSID\n"));
1762*4882a593Smuzhiyun 		return error;
1763*4882a593Smuzhiyun 	}
1764*4882a593Smuzhiyun 
1765*4882a593Smuzhiyun 	ssid.SSID_len = dtoh32(ssid.SSID_len);
1766*4882a593Smuzhiyun 
1767*4882a593Smuzhiyun 	/* Max SSID length check */
1768*4882a593Smuzhiyun 	if (ssid.SSID_len > IW_ESSID_MAX_SIZE) {
1769*4882a593Smuzhiyun 		ssid.SSID_len = IW_ESSID_MAX_SIZE;
1770*4882a593Smuzhiyun 	}
1771*4882a593Smuzhiyun 
1772*4882a593Smuzhiyun 	/* Get the current SSID */
1773*4882a593Smuzhiyun 	memcpy(extra, ssid.SSID, ssid.SSID_len);
1774*4882a593Smuzhiyun 
1775*4882a593Smuzhiyun 	/* NULL terminating as length of extra buffer is IW_ESSID_MAX_SIZE ie 32 */
1776*4882a593Smuzhiyun 	extra[IW_ESSID_MAX_SIZE] = '\0';
1777*4882a593Smuzhiyun 
1778*4882a593Smuzhiyun 	dwrq->length = ssid.SSID_len;
1779*4882a593Smuzhiyun 
1780*4882a593Smuzhiyun 	dwrq->flags = 1; /* active */
1781*4882a593Smuzhiyun 
1782*4882a593Smuzhiyun 	return 0;
1783*4882a593Smuzhiyun }
1784*4882a593Smuzhiyun 
1785*4882a593Smuzhiyun static int
wl_iw_set_nick(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)1786*4882a593Smuzhiyun wl_iw_set_nick(
1787*4882a593Smuzhiyun 	struct net_device *dev,
1788*4882a593Smuzhiyun 	struct iw_request_info *info,
1789*4882a593Smuzhiyun 	struct iw_point *dwrq,
1790*4882a593Smuzhiyun 	char *extra
1791*4882a593Smuzhiyun )
1792*4882a593Smuzhiyun {
1793*4882a593Smuzhiyun 	wl_iw_t *iw = IW_DEV_IF(dev);
1794*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWNICKN\n", dev->name));
1795*4882a593Smuzhiyun 
1796*4882a593Smuzhiyun 	if (!extra)
1797*4882a593Smuzhiyun 		return -EINVAL;
1798*4882a593Smuzhiyun 
1799*4882a593Smuzhiyun 	/* Check the size of the string */
1800*4882a593Smuzhiyun 	if (dwrq->length > sizeof(iw->nickname))
1801*4882a593Smuzhiyun 		return -E2BIG;
1802*4882a593Smuzhiyun 
1803*4882a593Smuzhiyun 	memcpy(iw->nickname, extra, dwrq->length);
1804*4882a593Smuzhiyun 	iw->nickname[dwrq->length - 1] = '\0';
1805*4882a593Smuzhiyun 
1806*4882a593Smuzhiyun 	return 0;
1807*4882a593Smuzhiyun }
1808*4882a593Smuzhiyun 
1809*4882a593Smuzhiyun static int
wl_iw_get_nick(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)1810*4882a593Smuzhiyun wl_iw_get_nick(
1811*4882a593Smuzhiyun 	struct net_device *dev,
1812*4882a593Smuzhiyun 	struct iw_request_info *info,
1813*4882a593Smuzhiyun 	struct iw_point *dwrq,
1814*4882a593Smuzhiyun 	char *extra
1815*4882a593Smuzhiyun )
1816*4882a593Smuzhiyun {
1817*4882a593Smuzhiyun 	wl_iw_t *iw = IW_DEV_IF(dev);
1818*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWNICKN\n", dev->name));
1819*4882a593Smuzhiyun 
1820*4882a593Smuzhiyun 	if (!extra)
1821*4882a593Smuzhiyun 		return -EINVAL;
1822*4882a593Smuzhiyun 
1823*4882a593Smuzhiyun 	strcpy(extra, iw->nickname);
1824*4882a593Smuzhiyun 	dwrq->length = strlen(extra) + 1;
1825*4882a593Smuzhiyun 
1826*4882a593Smuzhiyun 	return 0;
1827*4882a593Smuzhiyun }
1828*4882a593Smuzhiyun 
wl_iw_set_rate(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)1829*4882a593Smuzhiyun static int wl_iw_set_rate(
1830*4882a593Smuzhiyun 	struct net_device *dev,
1831*4882a593Smuzhiyun 	struct iw_request_info *info,
1832*4882a593Smuzhiyun 	struct iw_param *vwrq,
1833*4882a593Smuzhiyun 	char *extra
1834*4882a593Smuzhiyun )
1835*4882a593Smuzhiyun {
1836*4882a593Smuzhiyun 	wl_rateset_t rateset;
1837*4882a593Smuzhiyun 	int error, rate, i, error_bg, error_a;
1838*4882a593Smuzhiyun 
1839*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWRATE\n", dev->name));
1840*4882a593Smuzhiyun 
1841*4882a593Smuzhiyun 	/* Get current rateset */
1842*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset))))
1843*4882a593Smuzhiyun 		return error;
1844*4882a593Smuzhiyun 
1845*4882a593Smuzhiyun 	rateset.count = dtoh32(rateset.count);
1846*4882a593Smuzhiyun 
1847*4882a593Smuzhiyun 	if (vwrq->value < 0) {
1848*4882a593Smuzhiyun 		/* Select maximum rate */
1849*4882a593Smuzhiyun 		rate = rateset.rates[rateset.count - 1] & 0x7f;
1850*4882a593Smuzhiyun 	} else if (vwrq->value < rateset.count) {
1851*4882a593Smuzhiyun 		/* Select rate by rateset index */
1852*4882a593Smuzhiyun 		rate = rateset.rates[vwrq->value] & 0x7f;
1853*4882a593Smuzhiyun 	} else {
1854*4882a593Smuzhiyun 		/* Specified rate in bps */
1855*4882a593Smuzhiyun 		rate = vwrq->value / 500000;
1856*4882a593Smuzhiyun 	}
1857*4882a593Smuzhiyun 
1858*4882a593Smuzhiyun 	if (vwrq->fixed) {
1859*4882a593Smuzhiyun 		/*
1860*4882a593Smuzhiyun 			Set rate override,
1861*4882a593Smuzhiyun 			Since the is a/b/g-blind, both a/bg_rate are enforced.
1862*4882a593Smuzhiyun 		*/
1863*4882a593Smuzhiyun 		error_bg = dev_wlc_intvar_set(dev, "bg_rate", rate);
1864*4882a593Smuzhiyun 		error_a = dev_wlc_intvar_set(dev, "a_rate", rate);
1865*4882a593Smuzhiyun 
1866*4882a593Smuzhiyun 		if (error_bg && error_a)
1867*4882a593Smuzhiyun 			return (error_bg | error_a);
1868*4882a593Smuzhiyun 	} else {
1869*4882a593Smuzhiyun 		/*
1870*4882a593Smuzhiyun 			clear rate override
1871*4882a593Smuzhiyun 			Since the is a/b/g-blind, both a/bg_rate are enforced.
1872*4882a593Smuzhiyun 		*/
1873*4882a593Smuzhiyun 		/* 0 is for clearing rate override */
1874*4882a593Smuzhiyun 		error_bg = dev_wlc_intvar_set(dev, "bg_rate", 0);
1875*4882a593Smuzhiyun 		/* 0 is for clearing rate override */
1876*4882a593Smuzhiyun 		error_a = dev_wlc_intvar_set(dev, "a_rate", 0);
1877*4882a593Smuzhiyun 
1878*4882a593Smuzhiyun 		if (error_bg && error_a)
1879*4882a593Smuzhiyun 			return (error_bg | error_a);
1880*4882a593Smuzhiyun 
1881*4882a593Smuzhiyun 		/* Remove rates above selected rate */
1882*4882a593Smuzhiyun 		for (i = 0; i < rateset.count; i++)
1883*4882a593Smuzhiyun 			if ((rateset.rates[i] & 0x7f) > rate)
1884*4882a593Smuzhiyun 				break;
1885*4882a593Smuzhiyun 		rateset.count = htod32(i);
1886*4882a593Smuzhiyun 
1887*4882a593Smuzhiyun 		/* Set current rateset */
1888*4882a593Smuzhiyun 		if ((error = dev_wlc_ioctl(dev, WLC_SET_RATESET, &rateset, sizeof(rateset))))
1889*4882a593Smuzhiyun 			return error;
1890*4882a593Smuzhiyun 	}
1891*4882a593Smuzhiyun 
1892*4882a593Smuzhiyun 	return 0;
1893*4882a593Smuzhiyun }
1894*4882a593Smuzhiyun 
wl_iw_get_rate(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)1895*4882a593Smuzhiyun static int wl_iw_get_rate(
1896*4882a593Smuzhiyun 	struct net_device *dev,
1897*4882a593Smuzhiyun 	struct iw_request_info *info,
1898*4882a593Smuzhiyun 	struct iw_param *vwrq,
1899*4882a593Smuzhiyun 	char *extra
1900*4882a593Smuzhiyun )
1901*4882a593Smuzhiyun {
1902*4882a593Smuzhiyun 	int error, rate;
1903*4882a593Smuzhiyun 
1904*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWRATE\n", dev->name));
1905*4882a593Smuzhiyun 
1906*4882a593Smuzhiyun 	/* Report the current tx rate */
1907*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate))))
1908*4882a593Smuzhiyun 		return error;
1909*4882a593Smuzhiyun 	rate = dtoh32(rate);
1910*4882a593Smuzhiyun 	vwrq->value = rate * 500000;
1911*4882a593Smuzhiyun 
1912*4882a593Smuzhiyun 	return 0;
1913*4882a593Smuzhiyun }
1914*4882a593Smuzhiyun 
1915*4882a593Smuzhiyun static int
wl_iw_set_rts(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)1916*4882a593Smuzhiyun wl_iw_set_rts(
1917*4882a593Smuzhiyun 	struct net_device *dev,
1918*4882a593Smuzhiyun 	struct iw_request_info *info,
1919*4882a593Smuzhiyun 	struct iw_param *vwrq,
1920*4882a593Smuzhiyun 	char *extra
1921*4882a593Smuzhiyun )
1922*4882a593Smuzhiyun {
1923*4882a593Smuzhiyun 	int error, rts;
1924*4882a593Smuzhiyun 
1925*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWRTS\n", dev->name));
1926*4882a593Smuzhiyun 
1927*4882a593Smuzhiyun 	if (vwrq->disabled)
1928*4882a593Smuzhiyun 		rts = DOT11_DEFAULT_RTS_LEN;
1929*4882a593Smuzhiyun 	else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_RTS_LEN)
1930*4882a593Smuzhiyun 		return -EINVAL;
1931*4882a593Smuzhiyun 	else
1932*4882a593Smuzhiyun 		rts = vwrq->value;
1933*4882a593Smuzhiyun 
1934*4882a593Smuzhiyun 	if ((error = dev_wlc_intvar_set(dev, "rtsthresh", rts)))
1935*4882a593Smuzhiyun 		return error;
1936*4882a593Smuzhiyun 
1937*4882a593Smuzhiyun 	return 0;
1938*4882a593Smuzhiyun }
1939*4882a593Smuzhiyun 
1940*4882a593Smuzhiyun static int
wl_iw_get_rts(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)1941*4882a593Smuzhiyun wl_iw_get_rts(
1942*4882a593Smuzhiyun 	struct net_device *dev,
1943*4882a593Smuzhiyun 	struct iw_request_info *info,
1944*4882a593Smuzhiyun 	struct iw_param *vwrq,
1945*4882a593Smuzhiyun 	char *extra
1946*4882a593Smuzhiyun )
1947*4882a593Smuzhiyun {
1948*4882a593Smuzhiyun 	int error, rts;
1949*4882a593Smuzhiyun 
1950*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWRTS\n", dev->name));
1951*4882a593Smuzhiyun 
1952*4882a593Smuzhiyun 	if ((error = dev_wlc_intvar_get(dev, "rtsthresh", &rts)))
1953*4882a593Smuzhiyun 		return error;
1954*4882a593Smuzhiyun 
1955*4882a593Smuzhiyun 	vwrq->value = rts;
1956*4882a593Smuzhiyun 	vwrq->disabled = (rts >= DOT11_DEFAULT_RTS_LEN);
1957*4882a593Smuzhiyun 	vwrq->fixed = 1;
1958*4882a593Smuzhiyun 
1959*4882a593Smuzhiyun 	return 0;
1960*4882a593Smuzhiyun }
1961*4882a593Smuzhiyun 
1962*4882a593Smuzhiyun static int
wl_iw_set_frag(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)1963*4882a593Smuzhiyun wl_iw_set_frag(
1964*4882a593Smuzhiyun 	struct net_device *dev,
1965*4882a593Smuzhiyun 	struct iw_request_info *info,
1966*4882a593Smuzhiyun 	struct iw_param *vwrq,
1967*4882a593Smuzhiyun 	char *extra
1968*4882a593Smuzhiyun )
1969*4882a593Smuzhiyun {
1970*4882a593Smuzhiyun 	int error, frag;
1971*4882a593Smuzhiyun 
1972*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWFRAG\n", dev->name));
1973*4882a593Smuzhiyun 
1974*4882a593Smuzhiyun 	if (vwrq->disabled)
1975*4882a593Smuzhiyun 		frag = DOT11_DEFAULT_FRAG_LEN;
1976*4882a593Smuzhiyun 	else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_FRAG_LEN)
1977*4882a593Smuzhiyun 		return -EINVAL;
1978*4882a593Smuzhiyun 	else
1979*4882a593Smuzhiyun 		frag = vwrq->value;
1980*4882a593Smuzhiyun 
1981*4882a593Smuzhiyun 	if ((error = dev_wlc_intvar_set(dev, "fragthresh", frag)))
1982*4882a593Smuzhiyun 		return error;
1983*4882a593Smuzhiyun 
1984*4882a593Smuzhiyun 	return 0;
1985*4882a593Smuzhiyun }
1986*4882a593Smuzhiyun 
1987*4882a593Smuzhiyun static int
wl_iw_get_frag(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)1988*4882a593Smuzhiyun wl_iw_get_frag(
1989*4882a593Smuzhiyun 	struct net_device *dev,
1990*4882a593Smuzhiyun 	struct iw_request_info *info,
1991*4882a593Smuzhiyun 	struct iw_param *vwrq,
1992*4882a593Smuzhiyun 	char *extra
1993*4882a593Smuzhiyun )
1994*4882a593Smuzhiyun {
1995*4882a593Smuzhiyun 	int error, fragthreshold;
1996*4882a593Smuzhiyun 
1997*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWFRAG\n", dev->name));
1998*4882a593Smuzhiyun 
1999*4882a593Smuzhiyun 	if ((error = dev_wlc_intvar_get(dev, "fragthresh", &fragthreshold)))
2000*4882a593Smuzhiyun 		return error;
2001*4882a593Smuzhiyun 
2002*4882a593Smuzhiyun 	vwrq->value = fragthreshold;
2003*4882a593Smuzhiyun 	vwrq->disabled = (fragthreshold >= DOT11_DEFAULT_FRAG_LEN);
2004*4882a593Smuzhiyun 	vwrq->fixed = 1;
2005*4882a593Smuzhiyun 
2006*4882a593Smuzhiyun 	return 0;
2007*4882a593Smuzhiyun }
2008*4882a593Smuzhiyun 
2009*4882a593Smuzhiyun static int
wl_iw_set_txpow(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2010*4882a593Smuzhiyun wl_iw_set_txpow(
2011*4882a593Smuzhiyun 	struct net_device *dev,
2012*4882a593Smuzhiyun 	struct iw_request_info *info,
2013*4882a593Smuzhiyun 	struct iw_param *vwrq,
2014*4882a593Smuzhiyun 	char *extra
2015*4882a593Smuzhiyun )
2016*4882a593Smuzhiyun {
2017*4882a593Smuzhiyun 	int error, disable;
2018*4882a593Smuzhiyun 	uint16 txpwrmw;
2019*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWTXPOW\n", dev->name));
2020*4882a593Smuzhiyun 
2021*4882a593Smuzhiyun 	/* Make sure radio is off or on as far as software is concerned */
2022*4882a593Smuzhiyun 	disable = vwrq->disabled ? WL_RADIO_SW_DISABLE : 0;
2023*4882a593Smuzhiyun 	disable += WL_RADIO_SW_DISABLE << 16;
2024*4882a593Smuzhiyun 
2025*4882a593Smuzhiyun 	disable = htod32(disable);
2026*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_SET_RADIO, &disable, sizeof(disable))))
2027*4882a593Smuzhiyun 		return error;
2028*4882a593Smuzhiyun 
2029*4882a593Smuzhiyun 	/* If Radio is off, nothing more to do */
2030*4882a593Smuzhiyun 	if (disable & WL_RADIO_SW_DISABLE)
2031*4882a593Smuzhiyun 		return 0;
2032*4882a593Smuzhiyun 
2033*4882a593Smuzhiyun 	/* Only handle mW */
2034*4882a593Smuzhiyun 	if (!(vwrq->flags & IW_TXPOW_MWATT))
2035*4882a593Smuzhiyun 		return -EINVAL;
2036*4882a593Smuzhiyun 
2037*4882a593Smuzhiyun 	/* Value < 0 means just "on" or "off" */
2038*4882a593Smuzhiyun 	if (vwrq->value < 0)
2039*4882a593Smuzhiyun 		return 0;
2040*4882a593Smuzhiyun 
2041*4882a593Smuzhiyun 	if (vwrq->value > 0xffff) txpwrmw = 0xffff;
2042*4882a593Smuzhiyun 	else txpwrmw = (uint16)vwrq->value;
2043*4882a593Smuzhiyun 
2044*4882a593Smuzhiyun 	error = dev_wlc_intvar_set(dev, "qtxpower", (int)(bcm_mw_to_qdbm(txpwrmw)));
2045*4882a593Smuzhiyun 	return error;
2046*4882a593Smuzhiyun }
2047*4882a593Smuzhiyun 
2048*4882a593Smuzhiyun static int
wl_iw_get_txpow(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2049*4882a593Smuzhiyun wl_iw_get_txpow(
2050*4882a593Smuzhiyun 	struct net_device *dev,
2051*4882a593Smuzhiyun 	struct iw_request_info *info,
2052*4882a593Smuzhiyun 	struct iw_param *vwrq,
2053*4882a593Smuzhiyun 	char *extra
2054*4882a593Smuzhiyun )
2055*4882a593Smuzhiyun {
2056*4882a593Smuzhiyun 	int error, disable, txpwrdbm;
2057*4882a593Smuzhiyun 	uint8 result;
2058*4882a593Smuzhiyun 
2059*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWTXPOW\n", dev->name));
2060*4882a593Smuzhiyun 
2061*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_GET_RADIO, &disable, sizeof(disable))) ||
2062*4882a593Smuzhiyun 	    (error = dev_wlc_intvar_get(dev, "qtxpower", &txpwrdbm)))
2063*4882a593Smuzhiyun 		return error;
2064*4882a593Smuzhiyun 
2065*4882a593Smuzhiyun 	disable = dtoh32(disable);
2066*4882a593Smuzhiyun 	result = (uint8)(txpwrdbm & ~WL_TXPWR_OVERRIDE);
2067*4882a593Smuzhiyun 	vwrq->value = (int32)bcm_qdbm_to_mw(result);
2068*4882a593Smuzhiyun 	vwrq->fixed = 0;
2069*4882a593Smuzhiyun 	vwrq->disabled = (disable & (WL_RADIO_SW_DISABLE | WL_RADIO_HW_DISABLE)) ? 1 : 0;
2070*4882a593Smuzhiyun 	vwrq->flags = IW_TXPOW_MWATT;
2071*4882a593Smuzhiyun 
2072*4882a593Smuzhiyun 	return 0;
2073*4882a593Smuzhiyun }
2074*4882a593Smuzhiyun 
2075*4882a593Smuzhiyun #if WIRELESS_EXT > 10
2076*4882a593Smuzhiyun static int
wl_iw_set_retry(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2077*4882a593Smuzhiyun wl_iw_set_retry(
2078*4882a593Smuzhiyun 	struct net_device *dev,
2079*4882a593Smuzhiyun 	struct iw_request_info *info,
2080*4882a593Smuzhiyun 	struct iw_param *vwrq,
2081*4882a593Smuzhiyun 	char *extra
2082*4882a593Smuzhiyun )
2083*4882a593Smuzhiyun {
2084*4882a593Smuzhiyun 	int error, lrl, srl;
2085*4882a593Smuzhiyun 
2086*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWRETRY\n", dev->name));
2087*4882a593Smuzhiyun 
2088*4882a593Smuzhiyun 	/* Do not handle "off" or "lifetime" */
2089*4882a593Smuzhiyun 	if (vwrq->disabled || (vwrq->flags & IW_RETRY_LIFETIME))
2090*4882a593Smuzhiyun 		return -EINVAL;
2091*4882a593Smuzhiyun 
2092*4882a593Smuzhiyun 	/* Handle "[min|max] limit" */
2093*4882a593Smuzhiyun 	if (vwrq->flags & IW_RETRY_LIMIT) {
2094*4882a593Smuzhiyun 		/* "max limit" or just "limit" */
2095*4882a593Smuzhiyun #if WIRELESS_EXT > 20
2096*4882a593Smuzhiyun 		if ((vwrq->flags & IW_RETRY_LONG) ||(vwrq->flags & IW_RETRY_MAX) ||
2097*4882a593Smuzhiyun 			!((vwrq->flags & IW_RETRY_SHORT) || (vwrq->flags & IW_RETRY_MIN))) {
2098*4882a593Smuzhiyun #else
2099*4882a593Smuzhiyun 		if ((vwrq->flags & IW_RETRY_MAX) || !(vwrq->flags & IW_RETRY_MIN)) {
2100*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 20 */
2101*4882a593Smuzhiyun 
2102*4882a593Smuzhiyun 			lrl = htod32(vwrq->value);
2103*4882a593Smuzhiyun 			if ((error = dev_wlc_ioctl(dev, WLC_SET_LRL, &lrl, sizeof(lrl))))
2104*4882a593Smuzhiyun 				return error;
2105*4882a593Smuzhiyun 		}
2106*4882a593Smuzhiyun 		/* "min limit" or just "limit" */
2107*4882a593Smuzhiyun #if WIRELESS_EXT > 20
2108*4882a593Smuzhiyun 		if ((vwrq->flags & IW_RETRY_SHORT) ||(vwrq->flags & IW_RETRY_MIN) ||
2109*4882a593Smuzhiyun 			!((vwrq->flags & IW_RETRY_LONG) || (vwrq->flags & IW_RETRY_MAX))) {
2110*4882a593Smuzhiyun #else
2111*4882a593Smuzhiyun 		if ((vwrq->flags & IW_RETRY_MIN) || !(vwrq->flags & IW_RETRY_MAX)) {
2112*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 20 */
2113*4882a593Smuzhiyun 
2114*4882a593Smuzhiyun 			srl = htod32(vwrq->value);
2115*4882a593Smuzhiyun 			if ((error = dev_wlc_ioctl(dev, WLC_SET_SRL, &srl, sizeof(srl))))
2116*4882a593Smuzhiyun 				return error;
2117*4882a593Smuzhiyun 		}
2118*4882a593Smuzhiyun 	}
2119*4882a593Smuzhiyun 
2120*4882a593Smuzhiyun 	return 0;
2121*4882a593Smuzhiyun }
2122*4882a593Smuzhiyun 
2123*4882a593Smuzhiyun static int
2124*4882a593Smuzhiyun wl_iw_get_retry(
2125*4882a593Smuzhiyun 	struct net_device *dev,
2126*4882a593Smuzhiyun 	struct iw_request_info *info,
2127*4882a593Smuzhiyun 	struct iw_param *vwrq,
2128*4882a593Smuzhiyun 	char *extra
2129*4882a593Smuzhiyun )
2130*4882a593Smuzhiyun {
2131*4882a593Smuzhiyun 	int error, lrl, srl;
2132*4882a593Smuzhiyun 
2133*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWRETRY\n", dev->name));
2134*4882a593Smuzhiyun 
2135*4882a593Smuzhiyun 	vwrq->disabled = 0;      /* Can't be disabled */
2136*4882a593Smuzhiyun 
2137*4882a593Smuzhiyun 	/* Do not handle lifetime queries */
2138*4882a593Smuzhiyun 	if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME)
2139*4882a593Smuzhiyun 		return -EINVAL;
2140*4882a593Smuzhiyun 
2141*4882a593Smuzhiyun 	/* Get retry limits */
2142*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_GET_LRL, &lrl, sizeof(lrl))) ||
2143*4882a593Smuzhiyun 	    (error = dev_wlc_ioctl(dev, WLC_GET_SRL, &srl, sizeof(srl))))
2144*4882a593Smuzhiyun 		return error;
2145*4882a593Smuzhiyun 
2146*4882a593Smuzhiyun 	lrl = dtoh32(lrl);
2147*4882a593Smuzhiyun 	srl = dtoh32(srl);
2148*4882a593Smuzhiyun 
2149*4882a593Smuzhiyun 	/* Note : by default, display the min retry number */
2150*4882a593Smuzhiyun 	if (vwrq->flags & IW_RETRY_MAX) {
2151*4882a593Smuzhiyun 		vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
2152*4882a593Smuzhiyun 		vwrq->value = lrl;
2153*4882a593Smuzhiyun 	} else {
2154*4882a593Smuzhiyun 		vwrq->flags = IW_RETRY_LIMIT;
2155*4882a593Smuzhiyun 		vwrq->value = srl;
2156*4882a593Smuzhiyun 		if (srl != lrl)
2157*4882a593Smuzhiyun 			vwrq->flags |= IW_RETRY_MIN;
2158*4882a593Smuzhiyun 	}
2159*4882a593Smuzhiyun 
2160*4882a593Smuzhiyun 	return 0;
2161*4882a593Smuzhiyun }
2162*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 10 */
2163*4882a593Smuzhiyun 
2164*4882a593Smuzhiyun static int
2165*4882a593Smuzhiyun wl_iw_set_encode(
2166*4882a593Smuzhiyun 	struct net_device *dev,
2167*4882a593Smuzhiyun 	struct iw_request_info *info,
2168*4882a593Smuzhiyun 	struct iw_point *dwrq,
2169*4882a593Smuzhiyun 	char *extra
2170*4882a593Smuzhiyun )
2171*4882a593Smuzhiyun {
2172*4882a593Smuzhiyun 	wl_wsec_key_t key;
2173*4882a593Smuzhiyun 	int error, val, wsec;
2174*4882a593Smuzhiyun 
2175*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWENCODE\n", dev->name));
2176*4882a593Smuzhiyun 
2177*4882a593Smuzhiyun 	memset(&key, 0, sizeof(key));
2178*4882a593Smuzhiyun 
2179*4882a593Smuzhiyun 	if ((dwrq->flags & IW_ENCODE_INDEX) == 0) {
2180*4882a593Smuzhiyun 		/* Find the current key */
2181*4882a593Smuzhiyun 		for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS; key.index++) {
2182*4882a593Smuzhiyun 			val = htod32(key.index);
2183*4882a593Smuzhiyun 			if ((error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val))))
2184*4882a593Smuzhiyun 				return error;
2185*4882a593Smuzhiyun 			val = dtoh32(val);
2186*4882a593Smuzhiyun 			if (val)
2187*4882a593Smuzhiyun 				break;
2188*4882a593Smuzhiyun 		}
2189*4882a593Smuzhiyun 		/* Default to 0 */
2190*4882a593Smuzhiyun 		if (key.index == DOT11_MAX_DEFAULT_KEYS)
2191*4882a593Smuzhiyun 			key.index = 0;
2192*4882a593Smuzhiyun 	} else {
2193*4882a593Smuzhiyun 		key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
2194*4882a593Smuzhiyun 		if (key.index >= DOT11_MAX_DEFAULT_KEYS)
2195*4882a593Smuzhiyun 			return -EINVAL;
2196*4882a593Smuzhiyun 	}
2197*4882a593Smuzhiyun 
2198*4882a593Smuzhiyun 	/* Interpret "off" to mean no encryption */
2199*4882a593Smuzhiyun 	wsec = (dwrq->flags & IW_ENCODE_DISABLED) ? 0 : WEP_ENABLED;
2200*4882a593Smuzhiyun 
2201*4882a593Smuzhiyun 	if ((error = dev_wlc_intvar_set(dev, "wsec", wsec)))
2202*4882a593Smuzhiyun 		return error;
2203*4882a593Smuzhiyun 
2204*4882a593Smuzhiyun 	/* Old API used to pass a NULL pointer instead of IW_ENCODE_NOKEY */
2205*4882a593Smuzhiyun 	if (!extra || !dwrq->length || (dwrq->flags & IW_ENCODE_NOKEY)) {
2206*4882a593Smuzhiyun 		/* Just select a new current key */
2207*4882a593Smuzhiyun 		val = htod32(key.index);
2208*4882a593Smuzhiyun 		if ((error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY, &val, sizeof(val))))
2209*4882a593Smuzhiyun 			return error;
2210*4882a593Smuzhiyun 	} else {
2211*4882a593Smuzhiyun 		key.len = dwrq->length;
2212*4882a593Smuzhiyun 
2213*4882a593Smuzhiyun 		if (dwrq->length > sizeof(key.data))
2214*4882a593Smuzhiyun 			return -EINVAL;
2215*4882a593Smuzhiyun 
2216*4882a593Smuzhiyun 		memcpy(key.data, extra, dwrq->length);
2217*4882a593Smuzhiyun 
2218*4882a593Smuzhiyun 		key.flags = WL_PRIMARY_KEY;
2219*4882a593Smuzhiyun 		switch (key.len) {
2220*4882a593Smuzhiyun 		case WEP1_KEY_SIZE:
2221*4882a593Smuzhiyun 			key.algo = CRYPTO_ALGO_WEP1;
2222*4882a593Smuzhiyun 			break;
2223*4882a593Smuzhiyun 		case WEP128_KEY_SIZE:
2224*4882a593Smuzhiyun 			key.algo = CRYPTO_ALGO_WEP128;
2225*4882a593Smuzhiyun 			break;
2226*4882a593Smuzhiyun #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14)
2227*4882a593Smuzhiyun 		case TKIP_KEY_SIZE:
2228*4882a593Smuzhiyun 			key.algo = CRYPTO_ALGO_TKIP;
2229*4882a593Smuzhiyun 			break;
2230*4882a593Smuzhiyun #endif // endif
2231*4882a593Smuzhiyun 		case AES_KEY_SIZE:
2232*4882a593Smuzhiyun 			key.algo = CRYPTO_ALGO_AES_CCM;
2233*4882a593Smuzhiyun 			break;
2234*4882a593Smuzhiyun 		default:
2235*4882a593Smuzhiyun 			return -EINVAL;
2236*4882a593Smuzhiyun 		}
2237*4882a593Smuzhiyun 
2238*4882a593Smuzhiyun 		/* Set the new key/index */
2239*4882a593Smuzhiyun 		swap_key_from_BE(&key);
2240*4882a593Smuzhiyun 		if ((error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key))))
2241*4882a593Smuzhiyun 			return error;
2242*4882a593Smuzhiyun 	}
2243*4882a593Smuzhiyun 
2244*4882a593Smuzhiyun 	/* Interpret "restricted" to mean shared key authentication */
2245*4882a593Smuzhiyun 	val = (dwrq->flags & IW_ENCODE_RESTRICTED) ? 1 : 0;
2246*4882a593Smuzhiyun 	val = htod32(val);
2247*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_SET_AUTH, &val, sizeof(val))))
2248*4882a593Smuzhiyun 		return error;
2249*4882a593Smuzhiyun 
2250*4882a593Smuzhiyun 	return 0;
2251*4882a593Smuzhiyun }
2252*4882a593Smuzhiyun 
2253*4882a593Smuzhiyun static int
2254*4882a593Smuzhiyun wl_iw_get_encode(
2255*4882a593Smuzhiyun 	struct net_device *dev,
2256*4882a593Smuzhiyun 	struct iw_request_info *info,
2257*4882a593Smuzhiyun 	struct iw_point *dwrq,
2258*4882a593Smuzhiyun 	char *extra
2259*4882a593Smuzhiyun )
2260*4882a593Smuzhiyun {
2261*4882a593Smuzhiyun 	wl_wsec_key_t key;
2262*4882a593Smuzhiyun 	int error, val, wsec, auth;
2263*4882a593Smuzhiyun 
2264*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWENCODE\n", dev->name));
2265*4882a593Smuzhiyun 
2266*4882a593Smuzhiyun 	/* assure default values of zero for things we don't touch */
2267*4882a593Smuzhiyun 	bzero(&key, sizeof(wl_wsec_key_t));
2268*4882a593Smuzhiyun 
2269*4882a593Smuzhiyun 	if ((dwrq->flags & IW_ENCODE_INDEX) == 0) {
2270*4882a593Smuzhiyun 		/* Find the current key */
2271*4882a593Smuzhiyun 		for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS; key.index++) {
2272*4882a593Smuzhiyun 			val = key.index;
2273*4882a593Smuzhiyun 			if ((error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val))))
2274*4882a593Smuzhiyun 				return error;
2275*4882a593Smuzhiyun 			val = dtoh32(val);
2276*4882a593Smuzhiyun 			if (val)
2277*4882a593Smuzhiyun 				break;
2278*4882a593Smuzhiyun 		}
2279*4882a593Smuzhiyun 	} else
2280*4882a593Smuzhiyun 		key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
2281*4882a593Smuzhiyun 
2282*4882a593Smuzhiyun 	if (key.index >= DOT11_MAX_DEFAULT_KEYS)
2283*4882a593Smuzhiyun 		key.index = 0;
2284*4882a593Smuzhiyun 
2285*4882a593Smuzhiyun 	/* Get info */
2286*4882a593Smuzhiyun 
2287*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_GET_WSEC, &wsec, sizeof(wsec))) ||
2288*4882a593Smuzhiyun 	    (error = dev_wlc_ioctl(dev, WLC_GET_AUTH, &auth, sizeof(auth))))
2289*4882a593Smuzhiyun 		return error;
2290*4882a593Smuzhiyun 
2291*4882a593Smuzhiyun 	swap_key_to_BE(&key);
2292*4882a593Smuzhiyun 
2293*4882a593Smuzhiyun 	wsec = dtoh32(wsec);
2294*4882a593Smuzhiyun 	auth = dtoh32(auth);
2295*4882a593Smuzhiyun 	/* Get key length */
2296*4882a593Smuzhiyun 	dwrq->length = MIN(IW_ENCODING_TOKEN_MAX, key.len);
2297*4882a593Smuzhiyun 
2298*4882a593Smuzhiyun 	/* Get flags */
2299*4882a593Smuzhiyun 	dwrq->flags = key.index + 1;
2300*4882a593Smuzhiyun 	if (!(wsec & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED))) {
2301*4882a593Smuzhiyun 		/* Interpret "off" to mean no encryption */
2302*4882a593Smuzhiyun 		dwrq->flags |= IW_ENCODE_DISABLED;
2303*4882a593Smuzhiyun 	}
2304*4882a593Smuzhiyun 	if (auth) {
2305*4882a593Smuzhiyun 		/* Interpret "restricted" to mean shared key authentication */
2306*4882a593Smuzhiyun 		dwrq->flags |= IW_ENCODE_RESTRICTED;
2307*4882a593Smuzhiyun 	}
2308*4882a593Smuzhiyun 
2309*4882a593Smuzhiyun 	/* Get key */
2310*4882a593Smuzhiyun 	if (dwrq->length && extra)
2311*4882a593Smuzhiyun 		memcpy(extra, key.data, dwrq->length);
2312*4882a593Smuzhiyun 
2313*4882a593Smuzhiyun 	return 0;
2314*4882a593Smuzhiyun }
2315*4882a593Smuzhiyun 
2316*4882a593Smuzhiyun static int
2317*4882a593Smuzhiyun wl_iw_set_power(
2318*4882a593Smuzhiyun 	struct net_device *dev,
2319*4882a593Smuzhiyun 	struct iw_request_info *info,
2320*4882a593Smuzhiyun 	struct iw_param *vwrq,
2321*4882a593Smuzhiyun 	char *extra
2322*4882a593Smuzhiyun )
2323*4882a593Smuzhiyun {
2324*4882a593Smuzhiyun 	int error, pm;
2325*4882a593Smuzhiyun 
2326*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWPOWER\n", dev->name));
2327*4882a593Smuzhiyun 
2328*4882a593Smuzhiyun 	pm = vwrq->disabled ? PM_OFF : PM_MAX;
2329*4882a593Smuzhiyun 
2330*4882a593Smuzhiyun 	pm = htod32(pm);
2331*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm))))
2332*4882a593Smuzhiyun 		return error;
2333*4882a593Smuzhiyun 
2334*4882a593Smuzhiyun 	return 0;
2335*4882a593Smuzhiyun }
2336*4882a593Smuzhiyun 
2337*4882a593Smuzhiyun static int
2338*4882a593Smuzhiyun wl_iw_get_power(
2339*4882a593Smuzhiyun 	struct net_device *dev,
2340*4882a593Smuzhiyun 	struct iw_request_info *info,
2341*4882a593Smuzhiyun 	struct iw_param *vwrq,
2342*4882a593Smuzhiyun 	char *extra
2343*4882a593Smuzhiyun )
2344*4882a593Smuzhiyun {
2345*4882a593Smuzhiyun 	int error, pm;
2346*4882a593Smuzhiyun 
2347*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWPOWER\n", dev->name));
2348*4882a593Smuzhiyun 
2349*4882a593Smuzhiyun 	if ((error = dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm))))
2350*4882a593Smuzhiyun 		return error;
2351*4882a593Smuzhiyun 
2352*4882a593Smuzhiyun 	pm = dtoh32(pm);
2353*4882a593Smuzhiyun 	vwrq->disabled = pm ? 0 : 1;
2354*4882a593Smuzhiyun 	vwrq->flags = IW_POWER_ALL_R;
2355*4882a593Smuzhiyun 
2356*4882a593Smuzhiyun 	return 0;
2357*4882a593Smuzhiyun }
2358*4882a593Smuzhiyun 
2359*4882a593Smuzhiyun #if WIRELESS_EXT > 17
2360*4882a593Smuzhiyun static int
2361*4882a593Smuzhiyun wl_iw_set_wpaie(
2362*4882a593Smuzhiyun 	struct net_device *dev,
2363*4882a593Smuzhiyun 	struct iw_request_info *info,
2364*4882a593Smuzhiyun 	struct iw_point *iwp,
2365*4882a593Smuzhiyun 	char *extra
2366*4882a593Smuzhiyun )
2367*4882a593Smuzhiyun {
2368*4882a593Smuzhiyun #if defined(BCMWAPI_WPI)
2369*4882a593Smuzhiyun 	uchar buf[WLC_IOCTL_SMLEN] = {0};
2370*4882a593Smuzhiyun 	uchar *p = buf;
2371*4882a593Smuzhiyun 	int wapi_ie_size;
2372*4882a593Smuzhiyun 
2373*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWGENIE\n", dev->name));
2374*4882a593Smuzhiyun 
2375*4882a593Smuzhiyun 	if (extra[0] == DOT11_MNG_WAPI_ID)
2376*4882a593Smuzhiyun 	{
2377*4882a593Smuzhiyun 		wapi_ie_size = iwp->length;
2378*4882a593Smuzhiyun 		memcpy(p, extra, iwp->length);
2379*4882a593Smuzhiyun 		dev_wlc_bufvar_set(dev, "wapiie", buf, wapi_ie_size);
2380*4882a593Smuzhiyun 	}
2381*4882a593Smuzhiyun 	else
2382*4882a593Smuzhiyun #endif // endif
2383*4882a593Smuzhiyun 		dev_wlc_bufvar_set(dev, "wpaie", extra, iwp->length);
2384*4882a593Smuzhiyun 
2385*4882a593Smuzhiyun 	return 0;
2386*4882a593Smuzhiyun }
2387*4882a593Smuzhiyun 
2388*4882a593Smuzhiyun static int
2389*4882a593Smuzhiyun wl_iw_get_wpaie(
2390*4882a593Smuzhiyun 	struct net_device *dev,
2391*4882a593Smuzhiyun 	struct iw_request_info *info,
2392*4882a593Smuzhiyun 	struct iw_point *iwp,
2393*4882a593Smuzhiyun 	char *extra
2394*4882a593Smuzhiyun )
2395*4882a593Smuzhiyun {
2396*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWGENIE\n", dev->name));
2397*4882a593Smuzhiyun 	iwp->length = 64;
2398*4882a593Smuzhiyun 	dev_wlc_bufvar_get(dev, "wpaie", extra, iwp->length);
2399*4882a593Smuzhiyun 	return 0;
2400*4882a593Smuzhiyun }
2401*4882a593Smuzhiyun 
2402*4882a593Smuzhiyun static int
2403*4882a593Smuzhiyun wl_iw_set_encodeext(
2404*4882a593Smuzhiyun 	struct net_device *dev,
2405*4882a593Smuzhiyun 	struct iw_request_info *info,
2406*4882a593Smuzhiyun 	struct iw_point *dwrq,
2407*4882a593Smuzhiyun 	char *extra
2408*4882a593Smuzhiyun )
2409*4882a593Smuzhiyun {
2410*4882a593Smuzhiyun 	wl_wsec_key_t key;
2411*4882a593Smuzhiyun 	int error;
2412*4882a593Smuzhiyun 	struct iw_encode_ext *iwe;
2413*4882a593Smuzhiyun 
2414*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWENCODEEXT\n", dev->name));
2415*4882a593Smuzhiyun 
2416*4882a593Smuzhiyun 	memset(&key, 0, sizeof(key));
2417*4882a593Smuzhiyun 	iwe = (struct iw_encode_ext *)extra;
2418*4882a593Smuzhiyun 
2419*4882a593Smuzhiyun 	/* disable encryption completely  */
2420*4882a593Smuzhiyun 	if (dwrq->flags & IW_ENCODE_DISABLED) {
2421*4882a593Smuzhiyun 
2422*4882a593Smuzhiyun 	}
2423*4882a593Smuzhiyun 
2424*4882a593Smuzhiyun 	/* get the key index */
2425*4882a593Smuzhiyun 	key.index = 0;
2426*4882a593Smuzhiyun 	if (dwrq->flags & IW_ENCODE_INDEX)
2427*4882a593Smuzhiyun 		key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
2428*4882a593Smuzhiyun 
2429*4882a593Smuzhiyun 	key.len = iwe->key_len;
2430*4882a593Smuzhiyun 
2431*4882a593Smuzhiyun 	/* Instead of bcast for ea address for default wep keys, driver needs it to be Null */
2432*4882a593Smuzhiyun 	if (!ETHER_ISMULTI(iwe->addr.sa_data))
2433*4882a593Smuzhiyun 		bcopy((void *)&iwe->addr.sa_data, (char *)&key.ea, ETHER_ADDR_LEN);
2434*4882a593Smuzhiyun 
2435*4882a593Smuzhiyun 	/* check for key index change */
2436*4882a593Smuzhiyun 	if (key.len == 0) {
2437*4882a593Smuzhiyun 		if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
2438*4882a593Smuzhiyun 			WL_WSEC(("Changing the the primary Key to %d\n", key.index));
2439*4882a593Smuzhiyun 			/* change the key index .... */
2440*4882a593Smuzhiyun 			key.index = htod32(key.index);
2441*4882a593Smuzhiyun 			error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY,
2442*4882a593Smuzhiyun 				&key.index, sizeof(key.index));
2443*4882a593Smuzhiyun 			if (error)
2444*4882a593Smuzhiyun 				return error;
2445*4882a593Smuzhiyun 		}
2446*4882a593Smuzhiyun 		/* key delete */
2447*4882a593Smuzhiyun 		else {
2448*4882a593Smuzhiyun 			swap_key_from_BE(&key);
2449*4882a593Smuzhiyun 			error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
2450*4882a593Smuzhiyun 			if (error)
2451*4882a593Smuzhiyun 				return error;
2452*4882a593Smuzhiyun 		}
2453*4882a593Smuzhiyun 	}
2454*4882a593Smuzhiyun 	/* This case is used to allow an external 802.1x supplicant
2455*4882a593Smuzhiyun 	 * to pass the PMK to the in-driver supplicant for use in
2456*4882a593Smuzhiyun 	 * the 4-way handshake.
2457*4882a593Smuzhiyun 	 */
2458*4882a593Smuzhiyun 	else if (iwe->alg == IW_ENCODE_ALG_PMK) {
2459*4882a593Smuzhiyun 		int j;
2460*4882a593Smuzhiyun 		wsec_pmk_t pmk;
2461*4882a593Smuzhiyun 		char keystring[WSEC_MAX_PSK_LEN + 1];
2462*4882a593Smuzhiyun 		char* charptr = keystring;
2463*4882a593Smuzhiyun 		uint len;
2464*4882a593Smuzhiyun 
2465*4882a593Smuzhiyun 		/* copy the raw hex key to the appropriate format */
2466*4882a593Smuzhiyun 		for (j = 0; j < (WSEC_MAX_PSK_LEN / 2); j++) {
2467*4882a593Smuzhiyun 			(void)snprintf(charptr, 3, "%02x", iwe->key[j]);
2468*4882a593Smuzhiyun 			charptr += 2;
2469*4882a593Smuzhiyun 		}
2470*4882a593Smuzhiyun 		len = strlen(keystring);
2471*4882a593Smuzhiyun 		pmk.key_len = htod16(len);
2472*4882a593Smuzhiyun 		bcopy(keystring, pmk.key, len);
2473*4882a593Smuzhiyun 		pmk.flags = htod16(WSEC_PASSPHRASE);
2474*4882a593Smuzhiyun 
2475*4882a593Smuzhiyun 		error = dev_wlc_ioctl(dev, WLC_SET_WSEC_PMK, &pmk, sizeof(pmk));
2476*4882a593Smuzhiyun 		if (error)
2477*4882a593Smuzhiyun 			return error;
2478*4882a593Smuzhiyun 	}
2479*4882a593Smuzhiyun 
2480*4882a593Smuzhiyun 	else {
2481*4882a593Smuzhiyun 		if (iwe->key_len > sizeof(key.data))
2482*4882a593Smuzhiyun 			return -EINVAL;
2483*4882a593Smuzhiyun 
2484*4882a593Smuzhiyun 		WL_WSEC(("Setting the key index %d\n", key.index));
2485*4882a593Smuzhiyun 		if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
2486*4882a593Smuzhiyun 			WL_WSEC(("key is a Primary Key\n"));
2487*4882a593Smuzhiyun 			key.flags = WL_PRIMARY_KEY;
2488*4882a593Smuzhiyun 		}
2489*4882a593Smuzhiyun 
2490*4882a593Smuzhiyun 		bcopy((void *)iwe->key, key.data, iwe->key_len);
2491*4882a593Smuzhiyun 
2492*4882a593Smuzhiyun 		if (iwe->alg == IW_ENCODE_ALG_TKIP) {
2493*4882a593Smuzhiyun 			uint8 keybuf[8];
2494*4882a593Smuzhiyun 			bcopy(&key.data[24], keybuf, sizeof(keybuf));
2495*4882a593Smuzhiyun 			bcopy(&key.data[16], &key.data[24], sizeof(keybuf));
2496*4882a593Smuzhiyun 			bcopy(keybuf, &key.data[16], sizeof(keybuf));
2497*4882a593Smuzhiyun 		}
2498*4882a593Smuzhiyun 
2499*4882a593Smuzhiyun 		/* rx iv */
2500*4882a593Smuzhiyun 		if (iwe->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
2501*4882a593Smuzhiyun 			uchar *ivptr;
2502*4882a593Smuzhiyun 			ivptr = (uchar *)iwe->rx_seq;
2503*4882a593Smuzhiyun 			key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
2504*4882a593Smuzhiyun 				(ivptr[3] << 8) | ivptr[2];
2505*4882a593Smuzhiyun 			key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
2506*4882a593Smuzhiyun 			key.iv_initialized = TRUE;
2507*4882a593Smuzhiyun 		}
2508*4882a593Smuzhiyun 
2509*4882a593Smuzhiyun 		switch (iwe->alg) {
2510*4882a593Smuzhiyun 			case IW_ENCODE_ALG_NONE:
2511*4882a593Smuzhiyun 				key.algo = CRYPTO_ALGO_OFF;
2512*4882a593Smuzhiyun 				break;
2513*4882a593Smuzhiyun 			case IW_ENCODE_ALG_WEP:
2514*4882a593Smuzhiyun 				if (iwe->key_len == WEP1_KEY_SIZE)
2515*4882a593Smuzhiyun 					key.algo = CRYPTO_ALGO_WEP1;
2516*4882a593Smuzhiyun 				else
2517*4882a593Smuzhiyun 					key.algo = CRYPTO_ALGO_WEP128;
2518*4882a593Smuzhiyun 				break;
2519*4882a593Smuzhiyun 			case IW_ENCODE_ALG_TKIP:
2520*4882a593Smuzhiyun 				key.algo = CRYPTO_ALGO_TKIP;
2521*4882a593Smuzhiyun 				break;
2522*4882a593Smuzhiyun 			case IW_ENCODE_ALG_CCMP:
2523*4882a593Smuzhiyun 				key.algo = CRYPTO_ALGO_AES_CCM;
2524*4882a593Smuzhiyun 				break;
2525*4882a593Smuzhiyun #ifdef BCMWAPI_WPI
2526*4882a593Smuzhiyun 			case IW_ENCODE_ALG_SM4:
2527*4882a593Smuzhiyun 				key.algo = CRYPTO_ALGO_SMS4;
2528*4882a593Smuzhiyun 				if (iwe->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
2529*4882a593Smuzhiyun 					key.flags &= ~WL_PRIMARY_KEY;
2530*4882a593Smuzhiyun 				}
2531*4882a593Smuzhiyun 				break;
2532*4882a593Smuzhiyun #endif // endif
2533*4882a593Smuzhiyun 			default:
2534*4882a593Smuzhiyun 				break;
2535*4882a593Smuzhiyun 		}
2536*4882a593Smuzhiyun 		swap_key_from_BE(&key);
2537*4882a593Smuzhiyun 
2538*4882a593Smuzhiyun 		dhd_wait_pend8021x(dev);
2539*4882a593Smuzhiyun 
2540*4882a593Smuzhiyun 		error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
2541*4882a593Smuzhiyun 		if (error)
2542*4882a593Smuzhiyun 			return error;
2543*4882a593Smuzhiyun 	}
2544*4882a593Smuzhiyun 	return 0;
2545*4882a593Smuzhiyun }
2546*4882a593Smuzhiyun 
2547*4882a593Smuzhiyun #if WIRELESS_EXT > 17
2548*4882a593Smuzhiyun struct {
2549*4882a593Smuzhiyun 	pmkid_list_t pmkids;
2550*4882a593Smuzhiyun 	pmkid_t foo[MAXPMKID-1];
2551*4882a593Smuzhiyun } pmkid_list;
2552*4882a593Smuzhiyun static int
2553*4882a593Smuzhiyun wl_iw_set_pmksa(
2554*4882a593Smuzhiyun 	struct net_device *dev,
2555*4882a593Smuzhiyun 	struct iw_request_info *info,
2556*4882a593Smuzhiyun 	struct iw_param *vwrq,
2557*4882a593Smuzhiyun 	char *extra
2558*4882a593Smuzhiyun )
2559*4882a593Smuzhiyun {
2560*4882a593Smuzhiyun 	struct iw_pmksa *iwpmksa;
2561*4882a593Smuzhiyun 	uint i;
2562*4882a593Smuzhiyun 	char eabuf[ETHER_ADDR_STR_LEN];
2563*4882a593Smuzhiyun 	pmkid_t * pmkid_array = pmkid_list.pmkids.pmkid;
2564*4882a593Smuzhiyun 
2565*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWPMKSA\n", dev->name));
2566*4882a593Smuzhiyun 	iwpmksa = (struct iw_pmksa *)extra;
2567*4882a593Smuzhiyun 	bzero((char *)eabuf, ETHER_ADDR_STR_LEN);
2568*4882a593Smuzhiyun 	if (iwpmksa->cmd == IW_PMKSA_FLUSH) {
2569*4882a593Smuzhiyun 		WL_TRACE(("wl_iw_set_pmksa - IW_PMKSA_FLUSH\n"));
2570*4882a593Smuzhiyun 		bzero((char *)&pmkid_list, sizeof(pmkid_list));
2571*4882a593Smuzhiyun 	}
2572*4882a593Smuzhiyun 	if (iwpmksa->cmd == IW_PMKSA_REMOVE) {
2573*4882a593Smuzhiyun 		pmkid_list_t pmkid, *pmkidptr;
2574*4882a593Smuzhiyun 		pmkidptr = &pmkid;
2575*4882a593Smuzhiyun 		bcopy(&iwpmksa->bssid.sa_data[0], &pmkidptr->pmkid[0].BSSID, ETHER_ADDR_LEN);
2576*4882a593Smuzhiyun 		bcopy(&iwpmksa->pmkid[0], &pmkidptr->pmkid[0].PMKID, WPA2_PMKID_LEN);
2577*4882a593Smuzhiyun 		{
2578*4882a593Smuzhiyun 			uint j;
2579*4882a593Smuzhiyun 			WL_TRACE(("wl_iw_set_pmksa,IW_PMKSA_REMOVE - PMKID: %s = ",
2580*4882a593Smuzhiyun 				bcm_ether_ntoa(&pmkidptr->pmkid[0].BSSID,
2581*4882a593Smuzhiyun 				eabuf)));
2582*4882a593Smuzhiyun 			for (j = 0; j < WPA2_PMKID_LEN; j++)
2583*4882a593Smuzhiyun 				WL_TRACE(("%02x ", pmkidptr->pmkid[0].PMKID[j]));
2584*4882a593Smuzhiyun 			WL_TRACE(("\n"));
2585*4882a593Smuzhiyun 		}
2586*4882a593Smuzhiyun 		for (i = 0; i < pmkid_list.pmkids.npmkid; i++)
2587*4882a593Smuzhiyun 			if (!bcmp(&iwpmksa->bssid.sa_data[0], &pmkid_array[i].BSSID,
2588*4882a593Smuzhiyun 				ETHER_ADDR_LEN))
2589*4882a593Smuzhiyun 				break;
2590*4882a593Smuzhiyun 		for (; i < pmkid_list.pmkids.npmkid; i++) {
2591*4882a593Smuzhiyun 			bcopy(&pmkid_array[i+1].BSSID,
2592*4882a593Smuzhiyun 				&pmkid_array[i].BSSID,
2593*4882a593Smuzhiyun 				ETHER_ADDR_LEN);
2594*4882a593Smuzhiyun 			bcopy(&pmkid_array[i+1].PMKID,
2595*4882a593Smuzhiyun 				&pmkid_array[i].PMKID,
2596*4882a593Smuzhiyun 				WPA2_PMKID_LEN);
2597*4882a593Smuzhiyun 		}
2598*4882a593Smuzhiyun 		pmkid_list.pmkids.npmkid--;
2599*4882a593Smuzhiyun 	}
2600*4882a593Smuzhiyun 	if (iwpmksa->cmd == IW_PMKSA_ADD) {
2601*4882a593Smuzhiyun 		bcopy(&iwpmksa->bssid.sa_data[0],
2602*4882a593Smuzhiyun 			&pmkid_array[pmkid_list.pmkids.npmkid].BSSID,
2603*4882a593Smuzhiyun 			ETHER_ADDR_LEN);
2604*4882a593Smuzhiyun 		bcopy(&iwpmksa->pmkid[0], &pmkid_array[pmkid_list.pmkids.npmkid].PMKID,
2605*4882a593Smuzhiyun 			WPA2_PMKID_LEN);
2606*4882a593Smuzhiyun 		{
2607*4882a593Smuzhiyun 			uint j;
2608*4882a593Smuzhiyun 			uint k;
2609*4882a593Smuzhiyun 			k = pmkid_list.pmkids.npmkid;
2610*4882a593Smuzhiyun 			BCM_REFERENCE(k);
2611*4882a593Smuzhiyun 			WL_TRACE(("wl_iw_set_pmksa,IW_PMKSA_ADD - PMKID: %s = ",
2612*4882a593Smuzhiyun 				bcm_ether_ntoa(&pmkid_array[k].BSSID,
2613*4882a593Smuzhiyun 				eabuf)));
2614*4882a593Smuzhiyun 			for (j = 0; j < WPA2_PMKID_LEN; j++)
2615*4882a593Smuzhiyun 				WL_TRACE(("%02x ", pmkid_array[k].PMKID[j]));
2616*4882a593Smuzhiyun 			WL_TRACE(("\n"));
2617*4882a593Smuzhiyun 		}
2618*4882a593Smuzhiyun 		pmkid_list.pmkids.npmkid++;
2619*4882a593Smuzhiyun 	}
2620*4882a593Smuzhiyun 	WL_TRACE(("PRINTING pmkid LIST - No of elements %d\n", pmkid_list.pmkids.npmkid));
2621*4882a593Smuzhiyun 	for (i = 0; i < pmkid_list.pmkids.npmkid; i++) {
2622*4882a593Smuzhiyun 		uint j;
2623*4882a593Smuzhiyun 		WL_TRACE(("PMKID[%d]: %s = ", i,
2624*4882a593Smuzhiyun 			bcm_ether_ntoa(&pmkid_array[i].BSSID,
2625*4882a593Smuzhiyun 			eabuf)));
2626*4882a593Smuzhiyun 		for (j = 0; j < WPA2_PMKID_LEN; j++)
2627*4882a593Smuzhiyun 			WL_TRACE(("%02x ", pmkid_array[i].PMKID[j]));
2628*4882a593Smuzhiyun 		printf("\n");
2629*4882a593Smuzhiyun 	}
2630*4882a593Smuzhiyun 	WL_TRACE(("\n"));
2631*4882a593Smuzhiyun 	dev_wlc_bufvar_set(dev, "pmkid_info", (char *)&pmkid_list, sizeof(pmkid_list));
2632*4882a593Smuzhiyun 	return 0;
2633*4882a593Smuzhiyun }
2634*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 17 */
2635*4882a593Smuzhiyun 
2636*4882a593Smuzhiyun static int
2637*4882a593Smuzhiyun wl_iw_get_encodeext(
2638*4882a593Smuzhiyun 	struct net_device *dev,
2639*4882a593Smuzhiyun 	struct iw_request_info *info,
2640*4882a593Smuzhiyun 	struct iw_param *vwrq,
2641*4882a593Smuzhiyun 	char *extra
2642*4882a593Smuzhiyun )
2643*4882a593Smuzhiyun {
2644*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWENCODEEXT\n", dev->name));
2645*4882a593Smuzhiyun 	return 0;
2646*4882a593Smuzhiyun }
2647*4882a593Smuzhiyun 
2648*4882a593Smuzhiyun static int
2649*4882a593Smuzhiyun wl_iw_set_wpaauth(
2650*4882a593Smuzhiyun 	struct net_device *dev,
2651*4882a593Smuzhiyun 	struct iw_request_info *info,
2652*4882a593Smuzhiyun 	struct iw_param *vwrq,
2653*4882a593Smuzhiyun 	char *extra
2654*4882a593Smuzhiyun )
2655*4882a593Smuzhiyun {
2656*4882a593Smuzhiyun 	int error = 0;
2657*4882a593Smuzhiyun 	int paramid;
2658*4882a593Smuzhiyun 	int paramval;
2659*4882a593Smuzhiyun 	uint32 cipher_combined;
2660*4882a593Smuzhiyun 	int val = 0;
2661*4882a593Smuzhiyun 	wl_iw_t *iw = IW_DEV_IF(dev);
2662*4882a593Smuzhiyun 
2663*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWAUTH\n", dev->name));
2664*4882a593Smuzhiyun 
2665*4882a593Smuzhiyun 	paramid = vwrq->flags & IW_AUTH_INDEX;
2666*4882a593Smuzhiyun 	paramval = vwrq->value;
2667*4882a593Smuzhiyun 
2668*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCSIWAUTH, paramid = 0x%0x, paramval = 0x%0x\n",
2669*4882a593Smuzhiyun 		dev->name, paramid, paramval));
2670*4882a593Smuzhiyun 
2671*4882a593Smuzhiyun 	switch (paramid) {
2672*4882a593Smuzhiyun 
2673*4882a593Smuzhiyun 	case IW_AUTH_WPA_VERSION:
2674*4882a593Smuzhiyun 		/* supported wpa version disabled or wpa or wpa2 */
2675*4882a593Smuzhiyun 		if (paramval & IW_AUTH_WPA_VERSION_DISABLED)
2676*4882a593Smuzhiyun 			val = WPA_AUTH_DISABLED;
2677*4882a593Smuzhiyun 		else if (paramval & (IW_AUTH_WPA_VERSION_WPA))
2678*4882a593Smuzhiyun 			val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
2679*4882a593Smuzhiyun 		else if (paramval & IW_AUTH_WPA_VERSION_WPA2)
2680*4882a593Smuzhiyun 			val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
2681*4882a593Smuzhiyun #ifdef BCMWAPI_WPI
2682*4882a593Smuzhiyun 		else if (paramval & IW_AUTH_WAPI_VERSION_1)
2683*4882a593Smuzhiyun 			val = WAPI_AUTH_UNSPECIFIED;
2684*4882a593Smuzhiyun #endif // endif
2685*4882a593Smuzhiyun 		WL_TRACE(("%s: %d: setting wpa_auth to 0x%0x\n", __FUNCTION__, __LINE__, val));
2686*4882a593Smuzhiyun 		if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val)))
2687*4882a593Smuzhiyun 			return error;
2688*4882a593Smuzhiyun 		break;
2689*4882a593Smuzhiyun 
2690*4882a593Smuzhiyun 	case IW_AUTH_CIPHER_PAIRWISE:
2691*4882a593Smuzhiyun 	case IW_AUTH_CIPHER_GROUP: {
2692*4882a593Smuzhiyun 		int fbt_cap = 0;
2693*4882a593Smuzhiyun 
2694*4882a593Smuzhiyun 		if (paramid == IW_AUTH_CIPHER_PAIRWISE) {
2695*4882a593Smuzhiyun 			iw->pwsec = paramval;
2696*4882a593Smuzhiyun 		}
2697*4882a593Smuzhiyun 		else {
2698*4882a593Smuzhiyun 			iw->gwsec = paramval;
2699*4882a593Smuzhiyun 		}
2700*4882a593Smuzhiyun 
2701*4882a593Smuzhiyun 		if ((error = dev_wlc_intvar_get(dev, "wsec", &val)))
2702*4882a593Smuzhiyun 			return error;
2703*4882a593Smuzhiyun 
2704*4882a593Smuzhiyun 		cipher_combined = iw->gwsec | iw->pwsec;
2705*4882a593Smuzhiyun 		val &= ~(WEP_ENABLED | TKIP_ENABLED | AES_ENABLED);
2706*4882a593Smuzhiyun 		if (cipher_combined & (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104))
2707*4882a593Smuzhiyun 			val |= WEP_ENABLED;
2708*4882a593Smuzhiyun 		if (cipher_combined & IW_AUTH_CIPHER_TKIP)
2709*4882a593Smuzhiyun 			val |= TKIP_ENABLED;
2710*4882a593Smuzhiyun 		if (cipher_combined & IW_AUTH_CIPHER_CCMP)
2711*4882a593Smuzhiyun 			val |= AES_ENABLED;
2712*4882a593Smuzhiyun #ifdef BCMWAPI_WPI
2713*4882a593Smuzhiyun 		val &= ~SMS4_ENABLED;
2714*4882a593Smuzhiyun 		if (cipher_combined & IW_AUTH_CIPHER_SMS4)
2715*4882a593Smuzhiyun 			val |= SMS4_ENABLED;
2716*4882a593Smuzhiyun #endif // endif
2717*4882a593Smuzhiyun 
2718*4882a593Smuzhiyun 		if (iw->privacy_invoked && !val) {
2719*4882a593Smuzhiyun 			WL_WSEC(("%s: %s: 'Privacy invoked' TRUE but clearing wsec, assuming "
2720*4882a593Smuzhiyun 			         "we're a WPS enrollee\n", dev->name, __FUNCTION__));
2721*4882a593Smuzhiyun 			if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", TRUE))) {
2722*4882a593Smuzhiyun 				WL_WSEC(("Failed to set iovar is_WPS_enrollee\n"));
2723*4882a593Smuzhiyun 				return error;
2724*4882a593Smuzhiyun 			}
2725*4882a593Smuzhiyun 		} else if (val) {
2726*4882a593Smuzhiyun 			if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) {
2727*4882a593Smuzhiyun 				WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n"));
2728*4882a593Smuzhiyun 				return error;
2729*4882a593Smuzhiyun 			}
2730*4882a593Smuzhiyun 		}
2731*4882a593Smuzhiyun 
2732*4882a593Smuzhiyun 		if ((error = dev_wlc_intvar_set(dev, "wsec", val)))
2733*4882a593Smuzhiyun 			return error;
2734*4882a593Smuzhiyun 
2735*4882a593Smuzhiyun 		/* Ensure in-dongle supplicant is turned on when FBT wants to do the 4-way
2736*4882a593Smuzhiyun 		 * handshake.
2737*4882a593Smuzhiyun 		 */
2738*4882a593Smuzhiyun 		if (dev_wlc_intvar_get(dev, "fbt_cap", &fbt_cap) == 0) {
2739*4882a593Smuzhiyun 			if (fbt_cap == WLC_FBT_CAP_DRV_4WAY_AND_REASSOC) {
2740*4882a593Smuzhiyun 				if ((paramid == IW_AUTH_CIPHER_PAIRWISE) && (val & AES_ENABLED)) {
2741*4882a593Smuzhiyun 					if ((error = dev_wlc_intvar_set(dev, "sup_wpa", 1)))
2742*4882a593Smuzhiyun 						return error;
2743*4882a593Smuzhiyun 				}
2744*4882a593Smuzhiyun 				else if (val == 0) {
2745*4882a593Smuzhiyun 					if ((error = dev_wlc_intvar_set(dev, "sup_wpa", 0)))
2746*4882a593Smuzhiyun 						return error;
2747*4882a593Smuzhiyun 				}
2748*4882a593Smuzhiyun 			}
2749*4882a593Smuzhiyun 		}
2750*4882a593Smuzhiyun 		break;
2751*4882a593Smuzhiyun 	}
2752*4882a593Smuzhiyun 
2753*4882a593Smuzhiyun 	case IW_AUTH_KEY_MGMT:
2754*4882a593Smuzhiyun 		if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val)))
2755*4882a593Smuzhiyun 			return error;
2756*4882a593Smuzhiyun 
2757*4882a593Smuzhiyun 		if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
2758*4882a593Smuzhiyun 			if (paramval & (IW_AUTH_KEY_MGMT_FT_PSK | IW_AUTH_KEY_MGMT_PSK))
2759*4882a593Smuzhiyun 				val = WPA_AUTH_PSK;
2760*4882a593Smuzhiyun 			else
2761*4882a593Smuzhiyun 				val = WPA_AUTH_UNSPECIFIED;
2762*4882a593Smuzhiyun 			if (paramval & (IW_AUTH_KEY_MGMT_FT_802_1X | IW_AUTH_KEY_MGMT_FT_PSK))
2763*4882a593Smuzhiyun 				val |= WPA2_AUTH_FT;
2764*4882a593Smuzhiyun 		}
2765*4882a593Smuzhiyun 		else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
2766*4882a593Smuzhiyun 			if (paramval & (IW_AUTH_KEY_MGMT_FT_PSK | IW_AUTH_KEY_MGMT_PSK))
2767*4882a593Smuzhiyun 				val = WPA2_AUTH_PSK;
2768*4882a593Smuzhiyun 			else
2769*4882a593Smuzhiyun 				val = WPA2_AUTH_UNSPECIFIED;
2770*4882a593Smuzhiyun 			if (paramval & (IW_AUTH_KEY_MGMT_FT_802_1X | IW_AUTH_KEY_MGMT_FT_PSK))
2771*4882a593Smuzhiyun 				val |= WPA2_AUTH_FT;
2772*4882a593Smuzhiyun 		}
2773*4882a593Smuzhiyun #ifdef BCMWAPI_WPI
2774*4882a593Smuzhiyun 		if (paramval & (IW_AUTH_KEY_MGMT_WAPI_PSK | IW_AUTH_KEY_MGMT_WAPI_CERT))
2775*4882a593Smuzhiyun 			val = WAPI_AUTH_UNSPECIFIED;
2776*4882a593Smuzhiyun #endif // endif
2777*4882a593Smuzhiyun 		WL_TRACE(("%s: %d: setting wpa_auth to %d\n", __FUNCTION__, __LINE__, val));
2778*4882a593Smuzhiyun 		if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val)))
2779*4882a593Smuzhiyun 			return error;
2780*4882a593Smuzhiyun 		break;
2781*4882a593Smuzhiyun 
2782*4882a593Smuzhiyun 	case IW_AUTH_TKIP_COUNTERMEASURES:
2783*4882a593Smuzhiyun 		dev_wlc_bufvar_set(dev, "tkip_countermeasures", (char *)&paramval, 1);
2784*4882a593Smuzhiyun 		break;
2785*4882a593Smuzhiyun 
2786*4882a593Smuzhiyun 	case IW_AUTH_80211_AUTH_ALG:
2787*4882a593Smuzhiyun 		/* open shared */
2788*4882a593Smuzhiyun 		WL_ERROR(("Setting the D11auth %d\n", paramval));
2789*4882a593Smuzhiyun 		if (paramval & IW_AUTH_ALG_OPEN_SYSTEM)
2790*4882a593Smuzhiyun 			val = 0;
2791*4882a593Smuzhiyun 		else if (paramval & IW_AUTH_ALG_SHARED_KEY)
2792*4882a593Smuzhiyun 			val = 1;
2793*4882a593Smuzhiyun 		else
2794*4882a593Smuzhiyun 			error = 1;
2795*4882a593Smuzhiyun 		if (!error && (error = dev_wlc_intvar_set(dev, "auth", val)))
2796*4882a593Smuzhiyun 			return error;
2797*4882a593Smuzhiyun 		break;
2798*4882a593Smuzhiyun 
2799*4882a593Smuzhiyun 	case IW_AUTH_WPA_ENABLED:
2800*4882a593Smuzhiyun 		if (paramval == 0) {
2801*4882a593Smuzhiyun 			val = 0;
2802*4882a593Smuzhiyun 			WL_TRACE(("%s: %d: setting wpa_auth to %d\n", __FUNCTION__, __LINE__, val));
2803*4882a593Smuzhiyun 			error = dev_wlc_intvar_set(dev, "wpa_auth", val);
2804*4882a593Smuzhiyun 			return error;
2805*4882a593Smuzhiyun 		}
2806*4882a593Smuzhiyun 		else {
2807*4882a593Smuzhiyun 			/* If WPA is enabled, wpa_auth is set elsewhere */
2808*4882a593Smuzhiyun 		}
2809*4882a593Smuzhiyun 		break;
2810*4882a593Smuzhiyun 
2811*4882a593Smuzhiyun 	case IW_AUTH_DROP_UNENCRYPTED:
2812*4882a593Smuzhiyun 		dev_wlc_bufvar_set(dev, "wsec_restrict", (char *)&paramval, 1);
2813*4882a593Smuzhiyun 		break;
2814*4882a593Smuzhiyun 
2815*4882a593Smuzhiyun 	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
2816*4882a593Smuzhiyun 		dev_wlc_bufvar_set(dev, "rx_unencrypted_eapol", (char *)&paramval, 1);
2817*4882a593Smuzhiyun 		break;
2818*4882a593Smuzhiyun 
2819*4882a593Smuzhiyun #if WIRELESS_EXT > 17
2820*4882a593Smuzhiyun 
2821*4882a593Smuzhiyun 	case IW_AUTH_ROAMING_CONTROL:
2822*4882a593Smuzhiyun 		WL_TRACE(("%s: IW_AUTH_ROAMING_CONTROL\n", __FUNCTION__));
2823*4882a593Smuzhiyun 		/* driver control or user space app control */
2824*4882a593Smuzhiyun 		break;
2825*4882a593Smuzhiyun 
2826*4882a593Smuzhiyun 	case IW_AUTH_PRIVACY_INVOKED: {
2827*4882a593Smuzhiyun 		int wsec;
2828*4882a593Smuzhiyun 
2829*4882a593Smuzhiyun 		if (paramval == 0) {
2830*4882a593Smuzhiyun 			iw->privacy_invoked = FALSE;
2831*4882a593Smuzhiyun 			if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) {
2832*4882a593Smuzhiyun 				WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n"));
2833*4882a593Smuzhiyun 				return error;
2834*4882a593Smuzhiyun 			}
2835*4882a593Smuzhiyun 		} else {
2836*4882a593Smuzhiyun 			iw->privacy_invoked = TRUE;
2837*4882a593Smuzhiyun 			if ((error = dev_wlc_intvar_get(dev, "wsec", &wsec)))
2838*4882a593Smuzhiyun 				return error;
2839*4882a593Smuzhiyun 
2840*4882a593Smuzhiyun 			if (!WSEC_ENABLED(wsec)) {
2841*4882a593Smuzhiyun 				/* if privacy is true, but wsec is false, we are a WPS enrollee */
2842*4882a593Smuzhiyun 				if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", TRUE))) {
2843*4882a593Smuzhiyun 					WL_WSEC(("Failed to set iovar is_WPS_enrollee\n"));
2844*4882a593Smuzhiyun 					return error;
2845*4882a593Smuzhiyun 				}
2846*4882a593Smuzhiyun 			} else {
2847*4882a593Smuzhiyun 				if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) {
2848*4882a593Smuzhiyun 					WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n"));
2849*4882a593Smuzhiyun 					return error;
2850*4882a593Smuzhiyun 				}
2851*4882a593Smuzhiyun 			}
2852*4882a593Smuzhiyun 		}
2853*4882a593Smuzhiyun 		break;
2854*4882a593Smuzhiyun 	}
2855*4882a593Smuzhiyun 
2856*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 17 */
2857*4882a593Smuzhiyun 
2858*4882a593Smuzhiyun #ifdef BCMWAPI_WPI
2859*4882a593Smuzhiyun 
2860*4882a593Smuzhiyun 	case IW_AUTH_WAPI_ENABLED:
2861*4882a593Smuzhiyun 		if ((error = dev_wlc_intvar_get(dev, "wsec", &val)))
2862*4882a593Smuzhiyun 			return error;
2863*4882a593Smuzhiyun 		if (paramval) {
2864*4882a593Smuzhiyun 			val |= SMS4_ENABLED;
2865*4882a593Smuzhiyun 			if ((error = dev_wlc_intvar_set(dev, "wsec", val))) {
2866*4882a593Smuzhiyun 				WL_ERROR(("%s: setting wsec to 0x%0x returned error %d\n",
2867*4882a593Smuzhiyun 					__FUNCTION__, val, error));
2868*4882a593Smuzhiyun 				return error;
2869*4882a593Smuzhiyun 			}
2870*4882a593Smuzhiyun 			if ((error = dev_wlc_intvar_set(dev, "wpa_auth", WAPI_AUTH_UNSPECIFIED))) {
2871*4882a593Smuzhiyun 				WL_ERROR(("%s: setting wpa_auth(%d) returned %d\n",
2872*4882a593Smuzhiyun 					__FUNCTION__, WAPI_AUTH_UNSPECIFIED,
2873*4882a593Smuzhiyun 					error));
2874*4882a593Smuzhiyun 				return error;
2875*4882a593Smuzhiyun 			}
2876*4882a593Smuzhiyun 		}
2877*4882a593Smuzhiyun 
2878*4882a593Smuzhiyun 		break;
2879*4882a593Smuzhiyun 
2880*4882a593Smuzhiyun #endif /* BCMWAPI_WPI */
2881*4882a593Smuzhiyun 
2882*4882a593Smuzhiyun 	default:
2883*4882a593Smuzhiyun 		break;
2884*4882a593Smuzhiyun 	}
2885*4882a593Smuzhiyun 	return 0;
2886*4882a593Smuzhiyun }
2887*4882a593Smuzhiyun #define VAL_PSK(_val) (((_val) & WPA_AUTH_PSK) || ((_val) & WPA2_AUTH_PSK))
2888*4882a593Smuzhiyun 
2889*4882a593Smuzhiyun static int
2890*4882a593Smuzhiyun wl_iw_get_wpaauth(
2891*4882a593Smuzhiyun 	struct net_device *dev,
2892*4882a593Smuzhiyun 	struct iw_request_info *info,
2893*4882a593Smuzhiyun 	struct iw_param *vwrq,
2894*4882a593Smuzhiyun 	char *extra
2895*4882a593Smuzhiyun )
2896*4882a593Smuzhiyun {
2897*4882a593Smuzhiyun 	int error;
2898*4882a593Smuzhiyun 	int paramid;
2899*4882a593Smuzhiyun 	int paramval = 0;
2900*4882a593Smuzhiyun 	int val;
2901*4882a593Smuzhiyun 	wl_iw_t *iw = IW_DEV_IF(dev);
2902*4882a593Smuzhiyun 
2903*4882a593Smuzhiyun 	WL_TRACE(("%s: SIOCGIWAUTH\n", dev->name));
2904*4882a593Smuzhiyun 
2905*4882a593Smuzhiyun 	paramid = vwrq->flags & IW_AUTH_INDEX;
2906*4882a593Smuzhiyun 
2907*4882a593Smuzhiyun 	switch (paramid) {
2908*4882a593Smuzhiyun 	case IW_AUTH_WPA_VERSION:
2909*4882a593Smuzhiyun 		/* supported wpa version disabled or wpa or wpa2 */
2910*4882a593Smuzhiyun 		if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val)))
2911*4882a593Smuzhiyun 			return error;
2912*4882a593Smuzhiyun 		if (val & (WPA_AUTH_NONE | WPA_AUTH_DISABLED))
2913*4882a593Smuzhiyun 			paramval = IW_AUTH_WPA_VERSION_DISABLED;
2914*4882a593Smuzhiyun 		else if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED))
2915*4882a593Smuzhiyun 			paramval = IW_AUTH_WPA_VERSION_WPA;
2916*4882a593Smuzhiyun 		else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED))
2917*4882a593Smuzhiyun 			paramval = IW_AUTH_WPA_VERSION_WPA2;
2918*4882a593Smuzhiyun 		break;
2919*4882a593Smuzhiyun 
2920*4882a593Smuzhiyun 	case IW_AUTH_CIPHER_PAIRWISE:
2921*4882a593Smuzhiyun 		paramval = iw->pwsec;
2922*4882a593Smuzhiyun 		break;
2923*4882a593Smuzhiyun 
2924*4882a593Smuzhiyun 	case IW_AUTH_CIPHER_GROUP:
2925*4882a593Smuzhiyun 		paramval = iw->gwsec;
2926*4882a593Smuzhiyun 		break;
2927*4882a593Smuzhiyun 
2928*4882a593Smuzhiyun 	case IW_AUTH_KEY_MGMT:
2929*4882a593Smuzhiyun 		/* psk, 1x */
2930*4882a593Smuzhiyun 		if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val)))
2931*4882a593Smuzhiyun 			return error;
2932*4882a593Smuzhiyun 		if (VAL_PSK(val))
2933*4882a593Smuzhiyun 			paramval = IW_AUTH_KEY_MGMT_PSK;
2934*4882a593Smuzhiyun 		else
2935*4882a593Smuzhiyun 			paramval = IW_AUTH_KEY_MGMT_802_1X;
2936*4882a593Smuzhiyun 
2937*4882a593Smuzhiyun 		break;
2938*4882a593Smuzhiyun 	case IW_AUTH_TKIP_COUNTERMEASURES:
2939*4882a593Smuzhiyun 		dev_wlc_bufvar_get(dev, "tkip_countermeasures", (char *)&paramval, 1);
2940*4882a593Smuzhiyun 		break;
2941*4882a593Smuzhiyun 
2942*4882a593Smuzhiyun 	case IW_AUTH_DROP_UNENCRYPTED:
2943*4882a593Smuzhiyun 		dev_wlc_bufvar_get(dev, "wsec_restrict", (char *)&paramval, 1);
2944*4882a593Smuzhiyun 		break;
2945*4882a593Smuzhiyun 
2946*4882a593Smuzhiyun 	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
2947*4882a593Smuzhiyun 		dev_wlc_bufvar_get(dev, "rx_unencrypted_eapol", (char *)&paramval, 1);
2948*4882a593Smuzhiyun 		break;
2949*4882a593Smuzhiyun 
2950*4882a593Smuzhiyun 	case IW_AUTH_80211_AUTH_ALG:
2951*4882a593Smuzhiyun 		/* open, shared, leap */
2952*4882a593Smuzhiyun 		if ((error = dev_wlc_intvar_get(dev, "auth", &val)))
2953*4882a593Smuzhiyun 			return error;
2954*4882a593Smuzhiyun 		if (!val)
2955*4882a593Smuzhiyun 			paramval = IW_AUTH_ALG_OPEN_SYSTEM;
2956*4882a593Smuzhiyun 		else
2957*4882a593Smuzhiyun 			paramval = IW_AUTH_ALG_SHARED_KEY;
2958*4882a593Smuzhiyun 		break;
2959*4882a593Smuzhiyun 	case IW_AUTH_WPA_ENABLED:
2960*4882a593Smuzhiyun 		if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val)))
2961*4882a593Smuzhiyun 			return error;
2962*4882a593Smuzhiyun 		if (val)
2963*4882a593Smuzhiyun 			paramval = TRUE;
2964*4882a593Smuzhiyun 		else
2965*4882a593Smuzhiyun 			paramval = FALSE;
2966*4882a593Smuzhiyun 		break;
2967*4882a593Smuzhiyun 
2968*4882a593Smuzhiyun #if WIRELESS_EXT > 17
2969*4882a593Smuzhiyun 
2970*4882a593Smuzhiyun 	case IW_AUTH_ROAMING_CONTROL:
2971*4882a593Smuzhiyun 		WL_ERROR(("%s: IW_AUTH_ROAMING_CONTROL\n", __FUNCTION__));
2972*4882a593Smuzhiyun 		/* driver control or user space app control */
2973*4882a593Smuzhiyun 		break;
2974*4882a593Smuzhiyun 
2975*4882a593Smuzhiyun 	case IW_AUTH_PRIVACY_INVOKED:
2976*4882a593Smuzhiyun 		paramval = iw->privacy_invoked;
2977*4882a593Smuzhiyun 		break;
2978*4882a593Smuzhiyun 
2979*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 17 */
2980*4882a593Smuzhiyun 	}
2981*4882a593Smuzhiyun 	vwrq->value = paramval;
2982*4882a593Smuzhiyun 	return 0;
2983*4882a593Smuzhiyun }
2984*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 17 */
2985*4882a593Smuzhiyun 
2986*4882a593Smuzhiyun static const iw_handler wl_iw_handler[] =
2987*4882a593Smuzhiyun {
2988*4882a593Smuzhiyun 	(iw_handler) wl_iw_config_commit,	/* SIOCSIWCOMMIT */
2989*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_name,		/* SIOCGIWNAME */
2990*4882a593Smuzhiyun 	(iw_handler) NULL,			/* SIOCSIWNWID */
2991*4882a593Smuzhiyun 	(iw_handler) NULL,			/* SIOCGIWNWID */
2992*4882a593Smuzhiyun 	(iw_handler) wl_iw_set_freq,		/* SIOCSIWFREQ */
2993*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_freq,		/* SIOCGIWFREQ */
2994*4882a593Smuzhiyun 	(iw_handler) wl_iw_set_mode,		/* SIOCSIWMODE */
2995*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_mode,		/* SIOCGIWMODE */
2996*4882a593Smuzhiyun 	(iw_handler) NULL,			/* SIOCSIWSENS */
2997*4882a593Smuzhiyun 	(iw_handler) NULL,			/* SIOCGIWSENS */
2998*4882a593Smuzhiyun 	(iw_handler) NULL,			/* SIOCSIWRANGE */
2999*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_range,		/* SIOCGIWRANGE */
3000*4882a593Smuzhiyun 	(iw_handler) NULL,			/* SIOCSIWPRIV */
3001*4882a593Smuzhiyun 	(iw_handler) NULL,			/* SIOCGIWPRIV */
3002*4882a593Smuzhiyun 	(iw_handler) NULL,			/* SIOCSIWSTATS */
3003*4882a593Smuzhiyun 	(iw_handler) NULL,			/* SIOCGIWSTATS */
3004*4882a593Smuzhiyun 	(iw_handler) wl_iw_set_spy,		/* SIOCSIWSPY */
3005*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_spy,		/* SIOCGIWSPY */
3006*4882a593Smuzhiyun 	(iw_handler) NULL,			/* -- hole -- */
3007*4882a593Smuzhiyun 	(iw_handler) NULL,			/* -- hole -- */
3008*4882a593Smuzhiyun 	(iw_handler) wl_iw_set_wap,		/* SIOCSIWAP */
3009*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_wap,		/* SIOCGIWAP */
3010*4882a593Smuzhiyun #if WIRELESS_EXT > 17
3011*4882a593Smuzhiyun 	(iw_handler) wl_iw_mlme,		/* SIOCSIWMLME */
3012*4882a593Smuzhiyun #else
3013*4882a593Smuzhiyun 	(iw_handler) NULL,			/* -- hole -- */
3014*4882a593Smuzhiyun #endif // endif
3015*4882a593Smuzhiyun 	(iw_handler) wl_iw_iscan_get_aplist,	/* SIOCGIWAPLIST */
3016*4882a593Smuzhiyun #if WIRELESS_EXT > 13
3017*4882a593Smuzhiyun 	(iw_handler) wl_iw_iscan_set_scan,	/* SIOCSIWSCAN */
3018*4882a593Smuzhiyun 	(iw_handler) wl_iw_iscan_get_scan,	/* SIOCGIWSCAN */
3019*4882a593Smuzhiyun #else	/* WIRELESS_EXT > 13 */
3020*4882a593Smuzhiyun 	(iw_handler) NULL,			/* SIOCSIWSCAN */
3021*4882a593Smuzhiyun 	(iw_handler) NULL,			/* SIOCGIWSCAN */
3022*4882a593Smuzhiyun #endif	/* WIRELESS_EXT > 13 */
3023*4882a593Smuzhiyun 	(iw_handler) wl_iw_set_essid,		/* SIOCSIWESSID */
3024*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_essid,		/* SIOCGIWESSID */
3025*4882a593Smuzhiyun 	(iw_handler) wl_iw_set_nick,		/* SIOCSIWNICKN */
3026*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_nick,		/* SIOCGIWNICKN */
3027*4882a593Smuzhiyun 	(iw_handler) NULL,			/* -- hole -- */
3028*4882a593Smuzhiyun 	(iw_handler) NULL,			/* -- hole -- */
3029*4882a593Smuzhiyun 	(iw_handler) wl_iw_set_rate,		/* SIOCSIWRATE */
3030*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_rate,		/* SIOCGIWRATE */
3031*4882a593Smuzhiyun 	(iw_handler) wl_iw_set_rts,		/* SIOCSIWRTS */
3032*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_rts,		/* SIOCGIWRTS */
3033*4882a593Smuzhiyun 	(iw_handler) wl_iw_set_frag,		/* SIOCSIWFRAG */
3034*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_frag,		/* SIOCGIWFRAG */
3035*4882a593Smuzhiyun 	(iw_handler) wl_iw_set_txpow,		/* SIOCSIWTXPOW */
3036*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_txpow,		/* SIOCGIWTXPOW */
3037*4882a593Smuzhiyun #if WIRELESS_EXT > 10
3038*4882a593Smuzhiyun 	(iw_handler) wl_iw_set_retry,		/* SIOCSIWRETRY */
3039*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_retry,		/* SIOCGIWRETRY */
3040*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 10 */
3041*4882a593Smuzhiyun 	(iw_handler) wl_iw_set_encode,		/* SIOCSIWENCODE */
3042*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_encode,		/* SIOCGIWENCODE */
3043*4882a593Smuzhiyun 	(iw_handler) wl_iw_set_power,		/* SIOCSIWPOWER */
3044*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_power,		/* SIOCGIWPOWER */
3045*4882a593Smuzhiyun #if WIRELESS_EXT > 17
3046*4882a593Smuzhiyun 	(iw_handler) NULL,			/* -- hole -- */
3047*4882a593Smuzhiyun 	(iw_handler) NULL,			/* -- hole -- */
3048*4882a593Smuzhiyun 	(iw_handler) wl_iw_set_wpaie,		/* SIOCSIWGENIE */
3049*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_wpaie,		/* SIOCGIWGENIE */
3050*4882a593Smuzhiyun 	(iw_handler) wl_iw_set_wpaauth,		/* SIOCSIWAUTH */
3051*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_wpaauth,		/* SIOCGIWAUTH */
3052*4882a593Smuzhiyun 	(iw_handler) wl_iw_set_encodeext,	/* SIOCSIWENCODEEXT */
3053*4882a593Smuzhiyun 	(iw_handler) wl_iw_get_encodeext,	/* SIOCGIWENCODEEXT */
3054*4882a593Smuzhiyun 	(iw_handler) wl_iw_set_pmksa,		/* SIOCSIWPMKSA */
3055*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 17 */
3056*4882a593Smuzhiyun };
3057*4882a593Smuzhiyun 
3058*4882a593Smuzhiyun #if WIRELESS_EXT > 12
3059*4882a593Smuzhiyun enum {
3060*4882a593Smuzhiyun 	WL_IW_SET_LEDDC = SIOCIWFIRSTPRIV,
3061*4882a593Smuzhiyun 	WL_IW_SET_VLANMODE,
3062*4882a593Smuzhiyun 	WL_IW_SET_PM,
3063*4882a593Smuzhiyun #if WIRELESS_EXT > 17
3064*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 17 */
3065*4882a593Smuzhiyun 	WL_IW_SET_LAST
3066*4882a593Smuzhiyun };
3067*4882a593Smuzhiyun 
3068*4882a593Smuzhiyun static iw_handler wl_iw_priv_handler[] = {
3069*4882a593Smuzhiyun 	wl_iw_set_leddc,
3070*4882a593Smuzhiyun 	wl_iw_set_vlanmode,
3071*4882a593Smuzhiyun 	wl_iw_set_pm,
3072*4882a593Smuzhiyun #if WIRELESS_EXT > 17
3073*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 17 */
3074*4882a593Smuzhiyun 	NULL
3075*4882a593Smuzhiyun };
3076*4882a593Smuzhiyun 
3077*4882a593Smuzhiyun static struct iw_priv_args wl_iw_priv_args[] = {
3078*4882a593Smuzhiyun 	{
3079*4882a593Smuzhiyun 		WL_IW_SET_LEDDC,
3080*4882a593Smuzhiyun 		IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
3081*4882a593Smuzhiyun 		0,
3082*4882a593Smuzhiyun 		"set_leddc"
3083*4882a593Smuzhiyun 	},
3084*4882a593Smuzhiyun 	{
3085*4882a593Smuzhiyun 		WL_IW_SET_VLANMODE,
3086*4882a593Smuzhiyun 		IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
3087*4882a593Smuzhiyun 		0,
3088*4882a593Smuzhiyun 		"set_vlanmode"
3089*4882a593Smuzhiyun 	},
3090*4882a593Smuzhiyun 	{
3091*4882a593Smuzhiyun 		WL_IW_SET_PM,
3092*4882a593Smuzhiyun 		IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
3093*4882a593Smuzhiyun 		0,
3094*4882a593Smuzhiyun 		"set_pm"
3095*4882a593Smuzhiyun 	},
3096*4882a593Smuzhiyun #if WIRELESS_EXT > 17
3097*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 17 */
3098*4882a593Smuzhiyun 	{ 0, 0, 0, { 0 } }
3099*4882a593Smuzhiyun };
3100*4882a593Smuzhiyun 
3101*4882a593Smuzhiyun const struct iw_handler_def wl_iw_handler_def =
3102*4882a593Smuzhiyun {
3103*4882a593Smuzhiyun 	.num_standard = ARRAYSIZE(wl_iw_handler),
3104*4882a593Smuzhiyun 	.num_private = ARRAY_SIZE(wl_iw_priv_handler),
3105*4882a593Smuzhiyun 	.num_private_args = ARRAY_SIZE(wl_iw_priv_args),
3106*4882a593Smuzhiyun 	.standard = (const iw_handler *) wl_iw_handler,
3107*4882a593Smuzhiyun 	.private = wl_iw_priv_handler,
3108*4882a593Smuzhiyun 	.private_args = wl_iw_priv_args,
3109*4882a593Smuzhiyun #if WIRELESS_EXT >= 19
3110*4882a593Smuzhiyun 	get_wireless_stats: dhd_get_wireless_stats,
3111*4882a593Smuzhiyun #endif /* WIRELESS_EXT >= 19 */
3112*4882a593Smuzhiyun 	};
3113*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 12 */
3114*4882a593Smuzhiyun 
3115*4882a593Smuzhiyun int
3116*4882a593Smuzhiyun wl_iw_ioctl(
3117*4882a593Smuzhiyun 	struct net_device *dev,
3118*4882a593Smuzhiyun 	struct ifreq *rq,
3119*4882a593Smuzhiyun 	int cmd
3120*4882a593Smuzhiyun )
3121*4882a593Smuzhiyun {
3122*4882a593Smuzhiyun 	struct iwreq *wrq = (struct iwreq *) rq;
3123*4882a593Smuzhiyun 	struct iw_request_info info;
3124*4882a593Smuzhiyun 	iw_handler handler;
3125*4882a593Smuzhiyun 	char *extra = NULL;
3126*4882a593Smuzhiyun 	size_t token_size = 1;
3127*4882a593Smuzhiyun 	int max_tokens = 0, ret = 0;
3128*4882a593Smuzhiyun 
3129*4882a593Smuzhiyun 	if (cmd < SIOCIWFIRST ||
3130*4882a593Smuzhiyun 		IW_IOCTL_IDX(cmd) >= ARRAYSIZE(wl_iw_handler) ||
3131*4882a593Smuzhiyun 		!(handler = wl_iw_handler[IW_IOCTL_IDX(cmd)]))
3132*4882a593Smuzhiyun 		return -EOPNOTSUPP;
3133*4882a593Smuzhiyun 
3134*4882a593Smuzhiyun 	switch (cmd) {
3135*4882a593Smuzhiyun 
3136*4882a593Smuzhiyun 	case SIOCSIWESSID:
3137*4882a593Smuzhiyun 	case SIOCGIWESSID:
3138*4882a593Smuzhiyun 	case SIOCSIWNICKN:
3139*4882a593Smuzhiyun 	case SIOCGIWNICKN:
3140*4882a593Smuzhiyun 		max_tokens = IW_ESSID_MAX_SIZE + 1;
3141*4882a593Smuzhiyun 		break;
3142*4882a593Smuzhiyun 
3143*4882a593Smuzhiyun 	case SIOCSIWENCODE:
3144*4882a593Smuzhiyun 	case SIOCGIWENCODE:
3145*4882a593Smuzhiyun #if WIRELESS_EXT > 17
3146*4882a593Smuzhiyun 	case SIOCSIWENCODEEXT:
3147*4882a593Smuzhiyun 	case SIOCGIWENCODEEXT:
3148*4882a593Smuzhiyun #endif // endif
3149*4882a593Smuzhiyun 		max_tokens = IW_ENCODING_TOKEN_MAX;
3150*4882a593Smuzhiyun 		break;
3151*4882a593Smuzhiyun 
3152*4882a593Smuzhiyun 	case SIOCGIWRANGE:
3153*4882a593Smuzhiyun 		max_tokens = sizeof(struct iw_range);
3154*4882a593Smuzhiyun 		break;
3155*4882a593Smuzhiyun 
3156*4882a593Smuzhiyun 	case SIOCGIWAPLIST:
3157*4882a593Smuzhiyun 		token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality);
3158*4882a593Smuzhiyun 		max_tokens = IW_MAX_AP;
3159*4882a593Smuzhiyun 		break;
3160*4882a593Smuzhiyun 
3161*4882a593Smuzhiyun #if WIRELESS_EXT > 13
3162*4882a593Smuzhiyun 	case SIOCGIWSCAN:
3163*4882a593Smuzhiyun 		if (g_iscan) {
3164*4882a593Smuzhiyun 			max_tokens = wrq->u.data.length;
3165*4882a593Smuzhiyun 		}
3166*4882a593Smuzhiyun 		else
3167*4882a593Smuzhiyun 		{
3168*4882a593Smuzhiyun 			max_tokens = IW_SCAN_MAX_DATA;
3169*4882a593Smuzhiyun 		}
3170*4882a593Smuzhiyun 		break;
3171*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 13 */
3172*4882a593Smuzhiyun 
3173*4882a593Smuzhiyun 	case SIOCSIWSPY:
3174*4882a593Smuzhiyun 		token_size = sizeof(struct sockaddr);
3175*4882a593Smuzhiyun 		max_tokens = IW_MAX_SPY;
3176*4882a593Smuzhiyun 		break;
3177*4882a593Smuzhiyun 
3178*4882a593Smuzhiyun 	case SIOCGIWSPY:
3179*4882a593Smuzhiyun 		token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality);
3180*4882a593Smuzhiyun 		max_tokens = IW_MAX_SPY;
3181*4882a593Smuzhiyun 		break;
3182*4882a593Smuzhiyun 	default:
3183*4882a593Smuzhiyun 		break;
3184*4882a593Smuzhiyun 	}
3185*4882a593Smuzhiyun 
3186*4882a593Smuzhiyun 	if (max_tokens && wrq->u.data.pointer) {
3187*4882a593Smuzhiyun 		if (wrq->u.data.length > max_tokens)
3188*4882a593Smuzhiyun 			return -E2BIG;
3189*4882a593Smuzhiyun 
3190*4882a593Smuzhiyun 		if (!(extra = kmalloc(max_tokens * token_size, GFP_KERNEL)))
3191*4882a593Smuzhiyun 			return -ENOMEM;
3192*4882a593Smuzhiyun 
3193*4882a593Smuzhiyun 		if (copy_from_user(extra, wrq->u.data.pointer, wrq->u.data.length * token_size)) {
3194*4882a593Smuzhiyun 			kfree(extra);
3195*4882a593Smuzhiyun 			return -EFAULT;
3196*4882a593Smuzhiyun 		}
3197*4882a593Smuzhiyun 	}
3198*4882a593Smuzhiyun 
3199*4882a593Smuzhiyun 	info.cmd = cmd;
3200*4882a593Smuzhiyun 	info.flags = 0;
3201*4882a593Smuzhiyun 
3202*4882a593Smuzhiyun 	ret = handler(dev, &info, &wrq->u, extra);
3203*4882a593Smuzhiyun 
3204*4882a593Smuzhiyun 	if (extra) {
3205*4882a593Smuzhiyun 		if (copy_to_user(wrq->u.data.pointer, extra, wrq->u.data.length * token_size)) {
3206*4882a593Smuzhiyun 			kfree(extra);
3207*4882a593Smuzhiyun 			return -EFAULT;
3208*4882a593Smuzhiyun 		}
3209*4882a593Smuzhiyun 
3210*4882a593Smuzhiyun 		kfree(extra);
3211*4882a593Smuzhiyun 	}
3212*4882a593Smuzhiyun 
3213*4882a593Smuzhiyun 	return ret;
3214*4882a593Smuzhiyun }
3215*4882a593Smuzhiyun 
3216*4882a593Smuzhiyun /* Convert a connection status event into a connection status string.
3217*4882a593Smuzhiyun  * Returns TRUE if a matching connection status string was found.
3218*4882a593Smuzhiyun  */
3219*4882a593Smuzhiyun bool
3220*4882a593Smuzhiyun wl_iw_conn_status_str(uint32 event_type, uint32 status, uint32 reason,
3221*4882a593Smuzhiyun 	char* stringBuf, uint buflen)
3222*4882a593Smuzhiyun {
3223*4882a593Smuzhiyun 	typedef struct conn_fail_event_map_t {
3224*4882a593Smuzhiyun 		uint32 inEvent;			/* input: event type to match */
3225*4882a593Smuzhiyun 		uint32 inStatus;		/* input: event status code to match */
3226*4882a593Smuzhiyun 		uint32 inReason;		/* input: event reason code to match */
3227*4882a593Smuzhiyun 		const char* outName;	/* output: failure type */
3228*4882a593Smuzhiyun 		const char* outCause;	/* output: failure cause */
3229*4882a593Smuzhiyun 	} conn_fail_event_map_t;
3230*4882a593Smuzhiyun 
3231*4882a593Smuzhiyun 	/* Map of WLC_E events to connection failure strings */
3232*4882a593Smuzhiyun #	define WL_IW_DONT_CARE	9999
3233*4882a593Smuzhiyun 	const conn_fail_event_map_t event_map [] = {
3234*4882a593Smuzhiyun 		/* inEvent           inStatus                inReason         */
3235*4882a593Smuzhiyun 		/* outName outCause                                           */
3236*4882a593Smuzhiyun 		{WLC_E_SET_SSID,     WLC_E_STATUS_SUCCESS,   WL_IW_DONT_CARE,
3237*4882a593Smuzhiyun 		"Conn", "Success"},
3238*4882a593Smuzhiyun 		{WLC_E_SET_SSID,     WLC_E_STATUS_NO_NETWORKS, WL_IW_DONT_CARE,
3239*4882a593Smuzhiyun 		"Conn", "NoNetworks"},
3240*4882a593Smuzhiyun 		{WLC_E_SET_SSID,     WLC_E_STATUS_FAIL,      WL_IW_DONT_CARE,
3241*4882a593Smuzhiyun 		"Conn", "ConfigMismatch"},
3242*4882a593Smuzhiyun 		{WLC_E_PRUNE,        WL_IW_DONT_CARE,        WLC_E_PRUNE_ENCR_MISMATCH,
3243*4882a593Smuzhiyun 		"Conn", "EncrypMismatch"},
3244*4882a593Smuzhiyun 		{WLC_E_PRUNE,        WL_IW_DONT_CARE,        WLC_E_RSN_MISMATCH,
3245*4882a593Smuzhiyun 		"Conn", "RsnMismatch"},
3246*4882a593Smuzhiyun 		{WLC_E_AUTH,         WLC_E_STATUS_TIMEOUT,   WL_IW_DONT_CARE,
3247*4882a593Smuzhiyun 		"Conn", "AuthTimeout"},
3248*4882a593Smuzhiyun 		{WLC_E_AUTH,         WLC_E_STATUS_FAIL,      WL_IW_DONT_CARE,
3249*4882a593Smuzhiyun 		"Conn", "AuthFail"},
3250*4882a593Smuzhiyun 		{WLC_E_AUTH,         WLC_E_STATUS_NO_ACK,    WL_IW_DONT_CARE,
3251*4882a593Smuzhiyun 		"Conn", "AuthNoAck"},
3252*4882a593Smuzhiyun 		{WLC_E_REASSOC,      WLC_E_STATUS_FAIL,      WL_IW_DONT_CARE,
3253*4882a593Smuzhiyun 		"Conn", "ReassocFail"},
3254*4882a593Smuzhiyun 		{WLC_E_REASSOC,      WLC_E_STATUS_TIMEOUT,   WL_IW_DONT_CARE,
3255*4882a593Smuzhiyun 		"Conn", "ReassocTimeout"},
3256*4882a593Smuzhiyun 		{WLC_E_REASSOC,      WLC_E_STATUS_ABORT,     WL_IW_DONT_CARE,
3257*4882a593Smuzhiyun 		"Conn", "ReassocAbort"},
3258*4882a593Smuzhiyun 		{WLC_E_PSK_SUP,      WLC_SUP_KEYED,          WL_IW_DONT_CARE,
3259*4882a593Smuzhiyun 		"Sup", "ConnSuccess"},
3260*4882a593Smuzhiyun 		{WLC_E_PSK_SUP,      WL_IW_DONT_CARE,        WL_IW_DONT_CARE,
3261*4882a593Smuzhiyun 		"Sup", "WpaHandshakeFail"},
3262*4882a593Smuzhiyun 		{WLC_E_DEAUTH_IND,   WL_IW_DONT_CARE,        WL_IW_DONT_CARE,
3263*4882a593Smuzhiyun 		"Conn", "Deauth"},
3264*4882a593Smuzhiyun 		{WLC_E_DISASSOC_IND, WL_IW_DONT_CARE,        WL_IW_DONT_CARE,
3265*4882a593Smuzhiyun 		"Conn", "DisassocInd"},
3266*4882a593Smuzhiyun 		{WLC_E_DISASSOC,     WL_IW_DONT_CARE,        WL_IW_DONT_CARE,
3267*4882a593Smuzhiyun 		"Conn", "Disassoc"}
3268*4882a593Smuzhiyun 	};
3269*4882a593Smuzhiyun 
3270*4882a593Smuzhiyun 	const char* name = "";
3271*4882a593Smuzhiyun 	const char* cause = NULL;
3272*4882a593Smuzhiyun 	int i;
3273*4882a593Smuzhiyun 
3274*4882a593Smuzhiyun 	/* Search the event map table for a matching event */
3275*4882a593Smuzhiyun 	for (i = 0;  i < sizeof(event_map)/sizeof(event_map[0]);  i++) {
3276*4882a593Smuzhiyun 		const conn_fail_event_map_t* row = &event_map[i];
3277*4882a593Smuzhiyun 		if (row->inEvent == event_type &&
3278*4882a593Smuzhiyun 		    (row->inStatus == status || row->inStatus == WL_IW_DONT_CARE) &&
3279*4882a593Smuzhiyun 		    (row->inReason == reason || row->inReason == WL_IW_DONT_CARE)) {
3280*4882a593Smuzhiyun 			name = row->outName;
3281*4882a593Smuzhiyun 			cause = row->outCause;
3282*4882a593Smuzhiyun 			break;
3283*4882a593Smuzhiyun 		}
3284*4882a593Smuzhiyun 	}
3285*4882a593Smuzhiyun 
3286*4882a593Smuzhiyun 	/* If found, generate a connection failure string and return TRUE */
3287*4882a593Smuzhiyun 	if (cause) {
3288*4882a593Smuzhiyun 		memset(stringBuf, 0, buflen);
3289*4882a593Smuzhiyun 		(void)snprintf(stringBuf, buflen, "%s %s %02d %02d", name, cause, status, reason);
3290*4882a593Smuzhiyun 		WL_TRACE(("Connection status: %s\n", stringBuf));
3291*4882a593Smuzhiyun 		return TRUE;
3292*4882a593Smuzhiyun 	} else {
3293*4882a593Smuzhiyun 		return FALSE;
3294*4882a593Smuzhiyun 	}
3295*4882a593Smuzhiyun }
3296*4882a593Smuzhiyun 
3297*4882a593Smuzhiyun #if (WIRELESS_EXT > 14)
3298*4882a593Smuzhiyun /* Check if we have received an event that indicates connection failure
3299*4882a593Smuzhiyun  * If so, generate a connection failure report string.
3300*4882a593Smuzhiyun  * The caller supplies a buffer to hold the generated string.
3301*4882a593Smuzhiyun  */
3302*4882a593Smuzhiyun static bool
3303*4882a593Smuzhiyun wl_iw_check_conn_fail(wl_event_msg_t *e, char* stringBuf, uint buflen)
3304*4882a593Smuzhiyun {
3305*4882a593Smuzhiyun 	uint32 event = ntoh32(e->event_type);
3306*4882a593Smuzhiyun 	uint32 status =  ntoh32(e->status);
3307*4882a593Smuzhiyun 	uint32 reason =  ntoh32(e->reason);
3308*4882a593Smuzhiyun 
3309*4882a593Smuzhiyun 	if (wl_iw_conn_status_str(event, status, reason, stringBuf, buflen)) {
3310*4882a593Smuzhiyun 		return TRUE;
3311*4882a593Smuzhiyun 	} else
3312*4882a593Smuzhiyun 	{
3313*4882a593Smuzhiyun 		return FALSE;
3314*4882a593Smuzhiyun 	}
3315*4882a593Smuzhiyun }
3316*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 14 */
3317*4882a593Smuzhiyun 
3318*4882a593Smuzhiyun #ifndef IW_CUSTOM_MAX
3319*4882a593Smuzhiyun #define IW_CUSTOM_MAX 256 /* size of extra buffer used for translation of events */
3320*4882a593Smuzhiyun #endif /* IW_CUSTOM_MAX */
3321*4882a593Smuzhiyun 
3322*4882a593Smuzhiyun void
3323*4882a593Smuzhiyun wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data)
3324*4882a593Smuzhiyun {
3325*4882a593Smuzhiyun #if WIRELESS_EXT > 13
3326*4882a593Smuzhiyun 	union iwreq_data wrqu;
3327*4882a593Smuzhiyun 	char extra[IW_CUSTOM_MAX + 1];
3328*4882a593Smuzhiyun 	int cmd = 0;
3329*4882a593Smuzhiyun 	uint32 event_type = ntoh32(e->event_type);
3330*4882a593Smuzhiyun 	uint16 flags =  ntoh16(e->flags);
3331*4882a593Smuzhiyun 	uint32 datalen = ntoh32(e->datalen);
3332*4882a593Smuzhiyun 	uint32 status =  ntoh32(e->status);
3333*4882a593Smuzhiyun 
3334*4882a593Smuzhiyun 	memset(&wrqu, 0, sizeof(wrqu));
3335*4882a593Smuzhiyun 	memset(extra, 0, sizeof(extra));
3336*4882a593Smuzhiyun 
3337*4882a593Smuzhiyun 	memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN);
3338*4882a593Smuzhiyun 	wrqu.addr.sa_family = ARPHRD_ETHER;
3339*4882a593Smuzhiyun 
3340*4882a593Smuzhiyun 	switch (event_type) {
3341*4882a593Smuzhiyun 	case WLC_E_TXFAIL:
3342*4882a593Smuzhiyun 		cmd = IWEVTXDROP;
3343*4882a593Smuzhiyun 		break;
3344*4882a593Smuzhiyun #if WIRELESS_EXT > 14
3345*4882a593Smuzhiyun 	case WLC_E_JOIN:
3346*4882a593Smuzhiyun 	case WLC_E_ASSOC_IND:
3347*4882a593Smuzhiyun 	case WLC_E_REASSOC_IND:
3348*4882a593Smuzhiyun 		cmd = IWEVREGISTERED;
3349*4882a593Smuzhiyun 		break;
3350*4882a593Smuzhiyun 	case WLC_E_DEAUTH_IND:
3351*4882a593Smuzhiyun 	case WLC_E_DISASSOC_IND:
3352*4882a593Smuzhiyun 		cmd = SIOCGIWAP;
3353*4882a593Smuzhiyun 		wrqu.data.length = strlen(extra);
3354*4882a593Smuzhiyun 		bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN);
3355*4882a593Smuzhiyun 		bzero(&extra, ETHER_ADDR_LEN);
3356*4882a593Smuzhiyun 		break;
3357*4882a593Smuzhiyun 
3358*4882a593Smuzhiyun 	case WLC_E_LINK:
3359*4882a593Smuzhiyun 		cmd = SIOCGIWAP;
3360*4882a593Smuzhiyun 		wrqu.data.length = strlen(extra);
3361*4882a593Smuzhiyun 		if (!(flags & WLC_EVENT_MSG_LINK)) {
3362*4882a593Smuzhiyun 			bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN);
3363*4882a593Smuzhiyun 			bzero(&extra, ETHER_ADDR_LEN);
3364*4882a593Smuzhiyun 		}
3365*4882a593Smuzhiyun 		break;
3366*4882a593Smuzhiyun 	case WLC_E_ACTION_FRAME:
3367*4882a593Smuzhiyun 		cmd = IWEVCUSTOM;
3368*4882a593Smuzhiyun 		if (datalen + 1 <= sizeof(extra)) {
3369*4882a593Smuzhiyun 			wrqu.data.length = datalen + 1;
3370*4882a593Smuzhiyun 			extra[0] = WLC_E_ACTION_FRAME;
3371*4882a593Smuzhiyun 			memcpy(&extra[1], data, datalen);
3372*4882a593Smuzhiyun 			WL_TRACE(("WLC_E_ACTION_FRAME len %d \n", wrqu.data.length));
3373*4882a593Smuzhiyun 		}
3374*4882a593Smuzhiyun 		break;
3375*4882a593Smuzhiyun 
3376*4882a593Smuzhiyun 	case WLC_E_ACTION_FRAME_COMPLETE:
3377*4882a593Smuzhiyun 		cmd = IWEVCUSTOM;
3378*4882a593Smuzhiyun 		if (sizeof(status) + 1 <= sizeof(extra)) {
3379*4882a593Smuzhiyun 			wrqu.data.length = sizeof(status) + 1;
3380*4882a593Smuzhiyun 			extra[0] = WLC_E_ACTION_FRAME_COMPLETE;
3381*4882a593Smuzhiyun 			memcpy(&extra[1], &status, sizeof(status));
3382*4882a593Smuzhiyun 			WL_TRACE(("wl_iw_event status %d  \n", status));
3383*4882a593Smuzhiyun 		}
3384*4882a593Smuzhiyun 		break;
3385*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 14 */
3386*4882a593Smuzhiyun #if WIRELESS_EXT > 17
3387*4882a593Smuzhiyun 	case WLC_E_MIC_ERROR: {
3388*4882a593Smuzhiyun 		struct	iw_michaelmicfailure  *micerrevt = (struct  iw_michaelmicfailure  *)&extra;
3389*4882a593Smuzhiyun 		cmd = IWEVMICHAELMICFAILURE;
3390*4882a593Smuzhiyun 		wrqu.data.length = sizeof(struct iw_michaelmicfailure);
3391*4882a593Smuzhiyun 		if (flags & WLC_EVENT_MSG_GROUP)
3392*4882a593Smuzhiyun 			micerrevt->flags |= IW_MICFAILURE_GROUP;
3393*4882a593Smuzhiyun 		else
3394*4882a593Smuzhiyun 			micerrevt->flags |= IW_MICFAILURE_PAIRWISE;
3395*4882a593Smuzhiyun 		memcpy(micerrevt->src_addr.sa_data, &e->addr, ETHER_ADDR_LEN);
3396*4882a593Smuzhiyun 		micerrevt->src_addr.sa_family = ARPHRD_ETHER;
3397*4882a593Smuzhiyun 
3398*4882a593Smuzhiyun 		break;
3399*4882a593Smuzhiyun 	}
3400*4882a593Smuzhiyun 
3401*4882a593Smuzhiyun 	case WLC_E_ASSOC_REQ_IE:
3402*4882a593Smuzhiyun 		cmd = IWEVASSOCREQIE;
3403*4882a593Smuzhiyun 		wrqu.data.length = datalen;
3404*4882a593Smuzhiyun 		if (datalen < sizeof(extra))
3405*4882a593Smuzhiyun 			memcpy(extra, data, datalen);
3406*4882a593Smuzhiyun 		break;
3407*4882a593Smuzhiyun 
3408*4882a593Smuzhiyun 	case WLC_E_ASSOC_RESP_IE:
3409*4882a593Smuzhiyun 		cmd = IWEVASSOCRESPIE;
3410*4882a593Smuzhiyun 		wrqu.data.length = datalen;
3411*4882a593Smuzhiyun 		if (datalen < sizeof(extra))
3412*4882a593Smuzhiyun 			memcpy(extra, data, datalen);
3413*4882a593Smuzhiyun 		break;
3414*4882a593Smuzhiyun 
3415*4882a593Smuzhiyun 	case WLC_E_PMKID_CACHE: {
3416*4882a593Smuzhiyun 		struct iw_pmkid_cand *iwpmkidcand = (struct iw_pmkid_cand *)&extra;
3417*4882a593Smuzhiyun 		pmkid_cand_list_t *pmkcandlist;
3418*4882a593Smuzhiyun 		pmkid_cand_t	*pmkidcand;
3419*4882a593Smuzhiyun 		int count;
3420*4882a593Smuzhiyun 
3421*4882a593Smuzhiyun 		if (data == NULL)
3422*4882a593Smuzhiyun 			break;
3423*4882a593Smuzhiyun 
3424*4882a593Smuzhiyun 		cmd = IWEVPMKIDCAND;
3425*4882a593Smuzhiyun 		pmkcandlist = data;
3426*4882a593Smuzhiyun 		count = ntoh32_ua((uint8 *)&pmkcandlist->npmkid_cand);
3427*4882a593Smuzhiyun 		wrqu.data.length = sizeof(struct iw_pmkid_cand);
3428*4882a593Smuzhiyun 		pmkidcand = pmkcandlist->pmkid_cand;
3429*4882a593Smuzhiyun 		while (count) {
3430*4882a593Smuzhiyun 			bzero(iwpmkidcand, sizeof(struct iw_pmkid_cand));
3431*4882a593Smuzhiyun 			if (pmkidcand->preauth)
3432*4882a593Smuzhiyun 				iwpmkidcand->flags |= IW_PMKID_CAND_PREAUTH;
3433*4882a593Smuzhiyun 			bcopy(&pmkidcand->BSSID, &iwpmkidcand->bssid.sa_data,
3434*4882a593Smuzhiyun 			      ETHER_ADDR_LEN);
3435*4882a593Smuzhiyun 			wireless_send_event(dev, cmd, &wrqu, extra);
3436*4882a593Smuzhiyun 			pmkidcand++;
3437*4882a593Smuzhiyun 			count--;
3438*4882a593Smuzhiyun 		}
3439*4882a593Smuzhiyun 		break;
3440*4882a593Smuzhiyun 	}
3441*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 17 */
3442*4882a593Smuzhiyun 
3443*4882a593Smuzhiyun 	case WLC_E_SCAN_COMPLETE:
3444*4882a593Smuzhiyun #if WIRELESS_EXT > 14
3445*4882a593Smuzhiyun 		cmd = SIOCGIWSCAN;
3446*4882a593Smuzhiyun #endif // endif
3447*4882a593Smuzhiyun 		WL_TRACE(("event WLC_E_SCAN_COMPLETE\n"));
3448*4882a593Smuzhiyun 		if ((g_iscan) && (g_iscan->sysioc_pid >= 0) &&
3449*4882a593Smuzhiyun 			(g_iscan->iscan_state != ISCAN_STATE_IDLE))
3450*4882a593Smuzhiyun 			up(&g_iscan->sysioc_sem);
3451*4882a593Smuzhiyun 		break;
3452*4882a593Smuzhiyun 
3453*4882a593Smuzhiyun 	default:
3454*4882a593Smuzhiyun 		/* Cannot translate event */
3455*4882a593Smuzhiyun 		break;
3456*4882a593Smuzhiyun 	}
3457*4882a593Smuzhiyun 
3458*4882a593Smuzhiyun 	if (cmd) {
3459*4882a593Smuzhiyun 		if (cmd == SIOCGIWSCAN)
3460*4882a593Smuzhiyun 			wireless_send_event(dev, cmd, &wrqu, NULL);
3461*4882a593Smuzhiyun 		else
3462*4882a593Smuzhiyun 			wireless_send_event(dev, cmd, &wrqu, extra);
3463*4882a593Smuzhiyun 	}
3464*4882a593Smuzhiyun 
3465*4882a593Smuzhiyun #if WIRELESS_EXT > 14
3466*4882a593Smuzhiyun 	/* Look for WLC events that indicate a connection failure.
3467*4882a593Smuzhiyun 	 * If found, generate an IWEVCUSTOM event.
3468*4882a593Smuzhiyun 	 */
3469*4882a593Smuzhiyun 	memset(extra, 0, sizeof(extra));
3470*4882a593Smuzhiyun 	if (wl_iw_check_conn_fail(e, extra, sizeof(extra))) {
3471*4882a593Smuzhiyun 		cmd = IWEVCUSTOM;
3472*4882a593Smuzhiyun 		wrqu.data.length = strlen(extra);
3473*4882a593Smuzhiyun 		wireless_send_event(dev, cmd, &wrqu, extra);
3474*4882a593Smuzhiyun 	}
3475*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 14 */
3476*4882a593Smuzhiyun 
3477*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 13 */
3478*4882a593Smuzhiyun }
3479*4882a593Smuzhiyun 
3480*4882a593Smuzhiyun static int wl_iw_get_wireless_stats_cbfn(void *ctx, const uint8 *data,
3481*4882a593Smuzhiyun 	uint16 type, uint16 len)
3482*4882a593Smuzhiyun {
3483*4882a593Smuzhiyun 	struct iw_statistics *wstats = ctx;
3484*4882a593Smuzhiyun 	int res = BCME_OK;
3485*4882a593Smuzhiyun 
3486*4882a593Smuzhiyun 	switch (type) {
3487*4882a593Smuzhiyun 		case WL_CNT_XTLV_WLC: {
3488*4882a593Smuzhiyun 			const wl_cnt_wlc_t *cnt = (const wl_cnt_wlc_t *)data;
3489*4882a593Smuzhiyun 			if (len > sizeof(wl_cnt_wlc_t)) {
3490*4882a593Smuzhiyun 				printf("counter structure length invalid! %d > %d\n",
3491*4882a593Smuzhiyun 					len, (int)sizeof(wl_cnt_wlc_t));
3492*4882a593Smuzhiyun 			}
3493*4882a593Smuzhiyun 			wstats->discard.nwid = 0;
3494*4882a593Smuzhiyun 			wstats->discard.code = dtoh32(cnt->rxundec);
3495*4882a593Smuzhiyun 			wstats->discard.fragment = dtoh32(cnt->rxfragerr);
3496*4882a593Smuzhiyun 			wstats->discard.retries = dtoh32(cnt->txfail);
3497*4882a593Smuzhiyun 			wstats->discard.misc = dtoh32(cnt->rxrunt) + dtoh32(cnt->rxgiant);
3498*4882a593Smuzhiyun 			wstats->miss.beacon = 0;
3499*4882a593Smuzhiyun 			WL_TRACE(("wl_iw_get_wireless_stats counters txframe=%d txbyte=%d\n",
3500*4882a593Smuzhiyun 				dtoh32(cnt->txframe), dtoh32(cnt->txbyte)));
3501*4882a593Smuzhiyun 			WL_TRACE(("wl_iw_get_wireless_stats counters rxundec=%d\n",
3502*4882a593Smuzhiyun 				dtoh32(cnt->rxundec)));
3503*4882a593Smuzhiyun 			WL_TRACE(("wl_iw_get_wireless_stats counters txfail=%d\n",
3504*4882a593Smuzhiyun 				dtoh32(cnt->txfail)));
3505*4882a593Smuzhiyun 			WL_TRACE(("wl_iw_get_wireless_stats counters rxfragerr=%d\n",
3506*4882a593Smuzhiyun 				dtoh32(cnt->rxfragerr)));
3507*4882a593Smuzhiyun 			WL_TRACE(("wl_iw_get_wireless_stats counters rxrunt=%d\n",
3508*4882a593Smuzhiyun 				dtoh32(cnt->rxrunt)));
3509*4882a593Smuzhiyun 			WL_TRACE(("wl_iw_get_wireless_stats counters rxgiant=%d\n",
3510*4882a593Smuzhiyun 				dtoh32(cnt->rxgiant)));
3511*4882a593Smuzhiyun 			break;
3512*4882a593Smuzhiyun 		}
3513*4882a593Smuzhiyun 		case WL_CNT_XTLV_CNTV_LE10_UCODE:
3514*4882a593Smuzhiyun 		case WL_CNT_XTLV_LT40_UCODE_V1:
3515*4882a593Smuzhiyun 		case WL_CNT_XTLV_GE40_UCODE_V1:
3516*4882a593Smuzhiyun 		{
3517*4882a593Smuzhiyun 			/* Offsets of rxfrmtoolong and rxbadplcp are the same in
3518*4882a593Smuzhiyun 			 * wl_cnt_v_le10_mcst_t, wl_cnt_lt40mcst_v1_t, and wl_cnt_ge40mcst_v1_t.
3519*4882a593Smuzhiyun 			 * So we can just cast to wl_cnt_v_le10_mcst_t here.
3520*4882a593Smuzhiyun 			 */
3521*4882a593Smuzhiyun 			const wl_cnt_v_le10_mcst_t *cnt = (const wl_cnt_v_le10_mcst_t *)data;
3522*4882a593Smuzhiyun 			if (len != WL_CNT_MCST_STRUCT_SZ) {
3523*4882a593Smuzhiyun 				printf("counter structure length mismatch! %d != %d\n",
3524*4882a593Smuzhiyun 					len, WL_CNT_MCST_STRUCT_SZ);
3525*4882a593Smuzhiyun 			}
3526*4882a593Smuzhiyun 			WL_TRACE(("wl_iw_get_wireless_stats counters rxfrmtoolong=%d\n",
3527*4882a593Smuzhiyun 				dtoh32(cnt->rxfrmtoolong)));
3528*4882a593Smuzhiyun 			WL_TRACE(("wl_iw_get_wireless_stats counters rxbadplcp=%d\n",
3529*4882a593Smuzhiyun 				dtoh32(cnt->rxbadplcp)));
3530*4882a593Smuzhiyun 			BCM_REFERENCE(cnt);
3531*4882a593Smuzhiyun 			break;
3532*4882a593Smuzhiyun 		}
3533*4882a593Smuzhiyun 		default:
3534*4882a593Smuzhiyun 			WL_ERROR(("%s %d: Unsupported type %d\n", __FUNCTION__, __LINE__, type));
3535*4882a593Smuzhiyun 			break;
3536*4882a593Smuzhiyun 	}
3537*4882a593Smuzhiyun 	return res;
3538*4882a593Smuzhiyun }
3539*4882a593Smuzhiyun 
3540*4882a593Smuzhiyun int wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstats)
3541*4882a593Smuzhiyun {
3542*4882a593Smuzhiyun 	int res = 0;
3543*4882a593Smuzhiyun 	int phy_noise;
3544*4882a593Smuzhiyun 	int rssi;
3545*4882a593Smuzhiyun 	scb_val_t scb_val;
3546*4882a593Smuzhiyun #if WIRELESS_EXT > 11
3547*4882a593Smuzhiyun 	char *cntbuf = NULL;
3548*4882a593Smuzhiyun 	wl_cnt_info_t *cntinfo;
3549*4882a593Smuzhiyun 	uint16 ver;
3550*4882a593Smuzhiyun 	uint32 corerev = 0;
3551*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 11 */
3552*4882a593Smuzhiyun 
3553*4882a593Smuzhiyun 	phy_noise = 0;
3554*4882a593Smuzhiyun 	if ((res = dev_wlc_ioctl(dev, WLC_GET_PHY_NOISE, &phy_noise, sizeof(phy_noise))))
3555*4882a593Smuzhiyun 		goto done;
3556*4882a593Smuzhiyun 
3557*4882a593Smuzhiyun 	phy_noise = dtoh32(phy_noise);
3558*4882a593Smuzhiyun 	WL_TRACE(("wl_iw_get_wireless_stats phy noise=%d\n *****", phy_noise));
3559*4882a593Smuzhiyun 
3560*4882a593Smuzhiyun 	scb_val.val = 0;
3561*4882a593Smuzhiyun 	if ((res = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t))))
3562*4882a593Smuzhiyun 		goto done;
3563*4882a593Smuzhiyun 
3564*4882a593Smuzhiyun 	rssi = dtoh32(scb_val.val);
3565*4882a593Smuzhiyun 	WL_TRACE(("wl_iw_get_wireless_stats rssi=%d ****** \n", rssi));
3566*4882a593Smuzhiyun 	if (rssi <= WL_IW_RSSI_NO_SIGNAL)
3567*4882a593Smuzhiyun 		wstats->qual.qual = 0;
3568*4882a593Smuzhiyun 	else if (rssi <= WL_IW_RSSI_VERY_LOW)
3569*4882a593Smuzhiyun 		wstats->qual.qual = 1;
3570*4882a593Smuzhiyun 	else if (rssi <= WL_IW_RSSI_LOW)
3571*4882a593Smuzhiyun 		wstats->qual.qual = 2;
3572*4882a593Smuzhiyun 	else if (rssi <= WL_IW_RSSI_GOOD)
3573*4882a593Smuzhiyun 		wstats->qual.qual = 3;
3574*4882a593Smuzhiyun 	else if (rssi <= WL_IW_RSSI_VERY_GOOD)
3575*4882a593Smuzhiyun 		wstats->qual.qual = 4;
3576*4882a593Smuzhiyun 	else
3577*4882a593Smuzhiyun 		wstats->qual.qual = 5;
3578*4882a593Smuzhiyun 
3579*4882a593Smuzhiyun 	/* Wraps to 0 if RSSI is 0 */
3580*4882a593Smuzhiyun 	wstats->qual.level = 0x100 + rssi;
3581*4882a593Smuzhiyun 	wstats->qual.noise = 0x100 + phy_noise;
3582*4882a593Smuzhiyun #if WIRELESS_EXT > 18
3583*4882a593Smuzhiyun 	wstats->qual.updated |= (IW_QUAL_ALL_UPDATED | IW_QUAL_DBM);
3584*4882a593Smuzhiyun #else
3585*4882a593Smuzhiyun 	wstats->qual.updated |= 7;
3586*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 18 */
3587*4882a593Smuzhiyun 
3588*4882a593Smuzhiyun #if WIRELESS_EXT > 11
3589*4882a593Smuzhiyun 	WL_TRACE(("wl_iw_get_wireless_stats counters\n *****"));
3590*4882a593Smuzhiyun 
3591*4882a593Smuzhiyun 	cntbuf = kmalloc(MAX_WLIW_IOCTL_LEN, GFP_KERNEL);
3592*4882a593Smuzhiyun 	if (!cntbuf) {
3593*4882a593Smuzhiyun 		res = BCME_NOMEM;
3594*4882a593Smuzhiyun 		goto done;
3595*4882a593Smuzhiyun 	}
3596*4882a593Smuzhiyun 
3597*4882a593Smuzhiyun 	memset(cntbuf, 0, MAX_WLIW_IOCTL_LEN);
3598*4882a593Smuzhiyun 	res = dev_wlc_bufvar_get(dev, "counters", cntbuf, MAX_WLIW_IOCTL_LEN);
3599*4882a593Smuzhiyun 	if (res)
3600*4882a593Smuzhiyun 	{
3601*4882a593Smuzhiyun 		WL_ERROR(("wl_iw_get_wireless_stats counters failed error=%d ****** \n", res));
3602*4882a593Smuzhiyun 		goto done;
3603*4882a593Smuzhiyun 	}
3604*4882a593Smuzhiyun 
3605*4882a593Smuzhiyun 	cntinfo = (wl_cnt_info_t *)cntbuf;
3606*4882a593Smuzhiyun 	cntinfo->version = dtoh16(cntinfo->version);
3607*4882a593Smuzhiyun 	cntinfo->datalen = dtoh16(cntinfo->datalen);
3608*4882a593Smuzhiyun 	ver = cntinfo->version;
3609*4882a593Smuzhiyun 	CHK_CNTBUF_DATALEN(cntbuf, MAX_WLIW_IOCTL_LEN);
3610*4882a593Smuzhiyun 	if (ver > WL_CNT_T_VERSION) {
3611*4882a593Smuzhiyun 		WL_TRACE(("\tIncorrect version of counters struct: expected %d; got %d\n",
3612*4882a593Smuzhiyun 			WL_CNT_T_VERSION, ver));
3613*4882a593Smuzhiyun 		res = BCME_VERSION;
3614*4882a593Smuzhiyun 		goto done;
3615*4882a593Smuzhiyun 	}
3616*4882a593Smuzhiyun 
3617*4882a593Smuzhiyun 	if (ver == WL_CNT_VERSION_11) {
3618*4882a593Smuzhiyun 		wlc_rev_info_t revinfo;
3619*4882a593Smuzhiyun 		memset(&revinfo, 0, sizeof(revinfo));
3620*4882a593Smuzhiyun 		res = dev_wlc_ioctl(dev, WLC_GET_REVINFO, &revinfo, sizeof(revinfo));
3621*4882a593Smuzhiyun 		if (res) {
3622*4882a593Smuzhiyun 			WL_ERROR(("%s: WLC_GET_REVINFO failed %d\n", __FUNCTION__, res));
3623*4882a593Smuzhiyun 			goto done;
3624*4882a593Smuzhiyun 		}
3625*4882a593Smuzhiyun 		corerev = dtoh32(revinfo.corerev);
3626*4882a593Smuzhiyun 	}
3627*4882a593Smuzhiyun 
3628*4882a593Smuzhiyun 	res = wl_cntbuf_to_xtlv_format(NULL, cntinfo, MAX_WLIW_IOCTL_LEN, corerev);
3629*4882a593Smuzhiyun 	if (res) {
3630*4882a593Smuzhiyun 		WL_ERROR(("%s: wl_cntbuf_to_xtlv_format failed %d\n", __FUNCTION__, res));
3631*4882a593Smuzhiyun 		goto done;
3632*4882a593Smuzhiyun 	}
3633*4882a593Smuzhiyun 
3634*4882a593Smuzhiyun 	if ((res = bcm_unpack_xtlv_buf(wstats, cntinfo->data, cntinfo->datalen,
3635*4882a593Smuzhiyun 		BCM_XTLV_OPTION_ALIGN32, wl_iw_get_wireless_stats_cbfn))) {
3636*4882a593Smuzhiyun 		goto done;
3637*4882a593Smuzhiyun 	}
3638*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 11 */
3639*4882a593Smuzhiyun 
3640*4882a593Smuzhiyun done:
3641*4882a593Smuzhiyun #if WIRELESS_EXT > 11
3642*4882a593Smuzhiyun 	if (cntbuf) {
3643*4882a593Smuzhiyun 		kfree(cntbuf);
3644*4882a593Smuzhiyun 	}
3645*4882a593Smuzhiyun #endif /* WIRELESS_EXT > 11 */
3646*4882a593Smuzhiyun 	return res;
3647*4882a593Smuzhiyun }
3648*4882a593Smuzhiyun 
3649*4882a593Smuzhiyun static void
3650*4882a593Smuzhiyun wl_iw_timerfunc(ulong data)
3651*4882a593Smuzhiyun {
3652*4882a593Smuzhiyun 	iscan_info_t *iscan = (iscan_info_t *)data;
3653*4882a593Smuzhiyun 	iscan->timer_on = 0;
3654*4882a593Smuzhiyun 	if (iscan->iscan_state != ISCAN_STATE_IDLE) {
3655*4882a593Smuzhiyun 		WL_TRACE(("timer trigger\n"));
3656*4882a593Smuzhiyun 		up(&iscan->sysioc_sem);
3657*4882a593Smuzhiyun 	}
3658*4882a593Smuzhiyun }
3659*4882a593Smuzhiyun 
3660*4882a593Smuzhiyun static void
3661*4882a593Smuzhiyun wl_iw_set_event_mask(struct net_device *dev)
3662*4882a593Smuzhiyun {
3663*4882a593Smuzhiyun 	char eventmask[WL_EVENTING_MASK_LEN];
3664*4882a593Smuzhiyun 	char iovbuf[WL_EVENTING_MASK_LEN + 12];	/* Room for "event_msgs" + '\0' + bitvec */
3665*4882a593Smuzhiyun 
3666*4882a593Smuzhiyun 	dev_iw_iovar_getbuf(dev, "event_msgs", "", 0, iovbuf, sizeof(iovbuf));
3667*4882a593Smuzhiyun 	bcopy(iovbuf, eventmask, WL_EVENTING_MASK_LEN);
3668*4882a593Smuzhiyun 	setbit(eventmask, WLC_E_SCAN_COMPLETE);
3669*4882a593Smuzhiyun 	dev_iw_iovar_setbuf(dev, "event_msgs", eventmask, WL_EVENTING_MASK_LEN,
3670*4882a593Smuzhiyun 		iovbuf, sizeof(iovbuf));
3671*4882a593Smuzhiyun 
3672*4882a593Smuzhiyun }
3673*4882a593Smuzhiyun 
3674*4882a593Smuzhiyun static int
3675*4882a593Smuzhiyun wl_iw_iscan_prep(wl_scan_params_t *params, wlc_ssid_t *ssid)
3676*4882a593Smuzhiyun {
3677*4882a593Smuzhiyun 	int err = 0;
3678*4882a593Smuzhiyun 
3679*4882a593Smuzhiyun 	memcpy(&params->bssid, &ether_bcast, ETHER_ADDR_LEN);
3680*4882a593Smuzhiyun 	params->bss_type = DOT11_BSSTYPE_ANY;
3681*4882a593Smuzhiyun 	params->scan_type = 0;
3682*4882a593Smuzhiyun 	params->nprobes = -1;
3683*4882a593Smuzhiyun 	params->active_time = -1;
3684*4882a593Smuzhiyun 	params->passive_time = -1;
3685*4882a593Smuzhiyun 	params->home_time = -1;
3686*4882a593Smuzhiyun 	params->channel_num = 0;
3687*4882a593Smuzhiyun 
3688*4882a593Smuzhiyun 	params->nprobes = htod32(params->nprobes);
3689*4882a593Smuzhiyun 	params->active_time = htod32(params->active_time);
3690*4882a593Smuzhiyun 	params->passive_time = htod32(params->passive_time);
3691*4882a593Smuzhiyun 	params->home_time = htod32(params->home_time);
3692*4882a593Smuzhiyun 	if (ssid && ssid->SSID_len)
3693*4882a593Smuzhiyun 		memcpy(&params->ssid, ssid, sizeof(wlc_ssid_t));
3694*4882a593Smuzhiyun 
3695*4882a593Smuzhiyun 	return err;
3696*4882a593Smuzhiyun }
3697*4882a593Smuzhiyun 
3698*4882a593Smuzhiyun static int
3699*4882a593Smuzhiyun wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, uint16 action)
3700*4882a593Smuzhiyun {
3701*4882a593Smuzhiyun 	int params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_iscan_params_t, params));
3702*4882a593Smuzhiyun 	wl_iscan_params_t *params;
3703*4882a593Smuzhiyun 	int err = 0;
3704*4882a593Smuzhiyun 
3705*4882a593Smuzhiyun 	if (ssid && ssid->SSID_len) {
3706*4882a593Smuzhiyun 		params_size += sizeof(wlc_ssid_t);
3707*4882a593Smuzhiyun 	}
3708*4882a593Smuzhiyun 	params = (wl_iscan_params_t*)kmalloc(params_size, GFP_KERNEL);
3709*4882a593Smuzhiyun 	if (params == NULL) {
3710*4882a593Smuzhiyun 		return -ENOMEM;
3711*4882a593Smuzhiyun 	}
3712*4882a593Smuzhiyun 	memset(params, 0, params_size);
3713*4882a593Smuzhiyun 	ASSERT(params_size < WLC_IOCTL_SMLEN);
3714*4882a593Smuzhiyun 
3715*4882a593Smuzhiyun 	err = wl_iw_iscan_prep(&params->params, ssid);
3716*4882a593Smuzhiyun 
3717*4882a593Smuzhiyun 	if (!err) {
3718*4882a593Smuzhiyun 		params->version = htod32(ISCAN_REQ_VERSION);
3719*4882a593Smuzhiyun 		params->action = htod16(action);
3720*4882a593Smuzhiyun 		params->scan_duration = htod16(0);
3721*4882a593Smuzhiyun 
3722*4882a593Smuzhiyun 		/* params_size += OFFSETOF(wl_iscan_params_t, params); */
3723*4882a593Smuzhiyun 		(void) dev_iw_iovar_setbuf(iscan->dev, "iscan", params, params_size,
3724*4882a593Smuzhiyun 			iscan->ioctlbuf, WLC_IOCTL_SMLEN);
3725*4882a593Smuzhiyun 	}
3726*4882a593Smuzhiyun 
3727*4882a593Smuzhiyun 	kfree(params);
3728*4882a593Smuzhiyun 	return err;
3729*4882a593Smuzhiyun }
3730*4882a593Smuzhiyun 
3731*4882a593Smuzhiyun static uint32
3732*4882a593Smuzhiyun wl_iw_iscan_get(iscan_info_t *iscan)
3733*4882a593Smuzhiyun {
3734*4882a593Smuzhiyun 	iscan_buf_t * buf;
3735*4882a593Smuzhiyun 	iscan_buf_t * ptr;
3736*4882a593Smuzhiyun 	wl_iscan_results_t * list_buf;
3737*4882a593Smuzhiyun 	wl_iscan_results_t list;
3738*4882a593Smuzhiyun 	wl_scan_results_t *results;
3739*4882a593Smuzhiyun 	uint32 status;
3740*4882a593Smuzhiyun 
3741*4882a593Smuzhiyun 	/* buffers are allocated on demand */
3742*4882a593Smuzhiyun 	if (iscan->list_cur) {
3743*4882a593Smuzhiyun 		buf = iscan->list_cur;
3744*4882a593Smuzhiyun 		iscan->list_cur = buf->next;
3745*4882a593Smuzhiyun 	}
3746*4882a593Smuzhiyun 	else {
3747*4882a593Smuzhiyun 		buf = kmalloc(sizeof(iscan_buf_t), GFP_KERNEL);
3748*4882a593Smuzhiyun 		if (!buf)
3749*4882a593Smuzhiyun 			return WL_SCAN_RESULTS_ABORTED;
3750*4882a593Smuzhiyun 		buf->next = NULL;
3751*4882a593Smuzhiyun 		if (!iscan->list_hdr)
3752*4882a593Smuzhiyun 			iscan->list_hdr = buf;
3753*4882a593Smuzhiyun 		else {
3754*4882a593Smuzhiyun 			ptr = iscan->list_hdr;
3755*4882a593Smuzhiyun 			while (ptr->next) {
3756*4882a593Smuzhiyun 				ptr = ptr->next;
3757*4882a593Smuzhiyun 			}
3758*4882a593Smuzhiyun 			ptr->next = buf;
3759*4882a593Smuzhiyun 		}
3760*4882a593Smuzhiyun 	}
3761*4882a593Smuzhiyun 	memset(buf->iscan_buf, 0, WLC_IW_ISCAN_MAXLEN);
3762*4882a593Smuzhiyun 	list_buf = (wl_iscan_results_t*)buf->iscan_buf;
3763*4882a593Smuzhiyun 	results = &list_buf->results;
3764*4882a593Smuzhiyun 	results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE;
3765*4882a593Smuzhiyun 	results->version = 0;
3766*4882a593Smuzhiyun 	results->count = 0;
3767*4882a593Smuzhiyun 
3768*4882a593Smuzhiyun 	memset(&list, 0, sizeof(list));
3769*4882a593Smuzhiyun 	list.results.buflen = htod32(WLC_IW_ISCAN_MAXLEN);
3770*4882a593Smuzhiyun 	(void) dev_iw_iovar_getbuf(
3771*4882a593Smuzhiyun 		iscan->dev,
3772*4882a593Smuzhiyun 		"iscanresults",
3773*4882a593Smuzhiyun 		&list,
3774*4882a593Smuzhiyun 		WL_ISCAN_RESULTS_FIXED_SIZE,
3775*4882a593Smuzhiyun 		buf->iscan_buf,
3776*4882a593Smuzhiyun 		WLC_IW_ISCAN_MAXLEN);
3777*4882a593Smuzhiyun 	results->buflen = dtoh32(results->buflen);
3778*4882a593Smuzhiyun 	results->version = dtoh32(results->version);
3779*4882a593Smuzhiyun 	results->count = dtoh32(results->count);
3780*4882a593Smuzhiyun 	WL_TRACE(("results->count = %d\n", results->count));
3781*4882a593Smuzhiyun 
3782*4882a593Smuzhiyun 	WL_TRACE(("results->buflen = %d\n", results->buflen));
3783*4882a593Smuzhiyun 	status = dtoh32(list_buf->status);
3784*4882a593Smuzhiyun 	return status;
3785*4882a593Smuzhiyun }
3786*4882a593Smuzhiyun 
3787*4882a593Smuzhiyun static void wl_iw_send_scan_complete(iscan_info_t *iscan)
3788*4882a593Smuzhiyun {
3789*4882a593Smuzhiyun 	union iwreq_data wrqu;
3790*4882a593Smuzhiyun 
3791*4882a593Smuzhiyun 	memset(&wrqu, 0, sizeof(wrqu));
3792*4882a593Smuzhiyun 
3793*4882a593Smuzhiyun 	/* wext expects to get no data for SIOCGIWSCAN Event  */
3794*4882a593Smuzhiyun 	wireless_send_event(iscan->dev, SIOCGIWSCAN, &wrqu, NULL);
3795*4882a593Smuzhiyun }
3796*4882a593Smuzhiyun 
3797*4882a593Smuzhiyun static int
3798*4882a593Smuzhiyun _iscan_sysioc_thread(void *data)
3799*4882a593Smuzhiyun {
3800*4882a593Smuzhiyun 	uint32 status;
3801*4882a593Smuzhiyun 	iscan_info_t *iscan = (iscan_info_t *)data;
3802*4882a593Smuzhiyun 
3803*4882a593Smuzhiyun 	DAEMONIZE("iscan_sysioc");
3804*4882a593Smuzhiyun 
3805*4882a593Smuzhiyun 	status = WL_SCAN_RESULTS_PARTIAL;
3806*4882a593Smuzhiyun 	while (down_interruptible(&iscan->sysioc_sem) == 0) {
3807*4882a593Smuzhiyun 		if (iscan->timer_on) {
3808*4882a593Smuzhiyun 			del_timer(&iscan->timer);
3809*4882a593Smuzhiyun 			iscan->timer_on = 0;
3810*4882a593Smuzhiyun 		}
3811*4882a593Smuzhiyun 
3812*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
3813*4882a593Smuzhiyun 		rtnl_lock();
3814*4882a593Smuzhiyun #endif // endif
3815*4882a593Smuzhiyun 		status = wl_iw_iscan_get(iscan);
3816*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
3817*4882a593Smuzhiyun 		rtnl_unlock();
3818*4882a593Smuzhiyun #endif // endif
3819*4882a593Smuzhiyun 
3820*4882a593Smuzhiyun 		switch (status) {
3821*4882a593Smuzhiyun 			case WL_SCAN_RESULTS_PARTIAL:
3822*4882a593Smuzhiyun 				WL_TRACE(("iscanresults incomplete\n"));
3823*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
3824*4882a593Smuzhiyun 				rtnl_lock();
3825*4882a593Smuzhiyun #endif // endif
3826*4882a593Smuzhiyun 				/* make sure our buffer size is enough before going next round */
3827*4882a593Smuzhiyun 				wl_iw_iscan(iscan, NULL, WL_SCAN_ACTION_CONTINUE);
3828*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
3829*4882a593Smuzhiyun 				rtnl_unlock();
3830*4882a593Smuzhiyun #endif // endif
3831*4882a593Smuzhiyun 				/* Reschedule the timer */
3832*4882a593Smuzhiyun #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
3833*4882a593Smuzhiyun 				iscan->timer.expires = jiffies + msecs_to_jiffies(iscan->timer_ms);
3834*4882a593Smuzhiyun #else
3835*4882a593Smuzhiyun 				iscan->timer.timer.expires = jiffies +
3836*4882a593Smuzhiyun 					msecs_to_jiffies(iscan->timer_ms);
3837*4882a593Smuzhiyun #endif // endif
3838*4882a593Smuzhiyun 				add_timer(&iscan->timer);
3839*4882a593Smuzhiyun 				iscan->timer_on = 1;
3840*4882a593Smuzhiyun 				break;
3841*4882a593Smuzhiyun 			case WL_SCAN_RESULTS_SUCCESS:
3842*4882a593Smuzhiyun 				WL_TRACE(("iscanresults complete\n"));
3843*4882a593Smuzhiyun 				iscan->iscan_state = ISCAN_STATE_IDLE;
3844*4882a593Smuzhiyun 				wl_iw_send_scan_complete(iscan);
3845*4882a593Smuzhiyun 				break;
3846*4882a593Smuzhiyun 			case WL_SCAN_RESULTS_PENDING:
3847*4882a593Smuzhiyun 				WL_TRACE(("iscanresults pending\n"));
3848*4882a593Smuzhiyun 				/* Reschedule the timer */
3849*4882a593Smuzhiyun #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
3850*4882a593Smuzhiyun 				iscan->timer.expires = jiffies + msecs_to_jiffies(iscan->timer_ms);
3851*4882a593Smuzhiyun #else
3852*4882a593Smuzhiyun 				iscan->timer.timer.expires = jiffies +
3853*4882a593Smuzhiyun 					msecs_to_jiffies(iscan->timer_ms);
3854*4882a593Smuzhiyun #endif // endif
3855*4882a593Smuzhiyun 				add_timer(&iscan->timer);
3856*4882a593Smuzhiyun 				iscan->timer_on = 1;
3857*4882a593Smuzhiyun 				break;
3858*4882a593Smuzhiyun 			case WL_SCAN_RESULTS_ABORTED:
3859*4882a593Smuzhiyun 				WL_TRACE(("iscanresults aborted\n"));
3860*4882a593Smuzhiyun 				iscan->iscan_state = ISCAN_STATE_IDLE;
3861*4882a593Smuzhiyun 				wl_iw_send_scan_complete(iscan);
3862*4882a593Smuzhiyun 				break;
3863*4882a593Smuzhiyun 			default:
3864*4882a593Smuzhiyun 				WL_TRACE(("iscanresults returned unknown status %d\n", status));
3865*4882a593Smuzhiyun 				break;
3866*4882a593Smuzhiyun 		 }
3867*4882a593Smuzhiyun 	}
3868*4882a593Smuzhiyun 	complete_and_exit(&iscan->sysioc_exited, 0);
3869*4882a593Smuzhiyun }
3870*4882a593Smuzhiyun 
3871*4882a593Smuzhiyun int
3872*4882a593Smuzhiyun wl_iw_attach(struct net_device *dev, void * dhdp)
3873*4882a593Smuzhiyun {
3874*4882a593Smuzhiyun 	iscan_info_t *iscan = NULL;
3875*4882a593Smuzhiyun 
3876*4882a593Smuzhiyun 	if (!dev)
3877*4882a593Smuzhiyun 		return 0;
3878*4882a593Smuzhiyun 
3879*4882a593Smuzhiyun 	iscan = kmalloc(sizeof(iscan_info_t), GFP_KERNEL);
3880*4882a593Smuzhiyun 	if (!iscan)
3881*4882a593Smuzhiyun 		return -ENOMEM;
3882*4882a593Smuzhiyun 	memset(iscan, 0, sizeof(iscan_info_t));
3883*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
3884*4882a593Smuzhiyun 	iscan->kthread = NULL;
3885*4882a593Smuzhiyun #endif // endif
3886*4882a593Smuzhiyun 	iscan->sysioc_pid = -1;
3887*4882a593Smuzhiyun 	/* we only care about main interface so save a global here */
3888*4882a593Smuzhiyun 	g_iscan = iscan;
3889*4882a593Smuzhiyun 	iscan->dev = dev;
3890*4882a593Smuzhiyun 	iscan->iscan_state = ISCAN_STATE_IDLE;
3891*4882a593Smuzhiyun 
3892*4882a593Smuzhiyun 	/* Set up the timer */
3893*4882a593Smuzhiyun 	iscan->timer_ms    = 2000;
3894*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
3895*4882a593Smuzhiyun 	init_timer(&iscan->timer);
3896*4882a593Smuzhiyun 	iscan->timer.data = (ulong)iscan;
3897*4882a593Smuzhiyun 	iscan->timer.function = wl_iw_timerfunc;
3898*4882a593Smuzhiyun #else
3899*4882a593Smuzhiyun 	init_timer_compat(&iscan->timer, wl_iw_timerfunc, iscan);
3900*4882a593Smuzhiyun #endif // endif
3901*4882a593Smuzhiyun 	sema_init(&iscan->sysioc_sem, 0);
3902*4882a593Smuzhiyun 	init_completion(&iscan->sysioc_exited);
3903*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
3904*4882a593Smuzhiyun 	iscan->kthread = kthread_run(_iscan_sysioc_thread, iscan, "iscan_sysioc");
3905*4882a593Smuzhiyun 	iscan->sysioc_pid = iscan->kthread->pid;
3906*4882a593Smuzhiyun #else
3907*4882a593Smuzhiyun 	iscan->sysioc_pid = kernel_thread(_iscan_sysioc_thread, iscan, 0);
3908*4882a593Smuzhiyun #endif // endif
3909*4882a593Smuzhiyun 	if (iscan->sysioc_pid < 0)
3910*4882a593Smuzhiyun 		return -ENOMEM;
3911*4882a593Smuzhiyun 	return 0;
3912*4882a593Smuzhiyun }
3913*4882a593Smuzhiyun 
3914*4882a593Smuzhiyun void wl_iw_detach(void)
3915*4882a593Smuzhiyun {
3916*4882a593Smuzhiyun 	iscan_buf_t  *buf;
3917*4882a593Smuzhiyun 	iscan_info_t *iscan = g_iscan;
3918*4882a593Smuzhiyun 	if (!iscan)
3919*4882a593Smuzhiyun 		return;
3920*4882a593Smuzhiyun 	/* Delete iscan timer */
3921*4882a593Smuzhiyun 	if (iscan->timer_on) {
3922*4882a593Smuzhiyun 		del_timer_sync(&iscan->timer);
3923*4882a593Smuzhiyun 		iscan->timer_on = 0;
3924*4882a593Smuzhiyun 	}
3925*4882a593Smuzhiyun 	if (iscan->sysioc_pid >= 0) {
3926*4882a593Smuzhiyun 		KILL_PROC(iscan->sysioc_pid, SIGTERM);
3927*4882a593Smuzhiyun 		wait_for_completion(&iscan->sysioc_exited);
3928*4882a593Smuzhiyun 	}
3929*4882a593Smuzhiyun 
3930*4882a593Smuzhiyun 	while (iscan->list_hdr) {
3931*4882a593Smuzhiyun 		buf = iscan->list_hdr->next;
3932*4882a593Smuzhiyun 		kfree(iscan->list_hdr);
3933*4882a593Smuzhiyun 		iscan->list_hdr = buf;
3934*4882a593Smuzhiyun 	}
3935*4882a593Smuzhiyun 	kfree(iscan);
3936*4882a593Smuzhiyun 	g_iscan = NULL;
3937*4882a593Smuzhiyun }
3938*4882a593Smuzhiyun 
3939*4882a593Smuzhiyun #endif /* USE_IW */
3940