dhd_wlfc.c 104 KB

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