-- Copyright (C) 1984 by Xerox Corporation. All rights reserved.
-- EthernetOneHeadDicentra.mesa, HGM, 2-Dec-84 3:37:03
DIRECTORY
DicentraInputOutput USING [
GetExternalStatus, Input, InterruptLevel,
Output, ReadHole, SetNxmExpected, SetWakeupBits, SetWakeupMask, WriteHole],
Environment USING [Byte],
EthernetOneFace USING [GlobalStatePtr, Status],
HeadStartChain USING [Start],
Inline USING [BITAND, BITXOR],
MultibusAddresses USING [firstSunBoard],
Runtime USING [CallDebugger, IsBound],
Process USING [Detach, DisableTimeout, SetPriority],
ProcessPriorities USING [priorityPageFaultIO], -- Higher than Driver
SpecialRuntime USING [AllocateNakedCondition];
EthernetOneHeadDicentra: MONITOR
IMPORTS
RemainingHeads: HeadStartChain,
DicentraInputOutput, Inline, Process, Runtime, SpecialRuntime
EXPORTS HeadStartChain, EthernetOneFace =
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,
status: EthernetOneFace.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 ← TRUE;
CSB: TYPE = RECORD [
next: DeviceHandle,
sunIn: LONG POINTER TO SunIn,
sunOut: LONG POINTER TO SunOut,
firstIn, lastIn: ControlBlock,
firstOut, lastOut: ControlBlock,
missed: CARDINAL,
inInterrupt, outInterrupt: WORD];
-- SUN 3mb board layout
offsetOfNextBoard: CARDINAL = 128; -- 256 bytes
SunIn: TYPE = RECORD [
data: WORD,
unused: WORD,
ready: WORD,
switches: WORD ]; -- Reading switches resets Transmitter interrupt
SunOut: TYPE = RECORD [
data: WORD,
unused: WORD,
control: WORD,
addressFilter: WORD ];
maxNumberOfDevices: CARDINAL = 6;
first: DeviceHandle ← nullDeviceHandle;
csbs: ARRAY [0..maxNumberOfDevices) OF CSB;
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, 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;
DicentraInputOutput.Output[cb.length, @device.sunOut.data];
DicentraInputOutput.WriteHole[from: cb.buffer, to: @device.sunOut.data, words: cb.length];
END;
CheckSendStatus: INTERNAL PROCEDURE [device: DeviceHandle]
RETURNS [bogus: BOOLEAN ← FALSE] =
BEGIN
ready: WORD = DicentraInputOutput.Input[@device.sunIn.ready];
cb: ControlBlock = device.firstOut;
[] ← DicentraInputOutput.Input[@device.sunIn.switches]; -- Clears Out Int
IF cb = NIL THEN RETURN[TRUE];
cb.rawBits ← ready;
IF Inline.BITAND[ready, 100000B] = 0 THEN RETURN[TRUE]; -- Interrupt vanished!
IF Inline.BITAND[ready, 40000B] # 0 THEN
cb.status ← tooManyCollisions -- Timeout
ELSE cb.status ← ok;
DicentraInputOutput.SetWakeupBits[device.outInterrupt];
device.firstOut ← device.firstOut.next;
IF device.firstOut # NIL THEN StartOutput[device];
END;
QueueInput: PUBLIC ENTRY PROCEDURE [
device: DeviceHandle, buffer: LONG POINTER, length: CARDINAL, cb: ControlBlock] =
BEGIN
cb↑ ← [NIL, buffer, length, 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 ← FALSE] =
BEGIN
first, status: WORD;
cb: ControlBlock = device.firstIn;
words, slosh: CARDINAL;
first ← status ← DicentraInputOutput.Input[@device.sunIn.data];
IF Inline.BITAND[status, 100000B] # 0 THEN
status ← DicentraInputOutput.Input[@device.sunIn.data]; -- Look ahead krock
IF Inline.BITAND[status, 100000B] # 0 THEN RETURN[TRUE];
words ← Inline.BITAND[status, 7777B];
IF cb = NIL THEN
BEGIN
device.missed ← device.missed + 1;
FOR i: CARDINAL IN [0..words) DO
[] ← DicentraInputOutput.Input[@device.sunIn.data]; -- discard packet
ENDLOOP;
RETURN;
END;
SELECT TRUE FROM
Inline.BITAND[status, 040000B] # 0 => cb.status ← overrun;
Inline.BITAND[status, 030000B] = 030000B => cb.status ← crcAndBadAlignment;
Inline.BITAND[status, 010000B] # 0 => cb.status ← crc;
Inline.BITAND[status, 020000B] # 0 => cb.status ← badAlignmentButOkCrc;
ENDCASE => cb.status ← ok;
cb.rawBits ← status;
device.firstIn ← device.firstIn.next;
DicentraInputOutput.SetWakeupBits[device.inInterrupt];
slosh ← MIN[words, cb.length];
DicentraInputOutput.ReadHole[to: cb.buffer, from: @device.sunIn.data, words: slosh];
IF cb.status = ok AND slosh < 3 THEN cb.status ← badAlignmentButOkCrc;
IF slosh # words THEN
BEGIN
cb.status ← packetTooLong;
FOR i: CARDINAL IN [slosh..words) DO
[] ← DicentraInputOutput.Input[@device.sunIn.data];
ENDLOOP;
END;
cb.used ← slosh-1; -- CRC is included
END;
GetStatus: PUBLIC ENTRY PROCEDURE [cb: ControlBlock] RETURNS [EthernetOneFace.Status] =
BEGIN
IF interruptBits = 0 THEN [] ← CheckInterrupts[cb.device];
RETURN[cb.status];
END;
GetRetries: PUBLIC PROCEDURE [cb: ControlBlock] RETURNS [CARDINAL] =
BEGIN RETURN[0]; 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[ethernetOne] DO
hit: BOOLEAN ← FALSE;
FOR device: DeviceHandle ← first, device.next UNTIL device = nullDeviceHandle 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;
maxBogus ← MAX[maxBogus, bogus];
IF bogus > 100 THEN Runtime.CallDebugger["Dangling 3mb Interrupt"L];
ENDLOOP;
ENDLOOP;
END;
CheckInterrupts: INTERNAL PROCEDURE [device: DeviceHandle]
RETURNS [hit: BOOLEAN ← FALSE, recvBogus, tranBogus: CARDINAL ← 0] =
BEGIN
DO
ready: WORD = DicentraInputOutput.Input[@device.sunIn.ready];
SELECT TRUE FROM
Inline.BITAND[ready, 010000B] = 0 => EXIT;
Inline.BITAND[ready, 020000B] # 0 =>
BEGIN
IF CheckRecvStatus[device] THEN recvBogus ← recvBogus + 1 ELSE hit ← TRUE;
END;
Inline.BITAND[ready, 100000B] # 0 =>
BEGIN
IF CheckSendStatus[device] THEN tranBogus ← tranBogus + 1 ELSE hit ← TRUE;
END;
ENDCASE =>
BEGIN
again: WORD = DicentraInputOutput.Input[@device.sunIn.ready];
ERROR;
END;
ENDLOOP;
END;
TurnOn: PUBLIC ENTRY PROCEDURE [
device: DeviceHandle, host: Environment.Byte,
inInterrupt: WORD, outInterrupt: WORD,
globalState: EthernetOneFace.GlobalStatePtr] =
BEGIN
intLev: WORD = Inline.BITXOR[DicentraInputOutput.InterruptLevel[ethernetOne], 7B]*256;
device.firstIn ← device.lastIn ← device.firstOut ← device.lastOut ← NIL;
device.inInterrupt ← inInterrupt;
device.outInterrupt ← outInterrupt;
DicentraInputOutput.Output[134000B, @device.sunOut.control]; -- Init, no Int, reject
FOR i: CARDINAL IN [0..256) DO
DicentraInputOutput.Output[i, @device.sunOut.addressFilter];
ENDLOOP;
DicentraInputOutput.Output[114000B, @device.sunOut.control]; -- Init, no Int, accept
DicentraInputOutput.Output[0, @device.sunOut.addressFilter]; -- broadcast
DicentraInputOutput.Output[host, @device.sunOut.addressFilter]; -- me
DicentraInputOutput.SetWakeupMask[interruptBits, ethernetOne];
DicentraInputOutput.Output[0B+intLev, @device.sunOut.control];
FOR i: CARDINAL IN [0..10000) DO
[] ← DicentraInputOutput.Input[@device.sunIn.data];
ENDLOOP;
[] ← DicentraInputOutput.Input[@device.sunIn.switches]; -- Turn off Tran int
END;
TurnOff: PUBLIC ENTRY PROCEDURE [device: DeviceHandle] =
BEGIN
DicentraInputOutput.Output[114000B, @device.sunOut.control]; -- Init, no Int
DicentraInputOutput.SetWakeupMask[interruptBits, ethernetOne];
device.firstIn ← device.lastIn ← device.firstOut ← device.lastOut ← NIL;
device.inInterrupt ← device.outInterrupt ← 0;
END;
GetEthernet1Address: PUBLIC PROCEDURE [device: DeviceHandle]
RETURNS [net, host: [0..377B]] =
BEGIN
net ← 0;
host ← DicentraInputOutput.Input[@device.sunIn.switches];
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.firstSunBoard + i*offsetOfNextBoard;
switches: WORD;
device↑ ← [
next: nullDeviceHandle,
sunIn: thisBoard,
sunOut: thisBoard,
firstIn: NIL, lastIn: NIL,
firstOut: NIL, lastOut: NIL,
missed: 0,
inInterrupt: 0, outInterrupt: 0 ];
switches ← DicentraInputOutput.Input[@device.sunIn.switches];
IF switches = 0 THEN EXIT;
IF first = nullDeviceHandle THEN first ← device;
IF last # nullDeviceHandle THEN last.next ← device;
last ← device;
DicentraInputOutput.Output[114000B, @device.sunOut.control]; -- Init, no Int
ENDLOOP;
DicentraInputOutput.SetNxmExpected[FALSE];
IF Runtime.IsBound[LOOPHOLE[Process.Detach]] THEN
Process.Detach[FORK DeMultiplexInterrupts[]];
RemainingHeads.Start[];
END;
END....