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