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