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