PCITools.Mod 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. MODULE PCITools; (** AUTHOR "pjm/staubesv"; PURPOSE "PCI Bus Tools"; *)
  2. (**
  3. * This module provide two kinds of services related to PCI:
  4. * - Display information about all PCI busses/devices installed in the system
  5. * - Map PCI function to PCI device drivers
  6. *
  7. * Usage:
  8. *
  9. * PCITools.Scan ~ displays information about all installed PCI busses/devices
  10. * PCITools.Scan details ~ displays excessively detailed information
  11. * PCITools.DetectHardware ~ performs a PCI function to device driver mapping for all devices found
  12. *
  13. * System.Free PCITools ~
  14. *
  15. * Port of Oberon PCITools.Mod from "pjm" which is based on Linux pci.c and PCI Local Bus Specification Revision 2.0
  16. *
  17. * History:
  18. *
  19. * 18.06.2003 Fix for non-continuously numbered device functions (tf)
  20. * 15.09.2005 Ported to Bluebottle, added DetectHardware(), InstallPCIDrivers() & Extract(), removed ShowInterrupts() (staubesv)
  21. * 17.01.2006 Added WriteRegs & WriteCmdSts (staubesv)
  22. *)
  23. IMPORT
  24. SYSTEM, PCI, KernelLog, Streams, Files, Commands, Options, DriverDatabase;
  25. CONST
  26. HdrType = 0EH;
  27. Verbose = FALSE;
  28. ShowDrivers = TRUE; (* Also show available device/class drivers *)
  29. PCIIDS = "pci.ids"; (* File containing PCI vendor ID to vendor string mapping *)
  30. TYPE
  31. Device = POINTER TO RECORD
  32. bus: Bus; (* bus this device is on *)
  33. sibling: Device; (* next device on this bus *)
  34. next, prev: Device; (* chain of all devices *)
  35. devfn: LONGINT; (* dev = top 5 bits, fn = lower 3 bits *)
  36. device: LONGINT; (* device id *)
  37. vendor: LONGINT; (* vendor id *)
  38. class: LONGINT; (* base, sub, prog-if bytes *)
  39. revision : LONGINT; (* device revision *)
  40. irq, pin: LONGINT
  41. END;
  42. Bus = POINTER TO RECORD
  43. parent : Bus; (* parent bus this bridge is on *)
  44. children : Bus; (* chain of P2P bridges on this bus *)
  45. next : Bus; (* chain of all PCI buses *)
  46. self: Device; (* bridge device as seen by parent *)
  47. devices: Device; (* devices behind this bridge *)
  48. number: LONGINT; (* bus number *)
  49. primary, secondary: LONGINT; (* bridge numbers *)
  50. subordinate: LONGINT (* max number of subordinate buses *)
  51. END;
  52. PROCEDURE ScanBus(VAR bus: Bus; VAR devices : Device): LONGINT;
  53. VAR devfn, max, x, hdrtype, ht, buses: LONGINT; ismulti: BOOLEAN; dev: Device; child: Bus;
  54. BEGIN (* Only call from within EXCLUSIVE regions *)
  55. max := bus.secondary; ismulti := FALSE;
  56. FOR devfn := 0 TO 0FEH DO
  57. IF (devfn MOD 8 = 0) OR ismulti THEN
  58. ReadConfigByte(bus.number, devfn, HdrType, hdrtype);
  59. IF devfn MOD 8 = 0 THEN ismulti := ODD(hdrtype DIV 80H) END;
  60. ReadConfigDword(bus.number, devfn, PCI.DevReg, x);
  61. IF (x # -1) & (x # 0) THEN (* some boards return 0 instead of -1 for empty slot, according to Linux *)
  62. NEW(dev);
  63. dev.bus := bus; dev.devfn := devfn;
  64. dev.vendor := x MOD 10000H;
  65. dev.device := ASH(x, -16) MOD 10000H;
  66. ReadConfigByte(bus.number, devfn, PCI.IntlReg, dev.irq);
  67. ReadConfigByte(bus.number, devfn, PCI.IntlReg+1, dev.pin);
  68. ReadConfigDword(bus.number, devfn, PCI.RevIdReg, x);
  69. dev.class := ASH(x, -8) MOD 1000000H; (* upper 3 bytes *)
  70. dev.revision := x MOD 100H; (* lowest byte *)
  71. CASE ASH(dev.class, -8) OF
  72. 604H: ht := 1 (* bridge pci *)
  73. |607H: ht := 2 (* bridge cardbus *)
  74. ELSE ht := 0
  75. END;
  76. IF ht = hdrtype MOD 80H THEN
  77. dev.next := devices; devices := dev; dev.prev := NIL;
  78. dev.sibling := bus.devices; bus.devices := dev;
  79. IF ASH(dev.class, -8) = 604H THEN (* bridge pci *)
  80. NEW(child);
  81. child.next := bus.children; bus.children := child;
  82. child.self := dev; child.parent := bus;
  83. INC(max); child.secondary := max; child.number := max;
  84. child.primary := bus.secondary; child.subordinate := 0FFH;
  85. ReadConfigDword(bus.number, devfn, 18H, buses);
  86. IF buses MOD 1000000 # 0 THEN
  87. child.primary := buses MOD 100H;
  88. child.secondary := ASH(buses, -8) MOD 100H;
  89. child.subordinate := ASH(buses, -16) MOD 100H;
  90. child.number := child.secondary;
  91. max := ScanBus(child, devices)
  92. ELSE (* configure bus numbers for this bridge *)
  93. KernelLog.String("PCI: Warning: Bus numbers not configured."); KernelLog.Ln;
  94. END
  95. END
  96. ELSE
  97. KernelLog.String("PCI: Warning: Unknown header type (Bus: "); KernelLog.Int(bus.number, 0);
  98. KernelLog.String(", device: "); KernelLog.Int(dev.devfn DIV 8, 0);
  99. KernelLog.String(", function: "); KernelLog.Int(dev.devfn MOD 8, 0);
  100. KernelLog.String(", Header Type: "); KernelLog.Int(hdrtype, 0); KernelLog.Ln;
  101. END;
  102. (* ELSE
  103. ismulti := FALSE *) (* not all functions are continuously numbered *)
  104. END
  105. END
  106. END;
  107. RETURN max
  108. END ScanBus;
  109. PROCEDURE Extract(classcode : LONGINT; VAR class, subclass, protocol : LONGINT);
  110. BEGIN
  111. class := LSH(SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, classcode) * {16..23}), -16);
  112. subclass := LSH(SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, classcode) * {8..15}), -8);
  113. protocol := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, classcode) * {0..7});
  114. END Extract;
  115. PROCEDURE InstallPCIDrivers;
  116. VAR root : Bus; device : Device; nbrOfDevices : LONGINT; class, subclass, protocol : LONGINT;
  117. BEGIN {EXCLUSIVE}
  118. KernelLog.String("Looking for PCI devices..."); KernelLog.Ln;
  119. NEW(root);
  120. nbrOfDevices := ScanBus(root, device);
  121. WHILE(device # NIL) DO
  122. IF Verbose THEN
  123. KernelLog.String("Bus: "); IF device.bus = NIL THEN KernelLog.String("n/a"); ELSE KernelLog.Int(device.bus.number, 2); END;
  124. KernelLog.String(", Device: "); KernelLog.Int(ASH(device.devfn, -3) MOD 20H, 2); KernelLog.String(", Function: "); KernelLog.Int(device.devfn MOD 8, 2);
  125. KernelLog.String(": "); KernelLog.String("VendorID: "); KernelLog.Hex(device.vendor, -4); KernelLog.String(", DeviceID: "); KernelLog.Hex(device.device, -4);
  126. KernelLog.Ln;
  127. END;
  128. IF DriverDatabase.InstallDeviceDriver(DriverDatabase.PCI, device.vendor, device.device, device.revision) THEN
  129. ELSE
  130. Extract(device.class, class, subclass, protocol);
  131. IF DriverDatabase.InstallClassDriver(DriverDatabase.PCI, class, subclass, protocol, device.revision) THEN
  132. END;
  133. END;
  134. device := device.next;
  135. END;
  136. END InstallPCIDrivers;
  137. PROCEDURE HexDigit(ch: CHAR): BOOLEAN;
  138. BEGIN
  139. RETURN (ch >= "0") & (ch <= "9") OR (CAP(ch) >= "A") & (CAP(ch) <= "F")
  140. END HexDigit;
  141. PROCEDURE Read(VAR r: Files.Reader; VAR ch: CHAR);
  142. BEGIN
  143. IF ch = Streams.EOT THEN ch := 0X ELSE r.Char(ch) END
  144. END Read;
  145. PROCEDURE WriteDevice(w: Streams.Writer; class: BOOLEAN; p1, p2, p3: LONGINT; CONST l1, l2, l3: ARRAY OF CHAR; pciids : Files.File);
  146. VAR r: Files.Reader; ch: CHAR; level, value: LONGINT;
  147. PROCEDURE SkipLine(write: BOOLEAN);
  148. BEGIN
  149. WHILE (ch # 0X) & (ch # 0DX) & (ch # 0AX) DO
  150. IF write THEN w.Char(ch) END;
  151. Read(r, ch)
  152. END;
  153. REPEAT Read(r, ch) UNTIL (ch # 0DX) & (ch # 0AX)
  154. END SkipLine;
  155. PROCEDURE ReadHex(VAR x: LONGINT);
  156. BEGIN
  157. x := 0;
  158. LOOP
  159. IF (ch >= "0") & (ch <= "9") THEN
  160. x := x * 16 + (ORD(ch)-ORD("0"))
  161. ELSIF (CAP(ch) >= "A") & (CAP(ch) <= "F") THEN
  162. x := x * 16 + (ORD(CAP(ch))-ORD("A")+10)
  163. ELSE
  164. EXIT
  165. END;
  166. Read(r, ch)
  167. END
  168. END ReadHex;
  169. PROCEDURE GetLine(VAR level, value: LONGINT);
  170. BEGIN
  171. IF class THEN
  172. IF ch = "C" THEN Read(r, ch); Read(r, ch) END
  173. END;
  174. WHILE (ch # 0X) & (ch # 9X) & ~HexDigit(ch) DO SkipLine(FALSE) END;
  175. level := 0; WHILE ch = 9X DO INC(level); Read(r, ch) END;
  176. ReadHex(value);
  177. WHILE ch = " " DO Read(r, ch) END
  178. END GetLine;
  179. PROCEDURE Label(CONST l: ARRAY OF CHAR);
  180. BEGIN
  181. w.String(l); w.String(": ");
  182. END Label;
  183. BEGIN
  184. IF pciids = NIL THEN
  185. Label(l1); w.String("Unknown");
  186. Label(l2); w.String("Unknown");
  187. ELSE
  188. NEW(r, pciids, 0); Read(r, ch);
  189. IF class THEN WHILE (ch # 0X) & (ch # "C") DO SkipLine(FALSE) END; END;
  190. LOOP
  191. GetLine(level, value);
  192. IF (ch = 0X) OR (level = 0) & (value = p1) THEN EXIT END;
  193. SkipLine(FALSE)
  194. END;
  195. Label(l1);
  196. IF (ch # 0X) & (level = 0) & (value = p1) THEN
  197. SkipLine(TRUE); w.String(", ");
  198. LOOP
  199. GetLine(level, value);
  200. IF (ch = 0X) OR (level = 0) OR (level = 1) & (value = p2) THEN EXIT END;
  201. SkipLine(FALSE)
  202. END;
  203. Label(l2);
  204. IF (ch # 0X) & (level = 1) & (value = p2) THEN
  205. SkipLine(TRUE);
  206. LOOP
  207. GetLine(level, value);
  208. IF (ch = 0X) OR (level < 2) OR (level = 2) & (value = p3) THEN EXIT END;
  209. SkipLine(FALSE)
  210. END;
  211. IF (ch # 0X) & (level = 2) & (value = p3) THEN
  212. w.String(", "); Label(l3); SkipLine(TRUE)
  213. END
  214. ELSE
  215. w.String("Unknown")
  216. END
  217. ELSE
  218. w.String("Unknown")
  219. END
  220. END;
  221. END WriteDevice;
  222. PROCEDURE WriteB(w: Streams.Writer; x: LONGINT);
  223. CONST K = 1024; M = K*K; G = K*M;
  224. VAR mult: CHAR;
  225. BEGIN
  226. IF x MOD K # 0 THEN
  227. w.Int(x, 1)
  228. ELSE
  229. IF x MOD M # 0 THEN mult := "K"; x := x DIV K
  230. ELSIF x MOD G # 0 THEN mult := "M"; x := x DIV M
  231. ELSE mult := "G"; x := x DIV G
  232. END;
  233. w.Int(x, 1); w.Char(mult)
  234. END;
  235. w.String("B")
  236. END WriteB;
  237. PROCEDURE WriteBase(w: Streams.Writer; bus, devfn, reg: LONGINT; VAR double: BOOLEAN);
  238. VAR base, basehi, type, size: LONGINT; mask: SET;
  239. BEGIN
  240. double := FALSE; basehi := 0; size := 0;
  241. ReadConfigDword(bus, devfn, reg, base);
  242. IF base # 0 THEN
  243. WriteConfigDword(bus, devfn, reg, -1);
  244. ReadConfigDword(bus, devfn, reg, size);
  245. WriteConfigDword(bus, devfn, reg, base);
  246. IF ODD(base) THEN (* I/O *)
  247. IF ASH(base, -16) = 0 THEN mask := {2..15} ELSE mask := {2..31} END;
  248. type := base MOD 4
  249. ELSE (* memory *)
  250. mask := {4..31}; type := base MOD 10H
  251. END;
  252. size := SYSTEM.VAL(LONGINT, -(SYSTEM.VAL(SET, size) * mask))+1;
  253. size := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, size) * mask);
  254. IF type MOD 8 = 4 THEN (* 64-bit *)
  255. ReadConfigDword(bus, devfn, reg+4, basehi); double := TRUE
  256. END;
  257. DEC(base, type);
  258. (* write *)
  259. w.Char(9X); w.Char(9X);
  260. WriteB(w, size); w.String(" ");
  261. CASE type OF
  262. 0: w.String("32-bit memory")
  263. |1: w.String("I/O")
  264. |4: w.String("64-bit memory")
  265. |8: w.String("prefetchable 32-bit memory")
  266. |12: w.String("prefetchable 64-bit memory")
  267. ELSE w.String("type "); w.Int(type, 1)
  268. END;
  269. w.String(" at ");
  270. IF basehi # 0 THEN w.Hex(basehi, -2) END;
  271. w.Hex(base, -8); w.String("-");
  272. IF basehi # 0 THEN w.Hex(basehi, -8) END;
  273. w.Hex(base + size - 1, -8); w.Ln;
  274. END
  275. END WriteBase;
  276. PROCEDURE WriteDriver(w : Streams.Writer; dev : Device);
  277. VAR d, c : DriverDatabase.Driver; class, subclass, progintf : LONGINT;
  278. BEGIN
  279. w.String("Driver: ");
  280. d := DriverDatabase.GetDeviceSpecific(DriverDatabase.PCI, dev.vendor, dev.device, dev.revision);
  281. Extract(dev.class, class, subclass, progintf);
  282. c := DriverDatabase.GetClassSpecific(DriverDatabase.PCI, class, subclass, progintf, dev.revision);
  283. IF (c = NIL) & (d = NIL) THEN w.String("n/a");
  284. ELSIF (d # NIL) THEN
  285. w.String(d.commands^);
  286. ELSIF (c # NIL) THEN
  287. w.String(c.commands^);
  288. END;
  289. END WriteDriver;
  290. PROCEDURE WriteDev(w: Streams.Writer; dev: Device; pciids : Files.File; details : BOOLEAN);
  291. VAR bus, devfn, hdrtype, classrev, vendor, device, cmd, status, lastreg, reg, base: LONGINT; double: BOOLEAN;
  292. BEGIN
  293. bus := dev.bus.number; devfn := dev.devfn;
  294. ReadConfigByte(bus, devfn, HdrType, hdrtype);
  295. ReadConfigDword(bus, devfn, PCI.RevIdReg, classrev);
  296. ReadConfigWord(bus, devfn, PCI.DevReg, vendor);
  297. ReadConfigWord(bus, devfn, PCI.DevReg+2, device);
  298. ReadConfigWord(bus, devfn, PCI.CmdReg+2, status);
  299. ReadConfigWord(bus, devfn, PCI.CmdReg, cmd);
  300. w.String("Bus "); w.Int(bus, 1);
  301. w.String(", device "); w.Int(ASH(devfn, -3) MOD 20H, 1);
  302. w.String(", function "); w.Int(devfn MOD 8, 1);
  303. w.String(": class/rev "); w.Hex(classrev, -8);
  304. w.String(", vendor/device "); w.Hex(ASH(vendor, 16) + device, -8);
  305. w.String(", status/cmd "); w.Hex(ASH(status, 16) + cmd, -8);
  306. w.Ln;
  307. w.Char(9X);
  308. WriteDevice(w, TRUE, ASH(classrev, -24) MOD 100H, ASH(classrev, -16) MOD 100H, ASH(classrev, -8) MOD 100H, "Class", "Sub-class", "ProgIntfc", pciids);
  309. w.Ln;
  310. w.Char(9X);
  311. WriteDevice(w, FALSE, vendor, device, -1, "Vendor", "Device", "", pciids);
  312. w.Ln;
  313. IF ShowDrivers THEN
  314. w.Char(9X); WriteDriver(w, dev); w.Ln;
  315. END;
  316. IF (dev.irq # 0) OR (dev.pin # 0) THEN
  317. w.Char( 9X); w.Char( 9X);
  318. w.String("IRQ"); w.Int( dev.irq, 1);
  319. IF dev.pin # 0 THEN
  320. w.String(", INT"); w.Char( CHR(ORD("A")+dev.pin-1))
  321. END;
  322. w.Ln;
  323. END;
  324. CASE hdrtype MOD 80H OF
  325. 0: lastreg := PCI.Adr5Reg
  326. |1: lastreg := PCI.Adr1Reg
  327. ELSE lastreg := 0
  328. END;
  329. FOR reg := PCI.Adr0Reg TO lastreg BY 4 DO
  330. WriteBase(w, bus, devfn, reg, double);
  331. IF double THEN INC(reg, 4) END (* modifying FOR variable *)
  332. END;
  333. IF hdrtype MOD 80H = 0 THEN
  334. ReadConfigDword(bus, devfn, PCI.ROMReg, base);
  335. IF base # 0 THEN
  336. w.Char(9X); w.Char(9X);
  337. w.String("ROM at");
  338. w.Hex(base, -8); w.Ln;
  339. END
  340. END;
  341. IF details THEN WriteRegs(w, dev); WriteCmdSts(w, dev); END;
  342. w.Ln;
  343. END WriteDev;
  344. (* Dump PCI configuration space *)
  345. PROCEDURE WriteRegs(w : Streams.Writer; dev : Device);
  346. VAR value, offset : LONGINT;
  347. BEGIN
  348. w.Char(9X); w.String("PCI Configuration Space Registers: "); w.Ln;
  349. w.Char(0EX); (* KernelLog: non-proportional font *)
  350. FOR offset := 0 TO 3CH BY 4 DO
  351. w.Char(9X); w.Char(9X); w.Hex(offset, -2); w.Char("h"); w.Char(9X); w.Char(9X);
  352. ReadConfigDword(dev.bus.number, dev.devfn, PCI.DevReg + offset, value);
  353. w.Hex(value, -8); w.Ln;
  354. END;
  355. w.Char(0FX); (* KernelLog: proportional font *)
  356. END WriteRegs;
  357. (* Decode & display the command and the status register *)
  358. PROCEDURE WriteCmdSts(w : Streams.Writer; dev : Device);
  359. VAR value : LONGINT; dword : SET;
  360. BEGIN
  361. ReadConfigDword(dev.bus.number, dev.devfn, PCI.CmdReg, value); dword := SYSTEM.VAL(SET, value);
  362. w.Char(9X); w.String("Command Register: "); w.Ln;
  363. w.Char(9X); w.Char(9X);
  364. w.String("IO Space: "); IF 0 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
  365. w.String(", Memory Space: "); IF 1 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
  366. w.String(", Bus Master: "); IF 2 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
  367. w.String(", Special Cycles: "); IF 3 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
  368. w.String(", Memory Write and Invalidate: "); IF 4 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
  369. w.Ln;
  370. w.Char(9X); w.Char(9X);
  371. w.String("VGA Palette Snoop: "); IF 5 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
  372. w.String(", Parity Error Response: "); IF 6 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
  373. w.String(", Stepping Control: "); IF 7 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
  374. w.String(", SERR#: "); IF 8 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
  375. w.String(", Fast Back-to-Back: "); IF 9 IN dword THEN w.String("On"); ELSE w.String("Off"); END;
  376. w.Ln;
  377. w.Char(9X); w.String("Status Register: "); w.Ln;
  378. w.Char(9X); w.Char(9X);
  379. w.String("Capabilities List: "); IF 4 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
  380. w.String(", 66MHz Capable: "); IF 5 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
  381. w.String(", Fast Back-to-Back Capable: "); IF 7 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
  382. w.String(", Master Data Parity Error: "); IF 8 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
  383. w.Ln;
  384. w.Char(9X); w.Char(9X);
  385. w.String("DEVSEL timing: ");
  386. IF {9+16, 10+16} * dword = {} THEN w.String("Fast");
  387. ELSIF {9+16, 10+16} * dword = {9+16} THEN w.String("Medium");
  388. ELSIF {9+16, 10+16} * dword = {10+16} THEN w.String("Slow");
  389. ELSE w.String("ERROR");
  390. END;
  391. w.Ln;
  392. w.Char(9X); w.Char(9X);
  393. w.String("Signaled Target Abort: "); IF 11 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
  394. w.String(", Received Target Abort: "); IF 12 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
  395. w.String(", Received Master Abort: "); IF 13 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
  396. w.String(", Signaled System Error: "); IF 14 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
  397. w.String(", Detected Parity Error: "); IF 15 + 16 IN dword THEN w.String("Yes"); ELSE w.String("No"); END;
  398. w.Ln;
  399. END WriteCmdSts;
  400. PROCEDURE ReadConfigByte(bus, devfn, ofs: LONGINT; VAR val: LONGINT);
  401. VAR res: WORD;
  402. BEGIN
  403. res := PCI.ReadConfigByte(bus, ASH(devfn, -3) MOD 20H, devfn MOD 8, ofs, val);
  404. ASSERT(res = PCI.Done)
  405. END ReadConfigByte;
  406. PROCEDURE ReadConfigWord(bus, devfn, ofs: LONGINT; VAR val: LONGINT);
  407. VAR res: WORD;
  408. BEGIN
  409. res := PCI.ReadConfigWord(bus, ASH(devfn, -3) MOD 20H, devfn MOD 8, ofs, val);
  410. ASSERT(res = PCI.Done)
  411. END ReadConfigWord;
  412. PROCEDURE ReadConfigDword(bus, devfn, ofs: LONGINT; VAR val: LONGINT);
  413. VAR res: WORD;
  414. BEGIN
  415. res := PCI.ReadConfigDword(bus, ASH(devfn, -3) MOD 20H, devfn MOD 8, ofs, val);
  416. ASSERT(res = PCI.Done)
  417. END ReadConfigDword;
  418. PROCEDURE WriteConfigDword(bus, devfn, ofs, val: LONGINT);
  419. VAR res: WORD;
  420. BEGIN
  421. res := PCI.WriteConfigDword(bus, ASH(devfn, -3) MOD 20H, devfn MOD 8, ofs, val);
  422. ASSERT(res = PCI.Done)
  423. END WriteConfigDword;
  424. (** Exported commands *)
  425. (** Perform bus enumeration and display information about found PCI busses/devices *)
  426. PROCEDURE Scan*(context : Commands.Context); (** ["-d"|"--details"] ~ *)
  427. VAR
  428. options : Options.Options;
  429. root : Bus; dev, prev: Device;
  430. version, lastPCIBus, hw, count: LONGINT;
  431. pciids : Files.File;
  432. BEGIN {EXCLUSIVE}
  433. NEW(options);
  434. options.Add("d", "details", Options.Flag);
  435. IF options.Parse(context.arg, context.error) THEN
  436. context.out.String("PCITools: PCI bus enumeration:"); context.out.Ln;
  437. IF PCI.PCIPresent(version, lastPCIBus, hw) = PCI.Done THEN
  438. context.out.String("PCI Bus Information: "); context.out.Int(lastPCIBus + 1, 0); context.out.String(" bus(ses) found, PCI version: ");
  439. context.out.Hex(version DIV 256, -2); context.out.Char("."); context.out.Hex(version MOD 256, -2); context.out.Ln;
  440. context.out.Ln;
  441. NEW(root);
  442. root.subordinate := ScanBus(root, dev);
  443. count := 0; prev := NIL;
  444. WHILE dev # NIL DO
  445. dev.prev := prev; prev := dev;
  446. dev := dev.next; INC(count)
  447. END;
  448. pciids := Files.Old(PCIIDS);
  449. WHILE prev # NIL DO
  450. WriteDev(context.out, prev, pciids, options.GetFlag("details"));
  451. prev := prev.prev
  452. END;
  453. context.out.Int(count, 1); context.out.String(" devices found"); context.out.Ln;
  454. ELSE
  455. context.out.String("PCI not present"); context.out.Ln;
  456. END;
  457. END;
  458. END Scan;
  459. (** Perform bus enumeration and install appropriate device drivers if available *)
  460. PROCEDURE DetectHardware*(context : Commands.Context);
  461. VAR ver, last, hw : LONGINT;
  462. BEGIN
  463. IF DriverDatabase.enabled THEN
  464. IF (PCI.PCIPresent(ver, last, hw) = PCI.Done) THEN
  465. InstallPCIDrivers;
  466. ELSE
  467. context.out.String("PCITools: No PCI bus found."); context.out.Ln;
  468. END;
  469. ELSE
  470. context.out.String("PCITools: Automatic hardware detedtion is disabled."); context.out.Ln;
  471. END;
  472. END DetectHardware;
  473. END PCITools.
  474. PCITools.Scan ~ System.Free PCITools DriverDatabase ~
  475. PCITools.Scan details ~
  476. PCITools.DetectHardware ~