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