1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900 |
- /*
- * Broadcom Dongle Host Driver (DHD)
- * Prefered Network Offload and Wi-Fi Location Service(WLS) code.
- *
- * Copyright (C) 1999-2014, Broadcom Corporation
- *
- * Unless you and Broadcom execute a separate written software license
- * agreement governing use of this software, this software is licensed to you
- * under the terms of the GNU General Public License version 2 (the "GPL"),
- * available at http://www.broadcom.com/licenses/GPLv2.php, with the
- * following added to such license:
- *
- * As a special exception, the copyright holders of this software give you
- * permission to link this software with independent modules, and to copy and
- * distribute the resulting executable under terms of your choice, provided that
- * you also meet, for each linked independent module, the terms and conditions of
- * the license of that module. An independent module is a module which is not
- * derived from this software. The special exception does not apply to any
- * modifications of the software.
- *
- * Notwithstanding the above, under no circumstances may you combine this
- * software in any way with any other Broadcom software provided under a license
- * other than the GPL, without Broadcom's express prior written consent.
- *
- * $Id: dhd_pno.c 423669 2013-09-18 13:01:55Z yangj$
- */
- #ifdef PNO_SUPPORT
- #include <typedefs.h>
- #include <osl.h>
- #include <epivers.h>
- #include <bcmutils.h>
- #include <bcmendian.h>
- #include <linuxver.h>
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/list.h>
- #include <linux/sort.h>
- #include <dngl_stats.h>
- #include <wlioctl.h>
- #include <proto/bcmevent.h>
- #include <dhd.h>
- #include <dhd_pno.h>
- #include <dhd_dbg.h>
- #ifdef __BIG_ENDIAN
- #include <bcmendian.h>
- #define htod32(i) (bcmswap32(i))
- #define htod16(i) (bcmswap16(i))
- #define dtoh32(i) (bcmswap32(i))
- #define dtoh16(i) (bcmswap16(i))
- #define htodchanspec(i) htod16(i)
- #define dtohchanspec(i) dtoh16(i)
- #else
- #define htod32(i) (i)
- #define htod16(i) (i)
- #define dtoh32(i) (i)
- #define dtoh16(i) (i)
- #define htodchanspec(i) (i)
- #define dtohchanspec(i) (i)
- #endif /* IL_BIGENDINA */
- #define NULL_CHECK(p, s, err) \
- do { \
- if (!(p)) { \
- printf("NULL POINTER (%s) : %s\n", __FUNCTION__, (s)); \
- err = BCME_ERROR; \
- return err; \
- } \
- } while (0)
- #define PNO_GET_PNOSTATE(dhd) ((dhd_pno_status_info_t *)dhd->pno_state)
- #define PNO_BESTNET_LEN 1024
- #define PNO_ON 1
- #define PNO_OFF 0
- #define CHANNEL_2G_MAX 14
- #define MAX_NODE_CNT 5
- #define WLS_SUPPORTED(pno_state) (pno_state->wls_supported == TRUE)
- #define TIME_DIFF(timestamp1, timestamp2) (abs((uint32)(timestamp1/1000) \
- - (uint32)(timestamp2/1000)))
- #define ENTRY_OVERHEAD strlen("bssid=\nssid=\nfreq=\nlevel=\nage=\ndist=\ndistSd=\n====")
- #define TIME_MIN_DIFF 5
- static inline bool
- is_dfs(uint16 channel)
- {
- if (channel >= 52 && channel <= 64) /* class 2 */
- return TRUE;
- else if (channel >= 100 && channel <= 140) /* class 4 */
- return TRUE;
- else
- return FALSE;
- }
- int
- dhd_pno_clean(dhd_pub_t *dhd)
- {
- int pfn = 0;
- int err;
- dhd_pno_status_info_t *_pno_state;
- NULL_CHECK(dhd, "dhd is NULL", err);
- NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
- _pno_state = PNO_GET_PNOSTATE(dhd);
- DHD_PNO(("%s enter\n", __FUNCTION__));
- /* Disable PNO */
- err = dhd_iovar(dhd, 0, "pfn", (char *)&pfn, sizeof(pfn), 1);
- if (err < 0) {
- DHD_ERROR(("%s : failed to execute pfn(error : %d)\n",
- __FUNCTION__, err));
- goto exit;
- }
- _pno_state->pno_status = DHD_PNO_DISABLED;
- err = dhd_iovar(dhd, 0, "pfnclear", NULL, 0, 1);
- if (err < 0) {
- DHD_ERROR(("%s : failed to execute pfnclear(error : %d)\n",
- __FUNCTION__, err));
- }
- exit:
- return err;
- }
- static int
- _dhd_pno_suspend(dhd_pub_t *dhd)
- {
- int err;
- int suspend = 1;
- dhd_pno_status_info_t *_pno_state;
- NULL_CHECK(dhd, "dhd is NULL", err);
- NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
- DHD_PNO(("%s enter\n", __FUNCTION__));
- _pno_state = PNO_GET_PNOSTATE(dhd);
- err = dhd_iovar(dhd, 0, "pfn_suspend", (char *)&suspend, sizeof(suspend), 1);
- if (err < 0) {
- DHD_ERROR(("%s : failed to suspend pfn(error :%d)\n", __FUNCTION__, err));
- goto exit;
- }
- _pno_state->pno_status = DHD_PNO_SUSPEND;
- exit:
- return err;
- }
- static int
- _dhd_pno_enable(dhd_pub_t *dhd, int enable)
- {
- int err = BCME_OK;
- dhd_pno_status_info_t *_pno_state;
- NULL_CHECK(dhd, "dhd is NULL", err);
- NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
- _pno_state = PNO_GET_PNOSTATE(dhd);
- DHD_PNO(("%s enter\n", __FUNCTION__));
- if (enable & 0xfffe) {
- DHD_ERROR(("%s invalid value\n", __FUNCTION__));
- err = BCME_BADARG;
- goto exit;
- }
- if (!dhd_support_sta_mode(dhd)) {
- DHD_ERROR(("PNO is not allowed for non-STA mode"));
- err = BCME_BADOPTION;
- goto exit;
- }
- if (enable) {
- if ((_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) &&
- dhd_is_associated(dhd, NULL, NULL)) {
- DHD_ERROR(("%s Legacy PNO mode cannot be enabled "
- "in assoc mode , ignore it\n", __FUNCTION__));
- err = BCME_BADOPTION;
- goto exit;
- }
- }
- /* Enable/Disable PNO */
- err = dhd_iovar(dhd, 0, "pfn", (char *)&enable, sizeof(enable), 1);
- if (err < 0) {
- DHD_ERROR(("%s : failed to execute pfn_set\n", __FUNCTION__));
- goto exit;
- }
- _pno_state->pno_status = (enable)?
- DHD_PNO_ENABLED : DHD_PNO_DISABLED;
- if (!enable)
- _pno_state->pno_mode = DHD_PNO_NONE_MODE;
- DHD_PNO(("%s set pno as %s\n",
- __FUNCTION__, enable ? "Enable" : "Disable"));
- exit:
- return err;
- }
- static int
- _dhd_pno_set(dhd_pub_t *dhd, const dhd_pno_params_t *pno_params, dhd_pno_mode_t mode)
- {
- int err = BCME_OK;
- wl_pfn_param_t pfn_param;
- dhd_pno_params_t *_params;
- dhd_pno_status_info_t *_pno_state;
- bool combined_scan = FALSE;
- DHD_PNO(("%s enter\n", __FUNCTION__));
- NULL_CHECK(dhd, "dhd is NULL", err);
- NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
- _pno_state = PNO_GET_PNOSTATE(dhd);
- memset(&pfn_param, 0, sizeof(pfn_param));
- /* set pfn parameters */
- pfn_param.version = htod32(PFN_VERSION);
- pfn_param.flags = ((PFN_LIST_ORDER << SORT_CRITERIA_BIT) |
- (ENABLE << IMMEDIATE_SCAN_BIT) | (ENABLE << REPORT_SEPERATELY_BIT));
- if (mode == DHD_PNO_LEGACY_MODE) {
- /* check and set extra pno params */
- if ((pno_params->params_legacy.pno_repeat != 0) ||
- (pno_params->params_legacy.pno_freq_expo_max != 0)) {
- pfn_param.flags |= htod16(ENABLE << ENABLE_ADAPTSCAN_BIT);
- pfn_param.repeat = (uchar) (pno_params->params_legacy.pno_repeat);
- pfn_param.exp = (uchar) (pno_params->params_legacy.pno_freq_expo_max);
- }
- /* set up pno scan fr */
- if (pno_params->params_legacy.scan_fr != 0)
- pfn_param.scan_freq = htod32(pno_params->params_legacy.scan_fr);
- if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
- DHD_PNO(("will enable combined scan with BATCHIG SCAN MODE\n"));
- mode |= DHD_PNO_BATCH_MODE;
- combined_scan = TRUE;
- } else if (_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE) {
- DHD_PNO(("will enable combined scan with HOTLIST SCAN MODE\n"));
- mode |= DHD_PNO_HOTLIST_MODE;
- combined_scan = TRUE;
- }
- }
- if (mode & (DHD_PNO_BATCH_MODE | DHD_PNO_HOTLIST_MODE)) {
- /* Scan frequency of 30 sec */
- pfn_param.scan_freq = htod32(30);
- /* slow adapt scan is off by default */
- pfn_param.slow_freq = htod32(0);
- /* RSSI margin of 30 dBm */
- pfn_param.rssi_margin = htod16(30);
- /* Network timeout 60 sec */
- pfn_param.lost_network_timeout = htod32(60);
- /* best n = 2 by default */
- pfn_param.bestn = DEFAULT_BESTN;
- /* mscan m=0 by default, so not record best networks by default */
- pfn_param.mscan = DEFAULT_MSCAN;
- /* default repeat = 10 */
- pfn_param.repeat = DEFAULT_REPEAT;
- /* by default, maximum scan interval = 2^2
- * scan_freq when adaptive scan is turned on
- */
- pfn_param.exp = DEFAULT_EXP;
- if (mode == DHD_PNO_BATCH_MODE) {
- /* In case of BATCH SCAN */
- if (pno_params->params_batch.bestn)
- pfn_param.bestn = pno_params->params_batch.bestn;
- if (pno_params->params_batch.scan_fr)
- pfn_param.scan_freq = htod32(pno_params->params_batch.scan_fr);
- if (pno_params->params_batch.mscan)
- pfn_param.mscan = pno_params->params_batch.mscan;
- /* enable broadcast scan */
- pfn_param.flags |= (ENABLE << ENABLE_BD_SCAN_BIT);
- } else if (mode == DHD_PNO_HOTLIST_MODE) {
- /* In case of HOTLIST SCAN */
- if (pno_params->params_hotlist.scan_fr)
- pfn_param.scan_freq = htod32(pno_params->params_hotlist.scan_fr);
- pfn_param.bestn = 0;
- pfn_param.repeat = 0;
- /* enable broadcast scan */
- pfn_param.flags |= (ENABLE << ENABLE_BD_SCAN_BIT);
- }
- if (combined_scan) {
- /* Disable Adaptive Scan */
- pfn_param.flags &= ~(htod16(ENABLE << ENABLE_ADAPTSCAN_BIT));
- pfn_param.flags |= (ENABLE << ENABLE_BD_SCAN_BIT);
- pfn_param.repeat = 0;
- pfn_param.exp = 0;
- if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
- /* In case of Legacy PNO + BATCH SCAN */
- _params = &(_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS]);
- if (_params->params_batch.bestn)
- pfn_param.bestn = _params->params_batch.bestn;
- if (_params->params_batch.scan_fr)
- pfn_param.scan_freq = htod32(_params->params_batch.scan_fr);
- if (_params->params_batch.mscan)
- pfn_param.mscan = _params->params_batch.mscan;
- } else if (_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE) {
- /* In case of Legacy PNO + HOTLIST SCAN */
- _params = &(_pno_state->pno_params_arr[INDEX_OF_HOTLIST_PARAMS]);
- if (_params->params_hotlist.scan_fr)
- pfn_param.scan_freq = htod32(_params->params_hotlist.scan_fr);
- pfn_param.bestn = 0;
- pfn_param.repeat = 0;
- }
- }
- }
- if (pfn_param.scan_freq < htod32(PNO_SCAN_MIN_FW_SEC) ||
- pfn_param.scan_freq > htod32(PNO_SCAN_MAX_FW_SEC)) {
- DHD_ERROR(("%s pno freq(%d sec) is not valid \n",
- __FUNCTION__, PNO_SCAN_MIN_FW_SEC));
- err = BCME_BADARG;
- goto exit;
- }
- if (mode == DHD_PNO_BATCH_MODE) {
- int _tmp = pfn_param.bestn;
- /* set bestn to calculate the max mscan which firmware supports */
- err = dhd_iovar(dhd, 0, "pfnmem", (char *)&_tmp, sizeof(_tmp), 1);
- if (err < 0) {
- DHD_ERROR(("%s : failed to set pfnmem\n", __FUNCTION__));
- goto exit;
- }
- /* get max mscan which the firmware supports */
- err = dhd_iovar(dhd, 0, "pfnmem", (char *)&_tmp, sizeof(_tmp), 0);
- if (err < 0) {
- DHD_ERROR(("%s : failed to get pfnmem\n", __FUNCTION__));
- goto exit;
- }
- DHD_PNO((" returned mscan : %d, set bestn : %d\n", _tmp, pfn_param.bestn));
- pfn_param.mscan = MIN(pfn_param.mscan, _tmp);
- }
- err = dhd_iovar(dhd, 0, "pfn_set", (char *)&pfn_param, sizeof(pfn_param), 1);
- if (err < 0) {
- DHD_ERROR(("%s : failed to execute pfn_set\n", __FUNCTION__));
- goto exit;
- }
- /* need to return mscan if this is for batch scan instead of err */
- err = (mode == DHD_PNO_BATCH_MODE)? pfn_param.mscan : err;
- exit:
- return err;
- }
- static int
- _dhd_pno_add_ssid(dhd_pub_t *dhd, wlc_ssid_t* ssids_list, int nssid)
- {
- int err = BCME_OK;
- int i = 0;
- wl_pfn_t pfn_element;
- NULL_CHECK(dhd, "dhd is NULL", err);
- if (nssid) {
- NULL_CHECK(ssids_list, "ssid list is NULL", err);
- }
- memset(&pfn_element, 0, sizeof(pfn_element));
- {
- int j;
- for (j = 0; j < nssid; j++) {
- DHD_PNO(("%d: scan for %s size = %d\n", j,
- ssids_list[j].SSID, ssids_list[j].SSID_len));
- }
- }
- /* Check for broadcast ssid */
- for (i = 0; i < nssid; i++) {
- if (!ssids_list[i].SSID_len) {
- DHD_ERROR(("%d: Broadcast SSID is ilegal for PNO setting\n", i));
- err = BCME_ERROR;
- goto exit;
- }
- }
- /* set all pfn ssid */
- for (i = 0; i < nssid; i++) {
- pfn_element.infra = htod32(DOT11_BSSTYPE_INFRASTRUCTURE);
- pfn_element.auth = (DOT11_OPEN_SYSTEM);
- pfn_element.wpa_auth = htod32(WPA_AUTH_PFN_ANY);
- pfn_element.wsec = htod32(0);
- pfn_element.infra = htod32(1);
- pfn_element.flags = htod32(ENABLE << WL_PFN_HIDDEN_BIT);
- memcpy((char *)pfn_element.ssid.SSID, ssids_list[i].SSID,
- ssids_list[i].SSID_len);
- pfn_element.ssid.SSID_len = ssids_list[i].SSID_len;
- err = dhd_iovar(dhd, 0, "pfn_add", (char *)&pfn_element,
- sizeof(pfn_element), 1);
- if (err < 0) {
- DHD_ERROR(("%s : failed to execute pfn_add\n", __FUNCTION__));
- goto exit;
- }
- }
- exit:
- return err;
- }
- /* qsort compare function */
- static int
- _dhd_pno_cmpfunc(const void *a, const void *b)
- {
- return (*(uint16*)a - *(uint16*)b);
- }
- static int
- _dhd_pno_chan_merge(uint16 *d_chan_list, int *nchan,
- uint16 *chan_list1, int nchan1, uint16 *chan_list2, int nchan2)
- {
- int err = BCME_OK;
- int i = 0, j = 0, k = 0;
- uint16 tmp;
- NULL_CHECK(d_chan_list, "d_chan_list is NULL", err);
- NULL_CHECK(nchan, "nchan is NULL", err);
- NULL_CHECK(chan_list1, "chan_list1 is NULL", err);
- NULL_CHECK(chan_list2, "chan_list2 is NULL", err);
- /* chan_list1 and chan_list2 should be sorted at first */
- while (i < nchan1 && j < nchan2) {
- tmp = chan_list1[i] < chan_list2[j]?
- chan_list1[i++] : chan_list2[j++];
- for (; i < nchan1 && chan_list1[i] == tmp; i++);
- for (; j < nchan2 && chan_list2[j] == tmp; j++);
- d_chan_list[k++] = tmp;
- }
- while (i < nchan1) {
- tmp = chan_list1[i++];
- for (; i < nchan1 && chan_list1[i] == tmp; i++);
- d_chan_list[k++] = tmp;
- }
- while (j < nchan2) {
- tmp = chan_list2[j++];
- for (; j < nchan2 && chan_list2[j] == tmp; j++);
- d_chan_list[k++] = tmp;
- }
- *nchan = k;
- return err;
- }
- static int
- _dhd_pno_get_channels(dhd_pub_t *dhd, uint16 *d_chan_list,
- int *nchan, uint8 band, bool skip_dfs)
- {
- int err = BCME_OK;
- int i, j;
- uint32 chan_buf[WL_NUMCHANNELS + 1];
- wl_uint32_list_t *list;
- NULL_CHECK(dhd, "dhd is NULL", err);
- if (*nchan) {
- NULL_CHECK(d_chan_list, "d_chan_list is NULL", err);
- }
- list = (wl_uint32_list_t *) (void *)chan_buf;
- list->count = htod32(WL_NUMCHANNELS);
- err = dhd_wl_ioctl_cmd(dhd, WLC_GET_VALID_CHANNELS, chan_buf, sizeof(chan_buf), FALSE, 0);
- if (err < 0) {
- DHD_ERROR(("failed to get channel list (err: %d)\n", err));
- goto exit;
- }
- for (i = 0, j = 0; i < dtoh32(list->count) && i < *nchan; i++) {
- if (band == WLC_BAND_2G) {
- if (dtoh32(list->element[i]) > CHANNEL_2G_MAX)
- continue;
- } else if (band == WLC_BAND_5G) {
- if (dtoh32(list->element[i]) <= CHANNEL_2G_MAX)
- continue;
- if (skip_dfs && is_dfs(dtoh32(list->element[i])))
- continue;
- } else { /* All channels */
- if (skip_dfs && is_dfs(dtoh32(list->element[i])))
- continue;
- }
- d_chan_list[j++] = dtoh32(list->element[i]);
- }
- *nchan = j;
- exit:
- return err;
- }
- static int
- _dhd_pno_convert_format(dhd_pub_t *dhd, struct dhd_pno_batch_params *params_batch,
- char *buf, int nbufsize)
- {
- int err = BCME_OK;
- int bytes_written = 0, nreadsize = 0;
- int t_delta = 0;
- int nleftsize = nbufsize;
- uint8 cnt = 0;
- char *bp = buf;
- char eabuf[ETHER_ADDR_STR_LEN];
- #ifdef PNO_DEBUG
- char *_base_bp;
- char msg[150];
- #endif
- dhd_pno_bestnet_entry_t *iter, *next;
- dhd_pno_scan_results_t *siter, *snext;
- dhd_pno_best_header_t *phead, *pprev;
- NULL_CHECK(params_batch, "params_batch is NULL", err);
- if (nbufsize > 0)
- NULL_CHECK(buf, "buf is NULL", err);
- /* initialize the buffer */
- memset(buf, 0, nbufsize);
- DHD_PNO(("%s enter \n", __FUNCTION__));
- /* # of scans */
- if (!params_batch->get_batch.batch_started) {
- bp += nreadsize = sprintf(bp, "scancount=%d\n",
- params_batch->get_batch.expired_tot_scan_cnt);
- nleftsize -= nreadsize;
- params_batch->get_batch.batch_started = TRUE;
- }
- DHD_PNO(("%s scancount %d\n", __FUNCTION__, params_batch->get_batch.expired_tot_scan_cnt));
- /* preestimate scan count until which scan result this report is going to end */
- list_for_each_entry_safe(siter, snext,
- ¶ms_batch->get_batch.expired_scan_results_list, list) {
- phead = siter->bestnetheader;
- while (phead != NULL) {
- /* if left_size is less than bestheader total size , stop this */
- if (nleftsize <=
- (phead->tot_size + phead->tot_cnt * ENTRY_OVERHEAD))
- goto exit;
- /* increase scan count */
- cnt++;
- /* # best of each scan */
- DHD_PNO(("\n<loop : %d, apcount %d>\n", cnt - 1, phead->tot_cnt));
- /* attribute of the scan */
- if (phead->reason & PNO_STATUS_ABORT_MASK) {
- bp += nreadsize = sprintf(bp, "trunc\n");
- nleftsize -= nreadsize;
- }
- list_for_each_entry_safe(iter, next,
- &phead->entry_list, list) {
- t_delta = jiffies_to_msecs(jiffies - iter->recorded_time);
- #ifdef PNO_DEBUG
- _base_bp = bp;
- memset(msg, 0, sizeof(msg));
- #endif
- /* BSSID info */
- bp += nreadsize = sprintf(bp, "bssid=%s\n",
- bcm_ether_ntoa((const struct ether_addr *)&iter->BSSID, eabuf));
- nleftsize -= nreadsize;
- /* SSID */
- bp += nreadsize = sprintf(bp, "ssid=%s\n", iter->SSID);
- nleftsize -= nreadsize;
- /* channel */
- bp += nreadsize = sprintf(bp, "freq=%d\n",
- wf_channel2mhz(iter->channel,
- iter->channel <= CH_MAX_2G_CHANNEL?
- WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G));
- nleftsize -= nreadsize;
- /* RSSI */
- bp += nreadsize = sprintf(bp, "level=%d\n", iter->RSSI);
- nleftsize -= nreadsize;
- /* add the time consumed in Driver to the timestamp of firmware */
- iter->timestamp += t_delta;
- bp += nreadsize = sprintf(bp, "age=%d\n", iter->timestamp);
- nleftsize -= nreadsize;
- /* RTT0 */
- bp += nreadsize = sprintf(bp, "dist=%d\n",
- (iter->rtt0 == 0)? -1 : iter->rtt0);
- nleftsize -= nreadsize;
- /* RTT1 */
- bp += nreadsize = sprintf(bp, "distSd=%d\n",
- (iter->rtt0 == 0)? -1 : iter->rtt1);
- nleftsize -= nreadsize;
- bp += nreadsize = sprintf(bp, "%s", AP_END_MARKER);
- nleftsize -= nreadsize;
- list_del(&iter->list);
- MFREE(dhd->osh, iter, BESTNET_ENTRY_SIZE);
- #ifdef PNO_DEBUG
- memcpy(msg, _base_bp, bp - _base_bp);
- DHD_PNO(("Entry : \n%s", msg));
- #endif
- }
- bp += nreadsize = sprintf(bp, "%s", SCAN_END_MARKER);
- DHD_PNO(("%s", SCAN_END_MARKER));
- nleftsize -= nreadsize;
- pprev = phead;
- /* reset the header */
- siter->bestnetheader = phead = phead->next;
- MFREE(dhd->osh, pprev, BEST_HEADER_SIZE);
- siter->cnt_header--;
- }
- if (phead == NULL) {
- /* we store all entry in this scan , so it is ok to delete */
- list_del(&siter->list);
- MFREE(dhd->osh, siter, SCAN_RESULTS_SIZE);
- }
- }
- exit:
- if (cnt < params_batch->get_batch.expired_tot_scan_cnt) {
- DHD_ERROR(("Buffer size is small to save all batch entry,"
- " cnt : %d (remained_scan_cnt): %d\n",
- cnt, params_batch->get_batch.expired_tot_scan_cnt - cnt));
- }
- params_batch->get_batch.expired_tot_scan_cnt -= cnt;
- /* set FALSE only if the link list is empty after returning the data */
- if (list_empty(¶ms_batch->get_batch.expired_scan_results_list)) {
- params_batch->get_batch.batch_started = FALSE;
- bp += sprintf(bp, "%s", RESULTS_END_MARKER);
- DHD_PNO(("%s", RESULTS_END_MARKER));
- DHD_PNO(("%s : Getting the batching data is complete\n", __FUNCTION__));
- }
- /* return used memory in buffer */
- bytes_written = (int32)(bp - buf);
- return bytes_written;
- }
- static int
- _dhd_pno_clear_all_batch_results(dhd_pub_t *dhd, struct list_head *head, bool only_last)
- {
- int err = BCME_OK;
- int removed_scan_cnt = 0;
- dhd_pno_scan_results_t *siter, *snext;
- dhd_pno_best_header_t *phead, *pprev;
- dhd_pno_bestnet_entry_t *iter, *next;
- NULL_CHECK(dhd, "dhd is NULL", err);
- NULL_CHECK(head, "head is NULL", err);
- NULL_CHECK(head->next, "head->next is NULL", err);
- DHD_PNO(("%s enter\n", __FUNCTION__));
- list_for_each_entry_safe(siter, snext,
- head, list) {
- if (only_last) {
- /* in case that we need to delete only last one */
- if (!list_is_last(&siter->list, head)) {
- /* skip if the one is not last */
- continue;
- }
- }
- /* delete all data belong if the one is last */
- phead = siter->bestnetheader;
- while (phead != NULL) {
- removed_scan_cnt++;
- list_for_each_entry_safe(iter, next,
- &phead->entry_list, list) {
- list_del(&iter->list);
- MFREE(dhd->osh, iter, BESTNET_ENTRY_SIZE);
- }
- pprev = phead;
- phead = phead->next;
- MFREE(dhd->osh, pprev, BEST_HEADER_SIZE);
- }
- if (phead == NULL) {
- /* it is ok to delete top node */
- list_del(&siter->list);
- MFREE(dhd->osh, siter, SCAN_RESULTS_SIZE);
- }
- }
- return removed_scan_cnt;
- }
- static int
- _dhd_pno_cfg(dhd_pub_t *dhd, uint16 *channel_list, int nchan)
- {
- int err = BCME_OK;
- int i = 0;
- wl_pfn_cfg_t pfncfg_param;
- NULL_CHECK(dhd, "dhd is NULL", err);
- if (nchan) {
- NULL_CHECK(channel_list, "nchan is NULL", err);
- }
- DHD_PNO(("%s enter : nchan : %d\n", __FUNCTION__, nchan));
- memset(&pfncfg_param, 0, sizeof(wl_pfn_cfg_t));
- /* Setup default values */
- pfncfg_param.reporttype = htod32(WL_PFN_REPORT_ALLNET);
- pfncfg_param.channel_num = htod32(0);
- for (i = 0; i < nchan && nchan < WL_NUMCHANNELS; i++)
- pfncfg_param.channel_list[i] = channel_list[i];
- pfncfg_param.channel_num = htod32(nchan);
- err = dhd_iovar(dhd, 0, "pfn_cfg", (char *)&pfncfg_param, sizeof(pfncfg_param), 1);
- if (err < 0) {
- DHD_ERROR(("%s : failed to execute pfn_cfg\n", __FUNCTION__));
- goto exit;
- }
- exit:
- return err;
- }
- static int
- _dhd_pno_reinitialize_prof(dhd_pub_t *dhd, dhd_pno_params_t *params, dhd_pno_mode_t mode)
- {
- int err = BCME_OK;
- dhd_pno_status_info_t *_pno_state;
- NULL_CHECK(dhd, "dhd is NULL\n", err);
- NULL_CHECK(dhd->pno_state, "pno_state is NULL\n", err);
- DHD_PNO(("%s enter\n", __FUNCTION__));
- _pno_state = PNO_GET_PNOSTATE(dhd);
- mutex_lock(&_pno_state->pno_mutex);
- switch (mode) {
- case DHD_PNO_LEGACY_MODE: {
- struct dhd_pno_ssid *iter, *next;
- if (params->params_legacy.nssid > 0) {
- list_for_each_entry_safe(iter, next,
- ¶ms->params_legacy.ssid_list, list) {
- list_del(&iter->list);
- kfree(iter);
- }
- }
- params->params_legacy.nssid = 0;
- params->params_legacy.scan_fr = 0;
- params->params_legacy.pno_freq_expo_max = 0;
- params->params_legacy.pno_repeat = 0;
- params->params_legacy.nchan = 0;
- memset(params->params_legacy.chan_list, 0,
- sizeof(params->params_legacy.chan_list));
- break;
- }
- case DHD_PNO_BATCH_MODE: {
- params->params_batch.scan_fr = 0;
- params->params_batch.mscan = 0;
- params->params_batch.nchan = 0;
- params->params_batch.rtt = 0;
- params->params_batch.bestn = 0;
- params->params_batch.nchan = 0;
- params->params_batch.band = WLC_BAND_AUTO;
- memset(params->params_batch.chan_list, 0,
- sizeof(params->params_batch.chan_list));
- params->params_batch.get_batch.batch_started = FALSE;
- params->params_batch.get_batch.buf = NULL;
- params->params_batch.get_batch.bufsize = 0;
- params->params_batch.get_batch.reason = 0;
- _dhd_pno_clear_all_batch_results(dhd,
- ¶ms->params_batch.get_batch.scan_results_list, FALSE);
- _dhd_pno_clear_all_batch_results(dhd,
- ¶ms->params_batch.get_batch.expired_scan_results_list, FALSE);
- params->params_batch.get_batch.tot_scan_cnt = 0;
- params->params_batch.get_batch.expired_tot_scan_cnt = 0;
- params->params_batch.get_batch.top_node_cnt = 0;
- INIT_LIST_HEAD(¶ms->params_batch.get_batch.scan_results_list);
- INIT_LIST_HEAD(¶ms->params_batch.get_batch.expired_scan_results_list);
- break;
- }
- case DHD_PNO_HOTLIST_MODE: {
- struct dhd_pno_bssid *iter, *next;
- if (params->params_hotlist.nbssid > 0) {
- list_for_each_entry_safe(iter, next,
- ¶ms->params_hotlist.bssid_list, list) {
- list_del(&iter->list);
- kfree(iter);
- }
- }
- params->params_hotlist.scan_fr = 0;
- params->params_hotlist.nbssid = 0;
- params->params_hotlist.nchan = 0;
- params->params_batch.band = WLC_BAND_AUTO;
- memset(params->params_hotlist.chan_list, 0,
- sizeof(params->params_hotlist.chan_list));
- break;
- }
- default:
- DHD_ERROR(("%s : unknown mode : %d\n", __FUNCTION__, mode));
- break;
- }
- mutex_unlock(&_pno_state->pno_mutex);
- return err;
- }
- static int
- _dhd_pno_add_bssid(dhd_pub_t *dhd, wl_pfn_bssid_t *p_pfn_bssid, int nbssid)
- {
- int err = BCME_OK;
- NULL_CHECK(dhd, "dhd is NULL", err);
- if (nbssid) {
- NULL_CHECK(p_pfn_bssid, "bssid list is NULL", err);
- }
- err = dhd_iovar(dhd, 0, "pfn_add_bssid", (char *)&p_pfn_bssid,
- sizeof(wl_pfn_bssid_t) * nbssid, 1);
- if (err < 0) {
- DHD_ERROR(("%s : failed to execute pfn_cfg\n", __FUNCTION__));
- goto exit;
- }
- exit:
- return err;
- }
- int
- dhd_pno_stop_for_ssid(dhd_pub_t *dhd)
- {
- int err = BCME_OK;
- uint32 mode = 0;
- dhd_pno_status_info_t *_pno_state;
- dhd_pno_params_t *_params;
- wl_pfn_bssid_t *p_pfn_bssid = NULL;
- NULL_CHECK(dhd, "dev is NULL", err);
- NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
- _pno_state = PNO_GET_PNOSTATE(dhd);
- if (!(_pno_state->pno_mode & DHD_PNO_LEGACY_MODE)) {
- DHD_ERROR(("%s : LEGACY PNO MODE is not enabled\n", __FUNCTION__));
- goto exit;
- }
- DHD_PNO(("%s enter\n", __FUNCTION__));
- _pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
- /* restart Batch mode if the batch mode is on */
- if (_pno_state->pno_mode & (DHD_PNO_BATCH_MODE | DHD_PNO_HOTLIST_MODE)) {
- /* retrieve the batching data from firmware into host */
- dhd_pno_get_for_batch(dhd, NULL, 0, PNO_STATUS_DISABLE);
- /* save current pno_mode before calling dhd_pno_clean */
- mode = _pno_state->pno_mode;
- dhd_pno_clean(dhd);
- /* restore previous pno_mode */
- _pno_state->pno_mode = mode;
- if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
- _params = &(_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS]);
- /* restart BATCH SCAN */
- err = dhd_pno_set_for_batch(dhd, &_params->params_batch);
- if (err < 0) {
- _pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
- DHD_ERROR(("%s : failed to restart batch scan(err: %d)\n",
- __FUNCTION__, err));
- goto exit;
- }
- } else if (_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE) {
- /* restart HOTLIST SCAN */
- struct dhd_pno_bssid *iter, *next;
- _params = &(_pno_state->pno_params_arr[INDEX_OF_HOTLIST_PARAMS]);
- p_pfn_bssid = kzalloc(sizeof(wl_pfn_bssid_t) *
- _params->params_hotlist.nbssid, GFP_KERNEL);
- if (p_pfn_bssid == NULL) {
- DHD_ERROR(("%s : failed to allocate wl_pfn_bssid_t array"
- " (count: %d)",
- __FUNCTION__, _params->params_hotlist.nbssid));
- err = BCME_ERROR;
- _pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
- goto exit;
- }
- /* convert dhd_pno_bssid to wl_pfn_bssid */
- list_for_each_entry_safe(iter, next,
- &_params->params_hotlist.bssid_list, list) {
- memcpy(&p_pfn_bssid->macaddr,
- &iter->macaddr, ETHER_ADDR_LEN);
- p_pfn_bssid->flags = iter->flags;
- p_pfn_bssid++;
- }
- err = dhd_pno_set_for_hotlist(dhd, p_pfn_bssid, &_params->params_hotlist);
- if (err < 0) {
- _pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
- DHD_ERROR(("%s : failed to restart hotlist scan(err: %d)\n",
- __FUNCTION__, err));
- goto exit;
- }
- }
- } else {
- err = dhd_pno_clean(dhd);
- if (err < 0) {
- DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
- __FUNCTION__, err));
- goto exit;
- }
- }
- exit:
- if (p_pfn_bssid)
- kfree(p_pfn_bssid);
- return err;
- }
- int
- dhd_pno_enable(dhd_pub_t *dhd, int enable)
- {
- int err = BCME_OK;
- NULL_CHECK(dhd, "dhd is NULL", err);
- DHD_PNO(("%s enter\n", __FUNCTION__));
- return (_dhd_pno_enable(dhd, enable));
- }
- int
- dhd_pno_set_for_ssid(dhd_pub_t *dhd, wlc_ssid_t* ssid_list, int nssid,
- uint16 scan_fr, int pno_repeat, int pno_freq_expo_max, uint16 *channel_list, int nchan)
- {
- struct dhd_pno_ssid *_pno_ssid;
- dhd_pno_params_t *_params;
- dhd_pno_params_t *_params2;
- dhd_pno_status_info_t *_pno_state;
- uint16 _chan_list[WL_NUMCHANNELS];
- int32 tot_nchan = 0;
- int err = BCME_OK;
- int i;
- int mode = 0;
- NULL_CHECK(dhd, "dhd is NULL", err);
- NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
- _pno_state = PNO_GET_PNOSTATE(dhd);
- if (!dhd_support_sta_mode(dhd)) {
- err = BCME_BADOPTION;
- goto exit;
- }
- DHD_PNO(("%s enter : scan_fr :%d, pno_repeat :%d,"
- "pno_freq_expo_max: %d, nchan :%d\n", __FUNCTION__,
- scan_fr, pno_repeat, pno_freq_expo_max, nchan));
- _params = &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS]);
- if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
- DHD_ERROR(("%s : Legacy PNO mode was already started, "
- "will disable previous one to start new one\n", __FUNCTION__));
- err = dhd_pno_stop_for_ssid(dhd);
- if (err < 0) {
- DHD_ERROR(("%s : failed to stop legacy PNO (err %d)\n",
- __FUNCTION__, err));
- goto exit;
- }
- }
- _pno_state->pno_mode |= DHD_PNO_LEGACY_MODE;
- err = _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_LEGACY_MODE);
- if (err < 0) {
- DHD_ERROR(("%s : failed to reinitialize profile (err %d)\n",
- __FUNCTION__, err));
- goto exit;
- }
- memset(_chan_list, 0, sizeof(_chan_list));
- tot_nchan = nchan;
- if (tot_nchan > 0 && channel_list) {
- for (i = 0; i < nchan; i++)
- _params->params_legacy.chan_list[i] = _chan_list[i] = channel_list[i];
- }
- if (_pno_state->pno_mode & (DHD_PNO_BATCH_MODE | DHD_PNO_HOTLIST_MODE)) {
- DHD_PNO(("BATCH SCAN is on progress in firmware\n"));
- /* retrieve the batching data from firmware into host */
- dhd_pno_get_for_batch(dhd, NULL, 0, PNO_STATUS_DISABLE);
- /* store current pno_mode before disabling pno */
- mode = _pno_state->pno_mode;
- err = _dhd_pno_enable(dhd, PNO_OFF);
- if (err < 0) {
- DHD_ERROR(("%s : failed to disable PNO\n", __FUNCTION__));
- goto exit;
- }
- /* restore the previous mode */
- _pno_state->pno_mode = mode;
- /* use superset of channel list between two mode */
- if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
- _params2 = &(_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS]);
- if (_params2->params_batch.nchan > 0 && nchan > 0) {
- err = _dhd_pno_chan_merge(_chan_list, &tot_nchan,
- &_params2->params_batch.chan_list[0],
- _params2->params_batch.nchan,
- &channel_list[0], nchan);
- if (err < 0) {
- DHD_ERROR(("%s : failed to merge channel list"
- " between legacy and batch\n",
- __FUNCTION__));
- goto exit;
- }
- } else {
- DHD_PNO(("superset channel will use"
- " all channels in firmware\n"));
- }
- } else if (_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE) {
- _params2 = &(_pno_state->pno_params_arr[INDEX_OF_HOTLIST_PARAMS]);
- if (_params2->params_hotlist.nchan > 0 && nchan > 0) {
- err = _dhd_pno_chan_merge(_chan_list, &tot_nchan,
- &_params2->params_hotlist.chan_list[0],
- _params2->params_hotlist.nchan,
- &channel_list[0], nchan);
- if (err < 0) {
- DHD_ERROR(("%s : failed to merge channel list"
- " between legacy and hotlist\n",
- __FUNCTION__));
- goto exit;
- }
- }
- }
- }
- _params->params_legacy.scan_fr = scan_fr;
- _params->params_legacy.pno_repeat = pno_repeat;
- _params->params_legacy.pno_freq_expo_max = pno_freq_expo_max;
- _params->params_legacy.nchan = nchan;
- _params->params_legacy.nssid = nssid;
- INIT_LIST_HEAD(&_params->params_legacy.ssid_list);
- if ((err = _dhd_pno_set(dhd, _params, DHD_PNO_LEGACY_MODE)) < 0) {
- DHD_ERROR(("failed to set call pno_set (err %d) in firmware\n", err));
- goto exit;
- }
- if ((err = _dhd_pno_add_ssid(dhd, ssid_list, nssid)) < 0) {
- DHD_ERROR(("failed to add ssid list(err %d), %d in firmware\n", err, nssid));
- goto exit;
- }
- for (i = 0; i < nssid; i++) {
- _pno_ssid = kzalloc(sizeof(struct dhd_pno_ssid), GFP_KERNEL);
- if (_pno_ssid == NULL) {
- DHD_ERROR(("%s : failed to allocate struct dhd_pno_ssid\n",
- __FUNCTION__));
- goto exit;
- }
- _pno_ssid->SSID_len = ssid_list[i].SSID_len;
- memcpy(_pno_ssid->SSID, ssid_list[i].SSID, _pno_ssid->SSID_len);
- list_add_tail(&_pno_ssid->list, &_params->params_legacy.ssid_list);
- }
- if (tot_nchan > 0) {
- if ((err = _dhd_pno_cfg(dhd, _chan_list, tot_nchan)) < 0) {
- DHD_ERROR(("%s : failed to set call pno_cfg (err %d) in firmware\n",
- __FUNCTION__, err));
- goto exit;
- }
- }
- if (_pno_state->pno_status == DHD_PNO_DISABLED) {
- if ((err = _dhd_pno_enable(dhd, PNO_ON)) < 0)
- DHD_ERROR(("%s : failed to enable PNO\n", __FUNCTION__));
- }
- exit:
- /* clear mode in case of error */
- if (err < 0)
- _pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
- return err;
- }
- int
- dhd_pno_set_for_batch(dhd_pub_t *dhd, struct dhd_pno_batch_params *batch_params)
- {
- int err = BCME_OK;
- uint16 _chan_list[WL_NUMCHANNELS];
- int rem_nchan = 0, tot_nchan = 0;
- int mode = 0, mscan = 0;
- int i = 0;
- dhd_pno_params_t *_params;
- dhd_pno_params_t *_params2;
- dhd_pno_status_info_t *_pno_state;
- wlc_ssid_t *p_ssid_list = NULL;
- NULL_CHECK(dhd, "dhd is NULL", err);
- NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
- NULL_CHECK(batch_params, "batch_params is NULL", err);
- _pno_state = PNO_GET_PNOSTATE(dhd);
- DHD_PNO(("%s enter\n", __FUNCTION__));
- if (!dhd_support_sta_mode(dhd)) {
- err = BCME_BADOPTION;
- goto exit;
- }
- if (!WLS_SUPPORTED(_pno_state)) {
- DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
- err = BCME_UNSUPPORTED;
- goto exit;
- }
- _params = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS];
- if (!(_pno_state->pno_mode & DHD_PNO_BATCH_MODE)) {
- _pno_state->pno_mode |= DHD_PNO_BATCH_MODE;
- err = _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_BATCH_MODE);
- if (err < 0) {
- DHD_ERROR(("%s : failed to call _dhd_pno_reinitialize_prof\n",
- __FUNCTION__));
- goto exit;
- }
- } else {
- /* batch mode is already started */
- return -EBUSY;
- }
- _params->params_batch.scan_fr = batch_params->scan_fr;
- _params->params_batch.bestn = batch_params->bestn;
- _params->params_batch.mscan = (batch_params->mscan)?
- batch_params->mscan : DEFAULT_BATCH_MSCAN;
- _params->params_batch.nchan = batch_params->nchan;
- memcpy(_params->params_batch.chan_list, batch_params->chan_list,
- sizeof(_params->params_batch.chan_list));
- memset(_chan_list, 0, sizeof(_chan_list));
- rem_nchan = ARRAYSIZE(batch_params->chan_list) - batch_params->nchan;
- if (batch_params->band == WLC_BAND_2G || batch_params->band == WLC_BAND_5G) {
- /* get a valid channel list based on band B or A */
- err = _dhd_pno_get_channels(dhd,
- &_params->params_batch.chan_list[batch_params->nchan],
- &rem_nchan, batch_params->band, FALSE);
- if (err < 0) {
- DHD_ERROR(("%s: failed to get valid channel list(band : %d)\n",
- __FUNCTION__, batch_params->band));
- goto exit;
- }
- /* now we need to update nchan because rem_chan has valid channel count */
- _params->params_batch.nchan += rem_nchan;
- /* need to sort channel list */
- sort(_params->params_batch.chan_list, _params->params_batch.nchan,
- sizeof(_params->params_batch.chan_list[0]), _dhd_pno_cmpfunc, NULL);
- }
- #ifdef PNO_DEBUG
- {
- DHD_PNO(("Channel list : "));
- for (i = 0; i < _params->params_batch.nchan; i++) {
- DHD_PNO(("%d ", _params->params_batch.chan_list[i]));
- }
- DHD_PNO(("\n"));
- }
- #endif
- if (_params->params_batch.nchan) {
- /* copy the channel list into local array */
- memcpy(_chan_list, _params->params_batch.chan_list, sizeof(_chan_list));
- tot_nchan = _params->params_batch.nchan;
- }
- if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
- struct dhd_pno_ssid *iter, *next;
- DHD_PNO(("PNO SSID is on progress in firmware\n"));
- /* store current pno_mode before disabling pno */
- mode = _pno_state->pno_mode;
- err = _dhd_pno_enable(dhd, PNO_OFF);
- if (err < 0) {
- DHD_ERROR(("%s : failed to disable PNO\n", __FUNCTION__));
- goto exit;
- }
- /* restore the previous mode */
- _pno_state->pno_mode = mode;
- /* Use the superset for channelist between two mode */
- _params2 = &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS]);
- if (_params2->params_legacy.nchan > 0 && _params->params_batch.nchan > 0) {
- err = _dhd_pno_chan_merge(_chan_list, &tot_nchan,
- &_params2->params_legacy.chan_list[0],
- _params2->params_legacy.nchan,
- &_params->params_batch.chan_list[0], _params->params_batch.nchan);
- if (err < 0) {
- DHD_ERROR(("%s : failed to merge channel list"
- " between legacy and batch\n",
- __FUNCTION__));
- goto exit;
- }
- } else {
- DHD_PNO(("superset channel will use all channels in firmware\n"));
- }
- p_ssid_list = kzalloc(sizeof(wlc_ssid_t) *
- _params2->params_legacy.nssid, GFP_KERNEL);
- if (p_ssid_list == NULL) {
- DHD_ERROR(("%s : failed to allocate wlc_ssid_t array (count: %d)",
- __FUNCTION__, _params2->params_legacy.nssid));
- err = BCME_ERROR;
- _pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
- goto exit;
- }
- i = 0;
- /* convert dhd_pno_ssid to dhd_pno_ssid */
- list_for_each_entry_safe(iter, next, &_params2->params_legacy.ssid_list, list) {
- p_ssid_list[i].SSID_len = iter->SSID_len;
- memcpy(p_ssid_list->SSID, iter->SSID, p_ssid_list[i].SSID_len);
- i++;
- }
- if ((err = _dhd_pno_add_ssid(dhd, p_ssid_list,
- _params2->params_legacy.nssid)) < 0) {
- DHD_ERROR(("failed to add ssid list (err %d) in firmware\n", err));
- goto exit;
- }
- }
- if ((err = _dhd_pno_set(dhd, _params, DHD_PNO_BATCH_MODE)) < 0) {
- DHD_ERROR(("%s : failed to set call pno_set (err %d) in firmware\n",
- __FUNCTION__, err));
- goto exit;
- } else {
- /* we need to return mscan */
- mscan = err;
- }
- if (tot_nchan > 0) {
- if ((err = _dhd_pno_cfg(dhd, _chan_list, tot_nchan)) < 0) {
- DHD_ERROR(("%s : failed to set call pno_cfg (err %d) in firmware\n",
- __FUNCTION__, err));
- goto exit;
- }
- }
- if (_pno_state->pno_status == DHD_PNO_DISABLED) {
- if ((err = _dhd_pno_enable(dhd, PNO_ON)) < 0)
- DHD_ERROR(("%s : failed to enable PNO\n", __FUNCTION__));
- }
- exit:
- /* clear mode in case of error */
- if (err < 0)
- _pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
- else {
- /* return #max scan firmware can do */
- err = mscan;
- }
- if (p_ssid_list)
- kfree(p_ssid_list);
- return err;
- }
- static int
- _dhd_pno_get_for_batch(dhd_pub_t *dhd, char *buf, int bufsize, int reason)
- {
- int err = BCME_OK;
- int i, j;
- uint32 timestamp = 0;
- dhd_pno_params_t *_params = NULL;
- dhd_pno_status_info_t *_pno_state = NULL;
- wl_pfn_lscanresults_t *plbestnet = NULL;
- wl_pfn_lnet_info_t *plnetinfo;
- dhd_pno_bestnet_entry_t *pbestnet_entry;
- dhd_pno_best_header_t *pbestnetheader = NULL;
- dhd_pno_scan_results_t *pscan_results = NULL, *siter, *snext;
- bool allocate_header = FALSE;
- NULL_CHECK(dhd, "dhd is NULL", err);
- NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
- if (!dhd_support_sta_mode(dhd)) {
- err = BCME_BADOPTION;
- goto exit;
- }
- DHD_PNO(("%s enter\n", __FUNCTION__));
- _pno_state = PNO_GET_PNOSTATE(dhd);
- if (!WLS_SUPPORTED(_pno_state)) {
- DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
- err = BCME_UNSUPPORTED;
- goto exit;
- }
- if (!(_pno_state->pno_mode & DHD_PNO_BATCH_MODE)) {
- DHD_ERROR(("%s: Batching SCAN mode is not enabled\n", __FUNCTION__));
- goto exit;
- }
- mutex_lock(&_pno_state->pno_mutex);
- _params = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS];
- if (buf && bufsize) {
- if (!list_empty(&_params->params_batch.get_batch.expired_scan_results_list)) {
- /* need to check whether we have cashed data or not */
- DHD_PNO(("%s: have cashed batching data in Driver\n",
- __FUNCTION__));
- /* convert to results format */
- goto convert_format;
- } else {
- /* this is a first try to get batching results */
- if (!list_empty(&_params->params_batch.get_batch.scan_results_list)) {
- /* move the scan_results_list to expired_scan_results_lists */
- list_for_each_entry_safe(siter, snext,
- &_params->params_batch.get_batch.scan_results_list, list) {
- list_move_tail(&siter->list,
- &_params->params_batch.get_batch.expired_scan_results_list);
- }
- _params->params_batch.get_batch.top_node_cnt = 0;
- _params->params_batch.get_batch.expired_tot_scan_cnt =
- _params->params_batch.get_batch.tot_scan_cnt;
- _params->params_batch.get_batch.tot_scan_cnt = 0;
- goto convert_format;
- }
- }
- }
- /* create dhd_pno_scan_results_t whenever we got event WLC_E_PFN_BEST_BATCHING */
- pscan_results = (dhd_pno_scan_results_t *)MALLOC(dhd->osh, SCAN_RESULTS_SIZE);
- if (pscan_results == NULL) {
- err = BCME_NOMEM;
- DHD_ERROR(("failed to allocate dhd_pno_scan_results_t\n"));
- goto exit;
- }
- pscan_results->bestnetheader = NULL;
- pscan_results->cnt_header = 0;
- /* add the element into list unless total node cnt is less than MAX_NODE_ CNT */
- if (_params->params_batch.get_batch.top_node_cnt < MAX_NODE_CNT) {
- list_add(&pscan_results->list, &_params->params_batch.get_batch.scan_results_list);
- _params->params_batch.get_batch.top_node_cnt++;
- } else {
- int _removed_scan_cnt;
- /* remove oldest one and add new one */
- DHD_PNO(("%s : Remove oldest node and add new one\n", __FUNCTION__));
- _removed_scan_cnt = _dhd_pno_clear_all_batch_results(dhd,
- &_params->params_batch.get_batch.scan_results_list, TRUE);
- _params->params_batch.get_batch.tot_scan_cnt -= _removed_scan_cnt;
- list_add(&pscan_results->list, &_params->params_batch.get_batch.scan_results_list);
- }
- plbestnet = (wl_pfn_lscanresults_t *)MALLOC(dhd->osh, PNO_BESTNET_LEN);
- NULL_CHECK(plbestnet, "failed to allocate buffer for bestnet", err);
- DHD_PNO(("%s enter\n", __FUNCTION__));
- memset(plbestnet, 0, PNO_BESTNET_LEN);
- while (plbestnet->status != PFN_COMPLETE) {
- memset(plbestnet, 0, PNO_BESTNET_LEN);
- err = dhd_iovar(dhd, 0, "pfnlbest", (char *)plbestnet, PNO_BESTNET_LEN, 0);
- if (err < 0) {
- if (err == BCME_EPERM) {
- DHD_ERROR(("we cannot get the batching data "
- "during scanning in firmware, try again\n,"));
- msleep(500);
- continue;
- } else {
- DHD_ERROR(("%s : failed to execute pfnlbest (err :%d)\n",
- __FUNCTION__, err));
- goto exit;
- }
- }
- DHD_PNO(("ver %d, status : %d, count %d\n", plbestnet->version,
- plbestnet->status, plbestnet->count));
- if (plbestnet->version != PFN_SCANRESULT_VERSION) {
- err = BCME_VERSION;
- DHD_ERROR(("bestnet version(%d) is mismatch with Driver version(%d)\n",
- plbestnet->version, PFN_SCANRESULT_VERSION));
- goto exit;
- }
- plnetinfo = plbestnet->netinfo;
- for (i = 0; i < plbestnet->count; i++) {
- pbestnet_entry = (dhd_pno_bestnet_entry_t *)
- MALLOC(dhd->osh, BESTNET_ENTRY_SIZE);
- if (pbestnet_entry == NULL) {
- err = BCME_NOMEM;
- DHD_ERROR(("failed to allocate dhd_pno_bestnet_entry\n"));
- goto exit;
- }
- memset(pbestnet_entry, 0, BESTNET_ENTRY_SIZE);
- pbestnet_entry->recorded_time = jiffies; /* record the current time */
- /* create header for the first entry */
- allocate_header = (i == 0)? TRUE : FALSE;
- /* check whether the new generation is started or not */
- if (timestamp && (TIME_DIFF(timestamp, plnetinfo->timestamp)
- > TIME_MIN_DIFF))
- allocate_header = TRUE;
- timestamp = plnetinfo->timestamp;
- if (allocate_header) {
- pbestnetheader = (dhd_pno_best_header_t *)
- MALLOC(dhd->osh, BEST_HEADER_SIZE);
- if (pbestnetheader == NULL) {
- err = BCME_NOMEM;
- if (pbestnet_entry)
- MFREE(dhd->osh, pbestnet_entry,
- BESTNET_ENTRY_SIZE);
- DHD_ERROR(("failed to allocate dhd_pno_bestnet_entry\n"));
- goto exit;
- }
- /* increase total cnt of bestnet header */
- pscan_results->cnt_header++;
- /* need to record the reason to call dhd_pno_get_for_bach */
- if (reason)
- pbestnetheader->reason = (ENABLE << reason);
- memset(pbestnetheader, 0, BEST_HEADER_SIZE);
- /* initialize the head of linked list */
- INIT_LIST_HEAD(&(pbestnetheader->entry_list));
- /* link the pbestnet heaer into existed list */
- if (pscan_results->bestnetheader == NULL)
- /* In case of header */
- pscan_results->bestnetheader = pbestnetheader;
- else {
- dhd_pno_best_header_t *head = pscan_results->bestnetheader;
- pscan_results->bestnetheader = pbestnetheader;
- pbestnetheader->next = head;
- }
- }
- /* fills the best network info */
- pbestnet_entry->channel = plnetinfo->pfnsubnet.channel;
- pbestnet_entry->RSSI = plnetinfo->RSSI;
- if (plnetinfo->flags & PFN_PARTIAL_SCAN_MASK) {
- /* if RSSI is positive value, we assume that
- * this scan is aborted by other scan
- */
- DHD_PNO(("This scan is aborted\n"));
- pbestnetheader->reason = (ENABLE << PNO_STATUS_ABORT);
- }
- pbestnet_entry->rtt0 = plnetinfo->rtt0;
- pbestnet_entry->rtt1 = plnetinfo->rtt1;
- pbestnet_entry->timestamp = plnetinfo->timestamp;
- pbestnet_entry->SSID_len = plnetinfo->pfnsubnet.SSID_len;
- memcpy(pbestnet_entry->SSID, plnetinfo->pfnsubnet.SSID,
- pbestnet_entry->SSID_len);
- memcpy(&pbestnet_entry->BSSID, &plnetinfo->pfnsubnet.BSSID, ETHER_ADDR_LEN);
- /* add the element into list */
- list_add_tail(&pbestnet_entry->list, &pbestnetheader->entry_list);
- /* increase best entry count */
- pbestnetheader->tot_cnt++;
- pbestnetheader->tot_size += BESTNET_ENTRY_SIZE;
- DHD_PNO(("Header %d\n", pscan_results->cnt_header - 1));
- DHD_PNO(("\tSSID : "));
- for (j = 0; j < plnetinfo->pfnsubnet.SSID_len; j++)
- DHD_PNO(("%c", plnetinfo->pfnsubnet.SSID[j]));
- DHD_PNO(("\n"));
- DHD_PNO(("\tBSSID: %02x:%02x:%02x:%02x:%02x:%02x\n",
- plnetinfo->pfnsubnet.BSSID.octet[0],
- plnetinfo->pfnsubnet.BSSID.octet[1],
- plnetinfo->pfnsubnet.BSSID.octet[2],
- plnetinfo->pfnsubnet.BSSID.octet[3],
- plnetinfo->pfnsubnet.BSSID.octet[4],
- plnetinfo->pfnsubnet.BSSID.octet[5]));
- DHD_PNO(("\tchannel: %d, RSSI: %d, timestamp: %d ms\n",
- plnetinfo->pfnsubnet.channel,
- plnetinfo->RSSI, plnetinfo->timestamp));
- DHD_PNO(("\tRTT0 : %d, RTT1: %d\n", plnetinfo->rtt0, plnetinfo->rtt1));
- plnetinfo++;
- }
- }
- if (pscan_results->cnt_header == 0) {
- /* In case that we didn't get any data from the firmware
- * Remove the current scan_result list from get_bach.scan_results_list.
- */
- DHD_PNO(("NO BATCH DATA from Firmware, Delete current SCAN RESULT LIST\n"));
- list_del(&pscan_results->list);
- MFREE(dhd->osh, pscan_results, SCAN_RESULTS_SIZE);
- _params->params_batch.get_batch.top_node_cnt--;
- }
- /* increase total scan count using current scan count */
- _params->params_batch.get_batch.tot_scan_cnt += pscan_results->cnt_header;
- if (buf && bufsize) {
- /* This is a first try to get batching results */
- if (!list_empty(&_params->params_batch.get_batch.scan_results_list)) {
- /* move the scan_results_list to expired_scan_results_lists */
- list_for_each_entry_safe(siter, snext,
- &_params->params_batch.get_batch.scan_results_list, list) {
- list_move_tail(&siter->list,
- &_params->params_batch.get_batch.expired_scan_results_list);
- }
- /* reset gloval values after moving to expired list */
- _params->params_batch.get_batch.top_node_cnt = 0;
- _params->params_batch.get_batch.expired_tot_scan_cnt =
- _params->params_batch.get_batch.tot_scan_cnt;
- _params->params_batch.get_batch.tot_scan_cnt = 0;
- }
- convert_format:
- err = _dhd_pno_convert_format(dhd, &_params->params_batch, buf, bufsize);
- if (err < 0) {
- DHD_ERROR(("failed to convert the data into upper layer format\n"));
- goto exit;
- }
- }
- exit:
- if (plbestnet)
- MFREE(dhd->osh, plbestnet, PNO_BESTNET_LEN);
- if (_params) {
- _params->params_batch.get_batch.buf = NULL;
- _params->params_batch.get_batch.bufsize = 0;
- _params->params_batch.get_batch.bytes_written = err;
- }
- mutex_unlock(&_pno_state->pno_mutex);
- if (waitqueue_active(&_pno_state->get_batch_done.wait))
- complete(&_pno_state->get_batch_done);
- return err;
- }
- static void
- _dhd_pno_get_batch_handler(struct work_struct *work)
- {
- dhd_pno_status_info_t *_pno_state;
- dhd_pub_t *dhd;
- struct dhd_pno_batch_params *params_batch;
- DHD_PNO(("%s enter\n", __FUNCTION__));
- _pno_state = container_of(work, struct dhd_pno_status_info, work);
- dhd = _pno_state->dhd;
- if (dhd == NULL) {
- DHD_ERROR(("%s : dhd is NULL\n", __FUNCTION__));
- return;
- }
- params_batch = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS].params_batch;
- _dhd_pno_get_for_batch(dhd, params_batch->get_batch.buf,
- params_batch->get_batch.bufsize, params_batch->get_batch.reason);
- }
- int
- dhd_pno_get_for_batch(dhd_pub_t *dhd, char *buf, int bufsize, int reason)
- {
- int err = BCME_OK;
- char *pbuf = buf;
- dhd_pno_status_info_t *_pno_state;
- struct dhd_pno_batch_params *params_batch;
- NULL_CHECK(dhd, "dhd is NULL", err);
- NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
- if (!dhd_support_sta_mode(dhd)) {
- err = BCME_BADOPTION;
- goto exit;
- }
- DHD_PNO(("%s enter\n", __FUNCTION__));
- _pno_state = PNO_GET_PNOSTATE(dhd);
- if (!WLS_SUPPORTED(_pno_state)) {
- DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
- err = BCME_UNSUPPORTED;
- goto exit;
- }
- params_batch = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS].params_batch;
- if (!(_pno_state->pno_mode & DHD_PNO_BATCH_MODE)) {
- DHD_ERROR(("%s: Batching SCAN mode is not enabled\n", __FUNCTION__));
- memset(pbuf, 0, bufsize);
- pbuf += sprintf(pbuf, "scancount=%d\n", 0);
- sprintf(pbuf, "%s", RESULTS_END_MARKER);
- err = strlen(buf);
- goto exit;
- }
- params_batch->get_batch.buf = buf;
- params_batch->get_batch.bufsize = bufsize;
- params_batch->get_batch.reason = reason;
- params_batch->get_batch.bytes_written = 0;
- schedule_work(&_pno_state->work);
- wait_for_completion(&_pno_state->get_batch_done);
- err = params_batch->get_batch.bytes_written;
- exit:
- return err;
- }
- int
- dhd_pno_stop_for_batch(dhd_pub_t *dhd)
- {
- int err = BCME_OK;
- int mode = 0;
- int i = 0;
- dhd_pno_status_info_t *_pno_state;
- dhd_pno_params_t *_params;
- wl_pfn_bssid_t *p_pfn_bssid = NULL;
- wlc_ssid_t *p_ssid_list = NULL;
- NULL_CHECK(dhd, "dhd is NULL", err);
- NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
- _pno_state = PNO_GET_PNOSTATE(dhd);
- DHD_PNO(("%s enter\n", __FUNCTION__));
- if (!dhd_support_sta_mode(dhd)) {
- err = BCME_BADOPTION;
- goto exit;
- }
- if (!WLS_SUPPORTED(_pno_state)) {
- DHD_ERROR(("%s : wifi location service is not supported\n",
- __FUNCTION__));
- err = BCME_UNSUPPORTED;
- goto exit;
- }
- if (!(_pno_state->pno_mode & DHD_PNO_BATCH_MODE)) {
- DHD_ERROR(("%s : PNO BATCH MODE is not enabled\n", __FUNCTION__));
- goto exit;
- }
- _pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
- if (_pno_state->pno_mode & (DHD_PNO_LEGACY_MODE | DHD_PNO_HOTLIST_MODE)) {
- mode = _pno_state->pno_mode;
- dhd_pno_clean(dhd);
- _pno_state->pno_mode = mode;
- /* restart Legacy PNO if the Legacy PNO is on */
- if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
- struct dhd_pno_legacy_params *_params_legacy;
- struct dhd_pno_ssid *iter, *next;
- _params_legacy =
- &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS].params_legacy);
- p_ssid_list = kzalloc(sizeof(wlc_ssid_t) *
- _params_legacy->nssid, GFP_KERNEL);
- if (p_ssid_list == NULL) {
- DHD_ERROR(("%s : failed to allocate wlc_ssid_t array (count: %d)",
- __FUNCTION__, _params_legacy->nssid));
- err = BCME_ERROR;
- _pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
- goto exit;
- }
- i = 0;
- /* convert dhd_pno_ssid to dhd_pno_ssid */
- list_for_each_entry_safe(iter, next, &_params_legacy->ssid_list, list) {
- p_ssid_list[i].SSID_len = iter->SSID_len;
- memcpy(p_ssid_list[i].SSID, iter->SSID, p_ssid_list[i].SSID_len);
- i++;
- }
- err = dhd_pno_set_for_ssid(dhd, p_ssid_list, _params_legacy->nssid,
- _params_legacy->scan_fr, _params_legacy->pno_repeat,
- _params_legacy->pno_freq_expo_max, _params_legacy->chan_list,
- _params_legacy->nchan);
- if (err < 0) {
- _pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
- DHD_ERROR(("%s : failed to restart legacy PNO scan(err: %d)\n",
- __FUNCTION__, err));
- goto exit;
- }
- } else if (_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE) {
- struct dhd_pno_bssid *iter, *next;
- _params = &(_pno_state->pno_params_arr[INDEX_OF_HOTLIST_PARAMS]);
- p_pfn_bssid = kzalloc(sizeof(wl_pfn_bssid_t) *
- _params->params_hotlist.nbssid, GFP_KERNEL);
- if (p_pfn_bssid == NULL) {
- DHD_ERROR(("%s : failed to allocate wl_pfn_bssid_t array"
- " (count: %d)",
- __FUNCTION__, _params->params_hotlist.nbssid));
- err = BCME_ERROR;
- _pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
- goto exit;
- }
- i = 0;
- /* convert dhd_pno_bssid to wl_pfn_bssid */
- list_for_each_entry_safe(iter, next,
- &_params->params_hotlist.bssid_list, list) {
- memcpy(&p_pfn_bssid[i].macaddr, &iter->macaddr, ETHER_ADDR_LEN);
- p_pfn_bssid[i].flags = iter->flags;
- i++;
- }
- err = dhd_pno_set_for_hotlist(dhd, p_pfn_bssid, &_params->params_hotlist);
- if (err < 0) {
- _pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
- DHD_ERROR(("%s : failed to restart hotlist scan(err: %d)\n",
- __FUNCTION__, err));
- goto exit;
- }
- }
- } else {
- err = dhd_pno_clean(dhd);
- if (err < 0) {
- DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
- __FUNCTION__, err));
- goto exit;
- }
- }
- exit:
- _params = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS];
- _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_BATCH_MODE);
- if (p_ssid_list)
- kfree(p_ssid_list);
- if (p_pfn_bssid)
- kfree(p_pfn_bssid);
- return err;
- }
- int
- dhd_pno_set_for_hotlist(dhd_pub_t *dhd, wl_pfn_bssid_t *p_pfn_bssid,
- struct dhd_pno_hotlist_params *hotlist_params)
- {
- int err = BCME_OK;
- int i;
- uint16 _chan_list[WL_NUMCHANNELS];
- int rem_nchan = 0;
- int tot_nchan = 0;
- int mode = 0;
- dhd_pno_params_t *_params;
- dhd_pno_params_t *_params2;
- struct dhd_pno_bssid *_pno_bssid;
- dhd_pno_status_info_t *_pno_state;
- NULL_CHECK(dhd, "dhd is NULL", err);
- NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
- NULL_CHECK(hotlist_params, "hotlist_params is NULL", err);
- NULL_CHECK(p_pfn_bssid, "p_pfn_bssid is NULL", err);
- _pno_state = PNO_GET_PNOSTATE(dhd);
- DHD_PNO(("%s enter\n", __FUNCTION__));
- if (!dhd_support_sta_mode(dhd)) {
- err = BCME_BADOPTION;
- goto exit;
- }
- if (!WLS_SUPPORTED(_pno_state)) {
- DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
- err = BCME_UNSUPPORTED;
- goto exit;
- }
- _params = &_pno_state->pno_params_arr[INDEX_OF_HOTLIST_PARAMS];
- if (!(_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE)) {
- _pno_state->pno_mode |= DHD_PNO_HOTLIST_MODE;
- err = _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_HOTLIST_MODE);
- if (err < 0) {
- DHD_ERROR(("%s : failed to call _dhd_pno_reinitialize_prof\n",
- __FUNCTION__));
- goto exit;
- }
- }
- _params->params_batch.nchan = hotlist_params->nchan;
- _params->params_batch.scan_fr = hotlist_params->scan_fr;
- if (hotlist_params->nchan)
- memcpy(_params->params_hotlist.chan_list, hotlist_params->chan_list,
- sizeof(_params->params_hotlist.chan_list));
- memset(_chan_list, 0, sizeof(_chan_list));
- rem_nchan = ARRAYSIZE(hotlist_params->chan_list) - hotlist_params->nchan;
- if (hotlist_params->band == WLC_BAND_2G || hotlist_params->band == WLC_BAND_5G) {
- /* get a valid channel list based on band B or A */
- err = _dhd_pno_get_channels(dhd,
- &_params->params_hotlist.chan_list[hotlist_params->nchan],
- &rem_nchan, hotlist_params->band, FALSE);
- if (err < 0) {
- DHD_ERROR(("%s: failed to get valid channel list(band : %d)\n",
- __FUNCTION__, hotlist_params->band));
- goto exit;
- }
- /* now we need to update nchan because rem_chan has valid channel count */
- _params->params_hotlist.nchan += rem_nchan;
- /* need to sort channel list */
- sort(_params->params_hotlist.chan_list, _params->params_hotlist.nchan,
- sizeof(_params->params_hotlist.chan_list[0]), _dhd_pno_cmpfunc, NULL);
- }
- #ifdef PNO_DEBUG
- {
- int i;
- DHD_PNO(("Channel list : "));
- for (i = 0; i < _params->params_batch.nchan; i++) {
- DHD_PNO(("%d ", _params->params_batch.chan_list[i]));
- }
- DHD_PNO(("\n"));
- }
- #endif
- if (_params->params_hotlist.nchan) {
- /* copy the channel list into local array */
- memcpy(_chan_list, _params->params_hotlist.chan_list,
- sizeof(_chan_list));
- tot_nchan = _params->params_hotlist.nchan;
- }
- if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
- DHD_PNO(("PNO SSID is on progress in firmware\n"));
- /* store current pno_mode before disabling pno */
- mode = _pno_state->pno_mode;
- err = _dhd_pno_enable(dhd, PNO_OFF);
- if (err < 0) {
- DHD_ERROR(("%s : failed to disable PNO\n", __FUNCTION__));
- goto exit;
- }
- /* restore the previous mode */
- _pno_state->pno_mode = mode;
- /* Use the superset for channelist between two mode */
- _params2 = &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS]);
- if (_params2->params_legacy.nchan > 0 &&
- _params->params_hotlist.nchan > 0) {
- err = _dhd_pno_chan_merge(_chan_list, &tot_nchan,
- &_params2->params_legacy.chan_list[0],
- _params2->params_legacy.nchan,
- &_params->params_hotlist.chan_list[0],
- _params->params_hotlist.nchan);
- if (err < 0) {
- DHD_ERROR(("%s : failed to merge channel list"
- "between legacy and hotlist\n",
- __FUNCTION__));
- goto exit;
- }
- }
- }
- INIT_LIST_HEAD(&(_params->params_hotlist.bssid_list));
- err = _dhd_pno_add_bssid(dhd, p_pfn_bssid, hotlist_params->nbssid);
- if (err < 0) {
- DHD_ERROR(("%s : failed to call _dhd_pno_add_bssid(err :%d)\n",
- __FUNCTION__, err));
- goto exit;
- }
- if ((err = _dhd_pno_set(dhd, _params, DHD_PNO_HOTLIST_MODE)) < 0) {
- DHD_ERROR(("%s : failed to set call pno_set (err %d) in firmware\n",
- __FUNCTION__, err));
- goto exit;
- }
- if (tot_nchan > 0) {
- if ((err = _dhd_pno_cfg(dhd, _chan_list, tot_nchan)) < 0) {
- DHD_ERROR(("%s : failed to set call pno_cfg (err %d) in firmware\n",
- __FUNCTION__, err));
- goto exit;
- }
- }
- for (i = 0; i < hotlist_params->nbssid; i++) {
- _pno_bssid = kzalloc(sizeof(struct dhd_pno_bssid), GFP_KERNEL);
- NULL_CHECK(_pno_bssid, "_pfn_bssid is NULL", err);
- memcpy(&_pno_bssid->macaddr, &p_pfn_bssid[i].macaddr, ETHER_ADDR_LEN);
- _pno_bssid->flags = p_pfn_bssid[i].flags;
- list_add_tail(&_pno_bssid->list, &_params->params_hotlist.bssid_list);
- }
- _params->params_hotlist.nbssid = hotlist_params->nbssid;
- if (_pno_state->pno_status == DHD_PNO_DISABLED) {
- if ((err = _dhd_pno_enable(dhd, PNO_ON)) < 0)
- DHD_ERROR(("%s : failed to enable PNO\n", __FUNCTION__));
- }
- exit:
- /* clear mode in case of error */
- if (err < 0)
- _pno_state->pno_mode &= ~DHD_PNO_HOTLIST_MODE;
- return err;
- }
- int
- dhd_pno_stop_for_hotlist(dhd_pub_t *dhd)
- {
- int err = BCME_OK;
- uint32 mode = 0;
- int i = 0;
- dhd_pno_status_info_t *_pno_state;
- dhd_pno_params_t *_params;
- wlc_ssid_t *p_ssid_list = NULL;
- NULL_CHECK(dhd, "dhd is NULL", err);
- NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
- _pno_state = PNO_GET_PNOSTATE(dhd);
- if (!WLS_SUPPORTED(_pno_state)) {
- DHD_ERROR(("%s : wifi location service is not supported\n",
- __FUNCTION__));
- err = BCME_UNSUPPORTED;
- goto exit;
- }
- if (!(_pno_state->pno_mode & DHD_PNO_HOTLIST_MODE)) {
- DHD_ERROR(("%s : Hotlist MODE is not enabled\n",
- __FUNCTION__));
- goto exit;
- }
- _pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
- if (_pno_state->pno_mode & (DHD_PNO_LEGACY_MODE | DHD_PNO_BATCH_MODE)) {
- /* retrieve the batching data from firmware into host */
- dhd_pno_get_for_batch(dhd, NULL, 0, PNO_STATUS_DISABLE);
- /* save current pno_mode before calling dhd_pno_clean */
- mode = _pno_state->pno_mode;
- err = dhd_pno_clean(dhd);
- if (err < 0) {
- DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
- __FUNCTION__, err));
- goto exit;
- }
- /* restore previos pno mode */
- _pno_state->pno_mode = mode;
- if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
- /* restart Legacy PNO Scan */
- struct dhd_pno_legacy_params *_params_legacy;
- struct dhd_pno_ssid *iter, *next;
- _params_legacy =
- &(_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS].params_legacy);
- p_ssid_list =
- kzalloc(sizeof(wlc_ssid_t) * _params_legacy->nssid, GFP_KERNEL);
- if (p_ssid_list == NULL) {
- DHD_ERROR(("%s : failed to allocate wlc_ssid_t array (count: %d)",
- __FUNCTION__, _params_legacy->nssid));
- err = BCME_ERROR;
- _pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
- goto exit;
- }
- /* convert dhd_pno_ssid to dhd_pno_ssid */
- list_for_each_entry_safe(iter, next, &_params_legacy->ssid_list, list) {
- p_ssid_list[i].SSID_len = iter->SSID_len;
- memcpy(p_ssid_list[i].SSID, iter->SSID, p_ssid_list[i].SSID_len);
- i++;
- }
- err = dhd_pno_set_for_ssid(dhd, p_ssid_list, _params_legacy->nssid,
- _params_legacy->scan_fr, _params_legacy->pno_repeat,
- _params_legacy->pno_freq_expo_max, _params_legacy->chan_list,
- _params_legacy->nchan);
- if (err < 0) {
- _pno_state->pno_mode &= ~DHD_PNO_LEGACY_MODE;
- DHD_ERROR(("%s : failed to restart legacy PNO scan(err: %d)\n",
- __FUNCTION__, err));
- goto exit;
- }
- } else if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
- /* restart Batching Scan */
- _params = &(_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS]);
- /* restart BATCH SCAN */
- err = dhd_pno_set_for_batch(dhd, &_params->params_batch);
- if (err < 0) {
- _pno_state->pno_mode &= ~DHD_PNO_BATCH_MODE;
- DHD_ERROR(("%s : failed to restart batch scan(err: %d)\n",
- __FUNCTION__, err));
- goto exit;
- }
- }
- } else {
- err = dhd_pno_clean(dhd);
- if (err < 0) {
- DHD_ERROR(("%s : failed to call dhd_pno_clean (err: %d)\n",
- __FUNCTION__, err));
- goto exit;
- }
- }
- exit:
- if (p_ssid_list)
- kfree(p_ssid_list);
- return err;
- }
- int
- dhd_pno_event_handler(dhd_pub_t *dhd, wl_event_msg_t *event, void *event_data)
- {
- int err = BCME_OK;
- uint status, event_type, flags, datalen;
- dhd_pno_status_info_t *_pno_state;
- NULL_CHECK(dhd, "dhd is NULL", err);
- NULL_CHECK(dhd->pno_state, "pno_state is NULL", err);
- _pno_state = PNO_GET_PNOSTATE(dhd);
- if (!WLS_SUPPORTED(_pno_state)) {
- DHD_ERROR(("%s : wifi location service is not supported\n", __FUNCTION__));
- err = BCME_UNSUPPORTED;
- goto exit;
- }
- event_type = ntoh32(event->event_type);
- flags = ntoh16(event->flags);
- status = ntoh32(event->status);
- datalen = ntoh32(event->datalen);
- DHD_PNO(("%s enter : event_type :%d\n", __FUNCTION__, event_type));
- switch (event_type) {
- case WLC_E_PFN_BSSID_NET_FOUND:
- case WLC_E_PFN_BSSID_NET_LOST:
- /* TODO : need to implement event logic using generic netlink */
- break;
- case WLC_E_PFN_BEST_BATCHING:
- {
- struct dhd_pno_batch_params *params_batch;
- params_batch = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS].params_batch;
- if (!waitqueue_active(&_pno_state->get_batch_done.wait)) {
- DHD_PNO(("%s : WLC_E_PFN_BEST_BATCHING\n", __FUNCTION__));
- params_batch->get_batch.buf = NULL;
- params_batch->get_batch.bufsize = 0;
- params_batch->get_batch.reason = PNO_STATUS_EVENT;
- schedule_work(&_pno_state->work);
- } else
- DHD_PNO(("%s : WLC_E_PFN_BEST_BATCHING"
- "will skip this event\n", __FUNCTION__));
- break;
- }
- default:
- DHD_ERROR(("unknown event : %d\n", event_type));
- }
- exit:
- return err;
- }
- int dhd_pno_init(dhd_pub_t *dhd)
- {
- int err = BCME_OK;
- dhd_pno_status_info_t *_pno_state;
- NULL_CHECK(dhd, "dhd is NULL", err);
- DHD_PNO(("%s enter\n", __FUNCTION__));
- UNUSED_PARAMETER(_dhd_pno_suspend);
- if (dhd->pno_state)
- goto exit;
- dhd->pno_state = MALLOC(dhd->osh, sizeof(dhd_pno_status_info_t));
- NULL_CHECK(dhd->pno_state, "failed to create dhd_pno_state", err);
- memset(dhd->pno_state, 0, sizeof(dhd_pno_status_info_t));
- /* need to check whether current firmware support batching and hotlist scan */
- _pno_state = PNO_GET_PNOSTATE(dhd);
- _pno_state->wls_supported = TRUE;
- _pno_state->dhd = dhd;
- mutex_init(&_pno_state->pno_mutex);
- INIT_WORK(&_pno_state->work, _dhd_pno_get_batch_handler);
- init_completion(&_pno_state->get_batch_done);
- err = dhd_iovar(dhd, 0, "pfnlbest", NULL, 0, 0);
- if (err == BCME_UNSUPPORTED) {
- _pno_state->wls_supported = FALSE;
- DHD_INFO(("Current firmware doesn't support"
- " Android Location Service\n"));
- }
- exit:
- return err;
- }
- int dhd_pno_deinit(dhd_pub_t *dhd)
- {
- int err = BCME_OK;
- dhd_pno_status_info_t *_pno_state;
- dhd_pno_params_t *_params;
- NULL_CHECK(dhd, "dhd is NULL", err);
- DHD_PNO(("%s enter\n", __FUNCTION__));
- _pno_state = PNO_GET_PNOSTATE(dhd);
- NULL_CHECK(_pno_state, "pno_state is NULL", err);
- /* may need to free legacy ssid_list */
- if (_pno_state->pno_mode & DHD_PNO_LEGACY_MODE) {
- _params = &_pno_state->pno_params_arr[INDEX_OF_LEGACY_PARAMS];
- _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_LEGACY_MODE);
- }
- if (_pno_state->pno_mode & DHD_PNO_BATCH_MODE) {
- _params = &_pno_state->pno_params_arr[INDEX_OF_BATCH_PARAMS];
- /* clear resource if the BATCH MODE is on */
- _dhd_pno_reinitialize_prof(dhd, _params, DHD_PNO_BATCH_MODE);
- }
- cancel_work_sync(&_pno_state->work);
- MFREE(dhd->osh, _pno_state, sizeof(dhd_pno_status_info_t));
- dhd->pno_state = NULL;
- return err;
- }
- #endif /* PNO_SUPPORT */
|