-- Copyright (C) 1983, 1984, 1985 by Xerox Corporation. All rights reserved. -- SimplePhoneNetworkDriver.mesa, HGM, 10-Jun-85 22:17:10 -- SimplePhoneNetworkDriver.mesa, LSK, 9-Dec-84 18:36:28 -- Discarded Dialing and HalfDuplex, Copy Encapsulation from old DriverTypes -- From PhoneNetworkDriver.mesa (last edited by: AOF on: 16-Feb-83 9:40:08) DIRECTORY Environment USING [Block, Byte], Inline USING [LowHalf], Process USING [Detach, InitializeCondition, SetPriority, MsecToTicks], ProcessPriorities USING [priorityIOHigh], System USING [ GetClockPulses, GetGreenwichMeanTime, HostNumber, localHostNumber, MicrosecondsToPulses, nullHostNumber, nullNetworkNumber, Pulses, PulsesToMicroseconds], Buffer USING [AccessHandle, Buffer, MakePool, NSBuffer, PupBuffer, Type], CommFlags USING [driverStats], CommUtil USING [GetEthernetHostNumber], Driver USING [ NetworkObject, Network, PutOnGlobalDoneQueue, AddDeviceToChain, GetInputBuffer, PutOnGlobalInputQueue, ReturnFreeBuffer], DriverTypes USING [DeviceType, Encapsulation], PhoneCreate USING [], PhoneNetFriends USING [PhoneNetInfo, NetEnumerator, nullNetEnumerate], PhoneNetExtras USING [ leaf, leafBytesSend, leafDupsFiltered, leafPktsSend, nsBytesSend, nsCongestion, nsDupsFiltered, nsTooGreedy, pupBytesSend, pupCongestion, pupDupsFiltered, pupTooGreedy], PupTypes USING [PupErrorCode, PupHostID, PupSocketID], RS232C USING [ ChannelHandle, CompletionHandle, ChannelSuspended, CommParamObject, DeviceStatus, Get, GetStatus, PhysicalRecord, PhysicalRecordHandle, Put, Restart, SetParameter, Suspend, TransmitNow, TransferStatus, TransferWait]; SimplePhoneNetworkDriver: MONITOR IMPORTS Buffer, CommUtil, Driver, Inline, Process, RS232C, System EXPORTS Buffer, PhoneCreate, PhoneNetFriends = BEGIN Byte: TYPE = Environment.Byte; PhonePacketType: TYPE = MACHINE DEPENDENT{ pupPhonePacket(100B), nsPhonePacket(300B), -- turnAroundPhonePacket(301B), -- turnAroundMTSPhonePacket(302B), -- terminatePhonePacket(303B), -- terminateAckPhonePacket(304B), (LAST[Byte])}; Encapsulation: TYPE = MACHINE DEPENDENT RECORD [ SELECT OVERLAID DriverTypes.DeviceType FROM phonenet => [ framing0, framing1, framing2, framing3, framing4, framing5: Byte, recognition: Byte, -- 0 for auto recognition of OISCP vs SDLC/HDLC pnType: PhonePacketType, pnSrcID: System.HostNumber], ENDCASE]; phoneEncapsulationOffset: CARDINAL = 3; phoneEncapsulationBytes: CARDINAL = 8; encapsulationTrap: BOOLEAN [TRUE..TRUE] = (SIZE[Encapsulation] = SIZE[DriverTypes.Encapsulation]); -- EXPORTed TYPEs Network: PUBLIC TYPE = Driver.Network; InfoRecord: TYPE = RECORD [ channelHandle: RS232C.ChannelHandle, packetToSend: CONDITION, currentSendBuffer: Buffer.Buffer, firstHiSendBuffer, lastHiSendBuffer: Buffer.Buffer, firstLoSendBuffer, lastLoSendBuffer: Buffer.Buffer, packetsOnSendQueue: CARDINAL, timeSendStarted: System.Pulses, timeLastRecv: System.Pulses, bitsPerSecond: LONG CARDINAL, pool: Buffer.AccessHandle, network: Driver.NetworkObject, -- phone network object for Router stats: PhoneNetFriends.PhoneNetInfo, loophole: CrapForINRLoophole]; CrapForINRLoophole: TYPE = MONITORED RECORD [ clientData: LONG UNSPECIFIED, lineNumber: CARDINAL]; maxHiPriWords: CARDINAL = (2 + 50 + 22)/2; -- 50 data bytes in a pup maxQueueDepth: CARDINAL ← 15; -- For each of Pup, NS, ... maxForwarderDepth: CARDINAL ← 10; maxConnectionDepth: CARDINAL ← 5; leafSocket: PupTypes.PupSocketID = PhoneNetExtras.leaf; outstandingGets: CARDINAL = 2; info: InfoRecord; me: System.HostNumber ← System.localHostNumber; CreateSimplePhoneNet: PUBLIC PROCEDURE [ lineNumber: CARDINAL, chHandle: RS232C.ChannelHandle, commParams: RS232C.CommParamObject] = BEGIN Process.InitializeCondition[@info.packetToSend, Process.MsecToTicks[1000]]; IF CommFlags.driverStats THEN info.stats ← [ state: data, timeStarted: System.GetGreenwichMeanTime[], timeConnectionEstablished: System.GetGreenwichMeanTime[], remoteHostNumber: System.nullHostNumber, protocolVersion: old, lineNumber: NULL, duplexity: full, lineSpeed: bps9600, speed: 0, -- Observed, Kilobits/second negotiationMode: NULL, ourEntityClass: NULL, theirEntityClass: NULL, halfDuplexMode: NULL, hardwareStatsAvailable: TRUE, clientData: 0, clientHostNumber: NULL, sendQueueLength: 0, stats: ALL [0] ]; info.currentSendBuffer ← NIL; info.currentSendBuffer ← NIL; info.firstHiSendBuffer ← NIL; info.lastHiSendBuffer ← NIL; info.firstLoSendBuffer ← NIL; info.lastLoSendBuffer ← NIL; info.packetsOnSendQueue ← 0; info.bitsPerSecond ← 0; info.network ← [ next: NIL, decapsulateBuffer: Decapsulate, encapsulateAndSendPup: SendPup, encapsulateAndSendNS: SendNS, sendRawBuffer: SendRawBuffer, encapsulateAndForwardPup: ForwardPup, encapsulateAndForwardNS: ForwardNS, activateDriver: ActivateDriver, deactivateDriver: NIL, deleteDriver: NIL, changeNumberOfInputBuffers: NIL, index:, pupNetNumber: 0, pupHostNumber: CommUtil.GetEthernetHostNumber[], device: phonenet, alive: TRUE, buffers: 0, netNumber: System.nullNetworkNumber, pupStats: NIL, statsLevel0: @info.loophole, statsLevel1: NIL]; info.stats.lineNumber ← lineNumber; info.stats.remoteHostNumber ← System.nullHostNumber; info.channelHandle ← chHandle; info.loophole.clientData ← @info.stats; info.loophole.lineNumber ← lineNumber; RS232C.SetParameter[info.channelHandle, [dataTerminalReady[TRUE]]]; RS232C.SetParameter[info.channelHandle, [requestToSend[TRUE]]]; info.pool ← Buffer.MakePool[1, outstandingGets]; Driver.AddDeviceToChain[@info.network]; Process.Detach[FORK Receiver[]]; Process.Detach[FORK Sender[]]; Process.Detach[FORK Watcher[]]; END; -- 2400 baud is 300 bytes/sec. Allow 10% for slop and whatever to get 270. pulsesPerByte: System.Pulses = [(System.MicrosecondsToPulses[1000000]/270)+1]; recvIdleTimeout: System.Pulses = System.MicrosecondsToPulses[30000000]; Watcher: ENTRY PROCEDURE = BEGIN watch: CONDITION; tick: CONDITION; Process.InitializeCondition[@watch, Process.MsecToTicks[1000]]; Process.InitializeCondition[@tick, 1]; DO now: System.Pulses = System.GetClockPulses[]; b: Buffer.Buffer ← info.currentSendBuffer; IF b # NIL AND (now - info.timeSendStarted) > pulsesPerByte*b.driver.length*2 THEN BEGIN info.stats.remoteHostNumber ← System.nullHostNumber; RS232C.Suspend[info.channelHandle, output]; UNTIL info.packetsOnSendQueue = 0 DO WAIT tick; ENDLOOP; RS232C.Restart[info.channelHandle, output]; info.stats.remoteHostNumber ← System.nullHostNumber; END; WAIT watch; ENDLOOP; END; -- **************** Decapsulation **************** Decapsulate: PROCEDURE [b: Buffer.Buffer] RETURNS [type: Buffer.Type] = BEGIN encapsulation: LONG POINTER TO Encapsulation = LOOPHOLE[@b.encapsulation]; bytes: CARDINAL ← 2*b.driver.length; IF bytes < phoneEncapsulationBytes THEN GOTO Rejected; bytes ← bytes - phoneEncapsulationBytes; SELECT encapsulation.pnType FROM nsPhonePacket => BEGIN IF bytes < b.ns.pktLength THEN GOTO Rejected; type ← ns; END; pupPhonePacket => BEGIN IF bytes < b.pup.pupLength THEN GOTO Rejected; type ← pup; END; ENDCASE => GOTO Rejected; info.stats.remoteHostNumber ← encapsulation.pnSrcID; EXITS Rejected => BEGIN type ← rejected; IF CommFlags.driverStats THEN StatIncr[@info.stats.stats[pktsRejected]]; END; END; -- **************** Packet Transport / Sending **************** SendPup: ENTRY PROCEDURE [b: Buffer.PupBuffer, host: PupTypes.PupHostID] = BEGIN high: BOOLEAN; friends, pups: CARDINAL ← 0; encapsulation: LONG POINTER TO Encapsulation = LOOPHOLE[@b.encapsulation]; encapsulation↑ ← [ phonenet[ framing0: 0, framing1: 0, framing2: 0, framing3: 0, framing4: 0, framing5: 0, recognition: 0, pnType: pupPhonePacket, pnSrcID: me] ]; b.driver.length ← (b.pup.pupLength + 1 + phoneEncapsulationBytes)/2; [friends, pups, ] ← CountFriends[b, info.firstLoSendBuffer, friends, pups]; high ← b.driver.length < maxHiPriWords AND (friends = 0); [friends, pups, ] ← CountFriends[b, info.firstHiSendBuffer, friends, pups]; IF friends > maxConnectionDepth THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; StatIncr[@info.stats.stats[connTooGreedy]]; StatIncr[@info.stats.stats[PhoneNetExtras.pupTooGreedy]]; RETURN; END; IF pups > maxQueueDepth THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; StatIncr[@info.stats.stats[congestion]]; StatIncr[@info.stats.stats[PhoneNetExtras.pupCongestion]]; RETURN; END; QueueOutputBuffer[b, high]; StatIncr[@info.stats.stats[pupSent]]; StatBump[@info.stats.stats[PhoneNetExtras.pupBytesSend], b.driver.length*2]; IF b.pup.source.socket = leafSocket OR b.pup.dest.socket = leafSocket THEN { StatIncr[@info.stats.stats[PhoneNetExtras.leafPktsSend]]; StatBump[@info.stats.stats[PhoneNetExtras.leafBytesSend], b.driver.length*2]; }; END; SendNS: ENTRY PROCEDURE [b: Buffer.NSBuffer, host: System.HostNumber] = BEGIN high: BOOLEAN; friends, ns: CARDINAL ← 0; encapsulation: LONG POINTER TO Encapsulation = LOOPHOLE[@b.encapsulation]; encapsulation↑ ← [ phonenet[ framing0: 0, framing1: 0, framing2: 0, framing3: 0, framing4: 0, framing5: 0, recognition: 0, pnType: nsPhonePacket, pnSrcID: me] ]; b.driver.length ← (b.ns.pktLength + 1 + phoneEncapsulationBytes)/2; [friends, ns, ] ← CountFriends[b, info.firstLoSendBuffer, friends, ns]; high ← b.driver.length < maxHiPriWords AND (friends = 0); [friends, ns, ] ← CountFriends[b, info.firstHiSendBuffer, friends, ns]; IF friends > maxConnectionDepth THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; StatIncr[@info.stats.stats[connTooGreedy]]; StatIncr[@info.stats.stats[PhoneNetExtras.nsTooGreedy]]; RETURN; END; IF ns > maxQueueDepth THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; StatIncr[@info.stats.stats[congestion]]; StatIncr[@info.stats.stats[PhoneNetExtras.nsCongestion]]; RETURN; END; QueueOutputBuffer[b, high]; StatIncr[@info.stats.stats[nsSent]]; StatBump[@info.stats.stats[PhoneNetExtras.nsBytesSend], b.driver.length*2]; END; SendRawBuffer: ENTRY PROCEDURE [b: Buffer.Buffer] = BEGIN IF info.packetsOnSendQueue > maxQueueDepth THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; StatIncr[@info.stats.stats[congestion]]; RETURN; END; QueueOutputBuffer[b, FALSE]; StatIncr[@info.stats.stats[rawSent]]; END; ForwardPup: ENTRY PROCEDURE [b: Buffer.PupBuffer, host: PupTypes.PupHostID] RETURNS [PupTypes.PupErrorCode] = BEGIN high: BOOLEAN; dup, lic, ate: BOOLEAN; friends, pups: CARDINAL ← 0; encapsulation: LONG POINTER TO Encapsulation = LOOPHOLE[@b.encapsulation]; encapsulation↑ ← [ phonenet[ framing0: 0, framing1: 0, framing2: 0, framing3: 0, framing4: 0, framing5: 0, recognition: 0, pnType: pupPhonePacket, pnSrcID: me] ]; b.driver.length ← (b.pup.pupLength + 1 + phoneEncapsulationBytes)/2; [friends, pups, dup] ← CountFriends[b, info.firstLoSendBuffer, friends, pups]; high ← b.driver.length < maxHiPriWords AND (friends = 0); [friends, pups, lic] ← CountFriends[b, info.firstHiSendBuffer, friends, pups]; [friends, pups, ate] ← CountFriends[b, info.currentSendBuffer, friends, pups]; IF ((b.pup.pupType = aData OR b.pup.pupType = data) AND (dup OR lic OR ate) OR (b.pup.pupType = aData AND friends # 0 AND b.pup.pupLength = 22)) THEN { -- Barf, what an ugly hack to put into a gateway -- Duplicate data/aData OR (empty aData (probe) when something on the queue) StatIncr[@info.stats.stats[PhoneNetExtras.pupDupsFiltered]]; RETURN[LOOPHOLE[10002]]; }; IF (b.pup.source.socket = leafSocket OR b.pup.dest.socket = leafSocket) AND (dup OR lic OR ate) THEN { StatIncr[@info.stats.stats[PhoneNetExtras.leafDupsFiltered]]; RETURN[LOOPHOLE[10002]]; }; IF friends > maxConnectionDepth THEN BEGIN StatIncr[@info.stats.stats[connTooGreedy]]; StatIncr[@info.stats.stats[PhoneNetExtras.pupTooGreedy]]; RETURN[gatewayResourceLimitsPupErrorCode]; END; IF pups > maxForwarderDepth THEN BEGIN StatIncr[@info.stats.stats[congestion]]; StatIncr[@info.stats.stats[PhoneNetExtras.pupCongestion]]; RETURN[gatewayResourceLimitsPupErrorCode]; END; QueueOutputBuffer[b, high]; StatIncr[@info.stats.stats[pupSent]]; StatBump[@info.stats.stats[PhoneNetExtras.pupBytesSend], b.driver.length*2]; IF b.pup.source.socket = leafSocket OR b.pup.dest.socket = leafSocket THEN { StatIncr[@info.stats.stats[PhoneNetExtras.leafPktsSend]]; StatBump[@info.stats.stats[PhoneNetExtras.leafBytesSend], b.driver.length*2]; }; RETURN[noErrorPupErrorCode]; END; ForwardNS: ENTRY PROCEDURE [b: Buffer.PupBuffer, host: System.HostNumber] = BEGIN high: BOOLEAN; dup, lic, ate: BOOLEAN; friends, ns: CARDINAL ← 0; encapsulation: LONG POINTER TO Encapsulation = LOOPHOLE[@b.encapsulation]; encapsulation↑ ← [ phonenet[ framing0: 0, framing1: 0, framing2: 0, framing3: 0, framing4: 0, framing5: 0, recognition: 0, pnType: nsPhonePacket, pnSrcID: me] ]; b.driver.length ← (b.ns.pktLength + 1 + phoneEncapsulationBytes)/2; [friends, ns, dup] ← CountFriends[b, info.firstLoSendBuffer, friends, ns]; high ← b.driver.length < maxHiPriWords AND (friends = 0); [friends, ns, lic] ← CountFriends[b, info.firstHiSendBuffer, friends, ns]; [friends, ns, ate] ← CountFriends[b, info.currentSendBuffer, friends, ns]; IF dup OR lic OR ate THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; StatIncr[@info.stats.stats[PhoneNetExtras.nsDupsFiltered]]; RETURN; END; IF friends > maxConnectionDepth THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; StatIncr[@info.stats.stats[connTooGreedy]]; StatIncr[@info.stats.stats[PhoneNetExtras.nsTooGreedy]]; RETURN; END; IF ns > maxForwarderDepth THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; StatIncr[@info.stats.stats[congestion]]; StatIncr[@info.stats.stats[PhoneNetExtras.nsCongestion]]; RETURN; END; QueueOutputBuffer[b, high]; StatIncr[@info.stats.stats[nsSent]]; StatBump[@info.stats.stats[PhoneNetExtras.nsBytesSend], b.driver.length*2]; END; CountFriends: INTERNAL PROCEDURE [ b: Buffer.Buffer, head: Buffer.Buffer, friends, neighbors: CARDINAL] RETURNS [newFriends, newNeighbors: CARDINAL, headerMatch: BOOLEAN] = BEGIN encapsulation: LONG POINTER TO Encapsulation = LOOPHOLE[@b.encapsulation]; headerMatch ← FALSE; SELECT encapsulation.pnType FROM pupPhonePacket => { FOR maybe: Buffer.PupBuffer ← head, maybe.next UNTIL maybe = NIL DO encap: LONG POINTER TO Encapsulation = LOOPHOLE[@maybe.encapsulation]; IF encap.pnType = pupPhonePacket THEN { neighbors ← neighbors + 1; IF b.pup.dest = maybe.pup.dest THEN { PupHeader: TYPE = ARRAY [0..11) OF WORD; x: LONG POINTER TO PupHeader ← LOOPHOLE[@b.pup.pupLength]; y: LONG POINTER TO PupHeader ← LOOPHOLE[@maybe.pup.pupLength]; IF x↑ = y↑ THEN headerMatch ← TRUE; friends ← friends + 1; }; }; ENDLOOP; }; nsPhonePacket => { FOR maybe: Buffer.PupBuffer ← head, maybe.next UNTIL maybe = NIL DO encap: LONG POINTER TO Encapsulation = LOOPHOLE[@maybe.encapsulation]; IF encap.pnType = nsPhonePacket THEN { neighbors ← neighbors + 1; IF b.ns.destination = maybe.ns.destination THEN { SELECT b.ns.packetType FROM sequencedPacket => { IF b.ns.sequenceNumber = maybe.ns.sequenceNumber THEN headerMatch ← TRUE; }; packetExchange => { IF b.ns.exchangeID = maybe.ns.exchangeID THEN headerMatch ← TRUE; }; ENDCASE => NULL; friends ← friends + 1; }; }; ENDLOOP; }; ENDCASE => NULL; -- You get what you deserve RETURN[friends, neighbors, headerMatch] END; QueueOutputBuffer: INTERNAL PROCEDURE [b: Buffer.Buffer, high: BOOLEAN] = BEGIN b.network ← LONG[@info.network]; info.packetsOnSendQueue ← info.packetsOnSendQueue + 1; b.next ← NIL; IF high THEN BEGIN IF info.firstHiSendBuffer = NIL THEN info.firstHiSendBuffer ← b ELSE info.lastHiSendBuffer.next ← b; info.lastHiSendBuffer ← b; END ELSE BEGIN IF info.firstLoSendBuffer = NIL THEN info.firstLoSendBuffer ← b ELSE info.lastLoSendBuffer.next ← b; info.lastLoSendBuffer ← b; END; NOTIFY info.packetToSend; END; Sender: PROCEDURE = BEGIN Process.SetPriority[ProcessPriorities.priorityIOHigh]; DO StartSending[]; SendIt[]; FinishSending[]; ENDLOOP; END; StartSending: ENTRY PROCEDURE = BEGIN b: Buffer.Buffer; UNTIL (info.packetsOnSendQueue # 0) DO WAIT info.packetToSend; ENDLOOP; SELECT TRUE FROM info.firstHiSendBuffer # NIL => BEGIN b ← info.firstHiSendBuffer; info.firstHiSendBuffer ← b.next; END; info.firstLoSendBuffer # NIL => BEGIN b ← info.firstLoSendBuffer; info.firstLoSendBuffer ← b.next; END; ENDCASE => RETURN; info.currentSendBuffer ← b; END; SendIt: PROCEDURE = BEGIN complHandle: RS232C.CompletionHandle; rec: RS232C.PhysicalRecord ← [header: [NIL, 0, 0], body:, trailer: [NIL, 0, 0]]; xferstatus: RS232C.TransferStatus; b: Buffer.Buffer ← info.currentSendBuffer; rec.body.blockPointer ← LOOPHOLE[@b.encapsulation + phoneEncapsulationOffset]; rec.body.startIndex ← 0; rec.body.stopIndexPlusOne ← b.driver.length*2; -- even bytes complHandle ← RS232C.Put[info.channelHandle, @rec ! RS232C.ChannelSuspended => GOTO Zapped]; info.timeSendStarted ← System.GetClockPulses[]; [, xferstatus] ← RS232C.TransmitNow[info.channelHandle, complHandle]; IF xferstatus = aborted THEN GOTO Zapped; BEGIN bits: CARDINAL ← 7 + 16*b.driver.length + 16 + 7; now: System.Pulses ← System.GetClockPulses[]; micro: LONG CARDINAL ← System.PulsesToMicroseconds[[now-info.timeSendStarted]]; -- ARHG!!! 4000 * 1000000 overflows a LONG CARDINAL bitsPerSecond: LONG CARDINAL ← ((100000*bits)/micro)*10; info.bitsPerSecond ← (63*info.bitsPerSecond + bitsPerSecond)/64; info.stats.speed ← Inline.LowHalf[info.bitsPerSecond/1000]; END; IF CommFlags.driverStats THEN IF xferstatus = success THEN BEGIN StatIncr[@info.stats.stats[pktsSent]]; StatBump[@info.stats.stats[bytesSent], b.driver.length*2]; END ELSE StatIncr[@info.stats.stats[sendErrorBadStatus]]; EXITS Zapped => StatIncr[@info.stats.stats[queueTooOld]]; END; FinishSending: ENTRY PROCEDURE = BEGIN Driver.PutOnGlobalDoneQueue[info.currentSendBuffer]; info.packetsOnSendQueue ← info.packetsOnSendQueue - 1; info.currentSendBuffer ← NIL; END; -- **************** Packet Transport / Receiving **************** Receiver: PROCEDURE = BEGIN recArray: ARRAY [0..outstandingGets) OF RS232C.PhysicalRecord ← ALL[[[NIL, 0, 0], [NIL, 0, 0], [NIL, 0, 0]]]; bufferArray: ARRAY [0..outstandingGets) OF Buffer.Buffer ← ALL[NIL]; complArray: ARRAY [0..outstandingGets) OF RS232C.CompletionHandle; bufCount: CARDINAL ← 0; nextBuf: Buffer.Buffer; Process.SetPriority[ProcessPriorities.priorityIOHigh]; info.timeLastRecv ← System.GetClockPulses[]; DO FOR i: CARDINAL IN [0..outstandingGets) DO nextBuf ← Driver.GetInputBuffer[]; IF bufferArray[i] # NIL THEN BEGIN okToDispose: BOOLEAN ← TRUE; IF nextBuf=NIL THEN BEGIN IF bufCount <= 1 THEN BEGIN okToDispose ← FALSE; nextBuf ← bufferArray[i]; -- we keep this buffer because we can't get another END ELSE BEGIN bufCount ← bufCount - 1; END; END; AwaitAndDisposeOfFrame[complArray[i], bufferArray[i], okToDispose]; END -- non-NIL bufferArray[i] ELSE BEGIN IF nextBuf # NIL THEN bufCount ← bufCount + 1; END; bufferArray[i] ← nextBuf; IF bufferArray[i] # NIL THEN BEGIN recHandle: RS232C.PhysicalRecordHandle ← @recArray[i]; b: Buffer.Buffer ← bufferArray[i]; recHandle.body ← [ LOOPHOLE[@b.encapsulation + phoneEncapsulationOffset], 0, (b.driver.length - phoneEncapsulationOffset)*2]; complArray[i] ← RS232C.Get[info.channelHandle, recHandle]; END; ENDLOOP; ENDLOOP; -- of UNTIL END; AwaitAndDisposeOfFrame: PROCEDURE [ complHandle: RS232C.CompletionHandle, b: Buffer.Buffer, okToDispose: BOOLEAN] = BEGIN transferStatus: RS232C.TransferStatus; globalStatus: RS232C.DeviceStatus; bytes: CARDINAL; checksum: CARDINAL = 2; [bytes, transferStatus] ← RS232C.TransferWait[info.channelHandle, complHandle]; IF transferStatus = success THEN BEGIN encapsulation: LONG POINTER TO Encapsulation = LOOPHOLE[@b.encapsulation]; info.timeLastRecv ← System.GetClockPulses[]; IF ~okToDispose THEN RETURN; b.driver.length ← (bytes - checksum)/2; b.network ← LONG[@info.network]; b.driver.faceStatus ← unknown[101H]; b.time ← System.GetClockPulses[]; --record time received Driver.PutOnGlobalInputQueue[b]; IF CommFlags.driverStats THEN BEGIN StatIncr[@info.stats.stats[pktsReceived]]; StatBump[@info.stats.stats[bytesReceived], bytes - checksum]; END; END ELSE BEGIN -- bad frame => free the buffer IF ~okToDispose THEN RETURN; Driver.ReturnFreeBuffer[b]; IF CommFlags.driverStats AND (transferStatus # aborted) THEN BEGIN statsPtr: POINTER TO LONG CARDINAL; statsPtr ← SELECT transferStatus FROM dataLost => @info.stats.stats[rcvErrorDataLost], checksumError => @info.stats.stats[rcvErrorCRC], frameTimeout => @info.stats.stats[rcvErrorFrameTimeout], deviceError => @info.stats.stats[rcvDeviceError], ENDCASE => @info.stats.stats[rcvErrorUnknown]; StatIncr[statsPtr]; END; IF transferStatus = deviceError THEN BEGIN globalStatus ← RS232C.GetStatus[info.channelHandle]; IF globalStatus.dataLost THEN -- clear the info lost latch bit BEGIN RS232C.SetParameter[info.channelHandle, [latchBitClear[globalStatus]]]; IF CommFlags.driverStats THEN StatIncr[@info.stats.stats[rcvErrorNoGet]]; END; END; END; END; -- **************** Driver Management (by Router) **************** ActivateDriver: PROCEDURE = BEGIN END; -- **************** Statistics **************** StatIncr: PROCEDURE [counter: POINTER TO LONG CARDINAL] = INLINE BEGIN counter↑ ← (counter↑ + 1); END; StatBump: PROCEDURE [ counter: POINTER TO LONG CARDINAL, bumpAmount: CARDINAL] = INLINE BEGIN counter↑ ← (counter↑ + bumpAmount); END; -- **************** Keep Kluger/INR happy **************** GetDriverInfo: PUBLIC PROCEDURE [lineNumber: CARDINAL] RETURNS [--found:-- BOOLEAN, --info:-- PhoneNetFriends.PhoneNetInfo] = BEGIN IF info.network.alive AND lineNumber = info.stats.lineNumber THEN RETURN [TRUE, info.stats]; RETURN[FALSE, ]; END; StatsPtrToInfo: PUBLIC PROCEDURE [statsLevel0: LONG POINTER] RETURNS [clientData: LONG UNSPECIFIED, lineNumber: CARDINAL] = -- statsLevel0 argument is from a phonenet network object BEGIN stats: LONG POINTER TO CrapForINRLoophole = LOOPHOLE[statsLevel0]; RETURN [stats.clientData, stats.lineNumber]; END; EnumerateDriverInfo: PUBLIC PROCEDURE [current: PhoneNetFriends.NetEnumerator] RETURNS [PhoneNetFriends.NetEnumerator, PhoneNetFriends.PhoneNetInfo] = BEGIN nonNullNetEnumerate: PhoneNetFriends.NetEnumerator = LONG[LOOPHOLE [1]]; IF (current = PhoneNetFriends.nullNetEnumerate) AND info.network.alive THEN RETURN [nonNullNetEnumerate, info.stats] ELSE RETURN [PhoneNetFriends.nullNetEnumerate, info.stats]; -- no more END; -- init info.network.alive ← FALSE; END....