-- File: AltoSlaDriver.mesa
-- Edit: HGM, March 12, 1981 1:02 PM
-- Edit: BLyon, January 16, 1981 1:29 PM
DIRECTORY
BcplOps USING [CleanupReason],
ImageDefs USING [CleanupItem, AllReasons, AddCleanupProcedure],
Inline USING [BITAND, BITXOR, BITSHIFT, LowByte, HighByte, HighHalf],
Process USING [SetTimeout, DisableTimeout, MsecToTicks, SetPriority],
Storage USING [Node, CopyString, FreeString],
Password USING [Check, Encrypted],
StatsDefs USING [StatIncr, StatBump, StatCounterIndex],
CommFlags USING [doDebug, doStats],
CommUtilDefs USING [AddInterruptHandler, CopyLong, InterruptLevel],
AltoRam USING [GetTicks, msPerTick, ChangeControlReg, EnableEia, SetLineTab],
AltoSlaDefs,
DriverDefs USING [
GetGiantVector, Glitch, SlaStats, MaybeGetFreeBuffer, GetInputBuffer, Network,
NetworkObject, AddDeviceToChain, PutOnGlobalDoneQueue, PutOnGlobalInputQueue],
PupDefs,
BufferDefs,
PupTypes USING [allHosts, PupErrorCode],
DriverTypes USING [slaEncapsulationOffset, slaEncapsulationBytes, bufferSeal],
SpecialSystem USING [broadcastHostNumber, HostNumber];
AltoSlaDriver: MONITOR
IMPORTS
ImageDefs, Inline, Process, Storage, Password, StatsDefs, DriverDefs,
CommUtilDefs, BufferDefs, AltoRam, AltoSlaDefs
EXPORTS BufferDefs, DriverDefs, AltoSlaDefs
SHARES BufferDefs, DriverDefs, DriverTypes, SpecialSystem =
BEGIN OPEN StatsDefs, DriverDefs, AltoSlaDefs, BufferDefs;
-- EXPORTed TYPEs
Network: PUBLIC TYPE = DriverDefs.Network;
maxPacketsPerLine: CARDINAL = 10;
mode: {eia, commProc};
hardware: CONDITION;
cleanupItem: ImageDefs.CleanupItem ← [, ImageDefs.AllReasons, Broom];
hardProcess, watcherProcess: PROCESS;
pause: CONDITION;
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,
encapsulateOis: EncapsulateOis, sendBuffer: SendBuffer,
forwardBuffer: ForwardBuffer, activateDriver: ActivateDriver,
deactivateDriver: DeactivateDriver, deleteDriver: DeleteDriver,
interrupt: Interrupt, device: sla, alive:, speed: 10, index:, buffers:,
spare:, netNumber:, hostNumber:, next: NIL, pupStats: DriverDefs.SlaStats,
stats: NIL];
ImpossibleEndcase: PUBLIC ERROR = CODE;
QueueScrambled: PUBLIC ERROR = CODE;
BufferSealBroken: PUBLIC ERROR = CODE;
UnexpectedCompletionCodeFromMicrocode: PUBLIC ERROR = CODE;
LineNumberTooBig: PUBLIC ERROR = CODE;
CantTurnSlaDriverOffYet: PUBLIC ERROR = CODE;
CantMakeImageWhileDriverIsActive: PUBLIC ERROR = CODE;
HyperspaceNotSupported: PUBLIC ERROR = CODE;
activeLines: CARDINAL ← 0;
slaInterruptLevel: CommUtilDefs.InterruptLevel = 12;
slaInterruptBit: WORD = Inline.BITSHIFT[1, slaInterruptLevel];
routingInterval: CARDINAL = 5000/AltoRam.msPerTick;
routingTimeout: CARDINAL = 20000/AltoRam.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;
slaEncapsulationWords: CARDINAL = slaEncapsulationBytes/2;
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 StatCounterIndex;
statSlaFCT: PUBLIC StatCounterIndex;
Watcher: ENTRY PROCEDURE =
BEGIN
line: Line ← 0;
routeTime: CARDINAL ← AltoRam.GetTicks[];
watchit: CONDITION;
Process.SetTimeout[@watchit, Process.MsecToTicks[250]];
Process.SetPriority[2];
UNTIL pleaseStop DO
WAIT watchit;
-- should check for lost interrupts here if there is an easy way
FlushDeadLines[];
IF AltoRam.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
BEGIN
lib: LIB = @lineInfo[line];
IF dialoutPassword # NIL AND (lib.state # up AND lib.state # loopedBack)
THEN SendPasswordPacket[line];
SendRoutingPacket[line];
END;
IF (line ← line + 1) >= activeLines THEN
BEGIN
routeTime ← routeTime + routingInterval;
IF AltoRam.GetTicks[] - routeTime > routingInterval THEN
-- don't thrash if buffers are hard to get
routeTime ← AltoRam.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 = 270/AltoRam.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].finished AND
(t ← (AltoRam.GetTicks[] - lib.timeSendStarted)) >
-- b.length is words, not bytes
(overheadPerPacket + lib.sendBuffer.length*(2*2))/bytesPerTick);
timeout ← (AltoRam.GetTicks[] - lib.timeOfLastRT) > routingTimeout;
IF timeout OR stuck THEN
BEGIN
IF lib.state # down THEN
BEGIN
IF CommFlags.doStats THEN StatIncr[statSlaLineDied];
IF CommFlags.doStats AND stuck THEN StatIncr[statSlaPacketsStuck];
IF CommFlags.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;
StartLine[line];
END;
ENDLOOP;
END;
Interrupt: ENTRY PROCEDURE =
BEGIN
line: Line;
lib: LIB;
b, temp: Buffer;
lcb: LongLCB;
Process.SetPriority[3];
UNTIL pleaseStop DO
WAIT hardware;
FOR line IN [0..activeLines) DO
lib ← @lineInfo[line];
WHILE (b ← lib.recvBuffer) # NIL DO
IF CommFlags.doDebug AND b.seal # DriverTypes.bufferSeal THEN
Glitch[BufferSealBroken];
lcb ← b.iocbChain;
IF ~lcb.finished THEN EXIT;
lib.recvBuffer ← b.next;
IF lcb.microcodeError # ok THEN
BEGIN -- something screwed up, recycle this buffer
SELECT lcb.microcodeError FROM
hardware =>
BEGIN -- can only be ROR
lib.overrun ← lib.overrun + 1;
IF CommFlags.doStats THEN StatIncr[statSlaROR];
END;
bufferOverflow =>
BEGIN
lib.syncErrors ← lib.syncErrors + 1;
IF CommFlags.doStats THEN StatIncr[statSlaSyncError];
END;
missingStx, trashAfterDle =>
BEGIN
lib.controlErrors ← lib.controlErrors + 1;
IF CommFlags.doStats THEN StatIncr[statSlaControlError];
END;
crc =>
BEGIN
lib.crcErrors ← lib.crcErrors + 1;
IF CommFlags.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 CommFlags.doStats THEN StatIncr[statSlaEmptyFreeQueue];
LOOP;
END;
AppendBuffer[temp, line, recv];
b.network ← LONG[@myNetwork];
b.encapsulation.slaSourceLine ← line;
b.length ← (((b.length - slaEncapsulationOffset)*2) - lcb.bytesLeft)/2;
IF CommFlags.doStats THEN StatIncr[statSlaPacketsRecv];
IF CommFlags.doStats THEN StatBump[statSlaWordsRecv, b.length];
lib.packetsRecv ← lib.packetsRecv + 1;
lib.bytesRecv ← lib.bytesRecv + b.length*2;
PutOnGlobalInputQueue[b];
ENDLOOP;
WHILE (b ← lib.sendBuffer) # NIL DO
IF CommFlags.doDebug AND b.seal # DriverTypes.bufferSeal THEN
Glitch[BufferSealBroken];
lcb ← b.iocbChain;
IF ~lcb.finished THEN EXIT;
-- there should be only one at a time on the send chain
lib.sendBuffer ← b.next;
SELECT lcb.microcodeError FROM
ok =>
BEGIN -- normal good case
IF CommFlags.doStats THEN StatIncr[statSlaPacketsSent];
IF CommFlags.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;
hardware =>
BEGIN -- can only be FCT/TransOverrun
lib.overrun ← lib.overrun + 1;
IF CommFlags.doStats THEN StatIncr[statSlaFCT];
END;
ENDCASE => Glitch[UnexpectedCompletionCodeFromMicrocode];
IF b.encapsulation.slaBroadcast THEN
SendOverNextLine[b, b.encapsulation.slaSourceLine + 1]
ELSE DriverDefs.PutOnGlobalDoneQueue[b];
IF lib.sendBuffer = NIL THEN StartSending[line];
-- see if more to go out
ENDLOOP;
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 CommFlags.doDebug AND b.seal # DriverTypes.bufferSeal THEN
Glitch[BufferSealBroken];
b.device ← sla; -- this gets all actual input+output
IF Inline.HighHalf[b] # 0 THEN Glitch[HyperspaceNotSupported];
lcb↑ ←
[next: noLCB, interruptBit: slaInterruptBit, finished: FALSE,
microcodeError: ok, 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; ENDLOOP;
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.finished THEN head↑ ← short;
END;
ProcessRoutingPacket: ENTRY PROCEDURE [b: Buffer] =
BEGIN
p: LONG POINTER TO RoutingTablePacket = LOOPHOLE[@b.rawWords];
line: Line = b.encapsulation.slaSourceLine;
lib: LIB = @lineInfo[line];
lineInfo[line].timeOfLastRT ← AltoRam.GetTicks[];
PurgeLineFromRT[line];
IF p.sourceHost = myNetwork.hostNumber THEN
BEGIN -- line is looped back
IF lib.lineNeedsPassword THEN
BEGIN
SELECT lib.state FROM
passwordOk => lib.state ← loopedBack;
badPassword => NULL;
ENDCASE => lib.state ← needPassword;
END
ELSE lib.state ← loopedBack;
routingTable[myNetwork.hostNumber] ← [hops: 1, line: line];
END
ELSE
BEGIN
SELECT lib.state FROM
up =>
IF p.rt[myNetwork.hostNumber].hops # 1 THEN ReceivedRoutingTable[lib];
halfUp, passwordOk =>
IF p.rt[myNetwork.hostNumber].hops = 1 THEN lib.state ← up;
ENDCASE => ReceivedRoutingTable[lib];
IF lib.state = up THEN
FOR i: CARDINAL IN [0..MIN[p.numEntries, maxSlaHost]) DO
newHop: CARDINAL = p.rt[i].hops + 1;
rte: POINTER TO RoutingTableEntry = @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 CommFlags.doStats THEN StatIncr[statSlaRoutingPacketsReceived];
END;
ReceivedRoutingTable: PROCEDURE [lib: LIB] =
BEGIN
IF lib.lineNeedsPassword THEN
BEGIN IF lib.state # badPassword THEN lib.state ← needPassword; END
ELSE lib.state ← halfUp;
END;
PurgeLineFromRT: PROCEDURE [line: Line] =
BEGIN
FOR host: SlaHost IN SlaHost DO
IF routingTable[host].line = line THEN routingTable[host].hops ← longHop;
ENDLOOP;
END;
SendRoutingPacket: INTERNAL PROCEDURE [line: Line] =
BEGIN
lib: LIB = @lineInfo[line];
partner: CARDINAL = lib.partner;
b: Buffer;
p: LONG POINTER TO RoutingTablePacket;
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 (lib.state = halfUp OR lib.state = passwordOk) AND partner IN SlaHost THEN
p.rt[partner].hops ← 1;
b.length ← slaEncapsulationWords + SIZE[RoutingTablePacket];
b.encapsulation ←
[sla[
slaSpare1:, slaSpare2:, slaSpare3:, slaHi:, slaBroadcast: FALSE,
slaTimeQueued:, slaSourceLine: line, slaDestHost:, slaType: routing]];
b.encapsulation.slaTimeQueued ← AltoRam.GetTicks[];
Enqueue[@lib.hiPriQueue, b];
IF lib.sendBuffer = NIL THEN StartSending[line];
IF CommFlags.doStats THEN StatIncr[statSlaRoutingPacketsSent];
END;
DecapsulateBuffer: PROCEDURE [b: Buffer] RETURNS [BufferType] =
BEGIN
SELECT b.encapsulation.slaType FROM
pup =>
BEGIN
IF 2*b.length < b.pupLength + slaEncapsulationBytes THEN
BEGIN
IF CommFlags.doStats THEN StatIncr[statPupsDiscarded];
RETURN[rejected];
END;
RETURN[pup];
END;
ois =>
BEGIN
IF 2*b.length < b.ois.pktLength + slaEncapsulationBytes THEN
BEGIN
IF CommFlags.doStats THEN StatIncr[statOisDiscarded];
RETURN[rejected];
END;
RETURN[ois];
END;
routing =>
BEGIN
ProcessRoutingPacket[b];
ReturnFreeBuffer[b];
RETURN[processed];
END;
password =>
BEGIN
ProcessPasswordPacket[b];
ReturnFreeBuffer[b];
RETURN[processed];
END;
ENDCASE => RETURN[rejected];
END;
EncapsulatePup: PROCEDURE [b: PupBuffer, destination: PupHostID] =
BEGIN
broadcast: BOOLEAN = destination = PupTypes.allHosts;
IF CommFlags.doDebug AND b.seal # DriverTypes.bufferSeal THEN
Glitch[BufferSealBroken];
b.encapsulation ←
[sla[
slaSpare1:, slaSpare2:, slaSpare3:, slaHi:, slaBroadcast: broadcast,
slaTimeQueued:, slaSourceLine:, slaDestHost: destination, slaType: pup]];
b.length ← (b.pupLength + 1 + slaEncapsulationBytes)/2;
END;
EncapsulateOis: PROCEDURE [
b: OisBuffer, destination: SpecialSystem.HostNumber] =
BEGIN
broadcast: BOOLEAN = destination = SpecialSystem.broadcastHostNumber;
localDest: WORD;
IF CommFlags.doDebug AND b.seal # DriverTypes.bufferSeal THEN
Glitch[BufferSealBroken];
WITH dest: destination SELECT FROM
multicast => localDest ← maxSlaHost;
physical =>
BEGIN -- undo encoding from AltoSpecialSystem
IF dest.a=0 AND dest.b=125026B
AND Inline.HighByte[dest.c]=myNetwork.netNumber.b THEN
localDest ← Inline.LowByte[dest.c]
ELSE localDest ← maxSlaHost;
END;
ENDCASE => ERROR;
b.encapsulation ←
[sla[
slaSpare1:, slaSpare2:, slaSpare3:, slaHi:, slaBroadcast: broadcast,
slaTimeQueued:, slaSourceLine:, slaDestHost: localDest, slaType: ois]];
b.length ← (b.ois.pktLength + 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[noErrorPupErrorCode]; END;
IF host >= maxSlaHost OR routingTable[host].hops = longHop OR
(lib.state # up AND lib.state # loopedBack) THEN
BEGIN -- no route
IF CommFlags.doStats THEN StatIncr[statSlaNoRoute];
RETURN[cantGetTherePupErrorCode];
END;
IF CommFlags.doDebug 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 CommFlags.doStats THEN StatIncr[statSlaQueueFull];
lib.rejections ← lib.rejections + 1;
RETURN[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 CommFlags.doStats THEN StatIncr[statSlaConnectionLimit];
lib.rejections ← lib.rejections + 1;
lib.connRejections ← lib.connRejections + 1;
IF high THEN lib.hiRejections ← lib.hiRejections + 1;
RETURN[connectionLimitPupErrorCode];
END;
b.encapsulation.slaTimeQueued ← AltoRam.GetTicks[];
Enqueue[IF high THEN @lib.hiPriQueue ELSE @lib.lowPriQueue, b];
IF lib.sendBuffer = NIL THEN StartSending[line];
RETURN[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 CommFlags.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 # up AND lib.state # loopedBack) THEN
BEGIN -- no route
IF CommFlags.doStats THEN StatIncr[statSlaNoRoute];
DriverDefs.PutOnGlobalDoneQueue[b];
RETURN;
END;
IF CommFlags.doDebug 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 CommFlags.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 CommFlags.doStats THEN StatIncr[statSlaConnectionLimit];
DriverDefs.PutOnGlobalDoneQueue[b];
RETURN;
END;
b.encapsulation.slaTimeQueued ← AltoRam.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 ← AltoRam.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.PutOnGlobalDoneQueue[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
routing, password => RETURN;
pup =>
BEGIN
pup, maybe: PupBuffer;
pup ← LOOPHOLE[b];
maybe ← LOOPHOLE[q.first];
UNTIL maybe = NIL DO
IF maybe.encapsulation.slaType = pup
-- 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;
ois =>
BEGIN
ois, maybe: OisBuffer;
ois ← LOOPHOLE[b];
maybe ← LOOPHOLE[q.first];
UNTIL maybe = NIL DO
IF maybe.encapsulation.slaType = ois
-- Again, open code the multi-word compare.
AND ois.ois.destination.net = maybe.ois.destination.net AND
ois.ois.destination.host = maybe.ois.destination.host AND
ois.ois.destination.socket = maybe.ois.destination.socket THEN
n ← n + 1;
maybe ← LOOPHOLE[maybe.next];
ENDLOOP;
END;
ENDCASE => 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 ← AltoRam.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 => AltoRam.ChangeControlReg[line*commProcShift, 100100B];
ENDCASE => DriverDefs.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.
StopLine: INTERNAL PROCEDURE [line: Line] =
BEGIN
MaybeSendOverNextLine: INTERNAL PROCEDURE [b: Buffer] =
BEGIN
IF b.encapsulation.slaBroadcast THEN
SendOverNextLine[b, b.encapsulation.slaSourceLine + 1]
ELSE DriverDefs.PutOnGlobalDoneQueue[b];
END;
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
MaybeSendOverNextLine[b]; ENDLOOP;
UNTIL (b ← Dequeue[@lib.hiPriQueue]) = NIL DO
MaybeSendOverNextLine[b]; ENDLOOP;
WHILE (b ← lib.recvBuffer) # NIL DO
lib.recvBuffer ← b.next; ReturnFreeBuffer[b]; ENDLOOP;
WHILE (b ← lib.sendBuffer) # NIL DO
lib.sendBuffer ← b.next; MaybeSendOverNextLine[b]; ENDLOOP;
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 ← AltoRam.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;
AltoRam.ChangeControlReg[foo, 100200B]; -- Enable Receiver
END;
ENDCASE => DriverDefs.Glitch[ImpossibleEndcase];
END;
-- Be sure the microcode is loaded by the time you get here...
ActivateDriver: ENTRY PROCEDURE =
BEGIN
ImageDefs.AddCleanupProcedure[@cleanupItem];
CommUtilDefs.AddInterruptHandler[slaInterruptLevel, @hardware, 0];
hardProcess ← FORK Interrupt[];
watcherProcess ← FORK Watcher[];
SELECT mode FROM
eia => AltoRam.EnableEia[lineTable];
commProc => AltoRam.SetLineTab[lineTable];
ENDCASE => DriverDefs.Glitch[ImpossibleEndcase];
FOR line: Line IN [0..activeLines) DO StartLine[line]; ENDLOOP;
END;
DeactivateDriver: PROCEDURE = BEGIN Glitch[CantTurnSlaDriverOffYet]; END;
CreateEIADriver: PUBLIC PROCEDURE [host, net, lines: CARDINAL]
RETURNS [BOOLEAN] =
BEGIN mode ← eia; CreateSlaDriver[host, net, lines]; RETURN[TRUE]; END;
CreateCommProcDriver: PUBLIC PROCEDURE [host, net, lines: CARDINAL]
RETURNS [BOOLEAN] =
BEGIN mode ← commProc; CreateSlaDriver[host, net, lines]; RETURN[TRUE]; END;
CreateSlaDriver: PROCEDURE [host, net, lines: CARDINAL] =
BEGIN
line: Line;
h: SlaHost;
size: CARDINAL =
8 + maxByte + maxSlaHost*SIZE[RoutingTableEntry] +
maxLine*(SIZE[LineInfoBlock] + SIZE[LineTableEntry]);
p: POINTER ← Storage.Node[size + 1];
IF (LOOPHOLE[p, CARDINAL] MOD 2) # 0 THEN p ← p + 1;
-- LineTableEntrys must be on EVEN word boundries, so round up if necessary.
IF CommFlags.doDebug THEN
BEGIN DriverDefs.GetGiantVector[].slaThings ← p; END;
-- layout is: crcTable, junk, LTEs, routingTable, lineInfo
myNetwork.hostNumber ← host;
myNetwork.netNumber ← [0, net];
myNetwork.buffers ← 2*lines;
myNetwork.alive ← lines # 0;
activeLines ← lines;
crcTable ← p;
p ← p + maxByte;
IF CommFlags.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 + maxSlaHost*SIZE[RoutingTableEntry];
lineInfo ← p;
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,
inputState: rIdle, outputLCB: noLCB, outputState: tIdle];
lineInfo[line] ←
[sendBuffer: NIL, recvBuffer: NIL, state: down, lineNeedsPassword: FALSE,
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;
InitCrcTable[];
DriverDefs.AddDeviceToChain[@myNetwork, SIZE[LineControlBlock]];
END;
DeleteDriver: PROCEDURE = BEGIN END;
InitCrcTable: PROCEDURE =
BEGIN OPEN Inline;
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 LineTableEntry] =
BEGIN 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 RETURN[routingTable]; END;
GetSlaInfoBlocks: PUBLIC PROCEDURE
RETURNS [DESCRIPTOR FOR ARRAY OF LineInfoBlock] =
BEGIN RETURN[DESCRIPTOR[lineInfo, activeLines]]; END;
-- BEWARE: 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: BcplOps.CleanupReason] =
BEGIN
SELECT why FROM
Finish, Abort, -- going back to Exec
OutLd => -- going to Debugger
SELECT mode FROM
eia => AltoRam.EnableEia[NIL0];
commProc => SmashOffCommProc[];
ENDCASE => DriverDefs.Glitch[ImpossibleEndcase];
InLd => -- comming back from Debugger
SELECT mode FROM
eia => AltoRam.EnableEia[lineTable];
commProc => NULL; -- let things timeout and get restarted for now
ENDCASE => DriverDefs.Glitch[ImpossibleEndcase];
Save, Restore, Checkpoint, Restart, Continue =>
Glitch[CantMakeImageWhileDriverIsActive];
ENDCASE => Glitch[ImpossibleEndcase];
END;
saveInfo: ARRAY Line OF WORD;
SmashOffCommProc: PROCEDURE =
BEGIN
FOR line: Line IN Line DO
foo: CARDINAL = line*commProcShift;
-- Compiler doesn't like line*commProcShift;
saveInfo[line] ← (commProcControlAddr + foo)↑;
AltoRam.ChangeControlReg[foo, 300B];
ENDLOOP;
END;
-- Password things
dialinPassword: POINTER TO Password.Encrypted ← NIL;
dialoutPassword: STRING ← NIL;
RememberDialinPassword: PUBLIC PROCEDURE [e: Password.Encrypted] =
BEGIN
IF dialinPassword = NIL THEN
dialinPassword ← Storage.Node[SIZE[Password.Encrypted]];
dialinPassword↑ ← e;
END;
RememberDialoutPassword: PUBLIC PROCEDURE [s: STRING] =
BEGIN
Storage.FreeString[dialoutPassword];
dialoutPassword ← Storage.CopyString[s];
MixupString[dialoutPassword];
END;
MixupString: PROCEDURE [s: STRING] =
BEGIN
mixup: ARRAY [0..6) OF WORD = [125B, 252B, 314B, 063B, 307B, 070B];
FOR i: CARDINAL IN [0..s.length) DO
s[i] ← Inline.BITXOR[s[i], mixup[i MOD 6]]; ENDLOOP;
END;
LineNeedsPassword: PUBLIC PROCEDURE [line: Line] =
BEGIN
lib: LIB ← @lineInfo[line];
IF line >= activeLines THEN Glitch[LineNumberTooBig];
lib.lineNeedsPassword ← TRUE;
END;
ProcessPasswordPacket: PROCEDURE [b: Buffer] =
BEGIN
password: STRING = [20];
longPassword: LONG STRING = LOOPHOLE[@b.rawWords];
line: Line = b.encapsulation.slaSourceLine;
lib: LIB = @lineInfo[line];
ok: BOOLEAN;
IF longPassword.length > password.maxlength OR dialinPassword = NIL THEN
RETURN;
FOR i: CARDINAL IN [0..longPassword.length) DO
password[i] ← longPassword[i]; ENDLOOP;
password.length ← longPassword.length;
MixupString[password];
ok ← Password.Check[password, dialinPassword↑];
MixupString[password];
IF ok THEN
BEGIN
IF (lib.state = down OR lib.state = needPassword OR lib.state = badPassword)
THEN lib.state ← passwordOk;
END
ELSE lib.state ← badPassword;
END;
SendPasswordPacket: INTERNAL PROCEDURE [line: Line] =
BEGIN
-- Copy WordsForString to avoid locking StringsA
WordsForString: PROCEDURE [chars: CARDINAL] RETURNS [CARDINAL] =
BEGIN RETURN[2 + ((chars + 1)/2)]; END;
lib: LIB = @lineInfo[line];
b: Buffer;
length: CARDINAL ← WordsForString[dialoutPassword.length];
IF line >= activeLines THEN Glitch[LineNumberTooBig];
UNTIL (b ← DriverDefs.MaybeGetFreeBuffer[]) # NIL DO WAIT pause; ENDLOOP;
CommUtilDefs.CopyLong[to: @b.rawWords, nwords: length, from: dialoutPassword];
b.length ← slaEncapsulationWords + length;
b.encapsulation ←
[sla[
slaSpare1:, slaSpare2:, slaSpare3:, slaHi:, slaBroadcast: FALSE,
slaTimeQueued:, slaSourceLine: line, slaDestHost:, slaType: password]];
b.encapsulation.slaTimeQueued ← AltoRam.GetTicks[];
Enqueue[@lib.hiPriQueue, b];
IF lib.sendBuffer = NIL THEN StartSending[line];
END;
-- initialization
Process.DisableTimeout[@hardware];
Process.SetTimeout[@pause, Process.MsecToTicks[100]];
IF CommFlags.doStats THEN SetupSlaThings[];
END.