-- Copyright (C) 1983, 1984 by Xerox Corporation. All rights reserved. -- EthernetHeadDicentra.mesa, HGM, 20-Nov-84 14:47:59 DIRECTORY DicentraInputOutput USING [ GetExternalStatus, Input, Output, ReadBlock, SetNxmExpected, SetWakeupBits, SetWakeupMask, WriteBlock], EthernetFace USING [GlobalStatePtr, Status], HeadStartChain USING [Start], Inline USING [BITAND, BITNOT, BITOR, BITXOR, LowHalf], MultibusAddresses USING [first3ComBoard], Runtime USING [CallDebugger, IsBound], Process USING [Detach, DisableTimeout, SetPriority], ProcessPriorities USING [priorityPageFaultIO], -- Higher than Driver SpecialRuntime USING [AllocateNakedCondition], SpecialSystem USING [ProcessorID], System USING [GetClockPulses]; EthernetHeadDicentra: MONITOR IMPORTS RemainingHeads: HeadStartChain, DicentraInputOutput, Inline, Process, Runtime, SpecialRuntime, System EXPORTS HeadStartChain, EthernetFace = 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, mask: WORD, status: EthernetFace.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 ← FALSE; -- My CSB (and such) maxNumberOfDevices: CARDINAL = 6; first: DeviceHandle ← nullDeviceHandle; csbs: ARRAY [0..maxNumberOfDevices) OF CSB; CSB: TYPE = RECORD [ next: DeviceHandle, header: LONG POINTER TO Header, transmitBuffer: LONG POINTER TO ARRAY [0..1024) OF WORD, recvBufferA: LONG POINTER TO ARRAY [0..1024) OF WORD, recvBufferB: LONG POINTER TO ARRAY [0..1024) OF WORD, control: WORD, firstIn, lastIn: ControlBlock, firstOut, lastOut: ControlBlock, missed: CARDINAL, inInterrupt, outInterrupt: WORD ]; offsetOfNextBoard: CARDINAL = 8000H; -- 64K Bytes. Shadow behind first board -- 3Com board layout: BEWARE of ADR0 -- Yetch, a simple record "exceeds addressing limits". Header: TYPE = RECORD [ mecsr: WORD, meback: CARDINAL, trash1: ARRAY [0..512-(SIZE[WORD]+SIZE[CARDINAL])) OF WORD, stationAddressROM: SpecialSystem.ProcessorID, trash2: ARRAY [0..256-SIZE[SpecialSystem.ProcessorID]) OF WORD, stationAddressRAM: SpecialSystem.ProcessorID ]; packetFilter: WORD = 6B; -- Me and Broadcast inInterruptsAndPacketFilter: WORD = 306B; -- Buffer A and Buffer B outInterrupts: WORD = 60B; -- Output done and Jam 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, 1, 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; wordOffset: CARDINAL = 1024 - cb.length; DicentraInputOutput.WriteBlock[from: cb.buffer, to: device.transmitBuffer + wordOffset, words: cb.length]; device.control ← Inline.BITOR[device.control, outInterrupts]; DicentraInputOutput.Output[-20, @device.header.meback]; -- Initial 1ms delay DicentraInputOutput.Output[wordOffset*2, device.transmitBuffer]; DicentraInputOutput.Output[30000B+device.control, @device.header.mecsr]; -- Set TBSW, Clear Jam END; CheckSendStatus: INTERNAL PROCEDURE [device: DeviceHandle] RETURNS [bogus: BOOLEAN ← FALSE] = BEGIN status: WORD = DicentraInputOutput.Input[@device.header.mecsr]; cb: ControlBlock = device.firstOut; IF cb = NIL THEN BEGIN device.control ← Inline.BITAND[device.control, Inline.BITNOT[outInterrupts]]; DicentraInputOutput.Output[device.control, @device.header.mecsr]; -- Clear Out Int RETURN[TRUE]; END; IF Inline.BITAND[status, 10000B] # 0 THEN BEGIN -- Collision random: WORD ← Inline.LowHalf[System.GetClockPulses[]]; random ← Inline.BITAND[random, 1777B]; random ← Inline.BITAND[random, cb.mask]; IF cb.mask = 177777B THEN BEGIN -- Yetch. I think we have to reset the board. Yetch. cb.status ← tooManyCollisions; cb.rawBits ← status; DicentraInputOutput.SetWakeupBits[device.outInterrupt]; device.firstOut ← device.firstOut.next; IF device.firstOut # NIL THEN StartOutput[device] ELSE BEGIN device.control ← Inline.BITAND[device.control, Inline.BITNOT[outInterrupts]]; DicentraInputOutput.Output[device.control, @device.header.mecsr]; -- Clear Out Int END; RETURN; END; -- Note: Documentation bug: It doesn't mention the extra 1. DicentraInputOutput.Output[-(random+1), @device.header.meback]; DicentraInputOutput.Output[30000B+device.control, @device.header.mecsr]; -- Set TBSW, Clear Jam cb.mask ← cb.mask * 2 + 1; RETURN; END; IF Inline.BITAND[status, 20000B] # 0 THEN RETURN[TRUE]; cb.status ← ok; cb.rawBits ← status; DicentraInputOutput.SetWakeupBits[device.outInterrupt]; device.firstOut ← device.firstOut.next; IF device.firstOut # NIL THEN StartOutput[device] ELSE BEGIN device.control ← Inline.BITAND[device.control, Inline.BITNOT[outInterrupts]]; DicentraInputOutput.Output[device.control, @device.header.mecsr]; -- Clear Out Int END; END; QueueInput: PUBLIC ENTRY PROCEDURE [ device: DeviceHandle, buffer: LONG POINTER, length: CARDINAL, cb: ControlBlock] = BEGIN cb↑ ← [NIL, buffer, length, 0, 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 ← TRUE] = BEGIN DO status: WORD = Inline.BITAND[DicentraInputOutput.Input[@device.header.mecsr], 142000B]; SELECT status FROM 140000B, 142000B => RETURN; 0 => BEGIN GrabBuffer[device, device.recvBufferA, 40000B]; GrabBuffer[device, device.recvBufferB, 100000B]; END; 2000B => BEGIN GrabBuffer[device, device.recvBufferB, 100000B]; GrabBuffer[device, device.recvBufferA, 40000B]; END; 100000B, 102000B => GrabBuffer[device, device.recvBufferA, 40000B]; 40000B, 42000B => GrabBuffer[device, device.recvBufferB, 100000B]; ENDCASE => ERROR; bogus ← FALSE ENDLOOP; END; GrabBuffer: INTERNAL PROCEDURE [device: DeviceHandle, recv: LONG POINTER, mask: WORD] = BEGIN cb: ControlBlock = device.firstIn; rawBits: WORD ← DicentraInputOutput.Input[recv]; bytes, words: CARDINAL; bytes ← Inline.BITAND[rawBits, 3777B] - 2; IF cb = NIL THEN BEGIN device.missed ← device.missed + 1; DicentraInputOutput.Output[mask+device.control, @device.header.mecsr]; -- Start next read RETURN; END; words ← bytes/2; SELECT TRUE FROM Inline.BITAND[rawBits, 020000B] # 0 => cb.status ← packetTooLong; Inline.BITAND[rawBits, 104000B] # 0 => BEGIN error: WORD = Inline.BITAND[rawBits, 124000B]; SELECT error FROM 100000B => cb.status ← crc; 104000B => cb.status ← crcAndBadAlignment; 004000B => cb.status ← badAlignmentButOkCrc; ENDCASE => cb.status ← packetTooLong; END; bytes MOD 2 # 0 => cb.status ← badAlignmentButOkCrc; ENDCASE => cb.status ← ok; IF words > cb.length THEN BEGIN cb.status ← packetTooLong; words ← cb.length; END; DicentraInputOutput.ReadBlock[to: cb.buffer, from: recv + 1, words: words]; cb.used ← words; cb.mask ← bytes; cb.rawBits ← rawBits; DicentraInputOutput.SetWakeupBits[device.inInterrupt]; DicentraInputOutput.Output[mask+device.control, @device.header.mecsr]; -- Start next read device.firstIn ← device.firstIn.next; END; GetStatus: PUBLIC ENTRY PROCEDURE [cb: ControlBlock] RETURNS [EthernetFace.Status] = BEGIN IF interruptBits = 0 THEN [] ← CheckInterrupts[cb.device]; RETURN[cb.status]; END; GetRetries: PUBLIC PROCEDURE [cb: ControlBlock] RETURNS [CARDINAL] = BEGIN IF cb.direction # out THEN ERROR; IF cb.status = pending THEN ERROR; RETURN[ SELECT cb.mask FROM 1 => 0, 3 => 1, 7 => 2, 17B => 3, 37B => 4, 77B => 5, 177B => 6, 377B => 7, 777B => 8, 1777B => 9, 3777B => 10, 7777B => 11, 17777B => 12, 37777B => 13, 77777B => 14, 177777B => 15, ENDCASE => 16]; 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[ethernet] DO hit: BOOLEAN ← FALSE; FOR device: DeviceHandle ← first, device.next UNTIL device = NIL 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; IF ~hit THEN bogus ← bogus + 1; maxBogus ← MAX[maxBogus, bogus]; IF bogus > 100 THEN Runtime.CallDebugger["Dangling 10mb Interrupt"L]; ENDLOOP; ENDLOOP; END; CheckInterrupts: INTERNAL PROCEDURE [device: DeviceHandle] RETURNS [hit: BOOLEAN ← FALSE, recvBogus, tranBogus: CARDINAL ← 0] = BEGIN DO status: WORD ← DicentraInputOutput.Input[@device.header.mecsr]; status ← Inline.BITXOR[status, 160000B]; SELECT TRUE FROM Inline.BITAND[status, 140000B] # 0 => BEGIN IF CheckRecvStatus[device] THEN recvBogus ← recvBogus + 1 ELSE hit ← TRUE; END; Inline.BITAND[status, 30000B] # 0 AND Inline.BITAND[status, outInterrupts] # 0 => BEGIN IF CheckSendStatus[device] THEN tranBogus ← tranBogus + 1 ELSE hit ← TRUE; END; ENDCASE => RETURN; ENDLOOP; END; TurnOn: PUBLIC ENTRY PROCEDURE [ device: DeviceHandle, host: SpecialSystem.ProcessorID, inInterrupt: WORD, outInterrupt: WORD, globalState: EthernetFace.GlobalStatePtr] = BEGIN source: POINTER = @host; dest: LONG POINTER = @device.header.stationAddressRAM; DicentraInputOutput.Output[400B+0, @device.header.mecsr]; -- Reset device.firstIn ← device.lastIn ← device.firstOut ← device.lastOut ← NIL; device.inInterrupt ← inInterrupt; device.outInterrupt ← outInterrupt; FOR i: CARDINAL IN [0..SIZE[SpecialSystem.ProcessorID]) DO DicentraInputOutput.Output[(source+i)↑, dest+i]; ENDLOOP; device.control ← inInterruptsAndPacketFilter; DicentraInputOutput.SetWakeupMask[interruptBits, ethernet]; DicentraInputOutput.Output[144000B+device.control, @device.header.mecsr]; -- Set BBSW, ABSW, AMSW END; TurnOff: PUBLIC ENTRY PROCEDURE [device: DeviceHandle] = BEGIN device.control ← packetFilter; DicentraInputOutput.Output[400B+device.control, @device.header.mecsr]; -- Reset DicentraInputOutput.SetWakeupMask[interruptBits, ethernet]; device.firstIn ← device.lastIn ← device.firstOut ← device.lastOut ← NIL; 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.first3ComBoard + i*LONG[offsetOfNextBoard]; hostNumber1, hostNumber2: WORD; device↑ ← [ next: nullDeviceHandle, header: thisBoard, transmitBuffer: thisBoard + 1*1024, recvBufferA: thisBoard + 2*1024, recvBufferB: thisBoard + 3*1024, control: packetFilter, firstIn: NIL, lastIn: NIL, firstOut: NIL, lastOut: NIL, missed: 0, inInterrupt: 0, outInterrupt: 0 ]; DicentraInputOutput.Output[400B+device.control, @device.header.mecsr]; -- Reset hostNumber1 ← DicentraInputOutput.Input[@device.header.stationAddressROM + 0]; hostNumber2 ← DicentraInputOutput.Input[@device.header.stationAddressROM + 1]; hostNumber2 ← Inline.BITAND[hostNumber2, 0FF00H]; IF hostNumber1 # 0260H OR hostNumber2 # 8C00H THEN EXIT; IF first = nullDeviceHandle THEN first ← device; IF last # nullDeviceHandle THEN last.next ← device; last ← device; DicentraInputOutput.Output[400B+device.control, @device.header.mecsr]; -- Reset DicentraInputOutput.Output[144000B+device.control, @device.header.mecsr]; -- Set BBSW, ABSW, AMSW ENDLOOP; DicentraInputOutput.SetNxmExpected[FALSE]; IF Runtime.IsBound[LOOPHOLE[Process.Detach]] THEN Process.Detach[FORK DeMultiplexInterrupts[]]; RemainingHeads.Start[]; END; END....