-- Copyright (C) 1982, 1983 by Xerox Corporation. All rights reserved. -- SlaDriver.mesa, HGM, 30-Dec-83 15:54:15 DIRECTORY Environment USING [Byte], Heap USING [systemZone], Inline USING [LowHalf], System USING [GetClockPulses, HostNumber, MicrosecondsToPulses, nullNetworkNumber, Pulses], Buffer USING [AccessHandle, Buffer, GetBuffer, MakePool, NSBuffer, PupBuffer, Type], Driver USING [ NetworkObject, Network, PutOnGlobalDoneQueue, AddDeviceToChain, GetInputBuffer, PutOnGlobalInputQueue, ReturnFreeBuffer], DriverTypes USING [slaEncapsulationOffset, slaEncapsulationBytes], Process USING [Detach, MsecToTicks, SetPriority, SetTimeout], ProcessPriorities USING [priorityIOHigh], PupTypes USING [PupHostID, PupErrorCode], PupWireFormat USING [MesaToBcplLongNumber], RS232CCorrespondents USING [xerox850], RS232CEnvironment USING [CommParamObject], RS232C USING [ ChannelHandle, ChannelSuspended, CompletionHandle, Create, DeviceStatus, Get, GetStatus, PhysicalRecord, PhysicalRecordHandle, Put, Restart, SetLineType, SetParameter, Suspend, TransferWait, TransferStatus, TransmitNow], PhoneCreate USING [], Sla USING [ dle, DriverStatistics, etx, LineInfo, longHop, maxHops, maxSlaHost, noPartner, overheadPerPacket, RoutingTableEntry, RoutingTablePacket, SlaHost, State, stx, syn], SlaFormat USING [LineState, SlaStatsEntry, slaStatsReply, slaVersion]; SlaDriver: MONITOR IMPORTS Heap, Inline, Process, System, Buffer, Driver, PupWireFormat, RS232C EXPORTS Buffer, PhoneCreate SHARES Buffer = BEGIN z: UNCOUNTED ZONE = Heap.systemZone; nonAltoEtx: BOOLEAN ← FALSE; Byte: TYPE = Environment.Byte; -- EXPORTed TYPEs Network: PUBLIC TYPE = Driver.Network; info: LONG POINTER TO Sla.LineInfo; RoutingTable: TYPE = ARRAY Sla.SlaHost OF Sla.RoutingTableEntry; routingTable: LONG POINTER TO ARRAY Sla.SlaHost OF Sla.RoutingTableEntry ← NIL; stats: Sla.DriverStatistics; pool: Buffer.AccessHandle; outstandingGets: CARDINAL = 2; channelHandle: RS232C.ChannelHandle; -- The microcode doesn't help as much as it should copySize: CARDINAL = 2*1000; Copy: TYPE = PACKED ARRAY [0..copySize) OF Byte; sendCopy: LONG POINTER TO PACKED ARRAY [0..0) OF Byte; recvCopy: ARRAY [0..outstandingGets) OF LONG POINTER TO PACKED ARRAY [0..0) OF Byte; packetToSend: CONDITION; currentOutputBuffer, headOutputBuffer, tailOutputBuffer: Buffer.Buffer; packetsOnSendQueue: CARDINAL ← 0; myNet: Driver.NetworkObject ← [ next: NIL, decapsulateBuffer: Decapsulate, encapsulateAndSendPup: SendPup, encapsulateAndSendNS: SendNS, sendRawBuffer: SendRawBuffer, encapsulateAndForwardPup: ForwardPup, encapsulateAndForwardNS: ForwardNS, activateDriver: ActivateDriver, deactivateDriver: DeactivateDriver, deleteDriver: KillDriver, changeNumberOfInputBuffers: NIL, index:, device: sla, alive: TRUE, buffers: 0, netNumber: System.nullNetworkNumber, pupNetNumber: 0, pupHostNumber: 0, pupStats: SlaStats, statsLevel0: @stats, statsLevel1: NIL]; me: Network ← @myNet; CreateSla: PUBLIC PROCEDURE [host, net: CARDINAL] = BEGIN temp: LONG POINTER; commParm: RS232CEnvironment.CommParamObject ← [ full, bitSynchronous, bps9600, directConn[]]; channelHandle ← RS232C.Create[0, @commParm, preemptNever, preemptNever]; currentOutputBuffer ← headOutputBuffer ← tailOutputBuffer ← NIL; me.pupHostNumber ← host; me.pupNetNumber ← net; routingTable ← z.NEW[RoutingTable]; routingTable↑ ← ALL[[hops: Sla.longHop, line: 0]]; info ← z.NEW[Sla.LineInfo]; info↑ ← [ state: down, partner: Sla.noPartner, packetsSent: 0, packetsRecv: 0, bytesSent: 0, bytesRecv: 0, sendErrors: 0, recvErrors: 0, syncErrors: 0, crcErrors: 0, controlErrors: 0, garbagePackets: 0, deaths: 0, stuck: 0, timeout: 0, overrun: 0]; stats ← [1, LOOPHOLE[info], routingTable]; pool ← Buffer.MakePool[1, outstandingGets]; temp ← z.NEW[Copy]; sendCopy ← temp; FOR i: CARDINAL IN [0..outstandingGets) DO temp ← z.NEW[Copy]; recvCopy[i] ← temp; ENDLOOP; Driver.AddDeviceToChain[me]; END; ActivateDriver: PROCEDURE = BEGIN RS232C.SetLineType[channelHandle, byteSynchronous]; RS232C.SetParameter[ channelHandle, [correspondent[RS232CCorrespondents.xerox850]]]; RS232C.SetParameter[channelHandle, [syncChar[Sla.syn]]]; RS232C.SetParameter[channelHandle, [dataTerminalReady[TRUE]]]; RS232C.SetParameter[channelHandle, [requestToSend[TRUE]]]; IF FALSE THEN RS232C.SetLineType[channelHandle, bitSynchronous]; Process.Detach[FORK Watcher[]]; Process.Detach[FORK Receiver[]]; Process.Detach[FORK Sender[]]; END; DeactivateDriver, KillDriver: PROCEDURE = BEGIN ERROR; END; Decapsulate: PROCEDURE [b: Buffer.Buffer] RETURNS [type: Buffer.Type] = BEGIN bytes: CARDINAL ← 2*b.driver.length; IF bytes < DriverTypes.slaEncapsulationBytes THEN GOTO Rejected; bytes ← bytes - DriverTypes.slaEncapsulationBytes; SELECT b.encapsulation.slaType FROM ns => BEGIN IF bytes < b.ns.pktLength THEN GOTO Rejected; type ← ns; END; pup => BEGIN IF bytes < b.pup.pupLength THEN GOTO Rejected; type ← pup; END; routing => BEGIN ProcessRoutingPacket[b]; Driver.ReturnFreeBuffer[b]; type ← processed; END; ENDCASE => GOTO Rejected; EXITS Rejected => BEGIN type ← rejected; info.garbagePackets ← info.garbagePackets + 1; END; END; SendPup: ENTRY PROCEDURE [b: Buffer.PupBuffer, host: PupTypes.PupHostID] = BEGIN IF info.state # up AND info.state # loopedBack THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END; IF packetsOnSendQueue > 15 THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END; b.encapsulation ← [ sla[ slaSpare1: 0, slaSpare2: 0, slaSpare3: 0, slaHi: FALSE, slaBroadcast: FALSE, slaTimeQueued: 0, slaSourceLine: 0, slaDestHost: 0, slaType: pup]]; b.driver.length ← (b.pup.pupLength + 1 + DriverTypes.slaEncapsulationBytes)/2; SendBufferInternal[b]; END; SendNS: ENTRY PROCEDURE [b: Buffer.NSBuffer, host: System.HostNumber] = BEGIN IF info.state # up AND info.state # loopedBack THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END; IF packetsOnSendQueue > 15 THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END; b.encapsulation ← [ sla[ slaSpare1: 0, slaSpare2: 0, slaSpare3: 0, slaHi: FALSE, slaBroadcast: FALSE, slaTimeQueued: 0, slaSourceLine: 0, slaDestHost: 0, slaType: ns]]; b.driver.length ← (b.ns.pktLength + 1 + DriverTypes.slaEncapsulationBytes)/2; SendBufferInternal[b]; END; SendRawBuffer: ENTRY PROCEDURE [b: Buffer.Buffer] = BEGIN IF packetsOnSendQueue > 15 THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END; SendBufferInternal[b]; END; ForwardPup: ENTRY PROCEDURE [b: Buffer.PupBuffer, host: PupTypes.PupHostID] RETURNS [PupTypes.PupErrorCode] = BEGIN IF info.state # up AND info.state # loopedBack THEN RETURN[cantGetTherePupErrorCode]; IF packetsOnSendQueue > 10 THEN RETURN[gatewayResourceLimitsPupErrorCode]; b.encapsulation ← [ sla[ slaSpare1: 0, slaSpare2: 0, slaSpare3: 0, slaHi: FALSE, slaBroadcast: FALSE, slaTimeQueued: 0, slaSourceLine: 0, slaDestHost: 0, slaType: pup]]; b.driver.length ← (b.pup.pupLength + 1 + DriverTypes.slaEncapsulationBytes)/2; SendBufferInternal[b]; RETURN[noErrorPupErrorCode]; END; ForwardNS: ENTRY PROCEDURE [b: Buffer.PupBuffer, host: System.HostNumber] = BEGIN IF info.state # up AND info.state # loopedBack THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END; IF packetsOnSendQueue > 10 THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END; b.encapsulation ← [ sla[ slaSpare1: 0, slaSpare2: 0, slaSpare3: 0, slaHi: FALSE, slaBroadcast: FALSE, slaTimeQueued: 0, slaSourceLine: 0, slaDestHost: 0, slaType: ns]]; b.driver.length ← (b.ns.pktLength + 1 + DriverTypes.slaEncapsulationBytes)/2; SendBufferInternal[b]; END; SendBufferInternal: INTERNAL PROCEDURE [b: Buffer.Buffer] = BEGIN IF b.encapsulation.slaDestHost # 0 THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END; b.next ← NIL; IF headOutputBuffer = NIL THEN headOutputBuffer ← tailOutputBuffer ← b ELSE BEGIN tailOutputBuffer.next ← b; tailOutputBuffer ← b; END; packetsOnSendQueue ← packetsOnSendQueue + 1; NOTIFY packetToSend; END; Sender: PROCEDURE = BEGIN Process.SetPriority[ProcessPriorities.priorityIOHigh]; DO StartSending[]; SendIt[]; FinishSending[]; ENDLOOP; END; StartSending: ENTRY PROCEDURE = BEGIN UNTIL headOutputBuffer # NIL DO WAIT packetToSend; ENDLOOP; currentOutputBuffer ← headOutputBuffer; headOutputBuffer ← currentOutputBuffer.next; timeSendStarted ← System.GetClockPulses[]; -- Keep Watcher happy END; FinishSending: ENTRY PROCEDURE = BEGIN Driver.PutOnGlobalDoneQueue[currentOutputBuffer]; currentOutputBuffer ← NIL; packetsOnSendQueue ← packetsOnSendQueue - 1; END; SendIt: PROCEDURE = BEGIN b: Buffer.Buffer ← currentOutputBuffer; complHandle: RS232C.CompletionHandle; rec: RS232C.PhysicalRecord ← [ header: [NIL, 0, 0], body:, trailer: [NIL, 0, 0]]; to: CARDINAL ← 2; buffer: LONG POINTER TO PACKED ARRAY [0..0) OF Byte = LOOPHOLE[@b.encapsulation + DriverTypes.slaEncapsulationOffset]; xferstatus: RS232C.TransferStatus; IF b = NIL THEN ERROR; -- DLE doubling via software (barf) sendCopy[0] ← Sla.dle; sendCopy[1] ← Sla.stx; FOR from: CARDINAL IN [0..b.driver.length*2) DO -- even bytes byte: Byte ← buffer[from]; IF byte = Sla.dle THEN BEGIN sendCopy[to] ← Sla.dle; to ← to + 1; END; sendCopy[to] ← byte; to ← to + 1; ENDLOOP; sendCopy[to] ← Sla.dle; sendCopy[to + 1] ← Sla.etx; IF nonAltoEtx THEN sendCopy[to + 1] ← Sla.etx - 200B; -- ************************* to ← to + 2; IF to > copySize THEN ERROR; rec.body ← [blockPointer: sendCopy, startIndex: 0, stopIndexPlusOne: to]; complHandle ← RS232C.Put[channelHandle, @rec ! RS232C.ChannelSuspended => GOTO Zapped]; timeSendStarted ← System.GetClockPulses[]; [, xferstatus] ← RS232C.TransmitNow[channelHandle, complHandle]; IF xferstatus = aborted THEN GOTO Zapped; IF xferstatus = success THEN BEGIN info.packetsSent ← info.packetsSent + 1; info.bytesSent ← info.bytesSent + 2*b.driver.length; END ELSE info.sendErrors ← info.sendErrors + 1; EXITS Zapped => NULL; END; timeSendStarted: System.Pulses; routingInterval: System.Pulses = System.MicrosecondsToPulses[5000000]; routingTimeout: System.Pulses = [3*routingInterval]; Watcher: ENTRY PROCEDURE = BEGIN routeTime: System.Pulses ← System.GetClockPulses[]; watchit: CONDITION; Process.SetTimeout[@watchit, Process.MsecToTicks[500]]; Process.SetPriority[ProcessPriorities.priorityIOHigh]; DO WAIT watchit; -- should check for lost interrupts here if there is an easy way FlushDeadLines[]; IF System.GetClockPulses[] - routeTime > routingInterval THEN BEGIN SendRoutingPacket[]; routeTime ← [routeTime + routingInterval]; IF System.GetClockPulses[] - routeTime > routingInterval THEN -- don't thrash if buffers are hard to get routeTime ← System.GetClockPulses[]; END; ENDLOOP; END; -- 9600 baud is 1200 characters/sec. Allow 10% for slop and whatever to get 1000. -- 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 -- Bletch, 2400 baud has come back again /HGM, 25-Jan-82 20:41:02 pulsesPerByte: System.Pulses = System.MicrosecondsToPulses[4*1000*2]; FlushDeadLines: INTERNAL PROCEDURE = BEGIN b: Buffer.Buffer; stuck, timeout: BOOLEAN; t: System.Pulses ← System.GetClockPulses[]; THROUGH [0..1) DO b ← currentOutputBuffer; stuck ← (b # NIL AND ((t - timeSendStarted) > (Sla.overheadPerPacket + b.driver.length*2)*pulsesPerByte)); timeout ← (t - timeOfLastRT) > routingTimeout; IF stuck THEN BEGIN tick: CONDITION; Process.SetTimeout[@tick, 1]; RS232C.Suspend[channelHandle, output]; UNTIL packetsOnSendQueue = 0 DO WAIT tick; ENDLOOP; RS232C.Restart[channelHandle, output]; END; IF timeout OR stuck THEN BEGIN PurgeLineFromRT[]; IF info.state = up OR info.state = loopedBack THEN BEGIN info.deaths ← info.deaths + 1; IF timeout THEN info.timeout ← info.timeout + 1; IF stuck THEN info.stuck ← info.stuck + 1; END; info.state ← down; info.partner ← Sla.noPartner; END; ENDLOOP; END; Receiver: PROCEDURE = BEGIN rec: RS232C.PhysicalRecord ← [ header: [NIL, 0, 0], body:, trailer: [NIL, 0, 0]]; complArray: ARRAY [0..outstandingGets) OF RS232C.CompletionHandle; Process.SetPriority[ProcessPriorities.priorityIOHigh]; FOR i: CARDINAL IN [0..outstandingGets) DO rec.body ← [recvCopy[i], 0, copySize]; complArray[i] ← RS232C.Get[channelHandle, @rec]; ENDLOOP; DO FOR i: CARDINAL IN [0..outstandingGets) DO transferStatus: RS232C.TransferStatus; bytes: CARDINAL; [bytes, transferStatus] ← RS232C.TransferWait[ channelHandle, complArray[i]]; SELECT TRUE FROM transferStatus = success AND bytes < 6 + 22 => BEGIN info.garbagePackets ← info.garbagePackets + 1; END; transferStatus = success => BEGIN b: Buffer.Buffer ← Driver.GetInputBuffer[]; to: CARDINAL ← 0; IF b # NIL THEN BEGIN maxBytes: CARDINAL ← 2*(b.driver.length - DriverTypes.slaEncapsulationOffset); finger: LONG POINTER TO PACKED ARRAY [0..0) OF Byte = recvCopy[i]; thumb: LONG POINTER TO PACKED ARRAY [0..0) OF Byte = LOOPHOLE[@b.encapsulation + DriverTypes.slaEncapsulationOffset]; dleSeen: BOOLEAN ← FALSE; FOR i: CARDINAL IN [2..bytes - 4) DO -- 2 for dle stx, 4 for dle etx crc crc byte: Byte ← finger[i]; IF dleSeen THEN dleSeen ← FALSE ELSE IF byte = Sla.dle THEN BEGIN dleSeen ← TRUE; LOOP; END; IF to > maxBytes THEN EXIT; -- Ignore rest of packet thumb[to] ← byte; to ← to + 1; ENDLOOP; b.driver ← [ length: to/2, iocb: NIL, faceStatus: unknown[100H] ]; b.network ← me; Driver.PutOnGlobalInputQueue[b]; END; info.packetsRecv ← info.packetsRecv + 1; info.bytesRecv ← info.bytesRecv + to; END ENDCASE => BEGIN info.recvErrors ← info.recvErrors + 1; SELECT transferStatus FROM dataLost => info.overrun ← info.overrun + 1; checksumError => info.crcErrors ← info.crcErrors + 1; deviceError => info.controlErrors ← info.controlErrors + 1; ENDCASE => NULL; END; IF transferStatus = deviceError THEN BEGIN globalStatus: RS232C.DeviceStatus ← RS232C.GetStatus[channelHandle]; IF globalStatus.dataLost THEN BEGIN RS232C.SetParameter[channelHandle, [latchBitClear[globalStatus]]]; END; END; rec.body ← [recvCopy[i], 0, copySize]; complArray[i] ← RS232C.Get[channelHandle, @rec]; ENDLOOP; ENDLOOP; -- of UNTIL END; SlaStats: PUBLIC PROCEDURE [b: Buffer.PupBuffer, network: Network] RETURNS [BOOLEAN] = BEGIN stateToExternalState: ARRAY Sla.State OF SlaFormat.LineState = [ down: down, loopedBack: loopedBack, halfUp: halfUp, up: up]; activeLines: CARDINAL = 1; sizeOfRoutingTable: CARDINAL = Sla.maxSlaHost*SIZE[Sla.RoutingTableEntry]; rte: LONG POINTER TO Sla.RoutingTableEntry; sse: LONG POINTER TO SlaFormat.SlaStatsEntry; b.pup.pupWords[0] ← SlaFormat.slaStatsReply; b.pup.pupWords[1] ← SlaFormat.slaVersion; b.pup.pupWords[2] ← Sla.maxSlaHost; rte ← LOOPHOLE[@b.pup.pupWords[3]]; FOR host: Sla.SlaHost IN Sla.SlaHost DO rte↑ ← routingTable[host]; rte ← rte + SIZE[Sla.RoutingTableEntry]; ENDLOOP; b.pup.pupWords[3 + sizeOfRoutingTable] ← activeLines - 1; sse ← LOOPHOLE[@b.pup.pupWords[4 + sizeOfRoutingTable]]; sse↑ ← [ packetsSent: PupWireFormat.MesaToBcplLongNumber[info.packetsSent], packetsRecv: PupWireFormat.MesaToBcplLongNumber[info.packetsRecv], bytesSent: PupWireFormat.MesaToBcplLongNumber[info.bytesSent], bytesRecv: PupWireFormat.MesaToBcplLongNumber[info.bytesRecv], syncErrors: Inline.LowHalf[info.syncErrors], badCrc: Inline.LowHalf[info.crcErrors], controlError: Inline.LowHalf[info.controlErrors], state: stateToExternalState[info.state]]; b.pup.pupLength ← 22 + 2*(4 + sizeOfRoutingTable + activeLines*SIZE[SlaFormat.SlaStatsEntry]); RETURN[TRUE]; END; timeOfLastRT: System.Pulses; ProcessRoutingPacket: ENTRY PROCEDURE [b: Buffer.Buffer] = BEGIN p: LONG POINTER TO Sla.RoutingTablePacket = LOOPHOLE[@b.rawWords]; timeOfLastRT ← System.GetClockPulses[]; PurgeLineFromRT[]; IF p.sourceHost = me.pupHostNumber THEN BEGIN info.state ← loopedBack; routingTable[me.pupHostNumber] ← [hops: 1, line: 0]; END ELSE BEGIN SELECT info.state FROM up => IF p.rt[me.pupHostNumber].hops # 1 THEN info.state ← halfUp; halfUp => IF p.rt[me.pupHostNumber].hops = 1 THEN info.state ← up; ENDCASE => info.state ← halfUp; IF info.state = up THEN FOR i: CARDINAL IN (0..MIN[p.numEntries, Sla.maxSlaHost]) DO newHop: CARDINAL = p.rt[i].hops + 1; rte: LONG POINTER TO Sla.RoutingTableEntry = @routingTable[i]; IF newHop <= rte.hops THEN BEGIN rte.hops ← IF newHop <= Sla.maxHops THEN newHop ELSE Sla.longHop; rte.line ← 0; END; ENDLOOP; END; info.partner ← p.sourceHost; END; PurgeLineFromRT: PROCEDURE = BEGIN FOR host: Sla.SlaHost IN Sla.SlaHost DO routingTable[host].hops ← Sla.longHop; ENDLOOP; END; SendRoutingPacket: INTERNAL PROCEDURE = BEGIN b: Buffer.Buffer; p: LONG POINTER TO Sla.RoutingTablePacket; UNTIL (b ← Buffer.GetBuffer[raw, pool, send, fullBuffer, FALSE]) # NIL DO pause: CONDITION; Process.SetTimeout[@pause, Process.MsecToTicks[100]]; WAIT pause; ENDLOOP; p ← LOOPHOLE[@b.rawWords]; p.sourceHost ← me.pupHostNumber; p.numEntries ← Sla.maxSlaHost; p.rt ← routingTable↑; p.rt[me.pupHostNumber].hops ← 0; IF info.state = halfUp AND info.partner IN Sla.SlaHost THEN p.rt[info.partner].hops ← 1; b.driver.length ← DriverTypes.slaEncapsulationBytes/2 + SIZE[Sla.RoutingTablePacket]; b.encapsulation ← [ sla[ slaSpare1:, slaSpare2:, slaSpare3:, slaHi: TRUE, slaBroadcast: TRUE, slaTimeQueued:, slaSourceLine: 0, slaDestHost: 0, slaType: routing]]; SendBufferInternal[b]; END; END.