-- File: SimpleNSIOEthernetImpl.mesa - last edit: -- AOF 9-Feb-88 10:10:04 -- HGM 24-Oct-85 17:54:26, Remove SLOW Dove "bug fix" -- Copyright (C) 1983, 1985 , 1988 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], IEEE8023 USING [EncapObject, 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 IEEE8023.EncapObject, 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 IEEE8023.EncapObject] + NSTypes.maxIDPWordsPerPacket + IEEE8023.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[ IEEE8023.minWordsPerEthernetPacket, SIZE[ethernet IEEE8023.EncapObject] + (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.