-- 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.