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