-- File: SimpleNSIOEthernetImpl.mesa - last edit:
-- HGM 24-Oct-85 17:54:26, Remove SLOW Dove "bug fix"
-- AOF 16-Oct-85 20:45:58
-- Copyright (C) 1983, 1985 by Xerox Corporation. All rights reserved.
-- This module implements SimpleNSIO on the Ethernet (NOT the EthernetOne). It supplies level one and level zero encapsulation.
-- NOTE: The module SimpleNSIOEthernetOneImpl is a near-copy of this one. IF YOU MAKE A CHANGE HERE, it probably should be made to it too. SimplePUPIOEthernetOneImpl is another, somewhat less-similar module.
DIRECTORY
BootChannel USING [Result],
Checksum USING [ComputeChecksum],
Device USING [Type],
DeviceTypes USING [ethernet],
DriverTypes USING [Encapsulation, EthernetCRC, minWordsPerEthernetPacket],
Environment USING [Base, bytesPerWord, first64K],
EthernetFace USING [
AddCleanup, controlBlockSize, DeviceHandle, GetNextDevice, GetPacketLength,
GetStatus, globalStateSize, nullDeviceHandle, QueueInput, QueueOutput,
RemoveCleanup, TurnOff, TurnOn, GlobalStatePtr],
GermOps USING [GermWorldError],
Inline USING [LowHalf],
NSTypes USING [BufferBody, bytesPerIDPHeader, maxIDPWordsPerPacket, PacketType],
PilotMP USING [cGermERROR],
ProcessorFace USING [processorID],
ResidentHeap USING [FreeNode, MakeNode],
SimpleNSIO USING [
ByteCount, cantHandleDevice, Cleanups, timedOutBytes, timedOutResult],
SpecialSystem USING [
HostNumber, NetworkNumber, nullNetworkNumber, SocketNumber],
System USING [
GetClockPulses, Microseconds, MicrosecondsToPulses, NetworkAddress,
nullHostNumber, nullNetworkNumber, nullSocketNumber, Pulses, SocketNumber];
SimpleNSIOEthernetImpl: PROGRAM
IMPORTS
Checksum, EthernetFace, GermOps, Inline, ProcessorFace, ResidentHeap, System
EXPORTS SimpleNSIO, System
SHARES GermOps =
BEGIN
-- PARAMETERS:
sendTimeOut: System.Microseconds = 3000000;
-- TYPES AND VARIABLES:
HostNumber: PUBLIC --System-- TYPE = SpecialSystem.HostNumber;
NetworkNumber: PUBLIC --System-- TYPE = SpecialSystem.NetworkNumber;
SocketNumber: PUBLIC --System-- TYPE = SpecialSystem.SocketNumber;
NetworkAddress: TYPE = System.NetworkAddress;
ByteCount: TYPE = SimpleNSIO.ByteCount;
-- Level zero Ethernet packet format:
LevelZeroPacket: TYPE = MACHINE DEPENDENT RECORD [
encapsulation(0): ethernet DriverTypes.Encapsulation,
levelOnePacket(7): NSTypes.BufferBody];
bufferAlign: CARDINAL = 3; -- align encapsulation to this boundary..
bufferMod: CARDINAL = 4; -- .. modulo this.
slop: CARDINAL = 12; -- Why this? see Footnote (1) beneath LOG.
bufferSize: CARDINAL =
SIZE[ethernet DriverTypes.Encapsulation] +
NSTypes.maxIDPWordsPerPacket +
DriverTypes.EthernetCRC.SIZE +
slop;
buffer: LONG POINTER TO LevelZeroPacket;
longIocb: LONG POINTER ← NIL;
globalStorage: EthernetFace.GlobalStatePtr;
device: EthernetFace.DeviceHandle ← EthernetFace.nullDeviceHandle;
returnData: RECORD [
returnTo, returnedFrom: NetworkAddress, returnVia: HostNumber];
--==============================
-- Public Procedures
--==============================
GetRawBufferSize: PUBLIC PROCEDURE RETURNS [rawBufferSize: CARDINAL] = {
RETURN[bufferSize + bufferMod-1]};
Initialize: PUBLIC PROC [
deviceType: Device.Type, deviceOrdinal: CARDINAL, rawBuffer: LONG POINTER,
cleanups: SimpleNSIO.Cleanups]
RETURNS [
result: BootChannel.Result, bufferBody: LONG POINTER TO NSTypes.BufferBody] =
BEGIN
IF device # EthernetFace.nullDeviceHandle THEN Error[]; -- already initialized
IF deviceType # DeviceTypes.ethernet THEN
RETURN[SimpleNSIO.cantHandleDevice, NIL];
THROUGH [0..deviceOrdinal] DO
IF (device ← EthernetFace.GetNextDevice[device]) =
EthernetFace.nullDeviceHandle THEN
RETURN[SimpleNSIO.cantHandleDevice, NIL];
ENDLOOP;
longIocb ← @Environment.first64K[
ResidentHeap.MakeNode[EthernetFace.controlBlockSize, a4].node];
buffer ← LOOPHOLE[((LOOPHOLE[rawBuffer, LONG CARDINAL] + bufferMod-1)/bufferMod)*bufferMod
+ bufferAlign];
IF EthernetFace.globalStateSize # 0 THEN
[globalStorage, ] ← ResidentHeap.MakeNode[EthernetFace.globalStateSize, a4];
IF cleanups = doCleanup THEN EthernetFace.AddCleanup[device];
EtherTurnOn[];
RETURN[[ok[]], @buffer.levelOnePacket];
END; --Initialize--
Finalize: PUBLIC PROC [cleanups: SimpleNSIO.Cleanups] =
BEGIN
IF device = EthernetFace.nullDeviceHandle THEN Error[]; -- not initialized
EthernetFace.TurnOff[device];
IF cleanups = doCleanup THEN EthernetFace.RemoveCleanup[device];
device ← EthernetFace.nullDeviceHandle;
[] ← ResidentHeap.FreeNode[Inline.LowHalf[longIocb]];
longIocb ← NIL;
IF EthernetFace.globalStateSize # 0 THEN
[] ← ResidentHeap.FreeNode[globalStorage];
END;
ReceivePacket: PUBLIC PROC [
getFrom: NetworkAddress, mySocket: System.SocketNumber,
timeout: System.Microseconds]
RETURNS [
result: BootChannel.Result, dataBytes: ByteCount, type: NSTypes.PacketType,
source, destination: LONG POINTER TO NetworkAddress] =
BEGIN
startTime: System.Pulses = System.GetClockPulses[];
IF device = EthernetFace.nullDeviceHandle THEN Error[]; -- not initialized
DO --UNTIL packet arrives on requested socket--
EthernetFace.QueueInput[
device, @buffer.encapsulation, bufferSize, longIocb];
UNTIL EthernetFace.GetStatus[longIocb] # pending DO
IF TimeExpired[timeout: timeout, startTime: startTime] THEN
BEGIN
EthernetFace.TurnOff[device]; -- abort the input.
EtherTurnOn[];
RETURN[
dataBytes: SimpleNSIO.timedOutBytes,
type: TRASH, source: TRASH, destination: TRASH,
result: SimpleNSIO.timedOutResult];
END;
ENDLOOP;
IF EthernetFace.GetStatus[longIocb] ~= ok THEN LOOP;
IF buffer.encapsulation.ethernetType # ns THEN LOOP;
IF ~TestChecksum[@buffer.levelOnePacket] THEN LOOP;
IF buffer.levelOnePacket.destination.host ~= ProcessorFace.processorID THEN LOOP;
IF getFrom.net ~= System.nullNetworkNumber
AND getFrom.net ~= buffer.levelOnePacket.source.net THEN LOOP;
IF getFrom.host ~= System.nullHostNumber
AND getFrom.host ~= buffer.levelOnePacket.source.host THEN LOOP;
IF getFrom.socket ~= System.nullSocketNumber
AND getFrom.socket ~= buffer.levelOnePacket.source.socket THEN LOOP;
IF mySocket ~= System.nullSocketNumber
AND mySocket ~= buffer.levelOnePacket.destination.socket THEN LOOP;
IF EthernetFace.GetPacketLength[longIocb] <
LevelZeroWordsFromLevelOneBytes[
buffer.levelOnePacket.pktLength] THEN LOOP;
IF buffer.levelOnePacket.pktLength >= NSTypes.bytesPerIDPHeader THEN EXIT;
ENDLOOP;
returnData.returnTo ← buffer.levelOnePacket.source;
returnData.returnedFrom ← buffer.levelOnePacket.destination;
returnData.returnVia ← buffer.encapsulation.ethernetSource;
RETURN[
dataBytes: buffer.levelOnePacket.pktLength - NSTypes.bytesPerIDPHeader,
type: buffer.levelOnePacket.packetType,
source: @buffer.levelOnePacket.source,
destination: @buffer.levelOnePacket.destination,
result: [ok[]]];
END; --ReceivePacket--
ReturnPacket: PUBLIC PROC [dataBytes: ByteCount, type: NSTypes.PacketType]
RETURNS [result: BootChannel.Result] =
BEGIN
IF device = EthernetFace.nullDeviceHandle THEN Error[]; -- not initialized
buffer.encapsulation.ethernetDest ← returnData.returnVia;
buffer.levelOnePacket.source ← returnData.returnedFrom;
buffer.levelOnePacket.destination ← returnData.returnTo;
RETURN SendBuffer[dataBytes, type];
END;
SendPacket: PUBLIC PROC [
dataBytes: ByteCount, type: NSTypes.PacketType, sourceSocket: SocketNumber,
dest: NetworkAddress]
RETURNS [result: BootChannel.Result] =
BEGIN
IF device = EthernetFace.nullDeviceHandle THEN Error[]; -- not initialized;
buffer.encapsulation.ethernetDest ← dest.host;
buffer.levelOnePacket.source ← [
net: SpecialSystem.nullNetworkNumber, host: ProcessorFace.processorID,
socket: sourceSocket];
buffer.levelOnePacket.destination ← dest;
RETURN SendBuffer[dataBytes, type];
END;
--==============================
-- Private Procedures
--==============================
Error: PROC = {GermOps.GermWorldError[PilotMP.cGermERROR]};
SendBuffer: PROC [dataBytes: ByteCount, type: NSTypes.PacketType]
RETURNS [result: BootChannel.Result] =
-- Sends buffer; doesn't return till it is sent (but times out if can't send).
BEGIN
startTime: System.Pulses;
buffer.levelOnePacket.pktLength ← dataBytes + NSTypes.bytesPerIDPHeader;
buffer.levelOnePacket.transportControl ← [
trace: FALSE, filler: 0, hopCount: 0];
buffer.levelOnePacket.packetType ← type;
buffer.encapsulation.ethernetSource ← [ProcessorFace.processorID];
buffer.encapsulation.ethernetType ← ns;
SetChecksum[@buffer.levelOnePacket];
EthernetFace.QueueOutput[
device, @buffer.encapsulation,
LevelZeroWordsFromLevelOneBytes[buffer.levelOnePacket.pktLength],
longIocb];
IF FALSE THEN
THROUGH [0..1000) DO ENDLOOP; --we haven't figured out why this helps
startTime ← System.GetClockPulses[];
DO --until sent or timed out
IF EthernetFace.GetStatus[longIocb] # pending THEN EXIT;
IF TimeExpired[timeout: sendTimeOut, startTime: startTime] THEN {
EthernetFace.TurnOff[device]; EtherTurnOn[]; EXIT};
ENDLOOP;
RETURN[[ok[]]];
END; --SendBuffer--
SetChecksum: PROC [b: LONG POINTER TO NSTypes.BufferBody] = {
b.checksum ← Checksum.ComputeChecksum[
0, (b.pktLength + 1 - 2)/2, @b.pktLength]};
TestChecksum: PROC [b: LONG POINTER TO NSTypes.BufferBody] RETURNS [BOOLEAN] = {
RETURN[
b.checksum = Checksum.ComputeChecksum[
0, (b.pktLength + 1 - 2)/2, @b.pktLength] OR b.checksum = 177777B]};
TimeExpired: PROC [timeout, startTime: System.Microseconds] RETURNS [BOOLEAN] =
BEGIN
IF timeout = LAST[System.Microseconds] THEN RETURN[FALSE]
ELSE
RETURN[
(System.GetClockPulses[] - startTime) > System.MicrosecondsToPulses[
timeout]];
END;
EtherTurnOn: PROC = {EthernetFace.TurnOn[device, [ProcessorFace.processorID], 0, 0, globalStorage]};
LevelZeroWordsFromLevelOneBytes: PROC [bodyBytes: CARDINAL]
RETURNS [wds: CARDINAL] =
BEGIN
RETURN[MAX[
DriverTypes.minWordsPerEthernetPacket,
SIZE[ethernet DriverTypes.Encapsulation] +
(bodyBytes + Environment.bytesPerWord - 1)/Environment.bytesPerWord]];
END;
-- MAIN body:
--IF EthernetFace.globalStateSize # 0 THEN Error[];
END.
LOG
23-Oct-81 17:04:29 FXH Created file.
24-Nov-81 11:10:38 FXH Merged in use of ResidentHeap.
10-Dec-81 11:43:21 AWL
MicrosecondsToPulses now in System. Fixes for network address related things
now being defined in System.
10-Dec-81 17:11:11 AWL Export types to System from SpecialSystem.
20-Apr-83 13:10:33 WDK
OISCP => NS. Renamed module MiniOISCPDriverImpl.mesa => SimpleNSIOEthernetImpl. Now returns all nonfatal errors to caller. Make compatible with new SimpleNSIO. Now will talk to specified ethernet. Get constants from DriverTypes. Buffer passed in by client.
20-Jul-83 9:58:49 AWL
NSTypes.bytesPerPktHeader => NSTypes.bytesPerSppHeader,
NSTypes.maxBytesPerPkt => NSTypes.maxDataBytesPerSpp.
9-Aug-83 17:05:49 WDK
Previous change was wrong. Should be IDP, not SPP. Use new DriverTypes.EthernetCRC.
8-Aug-85 9:36:25 AOF
Process globalStorage when Faces says size is # 0 (Daybreak).
16-Oct-85 20:44:44 AOF
Add a small delay loop after queue output to the ethernet face. We don't know why this works, but it covers problems in both the DLion and Daybreak machines.
FOOTNOTE
added at time of Copyriting 8-Oct-84 18:18:35:
(1) "-- Hal says we need this. We should ask why." where the global constant ~slop~ is declared.