<> <> <> DIRECTORY DeviceCleanup USING [Item, Await], DoradoInputOutput USING [InputNoPE, IOAddress, Output], EthernetFace USING [Status], MPCodes USING [Code, ethernetHostChanged], PrincOps USING [alpha, SD, zMISC], PrincOpsUtils USING [BITAND, BITOR, LowHalf], ProcessorFace USING [SetMP, ProcessorID, processorID], TrapSupport USING [BumpPC, OpTrapTable]; EthernetHeadDorado: CEDAR PROGRAM IMPORTS DeviceCleanup, DoradoInputOutput, PrincOpsUtils, ProcessorFace, TrapSupport EXPORTS EthernetFace SHARES ProcessorFace = BEGIN <> <> CSB: TYPE = LONG POINTER TO ControllerStatusBlock; ControllerStatusBlock: TYPE = MACHINE DEPENDENT RECORD [ host: ProcessorFace.ProcessorID, nextInput: ShortIocb, inputWakeups: WORD, nextOutput: ShortIocb, outputWakeups: WORD, missed: CARDINAL, lastInput: IOCB, -- last IOCB on input queue, valid if nextInput#NIL lastOutput: IOCB]; -- last IOCB on output queue, valid if nextOutput#NIL IOCB: TYPE = LONG POINTER TO IOControlBlock; ShortIocb: TYPE = --Base RELATIVE-- POINTER TO IOControlBlock; IOControlBlock: TYPE = MACHINE DEPENDENT RECORD [ bytes: CARDINAL, buffer: LONG POINTER, retries: WORD, -- set by handle used: CARDINAL, -- input only, length of the received packet, in bytes completion: CompletionStatus _ nullCompletion, -- raw status from microcode, nonzero at end next: ShortIocb ]; <<>> <> ECtl: DoradoInputOutput.IOAddress = 26B; CompletionStatus: TYPE = MACHINE DEPENDENT RECORD [ hardware (0: 0..15): SELECT OVERLAID * FROM input => [ overflow (0: 0..0): BOOLEAN, noDribble (0: 1..1): BOOLEAN, fill1 (0: 2..2): [0..1) _ 0, goodCRC (0: 3..3): BOOLEAN, fill2 (0: 4..5): [0..3) _ 0, oddByte (0: 6..6): BOOLEAN, overRun (0: 7..7): BOOLEAN, fill3 (0: 8..15): [0..400B) _ 0], output => [ fill4 (0: 0..7): [0..400B) _ 0, rxOn (0: 8..8): BOOLEAN, txOn (0: 9..9): BOOLEAN, loopBack (0: 10..10): BOOLEAN, IECByPass (0: 11..11): BOOLEAN, -- error if found one by the head noWakeups (0: 12..12): BOOLEAN, -- error if found one by the head txStatus (0: 13..14): {txOK (0), underRun (1), collision (2), maxCollisions (3)}, txDataParityError (0: 15..15): BOOLEAN], ENDCASE]; nullCompletion: CompletionStatus = LOOPHOLE[0]; killedCompletion: CompletionStatus = LOOPHOLE[0FFFFH]; inputStatusMask: input CompletionStatus = LOOPHOLE[176400B]; -- oddByte OK inputGoodStatus: input CompletionStatus = [input[overflow: FALSE, noDribble: TRUE, goodCRC: TRUE, oddByte: FALSE, overRun: FALSE]]; outputErrorMask: output CompletionStatus = [output[rxOn: FALSE, txOn: FALSE, loopBack: FALSE, IECByPass: TRUE, noWakeups: TRUE, txStatus: maxCollisions, txDataParityError: TRUE]]; <> csb: CSB _ LOOPHOLE[LONG[177700B]]; Shorten: PROCEDURE [iocb: IOCB] RETURNS [ShortIocb] = TRUSTED INLINE { <> RETURN[LOOPHOLE[PrincOpsUtils.LowHalf[iocb]]]; }; aOff: PrincOps.alpha = 250B; MakeSureOff: PROC = TRUSTED MACHINE CODE { PrincOps.zMISC, aOff}; <> <> Handle: PUBLIC TYPE = LONG POINTER TO ControllerStatusBlock; Status: PUBLIC TYPE = EthernetFace.Status; ControlBlockRecord: PUBLIC TYPE = IOControlBlock; <> nullHandle: PUBLIC Handle _ NIL; controlBlockSize: PUBLIC CARDINAL _ SIZE[IOControlBlock]; hearSelf: PUBLIC BOOLEAN _ TRUE; <> <<>> <> HWMode: TYPE = MACHINE DEPENDENT {hwNormal (173000B), hwLoopBack (173100B)}; ResetBits: WORD = 40B + 20B; EnableTx: WORD = 47777B; hwMode: HWMode _ hwNormal; LoopbackMode: TYPE = {normal, loopBack}; SetLoopbackMode: PROC [mode: LoopbackMode] = { hwMode _ SELECT mode FROM normal => hwNormal, loopBack => hwLoopBack, ENDCASE => ERROR; }; QueueOutput: PUBLIC PROC [ handle: Handle, buffer: LONG POINTER, bytes: NAT, iocb: IOCB] = TRUSTED { iocb^ _ [ bytes: bytes, buffer: buffer, retries: 0, used: 0, completion: nullCompletion, next: NIL]; IF handle.nextOutput # NIL THEN handle.lastOutput.next _ Shorten[iocb]; IF handle.nextOutput = NIL AND iocb.completion = nullCompletion THEN { handle.nextOutput _ Shorten[iocb]; DoradoInputOutput.Output[EnableTx, ECtl]; }; -- poke hardware handle.lastOutput _ iocb; }; QueueInput: PUBLIC PROC [ handle: Handle, buffer: LONG POINTER, bytes: NAT, iocb: IOCB] = TRUSTED { iocb^ _ [ bytes: bytes, buffer: buffer, retries: 0, used: 0, completion: nullCompletion, next: NIL]; IF handle.nextInput # NIL THEN handle.lastInput.next _ Shorten[iocb]; IF handle.nextInput = NIL AND iocb.completion = nullCompletion THEN handle.nextInput _ Shorten[iocb]; handle.lastInput _ iocb; }; MarkKilled: PUBLIC PROC [iocb: IOCB] = TRUSTED { iocb.completion _ killedCompletion; }; GetStatusAndLength: PUBLIC PROC [iocb: IOCB] RETURNS [status: Status, bytes: NAT] = TRUSTED { IF iocb.completion = nullCompletion THEN RETURN[pending, 0]; IF iocb.completion = killedCompletion THEN RETURN[killedByDriver, 0]; IF LOOPHOLE[PrincOpsUtils.BITAND[LOOPHOLE[iocb.completion], LOOPHOLE[inputStatusMask]], CompletionStatus] = inputGoodStatus THEN status _ ok ELSE SELECT TRUE FROM iocb.completion.overflow => status _ packetTooLong; -- iocb.used = 0 => bounds fault iocb.completion.overRun => status _ overrun; ~iocb.completion.goodCRC => status _ crc; ~iocb.completion.noDribble, iocb.completion.oddByte => status _ badAlignmentButOkCrc; ENDCASE => status _ otherError; IF iocb.used = 0 THEN bytes _ 0 ELSE bytes _ iocb.used - 2; }; GetStatusAndCollisions: PUBLIC PROC [iocb: IOCB] RETURNS [status: Status, collisions: NAT] = TRUSTED { IF iocb.completion = nullCompletion THEN RETURN[pending, 0]; IF iocb.completion = killedCompletion THEN RETURN[killedByDriver, 0]; collisions _ IF iocb.retries = 17 THEN iocb.retries-1 ELSE iocb.retries; IF PrincOpsUtils.BITAND[LOOPHOLE[iocb.completion], LOOPHOLE[outputErrorMask]] = 0 THEN status _ ok ELSE SELECT iocb.completion.txStatus FROM txOK => status _ ok; underRun, collision => status _ underrun; maxCollisions => status _ tooManyCollisions; ENDCASE => status _ otherError; }; GetPacketsMissed: PUBLIC PROC [handle: Handle] RETURNS [CARDINAL] = TRUSTED { RETURN[handle.missed]; }; unimplemented: BOOL _ FALSE; FakeMakeSureOff: PROC = TRUSTED { TrapSupport.BumpPC[2]; unimplemented _ TRUE; }; GetNextDevice: PUBLIC PROC [handle: Handle] RETURNS [new: Handle _ nullHandle] = TRUSTED { opTrapTable: POINTER TO POINTER TO TrapSupport.OpTrapTable = LOOPHOLE[PrincOps.SD+137B]; foo: WORD; IF handle # nullHandle THEN RETURN; opTrapTable.misc[aOff] _ LOOPHOLE[FakeMakeSureOff]; MakeSureOff[]; -- Bogus microcode => UnimplementedInstruction IF unimplemented THEN RETURN; -- Bogus microcode DoradoInputOutput.Output[PrincOpsUtils.BITOR[LOOPHOLE[hwMode], ResetBits], ECtl]; foo _ DoradoInputOutput.InputNoPE[ECtl]; IF foo = 0 THEN RETURN; -- No Hardware (trial and error, 1 test case) new _ csb; }; TurnOn: PUBLIC PROC [ handle: Handle, inInterrupt, outInterrupt: WORD] = TRUSTED { MakeSureOff[]; handle^ _ [ host: ProcessorFace.processorID, nextInput: NIL, inputWakeups: inInterrupt, nextOutput: NIL, outputWakeups: outInterrupt, missed: 0, lastInput: NIL, lastOutput: NIL]; DoradoInputOutput.Output[PrincOpsUtils.BITOR[LOOPHOLE[hwMode], ResetBits], ECtl]; DoradoInputOutput.Output[LOOPHOLE[hwMode], ECtl]; -- turns on receiver }; TurnOff: PUBLIC PROC [handle: Handle] = { MakeSureOff[]; }; SetPromiscuous: PUBLIC PROC [handle: Handle, promiscuous: BOOL] = TRUSTED { SELECT promiscuous FROM TRUE => handle.host _ [0FFFFH, 0FFFFH, 0FFFFH]; FALSE => handle.host _ ProcessorFace.processorID; ENDCASE => ERROR; }; SetOutputDelay: PUBLIC PROC [handle: Handle, microseconds: CARDINAL] = {}; <> <<>> <> cleanupInitialized: BOOLEAN _ FALSE; savedCSB: ControllerStatusBlock; AddCleanup: PUBLIC PROC [handle: Handle] = TRUSTED { processorID: ProcessorFace.ProcessorID _ ProcessorFace.processorID; item: DeviceCleanup.Item; IF cleanupInitialized THEN RETURN; cleanupInitialized _ TRUE; DO SELECT DeviceCleanup.Await[@item] FROM kill => MakeSureOff[]; turnOff => { MakeSureOff[]; savedCSB _ handle^; handle.nextInput _ NIL; handle.nextOutput _ NIL; }; turnOn => { foo: WORD; <> MakeSureOff[]; IF processorID # ProcessorFace.processorID THEN ErrorHalt[MPCodes.ethernetHostChanged]; -- InLoaded on another machine? IF unimplemented THEN ErrorHalt[MPCodes.ethernetHostChanged]; -- Changed to wrong microcode DoradoInputOutput.Output[PrincOpsUtils.BITOR[LOOPHOLE[hwMode], ResetBits], ECtl]; foo _ DoradoInputOutput.InputNoPE[ECtl]; IF foo = 0 THEN ErrorHalt[MPCodes.ethernetHostChanged]; -- Hardware vanished handle^ _ [ host: savedCSB.host, nextInput: NIL, inputWakeups: 0, nextOutput: NIL, outputWakeups: 0, missed: 0, lastInput: NIL, lastOutput: NIL]; DoradoInputOutput.Output[PrincOpsUtils.BITOR[LOOPHOLE[hwMode], ResetBits], ECtl]; DoradoInputOutput.Output[LOOPHOLE[hwMode], ECtl]; }; ENDCASE; ENDLOOP}; RemoveCleanup: PUBLIC PROC [handle: Handle] = {}; ErrorHalt: PROC [code: MPCodes.Code] = INLINE { ProcessorFace.SetMP[code]; DO ENDLOOP }; END. -- EthernetHeadDorado.