MODULE RTL8169;
IMPORT
SYSTEM, Kernel, Machine, PCI,
Objects, Modules, Plugins, Network, KernelLog;
CONST
Name = "RTL8169#";
Description = "RealTek 8169 Gigabit ethernet driver";
MaxETHFrameSize = 1514;
TxMaxSize = 1600;
RxMaxSize = 600H;
RxRingSize = 1024;
TxRingSize = 1024;
SizeOfRxTxFDHdr = 16;
InterruptMask = {0, 1, 2, 3, 4, 5, 6, 7, 15};
Promisc = FALSE;
UnknownHW = 0;
RTL8169 = 1;
RTL8169S = 2;
DebugFind = 0;
DebugInit = 1;
DebugConfigs = 2;
DebugHWVer = 3;
DebugMAC = 4;
DebugStatus = 5;
DebugRxRing = 6;
DebugTxRing = 7;
DebugReceive = 8;
DebugTransmit = 9;
DebugInterrupt = 10;
DebugCleanup = 31;
Debug = {DebugFind, DebugInit, DebugTxRing, DebugCleanup};
VAR
installed: LONGINT;
TYPE
RxTxDescriptor = RECORD
flags: SET;
vLanTag: LONGINT;
bufAdrLo, bufAdrHi: LONGINT;
END;
TxBuffer = POINTER TO RECORD
data: ARRAY TxMaxSize OF CHAR;
next: TxBuffer;
END;
RxBuffer = POINTER TO RECORD
buf: Network.Buffer;
next: RxBuffer;
END;
LinkDevice = OBJECT (Network.LinkDevice)
VAR
ctrl: Controller;
hw: LONGINT;
PROCEDURE Linked*(): LONGINT;
BEGIN
RETURN ctrl.linkStatus;
END Linked;
PROCEDURE DoSend*(dst: Network.LinkAdr; type: LONGINT; VAR l3hdr, l4hdr, data: ARRAY OF CHAR; h3len, h4len, dofs, dlen: LONGINT);
BEGIN
ctrl.SendFrame(dst, type, l3hdr, l4hdr, data, h3len, h4len, dofs, dlen);
END DoSend;
PROCEDURE Finalize(connected: BOOLEAN);
BEGIN
ctrl.Finalize;
Finalize^(connected);
END Finalize;
END LinkDevice;
Controller = OBJECT
VAR
next: Controller;
base: SYSTEM.ADDRESS; irq: LONGINT;
dev: LinkDevice;
rds: ARRAY RxRingSize OF RxTxDescriptor;
tds: ARRAY TxRingSize OF RxTxDescriptor;
curRD, curTD: LONGINT;
firstRD, firstTD: LONGINT;
lastRD, lastTD: LONGINT;
rxBuffer, rxLast: RxBuffer;
txBuffer, txLast: TxBuffer;
nofFreeTx: LONGINT;
nRxOverflow: HUGEINT;
nTxOverflow: HUGEINT;
nRxFrames, nTxFrames: LONGINT;
nRxErrorFrames: LONGINT;
nTxErrorFrames: LONGINT;
linkStatus: LONGINT;
PROCEDURE &Init*(dev: LinkDevice; base: SYSTEM.ADDRESS; irq: LONGINT);
VAR
res, i: LONGINT;
s: SET;
BEGIN
SELF.next := installedControllers;
installedControllers := SELF;
SELF.base := base;
SELF.dev := dev;
SELF.irq := irq;
dev.ctrl := SELF;
nRxOverflow := 0;
nTxOverflow := 0;
nRxFrames := 0;
nTxFrames := 0;
nRxErrorFrames := 0;
nTxErrorFrames := 0;
dev.calcChecksum := {Network.ChecksumIP, Network.ChecksumTCP, Network.ChecksumUDP};
FOR i := 0 TO 5 DO
dev.broadcast[i] := 0FFX
END;
s := SYSTEM.VAL(SET, Read8(52H));
IF ~ (2 IN s) THEN
KernelLog.String("I/O Mapping is disabled!");
HALT(1000);
END;
IF ~ (3 IN s) THEN
KernelLog.String("MMIO is disabled!");
HALT(1000);
END;
s := SYSTEM.VAL(SET, Read8(6CH));
IF ~ (4 IN s) THEN
HwReset;
EnableTBI;
END;
ReadMACAddress;
GetHardwareVersion;
ResetNIC;
EnableTxRx(TRUE, TRUE);
Write8(0ECH, 32H);
Write16(0DAH, RxMaxSize);
s := SYSTEM.VAL(SET, Read32(40H));
s := (s - {17..18, 19}) + {8..10, 25};
Write32(40H, SYSTEM.VAL(LONGINT, s));
s := SYSTEM.VAL(SET, Read16(0E0H));
s := s + {3, 5};
Write16(0E0H, SYSTEM.VAL(LONGINT, s));
res := SetupTxRing();
Write32(20H, res);
Write32(20H + 4, 0);
res := SetupRxRing();
Write32(0E4H, res);
Write32(0E4H + 4, 0);
Write32(04CH, 0);
ConfigRx;
IF (irq >= 1) & (irq <= 15) THEN
Objects.InstallHandler(SELF.HandleInterrupt, Machine.IRQ0 + irq)
END;
Write16(3CH, SYSTEM.VAL(LONGINT, InterruptMask));
UpdateLinkStatus;
Network.registry.Add(dev, res);
ASSERT(res = Plugins.Ok);
INC(installed);
IF DebugConfigs IN Debug THEN
DebugConfig;
END;
END Init;
PROCEDURE SendFrame(dst: Network.LinkAdr; type: LONGINT; VAR l3hdr, l4hdr, data: ARRAY OF CHAR; h3len, h4len, dofs, dlen: LONGINT);
VAR
txLen, offset, type4: LONGINT;
bufBase: SYSTEM.ADDRESS;
chksums: SET;
BEGIN {EXCLUSIVE}
IF nofFreeTx <= 0 THEN
KernelLog.String("no tx buffers"); KernelLog.Ln;
INC(nTxOverflow);
END;
AWAIT(nofFreeTx > 0);
txLen := 14 + h3len + h4len + dlen;
bufBase := SYSTEM.ADR(txBuffer.data);
SYSTEM.MOVE(SYSTEM.ADR(dst[0]), bufBase, 6);
SYSTEM.MOVE(SYSTEM.ADR(dev.local[0]), bufBase + 6, 6);
SYSTEM.PUT16(bufBase + 12, SYSTEM.ROT(SYSTEM.VAL(INTEGER, SHORT(type)), 8));
offset := 14;
IF h3len > 0 THEN
SYSTEM.MOVE(SYSTEM.ADR(l3hdr[0]), bufBase + offset, h3len);
INC(offset, h3len);
END;
IF h4len > 0 THEN
SYSTEM.MOVE(SYSTEM.ADR(l4hdr[0]), bufBase + offset, h4len);
INC(offset, h4len);
END;
IF offset + dlen < MaxETHFrameSize THEN
SYSTEM.MOVE(SYSTEM.ADR(data[0]) + dofs, bufBase + offset, dlen);
INC(offset, dlen);
END;
WHILE offset < 60 DO
txBuffer.data[offset] := CHR(0);
INC(offset);
INC(txLen);
END;
IF DebugTransmit IN Debug THEN
KernelLog.String("Sending frame of length ");
KernelLog.Int(txLen, 0);
KernelLog.Ln;
END;
chksums := {};
IF type = 0800H THEN
INCL(chksums, 18);
type4 := SYSTEM.VAL(SHORTINT, l3hdr[9]);
IF type4 = 6 THEN
INCL(chksums, 16);
ELSIF type4 = 17 THEN
INCL(chksums, 17);
END;
END;
tds[curTD].flags := tds[curTD].flags * {30};
tds[curTD].flags := tds[curTD].flags + {31, 29, 28} + chksums;
tds[curTD].flags := tds[curTD].flags + (SYSTEM.VAL(SET, txLen) * {0..15});
INC(curTD);
IF curTD = TxRingSize THEN
curTD := firstTD;
END;
txBuffer := txBuffer.next;
DEC(nofFreeTx);
Write8(38H, SYSTEM.VAL(LONGINT, {6}));
END SendFrame;
PROCEDURE ConfigRx;
VAR s: SET;
BEGIN
s := SYSTEM.VAL(SET, Read32(44H));
s := (s * {7, 11..12, 17..31}) + {1..3, 9, 14..15, 16};
IF Promisc THEN
INCL(s, 0);
END;
Write32(44H, SYSTEM.VAL(LONGINT, s));
Write32(08H, SHORT(0FFFFFFFFH));
Write32(08H + 4, SHORT(0FFFFFFFFH));
END ConfigRx;
PROCEDURE SetTimer(val: LONGINT);
BEGIN
Write32(58H, val);
END SetTimer;
PROCEDURE GetHardwareVersion;
VAR s: SET;
BEGIN
s := SYSTEM.VAL(SET, Read32(40H));
s := s * {23, 26..30};
IF DebugHWVer IN Debug THEN
KernelLog.String("Hardware Version: ");
END;
IF s = {} THEN
dev.hw := RTL8169;
IF DebugHWVer IN Debug THEN
KernelLog.String("RTL8169");
END;
ELSIF ((s * {23, 26}) # {}) & ((s * {27..30}) = {}) THEN
dev.hw := RTL8169S;
IF DebugHWVer IN Debug THEN
KernelLog.String("RTL8169S/RTL8110S");
END;
ELSE
dev.hw := UnknownHW;
IF DebugHWVer IN Debug THEN
KernelLog.String("Hardware Version is unknown");
END;
END;
IF DebugHWVer IN Debug THEN
KernelLog.Ln;
END;
END GetHardwareVersion;
PROCEDURE ResetNIC;
VAR s: SET;
BEGIN
Write8(37H, SYSTEM.VAL(LONGINT, {4}));
REPEAT
Delay(10);
s := SYSTEM.VAL(SET, Read8(37H));
UNTIL ~(4 IN s);
END ResetNIC;
PROCEDURE EnableTxRx(tx, rx: BOOLEAN);
VAR s: SET;
BEGIN
s := SYSTEM.VAL(SET, Read8(37H));
s := s * {0..1, 5..7};
IF tx THEN
INCL(s, 2);
END;
IF rx THEN
INCL(s, 3);
END;
Write8(37H, SYSTEM.VAL(LONGINT, s));
END EnableTxRx;
PROCEDURE ReadMACAddress;
VAR
i: INTEGER;
res: LONGINT;
BEGIN
IF DebugMAC IN Debug THEN
KernelLog.String("MAC address is: ");
END;
FOR i := 0 TO 5 DO
res := Read8(i);
SYSTEM.PUT8(SYSTEM.ADR(dev.local[i]), res);
IF DebugMAC IN Debug THEN
IF i > 0 THEN
KernelLog.String("-");
END;
KernelLog.Hex(ORD(dev.local[i]), -2);
END;
END;
IF DebugMAC IN Debug THEN
KernelLog.Ln;
END;
dev.adrSize := 6;
END ReadMACAddress;
PROCEDURE Read8(reg: LONGINT): SHORTINT;
BEGIN
RETURN SYSTEM.GET8(base + reg);
END Read8;
PROCEDURE Write8(reg: LONGINT; val: LONGINT);
BEGIN
SYSTEM.PUT8(base + reg, SHORT(SHORT(val)));
END Write8;
PROCEDURE Read16(reg: LONGINT): INTEGER;
BEGIN
RETURN SYSTEM.GET16(base + reg);
END Read16;
PROCEDURE Write16(reg: LONGINT; val: LONGINT);
BEGIN
SYSTEM.PUT16(base + reg, SHORT(val));
END Write16;
PROCEDURE Read32(reg: LONGINT): LONGINT;
BEGIN
RETURN SYSTEM.GET32(base + reg);
END Read32;
PROCEDURE Write32(reg: LONGINT; val: LONGINT);
BEGIN
SYSTEM.PUT32(base + reg, val);
END Write32;
PROCEDURE EnableTBI;
VAR
s: SET;
i: LONGINT;
BEGIN
IF 7 IN SYSTEM.VAL(SET, Read8(6CH)) THEN RETURN END;
s := PHYRead(04H) + {5..8};
PHYWrite(04H, s);
PHYWrite(09H, {9});
PHYWrite(00H, {9, 12});
Delay(100);
FOR i := 1 TO 1000 DO
s := PHYRead(01H);
IF 5 IN s THEN
Delay(100);
IF DebugStatus IN Debug THEN
PrintStatus;
END;
RETURN;
ELSE
Delay(100);
END;
END;
END EnableTBI;
PROCEDURE HwReset;
VAR
s: SET;
i: LONGINT;
BEGIN
s := PHYRead(00H) + {15};
PHYWrite(00H, s);
FOR i := 1 TO 50 DO
IF ~(15 IN PHYRead(00H)) THEN
RETURN;
END;
END;
END HwReset;
PROCEDURE PHYWrite(regAdr: LONGINT; data: SET);
VAR
s: SET;
i: LONGINT;
BEGIN
s := {31};
s := s + (SYSTEM.VAL(SET, regAdr * 010000H) * {16..20});
s := s + (data * {0..15});
Write32(60H, SYSTEM.VAL(LONGINT, s));
Delay(100);
FOR i := 1 TO 2000 DO
IF SYSTEM.VAL(SET, Read32(60H)) * {31} = {} THEN
RETURN;
END;
Delay(100);
END;
END PHYWrite;
PROCEDURE PHYRead(regAdr: LONGINT): SET;
VAR
s: SET;
i: LONGINT;
BEGIN
s := SYSTEM.VAL(SET, regAdr * 010000H) * {16..20};
Write32(60H, SYSTEM.VAL(LONGINT, s));
Delay(100);
FOR i := 1 TO 2000 DO
s := SYSTEM.VAL(SET, Read32(60H));
IF 31 IN s THEN
RETURN (s * {0..15});
END;
Delay(100);
END;
RETURN {};
END PHYRead;
PROCEDURE AllocBuffer(VAR buf: TxBuffer);
BEGIN
NEW(buf);
END AllocBuffer;
PROCEDURE SetupRxRing(): Machine.Address32;
VAR
r: LONGINT;
adr, physAdr: SYSTEM.ADDRESS;
buf, prev: RxBuffer;
BEGIN
adr := SYSTEM.ADR(rds[0]);
adr := Machine.PhysicalAdr(adr, SizeOfRxTxFDHdr);
IF adr MOD 256 = 0 THEN
firstRD := 0;
ELSE
firstRD := 16 - (SHORT (adr MOD 256) DIV 16);
END;
IF DebugRxRing IN Debug THEN
KernelLog.String("Rx descriptor start = ");
KernelLog.Hex(adr, 8);
KernelLog.Ln;
KernelLog.String("first Rx descriptor id = ");
KernelLog.Int(firstRD, 0);
KernelLog.Ln;
END;
FOR r := firstRD TO RxRingSize - 1 DO
NEW(buf);
buf.buf := Network.GetNewBuffer();
ASSERT(buf.buf # NIL);
adr := SYSTEM.ADR(buf.buf.data[0]);
physAdr := Machine.PhysicalAdr(adr, Network.MaxPacketSize);
ASSERT(physAdr # Machine.NilAdr);
rds[r].flags := {31};
rds[r].flags := rds[r].flags + (SYSTEM.VAL(SET, Network.MaxPacketSize) * {0..13});
rds[r].vLanTag := 0;
rds[r].bufAdrLo := Machine.Ensure32BitAddress (physAdr);
rds[r].bufAdrHi := 0;
IF prev # NIL THEN
prev.next := buf;
ELSE
rxBuffer := buf;
END;
prev := buf;
END;
rxLast := buf;
rxLast.next := rxBuffer;
INCL(rds[RxRingSize - 1].flags, 30);
curRD := firstRD;
adr := SYSTEM.ADR(rds[firstRD]);
RETURN Machine.Ensure32BitAddress (Machine.PhysicalAdr(adr, SizeOfRxTxFDHdr));
END SetupRxRing;
PROCEDURE SetupTxRing(): Machine.Address32;
VAR
r: LONGINT;
adr, physAdr: SYSTEM.ADDRESS;
buf, prev: TxBuffer;
BEGIN
adr := SYSTEM.ADR(tds[0]);
adr := Machine.PhysicalAdr(adr, SizeOfRxTxFDHdr);
IF adr MOD 256 = 0 THEN
firstTD := 0;
ELSE
firstTD := 16 - (SHORT (adr MOD 256) DIV 16);
END;
lastTD := firstTD;
nofFreeTx := TxRingSize - firstTD;
IF DebugTxRing IN Debug THEN
KernelLog.String("Tx descriptor start = ");
KernelLog.Hex(adr, -8);
KernelLog.Ln;
KernelLog.String("first Tx descriptor id = ");
KernelLog.Int(firstTD, 0);
KernelLog.Ln;
KernelLog.String("nofFreeTx = ");
KernelLog.Int(nofFreeTx, 0);
KernelLog.Ln;
END;
FOR r := firstTD TO TxRingSize - 1 DO
AllocBuffer(buf);
adr := SYSTEM.ADR(buf.data[0]);
physAdr := Machine.PhysicalAdr(adr, TxMaxSize);
ASSERT(physAdr # Machine.NilAdr);
tds[r].flags := {};
tds[r].vLanTag := 0;
tds[r].bufAdrLo := Machine.Ensure32BitAddress (physAdr);
tds[r].bufAdrHi := 0;
IF prev # NIL THEN
prev.next := buf;
ELSE
txBuffer := buf;
END;
prev := buf;
END;
txLast := buf;
txLast.next := txBuffer;
INCL(tds[TxRingSize - 1].flags, 30);
curTD := firstTD;
adr := SYSTEM.ADR(tds[firstTD]);
RETURN Machine.Ensure32BitAddress (Machine.PhysicalAdr(adr, SizeOfRxTxFDHdr));
END SetupTxRing;
PROCEDURE ReadFrames;
VAR
adr: SYSTEM.ADDRESS; type, size: LONGINT;
dstAdr: Network.LinkAdr;
buf: Network.Buffer;
s: SET;
BEGIN
WHILE ~(31 IN rds[curRD].flags) DO
IF (21 IN rds[curRD].flags) THEN
INC(nRxErrorFrames);
ELSIF CheckChecksumErrors(rds[curRD]) THEN
size := SYSTEM.VAL(LONGINT, rds[curRD].flags * {0..13});
IF DebugReceive IN Debug THEN
KernelLog.String("Received a frame of length ");
KernelLog.Int(size, 0);
KernelLog.Ln;
END;
adr := SYSTEM.ADR(rxBuffer.buf.data[0]);
dstAdr := SYSTEM.VAL(Network.LinkAdr, rxBuffer.buf.data[0]);
rxBuffer.buf.src := SYSTEM.VAL(Network.LinkAdr, rxBuffer.buf.data[6]);
type := Network.GetNet2(rxBuffer.buf.data, 12);
buf := rxBuffer.buf;
buf.ofs := 14;
buf.len := size - 14;
buf.calcChecksum := { Network.ChecksumIP, Network.ChecksumUDP, Network.ChecksumTCP };
buf.next := NIL;
buf.prev := NIL;
IF type = 0DEADH THEN
SendFrame(buf.src, type + 1, buf.data, buf.data, buf.data, 0, 0, 0, buf.len);
ELSIF type = 0DEADH + 1 THEN
ELSE
dev.QueueBuffer(buf, type);
END;
INC(nRxFrames);
IF (type # 0DEADH) & (type # 0DEADH + 1) THEN
rxBuffer.buf := Network.GetNewBuffer();
buf := rxBuffer.buf;
ASSERT(rxBuffer.buf # NIL);
IF buf # NIL THEN
rds[curRD].bufAdrLo := Machine.Ensure32BitAddress (Machine.PhysicalAdr(SYSTEM.ADR(rxBuffer.buf.data[0]), Network.MaxPacketSize));
END;
END;
ELSE
IF DebugReceive IN Debug THEN
KernelLog.String("Checksum error detected!"); KernelLog.Ln;
END;
INC(nRxErrorFrames);
END;
rds[curRD].flags := {31} + (rds[curRD].flags * {30}) + (SYSTEM.VAL(SET, Network.MaxPacketSize) * {0..13});
rds[curRD].vLanTag := 0;
s := rds[curRD].flags;
rxBuffer := rxBuffer.next;
INC(curRD);
IF curRD = RxRingSize THEN
curRD := firstRD;
END;
END;
END ReadFrames;
PROCEDURE CheckChecksumErrors(d: RxTxDescriptor): BOOLEAN;
VAR proto: SET;
BEGIN
proto := d.flags * {17..18};
IF proto = {} THEN
RETURN TRUE;
ELSIF proto = {17} THEN
RETURN d.flags * {14, 16} = {};
ELSIF proto = {18} THEN
RETURN d.flags * {15, 16} = {};
ELSE
RETURN d.flags * {16} = {};
END;
END CheckChecksumErrors;
PROCEDURE HandleInterrupt;
VAR
status, ack: SET;
BEGIN
Write16(3CH, 0);
ack := {0};
status := SYSTEM.VAL(SET, Read16(3EH));
IF (15 IN InterruptMask) & (15 IN status) THEN
IF DebugInterrupt IN Debug THEN
KernelLog.String("System Error Interrupt"); KernelLog.Ln;
END;
INCL(ack, 15);
END;
IF (14 IN InterruptMask) & (14 IN status) THEN
IF DebugInterrupt IN Debug THEN
KernelLog.String("Timeout Interrupt"); KernelLog.Ln;
END;
INCL(ack, 14);
END;
IF (8 IN InterruptMask) & (8 IN status) THEN
IF DebugInterrupt IN Debug THEN
KernelLog.String("Software Interrupt"); KernelLog.Ln;
END;
INCL(ack, 8);
END;
IF (7 IN InterruptMask) & (7 IN status) THEN
IF DebugInterrupt IN Debug THEN
KernelLog.String("Tx Descriptor Unavailable Interrupt"); KernelLog.Ln;
END;
INCL(ack, 7);
END;
IF (6 IN InterruptMask) & (6 IN status) THEN
IF DebugInterrupt IN Debug THEN
KernelLog.String("Rx FIFO Overflow Interrupt"); KernelLog.Ln;
END;
INC(nRxOverflow);
INCL(ack, 6);
END;
IF (5 IN InterruptMask) & (5 IN status) THEN
IF DebugInterrupt IN Debug THEN
KernelLog.String("Link Change Interrupt"); KernelLog.Ln;
END;
UpdateLinkStatus;
INCL(ack, 5);
END;
IF (4 IN InterruptMask) & (4 IN status) THEN
IF DebugInterrupt IN Debug THEN
END;
INCL(ack, 4);
END;
IF (3 IN InterruptMask) & (3 IN status) THEN
IF DebugInterrupt IN Debug THEN
KernelLog.String("Transmit Error Interrupt"); KernelLog.Ln;
END;
INCL(ack, 3);
INC(nTxErrorFrames);
INCL(status, 2);
END;
IF (2 IN InterruptMask) & (2 IN status) THEN
IF DebugInterrupt IN Debug THEN
KernelLog.String("Transmit OK Interrupt"); KernelLog.Ln;
END;
UpdateTxRing;
INCL(ack, 2);
END;
IF (1 IN InterruptMask) & (1 IN status) THEN
IF DebugInterrupt IN Debug THEN
KernelLog.String("Receive Error Interrupt"); KernelLog.Ln;
END;
INCL(ack, 1);
INCL(status, 0);
END;
IF (0 IN InterruptMask) & (0 IN status) THEN
IF DebugInterrupt IN Debug THEN
END;
ReadFrames;
INCL(ack, 0);
END;
ack := status;
Write16(3EH, SYSTEM.VAL(LONGINT, ack));
Write16(3CH, SYSTEM.VAL(LONGINT, InterruptMask));
END HandleInterrupt;
PROCEDURE UpdateLinkStatus;
BEGIN
IF 1 IN SYSTEM.VAL(SET, Read8(6CH)) THEN
linkStatus := Network.LinkLinked;
ELSE
linkStatus := Network.LinkNotLinked;
END;
END UpdateLinkStatus;
PROCEDURE UpdateTxRing;
VAR i: LONGINT;
BEGIN { EXCLUSIVE }
i := lastTD;
WHILE (i # curTD) DO
IF DebugTransmit IN Debug THEN
KernelLog.String("*** Tx OK ***"); KernelLog.Ln;
END;
INC(i);
INC(nTxFrames);
INC(nofFreeTx);
IF i = TxRingSize THEN
i := firstTD;
END;
txLast := txLast.next;
END;
lastTD := i;
END UpdateTxRing;
PROCEDURE Finalize;
VAR
s: SET;
BEGIN
Network.registry.Remove(dev);
s := SYSTEM.VAL(SET, Read8(37H));
Write8(37H, SYSTEM.VAL(SHORTINT, s - {2, 3}));
Write8(37H, SYSTEM.VAL(SHORTINT, {4}));
Write16(3CH, 0);
WHILE (rxBuffer # NIL) & (rxBuffer.buf # NIL) DO
Network.ReturnBuffer(rxBuffer.buf);
rxBuffer.buf := NIL;
rxBuffer := rxBuffer.next;
END;
IF DebugCleanup IN Debug THEN
KernelLog.String("Removing IRQ Handler.");
KernelLog.Ln
END;
Objects.RemoveHandler(SELF.HandleInterrupt, Machine.IRQ0 + irq);
END Finalize;
PROCEDURE DebugConfig;
VAR
s: SET;
res: LONGINT;
BEGIN
KernelLog.String("*** BEGIN OF NIC CONFIGURATION ***"); KernelLog.Ln;
s := SYSTEM.VAL(SET, Read16(0E0H));
KernelLog.String("C+ Command:"); KernelLog.Ln;
KernelLog.String(" "); KernelLog.Bits(s, 0, 16); KernelLog.Ln;
KernelLog.String("Rx Descriptor base address:"); KernelLog.Ln;
KernelLog.String(" ");
res := Read32(0E4H + 4H);
KernelLog.Hex(res, 8);
res := Read32(0E4H);
KernelLog.Hex(res, 8);
KernelLog.Ln;
KernelLog.String("Tx Normal Priority Descriptor base address:"); KernelLog.Ln;
KernelLog.String(" ");
res := Read32(020H + 4H);
KernelLog.Hex(res, 8);
res := Read32(020H);
KernelLog.Hex(res, 8);
KernelLog.Ln;
res := Read16(0DAH);
KernelLog.String("Receive Packet Max Size:"); KernelLog.Ln;
KernelLog.String(" "); KernelLog.Int(res, 0); KernelLog.Ln;
res := Read8(0ECH);
KernelLog.String("Max Transmit Packet Size:"); KernelLog.Ln;
KernelLog.String(" "); KernelLog.Int(res * 32, 0); KernelLog.Ln;
s := SYSTEM.VAL(SET, Read32(40H));
KernelLog.String("Transmit Configuration:"); KernelLog.Ln;
KernelLog.String(" "); KernelLog.Bits(s, 0, 32); KernelLog.Ln;
s := SYSTEM.VAL(SET, Read32(44H));
KernelLog.String("Receive Configuration:"); KernelLog.Ln;
KernelLog.String(" "); KernelLog.Bits(s, 0, 32); KernelLog.Ln;
s := SYSTEM.VAL(SET, Read16(3CH));
KernelLog.String("interrupt mask:"); KernelLog.Ln;
KernelLog.String(" "); KernelLog.Bits(s, 0, 16); KernelLog.Ln;
s := SYSTEM.VAL(SET, Read8(37H));
KernelLog.String("command bits:"); KernelLog.Ln;
KernelLog.String(" "); KernelLog.Bits(s, 0, 8); KernelLog.Ln;
KernelLog.String("*** END OF NIC CONFIGURATION ***"); KernelLog.Ln;
END DebugConfig;
PROCEDURE PrintStatus;
VAR
phyStatus: SET;
BEGIN
phyStatus := SYSTEM.VAL(SET, Read8(6CH));
IF 1 IN phyStatus THEN
KernelLog.String(" Device is linked");
KernelLog.Ln;
ELSE
KernelLog.String(" Device is NOT linked");
KernelLog.Ln;
END;
IF 4 IN phyStatus THEN
KernelLog.String(" Linkspeed is 1GBps Full-Duplex");
KernelLog.Ln;
ELSE
IF 3 IN phyStatus THEN
KernelLog.String(" Linkspeed is 100MBps");
KernelLog.Ln;
ELSIF 2 IN phyStatus THEN
KernelLog.String(" Linkspeed is 10MBps");
KernelLog.Ln;
END;
IF 0 IN phyStatus THEN
KernelLog.String(" Device is in FULL-DUPLEX MODE");
KernelLog.Ln;
ELSE
KernelLog.String(" Device is in Half-Duplex Mode");
KernelLog.Ln;
END;
END;
IF 6 IN phyStatus THEN
KernelLog.String(" Transmit Flow Control enabled");
KernelLog.Ln;
END;
IF 5 IN phyStatus THEN
KernelLog.String(" Receive Flow Control enabled");
KernelLog.Ln;
END;
KernelLog.String(" nRxOverflow = ");
KernelLog.HIntHex(nRxOverflow, 16);
KernelLog.Ln;
KernelLog.String(" nTxOverflow = ");
KernelLog.HIntHex(nTxOverflow, 16);
KernelLog.Ln;
KernelLog.String(" Rx Missed Packet Counter = ");
KernelLog.Int(Read32(4CH), 0);
KernelLog.Ln;
KernelLog.String(" nRxFrames = ");
KernelLog.Int(nRxFrames, 0);
KernelLog.Ln;
KernelLog.String(" nTxFrames = ");
KernelLog.Int(nTxFrames, 0);
KernelLog.Ln;
KernelLog.String(" nRxErrorFrames = ");
KernelLog.Int(nRxErrorFrames, 0);
KernelLog.Ln;
KernelLog.String(" nTxErrorFrames = ");
KernelLog.Int(nTxErrorFrames, 0);
KernelLog.Ln;
END PrintStatus;
END Controller;
VAR
installedControllers: Controller;
PROCEDURE ScanPCI(vendor, device: LONGINT);
VAR index, bus, dev, fct, res, irq, i: LONGINT; base: SYSTEM.ADDRESS; d: LinkDevice; c: Controller; name: Plugins.Name;
BEGIN
index := 0;
WHILE (PCI.FindPCIDevice(device, vendor, index, bus, dev, fct) = PCI.Done) & (installed < 16) DO
res := PCI.ReadConfigDword(bus, dev, fct, PCI.Adr1Reg, i); ASSERT(res = PCI.Done);
base := i; ASSERT(~ODD(base));
DEC(base, base MOD 16);
Machine.MapPhysical(base, 0FFH, base);
res := PCI.ReadConfigByte(bus, dev, fct, PCI.IntlReg, irq); ASSERT(res = PCI.Done);
NEW(d, Network.TypeEthernet, MaxETHFrameSize - 14, 6);
name := Name;
i := 0; WHILE name[i] # 0X DO INC(i) END;
IF installed > 9 THEN
name[i] := CHR(ORD("A") + installed - 10);
ELSE
name[i] := CHR(ORD("0") + installed);
END;
name[i+1] := 0X;
IF DebugFind IN Debug THEN
KernelLog.String("Found device: ");
KernelLog.String(name);
KernelLog.String("; IRQ = ");
KernelLog.Int(irq, 0);
KernelLog.Ln;
END;
d.SetName(name);
d.desc := Description;
NEW(c, d, base, irq);
IF DebugStatus IN Debug THEN
c.PrintStatus;
END;
INC(index)
END
END ScanPCI;
PROCEDURE Install*;
BEGIN {EXCLUSIVE}
IF DebugFind IN Debug THEN
KernelLog.String("Searching devices...");
KernelLog.Ln
END;
IF installed = 0 THEN
ScanPCI(10ECH, 8169H);
END;
IF DebugFind IN Debug THEN
KernelLog.String("Find finished.");
KernelLog.Ln
END;
END Install;
PROCEDURE DebugStati*;
VAR c: Controller;
BEGIN
c := installedControllers;
WHILE c # NIL DO
c.PrintStatus;
c := c.next;
END;
END DebugStati;
PROCEDURE TestDevices*;
VAR c: Controller;
BEGIN
c := installedControllers;
WHILE c # NIL DO
TestDevice(c);
c := c.next;
END;
END TestDevices;
PROCEDURE TestDevice(ctrl: Controller);
VAR
i, diff, bytes, times: LONGINT;
milliTimer : Kernel.MilliTimer;
data: ARRAY 1024 OF CHAR;
bw: REAL;
dst: Network.LinkAdr;
BEGIN
dst[0] := 000X;
dst[1] := 030X;
dst[2] := 04FX;
dst[3] := 025X;
dst[4] := 0BBX;
dst[5] := 0DBX;
IF ctrl # NIL THEN
ctrl.nRxFrames := 0;
ctrl.nTxFrames := 0;
FOR i := 0 TO LEN(data)-1 DO
data[i] := CHR(i MOD 100H)
END;
Kernel.SetTimer(milliTimer, 0);
times := 1024 * 1024 * 2;
FOR i := 1 TO times DO
ctrl.SendFrame(dst, 0DEADH, data, data, data, 0, 0, 0, LEN(data));
IF i MOD 1024 = 0 THEN
Delay(1);
END;
END;
diff := Kernel.Elapsed(milliTimer);
times := ctrl.nRxFrames * 2;
bytes := (LEN(data));
KernelLog.String("stats:"); KernelLog.Ln;
KernelLog.String("frame size = ");
KernelLog.Int(bytes, 0);
KernelLog.String("; num frames = ");
KernelLog.Int(times, 0);
KernelLog.String("; time = ");
KernelLog.Int(diff, 0); KernelLog.String("ms");
KernelLog.String("; bandwidth = ");
bw := bytes * 1.0 * times / (diff / 1000.0);
KernelLog.Int(ENTIER(bw / 1024), 0); KernelLog.String("KB/s, ");
KernelLog.Int(ENTIER(bw * 8 / 1000 / 1000), 0); KernelLog.String("Mbps"); KernelLog.Ln;
END
END TestDevice;
PROCEDURE Cleanup;
BEGIN
WHILE installedControllers # NIL DO
IF DebugCleanup IN Debug THEN
KernelLog.Ln;
KernelLog.String("Removing ");
KernelLog.String(installedControllers.dev.name);
KernelLog.Ln;
installedControllers.PrintStatus;
END;
installedControllers.Finalize;
installedControllers := installedControllers.next;
IF DebugCleanup IN Debug THEN
KernelLog.String("Success!");
KernelLog.Ln;
END;
END;
installedControllers := NIL;
END Cleanup;
PROCEDURE Delay(ms: LONGINT);
VAR
t: Kernel.MilliTimer;
BEGIN
Kernel.SetTimer(t, ms);
REPEAT UNTIL Kernel.Expired(t);
END Delay;
BEGIN
Modules.InstallTermHandler(Cleanup);
END RTL8169.
(*
MAC address 00-30-4F-25-BB-DB
MAC address 00-08-A1-3C-06-CB
local IP 129.132.134.209
SystemTools.Free RTL8169 ~
RTL8169.Install ~
WMPerfMon.Open ~
RTL8169.DebugStati ~
IP.IPConfig ~
RTL8169.TestDevices~
OFSTools.Mount RAM RamFS 300000 4096 ~
TestNet.SetDevice "RTL8169#0" ~
TestNet.ShowDevices ~
TestNet.SendBroadcast ~
TestNet.SendBroadcastVar 1499 ~
TestNet.SendTest ^ 1 10 100 1000 ~
*)