12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429 |
- /*++
- Copyright (c) 2014 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- dwhcihc.c
- Abstract:
- This module implements support for the DesignWare Hi-Speed USB 2.O
- On-The-Go (HS OTG) Host Controller.
- Copyright (C) 2004-2013 by Synopsis, Inc.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- 1. Redistributions of source code must retain the above copyright notice,
- this list of conditions, and the following disclaimer, without
- modification.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. The names of the above-listed copyright holders may not be used to
- endorse or promote products derived from this software without specific
- prior written permission.
- This software is provided by the copyright holders and contributors "AS IS"
- and any express or implied warranties, including, by not limited to, the
- implied warranties or mechantability and fitness for a particular purpose
- are disclained. In no event shall the copyright owner or contributors be
- liable for any direct, indirect, incidental, special, exemplary, or
- consequential damages (including, but not limited to, procurement of
- substitue goods or services; loss of use, data, or profits; or business
- interruption) however caused and on any theory of liability, whether in
- contract, strict liability, or tort (including negligence or otherwise)
- arising in any way out of the use of this software, even if advised of the
- possibility of such damage.
- Author:
- Chris Stevens 38-Mar-2014
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/driver.h>
- #include <minoca/usb/usbhost.h>
- #include "dwhci.h"
- //
- // --------------------------------------------------------------------- Macros
- //
- //
- // These macros read from and write to a DWHCI host controller register. The
- // first argument is a PDWHCI_CONTROLLER, the second is a DWHCI_REGISTER. The
- // value on write is a ULONG.
- //
- #define DWHCI_READ_REGISTER(_Controller, _Register) \
- HlReadRegister32((_Controller)->RegisterBase + _Register)
- #define DWHCI_WRITE_REGISTER(_Controller, _Register, _Value) \
- HlWriteRegister32((_Controller)->RegisterBase + _Register, _Value)
- //
- // These macros read from and write to a DWHCI channel register. The first
- // argument is a PDWHCI_CONTROLLER, the second is a DWHCI_CHANNEL_REGISTER and
- // the third is a ULONG for the channel index. The value on write is a ULONG.
- //
- #define DWHCI_READ_CHANNEL_REGISTER(_Controller, _Register, _Channel) \
- HlReadRegister32((_Controller)->RegisterBase + \
- DwhciRegisterChannelBase + \
- (DwhciChannelRegistersSize * _Channel) + \
- _Register)
- #define DWHCI_WRITE_CHANNEL_REGISTER(_Controller, _Register, _Channel, _Value) \
- HlWriteRegister32(((_Controller)->RegisterBase + \
- DwhciRegisterChannelBase + \
- (DwhciChannelRegistersSize * _Channel) + \
- _Register), \
- _Value)
- //
- // This macro reads the frame number.
- //
- #define DWHCI_READ_FRAME_NUMBER(_Controller) \
- (((DWHCI_READ_REGISTER((_Controller), DwhciRegisterFrameNumber) & \
- DWHCI_FRAME_NUMBER_MASK) >> \
- DWHCI_FRAME_NUMBER_SHIFT) & \
- DWHCI_FRAME_NUMBER_MAX)
- //
- // This macro evaluates whether two frame numbers are in descending order,
- // taking wrapping into account.
- //
- #define DWHCI_FRAME_GREATER_THAN_OR_EQUAL(_Frame1, _Frame2) \
- (((((_Frame1) - (_Frame2)) & DWHCI_FRAME_NUMBER_MAX) & \
- DWHCI_FRAME_NUMBER_HIGH_BIT) == 0)
- //
- // This macro evaluates whether two frame numbers are in ascending order,
- // taking wrapping into account.
- //
- #define DWHCI_FRAME_LESS_THAN(_Frame1, _Frame2) \
- (((((_Frame1) - (_Frame2)) & DWHCI_FRAME_NUMBER_MAX) & \
- DWHCI_FRAME_NUMBER_HIGH_BIT) != 0)
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // Define the maximum number of channels that can exist in the host controller.
- //
- #define DWHCI_MAX_CHANNELS 16
- //
- // Define the default start frame offset for periodic transfers
- //
- #define DWHCI_DEFAULT_FRAME_OFFSET 15
- //
- // Define the maximum number of errors allowed on a split transfer.
- //
- #define DWHCI_SPLIT_ERROR_MAX 3
- //
- // Define the maximum number of complete splits allowed.
- //
- #define DWHCI_COMPLETE_SPLIT_MAX 3
- //
- // Define the mask to OR onto each interrupt split's next frame.
- //
- #define DWHCI_INTERRUPT_SPLIT_FRAME_MASK 0x7
- //
- // Define the number of microframes per frame.
- //
- #define DWHCI_MICROFRAMES_PER_FRAME 8
- #define DWHCI_MICROFRAMES_PER_FRAME_SHIFT 3
- //
- // Define the required alignment for DMA buffers.
- //
- #define DWHCI_DMA_ALIGNMENT 0x8
- //
- // Define the size of the control status buffer used as a bit bucket.
- //
- #define DWHCI_CONTROL_STATUS_BUFFER_SIZE 64
- //
- // Define the initial set of interrupts that the host controller is interested
- // in.
- //
- #define DWHCI_INITIAL_CORE_INTERRUPT_MASK \
- (DWHCI_CORE_INTERRUPT_DISCONNECT | \
- DWHCI_CORE_INTERRUPT_PORT | \
- DWHCI_CORE_INTERRUPT_HOST_CHANNEL)
- //
- // Define flags for DWHCI debugging.
- //
- #define DWHCI_DEBUG_FLAG_PORTS 0x1
- #define DWHCI_DEBUG_FLAG_TRANSFERS 0x2
- //
- // Define the value for an invalid frame.
- //
- #define DWHCI_INVALID_FRAME 0xFFFF
- //
- // Define the size of the window in which complete splits must finish, in
- // microframes. The start frame is recorded, and the start split actually
- // executes in the next microframe (1). Then there is a rest microframe (2),
- // followed by three microframes in which the complete split can finish (5).
- //
- #define DWHCI_SPLIT_NOT_YET_FRAME_WINDOW 5
- //
- // Define the DWHCI host controller revision that first handled automatic PING
- // processing for bulk and control transfers.
- //
- #define DWHCI_AUTOMATIC_PING_REVISION_MININUM 0x4f54271a
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- KSTATUS
- DwhcipCreateEndpoint (
- PVOID HostControllerContext,
- PUSB_HOST_ENDPOINT_CREATION_REQUEST Endpoint,
- PVOID *EndpointContext
- );
- VOID
- DwhcipResetEndpoint (
- PVOID HostControllerContext,
- PVOID EndpointContext,
- ULONG MaxPacketSize
- );
- VOID
- DwhcipDestroyEndpoint (
- PVOID HostControllerContext,
- PVOID EndpointContext
- );
- KSTATUS
- DwhcipCreateTransfer (
- PVOID HostControllerContext,
- PVOID EndpointContext,
- ULONG MaxBufferSize,
- ULONG Flags,
- PVOID *TransferContext
- );
- VOID
- DwhcipDestroyTransfer (
- PVOID HostControllerContext,
- PVOID EndpointContext,
- PVOID TransferContext
- );
- KSTATUS
- DwhcipSubmitTransfer (
- PVOID HostControllerContext,
- PVOID EndpointContext,
- PUSB_TRANSFER_INTERNAL Transfer,
- PVOID TransferContext
- );
- KSTATUS
- DwhcipCancelTransfer (
- PVOID HostControllerContext,
- PVOID EndpointContext,
- PUSB_TRANSFER_INTERNAL Transfer,
- PVOID TransferContext
- );
- KSTATUS
- DwhcipGetRootHubStatus (
- PVOID HostControllerContext,
- PUSB_HUB_STATUS HubStatus
- );
- KSTATUS
- DwhcipSetRootHubStatus (
- PVOID HostControllerContext,
- PUSB_HUB_STATUS NewStatus
- );
- RUNLEVEL
- DwhcipAcquireControllerLock (
- PDWHCI_CONTROLLER Controller
- );
- VOID
- DwhcipReleaseControllerLock (
- PDWHCI_CONTROLLER Controller,
- RUNLEVEL OldRunLevel
- );
- VOID
- DwhcipInterruptServiceDpc (
- PDPC Dpc
- );
- VOID
- DwhcipProcessInterrupt (
- PVOID Context
- );
- VOID
- DwhcipProcessStartOfFrameInterrupt (
- PDWHCI_CONTROLLER Controller
- );
- VOID
- DwhcipSaveChannelInterrupts (
- PDWHCI_CONTROLLER Controller
- );
- VOID
- DwhcipProcessChannelInterrupt (
- PDWHCI_CONTROLLER Controller,
- PULONG ChannelInterruptBits
- );
- VOID
- DwhcipProcessPotentiallyCompletedTransfer (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_TRANSFER Transfer,
- ULONG Interrupts,
- PBOOL RemoveSet,
- PBOOL AdvanceEndpoint
- );
- VOID
- DwhcipRemoveTransferSet (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_TRANSFER_SET TransferSet
- );
- VOID
- DwhcipProcessSplitEndpoint (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_ENDPOINT Endpoint,
- PULONG Interrupts
- );
- VOID
- DwhcipProcessPingEndpoint (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_ENDPOINT Endpoint,
- PULONG Interrupts
- );
- VOID
- DwhcipFillOutTransferDescriptor (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_TRANSFER_SET TransferSet,
- PDWHCI_TRANSFER DwhciTransfer,
- ULONG Offset,
- ULONG Length,
- BOOL LastTransfer
- );
- VOID
- DwhcipProcessSchedule (
- PDWHCI_CONTROLLER Controller,
- BOOL PeriodicOnly
- );
- KSTATUS
- DwhcipAllocateChannel (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_ENDPOINT Endpoint
- );
- VOID
- DwhcipFreeChannel (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_CHANNEL Channel
- );
- VOID
- DwhcipScheduleTransfer (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_ENDPOINT Endpoint
- );
- VOID
- DwhcipAdvanceEndpoint (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_ENDPOINT Endpoint
- );
- PDWHCI_TRANSFER
- DwhcipGetEndpointTransfer (
- PDWHCI_ENDPOINT Endpoint
- );
- KSTATUS
- DwhcipSoftReset (
- PDWHCI_CONTROLLER Controller
- );
- KSTATUS
- DwhcipInitializePhy (
- PDWHCI_CONTROLLER Controller
- );
- KSTATUS
- DwhcipInitializeUsb (
- PDWHCI_CONTROLLER Controller,
- ULONG UsbMode
- );
- KSTATUS
- DwhcipInitializeHostMode (
- PDWHCI_CONTROLLER Controller,
- ULONG ReceiveFifoSize,
- ULONG NonPeriodicTransmitFifoSize,
- ULONG PeriodicTransmitFifoSize
- );
- VOID
- DwhcipFlushFifo (
- PDWHCI_CONTROLLER Controller,
- BOOL TransmitFifo,
- ULONG TransmitFifoMask
- );
- KSTATUS
- DwhcipResetChannel (
- PDWHCI_CONTROLLER Controller,
- ULONG ChannelNumber
- );
- BOOL
- DwhcipHaltChannel (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_CHANNEL Channel
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // Define a bitfield of debug flags that enable various print messages for
- // DWHCI. See DWHCI_DEBUG_* definitions.
- //
- ULONG DwhciDebugFlags = 0x0;
- //
- // ------------------------------------------------------------------ Functions
- //
- PDWHCI_CONTROLLER
- DwhcipInitializeControllerState (
- PVOID RegisterBase,
- ULONG ChannelCount,
- USB_DEVICE_SPEED Speed,
- ULONG MaxTransferSize,
- ULONG MaxPacketCount,
- ULONG Revision
- )
- /*++
- Routine Description:
- This routine initializes the state and variables needed to start up a DWHCI
- host controller.
- Arguments:
- RegisterBase - Supplies the virtual address of the base of the registers.
- ChannelCount - Supplies the number of host controller channels.
- Speed - Supplies the speed of the DWHCI host controller.
- MaxTransferSize - Supplies the maximum transfer size for the DWHCI host
- controller.
- MaxPacketCount - Supplies the maximum packet count for the DWHCI host
- controller.
- Revision - Supplies the revision of the DWHCI host controller.
- Return Value:
- Returns a pointer to the DWHCI controller state object on success.
- NULL on failure.
- --*/
- {
- ULONG AllocationSize;
- PDWHCI_CHANNEL Channels;
- PDWHCI_CONTROLLER Controller;
- ULONG Index;
- ULONG IoBufferFlags;
- KSTATUS Status;
- //
- // Allocate the controller structure and fill it in.
- //
- AllocationSize = sizeof(DWHCI_CONTROLLER) +
- ((ChannelCount - 1) * sizeof(DWHCI_CHANNEL));
- Controller = MmAllocateNonPagedPool(AllocationSize, DWHCI_ALLOCATION_TAG);
- if (Controller == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto InitializeControllerStateEnd;
- }
- RtlZeroMemory(Controller, AllocationSize);
- Controller->RegisterBase = RegisterBase;
- Controller->UsbCoreHandle = INVALID_HANDLE;
- Controller->InterruptHandle = INVALID_HANDLE;
- INITIALIZE_LIST_HEAD(&(Controller->PeriodicActiveListHead));
- INITIALIZE_LIST_HEAD(&(Controller->PeriodicInactiveListHead));
- INITIALIZE_LIST_HEAD(&(Controller->PeriodicReadyListHead));
- INITIALIZE_LIST_HEAD(&(Controller->NonPeriodicActiveListHead));
- INITIALIZE_LIST_HEAD(&(Controller->NonPeriodicReadyListHead));
- INITIALIZE_LIST_HEAD(&(Controller->FreeChannelListHead));
- KeInitializeSpinLock(&(Controller->Lock));
- KeInitializeSpinLock(&(Controller->InterruptLock));
- Controller->PortCount = DWHCI_HOST_PORT_COUNT;
- Controller->Revision = Revision;
- Controller->Speed = Speed;
- Controller->MaxTransferSize = MaxTransferSize;
- Controller->MaxPacketCount = MaxPacketCount;
- Controller->NextFrame = DWHCI_INVALID_FRAME;
- Controller->InterruptDpc = KeCreateDpc(DwhcipInterruptServiceDpc,
- Controller);
- if (Controller->InterruptDpc == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto InitializeControllerStateEnd;
- }
- Controller->BlockAllocator = MmCreateBlockAllocator(
- sizeof(DWHCI_TRANSFER),
- DWHCI_BLOCK_ALLOCATOR_ALIGNMENT,
- DWHCI_BLOCK_ALLOCATOR_EXPANSION_COUNT,
- BLOCK_ALLOCATOR_FLAG_NON_PAGED,
- DWHCI_BLOCK_ALLOCATION_TAG);
- if (Controller->BlockAllocator == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto InitializeControllerStateEnd;
- }
- IoBufferFlags = IO_BUFFER_FLAG_PHYSICALLY_CONTIGUOUS;
- Controller->ControlStatusBuffer = MmAllocateNonPagedIoBuffer(
- 0,
- MAX_ULONG,
- DWHCI_DMA_ALIGNMENT,
- DWHCI_CONTROL_STATUS_BUFFER_SIZE,
- IoBufferFlags);
- if (Controller->ControlStatusBuffer == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto InitializeControllerStateEnd;
- }
- //
- // Initialize the channels.
- //
- Controller->ChannelCount = ChannelCount;
- Channels = (PDWHCI_CHANNEL)(Controller->Channel);
- for (Index = 0; Index < ChannelCount; Index += 1) {
- Channels[Index].ChannelNumber = Index;
- }
- Status = STATUS_SUCCESS;
- InitializeControllerStateEnd:
- if (!KSUCCESS(Status)) {
- if (Controller != NULL) {
- DwhcipDestroyControllerState(Controller);
- Controller = NULL;
- }
- }
- return Controller;
- }
- VOID
- DwhcipDestroyControllerState (
- PDWHCI_CONTROLLER Controller
- )
- /*++
- Routine Description:
- This routine destroys the memory associated with a DWHCI controller.
- Arguments:
- Controller - Supplies a pointer to the DWHCI controller state to release.
- Return Value:
- None.
- --*/
- {
- ASSERT(LIST_EMPTY(&(Controller->PeriodicActiveListHead)) != FALSE);
- ASSERT(LIST_EMPTY(&(Controller->PeriodicReadyListHead)) != FALSE);
- ASSERT(LIST_EMPTY(&(Controller->NonPeriodicActiveListHead)) != FALSE);
- ASSERT(LIST_EMPTY(&(Controller->NonPeriodicReadyListHead)) != FALSE);
- if (Controller->InterruptDpc != NULL) {
- KeDestroyDpc(Controller->InterruptDpc);
- }
- if (Controller->UsbCoreHandle != INVALID_HANDLE) {
- UsbHostDestroyControllerState(Controller->UsbCoreHandle);
- }
- if (Controller->BlockAllocator != NULL) {
- MmDestroyBlockAllocator(Controller->BlockAllocator);
- }
- if (Controller->ControlStatusBuffer != NULL) {
- MmFreeIoBuffer(Controller->ControlStatusBuffer);
- }
- MmFreeNonPagedPool(Controller);
- return;
- }
- KSTATUS
- DwhcipRegisterController (
- PDWHCI_CONTROLLER Controller,
- PDEVICE Device
- )
- /*++
- Routine Description:
- This routine registers the started DWHCI controller with the core USB
- library.
- Arguments:
- Controller - Supplies a pointer to the DWHCI controller state of the
- controller to register.
- Device - Supplies a pointer to the device object.
- Return Value:
- Status code.
- --*/
- {
- USB_HOST_CONTROLLER_INTERFACE Interface;
- KSTATUS Status;
- //
- // Fill out the functions that the USB core library will use to control
- // the DWHCI controller.
- //
- RtlZeroMemory(&Interface, sizeof(USB_HOST_CONTROLLER_INTERFACE));
- Interface.Version = USB_HOST_CONTROLLER_INTERFACE_VERSION;
- Interface.DriverObject = DwhciDriver;
- Interface.DeviceObject = Device;
- Interface.HostControllerContext = Controller;
- Interface.Speed = Controller->Speed;
- Interface.DebugPortSubType = -1;
- Interface.RootHubPortCount = Controller->PortCount;
- Interface.CreateEndpoint = DwhcipCreateEndpoint;
- Interface.ResetEndpoint = DwhcipResetEndpoint;
- Interface.DestroyEndpoint = DwhcipDestroyEndpoint;
- Interface.CreateTransfer = DwhcipCreateTransfer;
- Interface.DestroyTransfer = DwhcipDestroyTransfer;
- Interface.SubmitTransfer = DwhcipSubmitTransfer;
- Interface.CancelTransfer = DwhcipCancelTransfer;
- Interface.GetRootHubStatus = DwhcipGetRootHubStatus;
- Interface.SetRootHubStatus = DwhcipSetRootHubStatus;
- Status = UsbHostRegisterController(&Interface,
- &(Controller->UsbCoreHandle));
- if (!KSUCCESS(Status)) {
- goto RegisterControllerEnd;
- }
- RegisterControllerEnd:
- return Status;
- }
- KSTATUS
- DwhcipInitializeController (
- PDWHCI_CONTROLLER Controller
- )
- /*++
- Routine Description:
- This routine initializes and starts the DWHCI controller.
- Arguments:
- Controller - Supplies a pointer to the DWHCI controller state of the
- controller to reset.
- Return Value:
- Status code.
- --*/
- {
- ULONG AhbConfiguration;
- ULONG BurstLength;
- ULONG CoreInterruptMask;
- ULONG Hardware2;
- ULONG Hardware4;
- ULONG HostConfiguration;
- ULONG NonPeriodicTransmitFifoSize;
- ULONG PeriodicTransmitFifoSize;
- ULONG ReceiveFifoSize;
- KSTATUS Status;
- ULONG UsbCapabilities;
- ULONG UsbConfiguration;
- //
- // Before resetting the controller, save the FIFO sizes that may have been
- // programmed by ACPI. The reset will undo any of the work done by ACPI.
- //
- ReceiveFifoSize = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterReceiveFifoSize);
- NonPeriodicTransmitFifoSize = DWHCI_READ_REGISTER(
- Controller,
- DwhciRegisterNonPeriodicFifoSize);
- PeriodicTransmitFifoSize = DWHCI_READ_REGISTER(
- Controller,
- DwhciRegisterPeriodicFifoSize);
- //
- // Save the burst length configured by ACPI in the AHB register and disable
- // global interrupts.
- //
- AhbConfiguration = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterAhbConfiguration);
- BurstLength = (AhbConfiguration &
- DWHCI_AHB_CONFIGURATION_AXI_BURST_LENGTH_MASK);
- AhbConfiguration &= ~DWHCI_AHB_CONFIGURATION_INTERRUPT_ENABLE;
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterAhbConfiguration,
- AhbConfiguration);
- //
- // Clear the ULPI External VBUS and TS D-LINE pulse enable bits.
- //
- UsbConfiguration = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterUsbConfiguration);
- //
- // Save the USB capability bits in the USB configuration register. These do
- // not always agree with the mode set in the hardware 2 register.
- //
- UsbCapabilities = UsbConfiguration & (DWHCI_USB_CONFIGURATION_SRP_CAPABLE |
- DWHCI_USB_CONFIGURATION_HNP_CAPABLE);
- UsbConfiguration &= ~DWHCI_USB_CONFIGURATION_ULPI_DRIVER_EXTERNAL_VBUS;
- UsbConfiguration &= ~DWHCI_USB_CONFIGURATION_TS_DLINE_PULSE_ENABLE;
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterUsbConfiguration,
- UsbConfiguration);
- //
- // Perform a soft reset of the DWHCI core.
- //
- Status = DwhcipSoftReset(Controller);
- if (!KSUCCESS(Status)) {
- goto InitializeControllerEnd;
- }
- //
- // Initialize the physical layer.
- //
- Status = DwhcipInitializePhy(Controller);
- if (!KSUCCESS(Status)) {
- goto InitializeControllerEnd;
- }
- //
- // The host controller driver currently only supports internal DMA modes.
- //
- Hardware2 = DWHCI_READ_REGISTER(Controller, DwhciRegisterHardware2);
- if ((Hardware2 & DWHCI_HARDWARE2_ARCHITECTURE_MASK) !=
- DWHCI_HARDWARE2_ARCHITECTURE_INTERNAL_DMA) {
- Status = STATUS_NOT_SUPPORTED;
- goto InitializeControllerEnd;
- }
- //
- // The controller currently only supports non-descriptor DMA mode.
- //
- Hardware4 = DWHCI_READ_REGISTER(Controller, DwhciRegisterHardware4);
- if ((Hardware4 & DWHCI_HARDWARE4_DMA_DESCRIPTOR_MODE) != 0) {
- HostConfiguration = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterHostConfiguration);
- HostConfiguration &= ~DWHCI_HOST_CONFIGURATION_ENABLE_DMA_DESCRIPTOR;
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterHostConfiguration,
- HostConfiguration);
- }
- //
- // Enable DMA mode.
- //
- AhbConfiguration = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterAhbConfiguration);
- AhbConfiguration |= DWHCI_AHB_CONFIGURATION_DMA_ENABLE;
- AhbConfiguration &= ~DWHCI_AHB_CONFIGURATION_DMA_REMAINDER_MODE_MASK;
- AhbConfiguration |= DWHCI_AHB_CONFIGURATION_DMA_REMAINDER_MODE_INCREMENTAL;
- AhbConfiguration |= BurstLength;
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterAhbConfiguration,
- AhbConfiguration);
- //
- // Perform the necessary steps to initialize the USB configuration.
- //
- Status = DwhcipInitializeUsb(Controller, UsbCapabilities);
- if (!KSUCCESS(Status)) {
- goto InitializeControllerEnd;
- }
- //
- // The DWHCI can operate in one of two modes. Host mode or device mode.
- // Configure the controller to run in host mode.
- //
- Status = DwhcipInitializeHostMode(Controller,
- ReceiveFifoSize,
- NonPeriodicTransmitFifoSize,
- PeriodicTransmitFifoSize);
- if (!KSUCCESS(Status)) {
- goto InitializeControllerEnd;
- }
- //
- // Enable interrupts for the core and channels. Do not enable global
- // interrupts until the interrupt handle is initialized.
- //
- DWHCI_WRITE_REGISTER(Controller, DwhciRegisterOtgInterrupt, 0xFFFFFFFF);
- DWHCI_WRITE_REGISTER(Controller, DwhciRegisterCoreInterrupt, 0xFFFFFFFF);
- CoreInterruptMask = DWHCI_INITIAL_CORE_INTERRUPT_MASK;
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterCoreInterruptMask,
- CoreInterruptMask);
- //
- // Re-enable the global interrupts.
- //
- AhbConfiguration = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterAhbConfiguration);
- AhbConfiguration |= DWHCI_AHB_CONFIGURATION_INTERRUPT_ENABLE;
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterAhbConfiguration,
- AhbConfiguration);
- InitializeControllerEnd:
- return Status;
- }
- INTERRUPT_STATUS
- DwhcipInterruptService (
- PVOID Context
- )
- /*++
- Routine Description:
- This routine implements the DWHCI interrupt service routine.
- Arguments:
- Context - Supplies the context pointer given to the system when the
- interrupt was connected. In this case, this points to the DWHCI
- controller.
- Return Value:
- Interrupt status.
- --*/
- {
- PDWHCI_CONTROLLER Controller;
- ULONG FrameNumber;
- ULONG Interrupts;
- ULONG InterruptsMask;
- INTERRUPT_STATUS InterruptStatus;
- ULONG OriginalInterrupts;
- ULONG OriginalPendingInterrupts;
- ULONG PortInterrupts;
- Controller = (PDWHCI_CONTROLLER)Context;
- InterruptStatus = InterruptStatusNotClaimed;
- //
- // Read the interrupt register. If there are interesting interrupts, then
- // handle them.
- //
- Interrupts = DWHCI_READ_REGISTER(Controller, DwhciRegisterCoreInterrupt);
- InterruptsMask = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterCoreInterruptMask);
- Interrupts &= InterruptsMask;
- if (Interrupts != 0) {
- OriginalInterrupts = Interrupts;
- PortInterrupts = 0;
- InterruptStatus = InterruptStatusClaimed;
- KeAcquireSpinLock(&(Controller->InterruptLock));
- //
- // In order to clear the core host port interrupt, the host port
- // interrupt status must read and cleared.
- //
- if ((Interrupts & DWHCI_CORE_INTERRUPT_PORT) != 0) {
- PortInterrupts = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterHostPort);
- //
- // If none of the change bits are set, then ignore this host port
- // interrupt.
- //
- if ((PortInterrupts & DWHCI_HOST_PORT_INTERRUPT_MASK) == 0) {
- Interrupts &= ~DWHCI_CORE_INTERRUPT_PORT;
- PortInterrupts = 0;
- } else {
- PortInterrupts = (PortInterrupts &
- ~DWHCI_HOST_PORT_WRITE_TO_CLEAR_MASK) |
- (PortInterrupts &
- DWHCI_HOST_PORT_INTERRUPT_MASK);
- }
- }
- //
- // If there is a channel interrupt, then each channel's interrupt bits
- // needs to be saved and cleared in order to clear the core interrupt.
- //
- if ((Interrupts & DWHCI_CORE_INTERRUPT_HOST_CHANNEL) != 0) {
- DwhcipSaveChannelInterrupts(Controller);
- }
- //
- // On start of frame interrupts, check the current frame against the
- // next targeted start of frame. If it is less, then skip this start of
- // frame interrupt.
- //
- if ((Interrupts & DWHCI_CORE_INTERRUPT_START_OF_FRAME) != 0) {
- FrameNumber = DWHCI_READ_FRAME_NUMBER(Controller);
- if ((Controller->NextFrame == DWHCI_INVALID_FRAME) ||
- DWHCI_FRAME_LESS_THAN(FrameNumber, Controller->NextFrame)) {
- Interrupts &= ~DWHCI_CORE_INTERRUPT_START_OF_FRAME;
- }
- }
- //
- // If there were no pending interrupts to begin with and there are
- // interrupts left to process, then a DPC needs to be queued to process
- // these interrupts.
- //
- OriginalPendingInterrupts = Controller->PendingInterruptBits;
- Controller->PendingInterruptBits |= Interrupts;
- if ((OriginalPendingInterrupts == 0) && (Interrupts != 0)) {
- KeQueueDpc(Controller->InterruptDpc);
- }
- //
- // The host port register needs to be cleared of any change bits in
- // order to remove the core host port interrupt.
- //
- if (PortInterrupts != 0) {
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterHostPort,
- PortInterrupts);
- }
- //
- // Clear the bits in the core interrupt register to acknowledge the
- // interrupts.
- //
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterCoreInterrupt,
- OriginalInterrupts);
- KeReleaseSpinLock(&(Controller->InterruptLock));
- }
- return InterruptStatus;
- }
- VOID
- DwhcipSetInterruptHandle (
- PDWHCI_CONTROLLER Controller,
- HANDLE InterruptHandle
- )
- /*++
- Routine Description:
- This routine saves the handle of the connected interrupt in the DWHCI
- controller.
- Arguments:
- Controller - Supplies a pointer to the DWHCI controller state.
- InterruptHandle - Supplies the connected interrupt handle.
- Return Value:
- None.
- --*/
- {
- Controller->InterruptHandle = InterruptHandle;
- return;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- KSTATUS
- DwhcipCreateEndpoint (
- PVOID HostControllerContext,
- PUSB_HOST_ENDPOINT_CREATION_REQUEST Endpoint,
- PVOID *EndpointContext
- )
- /*++
- Routine Description:
- This routine is called by the USB core when a new endpoint is being opened.
- It allows the host controller to create and store any context needed to
- support a new endpoint (such as a queue head).
- Arguments:
- HostControllerContext - Supplies the context pointer passed to the USB core
- when the controller was created. This is used to identify the USB host
- controller to the host controller driver.
- Endpoint - Supplies a pointer containing information about the endpoint
- being created. The host controller cannot count on this buffer sticking
- around after the function returns. If it needs this information it
- should make a copy of it.
- EndpointContext - Supplies a pointer where the host controller can store a
- context pointer identifying the endpoint created.
- Return Value:
- STATUS_SUCCESS if the endpoint can be successfully accommodated.
- Failing status code if the endpoint cannot be opened.
- --*/
- {
- ULONG ChannelControl;
- PDWHCI_CONTROLLER Controller;
- ULONG HubAddress;
- PDWHCI_ENDPOINT NewEndpoint;
- ULONG PortAddress;
- KSTATUS Status;
- Controller = (PDWHCI_CONTROLLER)HostControllerContext;
- NewEndpoint = MmAllocateNonPagedPool(sizeof(DWHCI_ENDPOINT),
- DWHCI_ALLOCATION_TAG);
- if (NewEndpoint == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateEndpointEnd;
- }
- RtlZeroMemory(NewEndpoint, sizeof(DWHCI_ENDPOINT));
- INITIALIZE_LIST_HEAD(&(NewEndpoint->TransferSetListHead));
- NewEndpoint->TransferType = Endpoint->Type;
- //
- // The endpoint speed better be appropriate for the controller.
- //
- ASSERT((Controller->Speed != UsbDeviceSpeedHigh) ||
- ((Endpoint->Speed == UsbDeviceSpeedLow) ||
- (Endpoint->Speed == UsbDeviceSpeedFull) ||
- (Endpoint->Speed == UsbDeviceSpeedHigh)));
- ASSERT((Controller->Speed != UsbDeviceSpeedFull) ||
- ((Endpoint->Speed == UsbDeviceSpeedLow) ||
- (Endpoint->Speed == UsbDeviceSpeedFull)));
- NewEndpoint->Speed = Endpoint->Speed;
- NewEndpoint->DataToggle = DWHCI_PID_CODE_DATA_0;
- NewEndpoint->PollRate = Endpoint->PollRate;
- ASSERT(Endpoint->MaxPacketSize != 0);
- //
- // If the endpoint is a full or low speed endpoint, then the poll rate is
- // in milliseconds. But if the controller is high speed, it operates in 125
- // microsecond frames. Do the conversion - multiply by 8.
- //
- if ((Controller->Speed == UsbDeviceSpeedHigh) &&
- ((NewEndpoint->Speed == UsbDeviceSpeedLow) ||
- (NewEndpoint->Speed == UsbDeviceSpeedFull))) {
- NewEndpoint->PollRate <<= DWHCI_MICROFRAMES_PER_FRAME_SHIFT;
- NewEndpoint->PollRate &= DWHCI_FRAME_NUMBER_MAX;
- }
- //
- // If this is a high speed bulk OUT endpoint, then always start with the
- // PING protocol.
- //
- NewEndpoint->PingRequired = FALSE;
- if ((Endpoint->Type == UsbTransferTypeBulk) &&
- (Endpoint->Speed == UsbDeviceSpeedHigh) &&
- (Endpoint->Direction == UsbTransferDirectionOut)) {
- NewEndpoint->PingRequired = TRUE;
- }
- //
- // If this is a low or full speed endpoint on a high speed controller, then
- // initialize the split control with the hub port and hub address.
- //
- ASSERT(NewEndpoint->SplitControl == 0);
- if ((Controller->Speed == UsbDeviceSpeedHigh) &&
- (Endpoint->HubAddress != 0) &&
- ((NewEndpoint->Speed == UsbDeviceSpeedLow) ||
- (NewEndpoint->Speed == UsbDeviceSpeedFull))) {
- ASSERT(Endpoint->HubPortNumber != 0);
- PortAddress = (Endpoint->HubPortNumber <<
- DWHCI_CHANNEL_SPLIT_CONTROL_PORT_ADDRESS_SHIFT) &
- DWHCI_CHANNEL_SPLIT_CONTROL_PORT_ADDRESS_MASK;
- HubAddress = (Endpoint->HubAddress <<
- DWHCI_CHANNEL_SPLIT_CONTROL_HUB_ADDRESS_SHIFT) &
- DWHCI_CHANNEL_SPLIT_CONTROL_HUB_ADDRESS_MASK;
- NewEndpoint->SplitControl = PortAddress | HubAddress;
- //
- // TODO: The isochronous split schedule should be more precise.
- //
- NewEndpoint->SplitControl |= DWHCI_CHANNEL_SPLIT_CONTROL_POSITION_ALL;
- NewEndpoint->SplitControl |= DWHCI_CHANNEL_SPLIT_CONTROL_ENABLE;
- }
- NewEndpoint->MaxPacketSize = Endpoint->MaxPacketSize;
- NewEndpoint->EndpointNumber = Endpoint->EndpointNumber;
- //
- // Save the maximum number of packets that can be sent over this endpoint
- // in a single transfer and the maximum size of each transfer.
- //
- NewEndpoint->MaxPacketCount = Controller->MaxTransferSize /
- NewEndpoint->MaxPacketSize;
- if (NewEndpoint->MaxPacketCount > Controller->MaxPacketCount) {
- NewEndpoint->MaxPacketCount = Controller->MaxPacketCount;
- }
- NewEndpoint->MaxTransferSize = NewEndpoint->MaxPacketCount *
- NewEndpoint->MaxPacketSize;
- ASSERT(NewEndpoint->MaxPacketCount <= DWHCI_MAX_PACKET_COUNT);
- ASSERT(NewEndpoint->MaxTransferSize <= DWHCI_MAX_TRANSFER_SIZE);
- //
- // High-bandwidth multiple count packets are not supported.
- //
- ASSERT((NewEndpoint->MaxPacketSize &
- ~DWHCI_CHANNEL_CONTROL_MAX_PACKET_SIZE_MASK) == 0);
- //
- // Initialize the endpoints's channel control.
- //
- ChannelControl = (NewEndpoint->EndpointNumber <<
- DWHCI_CHANNEL_CONTROL_ENDPOINT_SHIFT) &
- DWHCI_CHANNEL_CONTROL_ENDPOINT_MASK;
- ChannelControl |= (NewEndpoint->MaxPacketSize <<
- DWHCI_CHANNEL_CONTROL_MAX_PACKET_SIZE_SHIFT) &
- DWHCI_CHANNEL_CONTROL_MAX_PACKET_SIZE_MASK;
- switch (NewEndpoint->TransferType) {
- case UsbTransferTypeControl:
- ChannelControl |= DWHCI_CHANNEL_CONTROL_ENDPOINT_CONTROL;
- break;
- case UsbTransferTypeInterrupt:
- ChannelControl |= DWHCI_CHANNEL_CONTROL_ENDPOINT_INTERRUPT;
- break;
- case UsbTransferTypeBulk:
- ChannelControl |= DWHCI_CHANNEL_CONTROL_ENDPOINT_BULK;
- break;
- case UsbTransferTypeIsochronous:
- ChannelControl |= DWHCI_CHANNEL_CONTROL_ENDPOINT_ISOCHRONOUS;
- break;
- default:
- ASSERT(FALSE);
- break;
- }
- if (NewEndpoint->Speed == UsbDeviceSpeedLow) {
- ChannelControl |= DWHCI_CHANNEL_CONTROL_LOW_SPEED;
- }
- ChannelControl |= (0x1 << DWHCI_CHANNEL_CONTROL_PACKETS_PER_FRAME_SHIFT) &
- DWHCI_CHANNEL_CONTROL_PACKETS_PER_FRAME_MASK;
- ChannelControl |= DWHCI_CHANNEL_CONTROL_ENABLE;
- ASSERT((ChannelControl & DWHCI_CHANNEL_CONTROL_DISABLE) == 0);
- NewEndpoint->ChannelControl = ChannelControl;
- Status = STATUS_SUCCESS;
- CreateEndpointEnd:
- if (!KSUCCESS(Status)) {
- if (NewEndpoint != NULL) {
- MmFreeNonPagedPool(NewEndpoint);
- NewEndpoint = NULL;
- }
- }
- *EndpointContext = NewEndpoint;
- return Status;
- }
- VOID
- DwhcipResetEndpoint (
- PVOID HostControllerContext,
- PVOID EndpointContext,
- ULONG MaxPacketSize
- )
- /*++
- Routine Description:
- This routine is called by the USB core when an endpoint needs to be reset.
- Arguments:
- HostControllerContext - Supplies the context pointer passed to the USB core
- when the controller was created. This is used to identify the USB host
- controller to the host controller driver.
- EndpointContext - Supplies a pointer to the context returned by the host
- controller when the endpoint was created.
- MaxPacketSize - Supplies the maximum transfer size of the endpoint.
- Return Value:
- None.
- --*/
- {
- ULONG ChannelControl;
- PDWHCI_CONTROLLER Controller;
- PDWHCI_ENDPOINT Endpoint;
- Endpoint = (PDWHCI_ENDPOINT)EndpointContext;
- Endpoint->DataToggle = DWHCI_PID_CODE_DATA_0;
- Controller = (PDWHCI_CONTROLLER)HostControllerContext;
- if (MaxPacketSize != Endpoint->MaxPacketSize) {
- Endpoint->MaxPacketSize = MaxPacketSize;
- ChannelControl = Endpoint->ChannelControl &
- ~DWHCI_CHANNEL_CONTROL_MAX_PACKET_SIZE_MASK;
- ChannelControl |= (Endpoint->MaxPacketSize <<
- DWHCI_CHANNEL_CONTROL_MAX_PACKET_SIZE_SHIFT) &
- DWHCI_CHANNEL_CONTROL_MAX_PACKET_SIZE_MASK;
- Endpoint->ChannelControl = ChannelControl;
- Endpoint->MaxPacketCount = Controller->MaxTransferSize / MaxPacketSize;
- if (Endpoint->MaxPacketCount > Controller->MaxPacketCount) {
- Endpoint->MaxPacketCount = Controller->MaxPacketCount;
- }
- Endpoint->MaxTransferSize = Endpoint->MaxPacketCount * MaxPacketSize;
- }
- return;
- }
- VOID
- DwhcipDestroyEndpoint (
- PVOID HostControllerContext,
- PVOID EndpointContext
- )
- /*++
- Routine Description:
- This routine tears down and destroys an endpoint created with the endpoint
- creation routine.
- Arguments:
- HostControllerContext - Supplies the context pointer passed to the USB core
- when the controller was created. This is used to identify the USB host
- controller to the host controller driver.
- EndpointContext - Supplies a pointer to the context returned by the host
- controller when the endpoint was created.
- Return Value:
- None.
- --*/
- {
- PDWHCI_ENDPOINT Endpoint;
- Endpoint = (PDWHCI_ENDPOINT)EndpointContext;
- ASSERT(LIST_EMPTY(&(Endpoint->TransferSetListHead)) != FALSE);
- MmFreeNonPagedPool(Endpoint);
- return;
- }
- KSTATUS
- DwhcipCreateTransfer (
- PVOID HostControllerContext,
- PVOID EndpointContext,
- ULONG MaxBufferSize,
- ULONG Flags,
- PVOID *TransferContext
- )
- /*++
- Routine Description:
- This routine allocates structures needed for the USB host controller to
- support a transfer.
- Arguments:
- HostControllerContext - Supplies the context pointer passed to the USB core
- when the controller was created. This is used to identify the USB host
- controller to the host controller driver.
- EndpointContext - Supplies a pointer to the host controller's context of
- the endpoint that this transfer will eventually be submitted to.
- MaxBufferSize - Supplies the maximum buffer length, in bytes, of the
- transfer when it is submitted. It is assumed that the host controller
- will set up as many transfer descriptors as are needed to support a
- transfer of this size.
- Flags - Supplies a bitfield of flags regarding the transaction. See
- USB_TRANSFER_FLAG_* definitions.
- TransferContext - Supplies a pointer where the host controller can store a
- context pointer containing any needed structures for the transfer.
- Return Value:
- None.
- --*/
- {
- ULONG AllocationSize;
- PDWHCI_CONTROLLER Controller;
- PDWHCI_ENDPOINT Endpoint;
- BOOL ForceShortTransfer;
- KSTATUS Status;
- PDWHCI_TRANSFER Transfer;
- PDWHCI_TRANSFER *TransferArray;
- ULONG TransferCount;
- ULONG TransferIndex;
- PDWHCI_TRANSFER_SET TransferSet;
- ASSERT(TransferContext != NULL);
- Controller = (PDWHCI_CONTROLLER)HostControllerContext;
- Endpoint = (PDWHCI_ENDPOINT)EndpointContext;
- TransferArray = NULL;
- ForceShortTransfer = FALSE;
- if ((Flags & USB_TRANSFER_FLAG_FORCE_SHORT_TRANSFER) != 0) {
- ForceShortTransfer = TRUE;
- }
- //
- // Figure out the number of transfers needed. The first 8 bytes of a
- // control transfer (the setup packet) are always on their own. Control
- // transfers also have a status stage at the end.
- //
- TransferCount = 0;
- if (Endpoint->TransferType == UsbTransferTypeControl) {
- ASSERT(MaxBufferSize >= sizeof(USB_SETUP_PACKET));
- MaxBufferSize -= sizeof(USB_SETUP_PACKET);
- //
- // Account for both the setup and status stage here.
- //
- TransferCount += 2;
- }
- //
- // Try to fit as many packets into each transfer as possible. Low speed
- // endpoints on high speed controllers requiring split transfers can only
- // execute one max packet size per transfer.
- //
- if (MaxBufferSize != 0) {
- if (Endpoint->SplitControl == 0) {
- TransferCount += MaxBufferSize / Endpoint->MaxTransferSize;
- if ((MaxBufferSize % Endpoint->MaxTransferSize) != 0) {
- TransferCount += 1;
- }
- } else {
- TransferCount += MaxBufferSize / Endpoint->MaxPacketSize;
- if ((MaxBufferSize % Endpoint->MaxPacketSize) != 0) {
- TransferCount += 1;
- }
- }
- //
- // If this transfer needs to indicate completion with a short packet,
- // make sure another transfer is available. This is only necessary if
- // the last packet might not be a short packet. Unfortunately the
- // terminating zero length packet cannot be added to the end of a
- // multi-packet transfer, so it needs its own.
- //
- if ((ForceShortTransfer != FALSE) &&
- (MaxBufferSize >= Endpoint->MaxPacketSize)) {
- TransferCount += 1;
- }
- //
- // Account for a USB transfer that will only send zero length packets and
- // for control transfers that need to force a zero length packet in the
- // data phase.
- //
- } else if ((ForceShortTransfer != FALSE) ||
- (Endpoint->TransferType != UsbTransferTypeControl)) {
- TransferCount += 1;
- }
- //
- // Allocate the transfer set structure.
- //
- AllocationSize = sizeof(DWHCI_TRANSFER_SET);
- if (TransferCount > 1) {
- AllocationSize += sizeof(PDWHCI_TRANSFER) * (TransferCount - 1);
- }
- TransferSet = MmAllocateNonPagedPool(AllocationSize, DWHCI_ALLOCATION_TAG);
- if (TransferSet == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateTransferEnd;
- }
- RtlZeroMemory(TransferSet, AllocationSize);
- INITIALIZE_LIST_HEAD(&(TransferSet->TransferListHead));
- TransferSet->TransferCount = TransferCount;
- TransferSet->Endpoint = Endpoint;
- TransferArray = (PDWHCI_TRANSFER *)(TransferSet->Transfer);
- //
- // Create the new transfers.
- //
- for (TransferIndex = 0; TransferIndex < TransferCount; TransferIndex += 1) {
- Transfer = MmAllocateBlock(Controller->BlockAllocator, NULL);
- if (Transfer == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateTransferEnd;
- }
- RtlZeroMemory(Transfer, sizeof(DWHCI_TRANSFER));
- TransferArray[TransferIndex] = Transfer;
- }
- Status = STATUS_SUCCESS;
- CreateTransferEnd:
- if (!KSUCCESS(Status)) {
- if (TransferSet != NULL) {
- for (TransferIndex = 0;
- TransferIndex < TransferCount;
- TransferIndex += 1) {
- Transfer = TransferArray[TransferIndex];
- if (Transfer != NULL) {
- MmFreeBlock(Controller->BlockAllocator, Transfer);
- }
- }
- MmFreeNonPagedPool(TransferSet);
- TransferSet = NULL;
- }
- }
- *TransferContext = TransferSet;
- return Status;
- }
- VOID
- DwhcipDestroyTransfer (
- PVOID HostControllerContext,
- PVOID EndpointContext,
- PVOID TransferContext
- )
- /*++
- Routine Description:
- This routine destroys host controller structures associated with a USB
- transfer.
- Arguments:
- HostControllerContext - Supplies the context pointer passed to the USB core
- when the controller was created. This is used to identify the USB host
- controller to the host controller driver.
- EndpointContext - Supplies a pointer to the host controller context for the
- endpoint this transfer belonged to.
- TransferContext - Supplies the pointer provided to the USB core by the host
- controller when the transfer was created.
- Return Value:
- None.
- --*/
- {
- PDWHCI_CONTROLLER Controller;
- PDWHCI_TRANSFER Transfer;
- PDWHCI_TRANSFER *TransferArray;
- ULONG TransferIndex;
- PDWHCI_TRANSFER_SET TransferSet;
- Controller = (PDWHCI_CONTROLLER)HostControllerContext;
- TransferSet = (PDWHCI_TRANSFER_SET)TransferContext;
- TransferArray = (PDWHCI_TRANSFER *)(TransferSet->Transfer);
- //
- // Free all transfers that were allocated.
- //
- for (TransferIndex = 0;
- TransferIndex < TransferSet->TransferCount;
- TransferIndex += 1) {
- Transfer = TransferArray[TransferIndex];
- ASSERT(Transfer != NULL);
- MmFreeBlock(Controller->BlockAllocator, Transfer);
- TransferArray[TransferIndex] = NULL;
- }
- MmFreeNonPagedPool(TransferSet);
- return;
- }
- KSTATUS
- DwhcipSubmitTransfer (
- PVOID HostControllerContext,
- PVOID EndpointContext,
- PUSB_TRANSFER_INTERNAL Transfer,
- PVOID TransferContext
- )
- /*++
- Routine Description:
- This routine submits a transfer to the USB host controller for execution.
- Arguments:
- HostControllerContext - Supplies the context pointer passed to the USB core
- when the controller was created. This is used to identify the USB host
- controller to the host controller driver.
- EndpointContext - Supplies the context pointer provided to the USB core by
- the host controller when the endpoint was created.
- Transfer - Supplies a pointer to the USB transfer to execute.
- TransferContext - Supplies the pointer provided to the USB core by the host
- controller when the transfer was created.
- Return Value:
- STATUS_SUCCESS if the transfer was successfully added to the hardware queue.
- Failure codes if the transfer could not be added.
- --*/
- {
- ULONG ChannelControl;
- PDWHCI_CONTROLLER Controller;
- BOOL ControlTransfer;
- ULONG CoreInterruptMask;
- PDWHCI_TRANSFER DwhciTransfer;
- PDWHCI_ENDPOINT Endpoint;
- UCHAR EndpointDeviceAddress;
- BOOL ForceShortTransfer;
- ULONG FrameNumber;
- ULONG FrameOffset;
- BOOL LastTransfer;
- ULONG Length;
- ULONG MaxTransferSize;
- ULONG NextFrame;
- ULONG Offset;
- RUNLEVEL OldRunLevel;
- ULONG TotalLength;
- PDWHCI_TRANSFER *TransferArray;
- ULONG TransferCount;
- ULONG TransferIndex;
- PDWHCI_TRANSFER_SET TransferSet;
- Controller = (PDWHCI_CONTROLLER)HostControllerContext;
- ControlTransfer = FALSE;
- Endpoint = (PDWHCI_ENDPOINT)EndpointContext;
- TransferSet = (PDWHCI_TRANSFER_SET)TransferContext;
- TransferArray = (PDWHCI_TRANSFER *)(TransferSet->Transfer);
- DwhciTransfer = NULL;
- //
- // Assume that this is going to be a rousing success.
- //
- Transfer->Public.Status = STATUS_SUCCESS;
- Transfer->Public.Error = UsbErrorNone;
- TransferSet->UsbTransfer = Transfer;
- //
- // Before filling out and inserting transfers, take a look to see if the
- // device address has changed. If it has, then it should still be in the
- // enumeration phase, meaning there are no pending transfers floating
- // around.
- //
- EndpointDeviceAddress = (Endpoint->ChannelControl &
- DWHCI_CHANNEL_CONTROL_DEVICE_ADDRESS_MASK) >>
- DWHCI_CHANNEL_CONTROL_DEVICE_ADDRESS_SHIFT;
- if (Transfer->DeviceAddress != EndpointDeviceAddress) {
- ASSERT(EndpointDeviceAddress == 0);
- ASSERT(Transfer->DeviceAddress != 0);
- ASSERT(LIST_EMPTY(&(Endpoint->TransferSetListHead)) != FALSE);
- ChannelControl = (Transfer->DeviceAddress <<
- DWHCI_CHANNEL_CONTROL_DEVICE_ADDRESS_SHIFT) &
- DWHCI_CHANNEL_CONTROL_DEVICE_ADDRESS_MASK;
- Endpoint->ChannelControl |= ChannelControl;
- }
- //
- // Determine the number of transfers needed for this transfer, and loop
- // filling them out. This is necessary because the number of transfers
- // per transfer is not constant; the system may re-use a transfer and
- // and change the length.
- //
- TransferCount = 0;
- TotalLength = Transfer->Public.Length;
- if (Endpoint->TransferType == UsbTransferTypeControl) {
- ControlTransfer = TRUE;
- ASSERT(TotalLength >= sizeof(USB_SETUP_PACKET));
- TotalLength -= sizeof(USB_SETUP_PACKET);
- //
- // Account for both the setup and status transfers.
- //
- TransferCount += 2;
- }
- ForceShortTransfer = FALSE;
- if ((Transfer->Public.Flags &
- USB_TRANSFER_FLAG_FORCE_SHORT_TRANSFER) != 0) {
- ForceShortTransfer = TRUE;
- }
- //
- // Determine the number of transfers in this set. Low speed endpoints on
- // high speed controllers requiring split transfers can only execute one
- // max packet size per transfer.
- //
- if (Endpoint->SplitControl == 0) {
- TransferCount += TotalLength / Endpoint->MaxTransferSize;
- if ((TotalLength % Endpoint->MaxTransferSize) != 0) {
- TransferCount += 1;
- }
- MaxTransferSize = Endpoint->MaxTransferSize;
- } else {
- TransferCount += TotalLength / Endpoint->MaxPacketSize;
- if ((TotalLength % Endpoint->MaxPacketSize) != 0) {
- TransferCount += 1;
- }
- MaxTransferSize = Endpoint->MaxPacketSize;
- }
- //
- // Add an extra transfer if it is needed for more data or to force a short
- // transfer. Make sure this accounts for non-control zero-length requests.
- //
- if (((ForceShortTransfer != FALSE) &&
- ((TotalLength % Endpoint->MaxPacketSize) == 0)) ||
- ((TotalLength == 0) &&
- (Endpoint->TransferType != UsbTransferTypeControl)) ) {
- TransferCount += 1;
- }
- ASSERT(TransferSet->TransferCount >= TransferCount);
- //
- // Initialize the DWHCI transfers required for this USB transfer and add
- // them to the transfer set's list head.
- //
- Offset = 0;
- LastTransfer = FALSE;
- INITIALIZE_LIST_HEAD(&(TransferSet->TransferListHead));
- for (TransferIndex = 0; TransferIndex < TransferCount; TransferIndex += 1) {
- //
- // Calculate the length for this transfer descriptor.
- //
- Length = MaxTransferSize;
- if (Offset + Length > Transfer->Public.Length) {
- Length = Transfer->Public.Length - Offset;
- }
- if (TransferIndex == (TransferCount - 1)) {
- LastTransfer = TRUE;
- }
- if (ControlTransfer != FALSE) {
- //
- // The first part of a control transfer is the setup packet, which
- // is always 8 bytes long.
- //
- if (Offset == 0) {
- Length = sizeof(USB_SETUP_PACKET);
- }
- //
- // The last part of a control transfer is the status phase and the
- // length better be zero.
- //
- ASSERT((LastTransfer == FALSE) || (Length == 0));
- }
- ASSERT((Length != 0) ||
- (LastTransfer != FALSE) ||
- ((ForceShortTransfer != FALSE) && (ControlTransfer != FALSE)));
- //
- // Fill out this transfer descriptor.
- //
- DwhciTransfer = TransferArray[TransferIndex];
- DwhcipFillOutTransferDescriptor(Controller,
- TransferSet,
- DwhciTransfer,
- Offset,
- Length,
- LastTransfer);
- //
- // Advance the buffer position.
- //
- Offset += Length;
- }
- //
- // Mark the current transfer as the last transfer.
- //
- DwhciTransfer->LastTransfer = TRUE;
- //
- // The controller lock is required for endpoint updates and schedule
- // processing.
- //
- OldRunLevel = DwhcipAcquireControllerLock(Controller);
- //
- // The transfer set is ready to go. Insert it into the endpoint's list of
- // transfer sets.
- //
- INSERT_BEFORE(&(TransferSet->EndpointListEntry),
- &(Endpoint->TransferSetListHead));
- //
- // If the endpoint is not already inserted into the schedule, insert it.
- //
- if (Endpoint->ListEntry.Next == NULL) {
- ASSERT(Endpoint->Scheduled == FALSE);
- if ((Endpoint->TransferType == UsbTransferTypeControl) ||
- (Endpoint->TransferType == UsbTransferTypeBulk)) {
- INSERT_BEFORE(&(Endpoint->ListEntry),
- &(Controller->NonPeriodicReadyListHead));
- //
- // There is now work on the non-periodic schedule that needs to be
- // done. Try to schedule it.
- //
- DwhcipProcessSchedule(Controller, FALSE);
- } else {
- ASSERT((Endpoint->TransferType == UsbTransferTypeInterrupt) ||
- (Endpoint->TransferType == UsbTransferTypeIsochronous));
- //
- // Schedule this endpoint for a (micro)frame shortly in the future
- // to kick it off.
- //
- FrameNumber = DWHCI_READ_FRAME_NUMBER(Controller);
- ASSERT(Endpoint->NextFrame == 0);
- //
- // Schedule for a future (micro)frame, but not further than the
- // poll rate.
- //
- FrameOffset = DWHCI_DEFAULT_FRAME_OFFSET;
- if (FrameOffset > Endpoint->PollRate) {
- FrameOffset = Endpoint->PollRate;
- }
- NextFrame = (FrameNumber + FrameOffset) & DWHCI_FRAME_NUMBER_MAX;
- //
- // Start splits are not allowed to start in the 6th microframe and
- // get less time for the complete splits the later they get
- // scheduled within a frame. Schedule them all for the last
- // microframe.
- //
- if ((Endpoint->SplitControl != 0) &&
- (Endpoint->TransferType == UsbTransferTypeInterrupt)) {
- NextFrame |= DWHCI_INTERRUPT_SPLIT_FRAME_MASK;
- }
- if ((Controller->NextFrame == DWHCI_INVALID_FRAME) ||
- DWHCI_FRAME_LESS_THAN(NextFrame, Controller->NextFrame)) {
- Controller->NextFrame = NextFrame;
- }
- Endpoint->NextFrame = NextFrame;
- //
- // These transfer need to wait for the start of the appropriate
- // (micro)frame. Activate the start-of-frame interrupt if the
- // periodic inactive list is currently empty.
- //
- if (LIST_EMPTY(&(Controller->PeriodicInactiveListHead)) != FALSE) {
- CoreInterruptMask = DWHCI_READ_REGISTER(
- Controller,
- DwhciRegisterCoreInterruptMask);
- CoreInterruptMask |= DWHCI_CORE_INTERRUPT_START_OF_FRAME;
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterCoreInterruptMask,
- CoreInterruptMask);
- }
- INSERT_BEFORE(&(Endpoint->ListEntry),
- &(Controller->PeriodicInactiveListHead));
- }
- }
- //
- // All done. Release the lock and return.
- //
- DwhcipReleaseControllerLock(Controller, OldRunLevel);
- return STATUS_SUCCESS;
- }
- KSTATUS
- DwhcipCancelTransfer (
- PVOID HostControllerContext,
- PVOID EndpointContext,
- PUSB_TRANSFER_INTERNAL Transfer,
- PVOID TransferContext
- )
- /*++
- Routine Description:
- This routine submits attempts to cancel a transfer that was previously
- submitted for execution.
- Arguments:
- HostControllerContext - Supplies the context pointer passed to the USB core
- when the controller was created. This is used to identify the USB host
- controller to the host controller driver.
- EndpointContext - Supplies the context pointer provided to the USB core by
- the host controller when the endpoint was created.
- Transfer - Supplies a pointer to the USB transfer to execute.
- TransferContext - Supplies the pointer provided to the USB core by the host
- controller when the transfer was created.
- Return Value:
- STATUS_SUCCESS if the transfer was successfully removed from the hardware
- queue.
- STATUS_TOO_LATE if the transfer had already completed.
- Other failure codes if the transfer could not be cancelled but has not yet
- completed.
- --*/
- {
- PDWHCI_CONTROLLER Controller;
- PDWHCI_ENDPOINT Endpoint;
- BOOL FirstSet;
- PDWHCI_TRANSFER_SET FirstTransferSet;
- BOOL Halted;
- RUNLEVEL OldRunLevel;
- BOOL RemoveSet;
- KSTATUS Status;
- PDWHCI_TRANSFER_SET TransferSet;
- Controller = (PDWHCI_CONTROLLER)HostControllerContext;
- TransferSet = (PDWHCI_TRANSFER_SET)TransferContext;
- ASSERT(TransferSet->UsbTransfer == Transfer);
- //
- // Lock the controller to manipulate the endpoint lists.
- //
- OldRunLevel = DwhcipAcquireControllerLock(Controller);
- //
- // If the transfer set was already taken off its endpoint list, then the
- // transfer has already completed.
- //
- if (TransferSet->EndpointListEntry.Next == NULL) {
- ASSERT(TransferSet->EndpointListEntry.Next == NULL);
- Status = STATUS_TOO_LATE;
- goto CancelTransferEnd;
- }
- //
- // Isochronous transfers are handled differently.
- //
- if (Transfer->Type == UsbTransferTypeIsochronous) {
- //
- // TODO: Implement support for isochronous transfers.
- //
- ASSERT(FALSE);
- Status = STATUS_NOT_IMPLEMENTED;
- goto CancelTransferEnd;
- }
- Endpoint = TransferSet->Endpoint;
- ASSERT(LIST_EMPTY(&(Endpoint->TransferSetListHead)) == FALSE);
- //
- // Only move the endpoint forward if removing the first transfer set.
- //
- FirstTransferSet = LIST_VALUE(Endpoint->TransferSetListHead.Next,
- DWHCI_TRANSFER_SET,
- EndpointListEntry);
- FirstSet = FALSE;
- if (TransferSet == FirstTransferSet) {
- FirstSet = TRUE;
- }
- //
- // Set the error state for the channel. It will either get pulled out of
- // the schedule below or halted, in the case of an active transfer. Once
- // the active transfer halts, it will see why based on this status.
- //
- Transfer->Public.Status = STATUS_OPERATION_CANCELLED;
- Transfer->Public.Error = UsbErrorTransferCancelled;
- //
- // If the transfer set is active on the endpoint, the endpoint has been
- // assigned a channel and the endpoint is actually scheduled on the channel,
- // then halt the channel. Halting a channel is not supported if the root
- // port is not connected. Just remove the transfer set.
- //
- RemoveSet = TRUE;
- if ((Controller->PortConnected != FALSE) &&
- (FirstSet != FALSE) &&
- (Endpoint->Channel != NULL) &&
- (Endpoint->Scheduled != FALSE)) {
- Halted = DwhcipHaltChannel(Controller, Endpoint->Channel);
- if (Halted == FALSE) {
- RemoveSet = FALSE;
- }
- }
- //
- // If the transfer set can be removed because it was not active or the
- // channel was successfully halted, do it. Also complete the transfer and
- // advance the endpoint to the next transfer, if any.
- //
- if (RemoveSet != FALSE) {
- DwhcipRemoveTransferSet(Controller, TransferSet);
- UsbHostProcessCompletedTransfer(Transfer);
- if (FirstSet != FALSE) {
- DwhcipAdvanceEndpoint(Controller, Endpoint);
- DwhcipProcessSchedule(Controller, FALSE);
- }
- Status = STATUS_SUCCESS;
- } else {
- Status = STATUS_TOO_LATE;
- }
- CancelTransferEnd:
- DwhcipReleaseControllerLock(Controller, OldRunLevel);
- return Status;
- }
- KSTATUS
- DwhcipGetRootHubStatus (
- PVOID HostControllerContext,
- PUSB_HUB_STATUS HubStatus
- )
- /*++
- Routine Description:
- This routine queries the host controller for the status of the root hub.
- Arguments:
- HostControllerContext - Supplies the context pointer passed to the USB core
- when the controller was created. This is used to identify the USB host
- controller to the host controller driver.
- HubStatus - Supplies a pointer where the host controller should fill out
- the root hub status.
- Return Value:
- STATUS_SUCCESS if the hub status was successfully queried.
- Failure codes if the status could not be queried.
- --*/
- {
- ULONG AcknowledgeStatus;
- USHORT ChangeBits;
- PDWHCI_CONTROLLER Controller;
- ULONG HardwareStatus;
- PUSB_PORT_STATUS PortStatus;
- USHORT SoftwareStatus;
- Controller = (PDWHCI_CONTROLLER)HostControllerContext;
- ASSERT(Controller->PortCount == DWHCI_HOST_PORT_COUNT);
- ASSERT(HubStatus->PortStatus != NULL);
- HardwareStatus = DWHCI_READ_REGISTER(Controller, DwhciRegisterHostPort);
- SoftwareStatus = 0;
- //
- // Set the software bits that correspond to the queried hardware bits.
- //
- if ((HardwareStatus & DWHCI_HOST_PORT_CONNECT_STATUS) != 0) {
- SoftwareStatus |= USB_PORT_STATUS_CONNECTED;
- switch (HardwareStatus & DWHCI_HOST_PORT_SPEED_MASK) {
- case DWHCI_HOST_PORT_SPEED_LOW:
- HubStatus->PortDeviceSpeed[0] = UsbDeviceSpeedLow;
- break;
- case DWHCI_HOST_PORT_SPEED_FULL:
- HubStatus->PortDeviceSpeed[0] = UsbDeviceSpeedFull;
- break;
- case DWHCI_HOST_PORT_SPEED_HIGH:
- HubStatus->PortDeviceSpeed[0] = UsbDeviceSpeedHigh;
- break;
- default:
- ASSERT(FALSE);
- break;
- }
- Controller->PortConnected = TRUE;
- } else {
- Controller->PortConnected = FALSE;
- }
- if ((HardwareStatus & DWHCI_HOST_PORT_ENABLE) != 0) {
- SoftwareStatus |= USB_PORT_STATUS_ENABLED;
- }
- if ((HardwareStatus & DWHCI_HOST_PORT_RESET) != 0) {
- SoftwareStatus |= USB_PORT_STATUS_RESET;
- }
- if ((HardwareStatus & DWHCI_HOST_PORT_OVER_CURRENT_ACTIVE) != 0) {
- SoftwareStatus |= USB_PORT_STATUS_OVER_CURRENT;
- }
- //
- // If the new software status is different from the current software
- // status, record the change bits and set the new software status.
- //
- PortStatus = &(HubStatus->PortStatus[0]);
- if (SoftwareStatus != PortStatus->Status) {
- ChangeBits = SoftwareStatus ^ PortStatus->Status;
- //
- // Because the change bits correspond with the status bits 1-to-1, just
- // OR in the change bits.
- //
- PortStatus->Change |= ChangeBits;
- PortStatus->Status = SoftwareStatus;
- }
- //
- // Acknowledge the over current change bit if it is set.
- //
- if ((HardwareStatus & DWHCI_HOST_PORT_OVER_CURRENT_CHANGE) != 0) {
- PortStatus->Change |= USB_PORT_STATUS_OVER_CURRENT;
- AcknowledgeStatus = HardwareStatus &
- ~DWHCI_HOST_PORT_WRITE_TO_CLEAR_MASK;
- AcknowledgeStatus |= DWHCI_HOST_PORT_OVER_CURRENT_CHANGE;
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterHostPort,
- AcknowledgeStatus);
- }
- //
- // Acknowledge the port connection status change in the hardware and set
- // the bit in the software's port status change bits. It may be that the
- // port transitioned from connected to connected and the above checks did
- // not pick up the change.
- //
- if ((HardwareStatus & DWHCI_HOST_PORT_CONNECT_STATUS_CHANGE) != 0) {
- PortStatus->Change |= USB_PORT_STATUS_CHANGE_CONNECTED;
- //
- // If the port is not in the middle of a reset, clear the connect
- // status change bit in the hardware by setting it to 1. Resets clear
- // the connect status changed bit.
- //
- if ((HardwareStatus & DWHCI_HOST_PORT_RESET) == 0) {
- AcknowledgeStatus = HardwareStatus &
- ~DWHCI_HOST_PORT_WRITE_TO_CLEAR_MASK;
- AcknowledgeStatus |= DWHCI_HOST_PORT_CONNECT_STATUS_CHANGE;
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterHostPort,
- AcknowledgeStatus);
- }
- }
- if ((DwhciDebugFlags & DWHCI_DEBUG_FLAG_PORTS) != 0) {
- RtlDebugPrint(
- "DWHCI: Controller 0x%x Port %d Status 0x%x. Connected %d, "
- "Enabled %d, Reset %d, Changed %d.\n",
- Controller,
- 0,
- HardwareStatus,
- (HardwareStatus & DWHCI_HOST_PORT_CONNECT_STATUS) != 0,
- (HardwareStatus & DWHCI_HOST_PORT_ENABLE) != 0,
- (HardwareStatus & DWHCI_HOST_PORT_RESET) != 0,
- (HardwareStatus & DWHCI_HOST_PORT_CONNECT_STATUS_CHANGE) != 0);
- }
- return STATUS_SUCCESS;
- }
- KSTATUS
- DwhcipSetRootHubStatus (
- PVOID HostControllerContext,
- PUSB_HUB_STATUS HubStatus
- )
- /*++
- Routine Description:
- This routine sets the state of the root hub in the USB host controller. It
- looks at the status change bits for each port in order to determine what
- needs to be set.
- Arguments:
- HostControllerContext - Supplies the context pointer passed to the USB core
- when the controller was created. This is used to identify the USB host
- controller to the host controller driver.
- HubStatus - Supplies a pointer to the status that should be set in the root
- hub.
- Return Value:
- STATUS_SUCCESS if the hub state was successfully programmed into the device.
- Failure codes if the status could not be set.
- --*/
- {
- PDWHCI_CONTROLLER Controller;
- ULONG HardwareStatus;
- ULONG OriginalHardwareStatus;
- PUSB_PORT_STATUS PortStatus;
- Controller = (PDWHCI_CONTROLLER)HostControllerContext;
- ASSERT(Controller->PortCount == DWHCI_HOST_PORT_COUNT);
- ASSERT(HubStatus->PortStatus != NULL);
- PortStatus = &(HubStatus->PortStatus[0]);
- if (PortStatus->Change == 0) {
- return STATUS_SUCCESS;
- }
- HardwareStatus = DWHCI_READ_REGISTER(Controller, DwhciRegisterHostPort);
- HardwareStatus &= ~DWHCI_HOST_PORT_WRITE_TO_CLEAR_MASK;
- OriginalHardwareStatus = HardwareStatus;
- //
- // Clear out the bits that may potentially be adjusted.
- //
- HardwareStatus &= ~(DWHCI_HOST_PORT_ENABLE |
- DWHCI_HOST_PORT_RESET |
- DWHCI_HOST_PORT_SUSPEND);
- //
- // Set the hardware bits according to the software bits passed in.
- //
- if ((PortStatus->Change & USB_PORT_STATUS_CHANGE_ENABLED) != 0) {
- //
- // If the port is being enabled, power it on.
- //
- if ((PortStatus->Status & USB_PORT_STATUS_ENABLED) != 0) {
- HardwareStatus |= DWHCI_HOST_PORT_POWER;
- //
- // Otherwise set the enable bit to disable.
- //
- } else {
- HardwareStatus |= DWHCI_HOST_PORT_ENABLE;
- }
- //
- // Acknowledge that the enable bit was handled.
- //
- PortStatus->Change &= ~ USB_PORT_STATUS_CHANGE_ENABLED;
- }
- if ((PortStatus->Change & USB_PORT_STATUS_CHANGE_RESET) != 0) {
- if ((PortStatus->Status & USB_PORT_STATUS_RESET) != 0) {
- HardwareStatus |= DWHCI_HOST_PORT_RESET | DWHCI_HOST_PORT_POWER;
- }
- //
- // Acknowledge that the reset bit was handled.
- //
- PortStatus->Change &= ~USB_PORT_STATUS_CHANGE_RESET;
- }
- //
- // Suspend the port if requested.
- //
- if ((PortStatus->Change & USB_PORT_STATUS_CHANGE_SUSPENDED) != 0) {
- if ((PortStatus->Status & USB_PORT_STATUS_SUSPENDED) != 0) {
- HardwareStatus |= DWHCI_HOST_PORT_SUSPEND;
- }
- //
- // Acknowledge that the suspended bit was handled.
- //
- PortStatus->Change &= ~USB_PORT_STATUS_CHANGE_SUSPENDED;
- }
- //
- // Write out the new value if it is different than the old one. If both the
- // enable (i.e. disable) bit and the reset bit are set, disable the port
- // first using the original hardware status.
- //
- if (HardwareStatus != OriginalHardwareStatus) {
- if (((HardwareStatus & DWHCI_HOST_PORT_ENABLE) != 0) &&
- ((HardwareStatus & DWHCI_HOST_PORT_RESET) != 0)) {
- OriginalHardwareStatus |= DWHCI_HOST_PORT_ENABLE;
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterHostPort,
- OriginalHardwareStatus);
- HardwareStatus &= ~DWHCI_HOST_PORT_ENABLE;
- }
- DWHCI_WRITE_REGISTER(Controller, DwhciRegisterHostPort, HardwareStatus);
- }
- //
- // If reset was set, wait a bit and then clear the reset flag.
- //
- if ((HardwareStatus & DWHCI_HOST_PORT_RESET) != 0) {
- KeDelayExecution(FALSE, FALSE, 50 * MICROSECONDS_PER_MILLISECOND);
- HardwareStatus = DWHCI_READ_REGISTER(Controller, DwhciRegisterHostPort);
- HardwareStatus &= ~DWHCI_HOST_PORT_WRITE_TO_CLEAR_MASK;
- HardwareStatus &= ~DWHCI_HOST_PORT_RESET;
- DWHCI_WRITE_REGISTER(Controller, DwhciRegisterHostPort, HardwareStatus);
- }
- return STATUS_SUCCESS;
- }
- RUNLEVEL
- DwhcipAcquireControllerLock (
- PDWHCI_CONTROLLER Controller
- )
- /*++
- Routine Description:
- This routine acquires the given DWHCI controller's lock at dispatch level.
- Arguments:
- Controller - Supplies a pointer to the controller to lock.
- Return Value:
- Returns the previous run-level, which must be passed in when the controller
- is unlocked.
- --*/
- {
- RUNLEVEL OldRunLevel;
- OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
- KeAcquireSpinLock(&(Controller->Lock));
- return OldRunLevel;
- }
- VOID
- DwhcipReleaseControllerLock (
- PDWHCI_CONTROLLER Controller,
- RUNLEVEL OldRunLevel
- )
- /*++
- Routine Description:
- This routine releases the given DWHCI controller's lock, and returns the
- run-level to its previous value.
- Arguments:
- Controller - Supplies a pointer to the controller to unlock.
- OldRunLevel - Supplies the original run level returned when the lock was
- acquired.
- Return Value:
- None.
- --*/
- {
- KeReleaseSpinLock(&(Controller->Lock));
- KeLowerRunLevel(OldRunLevel);
- return;
- }
- VOID
- DwhcipInterruptServiceDpc (
- PDPC Dpc
- )
- /*++
- Routine Description:
- This routine implements the DWHCI DPC that is queued when an interrupt
- fires.
- Arguments:
- Dpc - Supplies a pointer to the DPC that is running.
- Return Value:
- None.
- --*/
- {
- ASSERT(KeGetRunLevel() == RunLevelDispatch);
- DwhcipProcessInterrupt(Dpc->UserData);
- return;
- }
- VOID
- DwhcipProcessInterrupt (
- PVOID Context
- )
- /*++
- Routine Description:
- This routine performs the work associated with receiving an DWHCI
- interrupt. This routine runs at dispatch level.
- Arguments:
- Context - Supplies a pointer to the controller to service.
- Return Value:
- None.
- --*/
- {
- ULONG ChannelInterruptBits[DWHCI_MAX_CHANNELS];
- PDWHCI_CHANNEL Channels;
- PDWHCI_CONTROLLER Controller;
- ULONG Index;
- ULONG InterruptBits;
- RUNLEVEL OldRunLevel;
- Controller = (PDWHCI_CONTROLLER)Context;
- ASSERT(Controller->ChannelCount <= DWHCI_MAX_CHANNELS);
- //
- // Collect the pending interrupt bits and clear them to signal that another
- // DPC will need to be queued for any subsequent interrupts. If the
- // interrupt handle is not yet assigned, just raise to high. This will not
- // result in a priority inversion problem as this code always run at
- // dispatch, and thus cannot pre-empt the interrupt code while it has the
- // lock.
- //
- if (Controller->InterruptHandle == INVALID_HANDLE) {
- OldRunLevel = KeRaiseRunLevel(RunLevelHigh);
- } else {
- OldRunLevel = IoRaiseToInterruptRunLevel(Controller->InterruptHandle);
- }
- KeAcquireSpinLock(&(Controller->InterruptLock));
- InterruptBits = Controller->PendingInterruptBits;
- Controller->PendingInterruptBits = 0;
- //
- // Recording the pending interrupt bits for each channel.
- //
- if ((InterruptBits & DWHCI_CORE_INTERRUPT_HOST_CHANNEL) != 0) {
- Channels = (PDWHCI_CHANNEL)(Controller->Channel);
- for (Index = 0; Index < Controller->ChannelCount; Index += 1) {
- ChannelInterruptBits[Index] = Channels[Index].PendingInterruptBits;
- Channels[Index].PendingInterruptBits = 0;
- }
- }
- KeReleaseSpinLock(&(Controller->InterruptLock));
- KeLowerRunLevel(OldRunLevel);
- //
- // Lock the controller and loop until this routine has caught up with the
- // interrupts.
- //
- OldRunLevel = DwhcipAcquireControllerLock(Controller);
- //
- // If the start-of-frame interrupt fired, then try to schedule some of the
- // periodic transfers.
- //
- if ((InterruptBits & DWHCI_CORE_INTERRUPT_START_OF_FRAME) != 0) {
- DwhcipProcessStartOfFrameInterrupt(Controller);
- }
- //
- // If the port interrupt or the disconnect interrupt fired, then the host
- // port's status changed. Notify the USB core.
- //
- if (((InterruptBits & DWHCI_CORE_INTERRUPT_PORT) != 0) ||
- ((InterruptBits & DWHCI_CORE_INTERRUPT_DISCONNECT) != 0)) {
- UsbHostNotifyPortChange(Controller->UsbCoreHandle);
- }
- //
- // If the host channel interrupt fired, then iterate over the channel
- // interrupt array to determine which channels have work pending.
- //
- if ((InterruptBits & DWHCI_CORE_INTERRUPT_HOST_CHANNEL) != 0) {
- DwhcipProcessChannelInterrupt(Controller, ChannelInterruptBits);
- }
- DwhcipReleaseControllerLock(Controller, OldRunLevel);
- return;
- }
- VOID
- DwhcipProcessStartOfFrameInterrupt (
- PDWHCI_CONTROLLER Controller
- )
- /*++
- Routine Description:
- This routine processes the inactive periodic schedule to see if any
- transfer's period has expired. This routine assumes that the controller's
- lock is held.
- Arguments:
- Controller - Supplies a pointer to the state of the DWHCI controller who
- has reached the start of a frame.
- Return Value:
- None.
- --*/
- {
- ULONG CoreInterruptMask;
- PLIST_ENTRY CurrentEntry;
- PDWHCI_ENDPOINT Endpoint;
- ULONG FrameNumber;
- ULONG NextFrame;
- BOOL ProcessSchedule;
- //
- // The start of frame interrupt could have come in the middle of disabling
- // the interrupt. Check to make sure there is a valid next frame.
- //
- if (Controller->NextFrame == DWHCI_INVALID_FRAME) {
- return;
- }
- //
- // Iterate over the inactive periodic schedule looking for endpoints that
- // have something to submit for the current frame or some frame in the past.
- //
- NextFrame = DWHCI_INVALID_FRAME;
- ProcessSchedule = FALSE;
- FrameNumber = DWHCI_READ_FRAME_NUMBER(Controller);
- CurrentEntry = Controller->PeriodicInactiveListHead.Next;
- while (CurrentEntry != &(Controller->PeriodicInactiveListHead)) {
- Endpoint = LIST_VALUE(CurrentEntry, DWHCI_ENDPOINT, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- //
- // Skip any endpoints whose polling interval has not expired, but do
- // record the next frame.
- //
- if (DWHCI_FRAME_LESS_THAN(FrameNumber, Endpoint->NextFrame)) {
- if ((NextFrame == DWHCI_INVALID_FRAME) ||
- DWHCI_FRAME_LESS_THAN(Endpoint->NextFrame, NextFrame)) {
- NextFrame = Endpoint->NextFrame;
- }
- continue;
- }
- LIST_REMOVE(&(Endpoint->ListEntry));
- INSERT_BEFORE(&(Endpoint->ListEntry),
- &(Controller->PeriodicReadyListHead));
- ProcessSchedule = TRUE;
- }
- //
- // If the inactive list is empty, then disable the start-of-frame interrupt.
- //
- if (LIST_EMPTY(&(Controller->PeriodicInactiveListHead)) != FALSE) {
- CoreInterruptMask = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterCoreInterruptMask);
- CoreInterruptMask &= ~DWHCI_CORE_INTERRUPT_START_OF_FRAME;
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterCoreInterruptMask,
- CoreInterruptMask);
- ASSERT(NextFrame == DWHCI_INVALID_FRAME);
- }
- //
- // Update the controller's next start of frame to process. This is either
- // the smallest frame number out of the inactive periodic transfers or the
- // invalid frame number if there are no more inactive periodic transfers.
- //
- Controller->NextFrame = NextFrame;
- //
- // If something was switch from the inactive to the ready list, then kick
- // off the schedule.
- //
- if (ProcessSchedule != FALSE) {
- DwhcipProcessSchedule(Controller, TRUE);
- }
- return;
- }
- VOID
- DwhcipSaveChannelInterrupts (
- PDWHCI_CONTROLLER Controller
- )
- /*++
- Routine Description:
- This routine saves the current interupt status for each channel and clears
- any pending interrupts.
- Arguments:
- Controller - Supplies a pointer to the state of the DWHCI controller whose
- channel interrupts are to be saved.
- Return Value:
- None.
- --*/
- {
- ULONG Channel;
- ULONG ChannelBits;
- BOOL ChannelChanged;
- PDWHCI_CHANNEL Channels;
- ULONG Interrupts;
- //
- // A bit is set in the channel interrupt register for every channel that
- // needs attention.
- //
- ChannelBits = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterHostChannelInterrupt);
- Channels = (PDWHCI_CHANNEL)(Controller->Channel);
- for (Channel = 0; Channel < Controller->ChannelCount; Channel += 1) {
- ChannelChanged = FALSE;
- if ((ChannelBits & 0x1) != 0) {
- ChannelChanged = TRUE;
- }
- ChannelBits = ChannelBits >> 1;
- if (ChannelChanged == FALSE) {
- continue;
- }
- Interrupts = DWHCI_READ_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterInterrupt,
- Channel);
- //
- // Acknowledge the interrupts.
- //
- DWHCI_WRITE_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterInterrupt,
- Channel,
- Interrupts);
- //
- // If there is no endpoint assigned to this channel, then something is
- // not quite right. The interrupts have been acknowledged, but don't
- // record the pending status.
- //
- if (Channels[Channel].Endpoint == NULL) {
- Channels[Channel].PendingInterruptBits = 0;
- continue;
- }
- //
- // Save the unmasked interrupts for this channel.
- //
- Channels[Channel].PendingInterruptBits |= Interrupts;
- }
- return;
- }
- VOID
- DwhcipProcessChannelInterrupt (
- PDWHCI_CONTROLLER Controller,
- PULONG ChannelInterruptBits
- )
- /*++
- Routine Description:
- This routine handles a channel interrupt detected in the core interrupt
- register. It interates over the host channels processing any channel that
- has pending status.
- Arguments:
- Controller - Supplies a pointer to the controller state of the DWHCI
- host controller whose channel interrupt fired.
- ChannelInterruptBits - Supplies an array of pending interrupt bits for
- each channel.
- Return Value:
- None.
- --*/
- {
- BOOL AdvanceEndpoint;
- PDWHCI_CHANNEL Channels;
- PDWHCI_ENDPOINT Endpoint;
- ULONG Index;
- ULONG Interrupts;
- BOOL ProcessSchedule;
- BOOL RemoveSet;
- PDWHCI_TRANSFER Transfer;
- //
- // Iterate over all the channels, looking for pending interrupt bits.
- //
- Channels = (PDWHCI_CHANNEL)(Controller->Channel);
- ProcessSchedule = FALSE;
- for (Index = 0; Index < Controller->ChannelCount; Index += 1) {
- Interrupts = ChannelInterruptBits[Index];
- if (Interrupts == 0) {
- continue;
- }
- //
- // If there is no endpoint assigned to this channel, then something is
- // not quite right. Ignore the interrupts.
- //
- Endpoint = Channels[Index].Endpoint;
- if (Endpoint == NULL) {
- continue;
- }
- //
- // Pre-process endpoints using split transfers. This may modify the
- // interrupt state.
- //
- if (Endpoint->SplitControl != 0) {
- DwhcipProcessSplitEndpoint(Controller, Endpoint, &Interrupts);
- }
- //
- // Pre-process high speed bulk and control transfers to handle the
- // PING protocol.
- //
- if ((Endpoint->Speed == UsbDeviceSpeedHigh) &&
- ((Endpoint->TransferType == UsbTransferTypeBulk) ||
- (Endpoint->TransferType == UsbTransferTypeControl))) {
- DwhcipProcessPingEndpoint(Controller,
- Endpoint,
- &Interrupts);
- }
- //
- // Get the first transfer for the endpoint. That is the one to which
- // the interrupt status applies. Then process the endpoint.
- //
- Transfer = DwhcipGetEndpointTransfer(Endpoint);
- ASSERT(Transfer != NULL);
- DwhcipProcessPotentiallyCompletedTransfer(Controller,
- Transfer,
- Interrupts,
- &RemoveSet,
- &AdvanceEndpoint);
- if (RemoveSet != FALSE) {
- DwhcipRemoveTransferSet(Controller, Transfer->Set);
- UsbHostProcessCompletedTransfer(Transfer->Set->UsbTransfer);
- }
- //
- // Prepare the endpoint to move onto its next transfer.
- //
- if (AdvanceEndpoint != FALSE) {
- DwhcipAdvanceEndpoint(Controller, Endpoint);
- ProcessSchedule = TRUE;
- }
- }
- //
- // Try to pump other transfers through the schedule if some channels have
- // become available.
- //
- if (ProcessSchedule != FALSE) {
- DwhcipProcessSchedule(Controller, FALSE);
- }
- return;
- }
- VOID
- DwhcipProcessPotentiallyCompletedTransfer (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_TRANSFER Transfer,
- ULONG Interrupts,
- PBOOL RemoveSet,
- PBOOL AdvanceEndpoint
- )
- /*++
- Routine Description:
- This routine processes a potentially completed transfer, adjusting the USB
- transfer if the transfer errored out or completed.
- Arguments:
- Controller - Supplies a pointer to the controller state of the DWHCI host
- controller who owns the potentially completed transfer.
- Transfer - Supplies a pointer to the transfer to evaluate.
- Interrupts - Supplies the interrupt status for this transfer's channel.
- RemoveSet - Supplies a pointer to a boolean that receives TRUE if the
- transfer's set should be removed from the active list due to completion
- or failure, or FALSE otherwise.
- AdvanceEndpoint - Supplies a pointer to a boolean that receives TRUE if the
- transfer's endpoint should be advanced to the next transfer, or FALSE
- otherwise.
- Return Value:
- None.
- --*/
- {
- ULONG BytesRemaining;
- PDWHCI_CHANNEL Channel;
- PDWHCI_ENDPOINT Endpoint;
- ULONG Errors;
- BOOL Halted;
- ULONG LengthTransferred;
- BOOL RemoveTransfer;
- PDWHCI_TRANSFER StatusTransfer;
- ULONG Token;
- PDWHCI_TRANSFER_SET TransferSet;
- BOOL TransferShorted;
- PUSB_TRANSFER UsbTransfer;
- Channel = Transfer->Set->Endpoint->Channel;
- Endpoint = Transfer->Set->Endpoint;
- LengthTransferred = 0;
- RemoveTransfer = FALSE;
- *RemoveSet = FALSE;
- *AdvanceEndpoint = TRUE;
- TransferShorted = FALSE;
- UsbTransfer = &(Transfer->Set->UsbTransfer->Public);
- ASSERT(Channel != NULL);
- //
- // The transfer should not be removed if this routine is reached. Nor
- // should it's transfer set.
- //
- ASSERT(Transfer->SetListEntry.Next != NULL);
- ASSERT(Transfer->Set->EndpointListEntry.Next != NULL);
- //
- // Always read the transfer token to update the endpoint's data toggle.
- //
- Token = DWHCI_READ_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterToken,
- Channel->ChannelNumber);
- Endpoint->DataToggle = (Token & DWHCI_CHANNEL_TOKEN_PID_MASK) >>
- DWHCI_CHANNEL_TOKEN_PID_SHIFT;
- //
- // DATA2 may be returned, so if the toggle is not DATA0, just force it to
- // DATA1.
- //
- if (Endpoint->DataToggle != DWHCI_PID_CODE_DATA_0) {
- Endpoint->DataToggle = DWHCI_PID_CODE_DATA_1;
- }
- ASSERT(Endpoint->DataToggle != DWHCI_PID_CODE_MORE_DATA);
- //
- // If the transfer was already cancelled, then just remove the set and
- // exit.
- //
- if (UsbTransfer->Error == UsbErrorTransferCancelled) {
- ASSERT(UsbTransfer->Status == STATUS_OPERATION_CANCELLED);
- *RemoveSet = TRUE;
- goto ProcessPotentiallyCompletedTransferEnd;
- }
- //
- // If a device I/O error is set in the transfer, then this is just the
- // channel halt operation completing. The AHB error was already handled.
- //
- if (UsbTransfer->Error == UsbErrorTransferDeviceIo) {
- *RemoveSet = TRUE;
- goto ProcessPotentiallyCompletedTransferEnd;
- }
- //
- // If there was an error on the channel, then update the USB transfer's
- // error state.
- //
- Errors = Interrupts & DWHCI_CHANNEL_INTERRUPT_ERROR_MASK;
- if (Errors != 0) {
- *RemoveSet = TRUE;
- UsbTransfer->Status = STATUS_DEVICE_IO_ERROR;
- if ((Errors & DWHCI_CHANNEL_INTERRUPT_STALL) != 0) {
- UsbTransfer->Error = UsbErrorTransferStalled;
- } else if ((Errors & DWHCI_CHANNEL_INTERRUPT_TRANSACTION_ERROR) != 0) {
- UsbTransfer->Error = UsbErrorTransferCrcOrTimeoutError;
- } else if ((Errors & DWHCI_CHANNEL_INTERRUPT_BABBLE_ERROR) != 0) {
- UsbTransfer->Error = UsbErrorTransferBabbleDetected;
- } else if ((Errors &
- DWHCI_CHANNEL_INTERRUPT_DMA_BUFFER_NOT_AVAILABLE) != 0) {
- UsbTransfer->Error = UsbErrorTransferDataBuffer;
- } else if ((Errors & DWHCI_CHANNEL_INTERRUPT_AHB_ERROR) != 0) {
- UsbTransfer->Error = UsbErrorTransferDeviceIo;
- Halted = DwhcipHaltChannel(Controller, Channel);
- if (Halted == FALSE) {
- *RemoveSet = FALSE;
- *AdvanceEndpoint = FALSE;
- }
- }
- }
- //
- // If the transfer completed, then update the USB transfer's size. It is
- // only valid if the complete bit is set.
- //
- if ((Interrupts & DWHCI_CHANNEL_INTERRUPT_TRANSFER_COMPLETE) != 0) {
- //
- // For IN transfes, the channel token contains the number of unwritten
- // bytes in the transfer buffer.
- //
- if (Transfer->InTransfer != FALSE) {
- BytesRemaining = (Token & DWHCI_CHANNEL_TOKEN_TRANSFER_SIZE_MASK) >>
- DWHCI_CHANNEL_TOKEN_TRANSFER_SIZE_SHIFT;
- LengthTransferred = Transfer->TransferLength - BytesRemaining;
- //
- // For completed OUT transfers, it is assumed that all the bytes were
- // accepted. There are no bytes remaining.
- //
- } else {
- LengthTransferred = Transfer->TransferLength;
- }
- UsbTransfer->LengthTransferred += LengthTransferred;
- //
- // If the whole set is not already scheduled for removal, process the
- // completed status information to decided what happens to the transfer
- // and/or its set.
- //
- if (*RemoveSet == FALSE) {
- if (Transfer->LastTransfer != FALSE) {
- *RemoveSet = TRUE;
- } else if (LengthTransferred != Transfer->TransferLength) {
- TransferShorted = TRUE;
- } else {
- RemoveTransfer = TRUE;
- }
- }
- }
- //
- // For shorted transfers, either skip ahead to the status phase of a
- // control transfer or just return that the whole set should be removed.
- //
- if (TransferShorted != FALSE) {
- if (Endpoint->TransferType == UsbTransferTypeControl) {
- *RemoveSet = FALSE;
- //
- // The last entry in the transfer set should be the status transfer.
- //
- TransferSet = Transfer->Set;
- ASSERT(LIST_EMPTY(&(TransferSet->TransferListHead)) == FALSE);
- StatusTransfer = LIST_VALUE(TransferSet->TransferListHead.Previous,
- DWHCI_TRANSFER,
- SetListEntry);
- ASSERT(StatusTransfer->LastTransfer != FALSE);
- //
- // Remove everything from the list by simply re-initializing it and
- // then re-insert the status transfer as the only transfer.
- //
- INITIALIZE_LIST_HEAD(&(TransferSet->TransferListHead));
- INSERT_BEFORE(&(StatusTransfer->SetListEntry),
- &(TransferSet->TransferListHead));
- } else {
- *RemoveSet = TRUE;
- }
- //
- // Otherwise remove the single transfer if necessary.
- //
- } else if (RemoveTransfer != FALSE) {
- LIST_REMOVE(&(Transfer->SetListEntry));
- }
- ProcessPotentiallyCompletedTransferEnd:
- return;
- }
- VOID
- DwhcipRemoveTransferSet (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_TRANSFER_SET TransferSet
- )
- /*++
- Routine Description:
- This routine removes a transfer set from the schedule. This routine
- assumes that the controller lock is already held.
- Arguments:
- Controller - Supplies a pointer to the controller being operated on.
- TransferSet - Supplies a pointer to the set of transfers to remove.
- Return Value:
- None.
- --*/
- {
- LIST_REMOVE(&(TransferSet->EndpointListEntry));
- TransferSet->EndpointListEntry.Next = NULL;
- return;
- }
- VOID
- DwhcipProcessSplitEndpoint (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_ENDPOINT Endpoint,
- PULONG Interrupts
- )
- /*++
- Routine Description:
- This routine pre-processes a potentially completed transfer for an endpoint
- that must use split transfers.
- Arguments:
- Controller - Supplies a pointer to the controller state of the DWHCI host
- controller whose got a split endpoint with a potentially completed
- transfer.
- Endpoint - Supplies a pointer to an endpoint that sends split transfers.
- Interrupts - Supplies a pointer to the interrupt state for the endpoint's
- associated channel. This routine may modify the interrupt state.
- Return Value:
- None.
- --*/
- {
- ULONG EndFrame;
- ULONG Frame;
- ULONG LocalInterrupts;
- PDWHCI_TRANSFER Transfer;
- ASSERT(Endpoint->SplitControl != 0);
- LocalInterrupts = *Interrupts;
- //
- // Get the active transfer on this endpoint.
- //
- Transfer = DwhcipGetEndpointTransfer(Endpoint);
- ASSERT(Transfer != NULL);
- //
- // If this is a start split there are three possible paths: NAK, ACK, or an
- // error.
- //
- if (Transfer->CompleteSplitCount == 0) {
- //
- // A maximum of 3 errors are allowed. If the are fewer than three
- // errors for this transfer, then mask out the errors and retry the
- // start split.
- //
- if ((LocalInterrupts & DWHCI_CHANNEL_INTERRUPT_ERROR_MASK) != 0) {
- Transfer->ErrorCount += 1;
- if (Transfer->ErrorCount < DWHCI_SPLIT_ERROR_MAX) {
- LocalInterrupts &= ~DWHCI_CHANNEL_INTERRUPT_ERROR_MASK;
- }
- //
- // An ACK on a start split rolls over to the complete split.
- //
- } else if ((LocalInterrupts & DWHCI_CHANNEL_INTERRUPT_ACK) != 0) {
- Transfer->CompleteSplitCount = 1;
- LocalInterrupts &= ~DWHCI_CHANNEL_INTERRUPT_TRANSFER_COMPLETE;
- //
- // A NAK on a start split should retry the start split.
- //
- } else if ((LocalInterrupts & DWHCI_CHANNEL_INTERRUPT_NAK) != 0) {
- LocalInterrupts &= ~DWHCI_CHANNEL_INTERRUPT_TRANSFER_COMPLETE;
- }
- //
- // If this is a complete split, then there are five possible paths: NAK,
- // ACK, stall, error, and 'not yet'.
- //
- } else {
- //
- // A stall should cause the transfer to just abort. Set the errors to
- // the max.
- //
- if ((LocalInterrupts & DWHCI_CHANNEL_INTERRUPT_STALL) != 0) {
- Transfer->ErrorCount = DWHCI_SPLIT_ERROR_MAX;
- }
- //
- // A maximum of 3 errors are allowed. If the are fewer than three
- // errors on this endpoint, then mask out the errors. Control and bulk
- // data toggle errors cause the start split to be retried.
- //
- if ((LocalInterrupts & DWHCI_CHANNEL_INTERRUPT_ERROR_MASK) != 0) {
- Transfer->ErrorCount += 1;
- if (Transfer->ErrorCount < DWHCI_SPLIT_ERROR_MAX) {
- if (((Endpoint->TransferType == UsbTransferTypeBulk) ||
- (Endpoint->TransferType == UsbTransferTypeControl)) &&
- ((LocalInterrupts &
- DWHCI_CHANNEL_INTERRUPT_DATA_TOGGLE_ERROR) != 0)) {
- Transfer->CompleteSplitCount = 0;
- Transfer->ErrorCount = 0;
- }
- LocalInterrupts &= ~DWHCI_CHANNEL_INTERRUPT_ERROR_MASK;
- }
- //
- // An ACK on a complete split should finish the transfer.
- //
- } else if ((LocalInterrupts & DWHCI_CHANNEL_INTERRUPT_ACK) != 0) {
- LocalInterrupts |= DWHCI_CHANNEL_INTERRUPT_TRANSFER_COMPLETE;
- //
- // A NAK on the complete split causes the start split to be retried.
- //
- } else if ((LocalInterrupts & DWHCI_CHANNEL_INTERRUPT_NAK) != 0) {
- Transfer->CompleteSplitCount = 0;
- Transfer->ErrorCount = 0;
- LocalInterrupts &= ~DWHCI_CHANNEL_INTERRUPT_TRANSFER_COMPLETE;
- //
- // A NYET on the complete split should retry the complete split.
- //
- } else if ((LocalInterrupts & DWHCI_CHANNEL_INTERRUPT_NOT_YET) != 0) {
- LocalInterrupts &= ~DWHCI_CHANNEL_INTERRUPT_TRANSFER_COMPLETE;
- //
- // Interrupt endpoints are the exception. If this is not the last
- // (3rd) complete split or the complete split window has not passed,
- // then NYETs indicate that the complete split should be tried
- // again. Otherwise NYETs count towards the error count and the
- // start split is tried again if the maximum error is yet to be
- // reached.
- //
- if (Endpoint->TransferType == UsbTransferTypeInterrupt) {
- Frame = DWHCI_READ_FRAME_NUMBER(Controller);
- EndFrame = (Endpoint->StartFrame +
- DWHCI_SPLIT_NOT_YET_FRAME_WINDOW) &
- DWHCI_FRAME_NUMBER_MAX;
- if (DWHCI_FRAME_LESS_THAN(EndFrame, Frame) != FALSE) {
- LocalInterrupts |=
- DWHCI_CHANNEL_INTERRUPT_TRANSACTION_ERROR;
- Transfer->CompleteSplitCount = 0;
- } else if (Transfer->CompleteSplitCount >=
- DWHCI_COMPLETE_SPLIT_MAX) {
- Transfer->ErrorCount += 1;
- if (Transfer->ErrorCount >= DWHCI_SPLIT_ERROR_MAX) {
- LocalInterrupts |=
- DWHCI_CHANNEL_INTERRUPT_TRANSACTION_ERROR;
- }
- Transfer->CompleteSplitCount = 0;
- } else {
- Transfer->CompleteSplitCount += 1;
- }
- }
- }
- }
- *Interrupts = LocalInterrupts;
- return;
- }
- VOID
- DwhcipProcessPingEndpoint (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_ENDPOINT Endpoint,
- PULONG Interrupts
- )
- /*++
- Routine Description:
- This routine pre-processes a potentially completed transfer for an endpoint
- that must use PING protocol.
- Arguments:
- Controller - Supplies a pointer to the controller state of the DWHCI host
- controller whose got a high speed bulk or control endpoint with a
- potentially completed transfer.
- Endpoint - Supplies a pointer to an endpoint that implements the PING
- protocol.
- Interrupts - Supplies a pointer to the interrupt state for the endpoint's
- associated channel. This routine may modify the interrupt state.
- Return Value:
- None.
- --*/
- {
- ULONG LocalInterrupts;
- PDWHCI_TRANSFER NextTransfer;
- PDWHCI_TRANSFER Transfer;
- PDWHCI_TRANSFER_SET TransferSet;
- ASSERT(Endpoint->Speed == UsbDeviceSpeedHigh);
- ASSERT((Endpoint->TransferType == UsbTransferTypeBulk) ||
- (Endpoint->TransferType == UsbTransferTypeControl));
- ASSERT(Endpoint->SplitControl == 0);
- LocalInterrupts = *Interrupts;
- //
- // Get the active transfer on this endpoint.
- //
- Transfer = DwhcipGetEndpointTransfer(Endpoint);
- ASSERT(Transfer != NULL);
- TransferSet = Transfer->Set;
- //
- // IN endpoints do not implement the PING protocol.
- //
- if (TransferSet->UsbTransfer->Public.Direction == UsbTransferDirectionIn) {
- return;
- }
- //
- // Newer revisions do not require manual handling of the PING protocol.
- //
- if (Controller->Revision >= DWHCI_AUTOMATIC_PING_REVISION_MININUM) {
- return;
- }
- ASSERT(Endpoint->PingRequired == FALSE);
- //
- // For OUT bulk transfers, NAKs and NYETs require that the PING protocol
- // should be triggered on the next transfer for the endpoint.
- //
- if (Endpoint->TransferType == UsbTransferTypeBulk) {
- if (((LocalInterrupts & DWHCI_CHANNEL_INTERRUPT_NAK) != 0) ||
- ((LocalInterrupts & DWHCI_CHANNEL_INTERRUPT_NOT_YET) != 0)) {
- Endpoint->PingRequired = TRUE;
- }
- //
- // For control transfers, the PING protocol is only required on OUT data or
- // status phases so separate this between SETUP and not setup.
- //
- } else {
- ASSERT(Endpoint->TransferType == UsbTransferTypeControl);
- //
- // The PING protocol is not supported for the SETUP phase. If this is
- // the setup phase completing, then potentially set PING for the next
- // transfer, if it is OUT.
- //
- if ((Transfer->Token & DWHCI_CHANNEL_TOKEN_PID_MASK) ==
- DWHCI_CHANNEL_TOKEN_PID_CODE_SETUP) {
- if ((LocalInterrupts &
- DWHCI_CHANNEL_INTERRUPT_TRANSFER_COMPLETE) != 0) {
- ASSERT(Transfer->SetListEntry.Next !=
- &(TransferSet->TransferListHead));
- NextTransfer = LIST_VALUE(Transfer->SetListEntry.Next,
- DWHCI_TRANSFER,
- SetListEntry);
- if (NextTransfer->InTransfer == FALSE) {
- Endpoint->PingRequired = TRUE;
- }
- }
- //
- // Handle DATA transfers.
- //
- } else if (Transfer->LastTransfer == FALSE) {
- //
- // A DATA OUT that did not complete and sent NAK or NYET requires
- // a PING when the transfer is resent. Completed DATA OUTs do not
- // need to set the PING, because the status phase goes in the
- // opposite direction.
- //
- if ((Transfer->InTransfer == FALSE) &&
- (((LocalInterrupts & DWHCI_CHANNEL_INTERRUPT_NAK) != 0) ||
- ((LocalInterrupts & DWHCI_CHANNEL_INTERRUPT_NOT_YET) != 0))) {
- ASSERT((LocalInterrupts &
- DWHCI_CHANNEL_INTERRUPT_TRANSFER_COMPLETE) == 0);
- Endpoint->PingRequired = TRUE;
- //
- // Otherwise a completed DATA IN will transfer to the status phase,
- // which should begin with the PING protocol, as it is an OUT
- // transfer.
- //
- } else if ((Transfer->InTransfer != FALSE) &&
- ((LocalInterrupts &
- DWHCI_CHANNEL_INTERRUPT_TRANSFER_COMPLETE) != 0)) {
- Endpoint->PingRequired = TRUE;
- }
- //
- // Handle OUT status phases.
- //
- } else if ((Transfer->LastTransfer != FALSE) &&
- (Transfer->InTransfer == FALSE)) {
- //
- // If the OUT status phase NAKs or NYETs, then the PING protocol
- // needs to be invoked on the retry.
- //
- if (((LocalInterrupts & DWHCI_CHANNEL_INTERRUPT_NAK) != 0) ||
- ((LocalInterrupts & DWHCI_CHANNEL_INTERRUPT_NOT_YET) != 0)) {
- Endpoint->PingRequired = TRUE;
- }
- }
- }
- return;
- }
- VOID
- DwhcipFillOutTransferDescriptor (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_TRANSFER_SET TransferSet,
- PDWHCI_TRANSFER DwhciTransfer,
- ULONG Offset,
- ULONG Length,
- BOOL LastTransfer
- )
- /*++
- Routine Description:
- This routine fills out an DWHCI transfer descriptor.
- Arguments:
- Controller - Supplies a pointer to the DWHCI controller.
- TransferSet - Supplies a pointer to the transfer set this transfer belongs
- to.
- DwhciTransfer - Supplies a pointer to DWHCI's transfer descriptor
- information.
- Offset - Supplies the offset from the public transfer physical address that
- this transfer descriptor should be initialize to.
- Length - Supplies the length of the transfer, in bytes.
- LastTransfer - Supplies a boolean indicating if this transfer descriptor
- represents the last transfer in a set. For control transfers, this is
- the status phase where the in/out is reversed and the length had better
- be zero.
- Return Value:
- None.
- --*/
- {
- PDWHCI_ENDPOINT Endpoint;
- ULONG PacketCount;
- UCHAR PidCode;
- ULONG Token;
- PUSB_TRANSFER_INTERNAL Transfer;
- Endpoint = TransferSet->Endpoint;
- Transfer = TransferSet->UsbTransfer;
- DwhciTransfer->LastTransfer = FALSE;
- DwhciTransfer->TransferLength = Length;
- DwhciTransfer->Set = TransferSet;
- DwhciTransfer->ErrorCount = 0;
- DwhciTransfer->PhysicalAddress = Transfer->Public.BufferPhysicalAddress +
- Offset;
- //
- // The first packet in a control transfer is always a setup packet and is
- // not an IN transfer.
- //
- PidCode = 0;
- if ((Endpoint->TransferType == UsbTransferTypeControl) && (Offset == 0)) {
- PidCode = DWHCI_PID_CODE_SETUP;
- DwhciTransfer->InTransfer = FALSE;
- //
- // Do it backwards if this is the status phase. Status phases always have
- // a data toggle of 1 and the transfer direction is opposite that of the
- // transfer. The exception is if there was no data phase for the control
- // transfer - just the setup and status phases. In that case, the status
- // phase is always in the IN direction.
- //
- } else if ((Endpoint->TransferType == UsbTransferTypeControl) &&
- (LastTransfer != FALSE)) {
- ASSERT(Length == 0);
- PidCode = DWHCI_PID_CODE_DATA_1;
- if (Offset == sizeof(USB_SETUP_PACKET)) {
- DwhciTransfer->InTransfer = TRUE;
- } else if (Transfer->Public.Direction == UsbTransferDirectionIn) {
- DwhciTransfer->InTransfer = FALSE;
- } else {
- ASSERT(Transfer->Public.Direction == UsbTransferDirectionOut);
- DwhciTransfer->InTransfer = TRUE;
- }
- DwhciTransfer->PhysicalAddress =
- Controller->ControlStatusBuffer->Fragment[0].PhysicalAddress;
- //
- // Not setup and not status, fill this out like a normal descriptor.
- //
- } else {
- if (Transfer->Public.Direction == UsbTransferDirectionIn) {
- DwhciTransfer->InTransfer = TRUE;
- } else {
- ASSERT(Transfer->Public.Direction == UsbTransferDirectionOut);
- DwhciTransfer->InTransfer = FALSE;
- }
- }
- //
- // Determine which channel interrupts to set.
- //
- switch (Endpoint->TransferType) {
- case UsbTransferTypeIsochronous:
- //
- // TODO: Implement support for isochronous transfers.
- //
- ASSERT(FALSE);
- break;
- case UsbTransferTypeInterrupt:
- case UsbTransferTypeControl:
- case UsbTransferTypeBulk:
- DwhciTransfer->InterruptMask = DWHCI_CHANNEL_INTERRUPT_HALTED |
- DWHCI_CHANNEL_INTERRUPT_AHB_ERROR;
- break;
- default:
- ASSERT(FALSE);
- break;
- }
- //
- // If this transfer uses the split procotol, it will always begin with the
- // start split (i.e. a complete split count of zero).
- //
- DwhciTransfer->CompleteSplitCount = 0;
- //
- // Determine the number of packets in the transfer.
- //
- PacketCount = 1;
- if (DwhciTransfer->TransferLength > Endpoint->MaxPacketSize) {
- PacketCount = DwhciTransfer->TransferLength / Endpoint->MaxPacketSize;
- if ((DwhciTransfer->TransferLength % Endpoint->MaxPacketSize) != 0) {
- PacketCount += 1;
- }
- }
- ASSERT(PacketCount <= Endpoint->MaxPacketCount);
- //
- // Initialize the token that is to be written to a channel's transfer setup
- // register when submitting this transfer.
- //
- Token = (PacketCount << DWHCI_CHANNEL_TOKEN_PACKET_COUNT_SHIFT) &
- DWHCI_CHANNEL_TOKEN_PACKET_COUNT_MASK;
- Token |= (PidCode << DWHCI_CHANNEL_TOKEN_PID_SHIFT) &
- DWHCI_CHANNEL_TOKEN_PID_MASK;
- Token |= (DwhciTransfer->TransferLength <<
- DWHCI_CHANNEL_TOKEN_TRANSFER_SIZE_SHIFT) &
- DWHCI_CHANNEL_TOKEN_TRANSFER_SIZE_MASK;
- DwhciTransfer->Token = Token;
- //
- // Add the transfer into the transfer set's queue.
- //
- INSERT_BEFORE(&(DwhciTransfer->SetListEntry),
- &(TransferSet->TransferListHead));
- if ((DwhciDebugFlags & DWHCI_DEBUG_FLAG_TRANSFERS) != 0) {
- RtlDebugPrint("DWHCI: Adding transfer (0x%08x) to endpoint (0x%08x): "
- "TOKEN 0x%x, IN 0x%x, LAST 0x%x, INT 0x%08x, "
- "LENGTH 0x%x.\n",
- DwhciTransfer,
- Endpoint,
- DwhciTransfer->Token,
- DwhciTransfer->InTransfer,
- DwhciTransfer->LastTransfer,
- DwhciTransfer->InterruptMask,
- DwhciTransfer->TransferLength);
- }
- return;
- }
- VOID
- DwhcipProcessSchedule (
- PDWHCI_CONTROLLER Controller,
- BOOL PeriodicOnly
- )
- /*++
- Routine Description:
- This routine processes any pending activity on the given host controller's
- periodic and non-periodic schedules. If there are channels available to
- schedule work on, then work will be scheduled. This routine expects the
- controller lock to be held.
- Arguments:
- Controller - Supplies a pointer to the state for the DWHCI controller
- whose schedule needs to be processed.
- PeriodicOnly - Supplies a boolean indicating whether or not to only schedule
- periodic transfers.
- Return Value:
- None.
- --*/
- {
- PLIST_ENTRY CurrentEntry;
- PDWHCI_ENDPOINT Endpoint;
- KSTATUS Status;
- //
- // If there are any periodic endpoints waiting to be assigned a channel,
- // then try to move the endpoints from the ready list to the active list.
- //
- while (LIST_EMPTY(&(Controller->PeriodicReadyListHead)) == FALSE) {
- Endpoint = LIST_VALUE(Controller->PeriodicReadyListHead.Next,
- DWHCI_ENDPOINT,
- ListEntry);
- //
- // Initialize the channel to accept transfers from this endpoint.
- //
- Status = DwhcipAllocateChannel(Controller, Endpoint);
- if (!KSUCCESS(Status)) {
- break;
- }
- LIST_REMOVE(&(Endpoint->ListEntry));
- INSERT_BEFORE(&(Endpoint->ListEntry),
- &(Controller->PeriodicActiveListHead));
- }
- //
- // Process the active periodic endpoint list to try to push them through
- // the periodic queue.
- //
- CurrentEntry = Controller->PeriodicActiveListHead.Next;
- while (CurrentEntry != &(Controller->PeriodicActiveListHead)) {
- Endpoint = LIST_VALUE(CurrentEntry, DWHCI_ENDPOINT, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- if (Endpoint->Scheduled != FALSE) {
- continue;
- }
- DwhcipScheduleTransfer(Controller, Endpoint);
- }
- //
- // If only the periodic schedule was requested to be processed, then exit
- // immediately.
- //
- if (PeriodicOnly != FALSE) {
- return;
- }
- //
- // If there are any non-periodic endpoints waiting to be assigned a
- // channel, then try to move the endpoints from the ready list to the
- // active list.
- //
- while (LIST_EMPTY(&(Controller->NonPeriodicReadyListHead)) == FALSE) {
- Endpoint = LIST_VALUE(Controller->NonPeriodicReadyListHead.Next,
- DWHCI_ENDPOINT,
- ListEntry);
- //
- // Initialize the channel to accept transfers from this endpoint.
- //
- Status = DwhcipAllocateChannel(Controller, Endpoint);
- if (!KSUCCESS(Status)) {
- break;
- }
- LIST_REMOVE(&(Endpoint->ListEntry));
- INSERT_BEFORE(&(Endpoint->ListEntry),
- &(Controller->NonPeriodicActiveListHead));
- }
- //
- // Process the active non-periodic endpoint list to try to push them
- // through the non-periodic queue.
- //
- CurrentEntry = Controller->NonPeriodicActiveListHead.Next;
- while (CurrentEntry != &(Controller->NonPeriodicActiveListHead)) {
- Endpoint = LIST_VALUE(CurrentEntry, DWHCI_ENDPOINT, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- if (Endpoint->Scheduled != FALSE) {
- continue;
- }
- DwhcipScheduleTransfer(Controller, Endpoint);
- }
- return;
- }
- KSTATUS
- DwhcipAllocateChannel (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_ENDPOINT Endpoint
- )
- /*++
- Routine Description:
- This routine allocates a channel for use by the given endpoint.
- Arguments:
- Controller - Supplies a pointer to the state of the DWHCI controller from
- which to allocate a channel.
- Endpoint - Supplies a pointer to the DWHCI endpoint that is to be assigned
- to the channel.
- Return Value:
- Status code.
- --*/
- {
- PDWHCI_CHANNEL Channel;
- //
- // If the free channel list is empty, then exit immediately.
- //
- if (LIST_EMPTY(&(Controller->FreeChannelListHead)) != FALSE) {
- return STATUS_RESOURCE_IN_USE;
- }
- //
- // If this is a periodic endpoint and there is only one channel left, exit,
- // allowing the non-periodic endpoints some guaranteed progress.
- //
- if ((Controller->FreeChannelListHead.Next ==
- Controller->FreeChannelListHead.Previous) &&
- ((Endpoint->TransferType == UsbTransferTypeInterrupt) ||
- (Endpoint->TransferType == UsbTransferTypeIsochronous))) {
- return STATUS_RESOURCE_IN_USE;
- }
- //
- // Allocate the first channel in the free list.
- //
- Channel = LIST_VALUE(Controller->FreeChannelListHead.Next,
- DWHCI_CHANNEL,
- FreeListEntry);
- LIST_REMOVE(&(Channel->FreeListEntry));
- ASSERT(Channel->Endpoint == NULL);
- //
- // Associate the allocated channel with the given endpoint.
- //
- Channel->Endpoint = Endpoint;
- Endpoint->Channel = Channel;
- return STATUS_SUCCESS;
- }
- VOID
- DwhcipFreeChannel (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_CHANNEL Channel
- )
- /*++
- Routine Description:
- This routine frees the given channel from use by an endpoint.
- Arguments:
- Controller - Supplies a pointer to the state of the DWHCI controller from
- which to allocate a channel.
- Channel - Supplies a pointer to the DWHCI channel that is to be released.
- Return Value:
- None.
- --*/
- {
- ASSERT(Channel->Endpoint != NULL);
- Channel->Endpoint->Channel = NULL;
- Channel->Endpoint = NULL;
- INSERT_BEFORE(&(Channel->FreeListEntry),
- &(Controller->FreeChannelListHead));
- return;
- }
- VOID
- DwhcipScheduleTransfer (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_ENDPOINT Endpoint
- )
- /*++
- Routine Description:
- This routine schedules the next transfer for the given endpoint.
- Arguments:
- Controller - Supplies a pointer to the state of the DWHCI controller to
- which the endpoint belongs.
- Endpoint - Supplies a pointer to the endpoint whose next transfer is to be
- scheduled.
- Return Value:
- None.
- --*/
- {
- PDWHCI_CHANNEL Channel;
- ULONG Control;
- ULONG Frame;
- ULONG Interrupts;
- ULONG SplitControl;
- KSTATUS Status;
- ULONG Token;
- PDWHCI_TRANSFER Transfer;
- ASSERT(Endpoint->Channel != NULL);
- Channel = Endpoint->Channel;
- //
- // Find the next transfer for this endpoint. This transfer is the first
- // transfer in the first transfer set.
- //
- Transfer = DwhcipGetEndpointTransfer(Endpoint);
- ASSERT(Transfer != NULL);
- //
- // Initialize the host channel for use by the endpoint. Start by clearing
- // any interrupts on the channel.
- //
- DWHCI_WRITE_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterInterrupt,
- Channel->ChannelNumber,
- 0xFFFFFFFF);
- DWHCI_WRITE_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterInterruptMask,
- Channel->ChannelNumber,
- Transfer->InterruptMask);
- //
- // Enable host level interrupts for this channel.
- //
- Interrupts = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterHostChannelInterruptMask);
- Interrupts |= (1 << Channel->ChannelNumber);
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterHostChannelInterruptMask,
- Interrupts);
- //
- // If this is a full or low-speed device, then configure the split register.
- //
- Token = Transfer->Token;
- SplitControl = Endpoint->SplitControl;
- if (SplitControl != 0) {
- ASSERT((Endpoint->Speed == UsbDeviceSpeedLow) ||
- (Endpoint->Speed == UsbDeviceSpeedFull));
- ASSERT((SplitControl & DWHCI_CHANNEL_SPLIT_CONTROL_ENABLE) != 0);
- if (Transfer->CompleteSplitCount != 0) {
- if (Transfer->InTransfer == FALSE) {
- Token &= ~DWHCI_CHANNEL_TOKEN_TRANSFER_SIZE_MASK;
- }
- SplitControl |= DWHCI_CHANNEL_SPLIT_CONTROL_COMPLETE_SPLIT;
- //
- // Interrupt start splits are not allowed to be started in the 6th
- // microframe.
- //
- } else if (Endpoint->TransferType == UsbTransferTypeInterrupt) {
- Frame = DWHCI_READ_FRAME_NUMBER(Controller);
- if ((Frame & 0x7) == 0x6) {
- Status = STATUS_TRY_AGAIN;
- goto ScheduleTransferEnd;
- }
- Endpoint->StartFrame = Frame;
- }
- }
- //
- // Setup up the transfer register based on the transfer token. This
- // includes information on the transfer length, the PID, and number of
- // packets. If the PID is preset in the token, then use what is there,
- // otherwise use the current toggle pid stored in the endpoint.
- //
- if ((Transfer->Token & DWHCI_CHANNEL_TOKEN_PID_MASK) == 0) {
- Token |= (Endpoint->DataToggle << DWHCI_CHANNEL_TOKEN_PID_SHIFT) &
- DWHCI_CHANNEL_TOKEN_PID_MASK;
- } else {
- ASSERT(Endpoint->TransferType == UsbTransferTypeControl);
- }
- //
- // Set the PING protocol bit in the token if required.
- //
- if (Endpoint->PingRequired != FALSE) {
- ASSERT(Transfer->InTransfer == FALSE);
- ASSERT(Endpoint->Speed == UsbDeviceSpeedHigh);
- ASSERT((Endpoint->TransferType == UsbTransferTypeBulk) ||
- (Endpoint->TransferType == UsbTransferTypeControl));
- ASSERT((Endpoint->TransferType != UsbTransferTypeControl) ||
- ((Token & DWHCI_CHANNEL_TOKEN_PID_MASK) !=
- DWHCI_CHANNEL_TOKEN_PID_CODE_SETUP));
- Token |= DWHCI_CHANNEL_TOKEN_PING;
- //
- // Let the status of this transfer determine if another PING is
- // required.
- //
- Endpoint->PingRequired = FALSE;
- }
- DWHCI_WRITE_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterToken,
- Channel->ChannelNumber,
- Token);
- //
- // Program the DMA register.
- //
- ASSERT(Transfer->PhysicalAddress == (ULONG)Transfer->PhysicalAddress);
- ASSERT(IS_ALIGNED(Transfer->PhysicalAddress, DWHCI_DMA_ALIGNMENT) != FALSE);
- DWHCI_WRITE_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterDmaAddress,
- Channel->ChannelNumber,
- (ULONG)Transfer->PhysicalAddress);
- //
- // Program the split control register.
- //
- DWHCI_WRITE_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterSplitControl,
- Channel->ChannelNumber,
- SplitControl);
- //
- // Execute the final steps, enabling the channel to handle the transfer.
- //
- Control = Endpoint->ChannelControl;
- if (Transfer->InTransfer != FALSE) {
- Control |= DWHCI_CHANNEL_CONTROL_ENDPOINT_DIRECTION_IN;
- }
- switch (Endpoint->TransferType) {
- case UsbTransferTypeIsochronous:
- case UsbTransferTypeInterrupt:
- //
- // Set the odd frame bit if the current frame is even.
- //
- Frame = DWHCI_READ_FRAME_NUMBER(Controller);
- if ((Frame & 0x1) == 0) {
- Control |= DWHCI_CHANNEL_CONTROL_ODD_FRAME;
- }
- break;
- case UsbTransferTypeControl:
- case UsbTransferTypeBulk:
- break;
- default:
- ASSERT(FALSE);
- break;
- }
- ASSERT((Control & DWHCI_CHANNEL_CONTROL_ENABLE) != 0);
- ASSERT((Control & DWHCI_CHANNEL_CONTROL_DISABLE) == 0);
- DWHCI_WRITE_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterControl,
- Channel->ChannelNumber,
- Control);
- Endpoint->Scheduled = TRUE;
- Status = STATUS_SUCCESS;
- ScheduleTransferEnd:
- if (!KSUCCESS(Status)) {
- //
- // Disable interrupts for this channel.
- //
- DWHCI_WRITE_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterInterruptMask,
- Channel->ChannelNumber,
- 0);
- Interrupts = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterHostChannelInterruptMask);
- Interrupts &= ~(1 << Channel->ChannelNumber);
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterHostChannelInterruptMask,
- Interrupts);
- //
- // This should be an interrupt endpoint and it needs to try again. Just
- // move it back to the inactive list and trigger the start-of-frame
- // interrupt. Release the channel as well.
- //
- ASSERT(Status == STATUS_TRY_AGAIN);
- ASSERT(Endpoint->TransferType == UsbTransferTypeInterrupt);
- LIST_REMOVE(&(Endpoint->ListEntry));
- if (LIST_EMPTY(&(Controller->PeriodicInactiveListHead)) != FALSE) {
- Interrupts = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterCoreInterruptMask);
- Interrupts |= DWHCI_CORE_INTERRUPT_START_OF_FRAME;
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterCoreInterruptMask,
- Interrupts);
- }
- INSERT_BEFORE(&(Endpoint->ListEntry),
- &(Controller->PeriodicInactiveListHead));
- DwhcipFreeChannel(Controller, Endpoint->Channel);
- Endpoint->Scheduled = FALSE;
- }
- return;
- }
- VOID
- DwhcipAdvanceEndpoint (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_ENDPOINT Endpoint
- )
- /*++
- Routine Description:
- This routine prepares the given endpoint for its next transfer. This may
- or may not release the channel. This routine assumes that the caller
- will process the host controller's schedule shortly after calling this
- routine.
- Arguments:
- Controller - Supplies a pointer to the state of the DWHCI controller that
- owns the given endpoint.
- Endpoint - Supplies a pointer to the endpoint that needs to be advanced
- to its next transfer.
- Return Value:
- Returns TRUE if the endpoint had been using a channel and this routine
- released it. Returns FALSE otherwise.
- --*/
- {
- ULONG Base;
- PDWHCI_CHANNEL Channel;
- ULONG CoreInterruptMask;
- ULONG Delta;
- ULONG FrameNumber;
- BOOL FreeChannel;
- ULONG Interrupts;
- ULONG NextFrame;
- BOOL PeriodicInactiveWasEmpty;
- PDWHCI_TRANSFER Transfer;
- Channel = Endpoint->Channel;
- FreeChannel = FALSE;
- //
- // Disable and clear all interrupts on the current channel.
- //
- if (Channel != NULL) {
- DWHCI_WRITE_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterInterruptMask,
- Channel->ChannelNumber,
- 0);
- DWHCI_WRITE_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterInterrupt,
- Channel->ChannelNumber,
- 0xFFFFFFFF);
- //
- // Disable host level interrupts for this channel.
- //
- Interrupts = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterHostChannelInterruptMask);
- Interrupts &= ~(1 << Channel->ChannelNumber);
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterHostChannelInterruptMask,
- Interrupts);
- //
- // Assume that the channel will become available for other transfers.
- //
- FreeChannel = TRUE;
- }
- //
- // Before the endpoint is removed, determine the state of the periodic
- // inactive list.
- //
- PeriodicInactiveWasEmpty = FALSE;
- if (LIST_EMPTY(&(Controller->PeriodicInactiveListHead)) != FALSE) {
- PeriodicInactiveWasEmpty = TRUE;
- }
- //
- // Completely remove the endpoint from the schedule.
- //
- LIST_REMOVE(&(Endpoint->ListEntry));
- //
- // If there is more work left to do on this endpoint, then add it back to
- // the appropriate list.
- //
- if (LIST_EMPTY(&(Endpoint->TransferSetListHead)) == FALSE) {
- if ((Endpoint->TransferType == UsbTransferTypeControl) ||
- (Endpoint->TransferType == UsbTransferTypeBulk)) {
- INSERT_BEFORE(&(Endpoint->ListEntry),
- &(Controller->NonPeriodicReadyListHead));
- } else {
- ASSERT((Endpoint->TransferType == UsbTransferTypeInterrupt) ||
- (Endpoint->TransferType == UsbTransferTypeIsochronous));
- Transfer = DwhcipGetEndpointTransfer(Endpoint);
- ASSERT(Transfer != NULL);
- FrameNumber = DWHCI_READ_FRAME_NUMBER(Controller);
- //
- // When scheduling a complete split, schedule just ahead of the
- // start split's microframe.
- //
- if (Transfer->CompleteSplitCount != 0) {
- ASSERT(Endpoint->StartFrame != DWHCI_INVALID_FRAME);
- Base = Endpoint->StartFrame;
- Delta = 1 + Transfer->CompleteSplitCount;
- //
- // Otherwise the next (micro)frame is based on the current frame
- // and the poll rate, which is stored in (micro)frames.
- //
- } else {
- Base = FrameNumber;
- Delta = Endpoint->PollRate;
- }
- NextFrame = (Base + Delta) & DWHCI_FRAME_NUMBER_MAX;
- //
- // Start splits are not allowed to start in the 6th microframe and
- // get less time for the complete splits the later they get
- // scheduled within a frame. Schedule them all for the last
- // microframe.
- //
- if ((Endpoint->SplitControl != 0) &&
- (Endpoint->TransferType == UsbTransferTypeInterrupt) &&
- (Transfer->CompleteSplitCount == 0)) {
- NextFrame |= DWHCI_INTERRUPT_SPLIT_FRAME_MASK;
- }
- Endpoint->NextFrame = NextFrame;
- //
- // If the next frame has already come to pass and a channel is
- // assigned to the endpoint, then put the endpoint back on the
- // active list and do not free the channel.
- //
- if ((Channel != NULL) &&
- DWHCI_FRAME_GREATER_THAN_OR_EQUAL(FrameNumber, NextFrame)) {
- INSERT_BEFORE(&(Endpoint->ListEntry),
- &(Controller->PeriodicActiveListHead));
- FreeChannel = FALSE;
- //
- // Otherwise the endpoint must wait for the start of the
- // appropriate (micro)frame.
- //
- } else {
- if ((Controller->NextFrame == DWHCI_INVALID_FRAME) ||
- DWHCI_FRAME_LESS_THAN(NextFrame, Controller->NextFrame)) {
- Controller->NextFrame = NextFrame;
- }
- //
- // Activate the start-of-frame interrupt if the periodic
- // inactive list was empty when checked above.
- //
- if (PeriodicInactiveWasEmpty != FALSE) {
- CoreInterruptMask = DWHCI_READ_REGISTER(
- Controller,
- DwhciRegisterCoreInterruptMask);
- CoreInterruptMask |= DWHCI_CORE_INTERRUPT_START_OF_FRAME;
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterCoreInterruptMask,
- CoreInterruptMask);
- }
- INSERT_BEFORE(&(Endpoint->ListEntry),
- &(Controller->PeriodicInactiveListHead));
- }
- }
- //
- // Otherwise keep the endpoint off of all lists.
- //
- } else {
- Endpoint->NextFrame = 0;
- Endpoint->StartFrame = 0;
- Endpoint->ListEntry.Next = NULL;
- }
- //
- // Release the channel if the endpoint no longer needs it.
- //
- if ((Channel != NULL) && (FreeChannel != FALSE)) {
- DwhcipFreeChannel(Controller, Channel);
- }
- //
- // If this caused the inactive periodic list to become empty, then disable
- // the start-of-frame interrupts.
- //
- if ((PeriodicInactiveWasEmpty == FALSE) &&
- (LIST_EMPTY(&(Controller->PeriodicInactiveListHead)) != FALSE)) {
- CoreInterruptMask = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterCoreInterruptMask);
- CoreInterruptMask &= ~DWHCI_CORE_INTERRUPT_START_OF_FRAME;
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterCoreInterruptMask,
- CoreInterruptMask);
- Controller->NextFrame = DWHCI_INVALID_FRAME;
- }
- //
- // Note that the endpoint is not scheduled, so that it gets picked up the
- // next time the schedule is processed.
- //
- Endpoint->Scheduled = FALSE;
- return;
- }
- PDWHCI_TRANSFER
- DwhcipGetEndpointTransfer (
- PDWHCI_ENDPOINT Endpoint
- )
- /*++
- Routine Description:
- This routine returns the first transfer in the given endpoint's queue.
- Arguments:
- Endpoint - Supplies a pointer to an endpoint.
- Return Value:
- Returns a pointer to the first transfer on the endpoint or NULL if no such
- transfer exists.
- --*/
- {
- PDWHCI_TRANSFER Transfer;
- PDWHCI_TRANSFER_SET TransferSet;
- //
- // Find the next transfer for this endpoint. This transfer is the first
- // transfer in the first transfer set.
- //
- if (LIST_EMPTY(&(Endpoint->TransferSetListHead)) != FALSE) {
- return NULL;
- }
- TransferSet = LIST_VALUE(Endpoint->TransferSetListHead.Next,
- DWHCI_TRANSFER_SET,
- EndpointListEntry);
- if (LIST_EMPTY(&(TransferSet->TransferListHead)) != FALSE) {
- return NULL;
- }
- Transfer = LIST_VALUE(TransferSet->TransferListHead.Next,
- DWHCI_TRANSFER,
- SetListEntry);
- return Transfer;
- }
- KSTATUS
- DwhcipSoftReset (
- PDWHCI_CONTROLLER Controller
- )
- /*++
- Routine Description:
- This routine performs a soft reset of the DWHCI controller.
- Arguments:
- Controller - Supplies a pointer to the DWHCI controller state of the
- controller to reset.
- Return Value:
- Status code.
- --*/
- {
- ULONG CoreReset;
- //
- // Wait for the core reset register to report that the AHB is idle.
- //
- while (TRUE) {
- CoreReset = DWHCI_READ_REGISTER(Controller, DwhciRegisterCoreReset);
- if ((CoreReset & DWHCI_CORE_RESET_AHB_MASTER_IDLE) != 0) {
- break;
- }
- KeDelayExecution(FALSE, FALSE, 20 * MICROSECONDS_PER_MILLISECOND);
- }
- //
- // Execute the core soft reset by writing the soft reset bit to the
- // register.
- //
- CoreReset |= DWHCI_CORE_RESET_CORE_SOFT_RESET;
- DWHCI_WRITE_REGISTER(Controller, DwhciRegisterCoreReset, CoreReset);
- //
- // Now wait for the bit to clear.
- //
- while (TRUE) {
- CoreReset = DWHCI_READ_REGISTER(Controller, DwhciRegisterCoreReset);
- if ((CoreReset & DWHCI_CORE_RESET_CORE_SOFT_RESET) == 0) {
- break;
- }
- KeDelayExecution(FALSE, FALSE, 20 * MICROSECONDS_PER_MILLISECOND);
- }
- //
- // Execute a long delay to keep the DWHCI core in host mode.
- //
- KeDelayExecution(FALSE, FALSE, 200 * MICROSECONDS_PER_MILLISECOND);
- return STATUS_SUCCESS;
- }
- KSTATUS
- DwhcipInitializePhy (
- PDWHCI_CONTROLLER Controller
- )
- /*++
- Routine Description:
- This routine initializes the USB physical layer.
- Arguments:
- Controller - Supplies a pointer to the DWHCI controller state of the
- controller whose physical layer is to be initialized.
- Return Value:
- Status code.
- --*/
- {
- ULONG FullSpeedType;
- ULONG Hardware2;
- ULONG HighSpeedType;
- ULONG HostConfiguration;
- KSTATUS Status;
- ULONG UsbConfiguration;
- ULONG UsbFlags;
- ULONG UtmiWidth;
- //
- // Get the high speed type and the full speed type.
- //
- Hardware2 = DWHCI_READ_REGISTER(Controller, DwhciRegisterHardware2);
- HighSpeedType = Hardware2 & DWHCI_HARDWARE2_HIGH_SPEED_MASK;
- FullSpeedType = Hardware2 & DWHCI_HARDWARE2_FULL_SPEED_MASK;
- //
- // If this is a full speed controller, then initialize portions of physical
- // layer that are specific to full speed.
- //
- if (Controller->Speed == UsbDeviceSpeedFull) {
- //
- // Set the PHY select bit in the USB configuration register.
- //
- UsbConfiguration = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterUsbConfiguration);
- UsbConfiguration |= DWHCI_USB_CONFIGURATION_PHY_SELECT;
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterUsbConfiguration,
- UsbConfiguration);
- //
- // Perform a soft reset.
- //
- Status = DwhcipSoftReset(Controller);
- if (!KSUCCESS(Status)) {
- goto InitializePhysicalLayerEnd;
- }
- //
- // Set the full speed clock to 48 MHz in the host configuration
- // register.
- //
- HostConfiguration = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterHostConfiguration);
- HostConfiguration &= ~DWHCI_HOST_CONFIGURATION_CLOCK_RATE_MASK;
- HostConfiguration |= (DWHCI_HOST_CONFIGURATION_CLOCK_48_MHZ <<
- DWHCI_HOST_CONFIGURATION_CLOCK_RATE_SHIFT);
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterHostConfiguration,
- HostConfiguration);
- //
- // Otherwise, this is a high speed controller. Initialize high speed mode
- // in the physical layer.
- //
- } else {
- ASSERT(Controller->Speed == UsbDeviceSpeedHigh);
- ASSERT(HighSpeedType != DWHCI_HARDWARE2_HIGH_SPEED_NOT_SUPPORTED);
- //
- // Configure the USB based on the high speed type.
- //
- UsbConfiguration = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterUsbConfiguration);
- if (HighSpeedType == DWHCI_HARDWARE2_HIGH_SPEED_ULPI) {
- UsbConfiguration &= ~(DWHCI_USB_CONFIGURATION_PHY_INTERFACE_16 |
- DWHCI_USB_CONFIGURATION_DDR_SELECT |
- DWHCI_USB_CONFIGURATION_MODE_SELECT_MASK);
- UsbConfiguration |= DWHCI_USB_CONFIGURATION_MODE_SELECT_ULPI;
- } else {
- ASSERT((HighSpeedType == DWHCI_HARDWARE2_HIGH_SPEED_UTMI) ||
- (HighSpeedType == DWHCI_HARDWARE2_HIGH_SPEED_UTMI_ULPI));
- UsbConfiguration &= ~(DWHCI_USB_CONFIGURATION_MODE_SELECT_MASK |
- DWHCI_USB_CONFIGURATION_PHY_INTERFACE_16);
- UsbConfiguration |= DWHCI_USB_CONFIGURATION_MODE_SELECT_UTMI;
- //
- // Enable the physical interface 16 if the UTMI width is not 8 bit.
- //
- UtmiWidth = DWHCI_READ_REGISTER(Controller, DwhciRegisterHardware4);
- UtmiWidth &= ~DWHCI_HARDWARE4_UTMI_PHYSICAL_DATA_WIDTH_MASK;
- if (UtmiWidth != DWHCI_HARDWARE4_UTMI_PHYSICAL_DATA_WIDTH_8_BIT) {
- UsbConfiguration |= DWHCI_USB_CONFIGURATION_PHY_INTERFACE_16;
- }
- }
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterUsbConfiguration,
- UsbConfiguration);
- //
- // Perform a soft reset.
- //
- Status = DwhcipSoftReset(Controller);
- if (!KSUCCESS(Status)) {
- goto InitializePhysicalLayerEnd;
- }
- //
- // Set the high speed clock to 30-60 MHz in the host configuration
- // register.
- //
- HostConfiguration = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterHostConfiguration);
- HostConfiguration &= ~DWHCI_HOST_CONFIGURATION_CLOCK_RATE_MASK;
- HostConfiguration |= (DWHCI_HOST_CONFIGURATION_CLOCK_30_60_MHZ <<
- DWHCI_HOST_CONFIGURATION_CLOCK_RATE_SHIFT);
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterHostConfiguration,
- HostConfiguration);
- }
- //
- // Perform operations that are common to high and full speed.
- //
- UsbConfiguration = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterUsbConfiguration);
- UsbFlags = DWHCI_USB_CONFIGURATION_ULPI_FULL_SPEED_LOW_SPEED_SELECT |
- DWHCI_USB_CONFIGURATION_ULPI_CLOCK_SUSPEND_MODE;
- if ((HighSpeedType == DWHCI_HARDWARE2_HIGH_SPEED_ULPI) &&
- (FullSpeedType == DWHCI_HARDWARE2_FULL_SPEED_DEDICATED)) {
- UsbConfiguration |= UsbFlags;
- } else {
- UsbConfiguration &= ~UsbFlags;
- }
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterUsbConfiguration,
- UsbConfiguration);
- InitializePhysicalLayerEnd:
- return Status;
- }
- KSTATUS
- DwhcipInitializeUsb (
- PDWHCI_CONTROLLER Controller,
- ULONG UsbCapabilities
- )
- /*++
- Routine Description:
- This routine initialize the USB register for the DWHCI host controller.
- Arguments:
- Controller - Supplies a pointer to the DWHCI controller state of the
- controller whose USB register is to be initialized.
- UsbCapabilities - Supplies USB capability bits saved from the USB
- configuration register before the reset.
- Return Value:
- Status code.
- --*/
- {
- ULONG Hardware2;
- ULONG Mask;
- ULONG Mode;
- KSTATUS Status;
- ULONG UsbConfiguration;
- Mask = DWHCI_USB_CONFIGURATION_HNP_CAPABLE |
- DWHCI_USB_CONFIGURATION_SRP_CAPABLE;
- ASSERT((UsbCapabilities & ~Mask) == 0);
- UsbConfiguration = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterUsbConfiguration);
- UsbConfiguration &= ~Mask;
- Hardware2 = DWHCI_READ_REGISTER(Controller, DwhciRegisterHardware2);
- Mode = Hardware2 & DWHCI_HARDWARE2_MODE_MASK;
- Status = STATUS_SUCCESS;
- switch (Mode) {
- //
- // Not all controllers are made equal. Some that advertise HNP/SRP do
- // not actually support it and these bits must remain zero. Leave it up
- // to ACPI to set these bits. The supplied capabilities should hold the
- // values set by ACPI.
- //
- case DWHCI_HARDWARE2_MODE_HNP_SRP:
- UsbConfiguration |= UsbCapabilities;
- break;
- case DWHCI_HARDWARE2_MODE_SRP_ONLY:
- case DWHCI_HARDWARE2_MODE_SRP_DEVICE:
- case DWHCI_HARDWARE2_MODE_SRP_HOST:
- UsbConfiguration |= DWHCI_USB_CONFIGURATION_SRP_CAPABLE;
- break;
- case DWHCI_HARDWARE2_MODE_NO_HNP_SRP:
- case DWHCI_HARDWARE2_MODE_NO_SRP_DEVICE:
- case DWHCI_HARDWARE2_MODE_NO_SRP_HOST:
- break;
- default:
- ASSERT(FALSE);
- Status = STATUS_INVALID_CONFIGURATION;
- break;
- }
- if (KSUCCESS(Status)) {
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterUsbConfiguration,
- UsbConfiguration);
- }
- return Status;
- }
- KSTATUS
- DwhcipInitializeHostMode (
- PDWHCI_CONTROLLER Controller,
- ULONG ReceiveFifoSize,
- ULONG NonPeriodicTransmitFifoSize,
- ULONG PeriodicTransmitFifoSize
- )
- /*++
- Routine Description:
- This routine initializes the DWHCI controller in host mode.
- Arguments:
- Controller - Supplies a pointer to the DWHCI controller state of the
- controller whose USB register is to be initialized.
- ReceiveFifoSize - Supplies the receive FIFO size to set if the FIFO's are
- dynamic.
- NonPeriodicTransmitFifoSize - Supplies the non-periodic transmit FIFO size
- to set if the FIFO's are dynamic. This includes the FIFO offset.
- PeriodicTransmitFifoSize - Supplies the periodic transmit FIFO size to
- set if the FIFO's are dynamic. This includes the FIFO offset.
- Return Value:
- Status code.
- --*/
- {
- PDWHCI_CHANNEL Channels;
- ULONG Control;
- ULONG Hardware2;
- ULONG HostConfiguration;
- ULONG Index;
- ULONG OtgControl;
- ULONG PortStatus;
- KSTATUS Status;
- //
- // Restart the PHY clock.
- //
- DWHCI_WRITE_REGISTER(Controller, DwhciRegisterPowerAndClock, 0);
- //
- // Initialize the speed of the host controller.
- //
- if (Controller->Speed == UsbDeviceSpeedFull) {
- HostConfiguration = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterHostConfiguration);
- HostConfiguration |= DWHCI_HOST_CONFIGURATION_FULL_SPEED_LOW_SPEED_ONLY;
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterHostConfiguration,
- HostConfiguration);
- }
- //
- // If dynamic FIFO sizing is allowed, then set the FIFO sizes and
- // starting addresses using the provided values. Otherwise use what is
- // programmed in the registers.
- //
- Hardware2 = DWHCI_READ_REGISTER(Controller, DwhciRegisterHardware2);
- if ((Hardware2 & DWHCI_HARDWARE2_DYNAMIC_FIFO) != 0) {
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterReceiveFifoSize,
- ReceiveFifoSize);
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterNonPeriodicFifoSize,
- NonPeriodicTransmitFifoSize);
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterPeriodicFifoSize,
- PeriodicTransmitFifoSize);
- }
- //
- // Clear the Host Set HNP Enable in the OTG Control Register.
- //
- OtgControl = DWHCI_READ_REGISTER(Controller, DwhciRegisterOtgControl);
- OtgControl &= ~DWHCI_OTG_CONTROL_HOST_SET_HNP_ENABLE;
- DWHCI_WRITE_REGISTER(Controller, DwhciRegisterOtgControl, OtgControl);
- //
- // Flush the FIFOs.
- //
- DwhcipFlushFifo(Controller, TRUE, DWHCI_CORE_RESET_TRANSMIT_FIFO_FLUSH_ALL);
- DwhcipFlushFifo(Controller, FALSE, 0);
- //
- // First disable all the channels.
- //
- for (Index = 0; Index < Controller->ChannelCount; Index += 1) {
- Control = DWHCI_READ_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterControl,
- Index);
- Control &= ~(DWHCI_CHANNEL_CONTROL_ENDPOINT_DIRECTION_IN |
- DWHCI_CHANNEL_CONTROL_ENABLE);
- Control |= DWHCI_CHANNEL_CONTROL_DISABLE;
- DWHCI_WRITE_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterControl,
- Index,
- Control);
- }
- //
- // Reset every channel and add them to the list of free channels.
- //
- Channels = (PDWHCI_CHANNEL)(Controller->Channel);
- for (Index = 0; Index < Controller->ChannelCount; Index += 1) {
- Status = DwhcipResetChannel(Controller, Index);
- if (!KSUCCESS(Status)) {
- goto InitializeHostModeEnd;
- }
- //
- // Since the channel was just disabled, add it to the free list.
- //
- ASSERT(Channels[Index].Endpoint == NULL);
- INSERT_BEFORE(&(Channels[Index].FreeListEntry),
- &(Controller->FreeChannelListHead));
- }
- //
- // Initialize the power for the host controller.
- //
- PortStatus = DWHCI_READ_REGISTER(Controller, DwhciRegisterHostPort);
- if ((PortStatus & DWHCI_HOST_PORT_POWER) == 0) {
- PortStatus |= DWHCI_HOST_PORT_POWER;
- PortStatus &= ~DWHCI_HOST_PORT_WRITE_TO_CLEAR_MASK;
- DWHCI_WRITE_REGISTER(Controller, DwhciRegisterHostPort, PortStatus);
- }
- //
- // Disable all channel interrupts.
- //
- DWHCI_WRITE_REGISTER(Controller, DwhciRegisterHostChannelInterruptMask, 0);
- InitializeHostModeEnd:
- return Status;
- }
- VOID
- DwhcipFlushFifo (
- PDWHCI_CONTROLLER Controller,
- BOOL TransmitFifo,
- ULONG TransmitFifoMask
- )
- /*++
- Routine Description:
- This routine flushes either the one receive FIFO or the specified transmit
- FIFO.
- Arguments:
- Controller - Supplies a pointer to the DWHCI controller state of the
- controller whose FIFO is to be flushed.
- TransmitFifo - Supplies a boolean indicating whether or not the flush is
- for a transmit FIFO.
- TransmitFifoMask - Supplies a bitmask of transmission FIFOs to flush. See
- DWHCI_CORE_RESET_TRAMSIT_FIFO_FLUSH_* for available options.
- Return Value:
- None.
- --*/
- {
- ULONG CoreResetMask;
- ULONG CoreResetValue;
- //
- // Write the core reset register to initiate the FIFO flush.
- //
- if (TransmitFifo == FALSE) {
- CoreResetValue = DWHCI_CORE_RESET_RECEIVE_FIFO_FLUSH;
- CoreResetMask = CoreResetValue;
- } else {
- ASSERT((TransmitFifoMask &
- ~DWHCI_CORE_RESET_TRANSMIT_FIFO_FLUSH_MASK) ==
- 0);
- CoreResetValue = TransmitFifoMask;
- CoreResetMask = DWHCI_CORE_RESET_TRANSMIT_FIFO_FLUSH;
- }
- DWHCI_WRITE_REGISTER(Controller, DwhciRegisterCoreReset, CoreResetValue);
- //
- // Wait for the mask to go to zero.
- //
- while (TRUE) {
- CoreResetValue = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterCoreReset);
- if ((CoreResetValue & CoreResetMask) == 0) {
- break;
- }
- KeDelayExecution(FALSE, FALSE, 10);
- }
- KeDelayExecution(FALSE, FALSE, 10);
- return;
- }
- KSTATUS
- DwhcipResetChannel (
- PDWHCI_CONTROLLER Controller,
- ULONG ChannelNumber
- )
- /*++
- Routine Description:
- This routine resets the given channel for the supplied DWHCI controller.
- Arguments:
- Controller - Supplies a pointer to the controller state of the DWHCI
- controller whose channel is to be reset.
- ChannelNumber - Supplies the number of the channel to be reset.
- Return Value:
- Status code.
- --*/
- {
- ULONG Control;
- //
- // Reset the channel by setting both the enable and disable bits and then
- // wait for the enable bit to clear.
- //
- Control = DWHCI_READ_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterControl,
- ChannelNumber);
- Control &= ~DWHCI_CHANNEL_CONTROL_ENDPOINT_DIRECTION_IN;
- Control |= (DWHCI_CHANNEL_CONTROL_ENABLE |
- DWHCI_CHANNEL_CONTROL_DISABLE);
- DWHCI_WRITE_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterControl,
- ChannelNumber,
- Control);
- while (TRUE) {
- Control = DWHCI_READ_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterControl,
- ChannelNumber);
- if ((Control & DWHCI_CHANNEL_CONTROL_ENABLE) == 0) {
- break;
- }
- KeDelayExecution(FALSE, FALSE, 10);
- }
- return STATUS_SUCCESS;
- }
- BOOL
- DwhcipHaltChannel (
- PDWHCI_CONTROLLER Controller,
- PDWHCI_CHANNEL Channel
- )
- /*++
- Routine Description:
- This routine halts the given channel that belongs to the specified host
- controller.
- Arguments:
- Controller - Supplies a pointer to the controller state of the DWHCI
- controller whose channel is to be halted.
- Channel - Supplies a pointer to the channel to be halted.
- Return Value:
- Returns TRUE if the channel was successfully halted, or FALSE if an
- asynchronous halt was scheduled.
- --*/
- {
- ULONG ChannelControl;
- ULONG Interrupts;
- ASSERT(Channel->Endpoint != NULL);
- //
- // Make sure that the channel will only interrupt if it is halted.
- //
- DWHCI_WRITE_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterInterruptMask,
- Channel->ChannelNumber,
- DWHCI_CHANNEL_INTERRUPT_HALTED);
- //
- // Clear any other interrupts.
- //
- DWHCI_WRITE_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterInterrupt,
- Channel->ChannelNumber,
- ~DWHCI_CHANNEL_INTERRUPT_HALTED);
- //
- // If the channel is not currently enabled, then it is not active. There
- // should be no need to halt it.
- //
- ChannelControl = DWHCI_READ_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterControl,
- Channel->ChannelNumber);
- if ((ChannelControl & DWHCI_CHANNEL_CONTROL_ENABLE) == 0) {
- return TRUE;
- }
- //
- // Enable host level interrupts for this channel.
- //
- Interrupts = DWHCI_READ_REGISTER(Controller,
- DwhciRegisterHostChannelInterruptMask);
- Interrupts |= (1 << Channel->ChannelNumber);
- DWHCI_WRITE_REGISTER(Controller,
- DwhciRegisterHostChannelInterruptMask,
- Interrupts);
- //
- // Reset the channel by enabling and disabling it.
- //
- ChannelControl |= DWHCI_CHANNEL_CONTROL_DISABLE |
- DWHCI_CHANNEL_CONTROL_ENABLE;
- DWHCI_WRITE_CHANNEL_REGISTER(Controller,
- DwhciChannelRegisterControl,
- Channel->ChannelNumber,
- ChannelControl);
- return FALSE;
- }
|