2 * Broadcom Dongle Host Driver (DHD)
3 * Prefered Network Offload and Wi-Fi Location Service(WLS) code.
5 * Copyright (C) 1999-2016, Broadcom Corporation
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:
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.
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.
26 * <<Broadcom-WL-IPTag/Open:>>
28 * $Id: dhd_pno.c 606280 2015-12-15 05:28:25Z $
31 #if defined(GSCAN_SUPPORT) && !defined(PNO_SUPPORT)
32 #error "GSCAN needs PNO to be enabled!"
42 #include <bcmendian.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>
51 #include <proto/bcmevent.h>
56 #include <linux/gcd.h>
57 #endif /* GSCAN_SUPPORT */
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)
72 #define htodchanspec(i) (i)
73 #define dtohchanspec(i) (i)
74 #endif /* IL_BIGENDINA */
76 #define NULL_CHECK(p, s, err) \
79 printf("NULL POINTER (%s) : %s\n", __FUNCTION__, (s)); \
84 #define PNO_GET_PNOSTATE(dhd) ((dhd_pno_status_info_t *)dhd->pno_state)
85 #define PNO_BESTNET_LEN 1024
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)
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
);
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 */
110 is_dfs(uint16 channel
)
112 if (channel
>= 52 && channel
<= 64) /* class 2 */
114 else if (channel
>= 100 && channel
<= 140) /* class 4 */
120 dhd_pno_clean(dhd_pub_t
*dhd
)
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__
));
130 err
= dhd_iovar(dhd
, 0, "pfn", (char *)&pfn
, sizeof(pfn
), 1);
132 DHD_ERROR(("%s : failed to execute pfn(error : %d)\n",
136 _pno_state
->pno_status
= DHD_PNO_DISABLED
;
137 err
= dhd_iovar(dhd
, 0, "pfnclear", NULL
, 0, 1);
139 DHD_ERROR(("%s : failed to execute pfnclear(error : %d)\n",
147 dhd_is_pno_supported(dhd_pub_t
*dhd
)
149 dhd_pno_status_info_t
*_pno_state
;
151 if (!dhd
|| !dhd
->pno_state
) {
152 DHD_ERROR(("NULL POINTER : %s\n",
156 _pno_state
= PNO_GET_PNOSTATE(dhd
);
157 return WLS_SUPPORTED(_pno_state
);
161 dhd_pno_set_mac_oui(dhd_pub_t
*dhd
, uint8
*oui
)
164 dhd_pno_status_info_t
*_pno_state
;
166 if (!dhd
|| !dhd
->pno_state
) {
167 DHD_ERROR(("NULL POINTER : %s\n", __FUNCTION__
));
170 _pno_state
= PNO_GET_PNOSTATE(dhd
);
171 if (ETHER_ISMULTI(oui
)) {
172 DHD_ERROR(("Expected unicast OUI\n"));
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]));
185 convert_fw_rel_time_to_systime(uint32 fw_ts_ms
)
189 get_monotonic_boottime(&ts
);
190 return ((uint64
)(TIMESPEC_TO_US(ts
)) - (uint64
)(fw_ts_ms
* 1000));
194 _dhd_pno_gscan_cfg(dhd_pub_t
*dhd
, wl_pfn_gscan_cfg_t
*pfncfg_gscan_param
, int size
)
197 NULL_CHECK(dhd
, "dhd is NULL", err
);
199 DHD_PNO(("%s enter\n", __FUNCTION__
));
201 err
= dhd_iovar(dhd
, 0, "pfn_gscan_cfg", (char *)pfncfg_gscan_param
, size
, 1);
203 DHD_ERROR(("%s : failed to execute pfncfg_gscan_param\n", __FUNCTION__
));
211 is_batch_retrieval_complete(struct dhd_pno_gscan_params
*gscan_params
)
214 return (gscan_params
->get_batch_flag
== GSCAN_BATCH_RETRIEVAL_COMPLETE
);
216 #endif /* GSCAN_SUPPORT */
219 dhd_pno_set_mac_addr(dhd_pub_t
*dhd
, struct ether_addr
*macaddr
)
222 wl_pfn_macaddr_cfg_t cfg
;
224 cfg
.version
= WL_PFN_MACADDR_CFG_VER
;
225 if (ETHER_ISNULLADDR(macaddr
)) {
228 cfg
.flags
= (WL_PFN_MAC_OUI_ONLY_MASK
| WL_PFN_SET_MAC_UNASSOC_MASK
);
230 memcpy(&cfg
.macaddr
, macaddr
, ETHER_ADDR_LEN
);
232 err
= dhd_iovar(dhd
, 0, "pfn_macaddr", (char *)&cfg
, sizeof(cfg
), 1);
234 DHD_ERROR(("%s : failed to execute pfn_macaddr\n", __FUNCTION__
));
241 _dhd_pno_suspend(dhd_pub_t
*dhd
)
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
);
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);
253 DHD_ERROR(("%s : failed to suspend pfn(error :%d)\n", __FUNCTION__
, err
));
257 _pno_state
->pno_status
= DHD_PNO_SUSPEND
;
262 _dhd_pno_enable(dhd_pub_t
*dhd
, int enable
)
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__
));
271 if (enable
& 0xfffe) {
272 DHD_ERROR(("%s invalid value\n", __FUNCTION__
));
276 if (!dhd_support_sta_mode(dhd
)) {
277 DHD_ERROR(("PNO is not allowed for non-STA mode"));
278 err
= BCME_BADOPTION
;
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
;
290 /* Enable/Disable PNO */
291 err
= dhd_iovar(dhd
, 0, "pfn", (char *)&enable
, sizeof(enable
), 1);
293 DHD_ERROR(("%s : failed to execute pfn_set - %d\n", __FUNCTION__
, err
));
296 _pno_state
->pno_status
= (enable
)?
297 DHD_PNO_ENABLED
: DHD_PNO_DISABLED
;
299 _pno_state
->pno_mode
= DHD_PNO_NONE_MODE
;
301 DHD_PNO(("%s set pno as %s\n",
302 __FUNCTION__
, enable
? "Enable" : "Disable"));
308 _dhd_pno_set(dhd_pub_t
*dhd
, const dhd_pno_params_t
*pno_params
, dhd_pno_mode_t mode
)
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__
));
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
);
322 memset(&pfn_param
, 0, sizeof(pfn_param
));
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
);
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
;
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
;
353 #endif /* GSCAN_SUPPORT */
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
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
);
389 pfn_param
.repeat
= 0;
390 /* enable broadcast scan */
391 pfn_param
.flags
|= (ENABLE
<< ENABLE_BD_SCAN_BIT
);
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;
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
);
414 pfn_param
.repeat
= 0;
419 if (mode
& DHD_PNO_GSCAN_MODE
) {
420 uint32 lost_network_timeout
;
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
);
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;
434 pfn_param
.slow_freq
= 0;
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
;
440 _params
= &(_pno_state
->pno_params_arr
[INDEX_OF_LEGACY_PARAMS
]);
442 pfn_param
.scan_freq
= htod32(MIN(pno_params
->params_gscan
.scan_fr
,
443 _params
->params_legacy
.scan_fr
));
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
));
453 pfn_param
.lost_network_timeout
= htod32(GSCAN_MIN_BSSID_TIMEOUT
);
456 #endif /* GSCAN_SUPPORT */
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
));
467 memset(&macaddr
, 0, ETHER_ADDR_LEN
);
468 memcpy(&macaddr
, _pno_state
->pno_oui
, DOT11_OUI_LEN
);
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
);
474 DHD_ERROR(("%s : failed to set pno mac address, error - %d\n", __FUNCTION__
, err
));
480 if (mode
== DHD_PNO_BATCH_MODE
||
481 ((mode
& DHD_PNO_GSCAN_MODE
) && pno_params
->params_gscan
.mscan
))
483 if (mode
== DHD_PNO_BATCH_MODE
)
484 #endif /* GSCAN_SUPPORT */
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);
490 DHD_ERROR(("%s : failed to set pfnmem\n", __FUNCTION__
));
493 /* get max mscan which the firmware supports */
494 err
= dhd_iovar(dhd
, 0, "pfnmem", (char *)&_tmp
, sizeof(_tmp
), 0);
496 DHD_ERROR(("%s : failed to get pfnmem\n", __FUNCTION__
));
499 DHD_PNO((" returned mscan : %d, set bestn : %d\n", _tmp
, pfn_param
.bestn
));
500 pfn_param
.mscan
= MIN(pfn_param
.mscan
, _tmp
);
502 err
= dhd_iovar(dhd
, 0, "pfn_set", (char *)&pfn_param
, sizeof(pfn_param
), 1);
504 DHD_ERROR(("%s : failed to execute pfn_set %d\n", __FUNCTION__
, err
));
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
;
514 _dhd_pno_add_ssid(dhd_pub_t
*dhd
, wlc_ssid_ext_t
* ssids_list
, int nssid
)
518 wl_pfn_t pfn_element
;
519 NULL_CHECK(dhd
, "dhd is NULL", err
);
521 NULL_CHECK(ssids_list
, "ssid list is NULL", err
);
523 memset(&pfn_element
, 0, sizeof(pfn_element
));
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
));
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
));
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
);
549 pfn_element
.flags
= 0;
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);
557 DHD_ERROR(("%s : failed to execute pfn_add\n", __FUNCTION__
));
565 /* qsort compare function */
567 _dhd_pno_cmpfunc(const void *a
, const void *b
)
569 return (*(uint16
*)a
- *(uint16
*)b
);
573 _dhd_pno_chan_merge(uint16
*d_chan_list
, int *nchan
,
574 uint16
*chan_list1
, int nchan1
, uint16
*chan_list2
, int nchan2
)
577 int i
= 0, j
= 0, k
= 0;
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
;
593 tmp
= chan_list1
[i
++];
594 for (; i
< nchan1
&& chan_list1
[i
] == tmp
; i
++);
595 d_chan_list
[k
++] = tmp
;
599 tmp
= chan_list2
[j
++];
600 for (; j
< nchan2
&& chan_list2
[j
] == tmp
; j
++);
601 d_chan_list
[k
++] = tmp
;
609 _dhd_pno_get_channels(dhd_pub_t
*dhd
, uint16
*d_chan_list
,
610 int *nchan
, uint8 band
, bool skip_dfs
)
614 uint32 chan_buf
[WL_NUMCHANNELS
+ 1];
615 wl_uint32_list_t
*list
;
616 NULL_CHECK(dhd
, "dhd is NULL", err
);
618 NULL_CHECK(d_chan_list
, "d_chan_list is NULL", err
);
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);
624 DHD_ERROR(("failed to get channel list (err: %d)\n", err
));
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
)
631 } else if (band
== WLC_BAND_5G
) {
632 if (dtoh32(list
->element
[i
]) <= CHANNEL_2G_MAX
)
634 if (skip_dfs
&& is_dfs(dtoh32(list
->element
[i
])))
638 } else if (band
== WLC_BAND_AUTO
) {
639 if (skip_dfs
|| !is_dfs(dtoh32(list
->element
[i
])))
641 } else { /* All channels */
642 if (skip_dfs
&& is_dfs(dtoh32(list
->element
[i
])))
645 if (dtoh32(list
->element
[i
]) <= CHANNEL_5G_MAX
) {
646 d_chan_list
[j
++] = (uint16
) dtoh32(list
->element
[i
]);
658 _dhd_pno_convert_format(dhd_pub_t
*dhd
, struct dhd_pno_batch_params
*params_batch
,
659 char *buf
, int nbufsize
)
662 int bytes_written
= 0, nreadsize
= 0;
664 int nleftsize
= nbufsize
;
667 char eabuf
[ETHER_ADDR_STR_LEN
];
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
);
677 NULL_CHECK(buf
, "buf is NULL", err
);
678 /* initialize the buffer */
679 memset(buf
, 0, nbufsize
);
680 DHD_PNO(("%s enter \n", __FUNCTION__
));
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
;
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 ¶ms_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 */
696 (phead
->tot_size
+ phead
->tot_cnt
* ENTRY_OVERHEAD
))
698 /* increase scan count */
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
;
707 list_for_each_entry_safe(iter
, next
,
708 &phead
->entry_list
, list
) {
709 t_delta
= jiffies_to_msecs(jiffies
- iter
->recorded_time
);
712 memset(msg
, 0, sizeof(msg
));
715 bp
+= nreadsize
= sprintf(bp
, "bssid=%s\n",
716 bcm_ether_ntoa((const struct ether_addr
*)&iter
->BSSID
, eabuf
));
717 nleftsize
-= nreadsize
;
719 bp
+= nreadsize
= sprintf(bp
, "ssid=%s\n", iter
->SSID
);
720 nleftsize
-= nreadsize
;
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
;
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
;
735 bp
+= nreadsize
= sprintf(bp
, "dist=%d\n",
736 (iter
->rtt0
== 0)? -1 : iter
->rtt0
);
737 nleftsize
-= nreadsize
;
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
);
747 memcpy(msg
, _base_bp
, bp
- _base_bp
);
748 DHD_PNO(("Entry : \n%s", msg
));
751 bp
+= nreadsize
= sprintf(bp
, "%s", SCAN_END_MARKER
);
752 DHD_PNO(("%s", SCAN_END_MARKER
));
753 nleftsize
-= nreadsize
;
755 /* reset the header */
756 siter
->bestnetheader
= phead
= phead
->next
;
757 MFREE(dhd
->osh
, pprev
, BEST_HEADER_SIZE
);
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
);
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
));
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(¶ms_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__
));
781 /* return used memory in buffer */
782 bytes_written
= (int32
)(bp
- buf
);
783 return bytes_written
;
786 _dhd_pno_clear_all_batch_results(dhd_pub_t
*dhd
, struct list_head
*head
, bool only_last
)
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
,
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 */
806 /* delete all data belong if the one is last */
807 phead
= siter
->bestnetheader
;
808 while (phead
!= NULL
) {
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
);
817 MFREE(dhd
->osh
, pprev
, BEST_HEADER_SIZE
);
820 /* it is ok to delete top node */
821 list_del(&siter
->list
);
822 MFREE(dhd
->osh
, siter
, SCAN_RESULTS_SIZE
);
825 return removed_scan_cnt
;
829 _dhd_pno_cfg(dhd_pub_t
*dhd
, uint16
*channel_list
, int nchan
)
833 wl_pfn_cfg_t pfncfg_param
;
834 NULL_CHECK(dhd
, "dhd is NULL", err
);
836 NULL_CHECK(channel_list
, "nchan is NULL", err
);
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);
844 for (i
= 0; i
< nchan
&& nchan
< WL_NUMCHANNELS
; i
++)
845 pfncfg_param
.channel_list
[i
] = channel_list
[i
];
847 pfncfg_param
.channel_num
= htod32(nchan
);
848 err
= dhd_iovar(dhd
, 0, "pfn_cfg", (char *)&pfncfg_param
, sizeof(pfncfg_param
), 1);
850 DHD_ERROR(("%s : failed to execute pfn_cfg\n", __FUNCTION__
));
857 _dhd_pno_reinitialize_prof(dhd_pub_t
*dhd
, dhd_pno_params_t
*params
, dhd_pno_mode_t mode
)
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
);
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 ¶ms
->params_legacy
.ssid_list
, list
) {
872 list_del(&iter
->list
);
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
));
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 ¶ms
->params_batch
.get_batch
.scan_results_list
, FALSE
);
901 _dhd_pno_clear_all_batch_results(dhd
,
902 ¶ms
->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(¶ms
->params_batch
.get_batch
.scan_results_list
);
907 INIT_LIST_HEAD(¶ms
->params_batch
.get_batch
.expired_scan_results_list
);
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 ¶ms
->params_hotlist
.bssid_list
, list
) {
915 list_del(&iter
->list
);
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
));
928 DHD_ERROR(("%s : unknown mode : %d\n", __FUNCTION__
, mode
));
931 mutex_unlock(&_pno_state
->pno_mutex
);
935 _dhd_pno_add_bssid(dhd_pub_t
*dhd
, wl_pfn_bssid_t
*p_pfn_bssid
, int nbssid
)
938 NULL_CHECK(dhd
, "dhd is NULL", err
);
940 NULL_CHECK(p_pfn_bssid
, "bssid list is NULL", err
);
942 err
= dhd_iovar(dhd
, 0, "pfn_add_bssid", (char *)p_pfn_bssid
,
943 sizeof(wl_pfn_bssid_t
) * nbssid
, 1);
945 DHD_ERROR(("%s : failed to execute pfn_cfg\n", __FUNCTION__
));
954 _dhd_pno_add_significant_bssid(dhd_pub_t
*dhd
,
955 wl_pfn_significant_bssid_t
*p_pfn_significant_bssid
, int nbssid
)
958 NULL_CHECK(dhd
, "dhd is NULL", err
);
965 NULL_CHECK(p_pfn_significant_bssid
, "bssid list is NULL", err
);
967 err
= dhd_iovar(dhd
, 0, "pfn_add_swc_bssid", (char *)p_pfn_significant_bssid
,
968 sizeof(wl_pfn_significant_bssid_t
) * nbssid
, 1);
970 DHD_ERROR(("%s : failed to execute pfn_significant_bssid %d\n", __FUNCTION__
, err
));
976 #endif /* GSCAN_SUPPORT */
979 dhd_pno_stop_for_ssid(dhd_pub_t
*dhd
)
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__
));
993 DHD_PNO(("%s enter\n", __FUNCTION__
));
994 _pno_state
->pno_mode
&= ~DHD_PNO_LEGACY_MODE
;
996 if (_pno_state
->pno_mode
& DHD_PNO_GSCAN_MODE
) {
997 struct dhd_pno_gscan_params
*gscan_params
;
999 _params
= &_pno_state
->pno_params_arr
[INDEX_OF_GSCAN_PARAMS
];
1000 gscan_params
= &_params
->params_gscan
;
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
);
1008 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
1009 __FUNCTION__
, err
));
1012 /* restore previous pno_mode */
1013 _pno_state
->pno_mode
= mode
;
1015 err
= dhd_pno_initiate_gscan_request(dhd
, 1, 0);
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
;
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
);
1033 _pno_state
->pno_mode
&= ~DHD_PNO_BATCH_MODE
;
1034 DHD_ERROR(("%s : failed to restart batch scan(err: %d)\n",
1035 __FUNCTION__
, err
));
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"
1047 __FUNCTION__
, _params
->params_hotlist
.nbssid
));
1049 _pno_state
->pno_mode
&= ~DHD_PNO_HOTLIST_MODE
;
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
;
1060 err
= dhd_pno_set_for_hotlist(dhd
, p_pfn_bssid
, &_params
->params_hotlist
);
1062 _pno_state
->pno_mode
&= ~DHD_PNO_HOTLIST_MODE
;
1063 DHD_ERROR(("%s : failed to restart hotlist scan(err: %d)\n",
1064 __FUNCTION__
, err
));
1069 err
= dhd_pno_clean(dhd
);
1071 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
1072 __FUNCTION__
, err
));
1082 dhd_pno_enable(dhd_pub_t
*dhd
, int enable
)
1085 NULL_CHECK(dhd
, "dhd is NULL", err
);
1086 DHD_PNO(("%s enter\n", __FUNCTION__
));
1087 return (_dhd_pno_enable(dhd
, enable
));
1090 static wlc_ssid_ext_t
*
1091 dhd_pno_get_legacy_pno_ssid(dhd_pub_t
*dhd
, dhd_pno_status_info_t
*pno_state
)
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
;
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
));
1105 pno_state
->pno_mode
&= ~DHD_PNO_LEGACY_MODE
;
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
);
1121 dhd_pno_add_to_ssid_list(dhd_pno_params_t
*params
, wlc_ssid_ext_t
*ssid_list
,
1126 struct dhd_pno_ssid
*_pno_ssid
;
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
));
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",
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
, ¶ms
->params_legacy
.ssid_list
);
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
)
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;
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
);
1168 if (!dhd_support_sta_mode(dhd
)) {
1169 err
= BCME_BADOPTION
;
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
));
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
))
1182 if (_pno_state
->pno_mode
& DHD_PNO_LEGACY_MODE
)
1183 #endif /* GSCAN_SUPPORT */
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
);
1189 DHD_ERROR(("%s : failed to stop legacy PNO (err %d)\n",
1190 __FUNCTION__
, err
));
1194 _pno_state
->pno_mode
|= DHD_PNO_LEGACY_MODE
;
1195 err
= _dhd_pno_reinitialize_prof(dhd
, _params
, DHD_PNO_LEGACY_MODE
);
1197 DHD_ERROR(("%s : failed to reinitialize profile (err %d)\n",
1198 __FUNCTION__
, err
));
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
];
1207 #ifdef GSCAN_SUPPORT
1209 tot_nchan
= WL_NUMCHANNELS
;
1210 err
= _dhd_pno_get_channels(dhd
, _chan_list
, &tot_nchan
,
1211 (WLC_BAND_2G
| WLC_BAND_5G
), TRUE
);
1214 DHD_PNO(("Could not get channel list for PNO SSID\n"));
1216 for (i
= 0; i
< tot_nchan
; i
++)
1217 _params
->params_legacy
.chan_list
[i
] = _chan_list
[i
];
1220 #endif /* GSCAN_SUPPORT */
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
);
1230 DHD_ERROR(("%s : failed to disable PNO\n", __FUNCTION__
));
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
);
1244 DHD_ERROR(("%s : failed to merge channel list"
1245 " between legacy and batch\n",
1250 DHD_PNO(("superset channel will use"
1251 " all channels in firmware\n"));
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
);
1261 DHD_ERROR(("%s : failed to merge channel list"
1262 " between legacy and hotlist\n",
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) {
1282 DHD_PNO(("GSCAN mode is ON! Will restart GSCAN+Legacy PNO\n"));
1283 err
= dhd_pno_initiate_gscan_request(dhd
, 1, 0);
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
));
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
));
1295 if (dhd_pno_add_to_ssid_list(_params
, ssid_list
, nssid
) < 0) {
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
));
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__
));
1312 _dhd_pno_reinitialize_prof(dhd
, _params
, DHD_PNO_LEGACY_MODE
);
1315 /* clear mode in case of error */
1317 int ret
= dhd_pno_clean(dhd
);
1320 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
1321 __FUNCTION__
, ret
));
1323 _pno_state
->pno_mode
&= ~DHD_PNO_LEGACY_MODE
;
1330 dhd_pno_set_for_batch(dhd_pub_t
*dhd
, struct dhd_pno_batch_params
*batch_params
)
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
;
1349 if (!WLS_SUPPORTED(_pno_state
)) {
1350 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__
));
1351 err
= BCME_UNSUPPORTED
;
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
);
1359 DHD_ERROR(("%s : failed to call _dhd_pno_reinitialize_prof\n",
1364 /* batch mode is already started */
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
));
1375 memset(_chan_list
, 0, sizeof(_chan_list
));
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
);
1384 DHD_ERROR(("%s: failed to get valid channel list(band : %d)\n",
1385 __FUNCTION__
, batch_params
->band
));
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
);
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
]));
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
;
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
);
1414 DHD_ERROR(("%s : failed to disable PNO\n", __FUNCTION__
));
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
);
1427 DHD_ERROR(("%s : failed to merge channel list"
1428 " between legacy and batch\n",
1433 DHD_PNO(("superset channel will use all channels in firmware\n"));
1435 p_ssid_list
= dhd_pno_get_legacy_pno_ssid(dhd
, _pno_state
);
1438 DHD_ERROR(("failed to get Legacy PNO SSID list\n"));
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
));
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
));
1452 /* we need to return mscan */
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
));
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__
));
1467 /* clear mode in case of error */
1469 _pno_state
->pno_mode
&= ~DHD_PNO_BATCH_MODE
;
1471 /* return #max scan firmware can do */
1480 #ifdef GSCAN_SUPPORT
1482 dhd_pno_reset_cfg_gscan(dhd_pno_params_t
*_params
,
1483 dhd_pno_status_info_t
*_pno_state
, uint8 flags
)
1485 DHD_PNO(("%s enter\n", __FUNCTION__
));
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"));
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
);
1508 _params
->params_gscan
.nbssid_hotlist
= 0;
1509 DHD_PNO(("Flush Hotlist Config\n"));
1511 if (flags
& GSCAN_FLUSH_SIGNIFICANT_CFG
) {
1512 dhd_pno_significant_bssid_t
*iter
, *next
;
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
);
1521 _params
->params_gscan
.nbssid_significant_change
= 0;
1522 DHD_PNO(("Flush Significant Change Config\n"));
1529 dhd_pno_lock_batch_results(dhd_pub_t
*dhd
)
1531 dhd_pno_status_info_t
*_pno_state
;
1532 _pno_state
= PNO_GET_PNOSTATE(dhd
);
1533 mutex_lock(&_pno_state
->pno_mutex
);
1538 dhd_pno_unlock_batch_results(dhd_pub_t
*dhd
)
1540 dhd_pno_status_info_t
*_pno_state
;
1541 _pno_state
= PNO_GET_PNOSTATE(dhd
);
1542 mutex_unlock(&_pno_state
->pno_mutex
);
1547 dhd_wait_batch_results_complete(dhd_pub_t
*dhd
)
1549 dhd_pno_status_info_t
*_pno_state
;
1550 dhd_pno_params_t
*_params
;
1552 _pno_state
= PNO_GET_PNOSTATE(dhd
);
1553 _params
= &_pno_state
->pno_params_arr
[INDEX_OF_GSCAN_PARAMS
];
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;
1566 mutex_lock(&_pno_state
->pno_mutex
);
1567 iter
= _params
->params_gscan
.gscan_batch_cache
;
1569 num_results
+= iter
->tot_count
- iter
->tot_consumed
;
1572 mutex_unlock(&_pno_state
->pno_mutex
);
1574 /* All results consumed/No results cached??
1575 * Get fresh results from FW
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
));
1587 DHD_PNO(("%s: Wait complete\n", __FUNCTION__
));
1593 dhd_get_gscan_batch_results(dhd_pub_t
*dhd
, uint32
*len
)
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;
1600 _pno_state
= PNO_GET_PNOSTATE(dhd
);
1601 _params
= &_pno_state
->pno_params_arr
[INDEX_OF_GSCAN_PARAMS
];
1603 iter
= results
= _params
->params_gscan
.gscan_batch_cache
;
1605 num_results
+= iter
->tot_count
- iter
->tot_consumed
;
1610 *len
= ((num_results
<< 16) | (num_scan_ids
));
1615 dhd_pno_get_gscan(dhd_pub_t
*dhd
, dhd_pno_gscan_cmd_cfg_t type
,
1616 void *info
, uint32
*len
)
1619 dhd_pno_gscan_capabilities_t
*ptr
;
1622 DHD_ERROR(("%s: len is NULL\n", __FUNCTION__
));
1627 case DHD_PNO_GET_CAPABILITIES
:
1628 ptr
= (dhd_pno_gscan_capabilities_t
*)
1629 kmalloc(sizeof(dhd_pno_gscan_capabilities_t
), GFP_KERNEL
);
1632 /* Hardcoding these values for now, need to get
1633 * these values from FW, will change in a later check-in
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
;
1643 *len
= sizeof(dhd_pno_gscan_capabilities_t
);
1646 case DHD_PNO_GET_BATCH_RESULTS
:
1647 ret
= dhd_get_gscan_batch_results(dhd
, len
);
1649 case DHD_PNO_GET_CHANNEL_LIST
:
1651 uint16 ch_list
[WL_NUMCHANNELS
];
1652 uint32
*ptr
, mem_needed
, i
;
1653 int32 err
, nchan
= WL_NUMCHANNELS
;
1654 uint32
*gscan_band
= (uint32
*) info
;
1657 /* No band specified?, nothing to do */
1658 if ((*gscan_band
& GSCAN_BAND_MASK
) == 0) {
1659 DHD_PNO(("No band specified\n"));
1664 /* HAL and DHD use different bits for 2.4G and
1665 * 5G in bitmap. Hence translating it here...
1667 if (*gscan_band
& GSCAN_BG_BAND_MASK
) {
1668 band
|= WLC_BAND_2G
;
1670 if (*gscan_band
& GSCAN_A_BAND_MASK
) {
1671 band
|= WLC_BAND_5G
;
1674 err
= _dhd_pno_get_channels(dhd
, ch_list
, &nchan
,
1675 (band
& GSCAN_ABG_BAND_MASK
),
1676 !(*gscan_band
& GSCAN_DFS_MASK
));
1679 DHD_ERROR(("%s: failed to get valid channel list\n",
1683 mem_needed
= sizeof(uint32
) * nchan
;
1684 ptr
= (uint32
*) kmalloc(mem_needed
, GFP_KERNEL
);
1686 DHD_ERROR(("%s: Unable to malloc %d bytes\n",
1687 __FUNCTION__
, mem_needed
));
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
));
1700 DHD_ERROR(("%s: info buffer is NULL\n", __FUNCTION__
));
1705 DHD_ERROR(("%s: Unrecognized cmd type - %d\n", __FUNCTION__
, type
));
1714 dhd_pno_set_cfg_gscan(dhd_pub_t
*dhd
, dhd_pno_gscan_cmd_cfg_t type
,
1715 void *buf
, uint8 flush
)
1718 dhd_pno_params_t
*_params
;
1720 dhd_pno_status_info_t
*_pno_state
;
1722 NULL_CHECK(dhd
, "dhd is NULL", err
);
1724 DHD_PNO(("%s enter\n", __FUNCTION__
));
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
);
1731 case DHD_PNO_BATCH_SCAN_CFG_ID
:
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
;
1739 case DHD_PNO_GEOFENCE_SCAN_CFG_ID
:
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
;
1747 dhd_pno_reset_cfg_gscan(_params
, _pno_state
,
1748 GSCAN_FLUSH_HOTLIST_CFG
);
1754 if (!_params
->params_gscan
.nbssid_hotlist
) {
1755 INIT_LIST_HEAD(&_params
->params_gscan
.hotlist_bssid_list
);
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
+
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
);
1770 DHD_ERROR(("_pno_bssid is NULL, cannot kalloc %zd bytes",
1771 sizeof(struct dhd_pno_bssid
)));
1775 memcpy(&_pno_bssid
->macaddr
, &bssid_ptr
->macaddr
, ETHER_ADDR_LEN
);
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
);
1783 _params
->params_gscan
.nbssid_hotlist
+= ptr
->nbssid
;
1784 _params
->params_gscan
.lost_ap_window
= ptr
->lost_ap_window
;
1787 case DHD_PNO_SIGNIFICANT_SCAN_CFG_ID
:
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
;
1794 dhd_pno_reset_cfg_gscan(_params
, _pno_state
,
1795 GSCAN_FLUSH_SIGNIFICANT_CFG
);
1801 if (!_params
->params_gscan
.nbssid_significant_change
) {
1802 INIT_LIST_HEAD(&_params
->params_gscan
.significant_bssid_list
);
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
+
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
),
1819 if (!_pno_significant_change_bssid
) {
1820 DHD_ERROR(("SWC bssidptr is NULL, cannot kalloc %zd bytes",
1821 sizeof(dhd_pno_significant_bssid_t
)));
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
);
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
;
1841 case DHD_PNO_SCAN_CFG_ID
:
1843 int i
, k
, valid
= 0;
1845 gscan_scan_params_t
*ptr
= (gscan_scan_params_t
*)buf
;
1846 struct dhd_pno_gscan_channel_bucket
*ch_bucket
;
1848 if (ptr
->nchannel_buckets
<= GSCAN_MAX_CH_BUCKETS
) {
1849 _params
->params_gscan
.nchannel_buckets
= ptr
->nchannel_buckets
;
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
;
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
],
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...
1868 if (band
& GSCAN_BG_BAND_MASK
)
1869 ch_bucket
[i
].band
|= WLC_BAND_2G
;
1871 if (band
& GSCAN_A_BAND_MASK
)
1872 ch_bucket
[i
].band
|= WLC_BAND_5G
;
1874 if (band
& GSCAN_DFS_MASK
)
1875 ch_bucket
[i
].band
|= GSCAN_DFS_MASK
;
1877 ptr
->channel_bucket
[i
].bucket_freq_multiple
) {
1880 if (ptr
->channel_bucket
[i
].bucket_freq_multiple
< min
)
1881 min
= ptr
->channel_bucket
[i
].bucket_freq_multiple
;
1883 DHD_PNO(("band %d report_flag %d\n", ch_bucket
[i
].band
,
1884 ch_bucket
[i
].report_flag
));
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
;
1893 _params
->params_gscan
.scan_fr
= ptr
->scan_fr
;
1895 DHD_PNO(("num_buckets %d scan_fr %d\n", ptr
->nchannel_buckets
,
1896 _params
->params_gscan
.scan_fr
));
1904 DHD_ERROR(("%s: Unrecognized cmd type - %d\n", __FUNCTION__
, type
));
1908 mutex_unlock(&_pno_state
->pno_mutex
);
1915 validate_gscan_params(struct dhd_pno_gscan_params
*gscan_params
)
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
));
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
]));
1941 dhd_pno_set_for_gscan(dhd_pub_t
*dhd
, struct dhd_pno_gscan_params
*gscan_params
)
1945 uint16 _chan_list
[WL_NUMCHANNELS
];
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
;
1957 params_legacy
= &_pno_state
->pno_params_arr
[INDEX_OF_LEGACY_PARAMS
];
1958 _params
= &_pno_state
->pno_params_arr
[INDEX_OF_GSCAN_PARAMS
];
1960 NULL_CHECK(dhd
->pno_state
, "pno_state is NULL", err
);
1961 NULL_CHECK(gscan_params
, "gscan_params is NULL", err
);
1963 DHD_PNO(("%s enter\n", __FUNCTION__
));
1965 if (!dhd_support_sta_mode(dhd
)) {
1966 err
= BCME_BADOPTION
;
1969 if (!WLS_SUPPORTED(_pno_state
)) {
1970 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__
));
1971 err
= BCME_UNSUPPORTED
;
1975 if (!validate_gscan_params(gscan_params
)) {
1976 DHD_ERROR(("%s : Cannot start gscan - bad params\n", __FUNCTION__
));
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
))) {
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
);
1991 DHD_ERROR(("%s : failed to disable PNO\n", __FUNCTION__
));
1994 /* restore the previous mode */
1995 _pno_state
->pno_mode
= mode
;
1998 _pno_state
->pno_mode
|= DHD_PNO_GSCAN_MODE
;
2000 if (_pno_state
->pno_mode
& DHD_PNO_LEGACY_MODE
) {
2001 pssid_list
= dhd_pno_get_legacy_pno_ssid(dhd
, _pno_state
);
2005 DHD_ERROR(("failed to get Legacy PNO SSID list\n"));
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
));
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
));
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
);
2025 if (!pfn_gscan_cfg_t
) {
2026 DHD_ERROR(("%s: failed to malloc memory of size %d\n",
2027 __FUNCTION__
, gscan_param_size
));
2033 if (gscan_params
->mscan
) {
2034 pfn_gscan_cfg_t
->buffer_threshold
= gscan_params
->buffer_threshold
;
2036 pfn_gscan_cfg_t
->buffer_threshold
= GSCAN_BATCH_NO_THR_SET
;
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
;
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;
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
;
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
;
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
));
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
));
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
));
2080 if (gscan_params
->nbssid_significant_change
) {
2081 dhd_pno_significant_bssid_t
*iter
, *next
;
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",
2089 sizeof(wl_pfn_significant_bssid_t
) *
2090 gscan_params
->nbssid_significant_change
));
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
);
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
);
2107 DHD_ERROR(("%s : failed to call _dhd_pno_add_significant_bssid(err :%d)\n",
2108 __FUNCTION__
, err
));
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"
2121 __FUNCTION__
, _params
->params_hotlist
.nbssid
));
2123 _pno_state
->pno_mode
&= ~DHD_PNO_HOTLIST_MODE
;
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
;
2137 err
= _dhd_pno_add_bssid(dhd
, p_pfn_bssid
, gscan_params
->nbssid_hotlist
);
2139 DHD_ERROR(("%s : failed to call _dhd_pno_add_bssid(err :%d)\n",
2140 __FUNCTION__
, err
));
2145 if ((err
= _dhd_pno_enable(dhd
, PNO_ON
)) < 0) {
2146 DHD_ERROR(("%s : failed to enable PNO err %d\n", __FUNCTION__
, err
));
2150 /* clear mode in case of error */
2152 int ret
= dhd_pno_clean(dhd
);
2155 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
2156 __FUNCTION__
, ret
));
2158 _pno_state
->pno_mode
&= ~DHD_PNO_GSCAN_MODE
;
2162 kfree(p_pfn_significant_bssid
);
2164 if (pfn_gscan_cfg_t
) {
2165 MFREE(dhd
->osh
, pfn_gscan_cfg_t
, gscan_param_size
);
2168 MFREE(dhd
->osh
, ch_bucket
,
2169 (tot_num_buckets
* sizeof(wl_pfn_gscan_channel_bucket_t
)));
2177 dhd_pno_merge_gscan_pno_channels(dhd_pno_status_info_t
*pno_state
,
2179 uint8
*ch_scratch_pad
,
2180 wl_pfn_gscan_channel_bucket_t
*ch_bucket
,
2181 uint32
*num_buckets_to_fw
,
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
;
2192 if (!_params1
->params_legacy
.scan_fr
)
2193 _params1
->params_legacy
.scan_fr
= PNO_SCAN_MIN_FW_SEC
;
2195 is_legacy_scan_freq_higher
=
2196 _params
->params_gscan
.scan_fr
< _params1
->params_legacy
.scan_fr
;
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.
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
;
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
;
2217 _params
->params_gscan
.max_ch_bucket_freq
= max
;
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()
2226 for (i
= 0; i
< _params1
->params_legacy
.nchan
; i
++)
2227 ch_scratch_pad
[legacy_chan_list
[i
]] += 2;
2230 memcpy(chan_buf
, chan_list
, num_channels
* sizeof(uint16
));
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
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
];
2250 ch_bucket
[i
].bucket_end_index
= j
- 1;
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
];
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
2264 if (report_flag
< ch_bucket
[i
].report_flag
)
2265 report_flag
= ch_bucket
[i
].report_flag
;
2270 ch_bucket
[i
].bucket_end_index
= j
- 1;
2272 ch_bucket
[i
].bucket_end_index
= CHANNEL_BUCKET_EMPTY_INDEX
;
2273 *num_buckets_to_fw
= *num_buckets_to_fw
- 1;
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
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
];
2294 ch_bucket
[_params
->params_gscan
.nchannel_buckets
].bucket_end_index
= j
- 1;
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;
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
,
2308 uint32
*num_buckets
,
2309 uint32
*num_buckets_to_fw
)
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
;
2319 if (is_pno_legacy_running
)
2320 *num_buckets
= _params
->params_gscan
.nchannel_buckets
+ 1;
2322 *num_buckets
= _params
->params_gscan
.nchannel_buckets
;
2325 *num_buckets_to_fw
= *num_buckets
;
2328 ch_bucket
= (wl_pfn_gscan_channel_bucket_t
*) MALLOC(dhd
->osh
,
2329 ((*num_buckets
) * sizeof(wl_pfn_gscan_channel_bucket_t
)));
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;
2338 max
= gscan_buckets
[0].bucket_freq_multiple
;
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
;
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
));
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;
2361 num_channels
+= nchan
;
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
));
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;
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()
2390 for (i
= 0; i
< num_channels
; i
++) {
2391 if (!ch_scratch_pad
[chan_list
[i
]]) {
2392 ch_scratch_pad
[chan_list
[i
]] = 1;
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
);
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.
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
);
2415 kfree(ch_scratch_pad
);
2420 dhd_pno_stop_for_gscan(dhd_pub_t
*dhd
)
2424 wlc_ssid_ext_t
*pssid_list
= NULL
;
2425 dhd_pno_status_info_t
*_pno_state
;
2427 _pno_state
= PNO_GET_PNOSTATE(dhd
);
2428 DHD_PNO(("%s enter\n", __FUNCTION__
));
2430 if (!dhd_support_sta_mode(dhd
)) {
2431 err
= BCME_BADOPTION
;
2434 if (!WLS_SUPPORTED(_pno_state
)) {
2435 DHD_ERROR(("%s : wifi location service is not supported\n",
2437 err
= BCME_UNSUPPORTED
;
2441 if (!(_pno_state
->pno_mode
& DHD_PNO_GSCAN_MODE
)) {
2442 DHD_ERROR(("%s : GSCAN is not enabled\n", __FUNCTION__
));
2445 mutex_lock(&_pno_state
->pno_mutex
);
2446 mode
= _pno_state
->pno_mode
& ~DHD_PNO_GSCAN_MODE
;
2447 err
= dhd_pno_clean(dhd
);
2450 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
2451 __FUNCTION__
, err
));
2452 mutex_unlock(&_pno_state
->pno_mutex
);
2455 _pno_state
->pno_mode
= mode
;
2456 mutex_unlock(&_pno_state
->pno_mutex
);
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
];
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
);
2468 DHD_ERROR(("failed to get Legacy PNO SSID list\n"));
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
);
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
));
2494 dhd_pno_initiate_gscan_request(dhd_pub_t
*dhd
, bool run
, bool flush
)
2497 dhd_pno_params_t
*params
;
2498 dhd_pno_status_info_t
*_pno_state
;
2499 struct dhd_pno_gscan_params
*gscan_params
;
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
);
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
= ¶ms
->params_gscan
;
2510 err
= dhd_pno_set_for_gscan(dhd
, gscan_params
);
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
);
2517 /* Need to stop all gscan */
2518 err
= dhd_pno_stop_for_gscan(dhd
);
2525 dhd_pno_enable_full_scan_result(dhd_pub_t
*dhd
, bool real_time_flag
)
2528 dhd_pno_params_t
*params
;
2529 dhd_pno_status_info_t
*_pno_state
;
2530 struct dhd_pno_gscan_params
*gscan_params
;
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
);
2537 DHD_PNO(("%s enter\n", __FUNCTION__
));
2539 if (!WLS_SUPPORTED(_pno_state
)) {
2540 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__
));
2541 err
= BCME_UNSUPPORTED
;
2545 params
= &_pno_state
->pno_params_arr
[INDEX_OF_GSCAN_PARAMS
];
2546 gscan_params
= ¶ms
->params_gscan
;
2548 mutex_lock(&_pno_state
->pno_mutex
);
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
;
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
;
2566 DHD_PNO(("No change in flag - %d\n", old_flag
));
2569 DHD_PNO(("Gscan not started\n"));
2572 mutex_unlock(&_pno_state
->pno_mutex
);
2577 int dhd_gscan_batch_cache_cleanup(dhd_pub_t
*dhd
)
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
;
2585 _pno_state
= PNO_GET_PNOSTATE(dhd
);
2586 params
= &_pno_state
->pno_params_arr
[INDEX_OF_GSCAN_PARAMS
];
2587 gscan_params
= ¶ms
->params_gscan
;
2588 iter
= gscan_params
->gscan_batch_cache
;
2591 if (iter
->tot_consumed
== iter
->tot_count
) {
2598 gscan_params
->gscan_batch_cache
= iter
;
2599 ret
= (iter
== NULL
);
2604 _dhd_pno_get_gscan_batch_from_fw(dhd_pub_t
*dhd
)
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;
2619 NULL_CHECK(dhd
, "dhd is NULL\n", err
);
2620 NULL_CHECK(dhd
->pno_state
, "pno_state is NULL", err
);
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__
));
2626 if (!WLS_SUPPORTED(_pno_state
)) {
2627 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__
));
2628 err
= BCME_UNSUPPORTED
;
2632 gscan_params
= ¶ms
->params_gscan
;
2633 nAPs_per_scan
= (uint8
*) MALLOC(dhd
->osh
, gscan_params
->mscan
);
2635 if (!nAPs_per_scan
) {
2636 DHD_ERROR(("%s :Out of memory!! Cant malloc %d bytes\n", __FUNCTION__
,
2637 gscan_params
->mscan
));
2642 plbestnet
= (wl_pfn_lscanresults_t
*)MALLOC(dhd
->osh
, PNO_BESTNET_LEN
);
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 */
2648 iter
->tot_consumed
= iter
->tot_count
;
2651 dhd_gscan_batch_cache_cleanup(dhd
);
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
;
2658 timediff
= gscan_params
->scan_fr
* 1000;
2659 timediff
= timediff
>> 1;
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);
2668 DHD_ERROR(("%s : Cannot get all the batch results, err :%d\n",
2669 __FUNCTION__
, err
));
2670 goto exit_mutex_unlock
;
2672 DHD_PNO(("ver %d, status : %d, count %d\n", plbestnet
->version
,
2673 plbestnet
->status
, plbestnet
->count
));
2674 if (plbestnet
->version
!= PFN_SCANRESULT_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
;
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
2689 if (num_scans_in_cur_iter
> gscan_params
->mscan
) {
2690 num_scans_in_cur_iter
= 0;
2691 count
= plbestnet
->count
;
2694 if (TIME_DIFF_MS(timestamp
, plnetinfo
->timestamp
) > timediff
) {
2695 nAPs_per_scan
[num_scans_in_cur_iter
] = count
;
2697 num_scans_in_cur_iter
++;
2699 timestamp
= plnetinfo
->timestamp
;
2701 nAPs_per_scan
[num_scans_in_cur_iter
] = count
;
2702 num_scans_in_cur_iter
++;
2704 DHD_PNO(("num_scans_in_cur_iter %d\n", num_scans_in_cur_iter
));
2705 plnetinfo
= &plbestnet
->netinfo
[0];
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
);
2712 DHD_ERROR(("%s :Out of memory!! Cant malloc %d bytes\n",
2713 __FUNCTION__
, gscan_params
->mscan
));
2715 goto exit_mutex_unlock
;
2717 /* Need this check because the new set of results from FW
2718 * maybe a continuation of previous sets' scan results
2720 if (TIME_DIFF_MS(ts
, plnetinfo
->timestamp
) > timediff
) {
2721 iter
->scan_id
= ++scan_id
;
2723 iter
->scan_id
= scan_id
;
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;
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
);
2737 gscan_params
->gscan_batch_cache
= iter
;
2743 for (j
= 0; j
< nAPs_per_scan
[i
]; j
++, plnetinfo
++) {
2744 result
= &iter
->results
[j
];
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
;
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
,
2769 DHD_PNO(("\tSSID : "));
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
));
2788 mutex_unlock(&_pno_state
->pno_mutex
);
2790 params
->params_gscan
.get_batch_flag
= GSCAN_BATCH_RETRIEVAL_COMPLETE
;
2792 wake_up_interruptible(&_pno_state
->batch_get_wait
);
2793 if (nAPs_per_scan
) {
2794 MFREE(dhd
->osh
, nAPs_per_scan
, gscan_params
->mscan
);
2797 MFREE(dhd
->osh
, plbestnet
, PNO_BESTNET_LEN
);
2799 DHD_PNO(("Batch retrieval done!\n"));
2802 #endif /* GSCAN_SUPPORT */
2805 _dhd_pno_get_for_batch(dhd_pub_t
*dhd
, char *buf
, int bufsize
, int reason
)
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
;
2824 DHD_PNO(("%s enter\n", __FUNCTION__
));
2825 _pno_state
= PNO_GET_PNOSTATE(dhd
);
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
;
2832 #ifdef GSCAN_SUPPORT
2833 if (!(_pno_state
->pno_mode
& (DHD_PNO_BATCH_MODE
| DHD_PNO_GSCAN_MODE
)))
2835 if (!(_pno_state
->pno_mode
& DHD_PNO_BATCH_MODE
))
2836 #endif /* GSCAN_SUPPORT */
2838 DHD_ERROR(("%s: Batching SCAN mode is not enabled\n", __FUNCTION__
));
2839 goto exit_no_unlock
;
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",
2848 /* convert to results format */
2849 goto convert_format
;
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
);
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
;
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
) {
2871 DHD_ERROR(("failed to allocate dhd_pno_scan_results_t\n"));
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
++;
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
);
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);
2898 if (err
== BCME_EPERM
) {
2899 DHD_ERROR(("we cannot get the batching data "
2900 "during scanning in firmware, try again\n,"));
2904 DHD_ERROR(("%s : failed to execute pfnlbest (err :%d)\n",
2905 __FUNCTION__
, err
));
2909 DHD_PNO(("ver %d, status : %d, count %d\n", plbestnet
->version
,
2910 plbestnet
->status
, plbestnet
->count
));
2911 if (plbestnet
->version
!= PFN_SCANRESULT_VERSION
) {
2913 DHD_ERROR(("bestnet version(%d) is mismatch with Driver version(%d)\n",
2914 plbestnet
->version
, PFN_SCANRESULT_VERSION
));
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
) {
2923 DHD_ERROR(("failed to allocate dhd_pno_bestnet_entry\n"));
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
)
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
) {
2941 MFREE(dhd
->osh
, pbestnet_entry
,
2942 BESTNET_ENTRY_SIZE
);
2943 DHD_ERROR(("failed to allocate dhd_pno_bestnet_entry\n"));
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 */
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
;
2959 dhd_pno_best_header_t
*head
= pscan_results
->bestnetheader
;
2960 pscan_results
->bestnetheader
= pbestnetheader
;
2961 pbestnetheader
->next
= head
;
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
2971 DHD_PNO(("This scan is aborted\n"));
2972 pbestnetheader
->reason
= (ENABLE
<< PNO_STATUS_ABORT
);
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
;
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
]));
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
));
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.
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
--;
3019 /* increase total scan count using current scan count */
3020 _params
->params_batch
.get_batch
.tot_scan_cnt
+= pscan_results
->cnt_header
;
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
);
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;
3038 err
= _dhd_pno_convert_format(dhd
, &_params
->params_batch
, buf
, bufsize
);
3040 DHD_ERROR(("failed to convert the data into upper layer format\n"));
3046 MFREE(dhd
->osh
, plbestnet
, PNO_BESTNET_LEN
);
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
;
3052 mutex_unlock(&_pno_state
->pno_mutex
);
3054 if (waitqueue_active(&_pno_state
->get_batch_done
.wait
))
3055 complete(&_pno_state
->get_batch_done
);
3060 _dhd_pno_get_batch_handler(struct work_struct
*work
)
3062 dhd_pno_status_info_t
*_pno_state
;
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
;
3069 DHD_ERROR(("%s : dhd is NULL\n", __FUNCTION__
));
3073 #ifdef GSCAN_SUPPORT
3074 if (_pno_state
->pno_mode
& DHD_PNO_GSCAN_MODE
) {
3075 _dhd_pno_get_gscan_batch_from_fw(dhd
);
3078 #endif /* GSCAN_SUPPORT */
3080 params_batch
= &_pno_state
->pno_params_arr
[INDEX_OF_BATCH_PARAMS
].params_batch
;
3082 _dhd_pno_get_for_batch(dhd
, params_batch
->get_batch
.buf
,
3083 params_batch
->get_batch
.bufsize
, params_batch
->get_batch
.reason
);
3089 dhd_pno_get_for_batch(dhd_pub_t
*dhd
, char *buf
, int bufsize
, int reason
)
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
;
3101 DHD_PNO(("%s enter\n", __FUNCTION__
));
3102 _pno_state
= PNO_GET_PNOSTATE(dhd
);
3104 if (!WLS_SUPPORTED(_pno_state
)) {
3105 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__
));
3106 err
= BCME_UNSUPPORTED
;
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
));
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
);
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
);
3140 #ifdef GSCAN_SUPPORT
3141 if (!(_pno_state
->pno_mode
& DHD_PNO_GSCAN_MODE
))
3143 err
= params_batch
->get_batch
.bytes_written
;
3149 dhd_pno_stop_for_batch(dhd_pub_t
*dhd
)
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
;
3166 if (!WLS_SUPPORTED(_pno_state
)) {
3167 DHD_ERROR(("%s : wifi location service is not supported\n",
3169 err
= BCME_UNSUPPORTED
;
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"));
3180 if (!(_pno_state
->pno_mode
& DHD_PNO_BATCH_MODE
)) {
3181 DHD_ERROR(("%s : PNO BATCH MODE is not enabled\n", __FUNCTION__
));
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
;
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
;
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
);
3197 DHD_ERROR(("failed to get Legacy PNO SSID list\n"));
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
);
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
));
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"
3218 __FUNCTION__
, _params
->params_hotlist
.nbssid
));
3220 _pno_state
->pno_mode
&= ~DHD_PNO_HOTLIST_MODE
;
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
;
3231 err
= dhd_pno_set_for_hotlist(dhd
, p_pfn_bssid
, &_params
->params_hotlist
);
3233 _pno_state
->pno_mode
&= ~DHD_PNO_HOTLIST_MODE
;
3234 DHD_ERROR(("%s : failed to restart hotlist scan(err: %d)\n",
3235 __FUNCTION__
, err
));
3240 err
= dhd_pno_clean(dhd
);
3242 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
3243 __FUNCTION__
, err
));
3248 _params
= &_pno_state
->pno_params_arr
[INDEX_OF_BATCH_PARAMS
];
3249 _dhd_pno_reinitialize_prof(dhd
, _params
, DHD_PNO_BATCH_MODE
);
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
)
3261 uint16 _chan_list
[WL_NUMCHANNELS
];
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__
));
3276 if (!dhd_support_sta_mode(dhd
)) {
3277 err
= BCME_BADOPTION
;
3280 if (!WLS_SUPPORTED(_pno_state
)) {
3281 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__
));
3282 err
= BCME_UNSUPPORTED
;
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
);
3290 DHD_ERROR(("%s : failed to call _dhd_pno_reinitialize_prof\n",
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
));
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
);
3309 DHD_ERROR(("%s: failed to get valid channel list(band : %d)\n",
3310 __FUNCTION__
, hotlist_params
->band
));
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
);
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
]));
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
;
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
);
3341 DHD_ERROR(("%s : failed to disable PNO\n", __FUNCTION__
));
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
);
3356 DHD_ERROR(("%s : failed to merge channel list"
3357 "between legacy and hotlist\n",
3365 INIT_LIST_HEAD(&(_params
->params_hotlist
.bssid_list
));
3367 err
= _dhd_pno_add_bssid(dhd
, p_pfn_bssid
, hotlist_params
->nbssid
);
3369 DHD_ERROR(("%s : failed to call _dhd_pno_add_bssid(err :%d)\n",
3370 __FUNCTION__
, err
));
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
));
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
));
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
);
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__
));
3398 /* clear mode in case of error */
3400 _pno_state
->pno_mode
&= ~DHD_PNO_HOTLIST_MODE
;
3405 dhd_pno_stop_for_hotlist(dhd_pub_t
*dhd
)
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
);
3416 if (!WLS_SUPPORTED(_pno_state
)) {
3417 DHD_ERROR(("%s : wifi location service is not supported\n",
3419 err
= BCME_UNSUPPORTED
;
3423 if (!(_pno_state
->pno_mode
& DHD_PNO_HOTLIST_MODE
)) {
3424 DHD_ERROR(("%s : Hotlist MODE is not enabled\n",
3428 _pno_state
->pno_mode
&= ~DHD_PNO_BATCH_MODE
;
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
);
3437 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
3438 __FUNCTION__
, err
));
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
;
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
);
3451 DHD_ERROR(("failed to get Legacy PNO SSID list\n"));
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
);
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
));
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
);
3470 _pno_state
->pno_mode
&= ~DHD_PNO_BATCH_MODE
;
3471 DHD_ERROR(("%s : failed to restart batch scan(err: %d)\n",
3472 __FUNCTION__
, err
));
3477 err
= dhd_pno_clean(dhd
);
3479 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
3480 __FUNCTION__
, err
));
3489 #ifdef GSCAN_SUPPORT
3491 dhd_retreive_batch_scan_results(dhd_pub_t
*dhd
)
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
];
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
);
3509 DHD_PNO(("%s : WLC_E_PFN_BEST_BATCHING retrieval"
3510 "already in progress, will skip\n", __FUNCTION__
));
3517 /* Handle Significant WiFi Change (SWC) event from FW
3518 * Send event to HAL when all results arrive from FW
3521 dhd_handle_swc_evt(dhd_pub_t
*dhd
, const void *event_data
, int *send_evt_bytes
)
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
;
3532 gscan_params
= &(_pno_state
->pno_params_arr
[INDEX_OF_GSCAN_PARAMS
].params_gscan
);
3533 params
= &(gscan_params
->param_significant
);
3535 if (!results
->total_count
) {
3536 *send_evt_bytes
= 0;
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
,
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;
3553 DHD_ERROR(("RX'ed WLC_E_PFN_SWC evt from FW, previous evt not complete!!"));
3554 *send_evt_bytes
= 0;
3560 DHD_PNO(("%s: pkt_count %d total_count %d\n", __FUNCTION__
,
3561 results
->pkt_count
, results
->total_count
));
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]));
3573 change_array
= ¶ms
->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
;
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.
3583 ptr
= (void *) params
->change_array
;
3584 /* expecting the callee to free this mem chunk */
3585 params
->change_array
= NULL
;
3588 *send_evt_bytes
= 0;
3595 dhd_gscan_hotlist_cache_cleanup(dhd_pub_t
*dhd
, hotlist_type_t type
)
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
;
3604 gscan_params
= &(_pno_state
->pno_params_arr
[INDEX_OF_GSCAN_PARAMS
].params_gscan
);
3606 if (type
== HOTLIST_FOUND
) {
3607 iter
= gscan_params
->gscan_hotlist_found
;
3608 gscan_params
->gscan_hotlist_found
= NULL
;
3610 iter
= gscan_params
->gscan_hotlist_lost
;
3611 gscan_params
->gscan_hotlist_lost
= NULL
;
3624 dhd_process_full_gscan_result(dhd_pub_t
*dhd
, const void *data
, int *size
)
3626 wl_bss_info_t
*bi
= NULL
;
3627 wl_gscan_result_t
*gscan_result
;
3628 wifi_gscan_result_t
*result
= NULL
;
3637 gscan_result
= (wl_gscan_result_t
*)data
;
3639 if (!gscan_result
) {
3640 DHD_ERROR(("Invalid gscan result (NULL pointer)\n"));
3643 if (!gscan_result
->bss_info
) {
3644 DHD_ERROR(("Invalid gscan bss info (NULL pointer)\n"));
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
));
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
;
3659 mem_needed
= OFFSETOF(wifi_gscan_result_t
, ie_data
) + bi
->ie_length
;
3660 result
= kmalloc(mem_needed
, GFP_KERNEL
);
3663 DHD_ERROR(("%s Cannot malloc scan result buffer %d bytes\n",
3664 __FUNCTION__
, mem_needed
));
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
;
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
);
3690 dhd_handle_hotlist_scan_evt(dhd_pub_t
*dhd
, const void *event_data
,
3691 int *send_evt_bytes
, hotlist_type_t type
)
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;
3702 gscan_params
= &(_pno_state
->pno_params_arr
[INDEX_OF_GSCAN_PARAMS
].params_gscan
);
3704 if (!results
->count
) {
3705 *send_evt_bytes
= 0;
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
);
3713 if (!gscan_hotlist_cache
) {
3714 DHD_ERROR(("%s Cannot Malloc %d bytes!!\n", __FUNCTION__
, malloc_size
));
3715 *send_evt_bytes
= 0;
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
));
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
));
3729 gscan_hotlist_cache
->tot_count
= results
->count
;
3730 gscan_hotlist_cache
->tot_consumed
= 0;
3731 plnetinfo
= results
->netinfo
;
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;
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
;
3750 memcpy(hotlist_found_array
->ssid
, plnetinfo
->pfnsubnet
.SSID
,
3751 plnetinfo
->pfnsubnet
.SSID_len
);
3752 hotlist_found_array
->ssid
[plnetinfo
->pfnsubnet
.SSID_len
] = '\0';
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
));
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
;
3772 *send_evt_bytes
= total
* sizeof(wifi_gscan_result_t
);
3777 #endif /* GSCAN_SUPPORT */
3779 dhd_pno_event_handler(dhd_pub_t
*dhd
, wl_event_msg_t
*event
, void *event_data
)
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
;
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 */
3802 case WLC_E_PFN_BEST_BATCHING
:
3803 #ifndef GSCAN_SUPPORT
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
);
3814 DHD_PNO(("%s : WLC_E_PFN_BEST_BATCHING"
3815 "will skip this event\n", __FUNCTION__
));
3820 #endif /* !GSCAN_SUPPORT */
3822 DHD_ERROR(("unknown event : %d\n", event_type
));
3828 int dhd_pno_init(dhd_pub_t
*dhd
)
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
);
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"));
3856 DHD_ERROR(("%s: Support Android Location Service\n",
3863 int dhd_pno_deinit(dhd_pub_t
*dhd
)
3866 dhd_pno_status_info_t
*_pno_state
;
3867 dhd_pno_params_t
*_params
;
3868 NULL_CHECK(dhd
, "dhd is NULL", err
);
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
);
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
);
3886 #endif /* GSCAN_SUPPORT */
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
);
3893 cancel_work_sync(&_pno_state
->work
);
3894 MFREE(dhd
->osh
, _pno_state
, sizeof(dhd_pno_status_info_t
));
3895 dhd
->pno_state
= NULL
;
3898 #endif /* PNO_SUPPORT */