dhd_pno.c 62 KB

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