-- Copyright (C) 1984, 1985 by Xerox Corporation. All rights reserved.
-- PhoneDriver.mesa, HGM, 28-Sep-85 19:49:26
-- WIrish, 5-Dec-86 16:46:23, fixed counting packets on output queue
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;
b.next ← NIL;
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....