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