UsbEhci.Mod 128 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250
  1. MODULE UsbEhci; (** AUTHOR "staubesv"; PURPOSE "USB Enhanced Host Controller Driver"; *)
  2. (**
  3. * Bluebottle USB Enhanced Host Controller Driver
  4. * Implements the UsbHcdi host controller driver interface (HCDI)
  5. *
  6. * Usage:
  7. *
  8. * This module provides the core implementation of the EHCI driver. Finding EHCI controllers was moved to a separate
  9. * module to increase flexibility. If your EHCI controller is accessible with PCI, the command:
  10. * UsbEhciPCI.Install ~
  11. * will install EHCI drivers as needed. To remove them, simply uninstall Ehci modules:
  12. * SystemTools.Free UsbEhciPCI UsbEhci ~
  13. *
  14. * In other cases, specific modules can be written, that will instanciate drivers as needed.
  15. *
  16. * References:
  17. * Enhanced Host Controller Interface Specification for Universal Serial Bus, Rev. 1.0
  18. *
  19. * Notes:
  20. * - 64bit control data structures: This driver doesn`t support real 64bit operation. If the host controller indicates 64bit capabilities, i.e. all
  21. * pointers used for control data structures as qTd, QHs and buffers are 64bit memory addresses, the upper 32bit of the address are just set to zero
  22. * to selected the 0..4GB segment.
  23. *
  24. * TODOs:
  25. * - FSTN save path pointers
  26. * - use sparse tree for more fine granular scheduling
  27. * - implement isochronous transfers
  28. * - DataToggle would not work for Control Transfers spanning multiple TDs
  29. *
  30. * History:
  31. *
  32. * 24.11.2005 First release (staubesv)
  33. * 15.12.2005 Moved buffer checks to UsbHcdi.Mod (staubesv)
  34. * 05.01.2006 Fixed EnhancedHostController.DeleteTranfer (staubesv)
  35. * 10.01.2006 H/W scatter/gather support implemented, fixed bug when sending > 16KB blocks (staubesv)
  36. * 16.01.2006 FlushPCI added (staubesv)
  37. * 08.01.2006 Added ScheduleOn, more safe TD unlinked/QH removal (staubesv)
  38. * 01.03.2006 Fixed critical bug in CreateTDList (staubesv)
  39. * 02.03.2006 Implemented port indicator control (staubesv)
  40. * 03.04.2006 Improved interrupt sharing (staubesv)
  41. * 05.04.2006 Fixed BIOS-to-OS handoff (staubesv)
  42. * 28.06.2006 Use KernelLog.Hex instead of UsbHcdi.PrintHex (staubesv)
  43. * 30.06.2006 Bugfix in InitFramelist: Also reset QH structure for isochronousQH, fixed bug when removing periodic QHs (staubesv)
  44. * 03.06.2006 UpdatePipe removed (staubesv)
  45. * 04.06.2006 Allow LinkTD to automatically clear halt condition (staubesv)
  46. * 20.07.2006 Release HC ownership when HC driver is shutdown, introduced OverrideHcOwnership constant (staubesv)
  47. * 03.08.2006 Adapted to UsbHcdi, fixed control transfer > 1TD (staubesv)
  48. * 13.11.2006 UpdatePipeStatus: Set pipe.status to Usbdi.Stalled when a stall is detected (staubesv)
  49. *)
  50. IMPORT SYSTEM, KernelLog, Machine, Kernel, Objects, Modules, Locks, UsbHcdi, Usbdi, Debug := UsbDebug, UsbVarTdAlloc, UsbBuffers;
  51. CONST
  52. Description * = "USB Enhanced Host Controller";
  53. (* Some configuration stuff *)
  54. HcInterruptThreshold = 01H; (* Maximum rate at which the host controller will issue interrupts (in microframes, 125 microseconds) *)
  55. HcFrameListSize = 1024;
  56. (** Size of TD buffers *)
  57. TdBufferSize = 4096;
  58. ScatterGatherListSize = 4200; (* Number of entries in scatter/gather list -> limits the maximum transfer size *)
  59. (* How many transaction of a single queue head in the asynchronous schedule list is the host controller allowed to execute
  60. within a micro-frame? Valid values: [0..3] (0 disables the feature). Will only be set if the HC supports the Asynchronous Schedule Park Mode. Higher values
  61. should result in higher performance. *)
  62. HcAsyncParkModeCount = 3;
  63. (* Host Controller Capability Registers *)
  64. HcCapLength = 00H;
  65. HcCapHciVersion = 02H;
  66. HcCapSparams = 04H;
  67. HcCapCparams * = 08H;
  68. HcCapPortroute = 0CH;
  69. (* Host Controller Operational Registers *)
  70. HcUsbCmd * = 00H;
  71. HcUsbSts * = 04H;
  72. HcUsbIntr * = 08H;
  73. HcFrIndex * = 0CH;
  74. HcCtrlDsSegment * = 10H;
  75. HcPeriodicListBase * = 14H;
  76. HcAsyncListAddr * = 18H;
  77. HcConfigFlag * = 40H;
  78. HcPortSc * = 44H;
  79. (* HcUsbCmd register fields *)
  80. CmdInterruptThreshold * = {16..23};
  81. CmdAsyncSchedParkMode * = {11};
  82. CmdAsyncSchedParkCount * = {8..9};
  83. CmdLightHcReset * = {7}; (* Note: optional *)
  84. CmdAsyncAdvDoorbell * = {6};
  85. CmdAsyncSchedEnable * = {5};
  86. CmdPeriodicSchedEnable * = {4};
  87. CmdFrameListSize * = {2 .. 3};
  88. CmdHcReset * = {1};
  89. CmdRunStop * = {0};
  90. CmdReserved * = {10} + {12..15} + {24..31};
  91. (* HcUsbSts register fields *)
  92. StsAsyncSchedule * = {15};
  93. StsPeriodicSchedule * = {14};
  94. StsReclamation * = {13};
  95. StsHcHalted * = {12};
  96. (* HcUsbSts & HcUsbIntr common fields *)
  97. StsAsyncAdvance * = {5};
  98. StsHostSystemError * = {4};
  99. StsFrameListRollover * = {3};
  100. StsPortChange * = {2};
  101. StsUsbError * = {1};
  102. StsUsbInterrupt * = {0};
  103. (* Port Status & Control register, EHCIspec p. 26-30 *)
  104. PscWakeOnOvercurrent = {22};
  105. PscWakeOnDisconnect = {21};
  106. PscWakeOnConnect = {20};
  107. PscTestControl = {16..19};
  108. PscIndicatorControl = {14..15};
  109. PscPortOwner = {13};
  110. PscPortPower * = {12};
  111. PscLineStatus = {10..11};
  112. PscPortReset = {8};
  113. PscSuspend = {7};
  114. PscForcePortResume = {6};
  115. PscOvercurrentChange = {5};
  116. PscOvercurrentActive = {4};
  117. PscPortEnableChange = {3};
  118. PscPortEnable *= {2};
  119. PscConnectStatusChange = {1};
  120. PscCurrentConnectStatus = {0};
  121. PscReserved = {9} + {23..31};
  122. PscChangeMask = {1, 3, 5};
  123. (* Queue Element Transfer Descriptor; must be 32byte aligned *)
  124. (* Offsets *)
  125. QtdNextQtdPointer = 00H;
  126. QtdAltNextQtdPointer = 04H;
  127. QtdToken = 08H;
  128. QtdBufferPtr0 = 0CH;
  129. QtdBufferPtr1 = 10H;
  130. QtdBufferPtr2 = 14H;
  131. QtdBufferPtr3 = 18H;
  132. QtdBufferPtr4 = 1CH;
  133. QtdExtBufferPtr0 = 20H;
  134. QtdExtBufferPtr1 = 24H;
  135. QtdExtBufferPtr2 = 28H;
  136. QtdExtBufferPtr3 = 2CH;
  137. QtdExtBufferPtr4 = 30H;
  138. (* Masks *)
  139. QtdTerminate = {0};
  140. QtdBufferPtr = {12..31};
  141. (* qTD Token *)
  142. QtdDataToggle = {31};
  143. QtdBytesToTransfer = {16..30};
  144. QtdIoc = {15}; (* Interrupt on complete *)
  145. QtdCurrentPage = {12..14};
  146. QtdErrorCounter = {10..11};
  147. QtdPidCode = {8..9};
  148. QtdStatus = {0..7};
  149. (* Isochronous Transfer Descriptor *)
  150. ItdNextLinkPointer = 00H;
  151. ItdTransaction0 = 04H;
  152. ItdBufferPtr0 = 024H;
  153. ItdBufferPtr1 = 028H;
  154. ItdBufferPtr2 = 02CH;
  155. ItdExtBufferPtr0 = 40H;
  156. (* ITD Transaction *)
  157. ItdTransactionStatus = {28..31};
  158. ItdTransactionLength = {16..27};
  159. ItdTransactionIoc = {15};
  160. ItdTransactionPg = {12..14};
  161. ItdTransactionOffset = {0..11};
  162. (* ITD Buffer Pointers *)
  163. ItdBufferPtr = {12..31};
  164. (* ItdBufferPtr0 *)
  165. ItdEndPt = {8..11};
  166. ItdReserved = {7};
  167. ItdDevAdr = {0..6};
  168. (* ItdBufferPtr1 *)
  169. ItdIn = {11};
  170. ItdMaxPacketSize = {0..10};
  171. (* ItdBufferPtr2 *)
  172. ItdMult = {0..1};
  173. (* ITD Transaction Status *)
  174. ItdStatus = {28..31};
  175. ItdActive = {31};
  176. ItdDataBufferError = {30};
  177. ItdBabbleDetected = {29};
  178. ItdTransactionError = {28};
  179. (* Queue Head *)
  180. (* Offsets *)
  181. QhHorizontalLinkPointer = 00H;
  182. QhEpCapabilities1 = 04H;
  183. QhEpCapabilities2 = 08H;
  184. QhCurrentQtdPointer = 0CH;
  185. QhNextQtdPointer = 10H;
  186. QhAltNextQtdPointer = 14H;
  187. QhQtdToken = 18H;
  188. QhBufferPointer0 = 1CH;
  189. QhBufferPointer1 = 20H;
  190. QhBufferPointer2 = 24H;
  191. QhBufferPointer3 = 28H;
  192. QhBufferPointer4 = 2CH;
  193. QhExtBufferPointer0 = 30H;
  194. QhExtBufferPointer1 = 34H;
  195. QhExtBufferPointer2 = 38H;
  196. QhExtBufferPointer3 = 3CH;
  197. QhExtBufferPointer4 = 40H;
  198. (* Masks *)
  199. (* Queue Head Horizontal Link Pointer *)
  200. QhTyp = {1..2};
  201. QhTypItd = 0;
  202. QhTypQh = 1;
  203. QhTypSitd = 2;
  204. QhTypFstn = 3; (* Frame span traversal node *)
  205. QhTerminate = {0};
  206. (* Queue Head Endpoint Capabilities *)
  207. (* Dword 1 *)
  208. QhNakCountReload = {28..31};
  209. QhControlEndpointFlag = {27};
  210. QhMaxPacketLen = {16..26};
  211. QhHeadOfReclamation = {15};
  212. QhDataToggleControl = {14};
  213. QhEndpointSpeed = {12..13};
  214. QhEndpointNbr = {8..11};
  215. QhInactivate = {7};
  216. QhDeviceAddress = {0..6};
  217. (* Dword 2 *)
  218. QhMultiplier = {30..31}; (* High-Bandwidth Pipe Muliplier *)
  219. QhPortNbr = {23..29};
  220. QhHubAddr = {16..22};
  221. QhSplitCMask = {8..15};
  222. QhSMask = {0..7};
  223. (* Periodic Frame Span Traversal Node (FSTN) *)
  224. (* FSTN offsets *)
  225. FstnNormalPathLinkPointer = 0;
  226. FstnBackPathLinkPointer = 4;
  227. (* Status fields of qTD Token *)
  228. TdActive = {7}; (* If set, the HC will process the qTD *)
  229. TdHalted = {6}; (* Caused by babble, error counter transition from one to zero or STALL handshake. Will also clear TdActive. *)
  230. TdDataBufferError = {5}; (* Buffer overrun or underrun *)
  231. TdBabbleDetected = {4}; (* Babble. Will also set TdHalted *)
  232. TdTransactionError = {3}; (* No valid response from device during status update (Timeout, CRC errir, PID wrong...) *)
  233. TdMissedMicroFrame = {2};
  234. TdSplitTransactionState = {1};
  235. TdPingState = {0};
  236. (* Periodic Frame Span Traversal Node *)
  237. FstnNormalPathLink = 00H;
  238. FstnBackPathLink = 04H;
  239. (* Packet Identifier codes *)
  240. PidOut = 0;
  241. PidIn = 1;
  242. PidSetup = 2;
  243. PageSize = 4096;
  244. Polling = FALSE;
  245. TYPE
  246. Qh = POINTER {UNSAFE,UNTRACED} TO RECORD
  247. horizontalLink: Qh;
  248. epCapabilities: ARRAY 2 OF LONGINT;
  249. current, next, alternate: ADDRESS;
  250. token: LONGINT;
  251. buffers: ARRAY 5 OF LONGINT;
  252. (** For 64bits datastructures *)
  253. extBuffers: ARRAY 5 OF LONGINT;
  254. END;
  255. Qtd = POINTER {UNSAFE,UNTRACED} TO RECORD
  256. next, alternateNext: ADDRESS;
  257. token: LONGINT;
  258. buffers: ARRAY 5 OF LONGINT;
  259. (** For 64bits datastructures *)
  260. extBuffers: ARRAY 5 OF LONGINT;
  261. END;
  262. Itd = POINTER {UNSAFE,UNTRACED} TO RECORD
  263. next: ADDRESS;
  264. transactions: ARRAY 8 OF LONGINT;
  265. buffers: ARRAY 7 OF LONGINT;
  266. END;
  267. EnhancedHostController * = OBJECT (UsbHcdi.Hcd)
  268. VAR
  269. (** Framelist array. Aligned on 4096, at max 4096 B long *)
  270. framelist *: UsbHcdi.AlignedMemSpace;
  271. (** Number of 4 B elements actually used in the framelist *)
  272. framelistSize: LONGINT;
  273. (** Lock for syncrhonizing framelist operations *)
  274. framelistLock: Locks.Lock;
  275. (** Offset for safe modification of the framelist. Do not modify the framelist less than 1 + framelistOfs of the HC current position. *)
  276. framelistOfs: LONGINT;
  277. pwcr * : LONGINT; (* Port Wake Capability Register; Not implemented by device if pwcr = 0 *)
  278. (* Information from Host Controller Capability Registers *)
  279. (* HCSPARAMS - Structural Parameters *)
  280. capDebugPortNumber : LONGINT; (* 0: n/a, other: number of debug port (0-15)*)
  281. capPortIndicators : BOOLEAN; (* Do the ports support port indicator control? *)
  282. capNbrOfCompanionHc : LONGINT; (* How many companion host controllers are present (0-15) *)
  283. capPortsPerCompanion : LONGINT; (* Number of ports supported per companion host controller *)
  284. capPortRoutingRules : BOOLEAN; (* Port routing rules *)
  285. capPortPowerControl : BOOLEAN; (* Does the HC support Port Power Control? *)
  286. capNbrOfPorts : LONGINT; (* Number of physical downstream ports implemented by this host controller *)
  287. (* HCCPARAMS - Capability Parameters *)
  288. capIsoSchedThreshold : LONGINT; (* Isochronous Schedule Threshold *)
  289. capAsynchSchedPark : BOOLEAN; (* Does the controller support the park feature for high-speed transfers? *)
  290. capProgrammableFLG : BOOLEAN; (* FALSE: use default (1024); TRUE: Frame List size is programmable *)
  291. cap64bit : BOOLEAN; (* 32 / 64 bit memory pointers in the data structures *)
  292. (* EHCI Extended Capabilities Pointer. Used in relation with USB legacy support *)
  293. eecp : LONGINT;
  294. (* The size of control data structures is dependent on whether the HC uses 32bit or 64bit address pointers as indicated
  295. by the cap64bit field *)
  296. sizeQtd, alignQtd : LONGINT;
  297. sizeQh, alignQh : LONGINT;
  298. sizeItd, alignItd: LONGINT;
  299. (* HC Companion Port Route Descriptor, NIL of not available.
  300. If the capPortRoutingRules is TRUE, the HC provides a description of which port is routed to
  301. which companion HC. *)
  302. hcportroute : POINTER TO ARRAY OF LONGINT;
  303. (* queue heads *)
  304. isochronousQh* : LONGINT;
  305. interruptQh : POINTER TO ARRAY 11 OF LONGINT;
  306. (* this array will provide the 16byte aligned TD's for controlTD, bulkTD, isochronousTD and interruptTD[] *)
  307. qhlist : UsbHcdi.AlignedMemSpace;
  308. (** Allocator for TD and QH datastructures *)
  309. allocator: UsbVarTdAlloc.Allocator;
  310. (* The Asynchronous Advance Doorbell interrupt is always enabled by this driver. Since the interrupt handler will
  311. clear the bit that were set when it was invoked, it sets hcHandshake to TRUE, so its sticky *)
  312. hcHandshake * : BOOLEAN;
  313. (* Set of all currently enabled interrupts *)
  314. interruptsEnabled * : SET;
  315. handler: Handler;
  316. (** Enable power for the specified port *)
  317. PROCEDURE EnablePortPower*(port : LONGINT);
  318. VAR status : SET;
  319. BEGIN
  320. status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  321. SYSTEM.PUT32(ports[port], status - PscChangeMask + PscPortPower); FlushPCI;
  322. END EnablePortPower;
  323. (** Disable power for the specified port *)
  324. PROCEDURE DisablePortPower*(port : LONGINT);
  325. VAR status : SET;
  326. BEGIN
  327. status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  328. SYSTEM.PUT32(ports[port], status - PscChangeMask - PscPortPower); FlushPCI;
  329. END DisablePortPower;
  330. (** Enable the specified port.
  331. The EHCI host controllers do not explicitly support a port enable command. The port will be automatically enabled
  332. by the host controller after a port reset, if a high-speed capable device is attached to it *)
  333. PROCEDURE ResetAndEnablePort*(port : LONGINT) : BOOLEAN;
  334. VAR status : SET; mtimer : Kernel.MilliTimer;
  335. BEGIN
  336. status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  337. SYSTEM.PUT32(ports[port], status - PscChangeMask + PscPortReset - PscPortEnable); FlushPCI;
  338. Wait(UsbHcdi.PortResetTime); (* >= 10ms, USBspec *)
  339. SYSTEM.PUT32(ports[port], status - PscChangeMask - PscPortReset); FlushPCI;
  340. Wait(2+1); (* 2ms recovery interval according EHCIspec, p. 28 *)
  341. (* The host controller should have automatically enabled this port *)
  342. Kernel.SetTimer(mtimer, UsbHcdi.PortEnableTimeout);
  343. REPEAT
  344. status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  345. UNTIL (status * PscPortEnable # {}) OR Kernel.Expired(mtimer);
  346. RETURN status * PscPortEnable # {};
  347. END ResetAndEnablePort;
  348. (** Disable the specified port. *)
  349. PROCEDURE DisablePort*(port : LONGINT);
  350. VAR status : SET;
  351. BEGIN
  352. status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  353. SYSTEM.PUT32(ports[port], status - PscChangeMask- PscPortEnable);
  354. FlushPCI;
  355. END DisablePort;
  356. (** Suspend the specified port (selective suspend). *)
  357. PROCEDURE SuspendPort*(port : LONGINT) : BOOLEAN;
  358. VAR status : SET;
  359. BEGIN
  360. status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  361. IF (status * PscPortEnable # {}) & (status * PscPortOwner = {}) THEN
  362. SYSTEM.PUT32(ports[port], status - PscChangeMask + PscSuspend);
  363. FlushPCI;
  364. RETURN TRUE;
  365. END;
  366. RETURN FALSE;
  367. END SuspendPort;
  368. (** Resume a selectively suspended port. *)
  369. PROCEDURE ResumePort*(port : LONGINT) : BOOLEAN;
  370. VAR status : SET; timer : Kernel.Timer;
  371. BEGIN
  372. status := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  373. IF status * CmdRunStop = {} THEN
  374. (* HC must be running when resume a port. Otherwise, the device would automatically re-renter
  375. the suspended mode in 10 ms *)
  376. SYSTEM.PUT32(iobase + HcUsbCmd, status + CmdRunStop);
  377. FlushPCI;
  378. END;
  379. status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  380. IF (status * PscSuspend # {}) & (status * PscPortOwner = {}) THEN
  381. SYSTEM.PUT32(ports[port], status - PscChangeMask + PscForcePortResume);
  382. FlushPCI;
  383. NEW(timer); timer.Sleep(20); (* EHCI p. 60 *)
  384. SYSTEM.PUT32(ports[port], status - PscChangeMask - PscForcePortResume);
  385. FlushPCI;
  386. END;
  387. RETURN SYSTEM.VAL(SET, SYSTEM.GET32(ports[port])) * PscSuspend = {};
  388. (* TODO: write 1 to PORTSC Force resume bit if port is suspended; first wait 10ms (EHCIp59)*)
  389. END ResumePort;
  390. (** Suspend all ports and then stop the host controller. *)
  391. PROCEDURE Suspend*;
  392. VAR dword : SET; i : LONGINT; ignore : BOOLEAN;
  393. BEGIN
  394. (* Suspend all individual ports *)
  395. FOR i := 0 TO portCount - 1 DO ignore := SuspendPort(i); END;
  396. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd)); (* Stop HC *)
  397. SYSTEM.PUT32(iobase + HcUsbCmd, dword - CmdRunStop);
  398. FlushPCI;
  399. (* Put HC in lower device state via the PCI power management interface *)
  400. END Suspend;
  401. (** Restart the host controller and selectively resume all suspended ports. *)
  402. PROCEDURE Resume*() : BOOLEAN;
  403. VAR dword : SET; i : LONGINT; res : BOOLEAN;
  404. BEGIN
  405. (* Re-start the HC *)
  406. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  407. SYSTEM.PUT32(iobase + HcUsbCmd, dword + CmdRunStop);
  408. FlushPCI;
  409. (* Resume all individual ports *)
  410. res := TRUE;
  411. FOR i := 0 TO portCount - 1 DO
  412. IF ~ResumePort(i) THEN res := FALSE; END;
  413. END;
  414. RETURN res;
  415. END Resume;
  416. (**
  417. * Get the status of the specified port.
  418. * Registers which indicate status changes are reset by GetPortStatus.
  419. * Note: UsbHcdi.HighSpeed will only be correctly set when the port is enabled. The hub driver
  420. * takes care of this special behaviour by getting the port status again after it has enabled the port.
  421. * @param port Port to get the status of
  422. * @return Port status
  423. *)
  424. PROCEDURE GetPortStatus*(port : LONGINT; ack : BOOLEAN) : SET;
  425. VAR status, s : SET;
  426. BEGIN
  427. s := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  428. (* Clear all bits that reported a change event; the correspondig register are R/WC *)
  429. IF ack & ((s * PscChangeMask) # {}) THEN SYSTEM.PUT32(ports[port], s); END; FlushPCI;
  430. status := {};
  431. IF s * PscCurrentConnectStatus # {} THEN status := status + UsbHcdi.PortStatusDevicePresent; END;
  432. IF s * PscPortEnable # {} THEN status := status + UsbHcdi.PortStatusEnabled END;
  433. IF s * PscSuspend # {} THEN status := status + UsbHcdi.PortStatusSuspended END;
  434. IF s * PscOvercurrentActive # {} THEN status := status + UsbHcdi.PortStatusOverCurrent END;
  435. IF s * PscPortReset # {} THEN status := status + UsbHcdi.PortStatusReset END;
  436. IF s * PscPortPower # {} THEN status := status + UsbHcdi.PortStatusPowered END;
  437. IF s * PscConnectStatusChange # {} THEN status := status + UsbHcdi.PortStatusConnectChange END;
  438. IF s * PscPortEnableChange # {} THEN status := status + UsbHcdi.PortStatusEnabledChange END;
  439. IF s * PscOvercurrentChange # {} THEN status := status + UsbHcdi.PortStatusOverCurrentChange END;
  440. IF s * PscTestControl # {} THEN status := status + UsbHcdi.PortStatusTestControl END;
  441. IF s * PscIndicatorControl # {} THEN status := status + UsbHcdi.PortStatusIndicatorControl END;
  442. IF s * PscWakeOnOvercurrent # {} THEN status := status + UsbHcdi.PortStatusWakeOnOvercurrent; END;
  443. IF s * PscWakeOnDisconnect # {} THEN status := status + UsbHcdi.PortStatusWakeOnDisconnect; END;
  444. IF s * PscWakeOnConnect # {} THEN status := status + UsbHcdi.PortStatusWakeOnConnect; END;
  445. IF s * PscPortOwner # {} THEN status := status + UsbHcdi.PortStatusPortOwner; END;
  446. (* When a device is attached to a port of the root hub, the hub driver will try to reset and enable the port.
  447. The EHCI HC only enables the port if the connected device is a high-speed device which is determined during
  448. the reset. So if a device is attached to the port, the port is not in reset and it's enabled, it is a high-speed device *)
  449. IF (s * PscPortEnable = {}) & (s * PscCurrentConnectStatus # {}) & (s * PscPortPower # {}) & (s * {10} # {}) THEN (* Lowspeed device connected *)
  450. status := status + UsbHcdi.PortStatusLowSpeed;
  451. ELSIF (s * PscCurrentConnectStatus # {}) & (s * PscPortReset = {}) & (s * PscPortEnable # {}) THEN
  452. status := status + UsbHcdi.PortStatusHighSpeed;
  453. END;
  454. RETURN status;
  455. END GetPortStatus;
  456. PROCEDURE HasCompanion*(): BOOLEAN;
  457. BEGIN
  458. RETURN TRUE
  459. END HasCompanion;
  460. (** Route the specified port to a companion host controller if supported. *)
  461. PROCEDURE RoutePortToCompanion*(port : LONGINT);
  462. VAR dword : SET;
  463. BEGIN
  464. (* Assert ports are not globally routed to companion controllers *)
  465. ASSERT(SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcConfigFlag)) * {0} # {});
  466. dword := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  467. SYSTEM.PUT32(ports[port], dword - PscChangeMask + PscPortOwner); FlushPCI;
  468. END RoutePortToCompanion;
  469. (** Indicate a port state using the port indicators *)
  470. PROCEDURE IndicatePort*(port, indicate : LONGINT);
  471. VAR indicators, dword : SET;
  472. BEGIN
  473. IF indicate = UsbHcdi.Amber THEN indicators := {14};
  474. ELSIF indicate = UsbHcdi.Green THEN indicators := {15};
  475. ELSE indicators := {};
  476. END;
  477. dword := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  478. dword := dword - PscIndicatorControl + indicators;
  479. SYSTEM.PUT32(ports[port], dword); FlushPCI;
  480. END IndicatePort;
  481. (** Return the current frame number.
  482. The micro-frame number is incremented each micro-frame, i.e. per 125us. There are 8 micro-frames per frame *)
  483. PROCEDURE GetFrameNumber*() : INTEGER;
  484. BEGIN
  485. RETURN SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, LSH(SYSTEM.GET32(iobase + HcFrIndex), -3)) * {0..10});
  486. END GetFrameNumber;
  487. (*
  488. * Contruct the queue head of the specified pipe.
  489. * Fill in the following DWORDs into pipe.descriptors[0]:
  490. * - Queue Head Endpoint Capabilities 1
  491. * - Queue Head Endpoint Capabilities 2
  492. * - Current qTD Pointer
  493. * - Next qTD Pointer, Alternate Next qTD Pointer, qTD Token & all five qTD Buffer Pointers
  494. * The Queue Head Horizontal Link Pointer will be set by InsertPipeQH
  495. * @param pipe
  496. *)
  497. PROCEDURE BuildQueueHead * (pipe : UsbHcdi.Pipe);
  498. VAR
  499. qh: Qh;
  500. dword: SET;
  501. nakRL, multi, mmask: LONGINT;
  502. BEGIN
  503. ASSERT(pipe.maxPacketSize <= 1024); (* Maximum allowed packet size *)
  504. (*
  505. pipe.descriptors[0] := Align(pipe.descriptors[0], alignQh);
  506. pipe.tdBase := pipe.descriptors[0] + alignQh;;
  507. *)
  508. (*
  509. IF ~CheckBoundary(pipe.descriptors[0], sizeQh) THEN
  510. INC(pipe.descriptors[0], sizeQh);
  511. INC(pipe.tdBase, sizeQh)
  512. END;
  513. *)
  514. ASSERT(pipe.descriptors[0] # 0);
  515. qh := pipe.descriptors[0];
  516. (* Queue Head Horizontal Link Pointer is not set here *)
  517. (* Queue Head Endpoint Capabilities 1 *)
  518. nakRL := 3;
  519. IF pipe.type = UsbHcdi.PipeInterrupt THEN nakRL := 0; END; (* EHCIspec, p.83 *)
  520. dword := LSH(SYSTEM.VAL(SET, nakRL), 28) * QhNakCountReload;
  521. IF (pipe.speed # UsbHcdi.HighSpeed) & (pipe.type = UsbHcdi.PipeControl) THEN
  522. dword := dword + QhControlEndpointFlag;
  523. END;
  524. IF pipe.type = UsbHcdi.PipeControl THEN dword := dword + QhDataToggleControl; END;
  525. dword := dword + LSH(SYSTEM.VAL(SET, pipe.maxPacketSize), 16) * QhMaxPacketLen;
  526. IF (pipe.speed = UsbHcdi.LowSpeed) THEN (* EPS - endpoint speed *)
  527. dword := dword + {12}; (* Low-speed endpoint *)
  528. ELSIF (pipe.speed = UsbHcdi.FullSpeed) THEN
  529. (* Do nothing; Full-speed endpoint *)
  530. ELSIF (pipe.speed = UsbHcdi.HighSpeed) THEN
  531. dword := dword + {13}; (* High-speed endpoint *)
  532. ELSE
  533. HALT(99);
  534. END;
  535. dword := dword + LSH(SYSTEM.VAL(SET, pipe.endpoint), 8) * QhEndpointNbr;
  536. dword := dword + SYSTEM.VAL(SET, pipe.address) * QhDeviceAddress;
  537. qh.epCapabilities[0] := SYSTEM.VAL(LONGINT, dword);
  538. (* Queue Head Endpoint Capabilities 2 *)
  539. multi := 1; (* TODO: How many transactions per frame for high-speed isochronous and interrupts transfer are allowed? *)
  540. dword := LSH(SYSTEM.VAL(SET, multi), 30) * QhMultiplier;
  541. IF ((pipe.speed = UsbHcdi.LowSpeed) OR (pipe.speed = UsbHcdi.FullSpeed)) & (pipe.ttAddress # 0) THEN
  542. (* Hub port and address for split transaction *)
  543. dword := dword + LSH(SYSTEM.VAL(SET, pipe.ttAddress), 16) * QhHubAddr;
  544. dword := dword + LSH(SYSTEM.VAL(SET, pipe.ttPort + 1), 23) * QhPortNbr;
  545. IF pipe.type = UsbHcdi.PipeInterrupt THEN
  546. (* In which micro-frames the HC should issue Complete Split tokens *)
  547. dword := dword + LSH({2..6}, 8) * QhSplitCMask;
  548. END;
  549. END;
  550. mmask := 1;
  551. IF (pipe.type = UsbHcdi.PipeInterrupt) OR (pipe.type = UsbHcdi.PipeIsochronous) THEN
  552. dword := dword + SYSTEM.VAL(SET, mmask) * QhSMask;
  553. END;
  554. qh.epCapabilities[1] := SYSTEM.VAL(LONGINT, dword);
  555. qh.current := 0;
  556. (* Zero-out the queue head transfer overlay *)
  557. qh.next := SYSTEM.VAL(LONGINT, QhTerminate);
  558. qh.alternate := SYSTEM.VAL(LONGINT, QhTerminate);
  559. qh.token := 0;
  560. qh.buffers[0] := 0;
  561. qh.buffers[1] := 0;
  562. qh.buffers[2] := 0;
  563. qh.buffers[3] := 0;
  564. qh.buffers[4] := 0;
  565. IF cap64bit THEN
  566. qh.extBuffers[0] := 0;
  567. qh.extBuffers[1] := 0;
  568. qh.extBuffers[2] := 0;
  569. qh.extBuffers[3] := 0;
  570. qh.extBuffers[4] := 0
  571. END;
  572. END BuildQueueHead;
  573. (** Build a Queue Head for the specified pipe and insert it into the host controller schedule. *)
  574. PROCEDURE InsertQH (pipe : UsbHcdi.Pipe) (*: BOOLEAN*);
  575. VAR
  576. new, curr: Qh;
  577. adr, asyncListAddr, queue: LONGINT;
  578. dword: SET;
  579. BEGIN (*{EXCLUSIVE}*) (* Call from exclusive sections only *)
  580. ASSERT(state = UsbHcdi.Initialized);
  581. ASSERT((pipe.maxPacketSize > 0));
  582. ASSERT(pipe # NIL);
  583. ASSERT((pipe.type = UsbHcdi.PipeControl) OR (pipe.type = UsbHcdi.PipeBulk)
  584. OR (pipe.type = UsbHcdi.PipeInterrupt));
  585. (* Build a new QH *)
  586. (*pipe.descriptorLock.Acquire;*)
  587. (*ASSERT(pipe.descriptors = NIL);
  588. NEW(pipe.descriptors, 1);*)
  589. pipe.descriptors[0] := allocator.Allocate(sizeQh);
  590. AssertAlignment(pipe.descriptors[0], alignQh);
  591. BuildQueueHead(pipe);
  592. (*pipe.descriptorLock.Release;*)
  593. (*ShowQueueHead(pipe.descriptors[0], 0, cap64bit);*)
  594. (* Insert bulk and control QHs into circular QH list. *)
  595. new := pipe.descriptors[0];
  596. IF (pipe.type = UsbHcdi.PipeControl) OR (pipe.type = UsbHcdi.PipeBulk) THEN
  597. (* Insert into the asynchronous schedule list *)
  598. asyncListAddr := SYSTEM.GET32(iobase + HcAsyncListAddr);
  599. IF asyncListAddr = 0 THEN (* Not queue heads in the list yet *)
  600. (* Since the address is obviously invalid, the asynchronous schedule mustn't be enabled *)
  601. ASSERT(SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsAsyncSchedule = {});
  602. new.horizontalLink := QhHorizontalLink(new, QhTypQh, FALSE);
  603. (* If the asynchronous schedule is enabled, exactly one queue head MUST have the H-bit set. *)
  604. dword := SYSTEM.VAL(SET, new.epCapabilities[0]) + QhHeadOfReclamation;
  605. new.epCapabilities[0] := SYSTEM.VAL(LONGINT, dword);
  606. (*Machine.FlushDCacheRange(new, sizeQh);*)
  607. (*ShowQueueHead(new, 0, cap64bit);*)
  608. (* Insert the queue head into the schedule list and activate the asynchronous schedule *)
  609. SYSTEM.PUT32(iobase + HcAsyncListAddr, new); FlushPCI;
  610. IF ~ScheduleOn(CmdAsyncSchedEnable, TRUE) & (Debug.Level >= Debug.Errors) THEN Show("Failed to enable async schedule."); KernelLog.Ln; END;
  611. ELSE
  612. ASSERT(SYSTEM.VAL(SET, asyncListAddr) * {0..4} = {}); (* 32byte alignment *)
  613. curr := asyncListAddr;
  614. new.horizontalLink := curr.horizontalLink;
  615. (*Machine.FlushDCacheRange(new, sizeQh);*)
  616. (* Insert the newly created queue head into the asynchronous schedule list. *)
  617. curr.horizontalLink := QhHorizontalLink(new, QhTypQh, FALSE);
  618. (*Machine.FlushDCacheRange(curr, sizeQh);*)
  619. END;
  620. ELSE (* pipe is interrupt *)
  621. curr := SYSTEM.VAL(Qh, pipe.queue);
  622. new := pipe.descriptors[0];
  623. new.horizontalLink := curr.horizontalLink;
  624. (*SYSTEM.PUT32(pipe.qh + QhHorizontalLinkPointer, adr);*)
  625. curr.horizontalLink := SYSTEM.VAL(ADDRESS, new) + SYSTEM.VAL(LONGINT, {1} - {2});
  626. (*SYSTEM.PUT32(pipe.queue + QhHorizontalLinkPointer, pipe.qh + SYSTEM.VAL(LONGINT, {1} - {2}));*)
  627. (*Machine.FlushDCacheRange(SYSTEM.VAL(ADDRESS, new), sizeQh);
  628. Machine.FlushDCacheRange(pipe.queue, sizeQh);*)
  629. (* Enable the periodic list if necessary *)
  630. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts));
  631. IF dword * StsPeriodicSchedule = {} THEN
  632. IF ~ScheduleOn(CmdPeriodicSchedEnable, TRUE) THEN
  633. IF Debug.Level >= Debug.Errors THEN Show("Could not enable periodic schedule."); KernelLog.Ln; END;
  634. END;
  635. END;
  636. END;
  637. IF Debug.Trace & Debug.traceQueuing THEN Show("Inserted QH at "); KernelLog.Address(pipe.descriptors[0]); KernelLog.Ln END;
  638. (*Machine.FlushDCacheRange(pipe.descriptors[0], sizeQh)*)
  639. END InsertQH;
  640. (* Enable/Disable the periodic or asynchronous schedule. *)
  641. PROCEDURE ScheduleOn(cmd : SET; on : BOOLEAN) : BOOLEAN;
  642. VAR dword, sts : SET; mtimer : Kernel.MilliTimer;
  643. BEGIN (* Caller must hold obj lock *)
  644. ASSERT((cmd = CmdPeriodicSchedEnable) OR (cmd = CmdAsyncSchedEnable));
  645. IF Debug.Trace & Debug.traceQueuing THEN
  646. IF on THEN Show("Enabling"); ELSE Show("Disabling"); END;
  647. IF cmd = CmdAsyncSchedEnable THEN KernelLog.String(" asynchronous schedule."); ELSE KernelLog.String(" periodic schedule."); END;
  648. KernelLog.Ln;
  649. END;
  650. IF cmd = CmdAsyncSchedEnable THEN sts := StsAsyncSchedule; ELSE sts := StsPeriodicSchedule; END;
  651. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  652. ASSERT(dword * cmd = LSH(SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * sts, -10)); (* HcUsbCmd & HcUsbSts in consistent state *)
  653. IF on THEN dword := dword + cmd; ELSE dword := dword - cmd; END;
  654. SYSTEM.PUT32(iobase + HcUsbCmd, dword); FlushPCI;
  655. (* Wait until the HC reaches the desired state *)
  656. Kernel.SetTimer(mtimer, 500);
  657. WHILE ~Kernel.Expired(mtimer) & ((SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * sts # {}) # on) DO
  658. Objects.Yield;
  659. END;
  660. RETURN (SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * sts # {}) = on;
  661. END ScheduleOn;
  662. (*
  663. * Remove a queue head data structure from the host controller's asynchronous schedule.
  664. * The asynchronous schedule is a circular linked list of queue heads. At least one queue heads has
  665. * the H-bit (Head of asynchronous schedule list) set which is used by the host controller to detect
  666. * empty list conditions. There are two cases when we remove a queue head:
  667. * 1) It is the only queue head in the list. In this case, we disabled the asynchronous schedule execution and
  668. * and remove the queue head then.
  669. * 2) There are other queue heads in the list. If the queue head to be removed is the head of the list, we
  670. * need to set the H-bit for another queue head.
  671. *
  672. * Precondition: TDs are already removed from the QH, QH is inactive
  673. *)
  674. PROCEDURE RemoveAsyncQH(pipe : UsbHcdi.Pipe);
  675. VAR
  676. (*start, cur, prev : LONGINT; dword : SET;*)
  677. start, curr, prev: Qh;
  678. dword: SET;
  679. BEGIN (* Caller must hold obj lock *)
  680. prev := SYSTEM.GET32(iobase + HcAsyncListAddr);
  681. ASSERT((SYSTEM.VAL(ADDRESS, prev) # 0) & (SYSTEM.VAL(SET, prev) * {0..4} = {}));
  682. (*Machine.InvalidateDCacheRange(SYSTEM.VAL(ADDRESS, prev), sizeQh);*)
  683. prev := prev.horizontalLink;
  684. ASSERT((SYSTEM.VAL(SET, prev) * {1} # {}) & (SYSTEM.VAL(SET, prev) * QhTerminate = {})); (* Pointer references queue head *)
  685. prev := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, prev) * {5..31});
  686. (*Machine.InvalidateDCacheRange(prev, sizeQh);*)
  687. curr := prev.horizontalLink;
  688. ASSERT((SYSTEM.VAL(SET, curr) * {1} # {}) & (SYSTEM.VAL(SET, curr) * QhTerminate = {})); (* Pointer references queue head *)
  689. curr := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, curr) * {5..31});
  690. (*Machine.InvalidateDCacheRange(curr, sizeQh);*)
  691. (* prev is the address of the queue head that points to the queue head with the address cur *)
  692. IF curr = prev THEN (* Only one queue head in the list *)
  693. ASSERT(SYSTEM.VAL(SET, curr.epCapabilities[0]) * QhHeadOfReclamation # {});
  694. (*ASSERT(SYSTEM.VAL(SET, curr.token) * TdActive = {});*)
  695. IF curr = pipe.descriptors[0] THEN (* just disable asynchronous schedule *)
  696. IF ScheduleOn(CmdAsyncSchedEnable, FALSE) THEN
  697. SYSTEM.PUT32(iobase + HcAsyncListAddr, 0); FlushPCI; (* Mark as invalid. *)
  698. (* Free the queue head *)
  699. (*pipe.descriptorLock.Acquire;*)
  700. pipe.descriptors := NIL;
  701. (*pipe.descriptorLock.Release;*)
  702. allocator.Free(curr, sizeQh)
  703. ELSIF Debug.Level >= Debug.Errors THEN Show("Could not disable async schedule."); KernelLog.Ln;
  704. END;
  705. ELSIF Debug.Level >= Debug.Warnings THEN Show("Failed to remove QH from asynchronous schedule: QH not found."); KernelLog.Ln;
  706. END;
  707. ELSE (* Find and remove the queue head in the list *)
  708. (* Search the queue head that references the queue head to be removed *)
  709. start := curr;
  710. LOOP
  711. dword := SYSTEM.VAL(SET, curr.horizontalLink);
  712. ASSERT(dword * QhTerminate = {}); (* Circular lists don't terminate *)
  713. ASSERT(dword * {1} # {}); (* Pointer references queue head *)
  714. ASSERT(dword * {2..4} = {}); (* qTD pointers must be 32byte aligned *)
  715. prev := curr;
  716. curr := SYSTEM.VAL(LONGINT, dword * {5..31});
  717. IF curr = pipe.descriptors[0] THEN (* QH found *) EXIT; END;
  718. IF curr = start THEN (* list completely searched but QH not found *) EXIT; END;
  719. (*Machine.InvalidateDCacheRange(curr, sizeQh);*)
  720. END;
  721. IF curr = pipe.descriptors[0] THEN (* Found the queue head. prev is pointing to it *)
  722. (* If we remove the head of reclamation, elect a new one *)
  723. IF SYSTEM.VAL(SET, curr.epCapabilities[0]) * QhHeadOfReclamation # {} THEN
  724. IF Debug.Trace & Debug.traceQueuing THEN Show("Electing new head of reclamation."); KernelLog.Ln; END;
  725. dword := SYSTEM.VAL(SET, prev.epCapabilities[0]);
  726. prev.epCapabilities[0] := SYSTEM.VAL(LONGINT, dword + QhHeadOfReclamation);
  727. END;
  728. (* Remove QH from asynchronous list and inforam host controller *)
  729. prev.horizontalLink := curr.horizontalLink;
  730. (*Machine.FlushDCacheRange(prev, sizeQh);*)
  731. (* Free QH *)
  732. (*pipe.descriptorLock.Acquire;*)
  733. pipe.descriptors := NIL;
  734. (*pipe.descriptorLock.Release;*)
  735. allocator.Free(curr, sizeQh)
  736. ELSIF Debug.Level >= Debug.Warnings THEN Show("Failed to remove QH from asynchronous list: QH not found."); KernelLog.Ln;
  737. END;
  738. (* Before we may free the pipe ressources, we have to make sure that the HC has no cached references to the structure *)
  739. (* we just removed. *)
  740. IF ~HcHandshake() THEN
  741. IF Debug.Level >= Debug.Errors THEN Show("UsbEhci: Serious error: HC handshake failed."); KernelLog.Ln; END;
  742. END;
  743. END;
  744. IF Debug.Trace & Debug.traceQueuing THEN Show("Removed QH at "); KernelLog.Address(curr); KernelLog.Ln; END;
  745. END RemoveAsyncQH;
  746. (*
  747. * Inform the host controller that we removed something from the asynchronous schedule list. This is
  748. * necessary since the HC could have cached a copy of the pointer to the queue head we've just removed.
  749. *)
  750. PROCEDURE HcHandshake() : BOOLEAN;
  751. VAR dword : SET; mtimer : Kernel.MilliTimer; result : BOOLEAN;
  752. BEGIN (* caller holds object lock *)
  753. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts));
  754. ASSERT(dword * StsAsyncSchedule # {}); (* HC behaviour undefined if ringing doorbell while async schedule is off *)
  755. hcHandshake := FALSE;
  756. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  757. SYSTEM.PUT32(iobase + HcUsbCmd, dword + CmdAsyncAdvDoorbell); FlushPCI;
  758. Kernel.SetTimer(mtimer, 500);
  759. WHILE ~Kernel.Expired(mtimer) & (SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd)) * CmdAsyncAdvDoorbell # {}) DO
  760. Objects.Yield;
  761. END;
  762. result := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd)) * CmdAsyncAdvDoorbell = {}; (* The HC should have cleared the bit *)
  763. IF Debug.Trace & Debug.traceQueuing THEN
  764. Show("HC handshake "); IF result THEN KernelLog.String("succeeded."); ELSE KernelLog.String("failed."); END; KernelLog.Ln;
  765. END;
  766. RETURN result;
  767. END HcHandshake;
  768. PROCEDURE RemovePeriodicQH(pipe : UsbHcdi.Pipe);
  769. VAR
  770. timer : Kernel.Timer;
  771. cur, temp : Qh;
  772. next : SET;
  773. BEGIN (* caller must hold obj lock *)
  774. IF pipe.descriptors[0] = 0 THEN RETURN; END;
  775. cur := pipe.descriptors[0];
  776. LOOP
  777. (*Machine.InvalidateDCacheRange(cur, sizeQh);*)
  778. next := SYSTEM.VAL(SET, cur.horizontalLink);
  779. IF next * {5..31} = SYSTEM.VAL(SET, pipe.descriptors[0]) * {5..31} THEN (* found *)
  780. temp := SYSTEM.GET32(pipe.descriptors[0] + QhHorizontalLinkPointer);
  781. cur .horizontalLink := temp;
  782. (*Machine.FlushDCacheRange(cur, sizeQh);)*)
  783. IF Debug.Trace & Debug.traceQueuing THEN KernelLog.String("UsbEhci: Deleted Interrupt Pipe QH."); KernelLog.Ln; END;
  784. NEW(timer); timer.Sleep(10); (* HC has still access to QH, wait > 1ms *)
  785. EXIT;
  786. ELSIF next * QhTerminate # {} THEN (* not found, reached end of list *)
  787. IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbEhci: Could not delete interrupt QH -> QH not found."); KernelLog.Ln; END;
  788. EXIT;
  789. ELSE
  790. cur := SYSTEM.VAL(LONGINT, next * {5..31});
  791. END;
  792. END;
  793. IF Debug.Trace & Debug.traceQueuing THEN Show("Removed QH at "); KernelLog.Hex(pipe.descriptors[0], 8); KernelLog.Ln; END;
  794. END RemovePeriodicQH;
  795. (** Remove the pipe's queue head from the host controller schedule *)
  796. PROCEDURE RemoveQH (pipe : UsbHcdi.Pipe);
  797. BEGIN (*{EXCLUSIVE}*)
  798. IF Debug.Trace & Debug.traceQueuing THEN Show("Removing QH at "); KernelLog.Hex(pipe.descriptors[0], 8); KernelLog.Ln; END;
  799. (* Assume that no transfer is in progress for this pipe *)
  800. (* Then remove the pipe's queue head from the host controller schedule *)
  801. IF (pipe.type = UsbHcdi.PipeControl) OR (pipe.type = UsbHcdi.PipeBulk) THEN
  802. RemoveAsyncQH(pipe);
  803. ELSIF pipe.type = UsbHcdi.PipeInterrupt THEN
  804. RemovePeriodicQH(pipe);
  805. END;
  806. END RemoveQH;
  807. (*(** Checks whether TDs may be linked to the pipe's QH *)
  808. PROCEDURE LinkTDsAllowed*(pipe : UsbHcdi.Pipe) : BOOLEAN;
  809. VAR dword : SET;
  810. BEGIN {EXCLUSIVE}
  811. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.descriptors[0] + QhQtdToken));
  812. IF dword * TdActive # {} THEN
  813. IF Debug.Level >= Debug.Errors THEN Show("LinkTDs: ERROR: PIPE IS STILL ACTIVE!!!!"); KernelLog.Ln; END;
  814. pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.LinkTDsFailed;
  815. RETURN FALSE;
  816. END;
  817. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.descriptors[0] + QhNextQtdPointer));
  818. IF dword * QhTerminate = {} THEN
  819. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: LinkTDs: Overwriten valid pointer ?!?"); KernelLog.Ln; END;
  820. pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.LinkTDsFailed;
  821. RETURN FALSE;
  822. END;
  823. RETURN TRUE;
  824. END LinkTDsAllowed;
  825. (* Insert the TD list <td> into the queue (ED) <queue> *)
  826. PROCEDURE LinkTDs*(pipe : UsbHcdi.Pipe; qtd : Machine.Address32);
  827. VAR dword : SET;
  828. BEGIN {EXCLUSIVE}
  829. ASSERT(SYSTEM.VAL(SET, qtd) * {0..4} = {}); (* 32byte alignment *)
  830. (* Pipe must be inactive... *)
  831. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.descriptors[0] + QhQtdToken));
  832. IF dword * TdHalted # {} THEN
  833. IF Debug.Trace & Debug.tracePipes THEN Show("LinkTDs: Automatically clear halt condition"); KernelLog.Ln; END;
  834. ClearHalt(pipe);
  835. END;
  836. SYSTEM.PUT32(pipe.descriptors[0] + QhNextQtdPointer, qtd);
  837. (*Machine.FlushDCacheRange(pipe.descriptors[0], sizeQh);
  838. Machine.FlushDCacheRange(qtd, sizeQtd);*)
  839. IF SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsAsyncSchedule = {} THEN
  840. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  841. IF dword * CmdAsyncSchedEnable = {} THEN
  842. IF ~ScheduleOn(CmdAsyncSchedEnable, TRUE) & (Debug.Level >= Debug.Errors) THEN Show("Failed to re-enabled async schedule."); KernelLog.Ln; END;
  843. END;
  844. END;
  845. END LinkTDs;*)
  846. (** Remove all transfer descriptors from the pipe's queue head *)
  847. PROCEDURE UnlinkTDs (transfer: UsbHcdi.TransferToken);
  848. VAR
  849. i, len: LONGINT;
  850. dword: SET;
  851. timer: Kernel.Timer;
  852. prev, qtd: Qtd;
  853. qh: Qh;
  854. mtimer : Kernel.MilliTimer;
  855. BEGIN (*{EXCLUSIVE}*)
  856. (*IF pipe.firstTD = 0 THEN RETURN END; (* pipe has not yet been used *)*)
  857. (* We must inactivate all qTD of the queue head... *)
  858. (* we should wait until the transaction overlay is also inactive *)
  859. qh := transfer.pipe.descriptors[0];
  860. Kernel.SetTimer(mtimer, 2000);
  861. (*Machine.InvalidateDCacheRange(qh, sizeQh);*)
  862. dword := SYSTEM.VAL(SET, qh.token);
  863. WHILE ~Kernel.Expired(mtimer) & (dword * TdActive # {}) DO
  864. (*Machine.InvalidateDCacheRange(qh, sizeQh);*)
  865. dword := SYSTEM.VAL(SET, qh.token);
  866. Objects.Yield;
  867. END;
  868. IF dword * TdActive # {} THEN
  869. IF Debug.Level >= Debug.Errors THEN Show("Transaction overlay indicates active transfer!"); KernelLog.Ln; END;
  870. END;
  871. len := LEN(transfer.tds);
  872. WHILE (i < len) & (transfer.tds[i] # 0) DO
  873. qtd := transfer.tds[i];
  874. AssertAlignment(qtd, alignQtd);
  875. (*Machine.InvalidateDCacheRange(qtd, sizeQtd);*)
  876. dword := SYSTEM.VAL(SET, qtd.token);
  877. allocator.Free(qtd, sizeQtd);
  878. INC(i)
  879. END;
  880. qh.next := SYSTEM.VAL(LONGINT, QhTerminate);
  881. qh.alternate := SYSTEM.VAL(LONGINT, QhTerminate);
  882. IF transfer.len > 0 THEN
  883. Machine.InvalidateDCacheRange(transfer.buffer, transfer.len)
  884. END;
  885. END UnlinkTDs;
  886. (* Remove ITDs from framelist and free them *)
  887. PROCEDURE UnlinkIso (transfer: UsbHcdi.TransferToken);
  888. VAR
  889. i, count: LONGINT;
  890. hcFrame: ADDRESS;
  891. itd: Itd;
  892. BEGIN
  893. (*ShowFramelist(ADDRESSOF(framelist.data[framelist.base]), framelistSize, LSH(SYSTEM.GET32(iobase + HcFrIndex), -3) MOD framelistSize, FALSE);
  894. Objects.Yield;*)
  895. WHILE transfer.tds[count] # 0 DO INC(count) END;
  896. (* Make sure that HC will not process the ITDs during our work *)
  897. (*TRACE('Unlink acquire FL');*)
  898. (*!framelistLock.Acquire;*)
  899. REPEAT
  900. hcFrame := ADDRESSOF(framelist.data[framelist.base]) + (LSH(SYSTEM.GET32(iobase + HcFrIndex), -3) MOD framelistSize) * 4;
  901. (*KernelLog.Enter; KernelLog.String("Unlink waiting for HC"); KernelLog.Exit;*)
  902. UNTIL (hcFrame + 3 < transfer.tds[0]) OR (hcFrame >= transfer.tds[count]);
  903. (*KernelLog.Enter; KernelLog.String("Unlink at "); KernelLog.Int((transfer.tds[0] - ADDRESSOF(framelist.data[framelist.base])) DIV 4, 0); KernelLog.String(", HC: "); KernelLog.Int(LSH(SYSTEM.GET32(iobase + HcFrIndex), -3) MOD framelistSize, 0); KernelLog.Exit;*)
  904. WHILE transfer.tds[i] # 0 DO
  905. itd := SYSTEM.GET32(transfer.tds[i]);
  906. ASSERT(SYSTEM.VAL(ADDRESS, itd) MOD 32 = 0);
  907. ASSERT(SYSTEM.VAL(ADDRESS, itd) DIV 32 # 0);
  908. SYSTEM.PUT32(transfer.tds[i], itd.next);
  909. allocator.Free(itd, sizeItd);
  910. INC(i)
  911. END;
  912. (*Machine.FlushDCacheRange(ADDRESSOF(framelist.data[framelist.base]), framelistSize * 4);*)
  913. (*ShowFramelist(ADDRESSOF(framelist.data[framelist.base]), framelistSize, LSH(SYSTEM.GET32(iobase + HcFrIndex), -3) MOD framelistSize, FALSE);*)
  914. (*KernelLog.Enter; KernelLog.String("Unlinked"); KernelLog.Exit;*)
  915. (*!framelistLock.Release;*)
  916. (*TRACE('Unlink release FL');*)
  917. END UnlinkIso;
  918. (*
  919. * Clears the Halt bit in the pipe's queue head and removes any qTD from the pipe.
  920. * Note that this only makes sense if the Halt feature of the USB device is also cleared used the ClearFeature standard
  921. * request. This procedure here only changes the pipe's queue head.
  922. *)
  923. PROCEDURE ClearHalt(pipe : UsbHcdi.Pipe);
  924. VAR
  925. qh: Qh;
  926. dword : SET;
  927. BEGIN
  928. qh := pipe.descriptors[0];
  929. (*Machine.InvalidateDCacheRange(qh, sizeQh);*)
  930. dword := SYSTEM.VAL(SET, qh.token);
  931. IF dword * TdHalted # {} THEN
  932. qh.current := 0;
  933. (* Zero-out the queue head transfer overlay *)
  934. qh.next := SYSTEM.VAL(LONGINT, QhTerminate);
  935. qh.alternate := SYSTEM.VAL(LONGINT, QhTerminate);
  936. qh.token := 0;
  937. qh.buffers[0] := 0;
  938. qh.buffers[1] := 0;
  939. qh.buffers[2] := 0;
  940. qh.buffers[3] := 0;
  941. qh.buffers[4] := 0;
  942. IF cap64bit THEN
  943. qh.extBuffers[0] := 0;
  944. qh.extBuffers[1] := 0;
  945. qh.extBuffers[2] := 0;
  946. qh.extBuffers[3] := 0;
  947. qh.extBuffers[4] := 0;
  948. END;
  949. (*Machine.FlushDCacheRange(qh, sizeQh)*)
  950. ELSIF Debug.Level >= Debug.Warnings THEN Show("Tried to clear a non-halted pipe."); KernelLog.Ln;
  951. END;
  952. END ClearHalt;
  953. PROCEDURE Schedule * (transfer: UsbHcdi.TransferToken);
  954. BEGIN
  955. (* Checks *)
  956. ASSERT(transfer # NIL);
  957. ASSERT(transfer.pipe # NIL);
  958. CASE transfer.pipe.type OF
  959. UsbHcdi.PipeControl, UsbHcdi.PipeBulk:
  960. ScheduleAperiodic(transfer)
  961. |UsbHcdi.PipeIsochronous:
  962. ScheduleIso(transfer)
  963. |UsbHcdi.PipeInterrupt:
  964. (*ScheduleInterrupt(transfer)*)
  965. ScheduleAperiodic(transfer)
  966. ELSE
  967. (* Unknown pipe type *)
  968. ASSERT((transfer.pipe.type = UsbHcdi.PipeControl) OR (transfer.pipe.type = UsbHcdi.PipeIsochronous)
  969. OR (transfer.pipe.type = UsbHcdi.PipeBulk) OR (transfer.pipe.type = UsbHcdi.PipeInterrupt));
  970. END;
  971. END Schedule;
  972. (**
  973. * Cancel scheduled transfer.
  974. * Tries to cancel a scheduled transfer. Fails if transfer is completed or active.
  975. *)
  976. PROCEDURE Cancel * (transfer: UsbHcdi.TransferToken): BOOLEAN;
  977. BEGIN (*{EXCLUSIVE}*) HALT(301) (* abstract *)
  978. END Cancel;
  979. (**
  980. * Checks that a periodic pipe policy is allowed for this HCD. Returns FALSE if the policy
  981. * requires too much bandwidth, TRUE if the policy is schedulable.
  982. *)
  983. PROCEDURE CheckPipePolicy * (interval, size: LONGINT): BOOLEAN;
  984. BEGIN
  985. RETURN TRUE
  986. END CheckPipePolicy;
  987. (**
  988. * Some HCDIs require some pipe creations to be notified to them. This procedure is called whenever a new
  989. * pipe is created, so that the HCDI can take the necessary actions. This is needed, e.g. for bulk and control pipes
  990. * on EHCI: we build one qh per pipe.
  991. *)
  992. PROCEDURE RegisterPipe * (pipe: UsbHcdi.Pipe);
  993. BEGIN (* Call only from exclusive sections *)
  994. CASE pipe.type OF
  995. UsbHcdi.PipeControl, UsbHcdi.PipeBulk:
  996. NEW(pipe.descriptors, 1);
  997. InsertQH(pipe)
  998. |UsbHcdi.PipeIsochronous: (* nothing to do *)
  999. |UsbHcdi.PipeInterrupt:
  1000. IF pipe.irqInterval = 1 THEN (* 1ms queue *)
  1001. pipe.queue := interruptQh[0];
  1002. ELSIF pipe.irqInterval < 4 THEN (* 2ms queue *)
  1003. pipe.queue := interruptQh[1];
  1004. ELSIF pipe.irqInterval < 8 THEN (* 4ms queue *)
  1005. pipe.queue := interruptQh[2];
  1006. ELSIF pipe.irqInterval < 16 THEN (* 8ms queue *)
  1007. pipe.queue := interruptQh[3];
  1008. ELSIF pipe.irqInterval < 32 THEN (* 16ms queue *)
  1009. pipe.queue := interruptQh[4];
  1010. ELSE
  1011. pipe.queue := interruptQh[5]; (* 32 ms queue *)
  1012. END;
  1013. NEW(pipe.descriptors, 2);
  1014. InsertQH(pipe);
  1015. pipe.descriptors[1] := allocator.Allocate(sizeItd)
  1016. ELSE
  1017. (* Unknown pipe type *)
  1018. ASSERT((pipe.type = UsbHcdi.PipeControl) OR (pipe.type = UsbHcdi.PipeIsochronous)
  1019. OR (pipe.type = UsbHcdi.PipeBulk) OR (pipe.type = UsbHcdi.PipeInterrupt))
  1020. END
  1021. END RegisterPipe;
  1022. (**
  1023. * Some HCDIs require some pipe freeing to be notified to them. This procedure is called whenever a pipe is freed,
  1024. * so that the HCDI can take necessary steps. This is needed e.g. for bulk and control pipes on EHCI, so that the
  1025. * controller can remove the queue head.
  1026. *)
  1027. PROCEDURE UnregisterPipe * (pipe: UsbHcdi.Pipe);
  1028. BEGIN
  1029. CASE pipe.type OF
  1030. UsbHcdi.PipeControl, UsbHcdi.PipeBulk, UsbHcdi.PipeInterrupt:
  1031. RemoveQH(pipe)
  1032. |UsbHcdi.PipeIsochronous:
  1033. ELSE
  1034. (* Unknown pipe type *)
  1035. ASSERT((pipe.type = UsbHcdi.PipeControl) OR (pipe.type = UsbHcdi.PipeIsochronous)
  1036. OR (pipe.type = UsbHcdi.PipeBulk) OR (pipe.type = UsbHcdi.PipeInterrupt))
  1037. END
  1038. END UnregisterPipe;
  1039. (*
  1040. (**
  1041. * Put the specified control transfer into the host controller's schedule.
  1042. * USB Control Transfers use a three stage protocol:
  1043. * - stage 1: control setup transaction
  1044. * - stage 2: optional data stage
  1045. * - stage 3: status transaction
  1046. * For high-speed devices, the PING protocol must be used for OUT transactions in the data stage and status stage.
  1047. *
  1048. *
  1049. * @param pipe
  1050. * @param direction Direction of the control transfer (UsbHcdi.In (device-to-host) | UsbHcdi.Out (host-to-device))
  1051. * @param msg Control message
  1052. * @param bufferlen Number of bytes transmitted/received in the data stage
  1053. * @param buffer Buffer where to get/put the specified number of bytes
  1054. *)
  1055. PROCEDURE ScheduleControl*(pipe : UsbHcdi.Pipe; direction : LONGINT; msg : UsbHcdi.ControlMessage; bufferLen : LONGINT; VAR buffer : Usbdi.Buffer);
  1056. VAR
  1057. qtd : Machine.Address32;
  1058. dword : SET;
  1059. ranges : ARRAY ScatterGatherListSize OF Machine.Range;
  1060. numRanges : LONGINT;
  1061. BEGIN
  1062. (*Machine.FlushDCacheRange(ADDRESSOF(buffer[0]), bufferLen);*)
  1063. (* pipe.tdBase = pipe.descriptors[0] + 32 in UsbHcdi *)
  1064. (* pipe.firstTD := pipe.tdBase;
  1065. ASSERT(pipe.firstTD = pipe.descriptors[0] + alignQh);
  1066. *)
  1067. pipe.firstTD := pipe.tdBase - 32 + alignQh;
  1068. AssertAlignment(pipe.firstTD, alignQtd);
  1069. ASSERT(SYSTEM.VAL(SET, pipe.firstTD) * {0..4} = {}); (* qTDs must be 32byte aligned *)
  1070. ASSERT(pipe.firstTD MOD alignQtd = 0);
  1071. IF (pipe.speed = UsbHcdi.LowSpeed) OR (pipe.speed = UsbHcdi.FullSpeed) THEN
  1072. IF pipe.maxRetries = 0 THEN
  1073. (* For low-speed and full-speed devices, the value 0 is not allowed *)
  1074. pipe.maxRetries := 3;
  1075. END;
  1076. END;
  1077. (* Stage1: Control setup transaction *)
  1078. qtd := pipe.firstTD;
  1079. ASSERT((qtd + sizeQtd - 1 <= ADDRESSOF(pipe.tdBuffer[pipe.tdBufferLen-1])));
  1080. AssertAlignment(qtd+sizeQtd, alignQtd);
  1081. SYSTEM.PUT32(qtd + QtdNextQtdPointer, qtd + sizeQtd);
  1082. SYSTEM.PUT32(qtd + QtdAltNextQtdPointer, QtdTerminate); (* Mark Alternate Next qTD Pointer as invalid *)
  1083. dword := LSH(SYSTEM.VAL(SET, pipe.maxRetries), 10) * QtdErrorCounter; (* DataToggle = FALSE; Current Page = 0; no IOC *)
  1084. dword := dword + LSH(SYSTEM.VAL(SET, 8), 16) * QtdBytesToTransfer; (* 8byte control message *)
  1085. dword := dword + LSH(SYSTEM.VAL(SET, PidSetup), 8) * QtdPidCode + TdActive;
  1086. SYSTEM.PUT32(qtd + QtdToken, dword);
  1087. Machine.TranslateVirtual(ADDRESSOF(msg[0]), 8, numRanges, ranges);
  1088. IF numRanges = 0 THEN
  1089. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: ScheduleControl: Scatter/Gather list too small."); KernelLog.Ln; END;
  1090. pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.TransferTooLarge; RETURN;
  1091. END;
  1092. (* The HC will access the next buffer pointer when the buffer crosses a physical page... *)
  1093. SYSTEM.PUT32(qtd + QtdBufferPtr0, ranges[0].adr);
  1094. IF numRanges > 1 THEN (* buffer is across page boundaries *)
  1095. SYSTEM.PUT32(qtd + QtdBufferPtr1, ranges[1].adr)
  1096. ELSE
  1097. SYSTEM.PUT32(qtd + QtdBufferPtr1, 0);
  1098. END;
  1099. SYSTEM.PUT32(qtd + QtdBufferPtr2, 0);
  1100. SYSTEM.PUT32(qtd + QtdBufferPtr3, 0);
  1101. SYSTEM.PUT32(qtd + QtdBufferPtr4, 0);
  1102. IF cap64bit THEN
  1103. SYSTEM.PUT32(qtd + QtdExtBufferPtr0, 0);
  1104. SYSTEM.PUT32(qtd + QtdExtBufferPtr1, 0);
  1105. SYSTEM.PUT32(qtd + QtdExtBufferPtr2, 0);
  1106. SYSTEM.PUT32(qtd + QtdExtBufferPtr3, 0);
  1107. SYSTEM.PUT32(qtd + QtdExtBufferPtr4, 0);
  1108. END;
  1109. Machine.FlushDCacheRange(qtd, sizeQtd);
  1110. (* Setup phase always starts with dataToggle = FALSE, so now it must be TRUE *)
  1111. pipe.dataToggle := TRUE;
  1112. (* Stage 2: Optional data stage *)
  1113. IF bufferLen # 0 THEN
  1114. IF ~CreateTDList(pipe, direction, bufferLen, 0, buffer, qtd + sizeQtd, qtd, TRUE) THEN
  1115. pipe.status := Usbdi.Error; pipe.errors := UsbHcdi.Internal; RETURN;
  1116. END;
  1117. END;
  1118. Machine.FlushDCacheRange(qtd, sizeQtd);
  1119. qtd := qtd + sizeQtd;
  1120. AssertAlignment(qtd, alignQtd);
  1121. IF qtd + sizeQtd - 1 > ADDRESSOF(pipe.tdBuffer[pipe.tdBufferLen-1]) THEN
  1122. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: TD buffer too small."); KernelLog.Ln; END;
  1123. pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.OutOfTDs; RETURN;
  1124. END;
  1125. (* stage 3: status: build status TD *)
  1126. SYSTEM.PUT32(qtd + QtdNextQtdPointer, QtdTerminate); (* Last qTD in chain *)
  1127. SYSTEM.PUT32(qtd + QtdAltNextQtdPointer, QtdTerminate); (* Mark Alternate Next qTD Pointer as invalid *)
  1128. dword := QtdDataToggle + TdActive; (* dataToggle always TRUE and set ind TD in status stage; CC = not accessed *)
  1129. IF (direction = UsbHcdi.Out) OR (bufferLen = 0) THEN
  1130. dword := dword + LSH(SYSTEM.VAL(SET, PidIn), 8);
  1131. ELSE
  1132. dword := dword + LSH(SYSTEM.VAL(SET, PidOut), 8);
  1133. IF pipe.speed = UsbHcdi.HighSpeed THEN (* Do PING protocol *)
  1134. dword := dword + TdPingState;
  1135. END;
  1136. END;
  1137. dword := dword + LSH(SYSTEM.VAL(SET, pipe.maxRetries), 10) * QtdErrorCounter;
  1138. IF pipe.ioc THEN dword := dword + QtdIoc; END; (* Set interrupt on completion bit *)
  1139. SYSTEM.PUT32(qtd + QtdToken, dword);
  1140. SYSTEM.PUT32(qtd + QtdBufferPtr0, 0);
  1141. SYSTEM.PUT32(qtd + QtdBufferPtr1, 0);
  1142. SYSTEM.PUT32(qtd + QtdBufferPtr2, 0);
  1143. SYSTEM.PUT32(qtd + QtdBufferPtr3, 0);
  1144. SYSTEM.PUT32(qtd + QtdBufferPtr4, 0);
  1145. IF cap64bit THEN
  1146. SYSTEM.PUT32(qtd + QtdExtBufferPtr0, 0);
  1147. SYSTEM.PUT32(qtd + QtdExtBufferPtr1, 0);
  1148. SYSTEM.PUT32(qtd + QtdExtBufferPtr2, 0);
  1149. SYSTEM.PUT32(qtd + QtdExtBufferPtr3, 0);
  1150. SYSTEM.PUT32(qtd + QtdExtBufferPtr4, 0);
  1151. END;
  1152. Machine.FlushDCacheRange(ADDRESSOF(msg[0]), LEN(msg));
  1153. Machine.FlushDCacheRange(ADDRESSOF(buffer[0]), bufferLen);
  1154. Machine.FlushDCacheRange(qtd, sizeQtd);
  1155. pipe.lastTD := qtd;
  1156. END ScheduleControl;
  1157. PROCEDURE Schedule*(pipe : UsbHcdi.Pipe; bufferLen, offset: LONGINT; VAR buffer: Usbdi.Buffer);
  1158. VAR dword : SET;
  1159. BEGIN
  1160. Machine.FlushDCacheRange(ADDRESSOF(buffer[offset]), bufferLen);
  1161. SYSTEM.PUT32(pipe.descriptors[0] + QhCurrentQtdPointer, 0);
  1162. (* pipe.firstTD := pipe.tdBase;
  1163. ASSERT(pipe.firstTD = pipe.descriptors[0] + alignQh);
  1164. *)
  1165. pipe.firstTD := pipe.tdBase - 32 + alignQh;
  1166. AssertAlignment(pipe.firstTD, alignQtd);
  1167. ASSERT(pipe.firstTD MOD alignQtd = 0);
  1168. ASSERT(SYSTEM.VAL(SET, pipe.firstTD) * {0..4} = {}); (* qTDs must be 32byte aligned *)
  1169. IF ~CreateTDList(pipe, pipe.direction, bufferLen, offset, buffer, pipe.firstTD, pipe.lastTD, FALSE) THEN
  1170. pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.LinkTDsFailed; RETURN;
  1171. END;
  1172. SYSTEM.PUT32(pipe.lastTD + QtdNextQtdPointer, QhTerminate);
  1173. IF pipe.ioc THEN
  1174. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.lastTD + QtdToken));
  1175. dword := dword + QtdIoc;
  1176. SYSTEM.PUT32(pipe.lastTD + QtdToken, dword);
  1177. END;
  1178. END Schedule;
  1179. *)
  1180. (** Creates qTDs for the transfer and append them to the qtd list of the corresponding pipe. *)
  1181. PROCEDURE ScheduleAperiodic (transfer: UsbHcdi.TransferToken);
  1182. VAR
  1183. qh: Qh;
  1184. first, last: Qtd;
  1185. curr: Qtd;
  1186. dword: SET;
  1187. i, count: LONGINT;
  1188. BEGIN {EXCLUSIVE}
  1189. (*transfer.pipe.transferLock.Acquire;*)
  1190. IF transfer.len > 0 THEN
  1191. Machine.FlushDCacheRange(transfer.buffer, transfer.len)
  1192. END;
  1193. qh := transfer.pipe.descriptors[0];
  1194. ASSERT(SYSTEM.VAL(ADDRESS, qh) # 0);
  1195. AssertAlignment(qh, alignQh);
  1196. (*IF ~ScheduleOn(CmdAsyncSchedEnable, FALSE) THEN TRACE('BOOOOOOOOOOOOOOOOOOOOOOP') ELSE (*TRACE('YOUHOU')*) END;*)
  1197. IF transfer.pipe.type = UsbHcdi.PipeControl THEN
  1198. CreateControlTDs(transfer.pipe, transfer.pipe.direction, transfer.message, transfer.buffer, transfer.len, first, last, count)
  1199. ELSE
  1200. CreateQTDList(transfer.pipe, transfer.pipe.direction, transfer.len, transfer.buffer, first, last, count, FALSE)
  1201. END;
  1202. (* last is the end of the new qTD list: mark its next link as invalid *)
  1203. last.next := SYSTEM.VAL(LONGINT, QtdTerminate);
  1204. IF transfer.pipe.ioc THEN
  1205. dword := SYSTEM.VAL(SET, last.token) + QtdIoc;
  1206. last.token := SYSTEM.VAL(LONGINT, dword)
  1207. END;
  1208. (*Machine.FlushDCacheRange(last, sizeQtd);*)
  1209. (* Find the last qtd for the queue head and append the list 'first' to them. Acquire the pipe descriptor lock for that. *)
  1210. (*transfer.pipe.descriptorLock.Acquire;*)
  1211. (*IF ~ScheduleOn(CmdAsyncSchedEnable, FALSE) THEN
  1212. (*TRACE('BEEEEEEEEEP')*)
  1213. END;*)
  1214. (*Machine.InvalidateDCacheRange(qh, sizeQh);*)
  1215. dword := SYSTEM.VAL(SET, qh.token);
  1216. IF dword * TdHalted # {} THEN
  1217. IF Debug.Trace & Debug.tracePipes THEN Show("LinkTDs: Automatically clear halt condition"); KernelLog.Ln; END;
  1218. ClearHalt(transfer.pipe);
  1219. END;
  1220. REPEAT UNTIL (SYSTEM.VAL(SET, qh.token) * TdActive = {}) OR (SYSTEM.GET32(iobase + HcAsyncListAddr) # qh);
  1221. (*IF dword * TdActive # {} THEN Show("Schedule: pipe is active"); KernelLog.Ln; ShowQueueHead(qh, qh.current, cap64bit); Wait(10000) END;*)
  1222. IF (SYSTEM.VAL(SET, qh.next) * QhTerminate # {}) OR (qh.next = 0) THEN
  1223. qh.next := first
  1224. ELSE
  1225. curr := qh.next;
  1226. WHILE (SYSTEM.VAL(SET, curr.next) - QhTerminate # {}) & (QhTerminate * SYSTEM.VAL(SET, curr.next) = {}) DO
  1227. curr := curr.next
  1228. END;
  1229. curr.next := first;
  1230. END;
  1231. (*IF dword * TdActive # {} THEN Show("Schedule: pipe is active"); KernelLog.Ln; ShowQueueHead(qh, qh.next, cap64bit); LOOP END END;*)
  1232. (*KernelLog.String("========================================================="); KernelLog.Ln;
  1233. TRACE(transfer.len);*)
  1234. (*ShowQueueHead(qh, first, cap64bit);*)
  1235. IF ~ScheduleOn(CmdAsyncSchedEnable, TRUE) THEN
  1236. KernelLog.Enter;
  1237. KernelLog.String("UsbEhci: could not enable ansynchronous scheduling");
  1238. KernelLog.Exit
  1239. END;
  1240. (* Update transfer info with tds *)
  1241. i := 0;
  1242. curr := first;
  1243. WHILE i < count DO
  1244. ASSERT(SYSTEM.VAL(SET, curr) * QtdTerminate = {});
  1245. transfer.tds[i] := curr;
  1246. curr := curr.next;
  1247. INC(i)
  1248. END;
  1249. ASSERT(i = count);
  1250. ASSERT(SYSTEM.VAL(SET, curr) * QtdTerminate # {});
  1251. transfer.status := Usbdi.InProgress;
  1252. (* if pipe supports toggle, toggle *)
  1253. IF (transfer.pipe.type = UsbHcdi.PipeBulk) OR (transfer.pipe.type = UsbHcdi.PipeInterrupt) THEN
  1254. IF transfer.pipe.dataToggle THEN
  1255. transfer.pipe.dataToggle := FALSE
  1256. ELSE
  1257. transfer.pipe.dataToggle := TRUE
  1258. END
  1259. END
  1260. END ScheduleAperiodic;
  1261. (** Create TDs for a control pipe request *)
  1262. PROCEDURE CreateControlTDs (pipe: UsbHcdi.Pipe; direction: LONGINT; msg: UsbHcdi.ControlMessage; buffer: (*ARRAY OF CHAR*) ADDRESS; len: LONGINT;VAR firstTD, lastTD: Qtd; VAR tdCount: LONGINT);
  1263. VAR
  1264. curr, next, last: Qtd;
  1265. dword: SET;
  1266. ranges: POINTER TO ARRAY OF Machine.Range;
  1267. count, i, numRanges: LONGINT;
  1268. BEGIN
  1269. IF (pipe.speed = UsbHcdi.LowSpeed) OR (pipe.speed = UsbHcdi.FullSpeed) THEN
  1270. IF pipe.maxRetries = 0 THEN
  1271. (* For low-speed and full-speed devices, the value 0 is not allowed *)
  1272. pipe.maxRetries := 3;
  1273. END;
  1274. END;
  1275. (* Stage1: Control setup transaction *)
  1276. firstTD := allocator.Allocate(sizeQtd);
  1277. curr := firstTD;
  1278. AssertAlignment(curr, alignQtd);
  1279. curr.alternateNext := SYSTEM.VAL(LONGINT, QtdTerminate); (* Mark Alternate Next qTD Pointer as invalid *)
  1280. dword := LSH(SYSTEM.VAL(SET, pipe.maxRetries), 10) * QtdErrorCounter; (* DataToggle = FALSE; Current Page = 0; no IOC *)
  1281. dword := dword + LSH(SYSTEM.VAL(SET, 8), 16) * QtdBytesToTransfer; (* 8byte control message *)
  1282. dword := dword + LSH(SYSTEM.VAL(SET, PidSetup), 8) * QtdPidCode + TdActive;
  1283. curr.token := SYSTEM.VAL(LONGINT, dword);
  1284. i := (LEN(msg) DIV PageSize) + 1;
  1285. REPEAT
  1286. NEW(ranges, i);
  1287. Machine.TranslateVirtual(UsbBuffers.GetDataAddress(msg), 8, numRanges, ranges^);
  1288. INC(i)
  1289. UNTIL numRanges # 0;
  1290. (* The HC will access the next buffer pointer when the buffer crosses a physical page... *)
  1291. curr.buffers[0] := ranges[0].adr;
  1292. IF numRanges > 1 THEN (* buffer is across page boundaries *)
  1293. curr.buffers[1] := ranges[1].adr
  1294. ELSE
  1295. curr.buffers[1] := 0
  1296. END;
  1297. curr.buffers[2] := 0;
  1298. curr.buffers[3] := 0;
  1299. curr.buffers[4] := 0;
  1300. (*SYSTEM.PUT32(qtd + QtdBufferPtr2, 0);
  1301. SYSTEM.PUT32(qtd + QtdBufferPtr3, 0);
  1302. SYSTEM.PUT32(qtd + QtdBufferPtr4, 0);*)
  1303. IF cap64bit THEN
  1304. curr.extBuffers[0] := 0;
  1305. curr.extBuffers[1] := 0;
  1306. curr.extBuffers[2] := 0;
  1307. curr.extBuffers[3] := 0;
  1308. curr.extBuffers[4] := 0;
  1309. (*SYSTEM.PUT32(qtd + QtdExtBufferPtr0, 0);
  1310. SYSTEM.PUT32(qtd + QtdExtBufferPtr1, 0);
  1311. SYSTEM.PUT32(qtd + QtdExtBufferPtr2, 0);
  1312. SYSTEM.PUT32(qtd + QtdExtBufferPtr3, 0);
  1313. SYSTEM.PUT32(qtd + QtdExtBufferPtr4, 0);*)
  1314. END;
  1315. (*Machine.FlushDCacheRange(curr, sizeQtd);*)
  1316. (* Setup phase always starts with dataToggle = FALSE, so now it must be TRUE *)
  1317. pipe.dataToggle := TRUE;
  1318. (* Stage 2: Optional data stage *)
  1319. IF len # 0 THEN
  1320. CreateQTDList(pipe, direction, len, buffer, next, last, count, TRUE)
  1321. END;
  1322. IF count = 0 THEN
  1323. last := curr
  1324. ELSE
  1325. curr.next := next;
  1326. (*Machine.FlushDCacheRange(curr, sizeQtd)*)
  1327. END;
  1328. curr := allocator.Allocate(sizeQtd);
  1329. last.next := curr;
  1330. (*Machine.FlushDCacheRange(last, sizeQtd);*)
  1331. AssertAlignment(curr, alignQtd);
  1332. (* stage 3: status: build status TD *)
  1333. curr.next := SYSTEM.VAL(LONGINT, QtdTerminate);
  1334. curr.alternateNext := SYSTEM.VAL(LONGINT, QtdTerminate);
  1335. dword := QtdDataToggle + TdActive; (* dataToggle always TRUE and set ind TD in status stage; CC = not accessed *)
  1336. IF (direction = UsbHcdi.Out) OR (len = 0) THEN
  1337. dword := dword + LSH(SYSTEM.VAL(SET, PidIn), 8);
  1338. ELSE
  1339. dword := dword + LSH(SYSTEM.VAL(SET, PidOut), 8);
  1340. (*TRACE(pipe.speed);*)
  1341. (*IF pipe.speed = UsbHcdi.HighSpeed THEN (* Do PING protocol *)
  1342. dword := dword + TdPingState;
  1343. END;*)
  1344. END;
  1345. dword := dword + LSH(SYSTEM.VAL(SET, pipe.maxRetries), 10) * QtdErrorCounter;
  1346. IF pipe.ioc THEN dword := dword + QtdIoc; END; (* Set interrupt on completion bit *)
  1347. curr.token := SYSTEM.VAL(LONGINT, dword);
  1348. curr.buffers[0] := 0;
  1349. curr.buffers[1] := 0;
  1350. curr.buffers[2] := 0;
  1351. curr.buffers[3] := 0;
  1352. curr.buffers[4] := 0;
  1353. IF cap64bit THEN
  1354. curr.extBuffers[0] := 0;
  1355. curr.extBuffers[1] := 0;
  1356. curr.extBuffers[2] := 0;
  1357. curr.extBuffers[3] := 0;
  1358. curr.extBuffers[4] := 0;
  1359. END;
  1360. Machine.FlushDCacheRange(UsbBuffers.GetDataAddress(msg), LEN(msg));
  1361. (*Machine.FlushDCacheRange(buffer, len);*)
  1362. (*Machine.FlushDCacheRange(curr, sizeQtd);*)
  1363. lastTD := curr;
  1364. tdCount := count + 2
  1365. END CreateControlTDs;
  1366. (** Create qTD list *)
  1367. PROCEDURE CreateQTDList (pipe: UsbHcdi.Pipe; direction, len: LONGINT; buffer: ADDRESS; VAR firstTD, lastTD: Qtd; VAR tdCount: LONGINT; tdToggle: BOOLEAN);
  1368. VAR
  1369. prev, qtd: Qtd;
  1370. restlen, curlen, temp: LONGINT;
  1371. i, j: LONGINT;
  1372. dword: SET;
  1373. numRanges, idx, offset: LONGINT;
  1374. sgList: POINTER TO ARRAY OF Machine.Range;
  1375. buf: ADDRESS;
  1376. t: HUGEINT;
  1377. BEGIN
  1378. ASSERT((pipe.maxRetries >= 0) & (pipe.maxRetries <= 3));
  1379. Machine.FlushDCacheRange(buffer, len);
  1380. j := (len DIV PageSize) + 1;
  1381. REPEAT
  1382. NEW(sgList, j);
  1383. Machine.TranslateVirtual(buffer, len, numRanges, sgList^);
  1384. INC(j, 1)
  1385. UNTIL numRanges # 0;
  1386. (*KernelLog.Enter; KernelLog.Address(buffer); KernelLog.String(" -> "); KernelLog.Address(sgList[0].adr); KernelLog.Exit;*)
  1387. idx := 0;
  1388. offset := 0; (* offset from last qTD (must fill multiples of packetSize into qTD buffers) *)
  1389. curlen := 0; (* amount of data that is transferred in a single qTD *)
  1390. restlen := len; (* total amount of data to be transferred *)
  1391. firstTD := 0;
  1392. lastTD := 0;
  1393. tdCount := 0;
  1394. buf := sgList[idx].adr + offset;
  1395. WHILE restlen > 0 DO (* build qTD chain *)
  1396. (*TRACE(restlen);*)
  1397. (* allocate the next qTD of the chain *)
  1398. qtd := allocator.Allocate(sizeQtd);
  1399. IF SYSTEM.VAL(ADDRESS, prev) = 0 THEN
  1400. firstTD := qtd
  1401. ELSE
  1402. buf := GetNextPhysicalPage(sgList^, idx, buf);
  1403. prev.next := qtd;
  1404. (*Machine.FlushDCacheRange(prev, sizeQtd)*)
  1405. END;
  1406. (* Each qTD has four buffer pointers. Each buffer is 4K. The buffer must be virtually contiguous but may be *)
  1407. (* physically non-contiguous. The HC detects crossings of page boundaries and increments the current buffer pointer. *)
  1408. qtd.buffers[0] := buf;
  1409. curlen := PageSize - LONGINT(buf MOD PageSize);
  1410. (*TRACE(curlen);*)
  1411. IF curlen > restlen THEN (* No other buffer pointers needed, fits into the first page *)
  1412. curlen := restlen;
  1413. END;
  1414. (*TRACE(curlen);*)
  1415. ASSERT(curlen > 0);
  1416. restlen := restlen - curlen; offset := 0;
  1417. (*TRACE(restlen);*)
  1418. (* Fill in the other 4 buffer pointers *)
  1419. FOR j := 1 TO 4 DO
  1420. IF restlen <= 0 THEN
  1421. (*TRACE(0);*)
  1422. qtd.buffers[j] := 0;
  1423. ELSE
  1424. IF j = 4 THEN (* last buffer available in this qTD *)
  1425. temp := PageSize - ((curlen + PageSize) MOD pipe.maxPacketSize); (* data that fits into the last buffer (max) *)
  1426. IF restlen > temp THEN
  1427. (* The HC will issues USB transaction at pipe.maxPacketSize granularity. If this is not the *)
  1428. (* last qTD of this qTD chain, curlen must be multiple of pipe.maxPacketSize. If the last qTD of this *)
  1429. (* chain was not a multiple of pipe.maxPacketSize, the device will send more data (since we *)
  1430. (* requested more data) and the HC thinks it's a babble. *)
  1431. curlen := curlen + temp; restlen := restlen - temp; offset := temp;
  1432. (*TRACE(curlen);*)
  1433. buf := GetNextPhysicalPage(sgList^, idx, buf);
  1434. ASSERT(buf MOD PageSize = 0);
  1435. qtd.buffers[j] := buf;
  1436. IF temp < PageSize THEN
  1437. DEC(buf, PageSize - temp)
  1438. END;
  1439. ELSE (* this is the last qTD in chains *)
  1440. curlen := curlen + restlen; restlen := 0;
  1441. (*TRACE(curlen);*)
  1442. buf := GetNextPhysicalPage(sgList^, idx, buf);
  1443. ASSERT(buf MOD PageSize = 0);
  1444. qtd.buffers[j] := buf
  1445. END;
  1446. ELSE
  1447. IF restlen > PageSize THEN
  1448. (*TRACE(curlen);*)
  1449. curlen := curlen + PageSize; restlen := restlen - PageSize;
  1450. ELSE
  1451. (*TRACE(curlen);*)
  1452. curlen := curlen + restlen; restlen := 0;
  1453. END;
  1454. buf := GetNextPhysicalPage(sgList^, idx, buf);
  1455. ASSERT(buf MOD PageSize = 0);
  1456. qtd.buffers[j] := buf;
  1457. END
  1458. END
  1459. END;
  1460. IF cap64bit THEN
  1461. qtd.extBuffers[0] := 0;
  1462. qtd.extBuffers[1] := 0;
  1463. qtd.extBuffers[2] := 0;
  1464. qtd.extBuffers[3] := 0;
  1465. qtd.extBuffers[4] := 0;
  1466. END;
  1467. qtd.alternateNext := SYSTEM.VAL(LONGINT, QtdTerminate); (* Mark Alternate Next qTD Pointer as invalid *)
  1468. (*TRACE(curlen);*)
  1469. ASSERT(curlen <= 5000H); (* Maximum allowed value for a single qTD: 5*4KB *)
  1470. dword := TdActive;
  1471. dword := dword + LSH(SYSTEM.VAL(SET, pipe.maxRetries), 10) * QtdErrorCounter; (* Current Page=0 *)
  1472. (*TRACE(dword);*)
  1473. dword := dword + LSH(SYSTEM.VAL(SET, curlen), 16) * QtdBytesToTransfer;
  1474. (*TRACE(dword);*)
  1475. IF tdToggle THEN
  1476. IF pipe.dataToggle THEN dword := dword + QtdDataToggle; END;
  1477. (* Calculate datatoggle value for next TD *)
  1478. IF (curlen DIV pipe.maxPacketSize) MOD 2 # 0 THEN
  1479. pipe.dataToggle := ~pipe.dataToggle;
  1480. END;
  1481. END;
  1482. IF direction = UsbHcdi.In THEN
  1483. dword := dword + LSH(SYSTEM.VAL(SET, PidIn), 8);
  1484. ELSIF direction = UsbHcdi.Out THEN
  1485. dword := dword + LSH(SYSTEM.VAL(SET, PidOut), 8);
  1486. IF pipe.speed = UsbHcdi.HighSpeed THEN (* Do PING protocol *)
  1487. dword := dword + TdPingState;
  1488. END;
  1489. END;
  1490. (*TRACE(dword);*)
  1491. qtd.token := SYSTEM.VAL(LONGINT, dword);
  1492. (*TRACE(SYSTEM.VAL(SET, qtd.token));*)
  1493. (*Machine.FlushDCacheRange(qtd, sizeQtd);*)
  1494. prev := qtd;
  1495. INC(tdCount)
  1496. END;
  1497. lastTD := qtd;
  1498. END CreateQTDList;
  1499. PROCEDURE ScheduleIso (transfer: UsbHcdi.TransferToken);
  1500. VAR
  1501. sgList: ARRAY 1024 OF Machine.Range;
  1502. itds: ARRAY UsbHcdi.MaxTDs OF ADDRESS;
  1503. first, last, itd: Itd;
  1504. adr, currBuf, currOfs, currRange, currTrans, i, itdTxSize, j,
  1505. numFrames, numRanges, remLen, transSize: LONGINT;
  1506. fridx, interval, frame: LONGINT;
  1507. firstEntry: ADDRESS;
  1508. dword: SET;
  1509. BEGIN
  1510. Machine.TranslateVirtual(transfer.buffer, transfer.len, numRanges, sgList);
  1511. Machine.FlushDCacheRange(transfer.buffer, transfer.len);
  1512. IF numRanges = 0 THEN
  1513. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: Schedule: Scatter/Gather list too small"); KernelLog.Ln; END;
  1514. transfer.pipe.status := Usbdi.Error; transfer.pipe.errors := transfer.pipe.errors + UsbHcdi.TransferTooLarge;
  1515. END;
  1516. (* Compute transfer parameters *)
  1517. remLen := transfer.len;
  1518. (*KernelLog.String(":: Iso TX size: "); KernelLog.Int(transfer.len, 0); KernelLog.Ln;
  1519. KernelLog.String(":: iTD Size: "); KernelLog.Int(sizeItd, 0); KernelLog.Ln;*)
  1520. (* Prepare iTDs *)
  1521. first := allocator.Allocate(sizeItd);
  1522. itd := first;
  1523. IF itd = ADDRESS(0) THEN
  1524. KernelLog.String("ITD = 0, ABORTING");
  1525. KernelLog.Ln;
  1526. ASSERT(itd # ADDRESS(0))
  1527. END;
  1528. numFrames := 0;
  1529. currRange := 0;
  1530. currOfs := sgList[currRange].adr MOD 4096;
  1531. adr := sgList[currRange].adr - sgList[currRange].adr MOD 4096;
  1532. REPEAT
  1533. itdTxSize := 0;
  1534. currBuf := 0;
  1535. currTrans := 0;
  1536. itd.buffers[currBuf] := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, adr) * ItdBufferPtr);
  1537. (* iTD transactions *)
  1538. WHILE (remLen > 0) & (currTrans < 8) DO
  1539. (* Compute buffer offset and buffer page *)
  1540. IF currOfs >= 4096 THEN
  1541. (* Buffer offset overflow, allocate new buffer *)
  1542. currOfs := currOfs MOD 4096;
  1543. INC(currBuf);
  1544. INC(adr, 4096);
  1545. IF adr >= sgList[currRange].adr + sgList[currRange].size THEN
  1546. INC(currRange);
  1547. adr := sgList[currRange].adr - sgList[currRange].adr MOD 4096
  1548. END;
  1549. dword := SYSTEM.VAL(SET, adr) * ItdBufferPtr;
  1550. itd.buffers[currBuf] := SYSTEM.VAL(LONGINT, dword)
  1551. END;
  1552. (* Compute transaction size and evaluate need for IOC *)
  1553. transSize := transfer.pipe.maxPacketSize * transfer.pipe.mult;
  1554. dword := {};
  1555. IF remLen <= transSize THEN
  1556. (* Last transaction: transfer remaining bytes and set IOC *)
  1557. transSize := remLen;
  1558. dword := ItdTransactionIoc
  1559. END;
  1560. (* Write transaction *)
  1561. dword := dword + ItdActive + SYSTEM.VAL(SET, LSH(transSize, 16)) + SYSTEM.VAL(SET, LSH(currBuf, 12)) * {12 .. 14} + SYSTEM.VAL(SET, currOfs) * {0 .. 11};
  1562. itd.transactions[currTrans] := SYSTEM.VAL(LONGINT, dword);
  1563. INC(itdTxSize, transSize);
  1564. DEC(remLen, transSize);
  1565. INC(currOfs, transSize);
  1566. INC(currTrans)
  1567. (*;KernelLog.String(":: iTD "); KernelLog.Int((itd - pipe.firstTD) DIV sizeItd, 0);
  1568. (*KernelLog.String(" :: Transaction "); KernelLog.Int(currTrans, 0);
  1569. KernelLog.String(" :: TX Size "); KernelLog.Int(transSize, 0);
  1570. KernelLog.String(" :: Remaining "); KernelLog.Int(remLen, 0);*) KernelLog.Ln;*)
  1571. END;
  1572. (* First buffer is always used.
  1573. * This buffer can be the same as the last buffer of the previous iTD or the next physical page,
  1574. * depending on the overflow condition of currOfs. *)
  1575. IF currOfs >= 4096 THEN
  1576. (* Buffer offset overflow, allocate new buffer *)
  1577. currOfs := currOfs MOD 4096;
  1578. INC(currBuf);
  1579. INC(adr, 4096);
  1580. IF adr >= sgList[currRange].adr + sgList[currRange].size THEN
  1581. INC(currRange);
  1582. adr := sgList[currRange].adr - sgList[currRange].adr MOD 4096
  1583. END;
  1584. IF currBuf < 7 THEN
  1585. dword := SYSTEM.VAL(SET, adr) * ItdBufferPtr;
  1586. itd.buffers[currBuf] := SYSTEM.VAL(LONGINT, dword);
  1587. END
  1588. END;
  1589. (* Next Link *)
  1590. dword := {0};
  1591. itd.next := 1(*SYSTEM.VAL(LONGINT, dword)*);
  1592. (* iTD endpoint parameters *)
  1593. dword := SYSTEM.VAL(SET, itd.buffers[0]);
  1594. dword := dword * ItdBufferPtr + SYSTEM.VAL(SET, LSH(transfer.pipe.endpoint, 8)) * {8 .. 11} + SYSTEM.VAL(SET, transfer.pipe.address) * {0 .. 6};
  1595. itd.buffers[0] := SYSTEM.VAL(LONGINT, dword);
  1596. dword := SYSTEM.VAL(SET, itd.buffers[1]);
  1597. dword := dword * ItdBufferPtr + SYSTEM.VAL(SET, transfer.pipe.maxPacketSize) * {0 .. 10};
  1598. IF transfer.pipe.direction = UsbHcdi.In THEN INCL(dword, 11) END;
  1599. itd.buffers[1] := SYSTEM.VAL(LONGINT, dword);
  1600. dword := SYSTEM.VAL(SET, itd.buffers[2]);
  1601. dword := dword * ItdBufferPtr + SYSTEM.VAL(SET, transfer.pipe.mult) * {0 .. 1};
  1602. itd.buffers[2] := SYSTEM.VAL(LONGINT, dword);
  1603. (*Machine.FlushDCacheRange(itd, sizeItd);*)
  1604. (*ShowItd(itd, 0);
  1605. TRACE(SYSTEM.GET32(iobase + HcPeriodicListBase));*)
  1606. itds[numFrames] := itd;
  1607. INC(numFrames);
  1608. IF remLen > 0 THEN
  1609. itd := allocator.Allocate(sizeItd);
  1610. END;
  1611. UNTIL (remLen = 0) OR (numFrames = framelistSize);
  1612. IF (*numFrames >= framelistSize*) remLen > 0 THEN
  1613. KernelLog.String("UsbEhci: cannot schedule more than 1 frame list (");
  1614. KernelLog.Int(framelistSize, 0);
  1615. KernelLog.String(" iTDs) at a time. Transfer is too big.");
  1616. KernelLog.Ln;
  1617. ASSERT(remLen = 0)
  1618. END;
  1619. last := itd;
  1620. (*KernelLog.String("Number of iTDs: "); KernelLog.Int(numFrames, 0); KernelLog.Ln;
  1621. KernelLog.String("Total tx size: "); KernelLog.Int(transfer.len, 0); KernelLog.Ln;*)
  1622. (**TRACE('Sched acquire FL');*)
  1623. (*framelistLock.Acquire;*)
  1624. (*IF ~ScheduleOn(CmdPeriodicSchedEnable, FALSE) THEN TRACE('BOOOOOOOOOOOOOOOOOOOOOOP') ELSE (*TRACE('YOUHOU')*) END;*)
  1625. (* Link iTDs: find next framelist offset *)
  1626. (*!interval := LSH(1, pipe.irqInterval) DIV 8;
  1627. IF interval = 0 THEN interval := 1 END;*)
  1628. BEGIN {EXCLUSIVE}
  1629. interval := 1;
  1630. frame := framelist.base;
  1631. (* We must put the iTDs at this offset from the base of the periodic frame list: current index + 1 for uncertainty + 1 to get the next *)
  1632. SYSTEM.GET(iobase + HcFrIndex, fridx);
  1633. (*TRACE(fridx, LSH(fridx, -3) MOD framelistSize);*)
  1634. fridx := (LSH(fridx, -3) MOD framelistSize) + 1 + framelistOfs + 2;
  1635. firstEntry := ADDRESSOF(framelist.data[frame + fridx]);
  1636. (*TRACE(SYSTEM.GET32(iobase + HcPeriodicListBase), ADDRESSOF(framelist.data[framelist.base]));*)
  1637. (* Insert iTDs *)
  1638. (*TRACE(fridx);*)
  1639. (*ShowFramelist(ADDRESSOF(framelist.data[framelist.base]), framelistSize, LSH(SYSTEM.GET32(iobase + HcFrIndex), -3) MOD framelistSize, FALSE);*)
  1640. (*KernelLog.Enter; KernelLog.String("Link HC: "); KernelLog.Int(fridx - 3 - framelistOfs, 0); KernelLog.Exit;*)
  1641. FOR i := 0 TO numFrames - 1 DO
  1642. itd := itds[i];
  1643. ASSERT(SYSTEM.VAL(ADDRESS, itd) # 0);
  1644. fridx := fridx MOD framelistSize;
  1645. (* Get the next free entry *)
  1646. WHILE framelist.data[frame + fridx] MOD 8 = 0 DO fridx := (fridx + interval) MOD framelistSize END;
  1647. (*ShowItd(itd, 0);*)
  1648. (* Not an itd entry *)
  1649. itd.next := framelist.data[frame + fridx];
  1650. (*KernelLog.Enter; KernelLog.String("Link at "); KernelLog.Int(fridx, 0); KernelLog.Exit;*)
  1651. (*SYSTEM.PUT32(frame + 4 * fridx, SYSTEM.VAL(SET, itd) * {5 .. 31}); (* Typ = 00 and T = 0 *)*)
  1652. framelist.data[frame + fridx] := SYSTEM.VAL(ADDRESS, itd);
  1653. (* Store the address of the framelist entry instead of ITD directly *)
  1654. transfer.tds[i] := ADDRESSOF(framelist.data[frame + fridx]);
  1655. INC(fridx, interval)
  1656. END;
  1657. END;
  1658. (*lastEntry := ADDRESSOF(framelist.data[frame + fridx + 1]);*)
  1659. (*framelistLock.Release;*)
  1660. (*Machine.FlushDCacheRange(ADDRESSOF(framelist.data[frame]), framelistSize * 4);*)
  1661. (*TRACE('Sched release FL');*)
  1662. (*ShowFramelist(ADDRESSOF(framelist.data[framelist.base]), framelistSize, LSH(SYSTEM.GET32(iobase + HcFrIndex), -3) MOD framelistSize, FALSE);*)
  1663. IF SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsPeriodicSchedule = {} THEN
  1664. (*TRACE('PERIODIC SCHEDULE DISABLED');*)
  1665. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  1666. IF dword * CmdPeriodicSchedEnable = {} THEN
  1667. IF ~ScheduleOn(CmdPeriodicSchedEnable, TRUE) & (Debug.Level >= Debug.Errors) THEN Show("Failed to re-enable periodic schedule."); KernelLog.Ln; END;
  1668. END
  1669. END;
  1670. transfer.status := Usbdi.InProgress;
  1671. (*KernelLog.Enter; KernelLog.String(":: Entered iTDs at frame #"); KernelLog.Int(frame, 0); KernelLog.Ln;*)
  1672. (*KernelLog.String(":: Isochronous Scheduling Threshold: "); KernelLog.Int(frameListOfs, 0); KernelLog.Exit;*)
  1673. END ScheduleIso;
  1674. (** Schedule an interrupt transfer: create an iTD, link to QH from it and place it in framelist. *)
  1675. PROCEDURE ScheduleInterrupt (transfer: UsbHcdi.TransferToken);
  1676. VAR
  1677. itd: Itd;
  1678. qh: Qh;
  1679. frame, fridx: LONGINT;
  1680. BEGIN
  1681. (*itd := transfer.pipe.descriptors[1];*)
  1682. qh := transfer.pipe.descriptors[0];
  1683. (*itd.next := QhHorizontalLink(qh, QhTypQh, FALSE);*)
  1684. frame := framelist.base;
  1685. (* We must put the iTDs at this offset from the base of the periodic frame list: current index + 1 for uncertainty + 1 to get the next *)
  1686. fridx := SYSTEM.GET32(iobase + HcFrIndex);
  1687. fridx := (LSH(fridx, -3) MOD framelistSize) + 1 + framelistOfs;
  1688. framelist.data[frame + fridx] := QhHorizontalLink(qh, QhTypQh, FALSE);
  1689. END ScheduleInterrupt;
  1690. PROCEDURE InterruptHandler;
  1691. VAR s : SET;
  1692. BEGIN
  1693. IF Debug.Stats THEN INC(NnofInterrupts); END;
  1694. IF state >= UsbHcdi.Initialized THEN
  1695. s := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * interruptsEnabled;
  1696. (* Reset interrupt status register (Write clear)*)
  1697. SYSTEM.PUT32(iobase + HcUsbSts, s (** {0..5}*)); FlushPCI;
  1698. handler.Handle(s)
  1699. END
  1700. END InterruptHandler;
  1701. PROCEDURE DoHandleInterrupt (s: SET);
  1702. BEGIN (* Works without being exclusive *)
  1703. IF s # {} THEN
  1704. IF Debug.Stats THEN INC(NnofInterruptsHandled); END;
  1705. IF Debug.Trace & Debug.traceInterrupts THEN
  1706. Show("Interrupt: "); ShowInterrupts(s); KernelLog.Ln;
  1707. END;
  1708. IF s * StsAsyncAdvance # {} THEN hcHandshake := TRUE; END;
  1709. IF s * StsHostSystemError # {} THEN
  1710. Show("Serious error. Please restart the EHCI driver:");
  1711. IF SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsHcHalted # {} THEN
  1712. KernelLog.String(" [HC halted]");
  1713. SetState(UsbHcdi.Halted);
  1714. END;
  1715. KernelLog.Ln;
  1716. END;
  1717. IF s * StsFrameListRollover # {} THEN END;
  1718. IF s * StsPortChange # {} THEN
  1719. (* TODO: If wake-up, time 20ms, poll PscSuspend... enable HC if necessary*)
  1720. IF statusChangeHandler # NIL THEN statusChangeHandler(Usbdi.Ok, 0); END;
  1721. END;
  1722. IF (s * (StsUsbError + StsUsbInterrupt) # {}) OR (19 IN s) THEN (* USB Interrupt occured: can be IOC or ShortPacketInt *)
  1723. NotifyCompletionHandlers;
  1724. END;
  1725. END;
  1726. END DoHandleInterrupt;
  1727. (* re-evaluate the status of the pipe's qh (endpoint descriptor) and its TD list *)
  1728. PROCEDURE UpdatePipeStatus * (pipe : UsbHcdi.Pipe);
  1729. BEGIN
  1730. CASE pipe.type OF
  1731. UsbHcdi.PipeControl, UsbHcdi.PipeBulk, UsbHcdi.PipeInterrupt:
  1732. UpdatePipeStatusAsync(pipe)
  1733. |UsbHcdi.PipeIsochronous:
  1734. UpdatePipeStatusIso(pipe)
  1735. ELSE
  1736. (* Unknown pipe type *)
  1737. HALT(8)
  1738. END
  1739. END UpdatePipeStatus;
  1740. PROCEDURE UpdatePipeStatusAsync (pipe: UsbHcdi.Pipe);
  1741. CONST
  1742. MaxLoops = 10000;
  1743. VAR
  1744. qh: Qh;
  1745. after, before, qtd: Qtd;
  1746. transfer: UsbHcdi.TransferToken;
  1747. s, errors: SET;
  1748. i, restLen, len: LONGINT;
  1749. loop: LONGINT;
  1750. active: BOOLEAN;
  1751. BEGIN {EXCLUSIVE}
  1752. FlushPCI;
  1753. (* First look up active bit in the QH tranfer overlay *)
  1754. qh := pipe.descriptors[0];
  1755. (*Machine.InvalidateDCacheRange(qh, sizeQh);*)
  1756. (*s := SYSTEM.VAL(SET, qh.token);
  1757. (*ShowQueueHead(qh, 0, cap64bit); Wait(100);*)
  1758. IF (s * TdActive # {}) & (s * (TdHalted + TdDataBufferError + TdBabbleDetected + TdTransactionError + TdMissedMicroFrame) = {}) THEN
  1759. (* The HC hasn't yet executed the transaction *)
  1760. RETURN
  1761. END;*)
  1762. (*!
  1763. Disabling scheduling here leads to problems: some of the transfers does not get finished!
  1764. *)
  1765. (*REPEAT UNTIL (SYSTEM.VAL(SET, qh.token) * TdActive = {}) OR (SYSTEM.GET32(iobase + HcAsyncListAddr) # qh);*)
  1766. (*qh.token := SYSTEM.VAL(LONGINT, s - TdActive);*)
  1767. (*ASSERT(pipe.transferLock # NIL);*)
  1768. (*pipe.transferLock.Acquire;*)
  1769. transfer := pipe.transfers;
  1770. WHILE transfer # NIL DO
  1771. errors := UsbHcdi.NoErrors;
  1772. active := FALSE;
  1773. IF transfer.status = Usbdi.InProgress THEN
  1774. i := -1;
  1775. WHILE transfer.tds[i + 1] # 0 DO INC(i) END;
  1776. ASSERT(i >= 0);
  1777. qtd := transfer.tds[i];
  1778. IF SYSTEM.VAL(SET, qtd.token) * TdActive # {} THEN
  1779. i := 0;
  1780. (* Examine only transfers that are in progress *)
  1781. WHILE (i < LEN(transfer.tds)) & (transfer.tds[i] # 0) & ~active & (errors = UsbHcdi.NoErrors) DO
  1782. qtd := transfer.tds[i];
  1783. AssertAlignment(qtd, alignQtd);
  1784. (*Machine.InvalidateDCacheRange(qtd, sizeQtd);*)
  1785. s := SYSTEM.VAL(SET, qtd.token) * QtdStatus - TdPingState - TdSplitTransactionState;
  1786. IF s * TdActive # {} THEN
  1787. (* qTD is still active, no errors so far: skip to next transfer *)
  1788. active := TRUE
  1789. END;
  1790. (* At least one error occured *)
  1791. IF s * TdHalted # {} THEN errors := errors + UsbHcdi.Stalled; END;
  1792. IF s * TdDataBufferError # {} THEN errors := errors + UsbHcdi.Databuffer; END;
  1793. IF s * TdBabbleDetected # {} THEN errors := errors + UsbHcdi.Babble; END;
  1794. IF s * TdTransactionError # {} THEN errors := errors + UsbHcdi.CrcTimeout; END;
  1795. IF s * TdMissedMicroFrame # {} THEN errors := errors + UsbHcdi.Internal; END;
  1796. IF ~active & (transfer.len > 0) THEN
  1797. (* Data had to be transfered... *)
  1798. (* The host controller decrements the Total Bytes To Transfer field according the amount of data it did
  1799. transfer. If this field has not the value zero, the host controller did not transfer all data. If there is no
  1800. error reported, this is a short packet condition, which can be okay. *)
  1801. (* len bytes should have been transfered for this TD *)
  1802. len := SYSTEM.VAL(LONGINT, LSH(SYSTEM.VAL(SET, qtd.token) * QtdBytesToTransfer, -16));
  1803. IF (len # 0) THEN (* Short packet *)
  1804. restLen := restLen + len;
  1805. END
  1806. END;
  1807. INC(i)
  1808. END
  1809. END;
  1810. IF active THEN
  1811. (* no update for this transfer: still active *)
  1812. ELSE
  1813. transfer.errors := errors;
  1814. IF errors # UsbHcdi.NoErrors THEN
  1815. transfer.transfered := transfer.len - restLen;
  1816. IF errors * UsbHcdi.Stalled # {} THEN
  1817. transfer.status := Usbdi.Stalled;
  1818. ELSE
  1819. transfer.status := Usbdi.Error;
  1820. END;
  1821. ELSE
  1822. IF restLen = 0 THEN
  1823. transfer.transfered:= transfer.len;
  1824. transfer.status := Usbdi.Ok;
  1825. ELSE
  1826. transfer.transfered := transfer.len - restLen;
  1827. transfer.status := Usbdi.ShortPacket;
  1828. transfer.errors := transfer.errors + UsbHcdi.ShortPacket;
  1829. END
  1830. END;
  1831. (* transfer finished, unlink its TDs *)
  1832. UnlinkTDs(transfer);
  1833. END
  1834. END;
  1835. (*TRACE(pipe, pipe.transferLock, transfer, transfer.next);*)
  1836. transfer := transfer.next
  1837. END;
  1838. (*pipe.transferLock.ReleaseRead;*)
  1839. (*Machine.InvalidateDCacheRange(pipe.descriptors[0], sizeQh);*)
  1840. s := SYSTEM.VAL(SET, qh.token);
  1841. IF s * TdHalted # {} THEN
  1842. ClearHalt(pipe);
  1843. END;
  1844. END UpdatePipeStatusAsync;
  1845. (*
  1846. PROCEDURE UpdatePipeStatusInterrupt (pipe: UsbHcdi.Pipe);
  1847. VAR
  1848. qh: Qh;
  1849. transfer: UsbHcdi.TransferToken;
  1850. s, errors: SET;
  1851. restLen, len: LONGINT;
  1852. active, error: BOOLEAN;
  1853. BEGIN
  1854. FlushPCI;
  1855. (* First look up active bit in the QH tranfer overlay *)
  1856. qh := pipe.descriptors[0];
  1857. Machine.InvalidateDCacheRange(qh, sizeQh);
  1858. s := SYSTEM.VAL(SET, qh.token);
  1859. (*ShowQueueHead(qh, 0, cap64bit); Wait(1000);*)
  1860. IF s * TdActive # {} THEN (* The HC hasn't yet executed the transaction *) RETURN; END;
  1861. errors := UsbHcdi.NoErrors;
  1862. s := SYSTEM.VAL(SET, qh.token) * QtdStatus - TdPingState - TdSplitTransactionState;
  1863. IF s = {} THEN
  1864. (* No errors occured *)
  1865. ELSIF s * TdActive # {} THEN
  1866. (* qTD is still active, no errors so far: skip to next transfer *)
  1867. active := TRUE
  1868. ELSE
  1869. (* At least one error occured *)
  1870. IF s * TdHalted # {} THEN errors := errors + UsbHcdi.Stalled; END;
  1871. IF s * TdDataBufferError # {} THEN errors := errors + UsbHcdi.Databuffer; END;
  1872. IF s * TdBabbleDetected # {} THEN errors := errors + UsbHcdi.Babble; END;
  1873. IF s * TdTransactionError # {} THEN errors := errors + UsbHcdi.CrcTimeout; END;
  1874. IF s * TdMissedMicroFrame # {} THEN errors := errors + UsbHcdi.Internal; END;
  1875. error := TRUE
  1876. END;
  1877. IF ~active & (transfer.len > 0) THEN
  1878. (* Data had to be transfered... *)
  1879. (* The host controller decrements the Total Bytes To Transfer field according the amount of data it did
  1880. transfer. If this field has not the value zero, the host controller did not transfer all data. If there is no
  1881. error reported, this is a short packet condition, which can be okay. *)
  1882. (* len bytes should have been transfered for this TD *)
  1883. len := SYSTEM.VAL(LONGINT, LSH(SYSTEM.VAL(SET, qh.token) * QtdBytesToTransfer, -16));
  1884. IF (len # 0) THEN (* Short packet *)
  1885. restLen := restLen + len;
  1886. END
  1887. END;
  1888. transfer.errors := errors;
  1889. IF error THEN
  1890. transfer.transfered := transfer.len - restLen;
  1891. IF errors * UsbHcdi.Stalled # {} THEN
  1892. transfer.status := Usbdi.Stalled;
  1893. ELSE
  1894. transfer.status := Usbdi.Error;
  1895. END
  1896. ELSE
  1897. IF restLen = 0 THEN
  1898. transfer.transfered:= transfer.len;
  1899. transfer.status := Usbdi.Ok;
  1900. ELSE
  1901. transfer.transfered := transfer.len - restLen;
  1902. transfer.status := Usbdi.ShortPacket;
  1903. transfer.errors := transfer.errors + UsbHcdi.ShortPacket;
  1904. END
  1905. END;
  1906. END UpdatePipeStatusInterrupt;
  1907. *)
  1908. PROCEDURE UpdatePipeStatusIso (pipe: UsbHcdi.Pipe);
  1909. VAR
  1910. transfer: UsbHcdi.TransferToken;
  1911. itd: Itd;
  1912. dword, errors: SET;
  1913. len, tx, totalItd, activeItd: LONGINT;
  1914. active: BOOLEAN;
  1915. BEGIN
  1916. (*ShowFramelist(ADDRESSOF(framelist.data[framelist.base]), framelistSize, LSH(SYSTEM.GET32(iobase + HcFrIndex), -3) MOD framelistSize, TRUE);*)
  1917. (*TRACE('Update acquire Tk Read');*)
  1918. (*pipe.transferLock.AcquireRead;
  1919. framelistLock.Acquire;*)
  1920. transfer := pipe.transfers;
  1921. WHILE (transfer # NIL) DO
  1922. IF transfer.status = Usbdi.InProgress THEN
  1923. errors := {};
  1924. len := 0;
  1925. tx := 0;
  1926. totalItd := 0;
  1927. activeItd := 0;
  1928. active := FALSE;
  1929. (*TRACE('Update acquire FL');*)
  1930. WHILE (totalItd < UsbHcdi.MaxTDs) & (transfer.tds[totalItd] # ADDRESS(0)) (*& (transfer.tds[totalItd] # ADDRESS(1))*) DO
  1931. ASSERT(transfer.tds[totalItd] # ADDRESS(1));
  1932. itd := SYSTEM.GET32(transfer.tds[totalItd]);
  1933. (*(*! TRACE *)KernelLog.Enter; KernelLog.String('Update '); KernelLog.Address(itd); KernelLog.Char(' '); KernelLog.Int(SYSTEM.VAL(ADDRESS, itd) MOD 32, 0);
  1934. KernelLog.Char(' '); KernelLog.Int(SYSTEM.VAL(ADDRESS, itd) DIV 32, 0); KernelLog.Exit;*)
  1935. ASSERT(SYSTEM.VAL(ADDRESS, itd) MOD 32 = 0);
  1936. ASSERT(SYSTEM.VAL(ADDRESS, itd) DIV 32 # 0);
  1937. (*Machine.InvalidateDCacheRange(itd, sizeItd);*)
  1938. FOR tx := 0 TO 7 DO
  1939. dword := SYSTEM.VAL(SET, itd.transactions[tx]);
  1940. IF ItdActive * dword = {} THEN
  1941. (* Transaction finished, examine *)
  1942. errors := errors + dword * ItdTransactionStatus;
  1943. INC(len, LSH(SYSTEM.VAL(LONGINT, dword * ItdTransactionLength), -16));
  1944. ELSE
  1945. active := TRUE;
  1946. END
  1947. END;
  1948. INC(totalItd);
  1949. END;
  1950. (*TRACE('Update release FL');*)
  1951. (*KernelLog.Ln;*)
  1952. IF errors # {} THEN
  1953. (*ShowItd(itd, 8);*)
  1954. IF ItdDataBufferError * errors # {} THEN
  1955. transfer.errors := transfer.errors + UsbHcdi.Databuffer
  1956. END;
  1957. IF ItdBabbleDetected * errors # {} THEN
  1958. transfer.errors := transfer.errors + UsbHcdi.Babble
  1959. END;
  1960. transfer.status := Usbdi.Error;
  1961. IF ItdTransactionError * errors # {} THEN
  1962. transfer.errors := transfer.errors + UsbHcdi.Stalled;
  1963. transfer.status := Usbdi.Stalled
  1964. END;
  1965. BEGIN {EXCLUSIVE}
  1966. UnlinkIso(transfer)
  1967. END
  1968. ELSIF ~active THEN
  1969. IF len = transfer.len THEN
  1970. transfer.transfered := transfer.len;
  1971. transfer.status := Usbdi.Ok
  1972. ELSE
  1973. transfer.transfered := len;
  1974. transfer.status := Usbdi.ShortPacket;
  1975. transfer.errors := pipe.errors + UsbHcdi.ShortPacket;
  1976. END;
  1977. BEGIN {EXCLUSIVE}
  1978. UnlinkIso(transfer)
  1979. END
  1980. ELSE
  1981. (*transfer.transfered := len*)
  1982. END
  1983. END;
  1984. transfer := transfer.next;
  1985. END;
  1986. (*framelistLock.Release;
  1987. pipe.transferLock.ReleaseRead;*)
  1988. (*TRACE('Update release Tk Read');*)
  1989. (*TRACE(activeItd, totalItd)*)
  1990. END UpdatePipeStatusIso;
  1991. (* Reset the host controller. Note: This will NOT assert reset on the USB downstream ports. *)
  1992. PROCEDURE HardwareReset * () : BOOLEAN;
  1993. CONST MaxWaits = 1000; (* Timeout in milliseconds the HC must have completed the reset command *)
  1994. VAR dword : SET; i : LONGINT;
  1995. BEGIN
  1996. (* Host software mustn't reset the host controller when it's running. Stop it and ... *)
  1997. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  1998. SYSTEM.PUT32(iobase + HcUsbCmd, dword - CmdRunStop); FlushPCI;
  1999. (* ... wait until the HC is halted.*)
  2000. i := 1; dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts));
  2001. WHILE (dword * StsHcHalted = {}) & (i <= MaxWaits) DO
  2002. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts));
  2003. INC(i); Wait(1);
  2004. END;
  2005. IF dword * StsHcHalted = {} THEN (* HC did not stop *) RETURN FALSE; END;
  2006. (* Do the actual reset operation *)
  2007. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  2008. SYSTEM.PUT32(iobase + HcUsbCmd, dword + CmdHcReset); FlushPCI;
  2009. (* The host controller should clear the HCRESET bit when it has finished resetting *)
  2010. FOR i := 1 TO MaxWaits DO
  2011. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  2012. IF dword * CmdHcReset = {} THEN
  2013. RETURN TRUE;
  2014. END;
  2015. Wait(1);
  2016. END;
  2017. RETURN FALSE;
  2018. END HardwareReset;
  2019. (* HC moves to UsbSuspend state und almost all operational registers are reset.
  2020. Does not affect the root hub and its downstream ports *)
  2021. PROCEDURE SoftwareReset*() : BOOLEAN;
  2022. BEGIN
  2023. (* TODO: Implement *)
  2024. RETURN FALSE;
  2025. END SoftwareReset;
  2026. (* Initialization of the data structures of the Host Controller Communication Area *)
  2027. PROCEDURE InitFrameList(): BOOLEAN;
  2028. VAR fstn, i, k, j, shift : LONGINT;
  2029. BEGIN
  2030. (* Host controller interface data structures should not cross page-boundaries... 32 byte alignment.
  2031. These queue heads are used as skeleton and never contain any qTDs. *)
  2032. qhlist := UsbHcdi.GetAlignedMemSpace(2048, 4096);
  2033. framelist := UsbHcdi.GetAlignedMemSpace(4096, 4096); (* Must be 4K aligned *)
  2034. framelist := UsbHcdi.GetAlignedMemSpace(1024*1024, 1024*1024); (* Must be 4K aligned *)
  2035. Machine.DisableDCacheRange(ADDRESSOF(framelist.data[framelist.base]), 1024*1024);
  2036. IF capIsoSchedThreshold >= 8 THEN
  2037. framelistOfs := capIsoSchedThreshold MOD 8
  2038. ELSIF capIsoSchedThreshold > 0 THEN
  2039. framelistOfs := 1
  2040. ELSE
  2041. framelistOfs := 0
  2042. END;
  2043. (* Mark all entries of the framelist as invalid *)
  2044. FOR i := 0 TO 1024 - 1 DO
  2045. framelist.data[framelist.base + i] := 1
  2046. END;
  2047. (* Set up QHs. 11 Interrupt QHs and the isochronousQh + fstn *)
  2048. shift := sizeQh DIV 4; ASSERT(sizeQh MOD 4 = 0);
  2049. FOR i := 0 TO 12 DO
  2050. qhlist.data[qhlist.base + i*shift + (QhEpCapabilities1 DIV 4)] := 0;
  2051. qhlist.data[qhlist.base + i*shift + (QhEpCapabilities2 DIV 4)] := 0;
  2052. qhlist.data[qhlist.base + i*shift + (QhQtdToken DIV 4)] := 0;
  2053. qhlist.data[qhlist.base + i*shift + (QhCurrentQtdPointer DIV 4)] := 0;
  2054. qhlist.data[qhlist.base + i*shift + (QhNextQtdPointer DIV 4)] := 1; (* Pointer not valid *)
  2055. qhlist.data[qhlist.base + i*shift + (QhAltNextQtdPointer DIV 4)] := 1; (* Pointer not valid *)
  2056. FOR j := 0 TO 4 DO
  2057. qhlist.data[qhlist.base + i*shift + (QhBufferPointer0 DIV 4) + j] := 0;
  2058. END;
  2059. IF cap64bit THEN
  2060. FOR j := 0 TO 4 DO
  2061. qhlist.data[qhlist.base + i*shift + (QhExtBufferPointer0 DIV 4) + j] := 0;
  2062. END;
  2063. END;
  2064. END;
  2065. (* Addresses of queue heads *)
  2066. NEW(interruptQh);
  2067. FOR i := 0 TO 10 DO interruptQh[i] := Machine.Ensure32BitAddress (ADDRESSOF(qhlist.data[qhlist.base]) + i*sizeQh); END;
  2068. fstn := interruptQh[10] + sizeQh; (* Actually 8 bytes *)
  2069. isochronousQh := fstn + sizeQh;
  2070. FOR i := 10 TO 2 BY -1 DO
  2071. SYSTEM.PUT32(interruptQh[i] + QhHorizontalLinkPointer, interruptQh[i-1] + LSH(QhTypQh, 1));
  2072. END;
  2073. (* Link restore indicator. InterruptQh[1] points to FSTN points to InterruptQh[0] *)
  2074. SYSTEM.PUT32(interruptQh[1] + QhHorizontalLinkPointer, fstn + LSH(QhTypFstn, 1));
  2075. SYSTEM.PUT32(fstn + FstnNormalPathLinkPointer, interruptQh[0] + LSH(QhTypQh, 1));
  2076. SYSTEM.PUT32(fstn + FstnBackPathLinkPointer, QhTerminate); (* Indicates restore indicator *)
  2077. (* Interrupt Qh for 1ms points to isochronousQh *)
  2078. SYSTEM.PUT32(interruptQh[0] + QhHorizontalLinkPointer, isochronousQh + LSH(QhTypQh, 1));
  2079. SYSTEM.PUT32(isochronousQh + QhHorizontalLinkPointer, QhTerminate);
  2080. (* tree structure:
  2081. interrupt[0]: 1ms interrupt[1]: 2ms interrupt[2]: 4ms interrupt[3]: 8ms interrupt[4]: 16ms interrupt[5]: 32ms *)
  2082. (* => end of queue 10 points to 9, end of 8 points to 7 , ..., end of 1 points to 0 *)
  2083. (* => if we start at queue 10, then we will pass all others too; if we start at 9 then we will pass all queues < 9, too etc.*)
  2084. (* queue 0 executes 1024x, queue 1 executes 512x, queue 2 executes 256x, queue 3 executes 128x*)
  2085. (* queue 4 executes 64x, queue 5 executes 32x, queue 6 executes 16x, queue 7 executes 8x*)
  2086. (* queue 8 executes 4x, queue 9 executes 2x, queue 10 executes 1x *)
  2087. (* What does the following mean? => We count the 1's (starting at lsb) until we pass a zero *)
  2088. (* This count gives the queue number for a given slot *)
  2089. FOR i := 0 TO 1023 DO (* i is slot number, we want to calc the queue number (k) for this slot *)
  2090. k := 0; j := i;
  2091. LOOP
  2092. IF (SYSTEM.VAL(SET, j) * {0}) = {} THEN EXIT; END;
  2093. INC(k); j := j DIV 2;
  2094. END;
  2095. framelist.data[framelist.base + i] := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, interruptQh[k]) + {1});
  2096. (*Machine.FlushDCacheRange(ADDRESSOF(framelist.data[framelist.base]), 4096);*)
  2097. END;
  2098. RETURN TRUE;
  2099. END InitFrameList;
  2100. (* Initializes the host controller and builds up the data structures of the HCCA.
  2101. * @param iobase I/O base address (virtual, pointing to capability register at offset 0)
  2102. * @param int Interrupt Line
  2103. * @return TRUE if initialization succeeded, FALSE otherwise
  2104. *)
  2105. PROCEDURE Init * (iobase , irq : LONGINT) : BOOLEAN;
  2106. VAR
  2107. reg : LONGINT;
  2108. dword : SET; qword : HUGEINT;
  2109. ignore : BOOLEAN;
  2110. i : LONGINT;
  2111. BEGIN
  2112. IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbEhci: Starting host controller initialization..."); KernelLog.Ln; END;
  2113. SELF.iobase := iobase; SELF.irq := irq;
  2114. isHighSpeed := TRUE; DMAchaining := TRUE; sgListSize := ScatterGatherListSize;
  2115. (* Read in the Host Controller Capability Registers *)
  2116. (* Get and check EHCI revision *)
  2117. reg := SYSTEM.GET16(iobase + HcCapHciVersion);
  2118. IF reg # 0100H THEN
  2119. KernelLog.String("UsbEhci: Revision of EHCI Programming Interface not supported."); KernelLog.Ln;
  2120. RETURN FALSE;
  2121. END;
  2122. (* Get and parse the HC structural parameters register (HCSPARAM) *)
  2123. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcCapSparams));
  2124. capDebugPortNumber := SYSTEM.VAL(LONGINT, LSH(dword * {20..31}, -20));
  2125. IF dword * {16} # {} THEN capPortIndicators := TRUE; ELSE capPortIndicators := FALSE; END;
  2126. capNbrOfCompanionHc := SYSTEM.VAL(LONGINT, LSH(dword * {12..15}, -12));
  2127. capPortsPerCompanion := SYSTEM.VAL(LONGINT, LSH(dword * {8..11}, -8));
  2128. IF dword * {7} # {} THEN capPortRoutingRules := TRUE; ELSE capPortRoutingRules := FALSE; END;
  2129. IF dword * {4} # {} THEN capPortPowerControl := TRUE; ELSE capPortPowerControl := FALSE; END;
  2130. capNbrOfPorts := SYSTEM.VAL(LONGINT, dword * {0..3});
  2131. (* Get and parse the HC capability parameters register (HCCPARAM) *)
  2132. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcCapCparams));
  2133. capIsoSchedThreshold := SYSTEM.VAL(LONGINT, LSH(dword * {4..7}, -4));
  2134. IF dword * {2} # {} THEN capAsynchSchedPark := TRUE; ELSE capAsynchSchedPark := FALSE; END;
  2135. IF dword * {1} # {} THEN capProgrammableFLG := TRUE; ELSE capProgrammableFLG := FALSE; END;
  2136. IF dword * {0} # {} THEN cap64bit := TRUE; ELSE cap64bit := FALSE; END;
  2137. (* Get the EHCI Extended Capabilities Pointer (EECP) *)
  2138. eecp := SYSTEM.VAL(LONGINT, LSH(SYSTEM.VAL(SET, dword) * {8..15}, - 8));
  2139. (* Get HC companion port route description (60bits, 4bits per port *)
  2140. IF capPortRoutingRules THEN (* HC companion port route description available *)
  2141. qword := SYSTEM.GET32(iobase + HcCapPortroute);
  2142. qword := qword + LSH(SYSTEM.GET32(iobase + HcCapPortroute + 4), 32);
  2143. NEW(hcportroute, 16);
  2144. FOR i := 0 TO 15 DO
  2145. hcportroute[i] := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, LSH(qword, -i)) * {0..3});
  2146. END;
  2147. END;
  2148. (* Program the framelist size *)
  2149. IF capProgrammableFLG THEN (* Size of frame list can be programmed... use constant value *)
  2150. (* TODO: Programm it *)
  2151. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  2152. dword := dword - CmdFrameListSize - {15};
  2153. SYSTEM.PUT32(iobase + HcUsbCmd, dword);
  2154. framelistSize := 1024;
  2155. ELSE
  2156. framelistSize := 1024 (*LSH(SYSTEM.VAL(LONGINT, dword * {2 .. 3}), -2)*);
  2157. (*IF 15 IN dword THEN INC(framelistSize, 4) END;*)
  2158. END;
  2159. IF Debug.Trace & Debug.traceInit THEN
  2160. KernelLog.String("UsbEhci: Set frame list size to "); KernelLog.Int(framelistSize, 0);
  2161. KernelLog.String(" elements."); KernelLog.Ln;
  2162. END;
  2163. (* Build the emulated hub descriptor *)
  2164. dword := {};
  2165. NEW(hubDescriptor, 8);
  2166. hubDescriptor[0] := CHR(7);
  2167. hubDescriptor[1] := CHR(29H); (* Hub Descriptor *)
  2168. hubDescriptor[2] := CHR(capNbrOfPorts);
  2169. IF capPortPowerControl THEN (* If power control is available, EHCI root hubs always provide per port power control *)
  2170. dword := dword + {0};
  2171. ELSE (* No power switching implemented *)
  2172. dword := dword + {1};
  2173. END;
  2174. dword := dword + {3}; (* EHCI root hubs always provide per port overcurrent protection *)
  2175. IF capPortIndicators THEN (* Port indicator control support available *) dword := dword + {7}; END;
  2176. hubDescriptor[3] := CHR(SYSTEM.VAL(LONGINT, dword));
  2177. hubDescriptor[4] := CHR(0); (* Reserved *)
  2178. hubDescriptor[5] := CHR(10); (* 20ms Power on to power good *)
  2179. hubDescriptor[6] := CHR(0); (* Root hubs don't draw current from the USB *)
  2180. (* The Host Controller Capability Registers are readonly so we don't need further access to them and set
  2181. iobase to the base of the Host Controller Operational Registers *)
  2182. iobase := iobase + SYSTEM.GET8(iobase + HcCapLength);
  2183. SELF.iobase := iobase;
  2184. (* Calculate offset from iobase of the port status/controll register for each port *)
  2185. portCount := capNbrOfPorts;
  2186. NEW(ports, portCount);
  2187. FOR i := 0 TO portCount - 1 DO ports[i] := iobase + HcPortSc + i*4; END;
  2188. IF ~HardwareReset() THEN RETURN FALSE; END;
  2189. (* Bluebottle does not yet support 64bit address space. Set the 4GB segment selector for the control data structures to zero. *)
  2190. SYSTEM.PUT32(iobase + HcCtrlDsSegment, 0);
  2191. (* Note that the control data structures must finally be 32byte aligned. Since they occupy subsequent memory location when
  2192. associated with pipes, the value are rounded up to the next value for which value MOD 32 = 0 holds. *)
  2193. IF cap64bit THEN
  2194. sizeQh := 96; (* Actually: 68 Bytes *)
  2195. sizeQtd := 64; (* Actually: 52 Bytes *)
  2196. sizeItd := 96;
  2197. (*alignQh := 128;
  2198. alignQtd := 64;*)
  2199. ELSE
  2200. sizeQh := 64; (* Actually: 48 Byte *)
  2201. sizeQtd := 32;
  2202. sizeItd := 64;
  2203. (*alignQh := 64;
  2204. alignQtd := 32;*)
  2205. END;
  2206. (* Allocation scheme takes care of 4kB page crossing *)
  2207. alignQh := 32;
  2208. alignQtd := 32;
  2209. alignItd := 32;
  2210. IF ~InitFrameList() THEN
  2211. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: Initialization of HCCA failed."); KernelLog.Ln; END;
  2212. RETURN FALSE;
  2213. END;
  2214. (* If the Asynchronous Schedule Park Mode is not available or not enabled, the host controller must not
  2215. * execute more than one bus transaction per queue head, per traversal of the asynchronous schedule. If it
  2216. * is enabled, the host controller may execute Asynchronous Schedule Park Mode Count transaction if the
  2217. * endpoint belongs to a high-speed device. Results in better bus utilization. *)
  2218. IF capAsynchSchedPark THEN
  2219. ASSERT((HcAsyncParkModeCount >= 0) & (HcAsyncParkModeCount < 4));
  2220. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  2221. IF HcAsyncParkModeCount = 0 THEN (* Disable Asynchronous Schedule Park Mode *)
  2222. dword := dword - CmdAsyncSchedParkMode;
  2223. ELSE (* Enable Asynchronous Schedule Park Mode and set its count field in USBCMD *)
  2224. dword := dword + LSH(SYSTEM.VAL(SET, HcAsyncParkModeCount), 8) * CmdAsyncSchedParkCount;
  2225. dword := dword + CmdAsyncSchedParkMode;
  2226. END;
  2227. SYSTEM.PUT32(iobase + HcUsbCmd, dword);
  2228. END;
  2229. (* Set interrupt threshold *)
  2230. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  2231. dword := dword - {16 .. 23};
  2232. IF ((HcInterruptThreshold#01H) & (HcInterruptThreshold#02H) & (HcInterruptThreshold#04H) & (HcInterruptThreshold#08H)
  2233. & (HcInterruptThreshold#10H) & (HcInterruptThreshold#20H) & (HcInterruptThreshold#40H)) THEN
  2234. (* Wrong parameter value... use default *)
  2235. IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbEhci: Interrupt Threshold value invalid... using default setting."); KernelLog.Ln; END;
  2236. dword := dword + SYSTEM.VAL(SET, LSH(08H, 16)) * {16..23};
  2237. ELSE
  2238. dword := dword + SYSTEM.VAL(SET, LSH(HcInterruptThreshold, 16)) * {16..23};
  2239. END;
  2240. SYSTEM.PUT32(iobase + HcUsbCmd, dword); FlushPCI;
  2241. (* Try to start the host controller *)
  2242. IF Start() = FALSE THEN (* ERROR: Couldn't start the host controller. Controller was probably not correctly initialized. *)
  2243. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: Couldn't start host controller."); KernelLog.Ln; END;
  2244. ignore := HardwareReset();
  2245. RETURN FALSE;
  2246. END;
  2247. RETURN TRUE;
  2248. END Init;
  2249. (* PCI writes may be posted. A read forces posted writes to be flushed before the read transaction is proceeded. *)
  2250. PROCEDURE FlushPCI *;
  2251. VAR ignore : LONGINT;
  2252. BEGIN
  2253. ignore := SYSTEM.GET32(iobase + HcUsbSts);
  2254. END FlushPCI;
  2255. (* Release the HC ownership semaphore; eecp is the EHCI Extended Capability Pointer *)
  2256. PROCEDURE ReleaseHcOwnerShip(bus, device, function, eecp : LONGINT);
  2257. END ReleaseHcOwnerShip;
  2258. (*
  2259. * Start the host controller.
  2260. * This will:
  2261. * - enable interrupts for the host controller and install a interrupt handler
  2262. * - set the addresses for the periodic and asynchronous lists
  2263. * - turn the host controller on
  2264. * - route all ports to the EHCI controller
  2265. * - power on all ports of the root hub
  2266. *)
  2267. PROCEDURE Start * ():BOOLEAN;
  2268. VAR dword : SET;
  2269. TYPE
  2270. IRQPoller = OBJECT (* support polling i/o IRQ -- for testing *)
  2271. VAR handler: PROCEDURE {DELEGATE}; timer: Kernel.Timer;
  2272. PROCEDURE & Init* (h: PROCEDURE {DELEGATE});
  2273. BEGIN
  2274. handler := h;
  2275. NEW(timer);
  2276. END Init;
  2277. BEGIN{ACTIVE}
  2278. LOOP
  2279. handler();
  2280. timer.Sleep(100);
  2281. END;
  2282. END IRQPoller;
  2283. VAR is: IRQPoller;
  2284. BEGIN
  2285. IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbEhci: Starting host controller... "); KernelLog.Ln; END;
  2286. (* Enable Interrupts *)
  2287. IF Polling THEN
  2288. NEW(is, InterruptHandler)
  2289. ELSE
  2290. END;
  2291. Objects.InstallHandler(InterruptHandler, Machine.IRQ0+irq);
  2292. (* Clear interrupts *)
  2293. SYSTEM.PUT32(iobase + HcUsbSts, 0);
  2294. (* Enable all interrupts except the frame list rollover interrupt *)
  2295. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbIntr));
  2296. interruptsEnabled := dword + {0..5} - StsFrameListRollover;
  2297. SYSTEM.PUT32(iobase + HcUsbIntr, interruptsEnabled);
  2298. (* Set Addresses for queue heads *)
  2299. SYSTEM.PUT32(iobase + HcPeriodicListBase, ADDRESSOF(framelist.data[framelist.base]));
  2300. SYSTEM.PUT32(iobase + HcAsyncListAddr, 0); (* Invalid address -> list empty *)
  2301. (* Start controller *)
  2302. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  2303. dword := dword + CmdRunStop;
  2304. SYSTEM.PUT32(iobase + HcUsbCmd, dword); FlushPCI;
  2305. SetState(UsbHcdi.Operational);
  2306. (* Route all ports to this EHCI host controller *)
  2307. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcConfigFlag));
  2308. dword := dword + {0};
  2309. SYSTEM.PUT32(iobase + HcConfigFlag, dword); FlushPCI;
  2310. SetState(UsbHcdi.Initialized);
  2311. RETURN TRUE;
  2312. END Start;
  2313. PROCEDURE &Default*(bus, device, function : LONGINT);
  2314. BEGIN
  2315. Default^(bus, device, function); (* The high-speed default pipe uses 64byte maxPacketSize0 *)
  2316. pipes[0, 0, 0].maxPacketSize := 64;
  2317. NEW(allocator, 4096, 32);
  2318. NEW(framelistLock);
  2319. NEW(handler, SELF)
  2320. END Default;
  2321. PROCEDURE Cleanup;
  2322. BEGIN
  2323. IF state >= UsbHcdi.Initialized THEN Objects.RemoveHandler(InterruptHandler, Machine.IRQ0 + irq); END;
  2324. Cleanup^;
  2325. IF ~HardwareReset() THEN
  2326. IF Debug.Level >= Debug.Errors THEN Show("Host controller reset failed."); KernelLog.Ln; END;
  2327. END;
  2328. (* Release ownership of host controller *)
  2329. ReleaseHcOwnerShip(bus, device, function, eecp);
  2330. (* Unmap the HC's operational registers *)
  2331. Machine.UnmapPhysical(iobase, 4096);
  2332. handler.Stop
  2333. END Cleanup;
  2334. (** Displays the host controller's data struture on KernelLog *)
  2335. PROCEDURE ShowSchedule*;
  2336. CONST MaxIterations =21;
  2337. VAR dword : SET; first, cur : LONGINT; i, ms : LONGINT;
  2338. BEGIN
  2339. IF Debug.Trace THEN
  2340. KernelLog.String("Host Controller Data Structures for ");
  2341. KernelLog.String(name);
  2342. KernelLog.String(" ("); KernelLog.String(desc); KernelLog.String("):"); KernelLog.Ln;
  2343. KernelLog.String("Periodic Schedule: "); KernelLog.Ln;
  2344. IF SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsPeriodicSchedule = {} THEN
  2345. KernelLog.String("Periodic schedule is disabled."); KernelLog.Ln;
  2346. END;
  2347. KernelLog.String("*** Isochronous schedule: "); KernelLog.Ln;
  2348. IF SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsPeriodicSchedule # {} THEN
  2349. ShowFramelist(ADDRESSOF(framelist.data[framelist.base]), framelistSize, LSH(SYSTEM.GET32(iobase + HcFrIndex), -3) MOD framelistSize, TRUE)
  2350. ELSE
  2351. KernelLog.String("Periodic schedule is not enabled"); KernelLog.Ln
  2352. END;
  2353. KernelLog.String("*** Asynchronous list: "); KernelLog.Ln;
  2354. IF SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsAsyncSchedule # {} THEN
  2355. first := SYSTEM.GET32(iobase + HcAsyncListAddr);
  2356. IF (SYSTEM.VAL(SET, first) * {0..4} = {}) & (first # 0) THEN
  2357. first := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, first) * {5..31});
  2358. ShowQueueHead(first, 0, cap64bit);
  2359. i := 0; cur := first;
  2360. LOOP
  2361. cur := SYSTEM.GET32(cur + QhHorizontalLinkPointer);
  2362. IF (SYSTEM.VAL(SET, cur) * {2..4} # {}) OR (SYSTEM.VAL(SET, cur) * {1} = {}) THEN
  2363. KernelLog.String("Error: Queue head horizontal link pointer is invalid."); KernelLog.Ln;
  2364. EXIT;
  2365. END;
  2366. cur := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, cur) * {5..31});
  2367. IF (cur = first) OR (i >= MaxIterations) THEN EXIT END;
  2368. ShowQueueHead(cur, 0, cap64bit);
  2369. INC(i);
  2370. END;
  2371. IF i >= MaxIterations THEN
  2372. KernelLog.String("MaxIterations reached. Aborting..."); KernelLog.Ln;
  2373. END;
  2374. ELSE KernelLog.String("Error: Asynchronous Schedule List address is invalid.");
  2375. END;
  2376. ELSE KernelLog.String("Asynchronous Schedule is disabled.");
  2377. END;
  2378. KernelLog.Ln;
  2379. END;
  2380. END ShowSchedule;
  2381. PROCEDURE ShowPipe*(pipe : UsbHcdi.Pipe);
  2382. VAR
  2383. qh: Qh;
  2384. BEGIN
  2385. IF pipe.descriptors # NIL THEN
  2386. qh := SYSTEM.VAL(Qh, pipe.descriptors[0]);
  2387. IF (qh.next # 0) & (SYSTEM.VAL(SET, qh.next) * QhTerminate = {}) THEN
  2388. KernelLog.String("QH and qTD link:"); KernelLog.Ln;
  2389. ShowQueueHead(qh, qh.next, cap64bit)
  2390. ELSIF (qh.current # 0) & (SYSTEM.VAL(SET, qh.current) * QhTerminate = {}) THEN
  2391. KernelLog.String("QH and current qTD:"); KernelLog.Ln;
  2392. ShowQueueHead(qh, qh.current, cap64bit)
  2393. ELSE
  2394. KernelLog.String("QH with no scheduled qTD:"); KernelLog.Ln;
  2395. ShowQueueHead(qh, 0, cap64bit)
  2396. END
  2397. END
  2398. END ShowPipe;
  2399. (* Show some information on this host controller on KernelLog *)
  2400. PROCEDURE Diag;
  2401. VAR dword : SET;
  2402. BEGIN
  2403. IF Debug.Trace THEN
  2404. Diag^;
  2405. (* Host Controller structural capabilities *)
  2406. KernelLog.String(" HC Structural Parameters: "); KernelLog.Ln;
  2407. KernelLog.String(" Nbr of ports: "); KernelLog.Int(capNbrOfPorts, 0);
  2408. KernelLog.String(", Debug port: ");
  2409. IF capDebugPortNumber # 0 THEN KernelLog.Int(capDebugPortNumber, 0); ELSE KernelLog.String("n/a"); END;
  2410. KernelLog.Ln;
  2411. KernelLog.String(" Per port power control: ");
  2412. IF capPortPowerControl THEN KernelLog.String("Yes"); ELSE KernelLog.String("No"); END;
  2413. KernelLog.String(", Port indicator control: ");
  2414. IF capPortIndicators THEN KernelLog.String("Available"); ELSE KernelLog.String("n/a"); END;
  2415. KernelLog.Ln;
  2416. KernelLog.String(" Nbr of companion HCs: "); KernelLog.Int(capNbrOfCompanionHc, 0);
  2417. KernelLog.String(", Ports per companion: "); KernelLog.Int(capPortsPerCompanion, 0);
  2418. KernelLog.String(", Port routing rules: ");
  2419. IF capPortRoutingRules THEN KernelLog.String("Available"); ELSE KernelLog.String("n/a"); END;
  2420. KernelLog.Ln;
  2421. (* Host Controller capability parameters *)
  2422. KernelLog.String(" HC Capablilty Parameters:"); KernelLog.Ln;
  2423. KernelLog.String(" 64bit Data Structures: "); IF cap64bit THEN KernelLog.String("Yes"); ELSE KernelLog.String("No"); END;
  2424. KernelLog.String(", Async Schedule Park Mode support: "); IF capAsynchSchedPark THEN KernelLog.String("Yes"); ELSE KernelLog.String("No"); END;
  2425. KernelLog.Ln;
  2426. KernelLog.String(" Programmable Frame List Size: "); IF capAsynchSchedPark THEN KernelLog.String("Yes"); ELSE KernelLog.String("No"); END;
  2427. KernelLog.String(", Isochronous Scheduling Threshold: "); KernelLog.Int(capIsoSchedThreshold, 0);
  2428. KernelLog.Ln;
  2429. (* Host Controller Command Register *)
  2430. KernelLog.String(" HC Command Register: "); KernelLog.Ln;
  2431. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  2432. KernelLog.String(" Interrupt Threshold: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * {16..23}, -16)), 0);
  2433. KernelLog.String(", Async Schedule Park Mode: ");
  2434. IF dword * {11} # {} THEN
  2435. KernelLog.String("Enabled ("); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * {8..9}, -8)), 0); KernelLog.Char(")");
  2436. ELSE
  2437. KernelLog.String("Disabled");
  2438. END;
  2439. KernelLog.String(", Frame List Size: ");
  2440. CASE SYSTEM.VAL(LONGINT, LSH(dword * {2..3}, -2)) OF
  2441. 0: KernelLog.String("1024");
  2442. |1: KernelLog.String("512");
  2443. |2: KernelLog.String("256");
  2444. |3: KernelLog.String("Reserved");
  2445. END;
  2446. KernelLog.Ln;
  2447. (* Host Controller Status information *)
  2448. KernelLog.String(" HC Status Register:"); KernelLog.Ln;
  2449. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts));
  2450. KernelLog.String(" Asynchronous Schedule: ");
  2451. IF dword * StsAsyncSchedule # {} THEN KernelLog.String("Enabled"); ELSE KernelLog.String("Disabled"); END;
  2452. KernelLog.String(", Periodic Schedule: ");
  2453. IF dword * StsPeriodicSchedule # {} THEN KernelLog.String("Enabled"); ELSE KernelLog.String("Disabled"); END;
  2454. KernelLog.String(" ");
  2455. IF dword * StsReclamation # {} THEN KernelLog.String("[Reclamation]"); END;
  2456. IF dword * StsHcHalted # {} THEN KernelLog.String("[HcHalted]"); END;
  2457. KernelLog.Ln;
  2458. KernelLog.String(" Interrupt Status: "); ShowInterrupts(dword); KernelLog.Ln;
  2459. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbIntr));
  2460. KernelLog.String(" Interrupts Enabled: "); ShowInterrupts(dword); KernelLog.Ln;
  2461. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  2462. KernelLog.String(" HC operation: ");
  2463. IF dword * {0} # {} THEN KernelLog.String("Running"); ELSE KernelLog.String("Stopped"); END;
  2464. KernelLog.Ln;
  2465. END;
  2466. END Diag;
  2467. PROCEDURE Show * (CONST txt : ARRAY OF CHAR);
  2468. BEGIN
  2469. KernelLog.String("UsbEhci: "); KernelLog.String(name); KernelLog.String(" ("); KernelLog.String(desc); KernelLog.String("): ");
  2470. KernelLog.String(txt);
  2471. END Show;
  2472. PROCEDURE ShowHcFl;
  2473. BEGIN
  2474. ShowFramelist(ADDRESSOF(framelist.data[framelist.base]), framelistSize, LSH(SYSTEM.GET32(iobase + HcFrIndex), -3) MOD framelistSize, FALSE)
  2475. END ShowHcFl;
  2476. END EnhancedHostController;
  2477. Handler = OBJECT
  2478. VAR
  2479. hc: EnhancedHostController;
  2480. handle, active, trapped, next: BOOLEAN;
  2481. interrupts: SET;
  2482. PROCEDURE & Init (hc: EnhancedHostController);
  2483. BEGIN
  2484. SELF.hc := hc;
  2485. active := TRUE
  2486. END Init;
  2487. PROCEDURE Handle (interrupts: SET);
  2488. BEGIN {EXCLUSIVE}
  2489. IF next THEN
  2490. (*KernelLog.Enter; KernelLog.String("HANDLER: merging"); KernelLog.Exit;*)
  2491. SELF.interrupts := SELF.interrupts + interrupts
  2492. ELSE
  2493. IF handle THEN
  2494. (*KernelLog.Enter; KernelLog.String("HANDLER: buffering"); KernelLog.Exit;*)
  2495. next := TRUE
  2496. ELSE
  2497. (*KernelLog.Enter; KernelLog.String("HANDLER: handling"); KernelLog.Exit;*)
  2498. handle := TRUE
  2499. END;
  2500. SELF.interrupts := interrupts
  2501. END;
  2502. END Handle;
  2503. PROCEDURE Stop;
  2504. BEGIN {EXCLUSIVE}
  2505. active := FALSE
  2506. END Stop;
  2507. BEGIN {ACTIVE, SAFE}
  2508. IF trapped THEN
  2509. hc.Show("WARNING: interrupt handler trapped");
  2510. KernelLog.Ln
  2511. END;
  2512. trapped := TRUE;
  2513. handle := FALSE;
  2514. LOOP
  2515. BEGIN {EXCLUSIVE}
  2516. AWAIT(~active OR handle);
  2517. IF ~active THEN EXIT END;
  2518. END;
  2519. hc.DoHandleInterrupt(interrupts);
  2520. BEGIN {EXCLUSIVE}
  2521. (*KernelLog.Enter; KernelLog.String("HANDLER: done -- "); KernelLog.Boolean(handle); KernelLog.String(", "); KernelLog.Boolean(next); KernelLog.Exit;*)
  2522. handle := next;
  2523. next := FALSE
  2524. END;
  2525. END
  2526. END Handler;
  2527. PROCEDURE AssertAlignment(desc: ADDRESS; alignment: SIZE);
  2528. BEGIN
  2529. ASSERT(desc MOD alignment = 0);
  2530. END AssertAlignment;
  2531. (**
  2532. * Computes the address of the next 4kB page in the page sequence sgList.
  2533. *
  2534. * Return the physical page of size 4kB which is right after the physical address (adr + size) in the page ranges sgList.
  2535. * idx is the page index of adr in sgList. When returning, idx indexes the sgList entry containing the return value.
  2536. *)
  2537. PROCEDURE GetNextPhysicalPage (CONST sgList: ARRAY OF Machine.Range; VAR idx: LONGINT; adr: ADDRESS): ADDRESS;
  2538. BEGIN
  2539. IF adr + PageSize - adr MOD PageSize >= sgList[idx].adr + sgList[idx].size THEN
  2540. (* Next Page beginning not in the current range: use the next range *)
  2541. INC(idx);
  2542. ASSERT(idx < LEN(sgList));
  2543. RETURN sgList[idx].adr
  2544. ELSE
  2545. (* Align to next page size *)
  2546. RETURN adr + (-adr) MOD PageSize
  2547. END;
  2548. END GetNextPhysicalPage;
  2549. PROCEDURE AdvancePhysBuf (CONST sgList: ARRAY OF Machine.Range; VAR idx: LONGINT; adr: ADDRESS; size: LONGINT): ADDRESS;
  2550. VAR
  2551. next: LONGINT;
  2552. BEGIN
  2553. next := adr + size;
  2554. WHILE next - sgList[idx].adr > sgList[idx].size DO
  2555. (* Range continues into next buffer *)
  2556. DEC(size, sgList[idx].size);
  2557. INC(idx);
  2558. next := sgList[idx].adr + size
  2559. END;
  2560. RETURN next
  2561. END AdvancePhysBuf;
  2562. PROCEDURE Indent(spaces : LONGINT);
  2563. VAR i : LONGINT;
  2564. BEGIN
  2565. FOR i := 1 TO spaces DO KernelLog.Char(" "); END;
  2566. END Indent;
  2567. (*
  2568. * Display a textual representation of a queue head data structure and its associated qTD.
  2569. * @param qh Virtual memory address of queue head
  2570. * @param firstQtd First qTD of this queue. If 0, the qTD chain will not be shown
  2571. *)
  2572. PROCEDURE ShowQueueHead(qh, firstQtd : ADDRESS; cap64bit : BOOLEAN);
  2573. CONST MaxChainLen = 32;
  2574. VAR
  2575. dword : SET;
  2576. val, chainlen : LONGINT;
  2577. PROCEDURE ShowQhTyp(qh : LONGINT);
  2578. BEGIN
  2579. IF Debug.Trace THEN
  2580. val := SYSTEM.VAL(LONGINT, LSH(SYSTEM.VAL(SET, qh) * QhTyp, -1));
  2581. IF val = 0 THEN KernelLog.String("Isochronous Tranfers Desriptor");
  2582. ELSIF val = 1 THEN KernelLog.String("Queue Head");
  2583. ELSIF val = 2 THEN KernelLog.String("Split Transaction Isochronous Transfer Descriptor");
  2584. ELSIF val = 3 THEN KernelLog.String("Frame Span Traversal Node");
  2585. END;
  2586. END;
  2587. END ShowQhTyp;
  2588. BEGIN
  2589. IF Debug.Trace THEN
  2590. KernelLog.String("EHCI data structure at "); KernelLog.Address(qh); KernelLog.String(": ");
  2591. ShowQhTyp(qh); KernelLog.String(" ");
  2592. IF qh = 0 THEN KernelLog.String("Error: QH pointer = 0"); KernelLog.Ln;RETURN;
  2593. ELSIF SYSTEM.VAL(SET, qh) * {0..4} # {} THEN KernelLog.String("Error: Not aligned"); KernelLog.Ln; RETURN;
  2594. END;
  2595. KernelLog.Ln;
  2596. KernelLog.String(" Endpoint Capabilities 1: ");
  2597. dword := SYSTEM.VAL(SET, SYSTEM.GET32(qh + QhEpCapabilities1));
  2598. KernelLog.String(" DeviceAddr: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * QhDeviceAddress), 0);
  2599. KernelLog.String(", Endpoint: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QhEndpointNbr, -8)), 0);
  2600. KernelLog.String(", Speed: "); val := SYSTEM.VAL(LONGINT, LSH(dword * QhEndpointSpeed, -12));
  2601. IF val = 0 THEN KernelLog.String("FullSpeed");
  2602. ELSIF val = 1 THEN KernelLog.String("LowSpeed");
  2603. ELSIF val = 2 THEN KernelLog.String("HighSpeed");
  2604. ELSE KernelLog.String("ERROR: Not set correctly");
  2605. END;
  2606. KernelLog.String(", MaxPacketSize: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QhMaxPacketLen, -16)), 0);
  2607. KernelLog.String(", NakRL: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QhNakCountReload, -28)), 0);
  2608. KernelLog.String(", Flags: ");
  2609. IF dword * QhControlEndpointFlag # {} THEN KernelLog.String("[ControlEp]"); END;
  2610. IF dword * QhDataToggleControl # {} THEN KernelLog.String("[DataToggleControl]"); END;
  2611. IF dword * QhHeadOfReclamation # {} THEN KernelLog.String("[Head]"); END;
  2612. IF dword * QhInactivate # {} THEN KernelLog.String("[Inactivate]"); END;
  2613. KernelLog.Ln;
  2614. KernelLog.String(" Endpoint Capabilities 2: ");
  2615. dword := SYSTEM.VAL(SET, SYSTEM.GET32(qh + QhEpCapabilities2));
  2616. KernelLog.String("Mult: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QhMultiplier, -30)), 0);
  2617. KernelLog.String(", HubAddr: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QhHubAddr, -16)), 0);
  2618. KernelLog.String(", HubPort: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QhPortNbr, -23)), 0);
  2619. KernelLog.String(", SplitCMask: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QhSplitCMask, -8)), 0);
  2620. KernelLog.String(", QhSMask: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * QhSMask), 0);
  2621. KernelLog.Ln;
  2622. KernelLog.String(" Queue Head Horizontal Link Pointer: ");
  2623. dword := SYSTEM.VAL(SET, SYSTEM.GET32(qh + QhHorizontalLinkPointer));
  2624. IF dword * QhTerminate # {} THEN
  2625. KernelLog.String("Invalid ("); KernelLog.Address(SYSTEM.VAL(LONGINT, dword)); KernelLog.String("H)");
  2626. ELSE
  2627. KernelLog.Address(SYSTEM.VAL(LONGINT, dword * {5..31}));
  2628. KernelLog.String(" ("); ShowQhTyp(SYSTEM.VAL(LONGINT, dword)); KernelLog.String(")");
  2629. END;
  2630. KernelLog.Ln;
  2631. dword := SYSTEM.VAL(SET, SYSTEM.GET32(qh + QhCurrentQtdPointer));
  2632. KernelLog.String(" Current qTD Pointer: "); KernelLog.Address(SYSTEM.VAL(LONGINT, dword));
  2633. KernelLog.String(", Next qTD Pointer: "); KernelLog.Address(SYSTEM.GET32(qh + QhNextQtdPointer));
  2634. KernelLog.Ln;
  2635. KernelLog.String(" Transfer overlay: "); KernelLog.Ln;
  2636. ShowQtd(qh+ QhNextQtdPointer, 8, cap64bit, TRUE); KernelLog.Ln;
  2637. IF firstQtd # 0 THEN (* show qTD chain *)
  2638. KernelLog.String(" qTD chain:"); KernelLog.Ln;
  2639. IF SYSTEM.VAL(SET, firstQtd) * {0..4} # {} THEN
  2640. KernelLog.String(" qTD Pointer not 32byte aligned!"); KernelLog.Ln;
  2641. ELSE
  2642. chainlen := 0;
  2643. WHILE(SYSTEM.VAL(SET, firstQtd) * QhTerminate = {}) & (chainlen < MaxChainLen) DO
  2644. INC(chainlen);
  2645. ShowQtd(firstQtd, 8, cap64bit, FALSE); KernelLog.Ln;
  2646. (* Get next qTD *)
  2647. dword := SYSTEM.VAL(SET, SYSTEM.GET32(firstQtd + QtdNextQtdPointer));
  2648. IF dword * {1..4} # {} THEN
  2649. KernelLog.String(" Alignment error!"); KernelLog.Ln;
  2650. chainlen := MaxChainLen; (* abort *)
  2651. ELSIF dword * QhTerminate # {} THEN
  2652. KernelLog.String(" End of Chain"); KernelLog.Ln;
  2653. chainlen := MaxChainLen; (* abort *)
  2654. ELSE
  2655. firstQtd := SYSTEM.VAL(LONGINT, dword * {5..31});
  2656. END;
  2657. END;
  2658. END;
  2659. END;
  2660. END;
  2661. END ShowQueueHead;
  2662. PROCEDURE ShowQtd(adr: ADDRESS; spaces: LONGINT; cap64bit, overlay: BOOLEAN);
  2663. VAR
  2664. i, val: LONGINT;
  2665. dword: SET;
  2666. qtd: Qtd;
  2667. BEGIN
  2668. IF Debug.Trace THEN
  2669. qtd := adr;
  2670. Indent(spaces);
  2671. KernelLog.String("qTD at "); KernelLog.Address(qtd); KernelLog.String(": ");
  2672. IF SYSTEM.VAL(SET, qtd) * {0..3} # {} THEN
  2673. (* Regular qTDs are 32byte aligned. We allow 16byte alignment for the transfer overlay area *)
  2674. KernelLog.String("Not 16byte aligned... aborting."); KernelLog.Ln; RETURN;
  2675. ELSIF adr = 0 THEN
  2676. KernelLog.String("Address = 0?"); KernelLog.Ln; RETURN;
  2677. END;
  2678. KernelLog.Ln;
  2679. Indent(spaces+ 4);
  2680. KernelLog.String("qTD Token: ");
  2681. dword := SYSTEM.VAL(SET, qtd.token);
  2682. KernelLog.String("Pid: ");
  2683. val := SYSTEM.VAL(LONGINT, LSH(dword * QtdPidCode, -8));
  2684. IF val = PidSetup THEN KernelLog.String("SETUP");
  2685. ELSIF val = PidIn THEN KernelLog.String("IN");
  2686. ELSIF val = PidOut THEN KernelLog.String("OUT");
  2687. ELSE KernelLog.String("PID ERROR!");
  2688. END;
  2689. KernelLog.String(", DataToggle: ");
  2690. IF dword * QtdDataToggle # {} THEN KernelLog.String("DATA1"); ELSE KernelLog.String("DATA0"); END;
  2691. KernelLog.String(", Bytes to transfer: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QtdBytesToTransfer, -16)), 0);
  2692. KernelLog.String(", IOC: "); IF dword * QtdIoc # {} THEN KernelLog.String("Yes"); ELSE KernelLog.String("No"); END;
  2693. KernelLog.String(", CERR: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QtdErrorCounter, -10)) ,0);
  2694. KernelLog.Ln;
  2695. Indent(spaces + 4);
  2696. KernelLog.String("qTD Token Status: ");
  2697. dword := dword * QtdStatus;
  2698. IF dword * TdActive # {} THEN KernelLog.String("[Active]"); END;
  2699. IF dword * TdHalted # {} THEN KernelLog.String("[Halted]"); END;
  2700. IF dword * TdDataBufferError # {} THEN KernelLog.String("[DataBufferError]"); END;
  2701. IF dword * TdBabbleDetected # {} THEN KernelLog.String("[BabbleDetected]"); END;
  2702. IF dword * TdTransactionError # {} THEN KernelLog.String("[TransactionError]"); END;
  2703. IF dword * TdMissedMicroFrame # {} THEN KernelLog.String("[MissedMicroFrame]"); END;
  2704. IF dword * TdSplitTransactionState # {} THEN KernelLog.String("[SplitTransactionState]"); END;
  2705. IF dword * TdPingState # {} THEN KernelLog.String("[PingState]"); END;
  2706. KernelLog.Ln;
  2707. Indent(spaces + 4); KernelLog.String("Buffer information:");KernelLog.Ln;
  2708. Indent(spaces + 8); KernelLog.String("Current Buffer: ");
  2709. KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QtdCurrentPage, -12)), 0);
  2710. IF SYSTEM.VAL(SET, qtd) * {4} # {} THEN (* Should be transfer overlay since not 32byte aligned *)
  2711. KernelLog.String(", Nak counter: ");
  2712. KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(SYSTEM.VAL(SET, qtd.next) * {1..3}, -1)), 0);
  2713. END;
  2714. KernelLog.Ln;
  2715. FOR i := 0 TO 4 DO
  2716. val := qtd.buffers[i];
  2717. Indent(spaces + 8);
  2718. KernelLog.String("Buffer Pointer "); KernelLog.Int(i, 0); KernelLog.String(": "); KernelLog.Address(SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, val) * {12..31}));
  2719. val := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, val) * {0..11});
  2720. IF i = 0 THEN
  2721. KernelLog.String(" Current Offset: "); KernelLog.Hex(val, 8);
  2722. ELSIF overlay & (i = 1) THEN
  2723. KernelLog.String(" C-prog-mask: "); KernelLog.Hex(val, 8);
  2724. ELSIF overlay & (i = 2) THEN
  2725. KernelLog.String(" S-Bytes / Frametag: "); KernelLog.Hex(val, 8);
  2726. END;
  2727. KernelLog.Ln;
  2728. END;
  2729. IF cap64bit THEN
  2730. FOR i := 0 TO 4 DO
  2731. val := qtd.extBuffers[i];
  2732. Indent(spaces + 8);
  2733. KernelLog.String(" ExtBufferPointer"); KernelLog.Int(i, 0); KernelLog.String(": "); KernelLog.Address(val); KernelLog.Ln;
  2734. END;
  2735. END;
  2736. Indent(spaces + 4); KernelLog.String("Alternate Next qTD Pointer: ");
  2737. dword := SYSTEM.VAL(SET, (*SYSTEM.GET32(adr + QtdNextQtdPointer)*) qtd.alternateNext);
  2738. IF dword * QhTerminate # {} THEN
  2739. KernelLog.String("Invalid ("); KernelLog.Address(SYSTEM.VAL(LONGINT, dword)); KernelLog.String(")");
  2740. ELSIF dword * {1..3} # {} THEN
  2741. KernelLog.String("Alignment Error ("); KernelLog.Address(SYSTEM.VAL(LONGINT, dword)); KernelLog.String(")");
  2742. ELSE
  2743. KernelLog.Address(SYSTEM.VAL(LONGINT, dword));
  2744. END;
  2745. KernelLog.Ln;
  2746. (*val := SYSTEM.VAL(ADDRESS, qtd);*)
  2747. Indent(spaces + 4);
  2748. KernelLog.String("Next qTD Pointer: ");
  2749. dword := SYSTEM.VAL(SET, qtd.next);
  2750. (*SYSTEM.GET(SYSTEM.VAL(ADDRESS, qtd) + QtdNextQtdPointer, dword);*)
  2751. IF dword * QhTerminate # {} THEN
  2752. KernelLog.String("Invalid ("); KernelLog.Address(SYSTEM.VAL(LONGINT, dword)); KernelLog.String(")");
  2753. qtd := 0;
  2754. ELSIF dword * {1..3} # {} THEN
  2755. KernelLog.String("Alignment Error ("); KernelLog.Address(SYSTEM.VAL(LONGINT, dword)); KernelLog.String(")");
  2756. qtd := 0;
  2757. ELSE
  2758. KernelLog.Address(SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, dword) * {5..31}));
  2759. qtd := SYSTEM.VAL(LONGINT, dword * {5..31});
  2760. END;
  2761. KernelLog.Ln;
  2762. END;
  2763. END ShowQtd;
  2764. (*
  2765. PROCEDURE ShowItd(adr : LONGINT);
  2766. VAR dword : SET; i : LONGINT;
  2767. BEGIN
  2768. IF Debug.Trace THEN
  2769. KernelLog.String("UsbEhci: ITD at address "); KernelLog.Hex(adr, 8); KernelLog.Ln;
  2770. Indent(4);
  2771. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr));
  2772. KernelLog.String("Next Link Pointer: "); KernelLog.Hex(SYSTEM.VAL(LONGINT, dword * {5..31}), 8);
  2773. IF dword * {0} = {} THEN KernelLog.String(" [VALID]"); ELSE KernelLog.String("[INVALID]"); END;
  2774. KernelLog.String(", Typ: ");
  2775. CASE SYSTEM.VAL(LONGINT, LSH(dword * {1..2}, -1)) OF
  2776. 0: KernelLog.String("iTD");
  2777. |1 : KernelLog.String("QH");
  2778. |2 : KernelLog.String("siTD");
  2779. |3 : KernelLog.String("FSTN");
  2780. END;
  2781. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + 24H));
  2782. KernelLog.String(", Device Address: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {0..6}), 0);
  2783. KernelLog.String(", Endpoint: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * {8..11}, -7)), 0);
  2784. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + 28H));
  2785. KernelLog.String(" MaxPacket: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {0..10}), 0);
  2786. IF dword * {11} # {} THEN KernelLog.String(" [IN]"); ELSE KernelLog.String(" [OUT]"); END;
  2787. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + 2CH));
  2788. KernelLog.String(", MULT: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {0..1}), 0);
  2789. KernelLog.Ln;
  2790. FOR i := 0 TO 7 DO
  2791. Indent(4);
  2792. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + (i+1) * 4));
  2793. KernelLog.String("Transaction "); KernelLog.Int(i, 0); KernelLog.String(": ");
  2794. KernelLog.String("Status: ");
  2795. IF dword * ItdStatus # {} THEN
  2796. IF dword * ItdActive # {} THEN KernelLog.String("[ACTIVE]"); END;
  2797. IF dword * ItdDataBufferError # {} THEN KernelLog.String("[DataBufferError]"); END;
  2798. IF dword * ItdBabbleDetected # {} THEN KernelLog.String("[Babble]"); END;
  2799. IF dword * ItdTransactionError # {} THEN KernelLog.String("[TransactionError]"); END;
  2800. ELSE
  2801. KernelLog.String("[Done]");
  2802. END;
  2803. KernelLog.String(" Length: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * {16..27}, -16)), 0);
  2804. KernelLog.String(", PG: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * {12..14}, -12)), 0);
  2805. KernelLog.String(", Offset: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {0..11}), 0);
  2806. IF dword * {15} # {} THEN KernelLog.String(" [IOC]"); END;
  2807. KernelLog.Ln;
  2808. END;
  2809. FOR i := 0 TO 6 DO
  2810. Indent(4);
  2811. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + 24H + i*4));
  2812. KernelLog.String("Buffer Pointer Page "); KernelLog.Int(i, 0); KernelLog.String(": ");
  2813. KernelLog.Hex(SYSTEM.VAL(LONGINT, dword * {12..31}), 8);
  2814. KernelLog.Ln;
  2815. END;
  2816. END;
  2817. END ShowItd;
  2818. *)
  2819. PROCEDURE ShowInterrupts * (s : SET);
  2820. BEGIN
  2821. IF Debug.Trace THEN
  2822. IF s * StsAsyncAdvance # {} THEN KernelLog.String("[AsyncAdvance]"); END;
  2823. IF s * StsHostSystemError # {} THEN KernelLog.String("[HostSystemError]"); END;
  2824. IF s * StsFrameListRollover # {} THEN KernelLog.String("[FrameListRollover]"); END;
  2825. IF s * StsPortChange # {} THEN KernelLog.String("[PortChange]"); END;
  2826. IF s * StsUsbError # {} THEN KernelLog.String("[UsbError]"); END;
  2827. IF s * StsUsbInterrupt # {} THEN KernelLog.String("[UsbInterrupt]"); END;
  2828. END;
  2829. END ShowInterrupts;
  2830. PROCEDURE ShowItd (adr: ADDRESS; space: LONGINT);
  2831. VAR
  2832. dword: SET;
  2833. i: LONGINT;
  2834. BEGIN
  2835. IF Debug.Trace THEN
  2836. Indent(space);
  2837. IF adr = 0 THEN
  2838. KernelLog.String("UsbEhci: iTD at address 0, invalid address, aborting");
  2839. KernelLog.Ln;
  2840. RETURN
  2841. END;
  2842. KernelLog.String("UsbEhci: iTD at address "); KernelLog.Address(adr); KernelLog.Ln;
  2843. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + ItdNextLinkPointer));
  2844. Indent(space + 4);
  2845. KernelLog.String("Next Link Pointer: "); KernelLog.Address(SYSTEM.VAL(ADDRESS, dword));
  2846. IF 0 IN dword THEN KernelLog.String(" [INVALID]") END;
  2847. KernelLog.String(", Type: ");
  2848. CASE SYSTEM.VAL(LONGINT, dword * {1 .. 2}) DIV 2 OF
  2849. 0: KernelLog.String("iTD")
  2850. |1: KernelLog.String("QH")
  2851. |2: KernelLog.String("siTD")
  2852. |3: KernelLog.String("FSTN")
  2853. END;
  2854. KernelLog.Ln;
  2855. FOR i := 0 TO 7 DO
  2856. Indent(space + 4);
  2857. KernelLog.String("Transaction "); KernelLog.Int(i, 0); KernelLog.String(": ");
  2858. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + ItdTransaction0 + 4 * i));
  2859. KernelLog.String("TX Length "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {16 .. 27}) DIV 10000H, 0);
  2860. KernelLog.String(", Page select "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {12 .. 14}) DIV 1000H, 0);
  2861. KernelLog.String(", Tx offset "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {0 .. 11}), 0);
  2862. IF 15 IN dword THEN KernelLog.String(" [IOC]") END;
  2863. IF 30 IN dword THEN KernelLog.String(" [DATA BUFFER ERROR]") END;
  2864. IF 29 IN dword THEN KernelLog.String(" [BABBLE]") END;
  2865. IF 28 IN dword THEN KernelLog.String(" [ERROR]") END;
  2866. IF 31 IN dword THEN KernelLog.String(" [ACTIVE]") ELSE KernelLog.String(" [INACTIVE]") END;
  2867. KernelLog.Ln
  2868. END;
  2869. Indent(space + 4);
  2870. KernelLog.String("Buffer pointer 0: ");
  2871. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + ItdBufferPtr0));
  2872. KernelLog.String(" address "); KernelLog.Address(SYSTEM.VAL(ADDRESS, dword * {12 .. 31}));
  2873. KernelLog.String(", endpoint "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {8 .. 11}) DIV 100H, 0);
  2874. KernelLog.String(", dev address "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {0 .. 6}), 0);
  2875. KernelLog.Ln;
  2876. Indent(space + 4);
  2877. KernelLog.String("Buffer pointer 1: ");
  2878. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + ItdBufferPtr1));
  2879. KernelLog.String(" address "); KernelLog.Address(SYSTEM.VAL(ADDRESS, dword * {12 .. 31}));
  2880. KernelLog.String(", max packet size "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {0 .. 10}), 0);
  2881. IF 11 IN dword THEN KernelLog.String(" [IN]") ELSE KernelLog.String(" [OUT]") END;
  2882. KernelLog.Ln;
  2883. Indent(space + 4);
  2884. KernelLog.String("Buffer pointer 2: ");
  2885. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + ItdBufferPtr2));
  2886. KernelLog.String(" address "); KernelLog.Address(SYSTEM.VAL(ADDRESS, dword * {12 .. 31}));
  2887. KernelLog.String(", multi "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {0 .. 1}), 0); KernelLog.String("x");
  2888. KernelLog.Ln;
  2889. FOR i := 3 TO 6 DO
  2890. Indent(space + 4);
  2891. KernelLog.String("Buffer pointer "); KernelLog.Int(i, 0); KernelLog.String(": ");
  2892. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + ItdBufferPtr0 + 4 * i));
  2893. KernelLog.String(" address "); KernelLog.Address(SYSTEM.VAL(ADDRESS, dword * {12 .. 31}));
  2894. KernelLog.Ln
  2895. END
  2896. END
  2897. END ShowItd;
  2898. (** Prints the Framelist and its content. 'framelist' is the base address of the framelist, 'size' is the number of elements in the framelist. If 'expand's is TRUE, ITDs and QHs are also shown. *)
  2899. PROCEDURE ShowFramelist (framelist: ADDRESS; size, hc: LONGINT; expand: BOOLEAN);
  2900. VAR
  2901. i, entry: LONGINT;
  2902. BEGIN
  2903. FOR i := 0 TO size - 1 DO
  2904. entry := SYSTEM.GET32(framelist + 4 * i);
  2905. KernelLog.String("Frame entry "); KernelLog.Int(i, 4);
  2906. KernelLog.String(" at "); KernelLog.Address(framelist + 4 * i);
  2907. KernelLog.String(": "); KernelLog.Hex(entry, -8);
  2908. IF (hc # -1) & (i = hc) THEN KernelLog.String(" <- HC") END;
  2909. KernelLog.Ln;
  2910. IF expand & ~ODD(entry) THEN
  2911. ShowItd(entry, 4)
  2912. END
  2913. END;
  2914. END ShowFramelist;
  2915. (**
  2916. * Helper procedure to build a QH horizontal link field.
  2917. * Creates a horizontal link pointer field pointing to 'qh', with type 'type'.
  2918. * Link is marked as invalid if terminate is TRUE.
  2919. *)
  2920. PROCEDURE QhHorizontalLink (qh: ADDRESS; type: LONGINT; terminate: BOOLEAN): LONGINT;
  2921. VAR
  2922. dword: SET;
  2923. BEGIN
  2924. IF terminate THEN
  2925. dword := QhTerminate
  2926. END;
  2927. dword := dword + SYSTEM.VAL(SET, qh) + SYSTEM.VAL(SET, LSH(type, 1));
  2928. RETURN SYSTEM.VAL(LONGINT, dword)
  2929. END QhHorizontalLink;
  2930. (* Called when this module is unloaded. Unregister all EnhancedHostController objects *)
  2931. PROCEDURE Cleanup;
  2932. BEGIN
  2933. UsbHcdi.UnRegisterHostControllers(Description);
  2934. END Cleanup;
  2935. BEGIN
  2936. Modules.InstallTermHandler(Cleanup);
  2937. END UsbEhci.
  2938. UsbEhci.Install ~ SystemTools.Free UsbEhci ~