-- File: AltoSlaDriver.mesa, Last Edit:
-- MAS April 17, 1980 10:50 PM
-- HGM February 23, 1980 4:45 PM

-- Copyright Xerox Corporation 1979, 1980

DIRECTORY
InlineDefs: FROM "InlineDefs" USING [BITAND, BITXOR, BITSHIFT, HighHalf],
AltoRamDefs: FROM "AltoRamDefs" USING [
ChangeControlReg, EnableEia, SetLineTab],
AltoSlaDefs: FROM "AltoSlaDefs",
StatsDefs: FROM "StatsDefs" USING [StatIncr, StatBump, StatCounterIndex],
DriverDefs: FROM "DriverDefs" USING [
giantVector, doDebug, doCheck, doStats, Glitch,
SlaStats, MaybeGetFreeBuffer,
GetInputBuffer, NetworkObject, AddDeviceToChain,
PutOnGlobalDoneQueue, PutOnGlobalInputQueue],
CommUtilDefs: FROM "CommUtilDefs" USING [
GetTicks, msPerTick,
AllocateSegment,
SetTimeout, DisableTimeout, MsecToTicks,
InterruptLevel, AssignInterruptLevel, SetPriority, AddInterruptHandler,
CleanupItem, CleanupReason, AllReasons, AddCleanupProcedure],
PupDefs: FROM "PupDefs",
BufferDefs: FROM "BufferDefs",
PupTypes: FROM "PupTypes" USING [
allHosts, PupErrorCode,
cantGetTherePupErrorCode, gatewayResourceLimitsPupErrorCode,
noErrorPupErrorCode],
DriverTypes: FROM "DriverTypes" USING [
slaEncapsulationOffset, slaEncapsulationBytes, bufferSeal,
routingSlaPacket, pupSlaPacket];

AltoSlaDriver: MONITOR
IMPORTS
InlineDefs, StatsDefs, DriverDefs, CommUtilDefs, BufferDefs, AltoRamDefs, AltoSlaDefs
EXPORTS DriverDefs, AltoSlaDefs
SHARES BufferDefs, DriverDefs, DriverTypes =
BEGIN OPEN StatsDefs, DriverDefs, AltoSlaDefs, BufferDefs;

-- Dummies required to get around Compiler problem.
D0: SIGNAL = CODE;
D1: SIGNAL = CODE;
D2: SIGNAL = CODE;
D3: SIGNAL = CODE;
D4: SIGNAL = CODE;
D5: SIGNAL = CODE;
D6: SIGNAL = CODE;
D7: SIGNAL = CODE;
D10: SIGNAL = CODE;
D11: SIGNAL = CODE;
D12: SIGNAL = CODE;
D13: SIGNAL = CODE;
D14: SIGNAL = CODE;
D15: SIGNAL = CODE;
D16: SIGNAL = CODE;
D17: SIGNAL = CODE;
D20: SIGNAL = CODE;
D21: SIGNAL = CODE;
D22: SIGNAL = CODE;
D23: SIGNAL = CODE;
D24: SIGNAL = CODE;
D25: SIGNAL = CODE;
D26: SIGNAL = CODE;
D27: SIGNAL = CODE;
D30: SIGNAL = CODE;
D31: SIGNAL = CODE;
D32: SIGNAL = CODE;
D33: SIGNAL = CODE;
D34: SIGNAL = CODE;
D35: SIGNAL = CODE;
D36: SIGNAL = CODE;
D37: SIGNAL = CODE;

maxPacketsPerLine: CARDINAL = 10;

mode: {eia, commProc};

hardware: CONDITION;
cleanupItem: CommUtilDefs.CleanupItem ← [,CommUtilDefs.AllReasons,Broom];
hardProcess, watcherProcess, doitProcess: PROCESS;
ready: CONDITION;
pause: CONDITION;
inputQueue: QueueObject;
outputQueue: QueueObject;

crcTable: POINTER TO ARRAY [0..maxByte) OF WORD;
lineTable: POINTER TO ARRAY Line OF LineTableEntry;
lineInfo: POINTER TO ARRAY Line OF LineInfoBlock ← NIL;
routingTable: POINTER TO ARRAY SlaHost OF RoutingTableEntry ← NIL;

myNetwork: DriverDefs.NetworkObject ← [
decapsulateBuffer: DecapsulateBuffer,
encapsulatePup: EncapsulatePup,
encapsulateRpp: EncapsulateRpp,
sendBuffer: SendBuffer,
forwardBuffer: ForwardBuffer,
activateDriver: ActivateDriver,
deactivateDriver: DeactivateDriver,
deleteDriver: DeleteDriver,
interrupt: Interrupt,
device: sla,
alive: TRUE,
speed: 10,
index: ,
netNumber: ,
hostNumber: ,
next: NIL,
pupStats: DriverDefs.SlaStats,
stats: NIL ];

ImpossibleEndcase: PUBLIC ERROR = CODE;
QueueScrambled: PUBLIC ERROR = CODE;
BufferSealBroken: PUBLIC ERROR = CODE;
MachineIDTooBigForEthernet: PUBLIC ERROR = CODE;
UnexpectedCompletionCodeFromMicrocode: PUBLIC ERROR = CODE;
LineNumberTooBig: PUBLIC ERROR = CODE;
CantTurnSlaDriverOffYet: PUBLIC ERROR = CODE;
HardwareTypeMustBeSpecified: PUBLIC ERROR = CODE;
CantMakImageWhileEtherentDriverIsActive: PUBLIC ERROR = CODE;
HyperspaceNotSupported: PUBLIC ERROR = CODE;

activeLines: CARDINAL ← 0;

slaInterruptLevel: CommUtilDefs.InterruptLevel = CommUtilDefs.AssignInterruptLevel[12];
slaInterruptBit: WORD = InlineDefs.BITSHIFT[1,slaInterruptLevel];

routingInterval: CARDINAL = 5000/CommUtilDefs.msPerTick;
routingTimeout: CARDINAL = 20000/CommUtilDefs.msPerTick;

maxHiPriWords: CARDINAL = (2+50+22)/2; -- 50 data bytes in a pup

pleaseStop: BOOLEAN = FALSE; -- you can’t stop this thing yet

slaEncapsulationOffset: CARDINAL = DriverTypes.slaEncapsulationOffset;
slaEncapsulationBytes: CARDINAL = DriverTypes.slaEncapsulationBytes;

statSlaPacketsSent: PUBLIC StatCounterIndex;
statSlaWordsSent: PUBLIC StatCounterIndex;

statSlaPacketsRecv: PUBLIC StatCounterIndex;
statSlaWordsRecv: PUBLIC StatCounterIndex ;

statSlaRoutingPacketsSent: PUBLIC StatCounterIndex;
statSlaRoutingPacketsReceived: PUBLIC StatCounterIndex;

statSlaEmptyFreeQueue: PUBLIC StatCounterIndex;
statSlaROR: PUBLIC StatCounterIndex;
statSlaCRCError: PUBLIC StatCounterIndex;
statSlaSyncError: PUBLIC StatCounterIndex;
statSlaControlError: PUBLIC StatCounterIndex;

statSlaNoRoute: PUBLIC StatCounterIndex;
statSlaQueueFull: PUBLIC StatCounterIndex;
statSlaConnectionLimit: PUBLIC StatCounterIndex;

statSlaPacketsStuck: PUBLIC StatCounterIndex;
statSlaLineTimeout: PUBLIC StatCounterIndex;
statSlaLineDied: PUBLIC StatCounterIn
dex;
st
atSlaFCT: PUBLIC StatCounterIndex;

Watcher: ENTRY PROCEDURE =
BEGIN
line: Line ← 0;
routeTime: CARDINAL ← CommUtilDefs.GetTicks[];
watchit: CONDITION;
CommUtilDefs.SetTimeout[@watchit,CommUtilDefs.MsecToTicks[250]];
UNTIL pleaseStop DO
WAIT watchit;
-- should check for lost interrupts here if there is an easy way
FlushDeadLines[];
IF CommUtilDefs.GetTicks[]
-routeTime>routingInterval THEN
BEGIN
-- Send a routing table out over one line each time through to avoid
using up too many buffers at the same time.
IF line<activeLines THEN SendSlaRoutingTable[line];
IF (line←line+1)>=activeLines THEN
BEGIN
routeTime ← routeTime+routingInterval;
IF CommUtilDefs.GetTicks[]-routeTime>routingInterval THEN
-- don’t thrash if buffers are hard to get
routeTime ← CommUtilDefs.GetTicks[];
line ← 0;
END;
END;
ENDLOOP;
END;

-- 2400 baud is 300 characters/sec. Allow 10% for slop and whatever to get 270.
-- There is also the possibility for DLE doubling, so that is another factor of 2.
-- The framing is 9 bytes: SYN, SYN, SYN, DLE, STX, ..... DLE, ETX, CRC, CRC
bytesPerTick: CARDINAL = 2
70/CommUtilDefs.msPerTick; -- rounds down

FlushDeadLines: INTERNAL PROCEDURE =
BEGIN
line: Line;
lib: LIB;
b: Buffer;
stuck, timeout: BOOLEAN;
t: CARDINAL;
FOR line IN [0..activeLines) DO
lib ← @lineInfo[line];
b ← lib.sendBuffer;
stuck ← (b#NIL
AND LOOPHOLE[b.iocbChain,LongLCB].state=running
AND (t ← (CommUtilDefs.GetTicks[]-lib.timeSendStarted))>
-- b.length is words, not bytes
(overheadPerPacket+lib.sendBuffer.length*(2*2))/bytesPerTick);
timeout ← (CommUtilDefs.GetTicks[]-lib.timeOfLastRT)>routingTimeout;
IF timeout OR stuck THEN
BEGIN
IF lib.state#down THEN
BEGIN
IF doStats THEN StatIncr[statSlaLineDied];
IF doStats AND stuck THEN StatIncr[statSlaPacketsStuck];
IF doStats AND timeout THEN StatIncr[statSlaLineTimeout];
lib.deaths ← lib.deaths+1;
END;
IF stuck THEN lib.stuck ← lib.stuck+1;
IF timeout THEN lib.timeout ← lib.timeout+1;
StopLine[line];
PurgeLineFromRT[line];
lib.state ← down;
lib.partner ← noPartner;
StartL
ine[line];
END;
ENDLOOP;
END;

Interrupt: ENTRY PROCEDURE =
BEGIN
line: Line;
lib: LIB;
b, temp: Buffer;
lcb: LongLCB;
CommUtilDefs.SetPriority[3];
UNTIL pleaseStop DO
WAIT hardware;
FOR line IN [0..activeLines) DO
lib ← @lineInfo[line];
WHILE (b←lib.recvBuffer)#NIL DO
IF doDebug AND b.seal#DriverTypes.bufferSeal THEN Glitch[BufferSealBroken];
lcb ← b.iocbChain;
IF lcb.state=running THEN EXIT;
lib.recvBuffer ← b.next;
IF lcb.microcodeError#0 THEN
BEGIN -- something screwed up, recycle this buffer
SELECT lcb.microcodeError FROM
1 =>
BEGIN -- can only be ROR
lib.overrun ← lib.overrun+1;
IF doStats THEN StatIncr[statSlaROR];
END;
4 =>
BEGIN
lib.syncErrors ← lib.syncErrors+1;
IF doStats THEN StatIncr[statSlaSyncError];
END;
3, 5 =>
BEGIN
lib.controlErrors ← lib.controlErrors+1;
IF doStats THEN StatIncr[statSlaControlError];
END;
6 =>
BEGIN
lib.crcErrors ← lib.crcErrors+1;
IF doStats THEN StatIncr[statSlaCRCError];
END;
ENDCASE => Glitch[UnexpectedCompletionCodeFromMicrocode];
AppendBuffer[b,line,recv];
LOOP;
END;
IF (temp←DriverDefs.GetInputBuffer[])=NIL THEN
BEGIN -- queue empty, recycle this buffer
AppendBuffer[b,line,recv];
IF doStats THEN StatIncr[statSlaEmptyFreeQueue];
LOOP;
END;
b.network ← @myNetwork;
b.encapsulation.slaSourceLine ← line;
b.length ← (((b.length-slaEncapsulationOffset)*2)-lcb.bytesLeft)/2;
IF doStats THEN StatIncr[statSlaPacketsRecv];
IF doStats THEN StatBump[statSlaWordsRecv,b.length];
lib.packetsRecv ← lib.packetsRecv+1;
lib.bytesRecv ← lib.bytesRecv+b.length*2;
Enqueue[@inputQueue,b];
NOTIFY ready;
AppendBuffer[temp,line,recv];
ENDLOOP;
WHILE (b←lib.sendBuffer)#NIL DO
IF doDebug AND b.seal#DriverTypes.bufferSeal THEN Glitch[BufferSealBroken];
lcb←b.iocbChain;
IF lcb.state=running THEN EXIT;
-- there should be only one at a time on the send chain
lib.sendBuffer ← b.next;
SELECT lcb.microcodeError FROM
0 =>
BEGIN -- normal good case
IF doStats THEN StatIncr[statSlaPacketsSent];
IF doStats THEN StatBump[statSlaWordsSent,b.length];
IF b.encapsulation.slaHi THEN
BEGIN
lib.hiPacketsSent ← lib.hiPacketsSent+1;
lib.hiBytesSent ← lib.hiBytesSent+b.length*2;
lib.hiQueueDelay ← lib.hiQueueDelay+
(lib.timeSendStarted-b.encapsulation.slaTimeQueued);
END;
lib.packetsSent ← lib.packetsSent+1;
lib.bytesSent ← lib.bytesSent+b.length*2;
lib.queueDelay ← lib.queueDelay+
(lib.timeSendStarted-b.encapsulation.slaTimeQueued);
END;
1 =>
BEGIN -- can only be FCT/TransOverrun
lib.overrun ← lib.overrun+1;
IF doStats THEN StatIncr[statSlaFCT];
END;
ENDCASE => Glitch[UnexpectedCompletionCodeFromMicrocode];
Enqueue[@outputQueue,b];
NOTIFY ready;
IF lib.sendBuffer=NIL THEN StartSending[line]; -- see if more to go out
E
NDLOOP;
ENDLOOP;
ENDLOOP;
END;

AppendBuffer: INTERNAL PROCEDURE [b: Buffer, line: Line, flag: {send, recv}] =
BEGIN
lib: LIB ← @lineInfo[line];
lte: LTE = @lineTable[line];
lcb: LongLCB ← b.iocbChain;
first: LONG POINTER TO Buffer;
IF doDebug AND b.seal#DriverTypes.bufferSeal THEN Glitch[BufferSealBroken];
b.device ← sla; -- this gets all actual input+output
IF InlineDefs.HighHalf[b]#0 THEN Glitch[HyperspaceNotSupported];
lcb↑ ← [
next: noLCB,
interruptBit: slaInterruptBit,
state: running,
microcodeError: 0,
hardwareStatus: 0,
half: left,
bytesLeft: 2*b.length,
currentData: ShortenData[@b.encapsulation+slaEncapsulationOffset],
partialCrc: 0,
unused: ];
IF flag=recv THEN lcb.bytesLeft ← lcb.bytesLeft-2*slaEncapsulationOffset;
SELECT flag FROM
send =>
BEGIN
first ← @lib.sendBuffer;
AppendLCB[@lte.outputLCB,lcb];
END;
recv =>
BEGIN
first ← @lib.recvBuffer;
AppendLCB[@lte.inputLCB,lcb];
END;
ENDCASE => Glitch[ImpossibleEndcase];
b.next ← NIL;
IF first↑=NIL THEN first↑←b
ELSE
BEGIN
UNTIL first↑.next=NIL DO first←@first↑.next; END
LOOP;
first↑.next ← b;
END;
END;

AppendLCB: PROCEDURE [head: POINTER TO LCB, lcb: LongLCB] =
BEGIN
short, next, ptr: LCB;
short ← ShortenIocb[lcb];
IF (next←head↑)#noLCB THEN
BEGIN
DO
ptr ← next;
next ← ptr.next;
IF next=noLCB THEN EXIT;
ENDLOOP;
ptr.next ← short;
END;
IF head↑=noLCB AND lcb
.state=running THEN head↑ ← short;
END;

Doit: ENTRY PROCEDURE =
BEGIN
b: Buffer;
UNTIL pleaseStop DO
WAIT ready;
UNTIL inputQueue.length=0 DO
IF (b←Dequeue[@inputQueue])=NIL THEN Glitch[QueueScrambled];
IF doDebug AND b.seal#DriverTypes.bufferSeal THEN Glitch[BufferSealBroken];
SELECT b.encapsulation.slaType FROM
DriverTypes.routingSlaPacket =>
BEGIN
ProcessRoutingPacket[b];
ReturnFreeBuffer[b];
END;
ENDCASE =>
BEGIN
-- Maybe we should check
to be sure the line is up or loopedBack ???
DriverDefs.PutOnGlobalInputQueue[b];
END;
ENDLOOP;
UNTIL outputQueue.length=0 DO
IF (b←Dequeue[@outputQueue])=NIL THEN Glitch[QueueScrambled];
IF doDebug AND b.seal#DriverTypes.bufferSeal THEN Glitch[BufferSealBroken];
IF b.encapsulation.slaBroadcast THEN
SendOverNextLine[b,b.encapsulation.slaSourceLine+1]
ELSE DriverDefs.PutOnGlobalDoneQue
ue[b];
ENDLOOP
;
ENDLOOP;
END;

ProcessRoutingPacket: INTERNAL PROCEDURE [b: Buffer] =
BEGIN
p: POINTER TO RoutingTablePacket ← LOOPHOLE[@b.rawWords];
line: Line ← b.encapsulation.slaSourceLine;
lib: LIB ← @lineInfo[line];
lineInfo[line].timeOfLastRT ← CommUtilDefs.GetTicks[];
PurgeLineFromRT[line];
IF p.sourceHost=myNetwork.hostNumber THEN
BEGIN -- line is looped back
lib.state ← loopedBack;
routingTable[myNetwork.hostNumber] ← [hops: 1, line: line];
END
ELSE
BEGIN
i, newHop: CARDINAL;
rte: POINTER TO RoutingTableEntry;
SELECT lib.state FROM
up => IF p.rt[myNetwork.hostNumber].hops#1 THEN lib.state ← halfUp;
halfUp => IF p.rt[myNetwork.hostNumber].hops=1 THEN lib.state ← up;
ENDCASE => lib.state ← halfUp;
IF lib.state=up THEN
FOR i IN [0..MIN[p.numEntries,maxSlaHost]) DO
newHop ← p.rt[i].hops+1;
rte ← @routingTable[i];
IF newHop<=rte.hops THEN
BEGIN
rte.hops ← IF newHop<=maxHops THEN newHop ELSE longHop;
rte.line ← line;
END;
ENDLOOP;
END;
lib.partner ← p.sourceHost;
IF doStats THEN StatI
ncr[statSlaRoutingPacketsReceived];
END;

PurgeLineFromRT: PROCEDURE [line: Line] =
BEGIN
host: SlaHost;
FOR host IN SlaHost DO
IF routingTable[host].line=line THEN routingTable
[host].hops ← longHop;
ENDLOOP;
END;

SendSlaRoutingTable: INTERNAL PROCEDURE [line: Line] =
BEGIN
lib: LIB ← @lineInfo[line];
b: Buffer;
p: POINTER TO RoutingTablePacket;
partner: CARDINAL ← lineInfo[line].partner;
IF line>=activeLines THEN Glitch[LineNumberTooBig];
UNTIL (b←DriverDefs.MaybeGetFreeBuffer[])#NIL DO WAIT pause; ENDLOOP;
p ← LOOPHOLE[@b.rawWords];
p.sourceHost ← myNetwork.hostNumber;
p.numEntries ← maxSlaHost;
p.rt ← routingTable↑;
p.rt[myNetwork.hostNumber].hops ← 0;
IF partner IN SlaHost THEN p.rt[partner].hops ← 1;
b.length ← 1+SIZE[RoutingTablePacket];
b.encapsulation ← [ sla [
slaSpare1:, slaSpare2:, slaHi:,
slaBroadcast: FALSE,
slaTimeQueued: ,
slaSourceLine: line, slaDestHost: ,
slaType: DriverTypes.routingSlaPacket ] ];
b.encapsulation.slaTimeQueued ← CommUtilDefs.GetTicks[];
Enqueue[@lib.hiPriQueue,b];
IF lib.sendBuffer=NIL THEN StartSending[line];
IF doStats THEN St
atIncr[statSlaRoutingPacketsSent];
END;

DecapsulateBuffer: PROCEDURE [b: Buffer] RETURNS [BufferType] =
BEGIN
SELECT b.encapsulation.slaType FROM
DriverTypes.pupSlaPacket =>
BEGIN
IF 2*b.length<b.pupLength+slaEncapsulationBytes THEN
BEGIN
IF doStats THEN StatIncr[statPupsDiscarded];
RETURN[rejected];
END;
RETURN[pup];
EN
D;
ENDCASE
=> RETURN[rejected];
END;

EncapsulateRpp: PROCEDURE [RppBuf
fer, PupHostID] = LOOPHOLE[EncapsulatePup];
EncapsulatePup: PROCEDURE [b: PupBuffer, destination: PupHostID] =
BEGIN
broadcast: BOOLEAN = destination=PupTypes.allHosts;
IF doDebug AND b.seal#DriverTypes.bufferSeal THEN Glitch[BufferSealBroken];
b.encapsulation ← [ sla [
slaSpare1:, slaSpare2:, slaHi:,
slaBroadcast: broadcast,
slaTimeQueued:,
slaSourceLine: , slaDestHost: destination,
slaType: DriverTypes.pupSlaPacket ] ];
b.length ← (b.pu
pLength+1+slaEncapsulationBytes)/2;
END;

ForwardBuffer: ENTRY PROCEDURE [b: Buffer] RETURNS [PupTypes.PupErrorCode] =
BEGIN
host: SlaHost ← b.encapsulation.slaDestHost;
line: Line ← routingTable[host].line;
lib: LIB ← @lineInfo[line];
n: CARDINAL;
high: BOOLEAN;
b.device ← sla;
IF b.encapsulation.slaBroadcast THEN
BEGIN
SendOverNextLine[b,0];
RETURN[PupTypes.noErrorPupErrorCode];
END;
IF host>=maxSlaHost OR routingTable[host].hops=longHop OR lib.state=halfUp THEN
BEGIN -- no route
IF doStats THEN StatIncr[statSlaNoRoute];
RETURN[PupTypes.cantGetTherePupErrorCode];
END;
IF doCheck AND line>=activeLines THEN Glitch[LineNumberTooBig];
n ← lib.hiPriQueue.length+lib.lowPriQueue.length;
-- maybe we should throw away loPri packets if this is a hiPri one ?
IF n>MIN[maxPacketsPerLine,BufferDefs.BuffersLeft[]] THEN
BEGIN
IF doStats THEN StatIncr[statSlaQueueFull];
lib.rejections ← lib.rejections+1;
RETURN[PupTypes.gatewayResourceLimitsPupErrorCode];
END;
n ← CountFriends[b,@lib.lowPriQueue];
high ← b.length<maxHiPriWords AND n=0;
n ← n+CountFriends[b,@lib.hiPriQueue];
IF n>5 THEN
BEGIN
IF doStats THEN StatIncr[statSlaConnectionLimit];
lib.rejections ← lib.rejections+1;
lib.connRejections ← lib.connRejections+1;
IF high THEN lib.hi
Rejections ← lib.hiRejections+1;
RETURN[LOOPHOLE[12345B]];
END;
b.encapsulation.slaTimeQueued ← CommUtilDefs.GetTicks[];
Enqueue[IF high THEN @lib.hiPriQueue ELSE @lib.lowPriQueue,b];
IF lib.sendBuffer=NIL THEN StartSending[line];
RE
TURN[PupTypes.noErrorPupErrorCode];
END;

SendBuffer: ENTRY PROCEDURE [b: Buffer] =
BEGIN
host: SlaHost ← b.encapsulation.slaDestHost;
line: Line ← routingTable[host].line;
lib: LIB ← @lineInfo[line];
n: CARDINAL;
high: BOOLEAN;
IF doDebug AND b.seal#DriverTypes.bufferSeal THEN Glitch[BufferSealBroken];
b.device ← sla;
IF b.encapsulation.slaBroadcast THEN
BEGIN SendOverNextLine[b,0]; RETURN; END;
IF host>=maxSlaHost OR routingTable[host].hops=longHop OR lib.state=halfUp THEN
BEGIN -- no route
IF doStats THEN StatIncr[statSlaNoRoute];
DriverDefs.PutOnGlobalDoneQueue[b];
RETURN;
END;
IF doCheck AND line>=activeLines THEN Glitch[LineNumberTooBig];
n ← lib.hiPriQueue.length+lib.lowPriQueue.length;
-- maybe we should throw away loPri packets if this is a hiPri one ?
IF n>MIN[maxPacketsPerLine,BufferDefs.BuffersLeft[]] THEN
BEGIN
lib.rejections ← lib.rejections+1;
IF doStats THEN StatIncr[statSlaQueueFull];
DriverDefs.PutOnGlobalDoneQueue[b];
RETURN;
END;
n ← CountFriends[b,@lib.lowPriQueue];
high ← b.length<maxHiPriWords AND n=0;
n ← n+CountFriends[b,@lib.hiPriQueue];
IF n>5 THEN
BEGIN
lib.rejections ← lib.rejections+1;
lib.connRejections ← lib.connRejections+1;
IF high THEN lib.hiRejections ← lib.hiRejections+1;
IF doStats THEN StatIncr[statSlaConnectionLimit];
DriverDefs.PutOnGlobalDoneQueue[b];
RETURN;
END;
b.encapsulation.slaTimeQueued ← CommUtilDefs.GetTicks[];
Enqueue[IF high THEN @lib.hiPriQueue ELSE @lib.lowPriQueue,b];
IF lib.sendBuffer=NIL
THEN StartSending[line];
RETURN;
END;

SendOverNextLine: INTERNAL PROCEDURE [b: Buffer, firstLine: Line] =
BEGIN
line: Line;
lib: LIB;
limit: CARDINAL ← MIN[maxPacketsPerLine,BufferDefs.BuffersLeft[]];
FOR line IN [firstLine..activeLines) DO
lib ← @lineInfo[line];
IF lib.state=up OR lib.state=loopedBack THEN
BEGIN
IF (lib.hiPriQueue.length+lib.lowPriQueue.length)>limit THEN LOOP;
b.encapsulation.slaSourceLine ← line;
b.encapsulation.slaTimeQueued ← CommUtilDefs.GetTicks[];
-- Don’t bother checking order on broadcast packets
IF b.length<maxHiPriWords THEN Enqueue[@lib.hiPriQueue,b]
ELSE Enqueue[@lib.lowPriQueue,b];
IF lib.sendBuffer=NIL THEN StartSending[line];
RETURN;
END;
ENDLOOP;
DriverDefs.PutOnGlobalDoneQue
ue[b]; -- no more lines to send to
END;

CountFriends: INTERNAL PROCEDURE [b: Buffer, q: Queue] RETURNS [n: CARDINAL] =
BEGIN
n ← 0;
SELECT b.encapsulation.slaType FROM
DriverTypes.routingSlaPacket => RETURN;
DriverTypes.pupSlaPacket =>
BEGIN
pup, maybe: PupBuffer;
pup ← LOOPHOLE[b];
maybe ← LOOPHOLE[q.first];
UNTIL maybe=NIL DO
IF maybe.enc
apsulation.slaType=DriverTypes.pupSlaPacket
-- Open code the multi-word
compare because it uses non-resident code.
AND pup.dest.net=maybe.dest.net
AND pup.dest.host=maybe.dest.host
AND pup.dest.socket=maybe.dest.socket THEN n ← n+1;
maybe ← LOOPHOLE[maybe.next];
ENDLOOP;
END;
EN
DCASE => Glitch[ImpossibleEndcase];
END;

StartSending: INTERNAL PROCEDURE [line: Line] =
BEGIN
lib: LIB ← @lineInfo[line];
b: Buffer;
SELECT TRUE FROM
lib.hiPriQueue.length#0 =>
BEGIN b←Dequeue[@lib.hiPriQueue]; b.encapsulation.slaHi ← TRUE; END;
lib.lowPriQueue.length#0 =>
BEGIN b←Dequeue[@lib.lowPriQueue]; b.encapsulation.slaHi ← FALSE; END;
ENDCASE => RETURN; -- nothing to send
AppendBuffer[b,line,send];
lib.timeSendStarted ← CommUtilDefs.GetTicks[];
SELECT mode FROM -- Poke Transmitter
eia =>
BEGIN
eiaControlAddr↑ ← 140000B+line*eiaShift;
--
Yetch. Bug in hardware looses interrupts.
THROUGH [0..4) DO ENDLOOP; -- wait at least 38 microseconds
eiaControlAddr↑ ← 140000B+line*eiaShift;
END;
commProc => AltoRamDefs.ChangeControlReg[line*commProcShift,100100B];
ENDCASE => Dri
verDefs.Glitch[ImpossibleEndcase];
END;

-- This rest of this module is not used very often. Someday it should get split out into a separate module so that we don’t clutter up core with unused code. I
don’t want to bother doing that right now.

-- This gets used f
or every routing packet if a line is down.

StopLine: INTERNAL PROCEDURE [line: Line] =
BEGIN
b: Buffer;
lib: LIB = @lineInfo[line];
lte: LTE = @lineTable[line];
lte.inputState ← lte.outputState ← reset; -- turn off hardware
lte.inputLCB ← lte.outputLCB ← noLCB; -- yank LCB’s out from under it
UNTIL (b←Dequeue[@lib.lowPriQueue])=NIL DO Enqueue[@outputQueue,b]; ENDLOOP;
UNTIL (b←Dequeue[@lib.hiPriQueue])=NIL DO Enqueue[@outputQueue,b]; ENDLOOP;
NOTIFY ready;
WHILE (b←lib.recvBuffer)#NIL DO
lib.recvBuffer ← b.next;
ReturnFreeBuffer[b];
ENDLOOP;
WHILE (b←lib.sendBuffer)#NIL DO
lib.sendBuffer ← b.next;
Enqueue[@outputQue
ue,b];
ENDLOOP;
NOTIFY ready;
END;

StartLine: INTERNAL PROCEDURE [line: Line] =
BEGIN
lib: LIB = @lineInfo[line];
lte: LTE = @lineTable[line];
b: Buffer;
lte.inputState ← rIdle;
lte.outputState ← tIdle;
lib.timeOfLastRT ← CommUtilDefs.GetTicks[];
THROUGH [0..2) DO
UNTIL (b←DriverDefs.GetInputBuffer[])#NIL DO WAIT pause; ENDLOOP;
AppendBuffer[b,line,recv];
ENDLOOP;
SELECT mode FROM
eia =>
BEGIN
eiaControlAddr↑ ← 100410B+line*eiaShift; -- talk to dataset, 9600 baud
eiaControlAddr↑ ← 130200B+line*eiaShift; -- Reset
eiaControlAddr↑ ← 110700B+line*eiaShift; -- 8 data bits, no parity, sync
eiaControlAddr↑ ← 120000B+line*eiaShift+syn; -- sync char ← 26B
eiaControlAddr↑ ← 120400B+line*eiaShift+fill; -- fill character ← 377B
eiaControlAddr↑ ← 130100B+line*eiaShift; -- Restart Receiver
END;
commProc =>
BEGIN
foo: CARDINAL; -- Compiler doesn’t like line*commProcShift
foo ← line*commProcShift;
(commProcControlAddr+foo+0)↑ ← 60B;
(commProcControlAddr+foo+1)↑ ← 302B;
(commProcControlAddr+foo+2)↑ ← syn;
AltoRamDefs.ChangeControlReg[foo,100200B]; -- Enable Receiver
END;
ENDCASE => Dr
iverDefs.Glitch[ImpossibleEndcase];
END;

-- Be sure the micr
ocode is loaded by the time you get here...
ActivateDriver: ENTRY PROCEDURE =
BEGIN
line: Line;
debuggerBits: WORD ← 0;
CommUtilDefs.AddCleanupProcedure[@cleanupItem];
CommUtilDefs.AddInterruptHandler[slaInterruptLevel,@hardware,debuggerBits];
hardProcess ← FORK Interrupt[];
watcherProcess ← FORK Watcher[];
doitProcess ← FORK Doit[];
SELECT mode FROM
eia => AltoRamDefs.EnableEia[lineTable];
commProc => AltoRamDefs.SetLineTab[lineTable];
ENDCASE => DriverDefs.Glitch[ImpossibleEndcase];
FOR line IN [0..active
Lines) DO StartLine[line]; ENDLOOP;
END;

DeactivateDriver: PROCEDURE =
BEGIN

Glitch[CantT
urnSlaDriverOffYet];
END;

CreateSlaDriver: PUBLIC PROCEDURE [host, net, lines: CARDINAL]
RETURNS [BOOLEAN] =
BEGIN
line: Line;
h: SlaHost;
size: CARDINAL = 8+maxByte+maxSlaHost*SIZE[RoutingTableEntry]+
maxLine*(SIZE[LineInfoBlock]+SIZE[LineTableEntry]);
p: POINTER ← CommUtilDefs.AllocateSegment[size];
IF doDebug THEN
BEGIN
DriverDefs.giantVector.slaThings ← p;
END;
-- layout is: crcTable, junk, LTEs, routingTable, lineInfo
myNetwork.hostNumber ← host;
myNetwork.netNumber ← net;
SELECT InlineDefs.BITAND[lines,eiaFlag+commProcFlag] FROM
eiaFlag => mode ← eia;
commProcFlag => mode ← commProc;
ENDCASE => Glitch[HardwareTypeMustBeSpecified];
activeLines ← InlineDefs.BITAND[lines,17B];
-- LineTableEntrys must be on EVEN word boundries.
-- We get
it there because Segments are page aligned.
crcTable ← p; p ← p+maxByte;
IF doStats THEN myNetwork.stats ← p;
p↑ ← activeLines; -- For Debugging
(p+4)↑ ← (p+5)↑ ← (p+6)↑ ← 0; -- CommProc Timer stuff
lineTable ← p+8; (p+7)↑ ← crcTable; p ← 8+p+maxLine*SIZE[LineTableEntry];
routingTable ← p; p ← p+maxSlaHo
st*SIZE[RoutingTableEntry];
lineInfo ← p;
-- This needs to
be done before Bill’s microcode gets loaded
FOR h IN SlaHost DO routingTable[h] ← [hops: longHop, line: 0]; ENDLOOP;
FOR line IN Line DO
lineTable[line] ← [
link: @lineInfo[line],
hardwareStatus: 0,
inputLCB: noLCB,
fill1: 0,
inputState: rIdle,
outputLCB: noLCB,
fill2: 0,
outputState: tIdle ];
lineInfo[line] ← [
sendBuffer: NIL,
recvBuffer: NIL,
state: down,
partner: noPartner,
timeOfLastRT: , timeSendStarted:,
hiPriQueue: , lowPriQueue: ,
hiPacketsSent: 0, hiBytesSent: 0, hiQueueDelay: 0,
queueDelay: 0,
connRejections: 0, hiRejections: 0, rejections: 0,
deaths: 0, stuck: 0, timeout: 0, overrun: 0,
packetsSent: 0, packetsRecv: 0,
crcErrors: 0, syncErrors: 0, controlErrors: 0,
bytesSent: 0, bytesRecv: 0 ];
QueueInitialize[@lineInfo[line].hiPriQueue];
QueueInitialize[@lineInfo[line].lowPriQueue];
ENDLOOP;
QueueInitialize[@inputQueue];
QueueInitialize[@outputQueue];
InitCrcTable[];
DriverDefs.AddDeviceToChain[@myNetwork,SIZE[
LineControlBlock]];
RETURN[TRUE];
END;


DeleteDriv
er: PROCEDURE =
BEGIN
END;

InitCrcTable: PROCEDURE =
BEGIN OPEN InlineDefs;
i, power: CARDINAL;
crc, val: WORD;
magic: WORD = 120001B;
FOR i IN [0..maxByte) DO
crc ← 0;
val ← i;
FOR power IN [0..7] DO
IF BITAND[val,1]=0 THEN val←BITSHIFT[val,-1]
ELSE
BEGIN
crc ← BITXOR[crc,BITSHIFT[magic,-(7-power)]];
val ← BITXOR[BITSHIFT[val,-1],magic];
END;
ENDLOOP;
crcTable[i] ← crc;
ENDLOOP;
END;

GetSlaLineTable: PUBLIC PROCEDURE
RETURNS [POINTER TO ARRAY Line OF LineTableEn
try] =
BE
GIN
RETURN[lineTable];
END;

GetCrcTable: PUBLIC PROCEDURE
RETURNS [POINTER TO ARRAY [0..maxByte) OF
WORD] =
BEGIN
RETURN[crcTable];
END;

GetSlaRoutingTable: PUBLIC PROCEDURE
RETURNS [POINTER TO ARRAY SlaHost OF RoutingTableEntry
] =
BEGIN
RE
TURN[routingTable];
END;

GetSlaInfoBlocks: PUBLIC PROCEDURE
RETURNS [DESCRIPTOR FOR ARRAY OF LineInfoBlock] =
BEGIN
RETURN[
DESCRIPTOR[lineInfo,activeLines]];
END;


-- BE
WARE: Don’t try using WORRY Mode Breakpoints. There is no backstop mechanism to turn off the microcode when going to the dubegger, so it will start reading+writing into the Debuggers core image. Things will probably screwup if you get PUNTed.

Broom: PROCEDURE [why: CommUtilDefs.CleanupReason] =
BEGIN
SELECT why FROM
Finish, Abort, -- going back to Exec
OutLd => -- going to Debugger
SELECT mode FROM
eia => AltoRamDefs.EnableEia[NIL0];
commProc => SmashOffCommProc[];
ENDCASE => DriverDefs.Glitch[ImpossibleEndcase];
InLd => -- comming back from Debugger
SELECT mode FROM
eia => AltoRamDefs.EnableEia[lineTable];
commProc => NULL; -- let things timeout and get restarted for now
ENDCASE => DriverDefs.Glitch[ImpossibleEndcase];
Save, Restore, Checkpoint, Restart, Continue =>
Glitch[CantMakImageWhileEtherentDriverIsActive];
ENDCASE => Glitch[ImpossibleEndca
se];
END;


sa
veInfo: ARRAY Line OF WORD;
SmashOffCommProc: PROCEDURE =
BEGIN
line: Line;
foo: CARDINAL; -- Compiler doesn’t like line*commProcShift
FOR line IN Line DO
foo ← line*commProcShift;
saveInfo[line] ← (commProcControlAddr+foo)↑;
AltoRamDefs.ChangeControlReg[foo,300B];
ENDLOOP;
END;


-- initialization
CommUtilDefs.DisableTimeout[@hardware];
CommUtilDefs.SetTimeout[@ready,CommUtilDefs.MsecToTicks[60000]];
CommUtilDefs.SetTimeout[@pause,CommUtilDefs.MsecToTicks[100]];
IF doStats THEN SetupSlaThings[];
END. -- AltoSlaDriver