1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3 * Broadcom Dongle Host Driver (DHD)
4 * Prefered Network Offload and Wi-Fi Location Service(WLS) code.
5 *
6 * Copyright (C) 1999-2019, Broadcom.
7 *
8 * Unless you and Broadcom execute a separate written software license
9 * agreement governing use of this software, this software is licensed to you
10 * under the terms of the GNU General Public License version 2 (the "GPL"),
11 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
12 * following added to such license:
13 *
14 * As a special exception, the copyright holders of this software give you
15 * permission to link this software with independent modules, and to copy and
16 * distribute the resulting executable under terms of your choice, provided that
17 * you also meet, for each linked independent module, the terms and conditions of
18 * the license of that module. An independent module is a module which is not
19 * derived from this software. The special exception does not apply to any
20 * modifications of the software.
21 *
22 * Notwithstanding the above, under no circumstances may you combine this
23 * software in any way with any other Broadcom software provided under a license
24 * other than the GPL, without Broadcom's express prior written consent.
25 *
26 *
27 * <<Broadcom-WL-IPTag/Open:>>
28 *
29 * $Id: dhd_pno.c 812762 2019-04-02 09:36:26Z $
30 */
31
32 #if defined(GSCAN_SUPPORT) && !defined(PNO_SUPPORT)
33 #error "GSCAN needs PNO to be enabled!"
34 #endif // endif
35
36 #ifdef PNO_SUPPORT
37 #include <typedefs.h>
38 #include <osl.h>
39
40 #include <epivers.h>
41 #include <bcmutils.h>
42
43 #include <bcmendian.h>
44 #include <linuxver.h>
45 #include <linux/init.h>
46 #include <linux/kernel.h>
47 #include <linux/list.h>
48 #include <linux/sort.h>
49 #include <dngl_stats.h>
50 #include <wlioctl.h>
51
52 #include <bcmevent.h>
53 #include <dhd.h>
54 #include <dhd_pno.h>
55 #include <dhd_dbg.h>
56 #ifdef GSCAN_SUPPORT
57 #include <linux/gcd.h>
58 #endif /* GSCAN_SUPPORT */
59 #ifdef WL_CFG80211
60 #include <wl_cfg80211.h>
61 #endif /* WL_CFG80211 */
62
63 #ifdef __BIG_ENDIAN
64 #include <bcmendian.h>
65 #define htod32(i) (bcmswap32(i))
66 #define htod16(i) (bcmswap16(i))
67 #define dtoh32(i) (bcmswap32(i))
68 #define dtoh16(i) (bcmswap16(i))
69 #define htodchanspec(i) htod16(i)
70 #define dtohchanspec(i) dtoh16(i)
71 #else
72 #define htod32(i) (i)
73 #define htod16(i) (i)
74 #define dtoh32(i) (i)
75 #define dtoh16(i) (i)
76 #define htodchanspec(i) (i)
77 #define dtohchanspec(i) (i)
78 #endif /* IL_BIGENDINA */
79
80 #define NULL_CHECK(p, s, err) \
81 do { \
82 if (!(p)) { \
83 printf("NULL POINTER (%s) : %s\n", __FUNCTION__, (s)); \
84 err = BCME_ERROR; \
85 return err; \
86 } \
87 } while (0)
88 #define PNO_GET_PNOSTATE(dhd) ((dhd_pno_status_info_t *)dhd->pno_state)
89
90 #define PNO_BESTNET_LEN WLC_IOCTL_MEDLEN
91
92 #define PNO_ON 1
93 #define PNO_OFF 0
94 #define CHANNEL_2G_MIN 1
95 #define CHANNEL_2G_MAX 14
96 #define CHANNEL_5G_MIN 34
97 #define CHANNEL_5G_MAX 165
98 #define IS_2G_CHANNEL(ch) ((ch >= CHANNEL_2G_MIN) && \
99 (ch <= CHANNEL_2G_MAX))
100 #define IS_5G_CHANNEL(ch) ((ch >= CHANNEL_5G_MIN) && \
101 (ch <= CHANNEL_5G_MAX))
102 #define MAX_NODE_CNT 5
103 #define WLS_SUPPORTED(pno_state) (pno_state->wls_supported == TRUE)
104 #define TIME_DIFF(timestamp1, timestamp2) (abs((uint32)(timestamp1/1000) \
105 - (uint32)(timestamp2/1000)))
106 #define TIME_DIFF_MS(timestamp1, timestamp2) (abs((uint32)(timestamp1) \
107 - (uint32)(timestamp2)))
108 #define TIMESPEC_TO_US(ts) (((uint64)(ts).tv_sec * USEC_PER_SEC) + \
109 (ts).tv_nsec / NSEC_PER_USEC)
110
111 #define ENTRY_OVERHEAD strlen("bssid=\nssid=\nfreq=\nlevel=\nage=\ndist=\ndistSd=\n====")
112 #define TIME_MIN_DIFF 5
113
114 #define EVENT_DATABUF_MAXLEN (512 - sizeof(bcm_event_t))
115 #define EVENT_MAX_NETCNT_V1 \
116 ((EVENT_DATABUF_MAXLEN - sizeof(wl_pfn_scanresults_v1_t)) \
117 / sizeof(wl_pfn_net_info_v1_t) + 1)
118 #define EVENT_MAX_NETCNT_V2 \
119 ((EVENT_DATABUF_MAXLEN - sizeof(wl_pfn_scanresults_v2_t)) \
120 / sizeof(wl_pfn_net_info_v2_t) + 1)
121
122 #ifdef GSCAN_SUPPORT
123 static int _dhd_pno_flush_ssid(dhd_pub_t *dhd);
124 static wl_pfn_gscan_ch_bucket_cfg_t *
125 dhd_pno_gscan_create_channel_list(dhd_pub_t *dhd, dhd_pno_status_info_t *pno_state,
126 uint16 *chan_list, uint32 *num_buckets, uint32 *num_buckets_to_fw);
127 #endif /* GSCAN_SUPPORT */
128
129 static int dhd_pno_set_legacy_pno(dhd_pub_t *dhd, uint16 scan_fr, int pno_repeat,
130 int pno_freq_expo_max, uint16 *channel_list, int nchan);
131
132 static inline bool
is_dfs(dhd_pub_t * dhd,uint16 channel)133 is_dfs(dhd_pub_t *dhd, uint16 channel)
134 {
135 u32 ch;
136 s32 err;
137 u8 buf[32];
138
139 ch = wl_ch_host_to_driver(channel);
140 err = dhd_iovar(dhd, 0, "per_chan_info", (char *)&ch,
141 sizeof(u32), buf, sizeof(buf), FALSE);
142 if (unlikely(err)) {
143 DHD_ERROR(("get per chan info failed:%d\n", err));
144 return FALSE;
145 }
146 /* Check the channel flags returned by fw */
147 if (*((u32 *)buf) & WL_CHAN_PASSIVE) {
148 return TRUE;
149 }
150 return FALSE;
151 }
152
153 int
dhd_pno_clean(dhd_pub_t * dhd)154 dhd_pno_clean(dhd_pub_t *dhd)
155 {
156 int pfn = 0;
157 int err;
158 dhd_pno_status_info_t *_pno_state;
159 NULL_CHECK(dhd, "dhd is NULL", err);
160 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
161 _pno_state = PNO_GET_PNOSTATE(dhd);
162 DHD_PNO(("%s enter\n", __FUNCTION__));
163 /* Disable PNO */
164 err = dhd_iovar(dhd, 0, "pfn", (char *)&pfn, sizeof(pfn), NULL, 0, TRUE);
165 if (err < 0) {
166 DHD_ERROR(("%s : failed to execute pfn(error : %d)\n",
167 __FUNCTION__, err));
168 goto exit;
169 }
170 _pno_state->pno_status = DHD_PNO_DISABLED;
171 err = dhd_iovar(dhd, 0, "pfnclear", NULL, 0, NULL, 0, TRUE);
172 if (err < 0) {
173 DHD_ERROR(("%s : failed to execute pfnclear(error : %d)\n",
174 __FUNCTION__, err));
175 }
176 exit:
177 return err;
178 }
179
180 bool
dhd_is_pno_supported(dhd_pub_t * dhd)181 dhd_is_pno_supported(dhd_pub_t *dhd)
182 {
183 dhd_pno_status_info_t *_pno_state;
184
185 if (!dhd || !dhd->pno_state) {
186 DHD_ERROR(("NULL POINTER : %s\n",
187 __FUNCTION__));
188 return FALSE;
189 }
190 _pno_state = PNO_GET_PNOSTATE(dhd);
191 return WLS_SUPPORTED(_pno_state);
192 }
193
194 bool
dhd_is_legacy_pno_enabled(dhd_pub_t * dhd)195 dhd_is_legacy_pno_enabled(dhd_pub_t *dhd)
196 {
197 dhd_pno_status_info_t *_pno_state;
198
199 if (!dhd || !dhd->pno_state) {
200 DHD_ERROR(("NULL POINTER : %s\n",
201 __FUNCTION__));
202 return FALSE;
203 }
204 _pno_state = PNO_GET_PNOSTATE(dhd);
205 return ((_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) != 0);
206 }
207
208 #ifdef GSCAN_SUPPORT
209 static uint64
convert_fw_rel_time_to_systime(struct osl_timespec * ts,uint32 fw_ts_ms)210 convert_fw_rel_time_to_systime(struct osl_timespec *ts, uint32 fw_ts_ms)
211 {
212 return ((uint64)(TIMESPEC_TO_US(*ts)) - (uint64)(fw_ts_ms * 1000));
213 }
214
215 static void
dhd_pno_idx_to_ssid(struct dhd_pno_gscan_params * gscan_params,dhd_epno_results_t * res,uint32 idx)216 dhd_pno_idx_to_ssid(struct dhd_pno_gscan_params *gscan_params,
217 dhd_epno_results_t *res, uint32 idx)
218 {
219 dhd_pno_ssid_t *iter, *next;
220 int i;
221
222 /* If idx doesn't make sense */
223 if (idx >= gscan_params->epno_cfg.num_epno_ssid) {
224 DHD_ERROR(("No match, idx %d num_ssid %d\n", idx,
225 gscan_params->epno_cfg.num_epno_ssid));
226 goto exit;
227 }
228
229 if (gscan_params->epno_cfg.num_epno_ssid > 0) {
230 i = 0;
231
232 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
233 list_for_each_entry_safe(iter, next,
234 &gscan_params->epno_cfg.epno_ssid_list, list) {
235 GCC_DIAGNOSTIC_POP();
236 if (i++ == idx) {
237 memcpy(res->ssid, iter->SSID, iter->SSID_len);
238 res->ssid_len = iter->SSID_len;
239 return;
240 }
241 }
242 }
243 exit:
244 /* If we are here then there was no match */
245 res->ssid[0] = '\0';
246 res->ssid_len = 0;
247 return;
248 }
249
250 /* Translate HAL flag bitmask to BRCM FW flag bitmask */
251 void
dhd_pno_translate_epno_fw_flags(uint32 * flags)252 dhd_pno_translate_epno_fw_flags(uint32 *flags)
253 {
254 uint32 in_flags, fw_flags = 0;
255 in_flags = *flags;
256
257 if (in_flags & DHD_EPNO_A_BAND_TRIG) {
258 fw_flags |= WL_PFN_SSID_A_BAND_TRIG;
259 }
260
261 if (in_flags & DHD_EPNO_BG_BAND_TRIG) {
262 fw_flags |= WL_PFN_SSID_BG_BAND_TRIG;
263 }
264
265 if (!(in_flags & DHD_EPNO_STRICT_MATCH) &&
266 !(in_flags & DHD_EPNO_HIDDEN_SSID)) {
267 fw_flags |= WL_PFN_SSID_IMPRECISE_MATCH;
268 }
269
270 if (in_flags & DHD_EPNO_SAME_NETWORK) {
271 fw_flags |= WL_PFN_SSID_SAME_NETWORK;
272 }
273
274 /* Add any hard coded flags needed */
275 fw_flags |= WL_PFN_SUPPRESS_AGING_MASK;
276 *flags = fw_flags;
277
278 return;
279 }
280
281 /* Translate HAL auth bitmask to BRCM FW bitmask */
282 void
dhd_pno_set_epno_auth_flag(uint32 * wpa_auth)283 dhd_pno_set_epno_auth_flag(uint32 *wpa_auth)
284 {
285 switch (*wpa_auth) {
286 case DHD_PNO_AUTH_CODE_OPEN:
287 *wpa_auth = WPA_AUTH_DISABLED;
288 break;
289 case DHD_PNO_AUTH_CODE_PSK:
290 *wpa_auth = (WPA_AUTH_PSK | WPA2_AUTH_PSK);
291 break;
292 case DHD_PNO_AUTH_CODE_EAPOL:
293 *wpa_auth = ~WPA_AUTH_NONE;
294 break;
295 default:
296 DHD_ERROR(("%s: Unknown auth %d", __FUNCTION__, *wpa_auth));
297 *wpa_auth = WPA_AUTH_PFN_ANY;
298 break;
299 }
300 return;
301 }
302
303 /* Cleanup all results */
304 static void
dhd_gscan_clear_all_batch_results(dhd_pub_t * dhd)305 dhd_gscan_clear_all_batch_results(dhd_pub_t *dhd)
306 {
307 struct dhd_pno_gscan_params *gscan_params;
308 dhd_pno_status_info_t *_pno_state;
309 gscan_results_cache_t *iter;
310
311 _pno_state = PNO_GET_PNOSTATE(dhd);
312 gscan_params = &_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS].params_gscan;
313 iter = gscan_params->gscan_batch_cache;
314 /* Mark everything as consumed */
315 while (iter) {
316 iter->tot_consumed = iter->tot_count;
317 iter = iter->next;
318 }
319 dhd_gscan_batch_cache_cleanup(dhd);
320 return;
321 }
322
323 static int
_dhd_pno_gscan_cfg(dhd_pub_t * dhd,wl_pfn_gscan_cfg_t * pfncfg_gscan_param,int size)324 _dhd_pno_gscan_cfg(dhd_pub_t *dhd, wl_pfn_gscan_cfg_t *pfncfg_gscan_param, int size)
325 {
326 int err = BCME_OK;
327 NULL_CHECK(dhd, "dhd is NULL", err);
328
329 DHD_PNO(("%s enter\n", __FUNCTION__));
330
331 err = dhd_iovar(dhd, 0, "pfn_gscan_cfg", (char *)pfncfg_gscan_param, size, NULL, 0, TRUE);
332 if (err < 0) {
333 DHD_ERROR(("%s : failed to execute pfncfg_gscan_param\n", __FUNCTION__));
334 goto exit;
335 }
336 exit:
337 return err;
338 }
339
340 static int
_dhd_pno_flush_ssid(dhd_pub_t * dhd)341 _dhd_pno_flush_ssid(dhd_pub_t *dhd)
342 {
343 int err;
344 wl_pfn_t pfn_elem;
345 memset(&pfn_elem, 0, sizeof(wl_pfn_t));
346 pfn_elem.flags = htod32(WL_PFN_FLUSH_ALL_SSIDS);
347
348 err = dhd_iovar(dhd, 0, "pfn_add", (char *)&pfn_elem, sizeof(wl_pfn_t), NULL, 0, TRUE);
349 if (err < 0) {
350 DHD_ERROR(("%s : failed to execute pfn_add\n", __FUNCTION__));
351 }
352 return err;
353 }
354
355 static bool
is_batch_retrieval_complete(struct dhd_pno_gscan_params * gscan_params)356 is_batch_retrieval_complete(struct dhd_pno_gscan_params *gscan_params)
357 {
358 smp_rmb();
359 return (gscan_params->get_batch_flag == GSCAN_BATCH_RETRIEVAL_COMPLETE);
360 }
361 #endif /* GSCAN_SUPPORT */
362
363 static int
_dhd_pno_suspend(dhd_pub_t * dhd)364 _dhd_pno_suspend(dhd_pub_t *dhd)
365 {
366 int err;
367 int suspend = 1;
368 dhd_pno_status_info_t *_pno_state;
369 NULL_CHECK(dhd, "dhd is NULL", err);
370 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
371
372 DHD_PNO(("%s enter\n", __FUNCTION__));
373 _pno_state = PNO_GET_PNOSTATE(dhd);
374 err = dhd_iovar(dhd, 0, "pfn_suspend", (char *)&suspend, sizeof(suspend), NULL, 0, TRUE);
375 if (err < 0) {
376 DHD_ERROR(("%s : failed to suspend pfn(error :%d)\n", __FUNCTION__, err));
377 goto exit;
378
379 }
380 _pno_state->pno_status = DHD_PNO_SUSPEND;
381 exit:
382 return err;
383 }
384 static int
_dhd_pno_enable(dhd_pub_t * dhd,int enable)385 _dhd_pno_enable(dhd_pub_t *dhd, int enable)
386 {
387 int err = BCME_OK;
388 dhd_pno_status_info_t *_pno_state;
389 NULL_CHECK(dhd, "dhd is NULL", err);
390 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
391 _pno_state = PNO_GET_PNOSTATE(dhd);
392 DHD_PNO(("%s enter\n", __FUNCTION__));
393
394 if (enable & 0xfffe) {
395 DHD_ERROR(("%s invalid value\n", __FUNCTION__));
396 err = BCME_BADARG;
397 goto exit;
398 }
399 if (!dhd_support_sta_mode(dhd)) {
400 DHD_ERROR(("PNO is not allowed for non-STA mode"));
401 err = BCME_BADOPTION;
402 goto exit;
403 }
404 if (enable) {
405 if ((_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) &&
406 dhd_is_associated(dhd, 0, NULL)) {
407 DHD_ERROR(("%s Legacy PNO mode cannot be enabled "
408 "in assoc mode , ignore it\n", __FUNCTION__));
409 err = BCME_BADOPTION;
410 goto exit;
411 }
412 }
413 /* Enable/Disable PNO */
414 err = dhd_iovar(dhd, 0, "pfn", (char *)&enable, sizeof(enable), NULL, 0, TRUE);
415 if (err < 0) {
416 DHD_ERROR(("%s : failed to execute pfn_set - %d\n", __FUNCTION__, err));
417 goto exit;
418 }
419 _pno_state->pno_status = (enable)?
420 DHD_PNO_ENABLED : DHD_PNO_DISABLED;
421 if (!enable)
422 _pno_state->pno_mode = DHD_PNO_NONE_MODE;
423
424 DHD_PNO(("%s set pno as %s\n",
425 __FUNCTION__, enable ? "Enable" : "Disable"));
426 exit:
427 return err;
428 }
429
430 static int
_dhd_pno_set(dhd_pub_t * dhd,const dhd_pno_params_t * pno_params,dhd_pno_mode_t mode)431 _dhd_pno_set(dhd_pub_t *dhd, const dhd_pno_params_t *pno_params, dhd_pno_mode_t mode)
432 {
433 int err = BCME_OK;
434 wl_pfn_param_t pfn_param;
435 dhd_pno_params_t *_params;
436 dhd_pno_status_info_t *_pno_state;
437 bool combined_scan = FALSE;
438 DHD_PNO(("%s enter\n", __FUNCTION__));
439
440 NULL_CHECK(dhd, "dhd is NULL", err);
441 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
442 _pno_state = PNO_GET_PNOSTATE(dhd);
443
444 memset(&pfn_param, 0, sizeof(pfn_param));
445
446 /* set pfn parameters */
447 pfn_param.version = htod32(PFN_VERSION);
448 pfn_param.flags = ((PFN_LIST_ORDER << SORT_CRITERIA_BIT) |
449 (ENABLE << IMMEDIATE_SCAN_BIT) | (ENABLE << REPORT_SEPERATELY_BIT));
450 if (mode == DHD_PNO_LEGACY_MODE) {
451 /* check and set extra pno params */
452 if ((pno_params->params_legacy.pno_repeat != 0) ||
453 (pno_params->params_legacy.pno_freq_expo_max != 0)) {
454 pfn_param.flags |= htod16(ENABLE << ENABLE_ADAPTSCAN_BIT);
455 pfn_param.repeat = (uchar) (pno_params->params_legacy.pno_repeat);
456 pfn_param.exp = (uchar) (pno_params->params_legacy.pno_freq_expo_max);
457 }
458 /* set up pno scan fr */
459 if (pno_params->params_legacy.scan_fr != 0)
460 pfn_param.scan_freq = htod32(pno_params->params_legacy.scan_fr);
461 if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
462 DHD_PNO(("will enable combined scan with BATCHIG SCAN MODE\n"));
463 mode |= DHD_PNO_BATCH_MODE;
464 combined_scan = TRUE;
465 } else if (_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE) {
466 DHD_PNO(("will enable combined scan with HOTLIST SCAN MODE\n"));
467 mode |= DHD_PNO_HOTLIST_MODE;
468 combined_scan = TRUE;
469 }
470 #ifdef GSCAN_SUPPORT
471 else if (_pno_state->pno_mode & DHD_PNO_GSCAN_MODE) {
472 DHD_PNO(("will enable combined scan with GSCAN SCAN MODE\n"));
473 mode |= DHD_PNO_GSCAN_MODE;
474 }
475 #endif /* GSCAN_SUPPORT */
476 }
477 if (mode & (DHD_PNO_BATCH_MODE | DHD_PNO_HOTLIST_MODE)) {
478 /* Scan frequency of 30 sec */
479 pfn_param.scan_freq = htod32(30);
480 /* slow adapt scan is off by default */
481 pfn_param.slow_freq = htod32(0);
482 /* RSSI margin of 30 dBm */
483 pfn_param.rssi_margin = htod16(PNO_RSSI_MARGIN_DBM);
484 /* Network timeout 60 sec */
485 pfn_param.lost_network_timeout = htod32(60);
486 /* best n = 2 by default */
487 pfn_param.bestn = DEFAULT_BESTN;
488 /* mscan m=0 by default, so not record best networks by default */
489 pfn_param.mscan = DEFAULT_MSCAN;
490 /* default repeat = 10 */
491 pfn_param.repeat = DEFAULT_REPEAT;
492 /* by default, maximum scan interval = 2^2
493 * scan_freq when adaptive scan is turned on
494 */
495 pfn_param.exp = DEFAULT_EXP;
496 if (mode == DHD_PNO_BATCH_MODE) {
497 /* In case of BATCH SCAN */
498 if (pno_params->params_batch.bestn)
499 pfn_param.bestn = pno_params->params_batch.bestn;
500 if (pno_params->params_batch.scan_fr)
501 pfn_param.scan_freq = htod32(pno_params->params_batch.scan_fr);
502 if (pno_params->params_batch.mscan)
503 pfn_param.mscan = pno_params->params_batch.mscan;
504 /* enable broadcast scan */
505 pfn_param.flags |= (ENABLE << ENABLE_BD_SCAN_BIT);
506 } else if (mode == DHD_PNO_HOTLIST_MODE) {
507 /* In case of HOTLIST SCAN */
508 if (pno_params->params_hotlist.scan_fr)
509 pfn_param.scan_freq = htod32(pno_params->params_hotlist.scan_fr);
510 pfn_param.bestn = 0;
511 pfn_param.repeat = 0;
512 /* enable broadcast scan */
513 pfn_param.flags |= (ENABLE << ENABLE_BD_SCAN_BIT);
514 }
515 if (combined_scan) {
516 /* Disable Adaptive Scan */
517 pfn_param.flags &= ~(htod16(ENABLE << ENABLE_ADAPTSCAN_BIT));
518 pfn_param.flags |= (ENABLE << ENABLE_BD_SCAN_BIT);
519 pfn_param.repeat = 0;
520 pfn_param.exp = 0;
521 if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
522 /* In case of Legacy PNO + BATCH SCAN */
523 _params = &(_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS]);
524 if (_params->params_batch.bestn)
525 pfn_param.bestn = _params->params_batch.bestn;
526 if (_params->params_batch.scan_fr)
527 pfn_param.scan_freq = htod32(_params->params_batch.scan_fr);
528 if (_params->params_batch.mscan)
529 pfn_param.mscan = _params->params_batch.mscan;
530 } else if (_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE) {
531 /* In case of Legacy PNO + HOTLIST SCAN */
532 _params = &(_pno_state->pno_params_arr[INDEX_OF_HOTLIST_PARAMS]);
533 if (_params->params_hotlist.scan_fr)
534 pfn_param.scan_freq = htod32(_params->params_hotlist.scan_fr);
535 pfn_param.bestn = 0;
536 pfn_param.repeat = 0;
537 }
538 }
539 }
540 #ifdef GSCAN_SUPPORT
541 if (mode & DHD_PNO_GSCAN_MODE) {
542 uint32 lost_network_timeout;
543
544 pfn_param.scan_freq = htod32(pno_params->params_gscan.scan_fr);
545 if (pno_params->params_gscan.mscan) {
546 pfn_param.bestn = pno_params->params_gscan.bestn;
547 pfn_param.mscan = pno_params->params_gscan.mscan;
548 pfn_param.flags |= (ENABLE << ENABLE_BD_SCAN_BIT);
549 }
550 /* RSSI margin of 30 dBm */
551 pfn_param.rssi_margin = htod16(PNO_RSSI_MARGIN_DBM);
552 pfn_param.repeat = 0;
553 pfn_param.exp = 0;
554 pfn_param.slow_freq = 0;
555 pfn_param.flags |= htod16(ENABLE << ENABLE_ADAPTSCAN_BIT);
556
557 if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
558 dhd_pno_params_t *params;
559
560 params = &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS]);
561
562 pfn_param.scan_freq = gcd(pno_params->params_gscan.scan_fr,
563 params->params_legacy.scan_fr);
564
565 if ((params->params_legacy.pno_repeat != 0) ||
566 (params->params_legacy.pno_freq_expo_max != 0)) {
567 pfn_param.repeat = (uchar) (params->params_legacy.pno_repeat);
568 pfn_param.exp = (uchar) (params->params_legacy.pno_freq_expo_max);
569 }
570 }
571
572 lost_network_timeout = (pno_params->params_gscan.max_ch_bucket_freq *
573 pfn_param.scan_freq *
574 pno_params->params_gscan.lost_ap_window);
575 if (lost_network_timeout) {
576 pfn_param.lost_network_timeout = htod32(MIN(lost_network_timeout,
577 GSCAN_MIN_BSSID_TIMEOUT));
578 } else {
579 pfn_param.lost_network_timeout = htod32(GSCAN_MIN_BSSID_TIMEOUT);
580 }
581 } else
582 #endif /* GSCAN_SUPPORT */
583 {
584 if (pfn_param.scan_freq < htod32(PNO_SCAN_MIN_FW_SEC) ||
585 pfn_param.scan_freq > htod32(PNO_SCAN_MAX_FW_SEC)) {
586 DHD_ERROR(("%s pno freq(%d sec) is not valid \n",
587 __FUNCTION__, PNO_SCAN_MIN_FW_SEC));
588 err = BCME_BADARG;
589 goto exit;
590 }
591 }
592
593 err = dhd_set_rand_mac_oui(dhd);
594 /* Ignore if chip doesnt support the feature */
595 if (err < 0 && err != BCME_UNSUPPORTED) {
596 DHD_ERROR(("%s : failed to set random mac for PNO scan, %d\n", __FUNCTION__, err));
597 goto exit;
598 }
599
600 #ifdef GSCAN_SUPPORT
601 if (mode == DHD_PNO_BATCH_MODE ||
602 ((mode & DHD_PNO_GSCAN_MODE) && pno_params->params_gscan.mscan))
603 #else
604 if (mode == DHD_PNO_BATCH_MODE)
605 #endif /* GSCAN_SUPPORT */
606 {
607 int _tmp = pfn_param.bestn;
608 /* set bestn to calculate the max mscan which firmware supports */
609 err = dhd_iovar(dhd, 0, "pfnmem", (char *)&_tmp, sizeof(_tmp), NULL, 0, TRUE);
610 if (err < 0) {
611 DHD_ERROR(("%s : failed to set pfnmem\n", __FUNCTION__));
612 goto exit;
613 }
614 /* get max mscan which the firmware supports */
615 err = dhd_iovar(dhd, 0, "pfnmem", NULL, 0, (char *)&_tmp, sizeof(_tmp), FALSE);
616 if (err < 0) {
617 DHD_ERROR(("%s : failed to get pfnmem\n", __FUNCTION__));
618 goto exit;
619 }
620 pfn_param.mscan = MIN(pfn_param.mscan, _tmp);
621 DHD_PNO((" returned mscan : %d, set bestn : %d mscan %d\n", _tmp, pfn_param.bestn,
622 pfn_param.mscan));
623 }
624 err = dhd_iovar(dhd, 0, "pfn_set", (char *)&pfn_param, sizeof(pfn_param), NULL, 0, TRUE);
625 if (err < 0) {
626 DHD_ERROR(("%s : failed to execute pfn_set %d\n", __FUNCTION__, err));
627 goto exit;
628 }
629 /* need to return mscan if this is for batch scan instead of err */
630 err = (mode == DHD_PNO_BATCH_MODE)? pfn_param.mscan : err;
631 exit:
632 return err;
633 }
634
635 static int
_dhd_pno_add_ssid(dhd_pub_t * dhd,struct list_head * ssid_list,int nssid)636 _dhd_pno_add_ssid(dhd_pub_t *dhd, struct list_head* ssid_list, int nssid)
637 {
638 int err = BCME_OK;
639 int i = 0, mem_needed;
640 wl_pfn_t *pfn_elem_buf;
641 struct dhd_pno_ssid *iter, *next;
642
643 NULL_CHECK(dhd, "dhd is NULL", err);
644 if (!nssid) {
645 NULL_CHECK(ssid_list, "ssid list is NULL", err);
646 return BCME_ERROR;
647 }
648 mem_needed = (sizeof(wl_pfn_t) * nssid);
649 pfn_elem_buf = (wl_pfn_t *) MALLOCZ(dhd->osh, mem_needed);
650 if (!pfn_elem_buf) {
651 DHD_ERROR(("%s: Can't malloc %d bytes!\n", __FUNCTION__, mem_needed));
652 return BCME_NOMEM;
653 }
654
655 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
656 list_for_each_entry_safe(iter, next, ssid_list, list) {
657 GCC_DIAGNOSTIC_POP();
658 pfn_elem_buf[i].infra = htod32(1);
659 pfn_elem_buf[i].auth = htod32(DOT11_OPEN_SYSTEM);
660 pfn_elem_buf[i].wpa_auth = htod32(iter->wpa_auth);
661 pfn_elem_buf[i].flags = htod32(iter->flags);
662 if (iter->hidden)
663 pfn_elem_buf[i].flags |= htod32(ENABLE << WL_PFN_HIDDEN_BIT);
664 /* If a single RSSI threshold is defined, use that */
665 #ifdef PNO_MIN_RSSI_TRIGGER
666 pfn_elem_buf[i].flags |= ((PNO_MIN_RSSI_TRIGGER & 0xFF) << WL_PFN_RSSI_SHIFT);
667 #else
668 pfn_elem_buf[i].flags |= ((iter->rssi_thresh & 0xFF) << WL_PFN_RSSI_SHIFT);
669 #endif /* PNO_MIN_RSSI_TRIGGER */
670 memcpy((char *)pfn_elem_buf[i].ssid.SSID, iter->SSID,
671 iter->SSID_len);
672 pfn_elem_buf[i].ssid.SSID_len = iter->SSID_len;
673 DHD_PNO(("%s size = %d hidden = %d flags = %x rssi_thresh %d\n",
674 iter->SSID, iter->SSID_len, iter->hidden,
675 iter->flags, iter->rssi_thresh));
676 if (++i >= nssid) {
677 /* shouldn't happen */
678 break;
679 }
680 }
681
682 err = dhd_iovar(dhd, 0, "pfn_add", (char *)pfn_elem_buf, mem_needed, NULL, 0, TRUE);
683 if (err < 0) {
684 DHD_ERROR(("%s : failed to execute pfn_add\n", __FUNCTION__));
685 }
686 MFREE(dhd->osh, pfn_elem_buf, mem_needed);
687 return err;
688 }
689
690 /* qsort compare function */
691 static int
_dhd_pno_cmpfunc(const void * a,const void * b)692 _dhd_pno_cmpfunc(const void *a, const void *b)
693 {
694 return (*(const uint16*)a - *(const uint16*)b);
695 }
696
697 static int
_dhd_pno_chan_merge(uint16 * d_chan_list,int * nchan,uint16 * chan_list1,int nchan1,uint16 * chan_list2,int nchan2)698 _dhd_pno_chan_merge(uint16 *d_chan_list, int *nchan,
699 uint16 *chan_list1, int nchan1, uint16 *chan_list2, int nchan2)
700 {
701 int err = BCME_OK;
702 int i = 0, j = 0, k = 0;
703 uint16 tmp;
704 NULL_CHECK(d_chan_list, "d_chan_list is NULL", err);
705 NULL_CHECK(nchan, "nchan is NULL", err);
706 NULL_CHECK(chan_list1, "chan_list1 is NULL", err);
707 NULL_CHECK(chan_list2, "chan_list2 is NULL", err);
708 /* chan_list1 and chan_list2 should be sorted at first */
709 while (i < nchan1 && j < nchan2) {
710 tmp = chan_list1[i] < chan_list2[j]?
711 chan_list1[i++] : chan_list2[j++];
712 for (; i < nchan1 && chan_list1[i] == tmp; i++);
713 for (; j < nchan2 && chan_list2[j] == tmp; j++);
714 d_chan_list[k++] = tmp;
715 }
716
717 while (i < nchan1) {
718 tmp = chan_list1[i++];
719 for (; i < nchan1 && chan_list1[i] == tmp; i++);
720 d_chan_list[k++] = tmp;
721 }
722
723 while (j < nchan2) {
724 tmp = chan_list2[j++];
725 for (; j < nchan2 && chan_list2[j] == tmp; j++);
726 d_chan_list[k++] = tmp;
727
728 }
729 *nchan = k;
730 return err;
731 }
732
733 static int
_dhd_pno_get_channels(dhd_pub_t * dhd,uint16 * d_chan_list,int * nchan,uint8 band,bool skip_dfs)734 _dhd_pno_get_channels(dhd_pub_t *dhd, uint16 *d_chan_list,
735 int *nchan, uint8 band, bool skip_dfs)
736 {
737 int err = BCME_OK;
738 int i, j;
739 uint32 chan_buf[WL_NUMCHANNELS + 1];
740 wl_uint32_list_t *list;
741 NULL_CHECK(dhd, "dhd is NULL", err);
742 if (*nchan) {
743 NULL_CHECK(d_chan_list, "d_chan_list is NULL", err);
744 }
745 memset(&chan_buf, 0, sizeof(chan_buf));
746 list = (wl_uint32_list_t *) (void *)chan_buf;
747 list->count = htod32(WL_NUMCHANNELS);
748 err = dhd_wl_ioctl_cmd(dhd, WLC_GET_VALID_CHANNELS, chan_buf, sizeof(chan_buf), FALSE, 0);
749 if (err < 0) {
750 DHD_ERROR(("failed to get channel list (err: %d)\n", err));
751 return err;
752 }
753 for (i = 0, j = 0; i < dtoh32(list->count) && i < *nchan; i++) {
754 if (IS_2G_CHANNEL(dtoh32(list->element[i]))) {
755 if (!(band & WLC_BAND_2G)) {
756 /* Skip, if not 2g */
757 continue;
758 }
759 /* fall through to include the channel */
760 } else if (IS_5G_CHANNEL(dtoh32(list->element[i]))) {
761 bool dfs_channel = is_dfs(dhd, dtoh32(list->element[i]));
762 if ((skip_dfs && dfs_channel) ||
763 (!(band & WLC_BAND_5G) && !dfs_channel)) {
764 /* Skip the channel if:
765 * the DFS bit is NOT set & the channel is a dfs channel
766 * the band 5G is not set & the channel is a non DFS 5G channel
767 */
768 continue;
769 }
770 /* fall through to include the channel */
771 } else {
772 /* Not in range. Bad channel */
773 DHD_ERROR(("Not in range. bad channel\n"));
774 *nchan = 0;
775 return BCME_BADCHAN;
776 }
777
778 /* Include the channel */
779 d_chan_list[j++] = (uint16) dtoh32(list->element[i]);
780 }
781 *nchan = j;
782 return err;
783 }
784
785 static int
_dhd_pno_convert_format(dhd_pub_t * dhd,struct dhd_pno_batch_params * params_batch,char * buf,int nbufsize)786 _dhd_pno_convert_format(dhd_pub_t *dhd, struct dhd_pno_batch_params *params_batch,
787 char *buf, int nbufsize)
788 {
789 int err = BCME_OK;
790 int bytes_written = 0, nreadsize = 0;
791 int t_delta = 0;
792 int nleftsize = nbufsize;
793 uint8 cnt = 0;
794 char *bp = buf;
795 char eabuf[ETHER_ADDR_STR_LEN];
796 #ifdef PNO_DEBUG
797 char *_base_bp;
798 char msg[150];
799 #endif // endif
800 dhd_pno_bestnet_entry_t *iter, *next;
801 dhd_pno_scan_results_t *siter, *snext;
802 dhd_pno_best_header_t *phead, *pprev;
803 NULL_CHECK(params_batch, "params_batch is NULL", err);
804 if (nbufsize > 0)
805 NULL_CHECK(buf, "buf is NULL", err);
806 /* initialize the buffer */
807 memset(buf, 0, nbufsize);
808 DHD_PNO(("%s enter \n", __FUNCTION__));
809 /* # of scans */
810 if (!params_batch->get_batch.batch_started) {
811 bp += nreadsize = snprintf(bp, nleftsize, "scancount=%d\n",
812 params_batch->get_batch.expired_tot_scan_cnt);
813 nleftsize -= nreadsize;
814 params_batch->get_batch.batch_started = TRUE;
815 }
816 DHD_PNO(("%s scancount %d\n", __FUNCTION__, params_batch->get_batch.expired_tot_scan_cnt));
817 /* preestimate scan count until which scan result this report is going to end */
818 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
819 list_for_each_entry_safe(siter, snext,
820 ¶ms_batch->get_batch.expired_scan_results_list, list) {
821 GCC_DIAGNOSTIC_POP();
822 phead = siter->bestnetheader;
823 while (phead != NULL) {
824 /* if left_size is less than bestheader total size , stop this */
825 if (nleftsize <=
826 (phead->tot_size + phead->tot_cnt * ENTRY_OVERHEAD))
827 goto exit;
828 /* increase scan count */
829 cnt++;
830 /* # best of each scan */
831 DHD_PNO(("\n<loop : %d, apcount %d>\n", cnt - 1, phead->tot_cnt));
832 /* attribute of the scan */
833 if (phead->reason & PNO_STATUS_ABORT_MASK) {
834 bp += nreadsize = snprintf(bp, nleftsize, "trunc\n");
835 nleftsize -= nreadsize;
836 }
837 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
838 list_for_each_entry_safe(iter, next,
839 &phead->entry_list, list) {
840 GCC_DIAGNOSTIC_POP();
841 t_delta = jiffies_to_msecs(jiffies - iter->recorded_time);
842 #ifdef PNO_DEBUG
843 _base_bp = bp;
844 memset(msg, 0, sizeof(msg));
845 #endif // endif
846 /* BSSID info */
847 bp += nreadsize = snprintf(bp, nleftsize, "bssid=%s\n",
848 bcm_ether_ntoa((const struct ether_addr *)&iter->BSSID, eabuf));
849 nleftsize -= nreadsize;
850 /* SSID */
851 bp += nreadsize = snprintf(bp, nleftsize, "ssid=%s\n", iter->SSID);
852 nleftsize -= nreadsize;
853 /* channel */
854 bp += nreadsize = snprintf(bp, nleftsize, "freq=%d\n",
855 wf_channel2mhz(iter->channel,
856 iter->channel <= CH_MAX_2G_CHANNEL?
857 WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G));
858 nleftsize -= nreadsize;
859 /* RSSI */
860 bp += nreadsize = snprintf(bp, nleftsize, "level=%d\n", iter->RSSI);
861 nleftsize -= nreadsize;
862 /* add the time consumed in Driver to the timestamp of firmware */
863 iter->timestamp += t_delta;
864 bp += nreadsize = snprintf(bp, nleftsize,
865 "age=%d\n", iter->timestamp);
866 nleftsize -= nreadsize;
867 /* RTT0 */
868 bp += nreadsize = snprintf(bp, nleftsize, "dist=%d\n",
869 (iter->rtt0 == 0)? -1 : iter->rtt0);
870 nleftsize -= nreadsize;
871 /* RTT1 */
872 bp += nreadsize = snprintf(bp, nleftsize, "distSd=%d\n",
873 (iter->rtt0 == 0)? -1 : iter->rtt1);
874 nleftsize -= nreadsize;
875 bp += nreadsize = snprintf(bp, nleftsize, "%s", AP_END_MARKER);
876 nleftsize -= nreadsize;
877 list_del(&iter->list);
878 MFREE(dhd->osh, iter, BESTNET_ENTRY_SIZE);
879 #ifdef PNO_DEBUG
880 memcpy(msg, _base_bp, bp - _base_bp);
881 DHD_PNO(("Entry : \n%s", msg));
882 #endif // endif
883 }
884 bp += nreadsize = snprintf(bp, nleftsize, "%s", SCAN_END_MARKER);
885 DHD_PNO(("%s", SCAN_END_MARKER));
886 nleftsize -= nreadsize;
887 pprev = phead;
888 /* reset the header */
889 siter->bestnetheader = phead = phead->next;
890 MFREE(dhd->osh, pprev, BEST_HEADER_SIZE);
891
892 siter->cnt_header--;
893 }
894 if (phead == NULL) {
895 /* we store all entry in this scan , so it is ok to delete */
896 list_del(&siter->list);
897 MFREE(dhd->osh, siter, SCAN_RESULTS_SIZE);
898 }
899 }
900 exit:
901 if (cnt < params_batch->get_batch.expired_tot_scan_cnt) {
902 DHD_ERROR(("Buffer size is small to save all batch entry,"
903 " cnt : %d (remained_scan_cnt): %d\n",
904 cnt, params_batch->get_batch.expired_tot_scan_cnt - cnt));
905 }
906 params_batch->get_batch.expired_tot_scan_cnt -= cnt;
907 /* set FALSE only if the link list is empty after returning the data */
908 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
909 if (list_empty(¶ms_batch->get_batch.expired_scan_results_list)) {
910 GCC_DIAGNOSTIC_POP();
911 params_batch->get_batch.batch_started = FALSE;
912 bp += snprintf(bp, nleftsize, "%s", RESULTS_END_MARKER);
913 DHD_PNO(("%s", RESULTS_END_MARKER));
914 DHD_PNO(("%s : Getting the batching data is complete\n", __FUNCTION__));
915 }
916 /* return used memory in buffer */
917 bytes_written = (int32)(bp - buf);
918 return bytes_written;
919 }
920
921 static int
_dhd_pno_clear_all_batch_results(dhd_pub_t * dhd,struct list_head * head,bool only_last)922 _dhd_pno_clear_all_batch_results(dhd_pub_t *dhd, struct list_head *head, bool only_last)
923 {
924 int err = BCME_OK;
925 int removed_scan_cnt = 0;
926 dhd_pno_scan_results_t *siter, *snext;
927 dhd_pno_best_header_t *phead, *pprev;
928 dhd_pno_bestnet_entry_t *iter, *next;
929 NULL_CHECK(dhd, "dhd is NULL", err);
930 NULL_CHECK(head, "head is NULL", err);
931 NULL_CHECK(head->next, "head->next is NULL", err);
932 DHD_PNO(("%s enter\n", __FUNCTION__));
933
934 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
935 list_for_each_entry_safe(siter, snext,
936 head, list) {
937 if (only_last) {
938 /* in case that we need to delete only last one */
939 if (!list_is_last(&siter->list, head)) {
940 /* skip if the one is not last */
941 continue;
942 }
943 }
944 /* delete all data belong if the one is last */
945 phead = siter->bestnetheader;
946 while (phead != NULL) {
947 removed_scan_cnt++;
948 list_for_each_entry_safe(iter, next,
949 &phead->entry_list, list) {
950 list_del(&iter->list);
951 MFREE(dhd->osh, iter, BESTNET_ENTRY_SIZE);
952 }
953 pprev = phead;
954 phead = phead->next;
955 MFREE(dhd->osh, pprev, BEST_HEADER_SIZE);
956 }
957 if (phead == NULL) {
958 /* it is ok to delete top node */
959 list_del(&siter->list);
960 MFREE(dhd->osh, siter, SCAN_RESULTS_SIZE);
961 }
962 }
963 GCC_DIAGNOSTIC_POP();
964 return removed_scan_cnt;
965 }
966
967 static int
_dhd_pno_cfg(dhd_pub_t * dhd,uint16 * channel_list,int nchan)968 _dhd_pno_cfg(dhd_pub_t *dhd, uint16 *channel_list, int nchan)
969 {
970 int err = BCME_OK;
971 int i = 0;
972 wl_pfn_cfg_t pfncfg_param;
973 NULL_CHECK(dhd, "dhd is NULL", err);
974 if (nchan) {
975 NULL_CHECK(channel_list, "nchan is NULL", err);
976 }
977 if (nchan > WL_NUMCHANNELS) {
978 return BCME_RANGE;
979 }
980 DHD_PNO(("%s enter : nchan : %d\n", __FUNCTION__, nchan));
981 memset(&pfncfg_param, 0, sizeof(wl_pfn_cfg_t));
982 /* Setup default values */
983 pfncfg_param.reporttype = htod32(WL_PFN_REPORT_ALLNET);
984 pfncfg_param.channel_num = htod32(0);
985
986 for (i = 0; i < nchan; i++)
987 pfncfg_param.channel_list[i] = channel_list[i];
988
989 pfncfg_param.channel_num = htod32(nchan);
990 err = dhd_iovar(dhd, 0, "pfn_cfg", (char *)&pfncfg_param, sizeof(pfncfg_param), NULL, 0,
991 TRUE);
992 if (err < 0) {
993 DHD_ERROR(("%s : failed to execute pfn_cfg\n", __FUNCTION__));
994 goto exit;
995 }
996 exit:
997 return err;
998 }
999
1000 static int
_dhd_pno_reinitialize_prof(dhd_pub_t * dhd,dhd_pno_params_t * params,dhd_pno_mode_t mode)1001 _dhd_pno_reinitialize_prof(dhd_pub_t *dhd, dhd_pno_params_t *params, dhd_pno_mode_t mode)
1002 {
1003 int err = BCME_OK;
1004 dhd_pno_status_info_t *_pno_state;
1005 NULL_CHECK(dhd, "dhd is NULL\n", err);
1006 NULL_CHECK(dhd->pno_state, "pno_state is NULL\n", err);
1007 DHD_PNO(("%s enter\n", __FUNCTION__));
1008 _pno_state = PNO_GET_PNOSTATE(dhd);
1009 mutex_lock(&_pno_state->pno_mutex);
1010 switch (mode) {
1011 case DHD_PNO_LEGACY_MODE: {
1012 struct dhd_pno_ssid *iter, *next;
1013 if (params->params_legacy.nssid > 0) {
1014 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
1015 list_for_each_entry_safe(iter, next,
1016 ¶ms->params_legacy.ssid_list, list) {
1017 GCC_DIAGNOSTIC_POP();
1018 list_del(&iter->list);
1019 MFREE(dhd->osh, iter, sizeof(struct dhd_pno_ssid));
1020 }
1021 }
1022
1023 params->params_legacy.nssid = 0;
1024 params->params_legacy.scan_fr = 0;
1025 params->params_legacy.pno_freq_expo_max = 0;
1026 params->params_legacy.pno_repeat = 0;
1027 params->params_legacy.nchan = 0;
1028 memset(params->params_legacy.chan_list, 0,
1029 sizeof(params->params_legacy.chan_list));
1030 break;
1031 }
1032 case DHD_PNO_BATCH_MODE: {
1033 params->params_batch.scan_fr = 0;
1034 params->params_batch.mscan = 0;
1035 params->params_batch.nchan = 0;
1036 params->params_batch.rtt = 0;
1037 params->params_batch.bestn = 0;
1038 params->params_batch.nchan = 0;
1039 params->params_batch.band = WLC_BAND_AUTO;
1040 memset(params->params_batch.chan_list, 0,
1041 sizeof(params->params_batch.chan_list));
1042 params->params_batch.get_batch.batch_started = FALSE;
1043 params->params_batch.get_batch.buf = NULL;
1044 params->params_batch.get_batch.bufsize = 0;
1045 params->params_batch.get_batch.reason = 0;
1046 _dhd_pno_clear_all_batch_results(dhd,
1047 ¶ms->params_batch.get_batch.scan_results_list, FALSE);
1048 _dhd_pno_clear_all_batch_results(dhd,
1049 ¶ms->params_batch.get_batch.expired_scan_results_list, FALSE);
1050 params->params_batch.get_batch.tot_scan_cnt = 0;
1051 params->params_batch.get_batch.expired_tot_scan_cnt = 0;
1052 params->params_batch.get_batch.top_node_cnt = 0;
1053 INIT_LIST_HEAD(¶ms->params_batch.get_batch.scan_results_list);
1054 INIT_LIST_HEAD(¶ms->params_batch.get_batch.expired_scan_results_list);
1055 break;
1056 }
1057 case DHD_PNO_HOTLIST_MODE: {
1058 struct dhd_pno_bssid *iter, *next;
1059 if (params->params_hotlist.nbssid > 0) {
1060 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
1061 list_for_each_entry_safe(iter, next,
1062 ¶ms->params_hotlist.bssid_list, list) {
1063 GCC_DIAGNOSTIC_POP();
1064 list_del(&iter->list);
1065 MFREE(dhd->osh, iter, sizeof(struct dhd_pno_ssid));
1066 }
1067 }
1068 params->params_hotlist.scan_fr = 0;
1069 params->params_hotlist.nbssid = 0;
1070 params->params_hotlist.nchan = 0;
1071 params->params_batch.band = WLC_BAND_AUTO;
1072 memset(params->params_hotlist.chan_list, 0,
1073 sizeof(params->params_hotlist.chan_list));
1074 break;
1075 }
1076 default:
1077 DHD_ERROR(("%s : unknown mode : %d\n", __FUNCTION__, mode));
1078 break;
1079 }
1080 mutex_unlock(&_pno_state->pno_mutex);
1081 return err;
1082 }
1083
1084 static int
_dhd_pno_add_bssid(dhd_pub_t * dhd,wl_pfn_bssid_t * p_pfn_bssid,int nbssid)1085 _dhd_pno_add_bssid(dhd_pub_t *dhd, wl_pfn_bssid_t *p_pfn_bssid, int nbssid)
1086 {
1087 int err = BCME_OK;
1088 NULL_CHECK(dhd, "dhd is NULL", err);
1089 if (nbssid) {
1090 NULL_CHECK(p_pfn_bssid, "bssid list is NULL", err);
1091 }
1092 err = dhd_iovar(dhd, 0, "pfn_add_bssid", (char *)p_pfn_bssid,
1093 sizeof(wl_pfn_bssid_t) * nbssid, NULL, 0, TRUE);
1094 if (err < 0) {
1095 DHD_ERROR(("%s : failed to execute pfn_cfg\n", __FUNCTION__));
1096 goto exit;
1097 }
1098 exit:
1099 return err;
1100 }
1101
1102 int
dhd_pno_stop_for_ssid(dhd_pub_t * dhd)1103 dhd_pno_stop_for_ssid(dhd_pub_t *dhd)
1104 {
1105 int err = BCME_OK;
1106 uint32 mode = 0, cnt = 0;
1107 dhd_pno_status_info_t *_pno_state;
1108 dhd_pno_params_t *_params = NULL;
1109 wl_pfn_bssid_t *p_pfn_bssid = NULL, *tmp_bssid;
1110
1111 NULL_CHECK(dhd, "dev is NULL", err);
1112 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
1113 _pno_state = PNO_GET_PNOSTATE(dhd);
1114 if (!(_pno_state->pno_mode & DHD_PNO_LEGACY_MODE)) {
1115 DHD_ERROR(("%s : LEGACY PNO MODE is not enabled\n", __FUNCTION__));
1116 goto exit;
1117 }
1118 DHD_PNO(("%s enter\n", __FUNCTION__));
1119 /* If pno mode is PNO_LEGACY_MODE clear the pno values and unset the DHD_PNO_LEGACY_MODE */
1120 _params = &_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS];
1121 _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_LEGACY_MODE);
1122 _pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
1123
1124 #ifdef GSCAN_SUPPORT
1125 if (_pno_state->pno_mode & DHD_PNO_GSCAN_MODE) {
1126 struct dhd_pno_gscan_params *gscan_params;
1127
1128 _params = &_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS];
1129 gscan_params = &_params->params_gscan;
1130 if (gscan_params->mscan) {
1131 /* retrieve the batching data from firmware into host */
1132 err = dhd_wait_batch_results_complete(dhd);
1133 if (err != BCME_OK)
1134 goto exit;
1135 }
1136 /* save current pno_mode before calling dhd_pno_clean */
1137 mutex_lock(&_pno_state->pno_mutex);
1138 mode = _pno_state->pno_mode;
1139 err = dhd_pno_clean(dhd);
1140 if (err < 0) {
1141 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
1142 __FUNCTION__, err));
1143 mutex_unlock(&_pno_state->pno_mutex);
1144 goto exit;
1145 }
1146 /* restore previous pno_mode */
1147 _pno_state->pno_mode = mode;
1148 mutex_unlock(&_pno_state->pno_mutex);
1149 /* Restart gscan */
1150 err = dhd_pno_initiate_gscan_request(dhd, 1, 0);
1151 goto exit;
1152 }
1153 #endif /* GSCAN_SUPPORT */
1154 /* restart Batch mode if the batch mode is on */
1155 if (_pno_state->pno_mode & (DHD_PNO_BATCH_MODE | DHD_PNO_HOTLIST_MODE)) {
1156 /* retrieve the batching data from firmware into host */
1157 dhd_pno_get_for_batch(dhd, NULL, 0, PNO_STATUS_DISABLE);
1158 /* save current pno_mode before calling dhd_pno_clean */
1159 mode = _pno_state->pno_mode;
1160 err = dhd_pno_clean(dhd);
1161 if (err < 0) {
1162 err = BCME_ERROR;
1163 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
1164 __FUNCTION__, err));
1165 goto exit;
1166 }
1167
1168 /* restore previous pno_mode */
1169 _pno_state->pno_mode = mode;
1170 if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
1171 _params = &(_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS]);
1172 /* restart BATCH SCAN */
1173 err = dhd_pno_set_for_batch(dhd, &_params->params_batch);
1174 if (err < 0) {
1175 _pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
1176 DHD_ERROR(("%s : failed to restart batch scan(err: %d)\n",
1177 __FUNCTION__, err));
1178 goto exit;
1179 }
1180 } else if (_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE) {
1181 /* restart HOTLIST SCAN */
1182 struct dhd_pno_bssid *iter, *next;
1183 _params = &(_pno_state->pno_params_arr[INDEX_OF_HOTLIST_PARAMS]);
1184 p_pfn_bssid = MALLOCZ(dhd->osh, sizeof(wl_pfn_bssid_t) *
1185 _params->params_hotlist.nbssid);
1186 if (p_pfn_bssid == NULL) {
1187 DHD_ERROR(("%s : failed to allocate wl_pfn_bssid_t array"
1188 " (count: %d)",
1189 __FUNCTION__, _params->params_hotlist.nbssid));
1190 err = BCME_ERROR;
1191 _pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
1192 goto exit;
1193 }
1194 /* convert dhd_pno_bssid to wl_pfn_bssid */
1195 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
1196 cnt = 0;
1197 tmp_bssid = p_pfn_bssid;
1198 list_for_each_entry_safe(iter, next,
1199 &_params->params_hotlist.bssid_list, list) {
1200 GCC_DIAGNOSTIC_POP();
1201 memcpy(&tmp_bssid->macaddr,
1202 &iter->macaddr, ETHER_ADDR_LEN);
1203 tmp_bssid->flags = iter->flags;
1204 if (cnt < _params->params_hotlist.nbssid) {
1205 tmp_bssid++;
1206 cnt++;
1207 } else {
1208 DHD_ERROR(("%s: Allocated insufficient memory\n",
1209 __FUNCTION__));
1210 break;
1211 }
1212 }
1213 err = dhd_pno_set_for_hotlist(dhd, p_pfn_bssid, &_params->params_hotlist);
1214 if (err < 0) {
1215 _pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
1216 DHD_ERROR(("%s : failed to restart hotlist scan(err: %d)\n",
1217 __FUNCTION__, err));
1218 goto exit;
1219 }
1220 }
1221 } else {
1222 err = dhd_pno_clean(dhd);
1223 if (err < 0) {
1224 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
1225 __FUNCTION__, err));
1226 goto exit;
1227 }
1228 }
1229 exit:
1230 if (p_pfn_bssid) {
1231 MFREE(dhd->osh, p_pfn_bssid, sizeof(wl_pfn_bssid_t) *
1232 _params->params_hotlist.nbssid);
1233 }
1234 return err;
1235 }
1236
1237 int
dhd_pno_enable(dhd_pub_t * dhd,int enable)1238 dhd_pno_enable(dhd_pub_t *dhd, int enable)
1239 {
1240 int err = BCME_OK;
1241 NULL_CHECK(dhd, "dhd is NULL", err);
1242 DHD_PNO(("%s enter\n", __FUNCTION__));
1243 return (_dhd_pno_enable(dhd, enable));
1244 }
1245
1246 static int
dhd_pno_add_to_ssid_list(dhd_pub_t * dhd,struct list_head * ptr,wlc_ssid_ext_t * ssid_list,int nssid,int * num_ssid_added)1247 dhd_pno_add_to_ssid_list(dhd_pub_t *dhd, struct list_head *ptr, wlc_ssid_ext_t *ssid_list,
1248 int nssid, int *num_ssid_added)
1249 {
1250 int ret = BCME_OK;
1251 int i;
1252 struct dhd_pno_ssid *_pno_ssid;
1253
1254 for (i = 0; i < nssid; i++) {
1255 if (ssid_list[i].SSID_len > DOT11_MAX_SSID_LEN) {
1256 DHD_ERROR(("%s : Invalid SSID length %d\n",
1257 __FUNCTION__, ssid_list[i].SSID_len));
1258 ret = BCME_ERROR;
1259 goto exit;
1260 }
1261 /* Check for broadcast ssid */
1262 if (!ssid_list[i].SSID_len) {
1263 DHD_ERROR(("%d: Broadcast SSID is illegal for PNO setting\n", i));
1264 ret = BCME_ERROR;
1265 goto exit;
1266 }
1267 _pno_ssid = (struct dhd_pno_ssid *)MALLOCZ(dhd->osh,
1268 sizeof(struct dhd_pno_ssid));
1269 if (_pno_ssid == NULL) {
1270 DHD_ERROR(("%s : failed to allocate struct dhd_pno_ssid\n",
1271 __FUNCTION__));
1272 ret = BCME_ERROR;
1273 goto exit;
1274 }
1275 _pno_ssid->SSID_len = ssid_list[i].SSID_len;
1276 _pno_ssid->hidden = ssid_list[i].hidden;
1277 _pno_ssid->rssi_thresh = ssid_list[i].rssi_thresh;
1278 _pno_ssid->flags = ssid_list[i].flags;
1279 _pno_ssid->wpa_auth = WPA_AUTH_PFN_ANY;
1280
1281 memcpy(_pno_ssid->SSID, ssid_list[i].SSID, _pno_ssid->SSID_len);
1282 list_add_tail(&_pno_ssid->list, ptr);
1283 }
1284
1285 exit:
1286 *num_ssid_added = i;
1287 return ret;
1288 }
1289
1290 int
dhd_pno_set_for_ssid(dhd_pub_t * dhd,wlc_ssid_ext_t * ssid_list,int nssid,uint16 scan_fr,int pno_repeat,int pno_freq_expo_max,uint16 * channel_list,int nchan)1291 dhd_pno_set_for_ssid(dhd_pub_t *dhd, wlc_ssid_ext_t* ssid_list, int nssid,
1292 uint16 scan_fr, int pno_repeat, int pno_freq_expo_max, uint16 *channel_list, int nchan)
1293 {
1294 dhd_pno_status_info_t *_pno_state;
1295 dhd_pno_params_t *_params;
1296 struct dhd_pno_legacy_params *params_legacy;
1297 int err = BCME_OK;
1298
1299 if (!dhd || !dhd->pno_state) {
1300 DHD_ERROR(("%s: PNO Not enabled/Not ready\n", __FUNCTION__));
1301 return BCME_NOTREADY;
1302 }
1303
1304 if (!dhd_support_sta_mode(dhd)) {
1305 return BCME_BADOPTION;
1306 }
1307
1308 _pno_state = PNO_GET_PNOSTATE(dhd);
1309 _params = &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS]);
1310 params_legacy = &(_params->params_legacy);
1311 err = _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_LEGACY_MODE);
1312
1313 if (err < 0) {
1314 DHD_ERROR(("%s : failed to reinitialize profile (err %d)\n",
1315 __FUNCTION__, err));
1316 return err;
1317 }
1318
1319 INIT_LIST_HEAD(¶ms_legacy->ssid_list);
1320
1321 if (dhd_pno_add_to_ssid_list(dhd, ¶ms_legacy->ssid_list, ssid_list,
1322 nssid, ¶ms_legacy->nssid) < 0) {
1323 _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_LEGACY_MODE);
1324 return BCME_ERROR;
1325 }
1326
1327 DHD_PNO(("%s enter : nssid %d, scan_fr :%d, pno_repeat :%d,"
1328 "pno_freq_expo_max: %d, nchan :%d\n", __FUNCTION__,
1329 params_legacy->nssid, scan_fr, pno_repeat, pno_freq_expo_max, nchan));
1330
1331 return dhd_pno_set_legacy_pno(dhd, scan_fr, pno_repeat,
1332 pno_freq_expo_max, channel_list, nchan);
1333
1334 }
1335
1336 static int
dhd_pno_set_legacy_pno(dhd_pub_t * dhd,uint16 scan_fr,int pno_repeat,int pno_freq_expo_max,uint16 * channel_list,int nchan)1337 dhd_pno_set_legacy_pno(dhd_pub_t *dhd, uint16 scan_fr, int pno_repeat,
1338 int pno_freq_expo_max, uint16 *channel_list, int nchan)
1339 {
1340 dhd_pno_params_t *_params;
1341 dhd_pno_params_t *_params2;
1342 dhd_pno_status_info_t *_pno_state;
1343 uint16 _chan_list[WL_NUMCHANNELS];
1344 int32 tot_nchan = 0;
1345 int err = BCME_OK;
1346 int i, nssid;
1347 int mode = 0;
1348 struct list_head *ssid_list;
1349
1350 _pno_state = PNO_GET_PNOSTATE(dhd);
1351
1352 _params = &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS]);
1353 /* If GSCAN is also ON will handle this down below */
1354 #ifdef GSCAN_SUPPORT
1355 if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE &&
1356 !(_pno_state->pno_mode & DHD_PNO_GSCAN_MODE))
1357 #else
1358 if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE)
1359 #endif /* GSCAN_SUPPORT */
1360 {
1361 DHD_ERROR(("%s : Legacy PNO mode was already started, "
1362 "will disable previous one to start new one\n", __FUNCTION__));
1363 err = dhd_pno_stop_for_ssid(dhd);
1364 if (err < 0) {
1365 DHD_ERROR(("%s : failed to stop legacy PNO (err %d)\n",
1366 __FUNCTION__, err));
1367 return err;
1368 }
1369 }
1370 _pno_state->pno_mode |= DHD_PNO_LEGACY_MODE;
1371 memset(_chan_list, 0, sizeof(_chan_list));
1372 tot_nchan = MIN(nchan, WL_NUMCHANNELS);
1373 if (tot_nchan > 0 && channel_list) {
1374 for (i = 0; i < tot_nchan; i++)
1375 _params->params_legacy.chan_list[i] = _chan_list[i] = channel_list[i];
1376 }
1377 #ifdef GSCAN_SUPPORT
1378 else {
1379 tot_nchan = WL_NUMCHANNELS;
1380 err = _dhd_pno_get_channels(dhd, _chan_list, &tot_nchan,
1381 (WLC_BAND_2G | WLC_BAND_5G), FALSE);
1382 if (err < 0) {
1383 tot_nchan = 0;
1384 DHD_PNO(("Could not get channel list for PNO SSID\n"));
1385 } else {
1386 for (i = 0; i < tot_nchan; i++)
1387 _params->params_legacy.chan_list[i] = _chan_list[i];
1388 }
1389 }
1390 #endif /* GSCAN_SUPPORT */
1391
1392 if (_pno_state->pno_mode & (DHD_PNO_BATCH_MODE | DHD_PNO_HOTLIST_MODE)) {
1393 DHD_PNO(("BATCH SCAN is on progress in firmware\n"));
1394 /* retrieve the batching data from firmware into host */
1395 dhd_pno_get_for_batch(dhd, NULL, 0, PNO_STATUS_DISABLE);
1396 /* store current pno_mode before disabling pno */
1397 mode = _pno_state->pno_mode;
1398 err = _dhd_pno_enable(dhd, PNO_OFF);
1399 if (err < 0) {
1400 DHD_ERROR(("%s : failed to disable PNO\n", __FUNCTION__));
1401 goto exit;
1402 }
1403 /* restore the previous mode */
1404 _pno_state->pno_mode = mode;
1405 /* use superset of channel list between two mode */
1406 if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
1407 _params2 = &(_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS]);
1408 if (_params2->params_batch.nchan > 0 && tot_nchan > 0) {
1409 err = _dhd_pno_chan_merge(_chan_list, &tot_nchan,
1410 &_params2->params_batch.chan_list[0],
1411 _params2->params_batch.nchan,
1412 &channel_list[0], tot_nchan);
1413 if (err < 0) {
1414 DHD_ERROR(("%s : failed to merge channel list"
1415 " between legacy and batch\n",
1416 __FUNCTION__));
1417 goto exit;
1418 }
1419 } else {
1420 DHD_PNO(("superset channel will use"
1421 " all channels in firmware\n"));
1422 }
1423 } else if (_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE) {
1424 _params2 = &(_pno_state->pno_params_arr[INDEX_OF_HOTLIST_PARAMS]);
1425 if (_params2->params_hotlist.nchan > 0 && tot_nchan > 0) {
1426 err = _dhd_pno_chan_merge(_chan_list, &tot_nchan,
1427 &_params2->params_hotlist.chan_list[0],
1428 _params2->params_hotlist.nchan,
1429 &channel_list[0], tot_nchan);
1430 if (err < 0) {
1431 DHD_ERROR(("%s : failed to merge channel list"
1432 " between legacy and hotlist\n",
1433 __FUNCTION__));
1434 goto exit;
1435 }
1436 }
1437 }
1438 }
1439 _params->params_legacy.scan_fr = scan_fr;
1440 _params->params_legacy.pno_repeat = pno_repeat;
1441 _params->params_legacy.pno_freq_expo_max = pno_freq_expo_max;
1442 _params->params_legacy.nchan = tot_nchan;
1443 ssid_list = &_params->params_legacy.ssid_list;
1444 nssid = _params->params_legacy.nssid;
1445
1446 #ifdef GSCAN_SUPPORT
1447 /* dhd_pno_initiate_gscan_request will handle simultaneous Legacy PNO and GSCAN */
1448 if (_pno_state->pno_mode & DHD_PNO_GSCAN_MODE) {
1449 struct dhd_pno_gscan_params *gscan_params;
1450 gscan_params = &_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS].params_gscan;
1451 /* ePNO and Legacy PNO do not co-exist */
1452 if (gscan_params->epno_cfg.num_epno_ssid) {
1453 DHD_PNO(("ePNO and Legacy PNO do not co-exist\n"));
1454 err = BCME_EPERM;
1455 goto exit;
1456 }
1457 DHD_PNO(("GSCAN mode is ON! Will restart GSCAN+Legacy PNO\n"));
1458 err = dhd_pno_initiate_gscan_request(dhd, 1, 0);
1459 goto exit;
1460 }
1461 #endif /* GSCAN_SUPPORT */
1462 if ((err = _dhd_pno_set(dhd, _params, DHD_PNO_LEGACY_MODE)) < 0) {
1463 DHD_ERROR(("failed to set call pno_set (err %d) in firmware\n", err));
1464 goto exit;
1465 }
1466 if ((err = _dhd_pno_add_ssid(dhd, ssid_list, nssid)) < 0) {
1467 DHD_ERROR(("failed to add ssid list(err %d), %d in firmware\n", err, nssid));
1468 goto exit;
1469 }
1470 if (tot_nchan > 0) {
1471 if ((err = _dhd_pno_cfg(dhd, _chan_list, tot_nchan)) < 0) {
1472 DHD_ERROR(("%s : failed to set call pno_cfg (err %d) in firmware\n",
1473 __FUNCTION__, err));
1474 goto exit;
1475 }
1476 }
1477 if (_pno_state->pno_status == DHD_PNO_DISABLED) {
1478 if ((err = _dhd_pno_enable(dhd, PNO_ON)) < 0)
1479 DHD_ERROR(("%s : failed to enable PNO\n", __FUNCTION__));
1480 }
1481 exit:
1482 if (err < 0) {
1483 _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_LEGACY_MODE);
1484 }
1485 /* clear mode in case of error */
1486 if (err < 0) {
1487 int ret = dhd_pno_clean(dhd);
1488
1489 if (ret < 0) {
1490 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
1491 __FUNCTION__, ret));
1492 } else {
1493 _pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
1494 }
1495 }
1496 return err;
1497 }
1498
1499 int
dhd_pno_set_for_batch(dhd_pub_t * dhd,struct dhd_pno_batch_params * batch_params)1500 dhd_pno_set_for_batch(dhd_pub_t *dhd, struct dhd_pno_batch_params *batch_params)
1501 {
1502 int err = BCME_OK;
1503 uint16 _chan_list[WL_NUMCHANNELS];
1504 int rem_nchan = 0, tot_nchan = 0;
1505 int mode = 0, mscan = 0;
1506 dhd_pno_params_t *_params;
1507 dhd_pno_params_t *_params2;
1508 dhd_pno_status_info_t *_pno_state;
1509 NULL_CHECK(dhd, "dhd is NULL", err);
1510 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
1511 NULL_CHECK(batch_params, "batch_params is NULL", err);
1512 _pno_state = PNO_GET_PNOSTATE(dhd);
1513 DHD_PNO(("%s enter\n", __FUNCTION__));
1514 if (!dhd_support_sta_mode(dhd)) {
1515 err = BCME_BADOPTION;
1516 goto exit;
1517 }
1518 if (!WLS_SUPPORTED(_pno_state)) {
1519 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
1520 err = BCME_UNSUPPORTED;
1521 goto exit;
1522 }
1523 _params = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS];
1524 if (!(_pno_state->pno_mode & DHD_PNO_BATCH_MODE)) {
1525 _pno_state->pno_mode |= DHD_PNO_BATCH_MODE;
1526 err = _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_BATCH_MODE);
1527 if (err < 0) {
1528 DHD_ERROR(("%s : failed to call _dhd_pno_reinitialize_prof\n",
1529 __FUNCTION__));
1530 goto exit;
1531 }
1532 } else {
1533 /* batch mode is already started */
1534 return -EBUSY;
1535 }
1536 _params->params_batch.scan_fr = batch_params->scan_fr;
1537 _params->params_batch.bestn = batch_params->bestn;
1538 _params->params_batch.mscan = (batch_params->mscan)?
1539 batch_params->mscan : DEFAULT_BATCH_MSCAN;
1540 _params->params_batch.nchan = batch_params->nchan;
1541 memcpy(_params->params_batch.chan_list, batch_params->chan_list,
1542 sizeof(_params->params_batch.chan_list));
1543
1544 memset(_chan_list, 0, sizeof(_chan_list));
1545
1546 rem_nchan = ARRAYSIZE(batch_params->chan_list) - batch_params->nchan;
1547 if (batch_params->band == WLC_BAND_2G || batch_params->band == WLC_BAND_5G) {
1548 /* get a valid channel list based on band B or A */
1549 err = _dhd_pno_get_channels(dhd,
1550 &_params->params_batch.chan_list[batch_params->nchan],
1551 &rem_nchan, batch_params->band, FALSE);
1552 if (err < 0) {
1553 DHD_ERROR(("%s: failed to get valid channel list(band : %d)\n",
1554 __FUNCTION__, batch_params->band));
1555 goto exit;
1556 }
1557 /* now we need to update nchan because rem_chan has valid channel count */
1558 _params->params_batch.nchan += rem_nchan;
1559 /* need to sort channel list */
1560 sort(_params->params_batch.chan_list, _params->params_batch.nchan,
1561 sizeof(_params->params_batch.chan_list[0]), _dhd_pno_cmpfunc, NULL);
1562 }
1563 #ifdef PNO_DEBUG
1564 {
1565 DHD_PNO(("Channel list : "));
1566 for (i = 0; i < _params->params_batch.nchan; i++) {
1567 DHD_PNO(("%d ", _params->params_batch.chan_list[i]));
1568 }
1569 DHD_PNO(("\n"));
1570 }
1571 #endif // endif
1572 if (_params->params_batch.nchan) {
1573 /* copy the channel list into local array */
1574 memcpy(_chan_list, _params->params_batch.chan_list, sizeof(_chan_list));
1575 tot_nchan = _params->params_batch.nchan;
1576 }
1577 if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
1578 DHD_PNO(("PNO SSID is on progress in firmware\n"));
1579 /* store current pno_mode before disabling pno */
1580 mode = _pno_state->pno_mode;
1581 err = _dhd_pno_enable(dhd, PNO_OFF);
1582 if (err < 0) {
1583 DHD_ERROR(("%s : failed to disable PNO\n", __FUNCTION__));
1584 goto exit;
1585 }
1586 /* restore the previous mode */
1587 _pno_state->pno_mode = mode;
1588 /* Use the superset for channelist between two mode */
1589 _params2 = &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS]);
1590 if (_params2->params_legacy.nchan > 0 && _params->params_batch.nchan > 0) {
1591 err = _dhd_pno_chan_merge(_chan_list, &tot_nchan,
1592 &_params2->params_legacy.chan_list[0],
1593 _params2->params_legacy.nchan,
1594 &_params->params_batch.chan_list[0], _params->params_batch.nchan);
1595 if (err < 0) {
1596 DHD_ERROR(("%s : failed to merge channel list"
1597 " between legacy and batch\n",
1598 __FUNCTION__));
1599 goto exit;
1600 }
1601 } else {
1602 DHD_PNO(("superset channel will use all channels in firmware\n"));
1603 }
1604 if ((err = _dhd_pno_add_ssid(dhd, &_params2->params_legacy.ssid_list,
1605 _params2->params_legacy.nssid)) < 0) {
1606 DHD_ERROR(("failed to add ssid list (err %d) in firmware\n", err));
1607 goto exit;
1608 }
1609 }
1610 if ((err = _dhd_pno_set(dhd, _params, DHD_PNO_BATCH_MODE)) < 0) {
1611 DHD_ERROR(("%s : failed to set call pno_set (err %d) in firmware\n",
1612 __FUNCTION__, err));
1613 goto exit;
1614 } else {
1615 /* we need to return mscan */
1616 mscan = err;
1617 }
1618 if (tot_nchan > 0) {
1619 if ((err = _dhd_pno_cfg(dhd, _chan_list, tot_nchan)) < 0) {
1620 DHD_ERROR(("%s : failed to set call pno_cfg (err %d) in firmware\n",
1621 __FUNCTION__, err));
1622 goto exit;
1623 }
1624 }
1625 if (_pno_state->pno_status == DHD_PNO_DISABLED) {
1626 if ((err = _dhd_pno_enable(dhd, PNO_ON)) < 0)
1627 DHD_ERROR(("%s : failed to enable PNO\n", __FUNCTION__));
1628 }
1629 exit:
1630 /* clear mode in case of error */
1631 if (err < 0)
1632 _pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
1633 else {
1634 /* return #max scan firmware can do */
1635 err = mscan;
1636 }
1637 return err;
1638 }
1639
1640 #ifdef GSCAN_SUPPORT
1641
1642 static int
dhd_set_epno_params(dhd_pub_t * dhd,wl_ssid_ext_params_t * params,bool set)1643 dhd_set_epno_params(dhd_pub_t *dhd, wl_ssid_ext_params_t *params, bool set)
1644 {
1645 wl_pfn_ssid_cfg_t cfg;
1646 int err;
1647 NULL_CHECK(dhd, "dhd is NULL\n", err);
1648 memset(&cfg, 0, sizeof(wl_pfn_ssid_cfg_t));
1649 cfg.version = WL_PFN_SSID_CFG_VERSION;
1650
1651 /* If asked to clear params (set == FALSE) just set the CLEAR bit */
1652 if (!set)
1653 cfg.flags |= WL_PFN_SSID_CFG_CLEAR;
1654 else if (params)
1655 memcpy(&cfg.params, params, sizeof(wl_ssid_ext_params_t));
1656 err = dhd_iovar(dhd, 0, "pfn_ssid_cfg", (char *)&cfg,
1657 sizeof(wl_pfn_ssid_cfg_t), NULL, 0, TRUE);
1658 if (err != BCME_OK) {
1659 DHD_ERROR(("%s : Failed to execute pfn_ssid_cfg %d\n", __FUNCTION__, err));
1660 }
1661 return err;
1662 }
1663
1664 int
dhd_pno_flush_fw_epno(dhd_pub_t * dhd)1665 dhd_pno_flush_fw_epno(dhd_pub_t *dhd)
1666 {
1667 int err;
1668
1669 NULL_CHECK(dhd, "dhd is NULL\n", err);
1670
1671 err = dhd_set_epno_params(dhd, NULL, FALSE);
1672 if (err < 0) {
1673 DHD_ERROR(("failed to set ePNO params %d\n", err));
1674 return err;
1675 }
1676 err = _dhd_pno_flush_ssid(dhd);
1677 return err;
1678 }
1679
1680 int
dhd_pno_set_epno(dhd_pub_t * dhd)1681 dhd_pno_set_epno(dhd_pub_t *dhd)
1682 {
1683 int err = BCME_OK;
1684 dhd_pno_params_t *params;
1685 dhd_pno_status_info_t *_pno_state;
1686
1687 struct dhd_pno_gscan_params *gscan_params;
1688
1689 NULL_CHECK(dhd, "dhd is NULL\n", err);
1690 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
1691 _pno_state = PNO_GET_PNOSTATE(dhd);
1692 params = &_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS];
1693 gscan_params = ¶ms->params_gscan;
1694
1695 if (gscan_params->epno_cfg.num_epno_ssid) {
1696 DHD_PNO(("num_epno_ssid %d\n", gscan_params->epno_cfg.num_epno_ssid));
1697 if ((err = _dhd_pno_add_ssid(dhd, &gscan_params->epno_cfg.epno_ssid_list,
1698 gscan_params->epno_cfg.num_epno_ssid)) < 0) {
1699 DHD_ERROR(("failed to add ssid list (err %d) to firmware\n", err));
1700 return err;
1701 }
1702 err = dhd_set_epno_params(dhd, &gscan_params->epno_cfg.params, TRUE);
1703 if (err < 0) {
1704 DHD_ERROR(("failed to set ePNO params %d\n", err));
1705 }
1706 }
1707 return err;
1708 }
1709
1710 static void
dhd_pno_reset_cfg_gscan(dhd_pub_t * dhd,dhd_pno_params_t * _params,dhd_pno_status_info_t * _pno_state,uint8 flags)1711 dhd_pno_reset_cfg_gscan(dhd_pub_t *dhd, dhd_pno_params_t *_params,
1712 dhd_pno_status_info_t *_pno_state, uint8 flags)
1713 {
1714 DHD_PNO(("%s enter\n", __FUNCTION__));
1715
1716 if (flags & GSCAN_FLUSH_SCAN_CFG) {
1717 _params->params_gscan.bestn = 0;
1718 _params->params_gscan.mscan = 0;
1719 _params->params_gscan.buffer_threshold = GSCAN_BATCH_NO_THR_SET;
1720 _params->params_gscan.scan_fr = 0;
1721 _params->params_gscan.send_all_results_flag = 0;
1722 memset(_params->params_gscan.channel_bucket, 0,
1723 _params->params_gscan.nchannel_buckets *
1724 sizeof(struct dhd_pno_gscan_channel_bucket));
1725 _params->params_gscan.nchannel_buckets = 0;
1726 DHD_PNO(("Flush Scan config\n"));
1727 }
1728 if (flags & GSCAN_FLUSH_HOTLIST_CFG) {
1729 struct dhd_pno_bssid *iter, *next;
1730 if (_params->params_gscan.nbssid_hotlist > 0) {
1731 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
1732 list_for_each_entry_safe(iter, next,
1733 &_params->params_gscan.hotlist_bssid_list, list) {
1734 GCC_DIAGNOSTIC_POP();
1735 list_del(&iter->list);
1736 MFREE(dhd->osh, iter, sizeof(struct dhd_pno_bssid));
1737 }
1738 }
1739 _params->params_gscan.nbssid_hotlist = 0;
1740 DHD_PNO(("Flush Hotlist Config\n"));
1741 }
1742 if (flags & GSCAN_FLUSH_EPNO_CFG) {
1743 dhd_pno_ssid_t *iter, *next;
1744 dhd_epno_ssid_cfg_t *epno_cfg = &_params->params_gscan.epno_cfg;
1745
1746 if (epno_cfg->num_epno_ssid > 0) {
1747 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
1748 list_for_each_entry_safe(iter, next,
1749 &epno_cfg->epno_ssid_list, list) {
1750 GCC_DIAGNOSTIC_POP();
1751 list_del(&iter->list);
1752 MFREE(dhd->osh, iter, sizeof(struct dhd_pno_bssid));
1753 }
1754 epno_cfg->num_epno_ssid = 0;
1755 }
1756 memset(&epno_cfg->params, 0, sizeof(wl_ssid_ext_params_t));
1757 DHD_PNO(("Flushed ePNO Config\n"));
1758 }
1759
1760 return;
1761 }
1762
1763 int
dhd_pno_lock_batch_results(dhd_pub_t * dhd)1764 dhd_pno_lock_batch_results(dhd_pub_t *dhd)
1765 {
1766 dhd_pno_status_info_t *_pno_state;
1767 int err = BCME_OK;
1768
1769 NULL_CHECK(dhd, "dhd is NULL", err);
1770 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
1771 _pno_state = PNO_GET_PNOSTATE(dhd);
1772 mutex_lock(&_pno_state->pno_mutex);
1773 return err;
1774 }
1775
1776 void
dhd_pno_unlock_batch_results(dhd_pub_t * dhd)1777 dhd_pno_unlock_batch_results(dhd_pub_t *dhd)
1778 {
1779 dhd_pno_status_info_t *_pno_state;
1780 _pno_state = PNO_GET_PNOSTATE(dhd);
1781 mutex_unlock(&_pno_state->pno_mutex);
1782 return;
1783 }
1784
1785 int
dhd_wait_batch_results_complete(dhd_pub_t * dhd)1786 dhd_wait_batch_results_complete(dhd_pub_t *dhd)
1787 {
1788 dhd_pno_status_info_t *_pno_state;
1789 dhd_pno_params_t *_params;
1790 int err = BCME_OK;
1791
1792 NULL_CHECK(dhd, "dhd is NULL", err);
1793 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
1794 _pno_state = PNO_GET_PNOSTATE(dhd);
1795 _params = &_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS];
1796
1797 /* Has the workqueue finished its job already?? */
1798 if (_params->params_gscan.get_batch_flag == GSCAN_BATCH_RETRIEVAL_IN_PROGRESS) {
1799 DHD_PNO(("%s: Waiting to complete retrieval..\n", __FUNCTION__));
1800 wait_event_interruptible_timeout(_pno_state->batch_get_wait,
1801 is_batch_retrieval_complete(&_params->params_gscan),
1802 msecs_to_jiffies(GSCAN_BATCH_GET_MAX_WAIT));
1803 } else { /* GSCAN_BATCH_RETRIEVAL_COMPLETE */
1804 gscan_results_cache_t *iter;
1805 uint16 num_results = 0;
1806
1807 mutex_lock(&_pno_state->pno_mutex);
1808 iter = _params->params_gscan.gscan_batch_cache;
1809 while (iter) {
1810 num_results += iter->tot_count - iter->tot_consumed;
1811 iter = iter->next;
1812 }
1813 mutex_unlock(&_pno_state->pno_mutex);
1814
1815 /* All results consumed/No results cached??
1816 * Get fresh results from FW
1817 */
1818 if ((_pno_state->pno_mode & DHD_PNO_GSCAN_MODE) && !num_results) {
1819 DHD_PNO(("%s: No results cached, getting from FW..\n", __FUNCTION__));
1820 err = dhd_retreive_batch_scan_results(dhd);
1821 if (err == BCME_OK) {
1822 wait_event_interruptible_timeout(_pno_state->batch_get_wait,
1823 is_batch_retrieval_complete(&_params->params_gscan),
1824 msecs_to_jiffies(GSCAN_BATCH_GET_MAX_WAIT));
1825 }
1826 }
1827 }
1828 DHD_PNO(("%s: Wait complete\n", __FUNCTION__));
1829 return err;
1830 }
1831
1832 int
dhd_pno_set_cfg_gscan(dhd_pub_t * dhd,dhd_pno_gscan_cmd_cfg_t type,void * buf,bool flush)1833 dhd_pno_set_cfg_gscan(dhd_pub_t *dhd, dhd_pno_gscan_cmd_cfg_t type,
1834 void *buf, bool flush)
1835 {
1836 int err = BCME_OK;
1837 dhd_pno_params_t *_params;
1838 int i;
1839 dhd_pno_status_info_t *_pno_state;
1840
1841 NULL_CHECK(dhd, "dhd is NULL", err);
1842 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
1843
1844 DHD_PNO(("%s enter\n", __FUNCTION__));
1845
1846 _pno_state = PNO_GET_PNOSTATE(dhd);
1847 _params = &_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS];
1848 mutex_lock(&_pno_state->pno_mutex);
1849
1850 switch (type) {
1851 case DHD_PNO_BATCH_SCAN_CFG_ID:
1852 {
1853 gscan_batch_params_t *ptr = (gscan_batch_params_t *)buf;
1854 _params->params_gscan.bestn = ptr->bestn;
1855 _params->params_gscan.mscan = ptr->mscan;
1856 _params->params_gscan.buffer_threshold = ptr->buffer_threshold;
1857 }
1858 break;
1859 case DHD_PNO_GEOFENCE_SCAN_CFG_ID:
1860 {
1861 gscan_hotlist_scan_params_t *ptr = (gscan_hotlist_scan_params_t *)buf;
1862 struct dhd_pno_bssid *_pno_bssid;
1863 struct bssid_t *bssid_ptr;
1864 int8 flags;
1865
1866 if (flush) {
1867 dhd_pno_reset_cfg_gscan(dhd, _params, _pno_state,
1868 GSCAN_FLUSH_HOTLIST_CFG);
1869 }
1870
1871 if (!ptr->nbssid) {
1872 break;
1873 }
1874 if (!_params->params_gscan.nbssid_hotlist) {
1875 INIT_LIST_HEAD(&_params->params_gscan.hotlist_bssid_list);
1876 }
1877
1878 if ((_params->params_gscan.nbssid_hotlist +
1879 ptr->nbssid) > PFN_SWC_MAX_NUM_APS) {
1880 DHD_ERROR(("Excessive number of hotlist APs programmed %d\n",
1881 (_params->params_gscan.nbssid_hotlist +
1882 ptr->nbssid)));
1883 err = BCME_RANGE;
1884 goto exit;
1885 }
1886
1887 for (i = 0, bssid_ptr = ptr->bssid; i < ptr->nbssid; i++, bssid_ptr++) {
1888 _pno_bssid = (struct dhd_pno_bssid *)MALLOCZ(dhd->osh,
1889 sizeof(struct dhd_pno_bssid));
1890 if (!_pno_bssid) {
1891 DHD_ERROR(("_pno_bssid is NULL, cannot kalloc %zd bytes",
1892 sizeof(struct dhd_pno_bssid)));
1893 err = BCME_NOMEM;
1894 goto exit;
1895 }
1896 memcpy(&_pno_bssid->macaddr, &bssid_ptr->macaddr, ETHER_ADDR_LEN);
1897
1898 flags = (int8) bssid_ptr->rssi_reporting_threshold;
1899 _pno_bssid->flags = flags << WL_PFN_RSSI_SHIFT;
1900 list_add_tail(&_pno_bssid->list,
1901 &_params->params_gscan.hotlist_bssid_list);
1902 }
1903
1904 _params->params_gscan.nbssid_hotlist += ptr->nbssid;
1905 _params->params_gscan.lost_ap_window = ptr->lost_ap_window;
1906 }
1907 break;
1908 case DHD_PNO_SCAN_CFG_ID:
1909 {
1910 int k;
1911 uint16 band;
1912 gscan_scan_params_t *ptr = (gscan_scan_params_t *)buf;
1913 struct dhd_pno_gscan_channel_bucket *ch_bucket;
1914
1915 if (ptr->nchannel_buckets <= GSCAN_MAX_CH_BUCKETS) {
1916 _params->params_gscan.nchannel_buckets = ptr->nchannel_buckets;
1917
1918 memcpy(_params->params_gscan.channel_bucket, ptr->channel_bucket,
1919 _params->params_gscan.nchannel_buckets *
1920 sizeof(struct dhd_pno_gscan_channel_bucket));
1921 ch_bucket = _params->params_gscan.channel_bucket;
1922
1923 for (i = 0; i < ptr->nchannel_buckets; i++) {
1924 band = ch_bucket[i].band;
1925 for (k = 0; k < ptr->channel_bucket[i].num_channels; k++) {
1926 ch_bucket[i].chan_list[k] =
1927 wf_mhz2channel(ptr->channel_bucket[i].chan_list[k],
1928 0);
1929 }
1930 ch_bucket[i].band = 0;
1931 /* HAL and DHD use different bits for 2.4G and
1932 * 5G in bitmap. Hence translating it here...
1933 */
1934 if (band & GSCAN_BG_BAND_MASK) {
1935 ch_bucket[i].band |= WLC_BAND_2G;
1936 }
1937 if (band & GSCAN_A_BAND_MASK) {
1938 ch_bucket[i].band |= WLC_BAND_5G;
1939 }
1940 if (band & GSCAN_DFS_MASK) {
1941 ch_bucket[i].band |= GSCAN_DFS_MASK;
1942 }
1943 DHD_PNO(("band %d report_flag %d\n", ch_bucket[i].band,
1944 ch_bucket[i].report_flag));
1945 }
1946
1947 for (i = 0; i < ptr->nchannel_buckets; i++) {
1948 ch_bucket[i].bucket_freq_multiple =
1949 ch_bucket[i].bucket_freq_multiple/ptr->scan_fr;
1950 ch_bucket[i].bucket_max_multiple =
1951 ch_bucket[i].bucket_max_multiple/ptr->scan_fr;
1952 DHD_PNO(("mult %d max_mult %d\n",
1953 ch_bucket[i].bucket_freq_multiple,
1954 ch_bucket[i].bucket_max_multiple));
1955 }
1956 _params->params_gscan.scan_fr = ptr->scan_fr;
1957
1958 DHD_PNO(("num_buckets %d scan_fr %d\n", ptr->nchannel_buckets,
1959 _params->params_gscan.scan_fr));
1960 } else {
1961 err = BCME_BADARG;
1962 }
1963 }
1964 break;
1965 case DHD_PNO_EPNO_CFG_ID:
1966 if (flush) {
1967 dhd_pno_reset_cfg_gscan(dhd, _params, _pno_state,
1968 GSCAN_FLUSH_EPNO_CFG);
1969 }
1970 break;
1971 case DHD_PNO_EPNO_PARAMS_ID:
1972 if (flush) {
1973 memset(&_params->params_gscan.epno_cfg.params, 0,
1974 sizeof(wl_ssid_ext_params_t));
1975 }
1976 if (buf) {
1977 memcpy(&_params->params_gscan.epno_cfg.params, buf,
1978 sizeof(wl_ssid_ext_params_t));
1979 }
1980 break;
1981 default:
1982 err = BCME_BADARG;
1983 DHD_ERROR(("%s: Unrecognized cmd type - %d\n", __FUNCTION__, type));
1984 break;
1985 }
1986 exit:
1987 mutex_unlock(&_pno_state->pno_mutex);
1988 return err;
1989
1990 }
1991
1992 static bool
validate_gscan_params(struct dhd_pno_gscan_params * gscan_params)1993 validate_gscan_params(struct dhd_pno_gscan_params *gscan_params)
1994 {
1995 unsigned int i, k;
1996
1997 if (!gscan_params->scan_fr || !gscan_params->nchannel_buckets) {
1998 DHD_ERROR(("%s : Scan freq - %d or number of channel buckets - %d is empty\n",
1999 __FUNCTION__, gscan_params->scan_fr, gscan_params->nchannel_buckets));
2000 return false;
2001 }
2002
2003 for (i = 0; i < gscan_params->nchannel_buckets; i++) {
2004 if (!gscan_params->channel_bucket[i].band) {
2005 for (k = 0; k < gscan_params->channel_bucket[i].num_channels; k++) {
2006 if (gscan_params->channel_bucket[i].chan_list[k] > CHANNEL_5G_MAX) {
2007 DHD_ERROR(("%s : Unknown channel %d\n", __FUNCTION__,
2008 gscan_params->channel_bucket[i].chan_list[k]));
2009 return false;
2010 }
2011 }
2012 }
2013 }
2014
2015 return true;
2016 }
2017
2018 static int
dhd_pno_set_for_gscan(dhd_pub_t * dhd,struct dhd_pno_gscan_params * gscan_params)2019 dhd_pno_set_for_gscan(dhd_pub_t *dhd, struct dhd_pno_gscan_params *gscan_params)
2020 {
2021 int err = BCME_OK;
2022 int mode, i = 0;
2023 uint16 _chan_list[WL_NUMCHANNELS];
2024 int tot_nchan = 0;
2025 int num_buckets_to_fw, tot_num_buckets, gscan_param_size;
2026 dhd_pno_status_info_t *_pno_state = PNO_GET_PNOSTATE(dhd);
2027 wl_pfn_gscan_ch_bucket_cfg_t *ch_bucket = NULL;
2028 wl_pfn_gscan_cfg_t *pfn_gscan_cfg_t = NULL;
2029 wl_pfn_bssid_t *p_pfn_bssid = NULL;
2030 dhd_pno_params_t *_params;
2031 bool fw_flushed = FALSE;
2032
2033 _params = &_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS];
2034
2035 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
2036 NULL_CHECK(gscan_params, "gscan_params is NULL", err);
2037
2038 DHD_PNO(("%s enter\n", __FUNCTION__));
2039
2040 if (!dhd_support_sta_mode(dhd)) {
2041 err = BCME_BADOPTION;
2042 goto exit;
2043 }
2044 if (!WLS_SUPPORTED(_pno_state)) {
2045 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
2046 err = BCME_UNSUPPORTED;
2047 goto exit;
2048 }
2049
2050 if (!validate_gscan_params(gscan_params)) {
2051 DHD_ERROR(("%s : Cannot start gscan - bad params\n", __FUNCTION__));
2052 err = BCME_BADARG;
2053 goto exit;
2054 }
2055
2056 if (!(ch_bucket = dhd_pno_gscan_create_channel_list(dhd, _pno_state,
2057 _chan_list, &tot_num_buckets, &num_buckets_to_fw))) {
2058 goto exit;
2059 }
2060
2061 mutex_lock(&_pno_state->pno_mutex);
2062 /* Clear any pre-existing results in our cache
2063 * not consumed by framework
2064 */
2065 dhd_gscan_clear_all_batch_results(dhd);
2066 if (_pno_state->pno_mode & (DHD_PNO_GSCAN_MODE | DHD_PNO_LEGACY_MODE)) {
2067 /* store current pno_mode before disabling pno */
2068 mode = _pno_state->pno_mode;
2069 err = dhd_pno_clean(dhd);
2070 if (err < 0) {
2071 DHD_ERROR(("%s : failed to disable PNO\n", __FUNCTION__));
2072 mutex_unlock(&_pno_state->pno_mutex);
2073 goto exit;
2074 }
2075 fw_flushed = TRUE;
2076 /* restore the previous mode */
2077 _pno_state->pno_mode = mode;
2078 }
2079 _pno_state->pno_mode |= DHD_PNO_GSCAN_MODE;
2080 mutex_unlock(&_pno_state->pno_mutex);
2081
2082 if ((_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) &&
2083 !gscan_params->epno_cfg.num_epno_ssid) {
2084 struct dhd_pno_legacy_params *params_legacy;
2085 params_legacy =
2086 &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS].params_legacy);
2087
2088 if ((err = _dhd_pno_add_ssid(dhd, ¶ms_legacy->ssid_list,
2089 params_legacy->nssid)) < 0) {
2090 DHD_ERROR(("failed to add ssid list (err %d) in firmware\n", err));
2091 goto exit;
2092 }
2093 }
2094
2095 if ((err = _dhd_pno_set(dhd, _params, DHD_PNO_GSCAN_MODE)) < 0) {
2096 DHD_ERROR(("failed to set call pno_set (err %d) in firmware\n", err));
2097 goto exit;
2098 }
2099
2100 gscan_param_size = sizeof(wl_pfn_gscan_cfg_t) +
2101 (num_buckets_to_fw - 1) * sizeof(wl_pfn_gscan_ch_bucket_cfg_t);
2102 pfn_gscan_cfg_t = (wl_pfn_gscan_cfg_t *) MALLOCZ(dhd->osh, gscan_param_size);
2103
2104 if (!pfn_gscan_cfg_t) {
2105 DHD_ERROR(("%s: failed to malloc memory of size %d\n",
2106 __FUNCTION__, gscan_param_size));
2107 err = BCME_NOMEM;
2108 goto exit;
2109 }
2110
2111 pfn_gscan_cfg_t->version = WL_GSCAN_CFG_VERSION;
2112 if (gscan_params->mscan)
2113 pfn_gscan_cfg_t->buffer_threshold = gscan_params->buffer_threshold;
2114 else
2115 pfn_gscan_cfg_t->buffer_threshold = GSCAN_BATCH_NO_THR_SET;
2116
2117 pfn_gscan_cfg_t->flags =
2118 (gscan_params->send_all_results_flag & GSCAN_SEND_ALL_RESULTS_MASK);
2119 pfn_gscan_cfg_t->flags |= GSCAN_ALL_BUCKETS_IN_FIRST_SCAN_MASK;
2120 pfn_gscan_cfg_t->count_of_channel_buckets = num_buckets_to_fw;
2121 pfn_gscan_cfg_t->retry_threshold = GSCAN_RETRY_THRESHOLD;
2122
2123 for (i = 0; i < num_buckets_to_fw; i++) {
2124 pfn_gscan_cfg_t->channel_bucket[i].bucket_end_index =
2125 ch_bucket[i].bucket_end_index;
2126 pfn_gscan_cfg_t->channel_bucket[i].bucket_freq_multiple =
2127 ch_bucket[i].bucket_freq_multiple;
2128 pfn_gscan_cfg_t->channel_bucket[i].max_freq_multiple =
2129 ch_bucket[i].max_freq_multiple;
2130 pfn_gscan_cfg_t->channel_bucket[i].repeat =
2131 ch_bucket[i].repeat;
2132 pfn_gscan_cfg_t->channel_bucket[i].flag =
2133 ch_bucket[i].flag;
2134 }
2135
2136 tot_nchan = pfn_gscan_cfg_t->channel_bucket[num_buckets_to_fw - 1].bucket_end_index + 1;
2137 DHD_PNO(("Total channel num %d total ch_buckets %d ch_buckets_to_fw %d \n", tot_nchan,
2138 tot_num_buckets, num_buckets_to_fw));
2139
2140 if ((err = _dhd_pno_cfg(dhd, _chan_list, tot_nchan)) < 0) {
2141 DHD_ERROR(("%s : failed to set call pno_cfg (err %d) in firmware\n",
2142 __FUNCTION__, err));
2143 goto exit;
2144 }
2145
2146 if ((err = _dhd_pno_gscan_cfg(dhd, pfn_gscan_cfg_t, gscan_param_size)) < 0) {
2147 DHD_ERROR(("%s : failed to set call pno_gscan_cfg (err %d) in firmware\n",
2148 __FUNCTION__, err));
2149 goto exit;
2150 }
2151 /* Reprogram ePNO cfg from dhd cache if FW has been flushed */
2152 if (fw_flushed) {
2153 dhd_pno_set_epno(dhd);
2154 }
2155
2156 if (gscan_params->nbssid_hotlist) {
2157 struct dhd_pno_bssid *iter, *next;
2158 wl_pfn_bssid_t *ptr;
2159 p_pfn_bssid = (wl_pfn_bssid_t *)MALLOCZ(dhd->osh,
2160 sizeof(wl_pfn_bssid_t) * gscan_params->nbssid_hotlist);
2161 if (p_pfn_bssid == NULL) {
2162 DHD_ERROR(("%s : failed to allocate wl_pfn_bssid_t array"
2163 " (count: %d)",
2164 __FUNCTION__, _params->params_hotlist.nbssid));
2165 err = BCME_NOMEM;
2166 _pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
2167 goto exit;
2168 }
2169 ptr = p_pfn_bssid;
2170 /* convert dhd_pno_bssid to wl_pfn_bssid */
2171 DHD_PNO(("nhotlist %d\n", gscan_params->nbssid_hotlist));
2172 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
2173 list_for_each_entry_safe(iter, next,
2174 &gscan_params->hotlist_bssid_list, list) {
2175 char buffer_hotlist[64];
2176 GCC_DIAGNOSTIC_POP();
2177 memcpy(&ptr->macaddr,
2178 &iter->macaddr, ETHER_ADDR_LEN);
2179 BCM_REFERENCE(buffer_hotlist);
2180 DHD_PNO(("%s\n", bcm_ether_ntoa(&ptr->macaddr, buffer_hotlist)));
2181 ptr->flags = iter->flags;
2182 ptr++;
2183 }
2184
2185 err = _dhd_pno_add_bssid(dhd, p_pfn_bssid, gscan_params->nbssid_hotlist);
2186 if (err < 0) {
2187 DHD_ERROR(("%s : failed to call _dhd_pno_add_bssid(err :%d)\n",
2188 __FUNCTION__, err));
2189 goto exit;
2190 }
2191 }
2192
2193 if ((err = _dhd_pno_enable(dhd, PNO_ON)) < 0) {
2194 DHD_ERROR(("%s : failed to enable PNO err %d\n", __FUNCTION__, err));
2195 }
2196
2197 exit:
2198 /* clear mode in case of error */
2199 if (err < 0) {
2200 int ret = dhd_pno_clean(dhd);
2201
2202 if (ret < 0) {
2203 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
2204 __FUNCTION__, ret));
2205 } else {
2206 _pno_state->pno_mode &= ~DHD_PNO_GSCAN_MODE;
2207 }
2208 }
2209 MFREE(dhd->osh, p_pfn_bssid,
2210 sizeof(wl_pfn_bssid_t) * gscan_params->nbssid_hotlist);
2211 if (pfn_gscan_cfg_t) {
2212 MFREE(dhd->osh, pfn_gscan_cfg_t, gscan_param_size);
2213 }
2214 if (ch_bucket) {
2215 MFREE(dhd->osh, ch_bucket,
2216 (tot_num_buckets * sizeof(wl_pfn_gscan_ch_bucket_cfg_t)));
2217 }
2218 return err;
2219
2220 }
2221
2222 static wl_pfn_gscan_ch_bucket_cfg_t *
dhd_pno_gscan_create_channel_list(dhd_pub_t * dhd,dhd_pno_status_info_t * _pno_state,uint16 * chan_list,uint32 * num_buckets,uint32 * num_buckets_to_fw)2223 dhd_pno_gscan_create_channel_list(dhd_pub_t *dhd,
2224 dhd_pno_status_info_t *_pno_state,
2225 uint16 *chan_list,
2226 uint32 *num_buckets,
2227 uint32 *num_buckets_to_fw)
2228 {
2229 int i, num_channels, err, nchan = WL_NUMCHANNELS, ch_cnt;
2230 uint16 *ptr = chan_list, max;
2231 wl_pfn_gscan_ch_bucket_cfg_t *ch_bucket;
2232 dhd_pno_params_t *_params = &_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS];
2233 bool is_pno_legacy_running;
2234 dhd_pno_gscan_channel_bucket_t *gscan_buckets = _params->params_gscan.channel_bucket;
2235
2236 /* ePNO and Legacy PNO do not co-exist */
2237 is_pno_legacy_running = ((_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) &&
2238 !_params->params_gscan.epno_cfg.num_epno_ssid);
2239
2240 if (is_pno_legacy_running)
2241 *num_buckets = _params->params_gscan.nchannel_buckets + 1;
2242 else
2243 *num_buckets = _params->params_gscan.nchannel_buckets;
2244
2245 *num_buckets_to_fw = 0;
2246
2247 ch_bucket = (wl_pfn_gscan_ch_bucket_cfg_t *) MALLOC(dhd->osh,
2248 ((*num_buckets) * sizeof(wl_pfn_gscan_ch_bucket_cfg_t)));
2249
2250 if (!ch_bucket) {
2251 DHD_ERROR(("%s: failed to malloc memory of size %zd\n",
2252 __FUNCTION__, (*num_buckets) * sizeof(wl_pfn_gscan_ch_bucket_cfg_t)));
2253 *num_buckets_to_fw = *num_buckets = 0;
2254 return NULL;
2255 }
2256
2257 max = gscan_buckets[0].bucket_freq_multiple;
2258 num_channels = 0;
2259 /* nchan is the remaining space left in chan_list buffer
2260 * So any overflow list of channels is ignored
2261 */
2262 for (i = 0; i < _params->params_gscan.nchannel_buckets && nchan; i++) {
2263 if (!gscan_buckets[i].band) {
2264 ch_cnt = MIN(gscan_buckets[i].num_channels, (uint8)nchan);
2265 num_channels += ch_cnt;
2266 memcpy(ptr, gscan_buckets[i].chan_list,
2267 ch_cnt * sizeof(uint16));
2268 ptr = ptr + ch_cnt;
2269 } else {
2270 /* get a valid channel list based on band B or A */
2271 err = _dhd_pno_get_channels(dhd, ptr,
2272 &nchan, (gscan_buckets[i].band & GSCAN_ABG_BAND_MASK),
2273 !(gscan_buckets[i].band & GSCAN_DFS_MASK));
2274
2275 if (err < 0) {
2276 DHD_ERROR(("%s: failed to get valid channel list(band : %d)\n",
2277 __FUNCTION__, gscan_buckets[i].band));
2278 MFREE(dhd->osh, ch_bucket,
2279 ((*num_buckets) * sizeof(wl_pfn_gscan_ch_bucket_cfg_t)));
2280 *num_buckets_to_fw = *num_buckets = 0;
2281 return NULL;
2282 }
2283
2284 num_channels += nchan;
2285 ptr = ptr + nchan;
2286 }
2287
2288 ch_bucket[i].bucket_end_index = num_channels - 1;
2289 ch_bucket[i].bucket_freq_multiple = gscan_buckets[i].bucket_freq_multiple;
2290 ch_bucket[i].repeat = gscan_buckets[i].repeat;
2291 ch_bucket[i].max_freq_multiple = gscan_buckets[i].bucket_max_multiple;
2292 ch_bucket[i].flag = gscan_buckets[i].report_flag;
2293 /* HAL and FW interpretations are opposite for this bit */
2294 ch_bucket[i].flag ^= DHD_PNO_REPORT_NO_BATCH;
2295 if (max < gscan_buckets[i].bucket_freq_multiple)
2296 max = gscan_buckets[i].bucket_freq_multiple;
2297 nchan = WL_NUMCHANNELS - num_channels;
2298 *num_buckets_to_fw = *num_buckets_to_fw + 1;
2299 DHD_PNO(("end_idx %d freq_mult - %d\n",
2300 ch_bucket[i].bucket_end_index, ch_bucket[i].bucket_freq_multiple));
2301 }
2302
2303 _params->params_gscan.max_ch_bucket_freq = max;
2304 /* Legacy PNO maybe running, which means we need to create a legacy PNO bucket
2305 * Get GCF of Legacy PNO and Gscan scanfreq
2306 */
2307 if (is_pno_legacy_running) {
2308 dhd_pno_params_t *_params1 = &_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS];
2309 uint16 *legacy_chan_list = _params1->params_legacy.chan_list;
2310 uint16 common_freq;
2311 uint32 legacy_bucket_idx = _params->params_gscan.nchannel_buckets;
2312 /* If no space is left then only gscan buckets will be sent to FW */
2313 if (nchan) {
2314 common_freq = gcd(_params->params_gscan.scan_fr,
2315 _params1->params_legacy.scan_fr);
2316 max = gscan_buckets[0].bucket_freq_multiple;
2317 /* GSCAN buckets */
2318 for (i = 0; i < _params->params_gscan.nchannel_buckets; i++) {
2319 ch_bucket[i].bucket_freq_multiple *= _params->params_gscan.scan_fr;
2320 ch_bucket[i].bucket_freq_multiple /= common_freq;
2321 if (max < gscan_buckets[i].bucket_freq_multiple)
2322 max = gscan_buckets[i].bucket_freq_multiple;
2323 }
2324 /* Legacy PNO bucket */
2325 ch_bucket[legacy_bucket_idx].bucket_freq_multiple =
2326 _params1->params_legacy.scan_fr;
2327 ch_bucket[legacy_bucket_idx].bucket_freq_multiple /=
2328 common_freq;
2329 _params->params_gscan.max_ch_bucket_freq = MAX(max,
2330 ch_bucket[legacy_bucket_idx].bucket_freq_multiple);
2331 ch_bucket[legacy_bucket_idx].flag = CH_BUCKET_REPORT_REGULAR;
2332 /* Now add channels to the legacy scan bucket */
2333 for (i = 0; i < _params1->params_legacy.nchan && nchan; i++, nchan--) {
2334 ptr[i] = legacy_chan_list[i];
2335 num_channels++;
2336 }
2337 ch_bucket[legacy_bucket_idx].bucket_end_index = num_channels - 1;
2338 *num_buckets_to_fw = *num_buckets_to_fw + 1;
2339 DHD_PNO(("end_idx %d freq_mult - %d\n",
2340 ch_bucket[legacy_bucket_idx].bucket_end_index,
2341 ch_bucket[legacy_bucket_idx].bucket_freq_multiple));
2342 }
2343 }
2344 return ch_bucket;
2345 }
2346
2347 static int
dhd_pno_stop_for_gscan(dhd_pub_t * dhd)2348 dhd_pno_stop_for_gscan(dhd_pub_t *dhd)
2349 {
2350 int err = BCME_OK;
2351 int mode;
2352 dhd_pno_status_info_t *_pno_state;
2353
2354 _pno_state = PNO_GET_PNOSTATE(dhd);
2355 DHD_PNO(("%s enter\n", __FUNCTION__));
2356
2357 if (!dhd_support_sta_mode(dhd)) {
2358 err = BCME_BADOPTION;
2359 goto exit;
2360 }
2361 if (!WLS_SUPPORTED(_pno_state)) {
2362 DHD_ERROR(("%s : wifi location service is not supported\n",
2363 __FUNCTION__));
2364 err = BCME_UNSUPPORTED;
2365 goto exit;
2366 }
2367
2368 if (!(_pno_state->pno_mode & DHD_PNO_GSCAN_MODE)) {
2369 DHD_ERROR(("%s : GSCAN is not enabled\n", __FUNCTION__));
2370 goto exit;
2371 }
2372 if (_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS].params_gscan.mscan) {
2373 /* retrieve the batching data from firmware into host */
2374 err = dhd_wait_batch_results_complete(dhd);
2375 if (err != BCME_OK)
2376 goto exit;
2377 }
2378 mutex_lock(&_pno_state->pno_mutex);
2379 mode = _pno_state->pno_mode & ~DHD_PNO_GSCAN_MODE;
2380 err = dhd_pno_clean(dhd);
2381 if (err < 0) {
2382 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
2383 __FUNCTION__, err));
2384 mutex_unlock(&_pno_state->pno_mutex);
2385 return err;
2386 }
2387 _pno_state->pno_mode = mode;
2388 mutex_unlock(&_pno_state->pno_mutex);
2389
2390 /* Reprogram Legacy PNO if it was running */
2391 if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
2392 struct dhd_pno_legacy_params *params_legacy;
2393 uint16 chan_list[WL_NUMCHANNELS];
2394
2395 params_legacy = &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS].params_legacy);
2396 _pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
2397
2398 DHD_PNO(("Restarting Legacy PNO SSID scan...\n"));
2399 memcpy(chan_list, params_legacy->chan_list,
2400 (params_legacy->nchan * sizeof(uint16)));
2401 err = dhd_pno_set_legacy_pno(dhd, params_legacy->scan_fr,
2402 params_legacy->pno_repeat, params_legacy->pno_freq_expo_max,
2403 chan_list, params_legacy->nchan);
2404 if (err < 0) {
2405 DHD_ERROR(("%s : failed to restart legacy PNO scan(err: %d)\n",
2406 __FUNCTION__, err));
2407 goto exit;
2408 }
2409
2410 }
2411
2412 exit:
2413 return err;
2414 }
2415
2416 int
dhd_pno_initiate_gscan_request(dhd_pub_t * dhd,bool run,bool flush)2417 dhd_pno_initiate_gscan_request(dhd_pub_t *dhd, bool run, bool flush)
2418 {
2419 int err = BCME_OK;
2420 dhd_pno_params_t *params;
2421 dhd_pno_status_info_t *_pno_state;
2422 struct dhd_pno_gscan_params *gscan_params;
2423
2424 NULL_CHECK(dhd, "dhd is NULL\n", err);
2425 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
2426 _pno_state = PNO_GET_PNOSTATE(dhd);
2427
2428 DHD_PNO(("%s enter - run %d flush %d\n", __FUNCTION__, run, flush));
2429
2430 params = &_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS];
2431 gscan_params = ¶ms->params_gscan;
2432
2433 if (run) {
2434 err = dhd_pno_set_for_gscan(dhd, gscan_params);
2435 } else {
2436 if (flush) {
2437 mutex_lock(&_pno_state->pno_mutex);
2438 dhd_pno_reset_cfg_gscan(dhd, params, _pno_state, GSCAN_FLUSH_ALL_CFG);
2439 mutex_unlock(&_pno_state->pno_mutex);
2440 }
2441 /* Need to stop all gscan */
2442 err = dhd_pno_stop_for_gscan(dhd);
2443 }
2444
2445 return err;
2446 }
2447
2448 int
dhd_pno_enable_full_scan_result(dhd_pub_t * dhd,bool real_time_flag)2449 dhd_pno_enable_full_scan_result(dhd_pub_t *dhd, bool real_time_flag)
2450 {
2451 int err = BCME_OK;
2452 dhd_pno_params_t *params;
2453 dhd_pno_status_info_t *_pno_state;
2454 struct dhd_pno_gscan_params *gscan_params;
2455 uint8 old_flag;
2456
2457 NULL_CHECK(dhd, "dhd is NULL\n", err);
2458 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
2459 _pno_state = PNO_GET_PNOSTATE(dhd);
2460
2461 DHD_PNO(("%s enter\n", __FUNCTION__));
2462
2463 if (!WLS_SUPPORTED(_pno_state)) {
2464 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
2465 err = BCME_UNSUPPORTED;
2466 goto exit;
2467 }
2468
2469 params = &_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS];
2470 gscan_params = ¶ms->params_gscan;
2471
2472 mutex_lock(&_pno_state->pno_mutex);
2473
2474 old_flag = gscan_params->send_all_results_flag;
2475 gscan_params->send_all_results_flag = (uint8) real_time_flag;
2476 if (_pno_state->pno_mode & DHD_PNO_GSCAN_MODE) {
2477 if (old_flag != gscan_params->send_all_results_flag) {
2478 wl_pfn_gscan_cfg_t gscan_cfg;
2479
2480 gscan_cfg.version = WL_GSCAN_CFG_VERSION;
2481 gscan_cfg.flags = (gscan_params->send_all_results_flag &
2482 GSCAN_SEND_ALL_RESULTS_MASK);
2483 gscan_cfg.flags |= GSCAN_CFG_FLAGS_ONLY_MASK;
2484
2485 if ((err = _dhd_pno_gscan_cfg(dhd, &gscan_cfg,
2486 sizeof(wl_pfn_gscan_cfg_t))) < 0) {
2487 DHD_ERROR(("%s : pno_gscan_cfg failed (err %d) in firmware\n",
2488 __FUNCTION__, err));
2489 goto exit_mutex_unlock;
2490 }
2491 } else {
2492 DHD_PNO(("No change in flag - %d\n", old_flag));
2493 }
2494 } else {
2495 DHD_PNO(("Gscan not started\n"));
2496 }
2497 exit_mutex_unlock:
2498 mutex_unlock(&_pno_state->pno_mutex);
2499 exit:
2500 return err;
2501 }
2502
2503 /* Cleanup any consumed results
2504 * Return TRUE if all results consumed else FALSE
2505 */
dhd_gscan_batch_cache_cleanup(dhd_pub_t * dhd)2506 int dhd_gscan_batch_cache_cleanup(dhd_pub_t *dhd)
2507 {
2508 int ret = 0;
2509 dhd_pno_params_t *params;
2510 struct dhd_pno_gscan_params *gscan_params;
2511 dhd_pno_status_info_t *_pno_state;
2512 gscan_results_cache_t *iter, *tmp;
2513
2514 _pno_state = PNO_GET_PNOSTATE(dhd);
2515 params = &_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS];
2516 gscan_params = ¶ms->params_gscan;
2517 iter = gscan_params->gscan_batch_cache;
2518
2519 while (iter) {
2520 if (iter->tot_consumed == iter->tot_count) {
2521 tmp = iter->next;
2522 MFREE(dhd->osh, iter,
2523 ((iter->tot_count - 1) * sizeof(wifi_gscan_result_t))
2524 + sizeof(gscan_results_cache_t));
2525 iter = tmp;
2526 } else
2527 break;
2528 }
2529 gscan_params->gscan_batch_cache = iter;
2530 ret = (iter == NULL);
2531 return ret;
2532 }
2533
2534 static int
_dhd_pno_get_gscan_batch_from_fw(dhd_pub_t * dhd)2535 _dhd_pno_get_gscan_batch_from_fw(dhd_pub_t *dhd)
2536 {
2537 int err = BCME_OK;
2538 uint32 timestamp = 0, ts = 0, i, j, timediff;
2539 dhd_pno_params_t *params;
2540 dhd_pno_status_info_t *_pno_state;
2541 wl_pfn_lnet_info_v1_t *plnetinfo;
2542 wl_pfn_lnet_info_v2_t *plnetinfo_v2;
2543 struct dhd_pno_gscan_params *gscan_params;
2544 wl_pfn_lscanresults_v1_t *plbestnet_v1 = NULL;
2545 wl_pfn_lscanresults_v2_t *plbestnet_v2 = NULL;
2546 gscan_results_cache_t *iter, *tail;
2547 wifi_gscan_result_t *result;
2548 uint8 *nAPs_per_scan = NULL;
2549 uint8 num_scans_in_cur_iter;
2550 uint16 count;
2551 uint16 fwcount;
2552 uint16 fwstatus = PFN_INCOMPLETE;
2553 struct osl_timespec tm_spec;
2554
2555 /* Static asserts in _dhd_pno_get_for_batch() below guarantee the v1 and v2
2556 * net_info and subnet_info structures are compatible in size and SSID offset,
2557 * allowing v1 to be safely used in the code below except for lscanresults
2558 * fields themselves (status, count, offset to netinfo).
2559 */
2560
2561 NULL_CHECK(dhd, "dhd is NULL\n", err);
2562 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
2563
2564 _pno_state = PNO_GET_PNOSTATE(dhd);
2565 params = &_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS];
2566 DHD_PNO(("%s enter\n", __FUNCTION__));
2567
2568 if (!WLS_SUPPORTED(_pno_state)) {
2569 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
2570 err = BCME_UNSUPPORTED;
2571 goto exit;
2572 }
2573 if (!(_pno_state->pno_mode & DHD_PNO_GSCAN_MODE)) {
2574 DHD_ERROR(("%s: GSCAN is not enabled\n", __FUNCTION__));
2575 goto exit;
2576 }
2577 gscan_params = ¶ms->params_gscan;
2578 nAPs_per_scan = (uint8 *) MALLOC(dhd->osh, gscan_params->mscan);
2579
2580 if (!nAPs_per_scan) {
2581 DHD_ERROR(("%s :Out of memory!! Cant malloc %d bytes\n", __FUNCTION__,
2582 gscan_params->mscan));
2583 err = BCME_NOMEM;
2584 goto exit;
2585 }
2586
2587 plbestnet_v1 = (wl_pfn_lscanresults_v1_t *)MALLOC(dhd->osh, PNO_BESTNET_LEN);
2588 if (!plbestnet_v1) {
2589 DHD_ERROR(("%s :Out of memory!! Cant malloc %d bytes\n", __FUNCTION__,
2590 (int)PNO_BESTNET_LEN));
2591 err = BCME_NOMEM;
2592 goto exit;
2593 }
2594 plbestnet_v2 = (wl_pfn_lscanresults_v2_t *)plbestnet_v1;
2595
2596 mutex_lock(&_pno_state->pno_mutex);
2597
2598 dhd_gscan_clear_all_batch_results(dhd);
2599
2600 if (!(_pno_state->pno_mode & DHD_PNO_GSCAN_MODE)) {
2601 DHD_ERROR(("%s : GSCAN is not enabled\n", __FUNCTION__));
2602 goto exit_mutex_unlock;
2603 }
2604
2605 timediff = gscan_params->scan_fr * 1000;
2606 timediff = timediff >> 1;
2607
2608 /* Ok, now lets start getting results from the FW */
2609 tail = gscan_params->gscan_batch_cache;
2610 do {
2611 err = dhd_iovar(dhd, 0, "pfnlbest", NULL, 0, (char *)plbestnet_v1, PNO_BESTNET_LEN,
2612 FALSE);
2613 if (err < 0) {
2614 DHD_ERROR(("%s : Cannot get all the batch results, err :%d\n",
2615 __FUNCTION__, err));
2616 goto exit_mutex_unlock;
2617 }
2618 osl_get_monotonic_boottime(&tm_spec);
2619
2620 if (plbestnet_v1->version == PFN_LBEST_SCAN_RESULT_VERSION_V1) {
2621 fwstatus = plbestnet_v1->status;
2622 fwcount = plbestnet_v1->count;
2623 plnetinfo = &plbestnet_v1->netinfo[0];
2624
2625 DHD_PNO(("ver %d, status : %d, count %d\n",
2626 plbestnet_v1->version, fwstatus, fwcount));
2627
2628 if (fwcount == 0) {
2629 DHD_PNO(("No more batch results\n"));
2630 goto exit_mutex_unlock;
2631 }
2632 if (fwcount > BESTN_MAX) {
2633 DHD_ERROR(("%s :fwcount %d is greater than BESTN_MAX %d \n",
2634 __FUNCTION__, fwcount, (int)BESTN_MAX));
2635 /* Process only BESTN_MAX number of results per batch */
2636 fwcount = BESTN_MAX;
2637 }
2638 num_scans_in_cur_iter = 0;
2639
2640 timestamp = plnetinfo->timestamp;
2641 /* find out how many scans' results did we get in
2642 * this batch of FW results
2643 */
2644 for (i = 0, count = 0; i < fwcount; i++, count++, plnetinfo++) {
2645 /* Unlikely to happen, but just in case the results from
2646 * FW doesnt make sense..... Assume its part of one single scan
2647 */
2648 if (num_scans_in_cur_iter >= gscan_params->mscan) {
2649 num_scans_in_cur_iter = 0;
2650 count = fwcount;
2651 break;
2652 }
2653 if (TIME_DIFF_MS(timestamp, plnetinfo->timestamp) > timediff) {
2654 nAPs_per_scan[num_scans_in_cur_iter] = count;
2655 count = 0;
2656 num_scans_in_cur_iter++;
2657 }
2658 timestamp = plnetinfo->timestamp;
2659 }
2660 if (num_scans_in_cur_iter < gscan_params->mscan) {
2661 nAPs_per_scan[num_scans_in_cur_iter] = count;
2662 num_scans_in_cur_iter++;
2663 }
2664
2665 DHD_PNO(("num_scans_in_cur_iter %d\n", num_scans_in_cur_iter));
2666 /* reset plnetinfo to the first item for the next loop */
2667 plnetinfo -= i;
2668
2669 for (i = 0; i < num_scans_in_cur_iter; i++) {
2670 iter = (gscan_results_cache_t *)
2671 MALLOCZ(dhd->osh, ((nAPs_per_scan[i] - 1) *
2672 sizeof(wifi_gscan_result_t)) +
2673 sizeof(gscan_results_cache_t));
2674 if (!iter) {
2675 DHD_ERROR(("%s :Out of memory!! Cant malloc %d bytes\n",
2676 __FUNCTION__, gscan_params->mscan));
2677 err = BCME_NOMEM;
2678 goto exit_mutex_unlock;
2679 }
2680 /* Need this check because the new set of results from FW
2681 * maybe a continuation of previous sets' scan results
2682 */
2683 if (TIME_DIFF_MS(ts, plnetinfo->timestamp) > timediff) {
2684 iter->scan_id = ++gscan_params->scan_id;
2685 } else {
2686 iter->scan_id = gscan_params->scan_id;
2687 }
2688 DHD_PNO(("scan_id %d tot_count %d \n",
2689 gscan_params->scan_id, nAPs_per_scan[i]));
2690 iter->tot_count = nAPs_per_scan[i];
2691 iter->tot_consumed = 0;
2692 iter->flag = 0;
2693 if (plnetinfo->flags & PFN_PARTIAL_SCAN_MASK) {
2694 DHD_PNO(("This scan is aborted\n"));
2695 iter->flag = (ENABLE << PNO_STATUS_ABORT);
2696 } else if (gscan_params->reason) {
2697 iter->flag = (ENABLE << gscan_params->reason);
2698 }
2699
2700 if (!tail) {
2701 gscan_params->gscan_batch_cache = iter;
2702 } else {
2703 tail->next = iter;
2704 }
2705 tail = iter;
2706 iter->next = NULL;
2707 for (j = 0; j < nAPs_per_scan[i]; j++, plnetinfo++) {
2708 result = &iter->results[j];
2709
2710 result->channel =
2711 wf_channel2mhz(plnetinfo->pfnsubnet.channel,
2712 (plnetinfo->pfnsubnet.channel <= CH_MAX_2G_CHANNEL?
2713 WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G));
2714 result->rssi = (int32) plnetinfo->RSSI;
2715 result->beacon_period = 0;
2716 result->capability = 0;
2717 result->rtt = (uint64) plnetinfo->rtt0;
2718 result->rtt_sd = (uint64) plnetinfo->rtt1;
2719 result->ts = convert_fw_rel_time_to_systime(&tm_spec,
2720 plnetinfo->timestamp);
2721 ts = plnetinfo->timestamp;
2722 if (plnetinfo->pfnsubnet.SSID_len > DOT11_MAX_SSID_LEN) {
2723 DHD_ERROR(("%s: Invalid SSID length %d\n",
2724 __FUNCTION__,
2725 plnetinfo->pfnsubnet.SSID_len));
2726 plnetinfo->pfnsubnet.SSID_len = DOT11_MAX_SSID_LEN;
2727 }
2728 memcpy(result->ssid, plnetinfo->pfnsubnet.SSID,
2729 plnetinfo->pfnsubnet.SSID_len);
2730 result->ssid[plnetinfo->pfnsubnet.SSID_len] = '\0';
2731 memcpy(&result->macaddr, &plnetinfo->pfnsubnet.BSSID,
2732 ETHER_ADDR_LEN);
2733
2734 DHD_PNO(("\tSSID : "));
2735 DHD_PNO(("\n"));
2736 DHD_PNO(("\tBSSID: "MACDBG"\n",
2737 MAC2STRDBG(result->macaddr.octet)));
2738 DHD_PNO(("\tchannel: %d, RSSI: %d, timestamp: %d ms\n",
2739 plnetinfo->pfnsubnet.channel,
2740 plnetinfo->RSSI, plnetinfo->timestamp));
2741 DHD_PNO(("\tRTT0 : %d, RTT1: %d\n",
2742 plnetinfo->rtt0, plnetinfo->rtt1));
2743
2744 }
2745 }
2746
2747 } else if (plbestnet_v2->version == PFN_LBEST_SCAN_RESULT_VERSION_V2) {
2748 fwstatus = plbestnet_v2->status;
2749 fwcount = plbestnet_v2->count;
2750 plnetinfo_v2 = (wl_pfn_lnet_info_v2_t*)&plbestnet_v2->netinfo[0];
2751
2752 DHD_PNO(("ver %d, status : %d, count %d\n",
2753 plbestnet_v2->version, fwstatus, fwcount));
2754
2755 if (fwcount == 0) {
2756 DHD_PNO(("No more batch results\n"));
2757 goto exit_mutex_unlock;
2758 }
2759 if (fwcount > BESTN_MAX) {
2760 DHD_ERROR(("%s :fwcount %d is greater than BESTN_MAX %d \n",
2761 __FUNCTION__, fwcount, (int)BESTN_MAX));
2762 /* Process only BESTN_MAX number of results per batch */
2763 fwcount = BESTN_MAX;
2764 }
2765 num_scans_in_cur_iter = 0;
2766
2767 timestamp = plnetinfo_v2->timestamp;
2768 /* find out how many scans' results did we get
2769 * in this batch of FW results
2770 */
2771 for (i = 0, count = 0; i < fwcount; i++, count++, plnetinfo_v2++) {
2772 /* Unlikely to happen, but just in case the results from
2773 * FW doesnt make sense..... Assume its part of one single scan
2774 */
2775 if (num_scans_in_cur_iter >= gscan_params->mscan) {
2776 num_scans_in_cur_iter = 0;
2777 count = fwcount;
2778 break;
2779 }
2780 if (TIME_DIFF_MS(timestamp, plnetinfo_v2->timestamp) > timediff) {
2781 nAPs_per_scan[num_scans_in_cur_iter] = count;
2782 count = 0;
2783 num_scans_in_cur_iter++;
2784 }
2785 timestamp = plnetinfo_v2->timestamp;
2786 }
2787 if (num_scans_in_cur_iter < gscan_params->mscan) {
2788 nAPs_per_scan[num_scans_in_cur_iter] = count;
2789 num_scans_in_cur_iter++;
2790 }
2791
2792 DHD_PNO(("num_scans_in_cur_iter %d\n", num_scans_in_cur_iter));
2793 /* reset plnetinfo to the first item for the next loop */
2794 plnetinfo_v2 -= i;
2795
2796 for (i = 0; i < num_scans_in_cur_iter; i++) {
2797 iter = (gscan_results_cache_t *)
2798 MALLOCZ(dhd->osh, ((nAPs_per_scan[i] - 1) *
2799 sizeof(wifi_gscan_result_t)) +
2800 sizeof(gscan_results_cache_t));
2801 if (!iter) {
2802 DHD_ERROR(("%s :Out of memory!! Cant malloc %d bytes\n",
2803 __FUNCTION__, gscan_params->mscan));
2804 err = BCME_NOMEM;
2805 goto exit_mutex_unlock;
2806 }
2807 /* Need this check because the new set of results from FW
2808 * maybe a continuation of previous sets' scan results
2809 */
2810 if (TIME_DIFF_MS(ts, plnetinfo_v2->timestamp) > timediff) {
2811 iter->scan_id = ++gscan_params->scan_id;
2812 } else {
2813 iter->scan_id = gscan_params->scan_id;
2814 }
2815 DHD_PNO(("scan_id %d tot_count %d ch_bucket %x\n",
2816 gscan_params->scan_id, nAPs_per_scan[i],
2817 plbestnet_v2->scan_ch_buckets[i]));
2818 iter->tot_count = nAPs_per_scan[i];
2819 iter->scan_ch_bucket = plbestnet_v2->scan_ch_buckets[i];
2820 iter->tot_consumed = 0;
2821 iter->flag = 0;
2822 if (plnetinfo_v2->flags & PFN_PARTIAL_SCAN_MASK) {
2823 DHD_PNO(("This scan is aborted\n"));
2824 iter->flag = (ENABLE << PNO_STATUS_ABORT);
2825 } else if (gscan_params->reason) {
2826 iter->flag = (ENABLE << gscan_params->reason);
2827 }
2828
2829 if (!tail) {
2830 gscan_params->gscan_batch_cache = iter;
2831 } else {
2832 tail->next = iter;
2833 }
2834 tail = iter;
2835 iter->next = NULL;
2836 for (j = 0; j < nAPs_per_scan[i]; j++, plnetinfo_v2++) {
2837 result = &iter->results[j];
2838
2839 result->channel =
2840 wf_channel2mhz(plnetinfo_v2->pfnsubnet.channel,
2841 (plnetinfo_v2->pfnsubnet.channel <=
2842 CH_MAX_2G_CHANNEL?
2843 WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G));
2844 result->rssi = (int32) plnetinfo_v2->RSSI;
2845 /* Info not available & not expected */
2846 result->beacon_period = 0;
2847 result->capability = 0;
2848 result->rtt = (uint64) plnetinfo_v2->rtt0;
2849 result->rtt_sd = (uint64) plnetinfo_v2->rtt1;
2850 result->ts = convert_fw_rel_time_to_systime(&tm_spec,
2851 plnetinfo_v2->timestamp);
2852 ts = plnetinfo_v2->timestamp;
2853 if (plnetinfo_v2->pfnsubnet.SSID_len > DOT11_MAX_SSID_LEN) {
2854 DHD_ERROR(("%s: Invalid SSID length %d\n",
2855 __FUNCTION__,
2856 plnetinfo_v2->pfnsubnet.SSID_len));
2857 plnetinfo_v2->pfnsubnet.SSID_len =
2858 DOT11_MAX_SSID_LEN;
2859 }
2860 memcpy(result->ssid, plnetinfo_v2->pfnsubnet.u.SSID,
2861 plnetinfo_v2->pfnsubnet.SSID_len);
2862 result->ssid[plnetinfo_v2->pfnsubnet.SSID_len] = '\0';
2863 memcpy(&result->macaddr, &plnetinfo_v2->pfnsubnet.BSSID,
2864 ETHER_ADDR_LEN);
2865
2866 DHD_PNO(("\tSSID : "));
2867 DHD_PNO(("\n"));
2868 DHD_PNO(("\tBSSID: "MACDBG"\n",
2869 MAC2STRDBG(result->macaddr.octet)));
2870 DHD_PNO(("\tchannel: %d, RSSI: %d, timestamp: %d ms\n",
2871 plnetinfo_v2->pfnsubnet.channel,
2872 plnetinfo_v2->RSSI, plnetinfo_v2->timestamp));
2873 DHD_PNO(("\tRTT0 : %d, RTT1: %d\n",
2874 plnetinfo_v2->rtt0, plnetinfo_v2->rtt1));
2875
2876 }
2877 }
2878
2879 } else {
2880 err = BCME_VERSION;
2881 DHD_ERROR(("bestnet fw version %d not supported\n",
2882 plbestnet_v1->version));
2883 goto exit_mutex_unlock;
2884 }
2885 } while (fwstatus == PFN_INCOMPLETE);
2886
2887 exit_mutex_unlock:
2888 mutex_unlock(&_pno_state->pno_mutex);
2889 exit:
2890 params->params_gscan.get_batch_flag = GSCAN_BATCH_RETRIEVAL_COMPLETE;
2891 smp_wmb();
2892 wake_up_interruptible(&_pno_state->batch_get_wait);
2893 if (nAPs_per_scan) {
2894 MFREE(dhd->osh, nAPs_per_scan, gscan_params->mscan * sizeof(uint8));
2895 }
2896 if (plbestnet_v1) {
2897 MFREE(dhd->osh, plbestnet_v1, PNO_BESTNET_LEN);
2898 }
2899 DHD_PNO(("Batch retrieval done!\n"));
2900 return err;
2901 }
2902 #endif /* GSCAN_SUPPORT */
2903
2904 #if defined(GSCAN_SUPPORT) || defined(DHD_GET_VALID_CHANNELS)
2905 static void *
dhd_get_gscan_batch_results(dhd_pub_t * dhd,uint32 * len)2906 dhd_get_gscan_batch_results(dhd_pub_t *dhd, uint32 *len)
2907 {
2908 gscan_results_cache_t *iter, *results;
2909 dhd_pno_status_info_t *_pno_state;
2910 dhd_pno_params_t *_params;
2911 uint16 num_scan_ids = 0, num_results = 0;
2912
2913 _pno_state = PNO_GET_PNOSTATE(dhd);
2914 _params = &_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS];
2915
2916 iter = results = _params->params_gscan.gscan_batch_cache;
2917 while (iter) {
2918 num_results += iter->tot_count - iter->tot_consumed;
2919 num_scan_ids++;
2920 iter = iter->next;
2921 }
2922
2923 *len = ((num_results << 16) | (num_scan_ids));
2924 return results;
2925 }
2926
2927 void *
dhd_pno_get_gscan(dhd_pub_t * dhd,dhd_pno_gscan_cmd_cfg_t type,void * info,uint32 * len)2928 dhd_pno_get_gscan(dhd_pub_t *dhd, dhd_pno_gscan_cmd_cfg_t type,
2929 void *info, uint32 *len)
2930 {
2931 void *ret = NULL;
2932 dhd_pno_gscan_capabilities_t *ptr;
2933 dhd_pno_ssid_t *ssid_elem;
2934 dhd_pno_params_t *_params;
2935 dhd_epno_ssid_cfg_t *epno_cfg;
2936 dhd_pno_status_info_t *_pno_state;
2937
2938 if (!dhd || !dhd->pno_state) {
2939 DHD_ERROR(("NULL POINTER : %s\n", __FUNCTION__));
2940 return NULL;
2941 }
2942
2943 _pno_state = PNO_GET_PNOSTATE(dhd);
2944 _params = &_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS];
2945
2946 if (!len) {
2947 DHD_ERROR(("%s: len is NULL\n", __FUNCTION__));
2948 return NULL;
2949 }
2950
2951 switch (type) {
2952 case DHD_PNO_GET_CAPABILITIES:
2953 ptr = (dhd_pno_gscan_capabilities_t *)
2954 MALLOCZ(dhd->osh, sizeof(dhd_pno_gscan_capabilities_t));
2955 if (!ptr)
2956 break;
2957 /* Hardcoding these values for now, need to get
2958 * these values from FW, will change in a later check-in
2959 */
2960 ptr->max_scan_cache_size = GSCAN_MAX_AP_CACHE;
2961 ptr->max_scan_buckets = GSCAN_MAX_CH_BUCKETS;
2962 ptr->max_ap_cache_per_scan = GSCAN_MAX_AP_CACHE_PER_SCAN;
2963 ptr->max_rssi_sample_size = PFN_SWC_RSSI_WINDOW_MAX;
2964 ptr->max_scan_reporting_threshold = 100;
2965 ptr->max_hotlist_bssids = PFN_HOTLIST_MAX_NUM_APS;
2966 ptr->max_hotlist_ssids = 0;
2967 ptr->max_significant_wifi_change_aps = 0;
2968 ptr->max_bssid_history_entries = 0;
2969 ptr->max_epno_ssid_crc32 = MAX_EPNO_SSID_NUM;
2970 ptr->max_epno_hidden_ssid = MAX_EPNO_HIDDEN_SSID;
2971 ptr->max_white_list_ssid = MAX_WHITELIST_SSID;
2972 ret = (void *)ptr;
2973 *len = sizeof(dhd_pno_gscan_capabilities_t);
2974 break;
2975
2976 case DHD_PNO_GET_BATCH_RESULTS:
2977 ret = dhd_get_gscan_batch_results(dhd, len);
2978 break;
2979 case DHD_PNO_GET_CHANNEL_LIST:
2980 if (info) {
2981 uint16 ch_list[WL_NUMCHANNELS];
2982 uint32 *p, mem_needed, i;
2983 int32 err, nchan = WL_NUMCHANNELS;
2984 uint32 *gscan_band = (uint32 *) info;
2985 uint8 band = 0;
2986
2987 /* No band specified?, nothing to do */
2988 if ((*gscan_band & GSCAN_BAND_MASK) == 0) {
2989 DHD_PNO(("No band specified\n"));
2990 *len = 0;
2991 break;
2992 }
2993
2994 /* HAL and DHD use different bits for 2.4G and
2995 * 5G in bitmap. Hence translating it here...
2996 */
2997 if (*gscan_band & GSCAN_BG_BAND_MASK) {
2998 band |= WLC_BAND_2G;
2999 }
3000 if (*gscan_band & GSCAN_A_BAND_MASK) {
3001 band |= WLC_BAND_5G;
3002 }
3003
3004 err = _dhd_pno_get_channels(dhd, ch_list, &nchan,
3005 (band & GSCAN_ABG_BAND_MASK),
3006 !(*gscan_band & GSCAN_DFS_MASK));
3007
3008 if (err < 0) {
3009 DHD_ERROR(("%s: failed to get valid channel list\n",
3010 __FUNCTION__));
3011 *len = 0;
3012 } else {
3013 mem_needed = sizeof(uint32) * nchan;
3014 p = (uint32 *)MALLOC(dhd->osh, mem_needed);
3015 if (!p) {
3016 DHD_ERROR(("%s: Unable to malloc %d bytes\n",
3017 __FUNCTION__, mem_needed));
3018 break;
3019 }
3020 for (i = 0; i < nchan; i++) {
3021 p[i] = wf_channel2mhz(ch_list[i],
3022 (ch_list[i] <= CH_MAX_2G_CHANNEL?
3023 WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G));
3024 }
3025 ret = p;
3026 *len = mem_needed;
3027 }
3028 } else {
3029 *len = 0;
3030 DHD_ERROR(("%s: info buffer is NULL\n", __FUNCTION__));
3031 }
3032 break;
3033 case DHD_PNO_GET_NEW_EPNO_SSID_ELEM:
3034 epno_cfg = &_params->params_gscan.epno_cfg;
3035 if (epno_cfg->num_epno_ssid >=
3036 MAX_EPNO_SSID_NUM) {
3037 DHD_ERROR(("Excessive number of ePNO SSIDs programmed %d\n",
3038 epno_cfg->num_epno_ssid));
3039 return NULL;
3040 }
3041 if (!epno_cfg->num_epno_ssid) {
3042 INIT_LIST_HEAD(&epno_cfg->epno_ssid_list);
3043 }
3044 ssid_elem = MALLOCZ(dhd->osh, sizeof(dhd_pno_ssid_t));
3045 if (!ssid_elem) {
3046 DHD_ERROR(("EPNO ssid: cannot alloc %zd bytes",
3047 sizeof(dhd_pno_ssid_t)));
3048 return NULL;
3049 }
3050 epno_cfg->num_epno_ssid++;
3051 list_add_tail(&ssid_elem->list, &epno_cfg->epno_ssid_list);
3052 ret = ssid_elem;
3053 break;
3054 default:
3055 DHD_ERROR(("%s: Unrecognized cmd type - %d\n", __FUNCTION__, type));
3056 break;
3057 }
3058
3059 return ret;
3060
3061 }
3062 #endif /* GSCAN_SUPPORT || DHD_GET_VALID_CHANNELS */
3063
3064 static int
_dhd_pno_get_for_batch(dhd_pub_t * dhd,char * buf,int bufsize,int reason)3065 _dhd_pno_get_for_batch(dhd_pub_t *dhd, char *buf, int bufsize, int reason)
3066 {
3067 int err = BCME_OK;
3068 int i, j;
3069 uint32 timestamp = 0;
3070 dhd_pno_params_t *_params = NULL;
3071 dhd_pno_status_info_t *_pno_state = NULL;
3072 wl_pfn_lscanresults_v1_t *plbestnet_v1 = NULL;
3073 wl_pfn_lscanresults_v2_t *plbestnet_v2 = NULL;
3074 wl_pfn_lnet_info_v1_t *plnetinfo;
3075 wl_pfn_lnet_info_v2_t *plnetinfo_v2;
3076 dhd_pno_bestnet_entry_t *pbestnet_entry;
3077 dhd_pno_best_header_t *pbestnetheader = NULL;
3078 dhd_pno_scan_results_t *pscan_results = NULL, *siter, *snext;
3079 bool allocate_header = FALSE;
3080 uint16 fwstatus = PFN_INCOMPLETE;
3081 uint16 fwcount;
3082
3083 NULL_CHECK(dhd, "dhd is NULL", err);
3084 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
3085
3086 /* The static asserts below guarantee the v1 and v2 net_info and subnet_info
3087 * structures are compatible in size and SSID offset, allowing v1 to be safely
3088 * used in the code below except for lscanresults fields themselves
3089 * (status, count, offset to netinfo).
3090 */
3091 STATIC_ASSERT(sizeof(wl_pfn_net_info_v1_t) == sizeof(wl_pfn_net_info_v2_t));
3092 STATIC_ASSERT(sizeof(wl_pfn_lnet_info_v1_t) == sizeof(wl_pfn_lnet_info_v2_t));
3093 STATIC_ASSERT(sizeof(wl_pfn_subnet_info_v1_t) == sizeof(wl_pfn_subnet_info_v2_t));
3094 ASSERT(OFFSETOF(wl_pfn_subnet_info_v1_t, SSID) ==
3095 OFFSETOF(wl_pfn_subnet_info_v2_t, u.SSID));
3096
3097 DHD_PNO(("%s enter\n", __FUNCTION__));
3098 _pno_state = PNO_GET_PNOSTATE(dhd);
3099
3100 if (!dhd_support_sta_mode(dhd)) {
3101 err = BCME_BADOPTION;
3102 goto exit_no_unlock;
3103 }
3104
3105 if (!WLS_SUPPORTED(_pno_state)) {
3106 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
3107 err = BCME_UNSUPPORTED;
3108 goto exit_no_unlock;
3109 }
3110
3111 if (!(_pno_state->pno_mode & DHD_PNO_BATCH_MODE)) {
3112 DHD_ERROR(("%s: Batching SCAN mode is not enabled\n", __FUNCTION__));
3113 goto exit_no_unlock;
3114 }
3115 mutex_lock(&_pno_state->pno_mutex);
3116 _params = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS];
3117 if (buf && bufsize) {
3118 if (!list_empty(&_params->params_batch.get_batch.expired_scan_results_list)) {
3119 /* need to check whether we have cashed data or not */
3120 DHD_PNO(("%s: have cashed batching data in Driver\n",
3121 __FUNCTION__));
3122 /* convert to results format */
3123 goto convert_format;
3124 } else {
3125 /* this is a first try to get batching results */
3126 if (!list_empty(&_params->params_batch.get_batch.scan_results_list)) {
3127 /* move the scan_results_list to expired_scan_results_lists */
3128 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
3129 list_for_each_entry_safe(siter, snext,
3130 &_params->params_batch.get_batch.scan_results_list, list) {
3131 GCC_DIAGNOSTIC_POP();
3132 list_move_tail(&siter->list,
3133 &_params->params_batch.get_batch.expired_scan_results_list);
3134 }
3135 _params->params_batch.get_batch.top_node_cnt = 0;
3136 _params->params_batch.get_batch.expired_tot_scan_cnt =
3137 _params->params_batch.get_batch.tot_scan_cnt;
3138 _params->params_batch.get_batch.tot_scan_cnt = 0;
3139 goto convert_format;
3140 }
3141 }
3142 }
3143 /* create dhd_pno_scan_results_t whenever we got event WLC_E_PFN_BEST_BATCHING */
3144 pscan_results = (dhd_pno_scan_results_t *)MALLOC(dhd->osh, SCAN_RESULTS_SIZE);
3145 if (pscan_results == NULL) {
3146 err = BCME_NOMEM;
3147 DHD_ERROR(("failed to allocate dhd_pno_scan_results_t\n"));
3148 goto exit;
3149 }
3150 pscan_results->bestnetheader = NULL;
3151 pscan_results->cnt_header = 0;
3152 /* add the element into list unless total node cnt is less than MAX_NODE_ CNT */
3153 if (_params->params_batch.get_batch.top_node_cnt < MAX_NODE_CNT) {
3154 list_add(&pscan_results->list, &_params->params_batch.get_batch.scan_results_list);
3155 _params->params_batch.get_batch.top_node_cnt++;
3156 } else {
3157 int _removed_scan_cnt;
3158 /* remove oldest one and add new one */
3159 DHD_PNO(("%s : Remove oldest node and add new one\n", __FUNCTION__));
3160 _removed_scan_cnt = _dhd_pno_clear_all_batch_results(dhd,
3161 &_params->params_batch.get_batch.scan_results_list, TRUE);
3162 _params->params_batch.get_batch.tot_scan_cnt -= _removed_scan_cnt;
3163 list_add(&pscan_results->list, &_params->params_batch.get_batch.scan_results_list);
3164
3165 }
3166
3167 plbestnet_v1 = (wl_pfn_lscanresults_v1_t *)MALLOC(dhd->osh, PNO_BESTNET_LEN);
3168 NULL_CHECK(plbestnet_v1, "failed to allocate buffer for bestnet", err);
3169 plbestnet_v2 = (wl_pfn_lscanresults_v2_t*)plbestnet_v1;
3170
3171 DHD_PNO(("%s enter\n", __FUNCTION__));
3172 do {
3173 err = dhd_iovar(dhd, 0, "pfnlbest", NULL, 0, (char *)plbestnet_v1, PNO_BESTNET_LEN,
3174 FALSE);
3175 if (err < 0) {
3176 if (err == BCME_EPERM) {
3177 DHD_ERROR(("we cannot get the batching data "
3178 "during scanning in firmware, try again\n,"));
3179 msleep(500);
3180 continue;
3181 } else {
3182 DHD_ERROR(("%s : failed to execute pfnlbest (err :%d)\n",
3183 __FUNCTION__, err));
3184 goto exit;
3185 }
3186 }
3187
3188 if (plbestnet_v1->version == PFN_LBEST_SCAN_RESULT_VERSION_V1) {
3189 fwstatus = plbestnet_v1->status;
3190 fwcount = plbestnet_v1->count;
3191 plnetinfo = &plbestnet_v1->netinfo[0];
3192 if (fwcount == 0) {
3193 DHD_PNO(("No more batch results\n"));
3194 goto exit;
3195 }
3196 if (fwcount > BESTN_MAX) {
3197 DHD_ERROR(("%s :fwcount %d is greater than BESTN_MAX %d \n",
3198 __FUNCTION__, fwcount, (int)BESTN_MAX));
3199 /* Process only BESTN_MAX number of results per batch */
3200 fwcount = BESTN_MAX;
3201 }
3202 for (i = 0; i < fwcount; i++) {
3203 pbestnet_entry = (dhd_pno_bestnet_entry_t *)
3204 MALLOC(dhd->osh, BESTNET_ENTRY_SIZE);
3205 if (pbestnet_entry == NULL) {
3206 err = BCME_NOMEM;
3207 DHD_ERROR(("failed to allocate dhd_pno_bestnet_entry\n"));
3208 goto exit;
3209 }
3210 memset(pbestnet_entry, 0, BESTNET_ENTRY_SIZE);
3211 /* record the current time */
3212 pbestnet_entry->recorded_time = jiffies;
3213 /* create header for the first entry */
3214 allocate_header = (i == 0)? TRUE : FALSE;
3215 /* check whether the new generation is started or not */
3216 if (timestamp && (TIME_DIFF(timestamp, plnetinfo->timestamp)
3217 > TIME_MIN_DIFF))
3218 allocate_header = TRUE;
3219 timestamp = plnetinfo->timestamp;
3220 if (allocate_header) {
3221 pbestnetheader = (dhd_pno_best_header_t *)
3222 MALLOC(dhd->osh, BEST_HEADER_SIZE);
3223 if (pbestnetheader == NULL) {
3224 err = BCME_NOMEM;
3225 if (pbestnet_entry)
3226 MFREE(dhd->osh, pbestnet_entry,
3227 BESTNET_ENTRY_SIZE);
3228 DHD_ERROR(("failed to allocate"
3229 " dhd_pno_bestnet_entry\n"));
3230 goto exit;
3231 }
3232 /* increase total cnt of bestnet header */
3233 pscan_results->cnt_header++;
3234 /* need to record the reason to call dhd_pno_get_for_bach */
3235 if (reason)
3236 pbestnetheader->reason = (ENABLE << reason);
3237 memset(pbestnetheader, 0, BEST_HEADER_SIZE);
3238 /* initialize the head of linked list */
3239 INIT_LIST_HEAD(&(pbestnetheader->entry_list));
3240 /* link the pbestnet heaer into existed list */
3241 if (pscan_results->bestnetheader == NULL)
3242 /* In case of header */
3243 pscan_results->bestnetheader = pbestnetheader;
3244 else {
3245 dhd_pno_best_header_t *head =
3246 pscan_results->bestnetheader;
3247 pscan_results->bestnetheader = pbestnetheader;
3248 pbestnetheader->next = head;
3249 }
3250 }
3251 pbestnet_entry->channel = plnetinfo->pfnsubnet.channel;
3252 pbestnet_entry->RSSI = plnetinfo->RSSI;
3253 if (plnetinfo->flags & PFN_PARTIAL_SCAN_MASK) {
3254 /* if RSSI is positive value, we assume that
3255 * this scan is aborted by other scan
3256 */
3257 DHD_PNO(("This scan is aborted\n"));
3258 pbestnetheader->reason = (ENABLE << PNO_STATUS_ABORT);
3259 }
3260 pbestnet_entry->rtt0 = plnetinfo->rtt0;
3261 pbestnet_entry->rtt1 = plnetinfo->rtt1;
3262 pbestnet_entry->timestamp = plnetinfo->timestamp;
3263 if (plnetinfo->pfnsubnet.SSID_len > DOT11_MAX_SSID_LEN) {
3264 DHD_ERROR(("%s: Invalid SSID length"
3265 " %d: trimming it to max\n",
3266 __FUNCTION__, plnetinfo->pfnsubnet.SSID_len));
3267 plnetinfo->pfnsubnet.SSID_len = DOT11_MAX_SSID_LEN;
3268 }
3269 pbestnet_entry->SSID_len = plnetinfo->pfnsubnet.SSID_len;
3270 memcpy(pbestnet_entry->SSID, plnetinfo->pfnsubnet.SSID,
3271 pbestnet_entry->SSID_len);
3272 memcpy(&pbestnet_entry->BSSID, &plnetinfo->pfnsubnet.BSSID,
3273 ETHER_ADDR_LEN);
3274 /* add the element into list */
3275 list_add_tail(&pbestnet_entry->list, &pbestnetheader->entry_list);
3276 /* increase best entry count */
3277 pbestnetheader->tot_cnt++;
3278 pbestnetheader->tot_size += BESTNET_ENTRY_SIZE;
3279 DHD_PNO(("Header %d\n", pscan_results->cnt_header - 1));
3280 DHD_PNO(("\tSSID : "));
3281 for (j = 0; j < plnetinfo->pfnsubnet.SSID_len; j++)
3282 DHD_PNO(("%c", plnetinfo->pfnsubnet.SSID[j]));
3283 DHD_PNO(("\n"));
3284 DHD_PNO(("\tBSSID: "MACDBG"\n",
3285 MAC2STRDBG(plnetinfo->pfnsubnet.BSSID.octet)));
3286 DHD_PNO(("\tchannel: %d, RSSI: %d, timestamp: %d ms\n",
3287 plnetinfo->pfnsubnet.channel,
3288 plnetinfo->RSSI, plnetinfo->timestamp));
3289 DHD_PNO(("\tRTT0 : %d, RTT1: %d\n", plnetinfo->rtt0,
3290 plnetinfo->rtt1));
3291 plnetinfo++;
3292 }
3293 } else if (plbestnet_v2->version == PFN_LBEST_SCAN_RESULT_VERSION_V2) {
3294 fwstatus = plbestnet_v2->status;
3295 fwcount = plbestnet_v2->count;
3296 plnetinfo_v2 = (wl_pfn_lnet_info_v2_t*)&plbestnet_v2->netinfo[0];
3297 if (fwcount == 0) {
3298 DHD_PNO(("No more batch results\n"));
3299 goto exit;
3300 }
3301 if (fwcount > BESTN_MAX) {
3302 DHD_ERROR(("%s :fwcount %d is greater than BESTN_MAX %d \n",
3303 __FUNCTION__, fwcount, (int)BESTN_MAX));
3304 /* Process only BESTN_MAX number of results per batch */
3305 fwcount = BESTN_MAX;
3306 }
3307 DHD_PNO(("ver %d, status : %d, count %d\n",
3308 plbestnet_v2->version, fwstatus, fwcount));
3309
3310 for (i = 0; i < fwcount; i++) {
3311 pbestnet_entry = (dhd_pno_bestnet_entry_t *)
3312 MALLOC(dhd->osh, BESTNET_ENTRY_SIZE);
3313 if (pbestnet_entry == NULL) {
3314 err = BCME_NOMEM;
3315 DHD_ERROR(("failed to allocate dhd_pno_bestnet_entry\n"));
3316 goto exit;
3317 }
3318 memset(pbestnet_entry, 0, BESTNET_ENTRY_SIZE);
3319 /* record the current time */
3320 pbestnet_entry->recorded_time = jiffies;
3321 /* create header for the first entry */
3322 allocate_header = (i == 0)? TRUE : FALSE;
3323 /* check whether the new generation is started or not */
3324 if (timestamp && (TIME_DIFF(timestamp, plnetinfo_v2->timestamp)
3325 > TIME_MIN_DIFF))
3326 allocate_header = TRUE;
3327 timestamp = plnetinfo_v2->timestamp;
3328 if (allocate_header) {
3329 pbestnetheader = (dhd_pno_best_header_t *)
3330 MALLOC(dhd->osh, BEST_HEADER_SIZE);
3331 if (pbestnetheader == NULL) {
3332 err = BCME_NOMEM;
3333 if (pbestnet_entry)
3334 MFREE(dhd->osh, pbestnet_entry,
3335 BESTNET_ENTRY_SIZE);
3336 DHD_ERROR(("failed to allocate"
3337 " dhd_pno_bestnet_entry\n"));
3338 goto exit;
3339 }
3340 /* increase total cnt of bestnet header */
3341 pscan_results->cnt_header++;
3342 /* need to record the reason to call dhd_pno_get_for_bach */
3343 if (reason)
3344 pbestnetheader->reason = (ENABLE << reason);
3345 memset(pbestnetheader, 0, BEST_HEADER_SIZE);
3346 /* initialize the head of linked list */
3347 INIT_LIST_HEAD(&(pbestnetheader->entry_list));
3348 /* link the pbestnet heaer into existed list */
3349 if (pscan_results->bestnetheader == NULL)
3350 /* In case of header */
3351 pscan_results->bestnetheader = pbestnetheader;
3352 else {
3353 dhd_pno_best_header_t *head =
3354 pscan_results->bestnetheader;
3355 pscan_results->bestnetheader = pbestnetheader;
3356 pbestnetheader->next = head;
3357 }
3358 }
3359 /* fills the best network info */
3360 pbestnet_entry->channel = plnetinfo_v2->pfnsubnet.channel;
3361 pbestnet_entry->RSSI = plnetinfo_v2->RSSI;
3362 if (plnetinfo_v2->flags & PFN_PARTIAL_SCAN_MASK) {
3363 /* if RSSI is positive value, we assume that
3364 * this scan is aborted by other scan
3365 */
3366 DHD_PNO(("This scan is aborted\n"));
3367 pbestnetheader->reason = (ENABLE << PNO_STATUS_ABORT);
3368 }
3369 pbestnet_entry->rtt0 = plnetinfo_v2->rtt0;
3370 pbestnet_entry->rtt1 = plnetinfo_v2->rtt1;
3371 pbestnet_entry->timestamp = plnetinfo_v2->timestamp;
3372 if (plnetinfo_v2->pfnsubnet.SSID_len > DOT11_MAX_SSID_LEN) {
3373 DHD_ERROR(("%s: Invalid SSID length"
3374 " %d: trimming it to max\n",
3375 __FUNCTION__, plnetinfo_v2->pfnsubnet.SSID_len));
3376 plnetinfo_v2->pfnsubnet.SSID_len = DOT11_MAX_SSID_LEN;
3377 }
3378 pbestnet_entry->SSID_len = plnetinfo_v2->pfnsubnet.SSID_len;
3379 memcpy(pbestnet_entry->SSID, plnetinfo_v2->pfnsubnet.u.SSID,
3380 pbestnet_entry->SSID_len);
3381 memcpy(&pbestnet_entry->BSSID, &plnetinfo_v2->pfnsubnet.BSSID,
3382 ETHER_ADDR_LEN);
3383 /* add the element into list */
3384 list_add_tail(&pbestnet_entry->list, &pbestnetheader->entry_list);
3385 /* increase best entry count */
3386 pbestnetheader->tot_cnt++;
3387 pbestnetheader->tot_size += BESTNET_ENTRY_SIZE;
3388 DHD_PNO(("Header %d\n", pscan_results->cnt_header - 1));
3389 DHD_PNO(("\tSSID : "));
3390 for (j = 0; j < plnetinfo_v2->pfnsubnet.SSID_len; j++)
3391 DHD_PNO(("%c", plnetinfo_v2->pfnsubnet.u.SSID[j]));
3392 DHD_PNO(("\n"));
3393 DHD_PNO(("\tBSSID: "MACDBG"\n",
3394 MAC2STRDBG(plnetinfo_v2->pfnsubnet.BSSID.octet)));
3395 DHD_PNO(("\tchannel: %d, RSSI: %d, timestamp: %d ms\n",
3396 plnetinfo_v2->pfnsubnet.channel,
3397 plnetinfo_v2->RSSI, plnetinfo_v2->timestamp));
3398 DHD_PNO(("\tRTT0 : %d, RTT1: %d\n", plnetinfo_v2->rtt0,
3399 plnetinfo_v2->rtt1));
3400 plnetinfo_v2++;
3401 }
3402 } else {
3403 err = BCME_VERSION;
3404 DHD_ERROR(("bestnet fw version %d not supported\n",
3405 plbestnet_v1->version));
3406 goto exit;
3407 }
3408 } while (fwstatus != PFN_COMPLETE);
3409
3410 if (pscan_results->cnt_header == 0) {
3411 /* In case that we didn't get any data from the firmware
3412 * Remove the current scan_result list from get_bach.scan_results_list.
3413 */
3414 DHD_PNO(("NO BATCH DATA from Firmware, Delete current SCAN RESULT LIST\n"));
3415 list_del(&pscan_results->list);
3416 MFREE(dhd->osh, pscan_results, SCAN_RESULTS_SIZE);
3417 _params->params_batch.get_batch.top_node_cnt--;
3418 } else {
3419 /* increase total scan count using current scan count */
3420 _params->params_batch.get_batch.tot_scan_cnt += pscan_results->cnt_header;
3421 }
3422
3423 if (buf && bufsize) {
3424 /* This is a first try to get batching results */
3425 if (!list_empty(&_params->params_batch.get_batch.scan_results_list)) {
3426 /* move the scan_results_list to expired_scan_results_lists */
3427 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
3428 list_for_each_entry_safe(siter, snext,
3429 &_params->params_batch.get_batch.scan_results_list, list) {
3430 GCC_DIAGNOSTIC_POP();
3431 list_move_tail(&siter->list,
3432 &_params->params_batch.get_batch.expired_scan_results_list);
3433 }
3434 /* reset gloval values after moving to expired list */
3435 _params->params_batch.get_batch.top_node_cnt = 0;
3436 _params->params_batch.get_batch.expired_tot_scan_cnt =
3437 _params->params_batch.get_batch.tot_scan_cnt;
3438 _params->params_batch.get_batch.tot_scan_cnt = 0;
3439 }
3440 convert_format:
3441 err = _dhd_pno_convert_format(dhd, &_params->params_batch, buf, bufsize);
3442 if (err < 0) {
3443 DHD_ERROR(("failed to convert the data into upper layer format\n"));
3444 goto exit;
3445 }
3446 }
3447 exit:
3448 if (plbestnet_v1)
3449 MFREE(dhd->osh, plbestnet_v1, PNO_BESTNET_LEN);
3450 if (_params) {
3451 _params->params_batch.get_batch.buf = NULL;
3452 _params->params_batch.get_batch.bufsize = 0;
3453 _params->params_batch.get_batch.bytes_written = err;
3454 }
3455 mutex_unlock(&_pno_state->pno_mutex);
3456 exit_no_unlock:
3457 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0))
3458 if (waitqueue_active(&_pno_state->get_batch_done)) {
3459 _pno_state->batch_recvd = TRUE;
3460 wake_up(&_pno_state->get_batch_done);
3461 }
3462 #else
3463 if (waitqueue_active(&_pno_state->get_batch_done.wait))
3464 complete(&_pno_state->get_batch_done);
3465 #endif
3466 return err;
3467 }
3468
3469 static void
_dhd_pno_get_batch_handler(struct work_struct * work)3470 _dhd_pno_get_batch_handler(struct work_struct *work)
3471 {
3472 dhd_pno_status_info_t *_pno_state;
3473 dhd_pub_t *dhd;
3474 struct dhd_pno_batch_params *params_batch;
3475 DHD_PNO(("%s enter\n", __FUNCTION__));
3476 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
3477 _pno_state = container_of(work, struct dhd_pno_status_info, work);
3478 GCC_DIAGNOSTIC_POP();
3479
3480 dhd = _pno_state->dhd;
3481 if (dhd == NULL) {
3482 DHD_ERROR(("%s : dhd is NULL\n", __FUNCTION__));
3483 return;
3484 }
3485
3486 #ifdef GSCAN_SUPPORT
3487 _dhd_pno_get_gscan_batch_from_fw(dhd);
3488 #endif /* GSCAN_SUPPORT */
3489 if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
3490 params_batch = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS].params_batch;
3491
3492 _dhd_pno_get_for_batch(dhd, params_batch->get_batch.buf,
3493 params_batch->get_batch.bufsize, params_batch->get_batch.reason);
3494 }
3495 }
3496
3497 int
dhd_pno_get_for_batch(dhd_pub_t * dhd,char * buf,int bufsize,int reason)3498 dhd_pno_get_for_batch(dhd_pub_t *dhd, char *buf, int bufsize, int reason)
3499 {
3500 int err = BCME_OK;
3501 char *pbuf = buf;
3502 dhd_pno_status_info_t *_pno_state;
3503 struct dhd_pno_batch_params *params_batch;
3504 NULL_CHECK(dhd, "dhd is NULL", err);
3505 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
3506 if (!dhd_support_sta_mode(dhd)) {
3507 err = BCME_BADOPTION;
3508 goto exit;
3509 }
3510 DHD_PNO(("%s enter\n", __FUNCTION__));
3511 _pno_state = PNO_GET_PNOSTATE(dhd);
3512
3513 if (!WLS_SUPPORTED(_pno_state)) {
3514 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
3515 err = BCME_UNSUPPORTED;
3516 goto exit;
3517 }
3518 params_batch = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS].params_batch;
3519 #ifdef GSCAN_SUPPORT
3520 if (_pno_state->pno_mode & DHD_PNO_GSCAN_MODE) {
3521 struct dhd_pno_gscan_params *gscan_params;
3522 gscan_params = &_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS].params_gscan;
3523 gscan_params->reason = reason;
3524 err = dhd_retreive_batch_scan_results(dhd);
3525 if (err == BCME_OK) {
3526 wait_event_interruptible_timeout(_pno_state->batch_get_wait,
3527 is_batch_retrieval_complete(gscan_params),
3528 msecs_to_jiffies(GSCAN_BATCH_GET_MAX_WAIT));
3529 }
3530 } else
3531 #endif // endif
3532 {
3533 if (!(_pno_state->pno_mode & DHD_PNO_BATCH_MODE)) {
3534 DHD_ERROR(("%s: Batching SCAN mode is not enabled\n", __FUNCTION__));
3535 memset(pbuf, 0, bufsize);
3536 pbuf += snprintf(pbuf, bufsize, "scancount=%d\n", 0);
3537 snprintf(pbuf, bufsize, "%s", RESULTS_END_MARKER);
3538 err = strlen(buf);
3539 goto exit;
3540 }
3541 params_batch->get_batch.buf = buf;
3542 params_batch->get_batch.bufsize = bufsize;
3543 params_batch->get_batch.reason = reason;
3544 params_batch->get_batch.bytes_written = 0;
3545 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0))
3546 _pno_state->batch_recvd = FALSE;
3547 #endif
3548 schedule_work(&_pno_state->work);
3549 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0))
3550 wait_event(_pno_state->get_batch_done, _pno_state->batch_recvd);
3551 #else
3552 wait_for_completion(&_pno_state->get_batch_done);
3553 #endif
3554 }
3555
3556 #ifdef GSCAN_SUPPORT
3557 if (!(_pno_state->pno_mode & DHD_PNO_GSCAN_MODE))
3558 #endif // endif
3559 err = params_batch->get_batch.bytes_written;
3560 exit:
3561 return err;
3562 }
3563
3564 int
dhd_pno_stop_for_batch(dhd_pub_t * dhd)3565 dhd_pno_stop_for_batch(dhd_pub_t *dhd)
3566 {
3567 int err = BCME_OK;
3568 int mode = 0;
3569 int i = 0;
3570 dhd_pno_status_info_t *_pno_state;
3571 dhd_pno_params_t *_params;
3572 wl_pfn_bssid_t *p_pfn_bssid = NULL;
3573 NULL_CHECK(dhd, "dhd is NULL", err);
3574 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
3575 _pno_state = PNO_GET_PNOSTATE(dhd);
3576 DHD_PNO(("%s enter\n", __FUNCTION__));
3577 if (!dhd_support_sta_mode(dhd)) {
3578 err = BCME_BADOPTION;
3579 goto exit;
3580 }
3581 if (!WLS_SUPPORTED(_pno_state)) {
3582 DHD_ERROR(("%s : wifi location service is not supported\n",
3583 __FUNCTION__));
3584 err = BCME_UNSUPPORTED;
3585 goto exit;
3586 }
3587
3588 #ifdef GSCAN_SUPPORT
3589 if (_pno_state->pno_mode & DHD_PNO_GSCAN_MODE) {
3590 DHD_PNO(("Gscan is ongoing, nothing to stop here\n"));
3591 return err;
3592 }
3593 #endif // endif
3594
3595 if (!(_pno_state->pno_mode & DHD_PNO_BATCH_MODE)) {
3596 DHD_ERROR(("%s : PNO BATCH MODE is not enabled\n", __FUNCTION__));
3597 goto exit;
3598 }
3599 _pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
3600 if (_pno_state->pno_mode & (DHD_PNO_LEGACY_MODE | DHD_PNO_HOTLIST_MODE)) {
3601 mode = _pno_state->pno_mode;
3602 err = dhd_pno_clean(dhd);
3603 if (err < 0) {
3604 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
3605 __FUNCTION__, err));
3606 goto exit;
3607 }
3608
3609 _pno_state->pno_mode = mode;
3610 /* restart Legacy PNO if the Legacy PNO is on */
3611 if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
3612 struct dhd_pno_legacy_params *_params_legacy;
3613 _params_legacy =
3614 &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS].params_legacy);
3615 err = dhd_pno_set_legacy_pno(dhd, _params_legacy->scan_fr,
3616 _params_legacy->pno_repeat,
3617 _params_legacy->pno_freq_expo_max,
3618 _params_legacy->chan_list, _params_legacy->nchan);
3619 if (err < 0) {
3620 DHD_ERROR(("%s : failed to restart legacy PNO scan(err: %d)\n",
3621 __FUNCTION__, err));
3622 goto exit;
3623 }
3624 } else if (_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE) {
3625 struct dhd_pno_bssid *iter, *next;
3626 _params = &(_pno_state->pno_params_arr[INDEX_OF_HOTLIST_PARAMS]);
3627 p_pfn_bssid = (wl_pfn_bssid_t *)MALLOCZ(dhd->osh,
3628 sizeof(wl_pfn_bssid_t) * _params->params_hotlist.nbssid);
3629 if (p_pfn_bssid == NULL) {
3630 DHD_ERROR(("%s : failed to allocate wl_pfn_bssid_t array"
3631 " (count: %d)",
3632 __FUNCTION__, _params->params_hotlist.nbssid));
3633 err = BCME_ERROR;
3634 _pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
3635 goto exit;
3636 }
3637 i = 0;
3638 /* convert dhd_pno_bssid to wl_pfn_bssid */
3639 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
3640 list_for_each_entry_safe(iter, next,
3641 &_params->params_hotlist.bssid_list, list) {
3642 GCC_DIAGNOSTIC_POP();
3643 memcpy(&p_pfn_bssid[i].macaddr, &iter->macaddr, ETHER_ADDR_LEN);
3644 p_pfn_bssid[i].flags = iter->flags;
3645 i++;
3646 }
3647 err = dhd_pno_set_for_hotlist(dhd, p_pfn_bssid, &_params->params_hotlist);
3648 if (err < 0) {
3649 _pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
3650 DHD_ERROR(("%s : failed to restart hotlist scan(err: %d)\n",
3651 __FUNCTION__, err));
3652 goto exit;
3653 }
3654 }
3655 } else {
3656 err = dhd_pno_clean(dhd);
3657 if (err < 0) {
3658 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
3659 __FUNCTION__, err));
3660 goto exit;
3661 }
3662 }
3663 exit:
3664 _params = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS];
3665 _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_BATCH_MODE);
3666 MFREE(dhd->osh, p_pfn_bssid,
3667 sizeof(wl_pfn_bssid_t) * _params->params_hotlist.nbssid);
3668 return err;
3669 }
3670
3671 int
dhd_pno_set_for_hotlist(dhd_pub_t * dhd,wl_pfn_bssid_t * p_pfn_bssid,struct dhd_pno_hotlist_params * hotlist_params)3672 dhd_pno_set_for_hotlist(dhd_pub_t *dhd, wl_pfn_bssid_t *p_pfn_bssid,
3673 struct dhd_pno_hotlist_params *hotlist_params)
3674 {
3675 int err = BCME_OK;
3676 int i;
3677 uint16 _chan_list[WL_NUMCHANNELS];
3678 int rem_nchan = 0;
3679 int tot_nchan = 0;
3680 int mode = 0;
3681 dhd_pno_params_t *_params;
3682 dhd_pno_params_t *_params2;
3683 struct dhd_pno_bssid *_pno_bssid;
3684 dhd_pno_status_info_t *_pno_state;
3685 NULL_CHECK(dhd, "dhd is NULL", err);
3686 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
3687 NULL_CHECK(hotlist_params, "hotlist_params is NULL", err);
3688 NULL_CHECK(p_pfn_bssid, "p_pfn_bssid is NULL", err);
3689 _pno_state = PNO_GET_PNOSTATE(dhd);
3690 DHD_PNO(("%s enter\n", __FUNCTION__));
3691
3692 if (!dhd_support_sta_mode(dhd)) {
3693 err = BCME_BADOPTION;
3694 goto exit;
3695 }
3696 if (!WLS_SUPPORTED(_pno_state)) {
3697 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
3698 err = BCME_UNSUPPORTED;
3699 goto exit;
3700 }
3701 _params = &_pno_state->pno_params_arr[INDEX_OF_HOTLIST_PARAMS];
3702 if (!(_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE)) {
3703 _pno_state->pno_mode |= DHD_PNO_HOTLIST_MODE;
3704 err = _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_HOTLIST_MODE);
3705 if (err < 0) {
3706 DHD_ERROR(("%s : failed to call _dhd_pno_reinitialize_prof\n",
3707 __FUNCTION__));
3708 goto exit;
3709 }
3710 }
3711 _params->params_batch.nchan = hotlist_params->nchan;
3712 _params->params_batch.scan_fr = hotlist_params->scan_fr;
3713 if (hotlist_params->nchan)
3714 memcpy(_params->params_hotlist.chan_list, hotlist_params->chan_list,
3715 sizeof(_params->params_hotlist.chan_list));
3716 memset(_chan_list, 0, sizeof(_chan_list));
3717
3718 rem_nchan = ARRAYSIZE(hotlist_params->chan_list) - hotlist_params->nchan;
3719 if (hotlist_params->band == WLC_BAND_2G || hotlist_params->band == WLC_BAND_5G) {
3720 /* get a valid channel list based on band B or A */
3721 err = _dhd_pno_get_channels(dhd,
3722 &_params->params_hotlist.chan_list[hotlist_params->nchan],
3723 &rem_nchan, hotlist_params->band, FALSE);
3724 if (err < 0) {
3725 DHD_ERROR(("%s: failed to get valid channel list(band : %d)\n",
3726 __FUNCTION__, hotlist_params->band));
3727 goto exit;
3728 }
3729 /* now we need to update nchan because rem_chan has valid channel count */
3730 _params->params_hotlist.nchan += rem_nchan;
3731 /* need to sort channel list */
3732 sort(_params->params_hotlist.chan_list, _params->params_hotlist.nchan,
3733 sizeof(_params->params_hotlist.chan_list[0]), _dhd_pno_cmpfunc, NULL);
3734 }
3735 #ifdef PNO_DEBUG
3736 {
3737 int i;
3738 DHD_PNO(("Channel list : "));
3739 for (i = 0; i < _params->params_batch.nchan; i++) {
3740 DHD_PNO(("%d ", _params->params_batch.chan_list[i]));
3741 }
3742 DHD_PNO(("\n"));
3743 }
3744 #endif // endif
3745 if (_params->params_hotlist.nchan) {
3746 /* copy the channel list into local array */
3747 memcpy(_chan_list, _params->params_hotlist.chan_list,
3748 sizeof(_chan_list));
3749 tot_nchan = _params->params_hotlist.nchan;
3750 }
3751 if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
3752 DHD_PNO(("PNO SSID is on progress in firmware\n"));
3753 /* store current pno_mode before disabling pno */
3754 mode = _pno_state->pno_mode;
3755 err = _dhd_pno_enable(dhd, PNO_OFF);
3756 if (err < 0) {
3757 DHD_ERROR(("%s : failed to disable PNO\n", __FUNCTION__));
3758 goto exit;
3759 }
3760 /* restore the previous mode */
3761 _pno_state->pno_mode = mode;
3762 /* Use the superset for channelist between two mode */
3763 _params2 = &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS]);
3764 if (_params2->params_legacy.nchan > 0 &&
3765 _params->params_hotlist.nchan > 0) {
3766 err = _dhd_pno_chan_merge(_chan_list, &tot_nchan,
3767 &_params2->params_legacy.chan_list[0],
3768 _params2->params_legacy.nchan,
3769 &_params->params_hotlist.chan_list[0],
3770 _params->params_hotlist.nchan);
3771 if (err < 0) {
3772 DHD_ERROR(("%s : failed to merge channel list"
3773 "between legacy and hotlist\n",
3774 __FUNCTION__));
3775 goto exit;
3776 }
3777 }
3778
3779 }
3780
3781 INIT_LIST_HEAD(&(_params->params_hotlist.bssid_list));
3782
3783 err = _dhd_pno_add_bssid(dhd, p_pfn_bssid, hotlist_params->nbssid);
3784 if (err < 0) {
3785 DHD_ERROR(("%s : failed to call _dhd_pno_add_bssid(err :%d)\n",
3786 __FUNCTION__, err));
3787 goto exit;
3788 }
3789 if ((err = _dhd_pno_set(dhd, _params, DHD_PNO_HOTLIST_MODE)) < 0) {
3790 DHD_ERROR(("%s : failed to set call pno_set (err %d) in firmware\n",
3791 __FUNCTION__, err));
3792 goto exit;
3793 }
3794 if (tot_nchan > 0) {
3795 if ((err = _dhd_pno_cfg(dhd, _chan_list, tot_nchan)) < 0) {
3796 DHD_ERROR(("%s : failed to set call pno_cfg (err %d) in firmware\n",
3797 __FUNCTION__, err));
3798 goto exit;
3799 }
3800 }
3801 for (i = 0; i < hotlist_params->nbssid; i++) {
3802 _pno_bssid = (struct dhd_pno_bssid *)MALLOCZ(dhd->osh,
3803 sizeof(struct dhd_pno_bssid));
3804 NULL_CHECK(_pno_bssid, "_pfn_bssid is NULL", err);
3805 memcpy(&_pno_bssid->macaddr, &p_pfn_bssid[i].macaddr, ETHER_ADDR_LEN);
3806 _pno_bssid->flags = p_pfn_bssid[i].flags;
3807 list_add_tail(&_pno_bssid->list, &_params->params_hotlist.bssid_list);
3808 }
3809 _params->params_hotlist.nbssid = hotlist_params->nbssid;
3810 if (_pno_state->pno_status == DHD_PNO_DISABLED) {
3811 if ((err = _dhd_pno_enable(dhd, PNO_ON)) < 0)
3812 DHD_ERROR(("%s : failed to enable PNO\n", __FUNCTION__));
3813 }
3814 exit:
3815 /* clear mode in case of error */
3816 if (err < 0)
3817 _pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
3818 return err;
3819 }
3820
3821 int
dhd_pno_stop_for_hotlist(dhd_pub_t * dhd)3822 dhd_pno_stop_for_hotlist(dhd_pub_t *dhd)
3823 {
3824 int err = BCME_OK;
3825 uint32 mode = 0;
3826 dhd_pno_status_info_t *_pno_state;
3827 dhd_pno_params_t *_params;
3828 NULL_CHECK(dhd, "dhd is NULL", err);
3829 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
3830 _pno_state = PNO_GET_PNOSTATE(dhd);
3831
3832 if (!WLS_SUPPORTED(_pno_state)) {
3833 DHD_ERROR(("%s : wifi location service is not supported\n",
3834 __FUNCTION__));
3835 err = BCME_UNSUPPORTED;
3836 goto exit;
3837 }
3838
3839 if (!(_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE)) {
3840 DHD_ERROR(("%s : Hotlist MODE is not enabled\n",
3841 __FUNCTION__));
3842 goto exit;
3843 }
3844 _pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
3845
3846 if (_pno_state->pno_mode & (DHD_PNO_LEGACY_MODE | DHD_PNO_BATCH_MODE)) {
3847 /* retrieve the batching data from firmware into host */
3848 dhd_pno_get_for_batch(dhd, NULL, 0, PNO_STATUS_DISABLE);
3849 /* save current pno_mode before calling dhd_pno_clean */
3850 mode = _pno_state->pno_mode;
3851 err = dhd_pno_clean(dhd);
3852 if (err < 0) {
3853 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
3854 __FUNCTION__, err));
3855 goto exit;
3856 }
3857 /* restore previos pno mode */
3858 _pno_state->pno_mode = mode;
3859 if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
3860 /* restart Legacy PNO Scan */
3861 struct dhd_pno_legacy_params *_params_legacy;
3862 _params_legacy =
3863 &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS].params_legacy);
3864 err = dhd_pno_set_legacy_pno(dhd, _params_legacy->scan_fr,
3865 _params_legacy->pno_repeat, _params_legacy->pno_freq_expo_max,
3866 _params_legacy->chan_list, _params_legacy->nchan);
3867 if (err < 0) {
3868 DHD_ERROR(("%s : failed to restart legacy PNO scan(err: %d)\n",
3869 __FUNCTION__, err));
3870 goto exit;
3871 }
3872 } else if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
3873 /* restart Batching Scan */
3874 _params = &(_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS]);
3875 /* restart BATCH SCAN */
3876 err = dhd_pno_set_for_batch(dhd, &_params->params_batch);
3877 if (err < 0) {
3878 _pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
3879 DHD_ERROR(("%s : failed to restart batch scan(err: %d)\n",
3880 __FUNCTION__, err));
3881 goto exit;
3882 }
3883 }
3884 } else {
3885 err = dhd_pno_clean(dhd);
3886 if (err < 0) {
3887 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
3888 __FUNCTION__, err));
3889 goto exit;
3890 }
3891 }
3892 exit:
3893 return err;
3894 }
3895
3896 #ifdef GSCAN_SUPPORT
3897 int
dhd_retreive_batch_scan_results(dhd_pub_t * dhd)3898 dhd_retreive_batch_scan_results(dhd_pub_t *dhd)
3899 {
3900 int err = BCME_OK;
3901 dhd_pno_status_info_t *_pno_state;
3902 dhd_pno_params_t *_params;
3903 struct dhd_pno_batch_params *params_batch;
3904
3905 NULL_CHECK(dhd, "dhd is NULL", err);
3906 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
3907 _pno_state = PNO_GET_PNOSTATE(dhd);
3908 _params = &_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS];
3909
3910 params_batch = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS].params_batch;
3911 if (_params->params_gscan.get_batch_flag == GSCAN_BATCH_RETRIEVAL_COMPLETE) {
3912 DHD_PNO(("Retreive batch results\n"));
3913 params_batch->get_batch.buf = NULL;
3914 params_batch->get_batch.bufsize = 0;
3915 params_batch->get_batch.reason = PNO_STATUS_EVENT;
3916 _params->params_gscan.get_batch_flag = GSCAN_BATCH_RETRIEVAL_IN_PROGRESS;
3917 smp_wmb();
3918 schedule_work(&_pno_state->work);
3919 } else {
3920 DHD_PNO(("%s : WLC_E_PFN_BEST_BATCHING retrieval"
3921 "already in progress, will skip\n", __FUNCTION__));
3922 err = BCME_ERROR;
3923 }
3924
3925 return err;
3926 }
3927
3928 void
dhd_gscan_hotlist_cache_cleanup(dhd_pub_t * dhd,hotlist_type_t type)3929 dhd_gscan_hotlist_cache_cleanup(dhd_pub_t *dhd, hotlist_type_t type)
3930 {
3931 dhd_pno_status_info_t *_pno_state = PNO_GET_PNOSTATE(dhd);
3932 struct dhd_pno_gscan_params *gscan_params;
3933 gscan_results_cache_t *iter, *tmp;
3934
3935 if (!_pno_state) {
3936 return;
3937 }
3938 gscan_params = &(_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS].params_gscan);
3939
3940 if (type == HOTLIST_FOUND) {
3941 iter = gscan_params->gscan_hotlist_found;
3942 gscan_params->gscan_hotlist_found = NULL;
3943 } else {
3944 iter = gscan_params->gscan_hotlist_lost;
3945 gscan_params->gscan_hotlist_lost = NULL;
3946 }
3947
3948 while (iter) {
3949 tmp = iter->next;
3950 MFREE(dhd->osh, iter,
3951 ((iter->tot_count - 1) * sizeof(wifi_gscan_result_t))
3952 + sizeof(gscan_results_cache_t));
3953 iter = tmp;
3954 }
3955
3956 return;
3957 }
3958
3959 void *
dhd_process_full_gscan_result(dhd_pub_t * dhd,const void * data,uint32 len,int * size)3960 dhd_process_full_gscan_result(dhd_pub_t *dhd, const void *data, uint32 len, int *size)
3961 {
3962 wl_bss_info_t *bi = NULL;
3963 wl_gscan_result_t *gscan_result;
3964 wifi_gscan_full_result_t *result = NULL;
3965 u32 bi_length = 0;
3966 uint8 channel;
3967 uint32 mem_needed;
3968 struct osl_timespec ts;
3969 u32 bi_ie_length = 0;
3970 u32 bi_ie_offset = 0;
3971
3972 *size = 0;
3973 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
3974 gscan_result = (wl_gscan_result_t *)data;
3975 GCC_DIAGNOSTIC_POP();
3976 if (!gscan_result) {
3977 DHD_ERROR(("Invalid gscan result (NULL pointer)\n"));
3978 goto exit;
3979 }
3980
3981 if ((len < sizeof(*gscan_result)) ||
3982 (len < dtoh32(gscan_result->buflen)) ||
3983 (dtoh32(gscan_result->buflen) >
3984 (sizeof(*gscan_result) + WL_SCAN_IE_LEN_MAX))) {
3985 DHD_ERROR(("%s: invalid gscan buflen:%u\n", __FUNCTION__,
3986 dtoh32(gscan_result->buflen)));
3987 goto exit;
3988 }
3989
3990 bi = &gscan_result->bss_info[0].info;
3991 bi_length = dtoh32(bi->length);
3992 if (bi_length != (dtoh32(gscan_result->buflen) -
3993 WL_GSCAN_RESULTS_FIXED_SIZE - WL_GSCAN_INFO_FIXED_FIELD_SIZE)) {
3994 DHD_ERROR(("Invalid bss_info length %d: ignoring\n", bi_length));
3995 goto exit;
3996 }
3997 bi_ie_offset = dtoh32(bi->ie_offset);
3998 bi_ie_length = dtoh32(bi->ie_length);
3999 if ((bi_ie_offset + bi_ie_length) > bi_length) {
4000 DHD_ERROR(("%s: Invalid ie_length:%u or ie_offset:%u\n",
4001 __FUNCTION__, bi_ie_length, bi_ie_offset));
4002 goto exit;
4003 }
4004 if (bi->SSID_len > DOT11_MAX_SSID_LEN) {
4005 DHD_ERROR(("%s: Invalid SSID length:%u\n", __FUNCTION__, bi->SSID_len));
4006 goto exit;
4007 }
4008
4009 mem_needed = OFFSETOF(wifi_gscan_full_result_t, ie_data) + bi->ie_length;
4010 result = (wifi_gscan_full_result_t *)MALLOC(dhd->osh, mem_needed);
4011 if (!result) {
4012 DHD_ERROR(("%s Cannot malloc scan result buffer %d bytes\n",
4013 __FUNCTION__, mem_needed));
4014 goto exit;
4015 }
4016
4017 result->scan_ch_bucket = gscan_result->scan_ch_bucket;
4018 memcpy(result->fixed.ssid, bi->SSID, bi->SSID_len);
4019 result->fixed.ssid[bi->SSID_len] = '\0';
4020 channel = wf_chspec_ctlchan(bi->chanspec);
4021 result->fixed.channel = wf_channel2mhz(channel,
4022 (channel <= CH_MAX_2G_CHANNEL?
4023 WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G));
4024 result->fixed.rssi = (int32) bi->RSSI;
4025 result->fixed.rtt = 0;
4026 result->fixed.rtt_sd = 0;
4027 osl_get_monotonic_boottime(&ts);
4028 result->fixed.ts = (uint64) TIMESPEC_TO_US(ts);
4029 result->fixed.beacon_period = dtoh16(bi->beacon_period);
4030 result->fixed.capability = dtoh16(bi->capability);
4031 result->ie_length = bi_ie_length;
4032 memcpy(&result->fixed.macaddr, &bi->BSSID, ETHER_ADDR_LEN);
4033 memcpy(result->ie_data, ((uint8 *)bi + bi_ie_offset), bi_ie_length);
4034 *size = mem_needed;
4035 exit:
4036 return result;
4037 }
4038
4039 void *
dhd_pno_process_epno_result(dhd_pub_t * dhd,const void * data,uint32 event,int * size)4040 dhd_pno_process_epno_result(dhd_pub_t *dhd, const void *data, uint32 event, int *size)
4041 {
4042 dhd_epno_results_t *results = NULL;
4043 dhd_pno_status_info_t *_pno_state = PNO_GET_PNOSTATE(dhd);
4044 struct dhd_pno_gscan_params *gscan_params;
4045 uint32 count, mem_needed = 0, i;
4046 uint8 ssid[DOT11_MAX_SSID_LEN + 1];
4047 struct ether_addr *bssid;
4048
4049 *size = 0;
4050 if (!_pno_state)
4051 return NULL;
4052 gscan_params = &(_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS].params_gscan);
4053
4054 if (event == WLC_E_PFN_NET_FOUND || event == WLC_E_PFN_NET_LOST) {
4055 wl_pfn_scanresults_v1_t *pfn_result = (wl_pfn_scanresults_v1_t *)data;
4056 wl_pfn_scanresults_v2_t *pfn_result_v2 = (wl_pfn_scanresults_v2_t *)data;
4057 wl_pfn_net_info_v1_t *net;
4058 wl_pfn_net_info_v2_t *net_v2;
4059
4060 if (pfn_result->version == PFN_SCANRESULT_VERSION_V1) {
4061 if ((pfn_result->count == 0) || (pfn_result->count > EVENT_MAX_NETCNT_V1)) {
4062 DHD_ERROR(("%s event %d: wrong pfn v1 results count %d\n",
4063 __FUNCTION__, event, pfn_result->count));
4064 return NULL;
4065 }
4066 count = pfn_result->count;
4067 mem_needed = sizeof(dhd_epno_results_t) * count;
4068 results = (dhd_epno_results_t *)MALLOC(dhd->osh, mem_needed);
4069 if (!results) {
4070 DHD_ERROR(("%s: Can't malloc %d bytes for results\n", __FUNCTION__,
4071 mem_needed));
4072 return NULL;
4073 }
4074 for (i = 0; i < count; i++) {
4075 net = &pfn_result->netinfo[i];
4076 results[i].rssi = net->RSSI;
4077 results[i].channel = wf_channel2mhz(net->pfnsubnet.channel,
4078 (net->pfnsubnet.channel <= CH_MAX_2G_CHANNEL ?
4079 WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G));
4080 results[i].flags = (event == WLC_E_PFN_NET_FOUND) ?
4081 WL_PFN_SSID_EXT_FOUND: WL_PFN_SSID_EXT_LOST;
4082 results[i].ssid_len = min(net->pfnsubnet.SSID_len,
4083 (uint8)DOT11_MAX_SSID_LEN);
4084 bssid = &results[i].bssid;
4085 memcpy(bssid, &net->pfnsubnet.BSSID, ETHER_ADDR_LEN);
4086 if (!net->pfnsubnet.SSID_len) {
4087 DHD_ERROR(("%s: Gscan results indexing is not"
4088 " supported in version 1 \n", __FUNCTION__));
4089 MFREE(dhd->osh, results, mem_needed);
4090 return NULL;
4091 } else {
4092 memcpy(results[i].ssid, net->pfnsubnet.SSID,
4093 results[i].ssid_len);
4094 }
4095 memcpy(ssid, results[i].ssid, results[i].ssid_len);
4096 ssid[results[i].ssid_len] = '\0';
4097 DHD_PNO(("ssid - %s bssid "MACDBG" ch %d rssi %d flags %d\n",
4098 ssid, MAC2STRDBG(bssid->octet), results[i].channel,
4099 results[i].rssi, results[i].flags));
4100 }
4101 } else if (pfn_result_v2->version == PFN_SCANRESULT_VERSION_V2) {
4102 if ((pfn_result->count == 0) || (pfn_result->count > EVENT_MAX_NETCNT_V2)) {
4103 DHD_ERROR(("%s event %d: wrong pfn v2 results count %d\n",
4104 __FUNCTION__, event, pfn_result->count));
4105 return NULL;
4106 }
4107 count = pfn_result_v2->count;
4108 mem_needed = sizeof(dhd_epno_results_t) * count;
4109 results = (dhd_epno_results_t *)MALLOC(dhd->osh, mem_needed);
4110 if (!results) {
4111 DHD_ERROR(("%s: Can't malloc %d bytes for results\n", __FUNCTION__,
4112 mem_needed));
4113 return NULL;
4114 }
4115 for (i = 0; i < count; i++) {
4116 net_v2 = &pfn_result_v2->netinfo[i];
4117 results[i].rssi = net_v2->RSSI;
4118 results[i].channel = wf_channel2mhz(net_v2->pfnsubnet.channel,
4119 (net_v2->pfnsubnet.channel <= CH_MAX_2G_CHANNEL ?
4120 WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G));
4121 results[i].flags = (event == WLC_E_PFN_NET_FOUND) ?
4122 WL_PFN_SSID_EXT_FOUND: WL_PFN_SSID_EXT_LOST;
4123 results[i].ssid_len = min(net_v2->pfnsubnet.SSID_len,
4124 (uint8)DOT11_MAX_SSID_LEN);
4125 bssid = &results[i].bssid;
4126 memcpy(bssid, &net_v2->pfnsubnet.BSSID, ETHER_ADDR_LEN);
4127 if (!net_v2->pfnsubnet.SSID_len) {
4128 dhd_pno_idx_to_ssid(gscan_params, &results[i],
4129 net_v2->pfnsubnet.u.index);
4130 } else {
4131 memcpy(results[i].ssid, net_v2->pfnsubnet.u.SSID,
4132 results[i].ssid_len);
4133 }
4134 memcpy(ssid, results[i].ssid, results[i].ssid_len);
4135 ssid[results[i].ssid_len] = '\0';
4136 DHD_PNO(("ssid - %s bssid "MACDBG" ch %d rssi %d flags %d\n",
4137 ssid, MAC2STRDBG(bssid->octet), results[i].channel,
4138 results[i].rssi, results[i].flags));
4139 }
4140 } else {
4141 DHD_ERROR(("%s event %d: Incorrect version %d , not supported\n",
4142 __FUNCTION__, event, pfn_result->version));
4143 return NULL;
4144 }
4145 }
4146 *size = mem_needed;
4147 return results;
4148 }
4149
4150 void *
dhd_handle_hotlist_scan_evt(dhd_pub_t * dhd,const void * event_data,int * send_evt_bytes,hotlist_type_t type,u32 * buf_len)4151 dhd_handle_hotlist_scan_evt(dhd_pub_t *dhd, const void *event_data,
4152 int *send_evt_bytes, hotlist_type_t type, u32 *buf_len)
4153 {
4154 void *ptr = NULL;
4155 dhd_pno_status_info_t *_pno_state = PNO_GET_PNOSTATE(dhd);
4156 struct dhd_pno_gscan_params *gscan_params;
4157 wl_pfn_scanresults_v1_t *results_v1 = (wl_pfn_scanresults_v1_t *)event_data;
4158 wl_pfn_scanresults_v2_t *results_v2 = (wl_pfn_scanresults_v2_t *)event_data;
4159 wifi_gscan_result_t *hotlist_found_array;
4160 wl_pfn_net_info_v1_t *pnetinfo;
4161 wl_pfn_net_info_v2_t *pnetinfo_v2;
4162 gscan_results_cache_t *gscan_hotlist_cache;
4163 u32 malloc_size = 0, i, total = 0;
4164 struct osl_timespec tm_spec;
4165 uint16 fwstatus;
4166 uint16 fwcount;
4167
4168 /* Static asserts in _dhd_pno_get_for_batch() above guarantee the v1 and v2
4169 * net_info and subnet_info structures are compatible in size and SSID offset,
4170 * allowing v1 to be safely used in the code below except for lscanresults
4171 * fields themselves (status, count, offset to netinfo).
4172 */
4173
4174 *buf_len = 0;
4175 if (results_v1->version == PFN_SCANRESULTS_VERSION_V1) {
4176 fwstatus = results_v1->status;
4177 fwcount = results_v1->count;
4178 pnetinfo = &results_v1->netinfo[0];
4179
4180 gscan_params = &(_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS].params_gscan);
4181
4182 if (!fwcount || (fwcount > EVENT_MAX_NETCNT_V1)) {
4183 DHD_ERROR(("%s: wrong v1 fwcount:%d\n", __FUNCTION__, fwcount));
4184 *send_evt_bytes = 0;
4185 return ptr;
4186 }
4187
4188 osl_get_monotonic_boottime(&tm_spec);
4189 malloc_size = sizeof(gscan_results_cache_t) +
4190 ((fwcount - 1) * sizeof(wifi_gscan_result_t));
4191 gscan_hotlist_cache = (gscan_results_cache_t *)MALLOC(dhd->osh, malloc_size);
4192 if (!gscan_hotlist_cache) {
4193 DHD_ERROR(("%s Cannot Malloc %d bytes!!\n", __FUNCTION__, malloc_size));
4194 *send_evt_bytes = 0;
4195 return ptr;
4196 }
4197
4198 *buf_len = malloc_size;
4199 if (type == HOTLIST_FOUND) {
4200 gscan_hotlist_cache->next = gscan_params->gscan_hotlist_found;
4201 gscan_params->gscan_hotlist_found = gscan_hotlist_cache;
4202 DHD_PNO(("%s enter, FOUND results count %d\n", __FUNCTION__, fwcount));
4203 } else {
4204 gscan_hotlist_cache->next = gscan_params->gscan_hotlist_lost;
4205 gscan_params->gscan_hotlist_lost = gscan_hotlist_cache;
4206 DHD_PNO(("%s enter, LOST results count %d\n", __FUNCTION__, fwcount));
4207 }
4208
4209 gscan_hotlist_cache->tot_count = fwcount;
4210 gscan_hotlist_cache->tot_consumed = 0;
4211
4212 for (i = 0; i < fwcount; i++, pnetinfo++) {
4213 hotlist_found_array = &gscan_hotlist_cache->results[i];
4214 memset(hotlist_found_array, 0, sizeof(wifi_gscan_result_t));
4215 hotlist_found_array->channel = wf_channel2mhz(pnetinfo->pfnsubnet.channel,
4216 (pnetinfo->pfnsubnet.channel <= CH_MAX_2G_CHANNEL?
4217 WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G));
4218 hotlist_found_array->rssi = (int32) pnetinfo->RSSI;
4219
4220 hotlist_found_array->ts =
4221 convert_fw_rel_time_to_systime(&tm_spec,
4222 (pnetinfo->timestamp * 1000));
4223 if (pnetinfo->pfnsubnet.SSID_len > DOT11_MAX_SSID_LEN) {
4224 DHD_ERROR(("Invalid SSID length %d: trimming it to max\n",
4225 pnetinfo->pfnsubnet.SSID_len));
4226 pnetinfo->pfnsubnet.SSID_len = DOT11_MAX_SSID_LEN;
4227 }
4228 memcpy(hotlist_found_array->ssid, pnetinfo->pfnsubnet.SSID,
4229 pnetinfo->pfnsubnet.SSID_len);
4230 hotlist_found_array->ssid[pnetinfo->pfnsubnet.SSID_len] = '\0';
4231
4232 memcpy(&hotlist_found_array->macaddr, &pnetinfo->pfnsubnet.BSSID,
4233 ETHER_ADDR_LEN);
4234 DHD_PNO(("\t%s "MACDBG" rssi %d\n",
4235 hotlist_found_array->ssid,
4236 MAC2STRDBG(hotlist_found_array->macaddr.octet),
4237 hotlist_found_array->rssi));
4238 }
4239 } else if (results_v2->version == PFN_SCANRESULTS_VERSION_V2) {
4240 fwstatus = results_v2->status;
4241 fwcount = results_v2->count;
4242 pnetinfo_v2 = (wl_pfn_net_info_v2_t*)&results_v2->netinfo[0];
4243
4244 gscan_params = &(_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS].params_gscan);
4245
4246 if (!fwcount || (fwcount > EVENT_MAX_NETCNT_V2)) {
4247 DHD_ERROR(("%s: wrong v2 fwcount:%d\n", __FUNCTION__, fwcount));
4248 *send_evt_bytes = 0;
4249 return ptr;
4250 }
4251
4252 osl_get_monotonic_boottime(&tm_spec);
4253 malloc_size = sizeof(gscan_results_cache_t) +
4254 ((fwcount - 1) * sizeof(wifi_gscan_result_t));
4255 gscan_hotlist_cache =
4256 (gscan_results_cache_t *)MALLOC(dhd->osh, malloc_size);
4257 if (!gscan_hotlist_cache) {
4258 DHD_ERROR(("%s Cannot Malloc %d bytes!!\n", __FUNCTION__, malloc_size));
4259 *send_evt_bytes = 0;
4260 return ptr;
4261 }
4262 *buf_len = malloc_size;
4263 if (type == HOTLIST_FOUND) {
4264 gscan_hotlist_cache->next = gscan_params->gscan_hotlist_found;
4265 gscan_params->gscan_hotlist_found = gscan_hotlist_cache;
4266 DHD_PNO(("%s enter, FOUND results count %d\n", __FUNCTION__, fwcount));
4267 } else {
4268 gscan_hotlist_cache->next = gscan_params->gscan_hotlist_lost;
4269 gscan_params->gscan_hotlist_lost = gscan_hotlist_cache;
4270 DHD_PNO(("%s enter, LOST results count %d\n", __FUNCTION__, fwcount));
4271 }
4272
4273 gscan_hotlist_cache->tot_count = fwcount;
4274 gscan_hotlist_cache->tot_consumed = 0;
4275 gscan_hotlist_cache->scan_ch_bucket = results_v2->scan_ch_bucket;
4276
4277 for (i = 0; i < fwcount; i++, pnetinfo_v2++) {
4278 hotlist_found_array = &gscan_hotlist_cache->results[i];
4279 memset(hotlist_found_array, 0, sizeof(wifi_gscan_result_t));
4280 hotlist_found_array->channel =
4281 wf_channel2mhz(pnetinfo_v2->pfnsubnet.channel,
4282 (pnetinfo_v2->pfnsubnet.channel <= CH_MAX_2G_CHANNEL?
4283 WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G));
4284 hotlist_found_array->rssi = (int32) pnetinfo_v2->RSSI;
4285
4286 hotlist_found_array->ts =
4287 convert_fw_rel_time_to_systime(&tm_spec,
4288 (pnetinfo_v2->timestamp * 1000));
4289 if (pnetinfo_v2->pfnsubnet.SSID_len > DOT11_MAX_SSID_LEN) {
4290 DHD_ERROR(("Invalid SSID length %d: trimming it to max\n",
4291 pnetinfo_v2->pfnsubnet.SSID_len));
4292 pnetinfo_v2->pfnsubnet.SSID_len = DOT11_MAX_SSID_LEN;
4293 }
4294 memcpy(hotlist_found_array->ssid, pnetinfo_v2->pfnsubnet.u.SSID,
4295 pnetinfo_v2->pfnsubnet.SSID_len);
4296 hotlist_found_array->ssid[pnetinfo_v2->pfnsubnet.SSID_len] = '\0';
4297
4298 memcpy(&hotlist_found_array->macaddr, &pnetinfo_v2->pfnsubnet.BSSID,
4299 ETHER_ADDR_LEN);
4300 DHD_PNO(("\t%s "MACDBG" rssi %d\n",
4301 hotlist_found_array->ssid,
4302 MAC2STRDBG(hotlist_found_array->macaddr.octet),
4303 hotlist_found_array->rssi));
4304 }
4305 } else {
4306 DHD_ERROR(("%s: event version %d not supported\n",
4307 __FUNCTION__, results_v1->version));
4308 *send_evt_bytes = 0;
4309 return ptr;
4310 }
4311 if (fwstatus == PFN_COMPLETE) {
4312 ptr = (void *) gscan_hotlist_cache;
4313 while (gscan_hotlist_cache) {
4314 total += gscan_hotlist_cache->tot_count;
4315 gscan_hotlist_cache = gscan_hotlist_cache->next;
4316 }
4317 *send_evt_bytes = total * sizeof(wifi_gscan_result_t);
4318 }
4319
4320 return ptr;
4321 }
4322 #endif /* GSCAN_SUPPORT */
4323
4324 int
dhd_pno_event_handler(dhd_pub_t * dhd,wl_event_msg_t * event,void * event_data)4325 dhd_pno_event_handler(dhd_pub_t *dhd, wl_event_msg_t *event, void *event_data)
4326 {
4327 int err = BCME_OK;
4328 uint event_type;
4329 dhd_pno_status_info_t *_pno_state;
4330 NULL_CHECK(dhd, "dhd is NULL", err);
4331 NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
4332 _pno_state = PNO_GET_PNOSTATE(dhd);
4333 if (!WLS_SUPPORTED(_pno_state)) {
4334 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
4335 err = BCME_UNSUPPORTED;
4336 goto exit;
4337 }
4338 event_type = ntoh32(event->event_type);
4339 DHD_PNO(("%s enter : event_type :%d\n", __FUNCTION__, event_type));
4340 switch (event_type) {
4341 case WLC_E_PFN_BSSID_NET_FOUND:
4342 case WLC_E_PFN_BSSID_NET_LOST:
4343 /* TODO : need to implement event logic using generic netlink */
4344 break;
4345 case WLC_E_PFN_BEST_BATCHING:
4346 #ifndef GSCAN_SUPPORT
4347 {
4348 struct dhd_pno_batch_params *params_batch;
4349 params_batch = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS].params_batch;
4350 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0))
4351 if (!waitqueue_active(&_pno_state->get_batch_done))
4352 #else
4353 if (!waitqueue_active(&_pno_state->get_batch_done.wait))
4354 #endif
4355 {
4356 DHD_PNO(("%s : WLC_E_PFN_BEST_BATCHING\n", __FUNCTION__));
4357 params_batch->get_batch.buf = NULL;
4358 params_batch->get_batch.bufsize = 0;
4359 params_batch->get_batch.reason = PNO_STATUS_EVENT;
4360 schedule_work(&_pno_state->work);
4361 } else
4362 DHD_PNO(("%s : WLC_E_PFN_BEST_BATCHING"
4363 "will skip this event\n", __FUNCTION__));
4364 break;
4365 }
4366 #else
4367 break;
4368 #endif /* !GSCAN_SUPPORT */
4369 default:
4370 DHD_ERROR(("unknown event : %d\n", event_type));
4371 }
4372 exit:
4373 return err;
4374 }
4375
dhd_pno_init(dhd_pub_t * dhd)4376 int dhd_pno_init(dhd_pub_t *dhd)
4377 {
4378 int err = BCME_OK;
4379 dhd_pno_status_info_t *_pno_state;
4380 char *buf = NULL;
4381 NULL_CHECK(dhd, "dhd is NULL", err);
4382 DHD_PNO(("%s enter\n", __FUNCTION__));
4383 UNUSED_PARAMETER(_dhd_pno_suspend);
4384 if (dhd->pno_state)
4385 goto exit;
4386 dhd->pno_state = MALLOC(dhd->osh, sizeof(dhd_pno_status_info_t));
4387 NULL_CHECK(dhd->pno_state, "failed to create dhd_pno_state", err);
4388 memset(dhd->pno_state, 0, sizeof(dhd_pno_status_info_t));
4389 /* need to check whether current firmware support batching and hotlist scan */
4390 _pno_state = PNO_GET_PNOSTATE(dhd);
4391 _pno_state->wls_supported = TRUE;
4392 _pno_state->dhd = dhd;
4393 mutex_init(&_pno_state->pno_mutex);
4394 INIT_WORK(&_pno_state->work, _dhd_pno_get_batch_handler);
4395 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0))
4396 init_waitqueue_head(&_pno_state->get_batch_done);
4397 #else
4398 init_completion(&_pno_state->get_batch_done);
4399 #endif
4400 #ifdef GSCAN_SUPPORT
4401 init_waitqueue_head(&_pno_state->batch_get_wait);
4402 #endif /* GSCAN_SUPPORT */
4403 buf = MALLOC(dhd->osh, WLC_IOCTL_SMLEN);
4404 if (!buf) {
4405 DHD_ERROR((":%s buf alloc err.\n", __FUNCTION__));
4406 return BCME_NOMEM;
4407 }
4408 err = dhd_iovar(dhd, 0, "pfnlbest", NULL, 0, buf, WLC_IOCTL_SMLEN,
4409 FALSE);
4410 if (err == BCME_UNSUPPORTED) {
4411 _pno_state->wls_supported = FALSE;
4412 DHD_INFO(("Current firmware doesn't support"
4413 " Android Location Service\n"));
4414 } else {
4415 DHD_ERROR(("%s: Support Android Location Service\n",
4416 __FUNCTION__));
4417 }
4418 exit:
4419 MFREE(dhd->osh, buf, WLC_IOCTL_SMLEN);
4420 return err;
4421 }
4422
dhd_pno_deinit(dhd_pub_t * dhd)4423 int dhd_pno_deinit(dhd_pub_t *dhd)
4424 {
4425 int err = BCME_OK;
4426 dhd_pno_status_info_t *_pno_state;
4427 dhd_pno_params_t *_params;
4428 NULL_CHECK(dhd, "dhd is NULL", err);
4429
4430 DHD_PNO(("%s enter\n", __FUNCTION__));
4431 _pno_state = PNO_GET_PNOSTATE(dhd);
4432 NULL_CHECK(_pno_state, "pno_state is NULL", err);
4433 /* may need to free legacy ssid_list */
4434 if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
4435 _params = &_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS];
4436 _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_LEGACY_MODE);
4437 }
4438
4439 #ifdef GSCAN_SUPPORT
4440 if (_pno_state->pno_mode & DHD_PNO_GSCAN_MODE) {
4441 _params = &_pno_state->pno_params_arr[INDEX_OF_GSCAN_PARAMS];
4442 mutex_lock(&_pno_state->pno_mutex);
4443 dhd_pno_reset_cfg_gscan(dhd, _params, _pno_state, GSCAN_FLUSH_ALL_CFG);
4444 mutex_unlock(&_pno_state->pno_mutex);
4445 }
4446 #endif /* GSCAN_SUPPORT */
4447
4448 if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
4449 _params = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS];
4450 /* clear resource if the BATCH MODE is on */
4451 _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_BATCH_MODE);
4452 }
4453 cancel_work_sync(&_pno_state->work);
4454 MFREE(dhd->osh, _pno_state, sizeof(dhd_pno_status_info_t));
4455 dhd->pno_state = NULL;
4456 return err;
4457 }
4458 #endif /* PNO_SUPPORT */
4459