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