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