dhd_wlfc.c 125 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546
  1. /*
  2. * DHD PROP_TXSTATUS Module.
  3. *
  4. * Portions of this code are copyright (c) 2020 Cypress Semiconductor Corporation
  5. *
  6. * Copyright (C) 1999-2020, Broadcom Corporation
  7. *
  8. * Unless you and Broadcom execute a separate written software license
  9. * agreement governing use of this software, this software is licensed to you
  10. * under the terms of the GNU General Public License version 2 (the "GPL"),
  11. * available at http://www.broadcom.com/licenses/GPLv2.php, with the
  12. * following added to such license:
  13. *
  14. * As a special exception, the copyright holders of this software give you
  15. * permission to link this software with independent modules, and to copy and
  16. * distribute the resulting executable under terms of your choice, provided that
  17. * you also meet, for each linked independent module, the terms and conditions of
  18. * the license of that module. An independent module is a module which is not
  19. * derived from this software. The special exception does not apply to any
  20. * modifications of the software.
  21. *
  22. * Notwithstanding the above, under no circumstances may you combine this
  23. * software in any way with any other Broadcom software provided under a license
  24. * other than the GPL, without Broadcom's express prior written consent.
  25. *
  26. *
  27. * <<Broadcom-WL-IPTag/Open:>>
  28. *
  29. * $Id: dhd_wlfc.c 700323 2017-05-18 16:12:11Z $
  30. *
  31. */
  32. #include <typedefs.h>
  33. #include <osl.h>
  34. #include <bcmutils.h>
  35. #include <bcmendian.h>
  36. #include <dngl_stats.h>
  37. #include <dhd.h>
  38. #include <dhd_bus.h>
  39. #include <dhd_dbg.h>
  40. #ifdef PROP_TXSTATUS /* a form of flow control between host and dongle */
  41. #include <wlfc_proto.h>
  42. #include <dhd_wlfc.h>
  43. #endif // endif
  44. #ifdef DHDTCPACK_SUPPRESS
  45. #include <dhd_ip.h>
  46. #endif /* DHDTCPACK_SUPPRESS */
  47. /*
  48. * wlfc naming and lock rules:
  49. *
  50. * 1. Private functions name like _dhd_wlfc_XXX, declared as static and avoid wlfc lock operation.
  51. * 2. Public functions name like dhd_wlfc_XXX, use wlfc lock if needed.
  52. * 3. Non-Proptxstatus module call public functions only and avoid wlfc lock operation.
  53. *
  54. */
  55. #if defined(DHD_WLFC_THREAD)
  56. #define WLFC_THREAD_QUICK_RETRY_WAIT_MS 10 /* 10 msec */
  57. #define WLFC_THREAD_RETRY_WAIT_MS 10000 /* 10 sec */
  58. #endif /* defined (DHD_WLFC_THREAD) */
  59. #ifdef PROP_TXSTATUS
  60. #define DHD_WLFC_QMON_COMPLETE(entry)
  61. /** reordering related */
  62. #if defined(DHD_WLFC_THREAD)
  63. static void
  64. _dhd_wlfc_thread_wakeup(dhd_pub_t *dhdp)
  65. {
  66. dhdp->wlfc_thread_go = TRUE;
  67. wake_up_interruptible(&dhdp->wlfc_wqhead);
  68. }
  69. #endif /* DHD_WLFC_THREAD */
  70. static uint16
  71. _dhd_wlfc_adjusted_seq(void* p, uint8 current_seq)
  72. {
  73. uint16 seq;
  74. if (!p) {
  75. return 0xffff;
  76. }
  77. seq = WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
  78. if (seq < current_seq) {
  79. /* wrap around */
  80. seq += 256;
  81. }
  82. return seq;
  83. }
  84. /**
  85. * Enqueue a caller supplied packet on a caller supplied precedence queue, optionally reorder
  86. * suppressed packets.
  87. * @param[in] pq caller supplied packet queue to enqueue the packet on
  88. * @param[in] prec precedence of the to-be-queued packet
  89. * @param[in] p transmit packet to enqueue
  90. * @param[in] qHead if TRUE, enqueue to head instead of tail. Used to maintain d11 seq order.
  91. * @param[in] current_seq
  92. * @param[in] reOrder reOrder on odd precedence (=suppress queue)
  93. */
  94. static void
  95. _dhd_wlfc_prec_enque(struct pktq *pq, int prec, void* p, bool qHead,
  96. uint8 current_seq, bool reOrder)
  97. {
  98. struct pktq_prec *q;
  99. uint16 seq, seq2;
  100. void *p2, *p2_prev;
  101. if (!p)
  102. return;
  103. ASSERT(prec >= 0 && prec < pq->num_prec);
  104. ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */
  105. ASSERT(!pktq_full(pq));
  106. ASSERT(!pktqprec_full(pq, prec));
  107. q = &pq->q[prec];
  108. if (q->head == NULL) {
  109. /* empty queue */
  110. q->head = p;
  111. q->tail = p;
  112. } else {
  113. if (reOrder && (prec & 1)) {
  114. seq = _dhd_wlfc_adjusted_seq(p, current_seq);
  115. p2 = qHead ? q->head : q->tail;
  116. seq2 = _dhd_wlfc_adjusted_seq(p2, current_seq);
  117. if ((qHead &&((seq+1) > seq2)) || (!qHead && ((seq2+1) > seq))) {
  118. /* need reorder */
  119. p2 = q->head;
  120. p2_prev = NULL;
  121. seq2 = _dhd_wlfc_adjusted_seq(p2, current_seq);
  122. while (seq > seq2) {
  123. p2_prev = p2;
  124. p2 = PKTLINK(p2);
  125. if (!p2) {
  126. break;
  127. }
  128. seq2 = _dhd_wlfc_adjusted_seq(p2, current_seq);
  129. }
  130. if (p2_prev == NULL) {
  131. /* insert head */
  132. PKTSETLINK(p, q->head);
  133. q->head = p;
  134. } else if (p2 == NULL) {
  135. /* insert tail */
  136. PKTSETLINK(p2_prev, p);
  137. q->tail = p;
  138. } else {
  139. /* insert after p2_prev */
  140. PKTSETLINK(p, PKTLINK(p2_prev));
  141. PKTSETLINK(p2_prev, p);
  142. }
  143. goto exit;
  144. }
  145. }
  146. if (qHead) {
  147. PKTSETLINK(p, q->head);
  148. q->head = p;
  149. } else {
  150. PKTSETLINK(q->tail, p);
  151. q->tail = p;
  152. }
  153. }
  154. exit:
  155. q->n_pkts++;
  156. pq->n_pkts_tot++;
  157. if (pq->hi_prec < prec)
  158. pq->hi_prec = (uint8)prec;
  159. } /* _dhd_wlfc_prec_enque */
  160. /**
  161. * Create a place to store all packet pointers submitted to the firmware until a status comes back,
  162. * suppress or otherwise.
  163. *
  164. * hang-er: noun, a contrivance on which things are hung, as a hook.
  165. */
  166. /** @deprecated soon */
  167. static void*
  168. _dhd_wlfc_hanger_create(dhd_pub_t *dhd, int max_items)
  169. {
  170. int i;
  171. wlfc_hanger_t* hanger;
  172. /* allow only up to a specific size for now */
  173. ASSERT(max_items == WLFC_HANGER_MAXITEMS);
  174. if ((hanger = (wlfc_hanger_t*)DHD_OS_PREALLOC(dhd, DHD_PREALLOC_DHD_WLFC_HANGER,
  175. WLFC_HANGER_SIZE(max_items))) == NULL) {
  176. return NULL;
  177. }
  178. memset(hanger, 0, WLFC_HANGER_SIZE(max_items));
  179. hanger->max_items = max_items;
  180. for (i = 0; i < hanger->max_items; i++) {
  181. hanger->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
  182. }
  183. return hanger;
  184. }
  185. /** @deprecated soon */
  186. static int
  187. _dhd_wlfc_hanger_delete(dhd_pub_t *dhd, void* hanger)
  188. {
  189. wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
  190. if (h) {
  191. DHD_OS_PREFREE(dhd, h, WLFC_HANGER_SIZE(h->max_items));
  192. return BCME_OK;
  193. }
  194. return BCME_BADARG;
  195. }
  196. /** @deprecated soon */
  197. static uint16
  198. _dhd_wlfc_hanger_get_free_slot(void* hanger)
  199. {
  200. uint32 i;
  201. wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
  202. if (h) {
  203. i = h->slot_pos + 1;
  204. if (i == h->max_items) {
  205. i = 0;
  206. }
  207. while (i != h->slot_pos) {
  208. if (h->items[i].state == WLFC_HANGER_ITEM_STATE_FREE) {
  209. h->slot_pos = i;
  210. return (uint16)i;
  211. }
  212. i++;
  213. if (i == h->max_items)
  214. i = 0;
  215. }
  216. h->failed_slotfind++;
  217. }
  218. return WLFC_HANGER_MAXITEMS;
  219. }
  220. /** @deprecated soon */
  221. static int
  222. _dhd_wlfc_hanger_get_genbit(void* hanger, void* pkt, uint32 slot_id, int* gen)
  223. {
  224. int rc = BCME_OK;
  225. wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
  226. *gen = 0xff;
  227. /* this packet was not pushed at the time it went to the firmware */
  228. if (slot_id == WLFC_HANGER_MAXITEMS)
  229. return BCME_NOTFOUND;
  230. if (h) {
  231. if (h->items[slot_id].state != WLFC_HANGER_ITEM_STATE_FREE) {
  232. *gen = h->items[slot_id].gen;
  233. }
  234. else {
  235. DHD_ERROR(("Error: %s():%d item not used\n",
  236. __FUNCTION__, __LINE__));
  237. rc = BCME_NOTFOUND;
  238. }
  239. } else {
  240. rc = BCME_BADARG;
  241. }
  242. return rc;
  243. }
  244. /** @deprecated soon */
  245. static int
  246. _dhd_wlfc_hanger_pushpkt(void* hanger, void* pkt, uint32 slot_id)
  247. {
  248. int rc = BCME_OK;
  249. wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
  250. if (h && (slot_id < WLFC_HANGER_MAXITEMS)) {
  251. if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_FREE) {
  252. h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE;
  253. h->items[slot_id].pkt = pkt;
  254. h->items[slot_id].pkt_state = 0;
  255. h->items[slot_id].pkt_txstatus = 0;
  256. h->pushed++;
  257. } else {
  258. h->failed_to_push++;
  259. rc = BCME_NOTFOUND;
  260. }
  261. } else {
  262. rc = BCME_BADARG;
  263. }
  264. return rc;
  265. }
  266. /** @deprecated soon */
  267. static int
  268. _dhd_wlfc_hanger_poppkt(void* hanger, uint32 slot_id, void** pktout, bool remove_from_hanger)
  269. {
  270. int rc = BCME_OK;
  271. wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
  272. *pktout = NULL;
  273. /* this packet was not pushed at the time it went to the firmware */
  274. if (slot_id == WLFC_HANGER_MAXITEMS)
  275. return BCME_NOTFOUND;
  276. if (h) {
  277. if (h->items[slot_id].state != WLFC_HANGER_ITEM_STATE_FREE) {
  278. *pktout = h->items[slot_id].pkt;
  279. if (remove_from_hanger) {
  280. h->items[slot_id].state =
  281. WLFC_HANGER_ITEM_STATE_FREE;
  282. h->items[slot_id].pkt = NULL;
  283. h->items[slot_id].gen = 0xff;
  284. h->items[slot_id].identifier = 0;
  285. h->popped++;
  286. }
  287. } else {
  288. h->failed_to_pop++;
  289. rc = BCME_NOTFOUND;
  290. }
  291. } else {
  292. rc = BCME_BADARG;
  293. }
  294. return rc;
  295. }
  296. /** @deprecated soon */
  297. static int
  298. _dhd_wlfc_hanger_mark_suppressed(void* hanger, uint32 slot_id, uint8 gen)
  299. {
  300. int rc = BCME_OK;
  301. wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
  302. /* this packet was not pushed at the time it went to the firmware */
  303. if (slot_id == WLFC_HANGER_MAXITEMS)
  304. return BCME_NOTFOUND;
  305. if (h) {
  306. h->items[slot_id].gen = gen;
  307. if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE) {
  308. h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED;
  309. } else {
  310. rc = BCME_BADARG;
  311. }
  312. } else {
  313. rc = BCME_BADARG;
  314. }
  315. return rc;
  316. }
  317. /** remove reference of specific packet in hanger */
  318. /** @deprecated soon */
  319. static bool
  320. _dhd_wlfc_hanger_remove_reference(wlfc_hanger_t* h, void* pkt)
  321. {
  322. int i;
  323. if (!h || !pkt) {
  324. return FALSE;
  325. }
  326. i = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(pkt)));
  327. if ((i < h->max_items) && (pkt == h->items[i].pkt)) {
  328. if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
  329. h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
  330. h->items[i].pkt = NULL;
  331. h->items[i].gen = 0xff;
  332. h->items[i].identifier = 0;
  333. return TRUE;
  334. } else {
  335. DHD_ERROR(("Error: %s():%d item not suppressed\n",
  336. __FUNCTION__, __LINE__));
  337. }
  338. }
  339. return FALSE;
  340. }
  341. /** afq = At Firmware Queue, queue containing packets pending in the dongle */
  342. static int
  343. _dhd_wlfc_enque_afq(athost_wl_status_info_t* ctx, void *p)
  344. {
  345. wlfc_mac_descriptor_t* entry;
  346. uint16 entry_idx = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
  347. uint8 prec = DHD_PKTTAG_FIFO(PKTTAG(p));
  348. if (entry_idx < WLFC_MAC_DESC_TABLE_SIZE)
  349. entry = &ctx->destination_entries.nodes[entry_idx];
  350. else if (entry_idx < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM))
  351. entry = &ctx->destination_entries.interfaces[entry_idx - WLFC_MAC_DESC_TABLE_SIZE];
  352. else
  353. entry = &ctx->destination_entries.other;
  354. pktq_penq(&entry->afq, prec, p);
  355. return BCME_OK;
  356. }
  357. /** afq = At Firmware Queue, queue containing packets pending in the dongle */
  358. static int
  359. _dhd_wlfc_deque_afq(athost_wl_status_info_t* ctx, uint16 hslot, uint8 hcnt, uint8 prec,
  360. void **pktout)
  361. {
  362. wlfc_mac_descriptor_t *entry;
  363. struct pktq *pq;
  364. struct pktq_prec *q;
  365. void *p, *b;
  366. if (!ctx) {
  367. DHD_ERROR(("%s: ctx(%p), pktout(%p)\n", __FUNCTION__, ctx, pktout));
  368. return BCME_BADARG;
  369. }
  370. if (pktout) {
  371. *pktout = NULL;
  372. }
  373. ASSERT(hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM + 1));
  374. if (hslot < WLFC_MAC_DESC_TABLE_SIZE)
  375. entry = &ctx->destination_entries.nodes[hslot];
  376. else if (hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM))
  377. entry = &ctx->destination_entries.interfaces[hslot - WLFC_MAC_DESC_TABLE_SIZE];
  378. else
  379. entry = &ctx->destination_entries.other;
  380. pq = &entry->afq;
  381. ASSERT(prec < pq->num_prec);
  382. q = &pq->q[prec];
  383. b = NULL;
  384. p = q->head;
  385. while (p && (hcnt != WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p)))))
  386. {
  387. b = p;
  388. p = PKTLINK(p);
  389. }
  390. if (p == NULL) {
  391. /* none is matched */
  392. if (b) {
  393. DHD_ERROR(("%s: can't find matching seq(%d)\n", __FUNCTION__, hcnt));
  394. } else {
  395. DHD_ERROR(("%s: queue is empty\n", __FUNCTION__));
  396. }
  397. return BCME_ERROR;
  398. }
  399. bcm_pkt_validate_chk(p);
  400. if (!b) {
  401. /* head packet is matched */
  402. if ((q->head = PKTLINK(p)) == NULL) {
  403. q->tail = NULL;
  404. }
  405. } else {
  406. /* middle packet is matched */
  407. DHD_INFO(("%s: out of order, seq(%d), head_seq(%d)\n", __FUNCTION__, hcnt,
  408. WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(q->head)))));
  409. ctx->stats.ooo_pkts[prec]++;
  410. PKTSETLINK(b, PKTLINK(p));
  411. if (PKTLINK(p) == NULL) {
  412. q->tail = b;
  413. }
  414. }
  415. q->n_pkts--;
  416. pq->n_pkts_tot--;
  417. #ifdef WL_TXQ_STALL
  418. q->dequeue_count++;
  419. #endif // endif
  420. PKTSETLINK(p, NULL);
  421. if (pktout) {
  422. *pktout = p;
  423. }
  424. return BCME_OK;
  425. } /* _dhd_wlfc_deque_afq */
  426. /**
  427. * Flow control information piggy backs on packets, in the form of one or more TLVs. This function
  428. * pushes one or more TLVs onto a packet that is going to be sent towards the dongle.
  429. *
  430. * @param[in] ctx
  431. * @param[in/out] packet
  432. * @param[in] tim_signal TRUE if parameter 'tim_bmp' is valid
  433. * @param[in] tim_bmp
  434. * @param[in] mac_handle
  435. * @param[in] htodtag
  436. * @param[in] htodseq d11 seqno for seqno reuse, only used if 'seq reuse' was agreed upon
  437. * earlier between host and firmware.
  438. * @param[in] skip_wlfc_hdr
  439. */
  440. static int
  441. _dhd_wlfc_pushheader(athost_wl_status_info_t* ctx, void** packet, bool tim_signal,
  442. uint8 tim_bmp, uint8 mac_handle, uint32 htodtag, uint16 htodseq, bool skip_wlfc_hdr)
  443. {
  444. uint32 wl_pktinfo = 0;
  445. uint8* wlh;
  446. uint8 dataOffset = 0;
  447. uint8 fillers;
  448. uint8 tim_signal_len = 0;
  449. dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
  450. struct bdc_header *h;
  451. void *p = *packet;
  452. if (skip_wlfc_hdr)
  453. goto push_bdc_hdr;
  454. if (tim_signal) {
  455. tim_signal_len = TLV_HDR_LEN + WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP;
  456. }
  457. /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
  458. dataOffset = WLFC_CTL_VALUE_LEN_PKTTAG + TLV_HDR_LEN + tim_signal_len;
  459. if (WLFC_GET_REUSESEQ(dhdp->wlfc_mode)) {
  460. dataOffset += WLFC_CTL_VALUE_LEN_SEQ;
  461. }
  462. fillers = ROUNDUP(dataOffset, 4) - dataOffset;
  463. dataOffset += fillers;
  464. PKTPUSH(ctx->osh, p, dataOffset);
  465. wlh = (uint8*) PKTDATA(ctx->osh, p);
  466. wl_pktinfo = htol32(htodtag);
  467. wlh[TLV_TAG_OFF] = WLFC_CTL_TYPE_PKTTAG;
  468. wlh[TLV_LEN_OFF] = WLFC_CTL_VALUE_LEN_PKTTAG;
  469. memcpy(&wlh[TLV_HDR_LEN] /* dst */, &wl_pktinfo, sizeof(uint32));
  470. if (WLFC_GET_REUSESEQ(dhdp->wlfc_mode)) {
  471. uint16 wl_seqinfo = htol16(htodseq);
  472. wlh[TLV_LEN_OFF] += WLFC_CTL_VALUE_LEN_SEQ;
  473. memcpy(&wlh[TLV_HDR_LEN + WLFC_CTL_VALUE_LEN_PKTTAG], &wl_seqinfo,
  474. WLFC_CTL_VALUE_LEN_SEQ);
  475. }
  476. if (tim_signal_len) {
  477. wlh[dataOffset - fillers - tim_signal_len ] =
  478. WLFC_CTL_TYPE_PENDING_TRAFFIC_BMP;
  479. wlh[dataOffset - fillers - tim_signal_len + 1] =
  480. WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP;
  481. wlh[dataOffset - fillers - tim_signal_len + 2] = mac_handle;
  482. wlh[dataOffset - fillers - tim_signal_len + 3] = tim_bmp;
  483. }
  484. if (fillers)
  485. memset(&wlh[dataOffset - fillers], WLFC_CTL_TYPE_FILLER, fillers);
  486. push_bdc_hdr:
  487. PKTPUSH(ctx->osh, p, BDC_HEADER_LEN);
  488. h = (struct bdc_header *)PKTDATA(ctx->osh, p);
  489. h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
  490. if (PKTSUMNEEDED(p))
  491. h->flags |= BDC_FLAG_SUM_NEEDED;
  492. h->priority = (PKTPRIO(p) & BDC_PRIORITY_MASK);
  493. h->flags2 = 0;
  494. h->dataOffset = dataOffset >> 2;
  495. BDC_SET_IF_IDX(h, DHD_PKTTAG_IF(PKTTAG(p)));
  496. *packet = p;
  497. return BCME_OK;
  498. } /* _dhd_wlfc_pushheader */
  499. /**
  500. * Removes (PULLs) flow control related headers from the caller supplied packet, is invoked eg
  501. * when a packet is about to be freed.
  502. */
  503. static int
  504. _dhd_wlfc_pullheader(athost_wl_status_info_t* ctx, void* pktbuf)
  505. {
  506. struct bdc_header *h;
  507. if (PKTLEN(ctx->osh, pktbuf) < BDC_HEADER_LEN) {
  508. DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
  509. PKTLEN(ctx->osh, pktbuf), BDC_HEADER_LEN));
  510. return BCME_ERROR;
  511. }
  512. h = (struct bdc_header *)PKTDATA(ctx->osh, pktbuf);
  513. /* pull BDC header */
  514. PKTPULL(ctx->osh, pktbuf, BDC_HEADER_LEN);
  515. if (PKTLEN(ctx->osh, pktbuf) < (uint)(h->dataOffset << 2)) {
  516. DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
  517. PKTLEN(ctx->osh, pktbuf), (h->dataOffset << 2)));
  518. return BCME_ERROR;
  519. }
  520. /* pull wl-header */
  521. PKTPULL(ctx->osh, pktbuf, (h->dataOffset << 2));
  522. return BCME_OK;
  523. }
  524. /**
  525. * @param[in/out] p packet
  526. */
  527. static wlfc_mac_descriptor_t*
  528. _dhd_wlfc_find_table_entry(athost_wl_status_info_t* ctx, void* p)
  529. {
  530. int i;
  531. wlfc_mac_descriptor_t* table = ctx->destination_entries.nodes;
  532. uint8 ifid = DHD_PKTTAG_IF(PKTTAG(p));
  533. uint8* dstn = DHD_PKTTAG_DSTN(PKTTAG(p));
  534. wlfc_mac_descriptor_t* entry = DHD_PKTTAG_ENTRY(PKTTAG(p));
  535. int iftype = ctx->destination_entries.interfaces[ifid].iftype;
  536. /* saved one exists, return it */
  537. if (entry)
  538. return entry;
  539. /* Multicast destination, STA and P2P clients get the interface entry.
  540. * STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations
  541. * have their own entry.
  542. */
  543. if ((iftype == WLC_E_IF_ROLE_STA || ETHER_ISMULTI(dstn) ||
  544. iftype == WLC_E_IF_ROLE_P2P_CLIENT) &&
  545. (ctx->destination_entries.interfaces[ifid].occupied)) {
  546. entry = &ctx->destination_entries.interfaces[ifid];
  547. }
  548. if (entry && ETHER_ISMULTI(dstn)) {
  549. DHD_PKTTAG_SET_ENTRY(PKTTAG(p), entry);
  550. return entry;
  551. }
  552. for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
  553. if (table[i].occupied) {
  554. if (table[i].interface_id == ifid) {
  555. if (!memcmp(table[i].ea, dstn, ETHER_ADDR_LEN)) {
  556. entry = &table[i];
  557. break;
  558. }
  559. }
  560. }
  561. }
  562. if (entry == NULL)
  563. entry = &ctx->destination_entries.other;
  564. DHD_PKTTAG_SET_ENTRY(PKTTAG(p), entry);
  565. return entry;
  566. } /* _dhd_wlfc_find_table_entry */
  567. /**
  568. * In case a packet must be dropped (because eg the queues are full), various tallies have to be
  569. * be updated. Called from several other functions.
  570. * @param[in] dhdp pointer to public DHD structure
  571. * @param[in] prec precedence of the packet
  572. * @param[in] p the packet to be dropped
  573. * @param[in] bPktInQ TRUE if packet is part of a queue
  574. */
  575. static int
  576. _dhd_wlfc_prec_drop(dhd_pub_t *dhdp, int prec, void* p, bool bPktInQ)
  577. {
  578. athost_wl_status_info_t* ctx;
  579. void *pout = NULL;
  580. ASSERT(dhdp && p);
  581. if (prec < 0 || prec >= WLFC_PSQ_PREC_COUNT) {
  582. ASSERT(0);
  583. return BCME_BADARG;
  584. }
  585. ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
  586. if (!WLFC_GET_AFQ(dhdp->wlfc_mode) && (prec & 1)) {
  587. /* suppressed queue, need pop from hanger */
  588. _dhd_wlfc_hanger_poppkt(ctx->hanger, WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG
  589. (PKTTAG(p))), &pout, TRUE);
  590. ASSERT(p == pout);
  591. }
  592. if (!(prec & 1)) {
  593. #ifdef DHDTCPACK_SUPPRESS
  594. /* pkt in delayed q, so fake push BDC header for
  595. * dhd_tcpack_check_xmit() and dhd_txcomplete().
  596. */
  597. _dhd_wlfc_pushheader(ctx, &p, FALSE, 0, 0, 0, 0, TRUE);
  598. /* This packet is about to be freed, so remove it from tcp_ack_info_tbl
  599. * This must be one of...
  600. * 1. A pkt already in delayQ is evicted by another pkt with higher precedence
  601. * in _dhd_wlfc_prec_enq_with_drop()
  602. * 2. A pkt could not be enqueued to delayQ because it is full,
  603. * in _dhd_wlfc_enque_delayq().
  604. * 3. A pkt could not be enqueued to delayQ because it is full,
  605. * in _dhd_wlfc_rollback_packet_toq().
  606. */
  607. if (dhd_tcpack_check_xmit(dhdp, p) == BCME_ERROR) {
  608. DHD_ERROR(("%s %d: tcpack_suppress ERROR!!!"
  609. " Stop using it\n",
  610. __FUNCTION__, __LINE__));
  611. dhd_tcpack_suppress_set(dhdp, TCPACK_SUP_OFF);
  612. }
  613. #endif /* DHDTCPACK_SUPPRESS */
  614. }
  615. if (bPktInQ) {
  616. ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec>>1]--;
  617. ctx->pkt_cnt_per_ac[prec>>1]--;
  618. ctx->pkt_cnt_in_psq--;
  619. }
  620. ctx->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(p))][DHD_PKTTAG_FIFO(PKTTAG(p))]--;
  621. ctx->stats.pktout++;
  622. ctx->stats.drop_pkts[prec]++;
  623. dhd_txcomplete(dhdp, p, FALSE);
  624. PKTFREE(ctx->osh, p, TRUE);
  625. return 0;
  626. } /* _dhd_wlfc_prec_drop */
  627. /**
  628. * Called when eg the host handed a new packet over to the driver, or when the dongle reported
  629. * that a packet could currently not be transmitted (=suppressed). This function enqueues a transmit
  630. * packet in the host driver to be (re)transmitted at a later opportunity.
  631. * @param[in] dhdp pointer to public DHD structure
  632. * @param[in] qHead When TRUE, queue packet at head instead of tail, to preserve d11 sequence
  633. */
  634. static bool
  635. _dhd_wlfc_prec_enq_with_drop(dhd_pub_t *dhdp, struct pktq *pq, void *pkt, int prec, bool qHead,
  636. uint8 current_seq)
  637. {
  638. void *p = NULL;
  639. int eprec = -1; /* precedence to evict from */
  640. athost_wl_status_info_t* ctx;
  641. ASSERT(dhdp && pq && pkt);
  642. ASSERT(prec >= 0 && prec < pq->num_prec);
  643. ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
  644. /* Fast case, precedence queue is not full and we are also not
  645. * exceeding total queue length
  646. */
  647. if (!pktqprec_full(pq, prec) && !pktq_full(pq)) {
  648. goto exit;
  649. }
  650. /* Determine precedence from which to evict packet, if any */
  651. if (pktqprec_full(pq, prec)) {
  652. eprec = prec;
  653. } else if (pktq_full(pq)) {
  654. p = pktq_peek_tail(pq, &eprec);
  655. if (!p) {
  656. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  657. return FALSE;
  658. }
  659. if ((eprec > prec) || (eprec < 0)) {
  660. if (!pktqprec_empty(pq, prec)) {
  661. eprec = prec;
  662. } else {
  663. return FALSE;
  664. }
  665. }
  666. }
  667. /* Evict if needed */
  668. if (eprec >= 0) {
  669. /* Detect queueing to unconfigured precedence */
  670. ASSERT(!pktqprec_empty(pq, eprec));
  671. /* Evict all fragmented frames */
  672. dhd_prec_drop_pkts(dhdp, pq, eprec, _dhd_wlfc_prec_drop);
  673. }
  674. exit:
  675. /* Enqueue */
  676. _dhd_wlfc_prec_enque(pq, prec, pkt, qHead, current_seq,
  677. WLFC_GET_REORDERSUPP(dhdp->wlfc_mode));
  678. ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(pkt))][prec>>1]++;
  679. ctx->pkt_cnt_per_ac[prec>>1]++;
  680. ctx->pkt_cnt_in_psq++;
  681. return TRUE;
  682. } /* _dhd_wlfc_prec_enq_with_drop */
  683. /**
  684. * Called during eg the 'committing' of a transmit packet from the OS layer to a lower layer, in
  685. * the event that this 'commit' failed.
  686. */
  687. static int
  688. _dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t* ctx,
  689. void* p, ewlfc_packet_state_t pkt_type, uint32 hslot)
  690. {
  691. /*
  692. * put the packet back to the head of queue
  693. * - suppressed packet goes back to suppress sub-queue
  694. * - pull out the header, if new or delayed packet
  695. *
  696. * Note: hslot is used only when header removal is done.
  697. */
  698. wlfc_mac_descriptor_t* entry;
  699. int rc = BCME_OK;
  700. int prec, fifo_id;
  701. entry = _dhd_wlfc_find_table_entry(ctx, p);
  702. prec = DHD_PKTTAG_FIFO(PKTTAG(p));
  703. fifo_id = prec << 1;
  704. if (pkt_type == eWLFC_PKTTYPE_SUPPRESSED)
  705. fifo_id += 1;
  706. if (entry != NULL) {
  707. /*
  708. if this packet did not count against FIFO credit, it must have
  709. taken a requested_credit from the firmware (for pspoll etc.)
  710. */
  711. if ((prec != AC_COUNT) && !DHD_PKTTAG_CREDITCHECK(PKTTAG(p)))
  712. entry->requested_credit++;
  713. if (pkt_type == eWLFC_PKTTYPE_DELAYED) {
  714. /* decrement sequence count */
  715. WLFC_DECR_SEQCOUNT(entry, prec);
  716. /* remove header first */
  717. rc = _dhd_wlfc_pullheader(ctx, p);
  718. if (rc != BCME_OK) {
  719. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  720. goto exit;
  721. }
  722. }
  723. if (_dhd_wlfc_prec_enq_with_drop(ctx->dhdp, &entry->psq, p, fifo_id, TRUE,
  724. WLFC_SEQCOUNT(entry, fifo_id>>1))
  725. == FALSE) {
  726. /* enque failed */
  727. DHD_ERROR(("Error: %s():%d, fifo_id(%d)\n",
  728. __FUNCTION__, __LINE__, fifo_id));
  729. rc = BCME_ERROR;
  730. }
  731. } else {
  732. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  733. rc = BCME_ERROR;
  734. }
  735. exit:
  736. if (rc != BCME_OK) {
  737. ctx->stats.rollback_failed++;
  738. _dhd_wlfc_prec_drop(ctx->dhdp, fifo_id, p, FALSE);
  739. } else {
  740. ctx->stats.rollback++;
  741. }
  742. return rc;
  743. } /* _dhd_wlfc_rollback_packet_toq */
  744. /** Returns TRUE if host OS -> DHD flow control is allowed on the caller supplied interface */
  745. static bool
  746. _dhd_wlfc_allow_fc(athost_wl_status_info_t* ctx, uint8 ifid)
  747. {
  748. int prec, ac_traffic = WLFC_NO_TRAFFIC;
  749. for (prec = 0; prec < AC_COUNT; prec++) {
  750. if (ctx->pkt_cnt_in_drv[ifid][prec] > 0) {
  751. if (ac_traffic == WLFC_NO_TRAFFIC)
  752. ac_traffic = prec + 1;
  753. else if (ac_traffic != (prec + 1))
  754. ac_traffic = WLFC_MULTI_TRAFFIC;
  755. }
  756. }
  757. if (ac_traffic >= 1 && ac_traffic <= AC_COUNT) {
  758. /* single AC (BE/BK/VI/VO) in queue */
  759. if (ctx->allow_fc) {
  760. return TRUE;
  761. } else {
  762. uint32 delta;
  763. uint32 curr_t = OSL_SYSUPTIME();
  764. if (ctx->fc_defer_timestamp == 0) {
  765. /* first single ac scenario */
  766. ctx->fc_defer_timestamp = curr_t;
  767. return FALSE;
  768. }
  769. /* single AC duration, this handles wrap around, e.g. 1 - ~0 = 2. */
  770. delta = curr_t - ctx->fc_defer_timestamp;
  771. if (delta >= WLFC_FC_DEFER_PERIOD_MS) {
  772. ctx->allow_fc = TRUE;
  773. }
  774. }
  775. } else {
  776. /* multiple ACs or BCMC in queue */
  777. ctx->allow_fc = FALSE;
  778. ctx->fc_defer_timestamp = 0;
  779. }
  780. return ctx->allow_fc;
  781. } /* _dhd_wlfc_allow_fc */
  782. /**
  783. * Starts or stops the flow of transmit packets from the host OS towards the DHD, depending on
  784. * low/high watermarks.
  785. */
  786. static void
  787. _dhd_wlfc_flow_control_check(athost_wl_status_info_t* ctx, struct pktq* pq, uint8 if_id)
  788. {
  789. dhd_pub_t *dhdp;
  790. ASSERT(ctx);
  791. dhdp = (dhd_pub_t *)ctx->dhdp;
  792. ASSERT(dhdp);
  793. if (dhdp->skip_fc && dhdp->skip_fc((void *)dhdp, if_id))
  794. return;
  795. if ((ctx->hostif_flow_state[if_id] == OFF) && !_dhd_wlfc_allow_fc(ctx, if_id))
  796. return;
  797. if ((pq->n_pkts_tot <= WLFC_FLOWCONTROL_LOWATER) && (ctx->hostif_flow_state[if_id] == ON)) {
  798. /* start traffic */
  799. ctx->hostif_flow_state[if_id] = OFF;
  800. /*
  801. WLFC_DBGMESG(("qlen:%02d, if:%02d, ->OFF, start traffic %s()\n",
  802. pq->n_pkts_tot, if_id, __FUNCTION__));
  803. */
  804. WLFC_DBGMESG(("F"));
  805. dhd_txflowcontrol(dhdp, if_id, OFF);
  806. ctx->toggle_host_if = 0;
  807. }
  808. if (pq->n_pkts_tot >= WLFC_FLOWCONTROL_HIWATER && ctx->hostif_flow_state[if_id] == OFF) {
  809. /* stop traffic */
  810. ctx->hostif_flow_state[if_id] = ON;
  811. /*
  812. WLFC_DBGMESG(("qlen:%02d, if:%02d, ->ON, stop traffic %s()\n",
  813. pq->n_pkts_tot, if_id, __FUNCTION__));
  814. */
  815. WLFC_DBGMESG(("N"));
  816. dhd_txflowcontrol(dhdp, if_id, ON);
  817. ctx->host_ifidx = if_id;
  818. ctx->toggle_host_if = 1;
  819. }
  820. return;
  821. } /* _dhd_wlfc_flow_control_check */
  822. static int
  823. _dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
  824. uint8 ta_bmp)
  825. {
  826. int rc = BCME_OK;
  827. void* p = NULL;
  828. int dummylen = ((dhd_pub_t *)ctx->dhdp)->hdrlen+ 16;
  829. dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
  830. if (dhdp->proptxstatus_txoff) {
  831. rc = BCME_NORESOURCE;
  832. return rc;
  833. }
  834. /* allocate a dummy packet */
  835. p = PKTGET(ctx->osh, dummylen, TRUE);
  836. if (p) {
  837. PKTPULL(ctx->osh, p, dummylen);
  838. DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), 0);
  839. _dhd_wlfc_pushheader(ctx, &p, TRUE, ta_bmp, entry->mac_handle, 0, 0, FALSE);
  840. DHD_PKTTAG_SETSIGNALONLY(PKTTAG(p), 1);
  841. DHD_PKTTAG_WLFCPKT_SET(PKTTAG(p), 1);
  842. #ifdef PROP_TXSTATUS_DEBUG
  843. ctx->stats.signal_only_pkts_sent++;
  844. #endif // endif
  845. #if defined(BCMPCIE)
  846. rc = dhd_bus_txdata(dhdp->bus, p, ctx->host_ifidx);
  847. #else
  848. rc = dhd_bus_txdata(dhdp->bus, p);
  849. #endif // endif
  850. if (rc != BCME_OK) {
  851. _dhd_wlfc_pullheader(ctx, p);
  852. PKTFREE(ctx->osh, p, TRUE);
  853. }
  854. } else {
  855. DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n",
  856. __FUNCTION__, dummylen));
  857. rc = BCME_NOMEM;
  858. dhdp->tx_pktgetfail++;
  859. }
  860. return rc;
  861. } /* _dhd_wlfc_send_signalonly_packet */
  862. /**
  863. * Called on eg receiving 'mac close' indication from dongle. Updates the per-MAC administration
  864. * maintained in caller supplied parameter 'entry'.
  865. *
  866. * @param[in/out] entry administration about a remote MAC entity
  867. * @param[in] prec precedence queue for this remote MAC entitity
  868. *
  869. * Return value: TRUE if traffic availability changed
  870. */
  871. static bool
  872. _dhd_wlfc_traffic_pending_check(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
  873. int prec)
  874. {
  875. bool rc = FALSE;
  876. if (entry->state == WLFC_STATE_CLOSE) {
  877. if ((pktqprec_n_pkts(&entry->psq, (prec << 1)) == 0) &&
  878. (pktqprec_n_pkts(&entry->psq, ((prec << 1) + 1)) == 0)) {
  879. /* no packets in both 'normal' and 'suspended' queues */
  880. if (entry->traffic_pending_bmp & NBITVAL(prec)) {
  881. rc = TRUE;
  882. entry->traffic_pending_bmp =
  883. entry->traffic_pending_bmp & ~ NBITVAL(prec);
  884. }
  885. } else {
  886. /* packets are queued in host for transmission to dongle */
  887. if (!(entry->traffic_pending_bmp & NBITVAL(prec))) {
  888. rc = TRUE;
  889. entry->traffic_pending_bmp =
  890. entry->traffic_pending_bmp | NBITVAL(prec);
  891. }
  892. }
  893. }
  894. if (rc) {
  895. /* request a TIM update to firmware at the next piggyback opportunity */
  896. if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) {
  897. entry->send_tim_signal = 1;
  898. _dhd_wlfc_send_signalonly_packet(ctx, entry, entry->traffic_pending_bmp);
  899. entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
  900. entry->send_tim_signal = 0;
  901. } else {
  902. rc = FALSE;
  903. }
  904. }
  905. return rc;
  906. } /* _dhd_wlfc_traffic_pending_check */
  907. /**
  908. * Called on receiving a 'd11 suppressed' or 'wl suppressed' tx status from the firmware. Enqueues
  909. * the packet to transmit to firmware again at a later opportunity.
  910. */
  911. static int
  912. _dhd_wlfc_enque_suppressed(athost_wl_status_info_t* ctx, int prec, void* p)
  913. {
  914. wlfc_mac_descriptor_t* entry;
  915. entry = _dhd_wlfc_find_table_entry(ctx, p);
  916. if (entry == NULL) {
  917. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  918. return BCME_NOTFOUND;
  919. }
  920. /*
  921. - suppressed packets go to sub_queue[2*prec + 1] AND
  922. - delayed packets go to sub_queue[2*prec + 0] to ensure
  923. order of delivery.
  924. */
  925. if (_dhd_wlfc_prec_enq_with_drop(ctx->dhdp, &entry->psq, p, ((prec << 1) + 1), FALSE,
  926. WLFC_SEQCOUNT(entry, prec))
  927. == FALSE) {
  928. ctx->stats.delayq_full_error++;
  929. /* WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); */
  930. WLFC_DBGMESG(("s"));
  931. return BCME_ERROR;
  932. }
  933. /* A packet has been pushed, update traffic availability bitmap, if applicable */
  934. _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
  935. _dhd_wlfc_flow_control_check(ctx, &entry->psq, DHD_PKTTAG_IF(PKTTAG(p)));
  936. return BCME_OK;
  937. }
  938. /**
  939. * Called when a transmit packet is about to be 'committed' from the OS layer to a lower layer
  940. * towards the dongle (eg the DBUS layer). Updates wlfc administration. May modify packet.
  941. *
  942. * @param[in/out] ctx driver specific flow control administration
  943. * @param[in/out] entry The remote MAC entity for which the packet is destined.
  944. * @param[in/out] packet Packet to send. This function optionally adds TLVs to the packet.
  945. * @param[in] header_needed True if packet is 'new' to flow control
  946. * @param[out] slot Handle to container in which the packet was 'parked'
  947. */
  948. static int
  949. _dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t* ctx,
  950. wlfc_mac_descriptor_t* entry, void** packet, int header_needed, uint32* slot)
  951. {
  952. int rc = BCME_OK;
  953. int hslot = WLFC_HANGER_MAXITEMS;
  954. bool send_tim_update = FALSE;
  955. uint32 htod = 0;
  956. uint16 htodseq = 0;
  957. uint8 free_ctr;
  958. int gen = 0xff;
  959. dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
  960. void * p = *packet;
  961. *slot = hslot;
  962. if (entry == NULL) {
  963. entry = _dhd_wlfc_find_table_entry(ctx, p);
  964. }
  965. if (entry == NULL) {
  966. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  967. return BCME_ERROR;
  968. }
  969. if (entry->send_tim_signal) {
  970. /* sends a traffic indication bitmap to the dongle */
  971. send_tim_update = TRUE;
  972. entry->send_tim_signal = 0;
  973. entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
  974. }
  975. if (header_needed) {
  976. if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
  977. hslot = (uint)(entry - &ctx->destination_entries.nodes[0]);
  978. } else {
  979. hslot = _dhd_wlfc_hanger_get_free_slot(ctx->hanger);
  980. }
  981. gen = entry->generation;
  982. free_ctr = WLFC_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
  983. } else {
  984. if (WLFC_GET_REUSESEQ(dhdp->wlfc_mode)) {
  985. htodseq = DHD_PKTTAG_H2DSEQ(PKTTAG(p));
  986. }
  987. hslot = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
  988. if (WLFC_GET_REORDERSUPP(dhdp->wlfc_mode)) {
  989. gen = entry->generation;
  990. } else if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
  991. gen = WL_TXSTATUS_GET_GENERATION(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
  992. } else {
  993. _dhd_wlfc_hanger_get_genbit(ctx->hanger, p, hslot, &gen);
  994. }
  995. free_ctr = WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
  996. /* remove old header */
  997. _dhd_wlfc_pullheader(ctx, p);
  998. }
  999. if (hslot >= WLFC_HANGER_MAXITEMS) {
  1000. DHD_ERROR(("Error: %s():no hanger slot available\n", __FUNCTION__));
  1001. return BCME_ERROR;
  1002. }
  1003. WL_TXSTATUS_SET_FREERUNCTR(htod, free_ctr);
  1004. WL_TXSTATUS_SET_HSLOT(htod, hslot);
  1005. WL_TXSTATUS_SET_FIFO(htod, DHD_PKTTAG_FIFO(PKTTAG(p)));
  1006. WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST);
  1007. WL_TXSTATUS_SET_GENERATION(htod, gen);
  1008. DHD_PKTTAG_SETPKTDIR(PKTTAG(p), 1);
  1009. if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) {
  1010. /*
  1011. Indicate that this packet is being sent in response to an
  1012. explicit request from the firmware side.
  1013. */
  1014. WLFC_PKTFLAG_SET_PKTREQUESTED(htod);
  1015. } else {
  1016. WLFC_PKTFLAG_CLR_PKTREQUESTED(htod);
  1017. }
  1018. rc = _dhd_wlfc_pushheader(ctx, &p, send_tim_update,
  1019. entry->traffic_lastreported_bmp, entry->mac_handle, htod, htodseq, FALSE);
  1020. if (rc == BCME_OK) {
  1021. DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod);
  1022. if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
  1023. wlfc_hanger_t *h = (wlfc_hanger_t*)(ctx->hanger);
  1024. if (header_needed) {
  1025. /*
  1026. a new header was created for this packet.
  1027. push to hanger slot and scrub q. Since bus
  1028. send succeeded, increment seq number as well.
  1029. */
  1030. rc = _dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot);
  1031. if (rc == BCME_OK) {
  1032. #ifdef PROP_TXSTATUS_DEBUG
  1033. h->items[hslot].push_time =
  1034. OSL_SYSUPTIME();
  1035. #endif // endif
  1036. } else {
  1037. DHD_ERROR(("%s() hanger_pushpkt() failed, rc: %d\n",
  1038. __FUNCTION__, rc));
  1039. }
  1040. } else {
  1041. /* clear hanger state */
  1042. if (((wlfc_hanger_t*)(ctx->hanger))->items[hslot].pkt != p)
  1043. DHD_ERROR(("%s() pkt not match: cur %p, hanger pkt %p\n",
  1044. __FUNCTION__, p, h->items[hslot].pkt));
  1045. ASSERT(h->items[hslot].pkt == p);
  1046. bcm_object_feature_set(h->items[hslot].pkt,
  1047. BCM_OBJECT_FEATURE_PKT_STATE, 0);
  1048. h->items[hslot].pkt_state = 0;
  1049. h->items[hslot].pkt_txstatus = 0;
  1050. h->items[hslot].state = WLFC_HANGER_ITEM_STATE_INUSE;
  1051. }
  1052. }
  1053. if ((rc == BCME_OK) && header_needed) {
  1054. /* increment free running sequence count */
  1055. WLFC_INCR_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
  1056. }
  1057. }
  1058. *slot = hslot;
  1059. *packet = p;
  1060. return rc;
  1061. } /* _dhd_wlfc_pretx_pktprocess */
  1062. /**
  1063. * A remote wireless mac may be temporarily 'closed' due to power management. Returns '1' if remote
  1064. * mac is in the 'open' state, otherwise '0'.
  1065. */
  1066. static int
  1067. _dhd_wlfc_is_destination_open(athost_wl_status_info_t* ctx,
  1068. wlfc_mac_descriptor_t* entry, int prec)
  1069. {
  1070. wlfc_mac_descriptor_t* interfaces = ctx->destination_entries.interfaces;
  1071. if (entry->interface_id >= WLFC_MAX_IFNUM) {
  1072. ASSERT(&ctx->destination_entries.other == entry);
  1073. return 1;
  1074. }
  1075. if (interfaces[entry->interface_id].iftype ==
  1076. WLC_E_IF_ROLE_P2P_GO) {
  1077. /* - destination interface is of type p2p GO.
  1078. For a p2pGO interface, if the destination is OPEN but the interface is
  1079. CLOSEd, do not send traffic. But if the dstn is CLOSEd while there is
  1080. destination-specific-credit left send packets. This is because the
  1081. firmware storing the destination-specific-requested packet in queue.
  1082. */
  1083. if ((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) &&
  1084. (entry->requested_packet == 0)) {
  1085. return 0;
  1086. }
  1087. }
  1088. /* AP, p2p_go -> unicast desc entry, STA/p2p_cl -> interface desc. entry */
  1089. if ((((entry->state == WLFC_STATE_CLOSE) ||
  1090. (interfaces[entry->interface_id].state == WLFC_STATE_CLOSE)) &&
  1091. (entry->requested_credit == 0) &&
  1092. (entry->requested_packet == 0)) ||
  1093. (!(entry->ac_bitmap & (1 << prec)))) {
  1094. return 0;
  1095. }
  1096. return 1;
  1097. } /* _dhd_wlfc_is_destination_open */
  1098. /**
  1099. * Dequeues a suppressed or delayed packet from a queue
  1100. * @param[in/out] ctx Driver specific flow control administration
  1101. * @param[in] prec Precedence of queue to dequeue from
  1102. * @param[out] ac_credit_spent Boolean, returns 0 or 1
  1103. * @param[out] needs_hdr Boolean, returns 0 or 1
  1104. * @param[out] entry_out The remote MAC for which the packet is destined
  1105. * @param[in] only_no_credit If TRUE, searches all entries instead of just the active ones
  1106. *
  1107. * Return value: the dequeued packet
  1108. */
  1109. static void*
  1110. _dhd_wlfc_deque_delayedq(athost_wl_status_info_t* ctx, int prec,
  1111. uint8* ac_credit_spent, uint8* needs_hdr, wlfc_mac_descriptor_t** entry_out,
  1112. bool only_no_credit)
  1113. {
  1114. wlfc_mac_descriptor_t* entry;
  1115. int total_entries;
  1116. void* p = NULL;
  1117. int i;
  1118. uint8 credit_spent = ((prec == AC_COUNT) && !ctx->bcmc_credit_supported) ? 0 : 1;
  1119. *entry_out = NULL;
  1120. /* most cases a packet will count against FIFO credit */
  1121. *ac_credit_spent = credit_spent;
  1122. /* search all entries, include nodes as well as interfaces */
  1123. if (only_no_credit) {
  1124. total_entries = ctx->requested_entry_count;
  1125. } else {
  1126. total_entries = ctx->active_entry_count;
  1127. }
  1128. for (i = 0; i < total_entries; i++) {
  1129. if (only_no_credit) {
  1130. entry = ctx->requested_entry[i];
  1131. } else {
  1132. entry = ctx->active_entry_head;
  1133. /* move head to ensure fair round-robin */
  1134. ctx->active_entry_head = ctx->active_entry_head->next;
  1135. }
  1136. ASSERT(entry);
  1137. if (entry->occupied && _dhd_wlfc_is_destination_open(ctx, entry, prec) &&
  1138. (entry->transit_count < WL_TXSTATUS_FREERUNCTR_MASK) &&
  1139. (!entry->suppressed)) {
  1140. *ac_credit_spent = credit_spent;
  1141. if (entry->state == WLFC_STATE_CLOSE) {
  1142. *ac_credit_spent = 0;
  1143. }
  1144. /* higher precedence will be picked up first,
  1145. * i.e. suppressed packets before delayed ones
  1146. */
  1147. p = pktq_pdeq(&entry->psq, PSQ_SUP_IDX(prec));
  1148. *needs_hdr = 0;
  1149. if (p == NULL) {
  1150. /* De-Q from delay Q */
  1151. p = pktq_pdeq(&entry->psq, PSQ_DLY_IDX(prec));
  1152. *needs_hdr = 1;
  1153. }
  1154. if (p != NULL) {
  1155. bcm_pkt_validate_chk(p);
  1156. /* did the packet come from suppress sub-queue? */
  1157. if (entry->requested_credit > 0) {
  1158. entry->requested_credit--;
  1159. #ifdef PROP_TXSTATUS_DEBUG
  1160. entry->dstncredit_sent_packets++;
  1161. #endif // endif
  1162. } else if (entry->requested_packet > 0) {
  1163. entry->requested_packet--;
  1164. DHD_PKTTAG_SETONETIMEPKTRQST(PKTTAG(p));
  1165. }
  1166. *entry_out = entry;
  1167. ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec]--;
  1168. ctx->pkt_cnt_per_ac[prec]--;
  1169. ctx->pkt_cnt_in_psq--;
  1170. _dhd_wlfc_flow_control_check(ctx, &entry->psq,
  1171. DHD_PKTTAG_IF(PKTTAG(p)));
  1172. /*
  1173. * A packet has been picked up, update traffic availability bitmap,
  1174. * if applicable.
  1175. */
  1176. _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
  1177. return p;
  1178. }
  1179. }
  1180. }
  1181. return NULL;
  1182. } /* _dhd_wlfc_deque_delayedq */
  1183. /** Enqueues caller supplied packet on either a 'suppressed' or 'delayed' queue */
  1184. static int
  1185. _dhd_wlfc_enque_delayq(athost_wl_status_info_t* ctx, void* pktbuf, int prec)
  1186. {
  1187. wlfc_mac_descriptor_t* entry;
  1188. if (pktbuf != NULL) {
  1189. entry = _dhd_wlfc_find_table_entry(ctx, pktbuf);
  1190. if (entry == NULL) {
  1191. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  1192. return BCME_ERROR;
  1193. }
  1194. /*
  1195. - suppressed packets go to sub_queue[2*prec + 1] AND
  1196. - delayed packets go to sub_queue[2*prec + 0] to ensure
  1197. order of delivery.
  1198. */
  1199. if (_dhd_wlfc_prec_enq_with_drop(ctx->dhdp, &entry->psq, pktbuf, (prec << 1),
  1200. FALSE, WLFC_SEQCOUNT(entry, prec))
  1201. == FALSE) {
  1202. WLFC_DBGMESG(("D"));
  1203. ctx->stats.delayq_full_error++;
  1204. return BCME_ERROR;
  1205. }
  1206. /* A packet has been pushed, update traffic availability bitmap, if applicable */
  1207. _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
  1208. }
  1209. return BCME_OK;
  1210. } /* _dhd_wlfc_enque_delayq */
  1211. /** Returns TRUE if caller supplied packet is destined for caller supplied interface */
  1212. static bool _dhd_wlfc_ifpkt_fn(void* p, void *p_ifid)
  1213. {
  1214. if (!p || !p_ifid)
  1215. return FALSE;
  1216. return (DHD_PKTTAG_WLFCPKT(PKTTAG(p))&& (*((uint8 *)p_ifid) == DHD_PKTTAG_IF(PKTTAG(p))));
  1217. }
  1218. /** Returns TRUE if caller supplied packet is destined for caller supplied remote MAC */
  1219. static bool _dhd_wlfc_entrypkt_fn(void* p, void *entry)
  1220. {
  1221. if (!p || !entry)
  1222. return FALSE;
  1223. return (DHD_PKTTAG_WLFCPKT(PKTTAG(p))&& (entry == DHD_PKTTAG_ENTRY(PKTTAG(p))));
  1224. }
  1225. static void
  1226. _dhd_wlfc_return_implied_credit(athost_wl_status_info_t* wlfc, void* pkt)
  1227. {
  1228. dhd_pub_t *dhdp;
  1229. bool credit_return = FALSE;
  1230. if (!wlfc || !pkt) {
  1231. return;
  1232. }
  1233. dhdp = (dhd_pub_t *)(wlfc->dhdp);
  1234. if (dhdp && (dhdp->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) &&
  1235. DHD_PKTTAG_CREDITCHECK(PKTTAG(pkt))) {
  1236. int lender, credit_returned = 0;
  1237. uint8 fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pkt));
  1238. credit_return = TRUE;
  1239. /* Note that borrower is fifo_id */
  1240. /* Return credits to highest priority lender first */
  1241. for (lender = AC_COUNT; lender >= 0; lender--) {
  1242. if (wlfc->credits_borrowed[fifo_id][lender] > 0) {
  1243. wlfc->FIFO_credit[lender]++;
  1244. wlfc->credits_borrowed[fifo_id][lender]--;
  1245. credit_returned = 1;
  1246. break;
  1247. }
  1248. }
  1249. if (!credit_returned) {
  1250. wlfc->FIFO_credit[fifo_id]++;
  1251. }
  1252. }
  1253. BCM_REFERENCE(credit_return);
  1254. #if defined(DHD_WLFC_THREAD)
  1255. if (credit_return) {
  1256. _dhd_wlfc_thread_wakeup(dhdp);
  1257. }
  1258. #endif /* defined(DHD_WLFC_THREAD) */
  1259. }
  1260. /** Removes and frees a packet from the hanger. Called during eg tx complete. */
  1261. static void
  1262. _dhd_wlfc_hanger_free_pkt(athost_wl_status_info_t* wlfc, uint32 slot_id, uint8 pkt_state,
  1263. int pkt_txstatus)
  1264. {
  1265. wlfc_hanger_t* hanger;
  1266. wlfc_hanger_item_t* item;
  1267. if (!wlfc)
  1268. return;
  1269. hanger = (wlfc_hanger_t*)wlfc->hanger;
  1270. if (!hanger)
  1271. return;
  1272. if (slot_id == WLFC_HANGER_MAXITEMS)
  1273. return;
  1274. item = &hanger->items[slot_id];
  1275. if (item->pkt) {
  1276. item->pkt_state |= pkt_state;
  1277. if (pkt_txstatus != -1)
  1278. item->pkt_txstatus = (uint8)pkt_txstatus;
  1279. bcm_object_feature_set(item->pkt, BCM_OBJECT_FEATURE_PKT_STATE, item->pkt_state);
  1280. if (item->pkt_state == WLFC_HANGER_PKT_STATE_COMPLETE) {
  1281. void *p = NULL;
  1282. void *pkt = item->pkt;
  1283. uint8 old_state = item->state;
  1284. int ret = _dhd_wlfc_hanger_poppkt(wlfc->hanger, slot_id, &p, TRUE);
  1285. BCM_REFERENCE(ret);
  1286. BCM_REFERENCE(pkt);
  1287. ASSERT((ret == BCME_OK) && p && (pkt == p));
  1288. if (old_state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
  1289. printf("ERROR: free a suppressed pkt %p state %d pkt_state %d\n",
  1290. pkt, old_state, item->pkt_state);
  1291. }
  1292. ASSERT(old_state != WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED);
  1293. /* free packet */
  1294. wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(p))]
  1295. [DHD_PKTTAG_FIFO(PKTTAG(p))]--;
  1296. wlfc->stats.pktout++;
  1297. dhd_txcomplete((dhd_pub_t *)wlfc->dhdp, p, item->pkt_txstatus);
  1298. PKTFREE(wlfc->osh, p, TRUE);
  1299. }
  1300. } else {
  1301. /* free slot */
  1302. if (item->state == WLFC_HANGER_ITEM_STATE_FREE)
  1303. DHD_ERROR(("Error: %s():%d Multiple TXSTATUS or BUSRETURNED: %d (%d)\n",
  1304. __FUNCTION__, __LINE__, item->pkt_state, pkt_state));
  1305. item->state = WLFC_HANGER_ITEM_STATE_FREE;
  1306. }
  1307. } /* _dhd_wlfc_hanger_free_pkt */
  1308. /** Called during eg detach() */
  1309. static void
  1310. _dhd_wlfc_pktq_flush(athost_wl_status_info_t* ctx, struct pktq *pq,
  1311. bool dir, f_processpkt_t fn, void *arg, q_type_t q_type)
  1312. {
  1313. int prec;
  1314. dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
  1315. ASSERT(dhdp);
  1316. /* Optimize flush, if pktq len = 0, just return.
  1317. * pktq len of 0 means pktq's prec q's are all empty.
  1318. */
  1319. if (pq->n_pkts_tot == 0) {
  1320. return;
  1321. }
  1322. for (prec = 0; prec < pq->num_prec; prec++) {
  1323. struct pktq_prec *q;
  1324. void *p, *prev = NULL;
  1325. q = &pq->q[prec];
  1326. p = q->head;
  1327. while (p) {
  1328. bcm_pkt_validate_chk(p);
  1329. if (fn == NULL || (*fn)(p, arg)) {
  1330. bool head = (p == q->head);
  1331. if (head)
  1332. q->head = PKTLINK(p);
  1333. else
  1334. PKTSETLINK(prev, PKTLINK(p));
  1335. if (q_type == Q_TYPE_PSQ) {
  1336. if (!WLFC_GET_AFQ(dhdp->wlfc_mode) && (prec & 1)) {
  1337. _dhd_wlfc_hanger_remove_reference(ctx->hanger, p);
  1338. }
  1339. ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec>>1]--;
  1340. ctx->pkt_cnt_per_ac[prec>>1]--;
  1341. ctx->pkt_cnt_in_psq--;
  1342. ctx->stats.cleanup_psq_cnt++;
  1343. if (!(prec & 1)) {
  1344. /* pkt in delayed q, so fake push BDC header for
  1345. * dhd_tcpack_check_xmit() and dhd_txcomplete().
  1346. */
  1347. _dhd_wlfc_pushheader(ctx, &p, FALSE, 0, 0,
  1348. 0, 0, TRUE);
  1349. #ifdef DHDTCPACK_SUPPRESS
  1350. if (dhd_tcpack_check_xmit(dhdp, p) == BCME_ERROR) {
  1351. DHD_ERROR(("%s %d: tcpack_suppress ERROR!!!"
  1352. " Stop using it\n",
  1353. __FUNCTION__, __LINE__));
  1354. dhd_tcpack_suppress_set(dhdp,
  1355. TCPACK_SUP_OFF);
  1356. }
  1357. #endif /* DHDTCPACK_SUPPRESS */
  1358. }
  1359. } else if (q_type == Q_TYPE_AFQ) {
  1360. wlfc_mac_descriptor_t* entry =
  1361. _dhd_wlfc_find_table_entry(ctx, p);
  1362. if (entry->transit_count)
  1363. entry->transit_count--;
  1364. if (entry->suppr_transit_count) {
  1365. entry->suppr_transit_count--;
  1366. if (entry->suppressed &&
  1367. (!entry->onbus_pkts_count) &&
  1368. (!entry->suppr_transit_count))
  1369. entry->suppressed = FALSE;
  1370. }
  1371. _dhd_wlfc_return_implied_credit(ctx, p);
  1372. ctx->stats.cleanup_fw_cnt++;
  1373. }
  1374. PKTSETLINK(p, NULL);
  1375. if (dir) {
  1376. ctx->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(p))][prec>>1]--;
  1377. ctx->stats.pktout++;
  1378. dhd_txcomplete(dhdp, p, FALSE);
  1379. }
  1380. PKTFREE(ctx->osh, p, dir);
  1381. q->n_pkts--;
  1382. pq->n_pkts_tot--;
  1383. #ifdef WL_TXQ_STALL
  1384. q->dequeue_count++;
  1385. #endif // endif
  1386. p = (head ? q->head : PKTLINK(prev));
  1387. } else {
  1388. prev = p;
  1389. p = PKTLINK(p);
  1390. }
  1391. }
  1392. if (q->head == NULL) {
  1393. ASSERT(q->n_pkts == 0);
  1394. q->tail = NULL;
  1395. }
  1396. }
  1397. if (fn == NULL)
  1398. ASSERT(pq->n_pkts_tot == 0);
  1399. } /* _dhd_wlfc_pktq_flush */
  1400. /** !BCMDBUS specific function. Dequeues a packet from the caller supplied queue. */
  1401. static void*
  1402. _dhd_wlfc_pktq_pdeq_with_fn(struct pktq *pq, int prec, f_processpkt_t fn, void *arg)
  1403. {
  1404. struct pktq_prec *q;
  1405. void *p, *prev = NULL;
  1406. ASSERT(prec >= 0 && prec < pq->num_prec);
  1407. q = &pq->q[prec];
  1408. p = q->head;
  1409. while (p) {
  1410. if (fn == NULL || (*fn)(p, arg)) {
  1411. break;
  1412. } else {
  1413. prev = p;
  1414. p = PKTLINK(p);
  1415. }
  1416. }
  1417. if (p == NULL)
  1418. return NULL;
  1419. bcm_pkt_validate_chk(p);
  1420. if (prev == NULL) {
  1421. if ((q->head = PKTLINK(p)) == NULL) {
  1422. q->tail = NULL;
  1423. }
  1424. } else {
  1425. PKTSETLINK(prev, PKTLINK(p));
  1426. if (q->tail == p) {
  1427. q->tail = prev;
  1428. }
  1429. }
  1430. q->n_pkts--;
  1431. pq->n_pkts_tot--;
  1432. #ifdef WL_TXQ_STALL
  1433. q->dequeue_count++;
  1434. #endif // endif
  1435. PKTSETLINK(p, NULL);
  1436. return p;
  1437. }
  1438. /** !BCMDBUS specific function */
  1439. static void
  1440. _dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
  1441. {
  1442. int prec;
  1443. void *pkt = NULL, *head = NULL, *tail = NULL;
  1444. struct pktq *txq = (struct pktq *)dhd_bus_txq(dhd->bus);
  1445. athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
  1446. wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
  1447. wlfc_mac_descriptor_t* entry;
  1448. dhd_os_sdlock_txq(dhd);
  1449. for (prec = 0; prec < txq->num_prec; prec++) {
  1450. while ((pkt = _dhd_wlfc_pktq_pdeq_with_fn(txq, prec, fn, arg))) {
  1451. #ifdef DHDTCPACK_SUPPRESS
  1452. if (dhd_tcpack_check_xmit(dhd, pkt) == BCME_ERROR) {
  1453. DHD_ERROR(("%s %d: tcpack_suppress ERROR!!! Stop using it\n",
  1454. __FUNCTION__, __LINE__));
  1455. dhd_tcpack_suppress_set(dhd, TCPACK_SUP_OFF);
  1456. }
  1457. #endif /* DHDTCPACK_SUPPRESS */
  1458. if (!head) {
  1459. head = pkt;
  1460. }
  1461. if (tail) {
  1462. PKTSETLINK(tail, pkt);
  1463. }
  1464. tail = pkt;
  1465. }
  1466. }
  1467. dhd_os_sdunlock_txq(dhd);
  1468. while ((pkt = head)) {
  1469. head = PKTLINK(pkt);
  1470. PKTSETLINK(pkt, NULL);
  1471. entry = _dhd_wlfc_find_table_entry(wlfc, pkt);
  1472. if (!WLFC_GET_AFQ(dhd->wlfc_mode) &&
  1473. !_dhd_wlfc_hanger_remove_reference(h, pkt)) {
  1474. DHD_ERROR(("%s: can't find pkt(%p) in hanger, free it anyway\n",
  1475. __FUNCTION__, pkt));
  1476. }
  1477. if (entry->transit_count)
  1478. entry->transit_count--;
  1479. if (entry->suppr_transit_count) {
  1480. entry->suppr_transit_count--;
  1481. if (entry->suppressed &&
  1482. (!entry->onbus_pkts_count) &&
  1483. (!entry->suppr_transit_count))
  1484. entry->suppressed = FALSE;
  1485. }
  1486. _dhd_wlfc_return_implied_credit(wlfc, pkt);
  1487. wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pkt))][DHD_PKTTAG_FIFO(PKTTAG(pkt))]--;
  1488. wlfc->stats.pktout++;
  1489. wlfc->stats.cleanup_txq_cnt++;
  1490. dhd_txcomplete(dhd, pkt, FALSE);
  1491. PKTFREE(wlfc->osh, pkt, TRUE);
  1492. }
  1493. } /* _dhd_wlfc_cleanup_txq */
  1494. /** called during eg detach */
  1495. void
  1496. _dhd_wlfc_cleanup(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
  1497. {
  1498. int i;
  1499. int total_entries;
  1500. athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
  1501. wlfc_mac_descriptor_t* table;
  1502. wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
  1503. wlfc->stats.cleanup_txq_cnt = 0;
  1504. wlfc->stats.cleanup_psq_cnt = 0;
  1505. wlfc->stats.cleanup_fw_cnt = 0;
  1506. /*
  1507. * flush sequence should be txq -> psq -> hanger/afq, hanger has to be last one
  1508. */
  1509. /* flush bus->txq */
  1510. _dhd_wlfc_cleanup_txq(dhd, fn, arg);
  1511. /* flush psq, search all entries, include nodes as well as interfaces */
  1512. total_entries = sizeof(wlfc->destination_entries)/sizeof(wlfc_mac_descriptor_t);
  1513. table = (wlfc_mac_descriptor_t*)&wlfc->destination_entries;
  1514. for (i = 0; i < total_entries; i++) {
  1515. if (table[i].occupied) {
  1516. /* release packets held in PSQ (both delayed and suppressed) */
  1517. if (table[i].psq.n_pkts_tot) {
  1518. WLFC_DBGMESG(("%s(): PSQ[%d].len = %d\n",
  1519. __FUNCTION__, i, table[i].psq.n_pkts_tot));
  1520. _dhd_wlfc_pktq_flush(wlfc, &table[i].psq, TRUE,
  1521. fn, arg, Q_TYPE_PSQ);
  1522. }
  1523. /* free packets held in AFQ */
  1524. if (WLFC_GET_AFQ(dhd->wlfc_mode) && (table[i].afq.n_pkts_tot)) {
  1525. _dhd_wlfc_pktq_flush(wlfc, &table[i].afq, TRUE,
  1526. fn, arg, Q_TYPE_AFQ);
  1527. }
  1528. if ((fn == NULL) && (&table[i] != &wlfc->destination_entries.other)) {
  1529. table[i].occupied = 0;
  1530. if (table[i].transit_count || table[i].suppr_transit_count) {
  1531. DHD_ERROR(("%s: table[%d] transit(%d), suppr_tansit(%d)\n",
  1532. __FUNCTION__, i,
  1533. table[i].transit_count,
  1534. table[i].suppr_transit_count));
  1535. }
  1536. }
  1537. }
  1538. }
  1539. /*
  1540. . flush remained pkt in hanger queue, not in bus->txq nor psq.
  1541. . the remained pkt was successfully downloaded to dongle already.
  1542. . hanger slot state cannot be set to free until receive txstatus update.
  1543. */
  1544. if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
  1545. for (i = 0; i < h->max_items; i++) {
  1546. if ((h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) ||
  1547. (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED)) {
  1548. if (fn == NULL || (*fn)(h->items[i].pkt, arg)) {
  1549. h->items[i].state = WLFC_HANGER_ITEM_STATE_FLUSHED;
  1550. }
  1551. }
  1552. }
  1553. }
  1554. return;
  1555. } /* _dhd_wlfc_cleanup */
  1556. /** Called after eg the dongle signalled a new remote MAC that it connected with to the DHD */
  1557. static int
  1558. _dhd_wlfc_mac_entry_update(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
  1559. uint8 action, uint8 ifid, uint8 iftype, uint8* ea,
  1560. f_processpkt_t fn, void *arg)
  1561. {
  1562. int rc = BCME_OK;
  1563. if ((action == eWLFC_MAC_ENTRY_ACTION_ADD) || (action == eWLFC_MAC_ENTRY_ACTION_UPDATE)) {
  1564. entry->occupied = 1;
  1565. entry->state = WLFC_STATE_OPEN;
  1566. entry->requested_credit = 0;
  1567. entry->interface_id = ifid;
  1568. entry->iftype = iftype;
  1569. entry->ac_bitmap = 0xff; /* update this when handling APSD */
  1570. /* for an interface entry we may not care about the MAC address */
  1571. if (ea != NULL)
  1572. memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN);
  1573. if (action == eWLFC_MAC_ENTRY_ACTION_ADD) {
  1574. entry->suppressed = FALSE;
  1575. entry->transit_count = 0;
  1576. entry->suppr_transit_count = 0;
  1577. entry->onbus_pkts_count = 0;
  1578. }
  1579. if (action == eWLFC_MAC_ENTRY_ACTION_ADD) {
  1580. dhd_pub_t *dhdp = (dhd_pub_t *)(ctx->dhdp);
  1581. pktq_init(&entry->psq, WLFC_PSQ_PREC_COUNT, WLFC_PSQ_LEN);
  1582. _dhd_wlfc_flow_control_check(ctx, &entry->psq, ifid);
  1583. if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
  1584. pktq_init(&entry->afq, WLFC_AFQ_PREC_COUNT, WLFC_PSQ_LEN);
  1585. }
  1586. if (entry->next == NULL) {
  1587. /* not linked to anywhere, add to tail */
  1588. if (ctx->active_entry_head) {
  1589. entry->prev = ctx->active_entry_head->prev;
  1590. ctx->active_entry_head->prev->next = entry;
  1591. ctx->active_entry_head->prev = entry;
  1592. entry->next = ctx->active_entry_head;
  1593. } else {
  1594. ASSERT(ctx->active_entry_count == 0);
  1595. entry->prev = entry->next = entry;
  1596. ctx->active_entry_head = entry;
  1597. }
  1598. ctx->active_entry_count++;
  1599. } else {
  1600. DHD_ERROR(("%s():%d, entry(%d)\n", __FUNCTION__, __LINE__,
  1601. (int)(entry - &ctx->destination_entries.nodes[0])));
  1602. }
  1603. }
  1604. } else if (action == eWLFC_MAC_ENTRY_ACTION_DEL) {
  1605. /* When the entry is deleted, the packets that are queued in the entry must be
  1606. cleanup. The cleanup action should be before the occupied is set as 0.
  1607. */
  1608. _dhd_wlfc_cleanup(ctx->dhdp, fn, arg);
  1609. _dhd_wlfc_flow_control_check(ctx, &entry->psq, ifid);
  1610. entry->occupied = 0;
  1611. entry->state = WLFC_STATE_CLOSE;
  1612. memset(&entry->ea[0], 0, ETHER_ADDR_LEN);
  1613. if (entry->next) {
  1614. /* not floating, remove from Q */
  1615. if (ctx->active_entry_count <= 1) {
  1616. /* last item */
  1617. ctx->active_entry_head = NULL;
  1618. ctx->active_entry_count = 0;
  1619. } else {
  1620. entry->prev->next = entry->next;
  1621. entry->next->prev = entry->prev;
  1622. if (entry == ctx->active_entry_head) {
  1623. ctx->active_entry_head = entry->next;
  1624. }
  1625. ctx->active_entry_count--;
  1626. }
  1627. entry->next = entry->prev = NULL;
  1628. } else {
  1629. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  1630. }
  1631. }
  1632. return rc;
  1633. } /* _dhd_wlfc_mac_entry_update */
  1634. #ifdef LIMIT_BORROW
  1635. /** LIMIT_BORROW specific function */
  1636. static int
  1637. _dhd_wlfc_borrow_credit(athost_wl_status_info_t* ctx, int highest_lender_ac, int borrower_ac,
  1638. bool bBorrowAll)
  1639. {
  1640. int lender_ac, borrow_limit = 0;
  1641. int rc = -1;
  1642. if (ctx == NULL) {
  1643. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  1644. return -1;
  1645. }
  1646. /* Borrow from lowest priority available AC (including BC/MC credits) */
  1647. for (lender_ac = 0; lender_ac <= highest_lender_ac; lender_ac++) {
  1648. if (!bBorrowAll) {
  1649. borrow_limit = ctx->Init_FIFO_credit[lender_ac]/WLFC_BORROW_LIMIT_RATIO;
  1650. } else {
  1651. borrow_limit = 0;
  1652. }
  1653. if (ctx->FIFO_credit[lender_ac] > borrow_limit) {
  1654. ctx->credits_borrowed[borrower_ac][lender_ac]++;
  1655. ctx->FIFO_credit[lender_ac]--;
  1656. rc = lender_ac;
  1657. break;
  1658. }
  1659. }
  1660. return rc;
  1661. }
  1662. /** LIMIT_BORROW specific function */
  1663. static int _dhd_wlfc_return_credit(athost_wl_status_info_t* ctx, int lender_ac, int borrower_ac)
  1664. {
  1665. if ((ctx == NULL) || (lender_ac < 0) || (lender_ac > AC_COUNT) ||
  1666. (borrower_ac < 0) || (borrower_ac > AC_COUNT)) {
  1667. DHD_ERROR(("Error: %s():%d, ctx(%p), lender_ac(%d), borrower_ac(%d)\n",
  1668. __FUNCTION__, __LINE__, ctx, lender_ac, borrower_ac));
  1669. return BCME_BADARG;
  1670. }
  1671. ctx->credits_borrowed[borrower_ac][lender_ac]--;
  1672. ctx->FIFO_credit[lender_ac]++;
  1673. return BCME_OK;
  1674. }
  1675. #endif /* LIMIT_BORROW */
  1676. /**
  1677. * Called on an interface event (WLC_E_IF) indicated by firmware.
  1678. * @param action : eg eWLFC_MAC_ENTRY_ACTION_UPDATE or eWLFC_MAC_ENTRY_ACTION_ADD
  1679. */
  1680. static int
  1681. _dhd_wlfc_interface_entry_update(void* state,
  1682. uint8 action, uint8 ifid, uint8 iftype, uint8* ea)
  1683. {
  1684. athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
  1685. wlfc_mac_descriptor_t* entry;
  1686. if (ifid >= WLFC_MAX_IFNUM)
  1687. return BCME_BADARG;
  1688. entry = &ctx->destination_entries.interfaces[ifid];
  1689. return _dhd_wlfc_mac_entry_update(ctx, entry, action, ifid, iftype, ea,
  1690. _dhd_wlfc_ifpkt_fn, &ifid);
  1691. }
  1692. /**
  1693. * Called eg on receiving a WLC_E_BCMC_CREDIT_SUPPORT event from the dongle (broadcast/multicast
  1694. * specific)
  1695. */
  1696. static int
  1697. _dhd_wlfc_BCMCCredit_support_update(void* state)
  1698. {
  1699. athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
  1700. ctx->bcmc_credit_supported = TRUE;
  1701. return BCME_OK;
  1702. }
  1703. /** Called eg on receiving a WLC_E_FIFO_CREDIT_MAP event from the dongle */
  1704. static int
  1705. _dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits)
  1706. {
  1707. athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
  1708. int i;
  1709. for (i = 0; i <= 4; i++) {
  1710. if (ctx->Init_FIFO_credit[i] != ctx->FIFO_credit[i]) {
  1711. DHD_ERROR(("%s: credit[i] is not returned, (%d %d)\n",
  1712. __FUNCTION__, ctx->Init_FIFO_credit[i], ctx->FIFO_credit[i]));
  1713. }
  1714. }
  1715. /* update the AC FIFO credit map */
  1716. ctx->FIFO_credit[0] += (credits[0] - ctx->Init_FIFO_credit[0]);
  1717. ctx->FIFO_credit[1] += (credits[1] - ctx->Init_FIFO_credit[1]);
  1718. ctx->FIFO_credit[2] += (credits[2] - ctx->Init_FIFO_credit[2]);
  1719. ctx->FIFO_credit[3] += (credits[3] - ctx->Init_FIFO_credit[3]);
  1720. ctx->FIFO_credit[4] += (credits[4] - ctx->Init_FIFO_credit[4]);
  1721. ctx->Init_FIFO_credit[0] = credits[0];
  1722. ctx->Init_FIFO_credit[1] = credits[1];
  1723. ctx->Init_FIFO_credit[2] = credits[2];
  1724. ctx->Init_FIFO_credit[3] = credits[3];
  1725. ctx->Init_FIFO_credit[4] = credits[4];
  1726. /* credit for ATIM FIFO is not used yet. */
  1727. ctx->Init_FIFO_credit[5] = ctx->FIFO_credit[5] = 0;
  1728. return BCME_OK;
  1729. }
  1730. /**
  1731. * Called during committing of a transmit packet from the OS DHD layer to the next layer towards
  1732. * the dongle (eg the DBUS layer). All transmit packets flow via this function to the next layer.
  1733. *
  1734. * @param[in/out] ctx Driver specific flow control administration
  1735. * @param[in] ac Access Category (QoS) of called supplied packet
  1736. * @param[in] commit_info Contains eg the packet to send
  1737. * @param[in] fcommit Function pointer to transmit function of next software layer
  1738. * @param[in] commit_ctx Opaque context used when calling next layer
  1739. */
  1740. static int
  1741. _dhd_wlfc_handle_packet_commit(athost_wl_status_info_t* ctx, int ac,
  1742. dhd_wlfc_commit_info_t *commit_info, f_commitpkt_t fcommit, void* commit_ctx)
  1743. {
  1744. uint32 hslot;
  1745. int rc;
  1746. dhd_pub_t *dhdp = (dhd_pub_t *)(ctx->dhdp);
  1747. /*
  1748. if ac_fifo_credit_spent = 0
  1749. This packet will not count against the FIFO credit.
  1750. To ensure the txstatus corresponding to this packet
  1751. does not provide an implied credit (default behavior)
  1752. mark the packet accordingly.
  1753. if ac_fifo_credit_spent = 1
  1754. This is a normal packet and it counts against the FIFO
  1755. credit count.
  1756. */
  1757. DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), commit_info->ac_fifo_credit_spent);
  1758. rc = _dhd_wlfc_pretx_pktprocess(ctx, commit_info->mac_entry, &commit_info->p,
  1759. commit_info->needs_hdr, &hslot);
  1760. if (rc == BCME_OK) {
  1761. rc = fcommit(commit_ctx, commit_info->p);
  1762. if (rc == BCME_OK) {
  1763. uint8 gen = WL_TXSTATUS_GET_GENERATION(
  1764. DHD_PKTTAG_H2DTAG(PKTTAG(commit_info->p)));
  1765. ctx->stats.pkt2bus++;
  1766. if (commit_info->ac_fifo_credit_spent || (ac == AC_COUNT)) {
  1767. ctx->stats.send_pkts[ac]++;
  1768. WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac);
  1769. }
  1770. if (gen != commit_info->mac_entry->generation) {
  1771. /* will be suppressed back by design */
  1772. if (!commit_info->mac_entry->suppressed) {
  1773. commit_info->mac_entry->suppressed = TRUE;
  1774. }
  1775. commit_info->mac_entry->suppr_transit_count++;
  1776. }
  1777. commit_info->mac_entry->transit_count++;
  1778. commit_info->mac_entry->onbus_pkts_count++;
  1779. } else if (commit_info->needs_hdr) {
  1780. if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
  1781. void *pout = NULL;
  1782. /* pop hanger for delayed packet */
  1783. _dhd_wlfc_hanger_poppkt(ctx->hanger, WL_TXSTATUS_GET_HSLOT(
  1784. DHD_PKTTAG_H2DTAG(PKTTAG(commit_info->p))), &pout, TRUE);
  1785. ASSERT(commit_info->p == pout);
  1786. }
  1787. }
  1788. } else {
  1789. ctx->stats.generic_error++;
  1790. }
  1791. if (rc != BCME_OK) {
  1792. /*
  1793. pretx pkt process or bus commit has failed, rollback.
  1794. - remove wl-header for a delayed packet
  1795. - save wl-header header for suppressed packets
  1796. - reset credit check flag
  1797. */
  1798. _dhd_wlfc_rollback_packet_toq(ctx, commit_info->p, commit_info->pkt_type, hslot);
  1799. DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), 0);
  1800. }
  1801. return rc;
  1802. } /* _dhd_wlfc_handle_packet_commit */
  1803. /** Returns remote MAC descriptor for caller supplied MAC address */
  1804. static uint8
  1805. _dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t *dhdp, uint8 *ea)
  1806. {
  1807. wlfc_mac_descriptor_t* table =
  1808. ((athost_wl_status_info_t*)dhdp->wlfc_state)->destination_entries.nodes;
  1809. uint8 table_index;
  1810. if (ea != NULL) {
  1811. for (table_index = 0; table_index < WLFC_MAC_DESC_TABLE_SIZE; table_index++) {
  1812. if ((memcmp(ea, &table[table_index].ea[0], ETHER_ADDR_LEN) == 0) &&
  1813. table[table_index].occupied)
  1814. return table_index;
  1815. }
  1816. }
  1817. return WLFC_MAC_DESC_ID_INVALID;
  1818. }
  1819. /**
  1820. * Called when the host receives a WLFC_CTL_TYPE_TXSTATUS event from the dongle, indicating the
  1821. * status of a frame that the dongle attempted to transmit over the wireless medium.
  1822. */
  1823. static int
  1824. dhd_wlfc_suppressed_acked_update(dhd_pub_t *dhd, uint16 hslot, uint8 prec, uint8 hcnt)
  1825. {
  1826. athost_wl_status_info_t* ctx;
  1827. wlfc_mac_descriptor_t* entry = NULL;
  1828. struct pktq *pq;
  1829. struct pktq_prec *q;
  1830. void *p, *b;
  1831. if (!dhd) {
  1832. DHD_ERROR(("%s: dhd(%p)\n", __FUNCTION__, dhd));
  1833. return BCME_BADARG;
  1834. }
  1835. ctx = (athost_wl_status_info_t*)dhd->wlfc_state;
  1836. if (!ctx) {
  1837. DHD_ERROR(("%s: ctx(%p)\n", __FUNCTION__, ctx));
  1838. return BCME_ERROR;
  1839. }
  1840. ASSERT(hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM + 1));
  1841. if (hslot < WLFC_MAC_DESC_TABLE_SIZE)
  1842. entry = &ctx->destination_entries.nodes[hslot];
  1843. else if (hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM))
  1844. entry = &ctx->destination_entries.interfaces[hslot - WLFC_MAC_DESC_TABLE_SIZE];
  1845. else
  1846. entry = &ctx->destination_entries.other;
  1847. pq = &entry->psq;
  1848. ASSERT(((prec << 1) + 1) < pq->num_prec);
  1849. q = &pq->q[((prec << 1) + 1)];
  1850. b = NULL;
  1851. p = q->head;
  1852. while (p && (hcnt != WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p))))) {
  1853. b = p;
  1854. p = PKTLINK(p);
  1855. }
  1856. if (p == NULL) {
  1857. /* none is matched */
  1858. if (b) {
  1859. DHD_ERROR(("%s: can't find matching seq(%d)\n", __FUNCTION__, hcnt));
  1860. } else {
  1861. DHD_ERROR(("%s: queue is empty\n", __FUNCTION__));
  1862. }
  1863. return BCME_ERROR;
  1864. }
  1865. if (!b) {
  1866. /* head packet is matched */
  1867. if ((q->head = PKTLINK(p)) == NULL) {
  1868. q->tail = NULL;
  1869. }
  1870. } else {
  1871. /* middle packet is matched */
  1872. PKTSETLINK(b, PKTLINK(p));
  1873. if (PKTLINK(p) == NULL) {
  1874. q->tail = b;
  1875. }
  1876. }
  1877. q->n_pkts--;
  1878. pq->n_pkts_tot--;
  1879. #ifdef WL_TXQ_STALL
  1880. q->dequeue_count++;
  1881. #endif // endif
  1882. ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec]--;
  1883. ctx->pkt_cnt_per_ac[prec]--;
  1884. PKTSETLINK(p, NULL);
  1885. if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
  1886. _dhd_wlfc_enque_afq(ctx, p);
  1887. } else {
  1888. _dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot);
  1889. }
  1890. entry->transit_count++;
  1891. return BCME_OK;
  1892. }
  1893. static int
  1894. _dhd_wlfc_compressed_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info, uint8 len, void** p_mac)
  1895. {
  1896. uint8 status_flag_ori, status_flag;
  1897. uint32 status;
  1898. int ret = BCME_OK;
  1899. int remove_from_hanger_ori, remove_from_hanger = 1;
  1900. void* pktbuf = NULL;
  1901. uint8 fifo_id = 0, gen = 0, count = 0, hcnt;
  1902. uint16 hslot;
  1903. wlfc_mac_descriptor_t* entry = NULL;
  1904. athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
  1905. uint16 seq = 0, seq_fromfw = 0, seq_num = 0;
  1906. memcpy(&status, pkt_info, sizeof(uint32));
  1907. status = ltoh32(status);
  1908. status_flag = WL_TXSTATUS_GET_FLAGS(status);
  1909. hcnt = WL_TXSTATUS_GET_FREERUNCTR(status);
  1910. hslot = WL_TXSTATUS_GET_HSLOT(status);
  1911. fifo_id = WL_TXSTATUS_GET_FIFO(status);
  1912. gen = WL_TXSTATUS_GET_GENERATION(status);
  1913. if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
  1914. memcpy(&seq, pkt_info + WLFC_CTL_VALUE_LEN_TXSTATUS, WLFC_CTL_VALUE_LEN_SEQ);
  1915. seq = ltoh16(seq);
  1916. seq_fromfw = GET_WL_HAS_ASSIGNED_SEQ(seq);
  1917. seq_num = WL_SEQ_GET_NUM(seq);
  1918. }
  1919. wlfc->stats.txstatus_in += len;
  1920. if (status_flag == WLFC_CTL_PKTFLAG_DISCARD) {
  1921. wlfc->stats.pkt_freed += len;
  1922. } else if (status_flag == WLFC_CTL_PKTFLAG_DISCARD_NOACK) {
  1923. wlfc->stats.pkt_freed += len;
  1924. } else if (status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) {
  1925. wlfc->stats.d11_suppress += len;
  1926. remove_from_hanger = 0;
  1927. } else if (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS) {
  1928. wlfc->stats.wl_suppress += len;
  1929. remove_from_hanger = 0;
  1930. } else if (status_flag == WLFC_CTL_PKTFLAG_TOSSED_BYWLC) {
  1931. wlfc->stats.wlc_tossed_pkts += len;
  1932. } else if (status_flag == WLFC_CTL_PKTFLAG_SUPPRESS_ACKED) {
  1933. wlfc->stats.pkt_freed += len;
  1934. } else if (status_flag == WLFC_CTL_PKTFLAG_EXPIRED) {
  1935. wlfc->stats.pkt_exptime += len;
  1936. } else if (status_flag == WLFC_CTL_PKTFLAG_DROPPED) {
  1937. wlfc->stats.pkt_dropped += len;
  1938. }
  1939. if (dhd->proptxstatus_txstatus_ignore) {
  1940. if (!remove_from_hanger) {
  1941. DHD_ERROR(("suppress txstatus: %d\n", status_flag));
  1942. }
  1943. return BCME_OK;
  1944. }
  1945. status_flag_ori = status_flag;
  1946. remove_from_hanger_ori = remove_from_hanger;
  1947. while (count < len) {
  1948. if (status_flag == WLFC_CTL_PKTFLAG_SUPPRESS_ACKED) {
  1949. dhd_wlfc_suppressed_acked_update(dhd, hslot, fifo_id, hcnt);
  1950. }
  1951. if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
  1952. ret = _dhd_wlfc_deque_afq(wlfc, hslot, hcnt, fifo_id, &pktbuf);
  1953. } else {
  1954. status_flag = status_flag_ori;
  1955. remove_from_hanger = remove_from_hanger_ori;
  1956. ret = _dhd_wlfc_hanger_poppkt(wlfc->hanger, hslot, &pktbuf, FALSE);
  1957. if (!pktbuf) {
  1958. _dhd_wlfc_hanger_free_pkt(wlfc, hslot,
  1959. WLFC_HANGER_PKT_STATE_TXSTATUS, -1);
  1960. goto cont;
  1961. } else {
  1962. wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
  1963. if (h->items[hslot].state == WLFC_HANGER_ITEM_STATE_FLUSHED) {
  1964. status_flag = WLFC_CTL_PKTFLAG_DISCARD;
  1965. remove_from_hanger = 1;
  1966. }
  1967. }
  1968. }
  1969. if ((ret != BCME_OK) || !pktbuf) {
  1970. goto cont;
  1971. }
  1972. bcm_pkt_validate_chk(pktbuf);
  1973. /* set fifo_id to correct value because not all FW does that */
  1974. fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
  1975. entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
  1976. if (!remove_from_hanger) {
  1977. /* this packet was suppressed */
  1978. if (!entry->suppressed || (entry->generation != gen)) {
  1979. if (!entry->suppressed) {
  1980. entry->suppr_transit_count = entry->transit_count;
  1981. if (p_mac) {
  1982. *p_mac = entry;
  1983. }
  1984. } else {
  1985. DHD_ERROR(("gen(%d), entry->generation(%d)\n",
  1986. gen, entry->generation));
  1987. }
  1988. entry->suppressed = TRUE;
  1989. }
  1990. entry->generation = gen;
  1991. }
  1992. #ifdef PROP_TXSTATUS_DEBUG
  1993. if (!WLFC_GET_AFQ(dhd->wlfc_mode))
  1994. {
  1995. uint32 new_t = OSL_SYSUPTIME();
  1996. uint32 old_t;
  1997. uint32 delta;
  1998. old_t = ((wlfc_hanger_t*)(wlfc->hanger))->items[hslot].push_time;
  1999. wlfc->stats.latency_sample_count++;
  2000. if (new_t > old_t)
  2001. delta = new_t - old_t;
  2002. else
  2003. delta = 0xffffffff + new_t - old_t;
  2004. wlfc->stats.total_status_latency += delta;
  2005. wlfc->stats.latency_most_recent = delta;
  2006. wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta;
  2007. if (wlfc->stats.idx_delta == sizeof(wlfc->stats.deltas)/sizeof(uint32))
  2008. wlfc->stats.idx_delta = 0;
  2009. }
  2010. #endif /* PROP_TXSTATUS_DEBUG */
  2011. /* pick up the implicit credit from this packet */
  2012. if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) {
  2013. _dhd_wlfc_return_implied_credit(wlfc, pktbuf);
  2014. } else {
  2015. /*
  2016. if this packet did not count against FIFO credit, it must have
  2017. taken a requested_credit from the destination entry (for pspoll etc.)
  2018. */
  2019. if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf))) {
  2020. entry->requested_credit++;
  2021. #if defined(DHD_WLFC_THREAD)
  2022. _dhd_wlfc_thread_wakeup(dhd);
  2023. #endif /* DHD_WLFC_THREAD */
  2024. }
  2025. #ifdef PROP_TXSTATUS_DEBUG
  2026. entry->dstncredit_acks++;
  2027. #endif // endif
  2028. }
  2029. if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) ||
  2030. (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) {
  2031. /* save generation bit inside packet */
  2032. WL_TXSTATUS_SET_GENERATION(DHD_PKTTAG_H2DTAG(PKTTAG(pktbuf)), gen);
  2033. if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
  2034. WL_SEQ_SET_REUSE(DHD_PKTTAG_H2DSEQ(PKTTAG(pktbuf)), seq_fromfw);
  2035. WL_SEQ_SET_NUM(DHD_PKTTAG_H2DSEQ(PKTTAG(pktbuf)), seq_num);
  2036. }
  2037. ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf);
  2038. if (ret != BCME_OK) {
  2039. /* delay q is full, drop this packet */
  2040. DHD_WLFC_QMON_COMPLETE(entry);
  2041. _dhd_wlfc_prec_drop(dhd, (fifo_id << 1) + 1, pktbuf, FALSE);
  2042. } else {
  2043. if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
  2044. /* Mark suppressed to avoid a double free
  2045. during wlfc cleanup
  2046. */
  2047. _dhd_wlfc_hanger_mark_suppressed(wlfc->hanger, hslot, gen);
  2048. }
  2049. }
  2050. } else {
  2051. DHD_WLFC_QMON_COMPLETE(entry);
  2052. if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
  2053. _dhd_wlfc_hanger_free_pkt(wlfc, hslot,
  2054. WLFC_HANGER_PKT_STATE_TXSTATUS, TRUE);
  2055. } else {
  2056. dhd_txcomplete(dhd, pktbuf, TRUE);
  2057. wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pktbuf))]
  2058. [DHD_PKTTAG_FIFO(PKTTAG(pktbuf))]--;
  2059. wlfc->stats.pktout++;
  2060. /* free the packet */
  2061. PKTFREE(wlfc->osh, pktbuf, TRUE);
  2062. }
  2063. }
  2064. /* pkt back from firmware side */
  2065. if (entry->transit_count)
  2066. entry->transit_count--;
  2067. if (entry->suppr_transit_count) {
  2068. entry->suppr_transit_count--;
  2069. if (entry->suppressed &&
  2070. (!entry->onbus_pkts_count) &&
  2071. (!entry->suppr_transit_count))
  2072. entry->suppressed = FALSE;
  2073. }
  2074. cont:
  2075. hcnt = (hcnt + 1) & WL_TXSTATUS_FREERUNCTR_MASK;
  2076. if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
  2077. hslot = (hslot + 1) & WL_TXSTATUS_HSLOT_MASK;
  2078. }
  2079. if (WLFC_GET_REUSESEQ(dhd->wlfc_mode) && seq_fromfw) {
  2080. seq_num = (seq_num + 1) & WL_SEQ_NUM_MASK;
  2081. }
  2082. count++;
  2083. }
  2084. return BCME_OK;
  2085. } /* _dhd_wlfc_compressed_txstatus_update */
  2086. /**
  2087. * Called when eg host receives a 'WLFC_CTL_TYPE_FIFO_CREDITBACK' event from the dongle.
  2088. * @param[in] credits caller supplied credit that will be added to the host credit.
  2089. */
  2090. static int
  2091. _dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8* credits)
  2092. {
  2093. int i;
  2094. athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
  2095. for (i = 0; i < WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK; i++) {
  2096. #ifdef PROP_TXSTATUS_DEBUG
  2097. wlfc->stats.fifo_credits_back[i] += credits[i];
  2098. #endif // endif
  2099. /* update FIFO credits */
  2100. if (dhd->proptxstatus_mode == WLFC_FCMODE_EXPLICIT_CREDIT)
  2101. {
  2102. int lender; /* Note that borrower is i */
  2103. /* Return credits to highest priority lender first */
  2104. for (lender = AC_COUNT; (lender >= 0) && (credits[i] > 0); lender--) {
  2105. if (wlfc->credits_borrowed[i][lender] > 0) {
  2106. if (credits[i] >= wlfc->credits_borrowed[i][lender]) {
  2107. credits[i] -=
  2108. (uint8)wlfc->credits_borrowed[i][lender];
  2109. wlfc->FIFO_credit[lender] +=
  2110. wlfc->credits_borrowed[i][lender];
  2111. wlfc->credits_borrowed[i][lender] = 0;
  2112. } else {
  2113. wlfc->credits_borrowed[i][lender] -= credits[i];
  2114. wlfc->FIFO_credit[lender] += credits[i];
  2115. credits[i] = 0;
  2116. }
  2117. }
  2118. }
  2119. /* If we have more credits left over, these must belong to the AC */
  2120. if (credits[i] > 0) {
  2121. wlfc->FIFO_credit[i] += credits[i];
  2122. }
  2123. if (wlfc->FIFO_credit[i] > wlfc->Init_FIFO_credit[i]) {
  2124. wlfc->FIFO_credit[i] = wlfc->Init_FIFO_credit[i];
  2125. }
  2126. }
  2127. }
  2128. #if defined(DHD_WLFC_THREAD)
  2129. _dhd_wlfc_thread_wakeup(dhd);
  2130. #endif /* defined(DHD_WLFC_THREAD) */
  2131. return BCME_OK;
  2132. } /* _dhd_wlfc_fifocreditback_indicate */
  2133. /** !BCMDBUS specific function */
  2134. static void
  2135. _dhd_wlfc_suppress_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
  2136. {
  2137. athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
  2138. wlfc_mac_descriptor_t* entry;
  2139. int prec;
  2140. void *pkt = NULL, *head = NULL, *tail = NULL;
  2141. struct pktq *txq = (struct pktq *)dhd_bus_txq(dhd->bus);
  2142. uint8 results[WLFC_CTL_VALUE_LEN_TXSTATUS+WLFC_CTL_VALUE_LEN_SEQ];
  2143. uint8 credits[WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK] = {0};
  2144. uint32 htod = 0;
  2145. uint16 htodseq = 0;
  2146. bool bCreditUpdate = FALSE;
  2147. dhd_os_sdlock_txq(dhd);
  2148. for (prec = 0; prec < txq->num_prec; prec++) {
  2149. while ((pkt = _dhd_wlfc_pktq_pdeq_with_fn(txq, prec, fn, arg))) {
  2150. if (!head) {
  2151. head = pkt;
  2152. }
  2153. if (tail) {
  2154. PKTSETLINK(tail, pkt);
  2155. }
  2156. tail = pkt;
  2157. }
  2158. }
  2159. dhd_os_sdunlock_txq(dhd);
  2160. while ((pkt = head)) {
  2161. head = PKTLINK(pkt);
  2162. PKTSETLINK(pkt, NULL);
  2163. entry = _dhd_wlfc_find_table_entry(wlfc, pkt);
  2164. if (!entry) {
  2165. PKTFREE(dhd->osh, pkt, TRUE);
  2166. continue;
  2167. }
  2168. if (entry->onbus_pkts_count > 0) {
  2169. entry->onbus_pkts_count--;
  2170. }
  2171. if (entry->suppressed &&
  2172. (!entry->onbus_pkts_count) &&
  2173. (!entry->suppr_transit_count)) {
  2174. entry->suppressed = FALSE;
  2175. }
  2176. /* fake a suppression txstatus */
  2177. htod = DHD_PKTTAG_H2DTAG(PKTTAG(pkt));
  2178. WL_TXSTATUS_SET_FLAGS(htod, WLFC_CTL_PKTFLAG_WLSUPPRESS);
  2179. WL_TXSTATUS_SET_GENERATION(htod, entry->generation);
  2180. htod = htol32(htod);
  2181. memcpy(results, &htod, WLFC_CTL_VALUE_LEN_TXSTATUS);
  2182. if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
  2183. htodseq = DHD_PKTTAG_H2DSEQ(PKTTAG(pkt));
  2184. if (IS_WL_TO_REUSE_SEQ(htodseq)) {
  2185. SET_WL_HAS_ASSIGNED_SEQ(htodseq);
  2186. RESET_WL_TO_REUSE_SEQ(htodseq);
  2187. }
  2188. htodseq = htol16(htodseq);
  2189. memcpy(results + WLFC_CTL_VALUE_LEN_TXSTATUS, &htodseq,
  2190. WLFC_CTL_VALUE_LEN_SEQ);
  2191. }
  2192. if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
  2193. _dhd_wlfc_enque_afq(wlfc, pkt);
  2194. }
  2195. _dhd_wlfc_compressed_txstatus_update(dhd, results, 1, NULL);
  2196. /* fake a fifo credit back */
  2197. if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pkt))) {
  2198. credits[DHD_PKTTAG_FIFO(PKTTAG(pkt))]++;
  2199. bCreditUpdate = TRUE;
  2200. }
  2201. }
  2202. if (bCreditUpdate) {
  2203. _dhd_wlfc_fifocreditback_indicate(dhd, credits);
  2204. }
  2205. } /* _dhd_wlfc_suppress_txq */
  2206. static int
  2207. _dhd_wlfc_dbg_senum_check(dhd_pub_t *dhd, uint8 *value)
  2208. {
  2209. uint32 timestamp;
  2210. (void)dhd;
  2211. bcopy(&value[2], &timestamp, sizeof(uint32));
  2212. timestamp = ltoh32(timestamp);
  2213. DHD_INFO(("RXPKT: SEQ: %d, timestamp %d\n", value[1], timestamp));
  2214. return BCME_OK;
  2215. }
  2216. static int
  2217. _dhd_wlfc_rssi_indicate(dhd_pub_t *dhd, uint8* rssi)
  2218. {
  2219. (void)dhd;
  2220. (void)rssi;
  2221. return BCME_OK;
  2222. }
  2223. static void
  2224. _dhd_wlfc_add_requested_entry(athost_wl_status_info_t* wlfc, wlfc_mac_descriptor_t* entry)
  2225. {
  2226. int i;
  2227. if (!wlfc || !entry) {
  2228. return;
  2229. }
  2230. for (i = 0; i < wlfc->requested_entry_count; i++) {
  2231. if (entry == wlfc->requested_entry[i]) {
  2232. break;
  2233. }
  2234. }
  2235. if (i == wlfc->requested_entry_count) {
  2236. /* no match entry found */
  2237. ASSERT(wlfc->requested_entry_count <= (WLFC_MAC_DESC_TABLE_SIZE-1));
  2238. wlfc->requested_entry[wlfc->requested_entry_count++] = entry;
  2239. }
  2240. }
  2241. /** called on eg receiving 'mac open' event from the dongle. */
  2242. static void
  2243. _dhd_wlfc_remove_requested_entry(athost_wl_status_info_t* wlfc, wlfc_mac_descriptor_t* entry)
  2244. {
  2245. int i;
  2246. if (!wlfc || !entry) {
  2247. return;
  2248. }
  2249. for (i = 0; i < wlfc->requested_entry_count; i++) {
  2250. if (entry == wlfc->requested_entry[i]) {
  2251. break;
  2252. }
  2253. }
  2254. if (i < wlfc->requested_entry_count) {
  2255. /* found */
  2256. ASSERT(wlfc->requested_entry_count > 0);
  2257. wlfc->requested_entry_count--;
  2258. if (i != wlfc->requested_entry_count) {
  2259. wlfc->requested_entry[i] =
  2260. wlfc->requested_entry[wlfc->requested_entry_count];
  2261. }
  2262. wlfc->requested_entry[wlfc->requested_entry_count] = NULL;
  2263. }
  2264. }
  2265. /** called on eg receiving a WLFC_CTL_TYPE_MACDESC_ADD TLV from the dongle */
  2266. static int
  2267. _dhd_wlfc_mac_table_update(dhd_pub_t *dhd, uint8* value, uint8 type)
  2268. {
  2269. int rc;
  2270. athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
  2271. wlfc_mac_descriptor_t* table;
  2272. uint8 existing_index;
  2273. uint8 table_index;
  2274. uint8 ifid;
  2275. uint8* ea;
  2276. WLFC_DBGMESG(("%s(), mac ["MACDBG"],%s,idx:%d,id:0x%02x\n",
  2277. __FUNCTION__, MAC2STRDBG(&value[2]),
  2278. ((type == WLFC_CTL_TYPE_MACDESC_ADD) ? "ADD":"DEL"),
  2279. WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]), value[0]));
  2280. table = wlfc->destination_entries.nodes;
  2281. table_index = WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]);
  2282. ifid = value[1];
  2283. ea = &value[2];
  2284. _dhd_wlfc_remove_requested_entry(wlfc, &table[table_index]);
  2285. if (type == WLFC_CTL_TYPE_MACDESC_ADD) {
  2286. existing_index = _dhd_wlfc_find_mac_desc_id_from_mac(dhd, &value[2]);
  2287. if ((existing_index != WLFC_MAC_DESC_ID_INVALID) &&
  2288. (existing_index != table_index) && table[existing_index].occupied) {
  2289. /*
  2290. there is an existing different entry, free the old one
  2291. and move it to new index if necessary.
  2292. */
  2293. rc = _dhd_wlfc_mac_entry_update(wlfc, &table[existing_index],
  2294. eWLFC_MAC_ENTRY_ACTION_DEL, table[existing_index].interface_id,
  2295. table[existing_index].iftype, NULL, _dhd_wlfc_entrypkt_fn,
  2296. &table[existing_index]);
  2297. }
  2298. if (!table[table_index].occupied) {
  2299. /* this new MAC entry does not exist, create one */
  2300. table[table_index].mac_handle = value[0];
  2301. rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
  2302. eWLFC_MAC_ENTRY_ACTION_ADD, ifid,
  2303. wlfc->destination_entries.interfaces[ifid].iftype,
  2304. ea, NULL, NULL);
  2305. } else {
  2306. /* the space should have been empty, but it's not */
  2307. wlfc->stats.mac_update_failed++;
  2308. }
  2309. }
  2310. if (type == WLFC_CTL_TYPE_MACDESC_DEL) {
  2311. if (table[table_index].occupied) {
  2312. rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
  2313. eWLFC_MAC_ENTRY_ACTION_DEL, ifid,
  2314. wlfc->destination_entries.interfaces[ifid].iftype,
  2315. ea, _dhd_wlfc_entrypkt_fn, &table[table_index]);
  2316. } else {
  2317. /* the space should have been occupied, but it's not */
  2318. wlfc->stats.mac_update_failed++;
  2319. }
  2320. }
  2321. BCM_REFERENCE(rc);
  2322. return BCME_OK;
  2323. } /* _dhd_wlfc_mac_table_update */
  2324. /** Called on a 'mac open' or 'mac close' event indicated by the dongle */
  2325. static int
  2326. _dhd_wlfc_psmode_update(dhd_pub_t *dhd, uint8* value, uint8 type)
  2327. {
  2328. /* Handle PS on/off indication */
  2329. athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
  2330. wlfc_mac_descriptor_t* table;
  2331. wlfc_mac_descriptor_t* desc; /* a table maps from mac handle to mac descriptor */
  2332. uint8 mac_handle = value[0];
  2333. int i;
  2334. table = wlfc->destination_entries.nodes;
  2335. desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
  2336. if (desc->occupied) {
  2337. if (type == WLFC_CTL_TYPE_MAC_OPEN) {
  2338. desc->state = WLFC_STATE_OPEN;
  2339. desc->ac_bitmap = 0xff;
  2340. DHD_WLFC_CTRINC_MAC_OPEN(desc);
  2341. desc->requested_credit = 0;
  2342. desc->requested_packet = 0;
  2343. _dhd_wlfc_remove_requested_entry(wlfc, desc);
  2344. } else {
  2345. desc->state = WLFC_STATE_CLOSE;
  2346. DHD_WLFC_CTRINC_MAC_CLOSE(desc);
  2347. /* Indicate to firmware if there is any traffic pending. */
  2348. for (i = 0; i < AC_COUNT; i++) {
  2349. _dhd_wlfc_traffic_pending_check(wlfc, desc, i);
  2350. }
  2351. }
  2352. } else {
  2353. wlfc->stats.psmode_update_failed++;
  2354. }
  2355. return BCME_OK;
  2356. } /* _dhd_wlfc_psmode_update */
  2357. /** called upon receiving 'interface open' or 'interface close' event from the dongle */
  2358. static int
  2359. _dhd_wlfc_interface_update(dhd_pub_t *dhd, uint8* value, uint8 type)
  2360. {
  2361. /* Handle PS on/off indication */
  2362. athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
  2363. wlfc_mac_descriptor_t* table;
  2364. uint8 if_id = value[0];
  2365. if (if_id < WLFC_MAX_IFNUM) {
  2366. table = wlfc->destination_entries.interfaces;
  2367. if (table[if_id].occupied) {
  2368. if (type == WLFC_CTL_TYPE_INTERFACE_OPEN) {
  2369. table[if_id].state = WLFC_STATE_OPEN;
  2370. /* WLFC_DBGMESG(("INTERFACE[%d] OPEN\n", if_id)); */
  2371. } else {
  2372. table[if_id].state = WLFC_STATE_CLOSE;
  2373. /* WLFC_DBGMESG(("INTERFACE[%d] CLOSE\n", if_id)); */
  2374. }
  2375. return BCME_OK;
  2376. }
  2377. }
  2378. wlfc->stats.interface_update_failed++;
  2379. return BCME_OK;
  2380. }
  2381. /** Called on receiving a WLFC_CTL_TYPE_MAC_REQUEST_CREDIT TLV from the dongle */
  2382. static int
  2383. _dhd_wlfc_credit_request(dhd_pub_t *dhd, uint8* value)
  2384. {
  2385. athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
  2386. wlfc_mac_descriptor_t* table;
  2387. wlfc_mac_descriptor_t* desc;
  2388. uint8 mac_handle;
  2389. uint8 credit;
  2390. table = wlfc->destination_entries.nodes;
  2391. mac_handle = value[1];
  2392. credit = value[0];
  2393. desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
  2394. if (desc->occupied) {
  2395. desc->requested_credit = credit;
  2396. desc->ac_bitmap = value[2] & (~(1<<AC_COUNT));
  2397. _dhd_wlfc_add_requested_entry(wlfc, desc);
  2398. #if defined(DHD_WLFC_THREAD)
  2399. if (credit) {
  2400. _dhd_wlfc_thread_wakeup(dhd);
  2401. }
  2402. #endif /* DHD_WLFC_THREAD */
  2403. } else {
  2404. wlfc->stats.credit_request_failed++;
  2405. }
  2406. return BCME_OK;
  2407. }
  2408. /** Called on receiving a WLFC_CTL_TYPE_MAC_REQUEST_PACKET TLV from the dongle */
  2409. static int
  2410. _dhd_wlfc_packet_request(dhd_pub_t *dhd, uint8* value)
  2411. {
  2412. athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
  2413. wlfc_mac_descriptor_t* table;
  2414. wlfc_mac_descriptor_t* desc;
  2415. uint8 mac_handle;
  2416. uint8 packet_count;
  2417. table = wlfc->destination_entries.nodes;
  2418. mac_handle = value[1];
  2419. packet_count = value[0];
  2420. desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
  2421. if (desc->occupied) {
  2422. desc->requested_packet = packet_count;
  2423. desc->ac_bitmap = value[2] & (~(1<<AC_COUNT));
  2424. _dhd_wlfc_add_requested_entry(wlfc, desc);
  2425. #if defined(DHD_WLFC_THREAD)
  2426. if (packet_count) {
  2427. _dhd_wlfc_thread_wakeup(dhd);
  2428. }
  2429. #endif /* DHD_WLFC_THREAD */
  2430. } else {
  2431. wlfc->stats.packet_request_failed++;
  2432. }
  2433. return BCME_OK;
  2434. }
  2435. /** Called when host receives a WLFC_CTL_TYPE_HOST_REORDER_RXPKTS TLV from the dongle */
  2436. static void
  2437. _dhd_wlfc_reorderinfo_indicate(uint8 *val, uint8 len, uchar *info_buf, uint *info_len)
  2438. {
  2439. if (info_len) {
  2440. /* Check copy length to avoid buffer overrun. In case of length exceeding
  2441. * WLHOST_REORDERDATA_TOTLEN, return failure instead sending incomplete result
  2442. * of length WLHOST_REORDERDATA_TOTLEN
  2443. */
  2444. if ((info_buf) && (len <= WLHOST_REORDERDATA_TOTLEN)) {
  2445. bcopy(val, info_buf, len);
  2446. *info_len = len;
  2447. } else {
  2448. *info_len = 0;
  2449. }
  2450. }
  2451. }
  2452. /*
  2453. * public functions
  2454. */
  2455. bool dhd_wlfc_is_supported(dhd_pub_t *dhd)
  2456. {
  2457. bool rc = TRUE;
  2458. if (dhd == NULL) {
  2459. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  2460. return FALSE;
  2461. }
  2462. dhd_os_wlfc_block(dhd);
  2463. if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
  2464. rc = FALSE;
  2465. }
  2466. dhd_os_wlfc_unblock(dhd);
  2467. return rc;
  2468. }
  2469. int dhd_wlfc_enable(dhd_pub_t *dhd)
  2470. {
  2471. int i, rc = BCME_OK;
  2472. athost_wl_status_info_t* wlfc;
  2473. if (dhd == NULL) {
  2474. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  2475. return BCME_BADARG;
  2476. }
  2477. dhd_os_wlfc_block(dhd);
  2478. if (!dhd->wlfc_enabled || dhd->wlfc_state) {
  2479. rc = BCME_OK;
  2480. goto exit;
  2481. }
  2482. /* allocate space to track txstatus propagated from firmware */
  2483. dhd->wlfc_state = DHD_OS_PREALLOC(dhd, DHD_PREALLOC_DHD_WLFC_INFO,
  2484. sizeof(athost_wl_status_info_t));
  2485. if (dhd->wlfc_state == NULL) {
  2486. rc = BCME_NOMEM;
  2487. goto exit;
  2488. }
  2489. /* initialize state space */
  2490. wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
  2491. memset(wlfc, 0, sizeof(athost_wl_status_info_t));
  2492. /* remember osh & dhdp */
  2493. wlfc->osh = dhd->osh;
  2494. wlfc->dhdp = dhd;
  2495. if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
  2496. wlfc->hanger = _dhd_wlfc_hanger_create(dhd, WLFC_HANGER_MAXITEMS);
  2497. if (wlfc->hanger == NULL) {
  2498. DHD_OS_PREFREE(dhd, dhd->wlfc_state,
  2499. sizeof(athost_wl_status_info_t));
  2500. dhd->wlfc_state = NULL;
  2501. rc = BCME_NOMEM;
  2502. goto exit;
  2503. }
  2504. }
  2505. dhd->proptxstatus_mode = WLFC_FCMODE_EXPLICIT_CREDIT;
  2506. /* default to check rx pkt */
  2507. dhd->wlfc_rxpkt_chk = TRUE;
  2508. if (dhd->op_mode & DHD_FLAG_IBSS_MODE) {
  2509. dhd->wlfc_rxpkt_chk = FALSE;
  2510. }
  2511. /* initialize all interfaces to accept traffic */
  2512. for (i = 0; i < WLFC_MAX_IFNUM; i++) {
  2513. wlfc->hostif_flow_state[i] = OFF;
  2514. }
  2515. _dhd_wlfc_mac_entry_update(wlfc, &wlfc->destination_entries.other,
  2516. eWLFC_MAC_ENTRY_ACTION_ADD, 0xff, 0, NULL, NULL, NULL);
  2517. wlfc->allow_credit_borrow = 0;
  2518. wlfc->single_ac = 0;
  2519. wlfc->single_ac_timestamp = 0;
  2520. exit:
  2521. dhd_os_wlfc_unblock(dhd);
  2522. return rc;
  2523. } /* dhd_wlfc_enable */
  2524. #ifdef SUPPORT_P2P_GO_PS
  2525. /**
  2526. * Called when the host platform enters a lower power mode, eg right before a system hibernate.
  2527. * SUPPORT_P2P_GO_PS specific function.
  2528. */
  2529. int
  2530. dhd_wlfc_suspend(dhd_pub_t *dhd)
  2531. {
  2532. uint32 tlv = 0;
  2533. DHD_TRACE(("%s: masking wlfc events\n", __FUNCTION__));
  2534. if (!dhd->wlfc_enabled)
  2535. return -1;
  2536. if (!dhd_wl_ioctl_get_intiovar(dhd, "tlv", &tlv, WLC_GET_VAR, FALSE, 0))
  2537. return -1;
  2538. if ((tlv & (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS)) == 0)
  2539. return 0;
  2540. tlv &= ~(WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS);
  2541. if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0))
  2542. return -1;
  2543. return 0;
  2544. }
  2545. /**
  2546. * Called when the host platform resumes from a power management operation, eg resume after a
  2547. * system hibernate. SUPPORT_P2P_GO_PS specific function.
  2548. */
  2549. int
  2550. dhd_wlfc_resume(dhd_pub_t *dhd)
  2551. {
  2552. uint32 tlv = 0;
  2553. DHD_TRACE(("%s: unmasking wlfc events\n", __FUNCTION__));
  2554. if (!dhd->wlfc_enabled)
  2555. return -1;
  2556. if (!dhd_wl_ioctl_get_intiovar(dhd, "tlv", &tlv, WLC_GET_VAR, FALSE, 0))
  2557. return -1;
  2558. if ((tlv & (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS)) ==
  2559. (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS))
  2560. return 0;
  2561. tlv |= (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS);
  2562. if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0))
  2563. return -1;
  2564. return 0;
  2565. }
  2566. #endif /* SUPPORT_P2P_GO_PS */
  2567. /** A flow control header was received from firmware, containing one or more TLVs */
  2568. int
  2569. dhd_wlfc_parse_header_info(dhd_pub_t *dhd, void* pktbuf, int tlv_hdr_len, uchar *reorder_info_buf,
  2570. uint *reorder_info_len)
  2571. {
  2572. uint8 type, len;
  2573. uint8* value;
  2574. uint8* tmpbuf;
  2575. uint16 remainder = (uint16)tlv_hdr_len;
  2576. uint16 processed = 0;
  2577. athost_wl_status_info_t* wlfc = NULL;
  2578. void* entry;
  2579. if ((dhd == NULL) || (pktbuf == NULL)) {
  2580. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  2581. return BCME_BADARG;
  2582. }
  2583. dhd_os_wlfc_block(dhd);
  2584. if (dhd->proptxstatus_mode != WLFC_ONLY_AMPDU_HOSTREORDER) {
  2585. if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
  2586. dhd_os_wlfc_unblock(dhd);
  2587. return WLFC_UNSUPPORTED;
  2588. }
  2589. wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
  2590. }
  2591. tmpbuf = (uint8*)PKTDATA(dhd->osh, pktbuf);
  2592. if (remainder) {
  2593. while ((processed < (WLFC_MAX_PENDING_DATALEN * 2)) && (remainder > 0)) {
  2594. type = tmpbuf[processed];
  2595. if (type == WLFC_CTL_TYPE_FILLER) {
  2596. remainder -= 1;
  2597. processed += 1;
  2598. continue;
  2599. }
  2600. len = tmpbuf[processed + 1];
  2601. value = &tmpbuf[processed + 2];
  2602. if (remainder < (2 + len))
  2603. break;
  2604. remainder -= 2 + len;
  2605. processed += 2 + len;
  2606. entry = NULL;
  2607. DHD_INFO(("%s():%d type %d remainder %d processed %d\n",
  2608. __FUNCTION__, __LINE__, type, remainder, processed));
  2609. if (type == WLFC_CTL_TYPE_HOST_REORDER_RXPKTS)
  2610. _dhd_wlfc_reorderinfo_indicate(value, len, reorder_info_buf,
  2611. reorder_info_len);
  2612. if (wlfc == NULL) {
  2613. ASSERT(dhd->proptxstatus_mode == WLFC_ONLY_AMPDU_HOSTREORDER);
  2614. if (type != WLFC_CTL_TYPE_HOST_REORDER_RXPKTS &&
  2615. type != WLFC_CTL_TYPE_TRANS_ID)
  2616. DHD_INFO(("%s():%d dhd->wlfc_state is NULL yet!"
  2617. " type %d remainder %d processed %d\n",
  2618. __FUNCTION__, __LINE__, type, remainder, processed));
  2619. continue;
  2620. }
  2621. if (type == WLFC_CTL_TYPE_TXSTATUS) {
  2622. _dhd_wlfc_compressed_txstatus_update(dhd, value, 1, &entry);
  2623. } else if (type == WLFC_CTL_TYPE_COMP_TXSTATUS) {
  2624. uint8 compcnt_offset = WLFC_CTL_VALUE_LEN_TXSTATUS;
  2625. if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
  2626. compcnt_offset += WLFC_CTL_VALUE_LEN_SEQ;
  2627. }
  2628. _dhd_wlfc_compressed_txstatus_update(dhd, value,
  2629. value[compcnt_offset], &entry);
  2630. } else if (type == WLFC_CTL_TYPE_FIFO_CREDITBACK) {
  2631. _dhd_wlfc_fifocreditback_indicate(dhd, value);
  2632. } else if (type == WLFC_CTL_TYPE_RSSI) {
  2633. _dhd_wlfc_rssi_indicate(dhd, value);
  2634. } else if (type == WLFC_CTL_TYPE_MAC_REQUEST_CREDIT) {
  2635. _dhd_wlfc_credit_request(dhd, value);
  2636. } else if (type == WLFC_CTL_TYPE_MAC_REQUEST_PACKET) {
  2637. _dhd_wlfc_packet_request(dhd, value);
  2638. } else if ((type == WLFC_CTL_TYPE_MAC_OPEN) ||
  2639. (type == WLFC_CTL_TYPE_MAC_CLOSE)) {
  2640. _dhd_wlfc_psmode_update(dhd, value, type);
  2641. } else if ((type == WLFC_CTL_TYPE_MACDESC_ADD) ||
  2642. (type == WLFC_CTL_TYPE_MACDESC_DEL)) {
  2643. _dhd_wlfc_mac_table_update(dhd, value, type);
  2644. } else if (type == WLFC_CTL_TYPE_TRANS_ID) {
  2645. _dhd_wlfc_dbg_senum_check(dhd, value);
  2646. } else if ((type == WLFC_CTL_TYPE_INTERFACE_OPEN) ||
  2647. (type == WLFC_CTL_TYPE_INTERFACE_CLOSE)) {
  2648. _dhd_wlfc_interface_update(dhd, value, type);
  2649. }
  2650. if (entry && WLFC_GET_REORDERSUPP(dhd->wlfc_mode)) {
  2651. /* suppress all packets for this mac entry from bus->txq */
  2652. _dhd_wlfc_suppress_txq(dhd, _dhd_wlfc_entrypkt_fn, entry);
  2653. }
  2654. } /* while */
  2655. if (remainder != 0 && wlfc) {
  2656. /* trouble..., something is not right */
  2657. wlfc->stats.tlv_parse_failed++;
  2658. }
  2659. } /* if */
  2660. if (wlfc)
  2661. wlfc->stats.dhd_hdrpulls++;
  2662. dhd_os_wlfc_unblock(dhd);
  2663. return BCME_OK;
  2664. }
  2665. KERNEL_THREAD_RETURN_TYPE
  2666. dhd_wlfc_transfer_packets(void *data)
  2667. {
  2668. dhd_pub_t *dhdp = (dhd_pub_t *)data;
  2669. int ac, single_ac = 0, rc = BCME_OK;
  2670. dhd_wlfc_commit_info_t commit_info;
  2671. athost_wl_status_info_t* ctx;
  2672. int bus_retry_count = 0;
  2673. int pkt_send = 0;
  2674. int pkt_send_per_ac = 0;
  2675. uint8 tx_map = 0; /* packets (send + in queue), Bitmask for 4 ACs + BC/MC */
  2676. uint8 rx_map = 0; /* received packets, Bitmask for 4 ACs + BC/MC */
  2677. uint8 packets_map = 0; /* packets in queue, Bitmask for 4 ACs + BC/MC */
  2678. bool no_credit = FALSE;
  2679. int lender;
  2680. #if defined(DHD_WLFC_THREAD)
  2681. /* wait till someone wakeup me up, will change it at running time */
  2682. int wait_msec = msecs_to_jiffies(0xFFFFFFFF);
  2683. #endif /* defined(DHD_WLFC_THREAD) */
  2684. #if defined(DHD_WLFC_THREAD)
  2685. while (1) {
  2686. bus_retry_count = 0;
  2687. pkt_send = 0;
  2688. tx_map = 0;
  2689. rx_map = 0;
  2690. packets_map = 0;
  2691. wait_msec = wait_event_interruptible_timeout(dhdp->wlfc_wqhead,
  2692. dhdp->wlfc_thread_go, wait_msec);
  2693. if (kthread_should_stop()) {
  2694. break;
  2695. }
  2696. dhdp->wlfc_thread_go = FALSE;
  2697. dhd_os_wlfc_block(dhdp);
  2698. #endif /* defined(DHD_WLFC_THREAD) */
  2699. ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
  2700. #if defined(DHD_WLFC_THREAD)
  2701. if (!ctx)
  2702. goto exit;
  2703. #endif /* defined(DHD_WLFC_THREAD) */
  2704. memset(&commit_info, 0, sizeof(commit_info));
  2705. /*
  2706. Commit packets for regular AC traffic. Higher priority first.
  2707. First, use up FIFO credits available to each AC. Based on distribution
  2708. and credits left, borrow from other ACs as applicable
  2709. -NOTE:
  2710. If the bus between the host and firmware is overwhelmed by the
  2711. traffic from host, it is possible that higher priority traffic
  2712. starves the lower priority queue. If that occurs often, we may
  2713. have to employ weighted round-robin or ucode scheme to avoid
  2714. low priority packet starvation.
  2715. */
  2716. for (ac = AC_COUNT; ac >= 0; ac--) {
  2717. if (dhdp->wlfc_rxpkt_chk) {
  2718. /* check rx packet */
  2719. uint32 curr_t = OSL_SYSUPTIME(), delta;
  2720. delta = curr_t - ctx->rx_timestamp[ac];
  2721. if (delta < WLFC_RX_DETECTION_THRESHOLD_MS) {
  2722. rx_map |= (1 << ac);
  2723. }
  2724. }
  2725. if (ctx->pkt_cnt_per_ac[ac] == 0) {
  2726. continue;
  2727. }
  2728. tx_map |= (1 << ac);
  2729. single_ac = ac + 1;
  2730. pkt_send_per_ac = 0;
  2731. while ((FALSE == dhdp->proptxstatus_txoff) &&
  2732. (pkt_send_per_ac < WLFC_PACKET_BOUND)) {
  2733. /* packets from delayQ with less priority are fresh and
  2734. * they'd need header and have no MAC entry
  2735. */
  2736. no_credit = (ctx->FIFO_credit[ac] < 1);
  2737. if (dhdp->proptxstatus_credit_ignore ||
  2738. ((ac == AC_COUNT) && !ctx->bcmc_credit_supported)) {
  2739. no_credit = FALSE;
  2740. }
  2741. lender = -1;
  2742. #ifdef LIMIT_BORROW
  2743. if (no_credit && (ac < AC_COUNT) && (tx_map >= rx_map) &&
  2744. dhdp->wlfc_borrow_allowed) {
  2745. /* try borrow from lower priority */
  2746. lender = _dhd_wlfc_borrow_credit(ctx, ac - 1, ac, FALSE);
  2747. if (lender != -1) {
  2748. no_credit = FALSE;
  2749. }
  2750. }
  2751. #endif // endif
  2752. commit_info.needs_hdr = 1;
  2753. commit_info.mac_entry = NULL;
  2754. commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
  2755. &(commit_info.ac_fifo_credit_spent),
  2756. &(commit_info.needs_hdr),
  2757. &(commit_info.mac_entry),
  2758. no_credit);
  2759. commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
  2760. eWLFC_PKTTYPE_SUPPRESSED;
  2761. if (commit_info.p == NULL) {
  2762. #ifdef LIMIT_BORROW
  2763. if (lender != -1 && dhdp->wlfc_borrow_allowed) {
  2764. _dhd_wlfc_return_credit(ctx, lender, ac);
  2765. }
  2766. #endif // endif
  2767. break;
  2768. }
  2769. if (!dhdp->proptxstatus_credit_ignore && (lender == -1)) {
  2770. ASSERT(ctx->FIFO_credit[ac] >= commit_info.ac_fifo_credit_spent);
  2771. }
  2772. /* here we can ensure have credit or no credit needed */
  2773. rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
  2774. ctx->fcommit, ctx->commit_ctx);
  2775. /* Bus commits may fail (e.g. flow control); abort after retries */
  2776. if (rc == BCME_OK) {
  2777. pkt_send++;
  2778. pkt_send_per_ac++;
  2779. if (commit_info.ac_fifo_credit_spent && (lender == -1)) {
  2780. ctx->FIFO_credit[ac]--;
  2781. }
  2782. #ifdef LIMIT_BORROW
  2783. else if (!commit_info.ac_fifo_credit_spent && (lender != -1) &&
  2784. dhdp->wlfc_borrow_allowed) {
  2785. _dhd_wlfc_return_credit(ctx, lender, ac);
  2786. }
  2787. #endif // endif
  2788. } else {
  2789. #ifdef LIMIT_BORROW
  2790. if (lender != -1 && dhdp->wlfc_borrow_allowed) {
  2791. _dhd_wlfc_return_credit(ctx, lender, ac);
  2792. }
  2793. #endif // endif
  2794. bus_retry_count++;
  2795. if (bus_retry_count >= BUS_RETRIES) {
  2796. DHD_ERROR(("%s: bus error %d\n", __FUNCTION__, rc));
  2797. goto exit;
  2798. }
  2799. }
  2800. }
  2801. if (ctx->pkt_cnt_per_ac[ac]) {
  2802. packets_map |= (1 << ac);
  2803. }
  2804. }
  2805. if ((tx_map == 0) || dhdp->proptxstatus_credit_ignore) {
  2806. /* nothing send out or remain in queue */
  2807. rc = BCME_OK;
  2808. goto exit;
  2809. }
  2810. if (((tx_map & (tx_map - 1)) == 0) && (tx_map >= rx_map)) {
  2811. /* only one tx ac exist and no higher rx ac */
  2812. if ((single_ac == ctx->single_ac) && ctx->allow_credit_borrow) {
  2813. ac = single_ac - 1;
  2814. } else {
  2815. uint32 delta;
  2816. uint32 curr_t = OSL_SYSUPTIME();
  2817. if (single_ac != ctx->single_ac) {
  2818. /* new single ac traffic (first single ac or different single ac) */
  2819. ctx->allow_credit_borrow = 0;
  2820. ctx->single_ac_timestamp = curr_t;
  2821. ctx->single_ac = (uint8)single_ac;
  2822. rc = BCME_OK;
  2823. goto exit;
  2824. }
  2825. /* same ac traffic, check if it lasts enough time */
  2826. delta = curr_t - ctx->single_ac_timestamp;
  2827. if (delta >= WLFC_BORROW_DEFER_PERIOD_MS) {
  2828. /* wait enough time, can borrow now */
  2829. ctx->allow_credit_borrow = 1;
  2830. ac = single_ac - 1;
  2831. } else {
  2832. rc = BCME_OK;
  2833. goto exit;
  2834. }
  2835. }
  2836. } else {
  2837. /* If we have multiple AC traffic, turn off borrowing, mark time and bail out */
  2838. ctx->allow_credit_borrow = 0;
  2839. ctx->single_ac_timestamp = 0;
  2840. ctx->single_ac = 0;
  2841. rc = BCME_OK;
  2842. goto exit;
  2843. }
  2844. if (packets_map == 0) {
  2845. /* nothing to send, skip borrow */
  2846. rc = BCME_OK;
  2847. goto exit;
  2848. }
  2849. /* At this point, borrow all credits only for ac */
  2850. while (FALSE == dhdp->proptxstatus_txoff) {
  2851. #ifdef LIMIT_BORROW
  2852. if (dhdp->wlfc_borrow_allowed) {
  2853. if ((lender = _dhd_wlfc_borrow_credit(ctx, AC_COUNT, ac, TRUE)) == -1) {
  2854. break;
  2855. }
  2856. }
  2857. else
  2858. break;
  2859. #endif // endif
  2860. commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
  2861. &(commit_info.ac_fifo_credit_spent),
  2862. &(commit_info.needs_hdr),
  2863. &(commit_info.mac_entry),
  2864. FALSE);
  2865. if (commit_info.p == NULL) {
  2866. /* before borrow only one ac exists and now this only ac is empty */
  2867. #ifdef LIMIT_BORROW
  2868. _dhd_wlfc_return_credit(ctx, lender, ac);
  2869. #endif // endif
  2870. break;
  2871. }
  2872. commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
  2873. eWLFC_PKTTYPE_SUPPRESSED;
  2874. rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
  2875. ctx->fcommit, ctx->commit_ctx);
  2876. /* Bus commits may fail (e.g. flow control); abort after retries */
  2877. if (rc == BCME_OK) {
  2878. pkt_send++;
  2879. if (commit_info.ac_fifo_credit_spent) {
  2880. #ifndef LIMIT_BORROW
  2881. ctx->FIFO_credit[ac]--;
  2882. #endif // endif
  2883. } else {
  2884. #ifdef LIMIT_BORROW
  2885. _dhd_wlfc_return_credit(ctx, lender, ac);
  2886. #endif // endif
  2887. }
  2888. } else {
  2889. #ifdef LIMIT_BORROW
  2890. _dhd_wlfc_return_credit(ctx, lender, ac);
  2891. #endif // endif
  2892. bus_retry_count++;
  2893. if (bus_retry_count >= BUS_RETRIES) {
  2894. DHD_ERROR(("%s: bus error %d\n", __FUNCTION__, rc));
  2895. goto exit;
  2896. }
  2897. }
  2898. }
  2899. BCM_REFERENCE(pkt_send);
  2900. exit:
  2901. #if defined(DHD_WLFC_THREAD)
  2902. dhd_os_wlfc_unblock(dhdp);
  2903. if (ctx && ctx->pkt_cnt_in_psq && pkt_send) {
  2904. wait_msec = msecs_to_jiffies(WLFC_THREAD_QUICK_RETRY_WAIT_MS);
  2905. } else {
  2906. wait_msec = msecs_to_jiffies(WLFC_THREAD_RETRY_WAIT_MS);
  2907. }
  2908. }
  2909. return 0;
  2910. #else
  2911. return rc;
  2912. #endif /* defined(DHD_WLFC_THREAD) */
  2913. }
  2914. /**
  2915. * Enqueues a transmit packet in the next layer towards the dongle, eg the DBUS layer. Called by
  2916. * eg dhd_sendpkt().
  2917. * @param[in] dhdp Pointer to public DHD structure
  2918. * @param[in] fcommit Pointer to transmit function of next layer
  2919. * @param[in] commit_ctx Opaque context used when calling next layer
  2920. * @param[in] pktbuf Packet to send
  2921. * @param[in] need_toggle_host_if If TRUE, resets flag ctx->toggle_host_if
  2922. */
  2923. int
  2924. dhd_wlfc_commit_packets(dhd_pub_t *dhdp, f_commitpkt_t fcommit, void* commit_ctx, void *pktbuf,
  2925. bool need_toggle_host_if)
  2926. {
  2927. int rc = BCME_OK;
  2928. athost_wl_status_info_t* ctx;
  2929. #if defined(DHD_WLFC_THREAD)
  2930. if (!pktbuf)
  2931. return BCME_OK;
  2932. #endif /* defined(DHD_WLFC_THREAD) */
  2933. if ((dhdp == NULL) || (fcommit == NULL)) {
  2934. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  2935. return BCME_BADARG;
  2936. }
  2937. dhd_os_wlfc_block(dhdp);
  2938. if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
  2939. if (pktbuf) {
  2940. DHD_PKTTAG_WLFCPKT_SET(PKTTAG(pktbuf), 0);
  2941. }
  2942. rc = WLFC_UNSUPPORTED;
  2943. goto exit;
  2944. }
  2945. ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
  2946. if (dhdp->proptxstatus_module_ignore) {
  2947. if (pktbuf) {
  2948. uint32 htod = 0;
  2949. WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST);
  2950. _dhd_wlfc_pushheader(ctx, &pktbuf, FALSE, 0, 0, htod, 0, FALSE);
  2951. if (fcommit(commit_ctx, pktbuf)) {
  2952. /* free it if failed, otherwise do it in tx complete cb */
  2953. PKTFREE(ctx->osh, pktbuf, TRUE);
  2954. }
  2955. rc = BCME_OK;
  2956. }
  2957. goto exit;
  2958. }
  2959. if (pktbuf) {
  2960. int ac = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
  2961. ASSERT(ac <= AC_COUNT);
  2962. DHD_PKTTAG_WLFCPKT_SET(PKTTAG(pktbuf), 1);
  2963. /* en-queue the packets to respective queue. */
  2964. rc = _dhd_wlfc_enque_delayq(ctx, pktbuf, ac);
  2965. if (rc) {
  2966. _dhd_wlfc_prec_drop(ctx->dhdp, (ac << 1), pktbuf, FALSE);
  2967. } else {
  2968. ctx->stats.pktin++;
  2969. ctx->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pktbuf))][ac]++;
  2970. }
  2971. }
  2972. if (!ctx->fcommit) {
  2973. ctx->fcommit = fcommit;
  2974. } else {
  2975. ASSERT(ctx->fcommit == fcommit);
  2976. }
  2977. if (!ctx->commit_ctx) {
  2978. ctx->commit_ctx = commit_ctx;
  2979. } else {
  2980. ASSERT(ctx->commit_ctx == commit_ctx);
  2981. }
  2982. #if defined(DHD_WLFC_THREAD)
  2983. _dhd_wlfc_thread_wakeup(dhdp);
  2984. #else
  2985. dhd_wlfc_transfer_packets(dhdp);
  2986. #endif /* defined(DHD_WLFC_THREAD) */
  2987. exit:
  2988. dhd_os_wlfc_unblock(dhdp);
  2989. return rc;
  2990. } /* dhd_wlfc_commit_packets */
  2991. /**
  2992. * Called when the (lower) DBUS layer indicates completion (succesfull or not) of a transmit packet
  2993. */
  2994. int
  2995. dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success)
  2996. {
  2997. athost_wl_status_info_t* wlfc;
  2998. wlfc_mac_descriptor_t *entry;
  2999. void* pout = NULL;
  3000. int rtn = BCME_OK;
  3001. if ((dhd == NULL) || (txp == NULL)) {
  3002. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3003. return BCME_BADARG;
  3004. }
  3005. bcm_pkt_validate_chk(txp);
  3006. dhd_os_wlfc_block(dhd);
  3007. if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
  3008. rtn = WLFC_UNSUPPORTED;
  3009. goto EXIT;
  3010. }
  3011. wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
  3012. if (DHD_PKTTAG_SIGNALONLY(PKTTAG(txp))) {
  3013. #ifdef PROP_TXSTATUS_DEBUG
  3014. wlfc->stats.signal_only_pkts_freed++;
  3015. #endif // endif
  3016. /* is this a signal-only packet? */
  3017. _dhd_wlfc_pullheader(wlfc, txp);
  3018. PKTFREE(wlfc->osh, txp, TRUE);
  3019. goto EXIT;
  3020. }
  3021. entry = _dhd_wlfc_find_table_entry(wlfc, txp);
  3022. ASSERT(entry);
  3023. if (!success || dhd->proptxstatus_txstatus_ignore) {
  3024. WLFC_DBGMESG(("At: %s():%d, bus_complete() failure for %p, htod_tag:0x%08x\n",
  3025. __FUNCTION__, __LINE__, txp, DHD_PKTTAG_H2DTAG(PKTTAG(txp))));
  3026. if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
  3027. _dhd_wlfc_hanger_poppkt(wlfc->hanger, WL_TXSTATUS_GET_HSLOT(
  3028. DHD_PKTTAG_H2DTAG(PKTTAG(txp))), &pout, TRUE);
  3029. ASSERT(txp == pout);
  3030. }
  3031. /* indicate failure and free the packet */
  3032. dhd_txcomplete(dhd, txp, success);
  3033. /* return the credit, if necessary */
  3034. _dhd_wlfc_return_implied_credit(wlfc, txp);
  3035. if (entry->transit_count)
  3036. entry->transit_count--;
  3037. if (entry->suppr_transit_count)
  3038. entry->suppr_transit_count--;
  3039. wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(txp))][DHD_PKTTAG_FIFO(PKTTAG(txp))]--;
  3040. wlfc->stats.pktout++;
  3041. PKTFREE(wlfc->osh, txp, TRUE);
  3042. } else {
  3043. /* bus confirmed pkt went to firmware side */
  3044. if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
  3045. _dhd_wlfc_enque_afq(wlfc, txp);
  3046. } else {
  3047. int hslot = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(txp)));
  3048. _dhd_wlfc_hanger_free_pkt(wlfc, hslot,
  3049. WLFC_HANGER_PKT_STATE_BUSRETURNED, -1);
  3050. }
  3051. }
  3052. ASSERT(entry->onbus_pkts_count > 0);
  3053. if (entry->onbus_pkts_count > 0)
  3054. entry->onbus_pkts_count--;
  3055. if (entry->suppressed &&
  3056. (!entry->onbus_pkts_count) &&
  3057. (!entry->suppr_transit_count))
  3058. entry->suppressed = FALSE;
  3059. EXIT:
  3060. dhd_os_wlfc_unblock(dhd);
  3061. return rtn;
  3062. } /* dhd_wlfc_txcomplete */
  3063. int
  3064. dhd_wlfc_init(dhd_pub_t *dhd)
  3065. {
  3066. /* enable all signals & indicate host proptxstatus logic is active */
  3067. uint32 tlv, mode, fw_caps;
  3068. int ret = 0;
  3069. if (dhd == NULL) {
  3070. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3071. return BCME_BADARG;
  3072. }
  3073. dhd_os_wlfc_block(dhd);
  3074. if (dhd->wlfc_enabled) {
  3075. DHD_INFO(("%s():%d, Already enabled!\n", __FUNCTION__, __LINE__));
  3076. dhd_os_wlfc_unblock(dhd);
  3077. return BCME_OK;
  3078. }
  3079. dhd->wlfc_enabled = TRUE;
  3080. dhd_os_wlfc_unblock(dhd);
  3081. tlv = WLFC_FLAGS_RSSI_SIGNALS |
  3082. WLFC_FLAGS_XONXOFF_SIGNALS |
  3083. WLFC_FLAGS_CREDIT_STATUS_SIGNALS |
  3084. WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
  3085. WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
  3086. /*
  3087. try to enable/disable signaling by sending "tlv" iovar. if that fails,
  3088. fallback to no flow control? Print a message for now.
  3089. */
  3090. /* enable proptxtstatus signaling by default */
  3091. if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
  3092. /*
  3093. Leaving the message for now, it should be removed after a while; once
  3094. the tlv situation is stable.
  3095. */
  3096. DHD_INFO(("dhd_wlfc_init(): successfully %s bdcv2 tlv signaling, %d\n",
  3097. dhd->wlfc_enabled?"enabled":"disabled", tlv));
  3098. }
  3099. mode = 0;
  3100. /* query caps */
  3101. ret = dhd_wl_ioctl_get_intiovar(dhd, "wlfc_mode", &fw_caps, WLC_GET_VAR, FALSE, 0);
  3102. if (!ret) {
  3103. DHD_INFO(("%s: query wlfc_mode succeed, fw_caps=0x%x\n", __FUNCTION__, fw_caps));
  3104. if (WLFC_IS_OLD_DEF(fw_caps)) {
  3105. /* enable proptxtstatus v2 by default */
  3106. mode = WLFC_MODE_AFQ;
  3107. } else {
  3108. WLFC_SET_AFQ(mode, WLFC_GET_AFQ(fw_caps));
  3109. WLFC_SET_REORDERSUPP(mode, WLFC_GET_REORDERSUPP(fw_caps));
  3110. }
  3111. ret = dhd_wl_ioctl_set_intiovar(dhd, "wlfc_mode", mode, WLC_SET_VAR, TRUE, 0);
  3112. }
  3113. dhd_os_wlfc_block(dhd);
  3114. dhd->wlfc_mode = 0;
  3115. if (ret >= 0) {
  3116. if (WLFC_IS_OLD_DEF(mode)) {
  3117. WLFC_SET_AFQ(dhd->wlfc_mode, (mode == WLFC_MODE_AFQ));
  3118. } else {
  3119. dhd->wlfc_mode = mode;
  3120. }
  3121. }
  3122. DHD_INFO(("dhd_wlfc_init(): wlfc_mode=0x%x, ret=%d\n", dhd->wlfc_mode, ret));
  3123. #ifdef LIMIT_BORROW
  3124. dhd->wlfc_borrow_allowed = TRUE;
  3125. #endif // endif
  3126. dhd_os_wlfc_unblock(dhd);
  3127. if (dhd->plat_init)
  3128. dhd->plat_init((void *)dhd);
  3129. return BCME_OK;
  3130. } /* dhd_wlfc_init */
  3131. /** AMPDU host reorder specific function */
  3132. int
  3133. dhd_wlfc_hostreorder_init(dhd_pub_t *dhd)
  3134. {
  3135. /* enable only ampdu hostreorder here */
  3136. uint32 tlv;
  3137. if (dhd == NULL) {
  3138. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3139. return BCME_BADARG;
  3140. }
  3141. DHD_TRACE(("%s():%d Enter\n", __FUNCTION__, __LINE__));
  3142. tlv = WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
  3143. /* enable proptxtstatus signaling by default */
  3144. if (dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
  3145. DHD_ERROR(("%s(): failed to enable/disable bdcv2 tlv signaling\n",
  3146. __FUNCTION__));
  3147. } else {
  3148. /*
  3149. Leaving the message for now, it should be removed after a while; once
  3150. the tlv situation is stable.
  3151. */
  3152. DHD_ERROR(("%s(): successful bdcv2 tlv signaling, %d\n",
  3153. __FUNCTION__, tlv));
  3154. }
  3155. dhd_os_wlfc_block(dhd);
  3156. dhd->proptxstatus_mode = WLFC_ONLY_AMPDU_HOSTREORDER;
  3157. dhd_os_wlfc_unblock(dhd);
  3158. return BCME_OK;
  3159. }
  3160. int
  3161. dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
  3162. {
  3163. if (dhd == NULL) {
  3164. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3165. return BCME_BADARG;
  3166. }
  3167. dhd_os_wlfc_block(dhd);
  3168. if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
  3169. dhd_os_wlfc_unblock(dhd);
  3170. return WLFC_UNSUPPORTED;
  3171. }
  3172. _dhd_wlfc_cleanup_txq(dhd, fn, arg);
  3173. dhd_os_wlfc_unblock(dhd);
  3174. return BCME_OK;
  3175. }
  3176. /** release all packet resources */
  3177. int
  3178. dhd_wlfc_cleanup(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
  3179. {
  3180. if (dhd == NULL) {
  3181. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3182. return BCME_BADARG;
  3183. }
  3184. dhd_os_wlfc_block(dhd);
  3185. if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
  3186. dhd_os_wlfc_unblock(dhd);
  3187. return WLFC_UNSUPPORTED;
  3188. }
  3189. _dhd_wlfc_cleanup(dhd, fn, arg);
  3190. dhd_os_wlfc_unblock(dhd);
  3191. return BCME_OK;
  3192. }
  3193. int
  3194. dhd_wlfc_deinit(dhd_pub_t *dhd)
  3195. {
  3196. /* cleanup all psq related resources */
  3197. athost_wl_status_info_t* wlfc;
  3198. uint32 tlv = 0;
  3199. uint32 hostreorder = 0;
  3200. if (dhd == NULL) {
  3201. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3202. return BCME_BADARG;
  3203. }
  3204. dhd_os_wlfc_block(dhd);
  3205. if (!dhd->wlfc_enabled) {
  3206. DHD_ERROR(("%s():%d, Already disabled!\n", __FUNCTION__, __LINE__));
  3207. dhd_os_wlfc_unblock(dhd);
  3208. return BCME_OK;
  3209. }
  3210. dhd->wlfc_enabled = FALSE;
  3211. dhd_os_wlfc_unblock(dhd);
  3212. /* query ampdu hostreorder */
  3213. (void) dhd_wl_ioctl_get_intiovar(dhd, "ampdu_hostreorder",
  3214. &hostreorder, WLC_GET_VAR, FALSE, 0);
  3215. if (hostreorder) {
  3216. tlv = WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
  3217. DHD_ERROR(("%s():%d, maintain HOST RXRERODER flag in tvl\n",
  3218. __FUNCTION__, __LINE__));
  3219. }
  3220. /* Disable proptxtstatus signaling for deinit */
  3221. (void) dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0);
  3222. dhd_os_wlfc_block(dhd);
  3223. if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
  3224. dhd_os_wlfc_unblock(dhd);
  3225. return WLFC_UNSUPPORTED;
  3226. }
  3227. wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
  3228. _dhd_wlfc_cleanup(dhd, NULL, NULL);
  3229. if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
  3230. int i;
  3231. wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
  3232. for (i = 0; i < h->max_items; i++) {
  3233. if (h->items[i].state != WLFC_HANGER_ITEM_STATE_FREE) {
  3234. _dhd_wlfc_hanger_free_pkt(wlfc, i,
  3235. WLFC_HANGER_PKT_STATE_COMPLETE, TRUE);
  3236. }
  3237. }
  3238. /* delete hanger */
  3239. _dhd_wlfc_hanger_delete(dhd, h);
  3240. }
  3241. /* free top structure */
  3242. DHD_OS_PREFREE(dhd, dhd->wlfc_state,
  3243. sizeof(athost_wl_status_info_t));
  3244. dhd->wlfc_state = NULL;
  3245. dhd->proptxstatus_mode = hostreorder ?
  3246. WLFC_ONLY_AMPDU_HOSTREORDER : WLFC_FCMODE_NONE;
  3247. dhd_os_wlfc_unblock(dhd);
  3248. if (dhd->plat_deinit)
  3249. dhd->plat_deinit((void *)dhd);
  3250. return BCME_OK;
  3251. } /* dhd_wlfc_init */
  3252. /**
  3253. * Called on an interface event (WLC_E_IF) indicated by firmware
  3254. * @param[in] dhdp Pointer to public DHD structure
  3255. * @param[in] action eg eWLFC_MAC_ENTRY_ACTION_UPDATE or eWLFC_MAC_ENTRY_ACTION_ADD
  3256. */
  3257. int dhd_wlfc_interface_event(dhd_pub_t *dhdp, uint8 action, uint8 ifid, uint8 iftype, uint8* ea)
  3258. {
  3259. int rc;
  3260. if (dhdp == NULL) {
  3261. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3262. return BCME_BADARG;
  3263. }
  3264. dhd_os_wlfc_block(dhdp);
  3265. if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
  3266. dhd_os_wlfc_unblock(dhdp);
  3267. return WLFC_UNSUPPORTED;
  3268. }
  3269. rc = _dhd_wlfc_interface_entry_update(dhdp->wlfc_state, action, ifid, iftype, ea);
  3270. dhd_os_wlfc_unblock(dhdp);
  3271. return rc;
  3272. }
  3273. /** Called eg on receiving a WLC_E_FIFO_CREDIT_MAP event from the dongle */
  3274. int dhd_wlfc_FIFOcreditmap_event(dhd_pub_t *dhdp, uint8* event_data)
  3275. {
  3276. int rc;
  3277. if (dhdp == NULL) {
  3278. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3279. return BCME_BADARG;
  3280. }
  3281. dhd_os_wlfc_block(dhdp);
  3282. if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
  3283. dhd_os_wlfc_unblock(dhdp);
  3284. return WLFC_UNSUPPORTED;
  3285. }
  3286. rc = _dhd_wlfc_FIFOcreditmap_update(dhdp->wlfc_state, event_data);
  3287. dhd_os_wlfc_unblock(dhdp);
  3288. return rc;
  3289. }
  3290. #ifdef LIMIT_BORROW
  3291. int dhd_wlfc_disable_credit_borrow_event(dhd_pub_t *dhdp, uint8* event_data)
  3292. {
  3293. if (dhdp == NULL || event_data == NULL) {
  3294. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3295. return BCME_BADARG;
  3296. }
  3297. dhd_os_wlfc_block(dhdp);
  3298. dhdp->wlfc_borrow_allowed = (bool)(*(uint32 *)event_data);
  3299. dhd_os_wlfc_unblock(dhdp);
  3300. return BCME_OK;
  3301. }
  3302. #endif /* LIMIT_BORROW */
  3303. /**
  3304. * Called eg on receiving a WLC_E_BCMC_CREDIT_SUPPORT event from the dongle (broadcast/multicast
  3305. * specific)
  3306. */
  3307. int dhd_wlfc_BCMCCredit_support_event(dhd_pub_t *dhdp)
  3308. {
  3309. int rc;
  3310. if (dhdp == NULL) {
  3311. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3312. return BCME_BADARG;
  3313. }
  3314. dhd_os_wlfc_block(dhdp);
  3315. if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
  3316. dhd_os_wlfc_unblock(dhdp);
  3317. return WLFC_UNSUPPORTED;
  3318. }
  3319. rc = _dhd_wlfc_BCMCCredit_support_update(dhdp->wlfc_state);
  3320. dhd_os_wlfc_unblock(dhdp);
  3321. return rc;
  3322. }
  3323. /** debug specific function */
  3324. int
  3325. dhd_wlfc_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
  3326. {
  3327. int i;
  3328. uint8* ea;
  3329. athost_wl_status_info_t* wlfc;
  3330. wlfc_hanger_t* h;
  3331. wlfc_mac_descriptor_t* mac_table;
  3332. wlfc_mac_descriptor_t* interfaces;
  3333. char* iftypes[] = {"STA", "AP", "WDS", "p2pGO", "p2pCL"};
  3334. if (!dhdp || !strbuf) {
  3335. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3336. return BCME_BADARG;
  3337. }
  3338. dhd_os_wlfc_block(dhdp);
  3339. if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
  3340. dhd_os_wlfc_unblock(dhdp);
  3341. return WLFC_UNSUPPORTED;
  3342. }
  3343. wlfc = (athost_wl_status_info_t*)dhdp->wlfc_state;
  3344. h = (wlfc_hanger_t*)wlfc->hanger;
  3345. if (h == NULL) {
  3346. bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n");
  3347. }
  3348. mac_table = wlfc->destination_entries.nodes;
  3349. interfaces = wlfc->destination_entries.interfaces;
  3350. bcm_bprintf(strbuf, "---- wlfc stats ----\n");
  3351. if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
  3352. h = (wlfc_hanger_t*)wlfc->hanger;
  3353. if (h == NULL) {
  3354. bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n");
  3355. } else {
  3356. bcm_bprintf(strbuf, "wlfc hanger (pushed,popped,f_push,"
  3357. "f_pop,f_slot, pending) = (%d,%d,%d,%d,%d,%d)\n",
  3358. h->pushed,
  3359. h->popped,
  3360. h->failed_to_push,
  3361. h->failed_to_pop,
  3362. h->failed_slotfind,
  3363. (h->pushed - h->popped));
  3364. }
  3365. }
  3366. bcm_bprintf(strbuf, "wlfc fail(tlv,credit_rqst,mac_update,psmode_update), "
  3367. "(dq_full,rollback_fail) = (%d,%d,%d,%d), (%d,%d)\n",
  3368. wlfc->stats.tlv_parse_failed,
  3369. wlfc->stats.credit_request_failed,
  3370. wlfc->stats.mac_update_failed,
  3371. wlfc->stats.psmode_update_failed,
  3372. wlfc->stats.delayq_full_error,
  3373. wlfc->stats.rollback_failed);
  3374. bcm_bprintf(strbuf, "PKTS (init_credit,credit,sent,drop_d,drop_s,outoforder) "
  3375. "(AC0[%d,%d,%d,%d,%d,%d],AC1[%d,%d,%d,%d,%d,%d],AC2[%d,%d,%d,%d,%d,%d],"
  3376. "AC3[%d,%d,%d,%d,%d,%d],BC_MC[%d,%d,%d,%d,%d,%d])\n",
  3377. wlfc->Init_FIFO_credit[0], wlfc->FIFO_credit[0], wlfc->stats.send_pkts[0],
  3378. wlfc->stats.drop_pkts[0], wlfc->stats.drop_pkts[1], wlfc->stats.ooo_pkts[0],
  3379. wlfc->Init_FIFO_credit[1], wlfc->FIFO_credit[1], wlfc->stats.send_pkts[1],
  3380. wlfc->stats.drop_pkts[2], wlfc->stats.drop_pkts[3], wlfc->stats.ooo_pkts[1],
  3381. wlfc->Init_FIFO_credit[2], wlfc->FIFO_credit[2], wlfc->stats.send_pkts[2],
  3382. wlfc->stats.drop_pkts[4], wlfc->stats.drop_pkts[5], wlfc->stats.ooo_pkts[2],
  3383. wlfc->Init_FIFO_credit[3], wlfc->FIFO_credit[3], wlfc->stats.send_pkts[3],
  3384. wlfc->stats.drop_pkts[6], wlfc->stats.drop_pkts[7], wlfc->stats.ooo_pkts[3],
  3385. wlfc->Init_FIFO_credit[4], wlfc->FIFO_credit[4], wlfc->stats.send_pkts[4],
  3386. wlfc->stats.drop_pkts[8], wlfc->stats.drop_pkts[9], wlfc->stats.ooo_pkts[4]);
  3387. bcm_bprintf(strbuf, "\n");
  3388. for (i = 0; i < WLFC_MAX_IFNUM; i++) {
  3389. if (interfaces[i].occupied) {
  3390. char* iftype_desc;
  3391. if (interfaces[i].iftype > WLC_E_IF_ROLE_P2P_CLIENT)
  3392. iftype_desc = "<Unknown";
  3393. else
  3394. iftype_desc = iftypes[interfaces[i].iftype];
  3395. ea = interfaces[i].ea;
  3396. bcm_bprintf(strbuf, "INTERFACE[%d].ea = "
  3397. "["MACDBG"], if:%d, type: %s "
  3398. "netif_flow_control:%s\n", i,
  3399. MAC2STRDBG(ea), interfaces[i].interface_id,
  3400. iftype_desc, ((wlfc->hostif_flow_state[i] == OFF)
  3401. ? " OFF":" ON"));
  3402. bcm_bprintf(strbuf, "INTERFACE[%d].PSQ(len,state,credit),"
  3403. "(trans,supp_trans,onbus)"
  3404. "= (%d,%s,%d),(%d,%d,%d)\n",
  3405. i,
  3406. interfaces[i].psq.n_pkts_tot,
  3407. ((interfaces[i].state ==
  3408. WLFC_STATE_OPEN) ? "OPEN":"CLOSE"),
  3409. interfaces[i].requested_credit,
  3410. interfaces[i].transit_count,
  3411. interfaces[i].suppr_transit_count,
  3412. interfaces[i].onbus_pkts_count);
  3413. bcm_bprintf(strbuf, "INTERFACE[%d].PSQ"
  3414. "(delay0,sup0,afq0),(delay1,sup1,afq1),(delay2,sup2,afq2),"
  3415. "(delay3,sup3,afq3),(delay4,sup4,afq4) = (%d,%d,%d),"
  3416. "(%d,%d,%d),(%d,%d,%d),(%d,%d,%d),(%d,%d,%d)\n",
  3417. i,
  3418. interfaces[i].psq.q[0].n_pkts,
  3419. interfaces[i].psq.q[1].n_pkts,
  3420. interfaces[i].afq.q[0].n_pkts,
  3421. interfaces[i].psq.q[2].n_pkts,
  3422. interfaces[i].psq.q[3].n_pkts,
  3423. interfaces[i].afq.q[1].n_pkts,
  3424. interfaces[i].psq.q[4].n_pkts,
  3425. interfaces[i].psq.q[5].n_pkts,
  3426. interfaces[i].afq.q[2].n_pkts,
  3427. interfaces[i].psq.q[6].n_pkts,
  3428. interfaces[i].psq.q[7].n_pkts,
  3429. interfaces[i].afq.q[3].n_pkts,
  3430. interfaces[i].psq.q[8].n_pkts,
  3431. interfaces[i].psq.q[9].n_pkts,
  3432. interfaces[i].afq.q[4].n_pkts);
  3433. }
  3434. }
  3435. bcm_bprintf(strbuf, "\n");
  3436. for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
  3437. if (mac_table[i].occupied) {
  3438. ea = mac_table[i].ea;
  3439. bcm_bprintf(strbuf, "MAC_table[%d].ea = "
  3440. "["MACDBG"], if:%d \n", i,
  3441. MAC2STRDBG(ea), mac_table[i].interface_id);
  3442. bcm_bprintf(strbuf, "MAC_table[%d].PSQ(len,state,credit),"
  3443. "(trans,supp_trans,onbus)"
  3444. "= (%d,%s,%d),(%d,%d,%d)\n",
  3445. i,
  3446. mac_table[i].psq.n_pkts_tot,
  3447. ((mac_table[i].state ==
  3448. WLFC_STATE_OPEN) ? " OPEN":"CLOSE"),
  3449. mac_table[i].requested_credit,
  3450. mac_table[i].transit_count,
  3451. mac_table[i].suppr_transit_count,
  3452. mac_table[i].onbus_pkts_count);
  3453. #ifdef PROP_TXSTATUS_DEBUG
  3454. bcm_bprintf(strbuf, "MAC_table[%d]: (opened, closed) = (%d, %d)\n",
  3455. i, mac_table[i].opened_ct, mac_table[i].closed_ct);
  3456. #endif // endif
  3457. bcm_bprintf(strbuf, "MAC_table[%d].PSQ"
  3458. "(delay0,sup0,afq0),(delay1,sup1,afq1),(delay2,sup2,afq2),"
  3459. "(delay3,sup3,afq3),(delay4,sup4,afq4) =(%d,%d,%d),"
  3460. "(%d,%d,%d),(%d,%d,%d),(%d,%d,%d),(%d,%d,%d)\n",
  3461. i,
  3462. mac_table[i].psq.q[0].n_pkts,
  3463. mac_table[i].psq.q[1].n_pkts,
  3464. mac_table[i].afq.q[0].n_pkts,
  3465. mac_table[i].psq.q[2].n_pkts,
  3466. mac_table[i].psq.q[3].n_pkts,
  3467. mac_table[i].afq.q[1].n_pkts,
  3468. mac_table[i].psq.q[4].n_pkts,
  3469. mac_table[i].psq.q[5].n_pkts,
  3470. mac_table[i].afq.q[2].n_pkts,
  3471. mac_table[i].psq.q[6].n_pkts,
  3472. mac_table[i].psq.q[7].n_pkts,
  3473. mac_table[i].afq.q[3].n_pkts,
  3474. mac_table[i].psq.q[8].n_pkts,
  3475. mac_table[i].psq.q[9].n_pkts,
  3476. mac_table[i].afq.q[4].n_pkts);
  3477. }
  3478. }
  3479. #ifdef PROP_TXSTATUS_DEBUG
  3480. {
  3481. int avg;
  3482. int moving_avg = 0;
  3483. int moving_samples;
  3484. if (wlfc->stats.latency_sample_count) {
  3485. moving_samples = sizeof(wlfc->stats.deltas)/sizeof(uint32);
  3486. for (i = 0; i < moving_samples; i++)
  3487. moving_avg += wlfc->stats.deltas[i];
  3488. moving_avg /= moving_samples;
  3489. avg = (100 * wlfc->stats.total_status_latency) /
  3490. wlfc->stats.latency_sample_count;
  3491. bcm_bprintf(strbuf, "txstatus latency (average, last, moving[%d]) = "
  3492. "(%d.%d, %03d, %03d)\n",
  3493. moving_samples, avg/100, (avg - (avg/100)*100),
  3494. wlfc->stats.latency_most_recent,
  3495. moving_avg);
  3496. }
  3497. }
  3498. bcm_bprintf(strbuf, "wlfc- fifo[0-5] credit stats: sent = (%d,%d,%d,%d,%d,%d), "
  3499. "back = (%d,%d,%d,%d,%d,%d)\n",
  3500. wlfc->stats.fifo_credits_sent[0],
  3501. wlfc->stats.fifo_credits_sent[1],
  3502. wlfc->stats.fifo_credits_sent[2],
  3503. wlfc->stats.fifo_credits_sent[3],
  3504. wlfc->stats.fifo_credits_sent[4],
  3505. wlfc->stats.fifo_credits_sent[5],
  3506. wlfc->stats.fifo_credits_back[0],
  3507. wlfc->stats.fifo_credits_back[1],
  3508. wlfc->stats.fifo_credits_back[2],
  3509. wlfc->stats.fifo_credits_back[3],
  3510. wlfc->stats.fifo_credits_back[4],
  3511. wlfc->stats.fifo_credits_back[5]);
  3512. {
  3513. uint32 fifo_cr_sent = 0;
  3514. uint32 fifo_cr_acked = 0;
  3515. uint32 request_cr_sent = 0;
  3516. uint32 request_cr_ack = 0;
  3517. uint32 bc_mc_cr_ack = 0;
  3518. for (i = 0; i < sizeof(wlfc->stats.fifo_credits_sent)/sizeof(uint32); i++) {
  3519. fifo_cr_sent += wlfc->stats.fifo_credits_sent[i];
  3520. }
  3521. for (i = 0; i < sizeof(wlfc->stats.fifo_credits_back)/sizeof(uint32); i++) {
  3522. fifo_cr_acked += wlfc->stats.fifo_credits_back[i];
  3523. }
  3524. for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
  3525. if (wlfc->destination_entries.nodes[i].occupied) {
  3526. request_cr_sent +=
  3527. wlfc->destination_entries.nodes[i].dstncredit_sent_packets;
  3528. }
  3529. }
  3530. for (i = 0; i < WLFC_MAX_IFNUM; i++) {
  3531. if (wlfc->destination_entries.interfaces[i].occupied) {
  3532. request_cr_sent +=
  3533. wlfc->destination_entries.interfaces[i].dstncredit_sent_packets;
  3534. }
  3535. }
  3536. for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
  3537. if (wlfc->destination_entries.nodes[i].occupied) {
  3538. request_cr_ack +=
  3539. wlfc->destination_entries.nodes[i].dstncredit_acks;
  3540. }
  3541. }
  3542. for (i = 0; i < WLFC_MAX_IFNUM; i++) {
  3543. if (wlfc->destination_entries.interfaces[i].occupied) {
  3544. request_cr_ack +=
  3545. wlfc->destination_entries.interfaces[i].dstncredit_acks;
  3546. }
  3547. }
  3548. bcm_bprintf(strbuf, "wlfc- (sent, status) => pq(%d,%d), vq(%d,%d),"
  3549. "other:%d, bc_mc:%d, signal-only, (sent,freed): (%d,%d)",
  3550. fifo_cr_sent, fifo_cr_acked,
  3551. request_cr_sent, request_cr_ack,
  3552. wlfc->destination_entries.other.dstncredit_acks,
  3553. bc_mc_cr_ack,
  3554. wlfc->stats.signal_only_pkts_sent, wlfc->stats.signal_only_pkts_freed);
  3555. }
  3556. #endif /* PROP_TXSTATUS_DEBUG */
  3557. bcm_bprintf(strbuf, "\n");
  3558. bcm_bprintf(strbuf, "wlfc- pkt((in,2bus,txstats,hdrpull,out),"
  3559. "(dropped,hdr_only,wlc_tossed,wlc_dropped,wlc_exptime)"
  3560. "(freed,free_err,rollback)) = "
  3561. "((%d,%d,%d,%d,%d),(%d,%d,%d,%d,%d),(%d,%d,%d))\n",
  3562. wlfc->stats.pktin,
  3563. wlfc->stats.pkt2bus,
  3564. wlfc->stats.txstatus_in,
  3565. wlfc->stats.dhd_hdrpulls,
  3566. wlfc->stats.pktout,
  3567. wlfc->stats.pktdropped,
  3568. wlfc->stats.wlfc_header_only_pkt,
  3569. wlfc->stats.wlc_tossed_pkts,
  3570. wlfc->stats.pkt_dropped,
  3571. wlfc->stats.pkt_exptime,
  3572. wlfc->stats.pkt_freed,
  3573. wlfc->stats.pkt_free_err, wlfc->stats.rollback);
  3574. bcm_bprintf(strbuf, "wlfc- suppress((d11,wlc,err),enq(d11,wl,hq,mac?),retx(d11,wlc,hq)) = "
  3575. "((%d,%d,%d),(%d,%d,%d,%d),(%d,%d,%d))\n",
  3576. wlfc->stats.d11_suppress,
  3577. wlfc->stats.wl_suppress,
  3578. wlfc->stats.bad_suppress,
  3579. wlfc->stats.psq_d11sup_enq,
  3580. wlfc->stats.psq_wlsup_enq,
  3581. wlfc->stats.psq_hostq_enq,
  3582. wlfc->stats.mac_handle_notfound,
  3583. wlfc->stats.psq_d11sup_retx,
  3584. wlfc->stats.psq_wlsup_retx,
  3585. wlfc->stats.psq_hostq_retx);
  3586. bcm_bprintf(strbuf, "wlfc- cleanup(txq,psq,fw) = (%d,%d,%d)\n",
  3587. wlfc->stats.cleanup_txq_cnt,
  3588. wlfc->stats.cleanup_psq_cnt,
  3589. wlfc->stats.cleanup_fw_cnt);
  3590. bcm_bprintf(strbuf, "wlfc- generic error: %d\n", wlfc->stats.generic_error);
  3591. for (i = 0; i < WLFC_MAX_IFNUM; i++) {
  3592. bcm_bprintf(strbuf, "wlfc- if[%d], pkt_cnt_in_q/AC[0-4] = (%d,%d,%d,%d,%d)\n", i,
  3593. wlfc->pkt_cnt_in_q[i][0],
  3594. wlfc->pkt_cnt_in_q[i][1],
  3595. wlfc->pkt_cnt_in_q[i][2],
  3596. wlfc->pkt_cnt_in_q[i][3],
  3597. wlfc->pkt_cnt_in_q[i][4]);
  3598. }
  3599. bcm_bprintf(strbuf, "\n");
  3600. dhd_os_wlfc_unblock(dhdp);
  3601. return BCME_OK;
  3602. } /* dhd_wlfc_dump */
  3603. int dhd_wlfc_clear_counts(dhd_pub_t *dhd)
  3604. {
  3605. athost_wl_status_info_t* wlfc;
  3606. wlfc_hanger_t* hanger;
  3607. if (dhd == NULL) {
  3608. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3609. return BCME_BADARG;
  3610. }
  3611. dhd_os_wlfc_block(dhd);
  3612. if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
  3613. dhd_os_wlfc_unblock(dhd);
  3614. return WLFC_UNSUPPORTED;
  3615. }
  3616. wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
  3617. memset(&wlfc->stats, 0, sizeof(athost_wl_stat_counters_t));
  3618. if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
  3619. hanger = (wlfc_hanger_t*)wlfc->hanger;
  3620. hanger->pushed = 0;
  3621. hanger->popped = 0;
  3622. hanger->failed_slotfind = 0;
  3623. hanger->failed_to_pop = 0;
  3624. hanger->failed_to_push = 0;
  3625. }
  3626. dhd_os_wlfc_unblock(dhd);
  3627. return BCME_OK;
  3628. }
  3629. /** returns TRUE if flow control is enabled */
  3630. int dhd_wlfc_get_enable(dhd_pub_t *dhd, bool *val)
  3631. {
  3632. if (!dhd || !val) {
  3633. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3634. return BCME_BADARG;
  3635. }
  3636. dhd_os_wlfc_block(dhd);
  3637. *val = dhd->wlfc_enabled;
  3638. dhd_os_wlfc_unblock(dhd);
  3639. return BCME_OK;
  3640. }
  3641. /** Called via an IOVAR */
  3642. int dhd_wlfc_get_mode(dhd_pub_t *dhd, int *val)
  3643. {
  3644. if (!dhd || !val) {
  3645. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3646. return BCME_BADARG;
  3647. }
  3648. dhd_os_wlfc_block(dhd);
  3649. *val = dhd->wlfc_state ? dhd->proptxstatus_mode : 0;
  3650. dhd_os_wlfc_unblock(dhd);
  3651. return BCME_OK;
  3652. }
  3653. /** Called via an IOVAR */
  3654. int dhd_wlfc_set_mode(dhd_pub_t *dhd, int val)
  3655. {
  3656. if (!dhd) {
  3657. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3658. return BCME_BADARG;
  3659. }
  3660. dhd_os_wlfc_block(dhd);
  3661. if (dhd->wlfc_state) {
  3662. dhd->proptxstatus_mode = val & 0xff;
  3663. }
  3664. dhd_os_wlfc_unblock(dhd);
  3665. return BCME_OK;
  3666. }
  3667. /** Called when rx frame is received from the dongle */
  3668. bool dhd_wlfc_is_header_only_pkt(dhd_pub_t * dhd, void *pktbuf)
  3669. {
  3670. athost_wl_status_info_t* wlfc;
  3671. bool rc = FALSE;
  3672. if (dhd == NULL) {
  3673. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3674. return FALSE;
  3675. }
  3676. dhd_os_wlfc_block(dhd);
  3677. if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
  3678. dhd_os_wlfc_unblock(dhd);
  3679. return FALSE;
  3680. }
  3681. wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
  3682. if (PKTLEN(wlfc->osh, pktbuf) == 0) {
  3683. wlfc->stats.wlfc_header_only_pkt++;
  3684. rc = TRUE;
  3685. }
  3686. dhd_os_wlfc_unblock(dhd);
  3687. return rc;
  3688. }
  3689. int dhd_wlfc_flowcontrol(dhd_pub_t *dhdp, bool state, bool bAcquireLock)
  3690. {
  3691. if (dhdp == NULL) {
  3692. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3693. return BCME_BADARG;
  3694. }
  3695. if (bAcquireLock) {
  3696. dhd_os_wlfc_block(dhdp);
  3697. }
  3698. if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE) ||
  3699. dhdp->proptxstatus_module_ignore) {
  3700. if (bAcquireLock) {
  3701. dhd_os_wlfc_unblock(dhdp);
  3702. }
  3703. return WLFC_UNSUPPORTED;
  3704. }
  3705. if (state != dhdp->proptxstatus_txoff) {
  3706. dhdp->proptxstatus_txoff = state;
  3707. }
  3708. if (bAcquireLock) {
  3709. dhd_os_wlfc_unblock(dhdp);
  3710. }
  3711. return BCME_OK;
  3712. }
  3713. /** Called when eg an rx frame is received from the dongle */
  3714. int dhd_wlfc_save_rxpath_ac_time(dhd_pub_t * dhd, uint8 prio)
  3715. {
  3716. athost_wl_status_info_t* wlfc;
  3717. int rx_path_ac = -1;
  3718. if ((dhd == NULL) || (prio >= NUMPRIO)) {
  3719. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3720. return BCME_BADARG;
  3721. }
  3722. dhd_os_wlfc_block(dhd);
  3723. if (!dhd->wlfc_rxpkt_chk) {
  3724. dhd_os_wlfc_unblock(dhd);
  3725. return BCME_OK;
  3726. }
  3727. if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
  3728. dhd_os_wlfc_unblock(dhd);
  3729. return WLFC_UNSUPPORTED;
  3730. }
  3731. wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
  3732. rx_path_ac = prio2fifo[prio];
  3733. wlfc->rx_timestamp[rx_path_ac] = OSL_SYSUPTIME();
  3734. dhd_os_wlfc_unblock(dhd);
  3735. return BCME_OK;
  3736. }
  3737. /** called via an IOVAR */
  3738. int dhd_wlfc_get_module_ignore(dhd_pub_t *dhd, int *val)
  3739. {
  3740. if (!dhd || !val) {
  3741. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3742. return BCME_BADARG;
  3743. }
  3744. dhd_os_wlfc_block(dhd);
  3745. *val = dhd->proptxstatus_module_ignore;
  3746. dhd_os_wlfc_unblock(dhd);
  3747. return BCME_OK;
  3748. }
  3749. /** called via an IOVAR */
  3750. int dhd_wlfc_set_module_ignore(dhd_pub_t *dhd, int val)
  3751. {
  3752. uint32 tlv = 0;
  3753. bool bChanged = FALSE;
  3754. if (!dhd) {
  3755. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3756. return BCME_BADARG;
  3757. }
  3758. dhd_os_wlfc_block(dhd);
  3759. if ((bool)val != dhd->proptxstatus_module_ignore) {
  3760. dhd->proptxstatus_module_ignore = (val != 0);
  3761. /* force txstatus_ignore sync with proptxstatus_module_ignore */
  3762. dhd->proptxstatus_txstatus_ignore = dhd->proptxstatus_module_ignore;
  3763. if (FALSE == dhd->proptxstatus_module_ignore) {
  3764. tlv = WLFC_FLAGS_RSSI_SIGNALS |
  3765. WLFC_FLAGS_XONXOFF_SIGNALS |
  3766. WLFC_FLAGS_CREDIT_STATUS_SIGNALS |
  3767. WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE;
  3768. }
  3769. /* always enable host reorder */
  3770. tlv |= WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
  3771. bChanged = TRUE;
  3772. }
  3773. dhd_os_wlfc_unblock(dhd);
  3774. if (bChanged) {
  3775. /* select enable proptxtstatus signaling */
  3776. if (dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
  3777. DHD_ERROR(("%s: failed to set bdcv2 tlv signaling to 0x%x\n",
  3778. __FUNCTION__, tlv));
  3779. } else {
  3780. DHD_ERROR(("%s: successfully set bdcv2 tlv signaling to 0x%x\n",
  3781. __FUNCTION__, tlv));
  3782. }
  3783. }
  3784. #if defined(DHD_WLFC_THREAD)
  3785. _dhd_wlfc_thread_wakeup(dhd);
  3786. #endif /* defined(DHD_WLFC_THREAD) */
  3787. return BCME_OK;
  3788. }
  3789. /** called via an IOVAR */
  3790. int dhd_wlfc_get_credit_ignore(dhd_pub_t *dhd, int *val)
  3791. {
  3792. if (!dhd || !val) {
  3793. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3794. return BCME_BADARG;
  3795. }
  3796. dhd_os_wlfc_block(dhd);
  3797. *val = dhd->proptxstatus_credit_ignore;
  3798. dhd_os_wlfc_unblock(dhd);
  3799. return BCME_OK;
  3800. }
  3801. /** called via an IOVAR */
  3802. int dhd_wlfc_set_credit_ignore(dhd_pub_t *dhd, int val)
  3803. {
  3804. if (!dhd) {
  3805. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3806. return BCME_BADARG;
  3807. }
  3808. dhd_os_wlfc_block(dhd);
  3809. dhd->proptxstatus_credit_ignore = (val != 0);
  3810. dhd_os_wlfc_unblock(dhd);
  3811. return BCME_OK;
  3812. }
  3813. /** called via an IOVAR */
  3814. int dhd_wlfc_get_txstatus_ignore(dhd_pub_t *dhd, int *val)
  3815. {
  3816. if (!dhd || !val) {
  3817. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3818. return BCME_BADARG;
  3819. }
  3820. dhd_os_wlfc_block(dhd);
  3821. *val = dhd->proptxstatus_txstatus_ignore;
  3822. dhd_os_wlfc_unblock(dhd);
  3823. return BCME_OK;
  3824. }
  3825. /** called via an IOVAR */
  3826. int dhd_wlfc_set_txstatus_ignore(dhd_pub_t *dhd, int val)
  3827. {
  3828. if (!dhd) {
  3829. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3830. return BCME_BADARG;
  3831. }
  3832. dhd_os_wlfc_block(dhd);
  3833. dhd->proptxstatus_txstatus_ignore = (val != 0);
  3834. dhd_os_wlfc_unblock(dhd);
  3835. return BCME_OK;
  3836. }
  3837. /** called via an IOVAR */
  3838. int dhd_wlfc_get_rxpkt_chk(dhd_pub_t *dhd, int *val)
  3839. {
  3840. if (!dhd || !val) {
  3841. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3842. return BCME_BADARG;
  3843. }
  3844. dhd_os_wlfc_block(dhd);
  3845. *val = dhd->wlfc_rxpkt_chk;
  3846. dhd_os_wlfc_unblock(dhd);
  3847. return BCME_OK;
  3848. }
  3849. /** called via an IOVAR */
  3850. int dhd_wlfc_set_rxpkt_chk(dhd_pub_t *dhd, int val)
  3851. {
  3852. if (!dhd) {
  3853. DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
  3854. return BCME_BADARG;
  3855. }
  3856. dhd_os_wlfc_block(dhd);
  3857. dhd->wlfc_rxpkt_chk = (val != 0);
  3858. dhd_os_wlfc_unblock(dhd);
  3859. return BCME_OK;
  3860. }
  3861. #endif /* PROP_TXSTATUS */