-- 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.