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