PilotCommUtil.mesa - implementation module for Pilot provided utilities
Copyright © 1985 by Xerox Corporation. All rights reserved.
Russ Atkinson (RRA) October 17, 1985 6:08:37 pm PDT
Hal Murray November 1, 1985 3:23:45 pm PST
DIRECTORY
Basics USING [bytesPerWord, LongMult, LongDiv],
BufferDefs USING [OisBuffer],
Checksums USING [ComputeChecksum],
CommunicationInternal USING [],
CommUtilDefs USING [],
DriverDefs USING [GetInputBuffer, ReturnFreeBuffer],
DriverTypes USING [Encapsulation],
EthernetFace USING [ControlBlock, DeviceHandle, GlobalStatePtr, globalStateSize, GetNextDevice, GetPacketLength, GetStatus, nullDeviceHandle, QueueInput, QueueOutput, TurnOff, TurnOn],
EthernetOneFace USING [DeviceHandle, GetEthernet1Address, GetNextDevice, nullDeviceHandle],
MPCodes USING [findPupAddress, emptyMP],
NSAddress USING [broadcastHostNumber, HostNumber, nullNetworkNumber, NetworkAddress, ProcessorID, SocketNumber],
OISCPTypes USING [bytesPerPktHeader, PacketType, BufferBody],
PrincOpsUtils USING [Copy, LongCopy, LowHalf],
Process USING [MsecToTicks, SecondsToTicks, Ticks],
ProcessorFace USING [GetClockPulses, microsecondsPerHundredPulses, processorID, SetMP],
PupDefs USING [], --EXPORTS Time Conversion stuff
PupStream USING [Tocks], --EXPORTS Time Conversion stuff
PupTypes USING [PupAddress, PupHostID],
VM USING [AddressForPageNumber, Allocate, Free, Interval, lowCore, PageNumberForAddress, PagesForWords, Pin];
PilotCommUtil: MONITOR
IMPORTS Checksums, DriverDefs, EthernetFace, EthernetOneFace, PrincOpsUtils, Process, ProcessorFace, VM
EXPORTS CommunicationInternal, CommUtilDefs, DriverDefs, PupDefs, PupStream
SHARES BufferDefs
= {
Heap nodes. All these are currently resident, and in first 64K
MixedIocbSize: ERROR = CODE;
IOCB allocation.
iocbSize: CARDINAL ← 0;
IocbType: TYPE = RECORD[
next: LONG POINTER TO IocbType,
rest: SEQUENCE COMPUTED CARDINAL OF WORD];
freeIocb: LONG POINTER TO IocbType ← NIL;
AllocateIocb: PUBLIC ENTRY PROC [nwords: CARDINAL] RETURNS [i: LONG POINTER] = {
IF iocbSize = 0 THEN iocbSize ← nwords;
IF iocbSize # nwords THEN Glitch[MixedIocbSize];
IF freeIocb # NIL
THEN { i ← freeIocb; freeIocb ← freeIocb.next }
ELSE i ← VM.lowCore.NEW[IocbType[iocbSize-SIZE[IocbType[0]]]];
};
FreeIocb: PUBLIC ENTRY PROC [iocbPtr: LONG POINTER] = {
i: LONG POINTER TO IocbType = LOOPHOLE[iocbPtr];
i.next ← freeIocb; freeIocb ← i;
};
Clumps of pages
AllocateBuffers: PUBLIC PROC [nwords: CARDINAL] RETURNS [base: LONG POINTER] = {
interval: VM.Interval = VM.Allocate[VM.PagesForWords[nwords]];
VM.Pin[interval];
base ← VM.AddressForPageNumber[interval.page];
};
FreeBuffers: PUBLIC PROC [base: LONG POINTER, nwords: CARDINAL] = {
VM.Free[[page: VM.PageNumberForAddress[base], count: VM.PagesForWords[nwords]]];
};
Other goodies
Zero: PUBLIC PROC [p: POINTER, l: CARDINAL] = {
IF l = 0 THEN RETURN;
p^ ← 0;
PrincOpsUtils.Copy[from: p, to: p + 1, nwords: l - 1];
};
TimeCoversion for Pup package
implement with Process.Tick = Tock
Code depends upon Process.MsecToTicks corectly
rounding up the argument before caculating the result,
and SecondsToTicks returning LAST[Ticks] for large seconds.
TockConversionTroubles: PUBLIC ERROR = CODE;
SecondsToTocks: PUBLIC SAFE PROC [t: CARDINAL] RETURNS [PupStream.Tocks] = CHECKED {
tks: Process.Ticks = Process.SecondsToTicks[t];
IF tks = LAST[Process.Ticks] THEN ERROR TockConversionTroubles;
RETURN[[tks]];
};
this assumes that a tock is longer than an millisecond
MsToTocks: PUBLIC SAFE PROC [t: CARDINAL] RETURNS [PupStream.Tocks] = CHECKED {
IF t = 0 THEN ERROR TockConversionTroubles;
RETURN[[Process.MsecToTicks[t]]];
};
Communication error handler
errorHandler: PROC [ERROR] ← DefaultErrorHandler;
CaptureErrors: PUBLIC PROC [proc: PROC [ERROR]] = {errorHandler ← proc};
DefaultErrorHandler: PROC [why: ERROR] = { SIGNAL LOOPHOLE[why, SIGNAL] };
Glitch: PUBLIC PROC [why: ERROR] = {DO errorHandler[why] ENDLOOP};
FriendOfCopyLong: PUBLIC PROC [from: LONG POINTER, nwords: CARDINAL, to: LONG POINTER] = {
PrincOpsUtils.LongCopy[from, nwords, to];
};
myHostNumber: CARDINAL ← 0;
SmashMyHostNumber: PUBLIC ENTRY PROC [new: CARDINAL] = {
myHostNumber ← new;
};
GetEthernetHostNumber: PUBLIC ENTRY PROC RETURNS [CARDINAL] = {
ether: EthernetOneFace.DeviceHandle;
IF myHostNumber#0 THEN RETURN[myHostNumber]; -- easy known case
if the machine has a real ethernetOne then get the address from the device
ether ← EthernetOneFace.GetNextDevice[EthernetOneFace.nullDeviceHandle];
IF ether#EthernetOneFace.nullDeviceHandle THEN {
net, host: [0..256);
[net, host] ← EthernetOneFace.GetEthernet1Address[ether];
RETURN[myHostNumber ← host]; };
Hard case: there is no EthernetOne, so ask the name servers for our address.
ProcessorFace.SetMP[MPCodes.findPupAddress];
myHostNumber ← GetPupHostNumber[];
ProcessorFace.SetMP[MPCodes.emptyMP];
RETURN[myHostNumber];
};
Snitched from Germ (MiniEthernetDriverDLion) after pitching OISCP. HGM, November 1, 1985
global: EthernetFace.GlobalStatePtr ← LOOPHOLE[LONG[NIL]];
wordsPerPupHeader: CARDINAL = 11; -- includes checksum, not encapsulaton
bytesPerPupHeader: CARDINAL = wordsPerPupHeader*2;
translationRequest: CARDINAL = 1;
translationResponse: CARDINAL = 2;
GetPupHostNumber: PROC RETURNS [host: PupTypes.PupHostID] = {
oisBuf: BufferDefs.OisBuffer ← LOOPHOLE[DriverDefs.GetInputBuffer[]];
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];
minWordsPerEthernetPacket: CARDINAL = (64/2)-2; --*** Should move to DriverTypes
myID: NSAddress.ProcessorID ← LOOPHOLE[ProcessorFace.processorID];
ether: EthernetFace.DeviceHandle ← EthernetFace.GetNextDevice[EthernetFace.nullDeviceHandle];
IF ether = EthernetFace.nullDeviceHandle THEN ERROR No10MBDriverAndNo3MBDriver;
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[];
EthernetFace.TurnOff[ether];
EthernetFace.TurnOn[ether, LOOPHOLE[myID], 0, 0, global];
i ← i + 1;
request^ ← [i, i, translationRequest, myID];
oisBuf.encapsulation.ethernetDest ← NSAddress.broadcastHostNumber;
oisBuf.encapsulation.ethernetType ← ois;
oisBuf.ois.pktLength ← OISCPTypes.bytesPerPktHeader + SIZE[Request, Basics.bytesPerWord];
oisBuf.ois.transCntlAndPktTp ← [[FALSE, 0, 0], pupAddrTransPacket];
oisBuf.ois.source ← [
net: NSAddress.nullNetworkNumber,
host: myID,
socket: mySocket];
oisBuf.ois.destination ← [
net: NSAddress.nullNetworkNumber,
host: NSAddress.broadcastHostNumber,
socket: pupAddressTranslation];
SetOisChecksum[oisBuf];
oisBuf.encapsulation.ethernetSource ← myID;
EthernetFace.QueueOutput[
ether,
@oisBuf.encapsulation,
MAX[SIZE[DriverTypes.Encapsulation]+WdsPerEthernetPacket[oisBuf.ois.pktLength],
minWordsPerEthernetPacket],
oisBuf.iocbChain];
WHILE (ProcessorFace.GetClockPulses[] - startPulses) <= timeoutPulses DO
IF EthernetFace.GetStatus[oisBuf.iocbChain]#pending THEN EXIT;
REPEAT FINISHED => {
EthernetFace.TurnOff[ether];
EthernetFace.TurnOn[ether, LOOPHOLE[myID], 0, 0, global]; };
ENDLOOP;
WHILE (ProcessorFace.GetClockPulses[] - startPulses) <= timeoutPulses DO
oisBuf.encapsulation.ethernetType ← LOOPHOLE[0];
EthernetFace.QueueInput[
ether,
@oisBuf.encapsulation,
oisBuf.length,
oisBuf.iocbChain];
WHILE (ProcessorFace.GetClockPulses[] - startPulses) <= timeoutPulses DO
IF EthernetFace.GetStatus[oisBuf.iocbChain] = pending THEN LOOP;
IF EthernetFace.GetStatus[oisBuf.iocbChain] # 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 EthernetFace.GetPacketLength[oisBuf.iocbChain] < 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;
EthernetFace.TurnOff[ether];
DriverDefs.ReturnFreeBuffer[oisBuf];
RETURN[reply.answer.host]
ENDLOOP;
ENDLOOP;
ENDLOOP;
};
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];
};
No10MBDriverAndNo3MBDriver: PUBLIC ERROR = CODE;
DriverNeedsSomeGlobalStorage: PUBLIC ERROR = CODE;
IF EthernetFace.globalStateSize # 0 THEN ERROR DriverNeedsSomeGlobalStorage;
}....