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