MiniEthernetDriverDLion.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
BLyon on: March 12, 1981 4:37 PM
Sturgis: fixing GetEthernetHostNumber: September 23, 1983 2:18 pm
Birrell, October 31, 1983 3:34 pm
Russ Atkinson (RRA) March 14, 1985 5:08:09 pm PST
DIRECTORY
Basics USING [bytesPerWord, LongMult, LongDiv],
BufferDefs USING [Buffer, OisBuffer, PupBuffer, PupBufferObject],
Checksums USING [ComputeChecksum],
DriverTypes USING [Encapsulation, EthernetPacketType],
EthernetFace USING [AddCleanup, ControlBlock, DeviceHandle, GetNextDevice, GetPacketLength, GetStatus, GlobalStatePtr, globalStateSize, HostNumber, nullDeviceHandle, QueueInput, QueueOutput, RemoveCleanup, TurnOff, TurnOn],
MiniEthernetDefs USING [timedOut],
OISCPTypes USING [bytesPerPktHeader, PacketType, BufferBody],
PrincOpsUtils USING [LongCopy, LowHalf],
ProcessorFace USING [GetClockPulses, microsecondsPerHundredPulses, ProcessorID, processorID, SetMP],
PupTypes USING [allHosts, Pair, PupAddress, PupHostID, PupNetID, PupSocketID, PupType],
NSAddress USING [broadcastHostNumber, HostNumber, nullNetworkNumber, NetworkAddress, ProcessorID, SocketNumber];
MiniEthernetDriverDLion:
PROGRAM
IMPORTS Basics, Checksums, EthernetFace, PrincOpsUtils, ProcessorFace
EXPORTS MiniEthernetDefs
SHARES BufferDefs =
{ OPEN EthernetFace;
active: BOOL ← FALSE;
last: {in,out,reset};
knowBoardLocation: BOOL ← FALSE;
myID: NSAddress.ProcessorID ← LOOPHOLE[ProcessorFace.processorID];
myHost: PupTypes.PupHostID;
myNet: PupTypes.PupNetID;
hisPupHost: PupTypes.PupHostID;
hisEthernetAddress: NSAddress.HostNumber;
Global buffer for actual data transfers.
b: BufferDefs.PupBuffer;
pupBufferSize: CARDINAL;
ether: DeviceHandle;
longIocb: ControlBlock;
global: GlobalStatePtr ← LOOPHOLE[LONG[NIL]];
wordsPerPupHeader: CARDINAL = 11; -- includes checksum, not encapsulaton?
bytesPerPupHeader: CARDINAL = wordsPerPupHeader*2;
BoardLocationUnknown: PUBLIC ERROR = CODE;
DriverNotActive: PUBLIC ERROR = CODE;
BufferOverflow: PUBLIC ERROR = CODE;
ReturnToStrangePlace: PUBLIC ERROR = CODE;
DriverNeedsSomeGlobalStorage: PUBLIC ERROR = CODE;
DontKnowHowToTranslateThings: ERROR = CODE;
ActivateDriver:
PUBLIC
PROC [dataBuffer:
LONG
POINTER, length:
CARDINAL, iocb:
LONG
POINTER, avoidCleanup:
BOOL ←
FALSE]
RETURNS [
BOOL] = {
Note: dataBuffer must be quad word aligned
encapsulationOffset:
CARDINAL =
LOOPHOLE[
@LOOPHOLE[0, POINTER TO BufferDefs.PupBufferObject].encapsulation];
IF ~FindTheBoard[] THEN RETURN[FALSE];
knowBoardLocation ← TRUE;
ProcessorFace.SetMP[816--MPCodes.germEthernetDriverStarting--];
Beware of funny alignment constraints
b ← dataBuffer-encapsulationOffset+3;
pupBufferSize ← length-3-SIZE[DriverTypes.Encapsulation];
longIocb ← iocb;
[host: myHost, net: myNet] ← GetEthernet1Address[];
IF ~avoidCleanup THEN AddCleanup[ether];
allocate global storage here
TurnOn[ether,myID,0,0,global];
active ← TRUE;
last ← reset;
RETURN[TRUE];
};
KillDriver:
PUBLIC
PROC [avoidCleanup:
BOOL ←
FALSE] = {
TurnOff[ether];
IF ~avoidCleanup THEN RemoveCleanup[ether];
knowBoardLocation ← FALSE;
};
FindTheBoard:
PROC
RETURNS [
BOOL] = {
RETURN[(ether ← GetNextDevice[nullDeviceHandle])#nullDeviceHandle];
};
SendPacket:
PUBLIC
PROC [dest: PupTypes.PupAddress, me: PupTypes.PupSocketID, type: PupTypes.PupType, id: PupTypes.Pair, data:
LONG
POINTER, bytes:
CARDINAL] = {
IF ~active THEN ERROR DriverNotActive;
IF dest.host=PupTypes.allHosts
THEN
b.encapsulation.ethernetDest ← NSAddress.broadcastHostNumber
ELSE IF dest.host=hisPupHost THEN b.encapsulation.ethernetDest ← hisEthernetAddress
ELSE ERROR DontKnowHowToTranslateThings;
b.pupType ← type;
b.pupID ← id;
b.dest ← dest;
b.source ← [myNet, myHost, me];
SendPup[data, bytes];
};
following by analogy from MiniOISCPDriverImpl.SendPacket of APilot100
ByteCount: TYPE = CARDINAL;
SendOisPacket:
PROC[ dataLength: ByteCount, type: OISCPTypes.PacketType, sourceSocket: NSAddress.SocketNumber, dest: NSAddress.NetworkAddress] = {
oisBuf: BufferDefs.OisBuffer ← LOOPHOLE[b];
oisBuf.encapsulation.ethernetDest ← dest.host;
oisBuf.encapsulation.ethernetType ← ois;
oisBuf.ois.pktLength ← dataLength + OISCPTypes.bytesPerPktHeader;
oisBuf.ois.transCntlAndPktTp ← [[FALSE, 0, 0], type];
oisBuf.ois.source ← [net: NSAddress.nullNetworkNumber, host: myID, socket: sourceSocket];
oisBuf.ois.destination ← dest;
SetOisChecksum[oisBuf];
SendBuffer[WdsPerEthernetPacket[oisBuf.ois.pktLength]];
};
ReturnPacket:
PUBLIC
PROC [type: PupTypes.PupType, data:
LONG
POINTER, bytes:
CARDINAL] = {
temp: PupTypes.PupAddress;
IF ~active THEN ERROR DriverNotActive;
IF last#in THEN ERROR ReturnToStrangePlace;
b.encapsulation.ethernetDest ← b.encapsulation.ethernetSource;
b.pupType ← type;
temp ← b.dest; b.dest ← b.source; b.source ← temp;
SendPup[data, bytes];
};
SendPup:
PROC [data:
LONG
POINTER, bytes:
CARDINAL] = {
pupWords: CARDINAL = wordsPerPupHeader+(bytes+1)/2;
IF pupWords>pupBufferSize THEN ERROR BufferOverflow;
PrincOpsUtils.LongCopy[to: @b.pupWords, from: data, nwords: (bytes+1)/2];
b.pupLength ← bytesPerPupHeader+bytes;
b.pupTransportControl ← 0;
b.encapsulation.ethernetType ← pup;
SetPupChecksum[b];
last ← out;
SendBuffer[pupWords];
};
SendBuffer:
PROC [words:
CARDINAL] = {
minWordsPerEthernetPacket: CARDINAL = (64/2)-2; --*** Should move to DriverTypes
b.encapsulation.ethernetSource ← myID;
QueueOutput[ ether, @b.encapsulation, MAX[SIZE[DriverTypes.Encapsulation]+words, minWordsPerEthernetPacket], longIocb];
THROUGH [0..
LAST[
CARDINAL])
DO
IF GetStatus[longIocb]#pending THEN EXIT;
REPEAT
FINISHED => {
TurnOff[ether];
TurnOn[ether,myID,0,0,global];
};
ENDLOOP;
};
modified in analogy to APilot100 PilotCommUtilPup.GetEthernetHostNumber
translationRequest: CARDINAL = 1;
translationResponse: CARDINAL = 2;
GetEthernet1Address:
PROC
RETURNS [net: PupTypes.PupNetID, host: PupTypes.PupHostID] = {
oisBuf: BufferDefs.OisBuffer ← LOOPHOLE[b];
Request:
TYPE =
MACHINE
DEPENDENT
RECORD [
id0, id1: CARDINAL, arg: WORD, who: NSAddress.ProcessorID];
request: LONG POINTER TO Request = LOOPHOLE[@oisBuf.ois.oisWords[0]];
Reply:
TYPE =
MACHINE
DEPENDENT
RECORD [
id0, id1: CARDINAL, arg: WORD, answer: PupTypes.PupAddress];
reply: LONG POINTER TO Reply = LOOPHOLE[@oisBuf.ois.oisWords[0]];
pulsesPerSec:
LONG
CARDINAL ← Basics.LongMult[1000, Basics.LongDiv[100000, ProcessorFace.microsecondsPerHundredPulses]+1];
This is a conservative (high) estimate of the # of pulses in a second
timeoutPulses: LONG CARDINAL ← 0;
i: CARDINAL ← 1;
mySocket: NSAddress.SocketNumber ← LOOPHOLE[PrincOpsUtils.LowHalf[LOOPHOLE[ProcessorFace.GetClockPulses[]]]];
From the new OISCPCOnstants
pupAddressTranslation: NSAddress.SocketNumber ← LOOPHOLE[9];
pupAddrTransPacket: OISCPTypes.PacketType ← LOOPHOLE[6];
THROUGH [0..15]
DO timeoutPulses ← timeoutPulses + pulsesPerSec;
ENDLOOP;
RRA: Since we don't trust the multiplication to stay within the bounds supported by microcode, we do this in a simple-minded loop.
DO
startPulses: LONG CARDINAL = ProcessorFace.GetClockPulses[];
TurnOff[ether];
TurnOn[ether, myID, 0, 0, global];
i ← i + 1;
request^ ← [i, i, translationRequest, myID];
SendOisPacket[
SIZE[Request]*Basics.bytesPerWord,
pupAddrTransPacket,
mySocket,
[NSAddress.nullNetworkNumber, NSAddress.broadcastHostNumber, pupAddressTranslation]
];
WHILE (ProcessorFace.GetClockPulses[] - startPulses) <= timeoutPulses
DO
oisBuf.encapsulation.ethernetType ← LOOPHOLE[0];
QueueInput[
ether, @b.encapsulation, pupBufferSize+SIZE[DriverTypes.Encapsulation], longIocb];
WHILE (ProcessorFace.GetClockPulses[] - startPulses) <= timeoutPulses
DO
IF GetStatus[longIocb] = pending THEN LOOP;
IF GetStatus[longIocb] # ok THEN EXIT;
IF oisBuf.encapsulation.ethernetType # ois THEN EXIT;
IF oisBuf.ois.destination.host # myID THEN EXIT;
IF oisBuf.ois.source.socket # pupAddressTranslation THEN EXIT;
IF oisBuf.ois.destination.socket # mySocket THEN EXIT;
IF GetPacketLength[longIocb] < WdsPerEthernetPacket[oisBuf.ois.pktLength] THEN EXIT;
IF oisBuf.ois.pktLength < OISCPTypes.bytesPerPktHeader THEN EXIT;
IF (reply.arg # translationResponse) THEN EXIT;
IF ~TestOisChecksum[oisBuf] THEN EXIT;
TurnOff[ether];
RETURN [net: reply.answer.net, host: reply.answer.host]
ENDLOOP;
ENDLOOP;
ENDLOOP;
};
RecvPacket:
PUBLIC
PROC [source:
LONG
POINTER
TO PupTypes.PupAddress, me: PupTypes.PupSocketID, data:
LONG
POINTER, words:
CARDINAL, timeout:
PROC
RETURNS [
BOOL]]
RETURNS [bytes:
CARDINAL, id: PupTypes.Pair, type: PupTypes.PupType] = {
IF ~active THEN ERROR DriverNotActive;
DO
QueueInput[
ether, @b.encapsulation,
pupBufferSize+SIZE[DriverTypes.Encapsulation], longIocb];
UNTIL GetStatus[longIocb]#pending
DO
IF timeout[]
THEN {
TurnOff[ether];
TurnOn[ether,myID,0,0,global];
last ← reset;
RETURN[MiniEthernetDefs.timedOut,[0,0],LOOPHOLE[0]];
};
ENDLOOP;
IF GetStatus[longIocb]#ok THEN LOOP;
IF b.encapsulation.ethernetType=translation
THEN
{ -- See EthernetDriver
OPEN raw: LOOPHOLE[b, BufferDefs.Buffer];
AddressPair:
TYPE =
MACHINE
DEPENDENT
RECORD [
big: NSAddress.ProcessorID,
small: PupTypes.PupHostID, filler: [0..377B]];
request: LONG POINTER TO AddressPair = LOOPHOLE[@raw.rawWords[1]];
translationRequest: CARDINAL = 10101B;
translationResponse: CARDINAL = 7070B;
IF raw.rawWords[0]#translationRequest THEN LOOP;
IF request.small#myHost THEN LOOP;
b.encapsulation.ethernetDest ← b.encapsulation.ethernetSource;
raw.rawWords[0] ← translationResponse;
request.big ← myID;
SendBuffer[1+SIZE[AddressPair]];
LOOP;
};
IF b.encapsulation.ethernetType#pup THEN LOOP;
should check words that arrived against pupLength?
check here for routing table if we ever get that complicated
IF myNet#0 AND myNet#b.dest.net AND b.dest.net#0 THEN LOOP;
IF myHost#b.dest.host THEN LOOP; -- Can't recv broadcast
IF me#b.dest.socket THEN LOOP;
IF source.net#0 AND source.net#b.source.net AND b.source.net#0 THEN LOOP;
IF source.host#0 AND source.host#b.source.host THEN LOOP;
IF source.socket#[0,0] AND source.socket#b.source.socket THEN LOOP;
IF ~TestPupChecksum[b] THEN LOOP;
IF myNet=0 THEN myNet ← b.dest.net;
IF (b.pupLength-bytesPerPupHeader)>words*2 THEN LOOP;
bytes ← b.pupLength-bytesPerPupHeader;
PrincOpsUtils.LongCopy[to: data, from: @b.pupWords, nwords: (bytes+1)/2];
IF source.net=0 THEN source.net ← b.source.net;
IF source.host=0 THEN source.host ← b.source.host;
IF source.socket=[0,0] THEN source.socket ← b.source.socket;
last ← in;
hisPupHost ← source.host;
hisEthernetAddress ← b.encapsulation.ethernetSource;
RETURN[bytes,b.pupID,b.pupType];
ENDLOOP;
};
SetPupChecksum:
PUBLIC
PROC [b: BufferDefs.PupBuffer] = {
buf: LONG POINTER = @b.pupLength;
size: CARDINAL = (b.pupLength-1)/2;
(buf+size)^ ← Checksums.ComputeChecksum[cs: 0, nWords: size, p: buf];
};
TestPupChecksum:
PUBLIC
PROC [b: BufferDefs.PupBuffer]
RETURNS [
BOOL] = {
buf: LONG POINTER = @b.pupLength;
size: CARDINAL = (b.pupLength-1)/2;
cs: CARDINAL = (buf+size)^;
RETURN[cs=177777B OR cs=Checksums.ComputeChecksum[cs: 0, nWords: size, p: buf]];
};
following procedures in anaogy to apilot100 minioiscpdriverimpl.mesa
SetOisChecksum:
PROC [b: BufferDefs.OisBuffer] = {
b.ois.checksum ← Checksums.ComputeChecksum[
0, (b.ois.pktLength + 1 - 2)/2, @b.ois.pktLength]};
TestOisChecksum:
PROC [b: BufferDefs.OisBuffer]
RETURNS [
BOOL] = {
RETURN[
b.ois.checksum = Checksums.ComputeChecksum[
0, (b.ois.pktLength + 1 - 2)/2, @b.ois.pktLength]
OR b.ois.checksum = 177777B]};
WdsPerEthernetPacket:
PROC [bodyBytes:
CARDINAL]
RETURNS[wds:
CARDINAL] = {
minWdsPerEthernetPacket: CARDINAL = ((64-4)/Basics.bytesPerWord);
wds ←
SIZE[DriverTypes.Encapsulation] +
(bodyBytes + Basics.bytesPerWord-1)/Basics.bytesPerWord;
wds ← MAX[wds, minWdsPerEthernetPacket]};
IF globalStateSize # 0 THEN ERROR DriverNeedsSomeGlobalStorage;
}.
LOG
Time: January 29, 1980 6:00 PM By: Dalal Action: fixed Core Software AR 2658.
Time: February 7, 1980 11:18 PM By: Knutsen Action: ioPage now a long pointer. Initialize host socket right. INTEGER Divide
Time: February 9, 1980 2:43 PM By: Knutsen/HGM Action: fix clobber of 4 words before buffer
Time: April 20, 1980 4:03 PM By: HGM Action: Faceification, add ReturnPacket and timedOut
Time: April 28, 1980 7:36 PM By: HGM Action: Fix QueueOutput bug (again!) in RecvPacket
Time: July 2, 1980 5:44 PM By: HGM Action: Delete "DriverTypes." in front of pupEthernetPacket
Time: August 20, 1980 2:23 PM By: BLyon Action: renamed EthernetFace to EthernetOneFace.
Time: November 20, 1980 By: HGM+Forrest Action: DLion/EthernetTwo.
Time: February 9, 1981 12:36 PM By: Luniewski/BLyon Action: XWire => Ethernet. INcrease fudge in GetEthernet1Address to 20.
Time: July 25, 1983 3:45 pm: Sturgis: begin repairs to GetEtherNetHostNumber
RTE: July 27, 1983 3:23 pm: SendOISPacket should not test for active driver, as it is being called during the period when driver is begin activated.
Time: October 28, 1983 3:32 pm: Birrell: conversion to 5.0
Russ Atkinson (RRA) March 14, 1985 5:05:29 pm PST
GetEthernet1Address fixed to not send packets too quickly. There is a 15 second timeout, which is calculated entirely in microcode.