1
2 #include <linux/module.h>
3 #include <linux/netdevice.h>
4 #include <net/netlink.h>
5 #include <typedefs.h>
6 #include <linuxver.h>
7 #include <osl.h>
8
9 #include <bcmutils.h>
10 #include <bcmendian.h>
11 #include <ethernet.h>
12
13 #include <wl_android.h>
14 #include <linux/if_arp.h>
15 #include <asm/uaccess.h>
16 #include <linux/wireless.h>
17 #if defined(WL_WIRELESS_EXT)
18 #include <wl_iw.h>
19 #endif /* WL_WIRELESS_EXT */
20 #include <wldev_common.h>
21 #include <wlioctl.h>
22 #include <bcmutils.h>
23 #include <linux_osl.h>
24 #include <dhd_dbg.h>
25 #include <dngl_stats.h>
26 #include <dhd.h>
27 #include <dhd_config.h>
28 #ifdef WL_CFG80211
29 #include <wl_cfg80211.h>
30 #endif /* WL_CFG80211 */
31 #ifdef WL_ESCAN
32 #include <wl_escan.h>
33 #endif /* WL_ESCAN */
34
35 uint android_msg_level = ANDROID_ERROR_LEVEL | ANDROID_MSG_LEVEL;
36
37 #define AEXT_ERROR(name, arg1, args...) \
38 do { \
39 if (android_msg_level & ANDROID_ERROR_LEVEL) { \
40 printf("[%s] AEXT-ERROR) %s : " arg1, name, __func__, ## args); \
41 } \
42 } while (0)
43 #define AEXT_TRACE(name, arg1, args...) \
44 do { \
45 if (android_msg_level & ANDROID_TRACE_LEVEL) { \
46 printf("[%s] AEXT-TRACE) %s : " arg1, name, __func__, ## args); \
47 } \
48 } while (0)
49 #define AEXT_INFO(name, arg1, args...) \
50 do { \
51 if (android_msg_level & ANDROID_INFO_LEVEL) { \
52 printf("[%s] AEXT-INFO) %s : " arg1, name, __func__, ## args); \
53 } \
54 } while (0)
55 #define AEXT_DBG(name, arg1, args...) \
56 do { \
57 if (android_msg_level & ANDROID_DBG_LEVEL) { \
58 printf("[%s] AEXT-DBG) %s : " arg1, name, __func__, ## args); \
59 } \
60 } while (0)
61
62 #ifndef WL_CFG80211
63 #define htod32(i) i
64 #define htod16(i) i
65 #define dtoh32(i) i
66 #define dtoh16(i) i
67 #define htodchanspec(i) i
68 #define dtohchanspec(i) i
69 #define IEEE80211_BAND_2GHZ 0
70 #define IEEE80211_BAND_5GHZ 1
71 #define WL_SCAN_JOIN_PROBE_INTERVAL_MS 20
72 #define WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
73 #define WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
74 #endif /* WL_CFG80211 */
75
76 #ifndef IW_CUSTOM_MAX
77 #define IW_CUSTOM_MAX 256 /* size of extra buffer used for translation of events */
78 #endif /* IW_CUSTOM_MAX */
79
80 #define CMD_CHANNEL "CHANNEL"
81 #define CMD_CHANNELS "CHANNELS"
82 #define CMD_ROAM_TRIGGER "ROAM_TRIGGER"
83 #define CMD_PM "PM"
84 #define CMD_MONITOR "MONITOR"
85 #ifdef BTC_WAR
86 #define CMD_BTC_WAR "BTC_WAR"
87 #endif /* BTC_WAR */
88 #define CMD_SET_SUSPEND_BCN_LI_DTIM "SET_SUSPEND_BCN_LI_DTIM"
89 #define CMD_WLMSGLEVEL "WLMSGLEVEL"
90 #ifdef WL_EXT_IAPSTA
91 #define CMD_IAPSTA_INIT "IAPSTA_INIT"
92 #define CMD_IAPSTA_CONFIG "IAPSTA_CONFIG"
93 #define CMD_IAPSTA_ENABLE "IAPSTA_ENABLE"
94 #define CMD_IAPSTA_DISABLE "IAPSTA_DISABLE"
95 #define CMD_ISAM_INIT "ISAM_INIT"
96 #define CMD_ISAM_CONFIG "ISAM_CONFIG"
97 #define CMD_ISAM_ENABLE "ISAM_ENABLE"
98 #define CMD_ISAM_DISABLE "ISAM_DISABLE"
99 #define CMD_ISAM_STATUS "ISAM_STATUS"
100 #define CMD_ISAM_PEER_PATH "ISAM_PEER_PATH"
101 #define CMD_ISAM_PARAM "ISAM_PARAM"
102 #endif /* WL_EXT_IAPSTA */
103 #define CMD_AUTOCHANNEL "AUTOCHANNEL"
104 #define CMD_WL "WL"
105 #define CMD_CONF "CONF"
106
107 #if defined(PKT_STATICS) && defined(BCMSDIO)
108 #define CMD_DUMP_PKT_STATICS "DUMP_PKT_STATICS"
109 #define CMD_CLEAR_PKT_STATICS "CLEAR_PKT_STATICS"
110 extern void dhd_bus_dump_txpktstatics(dhd_pub_t *dhdp);
111 extern void dhd_bus_clear_txpktstatics(dhd_pub_t *dhdp);
112 #endif /* PKT_STATICS && BCMSDIO */
113
114 #ifdef IDHCP
115 typedef struct dhcpc_parameter {
116 uint32 ip_addr;
117 uint32 ip_serv;
118 uint32 lease_time;
119 } dhcpc_para_t;
120 #endif /* IDHCP */
121
122 #ifdef WL_EXT_WOWL
123 #define WL_WOWL_TCPFIN (1 << 26)
124 typedef struct wl_wowl_pattern2 {
125 char cmd[4];
126 wl_wowl_pattern_t wowl_pattern;
127 } wl_wowl_pattern2_t;
128 #endif /* WL_EXT_WOWL */
129
130 #ifdef WL_EXT_TCPKA
131 typedef struct tcpka_conn {
132 uint32 sess_id;
133 struct ether_addr dst_mac; /* Destinition Mac */
134 struct ipv4_addr src_ip; /* Sorce IP */
135 struct ipv4_addr dst_ip; /* Destinition IP */
136 uint16 ipid; /* Ip Identification */
137 uint16 srcport; /* Source Port Address */
138 uint16 dstport; /* Destination Port Address */
139 uint32 seq; /* TCP Sequence Number */
140 uint32 ack; /* TCP Ack Number */
141 uint16 tcpwin; /* TCP window */
142 uint32 tsval; /* Timestamp Value */
143 uint32 tsecr; /* Timestamp Echo Reply */
144 uint32 len; /* last packet payload len */
145 uint32 ka_payload_len; /* keep alive payload length */
146 uint8 ka_payload[1]; /* keep alive payload */
147 } tcpka_conn_t;
148
149 typedef struct tcpka_conn_sess {
150 uint32 sess_id; /* session id */
151 uint32 flag; /* enable/disable flag */
152 wl_mtcpkeep_alive_timers_pkt_t tcpka_timers;
153 } tcpka_conn_sess_t;
154
155 typedef struct tcpka_conn_info {
156 uint32 ipid;
157 uint32 seq;
158 uint32 ack;
159 } tcpka_conn_sess_info_t;
160 #endif /* WL_EXT_TCPKA */
161
162 typedef struct auth_name_map_t {
163 uint auth;
164 uint wpa_auth;
165 char *auth_name;
166 } auth_name_map_t;
167
168 const auth_name_map_t auth_name_map[] = {
169 {WL_AUTH_OPEN_SYSTEM, WPA_AUTH_DISABLED, "open"},
170 {WL_AUTH_SHARED_KEY, WPA_AUTH_DISABLED, "shared"},
171 {WL_AUTH_OPEN_SYSTEM, WPA_AUTH_PSK, "wpa/psk"},
172 {WL_AUTH_OPEN_SYSTEM, WPA2_AUTH_PSK, "wpa2/psk"},
173 {WL_AUTH_OPEN_SYSTEM, WPA2_AUTH_PSK_SHA256|WPA2_AUTH_PSK, "wpa2/psk/sha256"},
174 {WL_AUTH_OPEN_SYSTEM, WPA2_AUTH_FT|WPA2_AUTH_PSK, "wpa2/psk/ft"},
175 {WL_AUTH_OPEN_SYSTEM, WPA2_AUTH_UNSPECIFIED, "wpa2/eap"},
176 {WL_AUTH_OPEN_SYSTEM, WPA2_AUTH_FT|WPA2_AUTH_UNSPECIFIED, "wpa2/eap/ft"},
177 {WL_AUTH_OPEN_SYSTEM, WPA3_AUTH_SAE_PSK, "wpa3/psk"},
178 {WL_AUTH_SAE_KEY, WPA3_AUTH_SAE_PSK, "wpa3sae/psk"},
179 {WL_AUTH_OPEN_SYSTEM, WPA3_AUTH_SAE_PSK|WPA2_AUTH_PSK, "wpa3/psk"},
180 {WL_AUTH_SAE_KEY, WPA3_AUTH_SAE_PSK|WPA2_AUTH_PSK, "wpa3sae/psk"},
181 {WL_AUTH_OPEN_SYSTEM, 0x20, "wpa3/psk"},
182 {WL_AUTH_SAE_KEY, 0x20, "wpa3sae/psk"},
183 {WL_AUTH_OPEN_SYSTEM, WPA3_AUTH_SAE_PSK|WPA2_AUTH_PSK_SHA256|WPA2_AUTH_PSK, "wpa3/psk/sha256"},
184 {WL_AUTH_SAE_KEY, WPA3_AUTH_SAE_PSK|WPA2_AUTH_PSK_SHA256|WPA2_AUTH_PSK, "wpa3sae/psk/sha256"},
185 {WL_AUTH_OPEN_SYSTEM, 0x20|WPA2_AUTH_PSK_SHA256|WPA2_AUTH_PSK, "wpa3/psk/sha256"},
186 {WL_AUTH_SAE_KEY, 0x20|WPA2_AUTH_PSK_SHA256|WPA2_AUTH_PSK, "wpa3sae/psk/sha256"},
187 {WL_AUTH_OPEN_SYSTEM, WPA3_AUTH_OWE, "owe"},
188 };
189
190 typedef struct wsec_name_map_t {
191 uint wsec;
192 char *wsec_name;
193 } wsec_name_map_t;
194
195 const wsec_name_map_t wsec_name_map[] = {
196 {WSEC_NONE, "none"},
197 {WEP_ENABLED, "wep"},
198 {TKIP_ENABLED, "tkip"},
199 {AES_ENABLED, "aes"},
200 {TKIP_ENABLED|AES_ENABLED, "tkip/aes"},
201 };
202
203 static int wl_ext_wl_iovar(struct net_device *dev, char *command, int total_len);
204
205 int
wl_ext_ioctl(struct net_device * dev,u32 cmd,void * arg,u32 len,u32 set)206 wl_ext_ioctl(struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set)
207 {
208 int ret;
209
210 ret = wldev_ioctl(dev, cmd, arg, len, set);
211 if (ret)
212 AEXT_ERROR(dev->name, "cmd=%d, ret=%d\n", cmd, ret);
213 return ret;
214 }
215
216 int
wl_ext_iovar_getint(struct net_device * dev,s8 * iovar,s32 * val)217 wl_ext_iovar_getint(struct net_device *dev, s8 *iovar, s32 *val)
218 {
219 int ret;
220
221 ret = wldev_iovar_getint(dev, iovar, val);
222 if (ret)
223 AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar, ret);
224
225 return ret;
226 }
227
228 int
wl_ext_iovar_setint(struct net_device * dev,s8 * iovar,s32 val)229 wl_ext_iovar_setint(struct net_device *dev, s8 *iovar, s32 val)
230 {
231 int ret;
232
233 ret = wldev_iovar_setint(dev, iovar, val);
234 if (ret)
235 AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar, ret);
236
237 return ret;
238 }
239
240 int
wl_ext_iovar_getbuf(struct net_device * dev,s8 * iovar_name,void * param,s32 paramlen,void * buf,s32 buflen,struct mutex * buf_sync)241 wl_ext_iovar_getbuf(struct net_device *dev, s8 *iovar_name,
242 void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
243 {
244 int ret;
245
246 ret = wldev_iovar_getbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync);
247 if (ret != 0)
248 AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar_name, ret);
249
250 return ret;
251 }
252
253 int
wl_ext_iovar_setbuf(struct net_device * dev,s8 * iovar_name,void * param,s32 paramlen,void * buf,s32 buflen,struct mutex * buf_sync)254 wl_ext_iovar_setbuf(struct net_device *dev, s8 *iovar_name,
255 void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
256 {
257 int ret;
258
259 ret = wldev_iovar_setbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync);
260 if (ret != 0)
261 AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar_name, ret);
262
263 return ret;
264 }
265
266 int
wl_ext_iovar_setbuf_bsscfg(struct net_device * dev,s8 * iovar_name,void * param,s32 paramlen,void * buf,s32 buflen,s32 bsscfg_idx,struct mutex * buf_sync)267 wl_ext_iovar_setbuf_bsscfg(struct net_device *dev, s8 *iovar_name,
268 void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx,
269 struct mutex* buf_sync)
270 {
271 int ret;
272
273 ret = wldev_iovar_setbuf_bsscfg(dev, iovar_name, param, paramlen,
274 buf, buflen, bsscfg_idx, buf_sync);
275 if (ret < 0)
276 AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar_name, ret);
277
278 return ret;
279 }
280
281 static chanspec_t
wl_ext_chspec_to_legacy(chanspec_t chspec)282 wl_ext_chspec_to_legacy(chanspec_t chspec)
283 {
284 chanspec_t lchspec;
285
286 if (wf_chspec_malformed(chspec)) {
287 AEXT_ERROR("wlan", "input chanspec (0x%04X) malformed\n", chspec);
288 return INVCHANSPEC;
289 }
290
291 /* get the channel number */
292 lchspec = CHSPEC_CHANNEL(chspec);
293
294 /* convert the band */
295 if (CHSPEC_IS2G(chspec)) {
296 lchspec |= WL_LCHANSPEC_BAND_2G;
297 } else {
298 lchspec |= WL_LCHANSPEC_BAND_5G;
299 }
300
301 /* convert the bw and sideband */
302 if (CHSPEC_IS20(chspec)) {
303 lchspec |= WL_LCHANSPEC_BW_20;
304 lchspec |= WL_LCHANSPEC_CTL_SB_NONE;
305 } else if (CHSPEC_IS40(chspec)) {
306 lchspec |= WL_LCHANSPEC_BW_40;
307 if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_L) {
308 lchspec |= WL_LCHANSPEC_CTL_SB_LOWER;
309 } else {
310 lchspec |= WL_LCHANSPEC_CTL_SB_UPPER;
311 }
312 } else {
313 /* cannot express the bandwidth */
314 char chanbuf[CHANSPEC_STR_LEN];
315 AEXT_ERROR("wlan", "unable to convert chanspec %s (0x%04X) "
316 "to pre-11ac format\n",
317 wf_chspec_ntoa(chspec, chanbuf), chspec);
318 return INVCHANSPEC;
319 }
320
321 return lchspec;
322 }
323
324 chanspec_t
wl_ext_chspec_host_to_driver(struct dhd_pub * dhd,chanspec_t chanspec)325 wl_ext_chspec_host_to_driver(struct dhd_pub *dhd, chanspec_t chanspec)
326 {
327 if (dhd->conf->ioctl_ver == 1) {
328 chanspec = wl_ext_chspec_to_legacy(chanspec);
329 if (chanspec == INVCHANSPEC) {
330 return chanspec;
331 }
332 }
333 chanspec = htodchanspec(chanspec);
334
335 return chanspec;
336 }
337
338 static void
wl_ext_ch_to_chanspec(struct dhd_pub * dhd,int ch,struct wl_join_params * join_params,size_t * join_params_size)339 wl_ext_ch_to_chanspec(struct dhd_pub *dhd, int ch,
340 struct wl_join_params *join_params, size_t *join_params_size)
341 {
342 chanspec_t chanspec = 0;
343
344 if (ch != 0) {
345 join_params->params.chanspec_num = 1;
346 join_params->params.chanspec_list[0] = ch;
347
348 if (join_params->params.chanspec_list[0] <= CH_MAX_2G_CHANNEL)
349 chanspec |= WL_CHANSPEC_BAND_2G;
350 else
351 chanspec |= WL_CHANSPEC_BAND_5G;
352
353 chanspec |= WL_CHANSPEC_BW_20;
354 chanspec |= WL_CHANSPEC_CTL_SB_NONE;
355
356 *join_params_size += WL_ASSOC_PARAMS_FIXED_SIZE +
357 join_params->params.chanspec_num * sizeof(chanspec_t);
358
359 join_params->params.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK;
360 join_params->params.chanspec_list[0] |= chanspec;
361 join_params->params.chanspec_list[0] =
362 wl_ext_chspec_host_to_driver(dhd,
363 join_params->params.chanspec_list[0]);
364
365 join_params->params.chanspec_num =
366 htod32(join_params->params.chanspec_num);
367 }
368 }
369
370 #if defined(WL_EXT_IAPSTA) || defined(WL_CFG80211) || defined(WL_ESCAN)
371 static chanspec_t
wl_ext_chspec_from_legacy(chanspec_t legacy_chspec)372 wl_ext_chspec_from_legacy(chanspec_t legacy_chspec)
373 {
374 chanspec_t chspec;
375
376 /* get the channel number */
377 chspec = LCHSPEC_CHANNEL(legacy_chspec);
378
379 /* convert the band */
380 if (LCHSPEC_IS2G(legacy_chspec)) {
381 chspec |= WL_CHANSPEC_BAND_2G;
382 } else {
383 chspec |= WL_CHANSPEC_BAND_5G;
384 }
385
386 /* convert the bw and sideband */
387 if (LCHSPEC_IS20(legacy_chspec)) {
388 chspec |= WL_CHANSPEC_BW_20;
389 } else {
390 chspec |= WL_CHANSPEC_BW_40;
391 if (LCHSPEC_CTL_SB(legacy_chspec) == WL_LCHANSPEC_CTL_SB_LOWER) {
392 chspec |= WL_CHANSPEC_CTL_SB_L;
393 } else {
394 chspec |= WL_CHANSPEC_CTL_SB_U;
395 }
396 }
397
398 if (wf_chspec_malformed(chspec)) {
399 AEXT_ERROR("wlan", "output chanspec (0x%04X) malformed\n", chspec);
400 return INVCHANSPEC;
401 }
402
403 return chspec;
404 }
405
406 chanspec_t
wl_ext_chspec_driver_to_host(struct dhd_pub * dhd,chanspec_t chanspec)407 wl_ext_chspec_driver_to_host(struct dhd_pub *dhd, chanspec_t chanspec)
408 {
409 chanspec = dtohchanspec(chanspec);
410 if (dhd->conf->ioctl_ver == 1) {
411 chanspec = wl_ext_chspec_from_legacy(chanspec);
412 }
413
414 return chanspec;
415 }
416 #endif /* WL_EXT_IAPSTA || WL_CFG80211 || WL_ESCAN */
417
418 chanspec_band_t
wl_ext_wlcband_to_chanspec_band(int band)419 wl_ext_wlcband_to_chanspec_band(int band)
420 {
421 chanspec_band_t chanspec_band = INVCHANSPEC;
422
423 switch (band) {
424 #ifdef WL_6G_BAND
425 case WLC_BAND_6G:
426 chanspec_band = WL_CHANSPEC_BAND_6G;
427 break;
428 #endif /* WL_6G_BAND */
429 case WLC_BAND_5G:
430 chanspec_band = WL_CHANSPEC_BAND_5G;
431 break;
432 case WLC_BAND_2G:
433 chanspec_band = WL_CHANSPEC_BAND_2G;
434 break;
435 default:
436 AEXT_ERROR("wlan", "Invalid Frequency Band\n");
437 break;
438 }
439 return chanspec_band;
440 }
441
442 bool
wl_ext_check_scan(struct net_device * dev,dhd_pub_t * dhdp)443 wl_ext_check_scan(struct net_device *dev, dhd_pub_t *dhdp)
444 {
445 #ifdef WL_CFG80211
446 struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
447 #endif /* WL_CFG80211 */
448 #ifdef WL_ESCAN
449 struct wl_escan_info *escan = dhdp->escan;
450 #endif /* WL_ESCAN */
451
452 #ifdef WL_CFG80211
453 if (wl_get_drv_status_all(cfg, SCANNING)) {
454 AEXT_ERROR(dev->name, "cfg80211 scanning...\n");
455 return TRUE;
456 }
457 #endif /* WL_CFG80211 */
458
459 #ifdef WL_ESCAN
460 if (escan->escan_state == ESCAN_STATE_SCANING) {
461 AEXT_ERROR(dev->name, "escan scanning...\n");
462 return TRUE;
463 }
464 #endif /* WL_ESCAN */
465
466 return FALSE;
467 }
468
469 #if defined(WL_CFG80211) || defined(WL_ESCAN)
470 void
wl_ext_user_sync(struct dhd_pub * dhd,int ifidx,bool lock)471 wl_ext_user_sync(struct dhd_pub *dhd, int ifidx, bool lock)
472 {
473 struct net_device *dev = dhd_idx2net(dhd, ifidx);
474 #ifdef WL_CFG80211
475 struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
476 #endif /* WL_CFG80211 */
477 #ifdef WL_ESCAN
478 struct wl_escan_info *escan = dhd->escan;
479 #endif /* WL_ESCAN */
480
481 AEXT_INFO(dev->name, "lock=%d\n", lock);
482
483 if (lock) {
484 #if defined(WL_CFG80211)
485 mutex_lock(&cfg->usr_sync);
486 #endif
487 #if defined(WL_ESCAN)
488 mutex_lock(&escan->usr_sync);
489 #endif
490 } else {
491 #if defined(WL_CFG80211)
492 mutex_unlock(&cfg->usr_sync);
493 #endif
494 #if defined(WL_ESCAN)
495 mutex_unlock(&escan->usr_sync);
496 #endif
497 }
498 }
499 #endif /* WL_CFG80211 && WL_ESCAN */
500
501 static bool
wl_ext_event_complete(struct dhd_pub * dhd,int ifidx)502 wl_ext_event_complete(struct dhd_pub *dhd, int ifidx)
503 {
504 struct net_device *dev = dhd_idx2net(dhd, ifidx);
505 #ifdef WL_CFG80211
506 struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
507 #endif /* WL_CFG80211 */
508 #ifdef WL_ESCAN
509 struct wl_escan_info *escan = dhd->escan;
510 #endif /* WL_ESCAN */
511 bool complete = TRUE;
512
513 #ifdef WL_CFG80211
514 if (wl_get_drv_status_all(cfg, SCANNING)) {
515 AEXT_INFO(dev->name, "SCANNING\n");
516 complete = FALSE;
517 }
518 if (wl_get_drv_status_all(cfg, CONNECTING)) {
519 AEXT_INFO(dev->name, "CFG80211 CONNECTING\n");
520 complete = FALSE;
521 }
522 if (wl_get_drv_status_all(cfg, DISCONNECTING)) {
523 AEXT_INFO(dev->name, "DISCONNECTING\n");
524 complete = FALSE;
525 }
526 #endif /* WL_CFG80211 */
527 #ifdef WL_ESCAN
528 if (escan->escan_state == ESCAN_STATE_SCANING) {
529 AEXT_INFO(dev->name, "ESCAN_STATE_SCANING\n");
530 complete = FALSE;
531 }
532 #endif /* WL_ESCAN */
533 #ifdef WL_EXT_IAPSTA
534 if (wl_ext_sta_connecting(dev)) {
535 AEXT_INFO(dev->name, "CONNECTING\n");
536 complete = FALSE;
537 }
538 #endif /* WL_EXT_IAPSTA */
539
540 return complete;
541 }
542
543 void
wl_ext_wait_event_complete(struct dhd_pub * dhd,int ifidx)544 wl_ext_wait_event_complete(struct dhd_pub *dhd, int ifidx)
545 {
546 struct net_device *net;
547 s32 timeout = -1;
548
549 timeout = wait_event_interruptible_timeout(dhd->conf->event_complete,
550 wl_ext_event_complete(dhd, ifidx), msecs_to_jiffies(10000));
551 if (timeout <= 0 || !wl_ext_event_complete(dhd, ifidx)) {
552 wl_ext_event_complete(dhd, ifidx);
553 net = dhd_idx2net(dhd, ifidx);
554 AEXT_ERROR(net->name, "timeout\n");
555 }
556 }
557
558 void
wl_ext_bss_iovar_war(struct net_device * ndev,s32 * val)559 wl_ext_bss_iovar_war(struct net_device *ndev, s32 *val)
560 {
561 dhd_pub_t *dhd = dhd_get_pub(ndev);
562 uint chip;
563 bool need_war = false;
564
565 chip = dhd_conf_get_chip(dhd);
566
567 if (chip == BCM43362_CHIP_ID || chip == BCM4330_CHIP_ID ||
568 chip == BCM4354_CHIP_ID || chip == BCM4356_CHIP_ID ||
569 chip == BCM4371_CHIP_ID ||
570 chip == BCM43430_CHIP_ID ||
571 chip == BCM4345_CHIP_ID || chip == BCM43454_CHIP_ID ||
572 chip == BCM4359_CHIP_ID ||
573 chip == BCM43143_CHIP_ID || chip == BCM43242_CHIP_ID ||
574 chip == BCM43569_CHIP_ID) {
575 need_war = true;
576 }
577
578 if (need_war) {
579 /* Few firmware branches have issues in bss iovar handling and
580 * that can't be changed since they are in production.
581 */
582 if (*val == WLC_AP_IOV_OP_MANUAL_AP_BSSCFG_CREATE) {
583 *val = WLC_AP_IOV_OP_MANUAL_STA_BSSCFG_CREATE;
584 } else if (*val == WLC_AP_IOV_OP_MANUAL_STA_BSSCFG_CREATE) {
585 *val = WLC_AP_IOV_OP_MANUAL_AP_BSSCFG_CREATE;
586 } else {
587 /* Ignore for other bss enums */
588 return;
589 }
590 AEXT_TRACE(ndev->name, "wl bss %d\n", *val);
591 }
592 }
593
594 int
wl_ext_set_chanspec(struct net_device * dev,struct wl_chan_info * chan_info,chanspec_t * ret_chspec)595 wl_ext_set_chanspec(struct net_device *dev, struct wl_chan_info *chan_info,
596 chanspec_t *ret_chspec)
597 {
598 struct dhd_pub *dhd = dhd_get_pub(dev);
599 s32 _chan = chan_info->chan;
600 chanspec_t chspec = 0;
601 chanspec_t fw_chspec = 0;
602 u32 bw = WL_CHANSPEC_BW_20;
603 s32 err = BCME_OK;
604 s32 bw_cap = 0;
605 s8 iovar_buf[WLC_IOCTL_SMLEN];
606 struct {
607 u32 band;
608 u32 bw_cap;
609 } param = {0, 0};
610 chanspec_band_t chanspec_band = 0;
611
612 if ((chan_info->band != WLC_BAND_2G) && (chan_info->band != WLC_BAND_5G) &&
613 (chan_info->band != WLC_BAND_6G)) {
614 AEXT_ERROR(dev->name, "bad band %d\n", chan_info->band);
615 return BCME_BADBAND;
616 }
617
618 param.band = chan_info->band;
619 err = wldev_iovar_getbuf(dev, "bw_cap", ¶m, sizeof(param),
620 iovar_buf, WLC_IOCTL_SMLEN, NULL);
621 if (err) {
622 if (err != BCME_UNSUPPORTED) {
623 AEXT_TRACE(dev->name, "bw_cap failed, %d\n", err);
624 return err;
625 } else {
626 err = wl_ext_iovar_getint(dev, "mimo_bw_cap", &bw_cap);
627 if (bw_cap != WLC_N_BW_20ALL)
628 bw = WL_CHANSPEC_BW_40;
629 }
630 } else {
631 if (WL_BW_CAP_80MHZ(iovar_buf[0]))
632 bw = WL_CHANSPEC_BW_80;
633 else if (WL_BW_CAP_40MHZ(iovar_buf[0]))
634 bw = WL_CHANSPEC_BW_40;
635 else
636 bw = WL_CHANSPEC_BW_20;
637 }
638
639 set_channel:
640 chanspec_band = wl_ext_wlcband_to_chanspec_band(chan_info->band);
641 chspec = wf_create_chspec_from_primary(chan_info->chan, bw, chanspec_band);
642 if (wf_chspec_valid(chspec)) {
643 fw_chspec = wl_ext_chspec_host_to_driver(dhd, chspec);
644 if (fw_chspec != INVCHANSPEC) {
645 if ((err = wl_ext_iovar_setint(dev, "chanspec", fw_chspec)) == BCME_BADCHAN) {
646 if (bw == WL_CHANSPEC_BW_80)
647 goto change_bw;
648 err = wl_ext_ioctl(dev, WLC_SET_CHANNEL, &_chan, sizeof(_chan), 1);
649 WL_MSG(dev->name, "channel %s-%d\n", CHSPEC2BANDSTR(chspec), _chan);
650 } else if (err) {
651 AEXT_ERROR(dev->name, "failed to set chanspec error %d\n", err);
652 } else
653 WL_MSG(dev->name, "channel %s-%d(0x%x %sMHz)\n",
654 CHSPEC2BANDSTR(chspec), chan_info->chan, chspec,
655 CHSPEC_IS20(chspec)?"20":
656 CHSPEC_IS40(chspec)?"40":
657 CHSPEC_IS80(chspec)?"80":"160");
658 } else {
659 AEXT_ERROR(dev->name, "failed to convert host chanspec to fw chanspec\n");
660 err = BCME_ERROR;
661 }
662 } else {
663 change_bw:
664 if (bw == WL_CHANSPEC_BW_80)
665 bw = WL_CHANSPEC_BW_40;
666 else if (bw == WL_CHANSPEC_BW_40)
667 bw = WL_CHANSPEC_BW_20;
668 else
669 bw = 0;
670 if (bw)
671 goto set_channel;
672 AEXT_ERROR(dev->name, "Invalid chanspec 0x%x\n", chspec);
673 err = BCME_ERROR;
674 }
675 *ret_chspec = fw_chspec;
676
677 return err;
678 }
679
680 static int
wl_ext_channel(struct net_device * dev,char * command,int total_len)681 wl_ext_channel(struct net_device *dev, char* command, int total_len)
682 {
683 struct wl_chan_info chan_info;
684 int ret;
685 char band[16]="";
686 int channel = 0;
687 channel_info_t ci;
688 int bytes_written = 0;
689 chanspec_t fw_chspec;
690
691 AEXT_TRACE(dev->name, "cmd %s", command);
692
693 sscanf(command, "%*s %d %s", &channel, band);
694 if (strnicmp(band, "band=auto", strlen("band=auto")) == 0) {
695 chan_info.band = WLC_BAND_AUTO;
696 }
697 #ifdef WL_6G_BAND
698 else if (strnicmp(band, "band=6g", strlen("band=6g")) == 0) {
699 chan_info.band = WLC_BAND_6G;
700 }
701 #endif /* WL_6G_BAND */
702 else if (strnicmp(band, "band=5g", strlen("band=5g")) == 0) {
703 chan_info.band = WLC_BAND_5G;
704 }
705 else if (strnicmp(band, "band=2g", strlen("band=2g")) == 0) {
706 chan_info.band = WLC_BAND_2G;
707 }
708 else if (channel <= CH_MAX_2G_CHANNEL)
709 chan_info.band = WLC_BAND_2G;
710 else
711 chan_info.band = WLC_BAND_5G;
712
713 if (channel > 0) {
714 chan_info.chan = channel;
715 ret = wl_ext_set_chanspec(dev, &chan_info, &fw_chspec);
716 } else {
717 if (!(ret = wl_ext_ioctl(dev, WLC_GET_CHANNEL, &ci,
718 sizeof(channel_info_t), FALSE))) {
719 AEXT_TRACE(dev->name, "hw_channel %d\n", ci.hw_channel);
720 AEXT_TRACE(dev->name, "target_channel %d\n", ci.target_channel);
721 AEXT_TRACE(dev->name, "scan_channel %d\n", ci.scan_channel);
722 bytes_written = snprintf(command, sizeof(channel_info_t)+2,
723 "channel %d", ci.hw_channel);
724 AEXT_TRACE(dev->name, "command result is %s\n", command);
725 ret = bytes_written;
726 }
727 }
728
729 return ret;
730 }
731
732 static int
wl_ext_channels(struct net_device * dev,char * command,int total_len)733 wl_ext_channels(struct net_device *dev, char* command, int total_len)
734 {
735 int ret, i;
736 int bytes_written = -1;
737 u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
738 wl_uint32_list_t *list;
739
740 AEXT_TRACE(dev->name, "cmd %s", command);
741
742 memset(valid_chan_list, 0, sizeof(valid_chan_list));
743 list = (wl_uint32_list_t *)(void *) valid_chan_list;
744 list->count = htod32(WL_NUMCHANNELS);
745 ret = wl_ext_ioctl(dev, WLC_GET_VALID_CHANNELS, valid_chan_list,
746 sizeof(valid_chan_list), 0);
747 if (ret<0) {
748 AEXT_ERROR(dev->name, "get channels failed with %d\n", ret);
749 } else {
750 bytes_written = snprintf(command, total_len, "channels");
751 for (i = 0; i < dtoh32(list->count); i++) {
752 bytes_written += snprintf(command+bytes_written, total_len, " %d",
753 dtoh32(list->element[i]));
754 }
755 AEXT_TRACE(dev->name, "command result is %s\n", command);
756 ret = bytes_written;
757 }
758
759 return ret;
760 }
761
762 static int
wl_ext_roam_trigger(struct net_device * dev,char * command,int total_len)763 wl_ext_roam_trigger(struct net_device *dev, char* command, int total_len)
764 {
765 int ret = 0;
766 int roam_trigger[2] = {0, 0};
767 int trigger[2]= {0, 0};
768 int bytes_written=-1;
769
770 sscanf(command, "%*s %10d", &roam_trigger[0]);
771
772 if (roam_trigger[0]) {
773 roam_trigger[1] = WLC_BAND_ALL;
774 ret = wl_ext_ioctl(dev, WLC_SET_ROAM_TRIGGER, roam_trigger,
775 sizeof(roam_trigger), 1);
776 } else {
777 roam_trigger[1] = WLC_BAND_2G;
778 ret = wl_ext_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger,
779 sizeof(roam_trigger), 0);
780 if (!ret)
781 trigger[0] = roam_trigger[0];
782
783 roam_trigger[1] = WLC_BAND_5G;
784 ret = wl_ext_ioctl(dev, WLC_GET_ROAM_TRIGGER, &roam_trigger,
785 sizeof(roam_trigger), 0);
786 if (!ret)
787 trigger[1] = roam_trigger[0];
788
789 AEXT_TRACE(dev->name, "roam_trigger %d %d\n", trigger[0], trigger[1]);
790 bytes_written = snprintf(command, total_len, "%d %d", trigger[0], trigger[1]);
791 ret = bytes_written;
792 }
793
794 return ret;
795 }
796
797 static int
wl_ext_pm(struct net_device * dev,char * command,int total_len)798 wl_ext_pm(struct net_device *dev, char *command, int total_len)
799 {
800 int pm=-1, ret = -1;
801 char *pm_local;
802 int bytes_written=-1;
803
804 AEXT_TRACE(dev->name, "cmd %s", command);
805
806 sscanf(command, "%*s %d", &pm);
807
808 if (pm >= 0) {
809 ret = wl_ext_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), 1);
810 } else {
811 ret = wl_ext_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm), 0);
812 if (!ret) {
813 AEXT_TRACE(dev->name, "PM = %d", pm);
814 if (pm == PM_OFF)
815 pm_local = "PM_OFF";
816 else if(pm == PM_MAX)
817 pm_local = "PM_MAX";
818 else if(pm == PM_FAST)
819 pm_local = "PM_FAST";
820 else {
821 pm = 0;
822 pm_local = "Invalid";
823 }
824 bytes_written = snprintf(command, total_len, "PM %s", pm_local);
825 AEXT_TRACE(dev->name, "command result is %s\n", command);
826 ret = bytes_written;
827 }
828 }
829
830 return ret;
831 }
832
833 static int
wl_ext_monitor(struct net_device * dev,char * command,int total_len)834 wl_ext_monitor(struct net_device *dev, char *command, int total_len)
835 {
836 int val = -1, ret = -1;
837 int bytes_written=-1;
838
839 sscanf(command, "%*s %d", &val);
840
841 if (val >=0) {
842 ret = wl_ext_ioctl(dev, WLC_SET_MONITOR, &val, sizeof(val), 1);
843 } else {
844 ret = wl_ext_ioctl(dev, WLC_GET_MONITOR, &val, sizeof(val), 0);
845 if (!ret) {
846 AEXT_TRACE(dev->name, "monitor = %d\n", val);
847 bytes_written = snprintf(command, total_len, "monitor %d", val);
848 AEXT_TRACE(dev->name, "command result is %s\n", command);
849 ret = bytes_written;
850 }
851 }
852
853 return ret;
854 }
855
856 #ifdef BTC_WAR
857 extern int btc_war;
858 static int
wl_ext_btc_war(struct net_device * dev,char * command,int total_len)859 wl_ext_btc_war(struct net_device *dev, char *command, int total_len)
860 {
861 int user_btc_war = 0;
862 bool enable = FALSE;
863
864 sscanf(command, "%*s %d", &user_btc_war);
865
866 AEXT_TRACE(dev->name, "btc_war=%d, user_btc_war=%d\n",
867 btc_war, user_btc_war);
868
869 if (btc_war >= 0) {
870 btc_war = user_btc_war;
871 if (btc_war > 0)
872 enable = TRUE;
873 wl_ext_btc_config(dev, enable);
874 }
875
876 return 0;
877 }
878 #endif /* BTC_WAR */
879
880 s32
wl_ext_connect(struct net_device * dev,struct wl_conn_info * conn_info)881 wl_ext_connect(struct net_device *dev, struct wl_conn_info *conn_info)
882 {
883 struct dhd_pub *dhd = dhd_get_pub(dev);
884 wl_extjoin_params_t *ext_join_params = NULL;
885 struct wl_join_params join_params;
886 size_t join_params_size;
887 s32 err = 0;
888 u32 chan_cnt = 0;
889 s8 *iovar_buf = NULL;
890 char sec[64];
891
892 if (dhd->conf->chip == BCM43362_CHIP_ID)
893 goto set_ssid;
894
895 if (conn_info->channel) {
896 chan_cnt = 1;
897 }
898
899 iovar_buf = kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL);
900 if (iovar_buf == NULL) {
901 err = -ENOMEM;
902 goto exit;
903 }
904
905 /*
906 * Join with specific BSSID and cached SSID
907 * If SSID is zero join based on BSSID only
908 */
909 join_params_size = WL_EXTJOIN_PARAMS_FIXED_SIZE +
910 chan_cnt * sizeof(chanspec_t);
911 ext_join_params = (wl_extjoin_params_t*)kzalloc(join_params_size, GFP_KERNEL);
912 if (ext_join_params == NULL) {
913 err = -ENOMEM;
914 goto exit;
915 }
916 ext_join_params->ssid.SSID_len = min((uint32)sizeof(ext_join_params->ssid.SSID),
917 conn_info->ssid.SSID_len);
918 memcpy(&ext_join_params->ssid.SSID, conn_info->ssid.SSID, ext_join_params->ssid.SSID_len);
919 ext_join_params->ssid.SSID_len = htod32(ext_join_params->ssid.SSID_len);
920 /* increate dwell time to receive probe response or detect Beacon
921 * from target AP at a noisy air only during connect command
922 */
923 ext_join_params->scan.active_time = chan_cnt ? WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS : -1;
924 ext_join_params->scan.passive_time = chan_cnt ? WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS : -1;
925 /* Set up join scan parameters */
926 ext_join_params->scan.scan_type = -1;
927 ext_join_params->scan.nprobes = chan_cnt ?
928 (ext_join_params->scan.active_time/WL_SCAN_JOIN_PROBE_INTERVAL_MS) : -1;
929 ext_join_params->scan.home_time = -1;
930
931 if (memcmp(ðer_null, &conn_info->bssid, ETHER_ADDR_LEN))
932 memcpy(&ext_join_params->assoc.bssid, &conn_info->bssid, ETH_ALEN);
933 else
934 memcpy(&ext_join_params->assoc.bssid, ðer_bcast, ETH_ALEN);
935 ext_join_params->assoc.chanspec_num = chan_cnt;
936 if (chan_cnt) {
937 u16 band, bw, ctl_sb;
938 chanspec_t chspec;
939 band = (conn_info->channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G
940 : WL_CHANSPEC_BAND_5G;
941 bw = WL_CHANSPEC_BW_20;
942 ctl_sb = WL_CHANSPEC_CTL_SB_NONE;
943 chspec = (conn_info->channel | band | bw | ctl_sb);
944 ext_join_params->assoc.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK;
945 ext_join_params->assoc.chanspec_list[0] |= chspec;
946 ext_join_params->assoc.chanspec_list[0] =
947 wl_ext_chspec_host_to_driver(dhd,
948 ext_join_params->assoc.chanspec_list[0]);
949 }
950 ext_join_params->assoc.chanspec_num = htod32(ext_join_params->assoc.chanspec_num);
951
952 wl_ext_get_sec(dev, 0, sec, sizeof(sec), TRUE);
953 WL_MSG(dev->name,
954 "Connecting with %pM channel (%d) ssid \"%s\", len (%d), sec=%s\n\n",
955 &ext_join_params->assoc.bssid, conn_info->channel,
956 ext_join_params->ssid.SSID, ext_join_params->ssid.SSID_len, sec);
957 err = wl_ext_iovar_setbuf_bsscfg(dev, "join", ext_join_params,
958 join_params_size, iovar_buf, WLC_IOCTL_MAXLEN, conn_info->bssidx, NULL);
959
960 if (err) {
961 if (err == BCME_UNSUPPORTED) {
962 AEXT_TRACE(dev->name, "join iovar is not supported\n");
963 goto set_ssid;
964 } else {
965 AEXT_ERROR(dev->name, "error (%d)\n", err);
966 goto exit;
967 }
968 } else
969 goto exit;
970
971 set_ssid:
972 memset(&join_params, 0, sizeof(join_params));
973 join_params_size = sizeof(join_params.ssid);
974
975 join_params.ssid.SSID_len = min((uint32)sizeof(join_params.ssid.SSID),
976 conn_info->ssid.SSID_len);
977 memcpy(&join_params.ssid.SSID, conn_info->ssid.SSID, join_params.ssid.SSID_len);
978 join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len);
979 if (memcmp(ðer_null, &conn_info->bssid, ETHER_ADDR_LEN))
980 memcpy(&join_params.params.bssid, &conn_info->bssid, ETH_ALEN);
981 else
982 memcpy(&join_params.params.bssid, ðer_bcast, ETH_ALEN);
983
984 wl_ext_ch_to_chanspec(dhd, conn_info->channel, &join_params, &join_params_size);
985 AEXT_TRACE(dev->name, "join_param_size %zu\n", join_params_size);
986
987 if (join_params.ssid.SSID_len < IEEE80211_MAX_SSID_LEN) {
988 AEXT_INFO(dev->name, "ssid \"%s\", len (%d)\n", join_params.ssid.SSID,
989 join_params.ssid.SSID_len);
990 }
991 wl_ext_get_sec(dev, 0, sec, sizeof(sec), TRUE);
992 WL_MSG(dev->name,
993 "Connecting with %pM channel (%d) ssid \"%s\", len (%d), sec=%s\n\n",
994 &join_params.params.bssid, conn_info->channel,
995 join_params.ssid.SSID, join_params.ssid.SSID_len, sec);
996 err = wl_ext_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size, 1);
997
998 exit:
999 #ifdef WL_EXT_IAPSTA
1000 if (!err)
1001 wl_ext_add_remove_pm_enable_work(dev, TRUE);
1002 #endif /* WL_EXT_IAPSTA */
1003 if (iovar_buf)
1004 kfree(iovar_buf);
1005 if (ext_join_params)
1006 kfree(ext_join_params);
1007 return err;
1008
1009 }
1010
1011 void
wl_ext_get_sec(struct net_device * dev,int ifmode,char * sec,int total_len,bool dump)1012 wl_ext_get_sec(struct net_device *dev, int ifmode, char *sec, int total_len, bool dump)
1013 {
1014 int auth=0, wpa_auth=0, wsec=0, mfp=0, i;
1015 int bytes_written=0;
1016 bool match = FALSE;
1017
1018 memset(sec, 0, total_len);
1019 wl_ext_iovar_getint(dev, "auth", &auth);
1020 wl_ext_iovar_getint(dev, "wpa_auth", &wpa_auth);
1021 wl_ext_iovar_getint(dev, "wsec", &wsec);
1022 wldev_iovar_getint(dev, "mfp", &mfp);
1023
1024 #ifdef WL_EXT_IAPSTA
1025 if (ifmode == IMESH_MODE) {
1026 if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA_AUTH_DISABLED) {
1027 bytes_written += snprintf(sec+bytes_written, total_len, "open");
1028 } else if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA2_AUTH_PSK) {
1029 bytes_written += snprintf(sec+bytes_written, total_len, "sae");
1030 } else {
1031 bytes_written += snprintf(sec+bytes_written, total_len, "%d/0x%x",
1032 auth, wpa_auth);
1033 }
1034 } else
1035 #endif /* WL_EXT_IAPSTA */
1036 {
1037 match = FALSE;
1038 for (i=0; i<sizeof(auth_name_map)/sizeof(auth_name_map[0]); i++) {
1039 const auth_name_map_t* row = &auth_name_map[i];
1040 if (row->auth == auth && row->wpa_auth == wpa_auth) {
1041 bytes_written += snprintf(sec+bytes_written, total_len, "%s",
1042 row->auth_name);
1043 match = TRUE;
1044 break;
1045 }
1046 }
1047 if (!match) {
1048 bytes_written += snprintf(sec+bytes_written, total_len, "%d/0x%x",
1049 auth, wpa_auth);
1050 }
1051 }
1052
1053 if (mfp == WL_MFP_NONE) {
1054 bytes_written += snprintf(sec+bytes_written, total_len, "/mfpn");
1055 } else if (mfp == WL_MFP_CAPABLE) {
1056 bytes_written += snprintf(sec+bytes_written, total_len, "/mfpc");
1057 } else if (mfp == WL_MFP_REQUIRED) {
1058 bytes_written += snprintf(sec+bytes_written, total_len, "/mfpr");
1059 } else {
1060 bytes_written += snprintf(sec+bytes_written, total_len, "/%d", mfp);
1061 }
1062
1063 #ifdef WL_EXT_IAPSTA
1064 if (ifmode == IMESH_MODE) {
1065 if (wsec == WSEC_NONE) {
1066 bytes_written += snprintf(sec+bytes_written, total_len, "/none");
1067 } else {
1068 bytes_written += snprintf(sec+bytes_written, total_len, "/aes");
1069 }
1070 } else
1071 #endif /* WL_EXT_IAPSTA */
1072 {
1073 match = FALSE;
1074 for (i=0; i<sizeof(wsec_name_map)/sizeof(wsec_name_map[0]); i++) {
1075 const wsec_name_map_t* row = &wsec_name_map[i];
1076 if (row->wsec == (wsec&0x7)) {
1077 bytes_written += snprintf(sec+bytes_written, total_len, "/%s",
1078 row->wsec_name);
1079 match = TRUE;
1080 break;
1081 }
1082 }
1083 if (!match) {
1084 bytes_written += snprintf(sec+bytes_written, total_len, "/0x%x", wsec);
1085 }
1086 }
1087 if (dump) {
1088 AEXT_INFO(dev->name, "auth/wpa_auth/mfp/wsec = %d/0x%x/%d/0x%x\n",
1089 auth, wpa_auth, mfp, wsec);
1090 }
1091 }
1092
1093 bool
wl_ext_dfs_chan(struct wl_chan_info * chan_info)1094 wl_ext_dfs_chan(struct wl_chan_info *chan_info)
1095 {
1096 if (chan_info->band == WLC_BAND_5G && chan_info->chan >= 52 && chan_info->chan <= 144)
1097 return TRUE;
1098 return FALSE;
1099 }
1100
1101 bool
wl_ext_passive_chan(struct net_device * dev,struct wl_chan_info * chan_info)1102 wl_ext_passive_chan(struct net_device *dev, struct wl_chan_info *chan_info)
1103 {
1104 struct dhd_pub *dhd = dhd_get_pub(dev);
1105 u32 chanspec;
1106 s32 ret = BCME_OK;
1107
1108 chanspec = wf_create_chspec_from_primary(chan_info->chan,
1109 WL_CHANSPEC_BW_20, wl_ext_wlcband_to_chanspec_band(chan_info->band));
1110
1111 chanspec = wl_ext_chspec_host_to_driver(dhd, chanspec);
1112
1113 ret = wldev_iovar_getint(dev, "per_chan_info", &chanspec);
1114 if (!ret) {
1115 if (chanspec & WL_CHAN_PASSIVE)
1116 return TRUE;
1117 } else {
1118 if (chan_info->band == WLC_BAND_5G && chan_info->chan >= 52 && chan_info->chan <= 144)
1119 return TRUE;
1120 }
1121
1122 return FALSE;
1123 }
1124
1125 uint16
wl_ext_get_default_chan(struct net_device * dev,uint16 * chan_2g,uint16 * chan_5g,bool nodfs)1126 wl_ext_get_default_chan(struct net_device *dev,
1127 uint16 *chan_2g, uint16 *chan_5g, bool nodfs)
1128 {
1129 struct dhd_pub *dhd = dhd_get_pub(dev);
1130 struct wl_chan_info chan_info;
1131 uint16 chan_tmp = 0, chan = 0;
1132 wl_uint32_list_t *list;
1133 u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
1134 s32 ret = BCME_OK;
1135 int i;
1136
1137 *chan_2g = 0;
1138 *chan_5g = 0;
1139 memset(valid_chan_list, 0, sizeof(valid_chan_list));
1140 list = (wl_uint32_list_t *)(void *) valid_chan_list;
1141 list->count = htod32(WL_NUMCHANNELS);
1142 ret = wl_ext_ioctl(dev, WLC_GET_VALID_CHANNELS, valid_chan_list,
1143 sizeof(valid_chan_list), 0);
1144 if (ret == 0) {
1145 for (i=0; i<dtoh32(list->count); i++) {
1146 chan_tmp = dtoh32(list->element[i]);
1147 if (!dhd_conf_match_channel(dhd, chan_tmp))
1148 continue;
1149 if (chan_tmp <= 13 && !*chan_2g) {
1150 *chan_2g = chan_tmp;
1151 } else if (chan_tmp >= 36 && chan_tmp <= 161 && !*chan_5g) {
1152 chan_info.band = WLC_BAND_5G;
1153 chan_info.chan = chan_tmp;
1154 if (wl_ext_dfs_chan(&chan_info) && nodfs)
1155 continue;
1156 else if (wl_ext_passive_chan(dev, &chan_info))
1157 continue;
1158 else
1159 *chan_5g = chan_tmp;
1160 }
1161 }
1162 }
1163
1164 return chan;
1165 }
1166
1167 int
wl_ext_set_scan_time(struct net_device * dev,int scan_time,uint32 scan_get,uint32 scan_set)1168 wl_ext_set_scan_time(struct net_device *dev, int scan_time,
1169 uint32 scan_get, uint32 scan_set)
1170 {
1171 int ret, cur_scan_time;
1172
1173 ret = wl_ext_ioctl(dev, scan_get, &cur_scan_time, sizeof(cur_scan_time), 0);
1174 if (ret)
1175 return 0;
1176
1177 if (scan_time != cur_scan_time)
1178 wl_ext_ioctl(dev, scan_set, &scan_time, sizeof(scan_time), 1);
1179
1180 return cur_scan_time;
1181 }
1182
1183 static int
wl_ext_wlmsglevel(struct net_device * dev,char * command,int total_len)1184 wl_ext_wlmsglevel(struct net_device *dev, char *command, int total_len)
1185 {
1186 int val = -1, ret = 0;
1187 int bytes_written = 0;
1188
1189 sscanf(command, "%*s %x", &val);
1190
1191 if (val >=0) {
1192 if (val & DHD_ANDROID_VAL) {
1193 android_msg_level = (uint)(val & 0xFFFF);
1194 WL_MSG(dev->name, "android_msg_level=0x%x\n", android_msg_level);
1195 }
1196 #if defined(WL_WIRELESS_EXT)
1197 else if (val & DHD_IW_VAL) {
1198 iw_msg_level = (uint)(val & 0xFFFF);
1199 WL_MSG(dev->name, "iw_msg_level=0x%x\n", iw_msg_level);
1200 }
1201 #endif
1202 #ifdef WL_CFG80211
1203 else if (val & DHD_CFG_VAL) {
1204 wl_cfg80211_enable_trace((u32)(val & 0xFFFF));
1205 }
1206 #endif
1207 else if (val & DHD_CONFIG_VAL) {
1208 config_msg_level = (uint)(val & 0xFFFF);
1209 WL_MSG(dev->name, "config_msg_level=0x%x\n", config_msg_level);
1210 }
1211 else if (val & DHD_DUMP_VAL) {
1212 dump_msg_level = (uint)(val & 0xFFFF);
1213 WL_MSG(dev->name, "dump_msg_level=0x%x\n", dump_msg_level);
1214 }
1215 }
1216 else {
1217 bytes_written += snprintf(command+bytes_written, total_len,
1218 "android_msg_level=0x%x", android_msg_level);
1219 #if defined(WL_WIRELESS_EXT)
1220 bytes_written += snprintf(command+bytes_written, total_len,
1221 "\niw_msg_level=0x%x", iw_msg_level);
1222 #endif
1223 #ifdef WL_CFG80211
1224 bytes_written += snprintf(command+bytes_written, total_len,
1225 "\nwl_dbg_level=0x%x", wl_dbg_level);
1226 #endif
1227 bytes_written += snprintf(command+bytes_written, total_len,
1228 "\nconfig_msg_level=0x%x", config_msg_level);
1229 bytes_written += snprintf(command+bytes_written, total_len,
1230 "\ndump_msg_level=0x%x", dump_msg_level);
1231 AEXT_INFO(dev->name, "%s\n", command);
1232 ret = bytes_written;
1233 }
1234
1235 return ret;
1236 }
1237
1238 #ifdef WLEASYMESH
1239 #define CMD_EASYMESH "EASYMESH"
1240 //Set map 4 and dwds 1 on wlan0 interface
1241 #define EASYMESH_SLAVE "slave"
1242 #define EASYMESH_MASTER "master"
1243
1244 static int
wl_ext_easymesh(struct net_device * dev,char * command,int total_len)1245 wl_ext_easymesh(struct net_device *dev, char* command, int total_len)
1246 {
1247 int ret = 0, wlc_down = 1, wlc_up = 1, map = 4, dwds = 1;
1248
1249 AEXT_TRACE(dev->name, "command=%s, len=%d\n", command, total_len);
1250 if (strncmp(command, EASYMESH_SLAVE, strlen(EASYMESH_SLAVE)) == 0) {
1251 WL_MSG(dev->name, "try to set map %d, dwds %d\n", map, dwds);
1252 ret = wl_ext_ioctl(dev, WLC_DOWN, &wlc_down, sizeof(wlc_down), 1);
1253 if (ret)
1254 goto exit;
1255 wl_ext_iovar_setint(dev, "map", map);
1256 wl_ext_iovar_setint(dev, "dwds", dwds);
1257 ret = wl_ext_ioctl(dev, WLC_UP, &wlc_up, sizeof(wlc_up), 1);
1258 if (ret)
1259 goto exit;
1260 }
1261 else if (strncmp(command, EASYMESH_MASTER, strlen(EASYMESH_MASTER)) == 0) {
1262 map = dwds = 0;
1263 WL_MSG(dev->name, "try to set map %d, dwds %d\n", map, dwds);
1264 ret = wl_ext_ioctl(dev, WLC_DOWN, &wlc_down, sizeof(wlc_down), 1);
1265 if (ret) {
1266 goto exit;
1267 }
1268 wl_ext_iovar_setint(dev, "map", map);
1269 wl_ext_iovar_setint(dev, "dwds", dwds);
1270 ret = wl_ext_ioctl(dev, WLC_UP, &wlc_up, sizeof(wlc_up), 1);
1271 if (ret) {
1272 goto exit;
1273 }
1274 }
1275
1276 exit:
1277 return ret;
1278 }
1279 #endif /* WLEASYMESH */
1280
1281 int
wl_ext_add_del_ie(struct net_device * dev,uint pktflag,char * ie_data,const char * add_del_cmd)1282 wl_ext_add_del_ie(struct net_device *dev, uint pktflag, char *ie_data, const char* add_del_cmd)
1283 {
1284 vndr_ie_setbuf_t *vndr_ie = NULL;
1285 char iovar_buf[WLC_IOCTL_SMLEN]="\0";
1286 int ie_data_len = 0, tot_len = 0, iecount;
1287 int err = -1;
1288
1289 if (!strlen(ie_data)) {
1290 AEXT_ERROR(dev->name, "wrong ie %s\n", ie_data);
1291 goto exit;
1292 }
1293
1294 tot_len = (int)(sizeof(vndr_ie_setbuf_t) + ((strlen(ie_data)-2)/2));
1295 vndr_ie = (vndr_ie_setbuf_t *) kzalloc(tot_len, GFP_KERNEL);
1296 if (!vndr_ie) {
1297 AEXT_ERROR(dev->name, "IE memory alloc failed\n");
1298 err = -ENOMEM;
1299 goto exit;
1300 }
1301
1302 /* Copy the vndr_ie SET command ("add"/"del") to the buffer */
1303 strncpy(vndr_ie->cmd, add_del_cmd, VNDR_IE_CMD_LEN - 1);
1304 vndr_ie->cmd[VNDR_IE_CMD_LEN - 1] = '\0';
1305
1306 /* Set the IE count - the buffer contains only 1 IE */
1307 iecount = htod32(1);
1308 memcpy((void *)&vndr_ie->vndr_ie_buffer.iecount, &iecount, sizeof(s32));
1309
1310 /* Set packet flag to indicate that BEACON's will contain this IE */
1311 pktflag = htod32(pktflag);
1312 memcpy((void *)&vndr_ie->vndr_ie_buffer.vndr_ie_list[0].pktflag, &pktflag,
1313 sizeof(u32));
1314
1315 /* Set the IE ID */
1316 vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.id = (uchar)DOT11_MNG_VS_ID;
1317
1318 /* Set the IE LEN */
1319 vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.len = (strlen(ie_data)-2)/2;
1320
1321 /* Set the IE OUI and DATA */
1322 ie_data_len = wl_pattern_atoh(ie_data,
1323 (char *)vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui);
1324 if (ie_data_len <= 0) {
1325 AEXT_ERROR(dev->name, "wrong ie_data_len %d\n", (int)strlen(ie_data)-2);
1326 goto exit;
1327 }
1328
1329 err = wl_ext_iovar_setbuf(dev, "vndr_ie", vndr_ie, tot_len, iovar_buf,
1330 sizeof(iovar_buf), NULL);
1331
1332 exit:
1333 if (vndr_ie) {
1334 kfree(vndr_ie);
1335 }
1336 return err;
1337 }
1338
1339 #ifdef IDHCP
1340 /*
1341 terence 20190409:
1342 dhd_priv wl dhcpc_dump
1343 dhd_priv wl dhcpc_param <client ip> <server ip> <lease time>
1344 */
1345 static int
wl_ext_dhcpc_dump(struct net_device * dev,char * data,char * command,int total_len)1346 wl_ext_dhcpc_dump(struct net_device *dev, char *data, char *command,
1347 int total_len)
1348 {
1349 int ret = 0;
1350 int bytes_written = 0;
1351 uint32 ip_addr;
1352 char buf[20]="";
1353
1354 if (!data) {
1355 ret = wl_ext_iovar_getint(dev, "dhcpc_ip_addr", &ip_addr);
1356 if (!ret) {
1357 bcm_ip_ntoa((struct ipv4_addr *)&ip_addr, buf);
1358 bytes_written += snprintf(command+bytes_written, total_len,
1359 "ipaddr %s ", buf);
1360 }
1361
1362 ret = wl_ext_iovar_getint(dev, "dhcpc_ip_mask", &ip_addr);
1363 if (!ret) {
1364 bcm_ip_ntoa((struct ipv4_addr *)&ip_addr, buf);
1365 bytes_written += snprintf(command+bytes_written, total_len,
1366 "mask %s ", buf);
1367 }
1368
1369 ret = wl_ext_iovar_getint(dev, "dhcpc_ip_gateway", &ip_addr);
1370 if (!ret) {
1371 bcm_ip_ntoa((struct ipv4_addr *)&ip_addr, buf);
1372 bytes_written += snprintf(command+bytes_written, total_len,
1373 "gw %s ", buf);
1374 }
1375
1376 ret = wl_ext_iovar_getint(dev, "dhcpc_ip_dnsserv", &ip_addr);
1377 if (!ret) {
1378 bcm_ip_ntoa((struct ipv4_addr *)&ip_addr, buf);
1379 bytes_written += snprintf(command+bytes_written, total_len,
1380 "dnsserv %s ", buf);
1381 }
1382
1383 if (!bytes_written)
1384 bytes_written = -1;
1385
1386 AEXT_TRACE(dev->name, "command result is %s\n", command);
1387 }
1388
1389 return bytes_written;
1390 }
1391
1392 int
wl_ext_dhcpc_param(struct net_device * dev,char * data,char * command,int total_len)1393 wl_ext_dhcpc_param(struct net_device *dev, char *data, char *command,
1394 int total_len)
1395 {
1396 int ret = -1, bytes_written = 0;
1397 char ip_addr_str[20]="", ip_serv_str[20]="";
1398 struct dhcpc_parameter dhcpc_param;
1399 uint32 ip_addr, ip_serv, lease_time;
1400 char iovar_buf[WLC_IOCTL_SMLEN]="\0";
1401
1402 if (data) {
1403 AEXT_TRACE(dev->name, "cmd %s", command);
1404 sscanf(data, "%s %s %d", ip_addr_str, ip_serv_str, &lease_time);
1405 AEXT_TRACE(dev->name, "ip_addr = %s, ip_serv = %s, lease_time = %d",
1406 ip_addr_str, ip_serv_str, lease_time);
1407
1408 memset(&dhcpc_param, 0, sizeof(struct dhcpc_parameter));
1409 if (!bcm_atoipv4(ip_addr_str, (struct ipv4_addr *)&ip_addr)) {
1410 AEXT_ERROR(dev->name, "wrong ip_addr_str %s\n", ip_addr_str);
1411 ret = -1;
1412 goto exit;
1413 }
1414 dhcpc_param.ip_addr = ip_addr;
1415
1416 if (!bcm_atoipv4(ip_addr_str, (struct ipv4_addr *)&ip_serv)) {
1417 AEXT_ERROR(dev->name, "wrong ip_addr_str %s\n", ip_addr_str);
1418 ret = -1;
1419 goto exit;
1420 }
1421 dhcpc_param.ip_serv = ip_serv;
1422 dhcpc_param.lease_time = lease_time;
1423 ret = wl_ext_iovar_setbuf(dev, "dhcpc_param", &dhcpc_param,
1424 sizeof(struct dhcpc_parameter), iovar_buf, sizeof(iovar_buf), NULL);
1425 } else {
1426 ret = wl_ext_iovar_getbuf(dev, "dhcpc_param", &dhcpc_param,
1427 sizeof(struct dhcpc_parameter), iovar_buf, WLC_IOCTL_SMLEN, NULL);
1428 if (!ret) {
1429 bcm_ip_ntoa((struct ipv4_addr *)&dhcpc_param.ip_addr, ip_addr_str);
1430 bytes_written += snprintf(command + bytes_written, total_len,
1431 "ip_addr %s\n", ip_addr_str);
1432 bcm_ip_ntoa((struct ipv4_addr *)&dhcpc_param.ip_serv, ip_serv_str);
1433 bytes_written += snprintf(command + bytes_written, total_len,
1434 "ip_serv %s\n", ip_serv_str);
1435 bytes_written += snprintf(command + bytes_written, total_len,
1436 "lease_time %d\n", dhcpc_param.lease_time);
1437 AEXT_TRACE(dev->name, "command result is %s\n", command);
1438 ret = bytes_written;
1439 }
1440 }
1441
1442 exit:
1443 return ret;
1444 }
1445 #endif /* IDHCP */
1446
1447 int
wl_ext_mkeep_alive(struct net_device * dev,char * data,char * command,int total_len)1448 wl_ext_mkeep_alive(struct net_device *dev, char *data, char *command,
1449 int total_len)
1450 {
1451 struct dhd_pub *dhd = dhd_get_pub(dev);
1452 wl_mkeep_alive_pkt_v1_t *mkeep_alive_pktp;
1453 int ret = -1, i, ifidx, id, period=-1;
1454 char *packet = NULL, *buf = NULL;
1455 int bytes_written = 0;
1456
1457 if (data) {
1458 buf = kmalloc(total_len, GFP_KERNEL);
1459 if (buf == NULL) {
1460 AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
1461 goto exit;
1462 }
1463 packet = kmalloc(WLC_IOCTL_SMLEN, GFP_KERNEL);
1464 if (packet == NULL) {
1465 AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
1466 goto exit;
1467 }
1468 AEXT_TRACE(dev->name, "cmd %s", command);
1469 sscanf(data, "%d %d %s", &id, &period, packet);
1470 AEXT_TRACE(dev->name, "id=%d, period=%d, packet=%s", id, period, packet);
1471 if (period >= 0) {
1472 ifidx = dhd_net2idx(dhd->info, dev);
1473 ret = dhd_conf_mkeep_alive(dhd, ifidx, id, period, packet, FALSE);
1474 } else {
1475 if (id < 0)
1476 id = 0;
1477 ret = wl_ext_iovar_getbuf(dev, "mkeep_alive", &id, sizeof(id), buf,
1478 total_len, NULL);
1479 if (!ret) {
1480 mkeep_alive_pktp = (wl_mkeep_alive_pkt_v1_t *) buf;
1481 bytes_written += snprintf(command+bytes_written, total_len,
1482 "Id :%d\n"
1483 "Period (msec) :%d\n"
1484 "Length :%d\n"
1485 "Packet :0x",
1486 mkeep_alive_pktp->keep_alive_id,
1487 dtoh32(mkeep_alive_pktp->period_msec),
1488 dtoh16(mkeep_alive_pktp->len_bytes));
1489 for (i=0; i<mkeep_alive_pktp->len_bytes; i++) {
1490 bytes_written += snprintf(command+bytes_written, total_len,
1491 "%02x", mkeep_alive_pktp->data[i]);
1492 }
1493 AEXT_TRACE(dev->name, "command result is %s\n", command);
1494 ret = bytes_written;
1495 }
1496 }
1497 }
1498
1499 exit:
1500 if (buf)
1501 kfree(buf);
1502 if (packet)
1503 kfree(packet);
1504 return ret;
1505 }
1506
1507 #ifdef WL_EXT_TCPKA
1508 static int
wl_ext_tcpka_conn_add(struct net_device * dev,char * data,char * command,int total_len)1509 wl_ext_tcpka_conn_add(struct net_device *dev, char *data, char *command,
1510 int total_len)
1511 {
1512 int ret = 0;
1513 s8 iovar_buf[WLC_IOCTL_SMLEN];
1514 tcpka_conn_t *tcpka = NULL;
1515 uint32 sess_id = 0, ipid = 0, srcport = 0, dstport = 0, seq = 0, ack = 0,
1516 tcpwin = 0, tsval = 0, tsecr = 0, len = 0, ka_payload_len = 0;
1517 char dst_mac[ETHER_ADDR_STR_LEN], src_ip[IPV4_ADDR_STR_LEN],
1518 dst_ip[IPV4_ADDR_STR_LEN], ka_payload[32];
1519
1520 if (data) {
1521 memset(dst_mac, 0, sizeof(dst_mac));
1522 memset(src_ip, 0, sizeof(src_ip));
1523 memset(dst_ip, 0, sizeof(dst_ip));
1524 memset(ka_payload, 0, sizeof(ka_payload));
1525 sscanf(data, "%d %s %s %s %d %d %d %u %u %d %u %u %u %32s",
1526 &sess_id, dst_mac, src_ip, dst_ip, &ipid, &srcport, &dstport, &seq,
1527 &ack, &tcpwin, &tsval, &tsecr, &len, ka_payload);
1528
1529 ka_payload_len = strlen(ka_payload) / 2;
1530 tcpka = kmalloc(sizeof(struct tcpka_conn) + ka_payload_len, GFP_KERNEL);
1531 if (tcpka == NULL) {
1532 AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n",
1533 sizeof(struct tcpka_conn) + ka_payload_len);
1534 ret = -1;
1535 goto exit;
1536 }
1537 memset(tcpka, 0, sizeof(struct tcpka_conn) + ka_payload_len);
1538
1539 tcpka->sess_id = sess_id;
1540 if (!(ret = bcm_ether_atoe(dst_mac, &tcpka->dst_mac))) {
1541 AEXT_ERROR(dev->name, "mac parsing err addr=%s\n", dst_mac);
1542 ret = -1;
1543 goto exit;
1544 }
1545 if (!bcm_atoipv4(src_ip, &tcpka->src_ip)) {
1546 AEXT_ERROR(dev->name, "src_ip parsing err ip=%s\n", src_ip);
1547 ret = -1;
1548 goto exit;
1549 }
1550 if (!bcm_atoipv4(dst_ip, &tcpka->dst_ip)) {
1551 AEXT_ERROR(dev->name, "dst_ip parsing err ip=%s\n", dst_ip);
1552 ret = -1;
1553 goto exit;
1554 }
1555 tcpka->ipid = ipid;
1556 tcpka->srcport = srcport;
1557 tcpka->dstport = dstport;
1558 tcpka->seq = seq;
1559 tcpka->ack = ack;
1560 tcpka->tcpwin = tcpwin;
1561 tcpka->tsval = tsval;
1562 tcpka->tsecr = tsecr;
1563 tcpka->len = len;
1564 ka_payload_len = wl_pattern_atoh(ka_payload, (char *)tcpka->ka_payload);
1565 if (ka_payload_len == -1) {
1566 AEXT_ERROR(dev->name,"rejecting ka_payload=%s\n", ka_payload);
1567 ret = -1;
1568 goto exit;
1569 }
1570 tcpka->ka_payload_len = ka_payload_len;
1571
1572 AEXT_INFO(dev->name,
1573 "tcpka_conn_add %d %pM %pM %pM %d %d %d %u %u %d %u %u %u %u \"%s\"\n",
1574 tcpka->sess_id, &tcpka->dst_mac, &tcpka->src_ip, &tcpka->dst_ip,
1575 tcpka->ipid, tcpka->srcport, tcpka->dstport, tcpka->seq,
1576 tcpka->ack, tcpka->tcpwin, tcpka->tsval, tcpka->tsecr,
1577 tcpka->len, tcpka->ka_payload_len, tcpka->ka_payload);
1578
1579 ret = wl_ext_iovar_setbuf(dev, "tcpka_conn_add", (char *)tcpka,
1580 (sizeof(tcpka_conn_t) + tcpka->ka_payload_len - 1),
1581 iovar_buf, sizeof(iovar_buf), NULL);
1582 }
1583
1584 exit:
1585 if (tcpka)
1586 kfree(tcpka);
1587 return ret;
1588 }
1589
1590 static int
wl_ext_tcpka_conn_enable(struct net_device * dev,char * data,char * command,int total_len)1591 wl_ext_tcpka_conn_enable(struct net_device *dev, char *data, char *command,
1592 int total_len)
1593 {
1594 s8 iovar_buf[WLC_IOCTL_SMLEN];
1595 tcpka_conn_sess_t tcpka_conn;
1596 int ret = 0;
1597 uint32 sess_id = 0, flag, interval = 0, retry_interval = 0, retry_count = 0;
1598
1599 if (data) {
1600 sscanf(data, "%d %d %d %d %d",
1601 &sess_id, &flag, &interval, &retry_interval, &retry_count);
1602 tcpka_conn.sess_id = sess_id;
1603 tcpka_conn.flag = flag;
1604 if (tcpka_conn.flag) {
1605 tcpka_conn.tcpka_timers.interval = interval;
1606 tcpka_conn.tcpka_timers.retry_interval = retry_interval;
1607 tcpka_conn.tcpka_timers.retry_count = retry_count;
1608 } else {
1609 tcpka_conn.tcpka_timers.interval = 0;
1610 tcpka_conn.tcpka_timers.retry_interval = 0;
1611 tcpka_conn.tcpka_timers.retry_count = 0;
1612 }
1613
1614 AEXT_INFO(dev->name, "tcpka_conn_enable %d %d %d %d %d\n",
1615 tcpka_conn.sess_id, tcpka_conn.flag,
1616 tcpka_conn.tcpka_timers.interval,
1617 tcpka_conn.tcpka_timers.retry_interval,
1618 tcpka_conn.tcpka_timers.retry_count);
1619
1620 ret = wl_ext_iovar_setbuf(dev, "tcpka_conn_enable", (char *)&tcpka_conn,
1621 sizeof(tcpka_conn_sess_t), iovar_buf, sizeof(iovar_buf), NULL);
1622 }
1623
1624 return ret;
1625 }
1626
1627 static int
wl_ext_tcpka_conn_info(struct net_device * dev,char * data,char * command,int total_len)1628 wl_ext_tcpka_conn_info(struct net_device *dev, char *data, char *command,
1629 int total_len)
1630 {
1631 s8 iovar_buf[WLC_IOCTL_SMLEN];
1632 tcpka_conn_sess_info_t *info = NULL;
1633 uint32 sess_id = 0;
1634 int ret = 0, bytes_written = 0;
1635
1636 if (data) {
1637 sscanf(data, "%d", &sess_id);
1638 AEXT_INFO(dev->name, "tcpka_conn_sess_info %d\n", sess_id);
1639 ret = wl_ext_iovar_getbuf(dev, "tcpka_conn_sess_info", (char *)&sess_id,
1640 sizeof(uint32), iovar_buf, sizeof(iovar_buf), NULL);
1641 if (!ret) {
1642 info = (tcpka_conn_sess_info_t *) iovar_buf;
1643 bytes_written += snprintf(command+bytes_written, total_len,
1644 "id :%d\n"
1645 "ipid :%d\n"
1646 "seq :%u\n"
1647 "ack :%u",
1648 sess_id, info->ipid, info->seq, info->ack);
1649 AEXT_INFO(dev->name, "%s\n", command);
1650 ret = bytes_written;
1651 }
1652 }
1653
1654 return ret;
1655 }
1656 #endif /* WL_EXT_TCPKA */
1657
1658 static int
wl_ext_rsdb_mode(struct net_device * dev,char * data,char * command,int total_len)1659 wl_ext_rsdb_mode(struct net_device *dev, char *data, char *command,
1660 int total_len)
1661 {
1662 s8 iovar_buf[WLC_IOCTL_SMLEN];
1663 wl_config_t rsdb_mode_cfg = {1, 0}, *rsdb_p;
1664 int ret = 0;
1665
1666 if (data) {
1667 rsdb_mode_cfg.config = (int)simple_strtol(data, NULL, 0);
1668 ret = wl_ext_iovar_setbuf(dev, "rsdb_mode", (char *)&rsdb_mode_cfg,
1669 sizeof(rsdb_mode_cfg), iovar_buf, WLC_IOCTL_SMLEN, NULL);
1670 AEXT_INFO(dev->name, "rsdb_mode %d\n", rsdb_mode_cfg.config);
1671 } else {
1672 ret = wl_ext_iovar_getbuf(dev, "rsdb_mode", NULL, 0,
1673 iovar_buf, WLC_IOCTL_SMLEN, NULL);
1674 if (!ret) {
1675 rsdb_p = (wl_config_t *) iovar_buf;
1676 ret = snprintf(command, total_len, "%d", rsdb_p->config);
1677 AEXT_TRACE(dev->name, "command result is %s\n", command);
1678 }
1679 }
1680
1681 return ret;
1682 }
1683
1684 static int
wl_ext_recal(struct net_device * dev,char * data,char * command,int total_len)1685 wl_ext_recal(struct net_device *dev, char *data, char *command,
1686 int total_len)
1687 {
1688 struct dhd_pub *dhd = dhd_get_pub(dev);
1689 int ret = 0, i, nchan, nssid = 0;
1690 int params_size = WL_SCAN_PARAMS_V1_FIXED_SIZE + WL_NUMCHANNELS * sizeof(uint16);
1691 wl_scan_params_v1_t *params = NULL;
1692 char *p;
1693
1694 AEXT_TRACE(dev->name, "Enter\n");
1695
1696 if (data) {
1697 params_size += WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t);
1698 params = (wl_scan_params_v1_t *) kzalloc(params_size, GFP_KERNEL);
1699 if (params == NULL) {
1700 ret = -ENOMEM;
1701 goto exit;
1702 }
1703 memset(params, 0, params_size);
1704
1705 memcpy(¶ms->bssid, ðer_bcast, ETHER_ADDR_LEN);
1706 params->bss_type = DOT11_BSSTYPE_ANY;
1707 params->scan_type = 0;
1708 params->nprobes = -1;
1709 params->active_time = -1;
1710 params->passive_time = -1;
1711 params->home_time = -1;
1712 params->channel_num = 0;
1713
1714 params->scan_type |= WL_SCANFLAGS_PASSIVE;
1715 nchan = 2;
1716 params->channel_list[0] = wf_channel2chspec(1, WL_CHANSPEC_BW_20);
1717 params->channel_list[1] = wf_channel2chspec(2, WL_CHANSPEC_BW_20);
1718
1719 params->nprobes = htod32(params->nprobes);
1720 params->active_time = htod32(params->active_time);
1721 params->passive_time = htod32(params->passive_time);
1722 params->home_time = htod32(params->home_time);
1723
1724 for (i = 0; i < nchan; i++) {
1725 wl_ext_chspec_host_to_driver(dhd, params->channel_list[i]);
1726 }
1727
1728 p = (char*)params->channel_list + nchan * sizeof(uint16);
1729
1730 params->channel_num = htod32((nssid << WL_SCAN_PARAMS_NSSID_SHIFT) |
1731 (nchan & WL_SCAN_PARAMS_COUNT_MASK));
1732 params_size = p - (char*)params + nssid * sizeof(wlc_ssid_t);
1733
1734 AEXT_INFO(dev->name, "recal\n");
1735 ret = wl_ext_ioctl(dev, WLC_SCAN, params, params_size, 1);
1736 }
1737
1738 exit:
1739 if (params)
1740 kfree(params);
1741 return ret;
1742 }
1743
1744 static s32
wl_ext_add_remove_eventmsg(struct net_device * ndev,u16 event,bool add)1745 wl_ext_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add)
1746 {
1747 s8 iovbuf[WL_EVENTING_MASK_LEN + 12];
1748 s8 eventmask[WL_EVENTING_MASK_LEN];
1749 s32 err = 0;
1750
1751 if (!ndev)
1752 return -ENODEV;
1753
1754 /* Setup event_msgs */
1755 err = wldev_iovar_getbuf(ndev, "event_msgs", NULL, 0, iovbuf, sizeof(iovbuf), NULL);
1756 if (unlikely(err)) {
1757 AEXT_ERROR(ndev->name, "Get event_msgs error (%d)\n", err);
1758 goto eventmsg_out;
1759 }
1760 memcpy(eventmask, iovbuf, WL_EVENTING_MASK_LEN);
1761 if (add) {
1762 setbit(eventmask, event);
1763 } else {
1764 clrbit(eventmask, event);
1765 }
1766 err = wldev_iovar_setbuf(ndev, "event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf,
1767 sizeof(iovbuf), NULL);
1768 if (unlikely(err)) {
1769 AEXT_ERROR(ndev->name, "Set event_msgs error (%d)\n", err);
1770 goto eventmsg_out;
1771 }
1772
1773 eventmsg_out:
1774 return err;
1775 }
1776
1777 static int
wl_ext_event_msg(struct net_device * dev,char * data,char * command,int total_len)1778 wl_ext_event_msg(struct net_device *dev, char *data,
1779 char *command, int total_len)
1780 {
1781 s8 iovbuf[WL_EVENTING_MASK_LEN + 12];
1782 s8 eventmask[WL_EVENTING_MASK_LEN];
1783 int i, bytes_written = 0, add = -1;
1784 uint event;
1785 char *vbuf;
1786 bool skipzeros;
1787
1788 /* dhd_priv wl event_msg [offset] [1/0, 1 for add, 0 for remove] */
1789 /* dhd_priv wl event_msg 40 1 */
1790 if (data) {
1791 AEXT_TRACE(dev->name, "data = %s\n", data);
1792 sscanf(data, "%d %d", &event, &add);
1793 /* Setup event_msgs */
1794 bytes_written = wldev_iovar_getbuf(dev, "event_msgs", NULL, 0, iovbuf,
1795 sizeof(iovbuf), NULL);
1796 if (unlikely(bytes_written)) {
1797 AEXT_ERROR(dev->name, "Get event_msgs error (%d)\n", bytes_written);
1798 goto eventmsg_out;
1799 }
1800 memcpy(eventmask, iovbuf, WL_EVENTING_MASK_LEN);
1801 if (add == -1) {
1802 if (isset(eventmask, event))
1803 bytes_written += snprintf(command+bytes_written, total_len, "1");
1804 else
1805 bytes_written += snprintf(command+bytes_written, total_len, "0");
1806 AEXT_INFO(dev->name, "%s\n", command);
1807 goto eventmsg_out;
1808 }
1809 bytes_written = wl_ext_add_remove_eventmsg(dev, event, add);
1810 }
1811 else {
1812 /* Setup event_msgs */
1813 bytes_written = wldev_iovar_getbuf(dev, "event_msgs", NULL, 0, iovbuf,
1814 sizeof(iovbuf), NULL);
1815 if (bytes_written) {
1816 AEXT_ERROR(dev->name, "Get event_msgs error (%d)\n", bytes_written);
1817 goto eventmsg_out;
1818 }
1819 vbuf = (char *)iovbuf;
1820 bytes_written += snprintf(command+bytes_written, total_len, "0x");
1821 for (i = (sizeof(eventmask) - 1); i >= 0; i--) {
1822 if (vbuf[i] || (i == 0))
1823 skipzeros = FALSE;
1824 if (skipzeros)
1825 continue;
1826 bytes_written += snprintf(command+bytes_written, total_len,
1827 "%02x", vbuf[i] & 0xff);
1828 }
1829 AEXT_INFO(dev->name, "%s\n", command);
1830 }
1831
1832 eventmsg_out:
1833 return bytes_written;
1834 }
1835
1836 #ifdef PKT_FILTER_SUPPORT
1837 extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg);
1838 extern void dhd_pktfilter_offload_delete(dhd_pub_t *dhd, int id);
1839 extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
1840 static int
wl_ext_pkt_filter_add(struct net_device * dev,char * data,char * command,int total_len)1841 wl_ext_pkt_filter_add(struct net_device *dev, char *data, char *command,
1842 int total_len)
1843 {
1844 struct dhd_pub *dhd = dhd_get_pub(dev);
1845 int i, filter_id, new_id = 0, cnt;
1846 conf_pkt_filter_add_t *filter_add = &dhd->conf->pkt_filter_add;
1847 char **pktfilter = dhd->pktfilter;
1848 int err = 0;
1849
1850 if (data) {
1851 AEXT_TRACE(dev->name, "data = %s\n", data);
1852
1853 new_id = simple_strtol(data, NULL, 10);
1854 if (new_id <= 0) {
1855 AEXT_ERROR(dev->name, "wrong id %d\n", new_id);
1856 return -1;
1857 }
1858
1859 cnt = dhd->pktfilter_count;
1860 for (i=0; i<cnt; i++) {
1861 if (!pktfilter[i])
1862 continue;
1863 filter_id = simple_strtol(pktfilter[i], NULL, 10);
1864 if (new_id == filter_id) {
1865 AEXT_ERROR(dev->name, "filter id %d already in list\n", filter_id);
1866 return -1;
1867 }
1868 }
1869
1870 cnt = filter_add->count;
1871 if (cnt >= DHD_CONF_FILTER_MAX) {
1872 AEXT_ERROR(dev->name, "not enough filter\n");
1873 return -1;
1874 }
1875 for (i=0; i<cnt; i++) {
1876 filter_id = simple_strtol(filter_add->filter[i], NULL, 10);
1877 if (new_id == filter_id) {
1878 AEXT_ERROR(dev->name, "filter id %d already in list\n", filter_id);
1879 return -1;
1880 }
1881 }
1882
1883 strcpy(&filter_add->filter[cnt][0], data);
1884 dhd->pktfilter[dhd->pktfilter_count] = filter_add->filter[cnt];
1885 filter_add->count++;
1886 dhd->pktfilter_count++;
1887
1888 dhd_pktfilter_offload_set(dhd, data);
1889 AEXT_INFO(dev->name, "filter id %d added\n", new_id);
1890 }
1891
1892 return err;
1893 }
1894
1895 static int
wl_ext_pkt_filter_delete(struct net_device * dev,char * data,char * command,int total_len)1896 wl_ext_pkt_filter_delete(struct net_device *dev, char *data, char *command,
1897 int total_len)
1898 {
1899 struct dhd_pub *dhd = dhd_get_pub(dev);
1900 int i, j, filter_id, cnt;
1901 char **pktfilter = dhd->pktfilter;
1902 conf_pkt_filter_add_t *filter_add = &dhd->conf->pkt_filter_add;
1903 bool in_filter = FALSE;
1904 int id, err = 0;
1905
1906 if (data) {
1907 AEXT_TRACE(dev->name, "data = %s\n", data);
1908 id = (int)simple_strtol(data, NULL, 0);
1909
1910 cnt = filter_add->count;
1911 for (i=0; i<cnt; i++) {
1912 filter_id = simple_strtol(filter_add->filter[i], NULL, 10);
1913 if (id == filter_id) {
1914 in_filter = TRUE;
1915 memset(filter_add->filter[i], 0, PKT_FILTER_LEN);
1916 for (j=i; j<(cnt-1); j++) {
1917 strcpy(filter_add->filter[j], filter_add->filter[j+1]);
1918 memset(filter_add->filter[j+1], 0, PKT_FILTER_LEN);
1919 }
1920 cnt--;
1921 filter_add->count--;
1922 dhd->pktfilter_count--;
1923 }
1924 }
1925
1926 cnt = dhd->pktfilter_count;
1927 for (i=0; i<cnt; i++) {
1928 if (!pktfilter[i])
1929 continue;
1930 filter_id = simple_strtol(pktfilter[i], NULL, 10);
1931 if (id == filter_id) {
1932 in_filter = TRUE;
1933 memset(pktfilter[i], 0, strlen(pktfilter[i]));
1934 }
1935 }
1936
1937 if (in_filter) {
1938 dhd_pktfilter_offload_delete(dhd, id);
1939 AEXT_INFO(dev->name, "filter id %d deleted\n", id);
1940 } else {
1941 AEXT_ERROR(dev->name, "filter id %d not in list\n", id);
1942 err = -1;
1943 }
1944 }
1945
1946 return err;
1947 }
1948
1949 static int
wl_ext_pkt_filter_enable(struct net_device * dev,char * data,char * command,int total_len)1950 wl_ext_pkt_filter_enable(struct net_device *dev, char *data, char *command,
1951 int total_len)
1952 {
1953 struct dhd_pub *dhd = dhd_get_pub(dev);
1954 int err = 0, id, enable;
1955 int i, filter_id, cnt;
1956 char **pktfilter = dhd->pktfilter;
1957 bool in_filter = FALSE;
1958
1959 /* dhd_priv wl pkt_filter_enable [id] [1/0] */
1960 /* dhd_priv wl pkt_filter_enable 141 1 */
1961 if (data) {
1962 sscanf(data, "%d %d", &id, &enable);
1963
1964 cnt = dhd->pktfilter_count;
1965 for (i=0; i<cnt; i++) {
1966 if (!pktfilter[i])
1967 continue;
1968 filter_id = simple_strtol(pktfilter[i], NULL, 10);
1969 if (id == filter_id) {
1970 in_filter = TRUE;
1971 break;
1972 }
1973 }
1974
1975 if (in_filter) {
1976 dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
1977 enable, dhd_master_mode);
1978 AEXT_INFO(dev->name, "filter id %d %s\n", id, enable?"enabled":"disabled");
1979 } else {
1980 AEXT_ERROR(dev->name, "filter id %d not in list\n", id);
1981 err = -1;
1982 }
1983 }
1984
1985 return err;
1986 }
1987 #endif /* PKT_FILTER_SUPPORT */
1988
1989 #ifdef SENDPROB
1990 static int
wl_ext_send_probreq(struct net_device * dev,char * data,char * command,int total_len)1991 wl_ext_send_probreq(struct net_device *dev, char *data, char *command,
1992 int total_len)
1993 {
1994 int err = 0;
1995 char addr_str[16], addr[6];
1996 char iovar_buf[WLC_IOCTL_SMLEN]="\0";
1997 char ie_data[WLC_IOCTL_SMLEN] = "\0";
1998 wl_probe_params_t params;
1999
2000 /* dhd_priv wl send_probreq [dest. addr] [OUI+VAL] */
2001 /* dhd_priv wl send_probreq 0x00904c010203 0x00904c01020304050607 */
2002 if (data) {
2003 AEXT_TRACE(dev->name, "data = %s\n", data);
2004 sscanf(data, "%s %s", addr_str, ie_data);
2005 AEXT_TRACE(dev->name, "addr=%s, ie=%s\n", addr_str, ie_data);
2006
2007 if (strlen(addr_str) != 14) {
2008 AEXT_ERROR(dev->name, "wrong addr %s\n", addr_str);
2009 goto exit;
2010 }
2011 wl_pattern_atoh(addr_str, (char *) addr);
2012 memset(¶ms, 0, sizeof(params));
2013 memcpy(¶ms.bssid, addr, ETHER_ADDR_LEN);
2014 memcpy(¶ms.mac, addr, ETHER_ADDR_LEN);
2015
2016 err = wl_ext_add_del_ie(dev, VNDR_IE_PRBREQ_FLAG, ie_data, "add");
2017 if (err)
2018 goto exit;
2019 err = wl_ext_iovar_setbuf(dev, "sendprb", (char *)¶ms, sizeof(params),
2020 iovar_buf, sizeof(iovar_buf), NULL);
2021 OSL_SLEEP(100);
2022 wl_ext_add_del_ie(dev, VNDR_IE_PRBREQ_FLAG, ie_data, "del");
2023 }
2024
2025 exit:
2026 return err;
2027 }
2028
2029 static int
wl_ext_send_probresp(struct net_device * dev,char * data,char * command,int total_len)2030 wl_ext_send_probresp(struct net_device *dev, char *data, char *command,
2031 int total_len)
2032 {
2033 int err = 0;
2034 char addr_str[16], addr[6];
2035 char iovar_buf[WLC_IOCTL_SMLEN]="\0";
2036 char ie_data[WLC_IOCTL_SMLEN] = "\0";
2037
2038 /* dhd_priv wl send_probresp [dest. addr] [OUI+VAL] */
2039 /* dhd_priv wl send_probresp 0x00904c010203 0x00904c01020304050607 */
2040 if (data) {
2041 AEXT_TRACE(dev->name, "data = %s\n", data);
2042 sscanf(data, "%s %s", addr_str, ie_data);
2043 AEXT_TRACE(dev->name, "addr=%s, ie=%s\n", addr_str, ie_data);
2044
2045 if (strlen(addr_str) != 14) {
2046 AEXT_ERROR(dev->name, "wrong addr %s\n", addr_str);
2047 goto exit;
2048 }
2049 wl_pattern_atoh(addr_str, (char *) addr);
2050
2051 err = wl_ext_add_del_ie(dev, VNDR_IE_PRBRSP_FLAG, ie_data, "add");
2052 if (err)
2053 goto exit;
2054 err = wl_ext_iovar_setbuf(dev, "send_probresp", addr, sizeof(addr),
2055 iovar_buf, sizeof(iovar_buf), NULL);
2056 OSL_SLEEP(100);
2057 wl_ext_add_del_ie(dev, VNDR_IE_PRBRSP_FLAG, ie_data, "del");
2058 }
2059
2060 exit:
2061 return err;
2062 }
2063
2064 static int
wl_ext_recv_probreq(struct net_device * dev,char * data,char * command,int total_len)2065 wl_ext_recv_probreq(struct net_device *dev, char *data, char *command,
2066 int total_len)
2067 {
2068 int err = 0, enable = 0;
2069 char cmd[32];
2070 struct dhd_pub *dhd = dhd_get_pub(dev);
2071
2072 /* enable:
2073 1. dhd_priv wl 86 0
2074 2. dhd_priv wl event_msg 44 1
2075 disable:
2076 1. dhd_priv wl 86 2;
2077 2. dhd_priv wl event_msg 44 0
2078 */
2079 if (data) {
2080 AEXT_TRACE(dev->name, "data = %s\n", data);
2081 sscanf(data, "%d", &enable);
2082 if (enable) {
2083 strcpy(cmd, "wl 86 0");
2084 err = wl_ext_wl_iovar(dev, cmd, total_len);
2085 if (err)
2086 goto exit;
2087 strcpy(cmd, "wl event_msg 44 1");
2088 err = wl_ext_wl_iovar(dev, cmd, total_len);
2089 if (err)
2090 goto exit;
2091 dhd->recv_probereq = TRUE;
2092 } else {
2093 if (dhd->conf->pm) {
2094 strcpy(cmd, "wl 86 2");
2095 wl_ext_wl_iovar(dev, cmd, total_len);
2096 }
2097 strcpy(cmd, "wl event_msg 44 0");
2098 wl_ext_wl_iovar(dev, cmd, total_len);
2099 dhd->recv_probereq = FALSE;
2100 }
2101 }
2102
2103 exit:
2104 return err;
2105 }
2106
2107 static int
wl_ext_recv_probresp(struct net_device * dev,char * data,char * command,int total_len)2108 wl_ext_recv_probresp(struct net_device *dev, char *data, char *command,
2109 int total_len)
2110 {
2111 int err = 0, enable = 0;
2112 char cmd[64];
2113
2114 /* enable:
2115 1. dhd_priv wl pkt_filter_add 150 0 0 0 0xFF 0x50
2116 2. dhd_priv wl pkt_filter_enable 150 1
2117 3. dhd_priv wl mpc 0
2118 4. dhd_priv wl 108 1
2119 disable:
2120 1. dhd_priv wl 108 0
2121 2. dhd_priv wl mpc 1
2122 3. dhd_priv wl pkt_filter_disable 150 0
2123 4. dhd_priv pkt_filter_delete 150
2124 */
2125 if (data) {
2126 AEXT_TRACE(dev->name, "data = %s\n", data);
2127 sscanf(data, "%d", &enable);
2128 if (enable) {
2129 strcpy(cmd, "wl pkt_filter_add 150 0 0 0 0xFF 0x50");
2130 err = wl_ext_wl_iovar(dev, cmd, total_len);
2131 if (err)
2132 goto exit;
2133 strcpy(cmd, "wl pkt_filter_enable 150 1");
2134 err = wl_ext_wl_iovar(dev, cmd, total_len);
2135 if (err)
2136 goto exit;
2137 strcpy(cmd, "wl mpc 0");
2138 err = wl_ext_wl_iovar(dev, cmd, total_len);
2139 if (err)
2140 goto exit;
2141 strcpy(cmd, "wl 108 1");
2142 err= wl_ext_wl_iovar(dev, cmd, total_len);
2143 } else {
2144 strcpy(cmd, "wl 108 0");
2145 wl_ext_wl_iovar(dev, cmd, total_len);
2146 strcpy(cmd, "wl mpc 1");
2147 wl_ext_wl_iovar(dev, cmd, total_len);
2148 strcpy(cmd, "wl pkt_filter_enable 150 0");
2149 wl_ext_wl_iovar(dev, cmd, total_len);
2150 strcpy(cmd, "wl pkt_filter_delete 150");
2151 wl_ext_wl_iovar(dev, cmd, total_len);
2152 }
2153 }
2154
2155 exit:
2156 return err;
2157 }
2158 #endif /* SENDPROB */
2159
2160 #if defined(USE_IW)
2161 static int
wl_ext_gtk_key_info(struct net_device * dev,char * data,char * command,int total_len)2162 wl_ext_gtk_key_info(struct net_device *dev, char *data, char *command, int total_len)
2163 {
2164 struct dhd_pub *dhd = dhd_get_pub(dev);
2165 int err = 0;
2166 char iovar_buf[WLC_IOCTL_SMLEN]="\0";
2167 gtk_keyinfo_t keyinfo;
2168 bcol_gtk_para_t bcol_keyinfo;
2169
2170 /* wl gtk_key_info [kck kek replay_ctr] */
2171 /* wl gtk_key_info 001122..FF001122..FF00000000000001 */
2172 if (data) {
2173 if (!dhd->conf->rekey_offload) {
2174 AEXT_INFO(dev->name, "rekey_offload disabled\n");
2175 return BCME_UNSUPPORTED;
2176 }
2177
2178 memset(&bcol_keyinfo, 0, sizeof(bcol_keyinfo));
2179 bcol_keyinfo.enable = 1;
2180 bcol_keyinfo.ptk_len = 64;
2181 memcpy(&bcol_keyinfo.ptk, data, RSN_KCK_LENGTH+RSN_KEK_LENGTH);
2182 err = wl_ext_iovar_setbuf(dev, "bcol_gtk_rekey_ptk", &bcol_keyinfo,
2183 sizeof(bcol_keyinfo), iovar_buf, sizeof(iovar_buf), NULL);
2184 if (!err) {
2185 goto exit;
2186 }
2187
2188 memset(&keyinfo, 0, sizeof(keyinfo));
2189 memcpy(&keyinfo, data, RSN_KCK_LENGTH+RSN_KEK_LENGTH+RSN_REPLAY_LEN);
2190 err = wl_ext_iovar_setbuf(dev, "gtk_key_info", &keyinfo, sizeof(keyinfo),
2191 iovar_buf, sizeof(iovar_buf), NULL);
2192 if (err) {
2193 AEXT_ERROR(dev->name, "failed to set gtk_key_info\n");
2194 return err;
2195 }
2196 }
2197
2198 exit:
2199 if (android_msg_level & ANDROID_INFO_LEVEL) {
2200 prhex("kck", (uchar *)keyinfo.KCK, RSN_KCK_LENGTH);
2201 prhex("kek", (uchar *)keyinfo.KEK, RSN_KEK_LENGTH);
2202 prhex("replay_ctr", (uchar *)keyinfo.ReplayCounter, RSN_REPLAY_LEN);
2203 }
2204 return err;
2205 }
2206 #endif /* USE_IW */
2207
2208 #ifdef WL_EXT_WOWL
2209 static int
wl_ext_wowl_pattern(struct net_device * dev,char * data,char * command,int total_len)2210 wl_ext_wowl_pattern(struct net_device *dev, char *data, char *command,
2211 int total_len)
2212 {
2213 s8 iovar_buf[WLC_IOCTL_SMLEN];
2214 uint buf_len = 0;
2215 int offset;
2216 char mask[128]="\0", pattern[128]="\0", add[4]="\0",
2217 mask_tmp[128], *pmask_tmp;
2218 uint32 masksize, patternsize, pad_len = 0;
2219 wl_wowl_pattern2_t *wowl_pattern2 = NULL;
2220 wl_wowl_pattern_t *wowl_pattern = NULL;
2221 char *mask_and_pattern;
2222 wl_wowl_pattern_list_t *list;
2223 uint8 *ptr;
2224 int ret = 0, i, j, v;
2225
2226 if (data) {
2227 sscanf(data, "%s %d %s %s", add, &offset, mask_tmp, pattern);
2228 if (strcmp(add, "add") != 0 && strcmp(add, "clr") != 0) {
2229 AEXT_ERROR(dev->name, "first arg should be add or clr\n");
2230 goto exit;
2231 }
2232 if (!strcmp(add, "clr")) {
2233 AEXT_INFO(dev->name, "wowl_pattern clr\n");
2234 ret = wl_ext_iovar_setbuf(dev, "wowl_pattern", add,
2235 sizeof(add), iovar_buf, sizeof(iovar_buf), NULL);
2236 goto exit;
2237 }
2238 masksize = strlen(mask_tmp) -2;
2239 AEXT_TRACE(dev->name, "0 mask_tmp=%s, masksize=%d\n", mask_tmp, masksize);
2240
2241 // add pading
2242 if (masksize % 16)
2243 pad_len = (16 - masksize % 16);
2244 for (i=0; i<pad_len; i++)
2245 strcat(mask_tmp, "0");
2246 masksize += pad_len;
2247 AEXT_TRACE(dev->name, "1 mask_tmp=%s, masksize=%d\n", mask_tmp, masksize);
2248
2249 // translate 0x00 to 0, others to 1
2250 j = 0;
2251 pmask_tmp = &mask_tmp[2];
2252 for (i=0; i<masksize/2; i++) {
2253 if(strncmp(&pmask_tmp[i*2], "00", 2))
2254 pmask_tmp[j] = '1';
2255 else
2256 pmask_tmp[j] = '0';
2257 j++;
2258 }
2259 pmask_tmp[j] = '\0';
2260 masksize = masksize / 2;
2261 AEXT_TRACE(dev->name, "2 mask_tmp=%s, masksize=%d\n", mask_tmp, masksize);
2262
2263 // reorder per 8bits
2264 pmask_tmp = &mask_tmp[2];
2265 for (i=0; i<masksize/8; i++) {
2266 char c;
2267 for (j=0; j<4; j++) {
2268 c = pmask_tmp[i*8+j];
2269 pmask_tmp[i*8+j] = pmask_tmp[(i+1)*8-j-1];
2270 pmask_tmp[(i+1)*8-j-1] = c;
2271 }
2272 }
2273 AEXT_TRACE(dev->name, "3 mask_tmp=%s, masksize=%d\n", mask_tmp, masksize);
2274
2275 // translate 8bits to 1byte
2276 j = 0; v = 0;
2277 pmask_tmp = &mask_tmp[2];
2278 strcpy(mask, "0x");
2279 for (i=0; i<masksize; i++) {
2280 v = (v<<1) | (pmask_tmp[i]=='1');
2281 if (((i+1)%4) == 0) {
2282 if (v < 10)
2283 mask[j+2] = v + '0';
2284 else
2285 mask[j+2] = (v-10) + 'a';
2286 j++;
2287 v = 0;
2288 }
2289 }
2290 mask[j+2] = '\0';
2291 masksize = j/2;
2292 AEXT_TRACE(dev->name, "4 mask=%s, masksize=%d\n", mask, masksize);
2293
2294 patternsize = (strlen(pattern)-2)/2;
2295 buf_len = sizeof(wl_wowl_pattern2_t) + patternsize + masksize;
2296 wowl_pattern2 = kmalloc(buf_len, GFP_KERNEL);
2297 if (wowl_pattern2 == NULL) {
2298 AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", buf_len);
2299 goto exit;
2300 }
2301 memset(wowl_pattern2, 0, sizeof(wl_wowl_pattern2_t));
2302
2303 strncpy(wowl_pattern2->cmd, add, sizeof(add));
2304 wowl_pattern2->wowl_pattern.type = 0;
2305 wowl_pattern2->wowl_pattern.offset = offset;
2306 mask_and_pattern = (char*)wowl_pattern2 + sizeof(wl_wowl_pattern2_t);
2307
2308 wowl_pattern2->wowl_pattern.masksize = masksize;
2309 ret = wl_pattern_atoh(mask, mask_and_pattern);
2310 if (ret == -1) {
2311 AEXT_ERROR(dev->name, "rejecting mask=%s\n", mask);
2312 goto exit;
2313 }
2314
2315 mask_and_pattern += wowl_pattern2->wowl_pattern.masksize;
2316 wowl_pattern2->wowl_pattern.patternoffset = sizeof(wl_wowl_pattern_t) +
2317 wowl_pattern2->wowl_pattern.masksize;
2318
2319 wowl_pattern2->wowl_pattern.patternsize = patternsize;
2320 ret = wl_pattern_atoh(pattern, mask_and_pattern);
2321 if (ret == -1) {
2322 AEXT_ERROR(dev->name, "rejecting pattern=%s\n", pattern);
2323 goto exit;
2324 }
2325
2326 AEXT_INFO(dev->name, "%s %d %s %s\n", add, offset, mask, pattern);
2327
2328 ret = wl_ext_iovar_setbuf(dev, "wowl_pattern", (char *)wowl_pattern2,
2329 buf_len, iovar_buf, sizeof(iovar_buf), NULL);
2330 }
2331 else {
2332 ret = wl_ext_iovar_getbuf(dev, "wowl_pattern", NULL, 0,
2333 iovar_buf, sizeof(iovar_buf), NULL);
2334 if (!ret) {
2335 list = (wl_wowl_pattern_list_t *)iovar_buf;
2336 ret = snprintf(command, total_len, "#of patterns :%d\n", list->count);
2337 ptr = (uint8 *)list->pattern;
2338 for (i=0; i<list->count; i++) {
2339 uint8 *pattern;
2340 wowl_pattern = (wl_wowl_pattern_t *)ptr;
2341 ret += snprintf(command+ret, total_len,
2342 "Pattern %d:\n"
2343 "ID :0x%x\n"
2344 "Offset :%d\n"
2345 "Masksize :%d\n"
2346 "Mask :0x",
2347 i+1, (uint32)wowl_pattern->id, wowl_pattern->offset,
2348 wowl_pattern->masksize);
2349 pattern = ((uint8 *)wowl_pattern + sizeof(wl_wowl_pattern_t));
2350 for (j = 0; j < wowl_pattern->masksize; j++) {
2351 ret += snprintf(command+ret, total_len, "%02x", pattern[j]);
2352 }
2353 ret += snprintf(command+ret, total_len, "\n");
2354 ret += snprintf(command+ret, total_len,
2355 "PatternSize:%d\n"
2356 "Pattern :0x",
2357 wowl_pattern->patternsize);
2358
2359 pattern = ((uint8*)wowl_pattern + wowl_pattern->patternoffset);
2360 for (j=0; j<wowl_pattern->patternsize; j++)
2361 ret += snprintf(command+ret, total_len, "%02x", pattern[j]);
2362 ret += snprintf(command+ret, total_len, "\n");
2363 ptr += (wowl_pattern->masksize + wowl_pattern->patternsize +
2364 sizeof(wl_wowl_pattern_t));
2365 }
2366
2367 AEXT_INFO(dev->name, "%s\n", command);
2368 }
2369 }
2370
2371 exit:
2372 if (wowl_pattern2)
2373 kfree(wowl_pattern2);
2374 return ret;
2375 }
2376
2377 static int
wl_ext_wowl_wakeind(struct net_device * dev,char * data,char * command,int total_len)2378 wl_ext_wowl_wakeind(struct net_device *dev, char *data, char *command,
2379 int total_len)
2380 {
2381 s8 iovar_buf[WLC_IOCTL_SMLEN];
2382 wl_wowl_wakeind_t *wake = NULL;
2383 int ret = -1;
2384 char clr[6]="\0";
2385
2386 if (data) {
2387 sscanf(data, "%s", clr);
2388 if (!strcmp(clr, "clear")) {
2389 AEXT_INFO(dev->name, "wowl_wakeind clear\n");
2390 ret = wl_ext_iovar_setbuf(dev, "wowl_wakeind", clr, sizeof(clr),
2391 iovar_buf, sizeof(iovar_buf), NULL);
2392 } else {
2393 AEXT_ERROR(dev->name, "first arg should be clear\n");
2394 }
2395 } else {
2396 ret = wl_ext_iovar_getbuf(dev, "wowl_wakeind", NULL, 0,
2397 iovar_buf, sizeof(iovar_buf), NULL);
2398 if (!ret) {
2399 wake = (wl_wowl_wakeind_t *) iovar_buf;
2400 ret = snprintf(command, total_len, "wakeind=0x%x", wake->ucode_wakeind);
2401 if (wake->ucode_wakeind & WL_WOWL_MAGIC)
2402 ret += snprintf(command+ret, total_len, " (MAGIC packet)");
2403 if (wake->ucode_wakeind & WL_WOWL_NET)
2404 ret += snprintf(command+ret, total_len, " (Netpattern)");
2405 if (wake->ucode_wakeind & WL_WOWL_DIS)
2406 ret += snprintf(command+ret, total_len, " (Disassoc/Deauth)");
2407 if (wake->ucode_wakeind & WL_WOWL_BCN)
2408 ret += snprintf(command+ret, total_len, " (Loss of beacon)");
2409 if (wake->ucode_wakeind & WL_WOWL_TCPKEEP_TIME)
2410 ret += snprintf(command+ret, total_len, " (TCPKA timeout)");
2411 if (wake->ucode_wakeind & WL_WOWL_TCPKEEP_DATA)
2412 ret += snprintf(command+ret, total_len, " (TCPKA data)");
2413 if (wake->ucode_wakeind & WL_WOWL_TCPFIN)
2414 ret += snprintf(command+ret, total_len, " (TCP FIN)");
2415 AEXT_INFO(dev->name, "%s\n", command);
2416 }
2417 }
2418
2419 return ret;
2420 }
2421 #endif /* WL_EXT_WOWL */
2422
2423 #ifdef WL_GPIO_NOTIFY
2424 typedef struct notify_payload {
2425 int index;
2426 int len;
2427 char payload[128];
2428 } notify_payload_t;
2429
2430 static int
wl_ext_gpio_notify(struct net_device * dev,char * data,char * command,int total_len)2431 wl_ext_gpio_notify(struct net_device *dev, char *data, char *command,
2432 int total_len)
2433 {
2434 s8 iovar_buf[WLC_IOCTL_SMLEN];
2435 notify_payload_t notify, *pnotify = NULL;
2436 int i, ret = 0, bytes_written = 0;
2437 char frame_str[WLC_IOCTL_SMLEN+3];
2438
2439 if (data) {
2440 memset(¬ify, 0, sizeof(notify));
2441 memset(frame_str, 0, sizeof(frame_str));
2442 sscanf(data, "%d %s", ¬ify.index, frame_str);
2443
2444 if (notify.index < 0)
2445 notify.index = 0;
2446
2447 if (strlen(frame_str)) {
2448 notify.len = wl_pattern_atoh(frame_str, notify.payload);
2449 if (notify.len == -1) {
2450 AEXT_ERROR(dev->name, "rejecting pattern=%s\n", frame_str);
2451 goto exit;
2452 }
2453 AEXT_INFO(dev->name, "index=%d, len=%d\n", notify.index, notify.len);
2454 if (android_msg_level & ANDROID_INFO_LEVEL)
2455 prhex("payload", (uchar *)notify.payload, notify.len);
2456 ret = wl_ext_iovar_setbuf(dev, "bcol_gpio_noti", (char *)¬ify,
2457 sizeof(notify), iovar_buf, WLC_IOCTL_SMLEN, NULL);
2458 } else {
2459 AEXT_INFO(dev->name, "index=%d\n", notify.index);
2460 ret = wl_ext_iovar_getbuf(dev, "bcol_gpio_noti", ¬ify.index,
2461 sizeof(notify.index), iovar_buf, sizeof(iovar_buf), NULL);
2462 if (!ret) {
2463 pnotify = (notify_payload_t *)iovar_buf;
2464 bytes_written += snprintf(command+bytes_written, total_len,
2465 "Id :%d\n"
2466 "Packet :0x",
2467 pnotify->index);
2468 for (i=0; i<pnotify->len; i++) {
2469 bytes_written += snprintf(command+bytes_written, total_len,
2470 "%02x", pnotify->payload[i]);
2471 }
2472 AEXT_TRACE(dev->name, "command result is\n%s\n", command);
2473 ret = bytes_written;
2474 }
2475 }
2476 }
2477
2478 exit:
2479 return ret;
2480 }
2481 #endif /* WL_GPIO_NOTIFY */
2482
2483 #ifdef CSI_SUPPORT
2484 typedef struct csi_config {
2485 /* Peer device mac address. */
2486 struct ether_addr addr;
2487 /* BW to be used in the measurements. This needs to be supported both by the */
2488 /* device itself and the peer. */
2489 uint32 bw;
2490 /* Time interval between measurements (units: 1 ms). */
2491 uint32 period;
2492 /* CSI method */
2493 uint32 method;
2494 } csi_config_t;
2495
2496 typedef struct csi_list {
2497 uint32 cnt;
2498 csi_config_t configs[1];
2499 } csi_list_t;
2500
2501 static int
wl_ext_csi(struct net_device * dev,char * data,char * command,int total_len)2502 wl_ext_csi(struct net_device *dev, char *data, char *command, int total_len)
2503 {
2504 csi_config_t csi, *csip;
2505 csi_list_t *csi_list;
2506 int ret = -1, period=-1, i;
2507 char mac[32], *buf = NULL;
2508 struct ether_addr ea;
2509 int bytes_written = 0;
2510
2511 buf = kmalloc(WLC_IOCTL_SMLEN, GFP_KERNEL);
2512 if (buf == NULL) {
2513 AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
2514 goto exit;
2515 }
2516 memset(buf, 0, WLC_IOCTL_SMLEN);
2517
2518 if (data) {
2519 sscanf(data, "%s %d", mac, &period);
2520 ret = bcm_ether_atoe(mac, &ea);
2521 if (!ret) {
2522 AEXT_ERROR(dev->name, "rejecting mac=%s, ret=%d\n", mac, ret);
2523 goto exit;
2524 }
2525 AEXT_TRACE(dev->name, "mac=%pM, period=%d", &ea, period);
2526 if (period > 0) {
2527 memset(&csi, 0, sizeof(csi_config_t));
2528 bcopy(&ea, &csi.addr, ETHER_ADDR_LEN);
2529 csi.period = period;
2530 ret = wl_ext_iovar_setbuf(dev, "csi", (char *)&csi, sizeof(csi),
2531 buf, WLC_IOCTL_SMLEN, NULL);
2532 } else if (period == 0) {
2533 memset(&csi, 0, sizeof(csi_config_t));
2534 bcopy(&ea, &csi.addr, ETHER_ADDR_LEN);
2535 ret = wl_ext_iovar_setbuf(dev, "csi_del", (char *)&csi, sizeof(csi),
2536 buf, WLC_IOCTL_SMLEN, NULL);
2537 } else {
2538 ret = wl_ext_iovar_getbuf(dev, "csi", &ea, ETHER_ADDR_LEN, buf,
2539 WLC_IOCTL_SMLEN, NULL);
2540 if (!ret) {
2541 csip = (csi_config_t *) buf;
2542 /* Dump all lists */
2543 bytes_written += snprintf(command+bytes_written, total_len,
2544 "Mac :%pM\n"
2545 "Period :%d\n"
2546 "BW :%d\n"
2547 "Method :%d\n",
2548 &csip->addr, csip->period, csip->bw, csip->method);
2549 AEXT_TRACE(dev->name, "command result is %s\n", command);
2550 ret = bytes_written;
2551 }
2552 }
2553 }
2554 else {
2555 ret = wl_ext_iovar_getbuf(dev, "csi_list", NULL, 0, buf, WLC_IOCTL_SMLEN, NULL);
2556 if (!ret) {
2557 csi_list = (csi_list_t *)buf;
2558 bytes_written += snprintf(command+bytes_written, total_len,
2559 "Total number :%d\n", csi_list->cnt);
2560 for (i=0; i<csi_list->cnt; i++) {
2561 csip = &csi_list->configs[i];
2562 bytes_written += snprintf(command+bytes_written, total_len,
2563 "Idx :%d\n"
2564 "Mac :%pM\n"
2565 "Period :%d\n"
2566 "BW :%d\n"
2567 "Method :%d\n\n",
2568 i+1, &csip->addr, csip->period, csip->bw, csip->method);
2569 }
2570 AEXT_TRACE(dev->name, "command result is %s\n", command);
2571 ret = bytes_written;
2572 }
2573 }
2574
2575 exit:
2576 if (buf)
2577 kfree(buf);
2578 return ret;
2579 }
2580 #endif /* CSI_SUPPORT */
2581
2582 static int
wl_ext_get_country(struct net_device * dev,char * data,char * command,int total_len)2583 wl_ext_get_country(struct net_device *dev, char *data, char *command,
2584 int total_len)
2585 {
2586 struct dhd_pub *dhd = dhd_get_pub(dev);
2587 wl_country_t cspec = {{0}, 0, {0}};
2588 int bytes_written = 0, ret = 0;
2589
2590 if (data) {
2591 char *country_code = data;
2592 char *rev_info_delim = country_code + 2; /* 2 bytes of country code */
2593 int revinfo = 0;
2594 if ((rev_info_delim) &&
2595 (strnicmp(rev_info_delim, "/", strlen("/")) == 0) && (rev_info_delim + 1)) {
2596 revinfo = bcm_atoi(rev_info_delim + 1);
2597 }
2598 #ifdef WL_CFG80211
2599 bytes_written = wl_cfg80211_set_country_code(dev, country_code,
2600 true, true, revinfo);
2601 #else
2602 bytes_written = wldev_set_country(dev, country_code, true, true, revinfo);
2603 #endif /* WL_CFG80211 */
2604 } else {
2605 ret = dhd_conf_get_country(dhd, &cspec);
2606 if (!ret) {
2607 bytes_written += snprintf(command+bytes_written, total_len,
2608 "%s/%d", cspec.ccode, cspec.rev);
2609 }
2610 if (!bytes_written)
2611 bytes_written = -1;
2612 AEXT_TRACE(dev->name, "command result is %s\n", command);
2613 }
2614
2615 return bytes_written;
2616 }
2617
2618 static int
wl_ext_disable_5g_band(struct net_device * dev,char * data,char * command,int total_len)2619 wl_ext_disable_5g_band(struct net_device *dev, char *data, char *command,
2620 int total_len)
2621 {
2622 #ifdef WL_CFG80211
2623 struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
2624 #endif
2625 int ret = -1;
2626 int val;
2627
2628 if (data) {
2629 val = (int)simple_strtol(data, NULL, 0);
2630 ret = wl_ext_iovar_setint(dev, "disable_5g_band", val);
2631 #ifdef WL_CFG80211
2632 if (!ret)
2633 wl_update_wiphybands(cfg, true);
2634 #endif
2635 } else {
2636 ret = wl_ext_iovar_getint(dev, "disable_5g_band", &val);
2637 if (!ret) {
2638 ret = snprintf(command, total_len, "%d", val);
2639 AEXT_TRACE(dev->name, "command result is %s\n", command);
2640 }
2641 }
2642
2643 return ret;
2644 }
2645
2646 typedef int (wl_ext_tpl_parse_t)(struct net_device *dev, char *data, char *command,
2647 int total_len);
2648
2649 typedef struct wl_ext_iovar_tpl_t {
2650 int get;
2651 int set;
2652 char *name;
2653 wl_ext_tpl_parse_t *parse;
2654 } wl_ext_iovar_tpl_t;
2655
2656 const wl_ext_iovar_tpl_t wl_ext_iovar_tpl_list[] = {
2657 {WLC_GET_VAR, WLC_SET_VAR, "event_msg", wl_ext_event_msg},
2658 #if defined(USE_IW)
2659 {WLC_GET_VAR, WLC_SET_VAR, "gtk_key_info", wl_ext_gtk_key_info},
2660 #endif /* USE_IW */
2661 {WLC_GET_VAR, WLC_SET_VAR, "recal", wl_ext_recal},
2662 {WLC_GET_VAR, WLC_SET_VAR, "rsdb_mode", wl_ext_rsdb_mode},
2663 {WLC_GET_VAR, WLC_SET_VAR, "mkeep_alive", wl_ext_mkeep_alive},
2664 #ifdef PKT_FILTER_SUPPORT
2665 {WLC_GET_VAR, WLC_SET_VAR, "pkt_filter_add", wl_ext_pkt_filter_add},
2666 {WLC_GET_VAR, WLC_SET_VAR, "pkt_filter_delete", wl_ext_pkt_filter_delete},
2667 {WLC_GET_VAR, WLC_SET_VAR, "pkt_filter_enable", wl_ext_pkt_filter_enable},
2668 #endif /* PKT_FILTER_SUPPORT */
2669 #if defined(WL_EXT_IAPSTA) && defined(WLMESH)
2670 {WLC_GET_VAR, WLC_SET_VAR, "mesh_peer_status", wl_ext_mesh_peer_status},
2671 #endif /* WL_EXT_IAPSTA && WLMESH */
2672 #ifdef SENDPROB
2673 {WLC_GET_VAR, WLC_SET_VAR, "send_probreq", wl_ext_send_probreq},
2674 {WLC_GET_VAR, WLC_SET_VAR, "send_probresp", wl_ext_send_probresp},
2675 {WLC_GET_VAR, WLC_SET_VAR, "recv_probreq", wl_ext_recv_probreq},
2676 {WLC_GET_VAR, WLC_SET_VAR, "recv_probresp", wl_ext_recv_probresp},
2677 #endif /* SENDPROB */
2678 #ifdef WL_EXT_TCPKA
2679 {WLC_GET_VAR, WLC_SET_VAR, "tcpka_conn_add", wl_ext_tcpka_conn_add},
2680 {WLC_GET_VAR, WLC_SET_VAR, "tcpka_conn_enable", wl_ext_tcpka_conn_enable},
2681 {WLC_GET_VAR, WLC_SET_VAR, "tcpka_conn_sess_info", wl_ext_tcpka_conn_info},
2682 #endif /* WL_EXT_TCPKA */
2683 #ifdef WL_EXT_WOWL
2684 {WLC_GET_VAR, WLC_SET_VAR, "wowl_pattern", wl_ext_wowl_pattern},
2685 {WLC_GET_VAR, WLC_SET_VAR, "wowl_wakeind", wl_ext_wowl_wakeind},
2686 #endif /* WL_EXT_WOWL */
2687 #ifdef IDHCP
2688 {WLC_GET_VAR, WLC_SET_VAR, "dhcpc_dump", wl_ext_dhcpc_dump},
2689 {WLC_GET_VAR, WLC_SET_VAR, "dhcpc_param", wl_ext_dhcpc_param},
2690 #endif /* IDHCP */
2691 #ifdef WL_GPIO_NOTIFY
2692 {WLC_GET_VAR, WLC_SET_VAR, "bcol_gpio_noti", wl_ext_gpio_notify},
2693 #endif /* WL_GPIO_NOTIFY */
2694 #ifdef CSI_SUPPORT
2695 {WLC_GET_VAR, WLC_SET_VAR, "csi", wl_ext_csi},
2696 #endif /* CSI_SUPPORT */
2697 {WLC_GET_VAR, WLC_SET_VAR, "country", wl_ext_get_country},
2698 {WLC_GET_VAR, WLC_SET_VAR, "disable_5g_band", wl_ext_disable_5g_band},
2699 };
2700
2701 /*
2702 Ex: dhd_priv wl [cmd] [val]
2703 dhd_priv wl 85
2704 dhd_priv wl 86 1
2705 dhd_priv wl mpc
2706 dhd_priv wl mpc 1
2707 */
2708 static int
wl_ext_wl_iovar(struct net_device * dev,char * command,int total_len)2709 wl_ext_wl_iovar(struct net_device *dev, char *command, int total_len)
2710 {
2711 int cmd, val, ret = -1, i;
2712 char name[32], *pch, *pick_tmp, *data;
2713 int bytes_written=-1;
2714 const wl_ext_iovar_tpl_t *tpl = wl_ext_iovar_tpl_list;
2715 int tpl_count = ARRAY_SIZE(wl_ext_iovar_tpl_list);
2716 char *pEnd;
2717
2718 AEXT_TRACE(dev->name, "cmd %s\n", command);
2719 pick_tmp = command;
2720
2721 pch = bcmstrtok(&pick_tmp, " ", 0); // pick wl
2722 if (!pch || strncmp(pch, "wl", 2))
2723 goto exit;
2724
2725 pch = bcmstrtok(&pick_tmp, " ", 0); // pick cmd
2726 if (!pch)
2727 goto exit;
2728
2729 memset(name, 0 , sizeof (name));
2730 cmd = bcm_strtoul(pch, &pEnd, 0);
2731 if (cmd == 0 || strlen(pEnd)) {
2732 strcpy(name, pch);
2733 }
2734 data = bcmstrtok(&pick_tmp, "", 0); // pick data
2735 if (data && (cmd == 0|| strlen(pEnd))) {
2736 cmd = WLC_SET_VAR;
2737 } else if (cmd == 0|| strlen(pEnd)) {
2738 cmd = WLC_GET_VAR;
2739 }
2740
2741 /* look for a matching code in the table */
2742 for (i = 0; i < tpl_count; i++, tpl++) {
2743 if ((tpl->get == cmd || tpl->set == cmd) && !strcmp(tpl->name, name))
2744 break;
2745 }
2746 if (i < tpl_count && tpl->parse) {
2747 ret = tpl->parse(dev, data, command, total_len);
2748 } else {
2749 if (cmd == WLC_SET_VAR) {
2750 val = (int)simple_strtol(data, NULL, 0);
2751 AEXT_INFO(dev->name, "set %s %d\n", name, val);
2752 ret = wl_ext_iovar_setint(dev, name, val);
2753 } else if (cmd == WLC_GET_VAR) {
2754 AEXT_INFO(dev->name, "get %s\n", name);
2755 ret = wl_ext_iovar_getint(dev, name, &val);
2756 if (!ret) {
2757 bytes_written = snprintf(command, total_len, "%d", val);
2758 AEXT_INFO(dev->name, "command result is %s\n", command);
2759 ret = bytes_written;
2760 }
2761 } else if (data) {
2762 val = (int)simple_strtol(data, NULL, 0);
2763 AEXT_INFO(dev->name, "set %d %d\n", cmd, val);
2764 ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), TRUE);
2765 } else {
2766 AEXT_INFO(dev->name, "get %d\n", cmd);
2767 ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), FALSE);
2768 if (!ret) {
2769 bytes_written = snprintf(command, total_len, "%d", val);
2770 AEXT_INFO(dev->name, "command result is %s\n", command);
2771 ret = bytes_written;
2772 }
2773 }
2774 }
2775
2776 exit:
2777 return ret;
2778 }
2779
2780 int
wl_ext_conf_iovar(struct net_device * dev,char * command,int total_len)2781 wl_ext_conf_iovar(struct net_device *dev, char *command, int total_len)
2782 {
2783 int ret = 0;
2784 char name[32], *pch, *pick_tmp, *data;
2785 int bytes_written=-1;
2786 struct dhd_pub *dhd = dhd_get_pub(dev);
2787
2788 AEXT_TRACE(dev->name, "cmd %s\n", command);
2789 pick_tmp = command;
2790
2791 pch = bcmstrtok(&pick_tmp, " ", 0); // pick conf
2792 if (!pch || strncmp(pch, "conf", 4))
2793 goto exit;
2794
2795 pch = bcmstrtok(&pick_tmp, " ", 0); // pick cmd
2796 if (!pch)
2797 goto exit;
2798
2799 strncpy(name, pch, sizeof(name));
2800
2801 data = bcmstrtok(&pick_tmp, "", 0); // pick data
2802
2803 if (!strcmp(name, "pm")) {
2804 if (data) {
2805 dhd->conf->pm = simple_strtol(data, NULL, 0);
2806 ret = 0;
2807 } else {
2808 bytes_written = snprintf(command, total_len, "%d", dhd->conf->pm);
2809 ret = bytes_written;
2810 }
2811 } else {
2812 AEXT_ERROR(dev->name, "no config parameter found\n");
2813 }
2814
2815 exit:
2816 return ret;
2817 }
2818
2819 int
wl_android_ext_priv_cmd(struct net_device * net,char * command,int total_len,int * bytes_written)2820 wl_android_ext_priv_cmd(struct net_device *net, char *command,
2821 int total_len, int *bytes_written)
2822 {
2823 int ret = 0;
2824
2825 if (strnicmp(command, CMD_CHANNELS, strlen(CMD_CHANNELS)) == 0) {
2826 *bytes_written = wl_ext_channels(net, command, total_len);
2827 }
2828 else if (strnicmp(command, CMD_CHANNEL, strlen(CMD_CHANNEL)) == 0) {
2829 *bytes_written = wl_ext_channel(net, command, total_len);
2830 }
2831 else if (strnicmp(command, CMD_ROAM_TRIGGER, strlen(CMD_ROAM_TRIGGER)) == 0) {
2832 *bytes_written = wl_ext_roam_trigger(net, command, total_len);
2833 }
2834 else if (strnicmp(command, CMD_PM, strlen(CMD_PM)) == 0) {
2835 *bytes_written = wl_ext_pm(net, command, total_len);
2836 }
2837 else if (strnicmp(command, CMD_MONITOR, strlen(CMD_MONITOR)) == 0) {
2838 *bytes_written = wl_ext_monitor(net, command, total_len);
2839 }
2840 #ifdef BTC_WAR
2841 else if (strnicmp(command, CMD_BTC_WAR, strlen(CMD_BTC_WAR)) == 0) {
2842 *bytes_written = wl_ext_btc_war(net, command, total_len);
2843 }
2844 #endif /* BTC_WAR */
2845 else if (strnicmp(command, CMD_SET_SUSPEND_BCN_LI_DTIM, strlen(CMD_SET_SUSPEND_BCN_LI_DTIM)) == 0) {
2846 int bcn_li_dtim;
2847 bcn_li_dtim = (int)simple_strtol((command + strlen(CMD_SET_SUSPEND_BCN_LI_DTIM) + 1), NULL, 10);
2848 *bytes_written = net_os_set_suspend_bcn_li_dtim(net, bcn_li_dtim);
2849 }
2850 #ifdef WL_EXT_IAPSTA
2851 else if (strnicmp(command, CMD_IAPSTA_INIT, strlen(CMD_IAPSTA_INIT)) == 0 ||
2852 strnicmp(command, CMD_ISAM_INIT, strlen(CMD_ISAM_INIT)) == 0) {
2853 *bytes_written = wl_ext_isam_init(net, command, total_len);
2854 }
2855 else if (strnicmp(command, CMD_IAPSTA_CONFIG, strlen(CMD_IAPSTA_CONFIG)) == 0 ||
2856 strnicmp(command, CMD_ISAM_CONFIG, strlen(CMD_ISAM_CONFIG)) == 0) {
2857 *bytes_written = wl_ext_iapsta_config(net, command, total_len);
2858 }
2859 else if (strnicmp(command, CMD_IAPSTA_ENABLE, strlen(CMD_IAPSTA_ENABLE)) == 0 ||
2860 strnicmp(command, CMD_ISAM_ENABLE, strlen(CMD_ISAM_ENABLE)) == 0) {
2861 *bytes_written = wl_ext_iapsta_enable(net, command, total_len);
2862 }
2863 else if (strnicmp(command, CMD_IAPSTA_DISABLE, strlen(CMD_IAPSTA_DISABLE)) == 0 ||
2864 strnicmp(command, CMD_ISAM_DISABLE, strlen(CMD_ISAM_DISABLE)) == 0) {
2865 *bytes_written = wl_ext_iapsta_disable(net, command, total_len);
2866 }
2867 else if (strnicmp(command, CMD_ISAM_STATUS, strlen(CMD_ISAM_STATUS)) == 0) {
2868 *bytes_written = wl_ext_isam_status(net, command, total_len);
2869 }
2870 else if (strnicmp(command, CMD_ISAM_PARAM, strlen(CMD_ISAM_PARAM)) == 0) {
2871 *bytes_written = wl_ext_isam_param(net, command, total_len);
2872 }
2873 #if defined(WLMESH) && defined(WL_ESCAN)
2874 else if (strnicmp(command, CMD_ISAM_PEER_PATH, strlen(CMD_ISAM_PEER_PATH)) == 0) {
2875 *bytes_written = wl_ext_isam_peer_path(net, command, total_len);
2876 }
2877 #endif /* WLMESH && WL_ESCAN */
2878 #endif /* WL_EXT_IAPSTA */
2879 #ifdef WL_CFG80211
2880 else if (strnicmp(command, CMD_AUTOCHANNEL, strlen(CMD_AUTOCHANNEL)) == 0) {
2881 *bytes_written = wl_cfg80211_autochannel(net, command, total_len);
2882 }
2883 #endif /* WL_CFG80211 */
2884 #if defined(WL_WIRELESS_EXT) && defined(WL_ESCAN)
2885 else if (strnicmp(command, CMD_AUTOCHANNEL, strlen(CMD_AUTOCHANNEL)) == 0) {
2886 *bytes_written = wl_iw_autochannel(net, command, total_len);
2887 }
2888 #endif /* WL_WIRELESS_EXT && WL_ESCAN */
2889 else if (strnicmp(command, CMD_WLMSGLEVEL, strlen(CMD_WLMSGLEVEL)) == 0) {
2890 *bytes_written = wl_ext_wlmsglevel(net, command, total_len);
2891 }
2892 #ifdef WLEASYMESH
2893 else if (strnicmp(command, CMD_EASYMESH, strlen(CMD_EASYMESH)) == 0) {
2894 int skip = strlen(CMD_EASYMESH) + 1;
2895 *bytes_written = wl_ext_easymesh(net, command+skip, total_len);
2896 }
2897 #endif /* WLEASYMESH */
2898 #if defined(PKT_STATICS) && defined(BCMSDIO)
2899 else if (strnicmp(command, CMD_DUMP_PKT_STATICS, strlen(CMD_DUMP_PKT_STATICS)) == 0) {
2900 struct dhd_pub *dhd = dhd_get_pub(net);
2901 dhd_bus_dump_txpktstatics(dhd);
2902 }
2903 else if (strnicmp(command, CMD_CLEAR_PKT_STATICS, strlen(CMD_CLEAR_PKT_STATICS)) == 0) {
2904 struct dhd_pub *dhd = dhd_get_pub(net);
2905 dhd_bus_clear_txpktstatics(dhd);
2906 }
2907 #endif /* PKT_STATICS && BCMSDIO */
2908 else if (strnicmp(command, CMD_WL, strlen(CMD_WL)) == 0) {
2909 *bytes_written = wl_ext_wl_iovar(net, command, total_len);
2910 }
2911 else if (strnicmp(command, CMD_CONF, strlen(CMD_CONF)) == 0) {
2912 *bytes_written = wl_ext_conf_iovar(net, command, total_len);
2913 }
2914 else
2915 ret = -1;
2916
2917 return ret;
2918 }
2919
2920 #define CH_MIN_5G_CHANNEL 34
2921 int
wl_construct_ctl_chanspec_list(struct net_device * dev,wl_uint32_list_t * chan_list)2922 wl_construct_ctl_chanspec_list(struct net_device *dev, wl_uint32_list_t *chan_list)
2923 {
2924 void *list;
2925 u32 i, channel;
2926 chanspec_t chspec = 0;
2927 s32 err = BCME_OK;
2928 bool legacy_chan_info = FALSE;
2929 u16 list_count;
2930
2931 #define LOCAL_BUF_LEN 4096
2932 list = kmalloc(LOCAL_BUF_LEN, GFP_KERNEL);
2933 if (list == NULL) {
2934 WL_ERR(("failed to allocate local buf\n"));
2935 return -ENOMEM;
2936 }
2937
2938 err = wldev_iovar_getbuf(dev, "chan_info_list", NULL,
2939 0, list, LOCAL_BUF_LEN, NULL);
2940 if (err == BCME_UNSUPPORTED) {
2941 err = wl_ext_iovar_getbuf(dev, "chanspecs", NULL,
2942 0, list, LOCAL_BUF_LEN, NULL);
2943 if (err != BCME_OK) {
2944 WL_ERR(("get chanspecs err(%d)\n", err));
2945 kfree(list);
2946 return err;
2947 }
2948 /* Update indicating legacy chan info usage */
2949 legacy_chan_info = TRUE;
2950 } else if (err != BCME_OK) {
2951 WL_ERR(("get chan_info_list err(%d)\n", err));
2952 kfree(list);
2953 return err;
2954 }
2955
2956 list_count = legacy_chan_info ? ((wl_uint32_list_t *)list)->count :
2957 ((wl_chanspec_list_v1_t *)list)->count;
2958 for (i = 0; i < dtoh32(list_count); i++) {
2959 if (legacy_chan_info) {
2960 chspec = (chanspec_t)dtoh32(((wl_uint32_list_t *)list)->element[i]);
2961 } else {
2962 chspec = (chanspec_t)dtoh32
2963 (((wl_chanspec_list_v1_t *)list)->chspecs[i].chanspec);
2964 }
2965 chspec = wl_chspec_driver_to_host(chspec);
2966 channel = wf_chspec_ctlchan(chspec);
2967
2968 if (!CHSPEC_IS20(chspec)) {
2969 continue;
2970 }
2971 if (CHSPEC_IS2G(chspec) && (channel >= CH_MIN_2G_CHANNEL) &&
2972 (channel <= CH_MAX_2G_CHANNEL)) {
2973 chan_list->element[chan_list->count] = chspec;
2974 chan_list->count++;
2975 }
2976 #ifdef WL_6G_BAND
2977 else if (CHSPEC_IS6G(chspec) && (channel >= CH_MIN_6G_CHANNEL) &&
2978 (channel <= CH_MAX_6G_CHANNEL)) {
2979 if (channel == 2)
2980 continue;
2981 chan_list->element[chan_list->count] = chspec;
2982 chan_list->count++;
2983 }
2984 #endif /* WL_6G_BAND */
2985 else if (CHSPEC_IS5G(chspec) && (channel >= CH_MIN_5G_CHANNEL) &&
2986 (channel <= 165)) {
2987 chan_list->element[chan_list->count] = chspec;
2988 chan_list->count++;
2989 } else {
2990 continue;
2991 }
2992 }
2993
2994 kfree(list);
2995 #undef LOCAL_BUF_LEN
2996 return err;
2997 }
2998
2999 #if defined(WL_CFG80211) || defined(WL_ESCAN)
3000 int
wl_ext_get_distance(struct net_device * net,u32 band)3001 wl_ext_get_distance(struct net_device *net, u32 band)
3002 {
3003 u32 bw = WL_CHANSPEC_BW_20;
3004 s32 bw_cap = 0, distance = 0;
3005 struct {
3006 u32 band;
3007 u32 bw_cap;
3008 } param = {0, 0};
3009 char buf[WLC_IOCTL_SMLEN]="\0";
3010 s32 err = BCME_OK;
3011
3012 param.band = band;
3013 err = wldev_iovar_getbuf(net, "bw_cap", ¶m, sizeof(param), buf,
3014 sizeof(buf), NULL);
3015 if (err) {
3016 if (err != BCME_UNSUPPORTED) {
3017 AEXT_TRACE(net->name, "bw_cap failed, %d\n", err);
3018 return err;
3019 } else {
3020 err = wl_ext_iovar_getint(net, "mimo_bw_cap", &bw_cap);
3021 if (bw_cap != WLC_N_BW_20ALL)
3022 bw = WL_CHANSPEC_BW_40;
3023 }
3024 } else {
3025 if (WL_BW_CAP_80MHZ(buf[0]))
3026 bw = WL_CHANSPEC_BW_80;
3027 else if (WL_BW_CAP_40MHZ(buf[0]))
3028 bw = WL_CHANSPEC_BW_40;
3029 else
3030 bw = WL_CHANSPEC_BW_20;
3031 }
3032
3033 if (bw == WL_CHANSPEC_BW_20)
3034 distance = 2;
3035 else if (bw == WL_CHANSPEC_BW_40)
3036 distance = 4;
3037 else if (bw == WL_CHANSPEC_BW_80)
3038 distance = 8;
3039 else
3040 distance = 16;
3041 AEXT_INFO(net->name, "bw=0x%x, distance=%d\n", bw, distance);
3042
3043 return distance;
3044 }
3045
3046 int
wl_ext_get_best_channel(struct net_device * net,wl_bss_cache_ctrl_t * bss_cache_ctrl,int * best_2g_ch,int * best_5g_ch,int * best_6g_ch)3047 wl_ext_get_best_channel(struct net_device *net,
3048 #if defined(BSSCACHE)
3049 wl_bss_cache_ctrl_t *bss_cache_ctrl,
3050 #else
3051 wl_scan_results_v109_t *bss_list,
3052 #endif /* BSSCACHE */
3053 int *best_2g_ch, int *best_5g_ch, int *best_6g_ch)
3054 {
3055 struct dhd_pub *dhd = dhd_get_pub(net);
3056 struct wl_bss_info *bi = NULL; /* must be initialized */
3057 struct wl_chan_info chan_info;
3058 s32 i, j;
3059 #if defined(BSSCACHE)
3060 wl_bss_cache_t *node;
3061 #endif /* BSSCACHE */
3062 int b_band[CH_MAX_2G_CHANNEL]={0}, a_band1[4]={0}, a_band4[5]={0};
3063 #ifdef WL_6G_BAND
3064 int six_g_band5[24]={0}, six_g_band6[5]={0}, six_g_band7[18]={0}, six_g_band8[13]={0};
3065 s32 distance_6g;
3066 #endif /* WL_6G_BAND */
3067 s32 cen_ch, distance, distance_2g, distance_5g, chanspec, min_ap=999;
3068 wl_uint32_list_t *list = NULL;
3069 int ret;
3070 chanspec_t chspec;
3071 u32 channel;
3072
3073 memset(b_band, -1, sizeof(b_band));
3074 memset(a_band1, -1, sizeof(a_band1));
3075 memset(a_band4, -1, sizeof(a_band4));
3076 #ifdef WL_6G_BAND
3077 memset(six_g_band5, -1, sizeof(six_g_band5));
3078 memset(six_g_band6, -1, sizeof(six_g_band6));
3079 memset(six_g_band7, -1, sizeof(six_g_band7));
3080 memset(six_g_band8, -1, sizeof(six_g_band8));
3081 #endif /* WL_6G_BAND */
3082
3083 list = kzalloc(sizeof(u32)*(MAX_CTRL_CHANSPECS + 1), GFP_KERNEL);
3084 if (list == NULL) {
3085 AEXT_ERROR(net->name, "kzalloc failed\n");
3086 ret = -ENOMEM;
3087 goto exit;
3088 }
3089
3090 ret = wl_construct_ctl_chanspec_list(net, list);
3091 if (ret < 0) {
3092 AEXT_ERROR(net->name, "get channels failed with %d\n", ret);
3093 goto exit;
3094 } else {
3095 for (i = 0; i < list->count; i++) {
3096 chspec = list->element[i];
3097 channel = wf_chspec_ctlchan(chspec);
3098 chan_info.band = CHSPEC2WLC_BAND(chspec);
3099 chan_info.chan = channel;
3100 if (wl_ext_passive_chan(net, &chan_info)) {
3101 continue;
3102 }
3103 if (CHSPEC_IS2G(chspec) && (channel >= CH_MIN_2G_CHANNEL) &&
3104 (channel <= CH_MAX_2G_CHANNEL)) {
3105 b_band[channel-1] = 0;
3106 }
3107 #ifdef WL_6G_BAND
3108 else if (CHSPEC_IS6G(chspec) && (channel >= CH_MIN_6G_CHANNEL) &&
3109 (channel <= CH_MAX_6G_CHANNEL)) {
3110 if (channel <= 93)
3111 six_g_band5[(channel-1)/4] = 0;
3112 else if (channel >= 97 && channel <= 109)
3113 six_g_band6[(channel-97)/4] = 0;
3114 else if (channel >= 117 && channel <= 181)
3115 six_g_band7[(channel-117)/4] = 0;
3116 else if (channel >= 189 && channel <= 221)
3117 six_g_band8[(channel-189)/4] = 0;
3118 }
3119 #endif /* WL_6G_BAND */
3120 else if (CHSPEC_IS5G(chspec) && channel >= CH_MIN_5G_CHANNEL) {
3121 if (channel <= 48)
3122 a_band1[(channel-36)/4] = 0;
3123 else if (channel >= 149 && channel <= 161)
3124 a_band4[(channel-149)/4] = 0;
3125 }
3126 }
3127 }
3128
3129 distance_2g = wl_ext_get_distance(net, WLC_BAND_2G);
3130 distance_5g = wl_ext_get_distance(net, WLC_BAND_5G);
3131 #ifdef WL_6G_BAND
3132 distance_6g = wl_ext_get_distance(net, WLC_BAND_6G);
3133 #endif /* WL_6G_BAND */
3134
3135 #if defined(BSSCACHE)
3136 node = bss_cache_ctrl->m_cache_head;
3137 for (i=0; node && i<256; i++)
3138 #else
3139 for (i=0; i < bss_list->count; i++)
3140 #endif /* BSSCACHE */
3141 {
3142 #if defined(BSSCACHE)
3143 bi = node->results.bss_info;
3144 #else
3145 bi = bi ? (wl_bss_info_v109_t *)((uintptr)bi + dtoh32(bi->length)) : bss_list->bss_info;
3146 #endif /* BSSCACHE */
3147 chanspec = wl_ext_chspec_driver_to_host(dhd, bi->chanspec);
3148 cen_ch = CHSPEC_CHANNEL(bi->chanspec);
3149 distance = 0;
3150 if (CHSPEC_IS20(chanspec))
3151 distance += 2;
3152 else if (CHSPEC_IS40(chanspec))
3153 distance += 4;
3154 else if (CHSPEC_IS80(chanspec))
3155 distance += 8;
3156 else
3157 distance += 16;
3158
3159 if (CHSPEC_IS2G(chanspec)) {
3160 distance += distance_2g;
3161 for (j=0; j<ARRAYSIZE(b_band); j++) {
3162 if (b_band[j] >= 0 && abs(cen_ch-(1+j)) <= distance)
3163 b_band[j] += 1;
3164 }
3165 }
3166 #ifdef WL_6G_BAND
3167 else if (CHSPEC_IS6G(chanspec)) {
3168 distance += distance_6g;
3169 if (cen_ch <= 93) {
3170 for (j=0; j<ARRAYSIZE(six_g_band5); j++) {
3171 if (six_g_band5[j] >= 0 && abs(cen_ch-(93+j*4)) <= distance)
3172 six_g_band5[j] += 1;
3173 }
3174 }
3175 else if (channel >= 97 && channel <= 109) {
3176 for (j=0; j<ARRAYSIZE(six_g_band6); j++) {
3177 if (six_g_band6[j] >= 0 && abs(cen_ch-(97+j*4)) <= distance)
3178 six_g_band6[j] += 1;
3179 }
3180 }
3181 else if (channel >= 117 && channel <= 181) {
3182 for (j=0; j<ARRAYSIZE(six_g_band7); j++) {
3183 if (six_g_band7[j] >= 0 && abs(cen_ch-(117+j*4)) <= distance)
3184 six_g_band7[j] += 1;
3185 }
3186 }
3187 else if (channel >= 189 && channel <= 221) {
3188 for (j=0; j<ARRAYSIZE(six_g_band8); j++) {
3189 if (six_g_band8[j] >= 0 && abs(cen_ch-(189+j*4)) <= distance)
3190 six_g_band8[j] += 1;
3191 }
3192 }
3193 }
3194 #endif /* WL_6G_BAND */
3195 else {
3196 distance += distance_5g;
3197 if (cen_ch <= 48) {
3198 for (j=0; j<ARRAYSIZE(a_band1); j++) {
3199 if (a_band1[j] >= 0 && abs(cen_ch-(36+j*4)) <= distance)
3200 a_band1[j] += 1;
3201 }
3202 }
3203 else if (cen_ch >= 149) {
3204 for (j=0; j<ARRAYSIZE(a_band4); j++) {
3205 if (a_band4[j] >= 0 && abs(cen_ch-(149+j*4)) <= distance)
3206 a_band4[j] += 1;
3207 }
3208 }
3209 }
3210 #if defined(BSSCACHE)
3211 node = node->next;
3212 #endif /* BSSCACHE */
3213 }
3214
3215 *best_2g_ch = 0;
3216 min_ap = 999;
3217 for (i=0; i<CH_MAX_2G_CHANNEL; i++) {
3218 if(b_band[i] < min_ap && b_band[i] >= 0) {
3219 min_ap = b_band[i];
3220 *best_2g_ch = i+1;
3221 }
3222 }
3223 *best_5g_ch = 0;
3224 min_ap = 999;
3225 for (i=0; i<ARRAYSIZE(a_band1); i++) {
3226 if(a_band1[i] < min_ap && a_band1[i] >= 0) {
3227 min_ap = a_band1[i];
3228 *best_5g_ch = i*4 + 36;
3229 }
3230 }
3231 for (i=0; i<ARRAYSIZE(a_band4); i++) {
3232 if(a_band4[i] < min_ap && a_band4[i] >= 0) {
3233 min_ap = a_band4[i];
3234 *best_5g_ch = i*4 + 149;
3235 }
3236 }
3237 #ifdef WL_6G_BAND
3238 *best_6g_ch = 0;
3239 min_ap = 999;
3240 for (i=0; i<ARRAYSIZE(six_g_band5); i++) {
3241 if(six_g_band5[i] < min_ap && six_g_band5[i] >= 0) {
3242 min_ap = six_g_band5[i];
3243 *best_6g_ch = i*4 + 1;
3244 }
3245 }
3246 for (i=0; i<ARRAYSIZE(six_g_band6); i++) {
3247 if(six_g_band6[i] < min_ap && six_g_band6[i] >= 0) {
3248 min_ap = six_g_band6[i];
3249 *best_6g_ch = i*4 + 97;
3250 }
3251 }
3252 for (i=0; i<ARRAYSIZE(six_g_band7); i++) {
3253 if(six_g_band7[i] < min_ap && six_g_band7[i] >= 0) {
3254 min_ap = six_g_band7[i];
3255 *best_6g_ch = i*4 + 117;
3256 }
3257 }
3258 for (i=0; i<ARRAYSIZE(six_g_band8); i++) {
3259 if(six_g_band8[i] < min_ap && six_g_band8[i] >= 0) {
3260 min_ap = six_g_band8[i];
3261 *best_6g_ch = i*4 + 189;
3262 }
3263 }
3264 #endif /* WL_6G_BAND */
3265
3266 if (android_msg_level & ANDROID_INFO_LEVEL) {
3267 struct bcmstrbuf strbuf;
3268 char *tmp_buf = NULL;
3269 tmp_buf = kmalloc(WLC_IOCTL_MEDLEN, GFP_KERNEL);
3270 if (tmp_buf == NULL) {
3271 AEXT_ERROR(net->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
3272 goto exit;
3273 }
3274 bcm_binit(&strbuf, tmp_buf, WLC_IOCTL_MEDLEN);
3275 bcm_bprintf(&strbuf, "2g: ");
3276 for (j=0; j<ARRAYSIZE(b_band); j++)
3277 bcm_bprintf(&strbuf, "%d/%d, ", b_band[j], 1+j);
3278 bcm_bprintf(&strbuf, "\n");
3279 bcm_bprintf(&strbuf, "5g band 1: ");
3280 for (j=0; j<ARRAYSIZE(a_band1); j++)
3281 bcm_bprintf(&strbuf, "%d/%d, ", a_band1[j], 36+j*4);
3282 bcm_bprintf(&strbuf, "\n");
3283 bcm_bprintf(&strbuf, "5g band 4: ");
3284 for (j=0; j<ARRAYSIZE(a_band4); j++)
3285 bcm_bprintf(&strbuf, "%d/%d, ", a_band4[j], 149+j*4);
3286 bcm_bprintf(&strbuf, "\n");
3287 #ifdef WL_6G_BAND
3288 bcm_bprintf(&strbuf, "6g band 5: ");
3289 for (j=0; j<ARRAYSIZE(six_g_band5); j++)
3290 bcm_bprintf(&strbuf, "%d/%d, ", six_g_band5[j], 1+j*4);
3291 bcm_bprintf(&strbuf, "\n");
3292 bcm_bprintf(&strbuf, "6g band 6: ");
3293 for (j=0; j<ARRAYSIZE(six_g_band6); j++)
3294 bcm_bprintf(&strbuf, "%d/%d, ", six_g_band6[j], 97+j*4);
3295 bcm_bprintf(&strbuf, "\n");
3296 bcm_bprintf(&strbuf, "6g band 7: ");
3297 for (j=0; j<ARRAYSIZE(six_g_band7); j++)
3298 bcm_bprintf(&strbuf, "%d/%d, ", six_g_band7[j], 117+j*4);
3299 bcm_bprintf(&strbuf, "\n");
3300 bcm_bprintf(&strbuf, "6g band 8: ");
3301 for (j=0; j<ARRAYSIZE(six_g_band8); j++)
3302 bcm_bprintf(&strbuf, "%d/%d, ", six_g_band8[j], 189+j*4);
3303 bcm_bprintf(&strbuf, "\n");
3304 #endif /* WL_6G_BAND */
3305 bcm_bprintf(&strbuf, "best_2g_ch=%d, best_5g_ch=%d",
3306 *best_2g_ch, *best_5g_ch);
3307 #ifdef WL_6G_BAND
3308 bcm_bprintf(&strbuf, ", best_6g_ch=%d", *best_6g_ch);
3309 #endif /* WL_6G_BAND */
3310 bcm_bprintf(&strbuf, "\n");
3311 AEXT_INFO(net->name, "\n%s", strbuf.origbuf);
3312 if (tmp_buf) {
3313 kfree(tmp_buf);
3314 }
3315 }
3316
3317 exit:
3318 if (list)
3319 kfree(list);
3320 return ret;
3321 }
3322 #endif /* WL_CFG80211 || WL_ESCAN */
3323
3324 #ifdef WL_CFG80211
3325 #define APCS_MAX_RETRY 10
3326 static int
wl_ext_fw_apcs(struct net_device * dev,uint32 band)3327 wl_ext_fw_apcs(struct net_device *dev, uint32 band)
3328 {
3329 int channel = 0, chosen = 0, retry = 0, ret = 0, spect = 0;
3330 u8 *reqbuf = NULL;
3331 uint32 buf_size;
3332 chanspec_band_t acs_band = WLC_BAND_INVALID;
3333
3334 ret = wldev_ioctl_get(dev, WLC_GET_SPECT_MANAGMENT, &spect, sizeof(spect));
3335 if (ret) {
3336 AEXT_ERROR(dev->name, "ACS: error getting the spect, ret=%d\n", ret);
3337 goto done;
3338 }
3339
3340 if (spect > 0) {
3341 ret = wl_android_set_spect(dev, 0);
3342 if (ret < 0) {
3343 AEXT_ERROR(dev->name, "ACS: error while setting spect, ret=%d\n", ret);
3344 goto done;
3345 }
3346 }
3347
3348 reqbuf = kmalloc(CHANSPEC_BUF_SIZE, GFP_KERNEL);
3349 if (reqbuf == NULL) {
3350 AEXT_ERROR(dev->name, "failed to allocate chanspec buffer\n");
3351 goto done;
3352 }
3353 memset(reqbuf, 0, CHANSPEC_BUF_SIZE);
3354
3355 acs_band = wl_ext_wlcband_to_chanspec_band(band);
3356 if (acs_band == INVCHANSPEC) {
3357 acs_band = WL_CHANSPEC_BAND_2G;
3358 }
3359
3360 if ((ret = wl_android_get_band_chanspecs(dev, reqbuf, CHANSPEC_BUF_SIZE,
3361 acs_band, true)) < 0) {
3362 WL_ERR(("ACS chanspec retrieval failed!\n"));
3363 goto done;
3364 }
3365
3366 AEXT_INFO(dev->name, "ACS chanspec band 0x%x\n", acs_band);
3367
3368 buf_size = CHANSPEC_BUF_SIZE;
3369 ret = wldev_ioctl_set(dev, WLC_START_CHANNEL_SEL, (void *)reqbuf,
3370 buf_size);
3371 if (ret < 0) {
3372 AEXT_ERROR(dev->name, "can't start auto channel scan, err = %d\n", ret);
3373 channel = 0;
3374 goto done;
3375 }
3376
3377 /* Wait for auto channel selection, max 3000 ms */
3378 if ((band == WLC_BAND_2G) || (band == WLC_BAND_5G) || (band == WLC_BAND_6G)) {
3379 OSL_SLEEP(500);
3380 } else {
3381 /*
3382 * Full channel scan at the minimum takes 1.2secs
3383 * even with parallel scan. max wait time: 3500ms
3384 */
3385 OSL_SLEEP(1000);
3386 }
3387
3388 retry = APCS_MAX_RETRY;
3389 while (retry--) {
3390 ret = wldev_ioctl_get(dev, WLC_GET_CHANNEL_SEL, &chosen,
3391 sizeof(chosen));
3392 if (ret < 0) {
3393 chosen = 0;
3394 } else {
3395 chosen = dtoh32(chosen);
3396 }
3397
3398 if (wf_chspec_valid((chanspec_t)chosen)) {
3399 channel = wf_chspec_ctlchan((chanspec_t)chosen);
3400 acs_band = CHSPEC_BAND((chanspec_t)chosen);
3401 WL_MSG(dev->name, "selected channel = %d(band %d)\n",
3402 channel, CHSPEC2WLC_BAND((chanspec_t)chosen));
3403 break;
3404 }
3405 AEXT_INFO(dev->name, "%d tried, ret = %d, chosen = 0x%x\n",
3406 (APCS_MAX_RETRY - retry), ret, chosen);
3407 OSL_SLEEP(250);
3408 }
3409
3410 done:
3411 if (spect > 0) {
3412 if ((ret = wl_android_set_spect(dev, spect) < 0)) {
3413 AEXT_ERROR(dev->name, "ACS: error while setting spect\n");
3414 }
3415 }
3416
3417 if (reqbuf) {
3418 kfree(reqbuf);
3419 }
3420
3421 return chosen;
3422 }
3423 #endif /* WL_CFG80211 */
3424
3425 #ifdef WL_ESCAN
3426 int
wl_ext_drv_scan(struct net_device * dev,uint32 band,bool fast_scan)3427 wl_ext_drv_scan(struct net_device *dev, uint32 band, bool fast_scan)
3428 {
3429 int ret = -1, i, cnt = 0;
3430 int retry = 0, retry_max, retry_interval = 250, up = 1;
3431 wl_scan_info_t *scan_info = NULL;
3432
3433 scan_info = kmalloc(sizeof(wl_scan_info_t), GFP_KERNEL);
3434 if (scan_info == NULL) {
3435 AEXT_ERROR(dev->name, "kzalloc failed\n");
3436 ret = -ENOMEM;
3437 goto exit;
3438 }
3439
3440 retry_max = WL_ESCAN_TIMER_INTERVAL_MS/retry_interval;
3441 ret = wldev_ioctl_get(dev, WLC_GET_UP, &up, sizeof(s32));
3442 if (ret < 0 || up == 0) {
3443 ret = wldev_ioctl_set(dev, WLC_UP, &up, sizeof(s32));
3444 }
3445 memset(scan_info, 0, sizeof(wl_scan_info_t));
3446 if (band == WLC_BAND_2G || band == WLC_BAND_AUTO) {
3447 for (i=0; i<13; i++) {
3448 scan_info->channels.channel[i+cnt] = wf_create_chspec_from_primary(i+1,
3449 WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_2G);
3450 }
3451 cnt += 13;
3452 }
3453 if (band == WLC_BAND_5G || band == WLC_BAND_AUTO) {
3454 for (i=0; i<4; i++) {
3455 scan_info->channels.channel[i+cnt] = wf_create_chspec_from_primary(36+i*4,
3456 WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_5G);
3457 }
3458 cnt += 4;
3459 for (i=0; i<4; i++) {
3460 scan_info->channels.channel[i+cnt] = wf_create_chspec_from_primary(149+i*4,
3461 WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_5G);
3462 }
3463 cnt += 4;
3464 }
3465 #ifdef WL_6G_BAND
3466 if (band == WLC_BAND_6G || band == WLC_BAND_AUTO) {
3467 for (i=0; i<59; i++) {
3468 scan_info->channels.channel[i+cnt] = wf_create_chspec_from_primary(1+i*4,
3469 WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_6G);
3470 }
3471 cnt += 59;
3472 }
3473 #endif /* WL_6G_BAND */
3474 if (band == WLC_BAND_2G)
3475 fast_scan = FALSE;
3476 scan_info->channels.count = cnt;
3477 if (fast_scan)
3478 scan_info->scan_time = 40;
3479 scan_info->bcast_ssid = TRUE;
3480 retry = retry_max;
3481 while (retry--) {
3482 ret = wl_escan_set_scan(dev, scan_info);
3483 if (!ret)
3484 break;
3485 OSL_SLEEP(retry_interval);
3486 }
3487 if (retry == 0) {
3488 AEXT_ERROR(dev->name, "scan retry failed %d\n", retry_max);
3489 ret = -1;
3490 }
3491
3492 exit:
3493 if (scan_info)
3494 kfree(scan_info);
3495 return ret;
3496 }
3497
3498 static int
wl_ext_drv_apcs(struct net_device * dev,uint32 band)3499 wl_ext_drv_apcs(struct net_device *dev, uint32 band)
3500 {
3501 int ret = 0, chanspec = 0;
3502 struct dhd_pub *dhd = dhd_get_pub(dev);
3503 struct wl_escan_info *escan = NULL;
3504 int retry = 0, retry_max, retry_interval = 250;
3505
3506 escan = dhd->escan;
3507 WL_MSG(dev->name, "ACS_SCAN\n");
3508 escan->autochannel = 1;
3509 ret = wl_ext_drv_scan(dev, band, TRUE);
3510 if (ret < 0)
3511 goto done;
3512 retry_max = WL_ESCAN_TIMER_INTERVAL_MS/retry_interval;
3513 retry = retry_max;
3514 while (retry--) {
3515 if (escan->escan_state == ESCAN_STATE_IDLE) {
3516 if (band == WLC_BAND_5G) {
3517 chanspec = wf_create_chspec_from_primary(wf_chspec_primary20_chan(escan->best_5g_ch),
3518 WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_5G);
3519 }
3520 #ifdef WL_6G_BAND
3521 else if (band == WLC_BAND_6G) {
3522 chanspec = wf_create_chspec_from_primary(wf_chspec_primary20_chan(escan->best_6g_ch),
3523 WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_6G);
3524 }
3525 #endif /* WL_6G_BAND */
3526 else {
3527 chanspec = wf_create_chspec_from_primary(wf_chspec_primary20_chan(escan->best_2g_ch),
3528 WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_2G);
3529 }
3530 WL_MSG(dev->name, "selected channel = %d(0x%x)\n",
3531 wf_chspec_ctlchan(chanspec), chanspec);
3532 goto done;
3533 }
3534 AEXT_INFO(dev->name, "escan_state=%d, %d tried, ret = %d\n",
3535 escan->escan_state, (retry_max - retry), ret);
3536 OSL_SLEEP(retry_interval);
3537 }
3538
3539 done:
3540 escan->autochannel = 0;
3541
3542 return chanspec;
3543 }
3544 #endif /* WL_ESCAN */
3545
3546 int
wl_ext_autochannel(struct net_device * dev,uint acs,uint32 band)3547 wl_ext_autochannel(struct net_device *dev, uint acs, uint32 band)
3548 {
3549 int chosen = 0;
3550 uint16 chan_2g, chan_5g, channel;
3551
3552 AEXT_INFO(dev->name, "acs=0x%x, band=%s\n", acs, WLCBAND2STR(band));
3553
3554 #ifdef WL_CFG80211
3555 if (acs & ACS_FW_BIT) {
3556 int ret = 0;
3557 ret = wldev_ioctl_get(dev, WLC_GET_CHANNEL_SEL, &channel, sizeof(channel));
3558 chosen = 0;
3559 if (ret != BCME_UNSUPPORTED)
3560 chosen = wl_ext_fw_apcs(dev, band);
3561 if (chosen)
3562 return chosen;
3563 }
3564 #endif
3565
3566 #ifdef WL_ESCAN
3567 if (acs & ACS_DRV_BIT)
3568 chosen = wl_ext_drv_apcs(dev, band);
3569 #endif /* WL_ESCAN */
3570
3571 if (chosen == 0) {
3572 wl_ext_get_default_chan(dev, &chan_2g, &chan_5g, TRUE);
3573 if (band == WLC_BAND_5G) {
3574 chosen = wf_create_chspec_from_primary(wf_chspec_primary20_chan(chan_5g),
3575 WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_5G);
3576 channel = chan_5g;
3577 } else {
3578 chosen = wf_create_chspec_from_primary(wf_chspec_primary20_chan(chan_2g),
3579 WL_CHANSPEC_BW_20, WL_CHANSPEC_BAND_2G);
3580 channel = chan_2g;
3581 }
3582 AEXT_ERROR(dev->name, "ACS failed. Fall back to default channel (%s-%d) \n",
3583 CHSPEC2BANDSTR(chosen), channel);
3584 }
3585
3586 return chosen;
3587 }
3588
3589 #if defined(RSSIAVG)
3590 void
wl_free_rssi_cache(wl_rssi_cache_ctrl_t * rssi_cache_ctrl)3591 wl_free_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
3592 {
3593 wl_rssi_cache_t *node, *cur, **rssi_head;
3594 int i=0;
3595
3596 rssi_head = &rssi_cache_ctrl->m_cache_head;
3597 node = *rssi_head;
3598
3599 for (;node;) {
3600 AEXT_INFO("wlan", "Free %d with BSSID %pM\n", i, &node->BSSID);
3601 cur = node;
3602 node = cur->next;
3603 kfree(cur);
3604 i++;
3605 }
3606 *rssi_head = NULL;
3607 }
3608
3609 void
wl_delete_dirty_rssi_cache(wl_rssi_cache_ctrl_t * rssi_cache_ctrl)3610 wl_delete_dirty_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
3611 {
3612 wl_rssi_cache_t *node, *prev, **rssi_head;
3613 int i = -1, tmp = 0;
3614 struct osl_timespec now;
3615
3616 osl_do_gettimeofday(&now);
3617
3618 rssi_head = &rssi_cache_ctrl->m_cache_head;
3619 node = *rssi_head;
3620 prev = node;
3621 for (;node;) {
3622 i++;
3623 if (now.tv_sec > node->tv.tv_sec) {
3624 if (node == *rssi_head) {
3625 tmp = 1;
3626 *rssi_head = node->next;
3627 } else {
3628 tmp = 0;
3629 prev->next = node->next;
3630 }
3631 AEXT_INFO("wlan", "Del %d with BSSID %pM\n", i, &node->BSSID);
3632 kfree(node);
3633 if (tmp == 1) {
3634 node = *rssi_head;
3635 prev = node;
3636 } else {
3637 node = prev->next;
3638 }
3639 continue;
3640 }
3641 prev = node;
3642 node = node->next;
3643 }
3644 }
3645
3646 void
wl_delete_disconnected_rssi_cache(wl_rssi_cache_ctrl_t * rssi_cache_ctrl,u8 * bssid)3647 wl_delete_disconnected_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
3648 u8 *bssid)
3649 {
3650 wl_rssi_cache_t *node, *prev, **rssi_head;
3651 int i = -1, tmp = 0;
3652
3653 rssi_head = &rssi_cache_ctrl->m_cache_head;
3654 node = *rssi_head;
3655 prev = node;
3656 for (;node;) {
3657 i++;
3658 if (!memcmp(&node->BSSID, bssid, ETHER_ADDR_LEN)) {
3659 if (node == *rssi_head) {
3660 tmp = 1;
3661 *rssi_head = node->next;
3662 } else {
3663 tmp = 0;
3664 prev->next = node->next;
3665 }
3666 AEXT_INFO("wlan", "Del %d with BSSID %pM\n", i, &node->BSSID);
3667 kfree(node);
3668 if (tmp == 1) {
3669 node = *rssi_head;
3670 prev = node;
3671 } else {
3672 node = prev->next;
3673 }
3674 continue;
3675 }
3676 prev = node;
3677 node = node->next;
3678 }
3679 }
3680
3681 void
wl_reset_rssi_cache(wl_rssi_cache_ctrl_t * rssi_cache_ctrl)3682 wl_reset_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
3683 {
3684 wl_rssi_cache_t *node, **rssi_head;
3685
3686 rssi_head = &rssi_cache_ctrl->m_cache_head;
3687
3688 /* reset dirty */
3689 node = *rssi_head;
3690 for (;node;) {
3691 node->dirty += 1;
3692 node = node->next;
3693 }
3694 }
3695
3696 int
wl_update_connected_rssi_cache(struct net_device * net,wl_rssi_cache_ctrl_t * rssi_cache_ctrl,int * rssi_avg)3697 wl_update_connected_rssi_cache(struct net_device *net,
3698 wl_rssi_cache_ctrl_t *rssi_cache_ctrl, int *rssi_avg)
3699 {
3700 wl_rssi_cache_t *node, *prev, *leaf, **rssi_head;
3701 int j, k=0;
3702 int rssi, error=0;
3703 struct ether_addr bssid;
3704 struct osl_timespec now, timeout;
3705 scb_val_t scbval;
3706
3707 if (!g_wifi_on)
3708 return 0;
3709
3710 error = wldev_ioctl(net, WLC_GET_BSSID, &bssid, sizeof(bssid), 0);
3711 if (error == BCME_NOTASSOCIATED) {
3712 AEXT_INFO("wlan", "Not Associated! res:%d\n", error);
3713 return 0;
3714 }
3715 if (error) {
3716 AEXT_ERROR(net->name, "Could not get bssid (%d)\n", error);
3717 }
3718 error = wldev_get_rssi(net, &scbval);
3719 if (error) {
3720 AEXT_ERROR(net->name, "Could not get rssi (%d)\n", error);
3721 return error;
3722 }
3723 rssi = dtoh32(scbval.val);
3724
3725 osl_do_gettimeofday(&now);
3726 timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT;
3727 if (timeout.tv_sec < now.tv_sec) {
3728 /*
3729 * Integer overflow - assume long enough timeout to be assumed
3730 * to be infinite, i.e., the timeout would never happen.
3731 */
3732 AEXT_TRACE(net->name,
3733 "Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu\n",
3734 RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec);
3735 }
3736
3737 /* update RSSI */
3738 rssi_head = &rssi_cache_ctrl->m_cache_head;
3739 node = *rssi_head;
3740 prev = NULL;
3741 for (;node;) {
3742 if (!memcmp(&node->BSSID, &bssid, ETHER_ADDR_LEN)) {
3743 AEXT_INFO("wlan", "Update %d with BSSID %pM, RSSI=%d\n", k, &bssid, rssi);
3744 for (j=0; j<RSSIAVG_LEN-1; j++)
3745 node->RSSI[j] = node->RSSI[j+1];
3746 node->RSSI[j] = rssi;
3747 node->dirty = 0;
3748 node->tv = timeout;
3749 goto exit;
3750 }
3751 prev = node;
3752 node = node->next;
3753 k++;
3754 }
3755
3756 leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL);
3757 if (!leaf) {
3758 AEXT_ERROR(net->name, "Memory alloc failure %d\n", (int)sizeof(wl_rssi_cache_t));
3759 return 0;
3760 }
3761 AEXT_INFO(net->name, "Add %d with cached BSSID %pM, RSSI=%3d in the leaf\n",
3762 k, &bssid, rssi);
3763
3764 leaf->next = NULL;
3765 leaf->dirty = 0;
3766 leaf->tv = timeout;
3767 memcpy(&leaf->BSSID, &bssid, ETHER_ADDR_LEN);
3768 for (j=0; j<RSSIAVG_LEN; j++)
3769 leaf->RSSI[j] = rssi;
3770
3771 if (!prev)
3772 *rssi_head = leaf;
3773 else
3774 prev->next = leaf;
3775
3776 exit:
3777 *rssi_avg = (int)wl_get_avg_rssi(rssi_cache_ctrl, &bssid);
3778
3779 return error;
3780 }
3781
3782 void
wl_update_rssi_cache(wl_rssi_cache_ctrl_t * rssi_cache_ctrl,wl_scan_results_v109_t * ss_list)3783 wl_update_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
3784 wl_scan_results_v109_t *ss_list)
3785 {
3786 wl_rssi_cache_t *node, *prev, *leaf, **rssi_head;
3787 wl_bss_info_v109_t *bi = NULL;
3788 int i, j, k;
3789 struct osl_timespec now, timeout;
3790
3791 if (!ss_list->count)
3792 return;
3793
3794 osl_do_gettimeofday(&now);
3795 timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT;
3796 if (timeout.tv_sec < now.tv_sec) {
3797 /*
3798 * Integer overflow - assume long enough timeout to be assumed
3799 * to be infinite, i.e., the timeout would never happen.
3800 */
3801 AEXT_TRACE("wlan",
3802 "Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu\n",
3803 RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec);
3804 }
3805
3806 rssi_head = &rssi_cache_ctrl->m_cache_head;
3807
3808 /* update RSSI */
3809 for (i = 0; i < ss_list->count; i++) {
3810 node = *rssi_head;
3811 prev = NULL;
3812 k = 0;
3813 bi = bi ? (wl_bss_info_v109_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info;
3814 for (;node;) {
3815 if (!memcmp(&node->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) {
3816 AEXT_INFO("wlan", "Update %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
3817 k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
3818 for (j=0; j<RSSIAVG_LEN-1; j++)
3819 node->RSSI[j] = node->RSSI[j+1];
3820 node->RSSI[j] = dtoh16(bi->RSSI);
3821 node->dirty = 0;
3822 node->tv = timeout;
3823 break;
3824 }
3825 prev = node;
3826 node = node->next;
3827 k++;
3828 }
3829
3830 if (node)
3831 continue;
3832
3833 leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL);
3834 if (!leaf) {
3835 AEXT_ERROR("wlan", "Memory alloc failure %d\n",
3836 (int)sizeof(wl_rssi_cache_t));
3837 return;
3838 }
3839 AEXT_INFO("wlan", "Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\" in the leaf\n",
3840 k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
3841
3842 leaf->next = NULL;
3843 leaf->dirty = 0;
3844 leaf->tv = timeout;
3845 memcpy(&leaf->BSSID, &bi->BSSID, ETHER_ADDR_LEN);
3846 for (j=0; j<RSSIAVG_LEN; j++)
3847 leaf->RSSI[j] = dtoh16(bi->RSSI);
3848
3849 if (!prev)
3850 *rssi_head = leaf;
3851 else
3852 prev->next = leaf;
3853 }
3854 }
3855
3856 int16
wl_get_avg_rssi(wl_rssi_cache_ctrl_t * rssi_cache_ctrl,void * addr)3857 wl_get_avg_rssi(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, void *addr)
3858 {
3859 wl_rssi_cache_t *node, **rssi_head;
3860 int j, rssi_sum, rssi=RSSI_MINVAL;
3861
3862 rssi_head = &rssi_cache_ctrl->m_cache_head;
3863
3864 node = *rssi_head;
3865 for (;node;) {
3866 if (!memcmp(&node->BSSID, addr, ETHER_ADDR_LEN)) {
3867 rssi_sum = 0;
3868 rssi = 0;
3869 for (j=0; j<RSSIAVG_LEN; j++)
3870 rssi_sum += node->RSSI[RSSIAVG_LEN-j-1];
3871 rssi = rssi_sum / j;
3872 break;
3873 }
3874 node = node->next;
3875 }
3876 rssi = MIN(rssi, RSSI_MAXVAL);
3877 if (rssi == RSSI_MINVAL) {
3878 AEXT_ERROR("wlan", "BSSID %pM does not in RSSI cache\n", addr);
3879 }
3880 return (int16)rssi;
3881 }
3882 #endif /* RSSIAVG */
3883
3884 #if defined(RSSIOFFSET)
3885 int
wl_update_rssi_offset(struct net_device * net,int rssi)3886 wl_update_rssi_offset(struct net_device *net, int rssi)
3887 {
3888 #if defined(RSSIOFFSET_NEW)
3889 int j;
3890 #endif /* RSSIOFFSET_NEW */
3891
3892 if (!g_wifi_on)
3893 return rssi;
3894
3895 #if defined(RSSIOFFSET_NEW)
3896 for (j=0; j<RSSI_OFFSET; j++) {
3897 if (rssi - (RSSI_OFFSET_MINVAL+RSSI_OFFSET_INTVAL*(j+1)) < 0)
3898 break;
3899 }
3900 rssi += j;
3901 #else
3902 rssi += RSSI_OFFSET;
3903 #endif /* RSSIOFFSET_NEW */
3904 return MIN(rssi, RSSI_MAXVAL);
3905 }
3906 #endif /* RSSIOFFSET */
3907
3908 #if defined(BSSCACHE)
3909 void
wl_free_bss_cache(wl_bss_cache_ctrl_t * bss_cache_ctrl)3910 wl_free_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)
3911 {
3912 wl_bss_cache_t *node, *cur, **bss_head;
3913 int i=0;
3914
3915 AEXT_TRACE("wlan", "called\n");
3916
3917 bss_head = &bss_cache_ctrl->m_cache_head;
3918 node = *bss_head;
3919
3920 for (;node;) {
3921 AEXT_TRACE("wlan", "Free %d with BSSID %pM\n",
3922 i, &node->results.bss_info->BSSID);
3923 cur = node;
3924 node = cur->next;
3925 kfree(cur);
3926 i++;
3927 }
3928 *bss_head = NULL;
3929 }
3930
3931 void
wl_delete_dirty_bss_cache(wl_bss_cache_ctrl_t * bss_cache_ctrl)3932 wl_delete_dirty_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)
3933 {
3934 wl_bss_cache_t *node, *prev, **bss_head;
3935 int i = -1, tmp = 0;
3936 struct osl_timespec now;
3937
3938 osl_do_gettimeofday(&now);
3939
3940 bss_head = &bss_cache_ctrl->m_cache_head;
3941 node = *bss_head;
3942 prev = node;
3943 for (;node;) {
3944 i++;
3945 if (now.tv_sec > node->tv.tv_sec || node->dirty > BSSCACHE_DIRTY) {
3946 if (node == *bss_head) {
3947 tmp = 1;
3948 *bss_head = node->next;
3949 } else {
3950 tmp = 0;
3951 prev->next = node->next;
3952 }
3953 AEXT_TRACE("wlan", "Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
3954 i, &node->results.bss_info->BSSID,
3955 dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID);
3956 kfree(node);
3957 if (tmp == 1) {
3958 node = *bss_head;
3959 prev = node;
3960 } else {
3961 node = prev->next;
3962 }
3963 continue;
3964 }
3965 prev = node;
3966 node = node->next;
3967 }
3968 }
3969
3970 void
wl_delete_disconnected_bss_cache(wl_bss_cache_ctrl_t * bss_cache_ctrl,u8 * bssid)3971 wl_delete_disconnected_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl,
3972 u8 *bssid)
3973 {
3974 wl_bss_cache_t *node, *prev, **bss_head;
3975 int i = -1, tmp = 0;
3976
3977 bss_head = &bss_cache_ctrl->m_cache_head;
3978 node = *bss_head;
3979 prev = node;
3980 for (;node;) {
3981 i++;
3982 if (!memcmp(&node->results.bss_info->BSSID, bssid, ETHER_ADDR_LEN)) {
3983 if (node == *bss_head) {
3984 tmp = 1;
3985 *bss_head = node->next;
3986 } else {
3987 tmp = 0;
3988 prev->next = node->next;
3989 }
3990 AEXT_TRACE("wlan", "Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
3991 i, &node->results.bss_info->BSSID,
3992 dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID);
3993 kfree(node);
3994 if (tmp == 1) {
3995 node = *bss_head;
3996 prev = node;
3997 } else {
3998 node = prev->next;
3999 }
4000 continue;
4001 }
4002 prev = node;
4003 node = node->next;
4004 }
4005 }
4006
4007 int
wl_bss_cache_size(wl_bss_cache_ctrl_t * bss_cache_ctrl)4008 wl_bss_cache_size(wl_bss_cache_ctrl_t *bss_cache_ctrl)
4009 {
4010 wl_bss_cache_t *node, **bss_head;
4011 int bss_num = 0;
4012
4013 bss_head = &bss_cache_ctrl->m_cache_head;
4014
4015 node = *bss_head;
4016 for (;node;) {
4017 if (node->dirty > 1) {
4018 bss_num++;
4019 }
4020 node = node->next;
4021 }
4022 return bss_num;
4023 }
4024
4025 void
wl_reset_bss_cache(wl_bss_cache_ctrl_t * bss_cache_ctrl)4026 wl_reset_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)
4027 {
4028 wl_bss_cache_t *node, **bss_head;
4029
4030 bss_head = &bss_cache_ctrl->m_cache_head;
4031
4032 /* reset dirty */
4033 node = *bss_head;
4034 for (;node;) {
4035 node->dirty += 1;
4036 node = node->next;
4037 }
4038 }
4039
4040 static void
wl_bss_cache_dump(wl_rssi_cache_ctrl_t * rssi_cache_ctrl,wl_bss_cache_t * node)4041 wl_bss_cache_dump(
4042 #if defined(RSSIAVG)
4043 wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
4044 #endif /* RSSIAVG */
4045 wl_bss_cache_t *node)
4046 {
4047 int k = 0;
4048 int16 rssi;
4049
4050 for (;node;) {
4051 #if defined(RSSIAVG)
4052 rssi = wl_get_avg_rssi(rssi_cache_ctrl, &node->results.bss_info->BSSID);
4053 #else
4054 rssi = dtoh16(node->results.bss_info->RSSI);
4055 #endif /* RSSIAVG */
4056 k++;
4057 AEXT_TRACE("wlan", "dump %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
4058 k, &node->results.bss_info->BSSID, rssi, node->results.bss_info->SSID);
4059 node = node->next;
4060 }
4061 }
4062
4063 #if defined(SORT_BSS_CHANNEL)
4064 static wl_bss_cache_t *
wl_bss_cache_sort_channel(wl_bss_cache_t ** bss_head,wl_bss_cache_t * leaf)4065 wl_bss_cache_sort_channel(wl_bss_cache_t **bss_head, wl_bss_cache_t *leaf)
4066 {
4067 wl_bss_cache_t *node, *prev;
4068 uint16 channel, channel_node;
4069
4070 node = *bss_head;
4071 channel = wf_chspec_ctlchan(leaf->results.bss_info->chanspec);
4072 for (;node;) {
4073 channel_node = wf_chspec_ctlchan(node->results.bss_info->chanspec);
4074 if (channel_node > channel) {
4075 leaf->next = node;
4076 if (node == *bss_head)
4077 *bss_head = leaf;
4078 else
4079 prev->next = leaf;
4080 break;
4081 }
4082 prev = node;
4083 node = node->next;
4084 }
4085 if (node == NULL)
4086 prev->next = leaf;
4087
4088 return *bss_head;
4089 }
4090 #endif /* SORT_BSS_CHANNEL */
4091
4092 #if defined(SORT_BSS_RSSI)
4093 static wl_bss_cache_t *
wl_bss_cache_sort_rssi(wl_bss_cache_t ** bss_head,wl_bss_cache_t * leaf,wl_rssi_cache_ctrl_t * rssi_cache_ctrl)4094 wl_bss_cache_sort_rssi(wl_bss_cache_t **bss_head, wl_bss_cache_t *leaf
4095 #if defined(RSSIAVG)
4096 , wl_rssi_cache_ctrl_t *rssi_cache_ctrl
4097 #endif /* RSSIAVG */
4098 )
4099 {
4100 wl_bss_cache_t *node, *prev;
4101 int16 rssi, rssi_node;
4102
4103 node = *bss_head;
4104 #if defined(RSSIAVG)
4105 rssi = wl_get_avg_rssi(rssi_cache_ctrl, &leaf->results.bss_info->BSSID);
4106 #else
4107 rssi = dtoh16(leaf->results.bss_info->RSSI);
4108 #endif /* RSSIAVG */
4109 for (;node;) {
4110 #if defined(RSSIAVG)
4111 rssi_node = wl_get_avg_rssi(rssi_cache_ctrl,
4112 &node->results.bss_info->BSSID);
4113 #else
4114 rssi_node = dtoh16(node->results.bss_info->RSSI);
4115 #endif /* RSSIAVG */
4116 if (rssi > rssi_node) {
4117 leaf->next = node;
4118 if (node == *bss_head)
4119 *bss_head = leaf;
4120 else
4121 prev->next = leaf;
4122 break;
4123 }
4124 prev = node;
4125 node = node->next;
4126 }
4127 if (node == NULL)
4128 prev->next = leaf;
4129
4130 return *bss_head;
4131 }
4132 #endif /* SORT_BSS_BY_RSSI */
4133
4134 void
wl_update_bss_cache(wl_bss_cache_ctrl_t * bss_cache_ctrl,wl_rssi_cache_ctrl_t * rssi_cache_ctrl,wl_scan_results_v109_t * ss_list)4135 wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl,
4136 #if defined(RSSIAVG)
4137 wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
4138 #endif /* RSSIAVG */
4139 wl_scan_results_v109_t *ss_list)
4140 {
4141 wl_bss_cache_t *node, *node_target = NULL, *prev, *leaf, **bss_head;
4142 wl_bss_cache_t *node_rssi_prev = NULL, *node_rssi = NULL;
4143 wl_bss_info_v109_t *bi = NULL;
4144 int i, k=0, bss_num = 0;
4145 struct osl_timespec now, timeout;
4146 int16 rssi_min;
4147 bool rssi_replace = FALSE;
4148
4149 if (!ss_list->count)
4150 return;
4151
4152 osl_do_gettimeofday(&now);
4153 timeout.tv_sec = now.tv_sec + BSSCACHE_TIMEOUT;
4154 if (timeout.tv_sec < now.tv_sec) {
4155 /*
4156 * Integer overflow - assume long enough timeout to be assumed
4157 * to be infinite, i.e., the timeout would never happen.
4158 */
4159 AEXT_TRACE("wlan",
4160 "Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu\n",
4161 BSSCACHE_TIMEOUT, now.tv_sec, timeout.tv_sec);
4162 }
4163
4164 bss_head = &bss_cache_ctrl->m_cache_head;
4165
4166 // get the num of bss cache
4167 node = *bss_head;
4168 for (;node;) {
4169 node = node->next;
4170 bss_num++;
4171 }
4172
4173 for (i=0; i < ss_list->count; i++) {
4174 node = *bss_head;
4175 prev = NULL;
4176 node_target = NULL;
4177 node_rssi_prev = NULL;
4178 bi = bi ? (wl_bss_info_v109_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info;
4179
4180 // find the bss with same BSSID
4181 for (;node;) {
4182 if (!memcmp(&node->results.bss_info->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) {
4183 if (node == *bss_head)
4184 *bss_head = node->next;
4185 else {
4186 prev->next = node->next;
4187 }
4188 break;
4189 }
4190 prev = node;
4191 node = node->next;
4192 }
4193 if (node)
4194 node_target = node;
4195
4196 // find the bss with lowest RSSI
4197 if (!node_target && bss_num >= BSSCACHE_MAXCNT) {
4198 node = *bss_head;
4199 prev = NULL;
4200 rssi_min = dtoh16(bi->RSSI);
4201 for (;node;) {
4202 if (dtoh16(node->results.bss_info->RSSI) < rssi_min) {
4203 node_rssi = node;
4204 node_rssi_prev = prev;
4205 rssi_min = dtoh16(node->results.bss_info->RSSI);
4206 }
4207 prev = node;
4208 node = node->next;
4209 }
4210 if (dtoh16(bi->RSSI) > rssi_min) {
4211 rssi_replace = TRUE;
4212 node_target = node_rssi;
4213 if (node_rssi == *bss_head)
4214 *bss_head = node_rssi->next;
4215 else if (node_rssi) {
4216 node_rssi_prev->next = node_rssi->next;
4217 }
4218 }
4219 }
4220
4221 k++;
4222 if (bss_num < BSSCACHE_MAXCNT) {
4223 bss_num++;
4224 AEXT_TRACE("wlan",
4225 "Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
4226 k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
4227 } else if (node_target) {
4228 if (rssi_replace) {
4229 AEXT_TRACE("wlan",
4230 "Replace %d with cached BSSID %pM(%3d) => %pM(%3d), "\
4231 "SSID \"%s\" => \"%s\"\n",
4232 k, &node_target->results.bss_info->BSSID,
4233 dtoh16(node_target->results.bss_info->RSSI),
4234 &bi->BSSID, dtoh16(bi->RSSI),
4235 node_target->results.bss_info->SSID, bi->SSID);
4236 } else {
4237 AEXT_TRACE("wlan",
4238 "Update %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
4239 k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
4240 }
4241 kfree(node_target);
4242 node_target = NULL;
4243 } else {
4244 AEXT_TRACE("wlan", "Skip %d BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
4245 k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
4246 continue;
4247 }
4248
4249 leaf = kmalloc(dtoh32(bi->length) + sizeof(wl_bss_cache_t), GFP_KERNEL);
4250 if (!leaf) {
4251 AEXT_ERROR("wlan", "Memory alloc failure %d\n",
4252 dtoh32(bi->length) + (int)sizeof(wl_bss_cache_t));
4253 return;
4254 }
4255
4256 memcpy(leaf->results.bss_info, bi, dtoh32(bi->length));
4257 leaf->next = NULL;
4258 leaf->dirty = 0;
4259 leaf->tv = timeout;
4260 leaf->results.count = 1;
4261 leaf->results.version = ss_list->version;
4262
4263 if (*bss_head == NULL)
4264 *bss_head = leaf;
4265 else {
4266 #if defined(SORT_BSS_CHANNEL)
4267 *bss_head = wl_bss_cache_sort_channel(bss_head, leaf);
4268 #elif defined(SORT_BSS_RSSI)
4269 *bss_head = wl_bss_cache_sort_rssi(bss_head, leaf
4270 #if defined(RSSIAVG)
4271 , rssi_cache_ctrl
4272 #endif /* RSSIAVG */
4273 );
4274 #else
4275 leaf->next = *bss_head;
4276 *bss_head = leaf;
4277 #endif /* SORT_BSS_BY_RSSI */
4278 }
4279 }
4280 wl_bss_cache_dump(
4281 #if defined(RSSIAVG)
4282 rssi_cache_ctrl,
4283 #endif /* RSSIAVG */
4284 *bss_head);
4285 }
4286
4287 void
wl_release_bss_cache_ctrl(wl_bss_cache_ctrl_t * bss_cache_ctrl)4288 wl_release_bss_cache_ctrl(wl_bss_cache_ctrl_t *bss_cache_ctrl)
4289 {
4290 AEXT_TRACE("wlan", "Enter\n");
4291 wl_free_bss_cache(bss_cache_ctrl);
4292 }
4293 #endif /* BSSCACHE */
4294