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