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