UsbEhci.Mod 93 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213
  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, UsbHcdi, Usbdi, Debug := UsbDebug;
  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. ScatterGatherListSize = 4200; (* Number of entries in scatter/gather list -> limits the maximum transfer size *)
  57. (* How many transaction of a single queue head in the asynchronous schedule list is the host controller allowed to execute
  58. 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
  59. should result in higher performance. *)
  60. HcAsyncParkModeCount = 3;
  61. (* Host Controller Capability Registers *)
  62. HcCapLength = 00H;
  63. HcCapHciVersion = 02H;
  64. HcCapSparams = 04H;
  65. HcCapCparams * = 08H;
  66. HcCapPortroute = 0CH;
  67. (* Host Controller Operational Registers *)
  68. HcUsbCmd * = 00H;
  69. HcUsbSts * = 04H;
  70. HcUsbIntr * = 08H;
  71. HcFrIndex * = 0CH;
  72. HcCtrlDsSegment * = 10H;
  73. HcPeriodicListBase * = 14H;
  74. HcAsyncListAddr * = 18H;
  75. HcConfigFlag * = 40H;
  76. HcPortSc * = 44H;
  77. (* HcUsbCmd register fields *)
  78. CmdInterruptThreshold * = {16..23};
  79. CmdAsyncSchedParkMode * = {11};
  80. CmdAsyncSchedParkCount * = {8..9};
  81. CmdLightHcReset * = {7}; (* Note: optional *)
  82. CmdAsyncAdvDoorbell * = {6};
  83. CmdAsyncSchedEnable * = {5};
  84. CmdPeriodicSchedEnable * = {4};
  85. CmdFrameListSize * = {2 .. 3};
  86. CmdHcReset * = {1};
  87. CmdRunStop * = {0};
  88. CmdReserved * = {10} + {12..15} + {24..31};
  89. (* HcUsbSts register fields *)
  90. StsAsyncSchedule * = {15};
  91. StsPeriodicSchedule * = {14};
  92. StsReclamation * = {13};
  93. StsHcHalted * = {12};
  94. (* HcUsbSts & HcUsbIntr common fields *)
  95. StsAsyncAdvance * = {5};
  96. StsHostSystemError * = {4};
  97. StsFrameListRollover * = {3};
  98. StsPortChange * = {2};
  99. StsUsbError * = {1};
  100. StsUsbInterrupt * = {0};
  101. (* Port Status & Control register, EHCIspec p. 26-30 *)
  102. PscWakeOnOvercurrent = {22};
  103. PscWakeOnDisconnect = {21};
  104. PscWakeOnConnect = {20};
  105. PscTestControl = {16..19};
  106. PscIndicatorControl = {14..15};
  107. PscPortOwner = {13};
  108. PscPortPower * = {12};
  109. PscLineStatus = {10..11};
  110. PscPortReset = {8};
  111. PscSuspend = {7};
  112. PscForcePortResume = {6};
  113. PscOvercurrentChange = {5};
  114. PscOvercurrentActive = {4};
  115. PscPortEnableChange = {3};
  116. PscPortEnable = {2};
  117. PscConnectStatusChange = {1};
  118. PscCurrentConnectStatus = {0};
  119. PscReserved = {9} + {23..31};
  120. PscChangeMask = {1, 3, 5};
  121. (* Queue Element Transfer Descriptor; must be 32byte aligned *)
  122. (* Offsets *)
  123. QtdNextQtdPointer = 00H;
  124. QtdAltNextQtdPointer = 04H;
  125. QtdToken = 08H;
  126. QtdBufferPtr0 = 0CH;
  127. QtdBufferPtr1 = 10H;
  128. QtdBufferPtr2 = 14H;
  129. QtdBufferPtr3 = 18H;
  130. QtdBufferPtr4 = 1CH;
  131. QtdExtBufferPtr0 = 20H;
  132. QtdExtBufferPtr1 = 24H;
  133. QtdExtBufferPtr2 = 28H;
  134. QtdExtBufferPtr3 = 2CH;
  135. QtdExtBufferPtr4 = 30H;
  136. (* Masks *)
  137. QtdTerminate = {0};
  138. QtdBufferPtr = {12..31};
  139. (* qTD Token *)
  140. QtdDataToggle = {31};
  141. QtdBytesToTransfer = {16..30};
  142. QtdIoc = {15}; (* Interrupt on complete *)
  143. QtdCurrentPage = {12..14};
  144. QtdErrorCounter = {10..11};
  145. QtdPidCode = {8..9};
  146. QtdStatus = {0..7};
  147. (* Isochronous Transfer Descriptor *)
  148. ItdNextLinkPointer = 00H;
  149. ItdTransaction0 = 04H;
  150. ItdBufferPtr0 = 024H;
  151. ItdBufferPtr1 = 028H;
  152. ItdBufferPtr2 = 02CH;
  153. ItdExtBufferPtr0 = 40H;
  154. (* ITD Transaction *)
  155. ItdTransactionStatus = {28..31};
  156. ItdTransactionLength = {16..27};
  157. ItdTransactionIoc = {15};
  158. ItdTransactionPg = {12..14};
  159. ItdTransactionOffset = {0..11};
  160. (* ITD Buffer Pointers *)
  161. ItdBufferPtr = {12..31};
  162. (* ItdBufferPtr0 *)
  163. ItdEndPt = {8..11};
  164. ItdReserved = {7};
  165. ItdDevAdr = {0..6};
  166. (* ItdBufferPtr1 *)
  167. ItdIn = {11};
  168. ItdMaxPacketSize = {0..10};
  169. (* ItdBufferPtr2 *)
  170. ItdMult = {0..1};
  171. (* ITD Transaction Status *)
  172. ItdStatus = {28..31};
  173. ItdActive = {31};
  174. ItdDataBufferError = {30};
  175. ItdBabbleDetected = {29};
  176. ItdTransactionError = {28};
  177. (* Queue Head *)
  178. (* Offsets *)
  179. QhHorizontalLinkPointer = 00H;
  180. QhEpCapabilities1 = 04H;
  181. QhEpCapabilities2 = 08H;
  182. QhCurrentQtdPointer = 0CH;
  183. QhNextQtdPointer = 10H;
  184. QhAltNextQtdPointer = 14H;
  185. QhQtdToken = 18H;
  186. QhBufferPointer0 = 1CH;
  187. QhBufferPointer1 = 20H;
  188. QhBufferPointer2 = 24H;
  189. QhBufferPointer3 = 28H;
  190. QhBufferPointer4 = 2CH;
  191. QhExtBufferPointer0 = 30H;
  192. QhExtBufferPointer1 = 34H;
  193. QhExtBufferPointer2 = 38H;
  194. QhExtBufferPointer3 = 3CH;
  195. QhExtBufferPointer4 = 40H;
  196. (* Masks *)
  197. (* Queue Head Horizontal Link Pointer *)
  198. QhTyp = {1..2};
  199. QhTypItd = 0;
  200. QhTypQh = 1;
  201. QhTypSitd = 2;
  202. QhTypFstn = 3; (* Frame span traversal node *)
  203. QhTerminate = {0};
  204. (* Queue Head Endpoint Capabilities *)
  205. (* Dword 1 *)
  206. QhNakCountReload = {28..31};
  207. QhControlEndpointFlag = {27};
  208. QhMaxPacketLen = {16..26};
  209. QhHeadOfReclamation = {15};
  210. QhDataToggleControl = {14};
  211. QhEndpointSpeed = {12..13};
  212. QhEndpointNbr = {8..11};
  213. QhInactivate = {7};
  214. QhDeviceAddress = {0..6};
  215. (* Dword 2 *)
  216. QhMultiplier = {30..31}; (* High-Bandwidth Pipe Muliplier *)
  217. QhPortNbr = {23..29};
  218. QhHubAddr = {16..22};
  219. QhSplitCMask = {8..15};
  220. QhSMask = {0..7};
  221. (* Periodic Frame Span Traversal Node (FSTN) *)
  222. (* FSTN offsets *)
  223. FstnNormalPathLinkPointer = 0;
  224. FstnBackPathLinkPointer = 4;
  225. (* Status fields of qTD Token *)
  226. TdActive = {7}; (* If set, the HC will process the qTD *)
  227. TdHalted = {6}; (* Caused by babble, error counter transition from one to zero or STALL handshake. Will also clear TdActive. *)
  228. TdDataBufferError = {5}; (* Buffer overrun or underrun *)
  229. TdBabbleDetected = {4}; (* Babble. Will also set TdHalted *)
  230. TdTransactionError = {3}; (* No valid response from device during status update (Timeout, CRC errir, PID wrong...) *)
  231. TdMissedMicroFrame = {2};
  232. TdSplitTransactionState = {1};
  233. TdPingState = {0};
  234. (* Periodic Frame Span Traversal Node *)
  235. FstnNormalPathLink = 00H;
  236. FstnBackPathLink = 04H;
  237. (* Packet Identifier codes *)
  238. PidOut = 0;
  239. PidIn = 1;
  240. PidSetup = 2;
  241. PageSize = 4096;
  242. Polling = FALSE;
  243. TYPE
  244. EnhancedHostController * = OBJECT (UsbHcdi.Hcd)
  245. VAR
  246. framelist *: UsbHcdi.AlignedMemSpace;
  247. pwcr * : LONGINT; (* Port Wake Capability Register; Not implemented by device if pwcr = 0 *)
  248. (* Information from Host Controller Capability Registers *)
  249. (* HCSPARAMS - Structural Parameters *)
  250. capDebugPortNumber : LONGINT; (* 0: n/a, other: number of debug port (0-15)*)
  251. capPortIndicators : BOOLEAN; (* Do the ports support port indicator control? *)
  252. capNbrOfCompanionHc : LONGINT; (* How many companion host controllers are present (0-15) *)
  253. capPortsPerCompanion : LONGINT; (* Number of ports supported per companion host controller *)
  254. capPortRoutingRules : BOOLEAN; (* Port routing rules *)
  255. capPortPowerControl : BOOLEAN; (* Does the HC support Port Power Control? *)
  256. capNbrOfPorts : LONGINT; (* Number of physical downstream ports implemented by this host controller *)
  257. (* HCCPARAMS - Capability Parameters *)
  258. capIsoSchedThreshold : LONGINT; (* Isochronous Schedule Threshold *)
  259. capAsynchSchedPark : BOOLEAN; (* Does the controller support the park feature for high-speed transfers? *)
  260. capProgrammableFLG : BOOLEAN; (* FALSE: use default (1024); TRUE: Frame List size is programmable *)
  261. cap64bit : BOOLEAN; (* 32 / 64 bit memory pointers in the data structures *)
  262. (* EHCI Extended Capabilities Pointer. Used in relation with USB legacy support *)
  263. eecp : LONGINT;
  264. (* The size of control data structures is dependent on whether the HC uses 32bit or 64bit address pointers as indicated
  265. by the cap64bit field *)
  266. sizeQtd, alignQtd : LONGINT;
  267. sizeQh, alignQh : LONGINT;
  268. (* HC Companion Port Route Descriptor, NIL of not available.
  269. If the capPortRoutingRules is TRUE, the HC provides a description of which port is routed to
  270. which companion HC. *)
  271. hcportroute : POINTER TO ARRAY OF LONGINT;
  272. (* queue heads *)
  273. isochronousQh* : LONGINT;
  274. interruptQh : POINTER TO ARRAY 11 OF LONGINT;
  275. (* this array will provide the 16byte aligned TD's for controlTD, bulkTD, isochronousTD and interruptTD[] *)
  276. qhlist : UsbHcdi.AlignedMemSpace;
  277. (* The Asynchronous Advance Doorbell interrupt is always enabled by this driver. Since the interrupt handler will
  278. clear the bit that were set when it was invoked, it sets hcHandshake to TRUE, so its sticky *)
  279. hcHandshake * : BOOLEAN;
  280. (* Set of all currently enabled interrupts *)
  281. interruptsEnabled * : SET;
  282. (** Enable power for the specified port *)
  283. PROCEDURE EnablePortPower*(port : LONGINT);
  284. VAR status : SET;
  285. BEGIN
  286. status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  287. SYSTEM.PUT32(ports[port], status - PscChangeMask + PscPortPower); FlushPCI;
  288. END EnablePortPower;
  289. (** Disable power for the specified port *)
  290. PROCEDURE DisablePortPower*(port : LONGINT);
  291. VAR status : SET;
  292. BEGIN
  293. status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  294. SYSTEM.PUT32(ports[port], status - PscChangeMask - PscPortPower); FlushPCI;
  295. END DisablePortPower;
  296. (** Enable the specified port.
  297. The EHCI host controllers do not explicitly support a port enable command. The port will be automatically enabled
  298. by the host controller after a port reset, if a high-speed capable device is attached to it *)
  299. PROCEDURE ResetAndEnablePort*(port : LONGINT) : BOOLEAN;
  300. VAR status : SET; mtimer : Kernel.MilliTimer;
  301. BEGIN
  302. status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  303. SYSTEM.PUT32(ports[port], status - PscChangeMask + PscPortReset - PscPortEnable); FlushPCI;
  304. Wait(UsbHcdi.PortResetTime); (* >= 10ms, USBspec *)
  305. SYSTEM.PUT32(ports[port], status - PscChangeMask - PscPortReset); FlushPCI;
  306. Wait(2+1); (* 2ms recovery interval according EHCIspec, p. 28 *)
  307. (* The host controller should have automatically enabled this port *)
  308. Kernel.SetTimer(mtimer, UsbHcdi.PortEnableTimeout);
  309. REPEAT
  310. status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  311. UNTIL (status * PscPortEnable # {}) OR Kernel.Expired(mtimer);
  312. RETURN status * PscPortEnable # {};
  313. END ResetAndEnablePort;
  314. (** Disable the specified port. *)
  315. PROCEDURE DisablePort*(port : LONGINT);
  316. VAR status : SET;
  317. BEGIN
  318. status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  319. SYSTEM.PUT32(ports[port], status - PscChangeMask- PscPortEnable);
  320. FlushPCI;
  321. END DisablePort;
  322. (** Suspend the specified port (selective suspend). *)
  323. PROCEDURE SuspendPort*(port : LONGINT) : BOOLEAN;
  324. VAR status : SET;
  325. BEGIN
  326. status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  327. IF (status * PscPortEnable # {}) & (status * PscPortOwner = {}) THEN
  328. SYSTEM.PUT32(ports[port], status - PscChangeMask + PscSuspend);
  329. FlushPCI;
  330. RETURN TRUE;
  331. END;
  332. RETURN FALSE;
  333. END SuspendPort;
  334. (** Resume a selectively suspended port. *)
  335. PROCEDURE ResumePort*(port : LONGINT) : BOOLEAN;
  336. VAR status : SET; timer : Kernel.Timer;
  337. BEGIN
  338. status := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  339. IF status * CmdRunStop = {} THEN
  340. (* HC must be running when resume a port. Otherwise, the device would automatically re-renter
  341. the suspended mode in 10 ms *)
  342. SYSTEM.PUT32(iobase + HcUsbCmd, status + CmdRunStop);
  343. FlushPCI;
  344. END;
  345. status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  346. IF (status * PscSuspend # {}) & (status * PscPortOwner = {}) THEN
  347. SYSTEM.PUT32(ports[port], status - PscChangeMask + PscForcePortResume);
  348. FlushPCI;
  349. NEW(timer); timer.Sleep(20); (* EHCI p. 60 *)
  350. SYSTEM.PUT32(ports[port], status - PscChangeMask - PscForcePortResume);
  351. FlushPCI;
  352. END;
  353. RETURN SYSTEM.VAL(SET, SYSTEM.GET32(ports[port])) * PscSuspend = {};
  354. (* TODO: write 1 to PORTSC Force resume bit if port is suspended; first wait 10ms (EHCIp59)*)
  355. END ResumePort;
  356. (** Suspend all ports and then stop the host controller. *)
  357. PROCEDURE Suspend*;
  358. VAR dword : SET; i : LONGINT; ignore : BOOLEAN;
  359. BEGIN
  360. (* Suspend all individual ports *)
  361. FOR i := 0 TO portCount - 1 DO ignore := SuspendPort(i); END;
  362. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd)); (* Stop HC *)
  363. SYSTEM.PUT32(iobase + HcUsbCmd, dword - CmdRunStop);
  364. FlushPCI;
  365. (* Put HC in lower device state via the PCI power management interface *)
  366. END Suspend;
  367. (** Restart the host controller and selectively resume all suspended ports. *)
  368. PROCEDURE Resume*() : BOOLEAN;
  369. VAR dword : SET; i : LONGINT; res : BOOLEAN;
  370. BEGIN
  371. (* Re-start the HC *)
  372. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  373. SYSTEM.PUT32(iobase + HcUsbCmd, dword + CmdRunStop);
  374. FlushPCI;
  375. (* Resume all individual ports *)
  376. res := TRUE;
  377. FOR i := 0 TO portCount - 1 DO
  378. IF ~ResumePort(i) THEN res := FALSE; END;
  379. END;
  380. RETURN res;
  381. END Resume;
  382. (**
  383. * Get the status of the specified port.
  384. * Registers which indicate status changes are reset by GetPortStatus.
  385. * Note: UsbHcdi.HighSpeed will only be correctly set when the port is enabled. The hub driver
  386. * takes care of this special behaviour by getting the port status again after it has enabled the port.
  387. * @param port Port to get the status of
  388. * @return Port status
  389. *)
  390. PROCEDURE GetPortStatus*(port : LONGINT; ack : BOOLEAN) : SET;
  391. VAR status, s : SET;
  392. BEGIN
  393. s := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  394. (* Clear all bits that reported a change event; the correspondig register are R/WC *)
  395. IF ack & ((s * PscChangeMask) # {}) THEN SYSTEM.PUT32(ports[port], s); END; FlushPCI;
  396. status := {};
  397. IF s * PscCurrentConnectStatus # {} THEN status := status + UsbHcdi.PortStatusDevicePresent; END;
  398. IF s * PscPortEnable # {} THEN status := status + UsbHcdi.PortStatusEnabled END;
  399. IF s * PscSuspend # {} THEN status := status + UsbHcdi.PortStatusSuspended END;
  400. IF s * PscOvercurrentActive # {} THEN status := status + UsbHcdi.PortStatusOverCurrent END;
  401. IF s * PscPortReset # {} THEN status := status + UsbHcdi.PortStatusReset END;
  402. IF s * PscPortPower # {} THEN status := status + UsbHcdi.PortStatusPowered END;
  403. IF s * PscConnectStatusChange # {} THEN status := status + UsbHcdi.PortStatusConnectChange END;
  404. IF s * PscPortEnableChange # {} THEN status := status + UsbHcdi.PortStatusEnabledChange END;
  405. IF s * PscOvercurrentChange # {} THEN status := status + UsbHcdi.PortStatusOverCurrentChange END;
  406. IF s * PscTestControl # {} THEN status := status + UsbHcdi.PortStatusTestControl END;
  407. IF s * PscIndicatorControl # {} THEN status := status + UsbHcdi.PortStatusIndicatorControl END;
  408. IF s * PscWakeOnOvercurrent # {} THEN status := status + UsbHcdi.PortStatusWakeOnOvercurrent; END;
  409. IF s * PscWakeOnDisconnect # {} THEN status := status + UsbHcdi.PortStatusWakeOnDisconnect; END;
  410. IF s * PscWakeOnConnect # {} THEN status := status + UsbHcdi.PortStatusWakeOnConnect; END;
  411. IF s * PscPortOwner # {} THEN status := status + UsbHcdi.PortStatusPortOwner; END;
  412. (* When a device is attached to a port of the root hub, the hub driver will try to reset and enable the port.
  413. The EHCI HC only enables the port if the connected device is a high-speed device which is determined during
  414. 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 *)
  415. IF (s * PscPortEnable = {}) & (s * PscCurrentConnectStatus # {}) & (s * PscPortPower # {}) & (s * {10} # {}) THEN (* Lowspeed device connected *)
  416. status := status + UsbHcdi.PortStatusLowSpeed;
  417. ELSIF (s * PscCurrentConnectStatus # {}) & (s * PscPortReset = {}) & (s * PscPortEnable # {}) THEN
  418. status := status + UsbHcdi.PortStatusHighSpeed;
  419. END;
  420. RETURN status;
  421. END GetPortStatus;
  422. (** Route the specified port to a companion host controller if supported. *)
  423. PROCEDURE RoutePortToCompanion*(port : LONGINT);
  424. VAR dword : SET;
  425. BEGIN
  426. (* Assert ports are not globally routed to companion controllers *)
  427. ASSERT(SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcConfigFlag)) * {0} # {});
  428. dword := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  429. SYSTEM.PUT32(ports[port], dword - PscChangeMask + PscPortOwner); FlushPCI;
  430. END RoutePortToCompanion;
  431. (** Indicate a port state using the port indicators *)
  432. PROCEDURE IndicatePort*(port, indicate : LONGINT);
  433. VAR indicators, dword : SET;
  434. BEGIN
  435. IF indicate = UsbHcdi.Amber THEN indicators := {14};
  436. ELSIF indicate = UsbHcdi.Green THEN indicators := {15};
  437. ELSE indicators := {};
  438. END;
  439. dword := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  440. dword := dword - PscIndicatorControl + indicators;
  441. SYSTEM.PUT32(ports[port], dword); FlushPCI;
  442. END IndicatePort;
  443. (** Return the current frame number.
  444. The micro-frame number is incremented each micro-frame, i.e. per 125us. There are 8 micro-frames per frame *)
  445. PROCEDURE GetFrameNumber*() : INTEGER;
  446. BEGIN
  447. RETURN SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, LSH(SYSTEM.GET32(iobase + HcFrIndex), -3)) * {0..10});
  448. END GetFrameNumber;
  449. (*
  450. * Contruct the queue head of the specified pipe.
  451. * Fill in the following DWORDs into pipe.qh:
  452. * - Queue Head Endpoint Capabilities 1
  453. * - Queue Head Endpoint Capabilities 2
  454. * - Current qTD Pointer
  455. * - Next qTD Pointer, Alternate Next qTD Pointer, qTD Token & all five qTD Buffer Pointers
  456. * The Queue Head Horizontal Link Pointer will be set by InsertPipeQH
  457. * @param pipe
  458. *)
  459. PROCEDURE BuildQueueHead(pipe : UsbHcdi.Pipe);
  460. VAR dword : SET; nakRL, multi, mmask : LONGINT;
  461. BEGIN
  462. ASSERT(pipe.maxPacketSize <= 1024); (* Maximum allowed packet size *)
  463. (*
  464. pipe.qh := Align(pipe.qh, alignQh);
  465. pipe.tdBase := pipe.qh + alignQh;
  466. *)
  467. (*
  468. IF ~CheckBoundary(pipe.qh, sizeQh) THEN
  469. INC(pipe.qh, sizeQh);
  470. INC(pipe.tdBase, sizeQh)
  471. END;
  472. *)
  473. (* Queue Head Horizontal Link Pointer is not set here *)
  474. (* Queue Head Endpoint Capabilities 1 *)
  475. nakRL := 3;
  476. IF pipe.type = UsbHcdi.PipeInterrupt THEN nakRL := 0; END; (* EHCIspec, p.83 *)
  477. (* IF (pipe.speed = UsbHcdi.HighSpeed) & ((pipe.type = UsbHcdi.PipeBulk) OR
  478. (pipe.type = UsbHcdi.PipeControl)) (* Control OUT only *) THEN
  479. nakRL := pipe.irqInterval;
  480. END; *)
  481. dword := LSH(SYSTEM.VAL(SET, nakRL), 28) * QhNakCountReload;
  482. IF (pipe.speed # UsbHcdi.HighSpeed) & (pipe.type = UsbHcdi.PipeControl) THEN
  483. dword := dword + QhControlEndpointFlag;
  484. END;
  485. IF pipe.type = UsbHcdi.PipeControl THEN dword := dword + QhDataToggleControl; END;
  486. dword := dword + LSH(SYSTEM.VAL(SET, pipe.maxPacketSize), 16) * QhMaxPacketLen;
  487. IF (pipe.speed = UsbHcdi.LowSpeed) THEN (* EPS - endpoint speed *)
  488. dword := dword + {12}; (* Low-speed endpoint *)
  489. ELSIF (pipe.speed = UsbHcdi.FullSpeed) THEN
  490. (* Do nothing; Full-speed endpoint *)
  491. ELSIF (pipe.speed = UsbHcdi.HighSpeed) THEN
  492. dword := dword + {13}; (* High-speed endpoint *)
  493. ELSE
  494. HALT(99);
  495. END;
  496. dword := dword + LSH(SYSTEM.VAL(SET, pipe.endpoint), 8) * QhEndpointNbr;
  497. dword := dword + SYSTEM.VAL(SET, pipe.address) * QhDeviceAddress;
  498. SYSTEM.PUT32(pipe.qh + QhEpCapabilities1, dword);
  499. (* Queue Head Endpoint Capabilities 2 *)
  500. multi := 1; (* TODO: How many transactions per frame for high-speed isochronous and interrupts transfer are allowed? *)
  501. dword := LSH(SYSTEM.VAL(SET, multi), 30) * QhMultiplier;
  502. IF (pipe.speed = UsbHcdi.LowSpeed) OR (pipe.speed = UsbHcdi.FullSpeed) THEN
  503. ASSERT((pipe.ttAddress # 0) & (pipe.ttPort >= 0));
  504. (* Hub port and address for split transaction *)
  505. dword := dword + LSH(SYSTEM.VAL(SET, pipe.ttAddress), 16) * QhHubAddr;
  506. dword := dword + LSH(SYSTEM.VAL(SET, pipe.ttPort + 1), 23) * QhPortNbr;
  507. IF (pipe.type = UsbHcdi.PipeInterrupt) OR (pipe.type = UsbHcdi.PipeIsochronous) THEN
  508. (* In which micro-frames the HC should issue Complete Split tokens *)
  509. dword := dword + LSH({2..6}, 8) * QhSplitCMask;
  510. END;
  511. END;
  512. mmask := 1;
  513. IF (pipe.type = UsbHcdi.PipeInterrupt) OR (pipe.type = UsbHcdi.PipeIsochronous) THEN
  514. dword := dword + SYSTEM.VAL(SET, mmask) * QhSMask;
  515. END;
  516. SYSTEM.PUT32(pipe.qh + QhEpCapabilities2, dword);
  517. SYSTEM.PUT32(pipe.qh + QhCurrentQtdPointer, 0);
  518. (* Zero-out the queue head transfer overlay *)
  519. SYSTEM.PUT32(pipe.qh + QhNextQtdPointer, QhTerminate);
  520. SYSTEM.PUT32(pipe.qh + QhAltNextQtdPointer, QhTerminate);
  521. SYSTEM.PUT32(pipe.qh + QhQtdToken, 0);
  522. SYSTEM.PUT32(pipe.qh + QhBufferPointer0, 0);
  523. SYSTEM.PUT32(pipe.qh + QhBufferPointer1, 0);
  524. SYSTEM.PUT32(pipe.qh + QhBufferPointer2, 0);
  525. SYSTEM.PUT32(pipe.qh + QhBufferPointer3, 0);
  526. SYSTEM.PUT32(pipe.qh + QhBufferPointer4, 0);
  527. IF cap64bit THEN
  528. SYSTEM.PUT32(pipe.qh + QhExtBufferPointer0, 0);
  529. SYSTEM.PUT32(pipe.qh + QhExtBufferPointer1, 0);
  530. SYSTEM.PUT32(pipe.qh + QhExtBufferPointer2, 0);
  531. SYSTEM.PUT32(pipe.qh + QhExtBufferPointer3, 0);
  532. SYSTEM.PUT32(pipe.qh + QhExtBufferPointer4, 0);
  533. END;
  534. END BuildQueueHead;
  535. (** Build a Queue Head for the specified pipe and insert it into the host controller schedule. *)
  536. PROCEDURE InsertQH*(pipe : UsbHcdi.Pipe) : BOOLEAN;
  537. VAR adr, asyncListAddr : LONGINT; dword : SET;
  538. BEGIN (* Only call from exclusive regions *)
  539. ASSERT((pipe # NIL) & (pipe.qh # 0) & (SYSTEM.VAL(SET, pipe.qh) * {0..4} = {}));
  540. ASSERT((pipe.maxPacketSize > 0));
  541. CASE pipe.type OF (* In which queue should we insert the pipe ? *)
  542. |UsbHcdi.PipeControl : pipe.queue := 0;
  543. | UsbHcdi.PipeBulk : pipe.queue := 0;
  544. | UsbHcdi.PipeIsochronous :
  545. (* TODO: Implement isochronous transfers *)
  546. (* Enable the periodic list if necessary *)
  547. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  548. IF dword * CmdPeriodicSchedEnable = {} THEN
  549. SYSTEM.PUT32(iobase + HcUsbCmd, dword + CmdPeriodicSchedEnable); FlushPCI;
  550. END;
  551. RETURN TRUE;
  552. | UsbHcdi.PipeInterrupt :
  553. BEGIN
  554. IF pipe.irqInterval = 1 THEN (* 1ms queue *)
  555. pipe.queue := interruptQh[0];
  556. ELSIF pipe.irqInterval < 4 THEN (* 2ms queue *)
  557. pipe.queue := interruptQh[1];
  558. ELSIF pipe.irqInterval < 8 THEN (* 4ms queue *)
  559. pipe.queue := interruptQh[2];
  560. ELSIF pipe.irqInterval < 16 THEN (* 8ms queue *)
  561. pipe.queue := interruptQh[3];
  562. ELSIF pipe.irqInterval < 32 THEN (* 16ms queue *)
  563. pipe.queue := interruptQh[4];
  564. ELSE
  565. pipe.queue := interruptQh[5]; (* 32 ms queue *)
  566. END;
  567. END;
  568. ELSE
  569. RETURN FALSE;
  570. END;
  571. BuildQueueHead(pipe);
  572. IF pipe.queue = 0 THEN (* Insert into the asynchronous schedule list *)
  573. asyncListAddr := SYSTEM.GET32(iobase + HcAsyncListAddr);
  574. IF asyncListAddr = 0 THEN (* Not queue heads in the list yet *)
  575. (* Since the address is obviously invalid, the asynchronous schedule mustn't be enabled *)
  576. ASSERT(SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsAsyncSchedule = {});
  577. SYSTEM.PUT32(pipe.qh + QhHorizontalLinkPointer, SYSTEM.VAL(SET, pipe.qh) * {5..31} + {1} - {2} - QhTerminate);
  578. (* If the asynchronous schedule is enabled, exactly one queue head MUST have the H-bit set. *)
  579. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhEpCapabilities1)) + QhHeadOfReclamation;
  580. SYSTEM.PUT32(pipe.qh + QhEpCapabilities1, dword);
  581. Machine.FlushDCacheRange(pipe.qh, sizeQh);
  582. (* Insert the queue head into the schedule list and activate the asynchronous schedule *)
  583. SYSTEM.PUT32(iobase + HcAsyncListAddr, pipe.qh); FlushPCI;
  584. IF ~ScheduleOn(CmdAsyncSchedEnable, TRUE) & (Debug.Level >= Debug.Errors) THEN Show("Failed to enable async schedule."); KernelLog.Ln; END;
  585. ELSE
  586. ASSERT(SYSTEM.VAL(SET, asyncListAddr) * {0..4} = {}); (* 32byte alignment *)
  587. adr := asyncListAddr;
  588. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + QhHorizontalLinkPointer));
  589. SYSTEM.PUT32(pipe.qh + QhHorizontalLinkPointer, dword);
  590. Machine.FlushDCacheRange(pipe.qh, sizeQh);
  591. (* Insert the newly created queue head into the asynchronous schedule list. *)
  592. dword := SYSTEM.VAL(SET, pipe.qh) * {5..31} + {1} - {2} - QhTerminate;
  593. SYSTEM.PUT32(adr + QhHorizontalLinkPointer, dword);
  594. END;
  595. ELSE (* Insert into the periodic schedule list *)
  596. adr := SYSTEM.GET32(pipe.queue + QhHorizontalLinkPointer);
  597. SYSTEM.PUT32(pipe.qh + QhHorizontalLinkPointer, adr);
  598. SYSTEM.PUT32(pipe.queue + QhHorizontalLinkPointer, pipe.qh + SYSTEM.VAL(LONGINT, {1} - {2}));
  599. Machine.FlushDCacheRange(pipe.qh, sizeQh);
  600. Machine.FlushDCacheRange(pipe.queue, sizeQh);
  601. (* Enable the periodic list if necessary *)
  602. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts));
  603. IF dword * StsPeriodicSchedule = {} THEN
  604. IF ~ScheduleOn(CmdPeriodicSchedEnable, TRUE) THEN
  605. IF Debug.Level >= Debug.Errors THEN Show("Could not enable periodic schedule."); KernelLog.Ln; END;
  606. END;
  607. END;
  608. END;
  609. IF Debug.Trace & Debug.traceQueuing THEN Show("Inserted QH at "); KernelLog.Address(pipe.qh); KernelLog.Ln; END;
  610. Machine.FlushDCacheRange(pipe.qh, sizeQh);
  611. RETURN TRUE;
  612. END InsertQH;
  613. (* Enable/Disable the periodic or asynchronous schedule. *)
  614. PROCEDURE ScheduleOn(cmd : SET; on : BOOLEAN) : BOOLEAN;
  615. VAR dword, sts : SET; mtimer : Kernel.MilliTimer;
  616. BEGIN (* Caller must hold obj lock *)
  617. ASSERT((cmd = CmdPeriodicSchedEnable) OR (cmd = CmdAsyncSchedEnable));
  618. IF Debug.Trace & Debug.traceQueuing THEN
  619. IF on THEN Show("Enabling"); ELSE Show("Disabling"); END;
  620. IF cmd = CmdAsyncSchedEnable THEN KernelLog.String(" asynchronous schedule."); ELSE KernelLog.String(" periodic schedule."); END;
  621. KernelLog.Ln;
  622. END;
  623. IF cmd = CmdAsyncSchedEnable THEN sts := StsAsyncSchedule; ELSE sts := StsPeriodicSchedule; END;
  624. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  625. ASSERT(dword * cmd = LSH(SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * sts, -10)); (* HcUsbCmd & HcUsbSts in consistent state *)
  626. IF on THEN dword := dword + cmd; ELSE dword := dword - cmd; END;
  627. SYSTEM.PUT32(iobase + HcUsbCmd, dword); FlushPCI;
  628. (* Wait until the HC reaches the desired state *)
  629. Kernel.SetTimer(mtimer, 500);
  630. WHILE ~Kernel.Expired(mtimer) & ((SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * sts # {}) # on) DO
  631. Objects.Yield;
  632. END;
  633. RETURN (SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * sts # {}) = on;
  634. END ScheduleOn;
  635. (*
  636. * Remove a queue head data structure from the host controller's asynchronous schedule.
  637. * The asynchronous schedule is a circular linked list of queue heads. At least one queue heads has
  638. * the H-bit (Head of asynchronous schedule list) set which is used by the host controller to detect
  639. * empty list conditions. There are two cases when we remove a queue head:
  640. * 1) It is the only queue head in the list. In this case, we disabled the asynchronous schedule execution and
  641. * and remove the queue head then.
  642. * 2) There are other queue heads in the list. If the queue head to be removed is the head of the list, we
  643. * need to set the H-bit for another queue head.
  644. *
  645. * Precondition: TDs are already removed from the QH, QH is inactive
  646. *)
  647. PROCEDURE RemoveAsyncQH(pipe : UsbHcdi.Pipe);
  648. VAR start, cur, prev : LONGINT; dword : SET;
  649. BEGIN (* Caller must hold obj lock *)
  650. prev := SYSTEM.GET32(iobase + HcAsyncListAddr);
  651. ASSERT((prev # 0) & (SYSTEM.VAL(SET, prev) * {0..4} = {}));
  652. Machine.InvalidateDCacheRange(prev, sizeQh);
  653. prev := SYSTEM.GET32(prev + QhHorizontalLinkPointer);
  654. ASSERT((SYSTEM.VAL(SET, prev) * {1} # {}) & (SYSTEM.VAL(SET, prev) * QhTerminate = {})); (* Pointer references queue head *)
  655. prev := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, prev) * {5..31});
  656. Machine.InvalidateDCacheRange(prev, sizeQh);
  657. cur := SYSTEM.GET32(prev + QhHorizontalLinkPointer);
  658. ASSERT((SYSTEM.VAL(SET, cur) * {1} # {}) & (SYSTEM.VAL(SET, cur) * QhTerminate = {})); (* Pointer references queue head *)
  659. cur := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, cur) * {5..31});
  660. Machine.InvalidateDCacheRange(cur, sizeQh);
  661. (* prev is the address of the queue head that points to the queue head with the address cur *)
  662. IF cur = prev THEN (* Only one queue head in the list *)
  663. ASSERT(SYSTEM.VAL(SET, SYSTEM.GET32(cur + QhEpCapabilities1)) * QhHeadOfReclamation # {});
  664. ASSERT(SYSTEM.VAL(SET, SYSTEM.GET32(cur + QhQtdToken)) * TdActive = {});
  665. IF cur = pipe.qh THEN (* just disable asynchronous schedule *)
  666. IF ScheduleOn(CmdAsyncSchedEnable, FALSE) THEN
  667. SYSTEM.PUT32(iobase + HcAsyncListAddr, 0); FlushPCI; (* Mark as invalid. *)
  668. ELSIF Debug.Level >= Debug.Errors THEN Show("Could not disable async schedule."); KernelLog.Ln;
  669. END;
  670. ELSIF Debug.Level >= Debug.Warnings THEN Show("Failed to remove QH from asynchronous schedule: QH not found."); KernelLog.Ln;
  671. END;
  672. ELSE (* Find and remove the queue head in the list *)
  673. (* Search the queue head that references the queue head to be removed *)
  674. start := cur;
  675. LOOP
  676. dword := SYSTEM.VAL(SET, SYSTEM.GET32(cur + QhHorizontalLinkPointer));
  677. ASSERT(dword * QhTerminate = {}); (* Circular lists don't terminate *)
  678. ASSERT(dword * {1} # {}); (* Pointer references queue head *)
  679. ASSERT(dword * {2..4} = {}); (* qTD pointers must be 32byte aligned *)
  680. prev := cur;
  681. cur := SYSTEM.VAL(LONGINT, dword * {5..31});
  682. IF cur = pipe.qh THEN (* QH found *) EXIT; END;
  683. IF cur = start THEN (* list completely searched but QH not found *) EXIT; END;
  684. Machine.InvalidateDCacheRange(cur, sizeQh);
  685. END;
  686. IF cur = pipe.qh THEN (* Found the queue head. prev is pointing to it *)
  687. (* If we remove the head of reclamation, elect a new one *)
  688. IF SYSTEM.VAL(SET, SYSTEM.GET32(cur + QhEpCapabilities1)) * QhHeadOfReclamation # {} THEN
  689. IF Debug.Trace & Debug.traceQueuing THEN Show("Electing new head of reclamation."); KernelLog.Ln; END;
  690. dword := SYSTEM.VAL(SET, SYSTEM.GET32(prev + QhEpCapabilities1));
  691. SYSTEM.PUT32(prev + QhEpCapabilities1, dword + QhHeadOfReclamation);
  692. END;
  693. (* Remove QH from asynchronous list and inforam host controller *)
  694. dword := SYSTEM.VAL(SET, SYSTEM.GET32(cur + QhHorizontalLinkPointer));
  695. SYSTEM.PUT32(prev + QhHorizontalLinkPointer, dword);
  696. Machine.FlushDCacheRange(prev, sizeQh);
  697. ELSIF Debug.Level >= Debug.Warnings THEN Show("Failed to remove QH from asynchronous list: QH not found."); KernelLog.Ln;
  698. END;
  699. (* Before we may free the pipe ressources, we have to make sure that the HC has no cached references to the structure *)
  700. (* we just removed. *)
  701. IF ~HcHandshake() THEN
  702. IF Debug.Level >= Debug.Errors THEN Show("UsbEhci: Serious error: HC handshake failed."); KernelLog.Ln; END;
  703. END;
  704. END;
  705. IF Debug.Trace & Debug.traceQueuing THEN Show("Removed QH at "); KernelLog.Hex(pipe.qh, 8); KernelLog.Ln; END;
  706. END RemoveAsyncQH;
  707. (*
  708. * Inform the host controller that we removed something from the asynchronous schedule list. This is
  709. * necessary since the HC could have cached a copy of the pointer to the queue head we've just removed.
  710. *)
  711. PROCEDURE HcHandshake() : BOOLEAN;
  712. VAR dword : SET; mtimer : Kernel.MilliTimer; result : BOOLEAN;
  713. BEGIN (* caller holds object lock *)
  714. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts));
  715. ASSERT(dword * StsAsyncSchedule # {}); (* HC behaviour undefined if ringing doorbell while async schedule is off *)
  716. hcHandshake := FALSE;
  717. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  718. SYSTEM.PUT32(iobase + HcUsbCmd, dword + CmdAsyncAdvDoorbell); FlushPCI;
  719. Kernel.SetTimer(mtimer, 500);
  720. WHILE ~Kernel.Expired(mtimer) & (SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd)) * CmdAsyncAdvDoorbell # {}) DO
  721. Objects.Yield;
  722. END;
  723. result := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd)) * CmdAsyncAdvDoorbell = {}; (* The HC should have cleared the bit *)
  724. IF Debug.Trace & Debug.traceQueuing THEN
  725. Show("HC handshake "); IF result THEN KernelLog.String("succeeded."); ELSE KernelLog.String("failed."); END; KernelLog.Ln;
  726. END;
  727. RETURN result;
  728. END HcHandshake;
  729. PROCEDURE RemovePeriodicQH(pipe : UsbHcdi.Pipe);
  730. VAR timer : Kernel.Timer; cur, temp : LONGINT; next : SET;
  731. BEGIN (* caller must hold obj lock *)
  732. IF pipe.queue = 0 THEN RETURN; END;
  733. cur := pipe.queue;
  734. LOOP
  735. Machine.InvalidateDCacheRange(cur, sizeQh);
  736. next := SYSTEM.VAL(SET, SYSTEM.GET32(cur + QhHorizontalLinkPointer));
  737. IF next * {5..31} = SYSTEM.VAL(SET, pipe.qh) * {5..31} THEN (* found *)
  738. temp := SYSTEM.GET32(pipe.qh + QhHorizontalLinkPointer);
  739. SYSTEM.PUT32(cur + QhHorizontalLinkPointer, temp);
  740. Machine.FlushDCacheRange(cur, sizeQh);
  741. IF Debug.Trace & Debug.traceQueuing THEN KernelLog.String("UsbEhci: Deleted Interrupt Pipe QH."); KernelLog.Ln; END;
  742. NEW(timer); timer.Sleep(10); (* HC has still access to QH, wait > 1ms *)
  743. EXIT;
  744. ELSIF next * QhTerminate # {} THEN (* not found, reached end of list *)
  745. IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbEhci: Could not delete interrupt QH -> QH not found."); KernelLog.Ln; END;
  746. EXIT;
  747. ELSE
  748. cur := SYSTEM.VAL(LONGINT, next * {5..31});
  749. END;
  750. END;
  751. IF Debug.Trace & Debug.traceQueuing THEN Show("Removed QH at "); KernelLog.Hex(pipe.qh, 8); KernelLog.Ln; END;
  752. END RemovePeriodicQH;
  753. (** Remove the pipe's queue head from the host controller schedule *)
  754. PROCEDURE RemoveQH*(pipe : UsbHcdi.Pipe);
  755. BEGIN (* caller must hold obj lock *)
  756. IF Debug.Trace & Debug.traceQueuing THEN Show("Removing QH at "); KernelLog.Hex(pipe.qh, 8); KernelLog.Ln; END;
  757. (* First remove all transfer descriptors from the queue head *)
  758. UnlinkTDsInternal(pipe);
  759. (* Then remove the pipe's queue head from the host controller schedule *)
  760. IF (pipe.type = UsbHcdi.PipeControl) OR (pipe.type = UsbHcdi.PipeBulk) THEN
  761. RemoveAsyncQH(pipe);
  762. ELSIF pipe.type = UsbHcdi.PipeInterrupt THEN
  763. RemovePeriodicQH(pipe);
  764. ELSE
  765. (* TODO: Isochronous transfers not yet implemented *)
  766. END;
  767. END RemoveQH;
  768. (** Checks whether TDs may be linked to the pipe's QH *)
  769. PROCEDURE LinkTDsAllowed*(pipe : UsbHcdi.Pipe) : BOOLEAN;
  770. VAR dword : SET;
  771. BEGIN {EXCLUSIVE}
  772. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken));
  773. IF dword * TdActive # {} THEN
  774. IF Debug.Level >= Debug.Errors THEN Show("LinkTDs: ERROR: PIPE IS STILL ACTIVE!!!!"); KernelLog.Ln; END;
  775. pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.LinkTDsFailed;
  776. RETURN FALSE;
  777. END;
  778. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhNextQtdPointer));
  779. IF dword * QhTerminate = {} THEN
  780. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: LinkTDs: Overwriten valid pointer ?!?"); KernelLog.Ln; END;
  781. pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.LinkTDsFailed;
  782. RETURN FALSE;
  783. END;
  784. RETURN TRUE;
  785. END LinkTDsAllowed;
  786. (* Insert the TD list <td> into the queue (ED) <queue> *)
  787. PROCEDURE LinkTDs*(pipe : UsbHcdi.Pipe; qtd : Machine.Address32);
  788. VAR dword : SET;
  789. BEGIN {EXCLUSIVE}
  790. ASSERT(SYSTEM.VAL(SET, qtd) * {0..4} = {}); (* 32byte alignment *)
  791. (* Pipe must be inactive... *)
  792. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken));
  793. IF dword * TdHalted # {} THEN
  794. IF Debug.Trace & Debug.tracePipes THEN Show("LinkTDs: Automatically clear halt condition"); KernelLog.Ln; END;
  795. ClearHalt(pipe);
  796. END;
  797. SYSTEM.PUT32(pipe.qh + QhNextQtdPointer, qtd);
  798. Machine.FlushDCacheRange(pipe.qh, sizeQh);
  799. Machine.FlushDCacheRange(qtd, sizeQtd);
  800. IF SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsAsyncSchedule = {} THEN
  801. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  802. IF dword * CmdAsyncSchedEnable = {} THEN
  803. IF ~ScheduleOn(CmdAsyncSchedEnable, TRUE) & (Debug.Level >= Debug.Errors) THEN Show("Failed to re-enabled async schedule."); KernelLog.Ln; END;
  804. END;
  805. END;
  806. END LinkTDs;
  807. (** Remove all transfer descriptors from the pipe's queue head *)
  808. PROCEDURE UnlinkTDs*(pipe : UsbHcdi.Pipe);
  809. BEGIN {EXCLUSIVE}
  810. UnlinkTDsInternal(pipe);
  811. END UnlinkTDs;
  812. (** Remove all transfer descriptors from the pipe's queue head *)
  813. PROCEDURE UnlinkTDsInternal(pipe : UsbHcdi.Pipe);
  814. VAR dword : SET; timer : Kernel.Timer; qtd : LONGINT; mtimer : Kernel.MilliTimer;
  815. BEGIN (* caller must hold obj lock *)
  816. IF pipe.firstTD = 0 THEN RETURN END; (* pipe has not yet been used *)
  817. (* We must inactivate all qTD of the queue head... *)
  818. qtd := pipe.firstTD; ASSERT(pipe.lastTD >= pipe.firstTD); (* Consistency check *)
  819. AssertAlignment(qtd, alignQtd);
  820. WHILE qtd <= pipe.lastTD DO
  821. Machine.InvalidateDCacheRange(qtd, sizeQtd);
  822. AssertAlignment(qtd, alignQtd);
  823. dword := SYSTEM.VAL(SET, SYSTEM.GET32(qtd + QtdToken));
  824. SYSTEM.PUT32(qtd + QtdToken, dword - TdActive);
  825. qtd := qtd + sizeQtd;
  826. END;
  827. (* we should wait until the transaction overlay is also inactive *)
  828. Kernel.SetTimer(mtimer, 2000);
  829. Machine.InvalidateDCacheRange(pipe.qh, sizeQh);
  830. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken));
  831. WHILE ~Kernel.Expired(mtimer) & (dword * TdActive # {}) DO
  832. Machine.InvalidateDCacheRange(pipe.qh, sizeQh);
  833. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken));
  834. Objects.Yield;
  835. END;
  836. IF dword * TdActive # {} THEN
  837. IF Debug.Level >= Debug.Errors THEN Show("Transaction overlay indicates active transfer!"); KernelLog.Ln; END;
  838. END;
  839. NEW(timer); timer.Sleep(10); (* > 1ms - the HC could update the QhQtdToken field *)
  840. SYSTEM.PUT32(pipe.qh + QhQtdToken, 0);
  841. Machine.FlushDCacheRange(pipe.qh, sizeQh);
  842. timer.Sleep(2); (* > 1ms *)
  843. IF SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken)) * TdActive # {} THEN
  844. IF Debug.Level >= Debug.Errors THEN Show("Failed to unlink TDs from pipe:"); KernelLog.Ln; pipe.Show(TRUE); KernelLog.Ln; END;
  845. (* RETURN; *)
  846. END;
  847. SYSTEM.PUT32(pipe.qh + QhNextQtdPointer, QhTerminate);
  848. SYSTEM.PUT32(pipe.qh + QhAltNextQtdPointer, QhTerminate);
  849. SYSTEM.PUT32(pipe.qh + QhCurrentQtdPointer, 0); (* Bits 0-4 are reserved *)
  850. SYSTEM.PUT32(pipe.qh + QhQtdToken, 0);
  851. Machine.FlushDCacheRange(pipe.qh, sizeQh);
  852. pipe.firstTD := 0; pipe.lastTD := 0;
  853. END UnlinkTDsInternal;
  854. (*
  855. * Clears the Halt bit in the pipe's queue head and removes any qTD from the pipe.
  856. * Note that this only makes sense if the Halt feature of the USB device is also cleared used the ClearFeature standard
  857. * request. This procedure here only changes the pipe's queue head.
  858. *)
  859. PROCEDURE ClearHalt(pipe : UsbHcdi.Pipe);
  860. VAR dword : SET;
  861. BEGIN
  862. Machine.InvalidateDCacheRange(pipe.qh, sizeQh);
  863. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken));
  864. IF dword * TdHalted # {} THEN
  865. SYSTEM.PUT32(pipe.qh + QhCurrentQtdPointer, 0);
  866. (* Zero-out the queue head transfer overlay *)
  867. SYSTEM.PUT32(pipe.qh + QhNextQtdPointer, QhTerminate);
  868. SYSTEM.PUT32(pipe.qh + QhAltNextQtdPointer, QhTerminate);
  869. SYSTEM.PUT32(pipe.qh + QhQtdToken, 0);
  870. SYSTEM.PUT32(pipe.qh + QhBufferPointer0, 0);
  871. SYSTEM.PUT32(pipe.qh + QhBufferPointer1, 0);
  872. SYSTEM.PUT32(pipe.qh + QhBufferPointer2, 0);
  873. SYSTEM.PUT32(pipe.qh + QhBufferPointer3, 0);
  874. SYSTEM.PUT32(pipe.qh + QhBufferPointer4, 0);
  875. IF cap64bit THEN
  876. SYSTEM.PUT32(pipe.qh + QhExtBufferPointer0, 0);
  877. SYSTEM.PUT32(pipe.qh + QhExtBufferPointer1, 0);
  878. SYSTEM.PUT32(pipe.qh + QhExtBufferPointer2, 0);
  879. SYSTEM.PUT32(pipe.qh + QhExtBufferPointer3, 0);
  880. SYSTEM.PUT32(pipe.qh + QhExtBufferPointer4, 0);
  881. END;
  882. Machine.FlushDCacheRange(pipe.qh, sizeQh)
  883. ELSIF Debug.Level >= Debug.Warnings THEN Show("Tried to clear a non-halted pipe."); KernelLog.Ln;
  884. END;
  885. END ClearHalt;
  886. (**
  887. * Put the specified control transfer into the host controller's schedule.
  888. * USB Control Transfers use a three stage protocol:
  889. * - stage 1: control setup transaction
  890. * - stage 2: optional data stage
  891. * - stage 3: status transaction
  892. * For high-speed devices, the PING protocol must be used for OUT transactions in the data stage and status stage.
  893. *
  894. *
  895. * @param pipe
  896. * @param direction Direction of the control transfer (UsbHcdi.In (device-to-host) | UsbHcdi.Out (host-to-device))
  897. * @param msg Control message
  898. * @param bufferlen Number of bytes transmitted/received in the data stage
  899. * @param buffer Buffer where to get/put the specified number of bytes
  900. *)
  901. PROCEDURE ScheduleControl*(pipe : UsbHcdi.Pipe; direction : LONGINT; msg : UsbHcdi.ControlMessage; bufferLen : LONGINT; VAR buffer : Usbdi.Buffer);
  902. VAR
  903. qtd : Machine.Address32;
  904. dword : SET;
  905. ranges : ARRAY ScatterGatherListSize OF Machine.Range;
  906. numRanges : LONGINT;
  907. BEGIN
  908. (*Machine.FlushDCacheRange(ADDRESSOF(buffer[0]), bufferLen);*)
  909. (* pipe.tdBase = pipe.qh + 32 in UsbHcdi *)
  910. (* pipe.firstTD := pipe.tdBase;
  911. ASSERT(pipe.firstTD = pipe.qh + alignQh);
  912. *)
  913. pipe.firstTD := pipe.tdBase - 32 + alignQh;
  914. AssertAlignment(pipe.firstTD, alignQtd);
  915. ASSERT(SYSTEM.VAL(SET, pipe.firstTD) * {0..4} = {}); (* qTDs must be 32byte aligned *)
  916. ASSERT(pipe.firstTD MOD alignQtd = 0);
  917. IF (pipe.speed = UsbHcdi.LowSpeed) OR (pipe.speed = UsbHcdi.FullSpeed) THEN
  918. IF pipe.maxRetries = 0 THEN
  919. (* For low-speed and full-speed devices, the value 0 is not allowed *)
  920. pipe.maxRetries := 3;
  921. END;
  922. END;
  923. (* Stage1: Control setup transaction *)
  924. qtd := pipe.firstTD;
  925. ASSERT((qtd + sizeQtd - 1 <= ADDRESSOF(pipe.tdBuffer[pipe.tdBufferLen-1])));
  926. AssertAlignment(qtd+sizeQtd, alignQtd);
  927. SYSTEM.PUT32(qtd + QtdNextQtdPointer, qtd + sizeQtd);
  928. SYSTEM.PUT32(qtd + QtdAltNextQtdPointer, QtdTerminate); (* Mark Alternate Next qTD Pointer as invalid *)
  929. dword := LSH(SYSTEM.VAL(SET, pipe.maxRetries), 10) * QtdErrorCounter; (* DataToggle = FALSE; Current Page = 0; no IOC *)
  930. dword := dword + LSH(SYSTEM.VAL(SET, 8), 16) * QtdBytesToTransfer; (* 8byte control message *)
  931. dword := dword + LSH(SYSTEM.VAL(SET, PidSetup), 8) * QtdPidCode + TdActive;
  932. SYSTEM.PUT32(qtd + QtdToken, dword);
  933. Machine.TranslateVirtual(ADDRESSOF(msg[0]), 8, numRanges, ranges);
  934. IF numRanges = 0 THEN
  935. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: ScheduleControl: Scatter/Gather list too small."); KernelLog.Ln; END;
  936. pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.TransferTooLarge; RETURN;
  937. END;
  938. (* The HC will access the next buffer pointer when the buffer crosses a physical page... *)
  939. SYSTEM.PUT32(qtd + QtdBufferPtr0, ranges[0].adr);
  940. IF numRanges > 1 THEN (* buffer is across page boundaries *)
  941. SYSTEM.PUT32(qtd + QtdBufferPtr1, ranges[1].adr)
  942. ELSE
  943. SYSTEM.PUT32(qtd + QtdBufferPtr1, 0);
  944. END;
  945. SYSTEM.PUT32(qtd + QtdBufferPtr2, 0);
  946. SYSTEM.PUT32(qtd + QtdBufferPtr3, 0);
  947. SYSTEM.PUT32(qtd + QtdBufferPtr4, 0);
  948. IF cap64bit THEN
  949. SYSTEM.PUT32(qtd + QtdExtBufferPtr0, 0);
  950. SYSTEM.PUT32(qtd + QtdExtBufferPtr1, 0);
  951. SYSTEM.PUT32(qtd + QtdExtBufferPtr2, 0);
  952. SYSTEM.PUT32(qtd + QtdExtBufferPtr3, 0);
  953. SYSTEM.PUT32(qtd + QtdExtBufferPtr4, 0);
  954. END;
  955. Machine.FlushDCacheRange(qtd, sizeQtd);
  956. (* Setup phase always starts with dataToggle = FALSE, so now it must be TRUE *)
  957. pipe.dataToggle := TRUE;
  958. (* Stage 2: Optional data stage *)
  959. IF bufferLen # 0 THEN
  960. IF ~CreateTDList(pipe, direction, bufferLen, 0, buffer, qtd + sizeQtd, qtd, TRUE) THEN
  961. pipe.status := Usbdi.Error; pipe.errors := UsbHcdi.Internal; RETURN;
  962. END;
  963. END;
  964. Machine.FlushDCacheRange(qtd, sizeQtd);
  965. qtd := qtd + sizeQtd;
  966. AssertAlignment(qtd, alignQtd);
  967. IF qtd + sizeQtd - 1 > ADDRESSOF(pipe.tdBuffer[pipe.tdBufferLen-1]) THEN
  968. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: TD buffer too small."); KernelLog.Ln; END;
  969. pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.OutOfTDs; RETURN;
  970. END;
  971. (* stage 3: status: build status TD *)
  972. SYSTEM.PUT32(qtd + QtdNextQtdPointer, QtdTerminate); (* Last qTD in chain *)
  973. SYSTEM.PUT32(qtd + QtdAltNextQtdPointer, QtdTerminate); (* Mark Alternate Next qTD Pointer as invalid *)
  974. dword := QtdDataToggle + TdActive; (* dataToggle always TRUE and set ind TD in status stage; CC = not accessed *)
  975. IF (direction = UsbHcdi.Out) OR (bufferLen = 0) THEN
  976. dword := dword + LSH(SYSTEM.VAL(SET, PidIn), 8);
  977. ELSE
  978. dword := dword + LSH(SYSTEM.VAL(SET, PidOut), 8);
  979. IF pipe.speed = UsbHcdi.HighSpeed THEN (* Do PING protocol *)
  980. dword := dword + TdPingState;
  981. END;
  982. END;
  983. dword := dword + LSH(SYSTEM.VAL(SET, pipe.maxRetries), 10) * QtdErrorCounter;
  984. IF pipe.ioc THEN dword := dword + QtdIoc; END; (* Set interrupt on completion bit *)
  985. SYSTEM.PUT32(qtd + QtdToken, dword);
  986. SYSTEM.PUT32(qtd + QtdBufferPtr0, 0);
  987. SYSTEM.PUT32(qtd + QtdBufferPtr1, 0);
  988. SYSTEM.PUT32(qtd + QtdBufferPtr2, 0);
  989. SYSTEM.PUT32(qtd + QtdBufferPtr3, 0);
  990. SYSTEM.PUT32(qtd + QtdBufferPtr4, 0);
  991. IF cap64bit THEN
  992. SYSTEM.PUT32(qtd + QtdExtBufferPtr0, 0);
  993. SYSTEM.PUT32(qtd + QtdExtBufferPtr1, 0);
  994. SYSTEM.PUT32(qtd + QtdExtBufferPtr2, 0);
  995. SYSTEM.PUT32(qtd + QtdExtBufferPtr3, 0);
  996. SYSTEM.PUT32(qtd + QtdExtBufferPtr4, 0);
  997. END;
  998. Machine.FlushDCacheRange(ADDRESSOF(msg[0]), LEN(msg));
  999. Machine.FlushDCacheRange(ADDRESSOF(buffer[0]), bufferLen);
  1000. Machine.FlushDCacheRange(qtd, sizeQtd);
  1001. pipe.lastTD := qtd;
  1002. END ScheduleControl;
  1003. PROCEDURE Schedule*(pipe : UsbHcdi.Pipe; bufferLen, offset: LONGINT; VAR buffer: Usbdi.Buffer);
  1004. VAR dword : SET;
  1005. BEGIN
  1006. Machine.FlushDCacheRange(ADDRESSOF(buffer[offset]), bufferLen);
  1007. SYSTEM.PUT32(pipe.qh + QhCurrentQtdPointer, 0);
  1008. (* pipe.firstTD := pipe.tdBase;
  1009. ASSERT(pipe.firstTD = pipe.qh + alignQh);
  1010. *)
  1011. pipe.firstTD := pipe.tdBase - 32 + alignQh;
  1012. AssertAlignment(pipe.firstTD, alignQtd);
  1013. ASSERT(pipe.firstTD MOD alignQtd = 0);
  1014. ASSERT(SYSTEM.VAL(SET, pipe.firstTD) * {0..4} = {}); (* qTDs must be 32byte aligned *)
  1015. IF ~CreateTDList(pipe, pipe.direction, bufferLen, offset, buffer, pipe.firstTD, pipe.lastTD, FALSE) THEN
  1016. pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.LinkTDsFailed; RETURN;
  1017. END;
  1018. SYSTEM.PUT32(pipe.lastTD + QtdNextQtdPointer, QhTerminate);
  1019. IF pipe.ioc THEN
  1020. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.lastTD + QtdToken));
  1021. dword := dword + QtdIoc;
  1022. SYSTEM.PUT32(pipe.lastTD + QtdToken, dword);
  1023. END;
  1024. END Schedule;
  1025. PROCEDURE CreateTDList(pipe : UsbHcdi.Pipe; direction, len, ofs : LONGINT; VAR buffer : Usbdi.Buffer; firstTD : LONGINT; VAR lastTD : Machine.Address32; tdToggle : BOOLEAN) : BOOLEAN;
  1026. VAR
  1027. restlen, curlen, temp : LONGINT;
  1028. j, qtd, nextTd : LONGINT;
  1029. dword : SET;
  1030. numRanges, idx, offset : LONGINT;
  1031. BEGIN
  1032. ASSERT((pipe.maxRetries >= 0) & (pipe.maxRetries <= 3));
  1033. Machine.TranslateVirtual(ADDRESSOF(buffer[ofs]), len, numRanges, pipe.sgList^);
  1034. IF numRanges = 0 THEN
  1035. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: Schedule: Scatter/Gather list too small"); KernelLog.Ln; END;
  1036. pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.TransferTooLarge;
  1037. RETURN FALSE;
  1038. END;
  1039. qtd := firstTD - sizeQtd;
  1040. idx := 0;
  1041. offset := 0; (* offset from last qTD (must fill multiples of packetSize into qTD buffers) *)
  1042. curlen := 0; (* amount of data that is transferred in a single qTD *)
  1043. restlen := len; (* total amount of data to be transferred *)
  1044. WHILE restlen > 0 DO (* build qTD chain *)
  1045. qtd := qtd + sizeQtd;
  1046. IF qtd + sizeQtd - 1 > ADDRESSOF(pipe.tdBuffer[pipe.tdBufferLen-1]) THEN
  1047. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: TD buffer too small"); KernelLog.Ln; END;
  1048. pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.OutOfTDs;
  1049. RETURN FALSE;
  1050. END;
  1051. (* Each qTD has for buffer pointers. Each buffer is 4K. The buffer must be virtually contiguous but may be *)
  1052. (* physically non-contiguous. The HC detects crossings of page boundaries and increments the current buffer pointer. *)
  1053. SYSTEM.PUT32(qtd + QtdBufferPtr0, pipe.sgList[idx].adr + offset);
  1054. curlen := PageSize - LONGINT ((pipe.sgList[idx].adr + offset) MOD PageSize);
  1055. IF curlen > restlen THEN (* No other buffer pointers needed, fits into the first page *)
  1056. curlen := restlen;
  1057. END;
  1058. ASSERT(curlen > 0);
  1059. restlen := restlen - curlen; offset := 0;
  1060. INC(idx);
  1061. (* Fill in the other 4 buffer pointers *)
  1062. FOR j := 1 TO 4 DO
  1063. IF restlen <= 0 THEN
  1064. SYSTEM.PUT32(qtd + QtdBufferPtr0 + j*4, 0);
  1065. ELSE
  1066. IF j = 4 THEN (* last buffer available in this qTD *)
  1067. temp := PageSize - ((curlen + PageSize) MOD pipe.maxPacketSize); (* data that fits into the last buffer (max) *)
  1068. IF restlen > temp THEN
  1069. (* The HC will issues USB transaction at pipe.maxPacketSize granularity. If this is not the *)
  1070. (* last qTD of this qTD chain, curlen must be multiple of pipe.maxPacketSize. If the last qTD of this *)
  1071. (* chain was not a multiple of pipe.maxPacketSize, the device will send more data (since we *)
  1072. (* requested more data) and the HC thinks it's a babble. *)
  1073. curlen := curlen + temp; restlen := restlen - temp; offset := temp;
  1074. SYSTEM.PUT32(qtd + QtdBufferPtr0 + j*4, pipe.sgList[idx].adr);
  1075. IF offset = PageSize THEN INC(idx); offset := 0;
  1076. ELSE (* In the next iteration of the outer while loop, the same page will be completed -> don't increment idx *)
  1077. END;
  1078. ELSE (* this is the last qTD in chains *)
  1079. curlen := curlen + restlen; restlen := 0;
  1080. SYSTEM.PUT32(qtd + QtdBufferPtr0 + j*4, pipe.sgList[idx].adr);
  1081. END;
  1082. ELSE
  1083. IF restlen > PageSize THEN
  1084. curlen := curlen + PageSize; restlen := restlen - PageSize;
  1085. ELSE
  1086. curlen := curlen + restlen; restlen := 0;
  1087. END;
  1088. SYSTEM.PUT32(qtd + QtdBufferPtr0 + j*4, pipe.sgList[idx].adr);
  1089. INC(idx);
  1090. END;
  1091. END;
  1092. END;
  1093. IF cap64bit THEN
  1094. SYSTEM.PUT32(qtd + QtdExtBufferPtr0, 0);
  1095. SYSTEM.PUT32(qtd + QtdExtBufferPtr1, 0);
  1096. SYSTEM.PUT32(qtd + QtdExtBufferPtr2, 0);
  1097. SYSTEM.PUT32(qtd + QtdExtBufferPtr3, 0);
  1098. SYSTEM.PUT32(qtd + QtdExtBufferPtr4, 0);
  1099. END;
  1100. SYSTEM.PUT32(qtd + QtdNextQtdPointer, qtd + sizeQtd);
  1101. SYSTEM.PUT32(qtd + QtdAltNextQtdPointer, QtdTerminate); (* Mark Alternate Next qTD Pointer as invalid *)
  1102. ASSERT(curlen <= 5000H); (* Maximum allowed value for a single qTD: 5*4KB *)
  1103. dword := TdActive;
  1104. dword := dword + LSH(SYSTEM.VAL(SET, pipe.maxRetries), 10) * QtdErrorCounter; (* Current Page=0 *)
  1105. dword := dword + LSH(SYSTEM.VAL(SET, curlen), 16) * QtdBytesToTransfer;
  1106. IF tdToggle THEN
  1107. IF pipe.dataToggle THEN dword := dword + QtdDataToggle; END;
  1108. (* Calculate datatoggle value for next TD *)
  1109. IF (curlen DIV pipe.maxPacketSize) MOD 2 # 0 THEN
  1110. pipe.dataToggle := ~pipe.dataToggle;
  1111. END;
  1112. END;
  1113. IF direction = UsbHcdi.In THEN
  1114. dword := dword + LSH(SYSTEM.VAL(SET, PidIn), 8);
  1115. ELSIF direction = UsbHcdi.Out THEN
  1116. dword := dword + LSH(SYSTEM.VAL(SET, PidOut), 8);
  1117. IF pipe.speed = UsbHcdi.HighSpeed THEN (* Do PING protocol *)
  1118. dword := dword + TdPingState;
  1119. END;
  1120. END;
  1121. SYSTEM.PUT32(qtd + QtdToken, dword);
  1122. Machine.FlushDCacheRange(qtd, sizeQtd);
  1123. END;
  1124. lastTD := qtd;
  1125. RETURN TRUE;
  1126. END CreateTDList;
  1127. PROCEDURE InterruptHandler;
  1128. VAR s : SET;
  1129. BEGIN (* Works without being exclusive *)
  1130. IF Debug.Stats THEN INC(NnofInterrupts); END;
  1131. IF state >= UsbHcdi.Initialized THEN
  1132. s := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * interruptsEnabled;
  1133. IF s # {} THEN
  1134. IF Debug.Stats THEN INC(NnofInterruptsHandled); END;
  1135. IF Debug.Trace & Debug.traceInterrupts THEN
  1136. Show("Interrupt: "); ShowInterrupts(s); KernelLog.Ln;
  1137. END;
  1138. (* Reset interrupt status register (Write clear)*)
  1139. SYSTEM.PUT32(iobase + HcUsbSts, s * {0..5}); FlushPCI;
  1140. IF s * StsAsyncAdvance # {} THEN hcHandshake := TRUE; END;
  1141. IF s * StsHostSystemError # {} THEN
  1142. Show("Serious error. Please restart the EHCI driver:");
  1143. IF SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsHcHalted # {} THEN
  1144. KernelLog.String(" [HC halted]");
  1145. SetState(UsbHcdi.Halted);
  1146. END;
  1147. KernelLog.Ln;
  1148. END;
  1149. IF s * StsFrameListRollover # {} THEN END;
  1150. IF s * StsPortChange # {} THEN
  1151. (* TODO: If wake-up, time 20ms, poll PscSuspend... enable HC if necessary*)
  1152. IF statusChangeHandler # NIL THEN statusChangeHandler(Usbdi.Ok, 0); END;
  1153. END;
  1154. IF s * (StsUsbError + StsUsbInterrupt) # {} THEN (* USB Interrupt occured: can be IOC or ShortPacketInt *)
  1155. NotifyCompletionHandlers;
  1156. END;
  1157. END;
  1158. END;
  1159. END InterruptHandler;
  1160. (* re-evaluate the status of the pipe's qh (endpoint descriptor) and its TD list *)
  1161. PROCEDURE UpdatePipeStatus*(pipe : UsbHcdi.Pipe);
  1162. CONST MaxLoops = 10000;
  1163. VAR
  1164. qtd : LONGINT;
  1165. s, errors : SET;
  1166. restLen, len : LONGINT;
  1167. loop : LONGINT;
  1168. BEGIN
  1169. FlushPCI;
  1170. (* First look up active bit in the QH tranfer overlay *)
  1171. Machine.InvalidateDCacheRange(pipe.qh, sizeQh);
  1172. s := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken));
  1173. IF s * TdActive # {} THEN (* The HC hasn't yet executed the transaction *) RETURN; END;
  1174. errors := UsbHcdi.NoErrors; loop := 0; restLen := 0; qtd := pipe.firstTD;
  1175. LOOP
  1176. AssertAlignment(qtd, alignQtd);
  1177. Machine.InvalidateDCacheRange(qtd, sizeQtd);
  1178. (* evaluate condition codes (CC)*)
  1179. s := SYSTEM.VAL(SET, SYSTEM.GET32(qtd + QtdToken)) * QtdStatus - TdPingState - TdSplitTransactionState;
  1180. (* TODO: NOTE: TdPingState would be used as error indicator for split transactions *)
  1181. IF s = {} THEN (* No errors occured *)
  1182. ELSIF s * TdActive # {} THEN (* qTD is still active, no errors so far *)
  1183. RETURN;
  1184. ELSE (* At least one error occured *)
  1185. IF s * TdHalted # {} THEN errors := errors + UsbHcdi.Stalled; END;
  1186. IF s * TdDataBufferError # {} THEN errors := errors + UsbHcdi.Databuffer; END;
  1187. IF s * TdBabbleDetected # {} THEN errors := errors + UsbHcdi.Babble; END;
  1188. IF s * TdTransactionError # {} THEN errors := errors + UsbHcdi.CrcTimeout; END;
  1189. IF s * TdMissedMicroFrame # {} THEN errors := errors + UsbHcdi.Internal; END;
  1190. EXIT;
  1191. END;
  1192. IF pipe.transferLen > 0 THEN (* Data had to be transfered... *)
  1193. (* The host controller decrements the Total Bytes To Transfer field according the amount of data it did
  1194. transfer. If this field has not the value zero, the host controller did not transfer all data. If there is no
  1195. error reported, this is a short packet condition, which can be okay. *)
  1196. (* len bytes should have been transfered for this TD *)
  1197. len := SYSTEM.VAL(LONGINT, LSH(SYSTEM.VAL(SET, SYSTEM.GET32(qtd + QtdToken)) * QtdBytesToTransfer, -16));
  1198. IF (len # 0) THEN (* Short packet *)
  1199. restLen := restLen + len;
  1200. END;
  1201. END;
  1202. IF qtd = pipe.lastTD THEN EXIT; END; (* End of qTD chain *)
  1203. qtd := qtd + sizeQtd;
  1204. INC(loop);
  1205. IF loop > MaxLoops THEN (* Endless loop protection *)
  1206. IF Debug.Level >= Debug.Errors THEN Show("UpdateStatus: Serious error occured."); KernelLog.Ln; END;
  1207. EXIT;
  1208. END;
  1209. END; (* end loop *)
  1210. pipe.errors := errors;
  1211. IF errors = UsbHcdi.NoErrors THEN
  1212. IF restLen = 0 THEN
  1213. pipe.actLen := pipe.transferLen;
  1214. pipe.status := Usbdi.Ok;
  1215. ELSE
  1216. pipe.actLen := pipe.transferLen - restLen;
  1217. pipe.status := Usbdi.ShortPacket;
  1218. pipe.errors := pipe.errors + UsbHcdi.ShortPacket;
  1219. END;
  1220. ELSE
  1221. pipe.actLen := pipe.transferLen - restLen;
  1222. IF errors * UsbHcdi.Stalled # {} THEN
  1223. pipe.status := Usbdi.Stalled;
  1224. ELSE
  1225. pipe.status := Usbdi.Error;
  1226. END;
  1227. END;
  1228. Machine.InvalidateDCacheRange(pipe.qh, sizeQh);
  1229. s := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken));
  1230. IF s * TdHalted # {} THEN
  1231. ClearHalt(pipe);
  1232. END;
  1233. IF (pipe.type = UsbHcdi.PipeBulk) OR (pipe.type = UsbHcdi.PipeInterrupt) THEN
  1234. (* If we received an ACK, do the toggle *)
  1235. IF (pipe.status = Usbdi.Ok) OR (pipe.status = Usbdi.ShortPacket) THEN
  1236. Machine.InvalidateDCacheRange(pipe.lastTD, sizeQtd);
  1237. s := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.lastTD + QtdToken));
  1238. IF s * QtdDataToggle # {} THEN pipe.dataToggle := TRUE; ELSE pipe.dataToggle := FALSE; END;
  1239. END;
  1240. END;
  1241. END UpdatePipeStatus;
  1242. (* Reset the host controller. Note: This will NOT assert reset on the USB downstream ports. *)
  1243. PROCEDURE HardwareReset * () : BOOLEAN;
  1244. CONST MaxWaits = 1000; (* Timeout in milliseconds the HC must have completed the reset command *)
  1245. VAR dword : SET; i : LONGINT;
  1246. BEGIN
  1247. (* Host software mustn't reset the host controller when it's running. Stop it and ... *)
  1248. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  1249. SYSTEM.PUT32(iobase + HcUsbCmd, dword - CmdRunStop); FlushPCI;
  1250. (* ... wait until the HC is halted.*)
  1251. i := 1; dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts));
  1252. WHILE (dword * StsHcHalted = {}) & (i <= MaxWaits) DO
  1253. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts));
  1254. INC(i); Wait(1);
  1255. END;
  1256. IF dword * StsHcHalted = {} THEN (* HC did not stop *) RETURN FALSE; END;
  1257. (* Do the actual reset operation *)
  1258. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  1259. SYSTEM.PUT32(iobase + HcUsbCmd, dword + CmdHcReset); FlushPCI;
  1260. (* The host controller should clear the HCRESET bit when it has finished resetting *)
  1261. FOR i := 1 TO MaxWaits DO
  1262. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  1263. IF dword * CmdHcReset = {} THEN
  1264. RETURN TRUE;
  1265. END;
  1266. Wait(1);
  1267. END;
  1268. RETURN FALSE;
  1269. END HardwareReset;
  1270. (* HC moves to UsbSuspend state und almost all operational registers are reset.
  1271. Does not affect the root hub and its downstream ports *)
  1272. PROCEDURE SoftwareReset*() : BOOLEAN;
  1273. BEGIN
  1274. (* TODO: Implement *)
  1275. RETURN FALSE;
  1276. END SoftwareReset;
  1277. (* Initialization of the data structures of the Host Controller Communication Area *)
  1278. PROCEDURE InitFrameList(): BOOLEAN;
  1279. VAR fstn, i, k, j, shift : LONGINT;
  1280. BEGIN
  1281. (* Host controller interface data structures should not cross page-boundaries... 32 byte alignment.
  1282. These queue heads are used as skeleton and never contain any qTDs. *)
  1283. qhlist := UsbHcdi.GetAlignedMemSpace(2048, 4096);
  1284. framelist := UsbHcdi.GetAlignedMemSpace(4096, 4096); (* Must be 4K aligned *)
  1285. (* Set up QHs. 11 Interrupt QHs and the isochronousQh + fstn *)
  1286. shift := sizeQh DIV 4; ASSERT(sizeQh MOD 4 = 0);
  1287. FOR i := 0 TO 12 DO
  1288. qhlist.data[qhlist.base + i*shift + (QhEpCapabilities1 DIV 4)] := 0;
  1289. qhlist.data[qhlist.base + i*shift + (QhEpCapabilities2 DIV 4)] := 0;
  1290. qhlist.data[qhlist.base + i*shift + (QhQtdToken DIV 4)] := 0;
  1291. qhlist.data[qhlist.base + i*shift + (QhCurrentQtdPointer DIV 4)] := 0;
  1292. qhlist.data[qhlist.base + i*shift + (QhNextQtdPointer DIV 4)] := 1; (* Pointer not valid *)
  1293. qhlist.data[qhlist.base + i*shift + (QhAltNextQtdPointer DIV 4)] := 1; (* Pointer not valid *)
  1294. FOR j := 0 TO 4 DO
  1295. qhlist.data[qhlist.base + i*shift + (QhBufferPointer0 DIV 4) + j] := 0;
  1296. END;
  1297. IF cap64bit THEN
  1298. FOR j := 0 TO 4 DO
  1299. qhlist.data[qhlist.base + i*shift + (QhExtBufferPointer0 DIV 4) + j] := 0;
  1300. END;
  1301. END;
  1302. END;
  1303. (* Addresses of queue heads *)
  1304. NEW(interruptQh);
  1305. FOR i := 0 TO 10 DO interruptQh[i] := Machine.Ensure32BitAddress (ADDRESSOF(qhlist.data[qhlist.base]) + i*sizeQh); END;
  1306. fstn := interruptQh[10] + sizeQh; (* Actually 8 bytes *)
  1307. isochronousQh := fstn + sizeQh;
  1308. FOR i := 10 TO 2 BY -1 DO
  1309. SYSTEM.PUT32(interruptQh[i] + QhHorizontalLinkPointer, interruptQh[i-1] + LSH(QhTypQh, 1));
  1310. END;
  1311. (* Link restore indicator. InterruptQh[1] points to FSTN points to InterruptQh[0] *)
  1312. SYSTEM.PUT32(interruptQh[1] + QhHorizontalLinkPointer, fstn + LSH(QhTypFstn, 1));
  1313. SYSTEM.PUT32(fstn + FstnNormalPathLinkPointer, interruptQh[0] + LSH(QhTypQh, 1));
  1314. SYSTEM.PUT32(fstn + FstnBackPathLinkPointer, QhTerminate); (* Indicates restore indicator *)
  1315. (* Interrupt Qh for 1ms points to isochronousQh *)
  1316. SYSTEM.PUT32(interruptQh[0] + QhHorizontalLinkPointer, isochronousQh + LSH(QhTypQh, 1));
  1317. SYSTEM.PUT32(isochronousQh + QhHorizontalLinkPointer, QhTerminate);
  1318. (* tree structure:
  1319. interrupt[0]: 1ms interrupt[1]: 2ms interrupt[2]: 4ms interrupt[3]: 8ms interrupt[4]: 16ms interrupt[5]: 32ms *)
  1320. (* => end of queue 10 points to 9, end of 8 points to 7 , ..., end of 1 points to 0 *)
  1321. (* => 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.*)
  1322. (* queue 0 executes 1024x, queue 1 executes 512x, queue 2 executes 256x, queue 3 executes 128x*)
  1323. (* queue 4 executes 64x, queue 5 executes 32x, queue 6 executes 16x, queue 7 executes 8x*)
  1324. (* queue 8 executes 4x, queue 9 executes 2x, queue 10 executes 1x *)
  1325. (* What does the following mean? => We count the 1's (starting at lsb) until we pass a zero *)
  1326. (* This count gives the queue number for a given slot *)
  1327. FOR i := 0 TO 1023 DO (* i is slot number, we want to calc the queue number (k) for this slot *)
  1328. k := 0; j := i;
  1329. LOOP
  1330. IF (SYSTEM.VAL(SET, j) * {0}) = {} THEN EXIT; END;
  1331. INC(k); j := j DIV 2;
  1332. END;
  1333. framelist.data[framelist.base + i] := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, interruptQh[k]) + {1});
  1334. Machine.FlushDCacheRange(ADDRESSOF(framelist.data[framelist.base]), 4096);
  1335. END;
  1336. RETURN TRUE;
  1337. END InitFrameList;
  1338. (* Initializes the host controller and builds up the data structures of the HCCA.
  1339. * @param iobase I/O base address (virtual, pointing to capability register at offset 0)
  1340. * @param int Interrupt Line
  1341. * @return TRUE if initialization succeeded, FALSE otherwise
  1342. *)
  1343. PROCEDURE Init * (iobase , irq : LONGINT) : BOOLEAN;
  1344. VAR
  1345. reg : LONGINT;
  1346. dword : SET; qword : HUGEINT;
  1347. ignore : BOOLEAN;
  1348. i : LONGINT;
  1349. BEGIN
  1350. IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbEhci: Starting host controller initialization..."); KernelLog.Ln; END;
  1351. SELF.iobase := iobase; SELF.irq := irq;
  1352. isHighSpeed := TRUE; DMAchaining := TRUE; sgListSize := ScatterGatherListSize;
  1353. (* Read in the Host Controller Capability Registers *)
  1354. (* Get and check EHCI revision *)
  1355. reg := SYSTEM.GET16(iobase + HcCapHciVersion);
  1356. IF reg # 0100H THEN
  1357. KernelLog.String("UsbEhci: Revision of EHCI Programming Interface not supported."); KernelLog.Ln;
  1358. RETURN FALSE;
  1359. END;
  1360. (* Get and parse the HC structural parameters register (HCSPARAM) *)
  1361. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcCapSparams));
  1362. capDebugPortNumber := SYSTEM.VAL(LONGINT, LSH(dword * {20..31}, -20));
  1363. IF dword * {16} # {} THEN capPortIndicators := TRUE; ELSE capPortIndicators := FALSE; END;
  1364. capNbrOfCompanionHc := SYSTEM.VAL(LONGINT, LSH(dword * {12..15}, -12));
  1365. capPortsPerCompanion := SYSTEM.VAL(LONGINT, LSH(dword * {8..11}, -8));
  1366. IF dword * {7} # {} THEN capPortRoutingRules := TRUE; ELSE capPortRoutingRules := FALSE; END;
  1367. IF dword * {4} # {} THEN capPortPowerControl := TRUE; ELSE capPortPowerControl := FALSE; END;
  1368. capNbrOfPorts := SYSTEM.VAL(LONGINT, dword * {0..3});
  1369. (* Get and parse the HC capability parameters register (HCCPARAM) *)
  1370. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcCapCparams));
  1371. capIsoSchedThreshold := SYSTEM.VAL(LONGINT, LSH(dword * {4..7}, -4));
  1372. IF dword * {2} # {} THEN capAsynchSchedPark := TRUE; ELSE capAsynchSchedPark := FALSE; END;
  1373. IF dword * {1} # {} THEN capProgrammableFLG := TRUE; ELSE capProgrammableFLG := FALSE; END;
  1374. IF dword * {0} # {} THEN cap64bit := TRUE; ELSE cap64bit := FALSE; END;
  1375. (* Get the EHCI Extended Capabilities Pointer (EECP) *)
  1376. eecp := SYSTEM.VAL(LONGINT, LSH(SYSTEM.VAL(SET, dword) * {8..15}, - 8));
  1377. (* Get HC companion port route description (60bits, 4bits per port *)
  1378. IF capPortRoutingRules THEN (* HC companion port route description available *)
  1379. qword := SYSTEM.GET32(iobase + HcCapPortroute);
  1380. qword := qword + LSH(SYSTEM.GET32(iobase + HcCapPortroute + 4), 32);
  1381. NEW(hcportroute, 16);
  1382. FOR i := 0 TO 15 DO
  1383. hcportroute[i] := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, LSH(qword, -i)) * {0..3});
  1384. END;
  1385. END;
  1386. (* Build the emulated hub descriptor *)
  1387. NEW(hubDescriptor, 8);
  1388. hubDescriptor[0] := CHR(7);
  1389. hubDescriptor[1] := CHR(29H); (* Hub Descriptor *)
  1390. hubDescriptor[2] := CHR(capNbrOfPorts);
  1391. IF capPortPowerControl THEN (* If power control is available, EHCI root hubs always provide per port power control *)
  1392. dword := dword + {0};
  1393. ELSE (* No power switching implemented *)
  1394. dword := dword + {1};
  1395. END;
  1396. dword := dword + {3}; (* EHCI root hubs always provide per port overcurrent protection *)
  1397. IF capPortIndicators THEN (* Port indicator control support available *) dword := dword + {7}; END;
  1398. hubDescriptor[3] := CHR(SYSTEM.VAL(LONGINT, dword));
  1399. hubDescriptor[4] := CHR(0); (* Reserved *)
  1400. hubDescriptor[5] := CHR(10); (* 20ms Power on to power good *)
  1401. hubDescriptor[6] := CHR(0); (* Root hubs don't draw current from the USB *)
  1402. (* The Host Controller Capability Registers are readonly so we don't need further access to them and set
  1403. iobase to the base of the Host Controller Operational Registers *)
  1404. iobase := iobase + SYSTEM.GET8(iobase + HcCapLength);
  1405. SELF.iobase := iobase;
  1406. (* Calculate offset from iobase of the port status/controll register for each port *)
  1407. portCount := capNbrOfPorts;
  1408. NEW(ports, portCount);
  1409. FOR i := 0 TO portCount - 1 DO ports[i] := iobase + HcPortSc + i*4; END;
  1410. IF ~HardwareReset() THEN RETURN FALSE; END;
  1411. (* Bluebottle does not yet support 64bit address space. Set the 4GB segment selector for the control data structures to zero. *)
  1412. SYSTEM.PUT32(iobase + HcCtrlDsSegment, 0);
  1413. (* Note that the control data structures must finally be 32byte aligned. Since they occupy subsequent memory location when
  1414. associated with pipes, the value are rounded up to the next value for which value MOD 32 = 0 holds. *)
  1415. IF cap64bit THEN
  1416. sizeQh := 96; (* Actually: 68 Bytes *)
  1417. sizeQtd := 64; (* Actually: 52 Bytes *)
  1418. alignQh := 128;
  1419. alignQtd := 64;
  1420. ELSE
  1421. sizeQh := 64; (* Actually: 48 Byte *)
  1422. sizeQtd := 32;
  1423. alignQh := 64;
  1424. alignQtd := 32;
  1425. END;
  1426. IF ~InitFrameList() THEN
  1427. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: Initialization of HCCA failed."); KernelLog.Ln; END;
  1428. RETURN FALSE;
  1429. END;
  1430. (* If the Asynchronous Schedule Park Mode is not available or not enabled, the host controller must not
  1431. * execute more than one bus transaction per queue head, per traversal of the asynchronous schedule. If it
  1432. * is enabled, the host controller may execute Asynchronous Schedule Park Mode Count transaction if the
  1433. * endpoint belongs to a high-speed device. Results in better bus utilization. *)
  1434. IF capAsynchSchedPark THEN
  1435. ASSERT((HcAsyncParkModeCount >= 0) & (HcAsyncParkModeCount < 4));
  1436. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  1437. IF HcAsyncParkModeCount = 0 THEN (* Disable Asynchronous Schedule Park Mode *)
  1438. dword := dword - CmdAsyncSchedParkMode;
  1439. ELSE (* Enable Asynchronous Schedule Park Mode and set its count field in USBCMD *)
  1440. dword := dword + LSH(SYSTEM.VAL(SET, HcAsyncParkModeCount), 8) * CmdAsyncSchedParkCount;
  1441. dword := dword + CmdAsyncSchedParkMode;
  1442. END;
  1443. SYSTEM.PUT32(iobase + HcUsbCmd, dword);
  1444. END;
  1445. dword := {};
  1446. IF capProgrammableFLG THEN (* Size of frame list can be programmed... use constant value *)
  1447. IF Debug.Trace & Debug.traceInit THEN
  1448. KernelLog.String("UsbEhci: Set frame list size to "); KernelLog.Int(HcFrameListSize, 0);
  1449. KernelLog.String(" elements."); KernelLog.Ln;
  1450. END;
  1451. (* TODO: Programm it *)
  1452. END;
  1453. (* Set interrupt threshold *)
  1454. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  1455. dword := dword - {16 .. 23};
  1456. IF ((HcInterruptThreshold#01H) & (HcInterruptThreshold#02H) & (HcInterruptThreshold#04H) & (HcInterruptThreshold#08H)
  1457. & (HcInterruptThreshold#10H) & (HcInterruptThreshold#20H) & (HcInterruptThreshold#40H)) THEN
  1458. (* Wrong parameter value... use default *)
  1459. IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbEhci: Interrupt Threshold value invalid... using default setting."); KernelLog.Ln; END;
  1460. dword := dword + SYSTEM.VAL(SET, LSH(08H, 16)) * {16..23};
  1461. ELSE
  1462. dword := dword + SYSTEM.VAL(SET, LSH(HcInterruptThreshold, 16)) * {16..23};
  1463. END;
  1464. SYSTEM.PUT32(iobase + HcUsbCmd, dword); FlushPCI;
  1465. (* Try to start the host controller *)
  1466. IF Start() = FALSE THEN (* ERROR: Couldn't start the host controller. Controller was probably not correctly initialized. *)
  1467. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: Couldn't start host controller."); KernelLog.Ln; END;
  1468. ignore := HardwareReset();
  1469. RETURN FALSE;
  1470. END;
  1471. RETURN TRUE;
  1472. END Init;
  1473. (* PCI writes may be posted. A read forces posted writes to be flushed before the read transaction is proceeded. *)
  1474. PROCEDURE FlushPCI *;
  1475. VAR ignore : LONGINT;
  1476. BEGIN
  1477. ignore := SYSTEM.GET32(iobase + HcUsbSts);
  1478. END FlushPCI;
  1479. (* Release the HC ownership semaphore; eecp is the EHCI Extended Capability Pointer *)
  1480. PROCEDURE ReleaseHcOwnerShip(bus, device, function, eecp : LONGINT);
  1481. END ReleaseHcOwnerShip;
  1482. (**
  1483. * Inits the ULPI PHY of the controller.
  1484. *)
  1485. PROCEDURE InitUlpi(): BOOLEAN;
  1486. BEGIN
  1487. RETURN TRUE
  1488. END InitUlpi;
  1489. (*
  1490. * Start the host controller.
  1491. * This will:
  1492. * - enable interrupts for the host controller and install a interrupt handler
  1493. * - set the addresses for the periodic and asynchronous lists
  1494. * - turn the host controller on
  1495. * - route all ports to the EHCI controller
  1496. * - power on all ports of the root hub
  1497. *)
  1498. PROCEDURE Start * ():BOOLEAN;
  1499. VAR dword : SET;
  1500. TYPE
  1501. IRQPoller = OBJECT (* support polling i/o IRQ -- for testing *)
  1502. VAR handler: PROCEDURE {DELEGATE}; timer: Kernel.Timer;
  1503. PROCEDURE & Init* (h: PROCEDURE {DELEGATE});
  1504. BEGIN
  1505. handler := h;
  1506. NEW(timer);
  1507. END Init;
  1508. BEGIN{ACTIVE}
  1509. LOOP
  1510. handler();
  1511. timer.Sleep(1);
  1512. END;
  1513. END IRQPoller;
  1514. VAR is: IRQPoller;
  1515. BEGIN
  1516. IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbEhci: Starting host controller... "); KernelLog.Ln; END;
  1517. (* Enable Interrupts *)
  1518. SetState(UsbHcdi.Initialized);
  1519. IF Polling THEN
  1520. NEW(is, InterruptHandler)
  1521. ELSE
  1522. Objects.InstallHandler(InterruptHandler, Machine.IRQ0+irq);
  1523. END;
  1524. (* Clear interrupts *)
  1525. SYSTEM.PUT32(iobase + HcUsbSts, 0);
  1526. (* Enable all interrupts except the frame list rollover interrupt *)
  1527. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbIntr));
  1528. interruptsEnabled := dword + {0..5} - StsFrameListRollover;
  1529. SYSTEM.PUT32(iobase + HcUsbIntr, interruptsEnabled);
  1530. (* Set Addresses for queue heads *)
  1531. SYSTEM.PUT32(iobase + HcPeriodicListBase, ADDRESSOF(framelist.data[framelist.base]));
  1532. SYSTEM.PUT32(iobase + HcAsyncListAddr, 0); (* Invalid address -> list empty *)
  1533. (* Start controller *)
  1534. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  1535. dword := dword + CmdRunStop;
  1536. SYSTEM.PUT32(iobase + HcUsbCmd, dword); FlushPCI;
  1537. SetState(UsbHcdi.Operational);
  1538. (* Route all ports to this EHCI host controller *)
  1539. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcConfigFlag));
  1540. dword := dword + {0};
  1541. SYSTEM.PUT32(iobase + HcConfigFlag, dword); FlushPCI;
  1542. RETURN InitUlpi();
  1543. END Start;
  1544. PROCEDURE &Default*(bus, device, function : LONGINT);
  1545. BEGIN
  1546. Default^(bus, device, function); (* The high-speed default pipe uses 64byte maxPacketSize0 *)
  1547. pipes[0, 0, 0].maxPacketSize := 64;
  1548. END Default;
  1549. PROCEDURE Cleanup;
  1550. BEGIN
  1551. IF state >= UsbHcdi.Initialized THEN Objects.RemoveHandler(InterruptHandler, Machine.IRQ0 + irq); END;
  1552. Cleanup^;
  1553. IF ~HardwareReset() THEN
  1554. IF Debug.Level >= Debug.Errors THEN Show("Host controller reset failed."); KernelLog.Ln; END;
  1555. END;
  1556. (* Release ownership of host controller *)
  1557. ReleaseHcOwnerShip(bus, device, function, eecp);
  1558. (* Unmap the HC's operational registers *)
  1559. Machine.UnmapPhysical(iobase, 4096);
  1560. END Cleanup;
  1561. (** Displays the host controller's data struture on KernelLog *)
  1562. PROCEDURE ShowSchedule*;
  1563. CONST MaxIterations =21;
  1564. VAR dword : SET; first, cur : LONGINT; i, ms : LONGINT;
  1565. BEGIN
  1566. IF Debug.Trace THEN
  1567. KernelLog.String("Host Controller Data Structures for ");
  1568. KernelLog.String(name);
  1569. KernelLog.String(" ("); KernelLog.String(desc); KernelLog.String("):"); KernelLog.Ln;
  1570. KernelLog.String("Periodic Schedule: "); KernelLog.Ln;
  1571. IF SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsPeriodicSchedule = {} THEN
  1572. KernelLog.String("Periodic schedule is disabled."); KernelLog.Ln;
  1573. END;
  1574. KernelLog.String("*** Isochronous schedule: "); KernelLog.Ln;
  1575. ShowQueueHead(isochronousQh, 0, cap64bit);
  1576. ms := 1;
  1577. FOR i := 0 TO 10 DO
  1578. KernelLog.String("*** "); KernelLog.Int(ms, 0); KernelLog.String(" ms schedule:"); KernelLog.Ln;
  1579. ShowQueueHead(interruptQh[i], 0, cap64bit);
  1580. dword := SYSTEM.VAL(SET, SYSTEM.GET32(interruptQh[i] + QhHorizontalLinkPointer));
  1581. cur := 0;
  1582. LOOP
  1583. IF dword * QhTerminate # {} THEN EXIT; END;
  1584. IF cur > MaxIterations THEN
  1585. KernelLog.String("Maximum allowed iterations reached."); KernelLog.Ln;
  1586. EXIT;
  1587. END;
  1588. INC(cur);
  1589. IF i > 0 THEN
  1590. IF SYSTEM.VAL(LONGINT, dword * {5..31}) = interruptQh[i-1] THEN
  1591. dword := dword + QhTerminate;
  1592. ELSE
  1593. dword := dword * {5..31};
  1594. IF SYSTEM.VAL(SET, SYSTEM.GET32(SYSTEM.VAL(LONGINT, dword) + QhNextQtdPointer)) * QhTerminate # {} THEN
  1595. ShowQueueHead(SYSTEM.VAL(LONGINT, dword), 0, cap64bit);
  1596. ELSE
  1597. ShowQueueHead(SYSTEM.VAL(LONGINT, dword), SYSTEM.GET32(SYSTEM.VAL(LONGINT, dword) + QhNextQtdPointer), cap64bit);
  1598. END;
  1599. dword := SYSTEM.VAL(SET, SYSTEM.GET32(SYSTEM.VAL(LONGINT, dword) + QhHorizontalLinkPointer));
  1600. END;
  1601. ELSIF SYSTEM.VAL(LONGINT, dword * {5..31}) = isochronousQh THEN
  1602. dword := dword + QhTerminate;
  1603. ELSE
  1604. dword := dword * {5..31};
  1605. IF SYSTEM.VAL(SET, SYSTEM.GET32(SYSTEM.VAL(LONGINT, dword) + QhNextQtdPointer)) * QhTerminate # {} THEN
  1606. ShowQueueHead(SYSTEM.VAL(LONGINT, dword), 0, cap64bit);
  1607. ELSE
  1608. ShowQueueHead(SYSTEM.VAL(LONGINT, dword), SYSTEM.GET32(SYSTEM.VAL(LONGINT, dword) + QhNextQtdPointer), cap64bit);
  1609. END;
  1610. dword := SYSTEM.VAL(SET, SYSTEM.GET32(SYSTEM.VAL(LONGINT, dword) + QhHorizontalLinkPointer));
  1611. END;
  1612. END;
  1613. ms := ms * 2;
  1614. END;
  1615. KernelLog.String("*** Asynchronous list: "); KernelLog.Ln;
  1616. IF SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsAsyncSchedule # {} THEN
  1617. first := SYSTEM.GET32(iobase + HcAsyncListAddr);
  1618. IF (SYSTEM.VAL(SET, first) * {0..4} = {}) & (first # 0) THEN
  1619. first := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, first) * {5..31});
  1620. ShowQueueHead(first, 0, cap64bit);
  1621. i := 0; cur := first;
  1622. LOOP
  1623. cur := SYSTEM.GET32(cur + QhHorizontalLinkPointer);
  1624. IF (SYSTEM.VAL(SET, cur) * {2..4} # {}) OR (SYSTEM.VAL(SET, cur) * {1} = {}) THEN
  1625. KernelLog.String("Error: Queue head horizontal link pointer is invalid."); KernelLog.Ln;
  1626. EXIT;
  1627. END;
  1628. cur := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, cur) * {5..31});
  1629. IF (cur = first) OR (i >= MaxIterations) THEN EXIT END;
  1630. ShowQueueHead(cur, 0, cap64bit);
  1631. INC(i);
  1632. END;
  1633. IF i >= MaxIterations THEN
  1634. KernelLog.String("MaxIterations reached. Aborting..."); KernelLog.Ln;
  1635. END;
  1636. ELSE KernelLog.String("Error: Asynchronous Schedule List address is invalid.");
  1637. END;
  1638. ELSE KernelLog.String("Asynchronous Schedule is disabled.");
  1639. END;
  1640. KernelLog.Ln;
  1641. END;
  1642. END ShowSchedule;
  1643. PROCEDURE ShowPipe*(pipe : UsbHcdi.Pipe);
  1644. BEGIN
  1645. ShowQueueHead(pipe.qh, pipe.firstTD, cap64bit);
  1646. END ShowPipe;
  1647. (* Show some information on this host controller on KernelLog *)
  1648. PROCEDURE Diag;
  1649. VAR dword : SET;
  1650. BEGIN
  1651. IF Debug.Trace THEN
  1652. Diag^;
  1653. (* Host Controller structural capabilities *)
  1654. KernelLog.String(" HC Structural Parameters: "); KernelLog.Ln;
  1655. KernelLog.String(" Nbr of ports: "); KernelLog.Int(capNbrOfPorts, 0);
  1656. KernelLog.String(", Debug port: ");
  1657. IF capDebugPortNumber # 0 THEN KernelLog.Int(capDebugPortNumber, 0); ELSE KernelLog.String("n/a"); END;
  1658. KernelLog.Ln;
  1659. KernelLog.String(" Per port power control: ");
  1660. IF capPortPowerControl THEN KernelLog.String("Yes"); ELSE KernelLog.String("No"); END;
  1661. KernelLog.String(", Port indicator control: ");
  1662. IF capPortIndicators THEN KernelLog.String("Available"); ELSE KernelLog.String("n/a"); END;
  1663. KernelLog.Ln;
  1664. KernelLog.String(" Nbr of companion HCs: "); KernelLog.Int(capNbrOfCompanionHc, 0);
  1665. KernelLog.String(", Ports per companion: "); KernelLog.Int(capPortsPerCompanion, 0);
  1666. KernelLog.String(", Port routing rules: ");
  1667. IF capPortRoutingRules THEN KernelLog.String("Available"); ELSE KernelLog.String("n/a"); END;
  1668. KernelLog.Ln;
  1669. (* Host Controller capability parameters *)
  1670. KernelLog.String(" HC Capablilty Parameters:"); KernelLog.Ln;
  1671. KernelLog.String(" 64bit Data Structures: "); IF cap64bit THEN KernelLog.String("Yes"); ELSE KernelLog.String("No"); END;
  1672. KernelLog.String(", Async Schedule Park Mode support: "); IF capAsynchSchedPark THEN KernelLog.String("Yes"); ELSE KernelLog.String("No"); END;
  1673. KernelLog.Ln;
  1674. KernelLog.String(" Programmable Frame List Size: "); IF capAsynchSchedPark THEN KernelLog.String("Yes"); ELSE KernelLog.String("No"); END;
  1675. KernelLog.String(", Isochronous Scheduling Threshold: "); KernelLog.Int(capIsoSchedThreshold, 0);
  1676. KernelLog.Ln;
  1677. (* Host Controller Command Register *)
  1678. KernelLog.String(" HC Command Register: "); KernelLog.Ln;
  1679. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  1680. KernelLog.String(" Interrupt Threshold: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * {16..23}, -16)), 0);
  1681. KernelLog.String(", Async Schedule Park Mode: ");
  1682. IF dword * {11} # {} THEN
  1683. KernelLog.String("Enabled ("); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * {8..9}, -8)), 0); KernelLog.Char(")");
  1684. ELSE
  1685. KernelLog.String("Disabled");
  1686. END;
  1687. KernelLog.String(", Frame List Size: ");
  1688. CASE SYSTEM.VAL(LONGINT, LSH(dword * {2..3}, -2)) OF
  1689. 0: KernelLog.String("1024");
  1690. |1: KernelLog.String("512");
  1691. |2: KernelLog.String("256");
  1692. |3: KernelLog.String("Reserved");
  1693. END;
  1694. KernelLog.Ln;
  1695. (* Host Controller Status information *)
  1696. KernelLog.String(" HC Status Register:"); KernelLog.Ln;
  1697. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts));
  1698. KernelLog.String(" Asynchronous Schedule: ");
  1699. IF dword * StsAsyncSchedule # {} THEN KernelLog.String("Enabled"); ELSE KernelLog.String("Disabled"); END;
  1700. KernelLog.String(", Periodic Schedule: ");
  1701. IF dword * StsPeriodicSchedule # {} THEN KernelLog.String("Enabled"); ELSE KernelLog.String("Disabled"); END;
  1702. KernelLog.String(" ");
  1703. IF dword * StsReclamation # {} THEN KernelLog.String("[Reclamation]"); END;
  1704. IF dword * StsHcHalted # {} THEN KernelLog.String("[HcHalted]"); END;
  1705. KernelLog.Ln;
  1706. KernelLog.String(" Interrupt Status: "); ShowInterrupts(dword); KernelLog.Ln;
  1707. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbIntr));
  1708. KernelLog.String(" Interrupts Enabled: "); ShowInterrupts(dword); KernelLog.Ln;
  1709. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
  1710. KernelLog.String(" HC operation: ");
  1711. IF dword * {0} # {} THEN KernelLog.String("Running"); ELSE KernelLog.String("Stopped"); END;
  1712. KernelLog.Ln;
  1713. END;
  1714. END Diag;
  1715. PROCEDURE Show * (txt : ARRAY OF CHAR);
  1716. BEGIN
  1717. KernelLog.String("UsbEhci: "); KernelLog.String(name); KernelLog.String(" ("); KernelLog.String(desc); KernelLog.String("): ");
  1718. KernelLog.String(txt);
  1719. END Show;
  1720. END EnhancedHostController;
  1721. PROCEDURE AssertAlignment(desc: ADDRESS; alignment: SIZE);
  1722. BEGIN
  1723. ASSERT(desc MOD alignment = 0);
  1724. END AssertAlignment;
  1725. PROCEDURE Indent(spaces : LONGINT);
  1726. VAR i : LONGINT;
  1727. BEGIN
  1728. FOR i := 1 TO spaces DO KernelLog.Char(" "); END;
  1729. END Indent;
  1730. (*
  1731. * Display a textual representation of a queue head data structure and its associated qTD.
  1732. * @param qh Virtual memory address of queue head
  1733. * @param firstQtd First qTD of this queue. If 0, the qTD chain will not be shown
  1734. *)
  1735. PROCEDURE ShowQueueHead(qh, firstQtd : LONGINT; cap64bit : BOOLEAN);
  1736. CONST MaxChainLen = 32;
  1737. VAR
  1738. dword : SET;
  1739. val, chainlen : LONGINT;
  1740. PROCEDURE ShowQhTyp(qh : LONGINT);
  1741. BEGIN
  1742. IF Debug.Trace THEN
  1743. val := SYSTEM.VAL(LONGINT, LSH(SYSTEM.VAL(SET, qh) * QhTyp, -1));
  1744. IF val = 0 THEN KernelLog.String("Isochronous Tranfers Desriptor");
  1745. ELSIF val = 1 THEN KernelLog.String("Queue Head");
  1746. ELSIF val = 2 THEN KernelLog.String("Split Transaction Isochronous Transfer Descriptor");
  1747. ELSIF val = 3 THEN KernelLog.String("Frame Span Traversal Node");
  1748. END;
  1749. END;
  1750. END ShowQhTyp;
  1751. BEGIN
  1752. IF Debug.Trace THEN
  1753. KernelLog.String("EHCI data structure at "); KernelLog.Hex(qh, 8); KernelLog.String(": ");
  1754. ShowQhTyp(qh); KernelLog.String(" ");
  1755. IF qh = 0 THEN KernelLog.String("Error: QH pointer = 0"); KernelLog.Ln;RETURN;
  1756. ELSIF SYSTEM.VAL(SET, qh) * {0..4} # {} THEN KernelLog.String("Error: Not aligned"); KernelLog.Ln; RETURN;
  1757. END;
  1758. KernelLog.Ln;
  1759. KernelLog.String(" Endpoint Capabilities 1: ");
  1760. dword := SYSTEM.VAL(SET, SYSTEM.GET32(qh + QhEpCapabilities1));
  1761. KernelLog.String(" DeviceAddr: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * QhDeviceAddress), 0);
  1762. KernelLog.String(", Endpoint: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QhEndpointNbr, -8)), 0);
  1763. KernelLog.String(", Speed: "); val := SYSTEM.VAL(LONGINT, LSH(dword * QhEndpointSpeed, -12));
  1764. IF val = 0 THEN KernelLog.String("FullSpeed");
  1765. ELSIF val = 1 THEN KernelLog.String("LowSpeed");
  1766. ELSIF val = 2 THEN KernelLog.String("HighSpeed");
  1767. ELSE KernelLog.String("ERROR: Not set correctly");
  1768. END;
  1769. KernelLog.String(", MaxPacketSize: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QhMaxPacketLen, -16)), 0);
  1770. KernelLog.String(", NakRL: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QhNakCountReload, -28)), 0);
  1771. KernelLog.String(", Flags: ");
  1772. IF dword * QhControlEndpointFlag # {} THEN KernelLog.String("[ControlEp]"); END;
  1773. IF dword * QhDataToggleControl # {} THEN KernelLog.String("[DataToggleControl]"); END;
  1774. IF dword * QhHeadOfReclamation # {} THEN KernelLog.String("[Head]"); END;
  1775. IF dword * QhInactivate # {} THEN KernelLog.String("[Inactivate]"); END;
  1776. KernelLog.Ln;
  1777. KernelLog.String(" Endpoint Capabilities 2: ");
  1778. dword := SYSTEM.VAL(SET, SYSTEM.GET32(qh + QhEpCapabilities2));
  1779. KernelLog.String("Mult: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QhMultiplier, -30)), 0);
  1780. KernelLog.String(", HubAddr: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QhHubAddr, -16)), 0);
  1781. KernelLog.String(", HubPort: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QhPortNbr, -23)), 0);
  1782. KernelLog.String(", SplitCMask: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QhSplitCMask, -8)), 0);
  1783. KernelLog.String(", QhSMask: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * QhSMask), 0);
  1784. KernelLog.Ln;
  1785. KernelLog.String(" Queue Head Horizontal Link Pointer: ");
  1786. dword := SYSTEM.VAL(SET, SYSTEM.GET32(qh + QhHorizontalLinkPointer));
  1787. IF dword * QhTerminate # {} THEN
  1788. KernelLog.String("Invalid ("); KernelLog.Hex(SYSTEM.VAL(LONGINT, dword), 8); KernelLog.String("H)");
  1789. ELSE
  1790. KernelLog.Hex(SYSTEM.VAL(LONGINT, dword * {5..31}), 8);
  1791. KernelLog.String(" ("); ShowQhTyp(SYSTEM.VAL(LONGINT, dword)); KernelLog.String(")");
  1792. END;
  1793. KernelLog.Ln;
  1794. dword := SYSTEM.VAL(SET, SYSTEM.GET32(qh + QhCurrentQtdPointer));
  1795. KernelLog.String(" Current qTD Pointer: "); KernelLog.Hex(SYSTEM.VAL(LONGINT, dword), 8);
  1796. KernelLog.String(", Next qTD Pointer: "); KernelLog.Hex(SYSTEM.GET32(qh + QhNextQtdPointer), 8);
  1797. KernelLog.Ln;
  1798. KernelLog.String(" Transfer overlay: "); KernelLog.Ln;
  1799. ShowQtd(qh+16, 8, cap64bit, TRUE); KernelLog.Ln;
  1800. IF firstQtd # 0 THEN (* show qTD chain *)
  1801. KernelLog.String(" qTD chain:"); KernelLog.Ln;
  1802. IF SYSTEM.VAL(SET, firstQtd) * {0..4} # {} THEN
  1803. KernelLog.String(" qTD Pointer not 32byte aligned!"); KernelLog.Ln;
  1804. ELSE
  1805. chainlen := 0;
  1806. WHILE(SYSTEM.VAL(SET, firstQtd) * QhTerminate = {}) & (chainlen < MaxChainLen) DO
  1807. INC(chainlen);
  1808. ShowQtd(firstQtd, 8, cap64bit, FALSE); KernelLog.Ln;
  1809. (* Get next qTD *)
  1810. dword := SYSTEM.VAL(SET, SYSTEM.GET32(firstQtd + QtdNextQtdPointer));
  1811. IF dword * {1..4} # {} THEN
  1812. KernelLog.String(" Alignment error!"); KernelLog.Ln;
  1813. chainlen := MaxChainLen; (* abort *)
  1814. ELSIF dword * QhTerminate # {} THEN
  1815. KernelLog.String(" End of Chain"); KernelLog.Ln;
  1816. chainlen := MaxChainLen; (* abort *)
  1817. ELSE
  1818. firstQtd := SYSTEM.VAL(LONGINT, dword * {5..31});
  1819. END;
  1820. END;
  1821. END;
  1822. END;
  1823. END;
  1824. END ShowQueueHead;
  1825. PROCEDURE ShowQtd(qtd, spaces : LONGINT; cap64bit, overlay : BOOLEAN);
  1826. VAR i, val : LONGINT; dword : SET;
  1827. BEGIN
  1828. IF Debug.Trace THEN
  1829. Indent(spaces);
  1830. KernelLog.String("qTD at "); KernelLog.Hex(qtd, 8); KernelLog.String(": ");
  1831. IF SYSTEM.VAL(SET, qtd) * {0..3} # {} THEN
  1832. (* Regular qTDs are 32byte aligned. We allow 16byte alignment for the transfer overlay area *)
  1833. KernelLog.String("Not 16byte aligned... aborting."); KernelLog.Ln; RETURN;
  1834. ELSIF qtd = 0 THEN
  1835. KernelLog.String("Address = 0?"); KernelLog.Ln; RETURN;
  1836. END;
  1837. KernelLog.Ln;
  1838. Indent(spaces+ 4);
  1839. KernelLog.String("qTD Token: ");
  1840. dword := SYSTEM.VAL(SET, SYSTEM.GET32(qtd + QtdToken));
  1841. KernelLog.String("Pid: ");
  1842. val := SYSTEM.VAL(LONGINT, LSH(dword * QtdPidCode, -8));
  1843. IF val = PidSetup THEN KernelLog.String("SETUP");
  1844. ELSIF val = PidIn THEN KernelLog.String("IN");
  1845. ELSIF val = PidOut THEN KernelLog.String("OUT");
  1846. ELSE KernelLog.String("PID ERROR!");
  1847. END;
  1848. KernelLog.String(", DataToggle: ");
  1849. IF dword * QtdDataToggle # {} THEN KernelLog.String("DATA1"); ELSE KernelLog.String("DATA0"); END;
  1850. KernelLog.String(", Bytes to transfer: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QtdBytesToTransfer, -16)), 0);
  1851. KernelLog.String(", IOC: "); IF dword * QtdIoc # {} THEN KernelLog.String("Yes"); ELSE KernelLog.String("No"); END;
  1852. KernelLog.String(", CERR: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QtdErrorCounter, -10)) ,0);
  1853. KernelLog.Ln;
  1854. Indent(spaces + 4);
  1855. KernelLog.String("qTD Token Status: ");
  1856. dword := dword * QtdStatus;
  1857. IF dword * TdActive # {} THEN KernelLog.String("[Active]"); END;
  1858. IF dword * TdHalted # {} THEN KernelLog.String("[Halted]"); END;
  1859. IF dword * TdDataBufferError # {} THEN KernelLog.String("[DataBufferError]"); END;
  1860. IF dword * TdBabbleDetected # {} THEN KernelLog.String("[BabbleDetected]"); END;
  1861. IF dword * TdTransactionError # {} THEN KernelLog.String("[TransactionError]"); END;
  1862. IF dword * TdMissedMicroFrame # {} THEN KernelLog.String("[MissedMicroFrame]"); END;
  1863. IF dword * TdSplitTransactionState # {} THEN KernelLog.String("[SplitTransactionState]"); END;
  1864. IF dword * TdPingState # {} THEN KernelLog.String("[PingState]"); END;
  1865. KernelLog.Ln;
  1866. Indent(spaces + 4); KernelLog.String("Buffer information:");KernelLog.Ln;
  1867. Indent(spaces + 8); KernelLog.String("Current Buffer: ");
  1868. KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QtdCurrentPage, -12)), 0);
  1869. IF SYSTEM.VAL(SET, qtd) * {4} # {} THEN (* Should be transfer overlay since not 32byte aligned *)
  1870. KernelLog.String(", Nak counter: ");
  1871. KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(SYSTEM.VAL(SET, SYSTEM.GET32(qtd + QtdAltNextQtdPointer)) * {1..3}, -1)), 0);
  1872. END;
  1873. KernelLog.Ln;
  1874. FOR i := 0 TO 4 DO
  1875. val := SYSTEM.GET32(qtd + QtdBufferPtr0 + i*4);
  1876. Indent(spaces + 8);
  1877. KernelLog.String("Buffer Pointer "); KernelLog.Int(i, 0); KernelLog.String(": "); KernelLog.Hex(SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, val) * {12..31}), 8);
  1878. val := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, val) * {0..11});
  1879. IF i = 0 THEN
  1880. KernelLog.String(" Current Offset: "); KernelLog.Hex(val, 8);
  1881. ELSIF overlay & (i = 1) THEN
  1882. KernelLog.String(" C-prog-mask: "); KernelLog.Hex(val, 8);
  1883. ELSIF overlay & (i = 2) THEN
  1884. KernelLog.String(" S-Bytes / Frametag: "); KernelLog.Hex(val, 8);
  1885. END;
  1886. KernelLog.Ln;
  1887. END;
  1888. IF cap64bit THEN
  1889. FOR i := 0 TO 4 DO
  1890. val := SYSTEM.GET32(qtd + QtdExtBufferPtr0 + i*4);
  1891. Indent(spaces + 8);
  1892. KernelLog.String(" ExtBufferPointer"); KernelLog.Int(i, 0); KernelLog.String(": "); KernelLog.Hex(val, 8); KernelLog.Ln;
  1893. END;
  1894. END;
  1895. Indent(spaces + 4); KernelLog.String("Alternate Next qTD Pointer: ");
  1896. dword := SYSTEM.VAL(SET, SYSTEM.GET32(qtd + QtdAltNextQtdPointer));
  1897. IF dword * QhTerminate # {} THEN
  1898. KernelLog.String("Invalid ("); KernelLog.Hex(SYSTEM.VAL(LONGINT, dword), 8); KernelLog.String(")");
  1899. ELSIF dword * {1..3} # {} THEN
  1900. KernelLog.String("Alignment Error ("); KernelLog.Hex(SYSTEM.VAL(LONGINT, dword), 8); KernelLog.String(")");
  1901. ELSE
  1902. KernelLog.Hex(SYSTEM.VAL(LONGINT, dword), 8);
  1903. END;
  1904. KernelLog.Ln;
  1905. Indent(spaces + 4);
  1906. KernelLog.String("Next qTD Pointer: ");
  1907. dword := SYSTEM.VAL(SET, SYSTEM.GET32(qtd + QtdNextQtdPointer));
  1908. IF dword * QhTerminate # {} THEN
  1909. KernelLog.String("Invalid ("); KernelLog.Hex(SYSTEM.VAL(LONGINT, dword), 8); KernelLog.String(")");
  1910. qtd := 0;
  1911. ELSIF dword * {1..3} # {} THEN
  1912. KernelLog.String("Alignment Error ("); KernelLog.Hex(SYSTEM.VAL(LONGINT, dword), 8); KernelLog.String(")");
  1913. qtd := 0;
  1914. ELSE
  1915. KernelLog.Hex(SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, dword) * {5..31}), 8);
  1916. qtd := SYSTEM.VAL(LONGINT, dword * {5..31});
  1917. END;
  1918. KernelLog.Ln;
  1919. END;
  1920. END ShowQtd;
  1921. (*
  1922. PROCEDURE ShowItd(adr : LONGINT);
  1923. VAR dword : SET; i : LONGINT;
  1924. BEGIN
  1925. IF Debug.Trace THEN
  1926. KernelLog.String("UsbEhci: ITD at address "); KernelLog.Hex(adr, 8); KernelLog.Ln;
  1927. Indent(4);
  1928. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr));
  1929. KernelLog.String("Next Link Pointer: "); KernelLog.Hex(SYSTEM.VAL(LONGINT, dword * {5..31}), 8);
  1930. IF dword * {0} = {} THEN KernelLog.String(" [VALID]"); ELSE KernelLog.String("[INVALID]"); END;
  1931. KernelLog.String(", Typ: ");
  1932. CASE SYSTEM.VAL(LONGINT, LSH(dword * {1..2}, -1)) OF
  1933. 0: KernelLog.String("iTD");
  1934. |1 : KernelLog.String("QH");
  1935. |2 : KernelLog.String("siTD");
  1936. |3 : KernelLog.String("FSTN");
  1937. END;
  1938. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + 24H));
  1939. KernelLog.String(", Device Address: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {0..6}), 0);
  1940. KernelLog.String(", Endpoint: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * {8..11}, -7)), 0);
  1941. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + 28H));
  1942. KernelLog.String(" MaxPacket: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {0..10}), 0);
  1943. IF dword * {11} # {} THEN KernelLog.String(" [IN]"); ELSE KernelLog.String(" [OUT]"); END;
  1944. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + 2CH));
  1945. KernelLog.String(", MULT: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {0..1}), 0);
  1946. KernelLog.Ln;
  1947. FOR i := 0 TO 7 DO
  1948. Indent(4);
  1949. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + (i+1) * 4));
  1950. KernelLog.String("Transaction "); KernelLog.Int(i, 0); KernelLog.String(": ");
  1951. KernelLog.String("Status: ");
  1952. IF dword * ItdStatus # {} THEN
  1953. IF dword * ItdActive # {} THEN KernelLog.String("[ACTIVE]"); END;
  1954. IF dword * ItdDataBufferError # {} THEN KernelLog.String("[DataBufferError]"); END;
  1955. IF dword * ItdBabbleDetected # {} THEN KernelLog.String("[Babble]"); END;
  1956. IF dword * ItdTransactionError # {} THEN KernelLog.String("[TransactionError]"); END;
  1957. ELSE
  1958. KernelLog.String("[Done]");
  1959. END;
  1960. KernelLog.String(" Length: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * {16..27}, -16)), 0);
  1961. KernelLog.String(", PG: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * {12..14}, -12)), 0);
  1962. KernelLog.String(", Offset: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {0..11}), 0);
  1963. IF dword * {15} # {} THEN KernelLog.String(" [IOC]"); END;
  1964. KernelLog.Ln;
  1965. END;
  1966. FOR i := 0 TO 6 DO
  1967. Indent(4);
  1968. dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + 24H + i*4));
  1969. KernelLog.String("Buffer Pointer Page "); KernelLog.Int(i, 0); KernelLog.String(": ");
  1970. KernelLog.Hex(SYSTEM.VAL(LONGINT, dword * {12..31}), 8);
  1971. KernelLog.Ln;
  1972. END;
  1973. END;
  1974. END ShowItd;
  1975. *)
  1976. PROCEDURE ShowInterrupts * (s : SET);
  1977. BEGIN
  1978. IF Debug.Trace THEN
  1979. IF s * StsAsyncAdvance # {} THEN KernelLog.String("[AsyncAdvance]"); END;
  1980. IF s * StsHostSystemError # {} THEN KernelLog.String("[HostSystemError]"); END;
  1981. IF s * StsFrameListRollover # {} THEN KernelLog.String("[FrameListRollover]"); END;
  1982. IF s * StsPortChange # {} THEN KernelLog.String("[PortChange]"); END;
  1983. IF s * StsUsbError # {} THEN KernelLog.String("[UsbError]"); END;
  1984. IF s * StsUsbInterrupt # {} THEN KernelLog.String("[UsbInterrupt]"); END;
  1985. END;
  1986. END ShowInterrupts;
  1987. (* Called when this module is unloaded. Unregister all EnhancedHostController objects *)
  1988. PROCEDURE Cleanup;
  1989. BEGIN
  1990. UsbHcdi.UnRegisterHostControllers(Description);
  1991. END Cleanup;
  1992. BEGIN
  1993. Modules.InstallTermHandler(Cleanup);
  1994. END UsbEhci.
  1995. UsbEhci.Install ~ SystemTools.Free UsbEhci ~