-- 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 StatCounterIndex; statSlaFCT: 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 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 = 270/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; StartLine[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 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 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; 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.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.PutOnGlobalDoneQueue[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 StatIncr[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 StatIncr[statSlaRoutingPacketsSent]; END; DecapsulateBuffer: PROCEDURE [b: Buffer] RETURNS [BufferType] = BEGIN SELECT b.encapsulation.slaType FROM DriverTypes.pupSlaPacket => BEGIN IF 2*b.length RETURN[rejected]; END; EncapsulateRpp: PROCEDURE [RppBuffer, 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.pupLength+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.length5 THEN BEGIN IF doStats THEN StatIncr[statSlaConnectionLimit]; lib.rejections _ lib.rejections+1; lib.connRejections _ lib.connRejections+1; IF high THEN lib.hiRejections _ 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]; RETURN[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.length5 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 RETURN; DriverTypes.pupSlaPacket => BEGIN pup, maybe: PupBuffer; pup _ LOOPHOLE[b]; maybe _ LOOPHOLE[q.first]; UNTIL maybe=NIL DO IF maybe.encapsulation.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; 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 _ 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 => 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. -- This gets used for 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[@outputQueue,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 => DriverDefs.Glitch[ImpossibleEndcase]; END; -- Be sure the microcode 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..activeLines) DO StartLine[line]; ENDLOOP; END; DeactivateDriver: PROCEDURE = BEGIN Glitch[CantTurnSlaDriverOffYet]; 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+maxSlaHost*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; DeleteDriver: 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 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: 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[ImpossibleEndcase]; END; saveInfo: 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 (635)\5059b7B389i119I700b14B1089b9B3582b12B1179b9B344b4B475b69B448b20B1152b15B174b19B949b17B374b14B63b14B459b13B1336b16B233b10B1582b16B793b12B386bi80BI258b12B552i52I261i205I64b8B660b9B1231bi63I14B530b16B65b15B701i5I14i90I323bi63BI1096b12B30b12B432b15B110b11B107b18B119b16B132bi3I8i16I22i193I4i4I6B750b16B