• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Broadcom Dongle Host Driver (DHD)
3  * Prefered Network Offload and Wi-Fi Location Service(WLS) code.
4  *
5  * Copyright (C) 1999-2013, Broadcom Corporation
6  *
7  *      Unless you and Broadcom execute a separate written software license
8  * agreement governing use of this software, this software is licensed to you
9  * under the terms of the GNU General Public License version 2 (the "GPL"),
10  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
11  * following added to such license:
12  *
13  *      As a special exception, the copyright holders of this software give you
14  * permission to link this software with independent modules, and to copy and
15  * distribute the resulting executable under terms of your choice, provided that
16  * you also meet, for each linked independent module, the terms and conditions of
17  * the license of that module.  An independent module is a module which is not
18  * derived from this software.  The special exception does not apply to any
19  * modifications of the software.
20  *
21  *      Notwithstanding the above, under no circumstances may you combine this
22  * software in any way with any other Broadcom software provided under a license
23  * other than the GPL, without Broadcom's express prior written consent.
24  *
25  * $Id: dhd_pno.c 420056 2013-08-24 00:53:12Z $
26  */
27 #include <typedefs.h>
28 #include <osl.h>
29 
30 #include <epivers.h>
31 #include <bcmutils.h>
32 
33 #include <bcmendian.h>
34 #include <linuxver.h>
35 #include <linux/init.h>
36 #include <linux/kernel.h>
37 #include <linux/list.h>
38 #include <linux/sort.h>
39 #include <dngl_stats.h>
40 #include <wlioctl.h>
41 
42 #include <proto/bcmevent.h>
43 #include <dhd.h>
44 #include <dhd_pno.h>
45 #include <dhd_dbg.h>
46 
47 #ifdef __BIG_ENDIAN
48 #include <bcmendian.h>
49 #define htod32(i) (bcmswap32(i))
50 #define htod16(i) (bcmswap16(i))
51 #define dtoh32(i) (bcmswap32(i))
52 #define dtoh16(i) (bcmswap16(i))
53 #define htodchanspec(i) htod16(i)
54 #define dtohchanspec(i) dtoh16(i)
55 #else
56 #define htod32(i) (i)
57 #define htod16(i) (i)
58 #define dtoh32(i) (i)
59 #define dtoh16(i) (i)
60 #define htodchanspec(i) (i)
61 #define dtohchanspec(i) (i)
62 #endif /* IL_BIGENDINA */
63 
64 #define NULL_CHECK(p, s, err)  \
65 			do { \
66 				if (!(p)) { \
67 					printf("NULL POINTER (%s) : %s\n", __FUNCTION__, (s)); \
68 					err = BCME_ERROR; \
69 					return err; \
70 				} \
71 			} while (0)
72 #define PNO_GET_PNOSTATE(dhd) ((dhd_pno_status_info_t *)dhd->pno_state)
73 #define PNO_BESTNET_LEN 1024
74 #define PNO_ON 1
75 #define PNO_OFF 0
76 #define CHANNEL_2G_MAX 14
77 #define MAX_NODE_CNT 5
78 #define WLS_SUPPORTED(pno_state) (pno_state->wls_supported == TRUE)
79 #define TIME_DIFF(timestamp1, timestamp2) (abs((uint32)(timestamp1/1000)  \
80 						- (uint32)(timestamp2/1000)))
81 
82 #define ENTRY_OVERHEAD strlen("bssid=\nssid=\nfreq=\nlevel=\nage=\ndist=\ndistSd=\n====")
83 #define TIME_MIN_DIFF 5
84 static wlc_ssid_ext_t * dhd_pno_get_legacy_pno_ssid(dhd_pub_t *dhd,
85  dhd_pno_status_info_t *pno_state);
86 static inline bool
is_dfs(uint16 channel)87 is_dfs(uint16 channel)
88 {
89 	if (channel >= 52 && channel <= 64)			/* class 2 */
90 		return TRUE;
91 	else if (channel >= 100 && channel <= 140)	/* class 4 */
92 		return TRUE;
93 	else
94 		return FALSE;
95 }
96 
97 int
dhd_pno_clean(dhd_pub_t * dhd)98 dhd_pno_clean(dhd_pub_t *dhd)
99 {
100 	int pfn = 0;
101 	int err;
102 	dhd_pno_status_info_t *_pno_state;
103 	NULL_CHECK(dhd, "dhd is NULL", err);
104 	NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
105 	_pno_state = PNO_GET_PNOSTATE(dhd);
106 	DHD_PNO(("%s enter\n", __FUNCTION__));
107 	/* Disable PNO */
108 	err = dhd_iovar(dhd, 0, "pfn", (char *)&pfn, sizeof(pfn), 1);
109 	if (err < 0) {
110 		DHD_ERROR(("%s : failed to execute pfn(error : %d)\n",
111 			__FUNCTION__, err));
112 		goto exit;
113 	}
114 	_pno_state->pno_status = DHD_PNO_DISABLED;
115 	err = dhd_iovar(dhd, 0, "pfnclear", NULL, 0, 1);
116 	if (err < 0) {
117 		DHD_ERROR(("%s : failed to execute pfnclear(error : %d)\n",
118 			__FUNCTION__, err));
119 	}
120 exit:
121 	return err;
122 }
123 
124 static int
_dhd_pno_suspend(dhd_pub_t * dhd)125 _dhd_pno_suspend(dhd_pub_t *dhd)
126 {
127 	int err;
128 	int suspend = 1;
129 	dhd_pno_status_info_t *_pno_state;
130 	NULL_CHECK(dhd, "dhd is NULL", err);
131 	NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
132 
133 	DHD_PNO(("%s enter\n", __FUNCTION__));
134 	_pno_state = PNO_GET_PNOSTATE(dhd);
135 	err = dhd_iovar(dhd, 0, "pfn_suspend", (char *)&suspend, sizeof(suspend), 1);
136 	if (err < 0) {
137 		DHD_ERROR(("%s : failed to suspend pfn(error :%d)\n", __FUNCTION__, err));
138 		goto exit;
139 
140 	}
141 	_pno_state->pno_status = DHD_PNO_SUSPEND;
142 exit:
143 	return err;
144 }
145 static int
_dhd_pno_enable(dhd_pub_t * dhd,int enable)146 _dhd_pno_enable(dhd_pub_t *dhd, int enable)
147 {
148 	int err = BCME_OK;
149 	dhd_pno_status_info_t *_pno_state;
150 	NULL_CHECK(dhd, "dhd is NULL", err);
151 	NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
152 	_pno_state = PNO_GET_PNOSTATE(dhd);
153 	DHD_PNO(("%s enter\n", __FUNCTION__));
154 
155 	if (enable & 0xfffe) {
156 		DHD_ERROR(("%s invalid value\n", __FUNCTION__));
157 		err = BCME_BADARG;
158 		goto exit;
159 	}
160 	if (!dhd_support_sta_mode(dhd)) {
161 		DHD_ERROR(("PNO is not allowed for non-STA mode"));
162 		err = BCME_BADOPTION;
163 		goto exit;
164 	}
165 	if (enable) {
166 		if ((_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) &&
167 			dhd_is_associated(dhd, NULL, NULL)) {
168 			DHD_ERROR(("%s Legacy PNO mode cannot be enabled "
169 				"in assoc mode , ignore it\n", __FUNCTION__));
170 			err = BCME_BADOPTION;
171 			goto exit;
172 		}
173 	}
174 	/* Enable/Disable PNO */
175 	err = dhd_iovar(dhd, 0, "pfn", (char *)&enable, sizeof(enable), 1);
176 	if (err < 0) {
177 		DHD_ERROR(("%s : failed to execute pfn_set - %d\n", __FUNCTION__, err));
178 		goto exit;
179 	}
180 	_pno_state->pno_status = (enable)?
181 		DHD_PNO_ENABLED : DHD_PNO_DISABLED;
182 	if (!enable)
183 		_pno_state->pno_mode = DHD_PNO_NONE_MODE;
184 
185 	DHD_PNO(("%s set pno as %s\n",
186 		__FUNCTION__, enable ? "Enable" : "Disable"));
187 exit:
188 	return err;
189 }
190 
191 static int
_dhd_pno_set(dhd_pub_t * dhd,const dhd_pno_params_t * pno_params,dhd_pno_mode_t mode)192 _dhd_pno_set(dhd_pub_t *dhd, const dhd_pno_params_t *pno_params, dhd_pno_mode_t mode)
193 {
194 	int err = BCME_OK;
195 	wl_pfn_param_t pfn_param;
196 	dhd_pno_params_t *_params;
197 	dhd_pno_status_info_t *_pno_state;
198 	bool combined_scan = FALSE;
199 	DHD_PNO(("%s enter\n", __FUNCTION__));
200 
201 	NULL_CHECK(dhd, "dhd is NULL", err);
202 	NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
203 	_pno_state = PNO_GET_PNOSTATE(dhd);
204 
205 	memset(&pfn_param, 0, sizeof(pfn_param));
206 
207 	/* set pfn parameters */
208 	pfn_param.version = htod32(PFN_VERSION);
209 	pfn_param.flags = ((PFN_LIST_ORDER << SORT_CRITERIA_BIT) |
210 		(ENABLE << IMMEDIATE_SCAN_BIT) | (ENABLE << REPORT_SEPERATELY_BIT));
211 	if (mode == DHD_PNO_LEGACY_MODE) {
212 		/* check and set extra pno params */
213 		if ((pno_params->params_legacy.pno_repeat != 0) ||
214 			(pno_params->params_legacy.pno_freq_expo_max != 0)) {
215 			pfn_param.flags |= htod16(ENABLE << ENABLE_ADAPTSCAN_BIT);
216 			pfn_param.repeat = (uchar) (pno_params->params_legacy.pno_repeat);
217 			pfn_param.exp = (uchar) (pno_params->params_legacy.pno_freq_expo_max);
218 		}
219 		/* set up pno scan fr */
220 		if (pno_params->params_legacy.scan_fr != 0)
221 			pfn_param.scan_freq = htod32(pno_params->params_legacy.scan_fr);
222 		if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
223 			DHD_PNO(("will enable combined scan with BATCHIG SCAN MODE\n"));
224 			mode |= DHD_PNO_BATCH_MODE;
225 			combined_scan = TRUE;
226 		} else if (_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE) {
227 			DHD_PNO(("will enable combined scan with HOTLIST SCAN MODE\n"));
228 			mode |= DHD_PNO_HOTLIST_MODE;
229 			combined_scan = TRUE;
230 		}
231 	}
232 	if (mode & (DHD_PNO_BATCH_MODE | DHD_PNO_HOTLIST_MODE)) {
233 		/* Scan frequency of 30 sec */
234 		pfn_param.scan_freq = htod32(30);
235 		/* slow adapt scan is off by default */
236 		pfn_param.slow_freq = htod32(0);
237 		/* RSSI margin of 30 dBm */
238 		pfn_param.rssi_margin = htod16(30);
239 		/* Network timeout 60 sec */
240 		pfn_param.lost_network_timeout = htod32(60);
241 		/* best n = 2 by default */
242 		pfn_param.bestn = DEFAULT_BESTN;
243 		/* mscan m=0 by default, so not record best networks by default */
244 		pfn_param.mscan = DEFAULT_MSCAN;
245 		/*  default repeat = 10 */
246 		pfn_param.repeat = DEFAULT_REPEAT;
247 		/* by default, maximum scan interval = 2^2
248 		 * scan_freq when adaptive scan is turned on
249 		 */
250 		pfn_param.exp = DEFAULT_EXP;
251 		if (mode == DHD_PNO_BATCH_MODE) {
252 			/* In case of BATCH SCAN */
253 			if (pno_params->params_batch.bestn)
254 				pfn_param.bestn = pno_params->params_batch.bestn;
255 			if (pno_params->params_batch.scan_fr)
256 				pfn_param.scan_freq = htod32(pno_params->params_batch.scan_fr);
257 			if (pno_params->params_batch.mscan)
258 				pfn_param.mscan = pno_params->params_batch.mscan;
259 			/* enable broadcast scan */
260 			pfn_param.flags |= (ENABLE << ENABLE_BD_SCAN_BIT);
261 		} else if (mode == DHD_PNO_HOTLIST_MODE) {
262 			/* In case of HOTLIST SCAN */
263 			if (pno_params->params_hotlist.scan_fr)
264 				pfn_param.scan_freq = htod32(pno_params->params_hotlist.scan_fr);
265 			pfn_param.bestn = 0;
266 			pfn_param.repeat = 0;
267 			/* enable broadcast scan */
268 			pfn_param.flags |= (ENABLE << ENABLE_BD_SCAN_BIT);
269 		}
270 		if (combined_scan) {
271 			/* Disable Adaptive Scan */
272 			pfn_param.flags &= ~(htod16(ENABLE << ENABLE_ADAPTSCAN_BIT));
273 			pfn_param.flags |= (ENABLE << ENABLE_BD_SCAN_BIT);
274 			pfn_param.repeat = 0;
275 			pfn_param.exp = 0;
276 			if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
277 				/* In case of Legacy PNO + BATCH SCAN */
278 				_params = &(_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS]);
279 				if (_params->params_batch.bestn)
280 					pfn_param.bestn = _params->params_batch.bestn;
281 				if (_params->params_batch.scan_fr)
282 					pfn_param.scan_freq = htod32(_params->params_batch.scan_fr);
283 				if (_params->params_batch.mscan)
284 					pfn_param.mscan = _params->params_batch.mscan;
285 			} else if (_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE) {
286 				/* In case of Legacy PNO + HOTLIST SCAN */
287 				_params = &(_pno_state->pno_params_arr[INDEX_OF_HOTLIST_PARAMS]);
288 				if (_params->params_hotlist.scan_fr)
289 				pfn_param.scan_freq = htod32(_params->params_hotlist.scan_fr);
290 				pfn_param.bestn = 0;
291 				pfn_param.repeat = 0;
292 			}
293 		}
294 	}
295 	if (pfn_param.scan_freq < htod32(PNO_SCAN_MIN_FW_SEC) ||
296 		pfn_param.scan_freq > htod32(PNO_SCAN_MAX_FW_SEC)) {
297 		DHD_ERROR(("%s pno freq(%d sec) is not valid \n",
298 			__FUNCTION__, PNO_SCAN_MIN_FW_SEC));
299 		err = BCME_BADARG;
300 		goto exit;
301 	}
302 	if (mode == DHD_PNO_BATCH_MODE) {
303 		int _tmp = pfn_param.bestn;
304 		/* set bestn to calculate the max mscan which firmware supports */
305 		err = dhd_iovar(dhd, 0, "pfnmem", (char *)&_tmp, sizeof(_tmp), 1);
306 		if (err < 0) {
307 			DHD_ERROR(("%s : failed to set pfnmem\n", __FUNCTION__));
308 			goto exit;
309 		}
310 		/* get max mscan which the firmware supports */
311 		err = dhd_iovar(dhd, 0, "pfnmem", (char *)&_tmp, sizeof(_tmp), 0);
312 		if (err < 0) {
313 			DHD_ERROR(("%s : failed to get pfnmem\n", __FUNCTION__));
314 			goto exit;
315 		}
316 		DHD_PNO((" returned mscan : %d, set bestn : %d\n", _tmp, pfn_param.bestn));
317 		pfn_param.mscan = MIN(pfn_param.mscan, _tmp);
318 	}
319 	err = dhd_iovar(dhd, 0, "pfn_set", (char *)&pfn_param, sizeof(pfn_param), 1);
320 	if (err < 0) {
321 		DHD_ERROR(("%s : failed to execute pfn_set %d\n", __FUNCTION__, err));
322 		goto exit;
323 	}
324 	/* need to return mscan if this is for batch scan instead of err */
325 	err = (mode == DHD_PNO_BATCH_MODE)? pfn_param.mscan : err;
326 exit:
327 	return err;
328 }
329 static int
_dhd_pno_add_ssid(dhd_pub_t * dhd,wlc_ssid_ext_t * ssids_list,int nssid)330 _dhd_pno_add_ssid(dhd_pub_t *dhd, wlc_ssid_ext_t* ssids_list, int nssid)
331 {
332 	int err = BCME_OK;
333 	int i = 0;
334 	wl_pfn_t pfn_element;
335 	NULL_CHECK(dhd, "dhd is NULL", err);
336 	if (nssid) {
337 		NULL_CHECK(ssids_list, "ssid list is NULL", err);
338 	}
339 	memset(&pfn_element, 0, sizeof(pfn_element));
340 	{
341 		int j;
342 		for (j = 0; j < nssid; j++) {
343 			DHD_PNO(("%d: scan  for  %s size = %d hidden = %d\n", j,
344 				ssids_list[j].SSID, ssids_list[j].SSID_len, ssids_list[j].hidden));
345 		}
346 	}
347 	/* Check for broadcast ssid */
348 	for (i = 0; i < nssid; i++) {
349 		if (!ssids_list[i].SSID_len) {
350 			DHD_ERROR(("%d: Broadcast SSID is ilegal for PNO setting\n", i));
351 			err = BCME_ERROR;
352 			goto exit;
353 		}
354 	}
355 	/* set all pfn ssid */
356 	for (i = 0; i < nssid; i++) {
357 		pfn_element.infra = htod32(DOT11_BSSTYPE_INFRASTRUCTURE);
358 		pfn_element.auth = (DOT11_OPEN_SYSTEM);
359 		pfn_element.wpa_auth = htod32(WPA_AUTH_PFN_ANY);
360 		pfn_element.wsec = htod32(0);
361 		pfn_element.infra = htod32(1);
362 		if (ssids_list[i].hidden)
363 			pfn_element.flags = htod32(ENABLE << WL_PFN_HIDDEN_BIT);
364 		else
365 			pfn_element.flags = 0;
366 		memcpy((char *)pfn_element.ssid.SSID, ssids_list[i].SSID,
367 			ssids_list[i].SSID_len);
368 		pfn_element.ssid.SSID_len = ssids_list[i].SSID_len;
369 		err = dhd_iovar(dhd, 0, "pfn_add", (char *)&pfn_element,
370 			sizeof(pfn_element), 1);
371 		if (err < 0) {
372 			DHD_ERROR(("%s : failed to execute pfn_add\n", __FUNCTION__));
373 			goto exit;
374 		}
375 	}
376 exit:
377 	return err;
378 }
379 /* qsort compare function */
380 static int
_dhd_pno_cmpfunc(const void * a,const void * b)381 _dhd_pno_cmpfunc(const void *a, const void *b)
382 {
383 	return (*(uint16*)a - *(uint16*)b);
384 }
385 static int
_dhd_pno_chan_merge(uint16 * d_chan_list,int * nchan,uint16 * chan_list1,int nchan1,uint16 * chan_list2,int nchan2)386 _dhd_pno_chan_merge(uint16 *d_chan_list, int *nchan,
387 	uint16 *chan_list1, int nchan1, uint16 *chan_list2, int nchan2)
388 {
389 	int err = BCME_OK;
390 	int i = 0, j = 0, k = 0;
391 	uint16 tmp;
392 	NULL_CHECK(d_chan_list, "d_chan_list is NULL", err);
393 	NULL_CHECK(nchan, "nchan is NULL", err);
394 	NULL_CHECK(chan_list1, "chan_list1 is NULL", err);
395 	NULL_CHECK(chan_list2, "chan_list2 is NULL", err);
396 	/* chan_list1 and chan_list2 should be sorted at first */
397 	while (i < nchan1 && j < nchan2) {
398 		tmp = chan_list1[i] < chan_list2[j]?
399 			chan_list1[i++] : chan_list2[j++];
400 		for (; i < nchan1 && chan_list1[i] == tmp; i++);
401 		for (; j < nchan2 && chan_list2[j] == tmp; j++);
402 		d_chan_list[k++] = tmp;
403 	}
404 
405 	while (i < nchan1) {
406 		tmp = chan_list1[i++];
407 		for (; i < nchan1 && chan_list1[i] == tmp; i++);
408 		d_chan_list[k++] = tmp;
409 	}
410 
411 	while (j < nchan2) {
412 		tmp = chan_list2[j++];
413 		for (; j < nchan2 && chan_list2[j] == tmp; j++);
414 		d_chan_list[k++] = tmp;
415 
416 	}
417 	*nchan = k;
418 	return err;
419 }
420 static int
_dhd_pno_get_channels(dhd_pub_t * dhd,uint16 * d_chan_list,int * nchan,uint8 band,bool skip_dfs)421 _dhd_pno_get_channels(dhd_pub_t *dhd, uint16 *d_chan_list,
422 	int *nchan, uint8 band, bool skip_dfs)
423 {
424 	int err = BCME_OK;
425 	int i, j;
426 	uint32 chan_buf[WL_NUMCHANNELS + 1];
427 	wl_uint32_list_t *list;
428 	NULL_CHECK(dhd, "dhd is NULL", err);
429 	if (*nchan) {
430 		NULL_CHECK(d_chan_list, "d_chan_list is NULL", err);
431 	}
432 	list = (wl_uint32_list_t *) (void *)chan_buf;
433 	list->count = htod32(WL_NUMCHANNELS);
434 	err = dhd_wl_ioctl_cmd(dhd, WLC_GET_VALID_CHANNELS, chan_buf, sizeof(chan_buf), FALSE, 0);
435 	if (err < 0) {
436 		DHD_ERROR(("failed to get channel list (err: %d)\n", err));
437 		goto exit;
438 	}
439 	for (i = 0, j = 0; i < dtoh32(list->count) && i < *nchan; i++) {
440 		if (band == WLC_BAND_2G) {
441 			if (dtoh32(list->element[i]) > CHANNEL_2G_MAX)
442 				continue;
443 		} else if (band == WLC_BAND_5G) {
444 			if (dtoh32(list->element[i]) <= CHANNEL_2G_MAX)
445 				continue;
446 			if (skip_dfs && is_dfs(dtoh32(list->element[i])))
447 				continue;
448 
449 		} else { /* All channels */
450 			if (skip_dfs && is_dfs(dtoh32(list->element[i])))
451 				continue;
452 		}
453 		d_chan_list[j++] = dtoh32(list->element[i]);
454 	}
455 	*nchan = j;
456 exit:
457 	return err;
458 }
459 static int
_dhd_pno_convert_format(dhd_pub_t * dhd,struct dhd_pno_batch_params * params_batch,char * buf,int nbufsize)460 _dhd_pno_convert_format(dhd_pub_t *dhd, struct dhd_pno_batch_params *params_batch,
461 	char *buf, int nbufsize)
462 {
463 	int err = BCME_OK;
464 	int bytes_written = 0, nreadsize = 0;
465 	int t_delta = 0;
466 	int nleftsize = nbufsize;
467 	uint8 cnt = 0;
468 	char *bp = buf;
469 	char eabuf[ETHER_ADDR_STR_LEN];
470 #ifdef PNO_DEBUG
471 	char *_base_bp;
472 	char msg[150];
473 #endif
474 	dhd_pno_bestnet_entry_t *iter, *next;
475 	dhd_pno_scan_results_t *siter, *snext;
476 	dhd_pno_best_header_t *phead, *pprev;
477 	NULL_CHECK(params_batch, "params_batch is NULL", err);
478 	if (nbufsize > 0)
479 		NULL_CHECK(buf, "buf is NULL", err);
480 	/* initialize the buffer */
481 	memset(buf, 0, nbufsize);
482 	DHD_PNO(("%s enter \n", __FUNCTION__));
483 	/* # of scans */
484 	if (!params_batch->get_batch.batch_started) {
485 		bp += nreadsize = sprintf(bp, "scancount=%d\n",
486 			params_batch->get_batch.expired_tot_scan_cnt);
487 		nleftsize -= nreadsize;
488 		params_batch->get_batch.batch_started = TRUE;
489 	}
490 	DHD_PNO(("%s scancount %d\n", __FUNCTION__, params_batch->get_batch.expired_tot_scan_cnt));
491 	/* preestimate scan count until which scan result this report is going to end */
492 	list_for_each_entry_safe(siter, snext,
493 		&params_batch->get_batch.expired_scan_results_list, list) {
494 		phead = siter->bestnetheader;
495 		while (phead != NULL) {
496 			/* if left_size is less than bestheader total size , stop this */
497 			if (nleftsize <=
498 				(phead->tot_size + phead->tot_cnt * ENTRY_OVERHEAD))
499 				goto exit;
500 			/* increase scan count */
501 			cnt++;
502 			/* # best of each scan */
503 			DHD_PNO(("\n<loop : %d, apcount %d>\n", cnt - 1, phead->tot_cnt));
504 			/* attribute of the scan */
505 			if (phead->reason & PNO_STATUS_ABORT_MASK) {
506 				bp += nreadsize = sprintf(bp, "trunc\n");
507 				nleftsize -= nreadsize;
508 			}
509 			list_for_each_entry_safe(iter, next,
510 				&phead->entry_list, list) {
511 				t_delta = jiffies_to_msecs(jiffies - iter->recorded_time);
512 #ifdef PNO_DEBUG
513 				_base_bp = bp;
514 				memset(msg, 0, sizeof(msg));
515 #endif
516 				/* BSSID info */
517 				bp += nreadsize = sprintf(bp, "bssid=%s\n",
518 				bcm_ether_ntoa((const struct ether_addr *)&iter->BSSID, eabuf));
519 				nleftsize -= nreadsize;
520 				/* SSID */
521 				bp += nreadsize = sprintf(bp, "ssid=%s\n", iter->SSID);
522 				nleftsize -= nreadsize;
523 				/* channel */
524 				bp += nreadsize = sprintf(bp, "freq=%d\n",
525 				wf_channel2mhz(iter->channel,
526 				iter->channel <= CH_MAX_2G_CHANNEL?
527 				WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G));
528 				nleftsize -= nreadsize;
529 				/* RSSI */
530 				bp += nreadsize = sprintf(bp, "level=%d\n", iter->RSSI);
531 				nleftsize -= nreadsize;
532 				/* add the time consumed in Driver to the timestamp of firmware */
533 				iter->timestamp += t_delta;
534 				bp += nreadsize = sprintf(bp, "age=%d\n", iter->timestamp);
535 				nleftsize -= nreadsize;
536 				/* RTT0 */
537 				bp += nreadsize = sprintf(bp, "dist=%d\n",
538 				(iter->rtt0 == 0)? -1 : iter->rtt0);
539 				nleftsize -= nreadsize;
540 				/* RTT1 */
541 				bp += nreadsize = sprintf(bp, "distSd=%d\n",
542 				(iter->rtt0 == 0)? -1 : iter->rtt1);
543 				nleftsize -= nreadsize;
544 				bp += nreadsize = sprintf(bp, "%s", AP_END_MARKER);
545 				nleftsize -= nreadsize;
546 				list_del(&iter->list);
547 				MFREE(dhd->osh, iter, BESTNET_ENTRY_SIZE);
548 #ifdef PNO_DEBUG
549 				memcpy(msg, _base_bp, bp - _base_bp);
550 				DHD_PNO(("Entry : \n%s", msg));
551 #endif
552 			}
553 			bp += nreadsize = sprintf(bp, "%s", SCAN_END_MARKER);
554 			DHD_PNO(("%s", SCAN_END_MARKER));
555 			nleftsize -= nreadsize;
556 			pprev = phead;
557 			/* reset the header */
558 			siter->bestnetheader = phead = phead->next;
559 			MFREE(dhd->osh, pprev, BEST_HEADER_SIZE);
560 
561 			siter->cnt_header--;
562 		}
563 		if (phead == NULL) {
564 			/* we store all entry in this scan , so it is ok to delete */
565 			list_del(&siter->list);
566 			MFREE(dhd->osh, siter, SCAN_RESULTS_SIZE);
567 		}
568 	}
569 exit:
570 	if (cnt < params_batch->get_batch.expired_tot_scan_cnt) {
571 		DHD_ERROR(("Buffer size is small to save all batch entry,"
572 			" cnt : %d (remained_scan_cnt): %d\n",
573 			cnt, params_batch->get_batch.expired_tot_scan_cnt - cnt));
574 	}
575 	params_batch->get_batch.expired_tot_scan_cnt -= cnt;
576 	/* set FALSE only if the link list  is empty after returning the data */
577 	if (list_empty(&params_batch->get_batch.expired_scan_results_list)) {
578 		params_batch->get_batch.batch_started = FALSE;
579 		bp += sprintf(bp, "%s", RESULTS_END_MARKER);
580 		DHD_PNO(("%s", RESULTS_END_MARKER));
581 		DHD_PNO(("%s : Getting the batching data is complete\n", __FUNCTION__));
582 	}
583 	/* return used memory in buffer */
584 	bytes_written = (int32)(bp - buf);
585 	return bytes_written;
586 }
587 static int
_dhd_pno_clear_all_batch_results(dhd_pub_t * dhd,struct list_head * head,bool only_last)588 _dhd_pno_clear_all_batch_results(dhd_pub_t *dhd, struct list_head *head, bool only_last)
589 {
590 	int err = BCME_OK;
591 	int removed_scan_cnt = 0;
592 	dhd_pno_scan_results_t *siter, *snext;
593 	dhd_pno_best_header_t *phead, *pprev;
594 	dhd_pno_bestnet_entry_t *iter, *next;
595 	NULL_CHECK(dhd, "dhd is NULL", err);
596 	NULL_CHECK(head, "head is NULL", err);
597 	NULL_CHECK(head->next, "head->next is NULL", err);
598 	DHD_PNO(("%s enter\n", __FUNCTION__));
599 	list_for_each_entry_safe(siter, snext,
600 		head, list) {
601 		if (only_last) {
602 			/* in case that we need to delete only last one */
603 			if (!list_is_last(&siter->list, head)) {
604 				/* skip if the one is not last */
605 				continue;
606 			}
607 		}
608 		/* delete all data belong if the one is last */
609 		phead = siter->bestnetheader;
610 		while (phead != NULL) {
611 			removed_scan_cnt++;
612 			list_for_each_entry_safe(iter, next,
613 			&phead->entry_list, list) {
614 				list_del(&iter->list);
615 				MFREE(dhd->osh, iter, BESTNET_ENTRY_SIZE);
616 			}
617 			pprev = phead;
618 			phead = phead->next;
619 			MFREE(dhd->osh, pprev, BEST_HEADER_SIZE);
620 		}
621 		if (phead == NULL) {
622 			/* it is ok to delete top node */
623 			list_del(&siter->list);
624 			MFREE(dhd->osh, siter, SCAN_RESULTS_SIZE);
625 		}
626 	}
627 	return removed_scan_cnt;
628 }
629 
630 static int
_dhd_pno_cfg(dhd_pub_t * dhd,uint16 * channel_list,int nchan)631 _dhd_pno_cfg(dhd_pub_t *dhd, uint16 *channel_list, int nchan)
632 {
633 	int err = BCME_OK;
634 	int i = 0;
635 	wl_pfn_cfg_t pfncfg_param;
636 	NULL_CHECK(dhd, "dhd is NULL", err);
637 	if (nchan) {
638 		NULL_CHECK(channel_list, "nchan is NULL", err);
639 	}
640 	DHD_PNO(("%s enter :  nchan : %d\n", __FUNCTION__, nchan));
641 	memset(&pfncfg_param, 0, sizeof(wl_pfn_cfg_t));
642 	/* Setup default values */
643 	pfncfg_param.reporttype = htod32(WL_PFN_REPORT_ALLNET);
644 	pfncfg_param.channel_num = htod32(0);
645 
646 	for (i = 0; i < nchan && nchan < WL_NUMCHANNELS; i++)
647 		pfncfg_param.channel_list[i] = channel_list[i];
648 
649 	pfncfg_param.channel_num = htod32(nchan);
650 	err = dhd_iovar(dhd, 0, "pfn_cfg", (char *)&pfncfg_param, sizeof(pfncfg_param), 1);
651 	if (err < 0) {
652 		DHD_ERROR(("%s : failed to execute pfn_cfg\n", __FUNCTION__));
653 		goto exit;
654 	}
655 exit:
656 	return err;
657 }
658 static int
_dhd_pno_reinitialize_prof(dhd_pub_t * dhd,dhd_pno_params_t * params,dhd_pno_mode_t mode)659 _dhd_pno_reinitialize_prof(dhd_pub_t *dhd, dhd_pno_params_t *params, dhd_pno_mode_t mode)
660 {
661 	int err = BCME_OK;
662 	dhd_pno_status_info_t *_pno_state;
663 	NULL_CHECK(dhd, "dhd is NULL\n", err);
664 	NULL_CHECK(dhd->pno_state, "pno_state is NULL\n", err);
665 	DHD_PNO(("%s enter\n", __FUNCTION__));
666 	_pno_state = PNO_GET_PNOSTATE(dhd);
667 	mutex_lock(&_pno_state->pno_mutex);
668 	switch (mode) {
669 	case DHD_PNO_LEGACY_MODE: {
670 		struct dhd_pno_ssid *iter, *next;
671 		if (params->params_legacy.nssid > 0) {
672 			list_for_each_entry_safe(iter, next,
673 				&params->params_legacy.ssid_list, list) {
674 				list_del(&iter->list);
675 				kfree(iter);
676 			}
677 		}
678 		params->params_legacy.nssid = 0;
679 		params->params_legacy.scan_fr = 0;
680 		params->params_legacy.pno_freq_expo_max = 0;
681 		params->params_legacy.pno_repeat = 0;
682 		params->params_legacy.nchan = 0;
683 		memset(params->params_legacy.chan_list, 0,
684 			sizeof(params->params_legacy.chan_list));
685 		break;
686 	}
687 	case DHD_PNO_BATCH_MODE: {
688 		params->params_batch.scan_fr = 0;
689 		params->params_batch.mscan = 0;
690 		params->params_batch.nchan = 0;
691 		params->params_batch.rtt = 0;
692 		params->params_batch.bestn = 0;
693 		params->params_batch.nchan = 0;
694 		params->params_batch.band = WLC_BAND_AUTO;
695 		memset(params->params_batch.chan_list, 0,
696 			sizeof(params->params_batch.chan_list));
697 		params->params_batch.get_batch.batch_started = FALSE;
698 		params->params_batch.get_batch.buf = NULL;
699 		params->params_batch.get_batch.bufsize = 0;
700 		params->params_batch.get_batch.reason = 0;
701 		_dhd_pno_clear_all_batch_results(dhd,
702 			&params->params_batch.get_batch.scan_results_list, FALSE);
703 		_dhd_pno_clear_all_batch_results(dhd,
704 			&params->params_batch.get_batch.expired_scan_results_list, FALSE);
705 		params->params_batch.get_batch.tot_scan_cnt = 0;
706 		params->params_batch.get_batch.expired_tot_scan_cnt = 0;
707 		params->params_batch.get_batch.top_node_cnt = 0;
708 		INIT_LIST_HEAD(&params->params_batch.get_batch.scan_results_list);
709 		INIT_LIST_HEAD(&params->params_batch.get_batch.expired_scan_results_list);
710 		break;
711 	}
712 	case DHD_PNO_HOTLIST_MODE: {
713 		struct dhd_pno_bssid *iter, *next;
714 		if (params->params_hotlist.nbssid > 0) {
715 			list_for_each_entry_safe(iter, next,
716 				&params->params_hotlist.bssid_list, list) {
717 				list_del(&iter->list);
718 				kfree(iter);
719 			}
720 		}
721 		params->params_hotlist.scan_fr = 0;
722 		params->params_hotlist.nbssid = 0;
723 		params->params_hotlist.nchan = 0;
724 		params->params_batch.band = WLC_BAND_AUTO;
725 		memset(params->params_hotlist.chan_list, 0,
726 			sizeof(params->params_hotlist.chan_list));
727 		break;
728 	}
729 	default:
730 		DHD_ERROR(("%s : unknown mode : %d\n", __FUNCTION__, mode));
731 		break;
732 	}
733 	mutex_unlock(&_pno_state->pno_mutex);
734 	return err;
735 }
736 static int
_dhd_pno_add_bssid(dhd_pub_t * dhd,wl_pfn_bssid_t * p_pfn_bssid,int nbssid)737 _dhd_pno_add_bssid(dhd_pub_t *dhd, wl_pfn_bssid_t *p_pfn_bssid, int nbssid)
738 {
739 	int err = BCME_OK;
740 	NULL_CHECK(dhd, "dhd is NULL", err);
741 	if (nbssid) {
742 		NULL_CHECK(p_pfn_bssid, "bssid list is NULL", err);
743 	}
744 	err = dhd_iovar(dhd, 0, "pfn_add_bssid", (char *)p_pfn_bssid,
745 		sizeof(wl_pfn_bssid_t) * nbssid, 1);
746 	if (err < 0) {
747 		DHD_ERROR(("%s : failed to execute pfn_cfg\n", __FUNCTION__));
748 		goto exit;
749 	}
750 exit:
751 	return err;
752 }
753 int
dhd_pno_stop_for_ssid(dhd_pub_t * dhd)754 dhd_pno_stop_for_ssid(dhd_pub_t *dhd)
755 {
756 	int err = BCME_OK;
757 	uint32 mode = 0;
758 	dhd_pno_status_info_t *_pno_state;
759 	dhd_pno_params_t *_params;
760 	wl_pfn_bssid_t *p_pfn_bssid;
761 	NULL_CHECK(dhd, "dev is NULL", err);
762 	NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
763 	_pno_state = PNO_GET_PNOSTATE(dhd);
764 	if (!(_pno_state->pno_mode & DHD_PNO_LEGACY_MODE)) {
765 		DHD_ERROR(("%s : LEGACY PNO MODE is not enabled\n", __FUNCTION__));
766 		goto exit;
767 	}
768 	DHD_PNO(("%s enter\n", __FUNCTION__));
769 	_pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
770 	/* restart Batch mode  if the batch mode is on */
771 	if (_pno_state->pno_mode & (DHD_PNO_BATCH_MODE | DHD_PNO_HOTLIST_MODE)) {
772 		/* retrieve the batching data from firmware into host */
773 		dhd_pno_get_for_batch(dhd, NULL, 0, PNO_STATUS_DISABLE);
774 		/* save current pno_mode before calling dhd_pno_clean */
775 		mode = _pno_state->pno_mode;
776 		dhd_pno_clean(dhd);
777 		/* restore previous pno_mode */
778 		_pno_state->pno_mode = mode;
779 		if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
780 			_params = &(_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS]);
781 			/* restart BATCH SCAN */
782 			err = dhd_pno_set_for_batch(dhd, &_params->params_batch);
783 			if (err < 0) {
784 				_pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
785 				DHD_ERROR(("%s : failed to restart batch scan(err: %d)\n",
786 					__FUNCTION__, err));
787 				goto exit;
788 			}
789 		} else if (_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE) {
790 			/* restart HOTLIST SCAN */
791 			struct dhd_pno_bssid *iter, *next;
792 			_params = &(_pno_state->pno_params_arr[INDEX_OF_HOTLIST_PARAMS]);
793 			p_pfn_bssid = kzalloc(sizeof(wl_pfn_bssid_t) *
794 			_params->params_hotlist.nbssid, GFP_KERNEL);
795 			if (p_pfn_bssid == NULL) {
796 				DHD_ERROR(("%s : failed to allocate wl_pfn_bssid_t array"
797 				" (count: %d)",
798 					__FUNCTION__, _params->params_hotlist.nbssid));
799 				err = BCME_ERROR;
800 				_pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
801 				goto exit;
802 			}
803 			/* convert dhd_pno_bssid to wl_pfn_bssid */
804 			list_for_each_entry_safe(iter, next,
805 			&_params->params_hotlist.bssid_list, list) {
806 				memcpy(&p_pfn_bssid->macaddr,
807 				&iter->macaddr, ETHER_ADDR_LEN);
808 				p_pfn_bssid->flags = iter->flags;
809 				p_pfn_bssid++;
810 			}
811 			err = dhd_pno_set_for_hotlist(dhd, p_pfn_bssid, &_params->params_hotlist);
812 			if (err < 0) {
813 				_pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
814 				DHD_ERROR(("%s : failed to restart hotlist scan(err: %d)\n",
815 					__FUNCTION__, err));
816 				goto exit;
817 			}
818 		}
819 	} else {
820 		err = dhd_pno_clean(dhd);
821 		if (err < 0) {
822 			DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
823 				__FUNCTION__, err));
824 			goto exit;
825 		}
826 	}
827 exit:
828 	return err;
829 }
830 
831 int
dhd_pno_enable(dhd_pub_t * dhd,int enable)832 dhd_pno_enable(dhd_pub_t *dhd, int enable)
833 {
834 	int err = BCME_OK;
835 	NULL_CHECK(dhd, "dhd is NULL", err);
836 	DHD_PNO(("%s enter\n", __FUNCTION__));
837 	return (_dhd_pno_enable(dhd, enable));
838 }
839 
dhd_pno_get_legacy_pno_ssid(dhd_pub_t * dhd,dhd_pno_status_info_t * pno_state)840 static wlc_ssid_ext_t * dhd_pno_get_legacy_pno_ssid(dhd_pub_t *dhd,
841             dhd_pno_status_info_t *pno_state)
842 {
843 	int err = BCME_OK;
844 	int i;
845 	struct dhd_pno_ssid *iter, *next;
846 	dhd_pno_params_t	*_params1 = &pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS];
847 	wlc_ssid_ext_t *p_ssid_list;
848 
849 	p_ssid_list = kzalloc(sizeof(wlc_ssid_ext_t) *
850 	                   _params1->params_legacy.nssid, GFP_KERNEL);
851 	if (p_ssid_list == NULL) {
852 		DHD_ERROR(("%s : failed to allocate wlc_ssid_ext_t array (count: %d)",
853 			__FUNCTION__, _params1->params_legacy.nssid));
854 		err = BCME_ERROR;
855 		pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
856 		goto exit;
857 	}
858 	i = 0;
859 	/* convert dhd_pno_ssid to wlc_ssid_ext_t */
860 	list_for_each_entry_safe(iter, next, &_params1->params_legacy.ssid_list, list) {
861 		p_ssid_list[i].SSID_len = iter->SSID_len;
862 		p_ssid_list[i].hidden = iter->hidden;
863 		memcpy(p_ssid_list[i].SSID, iter->SSID, p_ssid_list[i].SSID_len);
864 		i++;
865 	}
866 exit:
867 	return p_ssid_list;
868 
869 }
870 
871 static int
dhd_pno_add_to_ssid_list(dhd_pno_params_t * params,wlc_ssid_ext_t * ssid_list,int nssid)872 dhd_pno_add_to_ssid_list(dhd_pno_params_t *params, wlc_ssid_ext_t *ssid_list,
873     int nssid)
874 {
875 	int ret = 0;
876 	int i;
877 	struct dhd_pno_ssid *_pno_ssid;
878 
879 	for (i = 0; i < nssid; i++) {
880 		if (ssid_list[i].SSID_len > DOT11_MAX_SSID_LEN) {
881 			DHD_ERROR(("%s : Invalid SSID length %d\n",
882 				__FUNCTION__, ssid_list[i].SSID_len));
883 			ret = BCME_ERROR;
884 			goto exit;
885 		}
886 		_pno_ssid = kzalloc(sizeof(struct dhd_pno_ssid), GFP_KERNEL);
887 		if (_pno_ssid == NULL) {
888 			DHD_ERROR(("%s : failed to allocate struct dhd_pno_ssid\n",
889 				__FUNCTION__));
890 			ret = BCME_ERROR;
891 			goto exit;
892 		}
893 		_pno_ssid->SSID_len = ssid_list[i].SSID_len;
894 		_pno_ssid->hidden = ssid_list[i].hidden;
895 		memcpy(_pno_ssid->SSID, ssid_list[i].SSID, _pno_ssid->SSID_len);
896 		list_add_tail(&_pno_ssid->list, &params->params_legacy.ssid_list);
897 	}
898 
899 exit:
900 	return ret;
901 }
902 
903 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)904 dhd_pno_set_for_ssid(dhd_pub_t *dhd, wlc_ssid_ext_t* ssid_list, int nssid,
905 	uint16  scan_fr, int pno_repeat, int pno_freq_expo_max, uint16 *channel_list, int nchan)
906 {
907 	dhd_pno_params_t *_params;
908 	dhd_pno_params_t *_params2;
909 	dhd_pno_status_info_t *_pno_state;
910 	uint16 _chan_list[WL_NUMCHANNELS];
911 	int32 tot_nchan = 0;
912 	int err = BCME_OK;
913 	int i;
914 	int mode = 0;
915 	NULL_CHECK(dhd, "dhd is NULL", err);
916 	NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
917 	_pno_state = PNO_GET_PNOSTATE(dhd);
918 
919 	if (!dhd_support_sta_mode(dhd)) {
920 		err = BCME_BADOPTION;
921 		goto exit_no_clear;
922 	}
923 	DHD_PNO(("%s enter : scan_fr :%d, pno_repeat :%d,"
924 			"pno_freq_expo_max: %d, nchan :%d\n", __FUNCTION__,
925 			scan_fr, pno_repeat, pno_freq_expo_max, nchan));
926 
927 	_params = &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS]);
928 	if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
929 		DHD_ERROR(("%s : Legacy PNO mode was already started, "
930 			"will disable previous one to start new one\n", __FUNCTION__));
931 		err = dhd_pno_stop_for_ssid(dhd);
932 		if (err < 0) {
933 			DHD_ERROR(("%s : failed to stop legacy PNO (err %d)\n",
934 				__FUNCTION__, err));
935 			goto exit_no_clear;
936 		}
937 	}
938 	_pno_state->pno_mode |= DHD_PNO_LEGACY_MODE;
939 	err = _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_LEGACY_MODE);
940 	if (err < 0) {
941 		DHD_ERROR(("%s : failed to reinitialize profile (err %d)\n",
942 			__FUNCTION__, err));
943 		goto exit_no_clear;
944 	}
945 	memset(_chan_list, 0, sizeof(_chan_list));
946 	tot_nchan = nchan;
947 	if (tot_nchan > 0 && channel_list) {
948 		for (i = 0; i < nchan; i++)
949 		_params->params_legacy.chan_list[i] = _chan_list[i] = channel_list[i];
950 	}
951 	if (_pno_state->pno_mode & (DHD_PNO_BATCH_MODE | DHD_PNO_HOTLIST_MODE)) {
952 		DHD_PNO(("BATCH SCAN is on progress in firmware\n"));
953 		/* retrieve the batching data from firmware into host */
954 		dhd_pno_get_for_batch(dhd, NULL, 0, PNO_STATUS_DISABLE);
955 		/* store current pno_mode before disabling pno */
956 		mode = _pno_state->pno_mode;
957 		err = _dhd_pno_enable(dhd, PNO_OFF);
958 		if (err < 0) {
959 			DHD_ERROR(("%s : failed to disable PNO\n", __FUNCTION__));
960 			goto exit_no_clear;
961 		}
962 		/* restore the previous mode */
963 		_pno_state->pno_mode = mode;
964 		/* use superset of channel list between two mode */
965 		if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
966 			_params2 = &(_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS]);
967 			if (_params2->params_batch.nchan > 0 && nchan > 0) {
968 				err = _dhd_pno_chan_merge(_chan_list, &tot_nchan,
969 					&_params2->params_batch.chan_list[0],
970 					_params2->params_batch.nchan,
971 					&channel_list[0], nchan);
972 				if (err < 0) {
973 					DHD_ERROR(("%s : failed to merge channel list"
974 					" between legacy and batch\n",
975 						__FUNCTION__));
976 					goto exit_no_clear;
977 				}
978 			}  else {
979 				DHD_PNO(("superset channel will use"
980 				" all channels in firmware\n"));
981 			}
982 		} else if (_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE) {
983 			_params2 = &(_pno_state->pno_params_arr[INDEX_OF_HOTLIST_PARAMS]);
984 			if (_params2->params_hotlist.nchan > 0 && nchan > 0) {
985 				err = _dhd_pno_chan_merge(_chan_list, &tot_nchan,
986 					&_params2->params_hotlist.chan_list[0],
987 					_params2->params_hotlist.nchan,
988 					&channel_list[0], nchan);
989 				if (err < 0) {
990 					DHD_ERROR(("%s : failed to merge channel list"
991 					" between legacy and hotlist\n",
992 						__FUNCTION__));
993 					goto exit_no_clear;
994 				}
995 			}
996 		}
997 	}
998 	_params->params_legacy.scan_fr = scan_fr;
999 	_params->params_legacy.pno_repeat = pno_repeat;
1000 	_params->params_legacy.pno_freq_expo_max = pno_freq_expo_max;
1001 	_params->params_legacy.nchan = nchan;
1002 	_params->params_legacy.nssid = nssid;
1003 	INIT_LIST_HEAD(&_params->params_legacy.ssid_list);
1004 	if ((err = _dhd_pno_set(dhd, _params, DHD_PNO_LEGACY_MODE)) < 0) {
1005 		DHD_ERROR(("failed to set call pno_set (err %d) in firmware\n", err));
1006 		goto exit;
1007 	}
1008 	if ((err = _dhd_pno_add_ssid(dhd, ssid_list, nssid)) < 0) {
1009 		DHD_ERROR(("failed to add ssid list(err %d), %d in firmware\n", err, nssid));
1010 		goto exit;
1011 	}
1012 	if (dhd_pno_add_to_ssid_list(_params, ssid_list, nssid) < 0) {
1013 		err = BCME_ERROR;
1014 		goto exit;
1015 	}
1016 	if (tot_nchan > 0) {
1017 		if ((err = _dhd_pno_cfg(dhd, _chan_list, tot_nchan)) < 0) {
1018 			DHD_ERROR(("%s : failed to set call pno_cfg (err %d) in firmware\n",
1019 				__FUNCTION__, err));
1020 			goto exit;
1021 		}
1022 	}
1023 	if (_pno_state->pno_status == DHD_PNO_DISABLED) {
1024 		if ((err = _dhd_pno_enable(dhd, PNO_ON)) < 0)
1025 			DHD_ERROR(("%s : failed to enable PNO\n", __FUNCTION__));
1026 	}
1027 exit:
1028 	if (err < 0)
1029 		_dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_LEGACY_MODE);
1030 exit_no_clear:
1031 	/* clear mode in case of error */
1032 	if (err < 0)
1033 		_pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
1034 	return err;
1035 }
1036 int
dhd_pno_set_for_batch(dhd_pub_t * dhd,struct dhd_pno_batch_params * batch_params)1037 dhd_pno_set_for_batch(dhd_pub_t *dhd, struct dhd_pno_batch_params *batch_params)
1038 {
1039 	int err = BCME_OK;
1040 	uint16 _chan_list[WL_NUMCHANNELS];
1041 	int rem_nchan = 0, tot_nchan = 0;
1042 	int mode = 0, mscan = 0;
1043 	dhd_pno_params_t *_params;
1044 	dhd_pno_params_t *_params2;
1045 	dhd_pno_status_info_t *_pno_state;
1046 	wlc_ssid_ext_t *p_ssid_list = NULL;
1047 	NULL_CHECK(dhd, "dhd is NULL", err);
1048 	NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
1049 	NULL_CHECK(batch_params, "batch_params is NULL", err);
1050 	_pno_state = PNO_GET_PNOSTATE(dhd);
1051 	DHD_PNO(("%s enter\n", __FUNCTION__));
1052 	if (!dhd_support_sta_mode(dhd)) {
1053 		err = BCME_BADOPTION;
1054 		goto exit;
1055 	}
1056 	if (!WLS_SUPPORTED(_pno_state)) {
1057 		DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
1058 		err = BCME_UNSUPPORTED;
1059 		goto exit;
1060 	}
1061 	_params = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS];
1062 	if (!(_pno_state->pno_mode & DHD_PNO_BATCH_MODE)) {
1063 		_pno_state->pno_mode |= DHD_PNO_BATCH_MODE;
1064 		err = _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_BATCH_MODE);
1065 		if (err < 0) {
1066 			DHD_ERROR(("%s : failed to call _dhd_pno_reinitialize_prof\n",
1067 				__FUNCTION__));
1068 			goto exit;
1069 		}
1070 	} else {
1071 		/* batch mode is already started */
1072 		return -EBUSY;
1073 	}
1074 	_params->params_batch.scan_fr = batch_params->scan_fr;
1075 	_params->params_batch.bestn = batch_params->bestn;
1076 	_params->params_batch.mscan = (batch_params->mscan)?
1077 		batch_params->mscan : DEFAULT_BATCH_MSCAN;
1078 	_params->params_batch.nchan = batch_params->nchan;
1079 	memcpy(_params->params_batch.chan_list, batch_params->chan_list,
1080 		sizeof(_params->params_batch.chan_list));
1081 
1082 	memset(_chan_list, 0, sizeof(_chan_list));
1083 
1084 	rem_nchan = ARRAYSIZE(batch_params->chan_list) - batch_params->nchan;
1085 	if (batch_params->band == WLC_BAND_2G || batch_params->band == WLC_BAND_5G) {
1086 		/* get a valid channel list based on band B or A */
1087 		err = _dhd_pno_get_channels(dhd,
1088 		&_params->params_batch.chan_list[batch_params->nchan],
1089 		&rem_nchan, batch_params->band, FALSE);
1090 		if (err < 0) {
1091 			DHD_ERROR(("%s: failed to get valid channel list(band : %d)\n",
1092 				__FUNCTION__, batch_params->band));
1093 			goto exit;
1094 		}
1095 		/* now we need to update nchan because rem_chan has valid channel count */
1096 		_params->params_batch.nchan += rem_nchan;
1097 		/* need to sort channel list */
1098 		sort(_params->params_batch.chan_list, _params->params_batch.nchan,
1099 			sizeof(_params->params_batch.chan_list[0]), _dhd_pno_cmpfunc, NULL);
1100 	}
1101 #ifdef PNO_DEBUG
1102 {
1103 		DHD_PNO(("Channel list : "));
1104 		for (i = 0; i < _params->params_batch.nchan; i++) {
1105 			DHD_PNO(("%d ", _params->params_batch.chan_list[i]));
1106 		}
1107 		DHD_PNO(("\n"));
1108 }
1109 #endif
1110 	if (_params->params_batch.nchan) {
1111 		/* copy the channel list into local array */
1112 		memcpy(_chan_list, _params->params_batch.chan_list, sizeof(_chan_list));
1113 		tot_nchan = _params->params_batch.nchan;
1114 	}
1115 	if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
1116 		DHD_PNO(("PNO SSID is on progress in firmware\n"));
1117 		/* store current pno_mode before disabling pno */
1118 		mode = _pno_state->pno_mode;
1119 		err = _dhd_pno_enable(dhd, PNO_OFF);
1120 		if (err < 0) {
1121 			DHD_ERROR(("%s : failed to disable PNO\n", __FUNCTION__));
1122 			goto exit;
1123 		}
1124 		/* restore the previous mode */
1125 		_pno_state->pno_mode = mode;
1126 		/* Use the superset for channelist between two mode */
1127 		_params2 = &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS]);
1128 		if (_params2->params_legacy.nchan > 0 && _params->params_batch.nchan > 0) {
1129 			err = _dhd_pno_chan_merge(_chan_list, &tot_nchan,
1130 				&_params2->params_legacy.chan_list[0],
1131 				_params2->params_legacy.nchan,
1132 				&_params->params_batch.chan_list[0], _params->params_batch.nchan);
1133 			if (err < 0) {
1134 				DHD_ERROR(("%s : failed to merge channel list"
1135 				" between legacy and batch\n",
1136 					__FUNCTION__));
1137 				goto exit;
1138 			}
1139 		} else {
1140 			DHD_PNO(("superset channel will use all channels in firmware\n"));
1141 		}
1142 		p_ssid_list = dhd_pno_get_legacy_pno_ssid(dhd, _pno_state);
1143 		if (!p_ssid_list) {
1144 			err = BCME_NOMEM;
1145 			DHD_ERROR(("failed to get Legacy PNO SSID list\n"));
1146 			goto exit;
1147 		}
1148 		if ((err = _dhd_pno_add_ssid(dhd, p_ssid_list,
1149 			_params2->params_legacy.nssid)) < 0) {
1150 			DHD_ERROR(("failed to add ssid list (err %d) in firmware\n", err));
1151 			goto exit;
1152 		}
1153 	}
1154 	if ((err = _dhd_pno_set(dhd, _params, DHD_PNO_BATCH_MODE)) < 0) {
1155 		DHD_ERROR(("%s : failed to set call pno_set (err %d) in firmware\n",
1156 			__FUNCTION__, err));
1157 		goto exit;
1158 	} else {
1159 		/* we need to return mscan */
1160 		mscan = err;
1161 	}
1162 	if (tot_nchan > 0) {
1163 		if ((err = _dhd_pno_cfg(dhd, _chan_list, tot_nchan)) < 0) {
1164 			DHD_ERROR(("%s : failed to set call pno_cfg (err %d) in firmware\n",
1165 				__FUNCTION__, err));
1166 			goto exit;
1167 		}
1168 	}
1169 	if (_pno_state->pno_status == DHD_PNO_DISABLED) {
1170 		if ((err = _dhd_pno_enable(dhd, PNO_ON)) < 0)
1171 			DHD_ERROR(("%s : failed to enable PNO\n", __FUNCTION__));
1172 	}
1173 exit:
1174 	/* clear mode in case of error */
1175 	if (err < 0)
1176 		_pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
1177 	else {
1178 		/* return #max scan firmware can do */
1179 		err = mscan;
1180 	}
1181 	kfree(p_ssid_list);
1182 	return err;
1183 }
1184 
1185 static int
_dhd_pno_get_for_batch(dhd_pub_t * dhd,char * buf,int bufsize,int reason)1186 _dhd_pno_get_for_batch(dhd_pub_t *dhd, char *buf, int bufsize, int reason)
1187 {
1188 	int err = BCME_OK;
1189 	int i, j;
1190 	uint32 timestamp = 0;
1191 	dhd_pno_params_t *_params = NULL;
1192 	dhd_pno_status_info_t *_pno_state = NULL;
1193 	wl_pfn_lscanresults_t *plbestnet = NULL;
1194 	wl_pfn_lnet_info_t *plnetinfo;
1195 	dhd_pno_bestnet_entry_t *pbestnet_entry;
1196 	dhd_pno_best_header_t *pbestnetheader = NULL;
1197 	dhd_pno_scan_results_t *pscan_results = NULL, *siter, *snext;
1198 	bool allocate_header = FALSE;
1199 	NULL_CHECK(dhd, "dhd is NULL", err);
1200 	NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
1201 	if (!dhd_support_sta_mode(dhd)) {
1202 		err = BCME_BADOPTION;
1203 		goto exit;
1204 	}
1205 	DHD_PNO(("%s enter\n", __FUNCTION__));
1206 	_pno_state = PNO_GET_PNOSTATE(dhd);
1207 
1208 	if (!WLS_SUPPORTED(_pno_state)) {
1209 		DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
1210 		err = BCME_UNSUPPORTED;
1211 		goto exit;
1212 	}
1213 	if (!(_pno_state->pno_mode & DHD_PNO_BATCH_MODE)) {
1214 		DHD_ERROR(("%s: Batching SCAN mode is not enabled\n", __FUNCTION__));
1215 		goto exit;
1216 	}
1217 	mutex_lock(&_pno_state->pno_mutex);
1218 	_params = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS];
1219 	if (buf && bufsize) {
1220 		if (!list_empty(&_params->params_batch.get_batch.expired_scan_results_list)) {
1221 			/* need to check whether we have cashed data or not */
1222 			DHD_PNO(("%s: have cashed batching data in Driver\n",
1223 				__FUNCTION__));
1224 			/* convert to results format */
1225 			goto convert_format;
1226 		} else {
1227 			/* this is a first try to get batching results */
1228 			if (!list_empty(&_params->params_batch.get_batch.scan_results_list)) {
1229 				/* move the scan_results_list to expired_scan_results_lists */
1230 				list_for_each_entry_safe(siter, snext,
1231 					&_params->params_batch.get_batch.scan_results_list, list) {
1232 					list_move_tail(&siter->list,
1233 					&_params->params_batch.get_batch.expired_scan_results_list);
1234 				}
1235 				_params->params_batch.get_batch.top_node_cnt = 0;
1236 				_params->params_batch.get_batch.expired_tot_scan_cnt =
1237 					_params->params_batch.get_batch.tot_scan_cnt;
1238 				_params->params_batch.get_batch.tot_scan_cnt = 0;
1239 				goto convert_format;
1240 			}
1241 		}
1242 	}
1243 	/* create dhd_pno_scan_results_t whenever we got event WLC_E_PFN_BEST_BATCHING */
1244 	pscan_results = (dhd_pno_scan_results_t *)MALLOC(dhd->osh, SCAN_RESULTS_SIZE);
1245 	if (pscan_results == NULL) {
1246 		err = BCME_NOMEM;
1247 		DHD_ERROR(("failed to allocate dhd_pno_scan_results_t\n"));
1248 		goto exit;
1249 	}
1250 	pscan_results->bestnetheader = NULL;
1251 	pscan_results->cnt_header = 0;
1252 	/* add the element into list unless total node cnt is less than MAX_NODE_ CNT */
1253 	if (_params->params_batch.get_batch.top_node_cnt < MAX_NODE_CNT) {
1254 		list_add(&pscan_results->list, &_params->params_batch.get_batch.scan_results_list);
1255 		_params->params_batch.get_batch.top_node_cnt++;
1256 	} else {
1257 		int _removed_scan_cnt;
1258 		/* remove oldest one and add new one */
1259 		DHD_PNO(("%s : Remove oldest node and add new one\n", __FUNCTION__));
1260 		_removed_scan_cnt = _dhd_pno_clear_all_batch_results(dhd,
1261 			&_params->params_batch.get_batch.scan_results_list, TRUE);
1262 		_params->params_batch.get_batch.tot_scan_cnt -= _removed_scan_cnt;
1263 		list_add(&pscan_results->list, &_params->params_batch.get_batch.scan_results_list);
1264 
1265 	}
1266 	plbestnet = (wl_pfn_lscanresults_t *)MALLOC(dhd->osh, PNO_BESTNET_LEN);
1267 	NULL_CHECK(plbestnet, "failed to allocate buffer for bestnet", err);
1268 	DHD_PNO(("%s enter\n", __FUNCTION__));
1269 	memset(plbestnet, 0, PNO_BESTNET_LEN);
1270 	while (plbestnet->status != PFN_COMPLETE) {
1271 		memset(plbestnet, 0, PNO_BESTNET_LEN);
1272 		err = dhd_iovar(dhd, 0, "pfnlbest", (char *)plbestnet, PNO_BESTNET_LEN, 0);
1273 		if (err < 0) {
1274 			if (err == BCME_EPERM) {
1275 				DHD_ERROR(("we cannot get the batching data "
1276 					"during scanning in firmware, try again\n,"));
1277 				msleep(500);
1278 				continue;
1279 			} else {
1280 				DHD_ERROR(("%s : failed to execute pfnlbest (err :%d)\n",
1281 					__FUNCTION__, err));
1282 				goto exit;
1283 			}
1284 		}
1285 		DHD_PNO(("ver %d, status : %d, count %d\n", plbestnet->version,
1286 			plbestnet->status, plbestnet->count));
1287 		if (plbestnet->version != PFN_SCANRESULT_VERSION) {
1288 			err = BCME_VERSION;
1289 			DHD_ERROR(("bestnet version(%d) is mismatch with Driver version(%d)\n",
1290 				plbestnet->version, PFN_SCANRESULT_VERSION));
1291 			goto exit;
1292 		}
1293 		plnetinfo = plbestnet->netinfo;
1294 		for (i = 0; i < plbestnet->count; i++) {
1295 			pbestnet_entry = (dhd_pno_bestnet_entry_t *)
1296 			MALLOC(dhd->osh, BESTNET_ENTRY_SIZE);
1297 			if (pbestnet_entry == NULL) {
1298 				err = BCME_NOMEM;
1299 				DHD_ERROR(("failed to allocate dhd_pno_bestnet_entry\n"));
1300 				goto exit;
1301 			}
1302 			memset(pbestnet_entry, 0, BESTNET_ENTRY_SIZE);
1303 			pbestnet_entry->recorded_time = jiffies; /* record the current time */
1304 			/* create header for the first entry */
1305 			allocate_header = (i == 0)? TRUE : FALSE;
1306 			/* check whether the new generation is started or not */
1307 			if (timestamp && (TIME_DIFF(timestamp, plnetinfo->timestamp)
1308 				> TIME_MIN_DIFF))
1309 				allocate_header = TRUE;
1310 			timestamp = plnetinfo->timestamp;
1311 			if (allocate_header) {
1312 				pbestnetheader = (dhd_pno_best_header_t *)
1313 				MALLOC(dhd->osh, BEST_HEADER_SIZE);
1314 				if (pbestnetheader == NULL) {
1315 					err = BCME_NOMEM;
1316 					if (pbestnet_entry)
1317 						MFREE(dhd->osh, pbestnet_entry,
1318 						BESTNET_ENTRY_SIZE);
1319 					DHD_ERROR(("failed to allocate dhd_pno_bestnet_entry\n"));
1320 					goto exit;
1321 				}
1322 				/* increase total cnt of bestnet header */
1323 				pscan_results->cnt_header++;
1324 				/* need to record the reason to call dhd_pno_get_for_bach */
1325 				if (reason)
1326 					pbestnetheader->reason = (ENABLE << reason);
1327 				memset(pbestnetheader, 0, BEST_HEADER_SIZE);
1328 				/* initialize the head of linked list */
1329 				INIT_LIST_HEAD(&(pbestnetheader->entry_list));
1330 				/* link the pbestnet heaer into existed list */
1331 				if (pscan_results->bestnetheader == NULL)
1332 					/* In case of header */
1333 					pscan_results->bestnetheader = pbestnetheader;
1334 				else {
1335 					dhd_pno_best_header_t *head = pscan_results->bestnetheader;
1336 					pscan_results->bestnetheader = pbestnetheader;
1337 					pbestnetheader->next = head;
1338 				}
1339 			}
1340 			/* fills the best network info */
1341 			pbestnet_entry->channel = plnetinfo->pfnsubnet.channel;
1342 			pbestnet_entry->RSSI = plnetinfo->RSSI;
1343 			if (plnetinfo->flags & PFN_PARTIAL_SCAN_MASK) {
1344 				/* if RSSI is positive value, we assume that
1345 				 * this scan is aborted by other scan
1346 				 */
1347 				DHD_PNO(("This scan is aborted\n"));
1348 				pbestnetheader->reason = (ENABLE << PNO_STATUS_ABORT);
1349 			}
1350 			pbestnet_entry->rtt0 = plnetinfo->rtt0;
1351 			pbestnet_entry->rtt1 = plnetinfo->rtt1;
1352 			pbestnet_entry->timestamp = plnetinfo->timestamp;
1353 			if (plnetinfo->pfnsubnet.SSID_len > DOT11_MAX_SSID_LEN) {
1354 				DHD_ERROR(("%s: Invalid SSID length %d: trimming it to max\n",
1355 				      __FUNCTION__, plnetinfo->pfnsubnet.SSID_len));
1356 				plnetinfo->pfnsubnet.SSID_len = DOT11_MAX_SSID_LEN;
1357 			}
1358 			pbestnet_entry->SSID_len = plnetinfo->pfnsubnet.SSID_len;
1359 			memcpy(pbestnet_entry->SSID, plnetinfo->pfnsubnet.SSID,
1360 				pbestnet_entry->SSID_len);
1361 			memcpy(&pbestnet_entry->BSSID, &plnetinfo->pfnsubnet.BSSID, ETHER_ADDR_LEN);
1362 			/* add the element into list */
1363 			list_add_tail(&pbestnet_entry->list, &pbestnetheader->entry_list);
1364 			/* increase best entry count */
1365 			pbestnetheader->tot_cnt++;
1366 			pbestnetheader->tot_size += BESTNET_ENTRY_SIZE;
1367 			DHD_PNO(("Header %d\n", pscan_results->cnt_header - 1));
1368 			DHD_PNO(("\tSSID : "));
1369 			for (j = 0; j < plnetinfo->pfnsubnet.SSID_len; j++)
1370 				DHD_PNO(("%c", plnetinfo->pfnsubnet.SSID[j]));
1371 			DHD_PNO(("\n"));
1372 			DHD_PNO(("\tBSSID: %02x:%02x:%02x:%02x:%02x:%02x\n",
1373 				plnetinfo->pfnsubnet.BSSID.octet[0],
1374 				plnetinfo->pfnsubnet.BSSID.octet[1],
1375 				plnetinfo->pfnsubnet.BSSID.octet[2],
1376 				plnetinfo->pfnsubnet.BSSID.octet[3],
1377 				plnetinfo->pfnsubnet.BSSID.octet[4],
1378 				plnetinfo->pfnsubnet.BSSID.octet[5]));
1379 			DHD_PNO(("\tchannel: %d, RSSI: %d, timestamp: %d ms\n",
1380 				plnetinfo->pfnsubnet.channel,
1381 				plnetinfo->RSSI, plnetinfo->timestamp));
1382 			DHD_PNO(("\tRTT0 : %d, RTT1: %d\n", plnetinfo->rtt0, plnetinfo->rtt1));
1383 			plnetinfo++;
1384 		}
1385 	}
1386 	if (pscan_results->cnt_header == 0) {
1387 		/* In case that we didn't get any data from the firmware
1388 		 * Remove the current scan_result list from get_bach.scan_results_list.
1389 		 */
1390 		DHD_PNO(("NO BATCH DATA from Firmware, Delete current SCAN RESULT LIST\n"));
1391 		list_del(&pscan_results->list);
1392 		MFREE(dhd->osh, pscan_results, SCAN_RESULTS_SIZE);
1393 		_params->params_batch.get_batch.top_node_cnt--;
1394 	}
1395 	/* increase total scan count using current scan count */
1396 	_params->params_batch.get_batch.tot_scan_cnt += pscan_results->cnt_header;
1397 
1398 	if (buf && bufsize) {
1399 		/* This is a first try to get batching results */
1400 		if (!list_empty(&_params->params_batch.get_batch.scan_results_list)) {
1401 			/* move the scan_results_list to expired_scan_results_lists */
1402 			list_for_each_entry_safe(siter, snext,
1403 				&_params->params_batch.get_batch.scan_results_list, list) {
1404 				list_move_tail(&siter->list,
1405 					&_params->params_batch.get_batch.expired_scan_results_list);
1406 			}
1407 			/* reset gloval values after  moving to expired list */
1408 			_params->params_batch.get_batch.top_node_cnt = 0;
1409 			_params->params_batch.get_batch.expired_tot_scan_cnt =
1410 				_params->params_batch.get_batch.tot_scan_cnt;
1411 			_params->params_batch.get_batch.tot_scan_cnt = 0;
1412 		}
1413 convert_format:
1414 		err = _dhd_pno_convert_format(dhd, &_params->params_batch, buf, bufsize);
1415 		if (err < 0) {
1416 			DHD_ERROR(("failed to convert the data into upper layer format\n"));
1417 			goto exit;
1418 		}
1419 	}
1420 exit:
1421 	if (plbestnet)
1422 		MFREE(dhd->osh, plbestnet, PNO_BESTNET_LEN);
1423 	if (_params) {
1424 		_params->params_batch.get_batch.buf = NULL;
1425 		_params->params_batch.get_batch.bufsize = 0;
1426 		_params->params_batch.get_batch.bytes_written = err;
1427 	}
1428 	mutex_unlock(&_pno_state->pno_mutex);
1429 	if (waitqueue_active(&_pno_state->get_batch_done.wait))
1430 		complete(&_pno_state->get_batch_done);
1431 	return err;
1432 }
1433 static void
_dhd_pno_get_batch_handler(struct work_struct * work)1434 _dhd_pno_get_batch_handler(struct work_struct *work)
1435 {
1436 	dhd_pno_status_info_t *_pno_state;
1437 	dhd_pub_t *dhd;
1438 	struct dhd_pno_batch_params *params_batch;
1439 	DHD_PNO(("%s enter\n", __FUNCTION__));
1440 	_pno_state = container_of(work, struct dhd_pno_status_info, work);
1441 	dhd = _pno_state->dhd;
1442 	if (dhd == NULL) {
1443 		DHD_ERROR(("%s : dhd is NULL\n", __FUNCTION__));
1444 		return;
1445 	}
1446 	params_batch = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS].params_batch;
1447 	_dhd_pno_get_for_batch(dhd, params_batch->get_batch.buf,
1448 		params_batch->get_batch.bufsize, params_batch->get_batch.reason);
1449 
1450 }
1451 
1452 int
dhd_pno_get_for_batch(dhd_pub_t * dhd,char * buf,int bufsize,int reason)1453 dhd_pno_get_for_batch(dhd_pub_t *dhd, char *buf, int bufsize, int reason)
1454 {
1455 	int err = BCME_OK;
1456 	char *pbuf = buf;
1457 	dhd_pno_status_info_t *_pno_state;
1458 	struct dhd_pno_batch_params *params_batch;
1459 	NULL_CHECK(dhd, "dhd is NULL", err);
1460 	NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
1461 	if (!dhd_support_sta_mode(dhd)) {
1462 		err = BCME_BADOPTION;
1463 		goto exit;
1464 	}
1465 	DHD_PNO(("%s enter\n", __FUNCTION__));
1466 	_pno_state = PNO_GET_PNOSTATE(dhd);
1467 
1468 	if (!WLS_SUPPORTED(_pno_state)) {
1469 		DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
1470 		err = BCME_UNSUPPORTED;
1471 		goto exit;
1472 	}
1473 	params_batch = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS].params_batch;
1474 	if (!(_pno_state->pno_mode & DHD_PNO_BATCH_MODE)) {
1475 		DHD_ERROR(("%s: Batching SCAN mode is not enabled\n", __FUNCTION__));
1476 		memset(pbuf, 0, bufsize);
1477 		pbuf += sprintf(pbuf, "scancount=%d\n", 0);
1478 		sprintf(pbuf, "%s", RESULTS_END_MARKER);
1479 		err = strlen(buf);
1480 		goto exit;
1481 	}
1482 	params_batch->get_batch.buf = buf;
1483 	params_batch->get_batch.bufsize = bufsize;
1484 	params_batch->get_batch.reason = reason;
1485 	params_batch->get_batch.bytes_written = 0;
1486 	schedule_work(&_pno_state->work);
1487 	wait_for_completion(&_pno_state->get_batch_done);
1488 	err = params_batch->get_batch.bytes_written;
1489 exit:
1490 	return err;
1491 }
1492 
1493 int
dhd_pno_stop_for_batch(dhd_pub_t * dhd)1494 dhd_pno_stop_for_batch(dhd_pub_t *dhd)
1495 {
1496 	int err = BCME_OK;
1497 	int mode = 0;
1498 	int i = 0;
1499 	dhd_pno_status_info_t *_pno_state;
1500 	dhd_pno_params_t *_params;
1501 	wl_pfn_bssid_t *p_pfn_bssid;
1502 	wlc_ssid_ext_t *p_ssid_list = NULL;
1503 	NULL_CHECK(dhd, "dhd is NULL", err);
1504 	NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
1505 	_pno_state = PNO_GET_PNOSTATE(dhd);
1506 	DHD_PNO(("%s enter\n", __FUNCTION__));
1507 	if (!dhd_support_sta_mode(dhd)) {
1508 		err = BCME_BADOPTION;
1509 		goto exit;
1510 	}
1511 	if (!WLS_SUPPORTED(_pno_state)) {
1512 		DHD_ERROR(("%s : wifi location service is not supported\n",
1513 			__FUNCTION__));
1514 		err = BCME_UNSUPPORTED;
1515 		goto exit;
1516 	}
1517 	if (!(_pno_state->pno_mode & DHD_PNO_BATCH_MODE)) {
1518 		DHD_ERROR(("%s : PNO BATCH MODE is not enabled\n", __FUNCTION__));
1519 		goto exit;
1520 	}
1521 	_pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
1522 	if (_pno_state->pno_mode & (DHD_PNO_LEGACY_MODE | DHD_PNO_HOTLIST_MODE)) {
1523 		mode = _pno_state->pno_mode;
1524 		dhd_pno_clean(dhd);
1525 		_pno_state->pno_mode = mode;
1526 		/* restart Legacy PNO if the Legacy PNO is on */
1527 		if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
1528 			struct dhd_pno_legacy_params *_params_legacy;
1529 			_params_legacy =
1530 				&(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS].params_legacy);
1531 			p_ssid_list = dhd_pno_get_legacy_pno_ssid(dhd, _pno_state);
1532 			if (!p_ssid_list) {
1533 				err = BCME_NOMEM;
1534 				DHD_ERROR(("failed to get Legacy PNO SSID list\n"));
1535 				goto exit;
1536 			}
1537 			err = dhd_pno_set_for_ssid(dhd, p_ssid_list, _params_legacy->nssid,
1538 				_params_legacy->scan_fr, _params_legacy->pno_repeat,
1539 				_params_legacy->pno_freq_expo_max, _params_legacy->chan_list,
1540 				_params_legacy->nchan);
1541 			if (err < 0) {
1542 				_pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
1543 				DHD_ERROR(("%s : failed to restart legacy PNO scan(err: %d)\n",
1544 					__FUNCTION__, err));
1545 				goto exit;
1546 			}
1547 		} else if (_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE) {
1548 			struct dhd_pno_bssid *iter, *next;
1549 			_params = &(_pno_state->pno_params_arr[INDEX_OF_HOTLIST_PARAMS]);
1550 			p_pfn_bssid = kzalloc(sizeof(wl_pfn_bssid_t) *
1551 				_params->params_hotlist.nbssid, GFP_KERNEL);
1552 			if (p_pfn_bssid == NULL) {
1553 				DHD_ERROR(("%s : failed to allocate wl_pfn_bssid_t array"
1554 					" (count: %d)",
1555 					__FUNCTION__, _params->params_hotlist.nbssid));
1556 				err = BCME_ERROR;
1557 				_pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
1558 				goto exit;
1559 			}
1560 			i = 0;
1561 			/* convert dhd_pno_bssid to wl_pfn_bssid */
1562 			list_for_each_entry_safe(iter, next,
1563 				&_params->params_hotlist.bssid_list, list) {
1564 				memcpy(&p_pfn_bssid[i].macaddr, &iter->macaddr, ETHER_ADDR_LEN);
1565 				p_pfn_bssid[i].flags = iter->flags;
1566 				i++;
1567 			}
1568 			err = dhd_pno_set_for_hotlist(dhd, p_pfn_bssid, &_params->params_hotlist);
1569 			if (err < 0) {
1570 				_pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
1571 				DHD_ERROR(("%s : failed to restart hotlist scan(err: %d)\n",
1572 					__FUNCTION__, err));
1573 				goto exit;
1574 			}
1575 		}
1576 	} else {
1577 		err = dhd_pno_clean(dhd);
1578 		if (err < 0) {
1579 			DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
1580 				__FUNCTION__, err));
1581 			goto exit;
1582 		}
1583 	}
1584 exit:
1585 	_params = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS];
1586 	_dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_BATCH_MODE);
1587 	kfree(p_ssid_list);
1588 	return err;
1589 }
1590 
1591 int
dhd_pno_set_for_hotlist(dhd_pub_t * dhd,wl_pfn_bssid_t * p_pfn_bssid,struct dhd_pno_hotlist_params * hotlist_params)1592 dhd_pno_set_for_hotlist(dhd_pub_t *dhd, wl_pfn_bssid_t *p_pfn_bssid,
1593 	struct dhd_pno_hotlist_params *hotlist_params)
1594 {
1595 	int err = BCME_OK;
1596 	int i;
1597 	uint16 _chan_list[WL_NUMCHANNELS];
1598 	int rem_nchan = 0;
1599 	int tot_nchan = 0;
1600 	int mode = 0;
1601 	dhd_pno_params_t *_params;
1602 	dhd_pno_params_t *_params2;
1603 	struct dhd_pno_bssid *_pno_bssid;
1604 	dhd_pno_status_info_t *_pno_state;
1605 	NULL_CHECK(dhd, "dhd is NULL", err);
1606 	NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
1607 	NULL_CHECK(hotlist_params, "hotlist_params is NULL", err);
1608 	NULL_CHECK(p_pfn_bssid, "p_pfn_bssid is NULL", err);
1609 	_pno_state = PNO_GET_PNOSTATE(dhd);
1610 	DHD_PNO(("%s enter\n", __FUNCTION__));
1611 
1612 	if (!dhd_support_sta_mode(dhd)) {
1613 		err = BCME_BADOPTION;
1614 		goto exit;
1615 	}
1616 	if (!WLS_SUPPORTED(_pno_state)) {
1617 		DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
1618 		err = BCME_UNSUPPORTED;
1619 		goto exit;
1620 	}
1621 	_params = &_pno_state->pno_params_arr[INDEX_OF_HOTLIST_PARAMS];
1622 	if (!(_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE)) {
1623 		_pno_state->pno_mode |= DHD_PNO_HOTLIST_MODE;
1624 		err = _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_HOTLIST_MODE);
1625 		if (err < 0) {
1626 			DHD_ERROR(("%s : failed to call _dhd_pno_reinitialize_prof\n",
1627 				__FUNCTION__));
1628 			goto exit;
1629 		}
1630 	}
1631 	_params->params_batch.nchan = hotlist_params->nchan;
1632 	_params->params_batch.scan_fr = hotlist_params->scan_fr;
1633 	if (hotlist_params->nchan)
1634 		memcpy(_params->params_hotlist.chan_list, hotlist_params->chan_list,
1635 			sizeof(_params->params_hotlist.chan_list));
1636 	memset(_chan_list, 0, sizeof(_chan_list));
1637 
1638 	rem_nchan = ARRAYSIZE(hotlist_params->chan_list) - hotlist_params->nchan;
1639 	if (hotlist_params->band == WLC_BAND_2G || hotlist_params->band == WLC_BAND_5G) {
1640 		/* get a valid channel list based on band B or A */
1641 		err = _dhd_pno_get_channels(dhd,
1642 		&_params->params_hotlist.chan_list[hotlist_params->nchan],
1643 		&rem_nchan, hotlist_params->band, FALSE);
1644 		if (err < 0) {
1645 			DHD_ERROR(("%s: failed to get valid channel list(band : %d)\n",
1646 				__FUNCTION__, hotlist_params->band));
1647 			goto exit;
1648 		}
1649 		/* now we need to update nchan because rem_chan has valid channel count */
1650 		_params->params_hotlist.nchan += rem_nchan;
1651 		/* need to sort channel list */
1652 		sort(_params->params_hotlist.chan_list, _params->params_hotlist.nchan,
1653 			sizeof(_params->params_hotlist.chan_list[0]), _dhd_pno_cmpfunc, NULL);
1654 	}
1655 #ifdef PNO_DEBUG
1656 {
1657 		int i;
1658 		DHD_PNO(("Channel list : "));
1659 		for (i = 0; i < _params->params_batch.nchan; i++) {
1660 			DHD_PNO(("%d ", _params->params_batch.chan_list[i]));
1661 		}
1662 		DHD_PNO(("\n"));
1663 }
1664 #endif
1665 	if (_params->params_hotlist.nchan) {
1666 		/* copy the channel list into local array */
1667 		memcpy(_chan_list, _params->params_hotlist.chan_list,
1668 			sizeof(_chan_list));
1669 		tot_nchan = _params->params_hotlist.nchan;
1670 	}
1671 	if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
1672 			DHD_PNO(("PNO SSID is on progress in firmware\n"));
1673 			/* store current pno_mode before disabling pno */
1674 			mode = _pno_state->pno_mode;
1675 			err = _dhd_pno_enable(dhd, PNO_OFF);
1676 			if (err < 0) {
1677 				DHD_ERROR(("%s : failed to disable PNO\n", __FUNCTION__));
1678 				goto exit;
1679 			}
1680 			/* restore the previous mode */
1681 			_pno_state->pno_mode = mode;
1682 			/* Use the superset for channelist between two mode */
1683 			_params2 = &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS]);
1684 			if (_params2->params_legacy.nchan > 0 &&
1685 				_params->params_hotlist.nchan > 0) {
1686 				err = _dhd_pno_chan_merge(_chan_list, &tot_nchan,
1687 					&_params2->params_legacy.chan_list[0],
1688 					_params2->params_legacy.nchan,
1689 					&_params->params_hotlist.chan_list[0],
1690 					_params->params_hotlist.nchan);
1691 				if (err < 0) {
1692 					DHD_ERROR(("%s : failed to merge channel list"
1693 						"between legacy and hotlist\n",
1694 						__FUNCTION__));
1695 					goto exit;
1696 				}
1697 			}
1698 
1699 	}
1700 
1701 	INIT_LIST_HEAD(&(_params->params_hotlist.bssid_list));
1702 
1703 	err = _dhd_pno_add_bssid(dhd, p_pfn_bssid, hotlist_params->nbssid);
1704 	if (err < 0) {
1705 		DHD_ERROR(("%s : failed to call _dhd_pno_add_bssid(err :%d)\n",
1706 			__FUNCTION__, err));
1707 		goto exit;
1708 	}
1709 	if ((err = _dhd_pno_set(dhd, _params, DHD_PNO_HOTLIST_MODE)) < 0) {
1710 		DHD_ERROR(("%s : failed to set call pno_set (err %d) in firmware\n",
1711 			__FUNCTION__, err));
1712 		goto exit;
1713 	}
1714 	if (tot_nchan > 0) {
1715 		if ((err = _dhd_pno_cfg(dhd, _chan_list, tot_nchan)) < 0) {
1716 			DHD_ERROR(("%s : failed to set call pno_cfg (err %d) in firmware\n",
1717 				__FUNCTION__, err));
1718 			goto exit;
1719 		}
1720 	}
1721 	for (i = 0; i < hotlist_params->nbssid; i++) {
1722 		_pno_bssid = kzalloc(sizeof(struct dhd_pno_bssid), GFP_KERNEL);
1723 		NULL_CHECK(_pno_bssid, "_pfn_bssid is NULL", err);
1724 		memcpy(&_pno_bssid->macaddr, &p_pfn_bssid[i].macaddr, ETHER_ADDR_LEN);
1725 		_pno_bssid->flags = p_pfn_bssid[i].flags;
1726 		list_add_tail(&_pno_bssid->list, &_params->params_hotlist.bssid_list);
1727 	}
1728 	_params->params_hotlist.nbssid = hotlist_params->nbssid;
1729 	if (_pno_state->pno_status == DHD_PNO_DISABLED) {
1730 		if ((err = _dhd_pno_enable(dhd, PNO_ON)) < 0)
1731 			DHD_ERROR(("%s : failed to enable PNO\n", __FUNCTION__));
1732 	}
1733 exit:
1734 	/* clear mode in case of error */
1735 	if (err < 0)
1736 		_pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
1737 	return err;
1738 }
1739 
1740 int
dhd_pno_stop_for_hotlist(dhd_pub_t * dhd)1741 dhd_pno_stop_for_hotlist(dhd_pub_t *dhd)
1742 {
1743 	int err = BCME_OK;
1744 	uint32 mode = 0;
1745 	dhd_pno_status_info_t *_pno_state;
1746 	dhd_pno_params_t *_params;
1747 	wlc_ssid_ext_t *p_ssid_list;
1748 	NULL_CHECK(dhd, "dhd is NULL", err);
1749 	NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
1750 	_pno_state = PNO_GET_PNOSTATE(dhd);
1751 
1752 	if (!WLS_SUPPORTED(_pno_state)) {
1753 		DHD_ERROR(("%s : wifi location service is not supported\n",
1754 			__FUNCTION__));
1755 		err = BCME_UNSUPPORTED;
1756 		goto exit;
1757 	}
1758 
1759 	if (!(_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE)) {
1760 		DHD_ERROR(("%s : Hotlist MODE is not enabled\n",
1761 			__FUNCTION__));
1762 		goto exit;
1763 	}
1764 	_pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
1765 
1766 	if (_pno_state->pno_mode & (DHD_PNO_LEGACY_MODE | DHD_PNO_BATCH_MODE)) {
1767 		/* retrieve the batching data from firmware into host */
1768 		dhd_pno_get_for_batch(dhd, NULL, 0, PNO_STATUS_DISABLE);
1769 		/* save current pno_mode before calling dhd_pno_clean */
1770 		mode = _pno_state->pno_mode;
1771 		err = dhd_pno_clean(dhd);
1772 		if (err < 0) {
1773 			DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
1774 				__FUNCTION__, err));
1775 			goto exit;
1776 		}
1777 		/* restore previos pno mode */
1778 		_pno_state->pno_mode = mode;
1779 		if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
1780 			/* restart Legacy PNO Scan */
1781 			struct dhd_pno_legacy_params *_params_legacy;
1782 			_params_legacy =
1783 			&(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS].params_legacy);
1784 			p_ssid_list = dhd_pno_get_legacy_pno_ssid(dhd, _pno_state);
1785 			if (!p_ssid_list) {
1786 				err = BCME_NOMEM;
1787 				DHD_ERROR(("failed to get Legacy PNO SSID list\n"));
1788 				goto exit;
1789 			}
1790 			err = dhd_pno_set_for_ssid(dhd, p_ssid_list, _params_legacy->nssid,
1791 				_params_legacy->scan_fr, _params_legacy->pno_repeat,
1792 				_params_legacy->pno_freq_expo_max, _params_legacy->chan_list,
1793 				_params_legacy->nchan);
1794 			if (err < 0) {
1795 				_pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
1796 				DHD_ERROR(("%s : failed to restart legacy PNO scan(err: %d)\n",
1797 					__FUNCTION__, err));
1798 				goto exit;
1799 			}
1800 		} else if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
1801 			/* restart Batching Scan */
1802 			_params = &(_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS]);
1803 			/* restart BATCH SCAN */
1804 			err = dhd_pno_set_for_batch(dhd, &_params->params_batch);
1805 			if (err < 0) {
1806 				_pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
1807 				DHD_ERROR(("%s : failed to restart batch scan(err: %d)\n",
1808 					__FUNCTION__,  err));
1809 				goto exit;
1810 			}
1811 		}
1812 	} else {
1813 		err = dhd_pno_clean(dhd);
1814 		if (err < 0) {
1815 			DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
1816 				__FUNCTION__, err));
1817 			goto exit;
1818 		}
1819 	}
1820 exit:
1821 	return err;
1822 }
1823 
1824 int
dhd_pno_event_handler(dhd_pub_t * dhd,wl_event_msg_t * event,void * event_data)1825 dhd_pno_event_handler(dhd_pub_t *dhd, wl_event_msg_t *event, void *event_data)
1826 {
1827 	int err = BCME_OK;
1828 	uint status, event_type, flags, datalen;
1829 	dhd_pno_status_info_t *_pno_state;
1830 	NULL_CHECK(dhd, "dhd is NULL", err);
1831 	NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
1832 	_pno_state = PNO_GET_PNOSTATE(dhd);
1833 	if (!WLS_SUPPORTED(_pno_state)) {
1834 		DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
1835 		err = BCME_UNSUPPORTED;
1836 		goto exit;
1837 	}
1838 	event_type = ntoh32(event->event_type);
1839 	flags = ntoh16(event->flags);
1840 	status = ntoh32(event->status);
1841 	datalen = ntoh32(event->datalen);
1842 	DHD_PNO(("%s enter : event_type :%d\n", __FUNCTION__, event_type));
1843 	switch (event_type) {
1844 	case WLC_E_PFN_BSSID_NET_FOUND:
1845 	case WLC_E_PFN_BSSID_NET_LOST:
1846 		/* XXX : how can we inform this to framework ? */
1847 		/* TODO : need to implement event logic using generic netlink */
1848 		break;
1849 	case WLC_E_PFN_BEST_BATCHING:
1850 	{
1851 		struct dhd_pno_batch_params *params_batch;
1852 		params_batch = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS].params_batch;
1853 		if (!waitqueue_active(&_pno_state->get_batch_done.wait)) {
1854 			DHD_PNO(("%s : WLC_E_PFN_BEST_BATCHING\n", __FUNCTION__));
1855 			params_batch->get_batch.buf = NULL;
1856 			params_batch->get_batch.bufsize = 0;
1857 			params_batch->get_batch.reason = PNO_STATUS_EVENT;
1858 			schedule_work(&_pno_state->work);
1859 		} else
1860 			DHD_PNO(("%s : WLC_E_PFN_BEST_BATCHING"
1861 				"will skip this event\n", __FUNCTION__));
1862 		break;
1863 	}
1864 	default:
1865 		DHD_ERROR(("unknown event : %d\n", event_type));
1866 	}
1867 exit:
1868 	return err;
1869 }
1870 
dhd_pno_init(dhd_pub_t * dhd)1871 int dhd_pno_init(dhd_pub_t *dhd)
1872 {
1873 	int err = BCME_OK;
1874 	dhd_pno_status_info_t *_pno_state;
1875 	NULL_CHECK(dhd, "dhd is NULL", err);
1876 	DHD_PNO(("%s enter\n", __FUNCTION__));
1877 	UNUSED_PARAMETER(_dhd_pno_suspend);
1878 	if (dhd->pno_state)
1879 		goto exit;
1880 	dhd->pno_state = MALLOC(dhd->osh, sizeof(dhd_pno_status_info_t));
1881 	NULL_CHECK(dhd->pno_state, "failed to create dhd_pno_state", err);
1882 	memset(dhd->pno_state, 0, sizeof(dhd_pno_status_info_t));
1883 	/* need to check whether current firmware support batching and hotlist scan */
1884 	_pno_state = PNO_GET_PNOSTATE(dhd);
1885 	_pno_state->wls_supported = TRUE;
1886 	_pno_state->dhd = dhd;
1887 	mutex_init(&_pno_state->pno_mutex);
1888 	INIT_WORK(&_pno_state->work, _dhd_pno_get_batch_handler);
1889 	init_completion(&_pno_state->get_batch_done);
1890 	err = dhd_iovar(dhd, 0, "pfnlbest", NULL, 0, 0);
1891 	if (err == BCME_UNSUPPORTED) {
1892 		_pno_state->wls_supported = FALSE;
1893 		DHD_INFO(("Current firmware doesn't support"
1894 			" Android Location Service\n"));
1895 	}
1896 exit:
1897 	return err;
1898 }
dhd_pno_deinit(dhd_pub_t * dhd)1899 int dhd_pno_deinit(dhd_pub_t *dhd)
1900 {
1901 	int err = BCME_OK;
1902 	dhd_pno_status_info_t *_pno_state;
1903 	dhd_pno_params_t *_params;
1904 	NULL_CHECK(dhd, "dhd is NULL", err);
1905 
1906 	DHD_PNO(("%s enter\n", __FUNCTION__));
1907 	_pno_state = PNO_GET_PNOSTATE(dhd);
1908 	NULL_CHECK(_pno_state, "pno_state is NULL", err);
1909 	/* may need to free legacy ssid_list */
1910 	if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
1911 		_params = &_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS];
1912 		_dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_LEGACY_MODE);
1913 	}
1914 	if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
1915 		_params = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS];
1916 		/* clear resource if the BATCH MODE is on */
1917 		_dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_BATCH_MODE);
1918 	}
1919 	cancel_work_sync(&_pno_state->work);
1920 	MFREE(dhd->osh, _pno_state, sizeof(dhd_pno_status_info_t));
1921 	dhd->pno_state = NULL;
1922 	return err;
1923 }
1924