-- Copyright (C) 1983 by Xerox Corporation. All rights reserved.
-- SimplePUPIOEthernetOneImpl.mesa, HGM, 12-Dec-83 0:59:23, fixup timeouts
-- 25-May-83 11:07:42 by DKnutsen
-- This module implements SimplePUPIO using the EthernetOne (NOT the Ethernet).
-- NOTE: The modules SimpleNSIOEthernetImpl and SimpleNSIOEthernetOneImpl are similar to this module. IF YOU MAKE A CHANGE HERE, it perhaps should be made to them too.
DIRECTORY
BootChannel USING [Result],
Device USING [Type],
DeviceTypes USING [ethernetOne],
DriverTypes USING [Encapsulation, ethernetOneEncapsulationOffset],
Environment USING [Byte, first64K],
EthernetOneFace USING [
AddCleanup, ControlBlock, controlBlockSize, DeviceHandle, GetEthernet1Address,
GetNextDevice, GetStatus, GlobalStatePtr, globalStateSize, nullDeviceHandle,
QueueInput, QueueOutput, RemoveCleanup, TurnOff, TurnOn],
GermOps USING [GermWorldError],
Inline USING [LongCOPY, LowHalf],
PilotMP USING [cGermERROR],
PupTypes USING [
BufferBody, maxDataWordsPerGatewayPup, Pair, PupAddress, PupHostID, PupNetID,
PupSocketID, PupType],
ResidentHeap USING [FreeNode, MakeNode],
System USING [GetClockPulses, Microseconds, MicrosecondsToPulses, Pulses],
SimplePUPIO USING [
ByteCount, cantHandleDevice, Cleanups, timedOutBytes, timedOutResult];
SimplePUPIOEthernetOneImpl: PROGRAM
IMPORTS EthernetOneFace, GermOps, Inline, ResidentHeap, System
EXPORTS SimplePUPIO SHARES GermOps =
BEGIN
LevelZeroPacket: TYPE = MACHINE DEPENDENT RECORD [
encapsulation(0): ethernetOne DriverTypes.Encapsulation,
pup(7): PupTypes.BufferBody];
ethernetOneHeaderOffset: CARDINAL = -- offset from start of encapsulation.
DriverTypes.ethernetOneEncapsulationOffset;
ethernetOneHeaderWds: CARDINAL = -- significant part of encapsulation.
SIZE[ethernetOne DriverTypes.Encapsulation] - ethernetOneHeaderOffset;
bufferAlign: CARDINAL = 0; -- align encapsulation.ethernetOneDest to this boundary..
bufferMod: CARDINAL = 4; -- .. modulo this.
slop: CARDINAL = 2 + 12; -- Hal says we need this
bufferSize: CARDINAL =
ethernetOneHeaderWds + PupTypes.maxDataWordsPerGatewayPup + slop;
b: LONG POINTER TO LevelZeroPacket;
last: {in, out, reset};
myHost: PupTypes.PupHostID;
myNet: PupTypes.PupNetID;
device: EthernetOneFace.DeviceHandle ← EthernetOneFace.nullDeviceHandle;
longIocb: EthernetOneFace.ControlBlock;
globalStorage: EthernetOneFace.GlobalStatePtr = LOOPHOLE[0];
wordsPerPupHeader: CARDINAL = 11;
bytesPerPupHeader: CARDINAL = wordsPerPupHeader*2;
--==============================
-- Public Procedures
--==============================
Finalize: PUBLIC PROCEDURE [cleanups: SimplePUPIO.Cleanups] =
BEGIN
IF device = EthernetOneFace.nullDeviceHandle THEN Error[]; -- not initialized
EthernetOneFace.TurnOff[device];
IF cleanups = doCleanup THEN EthernetOneFace.RemoveCleanup[device];
device ← EthernetOneFace.nullDeviceHandle;
[] ← ResidentHeap.FreeNode[Inline.LowHalf[longIocb]];
END;
GetEthernetHostNumber: PUBLIC PROCEDURE RETURNS [CARDINAL] = {
IF device = EthernetOneFace.nullDeviceHandle THEN Error[]; -- not initialized
RETURN[myHost]};
GetEthernetNetNumber: PUBLIC PROCEDURE RETURNS [CARDINAL] = {
IF device = EthernetOneFace.nullDeviceHandle THEN Error[]; -- not initialized
RETURN[myNet]};
GetRawBufferSize: PUBLIC PROCEDURE RETURNS [rawBufferSize: CARDINAL] = {
RETURN[bufferSize + bufferMod - 1]};
Initialize: PUBLIC PROCEDURE [
deviceType: Device.Type, deviceOrdinal: CARDINAL, rawBuffer: LONG POINTER,
cleanups: SimplePUPIO.Cleanups] RETURNS [result: BootChannel.Result] =
BEGIN
netNbr, hostNbr: Environment.Byte;
IF device # EthernetOneFace.nullDeviceHandle THEN Error[]; -- already initialized
IF deviceType # DeviceTypes.ethernetOne THEN
RETURN[SimplePUPIO.cantHandleDevice];
THROUGH [0..deviceOrdinal] DO
IF (device ← EthernetOneFace.GetNextDevice[device]) =
EthernetOneFace.nullDeviceHandle THEN
RETURN[SimplePUPIO.cantHandleDevice];
ENDLOOP;
longIocb ← @Environment.first64K[
ResidentHeap.MakeNode[EthernetOneFace.controlBlockSize, a4].node];
b ←
LOOPHOLE[((LOOPHOLE[rawBuffer, LONG CARDINAL] + bufferMod -
1)/bufferMod)*bufferMod + bufferAlign -
ethernetOneHeaderOffset];
[net: netNbr, host: hostNbr] ← EthernetOneFace.GetEthernet1Address[device];
myNet ← [netNbr];
myHost ← [hostNbr];
IF cleanups = doCleanup THEN EthernetOneFace.AddCleanup[device];
-- allocate global storage here if any required.
TurnOn[];
last ← reset;
RETURN[[ok[]]];
END;
ReceivePacket: PUBLIC PROCEDURE [
source: LONG POINTER TO PupTypes.PupAddress, me: PupTypes.PupSocketID,
data: LONG POINTER, maxWords: CARDINAL, timeout: System.Microseconds]
RETURNS [
result: BootChannel.Result, bytes: CARDINAL, id: PupTypes.Pair,
type: PupTypes.PupType] =
BEGIN
startTime: System.Pulses = System.GetClockPulses[];
IF device = EthernetOneFace.nullDeviceHandle THEN Error[]; -- not initialized
DO
EthernetOneFace.QueueInput[
device, @b.encapsulation + DriverTypes.ethernetOneEncapsulationOffset,
bufferSize, longIocb];
UNTIL EthernetOneFace.GetStatus[longIocb] # pending DO
IF TimeExpired[timeout: timeout, startTime: startTime] THEN
BEGIN
EthernetOneFace.TurnOff[device];
TurnOn[];
last ← reset;
RETURN[
SimplePUPIO.timedOutResult, SimplePUPIO.timedOutBytes, TRASH, TRASH];
END;
ENDLOOP;
IF EthernetOneFace.GetStatus[longIocb] # ok THEN LOOP;
IF b.encapsulation.ethernetOneType # pup THEN LOOP;
-- check here for routing table if we ever get that complicated
IF myNet # 0 AND myNet # b.pup.dest.net AND b.pup.dest.net # 0 THEN LOOP;
IF myHost # b.pup.dest.host THEN LOOP; -- Can't recv broadcast
IF me # b.pup.dest.socket THEN LOOP;
IF source.net # 0 AND source.net # b.pup.source.net AND b.pup.source.net # 0
THEN LOOP;
IF source.host # 0 AND source.host # b.pup.source.host THEN LOOP;
IF source.socket # [0, 0] AND source.socket # b.pup.source.socket THEN LOOP;
IF ~TestPupChecksum[@b.pup] THEN LOOP;
IF myNet = 0 THEN myNet ← b.pup.dest.net;
IF (b.pup.pupLength - bytesPerPupHeader) > maxWords*2 THEN --
LOOP; -- ignore packets larger than client wants
bytes ← b.pup.pupLength - bytesPerPupHeader;
Inline.LongCOPY[to: data, from: @b.pup.pupWords, nwords: (bytes + 1)/2];
IF source.net = 0 THEN source.net ← b.pup.source.net;
IF source.host = 0 THEN source.host ← b.pup.source.host;
IF source.socket = [0, 0] THEN source.socket ← b.pup.source.socket;
last ← in;
RETURN[[ok[]], bytes, b.pup.pupID, b.pup.pupType];
ENDLOOP;
END;
ReturnPacket: PUBLIC PROCEDURE [
type: PupTypes.PupType, data: LONG POINTER, bytes: CARDINAL]
RETURNS [result: BootChannel.Result] =
BEGIN
words: CARDINAL ← 2 + wordsPerPupHeader + (bytes + 1)/2;
temp: PupTypes.PupAddress;
IF device = EthernetOneFace.nullDeviceHandle THEN Error[]; -- not initialized
IF words > bufferSize THEN Error[]; -- buffer overflow.
IF last # in THEN Error[]; -- returned to strange place.
-- Don't use constructor since that clobbers preceding 4 words
b.encapsulation.ethernetOneDest ← b.encapsulation.ethernetOneSource;
b.encapsulation.ethernetOneSource ← myHost;
b.encapsulation.ethernetOneType ← pup;
b.pup.pupLength ← bytesPerPupHeader + bytes;
b.pup.pupTransportControl ← 0;
b.pup.pupType ← type;
temp ← b.pup.dest;
b.pup.dest ← b.pup.source;
b.pup.source ← temp;
Inline.LongCOPY[to: @b.pup.pupWords, from: data, nwords: (bytes + 1)/2];
SetPupChecksum[@b.pup];
last ← out;
EthernetOneFace.QueueOutput[
device, @b.encapsulation + DriverTypes.ethernetOneEncapsulationOffset, words,
longIocb];
THROUGH [0..LAST[CARDINAL]) DO
IF EthernetOneFace.GetStatus[longIocb] # pending THEN EXIT;
REPEAT FINISHED => BEGIN EthernetOneFace.TurnOff[device]; TurnOn[]; END;
ENDLOOP;
RETURN[[ok[]]];
END;
SendPacket: PUBLIC PROCEDURE [
dest: PupTypes.PupAddress, me: PupTypes.PupSocketID, type: PupTypes.PupType,
id: PupTypes.Pair, data: LONG POINTER, bytes: CARDINAL]
RETURNS [result: BootChannel.Result] =
BEGIN
words: CARDINAL ← 2 + wordsPerPupHeader + (bytes + 1)/2;
IF device = EthernetOneFace.nullDeviceHandle THEN Error[]; -- not initialized.
IF words > bufferSize THEN Error[]; -- buffer overflow
-- Don't use constructor since that clobbers preceding 5 words
-- (which are not really allocated).
b.encapsulation.ethernetOneDest ← dest.host;
b.encapsulation.ethernetOneSource ← myHost;
b.encapsulation.ethernetOneType ← pup;
b.pup.pupLength ← bytesPerPupHeader + bytes;
b.pup.pupTransportControl ← 0;
b.pup.pupType ← type;
b.pup.pupID ← id;
b.pup.dest ← dest;
b.pup.source ← [myNet, myHost, me];
Inline.LongCOPY[to: @b.pup.pupWords, from: data, nwords: (bytes + 1)/2];
SetPupChecksum[@b.pup];
last ← out;
EthernetOneFace.QueueOutput[
device, @b.encapsulation + DriverTypes.ethernetOneEncapsulationOffset, words,
longIocb];
THROUGH [0..LAST[CARDINAL]) DO
IF EthernetOneFace.GetStatus[longIocb] # pending THEN EXIT;
REPEAT FINISHED => {EthernetOneFace.TurnOff[device]; TurnOn[]};
ENDLOOP;
RETURN[[ok[]]];
END;
--==============================
-- Private Procedures
--==============================
Error: PROC = {GermOps.GermWorldError[PilotMP.cGermERROR]};
SetPupChecksum: PROCEDURE [b: LONG POINTER TO PupTypes.BufferBody] =
BEGIN
size: CARDINAL ← (b.pupLength - 1)/2;
checksumLoc: LONG POINTER ← @b.pupLength + size;
cs, t: CARDINAL;
p: LONG POINTER TO ARRAY [0..0) OF WORD = LOOPHOLE[@b.pupLength];
i: CARDINAL;
cs ← 0;
FOR i IN [0..size) DO
t ← cs + p[i];
cs ← (IF cs > t THEN t + 1 ELSE t);
cs ← (IF cs >= 100000B THEN cs*2 + 1 ELSE cs*2);
ENDLOOP;
IF cs = 177777B THEN cs ← 0;
checksumLoc↑ ← cs;
END;
TestPupChecksum: PROCEDURE [b: LONG POINTER TO PupTypes.BufferBody]
RETURNS [BOOLEAN] =
BEGIN
size: CARDINAL ← ((LOOPHOLE[b.pupLength - 1, CARDINAL])/2);
checksumLoc: LONG POINTER ← @b.pupLength + size;
cs, t: CARDINAL;
p: LONG POINTER TO ARRAY [0..0) OF WORD = LOOPHOLE[@b.pupLength];
i: CARDINAL;
IF checksumLoc↑ = 177777B THEN RETURN[TRUE];
cs ← 0;
FOR i IN [0..size) DO
t ← cs + p[i];
cs ← (IF cs > t THEN t + 1 ELSE t);
cs ← (IF cs >= 100000B THEN cs*2 + 1 ELSE cs*2);
ENDLOOP;
IF cs = 177777B THEN cs ← 0;
RETURN[checksumLoc↑ = cs];
END;
TimeExpired: PROC [timeout, startTime: System.Microseconds] RETURNS [BOOLEAN] =
BEGIN
IF timeout = LAST[System.Microseconds] THEN RETURN[FALSE];
RETURN[(System.GetClockPulses[] - startTime) > System.MicrosecondsToPulses[timeout]];
END;
TurnOn: PROC = {EthernetOneFace.TurnOn[device, myHost, 0, 0, globalStorage]};
-- MAIN:
IF EthernetOneFace.globalStateSize # 0 THEN Error[]; -- not implemented
END.
LOG
Jan 29, 80 6:00 PM Dalal fixed Core Software AR 2658.
Feb 7, 80 11:18 PM Knutsen
ioPage now a long pointer. Initialize host socket right. INTEGER Divide
Feb 9, 80 2:43 PM Murray fix clobber of 4 words before buffer
Apr 20, 80 4:03 PM Murray Faceification, add ReturnPacket and timedOut
Apr 28, 80 7:36 PM Murray Fix QueueOutput bug (again!) in RecvPacket
Jul 2, 80 5:44 PM Murray Delete "DriverTypes." in front of pupEthernetPacket
Aug 20, 80 2:23 PM BLyon renamed EthernetFace to EthernetOneFace.
3-Nov-81 9:21:07 Knutsen
Use ResidentHeap to allocate IOCB. Module renamed from
MiniEthernetDriver to MiniEthernetOneDriver.
12-Apr-83 10:52:15 DKnutsen
Renamed module MiniEthernetOneDriver.mesa => SimplePUPIOEthernetOneImpl.
Stop using Buffer(Defs). Make compatible with new DriverTypes. Make compatible with new SimplePUPIO.