wifi: update bcm driver to fix testWifiUsabilityStatsEntry fail [2/2]
[GitHub/LineageOS/G12/android_hardware_amlogic_kernel-modules_dhd-driver.git] / bcmdhd.101.10.240.x / wl_android_ext.c
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-%s] AEXT-ERROR) %s : " arg1, name, __func__, ## args); \
39 } \
40 } while (0)
41 #define AEXT_TRACE(name, arg1, args...) \
42 do { \
43 if (android_msg_level & ANDROID_TRACE_LEVEL) { \
44 printk(KERN_INFO "[dhd-%s] AEXT-TRACE) %s : " arg1, name, __func__, ## args); \
45 } \
46 } while (0)
47 #define AEXT_INFO(name, arg1, args...) \
48 do { \
49 if (android_msg_level & ANDROID_INFO_LEVEL) { \
50 printk(KERN_INFO "[dhd-%s] AEXT-INFO) %s : " arg1, name, __func__, ## args); \
51 } \
52 } while (0)
53 #define AEXT_DBG(name, arg1, args...) \
54 do { \
55 if (android_msg_level & ANDROID_DBG_LEVEL) { \
56 printk(KERN_INFO "[dhd-%s] AEXT-DBG) %s : " arg1, name, __func__, ## args); \
57 } \
58 } while (0)
59
60 #ifndef WL_CFG80211
61 #define htod32(i) i
62 #define htod16(i) i
63 #define dtoh32(i) i
64 #define dtoh16(i) i
65 #define htodchanspec(i) i
66 #define dtohchanspec(i) i
67 #define IEEE80211_BAND_2GHZ 0
68 #define IEEE80211_BAND_5GHZ 1
69 #define WL_SCAN_JOIN_PROBE_INTERVAL_MS 20
70 #define WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
71 #define WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
72 #endif /* WL_CFG80211 */
73 #define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
74
75 #ifndef IW_CUSTOM_MAX
76 #define IW_CUSTOM_MAX 256 /* size of extra buffer used for translation of events */
77 #endif /* IW_CUSTOM_MAX */
78
79 #define CMD_CHANNEL "CHANNEL"
80 #define CMD_CHANNELS "CHANNELS"
81 #define CMD_ROAM_TRIGGER "ROAM_TRIGGER"
82 #define CMD_PM "PM"
83 #define CMD_MONITOR "MONITOR"
84 #define CMD_SET_SUSPEND_BCN_LI_DTIM "SET_SUSPEND_BCN_LI_DTIM"
85 #define CMD_WLMSGLEVEL "WLMSGLEVEL"
86
87 #ifdef WL_EXT_IAPSTA
88 #include <net/rtnetlink.h>
89 #define CMD_IAPSTA_INIT "IAPSTA_INIT"
90 #define CMD_IAPSTA_CONFIG "IAPSTA_CONFIG"
91 #define CMD_IAPSTA_ENABLE "IAPSTA_ENABLE"
92 #define CMD_IAPSTA_DISABLE "IAPSTA_DISABLE"
93 #define CMD_ISAM_INIT "ISAM_INIT"
94 #define CMD_ISAM_CONFIG "ISAM_CONFIG"
95 #define CMD_ISAM_ENABLE "ISAM_ENABLE"
96 #define CMD_ISAM_DISABLE "ISAM_DISABLE"
97 #define CMD_ISAM_STATUS "ISAM_STATUS"
98 #define CMD_ISAM_PEER_PATH "ISAM_PEER_PATH"
99 #define CMD_ISAM_PARAM "ISAM_PARAM"
100 #ifdef PROP_TXSTATUS
101 #ifdef PROP_TXSTATUS_VSDB
102 #include <dhd_wlfc.h>
103 extern int disable_proptx;
104 #endif /* PROP_TXSTATUS_VSDB */
105 #endif /* PROP_TXSTATUS */
106 #endif /* WL_EXT_IAPSTA */
107 #define CMD_AUTOCHANNEL "AUTOCHANNEL"
108 #define CMD_WL "WL"
109
110 #ifdef WL_EXT_IAPSTA
111 typedef enum APSTAMODE {
112 IUNKNOWN_MODE = 0,
113 ISTAONLY_MODE = 1,
114 IAPONLY_MODE = 2,
115 ISTAAP_MODE = 3,
116 ISTAGO_MODE = 4,
117 ISTASTA_MODE = 5,
118 IDUALAP_MODE = 6,
119 ISTAAPAP_MODE = 7,
120 IMESHONLY_MODE = 8,
121 ISTAMESH_MODE = 9,
122 IMESHAP_MODE = 10,
123 ISTAAPMESH_MODE = 11,
124 IMESHAPAP_MODE = 12
125 } apstamode_t;
126
127 typedef enum IFMODE {
128 ISTA_MODE = 1,
129 IAP_MODE,
130 IGO_MODE,
131 IGC_MODE,
132 IMESH_MODE
133 } ifmode_t;
134
135 typedef enum BGNMODE {
136 IEEE80211B = 1,
137 IEEE80211G,
138 IEEE80211BG,
139 IEEE80211BGN,
140 IEEE80211BGNAC
141 } bgnmode_t;
142
143 typedef enum AUTHMODE {
144 AUTH_OPEN,
145 AUTH_SHARED,
146 AUTH_WPAPSK,
147 AUTH_WPA2PSK,
148 AUTH_WPAWPA2PSK,
149 AUTH_SAE
150 } authmode_t;
151
152 typedef enum ENCMODE {
153 ENC_NONE,
154 ENC_WEP,
155 ENC_TKIP,
156 ENC_AES,
157 ENC_TKIPAES
158 } encmode_t;
159
160 enum wl_if_list {
161 IF_PIF,
162 IF_VIF,
163 IF_VIF2,
164 MAX_IF_NUM
165 };
166
167 typedef enum WL_PRIO {
168 PRIO_AP,
169 PRIO_MESH,
170 PRIO_STA
171 } wl_prio_t;
172
173 typedef struct wl_if_info {
174 struct net_device *dev;
175 ifmode_t ifmode;
176 unsigned long status;
177 char prefix;
178 wl_prio_t prio;
179 int ifidx;
180 uint8 bssidx;
181 char ifname[IFNAMSIZ+1];
182 char ssid[DOT11_MAX_SSID_LEN];
183 struct ether_addr bssid;
184 bgnmode_t bgnmode;
185 int hidden;
186 int maxassoc;
187 uint16 channel;
188 authmode_t amode;
189 encmode_t emode;
190 char key[100];
191 #if defined(WLMESH) && defined(WL_ESCAN)
192 struct wl_escan_info *escan;
193 timer_list_compat_t delay_scan;
194 #endif /* WLMESH && WL_ESCAN */
195 struct delayed_work pm_enable_work;
196 struct mutex pm_sync;
197 #ifdef PROPTX_MAXCOUNT
198 int transit_maxcount;
199 #endif /* PROP_TXSTATUS_VSDB */
200 } wl_if_info_t;
201
202 #define CSA_FW_BIT (1<<0)
203 #define CSA_DRV_BIT (1<<1)
204
205 typedef struct wl_apsta_params {
206 struct wl_if_info if_info[MAX_IF_NUM];
207 struct dhd_pub *dhd;
208 int ioctl_ver;
209 bool init;
210 int rsdb;
211 bool vsdb;
212 uint csa;
213 uint acs;
214 bool radar;
215 apstamode_t apstamode;
216 wait_queue_head_t netif_change_event;
217 struct mutex usr_sync;
218 #if defined(WLMESH) && defined(WL_ESCAN)
219 int macs;
220 struct wl_mesh_params mesh_info;
221 #endif /* WLMESH && WL_ESCAN */
222 } wl_apsta_params_t;
223
224 #define MAX_AP_LINK_WAIT_TIME 3000
225 #define MAX_STA_LINK_WAIT_TIME 15000
226 enum wifi_isam_status {
227 ISAM_STATUS_IF_ADDING = 0,
228 ISAM_STATUS_IF_READY,
229 ISAM_STATUS_STA_CONNECTING,
230 ISAM_STATUS_STA_CONNECTED,
231 ISAM_STATUS_AP_CREATING,
232 ISAM_STATUS_AP_CREATED
233 };
234
235 #define wl_get_isam_status(cur_if, stat) \
236 (test_bit(ISAM_STATUS_ ## stat, &(cur_if)->status))
237 #define wl_set_isam_status(cur_if, stat) \
238 (set_bit(ISAM_STATUS_ ## stat, &(cur_if)->status))
239 #define wl_clr_isam_status(cur_if, stat) \
240 (clear_bit(ISAM_STATUS_ ## stat, &(cur_if)->status))
241 #define wl_chg_isam_status(cur_if, stat) \
242 (change_bit(ISAM_STATUS_ ## stat, &(cur_if)->status))
243
244 static int wl_ext_enable_iface(struct net_device *dev, char *ifname, int wait_up);
245 static int wl_ext_disable_iface(struct net_device *dev, char *ifname);
246 #if defined(WLMESH) && defined(WL_ESCAN)
247 static int wl_mesh_escan_attach(dhd_pub_t *dhd, struct wl_if_info *cur_if);
248 #endif /* WLMESH && WL_ESCAN */
249 #endif /* WL_EXT_IAPSTA */
250
251 #ifdef IDHCP
252 typedef struct dhcpc_parameter {
253 uint32 ip_addr;
254 uint32 ip_serv;
255 uint32 lease_time;
256 } dhcpc_para_t;
257 #endif /* IDHCP */
258
259 #ifdef WL_EXT_WOWL
260 #define WL_WOWL_TCPFIN (1 << 26)
261 typedef struct wl_wowl_pattern2 {
262 char cmd[4];
263 wl_wowl_pattern_t wowl_pattern;
264 } wl_wowl_pattern2_t;
265 #endif /* WL_EXT_WOWL */
266
267 #ifdef WL_EXT_TCPKA
268 typedef struct tcpka_conn {
269 uint32 sess_id;
270 struct ether_addr dst_mac; /* Destinition Mac */
271 struct ipv4_addr src_ip; /* Sorce IP */
272 struct ipv4_addr dst_ip; /* Destinition IP */
273 uint16 ipid; /* Ip Identification */
274 uint16 srcport; /* Source Port Address */
275 uint16 dstport; /* Destination Port Address */
276 uint32 seq; /* TCP Sequence Number */
277 uint32 ack; /* TCP Ack Number */
278 uint16 tcpwin; /* TCP window */
279 uint32 tsval; /* Timestamp Value */
280 uint32 tsecr; /* Timestamp Echo Reply */
281 uint32 len; /* last packet payload len */
282 uint32 ka_payload_len; /* keep alive payload length */
283 uint8 ka_payload[1]; /* keep alive payload */
284 } tcpka_conn_t;
285
286 typedef struct tcpka_conn_sess {
287 uint32 sess_id; /* session id */
288 uint32 flag; /* enable/disable flag */
289 wl_mtcpkeep_alive_timers_pkt_t tcpka_timers;
290 } tcpka_conn_sess_t;
291
292 typedef struct tcpka_conn_info {
293 uint32 ipid;
294 uint32 seq;
295 uint32 ack;
296 } tcpka_conn_sess_info_t;
297 #endif /* WL_EXT_TCPKA */
298
299 static int wl_ext_wl_iovar(struct net_device *dev, char *command, int total_len);
300
301 static int
302 wl_ext_ioctl(struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set)
303 {
304 int ret;
305
306 ret = wldev_ioctl(dev, cmd, arg, len, set);
307 if (ret)
308 AEXT_ERROR(dev->name, "cmd=%d, ret=%d\n", cmd, ret);
309 return ret;
310 }
311
312 static int
313 wl_ext_iovar_getint(struct net_device *dev, s8 *iovar, s32 *val)
314 {
315 int ret;
316
317 ret = wldev_iovar_getint(dev, iovar, val);
318 if (ret)
319 AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar, ret);
320
321 return ret;
322 }
323
324 static int
325 wl_ext_iovar_setint(struct net_device *dev, s8 *iovar, s32 val)
326 {
327 int ret;
328
329 ret = wldev_iovar_setint(dev, iovar, val);
330 if (ret)
331 AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar, ret);
332
333 return ret;
334 }
335
336 static int
337 wl_ext_iovar_getbuf(struct net_device *dev, s8 *iovar_name,
338 void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
339 {
340 int ret;
341
342 ret = wldev_iovar_getbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync);
343 if (ret != 0)
344 AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar_name, ret);
345
346 return ret;
347 }
348
349 static int
350 wl_ext_iovar_setbuf(struct net_device *dev, s8 *iovar_name,
351 void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
352 {
353 int ret;
354
355 ret = wldev_iovar_setbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync);
356 if (ret != 0)
357 AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar_name, ret);
358
359 return ret;
360 }
361
362 static int
363 wl_ext_iovar_setbuf_bsscfg(struct net_device *dev, s8 *iovar_name,
364 void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx,
365 struct mutex* buf_sync)
366 {
367 int ret;
368
369 ret = wldev_iovar_setbuf_bsscfg(dev, iovar_name, param, paramlen,
370 buf, buflen, bsscfg_idx, buf_sync);
371 if (ret < 0)
372 AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar_name, ret);
373
374 return ret;
375 }
376
377 static chanspec_t
378 wl_ext_chspec_to_legacy(chanspec_t chspec)
379 {
380 chanspec_t lchspec;
381
382 if (wf_chspec_malformed(chspec)) {
383 AEXT_ERROR("wlan", "input chanspec (0x%04X) malformed\n", chspec);
384 return INVCHANSPEC;
385 }
386
387 /* get the channel number */
388 lchspec = CHSPEC_CHANNEL(chspec);
389
390 /* convert the band */
391 if (CHSPEC_IS2G(chspec)) {
392 lchspec |= WL_LCHANSPEC_BAND_2G;
393 } else {
394 lchspec |= WL_LCHANSPEC_BAND_5G;
395 }
396
397 /* convert the bw and sideband */
398 if (CHSPEC_IS20(chspec)) {
399 lchspec |= WL_LCHANSPEC_BW_20;
400 lchspec |= WL_LCHANSPEC_CTL_SB_NONE;
401 } else if (CHSPEC_IS40(chspec)) {
402 lchspec |= WL_LCHANSPEC_BW_40;
403 if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_L) {
404 lchspec |= WL_LCHANSPEC_CTL_SB_LOWER;
405 } else {
406 lchspec |= WL_LCHANSPEC_CTL_SB_UPPER;
407 }
408 } else {
409 /* cannot express the bandwidth */
410 char chanbuf[CHANSPEC_STR_LEN];
411 AEXT_ERROR("wlan", "unable to convert chanspec %s (0x%04X) "
412 "to pre-11ac format\n",
413 wf_chspec_ntoa(chspec, chanbuf), chspec);
414 return INVCHANSPEC;
415 }
416
417 return lchspec;
418 }
419
420 static chanspec_t
421 wl_ext_chspec_host_to_driver(int ioctl_ver, chanspec_t chanspec)
422 {
423 if (ioctl_ver == 1) {
424 chanspec = wl_ext_chspec_to_legacy(chanspec);
425 if (chanspec == INVCHANSPEC) {
426 return chanspec;
427 }
428 }
429 chanspec = htodchanspec(chanspec);
430
431 return chanspec;
432 }
433
434 static void
435 wl_ext_ch_to_chanspec(int ioctl_ver, int ch,
436 struct wl_join_params *join_params, size_t *join_params_size)
437 {
438 chanspec_t chanspec = 0;
439
440 if (ch != 0) {
441 join_params->params.chanspec_num = 1;
442 join_params->params.chanspec_list[0] = ch;
443
444 if (join_params->params.chanspec_list[0] <= CH_MAX_2G_CHANNEL)
445 chanspec |= WL_CHANSPEC_BAND_2G;
446 else
447 chanspec |= WL_CHANSPEC_BAND_5G;
448
449 chanspec |= WL_CHANSPEC_BW_20;
450 chanspec |= WL_CHANSPEC_CTL_SB_NONE;
451
452 *join_params_size += WL_ASSOC_PARAMS_FIXED_SIZE +
453 join_params->params.chanspec_num * sizeof(chanspec_t);
454
455 join_params->params.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK;
456 join_params->params.chanspec_list[0] |= chanspec;
457 join_params->params.chanspec_list[0] =
458 wl_ext_chspec_host_to_driver(ioctl_ver,
459 join_params->params.chanspec_list[0]);
460
461 join_params->params.chanspec_num =
462 htod32(join_params->params.chanspec_num);
463 }
464 }
465
466 #if defined(WL_EXT_IAPSTA) || defined(WL_CFG80211) || defined(WL_ESCAN)
467 static chanspec_t
468 wl_ext_chspec_from_legacy(chanspec_t legacy_chspec)
469 {
470 chanspec_t chspec;
471
472 /* get the channel number */
473 chspec = LCHSPEC_CHANNEL(legacy_chspec);
474
475 /* convert the band */
476 if (LCHSPEC_IS2G(legacy_chspec)) {
477 chspec |= WL_CHANSPEC_BAND_2G;
478 } else {
479 chspec |= WL_CHANSPEC_BAND_5G;
480 }
481
482 /* convert the bw and sideband */
483 if (LCHSPEC_IS20(legacy_chspec)) {
484 chspec |= WL_CHANSPEC_BW_20;
485 } else {
486 chspec |= WL_CHANSPEC_BW_40;
487 if (LCHSPEC_CTL_SB(legacy_chspec) == WL_LCHANSPEC_CTL_SB_LOWER) {
488 chspec |= WL_CHANSPEC_CTL_SB_L;
489 } else {
490 chspec |= WL_CHANSPEC_CTL_SB_U;
491 }
492 }
493
494 if (wf_chspec_malformed(chspec)) {
495 AEXT_ERROR("wlan", "output chanspec (0x%04X) malformed\n", chspec);
496 return INVCHANSPEC;
497 }
498
499 return chspec;
500 }
501
502 static chanspec_t
503 wl_ext_chspec_driver_to_host(int ioctl_ver, chanspec_t chanspec)
504 {
505 chanspec = dtohchanspec(chanspec);
506 if (ioctl_ver == 1) {
507 chanspec = wl_ext_chspec_from_legacy(chanspec);
508 }
509
510 return chanspec;
511 }
512 #endif /* WL_EXT_IAPSTA || WL_CFG80211 || WL_ESCAN */
513
514 bool
515 wl_ext_check_scan(struct net_device *dev, dhd_pub_t *dhdp)
516 {
517 #ifdef WL_CFG80211
518 struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
519 #endif /* WL_CFG80211 */
520 #ifdef WL_ESCAN
521 struct wl_escan_info *escan = dhdp->escan;
522 #endif /* WL_ESCAN */
523
524 #ifdef WL_CFG80211
525 if (wl_get_drv_status_all(cfg, SCANNING)) {
526 AEXT_ERROR(dev->name, "cfg80211 scanning...\n");
527 return TRUE;
528 }
529 #endif /* WL_CFG80211 */
530
531 #ifdef WL_ESCAN
532 if (escan->escan_state == ESCAN_STATE_SCANING) {
533 AEXT_ERROR(dev->name, "escan scanning...\n");
534 return TRUE;
535 }
536 #endif /* WL_ESCAN */
537
538 return FALSE;
539 }
540
541 #if defined(WL_CFG80211) || defined(WL_ESCAN)
542 void
543 wl_ext_user_sync(struct dhd_pub *dhd, int ifidx, bool lock)
544 {
545 struct net_device *dev = dhd_idx2net(dhd, ifidx);
546 #ifdef WL_CFG80211
547 struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
548 #endif /* WL_CFG80211 */
549 #ifdef WL_ESCAN
550 struct wl_escan_info *escan = dhd->escan;
551 #endif /* WL_ESCAN */
552
553 AEXT_INFO(dev->name, "lock=%d\n", lock);
554
555 if (lock) {
556 #if defined(WL_CFG80211)
557 mutex_lock(&cfg->usr_sync);
558 #endif
559 #if defined(WL_ESCAN)
560 mutex_lock(&escan->usr_sync);
561 #endif
562 } else {
563 #if defined(WL_CFG80211)
564 mutex_unlock(&cfg->usr_sync);
565 #endif
566 #if defined(WL_ESCAN)
567 mutex_unlock(&escan->usr_sync);
568 #endif
569 }
570 }
571
572 bool
573 wl_ext_event_complete(struct dhd_pub *dhd, int ifidx)
574 {
575 struct net_device *dev = dhd_idx2net(dhd, ifidx);
576 #ifdef WL_CFG80211
577 struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
578 #endif /* WL_CFG80211 */
579 #ifdef WL_ESCAN
580 struct wl_escan_info *escan = dhd->escan;
581 #endif /* WL_ESCAN */
582 bool complete = TRUE;
583
584 #ifdef WL_CFG80211
585 if (wl_get_drv_status_all(cfg, SCANNING)) {
586 AEXT_INFO(dev->name, "SCANNING\n");
587 complete = FALSE;
588 }
589 if (wl_get_drv_status_all(cfg, CONNECTING)) {
590 AEXT_INFO(dev->name, "CONNECTING\n");
591 complete = FALSE;
592 }
593 if (wl_get_drv_status_all(cfg, DISCONNECTING)) {
594 AEXT_INFO(dev->name, "DISCONNECTING\n");
595 complete = FALSE;
596 }
597 #endif /* WL_CFG80211 */
598 #ifdef WL_ESCAN
599 if (escan->escan_state == ESCAN_STATE_SCANING) {
600 AEXT_INFO(dev->name, "ESCAN_STATE_SCANING\n");
601 complete = FALSE;
602 }
603 #endif /* WL_ESCAN */
604 if (dhd->conf->eapol_status >= EAPOL_STATUS_4WAY_START &&
605 dhd->conf->eapol_status < EAPOL_STATUS_4WAY_DONE) {
606 AEXT_INFO(dev->name, "4-WAY handshaking\n");
607 complete = FALSE;
608 }
609
610 return complete;
611 }
612 #endif /* WL_CFG80211 && WL_ESCAN */
613
614 static int
615 wl_ext_get_ioctl_ver(struct net_device *dev, int *ioctl_ver)
616 {
617 int ret = 0;
618 s32 val = 0;
619
620 val = 1;
621 ret = wl_ext_ioctl(dev, WLC_GET_VERSION, &val, sizeof(val), 0);
622 if (ret) {
623 return ret;
624 }
625 val = dtoh32(val);
626 if (val != WLC_IOCTL_VERSION && val != 1) {
627 AEXT_ERROR(dev->name, "Version mismatch, please upgrade. Got %d, expected %d or 1\n",
628 val, WLC_IOCTL_VERSION);
629 return BCME_VERSION;
630 }
631 *ioctl_ver = val;
632
633 return ret;
634 }
635
636 void
637 wl_ext_bss_iovar_war(struct net_device *ndev, s32 *val)
638 {
639 dhd_pub_t *dhd = dhd_get_pub(ndev);
640 uint chip;
641 bool need_war = false;
642
643 chip = dhd_conf_get_chip(dhd);
644
645 if (chip == BCM43362_CHIP_ID || chip == BCM4330_CHIP_ID ||
646 chip == BCM43430_CHIP_ID || chip == BCM43012_CHIP_ID ||
647 chip == BCM4354_CHIP_ID || chip == BCM4356_CHIP_ID ||
648 chip == BCM4345_CHIP_ID || chip == BCM4359_CHIP_ID ||
649 chip == BCM43143_CHIP_ID || chip == BCM43242_CHIP_ID ||
650 chip == BCM43569_CHIP_ID) {
651 need_war = true;
652 }
653
654 if (need_war) {
655 /* Few firmware branches have issues in bss iovar handling and
656 * that can't be changed since they are in production.
657 */
658 if (*val == WLC_AP_IOV_OP_MANUAL_AP_BSSCFG_CREATE) {
659 *val = WLC_AP_IOV_OP_MANUAL_STA_BSSCFG_CREATE;
660 } else if (*val == WLC_AP_IOV_OP_MANUAL_STA_BSSCFG_CREATE) {
661 *val = WLC_AP_IOV_OP_MANUAL_AP_BSSCFG_CREATE;
662 } else {
663 /* Ignore for other bss enums */
664 return;
665 }
666 AEXT_TRACE(ndev->name, "wl bss %d\n", *val);
667 }
668 }
669
670 static int
671 wl_ext_set_chanspec(struct net_device *dev, int ioctl_ver,
672 uint16 channel, chanspec_t *ret_chspec)
673 {
674 s32 _chan = channel;
675 chanspec_t chspec = 0;
676 chanspec_t fw_chspec = 0;
677 u32 bw = WL_CHANSPEC_BW_20;
678 s32 err = BCME_OK;
679 s32 bw_cap = 0;
680 s8 iovar_buf[WLC_IOCTL_SMLEN];
681 struct {
682 u32 band;
683 u32 bw_cap;
684 } param = {0, 0};
685 uint band;
686
687 if (_chan <= CH_MAX_2G_CHANNEL)
688 band = IEEE80211_BAND_2GHZ;
689 else
690 band = IEEE80211_BAND_5GHZ;
691
692 if (band == IEEE80211_BAND_5GHZ) {
693 param.band = WLC_BAND_5G;
694 err = wl_ext_iovar_getbuf(dev, "bw_cap", &param, sizeof(param),
695 iovar_buf, WLC_IOCTL_SMLEN, NULL);
696 if (err) {
697 if (err != BCME_UNSUPPORTED) {
698 AEXT_ERROR(dev->name, "bw_cap failed, %d\n", err);
699 return err;
700 } else {
701 err = wl_ext_iovar_getint(dev, "mimo_bw_cap", &bw_cap);
702 if (bw_cap != WLC_N_BW_20ALL)
703 bw = WL_CHANSPEC_BW_40;
704 }
705 } else {
706 if (WL_BW_CAP_80MHZ(iovar_buf[0]))
707 bw = WL_CHANSPEC_BW_80;
708 else if (WL_BW_CAP_40MHZ(iovar_buf[0]))
709 bw = WL_CHANSPEC_BW_40;
710 else
711 bw = WL_CHANSPEC_BW_20;
712
713 }
714 }
715 else if (band == IEEE80211_BAND_2GHZ)
716 bw = WL_CHANSPEC_BW_20;
717
718 set_channel:
719 chspec = wf_channel2chspec(_chan, bw);
720 if (wf_chspec_valid(chspec)) {
721 fw_chspec = wl_ext_chspec_host_to_driver(ioctl_ver, chspec);
722 if (fw_chspec != INVCHANSPEC) {
723 if ((err = wl_ext_iovar_setint(dev, "chanspec", fw_chspec)) == BCME_BADCHAN) {
724 if (bw == WL_CHANSPEC_BW_80)
725 goto change_bw;
726 err = wl_ext_ioctl(dev, WLC_SET_CHANNEL, &_chan, sizeof(_chan), 1);
727 WL_MSG(dev->name, "channel %d\n", _chan);
728 } else if (err) {
729 AEXT_ERROR(dev->name, "failed to set chanspec error %d\n", err);
730 } else
731 WL_MSG(dev->name, "channel %d, 0x%x\n", channel, chspec);
732 } else {
733 AEXT_ERROR(dev->name, "failed to convert host chanspec to fw chanspec\n");
734 err = BCME_ERROR;
735 }
736 } else {
737 change_bw:
738 if (bw == WL_CHANSPEC_BW_80)
739 bw = WL_CHANSPEC_BW_40;
740 else if (bw == WL_CHANSPEC_BW_40)
741 bw = WL_CHANSPEC_BW_20;
742 else
743 bw = 0;
744 if (bw)
745 goto set_channel;
746 AEXT_ERROR(dev->name, "Invalid chanspec 0x%x\n", chspec);
747 err = BCME_ERROR;
748 }
749 *ret_chspec = fw_chspec;
750
751 return err;
752 }
753
754 static int
755 wl_ext_channel(struct net_device *dev, char* command, int total_len)
756 {
757 int ret;
758 int channel=0;
759 channel_info_t ci;
760 int bytes_written = 0;
761 chanspec_t fw_chspec;
762 int ioctl_ver = 0;
763
764 AEXT_TRACE(dev->name, "cmd %s", command);
765
766 sscanf(command, "%*s %d", &channel);
767
768 if (channel > 0) {
769 wl_ext_get_ioctl_ver(dev, &ioctl_ver);
770 ret = wl_ext_set_chanspec(dev, ioctl_ver, channel, &fw_chspec);
771 } else {
772 if (!(ret = wl_ext_ioctl(dev, WLC_GET_CHANNEL, &ci,
773 sizeof(channel_info_t), FALSE))) {
774 AEXT_TRACE(dev->name, "hw_channel %d\n", ci.hw_channel);
775 AEXT_TRACE(dev->name, "target_channel %d\n", ci.target_channel);
776 AEXT_TRACE(dev->name, "scan_channel %d\n", ci.scan_channel);
777 bytes_written = snprintf(command, sizeof(channel_info_t)+2,
778 "channel %d", ci.hw_channel);
779 AEXT_TRACE(dev->name, "command result is %s\n", command);
780 ret = bytes_written;
781 }
782 }
783
784 return ret;
785 }
786
787 static int
788 wl_ext_channels(struct net_device *dev, char* command, int total_len)
789 {
790 int ret, i;
791 int bytes_written = -1;
792 u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
793 wl_uint32_list_t *list;
794
795 AEXT_TRACE(dev->name, "cmd %s", command);
796
797 memset(valid_chan_list, 0, sizeof(valid_chan_list));
798 list = (wl_uint32_list_t *)(void *) valid_chan_list;
799 list->count = htod32(WL_NUMCHANNELS);
800 ret = wl_ext_ioctl(dev, WLC_GET_VALID_CHANNELS, valid_chan_list,
801 sizeof(valid_chan_list), 0);
802 if (ret<0) {
803 AEXT_ERROR(dev->name, "get channels failed with %d\n", ret);
804 } else {
805 bytes_written = snprintf(command, total_len, "channels");
806 for (i = 0; i < dtoh32(list->count); i++) {
807 bytes_written += snprintf(command+bytes_written, total_len, " %d",
808 dtoh32(list->element[i]));
809 }
810 AEXT_TRACE(dev->name, "command result is %s\n", command);
811 ret = bytes_written;
812 }
813
814 return ret;
815 }
816
817 static int
818 wl_ext_roam_trigger(struct net_device *dev, char* command, int total_len)
819 {
820 int ret = 0;
821 int roam_trigger[2] = {0, 0};
822 int trigger[2]= {0, 0};
823 int bytes_written=-1;
824
825 sscanf(command, "%*s %10d", &roam_trigger[0]);
826
827 if (roam_trigger[0]) {
828 roam_trigger[1] = WLC_BAND_ALL;
829 ret = wl_ext_ioctl(dev, WLC_SET_ROAM_TRIGGER, roam_trigger,
830 sizeof(roam_trigger), 1);
831 } else {
832 roam_trigger[1] = WLC_BAND_2G;
833 ret = wl_ext_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger,
834 sizeof(roam_trigger), 0);
835 if (!ret)
836 trigger[0] = roam_trigger[0];
837
838 roam_trigger[1] = WLC_BAND_5G;
839 ret = wl_ext_ioctl(dev, WLC_GET_ROAM_TRIGGER, &roam_trigger,
840 sizeof(roam_trigger), 0);
841 if (!ret)
842 trigger[1] = roam_trigger[0];
843
844 AEXT_TRACE(dev->name, "roam_trigger %d %d\n", trigger[0], trigger[1]);
845 bytes_written = snprintf(command, total_len, "%d %d", trigger[0], trigger[1]);
846 ret = bytes_written;
847 }
848
849 return ret;
850 }
851
852 static int
853 wl_ext_pm(struct net_device *dev, char *command, int total_len)
854 {
855 int pm=-1, ret = -1;
856 char *pm_local;
857 int bytes_written=-1;
858
859 AEXT_TRACE(dev->name, "cmd %s", command);
860
861 sscanf(command, "%*s %d", &pm);
862
863 if (pm >= 0) {
864 ret = wl_ext_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), 1);
865 } else {
866 ret = wl_ext_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm), 0);
867 if (!ret) {
868 AEXT_TRACE(dev->name, "PM = %d", pm);
869 if (pm == PM_OFF)
870 pm_local = "PM_OFF";
871 else if(pm == PM_MAX)
872 pm_local = "PM_MAX";
873 else if(pm == PM_FAST)
874 pm_local = "PM_FAST";
875 else {
876 pm = 0;
877 pm_local = "Invalid";
878 }
879 bytes_written = snprintf(command, total_len, "PM %s", pm_local);
880 AEXT_TRACE(dev->name, "command result is %s\n", command);
881 ret = bytes_written;
882 }
883 }
884
885 return ret;
886 }
887
888 static int
889 wl_ext_monitor(struct net_device *dev, char *command, int total_len)
890 {
891 int val = -1, ret = -1;
892 int bytes_written=-1;
893
894 sscanf(command, "%*s %d", &val);
895
896 if (val >=0) {
897 ret = wl_ext_ioctl(dev, WLC_SET_MONITOR, &val, sizeof(val), 1);
898 } else {
899 ret = wl_ext_ioctl(dev, WLC_GET_MONITOR, &val, sizeof(val), 0);
900 if (!ret) {
901 AEXT_TRACE(dev->name, "monitor = %d\n", val);
902 bytes_written = snprintf(command, total_len, "monitor %d", val);
903 AEXT_TRACE(dev->name, "command result is %s\n", command);
904 ret = bytes_written;
905 }
906 }
907
908 return ret;
909 }
910
911 s32
912 wl_ext_connect(struct net_device *dev, struct wl_conn_info *conn_info)
913 {
914 struct dhd_pub *dhd = dhd_get_pub(dev);
915 wl_extjoin_params_t *ext_join_params = NULL;
916 struct wl_join_params join_params;
917 size_t join_params_size;
918 s32 err = 0;
919 u32 chan_cnt = 0;
920 s8 *iovar_buf = NULL;
921 int ioctl_ver = 0;
922 char sec[32];
923
924 wl_ext_get_ioctl_ver(dev, &ioctl_ver);
925
926 if (dhd->conf->chip == BCM43362_CHIP_ID)
927 goto set_ssid;
928
929 if (conn_info->channel) {
930 chan_cnt = 1;
931 }
932
933 iovar_buf = kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL);
934 if (iovar_buf == NULL) {
935 err = -ENOMEM;
936 goto exit;
937 }
938
939 /*
940 * Join with specific BSSID and cached SSID
941 * If SSID is zero join based on BSSID only
942 */
943 join_params_size = WL_EXTJOIN_PARAMS_FIXED_SIZE +
944 chan_cnt * sizeof(chanspec_t);
945 ext_join_params = (wl_extjoin_params_t*)kzalloc(join_params_size, GFP_KERNEL);
946 if (ext_join_params == NULL) {
947 err = -ENOMEM;
948 goto exit;
949 }
950 ext_join_params->ssid.SSID_len = min((uint32)sizeof(ext_join_params->ssid.SSID),
951 conn_info->ssid.SSID_len);
952 memcpy(&ext_join_params->ssid.SSID, conn_info->ssid.SSID, ext_join_params->ssid.SSID_len);
953 ext_join_params->ssid.SSID_len = htod32(ext_join_params->ssid.SSID_len);
954 /* increate dwell time to receive probe response or detect Beacon
955 * from target AP at a noisy air only during connect command
956 */
957 ext_join_params->scan.active_time = chan_cnt ? WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS : -1;
958 ext_join_params->scan.passive_time = chan_cnt ? WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS : -1;
959 /* Set up join scan parameters */
960 ext_join_params->scan.scan_type = -1;
961 ext_join_params->scan.nprobes = chan_cnt ?
962 (ext_join_params->scan.active_time/WL_SCAN_JOIN_PROBE_INTERVAL_MS) : -1;
963 ext_join_params->scan.home_time = -1;
964
965 if (memcmp(&ether_null, &conn_info->bssid, ETHER_ADDR_LEN))
966 memcpy(&ext_join_params->assoc.bssid, &conn_info->bssid, ETH_ALEN);
967 else
968 memcpy(&ext_join_params->assoc.bssid, &ether_bcast, ETH_ALEN);
969 ext_join_params->assoc.chanspec_num = chan_cnt;
970 if (chan_cnt) {
971 u16 band, bw, ctl_sb;
972 chanspec_t chspec;
973 band = (conn_info->channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G
974 : WL_CHANSPEC_BAND_5G;
975 bw = WL_CHANSPEC_BW_20;
976 ctl_sb = WL_CHANSPEC_CTL_SB_NONE;
977 chspec = (conn_info->channel | band | bw | ctl_sb);
978 ext_join_params->assoc.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK;
979 ext_join_params->assoc.chanspec_list[0] |= chspec;
980 ext_join_params->assoc.chanspec_list[0] =
981 wl_ext_chspec_host_to_driver(ioctl_ver,
982 ext_join_params->assoc.chanspec_list[0]);
983 }
984 ext_join_params->assoc.chanspec_num = htod32(ext_join_params->assoc.chanspec_num);
985
986 wl_ext_get_sec(dev, 0, sec, sizeof(sec));
987 WL_MSG(dev->name,
988 "Connecting with %pM channel (%d) ssid \"%s\", len (%d), sec=%s\n\n",
989 &ext_join_params->assoc.bssid, conn_info->channel,
990 ext_join_params->ssid.SSID, ext_join_params->ssid.SSID_len, sec);
991 err = wl_ext_iovar_setbuf_bsscfg(dev, "join", ext_join_params,
992 join_params_size, iovar_buf, WLC_IOCTL_MAXLEN, conn_info->bssidx, NULL);
993
994 if (err) {
995 if (err == BCME_UNSUPPORTED) {
996 AEXT_TRACE(dev->name, "join iovar is not supported\n");
997 goto set_ssid;
998 } else {
999 AEXT_ERROR(dev->name, "error (%d)\n", err);
1000 goto exit;
1001 }
1002 } else
1003 goto exit;
1004
1005 set_ssid:
1006 memset(&join_params, 0, sizeof(join_params));
1007 join_params_size = sizeof(join_params.ssid);
1008
1009 join_params.ssid.SSID_len = min((uint32)sizeof(join_params.ssid.SSID),
1010 conn_info->ssid.SSID_len);
1011 memcpy(&join_params.ssid.SSID, conn_info->ssid.SSID, join_params.ssid.SSID_len);
1012 join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len);
1013 if (memcmp(&ether_null, &conn_info->bssid, ETHER_ADDR_LEN))
1014 memcpy(&join_params.params.bssid, &conn_info->bssid, ETH_ALEN);
1015 else
1016 memcpy(&join_params.params.bssid, &ether_bcast, ETH_ALEN);
1017
1018 wl_ext_ch_to_chanspec(ioctl_ver, conn_info->channel, &join_params, &join_params_size);
1019 AEXT_TRACE(dev->name, "join_param_size %zu\n", join_params_size);
1020
1021 if (join_params.ssid.SSID_len < IEEE80211_MAX_SSID_LEN) {
1022 AEXT_INFO(dev->name, "ssid \"%s\", len (%d)\n", join_params.ssid.SSID,
1023 join_params.ssid.SSID_len);
1024 }
1025 wl_ext_get_sec(dev, 0, sec, sizeof(sec));
1026 WL_MSG(dev->name,
1027 "Connecting with %pM channel (%d) ssid \"%s\", len (%d), sec=%s\n\n",
1028 &join_params.params.bssid, conn_info->channel,
1029 join_params.ssid.SSID, join_params.ssid.SSID_len, sec);
1030 err = wl_ext_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size, 1);
1031
1032 exit:
1033 #ifdef WL_EXT_IAPSTA
1034 if (!err)
1035 wl_ext_add_remove_pm_enable_work(dev, TRUE);
1036 #endif /* WL_EXT_IAPSTA */
1037 if (iovar_buf)
1038 kfree(iovar_buf);
1039 if (ext_join_params)
1040 kfree(ext_join_params);
1041 return err;
1042
1043 }
1044
1045 void
1046 wl_ext_get_sec(struct net_device *dev, int ifmode, char *sec, int total_len)
1047 {
1048 int auth=0, wpa_auth=0, wsec=0, mfp=0;
1049 int bytes_written=0;
1050
1051 memset(sec, 0, total_len);
1052 wl_ext_iovar_getint(dev, "auth", &auth);
1053 wl_ext_iovar_getint(dev, "wpa_auth", &wpa_auth);
1054 wl_ext_iovar_getint(dev, "wsec", &wsec);
1055 wldev_iovar_getint(dev, "mfp", &mfp);
1056
1057 #ifdef WL_EXT_IAPSTA
1058 if (ifmode == IMESH_MODE) {
1059 if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA_AUTH_DISABLED) {
1060 bytes_written += snprintf(sec+bytes_written, total_len, "open");
1061 } else if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA2_AUTH_PSK) {
1062 bytes_written += snprintf(sec+bytes_written, total_len, "sae");
1063 } else {
1064 bytes_written += snprintf(sec+bytes_written, total_len, "%d/0x%x",
1065 auth, wpa_auth);
1066 }
1067 } else
1068 #endif /* WL_EXT_IAPSTA */
1069 {
1070 if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA_AUTH_DISABLED) {
1071 bytes_written += snprintf(sec+bytes_written, total_len, "open");
1072 } else if (auth == WL_AUTH_SHARED_KEY && wpa_auth == WPA_AUTH_DISABLED) {
1073 bytes_written += snprintf(sec+bytes_written, total_len, "shared");
1074 } else if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA_AUTH_PSK) {
1075 bytes_written += snprintf(sec+bytes_written, total_len, "wpapsk");
1076 } else if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA2_AUTH_PSK) {
1077 bytes_written += snprintf(sec+bytes_written, total_len, "wpa2psk");
1078 } else if ((auth == WL_AUTH_OPEN_SYSTEM || auth == WL_AUTH_SAE_KEY) &&
1079 wpa_auth == WPA3_AUTH_SAE_PSK) {
1080 bytes_written += snprintf(sec+bytes_written, total_len, "wpa3");
1081 } else if ((auth == WL_AUTH_OPEN_SYSTEM || auth == WL_AUTH_SAE_KEY) &&
1082 wpa_auth == 0x20) {
1083 bytes_written += snprintf(sec+bytes_written, total_len, "wpa3");
1084 } else {
1085 bytes_written += snprintf(sec+bytes_written, total_len, "%d/0x%x",
1086 auth, wpa_auth);
1087 }
1088 }
1089
1090 if (mfp == WL_MFP_NONE) {
1091 bytes_written += snprintf(sec+bytes_written, total_len, "/mfpn");
1092 } else if (mfp == WL_MFP_CAPABLE) {
1093 bytes_written += snprintf(sec+bytes_written, total_len, "/mfpc");
1094 } else if (mfp == WL_MFP_REQUIRED) {
1095 bytes_written += snprintf(sec+bytes_written, total_len, "/mfpr");
1096 } else {
1097 bytes_written += snprintf(sec+bytes_written, total_len, "/%d", mfp);
1098 }
1099
1100 #ifdef WL_EXT_IAPSTA
1101 if (ifmode == IMESH_MODE) {
1102 if (wsec == WSEC_NONE) {
1103 bytes_written += snprintf(sec+bytes_written, total_len, "/none");
1104 } else {
1105 bytes_written += snprintf(sec+bytes_written, total_len, "/aes");
1106 }
1107 } else
1108 #endif /* WL_EXT_IAPSTA */
1109 {
1110 if (wsec == WSEC_NONE) {
1111 bytes_written += snprintf(sec+bytes_written, total_len, "/none");
1112 } else if (wsec == WEP_ENABLED) {
1113 bytes_written += snprintf(sec+bytes_written, total_len, "/wep");
1114 } else if (wsec == (TKIP_ENABLED|AES_ENABLED) ||
1115 wsec == (WSEC_SWFLAG|TKIP_ENABLED|AES_ENABLED)) {
1116 bytes_written += snprintf(sec+bytes_written, total_len, "/tkipaes");
1117 } else if (wsec == TKIP_ENABLED || wsec == (WSEC_SWFLAG|TKIP_ENABLED)) {
1118 bytes_written += snprintf(sec+bytes_written, total_len, "/tkip");
1119 } else if (wsec == AES_ENABLED || wsec == (WSEC_SWFLAG|AES_ENABLED)) {
1120 bytes_written += snprintf(sec+bytes_written, total_len, "/aes");
1121 } else {
1122 bytes_written += snprintf(sec+bytes_written, total_len, "/0x%x", wsec);
1123 }
1124 }
1125
1126 }
1127
1128 static bool
1129 wl_ext_dfs_chan(uint16 chan)
1130 {
1131 if (chan >= 52 && chan <= 144)
1132 return TRUE;
1133 return FALSE;
1134 }
1135
1136 static uint16
1137 wl_ext_get_default_chan(struct net_device *dev,
1138 uint16 *chan_2g, uint16 *chan_5g, bool nodfs)
1139 {
1140 struct dhd_pub *dhd = dhd_get_pub(dev);
1141 uint16 chan_tmp = 0, chan = 0;
1142 wl_uint32_list_t *list;
1143 u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
1144 s32 ret = BCME_OK;
1145 int i;
1146
1147 *chan_2g = 0;
1148 *chan_5g = 0;
1149 memset(valid_chan_list, 0, sizeof(valid_chan_list));
1150 list = (wl_uint32_list_t *)(void *) valid_chan_list;
1151 list->count = htod32(WL_NUMCHANNELS);
1152 ret = wl_ext_ioctl(dev, WLC_GET_VALID_CHANNELS, valid_chan_list,
1153 sizeof(valid_chan_list), 0);
1154 if (ret == 0) {
1155 for (i=0; i<dtoh32(list->count); i++) {
1156 chan_tmp = dtoh32(list->element[i]);
1157 if (!dhd_conf_match_channel(dhd, chan_tmp))
1158 continue;
1159 if (chan_tmp <= 13) {
1160 *chan_2g = chan_tmp;
1161 } else {
1162 if (wl_ext_dfs_chan(chan_tmp) && nodfs)
1163 continue;
1164 else if (chan_tmp >= 36 && chan_tmp <= 161)
1165 *chan_5g = chan_tmp;
1166 }
1167 }
1168 }
1169
1170 return chan;
1171 }
1172
1173 static int
1174 wl_ext_wlmsglevel(struct net_device *dev, char *command, int total_len)
1175 {
1176 int val = -1, ret = 0;
1177 int bytes_written = 0;
1178
1179 sscanf(command, "%*s %x", &val);
1180
1181 if (val >=0) {
1182 if (val & DHD_ANDROID_VAL) {
1183 android_msg_level = (uint)(val & 0xFFFF);
1184 printf("android_msg_level=0x%x\n", android_msg_level);
1185 }
1186 #if defined(WL_WIRELESS_EXT)
1187 else if (val & DHD_IW_VAL) {
1188 iw_msg_level = (uint)(val & 0xFFFF);
1189 printf("iw_msg_level=0x%x\n", iw_msg_level);
1190 }
1191 #endif
1192 #ifdef WL_CFG80211
1193 else if (val & DHD_CFG_VAL) {
1194 wl_cfg80211_enable_trace((u32)(val & 0xFFFF));
1195 }
1196 #endif
1197 else if (val & DHD_CONFIG_VAL) {
1198 config_msg_level = (uint)(val & 0xFFFF);
1199 printf("config_msg_level=0x%x\n", config_msg_level);
1200 }
1201 else if (val & DHD_DUMP_VAL) {
1202 dump_msg_level = (uint)(val & 0xFFFF);
1203 printf("dump_msg_level=0x%x\n", dump_msg_level);
1204 }
1205 }
1206 else {
1207 bytes_written += snprintf(command+bytes_written, total_len,
1208 "android_msg_level=0x%x", android_msg_level);
1209 #if defined(WL_WIRELESS_EXT)
1210 bytes_written += snprintf(command+bytes_written, total_len,
1211 "\niw_msg_level=0x%x", iw_msg_level);
1212 #endif
1213 #ifdef WL_CFG80211
1214 bytes_written += snprintf(command+bytes_written, total_len,
1215 "\nwl_dbg_level=0x%x", wl_dbg_level);
1216 #endif
1217 bytes_written += snprintf(command+bytes_written, total_len,
1218 "\nconfig_msg_level=0x%x", config_msg_level);
1219 bytes_written += snprintf(command+bytes_written, total_len,
1220 "\ndump_msg_level=0x%x", dump_msg_level);
1221 AEXT_INFO(dev->name, "%s\n", command);
1222 ret = bytes_written;
1223 }
1224
1225 return ret;
1226 }
1227
1228 #if defined(SENDPROB) || (defined(WLMESH) && defined(WL_ESCAN))
1229 static int
1230 wl_ext_add_del_ie(struct net_device *dev, uint pktflag, char *ie_data, const char* add_del_cmd)
1231 {
1232 vndr_ie_setbuf_t *vndr_ie = NULL;
1233 char iovar_buf[WLC_IOCTL_SMLEN]="\0";
1234 int ie_data_len = 0, tot_len = 0, iecount;
1235 int err = -1;
1236
1237 if (!strlen(ie_data)) {
1238 AEXT_ERROR(dev->name, "wrong ie %s\n", ie_data);
1239 goto exit;
1240 }
1241
1242 tot_len = (int)(sizeof(vndr_ie_setbuf_t) + ((strlen(ie_data)-2)/2));
1243 vndr_ie = (vndr_ie_setbuf_t *) kzalloc(tot_len, GFP_KERNEL);
1244 if (!vndr_ie) {
1245 AEXT_ERROR(dev->name, "IE memory alloc failed\n");
1246 err = -ENOMEM;
1247 goto exit;
1248 }
1249
1250 /* Copy the vndr_ie SET command ("add"/"del") to the buffer */
1251 strncpy(vndr_ie->cmd, add_del_cmd, VNDR_IE_CMD_LEN - 1);
1252 vndr_ie->cmd[VNDR_IE_CMD_LEN - 1] = '\0';
1253
1254 /* Set the IE count - the buffer contains only 1 IE */
1255 iecount = htod32(1);
1256 memcpy((void *)&vndr_ie->vndr_ie_buffer.iecount, &iecount, sizeof(s32));
1257
1258 /* Set packet flag to indicate that BEACON's will contain this IE */
1259 pktflag = htod32(pktflag);
1260 memcpy((void *)&vndr_ie->vndr_ie_buffer.vndr_ie_list[0].pktflag, &pktflag,
1261 sizeof(u32));
1262
1263 /* Set the IE ID */
1264 vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.id = (uchar)DOT11_MNG_VS_ID;
1265
1266 /* Set the IE LEN */
1267 vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.len = (strlen(ie_data)-2)/2;
1268
1269 /* Set the IE OUI and DATA */
1270 ie_data_len = wl_pattern_atoh(ie_data,
1271 (char *)vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui);
1272 if (ie_data_len <= 0) {
1273 AEXT_ERROR(dev->name, "wrong ie_data_len %d\n", (int)strlen(ie_data)-2);
1274 goto exit;
1275 }
1276
1277 err = wl_ext_iovar_setbuf(dev, "vndr_ie", vndr_ie, tot_len, iovar_buf,
1278 sizeof(iovar_buf), NULL);
1279
1280 exit:
1281 if (vndr_ie) {
1282 kfree(vndr_ie);
1283 }
1284 return err;
1285 }
1286 #endif /* SENDPROB || (WLMESH && WL_ESCAN) */
1287
1288 #ifdef WL_EXT_IAPSTA
1289 #define WL_PM_ENABLE_TIMEOUT 10000
1290 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \
1291 4 && __GNUC_MINOR__ >= 6))
1292 #define BCM_SET_CONTAINER_OF(entry, ptr, type, member) \
1293 _Pragma("GCC diagnostic push") \
1294 _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") \
1295 entry = container_of((ptr), type, member); \
1296 _Pragma("GCC diagnostic pop")
1297 #else
1298 #define BCM_SET_CONTAINER_OF(entry, ptr, type, member) \
1299 entry = container_of((ptr), type, member);
1300 #endif /* STRICT_GCC_WARNINGS */
1301
1302 static void
1303 wl_ext_pm_work_handler(struct work_struct *work)
1304 {
1305 struct wl_if_info *cur_if;
1306 s32 pm = PM_FAST;
1307 dhd_pub_t *dhd;
1308
1309 BCM_SET_CONTAINER_OF(cur_if, work, struct wl_if_info, pm_enable_work.work);
1310
1311 AEXT_TRACE("wlan", "%s: Enter\n", __FUNCTION__);
1312
1313 if (cur_if->dev == NULL)
1314 return;
1315
1316 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \
1317 4 && __GNUC_MINOR__ >= 6))
1318 _Pragma("GCC diagnostic push")
1319 _Pragma("GCC diagnostic ignored \"-Wcast-qual\"")
1320 #endif
1321
1322 dhd = dhd_get_pub(cur_if->dev);
1323
1324 if (!dhd || !dhd->up) {
1325 AEXT_TRACE(cur_if->ifname, "dhd is null or not up\n");
1326 return;
1327 }
1328 if (dhd_conf_get_pm(dhd) >= 0)
1329 pm = dhd_conf_get_pm(dhd);
1330 wl_ext_ioctl(cur_if->dev, WLC_SET_PM, &pm, sizeof(pm), 1);
1331 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \
1332 4 && __GNUC_MINOR__ >= 6))
1333 _Pragma("GCC diagnostic pop")
1334 #endif
1335 DHD_PM_WAKE_UNLOCK(dhd);
1336
1337 }
1338
1339 void
1340 wl_ext_add_remove_pm_enable_work(struct net_device *dev, bool add)
1341 {
1342 dhd_pub_t *dhd = dhd_get_pub(dev);
1343 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
1344 struct wl_if_info *cur_if = NULL, *tmp_if = NULL;
1345 u16 wq_duration = 0;
1346 s32 pm = PM_OFF;
1347 int i;
1348
1349 for (i=0; i<MAX_IF_NUM; i++) {
1350 tmp_if = &apsta_params->if_info[i];
1351 if (tmp_if->dev && tmp_if->dev == dev) {
1352 cur_if = tmp_if;
1353 break;
1354 }
1355 }
1356
1357 if (!cur_if)
1358 return;
1359
1360 mutex_lock(&cur_if->pm_sync);
1361 /*
1362 * Make cancel and schedule work part mutually exclusive
1363 * so that while cancelling, we are sure that there is no
1364 * work getting scheduled.
1365 */
1366
1367 if (delayed_work_pending(&cur_if->pm_enable_work)) {
1368 cancel_delayed_work_sync(&cur_if->pm_enable_work);
1369 DHD_PM_WAKE_UNLOCK(dhd);
1370 }
1371
1372 if (add) {
1373 wq_duration = (WL_PM_ENABLE_TIMEOUT);
1374 }
1375
1376 /* It should schedule work item only if driver is up */
1377 if (dhd->up) {
1378 if (dhd_conf_get_pm(dhd) >= 0)
1379 pm = dhd_conf_get_pm(dhd);
1380 wl_ext_ioctl(cur_if->dev, WLC_SET_PM, &pm, sizeof(pm), 1);
1381 if (wq_duration) {
1382 if (schedule_delayed_work(&cur_if->pm_enable_work,
1383 msecs_to_jiffies((const unsigned int)wq_duration))) {
1384 DHD_PM_WAKE_LOCK_TIMEOUT(dhd, wq_duration);
1385 } else {
1386 AEXT_ERROR(cur_if->ifname, "Can't schedule pm work handler\n");
1387 }
1388 }
1389 }
1390 mutex_unlock(&cur_if->pm_sync);
1391
1392 }
1393
1394 static int
1395 wl_ext_parse_wep(char *key, struct wl_wsec_key *wsec_key)
1396 {
1397 char hex[] = "XX";
1398 unsigned char *data = wsec_key->data;
1399 char *keystr = key;
1400
1401 switch (strlen(keystr)) {
1402 case 5:
1403 case 13:
1404 case 16:
1405 wsec_key->len = strlen(keystr);
1406 memcpy(data, keystr, wsec_key->len + 1);
1407 break;
1408 case 12:
1409 case 28:
1410 case 34:
1411 case 66:
1412 /* strip leading 0x */
1413 if (!strnicmp(keystr, "0x", 2))
1414 keystr += 2;
1415 else
1416 return -1;
1417 /* fall through */
1418 case 10:
1419 case 26:
1420 case 32:
1421 case 64:
1422 wsec_key->len = strlen(keystr) / 2;
1423 while (*keystr) {
1424 strncpy(hex, keystr, 2);
1425 *data++ = (char) strtoul(hex, NULL, 16);
1426 keystr += 2;
1427 }
1428 break;
1429 default:
1430 return -1;
1431 }
1432
1433 switch (wsec_key->len) {
1434 case 5:
1435 wsec_key->algo = CRYPTO_ALGO_WEP1;
1436 break;
1437 case 13:
1438 wsec_key->algo = CRYPTO_ALGO_WEP128;
1439 break;
1440 case 16:
1441 /* default to AES-CCM */
1442 wsec_key->algo = CRYPTO_ALGO_AES_CCM;
1443 break;
1444 case 32:
1445 wsec_key->algo = CRYPTO_ALGO_TKIP;
1446 break;
1447 default:
1448 return -1;
1449 }
1450
1451 /* Set as primary wsec_key by default */
1452 wsec_key->flags |= WL_PRIMARY_KEY;
1453
1454 return 0;
1455 }
1456
1457 static int
1458 wl_ext_set_bgnmode(struct wl_if_info *cur_if)
1459 {
1460 struct net_device *dev = cur_if->dev;
1461 bgnmode_t bgnmode = cur_if->bgnmode;
1462 int val;
1463
1464 if (bgnmode == 0)
1465 return 0;
1466
1467 wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
1468 if (bgnmode == IEEE80211B) {
1469 wl_ext_iovar_setint(dev, "nmode", 0);
1470 val = 0;
1471 wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
1472 AEXT_TRACE(dev->name, "Network mode: B only\n");
1473 } else if (bgnmode == IEEE80211G) {
1474 wl_ext_iovar_setint(dev, "nmode", 0);
1475 val = 2;
1476 wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
1477 AEXT_TRACE(dev->name, "Network mode: G only\n");
1478 } else if (bgnmode == IEEE80211BG) {
1479 wl_ext_iovar_setint(dev, "nmode", 0);
1480 val = 1;
1481 wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
1482 AEXT_TRACE(dev->name, "Network mode: B/G mixed\n");
1483 } else if (bgnmode == IEEE80211BGN) {
1484 wl_ext_iovar_setint(dev, "nmode", 0);
1485 wl_ext_iovar_setint(dev, "nmode", 1);
1486 wl_ext_iovar_setint(dev, "vhtmode", 0);
1487 val = 1;
1488 wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
1489 AEXT_TRACE(dev->name, "Network mode: B/G/N mixed\n");
1490 } else if (bgnmode == IEEE80211BGNAC) {
1491 wl_ext_iovar_setint(dev, "nmode", 0);
1492 wl_ext_iovar_setint(dev, "nmode", 1);
1493 wl_ext_iovar_setint(dev, "vhtmode", 1);
1494 val = 1;
1495 wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
1496 AEXT_TRACE(dev->name, "Network mode: B/G/N/AC mixed\n");
1497 }
1498 wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
1499
1500 return 0;
1501 }
1502
1503 static int
1504 wl_ext_set_amode(struct wl_if_info *cur_if)
1505 {
1506 struct net_device *dev = cur_if->dev;
1507 authmode_t amode = cur_if->amode;
1508 int auth=0, wpa_auth=0;
1509
1510 #ifdef WLMESH
1511 if (cur_if->ifmode == IMESH_MODE) {
1512 if (amode == AUTH_SAE) {
1513 auth = WL_AUTH_OPEN_SYSTEM;
1514 wpa_auth = WPA2_AUTH_PSK;
1515 AEXT_INFO(dev->name, "SAE\n");
1516 } else {
1517 auth = WL_AUTH_OPEN_SYSTEM;
1518 wpa_auth = WPA_AUTH_DISABLED;
1519 AEXT_INFO(dev->name, "Open System\n");
1520 }
1521 } else
1522 #endif /* WLMESH */
1523 if (amode == AUTH_OPEN) {
1524 auth = WL_AUTH_OPEN_SYSTEM;
1525 wpa_auth = WPA_AUTH_DISABLED;
1526 AEXT_INFO(dev->name, "Open System\n");
1527 } else if (amode == AUTH_SHARED) {
1528 auth = WL_AUTH_SHARED_KEY;
1529 wpa_auth = WPA_AUTH_DISABLED;
1530 AEXT_INFO(dev->name, "Shared Key\n");
1531 } else if (amode == AUTH_WPAPSK) {
1532 auth = WL_AUTH_OPEN_SYSTEM;
1533 wpa_auth = WPA_AUTH_PSK;
1534 AEXT_INFO(dev->name, "WPA-PSK\n");
1535 } else if (amode == AUTH_WPA2PSK) {
1536 auth = WL_AUTH_OPEN_SYSTEM;
1537 wpa_auth = WPA2_AUTH_PSK;
1538 AEXT_INFO(dev->name, "WPA2-PSK\n");
1539 } else if (amode == AUTH_WPAWPA2PSK) {
1540 auth = WL_AUTH_OPEN_SYSTEM;
1541 wpa_auth = WPA2_AUTH_PSK | WPA_AUTH_PSK;
1542 AEXT_INFO(dev->name, "WPA/WPA2-PSK\n");
1543 }
1544 #ifdef WLMESH
1545 if (cur_if->ifmode == IMESH_MODE) {
1546 s32 val = WL_BSSTYPE_MESH;
1547 wl_ext_ioctl(dev, WLC_SET_INFRA, &val, sizeof(val), 1);
1548 } else
1549 #endif /* WLMESH */
1550 if (cur_if->ifmode == ISTA_MODE) {
1551 s32 val = WL_BSSTYPE_INFRA;
1552 wl_ext_ioctl(dev, WLC_SET_INFRA, &val, sizeof(val), 1);
1553 }
1554 wl_ext_iovar_setint(dev, "auth", auth);
1555
1556 wl_ext_iovar_setint(dev, "wpa_auth", wpa_auth);
1557
1558 return 0;
1559 }
1560
1561 static int
1562 wl_ext_set_emode(struct wl_apsta_params *apsta_params,
1563 struct wl_if_info *cur_if)
1564 {
1565 struct net_device *dev = cur_if->dev;
1566 int wsec=0;
1567 struct wl_wsec_key wsec_key;
1568 wsec_pmk_t psk;
1569 authmode_t amode = cur_if->amode;
1570 encmode_t emode = cur_if->emode;
1571 char *key = cur_if->key;
1572 struct dhd_pub *dhd = apsta_params->dhd;
1573
1574 memset(&wsec_key, 0, sizeof(wsec_key));
1575 memset(&psk, 0, sizeof(psk));
1576
1577 #ifdef WLMESH
1578 if (cur_if->ifmode == IMESH_MODE) {
1579 if (amode == AUTH_SAE) {
1580 wsec = AES_ENABLED;
1581 } else {
1582 wsec = WSEC_NONE;
1583 }
1584 } else
1585 #endif /* WLMESH */
1586 if (emode == ENC_NONE) {
1587 wsec = WSEC_NONE;
1588 AEXT_INFO(dev->name, "No securiy\n");
1589 } else if (emode == ENC_WEP) {
1590 wsec = WEP_ENABLED;
1591 wl_ext_parse_wep(key, &wsec_key);
1592 AEXT_INFO(dev->name, "WEP key \"%s\"\n", wsec_key.data);
1593 } else if (emode == ENC_TKIP) {
1594 wsec = TKIP_ENABLED;
1595 psk.key_len = strlen(key);
1596 psk.flags = WSEC_PASSPHRASE;
1597 memcpy(psk.key, key, strlen(key));
1598 AEXT_INFO(dev->name, "TKIP key \"%s\"\n", psk.key);
1599 } else if (emode == ENC_AES || amode == AUTH_SAE) {
1600 wsec = AES_ENABLED;
1601 psk.key_len = strlen(key);
1602 psk.flags = WSEC_PASSPHRASE;
1603 memcpy(psk.key, key, strlen(key));
1604 AEXT_INFO(dev->name, "AES key \"%s\"\n", psk.key);
1605 } else if (emode == ENC_TKIPAES) {
1606 wsec = TKIP_ENABLED | AES_ENABLED;
1607 psk.key_len = strlen(key);
1608 psk.flags = WSEC_PASSPHRASE;
1609 memcpy(psk.key, key, strlen(key));
1610 AEXT_INFO(dev->name, "TKIP/AES key \"%s\"\n", psk.key);
1611 }
1612 if (dhd->conf->chip == BCM43430_CHIP_ID && cur_if->ifidx > 0 && wsec >= 2 &&
1613 apsta_params->apstamode == ISTAAP_MODE) {
1614 wsec |= WSEC_SWFLAG; // terence 20180628: fix me, this is a workaround
1615 }
1616
1617 wl_ext_iovar_setint(dev, "wsec", wsec);
1618
1619 #ifdef WLMESH
1620 if (cur_if->ifmode == IMESH_MODE) {
1621 if (amode == AUTH_SAE) {
1622 s8 iovar_buf[WLC_IOCTL_SMLEN];
1623 AEXT_INFO(dev->name, "AES key \"%s\"\n", key);
1624 wl_ext_iovar_setint(dev, "mesh_auth_proto", 1);
1625 wl_ext_iovar_setint(dev, "mfp", WL_MFP_REQUIRED);
1626 wl_ext_iovar_setbuf(dev, "sae_password", key, strlen(key),
1627 iovar_buf, WLC_IOCTL_SMLEN, NULL);
1628 } else {
1629 AEXT_INFO(dev->name, "No securiy\n");
1630 wl_ext_iovar_setint(dev, "mesh_auth_proto", 0);
1631 wl_ext_iovar_setint(dev, "mfp", WL_MFP_NONE);
1632 }
1633 } else
1634 #endif /* WLMESH */
1635 if (emode == ENC_WEP) {
1636 wl_ext_ioctl(dev, WLC_SET_KEY, &wsec_key, sizeof(wsec_key), 1);
1637 } else if (emode == ENC_TKIP || emode == ENC_AES || emode == ENC_TKIPAES) {
1638 if (cur_if->ifmode == ISTA_MODE)
1639 wl_ext_iovar_setint(dev, "sup_wpa", 1);
1640 wl_ext_ioctl(dev, WLC_SET_WSEC_PMK, &psk, sizeof(psk), 1);
1641 }
1642
1643 return 0;
1644 }
1645
1646 static u32
1647 wl_ext_get_chanspec(struct wl_apsta_params *apsta_params,
1648 struct net_device *dev)
1649 {
1650 int ret = 0;
1651 struct ether_addr bssid;
1652 u32 chanspec = 0;
1653
1654 ret = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, sizeof(bssid), 0);
1655 if (ret != BCME_NOTASSOCIATED && memcmp(&ether_null, &bssid, ETHER_ADDR_LEN)) {
1656 if (wl_ext_iovar_getint(dev, "chanspec", (s32 *)&chanspec) == BCME_OK) {
1657 chanspec = wl_ext_chspec_driver_to_host(apsta_params->ioctl_ver, chanspec);
1658 return chanspec;
1659 }
1660 }
1661
1662 return 0;
1663 }
1664
1665 static uint16
1666 wl_ext_get_chan(struct wl_apsta_params *apsta_params, struct net_device *dev)
1667 {
1668 int ret = 0;
1669 uint16 chan = 0, ctl_chan;
1670 struct ether_addr bssid;
1671 u32 chanspec = 0;
1672
1673 ret = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, sizeof(bssid), 0);
1674 if (ret != BCME_NOTASSOCIATED && memcmp(&ether_null, &bssid, ETHER_ADDR_LEN)) {
1675 if (wl_ext_iovar_getint(dev, "chanspec", (s32 *)&chanspec) == BCME_OK) {
1676 chanspec = wl_ext_chspec_driver_to_host(apsta_params->ioctl_ver, chanspec);
1677 ctl_chan = wf_chspec_ctlchan(chanspec);
1678 chan = (u16)(ctl_chan & 0x00FF);
1679 return chan;
1680 }
1681 }
1682
1683 return 0;
1684 }
1685
1686 static chanspec_t
1687 wl_ext_chan_to_chanspec(struct wl_apsta_params *apsta_params,
1688 struct net_device *dev, uint16 channel)
1689 {
1690 s32 _chan = channel;
1691 chanspec_t chspec = 0;
1692 chanspec_t fw_chspec = 0;
1693 u32 bw = WL_CHANSPEC_BW_20;
1694 s32 err = BCME_OK;
1695 s32 bw_cap = 0;
1696 s8 iovar_buf[WLC_IOCTL_SMLEN];
1697 struct {
1698 u32 band;
1699 u32 bw_cap;
1700 } param = {0, 0};
1701 uint band;
1702
1703 if (_chan <= CH_MAX_2G_CHANNEL)
1704 band = IEEE80211_BAND_2GHZ;
1705 else
1706 band = IEEE80211_BAND_5GHZ;
1707
1708 if (band == IEEE80211_BAND_5GHZ) {
1709 param.band = WLC_BAND_5G;
1710 err = wl_ext_iovar_getbuf(dev, "bw_cap", &param, sizeof(param),
1711 iovar_buf, WLC_IOCTL_SMLEN, NULL);
1712 if (err) {
1713 if (err != BCME_UNSUPPORTED) {
1714 AEXT_ERROR(dev->name, "bw_cap failed, %d\n", err);
1715 return err;
1716 } else {
1717 err = wl_ext_iovar_getint(dev, "mimo_bw_cap", &bw_cap);
1718 if (bw_cap != WLC_N_BW_20ALL)
1719 bw = WL_CHANSPEC_BW_40;
1720 }
1721 } else {
1722 if (WL_BW_CAP_80MHZ(iovar_buf[0]))
1723 bw = WL_CHANSPEC_BW_80;
1724 else if (WL_BW_CAP_40MHZ(iovar_buf[0]))
1725 bw = WL_CHANSPEC_BW_40;
1726 else
1727 bw = WL_CHANSPEC_BW_20;
1728 }
1729 }
1730 else if (band == IEEE80211_BAND_2GHZ)
1731 bw = WL_CHANSPEC_BW_20;
1732
1733 set_channel:
1734 chspec = wf_channel2chspec(_chan, bw);
1735 if (wf_chspec_valid(chspec)) {
1736 fw_chspec = wl_ext_chspec_host_to_driver(apsta_params->ioctl_ver, chspec);
1737 if (fw_chspec == INVCHANSPEC) {
1738 AEXT_ERROR(dev->name, "failed to convert host chanspec to fw chanspec\n");
1739 fw_chspec = 0;
1740 }
1741 } else {
1742 if (bw == WL_CHANSPEC_BW_80)
1743 bw = WL_CHANSPEC_BW_40;
1744 else if (bw == WL_CHANSPEC_BW_40)
1745 bw = WL_CHANSPEC_BW_20;
1746 else
1747 bw = 0;
1748 if (bw)
1749 goto set_channel;
1750 AEXT_ERROR(dev->name, "Invalid chanspec 0x%x\n", chspec);
1751 err = BCME_ERROR;
1752 }
1753
1754 return fw_chspec;
1755 }
1756
1757 static bool
1758 wl_ext_radar_detect(struct net_device *dev)
1759 {
1760 int ret = BCME_OK;
1761 bool radar = FALSE;
1762 s32 val = 0;
1763
1764 if ((ret = wldev_ioctl(dev, WLC_GET_RADAR, &val, sizeof(int), false) == 0)) {
1765 radar = TRUE;
1766 }
1767
1768 return radar;
1769 }
1770
1771 static struct wl_if_info *
1772 wl_ext_if_enabled(struct wl_apsta_params *apsta_params, ifmode_t ifmode)
1773 {
1774 struct wl_if_info *tmp_if, *target_if = NULL;
1775 int i;
1776
1777 for (i=0; i<MAX_IF_NUM; i++) {
1778 tmp_if = &apsta_params->if_info[i];
1779 if (tmp_if && tmp_if->ifmode == ifmode &&
1780 wl_get_isam_status(tmp_if, IF_READY)) {
1781 if (wl_ext_get_chan(apsta_params, tmp_if->dev)) {
1782 target_if = tmp_if;
1783 break;
1784 }
1785 }
1786 }
1787
1788 return target_if;
1789 }
1790
1791 #ifndef WL_STATIC_IF
1792 s32
1793 wl_ext_add_del_bss(struct net_device *ndev, s32 bsscfg_idx,
1794 int iftype, s32 del, u8 *addr)
1795 {
1796 s32 ret = BCME_OK;
1797 s32 val = 0;
1798 u8 ioctl_buf[WLC_IOCTL_SMLEN];
1799 struct {
1800 s32 cfg;
1801 s32 val;
1802 struct ether_addr ea;
1803 } bss_setbuf;
1804
1805 AEXT_TRACE(ndev->name, "wl_iftype:%d del:%d \n", iftype, del);
1806
1807 bzero(&bss_setbuf, sizeof(bss_setbuf));
1808
1809 /* AP=2, STA=3, up=1, down=0, val=-1 */
1810 if (del) {
1811 val = WLC_AP_IOV_OP_DELETE;
1812 } else if (iftype == WL_INTERFACE_CREATE_AP) {
1813 /* Add/role change to AP Interface */
1814 AEXT_TRACE(ndev->name, "Adding AP Interface\n");
1815 val = WLC_AP_IOV_OP_MANUAL_AP_BSSCFG_CREATE;
1816 } else if (iftype == WL_INTERFACE_CREATE_STA) {
1817 /* Add/role change to STA Interface */
1818 AEXT_TRACE(ndev->name, "Adding STA Interface\n");
1819 val = WLC_AP_IOV_OP_MANUAL_STA_BSSCFG_CREATE;
1820 } else {
1821 AEXT_ERROR(ndev->name, "add_del_bss NOT supported for IFACE type:0x%x", iftype);
1822 return -EINVAL;
1823 }
1824
1825 if (!del) {
1826 wl_ext_bss_iovar_war(ndev, &val);
1827 }
1828
1829 bss_setbuf.cfg = htod32(bsscfg_idx);
1830 bss_setbuf.val = htod32(val);
1831
1832 if (addr) {
1833 memcpy(&bss_setbuf.ea.octet, addr, ETH_ALEN);
1834 }
1835
1836 AEXT_INFO(ndev->name, "wl bss %d bssidx:%d\n", val, bsscfg_idx);
1837 ret = wl_ext_iovar_setbuf(ndev, "bss", &bss_setbuf, sizeof(bss_setbuf),
1838 ioctl_buf, WLC_IOCTL_SMLEN, NULL);
1839 if (ret != 0)
1840 AEXT_ERROR(ndev->name, "'bss %d' failed with %d\n", val, ret);
1841
1842 return ret;
1843 }
1844
1845 static int
1846 wl_ext_interface_ops(struct net_device *dev,
1847 struct wl_apsta_params *apsta_params, int iftype, u8 *addr)
1848 {
1849 s32 ret;
1850 struct wl_interface_create_v2 iface;
1851 wl_interface_create_v3_t iface_v3;
1852 struct wl_interface_info_v1 *info;
1853 wl_interface_info_v2_t *info_v2;
1854 uint32 ifflags = 0;
1855 bool use_iface_info_v2 = false;
1856 u8 ioctl_buf[WLC_IOCTL_SMLEN];
1857 wl_wlc_version_t wlc_ver;
1858
1859 /* Interface create */
1860 bzero(&iface, sizeof(iface));
1861
1862 if (addr) {
1863 ifflags |= WL_INTERFACE_MAC_USE;
1864 }
1865
1866 ret = wldev_iovar_getbuf(dev, "wlc_ver", NULL, 0,
1867 &wlc_ver, sizeof(wl_wlc_version_t), NULL);
1868 if ((ret == BCME_OK) && (wlc_ver.wlc_ver_major >= 5)) {
1869 ret = wldev_iovar_getbuf(dev, "interface_create",
1870 &iface, sizeof(struct wl_interface_create_v2),
1871 ioctl_buf, sizeof(ioctl_buf), NULL);
1872 if ((ret == BCME_OK) && (*((uint32 *)ioctl_buf) == WL_INTERFACE_CREATE_VER_3)) {
1873 use_iface_info_v2 = true;
1874 bzero(&iface_v3, sizeof(wl_interface_create_v3_t));
1875 iface_v3.ver = WL_INTERFACE_CREATE_VER_3;
1876 iface_v3.iftype = iftype;
1877 iface_v3.flags = ifflags;
1878 if (addr) {
1879 memcpy(&iface_v3.mac_addr.octet, addr, ETH_ALEN);
1880 }
1881 ret = wl_ext_iovar_getbuf(dev, "interface_create",
1882 &iface_v3, sizeof(wl_interface_create_v3_t),
1883 ioctl_buf, sizeof(ioctl_buf), NULL);
1884 if (unlikely(ret)) {
1885 AEXT_ERROR(dev->name, "Interface v3 create failed!! ret %d\n", ret);
1886 return ret;
1887 }
1888 }
1889 }
1890
1891 /* success case */
1892 if (use_iface_info_v2 == true) {
1893 info_v2 = (wl_interface_info_v2_t *)ioctl_buf;
1894 ret = info_v2->bsscfgidx;
1895 } else {
1896 /* Use v1 struct */
1897 iface.ver = WL_INTERFACE_CREATE_VER_2;
1898 iface.iftype = iftype;
1899 iface.flags = ifflags;
1900 if (addr) {
1901 memcpy(&iface.mac_addr.octet, addr, ETH_ALEN);
1902 }
1903 ret = wldev_iovar_getbuf(dev, "interface_create",
1904 &iface, sizeof(struct wl_interface_create_v2),
1905 ioctl_buf, sizeof(ioctl_buf), NULL);
1906 if (ret == BCME_OK) {
1907 info = (struct wl_interface_info_v1 *)ioctl_buf;
1908 ret = info->bsscfgidx;
1909 }
1910 }
1911
1912 AEXT_INFO(dev->name, "wl interface create success!! bssidx:%d \n", ret);
1913 return ret;
1914 }
1915
1916 static void
1917 wl_ext_wait_netif_change(struct wl_apsta_params *apsta_params,
1918 struct wl_if_info *cur_if)
1919 {
1920 rtnl_unlock();
1921 wait_event_interruptible_timeout(apsta_params->netif_change_event,
1922 wl_get_isam_status(cur_if, IF_READY),
1923 msecs_to_jiffies(MAX_AP_LINK_WAIT_TIME));
1924 rtnl_lock();
1925 }
1926
1927 static void
1928 wl_ext_interface_create(struct net_device *dev, struct wl_apsta_params *apsta_params,
1929 struct wl_if_info *cur_if, int iftype, u8 *addr)
1930 {
1931 s32 ret;
1932
1933 wl_set_isam_status(cur_if, IF_ADDING);
1934 ret = wl_ext_interface_ops(dev, apsta_params, iftype, addr);
1935 if (ret == BCME_UNSUPPORTED) {
1936 wl_ext_add_del_bss(dev, 1, iftype, 0, addr);
1937 }
1938 wl_ext_wait_netif_change(apsta_params, cur_if);
1939 }
1940
1941 static void
1942 wl_ext_iapsta_intf_add(struct net_device *dev, struct wl_apsta_params *apsta_params)
1943 {
1944 struct dhd_pub *dhd;
1945 apstamode_t apstamode = apsta_params->apstamode;
1946 struct wl_if_info *cur_if;
1947 s8 iovar_buf[WLC_IOCTL_SMLEN];
1948 wl_p2p_if_t ifreq;
1949 struct ether_addr mac_addr;
1950
1951 dhd = dhd_get_pub(dev);
1952 bzero(&mac_addr, sizeof(mac_addr));
1953
1954 if (apstamode == ISTAAP_MODE) {
1955 cur_if = &apsta_params->if_info[IF_VIF];
1956 wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
1957 }
1958 else if (apstamode == ISTAGO_MODE) {
1959 bzero(&ifreq, sizeof(wl_p2p_if_t));
1960 ifreq.type = htod32(WL_P2P_IF_GO);
1961 cur_if = &apsta_params->if_info[IF_VIF];
1962 wl_set_isam_status(cur_if, IF_ADDING);
1963 wl_ext_iovar_setbuf(dev, "p2p_ifadd", &ifreq, sizeof(ifreq),
1964 iovar_buf, WLC_IOCTL_SMLEN, NULL);
1965 wl_ext_wait_netif_change(apsta_params, cur_if);
1966 }
1967 else if (apstamode == ISTASTA_MODE) {
1968 cur_if = &apsta_params->if_info[IF_VIF];
1969 memcpy(&mac_addr, dev->dev_addr, ETHER_ADDR_LEN);
1970 mac_addr.octet[0] |= 0x02;
1971 wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_STA,
1972 (u8*)&mac_addr);
1973 }
1974 else if (apstamode == IDUALAP_MODE) {
1975 cur_if = &apsta_params->if_info[IF_VIF];
1976 wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
1977 }
1978 else if (apstamode == ISTAAPAP_MODE) {
1979 u8 rand_bytes[2] = {0, };
1980 get_random_bytes(&rand_bytes, sizeof(rand_bytes));
1981 cur_if = &apsta_params->if_info[IF_VIF];
1982 memcpy(&mac_addr, dev->dev_addr, ETHER_ADDR_LEN);
1983 mac_addr.octet[0] |= 0x02;
1984 mac_addr.octet[5] += 0x01;
1985 memcpy(&mac_addr.octet[3], rand_bytes, sizeof(rand_bytes));
1986 wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP,
1987 (u8*)&mac_addr);
1988 cur_if = &apsta_params->if_info[IF_VIF2];
1989 memcpy(&mac_addr, dev->dev_addr, ETHER_ADDR_LEN);
1990 mac_addr.octet[0] |= 0x02;
1991 mac_addr.octet[5] += 0x02;
1992 memcpy(&mac_addr.octet[3], rand_bytes, sizeof(rand_bytes));
1993 wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP,
1994 (u8*)&mac_addr);
1995 }
1996 #ifdef WLMESH
1997 else if (apstamode == ISTAMESH_MODE) {
1998 cur_if = &apsta_params->if_info[IF_VIF];
1999 wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_STA, NULL);
2000 }
2001 else if (apstamode == IMESHAP_MODE) {
2002 cur_if = &apsta_params->if_info[IF_VIF];
2003 wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
2004 }
2005 else if (apstamode == ISTAAPMESH_MODE) {
2006 cur_if = &apsta_params->if_info[IF_VIF];
2007 wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
2008 cur_if = &apsta_params->if_info[IF_VIF2];
2009 wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_STA, NULL);
2010 }
2011 else if (apstamode == IMESHAPAP_MODE) {
2012 cur_if = &apsta_params->if_info[IF_VIF];
2013 wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
2014 cur_if = &apsta_params->if_info[IF_VIF2];
2015 wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
2016 }
2017 #endif /* WLMESH */
2018
2019 }
2020 #endif /* WL_STATIC_IF */
2021
2022 static void
2023 wl_ext_iapsta_preinit(struct net_device *dev, struct wl_apsta_params *apsta_params)
2024 {
2025 struct dhd_pub *dhd;
2026 apstamode_t apstamode = apsta_params->apstamode;
2027 struct wl_if_info *cur_if;
2028 s8 iovar_buf[WLC_IOCTL_SMLEN];
2029 s32 val = 0;
2030 int i;
2031
2032 dhd = dhd_get_pub(dev);
2033
2034 for (i=0; i<MAX_IF_NUM; i++) {
2035 cur_if = &apsta_params->if_info[i];
2036 if (i >= 1 && !strlen(cur_if->ifname))
2037 snprintf(cur_if->ifname, IFNAMSIZ, "wlan%d", i);
2038 if (cur_if->ifmode == ISTA_MODE) {
2039 cur_if->channel = 0;
2040 cur_if->maxassoc = -1;
2041 cur_if->prio = PRIO_STA;
2042 cur_if->prefix = 'S';
2043 snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_sta");
2044 } else if (cur_if->ifmode == IAP_MODE) {
2045 cur_if->channel = 1;
2046 cur_if->maxassoc = -1;
2047 cur_if->prio = PRIO_AP;
2048 cur_if->prefix = 'A';
2049 snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_ap");
2050 #ifdef WLMESH
2051 } else if (cur_if->ifmode == IMESH_MODE) {
2052 cur_if->channel = 1;
2053 cur_if->maxassoc = -1;
2054 cur_if->prio = PRIO_MESH;
2055 cur_if->prefix = 'M';
2056 snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_mesh");
2057 #ifdef WL_ESCAN
2058 if (i == 0 && apsta_params->macs)
2059 wl_mesh_escan_attach(dhd, cur_if);
2060 #endif /* WL_ESCAN */
2061 #endif /* WLMESH */
2062 }
2063 }
2064
2065 if (FW_SUPPORTED(dhd, rsdb)) {
2066 if (apstamode == IDUALAP_MODE)
2067 apsta_params->rsdb = -1;
2068 else if (apstamode == ISTAAPAP_MODE)
2069 apsta_params->rsdb = 0;
2070 if (apstamode == ISTAAPAP_MODE || apstamode == IDUALAP_MODE ||
2071 apstamode == IMESHONLY_MODE || apstamode == ISTAMESH_MODE ||
2072 apstamode == IMESHAP_MODE || apstamode == ISTAAPMESH_MODE ||
2073 apstamode == IMESHAPAP_MODE) {
2074 wl_config_t rsdb_mode_cfg = {0, 0};
2075 rsdb_mode_cfg.config = apsta_params->rsdb;
2076 AEXT_INFO(dev->name, "set rsdb_mode %d\n", rsdb_mode_cfg.config);
2077 wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
2078 wl_ext_iovar_setbuf(dev, "rsdb_mode", &rsdb_mode_cfg,
2079 sizeof(rsdb_mode_cfg), iovar_buf, sizeof(iovar_buf), NULL);
2080 wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
2081 }
2082 } else {
2083 apsta_params->rsdb = 0;
2084 }
2085
2086 if (apstamode == ISTAONLY_MODE) {
2087 wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
2088 wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls
2089 // don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off
2090 wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
2091 } else if (apstamode == IAPONLY_MODE) {
2092 wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
2093 #ifdef ARP_OFFLOAD_SUPPORT
2094 /* IF SoftAP is enabled, disable arpoe */
2095 dhd_arp_offload_set(dhd, 0);
2096 dhd_arp_offload_enable(dhd, FALSE);
2097 #endif /* ARP_OFFLOAD_SUPPORT */
2098 wl_ext_iovar_setint(dev, "mpc", 0);
2099 wl_ext_iovar_setint(dev, "apsta", 0);
2100 val = 1;
2101 wl_ext_ioctl(dev, WLC_SET_AP, &val, sizeof(val), 1);
2102 #ifdef PROP_TXSTATUS_VSDB
2103 #if defined(BCMSDIO)
2104 if (!(FW_SUPPORTED(dhd, rsdb)) && !disable_proptx) {
2105 bool enabled;
2106 dhd_wlfc_get_enable(dhd, &enabled);
2107 if (!enabled) {
2108 dhd_wlfc_init(dhd);
2109 wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
2110 }
2111 }
2112 #endif /* BCMSDIO */
2113 #endif /* PROP_TXSTATUS_VSDB */
2114 }
2115 else if (apstamode == ISTAAP_MODE) {
2116 wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
2117 wl_ext_iovar_setint(dev, "mpc", 0);
2118 wl_ext_iovar_setint(dev, "apsta", 1);
2119 wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
2120 }
2121 else if (apstamode == ISTAGO_MODE) {
2122 wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
2123 wl_ext_iovar_setint(dev, "apsta", 1);
2124 wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
2125 }
2126 else if (apstamode == ISTASTA_MODE) {
2127 }
2128 else if (apstamode == IDUALAP_MODE) {
2129 wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
2130 /* IF SoftAP is enabled, disable arpoe or wlan1 will ping fail */
2131 #ifdef ARP_OFFLOAD_SUPPORT
2132 /* IF SoftAP is enabled, disable arpoe */
2133 dhd_arp_offload_set(dhd, 0);
2134 dhd_arp_offload_enable(dhd, FALSE);
2135 #endif /* ARP_OFFLOAD_SUPPORT */
2136 wl_ext_iovar_setint(dev, "mpc", 0);
2137 wl_ext_iovar_setint(dev, "mbcn", 1);
2138 wl_ext_iovar_setint(dev, "apsta", 0);
2139 wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
2140 val = 1;
2141 wl_ext_ioctl(dev, WLC_SET_AP, &val, sizeof(val), 1);
2142 }
2143 else if (apstamode == ISTAAPAP_MODE) {
2144 wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
2145 wl_ext_iovar_setint(dev, "mpc", 0);
2146 wl_ext_iovar_setint(dev, "mbss", 1);
2147 wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls
2148 wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
2149 // don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off
2150 }
2151 #ifdef WLMESH
2152 else if (apstamode == IMESHONLY_MODE || apstamode == ISTAMESH_MODE ||
2153 apstamode == IMESHAP_MODE || apstamode == ISTAAPMESH_MODE ||
2154 apstamode == IMESHAPAP_MODE) {
2155 int pm = 0;
2156 wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
2157 wl_ext_iovar_setint(dev, "mpc", 0);
2158 if (apstamode == IMESHONLY_MODE)
2159 wl_ext_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), 1);
2160 else
2161 wl_ext_iovar_setint(dev, "mbcn", 1);
2162 wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls
2163 wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
2164 // don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off
2165 }
2166 #endif /* WLMESH */
2167
2168 wl_ext_get_ioctl_ver(dev, &apsta_params->ioctl_ver);
2169 apsta_params->init = TRUE;
2170
2171 WL_MSG(dev->name, "apstamode=%d\n", apstamode);
2172 }
2173
2174 static int
2175 wl_ext_isam_param(struct net_device *dev, char *command, int total_len)
2176 {
2177 struct dhd_pub *dhd = dhd_get_pub(dev);
2178 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
2179 int ret = -1;
2180 char *pick_tmp, *data, *param;
2181 int bytes_written=-1;
2182
2183 AEXT_TRACE(dev->name, "command=%s, len=%d\n", command, total_len);
2184
2185 pick_tmp = command;
2186 param = bcmstrtok(&pick_tmp, " ", 0); // pick isam_param
2187 param = bcmstrtok(&pick_tmp, " ", 0); // pick cmd
2188 while (param != NULL) {
2189 data = bcmstrtok(&pick_tmp, " ", 0); // pick data
2190 if (!strcmp(param, "acs")) {
2191 if (data) {
2192 apsta_params->acs = simple_strtol(data, NULL, 0);
2193 ret = 0;
2194 } else {
2195 bytes_written = snprintf(command, total_len, "%d", apsta_params->acs);
2196 ret = bytes_written;
2197 goto exit;
2198 }
2199 }
2200 param = bcmstrtok(&pick_tmp, " ", 0); // pick cmd
2201 }
2202
2203 exit:
2204 return ret;
2205 }
2206
2207 static int
2208 wl_ext_isam_init(struct net_device *dev, char *command, int total_len)
2209 {
2210 struct dhd_pub *dhd = dhd_get_pub(dev);
2211 char *pch, *pick_tmp, *pick_tmp2, *param;
2212 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
2213 int i;
2214
2215 if (apsta_params->init) {
2216 AEXT_ERROR(dev->name, "don't init twice\n");
2217 return -1;
2218 }
2219 AEXT_TRACE(dev->name, "command=%s, len=%d\n", command, total_len);
2220
2221 pick_tmp = command;
2222 param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_init
2223 param = bcmstrtok(&pick_tmp, " ", 0);
2224 while (param != NULL) {
2225 pick_tmp2 = bcmstrtok(&pick_tmp, " ", 0);
2226 if (!pick_tmp2) {
2227 AEXT_ERROR(dev->name, "wrong param %s\n", param);
2228 return -1;
2229 }
2230 if (!strcmp(param, "mode")) {
2231 pch = NULL;
2232 if (!strcmp(pick_tmp2, "sta")) {
2233 apsta_params->apstamode = ISTAONLY_MODE;
2234 } else if (!strcmp(pick_tmp2, "ap")) {
2235 apsta_params->apstamode = IAPONLY_MODE;
2236 } else if (!strcmp(pick_tmp2, "sta-ap")) {
2237 apsta_params->apstamode = ISTAAP_MODE;
2238 } else if (!strcmp(pick_tmp2, "sta-sta")) {
2239 apsta_params->apstamode = ISTASTA_MODE;
2240 apsta_params->vsdb = TRUE;
2241 } else if (!strcmp(pick_tmp2, "ap-ap")) {
2242 apsta_params->apstamode = IDUALAP_MODE;
2243 } else if (!strcmp(pick_tmp2, "sta-ap-ap")) {
2244 apsta_params->apstamode = ISTAAPAP_MODE;
2245 } else if (!strcmp(pick_tmp2, "apsta")) {
2246 apsta_params->apstamode = ISTAAP_MODE;
2247 apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE;
2248 apsta_params->if_info[IF_VIF].ifmode = IAP_MODE;
2249 } else if (!strcmp(pick_tmp2, "dualap")) {
2250 apsta_params->apstamode = IDUALAP_MODE;
2251 apsta_params->if_info[IF_PIF].ifmode = IAP_MODE;
2252 apsta_params->if_info[IF_VIF].ifmode = IAP_MODE;
2253 } else if (!strcmp(pick_tmp2, "sta-go") ||
2254 !strcmp(pick_tmp2, "gosta")) {
2255 if (!FW_SUPPORTED(dhd, p2p)) {
2256 return -1;
2257 }
2258 apsta_params->apstamode = ISTAGO_MODE;
2259 apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE;
2260 apsta_params->if_info[IF_VIF].ifmode = IAP_MODE;
2261 #ifdef WLMESH
2262 } else if (!strcmp(pick_tmp2, "mesh")) {
2263 apsta_params->apstamode = IMESHONLY_MODE;
2264 } else if (!strcmp(pick_tmp2, "sta-mesh")) {
2265 apsta_params->apstamode = ISTAMESH_MODE;
2266 } else if (!strcmp(pick_tmp2, "sta-ap-mesh")) {
2267 apsta_params->apstamode = ISTAAPMESH_MODE;
2268 } else if (!strcmp(pick_tmp2, "mesh-ap")) {
2269 apsta_params->apstamode = IMESHAP_MODE;
2270 } else if (!strcmp(pick_tmp2, "mesh-ap-ap")) {
2271 apsta_params->apstamode = IMESHAPAP_MODE;
2272 #endif /* WLMESH */
2273 } else {
2274 AEXT_ERROR(dev->name, "mode [sta|ap|sta-ap|ap-ap]\n");
2275 return -1;
2276 }
2277 pch = bcmstrtok(&pick_tmp2, " -", 0);
2278 for (i=0; i<MAX_IF_NUM && pch; i++) {
2279 if (!strcmp(pch, "sta"))
2280 apsta_params->if_info[i].ifmode = ISTA_MODE;
2281 else if (!strcmp(pch, "ap"))
2282 apsta_params->if_info[i].ifmode = IAP_MODE;
2283 #ifdef WLMESH
2284 else if (!strcmp(pch, "mesh")) {
2285 if (dhd->conf->fw_type != FW_TYPE_MESH) {
2286 AEXT_ERROR(dev->name, "wrong fw type\n");
2287 return -1;
2288 }
2289 apsta_params->if_info[i].ifmode = IMESH_MODE;
2290 }
2291 #endif /* WLMESH */
2292 pch = bcmstrtok(&pick_tmp2, " -", 0);
2293 }
2294 }
2295 else if (!strcmp(param, "rsdb")) {
2296 apsta_params->rsdb = (int)simple_strtol(pick_tmp2, NULL, 0);
2297 } else if (!strcmp(param, "vsdb")) {
2298 if (!strcmp(pick_tmp2, "y")) {
2299 apsta_params->vsdb = TRUE;
2300 } else if (!strcmp(pick_tmp2, "n")) {
2301 apsta_params->vsdb = FALSE;
2302 } else {
2303 AEXT_ERROR(dev->name, "vsdb [y|n]\n");
2304 return -1;
2305 }
2306 } else if (!strcmp(param, "csa")) {
2307 apsta_params->csa = (int)simple_strtol(pick_tmp2, NULL, 0);
2308 } else if (!strcmp(param, "acs")) {
2309 apsta_params->acs = (int)simple_strtol(pick_tmp2, NULL, 0);
2310 #if defined(WLMESH) && defined(WL_ESCAN)
2311 } else if (!strcmp(param, "macs")) {
2312 apsta_params->macs = (int)simple_strtol(pick_tmp2, NULL, 0);
2313 #endif /* WLMESH && WL_ESCAN */
2314 } else if (!strcmp(param, "ifname")) {
2315 pch = NULL;
2316 pch = bcmstrtok(&pick_tmp2, " -", 0);
2317 for (i=0; i<MAX_IF_NUM && pch; i++) {
2318 strcpy(apsta_params->if_info[i].ifname, pch);
2319 pch = bcmstrtok(&pick_tmp2, " -", 0);
2320 }
2321 } else if (!strcmp(param, "vifname")) {
2322 strcpy(apsta_params->if_info[IF_VIF].ifname, pick_tmp2);
2323 }
2324 param = bcmstrtok(&pick_tmp, " ", 0);
2325 }
2326
2327 if (apsta_params->apstamode == 0) {
2328 AEXT_ERROR(dev->name, "mode [sta|ap|sta-ap|ap-ap]\n");
2329 return -1;
2330 }
2331
2332 wl_ext_iapsta_preinit(dev, apsta_params);
2333 #ifndef WL_STATIC_IF
2334 wl_ext_iapsta_intf_add(dev, apsta_params);
2335 #endif /* WL_STATIC_IF */
2336
2337 return 0;
2338 }
2339
2340 static int
2341 wl_ext_parse_config(struct wl_if_info *cur_if, char *command, char **pick_next)
2342 {
2343 char *pch, *pick_tmp;
2344 char name[20], data[100];
2345 int i, j, len;
2346 char *ifname_head = NULL;
2347
2348 typedef struct config_map_t {
2349 char name[20];
2350 char *head;
2351 char *tail;
2352 } config_map_t;
2353
2354 config_map_t config_map [] = {
2355 {" ifname ", NULL, NULL},
2356 {" ssid ", NULL, NULL},
2357 {" bssid ", NULL, NULL},
2358 {" bgnmode ", NULL, NULL},
2359 {" hidden ", NULL, NULL},
2360 {" maxassoc ", NULL, NULL},
2361 {" chan ", NULL, NULL},
2362 {" amode ", NULL, NULL},
2363 {" emode ", NULL, NULL},
2364 {" key ", NULL, NULL},
2365 };
2366 config_map_t *row, *row_prev;
2367
2368 pick_tmp = command;
2369
2370 // reset head and tail
2371 for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]); i++) {
2372 row = &config_map[i];
2373 row->head = NULL;
2374 row->tail = pick_tmp + strlen(pick_tmp);
2375 }
2376
2377 // pick head
2378 for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]); i++) {
2379 row = &config_map[i];
2380 pch = strstr(pick_tmp, row->name);
2381 if (pch) {
2382 row->head = pch;
2383 }
2384 }
2385
2386 // sort by head
2387 for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]) - 1; i++) {
2388 row_prev = &config_map[i];
2389 for (j = i+1; j < sizeof(config_map)/sizeof(config_map[0]); j++) {
2390 row = &config_map[j];
2391 if (row->head < row_prev->head) {
2392 strcpy(name, row_prev->name);
2393 strcpy(row_prev->name, row->name);
2394 strcpy(row->name, name);
2395 pch = row_prev->head;
2396 row_prev->head = row->head;
2397 row->head = pch;
2398 }
2399 }
2400 }
2401
2402 // pick tail
2403 for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]) - 1; i++) {
2404 row_prev = &config_map[i];
2405 row = &config_map[i+1];
2406 if (row_prev->head) {
2407 row_prev->tail = row->head;
2408 }
2409 }
2410
2411 // remove name from head
2412 for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]); i++) {
2413 row = &config_map[i];
2414 if (row->head) {
2415 if (!strcmp(row->name, " ifname ")) {
2416 ifname_head = row->head + 1;
2417 break;
2418 }
2419 row->head += strlen(row->name);
2420 }
2421 }
2422
2423 for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]); i++) {
2424 row = &config_map[i];
2425 if (row->head) {
2426 memset(data, 0, sizeof(data));
2427 if (row->tail && row->tail > row->head) {
2428 strncpy(data, row->head, row->tail-row->head);
2429 } else {
2430 strcpy(data, row->head);
2431 }
2432 pick_tmp = data;
2433
2434 if (!strcmp(row->name, " ifname ")) {
2435 break;
2436 } else if (!strcmp(row->name, " ssid ")) {
2437 len = strlen(pick_tmp);
2438 memset(cur_if->ssid, 0, sizeof(cur_if->ssid));
2439 if (pick_tmp[0] == '"' && pick_tmp[len-1] == '"')
2440 strncpy(cur_if->ssid, &pick_tmp[1], len-2);
2441 else
2442 strcpy(cur_if->ssid, pick_tmp);
2443 } else if (!strcmp(row->name, " bssid ")) {
2444 pch = bcmstrtok(&pick_tmp, ": ", 0);
2445 for (j=0; j<6 && pch; j++) {
2446 ((u8 *)&cur_if->bssid)[j] = (int)simple_strtol(pch, NULL, 16);
2447 pch = bcmstrtok(&pick_tmp, ": ", 0);
2448 }
2449 } else if (!strcmp(row->name, " bgnmode ")) {
2450 if (!strcmp(pick_tmp, "b"))
2451 cur_if->bgnmode = IEEE80211B;
2452 else if (!strcmp(pick_tmp, "g"))
2453 cur_if->bgnmode = IEEE80211G;
2454 else if (!strcmp(pick_tmp, "bg"))
2455 cur_if->bgnmode = IEEE80211BG;
2456 else if (!strcmp(pick_tmp, "bgn"))
2457 cur_if->bgnmode = IEEE80211BGN;
2458 else if (!strcmp(pick_tmp, "bgnac"))
2459 cur_if->bgnmode = IEEE80211BGNAC;
2460 else {
2461 AEXT_ERROR(cur_if->dev->name, "bgnmode [b|g|bg|bgn|bgnac]\n");
2462 return -1;
2463 }
2464 } else if (!strcmp(row->name, " hidden ")) {
2465 if (!strcmp(pick_tmp, "n"))
2466 cur_if->hidden = 0;
2467 else if (!strcmp(pick_tmp, "y"))
2468 cur_if->hidden = 1;
2469 else {
2470 AEXT_ERROR(cur_if->dev->name, "hidden [y|n]\n");
2471 return -1;
2472 }
2473 } else if (!strcmp(row->name, " maxassoc ")) {
2474 cur_if->maxassoc = (int)simple_strtol(pick_tmp, NULL, 10);
2475 } else if (!strcmp(row->name, " chan ")) {
2476 cur_if->channel = (int)simple_strtol(pick_tmp, NULL, 10);
2477 } else if (!strcmp(row->name, " amode ")) {
2478 if (!strcmp(pick_tmp, "open"))
2479 cur_if->amode = AUTH_OPEN;
2480 else if (!strcmp(pick_tmp, "shared"))
2481 cur_if->amode = AUTH_SHARED;
2482 else if (!strcmp(pick_tmp, "wpapsk"))
2483 cur_if->amode = AUTH_WPAPSK;
2484 else if (!strcmp(pick_tmp, "wpa2psk"))
2485 cur_if->amode = AUTH_WPA2PSK;
2486 else if (!strcmp(pick_tmp, "wpawpa2psk"))
2487 cur_if->amode = AUTH_WPAWPA2PSK;
2488 else if (!strcmp(pick_tmp, "sae"))
2489 cur_if->amode = AUTH_SAE;
2490 else {
2491 AEXT_ERROR(cur_if->dev->name, "amode [open|shared|wpapsk|wpa2psk|wpawpa2psk]\n");
2492 return -1;
2493 }
2494 } else if (!strcmp(row->name, " emode ")) {
2495 if (!strcmp(pick_tmp, "none"))
2496 cur_if->emode = ENC_NONE;
2497 else if (!strcmp(pick_tmp, "wep"))
2498 cur_if->emode = ENC_WEP;
2499 else if (!strcmp(pick_tmp, "tkip"))
2500 cur_if->emode = ENC_TKIP;
2501 else if (!strcmp(pick_tmp, "aes"))
2502 cur_if->emode = ENC_AES;
2503 else if (!strcmp(pick_tmp, "tkipaes"))
2504 cur_if->emode = ENC_TKIPAES;
2505 else {
2506 AEXT_ERROR(cur_if->dev->name, "emode [none|wep|tkip|aes|tkipaes]\n");
2507 return -1;
2508 }
2509 } else if (!strcmp(row->name, " key ")) {
2510 len = strlen(pick_tmp);
2511 memset(cur_if->key, 0, sizeof(cur_if->key));
2512 if (pick_tmp[0] == '"' && pick_tmp[len-1] == '"')
2513 strncpy(cur_if->key, &pick_tmp[1], len-2);
2514 else
2515 strcpy(cur_if->key, pick_tmp);
2516 }
2517 }
2518 }
2519
2520 *pick_next = ifname_head;
2521 return 0;
2522 }
2523
2524 static int
2525 wl_ext_iapsta_config(struct net_device *dev, char *command, int total_len)
2526 {
2527 struct dhd_pub *dhd = dhd_get_pub(dev);
2528 int ret=0, i;
2529 char *pch, *pch2, *pick_tmp, *pick_next=NULL, *param;
2530 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
2531 char ifname[IFNAMSIZ+1];
2532 struct wl_if_info *cur_if = NULL, *tmp_if = NULL;
2533
2534 if (!apsta_params->init) {
2535 AEXT_ERROR(dev->name, "please init first\n");
2536 return -1;
2537 }
2538
2539 AEXT_TRACE(dev->name, "command=%s, len=%d\n", command, total_len);
2540
2541 pick_tmp = command;
2542 param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_config
2543
2544 mutex_lock(&apsta_params->usr_sync);
2545
2546 while (pick_tmp != NULL) {
2547 memset(ifname, 0, IFNAMSIZ+1);
2548 if (!strncmp(pick_tmp, "ifname ", strlen("ifname "))) {
2549 pch = pick_tmp + strlen("ifname ");
2550 pch2 = strchr(pch, ' ');
2551 if (pch && pch2) {
2552 strncpy(ifname, pch, pch2-pch);
2553 } else {
2554 AEXT_ERROR(dev->name, "ifname [wlanX]\n");
2555 ret = -1;
2556 break;
2557 }
2558 for (i=0; i<MAX_IF_NUM; i++) {
2559 tmp_if = &apsta_params->if_info[i];
2560 if (tmp_if->dev && !strcmp(tmp_if->dev->name, ifname)) {
2561 cur_if = tmp_if;
2562 break;
2563 }
2564 }
2565 if (!cur_if) {
2566 AEXT_ERROR(dev->name, "wrong ifname=%s in apstamode=%d\n",
2567 ifname, apsta_params->apstamode);
2568 ret = -1;
2569 break;
2570 }
2571 ret = wl_ext_parse_config(cur_if, pick_tmp, &pick_next);
2572 if (ret)
2573 break;
2574 pick_tmp = pick_next;
2575 } else {
2576 AEXT_ERROR(dev->name, "first arg must be ifname\n");
2577 ret = -1;
2578 break;
2579 }
2580
2581 }
2582
2583 mutex_unlock(&apsta_params->usr_sync);
2584
2585 return ret;
2586 }
2587
2588 static int
2589 wl_ext_assoclist(struct net_device *dev, char *data, char *command,
2590 int total_len)
2591 {
2592 int ret = 0, i, maxassoc = 0, bytes_written = 0;
2593 char mac_buf[MAX_NUM_OF_ASSOCLIST *
2594 sizeof(struct ether_addr) + sizeof(uint)] = {0};
2595 struct maclist *assoc_maclist = (struct maclist *)mac_buf;
2596
2597 assoc_maclist->count = htod32(MAX_NUM_OF_ASSOCLIST);
2598 ret = wl_ext_ioctl(dev, WLC_GET_ASSOCLIST, assoc_maclist, sizeof(mac_buf), 0);
2599 if (ret)
2600 return -1;
2601 maxassoc = dtoh32(assoc_maclist->count);
2602 bytes_written += snprintf(command+bytes_written, total_len,
2603 "%2s: %12s",
2604 "no", "------addr------");
2605 for (i=0; i<maxassoc; i++) {
2606 bytes_written += snprintf(command+bytes_written, total_len,
2607 "\n%2d: %pM", i, &assoc_maclist->ea[i]);
2608 }
2609
2610 return bytes_written;
2611 }
2612
2613 #ifdef WLMESH
2614 static int
2615 wl_mesh_print_peer_info(mesh_peer_info_ext_t *mpi_ext,
2616 uint32 peer_results_count, char *command, int total_len)
2617 {
2618 char *peering_map[] = MESH_PEERING_STATE_STRINGS;
2619 uint32 count = 0;
2620 int bytes_written = 0;
2621
2622 bytes_written += snprintf(command+bytes_written, total_len,
2623 "%2s: %12s : %6s : %-6s : %6s :"
2624 " %5s : %4s : %4s : %11s : %4s",
2625 "no", "------addr------ ", "l.aid", "state", "p.aid",
2626 "mppid", "llid", "plid", "entry_state", "rssi");
2627 for (count=0; count < peer_results_count; count++) {
2628 if (mpi_ext->entry_state != MESH_SELF_PEER_ENTRY_STATE_TIMEDOUT) {
2629 bytes_written += snprintf(command+bytes_written, total_len,
2630 "\n%2d: %pM : 0x%4x : %6s : 0x%4x :"
2631 " %5d : %4d : %4d : %11s : %4d",
2632 count, &mpi_ext->ea, mpi_ext->local_aid,
2633 peering_map[mpi_ext->peer_info.state],
2634 mpi_ext->peer_info.peer_aid,
2635 mpi_ext->peer_info.mesh_peer_prot_id,
2636 mpi_ext->peer_info.local_link_id,
2637 mpi_ext->peer_info.peer_link_id,
2638 (mpi_ext->entry_state == MESH_SELF_PEER_ENTRY_STATE_ACTIVE) ?
2639 "ACTIVE" :
2640 "EXTERNAL",
2641 mpi_ext->rssi);
2642 } else {
2643 bytes_written += snprintf(command+bytes_written, total_len,
2644 "\n%2d: %pM : %6s : %5s : %6s :"
2645 " %5s : %4s : %4s : %11s : %4s",
2646 count, &mpi_ext->ea, " NA ", " NA ", " NA ",
2647 " NA ", " NA ", " NA ", " TIMEDOUT ", " NA ");
2648 }
2649 mpi_ext++;
2650 }
2651
2652 return bytes_written;
2653 }
2654
2655 static int
2656 wl_mesh_get_peer_results(struct net_device *dev, char *buf, int len)
2657 {
2658 int indata, inlen;
2659 mesh_peer_info_dump_t *peer_results;
2660 int ret;
2661
2662 memset(buf, 0, len);
2663 peer_results = (mesh_peer_info_dump_t *)buf;
2664 indata = htod32(len);
2665 inlen = 4;
2666 ret = wl_ext_iovar_getbuf(dev, "mesh_peer_status", &indata, inlen, buf, len, NULL);
2667 if (!ret) {
2668 peer_results = (mesh_peer_info_dump_t *)buf;
2669 ret = peer_results->count;
2670 }
2671
2672 return ret;
2673 }
2674
2675 static int
2676 wl_ext_mesh_peer_status(struct net_device *dev, char *data, char *command,
2677 int total_len)
2678 {
2679 struct dhd_pub *dhd = dhd_get_pub(dev);
2680 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
2681 int i;
2682 struct wl_if_info *cur_if;
2683 mesh_peer_info_dump_t *peer_results;
2684 mesh_peer_info_ext_t *mpi_ext;
2685 char *peer_buf = NULL;
2686 int peer_len = WLC_IOCTL_MAXLEN;
2687 int dump_written = 0, ret;
2688
2689 if (!data) {
2690 peer_buf = kmalloc(peer_len, GFP_KERNEL);
2691 if (peer_buf == NULL) {
2692 AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n",
2693 peer_len);
2694 return -1;
2695 }
2696 for (i=0; i<MAX_IF_NUM; i++) {
2697 cur_if = &apsta_params->if_info[i];
2698 if (cur_if && dev == cur_if->dev && cur_if->ifmode == IMESH_MODE) {
2699 memset(peer_buf, 0, peer_len);
2700 ret = wl_mesh_get_peer_results(dev, peer_buf, peer_len);
2701 if (ret >= 0) {
2702 peer_results = (mesh_peer_info_dump_t *)peer_buf;
2703 mpi_ext = (mesh_peer_info_ext_t *)peer_results->mpi_ext;
2704 dump_written += wl_mesh_print_peer_info(mpi_ext,
2705 peer_results->count, command+dump_written,
2706 total_len-dump_written);
2707 }
2708 } else if (cur_if && dev == cur_if->dev) {
2709 AEXT_ERROR(dev->name, "[%s][%c] is not mesh interface\n",
2710 cur_if->ifname, cur_if->prefix);
2711 }
2712 }
2713 }
2714
2715 if (peer_buf)
2716 kfree(peer_buf);
2717 return dump_written;
2718 }
2719
2720 #ifdef WL_ESCAN
2721 #define WL_MESH_DELAY_SCAN_MS 3000
2722 static void
2723 wl_mesh_timer(unsigned long data)
2724 {
2725 wl_event_msg_t msg;
2726 struct wl_if_info *mesh_if = (struct wl_if_info *)data;
2727 struct dhd_pub *dhd;
2728
2729 if (!mesh_if) {
2730 AEXT_ERROR("wlan", "mesh_if is not ready\n");
2731 return;
2732 }
2733
2734 if (!mesh_if->dev) {
2735 AEXT_ERROR("wlan", "ifidx %d is not ready\n", mesh_if->ifidx);
2736 return;
2737 }
2738 dhd = dhd_get_pub(mesh_if->dev);
2739
2740 bzero(&msg, sizeof(wl_event_msg_t));
2741 AEXT_TRACE(mesh_if->dev->name, "timer expired\n");
2742
2743 msg.ifidx = mesh_if->ifidx;
2744 msg.event_type = hton32(WLC_E_RESERVED);
2745 msg.reason = 0xFFFFFFFF;
2746 wl_ext_event_send(dhd->event_params, &msg, NULL);
2747 }
2748
2749 static void
2750 wl_mesh_set_timer(struct wl_if_info *mesh_if, uint timeout)
2751 {
2752 AEXT_TRACE(mesh_if->dev->name, "timeout=%d\n", timeout);
2753
2754 if (timer_pending(&mesh_if->delay_scan))
2755 del_timer_sync(&mesh_if->delay_scan);
2756
2757 if (timeout) {
2758 if (timer_pending(&mesh_if->delay_scan))
2759 del_timer_sync(&mesh_if->delay_scan);
2760 mod_timer(&mesh_if->delay_scan, jiffies + msecs_to_jiffies(timeout));
2761 }
2762 }
2763
2764 static int
2765 wl_mesh_clear_vndr_ie(struct net_device *dev, uchar *oui)
2766 {
2767 char *vndr_ie_buf = NULL;
2768 vndr_ie_setbuf_t *vndr_ie = NULL;
2769 ie_getbuf_t vndr_ie_tmp;
2770 char *iovar_buf = NULL;
2771 int err = -1, i;
2772 vndr_ie_buf_t *vndr_ie_dump = NULL;
2773 uchar *iebuf;
2774 vndr_ie_info_t *ie_info;
2775 vndr_ie_t *ie;
2776
2777 vndr_ie_buf = kzalloc(WLC_IOCTL_SMLEN, GFP_KERNEL);
2778 if (!vndr_ie_buf) {
2779 AEXT_ERROR(dev->name, "IE memory alloc failed\n");
2780 err = -ENOMEM;
2781 goto exit;
2782 }
2783
2784 iovar_buf = kzalloc(WLC_IOCTL_MEDLEN, GFP_KERNEL);
2785 if (!iovar_buf) {
2786 AEXT_ERROR(dev->name, "iovar_buf alloc failed\n");
2787 err = -ENOMEM;
2788 goto exit;
2789 }
2790
2791 memset(iovar_buf, 0, WLC_IOCTL_MEDLEN);
2792 vndr_ie_tmp.pktflag = (uint32) -1;
2793 vndr_ie_tmp.id = (uint8) DOT11_MNG_PROPR_ID;
2794 err = wl_ext_iovar_getbuf(dev, "vndr_ie", &vndr_ie_tmp, sizeof(vndr_ie_tmp),
2795 iovar_buf, WLC_IOCTL_MEDLEN, NULL);
2796 if (err)
2797 goto exit;
2798
2799 vndr_ie_dump = (vndr_ie_buf_t *)iovar_buf;
2800 if (!vndr_ie_dump->iecount)
2801 goto exit;
2802
2803 iebuf = (uchar *)&vndr_ie_dump->vndr_ie_list[0];
2804 for (i=0; i<vndr_ie_dump->iecount; i++) {
2805 ie_info = (vndr_ie_info_t *) iebuf;
2806 ie = &ie_info->vndr_ie_data;
2807 if (memcmp(ie->oui, oui, 3))
2808 memset(ie->oui, 0, 3);
2809 iebuf += sizeof(uint32) + ie->len + VNDR_IE_HDR_LEN;
2810 }
2811
2812 vndr_ie = (vndr_ie_setbuf_t *) vndr_ie_buf;
2813 strncpy(vndr_ie->cmd, "del", VNDR_IE_CMD_LEN - 1);
2814 vndr_ie->cmd[VNDR_IE_CMD_LEN - 1] = '\0';
2815 memcpy(&vndr_ie->vndr_ie_buffer, vndr_ie_dump, WLC_IOCTL_SMLEN-VNDR_IE_CMD_LEN-1);
2816
2817 memset(iovar_buf, 0, WLC_IOCTL_MEDLEN);
2818 err = wl_ext_iovar_setbuf(dev, "vndr_ie", vndr_ie, WLC_IOCTL_SMLEN, iovar_buf,
2819 WLC_IOCTL_MEDLEN, NULL);
2820
2821 exit:
2822 if (vndr_ie) {
2823 kfree(vndr_ie);
2824 }
2825 if (iovar_buf) {
2826 kfree(iovar_buf);
2827 }
2828 return err;
2829 }
2830
2831 static int
2832 wl_mesh_clear_mesh_info(struct wl_apsta_params *apsta_params,
2833 struct wl_if_info *mesh_if, bool scan)
2834 {
2835 struct wl_mesh_params *mesh_info = &apsta_params->mesh_info;
2836 uchar mesh_oui[]={0x00, 0x22, 0xf4};
2837 int ret;
2838
2839 AEXT_TRACE(mesh_if->dev->name, "Enter\n");
2840
2841 ret = wl_mesh_clear_vndr_ie(mesh_if->dev, mesh_oui);
2842 memset(mesh_info, 0, sizeof(struct wl_mesh_params));
2843 if (scan) {
2844 mesh_info->scan_channel = wl_ext_get_chan(apsta_params, mesh_if->dev);
2845 wl_mesh_set_timer(mesh_if, 100);
2846 }
2847
2848 return ret;
2849 }
2850
2851 static int
2852 wl_mesh_update_vndr_ie(struct wl_apsta_params *apsta_params,
2853 struct wl_if_info *mesh_if)
2854 {
2855 struct wl_mesh_params *mesh_info = &apsta_params->mesh_info;
2856 char *vndr_ie;
2857 uchar mesh_oui[]={0x00, 0x22, 0xf4};
2858 int bytes_written = 0;
2859 int ret = 0, i, vndr_ie_len;
2860 uint8 *peer_bssid;
2861
2862 wl_mesh_clear_vndr_ie(mesh_if->dev, mesh_oui);
2863
2864 vndr_ie_len = WLC_IOCTL_MEDLEN;
2865 vndr_ie = kmalloc(vndr_ie_len, GFP_KERNEL);
2866 if (vndr_ie == NULL) {
2867 AEXT_ERROR(mesh_if->dev->name, "Failed to allocate buffer of %d bytes\n",
2868 WLC_IOCTL_MEDLEN);
2869 ret = -1;
2870 goto exit;
2871 }
2872
2873 bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
2874 "0x%02x%02x%02x", mesh_oui[0], mesh_oui[1], mesh_oui[2]);
2875
2876 bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
2877 "%02x%02x%02x%02x%02x%02x%02x%02x", MESH_INFO_MASTER_BSSID, ETHER_ADDR_LEN,
2878 ((u8 *)(&mesh_info->master_bssid))[0], ((u8 *)(&mesh_info->master_bssid))[1],
2879 ((u8 *)(&mesh_info->master_bssid))[2], ((u8 *)(&mesh_info->master_bssid))[3],
2880 ((u8 *)(&mesh_info->master_bssid))[4], ((u8 *)(&mesh_info->master_bssid))[5]);
2881
2882 bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
2883 "%02x%02x%02x", MESH_INFO_MASTER_CHANNEL, 1, mesh_info->master_channel);
2884
2885 bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
2886 "%02x%02x%02x", MESH_INFO_HOP_CNT, 1, mesh_info->hop_cnt);
2887
2888 bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
2889 "%02x%02x", MESH_INFO_PEER_BSSID, mesh_info->hop_cnt*ETHER_ADDR_LEN);
2890 for (i=0; i<mesh_info->hop_cnt && i<MAX_HOP_LIST; i++) {
2891 peer_bssid = (uint8 *)&mesh_info->peer_bssid[i];
2892 bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
2893 "%02x%02x%02x%02x%02x%02x",
2894 peer_bssid[0], peer_bssid[1], peer_bssid[2],
2895 peer_bssid[3], peer_bssid[4], peer_bssid[5]);
2896 }
2897
2898 ret = wl_ext_add_del_ie(mesh_if->dev, VNDR_IE_BEACON_FLAG|VNDR_IE_PRBRSP_FLAG,
2899 vndr_ie, "add");
2900 if (!ret) {
2901 AEXT_INFO(mesh_if->dev->name, "mbssid=%pM, mchannel=%d, hop=%d, pbssid=%pM\n",
2902 &mesh_info->master_bssid, mesh_info->master_channel, mesh_info->hop_cnt,
2903 mesh_info->peer_bssid);
2904 }
2905
2906 exit:
2907 if (vndr_ie)
2908 kfree(vndr_ie);
2909 return ret;
2910 }
2911
2912 static bool
2913 wl_mesh_update_master_info(struct wl_apsta_params *apsta_params,
2914 struct wl_if_info *mesh_if)
2915 {
2916 struct wl_mesh_params *mesh_info = &apsta_params->mesh_info;
2917 struct wl_if_info *sta_if = NULL;
2918 bool updated = FALSE;
2919
2920 sta_if = wl_ext_if_enabled(apsta_params, ISTA_MODE);
2921 if (sta_if) {
2922 wldev_ioctl(mesh_if->dev, WLC_GET_BSSID, &mesh_info->master_bssid,
2923 ETHER_ADDR_LEN, 0);
2924 mesh_info->master_channel = wl_ext_get_chan(apsta_params, mesh_if->dev);
2925 mesh_info->hop_cnt = 0;
2926 memset(mesh_info->peer_bssid, 0, MAX_HOP_LIST*ETHER_ADDR_LEN);
2927 if (!wl_mesh_update_vndr_ie(apsta_params, mesh_if))
2928 updated = TRUE;
2929 }
2930
2931 return updated;
2932 }
2933
2934 static bool
2935 wl_mesh_update_mesh_info(struct wl_apsta_params *apsta_params,
2936 struct wl_if_info *mesh_if)
2937 {
2938 struct wl_mesh_params *mesh_info = &apsta_params->mesh_info, peer_mesh_info;
2939 uint32 count = 0;
2940 char *dump_buf = NULL;
2941 mesh_peer_info_dump_t *peer_results;
2942 mesh_peer_info_ext_t *mpi_ext;
2943 struct ether_addr bssid;
2944 bool updated = FALSE, bss_found = FALSE;
2945 uint16 cur_chan;
2946
2947 dump_buf = kmalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL);
2948 if (dump_buf == NULL) {
2949 AEXT_ERROR(mesh_if->dev->name, "Failed to allocate buffer of %d bytes\n",
2950 WLC_IOCTL_MAXLEN);
2951 return FALSE;
2952 }
2953 count = wl_mesh_get_peer_results(mesh_if->dev, dump_buf, WLC_IOCTL_MAXLEN);
2954 if (count > 0) {
2955 memset(&bssid, 0, ETHER_ADDR_LEN);
2956 wldev_ioctl(mesh_if->dev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, 0);
2957 peer_results = (mesh_peer_info_dump_t *)dump_buf;
2958 mpi_ext = (mesh_peer_info_ext_t *)peer_results->mpi_ext;
2959 for (count = 0; count < peer_results->count; count++) {
2960 if (mpi_ext->entry_state != MESH_SELF_PEER_ENTRY_STATE_TIMEDOUT &&
2961 mpi_ext->peer_info.state == MESH_PEERING_ESTAB) {
2962 memset(&peer_mesh_info, 0, sizeof(struct wl_mesh_params));
2963 bss_found = wl_escan_mesh_info(mesh_if->dev, mesh_if->escan,
2964 &mpi_ext->ea, &peer_mesh_info);
2965 if (bss_found && (mesh_info->master_channel == 0 ||
2966 peer_mesh_info.hop_cnt <= mesh_info->hop_cnt) &&
2967 memcmp(&peer_mesh_info.peer_bssid, &bssid, ETHER_ADDR_LEN)) {
2968 memcpy(&mesh_info->master_bssid, &peer_mesh_info.master_bssid,
2969 ETHER_ADDR_LEN);
2970 mesh_info->master_channel = peer_mesh_info.master_channel;
2971 mesh_info->hop_cnt = peer_mesh_info.hop_cnt+1;
2972 memset(mesh_info->peer_bssid, 0, MAX_HOP_LIST*ETHER_ADDR_LEN);
2973 memcpy(&mesh_info->peer_bssid, &mpi_ext->ea, ETHER_ADDR_LEN);
2974 memcpy(&mesh_info->peer_bssid[1], peer_mesh_info.peer_bssid,
2975 (MAX_HOP_LIST-1)*ETHER_ADDR_LEN);
2976 updated = TRUE;
2977 }
2978 }
2979 mpi_ext++;
2980 }
2981 if (updated) {
2982 if (wl_mesh_update_vndr_ie(apsta_params, mesh_if)) {
2983 AEXT_ERROR(mesh_if->dev->name, "update failed\n");
2984 mesh_info->master_channel = 0;
2985 updated = FALSE;
2986 goto exit;
2987 }
2988 }
2989 }
2990
2991 if (!mesh_info->master_channel) {
2992 wlc_ssid_t cur_ssid;
2993 char sec[32];
2994 bool sae = FALSE;
2995 memset(&peer_mesh_info, 0, sizeof(struct wl_mesh_params));
2996 wl_ext_ioctl(mesh_if->dev, WLC_GET_SSID, &cur_ssid, sizeof(cur_ssid), 0);
2997 wl_ext_get_sec(mesh_if->dev, mesh_if->ifmode, sec, sizeof(sec));
2998 if (strnicmp(sec, "sae/sae", strlen("sae/sae")) == 0)
2999 sae = TRUE;
3000 cur_chan = wl_ext_get_chan(apsta_params, mesh_if->dev);
3001 bss_found = wl_escan_mesh_peer(mesh_if->dev, mesh_if->escan, &cur_ssid, cur_chan,
3002 sae, &peer_mesh_info);
3003
3004 if (bss_found && peer_mesh_info.master_channel&&
3005 (cur_chan != peer_mesh_info.master_channel)) {
3006 WL_MSG(mesh_if->ifname, "moving channel %d -> %d\n",
3007 cur_chan, peer_mesh_info.master_channel);
3008 wl_ext_disable_iface(mesh_if->dev, mesh_if->ifname);
3009 mesh_if->channel = peer_mesh_info.master_channel;
3010 wl_ext_enable_iface(mesh_if->dev, mesh_if->ifname, 500);
3011 }
3012 }
3013
3014 exit:
3015 if (dump_buf)
3016 kfree(dump_buf);
3017 return updated;
3018 }
3019
3020 static void
3021 wl_mesh_event_handler( struct wl_apsta_params *apsta_params,
3022 struct wl_if_info *mesh_if, const wl_event_msg_t *e, void *data)
3023 {
3024 struct wl_mesh_params *mesh_info = &apsta_params->mesh_info;
3025 uint32 event_type = ntoh32(e->event_type);
3026 uint32 status = ntoh32(e->status);
3027 uint32 reason = ntoh32(e->reason);
3028 wlc_ssid_t ssid;
3029 int ret;
3030
3031 if (wl_get_isam_status(mesh_if, AP_CREATED) &&
3032 ((event_type == WLC_E_SET_SSID && status == WLC_E_STATUS_SUCCESS) ||
3033 (event_type == WLC_E_LINK && status == WLC_E_STATUS_SUCCESS &&
3034 reason == WLC_E_REASON_INITIAL_ASSOC))) {
3035 if (!wl_mesh_update_master_info(apsta_params, mesh_if)) {
3036 mesh_info->scan_channel = wl_ext_get_chan(apsta_params, mesh_if->dev);
3037 wl_mesh_set_timer(mesh_if, WL_MESH_DELAY_SCAN_MS);
3038 }
3039 }
3040 else if ((event_type == WLC_E_LINK && reason == WLC_E_LINK_BSSCFG_DIS) ||
3041 (event_type == WLC_E_LINK && status == WLC_E_STATUS_SUCCESS &&
3042 reason == WLC_E_REASON_DEAUTH)) {
3043 wl_mesh_clear_mesh_info(apsta_params, mesh_if, FALSE);
3044 }
3045 else if (wl_get_isam_status(mesh_if, AP_CREATED) &&
3046 (event_type == WLC_E_ASSOC_IND || event_type == WLC_E_REASSOC_IND) &&
3047 reason == DOT11_SC_SUCCESS) {
3048 mesh_info->scan_channel = wl_ext_get_chan(apsta_params, mesh_if->dev);
3049 wl_mesh_set_timer(mesh_if, 100);
3050 }
3051 else if (event_type == WLC_E_DISASSOC_IND || event_type == WLC_E_DEAUTH_IND ||
3052 (event_type == WLC_E_DEAUTH && reason != DOT11_RC_RESERVED)) {
3053 if (!memcmp(&mesh_info->peer_bssid, &e->addr, ETHER_ADDR_LEN))
3054 wl_mesh_clear_mesh_info(apsta_params, mesh_if, TRUE);
3055 }
3056 else if (wl_get_isam_status(mesh_if, AP_CREATED) &&
3057 event_type == WLC_E_RESERVED && reason == 0xFFFFFFFF) {
3058 if (!wl_mesh_update_master_info(apsta_params, mesh_if)) {
3059 wl_ext_ioctl(mesh_if->dev, WLC_GET_SSID, &ssid, sizeof(ssid), 0);
3060 ret = wl_escan_set_scan(mesh_if->dev, apsta_params->dhd, &ssid,
3061 mesh_info->scan_channel, FALSE);
3062 if (ret)
3063 wl_mesh_set_timer(mesh_if, WL_MESH_DELAY_SCAN_MS);
3064 }
3065 }
3066 else if (wl_get_isam_status(mesh_if, AP_CREATED) &&
3067 ((event_type == WLC_E_ESCAN_RESULT && status == WLC_E_STATUS_SUCCESS) ||
3068 (event_type == WLC_E_ESCAN_RESULT &&
3069 (status == WLC_E_STATUS_ABORT || status == WLC_E_STATUS_NEWSCAN ||
3070 status == WLC_E_STATUS_11HQUIET || status == WLC_E_STATUS_CS_ABORT ||
3071 status == WLC_E_STATUS_NEWASSOC || status == WLC_E_STATUS_TIMEOUT)))) {
3072 if (!wl_mesh_update_master_info(apsta_params, mesh_if)) {
3073 if (!wl_mesh_update_mesh_info(apsta_params, mesh_if)) {
3074 mesh_info->scan_channel = 0;
3075 wl_mesh_set_timer(mesh_if, WL_MESH_DELAY_SCAN_MS);
3076 }
3077 }
3078 }
3079 }
3080
3081 static void
3082 wl_mesh_escan_detach(dhd_pub_t *dhd, struct wl_if_info *mesh_if)
3083 {
3084 AEXT_TRACE(mesh_if->dev->name, "Enter\n");
3085
3086 del_timer_sync(&mesh_if->delay_scan);
3087
3088 if (mesh_if->escan) {
3089 mesh_if->escan = NULL;
3090 }
3091 }
3092
3093 static int
3094 wl_mesh_escan_attach(dhd_pub_t *dhd, struct wl_if_info *mesh_if)
3095 {
3096 AEXT_TRACE(mesh_if->dev->name, "Enter\n");
3097
3098 mesh_if->escan = dhd->escan;
3099 init_timer_compat(&mesh_if->delay_scan, wl_mesh_timer, mesh_if);
3100
3101 return 0;
3102 }
3103
3104 static uint
3105 wl_mesh_update_peer_path(struct wl_if_info *mesh_if, char *command,
3106 int total_len)
3107 {
3108 struct wl_mesh_params peer_mesh_info;
3109 uint32 count = 0;
3110 char *dump_buf = NULL;
3111 mesh_peer_info_dump_t *peer_results;
3112 mesh_peer_info_ext_t *mpi_ext;
3113 int bytes_written = 0, j, k;
3114 bool bss_found = FALSE;
3115
3116 dump_buf = kmalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL);
3117 if (dump_buf == NULL) {
3118 AEXT_ERROR(mesh_if->dev->name, "Failed to allocate buffer of %d bytes\n",
3119 WLC_IOCTL_MAXLEN);
3120 return FALSE;
3121 }
3122 count = wl_mesh_get_peer_results(mesh_if->dev, dump_buf, WLC_IOCTL_MAXLEN);
3123 if (count > 0) {
3124 peer_results = (mesh_peer_info_dump_t *)dump_buf;
3125 mpi_ext = (mesh_peer_info_ext_t *)peer_results->mpi_ext;
3126 for (count = 0; count < peer_results->count; count++) {
3127 if (mpi_ext->entry_state != MESH_SELF_PEER_ENTRY_STATE_TIMEDOUT &&
3128 mpi_ext->peer_info.state == MESH_PEERING_ESTAB) {
3129 memset(&peer_mesh_info, 0, sizeof(struct wl_mesh_params));
3130 bss_found = wl_escan_mesh_info(mesh_if->dev, mesh_if->escan,
3131 &mpi_ext->ea, &peer_mesh_info);
3132 if (bss_found) {
3133 bytes_written += snprintf(command+bytes_written, total_len,
3134 "\npeer=%pM, hop=%d",
3135 &mpi_ext->ea, peer_mesh_info.hop_cnt);
3136 for (j=1; j<peer_mesh_info.hop_cnt; j++) {
3137 bytes_written += snprintf(command+bytes_written,
3138 total_len, "\n");
3139 for (k=0; k<j; k++) {
3140 bytes_written += snprintf(command+bytes_written,
3141 total_len, " ");
3142 }
3143 bytes_written += snprintf(command+bytes_written, total_len,
3144 "%pM", &peer_mesh_info.peer_bssid[j]);
3145 }
3146 }
3147 }
3148 mpi_ext++;
3149 }
3150 }
3151
3152 if (dump_buf)
3153 kfree(dump_buf);
3154 return bytes_written;
3155 }
3156
3157 static int
3158 wl_ext_isam_peer_path(struct net_device *dev, char *command, int total_len)
3159 {
3160 struct dhd_pub *dhd = dhd_get_pub(dev);
3161 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
3162 struct wl_mesh_params *mesh_info = &apsta_params->mesh_info;
3163 struct wl_if_info *tmp_if;
3164 uint16 chan = 0;
3165 char *dump_buf = NULL;
3166 int dump_len = WLC_IOCTL_MEDLEN;
3167 int dump_written = 0;
3168 int i;
3169
3170 if (command || android_msg_level & ANDROID_INFO_LEVEL) {
3171 if (command) {
3172 dump_buf = command;
3173 dump_len = total_len;
3174 } else {
3175 dump_buf = kmalloc(dump_len, GFP_KERNEL);
3176 if (dump_buf == NULL) {
3177 AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n",
3178 dump_len);
3179 return -1;
3180 }
3181 }
3182 for (i=0; i<MAX_IF_NUM; i++) {
3183 tmp_if = &apsta_params->if_info[i];
3184 if (tmp_if->dev && tmp_if->ifmode == IMESH_MODE && apsta_params->macs) {
3185 chan = wl_ext_get_chan(apsta_params, tmp_if->dev);
3186 if (chan) {
3187 dump_written += snprintf(dump_buf+dump_written, dump_len,
3188 "[dhd-%s-%c] mbssid=%pM, mchan=%d, hop=%d, pbssid=%pM",
3189 tmp_if->ifname, tmp_if->prefix, &mesh_info->master_bssid,
3190 mesh_info->master_channel, mesh_info->hop_cnt,
3191 &mesh_info->peer_bssid);
3192 dump_written += wl_mesh_update_peer_path(tmp_if,
3193 dump_buf+dump_written, dump_len-dump_written);
3194 }
3195 }
3196 }
3197 AEXT_INFO(dev->name, "%s\n", dump_buf);
3198 }
3199
3200 if (!command && dump_buf)
3201 kfree(dump_buf);
3202 return dump_written;
3203 }
3204 #endif /* WL_ESCAN */
3205 #endif /* WLMESH */
3206
3207 static int
3208 wl_ext_isam_status(struct net_device *dev, char *command, int total_len)
3209 {
3210 struct dhd_pub *dhd = dhd_get_pub(dev);
3211 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
3212 int i;
3213 struct wl_if_info *tmp_if;
3214 uint16 chan = 0;
3215 wlc_ssid_t ssid = { 0, {0} };
3216 struct ether_addr bssid;
3217 scb_val_t scb_val;
3218 char sec[32];
3219 u32 chanspec = 0;
3220 char *dump_buf = NULL;
3221 int dump_len = WLC_IOCTL_MEDLEN;
3222 int dump_written = 0;
3223
3224 if (command || android_msg_level & ANDROID_INFO_LEVEL) {
3225 if (command) {
3226 dump_buf = command;
3227 dump_len = total_len;
3228 } else {
3229 dump_buf = kmalloc(dump_len, GFP_KERNEL);
3230 if (dump_buf == NULL) {
3231 AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n",
3232 dump_len);
3233 return -1;
3234 }
3235 }
3236 dump_written += snprintf(dump_buf+dump_written, dump_len,
3237 "apstamode=%d", apsta_params->apstamode);
3238 for (i=0; i<MAX_IF_NUM; i++) {
3239 memset(&ssid, 0, sizeof(ssid));
3240 memset(&bssid, 0, sizeof(bssid));
3241 memset(&scb_val, 0, sizeof(scb_val));
3242 tmp_if = &apsta_params->if_info[i];
3243 if (tmp_if->dev) {
3244 chan = wl_ext_get_chan(apsta_params, tmp_if->dev);
3245 if (chan) {
3246 wl_ext_ioctl(tmp_if->dev, WLC_GET_SSID, &ssid, sizeof(ssid), 0);
3247 wldev_ioctl(tmp_if->dev, WLC_GET_BSSID, &bssid, sizeof(bssid), 0);
3248 wldev_ioctl(tmp_if->dev, WLC_GET_RSSI, &scb_val,
3249 sizeof(scb_val_t), 0);
3250 chanspec = wl_ext_get_chanspec(apsta_params, tmp_if->dev);
3251 wl_ext_get_sec(tmp_if->dev, tmp_if->ifmode, sec, sizeof(sec));
3252 dump_written += snprintf(dump_buf+dump_written, dump_len,
3253 "\n[dhd-%s-%c]: bssid=%pM, chan=%3d(0x%x %sMHz), "
3254 "rssi=%3d, sec=%-15s, SSID=\"%s\"",
3255 tmp_if->ifname, tmp_if->prefix, &bssid, chan, chanspec,
3256 CHSPEC_IS20(chanspec)?"20":
3257 CHSPEC_IS40(chanspec)?"40":
3258 CHSPEC_IS80(chanspec)?"80":"160",
3259 dtoh32(scb_val.val), sec, ssid.SSID);
3260 if (tmp_if->ifmode == IAP_MODE) {
3261 dump_written += snprintf(dump_buf+dump_written, dump_len, "\n");
3262 dump_written += wl_ext_assoclist(tmp_if->dev, NULL,
3263 dump_buf+dump_written, dump_len-dump_written);
3264 }
3265 #ifdef WLMESH
3266 else if (tmp_if->ifmode == IMESH_MODE) {
3267 dump_written += snprintf(dump_buf+dump_written, dump_len, "\n");
3268 dump_written += wl_ext_mesh_peer_status(tmp_if->dev, NULL,
3269 dump_buf+dump_written, dump_len-dump_written);
3270 }
3271 #endif /* WLMESH */
3272 } else {
3273 dump_written += snprintf(dump_buf+dump_written, dump_len,
3274 "\n[dhd-%s-%c]:", tmp_if->ifname, tmp_if->prefix);
3275 }
3276 }
3277 }
3278 AEXT_INFO(dev->name, "%s\n", dump_buf);
3279 }
3280
3281 if (!command && dump_buf)
3282 kfree(dump_buf);
3283 return dump_written;
3284 }
3285
3286 static bool
3287 wl_ext_master_if(struct wl_if_info *cur_if)
3288 {
3289 if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE)
3290 return TRUE;
3291 else
3292 return FALSE;
3293 }
3294
3295 static int
3296 wl_ext_if_down(struct wl_apsta_params *apsta_params, struct wl_if_info *cur_if)
3297 {
3298 s8 iovar_buf[WLC_IOCTL_SMLEN];
3299 scb_val_t scbval;
3300 struct {
3301 s32 cfg;
3302 s32 val;
3303 } bss_setbuf;
3304 apstamode_t apstamode = apsta_params->apstamode;
3305
3306 WL_MSG(cur_if->ifname, "[%c] Turning off...\n", cur_if->prefix);
3307
3308 if (cur_if->ifmode == ISTA_MODE) {
3309 wl_ext_ioctl(cur_if->dev, WLC_DISASSOC, NULL, 0, 1);
3310 } else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
3311 // deauthenticate all STA first
3312 memcpy(scbval.ea.octet, &ether_bcast, ETHER_ADDR_LEN);
3313 wl_ext_ioctl(cur_if->dev, WLC_SCB_DEAUTHENTICATE, &scbval.ea, ETHER_ADDR_LEN, 1);
3314 }
3315
3316 if (apstamode == IAPONLY_MODE || apstamode == IMESHONLY_MODE) {
3317 wl_ext_ioctl(cur_if->dev, WLC_DOWN, NULL, 0, 1);
3318 } else {
3319 bss_setbuf.cfg = 0xffffffff;
3320 bss_setbuf.val = htod32(0);
3321 wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf),
3322 iovar_buf, WLC_IOCTL_SMLEN, NULL);
3323 }
3324 wl_clr_isam_status(cur_if, AP_CREATED);
3325
3326 return 0;
3327 }
3328
3329 static int
3330 wl_ext_if_up(struct wl_apsta_params *apsta_params, struct wl_if_info *cur_if)
3331 {
3332 s8 iovar_buf[WLC_IOCTL_SMLEN];
3333 struct {
3334 s32 cfg;
3335 s32 val;
3336 } bss_setbuf;
3337 apstamode_t apstamode = apsta_params->apstamode;
3338 chanspec_t fw_chspec;
3339 u32 timeout;
3340 wlc_ssid_t ssid = { 0, {0} };
3341 uint16 chan = 0;
3342
3343 if (cur_if->ifmode != IAP_MODE) {
3344 AEXT_ERROR(cur_if->ifname, "Wrong ifmode\n");
3345 return 0;
3346 }
3347
3348 if (wl_ext_dfs_chan(cur_if->channel) && !apsta_params->radar) {
3349 WL_MSG(cur_if->ifname, "[%c] skip DFS channel %d\n",
3350 cur_if->prefix, cur_if->channel);
3351 return 0;
3352 } else if (!cur_if->channel) {
3353 WL_MSG(cur_if->ifname, "[%c] no valid channel\n", cur_if->prefix);
3354 return 0;
3355 }
3356
3357 WL_MSG(cur_if->ifname, "[%c] Turning on...\n", cur_if->prefix);
3358
3359 wl_ext_set_chanspec(cur_if->dev, apsta_params->ioctl_ver, cur_if->channel,
3360 &fw_chspec);
3361
3362 wl_clr_isam_status(cur_if, AP_CREATED);
3363 wl_set_isam_status(cur_if, AP_CREATING);
3364 if (apstamode == IAPONLY_MODE) {
3365 wl_ext_ioctl(cur_if->dev, WLC_UP, NULL, 0, 1);
3366 } else {
3367 bss_setbuf.cfg = 0xffffffff;
3368 bss_setbuf.val = htod32(1);
3369 wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf,
3370 sizeof(bss_setbuf), iovar_buf, WLC_IOCTL_SMLEN, NULL);
3371 }
3372
3373 timeout = wait_event_interruptible_timeout(apsta_params->netif_change_event,
3374 wl_get_isam_status(cur_if, AP_CREATED),
3375 msecs_to_jiffies(MAX_AP_LINK_WAIT_TIME));
3376 if (timeout <= 0 || !wl_get_isam_status(cur_if, AP_CREATED)) {
3377 wl_ext_if_down(apsta_params, cur_if);
3378 WL_MSG(cur_if->ifname, "[%c] failed to up with SSID: \"%s\"\n",
3379 cur_if->prefix, cur_if->ssid);
3380 } else {
3381 wl_ext_ioctl(cur_if->dev, WLC_GET_SSID, &ssid, sizeof(ssid), 0);
3382 chan = wl_ext_get_chan(apsta_params, cur_if->dev);
3383 WL_MSG(cur_if->ifname, "[%c] enabled with SSID: \"%s\" on channel %d\n",
3384 cur_if->prefix, ssid.SSID, chan);
3385 }
3386 wl_clr_isam_status(cur_if, AP_CREATING);
3387
3388 wl_ext_isam_status(cur_if->dev, NULL, 0);
3389
3390 return 0;
3391 }
3392
3393 static int
3394 wl_ext_disable_iface(struct net_device *dev, char *ifname)
3395 {
3396 struct dhd_pub *dhd = dhd_get_pub(dev);
3397 int i;
3398 s8 iovar_buf[WLC_IOCTL_SMLEN];
3399 wlc_ssid_t ssid = { 0, {0} };
3400 scb_val_t scbval;
3401 struct {
3402 s32 cfg;
3403 s32 val;
3404 } bss_setbuf;
3405 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
3406 apstamode_t apstamode = apsta_params->apstamode;
3407 struct wl_if_info *cur_if = NULL, *tmp_if = NULL;
3408
3409 for (i=0; i<MAX_IF_NUM; i++) {
3410 tmp_if = &apsta_params->if_info[i];
3411 if (tmp_if->dev && !strcmp(tmp_if->dev->name, ifname)) {
3412 cur_if = tmp_if;
3413 break;
3414 }
3415 }
3416 if (!cur_if) {
3417 AEXT_ERROR(dev->name, "wrong ifname=%s or dev not ready\n", ifname);
3418 return -1;
3419 }
3420
3421 mutex_lock(&apsta_params->usr_sync);
3422 WL_MSG(ifname, "[%c] Disabling...\n", cur_if->prefix);
3423
3424 if (cur_if->ifmode == ISTA_MODE) {
3425 wl_ext_ioctl(cur_if->dev, WLC_DISASSOC, NULL, 0, 1);
3426 wl_ext_add_remove_pm_enable_work(dev, FALSE);
3427 } else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
3428 // deauthenticate all STA first
3429 memcpy(scbval.ea.octet, &ether_bcast, ETHER_ADDR_LEN);
3430 wl_ext_ioctl(cur_if->dev, WLC_SCB_DEAUTHENTICATE, &scbval.ea, ETHER_ADDR_LEN, 1);
3431 }
3432
3433 if (apstamode == IAPONLY_MODE || apstamode == IMESHONLY_MODE) {
3434 wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
3435 wl_ext_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); // reset ssid
3436 wl_ext_iovar_setint(dev, "mpc", 1);
3437 } else if ((apstamode==ISTAAP_MODE || apstamode==ISTAGO_MODE) &&
3438 cur_if->ifmode == IAP_MODE) {
3439 bss_setbuf.cfg = 0xffffffff;
3440 bss_setbuf.val = htod32(0);
3441 wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf),
3442 iovar_buf, WLC_IOCTL_SMLEN, NULL);
3443 wl_ext_iovar_setint(dev, "mpc", 1);
3444 #ifdef ARP_OFFLOAD_SUPPORT
3445 /* IF SoftAP is disabled, enable arpoe back for STA mode. */
3446 dhd_arp_offload_set(dhd, dhd_arp_mode);
3447 dhd_arp_offload_enable(dhd, TRUE);
3448 #endif /* ARP_OFFLOAD_SUPPORT */
3449 #ifdef PROP_TXSTATUS_VSDB
3450 #if defined(BCMSDIO)
3451 if (dhd->conf->disable_proptx!=0) {
3452 bool enabled;
3453 dhd_wlfc_get_enable(dhd, &enabled);
3454 if (enabled) {
3455 dhd_wlfc_deinit(dhd);
3456 }
3457 }
3458 #endif /* BCMSDIO */
3459 #endif /* PROP_TXSTATUS_VSDB */
3460 }
3461 else if (apstamode == IDUALAP_MODE || apstamode == ISTAAPAP_MODE) {
3462 bss_setbuf.cfg = 0xffffffff;
3463 bss_setbuf.val = htod32(0);
3464 wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf),
3465 iovar_buf, WLC_IOCTL_SMLEN, NULL);
3466 #ifdef WLMESH
3467 } else if (apstamode == ISTAMESH_MODE || apstamode == IMESHAP_MODE ||
3468 apstamode == ISTAAPMESH_MODE || apstamode == IMESHAPAP_MODE) {
3469 bss_setbuf.cfg = 0xffffffff;
3470 bss_setbuf.val = htod32(0);
3471 wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf),
3472 iovar_buf, WLC_IOCTL_SMLEN, NULL);
3473 if (cur_if->ifmode == IMESH_MODE) {
3474 int scan_assoc_time = DHD_SCAN_ASSOC_ACTIVE_TIME;
3475 for (i=0; i<MAX_IF_NUM; i++) {
3476 tmp_if = &apsta_params->if_info[i];
3477 if (tmp_if->dev && tmp_if->ifmode == ISTA_MODE) {
3478 wl_ext_ioctl(tmp_if->dev, WLC_SET_SCAN_CHANNEL_TIME,
3479 &scan_assoc_time, sizeof(scan_assoc_time), 1);
3480 }
3481 }
3482 }
3483 #endif /* WLMESH */
3484 }
3485
3486 wl_clr_isam_status(cur_if, AP_CREATED);
3487
3488 WL_MSG(ifname, "[%c] Exit\n", cur_if->prefix);
3489 mutex_unlock(&apsta_params->usr_sync);
3490 return 0;
3491 }
3492
3493 static int
3494 wl_ext_iapsta_disable(struct net_device *dev, char *command, int total_len)
3495 {
3496 int ret = 0;
3497 char *pch, *pick_tmp, *param;
3498 char ifname[IFNAMSIZ+1];
3499
3500 AEXT_TRACE(dev->name, "command=%s, len=%d\n", command, total_len);
3501
3502 pick_tmp = command;
3503 param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_disable
3504 param = bcmstrtok(&pick_tmp, " ", 0);
3505 while (param != NULL) {
3506 if (!strcmp(param, "ifname")) {
3507 pch = bcmstrtok(&pick_tmp, " ", 0);
3508 if (pch) {
3509 strcpy(ifname, pch);
3510 ret = wl_ext_disable_iface(dev, ifname);
3511 if (ret)
3512 return ret;
3513 }
3514 else {
3515 AEXT_ERROR(dev->name, "ifname [wlanX]\n");
3516 return -1;
3517 }
3518 }
3519 param = bcmstrtok(&pick_tmp, " ", 0);
3520 }
3521
3522 return ret;
3523 }
3524
3525 static bool
3526 wl_ext_diff_band(uint16 chan1, uint16 chan2)
3527 {
3528 if ((chan1 <= CH_MAX_2G_CHANNEL && chan2 > CH_MAX_2G_CHANNEL) ||
3529 (chan1 > CH_MAX_2G_CHANNEL && chan2 <= CH_MAX_2G_CHANNEL)) {
3530 return TRUE;
3531 }
3532 return FALSE;
3533 }
3534
3535 static uint16
3536 wl_ext_same_band(struct wl_apsta_params *apsta_params,
3537 struct wl_if_info *cur_if, bool nodfs)
3538 {
3539 struct wl_if_info *tmp_if;
3540 uint16 tmp_chan, target_chan = 0;
3541 wl_prio_t max_prio;
3542 int i;
3543
3544 // find the max prio
3545 max_prio = cur_if->prio;
3546 for (i=0; i<MAX_IF_NUM; i++) {
3547 tmp_if = &apsta_params->if_info[i];
3548 if (cur_if != tmp_if && wl_get_isam_status(tmp_if, IF_READY) &&
3549 tmp_if->prio > max_prio) {
3550 tmp_chan = wl_ext_get_chan(apsta_params, tmp_if->dev);
3551 if (wl_ext_dfs_chan(tmp_chan) && nodfs)
3552 continue;
3553 if (tmp_chan && !wl_ext_diff_band(cur_if->channel, tmp_chan)) {
3554 target_chan = tmp_chan;
3555 max_prio = tmp_if->prio;
3556 }
3557 }
3558 }
3559
3560 return target_chan;
3561 }
3562
3563 static uint16
3564 wl_ext_get_vsdb_chan(struct wl_apsta_params *apsta_params,
3565 struct wl_if_info *cur_if, struct wl_if_info *target_if)
3566 {
3567 uint16 target_chan = 0, cur_chan = cur_if->channel;
3568
3569 target_chan = wl_ext_get_chan(apsta_params, target_if->dev);
3570 if (target_chan) {
3571 AEXT_INFO(cur_if->ifname, "cur_chan=%d, target_chan=%d\n",
3572 cur_chan, target_chan);
3573 if (wl_ext_diff_band(cur_chan, target_chan)) {
3574 if (!apsta_params->rsdb)
3575 return target_chan;
3576 } else {
3577 if (cur_chan != target_chan)
3578 return target_chan;
3579 }
3580 }
3581
3582 return 0;
3583 }
3584
3585 static int
3586 wl_ext_rsdb_core_conflict(struct wl_apsta_params *apsta_params,
3587 struct wl_if_info *cur_if)
3588 {
3589 struct wl_if_info *tmp_if;
3590 uint16 cur_chan, tmp_chan;
3591 int i;
3592
3593 if (apsta_params->rsdb) {
3594 cur_chan = wl_ext_get_chan(apsta_params, cur_if->dev);
3595 for (i=0; i<MAX_IF_NUM; i++) {
3596 tmp_if = &apsta_params->if_info[i];
3597 if (tmp_if != cur_if && wl_get_isam_status(tmp_if, IF_READY) &&
3598 tmp_if->prio > cur_if->prio) {
3599 tmp_chan = wl_ext_get_chan(apsta_params, tmp_if->dev);
3600 if (!tmp_chan)
3601 continue;
3602 if (wl_ext_diff_band(cur_chan, tmp_chan) &&
3603 wl_ext_diff_band(cur_chan, cur_if->channel))
3604 return TRUE;
3605 else if (!wl_ext_diff_band(cur_chan, tmp_chan) &&
3606 wl_ext_diff_band(cur_chan, cur_if->channel))
3607 return TRUE;
3608 }
3609 }
3610 }
3611 return FALSE;
3612 }
3613
3614 static int
3615 wl_ext_trigger_csa(struct wl_apsta_params *apsta_params, struct wl_if_info *cur_if)
3616 {
3617 s8 iovar_buf[WLC_IOCTL_SMLEN];
3618 bool core_conflict = FALSE;
3619
3620 if (wl_ext_master_if(cur_if) && (apsta_params->csa & CSA_DRV_BIT)) {
3621 if (!cur_if->channel) {
3622 WL_MSG(cur_if->ifname, "[%c] no valid channel\n", cur_if->prefix);
3623 } else if (wl_ext_dfs_chan(cur_if->channel) && !apsta_params->radar) {
3624 WL_MSG(cur_if->ifname, "[%c] skip DFS channel %d\n",
3625 cur_if->prefix, cur_if->channel);
3626 wl_ext_if_down(apsta_params, cur_if);
3627 } else {
3628 wl_chan_switch_t csa_arg;
3629 memset(&csa_arg, 0, sizeof(csa_arg));
3630 csa_arg.mode = 1;
3631 csa_arg.count = 3;
3632 csa_arg.chspec = wl_ext_chan_to_chanspec(apsta_params, cur_if->dev,
3633 cur_if->channel);
3634 core_conflict = wl_ext_rsdb_core_conflict(apsta_params, cur_if);
3635 if (core_conflict) {
3636 WL_MSG(cur_if->ifname, "[%c] Skip CSA due to rsdb core conflict\n",
3637 cur_if->prefix);
3638 } else if (csa_arg.chspec) {
3639 WL_MSG(cur_if->ifname, "[%c] Trigger CSA to channel %d(0x%x)\n",
3640 cur_if->prefix, cur_if->channel, csa_arg.chspec);
3641 wl_set_isam_status(cur_if, AP_CREATING);
3642 wl_ext_iovar_setbuf(cur_if->dev, "csa", &csa_arg, sizeof(csa_arg),
3643 iovar_buf, sizeof(iovar_buf), NULL);
3644 OSL_SLEEP(500);
3645 wl_clr_isam_status(cur_if, AP_CREATING);
3646 wl_ext_isam_status(cur_if->dev, NULL, 0);
3647 } else {
3648 AEXT_ERROR(cur_if->ifname, "fail to get chanspec\n");
3649 }
3650 }
3651 }
3652
3653 return 0;
3654 }
3655
3656 static void
3657 wl_ext_move_cur_dfs_channel(struct wl_apsta_params *apsta_params,
3658 struct wl_if_info *cur_if)
3659 {
3660 uint16 other_chan = 0, cur_chan = cur_if->channel;
3661 uint16 chan_2g = 0, chan_5g = 0;
3662 uint32 auto_band = WLC_BAND_2G;
3663
3664 if (wl_ext_master_if(cur_if) && wl_ext_dfs_chan(cur_if->channel) &&
3665 !apsta_params->radar) {
3666
3667 wl_ext_get_default_chan(cur_if->dev, &chan_2g, &chan_5g, TRUE);
3668 if (!chan_2g && !chan_5g) {
3669 cur_if->channel = 0;
3670 WL_MSG(cur_if->ifname, "[%c] no valid channel\n", cur_if->prefix);
3671 return;
3672 }
3673
3674 if (apsta_params->vsdb) {
3675 if (chan_5g) {
3676 cur_if->channel = chan_5g;
3677 auto_band = WLC_BAND_5G;
3678 other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
3679 } else {
3680 cur_if->channel = chan_2g;
3681 auto_band = WLC_BAND_2G;
3682 other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
3683 }
3684 if (!other_chan) {
3685 other_chan = wl_ext_autochannel(cur_if->dev, ACS_FW_BIT|ACS_DRV_BIT,
3686 auto_band);
3687 }
3688 if (other_chan)
3689 cur_if->channel = other_chan;
3690 } else if (apsta_params->rsdb) {
3691 if (chan_5g) {
3692 cur_if->channel = chan_5g;
3693 auto_band = WLC_BAND_5G;
3694 other_chan = wl_ext_same_band(apsta_params, cur_if, FALSE);
3695 if (wl_ext_dfs_chan(other_chan) && chan_2g) {
3696 cur_if->channel = chan_2g;
3697 auto_band = WLC_BAND_2G;
3698 other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
3699 }
3700 } else {
3701 cur_if->channel = chan_2g;
3702 auto_band = WLC_BAND_2G;
3703 other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
3704 }
3705 if (!other_chan) {
3706 other_chan = wl_ext_autochannel(cur_if->dev, ACS_FW_BIT|ACS_DRV_BIT,
3707 auto_band);
3708 }
3709 if (other_chan)
3710 cur_if->channel = other_chan;
3711 } else {
3712 cur_if->channel = chan_5g;
3713 auto_band = WLC_BAND_5G;
3714 other_chan = wl_ext_same_band(apsta_params, cur_if, FALSE);
3715 if (wl_ext_dfs_chan(other_chan)) {
3716 cur_if->channel = 0;
3717 }
3718 else if (!other_chan) {
3719 other_chan = wl_ext_autochannel(cur_if->dev, ACS_FW_BIT|ACS_DRV_BIT,
3720 auto_band);
3721 }
3722 if (other_chan)
3723 cur_if->channel = other_chan;
3724 }
3725 WL_MSG(cur_if->ifname, "[%c] move channel %d => %d\n",
3726 cur_if->prefix, cur_chan, cur_if->channel);
3727 }
3728 }
3729
3730 static void
3731 wl_ext_move_other_dfs_channel(struct wl_apsta_params *apsta_params,
3732 struct wl_if_info *cur_if)
3733 {
3734 uint16 other_chan = 0, cur_chan = cur_if->channel;
3735 uint16 chan_2g = 0, chan_5g = 0;
3736 uint32 auto_band = WLC_BAND_2G;
3737
3738 if (wl_ext_master_if(cur_if) && wl_ext_dfs_chan(cur_if->channel) &&
3739 !apsta_params->radar) {
3740
3741 wl_ext_get_default_chan(cur_if->dev, &chan_2g, &chan_5g, TRUE);
3742 if (!chan_2g && !chan_5g) {
3743 cur_if->channel = 0;
3744 WL_MSG(cur_if->ifname, "[%c] no valid channel\n", cur_if->prefix);
3745 return;
3746 }
3747
3748 if (apsta_params->vsdb) {
3749 if (chan_5g) {
3750 cur_if->channel = chan_5g;
3751 auto_band = WLC_BAND_5G;
3752 other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
3753 } else {
3754 cur_if->channel = chan_2g;
3755 auto_band = WLC_BAND_2G;
3756 other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
3757 }
3758 if (!other_chan) {
3759 other_chan = wl_ext_autochannel(cur_if->dev, ACS_FW_BIT|ACS_DRV_BIT,
3760 auto_band);
3761 }
3762 if (other_chan)
3763 cur_if->channel = other_chan;
3764 } else if (apsta_params->rsdb) {
3765 if (chan_2g) {
3766 cur_if->channel = chan_2g;
3767 auto_band = WLC_BAND_2G;
3768 other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
3769 if (!other_chan) {
3770 other_chan = wl_ext_autochannel(cur_if->dev, ACS_FW_BIT|ACS_DRV_BIT,
3771 auto_band);
3772 }
3773 } else {
3774 cur_if->channel = 0;
3775 }
3776 if (other_chan)
3777 cur_if->channel = other_chan;
3778 } else {
3779 cur_if->channel = 0;
3780 }
3781 WL_MSG(cur_if->ifname, "[%c] move channel %d => %d\n",
3782 cur_if->prefix, cur_chan, cur_if->channel);
3783 }
3784 }
3785
3786 static uint16
3787 wl_ext_move_cur_channel(struct wl_apsta_params *apsta_params,
3788 struct wl_if_info *cur_if)
3789 {
3790 struct wl_if_info *tmp_if, *target_if = NULL;
3791 uint16 tmp_chan, target_chan = 0;
3792 wl_prio_t max_prio;
3793 int i;
3794
3795 if (apsta_params->vsdb) {
3796 target_chan = cur_if->channel;
3797 goto exit;
3798 }
3799
3800 // find the max prio
3801 max_prio = cur_if->prio;
3802 for (i=0; i<MAX_IF_NUM; i++) {
3803 tmp_if = &apsta_params->if_info[i];
3804 if (cur_if != tmp_if && wl_get_isam_status(tmp_if, IF_READY) &&
3805 tmp_if->prio > max_prio) {
3806 tmp_chan = wl_ext_get_vsdb_chan(apsta_params, cur_if, tmp_if);
3807 if (tmp_chan) {
3808 target_if = tmp_if;
3809 target_chan = tmp_chan;
3810 max_prio = tmp_if->prio;
3811 }
3812 }
3813 }
3814
3815 if (target_chan) {
3816 tmp_chan = wl_ext_get_chan(apsta_params, cur_if->dev);
3817 if (apsta_params->rsdb && tmp_chan &&
3818 wl_ext_diff_band(tmp_chan, target_chan)) {
3819 WL_MSG(cur_if->ifname, "[%c] keep on current channel %d\n",
3820 cur_if->prefix, tmp_chan);
3821 cur_if->channel = 0;
3822 } else {
3823 WL_MSG(cur_if->ifname, "[%c] channel=%d => %s[%c] channel=%d\n",
3824 cur_if->prefix, cur_if->channel,
3825 target_if->ifname, target_if->prefix, target_chan);
3826 cur_if->channel = target_chan;
3827 }
3828 }
3829
3830 exit:
3831 wl_ext_move_cur_dfs_channel(apsta_params, cur_if);
3832
3833 return cur_if->channel;
3834 }
3835
3836 static void
3837 wl_ext_move_other_channel(struct wl_apsta_params *apsta_params,
3838 struct wl_if_info *cur_if)
3839 {
3840 struct wl_if_info *tmp_if, *target_if=NULL;
3841 uint16 tmp_chan, target_chan = 0;
3842 wl_prio_t max_prio = 0, cur_prio;
3843 int i;
3844
3845 if (apsta_params->vsdb || !cur_if->channel) {
3846 return;
3847 }
3848
3849 // find the max prio, but lower than cur_if
3850 cur_prio = cur_if->prio;
3851 for (i=0; i<MAX_IF_NUM; i++) {
3852 tmp_if = &apsta_params->if_info[i];
3853 if (cur_if != tmp_if && wl_get_isam_status(tmp_if, IF_READY) &&
3854 tmp_if->prio >= max_prio && tmp_if->prio <= cur_prio) {
3855 tmp_chan = wl_ext_get_vsdb_chan(apsta_params, cur_if, tmp_if);
3856 if (tmp_chan) {
3857 target_if = tmp_if;
3858 target_chan = tmp_chan;
3859 max_prio = tmp_if->prio;
3860 }
3861 }
3862 }
3863
3864 if (target_if) {
3865 WL_MSG(target_if->ifname, "channel=%d => %s channel=%d\n",
3866 target_chan, cur_if->ifname, cur_if->channel);
3867 target_if->channel = cur_if->channel;
3868 wl_ext_move_other_dfs_channel(apsta_params, target_if);
3869 if (apsta_params->csa == 0) {
3870 wl_ext_if_down(apsta_params, target_if);
3871 wl_ext_move_other_channel(apsta_params, cur_if);
3872 if (target_if->ifmode == ISTA_MODE || target_if->ifmode == IMESH_MODE) {
3873 wl_ext_enable_iface(target_if->dev, target_if->ifname, 0);
3874 } else if (target_if->ifmode == IAP_MODE) {
3875 wl_ext_if_up(apsta_params, target_if);
3876 }
3877 } else {
3878 wl_ext_trigger_csa(apsta_params, target_if);
3879 }
3880 }
3881
3882 }
3883
3884 static bool
3885 wl_ext_wait_other_enabling(struct wl_apsta_params *apsta_params,
3886 struct wl_if_info *cur_if)
3887 {
3888 struct wl_if_info *tmp_if;
3889 bool enabling = FALSE;
3890 u32 timeout = 1;
3891 int i;
3892
3893 for (i=0; i<MAX_IF_NUM; i++) {
3894 tmp_if = &apsta_params->if_info[i];
3895 if (tmp_if->dev && tmp_if->dev != cur_if->dev) {
3896 if (tmp_if->ifmode == ISTA_MODE)
3897 enabling = wl_get_isam_status(tmp_if, STA_CONNECTING);
3898 else if (tmp_if->ifmode == IAP_MODE || tmp_if->ifmode == IMESH_MODE)
3899 enabling = wl_get_isam_status(tmp_if, AP_CREATING);
3900 if (enabling)
3901 WL_MSG(cur_if->ifname, "waiting for %s[%c] enabling...\n",
3902 tmp_if->ifname, tmp_if->prefix);
3903 if (enabling && tmp_if->ifmode == ISTA_MODE) {
3904 timeout = wait_event_interruptible_timeout(
3905 apsta_params->netif_change_event,
3906 !wl_get_isam_status(tmp_if, STA_CONNECTING),
3907 msecs_to_jiffies(MAX_STA_LINK_WAIT_TIME));
3908 } else if (enabling &&
3909 (tmp_if->ifmode == IAP_MODE || tmp_if->ifmode == IMESH_MODE)) {
3910 timeout = wait_event_interruptible_timeout(
3911 apsta_params->netif_change_event,
3912 !wl_get_isam_status(tmp_if, AP_CREATING),
3913 msecs_to_jiffies(MAX_STA_LINK_WAIT_TIME));
3914 }
3915 if (tmp_if->ifmode == ISTA_MODE)
3916 enabling = wl_get_isam_status(tmp_if, STA_CONNECTING);
3917 else if (tmp_if->ifmode == IAP_MODE || tmp_if->ifmode == IMESH_MODE)
3918 enabling = wl_get_isam_status(tmp_if, AP_CREATING);
3919 if (timeout <= 0 || enabling) {
3920 WL_MSG(cur_if->ifname, "%s[%c] is still enabling...\n",
3921 tmp_if->ifname, tmp_if->prefix);
3922 }
3923 }
3924 }
3925
3926 return enabling;
3927 }
3928
3929 static int
3930 wl_ext_enable_iface(struct net_device *dev, char *ifname, int wait_up)
3931 {
3932 struct dhd_pub *dhd = dhd_get_pub(dev);
3933 int i, ret = 0;
3934 s8 iovar_buf[WLC_IOCTL_SMLEN];
3935 wlc_ssid_t ssid = { 0, {0} };
3936 chanspec_t fw_chspec;
3937 struct {
3938 s32 cfg;
3939 s32 val;
3940 } bss_setbuf;
3941 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
3942 apstamode_t apstamode = apsta_params->apstamode;
3943 struct wl_if_info *cur_if = NULL, *tmp_if = NULL;
3944 uint16 cur_chan;
3945 struct wl_conn_info conn_info;
3946 u32 timeout;
3947
3948 for (i=0; i<MAX_IF_NUM; i++) {
3949 tmp_if = &apsta_params->if_info[i];
3950 if (tmp_if->dev && !strcmp(tmp_if->dev->name, ifname)) {
3951 cur_if = tmp_if;
3952 break;
3953 }
3954 }
3955 if (!cur_if) {
3956 AEXT_ERROR(dev->name, "wrong ifname=%s or dev not ready\n", ifname);
3957 return -1;
3958 }
3959
3960 mutex_lock(&apsta_params->usr_sync);
3961
3962 if (cur_if->ifmode == ISTA_MODE) {
3963 wl_set_isam_status(cur_if, STA_CONNECTING);
3964 } else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
3965 wl_set_isam_status(cur_if, AP_CREATING);
3966 }
3967
3968 wl_ext_isam_status(cur_if->dev, NULL, 0);
3969 WL_MSG(ifname, "[%c] Enabling...\n", cur_if->prefix);
3970
3971 wl_ext_wait_other_enabling(apsta_params, cur_if);
3972
3973 if (wl_ext_master_if(cur_if) && apsta_params->acs) {
3974 uint16 chan_2g, chan_5g;
3975 uint auto_band;
3976 auto_band = WL_GET_BAND(cur_if->channel);
3977 wl_ext_get_default_chan(cur_if->dev, &chan_2g, &chan_5g, TRUE);
3978 if ((chan_2g && auto_band == WLC_BAND_2G) ||
3979 (chan_5g && auto_band == WLC_BAND_5G)) {
3980 cur_if->channel = wl_ext_autochannel(cur_if->dev, apsta_params->acs,
3981 auto_band);
3982 } else {
3983 AEXT_ERROR(ifname, "invalid channel\n");
3984 ret = -1;
3985 goto exit;
3986 }
3987 }
3988
3989 wl_ext_move_cur_channel(apsta_params, cur_if);
3990
3991 if (wl_ext_master_if(cur_if) && !cur_if->channel) {
3992 AEXT_ERROR(ifname, "skip channel 0\n");
3993 ret = -1;
3994 goto exit;
3995 }
3996
3997 cur_chan = wl_ext_get_chan(apsta_params, cur_if->dev);
3998 if (cur_chan) {
3999 AEXT_INFO(cur_if->ifname, "Associated\n");
4000 if (cur_chan != cur_if->channel) {
4001 wl_ext_trigger_csa(apsta_params, cur_if);
4002 }
4003 goto exit;
4004 }
4005 if (cur_if->ifmode == ISTA_MODE) {
4006 wl_clr_isam_status(cur_if, STA_CONNECTED);
4007 } else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
4008 wl_clr_isam_status(cur_if, AP_CREATED);
4009 }
4010
4011 wl_ext_move_other_channel(apsta_params, cur_if);
4012
4013 if (cur_if->ifidx > 0) {
4014 wl_ext_iovar_setbuf(cur_if->dev, "cur_etheraddr", (u8 *)cur_if->dev->dev_addr,
4015 ETHER_ADDR_LEN, iovar_buf, WLC_IOCTL_SMLEN, NULL);
4016 }
4017
4018 // set ssid for AP
4019 ssid.SSID_len = strlen(cur_if->ssid);
4020 memcpy(ssid.SSID, cur_if->ssid, ssid.SSID_len);
4021 if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
4022 wl_ext_iovar_setint(dev, "mpc", 0);
4023 if (apstamode == IAPONLY_MODE || apstamode == IMESHONLY_MODE) {
4024 wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
4025 } else if (apstamode==ISTAAP_MODE || apstamode==ISTAGO_MODE) {
4026 wl_ext_iovar_setbuf_bsscfg(cur_if->dev, "ssid", &ssid, sizeof(ssid),
4027 iovar_buf, WLC_IOCTL_SMLEN, cur_if->bssidx, NULL);
4028 }
4029 }
4030
4031 if (wl_ext_master_if(cur_if)) {
4032 wl_ext_set_bgnmode(cur_if);
4033 if (!cur_if->channel) {
4034 cur_if->channel = 1;
4035 }
4036 ret = wl_ext_set_chanspec(cur_if->dev, apsta_params->ioctl_ver,
4037 cur_if->channel, &fw_chspec);
4038 if (ret)
4039 goto exit;
4040 }
4041
4042 wl_ext_set_amode(cur_if);
4043 wl_ext_set_emode(apsta_params, cur_if);
4044
4045 if (cur_if->ifmode == ISTA_MODE) {
4046 conn_info.bssidx = cur_if->bssidx;
4047 conn_info.channel = cur_if->channel;
4048 memcpy(conn_info.ssid.SSID, cur_if->ssid, strlen(cur_if->ssid));
4049 conn_info.ssid.SSID_len = strlen(cur_if->ssid);
4050 memcpy(&conn_info.bssid, &cur_if->bssid, ETHER_ADDR_LEN);
4051 }
4052 if (cur_if->ifmode == IAP_MODE) {
4053 if (cur_if->maxassoc >= 0)
4054 wl_ext_iovar_setint(dev, "maxassoc", cur_if->maxassoc);
4055 // terence: fix me, hidden does not work in dualAP mode
4056 if (cur_if->hidden > 0) {
4057 wl_ext_ioctl(cur_if->dev, WLC_SET_CLOSED, &cur_if->hidden,
4058 sizeof(cur_if->hidden), 1);
4059 WL_MSG(ifname, "[%c] Broadcast SSID: %s\n",
4060 cur_if->prefix, cur_if->hidden ? "OFF":"ON");
4061 }
4062 }
4063
4064 if (apstamode == ISTAONLY_MODE) {
4065 wl_ext_connect(cur_if->dev, &conn_info);
4066 } else if (apstamode == IAPONLY_MODE) {
4067 wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);
4068 wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
4069 } else if (apstamode == ISTAAP_MODE || apstamode == ISTAGO_MODE) {
4070 if (cur_if->ifmode == ISTA_MODE) {
4071 wl_ext_connect(cur_if->dev, &conn_info);
4072 } else {
4073 if (FW_SUPPORTED(dhd, rsdb)) {
4074 wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);
4075 } else {
4076 bss_setbuf.cfg = htod32(cur_if->bssidx);
4077 bss_setbuf.val = htod32(1);
4078 wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf,
4079 sizeof(bss_setbuf), iovar_buf, WLC_IOCTL_SMLEN, NULL);
4080 }
4081 #ifdef ARP_OFFLOAD_SUPPORT
4082 /* IF SoftAP is enabled, disable arpoe */
4083 dhd_arp_offload_set(dhd, 0);
4084 dhd_arp_offload_enable(dhd, FALSE);
4085 #endif /* ARP_OFFLOAD_SUPPORT */
4086 #ifdef PROP_TXSTATUS_VSDB
4087 #if defined(BCMSDIO)
4088 if (!(FW_SUPPORTED(dhd, rsdb)) && !disable_proptx) {
4089 bool enabled;
4090 dhd_wlfc_get_enable(dhd, &enabled);
4091 if (!enabled) {
4092 dhd_wlfc_init(dhd);
4093 wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
4094 }
4095 }
4096 #endif /* BCMSDIO */
4097 #endif /* PROP_TXSTATUS_VSDB */
4098 }
4099 }
4100 else if (apstamode == IDUALAP_MODE) {
4101 wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);
4102 } else if (apstamode == ISTAAPAP_MODE) {
4103 if (cur_if->ifmode == ISTA_MODE) {
4104 wl_ext_connect(cur_if->dev, &conn_info);
4105 } else if (cur_if->ifmode == IAP_MODE) {
4106 wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);
4107 } else {
4108 AEXT_ERROR(cur_if->ifname, "wrong ifmode %d\n", cur_if->ifmode);
4109 }
4110 #ifdef WLMESH
4111 } else if (apstamode == IMESHONLY_MODE ||
4112 apstamode == ISTAMESH_MODE || apstamode == IMESHAP_MODE ||
4113 apstamode == ISTAAPMESH_MODE || apstamode == IMESHAPAP_MODE) {
4114 if (cur_if->ifmode == ISTA_MODE) {
4115 wl_ext_connect(cur_if->dev, &conn_info);
4116 } else if (cur_if->ifmode == IAP_MODE) {
4117 wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);
4118 } else if (cur_if->ifmode == IMESH_MODE) {
4119 struct wl_join_params join_params;
4120 // need to up before setting ssid
4121 memset(&join_params, 0, sizeof(join_params));
4122 join_params.ssid.SSID_len = strlen(cur_if->ssid);
4123 memcpy((void *)join_params.ssid.SSID, cur_if->ssid, strlen(cur_if->ssid));
4124 join_params.params.chanspec_list[0] = fw_chspec;
4125 join_params.params.chanspec_num = 1;
4126 wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &join_params, sizeof(join_params), 1);
4127 } else {
4128 AEXT_ERROR(cur_if->ifname, "wrong ifmode %d\n", cur_if->ifmode);
4129 }
4130 #endif /* WLMESH */
4131 }
4132
4133 if (wait_up) {
4134 OSL_SLEEP(wait_up);
4135 } else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
4136 timeout = wait_event_interruptible_timeout(apsta_params->netif_change_event,
4137 wl_get_isam_status(cur_if, AP_CREATED),
4138 msecs_to_jiffies(MAX_AP_LINK_WAIT_TIME));
4139 if (timeout <= 0 || !wl_get_isam_status(cur_if, AP_CREATED)) {
4140 mutex_unlock(&apsta_params->usr_sync);
4141 wl_ext_disable_iface(dev, cur_if->ifname);
4142 WL_MSG(ifname, "[%c] failed to enable with SSID: \"%s\"\n",
4143 cur_if->prefix, cur_if->ssid);
4144 ret = -1;
4145 }
4146 }
4147
4148 if (wl_get_isam_status(cur_if, AP_CREATED) &&
4149 (cur_if->ifmode == IMESH_MODE || cur_if->ifmode == IAP_MODE) &&
4150 (apstamode == ISTAAP_MODE || apstamode == ISTAAPAP_MODE ||
4151 apstamode == ISTAMESH_MODE || apstamode == IMESHAP_MODE ||
4152 apstamode == ISTAAPMESH_MODE || apstamode == IMESHAPAP_MODE)) {
4153 int scan_assoc_time = 80;
4154 for (i=0; i<MAX_IF_NUM; i++) {
4155 tmp_if = &apsta_params->if_info[i];
4156 if (tmp_if->dev && tmp_if->ifmode == ISTA_MODE) {
4157 wl_ext_ioctl(tmp_if->dev, WLC_SET_SCAN_CHANNEL_TIME,
4158 &scan_assoc_time, sizeof(scan_assoc_time), 1);
4159 }
4160 }
4161 }
4162
4163 wl_ext_isam_status(cur_if->dev, NULL, 0);
4164
4165 exit:
4166 if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
4167 wl_clr_isam_status(cur_if, AP_CREATING);
4168 }
4169 WL_MSG(ifname, "[%c] Exit ret=%d\n", cur_if->prefix, ret);
4170 mutex_unlock(&apsta_params->usr_sync);
4171 return ret;
4172 }
4173
4174 static int
4175 wl_ext_iapsta_enable(struct net_device *dev, char *command, int total_len)
4176 {
4177 int ret = 0;
4178 char *pch, *pick_tmp, *param;
4179 char ifname[IFNAMSIZ+1];
4180
4181 AEXT_TRACE(dev->name, "command=%s, len=%d\n", command, total_len);
4182
4183 pick_tmp = command;
4184 param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_enable
4185 param = bcmstrtok(&pick_tmp, " ", 0);
4186 while (param != NULL) {
4187 if (!strcmp(param, "ifname")) {
4188 pch = bcmstrtok(&pick_tmp, " ", 0);
4189 if (pch) {
4190 strcpy(ifname, pch);
4191 ret = wl_ext_enable_iface(dev, ifname, 0);
4192 if (ret)
4193 return ret;
4194 } else {
4195 AEXT_ERROR(dev->name, "ifname [wlanX]\n");
4196 return -1;
4197 }
4198 }
4199 param = bcmstrtok(&pick_tmp, " ", 0);
4200 }
4201
4202 return ret;
4203 }
4204
4205 #ifdef PROPTX_MAXCOUNT
4206 int
4207 wl_ext_get_wlfc_maxcount(struct dhd_pub *dhd, int ifidx)
4208 {
4209 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
4210 struct wl_if_info *tmp_if, *cur_if = NULL;
4211 int i, maxcount = WL_TXSTATUS_FREERUNCTR_MASK;
4212
4213 if (!apsta_params->rsdb)
4214 return maxcount;
4215
4216 for (i=0; i<MAX_IF_NUM; i++) {
4217 tmp_if = &apsta_params->if_info[i];
4218 if (tmp_if->dev && tmp_if->ifidx == ifidx) {
4219 cur_if = tmp_if;
4220 maxcount = cur_if->transit_maxcount;
4221 }
4222 }
4223
4224 if (cur_if)
4225 AEXT_INFO(cur_if->ifname, "update maxcount %d\n", maxcount);
4226 else
4227 AEXT_INFO("wlan", "update maxcount %d for ifidx %d\n", maxcount, ifidx);
4228 return maxcount;
4229 }
4230
4231 void
4232 wl_ext_update_wlfc_maxcount(struct dhd_pub *dhd)
4233 {
4234 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
4235 struct wl_if_info *tmp_if;
4236 bool band_5g = FALSE;
4237 uint16 chan = 0;
4238 int i, ret;
4239
4240 if (!apsta_params->rsdb)
4241 return;
4242
4243 for (i=0; i<MAX_IF_NUM; i++) {
4244 tmp_if = &apsta_params->if_info[i];
4245 if (tmp_if->dev) {
4246 chan = wl_ext_get_chan(apsta_params, tmp_if->dev);
4247 if (chan > CH_MAX_2G_CHANNEL) {
4248 tmp_if->transit_maxcount = dhd->conf->proptx_maxcnt_5g;
4249 ret = dhd_wlfc_update_maxcount(dhd, tmp_if->ifidx,
4250 tmp_if->transit_maxcount);
4251 if (ret == 0)
4252 AEXT_INFO(tmp_if->ifname, "updated maxcount %d\n",
4253 tmp_if->transit_maxcount);
4254 band_5g = TRUE;
4255 }
4256 }
4257 }
4258
4259 for (i=0; i<MAX_IF_NUM; i++) {
4260 tmp_if = &apsta_params->if_info[i];
4261 if (tmp_if->dev) {
4262 chan = wl_ext_get_chan(apsta_params, tmp_if->dev);
4263 if ((chan == 0) || (chan <= CH_MAX_2G_CHANNEL && chan >= CH_MIN_2G_CHANNEL)) {
4264 if (chan == 0) {
4265 tmp_if->transit_maxcount = WL_TXSTATUS_FREERUNCTR_MASK;
4266 } else if (band_5g) {
4267 tmp_if->transit_maxcount = dhd->conf->proptx_maxcnt_2g;
4268 } else {
4269 tmp_if->transit_maxcount = dhd->conf->proptx_maxcnt_5g;
4270 }
4271 ret = dhd_wlfc_update_maxcount(dhd, tmp_if->ifidx,
4272 tmp_if->transit_maxcount);
4273 if (ret == 0)
4274 AEXT_INFO(tmp_if->ifname, "updated maxcount %d\n",
4275 tmp_if->transit_maxcount);
4276 }
4277 }
4278 }
4279 }
4280 #endif /* PROPTX_MAXCOUNT */
4281
4282 static int
4283 wl_ext_iapsta_event(struct net_device *dev,
4284 struct wl_apsta_params *apsta_params, wl_event_msg_t *e, void* data)
4285 {
4286 struct wl_if_info *cur_if = NULL, *tmp_if = NULL;
4287 #if defined(WLMESH) && defined(WL_ESCAN)
4288 struct wl_if_info *mesh_if = NULL;
4289 #endif /* WLMESH && WL_ESCAN */
4290 int i;
4291 uint32 event_type = ntoh32(e->event_type);
4292 uint32 status = ntoh32(e->status);
4293 uint32 reason = ntoh32(e->reason);
4294 uint16 flags = ntoh16(e->flags);
4295
4296 for (i=0; i<MAX_IF_NUM; i++) {
4297 tmp_if = &apsta_params->if_info[i];
4298 if (tmp_if->dev == dev) {
4299 cur_if = tmp_if;
4300 break;
4301 }
4302 }
4303 #if defined(WLMESH) && defined(WL_ESCAN)
4304 for (i=0; i<MAX_IF_NUM; i++) {
4305 tmp_if = &apsta_params->if_info[i];
4306 if (tmp_if->dev && tmp_if->ifmode == IMESH_MODE) {
4307 mesh_if = tmp_if;
4308 break;
4309 }
4310 }
4311 #endif /* WLMESH && WL_ESCAN */
4312 if (!cur_if || !cur_if->dev) {
4313 AEXT_DBG(dev->name, "ifidx %d is not ready\n", e->ifidx);
4314 return -1;
4315 }
4316
4317 if (cur_if->ifmode == ISTA_MODE || cur_if->ifmode == IGC_MODE) {
4318 if (event_type == WLC_E_LINK) {
4319 if (!(flags & WLC_EVENT_MSG_LINK)) {
4320 WL_MSG(cur_if->ifname,
4321 "[%c] Link down with %pM, %s(%d), reason %d\n",
4322 cur_if->prefix, &e->addr, bcmevent_get_name(event_type),
4323 event_type, reason);
4324 wl_clr_isam_status(cur_if, STA_CONNECTED);
4325 #if defined(WLMESH) && defined(WL_ESCAN)
4326 if (mesh_if && apsta_params->macs)
4327 wl_mesh_clear_mesh_info(apsta_params, mesh_if, TRUE);
4328 #endif /* WLMESH && WL_ESCAN */
4329 } else {
4330 WL_MSG(cur_if->ifname, "[%c] Link UP with %pM\n",
4331 cur_if->prefix, &e->addr);
4332 wl_set_isam_status(cur_if, STA_CONNECTED);
4333 #if defined(WLMESH) && defined(WL_ESCAN)
4334 if (mesh_if && apsta_params->macs)
4335 wl_mesh_update_master_info(apsta_params, mesh_if);
4336 #endif /* WLMESH && WL_ESCAN */
4337 }
4338 wl_clr_isam_status(cur_if, STA_CONNECTING);
4339 wake_up_interruptible(&apsta_params->netif_change_event);
4340 #ifdef PROPTX_MAXCOUNT
4341 wl_ext_update_wlfc_maxcount(apsta_params->dhd);
4342 #endif /* PROPTX_MAXCOUNT */
4343 } else if (event_type == WLC_E_SET_SSID && status != WLC_E_STATUS_SUCCESS) {
4344 WL_MSG(cur_if->ifname,
4345 "connect failed event=%d, reason=%d, status=%d\n",
4346 event_type, reason, status);
4347 wl_clr_isam_status(cur_if, STA_CONNECTING);
4348 wake_up_interruptible(&apsta_params->netif_change_event);
4349 #if defined(WLMESH) && defined(WL_ESCAN)
4350 if (mesh_if && apsta_params->macs)
4351 wl_mesh_clear_mesh_info(apsta_params, mesh_if, TRUE);
4352 #endif /* WLMESH && WL_ESCAN */
4353 #ifdef PROPTX_MAXCOUNT
4354 wl_ext_update_wlfc_maxcount(apsta_params->dhd);
4355 #endif /* PROPTX_MAXCOUNT */
4356 } else if (event_type == WLC_E_DEAUTH || event_type == WLC_E_DEAUTH_IND ||
4357 event_type == WLC_E_DISASSOC || event_type == WLC_E_DISASSOC_IND) {
4358 WL_MSG(cur_if->ifname, "[%c] Link down with %pM, %s(%d), reason %d\n",
4359 cur_if->prefix, &e->addr, bcmevent_get_name(event_type),
4360 event_type, reason);
4361 #if defined(WLMESH) && defined(WL_ESCAN)
4362 if (mesh_if && apsta_params->macs)
4363 wl_mesh_clear_mesh_info(apsta_params, mesh_if, TRUE);
4364 #endif /* WLMESH && WL_ESCAN */
4365 }
4366 }
4367 else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
4368 if ((event_type == WLC_E_SET_SSID && status == WLC_E_STATUS_SUCCESS) ||
4369 (event_type == WLC_E_LINK && status == WLC_E_STATUS_SUCCESS &&
4370 reason == WLC_E_REASON_INITIAL_ASSOC)) {
4371 if (wl_get_isam_status(cur_if, AP_CREATING)) {
4372 WL_MSG(cur_if->ifname, "[%c] Link up (etype=%d)\n",
4373 cur_if->prefix, event_type);
4374 wl_set_isam_status(cur_if, AP_CREATED);
4375 wake_up_interruptible(&apsta_params->netif_change_event);
4376 } else {
4377 wl_set_isam_status(cur_if, AP_CREATED);
4378 WL_MSG(cur_if->ifname, "[%c] Link up w/o creating? (etype=%d)\n",
4379 cur_if->prefix, event_type);
4380 }
4381 #ifdef PROPTX_MAXCOUNT
4382 wl_ext_update_wlfc_maxcount(apsta_params->dhd);
4383 #endif /* PROPTX_MAXCOUNT */
4384 }
4385 else if ((event_type == WLC_E_LINK && reason == WLC_E_LINK_BSSCFG_DIS) ||
4386 (event_type == WLC_E_LINK && status == WLC_E_STATUS_SUCCESS &&
4387 reason == WLC_E_REASON_DEAUTH)) {
4388 wl_clr_isam_status(cur_if, AP_CREATED);
4389 WL_MSG(cur_if->ifname, "[%c] Link down, reason=%d\n",
4390 cur_if->prefix, reason);
4391 #ifdef PROPTX_MAXCOUNT
4392 wl_ext_update_wlfc_maxcount(apsta_params->dhd);
4393 #endif /* PROPTX_MAXCOUNT */
4394 }
4395 else if ((event_type == WLC_E_ASSOC_IND || event_type == WLC_E_REASSOC_IND) &&
4396 reason == DOT11_SC_SUCCESS) {
4397 WL_MSG(cur_if->ifname, "[%c] connected device %pM\n",
4398 cur_if->prefix, &e->addr);
4399 wl_ext_isam_status(cur_if->dev, NULL, 0);
4400 }
4401 else if (event_type == WLC_E_DISASSOC_IND ||
4402 event_type == WLC_E_DEAUTH_IND ||
4403 (event_type == WLC_E_DEAUTH && reason != DOT11_RC_RESERVED)) {
4404 WL_MSG_RLMT(cur_if->ifname, &e->addr, ETHER_ADDR_LEN,
4405 "[%c] disconnected device %pM, %s(%d), reason=%d\n",
4406 cur_if->prefix, &e->addr, bcmevent_get_name(event_type),
4407 event_type, reason);
4408 wl_ext_isam_status(cur_if->dev, NULL, 0);
4409 }
4410 #if defined(WLMESH) && defined(WL_ESCAN)
4411 if (cur_if->ifmode == IMESH_MODE && apsta_params->macs)
4412 wl_mesh_event_handler(apsta_params, cur_if, e, data);
4413 #endif /* WLMESH && WL_ESCAN */
4414 }
4415
4416 return 0;
4417 }
4418
4419 #ifdef WL_CFG80211
4420 u32
4421 wl_ext_iapsta_update_channel(dhd_pub_t *dhd, struct net_device *dev,
4422 u32 channel)
4423 {
4424 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
4425 struct wl_if_info *cur_if = NULL, *tmp_if = NULL;
4426 int i;
4427
4428 for (i=0; i<MAX_IF_NUM; i++) {
4429 tmp_if = &apsta_params->if_info[i];
4430 if (tmp_if->dev && tmp_if->dev == dev) {
4431 cur_if = tmp_if;
4432 break;
4433 }
4434 }
4435
4436 if (cur_if) {
4437 wl_ext_isam_status(cur_if->dev, NULL, 0);
4438 cur_if->channel = channel;
4439 if (wl_ext_master_if(cur_if) && apsta_params->acs) {
4440 uint auto_band = WL_GET_BAND(channel);
4441 cur_if->channel = wl_ext_autochannel(cur_if->dev, apsta_params->acs,
4442 auto_band);
4443 }
4444 channel = wl_ext_move_cur_channel(apsta_params, cur_if);
4445 if (channel)
4446 wl_ext_move_other_channel(apsta_params, cur_if);
4447 if (cur_if->ifmode == ISTA_MODE)
4448 wl_set_isam_status(cur_if, STA_CONNECTING);
4449 }
4450
4451 return channel;
4452 }
4453
4454 static int
4455 wl_ext_iftype_to_ifmode(struct net_device *net, int wl_iftype, ifmode_t *ifmode)
4456 {
4457 switch (wl_iftype) {
4458 case WL_IF_TYPE_STA:
4459 *ifmode = ISTA_MODE;
4460 break;
4461 case WL_IF_TYPE_AP:
4462 *ifmode = IAP_MODE;
4463 break;
4464 case WL_IF_TYPE_P2P_GO:
4465 *ifmode = IGO_MODE;
4466 break;
4467 case WL_IF_TYPE_P2P_GC:
4468 *ifmode = IGC_MODE;
4469 break;
4470 default:
4471 AEXT_ERROR(net->name, "Unknown interface wl_iftype:0x%x\n", wl_iftype);
4472 return BCME_ERROR;
4473 }
4474 return BCME_OK;
4475 }
4476
4477 void
4478 wl_ext_iapsta_update_iftype(struct net_device *net, int ifidx, int wl_iftype)
4479 {
4480 struct dhd_pub *dhd = dhd_get_pub(net);
4481 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
4482 struct wl_if_info *cur_if = NULL;
4483
4484 AEXT_TRACE(net->name, "ifidx=%d, wl_iftype=%d\n", ifidx, wl_iftype);
4485
4486 if (ifidx < MAX_IF_NUM) {
4487 cur_if = &apsta_params->if_info[ifidx];
4488 }
4489
4490 if (cur_if) {
4491 if (wl_iftype == WL_IF_TYPE_STA) {
4492 cur_if->ifmode = ISTA_MODE;
4493 cur_if->prio = PRIO_STA;
4494 cur_if->prefix = 'S';
4495 } else if (wl_iftype == WL_IF_TYPE_AP && cur_if->ifmode != IMESH_MODE) {
4496 cur_if->ifmode = IAP_MODE;
4497 cur_if->prio = PRIO_AP;
4498 cur_if->prefix = 'A';
4499 } else if (wl_iftype == WL_IF_TYPE_P2P_GO) {
4500 cur_if->ifmode = IGO_MODE;
4501 cur_if->prio = PRIO_AP;
4502 cur_if->prefix = 'P';
4503 apsta_params->vsdb = TRUE;
4504 } else if (wl_iftype == WL_IF_TYPE_P2P_GC) {
4505 cur_if->ifmode = IGC_MODE;
4506 cur_if->prio = PRIO_STA;
4507 cur_if->prefix = 'P';
4508 apsta_params->vsdb = TRUE;
4509 wl_ext_iovar_setint(cur_if->dev, "assoc_retry_max", 3);
4510 }
4511 }
4512 }
4513
4514 void
4515 wl_ext_iapsta_ifadding(struct net_device *net, int ifidx)
4516 {
4517 struct dhd_pub *dhd = dhd_get_pub(net);
4518 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
4519 struct wl_if_info *cur_if = NULL;
4520
4521 AEXT_TRACE(net->name, "ifidx=%d\n", ifidx);
4522 if (ifidx < MAX_IF_NUM) {
4523 cur_if = &apsta_params->if_info[ifidx];
4524 wl_set_isam_status(cur_if, IF_ADDING);
4525 }
4526 }
4527
4528 bool
4529 wl_ext_iapsta_iftype_enabled(struct net_device *net, int wl_iftype)
4530 {
4531 struct dhd_pub *dhd = dhd_get_pub(net);
4532 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
4533 struct wl_if_info *cur_if = NULL;
4534 ifmode_t ifmode = 0;
4535
4536 wl_ext_iftype_to_ifmode(net, wl_iftype, &ifmode);
4537 cur_if = wl_ext_if_enabled(apsta_params, ifmode);
4538 if (cur_if)
4539 return TRUE;
4540
4541 return FALSE;
4542 }
4543
4544 bool
4545 wl_ext_iapsta_mesh_creating(struct net_device *net)
4546 {
4547 struct dhd_pub *dhd = dhd_get_pub(net);
4548 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
4549 struct wl_if_info *cur_if;
4550 int i;
4551
4552 if (apsta_params) {
4553 for (i=0; i<MAX_IF_NUM; i++) {
4554 cur_if = &apsta_params->if_info[i];
4555 if (cur_if->ifmode==IMESH_MODE && wl_get_isam_status(cur_if, IF_ADDING))
4556 return TRUE;
4557 }
4558 }
4559 return FALSE;
4560 }
4561 #endif /* WL_CFG80211 */
4562
4563 int
4564 wl_ext_iapsta_alive_preinit(struct net_device *dev)
4565 {
4566 struct dhd_pub *dhd = dhd_get_pub(dev);
4567 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
4568
4569 if (apsta_params->init == TRUE) {
4570 AEXT_ERROR(dev->name, "don't init twice\n");
4571 return -1;
4572 }
4573
4574 AEXT_TRACE(dev->name, "Enter\n");
4575
4576 apsta_params->init = TRUE;
4577
4578 return 0;
4579 }
4580
4581 int
4582 wl_ext_iapsta_alive_postinit(struct net_device *dev)
4583 {
4584 struct dhd_pub *dhd = dhd_get_pub(dev);
4585 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
4586 s32 apsta = 0, ap = 0;
4587 struct wl_if_info *cur_if;
4588 int i;
4589
4590 wl_ext_iovar_getint(dev, "apsta", &apsta);
4591 wl_ext_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap), 0);
4592 if (apsta == 1 || ap == 0) {
4593 apsta_params->apstamode = ISTAONLY_MODE;
4594 apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE;
4595 op_mode = DHD_FLAG_STA_MODE;
4596 } else {
4597 apsta_params->apstamode = IAPONLY_MODE;
4598 apsta_params->if_info[IF_PIF].ifmode = IAP_MODE;
4599 op_mode = DHD_FLAG_HOSTAP_MODE;
4600 }
4601 // fix me: how to check it's ISTAAP_MODE or IDUALAP_MODE?
4602
4603 wl_ext_get_ioctl_ver(dev, &apsta_params->ioctl_ver);
4604 WL_MSG(dev->name, "apstamode=%d\n", apsta_params->apstamode);
4605
4606 for (i=0; i<MAX_IF_NUM; i++) {
4607 cur_if = &apsta_params->if_info[i];
4608 if (i == 1 && !strlen(cur_if->ifname))
4609 strcpy(cur_if->ifname, "wlan1");
4610 if (i == 2 && !strlen(cur_if->ifname))
4611 strcpy(cur_if->ifname, "wlan2");
4612 if (cur_if->ifmode == ISTA_MODE) {
4613 cur_if->channel = 0;
4614 cur_if->maxassoc = -1;
4615 wl_set_isam_status(cur_if, IF_READY);
4616 cur_if->prio = PRIO_STA;
4617 cur_if->prefix = 'S';
4618 snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_sta");
4619 } else if (cur_if->ifmode == IAP_MODE) {
4620 cur_if->channel = 1;
4621 cur_if->maxassoc = -1;
4622 wl_set_isam_status(cur_if, IF_READY);
4623 cur_if->prio = PRIO_AP;
4624 cur_if->prefix = 'A';
4625 snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_ap");
4626 #ifdef WLMESH
4627 } else if (cur_if->ifmode == IMESH_MODE) {
4628 cur_if->channel = 1;
4629 cur_if->maxassoc = -1;
4630 wl_set_isam_status(cur_if, IF_READY);
4631 cur_if->prio = PRIO_MESH;
4632 cur_if->prefix = 'M';
4633 snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_mesh");
4634 #endif /* WLMESH */
4635 }
4636 }
4637
4638 return op_mode;
4639 }
4640
4641 static int
4642 wl_ext_iapsta_get_rsdb(struct net_device *net, struct dhd_pub *dhd)
4643 {
4644 s8 iovar_buf[WLC_IOCTL_SMLEN];
4645 wl_config_t *rsdb_p;
4646 int ret = 0, rsdb = 0;
4647
4648 if (dhd->conf->chip == BCM4359_CHIP_ID) {
4649 ret = wldev_iovar_getbuf(net, "rsdb_mode", NULL, 0,
4650 iovar_buf, WLC_IOCTL_SMLEN, NULL);
4651 if (!ret) {
4652 if (dhd->conf->fw_type == FW_TYPE_MESH) {
4653 rsdb = 1;
4654 } else {
4655 rsdb_p = (wl_config_t *) iovar_buf;
4656 rsdb = rsdb_p->config;
4657 }
4658 }
4659 }
4660
4661 AEXT_INFO(net->name, "rsdb_mode=%d\n", rsdb);
4662
4663 return rsdb;
4664 }
4665
4666 static void
4667 wl_ext_iapsta_postinit(struct net_device *net, struct wl_if_info *cur_if)
4668 {
4669 struct dhd_pub *dhd = dhd_get_pub(net);
4670 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
4671 int pm;
4672
4673 AEXT_TRACE(cur_if->ifname, "ifidx=%d\n", cur_if->ifidx);
4674 if (cur_if->ifidx == 0) {
4675 apsta_params->rsdb = wl_ext_iapsta_get_rsdb(net, dhd);
4676 apsta_params->vsdb = FALSE;
4677 apsta_params->csa = 0;
4678 apsta_params->acs = 0;
4679 apsta_params->radar = wl_ext_radar_detect(net);
4680 if (dhd->conf->fw_type == FW_TYPE_MESH) {
4681 apsta_params->csa |= (CSA_FW_BIT | CSA_DRV_BIT);
4682 }
4683 } else {
4684 if (cur_if->ifmode == ISTA_MODE) {
4685 wl_ext_iovar_setint(cur_if->dev, "roam_off", dhd->conf->roam_off);
4686 wl_ext_iovar_setint(cur_if->dev, "bcn_timeout", dhd->conf->bcn_timeout);
4687 if (dhd->conf->pm >= 0)
4688 pm = dhd->conf->pm;
4689 else
4690 pm = PM_FAST;
4691 wl_ext_ioctl(cur_if->dev, WLC_SET_PM, &pm, sizeof(pm), 1);
4692 wl_ext_iovar_setint(cur_if->dev, "assoc_retry_max", 20);
4693 }
4694 #ifdef WLMESH
4695 else if (cur_if->ifmode == IMESH_MODE) {
4696 pm = 0;
4697 wl_ext_ioctl(cur_if->dev, WLC_SET_PM, &pm, sizeof(pm), 1);
4698 }
4699 #endif /* WLMESH */
4700 }
4701 #ifdef PROPTX_MAXCOUNT
4702 wl_ext_update_wlfc_maxcount(dhd);
4703 #endif /* PROPTX_MAXCOUNT */
4704
4705 }
4706
4707 int
4708 wl_ext_iapsta_attach_name(struct net_device *net, int ifidx)
4709 {
4710 struct dhd_pub *dhd = dhd_get_pub(net);
4711 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
4712 struct wl_if_info *cur_if = NULL;
4713
4714 AEXT_TRACE(net->name, "ifidx=%d\n", ifidx);
4715 if (ifidx < MAX_IF_NUM) {
4716 cur_if = &apsta_params->if_info[ifidx];
4717 }
4718 if (ifidx == 0) {
4719 strcpy(cur_if->ifname, net->name);
4720 wl_ext_iapsta_postinit(net, cur_if);
4721 wl_set_isam_status(cur_if, IF_READY);
4722 } else if (cur_if && wl_get_isam_status(cur_if, IF_ADDING)) {
4723 strcpy(cur_if->ifname, net->name);
4724 wl_ext_iapsta_postinit(net, cur_if);
4725 wl_clr_isam_status(cur_if, IF_ADDING);
4726 wl_set_isam_status(cur_if, IF_READY);
4727 #ifndef WL_STATIC_IF
4728 wake_up_interruptible(&apsta_params->netif_change_event);
4729 #endif /* WL_STATIC_IF */
4730 }
4731
4732 return 0;
4733 }
4734
4735 int
4736 wl_ext_iapsta_update_net_device(struct net_device *net, int ifidx)
4737 {
4738 struct dhd_pub *dhd = dhd_get_pub(net);
4739 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
4740 struct wl_if_info *cur_if = NULL, *primary_if;
4741
4742 AEXT_TRACE(net->name, "ifidx=%d\n", ifidx);
4743 if (ifidx < MAX_IF_NUM) {
4744 cur_if = &apsta_params->if_info[ifidx];
4745 }
4746 if (cur_if && wl_get_isam_status(cur_if, IF_ADDING)) {
4747 primary_if = &apsta_params->if_info[IF_PIF];
4748 if (strlen(cur_if->ifname)) {
4749 memset(net->name, 0, sizeof(IFNAMSIZ));
4750 strcpy(net->name, cur_if->ifname);
4751 net->name[IFNAMSIZ-1] = '\0';
4752 }
4753 #ifndef WL_STATIC_IF
4754 if (apsta_params->apstamode != IUNKNOWN_MODE &&
4755 apsta_params->apstamode != ISTAAPAP_MODE &&
4756 apsta_params->apstamode != ISTASTA_MODE) {
4757 memcpy(net->dev_addr, primary_if->dev->dev_addr, ETHER_ADDR_LEN);
4758 net->dev_addr[0] |= 0x02;
4759 if (ifidx >= 2) {
4760 net->dev_addr[4] ^= 0x80;
4761 net->dev_addr[4] += ifidx;
4762 net->dev_addr[5] += (ifidx-1);
4763 }
4764 }
4765 #endif /* WL_STATIC_IF */
4766 }
4767
4768 return 0;
4769 }
4770
4771 int
4772 wl_ext_iapsta_attach_netdev(struct net_device *net, int ifidx, uint8 bssidx)
4773 {
4774 struct dhd_pub *dhd = dhd_get_pub(net);
4775 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
4776 struct wl_if_info *cur_if = NULL, *primary_if;
4777
4778 AEXT_TRACE(net->name, "ifidx=%d, bssidx=%d\n", ifidx, bssidx);
4779 if (ifidx < MAX_IF_NUM) {
4780 cur_if = &apsta_params->if_info[ifidx];
4781 }
4782 if (ifidx == 0) {
4783 memset(apsta_params, 0, sizeof(struct wl_apsta_params));
4784 apsta_params->dhd = dhd;
4785 cur_if->dev = net;
4786 cur_if->ifidx = ifidx;
4787 cur_if->bssidx = bssidx;
4788 cur_if->ifmode = ISTA_MODE;
4789 cur_if->prio = PRIO_STA;
4790 cur_if->prefix = 'S';
4791 wl_ext_event_register(net, dhd, WLC_E_LAST, wl_ext_iapsta_event,
4792 apsta_params, PRIO_EVENT_IAPSTA);
4793 strcpy(cur_if->ifname, net->name);
4794 init_waitqueue_head(&apsta_params->netif_change_event);
4795 mutex_init(&apsta_params->usr_sync);
4796 mutex_init(&cur_if->pm_sync);
4797 INIT_DELAYED_WORK(&cur_if->pm_enable_work, wl_ext_pm_work_handler);
4798 } else if (cur_if && wl_get_isam_status(cur_if, IF_ADDING)) {
4799 primary_if = &apsta_params->if_info[IF_PIF];
4800 cur_if->dev = net;
4801 cur_if->ifidx = ifidx;
4802 cur_if->bssidx = bssidx;
4803 wl_ext_event_register(net, dhd, WLC_E_LAST, wl_ext_iapsta_event,
4804 apsta_params, PRIO_EVENT_IAPSTA);
4805 #if defined(WLMESH) && defined(WL_ESCAN)
4806 if (cur_if->ifmode == IMESH_MODE && apsta_params->macs) {
4807 wl_mesh_escan_attach(dhd, cur_if);
4808 }
4809 #endif /* WLMESH && WL_ESCAN */
4810 mutex_init(&cur_if->pm_sync);
4811 INIT_DELAYED_WORK(&cur_if->pm_enable_work, wl_ext_pm_work_handler);
4812 }
4813
4814 return 0;
4815 }
4816
4817 int
4818 wl_ext_iapsta_dettach_netdev(struct net_device *net, int ifidx)
4819 {
4820 struct dhd_pub *dhd = dhd_get_pub(net);
4821 struct wl_apsta_params *apsta_params = dhd->iapsta_params;
4822 struct wl_if_info *cur_if = NULL;
4823
4824 if (!apsta_params)
4825 return 0;
4826
4827 AEXT_TRACE(net->name, "ifidx=%d\n", ifidx);
4828 if (ifidx < MAX_IF_NUM) {
4829 cur_if = &apsta_params->if_info[ifidx];
4830 }
4831
4832 if (ifidx == 0) {
4833 wl_ext_add_remove_pm_enable_work(net, FALSE);
4834 wl_ext_event_deregister(net, dhd, WLC_E_LAST, wl_ext_iapsta_event);
4835 #if defined(WLMESH) && defined(WL_ESCAN)
4836 if (cur_if->ifmode == IMESH_MODE && apsta_params->macs) {
4837 wl_mesh_escan_detach(dhd, cur_if);
4838 }
4839 #endif /* WLMESH && WL_ESCAN */
4840 memset(apsta_params, 0, sizeof(struct wl_apsta_params));
4841 } else if (cur_if && (wl_get_isam_status(cur_if, IF_READY) ||
4842 wl_get_isam_status(cur_if, IF_ADDING))) {
4843 wl_ext_add_remove_pm_enable_work(net, FALSE);
4844 wl_ext_event_deregister(net, dhd, WLC_E_LAST, wl_ext_iapsta_event);
4845 #if defined(WLMESH) && defined(WL_ESCAN)
4846 if (cur_if->ifmode == IMESH_MODE && apsta_params->macs) {
4847 wl_mesh_escan_detach(dhd, cur_if);
4848 }
4849 #endif /* WLMESH && WL_ESCAN */
4850 memset(cur_if, 0, sizeof(struct wl_if_info));
4851 }
4852
4853 return 0;
4854 }
4855
4856 int
4857 wl_ext_iapsta_attach(dhd_pub_t *pub)
4858 {
4859 struct wl_apsta_params *iapsta_params;
4860
4861 iapsta_params = kzalloc(sizeof(struct wl_apsta_params), GFP_KERNEL);
4862 if (unlikely(!iapsta_params)) {
4863 AEXT_ERROR("wlan", "Could not allocate apsta_params\n");
4864 return -ENOMEM;
4865 }
4866 pub->iapsta_params = (void *)iapsta_params;
4867
4868 return 0;
4869 }
4870
4871 void
4872 wl_ext_iapsta_dettach(dhd_pub_t *pub)
4873 {
4874 if (pub->iapsta_params) {
4875 kfree(pub->iapsta_params);
4876 pub->iapsta_params = NULL;
4877 }
4878 }
4879 #endif /* WL_EXT_IAPSTA */
4880
4881 #ifdef IDHCP
4882 /*
4883 terence 20190409:
4884 dhd_priv wl dhcpc_dump
4885 dhd_priv wl dhcpc_param <client ip> <server ip> <lease time>
4886 */
4887 static int
4888 wl_ext_dhcpc_dump(struct net_device *dev, char *data, char *command,
4889 int total_len)
4890 {
4891 int ret = 0;
4892 int bytes_written = 0;
4893 uint32 ip_addr;
4894 char buf[20]="";
4895
4896 if (!data) {
4897 ret = wl_ext_iovar_getint(dev, "dhcpc_ip_addr", &ip_addr);
4898 if (!ret) {
4899 bcm_ip_ntoa((struct ipv4_addr *)&ip_addr, buf);
4900 bytes_written += snprintf(command+bytes_written, total_len,
4901 "ipaddr %s ", buf);
4902 }
4903
4904 ret = wl_ext_iovar_getint(dev, "dhcpc_ip_mask", &ip_addr);
4905 if (!ret) {
4906 bcm_ip_ntoa((struct ipv4_addr *)&ip_addr, buf);
4907 bytes_written += snprintf(command+bytes_written, total_len,
4908 "mask %s ", buf);
4909 }
4910
4911 ret = wl_ext_iovar_getint(dev, "dhcpc_ip_gateway", &ip_addr);
4912 if (!ret) {
4913 bcm_ip_ntoa((struct ipv4_addr *)&ip_addr, buf);
4914 bytes_written += snprintf(command+bytes_written, total_len,
4915 "gw %s ", buf);
4916 }
4917
4918 ret = wl_ext_iovar_getint(dev, "dhcpc_ip_dnsserv", &ip_addr);
4919 if (!ret) {
4920 bcm_ip_ntoa((struct ipv4_addr *)&ip_addr, buf);
4921 bytes_written += snprintf(command+bytes_written, total_len,
4922 "dnsserv %s ", buf);
4923 }
4924
4925 if (!bytes_written)
4926 bytes_written = -1;
4927
4928 AEXT_TRACE(dev->name, "command result is %s\n", command);
4929 }
4930
4931 return bytes_written;
4932 }
4933
4934 int
4935 wl_ext_dhcpc_param(struct net_device *dev, char *data, char *command,
4936 int total_len)
4937 {
4938 int ret = -1, bytes_written = 0;
4939 char ip_addr_str[20]="", ip_serv_str[20]="";
4940 struct dhcpc_parameter dhcpc_param;
4941 uint32 ip_addr, ip_serv, lease_time;
4942 char iovar_buf[WLC_IOCTL_SMLEN]="\0";
4943
4944 if (data) {
4945 AEXT_TRACE(dev->name, "cmd %s", command);
4946 sscanf(data, "%s %s %d", ip_addr_str, ip_serv_str, &lease_time);
4947 AEXT_TRACE(dev->name, "ip_addr = %s, ip_serv = %s, lease_time = %d",
4948 ip_addr_str, ip_serv_str, lease_time);
4949
4950 memset(&dhcpc_param, 0, sizeof(struct dhcpc_parameter));
4951 if (!bcm_atoipv4(ip_addr_str, (struct ipv4_addr *)&ip_addr)) {
4952 AEXT_ERROR(dev->name, "wrong ip_addr_str %s\n", ip_addr_str);
4953 ret = -1;
4954 goto exit;
4955 }
4956 dhcpc_param.ip_addr = ip_addr;
4957
4958 if (!bcm_atoipv4(ip_addr_str, (struct ipv4_addr *)&ip_serv)) {
4959 AEXT_ERROR(dev->name, "wrong ip_addr_str %s\n", ip_addr_str);
4960 ret = -1;
4961 goto exit;
4962 }
4963 dhcpc_param.ip_serv = ip_serv;
4964 dhcpc_param.lease_time = lease_time;
4965 ret = wl_ext_iovar_setbuf(dev, "dhcpc_param", &dhcpc_param,
4966 sizeof(struct dhcpc_parameter), iovar_buf, sizeof(iovar_buf), NULL);
4967 } else {
4968 ret = wl_ext_iovar_getbuf(dev, "dhcpc_param", &dhcpc_param,
4969 sizeof(struct dhcpc_parameter), iovar_buf, WLC_IOCTL_SMLEN, NULL);
4970 if (!ret) {
4971 bcm_ip_ntoa((struct ipv4_addr *)&dhcpc_param.ip_addr, ip_addr_str);
4972 bytes_written += snprintf(command + bytes_written, total_len,
4973 "ip_addr %s\n", ip_addr_str);
4974 bcm_ip_ntoa((struct ipv4_addr *)&dhcpc_param.ip_serv, ip_serv_str);
4975 bytes_written += snprintf(command + bytes_written, total_len,
4976 "ip_serv %s\n", ip_serv_str);
4977 bytes_written += snprintf(command + bytes_written, total_len,
4978 "lease_time %d\n", dhcpc_param.lease_time);
4979 AEXT_TRACE(dev->name, "command result is %s\n", command);
4980 ret = bytes_written;
4981 }
4982 }
4983
4984 exit:
4985 return ret;
4986 }
4987 #endif /* IDHCP */
4988
4989 int
4990 wl_ext_mkeep_alive(struct net_device *dev, char *data, char *command,
4991 int total_len)
4992 {
4993 struct dhd_pub *dhd = dhd_get_pub(dev);
4994 wl_mkeep_alive_pkt_t *mkeep_alive_pktp;
4995 int ret = -1, i, ifidx, id, period=-1;
4996 char *packet = NULL, *buf = NULL;
4997 int bytes_written = 0;
4998
4999 if (data) {
5000 buf = kmalloc(total_len, GFP_KERNEL);
5001 if (buf == NULL) {
5002 AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
5003 goto exit;
5004 }
5005 packet = kmalloc(WLC_IOCTL_SMLEN, GFP_KERNEL);
5006 if (packet == NULL) {
5007 AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
5008 goto exit;
5009 }
5010 AEXT_TRACE(dev->name, "cmd %s", command);
5011 sscanf(data, "%d %d %s", &id, &period, packet);
5012 AEXT_TRACE(dev->name, "id=%d, period=%d, packet=%s", id, period, packet);
5013 if (period >= 0) {
5014 ifidx = dhd_net2idx(dhd->info, dev);
5015 ret = dhd_conf_mkeep_alive(dhd, ifidx, id, period, packet, FALSE);
5016 } else {
5017 if (id < 0)
5018 id = 0;
5019 ret = wl_ext_iovar_getbuf(dev, "mkeep_alive", &id, sizeof(id), buf,
5020 total_len, NULL);
5021 if (!ret) {
5022 mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *) buf;
5023 bytes_written += snprintf(command+bytes_written, total_len,
5024 "Id :%d\n"
5025 "Period (msec) :%d\n"
5026 "Length :%d\n"
5027 "Packet :0x",
5028 mkeep_alive_pktp->keep_alive_id,
5029 dtoh32(mkeep_alive_pktp->period_msec),
5030 dtoh16(mkeep_alive_pktp->len_bytes));
5031 for (i=0; i<mkeep_alive_pktp->len_bytes; i++) {
5032 bytes_written += snprintf(command+bytes_written, total_len,
5033 "%02x", mkeep_alive_pktp->data[i]);
5034 }
5035 AEXT_TRACE(dev->name, "command result is %s\n", command);
5036 ret = bytes_written;
5037 }
5038 }
5039 }
5040
5041 exit:
5042 if (buf)
5043 kfree(buf);
5044 if (packet)
5045 kfree(packet);
5046 return ret;
5047 }
5048
5049 #ifdef WL_EXT_TCPKA
5050 static int
5051 wl_ext_tcpka_conn_add(struct net_device *dev, char *data, char *command,
5052 int total_len)
5053 {
5054 int ret = 0;
5055 s8 iovar_buf[WLC_IOCTL_SMLEN];
5056 tcpka_conn_t *tcpka = NULL;
5057 uint32 sess_id = 0, ipid = 0, srcport = 0, dstport = 0, seq = 0, ack = 0,
5058 tcpwin = 0, tsval = 0, tsecr = 0, len = 0, ka_payload_len = 0;
5059 char dst_mac[ETHER_ADDR_STR_LEN], src_ip[IPV4_ADDR_STR_LEN],
5060 dst_ip[IPV4_ADDR_STR_LEN], ka_payload[32];
5061
5062 if (data) {
5063 memset(dst_mac, 0, sizeof(dst_mac));
5064 memset(src_ip, 0, sizeof(src_ip));
5065 memset(dst_ip, 0, sizeof(dst_ip));
5066 memset(ka_payload, 0, sizeof(ka_payload));
5067 sscanf(data, "%d %s %s %s %d %d %d %u %u %d %u %u %u %32s",
5068 &sess_id, dst_mac, src_ip, dst_ip, &ipid, &srcport, &dstport, &seq,
5069 &ack, &tcpwin, &tsval, &tsecr, &len, ka_payload);
5070
5071 ka_payload_len = strlen(ka_payload) / 2;
5072 tcpka = kmalloc(sizeof(struct tcpka_conn) + ka_payload_len, GFP_KERNEL);
5073 if (tcpka == NULL) {
5074 AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n",
5075 sizeof(struct tcpka_conn) + ka_payload_len);
5076 goto exit;
5077 }
5078 memset(tcpka, 0, sizeof(struct tcpka_conn) + ka_payload_len);
5079
5080 tcpka->sess_id = sess_id;
5081 if (!(ret = bcm_ether_atoe(dst_mac, &tcpka->dst_mac))) {
5082 AEXT_ERROR(dev->name, "mac parsing err addr=%s\n", dst_mac);
5083 goto exit;
5084 }
5085 if (!bcm_atoipv4(src_ip, &tcpka->src_ip)) {
5086 AEXT_ERROR(dev->name, "src_ip parsing err ip=%s\n", src_ip);
5087 goto exit;
5088 }
5089 if (!bcm_atoipv4(dst_ip, &tcpka->dst_ip)) {
5090 AEXT_ERROR(dev->name, "dst_ip parsing err ip=%s\n", dst_ip);
5091 goto exit;
5092 }
5093 tcpka->ipid = ipid;
5094 tcpka->srcport = srcport;
5095 tcpka->dstport = dstport;
5096 tcpka->seq = seq;
5097 tcpka->ack = ack;
5098 tcpka->tcpwin = tcpwin;
5099 tcpka->tsval = tsval;
5100 tcpka->tsecr = tsecr;
5101 tcpka->len = len;
5102 ka_payload_len = wl_pattern_atoh(ka_payload, (char *)tcpka->ka_payload);
5103 if (ka_payload_len == -1) {
5104 AEXT_ERROR(dev->name,"rejecting ka_payload=%s\n", ka_payload);
5105 goto exit;
5106 }
5107 tcpka->ka_payload_len = ka_payload_len;
5108
5109 AEXT_INFO(dev->name,
5110 "tcpka_conn_add %d %pM %pM %pM %d %d %d %u %u %d %u %u %u %u \"%s\"\n",
5111 tcpka->sess_id, &tcpka->dst_mac, &tcpka->src_ip, &tcpka->dst_ip,
5112 tcpka->ipid, tcpka->srcport, tcpka->dstport, tcpka->seq,
5113 tcpka->ack, tcpka->tcpwin, tcpka->tsval, tcpka->tsecr,
5114 tcpka->len, tcpka->ka_payload_len, tcpka->ka_payload);
5115
5116 ret = wl_ext_iovar_setbuf(dev, "tcpka_conn_add", (char *)tcpka,
5117 (sizeof(tcpka_conn_t) + tcpka->ka_payload_len - 1),
5118 iovar_buf, sizeof(iovar_buf), NULL);
5119 }
5120
5121 exit:
5122 if (tcpka)
5123 kfree(tcpka);
5124 return ret;
5125 }
5126
5127 static int
5128 wl_ext_tcpka_conn_enable(struct net_device *dev, char *data, char *command,
5129 int total_len)
5130 {
5131 s8 iovar_buf[WLC_IOCTL_SMLEN];
5132 tcpka_conn_sess_t tcpka_conn;
5133 int ret = 0;
5134 uint32 sess_id = 0, flag, interval = 0, retry_interval = 0, retry_count = 0;
5135
5136 if (data) {
5137 sscanf(data, "%d %d %d %d %d",
5138 &sess_id, &flag, &interval, &retry_interval, &retry_count);
5139 tcpka_conn.sess_id = sess_id;
5140 tcpka_conn.flag = flag;
5141 if (tcpka_conn.flag) {
5142 tcpka_conn.tcpka_timers.interval = interval;
5143 tcpka_conn.tcpka_timers.retry_interval = retry_interval;
5144 tcpka_conn.tcpka_timers.retry_count = retry_count;
5145 } else {
5146 tcpka_conn.tcpka_timers.interval = 0;
5147 tcpka_conn.tcpka_timers.retry_interval = 0;
5148 tcpka_conn.tcpka_timers.retry_count = 0;
5149 }
5150
5151 AEXT_INFO(dev->name, "tcpka_conn_enable %d %d %d %d %d\n",
5152 tcpka_conn.sess_id, tcpka_conn.flag,
5153 tcpka_conn.tcpka_timers.interval,
5154 tcpka_conn.tcpka_timers.retry_interval,
5155 tcpka_conn.tcpka_timers.retry_count);
5156
5157 ret = wl_ext_iovar_setbuf(dev, "tcpka_conn_enable", (char *)&tcpka_conn,
5158 sizeof(tcpka_conn_sess_t), iovar_buf, sizeof(iovar_buf), NULL);
5159 }
5160
5161 return ret;
5162 }
5163
5164 static int
5165 wl_ext_tcpka_conn_info(struct net_device *dev, char *data, char *command,
5166 int total_len)
5167 {
5168 s8 iovar_buf[WLC_IOCTL_SMLEN];
5169 tcpka_conn_sess_info_t *info = NULL;
5170 uint32 sess_id = 0;
5171 int ret = 0, bytes_written = 0;
5172
5173 if (data) {
5174 sscanf(data, "%d", &sess_id);
5175 AEXT_INFO(dev->name, "tcpka_conn_sess_info %d\n", sess_id);
5176 ret = wl_ext_iovar_getbuf(dev, "tcpka_conn_sess_info", (char *)&sess_id,
5177 sizeof(uint32), iovar_buf, sizeof(iovar_buf), NULL);
5178 if (!ret) {
5179 info = (tcpka_conn_sess_info_t *) iovar_buf;
5180 bytes_written += snprintf(command+bytes_written, total_len,
5181 "id :%d\n"
5182 "ipid :%d\n"
5183 "seq :%u\n"
5184 "ack :%u",
5185 sess_id, info->ipid, info->seq, info->ack);
5186 AEXT_INFO(dev->name, "%s\n", command);
5187 ret = bytes_written;
5188 }
5189 }
5190
5191 return ret;
5192 }
5193 #endif /* WL_EXT_TCPKA */
5194
5195 static int
5196 wl_ext_rsdb_mode(struct net_device *dev, char *data, char *command,
5197 int total_len)
5198 {
5199 s8 iovar_buf[WLC_IOCTL_SMLEN];
5200 wl_config_t rsdb_mode_cfg = {1, 0}, *rsdb_p;
5201 int ret = 0;
5202
5203 if (data) {
5204 rsdb_mode_cfg.config = (int)simple_strtol(data, NULL, 0);
5205 ret = wl_ext_iovar_setbuf(dev, "rsdb_mode", (char *)&rsdb_mode_cfg,
5206 sizeof(rsdb_mode_cfg), iovar_buf, WLC_IOCTL_SMLEN, NULL);
5207 AEXT_INFO(dev->name, "rsdb_mode %d\n", rsdb_mode_cfg.config);
5208 } else {
5209 ret = wl_ext_iovar_getbuf(dev, "rsdb_mode", NULL, 0,
5210 iovar_buf, WLC_IOCTL_SMLEN, NULL);
5211 if (!ret) {
5212 rsdb_p = (wl_config_t *) iovar_buf;
5213 ret = snprintf(command, total_len, "%d", rsdb_p->config);
5214 AEXT_TRACE(dev->name, "command result is %s\n", command);
5215 }
5216 }
5217
5218 return ret;
5219 }
5220
5221 static int
5222 wl_ext_recal(struct net_device *dev, char *data, char *command,
5223 int total_len)
5224 {
5225 int ret = 0, i, nchan, nssid = 0;
5226 int params_size = WL_SCAN_PARAMS_FIXED_SIZE + WL_NUMCHANNELS * sizeof(uint16);
5227 wl_scan_params_t *params = NULL;
5228 int ioctl_ver;
5229 char *p;
5230
5231 AEXT_TRACE(dev->name, "Enter\n");
5232
5233 if (data) {
5234 params_size += WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t);
5235 params = (wl_scan_params_t *) kzalloc(params_size, GFP_KERNEL);
5236 if (params == NULL) {
5237 ret = -ENOMEM;
5238 goto exit;
5239 }
5240 memset(params, 0, params_size);
5241
5242 wl_ext_get_ioctl_ver(dev, &ioctl_ver);
5243
5244 memcpy(&params->bssid, &ether_bcast, ETHER_ADDR_LEN);
5245 params->bss_type = DOT11_BSSTYPE_ANY;
5246 params->scan_type = 0;
5247 params->nprobes = -1;
5248 params->active_time = -1;
5249 params->passive_time = -1;
5250 params->home_time = -1;
5251 params->channel_num = 0;
5252
5253 params->scan_type |= WL_SCANFLAGS_PASSIVE;
5254 nchan = 2;
5255 params->channel_list[0] = wf_channel2chspec(1, WL_CHANSPEC_BW_20);
5256 params->channel_list[1] = wf_channel2chspec(2, WL_CHANSPEC_BW_20);
5257
5258 params->nprobes = htod32(params->nprobes);
5259 params->active_time = htod32(params->active_time);
5260 params->passive_time = htod32(params->passive_time);
5261 params->home_time = htod32(params->home_time);
5262
5263 for (i = 0; i < nchan; i++) {
5264 wl_ext_chspec_host_to_driver(ioctl_ver, params->channel_list[i]);
5265 }
5266
5267 p = (char*)params->channel_list + nchan * sizeof(uint16);
5268
5269 params->channel_num = htod32((nssid << WL_SCAN_PARAMS_NSSID_SHIFT) |
5270 (nchan & WL_SCAN_PARAMS_COUNT_MASK));
5271 params_size = p - (char*)params + nssid * sizeof(wlc_ssid_t);
5272
5273 AEXT_INFO(dev->name, "recal\n");
5274 ret = wl_ext_ioctl(dev, WLC_SCAN, params, params_size, 1);
5275 }
5276
5277 exit:
5278 if (params)
5279 kfree(params);
5280 return ret;
5281 }
5282
5283 static s32
5284 wl_ext_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add)
5285 {
5286 s8 iovbuf[WL_EVENTING_MASK_LEN + 12];
5287 s8 eventmask[WL_EVENTING_MASK_LEN];
5288 s32 err = 0;
5289
5290 if (!ndev)
5291 return -ENODEV;
5292
5293 /* Setup event_msgs */
5294 err = wldev_iovar_getbuf(ndev, "event_msgs", NULL, 0, iovbuf, sizeof(iovbuf), NULL);
5295 if (unlikely(err)) {
5296 AEXT_ERROR(ndev->name, "Get event_msgs error (%d)\n", err);
5297 goto eventmsg_out;
5298 }
5299 memcpy(eventmask, iovbuf, WL_EVENTING_MASK_LEN);
5300 if (add) {
5301 setbit(eventmask, event);
5302 } else {
5303 clrbit(eventmask, event);
5304 }
5305 err = wldev_iovar_setbuf(ndev, "event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf,
5306 sizeof(iovbuf), NULL);
5307 if (unlikely(err)) {
5308 AEXT_ERROR(ndev->name, "Set event_msgs error (%d)\n", err);
5309 goto eventmsg_out;
5310 }
5311
5312 eventmsg_out:
5313 return err;
5314 }
5315
5316 static int
5317 wl_ext_event_msg(struct net_device *dev, char *data,
5318 char *command, int total_len)
5319 {
5320 s8 iovbuf[WL_EVENTING_MASK_LEN + 12];
5321 s8 eventmask[WL_EVENTING_MASK_LEN];
5322 int i, bytes_written = 0, add = -1;
5323 uint event;
5324 char *vbuf;
5325 bool skipzeros;
5326
5327 /* dhd_priv wl event_msg [offset] [1/0, 1 for add, 0 for remove] */
5328 /* dhd_priv wl event_msg 40 1 */
5329 if (data) {
5330 AEXT_TRACE(dev->name, "data = %s\n", data);
5331 sscanf(data, "%d %d", &event, &add);
5332 /* Setup event_msgs */
5333 bytes_written = wldev_iovar_getbuf(dev, "event_msgs", NULL, 0, iovbuf,
5334 sizeof(iovbuf), NULL);
5335 if (unlikely(bytes_written)) {
5336 AEXT_ERROR(dev->name, "Get event_msgs error (%d)\n", bytes_written);
5337 goto eventmsg_out;
5338 }
5339 memcpy(eventmask, iovbuf, WL_EVENTING_MASK_LEN);
5340 if (add == -1) {
5341 if (isset(eventmask, event))
5342 bytes_written += snprintf(command+bytes_written, total_len, "1");
5343 else
5344 bytes_written += snprintf(command+bytes_written, total_len, "0");
5345 AEXT_INFO(dev->name, "%s\n", command);
5346 goto eventmsg_out;
5347 }
5348 bytes_written = wl_ext_add_remove_eventmsg(dev, event, add);
5349 }
5350 else {
5351 /* Setup event_msgs */
5352 bytes_written = wldev_iovar_getbuf(dev, "event_msgs", NULL, 0, iovbuf,
5353 sizeof(iovbuf), NULL);
5354 if (bytes_written) {
5355 AEXT_ERROR(dev->name, "Get event_msgs error (%d)\n", bytes_written);
5356 goto eventmsg_out;
5357 }
5358 vbuf = (char *)iovbuf;
5359 bytes_written += snprintf(command+bytes_written, total_len, "0x");
5360 for (i = (sizeof(eventmask) - 1); i >= 0; i--) {
5361 if (vbuf[i] || (i == 0))
5362 skipzeros = FALSE;
5363 if (skipzeros)
5364 continue;
5365 bytes_written += snprintf(command+bytes_written, total_len,
5366 "%02x", vbuf[i] & 0xff);
5367 }
5368 AEXT_INFO(dev->name, "%s\n", command);
5369 }
5370
5371 eventmsg_out:
5372 return bytes_written;
5373 }
5374
5375 #ifdef PKT_FILTER_SUPPORT
5376 extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg);
5377 extern void dhd_pktfilter_offload_delete(dhd_pub_t *dhd, int id);
5378 extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
5379 static int
5380 wl_ext_pkt_filter_add(struct net_device *dev, char *data, char *command,
5381 int total_len)
5382 {
5383 struct dhd_pub *dhd = dhd_get_pub(dev);
5384 int i, filter_id, new_id = 0, cnt;
5385 conf_pkt_filter_add_t *filter_add = &dhd->conf->pkt_filter_add;
5386 char **pktfilter = dhd->pktfilter;
5387 int err = 0;
5388
5389 if (data) {
5390 AEXT_TRACE(dev->name, "data = %s\n", data);
5391
5392 new_id = simple_strtol(data, NULL, 10);
5393 if (new_id <= 0) {
5394 AEXT_ERROR(dev->name, "wrong id %d\n", new_id);
5395 return -1;
5396 }
5397
5398 cnt = dhd->pktfilter_count;
5399 for (i=0; i<cnt; i++) {
5400 if (!pktfilter[i])
5401 continue;
5402 filter_id = simple_strtol(pktfilter[i], NULL, 10);
5403 if (new_id == filter_id) {
5404 AEXT_ERROR(dev->name, "filter id %d already in list\n", filter_id);
5405 return -1;
5406 }
5407 }
5408
5409 cnt = filter_add->count;
5410 if (cnt >= DHD_CONF_FILTER_MAX) {
5411 AEXT_ERROR(dev->name, "not enough filter\n");
5412 return -1;
5413 }
5414 for (i=0; i<cnt; i++) {
5415 filter_id = simple_strtol(filter_add->filter[i], NULL, 10);
5416 if (new_id == filter_id) {
5417 AEXT_ERROR(dev->name, "filter id %d already in list\n", filter_id);
5418 return -1;
5419 }
5420 }
5421
5422 strcpy(&filter_add->filter[cnt][0], data);
5423 dhd->pktfilter[dhd->pktfilter_count] = filter_add->filter[cnt];
5424 filter_add->count++;
5425 dhd->pktfilter_count++;
5426
5427 dhd_pktfilter_offload_set(dhd, data);
5428 AEXT_INFO(dev->name, "filter id %d added\n", new_id);
5429 }
5430
5431 return err;
5432 }
5433
5434 static int
5435 wl_ext_pkt_filter_delete(struct net_device *dev, char *data, char *command,
5436 int total_len)
5437 {
5438 struct dhd_pub *dhd = dhd_get_pub(dev);
5439 int i, j, filter_id, cnt;
5440 char **pktfilter = dhd->pktfilter;
5441 conf_pkt_filter_add_t *filter_add = &dhd->conf->pkt_filter_add;
5442 bool in_filter = FALSE;
5443 int id, err = 0;
5444
5445 if (data) {
5446 AEXT_TRACE(dev->name, "data = %s\n", data);
5447 id = (int)simple_strtol(data, NULL, 0);
5448
5449 cnt = filter_add->count;
5450 for (i=0; i<cnt; i++) {
5451 filter_id = simple_strtol(filter_add->filter[i], NULL, 10);
5452 if (id == filter_id) {
5453 in_filter = TRUE;
5454 memset(filter_add->filter[i], 0, PKT_FILTER_LEN);
5455 for (j=i; j<(cnt-1); j++) {
5456 strcpy(filter_add->filter[j], filter_add->filter[j+1]);
5457 memset(filter_add->filter[j+1], 0, PKT_FILTER_LEN);
5458 }
5459 cnt--;
5460 filter_add->count--;
5461 dhd->pktfilter_count--;
5462 }
5463 }
5464
5465 cnt = dhd->pktfilter_count;
5466 for (i=0; i<cnt; i++) {
5467 if (!pktfilter[i])
5468 continue;
5469 filter_id = simple_strtol(pktfilter[i], NULL, 10);
5470 if (id == filter_id) {
5471 in_filter = TRUE;
5472 memset(pktfilter[i], 0, strlen(pktfilter[i]));
5473 }
5474 }
5475
5476 if (in_filter) {
5477 dhd_pktfilter_offload_delete(dhd, id);
5478 AEXT_INFO(dev->name, "filter id %d deleted\n", id);
5479 } else {
5480 AEXT_ERROR(dev->name, "filter id %d not in list\n", id);
5481 err = -1;
5482 }
5483 }
5484
5485 return err;
5486 }
5487
5488 static int
5489 wl_ext_pkt_filter_enable(struct net_device *dev, char *data, char *command,
5490 int total_len)
5491 {
5492 struct dhd_pub *dhd = dhd_get_pub(dev);
5493 int err = 0, id, enable;
5494 int i, filter_id, cnt;
5495 char **pktfilter = dhd->pktfilter;
5496 bool in_filter = FALSE;
5497
5498 /* dhd_priv wl pkt_filter_enable [id] [1/0] */
5499 /* dhd_priv wl pkt_filter_enable 141 1 */
5500 if (data) {
5501 sscanf(data, "%d %d", &id, &enable);
5502
5503 cnt = dhd->pktfilter_count;
5504 for (i=0; i<cnt; i++) {
5505 if (!pktfilter[i])
5506 continue;
5507 filter_id = simple_strtol(pktfilter[i], NULL, 10);
5508 if (id == filter_id) {
5509 in_filter = TRUE;
5510 break;
5511 }
5512 }
5513
5514 if (in_filter) {
5515 dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
5516 enable, dhd_master_mode);
5517 AEXT_INFO(dev->name, "filter id %d %s\n", id, enable?"enabled":"disabled");
5518 } else {
5519 AEXT_ERROR(dev->name, "filter id %d not in list\n", id);
5520 err = -1;
5521 }
5522 }
5523
5524 return err;
5525 }
5526 #endif /* PKT_FILTER_SUPPORT */
5527
5528 #ifdef SENDPROB
5529 static int
5530 wl_ext_send_probreq(struct net_device *dev, char *data, char *command,
5531 int total_len)
5532 {
5533 int err = 0;
5534 char addr_str[16], addr[6];
5535 char iovar_buf[WLC_IOCTL_SMLEN]="\0";
5536 char ie_data[WLC_IOCTL_SMLEN] = "\0";
5537 wl_probe_params_t params;
5538
5539 /* dhd_priv wl send_probreq [dest. addr] [OUI+VAL] */
5540 /* dhd_priv wl send_probreq 0x00904c010203 0x00904c01020304050607 */
5541 if (data) {
5542 AEXT_TRACE(dev->name, "data = %s\n", data);
5543 sscanf(data, "%s %s", addr_str, ie_data);
5544 AEXT_TRACE(dev->name, "addr=%s, ie=%s\n", addr_str, ie_data);
5545
5546 if (strlen(addr_str) != 14) {
5547 AEXT_ERROR(dev->name, "wrong addr %s\n", addr_str);
5548 goto exit;
5549 }
5550 wl_pattern_atoh(addr_str, (char *) addr);
5551 memset(&params, 0, sizeof(params));
5552 memcpy(&params.bssid, addr, ETHER_ADDR_LEN);
5553 memcpy(&params.mac, addr, ETHER_ADDR_LEN);
5554
5555 err = wl_ext_add_del_ie(dev, VNDR_IE_PRBREQ_FLAG, ie_data, "add");
5556 if (err)
5557 goto exit;
5558 err = wl_ext_iovar_setbuf(dev, "sendprb", (char *)&params, sizeof(params),
5559 iovar_buf, sizeof(iovar_buf), NULL);
5560 OSL_SLEEP(100);
5561 wl_ext_add_del_ie(dev, VNDR_IE_PRBREQ_FLAG, ie_data, "del");
5562 }
5563
5564 exit:
5565 return err;
5566 }
5567
5568 static int
5569 wl_ext_send_probresp(struct net_device *dev, char *data, char *command,
5570 int total_len)
5571 {
5572 int err = 0;
5573 char addr_str[16], addr[6];
5574 char iovar_buf[WLC_IOCTL_SMLEN]="\0";
5575 char ie_data[WLC_IOCTL_SMLEN] = "\0";
5576
5577 /* dhd_priv wl send_probresp [dest. addr] [OUI+VAL] */
5578 /* dhd_priv wl send_probresp 0x00904c010203 0x00904c01020304050607 */
5579 if (data) {
5580 AEXT_TRACE(dev->name, "data = %s\n", data);
5581 sscanf(data, "%s %s", addr_str, ie_data);
5582 AEXT_TRACE(dev->name, "addr=%s, ie=%s\n", addr_str, ie_data);
5583
5584 if (strlen(addr_str) != 14) {
5585 AEXT_ERROR(dev->name, "wrong addr %s\n", addr_str);
5586 goto exit;
5587 }
5588 wl_pattern_atoh(addr_str, (char *) addr);
5589
5590 err = wl_ext_add_del_ie(dev, VNDR_IE_PRBRSP_FLAG, ie_data, "add");
5591 if (err)
5592 goto exit;
5593 err = wl_ext_iovar_setbuf(dev, "send_probresp", addr, sizeof(addr),
5594 iovar_buf, sizeof(iovar_buf), NULL);
5595 OSL_SLEEP(100);
5596 wl_ext_add_del_ie(dev, VNDR_IE_PRBRSP_FLAG, ie_data, "del");
5597 }
5598
5599 exit:
5600 return err;
5601 }
5602
5603 static int
5604 wl_ext_recv_probreq(struct net_device *dev, char *data, char *command,
5605 int total_len)
5606 {
5607 int err = 0, enable = 0;
5608 char cmd[32];
5609 struct dhd_pub *dhd = dhd_get_pub(dev);
5610
5611 /* enable:
5612 1. dhd_priv wl 86 0
5613 2. dhd_priv wl event_msg 44 1
5614 disable:
5615 1. dhd_priv wl 86 2;
5616 2. dhd_priv wl event_msg 44 0
5617 */
5618 if (data) {
5619 AEXT_TRACE(dev->name, "data = %s\n", data);
5620 sscanf(data, "%d", &enable);
5621 if (enable) {
5622 strcpy(cmd, "wl 86 0");
5623 err = wl_ext_wl_iovar(dev, cmd, total_len);
5624 if (err)
5625 goto exit;
5626 strcpy(cmd, "wl event_msg 44 1");
5627 err = wl_ext_wl_iovar(dev, cmd, total_len);
5628 if (err)
5629 goto exit;
5630 dhd->recv_probereq = TRUE;
5631 } else {
5632 if (dhd->conf->pm)
5633 strcpy(cmd, "wl 86 2"); {
5634 wl_ext_wl_iovar(dev, cmd, total_len);
5635 }
5636 strcpy(cmd, "wl event_msg 44 0");
5637 wl_ext_wl_iovar(dev, cmd, total_len);
5638 dhd->recv_probereq = FALSE;
5639 }
5640 }
5641
5642 exit:
5643 return err;
5644 }
5645
5646 static int
5647 wl_ext_recv_probresp(struct net_device *dev, char *data, char *command,
5648 int total_len)
5649 {
5650 int err = 0, enable = 0;
5651 char cmd[32];
5652
5653 /* enable:
5654 1. dhd_priv wl pkt_filter_add 150 0 0 0 0xFF 0x50
5655 2. dhd_priv wl pkt_filter_enable 150 1
5656 3. dhd_priv wl mpc 0
5657 4. dhd_priv wl 108 1
5658 disable:
5659 1. dhd_priv wl 108 0
5660 2. dhd_priv wl mpc 1
5661 3. dhd_priv wl pkt_filter_disable 150 0
5662 4. dhd_priv pkt_filter_delete 150
5663 */
5664 if (data) {
5665 AEXT_TRACE(dev->name, "data = %s\n", data);
5666 sscanf(data, "%d", &enable);
5667 if (enable) {
5668 strcpy(cmd, "wl pkt_filter_add 150 0 0 0 0xFF 0x50");
5669 err = wl_ext_wl_iovar(dev, cmd, total_len);
5670 if (err)
5671 goto exit;
5672 strcpy(cmd, "wl pkt_filter_enable 150 1");
5673 err = wl_ext_wl_iovar(dev, cmd, total_len);
5674 if (err)
5675 goto exit;
5676 strcpy(cmd, "wl mpc 0");
5677 err = wl_ext_wl_iovar(dev, cmd, total_len);
5678 if (err)
5679 goto exit;
5680 strcpy(cmd, "wl 108 1");
5681 err= wl_ext_wl_iovar(dev, cmd, total_len);
5682 } else {
5683 strcpy(cmd, "wl 108 0");
5684 wl_ext_wl_iovar(dev, cmd, total_len);
5685 strcpy(cmd, "wl mpc 1");
5686 wl_ext_wl_iovar(dev, cmd, total_len);
5687 strcpy(cmd, "wl pkt_filter_enable 150 0");
5688 wl_ext_wl_iovar(dev, cmd, total_len);
5689 strcpy(cmd, "wl pkt_filter_delete 150");
5690 wl_ext_wl_iovar(dev, cmd, total_len);
5691 }
5692 }
5693
5694 exit:
5695 return err;
5696 }
5697 #endif /* SENDPROB */
5698
5699 static int
5700 wl_ext_gtk_key_info(struct net_device *dev, char *data, char *command, int total_len)
5701 {
5702 int err = 0;
5703 char iovar_buf[WLC_IOCTL_SMLEN]="\0";
5704 gtk_keyinfo_t keyinfo;
5705 bcol_gtk_para_t bcol_keyinfo;
5706
5707 /* wl gtk_key_info [kck kek replay_ctr] */
5708 /* wl gtk_key_info 001122..FF001122..FF00000000000001 */
5709 if (data) {
5710 memset(&keyinfo, 0, sizeof(keyinfo));
5711 memcpy(&keyinfo, data, RSN_KCK_LENGTH+RSN_KEK_LENGTH+RSN_REPLAY_LEN);
5712 if (android_msg_level & ANDROID_INFO_LEVEL) {
5713 prhex("kck", (uchar *)keyinfo.KCK, RSN_KCK_LENGTH);
5714 prhex("kek", (uchar *)keyinfo.KEK, RSN_KEK_LENGTH);
5715 prhex("replay_ctr", (uchar *)keyinfo.ReplayCounter, RSN_REPLAY_LEN);
5716 }
5717
5718 memset(&bcol_keyinfo, 0, sizeof(bcol_keyinfo));
5719 bcol_keyinfo.enable = 1;
5720 bcol_keyinfo.ptk_len = 64;
5721 memcpy(&bcol_keyinfo.ptk, data, RSN_KCK_LENGTH+RSN_KEK_LENGTH);
5722 err = wl_ext_iovar_setbuf(dev, "bcol_gtk_rekey_ptk", &bcol_keyinfo,
5723 sizeof(bcol_keyinfo), iovar_buf, sizeof(iovar_buf), NULL);
5724 if (!err) {
5725 goto exit;
5726 }
5727
5728 err = wl_ext_iovar_setbuf(dev, "gtk_key_info", &keyinfo, sizeof(keyinfo),
5729 iovar_buf, sizeof(iovar_buf), NULL);
5730 if (err) {
5731 AEXT_ERROR(dev->name, "failed to set gtk_key_info\n");
5732 goto exit;
5733 }
5734 }
5735
5736 exit:
5737 return err;
5738 }
5739
5740 #ifdef WL_EXT_WOWL
5741 static int
5742 wl_ext_wowl_pattern(struct net_device *dev, char *data, char *command,
5743 int total_len)
5744 {
5745 s8 iovar_buf[WLC_IOCTL_SMLEN];
5746 uint buf_len = 0;
5747 int offset;
5748 char mask[128]="\0", pattern[128]="\0", add[4]="\0",
5749 mask_tmp[128], *pmask_tmp;
5750 uint32 masksize, patternsize, pad_len = 0;
5751 wl_wowl_pattern2_t *wowl_pattern2 = NULL;
5752 wl_wowl_pattern_t *wowl_pattern = NULL;
5753 char *mask_and_pattern;
5754 wl_wowl_pattern_list_t *list;
5755 uint8 *ptr;
5756 int ret = 0, i, j, v;
5757
5758 if (data) {
5759 sscanf(data, "%s %d %s %s", add, &offset, mask_tmp, pattern);
5760 if (strcmp(add, "add") != 0 && strcmp(add, "clr") != 0) {
5761 AEXT_ERROR(dev->name, "first arg should be add or clr\n");
5762 goto exit;
5763 }
5764 if (!strcmp(add, "clr")) {
5765 AEXT_INFO(dev->name, "wowl_pattern clr\n");
5766 ret = wl_ext_iovar_setbuf(dev, "wowl_pattern", add,
5767 sizeof(add), iovar_buf, sizeof(iovar_buf), NULL);
5768 goto exit;
5769 }
5770 masksize = strlen(mask_tmp) -2;
5771 AEXT_TRACE(dev->name, "0 mask_tmp=%s, masksize=%d\n", mask_tmp, masksize);
5772
5773 // add pading
5774 if (masksize % 16)
5775 pad_len = (16 - masksize % 16);
5776 for (i=0; i<pad_len; i++)
5777 strcat(mask_tmp, "0");
5778 masksize += pad_len;
5779 AEXT_TRACE(dev->name, "1 mask_tmp=%s, masksize=%d\n", mask_tmp, masksize);
5780
5781 // translate 0x00 to 0, others to 1
5782 j = 0;
5783 pmask_tmp = &mask_tmp[2];
5784 for (i=0; i<masksize/2; i++) {
5785 if(strncmp(&pmask_tmp[i*2], "00", 2))
5786 pmask_tmp[j] = '1';
5787 else
5788 pmask_tmp[j] = '0';
5789 j++;
5790 }
5791 pmask_tmp[j] = '\0';
5792 masksize = masksize / 2;
5793 AEXT_TRACE(dev->name, "2 mask_tmp=%s, masksize=%d\n", mask_tmp, masksize);
5794
5795 // reorder per 8bits
5796 pmask_tmp = &mask_tmp[2];
5797 for (i=0; i<masksize/8; i++) {
5798 char c;
5799 for (j=0; j<4; j++) {
5800 c = pmask_tmp[i*8+j];
5801 pmask_tmp[i*8+j] = pmask_tmp[(i+1)*8-j-1];
5802 pmask_tmp[(i+1)*8-j-1] = c;
5803 }
5804 }
5805 AEXT_TRACE(dev->name, "3 mask_tmp=%s, masksize=%d\n", mask_tmp, masksize);
5806
5807 // translate 8bits to 1byte
5808 j = 0; v = 0;
5809 pmask_tmp = &mask_tmp[2];
5810 strcpy(mask, "0x");
5811 for (i=0; i<masksize; i++) {
5812 v = (v<<1) | (pmask_tmp[i]=='1');
5813 if (((i+1)%4) == 0) {
5814 if (v < 10)
5815 mask[j+2] = v + '0';
5816 else
5817 mask[j+2] = (v-10) + 'a';
5818 j++;
5819 v = 0;
5820 }
5821 }
5822 mask[j+2] = '\0';
5823 masksize = j/2;
5824 AEXT_TRACE(dev->name, "4 mask=%s, masksize=%d\n", mask, masksize);
5825
5826 patternsize = (strlen(pattern)-2)/2;
5827 buf_len = sizeof(wl_wowl_pattern2_t) + patternsize + masksize;
5828 wowl_pattern2 = kmalloc(buf_len, GFP_KERNEL);
5829 if (wowl_pattern2 == NULL) {
5830 AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", buf_len);
5831 goto exit;
5832 }
5833 memset(wowl_pattern2, 0, sizeof(wl_wowl_pattern2_t));
5834
5835 strncpy(wowl_pattern2->cmd, add, sizeof(add));
5836 wowl_pattern2->wowl_pattern.type = 0;
5837 wowl_pattern2->wowl_pattern.offset = offset;
5838 mask_and_pattern = (char*)wowl_pattern2 + sizeof(wl_wowl_pattern2_t);
5839
5840 wowl_pattern2->wowl_pattern.masksize = masksize;
5841 ret = wl_pattern_atoh(mask, mask_and_pattern);
5842 if (ret == -1) {
5843 AEXT_ERROR(dev->name, "rejecting mask=%s\n", mask);
5844 goto exit;
5845 }
5846
5847 mask_and_pattern += wowl_pattern2->wowl_pattern.masksize;
5848 wowl_pattern2->wowl_pattern.patternoffset = sizeof(wl_wowl_pattern_t) +
5849 wowl_pattern2->wowl_pattern.masksize;
5850
5851 wowl_pattern2->wowl_pattern.patternsize = patternsize;
5852 ret = wl_pattern_atoh(pattern, mask_and_pattern);
5853 if (ret == -1) {
5854 AEXT_ERROR(dev->name, "rejecting pattern=%s\n", pattern);
5855 goto exit;
5856 }
5857
5858 AEXT_INFO(dev->name, "%s %d %s %s\n", add, offset, mask, pattern);
5859
5860 ret = wl_ext_iovar_setbuf(dev, "wowl_pattern", (char *)wowl_pattern2,
5861 buf_len, iovar_buf, sizeof(iovar_buf), NULL);
5862 }
5863 else {
5864 ret = wl_ext_iovar_getbuf(dev, "wowl_pattern", NULL, 0,
5865 iovar_buf, sizeof(iovar_buf), NULL);
5866 if (!ret) {
5867 list = (wl_wowl_pattern_list_t *)iovar_buf;
5868 ret = snprintf(command, total_len, "#of patterns :%d\n", list->count);
5869 ptr = (uint8 *)list->pattern;
5870 for (i=0; i<list->count; i++) {
5871 uint8 *pattern;
5872 wowl_pattern = (wl_wowl_pattern_t *)ptr;
5873 ret += snprintf(command+ret, total_len,
5874 "Pattern %d:\n"
5875 "ID :0x%x\n"
5876 "Offset :%d\n"
5877 "Masksize :%d\n"
5878 "Mask :0x",
5879 i+1, (uint32)wowl_pattern->id, wowl_pattern->offset,
5880 wowl_pattern->masksize);
5881 pattern = ((uint8 *)wowl_pattern + sizeof(wl_wowl_pattern_t));
5882 for (j = 0; j < wowl_pattern->masksize; j++) {
5883 ret += snprintf(command+ret, total_len, "%02x", pattern[j]);
5884 }
5885 ret += snprintf(command+ret, total_len, "\n");
5886 ret += snprintf(command+ret, total_len,
5887 "PatternSize:%d\n"
5888 "Pattern :0x",
5889 wowl_pattern->patternsize);
5890
5891 pattern = ((uint8*)wowl_pattern + wowl_pattern->patternoffset);
5892 for (j=0; j<wowl_pattern->patternsize; j++)
5893 ret += snprintf(command+ret, total_len, "%02x", pattern[j]);
5894 ret += snprintf(command+ret, total_len, "\n");
5895 ptr += (wowl_pattern->masksize + wowl_pattern->patternsize +
5896 sizeof(wl_wowl_pattern_t));
5897 }
5898
5899 AEXT_INFO(dev->name, "%s\n", command);
5900 }
5901 }
5902
5903 exit:
5904 if (wowl_pattern2)
5905 kfree(wowl_pattern2);
5906 return ret;
5907 }
5908
5909 static int
5910 wl_ext_wowl_wakeind(struct net_device *dev, char *data, char *command,
5911 int total_len)
5912 {
5913 s8 iovar_buf[WLC_IOCTL_SMLEN];
5914 wl_wowl_wakeind_t *wake = NULL;
5915 int ret = -1;
5916 char clr[6]="\0";
5917
5918 if (data) {
5919 sscanf(data, "%s", clr);
5920 if (!strcmp(clr, "clear")) {
5921 AEXT_INFO(dev->name, "wowl_wakeind clear\n");
5922 ret = wl_ext_iovar_setbuf(dev, "wowl_wakeind", clr, sizeof(clr),
5923 iovar_buf, sizeof(iovar_buf), NULL);
5924 } else {
5925 AEXT_ERROR(dev->name, "first arg should be clear\n");
5926 }
5927 } else {
5928 ret = wl_ext_iovar_getbuf(dev, "wowl_wakeind", NULL, 0,
5929 iovar_buf, sizeof(iovar_buf), NULL);
5930 if (!ret) {
5931 wake = (wl_wowl_wakeind_t *) iovar_buf;
5932 ret = snprintf(command, total_len, "wakeind=0x%x", wake->ucode_wakeind);
5933 if (wake->ucode_wakeind & WL_WOWL_MAGIC)
5934 ret += snprintf(command+ret, total_len, " (MAGIC packet)");
5935 if (wake->ucode_wakeind & WL_WOWL_NET)
5936 ret += snprintf(command+ret, total_len, " (Netpattern)");
5937 if (wake->ucode_wakeind & WL_WOWL_DIS)
5938 ret += snprintf(command+ret, total_len, " (Disassoc/Deauth)");
5939 if (wake->ucode_wakeind & WL_WOWL_BCN)
5940 ret += snprintf(command+ret, total_len, " (Loss of beacon)");
5941 if (wake->ucode_wakeind & WL_WOWL_TCPKEEP_TIME)
5942 ret += snprintf(command+ret, total_len, " (TCPKA timeout)");
5943 if (wake->ucode_wakeind & WL_WOWL_TCPKEEP_DATA)
5944 ret += snprintf(command+ret, total_len, " (TCPKA data)");
5945 if (wake->ucode_wakeind & WL_WOWL_TCPFIN)
5946 ret += snprintf(command+ret, total_len, " (TCP FIN)");
5947 AEXT_INFO(dev->name, "%s\n", command);
5948 }
5949 }
5950
5951 return ret;
5952 }
5953 #endif /* WL_EXT_WOWL */
5954
5955 #ifdef WL_GPIO_NOTIFY
5956 typedef struct notify_payload {
5957 int index;
5958 int len;
5959 char payload[128];
5960 } notify_payload_t;
5961
5962 static int
5963 wl_ext_gpio_notify(struct net_device *dev, char *data, char *command,
5964 int total_len)
5965 {
5966 s8 iovar_buf[WLC_IOCTL_SMLEN];
5967 notify_payload_t notify, *pnotify = NULL;
5968 int i, ret = 0, bytes_written = 0;
5969 char frame_str[WLC_IOCTL_SMLEN+3];
5970
5971 if (data) {
5972 memset(&notify, 0, sizeof(notify));
5973 memset(frame_str, 0, sizeof(frame_str));
5974 sscanf(data, "%d %s", &notify.index, frame_str);
5975
5976 if (notify.index < 0)
5977 notify.index = 0;
5978
5979 if (strlen(frame_str)) {
5980 notify.len = wl_pattern_atoh(frame_str, notify.payload);
5981 if (notify.len == -1) {
5982 AEXT_ERROR(dev->name, "rejecting pattern=%s\n", frame_str);
5983 goto exit;
5984 }
5985 AEXT_INFO(dev->name, "index=%d, len=%d\n", notify.index, notify.len);
5986 if (android_msg_level & ANDROID_INFO_LEVEL)
5987 prhex("payload", (uchar *)notify.payload, notify.len);
5988 ret = wl_ext_iovar_setbuf(dev, "bcol_gpio_noti", (char *)&notify,
5989 sizeof(notify), iovar_buf, WLC_IOCTL_SMLEN, NULL);
5990 } else {
5991 AEXT_INFO(dev->name, "index=%d\n", notify.index);
5992 ret = wl_ext_iovar_getbuf(dev, "bcol_gpio_noti", &notify.index,
5993 sizeof(notify.index), iovar_buf, sizeof(iovar_buf), NULL);
5994 if (!ret) {
5995 pnotify = (notify_payload_t *)iovar_buf;
5996 bytes_written += snprintf(command+bytes_written, total_len,
5997 "Id :%d\n"
5998 "Packet :0x",
5999 pnotify->index);
6000 for (i=0; i<pnotify->len; i++) {
6001 bytes_written += snprintf(command+bytes_written, total_len,
6002 "%02x", pnotify->payload[i]);
6003 }
6004 AEXT_TRACE(dev->name, "command result is\n%s\n", command);
6005 ret = bytes_written;
6006 }
6007 }
6008 }
6009
6010 exit:
6011 return ret;
6012 }
6013 #endif /* WL_GPIO_NOTIFY */
6014
6015 #ifdef CSI_SUPPORT
6016 typedef struct csi_config {
6017 /* Peer device mac address. */
6018 struct ether_addr addr;
6019 /* BW to be used in the measurements. This needs to be supported both by the */
6020 /* device itself and the peer. */
6021 uint32 bw;
6022 /* Time interval between measurements (units: 1 ms). */
6023 uint32 period;
6024 /* CSI method */
6025 uint32 method;
6026 } csi_config_t;
6027
6028 typedef struct csi_list {
6029 uint32 cnt;
6030 csi_config_t configs[1];
6031 } csi_list_t;
6032
6033 static int
6034 wl_ether_atoe(const char *a, struct ether_addr *n)
6035 {
6036 char *c = NULL;
6037 int i = 0;
6038
6039 memset(n, 0, ETHER_ADDR_LEN);
6040 for (;;) {
6041 n->octet[i++] = (uint8)strtoul(a, &c, 16);
6042 if (!*c++ || i == ETHER_ADDR_LEN)
6043 break;
6044 a = c;
6045 }
6046 return (i == ETHER_ADDR_LEN);
6047 }
6048
6049 static int
6050 wl_ext_csi(struct net_device *dev, char *data, char *command, int total_len)
6051 {
6052 csi_config_t csi, *csip;
6053 csi_list_t *csi_list;
6054 int ret = -1, period=-1, i;
6055 char mac[32], *buf = NULL;
6056 struct ether_addr ea;
6057 int bytes_written = 0;
6058
6059 buf = kmalloc(WLC_IOCTL_SMLEN, GFP_KERNEL);
6060 if (buf == NULL) {
6061 AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
6062 goto exit;
6063 }
6064 memset(buf, 0, WLC_IOCTL_SMLEN);
6065
6066 if (data) {
6067 sscanf(data, "%s %d", mac, &period);
6068 ret = wl_ether_atoe(mac, &ea);
6069 if (!ret) {
6070 AEXT_ERROR(dev->name, "rejecting mac=%s, ret=%d\n", mac, ret);
6071 goto exit;
6072 }
6073 AEXT_TRACE(dev->name, "mac=%pM, period=%d", &ea, period);
6074 if (period > 0) {
6075 memset(&csi, 0, sizeof(csi_config_t));
6076 bcopy(&ea, &csi.addr, ETHER_ADDR_LEN);
6077 csi.period = period;
6078 ret = wl_ext_iovar_setbuf(dev, "csi", (char *)&csi, sizeof(csi),
6079 buf, WLC_IOCTL_SMLEN, NULL);
6080 } else if (period == 0) {
6081 memset(&csi, 0, sizeof(csi_config_t));
6082 bcopy(&ea, &csi.addr, ETHER_ADDR_LEN);
6083 ret = wl_ext_iovar_setbuf(dev, "csi_del", (char *)&csi, sizeof(csi),
6084 buf, WLC_IOCTL_SMLEN, NULL);
6085 } else {
6086 ret = wl_ext_iovar_getbuf(dev, "csi", &ea, ETHER_ADDR_LEN, buf,
6087 WLC_IOCTL_SMLEN, NULL);
6088 if (!ret) {
6089 csip = (csi_config_t *) buf;
6090 /* Dump all lists */
6091 bytes_written += snprintf(command+bytes_written, total_len,
6092 "Mac :%pM\n"
6093 "Period :%d\n"
6094 "BW :%d\n"
6095 "Method :%d\n",
6096 &csip->addr, csip->period, csip->bw, csip->method);
6097 AEXT_TRACE(dev->name, "command result is %s\n", command);
6098 ret = bytes_written;
6099 }
6100 }
6101 }
6102 else {
6103 ret = wl_ext_iovar_getbuf(dev, "csi_list", NULL, 0, buf, WLC_IOCTL_SMLEN, NULL);
6104 if (!ret) {
6105 csi_list = (csi_list_t *)buf;
6106 bytes_written += snprintf(command+bytes_written, total_len,
6107 "Total number :%d\n", csi_list->cnt);
6108 for (i=0; i<csi_list->cnt; i++) {
6109 csip = &csi_list->configs[i];
6110 bytes_written += snprintf(command+bytes_written, total_len,
6111 "Idx :%d\n"
6112 "Mac :%pM\n"
6113 "Period :%d\n"
6114 "BW :%d\n"
6115 "Method :%d\n\n",
6116 i+1, &csip->addr, csip->period, csip->bw, csip->method);
6117 }
6118 AEXT_TRACE(dev->name, "command result is %s\n", command);
6119 ret = bytes_written;
6120 }
6121 }
6122
6123 exit:
6124 if (buf)
6125 kfree(buf);
6126 return ret;
6127 }
6128 #endif /* CSI_SUPPORT */
6129
6130 typedef int (wl_ext_tpl_parse_t)(struct net_device *dev, char *data, char *command,
6131 int total_len);
6132
6133 typedef struct wl_ext_iovar_tpl_t {
6134 int get;
6135 int set;
6136 char *name;
6137 wl_ext_tpl_parse_t *parse;
6138 } wl_ext_iovar_tpl_t;
6139
6140 const wl_ext_iovar_tpl_t wl_ext_iovar_tpl_list[] = {
6141 {WLC_GET_VAR, WLC_SET_VAR, "event_msg", wl_ext_event_msg},
6142 {WLC_GET_VAR, WLC_SET_VAR, "gtk_key_info", wl_ext_gtk_key_info},
6143 {WLC_GET_VAR, WLC_SET_VAR, "recal", wl_ext_recal},
6144 {WLC_GET_VAR, WLC_SET_VAR, "rsdb_mode", wl_ext_rsdb_mode},
6145 {WLC_GET_VAR, WLC_SET_VAR, "mkeep_alive", wl_ext_mkeep_alive},
6146 #ifdef PKT_FILTER_SUPPORT
6147 {WLC_GET_VAR, WLC_SET_VAR, "pkt_filter_add", wl_ext_pkt_filter_add},
6148 {WLC_GET_VAR, WLC_SET_VAR, "pkt_filter_delete", wl_ext_pkt_filter_delete},
6149 {WLC_GET_VAR, WLC_SET_VAR, "pkt_filter_enable", wl_ext_pkt_filter_enable},
6150 #endif /* PKT_FILTER_SUPPORT */
6151 #if defined(WL_EXT_IAPSTA) && defined(WLMESH)
6152 {WLC_GET_VAR, WLC_SET_VAR, "mesh_peer_status", wl_ext_mesh_peer_status},
6153 #endif /* WL_EXT_IAPSTA && WLMESH */
6154 #ifdef SENDPROB
6155 {WLC_GET_VAR, WLC_SET_VAR, "send_probreq", wl_ext_send_probreq},
6156 {WLC_GET_VAR, WLC_SET_VAR, "send_probresp", wl_ext_send_probresp},
6157 {WLC_GET_VAR, WLC_SET_VAR, "recv_probreq", wl_ext_recv_probreq},
6158 {WLC_GET_VAR, WLC_SET_VAR, "recv_probresp", wl_ext_recv_probresp},
6159 #endif /* SENDPROB */
6160 #ifdef WL_EXT_TCPKA
6161 {WLC_GET_VAR, WLC_SET_VAR, "tcpka_conn_add", wl_ext_tcpka_conn_add},
6162 {WLC_GET_VAR, WLC_SET_VAR, "tcpka_conn_enable", wl_ext_tcpka_conn_enable},
6163 {WLC_GET_VAR, WLC_SET_VAR, "tcpka_conn_sess_info", wl_ext_tcpka_conn_info},
6164 #endif /* WL_EXT_TCPKA */
6165 #ifdef WL_EXT_WOWL
6166 {WLC_GET_VAR, WLC_SET_VAR, "wowl_pattern", wl_ext_wowl_pattern},
6167 {WLC_GET_VAR, WLC_SET_VAR, "wowl_wakeind", wl_ext_wowl_wakeind},
6168 #endif /* WL_EXT_WOWL */
6169 #ifdef IDHCP
6170 {WLC_GET_VAR, WLC_SET_VAR, "dhcpc_dump", wl_ext_dhcpc_dump},
6171 {WLC_GET_VAR, WLC_SET_VAR, "dhcpc_param", wl_ext_dhcpc_param},
6172 #endif /* IDHCP */
6173 #ifdef WL_GPIO_NOTIFY
6174 {WLC_GET_VAR, WLC_SET_VAR, "bcol_gpio_noti", wl_ext_gpio_notify},
6175 #endif /* WL_GPIO_NOTIFY */
6176 #ifdef CSI_SUPPORT
6177 {WLC_GET_VAR, WLC_SET_VAR, "csi", wl_ext_csi},
6178 #endif /* CSI_SUPPORT */
6179 };
6180
6181 /*
6182 Ex: dhd_priv wl [cmd] [val]
6183 dhd_priv wl 85
6184 dhd_priv wl 86 1
6185 dhd_priv wl mpc
6186 dhd_priv wl mpc 1
6187 */
6188 static int
6189 wl_ext_wl_iovar(struct net_device *dev, char *command, int total_len)
6190 {
6191 int cmd, val, ret = -1, i;
6192 char name[32], *pch, *pick_tmp, *data;
6193 int bytes_written=-1;
6194 const wl_ext_iovar_tpl_t *tpl = wl_ext_iovar_tpl_list;
6195 int tpl_count = ARRAY_SIZE(wl_ext_iovar_tpl_list);
6196
6197 AEXT_TRACE(dev->name, "cmd %s\n", command);
6198 pick_tmp = command;
6199
6200 pch = bcmstrtok(&pick_tmp, " ", 0); // pick wl
6201 if (!pch || strncmp(pch, "wl", 2))
6202 goto exit;
6203
6204 pch = bcmstrtok(&pick_tmp, " ", 0); // pick cmd
6205 if (!pch)
6206 goto exit;
6207
6208 memset(name, 0 , sizeof (name));
6209 cmd = (int)simple_strtol(pch, NULL, 0);
6210 if (cmd == 0) {
6211 strcpy(name, pch);
6212 }
6213 data = bcmstrtok(&pick_tmp, "", 0); // pick data
6214 if (data && cmd == 0) {
6215 cmd = WLC_SET_VAR;
6216 } else if (cmd == 0) {
6217 cmd = WLC_GET_VAR;
6218 }
6219
6220 /* look for a matching code in the table */
6221 for (i = 0; i < tpl_count; i++, tpl++) {
6222 if ((tpl->get == cmd || tpl->set == cmd) && !strcmp(tpl->name, name))
6223 break;
6224 }
6225 if (i < tpl_count && tpl->parse) {
6226 ret = tpl->parse(dev, data, command, total_len);
6227 } else {
6228 if (cmd == WLC_SET_VAR) {
6229 val = (int)simple_strtol(data, NULL, 0);
6230 AEXT_INFO(dev->name, "set %s %d\n", name, val);
6231 ret = wl_ext_iovar_setint(dev, name, val);
6232 } else if (cmd == WLC_GET_VAR) {
6233 AEXT_INFO(dev->name, "get %s\n", name);
6234 ret = wl_ext_iovar_getint(dev, name, &val);
6235 if (!ret) {
6236 bytes_written = snprintf(command, total_len, "%d", val);
6237 AEXT_INFO(dev->name, "command result is %s\n", command);
6238 ret = bytes_written;
6239 }
6240 } else if (data) {
6241 val = (int)simple_strtol(data, NULL, 0);
6242 AEXT_INFO(dev->name, "set %d %d\n", cmd, val);
6243 ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), TRUE);
6244 } else {
6245 AEXT_INFO(dev->name, "get %d\n", cmd);
6246 ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), FALSE);
6247 if (!ret) {
6248 bytes_written = snprintf(command, total_len, "%d", val);
6249 AEXT_INFO(dev->name, "command result is %s\n", command);
6250 ret = bytes_written;
6251 }
6252 }
6253 }
6254
6255 exit:
6256 return ret;
6257 }
6258
6259 int
6260 wl_android_ext_priv_cmd(struct net_device *net, char *command,
6261 int total_len, int *bytes_written)
6262 {
6263 int ret = 0;
6264
6265 if (strnicmp(command, CMD_CHANNELS, strlen(CMD_CHANNELS)) == 0) {
6266 *bytes_written = wl_ext_channels(net, command, total_len);
6267 }
6268 else if (strnicmp(command, CMD_CHANNEL, strlen(CMD_CHANNEL)) == 0) {
6269 *bytes_written = wl_ext_channel(net, command, total_len);
6270 }
6271 else if (strnicmp(command, CMD_ROAM_TRIGGER, strlen(CMD_ROAM_TRIGGER)) == 0) {
6272 *bytes_written = wl_ext_roam_trigger(net, command, total_len);
6273 }
6274 else if (strnicmp(command, CMD_PM, strlen(CMD_PM)) == 0) {
6275 *bytes_written = wl_ext_pm(net, command, total_len);
6276 }
6277 else if (strnicmp(command, CMD_MONITOR, strlen(CMD_MONITOR)) == 0) {
6278 *bytes_written = wl_ext_monitor(net, command, total_len);
6279 }
6280 else if (strnicmp(command, CMD_SET_SUSPEND_BCN_LI_DTIM, strlen(CMD_SET_SUSPEND_BCN_LI_DTIM)) == 0) {
6281 int bcn_li_dtim;
6282 bcn_li_dtim = (int)simple_strtol((command + strlen(CMD_SET_SUSPEND_BCN_LI_DTIM) + 1), NULL, 10);
6283 *bytes_written = net_os_set_suspend_bcn_li_dtim(net, bcn_li_dtim);
6284 }
6285 #ifdef WL_EXT_IAPSTA
6286 else if (strnicmp(command, CMD_IAPSTA_INIT, strlen(CMD_IAPSTA_INIT)) == 0 ||
6287 strnicmp(command, CMD_ISAM_INIT, strlen(CMD_ISAM_INIT)) == 0) {
6288 *bytes_written = wl_ext_isam_init(net, command, total_len);
6289 }
6290 else if (strnicmp(command, CMD_IAPSTA_CONFIG, strlen(CMD_IAPSTA_CONFIG)) == 0 ||
6291 strnicmp(command, CMD_ISAM_CONFIG, strlen(CMD_ISAM_CONFIG)) == 0) {
6292 *bytes_written = wl_ext_iapsta_config(net, command, total_len);
6293 }
6294 else if (strnicmp(command, CMD_IAPSTA_ENABLE, strlen(CMD_IAPSTA_ENABLE)) == 0 ||
6295 strnicmp(command, CMD_ISAM_ENABLE, strlen(CMD_ISAM_ENABLE)) == 0) {
6296 *bytes_written = wl_ext_iapsta_enable(net, command, total_len);
6297 }
6298 else if (strnicmp(command, CMD_IAPSTA_DISABLE, strlen(CMD_IAPSTA_DISABLE)) == 0 ||
6299 strnicmp(command, CMD_ISAM_DISABLE, strlen(CMD_ISAM_DISABLE)) == 0) {
6300 *bytes_written = wl_ext_iapsta_disable(net, command, total_len);
6301 }
6302 else if (strnicmp(command, CMD_ISAM_STATUS, strlen(CMD_ISAM_STATUS)) == 0) {
6303 *bytes_written = wl_ext_isam_status(net, command, total_len);
6304 }
6305 else if (strnicmp(command, CMD_ISAM_PARAM, strlen(CMD_ISAM_PARAM)) == 0) {
6306 *bytes_written = wl_ext_isam_param(net, command, total_len);
6307 }
6308 #if defined(WLMESH) && defined(WL_ESCAN)
6309 else if (strnicmp(command, CMD_ISAM_PEER_PATH, strlen(CMD_ISAM_PEER_PATH)) == 0) {
6310 *bytes_written = wl_ext_isam_peer_path(net, command, total_len);
6311 }
6312 #endif /* WLMESH && WL_ESCAN */
6313 #endif /* WL_EXT_IAPSTA */
6314 #ifdef WL_CFG80211
6315 else if (strnicmp(command, CMD_AUTOCHANNEL, strlen(CMD_AUTOCHANNEL)) == 0) {
6316 *bytes_written = wl_cfg80211_autochannel(net, command, total_len);
6317 }
6318 #endif /* WL_CFG80211 */
6319 #if defined(WL_WIRELESS_EXT) && defined(WL_ESCAN)
6320 else if (strnicmp(command, CMD_AUTOCHANNEL, strlen(CMD_AUTOCHANNEL)) == 0) {
6321 *bytes_written = wl_iw_autochannel(net, command, total_len);
6322 }
6323 #endif /* WL_WIRELESS_EXT && WL_ESCAN */
6324 else if (strnicmp(command, CMD_WLMSGLEVEL, strlen(CMD_WLMSGLEVEL)) == 0) {
6325 *bytes_written = wl_ext_wlmsglevel(net, command, total_len);
6326 }
6327 else if (strnicmp(command, CMD_WL, strlen(CMD_WL)) == 0) {
6328 *bytes_written = wl_ext_wl_iovar(net, command, total_len);
6329 }
6330 else
6331 ret = -1;
6332
6333 return ret;
6334 }
6335
6336 #if defined(WL_CFG80211) || defined(WL_ESCAN)
6337 int
6338 wl_ext_get_distance(struct net_device *net, u32 band)
6339 {
6340 u32 bw = WL_CHANSPEC_BW_20;
6341 s32 bw_cap = 0, distance = 0;
6342 struct {
6343 u32 band;
6344 u32 bw_cap;
6345 } param = {0, 0};
6346 char buf[WLC_IOCTL_SMLEN]="\0";
6347 s32 err = BCME_OK;
6348
6349 param.band = band;
6350 err = wl_ext_iovar_getbuf(net, "bw_cap", &param, sizeof(param), buf,
6351 sizeof(buf), NULL);
6352 if (err) {
6353 if (err != BCME_UNSUPPORTED) {
6354 AEXT_ERROR(net->name, "bw_cap failed, %d\n", err);
6355 return err;
6356 } else {
6357 err = wl_ext_iovar_getint(net, "mimo_bw_cap", &bw_cap);
6358 if (bw_cap != WLC_N_BW_20ALL)
6359 bw = WL_CHANSPEC_BW_40;
6360 }
6361 } else {
6362 if (WL_BW_CAP_80MHZ(buf[0]))
6363 bw = WL_CHANSPEC_BW_80;
6364 else if (WL_BW_CAP_40MHZ(buf[0]))
6365 bw = WL_CHANSPEC_BW_40;
6366 else
6367 bw = WL_CHANSPEC_BW_20;
6368 }
6369
6370 if (bw == WL_CHANSPEC_BW_20)
6371 distance = 2;
6372 else if (bw == WL_CHANSPEC_BW_40)
6373 distance = 4;
6374 else if (bw == WL_CHANSPEC_BW_80)
6375 distance = 8;
6376 else
6377 distance = 16;
6378 AEXT_INFO(net->name, "bw=0x%x, distance=%d\n", bw, distance);
6379
6380 return distance;
6381 }
6382
6383 int
6384 wl_ext_get_best_channel(struct net_device *net,
6385 #if defined(BSSCACHE)
6386 wl_bss_cache_ctrl_t *bss_cache_ctrl,
6387 #else
6388 struct wl_scan_results *bss_list,
6389 #endif /* BSSCACHE */
6390 int ioctl_ver, int *best_2g_ch, int *best_5g_ch
6391 )
6392 {
6393 struct wl_bss_info *bi = NULL; /* must be initialized */
6394 s32 i, j;
6395 #if defined(BSSCACHE)
6396 wl_bss_cache_t *node;
6397 #endif /* BSSCACHE */
6398 int b_band[CH_MAX_2G_CHANNEL]={0}, a_band1[4]={0}, a_band4[5]={0};
6399 s32 cen_ch, distance, distance_2g, distance_5g, ch, min_ap=999;
6400 u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
6401 wl_uint32_list_t *list;
6402 int ret;
6403 chanspec_t chanspec;
6404 struct dhd_pub *dhd = dhd_get_pub(net);
6405
6406 memset(b_band, -1, sizeof(b_band));
6407 memset(a_band1, -1, sizeof(a_band1));
6408 memset(a_band4, -1, sizeof(a_band4));
6409
6410 memset(valid_chan_list, 0, sizeof(valid_chan_list));
6411 list = (wl_uint32_list_t *)(void *) valid_chan_list;
6412 list->count = htod32(WL_NUMCHANNELS);
6413 ret = wl_ext_ioctl(net, WLC_GET_VALID_CHANNELS, &valid_chan_list,
6414 sizeof(valid_chan_list), 0);
6415 if (ret<0) {
6416 AEXT_ERROR(net->name, "get channels failed with %d\n", ret);
6417 return 0;
6418 } else {
6419 for (i = 0; i < dtoh32(list->count); i++) {
6420 ch = dtoh32(list->element[i]);
6421 if (!dhd_conf_match_channel(dhd, ch))
6422 continue;
6423 if (ch < CH_MAX_2G_CHANNEL)
6424 b_band[ch-1] = 0;
6425 else if (ch <= 48)
6426 a_band1[(ch-36)/4] = 0;
6427 else if (ch >= 149 && ch <= 161)
6428 a_band4[(ch-149)/4] = 0;
6429 }
6430 }
6431
6432 distance_2g = wl_ext_get_distance(net, WLC_BAND_2G);
6433 distance_5g = wl_ext_get_distance(net, WLC_BAND_5G);
6434
6435 #if defined(BSSCACHE)
6436 node = bss_cache_ctrl->m_cache_head;
6437 for (i=0; node && i<256; i++)
6438 #else
6439 for (i=0; i < bss_list->count; i++)
6440 #endif /* BSSCACHE */
6441 {
6442 #if defined(BSSCACHE)
6443 bi = node->results.bss_info;
6444 #else
6445 bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : bss_list->bss_info;
6446 #endif /* BSSCACHE */
6447 chanspec = wl_ext_chspec_driver_to_host(ioctl_ver, bi->chanspec);
6448 cen_ch = CHSPEC_CHANNEL(bi->chanspec);
6449 distance = 0;
6450 if (CHSPEC_IS20(chanspec))
6451 distance += 2;
6452 else if (CHSPEC_IS40(chanspec))
6453 distance += 4;
6454 else if (CHSPEC_IS80(chanspec))
6455 distance += 8;
6456 else
6457 distance += 16;
6458
6459 if (CHSPEC_IS2G(chanspec)) {
6460 distance += distance_2g;
6461 for (j=0; j<ARRAYSIZE(b_band); j++) {
6462 if (b_band[j] >= 0 && abs(cen_ch-(1+j)) <= distance)
6463 b_band[j] += 1;
6464 }
6465 } else {
6466 distance += distance_5g;
6467 if (cen_ch <= 48) {
6468 for (j=0; j<ARRAYSIZE(a_band1); j++) {
6469 if (a_band1[j] >= 0 && abs(cen_ch-(36+j*4)) <= distance)
6470 a_band1[j] += 1;
6471 }
6472 } else if (cen_ch >= 149) {
6473 for (j=0; j<ARRAYSIZE(a_band4); j++) {
6474 if (a_band4[j] >= 0 && abs(cen_ch-(149+j*4)) <= distance)
6475 a_band4[j] += 1;
6476 }
6477 }
6478 }
6479 #if defined(BSSCACHE)
6480 node = node->next;
6481 #endif /* BSSCACHE */
6482 }
6483
6484 *best_2g_ch = 0;
6485 min_ap = 999;
6486 for (i=0; i<CH_MAX_2G_CHANNEL; i++) {
6487 if(b_band[i] < min_ap && b_band[i] >= 0) {
6488 min_ap = b_band[i];
6489 *best_2g_ch = i+1;
6490 }
6491 }
6492 *best_5g_ch = 0;
6493 min_ap = 999;
6494 for (i=0; i<ARRAYSIZE(a_band1); i++) {
6495 if(a_band1[i] < min_ap && a_band1[i] >= 0) {
6496 min_ap = a_band1[i];
6497 *best_5g_ch = i*4 + 36;
6498 }
6499 }
6500 for (i=0; i<ARRAYSIZE(a_band4); i++) {
6501 if(a_band4[i] < min_ap && a_band4[i] >= 0) {
6502 min_ap = a_band4[i];
6503 *best_5g_ch = i*4 + 149;
6504 }
6505 }
6506
6507 if (android_msg_level & ANDROID_INFO_LEVEL) {
6508 struct bcmstrbuf strbuf;
6509 char *tmp_buf = NULL;
6510 tmp_buf = kmalloc(WLC_IOCTL_SMLEN, GFP_KERNEL);
6511 if (tmp_buf == NULL) {
6512 AEXT_ERROR(net->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
6513 goto exit;
6514 }
6515 bcm_binit(&strbuf, tmp_buf, WLC_IOCTL_SMLEN);
6516 for (j=0; j<ARRAYSIZE(b_band); j++)
6517 bcm_bprintf(&strbuf, "%d/%d, ", b_band[j], 1+j);
6518 bcm_bprintf(&strbuf, "\n");
6519 for (j=0; j<ARRAYSIZE(a_band1); j++)
6520 bcm_bprintf(&strbuf, "%d/%d, ", a_band1[j], 36+j*4);
6521 bcm_bprintf(&strbuf, "\n");
6522 for (j=0; j<ARRAYSIZE(a_band4); j++)
6523 bcm_bprintf(&strbuf, "%d/%d, ", a_band4[j], 149+j*4);
6524 bcm_bprintf(&strbuf, "\n");
6525 bcm_bprintf(&strbuf, "best_2g_ch=%d, best_5g_ch=%d\n",
6526 *best_2g_ch, *best_5g_ch);
6527 AEXT_INFO(net->name, "\n%s", strbuf.origbuf);
6528 if (tmp_buf) {
6529 kfree(tmp_buf);
6530 }
6531 }
6532
6533 exit:
6534 return 0;
6535 }
6536 #endif /* WL_CFG80211 || WL_ESCAN */
6537
6538 #ifdef WL_CFG80211
6539 #define APCS_MAX_RETRY 10
6540 static int
6541 wl_ext_fw_apcs(struct net_device *dev, uint32 band)
6542 {
6543 int channel = 0, chosen = 0, retry = 0, ret = 0, spect = 0;
6544 u8 *reqbuf = NULL;
6545 uint32 buf_size;
6546
6547 ret = wldev_ioctl_get(dev, WLC_GET_SPECT_MANAGMENT, &spect, sizeof(spect));
6548 if (ret) {
6549 AEXT_ERROR(dev->name, "ACS: error getting the spect, ret=%d\n", ret);
6550 goto done;
6551 }
6552
6553 if (spect > 0) {
6554 ret = wl_android_set_spect(dev, 0);
6555 if (ret < 0) {
6556 AEXT_ERROR(dev->name, "ACS: error while setting spect, ret=%d\n", ret);
6557 goto done;
6558 }
6559 }
6560
6561 reqbuf = kmalloc(CHANSPEC_BUF_SIZE, GFP_KERNEL);
6562 if (reqbuf == NULL) {
6563 AEXT_ERROR(dev->name, "failed to allocate chanspec buffer\n");
6564 goto done;
6565 }
6566 memset(reqbuf, 0, CHANSPEC_BUF_SIZE);
6567
6568 if (band == WLC_BAND_AUTO) {
6569 AEXT_INFO(dev->name, "ACS full channel scan \n");
6570 reqbuf[0] = htod32(0);
6571 } else if (band == WLC_BAND_5G) {
6572 AEXT_INFO(dev->name, "ACS 5G band scan \n");
6573 if ((ret = wl_cfg80211_get_chanspecs_5g(dev, reqbuf, CHANSPEC_BUF_SIZE)) < 0) {
6574 AEXT_ERROR(dev->name, "ACS 5g chanspec retreival failed! \n");
6575 goto done;
6576 }
6577 } else if (band == WLC_BAND_2G) {
6578 /*
6579 * If channel argument is not provided/ argument 20 is provided,
6580 * Restrict channel to 2GHz, 20MHz BW, No SB
6581 */
6582 AEXT_INFO(dev->name, "ACS 2G band scan \n");
6583 if ((ret = wl_cfg80211_get_chanspecs_2g(dev, reqbuf, CHANSPEC_BUF_SIZE)) < 0) {
6584 AEXT_ERROR(dev->name, "ACS 2g chanspec retreival failed! \n");
6585 goto done;
6586 }
6587 } else {
6588 AEXT_ERROR(dev->name, "ACS: No band chosen\n");
6589 goto done;
6590 }
6591
6592 buf_size = (band == WLC_BAND_AUTO) ? sizeof(int) : CHANSPEC_BUF_SIZE;
6593 ret = wldev_ioctl_set(dev, WLC_START_CHANNEL_SEL, (void *)reqbuf,
6594 buf_size);
6595 if (ret < 0) {
6596 AEXT_ERROR(dev->name, "can't start auto channel scan, err = %d\n", ret);
6597 channel = 0;
6598 goto done;
6599 }
6600
6601 /* Wait for auto channel selection, max 3000 ms */
6602 if ((band == WLC_BAND_2G) || (band == WLC_BAND_5G)) {
6603 OSL_SLEEP(500);
6604 } else {
6605 /*
6606 * Full channel scan at the minimum takes 1.2secs
6607 * even with parallel scan. max wait time: 3500ms
6608 */
6609 OSL_SLEEP(1000);
6610 }
6611
6612 retry = APCS_MAX_RETRY;
6613 while (retry--) {
6614 ret = wldev_ioctl_get(dev, WLC_GET_CHANNEL_SEL, &chosen,
6615 sizeof(chosen));
6616 if (ret < 0) {
6617 chosen = 0;
6618 } else {
6619 chosen = dtoh32(chosen);
6620 }
6621
6622 if (chosen) {
6623 int chosen_band;
6624 int apcs_band;
6625 #ifdef D11AC_IOTYPES
6626 if (wl_cfg80211_get_ioctl_version() == 1) {
6627 channel = LCHSPEC_CHANNEL((chanspec_t)chosen);
6628 } else {
6629 channel = CHSPEC_CHANNEL((chanspec_t)chosen);
6630 }
6631 #else
6632 channel = CHSPEC_CHANNEL((chanspec_t)chosen);
6633 #endif /* D11AC_IOTYPES */
6634 apcs_band = (band == WLC_BAND_AUTO) ? WLC_BAND_2G : band;
6635 chosen_band = (channel <= CH_MAX_2G_CHANNEL) ? WLC_BAND_2G : WLC_BAND_5G;
6636 if (apcs_band == chosen_band) {
6637 WL_MSG(dev->name, "selected channel = %d\n", channel);
6638 break;
6639 }
6640 }
6641 AEXT_INFO(dev->name, "%d tried, ret = %d, chosen = 0x%x\n",
6642 (APCS_MAX_RETRY - retry), ret, chosen);
6643 OSL_SLEEP(250);
6644 }
6645
6646 done:
6647 if (spect > 0) {
6648 if ((ret = wl_android_set_spect(dev, spect) < 0)) {
6649 AEXT_ERROR(dev->name, "ACS: error while setting spect\n");
6650 }
6651 }
6652
6653 if (reqbuf) {
6654 kfree(reqbuf);
6655 }
6656
6657 return channel;
6658 }
6659 #endif /* WL_CFG80211 */
6660
6661 #ifdef WL_ESCAN
6662 int
6663 wl_ext_drv_apcs(struct net_device *dev, uint32 band)
6664 {
6665 int ret = 0, channel = 0;
6666 struct dhd_pub *dhd = dhd_get_pub(dev);
6667 struct wl_escan_info *escan = NULL;
6668 int retry = 0, retry_max, retry_interval = 250, up = 1;
6669 #ifdef WL_CFG80211
6670 struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
6671 #endif /* WL_CFG80211 */
6672
6673 escan = dhd->escan;
6674 if (dhd) {
6675 retry_max = WL_ESCAN_TIMER_INTERVAL_MS/retry_interval;
6676 ret = wldev_ioctl_get(dev, WLC_GET_UP, &up, sizeof(s32));
6677 if (ret < 0 || up == 0) {
6678 ret = wldev_ioctl_set(dev, WLC_UP, &up, sizeof(s32));
6679 }
6680 retry = retry_max;
6681 while (retry--) {
6682 if (escan->escan_state == ESCAN_STATE_SCANING
6683 #ifdef WL_CFG80211
6684 || wl_get_drv_status_all(cfg, SCANNING)
6685 #endif
6686 )
6687 {
6688 AEXT_INFO(dev->name, "Scanning %d tried, ret = %d\n",
6689 (retry_max - retry), ret);
6690 } else {
6691 escan->autochannel = 1;
6692 ret = wl_escan_set_scan(dev, dhd, NULL, 0, TRUE);
6693 if (!ret)
6694 break;
6695 }
6696 OSL_SLEEP(retry_interval);
6697 }
6698 if ((retry == 0) || (ret < 0))
6699 goto done;
6700 retry = retry_max;
6701 while (retry--) {
6702 if (escan->escan_state == ESCAN_STATE_IDLE) {
6703 if (band == WLC_BAND_5G)
6704 channel = escan->best_5g_ch;
6705 else
6706 channel = escan->best_2g_ch;
6707 WL_MSG(dev->name, "selected channel = %d\n", channel);
6708 goto done;
6709 }
6710 AEXT_INFO(dev->name, "escan_state=%d, %d tried, ret = %d\n",
6711 escan->escan_state, (retry_max - retry), ret);
6712 OSL_SLEEP(retry_interval);
6713 }
6714 if ((retry == 0) || (ret < 0))
6715 goto done;
6716 }
6717
6718 done:
6719 if (escan)
6720 escan->autochannel = 0;
6721
6722 return channel;
6723 }
6724 #endif /* WL_ESCAN */
6725
6726 int
6727 wl_ext_autochannel(struct net_device *dev, uint acs, uint32 band)
6728 {
6729 int channel = 0;
6730 uint16 chan_2g, chan_5g;
6731
6732 AEXT_INFO(dev->name, "acs=0x%x, band=%d \n", acs, band);
6733
6734 #ifdef WL_CFG80211
6735 if (acs & ACS_FW_BIT) {
6736 int ret = 0;
6737 ret = wldev_ioctl_get(dev, WLC_GET_CHANNEL_SEL, &channel, sizeof(channel));
6738 channel = 0;
6739 if (ret != BCME_UNSUPPORTED)
6740 channel = wl_ext_fw_apcs(dev, band);
6741 if (channel)
6742 return channel;
6743 }
6744 #endif
6745
6746 #ifdef WL_ESCAN
6747 if (acs & ACS_DRV_BIT)
6748 channel = wl_ext_drv_apcs(dev, band);
6749 #endif /* WL_ESCAN */
6750
6751 if (channel == 0) {
6752 wl_ext_get_default_chan(dev, &chan_2g, &chan_5g, TRUE);
6753 if (band == WLC_BAND_5G) {
6754 channel = chan_5g;
6755 } else {
6756 channel = chan_2g;
6757 }
6758 AEXT_ERROR(dev->name, "ACS failed. Fall back to default channel (%d) \n", channel);
6759 }
6760
6761 return channel;
6762 }
6763
6764 #if defined(RSSIAVG)
6765 void
6766 wl_free_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
6767 {
6768 wl_rssi_cache_t *node, *cur, **rssi_head;
6769 int i=0;
6770
6771 rssi_head = &rssi_cache_ctrl->m_cache_head;
6772 node = *rssi_head;
6773
6774 for (;node;) {
6775 AEXT_INFO("wlan", "Free %d with BSSID %pM\n", i, &node->BSSID);
6776 cur = node;
6777 node = cur->next;
6778 kfree(cur);
6779 i++;
6780 }
6781 *rssi_head = NULL;
6782 }
6783
6784 void
6785 wl_delete_dirty_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
6786 {
6787 wl_rssi_cache_t *node, *prev, **rssi_head;
6788 int i = -1, tmp = 0;
6789 struct osl_timespec now;
6790
6791 osl_do_gettimeofday(&now);
6792
6793 rssi_head = &rssi_cache_ctrl->m_cache_head;
6794 node = *rssi_head;
6795 prev = node;
6796 for (;node;) {
6797 i++;
6798 if (now.tv_sec > node->tv.tv_sec) {
6799 if (node == *rssi_head) {
6800 tmp = 1;
6801 *rssi_head = node->next;
6802 } else {
6803 tmp = 0;
6804 prev->next = node->next;
6805 }
6806 AEXT_INFO("wlan", "Del %d with BSSID %pM\n", i, &node->BSSID);
6807 kfree(node);
6808 if (tmp == 1) {
6809 node = *rssi_head;
6810 prev = node;
6811 } else {
6812 node = prev->next;
6813 }
6814 continue;
6815 }
6816 prev = node;
6817 node = node->next;
6818 }
6819 }
6820
6821 void
6822 wl_delete_disconnected_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
6823 u8 *bssid)
6824 {
6825 wl_rssi_cache_t *node, *prev, **rssi_head;
6826 int i = -1, tmp = 0;
6827
6828 rssi_head = &rssi_cache_ctrl->m_cache_head;
6829 node = *rssi_head;
6830 prev = node;
6831 for (;node;) {
6832 i++;
6833 if (!memcmp(&node->BSSID, bssid, ETHER_ADDR_LEN)) {
6834 if (node == *rssi_head) {
6835 tmp = 1;
6836 *rssi_head = node->next;
6837 } else {
6838 tmp = 0;
6839 prev->next = node->next;
6840 }
6841 AEXT_INFO("wlan", "Del %d with BSSID %pM\n", i, &node->BSSID);
6842 kfree(node);
6843 if (tmp == 1) {
6844 node = *rssi_head;
6845 prev = node;
6846 } else {
6847 node = prev->next;
6848 }
6849 continue;
6850 }
6851 prev = node;
6852 node = node->next;
6853 }
6854 }
6855
6856 void
6857 wl_reset_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
6858 {
6859 wl_rssi_cache_t *node, **rssi_head;
6860
6861 rssi_head = &rssi_cache_ctrl->m_cache_head;
6862
6863 /* reset dirty */
6864 node = *rssi_head;
6865 for (;node;) {
6866 node->dirty += 1;
6867 node = node->next;
6868 }
6869 }
6870
6871 int
6872 wl_update_connected_rssi_cache(struct net_device *net,
6873 wl_rssi_cache_ctrl_t *rssi_cache_ctrl, int *rssi_avg)
6874 {
6875 wl_rssi_cache_t *node, *prev, *leaf, **rssi_head;
6876 int j, k=0;
6877 int rssi, error=0;
6878 struct ether_addr bssid;
6879 struct osl_timespec now, timeout;
6880 scb_val_t scbval;
6881
6882 if (!g_wifi_on)
6883 return 0;
6884
6885 error = wldev_ioctl(net, WLC_GET_BSSID, &bssid, sizeof(bssid), 0);
6886 if (error == BCME_NOTASSOCIATED) {
6887 AEXT_INFO("wlan", "Not Associated! res:%d\n", error);
6888 return 0;
6889 }
6890 if (error) {
6891 AEXT_ERROR(net->name, "Could not get bssid (%d)\n", error);
6892 }
6893 error = wldev_get_rssi(net, &scbval);
6894 if (error) {
6895 AEXT_ERROR(net->name, "Could not get rssi (%d)\n", error);
6896 return error;
6897 }
6898 rssi = scbval.val;
6899
6900 osl_do_gettimeofday(&now);
6901 timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT;
6902 if (timeout.tv_sec < now.tv_sec) {
6903 /*
6904 * Integer overflow - assume long enough timeout to be assumed
6905 * to be infinite, i.e., the timeout would never happen.
6906 */
6907 AEXT_TRACE(net->name,
6908 "Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu\n",
6909 RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec);
6910 }
6911
6912 /* update RSSI */
6913 rssi_head = &rssi_cache_ctrl->m_cache_head;
6914 node = *rssi_head;
6915 prev = NULL;
6916 for (;node;) {
6917 if (!memcmp(&node->BSSID, &bssid, ETHER_ADDR_LEN)) {
6918 AEXT_INFO("wlan", "Update %d with BSSID %pM, RSSI=%d\n", k, &bssid, rssi);
6919 for (j=0; j<RSSIAVG_LEN-1; j++)
6920 node->RSSI[j] = node->RSSI[j+1];
6921 node->RSSI[j] = rssi;
6922 node->dirty = 0;
6923 node->tv = timeout;
6924 goto exit;
6925 }
6926 prev = node;
6927 node = node->next;
6928 k++;
6929 }
6930
6931 leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL);
6932 if (!leaf) {
6933 AEXT_ERROR(net->name, "Memory alloc failure %d\n", (int)sizeof(wl_rssi_cache_t));
6934 return 0;
6935 }
6936 AEXT_INFO(net->name, "Add %d with cached BSSID %pM, RSSI=%3d in the leaf\n",
6937 k, &bssid, rssi);
6938
6939 leaf->next = NULL;
6940 leaf->dirty = 0;
6941 leaf->tv = timeout;
6942 memcpy(&leaf->BSSID, &bssid, ETHER_ADDR_LEN);
6943 for (j=0; j<RSSIAVG_LEN; j++)
6944 leaf->RSSI[j] = rssi;
6945
6946 if (!prev)
6947 *rssi_head = leaf;
6948 else
6949 prev->next = leaf;
6950
6951 exit:
6952 *rssi_avg = (int)wl_get_avg_rssi(rssi_cache_ctrl, &bssid);
6953
6954 return error;
6955 }
6956
6957 void
6958 wl_update_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
6959 wl_scan_results_t *ss_list)
6960 {
6961 wl_rssi_cache_t *node, *prev, *leaf, **rssi_head;
6962 wl_bss_info_t *bi = NULL;
6963 int i, j, k;
6964 struct osl_timespec now, timeout;
6965
6966 if (!ss_list->count)
6967 return;
6968
6969 osl_do_gettimeofday(&now);
6970 timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT;
6971 if (timeout.tv_sec < now.tv_sec) {
6972 /*
6973 * Integer overflow - assume long enough timeout to be assumed
6974 * to be infinite, i.e., the timeout would never happen.
6975 */
6976 AEXT_TRACE("wlan",
6977 "Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu\n",
6978 RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec);
6979 }
6980
6981 rssi_head = &rssi_cache_ctrl->m_cache_head;
6982
6983 /* update RSSI */
6984 for (i = 0; i < ss_list->count; i++) {
6985 node = *rssi_head;
6986 prev = NULL;
6987 k = 0;
6988 bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info;
6989 for (;node;) {
6990 if (!memcmp(&node->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) {
6991 AEXT_INFO("wlan", "Update %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
6992 k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
6993 for (j=0; j<RSSIAVG_LEN-1; j++)
6994 node->RSSI[j] = node->RSSI[j+1];
6995 node->RSSI[j] = dtoh16(bi->RSSI);
6996 node->dirty = 0;
6997 node->tv = timeout;
6998 break;
6999 }
7000 prev = node;
7001 node = node->next;
7002 k++;
7003 }
7004
7005 if (node)
7006 continue;
7007
7008 leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL);
7009 if (!leaf) {
7010 AEXT_ERROR("wlan", "Memory alloc failure %d\n",
7011 (int)sizeof(wl_rssi_cache_t));
7012 return;
7013 }
7014 AEXT_INFO("wlan", "Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\" in the leaf\n",
7015 k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
7016
7017 leaf->next = NULL;
7018 leaf->dirty = 0;
7019 leaf->tv = timeout;
7020 memcpy(&leaf->BSSID, &bi->BSSID, ETHER_ADDR_LEN);
7021 for (j=0; j<RSSIAVG_LEN; j++)
7022 leaf->RSSI[j] = dtoh16(bi->RSSI);
7023
7024 if (!prev)
7025 *rssi_head = leaf;
7026 else
7027 prev->next = leaf;
7028 }
7029 }
7030
7031 int16
7032 wl_get_avg_rssi(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, void *addr)
7033 {
7034 wl_rssi_cache_t *node, **rssi_head;
7035 int j, rssi_sum, rssi=RSSI_MINVAL;
7036
7037 rssi_head = &rssi_cache_ctrl->m_cache_head;
7038
7039 node = *rssi_head;
7040 for (;node;) {
7041 if (!memcmp(&node->BSSID, addr, ETHER_ADDR_LEN)) {
7042 rssi_sum = 0;
7043 rssi = 0;
7044 for (j=0; j<RSSIAVG_LEN; j++)
7045 rssi_sum += node->RSSI[RSSIAVG_LEN-j-1];
7046 rssi = rssi_sum / j;
7047 break;
7048 }
7049 node = node->next;
7050 }
7051 rssi = MIN(rssi, RSSI_MAXVAL);
7052 if (rssi == RSSI_MINVAL) {
7053 AEXT_ERROR("wlan", "BSSID %pM does not in RSSI cache\n", addr);
7054 }
7055 return (int16)rssi;
7056 }
7057 #endif /* RSSIAVG */
7058
7059 #if defined(RSSIOFFSET)
7060 int
7061 wl_update_rssi_offset(struct net_device *net, int rssi)
7062 {
7063 #if defined(RSSIOFFSET_NEW)
7064 int j;
7065 #endif /* RSSIOFFSET_NEW */
7066
7067 if (!g_wifi_on)
7068 return rssi;
7069
7070 #if defined(RSSIOFFSET_NEW)
7071 for (j=0; j<RSSI_OFFSET; j++) {
7072 if (rssi - (RSSI_OFFSET_MINVAL+RSSI_OFFSET_INTVAL*(j+1)) < 0)
7073 break;
7074 }
7075 rssi += j;
7076 #else
7077 rssi += RSSI_OFFSET;
7078 #endif /* RSSIOFFSET_NEW */
7079 return MIN(rssi, RSSI_MAXVAL);
7080 }
7081 #endif /* RSSIOFFSET */
7082
7083 #if defined(BSSCACHE)
7084 void
7085 wl_free_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)
7086 {
7087 wl_bss_cache_t *node, *cur, **bss_head;
7088 int i=0;
7089
7090 AEXT_TRACE("wlan", "called\n");
7091
7092 bss_head = &bss_cache_ctrl->m_cache_head;
7093 node = *bss_head;
7094
7095 for (;node;) {
7096 AEXT_TRACE("wlan", "Free %d with BSSID %pM\n",
7097 i, &node->results.bss_info->BSSID);
7098 cur = node;
7099 node = cur->next;
7100 kfree(cur);
7101 i++;
7102 }
7103 *bss_head = NULL;
7104 }
7105
7106 void
7107 wl_delete_dirty_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)
7108 {
7109 wl_bss_cache_t *node, *prev, **bss_head;
7110 int i = -1, tmp = 0;
7111 struct osl_timespec now;
7112
7113 osl_do_gettimeofday(&now);
7114
7115 bss_head = &bss_cache_ctrl->m_cache_head;
7116 node = *bss_head;
7117 prev = node;
7118 for (;node;) {
7119 i++;
7120 if (now.tv_sec > node->tv.tv_sec) {
7121 if (node == *bss_head) {
7122 tmp = 1;
7123 *bss_head = node->next;
7124 } else {
7125 tmp = 0;
7126 prev->next = node->next;
7127 }
7128 AEXT_TRACE("wlan", "Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
7129 i, &node->results.bss_info->BSSID,
7130 dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID);
7131 kfree(node);
7132 if (tmp == 1) {
7133 node = *bss_head;
7134 prev = node;
7135 } else {
7136 node = prev->next;
7137 }
7138 continue;
7139 }
7140 prev = node;
7141 node = node->next;
7142 }
7143 }
7144
7145 void
7146 wl_delete_disconnected_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl,
7147 u8 *bssid)
7148 {
7149 wl_bss_cache_t *node, *prev, **bss_head;
7150 int i = -1, tmp = 0;
7151
7152 bss_head = &bss_cache_ctrl->m_cache_head;
7153 node = *bss_head;
7154 prev = node;
7155 for (;node;) {
7156 i++;
7157 if (!memcmp(&node->results.bss_info->BSSID, bssid, ETHER_ADDR_LEN)) {
7158 if (node == *bss_head) {
7159 tmp = 1;
7160 *bss_head = node->next;
7161 } else {
7162 tmp = 0;
7163 prev->next = node->next;
7164 }
7165 AEXT_TRACE("wlan", "Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
7166 i, &node->results.bss_info->BSSID,
7167 dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID);
7168 kfree(node);
7169 if (tmp == 1) {
7170 node = *bss_head;
7171 prev = node;
7172 } else {
7173 node = prev->next;
7174 }
7175 continue;
7176 }
7177 prev = node;
7178 node = node->next;
7179 }
7180 }
7181
7182 void
7183 wl_reset_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)
7184 {
7185 wl_bss_cache_t *node, **bss_head;
7186
7187 bss_head = &bss_cache_ctrl->m_cache_head;
7188
7189 /* reset dirty */
7190 node = *bss_head;
7191 for (;node;) {
7192 node->dirty += 1;
7193 node = node->next;
7194 }
7195 }
7196
7197 void dump_bss_cache(
7198 #if defined(RSSIAVG)
7199 wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
7200 #endif /* RSSIAVG */
7201 wl_bss_cache_t *node)
7202 {
7203 int k = 0;
7204 int16 rssi;
7205
7206 for (;node;) {
7207 #if defined(RSSIAVG)
7208 rssi = wl_get_avg_rssi(rssi_cache_ctrl, &node->results.bss_info->BSSID);
7209 #else
7210 rssi = dtoh16(node->results.bss_info->RSSI);
7211 #endif /* RSSIAVG */
7212 AEXT_TRACE("wlan", "dump %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
7213 k, &node->results.bss_info->BSSID, rssi, node->results.bss_info->SSID);
7214 k++;
7215 node = node->next;
7216 }
7217 }
7218
7219 void
7220 wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl,
7221 #if defined(RSSIAVG)
7222 wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
7223 #endif /* RSSIAVG */
7224 wl_scan_results_t *ss_list)
7225 {
7226 wl_bss_cache_t *node, *prev, *leaf, **bss_head;
7227 wl_bss_info_t *bi = NULL;
7228 int i, k=0;
7229 #if defined(SORT_BSS_BY_RSSI)
7230 int16 rssi, rssi_node;
7231 #endif /* SORT_BSS_BY_RSSI */
7232 struct osl_timespec now, timeout;
7233
7234 if (!ss_list->count)
7235 return;
7236
7237 osl_do_gettimeofday(&now);
7238 timeout.tv_sec = now.tv_sec + BSSCACHE_TIMEOUT;
7239 if (timeout.tv_sec < now.tv_sec) {
7240 /*
7241 * Integer overflow - assume long enough timeout to be assumed
7242 * to be infinite, i.e., the timeout would never happen.
7243 */
7244 AEXT_TRACE("wlan",
7245 "Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu\n",
7246 BSSCACHE_TIMEOUT, now.tv_sec, timeout.tv_sec);
7247 }
7248
7249 bss_head = &bss_cache_ctrl->m_cache_head;
7250
7251 for (i=0; i < ss_list->count; i++) {
7252 node = *bss_head;
7253 prev = NULL;
7254 bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info;
7255
7256 for (;node;) {
7257 if (!memcmp(&node->results.bss_info->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) {
7258 if (node == *bss_head)
7259 *bss_head = node->next;
7260 else {
7261 prev->next = node->next;
7262 }
7263 break;
7264 }
7265 prev = node;
7266 node = node->next;
7267 }
7268
7269 leaf = kmalloc(dtoh32(bi->length) + sizeof(wl_bss_cache_t), GFP_KERNEL);
7270 if (!leaf) {
7271 AEXT_ERROR("wlan", "Memory alloc failure %d\n",
7272 dtoh32(bi->length) + (int)sizeof(wl_bss_cache_t));
7273 return;
7274 }
7275 if (node) {
7276 kfree(node);
7277 node = NULL;
7278 AEXT_TRACE("wlan",
7279 "Update %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
7280 k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
7281 } else
7282 AEXT_TRACE("wlan",
7283 "Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
7284 k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
7285
7286 memcpy(leaf->results.bss_info, bi, dtoh32(bi->length));
7287 leaf->next = NULL;
7288 leaf->dirty = 0;
7289 leaf->tv = timeout;
7290 leaf->results.count = 1;
7291 leaf->results.version = ss_list->version;
7292 k++;
7293
7294 if (*bss_head == NULL)
7295 *bss_head = leaf;
7296 else {
7297 #if defined(SORT_BSS_BY_RSSI)
7298 node = *bss_head;
7299 #if defined(RSSIAVG)
7300 rssi = wl_get_avg_rssi(rssi_cache_ctrl, &leaf->results.bss_info->BSSID);
7301 #else
7302 rssi = dtoh16(leaf->results.bss_info->RSSI);
7303 #endif /* RSSIAVG */
7304 for (;node;) {
7305 #if defined(RSSIAVG)
7306 rssi_node = wl_get_avg_rssi(rssi_cache_ctrl,
7307 &node->results.bss_info->BSSID);
7308 #else
7309 rssi_node = dtoh16(node->results.bss_info->RSSI);
7310 #endif /* RSSIAVG */
7311 if (rssi > rssi_node) {
7312 leaf->next = node;
7313 if (node == *bss_head)
7314 *bss_head = leaf;
7315 else
7316 prev->next = leaf;
7317 break;
7318 }
7319 prev = node;
7320 node = node->next;
7321 }
7322 if (node == NULL)
7323 prev->next = leaf;
7324 #else
7325 leaf->next = *bss_head;
7326 *bss_head = leaf;
7327 #endif /* SORT_BSS_BY_RSSI */
7328 }
7329 }
7330 dump_bss_cache(
7331 #if defined(RSSIAVG)
7332 rssi_cache_ctrl,
7333 #endif /* RSSIAVG */
7334 *bss_head);
7335 }
7336
7337 void
7338 wl_release_bss_cache_ctrl(wl_bss_cache_ctrl_t *bss_cache_ctrl)
7339 {
7340 AEXT_TRACE("wlan", "Enter\n");
7341 wl_free_bss_cache(bss_cache_ctrl);
7342 }
7343 #endif /* BSSCACHE */
7344
7345