UsbOhci.Mod 66 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594
  1. MODULE UsbOhci; (** AUTHOR "staubesv"; PURPOSE "USB Open Host Controller Driver" *)
  2. (**
  3. * Bluebottle USB Open Host Controller Driver
  4. * Implements the UsbHcdi host controller driver interface (HCDI)
  5. *
  6. * Usage:
  7. *
  8. * UsbOhci.Install ~ loads this device driver
  9. * SystemTools.Free UsbOhci ~ unloads it
  10. *
  11. * References:
  12. *
  13. * OpenHCI Open Host Controller Interface Specification for USB, Release 1.0a
  14. *
  15. * History:
  16. *
  17. * 01.12.2005 History started (staubesv)
  18. * 07.12.2005 Removed shadow registers, improved HC initialization (staubesv)
  19. * 15.12.2005 Moved buffer checks to UsbHcdi.Mod (staubesv)
  20. * 11.01.2006 Implemented H/W scatter/gather support (staubesv)
  21. * 16.01.2006 FlushPCI added (staubesv)
  22. * 25.01.2006 Make sure HCCA does not cross page boundary (staubesv)
  23. * 01.03.2006 Fixed critical bug in OpenHostController.CreateTDlist (staubesv)
  24. * 08.03.2006 LinkTDs: Allow linking TDs to a halted ED (staubesv)
  25. * 28.06.2006 Use KernelLog.Hex instead of UsbHcdi.PrintHex (staubesv)
  26. * 03.08.2006 Adapted to UsbHcdi, Unlink TDs from ED if ED halted in UpdatePipeStatus, Fixed datatoggle for control transfer > 1TD,
  27. * correctly calclulate actLen in case of short packets (staubesv)
  28. * 11.08.2006 Cleaner handling of root hub port status register operations (staubesv)
  29. * 13.11.2006 UpdatePipeStatus: Set pipe.status to Usbdi.Stalled when a stall is detected (staubesv)
  30. * 03.07.2007 Enable bus mastering for HC if not already enabled (staubesv)
  31. *
  32. * TODOs:
  33. * - isochronous transfers
  34. * - deactivate tds in UnlinkTDs
  35. *)
  36. IMPORT SYSTEM, KernelLog, Machine, PCI, Kernel, Objects, Modules, UsbHcdi, Usbdi, Debug := UsbDebug;
  37. CONST
  38. Description = "USB Open Host Controller";
  39. ScatterGatherListSize = 4000;
  40. PageSize = 4096;
  41. (* Operational register offsets from io base address; OHCI Spec. 1.0 *)
  42. HcRevision = 0H;
  43. HcControl = 4H;
  44. HcCommandStatus = 8H;
  45. HcInterruptStatus = 0CH;
  46. HcInterruptEnable = 10H;
  47. HcInterruptDisable = 14H;
  48. HcHCCA = 18H;
  49. HcPeriodCurrentED = 1CH;
  50. HcControlHeadED = 20H;
  51. HcControlCurrentED = 24H;
  52. HcBulkHeadED = 28H;
  53. HcBulkCurrentED = 2CH;
  54. HcDoneHead = 30H;
  55. HcFmInterval = 34H;
  56. HcFmRemaining = 38H;
  57. HcFmNumber = 3CH;
  58. HcPeriodicStart = 40H;
  59. HcLSThreshold = 44H;
  60. HcRhDescriptorA = 48H;
  61. HcRhDescriptorB = 4CH;
  62. HcRhStatus = 50H;
  63. HcRhPortStatus1 = 54H;
  64. (* HcRhPortStatus[n] = n * 4 +54; *)
  65. (* HcRevision Register *)
  66. HcRevRevision = {0..7};
  67. HcRevLegacySupport = {8};
  68. (* Legacy Support Registers (only available if Bit 8 is set in the register HcRevision *)
  69. HceControl = 100H;
  70. HceInput = 104H;
  71. HceOutput = 108H;
  72. HceStatus = 10CH;
  73. HceControlReserved = {9..31};
  74. (* HcControl register *)
  75. HcConControlBulkServiceRatio = {0..1};
  76. HcConPeriodicListEnable = {2};
  77. HcConIsochronousEnable = {3};
  78. HcConControlListEnable = {4};
  79. HcConBulkListEnable = {5};
  80. HcConHcFunctionalState = {6..7};
  81. HcConInterruptRouting = {8};
  82. HcConRemoteWakeupConnected = {9};
  83. HcConRemoteWakeupEnable = {10};
  84. HcConReserved = {11..31}; (* do not alter *)
  85. (* HcControl HcConFunctionalState coding *)
  86. UsbReset = 0;
  87. UsbResume = 1;
  88. UsbOperational = 2;
  89. UsbSuspend = 3;
  90. (* HcCommandStatus register *)
  91. HcCmdHostControllerReset = {0};
  92. HcCmdControlListFilled = {1};
  93. HcCmdBulkListFilled = {2};
  94. HcCmdOwnershipChangeRequest = {3};
  95. HcCmdSchedulingOverrunCount = {16,17};
  96. (* HcInterruptStatus register *)
  97. HcIntSchedulingOverrun = {0};
  98. HcIntWriteBackDoneHead = {1};
  99. HcIntStartOfFrame = {2};
  100. HcIntResumeDetected = {3};
  101. HcIntUnrecoverableError = {4};
  102. HcIntFrameNumberOverflow = {5};
  103. HcIntRootHubStatusChange = {6};
  104. HcIntReserved = {7..29} + {31}; (* Bit 31 has alwalys to be 0 (OHCIspec) *)
  105. HcIntOwnerShipChange = {30};
  106. (* HcInterruptEnable / HciInterruptDisable register; Write 1: set / clear; Write 0: leave unchanged *)
  107. IntSchedulingOverrun = {0};
  108. IntHcDoneHeadWriteback = {1};
  109. IntStartOfFrame = {2};
  110. IntResumeDetect = {3};
  111. IntUnrecoverableError = {4};
  112. IntFrameNumberOverflow = {5};
  113. IntRootHubStatusChange = {6};
  114. IntOwnerShipChange = {30};
  115. IntMasterInterruptEnable = {31};
  116. IntReservedMask = {7..29};
  117. (* HcFmInterval register (R/W) *)
  118. HcFmiFrameInterval = {0..13};
  119. HcFmiFsLargestDataPacket = {16..30};
  120. HcFmiFrameIntervalToggle = {31};
  121. HcFmiReserved = {14,15};
  122. (* HcPeriodicStart register (R/W) *)
  123. HcPerPeriodicStart = {0..13};
  124. HcPerReserved = {14..31};
  125. (* HcRhDescriptorA register (R) *)
  126. HcRhaNumberDownstreamPorts = {0..7};
  127. HcRhaNoPowerSwitching = {9};
  128. HcRhaPowerSwitchingMode = {8};
  129. HcRhaDeviceType = {10}; (* should be zero *)
  130. HcRhaOverCurrentProtectionMode = {11};
  131. HcRhaNoOverCurrentProtection = {12};
  132. HcRhaPowerOnToPowerGoodTime = {24..31}; (* unit of time is 2ms *)
  133. (* HcRhDescriptorB register (R/W)*)
  134. HcRhbDeviceRemovable = {1..15};
  135. HcRhbPortPowerControlMask = {17..31};
  136. HcRhbReserved = {0,16};
  137. (* HcRhStatus register (R/W) *)
  138. HcRhsLocalPowerStatus = {0};
  139. HcRhsOverCurrentIndicator = {1};
  140. HcRhsDeviceRemoteWakeupEnable = {15};
  141. HcRhsLocalPowerStatusChange = {16};
  142. HcRhsOverCurrentIndicatorChange = {17};
  143. HcRhsClearRemoteWakeupEnable = {31};
  144. HcRhsReservedMask = {2..14} + {18..30}; (* reserved bits should always be written '0' *)
  145. (* HcRhStatus register when written '1' *)
  146. HcRhsClearGlobalPower = {0};
  147. HcRhsSetRemoteWakeupEnable = {15};
  148. HcRhsSetGlobalPower = {16};
  149. (* Writing zeros to RhPortStatus register has no effect *)
  150. (* HcRhPortStatus register (R) *)
  151. HcPsCurrentConnectStatus = {0};
  152. HcPsPortEnableStatus = {1};
  153. HcPsPortSuspendStatus = {2};
  154. HcPsPortOverCurrentIndicator = {3};
  155. HcPsPortResetStatus = {4};
  156. HcPsPortPowerStatus = {8};
  157. HcPsLowSpeedDeviceAttached = {9};
  158. (* HcRhPortStatusRegister (W) *)
  159. HcPsClearPortEnable = {0};
  160. HcPsSetPortEnable = {1};
  161. HcPsSetPortSuspend = {2};
  162. HcPsClearSuspendStatus = {3};
  163. HcPsSetPortReset = {4};
  164. HcPsSetPortPower = {8};
  165. HcPsClearPortPower = {9};
  166. (* Write Clear *)
  167. HcPsConnectStatusChange = {16};
  168. HcPsPortEnableStatusChange = {17};
  169. HcPsSuspendStatusChange = {18};
  170. HcPsOverCurrentIndicatorChange = {19};
  171. HcPsPortResetStatusChange = {20};
  172. HcPsReserved = {5..7} + {10..15} + {21..31}; (* reserved bits should always be written '0' *)
  173. HcPsChangeMask = {16..20};
  174. (* Endpoint Descriptor Format (16byte structure, must be 16byte aligned) *)
  175. (* I use a 32 byte data structure. See below *)
  176. (* Offsets: *)
  177. EdControlStatus = 0;
  178. EdTailP = 4;
  179. EdHeadP = 8;
  180. EdNextEdP = 12;
  181. (* Dword 0 *)
  182. EdFunctionAddress = {0..6};
  183. EdEndpointNumber = {7..10};
  184. EdDirection = {11..12};
  185. EdSpeed = {13};
  186. EdSkip = {14};
  187. EdFormat = {15};
  188. EdMaximumPacketSize = {16..26};
  189. (* Dword 2 *)
  190. EdHalted = {0};
  191. EdToggleCarry = {1};
  192. (* Used in ED's and TD's to describe the transfers direction *)
  193. PidSetup = 0; (* get direction from TD *)
  194. PidOut = 1;
  195. PidIn = 2;
  196. (* Transfer Descriptor Format (16byte structure, must be 16byte aligned) *)
  197. (* Offsets: *)
  198. TdCommand = 0;
  199. TdCurrentBufferP = 4;
  200. TdNextTdP = 8;
  201. TdBufferEndP = 12;
  202. (* Dword 0 *)
  203. TdTransferSize = {0..17}; (* bluebottle specific: how many bytes should have been transfered by this TD *)
  204. TdBufferRounding = {18};
  205. TdDirectionPid = {19..20};
  206. TdDelayInterrupt = {21..23};
  207. TdDataToggle = {24};
  208. TdDataToggleFromTd = {25};
  209. TdErrorCount = {26..27};
  210. TdConditionCode = {28..31};
  211. (* Dword 1 *)
  212. TdCurrentBufferPointer = {0..31};
  213. (* Dword 2 *)
  214. TdNextTd = {4..31};
  215. (* Dword 3 *)
  216. TdBufferEnd = {0..31};
  217. (* TD Completion Codes *)
  218. TdNoError = 0;
  219. TdCrc = 1;
  220. TdBitStuffing = 2;
  221. TdDataToggleMismatch = 3;
  222. TdStall = 4;
  223. TdDeviceNotResponding = 5;
  224. TdPidCheckFailure = 6;
  225. TdUnexpectedPid = 7;
  226. TdDataOverrun = 8;
  227. TdDataUnderrun = 9;
  228. (* bits 10 & 11 are reserved *)
  229. TdBufferOverrun = 12;
  230. TdBufferUnderrun = 13;
  231. TdNotAccessed1 = 14;
  232. TdNotAccessed2 = 15;
  233. (* 1110 & 1111 : not accessed & init value *)
  234. (* static, disqabled endpoint descriptor to build basic data structure in the HCCA *)
  235. (* 3: control-,bulk- and isochronousTD; 6: interruptTD[0..5]; 1: NullQueue 1: alignment *)
  236. TdListSize = 3 + 6 + 1 + 1;
  237. (* HCCA : hcca.data[base+offset] *)
  238. HccaInterruptTable = 0;
  239. HccaFrameNumber = 32; (* lower 16 bits *)
  240. HccaDoneHead = 36;
  241. (* how many Queue Heads should the debug procedure ShowSchedule() show... *)
  242. (* useful if the schedule data structure is corrupted *)
  243. ShowScheduleMaxQH = 1000;
  244. (* constants used for hc initialization *)
  245. HccFSLargestDataPacket = 1000*8 ; (* in bits *)
  246. HccFrameInterval = 2EDFH;
  247. HccPeriodicStart = 3E67H;
  248. TYPE
  249. OpenHostController = OBJECT (UsbHcdi.Hcd)
  250. VAR
  251. (* Host Controller Communication Area (HCCA) *)
  252. hcca : UsbHcdi.AlignedMemSpace;
  253. (* host controller revision (HcRevision Register 0..7), BCD coded *)
  254. revision : LONGINT;
  255. legacySupport : BOOLEAN;
  256. (* queue heads *)
  257. controlED : LONGINT;
  258. bulkED : LONGINT;
  259. isochronousED : LONGINT;
  260. interruptED : POINTER TO ARRAY 6 OF LONGINT;
  261. nullTD : LONGINT;
  262. (* this array will provide the 16byte aligned TD's for controlTD, bulkTD, isochronousTD and interruptTD[] *)
  263. tdlist : UsbHcdi.AlignedMemSpace;
  264. globalPowerSwitching : BOOLEAN;
  265. (** Enable power for the specified port *)
  266. PROCEDURE EnablePortPower*(port : LONGINT);
  267. BEGIN
  268. IF ~globalPowerSwitching THEN
  269. SYSTEM.PUT32(ports[port], HcPsSetPortPower);
  270. ELSE
  271. SYSTEM.PUT32(iobase + HcRhStatus, HcRhsSetGlobalPower);
  272. END;
  273. FlushPCI;
  274. END EnablePortPower;
  275. (** Disable power for the specified port *)
  276. PROCEDURE DisablePortPower*(port : LONGINT);
  277. BEGIN
  278. IF ~globalPowerSwitching THEN
  279. SYSTEM.PUT32(ports[port], HcPsClearPortPower);
  280. ELSE
  281. (* Disables power for all ports! *)
  282. SYSTEM.PUT32(iobase + HcRhStatus, HcRhsClearGlobalPower);
  283. END;
  284. FlushPCI;
  285. END DisablePortPower;
  286. (** Reset and enable specified port *)
  287. PROCEDURE ResetAndEnablePort*(port : LONGINT) : BOOLEAN;
  288. VAR status : SET; mtimer : Kernel.MilliTimer;
  289. BEGIN
  290. SYSTEM.PUT32(ports[port], HcPsSetPortReset); FlushPCI;
  291. Wait(UsbHcdi.PortResetTime); (* >= 10ms, USBspec *)
  292. Kernel.SetTimer(mtimer, UsbHcdi.PortEnableTimeout);
  293. REPEAT
  294. status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  295. UNTIL (status * HcPsPortEnableStatus # {}) OR Kernel.Expired(mtimer);
  296. RETURN status * HcPsPortEnableStatus # {};
  297. END ResetAndEnablePort;
  298. (** Disables port number <port> on this root hub *)
  299. PROCEDURE DisablePort*(port : LONGINT);
  300. BEGIN
  301. SYSTEM.PUT32(ports[port], HcPsClearPortEnable); FlushPCI;
  302. SYSTEM.PUT32(ports[port], HcPsChangeMask); FlushPCI;
  303. END DisablePort;
  304. (** Get the status of the port <port> of this root hub. Registers which indicate changes are reset by GetPortStatus *)
  305. PROCEDURE GetPortStatus*(port : LONGINT; ack : BOOLEAN):SET;
  306. VAR status, s : SET;
  307. BEGIN
  308. s := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
  309. (* clear all bits that reported a change event *)
  310. IF ack & ((s * HcPsChangeMask) # {}) THEN SYSTEM.PUT32(ports[port], HcPsChangeMask * s); FlushPCI; END;
  311. status := {};
  312. IF s * HcPsCurrentConnectStatus # {} THEN status := status + UsbHcdi.PortStatusDevicePresent; END;
  313. IF s * HcPsPortEnableStatus # {} THEN status := status + UsbHcdi.PortStatusEnabled END;
  314. IF s * HcPsPortSuspendStatus # {} THEN status := status + UsbHcdi.PortStatusSuspended END;
  315. IF s * HcPsPortOverCurrentIndicator # {} THEN status := status + UsbHcdi.PortStatusOverCurrent END;
  316. IF s * HcPsPortResetStatus # {} THEN status := status + UsbHcdi.PortStatusReset END;
  317. IF s * HcPsPortPowerStatus # {} THEN status := status + UsbHcdi.PortStatusPowered END;
  318. IF s * HcPsConnectStatusChange # {} THEN status := status + UsbHcdi.PortStatusConnectChange END;
  319. IF s * HcPsPortEnableStatusChange # {} THEN status := status + UsbHcdi.PortStatusEnabledChange END;
  320. IF s * HcPsSuspendStatusChange # {} THEN status := status + UsbHcdi.PortStatusSuspendChange END;
  321. IF s * HcPsOverCurrentIndicatorChange # {} THEN status := status + UsbHcdi.PortStatusOverCurrentChange END;
  322. IF s * HcPsPortResetStatusChange # {} THEN status := status + UsbHcdi.PortStatusResetChange END;
  323. IF s * HcPsLowSpeedDeviceAttached # {} THEN
  324. status := status + UsbHcdi.PortStatusLowSpeed;
  325. ELSE
  326. status := status + UsbHcdi.PortStatusFullSpeed;
  327. END;
  328. RETURN status;
  329. END GetPortStatus;
  330. (* Returns the current frame number; the frame number is incremented by the Host Controller at the end of each frame time *)
  331. PROCEDURE GetFrameNumber*() : INTEGER;
  332. BEGIN
  333. RETURN SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, hcca.data[hcca.base + HccaFrameNumber]) * {0..15});
  334. END GetFrameNumber;
  335. (* Build and then insert the endpoint descriptor of the pipe into the host controller schedule *)
  336. PROCEDURE InsertQH*(pipe : UsbHcdi.Pipe) : BOOLEAN;
  337. VAR nextED : LONGINT; dword : SET;
  338. BEGIN (* only call from exclusive regions *)
  339. ASSERT((pipe#NIL) & (pipe.qh#0) & (SYSTEM.VAL(SET, pipe.qh) * {0..3} = {})); (* 16byte alignment *)
  340. ASSERT((pipe.maxPacketSize > 0));
  341. CASE pipe.type OF (* in which queue should we insert the pipe ? *)
  342. | UsbHcdi.PipeControl : pipe.queue := controlED;
  343. | UsbHcdi.PipeBulk : pipe.queue := bulkED;
  344. | UsbHcdi.PipeIsochronous : pipe.queue := isochronousED;
  345. | UsbHcdi.PipeInterrupt :
  346. BEGIN
  347. IF pipe.irqInterval = 1 THEN (* 1ms queue *)
  348. pipe.queue := interruptED[0];
  349. ELSIF pipe.irqInterval < 4 THEN (* 2ms queue *)
  350. pipe.queue := interruptED[1];
  351. ELSIF pipe.irqInterval < 8 THEN (* 4ms queue *)
  352. pipe.queue := interruptED[2];
  353. ELSIF pipe.irqInterval < 16 THEN (* 8ms queue *)
  354. pipe.queue := interruptED[3];
  355. ELSIF pipe.irqInterval < 32 THEN (* 16ms queue *)
  356. pipe.queue := interruptED[4];
  357. ELSE
  358. pipe.queue := interruptED[5]; (* 32 ms queue *)
  359. END;
  360. END;
  361. ELSE
  362. RETURN FALSE;
  363. END;
  364. (* build the pipe's endpoint descriptor *)
  365. (* dword0: 0..6: function address; 7..10: endpoint number; 11..12: direction; 13: Speed; 14: Skip; 15: Format; 16..26: maximum packet size; 27..31: available *)
  366. dword := SYSTEM.VAL(SET, pipe.address) * EdFunctionAddress + (SYSTEM.VAL(SET, LSH(pipe.endpoint, 7)) * EdEndpointNumber);
  367. IF pipe.type = UsbHcdi.PipeControl THEN (* get direction from TD *)
  368. (* bit 11&12 both zero *)
  369. ELSE (* get direction from ED *)
  370. IF pipe.direction = UsbHcdi.In THEN
  371. INCL(dword, 12); (* PidIn *)
  372. ELSIF pipe.direction = UsbHcdi.Out THEN
  373. INCL(dword, 11); (* PidOut *)
  374. ELSE
  375. HALT(90);
  376. END;
  377. END;
  378. IF pipe.speed = UsbHcdi.LowSpeed THEN dword := dword + EdSpeed; END;
  379. IF pipe.type = UsbHcdi.PipeIsochronous THEN dword := dword + EdFormat; END;
  380. dword := dword + (SYSTEM.VAL(SET, LSH(pipe.maxPacketSize, 16)) * EdMaximumPacketSize);
  381. dword := dword + EdSkip; (* HC should not (yet) process this ED *)
  382. SYSTEM.PUT32(pipe.qh + EdControlStatus, dword);
  383. (* dword1: 0..3: available; 4..31: TailP *)
  384. SYSTEM.PUT32(pipe.qh + EdTailP , nullTD);
  385. (* dword2: 0: halted; 1: dataToggle; 2..3: 00; 4..31: HeadP *)
  386. SYSTEM.PUT32(pipe.qh + EdHeadP , nullTD);
  387. (* dword3: NextED pointer (Physical Address!!) *)
  388. nextED := SYSTEM.GET32(pipe.queue + EdNextEdP); (* get NextED field of the queue; nextED contains physical address *)
  389. SYSTEM.PUT32(pipe.qh + EdNextEdP, SYSTEM.VAL(SET, nextED) * {4..31});
  390. SYSTEM.PUT32(pipe.queue + EdNextEdP, pipe.qh);
  391. RETURN TRUE;
  392. END InsertQH;
  393. (* Delete the queue head <qh> in the queue <queue> *)
  394. PROCEDURE RemoveQH*(pipe : UsbHcdi.Pipe);
  395. VAR prev, temp : LONGINT; dword : SET;
  396. BEGIN (* caller must hold obj lock *)
  397. (* REMEMBER: The Host Controller is concurrently accessing dword1-3 of the ED's *)
  398. (* set EdSkip flag (HC shall not process the ED) *)
  399. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh)) + EdSkip;
  400. SYSTEM.PUT32(pipe.qh, dword);
  401. (* search to ED that points to the ED of the pipe *)
  402. prev := pipe.queue;
  403. LOOP
  404. temp := SYSTEM.GET32(prev + EdNextEdP);
  405. IF (temp = pipe.qh) OR (temp = 0) THEN EXIT; END;
  406. prev := temp;
  407. ASSERT(SYSTEM.VAL(SET, prev) * {0..3} = {});
  408. END;
  409. IF temp = 0 THEN
  410. IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbOhci: DeleteQH: Pipe not found."); KernelLog.Ln; END;
  411. RETURN;
  412. END;
  413. (* remove pipe.qh from EDList *)
  414. SYSTEM.PUT32(prev + EdNextEdP, SYSTEM.GET32(pipe.qh + EdNextEdP));
  415. (* the ED is not any more in the EDList, but it is possible the HC is processing the ED right now... *)
  416. (* We disable the list processing for the list that contained the deleted ED *)
  417. CASE pipe.type OF
  418. | UsbHcdi.PipeControl:
  419. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcControl)) - HcConControlListEnable;
  420. SYSTEM.PUT32(iobase + HcControl, dword); FlushPCI;
  421. temp := SYSTEM.GET32(iobase + HcControlCurrentED);
  422. IF temp = pipe.qh THEN (* deleted ED currently in process... update *)
  423. SYSTEM.PUT32(iobase + HcControlCurrentED, 0); FlushPCI;
  424. END;
  425. (* re-enable list processing of the control list *)
  426. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcControl)) + HcConControlListEnable;
  427. SYSTEM.PUT32(iobase + HcControl, dword); FlushPCI;
  428. | UsbHcdi.PipeBulk :
  429. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcControl)) - HcConBulkListEnable;
  430. SYSTEM.PUT32(iobase + HcControl, dword); FlushPCI;
  431. temp := SYSTEM.GET32(iobase + HcBulkCurrentED);
  432. IF temp = pipe.qh THEN (* deleted ED currently in process... update *)
  433. SYSTEM.PUT32(iobase + HcBulkCurrentED, 0); FlushPCI;
  434. END;
  435. (* re-enable list processing of the control list *)
  436. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcControl))+HcConBulkListEnable;
  437. SYSTEM.PUT32(iobase + HcControl, dword); FlushPCI;
  438. ELSE
  439. END;
  440. END RemoveQH;
  441. (** Checks whether TDs may be linked to the pipe's QH *)
  442. PROCEDURE LinkTDsAllowed*(pipe : UsbHcdi.Pipe) : BOOLEAN;
  443. VAR headP, tailP : SET;
  444. BEGIN {EXCLUSIVE}
  445. headP := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdHeadP)) * {4..31};
  446. tailP := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdTailP)) * {4..31};
  447. IF headP # tailP THEN (* There are TDs linked to this ED *)
  448. pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.LinkTDsFailed;
  449. RETURN FALSE;
  450. ELSE
  451. RETURN TRUE;
  452. END;
  453. END LinkTDsAllowed;
  454. (* Insert the TD list <td> into the queue (ED) <queue> *)
  455. PROCEDURE LinkTDs*(pipe : UsbHcdi.Pipe; td : LONGINT);
  456. VAR dword : SET;
  457. BEGIN {EXCLUSIVE}
  458. (* endpoint should not be processed by the HC ... *)
  459. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdControlStatus)) + EdSkip;
  460. SYSTEM.PUT32(pipe.qh + EdControlStatus, dword);
  461. (* inserts the tdlist <td> into the queue *)
  462. SYSTEM.PUT32(pipe.qh + EdTailP, nullTD); (* TailP ::= pointer to td *)
  463. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdHeadP)); (* we need to preserve the lower 4 bits *)
  464. SYSTEM.PUT32(pipe.qh + EdHeadP, SYSTEM.VAL(SET, td) * {4..31} + dword * {1..3}); (* HeadPointer :: = td; Clear Halt bit if set *)
  465. (* enable pipe *)
  466. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdControlStatus));
  467. SYSTEM.PUT32(pipe.qh + EdControlStatus, dword - EdSkip);
  468. IF pipe.type = UsbHcdi.PipeControl THEN (* Set ControlListFilled Bit... bits written 0 remain unchanged *)
  469. SYSTEM.PUT32(iobase + HcCommandStatus, HcCmdControlListFilled);
  470. ELSIF pipe.type = UsbHcdi.PipeBulk THEN (* Set BulkListFilled Bit... bits written 0 remain unchanged *)
  471. SYSTEM.PUT32(iobase + HcCommandStatus, HcCmdBulkListFilled);
  472. END;
  473. FlushPCI;
  474. END LinkTDs;
  475. (** Remove all transfer descriptors from the pipe's endpoint descriptor *)
  476. PROCEDURE UnlinkTDs*(pipe : UsbHcdi.Pipe);
  477. VAR dword : SET;
  478. BEGIN {EXCLUSIVE}
  479. IF pipe.firstTD = 0 THEN RETURN END; (* pipe has not yet been used *)
  480. (* disable processing for this endpoint descriptor *)
  481. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdControlStatus));
  482. SYSTEM.PUT32(pipe.qh + EdControlStatus, dword + EdSkip);
  483. (* remove TD list *)
  484. SYSTEM.PUT32(pipe.qh+ EdTailP, nullTD); (* TailP ::= pointer to td *)
  485. SYSTEM.PUT32(pipe.qh + EdHeadP, nullTD); (* HeadPointer :: = td *)
  486. END UnlinkTDs;
  487. (* Clears the EdHalted bit in the Endpoint descriptor and sets the EdSkip bit to prevent processing by the HC *)
  488. PROCEDURE ClearHalt*(pipe : UsbHcdi.Pipe);
  489. VAR dword : SET; temp : SET;
  490. BEGIN
  491. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdHeadP));
  492. IF dword * EdHalted # {} THEN
  493. temp := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdControlStatus));
  494. temp := temp + EdSkip;
  495. SYSTEM.PUT32(pipe.qh + EdControlStatus, temp);
  496. dword := dword - EdHalted - EdToggleCarry;
  497. SYSTEM.PUT32(pipe.qh + EdHeadP, dword);
  498. ELSE
  499. IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbOhci: ClearHalt: Pipe is not halted."); KernelLog.Ln; END;
  500. END;
  501. END ClearHalt;
  502. PROCEDURE ScheduleControl*(pipe : UsbHcdi.Pipe; direction : LONGINT; msg : UsbHcdi.ControlMessage; bufferLen : LONGINT; VAR buffer : Usbdi.Buffer);
  503. VAR ranges : ARRAY ScatterGatherListSize OF Machine.Range; td, numRanges : LONGINT; dword : SET;
  504. BEGIN
  505. (* control transfers use a three stage protocol:
  506. * stage1: control setup transaction
  507. * stage2: optional data stage
  508. * stage3: status transaction *)
  509. pipe.firstTD := pipe.tdBase; td := pipe.tdBase;
  510. (* stage1: control setup transaction: build the setup TD *)
  511. (* dword0: no IOC; EdDirection = EdPidSetup; DataToggle in TDs; DataToggle = Data0; ErrorCount = 0; CC = not accessed *)
  512. SYSTEM.PUT32(td + TdCommand, TdDelayInterrupt + TdDataToggleFromTd + {29,30,31});
  513. SYSTEM.PUT32(td + TdNextTdP, td + 16);
  514. Machine.TranslateVirtual(ADDRESSOF(msg[0]), 8, numRanges, ranges);
  515. IF numRanges = 0 THEN
  516. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: Scatter/Gather list too small."); KernelLog.Ln; END;
  517. pipe.status := Usbdi.Error; pipe.errors := UsbHcdi.TransferTooLarge; RETURN;
  518. END;
  519. SYSTEM.PUT32(td + TdCurrentBufferP, ranges[0].adr);
  520. IF numRanges = 1 THEN
  521. SYSTEM.PUT32(td + TdBufferEndP, ranges[0].adr + 8 - 1);
  522. ELSE
  523. SYSTEM.PUT32(td + TdBufferEndP, ranges[1].adr + ranges[1].size - 1);
  524. END;
  525. ASSERT(SYSTEM.GET32(td + TdBufferEndP) - SYSTEM.GET32(td + TdCurrentBufferP) + 1 = 8); (* 8 byte control message *)
  526. (* setup phase always starts with dataToggle = FALSE, so now it must be TRUE *)
  527. pipe.dataToggle := TRUE;
  528. (* stage 2: (optional) data stage *)
  529. IF bufferLen # 0 THEN
  530. IF ~CreateTDList(pipe, direction, bufferLen, 0, buffer, td + 16, td, TRUE) THEN
  531. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: Scatter/Gather list too small."); KernelLog.Ln; END;
  532. pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.TransferTooLarge; RETURN;
  533. END;
  534. END;
  535. td := td + 16;
  536. IF td + 15 > ADDRESSOF(pipe.tdBuffer[pipe.tdBufferLen-1]) THEN
  537. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: TD buffer too small"); KernelLog.Ln; END;
  538. pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.OutOfTDs; RETURN;
  539. END;
  540. (* stage 3: status: build status TD *)
  541. dword := TdDataToggle + TdDataToggleFromTd + {29,30,31}; (* dataToggle always TRUE and set ind TD in status stage; CC = not accessed *)
  542. IF (direction = UsbHcdi.Out) OR (bufferLen = 0) THEN
  543. INCL(dword, 20); (* PidIn *)
  544. ELSE
  545. INCL(dword, 19); (* PidOut *)
  546. END;
  547. IF pipe.ioc THEN (* enable interrupt on completion for this TD *)
  548. (* okay, but ... optimization possible : DelayInterrupt *)
  549. ELSE
  550. dword := dword + TdDelayInterrupt; (* no IOC *)
  551. END;
  552. SYSTEM.PUT32(td + TdCommand, dword);
  553. SYSTEM.PUT32(td + TdCurrentBufferP, 0); (* no data *)
  554. SYSTEM.PUT32(td + TdNextTdP, nullTD);
  555. SYSTEM.PUT32(td + TdBufferEndP, 0);
  556. pipe.lastTD := td;
  557. END ScheduleControl;
  558. PROCEDURE Schedule*(pipe : UsbHcdi.Pipe; bufferLen, offset: LONGINT; VAR buffer: Usbdi.Buffer);
  559. VAR dword : SET;
  560. BEGIN
  561. pipe.firstTD := pipe.tdBase;
  562. IF ~CreateTDList(pipe, pipe.direction, bufferLen, offset, buffer, pipe.firstTD, pipe.lastTD, FALSE) THEN
  563. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: Scatter/Gather list too small."); KernelLog.Ln; END;
  564. pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.TransferTooLarge; RETURN;
  565. END;
  566. SYSTEM.PUT32(pipe.lastTD + TdNextTdP, nullTD);
  567. IF pipe.ioc THEN
  568. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.lastTD + TdCommand));
  569. dword := dword - TdDelayInterrupt; (* Enable IOC by delaying interrupts for zero frames *)
  570. SYSTEM.PUT32(pipe.lastTD + TdCommand, dword);
  571. END;
  572. END Schedule;
  573. PROCEDURE CreateTDList(pipe : UsbHcdi.Pipe; direction, len, ofs : LONGINT; VAR buffer : Usbdi.Buffer; firstTD : LONGINT; VAR lastTD : LONGINT; tdToggle : BOOLEAN) : BOOLEAN;
  574. VAR
  575. restlen, curlen : LONGINT;
  576. numRanges, idx, offset : LONGINT;
  577. td, temp : LONGINT;
  578. dword : SET;
  579. BEGIN
  580. Machine.TranslateVirtual(ADDRESSOF(buffer[ofs]), len, numRanges, pipe.sgList^);
  581. IF numRanges = 0 THEN
  582. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: Scatter/Gather list too small."); KernelLog.Ln; END;
  583. pipe.status := Usbdi.Error; pipe.errors := UsbHcdi.TransferTooLarge; RETURN FALSE;
  584. END;
  585. td := firstTD - 16;
  586. restlen := len; idx := 0; offset := 0;
  587. WHILE restlen > 0 DO (* build TD chain *)
  588. td := td + 16;
  589. IF td + 15 > ADDRESSOF(pipe.tdBuffer[pipe.tdBufferLen-1]) THEN
  590. pipe.errors := pipe.errors + UsbHcdi.OutOfTDs;
  591. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: TD buffer too small"); KernelLog.Ln; END;
  592. RETURN FALSE;
  593. END;
  594. (* Each TD can have at maximum 8KB buffer space. The buffer must be virtually contiguous. The buffer may, however, *)
  595. (* spawn across one page boundary. When the HC detects a page crossing, it will use the 20 msb bits of the TdBufferEndP *)
  596. (* pointer to get the next page frame number. *)
  597. (* dword1: current buffer pointer *)
  598. SYSTEM.PUT32(td + TdCurrentBufferP, pipe.sgList[idx].adr + offset); (* Adding offset is okay since adr is page aligned or offset is zero *)
  599. curlen := PageSize - LONGINT ((pipe.sgList[idx].adr + offset) MOD PageSize);
  600. IF curlen >= restlen THEN (* no page crossing, last TD in chain *)
  601. curlen := restlen; restlen := 0;
  602. SYSTEM.PUT32(td + TdBufferEndP, pipe.sgList[idx].adr + offset + curlen - 1);
  603. ELSE (* Page crossing; will still have room for 4096 Bytes in this TD *)
  604. restlen := restlen - curlen; offset := 0; INC(idx);
  605. ASSERT(idx < numRanges);
  606. (* The special thing here is, that, curlen must be a multiple of pipe.maxPacketSize if this is not the last TD in the chain. *)
  607. (* Otherwise we would ask/sent for less data than device exspects to send/receive. *)
  608. temp := PageSize - ((curlen + PageSize) MOD pipe.maxPacketSize); (* max amount of data that fits into second buffer *)
  609. IF restlen > temp THEN (* Not last TD *)
  610. (* There will be more TDs. *)
  611. curlen := curlen + temp; offset := temp; restlen := restlen - temp;
  612. SYSTEM.PUT32(td + TdBufferEndP, pipe.sgList[idx].adr + temp - 1);
  613. IF offset = PageSize THEN INC(idx); offset := 0;
  614. ELSE (* Same page (idx) will be filled in first buffer pointer of next TD in chain *)
  615. END;
  616. ELSE (* Last TD in chain *)
  617. curlen := curlen + restlen;
  618. SYSTEM.PUT32(td + TdBufferEndP, pipe.sgList[idx].adr + restlen - 1);
  619. restlen := 0;
  620. END;
  621. END;
  622. ASSERT(curlen <= 8192);
  623. SYSTEM.PUT32(td + TdNextTdP, td + 16);
  624. dword := {29,30,31} + TdDelayInterrupt + TdBufferRounding ; (* CC = not accessed; no IOC *)
  625. dword := dword + SYSTEM.VAL(SET, curlen) * TdTransferSize;
  626. IF tdToggle THEN
  627. dword := dword + TdDataToggleFromTd;
  628. IF pipe.dataToggle THEN dword := dword + TdDataToggle; END;
  629. (* Calculate datatoggle value for next TD *)
  630. IF (curlen DIV pipe.maxPacketSize) MOD 2 # 0 THEN
  631. pipe.dataToggle := ~pipe.dataToggle;
  632. END;
  633. END;
  634. IF direction = UsbHcdi.In THEN
  635. INCL(dword, 20);
  636. ELSIF direction = UsbHcdi.Out THEN
  637. INCL(dword, 19);
  638. END;
  639. SYSTEM.PUT32(td + TdCommand, dword);
  640. END;
  641. lastTD := td;
  642. RETURN TRUE;
  643. END CreateTDList;
  644. PROCEDURE InterruptHandler;
  645. VAR s : SET;
  646. BEGIN (* works without being exclusive *)
  647. IF Debug.Stats THEN INC(NnofInterrupts); END;
  648. IF state >= UsbHcdi.Initialized THEN
  649. s := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcInterruptStatus));
  650. IF s # {} THEN
  651. IF Debug.Stats THEN INC(NnofInterruptsHandled); END;
  652. (* Reset interrupt status register *)
  653. SYSTEM.PUT32(iobase + HcInterruptStatus, s - HcIntReserved); FlushPCI;
  654. IF s * HcIntSchedulingOverrun # {} THEN KernelLog.String("UsbOhci: HcIntSchedulingOverrun"); KernelLog.Ln; END;
  655. IF s * HcIntResumeDetected # {} THEN KernelLog.String("UsbOhci: HcIntResumeDetected"); KernelLog.Ln; END;
  656. IF s * HcIntUnrecoverableError # {} THEN KernelLog.String("UsbOhci: HcIntUnrecoverableError"); KernelLog.Ln; END;
  657. IF s * HcIntFrameNumberOverflow # {} THEN (* KernelLog.String("UsbOhci: HcIntFrameNumberOverflow"); KernelLog.Ln; *)END;
  658. IF s * HcIntRootHubStatusChange # {} THEN
  659. IF statusChangeHandler # NIL THEN statusChangeHandler(0, 0); END;
  660. END;
  661. IF s * HcIntOwnerShipChange # {} THEN KernelLog.String("UsbOhci: HcIntOwnerShipChange"); KernelLog.Ln; END;
  662. IF s * HcIntWriteBackDoneHead # {} THEN
  663. NotifyCompletionHandlers;
  664. END;
  665. END;
  666. END;
  667. END InterruptHandler;
  668. (* re-evaluate the status of the pipe's qh (endpoint descriptor) and its TD list *)
  669. PROCEDURE UpdatePipeStatus*(pipe : UsbHcdi.Pipe);
  670. CONST
  671. MaxLoops = 10000;
  672. VAR
  673. tailP, headP : SET;
  674. td : LONGINT;
  675. currentBufferP, bufferEndP : LONGINT;
  676. dword, errors : SET;
  677. cc : LONGINT;
  678. actLen, len : LONGINT;
  679. loop : LONGINT;
  680. BEGIN
  681. IF SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdControlStatus)) * EdSkip # {} THEN RETURN; END;
  682. tailP := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdTailP));
  683. headP := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdHeadP));
  684. IF headP * EdHalted # {} THEN (* some error occured when processing this TD list ... *)
  685. td := pipe.firstTD; loop := 0; actLen := 0; errors := UsbHcdi.NoErrors;
  686. LOOP
  687. ASSERT(SYSTEM.VAL(SET, td) * {0..3} = {});
  688. (* evaluate condition codes (CC)*)
  689. dword := SYSTEM.VAL(SET, SYSTEM.GET32(td + TdCommand));
  690. cc := LSH(SYSTEM.VAL(LONGINT, dword * TdConditionCode), -28);
  691. CASE cc OF
  692. | TdNoError : (* only Usbdi.ResOK if all TD's do not have errors *)
  693. | TdCrc : errors := errors + UsbHcdi.Crc;
  694. | TdBitStuffing : errors := errors + UsbHcdi.BitStuff;
  695. | TdDataToggleMismatch : errors := errors + UsbHcdi.DataToggleMismatch;
  696. | TdStall : errors := errors + UsbHcdi.Stalled;
  697. | TdDeviceNotResponding : errors := errors + UsbHcdi.DeviceNotResponding;
  698. | TdPidCheckFailure : errors := errors + UsbHcdi.PidCheckFailure;
  699. | TdUnexpectedPid : errors := errors + UsbHcdi.UnexpectedPid;
  700. | TdDataOverrun : errors := errors + UsbHcdi.Babble;
  701. | TdDataUnderrun : errors := errors + UsbHcdi.ShortPacket;
  702. | TdBufferOverrun : errors := errors + UsbHcdi.Databuffer;
  703. | TdBufferUnderrun : errors := errors + UsbHcdi.Databuffer;
  704. ELSE
  705. (* there's something wrong ... either the TD has not been accessed (which is not possible since the information in the ED) *)
  706. errors := errors + UsbHcdi.Internal;
  707. END;
  708. IF pipe.transferLen > 0 THEN (* data had to be transfered... *)
  709. (* len bytes should have been transfered for this TD *)
  710. len := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, SYSTEM.GET32(td + TdCommand)) * TdTransferSize);
  711. currentBufferP := SYSTEM.GET32(td + TdCurrentBufferP);
  712. IF currentBufferP = 0 THEN (* all bytes transfered *)
  713. actLen := actLen + len;
  714. ELSE (* short packet *)
  715. bufferEndP := SYSTEM.GET32(td + TdBufferEndP);
  716. actLen := actLen + len - (bufferEndP - currentBufferP + 1);
  717. errors := errors + UsbHcdi.ShortPacket;
  718. END;
  719. END;
  720. IF errors - UsbHcdi.ShortPacket # {} THEN EXIT; END;
  721. IF td = pipe.lastTD THEN EXIT; END;
  722. td := td + 16;
  723. IF loop > MaxLoops THEN EXIT; END;
  724. INC(loop);
  725. END; (* end loop *)
  726. pipe.actLen := actLen; pipe.errors := errors;
  727. IF errors = UsbHcdi.NoErrors THEN
  728. pipe.status := Usbdi.Ok;
  729. ELSIF errors = UsbHcdi.ShortPacket THEN
  730. pipe.status := Usbdi.ShortPacket;
  731. ELSE
  732. IF errors * UsbHcdi.Stalled # {} THEN
  733. pipe.status := Usbdi.Stalled;
  734. ELSE
  735. pipe.status := Usbdi.Error;
  736. END;
  737. END;
  738. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdControlStatus));
  739. SYSTEM.PUT32(pipe.qh + EdControlStatus, dword + EdSkip);
  740. (* Unlink TDs from ED *)
  741. SYSTEM.PUT32(pipe.qh+ EdTailP, nullTD);
  742. SYSTEM.PUT32(pipe.qh + EdHeadP, nullTD);
  743. ELSIF (headP * {4..31} = tailP * {4..31}) THEN (* no errors occured *)
  744. pipe.actLen := pipe.transferLen;
  745. pipe.status := Usbdi.Ok;
  746. pipe.errors := errors;
  747. ELSE (* TD still active *)
  748. (* do nothing *)
  749. END;
  750. END UpdatePipeStatus;
  751. (* Set HC functional state to UsbReset. This causes the root hub and its downstream port to be reset and possible powered off (the latter) *)
  752. PROCEDURE HardwareReset() : BOOLEAN;
  753. VAR timer : Kernel.Timer;
  754. BEGIN
  755. SYSTEM.PUT32(iobase + HcControl, LSH(UsbReset, 6)); FlushPCI;
  756. NEW(timer); timer.Sleep(UsbHcdi.RootHubResetTime);
  757. RETURN TRUE;
  758. END HardwareReset;
  759. (* HC moves to UsbSuspend state und almost all operational registers are reset.
  760. * Does not affect the root hub and its downstream ports *)
  761. PROCEDURE SoftwareReset() : BOOLEAN;
  762. VAR millitimer : Kernel.MilliTimer; dword : SET;
  763. BEGIN
  764. SYSTEM.PUT32(iobase + HcCommandStatus, HcCmdHostControllerReset); FlushPCI;
  765. Kernel.SetTimer(millitimer, 10);
  766. LOOP (* OpenHCI Spec.: Controller must clear this bit within 10 microseconds. We allow 10ms *)
  767. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcCommandStatus));
  768. IF (HcCmdHostControllerReset * dword = {}) OR Kernel.Expired(millitimer) THEN EXIT END;
  769. END;
  770. RETURN HcCmdHostControllerReset * dword = {};
  771. END SoftwareReset;
  772. (* initialization of the data structures of the Host Controller Communication Area; Only useb by Init(...) *)
  773. PROCEDURE InitHcca(): BOOLEAN;
  774. VAR i, k, j : LONGINT;
  775. BEGIN
  776. (* HCCA needs to be 256-byte aligned *)
  777. hcca := UsbHcdi.GetAlignedMemSpace(256, 4096); (* May not cross page boundary *)
  778. (* okay... now allocate memory for the 16byte aligned queue heads ... *)
  779. tdlist := UsbHcdi.GetAlignedMemSpace(16*TdListSize, 16);
  780. NEW(interruptED); FOR i := 0 TO 5 DO interruptED[i] := Machine.Ensure32BitAddress (ADDRESSOF(tdlist.data[tdlist.base]) + i*16); END;
  781. isochronousED := interruptED[5] + 16;
  782. controlED := isochronousED + 16;
  783. bulkED := controlED +16;
  784. (* if the HeadP and the TailP pointers of a ED both points to nullQueue, there are no TDs in that ED TD list *)
  785. nullTD := bulkED + 16;
  786. (* set the Skip Flags, HeadP and TailP fields of all 66 ED's (63 interrupt + bulk + control + iso) *)
  787. FOR i := 0 TO 8 DO
  788. tdlist.data[tdlist.base + i*4 + (EdControlStatus DIV 4)] := SYSTEM.VAL(LONGINT, EdSkip);
  789. tdlist.data[tdlist.base + i*4 + (EdTailP DIV 4)] := nullTD;
  790. tdlist.data[tdlist.base + i*4 + (EdHeadP DIV 4)] := nullTD;
  791. tdlist.data[tdlist.base + i*4 + (EdNextEdP DIV 4)] := 0;
  792. END;
  793. (* tree structure:
  794. The host controller uses the lower 5 bits of the framenumber as index into an 32 entry head pointer table in the HCCA.
  795. We want them to point to 32 interrupt endpoint descriptors. These are schedule all 32ms.
  796. interrupt[0]: 1ms
  797. interrupt[1]: 2ms
  798. interrupt[2]: 4ms
  799. interrupt[3]: 8ms
  800. interrupt[4]: 16ms
  801. interrupt[5]: 32ms
  802. *)
  803. FOR i := 5 TO 1 BY -1 DO
  804. SYSTEM.PUT32(interruptED[i] + EdNextEdP, interruptED[i-1]);
  805. END;
  806. (* interrupt TD for 1ms points to isochronousTD *)
  807. SYSTEM.PUT32(interruptED[0] + EdNextEdP, isochronousED);
  808. FOR i := 0 TO 31 DO (* i is slot number, we want to calc the queue number (k) for this slot *)
  809. k := 0; j := i;
  810. LOOP (* k: counts '1' in j from the lsb until the first '0' *)
  811. IF (SYSTEM.VAL(SET, j) * {0}) = {} THEN EXIT; END;
  812. INC(k); j := j DIV 2;
  813. END;
  814. hcca.data[hcca.base + i] := interruptED[k];
  815. END;
  816. RETURN TRUE;
  817. END InitHcca;
  818. (* Initializes the host controller and builds up the data structures of the HCCA *)
  819. PROCEDURE Init(virtualbase, IRQ : LONGINT) : BOOLEAN;
  820. CONST MaxOwnershipChangeRequests = 100;
  821. VAR
  822. dword, hcRhDescriptorA, hcRhDescriptorB, Reg_HcFmInterval : SET;
  823. timer : Kernel.Timer;
  824. periodicStart : LONGINT;
  825. hcControl, temp, i : LONGINT;
  826. ignore : BOOLEAN;
  827. BEGIN
  828. IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Starting host controller initialization..."); KernelLog.Ln; END;
  829. iobase := virtualbase; irq := IRQ;
  830. DMAchaining := TRUE; sgListSize := ScatterGatherListSize;
  831. (* get revision of the open host controller : Bit 0..7 of the HcRevision register *)
  832. temp := SYSTEM.GET32(iobase + HcRevision);
  833. revision:= SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, temp) * HcRevRevision);
  834. IF revision #10H THEN
  835. KernelLog.String("UsbOhci: Revision of OHCI Programming Interface not supported."); KernelLog.Ln;
  836. RETURN FALSE;
  837. END;
  838. (* check whether the controller does have legacy support registers : Bit 8 of HcRevision register *)
  839. IF SYSTEM.VAL(SET, temp) * HcRevLegacySupport # {} THEN
  840. legacySupport := TRUE;
  841. END;
  842. (* save contents from HcFmInterval register ... *)
  843. Reg_HcFmInterval := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcFmInterval));
  844. (* Host controller already in use by other instances (SMM/BIOS driver) ? *)
  845. hcControl := SYSTEM.GET32(iobase + HcControl);
  846. IF (SYSTEM.VAL(SET, hcControl) * HcConInterruptRouting) # {} THEN (* Interrupts routed to SMI -> SMM driver active *)
  847. IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Host Controller initialization: SMM driver found"); KernelLog.Ln; END;
  848. SYSTEM.PUT32(iobase + HcCommandStatus, HcCmdOwnershipChangeRequest); FlushPCI;
  849. NEW(timer);
  850. (* the SMM driver should clear the OwnershipChangeRequest bit *)
  851. i := 0;
  852. LOOP
  853. temp := SYSTEM.GET32(iobase + HcControl);
  854. IF SYSTEM.VAL(SET, temp) * HcConInterruptRouting = {} THEN EXIT; END;
  855. INC(i);
  856. IF i > MaxOwnershipChangeRequests THEN EXIT; END;
  857. timer.Sleep(1);
  858. END;
  859. hcControl := temp;
  860. (* OwnerShipChange successful ? *)
  861. IF SYSTEM.VAL(SET, temp) * HcConInterruptRouting # {} THEN
  862. IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Host Controller initialization: SMM driver did not response to OwnerShipChangeRequest."); KernelLog.Ln; END;
  863. ELSE
  864. IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Host Controller initialization: OwnerShipChangeRequest succeeded."); KernelLog.Ln; END;
  865. END;
  866. ELSIF SYSTEM.VAL(SET, hcControl) * HcConHcFunctionalState # {} THEN (* USB state not UsbReset -> BIOS driver active *)
  867. IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Controller initialization: BIOS driver found"); KernelLog.Ln; END;
  868. ELSE (* neither SMM driver nor BIOS driver were active -> cold start *)
  869. IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Controller initialization: Cold start..."); KernelLog.Ln; END;
  870. END;
  871. IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Host Controller Initialization: USB Reset."); KernelLog.Ln; END;
  872. IF ~HardwareReset() THEN RETURN FALSE; END;
  873. (* Resets most registers. After a software reset, the HC is in mode UsbSuspend. *)
  874. IF ~SoftwareReset() THEN RETURN FALSE; END;
  875. (* restore contents of HcFmInterval register ... *)
  876. SYSTEM.PUT32(iobase + HcFmInterval, Reg_HcFmInterval);
  877. (* According to the OHCIspec, restoring the contents of HcFmInterval should be enough. Nevertheless,
  878. check its contents to be sure it really works *)
  879. IF (Reg_HcFmInterval * HcFmiFrameInterval = {}) OR (Reg_HcFmInterval * HcFmiFsLargestDataPacket = {}) THEN
  880. IF Debug.Trace & Debug.traceInit THEN
  881. KernelLog.String("UsbOhci: HcFmInterval values invalid. Set to defaults."); KernelLog.Ln;
  882. END;
  883. IF (Reg_HcFmInterval * HcFmiFrameInterval = {}) THEN (* set to default *)
  884. Reg_HcFmInterval := Reg_HcFmInterval + (SYSTEM.VAL(SET, HccFrameInterval) * HcFmiFrameInterval);
  885. END;
  886. IF (Reg_HcFmInterval * HcFmiFsLargestDataPacket = {}) THEN (* set to default *)
  887. Reg_HcFmInterval := Reg_HcFmInterval + (LSH(SYSTEM.VAL(SET, HccFSLargestDataPacket), 16) * HcFmiFsLargestDataPacket);
  888. END;
  889. END;
  890. (* tell the HC that we updated the field *)
  891. IF (Reg_HcFmInterval * HcFmiFrameIntervalToggle = {}) THEN
  892. Reg_HcFmInterval := Reg_HcFmInterval + HcFmiFrameIntervalToggle;
  893. ELSE
  894. Reg_HcFmInterval := Reg_HcFmInterval - HcFmiFrameIntervalToggle;
  895. END;
  896. SYSTEM.PUT32(iobase + HcFmInterval, Reg_HcFmInterval);
  897. IF legacySupport THEN
  898. (* diable legacy emulation if enabled *)
  899. temp := SYSTEM.GET32(iobase + HceControl);
  900. IF SYSTEM.VAL(SET, temp) * {0} # {} THEN
  901. (* disable legacy emulation *)
  902. SYSTEM.PUT32(iobase + HceControl, SYSTEM.VAL(SET, temp) * HceControlReserved); FlushPCI;
  903. IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Disabled legacy emulation."); KernelLog.Ln; END;
  904. END;
  905. ELSE
  906. legacySupport := FALSE;
  907. END;
  908. (* determine the number of ports the root hub provides *)
  909. hcRhDescriptorA := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcRhDescriptorA));
  910. portCount := SYSTEM.VAL(LONGINT, hcRhDescriptorA * HcRhaNumberDownstreamPorts);
  911. IF (portCount < 1) OR (portCount > 15) THEN
  912. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: Host Controller Initialization failed (port count error)."); KernelLog.Ln; END;
  913. RETURN FALSE;
  914. END;
  915. NEW(ports, portCount);
  916. (* Calculate offset from iobase of the port status/controll register for each port *)
  917. FOR i := 0 TO portCount-1 DO ports[ i ] := iobase + HcRhPortStatus1 + i*4; END;
  918. (* Build the emulated hub descriptor *)
  919. NEW(hubDescriptor, 8);
  920. hubDescriptor[0] := CHR(7);
  921. hubDescriptor[1] := CHR(29H); (* Hub Descriptor *)
  922. hubDescriptor[2] := CHR(portCount);
  923. IF hcRhDescriptorA * HcRhaNoPowerSwitching # {} THEN (* Power switchting not supported *)
  924. dword := dword + {1};
  925. ELSIF hcRhDescriptorA * HcRhaPowerSwitchingMode # {} THEN (* Per port power switching *)
  926. dword := dword + {0};
  927. hcRhDescriptorB := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcRhDescriptorB)); (* preserve reserved bits ... *)
  928. hcRhDescriptorB := hcRhDescriptorB + HcRhbPortPowerControlMask;
  929. SYSTEM.PUT32(iobase + HcRhDescriptorB, hcRhDescriptorB - HcRhbDeviceRemovable); FlushPCI;
  930. ELSE (* Ganged power switching *)
  931. globalPowerSwitching := TRUE;
  932. END;
  933. IF hcRhDescriptorA * HcRhaNoOverCurrentProtection # {} THEN (* Overcurrent protection not supported *)
  934. dword := dword + {4};
  935. ELSIF hcRhDescriptorA * HcRhaOverCurrentProtectionMode # {} THEN (* Per port overcurrent protection *)
  936. dword := dword + {3};
  937. ELSE (* Global overcurrent protection *)
  938. (* do nothing *)
  939. END;
  940. hubDescriptor[3] := CHR(SYSTEM.VAL(LONGINT, dword));
  941. hubDescriptor[4] := CHR(0); (* Reserved *)
  942. hubDescriptor[5] := CHR(SYSTEM.VAL(LONGINT, LSH(hcRhDescriptorA * HcRhaPowerOnToPowerGoodTime, -24)));
  943. hubDescriptor[6] := CHR(0); (* Root hubs don't draw current from the USB *)
  944. (* HC now in UsbSuspend state... if we do not change the HC functional state to UsbOperational within 2ms, we need to go into UsbResume... *)
  945. IF ~InitHcca() THEN
  946. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: Initialization of HCCA failed."); KernelLog.Ln; END;
  947. RETURN FALSE;
  948. END;
  949. (* try to start the host controller *)
  950. IF Start() = FALSE THEN (* ERROR: Couldn't start the host controller. Controller was probably not correctly initialized. *)
  951. ignore := SoftwareReset(); KernelLog.String("UsbOhci: Couldn't start host controller."); KernelLog.Ln; RETURN FALSE;
  952. END;
  953. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcFmInterval));
  954. IF (dword # Reg_HcFmInterval) THEN (* quirk: Some HCs need to be in the state operational for this change to be effective *)
  955. SYSTEM.PUT32(iobase + HcFmInterval, Reg_HcFmInterval);
  956. END;
  957. (* Set HcPeriodicStart to a value that is 90% of the value in FrameInterval field of the HcFmInterval register *)
  958. periodicStart := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, Reg_HcFmInterval) * HcFmiFrameInterval);
  959. periodicStart := (periodicStart * 9) DIV 10;
  960. dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcPeriodicStart)); (* we need to preserve reserved bits... *)
  961. dword := dword - HcPerPeriodicStart + (SYSTEM.VAL(SET, periodicStart) * HcPerPeriodicStart);
  962. SYSTEM.PUT32(iobase + HcPeriodicStart, dword);
  963. RETURN TRUE;
  964. END Init;
  965. (* Resets and then starts the host controller. As soon as the host controller is started, it processes the schedule *)
  966. PROCEDURE Start():BOOLEAN;
  967. VAR dword : SET;
  968. BEGIN
  969. IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Starting host controller..."); END;
  970. (* set base address of the HCCA *)
  971. SYSTEM.PUT32(iobase + HcHCCA, ADDRESSOF(hcca.data[hcca.base]));
  972. (* set the bulk list and the control list head pointers *)
  973. SYSTEM.PUT32(iobase + HcControlHeadED, controlED);
  974. SYSTEM.PUT32(iobase + HcBulkHeadED, bulkED);
  975. SYSTEM.PUT32(iobase + HcControlCurrentED, 0);
  976. SYSTEM.PUT32(iobase + HcBulkCurrentED, 0);
  977. FlushPCI;
  978. SetState(UsbHcdi.Initialized);
  979. Objects.InstallHandler(InterruptHandler, Machine.IRQ0+irq);
  980. (* enable all interrupts *)
  981. dword := IntMasterInterruptEnable + IntHcDoneHeadWriteback + IntFrameNumberOverflow + IntUnrecoverableError + IntOwnerShipChange + IntRootHubStatusChange;
  982. SYSTEM.PUT32(iobase + HcInterruptEnable, dword); FlushPCI;
  983. (* Set HcControl Register: Start the controller by set the hc functional state to UsbOperational *)
  984. (* ControlBulkServiceRatio = 1:1 (Bit0&Bit1: 00); Enable List Processing; UsbOperational (Bit 7) *)
  985. dword := HcConPeriodicListEnable (*+ HcConIsochronousEnable *) + HcConControlListEnable + HcConBulkListEnable + {7};
  986. SYSTEM.PUT32(iobase + HcControl, dword); FlushPCI;
  987. SetState(UsbHcdi.Operational);
  988. IF Debug.Trace & Debug.traceInit THEN KernelLog.String("done."); KernelLog.Ln; END;
  989. RETURN TRUE;
  990. END Start;
  991. (* PCI writes may be posted. A read forces posted writes to be flushed before the read transaction is proceeded. *)
  992. PROCEDURE FlushPCI;
  993. VAR ignore : LONGINT;
  994. BEGIN
  995. ignore := SYSTEM.GET32(iobase + HcControl)
  996. END FlushPCI;
  997. PROCEDURE Cleanup;
  998. BEGIN
  999. IF state >= UsbHcdi.Initialized THEN Objects.RemoveHandler(InterruptHandler, Machine.IRQ0 + irq);END;
  1000. Cleanup^;
  1001. IF ~HardwareReset() THEN
  1002. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: Host controller reset failed."); KernelLog.Ln; END;
  1003. END;
  1004. IF ~SoftwareReset() THEN
  1005. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: Software reset failed."); KernelLog.Ln; END;
  1006. END;
  1007. (* Unmap the HC's operational registers *)
  1008. Machine.UnmapPhysical(iobase, 4096);
  1009. END Cleanup;
  1010. PROCEDURE & Default * (bus, device, function : LONGINT);
  1011. BEGIN
  1012. Default^(bus, device, function);
  1013. pipes[0, 0, 0].maxPacketSize := 8;
  1014. END Default;
  1015. (** Displays the host controller's data struture on KernelLog *)
  1016. PROCEDURE ShowSchedule*;
  1017. BEGIN
  1018. IF Debug.Trace THEN
  1019. KernelLog.String("Host Controller Data Structures for ");
  1020. KernelLog.String(name); KernelLog.String(" ("); KernelLog.String(desc); KernelLog.String("):"); KernelLog.Ln;
  1021. HumanSchedule(SELF);
  1022. END;
  1023. END ShowSchedule;
  1024. PROCEDURE ShowPipe*(pipe : UsbHcdi.Pipe);
  1025. BEGIN
  1026. HumanED(pipe.qh, SELF, 4, FALSE, FALSE);
  1027. HumanTD(pipe.firstTD, pipe.lastTD, nullTD, 8);
  1028. END ShowPipe;
  1029. (* for debugging: display diagnostics of this host controller to KernelLog *)
  1030. PROCEDURE Diag*;
  1031. VAR s : SET; temp : LONGINT;
  1032. BEGIN
  1033. IF Debug.Trace THEN
  1034. Diag^;
  1035. (* display information of the HcRevision register *)
  1036. temp := SYSTEM.GET32(iobase+HcRevision);
  1037. KernelLog.String(" HcRevision: ");
  1038. KernelLog.Hex(SYSTEM.VAL(LONGINT, LSH(SYSTEM.VAL(SET, temp) * {4..7}, -4)), -2); KernelLog.String(".");
  1039. KernelLog.Hex(SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, temp) * {0..3}), -2);
  1040. KernelLog.String(" Legacy support: ");
  1041. IF HcRevLegacySupport * SYSTEM.VAL(SET, temp) # {} THEN
  1042. KernelLog.String("Yes");
  1043. KernelLog.String(" [HceControl: "); temp := SYSTEM.GET32(iobase+HceControl); KernelLog.Hex(temp, 8); KernelLog.String("H]");
  1044. KernelLog.String(" [HceStatus: "); temp := SYSTEM.GET32(iobase+HceStatus); KernelLog.Hex(temp, 8); KernelLog.String("H]");
  1045. KernelLog.String(" [HceInput: "); temp := SYSTEM.GET32(iobase+HceInput); KernelLog.Hex(temp, 8); KernelLog.String("H]");
  1046. KernelLog.String(" [HceOutput: "); temp := SYSTEM.GET32(iobase+HceOutput); KernelLog.Hex(temp, 8); KernelLog.String("H]");
  1047. ELSE
  1048. KernelLog.String("No");
  1049. END;
  1050. KernelLog.Ln;
  1051. (* display information of the HcControl register *)
  1052. KernelLog.String(" HcControl: State: ");
  1053. temp := SYSTEM.GET32(iobase + HcControl);
  1054. s := SYSTEM.VAL(SET, temp);
  1055. CASE SYSTEM.VAL(LONGINT, LSH(s*HcConHcFunctionalState, -6)) OF
  1056. UsbReset : KernelLog.String("UsbReset");
  1057. | UsbResume : KernelLog.String("UsbResume");
  1058. | UsbOperational : KernelLog.String("UsbOperational");
  1059. | UsbSuspend : KernelLog.String("UsbSuspend");
  1060. ELSE
  1061. KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(s*HcConHcFunctionalState,-6)),0);
  1062. END;
  1063. KernelLog.String(" C/B-Ratio: ");
  1064. temp := SYSTEM.VAL(LONGINT, s * HcConControlBulkServiceRatio);
  1065. KernelLog.Int(temp, 0); KernelLog.String(": 1,");
  1066. KernelLog.String(" Flags: ");
  1067. IF s * HcConPeriodicListEnable # {} THEN KernelLog.String(" [PeriodicListEnabled]"); END;
  1068. IF s * HcConControlListEnable # {} THEN KernelLog.String(" [ControlListEnabled]"); END;
  1069. IF s * HcConBulkListEnable # {} THEN KernelLog.String(" [BulkListEnabled]"); END;
  1070. IF s * HcConIsochronousEnable # {} THEN KernelLog.String(" [IsochronousEnabled]"); END;
  1071. IF s * HcConInterruptRouting # {} THEN KernelLog.String(" [InterruptRouting]"); END;
  1072. IF s * HcConRemoteWakeupConnected # {} THEN KernelLog.String(" [RemoteWakeupConnected]"); END;
  1073. IF s * HcConRemoteWakeupEnable # {} THEN KernelLog.String(" [RemoteWakeupEnabled]"); END;
  1074. KernelLog.Ln;
  1075. (* display information from HcCommandStatus register *)
  1076. KernelLog.String(" HcCommandStatus: SchedulingOverrungCount: ");
  1077. KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(s * HcCmdSchedulingOverrunCount,16)), 0);
  1078. KernelLog.String(", Flags: ");
  1079. temp := SYSTEM.GET32(iobase + HcCommandStatus);
  1080. s := SYSTEM.VAL(SET, temp);
  1081. IF s * HcCmdHostControllerReset # {} THEN KernelLog.String("[Reset]"); END;
  1082. IF s * HcCmdControlListFilled # {} THEN KernelLog.String("[ControlListFilled]"); END;
  1083. IF s * HcCmdBulkListFilled # {} THEN KernelLog.String("[BulkListFilled]"); END;
  1084. IF s * HcCmdOwnershipChangeRequest # {} THEN KernelLog.String("[OwnerShipRequest]"); END;
  1085. KernelLog.Ln;
  1086. (* HcInterruptEnable register *)
  1087. temp := SYSTEM.GET32(iobase + HcInterruptEnable);
  1088. s := SYSTEM.VAL(SET, temp);
  1089. KernelLog.String(" Interrupts Enabled: ");
  1090. IF s * IntMasterInterruptEnable # {} THEN KernelLog.String("[MasterInterrupt]"); END;
  1091. IF s * IntSchedulingOverrun # {} THEN KernelLog.String("[SchedulingOverflow]"); END;
  1092. IF s * IntHcDoneHeadWriteback # {} THEN KernelLog.String("[WBDoneHead]"); END;
  1093. IF s * IntStartOfFrame # {} THEN KernelLog.String("[SOF]"); END;
  1094. IF s * IntResumeDetect # {} THEN KernelLog.String("[ResumeDetect]"); END;
  1095. IF s * IntUnrecoverableError # {} THEN KernelLog.String("[Error]"); END;
  1096. IF s * IntFrameNumberOverflow # {} THEN KernelLog.String("[FmOverflow]"); END;
  1097. IF s * IntRootHubStatusChange # {} THEN KernelLog.String("[RHStatusChange]"); END;
  1098. IF s * IntOwnerShipChange # {} THEN KernelLog.String("[OwnerShipChange]"); END;
  1099. KernelLog.Ln;
  1100. (* display information from HcInterruptStatus register *)
  1101. temp := SYSTEM.GET32(iobase + HcInterruptStatus);
  1102. s := SYSTEM.VAL(SET, temp);
  1103. KernelLog.String(" HcInterruptStatus: ");
  1104. IF s # {} THEN
  1105. IF s * HcIntSchedulingOverrun # {} THEN KernelLog.String("[SchedulingOverrun]"); END;
  1106. IF s * HcIntWriteBackDoneHead # {} THEN KernelLog.String("[WriteBackDoneHead]"); END;
  1107. IF s * HcIntStartOfFrame # {} THEN KernelLog.String("[StartOfFrame]"); END;
  1108. IF s * HcIntResumeDetected # {} THEN KernelLog.String("[ResumeDetected]"); END;
  1109. IF s * HcIntUnrecoverableError # {} THEN KernelLog.String("[UnrecoverableError]"); END;
  1110. IF s * HcIntFrameNumberOverflow # {} THEN KernelLog.String("[FrameNumberOverflow]"); END;
  1111. IF s * HcIntRootHubStatusChange # {} THEN KernelLog.String("[RooHubStatusChange]"); END;
  1112. IF s * HcIntOwnerShipChange # {} THEN KernelLog.String("[IntOwnerShipChange]"); END;
  1113. ELSE
  1114. KernelLog.String(" [ok]");
  1115. END;
  1116. KernelLog.Ln;
  1117. (* display current framenumber *)
  1118. temp := SYSTEM.GET32(iobase + HcFmNumber);
  1119. KernelLog.String(" Frame: Nbr: "); KernelLog.Hex(temp, 8);
  1120. s := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcFmInterval));
  1121. KernelLog.String(" FmInterval: "); KernelLog.Int(SYSTEM.VAL(LONGINT, s * HcFmiFrameInterval), 0);
  1122. KernelLog.String(" FSMaxPacketSize: "); KernelLog.Int(LSH(SYSTEM.VAL(LONGINT, s * HcFmiFsLargestDataPacket),-16), 0); KernelLog.String(" Bits, ");
  1123. KernelLog.String(" FmiToggle: ");
  1124. IF s * HcFmiFrameIntervalToggle # {} THEN KernelLog.String("Set"); ELSE KernelLog.String("Not Set"); END;
  1125. temp := SYSTEM.GET32(iobase + HcPeriodicStart); KernelLog.String(" PeriodicStart: "); KernelLog.Hex(temp, 8);
  1126. temp := SYSTEM.GET32(iobase + HcPeriodCurrentED); KernelLog.String(" PeriodicCurrentED: "); KernelLog.Hex(temp, 8);
  1127. KernelLog.Ln;
  1128. (* display list head addresses *)
  1129. temp := SYSTEM.GET32(iobase + HcControlHeadED); KernelLog.String(" ControlHeadED: "); KernelLog.Hex(temp, 8);
  1130. temp := SYSTEM.GET32(iobase + HcControlCurrentED); KernelLog.String(" CurrentControlED: "); KernelLog.Hex(temp, 8);
  1131. temp := SYSTEM.GET32(iobase + HcBulkHeadED); KernelLog.String(" BulkHeadED: "); KernelLog.Hex(temp, 8);
  1132. temp := SYSTEM.GET32(iobase + HcBulkCurrentED); KernelLog.String(" CurrentBulkED: "); KernelLog.Hex(temp, 8);
  1133. KernelLog.Ln;
  1134. END;
  1135. END Diag;
  1136. END OpenHostController;
  1137. VAR
  1138. qhCounter : LONGINT; (* used by HumanQH; only for debug puposes *)
  1139. (* debug: displays the information in the queue head qh and all TD's in that queue
  1140. * ed : virtual address if first ED in EDList *)
  1141. PROCEDURE HumanSchedule(controller : OpenHostController);
  1142. BEGIN
  1143. IF Debug.Trace THEN
  1144. HumanED(controller.interruptED[5], controller, 4, TRUE, TRUE);
  1145. HumanED(controller.bulkED, controller, 4, TRUE, TRUE);
  1146. HumanED(controller.controlED, controller, 4, TRUE, TRUE);
  1147. (* reset qhCounter *)
  1148. qhCounter := 0;
  1149. END;
  1150. END HumanSchedule;
  1151. PROCEDURE Indent(spaces : LONGINT);
  1152. VAR i : LONGINT;
  1153. BEGIN
  1154. FOR i := 1 TO spaces DO KernelLog.Char(" "); END;
  1155. END Indent;
  1156. (* Displays endpoint descriptor information to the kernel log; addr : Physical Address!! *)
  1157. PROCEDURE HumanED(ed : LONGINT; controller : OpenHostController; spaces : LONGINT; showTds, showEds : BOOLEAN);
  1158. VAR dword : SET; adr, ep : LONGINT; pipe : UsbHcdi.Pipe;
  1159. BEGIN
  1160. IF Debug.Trace THEN
  1161. (* to avoid endless loops because of (wrong) circular data structures *)
  1162. IF qhCounter > ShowScheduleMaxQH THEN
  1163. KernelLog.String("UsbOhci: HumanED: UsbOhci.ShowScheduleMaxQH showed... aborting."); KernelLog.Ln;
  1164. RETURN;
  1165. ELSE
  1166. INC(qhCounter);
  1167. END;
  1168. ed := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, ed) * {4..31});
  1169. dword :=SYSTEM.VAL(SET, SYSTEM.GET32(ed + EdControlStatus));
  1170. adr := SYSTEM.VAL(LONGINT, dword * EdFunctionAddress);
  1171. ep := SYSTEM.VAL(LONGINT, LSH(dword * EdEndpointNumber, -7));
  1172. Indent(spaces); KernelLog.String("ED at address: "); KernelLog.Hex(ed, 8); KernelLog.String("H ");
  1173. IF ed = controller.controlED THEN KernelLog.String("(ControlList Head)");
  1174. ELSIF ed = controller.bulkED THEN KernelLog.String("(BulkList Head)");
  1175. ELSIF ed = controller.isochronousED THEN KernelLog.String("(IsochronousList Head)");
  1176. ELSIF ed = controller.interruptED[0] THEN KernelLog.String("(1ms Interrupt Head)");
  1177. ELSIF ed = controller.interruptED[1] THEN KernelLog.String("(2ms Interrupt Head)");
  1178. ELSIF ed = controller.interruptED[2] THEN KernelLog.String("(4ms Interrupt Head)");
  1179. ELSIF ed = controller.interruptED[3] THEN KernelLog.String("(8ms Interrupt Head)");
  1180. ELSIF ed = controller.interruptED[4] THEN KernelLog.String("(16ms Interrupt Head)");
  1181. ELSIF ed = controller.interruptED[5] THEN KernelLog.String("(32ms Interrupt Head)");
  1182. ELSE
  1183. KernelLog.String("(USB Pipe)");
  1184. END;
  1185. KernelLog.Ln;
  1186. (* Display information from EdControlStatus field *)
  1187. Indent(spaces+4);
  1188. KernelLog.String("Adr: "); KernelLog.Int(adr, 0); KernelLog.String(" Ep: "); KernelLog.Int(ep, 0);
  1189. IF pipe # NIL THEN
  1190. KernelLog.String(" Type: ");
  1191. CASE pipe.type OF
  1192. UsbHcdi.PipeControl : KernelLog.String("Control");
  1193. | UsbHcdi.PipeBulk : KernelLog.String("Bulk");
  1194. | UsbHcdi.PipeIsochronous : KernelLog.String("Isochronous");
  1195. | UsbHcdi.PipeInterrupt : KernelLog.String("Interrupt");
  1196. ELSE
  1197. KernelLog.String("UNKNOWN!!!");
  1198. END;
  1199. END;
  1200. KernelLog.String(" Dir: ");
  1201. CASE SYSTEM.VAL(LONGINT, LSH(dword * EdDirection ,-11)) OF
  1202. PidIn : KernelLog.String("In");
  1203. | PidOut : KernelLog.String("Out");
  1204. ELSE
  1205. KernelLog.String("Specified in TD");
  1206. END;
  1207. KernelLog.String(" MaxPacketSize: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * EdMaximumPacketSize, -16)), 0); KernelLog.String(" Bytes");
  1208. IF pipe # NIL THEN
  1209. KernelLog.String(", LastStatus: "); UsbHcdi.ShowStatus(pipe.status);
  1210. END;
  1211. KernelLog.String(", Flags: ");
  1212. IF EdSkip * dword # {} THEN KernelLog.String("[Skip]"); END;
  1213. IF EdSpeed * dword # {} THEN KernelLog.String("[LowSpeed]"); ELSE KernelLog.String("[FullSpeed]"); END;
  1214. IF EdFormat * dword # {} THEN KernelLog.String("[Isochronous]"); END;
  1215. dword := SYSTEM.VAL(SET, SYSTEM.GET32(ed + EdHeadP));
  1216. IF EdHalted * dword # {} THEN KernelLog.String("[Halted]"); END;
  1217. IF EdToggleCarry * dword # {} THEN KernelLog.String("[Toggle=DATA1]"); ELSE KernelLog.String("[Toggle=DATA0]"); END;
  1218. KernelLog.Ln;
  1219. Indent(spaces+4); KernelLog.String("HeadP: ");
  1220. dword := SYSTEM.VAL(SET, SYSTEM.GET32(ed + EdHeadP)) * {4..31};
  1221. IF SYSTEM.VAL(LONGINT, dword) = controller.nullTD THEN
  1222. KernelLog.String("NullTD");
  1223. ELSE
  1224. KernelLog.Hex(SYSTEM.VAL(LONGINT, dword * {4..31}), 8); KernelLog.String("H");
  1225. END;
  1226. KernelLog.String(", TailP: ");
  1227. dword := SYSTEM.VAL(SET, SYSTEM.GET32(ed + EdTailP));
  1228. IF SYSTEM.VAL(LONGINT, dword) = controller.nullTD THEN
  1229. KernelLog.String("NullTD");
  1230. ELSE
  1231. KernelLog.Hex(SYSTEM.VAL(LONGINT, dword * {4..31}), 8); KernelLog.String("H");
  1232. END;
  1233. dword := SYSTEM.VAL(SET, SYSTEM.GET32(ed + EdNextEdP));
  1234. KernelLog.String(", NextED: ");
  1235. IF dword = {} THEN
  1236. KernelLog.String("None");
  1237. ELSE
  1238. KernelLog.Hex(SYSTEM.VAL(LONGINT, dword * {4..31}), 8); KernelLog.String("H");
  1239. END;
  1240. KernelLog.Ln;
  1241. (* Show TD list of this endpoint descriptor *)
  1242. Indent(spaces+4); KernelLog.String("TDList: ");
  1243. dword := SYSTEM.VAL(SET, SYSTEM.GET32(ed + EdHeadP)) * {4..31};
  1244. IF SYSTEM.VAL(SET, SYSTEM.GET32(ed + EdTailP)) * {4..31} = dword THEN (* HeadP == TailP *)
  1245. KernelLog.String("Empty"); KernelLog.Ln;
  1246. ELSE (* There are TD's in the list ... show TD list. *)
  1247. KernelLog.String("Non_Empty - First TD at "); KernelLog.Hex(SYSTEM.VAL(LONGINT, dword * {4..31}), 8); KernelLog.Char("H"); KernelLog.Ln; KernelLog.Ln;
  1248. IF showTds THEN
  1249. KernelLog.Ln; HumanTD(SYSTEM.VAL(LONGINT, dword * {4..31}), 0, controller.nullTD, spaces + 4); KernelLog.Ln;
  1250. END;
  1251. END;
  1252. (* Show next endpoint descriptor in list *)
  1253. IF showEds THEN
  1254. dword := SYSTEM.VAL(SET, SYSTEM.GET32(ed + EdNextEdP)) * {4..31}; (* get NextED field *)
  1255. IF dword # {} THEN
  1256. HumanED(SYSTEM.VAL(LONGINT, dword), controller, 4, showTds, showEds);
  1257. END;
  1258. END;
  1259. END;
  1260. END HumanED;
  1261. (* debug: displays the information of the Transfer Descriptor nexttd and all linked TD's and displays it *)
  1262. (* nextd : Physical Address !!! *)
  1263. PROCEDURE HumanTD(nexttd, lasttd, nulltd: LONGINT; spaces : LONGINT);
  1264. CONST MaxTDListLength = 200;
  1265. VAR val, val2 : LONGINT; dword : SET; iteration : LONGINT;
  1266. BEGIN
  1267. IF Debug.Trace THEN
  1268. iteration := 1;
  1269. LOOP
  1270. IF iteration > MaxTDListLength THEN (* for the case that addresses are misinterpreted ... *)
  1271. Indent(spaces); KernelLog.String("UsbOhci.HumandTD.MaxTDListLength reached... aborting.");
  1272. EXIT;
  1273. END;
  1274. Indent(spaces); KernelLog.String("TD at address: "); KernelLog.Hex(nexttd, 8); KernelLog.String("H"); KernelLog.Ln;
  1275. IF SYSTEM.VAL(SET, nexttd) * {0..3} # {} THEN
  1276. Indent(spaces); KernelLog.String("Error: not 16byte aligned!!");
  1277. EXIT;
  1278. ELSIF nexttd = 0 THEN
  1279. Indent(spaces); KernelLog.String("Error: Address = 0 !!!"); KernelLog.Ln;
  1280. EXIT;
  1281. ELSE
  1282. (* TD Completion Codes *)
  1283. Indent(spaces+4); KernelLog.String("Condition Codes:");
  1284. dword := SYSTEM.VAL(SET, SYSTEM.GET32(nexttd + TdCommand));
  1285. CASE SYSTEM.VAL(LONGINT, LSH(dword,-28)) OF
  1286. | TdNoError : KernelLog.String("[NoError]");
  1287. | TdCrc : KernelLog.String("[CRC]");
  1288. | TdBitStuffing : KernelLog.String("[BitStuffError]");
  1289. | TdDataToggleMismatch : KernelLog.String("[DataToggleMismatch]");
  1290. | TdStall : KernelLog.String("[STALL]");
  1291. | TdDeviceNotResponding : KernelLog.String("[DeviceNotResponding]");
  1292. | TdPidCheckFailure : KernelLog.String("[PidCheckFailure]");
  1293. | TdUnexpectedPid : KernelLog.String("[UnexpectedPid]");
  1294. | TdDataOverrun : KernelLog.String("[DataOverrun]");
  1295. | TdDataUnderrun : KernelLog.String("[DataUnderrun]");
  1296. | TdBufferOverrun : KernelLog.String("[BufferOverrun]");
  1297. | TdBufferUnderrun : KernelLog.String("[BufferUnderrun]");
  1298. | 14..15: KernelLog.String("[NotAccessed]");
  1299. ELSE
  1300. KernelLog.String("[ConditionCodesNotValid]");
  1301. END;
  1302. KernelLog.Ln;
  1303. Indent(spaces+4); KernelLog.String("Pid: ");
  1304. CASE SYSTEM.VAL(LONGINT, LSH(dword * TdDirectionPid, -19)) OF
  1305. PidSetup : KernelLog.String("PidSetup");
  1306. | PidIn : KernelLog.String("PidIn");
  1307. | PidOut : KernelLog.String("PidOut");
  1308. | 3 : KernelLog.String("Reserved");
  1309. ELSE
  1310. KernelLog.String("Invalid");
  1311. END;
  1312. KernelLog.String(", Flags: ");
  1313. IF dword * TdBufferRounding # {} THEN KernelLog.String("[BufferRounding]"); END;
  1314. IF 25 IN dword THEN KernelLog.String("[ToggleFromTD]"); ELSE KernelLog.String("[ToggleFromEd]"); END;
  1315. IF 24 IN dword THEN KernelLog.String("[Toggle=DATA1]"); ELSE KernelLog.String("[Toggle=DATA0]"); END;
  1316. IF SYSTEM.VAL(LONGINT, LSH(dword * TdDelayInterrupt, -21)) # 7 THEN (* IOC enabled *)
  1317. KernelLog.String("[IOC, MaxDelay: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * TdDelayInterrupt, -21)), 0); KernelLog.Char("]");
  1318. END;
  1319. KernelLog.String(", Errors: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * TdErrorCount, -26)), 0);
  1320. KernelLog.Ln;
  1321. (* CurrentBufferPointer, BufferEndPointer & NextTD *)
  1322. Indent(spaces+4); KernelLog.String("CurBufferP: ");
  1323. val := SYSTEM.GET32(nexttd + TdCurrentBufferP); KernelLog.Hex(val, 8); KernelLog.String("H");
  1324. KernelLog.String(", BufferEnd: ");
  1325. val2 := SYSTEM.GET32(nexttd + TdBufferEndP); KernelLog.Hex(val2, 8); KernelLog.String("H");
  1326. KernelLog.String(" (");
  1327. IF val = 0 THEN KernelLog.String("0");
  1328. ELSE KernelLog.Int(val2 - val + 1, 0);
  1329. END;
  1330. KernelLog.String(" Bytes)");
  1331. val := SYSTEM.GET32(nexttd + TdNextTdP);
  1332. KernelLog.String(", NextTD: ");
  1333. IF val = nulltd THEN KernelLog.String("NullTD");
  1334. ELSE KernelLog.Hex(val, 8); KernelLog.String("H");
  1335. END;
  1336. KernelLog.Ln;
  1337. IF SYSTEM.GET32(nexttd + TdNextTdP) = nulltd THEN
  1338. Indent(spaces); KernelLog.String("NullTD (EOL)"); KernelLog.Ln; EXIT;
  1339. ELSIF nexttd = lasttd THEN
  1340. Indent(spaces); KernelLog.String("Pipe.lastTD (EOL)"); KernelLog.Ln; EXIT;
  1341. ELSIF nexttd = 0 THEN
  1342. Indent(spaces); KernelLog.String("nexttd adr is zero"); KernelLog.Ln; EXIT;
  1343. END;
  1344. KernelLog.Ln;
  1345. nexttd := nexttd + 16;
  1346. INC(iteration);
  1347. END;
  1348. END;
  1349. END;
  1350. END HumanTD;
  1351. (* Find UHCI controllers on the PCI bus, create correspondig UCHIcontroller object and register them in the UHCI USB host controllers registry;
  1352. called by FindControllers *)
  1353. PROCEDURE PCIFindOhci;
  1354. CONST
  1355. OhciClassCode = 0C0310H;
  1356. PCIStatusErrorMask = {24,27,28,29,30,31};
  1357. VAR
  1358. hostController : OpenHostController;
  1359. bus, device, function : LONGINT;
  1360. iobasePhys, irq : LONGINT;
  1361. iobaseVirt: ADDRESS;
  1362. pciCmdReg : LONGINT;
  1363. index : LONGINT;
  1364. res: LONGINT;
  1365. BEGIN
  1366. index := 0;
  1367. IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Looking for PCI Open Host Controllers..."); KernelLog.Ln; END;
  1368. (* traverse all USB Open Host Controllers of all PCI busses in the system *)
  1369. WHILE PCI.FindPCIClassCode(OhciClassCode, index, bus, device, function) = PCI.Done DO
  1370. res := PCI.ReadConfigDword(bus, device, function, PCI.CmdReg, pciCmdReg); ASSERT(res = PCI.Done);
  1371. IF SYSTEM.VAL(SET, pciCmdReg) * PCIStatusErrorMask # {} THEN
  1372. KernelLog.String("UsbOhci: PCI device is in error state."); KernelLog.Ln;
  1373. ELSIF PCI.Enable(PCI.MemorySpace + PCI.BusMaster, bus, device, function) # PCI.Done THEN
  1374. KernelLog.String("UsbOhci: Could not enable nus mastering or memory space access."); KernelLog.Ln;
  1375. ELSE
  1376. res := PCI.ReadConfigByte(bus, device, function, PCI.IntlReg, irq); ASSERT(res = PCI.Done);
  1377. res := PCI.ReadConfigDword(bus, device, function, PCI.Adr0Reg, iobasePhys); ASSERT(res = PCI.Done);
  1378. IF SYSTEM.VAL(SET, iobasePhys) * {0} # {} THEN
  1379. KernelLog.String("UsbOhci: Error: Operational Register are not memory mapped"); KernelLog.Ln;
  1380. ELSIF SYSTEM.VAL(SET, iobasePhys) * {1,2,3} # {} THEN
  1381. KernelLog.String("UsbOhci: Error: Operational Register are not correctly mapped "); KernelLog.Ln;
  1382. ELSIF irq = 0 THEN
  1383. KernelLog.String("UsbOhci: Error: Please enable interrupts for USB Host Controller."); KernelLog.Ln;
  1384. ELSE
  1385. (* OHCI Spec: iobase address are the higher 20 bits *)
  1386. iobasePhys := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, iobasePhys) * {12..31});
  1387. Machine.MapPhysical(iobasePhys, 4096, iobaseVirt);
  1388. NEW(hostController, bus, device, function);
  1389. IF hostController.Init(Machine.Ensure32BitAddress(iobaseVirt), irq) THEN (* host controller has been initialized and started successfully *)
  1390. IF Debug.Verbose THEN
  1391. KernelLog.Enter;
  1392. KernelLog.String("UsbOhci: Initialised USB Open Host Controller at base 0"); KernelLog.Hex(iobasePhys, 8);
  1393. KernelLog.String(", Irq: "); KernelLog.Int(irq, 0);
  1394. KernelLog.Exit;
  1395. END;
  1396. UsbHcdi.RegisterHostController(hostController, Description);
  1397. ELSE (* ERROR: host controller initialization failed *)
  1398. KernelLog.Enter;
  1399. KernelLog.String("UsbOhci: Cannot init USB Open Host Controller at base 0"); KernelLog.Hex(iobasePhys, 8);
  1400. KernelLog.String(", Irq: "); KernelLog.Int(irq, 0);
  1401. KernelLog.Exit;
  1402. END;
  1403. END;
  1404. END;
  1405. INC(index);
  1406. END; (* end while loop *)
  1407. END PCIFindOhci;
  1408. (* called when this module is unloaded *)
  1409. PROCEDURE Cleanup;
  1410. BEGIN
  1411. UsbHcdi.UnRegisterHostControllers(Description);
  1412. END Cleanup;
  1413. PROCEDURE Install*;
  1414. (* Load module *)
  1415. END Install;
  1416. BEGIN
  1417. Modules.InstallTermHandler(Cleanup);
  1418. (* Find, init and start all compatible UHCI USB host controllers and register them in the UsbHcdi.controllers registry *)
  1419. PCIFindOhci;
  1420. END UsbOhci.
  1421. UsbOhci.Install ~ SystemTools.Free UsbOhci ~