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