2 * Broadcom Dongle Host Driver (DHD)
3 * Prefered Network Offload and Wi-Fi Location Service(WLS) code.
5 * $Copyright Open Broadcom Corporation$
7 * $Id: dhd_pno.c 423669 2013-09-18 13:01:55Z yangj$
16 #include <bcmendian.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>
25 #include <proto/bcmevent.h>
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)
43 #define htodchanspec(i) (i)
44 #define dtohchanspec(i) (i)
45 #endif /* IL_BIGENDINA */
47 #define NULL_CHECK(p, s, err) \
50 printf("NULL POINTER (%s) : %s\n", __FUNCTION__, (s)); \
55 #define PNO_GET_PNOSTATE(dhd) ((dhd_pno_status_info_t *)dhd->pno_state)
56 #define PNO_BESTNET_LEN 1024
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)))
65 #define ENTRY_OVERHEAD strlen("bssid=\nssid=\nfreq=\nlevel=\nage=\ndist=\ndistSd=\n====")
66 #define TIME_MIN_DIFF 5
68 is_dfs(uint16 channel)
70 if (channel >= 52 && channel <= 64) /* class 2 */
72 else if (channel >= 100 && channel <= 140) /* class 4 */
78 dhd_pno_clean(dhd_pub_t *dhd)
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__));
88 err = dhd_iovar(dhd, 0, "pfn", (char *)&pfn, sizeof(pfn), 1);
90 DHD_ERROR(("%s : failed to execute pfn(error : %d)\n",
94 _pno_state->pno_status = DHD_PNO_DISABLED;
95 err = dhd_iovar(dhd, 0, "pfnclear", NULL, 0, 1);
97 DHD_ERROR(("%s : failed to execute pfnclear(error : %d)\n",
105 _dhd_pno_suspend(dhd_pub_t *dhd)
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);
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);
117 DHD_ERROR(("%s : failed to suspend pfn(error :%d)\n", __FUNCTION__, err));
121 _pno_state->pno_status = DHD_PNO_SUSPEND;
126 _dhd_pno_enable(dhd_pub_t *dhd, int enable)
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__));
135 if (enable & 0xfffe) {
136 DHD_ERROR(("%s invalid value\n", __FUNCTION__));
140 if (!dhd_support_sta_mode(dhd)) {
141 DHD_ERROR(("PNO is not allowed for non-STA mode"));
142 err = BCME_BADOPTION;
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;
154 /* Enable/Disable PNO */
155 err = dhd_iovar(dhd, 0, "pfn", (char *)&enable, sizeof(enable), 1);
157 DHD_ERROR(("%s : failed to execute pfn_set\n", __FUNCTION__));
160 _pno_state->pno_status = (enable)?
161 DHD_PNO_ENABLED : DHD_PNO_DISABLED;
163 _pno_state->pno_mode = DHD_PNO_NONE_MODE;
165 DHD_PNO(("%s set pno as %s\n",
166 __FUNCTION__, enable ? "Enable" : "Disable"));
172 _dhd_pno_set(dhd_pub_t *dhd, const dhd_pno_params_t *pno_params, dhd_pno_mode_t mode)
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__));
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);
185 memset(&pfn_param, 0, sizeof(pfn_param));
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);
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;
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
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);
246 pfn_param.repeat = 0;
247 /* enable broadcast scan */
248 pfn_param.flags |= (ENABLE << ENABLE_BD_SCAN_BIT);
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;
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);
271 pfn_param.repeat = 0;
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));
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);
287 DHD_ERROR(("%s : failed to set pfnmem\n", __FUNCTION__));
290 /* get max mscan which the firmware supports */
291 err = dhd_iovar(dhd, 0, "pfnmem", (char *)&_tmp, sizeof(_tmp), 0);
293 DHD_ERROR(("%s : failed to get pfnmem\n", __FUNCTION__));
296 DHD_PNO((" returned mscan : %d, set bestn : %d\n", _tmp, pfn_param.bestn));
297 pfn_param.mscan = MIN(pfn_param.mscan, _tmp);
299 err = dhd_iovar(dhd, 0, "pfn_set", (char *)&pfn_param, sizeof(pfn_param), 1);
301 DHD_ERROR(("%s : failed to execute pfn_set\n", __FUNCTION__));
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;
310 _dhd_pno_add_ssid(dhd_pub_t *dhd, wlc_ssid_t* ssids_list, int nssid)
314 wl_pfn_t pfn_element;
315 NULL_CHECK(dhd, "dhd is NULL", err);
317 NULL_CHECK(ssids_list, "ssid list is NULL", err);
319 memset(&pfn_element, 0, sizeof(pfn_element));
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));
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));
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);
349 DHD_ERROR(("%s : failed to execute pfn_add\n", __FUNCTION__));
356 /* qsort compare function */
358 _dhd_pno_cmpfunc(const void *a, const void *b)
360 return (*(uint16*)a - *(uint16*)b);
363 _dhd_pno_chan_merge(uint16 *d_chan_list, int *nchan,
364 uint16 *chan_list1, int nchan1, uint16 *chan_list2, int nchan2)
367 int i = 0, j = 0, k = 0;
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;
383 tmp = chan_list1[i++];
384 for (; i < nchan1 && chan_list1[i] == tmp; i++);
385 d_chan_list[k++] = tmp;
389 tmp = chan_list2[j++];
390 for (; j < nchan2 && chan_list2[j] == tmp; j++);
391 d_chan_list[k++] = tmp;
398 _dhd_pno_get_channels(dhd_pub_t *dhd, uint16 *d_chan_list,
399 int *nchan, uint8 band, bool skip_dfs)
403 uint32 chan_buf[WL_NUMCHANNELS + 1];
404 wl_uint32_list_t *list;
405 NULL_CHECK(dhd, "dhd is NULL", err);
407 NULL_CHECK(d_chan_list, "d_chan_list is NULL", err);
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);
413 DHD_ERROR(("failed to get channel list (err: %d)\n", err));
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)
420 } else if (band == WLC_BAND_5G) {
421 if (dtoh32(list->element[i]) <= CHANNEL_2G_MAX)
423 if (skip_dfs && is_dfs(dtoh32(list->element[i])))
426 } else { /* All channels */
427 if (skip_dfs && is_dfs(dtoh32(list->element[i])))
430 d_chan_list[j++] = dtoh32(list->element[i]);
437 _dhd_pno_convert_format(dhd_pub_t *dhd, struct dhd_pno_batch_params *params_batch,
438 char *buf, int nbufsize)
441 int bytes_written = 0, nreadsize = 0;
443 int nleftsize = nbufsize;
446 char eabuf[ETHER_ADDR_STR_LEN];
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);
456 NULL_CHECK(buf, "buf is NULL", err);
457 /* initialize the buffer */
458 memset(buf, 0, nbufsize);
459 DHD_PNO(("%s enter \n", __FUNCTION__));
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;
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 ¶ms_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 */
475 (phead->tot_size + phead->tot_cnt * ENTRY_OVERHEAD))
477 /* increase scan count */
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;
486 list_for_each_entry_safe(iter, next,
487 &phead->entry_list, list) {
488 t_delta = jiffies_to_msecs(jiffies - iter->recorded_time);
491 memset(msg, 0, sizeof(msg));
494 bp += nreadsize = sprintf(bp, "bssid=%s\n",
495 bcm_ether_ntoa((const struct ether_addr *)&iter->BSSID, eabuf));
496 nleftsize -= nreadsize;
498 bp += nreadsize = sprintf(bp, "ssid=%s\n", iter->SSID);
499 nleftsize -= nreadsize;
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;
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;
514 bp += nreadsize = sprintf(bp, "dist=%d\n",
515 (iter->rtt0 == 0)? -1 : iter->rtt0);
516 nleftsize -= nreadsize;
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);
526 memcpy(msg, _base_bp, bp - _base_bp);
527 DHD_PNO(("Entry : \n%s", msg));
530 bp += nreadsize = sprintf(bp, "%s", SCAN_END_MARKER);
531 DHD_PNO(("%s", SCAN_END_MARKER));
532 nleftsize -= nreadsize;
534 /* reset the header */
535 siter->bestnetheader = phead = phead->next;
536 MFREE(dhd->osh, pprev, BEST_HEADER_SIZE);
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);
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));
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(¶ms_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__));
560 /* return used memory in buffer */
561 bytes_written = (int32)(bp - buf);
562 return bytes_written;
565 _dhd_pno_clear_all_batch_results(dhd_pub_t *dhd, struct list_head *head, bool only_last)
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,
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 */
585 /* delete all data belong if the one is last */
586 phead = siter->bestnetheader;
587 while (phead != NULL) {
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);
596 MFREE(dhd->osh, pprev, BEST_HEADER_SIZE);
599 /* it is ok to delete top node */
600 list_del(&siter->list);
601 MFREE(dhd->osh, siter, SCAN_RESULTS_SIZE);
604 return removed_scan_cnt;
608 _dhd_pno_cfg(dhd_pub_t *dhd, uint16 *channel_list, int nchan)
612 wl_pfn_cfg_t pfncfg_param;
613 NULL_CHECK(dhd, "dhd is NULL", err);
615 NULL_CHECK(channel_list, "nchan is NULL", err);
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);
623 for (i = 0; i < nchan && nchan < WL_NUMCHANNELS; i++)
624 pfncfg_param.channel_list[i] = channel_list[i];
626 pfncfg_param.channel_num = htod32(nchan);
627 err = dhd_iovar(dhd, 0, "pfn_cfg", (char *)&pfncfg_param, sizeof(pfncfg_param), 1);
629 DHD_ERROR(("%s : failed to execute pfn_cfg\n", __FUNCTION__));
636 _dhd_pno_reinitialize_prof(dhd_pub_t *dhd, dhd_pno_params_t *params, dhd_pno_mode_t mode)
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);
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 ¶ms->params_legacy.ssid_list, list) {
651 list_del(&iter->list);
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));
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 ¶ms->params_batch.get_batch.scan_results_list, FALSE);
680 _dhd_pno_clear_all_batch_results(dhd,
681 ¶ms->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(¶ms->params_batch.get_batch.scan_results_list);
686 INIT_LIST_HEAD(¶ms->params_batch.get_batch.expired_scan_results_list);
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 ¶ms->params_hotlist.bssid_list, list) {
694 list_del(&iter->list);
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));
707 DHD_ERROR(("%s : unknown mode : %d\n", __FUNCTION__, mode));
710 mutex_unlock(&_pno_state->pno_mutex);
714 _dhd_pno_add_bssid(dhd_pub_t *dhd, wl_pfn_bssid_t *p_pfn_bssid, int nbssid)
717 NULL_CHECK(dhd, "dhd is NULL", err);
719 NULL_CHECK(p_pfn_bssid, "bssid list is NULL", err);
721 err = dhd_iovar(dhd, 0, "pfn_add_bssid", (char *)&p_pfn_bssid,
722 sizeof(wl_pfn_bssid_t) * nbssid, 1);
724 DHD_ERROR(("%s : failed to execute pfn_cfg\n", __FUNCTION__));
731 dhd_pno_stop_for_ssid(dhd_pub_t *dhd)
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__));
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;
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);
761 _pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
762 DHD_ERROR(("%s : failed to restart batch scan(err: %d)\n",
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"
775 __FUNCTION__, _params->params_hotlist.nbssid));
777 _pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
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;
788 err = dhd_pno_set_for_hotlist(dhd, p_pfn_bssid, &_params->params_hotlist);
790 _pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
791 DHD_ERROR(("%s : failed to restart hotlist scan(err: %d)\n",
797 err = dhd_pno_clean(dhd);
799 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
809 dhd_pno_enable(dhd_pub_t *dhd, int enable)
812 NULL_CHECK(dhd, "dhd is NULL", err);
813 DHD_PNO(("%s enter\n", __FUNCTION__));
814 return (_dhd_pno_enable(dhd, enable));
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)
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];
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);
834 if (!dhd_support_sta_mode(dhd)) {
835 err = BCME_BADOPTION;
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));
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);
848 DHD_ERROR(("%s : failed to stop legacy PNO (err %d)\n",
853 _pno_state->pno_mode |= DHD_PNO_LEGACY_MODE;
854 err = _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_LEGACY_MODE);
856 DHD_ERROR(("%s : failed to reinitialize profile (err %d)\n",
860 memset(_chan_list, 0, sizeof(_chan_list));
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];
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);
874 DHD_ERROR(("%s : failed to disable PNO\n", __FUNCTION__));
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);
888 DHD_ERROR(("%s : failed to merge channel list"
889 " between legacy and batch\n",
894 DHD_PNO(("superset channel will use"
895 " all channels in firmware\n"));
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);
905 DHD_ERROR(("%s : failed to merge channel list"
906 " between legacy and hotlist\n",
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));
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));
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",
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);
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",
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__));
951 /* clear mode in case of error */
953 _pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
957 dhd_pno_set_for_batch(dhd_pub_t *dhd, struct dhd_pno_batch_params *batch_params)
960 uint16 _chan_list[WL_NUMCHANNELS];
961 int rem_nchan = 0, tot_nchan = 0;
962 int mode = 0, mscan = 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;
977 if (!WLS_SUPPORTED(_pno_state)) {
978 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
979 err = BCME_UNSUPPORTED;
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);
987 DHD_ERROR(("%s : failed to call _dhd_pno_reinitialize_prof\n",
992 /* batch mode is already started */
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));
1003 memset(_chan_list, 0, sizeof(_chan_list));
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);
1012 DHD_ERROR(("%s: failed to get valid channel list(band : %d)\n",
1013 __FUNCTION__, batch_params->band));
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);
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]));
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;
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);
1043 DHD_ERROR(("%s : failed to disable PNO\n", __FUNCTION__));
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);
1056 DHD_ERROR(("%s : failed to merge channel list"
1057 " between legacy and batch\n",
1062 DHD_PNO(("superset channel will use all channels in firmware\n"));
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));
1070 _pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
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);
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));
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));
1091 /* we need to return mscan */
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));
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__));
1106 /* clear mode in case of error */
1108 _pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
1110 /* return #max scan firmware can do */
1119 _dhd_pno_get_for_batch(dhd_pub_t *dhd, char *buf, int bufsize, int reason)
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;
1138 DHD_PNO(("%s enter\n", __FUNCTION__));
1139 _pno_state = PNO_GET_PNOSTATE(dhd);
1141 if (!WLS_SUPPORTED(_pno_state)) {
1142 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
1143 err = BCME_UNSUPPORTED;
1146 if (!(_pno_state->pno_mode & DHD_PNO_BATCH_MODE)) {
1147 DHD_ERROR(("%s: Batching SCAN mode is not enabled\n", __FUNCTION__));
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",
1157 /* convert to results format */
1158 goto convert_format;
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);
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;
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) {
1180 DHD_ERROR(("failed to allocate dhd_pno_scan_results_t\n"));
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++;
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);
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);
1207 if (err == BCME_EPERM) {
1208 DHD_ERROR(("we cannot get the batching data "
1209 "during scanning in firmware, try again\n,"));
1213 DHD_ERROR(("%s : failed to execute pfnlbest (err :%d)\n",
1214 __FUNCTION__, err));
1218 DHD_PNO(("ver %d, status : %d, count %d\n", plbestnet->version,
1219 plbestnet->status, plbestnet->count));
1220 if (plbestnet->version != PFN_SCANRESULT_VERSION) {
1222 DHD_ERROR(("bestnet version(%d) is mismatch with Driver version(%d)\n",
1223 plbestnet->version, PFN_SCANRESULT_VERSION));
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) {
1232 DHD_ERROR(("failed to allocate dhd_pno_bestnet_entry\n"));
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)
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) {
1250 MFREE(dhd->osh, pbestnet_entry,
1251 BESTNET_ENTRY_SIZE);
1252 DHD_ERROR(("failed to allocate dhd_pno_bestnet_entry\n"));
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 */
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;
1268 dhd_pno_best_header_t *head = pscan_results->bestnetheader;
1269 pscan_results->bestnetheader = pbestnetheader;
1270 pbestnetheader->next = head;
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
1280 DHD_PNO(("This scan is aborted\n"));
1281 pbestnetheader->reason = (ENABLE << PNO_STATUS_ABORT);
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]));
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));
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.
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--;
1323 /* increase total scan count using current scan count */
1324 _params->params_batch.get_batch.tot_scan_cnt += pscan_results->cnt_header;
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);
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;
1342 err = _dhd_pno_convert_format(dhd, &_params->params_batch, buf, bufsize);
1344 DHD_ERROR(("failed to convert the data into upper layer format\n"));
1350 MFREE(dhd->osh, plbestnet, PNO_BESTNET_LEN);
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;
1356 mutex_unlock(&_pno_state->pno_mutex);
1357 if (waitqueue_active(&_pno_state->get_batch_done.wait))
1358 complete(&_pno_state->get_batch_done);
1362 _dhd_pno_get_batch_handler(struct work_struct *work)
1364 dhd_pno_status_info_t *_pno_state;
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;
1371 DHD_ERROR(("%s : dhd is NULL\n", __FUNCTION__));
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);
1381 dhd_pno_get_for_batch(dhd_pub_t *dhd, char *buf, int bufsize, int reason)
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;
1393 DHD_PNO(("%s enter\n", __FUNCTION__));
1394 _pno_state = PNO_GET_PNOSTATE(dhd);
1396 if (!WLS_SUPPORTED(_pno_state)) {
1397 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
1398 err = BCME_UNSUPPORTED;
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);
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;
1422 dhd_pno_stop_for_batch(dhd_pub_t *dhd)
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;
1439 if (!WLS_SUPPORTED(_pno_state)) {
1440 DHD_ERROR(("%s : wifi location service is not supported\n",
1442 err = BCME_UNSUPPORTED;
1445 if (!(_pno_state->pno_mode & DHD_PNO_BATCH_MODE)) {
1446 DHD_ERROR(("%s : PNO BATCH MODE is not enabled\n", __FUNCTION__));
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;
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;
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));
1466 _pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
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);
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);
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));
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"
1494 __FUNCTION__, _params->params_hotlist.nbssid));
1496 _pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
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;
1507 err = dhd_pno_set_for_hotlist(dhd, p_pfn_bssid, &_params->params_hotlist);
1509 _pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
1510 DHD_ERROR(("%s : failed to restart hotlist scan(err: %d)\n",
1511 __FUNCTION__, err));
1516 err = dhd_pno_clean(dhd);
1518 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
1519 __FUNCTION__, err));
1524 _params = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS];
1525 _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_BATCH_MODE);
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)
1537 uint16 _chan_list[WL_NUMCHANNELS];
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__));
1552 if (!dhd_support_sta_mode(dhd)) {
1553 err = BCME_BADOPTION;
1556 if (!WLS_SUPPORTED(_pno_state)) {
1557 DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
1558 err = BCME_UNSUPPORTED;
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);
1566 DHD_ERROR(("%s : failed to call _dhd_pno_reinitialize_prof\n",
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));
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);
1585 DHD_ERROR(("%s: failed to get valid channel list(band : %d)\n",
1586 __FUNCTION__, hotlist_params->band));
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);
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]));
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;
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);
1617 DHD_ERROR(("%s : failed to disable PNO\n", __FUNCTION__));
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);
1632 DHD_ERROR(("%s : failed to merge channel list"
1633 "between legacy and hotlist\n",
1641 INIT_LIST_HEAD(&(_params->params_hotlist.bssid_list));
1643 err = _dhd_pno_add_bssid(dhd, p_pfn_bssid, hotlist_params->nbssid);
1645 DHD_ERROR(("%s : failed to call _dhd_pno_add_bssid(err :%d)\n",
1646 __FUNCTION__, err));
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));
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));
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);
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__));
1674 /* clear mode in case of error */
1676 _pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
1681 dhd_pno_stop_for_hotlist(dhd_pub_t *dhd)
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);
1692 if (!WLS_SUPPORTED(_pno_state)) {
1693 DHD_ERROR(("%s : wifi location service is not supported\n",
1695 err = BCME_UNSUPPORTED;
1699 if (!(_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE)) {
1700 DHD_ERROR(("%s : Hotlist MODE is not enabled\n",
1704 _pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
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);
1713 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
1714 __FUNCTION__, err));
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;
1724 &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS].params_legacy);
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));
1731 _pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
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);
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);
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));
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);
1756 _pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
1757 DHD_ERROR(("%s : failed to restart batch scan(err: %d)\n",
1758 __FUNCTION__, err));
1763 err = dhd_pno_clean(dhd);
1765 DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
1766 __FUNCTION__, err));
1775 dhd_pno_event_handler(dhd_pub_t *dhd, wl_event_msg_t *event, void *event_data)
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;
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 */
1798 case WLC_E_PFN_BEST_BATCHING:
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);
1809 DHD_PNO(("%s : WLC_E_PFN_BEST_BATCHING"
1810 "will skip this event\n", __FUNCTION__));
1814 DHD_ERROR(("unknown event : %d\n", event_type));
1820 int dhd_pno_init(dhd_pub_t *dhd)
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);
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"));
1848 int dhd_pno_deinit(dhd_pub_t *dhd)
1851 dhd_pno_status_info_t *_pno_state;
1852 dhd_pno_params_t *_params;
1853 NULL_CHECK(dhd, "dhd is NULL", err);
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);
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);
1869 cancel_work_sync(&_pno_state->work);
1870 MFREE(dhd->osh, _pno_state, sizeof(dhd_pno_status_info_t));
1871 dhd->pno_state = NULL;
1874 #endif /* PNO_SUPPORT */