DIRECTORY
Basics USING [BITAND, LongNumber],
BufferDefs,
CommFlags USING [doDebug, doStats],
DriverDefs USING [GetGiantVector, GiantVector, Glitch, GetDeviceChain, GetInputBuffer, Network, NetworkObject, AddDeviceToChain, PutOnGlobalDoneQueue, PutOnGlobalInputQueue],
DriverTypes USING [Byte, ethernetEncapsulationOffset, ethernetEncapsulationBytes],
EthernetOneFace,
EthernetDriverStats,
GermSwap USING [switches],
LoadState USING [SelfDestruct],
PrincOpsUtils USING [AllocateNakedCondition, DeallocateNakedCondition, GlobalFrame, HighHalf, LongCopy, LowHalf, LongZero],
Process USING [Detach, DisableTimeout, MsecToTicks, SecondsToTicks, SetPriority, SetTimeout, Ticks, Yield],
ProcessorFace USING [GetGreenwichMeanTime, GetClockPulses, GreenwichMeanTime, microsecondsPerHundredPulses],
PupTypes USING [allHosts, PupErrorCode, PupHostID],
RPCPrivate USING[ rpcSocket ],
StatsDefs USING [StatBump, StatIncr, StatCounterIndex],
SpecialCommunication USING [],
NSAddress
USING [broadcastHostNumber, ProcessorID, HostNumber, GetProcessorID, nullNetworkNumber];
EthernetOneDriver:
MONITOR
IMPORTS Basics, BufferDefs, DriverDefs, GermSwap, StatsDefs, EthernetOneFace, LoadState, PrincOpsUtils, Process, ProcessorFace, NSAddress
EXPORTS BufferDefs, DriverDefs, EthernetDriverStats, SpecialCommunication, RPCPrivate
SHARES BufferDefs, DriverTypes, NSAddress =
{ OPEN StatsDefs, BufferDefs, EthernetOneFace, NSAddress;
RPC: disable "doStats" for this module --
doStats: BOOL = CommFlags.doStats AND FALSE;
EXPORTed TYPEs
Network: PUBLIC TYPE = DriverDefs.Network;
ethernetEncapsulationOffset: CARDINAL = DriverTypes.ethernetEncapsulationOffset;
ethernetEncapsulationBytes: CARDINAL = DriverTypes.ethernetEncapsulationBytes;
ether: DeviceHandle;
globalStatePtr: GlobalStatePtr; -- Allocate space if needed
inProcess, outProcess: PROCESS;
inWait, outWait: LONG POINTER TO CONDITION ← NIL;
firstOutputBuffer, lastOutputBuffer: Buffer;
firstInputBuffer, lastInputBuffer: Buffer;
inInterruptMask, outInterruptMask: WORD;
watcherProcess: PROCESS;
pleaseStop: BOOL;
timer: CONDITION;
timeLastRecv, timeSendStarted: Pulses;
fiveSecondsOfPulses: Pulses;
fiveHalfSecondsOfPulses: Pulses;
oneSecondOfPulses: Pulses;
inputQueueLength: CARDINAL ← 1;
inputBuffersInQueue: CARDINAL;
myNetwork: DriverDefs.NetworkObject ← [
decapsulateBuffer: DecapsulateBuffer, encapsulatePup: EncapsulatePup,
encapsulateOis: EncapsulateOis, sendBuffer: SendBuffer,
forwardBuffer: ForwardBuffer, activateDriver: ActivateDriver,
deactivateDriver: DeactivateDriver, deleteDriver: DeleteDriver,
interrupt: InInterrupt,
changeNumberOfInputBuffers: MaybeChangeNumberOfInputBuffers, alive: TRUE,
speed: 3000, -- in kiloBits/sec
buffers:, spare:, device: ethernetOne, index:, netNumber: nullNetworkNumber,
hostNumber:, next: NIL, pupStats: NIL, stats: NIL];
myEar: CARDINAL;
what address am I listening for (verses myNetwork.hostNumber, my real address).
getGarbage: BOOL ← FALSE; -- when true, we deliver any packet
FunnyRetransmissionMask: PUBLIC ERROR = CODE;
MachineIDTooBigForEthernet: PUBLIC ERROR = CODE;
DriverNotActive: PUBLIC ERROR = CODE;
DriverAlreadyActive: PUBLIC ERROR = CODE;
EthernetNetNumberScrambled: PUBLIC ERROR = CODE;
CantMakImageWhileEtherentDriverIsActive: PUBLIC ERROR = CODE;
OnlyTwoDriversArePossible: PUBLIC ERROR = CODE;
BufferMustBeQuadWordAligned: PUBLIC ERROR = CODE;
IOCBMustBeQuadWordAligned: PUBLIC ERROR = CODE;
IOCBMustBeInFirstMDS: PUBLIC ERROR = CODE;
etherStats: EthernetDriverStats.EtherStats ← NIL;
Alignment:
PROC [lp:
LONG
POINTER]
RETURNS [
CARDINAL] =
INLINE {
RETURN [Basics.BITAND[LOOPHOLE[PrincOpsUtils.LowHalf[lp]], 3]];
};
InInterrupt:
ENTRY
PROC =
{
acceptBuffer: BOOL;
this, new: Buffer;
status: Status;
lastMissed, missed: CARDINAL ← GetPacketsMissed[ether];
Process.SetPriority[3];
DO
UNTIL pleaseStop OR (this ← firstInputBuffer) # NIL DO WAIT inWait; ENDLOOP;
IF pleaseStop THEN EXIT;
status ← GetStatus[this.iocbChain];
IF doStats
AND status # pending
THEN
StatIncr[statEtherInterruptDuringInterrupt];
UNTIL pleaseStop
OR status # pending
DO
WAIT inWait;
status ← GetStatus[this.iocbChain];
IF doStats AND status = pending THEN StatIncr[statEtherMissingStatus];
ENDLOOP;
IF doStats
AND (missed ← GetPacketsMissed[ether]) # lastMissed
THEN {
StatBump[statEtherEmptyNoBuffer, missed - lastMissed];
lastMissed ← missed;
};
IF pleaseStop THEN EXIT;
firstInputBuffer ← firstInputBuffer.next;
SELECT status
FROM
ok => acceptBuffer ← TRUE;
ENDCASE => {
etherStats.badRecvStatus ← etherStats.badRecvStatus + 1;
IF status = overrun THEN etherStats.overruns ← etherStats.overruns + 1;
acceptBuffer ← getGarbage; -- we may be collecting garbage packets
IF doStats
THEN
SELECT status
FROM
packetTooLong => StatIncr[statEtherReceivedTooLong];
badAlignmentButOkCrc => StatIncr[statEtherReceivedNot16];
crc => StatIncr[statEtherReceivedBadCRC];
crcAndBadAlignment => StatIncr[statEtherReceivedNot16BadCRC];
overrun => StatIncr[statEtherReceivedOverrun];
ENDCASE => StatIncr[statEtherReceivedBadStatus];
};
IF acceptBuffer
THEN {
allocateAnother: BOOL;
this.time ← timeLastRecv ← ProcessorFace.GetClockPulses[];
this.length ← GetPacketLength[this.iocbChain];
this.network ← LONG[@myNetwork]; -- LONG because of Mokelumne compiler bug
etherStats.packetsRecv ← etherStats.packetsRecv + 1;
etherStats.wordsRecv ← etherStats.wordsRecv + this.length;
IF doStats
THEN {
StatIncr[statEtherPacketsReceived];
StatBump[statEtherWordsReceived, this.length];
};
RPC: side door into RPC packet dispatching code --
IF EnqueueRecvd # NIL
AND this.encapsulation.ethernetOneType = pup
AND 2*this.length >= this.pupLength + ethernetEncapsulationBytes
AND this.dest.socket = RPCPrivate.rpcSocket
THEN {
this.next ← NIL -- assumed by SendBuffer --;
IF EnqueueRecvd[LOOPHOLE[this,PupBuffer]]
THEN { new ←
NIL;
allocateAnother ← (loanedToRPC+2 > inputBuffersInQueue) }
ELSE { new ← this; allocateAnother ←
FALSE;
new.length ← systemBufferSize };
}
ELSE { DriverDefs.PutOnGlobalInputQueue[this]; allocateAnother ← TRUE };
IF allocateAnother
THEN {
IF (new ← DriverDefs.GetInputBuffer[]) =
NIL
THEN {
Rats, couldn't get a new buffer
inputBuffersInQueue ← inputBuffersInQueue - 1;
NOTIFY timer;
etherStats.inputOff ← etherStats.inputOff + 1;
IF doStats THEN StatIncr[statEtherEmptyFreeQueue];
}; -- cant get new input buffer clause
}
ELSE
-- we may have loaned the buffer to RPC; don't replace it --
IF new = NIL THEN loanedToRPC ← loanedToRPC + 1;
} -- accept buffer clause
ELSE
{
new ← this; -- Some kind of error, recycle this buffer
}; -- reject buffer clause
add new buffer to end of input chain
IF new #
NIL
THEN
{
new.device ← ethernetOne;
QueueInput[
ether, @new.encapsulation + ethernetEncapsulationOffset,
new.length - ethernetEncapsulationOffset, new.iocbChain];
IF systemBufferSize = 0 THEN systemBufferSize ← new.length;
new.next ← NIL;
IF firstInputBuffer = NIL THEN firstInputBuffer ← new
ELSE lastInputBuffer.next ← new;
lastInputBuffer ← new;
};
ENDLOOP;
};
OutInterrupt:
ENTRY
PROC =
{
b: Buffer;
status: Status;
Process.SetPriority[3];
UNTIL pleaseStop
DO
DO
forever until something interesting happens
IF pleaseStop THEN EXIT;
we compute the values each time around since the value of b can change if
the watcher shoots down the output.
IF (b ← firstOutputBuffer) #
NIL
AND (status ← GetStatus[b.iocbChain]) #
pending THEN EXIT;
WAIT outWait;
ENDLOOP;
IF pleaseStop THEN EXIT; -- so that we do not do something below
SELECT status
FROM
ok => {
tries: CARDINAL ← GetRetries[b.iocbChain];
etherStats.packetsSent ← etherStats.packetsSent + 1;
etherStats.wordsSent ← etherStats.wordsSent + b.length;
etherStats.loadTable[tries] ← etherStats.loadTable[tries] + 1;
IF doStats
THEN {
statEtherSendsCollision1: StatsDefs.StatCounterIndex = statEtherSendsCollision1;
first: CARDINAL = LOOPHOLE[statEtherSendsCollision1];
IF tries # 0 THEN StatIncr[LOOPHOLE[first + tries]];
};
};
ENDCASE => {
SELECT status
FROM
tooManyCollisions => {
etherStats.loadTable[EthernetDriverStats.MaxTries] ← etherStats.loadTable[EthernetDriverStats.MaxTries] + 1;
IF doStats THEN StatIncr[statEtherSendsCollisionLoadOverflow];
};
underrun => {
etherStats.overruns ← etherStats.overruns + 1;
IF doStats THEN StatIncr[statEtherSendOverrun];
};
ENDCASE => {
etherStats.badSendStatus ← etherStats.badSendStatus + 1;
IF doStats THEN StatIncr[statEtherSendBadStatus];
};
};
We don't resend things that screwup
firstOutputBuffer ← firstOutputBuffer.next;
DriverDefs.PutOnGlobalDoneQueue[b];
ENDLOOP;
};
GetElapsedPulses:
PROC [startTime: Pulses]
RETURNS [Pulses] =
INLINE
{
RETURN[ProcessorFace.GetClockPulses[] - startTime];
};
Watcher:
PROC =
{
UNTIL pleaseStop
DO
In either case, an interrupt should be pending. 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 InputChainOK[] THEN EXIT;
REPEAT FINISHED => { WatcherNotify[]; };
ENDLOOP;
Check for lost output interrupt
THROUGH [0..25)
DO
IF OutputChainOK[] THEN EXIT;
REPEAT FINISHED => { WatcherNotify[]; };
ENDLOOP;
Check for stuck input
IF GetElapsedPulses[timeLastRecv] > fiveSecondsOfPulses THEN FixupInput[];
Check for stuck output
IF firstOutputBuffer #
NIL
AND
(GetElapsedPulses[timeSendStarted] > fiveHalfSecondsOfPulses) THEN
ShootDownOutput[];
IF InputBufferQueueOK[] THEN WatcherWait[];
ENDLOOP;
};
InputChainOK:
ENTRY
PROC
RETURNS [
BOOL] =
INLINE
{
RETURN[
(firstInputBuffer =
NIL)
OR
(GetStatus[firstInputBuffer.iocbChain] = pending)];
};
OutputChainOK:
ENTRY
PROC
RETURNS [
BOOL] =
INLINE
{
RETURN[
(firstOutputBuffer =
NIL)
OR
(GetStatus[firstOutputBuffer.iocbChain] = pending)];
};
InputBufferQueueOK:
PROC
RETURNS [
BOOL] =
INLINE
{
b: Buffer;
enterTime: Pulses ← ProcessorFace.GetClockPulses[];
QueueInputBufferLocked:
ENTRY
PROC =
INLINE
{
QueueInput[
ether, @b.encapsulation + ethernetEncapsulationOffset,
b.length - ethernetEncapsulationOffset, b.iocbChain];
IF systemBufferSize = 0 THEN systemBufferSize ← b.length;
IF firstInputBuffer = NIL THEN firstInputBuffer ← b
ELSE lastInputBuffer.next ← b;
lastInputBuffer ← b;
inputBuffersInQueue ← inputBuffersInQueue + 1;
};
WHILE (inputBuffersInQueue < myNetwork.buffers)
DO
not MONITOR protected compair !!
IF (b ← DriverDefs.GetInputBuffer[
TRUE]) #
NIL
THEN
{ b.device ← ethernetOne; b.next ← NIL; QueueInputBufferLocked[]; };
IF GetElapsedPulses[enterTime] > oneSecondOfPulses THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
WatcherWait: ENTRY PROC = INLINE { WAIT timer; };
WatcherNotify:
ENTRY
PROC =
INLINE
{
IF doStats THEN StatIncr[statEtherLostInterrupts];
SmashCSBs[]; -- this will leave output dangling
};
FixupInput:
ENTRY
PROC =
INLINE
{
IF doStats THEN StatIncr[statInputIdle];
SmashCSBs[]; -- this will leave output dangling
};
ShootDownOutput:
ENTRY
PROC =
INLINE
{
This happens if the transciever is unplugged
b: Buffer;
TurnOff[ether];
UNTIL firstOutputBuffer =
NIL
DO
b ← firstOutputBuffer;
firstOutputBuffer ← firstOutputBuffer.next;
DriverDefs.PutOnGlobalDoneQueue[b];
IF doStats THEN StatIncr[statPacketsStuckInOutput];
ENDLOOP;
SmashCSBs[];
};
EnqueueRecvd: PROC[BufferDefs.PupBuffer]RETURNS[BOOL] ← NIL;
GetRPCPackets:
PUBLIC
--RPCPrivate--
ENTRY
PROC[
p: PROC[BufferDefs.PupBuffer]RETURNS[BOOL] ] =
{ EnqueueRecvd ← p };
ReturnBuffer:
PUBLIC
--RPCPrivate--
ENTRY
PROC[c: PupBuffer] =
{
b: Buffer = c;
IF loanedToRPC > 0
THEN
{
QueueInput[
ether, @b.encapsulation + ethernetEncapsulationOffset,
(b.length ← systemBufferSize) - ethernetEncapsulationOffset, b.iocbChain];
IF firstInputBuffer = NIL THEN firstInputBuffer ← b
ELSE lastInputBuffer.next ← b;
lastInputBuffer ← b;
loanedToRPC ← loanedToRPC-1;
}
ELSE ReturnFreeBuffer[b];
};
DecapsulateBuffer:
PROC [b: Buffer]
RETURNS [BufferType] =
{
SELECT b.encapsulation.ethernetOneType
FROM
pup =>
{
IF 2*b.length < b.pupLength + ethernetEncapsulationBytes
THEN
{
IF doStats THEN StatIncr[statPupsDiscarded];
RETURN[rejected];
};
RETURN[pup];
};
ois =>
{
IF 2*b.length < b.ois.pktLength + ethernetEncapsulationBytes
THEN
{ IF doStats THEN StatIncr[statOisDiscarded]; RETURN[rejected]; };
RETURN[ois];
};
translation =>
{
IF b.rawWords[0] = translationRequest THEN ReceiveRequest[b]
ELSE IF b.rawWords[0] = translationResponse THEN receiveAck[b]
ELSE
RETURN[rejected];
RETURN[processed];
};
ENDCASE => RETURN[rejected];
};
EncapsulatePup:
PROC [b: PupBuffer, destination: PupHostID] =
{
b.encapsulation ←
[ethernetOne[
etherSpare1:, etherSpare2:, etherSpare3:, etherSpare4:, etherSpare5:,
translationWorked: TRUE, ethernetOneDest: destination,
ethernetOneSource: myNetwork.hostNumber, ethernetOneType: pup]];
b.length ← (b.pupLength + 1 + ethernetEncapsulationBytes)/2;
};
EncapsulateOis:
PROC [
b: OisBuffer, destination: NSAddress.HostNumber] =
{
foundIt: BOOL;
ethernetAddr: Ethernet1Addr;
[foundIt, ethernetAddr] ← translate[destination];
IF foundIt
THEN
{
b.encapsulation ←
[ethernetOne[
etherSpare1:, etherSpare2:, etherSpare3:, etherSpare4:, etherSpare5:,
translationWorked: TRUE, ethernetOneDest: ethernetAddr,
ethernetOneSource: myNetwork.hostNumber, ethernetOneType: ois]];
b.length ← (b.ois.pktLength + 1 + ethernetEncapsulationBytes)/2;
}
ELSE
{
b.encapsulation ←
[ethernetOne[
etherSpare1:, etherSpare2:, etherSpare3:, etherSpare4:, etherSpare5:,
translationWorked: FALSE, ethernetOneDest:, ethernetOneSource:, ethernetOneType:]];
};
};
ForwardBuffer:
PROC [b: Buffer]
RETURNS [PupTypes.PupErrorCode] =
{
IF
FALSE
THEN
-- outputQueue.length>10 THEN
RETURN[gatewayResourceLimitsPupErrorCode]; -- transceiver unplugged?
SendBuffer[b];
RETURN[noErrorPupErrorCode];
};
SendBuffer:
ENTRY
PROC [b: Buffer] =
{
IF pleaseStop THEN DriverDefs.Glitch[DriverNotActive];
IF ~b.encapsulation.translationWorked
THEN
{ DriverDefs.PutOnGlobalDoneQueue[b]; RETURN; };
IF ~hearSelf
AND
(b.encapsulation.ethernetOneDest = myNetwork.hostNumber
OR
b.encapsulation.ethernetOneDest = PupTypes.allHosts) THEN
{ -- sending to ourself, copy it over since we can't hear it
copy: Buffer ← DriverDefs.GetInputBuffer[];
IF copy #
NIL
THEN
{
copy.device ← ethernetOne;
PrincOpsUtils.LongCopy[
from: @b.encapsulation + ethernetEncapsulationOffset, nwords: b.length,
to: @copy.encapsulation + ethernetEncapsulationOffset];
copy.length ← b.length;
copy.network ← LONG[@myNetwork]; -- LONG because of Mokelumne compiler bug
IF doStats THEN StatIncr[statEtherPacketsLocal];
IF doStats THEN StatBump[statEtherWordsLocal, b.length];
DriverDefs.PutOnGlobalInputQueue[copy];
}
ELSE IF doStats THEN StatIncr[statEtherEmptyFreeQueue];
};
SendBufferInternal[b];
};
SendBufferInternal:
INTERNAL
PROC [b: Buffer] =
{
b.device ← ethernetOne;
QueueOutput[
ether, @b.encapsulation + ethernetEncapsulationOffset, b.length,
b.iocbChain];
IF doStats THEN StatIncr[statEtherPacketsSent];
IF doStats THEN StatBump[statEtherWordsSent, b.length];
IF firstOutputBuffer = NIL THEN firstOutputBuffer ← b
ELSE lastOutputBuffer.next ← b;
lastOutputBuffer ← b;
timeSendStarted ← ProcessorFace.GetClockPulses[];
};
for changing the number of buffers while running
numberOfExtraBuffer: CARDINAL = 3;
bufferAccessHandle: BufferDefs.BufferAccessHandle;
this should only be called from Boss
No MONITOR PROTECTION here.
MaybeChangeNumberOfInputBuffers:
PROC [increaseBuffers:
BOOL] =
{
IF increaseBuffers
THEN
{
IF bufferAccessHandle =
NIL
THEN
{
bufferAccessHandle ← BufferDefs.MakeBufferPool[numberOfExtraBuffer];
myNetwork.buffers ← myNetwork.buffers + numberOfExtraBuffer;
};
}
ELSE
{
IF bufferAccessHandle #
NIL
THEN
{
myNetwork.buffers ← myNetwork.buffers - numberOfExtraBuffer;
BufferDefs.FreeBufferPool[bufferAccessHandle];
bufferAccessHandle ← NIL;
};
};
};
COLD code, only used when turning things on+off
AdjustLengtoOfD0EthernetInputQueue:
PUBLIC
PROC [n:
CARDINAL] =
{ inputQueueLength ← n; };
CreateDefaultEthernetOneDrivers:
PUBLIC
PROC
RETURNS [
BOOL] =
{
deviceNumber: CARDINAL ← 0;
etherDevice: DeviceHandle ← GetNextDevice[nullDeviceHandle];
IF GermSwap.switches[a] 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];
};
CreateAnEthernetOneDriver:
PROC [
etherDevice: DeviceHandle, deviceNumber: CARDINAL] =
{
IF deviceNumber # 0
THEN
{
him: POINTER TO FRAME[EthernetOneDriver] ← NEW EthernetOneDriver;
him.SetupEthernetOneDriver[etherDevice];
}
ELSE SetupEthernetOneDriver[etherDevice];
};
SetupEthernetOneDriver:
PROC [etherDevice: DeviceHandle] = {
net, host: DriverTypes.Byte;
ether ← etherDevice;
[net, host] ← GetEthernet1Address[ether];
IF net # 0
AND net # myNetwork.netNumber.b
AND myNetwork.netNumber #
nullNetworkNumber THEN DriverDefs.Glitch[EthernetNetNumberScrambled];
IF myNetwork.netNumber = nullNetworkNumber
THEN
myNetwork.netNumber ← [a: 0, b: net];
pleaseStop ← TRUE;
myNetwork.buffers ← inputQueueLength;
etherStats ← NEW[EthernetDriverStats.EtherStatsRep];
myNetwork.stats ← LOOPHOLE[etherStats];
PrincOpsUtils.LongZero[LOOPHOLE[etherStats], SIZE[EthernetDriverStats.EtherStatsRep]];
DriverDefs.AddDeviceToChain[@myNetwork, controlBlockSize];
};
ActivateDriver:
PROC = {
b: Buffer;
net, host: DriverTypes.Byte;
IF ~pleaseStop THEN DriverDefs.Glitch[DriverAlreadyActive];
fiveSecondsOfPulses ← MicrosecondsToPulses[5000000];
fiveHalfSecondsOfPulses ← MicrosecondsToPulses[2500000];
oneSecondOfPulses ← MicrosecondsToPulses[1000000];
[net, host] ← GetEthernet1Address[ether];
getGarbage ← pleaseStop ← FALSE;
TurnOff[ether];
AddCleanup[ether];
firstInputBuffer ← lastInputBuffer ← NIL;
firstOutputBuffer ← lastOutputBuffer ← NIL;
bufferAccessHandle ← NIL;
myEar ← myNetwork.hostNumber ← host;
inputBuffersInQueue ← loanedToRPC ← 0;
THROUGH [0..myNetwork.buffers)
DO
IF (b ← DriverDefs.GetInputBuffer[
TRUE])#
NIL
THEN {
inputBuffersInQueue ← inputBuffersInQueue + 1;
IF CommFlags.doDebug
AND Alignment[@b.encapsulation + ethernetEncapsulationOffset] # 0
THEN
DriverDefs.Glitch[BufferMustBeQuadWordAligned];
IF CommFlags.doDebug
AND Alignment[b.iocbChain] # 0
THEN
DriverDefs.Glitch[IOCBMustBeQuadWordAligned];
IF CommFlags.doDebug
AND PrincOpsUtils.HighHalf[b.iocbChain] # 0
THEN
DriverDefs.Glitch[IOCBMustBeInFirstMDS];
b.device ← ethernetOne;
IF firstInputBuffer = NIL THEN firstInputBuffer ← b;
IF lastInputBuffer # NIL THEN lastInputBuffer.next ← b;
lastInputBuffer ← b;
};
ENDLOOP;
[cv: inWait, mask: inInterruptMask] ← PrincOpsUtils.AllocateNakedCondition[];
Process.DisableTimeout[inWait];
[cv: outWait, mask: outInterruptMask] ← PrincOpsUtils.AllocateNakedCondition[];
Process.DisableTimeout[outWait];
SmashCSBs[];
inProcess ← FORK InInterrupt[];
outProcess ← FORK OutInterrupt[];
watcherProcess ← FORK Watcher[];
CreateCache[];
};
SetEthernetOneListener:
PUBLIC
ENTRY
PROC [physicalOrder:
CARDINAL, newHostNumber:
CARDINAL]
RETURNS [success:
BOOL] = {
him: POINTER TO FRAME[EthernetOneDriver];
network: Network ← GetNthDeviceLikeMe[physicalOrder];
IF network = NIL THEN RETURN[FALSE];
him ← LOOPHOLE[PrincOpsUtils.GlobalFrame[LOOPHOLE[network.interrupt]]];
him.EthernetOneListenForHost[newHostNumber];
RETURN[TRUE];
};
EthernetOneListenForHost:
PROC [newHostNumber:
CARDINAL] = {
myEar ← newHostNumber;
SmashCSBs[];
};
GetNthDeviceLikeMe:
PROC [physicalOrder:
CARDINAL]
RETURNS [net: Network] = {
Note that this numbers the devices starting from 1, in the Fortran tradition.
i: CARDINAL ← 0;
net ← DriverDefs.GetDeviceChain[];
WHILE net #
NIL
DO
IF net.device = myNetwork.device
THEN
IF (i ← i + 1) = physicalOrder THEN RETURN;
net ← net.next;
ENDLOOP;
};
Exported to EthernetDriverStats
GetEthernetOneConfig:
PUBLIC
SAFE
PROC
RETURNS [instances:
NAT ← 0] =
TRUSTED {
Returns how many instances are present for the Xerox research Ethernet (3Mbit).
net: Network ← DriverDefs.GetDeviceChain[];
WHILE net #
NIL
DO
IF net.device = myNetwork.device THEN instances ← instances + 1;
net ← net.next;
ENDLOOP;
};
GetEthernetOneStats:
PUBLIC
SAFE
PROC [instance:
NAT]
RETURNS [EthernetDriverStats.EtherStats ←
NIL] =
TRUSTED {
... returns the EtherStats object for the given instance of the Xerox research Ethernet driver. Note that these numbers are not monitor protected, so be prepared for strange results every so often. NIL will be returned for invalid instances.
net: Network ← GetNthDeviceLikeMe[instance+1];
IF net #
NIL
THEN
RETURN [
LOOPHOLE[net.stats]];
Note that it is OK to do this, since we are always holding onto the REF through the global frame associated with the network driver.
};
SetEthernetOneCollectGarbageToo:
PUBLIC
ENTRY
PROC [physicalOrder:
CARDINAL, collectGarbage:
BOOL]
RETURNS [success:
BOOL] = {
him: POINTER TO FRAME[EthernetOneDriver];
network: Network ← GetNthDeviceLikeMe[physicalOrder];
IF network = NIL THEN RETURN[FALSE];
him ← LOOPHOLE[PrincOpsUtils.GlobalFrame[LOOPHOLE[network.interrupt]]];
him.SetCollectGarbageToo[collectGarbage];
RETURN[TRUE];
};
SetCollectGarbageToo:
PROC [collectGarbage:
BOOL] = {
getGarbage ← collectGarbage;
};
SmashCSBs:
PROC = {
b: Buffer;
TurnOn[ether, myEar, inInterruptMask, outInterruptMask, globalStatePtr];
FOR b ← firstInputBuffer, b.next
UNTIL b =
NIL
DO
QueueInput[
ether, @b.encapsulation + ethernetEncapsulationOffset,
b.length - ethernetEncapsulationOffset, b.iocbChain];
ENDLOOP;
timeLastRecv ← ProcessorFace.GetClockPulses[];
};
DeleteDriver:
PROC = {
IF ether # GetNextDevice[nullDeviceHandle] THEN LoadState.SelfDestruct[];
};
DeactivateDriver:
PROC = {
b: Buffer;
IF pleaseStop THEN DriverDefs.Glitch[DriverNotActive];
pleaseStop ← TRUE;
KillInterruptRoutines[];
JOIN inProcess;
JOIN outProcess;
TurnOff[ether];
PrincOpsUtils.DeallocateNakedCondition[inWait];
PrincOpsUtils.DeallocateNakedCondition[outWait];
inWait ← outWait ← NIL;
MaybeChangeNumberOfInputBuffers[FALSE];
KillDriverLocked[];
JOIN watcherProcess;
RemoveCleanup[ether];
UNTIL firstInputBuffer =
NIL
DO
b ← firstInputBuffer;
firstInputBuffer ← b.next;
ReturnFreeBuffer[b];
ENDLOOP;
UNTIL firstOutputBuffer =
NIL
DO
b ← firstOutputBuffer;
firstOutputBuffer ← b.next;
ReturnFreeBuffer[b];
ENDLOOP;
DeleteCache[];
};
KillInterruptRoutines:
ENTRY
PROC =
INLINE {
NOTIFY inWait^; NOTIFY outWait^;
};
KillDriverLocked: ENTRY PROC = INLINE { NOTIFY timer; };
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Ugly thing to take care of handling the Ethernet1 (it uses 8 bit addresses).
This will go away when OIS Communications stops using Ethernet1's.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
types
OisAddr: TYPE = NSAddress.HostNumber;
Ethernet1Addr: TYPE = DriverTypes.Byte;
AddressPair:
TYPE =
MACHINE
DEPENDENT
RECORD [
oisAddr: OisAddr, ethernet1Addr: Ethernet1Addr, filler: [0..377B]];
CacheEntry: TYPE = REF CacheObject;
CacheObject:
TYPE =
MACHINE
DEPENDENT
RECORD [
nextLink: CacheEntry,
addressPair: AddressPair,
tries: CARDINAL,
timeStamp: ProcessorFace.GreenwichMeanTime,
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;
retryTime: LONG CARDINAL ← 2; -- two seconds
demonActiveTime: Process.Ticks ← Process.SecondsToTicks[1]; -- one second
deactivateTime: LONG CARDINAL ← 3*60; -- three minutes
demonSleepTime: Process.Ticks ← Process.SecondsToTicks[60*5]; -- five minutes
cacheEvent: CONDITION;
demonRunning: BOOL;
lastTranslationTime: ProcessorFace.GreenwichMeanTime;
receiveAck: PROC [b: Buffer] ← InactiveReceiveAck;
translate:
PROC [OisAddr]
RETURNS [
BOOL, Ethernet1Addr] ←
InactiveTranslate;
etherHost: Ethernet1Addr;
oisHost: OisAddr;
interface
CreateCache:
ENTRY
PROC =
INLINE {
cacheQueueHead ← broadCastPairEntry ← myAddressPairEntry ← NIL;
translate ← InactiveTranslate;
receiveAck ← InactiveReceiveAck;
demonRunning ← FALSE;
[, etherHost] ← GetEthernet1Address[ether];
oisHost ← NSAddress.GetProcessorID[];
};
StartCache:
ENTRY
PROC =
INLINE {
etherHost: Ethernet1Addr;
oisHost: OisAddr ← NSAddress.GetProcessorID[];
aP: AddressPair ← [NSAddress.broadcastHostNumber, PupTypes.allHosts, 0];
[, etherHost] ← GetEthernet1Address[ether];
translate ← Translate;
receiveAck ← ReceiveAck;
Process.SetTimeout[@cacheEvent, demonActiveTime];
broadCastPairEntry ← AddAddressPair[aP];
aP ← [oisHost, etherHost, 0];
myAddressPairEntry ← AddAddressPair[aP];
demonRunning ← TRUE;
Process.Detach[FORK Demon[]];
};
DeleteCache:
PROC =
INLINE {
e: CacheEntry;
DeleteCacheLocked: ENTRY PROC = INLINE { NOTIFY cacheEvent; };
DeleteCacheLocked
WHILE demonRunning DO DeleteCacheLocked[]; Process.Yield[]; ENDLOOP;
cleanup in case demon was never running
WHILE (cacheQueueHead #
NIL)
DO
e ← cacheQueueHead;
cacheQueueHead ← e.nextLink;
Heap.FreeNode[p: e];--
ENDLOOP;
};
assume protection by lock
depth: CARDINAL;
FindEntry:
INTERNAL
PROC [oisAddr: OisAddr]
RETURNS [entry: CacheEntry] = {
IF doStats THEN depth ← 0;
entry ← cacheQueueHead;
WHILE entry #
NIL
DO
IF oisAddr = entry.addressPair.oisAddr THEN RETURN;
entry ← entry.nextLink;
IF doStats THEN depth ← depth + 1;
ENDLOOP;
};
assume protection by lock
AddEntry:
INTERNAL
PROC [entry: CacheEntry] = {
entry.nextLink ← cacheQueueHead; cacheQueueHead ← entry; };
assume protection by lock
RemoveEntry:
INTERNAL
PROC [entry: CacheEntry] = {
e, pred: CacheEntry;
IF (pred ← cacheQueueHead) = entry
THEN {
cacheQueueHead ← cacheQueueHead.nextLink;
RETURN;
};
e ← pred.nextLink;
WHILE e #
NIL
DO
IF e = entry THEN { pred.nextLink ← entry.nextLink; RETURN; };
pred ← e;
e ← pred.nextLink;
ENDLOOP;
ERROR; -- entry not found
};
InactiveTranslate:
PROC [oisAddr: OisAddr]
RETURNS [foundIt:
BOOL, ethernet1Addr: Ethernet1Addr] = {
StartCache[];
[foundIt, ethernet1Addr] ← Translate[oisAddr];
};
locks
Translate:
ENTRY
PROC [oisAddr: OisAddr]
RETURNS [foundIt:
BOOL, ethernet1Addr: Ethernet1Addr] = {
e: CacheEntry;
foundIt ← FALSE;
lastTranslationTime ← ProcessorFace.GetGreenwichMeanTime[];
IF (e ← FindEntry[oisAddr]) #
NIL
THEN {
IF e # cacheQueueHead
THEN {
put e at the head of the queue
IF doStats THEN StatBump[cacheDepth, depth];
RemoveEntry[e];
AddEntry[e];
};
SELECT e.status
FROM
active => {
foundIt ← TRUE;
ethernet1Addr ← e.addressPair.ethernet1Addr;
e.timeStamp ← lastTranslationTime;
};
zombie => {
e.status ← new;
e.tries ← 0;
e.timeStamp ← lastTranslationTime;
NOTIFY cacheEvent;
};
ENDCASE => NULL;
} -- of found it
ELSE {
entry not found, so add a new one
IF doStats THEN StatIncr[cacheFault];
e ← NEW[CacheObject];
e.status ← new;
e.tries ← 0;
e.timeStamp ← lastTranslationTime;
e.addressPair ← [oisAddr: oisAddr, ethernet1Addr:, filler:];
AddEntry[e];
NOTIFY cacheEvent;
};
};
assume protection by lock
AddAddressPair:
INTERNAL
PROC [aP: AddressPair]
RETURNS [e: CacheEntry] = {
IF (e ← FindEntry[aP.oisAddr]) =
NIL
THEN
{ e ← NEW[CacheObject]; AddEntry[e]; };
e.addressPair ← aP;
e.status ← active;
e.timeStamp ← ProcessorFace.GetGreenwichMeanTime[];
};
assume protection by lock
DeallocateEntry:
INTERNAL
PROC [e: CacheEntry] = {
there are two entries that we do not want to throw out!!
IF (e = broadCastPairEntry)
OR (e = myAddressPairEntry)
THEN
e.timeStamp ← ProcessorFace.GetGreenwichMeanTime[]
ELSE { RemoveEntry[e]; --Heap.FreeNode[p: e];-- };
};
locks
Demon:
ENTRY
PROC = {
translationInactiveTime: LONG CARDINAL = 10*60;
demon will die if no services are needed in ten minutes
now: ProcessorFace.GreenwichMeanTime;
t: LONG CARDINAL;
e, nextE: CacheEntry;
pendingEntries: BOOL;
demonRunning ← TRUE;
lastTranslationTime ← ProcessorFace.GetGreenwichMeanTime[];
Process.SetPriority[3];
UNTIL pleaseStop
DO
WAIT cacheEvent;
IF (now ← ProcessorFace.GetGreenwichMeanTime[]) - lastTranslationTime > translationInactiveTime OR pleaseStop THEN EXIT;
pendingEntries ← FALSE;
e ← cacheQueueHead;
WHILE (e #
NIL)
DO
nextE ← e.nextLink;
t ← now - e.timeStamp;
SELECT e.status
FROM
active, zombie => {
IF t > deactivateTime THEN DeallocateEntry[e];
};
pending => {
pendingEntries ← TRUE;
IF t > retryTime
THEN {
e.tries ← e.tries + 1;
IF e.tries > retryLimit
THEN {
e.status ← zombie;
IF doStats THEN StatIncr[unsuccessfulTranslation];
}
ELSE {
IF doStats THEN StatIncr[translationRetries];
SendRequest[e];
e.timeStamp ← ProcessorFace.GetGreenwichMeanTime[];
};
};
};
new => {
pendingEntries ← TRUE;
SendRequest[e];
e.status ← pending;
e.timeStamp ← ProcessorFace.GetGreenwichMeanTime[];
};
ENDCASE => ERROR;
e ← nextE;
ENDLOOP; -- end of queue entries loop
IF pendingEntries THEN Process.SetTimeout[@cacheEvent, demonActiveTime]
ELSE Process.SetTimeout[@cacheEvent, demonSleepTime];
ENDLOOP; -- end of infinite loop
receiveAck ← InactiveReceiveAck;
translate ← InactiveTranslate;
e ← cacheQueueHead;
cacheQueueHead ← myAddressPairEntry ← broadCastPairEntry ← NIL;
WHILE e # NIL DO nextE ← e.nextLink; --Heap.FreeNode[p: e];-- e ← nextE; ENDLOOP;
demonRunning ← FALSE;
};
assume locked
SendRequest:
INTERNAL
PROC [e: CacheEntry] = {
b: Buffer;
request: LONG POINTER TO AddressPair;
IF (b ← DriverDefs.GetInputBuffer[]) #
NIL
THEN {
broadcast the trnslation request
b.encapsulation ← [
ethernetOne[
etherSpare1:, etherSpare2:, etherSpare3:, etherSpare4:, etherSpare5:,
translationWorked:, ethernetOneDest: PupTypes.allHosts,
ethernetOneSource: myNetwork.hostNumber, ethernetOneType: translation]];
b.length ← (1 + ethernetEncapsulationBytes)/2 + 2*SIZE[AddressPair] + 1;
b.rawWords[0] ← translationRequest;
request ← LOOPHOLE[@b.rawWords[1]];
request^ ← e.addressPair;
also send our addresses, so responder does not fault
request ← request + SIZE[AddressPair];
request^ ← myAddressPairEntry.addressPair;
send it
SendBufferInternal[b];
};
};
locks
we now own buffer b
InactiveReceiveAck:
PROC [b: Buffer] = {
b.requeueProcedure[b];
};
ReceiveAck:
ENTRY
PROC [b: Buffer] = {
IF b.encapsulation.ethernetOneDest = myAddressPairEntry.addressPair.ethernet1Addr
THEN {
receipt: LONG POINTER TO AddressPair ← LOOPHOLE[@b.rawWords[1]];
[] ← AddAddressPair[receipt^];
};
b.requeueProcedure[b];
};
locks
we now own buffer b
ReceiveRequest:
ENTRY
PROC [b: Buffer] = {
request, requesterAddr: LONG POINTER TO AddressPair;
request ← LOOPHOLE[@b.rawWords[1]];
IF request.oisAddr = oisHost
THEN {
since the requester is probably going to talk to us, add his address before we take a fault
requesterAddr ← request + SIZE[AddressPair];
[] ← AddAddressPair[requesterAddr^];
IF doStats THEN StatIncr[requestsForMe];
request.ethernet1Addr ← etherHost;
SendAck[request^, b.encapsulation.ethernetOneSource, b]; -- we lose ownership of b
}
ELSE b.requeueProcedure[b];
};
assume protection by lock
we now own buffer b
SendAck:
INTERNAL
PROC [aP: AddressPair, to: DriverTypes.Byte, b: Buffer] =
INLINE {
response: LONG POINTER TO AddressPair;
IF b #
NIL
THEN {
b.encapsulation ←
[ethernetOne[
etherSpare1:, etherSpare2:, etherSpare3:, etherSpare4:, etherSpare5:,
translationWorked:, ethernetOneDest: to, ethernetOneSource: myNetwork.hostNumber,
ethernetOneType: translation]];
b.length ← (1 + ethernetEncapsulationBytes)/2 + SIZE[AddressPair] + 1;
b.rawWords[0] ← translationResponse;
response ← LOOPHOLE[@b.rawWords[1]];
response^ ← aP;
send it
SendBufferInternal[b];
};
};
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
End of Ethernet1 uglyness
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
initialization
Process.SetTimeout[@timer, Process.MsecToTicks[1000]];
IF CommFlags.doDebug
THEN {
debugPointer: LONG POINTER TO DriverDefs.GiantVector ← DriverDefs.GetGiantVector[];
debugPointer.currentInputBuffer ← @firstInputBuffer;
debugPointer.nextInputBuffer ← @lastInputBuffer;
debugPointer.currentOutputBuffer ← @firstOutputBuffer;
};
}. -- EthernetOneDriver