-- Copyright (C) 1984, 1985 by Xerox Corporation. All rights reserved. -- PhoneDriver.mesa, HGM, 9-Apr-85 22:17:39 DIRECTORY Environment USING [Byte], Inline USING [BITXOR, LowHalf], Process USING [DisableTimeout, InitializeCondition, MsecToTicks, SetPriority, Yield], ProcessPriorities USING [priorityPageFaultHigh], -- Below Ethernet Drivers SpecialRuntime USING [AllocateNakedCondition], String USING [EqualString], System USING [ GetClockPulses, GetGreenwichMeanTime, HostNumber, localHostNumber, MicrosecondsToPulses, nullHostNumber, nullNetworkNumber, Pulses, PulsesToMicroseconds], Buffer USING [AccessHandle, Buffer, MakePool, NSBuffer, PupBuffer, ReturnBuffer, Type], CommUtil USING [AllocateIocbs], Driver USING [ NetworkObject, Network, PutOnGlobalDoneQueue, AddDeviceToChain, GetInputBuffer, PutOnGlobalInputQueue], DriverTypes USING [DeviceType, Encapsulation], PhoneNetFriends USING [NetEnumerator, nullNetEnumerate, PhoneNetInfo], PhoneNetExtras USING [nsCongestion, nsTooGreedy, pupCongestion, pupTooGreedy], PupTypes USING [PupHostID, PupErrorCode], DESFace USING [ Block, CorrectParity, ECBDecrypt, ECBEncrypt, nullKey, Key], PhoneCreate USING [], PhoneFace USING [ controlBlockSize, GetPacketLength, GetPacketsMissed, GetRecvStatus, GetSendStatus, QueueInput, QueueOutput, ResetLine, Status, TurnOn]; PhoneDriver: MONITOR LOCKS lock↑ IMPORTS Inline, Process, SpecialRuntime, String, System, Buffer, CommUtil, DESFace, Driver, PhoneFace EXPORTS Buffer, PhoneCreate, PhoneNetFriends = BEGIN -- EXPORTed TYPEs Network: PUBLIC TYPE = Driver.Network; Byte: TYPE = Environment.Byte; 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; 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]); lock: POINTER TO MONITORLOCK ← @myLock; myLock: MONITORLOCK; Info: TYPE = LONG POINTER TO InfoRecord; InfoRecord: TYPE = RECORD [ lineNumber: CARDINAL, currentSendBuffer: Buffer.Buffer, -- Beware: this buffer may be encrypted firstHiSendBuffer, lastHiSendBuffer: Buffer.Buffer, firstLoSendBuffer, lastLoSendBuffer: Buffer.Buffer, packetsOnSendQueue: CARDINAL, timeSendStarted: System.Pulses, firstRecvBuffer, lastRecvBuffer: Buffer.Buffer, timeLastRecv: System.Pulses, missed: CARDINAL, bitsPerSecond: LONG CARDINAL, network: Driver.NetworkObject, stats: PhoneNetFriends.PhoneNetInfo, loophole: CrapForINRLoophole, encrypt: BOOLEAN, dlion: BOOLEAN, key: DESFace.Key, pool: Buffer.AccessHandle, sendIocb: LONG POINTER, recvIocbs: LONG POINTER, next: Info]; CrapForINRLoophole: TYPE = MONITORED RECORD [ clientData: LONG UNSPECIFIED, lineNumber: CARDINAL]; info: InfoRecord ← [ lineNumber: , currentSendBuffer: NIL, firstHiSendBuffer: NIL, lastHiSendBuffer: NIL, firstLoSendBuffer: NIL, lastLoSendBuffer: NIL, packetsOnSendQueue: 0, timeSendStarted: , firstRecvBuffer: NIL, lastRecvBuffer: NIL, timeLastRecv: , missed: 0, bitsPerSecond: 0, network: [ next: NIL, decapsulateBuffer: Decapsulate, encapsulateAndSendPup: SendPup, encapsulateAndSendNS: SendNS, sendRawBuffer: SendRawBuffer, encapsulateAndForwardPup: ForwardPup, encapsulateAndForwardNS: ForwardNS, activateDriver: ActivateDriver, deactivateDriver: KillDriver, deleteDriver: KillDriver, changeNumberOfInputBuffers: NIL, index: , netNumber: System.nullNetworkNumber, pupNetNumber: 0, pupHostNumber: 0, device: phonenet, alive: FALSE, buffers: 10, -- This should be overkill pupStats: NIL, statsLevel0: @info.loophole, statsLevel1: NIL], 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] ], loophole: [, @info.stats, ], encrypt: FALSE, dlion: FALSE, key: TRASH, pool: NIL, sendIocb: NIL, recvIocbs: NIL, next: NIL ]; me: System.HostNumber = System.localHostNumber; -- Shared by all lines interrupt, watcher: PROCESS ← NIL; pleaseStop: BOOLEAN ← FALSE; hardware: LONG POINTER TO CONDITION ← NIL; interruptBits: WORD ← 0; DuplicateLineNumber: ERROR = CODE; -- **************** Interface to Router(s) **************** 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; IF info.encrypt THEN BEGIN words: CARDINAL ← (b.driver.length+1)/2; blocks: CARDINAL = (words+SIZE[DESFace.Block]-1)/SIZE[DESFace.Block]; data: LONG POINTER = @b.encapsulation + phoneEncapsulationOffset; DESFace.ECBDecrypt[info.key, blocks, data, data]; END; 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; StatIncr[@info.stats.stats[pktsRejected]]; END; END; SendPup: ENTRY PROCEDURE [b: Buffer.PupBuffer, systemElem: 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]]; 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]]; 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[controlPktReceived]]; RETURN[LOOPHOLE[10002]]; }; IF friends > maxConnectionDepth THEN { StatIncr[@info.stats.stats[connTooGreedy]]; StatIncr[@info.stats.stats[PhoneNetExtras.pupTooGreedy]]; RETURN[gatewayResourceLimitsPupErrorCode]; }; IF pups > maxForwarderDepth THEN { StatIncr[@info.stats.stats[congestion]]; StatIncr[@info.stats.stats[PhoneNetExtras.pupCongestion]]; RETURN[gatewayResourceLimitsPupErrorCode]; }; QueueOutputBuffer[b, high]; StatIncr[@info.stats.stats[pupSent]]; RETURN[noErrorPupErrorCode]; END; ForwardNS: ENTRY PROCEDURE [b: Buffer.PupBuffer, 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 > 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]]; 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 => { maybe: Buffer.PupBuffer ← head; 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; }; }; maybe ← maybe.next; ENDLOOP; }; nsPhonePacket => { maybe: Buffer.NSBuffer ← head; 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 friends ← friends + 1; }; maybe ← maybe.next; 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; IF info.currentSendBuffer = NIL THEN StartSending[@info]; END; StartSending: INTERNAL PROCEDURE [line: Info] = BEGIN b: Buffer.Buffer; SELECT TRUE FROM line.firstHiSendBuffer # NIL => BEGIN b ← line.firstHiSendBuffer; line.firstHiSendBuffer ← b.next; END; line.firstLoSendBuffer # NIL => BEGIN b ← line.firstLoSendBuffer; line.firstLoSendBuffer ← b.next; END; ENDCASE => RETURN; line.currentSendBuffer ← b; IF line.encrypt THEN BEGIN words: CARDINAL ← b.driver.length; blocks: CARDINAL = (words+SIZE[DESFace.Block]-1)/SIZE[DESFace.Block]; data: LONG POINTER = @b.encapsulation + phoneEncapsulationOffset; DESFace.ECBEncrypt[line.key, blocks, data, data]; b.driver.length ← blocks*SIZE[DESFace.Block]; -- Round up to decrypt last block END; IF line.dlion THEN Wait[7]; b.driver.iocb ← line.sendIocb; PhoneFace.QueueOutput[ line.lineNumber, b.driver.iocb, @b.encapsulation + phoneEncapsulationOffset, 2*b.driver.length]; line.timeSendStarted ← System.GetClockPulses[]; END; Wait: PROCEDURE [ms: CARDINAL] = BEGIN start: System.Pulses = System.GetClockPulses[]; DO now: System.Pulses = System.GetClockPulses[]; IF System.PulsesToMicroseconds[[now-start]] > ms*1000 THEN EXIT; Process.Yield[]; -- Yetch. Pause is too crude ENDLOOP; END; FinishSending: PROCEDURE [line: Info, b: Buffer.Buffer] = BEGIN IF line.encrypt AND (b.allNets OR b.requeueProcedure # Buffer.ReturnBuffer) THEN BEGIN -- Oops. Untrash it words: CARDINAL ← b.driver.length; blocks: CARDINAL = (words+SIZE[DESFace.Block]-1)/SIZE[DESFace.Block]; data: LONG POINTER = @b.encapsulation + phoneEncapsulationOffset; DESFace.ECBDecrypt[line.key, blocks, data, data]; END; Driver.PutOnGlobalDoneQueue[b]; END; -- **************** Watcher to timeout stuck things **************** -- 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; Process.InitializeCondition[@watch, Process.MsecToTicks[1000]]; UNTIL pleaseStop DO WAIT watch; FOR line: Info ← @info, line.next UNTIL line = NIL DO flap: BOOLEAN ← FALSE; now: System.Pulses = System.GetClockPulses[]; b: Buffer.Buffer ← line.currentSendBuffer; IF PhoneFace.GetPacketsMissed[line.lineNumber] # line.missed THEN BEGIN oldMissed: CARDINAL ← line.missed; newMissed: CARDINAL ← PhoneFace.GetPacketsMissed[line.lineNumber]; line.missed ← newMissed; StatBump[@line.stats.stats[rcvErrorNoGet], (newMissed - oldMissed)]; END; IF b # NIL AND (now - line.timeSendStarted) > pulsesPerByte*b.driver.length*2 THEN BEGIN StatIncr[@line.stats.stats[queueTooOld]]; flap ← TRUE; END; IF (now - line.timeLastRecv) > recvIdleTimeout THEN BEGIN StatIncr[@line.stats.stats[tooLongSinceLastReceive]]; flap ← TRUE; END; IF flap THEN BEGIN line.stats.remoteHostNumber ← System.nullHostNumber; PhoneFace.ResetLine[line.lineNumber]; IF line.currentSendBuffer # NIL THEN BEGIN FinishSending[line, line.currentSendBuffer]; line.currentSendBuffer ← NIL; END; UNTIL line.firstHiSendBuffer = NIL DO b: Buffer.Buffer ← line.firstHiSendBuffer; line.firstHiSendBuffer ← b.next; Driver.PutOnGlobalDoneQueue[b]; ENDLOOP; UNTIL line.firstLoSendBuffer = NIL DO b: Buffer.Buffer ← line.firstLoSendBuffer; line.firstLoSendBuffer ← b.next; Driver.PutOnGlobalDoneQueue[b]; ENDLOOP; line.packetsOnSendQueue ← 0; FOR b: Buffer.Buffer ← line.firstRecvBuffer, b.next UNTIL b = NIL DO PhoneFace.QueueInput[ line.lineNumber, b.driver.iocb, @b.encapsulation + phoneEncapsulationOffset, 2*(b.driver.length - phoneEncapsulationOffset)]; ENDLOOP; line.timeLastRecv ← System.GetClockPulses[]; END; ENDLOOP; ENDLOOP; END; -- **************** Interrupt Processing **************** Interrupt: ENTRY PROCEDURE = BEGIN Process.SetPriority[ProcessPriorities.priorityPageFaultHigh]; UNTIL pleaseStop DO WAIT hardware; FOR line: Info ← @info, line.next UNTIL line = NIL DO DO status: PhoneFace.Status; b: Buffer.Buffer ← line.currentSendBuffer; IF b = NIL THEN EXIT; status ← PhoneFace.GetSendStatus[b.driver.iocb]; IF status = pending THEN EXIT; IF status = ok THEN BEGIN bits: CARDINAL ← 7 + 16*b.driver.length + 16 + 7; now: System.Pulses ← System.GetClockPulses[]; micro: LONG CARDINAL ← System.PulsesToMicroseconds[[now-line.timeSendStarted]]; -- ARHG!!! 4000 * 1000000 overflows a LONG CARDINAL bitsPerSecond: LONG CARDINAL = ((100000*bits)/micro)*10; line.bitsPerSecond ← (63*line.bitsPerSecond + bitsPerSecond)/64; line.stats.speed ← Inline.LowHalf[line.bitsPerSecond/1000]; StatIncr[@line.stats.stats[pktsSent]]; StatBump[@line.stats.stats[bytesSent], 2*b.driver.length]; END ELSE StatIncr[@line.stats.stats[sendErrorBadStatus]]; line.packetsOnSendQueue ← line.packetsOnSendQueue - 1; line.currentSendBuffer ← NIL; FinishSending[line, b]; StartSending[line]; ENDLOOP; WHILE line.firstRecvBuffer # NIL DO b: Buffer.Buffer ← line.firstRecvBuffer; status: PhoneFace.Status ← PhoneFace.GetRecvStatus[b.driver.iocb]; newBuffer: Buffer.Buffer ← NIL; IF status = pending THEN EXIT; line.firstRecvBuffer ← b.next; IF status # ok THEN BEGIN SELECT status FROM overrun => StatIncr[@line.stats.stats[rcvDeviceError]]; packetTooLong => StatIncr[@line.stats.stats[rcvErrorDataLost]]; crc => StatIncr[@line.stats.stats[rcvErrorCRC]]; ENDCASE => StatIncr[@line.stats.stats[rcvErrorUnknown]]; newBuffer ← b; b ← NIL; END ELSE BEGIN newBuffer ← Driver.GetInputBuffer[]; IF newBuffer = NIL THEN BEGIN -- Rats, recycle this packet StatIncr[@line.stats.stats[rcvErrorNoGet]]; newBuffer ← b; b ← NIL; END ELSE newBuffer.driver.iocb ← b.driver.iocb; END; IF b # NIL THEN BEGIN encapsulation: LONG POINTER TO Encapsulation = LOOPHOLE[@b.encapsulation]; bytes: CARDINAL ← PhoneFace.GetPacketLength[b.driver.iocb]; b.network ← @line.network; b.driver.length ← bytes; b.driver.faceStatus ← unknown[101H]; Driver.PutOnGlobalInputQueue[b]; line.timeLastRecv ← System.GetClockPulses[]; StatIncr[@line.stats.stats[pktsReceived]]; StatBump[@line.stats.stats[bytesReceived], bytes]; END; newBuffer.network ← @line.network; PhoneFace.QueueInput[ line.lineNumber, newBuffer.driver.iocb, @newBuffer.encapsulation + phoneEncapsulationOffset, 2*(newBuffer.driver.length - phoneEncapsulationOffset)]; IF line.firstRecvBuffer = NIL THEN line.firstRecvBuffer ← newBuffer ELSE line.lastRecvBuffer.next ← newBuffer; line.lastRecvBuffer ← newBuffer; newBuffer.next ← NIL; ENDLOOP; ENDLOOP; ENDLOOP; END; -- **************** Driver Management **************** CreatePhone: PUBLIC PROCEDURE [host, net, lineNumber: CARDINAL, password: LONG STRING] = BEGIN line: Info; IF watcher = NIL THEN BEGIN [cv: hardware, mask: interruptBits] ← SpecialRuntime.AllocateNakedCondition[]; Process.DisableTimeout[hardware]; PhoneFace.TurnOn[interruptBits]; -- Also gets it STARTed interrupt ← FORK Interrupt[]; watcher ← FORK Watcher[]; line ← @info; END ELSE BEGIN him: LONG POINTER TO FRAME[PhoneDriver] ← NEW PhoneDriver; START him; him.lock ← @myLock; line ← @him.info; FOR finger: Info ← @info, finger.next DO IF finger.lineNumber = lineNumber THEN ERROR DuplicateLineNumber; IF finger.next = NIL THEN BEGIN finger.next ← line; EXIT; END; ENDLOOP; END; line.network.alive ← TRUE; line.lineNumber ← lineNumber; line.stats.lineNumber ← lineNumber; line.loophole.lineNumber ← lineNumber; line.network.pupHostNumber ← host; line.network.pupNetNumber ← net; SELECT TRUE FROM password = NIL => NULL; String.EqualString[password, "DLion"L] => line.dlion ← TRUE; ENDCASE => BEGIN line.key ← MakeKey[password]; line.encrypt ← TRUE; END; line.pool ← Buffer.MakePool[0, line.network.buffers]; line.sendIocb ← CommUtil.AllocateIocbs[(line.network.buffers+1)*PhoneFace.controlBlockSize]; line.recvIocbs ← line.sendIocb + PhoneFace.controlBlockSize; Driver.AddDeviceToChain[@line.network]; END; -- Don't muck with this unless you are prepared to change both ends. -- This doesn't hide the string very well. MakeKey: PROCEDURE [source: LONG STRING] RETURNS [key: DESFace.Key] = BEGIN key ← DESFace.nullKey; FOR i: CARDINAL IN [0..source.length) DO j: CARDINAL = i MOD 8; c: CHARACTER = IF source[i] IN ['A..'Z] THEN 'a + (source[i] - 'A) ELSE source[i]; key[j].b ← Inline.BITXOR[LOOPHOLE[c, [0..127]], key[j].b]; ENDLOOP; DESFace.CorrectParity[@key]; END; ActivateDriver: ENTRY PROCEDURE = BEGIN tick: CONDITION; Process.InitializeCondition[@tick, 1]; PhoneFace.ResetLine[info.lineNumber]; FOR i: CARDINAL IN [0..info.network.buffers) DO b: Buffer.Buffer; UNTIL (b ← Driver.GetInputBuffer[FALSE]) # NIL DO WAIT tick; ENDLOOP; b.driver.iocb ← info.recvIocbs + i*PhoneFace.controlBlockSize; IF info.firstRecvBuffer = NIL THEN info.firstRecvBuffer ← b ELSE info.lastRecvBuffer.next ← b; PhoneFace.QueueInput[ info.lineNumber, b.driver.iocb, @b.encapsulation + phoneEncapsulationOffset, 2*(b.driver.length - phoneEncapsulationOffset)]; info.lastRecvBuffer ← b; b.next ← NIL; ENDLOOP; info.timeLastRecv ← System.GetClockPulses[]; info.missed ← PhoneFace.GetPacketsMissed[info.lineNumber]; END; KillDriver: PROCEDURE = BEGIN ERROR; END; -- **************** Statistics **************** StatIncr: PROCEDURE [counter: LONG POINTER TO LONG CARDINAL] = INLINE BEGIN counter↑ ← counter↑ + 1; END; StatBump: PROCEDURE [ counter: LONG 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 FOR finger: Info ← @info, finger.next UNTIL finger = NIL DO IF finger.network.alive AND finger.lineNumber = lineNumber THEN RETURN [TRUE, finger.stats]; ENDLOOP; 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 hisInfo: Info ← current; IF (current = PhoneNetFriends.nullNetEnumerate) AND info.network.alive THEN RETURN[@info, info.stats]; IF hisInfo.next = NIL THEN RETURN [PhoneNetFriends.nullNetEnumerate, info.stats]; RETURN [hisInfo.next, hisInfo.next.stats]; END; END....