-- Copyright (C) 1983, 1984 by Xerox Corporation. All rights reserved.
-- EthernetHeadDicentra.mesa, HGM, 20-Nov-84 14:47:59
DIRECTORY
DicentraInputOutput USING [
GetExternalStatus, Input, Output, ReadBlock,
SetNxmExpected, SetWakeupBits, SetWakeupMask, WriteBlock],
EthernetFace USING [GlobalStatePtr, Status],
HeadStartChain USING [Start],
Inline USING [BITAND, BITNOT, BITOR, BITXOR, LowHalf],
MultibusAddresses USING [first3ComBoard],
Runtime USING [CallDebugger, IsBound],
Process USING [Detach, DisableTimeout, SetPriority],
ProcessPriorities USING [priorityPageFaultIO], -- Higher than Driver
SpecialRuntime USING [AllocateNakedCondition],
SpecialSystem USING [ProcessorID],
System USING [GetClockPulses];
EthernetHeadDicentra: MONITOR
IMPORTS
RemainingHeads: HeadStartChain,
DicentraInputOutput, Inline, Process, Runtime, SpecialRuntime, System
EXPORTS HeadStartChain, EthernetFace =
BEGIN
-- TYPEs
ControlBlock: TYPE = LONG POINTER TO ControlBlockRecord;
-- EXPORTed TYPEs
DeviceHandle: PUBLIC TYPE = POINTER TO CSB;
ControlBlockRecord: PUBLIC TYPE = RECORD [
next: ControlBlock,
buffer: LONG POINTER,
length: CARDINAL,
used: CARDINAL,
mask: WORD,
status: EthernetFace.Status,
rawBits: WORD,
device: DeviceHandle,
direction: {in, out} ];
-- EXPORTed variables
nullDeviceHandle: PUBLIC DeviceHandle ← NIL;
globalStateSize: PUBLIC CARDINAL ← 0;
controlBlockSize: PUBLIC CARDINAL ← SIZE[ControlBlockRecord];
hearSelf: PUBLIC BOOLEAN ← FALSE;
-- My CSB (and such)
maxNumberOfDevices: CARDINAL = 6;
first: DeviceHandle ← nullDeviceHandle;
csbs: ARRAY [0..maxNumberOfDevices) OF CSB;
CSB: TYPE = RECORD [
next: DeviceHandle,
header: LONG POINTER TO Header,
transmitBuffer: LONG POINTER TO ARRAY [0..1024) OF WORD,
recvBufferA: LONG POINTER TO ARRAY [0..1024) OF WORD,
recvBufferB: LONG POINTER TO ARRAY [0..1024) OF WORD,
control: WORD,
firstIn, lastIn: ControlBlock,
firstOut, lastOut: ControlBlock,
missed: CARDINAL,
inInterrupt, outInterrupt: WORD ];
offsetOfNextBoard: CARDINAL = 8000H; -- 64K Bytes. Shadow behind first board
-- 3Com board layout: BEWARE of ADR0
-- Yetch, a simple record "exceeds addressing limits".
Header: TYPE = RECORD [
mecsr: WORD,
meback: CARDINAL,
trash1: ARRAY [0..512-(SIZE[WORD]+SIZE[CARDINAL])) OF WORD,
stationAddressROM: SpecialSystem.ProcessorID,
trash2: ARRAY [0..256-SIZE[SpecialSystem.ProcessorID]) OF WORD,
stationAddressRAM: SpecialSystem.ProcessorID ];
packetFilter: WORD = 6B; -- Me and Broadcast
inInterruptsAndPacketFilter: WORD = 306B; -- Buffer A and Buffer B
outInterrupts: WORD = 60B; -- Output done and Jam
GetNextDevice: PUBLIC PROCEDURE [in: DeviceHandle] RETURNS [out: DeviceHandle] =
BEGIN
IF in = nullDeviceHandle THEN out ← first
ELSE out ← in.next;
END;
QueueOutput: PUBLIC ENTRY PROCEDURE [
device: DeviceHandle, buffer: LONG POINTER, length: CARDINAL, cb: ControlBlock] =
BEGIN
cb↑ ← [NIL, buffer, length, 0, 1, pending, 0, device, out];
IF device.firstOut = NIL THEN BEGIN device.firstOut ← cb; StartOutput[device]; END
ELSE device.lastOut.next ← cb;
device.lastOut ← cb;
END;
StartOutput: INTERNAL PROCEDURE [device: DeviceHandle] =
BEGIN
cb: ControlBlock = device.firstOut;
wordOffset: CARDINAL = 1024 - cb.length;
DicentraInputOutput.WriteBlock[from: cb.buffer, to: device.transmitBuffer + wordOffset, words: cb.length];
device.control ← Inline.BITOR[device.control, outInterrupts];
DicentraInputOutput.Output[-20, @device.header.meback]; -- Initial 1ms delay
DicentraInputOutput.Output[wordOffset*2, device.transmitBuffer];
DicentraInputOutput.Output[30000B+device.control, @device.header.mecsr]; -- Set TBSW, Clear Jam
END;
CheckSendStatus: INTERNAL PROCEDURE [device: DeviceHandle] RETURNS [bogus: BOOLEAN ← FALSE] =
BEGIN
status: WORD = DicentraInputOutput.Input[@device.header.mecsr];
cb: ControlBlock = device.firstOut;
IF cb = NIL THEN
BEGIN
device.control ← Inline.BITAND[device.control, Inline.BITNOT[outInterrupts]];
DicentraInputOutput.Output[device.control, @device.header.mecsr]; -- Clear Out Int
RETURN[TRUE];
END;
IF Inline.BITAND[status, 10000B] # 0 THEN
BEGIN -- Collision
random: WORD ← Inline.LowHalf[System.GetClockPulses[]];
random ← Inline.BITAND[random, 1777B];
random ← Inline.BITAND[random, cb.mask];
IF cb.mask = 177777B THEN
BEGIN
-- Yetch. I think we have to reset the board. Yetch.
cb.status ← tooManyCollisions;
cb.rawBits ← status;
DicentraInputOutput.SetWakeupBits[device.outInterrupt];
device.firstOut ← device.firstOut.next;
IF device.firstOut # NIL THEN StartOutput[device]
ELSE
BEGIN
device.control ← Inline.BITAND[device.control, Inline.BITNOT[outInterrupts]];
DicentraInputOutput.Output[device.control, @device.header.mecsr]; -- Clear Out Int
END;
RETURN;
END;
-- Note: Documentation bug: It doesn't mention the extra 1.
DicentraInputOutput.Output[-(random+1), @device.header.meback];
DicentraInputOutput.Output[30000B+device.control, @device.header.mecsr]; -- Set TBSW, Clear Jam
cb.mask ← cb.mask * 2 + 1;
RETURN;
END;
IF Inline.BITAND[status, 20000B] # 0 THEN RETURN[TRUE];
cb.status ← ok;
cb.rawBits ← status;
DicentraInputOutput.SetWakeupBits[device.outInterrupt];
device.firstOut ← device.firstOut.next;
IF device.firstOut # NIL THEN StartOutput[device]
ELSE
BEGIN
device.control ← Inline.BITAND[device.control, Inline.BITNOT[outInterrupts]];
DicentraInputOutput.Output[device.control, @device.header.mecsr]; -- Clear Out Int
END;
END;
QueueInput: PUBLIC ENTRY PROCEDURE [
device: DeviceHandle, buffer: LONG POINTER, length: CARDINAL, cb: ControlBlock] =
BEGIN
cb↑ ← [NIL, buffer, length, 0, 0, pending, 0, device, in];
IF device.firstIn = NIL THEN device.firstIn ← cb
ELSE device.lastIn.next ← cb;
device.lastIn ← cb;
END;
CheckRecvStatus: INTERNAL PROCEDURE [device: DeviceHandle] RETURNS [bogus: BOOLEAN ← TRUE] =
BEGIN
DO
status: WORD = Inline.BITAND[DicentraInputOutput.Input[@device.header.mecsr], 142000B];
SELECT status FROM
140000B, 142000B => RETURN;
0 =>
BEGIN
GrabBuffer[device, device.recvBufferA, 40000B];
GrabBuffer[device, device.recvBufferB, 100000B];
END;
2000B =>
BEGIN
GrabBuffer[device, device.recvBufferB, 100000B];
GrabBuffer[device, device.recvBufferA, 40000B];
END;
100000B, 102000B => GrabBuffer[device, device.recvBufferA, 40000B];
40000B, 42000B => GrabBuffer[device, device.recvBufferB, 100000B];
ENDCASE => ERROR;
bogus ← FALSE
ENDLOOP;
END;
GrabBuffer: INTERNAL PROCEDURE [device: DeviceHandle, recv: LONG POINTER, mask: WORD] =
BEGIN
cb: ControlBlock = device.firstIn;
rawBits: WORD ← DicentraInputOutput.Input[recv];
bytes, words: CARDINAL;
bytes ← Inline.BITAND[rawBits, 3777B] - 2;
IF cb = NIL THEN
BEGIN
device.missed ← device.missed + 1;
DicentraInputOutput.Output[mask+device.control, @device.header.mecsr]; -- Start next read
RETURN;
END;
words ← bytes/2;
SELECT TRUE FROM
Inline.BITAND[rawBits, 020000B] # 0 => cb.status ← packetTooLong;
Inline.BITAND[rawBits, 104000B] # 0 =>
BEGIN
error: WORD = Inline.BITAND[rawBits, 124000B];
SELECT error FROM
100000B => cb.status ← crc;
104000B => cb.status ← crcAndBadAlignment;
004000B => cb.status ← badAlignmentButOkCrc;
ENDCASE => cb.status ← packetTooLong;
END;
bytes MOD 2 # 0 => cb.status ← badAlignmentButOkCrc;
ENDCASE => cb.status ← ok;
IF words > cb.length THEN
BEGIN
cb.status ← packetTooLong;
words ← cb.length;
END;
DicentraInputOutput.ReadBlock[to: cb.buffer, from: recv + 1, words: words];
cb.used ← words;
cb.mask ← bytes;
cb.rawBits ← rawBits;
DicentraInputOutput.SetWakeupBits[device.inInterrupt];
DicentraInputOutput.Output[mask+device.control, @device.header.mecsr]; -- Start next read
device.firstIn ← device.firstIn.next;
END;
GetStatus: PUBLIC ENTRY PROCEDURE [cb: ControlBlock] RETURNS [EthernetFace.Status] =
BEGIN
IF interruptBits = 0 THEN [] ← CheckInterrupts[cb.device];
RETURN[cb.status];
END;
GetRetries: PUBLIC PROCEDURE [cb: ControlBlock] RETURNS [CARDINAL] =
BEGIN
IF cb.direction # out THEN ERROR;
IF cb.status = pending THEN ERROR;
RETURN[
SELECT cb.mask FROM
1 => 0,
3 => 1,
7 => 2,
17B => 3,
37B => 4,
77B => 5,
177B => 6,
377B => 7,
777B => 8,
1777B => 9,
3777B => 10,
7777B => 11,
17777B => 12,
37777B => 13,
77777B => 14,
177777B => 15,
ENDCASE => 16];
END;
GetPacketLength: PUBLIC PROCEDURE [cb: ControlBlock] RETURNS [CARDINAL] =
BEGIN
IF cb.direction # in THEN ERROR;
IF cb.status = pending THEN ERROR;
RETURN[cb.used];
END;
GetPacketsMissed: PUBLIC PROCEDURE [device: DeviceHandle] RETURNS [CARDINAL] =
BEGIN RETURN[device.missed]; END;
wait: LONG POINTER TO CONDITION ← NIL;
interruptBits: WORD ← 0;
maxBogus, totalRecvBogus, totalTranBogus: CARDINAL ← 0;
DeMultiplexInterrupts: ENTRY PROCEDURE =
BEGIN
[cv: wait, mask: interruptBits] ← SpecialRuntime.AllocateNakedCondition[];
Process.DisableTimeout[wait];
Process.SetPriority[ProcessPriorities.priorityPageFaultIO];
DO
bogus: CARDINAL ← 0;
WAIT wait;
WHILE DicentraInputOutput.GetExternalStatus[].ints[ethernet] DO
hit: BOOLEAN ← FALSE;
FOR device: DeviceHandle ← first, device.next UNTIL device = NIL DO
thisHit: BOOLEAN;
thisRecvBogus, thisTranBogus: CARDINAL;
[thisHit, thisRecvBogus, thisTranBogus] ← CheckInterrupts[device];
hit ← hit OR thisHit;
bogus ← bogus + thisRecvBogus + thisTranBogus;
totalRecvBogus ← totalRecvBogus + thisRecvBogus;
totalTranBogus ← totalTranBogus + thisTranBogus;
ENDLOOP;
IF ~hit THEN bogus ← bogus + 1;
maxBogus ← MAX[maxBogus, bogus];
IF bogus > 100 THEN Runtime.CallDebugger["Dangling 10mb Interrupt"L];
ENDLOOP;
ENDLOOP;
END;
CheckInterrupts: INTERNAL PROCEDURE [device: DeviceHandle]
RETURNS [hit: BOOLEAN ← FALSE, recvBogus, tranBogus: CARDINAL ← 0] =
BEGIN
DO
status: WORD ← DicentraInputOutput.Input[@device.header.mecsr];
status ← Inline.BITXOR[status, 160000B];
SELECT TRUE FROM
Inline.BITAND[status, 140000B] # 0 =>
BEGIN
IF CheckRecvStatus[device] THEN recvBogus ← recvBogus + 1 ELSE hit ← TRUE;
END;
Inline.BITAND[status, 30000B] # 0 AND Inline.BITAND[status, outInterrupts] # 0 =>
BEGIN
IF CheckSendStatus[device] THEN tranBogus ← tranBogus + 1 ELSE hit ← TRUE;
END;
ENDCASE => RETURN;
ENDLOOP;
END;
TurnOn: PUBLIC ENTRY PROCEDURE [
device: DeviceHandle, host: SpecialSystem.ProcessorID,
inInterrupt: WORD, outInterrupt: WORD,
globalState: EthernetFace.GlobalStatePtr] =
BEGIN
source: POINTER = @host;
dest: LONG POINTER = @device.header.stationAddressRAM;
DicentraInputOutput.Output[400B+0, @device.header.mecsr]; -- Reset
device.firstIn ← device.lastIn ← device.firstOut ← device.lastOut ← NIL;
device.inInterrupt ← inInterrupt;
device.outInterrupt ← outInterrupt;
FOR i: CARDINAL IN [0..SIZE[SpecialSystem.ProcessorID]) DO
DicentraInputOutput.Output[(source+i)↑, dest+i];
ENDLOOP;
device.control ← inInterruptsAndPacketFilter;
DicentraInputOutput.SetWakeupMask[interruptBits, ethernet];
DicentraInputOutput.Output[144000B+device.control, @device.header.mecsr]; -- Set BBSW, ABSW, AMSW
END;
TurnOff: PUBLIC ENTRY PROCEDURE [device: DeviceHandle] =
BEGIN
device.control ← packetFilter;
DicentraInputOutput.Output[400B+device.control, @device.header.mecsr]; -- Reset
DicentraInputOutput.SetWakeupMask[interruptBits, ethernet];
device.firstIn ← device.lastIn ← device.firstOut ← device.lastOut ← NIL;
END;
-- None needed: it's not reading directly into our memory
AddCleanup, RemoveCleanup: PUBLIC PROCEDURE [DeviceHandle] = BEGIN END;
Start: PUBLIC PROCEDURE =
BEGIN
last: DeviceHandle ← nullDeviceHandle;
DicentraInputOutput.SetNxmExpected[TRUE];
FOR i: CARDINAL IN [0..maxNumberOfDevices) DO
device: DeviceHandle = @csbs[i];
thisBoard: LONG POINTER = MultibusAddresses.first3ComBoard + i*LONG[offsetOfNextBoard];
hostNumber1, hostNumber2: WORD;
device↑ ← [
next: nullDeviceHandle,
header: thisBoard,
transmitBuffer: thisBoard + 1*1024,
recvBufferA: thisBoard + 2*1024,
recvBufferB: thisBoard + 3*1024,
control: packetFilter,
firstIn: NIL, lastIn: NIL,
firstOut: NIL, lastOut: NIL,
missed: 0,
inInterrupt: 0, outInterrupt: 0 ];
DicentraInputOutput.Output[400B+device.control, @device.header.mecsr]; -- Reset
hostNumber1 ← DicentraInputOutput.Input[@device.header.stationAddressROM + 0];
hostNumber2 ← DicentraInputOutput.Input[@device.header.stationAddressROM + 1];
hostNumber2 ← Inline.BITAND[hostNumber2, 0FF00H];
IF hostNumber1 # 0260H OR hostNumber2 # 8C00H THEN EXIT;
IF first = nullDeviceHandle THEN first ← device;
IF last # nullDeviceHandle THEN last.next ← device;
last ← device;
DicentraInputOutput.Output[400B+device.control, @device.header.mecsr]; -- Reset
DicentraInputOutput.Output[144000B+device.control, @device.header.mecsr]; -- Set BBSW, ABSW, AMSW
ENDLOOP;
DicentraInputOutput.SetNxmExpected[FALSE];
IF Runtime.IsBound[LOOPHOLE[Process.Detach]] THEN
Process.Detach[FORK DeMultiplexInterrupts[]];
RemainingHeads.Start[];
END;
END....