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