-- File: EthernetOneDriver.mesa - last edit:
-- AOF 17-Feb-88 18:57:53
-- HGM 5-Oct-85 13:42:20
-- Copyright (C) 1983, 1984, 1985, 1988 by Xerox Corporation. All rights reserved.
-- NB: Allocates 10 buffers for Dicentras
DIRECTORY
Buffer USING [
AccessHandle, Buffer, DataBytesPerRawBuffer, dataLinkReserve,
DestroyPool, MakePool, Type],
CommFlags USING [doDebug, doStats, driverStats],
CommHeap USING [zone],
CommunicationInternal USING [NSPackageDestroy, NSPackageMake],
CommUtil USING [AllocateIocbs, FreeIocbs],
Driver USING [
Glitch, GetDeviceChain, GetInputBuffer, Device, DeviceObject,
AddDeviceToChain, PutOnGlobalDoneQueue, PutOnGlobalInputQueue,
ReturnFreeBuffer],
EthernetOneDriverTypes USING [Byte, Encapsulation],
Environment USING [Byte, bytesPerWord],
EthernetOneFace,
EthernetDriverFriends USING [EtherStatsInfo],
Inline USING [LowHalf, HighHalf, BITAND, LongCOPY],
PilotSwitches USING [noEthernetOne],
Process USING [
Abort, Detach, DisableTimeout, EnableAborts, GetPriority,
Priority, SecondsToTicks, SetPriority, SetTimeout, Yield],
ProcessPriorities USING [priorityIOHigh],
SpecialRuntime USING [AllocateNakedCondition, DeallocateNakedCondition],
Protocol1 USING [AddFamilyMember, Family, MatrixRecord],
NS3MBit USING [SetFaceStatus],
NSBuffer USING [Body],
PupDefs USING [Body, GetPupPackageUseCount, PupPackageDestroy, PupPackageMake],
PupRouterDefs USING [ContextObject],
PupTypes USING [allHosts, PupHostID],
RoutingTable USING [ContextObject],
Runtime USING [GlobalFrame, SelfDestruct],
Stats USING [StatBump, StatIncr, StatCounterIndex],
SpecialCommunication USING [],
SpecialSystem USING [HostNumber, GetProcessorID, NetworkNumber],
System USING [
broadcastHostNumber, GetClockPulses, HostNumber, MicrosecondsToPulses,
nullHostNumber, nullNetworkNumber, Pulses, switches--['<]--];
EthernetOneDriver: MONITOR
IMPORTS
Buffer, CommHeap, CommunicationInternal, CommUtil, Driver, Stats, EthernetOneFace,
Inline, NS3MBit, Process, Protocol1, PupDefs, Runtime,
SpecialRuntime, SpecialSystem, System
EXPORTS Buffer, NS3MBit, SpecialCommunication, System =
BEGIN
OPEN EthernetOneFace;
--EXPORTed TYPEs
Device: PUBLIC TYPE = Driver.Device;
HostNumber: PUBLIC TYPE = SpecialSystem.HostNumber;
NetworkNumber: PUBLIC TYPE = SpecialSystem.NetworkNumber;
ether: DeviceHandle;
myEar: CARDINAL; -- address am I actually listening for
getGarbage: BOOLEAN ← FALSE; --when true, we deliver any packet
setupEthernetOneDriver: PROC[etherDevice: DeviceHandle];
ethernetOneListenForHost: PROC[newHostNumber: CARDINAL];
ns: RoutingTable.ContextObject ← [
netNumber: System.nullNetworkNumber, network: @myDevice, stats: NIL];
pup: PupRouterDefs.ContextObject ← [protocol: NIL, network: @myDevice,
pupNetNumber: 0, pupHostNumber: 377B];
inputState: RECORD[
process: PROCESS,
mask: WORD,
access: Buffer.AccessHandle,
inWait: LONG POINTER TO CONDITION,
firstBuffer, lastBuffer: Buffer.Buffer,
queueAllowed, queueLength: CARDINAL,
timeLastRecv: LONG CARDINAL,
lastMissed: CARDINAL];
outputState: RECORD[
process: PROCESS,
mask: WORD,
outWait: LONG POINTER TO CONDITION,
firstBuffer, lastBuffer: Buffer.Buffer,
timeSendDone: LONG CARDINAL];
watcherState: RECORD[
pleaseStop: BOOLEAN,
process: PROCESS, timer: CONDITION];
--IOCBs
iocbState: RECORD[wait: CONDITION, first, free: FreeIocb];
FreeIocb: TYPE = LONG POINTER TO IocbObject;
IocbObject: TYPE = RECORD[
next: FreeIocb, rest: SEQUENCE COMPUTED CARDINAL OF WORD];
seconds: RECORD[one, fiveHalf, forty: LONG CARDINAL];
myDevice: Driver.DeviceObject ← [
matrix: DESCRIPTOR[NIL, 0],
sendRawBuffer: SendRawBuffer,
activateDriver: ActivateDriver,
deactivateDriver: DeactivateDriver, deleteDriver: DeleteDriver,
buffers:, device: ethernetOne, receiveBufferLen: 1500,
changeNumberOfInputBuffers: MaybeChangeNumberOfInputBuffers,
alive: TRUE, index: , next: NIL, lineSpeed: 3000, lineNumber: 0, stats: NIL];
LostAllIocbs: PUBLIC ERROR = CODE;
IOCBSizeIsZero: PUBLIC ERROR = CODE;
DriverNotActive: PUBLIC ERROR = CODE;
DriverAlreadyActive: PUBLIC ERROR = CODE;
IOCBMustBeInFirstMDS: PUBLIC ERROR = CODE;
IOCBMustBeQuadWordAligned: PUBLIC ERROR = CODE;
EthernetNetNumberScrambled: PUBLIC ERROR = CODE;
BufferMustBeAlmostQuadWordAligned: PUBLIC ERROR = CODE;
etherStats: EthernetDriverFriends.EtherStatsInfo;
bpw: NATURAL = Environment.bytesPerWord;
<<
The physical overhead is the number of bytes used by the MAC layer to
encapsulate a Network Protocol Data Unit. This physical overhead includes
EthernetOne encapsulation.
>>
physOverhead: NATURAL = bpw *
(SIZE[EthernetOneDriverTypes.Encapsulation] +
SIZE[WORD--EthernetOneDriverTypes.EthernetOneCRC--]);
encapBytes: CARDINAL = bpw * SIZE[EthernetOneDriverTypes.Encapsulation];
<<
This driver starts the reception of physical frame offset into the space
reserved by the buffer manager for the LLC. This is so frames received
using one LLC (eg. EthernetOne) can be encapsulated using another LLC (eg.
IEEE 802.2) without wiping out the FixedOverhead portion of the buffer.
errorCheck: NATURAL[22..1518] = Buffer.dataLinkReserve; -- this must be true
>>
offsetToDataUnit: NATURAL = Buffer.dataLinkReserve / bpw -
SIZE[EthernetOneDriverTypes.Encapsulation]; --words
--Hot Procedures
GetBufferAndIocb: INTERNAL PROC RETURNS[b: Buffer.Buffer] =
BEGIN
receiveLength: NATURAL = myDevice.receiveBufferLen + physOverhead;
SELECT TRUE FROM
(iocbState.free = NIL) =>
{IF CommFlags.doStats THEN Stats.StatIncr[statsIocbWait]; RETURN[NIL]};
((b ← Driver.GetInputBuffer[FALSE, receiveLength]) = NIL) => RETURN;
ENDCASE;
b.fo.driver.iocb ← iocbState.free;
iocbState.free ← iocbState.free.next;
b.fo.driver.length ← receiveLength;
b.fo.driver.faceStatus ← ethernet[pending];
END; --GetBufferAndIocb
FreeBufferAndIocb: INTERNAL PROC[
queueProc: PROC[b: Buffer.Buffer], b: Buffer.Buffer] =
BEGIN
iocb: FreeIocb ← b.fo.driver.iocb;
IF iocb # NIL THEN
BEGIN
iocb.next ← iocbState.free;
iocbState.free ← iocb;
b.fo.driver.iocb ← NIL;
END;
queueProc[b];
END; --FreeBufferAndIocb
InInterrupt: ENTRY PROC =
BEGIN
status: Status;
acceptBuffer: BOOLEAN;
this, new: Buffer.Buffer;
DO
ENABLE ABORTED => EXIT;
UNTIL (this ← inputState.firstBuffer) # NIL DO
WAIT inputState.inWait; ENDLOOP;
status ← GetStatus[this.fo.driver.iocb];
IF CommFlags.doStats AND status # pending THEN
Stats.StatIncr[statEtherInterruptDuringInterrupt];
UNTIL status # pending DO
WAIT inputState.inWait;
status ← GetStatus[this.fo.driver.iocb];
IF CommFlags.doStats AND status = pending
THEN Stats.StatIncr[statEtherMissingStatus];
ENDLOOP;
inputState.firstBuffer ← inputState.firstBuffer.fo.next;
IF status = ok THEN acceptBuffer ← TRUE
ELSE
BEGIN
acceptBuffer ← getGarbage; --we may be collecting garbage packets
IF CommFlags.driverStats
THEN etherStats.badRecvStatus ← etherStats.badRecvStatus + 1;
IF CommFlags.doStats THEN Stats.StatIncr[statEtherReceivedBadStatus];
IF CommFlags.doStats OR CommFlags.driverStats THEN
SELECT status FROM
packetTooLong =>
BEGIN
IF CommFlags.driverStats
THEN etherStats.packetTooLong ← etherStats.packetTooLong + 1;
IF CommFlags.doStats THEN Stats.StatIncr[statEtherReceivedTooLong];
END;
badAlignmentButOkCrc =>
BEGIN
IF CommFlags.driverStats THEN etherStats.badAlignmentButOkCrc ←
etherStats.badAlignmentButOkCrc + 1;
IF CommFlags.doStats THEN Stats.StatIncr[statEtherReceivedNot16];
END;
crc =>
BEGIN
IF CommFlags.driverStats THEN
etherStats.badCrc ← etherStats.badCrc + 1;
IF CommFlags.doStats THEN Stats.StatIncr[statEtherReceivedBadCRC];
END;
crcAndBadAlignment =>
BEGIN
IF CommFlags.driverStats THEN
etherStats.crcAndBadAlignment ← etherStats.crcAndBadAlignment+1;
IF CommFlags.doStats THEN
Stats.StatIncr[statEtherReceivedNot16BadCRC];
END;
overrun =>
BEGIN
IF CommFlags.driverStats THEN
etherStats.overrun ← etherStats.overrun + 1;
IF CommFlags.doStats THEN Stats.StatIncr[statEtherReceivedOverrun];
END;
ENDCASE;
END;
IF ~acceptBuffer THEN {new ← this; new.fo.next ← NIL} --recycle this buffer
ELSE
BEGIN
inputState.timeLastRecv ← this.fo.time ← System.GetClockPulses[];
this.fo.driver.length ← GetPacketLength[this.fo.driver.iocb];
NS3MBit.SetFaceStatus[this, status];
this.fo.network ← LONG[@myDevice];
IF CommFlags.driverStats THEN
BEGIN
etherStats.packetsRecv ← etherStats.packetsRecv + 1;
etherStats.wordsRecv ← etherStats.wordsRecv + this.fo.driver.length;
END;
IF CommFlags.doStats THEN
BEGIN
Stats.StatIncr[statEtherPacketsReceived];
Stats.StatBump[statEtherWordsReceived, this.fo.driver.length];
END;
FreeBufferAndIocb[Driver.PutOnGlobalInputQueue, this];
SELECT TRUE FROM
(inputState.queueLength > inputState.queueAllowed) =>
BEGIN
new ← NIL;
inputState.queueLength ← inputState.queueLength - 1;
NOTIFY watcherState.timer;
IF CommFlags.doStats THEN Stats.StatIncr[statEtherEmptyFreeQueue];
END;
((new ← GetBufferAndIocb[]) = NIL) =>
BEGIN --Rats, couldn't or didn't want a new buffer
inputState.queueLength ← inputState.queueLength - 1;
NOTIFY watcherState.timer;
IF CommFlags.doStats THEN Stats.StatIncr[statEtherEmptyFreeQueue];
END;
ENDCASE;
END; --acceptBuffer clause
--add new buffer to end of input chain
IF new # NIL THEN
BEGIN
NS3MBit.SetFaceStatus[new, pending];
QueueInput[
ether, (new.linkLayer.blockPointer + offsetToDataUnit),
(new.fo.driver.length / bpw) - offsetToDataUnit, new.fo.driver.iocb];
IF inputState.firstBuffer = NIL THEN inputState.firstBuffer ← new
ELSE inputState.lastBuffer.fo.next ← new;
inputState.lastBuffer ← new;
END;
ENDLOOP;
inputState.process ← NIL;
END;
OutInterrupt: ENTRY PROC =
BEGIN
b: Buffer.Buffer;
status: Status;
DO
ENABLE ABORTED => EXIT;
DO
--we compute the values each time around since the value of b can
--change if the watcher shoots down the output.
SELECT TRUE FROM
((b ← outputState.firstBuffer) = NIL) => NULL;
((status ← GetStatus[b.fo.driver.iocb]) # pending) => EXIT;
ENDCASE;
WAIT outputState.outWait;
ENDLOOP;
outputState.timeSendDone ← System.GetClockPulses[]; --still transmiting
NS3MBit.SetFaceStatus[b, status];
IF status = ok THEN
BEGIN
IF CommFlags.doStats OR CommFlags.driverStats THEN
BEGIN
tries: CARDINAL ← GetRetries[b.fo.driver.iocb];
statEtherSendsCollision1: Stats.StatCounterIndex =
statEtherSendsCollision1;
first: CARDINAL = LOOPHOLE[statEtherSendsCollision1];
IF CommFlags.driverStats THEN
BEGIN
etherStats.packetsSent ← etherStats.packetsSent + 1;
etherStats.wordsSent ← etherStats.wordsSent + b.fo.driver.length;
etherStats.loadTable[tries] ← etherStats.loadTable[tries] + 1;
END;
IF CommFlags.doStats AND tries # 0 THEN
Stats.StatIncr[LOOPHOLE[first + tries]];
END;
END
ELSE
BEGIN
IF CommFlags.driverStats THEN
etherStats.badSendStatus ← etherStats.badSendStatus + 1;
IF CommFlags.doStats THEN Stats.StatIncr[statEtherSendBadStatus];
IF CommFlags.doStats OR CommFlags.driverStats THEN
SELECT status FROM
tooManyCollisions =>
BEGIN
IF CommFlags.driverStats THEN
etherStats.tooManyCollisions ← etherStats.tooManyCollisions + 1;
IF CommFlags.doStats THEN
Stats.StatIncr[statEtherSendsCollisionLoadOverflow];
END;
underrun =>
BEGIN
IF CommFlags.driverStats THEN
etherStats.underrun ← etherStats.underrun + 1;
IF CommFlags.doStats THEN Stats.StatIncr[statEtherSendOverrun];
END;
ENDCASE;
END;
--We don't resend things that screwup
outputState.firstBuffer ← outputState.firstBuffer.fo.next;
FreeBufferAndIocb[Driver.PutOnGlobalDoneQueue, b];
ENDLOOP;
outputState.process ← NIL;
END;
Watcher: PROC =
BEGIN
CheckForIdleInput: ENTRY PROC = INLINE
BEGIN
IF (System.GetClockPulses[] - inputState.timeLastRecv) <
seconds.forty THEN RETURN;
IF CommFlags.doStats THEN Stats.StatIncr[statInputIdle];
IF CommFlags.driverStats THEN
etherStats.idleInput ← etherStats.idleInput + 1;
SmashCSBs[]; --this will leave output dangling
END; --CheckForIdleInput
CheckForStuckOutput: ENTRY PROC = INLINE
BEGIN
SELECT TRUE FROM
outputState.firstBuffer = NIL => NULL;
((System.GetClockPulses[] - outputState.timeSendDone) < seconds.fiveHalf) => RETURN;
ENDCASE =>
BEGIN
--This happens if the transciever is unplugged
TurnOff[ether];
UNTIL outputState.firstBuffer = NIL DO
b ← outputState.firstBuffer;
outputState.firstBuffer ← outputState.firstBuffer.fo.next;
FreeBufferAndIocb[Driver.PutOnGlobalDoneQueue, b];
IF CommFlags.doStats THEN Stats.StatIncr[statPacketsStuckInOutput];
IF CommFlags.driverStats THEN
etherStats.stuckOutput ← etherStats.stuckOutput + 1;
ENDLOOP;
SmashCSBs[];
END;
outputState.timeSendDone ← System.GetClockPulses[];
END; --CheckForStuckOutput
QueueInputBufferLocked: ENTRY PROC = INLINE
BEGIN ENABLE UNWIND => NULL;
SELECT TRUE FROM
(b = NIL) => WAIT watcherState.timer; --this is an alternate WAIT
((b.fo.driver.iocb ← iocbState.free) # NIL) =>
BEGIN
iocbState.free ← iocbState.free.next;
NS3MBit.SetFaceStatus[b, pending];
QueueInput[
ether, (b.linkLayer.blockPointer + offsetToDataUnit),
(b.fo.driver.length / bpw) - offsetToDataUnit, b.fo.driver.iocb];
IF inputState.firstBuffer = NIL THEN inputState.firstBuffer ← b
ELSE inputState.lastBuffer.fo.next ← b;
inputState.lastBuffer ← b;
inputState.queueLength ← inputState.queueLength + 1;
END;
ENDCASE => --the ENDCASE better be rare!
BEGIN
IF CommFlags.doStats THEN Stats.StatIncr[statsIocbWait];
Driver.ReturnFreeBuffer[b]; --give the buffer back (ARGH!!)
WAIT watcherState.timer; --this is an alternate WAIT
END;
END; --QueueInputBufferLocked
WaitForTimer: ENTRY PROC = INLINE
BEGIN ENABLE UNWIND => NULL;
WAIT watcherState.timer;
END;
b: Buffer.Buffer;
enterLoop: LONG CARDINAL;
inputState.lastMissed ← GetPacketsMissed[ether];
DO
ENABLE ABORTED => EXIT;
enterLoop ← System.GetClockPulses[];
--Check for lost interrupts
IF CheckBuffer[@inputState.firstBuffer] OR
CheckBuffer[@outputState.firstBuffer] THEN WatchCarefully[];
CheckForIdleInput[];
CheckForStuckOutput[];
WHILE (inputState.queueLength < inputState.queueAllowed) DO
--don't lock monitor and wait for a buffer
b ← Driver.GetInputBuffer[
TRUE, myDevice.receiveBufferLen + physOverhead];
QueueInputBufferLocked[]; --give it a chance to wait
IF b = NIL THEN EXIT; --but don't futz around forever
ENDLOOP;
IF CommFlags.doStats OR CommFlags.driverStats THEN
BEGIN
missed: CARDINAL ← GetPacketsMissed[ether];
lost: CARDINAL ← missed - inputState.lastMissed;
IF lost # 0 THEN
BEGIN
IF CommFlags.doStats
THEN Stats.StatBump[statEtherEmptyNoBuffer, lost];
IF CommFlags.driverStats
THEN etherStats.packetsMissed ← etherStats.packetsMissed + lost;
inputState.lastMissed ← missed;
END;
END;
IF (System.GetClockPulses[] - enterLoop) < seconds.one THEN
WaitForTimer[];
ENDLOOP;
END; --Watcher
CheckBuffer: ENTRY PROC[p: POINTER TO Buffer.Buffer]
RETURNS [trouble: BOOLEAN] =
BEGIN
b: Buffer.Buffer ← p↑;
IF b = NIL THEN RETURN[FALSE];
RETURN[(GetStatus[b.fo.driver.iocb] # pending)];
END;
WatchCarefully: PROC =
BEGIN
<<
In status # pending, an interrupt should have happened. Since the interrupt
routine is higher priority than we are, it should get processed before we
can see it. If we get here, an interrupt has probably been lost. It could
have been generated between the time we started decoding the instruction
and the time that the data is actually fetched. That is why we look several
times. Of course, if it is still not zero when we look again, it could be
a new interrupt that has just arrived.
Check for lost input interrupt.
>>
THROUGH [0..25) DO
IF ~CheckBuffer[@inputState.firstBuffer] THEN EXIT;
REPEAT FINISHED => WatcherNotify[];
ENDLOOP;
--Check for lost output interrupt
THROUGH [0..25) DO
IF ~CheckBuffer[@outputState.firstBuffer] THEN EXIT;
REPEAT FINISHED => WatcherNotify[];
ENDLOOP;
END;
WatcherNotify: ENTRY PROC =
BEGIN
IF CommFlags.doStats THEN Stats.StatIncr[statEtherLostInterrupts];
SmashCSBs[]; --this will leave output dangling
END;
DecapsulateNS: PROC [b: Buffer.Buffer] RETURNS [type: Buffer.Type] =
BEGIN
dll: LONG POINTER TO EthernetOneDriverTypes.Encapsulation;
bytes: CARDINAL ← b.fo.driver.length;
IF bytes < encapBytes THEN GOTO Rejected;
dll ← LOOPHOLE[b.linkLayer.blockPointer];
bytes ← bytes - encapBytes;
SELECT dll.ethernetOneType FROM
ns =>
BEGIN
ndu: NSBuffer.Body = LOOPHOLE[
dll + SIZE[EthernetOneDriverTypes.Encapsulation]];
IF bytes < ndu.pktLength THEN GOTO Rejected;
type ← ns;
b.highLayer ← [LOOPHOLE[dll], 0, bytes - encapBytes];
b.linkLayer.stopIndexPlusOne ← encapBytes;
END;
translation =>
BEGIN
ReceiveTranslate[b];
type ← orphan;
END;
ENDCASE => type ← vagrant;
EXITS Rejected =>
BEGIN
type ← orphan;
IF CommFlags.driverStats THEN Stats.StatIncr[statPacketsDiscarded];
END;
END;
decapsulatePup: PROC [b: Buffer.Buffer]
RETURNS [type: Buffer.Type] ← DecapsulatePup;
DecapsulatePup: PROC [b: Buffer.Buffer] RETURNS [type: Buffer.Type] =
BEGIN
dll: LONG POINTER TO EthernetOneDriverTypes.Encapsulation;
bytes: CARDINAL ← b.fo.driver.length;
IF bytes < encapBytes THEN GOTO Rejected;
bytes ← bytes - encapBytes;
dll ← LOOPHOLE[b.linkLayer.blockPointer];
SELECT dll.ethernetOneType FROM
pup =>
BEGIN
ndu: PupDefs.Body = LOOPHOLE[
dll + SIZE[EthernetOneDriverTypes.Encapsulation]];
IF bytes < ndu.pupLength THEN GOTO Rejected;
type ← pup;
b.highLayer ← [LOOPHOLE[dll], 0, bytes - encapBytes];
b.linkLayer.stopIndexPlusOne ← encapBytes;
END;
ENDCASE => type ← vagrant;
EXITS Rejected =>
BEGIN
type ← orphan;
IF CommFlags.driverStats THEN Stats.StatIncr[statPacketsDiscarded];
END;
END;
encapsulatePup: PROC [
b: Buffer.Buffer, immediate: LONG POINTER TO PupTypes.PupHostID]
← EncapsulatePup;
EncapsulatePup: PROC [
b: Buffer.Buffer, immediate: LONG POINTER TO PupTypes.PupHostID] =
BEGIN
body: PupDefs.Body = LOOPHOLE[b.highLayer.blockPointer];
dll: LONG POINTER TO EthernetOneDriverTypes.Encapsulation;
dll ← LOOPHOLE[body - SIZE[EthernetOneDriverTypes.Encapsulation]];
dll↑ ← [
ethernetOne[ethernetOneDest: immediate↑,
ethernetOneSource: pup.pupHostNumber, ethernetOneType: pup]];
b.fo.driver.length ← Inline.BITAND[
body.pupLength + encapBytes + bpw - 1, 177776B];
END;
EncapsulateNS: PROC [b: Buffer.Buffer, immediate: LONG POINTER TO HostNumber] =
BEGIN
foundIt: BOOLEAN;
ethernetAddr: PupTypes.PupHostID;
dll: LONG POINTER TO EthernetOneDriverTypes.Encapsulation;
body: NSBuffer.Body = LOOPHOLE[b.highLayer.blockPointer];
dll ← LOOPHOLE[body - SIZE[EthernetOneDriverTypes.Encapsulation]];
[foundIt, ethernetAddr] ← Translate[immediate↑];
IF foundIt THEN
BEGIN
dll↑ ← [
ethernetOne[ethernetOneDest: ethernetAddr,
ethernetOneSource: pup.pupHostNumber, ethernetOneType: ns]];
b.fo.driver.length ← Inline.BITAND[
body.pktLength + encapBytes + bpw - 1, 177776B];
END
ELSE
BEGIN
--If the translation doesn't work, the source will be set to a broadcast
--address. That is always wrong, so it should be detectable.
dll↑ ← [
ethernetOne[ethernetOneDest: , ethernetOneType: ,
ethernetOneSource: PupTypes.allHosts]];
END;
END;
SendRawBuffer: ENTRY PROC[b: Buffer.Buffer] =
BEGIN
dll: LONG POINTER TO EthernetOneDriverTypes.Encapsulation = LOOPHOLE[
b.linkLayer.blockPointer];
IF watcherState.pleaseStop THEN Driver.Glitch[DriverNotActive];
IF dll.ethernetOneSource = PupTypes.allHosts THEN
BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END;
IF ~hearSelf
AND
(dll.ethernetOneDest = pup.pupHostNumber
OR dll.ethernetOneDest = PupTypes.allHosts) THEN
BEGIN --sending to ourself, copy it over since we can't hear it
copy: Buffer.Buffer ← Driver.GetInputBuffer[
FALSE, Buffer.DataBytesPerRawBuffer[b]];
IF copy # NIL THEN
BEGIN
words: NATURAL = (b.fo.driver.length + bpw - 1) / bpw;
Inline.LongCOPY[
from: @b.linkLayer.blockPointer,
nwords: words, to: @copy.linkLayer.blockPointer];
copy.linkLayer.startIndex ← 0;
copy.linkLayer.stopIndexPlusOne ← b.fo.driver.length; --copy length
copy.fo.driver ← b.fo.driver;
copy.fo.time ← System.GetClockPulses[]; --pretty close to this time
copy.fo.network ← LONG[@myDevice]; --LONG because of Mokelumne compiler bug
IF CommFlags.doStats THEN Stats.StatIncr[statEtherPacketsLocal];
IF CommFlags.doStats THEN
Stats.StatBump[statEtherWordsLocal, b.fo.driver.length];
Driver.PutOnGlobalInputQueue[copy];
END
ELSE IF CommFlags.doStats THEN Stats.StatIncr[statEtherEmptyFreeQueue];
END;
SendBufferInternal[b];
END;
SendBufferInternal: INTERNAL PROC[b: Buffer.Buffer] =
BEGIN
NS3MBit.SetFaceStatus[b, pending];
b.fo.next ← NIL;
UNTIL (b.fo.driver.iocb ← iocbState.free) # NIL DO
IF CommFlags.doStats THEN Stats.StatIncr[statsIocbWait];
WAIT iocbState.wait;
ENDLOOP;
iocbState.free ← iocbState.free.next;
QueueOutput[
ether, @b.linkLayer.blockPointer, b.fo.driver.length / bpw,
b.fo.driver.iocb];
IF CommFlags.doStats THEN Stats.StatIncr[statEtherPacketsSent];
IF CommFlags.doStats THEN Stats.StatBump[statEtherWordsSent, b.fo.driver.length];
IF outputState.firstBuffer = NIL THEN
BEGIN
outputState.firstBuffer ← b;
outputState.timeSendDone ← System.GetClockPulses[];
END
ELSE outputState.lastBuffer.fo.next ← b;
outputState.lastBuffer ← b;
END;
--for changing the number of buffers while running
numberOfExtraBuffer: CARDINAL = 2;
--this should only be called from Boss
--No MONITOR PROTECTION here.
MaybeChangeNumberOfInputBuffers: PROC[increaseBuffers: BOOLEAN] =
BEGIN
IF increaseBuffers THEN
BEGIN
IF inputState.access = NIL THEN
inputState.access ← Buffer.MakePool[0, numberOfExtraBuffer];
IF inputState.queueAllowed < numberOfExtraBuffer THEN
inputState.queueAllowed ← myDevice.buffers ←
myDevice.buffers + numberOfExtraBuffer;
END
ELSE
BEGIN
IF inputState.access # NIL THEN
{Buffer.DestroyPool[inputState.access]; inputState.access ← NIL};
IF inputState.queueAllowed >= numberOfExtraBuffer THEN
inputState.queueAllowed ← myDevice.buffers ←
myDevice.buffers - numberOfExtraBuffer;
END;
END;
--COLD code, only used when turning things on+off
CreateDefaultEthernetOneDrivers: PUBLIC PROC RETURNS [BOOLEAN] =
BEGIN
deviceNumber: CARDINAL ← 0;
etherDevice: DeviceHandle ← GetNextDevice[nullDeviceHandle];
IF System.switches[PilotSwitches.noEthernetOne] = down THEN RETURN[FALSE];
IF etherDevice = nullDeviceHandle THEN RETURN[FALSE];
WHILE etherDevice # nullDeviceHandle DO
CreateAnEthernetOneDriver[etherDevice, deviceNumber];
etherDevice ← GetNextDevice[etherDevice];
deviceNumber ← deviceNumber + 1;
ENDLOOP;
RETURN[TRUE];
END;
CreateAnEthernetOneDriver: PROC [
etherDevice: DeviceHandle, deviceNumber: CARDINAL] =
BEGIN
IF deviceNumber # 0 THEN
BEGIN
him: LONG POINTER TO FRAME[EthernetOneDriver] ← NEW EthernetOneDriver;
START him; --so he'll initialize the procedure
him.setupEthernetOneDriver[etherDevice];
END
ELSE SetupEthernetOneDriver[etherDevice];
END;
firstTime: BOOL ← TRUE;
firstPool: Buffer.AccessHandle;
SetupEthernetOneDriver: PROC[etherDevice: DeviceHandle] =
BEGIN
ether ← etherDevice;
[, pup.pupHostNumber] ← GetEthernet1Address[ether];
watcherState.pleaseStop ← TRUE;
myDevice.buffers ← inputState.queueLength ← 10;
Driver.AddDeviceToChain[@myDevice];
IF TRUE THEN
BEGIN -- Startup BUGs in Boss
firstPool ← Buffer.MakePool[0, myDevice.buffers + numberOfExtraBuffer];
END;
IF CommFlags.doStats OR CommFlags.driverStats THEN
BEGIN
myDevice.stats ← @etherStats;
etherStats ← [];
END;
IF PupDefs.GetPupPackageUseCount[] > 0 THEN AdoptForPup[];
AdoptForNS[];
END;
AdoptForNS: PROC =
BEGIN
family: Protocol1.Family ← CommunicationInternal.NSPackageMake[];
matrix: Protocol1.MatrixRecord ← [ --AddFamilyMember copies fields
family: family, context: @ns,
encapsulator: EncapsulateNS,
decapsulator: DecapsulateNS];
Protocol1.AddFamilyMember[LONG[@myDevice], @matrix];
CommunicationInternal.NSPackageDestroy[]; -- Didn't really want to turn it on
END;
AdoptForPup: PROC =
BEGIN
family: Protocol1.Family ← PupDefs.PupPackageMake[];
matrix: Protocol1.MatrixRecord ← [ --AddFamilyMember copies fields
family: family, context: @pup,
encapsulator: EncapsulatePup,
decapsulator: DecapsulatePup];
Protocol1.AddFamilyMember[LONG[@myDevice], @matrix];
PupDefs.PupPackageDestroy[]; -- Beware: This undoes everything if the PupPackage wasn't on
END;
ActivateDriver: PROC =
BEGIN
b: Buffer.Buffer;
iocbs: CARDINAL = (myDevice.buffers + numberOfExtraBuffer) * 2;
size: CARDINAL = Inline.BITAND[(controlBlockSize + 3), 0FFFCH];
IF ~watcherState.pleaseStop THEN Driver.Glitch[DriverAlreadyActive];
seconds.forty ← System.MicrosecondsToPulses[40000000];
seconds.fiveHalf ← System.MicrosecondsToPulses[2500000];
seconds.one ← System.MicrosecondsToPulses[1000000];
[, pup.pupHostNumber] ← GetEthernet1Address[ether];
getGarbage ← watcherState.pleaseStop ← FALSE;
TurnOff[ether];
AddCleanup[ether];
inputState.access ← NIL;
inputState.queueLength ← 0;
inputState.queueAllowed ← myDevice.buffers;
inputState.firstBuffer ← inputState.lastBuffer ← NIL;
outputState.firstBuffer ← outputState.lastBuffer ← NIL;
myEar ← pup.pupHostNumber;
IF controlBlockSize = 0 THEN Driver.Glitch[IOCBSizeIsZero];
iocbState.free ← NIL;
Process.EnableAborts[@iocbState.wait];
Process.DisableTimeout[@iocbState.wait];
iocbState.first ← iocbState.free ← CommUtil.AllocateIocbs[size * iocbs];
FOR i: CARDINAL IN[0..iocbs - 1) DO
iocbState.first ← iocbState.first.next ←
iocbState.first + size;
REPEAT FINISHED =>
{iocbState.first.next ← NIL; iocbState.first ← iocbState.free};
ENDLOOP;
THROUGH [0..myDevice.buffers) DO
IF (b ← Driver.GetInputBuffer[
TRUE, myDevice.receiveBufferLen + physOverhead]) # NIL THEN
BEGIN
IF iocbState.free = NIL THEN Driver.Glitch[LostAllIocbs];
b.fo.driver.iocb ← iocbState.free;
iocbState.free ← iocbState.free.next;
inputState.queueLength ← inputState.queueLength + 1;
<<IF CommFlags.doDebug
AND Inline.BITAND[Inline.LowHalf[
@b.encapsulation + encapOffset], 3] # 0 THEN
Driver.Glitch[BufferMustBeAlmostQuadWordAligned];>>
IF CommFlags.doDebug
AND Inline.BITAND[Inline.LowHalf[b.fo.driver.iocb], 3] # 0 THEN
Driver.Glitch[IOCBMustBeQuadWordAligned];
IF CommFlags.doDebug AND Inline.HighHalf[b.fo.driver.iocb] # 0 THEN
Driver.Glitch[IOCBMustBeInFirstMDS];
NS3MBit.SetFaceStatus[b, pending];
IF inputState.firstBuffer = NIL THEN inputState.firstBuffer ← b;
IF inputState.lastBuffer # NIL THEN inputState.lastBuffer.fo.next ← b;
inputState.lastBuffer ← b;
END;
ENDLOOP;
[cv: inputState.inWait, mask: inputState.mask] ←
SpecialRuntime.AllocateNakedCondition[];
Process.DisableTimeout[inputState.inWait];
Process.EnableAborts[inputState.inWait];
[cv: outputState.outWait, mask: outputState.mask] ←
SpecialRuntime.AllocateNakedCondition[];
Process.DisableTimeout[outputState.outWait];
Process.EnableAborts[outputState.outWait];
SmashCSBs[];
BEGIN
priority: Process.Priority ← Process.GetPriority[];
Process.SetPriority[ProcessPriorities.priorityIOHigh];
inputState.process ← FORK InInterrupt[];
outputState.process ← FORK OutInterrupt[];
Process.SetPriority[priority];
END;
Process.EnableAborts[@watcherState.timer];
Process.SetTimeout[@watcherState.timer, Process.SecondsToTicks[1]];
watcherState.process ← FORK Watcher[];
CreateCache[];
myDevice.alive ← TRUE; --hope I ain't lying.
END;
SetEthernetOneListener: PUBLIC ENTRY PROC[
physicalOrder: CARDINAL, newHostNumber: CARDINAL]
RETURNS [success: BOOLEAN] =
BEGIN
him: LONG POINTER TO FRAME[EthernetOneDriver];
network: Device ← GetNthDeviceLikeMe[physicalOrder];
IF network = NIL THEN RETURN[FALSE];
him ← LOOPHOLE[Runtime.GlobalFrame[LOOPHOLE[network.sendRawBuffer]]];
him.ethernetOneListenForHost[newHostNumber];
RETURN[TRUE];
END;
EthernetOneListenForHost: PROC[newHostNumber: CARDINAL] =
BEGIN myEar ← newHostNumber; SmashCSBs[]; END;
GetNthDeviceLikeMe: PROC[physicalOrder: CARDINAL]
RETURNS [net: Device] =
BEGIN
i: CARDINAL ← 0;
net ← Driver.GetDeviceChain[];
WHILE net # NIL DO
IF net.device = myDevice.device THEN
IF (i ← i + 1) = physicalOrder THEN RETURN;
net ← net.next;
ENDLOOP;
END;
SetEthernetOneCollectGarbageToo: PUBLIC ENTRY PROC[
physicalOrder: CARDINAL, collectGarbage: BOOLEAN]
RETURNS [success: BOOLEAN] =
BEGIN
him: LONG POINTER TO FRAME[EthernetOneDriver];
network: Device ← GetNthDeviceLikeMe[physicalOrder];
IF network = NIL THEN RETURN[FALSE];
him ← LOOPHOLE[Runtime.GlobalFrame[LOOPHOLE[network.sendRawBuffer]]];
him.getGarbage ← collectGarbage;
RETURN[TRUE];
END;
SmashCSBs: PROC =
BEGIN
TurnOn[ether, myEar, inputState.mask, outputState.mask, LOOPHOLE[0]];
inputState.lastMissed ← GetPacketsMissed[ether];
FOR b: Buffer.Buffer ← inputState.firstBuffer, b.fo.next UNTIL b = NIL DO
QueueInput[
ether, LOOPHOLE[b.linkLayer.blockPointer + offsetToDataUnit],
(b.fo.driver.length / bpw) - offsetToDataUnit, b.fo.driver.iocb];
ENDLOOP;
inputState.timeLastRecv ← System.GetClockPulses[];
END;
DeleteDriver: PROC =
BEGIN
IF ether # GetNextDevice[nullDeviceHandle] THEN Runtime.SelfDestruct[];
END;
DeactivateDriver: PROC =
BEGIN
process: PROCESS;
b: Buffer.Buffer;
IF watcherState.pleaseStop THEN Driver.Glitch[DriverNotActive];
myDevice.alive ← FALSE; --stop all outgoing traffic
watcherState.pleaseStop ← TRUE;
process ← inputState.process;
UNTIL inputState.process = NIL DO
Process.EnableAborts[inputState.inWait];
Process.Abort[process];
REPEAT FINISHED => JOIN process;
ENDLOOP;
process ← outputState.process;
UNTIL outputState.process = NIL DO
Process.EnableAborts[outputState.outWait];
Process.Abort[process];
REPEAT FINISHED => JOIN process;
ENDLOOP;
Process.Abort[watcherState.process]; JOIN watcherState.process;
TurnOff[ether];
SpecialRuntime.DeallocateNakedCondition[inputState.inWait];
SpecialRuntime.DeallocateNakedCondition[outputState.outWait];
inputState.inWait ← outputState.outWait ← NIL;
MaybeChangeNumberOfInputBuffers[FALSE];
IF firstTime THEN
BEGIN
firstTime← FALSE;
Buffer.DestroyPool[firstPool];
END;
RemoveCleanup[ether];
UNTIL inputState.firstBuffer = NIL DO
b ← inputState.firstBuffer;
inputState.firstBuffer ← b.fo.next;
Driver.ReturnFreeBuffer[b];
ENDLOOP;
UNTIL outputState.firstBuffer = NIL DO
b ← outputState.firstBuffer;
outputState.firstBuffer ← b.fo.next;
Driver.PutOnGlobalDoneQueue[b];
ENDLOOP;
CommUtil.FreeIocbs[iocbState.first];
iocbState.free ← iocbState.first ← NIL;
DeleteCache[];
END;
AddressPair: TYPE = MACHINE DEPENDENT RECORD [
nsAddr: HostNumber, ethernet1Addr: PupTypes.PupHostID, filler: [0..377B]];
TranslationDataUnit: TYPE = LONG POINTER TO TranslationObject;
TranslationObject: TYPE = MACHINE DEPENDENT RECORD [
function: TranslationFunction, his, mine: AddressPair];
TranslationFunction: TYPE = MACHINE DEPENDENT {
(0), translationResponse(7070B), translationRequest(10101B), (WORD.LAST)};
CacheEntry: TYPE = LONG POINTER TO CacheObject;
CacheObject: TYPE = MACHINE DEPENDENT RECORD [
nextLink: CacheEntry,
addressPair: AddressPair,
tries: CARDINAL,
timeStamp: System.Pulses,
status: CacheStatus,
filler: [0..37777B]];
CacheStatus: TYPE = {new, pending, active, zombie};
--variables
translationRequest: CARDINAL = 10101B;
translationResponse: CARDINAL = 7070B;
cacheQueueHead: CacheEntry;
broadCastPairEntry: CacheEntry; --permanent
myAddressPairEntry: CacheEntry; --permanent
retryLimit: CARDINAL ← 10B;
depth: CARDINAL; --debugging
retryPulses: System.Pulses ← System.MicrosecondsToPulses[2000000]; --two seconds
deactivatePulses: System.Pulses ← System.MicrosecondsToPulses[180000000]; --three minutes
cacheEvent: CONDITION;
demonRunning: BOOLEAN;
etherHost: PupTypes.PupHostID;
nsHost: HostNumber ← SpecialSystem.GetProcessorID[];
CreateCache: PROC =
BEGIN
cacheQueueHead ← NIL;
etherHost ← [GetEthernet1Address[ether].host];
Process.SetTimeout[@cacheEvent, Process.SecondsToTicks[1]];
broadCastPairEntry ← AddAddressPair[[System.broadcastHostNumber, PupTypes.allHosts, 0]];
myAddressPairEntry ← AddAddressPair[[nsHost, etherHost, 0]];
demonRunning ← TRUE;
Process.Detach[FORK Demon[]];
END;
DeleteCache: PROC =
BEGIN
DeleteCacheLocked: ENTRY PROC = BEGIN NOTIFY cacheEvent; END;
e: CacheEntry;
WHILE demonRunning DO DeleteCacheLocked[]; Process.Yield[]; ENDLOOP;
--cleanup in case demon was never running
WHILE (cacheQueueHead # NIL) DO
e ← cacheQueueHead;
cacheQueueHead ← e.nextLink;
CommHeap.zone.FREE[@e];
ENDLOOP;
END;
FindEntry: INTERNAL PROC[nsAddr: HostNumber] RETURNS [entry: CacheEntry] =
BEGIN
IF CommFlags.doStats THEN depth ← 0;
entry ← cacheQueueHead;
WHILE entry # NIL DO
IF nsAddr = entry.addressPair.nsAddr THEN RETURN;
entry ← entry.nextLink;
IF CommFlags.doStats THEN depth ← depth + 1;
ENDLOOP;
END;
AddEntry: INTERNAL PROC [entry: CacheEntry] =
{entry.nextLink ← cacheQueueHead; cacheQueueHead ← entry};
RemoveEntry: INTERNAL PROC [entry: CacheEntry] =
BEGIN
e, pred: CacheEntry;
IF (pred ← cacheQueueHead) = entry THEN
BEGIN cacheQueueHead ← cacheQueueHead.nextLink; RETURN; END;
e ← pred.nextLink;
WHILE e # NIL DO
IF e = entry THEN BEGIN pred.nextLink ← entry.nextLink; RETURN; END;
pred ← e;
e ← pred.nextLink;
ENDLOOP;
ERROR;
END;
Translate: ENTRY PROC [nsAddr: HostNumber]
RETURNS [foundIt: BOOLEAN, ethernet1Addr: PupTypes.PupHostID] =
BEGIN
e: CacheEntry;
foundIt ← FALSE;
IF (e ← FindEntry[nsAddr]) # NIL THEN
BEGIN
IF e # cacheQueueHead THEN --put e at the head of the queue
BEGIN
IF CommFlags.doStats THEN Stats.StatBump[cacheDepth, depth];
RemoveEntry[e];
AddEntry[e];
END;
SELECT e.status FROM
active =>
BEGIN
foundIt ← TRUE;
ethernet1Addr ← e.addressPair.ethernet1Addr;
e.timeStamp ← System.GetClockPulses[];
END;
zombie =>
BEGIN
e.status ← new;
e.tries ← 0;
e.timeStamp ← System.GetClockPulses[];
NOTIFY cacheEvent;
END;
ENDCASE => NULL;
END --of found it
ELSE --entry not found, so add a new one
BEGIN
IF CommFlags.doStats THEN Stats.StatIncr[cacheFault];
e ← CommHeap.zone.NEW[CacheObject];
e.status ← new;
e.tries ← 0;
e.timeStamp ← System.GetClockPulses[];
e.addressPair ← [nsAddr: nsAddr, ethernet1Addr: [0], filler: 0];
AddEntry[e];
NOTIFY cacheEvent;
END;
END;
AddAddressPair: ENTRY PROC[aP: AddressPair] RETURNS [e: CacheEntry] =
BEGIN
e ← FindEntry[aP.nsAddr];
SELECT e FROM
NIL => {
e ← CommHeap.zone.NEW[CacheObject];
AddEntry[e]; };
broadCastPairEntry, myAddressPairEntry => RETURN;
ENDCASE;
e.addressPair ← aP;
e.status ← active;
e.timeStamp ← System.GetClockPulses[];
END;
DeallocateEntry: INTERNAL PROC[e: CacheEntry] =
BEGIN
IF (e = broadCastPairEntry) OR (e = myAddressPairEntry) THEN
e.timeStamp ← System.GetClockPulses[]
ELSE { RemoveEntry[e]; CommHeap.zone.FREE[@e]; };
END;
Demon: ENTRY PROC =
BEGIN
demonRunning ← TRUE;
Process.SetPriority[ProcessPriorities.priorityIOHigh];
UNTIL watcherState.pleaseStop DO
ENABLE ABORTED => EXIT;
pendingEntries: BOOLEAN ← FALSE;
e: CacheEntry;
WAIT cacheEvent;
e ← cacheQueueHead;
WHILE (e # NIL) DO
age: System.Pulses ← [System.GetClockPulses[] - e.timeStamp];
nextE: CacheEntry ← e.nextLink;
SELECT e.status FROM
active, zombie => { IF age > deactivatePulses THEN DeallocateEntry[e]};
pending =>
BEGIN
pendingEntries ← TRUE;
IF age > retryPulses THEN
BEGIN
e.tries ← e.tries + 1;
IF e.tries > retryLimit THEN
BEGIN
e.status ← zombie;
IF CommFlags.doStats THEN Stats.StatIncr[unsuccessfulTranslation];
END
ELSE
BEGIN
IF CommFlags.doStats THEN Stats.StatIncr[translationRetries];
SendRequest[e];
e.timeStamp ← System.GetClockPulses[];
END;
END;
END;
new =>
BEGIN
pendingEntries ← TRUE;
SendRequest[e];
e.status ← pending;
e.timeStamp ← System.GetClockPulses[];
END;
ENDCASE => ERROR;
e ← nextE;
ENDLOOP; --end of queue entries loop
IF pendingEntries THEN
Process.SetTimeout[@cacheEvent, Process.SecondsToTicks[1]]
ELSE Process.SetTimeout[@cacheEvent, Process.SecondsToTicks[60*5]];
ENDLOOP; --end of infinite loop
BEGIN
e, nextE: CacheEntry;
e ← cacheQueueHead;
cacheQueueHead ← myAddressPairEntry ← broadCastPairEntry ← NIL;
WHILE e # NIL DO
nextE ← e.nextLink; CommHeap.zone.FREE[@e]; e ← nextE; ENDLOOP;
END;
demonRunning ← FALSE;
END;
bytesPerTranslationResponse: NATURAL = physOverhead + bpw*SIZE[AddressPair];
bytesPerTranslationRequest: NATURAL = physOverhead + 2*bpw*SIZE[AddressPair];
SendRequest: INTERNAL PROC[e: CacheEntry] =
BEGIN
b: Buffer.Buffer;
IF (b ← Driver.GetInputBuffer[FALSE, bytesPerTranslationRequest]) # NIL THEN
BEGIN
dll: LONG POINTER TO EthernetOneDriverTypes.Encapsulation = LOOPHOLE[
b.linkLayer.blockPointer];
tdu: TranslationDataUnit = LOOPHOLE[
dll + SIZE[EthernetOneDriverTypes.Encapsulation]];
dll↑ ← [
ethernetOne[ethernetOneDest: PupTypes.allHosts,
ethernetOneSource: pup.pupHostNumber, ethernetOneType: translation]];
tdu↑ ← [function: translationRequest,
his: e.addressPair, mine: myAddressPairEntry.addressPair];
tdu.his.filler ← e.tries;
b.fo.driver.length ← bytesPerTranslationRequest; --longer than response
SendBufferInternal[b]; --okay, it should be ready
END;
END;
ReceiveTranslate: PROC [b: Buffer.Buffer] =
BEGIN
dll: LONG POINTER TO EthernetOneDriverTypes.Encapsulation = LOOPHOLE[
b.linkLayer.blockPointer];
tdu: TranslationDataUnit = LOOPHOLE[
dll + SIZE[EthernetOneDriverTypes.Encapsulation]];
SELECT tdu.function FROM
translationRequest =>
BEGIN
IF b.fo.driver.length < bytesPerTranslationRequest THEN RETURN;
IF tdu.his.nsAddr = nsHost THEN
BEGIN
a: Buffer.Buffer;
atdu: TranslationDataUnit;
adll: LONG POINTER TO EthernetOneDriverTypes.Encapsulation;
-- since the requester is probably going to talk to us,
-- add his address before we take a fault
[] ← AddAddressPair[tdu.mine];
tdu.mine.ethernet1Addr ← [pup.pupHostNumber];
IF CommFlags.doStats THEN Stats.StatIncr[requestsForMe];
a ← Driver.GetInputBuffer[FALSE, bytesPerTranslationRequest];
IF a = NIL THEN RETURN;
adll ← LOOPHOLE[a.linkLayer.blockPointer];
atdu ← LOOPHOLE[dll + SIZE[EthernetOneDriverTypes.Encapsulation]];
adll↑ ← [
ethernetOne[
ethernetOneDest: dll.ethernetOneSource,
ethernetOneSource: pup.pupHostNumber,
ethernetOneType: translation]];
atdu↑ ← [function: translationResponse, his: tdu.his, mine: ];
a.fo.driver.length ← bytesPerTranslationResponse; --shorter than req.
SendRawBuffer[a];
END;
END;
translationResponse =>
BEGIN
IF b.fo.driver.length < bytesPerTranslationResponse THEN RETURN;
IF dll.ethernetOneDest # etherHost THEN RETURN;
[] ← AddAddressPair[tdu.his];
END;
ENDCASE;
END;
getInfo: PROC [pup: PupTypes.PupHostID, network: Device]
RETURNS [ns: System.HostNumber] ← GetInfo;
GetInfo: PUBLIC ENTRY PROC [pup: PupTypes.PupHostID, network: Device]
RETURNS [ns: System.HostNumber] =
BEGIN
ns ← System.nullHostNumber;
IF network = @myDevice THEN
BEGIN
FOR entry: CacheEntry ← cacheQueueHead, entry.nextLink UNTIL entry = NIL DO
IF entry.status # active THEN LOOP;
IF pup # entry.addressPair.ethernet1Addr THEN LOOP;
ns ← entry.addressPair.nsAddr;
RETURN;
ENDLOOP;
RETURN;
END;
FOR finger: Device ← myDevice.next, finger.next UNTIL finger = NIL DO
him: LONG POINTER TO FRAME[EthernetOneDriver];
IF finger.device # ethernetOne THEN LOOP;
him ← LOOPHOLE[Runtime.GlobalFrame[LOOPHOLE[finger.sendRawBuffer]]];
ns ← him.getInfo[pup, network];
EXIT;
ENDLOOP;
END;
--* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
--End of Ethernet1 uglyness
--* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
--initialization
setupEthernetOneDriver ← SetupEthernetOneDriver; --for multi instances
ethernetOneListenForHost ← EthernetOneListenForHost;
END. --EthernetOneDriver