MODULE PCI;
IMPORT SYSTEM, Machine, KernelLog;
CONST
Trace = TRUE;
DevReg* = 0H; CmdReg* = 4H; RevIdReg* = 8H; CLSReg* = 0CH;
Adr0Reg* = 10H; Adr1Reg* = 14H; Adr2Reg* = 18H;
Adr3Reg* = 1CH; Adr4Reg* = 20H; Adr5Reg* = 24H;
CISReg* = 28H; SubvReg* = 2CH; ROMReg* = 30H; IntlReg* = 3CH;
IOSpace* = {0};
MemorySpace* = {1};
BusMaster* = {2};
debug = FALSE;
Done* = 0; NoPCI* = -1; NoBios32* = -1; Error* = -2;
FuncNotSupported* = 81H; BadVendorId* = 83H; DeviceNotFound* = 86H;
BadRegisterNumber* = 87H; SetFailed* = 88H; BufferTooSmall* = 89H;
PCIServiceId = 49435024H;
PCIString = 20494350H;
PCIFunctionId = 0B1H*256;
PCIBiosPresent = 1H; findPCIDevice = 2H; findPCIClassCode = 3H; generateSpecialCycle = 6H;
readConfigByte = 8H; readConfigWord = 9H; readConfigDword = 0AH;
writeConfigByte = 0BH; writeConfigWord = 0CH; writeConfigDword = 0DH;
getIrqRoutingOptions = 0EH; setPCIIrq = 0FH;
TYPE
RouteTable* = POINTER TO RouteTableDesc;
RouteTableDesc* = RECORD
busNr*, devNr*, slotNr*: LONGINT;
linkValIntA*, linkValIntB*, linkValIntC*, linkValIntD*: CHAR;
IrqBitmapA*, IrqBitmapB*, IrqBitmapC*, IrqBitmapD*: SET;
next*: RouteTable
END;
RouteBuffer = RECORD
BufferSize, SegSelector: INTEGER;
DataBufferAdr: LONGINT
END;
VAR
Bios32Def: RECORD
sig: ARRAY 4 OF CHAR;
entry: LONGINT;
rev, len, chksum: CHAR;
res: ARRAY 5 OF CHAR;
END;
bios32: BOOLEAN;
pci: BOOLEAN;
pciEntry: LONGINT;
PROCEDURE -BiosServiceDirectory(sadr, sid, fs: LONGINT; VAR badr: SYSTEM.ADDRESS; VAR len, entry: LONGINT): CHAR;
CODE {SYSTEM.i386}
DB 55H ; push ebp
DB 8BH, 0ECH ; mov ebp, esp
DB 66H, 8CH, 0C8H ; mov ax, cs
DB 50H ; push eax
DB 8BH, 45H, 14H ; mov eax, [ebp+20]
DB 8BH, 5DH, 10H ; mov ebx, [ebp+16]
DB 8BH, 55H, 18H ; ; mov edx, [ebp+24]
DB 0FFH, 0D2H ; call edx
; DB 0FFH, 55H, 18H ; call [dword ebp+24]
DB 8BH, 75H, 0CH ; mov esi, [ebp+12]
DB 89H, 1EH ; mov [esi], ebx
DB 8BH, 75H, 08H ; mov esi, [ebp+8]
DB 89H, 0EH ; mov [esi], ecx
DB 8BH, 75H, 04H ; mov esi, [ebp+4]
DB 89H, 16H ; mov [esi], edx
DB 5DH ; pop ebp
DB 081H, 0C4H, 018H, 0H, 0H, 0H ; add esp, 24
END BiosServiceDirectory;
PROCEDURE FindBios32Service*(serviceId, serviceFkt: LONGINT; VAR serviceAdr: SYSTEM.ADDRESS; VAR serviceLen, entryOffset: LONGINT): LONGINT;
VAR ch: CHAR; res: LONGINT; state: SET;
BEGIN {EXCLUSIVE}
IF bios32 THEN
state := Machine.DisableInterrupts();
ch := BiosServiceDirectory(Bios32Def.entry, serviceId, serviceFkt,
serviceAdr, serviceLen, entryOffset);
Machine.RestoreInterrupts(state);
res := ORD(ch)
ELSE
res := NoBios32
END;
RETURN res
END FindBios32Service;
PROCEDURE DetectBios32;
VAR adr, chksum, i: LONGINT; s: ARRAY 16 OF CHAR;
BEGIN
adr := 0E0000H; chksum := 0FFH;
REPEAT
SYSTEM.MOVE(adr, SYSTEM.ADR(s[0]), 16);
IF (s[0] = "_") & (s[1] = "3") & (s[2] = "2") & (s[3] = "_") THEN
chksum := 0; i := 0;
WHILE i < 16 DO
chksum := chksum + ORD(s[i]);
IF FALSE & debug THEN KernelLog.Int(ORD(s[i]), 1); KernelLog.Char(" ") END;
INC(i)
END;
chksum := chksum MOD 256;
END;
INC(adr, 16)
UNTIL (chksum = 0) OR (adr = 0100000H);
bios32 := chksum = 0;
IF bios32 THEN
SYSTEM.MOVE(SYSTEM.ADR(s[0]), SYSTEM.ADR(Bios32Def), 16);
IF Trace THEN
KernelLog.String("PCI Bios32 detected at: "); KernelLog.Hex(adr-16, 8); KernelLog.Ln;
KernelLog.String(" Sig: ");
KernelLog.Char(Bios32Def.sig[0]); KernelLog.Char(Bios32Def.sig[1]);
KernelLog.Char(Bios32Def.sig[2]); KernelLog.Char(Bios32Def.sig[3]);
KernelLog.String(", Entry: "); KernelLog.Hex(Bios32Def.entry, 8);
KernelLog.String(", Revision: "); KernelLog.Int(ORD(Bios32Def.rev), 1);
KernelLog.String(", Length: "); KernelLog.Int(ORD(Bios32Def.len)*16, 1);
KernelLog.String(", Checksum: "); KernelLog.Int(ORD(Bios32Def.chksum), 1);
KernelLog.Ln
END
ELSE
IF Trace THEN
KernelLog.String("No PCI BIOS32 detected"); KernelLog.Ln
END;
END
END DetectBios32;
PROCEDURE -pcicall(entry:LONGINT; VAR eax,ebx,ecx,edx,esi,edi:LONGINT; VAR eflags: SET);
CODE {SYSTEM.i386}
DB 55H ; push ebp
DB 8BH, 0ECH ; mov ebp, esp
DB 8CH, 0C8H ; mov eax, cs
DB 50H ; push eax
DB 8BH, 45H, 08H ; mov eax, [ebp+8]
DB 8BH, 38H ; mov edi, [eax]
DB 8BH, 45H, 0CH ; mov eax, [ebp+12]
DB 8BH, 30H ; mov esi, [eax]
DB 8BH, 45H, 10H ; mov eax, [ebp+16]
DB 8BH, 10H ; mov edx, [eax]
DB 8BH, 45H, 14H ; mov eax, [ebp+20]
DB 8BH, 08H ; mov ecx, [eax]
DB 8BH, 45H, 18H ; mov eax, [ebp+24]
DB 8BH, 18H ; mov ebx, [eax]
DB 8BH, 45H, 1CH ; mov eax, [ebp+28]
DB 8BH, 00H ; mov eax, [eax]
DB 0FFH, 55H, 20H ; call [dword ebp+32]
DB 9CH ; pushfd
DB 50H ; push eax
DB 8BH, 45H, 08H ; mov eax, [ebp+8]
DB 89H, 38H ; mov [eax], edi
DB 8BH, 45H, 0CH ; mov eax, [ebp+12]
DB 89H, 30H ; mov [eax], esi
DB 8BH, 45H, 10H ; mov eax, [ebp+16]
DB 89H, 10H ; mov [eax], edx
DB 8BH, 45H, 14H ; mov eax, [ebp+20]
DB 89H, 08H ; mov [eax], ecx
DB 8BH, 45H, 18H ; mov eax, [ebp+24]
DB 89H, 18H ; mov [eax], ebx
DB 5BH ; pop ebx
DB 8BH, 45H, 1CH ; mov eax, [ebp+28]
DB 89H, 18H ; mov [eax], ebx
DB 5BH ; pop ebx
DB 8BH, 45H, 04H ; mov eax, [ebp+4]
DB 89H, 18H ; mov [eax], ebx
DB 5DH ; pop ebp
DB 081H, 0C4H, 020H, 0H, 0H, 0H ; add esp, 32
END pcicall;
PROCEDURE OutRegs(eax, ebx, ecx, edx, esi, edi: LONGINT; eflags: SET);
BEGIN
IF debug THEN
KernelLog.String(" eax: "); KernelLog.Hex(eax, 8); KernelLog.Ln;
KernelLog.String(" ebx: "); KernelLog.Hex(ebx, 8); KernelLog.Ln;
KernelLog.String(" ecx: "); KernelLog.Hex(ecx, 8); KernelLog.Ln;
KernelLog.String(" edx: "); KernelLog.Hex(edx, 8); KernelLog.Ln;
KernelLog.String(" esi: "); KernelLog.Hex(esi, 8); KernelLog.Ln;
KernelLog.String(" edi: "); KernelLog.Hex(edi, 8); KernelLog.Ln;
KernelLog.String(" eflags: "); KernelLog.Hex(SYSTEM.VAL(LONGINT, eflags), 8); KernelLog.Ln
END
END OutRegs;
PROCEDURE PCIPresent*(VAR version, lastPCIbus, hwMech: LONGINT): LONGINT;
VAR res, eax, ebx, ecx, edx, esi, edi: LONGINT; eflags, state: SET;
BEGIN {EXCLUSIVE}
IF pci THEN
eax := PCIFunctionId + PCIBiosPresent;
state := Machine.DisableInterrupts();
pcicall(pciEntry, eax, ebx, ecx, edx, esi, edi, eflags);
Machine.RestoreInterrupts(state);
res := (eax DIV 100H) MOD 100H;
IF (edx = PCIString) & ~(0 IN eflags) & (res = Done) THEN
version := ebx MOD 10000H;
lastPCIbus := ecx MOD 100H;
hwMech := eax MOD 100H
ELSIF res = 0 THEN res := NoPCI
END;
IF debug THEN
KernelLog.String("PCIPresent:"); KernelLog.Ln;
OutRegs(eax, ebx, ecx, edx, esi, edi, eflags)
END
ELSE
res := NoPCI
END;
RETURN res
END PCIPresent;
PROCEDURE FindPCIDevice*(devId, vendId, idx: LONGINT; VAR busNr, devNr, fktNr: LONGINT): LONGINT;
VAR res, eax, ebx, ecx, edx, esi, edi: LONGINT; eflags, state: SET;
BEGIN {EXCLUSIVE}
IF pci THEN
eax := PCIFunctionId + findPCIDevice;
ecx := devId; edx := vendId; esi := idx;
state := Machine.DisableInterrupts();
pcicall(pciEntry, eax, ebx, ecx, edx, esi, edi, eflags);
Machine.RestoreInterrupts(state);
res := (eax DIV 100H) MOD 100H; ASSERT(~((0 IN eflags) & (res=0)));
IF ~(0 IN eflags) & (res = Done) THEN
busNr := (ebx DIV 100H) MOD 100H;
devNr := (ebx DIV 8) MOD 20H;
fktNr := ebx MOD 8
END;
IF debug THEN
KernelLog.String("FindPCIDevice:"); KernelLog.Ln;
OutRegs(eax, ebx, ecx, edx, esi, edi, eflags)
END
ELSE
res := NoPCI
END;
RETURN res
END FindPCIDevice;
PROCEDURE FindPCIClassCode*(classCode, idx: LONGINT; VAR busNr, devNr, fktNr: LONGINT): LONGINT;
VAR res, eax, ebx, ecx, edx, esi, edi: LONGINT; eflags, state: SET;
BEGIN {EXCLUSIVE}
IF pci THEN
eax := PCIFunctionId + findPCIClassCode;
ecx := classCode; esi := idx;
state := Machine.DisableInterrupts();
pcicall(pciEntry, eax, ebx, ecx, edx, esi, edi, eflags);
Machine.RestoreInterrupts(state);
res := (eax DIV 100H) MOD 100H; ASSERT(~((0 IN eflags) & (res=0)));
IF ~(0 IN eflags) & (res = Done) THEN
busNr := (ebx DIV 100H) MOD 100H;
devNr := (ebx DIV 8) MOD 20H;
fktNr := ebx MOD 8
END;
IF debug THEN
KernelLog.String("FindPCIClassCode:"); KernelLog.Ln;
OutRegs(eax, ebx, ecx, edx, esi, edi, eflags)
END
ELSE
res := NoPCI
END;
RETURN res
END FindPCIClassCode;
PROCEDURE GenerateSpecialCycle*(busNr, specCycleData: LONGINT): LONGINT;
VAR res, eax, ebx, ecx, edx, esi, edi: LONGINT; eflags, state: SET;
BEGIN {EXCLUSIVE}
IF pci THEN
eax := PCIFunctionId + generateSpecialCycle;
ebx := busNr*100H; edx := specCycleData;
state := Machine.DisableInterrupts();
pcicall(pciEntry, eax, ebx, ecx, edx, esi, edi, eflags);
Machine.RestoreInterrupts(state);
res := (eax DIV 100H) MOD 100H; ASSERT(~((0 IN eflags) & (res=0)));
IF debug THEN
KernelLog.String("GenerateSpecialCycle:"); KernelLog.Ln;
OutRegs(eax, ebx, ecx, edx, esi, edi, eflags)
END
ELSE
res := NoPCI
END;
RETURN res
END GenerateSpecialCycle;
PROCEDURE GetIrqRoutingOptions*(VAR rt: RouteTable; VAR IrqBitmap: SET): LONGINT;
CONST dbN = 16*8;
VAR
res, eax, ebx, ecx, edx, esi, edi, i: LONGINT; eflags, state: SET;
rb: RouteBuffer; db: ARRAY dbN OF CHAR;
last: RouteTable;
BEGIN {EXCLUSIVE}
IF pci THEN
eax := PCIFunctionId + getIrqRoutingOptions;
rb.BufferSize := dbN; rb.SegSelector := 0; rb.DataBufferAdr := SYSTEM.ADR(db[0]);
ebx := 0H; edi := SYSTEM.ADR(rb);
state := Machine.DisableInterrupts();
pcicall(pciEntry, eax, ebx, ecx, edx, esi, edi, eflags);
Machine.RestoreInterrupts(state);
res := (eax DIV 100H) MOD 100H; ASSERT(~((0 IN eflags) & (res=0)));
ASSERT(res # BufferTooSmall);
IF ~(0 IN eflags) & (res = Done) THEN
IrqBitmap := SYSTEM.VAL(SET, ebx);
NEW(rt); rt.next := NIL; last := rt; i := 0;
WHILE i < rb.BufferSize DO
NEW(last.next); last := last.next; last.next := NIL;
last.busNr := ORD(db[i]); INC(i);
last.devNr := ORD(db[i]) DIV 8; INC(i);
last.linkValIntA := db[i]; INC(i);
last.IrqBitmapA := SYSTEM.VAL(SET, LONG(ORD(db[i])+ORD(db[i+1])*100H)); INC(i, 2);
last.linkValIntB := db[i]; INC(i);
last.IrqBitmapB := SYSTEM.VAL(SET, LONG(ORD(db[i])+ORD(db[i+1])*100H)); INC(i, 2);
last.linkValIntC:= db[i]; INC(i);
last.IrqBitmapC := SYSTEM.VAL(SET, LONG(ORD(db[i])+ORD(db[i+1])*100H)); INC(i, 2);
last.linkValIntD := db[i]; INC(i);
last.IrqBitmapD := SYSTEM.VAL(SET, LONG(ORD(db[i])+ORD(db[i+1])*100H)); INC(i, 2);
last.slotNr := ORD(db[i]); INC(i);
INC(i)
END;
rt := rt.next
END;
IF debug THEN
KernelLog.String("GetIrqRoutingOptions:"); KernelLog.Ln;
OutRegs(eax, ebx, ecx, edx, esi, edi, eflags)
END
ELSE
res := NoPCI
END;
RETURN res
END GetIrqRoutingOptions;
PROCEDURE SetPCIIrq*(IntPin, IrqNum, busNr, devNr, fktNr: LONGINT): LONGINT;
VAR res, eax, ebx, ecx, edx, esi, edi: LONGINT; eflags, state: SET;
BEGIN {EXCLUSIVE}
IF pci THEN
eax := PCIFunctionId + setPCIIrq;
ecx := IrqNum*100H + IntPin; ebx := busNr*100H+devNr*8+fktNr;
state := Machine.DisableInterrupts();
pcicall(pciEntry, eax, ebx, ecx, edx, esi, edi, eflags);
Machine.RestoreInterrupts(state);
res := (eax DIV 100H) MOD 100H; ASSERT(~((0 IN eflags) & (res=0)));
IF debug THEN
KernelLog.String("SetPCIHwInt:"); KernelLog.Ln;
OutRegs(eax, ebx, ecx, edx, esi, edi, eflags)
END
ELSE
res := NoPCI
END;
RETURN res
END SetPCIIrq;
PROCEDURE Enable*(mask : SET; busNr, devNr, fktNr : LONGINT) : LONGINT;
VAR cmdReg : LONGINT; res : LONGINT;
BEGIN
res := ReadConfigWord(busNr, devNr, fktNr, CmdReg, cmdReg);
IF (res = Done) THEN
IF mask - SYSTEM.VAL(SET, cmdReg) # {} THEN
cmdReg := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, cmdReg) + mask);
res := WriteConfigWord(busNr, devNr, fktNr, CmdReg, cmdReg);
IF (res = Done) THEN
res := ReadConfigWord(busNr, devNr, fktNr, CmdReg, cmdReg);
IF (res = Done) THEN
IF mask - SYSTEM.VAL(SET, cmdReg) # {} THEN
res := Error;
END;
END;
END;
END;
END;
RETURN res;
END Enable;
PROCEDURE ReadConfig(fkt, busNr, devNr, fktNr, regNr: LONGINT; mask: SET; VAR regVal: LONGINT): LONGINT;
VAR res, eax, ebx, ecx, edx, esi, edi: LONGINT; eflags, state: SET;
BEGIN {EXCLUSIVE}
IF pci THEN
eax := PCIFunctionId + fkt;
ebx := busNr*100H+devNr*8+fktNr; edi := regNr;
state := Machine.DisableInterrupts();
pcicall(pciEntry, eax, ebx, ecx, edx, esi, edi, eflags);
Machine.RestoreInterrupts(state);
res := (eax DIV 100H) MOD 100H; ASSERT(~((0 IN eflags) & (res=0)));
IF ~(0 IN eflags) & (res = Done) THEN
regVal := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, ecx)*mask)
END;
IF debug THEN
KernelLog.String("ReadConfig ("); KernelLog.Int(fkt, 1); KernelLog.String("):"); KernelLog.Ln;
OutRegs(eax, ebx, ecx, edx, esi, edi, eflags)
END
ELSE
res := NoPCI
END;
RETURN res
END ReadConfig;
PROCEDURE ReadConfigByte*(busNr, devNr, fktNr, regNr: LONGINT; VAR regVal: LONGINT): LONGINT;
BEGIN
RETURN ReadConfig(readConfigByte, busNr, devNr, fktNr, regNr, {0..7}, regVal)
END ReadConfigByte;
PROCEDURE ReadConfigWord*(busNr, devNr, fktNr, regNr: LONGINT; VAR regVal: LONGINT): LONGINT;
BEGIN
ASSERT(regNr MOD 2 = 0);
RETURN ReadConfig(readConfigWord, busNr, devNr, fktNr, regNr, {0..15}, regVal)
END ReadConfigWord;
PROCEDURE ReadConfigDword*(busNr, devNr, fktNr, regNr: LONGINT; VAR regVal: LONGINT): LONGINT;
BEGIN
ASSERT(regNr MOD 4 = 0);
RETURN ReadConfig(readConfigDword, busNr, devNr, fktNr, regNr, {0..31}, regVal)
END ReadConfigDword;
PROCEDURE WriteConfig(fkt, busNr, devNr, fktNr, regNr, regVal: LONGINT): LONGINT;
VAR res, eax, ebx, ecx, edx, esi, edi: LONGINT; eflags, state: SET;
BEGIN {EXCLUSIVE}
IF pci THEN
eax := PCIFunctionId + fkt;
ebx := busNr*100H+devNr*8+fktNr; ecx := regVal; edi := regNr;
state := Machine.DisableInterrupts();
pcicall(pciEntry, eax, ebx, ecx, edx, esi, edi, eflags);
Machine.RestoreInterrupts(state);
res := (eax DIV 100H) MOD 100H; ASSERT(~((0 IN eflags) & (res=0)));
IF debug THEN
KernelLog.String("WriteConfig ("); KernelLog.Int(fkt, 1); KernelLog.String("):"); KernelLog.Ln;
OutRegs(eax, ebx, ecx, edx, esi, edi, eflags)
END
ELSE
res := NoPCI
END;
RETURN res
END WriteConfig;
PROCEDURE WriteConfigByte*(busNr, devNr, fktNr, regNr, regVal: LONGINT): LONGINT;
BEGIN
RETURN WriteConfig(writeConfigByte, busNr, devNr, fktNr, regNr, regVal)
END WriteConfigByte;
PROCEDURE WriteConfigWord*(busNr, devNr, fktNr, regNr, regVal: LONGINT): LONGINT;
BEGIN
ASSERT(regNr MOD 2 = 0);
RETURN WriteConfig(writeConfigWord, busNr, devNr, fktNr, regNr, regVal)
END WriteConfigWord;
PROCEDURE WriteConfigDword*(busNr, devNr, fktNr, regNr, regVal: LONGINT): LONGINT;
BEGIN
ASSERT(regNr MOD 4 = 0);
RETURN WriteConfig(writeConfigDword, busNr, devNr, fktNr, regNr, regVal)
END WriteConfigDword;
PROCEDURE DetectPCI;
VAR res, len, entry: LONGINT; adr: SYSTEM.ADDRESS;
BEGIN
res := FindBios32Service(PCIServiceId, 0, adr, len, entry);
pci := res = Done;
IF pci THEN
Machine.MapPhysical(adr, len, adr);
ASSERT(adr # Machine.NilAdr);
pciEntry := adr+entry
END
END DetectPCI;
PROCEDURE Show*;
VAR version, lastPCIBus, hwMech, res : LONGINT;
BEGIN
IF ~PCIDisabled() THEN
res := PCIPresent(version, lastPCIBus, hwMech);
IF (res = Done) THEN
KernelLog.Enter;
KernelLog.String("PCI: "); KernelLog.Int(lastPCIBus + 1, 0); KernelLog.String(" bus(ses) found, PCI version: ");
KernelLog.Hex(version DIV 256, -2); KernelLog.Char("."); KernelLog.Hex(version MOD 256, -2);
KernelLog.Exit;
ELSE
KernelLog.Enter; KernelLog.String("PCI: No bus found."); KernelLog.Exit;
END;
ELSE
KernelLog.Enter; KernelLog.String("PCI: Not available (Disabled by user)."); KernelLog.Exit;
END;
END Show;
PROCEDURE PCIDisabled() : BOOLEAN;
VAR string : ARRAY 2 OF CHAR;
BEGIN
Machine.GetConfig("DisablePCI", string);
RETURN string = "1";
END PCIDisabled;
BEGIN
pci := FALSE; bios32 := FALSE;
IF ~PCIDisabled() THEN
DetectBios32;
DetectPCI;
END;
Show;
END PCI.
(**
Notes
PCI devices are uniquely identified by their vendor ID and device ID. For example, a 3Com 905B Etherlink XL ethernet card has vendor ID 10B7H (3Com) and device ID 9055H. To get access to this card, use the FindPCIDevice call. The third parameter (idx) is used to find multiple instances of the card. If set to 0, the first card is returned; if set to 1, the second; etc. The last three parameters return the bus number, device number and function number of the card, respectively. This triple can be used with the other calls (e.g., ReadConfig..., WriteConfig...) to address a specific card.
Example:
VAR res, bus, dev, fkt: LONGINT;
(* look for a 3Com 905B ethernet card *)
res := PCI.FindPCIDevice(9055H, 10B7H, 0, bus, dev, fkt);
IF res = PCI.Done THEN (* found at (bus, dev, fkt) *) END
The PCI configuration area is a standardized set of registers provided by every PCI device. It can be accessed using the ReadConfig... and WriteConfig... calls. Typically, registers 10H, 14H, ..., 24H specify the base addresses of a card. Bit 0 is 1 if the address is in the I/O space, and 0 if it is in the physical memory space. For I/O addresses, the bottom two bits should be masked off, and for physical memory addresses, the bottom 4 bits should be masked off.
Example:
VAR res, adr: LONGINT;
(* find the I/O base address of the ethernet controller *)
res := PCI.ReadConfigDword(bus, dev, fkt, 10H, adr);
IF res = PCI.Done THEN
ASSERT(ODD(adr)); (* must be I/O mapped *)
DEC(adr, adr MOD 4); (* strip lower 2 bits *)
...
SYSTEM.PORTIN(adr+X, x) (* read some device register *)
END
To access a memory-mapped device, its address range has to be mapped into the virtual address space first.
Example:
CONST Size = 4096; (* the device has 4KB of registers *)
VAR res, physAdr, virtAdr: LONGINT;
(* find the base address of a memory-mapped device *)
res := PCI.ReadConfigDword(bus, dev, fkt, 10H, physAdr);
IF res = PCI.Done THEN
ASSERT(~ODD(physAdr)); (* must be memory mapped *)
DEC(physAdr, physAdr MOD 16); (* strip lower 4 bits *)
Machine.MapPhysical(physAdr, Size, virtAdr);
...
x := SYSTEM.GET32(virtAdr+X); (* read some device register *)
...
Machine.UnmapPhysical(virtAdr, Size)
END
*)