-- Copyright (C) 1984 by Xerox Corporation. All rights reserved. -- EthernetOneHeadDicentra.mesa, HGM, 2-Dec-84 3:37:03 DIRECTORY DicentraInputOutput USING [ GetExternalStatus, Input, InterruptLevel, Output, ReadHole, SetNxmExpected, SetWakeupBits, SetWakeupMask, WriteHole], Environment USING [Byte], EthernetOneFace USING [GlobalStatePtr, Status], HeadStartChain USING [Start], Inline USING [BITAND, BITXOR], MultibusAddresses USING [firstSunBoard], Runtime USING [CallDebugger, IsBound], Process USING [Detach, DisableTimeout, SetPriority], ProcessPriorities USING [priorityPageFaultIO], -- Higher than Driver SpecialRuntime USING [AllocateNakedCondition]; EthernetOneHeadDicentra: MONITOR IMPORTS RemainingHeads: HeadStartChain, DicentraInputOutput, Inline, Process, Runtime, SpecialRuntime EXPORTS HeadStartChain, EthernetOneFace = BEGIN -- TYPEs ControlBlock: TYPE = LONG POINTER TO ControlBlockRecord; -- EXPORTed TYPEs DeviceHandle: PUBLIC TYPE = POINTER TO CSB; ControlBlockRecord: PUBLIC TYPE = RECORD [ next: ControlBlock, buffer: LONG POINTER, length: CARDINAL, used: CARDINAL, status: EthernetOneFace.Status, rawBits: WORD, device: DeviceHandle, direction: {in, out} ]; -- EXPORTed variables nullDeviceHandle: PUBLIC DeviceHandle ¬ NIL; globalStateSize: PUBLIC CARDINAL ¬ 0; controlBlockSize: PUBLIC CARDINAL ¬ SIZE[ControlBlockRecord]; hearSelf: PUBLIC BOOLEAN ¬ TRUE; CSB: TYPE = RECORD [ next: DeviceHandle, sunIn: LONG POINTER TO SunIn, sunOut: LONG POINTER TO SunOut, firstIn, lastIn: ControlBlock, firstOut, lastOut: ControlBlock, missed: CARDINAL, inInterrupt, outInterrupt: WORD]; -- SUN 3mb board layout offsetOfNextBoard: CARDINAL = 128; -- 256 bytes SunIn: TYPE = RECORD [ data: WORD, unused: WORD, ready: WORD, switches: WORD ]; -- Reading switches resets Transmitter interrupt SunOut: TYPE = RECORD [ data: WORD, unused: WORD, control: WORD, addressFilter: WORD ]; maxNumberOfDevices: CARDINAL = 6; first: DeviceHandle ¬ nullDeviceHandle; csbs: ARRAY [0..maxNumberOfDevices) OF CSB; GetNextDevice: PUBLIC PROCEDURE [in: DeviceHandle] RETURNS [out: DeviceHandle] = BEGIN IF in = nullDeviceHandle THEN out ¬ first ELSE out ¬ in.next; END; QueueOutput: PUBLIC ENTRY PROCEDURE [ device: DeviceHandle, buffer: LONG POINTER, length: CARDINAL, cb: ControlBlock] = BEGIN cb­ ¬ [NIL, buffer, length, 0, pending, 0, device, out]; IF device.firstOut = NIL THEN BEGIN device.firstOut ¬ cb; StartOutput[device]; END ELSE device.lastOut.next ¬ cb; device.lastOut ¬ cb; END; StartOutput: INTERNAL PROCEDURE [device: DeviceHandle] = BEGIN cb: ControlBlock = device.firstOut; DicentraInputOutput.Output[cb.length, @device.sunOut.data]; DicentraInputOutput.WriteHole[from: cb.buffer, to: @device.sunOut.data, words: cb.length]; END; CheckSendStatus: INTERNAL PROCEDURE [device: DeviceHandle] RETURNS [bogus: BOOLEAN ¬ FALSE] = BEGIN ready: WORD = DicentraInputOutput.Input[@device.sunIn.ready]; cb: ControlBlock = device.firstOut; [] ¬ DicentraInputOutput.Input[@device.sunIn.switches]; -- Clears Out Int IF cb = NIL THEN RETURN[TRUE]; cb.rawBits ¬ ready; IF Inline.BITAND[ready, 100000B] = 0 THEN RETURN[TRUE]; -- Interrupt vanished! IF Inline.BITAND[ready, 40000B] # 0 THEN cb.status ¬ tooManyCollisions -- Timeout ELSE cb.status ¬ ok; DicentraInputOutput.SetWakeupBits[device.outInterrupt]; device.firstOut ¬ device.firstOut.next; IF device.firstOut # NIL THEN StartOutput[device]; END; QueueInput: PUBLIC ENTRY PROCEDURE [ device: DeviceHandle, buffer: LONG POINTER, length: CARDINAL, cb: ControlBlock] = BEGIN cb­ ¬ [NIL, buffer, length, 0, pending, 0, device, in]; IF device.firstIn = NIL THEN device.firstIn ¬ cb ELSE device.lastIn.next ¬ cb; device.lastIn ¬ cb; END; CheckRecvStatus: INTERNAL PROCEDURE [device: DeviceHandle] RETURNS [bogus: BOOLEAN ¬ FALSE] = BEGIN first, status: WORD; cb: ControlBlock = device.firstIn; words, slosh: CARDINAL; first ¬ status ¬ DicentraInputOutput.Input[@device.sunIn.data]; IF Inline.BITAND[status, 100000B] # 0 THEN status ¬ DicentraInputOutput.Input[@device.sunIn.data]; -- Look ahead krock IF Inline.BITAND[status, 100000B] # 0 THEN RETURN[TRUE]; words ¬ Inline.BITAND[status, 7777B]; IF cb = NIL THEN BEGIN device.missed ¬ device.missed + 1; FOR i: CARDINAL IN [0..words) DO [] ¬ DicentraInputOutput.Input[@device.sunIn.data]; -- discard packet ENDLOOP; RETURN; END; SELECT TRUE FROM Inline.BITAND[status, 040000B] # 0 => cb.status ¬ overrun; Inline.BITAND[status, 030000B] = 030000B => cb.status ¬ crcAndBadAlignment; Inline.BITAND[status, 010000B] # 0 => cb.status ¬ crc; Inline.BITAND[status, 020000B] # 0 => cb.status ¬ badAlignmentButOkCrc; ENDCASE => cb.status ¬ ok; cb.rawBits ¬ status; device.firstIn ¬ device.firstIn.next; DicentraInputOutput.SetWakeupBits[device.inInterrupt]; slosh ¬ MIN[words, cb.length]; DicentraInputOutput.ReadHole[to: cb.buffer, from: @device.sunIn.data, words: slosh]; IF cb.status = ok AND slosh < 3 THEN cb.status ¬ badAlignmentButOkCrc; IF slosh # words THEN BEGIN cb.status ¬ packetTooLong; FOR i: CARDINAL IN [slosh..words) DO [] ¬ DicentraInputOutput.Input[@device.sunIn.data]; ENDLOOP; END; cb.used ¬ slosh-1; -- CRC is included END; GetStatus: PUBLIC ENTRY PROCEDURE [cb: ControlBlock] RETURNS [EthernetOneFace.Status] = BEGIN IF interruptBits = 0 THEN [] ¬ CheckInterrupts[cb.device]; RETURN[cb.status]; END; GetRetries: PUBLIC PROCEDURE [cb: ControlBlock] RETURNS [CARDINAL] = BEGIN RETURN[0]; END; GetPacketLength: PUBLIC PROCEDURE [cb: ControlBlock] RETURNS [CARDINAL] = BEGIN IF cb.direction # in THEN ERROR; IF cb.status = pending THEN ERROR; RETURN[cb.used]; END; GetPacketsMissed: PUBLIC PROCEDURE [device: DeviceHandle] RETURNS [CARDINAL] = BEGIN RETURN[device.missed]; END; wait: LONG POINTER TO CONDITION ¬ NIL; interruptBits: WORD ¬ 0; maxBogus, totalRecvBogus, totalTranBogus: CARDINAL ¬ 0; DeMultiplexInterrupts: ENTRY PROCEDURE = BEGIN [cv: wait, mask: interruptBits] ¬ SpecialRuntime.AllocateNakedCondition[]; Process.DisableTimeout[wait]; Process.SetPriority[ProcessPriorities.priorityPageFaultIO]; DO bogus: CARDINAL ¬ 0; WAIT wait; WHILE DicentraInputOutput.GetExternalStatus[].ints[ethernetOne] DO hit: BOOLEAN ¬ FALSE; FOR device: DeviceHandle ¬ first, device.next UNTIL device = nullDeviceHandle DO thisHit: BOOLEAN; thisRecvBogus, thisTranBogus: CARDINAL; [thisHit, thisRecvBogus, thisTranBogus] ¬ CheckInterrupts[device]; hit ¬ hit OR thisHit; bogus ¬ bogus + thisRecvBogus + thisTranBogus; totalRecvBogus ¬ totalRecvBogus + thisRecvBogus; totalTranBogus ¬ totalTranBogus + thisTranBogus; ENDLOOP; maxBogus ¬ MAX[maxBogus, bogus]; IF bogus > 100 THEN Runtime.CallDebugger["Dangling 3mb Interrupt"L]; ENDLOOP; ENDLOOP; END; CheckInterrupts: INTERNAL PROCEDURE [device: DeviceHandle] RETURNS [hit: BOOLEAN ¬ FALSE, recvBogus, tranBogus: CARDINAL ¬ 0] = BEGIN DO ready: WORD = DicentraInputOutput.Input[@device.sunIn.ready]; SELECT TRUE FROM Inline.BITAND[ready, 010000B] = 0 => EXIT; Inline.BITAND[ready, 020000B] # 0 => BEGIN IF CheckRecvStatus[device] THEN recvBogus ¬ recvBogus + 1 ELSE hit ¬ TRUE; END; Inline.BITAND[ready, 100000B] # 0 => BEGIN IF CheckSendStatus[device] THEN tranBogus ¬ tranBogus + 1 ELSE hit ¬ TRUE; END; ENDCASE => BEGIN again: WORD = DicentraInputOutput.Input[@device.sunIn.ready]; ERROR; END; ENDLOOP; END; TurnOn: PUBLIC ENTRY PROCEDURE [ device: DeviceHandle, host: Environment.Byte, inInterrupt: WORD, outInterrupt: WORD, globalState: EthernetOneFace.GlobalStatePtr] = BEGIN intLev: WORD = Inline.BITXOR[DicentraInputOutput.InterruptLevel[ethernetOne], 7B]*256; device.firstIn ¬ device.lastIn ¬ device.firstOut ¬ device.lastOut ¬ NIL; device.inInterrupt ¬ inInterrupt; device.outInterrupt ¬ outInterrupt; DicentraInputOutput.Output[134000B, @device.sunOut.control]; -- Init, no Int, reject FOR i: CARDINAL IN [0..256) DO DicentraInputOutput.Output[i, @device.sunOut.addressFilter]; ENDLOOP; DicentraInputOutput.Output[114000B, @device.sunOut.control]; -- Init, no Int, accept DicentraInputOutput.Output[0, @device.sunOut.addressFilter]; -- broadcast DicentraInputOutput.Output[host, @device.sunOut.addressFilter]; -- me DicentraInputOutput.SetWakeupMask[interruptBits, ethernetOne]; DicentraInputOutput.Output[0B+intLev, @device.sunOut.control]; FOR i: CARDINAL IN [0..10000) DO [] ¬ DicentraInputOutput.Input[@device.sunIn.data]; ENDLOOP; [] ¬ DicentraInputOutput.Input[@device.sunIn.switches]; -- Turn off Tran int END; TurnOff: PUBLIC ENTRY PROCEDURE [device: DeviceHandle] = BEGIN DicentraInputOutput.Output[114000B, @device.sunOut.control]; -- Init, no Int DicentraInputOutput.SetWakeupMask[interruptBits, ethernetOne]; device.firstIn ¬ device.lastIn ¬ device.firstOut ¬ device.lastOut ¬ NIL; device.inInterrupt ¬ device.outInterrupt ¬ 0; END; GetEthernet1Address: PUBLIC PROCEDURE [device: DeviceHandle] RETURNS [net, host: [0..377B]] = BEGIN net ¬ 0; host ¬ DicentraInputOutput.Input[@device.sunIn.switches]; END; -- None needed: it's not reading directly into our memory AddCleanup, RemoveCleanup: PUBLIC PROCEDURE [DeviceHandle] = BEGIN END; Start: PUBLIC PROCEDURE = BEGIN last: DeviceHandle ¬ nullDeviceHandle; DicentraInputOutput.SetNxmExpected[TRUE]; FOR i: CARDINAL IN [0..maxNumberOfDevices) DO device: DeviceHandle = @csbs[i]; thisBoard: LONG POINTER = MultibusAddresses.firstSunBoard + i*offsetOfNextBoard; switches: WORD; device­ ¬ [ next: nullDeviceHandle, sunIn: thisBoard, sunOut: thisBoard, firstIn: NIL, lastIn: NIL, firstOut: NIL, lastOut: NIL, missed: 0, inInterrupt: 0, outInterrupt: 0 ]; switches ¬ DicentraInputOutput.Input[@device.sunIn.switches]; IF switches = 0 THEN EXIT; IF first = nullDeviceHandle THEN first ¬ device; IF last # nullDeviceHandle THEN last.next ¬ device; last ¬ device; DicentraInputOutput.Output[114000B, @device.sunOut.control]; -- Init, no Int ENDLOOP; DicentraInputOutput.SetNxmExpected[FALSE]; IF Runtime.IsBound[LOOPHOLE[Process.Detach]] THEN Process.Detach[FORK DeMultiplexInterrupts[]]; RemainingHeads.Start[]; END; END....