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