DIRECTORY
Basics USING [bytesPerWord],
BasicTime USING [GetClockPulses, MicrosecondsToPulses, Pulses],
Booting USING [RegisterProcs, RollbackProc, switches],
CommBuffer USING [],
Driver USING [AllocBuffer, AddNetwork, Buffer, bytesToRead, FreeBuffer, Network, NetworkObject, NoThankYou, recvPriority, sendPriority, watcherPriority, wordsInIocb],
DriverType USING [Encapsulation, ethernetEncapsulationBytes, ethernetEncapsulationOffset, ethernetMinBytes, IsMulticastHost],
EthernetFace USING [AddCleanup, controlBlockSize, GetNextDevice, GetStatusAndCollisions, GetStatusAndLength, GetPacketsMissed, Handle, hearSelf, IOCB, MarkKilled, nullHandle, QueueInput, QueueOutput, SetPromiscuous, Status, TurnOff, TurnOn],
EthernetDriverStats USING [EtherStats, EtherStatsRep, MaxTries],
GermSwap USING [], -- Needed by Booting.switches
IP USING [nullAddress],
NS USING [broadcastHost, GetThisHost, Host, unknownNet],
PrincOpsUtils USING [AllocateNakedCondition, LongCopy],
Process USING [Detach, DisableTimeout, GetPriority, MsecToTicks, Priority, SetPriority, SetTimeout],
Pup USING [Host, nullHost, nullNet],
SafeStorage USING [PinObject];
 
EthernetDriver: 
CEDAR 
MONITOR 
LOCKS data 
USING data: InstanceData
IMPORTS BasicTime, Booting, Driver, DriverType, EthernetFace, NS, PrincOpsUtils, Process, SafeStorage
EXPORTS CommBuffer = {
Buffer: TYPE = Driver.Buffer;
Network: TYPE = Driver.Network;
Encapsulation: PUBLIC TYPE = DriverType.Encapsulation;
Next: 
PROC [b: Buffer] 
RETURNS [Buffer] = 
TRUSTED 
INLINE {
RETURN[LOOPHOLE[b.ovh.next]]; };
 
Data: 
PROC [b: Buffer] 
RETURNS [
LONG 
POINTER] = 
TRUSTED 
INLINE {
RETURN[@b.ovh.encap + DriverType.ethernetEncapsulationOffset]; };
 
Bytes: 
PROC [bytes: 
NAT] 
RETURNS [
NAT] = 
TRUSTED 
INLINE {
RETURN[bytes + DriverType.ethernetEncapsulationBytes]; };
 
BytesToRead: 
PROC 
RETURNS [
NAT] = 
INLINE {
RETURN[Bytes[Driver.bytesToRead]]; };
 
Iocb: 
PROC [b: Buffer] 
RETURNS [EthernetFace.
IOCB] = 
TRUSTED 
INLINE {
RETURN[LOOPHOLE[b.ovh.iocb]]; };
 
ElapsedPulses: 
PROC [startTime: BasicTime.Pulses] 
RETURNS [BasicTime.Pulses] =
 INLINE {
RETURN[BasicTime.GetClockPulses[] - startTime]; };
 
InstanceData: TYPE = REF InstanceDataRep;
InstanceDataRep: 
TYPE = 
MONITORED 
RECORD [
ether: EthernetFace.Handle,
timer: CONDITION,
inWait, outWait: LONG POINTER TO CONDITION ← NIL,
inHickup, outHickup: CONDITION,
inHickups, inBurps: INT ← 0,
outHickups, outBurps: INT ← 0,
inInterruptMask, outInterruptMask: WORD,
firstInputBuffer, lastInputBuffer: Driver.Buffer,
firstOutputBuffer, lastOutputBuffer: Driver.Buffer,
timeLastRecv, timeSendStarted: BasicTime.Pulses,
lastMissed: CARDINAL,
numberOfInputBuffers: CARDINAL,
me: NS.Host,
promiscuous: BOOL,
stats: EthernetDriverStats.EtherStats ];
defaultNumberOfInputBuffers: NAT ← 5;
thirtySecondsOfPulses: BasicTime.Pulses ← BasicTime.MicrosecondsToPulses[30000000];
fiveSecondsOfPulses: BasicTime.Pulses ← BasicTime.MicrosecondsToPulses[5000000];
Sending
GetNSEncapsulation: 
PROC [network: Network, dest: 
NS.Host] 
RETURNS [encap: Encapsulation] = 
TRUSTED {
data: InstanceData = NARROW[network.instanceData];
encap ← [ ethernet[
ethernetDest: dest,
ethernetSource: data.me,
ethernetType: ns ]];
};
 
Return: 
PROC [network: Network, buffer: Buffer, bytes: 
NAT] = {
data: InstanceData = NARROW[network.instanceData];
buffer.ovh.encap.ethernetDest ← buffer.ovh.encap.ethernetSource;
buffer.ovh.encap.ethernetSource ← data.me;
Send[network, buffer, bytes];
};
 
unknownDest: LONG CARDINAL ← 0;
Send: 
PROC [network: Network, buffer: Buffer, bytes: 
NAT] = {
data: InstanceData = NARROW[network.instanceData];
priority: Process.Priority = Process.GetPriority[];
dest: NS.Host ← buffer.ovh.encap.ethernetDest;
IF network.dead THEN RETURN;
IF buffer.ovh.encap.ethernetType = translationFailed 
THEN {
unknownDest ← unknownDest.SUCC;
RETURN; };
 
IF priority # Driver.sendPriority THEN Process.SetPriority[Driver.sendPriority];
IF ~EthernetFace.hearSelf
AND (dest = data.me 
OR DriverType.IsMulticastHost[dest] 
OR data.promiscuous) 
THEN {
sending to ourself, copy it over since we can't hear it
copy: Buffer ← Driver.AllocBuffer[];
words: NAT ← (Bytes[bytes]+Basics.bytesPerWord-1) / Basics.bytesPerWord;
copy.ovh.network ← network;
copy.ovh.next ← NIL;
copy.ovh.direction ← none;
TRUSTED {
PrincOpsUtils.LongCopy[from: Data[buffer], nwords: words, to: Data[copy]]; };
 
SELECT copy.ovh.encap.ethernetType 
FROM
ns => copy ← network.ns.recv[network, copy, bytes];
oldPup => copy ← network.pup.recv[network, copy, bytes];
oldPupTranslation => copy ← network.pup.recvTranslate[network, copy, bytes];
ip => copy ← network.ip.recv[network, copy, bytes];
arp => copy ← network.ip.recvTranslate[network, copy, bytes];
ENDCASE => copy ← network.other.recv[network, copy, bytes];
 
IF copy # NIL THEN Driver.FreeBuffer[copy]; };
 
buffer.ovh.network ← network;
buffer.ovh.next ← NIL;
SendInner[data, buffer, bytes];
IF priority # Driver.sendPriority THEN Process.SetPriority[priority];
};
 
Unless you are remote debugging, an error or breakpoint in here will probably kill your whole machine. The problem is that the debugger wants to check the time stamp on files.
SendInner: 
ENTRY 
PROC [data: InstanceData, b: Buffer, bytes: 
NAT] = {
stats: EthernetDriverStats.EtherStats = data.stats;
status: EthernetFace.Status;
collisions: NAT;
bytes ← MAX[bytes, DriverType.ethernetMinBytes];
EthernetFace.QueueOutput[data.ether, Data[b], Bytes[bytes], Iocb[b]];
IF data.firstOutputBuffer # NIL AND data.lastOutputBuffer.ovh.next # NIL THEN ERROR;
IF data.firstOutputBuffer = NIL THEN data.firstOutputBuffer ← b
ELSE data.lastOutputBuffer.ovh.next ← b;
data.lastOutputBuffer ← b;
data.timeSendStarted ← BasicTime.GetClockPulses[];
DO
TRUSTED { WAIT data.outWait^; };
[status, collisions] ← EthernetFace.GetStatusAndCollisions[Iocb[b]];
IF status # pending THEN EXIT;
data.outBurps ← data.outBurps.SUCC;
BROADCAST data.outHickup;
ENDLOOP;
 
UNTIL b = data.firstOutputBuffer 
DO
data.outHickups ← data.outHickups.SUCC;
WAIT data.outHickup;
ENDLOOP;
 
data.firstOutputBuffer ← Next[data.firstOutputBuffer];
SELECT status 
FROM
ok => {
stats.packetsSent ← stats.packetsSent + 1;
stats.wordsSent ← stats.wordsSent + bytes/Basics.bytesPerWord;
stats.loadTable[collisions] ← stats.loadTable[collisions] + 1;
};
ENDCASE => {
SELECT status 
FROM
tooManyCollisions => {
tooMany: NAT = EthernetDriverStats.MaxTries;
stats.loadTable[tooMany] ← stats.loadTable[tooMany] + 1; };
underrun => stats.overruns ← stats.overruns + 1;
ENDCASE => stats.badSendStatus ← stats.badSendStatus + 1;
 
};
 
 
BROADCAST data.outHickup;
};
 
 
Receiving
Recv: 
PROC [network: Network] = {
data: InstanceData = NARROW[network.instanceData];
b: Buffer ← Driver.AllocBuffer[];
Process.SetPriority[Driver.recvPriority];
DO
good: BOOL;
bytes: NAT;
b.ovh.network ← network;
b.ovh.next ← NIL;
b.ovh.direction ← none;
[good, bytes] ← RecvInner[data, b];
b.ovh.next ← NIL;
IF bytes < DriverType.ethernetEncapsulationBytes THEN good ← FALSE
ELSE bytes ← bytes - DriverType.ethernetEncapsulationBytes;
IF good 
THEN
SELECT b.ovh.encap.ethernetType 
FROM
ns => b ← network.ns.recv[network, b, bytes];
oldPup => b ← network.pup.recv[network, b, bytes];
oldPupTranslation => b ← network.pup.recvTranslate[network, b, bytes];
ip => b ← network.ip.recv[network, b, bytes];
arp => b ← network.ip.recvTranslate[network, b, bytes];
ENDCASE => b ← network.other.recv[network, b, bytes]
 
 
ELSE b ← network.error.recv[network, b, bytes];
IF b = NIL THEN b ← Driver.AllocBuffer[];
ENDLOOP;
 
};
 
Unless you are remote debugging, an error or breakpoint in here will probably kill your whole machine. The problem is that the debugger wants to check the time stamp on files.
RecvInner: 
ENTRY 
PROC [data: InstanceData, b: Buffer] 
RETURNS [good: 
BOOL, bytes: 
NAT] = {
stats: EthernetDriverStats.EtherStats = data.stats;
status: EthernetFace.Status;
EthernetFace.QueueInput[data.ether, Data[b], BytesToRead[], Iocb[b]];
IF data.firstInputBuffer # NIL AND data.lastInputBuffer.ovh.next # NIL THEN ERROR;
IF data.firstInputBuffer = NIL THEN data.firstInputBuffer ← b
ELSE data.lastInputBuffer.ovh.next ← b;
data.lastInputBuffer ← b;
DO
TRUSTED { WAIT data.inWait^; };
[status, bytes] ← EthernetFace.GetStatusAndLength[Iocb[b]];
IF status # pending THEN EXIT;
data.inBurps ← data.inBurps.SUCC;
BROADCAST data.inHickup;
ENDLOOP;
 
UNTIL b = data.firstInputBuffer 
DO
data.inHickups ← data.inHickups.SUCC;
WAIT data.inHickup;
ENDLOOP;
 
data.firstInputBuffer ← Next[data.firstInputBuffer];
data.timeLastRecv ← BasicTime.GetClockPulses[];
SELECT status 
FROM
ok => {
good ← TRUE;
stats.packetsRecv ← stats.packetsRecv + 1;
stats.wordsRecv ← stats.wordsRecv + bytes/Basics.bytesPerWord; };
ENDCASE => {
good ← FALSE;
stats.badRecvStatus ← stats.badRecvStatus + 1;
IF status = overrun THEN stats.overruns ← stats.overruns + 1; };
 
 
BROADCAST data.inHickup;
};
 
 
Watching
Rollback: Booting.RollbackProc = {
[clientData: REF ANY]
network: Network = NARROW[clientData];
data: InstanceData = NARROW[network.instanceData];
SmashCSB[data];
};
 
Watcher: 
PROC [network: Network] = {
data: InstanceData = NARROW[network.instanceData];
stats: EthernetDriverStats.EtherStats = NARROW[network.stats];
missedIn: NAT ← 0;
missedOut: NAT ← 0;
inputNotifys: INT ← 0;
outputNotifys: INT ← 0;
fixupInputs: INT ← 0;
shootDownOutputs: INT ← 0;
Process.SetPriority[Driver.watcherPriority];
DO
missed: CARDINAL ← EthernetFace.GetPacketsMissed[data.ether];
newMissed: CARDINAL ← (missed - data.lastMissed);
This is the only place where inputOff gets updated, so we don't need the ML.
IF newMissed < 10000 THEN stats.inputOff ← stats.inputOff + newMissed;
data.lastMissed ← missed;
Since the interrupt routines are higher priority than we are, all the interrupts should get processed before we can see them. If see anything interesting, an interrupt has probably been lost. However, there is a slim chance it was 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 not process when we look again, it could be a new interrupt that has just arrived.
FOR i: 
NAT 
IN [0..25) 
DO 
-- Check for lost input interrupts
IF InputChainOK[data] THEN { missedIn ← 0; EXIT; };
REPEAT 
FINISHED => {
missedIn ← missedIn.SUCC;
inputNotifys ← inputNotifys.SUCC;
WatcherNotifyInput[data]; };
 
ENDLOOP;
 
FOR i: 
NAT 
IN [0..25) 
DO 
-- Check for lost output interrupts
IF OutputChainOK[data] THEN { missedOut ← 0; EXIT; };
REPEAT 
FINISHED => {
missedOut ← missedOut.SUCC;
outputNotifys ← outputNotifys.SUCC;
WatcherNotifyOutput[data]; };
 
ENDLOOP;
 
IF (missedIn > 10) -- Check for input confusion
OR (ElapsedPulses[data.timeLastRecv] > thirtySecondsOfPulses) 
THEN {
missedIn ← 0;
fixupInputs ← fixupInputs.SUCC;
SmashCSB[data]; };
 
IF (missedOut > 10) -- Check for output confusion
OR (data.firstOutputBuffer # 
NIL
AND (ElapsedPulses[data.timeSendStarted] > fiveSecondsOfPulses)) THEN {
missedOut ← 0;
shootDownOutputs ← shootDownOutputs.SUCC;
SmashCSB[data]; };
 
WatcherWait[data];
ENDLOOP;
 
};
 
InputChainOK: 
ENTRY 
PROC [data: InstanceData] 
RETURNS [
BOOL] = {
status: EthernetFace.Status;
IF data.firstInputBuffer = NIL THEN RETURN[TRUE];
status ← EthernetFace.GetStatusAndLength[Iocb[data.firstInputBuffer]].status;
IF status = pending THEN RETURN[TRUE];
RETURN[FALSE];
};
 
OutputChainOK: 
ENTRY 
PROC [data: InstanceData] 
RETURNS [
BOOL] = {
status: EthernetFace.Status;
IF data.firstOutputBuffer = NIL THEN RETURN[TRUE];
status ← EthernetFace.GetStatusAndCollisions[Iocb[data.firstOutputBuffer]].status;
IF status = pending THEN RETURN[TRUE];
RETURN[FALSE];
};
 
WatcherNotifyInput: 
ENTRY 
PROC [data: InstanceData] = 
TRUSTED {
NOTIFY data.inWait^;
};
 
WatcherNotifyOutput: 
ENTRY 
PROC [data: InstanceData] = 
TRUSTED {
NOTIFY data.outWait^;
};
 
SmashCSB: 
ENTRY 
PROC [data: InstanceData] = {
EthernetFace.TurnOff[data.ether];
FOR b: Buffer ← data.firstInputBuffer, Next[b] 
UNTIL b = 
NIL 
DO
status: EthernetFace.Status;
status ← EthernetFace.GetStatusAndLength[Iocb[b]].status;
IF status = pending THEN EthernetFace.MarkKilled[Iocb[b]];
TRUSTED { NOTIFY data.inWait^; };
ENDLOOP;
 
FOR b: Buffer ← data.firstOutputBuffer, Next[b] 
UNTIL b = 
NIL 
DO
status: EthernetFace.Status;
status ← EthernetFace.GetStatusAndCollisions[Iocb[b]].status;
IF status = pending THEN EthernetFace.MarkKilled[Iocb[b]];
TRUSTED { NOTIFY data.outWait^; };
ENDLOOP;
 
BROADCAST data.inHickup;
BROADCAST data.outHickup;
EthernetFace.TurnOn[data.ether, data.inInterruptMask, data.outInterruptMask];
data.lastMissed ← EthernetFace.GetPacketsMissed[data.ether];
data.timeLastRecv ← BasicTime.GetClockPulses[];
IF data.promiscuous THEN EthernetFace.SetPromiscuous[data.ether, TRUE];
};
 
WatcherWait: 
ENTRY 
PROC [data: InstanceData] = {
WAIT data.timer;
};
 
 
SetPromiscuous: 
PROC [network: Network, promiscuous: 
BOOL] = {
data: InstanceData = NARROW[network.instanceData];
EthernetFace.SetPromiscuous[data.ether, promiscuous];
data.promiscuous ← promiscuous;
};
 
skipTheBugs: INT ← 0;
IsThisForMe: 
PROC [network: Network, buffer: Buffer] 
RETURNS [yes: 
BOOL] = {
data: InstanceData = NARROW[network.instanceData];
dest: NS.Host ← buffer.ovh.encap.ethernetDest;
IF dest = data.me THEN RETURN[TRUE];
IF dest = NS.broadcastHost THEN RETURN[TRUE];
IF DriverType.IsMulticastHost[dest] 
THEN skipTheBugs ← skipTheBugs.
SUCC;
Bug in 12.0. Fling packet to random place if don't know translation info yet.
 
RETURN[FALSE];
};
 
ToBroadcast: 
PROC [network: Network, buffer: Buffer] 
RETURNS [yes: 
BOOL] = {
dest: NS.Host ← buffer.ovh.encap.ethernetDest;
IF DriverType.IsMulticastHost[dest] THEN RETURN[TRUE];
RETURN[FALSE];
};
 
MoreBuffers: 
PROC [network: Network, total: 
NAT] = {
data: InstanceData = NARROW[network.instanceData];
IF total < data.numberOfInputBuffers THEN RETURN;
FOR i: 
NAT 
IN [data.numberOfInputBuffers..total) 
DO
TRUSTED { Process.Detach[FORK Recv[network]]; };
ENDLOOP;
 
data.numberOfInputBuffers ← total;
};
 
 
}.
If you are interested in the interrupt tangle, look at the comments at the end of EthernetOneDriver.mesa.