-- Copyright (C) 1984, 1985 by Xerox Corporation. All rights reserved. -- PhoneDriver.mesa, HGM, 28-Sep-85 19:49:26 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, NetworkNumber, nullHostNumber, nullNetworkNumber, Pulses, PulsesToMicroseconds], Buffer USING [AccessHandle, Buffer, MakePool, NSBuffer, PupBuffer, ReturnBuffer, Type], CommHeap USING [zone], CommunicationInternal USING [NSPackageMake], CommUtil USING [AllocateIocbs], Driver USING [ NetworkObject, Network, PutOnGlobalDoneQueue, AddDeviceToChain, GetInputBuffer, PutOnGlobalInputQueue], DriverTypes USING [DeviceType, Encapsulation], ForwarderDefs USING [DoErrorPup], NSTypes USING [ErrorCode], PhoneNetFriends USING [NetEnumerator, nullNetEnumerate, PhoneNetInfo], PhoneNetExtras USING [leaf, leafBytesSend, leafDupsFiltered, leafPktsSend, nsBytesSend, nsCongestion, nsDupsFiltered, nsTooGreedy, pupBytesSend, pupCongestion, pupDupsFiltered, pupTooGreedy], Protocol1 USING [AddFamilyMember, Family, MatrixRecord], PupDefs USING [PupPackageMake], PupRouterDefs USING [ContextObject], PupTypes USING [PupErrorCode, PupHostID, PupSocketID], RouterInternal USING [SendErrorPacket], RoutingTable USING [ContextObject], 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, CommHeap, CommunicationInternal, CommUtil, DESFace, Driver, ForwarderDefs, PhoneFace, Protocol1, PupDefs, RouterInternal EXPORTS Buffer, PhoneCreate, PhoneNetFriends = BEGIN -- EXPORTed TYPEs Network: PUBLIC TYPE = Driver.Network; congestionDiscard: NSTypes.ErrorCode = LOOPHOLE [1006B]; -- not yet in NSTypes def Byte: TYPE = Environment.Byte; doStats: BOOL ← TRUE; 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; 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, encrypt: BOOLEAN, dlion: BOOLEAN, key: DESFace.Key, pool: Buffer.AccessHandle, sendIocb: LONG POINTER, recvIocbs: LONG POINTER, recvIocbCounter: CARDINAL, procs: Procs, next: Info]; Procs: TYPE = RECORD [ encapsulateNS: PROCEDURE [Buffer.PupBuffer, LONG POINTER TO System.HostNumber], decapsulateNS: PROCEDURE [Buffer.Buffer] RETURNS [Buffer.Type], encapsulatePup: PROCEDURE [Buffer.PupBuffer, LONG POINTER TO PupTypes.PupHostID], decapsulatePup: PROCEDURE [Buffer.Buffer] RETURNS [Buffer.Type] ]; 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, activateDriver: ActivateDriver, deactivateDriver: KillDriver, matrix: DESCRIPTOR[NIL, 0], sendRawBuffer: SendRawBuffer, changeNumberOfInputBuffers: NIL, index: , device: phonenet, alive: FALSE, buffers: 10, -- This should be overkill lineSpeed: 0, lineNumber: 0, stats: @info.stats], 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: active, ourEntityClass: internetworkRouter, theirEntityClass: internetworkRouter, halfDuplexMode: unknown, hardwareStatsAvailable: TRUE, clientData: 0, clientHostNumber: System.nullHostNumber, sendQueueLength: 0, stats: ALL [0] ], encrypt: FALSE, dlion: FALSE, key: TRASH, pool: NIL, sendIocb: NIL, recvIocbs: NIL, recvIocbCounter: 0, procs: [ encapsulateNS: EncapsulateNS, decapsulateNS: DecapsulateNS, encapsulatePup: EncapsulatePup, decapsulatePup: DecapsulatePup ], 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) **************** SendRawBuffer: PROCEDURE [b: Buffer.PupBuffer] = BEGIN -- Actually queued/sent by Encapsulation routines END; EncapsulatePup: PROCEDURE [b: Buffer.PupBuffer, immediate: LONG POINTER TO PupTypes.PupHostID] = BEGIN errorText: STRING; 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; errorText ← QueuePup[b]; IF errorText # NIL THEN ForwarderDefs.DoErrorPup[b, gatewayResourceLimitsPupErrorCode, errorText]; END; QueuePup: ENTRY PROCEDURE [b: Buffer.PupBuffer] RETURNS [errorText: STRING] = BEGIN forwarding: BOOL ← b.pup.pupTransportControl > 0FH; -- 4 bits of hop, 4 spares high: BOOLEAN; dup, lic, ate, duplicate: BOOLEAN; friends, pups: CARDINAL ← 0; [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]; duplicate ← dup OR lic OR ate; IF ((b.pup.pupType = aData OR b.pup.pupType = data) AND duplicate 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["Duplicate BSP probe or aData"]; }; IF (b.pup.source.socket = leafSocket OR b.pup.dest.socket = leafSocket) AND duplicate THEN { StatIncr[@info.stats.stats[PhoneNetExtras.leafDupsFiltered]]; RETURN["Duplicate Leaf packet"]; }; IF friends > maxConnectionDepth THEN { StatIncr[@info.stats.stats[connTooGreedy]]; StatIncr[@info.stats.stats[PhoneNetExtras.pupTooGreedy]]; RETURN["Too many Pups from this connection"]; }; IF pups > maxQueueDepth OR (forwarding AND pups > maxForwarderDepth) THEN { StatIncr[@info.stats.stats[congestion]]; StatIncr[@info.stats.stats[PhoneNetExtras.pupCongestion]]; RETURN["Too many Pups on this queue"]; }; 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[NIL]; END; DecapsulatePup: 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 FALSE AND info.encrypt THEN -- It's already been decrypted by DecapsulateNS. -- BEWARE: This depends upon the order in which things get tried. 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; bytes ← bytes - phoneEncapsulationBytes; SELECT encapsulation.pnType FROM pupPhonePacket => BEGIN IF bytes < b.pup.pupLength THEN GOTO Rejected; type ← pup; info.stats.remoteHostNumber ← encapsulation.pnSrcID; END; ENDCASE => type ← vagrant; EXITS Rejected => BEGIN type ← orphan; StatIncr[@info.stats.stats[pktsRejected]]; END; END; EncapsulateNS: PROCEDURE [b: Buffer.PupBuffer, immediate: LONG POINTER TO System.HostNumber] = BEGIN errorText: STRING; 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; errorText ← QueueNS[b]; IF errorText # NIL THEN RouterInternal.SendErrorPacket[b, congestionDiscard, errorText.length]; END; QueueNS: ENTRY PROCEDURE [b: Buffer.PupBuffer] RETURNS [errorText: STRING] = BEGIN forwarding: BOOL ← b.ns.transportControl.hopCount > 0; high: BOOLEAN; dup, lic, ate: BOOLEAN; friends, ns: CARDINAL ← 0; [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 StatIncr[@info.stats.stats[PhoneNetExtras.nsDupsFiltered]]; RETURN["Duplicate packet filtered"]; END; IF friends > maxConnectionDepth THEN BEGIN StatIncr[@info.stats.stats[connTooGreedy]]; StatIncr[@info.stats.stats[PhoneNetExtras.nsTooGreedy]]; RETURN["Connection too greedy"]; END; IF ns > maxQueueDepth OR (forwarding AND ns > maxForwarderDepth) THEN BEGIN StatIncr[@info.stats.stats[congestion]]; StatIncr[@info.stats.stats[PhoneNetExtras.nsCongestion]]; RETURN["Too many NS packets on queue already"]; END; QueueOutputBuffer[b, high]; StatIncr[@info.stats.stats[nsSent]]; StatBump[@info.stats.stats[PhoneNetExtras.nsBytesSend], b.driver.length*2]; RETURN[NIL]; END; DecapsulateNS: 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; bytes ← bytes - phoneEncapsulationBytes; SELECT encapsulation.pnType FROM nsPhonePacket => BEGIN IF bytes < b.ns.pktLength THEN GOTO Rejected; type ← ns; END; ENDCASE => type ← vagrant; info.stats.remoteHostNumber ← encapsulation.pnSrcID; EXITS Rejected => BEGIN type ← orphan; StatIncr[@info.stats.stats[pktsRejected]]; END; 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 { pupHeaderWords: CARDINAL = 11; 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.NSBuffer ← 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 IF b.driver.iocb # NIL THEN ERROR; b.network ← LONG[@info.network]; info.packetsOnSendQueue ← info.packetsOnSendQueue + 1; b.next ← NIL; IF high THEN BEGIN b.driver.faceStatus ← unknown[101B]; IF info.firstHiSendBuffer = NIL THEN info.firstHiSendBuffer ← b ELSE info.lastHiSendBuffer.next ← b; info.lastHiSendBuffer ← b; END ELSE BEGIN b.driver.faceStatus ← unknown[102B]; 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[5]; b.driver.iocb ← line.sendIocb; b.driver.faceStatus ← unknown[103B]; 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[]; microseconds: LONG CARDINAL = ms*LONG[1000]; DO now: System.Pulses = System.GetClockPulses[]; IF System.PulsesToMicroseconds[[now-start]] > microseconds 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; line.currentSendBuffer ← NIL; IF b.driver.iocb # line.sendIocb THEN ERROR; b.driver.iocb ← NIL; b.driver.faceStatus ← unknown[104B]; 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]; 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]; line.network.lineSpeed ← line.stats.speed; 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; 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 { IF newBuffer.driver.iocb # NIL THEN ERROR; 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.iocb ← NIL; b.driver.faceStatus ← unknown[202B]; Driver.PutOnGlobalInputQueue[b]; line.timeLastRecv ← System.GetClockPulses[]; StatIncr[@line.stats.stats[pktsReceived]]; StatBump[@line.stats.stats[bytesReceived], bytes]; END; IF newBuffer.driver.iocb = line.recvIocbs THEN line.recvIocbCounter ← 0 ELSE { line.recvIocbCounter ← line.recvIocbCounter + 1; IF line.recvIocbCounter > line.network.buffers THEN ERROR; }; newBuffer.network ← @line.network; newBuffer.driver.faceStatus ← unknown[201B]; 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.lineNumber ← lineNumber; line.stats.lineNumber ← lineNumber; line.lineNumber ← lineNumber; 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]; AdoptForPup[line, host, net]; AdoptForNS[line, System.nullNetworkNumber]; END; AdoptForNS: PUBLIC PROC [info: Info, networkNumber: System.NetworkNumber] = BEGIN driver: Driver.Network ← @info.network; family: Protocol1.Family ← CommunicationInternal.NSPackageMake[]; matrix: Protocol1.MatrixRecord ← [ --AddFamilyMember copies fields family: family, context: , encapsulator: info.procs.encapsulateNS, decapsulator: info.procs.decapsulateNS]; matrix.context ← CommHeap.zone.NEW[RoutingTable.ContextObject ← [ netNumber: networkNumber, network: driver, stats: NIL]]; Protocol1.AddFamilyMember[driver, @matrix]; END; AdoptForPup: PROC [info: Info, pupHostNumber, pupNetNumber: CARDINAL] = BEGIN driver: Driver.Network ← @info.network; family: Protocol1.Family ← PupDefs.PupPackageMake[]; matrix: Protocol1.MatrixRecord ← [ --AddFamilyMember copies fields family: family, context: , encapsulator: info.procs.encapsulatePup, decapsulator: info.procs.decapsulatePup]; matrix.context ← CommHeap.zone.NEW[PupRouterDefs.ContextObject ← [ protocol: NIL, network: driver, pupNetNumber: pupNetNumber, pupHostNumber: pupHostNumber]]; Protocol1.AddFamilyMember[driver, @matrix]; matrix.family.stateChanged[driver, matrix.context, add]; -- Bug somewhere ########## 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; b.driver.faceStatus ← unknown[201B]; 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]; info.network.alive ← TRUE; 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 [stats: LONG POINTER] -- RETURNS [clientData: LONG POINTER TO PhoneNetFriends.PhoneNetInfo, lineNumber: CARDINAL] = -- BEGIN -- info: Info = LOOPHOLE[stats]; -- RETURN[@info.stats, info.lineNumber]; -- END; StatsPtrToStats: PUBLIC PROCEDURE [stats: LONG POINTER] RETURNS [PhoneNetFriends.PhoneNetInfo] = BEGIN p: LONG POINTER TO PhoneNetFriends.PhoneNetInfo = stats; RETURN[p↑]; 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....