| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546 |
- /*
- * DHD PROP_TXSTATUS Module.
- *
- * Portions of this code are copyright (c) 2020 Cypress Semiconductor Corporation
- *
- * Copyright (C) 1999-2020, Broadcom Corporation
- *
- * Unless you and Broadcom execute a separate written software license
- * agreement governing use of this software, this software is licensed to you
- * under the terms of the GNU General Public License version 2 (the "GPL"),
- * available at http://www.broadcom.com/licenses/GPLv2.php, with the
- * following added to such license:
- *
- * As a special exception, the copyright holders of this software give you
- * permission to link this software with independent modules, and to copy and
- * distribute the resulting executable under terms of your choice, provided that
- * you also meet, for each linked independent module, the terms and conditions of
- * the license of that module. An independent module is a module which is not
- * derived from this software. The special exception does not apply to any
- * modifications of the software.
- *
- * Notwithstanding the above, under no circumstances may you combine this
- * software in any way with any other Broadcom software provided under a license
- * other than the GPL, without Broadcom's express prior written consent.
- *
- *
- * <<Broadcom-WL-IPTag/Open:>>
- *
- * $Id: dhd_wlfc.c 700323 2017-05-18 16:12:11Z $
- *
- */
- #include <typedefs.h>
- #include <osl.h>
- #include <bcmutils.h>
- #include <bcmendian.h>
- #include <dngl_stats.h>
- #include <dhd.h>
- #include <dhd_bus.h>
- #include <dhd_dbg.h>
- #ifdef PROP_TXSTATUS /* a form of flow control between host and dongle */
- #include <wlfc_proto.h>
- #include <dhd_wlfc.h>
- #endif // endif
- #ifdef DHDTCPACK_SUPPRESS
- #include <dhd_ip.h>
- #endif /* DHDTCPACK_SUPPRESS */
- /*
- * wlfc naming and lock rules:
- *
- * 1. Private functions name like _dhd_wlfc_XXX, declared as static and avoid wlfc lock operation.
- * 2. Public functions name like dhd_wlfc_XXX, use wlfc lock if needed.
- * 3. Non-Proptxstatus module call public functions only and avoid wlfc lock operation.
- *
- */
- #if defined(DHD_WLFC_THREAD)
- #define WLFC_THREAD_QUICK_RETRY_WAIT_MS 10 /* 10 msec */
- #define WLFC_THREAD_RETRY_WAIT_MS 10000 /* 10 sec */
- #endif /* defined (DHD_WLFC_THREAD) */
- #ifdef PROP_TXSTATUS
- #define DHD_WLFC_QMON_COMPLETE(entry)
- /** reordering related */
- #if defined(DHD_WLFC_THREAD)
- static void
- _dhd_wlfc_thread_wakeup(dhd_pub_t *dhdp)
- {
- dhdp->wlfc_thread_go = TRUE;
- wake_up_interruptible(&dhdp->wlfc_wqhead);
- }
- #endif /* DHD_WLFC_THREAD */
- static uint16
- _dhd_wlfc_adjusted_seq(void* p, uint8 current_seq)
- {
- uint16 seq;
- if (!p) {
- return 0xffff;
- }
- seq = WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
- if (seq < current_seq) {
- /* wrap around */
- seq += 256;
- }
- return seq;
- }
- /**
- * Enqueue a caller supplied packet on a caller supplied precedence queue, optionally reorder
- * suppressed packets.
- * @param[in] pq caller supplied packet queue to enqueue the packet on
- * @param[in] prec precedence of the to-be-queued packet
- * @param[in] p transmit packet to enqueue
- * @param[in] qHead if TRUE, enqueue to head instead of tail. Used to maintain d11 seq order.
- * @param[in] current_seq
- * @param[in] reOrder reOrder on odd precedence (=suppress queue)
- */
- static void
- _dhd_wlfc_prec_enque(struct pktq *pq, int prec, void* p, bool qHead,
- uint8 current_seq, bool reOrder)
- {
- struct pktq_prec *q;
- uint16 seq, seq2;
- void *p2, *p2_prev;
- if (!p)
- return;
- ASSERT(prec >= 0 && prec < pq->num_prec);
- ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */
- ASSERT(!pktq_full(pq));
- ASSERT(!pktqprec_full(pq, prec));
- q = &pq->q[prec];
- if (q->head == NULL) {
- /* empty queue */
- q->head = p;
- q->tail = p;
- } else {
- if (reOrder && (prec & 1)) {
- seq = _dhd_wlfc_adjusted_seq(p, current_seq);
- p2 = qHead ? q->head : q->tail;
- seq2 = _dhd_wlfc_adjusted_seq(p2, current_seq);
- if ((qHead &&((seq+1) > seq2)) || (!qHead && ((seq2+1) > seq))) {
- /* need reorder */
- p2 = q->head;
- p2_prev = NULL;
- seq2 = _dhd_wlfc_adjusted_seq(p2, current_seq);
- while (seq > seq2) {
- p2_prev = p2;
- p2 = PKTLINK(p2);
- if (!p2) {
- break;
- }
- seq2 = _dhd_wlfc_adjusted_seq(p2, current_seq);
- }
- if (p2_prev == NULL) {
- /* insert head */
- PKTSETLINK(p, q->head);
- q->head = p;
- } else if (p2 == NULL) {
- /* insert tail */
- PKTSETLINK(p2_prev, p);
- q->tail = p;
- } else {
- /* insert after p2_prev */
- PKTSETLINK(p, PKTLINK(p2_prev));
- PKTSETLINK(p2_prev, p);
- }
- goto exit;
- }
- }
- if (qHead) {
- PKTSETLINK(p, q->head);
- q->head = p;
- } else {
- PKTSETLINK(q->tail, p);
- q->tail = p;
- }
- }
- exit:
- q->n_pkts++;
- pq->n_pkts_tot++;
- if (pq->hi_prec < prec)
- pq->hi_prec = (uint8)prec;
- } /* _dhd_wlfc_prec_enque */
- /**
- * Create a place to store all packet pointers submitted to the firmware until a status comes back,
- * suppress or otherwise.
- *
- * hang-er: noun, a contrivance on which things are hung, as a hook.
- */
- /** @deprecated soon */
- static void*
- _dhd_wlfc_hanger_create(dhd_pub_t *dhd, int max_items)
- {
- int i;
- wlfc_hanger_t* hanger;
- /* allow only up to a specific size for now */
- ASSERT(max_items == WLFC_HANGER_MAXITEMS);
- if ((hanger = (wlfc_hanger_t*)DHD_OS_PREALLOC(dhd, DHD_PREALLOC_DHD_WLFC_HANGER,
- WLFC_HANGER_SIZE(max_items))) == NULL) {
- return NULL;
- }
- memset(hanger, 0, WLFC_HANGER_SIZE(max_items));
- hanger->max_items = max_items;
- for (i = 0; i < hanger->max_items; i++) {
- hanger->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
- }
- return hanger;
- }
- /** @deprecated soon */
- static int
- _dhd_wlfc_hanger_delete(dhd_pub_t *dhd, void* hanger)
- {
- wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
- if (h) {
- DHD_OS_PREFREE(dhd, h, WLFC_HANGER_SIZE(h->max_items));
- return BCME_OK;
- }
- return BCME_BADARG;
- }
- /** @deprecated soon */
- static uint16
- _dhd_wlfc_hanger_get_free_slot(void* hanger)
- {
- uint32 i;
- wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
- if (h) {
- i = h->slot_pos + 1;
- if (i == h->max_items) {
- i = 0;
- }
- while (i != h->slot_pos) {
- if (h->items[i].state == WLFC_HANGER_ITEM_STATE_FREE) {
- h->slot_pos = i;
- return (uint16)i;
- }
- i++;
- if (i == h->max_items)
- i = 0;
- }
- h->failed_slotfind++;
- }
- return WLFC_HANGER_MAXITEMS;
- }
- /** @deprecated soon */
- static int
- _dhd_wlfc_hanger_get_genbit(void* hanger, void* pkt, uint32 slot_id, int* gen)
- {
- int rc = BCME_OK;
- wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
- *gen = 0xff;
- /* this packet was not pushed at the time it went to the firmware */
- if (slot_id == WLFC_HANGER_MAXITEMS)
- return BCME_NOTFOUND;
- if (h) {
- if (h->items[slot_id].state != WLFC_HANGER_ITEM_STATE_FREE) {
- *gen = h->items[slot_id].gen;
- }
- else {
- DHD_ERROR(("Error: %s():%d item not used\n",
- __FUNCTION__, __LINE__));
- rc = BCME_NOTFOUND;
- }
- } else {
- rc = BCME_BADARG;
- }
- return rc;
- }
- /** @deprecated soon */
- static int
- _dhd_wlfc_hanger_pushpkt(void* hanger, void* pkt, uint32 slot_id)
- {
- int rc = BCME_OK;
- wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
- if (h && (slot_id < WLFC_HANGER_MAXITEMS)) {
- if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_FREE) {
- h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE;
- h->items[slot_id].pkt = pkt;
- h->items[slot_id].pkt_state = 0;
- h->items[slot_id].pkt_txstatus = 0;
- h->pushed++;
- } else {
- h->failed_to_push++;
- rc = BCME_NOTFOUND;
- }
- } else {
- rc = BCME_BADARG;
- }
- return rc;
- }
- /** @deprecated soon */
- static int
- _dhd_wlfc_hanger_poppkt(void* hanger, uint32 slot_id, void** pktout, bool remove_from_hanger)
- {
- int rc = BCME_OK;
- wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
- *pktout = NULL;
- /* this packet was not pushed at the time it went to the firmware */
- if (slot_id == WLFC_HANGER_MAXITEMS)
- return BCME_NOTFOUND;
- if (h) {
- if (h->items[slot_id].state != WLFC_HANGER_ITEM_STATE_FREE) {
- *pktout = h->items[slot_id].pkt;
- if (remove_from_hanger) {
- h->items[slot_id].state =
- WLFC_HANGER_ITEM_STATE_FREE;
- h->items[slot_id].pkt = NULL;
- h->items[slot_id].gen = 0xff;
- h->items[slot_id].identifier = 0;
- h->popped++;
- }
- } else {
- h->failed_to_pop++;
- rc = BCME_NOTFOUND;
- }
- } else {
- rc = BCME_BADARG;
- }
- return rc;
- }
- /** @deprecated soon */
- static int
- _dhd_wlfc_hanger_mark_suppressed(void* hanger, uint32 slot_id, uint8 gen)
- {
- int rc = BCME_OK;
- wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
- /* this packet was not pushed at the time it went to the firmware */
- if (slot_id == WLFC_HANGER_MAXITEMS)
- return BCME_NOTFOUND;
- if (h) {
- h->items[slot_id].gen = gen;
- if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE) {
- h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED;
- } else {
- rc = BCME_BADARG;
- }
- } else {
- rc = BCME_BADARG;
- }
- return rc;
- }
- /** remove reference of specific packet in hanger */
- /** @deprecated soon */
- static bool
- _dhd_wlfc_hanger_remove_reference(wlfc_hanger_t* h, void* pkt)
- {
- int i;
- if (!h || !pkt) {
- return FALSE;
- }
- i = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(pkt)));
- if ((i < h->max_items) && (pkt == h->items[i].pkt)) {
- if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
- h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
- h->items[i].pkt = NULL;
- h->items[i].gen = 0xff;
- h->items[i].identifier = 0;
- return TRUE;
- } else {
- DHD_ERROR(("Error: %s():%d item not suppressed\n",
- __FUNCTION__, __LINE__));
- }
- }
- return FALSE;
- }
- /** afq = At Firmware Queue, queue containing packets pending in the dongle */
- static int
- _dhd_wlfc_enque_afq(athost_wl_status_info_t* ctx, void *p)
- {
- wlfc_mac_descriptor_t* entry;
- uint16 entry_idx = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
- uint8 prec = DHD_PKTTAG_FIFO(PKTTAG(p));
- if (entry_idx < WLFC_MAC_DESC_TABLE_SIZE)
- entry = &ctx->destination_entries.nodes[entry_idx];
- else if (entry_idx < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM))
- entry = &ctx->destination_entries.interfaces[entry_idx - WLFC_MAC_DESC_TABLE_SIZE];
- else
- entry = &ctx->destination_entries.other;
- pktq_penq(&entry->afq, prec, p);
- return BCME_OK;
- }
- /** afq = At Firmware Queue, queue containing packets pending in the dongle */
- static int
- _dhd_wlfc_deque_afq(athost_wl_status_info_t* ctx, uint16 hslot, uint8 hcnt, uint8 prec,
- void **pktout)
- {
- wlfc_mac_descriptor_t *entry;
- struct pktq *pq;
- struct pktq_prec *q;
- void *p, *b;
- if (!ctx) {
- DHD_ERROR(("%s: ctx(%p), pktout(%p)\n", __FUNCTION__, ctx, pktout));
- return BCME_BADARG;
- }
- if (pktout) {
- *pktout = NULL;
- }
- ASSERT(hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM + 1));
- if (hslot < WLFC_MAC_DESC_TABLE_SIZE)
- entry = &ctx->destination_entries.nodes[hslot];
- else if (hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM))
- entry = &ctx->destination_entries.interfaces[hslot - WLFC_MAC_DESC_TABLE_SIZE];
- else
- entry = &ctx->destination_entries.other;
- pq = &entry->afq;
- ASSERT(prec < pq->num_prec);
- q = &pq->q[prec];
- b = NULL;
- p = q->head;
- while (p && (hcnt != WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p)))))
- {
- b = p;
- p = PKTLINK(p);
- }
- if (p == NULL) {
- /* none is matched */
- if (b) {
- DHD_ERROR(("%s: can't find matching seq(%d)\n", __FUNCTION__, hcnt));
- } else {
- DHD_ERROR(("%s: queue is empty\n", __FUNCTION__));
- }
- return BCME_ERROR;
- }
- bcm_pkt_validate_chk(p);
- if (!b) {
- /* head packet is matched */
- if ((q->head = PKTLINK(p)) == NULL) {
- q->tail = NULL;
- }
- } else {
- /* middle packet is matched */
- DHD_INFO(("%s: out of order, seq(%d), head_seq(%d)\n", __FUNCTION__, hcnt,
- WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(q->head)))));
- ctx->stats.ooo_pkts[prec]++;
- PKTSETLINK(b, PKTLINK(p));
- if (PKTLINK(p) == NULL) {
- q->tail = b;
- }
- }
- q->n_pkts--;
- pq->n_pkts_tot--;
- #ifdef WL_TXQ_STALL
- q->dequeue_count++;
- #endif // endif
- PKTSETLINK(p, NULL);
- if (pktout) {
- *pktout = p;
- }
- return BCME_OK;
- } /* _dhd_wlfc_deque_afq */
- /**
- * Flow control information piggy backs on packets, in the form of one or more TLVs. This function
- * pushes one or more TLVs onto a packet that is going to be sent towards the dongle.
- *
- * @param[in] ctx
- * @param[in/out] packet
- * @param[in] tim_signal TRUE if parameter 'tim_bmp' is valid
- * @param[in] tim_bmp
- * @param[in] mac_handle
- * @param[in] htodtag
- * @param[in] htodseq d11 seqno for seqno reuse, only used if 'seq reuse' was agreed upon
- * earlier between host and firmware.
- * @param[in] skip_wlfc_hdr
- */
- static int
- _dhd_wlfc_pushheader(athost_wl_status_info_t* ctx, void** packet, bool tim_signal,
- uint8 tim_bmp, uint8 mac_handle, uint32 htodtag, uint16 htodseq, bool skip_wlfc_hdr)
- {
- uint32 wl_pktinfo = 0;
- uint8* wlh;
- uint8 dataOffset = 0;
- uint8 fillers;
- uint8 tim_signal_len = 0;
- dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
- struct bdc_header *h;
- void *p = *packet;
- if (skip_wlfc_hdr)
- goto push_bdc_hdr;
- if (tim_signal) {
- tim_signal_len = TLV_HDR_LEN + WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP;
- }
- /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
- dataOffset = WLFC_CTL_VALUE_LEN_PKTTAG + TLV_HDR_LEN + tim_signal_len;
- if (WLFC_GET_REUSESEQ(dhdp->wlfc_mode)) {
- dataOffset += WLFC_CTL_VALUE_LEN_SEQ;
- }
- fillers = ROUNDUP(dataOffset, 4) - dataOffset;
- dataOffset += fillers;
- PKTPUSH(ctx->osh, p, dataOffset);
- wlh = (uint8*) PKTDATA(ctx->osh, p);
- wl_pktinfo = htol32(htodtag);
- wlh[TLV_TAG_OFF] = WLFC_CTL_TYPE_PKTTAG;
- wlh[TLV_LEN_OFF] = WLFC_CTL_VALUE_LEN_PKTTAG;
- memcpy(&wlh[TLV_HDR_LEN] /* dst */, &wl_pktinfo, sizeof(uint32));
- if (WLFC_GET_REUSESEQ(dhdp->wlfc_mode)) {
- uint16 wl_seqinfo = htol16(htodseq);
- wlh[TLV_LEN_OFF] += WLFC_CTL_VALUE_LEN_SEQ;
- memcpy(&wlh[TLV_HDR_LEN + WLFC_CTL_VALUE_LEN_PKTTAG], &wl_seqinfo,
- WLFC_CTL_VALUE_LEN_SEQ);
- }
- if (tim_signal_len) {
- wlh[dataOffset - fillers - tim_signal_len ] =
- WLFC_CTL_TYPE_PENDING_TRAFFIC_BMP;
- wlh[dataOffset - fillers - tim_signal_len + 1] =
- WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP;
- wlh[dataOffset - fillers - tim_signal_len + 2] = mac_handle;
- wlh[dataOffset - fillers - tim_signal_len + 3] = tim_bmp;
- }
- if (fillers)
- memset(&wlh[dataOffset - fillers], WLFC_CTL_TYPE_FILLER, fillers);
- push_bdc_hdr:
- PKTPUSH(ctx->osh, p, BDC_HEADER_LEN);
- h = (struct bdc_header *)PKTDATA(ctx->osh, p);
- h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
- if (PKTSUMNEEDED(p))
- h->flags |= BDC_FLAG_SUM_NEEDED;
- h->priority = (PKTPRIO(p) & BDC_PRIORITY_MASK);
- h->flags2 = 0;
- h->dataOffset = dataOffset >> 2;
- BDC_SET_IF_IDX(h, DHD_PKTTAG_IF(PKTTAG(p)));
- *packet = p;
- return BCME_OK;
- } /* _dhd_wlfc_pushheader */
- /**
- * Removes (PULLs) flow control related headers from the caller supplied packet, is invoked eg
- * when a packet is about to be freed.
- */
- static int
- _dhd_wlfc_pullheader(athost_wl_status_info_t* ctx, void* pktbuf)
- {
- struct bdc_header *h;
- if (PKTLEN(ctx->osh, pktbuf) < BDC_HEADER_LEN) {
- DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
- PKTLEN(ctx->osh, pktbuf), BDC_HEADER_LEN));
- return BCME_ERROR;
- }
- h = (struct bdc_header *)PKTDATA(ctx->osh, pktbuf);
- /* pull BDC header */
- PKTPULL(ctx->osh, pktbuf, BDC_HEADER_LEN);
- if (PKTLEN(ctx->osh, pktbuf) < (uint)(h->dataOffset << 2)) {
- DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
- PKTLEN(ctx->osh, pktbuf), (h->dataOffset << 2)));
- return BCME_ERROR;
- }
- /* pull wl-header */
- PKTPULL(ctx->osh, pktbuf, (h->dataOffset << 2));
- return BCME_OK;
- }
- /**
- * @param[in/out] p packet
- */
- static wlfc_mac_descriptor_t*
- _dhd_wlfc_find_table_entry(athost_wl_status_info_t* ctx, void* p)
- {
- int i;
- wlfc_mac_descriptor_t* table = ctx->destination_entries.nodes;
- uint8 ifid = DHD_PKTTAG_IF(PKTTAG(p));
- uint8* dstn = DHD_PKTTAG_DSTN(PKTTAG(p));
- wlfc_mac_descriptor_t* entry = DHD_PKTTAG_ENTRY(PKTTAG(p));
- int iftype = ctx->destination_entries.interfaces[ifid].iftype;
- /* saved one exists, return it */
- if (entry)
- return entry;
- /* Multicast destination, STA and P2P clients get the interface entry.
- * STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations
- * have their own entry.
- */
- if ((iftype == WLC_E_IF_ROLE_STA || ETHER_ISMULTI(dstn) ||
- iftype == WLC_E_IF_ROLE_P2P_CLIENT) &&
- (ctx->destination_entries.interfaces[ifid].occupied)) {
- entry = &ctx->destination_entries.interfaces[ifid];
- }
- if (entry && ETHER_ISMULTI(dstn)) {
- DHD_PKTTAG_SET_ENTRY(PKTTAG(p), entry);
- return entry;
- }
- for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
- if (table[i].occupied) {
- if (table[i].interface_id == ifid) {
- if (!memcmp(table[i].ea, dstn, ETHER_ADDR_LEN)) {
- entry = &table[i];
- break;
- }
- }
- }
- }
- if (entry == NULL)
- entry = &ctx->destination_entries.other;
- DHD_PKTTAG_SET_ENTRY(PKTTAG(p), entry);
- return entry;
- } /* _dhd_wlfc_find_table_entry */
- /**
- * In case a packet must be dropped (because eg the queues are full), various tallies have to be
- * be updated. Called from several other functions.
- * @param[in] dhdp pointer to public DHD structure
- * @param[in] prec precedence of the packet
- * @param[in] p the packet to be dropped
- * @param[in] bPktInQ TRUE if packet is part of a queue
- */
- static int
- _dhd_wlfc_prec_drop(dhd_pub_t *dhdp, int prec, void* p, bool bPktInQ)
- {
- athost_wl_status_info_t* ctx;
- void *pout = NULL;
- ASSERT(dhdp && p);
- if (prec < 0 || prec >= WLFC_PSQ_PREC_COUNT) {
- ASSERT(0);
- return BCME_BADARG;
- }
- ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
- if (!WLFC_GET_AFQ(dhdp->wlfc_mode) && (prec & 1)) {
- /* suppressed queue, need pop from hanger */
- _dhd_wlfc_hanger_poppkt(ctx->hanger, WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG
- (PKTTAG(p))), &pout, TRUE);
- ASSERT(p == pout);
- }
- if (!(prec & 1)) {
- #ifdef DHDTCPACK_SUPPRESS
- /* pkt in delayed q, so fake push BDC header for
- * dhd_tcpack_check_xmit() and dhd_txcomplete().
- */
- _dhd_wlfc_pushheader(ctx, &p, FALSE, 0, 0, 0, 0, TRUE);
- /* This packet is about to be freed, so remove it from tcp_ack_info_tbl
- * This must be one of...
- * 1. A pkt already in delayQ is evicted by another pkt with higher precedence
- * in _dhd_wlfc_prec_enq_with_drop()
- * 2. A pkt could not be enqueued to delayQ because it is full,
- * in _dhd_wlfc_enque_delayq().
- * 3. A pkt could not be enqueued to delayQ because it is full,
- * in _dhd_wlfc_rollback_packet_toq().
- */
- if (dhd_tcpack_check_xmit(dhdp, p) == BCME_ERROR) {
- DHD_ERROR(("%s %d: tcpack_suppress ERROR!!!"
- " Stop using it\n",
- __FUNCTION__, __LINE__));
- dhd_tcpack_suppress_set(dhdp, TCPACK_SUP_OFF);
- }
- #endif /* DHDTCPACK_SUPPRESS */
- }
- if (bPktInQ) {
- ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec>>1]--;
- ctx->pkt_cnt_per_ac[prec>>1]--;
- ctx->pkt_cnt_in_psq--;
- }
- ctx->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(p))][DHD_PKTTAG_FIFO(PKTTAG(p))]--;
- ctx->stats.pktout++;
- ctx->stats.drop_pkts[prec]++;
- dhd_txcomplete(dhdp, p, FALSE);
- PKTFREE(ctx->osh, p, TRUE);
- return 0;
- } /* _dhd_wlfc_prec_drop */
- /**
- * Called when eg the host handed a new packet over to the driver, or when the dongle reported
- * that a packet could currently not be transmitted (=suppressed). This function enqueues a transmit
- * packet in the host driver to be (re)transmitted at a later opportunity.
- * @param[in] dhdp pointer to public DHD structure
- * @param[in] qHead When TRUE, queue packet at head instead of tail, to preserve d11 sequence
- */
- static bool
- _dhd_wlfc_prec_enq_with_drop(dhd_pub_t *dhdp, struct pktq *pq, void *pkt, int prec, bool qHead,
- uint8 current_seq)
- {
- void *p = NULL;
- int eprec = -1; /* precedence to evict from */
- athost_wl_status_info_t* ctx;
- ASSERT(dhdp && pq && pkt);
- ASSERT(prec >= 0 && prec < pq->num_prec);
- ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
- /* Fast case, precedence queue is not full and we are also not
- * exceeding total queue length
- */
- if (!pktqprec_full(pq, prec) && !pktq_full(pq)) {
- goto exit;
- }
- /* Determine precedence from which to evict packet, if any */
- if (pktqprec_full(pq, prec)) {
- eprec = prec;
- } else if (pktq_full(pq)) {
- p = pktq_peek_tail(pq, &eprec);
- if (!p) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return FALSE;
- }
- if ((eprec > prec) || (eprec < 0)) {
- if (!pktqprec_empty(pq, prec)) {
- eprec = prec;
- } else {
- return FALSE;
- }
- }
- }
- /* Evict if needed */
- if (eprec >= 0) {
- /* Detect queueing to unconfigured precedence */
- ASSERT(!pktqprec_empty(pq, eprec));
- /* Evict all fragmented frames */
- dhd_prec_drop_pkts(dhdp, pq, eprec, _dhd_wlfc_prec_drop);
- }
- exit:
- /* Enqueue */
- _dhd_wlfc_prec_enque(pq, prec, pkt, qHead, current_seq,
- WLFC_GET_REORDERSUPP(dhdp->wlfc_mode));
- ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(pkt))][prec>>1]++;
- ctx->pkt_cnt_per_ac[prec>>1]++;
- ctx->pkt_cnt_in_psq++;
- return TRUE;
- } /* _dhd_wlfc_prec_enq_with_drop */
- /**
- * Called during eg the 'committing' of a transmit packet from the OS layer to a lower layer, in
- * the event that this 'commit' failed.
- */
- static int
- _dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t* ctx,
- void* p, ewlfc_packet_state_t pkt_type, uint32 hslot)
- {
- /*
- * put the packet back to the head of queue
- * - suppressed packet goes back to suppress sub-queue
- * - pull out the header, if new or delayed packet
- *
- * Note: hslot is used only when header removal is done.
- */
- wlfc_mac_descriptor_t* entry;
- int rc = BCME_OK;
- int prec, fifo_id;
- entry = _dhd_wlfc_find_table_entry(ctx, p);
- prec = DHD_PKTTAG_FIFO(PKTTAG(p));
- fifo_id = prec << 1;
- if (pkt_type == eWLFC_PKTTYPE_SUPPRESSED)
- fifo_id += 1;
- if (entry != NULL) {
- /*
- if this packet did not count against FIFO credit, it must have
- taken a requested_credit from the firmware (for pspoll etc.)
- */
- if ((prec != AC_COUNT) && !DHD_PKTTAG_CREDITCHECK(PKTTAG(p)))
- entry->requested_credit++;
- if (pkt_type == eWLFC_PKTTYPE_DELAYED) {
- /* decrement sequence count */
- WLFC_DECR_SEQCOUNT(entry, prec);
- /* remove header first */
- rc = _dhd_wlfc_pullheader(ctx, p);
- if (rc != BCME_OK) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- goto exit;
- }
- }
- if (_dhd_wlfc_prec_enq_with_drop(ctx->dhdp, &entry->psq, p, fifo_id, TRUE,
- WLFC_SEQCOUNT(entry, fifo_id>>1))
- == FALSE) {
- /* enque failed */
- DHD_ERROR(("Error: %s():%d, fifo_id(%d)\n",
- __FUNCTION__, __LINE__, fifo_id));
- rc = BCME_ERROR;
- }
- } else {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- rc = BCME_ERROR;
- }
- exit:
- if (rc != BCME_OK) {
- ctx->stats.rollback_failed++;
- _dhd_wlfc_prec_drop(ctx->dhdp, fifo_id, p, FALSE);
- } else {
- ctx->stats.rollback++;
- }
- return rc;
- } /* _dhd_wlfc_rollback_packet_toq */
- /** Returns TRUE if host OS -> DHD flow control is allowed on the caller supplied interface */
- static bool
- _dhd_wlfc_allow_fc(athost_wl_status_info_t* ctx, uint8 ifid)
- {
- int prec, ac_traffic = WLFC_NO_TRAFFIC;
- for (prec = 0; prec < AC_COUNT; prec++) {
- if (ctx->pkt_cnt_in_drv[ifid][prec] > 0) {
- if (ac_traffic == WLFC_NO_TRAFFIC)
- ac_traffic = prec + 1;
- else if (ac_traffic != (prec + 1))
- ac_traffic = WLFC_MULTI_TRAFFIC;
- }
- }
- if (ac_traffic >= 1 && ac_traffic <= AC_COUNT) {
- /* single AC (BE/BK/VI/VO) in queue */
- if (ctx->allow_fc) {
- return TRUE;
- } else {
- uint32 delta;
- uint32 curr_t = OSL_SYSUPTIME();
- if (ctx->fc_defer_timestamp == 0) {
- /* first single ac scenario */
- ctx->fc_defer_timestamp = curr_t;
- return FALSE;
- }
- /* single AC duration, this handles wrap around, e.g. 1 - ~0 = 2. */
- delta = curr_t - ctx->fc_defer_timestamp;
- if (delta >= WLFC_FC_DEFER_PERIOD_MS) {
- ctx->allow_fc = TRUE;
- }
- }
- } else {
- /* multiple ACs or BCMC in queue */
- ctx->allow_fc = FALSE;
- ctx->fc_defer_timestamp = 0;
- }
- return ctx->allow_fc;
- } /* _dhd_wlfc_allow_fc */
- /**
- * Starts or stops the flow of transmit packets from the host OS towards the DHD, depending on
- * low/high watermarks.
- */
- static void
- _dhd_wlfc_flow_control_check(athost_wl_status_info_t* ctx, struct pktq* pq, uint8 if_id)
- {
- dhd_pub_t *dhdp;
- ASSERT(ctx);
- dhdp = (dhd_pub_t *)ctx->dhdp;
- ASSERT(dhdp);
- if (dhdp->skip_fc && dhdp->skip_fc((void *)dhdp, if_id))
- return;
- if ((ctx->hostif_flow_state[if_id] == OFF) && !_dhd_wlfc_allow_fc(ctx, if_id))
- return;
- if ((pq->n_pkts_tot <= WLFC_FLOWCONTROL_LOWATER) && (ctx->hostif_flow_state[if_id] == ON)) {
- /* start traffic */
- ctx->hostif_flow_state[if_id] = OFF;
- /*
- WLFC_DBGMESG(("qlen:%02d, if:%02d, ->OFF, start traffic %s()\n",
- pq->n_pkts_tot, if_id, __FUNCTION__));
- */
- WLFC_DBGMESG(("F"));
- dhd_txflowcontrol(dhdp, if_id, OFF);
- ctx->toggle_host_if = 0;
- }
- if (pq->n_pkts_tot >= WLFC_FLOWCONTROL_HIWATER && ctx->hostif_flow_state[if_id] == OFF) {
- /* stop traffic */
- ctx->hostif_flow_state[if_id] = ON;
- /*
- WLFC_DBGMESG(("qlen:%02d, if:%02d, ->ON, stop traffic %s()\n",
- pq->n_pkts_tot, if_id, __FUNCTION__));
- */
- WLFC_DBGMESG(("N"));
- dhd_txflowcontrol(dhdp, if_id, ON);
- ctx->host_ifidx = if_id;
- ctx->toggle_host_if = 1;
- }
- return;
- } /* _dhd_wlfc_flow_control_check */
- static int
- _dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
- uint8 ta_bmp)
- {
- int rc = BCME_OK;
- void* p = NULL;
- int dummylen = ((dhd_pub_t *)ctx->dhdp)->hdrlen+ 16;
- dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
- if (dhdp->proptxstatus_txoff) {
- rc = BCME_NORESOURCE;
- return rc;
- }
- /* allocate a dummy packet */
- p = PKTGET(ctx->osh, dummylen, TRUE);
- if (p) {
- PKTPULL(ctx->osh, p, dummylen);
- DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), 0);
- _dhd_wlfc_pushheader(ctx, &p, TRUE, ta_bmp, entry->mac_handle, 0, 0, FALSE);
- DHD_PKTTAG_SETSIGNALONLY(PKTTAG(p), 1);
- DHD_PKTTAG_WLFCPKT_SET(PKTTAG(p), 1);
- #ifdef PROP_TXSTATUS_DEBUG
- ctx->stats.signal_only_pkts_sent++;
- #endif // endif
- #if defined(BCMPCIE)
- rc = dhd_bus_txdata(dhdp->bus, p, ctx->host_ifidx);
- #else
- rc = dhd_bus_txdata(dhdp->bus, p);
- #endif // endif
- if (rc != BCME_OK) {
- _dhd_wlfc_pullheader(ctx, p);
- PKTFREE(ctx->osh, p, TRUE);
- }
- } else {
- DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n",
- __FUNCTION__, dummylen));
- rc = BCME_NOMEM;
- dhdp->tx_pktgetfail++;
- }
- return rc;
- } /* _dhd_wlfc_send_signalonly_packet */
- /**
- * Called on eg receiving 'mac close' indication from dongle. Updates the per-MAC administration
- * maintained in caller supplied parameter 'entry'.
- *
- * @param[in/out] entry administration about a remote MAC entity
- * @param[in] prec precedence queue for this remote MAC entitity
- *
- * Return value: TRUE if traffic availability changed
- */
- static bool
- _dhd_wlfc_traffic_pending_check(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
- int prec)
- {
- bool rc = FALSE;
- if (entry->state == WLFC_STATE_CLOSE) {
- if ((pktqprec_n_pkts(&entry->psq, (prec << 1)) == 0) &&
- (pktqprec_n_pkts(&entry->psq, ((prec << 1) + 1)) == 0)) {
- /* no packets in both 'normal' and 'suspended' queues */
- if (entry->traffic_pending_bmp & NBITVAL(prec)) {
- rc = TRUE;
- entry->traffic_pending_bmp =
- entry->traffic_pending_bmp & ~ NBITVAL(prec);
- }
- } else {
- /* packets are queued in host for transmission to dongle */
- if (!(entry->traffic_pending_bmp & NBITVAL(prec))) {
- rc = TRUE;
- entry->traffic_pending_bmp =
- entry->traffic_pending_bmp | NBITVAL(prec);
- }
- }
- }
- if (rc) {
- /* request a TIM update to firmware at the next piggyback opportunity */
- if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) {
- entry->send_tim_signal = 1;
- _dhd_wlfc_send_signalonly_packet(ctx, entry, entry->traffic_pending_bmp);
- entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
- entry->send_tim_signal = 0;
- } else {
- rc = FALSE;
- }
- }
- return rc;
- } /* _dhd_wlfc_traffic_pending_check */
- /**
- * Called on receiving a 'd11 suppressed' or 'wl suppressed' tx status from the firmware. Enqueues
- * the packet to transmit to firmware again at a later opportunity.
- */
- static int
- _dhd_wlfc_enque_suppressed(athost_wl_status_info_t* ctx, int prec, void* p)
- {
- wlfc_mac_descriptor_t* entry;
- entry = _dhd_wlfc_find_table_entry(ctx, p);
- if (entry == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_NOTFOUND;
- }
- /*
- - suppressed packets go to sub_queue[2*prec + 1] AND
- - delayed packets go to sub_queue[2*prec + 0] to ensure
- order of delivery.
- */
- if (_dhd_wlfc_prec_enq_with_drop(ctx->dhdp, &entry->psq, p, ((prec << 1) + 1), FALSE,
- WLFC_SEQCOUNT(entry, prec))
- == FALSE) {
- ctx->stats.delayq_full_error++;
- /* WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); */
- WLFC_DBGMESG(("s"));
- return BCME_ERROR;
- }
- /* A packet has been pushed, update traffic availability bitmap, if applicable */
- _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
- _dhd_wlfc_flow_control_check(ctx, &entry->psq, DHD_PKTTAG_IF(PKTTAG(p)));
- return BCME_OK;
- }
- /**
- * Called when a transmit packet is about to be 'committed' from the OS layer to a lower layer
- * towards the dongle (eg the DBUS layer). Updates wlfc administration. May modify packet.
- *
- * @param[in/out] ctx driver specific flow control administration
- * @param[in/out] entry The remote MAC entity for which the packet is destined.
- * @param[in/out] packet Packet to send. This function optionally adds TLVs to the packet.
- * @param[in] header_needed True if packet is 'new' to flow control
- * @param[out] slot Handle to container in which the packet was 'parked'
- */
- static int
- _dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t* ctx,
- wlfc_mac_descriptor_t* entry, void** packet, int header_needed, uint32* slot)
- {
- int rc = BCME_OK;
- int hslot = WLFC_HANGER_MAXITEMS;
- bool send_tim_update = FALSE;
- uint32 htod = 0;
- uint16 htodseq = 0;
- uint8 free_ctr;
- int gen = 0xff;
- dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
- void * p = *packet;
- *slot = hslot;
- if (entry == NULL) {
- entry = _dhd_wlfc_find_table_entry(ctx, p);
- }
- if (entry == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_ERROR;
- }
- if (entry->send_tim_signal) {
- /* sends a traffic indication bitmap to the dongle */
- send_tim_update = TRUE;
- entry->send_tim_signal = 0;
- entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
- }
- if (header_needed) {
- if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
- hslot = (uint)(entry - &ctx->destination_entries.nodes[0]);
- } else {
- hslot = _dhd_wlfc_hanger_get_free_slot(ctx->hanger);
- }
- gen = entry->generation;
- free_ctr = WLFC_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
- } else {
- if (WLFC_GET_REUSESEQ(dhdp->wlfc_mode)) {
- htodseq = DHD_PKTTAG_H2DSEQ(PKTTAG(p));
- }
- hslot = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
- if (WLFC_GET_REORDERSUPP(dhdp->wlfc_mode)) {
- gen = entry->generation;
- } else if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
- gen = WL_TXSTATUS_GET_GENERATION(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
- } else {
- _dhd_wlfc_hanger_get_genbit(ctx->hanger, p, hslot, &gen);
- }
- free_ctr = WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
- /* remove old header */
- _dhd_wlfc_pullheader(ctx, p);
- }
- if (hslot >= WLFC_HANGER_MAXITEMS) {
- DHD_ERROR(("Error: %s():no hanger slot available\n", __FUNCTION__));
- return BCME_ERROR;
- }
- WL_TXSTATUS_SET_FREERUNCTR(htod, free_ctr);
- WL_TXSTATUS_SET_HSLOT(htod, hslot);
- WL_TXSTATUS_SET_FIFO(htod, DHD_PKTTAG_FIFO(PKTTAG(p)));
- WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST);
- WL_TXSTATUS_SET_GENERATION(htod, gen);
- DHD_PKTTAG_SETPKTDIR(PKTTAG(p), 1);
- if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) {
- /*
- Indicate that this packet is being sent in response to an
- explicit request from the firmware side.
- */
- WLFC_PKTFLAG_SET_PKTREQUESTED(htod);
- } else {
- WLFC_PKTFLAG_CLR_PKTREQUESTED(htod);
- }
- rc = _dhd_wlfc_pushheader(ctx, &p, send_tim_update,
- entry->traffic_lastreported_bmp, entry->mac_handle, htod, htodseq, FALSE);
- if (rc == BCME_OK) {
- DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod);
- if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
- wlfc_hanger_t *h = (wlfc_hanger_t*)(ctx->hanger);
- if (header_needed) {
- /*
- a new header was created for this packet.
- push to hanger slot and scrub q. Since bus
- send succeeded, increment seq number as well.
- */
- rc = _dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot);
- if (rc == BCME_OK) {
- #ifdef PROP_TXSTATUS_DEBUG
- h->items[hslot].push_time =
- OSL_SYSUPTIME();
- #endif // endif
- } else {
- DHD_ERROR(("%s() hanger_pushpkt() failed, rc: %d\n",
- __FUNCTION__, rc));
- }
- } else {
- /* clear hanger state */
- if (((wlfc_hanger_t*)(ctx->hanger))->items[hslot].pkt != p)
- DHD_ERROR(("%s() pkt not match: cur %p, hanger pkt %p\n",
- __FUNCTION__, p, h->items[hslot].pkt));
- ASSERT(h->items[hslot].pkt == p);
- bcm_object_feature_set(h->items[hslot].pkt,
- BCM_OBJECT_FEATURE_PKT_STATE, 0);
- h->items[hslot].pkt_state = 0;
- h->items[hslot].pkt_txstatus = 0;
- h->items[hslot].state = WLFC_HANGER_ITEM_STATE_INUSE;
- }
- }
- if ((rc == BCME_OK) && header_needed) {
- /* increment free running sequence count */
- WLFC_INCR_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
- }
- }
- *slot = hslot;
- *packet = p;
- return rc;
- } /* _dhd_wlfc_pretx_pktprocess */
- /**
- * A remote wireless mac may be temporarily 'closed' due to power management. Returns '1' if remote
- * mac is in the 'open' state, otherwise '0'.
- */
- static int
- _dhd_wlfc_is_destination_open(athost_wl_status_info_t* ctx,
- wlfc_mac_descriptor_t* entry, int prec)
- {
- wlfc_mac_descriptor_t* interfaces = ctx->destination_entries.interfaces;
- if (entry->interface_id >= WLFC_MAX_IFNUM) {
- ASSERT(&ctx->destination_entries.other == entry);
- return 1;
- }
- if (interfaces[entry->interface_id].iftype ==
- WLC_E_IF_ROLE_P2P_GO) {
- /* - destination interface is of type p2p GO.
- For a p2pGO interface, if the destination is OPEN but the interface is
- CLOSEd, do not send traffic. But if the dstn is CLOSEd while there is
- destination-specific-credit left send packets. This is because the
- firmware storing the destination-specific-requested packet in queue.
- */
- if ((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) &&
- (entry->requested_packet == 0)) {
- return 0;
- }
- }
- /* AP, p2p_go -> unicast desc entry, STA/p2p_cl -> interface desc. entry */
- if ((((entry->state == WLFC_STATE_CLOSE) ||
- (interfaces[entry->interface_id].state == WLFC_STATE_CLOSE)) &&
- (entry->requested_credit == 0) &&
- (entry->requested_packet == 0)) ||
- (!(entry->ac_bitmap & (1 << prec)))) {
- return 0;
- }
- return 1;
- } /* _dhd_wlfc_is_destination_open */
- /**
- * Dequeues a suppressed or delayed packet from a queue
- * @param[in/out] ctx Driver specific flow control administration
- * @param[in] prec Precedence of queue to dequeue from
- * @param[out] ac_credit_spent Boolean, returns 0 or 1
- * @param[out] needs_hdr Boolean, returns 0 or 1
- * @param[out] entry_out The remote MAC for which the packet is destined
- * @param[in] only_no_credit If TRUE, searches all entries instead of just the active ones
- *
- * Return value: the dequeued packet
- */
- static void*
- _dhd_wlfc_deque_delayedq(athost_wl_status_info_t* ctx, int prec,
- uint8* ac_credit_spent, uint8* needs_hdr, wlfc_mac_descriptor_t** entry_out,
- bool only_no_credit)
- {
- wlfc_mac_descriptor_t* entry;
- int total_entries;
- void* p = NULL;
- int i;
- uint8 credit_spent = ((prec == AC_COUNT) && !ctx->bcmc_credit_supported) ? 0 : 1;
- *entry_out = NULL;
- /* most cases a packet will count against FIFO credit */
- *ac_credit_spent = credit_spent;
- /* search all entries, include nodes as well as interfaces */
- if (only_no_credit) {
- total_entries = ctx->requested_entry_count;
- } else {
- total_entries = ctx->active_entry_count;
- }
- for (i = 0; i < total_entries; i++) {
- if (only_no_credit) {
- entry = ctx->requested_entry[i];
- } else {
- entry = ctx->active_entry_head;
- /* move head to ensure fair round-robin */
- ctx->active_entry_head = ctx->active_entry_head->next;
- }
- ASSERT(entry);
- if (entry->occupied && _dhd_wlfc_is_destination_open(ctx, entry, prec) &&
- (entry->transit_count < WL_TXSTATUS_FREERUNCTR_MASK) &&
- (!entry->suppressed)) {
- *ac_credit_spent = credit_spent;
- if (entry->state == WLFC_STATE_CLOSE) {
- *ac_credit_spent = 0;
- }
- /* higher precedence will be picked up first,
- * i.e. suppressed packets before delayed ones
- */
- p = pktq_pdeq(&entry->psq, PSQ_SUP_IDX(prec));
- *needs_hdr = 0;
- if (p == NULL) {
- /* De-Q from delay Q */
- p = pktq_pdeq(&entry->psq, PSQ_DLY_IDX(prec));
- *needs_hdr = 1;
- }
- if (p != NULL) {
- bcm_pkt_validate_chk(p);
- /* did the packet come from suppress sub-queue? */
- if (entry->requested_credit > 0) {
- entry->requested_credit--;
- #ifdef PROP_TXSTATUS_DEBUG
- entry->dstncredit_sent_packets++;
- #endif // endif
- } else if (entry->requested_packet > 0) {
- entry->requested_packet--;
- DHD_PKTTAG_SETONETIMEPKTRQST(PKTTAG(p));
- }
- *entry_out = entry;
- ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec]--;
- ctx->pkt_cnt_per_ac[prec]--;
- ctx->pkt_cnt_in_psq--;
- _dhd_wlfc_flow_control_check(ctx, &entry->psq,
- DHD_PKTTAG_IF(PKTTAG(p)));
- /*
- * A packet has been picked up, update traffic availability bitmap,
- * if applicable.
- */
- _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
- return p;
- }
- }
- }
- return NULL;
- } /* _dhd_wlfc_deque_delayedq */
- /** Enqueues caller supplied packet on either a 'suppressed' or 'delayed' queue */
- static int
- _dhd_wlfc_enque_delayq(athost_wl_status_info_t* ctx, void* pktbuf, int prec)
- {
- wlfc_mac_descriptor_t* entry;
- if (pktbuf != NULL) {
- entry = _dhd_wlfc_find_table_entry(ctx, pktbuf);
- if (entry == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_ERROR;
- }
- /*
- - suppressed packets go to sub_queue[2*prec + 1] AND
- - delayed packets go to sub_queue[2*prec + 0] to ensure
- order of delivery.
- */
- if (_dhd_wlfc_prec_enq_with_drop(ctx->dhdp, &entry->psq, pktbuf, (prec << 1),
- FALSE, WLFC_SEQCOUNT(entry, prec))
- == FALSE) {
- WLFC_DBGMESG(("D"));
- ctx->stats.delayq_full_error++;
- return BCME_ERROR;
- }
- /* A packet has been pushed, update traffic availability bitmap, if applicable */
- _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
- }
- return BCME_OK;
- } /* _dhd_wlfc_enque_delayq */
- /** Returns TRUE if caller supplied packet is destined for caller supplied interface */
- static bool _dhd_wlfc_ifpkt_fn(void* p, void *p_ifid)
- {
- if (!p || !p_ifid)
- return FALSE;
- return (DHD_PKTTAG_WLFCPKT(PKTTAG(p))&& (*((uint8 *)p_ifid) == DHD_PKTTAG_IF(PKTTAG(p))));
- }
- /** Returns TRUE if caller supplied packet is destined for caller supplied remote MAC */
- static bool _dhd_wlfc_entrypkt_fn(void* p, void *entry)
- {
- if (!p || !entry)
- return FALSE;
- return (DHD_PKTTAG_WLFCPKT(PKTTAG(p))&& (entry == DHD_PKTTAG_ENTRY(PKTTAG(p))));
- }
- static void
- _dhd_wlfc_return_implied_credit(athost_wl_status_info_t* wlfc, void* pkt)
- {
- dhd_pub_t *dhdp;
- bool credit_return = FALSE;
- if (!wlfc || !pkt) {
- return;
- }
- dhdp = (dhd_pub_t *)(wlfc->dhdp);
- if (dhdp && (dhdp->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) &&
- DHD_PKTTAG_CREDITCHECK(PKTTAG(pkt))) {
- int lender, credit_returned = 0;
- uint8 fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pkt));
- credit_return = TRUE;
- /* Note that borrower is fifo_id */
- /* Return credits to highest priority lender first */
- for (lender = AC_COUNT; lender >= 0; lender--) {
- if (wlfc->credits_borrowed[fifo_id][lender] > 0) {
- wlfc->FIFO_credit[lender]++;
- wlfc->credits_borrowed[fifo_id][lender]--;
- credit_returned = 1;
- break;
- }
- }
- if (!credit_returned) {
- wlfc->FIFO_credit[fifo_id]++;
- }
- }
- BCM_REFERENCE(credit_return);
- #if defined(DHD_WLFC_THREAD)
- if (credit_return) {
- _dhd_wlfc_thread_wakeup(dhdp);
- }
- #endif /* defined(DHD_WLFC_THREAD) */
- }
- /** Removes and frees a packet from the hanger. Called during eg tx complete. */
- static void
- _dhd_wlfc_hanger_free_pkt(athost_wl_status_info_t* wlfc, uint32 slot_id, uint8 pkt_state,
- int pkt_txstatus)
- {
- wlfc_hanger_t* hanger;
- wlfc_hanger_item_t* item;
- if (!wlfc)
- return;
- hanger = (wlfc_hanger_t*)wlfc->hanger;
- if (!hanger)
- return;
- if (slot_id == WLFC_HANGER_MAXITEMS)
- return;
- item = &hanger->items[slot_id];
- if (item->pkt) {
- item->pkt_state |= pkt_state;
- if (pkt_txstatus != -1)
- item->pkt_txstatus = (uint8)pkt_txstatus;
- bcm_object_feature_set(item->pkt, BCM_OBJECT_FEATURE_PKT_STATE, item->pkt_state);
- if (item->pkt_state == WLFC_HANGER_PKT_STATE_COMPLETE) {
- void *p = NULL;
- void *pkt = item->pkt;
- uint8 old_state = item->state;
- int ret = _dhd_wlfc_hanger_poppkt(wlfc->hanger, slot_id, &p, TRUE);
- BCM_REFERENCE(ret);
- BCM_REFERENCE(pkt);
- ASSERT((ret == BCME_OK) && p && (pkt == p));
- if (old_state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
- printf("ERROR: free a suppressed pkt %p state %d pkt_state %d\n",
- pkt, old_state, item->pkt_state);
- }
- ASSERT(old_state != WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED);
- /* free packet */
- wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(p))]
- [DHD_PKTTAG_FIFO(PKTTAG(p))]--;
- wlfc->stats.pktout++;
- dhd_txcomplete((dhd_pub_t *)wlfc->dhdp, p, item->pkt_txstatus);
- PKTFREE(wlfc->osh, p, TRUE);
- }
- } else {
- /* free slot */
- if (item->state == WLFC_HANGER_ITEM_STATE_FREE)
- DHD_ERROR(("Error: %s():%d Multiple TXSTATUS or BUSRETURNED: %d (%d)\n",
- __FUNCTION__, __LINE__, item->pkt_state, pkt_state));
- item->state = WLFC_HANGER_ITEM_STATE_FREE;
- }
- } /* _dhd_wlfc_hanger_free_pkt */
- /** Called during eg detach() */
- static void
- _dhd_wlfc_pktq_flush(athost_wl_status_info_t* ctx, struct pktq *pq,
- bool dir, f_processpkt_t fn, void *arg, q_type_t q_type)
- {
- int prec;
- dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
- ASSERT(dhdp);
- /* Optimize flush, if pktq len = 0, just return.
- * pktq len of 0 means pktq's prec q's are all empty.
- */
- if (pq->n_pkts_tot == 0) {
- return;
- }
- for (prec = 0; prec < pq->num_prec; prec++) {
- struct pktq_prec *q;
- void *p, *prev = NULL;
- q = &pq->q[prec];
- p = q->head;
- while (p) {
- bcm_pkt_validate_chk(p);
- if (fn == NULL || (*fn)(p, arg)) {
- bool head = (p == q->head);
- if (head)
- q->head = PKTLINK(p);
- else
- PKTSETLINK(prev, PKTLINK(p));
- if (q_type == Q_TYPE_PSQ) {
- if (!WLFC_GET_AFQ(dhdp->wlfc_mode) && (prec & 1)) {
- _dhd_wlfc_hanger_remove_reference(ctx->hanger, p);
- }
- ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec>>1]--;
- ctx->pkt_cnt_per_ac[prec>>1]--;
- ctx->pkt_cnt_in_psq--;
- ctx->stats.cleanup_psq_cnt++;
- if (!(prec & 1)) {
- /* pkt in delayed q, so fake push BDC header for
- * dhd_tcpack_check_xmit() and dhd_txcomplete().
- */
- _dhd_wlfc_pushheader(ctx, &p, FALSE, 0, 0,
- 0, 0, TRUE);
- #ifdef DHDTCPACK_SUPPRESS
- if (dhd_tcpack_check_xmit(dhdp, p) == BCME_ERROR) {
- DHD_ERROR(("%s %d: tcpack_suppress ERROR!!!"
- " Stop using it\n",
- __FUNCTION__, __LINE__));
- dhd_tcpack_suppress_set(dhdp,
- TCPACK_SUP_OFF);
- }
- #endif /* DHDTCPACK_SUPPRESS */
- }
- } else if (q_type == Q_TYPE_AFQ) {
- wlfc_mac_descriptor_t* entry =
- _dhd_wlfc_find_table_entry(ctx, p);
- if (entry->transit_count)
- entry->transit_count--;
- if (entry->suppr_transit_count) {
- entry->suppr_transit_count--;
- if (entry->suppressed &&
- (!entry->onbus_pkts_count) &&
- (!entry->suppr_transit_count))
- entry->suppressed = FALSE;
- }
- _dhd_wlfc_return_implied_credit(ctx, p);
- ctx->stats.cleanup_fw_cnt++;
- }
- PKTSETLINK(p, NULL);
- if (dir) {
- ctx->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(p))][prec>>1]--;
- ctx->stats.pktout++;
- dhd_txcomplete(dhdp, p, FALSE);
- }
- PKTFREE(ctx->osh, p, dir);
- q->n_pkts--;
- pq->n_pkts_tot--;
- #ifdef WL_TXQ_STALL
- q->dequeue_count++;
- #endif // endif
- p = (head ? q->head : PKTLINK(prev));
- } else {
- prev = p;
- p = PKTLINK(p);
- }
- }
- if (q->head == NULL) {
- ASSERT(q->n_pkts == 0);
- q->tail = NULL;
- }
- }
- if (fn == NULL)
- ASSERT(pq->n_pkts_tot == 0);
- } /* _dhd_wlfc_pktq_flush */
- /** !BCMDBUS specific function. Dequeues a packet from the caller supplied queue. */
- static void*
- _dhd_wlfc_pktq_pdeq_with_fn(struct pktq *pq, int prec, f_processpkt_t fn, void *arg)
- {
- struct pktq_prec *q;
- void *p, *prev = NULL;
- ASSERT(prec >= 0 && prec < pq->num_prec);
- q = &pq->q[prec];
- p = q->head;
- while (p) {
- if (fn == NULL || (*fn)(p, arg)) {
- break;
- } else {
- prev = p;
- p = PKTLINK(p);
- }
- }
- if (p == NULL)
- return NULL;
- bcm_pkt_validate_chk(p);
- if (prev == NULL) {
- if ((q->head = PKTLINK(p)) == NULL) {
- q->tail = NULL;
- }
- } else {
- PKTSETLINK(prev, PKTLINK(p));
- if (q->tail == p) {
- q->tail = prev;
- }
- }
- q->n_pkts--;
- pq->n_pkts_tot--;
- #ifdef WL_TXQ_STALL
- q->dequeue_count++;
- #endif // endif
- PKTSETLINK(p, NULL);
- return p;
- }
- /** !BCMDBUS specific function */
- static void
- _dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
- {
- int prec;
- void *pkt = NULL, *head = NULL, *tail = NULL;
- struct pktq *txq = (struct pktq *)dhd_bus_txq(dhd->bus);
- athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
- wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
- wlfc_mac_descriptor_t* entry;
- dhd_os_sdlock_txq(dhd);
- for (prec = 0; prec < txq->num_prec; prec++) {
- while ((pkt = _dhd_wlfc_pktq_pdeq_with_fn(txq, prec, fn, arg))) {
- #ifdef DHDTCPACK_SUPPRESS
- if (dhd_tcpack_check_xmit(dhd, pkt) == BCME_ERROR) {
- DHD_ERROR(("%s %d: tcpack_suppress ERROR!!! Stop using it\n",
- __FUNCTION__, __LINE__));
- dhd_tcpack_suppress_set(dhd, TCPACK_SUP_OFF);
- }
- #endif /* DHDTCPACK_SUPPRESS */
- if (!head) {
- head = pkt;
- }
- if (tail) {
- PKTSETLINK(tail, pkt);
- }
- tail = pkt;
- }
- }
- dhd_os_sdunlock_txq(dhd);
- while ((pkt = head)) {
- head = PKTLINK(pkt);
- PKTSETLINK(pkt, NULL);
- entry = _dhd_wlfc_find_table_entry(wlfc, pkt);
- if (!WLFC_GET_AFQ(dhd->wlfc_mode) &&
- !_dhd_wlfc_hanger_remove_reference(h, pkt)) {
- DHD_ERROR(("%s: can't find pkt(%p) in hanger, free it anyway\n",
- __FUNCTION__, pkt));
- }
- if (entry->transit_count)
- entry->transit_count--;
- if (entry->suppr_transit_count) {
- entry->suppr_transit_count--;
- if (entry->suppressed &&
- (!entry->onbus_pkts_count) &&
- (!entry->suppr_transit_count))
- entry->suppressed = FALSE;
- }
- _dhd_wlfc_return_implied_credit(wlfc, pkt);
- wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pkt))][DHD_PKTTAG_FIFO(PKTTAG(pkt))]--;
- wlfc->stats.pktout++;
- wlfc->stats.cleanup_txq_cnt++;
- dhd_txcomplete(dhd, pkt, FALSE);
- PKTFREE(wlfc->osh, pkt, TRUE);
- }
- } /* _dhd_wlfc_cleanup_txq */
- /** called during eg detach */
- void
- _dhd_wlfc_cleanup(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
- {
- int i;
- int total_entries;
- athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
- wlfc_mac_descriptor_t* table;
- wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
- wlfc->stats.cleanup_txq_cnt = 0;
- wlfc->stats.cleanup_psq_cnt = 0;
- wlfc->stats.cleanup_fw_cnt = 0;
- /*
- * flush sequence should be txq -> psq -> hanger/afq, hanger has to be last one
- */
- /* flush bus->txq */
- _dhd_wlfc_cleanup_txq(dhd, fn, arg);
- /* flush psq, search all entries, include nodes as well as interfaces */
- total_entries = sizeof(wlfc->destination_entries)/sizeof(wlfc_mac_descriptor_t);
- table = (wlfc_mac_descriptor_t*)&wlfc->destination_entries;
- for (i = 0; i < total_entries; i++) {
- if (table[i].occupied) {
- /* release packets held in PSQ (both delayed and suppressed) */
- if (table[i].psq.n_pkts_tot) {
- WLFC_DBGMESG(("%s(): PSQ[%d].len = %d\n",
- __FUNCTION__, i, table[i].psq.n_pkts_tot));
- _dhd_wlfc_pktq_flush(wlfc, &table[i].psq, TRUE,
- fn, arg, Q_TYPE_PSQ);
- }
- /* free packets held in AFQ */
- if (WLFC_GET_AFQ(dhd->wlfc_mode) && (table[i].afq.n_pkts_tot)) {
- _dhd_wlfc_pktq_flush(wlfc, &table[i].afq, TRUE,
- fn, arg, Q_TYPE_AFQ);
- }
- if ((fn == NULL) && (&table[i] != &wlfc->destination_entries.other)) {
- table[i].occupied = 0;
- if (table[i].transit_count || table[i].suppr_transit_count) {
- DHD_ERROR(("%s: table[%d] transit(%d), suppr_tansit(%d)\n",
- __FUNCTION__, i,
- table[i].transit_count,
- table[i].suppr_transit_count));
- }
- }
- }
- }
- /*
- . flush remained pkt in hanger queue, not in bus->txq nor psq.
- . the remained pkt was successfully downloaded to dongle already.
- . hanger slot state cannot be set to free until receive txstatus update.
- */
- if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
- for (i = 0; i < h->max_items; i++) {
- if ((h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) ||
- (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED)) {
- if (fn == NULL || (*fn)(h->items[i].pkt, arg)) {
- h->items[i].state = WLFC_HANGER_ITEM_STATE_FLUSHED;
- }
- }
- }
- }
- return;
- } /* _dhd_wlfc_cleanup */
- /** Called after eg the dongle signalled a new remote MAC that it connected with to the DHD */
- static int
- _dhd_wlfc_mac_entry_update(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
- uint8 action, uint8 ifid, uint8 iftype, uint8* ea,
- f_processpkt_t fn, void *arg)
- {
- int rc = BCME_OK;
- if ((action == eWLFC_MAC_ENTRY_ACTION_ADD) || (action == eWLFC_MAC_ENTRY_ACTION_UPDATE)) {
- entry->occupied = 1;
- entry->state = WLFC_STATE_OPEN;
- entry->requested_credit = 0;
- entry->interface_id = ifid;
- entry->iftype = iftype;
- entry->ac_bitmap = 0xff; /* update this when handling APSD */
- /* for an interface entry we may not care about the MAC address */
- if (ea != NULL)
- memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN);
- if (action == eWLFC_MAC_ENTRY_ACTION_ADD) {
- entry->suppressed = FALSE;
- entry->transit_count = 0;
- entry->suppr_transit_count = 0;
- entry->onbus_pkts_count = 0;
- }
- if (action == eWLFC_MAC_ENTRY_ACTION_ADD) {
- dhd_pub_t *dhdp = (dhd_pub_t *)(ctx->dhdp);
- pktq_init(&entry->psq, WLFC_PSQ_PREC_COUNT, WLFC_PSQ_LEN);
- _dhd_wlfc_flow_control_check(ctx, &entry->psq, ifid);
- if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
- pktq_init(&entry->afq, WLFC_AFQ_PREC_COUNT, WLFC_PSQ_LEN);
- }
- if (entry->next == NULL) {
- /* not linked to anywhere, add to tail */
- if (ctx->active_entry_head) {
- entry->prev = ctx->active_entry_head->prev;
- ctx->active_entry_head->prev->next = entry;
- ctx->active_entry_head->prev = entry;
- entry->next = ctx->active_entry_head;
- } else {
- ASSERT(ctx->active_entry_count == 0);
- entry->prev = entry->next = entry;
- ctx->active_entry_head = entry;
- }
- ctx->active_entry_count++;
- } else {
- DHD_ERROR(("%s():%d, entry(%d)\n", __FUNCTION__, __LINE__,
- (int)(entry - &ctx->destination_entries.nodes[0])));
- }
- }
- } else if (action == eWLFC_MAC_ENTRY_ACTION_DEL) {
- /* When the entry is deleted, the packets that are queued in the entry must be
- cleanup. The cleanup action should be before the occupied is set as 0.
- */
- _dhd_wlfc_cleanup(ctx->dhdp, fn, arg);
- _dhd_wlfc_flow_control_check(ctx, &entry->psq, ifid);
- entry->occupied = 0;
- entry->state = WLFC_STATE_CLOSE;
- memset(&entry->ea[0], 0, ETHER_ADDR_LEN);
- if (entry->next) {
- /* not floating, remove from Q */
- if (ctx->active_entry_count <= 1) {
- /* last item */
- ctx->active_entry_head = NULL;
- ctx->active_entry_count = 0;
- } else {
- entry->prev->next = entry->next;
- entry->next->prev = entry->prev;
- if (entry == ctx->active_entry_head) {
- ctx->active_entry_head = entry->next;
- }
- ctx->active_entry_count--;
- }
- entry->next = entry->prev = NULL;
- } else {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- }
- }
- return rc;
- } /* _dhd_wlfc_mac_entry_update */
- #ifdef LIMIT_BORROW
- /** LIMIT_BORROW specific function */
- static int
- _dhd_wlfc_borrow_credit(athost_wl_status_info_t* ctx, int highest_lender_ac, int borrower_ac,
- bool bBorrowAll)
- {
- int lender_ac, borrow_limit = 0;
- int rc = -1;
- if (ctx == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return -1;
- }
- /* Borrow from lowest priority available AC (including BC/MC credits) */
- for (lender_ac = 0; lender_ac <= highest_lender_ac; lender_ac++) {
- if (!bBorrowAll) {
- borrow_limit = ctx->Init_FIFO_credit[lender_ac]/WLFC_BORROW_LIMIT_RATIO;
- } else {
- borrow_limit = 0;
- }
- if (ctx->FIFO_credit[lender_ac] > borrow_limit) {
- ctx->credits_borrowed[borrower_ac][lender_ac]++;
- ctx->FIFO_credit[lender_ac]--;
- rc = lender_ac;
- break;
- }
- }
- return rc;
- }
- /** LIMIT_BORROW specific function */
- static int _dhd_wlfc_return_credit(athost_wl_status_info_t* ctx, int lender_ac, int borrower_ac)
- {
- if ((ctx == NULL) || (lender_ac < 0) || (lender_ac > AC_COUNT) ||
- (borrower_ac < 0) || (borrower_ac > AC_COUNT)) {
- DHD_ERROR(("Error: %s():%d, ctx(%p), lender_ac(%d), borrower_ac(%d)\n",
- __FUNCTION__, __LINE__, ctx, lender_ac, borrower_ac));
- return BCME_BADARG;
- }
- ctx->credits_borrowed[borrower_ac][lender_ac]--;
- ctx->FIFO_credit[lender_ac]++;
- return BCME_OK;
- }
- #endif /* LIMIT_BORROW */
- /**
- * Called on an interface event (WLC_E_IF) indicated by firmware.
- * @param action : eg eWLFC_MAC_ENTRY_ACTION_UPDATE or eWLFC_MAC_ENTRY_ACTION_ADD
- */
- static int
- _dhd_wlfc_interface_entry_update(void* state,
- uint8 action, uint8 ifid, uint8 iftype, uint8* ea)
- {
- athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
- wlfc_mac_descriptor_t* entry;
- if (ifid >= WLFC_MAX_IFNUM)
- return BCME_BADARG;
- entry = &ctx->destination_entries.interfaces[ifid];
- return _dhd_wlfc_mac_entry_update(ctx, entry, action, ifid, iftype, ea,
- _dhd_wlfc_ifpkt_fn, &ifid);
- }
- /**
- * Called eg on receiving a WLC_E_BCMC_CREDIT_SUPPORT event from the dongle (broadcast/multicast
- * specific)
- */
- static int
- _dhd_wlfc_BCMCCredit_support_update(void* state)
- {
- athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
- ctx->bcmc_credit_supported = TRUE;
- return BCME_OK;
- }
- /** Called eg on receiving a WLC_E_FIFO_CREDIT_MAP event from the dongle */
- static int
- _dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits)
- {
- athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
- int i;
- for (i = 0; i <= 4; i++) {
- if (ctx->Init_FIFO_credit[i] != ctx->FIFO_credit[i]) {
- DHD_ERROR(("%s: credit[i] is not returned, (%d %d)\n",
- __FUNCTION__, ctx->Init_FIFO_credit[i], ctx->FIFO_credit[i]));
- }
- }
- /* update the AC FIFO credit map */
- ctx->FIFO_credit[0] += (credits[0] - ctx->Init_FIFO_credit[0]);
- ctx->FIFO_credit[1] += (credits[1] - ctx->Init_FIFO_credit[1]);
- ctx->FIFO_credit[2] += (credits[2] - ctx->Init_FIFO_credit[2]);
- ctx->FIFO_credit[3] += (credits[3] - ctx->Init_FIFO_credit[3]);
- ctx->FIFO_credit[4] += (credits[4] - ctx->Init_FIFO_credit[4]);
- ctx->Init_FIFO_credit[0] = credits[0];
- ctx->Init_FIFO_credit[1] = credits[1];
- ctx->Init_FIFO_credit[2] = credits[2];
- ctx->Init_FIFO_credit[3] = credits[3];
- ctx->Init_FIFO_credit[4] = credits[4];
- /* credit for ATIM FIFO is not used yet. */
- ctx->Init_FIFO_credit[5] = ctx->FIFO_credit[5] = 0;
- return BCME_OK;
- }
- /**
- * Called during committing of a transmit packet from the OS DHD layer to the next layer towards
- * the dongle (eg the DBUS layer). All transmit packets flow via this function to the next layer.
- *
- * @param[in/out] ctx Driver specific flow control administration
- * @param[in] ac Access Category (QoS) of called supplied packet
- * @param[in] commit_info Contains eg the packet to send
- * @param[in] fcommit Function pointer to transmit function of next software layer
- * @param[in] commit_ctx Opaque context used when calling next layer
- */
- static int
- _dhd_wlfc_handle_packet_commit(athost_wl_status_info_t* ctx, int ac,
- dhd_wlfc_commit_info_t *commit_info, f_commitpkt_t fcommit, void* commit_ctx)
- {
- uint32 hslot;
- int rc;
- dhd_pub_t *dhdp = (dhd_pub_t *)(ctx->dhdp);
- /*
- if ac_fifo_credit_spent = 0
- This packet will not count against the FIFO credit.
- To ensure the txstatus corresponding to this packet
- does not provide an implied credit (default behavior)
- mark the packet accordingly.
- if ac_fifo_credit_spent = 1
- This is a normal packet and it counts against the FIFO
- credit count.
- */
- DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), commit_info->ac_fifo_credit_spent);
- rc = _dhd_wlfc_pretx_pktprocess(ctx, commit_info->mac_entry, &commit_info->p,
- commit_info->needs_hdr, &hslot);
- if (rc == BCME_OK) {
- rc = fcommit(commit_ctx, commit_info->p);
- if (rc == BCME_OK) {
- uint8 gen = WL_TXSTATUS_GET_GENERATION(
- DHD_PKTTAG_H2DTAG(PKTTAG(commit_info->p)));
- ctx->stats.pkt2bus++;
- if (commit_info->ac_fifo_credit_spent || (ac == AC_COUNT)) {
- ctx->stats.send_pkts[ac]++;
- WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac);
- }
- if (gen != commit_info->mac_entry->generation) {
- /* will be suppressed back by design */
- if (!commit_info->mac_entry->suppressed) {
- commit_info->mac_entry->suppressed = TRUE;
- }
- commit_info->mac_entry->suppr_transit_count++;
- }
- commit_info->mac_entry->transit_count++;
- commit_info->mac_entry->onbus_pkts_count++;
- } else if (commit_info->needs_hdr) {
- if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
- void *pout = NULL;
- /* pop hanger for delayed packet */
- _dhd_wlfc_hanger_poppkt(ctx->hanger, WL_TXSTATUS_GET_HSLOT(
- DHD_PKTTAG_H2DTAG(PKTTAG(commit_info->p))), &pout, TRUE);
- ASSERT(commit_info->p == pout);
- }
- }
- } else {
- ctx->stats.generic_error++;
- }
- if (rc != BCME_OK) {
- /*
- pretx pkt process or bus commit has failed, rollback.
- - remove wl-header for a delayed packet
- - save wl-header header for suppressed packets
- - reset credit check flag
- */
- _dhd_wlfc_rollback_packet_toq(ctx, commit_info->p, commit_info->pkt_type, hslot);
- DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), 0);
- }
- return rc;
- } /* _dhd_wlfc_handle_packet_commit */
- /** Returns remote MAC descriptor for caller supplied MAC address */
- static uint8
- _dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t *dhdp, uint8 *ea)
- {
- wlfc_mac_descriptor_t* table =
- ((athost_wl_status_info_t*)dhdp->wlfc_state)->destination_entries.nodes;
- uint8 table_index;
- if (ea != NULL) {
- for (table_index = 0; table_index < WLFC_MAC_DESC_TABLE_SIZE; table_index++) {
- if ((memcmp(ea, &table[table_index].ea[0], ETHER_ADDR_LEN) == 0) &&
- table[table_index].occupied)
- return table_index;
- }
- }
- return WLFC_MAC_DESC_ID_INVALID;
- }
- /**
- * Called when the host receives a WLFC_CTL_TYPE_TXSTATUS event from the dongle, indicating the
- * status of a frame that the dongle attempted to transmit over the wireless medium.
- */
- static int
- dhd_wlfc_suppressed_acked_update(dhd_pub_t *dhd, uint16 hslot, uint8 prec, uint8 hcnt)
- {
- athost_wl_status_info_t* ctx;
- wlfc_mac_descriptor_t* entry = NULL;
- struct pktq *pq;
- struct pktq_prec *q;
- void *p, *b;
- if (!dhd) {
- DHD_ERROR(("%s: dhd(%p)\n", __FUNCTION__, dhd));
- return BCME_BADARG;
- }
- ctx = (athost_wl_status_info_t*)dhd->wlfc_state;
- if (!ctx) {
- DHD_ERROR(("%s: ctx(%p)\n", __FUNCTION__, ctx));
- return BCME_ERROR;
- }
- ASSERT(hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM + 1));
- if (hslot < WLFC_MAC_DESC_TABLE_SIZE)
- entry = &ctx->destination_entries.nodes[hslot];
- else if (hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM))
- entry = &ctx->destination_entries.interfaces[hslot - WLFC_MAC_DESC_TABLE_SIZE];
- else
- entry = &ctx->destination_entries.other;
- pq = &entry->psq;
- ASSERT(((prec << 1) + 1) < pq->num_prec);
- q = &pq->q[((prec << 1) + 1)];
- b = NULL;
- p = q->head;
- while (p && (hcnt != WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p))))) {
- b = p;
- p = PKTLINK(p);
- }
- if (p == NULL) {
- /* none is matched */
- if (b) {
- DHD_ERROR(("%s: can't find matching seq(%d)\n", __FUNCTION__, hcnt));
- } else {
- DHD_ERROR(("%s: queue is empty\n", __FUNCTION__));
- }
- return BCME_ERROR;
- }
- if (!b) {
- /* head packet is matched */
- if ((q->head = PKTLINK(p)) == NULL) {
- q->tail = NULL;
- }
- } else {
- /* middle packet is matched */
- PKTSETLINK(b, PKTLINK(p));
- if (PKTLINK(p) == NULL) {
- q->tail = b;
- }
- }
- q->n_pkts--;
- pq->n_pkts_tot--;
- #ifdef WL_TXQ_STALL
- q->dequeue_count++;
- #endif // endif
- ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec]--;
- ctx->pkt_cnt_per_ac[prec]--;
- PKTSETLINK(p, NULL);
- if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
- _dhd_wlfc_enque_afq(ctx, p);
- } else {
- _dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot);
- }
- entry->transit_count++;
- return BCME_OK;
- }
- static int
- _dhd_wlfc_compressed_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info, uint8 len, void** p_mac)
- {
- uint8 status_flag_ori, status_flag;
- uint32 status;
- int ret = BCME_OK;
- int remove_from_hanger_ori, remove_from_hanger = 1;
- void* pktbuf = NULL;
- uint8 fifo_id = 0, gen = 0, count = 0, hcnt;
- uint16 hslot;
- wlfc_mac_descriptor_t* entry = NULL;
- athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
- uint16 seq = 0, seq_fromfw = 0, seq_num = 0;
- memcpy(&status, pkt_info, sizeof(uint32));
- status = ltoh32(status);
- status_flag = WL_TXSTATUS_GET_FLAGS(status);
- hcnt = WL_TXSTATUS_GET_FREERUNCTR(status);
- hslot = WL_TXSTATUS_GET_HSLOT(status);
- fifo_id = WL_TXSTATUS_GET_FIFO(status);
- gen = WL_TXSTATUS_GET_GENERATION(status);
- if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
- memcpy(&seq, pkt_info + WLFC_CTL_VALUE_LEN_TXSTATUS, WLFC_CTL_VALUE_LEN_SEQ);
- seq = ltoh16(seq);
- seq_fromfw = GET_WL_HAS_ASSIGNED_SEQ(seq);
- seq_num = WL_SEQ_GET_NUM(seq);
- }
- wlfc->stats.txstatus_in += len;
- if (status_flag == WLFC_CTL_PKTFLAG_DISCARD) {
- wlfc->stats.pkt_freed += len;
- } else if (status_flag == WLFC_CTL_PKTFLAG_DISCARD_NOACK) {
- wlfc->stats.pkt_freed += len;
- } else if (status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) {
- wlfc->stats.d11_suppress += len;
- remove_from_hanger = 0;
- } else if (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS) {
- wlfc->stats.wl_suppress += len;
- remove_from_hanger = 0;
- } else if (status_flag == WLFC_CTL_PKTFLAG_TOSSED_BYWLC) {
- wlfc->stats.wlc_tossed_pkts += len;
- } else if (status_flag == WLFC_CTL_PKTFLAG_SUPPRESS_ACKED) {
- wlfc->stats.pkt_freed += len;
- } else if (status_flag == WLFC_CTL_PKTFLAG_EXPIRED) {
- wlfc->stats.pkt_exptime += len;
- } else if (status_flag == WLFC_CTL_PKTFLAG_DROPPED) {
- wlfc->stats.pkt_dropped += len;
- }
- if (dhd->proptxstatus_txstatus_ignore) {
- if (!remove_from_hanger) {
- DHD_ERROR(("suppress txstatus: %d\n", status_flag));
- }
- return BCME_OK;
- }
- status_flag_ori = status_flag;
- remove_from_hanger_ori = remove_from_hanger;
- while (count < len) {
- if (status_flag == WLFC_CTL_PKTFLAG_SUPPRESS_ACKED) {
- dhd_wlfc_suppressed_acked_update(dhd, hslot, fifo_id, hcnt);
- }
- if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
- ret = _dhd_wlfc_deque_afq(wlfc, hslot, hcnt, fifo_id, &pktbuf);
- } else {
- status_flag = status_flag_ori;
- remove_from_hanger = remove_from_hanger_ori;
- ret = _dhd_wlfc_hanger_poppkt(wlfc->hanger, hslot, &pktbuf, FALSE);
- if (!pktbuf) {
- _dhd_wlfc_hanger_free_pkt(wlfc, hslot,
- WLFC_HANGER_PKT_STATE_TXSTATUS, -1);
- goto cont;
- } else {
- wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
- if (h->items[hslot].state == WLFC_HANGER_ITEM_STATE_FLUSHED) {
- status_flag = WLFC_CTL_PKTFLAG_DISCARD;
- remove_from_hanger = 1;
- }
- }
- }
- if ((ret != BCME_OK) || !pktbuf) {
- goto cont;
- }
- bcm_pkt_validate_chk(pktbuf);
- /* set fifo_id to correct value because not all FW does that */
- fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
- entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
- if (!remove_from_hanger) {
- /* this packet was suppressed */
- if (!entry->suppressed || (entry->generation != gen)) {
- if (!entry->suppressed) {
- entry->suppr_transit_count = entry->transit_count;
- if (p_mac) {
- *p_mac = entry;
- }
- } else {
- DHD_ERROR(("gen(%d), entry->generation(%d)\n",
- gen, entry->generation));
- }
- entry->suppressed = TRUE;
- }
- entry->generation = gen;
- }
- #ifdef PROP_TXSTATUS_DEBUG
- if (!WLFC_GET_AFQ(dhd->wlfc_mode))
- {
- uint32 new_t = OSL_SYSUPTIME();
- uint32 old_t;
- uint32 delta;
- old_t = ((wlfc_hanger_t*)(wlfc->hanger))->items[hslot].push_time;
- wlfc->stats.latency_sample_count++;
- if (new_t > old_t)
- delta = new_t - old_t;
- else
- delta = 0xffffffff + new_t - old_t;
- wlfc->stats.total_status_latency += delta;
- wlfc->stats.latency_most_recent = delta;
- wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta;
- if (wlfc->stats.idx_delta == sizeof(wlfc->stats.deltas)/sizeof(uint32))
- wlfc->stats.idx_delta = 0;
- }
- #endif /* PROP_TXSTATUS_DEBUG */
- /* pick up the implicit credit from this packet */
- if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) {
- _dhd_wlfc_return_implied_credit(wlfc, pktbuf);
- } else {
- /*
- if this packet did not count against FIFO credit, it must have
- taken a requested_credit from the destination entry (for pspoll etc.)
- */
- if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf))) {
- entry->requested_credit++;
- #if defined(DHD_WLFC_THREAD)
- _dhd_wlfc_thread_wakeup(dhd);
- #endif /* DHD_WLFC_THREAD */
- }
- #ifdef PROP_TXSTATUS_DEBUG
- entry->dstncredit_acks++;
- #endif // endif
- }
- if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) ||
- (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) {
- /* save generation bit inside packet */
- WL_TXSTATUS_SET_GENERATION(DHD_PKTTAG_H2DTAG(PKTTAG(pktbuf)), gen);
- if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
- WL_SEQ_SET_REUSE(DHD_PKTTAG_H2DSEQ(PKTTAG(pktbuf)), seq_fromfw);
- WL_SEQ_SET_NUM(DHD_PKTTAG_H2DSEQ(PKTTAG(pktbuf)), seq_num);
- }
- ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf);
- if (ret != BCME_OK) {
- /* delay q is full, drop this packet */
- DHD_WLFC_QMON_COMPLETE(entry);
- _dhd_wlfc_prec_drop(dhd, (fifo_id << 1) + 1, pktbuf, FALSE);
- } else {
- if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
- /* Mark suppressed to avoid a double free
- during wlfc cleanup
- */
- _dhd_wlfc_hanger_mark_suppressed(wlfc->hanger, hslot, gen);
- }
- }
- } else {
- DHD_WLFC_QMON_COMPLETE(entry);
- if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
- _dhd_wlfc_hanger_free_pkt(wlfc, hslot,
- WLFC_HANGER_PKT_STATE_TXSTATUS, TRUE);
- } else {
- dhd_txcomplete(dhd, pktbuf, TRUE);
- wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pktbuf))]
- [DHD_PKTTAG_FIFO(PKTTAG(pktbuf))]--;
- wlfc->stats.pktout++;
- /* free the packet */
- PKTFREE(wlfc->osh, pktbuf, TRUE);
- }
- }
- /* pkt back from firmware side */
- if (entry->transit_count)
- entry->transit_count--;
- if (entry->suppr_transit_count) {
- entry->suppr_transit_count--;
- if (entry->suppressed &&
- (!entry->onbus_pkts_count) &&
- (!entry->suppr_transit_count))
- entry->suppressed = FALSE;
- }
- cont:
- hcnt = (hcnt + 1) & WL_TXSTATUS_FREERUNCTR_MASK;
- if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
- hslot = (hslot + 1) & WL_TXSTATUS_HSLOT_MASK;
- }
- if (WLFC_GET_REUSESEQ(dhd->wlfc_mode) && seq_fromfw) {
- seq_num = (seq_num + 1) & WL_SEQ_NUM_MASK;
- }
- count++;
- }
- return BCME_OK;
- } /* _dhd_wlfc_compressed_txstatus_update */
- /**
- * Called when eg host receives a 'WLFC_CTL_TYPE_FIFO_CREDITBACK' event from the dongle.
- * @param[in] credits caller supplied credit that will be added to the host credit.
- */
- static int
- _dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8* credits)
- {
- int i;
- athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
- for (i = 0; i < WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK; i++) {
- #ifdef PROP_TXSTATUS_DEBUG
- wlfc->stats.fifo_credits_back[i] += credits[i];
- #endif // endif
- /* update FIFO credits */
- if (dhd->proptxstatus_mode == WLFC_FCMODE_EXPLICIT_CREDIT)
- {
- int lender; /* Note that borrower is i */
- /* Return credits to highest priority lender first */
- for (lender = AC_COUNT; (lender >= 0) && (credits[i] > 0); lender--) {
- if (wlfc->credits_borrowed[i][lender] > 0) {
- if (credits[i] >= wlfc->credits_borrowed[i][lender]) {
- credits[i] -=
- (uint8)wlfc->credits_borrowed[i][lender];
- wlfc->FIFO_credit[lender] +=
- wlfc->credits_borrowed[i][lender];
- wlfc->credits_borrowed[i][lender] = 0;
- } else {
- wlfc->credits_borrowed[i][lender] -= credits[i];
- wlfc->FIFO_credit[lender] += credits[i];
- credits[i] = 0;
- }
- }
- }
- /* If we have more credits left over, these must belong to the AC */
- if (credits[i] > 0) {
- wlfc->FIFO_credit[i] += credits[i];
- }
- if (wlfc->FIFO_credit[i] > wlfc->Init_FIFO_credit[i]) {
- wlfc->FIFO_credit[i] = wlfc->Init_FIFO_credit[i];
- }
- }
- }
- #if defined(DHD_WLFC_THREAD)
- _dhd_wlfc_thread_wakeup(dhd);
- #endif /* defined(DHD_WLFC_THREAD) */
- return BCME_OK;
- } /* _dhd_wlfc_fifocreditback_indicate */
- /** !BCMDBUS specific function */
- static void
- _dhd_wlfc_suppress_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
- {
- athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
- wlfc_mac_descriptor_t* entry;
- int prec;
- void *pkt = NULL, *head = NULL, *tail = NULL;
- struct pktq *txq = (struct pktq *)dhd_bus_txq(dhd->bus);
- uint8 results[WLFC_CTL_VALUE_LEN_TXSTATUS+WLFC_CTL_VALUE_LEN_SEQ];
- uint8 credits[WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK] = {0};
- uint32 htod = 0;
- uint16 htodseq = 0;
- bool bCreditUpdate = FALSE;
- dhd_os_sdlock_txq(dhd);
- for (prec = 0; prec < txq->num_prec; prec++) {
- while ((pkt = _dhd_wlfc_pktq_pdeq_with_fn(txq, prec, fn, arg))) {
- if (!head) {
- head = pkt;
- }
- if (tail) {
- PKTSETLINK(tail, pkt);
- }
- tail = pkt;
- }
- }
- dhd_os_sdunlock_txq(dhd);
- while ((pkt = head)) {
- head = PKTLINK(pkt);
- PKTSETLINK(pkt, NULL);
- entry = _dhd_wlfc_find_table_entry(wlfc, pkt);
- if (!entry) {
- PKTFREE(dhd->osh, pkt, TRUE);
- continue;
- }
- if (entry->onbus_pkts_count > 0) {
- entry->onbus_pkts_count--;
- }
- if (entry->suppressed &&
- (!entry->onbus_pkts_count) &&
- (!entry->suppr_transit_count)) {
- entry->suppressed = FALSE;
- }
- /* fake a suppression txstatus */
- htod = DHD_PKTTAG_H2DTAG(PKTTAG(pkt));
- WL_TXSTATUS_SET_FLAGS(htod, WLFC_CTL_PKTFLAG_WLSUPPRESS);
- WL_TXSTATUS_SET_GENERATION(htod, entry->generation);
- htod = htol32(htod);
- memcpy(results, &htod, WLFC_CTL_VALUE_LEN_TXSTATUS);
- if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
- htodseq = DHD_PKTTAG_H2DSEQ(PKTTAG(pkt));
- if (IS_WL_TO_REUSE_SEQ(htodseq)) {
- SET_WL_HAS_ASSIGNED_SEQ(htodseq);
- RESET_WL_TO_REUSE_SEQ(htodseq);
- }
- htodseq = htol16(htodseq);
- memcpy(results + WLFC_CTL_VALUE_LEN_TXSTATUS, &htodseq,
- WLFC_CTL_VALUE_LEN_SEQ);
- }
- if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
- _dhd_wlfc_enque_afq(wlfc, pkt);
- }
- _dhd_wlfc_compressed_txstatus_update(dhd, results, 1, NULL);
- /* fake a fifo credit back */
- if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pkt))) {
- credits[DHD_PKTTAG_FIFO(PKTTAG(pkt))]++;
- bCreditUpdate = TRUE;
- }
- }
- if (bCreditUpdate) {
- _dhd_wlfc_fifocreditback_indicate(dhd, credits);
- }
- } /* _dhd_wlfc_suppress_txq */
- static int
- _dhd_wlfc_dbg_senum_check(dhd_pub_t *dhd, uint8 *value)
- {
- uint32 timestamp;
- (void)dhd;
- bcopy(&value[2], ×tamp, sizeof(uint32));
- timestamp = ltoh32(timestamp);
- DHD_INFO(("RXPKT: SEQ: %d, timestamp %d\n", value[1], timestamp));
- return BCME_OK;
- }
- static int
- _dhd_wlfc_rssi_indicate(dhd_pub_t *dhd, uint8* rssi)
- {
- (void)dhd;
- (void)rssi;
- return BCME_OK;
- }
- static void
- _dhd_wlfc_add_requested_entry(athost_wl_status_info_t* wlfc, wlfc_mac_descriptor_t* entry)
- {
- int i;
- if (!wlfc || !entry) {
- return;
- }
- for (i = 0; i < wlfc->requested_entry_count; i++) {
- if (entry == wlfc->requested_entry[i]) {
- break;
- }
- }
- if (i == wlfc->requested_entry_count) {
- /* no match entry found */
- ASSERT(wlfc->requested_entry_count <= (WLFC_MAC_DESC_TABLE_SIZE-1));
- wlfc->requested_entry[wlfc->requested_entry_count++] = entry;
- }
- }
- /** called on eg receiving 'mac open' event from the dongle. */
- static void
- _dhd_wlfc_remove_requested_entry(athost_wl_status_info_t* wlfc, wlfc_mac_descriptor_t* entry)
- {
- int i;
- if (!wlfc || !entry) {
- return;
- }
- for (i = 0; i < wlfc->requested_entry_count; i++) {
- if (entry == wlfc->requested_entry[i]) {
- break;
- }
- }
- if (i < wlfc->requested_entry_count) {
- /* found */
- ASSERT(wlfc->requested_entry_count > 0);
- wlfc->requested_entry_count--;
- if (i != wlfc->requested_entry_count) {
- wlfc->requested_entry[i] =
- wlfc->requested_entry[wlfc->requested_entry_count];
- }
- wlfc->requested_entry[wlfc->requested_entry_count] = NULL;
- }
- }
- /** called on eg receiving a WLFC_CTL_TYPE_MACDESC_ADD TLV from the dongle */
- static int
- _dhd_wlfc_mac_table_update(dhd_pub_t *dhd, uint8* value, uint8 type)
- {
- int rc;
- athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
- wlfc_mac_descriptor_t* table;
- uint8 existing_index;
- uint8 table_index;
- uint8 ifid;
- uint8* ea;
- WLFC_DBGMESG(("%s(), mac ["MACDBG"],%s,idx:%d,id:0x%02x\n",
- __FUNCTION__, MAC2STRDBG(&value[2]),
- ((type == WLFC_CTL_TYPE_MACDESC_ADD) ? "ADD":"DEL"),
- WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]), value[0]));
- table = wlfc->destination_entries.nodes;
- table_index = WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]);
- ifid = value[1];
- ea = &value[2];
- _dhd_wlfc_remove_requested_entry(wlfc, &table[table_index]);
- if (type == WLFC_CTL_TYPE_MACDESC_ADD) {
- existing_index = _dhd_wlfc_find_mac_desc_id_from_mac(dhd, &value[2]);
- if ((existing_index != WLFC_MAC_DESC_ID_INVALID) &&
- (existing_index != table_index) && table[existing_index].occupied) {
- /*
- there is an existing different entry, free the old one
- and move it to new index if necessary.
- */
- rc = _dhd_wlfc_mac_entry_update(wlfc, &table[existing_index],
- eWLFC_MAC_ENTRY_ACTION_DEL, table[existing_index].interface_id,
- table[existing_index].iftype, NULL, _dhd_wlfc_entrypkt_fn,
- &table[existing_index]);
- }
- if (!table[table_index].occupied) {
- /* this new MAC entry does not exist, create one */
- table[table_index].mac_handle = value[0];
- rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
- eWLFC_MAC_ENTRY_ACTION_ADD, ifid,
- wlfc->destination_entries.interfaces[ifid].iftype,
- ea, NULL, NULL);
- } else {
- /* the space should have been empty, but it's not */
- wlfc->stats.mac_update_failed++;
- }
- }
- if (type == WLFC_CTL_TYPE_MACDESC_DEL) {
- if (table[table_index].occupied) {
- rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
- eWLFC_MAC_ENTRY_ACTION_DEL, ifid,
- wlfc->destination_entries.interfaces[ifid].iftype,
- ea, _dhd_wlfc_entrypkt_fn, &table[table_index]);
- } else {
- /* the space should have been occupied, but it's not */
- wlfc->stats.mac_update_failed++;
- }
- }
- BCM_REFERENCE(rc);
- return BCME_OK;
- } /* _dhd_wlfc_mac_table_update */
- /** Called on a 'mac open' or 'mac close' event indicated by the dongle */
- static int
- _dhd_wlfc_psmode_update(dhd_pub_t *dhd, uint8* value, uint8 type)
- {
- /* Handle PS on/off indication */
- athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
- wlfc_mac_descriptor_t* table;
- wlfc_mac_descriptor_t* desc; /* a table maps from mac handle to mac descriptor */
- uint8 mac_handle = value[0];
- int i;
- table = wlfc->destination_entries.nodes;
- desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
- if (desc->occupied) {
- if (type == WLFC_CTL_TYPE_MAC_OPEN) {
- desc->state = WLFC_STATE_OPEN;
- desc->ac_bitmap = 0xff;
- DHD_WLFC_CTRINC_MAC_OPEN(desc);
- desc->requested_credit = 0;
- desc->requested_packet = 0;
- _dhd_wlfc_remove_requested_entry(wlfc, desc);
- } else {
- desc->state = WLFC_STATE_CLOSE;
- DHD_WLFC_CTRINC_MAC_CLOSE(desc);
- /* Indicate to firmware if there is any traffic pending. */
- for (i = 0; i < AC_COUNT; i++) {
- _dhd_wlfc_traffic_pending_check(wlfc, desc, i);
- }
- }
- } else {
- wlfc->stats.psmode_update_failed++;
- }
- return BCME_OK;
- } /* _dhd_wlfc_psmode_update */
- /** called upon receiving 'interface open' or 'interface close' event from the dongle */
- static int
- _dhd_wlfc_interface_update(dhd_pub_t *dhd, uint8* value, uint8 type)
- {
- /* Handle PS on/off indication */
- athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
- wlfc_mac_descriptor_t* table;
- uint8 if_id = value[0];
- if (if_id < WLFC_MAX_IFNUM) {
- table = wlfc->destination_entries.interfaces;
- if (table[if_id].occupied) {
- if (type == WLFC_CTL_TYPE_INTERFACE_OPEN) {
- table[if_id].state = WLFC_STATE_OPEN;
- /* WLFC_DBGMESG(("INTERFACE[%d] OPEN\n", if_id)); */
- } else {
- table[if_id].state = WLFC_STATE_CLOSE;
- /* WLFC_DBGMESG(("INTERFACE[%d] CLOSE\n", if_id)); */
- }
- return BCME_OK;
- }
- }
- wlfc->stats.interface_update_failed++;
- return BCME_OK;
- }
- /** Called on receiving a WLFC_CTL_TYPE_MAC_REQUEST_CREDIT TLV from the dongle */
- static int
- _dhd_wlfc_credit_request(dhd_pub_t *dhd, uint8* value)
- {
- athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
- wlfc_mac_descriptor_t* table;
- wlfc_mac_descriptor_t* desc;
- uint8 mac_handle;
- uint8 credit;
- table = wlfc->destination_entries.nodes;
- mac_handle = value[1];
- credit = value[0];
- desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
- if (desc->occupied) {
- desc->requested_credit = credit;
- desc->ac_bitmap = value[2] & (~(1<<AC_COUNT));
- _dhd_wlfc_add_requested_entry(wlfc, desc);
- #if defined(DHD_WLFC_THREAD)
- if (credit) {
- _dhd_wlfc_thread_wakeup(dhd);
- }
- #endif /* DHD_WLFC_THREAD */
- } else {
- wlfc->stats.credit_request_failed++;
- }
- return BCME_OK;
- }
- /** Called on receiving a WLFC_CTL_TYPE_MAC_REQUEST_PACKET TLV from the dongle */
- static int
- _dhd_wlfc_packet_request(dhd_pub_t *dhd, uint8* value)
- {
- athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
- wlfc_mac_descriptor_t* table;
- wlfc_mac_descriptor_t* desc;
- uint8 mac_handle;
- uint8 packet_count;
- table = wlfc->destination_entries.nodes;
- mac_handle = value[1];
- packet_count = value[0];
- desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
- if (desc->occupied) {
- desc->requested_packet = packet_count;
- desc->ac_bitmap = value[2] & (~(1<<AC_COUNT));
- _dhd_wlfc_add_requested_entry(wlfc, desc);
- #if defined(DHD_WLFC_THREAD)
- if (packet_count) {
- _dhd_wlfc_thread_wakeup(dhd);
- }
- #endif /* DHD_WLFC_THREAD */
- } else {
- wlfc->stats.packet_request_failed++;
- }
- return BCME_OK;
- }
- /** Called when host receives a WLFC_CTL_TYPE_HOST_REORDER_RXPKTS TLV from the dongle */
- static void
- _dhd_wlfc_reorderinfo_indicate(uint8 *val, uint8 len, uchar *info_buf, uint *info_len)
- {
- if (info_len) {
- /* Check copy length to avoid buffer overrun. In case of length exceeding
- * WLHOST_REORDERDATA_TOTLEN, return failure instead sending incomplete result
- * of length WLHOST_REORDERDATA_TOTLEN
- */
- if ((info_buf) && (len <= WLHOST_REORDERDATA_TOTLEN)) {
- bcopy(val, info_buf, len);
- *info_len = len;
- } else {
- *info_len = 0;
- }
- }
- }
- /*
- * public functions
- */
- bool dhd_wlfc_is_supported(dhd_pub_t *dhd)
- {
- bool rc = TRUE;
- if (dhd == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return FALSE;
- }
- dhd_os_wlfc_block(dhd);
- if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
- rc = FALSE;
- }
- dhd_os_wlfc_unblock(dhd);
- return rc;
- }
- int dhd_wlfc_enable(dhd_pub_t *dhd)
- {
- int i, rc = BCME_OK;
- athost_wl_status_info_t* wlfc;
- if (dhd == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- if (!dhd->wlfc_enabled || dhd->wlfc_state) {
- rc = BCME_OK;
- goto exit;
- }
- /* allocate space to track txstatus propagated from firmware */
- dhd->wlfc_state = DHD_OS_PREALLOC(dhd, DHD_PREALLOC_DHD_WLFC_INFO,
- sizeof(athost_wl_status_info_t));
- if (dhd->wlfc_state == NULL) {
- rc = BCME_NOMEM;
- goto exit;
- }
- /* initialize state space */
- wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
- memset(wlfc, 0, sizeof(athost_wl_status_info_t));
- /* remember osh & dhdp */
- wlfc->osh = dhd->osh;
- wlfc->dhdp = dhd;
- if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
- wlfc->hanger = _dhd_wlfc_hanger_create(dhd, WLFC_HANGER_MAXITEMS);
- if (wlfc->hanger == NULL) {
- DHD_OS_PREFREE(dhd, dhd->wlfc_state,
- sizeof(athost_wl_status_info_t));
- dhd->wlfc_state = NULL;
- rc = BCME_NOMEM;
- goto exit;
- }
- }
- dhd->proptxstatus_mode = WLFC_FCMODE_EXPLICIT_CREDIT;
- /* default to check rx pkt */
- dhd->wlfc_rxpkt_chk = TRUE;
- if (dhd->op_mode & DHD_FLAG_IBSS_MODE) {
- dhd->wlfc_rxpkt_chk = FALSE;
- }
- /* initialize all interfaces to accept traffic */
- for (i = 0; i < WLFC_MAX_IFNUM; i++) {
- wlfc->hostif_flow_state[i] = OFF;
- }
- _dhd_wlfc_mac_entry_update(wlfc, &wlfc->destination_entries.other,
- eWLFC_MAC_ENTRY_ACTION_ADD, 0xff, 0, NULL, NULL, NULL);
- wlfc->allow_credit_borrow = 0;
- wlfc->single_ac = 0;
- wlfc->single_ac_timestamp = 0;
- exit:
- dhd_os_wlfc_unblock(dhd);
- return rc;
- } /* dhd_wlfc_enable */
- #ifdef SUPPORT_P2P_GO_PS
- /**
- * Called when the host platform enters a lower power mode, eg right before a system hibernate.
- * SUPPORT_P2P_GO_PS specific function.
- */
- int
- dhd_wlfc_suspend(dhd_pub_t *dhd)
- {
- uint32 tlv = 0;
- DHD_TRACE(("%s: masking wlfc events\n", __FUNCTION__));
- if (!dhd->wlfc_enabled)
- return -1;
- if (!dhd_wl_ioctl_get_intiovar(dhd, "tlv", &tlv, WLC_GET_VAR, FALSE, 0))
- return -1;
- if ((tlv & (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS)) == 0)
- return 0;
- tlv &= ~(WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS);
- if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0))
- return -1;
- return 0;
- }
- /**
- * Called when the host platform resumes from a power management operation, eg resume after a
- * system hibernate. SUPPORT_P2P_GO_PS specific function.
- */
- int
- dhd_wlfc_resume(dhd_pub_t *dhd)
- {
- uint32 tlv = 0;
- DHD_TRACE(("%s: unmasking wlfc events\n", __FUNCTION__));
- if (!dhd->wlfc_enabled)
- return -1;
- if (!dhd_wl_ioctl_get_intiovar(dhd, "tlv", &tlv, WLC_GET_VAR, FALSE, 0))
- return -1;
- if ((tlv & (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS)) ==
- (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS))
- return 0;
- tlv |= (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS);
- if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0))
- return -1;
- return 0;
- }
- #endif /* SUPPORT_P2P_GO_PS */
- /** A flow control header was received from firmware, containing one or more TLVs */
- int
- dhd_wlfc_parse_header_info(dhd_pub_t *dhd, void* pktbuf, int tlv_hdr_len, uchar *reorder_info_buf,
- uint *reorder_info_len)
- {
- uint8 type, len;
- uint8* value;
- uint8* tmpbuf;
- uint16 remainder = (uint16)tlv_hdr_len;
- uint16 processed = 0;
- athost_wl_status_info_t* wlfc = NULL;
- void* entry;
- if ((dhd == NULL) || (pktbuf == NULL)) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- if (dhd->proptxstatus_mode != WLFC_ONLY_AMPDU_HOSTREORDER) {
- if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
- dhd_os_wlfc_unblock(dhd);
- return WLFC_UNSUPPORTED;
- }
- wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
- }
- tmpbuf = (uint8*)PKTDATA(dhd->osh, pktbuf);
- if (remainder) {
- while ((processed < (WLFC_MAX_PENDING_DATALEN * 2)) && (remainder > 0)) {
- type = tmpbuf[processed];
- if (type == WLFC_CTL_TYPE_FILLER) {
- remainder -= 1;
- processed += 1;
- continue;
- }
- len = tmpbuf[processed + 1];
- value = &tmpbuf[processed + 2];
- if (remainder < (2 + len))
- break;
- remainder -= 2 + len;
- processed += 2 + len;
- entry = NULL;
- DHD_INFO(("%s():%d type %d remainder %d processed %d\n",
- __FUNCTION__, __LINE__, type, remainder, processed));
- if (type == WLFC_CTL_TYPE_HOST_REORDER_RXPKTS)
- _dhd_wlfc_reorderinfo_indicate(value, len, reorder_info_buf,
- reorder_info_len);
- if (wlfc == NULL) {
- ASSERT(dhd->proptxstatus_mode == WLFC_ONLY_AMPDU_HOSTREORDER);
- if (type != WLFC_CTL_TYPE_HOST_REORDER_RXPKTS &&
- type != WLFC_CTL_TYPE_TRANS_ID)
- DHD_INFO(("%s():%d dhd->wlfc_state is NULL yet!"
- " type %d remainder %d processed %d\n",
- __FUNCTION__, __LINE__, type, remainder, processed));
- continue;
- }
- if (type == WLFC_CTL_TYPE_TXSTATUS) {
- _dhd_wlfc_compressed_txstatus_update(dhd, value, 1, &entry);
- } else if (type == WLFC_CTL_TYPE_COMP_TXSTATUS) {
- uint8 compcnt_offset = WLFC_CTL_VALUE_LEN_TXSTATUS;
- if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
- compcnt_offset += WLFC_CTL_VALUE_LEN_SEQ;
- }
- _dhd_wlfc_compressed_txstatus_update(dhd, value,
- value[compcnt_offset], &entry);
- } else if (type == WLFC_CTL_TYPE_FIFO_CREDITBACK) {
- _dhd_wlfc_fifocreditback_indicate(dhd, value);
- } else if (type == WLFC_CTL_TYPE_RSSI) {
- _dhd_wlfc_rssi_indicate(dhd, value);
- } else if (type == WLFC_CTL_TYPE_MAC_REQUEST_CREDIT) {
- _dhd_wlfc_credit_request(dhd, value);
- } else if (type == WLFC_CTL_TYPE_MAC_REQUEST_PACKET) {
- _dhd_wlfc_packet_request(dhd, value);
- } else if ((type == WLFC_CTL_TYPE_MAC_OPEN) ||
- (type == WLFC_CTL_TYPE_MAC_CLOSE)) {
- _dhd_wlfc_psmode_update(dhd, value, type);
- } else if ((type == WLFC_CTL_TYPE_MACDESC_ADD) ||
- (type == WLFC_CTL_TYPE_MACDESC_DEL)) {
- _dhd_wlfc_mac_table_update(dhd, value, type);
- } else if (type == WLFC_CTL_TYPE_TRANS_ID) {
- _dhd_wlfc_dbg_senum_check(dhd, value);
- } else if ((type == WLFC_CTL_TYPE_INTERFACE_OPEN) ||
- (type == WLFC_CTL_TYPE_INTERFACE_CLOSE)) {
- _dhd_wlfc_interface_update(dhd, value, type);
- }
- if (entry && WLFC_GET_REORDERSUPP(dhd->wlfc_mode)) {
- /* suppress all packets for this mac entry from bus->txq */
- _dhd_wlfc_suppress_txq(dhd, _dhd_wlfc_entrypkt_fn, entry);
- }
- } /* while */
- if (remainder != 0 && wlfc) {
- /* trouble..., something is not right */
- wlfc->stats.tlv_parse_failed++;
- }
- } /* if */
- if (wlfc)
- wlfc->stats.dhd_hdrpulls++;
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- KERNEL_THREAD_RETURN_TYPE
- dhd_wlfc_transfer_packets(void *data)
- {
- dhd_pub_t *dhdp = (dhd_pub_t *)data;
- int ac, single_ac = 0, rc = BCME_OK;
- dhd_wlfc_commit_info_t commit_info;
- athost_wl_status_info_t* ctx;
- int bus_retry_count = 0;
- int pkt_send = 0;
- int pkt_send_per_ac = 0;
- uint8 tx_map = 0; /* packets (send + in queue), Bitmask for 4 ACs + BC/MC */
- uint8 rx_map = 0; /* received packets, Bitmask for 4 ACs + BC/MC */
- uint8 packets_map = 0; /* packets in queue, Bitmask for 4 ACs + BC/MC */
- bool no_credit = FALSE;
- int lender;
- #if defined(DHD_WLFC_THREAD)
- /* wait till someone wakeup me up, will change it at running time */
- int wait_msec = msecs_to_jiffies(0xFFFFFFFF);
- #endif /* defined(DHD_WLFC_THREAD) */
- #if defined(DHD_WLFC_THREAD)
- while (1) {
- bus_retry_count = 0;
- pkt_send = 0;
- tx_map = 0;
- rx_map = 0;
- packets_map = 0;
- wait_msec = wait_event_interruptible_timeout(dhdp->wlfc_wqhead,
- dhdp->wlfc_thread_go, wait_msec);
- if (kthread_should_stop()) {
- break;
- }
- dhdp->wlfc_thread_go = FALSE;
- dhd_os_wlfc_block(dhdp);
- #endif /* defined(DHD_WLFC_THREAD) */
- ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
- #if defined(DHD_WLFC_THREAD)
- if (!ctx)
- goto exit;
- #endif /* defined(DHD_WLFC_THREAD) */
- memset(&commit_info, 0, sizeof(commit_info));
- /*
- Commit packets for regular AC traffic. Higher priority first.
- First, use up FIFO credits available to each AC. Based on distribution
- and credits left, borrow from other ACs as applicable
- -NOTE:
- If the bus between the host and firmware is overwhelmed by the
- traffic from host, it is possible that higher priority traffic
- starves the lower priority queue. If that occurs often, we may
- have to employ weighted round-robin or ucode scheme to avoid
- low priority packet starvation.
- */
- for (ac = AC_COUNT; ac >= 0; ac--) {
- if (dhdp->wlfc_rxpkt_chk) {
- /* check rx packet */
- uint32 curr_t = OSL_SYSUPTIME(), delta;
- delta = curr_t - ctx->rx_timestamp[ac];
- if (delta < WLFC_RX_DETECTION_THRESHOLD_MS) {
- rx_map |= (1 << ac);
- }
- }
- if (ctx->pkt_cnt_per_ac[ac] == 0) {
- continue;
- }
- tx_map |= (1 << ac);
- single_ac = ac + 1;
- pkt_send_per_ac = 0;
- while ((FALSE == dhdp->proptxstatus_txoff) &&
- (pkt_send_per_ac < WLFC_PACKET_BOUND)) {
- /* packets from delayQ with less priority are fresh and
- * they'd need header and have no MAC entry
- */
- no_credit = (ctx->FIFO_credit[ac] < 1);
- if (dhdp->proptxstatus_credit_ignore ||
- ((ac == AC_COUNT) && !ctx->bcmc_credit_supported)) {
- no_credit = FALSE;
- }
- lender = -1;
- #ifdef LIMIT_BORROW
- if (no_credit && (ac < AC_COUNT) && (tx_map >= rx_map) &&
- dhdp->wlfc_borrow_allowed) {
- /* try borrow from lower priority */
- lender = _dhd_wlfc_borrow_credit(ctx, ac - 1, ac, FALSE);
- if (lender != -1) {
- no_credit = FALSE;
- }
- }
- #endif // endif
- commit_info.needs_hdr = 1;
- commit_info.mac_entry = NULL;
- commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
- &(commit_info.ac_fifo_credit_spent),
- &(commit_info.needs_hdr),
- &(commit_info.mac_entry),
- no_credit);
- commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
- eWLFC_PKTTYPE_SUPPRESSED;
- if (commit_info.p == NULL) {
- #ifdef LIMIT_BORROW
- if (lender != -1 && dhdp->wlfc_borrow_allowed) {
- _dhd_wlfc_return_credit(ctx, lender, ac);
- }
- #endif // endif
- break;
- }
- if (!dhdp->proptxstatus_credit_ignore && (lender == -1)) {
- ASSERT(ctx->FIFO_credit[ac] >= commit_info.ac_fifo_credit_spent);
- }
- /* here we can ensure have credit or no credit needed */
- rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
- ctx->fcommit, ctx->commit_ctx);
- /* Bus commits may fail (e.g. flow control); abort after retries */
- if (rc == BCME_OK) {
- pkt_send++;
- pkt_send_per_ac++;
- if (commit_info.ac_fifo_credit_spent && (lender == -1)) {
- ctx->FIFO_credit[ac]--;
- }
- #ifdef LIMIT_BORROW
- else if (!commit_info.ac_fifo_credit_spent && (lender != -1) &&
- dhdp->wlfc_borrow_allowed) {
- _dhd_wlfc_return_credit(ctx, lender, ac);
- }
- #endif // endif
- } else {
- #ifdef LIMIT_BORROW
- if (lender != -1 && dhdp->wlfc_borrow_allowed) {
- _dhd_wlfc_return_credit(ctx, lender, ac);
- }
- #endif // endif
- bus_retry_count++;
- if (bus_retry_count >= BUS_RETRIES) {
- DHD_ERROR(("%s: bus error %d\n", __FUNCTION__, rc));
- goto exit;
- }
- }
- }
- if (ctx->pkt_cnt_per_ac[ac]) {
- packets_map |= (1 << ac);
- }
- }
- if ((tx_map == 0) || dhdp->proptxstatus_credit_ignore) {
- /* nothing send out or remain in queue */
- rc = BCME_OK;
- goto exit;
- }
- if (((tx_map & (tx_map - 1)) == 0) && (tx_map >= rx_map)) {
- /* only one tx ac exist and no higher rx ac */
- if ((single_ac == ctx->single_ac) && ctx->allow_credit_borrow) {
- ac = single_ac - 1;
- } else {
- uint32 delta;
- uint32 curr_t = OSL_SYSUPTIME();
- if (single_ac != ctx->single_ac) {
- /* new single ac traffic (first single ac or different single ac) */
- ctx->allow_credit_borrow = 0;
- ctx->single_ac_timestamp = curr_t;
- ctx->single_ac = (uint8)single_ac;
- rc = BCME_OK;
- goto exit;
- }
- /* same ac traffic, check if it lasts enough time */
- delta = curr_t - ctx->single_ac_timestamp;
- if (delta >= WLFC_BORROW_DEFER_PERIOD_MS) {
- /* wait enough time, can borrow now */
- ctx->allow_credit_borrow = 1;
- ac = single_ac - 1;
- } else {
- rc = BCME_OK;
- goto exit;
- }
- }
- } else {
- /* If we have multiple AC traffic, turn off borrowing, mark time and bail out */
- ctx->allow_credit_borrow = 0;
- ctx->single_ac_timestamp = 0;
- ctx->single_ac = 0;
- rc = BCME_OK;
- goto exit;
- }
- if (packets_map == 0) {
- /* nothing to send, skip borrow */
- rc = BCME_OK;
- goto exit;
- }
- /* At this point, borrow all credits only for ac */
- while (FALSE == dhdp->proptxstatus_txoff) {
- #ifdef LIMIT_BORROW
- if (dhdp->wlfc_borrow_allowed) {
- if ((lender = _dhd_wlfc_borrow_credit(ctx, AC_COUNT, ac, TRUE)) == -1) {
- break;
- }
- }
- else
- break;
- #endif // endif
- commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
- &(commit_info.ac_fifo_credit_spent),
- &(commit_info.needs_hdr),
- &(commit_info.mac_entry),
- FALSE);
- if (commit_info.p == NULL) {
- /* before borrow only one ac exists and now this only ac is empty */
- #ifdef LIMIT_BORROW
- _dhd_wlfc_return_credit(ctx, lender, ac);
- #endif // endif
- break;
- }
- commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
- eWLFC_PKTTYPE_SUPPRESSED;
- rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
- ctx->fcommit, ctx->commit_ctx);
- /* Bus commits may fail (e.g. flow control); abort after retries */
- if (rc == BCME_OK) {
- pkt_send++;
- if (commit_info.ac_fifo_credit_spent) {
- #ifndef LIMIT_BORROW
- ctx->FIFO_credit[ac]--;
- #endif // endif
- } else {
- #ifdef LIMIT_BORROW
- _dhd_wlfc_return_credit(ctx, lender, ac);
- #endif // endif
- }
- } else {
- #ifdef LIMIT_BORROW
- _dhd_wlfc_return_credit(ctx, lender, ac);
- #endif // endif
- bus_retry_count++;
- if (bus_retry_count >= BUS_RETRIES) {
- DHD_ERROR(("%s: bus error %d\n", __FUNCTION__, rc));
- goto exit;
- }
- }
- }
- BCM_REFERENCE(pkt_send);
- exit:
- #if defined(DHD_WLFC_THREAD)
- dhd_os_wlfc_unblock(dhdp);
- if (ctx && ctx->pkt_cnt_in_psq && pkt_send) {
- wait_msec = msecs_to_jiffies(WLFC_THREAD_QUICK_RETRY_WAIT_MS);
- } else {
- wait_msec = msecs_to_jiffies(WLFC_THREAD_RETRY_WAIT_MS);
- }
- }
- return 0;
- #else
- return rc;
- #endif /* defined(DHD_WLFC_THREAD) */
- }
- /**
- * Enqueues a transmit packet in the next layer towards the dongle, eg the DBUS layer. Called by
- * eg dhd_sendpkt().
- * @param[in] dhdp Pointer to public DHD structure
- * @param[in] fcommit Pointer to transmit function of next layer
- * @param[in] commit_ctx Opaque context used when calling next layer
- * @param[in] pktbuf Packet to send
- * @param[in] need_toggle_host_if If TRUE, resets flag ctx->toggle_host_if
- */
- int
- dhd_wlfc_commit_packets(dhd_pub_t *dhdp, f_commitpkt_t fcommit, void* commit_ctx, void *pktbuf,
- bool need_toggle_host_if)
- {
- int rc = BCME_OK;
- athost_wl_status_info_t* ctx;
- #if defined(DHD_WLFC_THREAD)
- if (!pktbuf)
- return BCME_OK;
- #endif /* defined(DHD_WLFC_THREAD) */
- if ((dhdp == NULL) || (fcommit == NULL)) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhdp);
- if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
- if (pktbuf) {
- DHD_PKTTAG_WLFCPKT_SET(PKTTAG(pktbuf), 0);
- }
- rc = WLFC_UNSUPPORTED;
- goto exit;
- }
- ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
- if (dhdp->proptxstatus_module_ignore) {
- if (pktbuf) {
- uint32 htod = 0;
- WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST);
- _dhd_wlfc_pushheader(ctx, &pktbuf, FALSE, 0, 0, htod, 0, FALSE);
- if (fcommit(commit_ctx, pktbuf)) {
- /* free it if failed, otherwise do it in tx complete cb */
- PKTFREE(ctx->osh, pktbuf, TRUE);
- }
- rc = BCME_OK;
- }
- goto exit;
- }
- if (pktbuf) {
- int ac = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
- ASSERT(ac <= AC_COUNT);
- DHD_PKTTAG_WLFCPKT_SET(PKTTAG(pktbuf), 1);
- /* en-queue the packets to respective queue. */
- rc = _dhd_wlfc_enque_delayq(ctx, pktbuf, ac);
- if (rc) {
- _dhd_wlfc_prec_drop(ctx->dhdp, (ac << 1), pktbuf, FALSE);
- } else {
- ctx->stats.pktin++;
- ctx->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pktbuf))][ac]++;
- }
- }
- if (!ctx->fcommit) {
- ctx->fcommit = fcommit;
- } else {
- ASSERT(ctx->fcommit == fcommit);
- }
- if (!ctx->commit_ctx) {
- ctx->commit_ctx = commit_ctx;
- } else {
- ASSERT(ctx->commit_ctx == commit_ctx);
- }
- #if defined(DHD_WLFC_THREAD)
- _dhd_wlfc_thread_wakeup(dhdp);
- #else
- dhd_wlfc_transfer_packets(dhdp);
- #endif /* defined(DHD_WLFC_THREAD) */
- exit:
- dhd_os_wlfc_unblock(dhdp);
- return rc;
- } /* dhd_wlfc_commit_packets */
- /**
- * Called when the (lower) DBUS layer indicates completion (succesfull or not) of a transmit packet
- */
- int
- dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success)
- {
- athost_wl_status_info_t* wlfc;
- wlfc_mac_descriptor_t *entry;
- void* pout = NULL;
- int rtn = BCME_OK;
- if ((dhd == NULL) || (txp == NULL)) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- bcm_pkt_validate_chk(txp);
- dhd_os_wlfc_block(dhd);
- if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
- rtn = WLFC_UNSUPPORTED;
- goto EXIT;
- }
- wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
- if (DHD_PKTTAG_SIGNALONLY(PKTTAG(txp))) {
- #ifdef PROP_TXSTATUS_DEBUG
- wlfc->stats.signal_only_pkts_freed++;
- #endif // endif
- /* is this a signal-only packet? */
- _dhd_wlfc_pullheader(wlfc, txp);
- PKTFREE(wlfc->osh, txp, TRUE);
- goto EXIT;
- }
- entry = _dhd_wlfc_find_table_entry(wlfc, txp);
- ASSERT(entry);
- if (!success || dhd->proptxstatus_txstatus_ignore) {
- WLFC_DBGMESG(("At: %s():%d, bus_complete() failure for %p, htod_tag:0x%08x\n",
- __FUNCTION__, __LINE__, txp, DHD_PKTTAG_H2DTAG(PKTTAG(txp))));
- if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
- _dhd_wlfc_hanger_poppkt(wlfc->hanger, WL_TXSTATUS_GET_HSLOT(
- DHD_PKTTAG_H2DTAG(PKTTAG(txp))), &pout, TRUE);
- ASSERT(txp == pout);
- }
- /* indicate failure and free the packet */
- dhd_txcomplete(dhd, txp, success);
- /* return the credit, if necessary */
- _dhd_wlfc_return_implied_credit(wlfc, txp);
- if (entry->transit_count)
- entry->transit_count--;
- if (entry->suppr_transit_count)
- entry->suppr_transit_count--;
- wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(txp))][DHD_PKTTAG_FIFO(PKTTAG(txp))]--;
- wlfc->stats.pktout++;
- PKTFREE(wlfc->osh, txp, TRUE);
- } else {
- /* bus confirmed pkt went to firmware side */
- if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
- _dhd_wlfc_enque_afq(wlfc, txp);
- } else {
- int hslot = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(txp)));
- _dhd_wlfc_hanger_free_pkt(wlfc, hslot,
- WLFC_HANGER_PKT_STATE_BUSRETURNED, -1);
- }
- }
- ASSERT(entry->onbus_pkts_count > 0);
- if (entry->onbus_pkts_count > 0)
- entry->onbus_pkts_count--;
- if (entry->suppressed &&
- (!entry->onbus_pkts_count) &&
- (!entry->suppr_transit_count))
- entry->suppressed = FALSE;
- EXIT:
- dhd_os_wlfc_unblock(dhd);
- return rtn;
- } /* dhd_wlfc_txcomplete */
- int
- dhd_wlfc_init(dhd_pub_t *dhd)
- {
- /* enable all signals & indicate host proptxstatus logic is active */
- uint32 tlv, mode, fw_caps;
- int ret = 0;
- if (dhd == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- if (dhd->wlfc_enabled) {
- DHD_INFO(("%s():%d, Already enabled!\n", __FUNCTION__, __LINE__));
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- dhd->wlfc_enabled = TRUE;
- dhd_os_wlfc_unblock(dhd);
- tlv = WLFC_FLAGS_RSSI_SIGNALS |
- WLFC_FLAGS_XONXOFF_SIGNALS |
- WLFC_FLAGS_CREDIT_STATUS_SIGNALS |
- WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
- WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
- /*
- try to enable/disable signaling by sending "tlv" iovar. if that fails,
- fallback to no flow control? Print a message for now.
- */
- /* enable proptxtstatus signaling by default */
- if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
- /*
- Leaving the message for now, it should be removed after a while; once
- the tlv situation is stable.
- */
- DHD_INFO(("dhd_wlfc_init(): successfully %s bdcv2 tlv signaling, %d\n",
- dhd->wlfc_enabled?"enabled":"disabled", tlv));
- }
- mode = 0;
- /* query caps */
- ret = dhd_wl_ioctl_get_intiovar(dhd, "wlfc_mode", &fw_caps, WLC_GET_VAR, FALSE, 0);
- if (!ret) {
- DHD_INFO(("%s: query wlfc_mode succeed, fw_caps=0x%x\n", __FUNCTION__, fw_caps));
- if (WLFC_IS_OLD_DEF(fw_caps)) {
- /* enable proptxtstatus v2 by default */
- mode = WLFC_MODE_AFQ;
- } else {
- WLFC_SET_AFQ(mode, WLFC_GET_AFQ(fw_caps));
- WLFC_SET_REORDERSUPP(mode, WLFC_GET_REORDERSUPP(fw_caps));
- }
- ret = dhd_wl_ioctl_set_intiovar(dhd, "wlfc_mode", mode, WLC_SET_VAR, TRUE, 0);
- }
- dhd_os_wlfc_block(dhd);
- dhd->wlfc_mode = 0;
- if (ret >= 0) {
- if (WLFC_IS_OLD_DEF(mode)) {
- WLFC_SET_AFQ(dhd->wlfc_mode, (mode == WLFC_MODE_AFQ));
- } else {
- dhd->wlfc_mode = mode;
- }
- }
- DHD_INFO(("dhd_wlfc_init(): wlfc_mode=0x%x, ret=%d\n", dhd->wlfc_mode, ret));
- #ifdef LIMIT_BORROW
- dhd->wlfc_borrow_allowed = TRUE;
- #endif // endif
- dhd_os_wlfc_unblock(dhd);
- if (dhd->plat_init)
- dhd->plat_init((void *)dhd);
- return BCME_OK;
- } /* dhd_wlfc_init */
- /** AMPDU host reorder specific function */
- int
- dhd_wlfc_hostreorder_init(dhd_pub_t *dhd)
- {
- /* enable only ampdu hostreorder here */
- uint32 tlv;
- if (dhd == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- DHD_TRACE(("%s():%d Enter\n", __FUNCTION__, __LINE__));
- tlv = WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
- /* enable proptxtstatus signaling by default */
- if (dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
- DHD_ERROR(("%s(): failed to enable/disable bdcv2 tlv signaling\n",
- __FUNCTION__));
- } else {
- /*
- Leaving the message for now, it should be removed after a while; once
- the tlv situation is stable.
- */
- DHD_ERROR(("%s(): successful bdcv2 tlv signaling, %d\n",
- __FUNCTION__, tlv));
- }
- dhd_os_wlfc_block(dhd);
- dhd->proptxstatus_mode = WLFC_ONLY_AMPDU_HOSTREORDER;
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- int
- dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
- {
- if (dhd == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
- dhd_os_wlfc_unblock(dhd);
- return WLFC_UNSUPPORTED;
- }
- _dhd_wlfc_cleanup_txq(dhd, fn, arg);
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- /** release all packet resources */
- int
- dhd_wlfc_cleanup(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
- {
- if (dhd == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
- dhd_os_wlfc_unblock(dhd);
- return WLFC_UNSUPPORTED;
- }
- _dhd_wlfc_cleanup(dhd, fn, arg);
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- int
- dhd_wlfc_deinit(dhd_pub_t *dhd)
- {
- /* cleanup all psq related resources */
- athost_wl_status_info_t* wlfc;
- uint32 tlv = 0;
- uint32 hostreorder = 0;
- if (dhd == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- if (!dhd->wlfc_enabled) {
- DHD_ERROR(("%s():%d, Already disabled!\n", __FUNCTION__, __LINE__));
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- dhd->wlfc_enabled = FALSE;
- dhd_os_wlfc_unblock(dhd);
- /* query ampdu hostreorder */
- (void) dhd_wl_ioctl_get_intiovar(dhd, "ampdu_hostreorder",
- &hostreorder, WLC_GET_VAR, FALSE, 0);
- if (hostreorder) {
- tlv = WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
- DHD_ERROR(("%s():%d, maintain HOST RXRERODER flag in tvl\n",
- __FUNCTION__, __LINE__));
- }
- /* Disable proptxtstatus signaling for deinit */
- (void) dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0);
- dhd_os_wlfc_block(dhd);
- if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
- dhd_os_wlfc_unblock(dhd);
- return WLFC_UNSUPPORTED;
- }
- wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
- _dhd_wlfc_cleanup(dhd, NULL, NULL);
- if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
- int i;
- wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
- for (i = 0; i < h->max_items; i++) {
- if (h->items[i].state != WLFC_HANGER_ITEM_STATE_FREE) {
- _dhd_wlfc_hanger_free_pkt(wlfc, i,
- WLFC_HANGER_PKT_STATE_COMPLETE, TRUE);
- }
- }
- /* delete hanger */
- _dhd_wlfc_hanger_delete(dhd, h);
- }
- /* free top structure */
- DHD_OS_PREFREE(dhd, dhd->wlfc_state,
- sizeof(athost_wl_status_info_t));
- dhd->wlfc_state = NULL;
- dhd->proptxstatus_mode = hostreorder ?
- WLFC_ONLY_AMPDU_HOSTREORDER : WLFC_FCMODE_NONE;
- dhd_os_wlfc_unblock(dhd);
- if (dhd->plat_deinit)
- dhd->plat_deinit((void *)dhd);
- return BCME_OK;
- } /* dhd_wlfc_init */
- /**
- * Called on an interface event (WLC_E_IF) indicated by firmware
- * @param[in] dhdp Pointer to public DHD structure
- * @param[in] action eg eWLFC_MAC_ENTRY_ACTION_UPDATE or eWLFC_MAC_ENTRY_ACTION_ADD
- */
- int dhd_wlfc_interface_event(dhd_pub_t *dhdp, uint8 action, uint8 ifid, uint8 iftype, uint8* ea)
- {
- int rc;
- if (dhdp == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhdp);
- if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
- dhd_os_wlfc_unblock(dhdp);
- return WLFC_UNSUPPORTED;
- }
- rc = _dhd_wlfc_interface_entry_update(dhdp->wlfc_state, action, ifid, iftype, ea);
- dhd_os_wlfc_unblock(dhdp);
- return rc;
- }
- /** Called eg on receiving a WLC_E_FIFO_CREDIT_MAP event from the dongle */
- int dhd_wlfc_FIFOcreditmap_event(dhd_pub_t *dhdp, uint8* event_data)
- {
- int rc;
- if (dhdp == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhdp);
- if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
- dhd_os_wlfc_unblock(dhdp);
- return WLFC_UNSUPPORTED;
- }
- rc = _dhd_wlfc_FIFOcreditmap_update(dhdp->wlfc_state, event_data);
- dhd_os_wlfc_unblock(dhdp);
- return rc;
- }
- #ifdef LIMIT_BORROW
- int dhd_wlfc_disable_credit_borrow_event(dhd_pub_t *dhdp, uint8* event_data)
- {
- if (dhdp == NULL || event_data == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhdp);
- dhdp->wlfc_borrow_allowed = (bool)(*(uint32 *)event_data);
- dhd_os_wlfc_unblock(dhdp);
- return BCME_OK;
- }
- #endif /* LIMIT_BORROW */
- /**
- * Called eg on receiving a WLC_E_BCMC_CREDIT_SUPPORT event from the dongle (broadcast/multicast
- * specific)
- */
- int dhd_wlfc_BCMCCredit_support_event(dhd_pub_t *dhdp)
- {
- int rc;
- if (dhdp == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhdp);
- if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
- dhd_os_wlfc_unblock(dhdp);
- return WLFC_UNSUPPORTED;
- }
- rc = _dhd_wlfc_BCMCCredit_support_update(dhdp->wlfc_state);
- dhd_os_wlfc_unblock(dhdp);
- return rc;
- }
- /** debug specific function */
- int
- dhd_wlfc_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
- {
- int i;
- uint8* ea;
- athost_wl_status_info_t* wlfc;
- wlfc_hanger_t* h;
- wlfc_mac_descriptor_t* mac_table;
- wlfc_mac_descriptor_t* interfaces;
- char* iftypes[] = {"STA", "AP", "WDS", "p2pGO", "p2pCL"};
- if (!dhdp || !strbuf) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhdp);
- if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
- dhd_os_wlfc_unblock(dhdp);
- return WLFC_UNSUPPORTED;
- }
- wlfc = (athost_wl_status_info_t*)dhdp->wlfc_state;
- h = (wlfc_hanger_t*)wlfc->hanger;
- if (h == NULL) {
- bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n");
- }
- mac_table = wlfc->destination_entries.nodes;
- interfaces = wlfc->destination_entries.interfaces;
- bcm_bprintf(strbuf, "---- wlfc stats ----\n");
- if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
- h = (wlfc_hanger_t*)wlfc->hanger;
- if (h == NULL) {
- bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n");
- } else {
- bcm_bprintf(strbuf, "wlfc hanger (pushed,popped,f_push,"
- "f_pop,f_slot, pending) = (%d,%d,%d,%d,%d,%d)\n",
- h->pushed,
- h->popped,
- h->failed_to_push,
- h->failed_to_pop,
- h->failed_slotfind,
- (h->pushed - h->popped));
- }
- }
- bcm_bprintf(strbuf, "wlfc fail(tlv,credit_rqst,mac_update,psmode_update), "
- "(dq_full,rollback_fail) = (%d,%d,%d,%d), (%d,%d)\n",
- wlfc->stats.tlv_parse_failed,
- wlfc->stats.credit_request_failed,
- wlfc->stats.mac_update_failed,
- wlfc->stats.psmode_update_failed,
- wlfc->stats.delayq_full_error,
- wlfc->stats.rollback_failed);
- bcm_bprintf(strbuf, "PKTS (init_credit,credit,sent,drop_d,drop_s,outoforder) "
- "(AC0[%d,%d,%d,%d,%d,%d],AC1[%d,%d,%d,%d,%d,%d],AC2[%d,%d,%d,%d,%d,%d],"
- "AC3[%d,%d,%d,%d,%d,%d],BC_MC[%d,%d,%d,%d,%d,%d])\n",
- wlfc->Init_FIFO_credit[0], wlfc->FIFO_credit[0], wlfc->stats.send_pkts[0],
- wlfc->stats.drop_pkts[0], wlfc->stats.drop_pkts[1], wlfc->stats.ooo_pkts[0],
- wlfc->Init_FIFO_credit[1], wlfc->FIFO_credit[1], wlfc->stats.send_pkts[1],
- wlfc->stats.drop_pkts[2], wlfc->stats.drop_pkts[3], wlfc->stats.ooo_pkts[1],
- wlfc->Init_FIFO_credit[2], wlfc->FIFO_credit[2], wlfc->stats.send_pkts[2],
- wlfc->stats.drop_pkts[4], wlfc->stats.drop_pkts[5], wlfc->stats.ooo_pkts[2],
- wlfc->Init_FIFO_credit[3], wlfc->FIFO_credit[3], wlfc->stats.send_pkts[3],
- wlfc->stats.drop_pkts[6], wlfc->stats.drop_pkts[7], wlfc->stats.ooo_pkts[3],
- wlfc->Init_FIFO_credit[4], wlfc->FIFO_credit[4], wlfc->stats.send_pkts[4],
- wlfc->stats.drop_pkts[8], wlfc->stats.drop_pkts[9], wlfc->stats.ooo_pkts[4]);
- bcm_bprintf(strbuf, "\n");
- for (i = 0; i < WLFC_MAX_IFNUM; i++) {
- if (interfaces[i].occupied) {
- char* iftype_desc;
- if (interfaces[i].iftype > WLC_E_IF_ROLE_P2P_CLIENT)
- iftype_desc = "<Unknown";
- else
- iftype_desc = iftypes[interfaces[i].iftype];
- ea = interfaces[i].ea;
- bcm_bprintf(strbuf, "INTERFACE[%d].ea = "
- "["MACDBG"], if:%d, type: %s "
- "netif_flow_control:%s\n", i,
- MAC2STRDBG(ea), interfaces[i].interface_id,
- iftype_desc, ((wlfc->hostif_flow_state[i] == OFF)
- ? " OFF":" ON"));
- bcm_bprintf(strbuf, "INTERFACE[%d].PSQ(len,state,credit),"
- "(trans,supp_trans,onbus)"
- "= (%d,%s,%d),(%d,%d,%d)\n",
- i,
- interfaces[i].psq.n_pkts_tot,
- ((interfaces[i].state ==
- WLFC_STATE_OPEN) ? "OPEN":"CLOSE"),
- interfaces[i].requested_credit,
- interfaces[i].transit_count,
- interfaces[i].suppr_transit_count,
- interfaces[i].onbus_pkts_count);
- bcm_bprintf(strbuf, "INTERFACE[%d].PSQ"
- "(delay0,sup0,afq0),(delay1,sup1,afq1),(delay2,sup2,afq2),"
- "(delay3,sup3,afq3),(delay4,sup4,afq4) = (%d,%d,%d),"
- "(%d,%d,%d),(%d,%d,%d),(%d,%d,%d),(%d,%d,%d)\n",
- i,
- interfaces[i].psq.q[0].n_pkts,
- interfaces[i].psq.q[1].n_pkts,
- interfaces[i].afq.q[0].n_pkts,
- interfaces[i].psq.q[2].n_pkts,
- interfaces[i].psq.q[3].n_pkts,
- interfaces[i].afq.q[1].n_pkts,
- interfaces[i].psq.q[4].n_pkts,
- interfaces[i].psq.q[5].n_pkts,
- interfaces[i].afq.q[2].n_pkts,
- interfaces[i].psq.q[6].n_pkts,
- interfaces[i].psq.q[7].n_pkts,
- interfaces[i].afq.q[3].n_pkts,
- interfaces[i].psq.q[8].n_pkts,
- interfaces[i].psq.q[9].n_pkts,
- interfaces[i].afq.q[4].n_pkts);
- }
- }
- bcm_bprintf(strbuf, "\n");
- for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
- if (mac_table[i].occupied) {
- ea = mac_table[i].ea;
- bcm_bprintf(strbuf, "MAC_table[%d].ea = "
- "["MACDBG"], if:%d \n", i,
- MAC2STRDBG(ea), mac_table[i].interface_id);
- bcm_bprintf(strbuf, "MAC_table[%d].PSQ(len,state,credit),"
- "(trans,supp_trans,onbus)"
- "= (%d,%s,%d),(%d,%d,%d)\n",
- i,
- mac_table[i].psq.n_pkts_tot,
- ((mac_table[i].state ==
- WLFC_STATE_OPEN) ? " OPEN":"CLOSE"),
- mac_table[i].requested_credit,
- mac_table[i].transit_count,
- mac_table[i].suppr_transit_count,
- mac_table[i].onbus_pkts_count);
- #ifdef PROP_TXSTATUS_DEBUG
- bcm_bprintf(strbuf, "MAC_table[%d]: (opened, closed) = (%d, %d)\n",
- i, mac_table[i].opened_ct, mac_table[i].closed_ct);
- #endif // endif
- bcm_bprintf(strbuf, "MAC_table[%d].PSQ"
- "(delay0,sup0,afq0),(delay1,sup1,afq1),(delay2,sup2,afq2),"
- "(delay3,sup3,afq3),(delay4,sup4,afq4) =(%d,%d,%d),"
- "(%d,%d,%d),(%d,%d,%d),(%d,%d,%d),(%d,%d,%d)\n",
- i,
- mac_table[i].psq.q[0].n_pkts,
- mac_table[i].psq.q[1].n_pkts,
- mac_table[i].afq.q[0].n_pkts,
- mac_table[i].psq.q[2].n_pkts,
- mac_table[i].psq.q[3].n_pkts,
- mac_table[i].afq.q[1].n_pkts,
- mac_table[i].psq.q[4].n_pkts,
- mac_table[i].psq.q[5].n_pkts,
- mac_table[i].afq.q[2].n_pkts,
- mac_table[i].psq.q[6].n_pkts,
- mac_table[i].psq.q[7].n_pkts,
- mac_table[i].afq.q[3].n_pkts,
- mac_table[i].psq.q[8].n_pkts,
- mac_table[i].psq.q[9].n_pkts,
- mac_table[i].afq.q[4].n_pkts);
- }
- }
- #ifdef PROP_TXSTATUS_DEBUG
- {
- int avg;
- int moving_avg = 0;
- int moving_samples;
- if (wlfc->stats.latency_sample_count) {
- moving_samples = sizeof(wlfc->stats.deltas)/sizeof(uint32);
- for (i = 0; i < moving_samples; i++)
- moving_avg += wlfc->stats.deltas[i];
- moving_avg /= moving_samples;
- avg = (100 * wlfc->stats.total_status_latency) /
- wlfc->stats.latency_sample_count;
- bcm_bprintf(strbuf, "txstatus latency (average, last, moving[%d]) = "
- "(%d.%d, %03d, %03d)\n",
- moving_samples, avg/100, (avg - (avg/100)*100),
- wlfc->stats.latency_most_recent,
- moving_avg);
- }
- }
- bcm_bprintf(strbuf, "wlfc- fifo[0-5] credit stats: sent = (%d,%d,%d,%d,%d,%d), "
- "back = (%d,%d,%d,%d,%d,%d)\n",
- wlfc->stats.fifo_credits_sent[0],
- wlfc->stats.fifo_credits_sent[1],
- wlfc->stats.fifo_credits_sent[2],
- wlfc->stats.fifo_credits_sent[3],
- wlfc->stats.fifo_credits_sent[4],
- wlfc->stats.fifo_credits_sent[5],
- wlfc->stats.fifo_credits_back[0],
- wlfc->stats.fifo_credits_back[1],
- wlfc->stats.fifo_credits_back[2],
- wlfc->stats.fifo_credits_back[3],
- wlfc->stats.fifo_credits_back[4],
- wlfc->stats.fifo_credits_back[5]);
- {
- uint32 fifo_cr_sent = 0;
- uint32 fifo_cr_acked = 0;
- uint32 request_cr_sent = 0;
- uint32 request_cr_ack = 0;
- uint32 bc_mc_cr_ack = 0;
- for (i = 0; i < sizeof(wlfc->stats.fifo_credits_sent)/sizeof(uint32); i++) {
- fifo_cr_sent += wlfc->stats.fifo_credits_sent[i];
- }
- for (i = 0; i < sizeof(wlfc->stats.fifo_credits_back)/sizeof(uint32); i++) {
- fifo_cr_acked += wlfc->stats.fifo_credits_back[i];
- }
- for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
- if (wlfc->destination_entries.nodes[i].occupied) {
- request_cr_sent +=
- wlfc->destination_entries.nodes[i].dstncredit_sent_packets;
- }
- }
- for (i = 0; i < WLFC_MAX_IFNUM; i++) {
- if (wlfc->destination_entries.interfaces[i].occupied) {
- request_cr_sent +=
- wlfc->destination_entries.interfaces[i].dstncredit_sent_packets;
- }
- }
- for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
- if (wlfc->destination_entries.nodes[i].occupied) {
- request_cr_ack +=
- wlfc->destination_entries.nodes[i].dstncredit_acks;
- }
- }
- for (i = 0; i < WLFC_MAX_IFNUM; i++) {
- if (wlfc->destination_entries.interfaces[i].occupied) {
- request_cr_ack +=
- wlfc->destination_entries.interfaces[i].dstncredit_acks;
- }
- }
- bcm_bprintf(strbuf, "wlfc- (sent, status) => pq(%d,%d), vq(%d,%d),"
- "other:%d, bc_mc:%d, signal-only, (sent,freed): (%d,%d)",
- fifo_cr_sent, fifo_cr_acked,
- request_cr_sent, request_cr_ack,
- wlfc->destination_entries.other.dstncredit_acks,
- bc_mc_cr_ack,
- wlfc->stats.signal_only_pkts_sent, wlfc->stats.signal_only_pkts_freed);
- }
- #endif /* PROP_TXSTATUS_DEBUG */
- bcm_bprintf(strbuf, "\n");
- bcm_bprintf(strbuf, "wlfc- pkt((in,2bus,txstats,hdrpull,out),"
- "(dropped,hdr_only,wlc_tossed,wlc_dropped,wlc_exptime)"
- "(freed,free_err,rollback)) = "
- "((%d,%d,%d,%d,%d),(%d,%d,%d,%d,%d),(%d,%d,%d))\n",
- wlfc->stats.pktin,
- wlfc->stats.pkt2bus,
- wlfc->stats.txstatus_in,
- wlfc->stats.dhd_hdrpulls,
- wlfc->stats.pktout,
- wlfc->stats.pktdropped,
- wlfc->stats.wlfc_header_only_pkt,
- wlfc->stats.wlc_tossed_pkts,
- wlfc->stats.pkt_dropped,
- wlfc->stats.pkt_exptime,
- wlfc->stats.pkt_freed,
- wlfc->stats.pkt_free_err, wlfc->stats.rollback);
- bcm_bprintf(strbuf, "wlfc- suppress((d11,wlc,err),enq(d11,wl,hq,mac?),retx(d11,wlc,hq)) = "
- "((%d,%d,%d),(%d,%d,%d,%d),(%d,%d,%d))\n",
- wlfc->stats.d11_suppress,
- wlfc->stats.wl_suppress,
- wlfc->stats.bad_suppress,
- wlfc->stats.psq_d11sup_enq,
- wlfc->stats.psq_wlsup_enq,
- wlfc->stats.psq_hostq_enq,
- wlfc->stats.mac_handle_notfound,
- wlfc->stats.psq_d11sup_retx,
- wlfc->stats.psq_wlsup_retx,
- wlfc->stats.psq_hostq_retx);
- bcm_bprintf(strbuf, "wlfc- cleanup(txq,psq,fw) = (%d,%d,%d)\n",
- wlfc->stats.cleanup_txq_cnt,
- wlfc->stats.cleanup_psq_cnt,
- wlfc->stats.cleanup_fw_cnt);
- bcm_bprintf(strbuf, "wlfc- generic error: %d\n", wlfc->stats.generic_error);
- for (i = 0; i < WLFC_MAX_IFNUM; i++) {
- bcm_bprintf(strbuf, "wlfc- if[%d], pkt_cnt_in_q/AC[0-4] = (%d,%d,%d,%d,%d)\n", i,
- wlfc->pkt_cnt_in_q[i][0],
- wlfc->pkt_cnt_in_q[i][1],
- wlfc->pkt_cnt_in_q[i][2],
- wlfc->pkt_cnt_in_q[i][3],
- wlfc->pkt_cnt_in_q[i][4]);
- }
- bcm_bprintf(strbuf, "\n");
- dhd_os_wlfc_unblock(dhdp);
- return BCME_OK;
- } /* dhd_wlfc_dump */
- int dhd_wlfc_clear_counts(dhd_pub_t *dhd)
- {
- athost_wl_status_info_t* wlfc;
- wlfc_hanger_t* hanger;
- if (dhd == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
- dhd_os_wlfc_unblock(dhd);
- return WLFC_UNSUPPORTED;
- }
- wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
- memset(&wlfc->stats, 0, sizeof(athost_wl_stat_counters_t));
- if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
- hanger = (wlfc_hanger_t*)wlfc->hanger;
- hanger->pushed = 0;
- hanger->popped = 0;
- hanger->failed_slotfind = 0;
- hanger->failed_to_pop = 0;
- hanger->failed_to_push = 0;
- }
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- /** returns TRUE if flow control is enabled */
- int dhd_wlfc_get_enable(dhd_pub_t *dhd, bool *val)
- {
- if (!dhd || !val) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- *val = dhd->wlfc_enabled;
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- /** Called via an IOVAR */
- int dhd_wlfc_get_mode(dhd_pub_t *dhd, int *val)
- {
- if (!dhd || !val) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- *val = dhd->wlfc_state ? dhd->proptxstatus_mode : 0;
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- /** Called via an IOVAR */
- int dhd_wlfc_set_mode(dhd_pub_t *dhd, int val)
- {
- if (!dhd) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- if (dhd->wlfc_state) {
- dhd->proptxstatus_mode = val & 0xff;
- }
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- /** Called when rx frame is received from the dongle */
- bool dhd_wlfc_is_header_only_pkt(dhd_pub_t * dhd, void *pktbuf)
- {
- athost_wl_status_info_t* wlfc;
- bool rc = FALSE;
- if (dhd == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return FALSE;
- }
- dhd_os_wlfc_block(dhd);
- if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
- dhd_os_wlfc_unblock(dhd);
- return FALSE;
- }
- wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
- if (PKTLEN(wlfc->osh, pktbuf) == 0) {
- wlfc->stats.wlfc_header_only_pkt++;
- rc = TRUE;
- }
- dhd_os_wlfc_unblock(dhd);
- return rc;
- }
- int dhd_wlfc_flowcontrol(dhd_pub_t *dhdp, bool state, bool bAcquireLock)
- {
- if (dhdp == NULL) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- if (bAcquireLock) {
- dhd_os_wlfc_block(dhdp);
- }
- if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE) ||
- dhdp->proptxstatus_module_ignore) {
- if (bAcquireLock) {
- dhd_os_wlfc_unblock(dhdp);
- }
- return WLFC_UNSUPPORTED;
- }
- if (state != dhdp->proptxstatus_txoff) {
- dhdp->proptxstatus_txoff = state;
- }
- if (bAcquireLock) {
- dhd_os_wlfc_unblock(dhdp);
- }
- return BCME_OK;
- }
- /** Called when eg an rx frame is received from the dongle */
- int dhd_wlfc_save_rxpath_ac_time(dhd_pub_t * dhd, uint8 prio)
- {
- athost_wl_status_info_t* wlfc;
- int rx_path_ac = -1;
- if ((dhd == NULL) || (prio >= NUMPRIO)) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- if (!dhd->wlfc_rxpkt_chk) {
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
- dhd_os_wlfc_unblock(dhd);
- return WLFC_UNSUPPORTED;
- }
- wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
- rx_path_ac = prio2fifo[prio];
- wlfc->rx_timestamp[rx_path_ac] = OSL_SYSUPTIME();
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- /** called via an IOVAR */
- int dhd_wlfc_get_module_ignore(dhd_pub_t *dhd, int *val)
- {
- if (!dhd || !val) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- *val = dhd->proptxstatus_module_ignore;
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- /** called via an IOVAR */
- int dhd_wlfc_set_module_ignore(dhd_pub_t *dhd, int val)
- {
- uint32 tlv = 0;
- bool bChanged = FALSE;
- if (!dhd) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- if ((bool)val != dhd->proptxstatus_module_ignore) {
- dhd->proptxstatus_module_ignore = (val != 0);
- /* force txstatus_ignore sync with proptxstatus_module_ignore */
- dhd->proptxstatus_txstatus_ignore = dhd->proptxstatus_module_ignore;
- if (FALSE == dhd->proptxstatus_module_ignore) {
- tlv = WLFC_FLAGS_RSSI_SIGNALS |
- WLFC_FLAGS_XONXOFF_SIGNALS |
- WLFC_FLAGS_CREDIT_STATUS_SIGNALS |
- WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE;
- }
- /* always enable host reorder */
- tlv |= WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
- bChanged = TRUE;
- }
- dhd_os_wlfc_unblock(dhd);
- if (bChanged) {
- /* select enable proptxtstatus signaling */
- if (dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
- DHD_ERROR(("%s: failed to set bdcv2 tlv signaling to 0x%x\n",
- __FUNCTION__, tlv));
- } else {
- DHD_ERROR(("%s: successfully set bdcv2 tlv signaling to 0x%x\n",
- __FUNCTION__, tlv));
- }
- }
- #if defined(DHD_WLFC_THREAD)
- _dhd_wlfc_thread_wakeup(dhd);
- #endif /* defined(DHD_WLFC_THREAD) */
- return BCME_OK;
- }
- /** called via an IOVAR */
- int dhd_wlfc_get_credit_ignore(dhd_pub_t *dhd, int *val)
- {
- if (!dhd || !val) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- *val = dhd->proptxstatus_credit_ignore;
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- /** called via an IOVAR */
- int dhd_wlfc_set_credit_ignore(dhd_pub_t *dhd, int val)
- {
- if (!dhd) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- dhd->proptxstatus_credit_ignore = (val != 0);
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- /** called via an IOVAR */
- int dhd_wlfc_get_txstatus_ignore(dhd_pub_t *dhd, int *val)
- {
- if (!dhd || !val) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- *val = dhd->proptxstatus_txstatus_ignore;
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- /** called via an IOVAR */
- int dhd_wlfc_set_txstatus_ignore(dhd_pub_t *dhd, int val)
- {
- if (!dhd) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- dhd->proptxstatus_txstatus_ignore = (val != 0);
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- /** called via an IOVAR */
- int dhd_wlfc_get_rxpkt_chk(dhd_pub_t *dhd, int *val)
- {
- if (!dhd || !val) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- *val = dhd->wlfc_rxpkt_chk;
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- /** called via an IOVAR */
- int dhd_wlfc_set_rxpkt_chk(dhd_pub_t *dhd, int val)
- {
- if (!dhd) {
- DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
- return BCME_BADARG;
- }
- dhd_os_wlfc_block(dhd);
- dhd->wlfc_rxpkt_chk = (val != 0);
- dhd_os_wlfc_unblock(dhd);
- return BCME_OK;
- }
- #endif /* PROP_TXSTATUS */
|