MiniEthernetDriver.mesa
Hal Murray, June 3, 1986 3:55:16 pm PDT
If you make an edit in this module, consider making the same change in MiniEthernetOneDriver.
DIRECTORY
Basics USING [bytesPerWord, LongDiv, LongMult, LowHalf],
Checksum USING [ComputeChecksum],
CommBuffer USING [Overhead],
CommDriver USING [BufferObject],
CommDriverType USING [ethernetEncapsulationBytes, ethernetEncapsulationOffset, Encapsulation, ethernetMinBytes],
Endian USING [FWORD, HWORD, bytesPerHWord],
EthernetFace USING [GetNextDevice, GetStatusAndCollisions, GetStatusAndLength, Handle, IOCB, nullHandle, QueueInput, QueueOutput, TurnOff, TurnOn],
GermPrivate USING [ShowMP],
MiniEthernetDefs USING [timedOut],
MPCodes USING [germFindingPupHostNumber, serverSaysNoPupAddress],
XNS USING [Address, broadcastHost, Host, unknownNet, Socket],
PrincOpsUtils USING [LongCopy],
ProcessorFace USING [GetClockPulses, microsecondsPerHundredPulses, ProcessorID, processorID],
Pup USING [Address, allHosts, Host, Net, nullHost, nullNet, nullSocket, Socket],
PupBuffer USING [noChecksum, PupBufferPointer, WordsWithoutChecksum],
PupType USING [bytesOfPupOverhead, Type];
MiniEthernetDriver: PROGRAM
IMPORTS Basics, Checksum, EthernetFace, GermPrivate, PrincOpsUtils, ProcessorFace, PupBuffer
EXPORTS CommBuffer, MiniEthernetDefs = {
Encapsulation: PUBLIC TYPE = CommDriverType.Encapsulation;
BYTE: TYPE = [0..100H);
active: BOOLFALSE;
last: {in, out, reset};
myEtherHost: XNS.Host ← LOOPHOLE[ProcessorFace.processorID];
myPupHost: Pup.Host;
myPupNet: Pup.Net;
Ethernet buffer. This is merely storage for MiniDriver
bufWords: CARDINAL = 300;
bufferBytes: CARDINAL = bufWords*Basics.bytesPerWord;
fudge: CARDINAL = 30;
eBuff: ARRAY [0..bufWords + fudge) OF WORD;
eBuffer: POINTER = LOOPHOLE[@eBuff];
b: PupBuffer.PupBufferPointer; -- Global buffer for actual data transfers
ether: EthernetFace.Handle;
longIocb: EthernetFace.IOCB;
bytesPerPupHeader: CARDINAL = PupType.bytesOfPupOverhead;
DriverNotActive: PUBLIC ERROR = CODE;
BufferOverflow: PUBLIC ERROR = CODE;
SendToStrangePlace: PUBLIC ERROR = CODE;
ReturnToStrangePlace: PUBLIC ERROR = CODE;
ActivateDriver: PUBLIC PROC [iocb: LONG POINTER] RETURNS [BOOL] = {
p: LONG POINTER ← @b.ovh.encap + CommDriverType.ethernetEncapsulationOffset;
q: LONG POINTER ← b;
fudge: LONG CARDINAL ← p - q;
b ← eBuffer - fudge;
IF ~FindTheBoard[] THEN RETURN[FALSE];
longIocb ← iocb;
EthernetFace.TurnOff[ether];
EthernetFace.TurnOn[ether, 0, 0];
active ← TRUE;
last ← reset;
myPupNet ← Pup.nullNet;
myPupHost ← [GetMyPupHostNumber[]];
last ← reset;
RETURN[TRUE];
};
FindTheBoard: PROC RETURNS [BOOL] = {
ether ← EthernetFace.GetNextDevice[EthernetFace.nullHandle];
RETURN[ether # EthernetFace.nullHandle];
};
KillDriver: PUBLIC PROC = {
EthernetFace.TurnOff[ether];
active ← FALSE;
};
BorrowTheBuffer: PUBLIC PROC RETURNS [p: LONG POINTER, bytes: CARDINAL] = {
RETURN[eBuffer+fudge, bufferBytes];
};
SendPacket: PUBLIC PROC [dest: Pup.Address, me: Pup.Socket, type: PupType.Type, id: Endian.FWORD, data: LONG POINTER, bytes: CARDINAL] = {
words: CARDINAL = (bytes + Basics.bytesPerWord-1)/Basics.bytesPerWord;
SELECT TRUE FROM
dest.host = Pup.allHosts => b.ovh.encap.ethernetDest ← XNS.broadcastHost;
ENDCASE => ERROR SendToStrangePlace;
b.byteLength ← bytesPerPupHeader + bytes;
b.type ← type;
b.id ← id;
b.dest ← dest;
b.source.socket ← me;
PrincOpsUtils.LongCopy[to: @b.byte, from: data, nwords: words];
SendPupBuffer[];
};
ReturnPacket: PUBLIC PROC [type: PupType.Type, id: Endian.FWORD, data: LONG POINTER, bytes: CARDINAL] = {
words: CARDINAL = (bytes + Basics.bytesPerWord-1)/Basics.bytesPerWord;
temp: Pup.Socket;
IF last # in THEN ERROR ReturnToStrangePlace;
b.ovh.encap.ethernetDest ← b.ovh.encap.ethernetSource;
b.byteLength ← bytesPerPupHeader + bytes;
b.type ← type;
b.id ← id;
temp ← b.dest.socket;
b.dest ← b.source;
b.source.socket ← temp;
PrincOpsUtils.LongCopy[to: @b.byte, from: data, nwords: words];
SendPupBuffer[];
};
SendPupBuffer: PROC = {
b.hopCount ← 0;
b.spares ← 0;
b.source.net ← myPupNet;
b.source.host ← myPupHost;
SetPupChecksum[b];
b.ovh.encap.ethernetType ← oldPup;
SendBuffer[b.byteLength];
};
SendBuffer: PROC [bytes: CARDINAL] = {
IF ~active THEN ERROR DriverNotActive;
IF bytes > bufferBytes THEN ERROR BufferOverflow;
IF bytes < CommDriverType.ethernetMinBytes THEN
bytes ← CommDriverType.ethernetMinBytes;
b.ovh.encap.ethernetSource ← myEtherHost;
last ← out;
EthernetFace.QueueOutput[
ether,
@b.ovh.encap + CommDriverType.ethernetEncapsulationOffset,
bytes + CommDriverType.ethernetEncapsulationBytes,
longIocb];
THROUGH [0..LAST[CARDINAL]) DO
IF EthernetFace.GetStatusAndCollisions[longIocb].status # pending THEN EXIT;
REPEAT FINISHED => {
EthernetFace.TurnOff[ether];
EthernetFace.TurnOn[ether, 0, 0]; };
ENDLOOP;
};
RecvPacket: PUBLIC PROC [source: LONG POINTER TO Pup.Address, me: Pup.Socket, data: LONG POINTER, maxBytes: CARDINAL, timeout: PROC RETURNS [BOOL]] RETURNS [bytes: CARDINAL, id: Endian.FWORD, type: PupType.Type] = {
IF ~active THEN ERROR DriverNotActive;
DO
EthernetFace.QueueInput[
ether,
@b.ovh.encap + CommDriverType.ethernetEncapsulationOffset,
bufferBytes,
longIocb];
UNTIL EthernetFace.GetStatusAndLength[longIocb].status # pending DO
IF timeout[] THEN {
EthernetFace.TurnOff[ether];
EthernetFace.TurnOn[ether, 0, 0];
last ← reset;
RETURN[MiniEthernetDefs.timedOut, [0, 0], LOOPHOLE[0]];
};
ENDLOOP;
IF EthernetFace.GetStatusAndLength[longIocb].status # ok THEN LOOP;
bytes ← EthernetFace.GetStatusAndLength[longIocb].bytes;
IF b.ovh.encap.ethernetType = oldPupTranslation THEN { -- See PupEthernetTranslation
AddressPair: TYPE = MACHINE DEPENDENT RECORD [
big: XNS.Host,
small: Pup.Host, filler: [0..377B]];
raw: LONG POINTER TO CommDriver.BufferObject = LOOPHOLE[b];
inOut: LONG POINTER TO CARDINAL = LOOPHOLE[@raw.data];
request: LONG POINTER TO AddressPair = LOOPHOLE[@raw.data + 1];
translationRequest: CARDINAL = 10101B;
translationResponse: CARDINAL = 7070B;
IF inOut^ # translationRequest THEN LOOP;
IF request.small # myPupHost THEN LOOP;
raw.ovh.encap.ethernetDest ← raw.ovh.encap.ethernetSource;
inOut^ ← translationResponse;
request.big ← myEtherHost;
SendBuffer[Basics.bytesPerWord*(1+SIZE[AddressPair]+SIZE[AddressPair])];
Extra AddressPair in the answer is a Quirk to keep Unix happy.
LOOP;
};
IF b.ovh.encap.ethernetType # oldPup THEN LOOP;
should check words that arrived against pupLength?
check here for routing table if we ever get that complicated
IF myPupNet # Pup.nullNet AND myPupNet # b.dest.net AND b.dest.net # Pup.nullNet THEN LOOP;
IF myPupHost # b.dest.host THEN LOOP; -- Can't recv broadcast
IF me # b.dest.socket THEN LOOP;
IF source.net # Pup.nullNet AND source.net # b.source.net AND b.source.net # Pup.nullNet THEN
LOOP;
IF source.host # Pup.nullHost AND source.host # b.source.host THEN LOOP;
IF source.socket # Pup.nullSocket AND source.socket # b.source.socket THEN LOOP;
IF myPupNet = Pup.nullNet THEN myPupNet ← b.dest.net;
bytes ← b.byteLength - bytesPerPupHeader;
IF bytes > maxBytes THEN LOOP;
IF ~TestPupChecksum[b] THEN LOOP;
PrincOpsUtils.LongCopy[to: data, from: @b.byte, nwords: (bytes + 1)/2];
IF source.net = Pup.nullNet THEN source.net ← b.source.net;
IF source.host = Pup.nullHost THEN source.host ← b.source.host;
IF source.socket = Pup.nullSocket THEN source.socket ← b.source.socket;
last ← in;
RETURN[bytes, b.id, b.type];
ENDLOOP;
};
SetPupChecksum: PROC [b: PupBuffer.PupBufferPointer] = {
words: NAT = PupBuffer.WordsWithoutChecksum[b.byteLength];
checksumLoc: LONG POINTER TO Endian.HWORD ← @b.byteLength + words;
checksumLoc^ ← Checksum.ComputeChecksum[0, words, @b.byteLength];
};
TestPupChecksum: PROC [b: PupBuffer.PupBufferPointer] RETURNS [BOOL] = {
words: NAT = PupBuffer.WordsWithoutChecksum[b.byteLength];
checksumLoc: LONG POINTER TO Endian.HWORD ← @b.byteLength + words;
hisChecksum: Endian.HWORD ← checksumLoc^;
checksum: Endian.HWORD;
IF hisChecksum = PupBuffer.noChecksum THEN RETURN[TRUE];
checksum ← Checksum.ComputeChecksum[0, words, @b.byteLength];
RETURN[checksum = hisChecksum];
};
Kludgery to use XNS protocols to learn our Pup Host number
PacketType: TYPE = MACHINE DEPENDENT { pupAddrTransPacket(4), (LAST[BYTE]) };
NSBufferPointer: TYPE = LONG POINTER TO RECORD [
ovh: CommBuffer.Overhead,
checksum: Endian.HWORD,
pktLength: Endian.HWORD, -- in bytes, includes header
transportControl: BYTE,
packetType: MACHINE DEPENDENT { pupAddrTransPacket(4), (LAST[BYTE]) },
destination: XNS.Address,
source: XNS.Address,
id: Endian.FWORD,
code: MACHINE DEPENDENT { request(1), response(2), error(3), (LAST[Endian.HWORD]) },
body: SELECT OVERLAID * FROM
request => [request: XNS.Host],
response => [response: Pup.Address],
ENDCASE ];
pupAddressTranslation: XNS.Socket ← LOOPHOLE[9];
myNsSocket: XNS.Socket ← LOOPHOLE[Basics.LowHalf[ProcessorFace.GetClockPulses[]]];
id: Endian.FWORDLOOPHOLE[ProcessorFace.GetClockPulses[]];
GetMyPupHostNumber: PROC RETURNS [Pup.Host] = {
nsBuffer: NSBufferPointer ← LOOPHOLE[b];
oldB: PupBuffer.PupBufferPointer ← b;
GermPrivate.ShowMP[MPCodes.germFindingPupHostNumber];
DO
start: LONG CARDINAL = SetTimeout[30];
EthernetFace.TurnOff[ether];
EthernetFace.TurnOn[ether, 0, 0];
nsBuffer.pktLength ← (15+6)*Basics.bytesPerWord;
nsBuffer.transportControl ← 0;
nsBuffer.packetType ← pupAddrTransPacket;
nsBuffer.destination ← [XNS.unknownNet, XNS.broadcastHost, pupAddressTranslation];
nsBuffer.source ← [XNS.unknownNet, myEtherHost, myNsSocket];
nsBuffer.id ← id;
nsBuffer.code ← request;
nsBuffer.request ← myEtherHost;
b.ovh.encap.ethernetDest ← XNS.broadcastHost;
b.ovh.encap.ethernetType ← xns;
SetNSChecksum[nsBuffer];
SendBuffer[nsBuffer.pktLength];
UNTIL TimeoutHasExpired[start] DO
EthernetFace.QueueInput[
ether,
@b.ovh.encap + CommDriverType.ethernetEncapsulationOffset,
bufferBytes,
longIocb];
UNTIL EthernetFace.GetStatusAndLength[longIocb].status # pending DO
IF TimeoutHasExpired[start] THEN {
EthernetFace.TurnOff[ether];
EthernetFace.TurnOn[ether, 0, 0];
EXIT; };
ENDLOOP;
IF EthernetFace.GetStatusAndLength[longIocb].status # ok THEN LOOP;
IF HostsDiffer[b.ovh.encap.ethernetDest, myEtherHost] THEN LOOP;
IF b.ovh.encap.ethernetType # xns THEN LOOP;
IF HostsDiffer[nsBuffer.destination.host, myEtherHost] THEN LOOP;
IF nsBuffer.id # id THEN LOOP;
IF nsBuffer.code = error THEN GermPrivate.ShowMP[MPCodes.serverSaysNoPupAddress];
IF nsBuffer.code # response THEN LOOP;
RETURN[nsBuffer.response.host];
ENDLOOP;
ENDLOOP;
};
Timers - From BootChannelEther
pulsesPerSecond: CARDINAL = Basics.LongDiv[100*1000000, ProcessorFace.microsecondsPerHundredPulses];
SetTimeout: PROC [timeout: CARDINAL --seconds--] RETURNS [timer: LONG CARDINAL] = {
RETURN [ProcessorFace.GetClockPulses[] + Basics.LongMult[timeout, pulsesPerSecond]];
};
TimeoutHasExpired: PROC [timer: LONG CARDINAL] RETURNS [BOOL] = {
RETURN [LOOPHOLE[ProcessorFace.GetClockPulses[]-timer, INT] >= 0];
};
SetNSChecksum: PROC [b: NSBufferPointer] = {
words: NAT = (b.pktLength-1)/Endian.bytesPerHWord;
b.checksum ← Checksum.ComputeChecksum[0, words, @b.pktLength];
};
TestNSChecksum: PROC [b: NSBufferPointer] RETURNS [BOOL] = {
words: NAT = (b.pktLength-1)/Endian.bytesPerHWord;
hisChecksum: Endian.HWORD ← Checksum.ComputeChecksum[0, words, @b.pktLength];
IF b.checksum = 0FFFFH THEN RETURN[TRUE];
IF b.checksum = hisChecksum THEN RETURN[TRUE];
RETURN[FALSE];
};
HostsDiffer: PROC [a, b: XNS.Host] RETURNS [BOOL] = {
Oops. Dorados don't have multi word compare in microcode.
IF a.a # b.a THEN RETURN[TRUE];
IF a.b # b.b THEN RETURN[TRUE];
IF a.c # b.c THEN RETURN[TRUE];
IF a.d # b.d THEN RETURN[TRUE];
IF a.e # b.e THEN RETURN[TRUE];
IF a.f # b.f THEN RETURN[TRUE];
RETURN[FALSE];
};
}.