BIOS.UsbUhci.Mod 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107
  1. MODULE UsbUhci; (** AUTHOR "cplattner/staubesv"; PURPOSE "USB Universal Host Controller Driver" *)
  2. (**
  3. * Bluebottle UHCI USB Host Controller Driver
  4. * Implements the UsbHcdi host controller driver interface (HCDI)
  5. *
  6. * IMPORTANT NOTE:
  7. * The UHCI specification doesn't specify how to determine the number of downstream port of the root hub. The driver simply
  8. * assumes that you have two of these ports.
  9. * In the case you're HC implementation provides more port, you have to adapt the NumberOfPorts constant.
  10. *
  11. * Usage:
  12. *
  13. * UsbUhci.Install ~ to load this device driver
  14. * SystemTools.Free UsbUhci ~ unloads it
  15. *
  16. * References:
  17. * Universal Host Controller Interface (UHCI) Design Guide, Revision 1.1, http://www.intel.com
  18. *
  19. * History:
  20. *
  21. * 30.09.2000 First release (cp)
  22. * 18.10.2000 Bugfixes, added new debug support (cp)
  23. * 20.10.2000 Memory fragmentation fixed (cp)
  24. * 22.10.2000 TD allocation changed, IRQ spread fixed, irq-out bugfix (cp)
  25. * 10.11.2003 Introduction of host controller driver interface (HCDI), OOP, new per pipe memory allocation (staubesv)
  26. * 20.11.2005 PCIFindUhci changed to recognize UHCI HC's by their PCI class code, replaced SYSTEM.PUT/GET by SYSTEM.PUTxx/GETxx (staubesv)
  27. * 13.12.2005 Fixed UhciController.RestartPipe for ShortPacket conditions (staubesv)
  28. * 15.12.2005 Moved buffer checks to UsbHcdi.Mod (staubesv)
  29. * 05.01.2006 Abort tranfer when number of TDs is too small instead of trapping (staubesv)
  30. * 03.01.2006 Improved UnLinkTDs & RemoveQH (staubesv)
  31. * 08.03.2006 Added check in LinkTDs (staubesv)
  32. * 20.06.2006 Wait until HC has enabled port after setting the PortEnabled bit in procedure ResetPort (staubesv)
  33. * 28.06.2006 Use KernelLog.Hex instead of UsbHcdi.PrintHex (staubesv)
  34. * 03.07.2006 ResetAndEnablePort instead of two separate procedures (staubesv)
  35. * 03.08.2006 Adapted to UsbHcdi (LinkTDsAllowed) (staubesv)
  36. * 13.11.2006 UpdatePipeStatus: Set pipe.status to Usbdi.Stalled when a stall is detected (staubesv)
  37. * 05.01.2007 Added Interrupts tracing (staubesv)
  38. *
  39. * TODOs
  40. * - recognize number of downstream ports automatically
  41. * - isochronous transfers
  42. * - suspend/resume
  43. * - use aligned memory space provided by UsbHcdi instead of doing it self
  44. *)
  45. IMPORT SYSTEM, KernelLog, Machine, PCI, Kernel, Objects, Modules, UsbHcdi, Usbdi, Debug := UsbDebug;
  46. CONST
  47. Description = "UHCI USB Host Controller";
  48. (* Number of ports the host controller's root hub provide *)
  49. NumberOfPorts = 2;
  50. (* Offsets of USB registers from base io-address according UHCI design guide *)
  51. UsbCmd = 0H; (* command register*)
  52. UsbSts = 2H; (* status register*)
  53. UsbIntr = 4H; (* interupt enable*)
  54. FrNum = 6H; (* frame number *)
  55. FlBaseAdr = 8H; (* frame list base address *)
  56. SofMod = 0CH; (* start of frame modify *)
  57. PortSc1 = 10H; (* port 1 status / control *)
  58. PortSc2 = 12H; (* port 2 status / control *)
  59. (* Bits of the USB Command (UsbCmd) Register *)
  60. CmdMaxPacket = {7};
  61. CmdConfigureFlag = {6};
  62. CmdSoftwareDebug = {5};
  63. CmdForceGlobalResume = {4};
  64. CmdEnterGlobalSuspend = {3};
  65. CmdGlobalReset = {2};
  66. CmdHostControllerReset = {1};
  67. CmdRunStop = {0};
  68. (* Bits of the USB Status (UsbSts) Register *)
  69. StatusHChalted = {5};
  70. StatusProcessError = {4};
  71. StatusSystemError = {3};
  72. StatusResumeDetect = {2};
  73. StatusErrorInt = {1};
  74. StatusInt = {0};
  75. (* Bits of the USB Interrupt Enable (UsbIntr) Register *)
  76. IntShortPacket = {3};
  77. IntIOC = {2};
  78. IntResume = {1};
  79. IntTimeoutCRC = {0};
  80. (* Bits of the USB Port Status and Control (PortSc) Register *)
  81. PortSuspend = {12};
  82. PortReset = {9};
  83. PortLowSpeed = {8};
  84. PortResumeDetect = {6};
  85. PortLineStatus = {4,5};
  86. PortEnabledChange = {3};
  87. PortEnabled = {2};
  88. PortConnectStatusChange = {1};
  89. PortCurrentConnectStatus = {0};
  90. PortChangeMask = {1,3};
  91. (* Offsets of Queue Heads QH *)
  92. QhLinkPointer = 0;
  93. QhElementLinkPointer = 4;
  94. (* Fields of QhLinkPointer & QhElementLinkPointer *)
  95. QhTerminate = {0};
  96. QhTdSelect = {1};
  97. (* Offsets of 16byte transfer descriptors TDs *)
  98. TDLinkPointer = 0;
  99. TDControlStatus = 4;
  100. TDToken = 8;
  101. TDBufferPointer = 12;
  102. (* Bits of the TD Link Pointer DWORD*)
  103. TDTerminate = {0};
  104. TDQHSelect = {1};
  105. TDDepthFirst = {2};
  106. (* Bits of the TD Control and Status DWORD *)
  107. TDBitStuff = {17};
  108. TDCrcTimeout = {18};
  109. TDNak = {19};
  110. TDBabble = {20};
  111. TDDataBuffer = {21};
  112. TDStalled = {22};
  113. TDActive = {23};
  114. TDIOC = {24};
  115. TDIOS = {25};
  116. TDLS = {26};
  117. TDERR0 = {}; TDERR1 = {27}; TDERR2 = {28}; TDERR3 = {27,28};
  118. TDSPD = {29};
  119. (* Bits of the TD Token DWORD *)
  120. TDDataToggle = {19};
  121. (* TD Token Packet Identification (PID) field *)
  122. PidOut = 0E1H;
  123. PidIn = 069H;
  124. PidSetup = 02DH;
  125. (* 3: control-,bulk- and isochronousTD; 11: interruptTD[0..11]; 1: alignment *)
  126. TdListSize = 3 + 11 + 1;
  127. (* How many Queue Heads should the debug procedure ShowSchedule() show; useful if the schedule data structure is corrupted *)
  128. ShowScheduleMaxQH = 25;
  129. TYPE
  130. UhciFrameList = POINTER TO RECORD
  131. index : SIZE;
  132. framelistbase : Machine.Address32;
  133. field : ARRAY 2*1024 OF LONGINT; (* hack: double it, so that we pass a 4K page boundary; field must be 4K aligned *)
  134. END;
  135. UhciController = OBJECT (UsbHcdi.Hcd)
  136. VAR
  137. (* Serial Bus Specification Release Number (for PCI config space)*)
  138. bcdUSB : LONGINT;
  139. (* UHCI data structures specific *)
  140. framelist : UhciFrameList;
  141. (* queue heads; only exported for debug purposes *)
  142. controlTD : Machine.Address32;
  143. bulkTD : Machine.Address32;
  144. isochronousTD : Machine.Address32;
  145. interruptTD : ARRAY 11 OF Machine.Address32;
  146. (* This array will provide the 16byte aligned TD's for controlTD, bulkTD, isochronousTD and interruptTD[] *)
  147. tdlist : POINTER TO ARRAY OF CHAR;
  148. tdlistbase : Machine.Address32; (* first 16byte aligned TD in tdlist *)
  149. (** Enable power for the specified port *)
  150. PROCEDURE EnablePortPower*(port : LONGINT);
  151. (* Do nothing: Port power is always provided by UHCI host controllers *)
  152. END EnablePortPower;
  153. (** Disable power of the specified port *)
  154. PROCEDURE DisablePortPower*(port : LONGINT);
  155. (* Do nothing: Port power is always provided by UHCI host controllers *)
  156. END DisablePortPower;
  157. (** Reset and enable the specified port *)
  158. PROCEDURE ResetAndEnablePort*(port : LONGINT) : BOOLEAN;
  159. VAR status : INTEGER; mtimer : Kernel.MilliTimer;
  160. BEGIN
  161. (* reset port *)
  162. Machine.Portin16(ports[port], SYSTEM.VAL(INTEGER, status));
  163. Machine.Portout16(ports[port], SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, status) + PortReset - PortChangeMask));
  164. Wait(UsbHcdi.PortResetTime); (* at least 10ms [USB2.0spec, 11.5.1.5 Resetting] *)
  165. Machine.Portout16(ports[port], SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, status) - PortReset - PortChangeMask));
  166. Wait(2); (* Nowhere specified, but doesn't work without *)
  167. Machine.Portin16(ports[port], SYSTEM.VAL(INTEGER, status));
  168. IF SYSTEM.VAL(SET, status) * PortReset # {} THEN RETURN FALSE; END;
  169. (* enable port *)
  170. Machine.Portout16(ports[port], SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, status) + PortEnabled - PortChangeMask));
  171. Kernel.SetTimer(mtimer, UsbHcdi.PortEnableTimeout);
  172. REPEAT
  173. Machine.Portin16(ports[port], SYSTEM.VAL(INTEGER, status));
  174. UNTIL (SYSTEM.VAL(SET, status) * PortEnabled # {}) OR Kernel.Expired(mtimer);
  175. (* The HC will set PortConnectStatusChange & PortEnabledChange, clear it *)
  176. Machine.Portout16(ports[port], SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, status) + PortChangeMask));
  177. RETURN SYSTEM.VAL(SET, status) * PortEnabled # {};
  178. END ResetAndEnablePort;
  179. (** Disables port number <port> on this root hub *)
  180. PROCEDURE DisablePort*(port : LONGINT);
  181. VAR status : INTEGER; mtimer : Kernel.MilliTimer;
  182. BEGIN
  183. Machine.Portin16(ports[port], SYSTEM.VAL(INTEGER, status));
  184. Machine.Portout16(ports[port], SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, status) - PortEnabled - PortChangeMask));
  185. Kernel.SetTimer(mtimer, UsbHcdi.PortEnableTimeout);
  186. REPEAT
  187. Machine.Portin16(ports[port], SYSTEM.VAL(INTEGER, status));
  188. UNTIL (SYSTEM.VAL(SET, status) * PortEnabled = {}) OR Kernel.Expired(mtimer);
  189. (* The HC will set PortConnectStatusChange & PortEnabledChange, clear it *)
  190. Machine.Portout16(ports[port], SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, status) + PortChangeMask));
  191. END DisablePort;
  192. (**
  193. * Get the status of the port <port> of this root hub.
  194. * Registers which indicate changes are reset by GetPortStatus if ack = TRUE;
  195. *)
  196. PROCEDURE GetPortStatus*(port : LONGINT; ack : BOOLEAN):SET;
  197. VAR status, s : SET;
  198. BEGIN
  199. Machine.Portin16(ports[port], SYSTEM.VAL(INTEGER, s));
  200. (* reset the PortEnabledChange and the PortConnectStatusChange bit *)
  201. IF ack & (s * PortChangeMask # {}) THEN Machine.Portout16(ports[port], SYSTEM.VAL(INTEGER, s)); END;
  202. status := UsbHcdi.PortStatusPowered; (* UHCI root hub ports are always powered *)
  203. IF s * PortCurrentConnectStatus # {} THEN status := status + UsbHcdi.PortStatusDevicePresent END;
  204. IF s * PortConnectStatusChange # {} THEN status := status + UsbHcdi.PortStatusConnectChange; END;
  205. IF s * PortEnabled # {} THEN status := status + UsbHcdi.PortStatusEnabled END;
  206. IF s * PortEnabledChange # {} THEN status := status + UsbHcdi.PortStatusEnabledChange; END;
  207. IF s * PortReset # {} THEN status := status + UsbHcdi.PortStatusReset; END;
  208. IF s * PortSuspend # {} THEN status := status + UsbHcdi.PortStatusSuspended; END;
  209. IF s * PortLowSpeed # {} THEN
  210. status := status + UsbHcdi.PortStatusLowSpeed;
  211. ELSE
  212. status := status + UsbHcdi.PortStatusFullSpeed;
  213. END;
  214. RETURN status;
  215. END GetPortStatus;
  216. (* Since UHCI host controllers do not support port change notification via interrupts, they must be polled *)
  217. PROCEDURE SetStatusChangeHandler*(handler : UsbHcdi.StatusChangeHandler) : BOOLEAN;
  218. BEGIN
  219. RETURN FALSE;
  220. END SetStatusChangeHandler;
  221. (** The Host Controller sends the global reset signal on the USB and then resets all its logic, including the internal
  222. hub registers. The hub registers are reset to their power on state. *)
  223. PROCEDURE Reset;
  224. BEGIN {EXCLUSIVE}
  225. (* do a global reset for 50ms, disconnect all devices *)
  226. Machine.Portout16(iobase + UsbCmd, SYSTEM.VAL(INTEGER, CmdGlobalReset) ); Wait (50);
  227. Machine.Portout16(iobase + UsbCmd, SYSTEM.VAL(INTEGER, 0) ); Wait (10);
  228. END Reset;
  229. (* Returns the current frame number; the frame number is incremented by the Host Controller at the end of each frame time;
  230. * GetFrameNumber() is used for time stamps *)
  231. PROCEDURE GetFrameNumber*() : LONGINT;
  232. VAR frameNumber : INTEGER;
  233. BEGIN
  234. Machine.Portin16(iobase + FrNum, frameNumber);
  235. frameNumber := SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, frameNumber) * {0..10});
  236. RETURN SYSTEM.VAL(LONGINT, frameNumber);
  237. END GetFrameNumber;
  238. (** Removes the USB transfer request <req> from the host controller schedule *)
  239. PROCEDURE UnlinkTDs*(pipe : UsbHcdi.Pipe);
  240. BEGIN {EXCLUSIVE}
  241. UnlinkTDsInternal(pipe);
  242. END UnlinkTDs;
  243. (** Removes the USB transfer request <req> from the host controller schedule *)
  244. PROCEDURE UnlinkTDsInternal*(pipe : UsbHcdi.Pipe);
  245. VAR td : Machine.Address32; dword : SET; timer : Kernel.Timer;
  246. BEGIN
  247. IF pipe.firstTD = 0 THEN RETURN END; (* pipe has not yet been used *)
  248. (* deactivate all TDs in list *)
  249. td := Machine.Ensure32BitAddress (pipe.firstTD);
  250. ASSERT(SYSTEM.VAL(SET, td) * {0..3} = {}); (* 16byte alignment *)
  251. WHILE td < pipe.lastTD DO
  252. dword := SYSTEM.VAL(SET, SYSTEM.GET32(td + TDControlStatus));
  253. SYSTEM.PUT32(td + TDControlStatus, dword - TDActive);
  254. td := td + 16;
  255. END;
  256. NEW(timer); timer.Sleep(10); (* > 1ms to be sure that the HC doesn't access the tds anymore *)
  257. SYSTEM.PUT32(pipe.qh + QhElementLinkPointer, QhTerminate); (* Unlink TD list *)
  258. END UnlinkTDsInternal;
  259. (* Set the QhTerminate bit in the Queue Head Element Link Pointer to unchain TDs *)
  260. PROCEDURE ClearHalt*(pipe : UsbHcdi.Pipe);
  261. VAR dword : SET;
  262. BEGIN
  263. ASSERT((pipe # NIL) & (pipe.qh # 0));
  264. dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhElementLinkPointer));
  265. ASSERT(dword * QhTdSelect = {}); (* We don't allow a QH to be queued in another QH, UHCI does, though *)
  266. dword := dword + QhTerminate;
  267. SYSTEM.PUT32(pipe.qh + QhElementLinkPointer, dword);
  268. END ClearHalt;
  269. (* Insert the queue head qh into the appropriate queue *)
  270. PROCEDURE InsertQH*(pipe : UsbHcdi.Pipe) : BOOLEAN;
  271. VAR qhtmp, slots, index, queueslots : LONGINT;
  272. BEGIN (* only call from exclusive regions *)
  273. (* build qh. Both link and queue head link pointer are invalid *)
  274. ASSERT((pipe # NIL) & (pipe.qh # 0) & (SYSTEM.VAL(SET, pipe.qh) * {0..3} = {}));
  275. BuildQH(pipe.qh, 3, 1, pipe, 0); (* 3: select QH (2) + Terminate (1) *)
  276. CASE pipe.type OF (* in which queue should we insert the pipe ? *)
  277. UsbHcdi.PipeControl : pipe.queue := controlTD;
  278. | UsbHcdi.PipeBulk : pipe.queue := bulkTD;
  279. | UsbHcdi.PipeIsochronous : pipe.queue := isochronousTD;
  280. | UsbHcdi.PipeInterrupt :
  281. BEGIN
  282. slots := 1024 DIV pipe.irqInterval;
  283. index := 0; queueslots := 1024;
  284. LOOP
  285. IF queueslots = 0 THEN index := 10; EXIT; END;
  286. IF slots >= queueslots THEN EXIT END;
  287. queueslots := queueslots DIV 2; INC (index)
  288. END;
  289. pipe.queue := interruptTD[index]
  290. END;
  291. ELSE
  292. RETURN FALSE;
  293. END;
  294. (* insert the pipe's queue head into the queue *)
  295. qhtmp := SYSTEM.GET32(pipe.queue); (* qhtmp:= queue head link pointer of queue head <queue> *)
  296. SYSTEM.PUT32(pipe.qh, qhtmp); (* queue head link pointer of <qh> := qhtmp (hasn't been used before) *)
  297. SYSTEM.PUT32(pipe.queue, SYSTEM.VAL(SET, pipe.qh) + {1}); (* queue head link pointer of <queue> := pointer to <qh> *)
  298. RETURN TRUE;
  299. END InsertQH;
  300. (* Delete the queue head <qh> in the queue <queue> *)
  301. PROCEDURE RemoveQH*(pipe : UsbHcdi.Pipe);
  302. VAR qhtmp, queue, delthis : Machine.Address32; timer : Kernel.Timer;
  303. BEGIN (* caller must hold obj lock *)
  304. UnlinkTDsInternal(pipe);
  305. delthis := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, pipe.qh) + {1});
  306. queue := Machine.Ensure32BitAddress (pipe.queue);
  307. LOOP (* traverse list of queue heads *)
  308. qhtmp := SYSTEM.GET32(queue);
  309. IF qhtmp = 1 THEN (* end of list but not found ? *)
  310. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbUhci: Fatal error, DeleteQH cannot find qh entry in queue"); KernelLog.Ln; END;
  311. EXIT;
  312. END;
  313. IF qhtmp = delthis THEN (* delete qh *)
  314. qhtmp := SYSTEM.GET32(pipe.qh);
  315. SYSTEM.PUT32(queue, qhtmp);
  316. EXIT;
  317. END;
  318. queue := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, qhtmp) - {1})
  319. END;
  320. NEW(timer); timer.Sleep(10); (* > 1ms to be sure that HC is not operating on this QH *)
  321. END RemoveQH;
  322. (** Checks whether TDs may be linked to the pipe's QH *)
  323. PROCEDURE LinkTDsAllowed*(pipe : UsbHcdi.Pipe) : BOOLEAN;
  324. BEGIN {EXCLUSIVE}
  325. RETURN TRUE;
  326. END LinkTDsAllowed;
  327. (* Insert the TD list <td> into the queue <queue> *)
  328. PROCEDURE LinkTDs*(pipe : UsbHcdi.Pipe; td : Machine.Address32);
  329. BEGIN {EXCLUSIVE}
  330. ASSERT(SYSTEM.VAL(SET, td) * {0..3} = {}); (* 16byte alignment of TDs *)
  331. SYSTEM.PUT32(pipe.qh + QhElementLinkPointer, Machine.Ensure32BitAddress (td)); (* queue element link pointer ::= pointer to td *)
  332. END LinkTDs;
  333. PROCEDURE ScheduleControl*(pipe : UsbHcdi.Pipe; direction : LONGINT; msg : UsbHcdi.ControlMessage; bufferLen : LONGINT; VAR buffer : Usbdi.Buffer);
  334. VAR td : Machine.Address32; pid, restlen, databufferAdr, curlen : LONGINT; tdStatus : SET;
  335. BEGIN
  336. (* Control transfers use a three stage protocol: *)
  337. (* stage1: control setup transaction *)
  338. (* stage2: optional data stage *)
  339. (* stage3: status transaction *)
  340. (* stage1: setup stage *)
  341. pipe.firstTD := pipe.tdBase; td := pipe.tdBase;
  342. IF Debug.StrongChecks THEN DoStrongChecks(pipe, bufferLen, 0, buffer); END;
  343. tdStatus := TDActive + TDERR3;
  344. IF pipe.speed = UsbHcdi.LowSpeed THEN tdStatus := tdStatus + TDLS; END;
  345. (* build the setup TD *)
  346. BuildTDLinkPointer(td, TDDepthFirst, td + 16);
  347. BuildTDControlStatus(td, tdStatus);
  348. BuildTDToken(td, PidSetup, pipe.address, pipe.endpoint MOD 16, FALSE, 8); (* dataToggle = FALSE *)
  349. SYSTEM.PUT32(td + TDBufferPointer, ADDRESSOF(msg[0]));
  350. (* setup phase always starts with dataToggle = FALSE, so now it must be TRUE *)
  351. pipe.dataToggle := TRUE;
  352. (* stage 2: (optional) data stage *)
  353. IF bufferLen # 0 THEN
  354. IF direction = UsbHcdi.In THEN pid := PidIn; ELSE pid := PidOut; END;
  355. databufferAdr := Machine.Ensure32BitAddress (pipe.physBufferAdr); (* offset included *)
  356. restlen := bufferLen;
  357. WHILE restlen > 0 DO (* build TD chain *)
  358. td := td + 16;
  359. IF restlen > pipe.maxPacketSize THEN
  360. curlen := pipe.maxPacketSize;
  361. ELSE
  362. curlen := restlen;
  363. END;
  364. (* Build TD *)
  365. BuildTDLinkPointer(td, TDDepthFirst, td + 16);
  366. BuildTDControlStatus(td, tdStatus);
  367. BuildTDToken(td, pid, pipe.address, pipe.endpoint MOD 16, pipe.dataToggle, curlen);
  368. SYSTEM.PUT32(td + TDBufferPointer, databufferAdr);
  369. pipe.dataToggle := ~pipe.dataToggle;
  370. databufferAdr := databufferAdr + curlen;
  371. restlen := restlen - curlen;
  372. END;
  373. END;
  374. (* stage 3: status *)
  375. tdStatus := tdStatus - TDSPD;
  376. IF pipe.ioc THEN tdStatus := tdStatus + TDIOC; END; (* enable interrupt on completion for this TD *)
  377. IF (direction = UsbHcdi.Out) OR (bufferLen = 0) THEN
  378. pid := PidIn;
  379. ELSE
  380. pid := PidOut;
  381. END;
  382. td := td + 16;
  383. (* build status TD *)
  384. BuildTDLinkPointer(td, TDTerminate, 0);
  385. BuildTDControlStatus(td, tdStatus);
  386. BuildTDToken(td, pid, pipe.address, pipe.endpoint MOD 16, TRUE, 0); (* dataToggle always TRUE in status stage *)
  387. SYSTEM.PUT32(td + TDBufferPointer, 0);
  388. pipe.lastTD := td;
  389. END ScheduleControl;
  390. PROCEDURE Schedule*(pipe : UsbHcdi.Pipe; bufferLen, offset: LONGINT; VAR buffer: Usbdi.Buffer);
  391. VAR td : Machine.Address32; pid, restlen, curlen, databuffer : LONGINT; tdStatus : SET;
  392. BEGIN
  393. pipe.firstTD := pipe.tdBase;
  394. IF Debug.StrongChecks THEN DoStrongChecks(pipe, bufferLen, offset, buffer); END;
  395. (* enough TD's to support transfer of bufferLen bytes? *)
  396. IF pipe.firstTD + 16*((pipe.transferLen DIV pipe.maxPacketSize) +1) > ADDRESSOF(pipe.tdBuffer[pipe.tdBufferLen-1]) THEN
  397. KernelLog.String("UsbUhci: TD buffer too small to support requested tranfer size."); KernelLog.Ln;
  398. pipe.status := Usbdi.Error; pipe.errors := UsbHcdi.OutOfTDs;
  399. RETURN;
  400. END;
  401. tdStatus := TDActive + TDERR3;
  402. IF pipe.speed = UsbHcdi.LowSpeed THEN tdStatus := tdStatus + TDLS; END;
  403. IF pipe.direction = UsbHcdi.In THEN
  404. tdStatus := tdStatus + TDSPD;
  405. pid := PidIn;
  406. ELSE
  407. pid := PidOut;
  408. END;
  409. restlen := bufferLen;
  410. databuffer := Machine.Ensure32BitAddress (pipe.physBufferAdr); (* offset already added *)
  411. td := pipe.firstTD - 16;
  412. WHILE restlen > 0 DO (* build TD chain *)
  413. td := td + 16;
  414. IF restlen > pipe.maxPacketSize THEN
  415. curlen := pipe.maxPacketSize;
  416. BuildTDLinkPointer(td, TDDepthFirst, td + 16);
  417. ELSE (* last TD in chain *)
  418. curlen := restlen;
  419. IF restlen < pipe.maxPacketSize THEN tdStatus := tdStatus - TDSPD; ELSE tdStatus := tdStatus + TDSPD; END; (* disable shortPacket detection *)
  420. IF pipe.ioc THEN tdStatus := tdStatus + TDIOC; ELSE tdStatus := tdStatus - TDIOC; END; (* enable interrupt on completion for this TD *)
  421. BuildTDLinkPointer(td, TDTerminate, 0);
  422. END;
  423. BuildTDControlStatus(td, tdStatus);
  424. BuildTDToken(td, pid, pipe.address, pipe.endpoint MOD 16, pipe.dataToggle, curlen);
  425. SYSTEM.PUT32(td + TDBufferPointer, databuffer);
  426. pipe.dataToggle := ~ pipe.dataToggle;
  427. databuffer := databuffer + curlen;
  428. restlen := restlen - curlen;
  429. END;
  430. pipe.lastTD := td;
  431. END Schedule;
  432. PROCEDURE InterruptHandler;
  433. VAR s : SET;
  434. BEGIN (* works without being exclusive *)
  435. IF Debug.Stats THEN INC(NnofInterrupts); END;
  436. IF state >= UsbHcdi.Initialized THEN (* controller is active -> handle interrupts *)
  437. Machine.Portin16(iobase + UsbSts, SYSTEM.VAL(INTEGER, s));
  438. IF s # {} THEN
  439. IF Debug.Stats THEN INC(NnofInterruptsHandled); END;
  440. IF Debug.Trace & Debug.traceInterrupts THEN ShowInterrupts(s); END;
  441. (* reset the USB status register *)
  442. Machine.Portout16(iobase + UsbSts, SYSTEM.VAL(INTEGER, s));
  443. IF s * StatusHChalted # {} THEN KernelLog.String("UsbUhci: Error: Host Controller halted"); KernelLog.Ln; SetState(UsbHcdi.Halted); END;
  444. IF s * StatusProcessError # {} THEN KernelLog.String("UsbUhci: Host Controller process error"); KernelLog.Ln; END;
  445. IF s * StatusSystemError # {} THEN KernelLog.String("UsbUhci: Host system error"); KernelLog.Ln; END;
  446. IF s * (StatusErrorInt + StatusInt) # {} THEN
  447. NotifyCompletionHandlers;
  448. END;
  449. END;
  450. END;
  451. END InterruptHandler;
  452. (* the host controller writes the number of actually transfered bytes into the ActLen field of each executed TD.
  453. * The GetTransferredLen procedure builds the sum of all TD.ActLen fields of all executed TD's in the queue <qh> and then
  454. * writes this value into the req.bufferLen field *)
  455. PROCEDURE GetTransferredLen(pipe : UsbHcdi.Pipe; VAR errors : SET);
  456. VAR td, thislen, actlen, maxlen, val : LONGINT; addr : Machine.Address32; s : SET;
  457. BEGIN
  458. actlen := 0; addr := pipe.firstTD;
  459. LOOP
  460. val := SYSTEM.GET32(addr + TDControlStatus);
  461. s := SYSTEM.VAL(SET, val);
  462. IF s * TDActive # {} THEN (* this TD has not yet been executed *) EXIT; END;
  463. thislen := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, val+1) * {0..10}); (* access the ActLen field of the TD *)
  464. val := SYSTEM.GET32(addr + TDToken);
  465. IF SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, val) * {0..7}) # PidSetup THEN actlen := actlen + thislen; END;
  466. maxlen := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, ASH(val, -21) + 1) * {0..10});
  467. IF thislen < maxlen THEN
  468. errors := errors + UsbHcdi.ShortPacket;
  469. END;
  470. IF s * (TDCrcTimeout + TDDataBuffer + TDStalled + TDBitStuff + TDBabble + TDNak) # {} THEN EXIT END;
  471. td := SYSTEM.GET32(addr + TDLinkPointer);
  472. IF SYSTEM.VAL(SET, td) * {0} # {} THEN (* TD link pointer not valid (last TD was the last TD in queue) *) EXIT; END;
  473. addr := SYSTEM.VAL(Machine.Address32, SYSTEM.VAL(SET, td) * {4..31}); (* addr := physical addr of td *)
  474. END;
  475. pipe.actLen := actlen;
  476. END GetTransferredLen;
  477. PROCEDURE UpdatePipeStatus*(pipe : UsbHcdi.Pipe);
  478. VAR
  479. val, pid : LONGINT;
  480. td, addr : Machine.Address32;
  481. errors, s : SET;
  482. datatoggle : BOOLEAN;
  483. last : BOOLEAN;
  484. BEGIN
  485. (* be aware of the fact the the USB host controller is concurrently accessing AND modifying this datastructure !! *)
  486. (* test if the queue was finished *)
  487. td := SYSTEM.GET32(pipe.qh + QhElementLinkPointer); (* get queue head element link pointer *)
  488. IF td = 1 THEN (* yes, fetch status of last td in queue (queue head element link pointer is not valid) *)
  489. td := pipe.lastTD;
  490. ELSE
  491. td := SYSTEM.VAL(Machine.Address32, SYSTEM.VAL(SET, td) * {4..31});
  492. END;
  493. IF td = pipe.lastTD THEN last := TRUE; END;
  494. addr := SYSTEM.VAL(Machine.Address32, SYSTEM.VAL(SET, td) * {4..31}); (* addr := physical addr of td *)
  495. val := SYSTEM.GET32(addr + TDControlStatus);
  496. s := SYSTEM.VAL(SET, val);
  497. (* the USB host controller could already have changed the status of the TD at this time *)
  498. errors := UsbHcdi.NoErrors;
  499. IF s * TDActive # {} THEN RETURN; END;
  500. IF s * TDNak # {} THEN errors := errors + UsbHcdi.Nak; END; (* Actually, this is not really an error -> flow control *)
  501. val := SYSTEM.GET32(addr + TDToken);
  502. pid := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, val) * {0..7});
  503. IF SYSTEM.VAL(SET, val) * TDDataToggle # {} THEN datatoggle := TRUE ELSE datatoggle := FALSE END;
  504. IF (s * (TDCrcTimeout + TDDataBuffer + TDStalled + TDBitStuff + TDBabble)) # {} THEN (* marginal speed up *)
  505. IF s * TDCrcTimeout # {} THEN errors := errors + UsbHcdi.CrcTimeout; END;
  506. IF s * TDDataBuffer # {} THEN errors := errors + UsbHcdi.Databuffer; END;
  507. IF s * TDStalled # {} THEN errors := errors + UsbHcdi.Stalled; END;
  508. IF s * TDBitStuff # {} THEN errors := errors + UsbHcdi.BitStuff; END;
  509. IF s * TDBabble # {} THEN errors := errors + UsbHcdi.Babble; END;
  510. END;
  511. IF ~last & (errors - UsbHcdi.Nak = {}) THEN RETURN; END;
  512. td := SYSTEM.GET32(pipe.qh + QhElementLinkPointer); (* get queue head element link pointer *)
  513. (* Test for errors *)
  514. GetTransferredLen(pipe, errors);
  515. pipe.errors := errors;
  516. IF errors = UsbHcdi.NoErrors THEN
  517. pipe.status := Usbdi.Ok;
  518. ELSIF errors = UsbHcdi.ShortPacket THEN
  519. pipe.status := Usbdi.ShortPacket;
  520. ELSE
  521. IF errors * UsbHcdi.Stalled # {} THEN
  522. pipe.status := Usbdi.Stalled;
  523. ELSE
  524. pipe.status := Usbdi.Error;
  525. END;
  526. END;
  527. IF (pipe.type = UsbHcdi.PipeBulk) OR (pipe.type = UsbHcdi.PipeInterrupt) THEN
  528. (* if we receive an ACK, do the toggle *)
  529. IF (pipe.status = Usbdi.Ok) OR (pipe.status = Usbdi.ShortPacket) THEN
  530. pipe.dataToggle := ~datatoggle;
  531. END;
  532. END;
  533. END UpdatePipeStatus;
  534. (* UHCI queue head format
  535. *
  536. * Offset: Value:
  537. * 00 Queue Head Link Pointer
  538. * 04 Queue Element Link Pointer
  539. * 08 [Oberon specific] Pointer to PipePolicy
  540. * 12 [Oberon specific] Pointer to last TD in queue
  541. *)
  542. PROCEDURE BuildQH(td: Machine.Address32; f1, f2: LONGINT; f3 : ANY; f4 : LONGINT);
  543. BEGIN
  544. SYSTEM.PUT32(td, f1); (* QH queue head link pointer field *)
  545. SYSTEM.PUT32(td + 4, f2); (* QH queue element link pointer field *)
  546. SYSTEM.PUT32(td + 8, f3); (* QH pointer to pipepolicy *)
  547. SYSTEM.PUT32(td + 12, f4); (* QH pointer to last TD in queue *)
  548. END BuildQH;
  549. (* UHCI Transfer Descriptor (TD) Format
  550. *
  551. * Offset: Value:
  552. * 00 Link Pointer[31:4]
  553. * 04 Flags - Status - Actlen
  554. * 08 MaxLen - D Toggle - EndPt - Device Address - PID
  555. * 12 Buffer Pointer
  556. *
  557. * For more details see Intel UHCI Design Guide v1.1, p. 20-25
  558. * builds a TD. Is also used to build queue heads *)
  559. PROCEDURE BuildTD(td : Machine.Address32; f1, f2, f3, f4 : LONGINT);
  560. BEGIN
  561. SYSTEM.PUT32(td + TDLinkPointer, f1);
  562. SYSTEM.PUT32(td + TDControlStatus, f2);
  563. SYSTEM.PUT32(td + TDToken, f3);
  564. SYSTEM.PUT32(td + TDBufferPointer, f4);
  565. END BuildTD;
  566. PROCEDURE BuildTDLinkPointer (VAR td : Machine.Address32; flags : SET; LinkPointer : LONGINT);
  567. BEGIN
  568. ASSERT( SYSTEM.VAL( SET, LinkPointer) * {0..3} = {});
  569. SYSTEM.PUT32(td, SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, LinkPointer) + flags));
  570. END BuildTDLinkPointer;
  571. PROCEDURE BuildTDControlStatus (VAR td : Machine.Address32; status : SET);
  572. BEGIN
  573. status := status + {0..10}; SYSTEM.PUT32(td + TDControlStatus, status);
  574. END BuildTDControlStatus;
  575. PROCEDURE BuildTDToken (VAR td: Machine.Address32; PID, DeviceAddress, EndPoint : LONGINT; DataToggle : BOOLEAN; maxLength : LONGINT);
  576. VAR s : SET;
  577. BEGIN
  578. (* maxLength specifies the maximum number of data bytes allowed for the transfer,
  579. * allowed values: 0x000-0x4FF (1-1280 bytes) and 0x7FF (0 bytes) *)
  580. ASSERT(((maxLength>=0000H) & (maxLength<=04FFH)) OR (maxLength=07FFH));
  581. IF maxLength = 0 THEN maxLength := 07FFH; (* 0 bytes = Null Data packet *)
  582. ELSE maxLength := maxLength - 1; (* maxLength==0 -> 1 Byte *)
  583. END;
  584. s := {}; IF DataToggle THEN s := s + TDDataToggle END;
  585. SYSTEM.PUT32(td + TDToken, SYSTEM.VAL(SET, PID + ASH(DeviceAddress, 8) + ASH(EndPoint, 15) + ASH(maxLength, 21)) + s);
  586. END BuildTDToken;
  587. (* Initializes the host controller and builds up the basics of the UHCI data structures *)
  588. PROCEDURE Init(base: Machine.Address32; IRQ : LONGINT) : BOOLEAN;
  589. VAR i, k, j : LONGINT; dword : SET;
  590. BEGIN
  591. iobase := base; irq := IRQ; DMAchaining := FALSE;
  592. (* Configure the ports; no more documented in the specs, but 3-4 possible *)
  593. portCount := NumberOfPorts; NEW(ports, portCount);
  594. (* Calculate offset from iobase of the port status/controll register for each port *)
  595. FOR i := 0 TO portCount - 1 DO ports[i] := iobase + PortSc1 + i*2; END;
  596. (* Build the emulated hub descriptor *)
  597. NEW(hubDescriptor, 8);
  598. hubDescriptor[0] := CHR(7);
  599. hubDescriptor[1] := CHR(29H); (* Hub Descriptor *)
  600. hubDescriptor[2] := CHR(portCount);
  601. dword := dword + {1}; (* UHCI root hubs don't implement port power switching *)
  602. dword := dword + {4}; (* UHCI root hubs don't implement overcurrent detection *)
  603. hubDescriptor[3] := CHR(SYSTEM.VAL(LONGINT, dword));
  604. hubDescriptor[4] := CHR(0); (* Reserved *)
  605. hubDescriptor[5] := CHR(10); (* 20ms Power on to power good *)
  606. hubDescriptor[6] := CHR(0); (* Root hubs don't draw current from the USB *)
  607. NEW(framelist);
  608. (* Calculate the address of the 4K boundary contained in the 8K buffe (framelist base address has to be 4KB aligned) *)
  609. framelist.framelistbase := SYSTEM.VAL(Machine.Address32, SYSTEM.VAL(SET, ADDRESSOF(framelist.field[0]) + 1024*4 - 1) * {12..31});
  610. ASSERT (Machine.Is32BitAddress (framelist.framelistbase));
  611. (* Calculate the index which points to the element positioned at the 4K boundary *)
  612. framelist.index := ( framelist.framelistbase - ADDRESSOF(framelist.field[0]) ) DIV 4;
  613. (* Okay... now allocate the 16byte aligned Queue Heads ... *)
  614. NEW(tdlist, 16*TdListSize);
  615. tdlistbase := SYSTEM.VAL(Machine.Address32, SYSTEM.VAL(SET, ADDRESSOF(tdlist[0])+15) * {4..31});
  616. ASSERT (Machine.Is32BitAddress (tdlistbase));
  617. ASSERT(SYSTEM.VAL(SET, tdlistbase) * {0..3} = {});
  618. ASSERT((tdlistbase >= ADDRESSOF(tdlist[0])) & (tdlistbase < ADDRESSOF(tdlist[16*TdListSize-1])));
  619. (* Set up the frame list pointer skeleton *)
  620. controlTD := tdlistbase;
  621. bulkTD := tdlistbase + 16;
  622. isochronousTD := tdlistbase + 32;
  623. FOR i := 0 TO 10 DO
  624. ASSERT(tdlistbase + 48 + i*16 <= ADDRESSOF(tdlist[16*TdListSize-1]));
  625. interruptTD[i] := tdlistbase + 48 + i*16;
  626. END;
  627. (* Build queue heads for bulk-, control- and interupt transfers *)
  628. BuildTD(bulkTD, 1, 1, 0, 0);
  629. BuildTD(controlTD, SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, bulkTD) + {1} ), 1, 0, 0);
  630. BuildTD(isochronousTD, SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, controlTD) + {1}), 1, 0, 0);
  631. BuildTD(interruptTD[0], SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, isochronousTD) + {1} ), 1, 0, 0);
  632. FOR i:=1 TO 10 DO
  633. BuildTD(interruptTD[i], SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, interruptTD[i-1]) + {1} ), 1, 0, 0);
  634. END;
  635. (* => end of queue 10 points to 9, end of 8 points to 7 , ..., end of 1 points to 0 *)
  636. (* => 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.*)
  637. (* queue 0 executes 1024x, queue 1 executes 512x, queue 2 executes 256x, queue 3 executes 128x*)
  638. (* queue 4 executes 64x, queue 5 executes 32x, queue 6 executes 16x, queue 7 executes 8x*)
  639. (* queue 8 executes 4x, queue 9 executes 2x, queue 10 executes 1x *)
  640. (* What does the following mean? => We count the 1's (starting at lsb) until we pass a zero *)
  641. (* This count gives the queue number for a given slot *)
  642. FOR i := 0 TO 1023 DO (* i is slot number, we want to calc the queue number (k) for this slot *)
  643. k := 0; j := i;
  644. LOOP
  645. IF (SYSTEM.VAL(SET, j) * {0}) = {} THEN EXIT; END;
  646. INC(k); j := j DIV 2;
  647. END;
  648. framelist.field[framelist.index + i] := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, interruptTD[k]) + {1} );
  649. END;
  650. Reset; (* Reset the host controller *)
  651. (* try to start the host controller *)
  652. IF Start() = FALSE THEN (* ERROR: Couldn't start the host controller. Controller was probably not correctly initialized. *)
  653. Reset; KernelLog.String("UsbUhci: Couldn't start controller."); KernelLog.Ln; RETURN FALSE;
  654. END;
  655. Objects.InstallHandler(InterruptHandler, Machine.IRQ0+irq);
  656. RETURN TRUE;
  657. END Init;
  658. (* Resets and then starts the host controller. As soon as the host controller is started, it processes the schedule *)
  659. PROCEDURE Start():BOOLEAN;
  660. VAR t : LONGINT; s : SET;
  661. BEGIN
  662. (* Perform host controller reset *)
  663. Machine.Portout16(iobase + UsbCmd, SYSTEM.VAL(INTEGER, CmdHostControllerReset) );
  664. (* The host controller will clear the CmdHostControllerReset Bit after about 64 bit times *)
  665. t := 10000;
  666. REPEAT
  667. Machine.Portin16(iobase + UsbCmd, SYSTEM.VAL(INTEGER, s));
  668. DEC( t );
  669. UNTIL ((t = 0) OR ( ~(1 IN s)));
  670. IF t = 0 THEN (* ERROR: Host Controller should have cleared the Bit !! *) RETURN FALSE END;
  671. (* Enable all interrupts, but remember: perhaps they are not being routed by the PIIX4 *)
  672. Machine.Portout16(iobase + UsbIntr, SYSTEM.VAL(INTEGER, IntShortPacket + IntIOC + IntResume + IntTimeoutCRC) );
  673. (* We start at frame 0 and also set the framelistbase address *)
  674. Machine.Portout16(iobase + FrNum, SYSTEM.VAL(INTEGER, 0) ); (* 16 bit! *)
  675. Machine.Portout32(iobase + FlBaseAdr, SYSTEM.VAL(LONGINT, framelist.framelistbase));
  676. (* Start the controller, set max-packet size (64byte) and set the pseudo-semaphore which we don't need *)
  677. Machine.Portout16(iobase + UsbCmd, SYSTEM.VAL(INTEGER, CmdRunStop + CmdConfigureFlag + CmdMaxPacket) );
  678. SetState(UsbHcdi.Operational);
  679. RETURN TRUE;
  680. END Start;
  681. PROCEDURE Cleanup;
  682. BEGIN
  683. IF state >= UsbHcdi.Initialized THEN Objects.RemoveHandler(InterruptHandler, Machine.IRQ0 + irq); END;
  684. Cleanup^;
  685. Reset;
  686. END Cleanup;
  687. PROCEDURE ShowPipe*(pipe : UsbHcdi.Pipe);
  688. BEGIN
  689. HumanTD(pipe.firstTD);
  690. END ShowPipe;
  691. (** Displays the host controller's data struture on KernelLog *)
  692. PROCEDURE ShowSchedule*;
  693. BEGIN
  694. IF Debug.Trace THEN
  695. KernelLog.String("Host Controller Data Structures of ");
  696. KernelLog.String(name); KernelLog.String(" ("); KernelLog.String(desc); KernelLog.String("):"); KernelLog.Ln;
  697. HumanQH(controlTD, SELF);
  698. HumanQH(interruptTD[10], SELF);
  699. END;
  700. END ShowSchedule;
  701. PROCEDURE DoStrongChecks(pipe : UsbHcdi.Pipe; len, ofs : LONGINT; VAR buffer : Usbdi.Buffer);
  702. VAR i : ADDRESS;
  703. BEGIN
  704. IF len > 0 THEN
  705. i := Machine.PhysicalAdr(ADDRESSOF(buffer[0]), len);
  706. IF i = -1 THEN
  707. KernelLog.String("UsbUhci: Buffers must be physically contiguoues"); KernelLog.Ln;
  708. HALT(99)
  709. END;
  710. END;
  711. IF pipe.type = UsbHcdi.PipeControl THEN
  712. ASSERT((pipe.firstTD + 16*((pipe.transferLen DIV pipe.maxPacketSize) +3)) <= ADDRESSOF(pipe.tdBuffer[pipe.tdBufferLen-1])); (* enough TDs *)
  713. ELSE
  714. ASSERT((pipe.firstTD + 16*((pipe.transferLen DIV pipe.maxPacketSize) +1)) <= ADDRESSOF(pipe.tdBuffer[pipe.tdBufferLen-1])); (* enough TDs *)
  715. END;
  716. END DoStrongChecks;
  717. (* for debugging: display diagnostics of this host controller to KernelLog *)
  718. PROCEDURE Diag;
  719. VAR s : SET; framenum : LONGINT;
  720. BEGIN
  721. IF Debug.Trace THEN
  722. Diag^;
  723. Machine.Portin16 (iobase + FrNum, SYSTEM.VAL(INTEGER, framenum));
  724. framenum := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, framenum) * {0..10});
  725. KernelLog.String(" Frame 0"); KernelLog.Hex(framenum, 8);
  726. KernelLog.String(" LastStatus: ");
  727. Machine.Portin16 (iobase + UsbSts, SYSTEM.VAL(INTEGER, s));
  728. IF s # {} THEN
  729. IF s * StatusHChalted # {} THEN KernelLog.String ("[HChalted]"); END;
  730. IF s * StatusProcessError # {} THEN KernelLog.String ("[Process Error]"); END;
  731. IF s * StatusSystemError # {} THEN KernelLog.String ("[System Error]"); END;
  732. IF s * StatusResumeDetect # {} THEN KernelLog.String ("[Resume Detect]"); END;
  733. IF s * StatusErrorInt # {} THEN KernelLog.String ("[ErrorInt]"); END;
  734. IF s * StatusInt # {} THEN KernelLog.String ("[Int]"); END;
  735. KernelLog.Ln;
  736. ELSE
  737. KernelLog.String("[ok]"); KernelLog.Ln;
  738. END;
  739. KernelLog.String (" IRQ enable status: ");
  740. Machine.Portin16 (iobase + UsbIntr, SYSTEM.VAL(INTEGER, s));
  741. IF s * IntShortPacket # {} THEN KernelLog.String("[Short Packet]"); END;
  742. IF s * IntIOC # {} THEN KernelLog.String ("[IOC]"); END;
  743. IF s * IntResume # {} THEN KernelLog.String ("[Resume]"); END;
  744. IF s * IntTimeoutCRC # {} THEN KernelLog.String ("[Timeout/CRC]"); END;
  745. KernelLog.Ln;
  746. END;
  747. END Diag;
  748. END UhciController;
  749. VAR
  750. qhCounter : LONGINT; (* used by HumanQH; only for debug puposes *)
  751. (* Debug: displays the information in the queue head qh and all TD's in that queue the queue is traversed only horizontally *)
  752. PROCEDURE HumanQH(qh : LONGINT; controller : UhciController);
  753. VAR val,addr : LONGINT; s : SET; pipe : UsbHcdi.Pipe; ptr : ANY;
  754. BEGIN
  755. IF Debug.Trace THEN
  756. IF qhCounter > ShowScheduleMaxQH THEN
  757. KernelLog.String("UsbUhci: HumanQH: UsbUhci.ShowScheduleMaxQH showed... aborting."); KernelLog.Ln;
  758. RETURN;
  759. ELSE
  760. INC(qhCounter);
  761. END;
  762. KernelLog.String("QH at address: "); KernelLog.Hex(qh, 8); KernelLog.String("H");
  763. IF qh = controller.controlTD THEN KernelLog.String(" (Controller Control Queue)");
  764. ELSIF qh = controller.bulkTD THEN KernelLog.String(" (Controller Bulk Queue)");
  765. ELSIF qh = controller. isochronousTD THEN KernelLog.String(" (Controller Isochronous Queue)");
  766. ELSIF qh = controller. interruptTD[0] THEN KernelLog.String(" (Controller 1ms-Interrupt Queue)");
  767. ELSIF qh = controller. interruptTD[1] THEN KernelLog.String(" (Controller 2ms-Interrupt Queue)");
  768. ELSIF qh = controller. interruptTD[2] THEN KernelLog.String(" (Controller 4ms-Interrupt Queue)");
  769. ELSIF qh = controller. interruptTD[3] THEN KernelLog.String(" (Controller 8ms-Interrupt Queue)");
  770. ELSIF qh = controller. interruptTD[4] THEN KernelLog.String(" (Controller 16ms-Interrupt Queue)");
  771. ELSIF qh = controller. interruptTD[5] THEN KernelLog.String(" (Controller 32ms-Interrupt Queue)");
  772. ELSIF qh = controller.interruptTD[6] THEN KernelLog.String(" (Controller 64ms-Interrupt Queue)");
  773. ELSIF qh = controller.interruptTD[7] THEN KernelLog.String(" (Controller 128ms-Interrupt Queue)");
  774. ELSIF qh = controller.interruptTD[8] THEN KernelLog.String(" (Controller 256ms-Interrupt Queue)");
  775. ELSIF qh = controller.interruptTD[9] THEN KernelLog.String(" (Controller 512ms-Interrupt Queue)");
  776. ELSIF qh = controller.interruptTD[10] THEN KernelLog.String(" (Controller 1024ms-Interrupt Queue)");
  777. ELSE
  778. ptr := SYSTEM.VAL(ANY, SYSTEM.GET32(qh + 8)); (* should be a pointer to a pipe *)
  779. IF ptr # NIL THEN
  780. pipe := ptr (UsbHcdi.Pipe);
  781. CASE pipe.type OF
  782. UsbHcdi.PipeBulk : KernelLog.String(" (Bulk");
  783. |UsbHcdi.PipeControl : KernelLog.String(" (Control");
  784. |UsbHcdi.PipeInterrupt : KernelLog.String(" (Interrupt");
  785. |UsbHcdi.PipeIsochronous : KernelLog.String(" (Isochronous");
  786. ELSE
  787. KernelLog.String(" (Unknown");
  788. END;
  789. KernelLog.String(" Pipe Adr: "); KernelLog.Int(pipe.address, 0);
  790. KernelLog.String(" Ep: "); KernelLog.Int(pipe.endpoint, 0);
  791. KernelLog.String(")"); KernelLog.Ln;
  792. ELSE
  793. KernelLog.String("(unknown)");
  794. END;
  795. END;
  796. KernelLog.Ln;
  797. (* first show the information of the Queue Head Link pointer (QHLP) *)
  798. KernelLog.String(" QH link pointer: ");
  799. val := SYSTEM.GET32(qh); s := SYSTEM.VAL(SET, val);
  800. IF s * TDQHSelect # {} THEN KernelLog.String(" [QH]"); ELSE KernelLog.String(" [TD]"); END;
  801. IF s * TDTerminate # {} THEN KernelLog.String(" [Terminate]"); END;
  802. val := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, val) * {4..31});
  803. addr := val; (* addr := queue head link pointer *)
  804. KernelLog.String("[PTR val: "); KernelLog.Hex(val, 8); KernelLog.String("H]"); KernelLog.Ln;
  805. (* second show the information of the Queue Element Link Pointer (QELP) *)
  806. KernelLog.String(" QH Element Link Pointer: ");
  807. val := SYSTEM.GET32(qh + 4); s := SYSTEM.VAL(SET,val);
  808. IF s * TDQHSelect # {} THEN KernelLog.String(" [QH]"); ELSE KernelLog.String(" [TD]"); END;
  809. IF s * TDTerminate # {} THEN KernelLog.String(" [Terminate]"); END;
  810. val := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, val) * {4..31});
  811. KernelLog.String("[PTR val: "); KernelLog.Hex(val, 8); KernelLog.String("G]"); KernelLog.Ln;
  812. (* third show the queued TDs in this queue *)
  813. IF s * TDTerminate = {} THEN (* there are some queued TDs or QHs - show them *)
  814. IF s * TDQHSelect # {} THEN (* is a Queue Head *)
  815. HumanQH(val, controller);
  816. ELSE (* it's a TD *)
  817. HumanTD(val);
  818. END;
  819. END;
  820. IF addr # 0 THEN (* there are other queue heads in the queue *)
  821. KernelLog.String("next queue head in queue:"); KernelLog.Ln; KernelLog.Ln;
  822. HumanQH(addr, controller);
  823. END;
  824. (* reset qhCounter *)
  825. qhCounter := 0;
  826. END;
  827. END HumanQH;
  828. (* Debug: displays the information of the Transfer Descriptor nexttd and all linked TD's and displays it *)
  829. PROCEDURE HumanTD(nexttd: Machine.Address32);
  830. VAR addr, val : LONGINT; s : SET;
  831. BEGIN
  832. IF Debug.Trace THEN
  833. LOOP
  834. addr := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, nexttd) * {4..31}); (* TD's are 16byte aligned *)
  835. KernelLog.String("TD at address: "); KernelLog.Hex(addr, 8); KernelLog.Ln;
  836. (* link pointer information *)
  837. val := SYSTEM.GET32(addr); s := SYSTEM.VAL(SET, val);
  838. KernelLog.String(" LinkPTR:");
  839. IF s * TDDepthFirst # {} THEN KernelLog.String(" [Depthfirst]"); ELSE KernelLog.String("[BreathFirst]"); END;
  840. IF s * TDQHSelect # {} THEN KernelLog.String(" [QH]"); ELSE KernelLog.String(" [TD]"); END;
  841. IF s * TDTerminate # {} THEN KernelLog.String(" [Terminate]"); END;
  842. KernelLog.String(" [PTR Val: "); KernelLog.Hex((val DIV 16) * 16, 8); KernelLog.String("H]");
  843. KernelLog.Ln;
  844. (* control & status field information *)
  845. val := SYSTEM.GET32(addr + 4); s := SYSTEM.VAL(SET, val);
  846. KernelLog.String(" Control&Status:");
  847. IF s * TDActive # {} THEN KernelLog.String(" [Active]"); END;
  848. IF s * TDIOC # {} THEN KernelLog.String(" [IOC]"); END;
  849. IF s * TDIOS # {} THEN KernelLog.String(" [IOS]"); END;
  850. IF s * TDLS # {} THEN KernelLog.String(" [LowSpeed]"); END;
  851. IF s * TDERR1 # {} THEN KernelLog.String(" [Err1]"); END;
  852. IF s * TDERR2 # {} THEN KernelLog.String(" [Err2]"); END;
  853. IF s * TDSPD # {} THEN KernelLog.String(" [SPD]"); END;
  854. IF s * TDCrcTimeout # {} THEN KernelLog.String(" [CRC/Timeout]"); END;
  855. IF s * TDNak # {} THEN KernelLog.String(" [NAK]"); END;
  856. IF s * TDDataBuffer # {} THEN KernelLog.String(" [DataBuffer]"); END;
  857. IF s * TDStalled # {} THEN KernelLog.String(" [Stalled]"); END;
  858. IF s * TDBitStuff # {} THEN KernelLog.String(" [BitStuff]"); END;
  859. IF s * TDBabble # {} THEN KernelLog.String(" [Babble]"); END;
  860. KernelLog.String (" [Actlen: "); val := SYSTEM.VAL(LONGINT, s * {0..10});
  861. IF val = 07FFH THEN val := 0; ELSE INC(val); END; KernelLog.Int(val, 0); KernelLog.String(" ]");
  862. KernelLog.String(" [MaxLen: "); val := SYSTEM.GET32(addr + 8);
  863. val := LSH(val, -21); IF val = 7FFH THEN val := 0; ELSE INC(val); END;
  864. KernelLog.Int(val, 0); KernelLog.String(" ]"); KernelLog.Ln;
  865. KernelLog.String(" PID: ");
  866. val := SYSTEM.GET32(addr + 8); val := val MOD 256;
  867. CASE val OF
  868. PidOut : KernelLog.String("OUT");
  869. | PidIn : KernelLog.String("IN");
  870. | PidSetup : KernelLog.String("SETUP");
  871. ELSE
  872. KernelLog.String("Unkown ("); KernelLog.Hex(val, 8); KernelLog.String("H)");
  873. END;
  874. KernelLog.String(" DataToggle: ");
  875. val := SYSTEM.GET32(addr + 8);
  876. s := SYSTEM.VAL(SET,val);
  877. IF s * TDDataToggle # {} THEN KernelLog.String("DATA1"); ELSE KernelLog.String("DATA0"); END;
  878. KernelLog.String(" Device Adr: "); KernelLog.Int(LSH(SYSTEM.VAL(LONGINT, s * {8..14}), -8),0);
  879. KernelLog.String(" Endpoint: "); KernelLog.Int(LSH(SYSTEM.VAL(LONGINT, s * {15..18}), -15),0);
  880. KernelLog.Ln;
  881. nexttd := SYSTEM.GET32(addr);
  882. IF SYSTEM.VAL(SET, nexttd) * {0} # {} THEN EXIT; END;
  883. IF nexttd = 0 THEN KernelLog.String("ERROR! Terminate was not set but address is 0"); KernelLog.Ln; EXIT; END;
  884. END;
  885. END;
  886. END HumanTD;
  887. PROCEDURE ShowInterrupts(s : SET);
  888. BEGIN
  889. KernelLog.String("UsbUhci: Interrupt: ");
  890. IF s * StatusHChalted # {} THEN KernelLog.String("[HcHalted]"); END;
  891. IF s * StatusProcessError # {} THEN KernelLog.String("[ProcessError]"); END;
  892. IF s * StatusSystemError # {} THEN KernelLog.String("[SystemError]"); END;
  893. IF s * StatusErrorInt # {} THEN KernelLog.String("[ErrorInt]"); END;
  894. IF s * StatusInt # {} THEN KernelLog.String("[Int]"); END;
  895. KernelLog.Ln;
  896. END ShowInterrupts;
  897. (* Scan all PCI busses for UHCI compliant host controllers *)
  898. PROCEDURE PCIFindUhci;
  899. CONST
  900. UhciClassCode = 0C0300H;
  901. VAR
  902. hostController : UhciController;
  903. bus, device, function : LONGINT;
  904. iobase, irqline : LONGINT;
  905. index : LONGINT;
  906. res : WORD;
  907. BEGIN
  908. index := 0;
  909. WHILE PCI.FindPCIClassCode(UhciClassCode, index, bus, device, function) = PCI.Done DO
  910. (* Get IRQ line - should be set by BIOS *)
  911. res := PCI.ReadConfigByte(bus, device, function, PCI.IntlReg, irqline); ASSERT(res = PCI.Done);
  912. res := PCI.ReadConfigDword(bus, device, function, PCI.Adr4Reg, iobase); ASSERT(res = PCI.Done);
  913. iobase := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, iobase) * {1..31});
  914. IF irqline # 0 THEN
  915. res := PCI.WriteConfigWord(bus, device, function, 0C0H, 2000H); (* disable legacy emulation *)
  916. IF res = PCI.Done THEN
  917. NEW(hostController, bus, device, function);
  918. IF hostController.Init(iobase, irqline) THEN (* host controller has been initialized and started successfully *)
  919. res := PCI.ReadConfigByte(bus, device, function, 60H, hostController.bcdUSB); (* ignore res *)
  920. IF Debug.Verbose THEN
  921. KernelLog.Enter;
  922. KernelLog.String("UsbUhci: Initialised USB UHCI controller at base 0"); KernelLog.Hex(iobase, 8);
  923. KernelLog.String("H, Irq: "); KernelLog.Int(irqline, 0);
  924. KernelLog.String(" USB version: "); KernelLog.Hex(LSH(hostController.bcdUSB, -4), -2); KernelLog.Char(".");
  925. KernelLog.Hex(hostController.bcdUSB MOD 10H, -2);
  926. KernelLog.Exit;
  927. END;
  928. UsbHcdi.RegisterHostController(hostController, Description);
  929. ELSE
  930. KernelLog.Enter;
  931. KernelLog.String("UsbUhci: ERROR: Cannot init USB UHCI controller at base 0"); KernelLog.Hex(iobase, 8);
  932. KernelLog.String("H, Irq: "); KernelLog.Int(irqline, 0);
  933. KernelLog.Exit;
  934. END;
  935. ELSE KernelLog.String("UsbUhci: Error when accessing PCI configuration space (2)"); KernelLog.Ln;
  936. END;
  937. ELSE
  938. KernelLog.Enter;
  939. KernelLog.String("UsbUhci: Please enable USB-Interrupt in BIOS for UHCI host controller at base 0");
  940. KernelLog.Hex(iobase, 8); KernelLog.Char("H"); KernelLog.Ln;
  941. KernelLog.Exit;
  942. END;
  943. INC(index);
  944. END;
  945. END PCIFindUhci;
  946. (** Scan the PCI bus(ses) for UHCI compliant USB host controllers and start them *)
  947. PROCEDURE Install*;
  948. (* Load module *)
  949. END Install;
  950. PROCEDURE Cleanup;
  951. BEGIN
  952. UsbHcdi.UnRegisterHostControllers(Description);
  953. END Cleanup;
  954. BEGIN
  955. Modules.InstallTermHandler(Cleanup);
  956. (* Find, init and start all compatible UHCI USB host controllers and register them in the UsbHcdi.controllers registry *)
  957. PCIFindUhci;
  958. END UsbUhci.
  959. UsbUhci.Install ~ SystemTools.Free UsbUhci ~