dhd_pno.c 145 KB

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