<> <> <> <> <> <> <> <> DIRECTORY Basics USING [BITAND, bytesPerWord, HighByte, LowHalf], DeviceCleanup USING [Item, Await], DoradoInputOutput USING [Input, IOAddress, Output, ResetEther], EthernetOneFace USING [HostArray, Status], MPCodes USING [Code, ethernetHostChanged], ProcessorFace USING [SetMP]; EthernetOneHeadDorado: CEDAR PROGRAM IMPORTS Basics, DeviceCleanup, DoradoInputOutput, ProcessorFace EXPORTS EthernetOneFace = { BYTE: TYPE = [0..100H); HostArray: TYPE = EthernetOneFace.HostArray; Status: TYPE = EthernetOneFace.Status; csb: Handle _ LOOPHOLE[LONG[177600B]]; ControllerStatusBlock: TYPE = MACHINE DEPENDENT RECORD [ nextInput: ShortIocb, inInterruptBit: WORD, host: WORD _ 0FFFFH, -- <0 => compatability with old microcode missed: CARDINAL, lastInput: IOCB, -- valid IFF nextInput # NIL, not used by microcode firstInterstices: ARRAY [6..10B) OF WORD _ ALL[0], nextOutput: ShortIocb, outInterruptBit: WORD, minPacketSpacing: CARDINAL, lastOutput: IOCB, -- valid IFF nextOutput # NIL, not used by microcode secondInterstices: ARRAY [15B..30B) OF WORD _ ALL[0], hosts: HostArray]; -- current set of hosts to which receiver code responds IOCB: TYPE = LONG POINTER TO IOControlBlock; ShortIocb: TYPE = -- BASE RELATIVE-- POINTER TO IOControlBlock; IOControlBlock: TYPE = MACHINE DEPENDENT RECORD [ next: ShortIocb, completion: CompletionStatus, used: CARDINAL, -- input only load: CARDINAL, -- output only words: CARDINAL, buffer: LONG POINTER ]; CompletionStatus: TYPE = MACHINE DEPENDENT RECORD [ microcode (0: 0..7): {pending (0), done (1), bufferOverflow (2), loadOverflow (3), killed(37)}, hardware (0: 8..15): SELECT OVERLAID * FROM input => [ rxCollision (0: 8..8), fill1 (0: 9..9), rxDataLate (0: 10..10), fill2 (0: 11..11), rxCRCError (0: 12..12), fill3 (0: 13..14), rxIncTrans (0: 15..15): BOOLEAN], output => [ rxOn (0: 8..8), txOn (0: 9..9), loopBack (0: 10..10), txCollision (0: 11..11), noWakeups (0: 12..12), txDataLate (0: 13..13), singleStep (0: 14..14), txFifoPE (0: 15..15): BOOLEAN], ENDCASE]; inputErrorMask: input CompletionStatus = [ microcode: pending, hardware: input[rxCollision: TRUE, fill1: FALSE, rxDataLate: TRUE, fill2: FALSE, rxCRCError: TRUE, fill3: FALSE, rxIncTrans: TRUE]]; outputErrorMask: output CompletionStatus = [ microcode: pending, hardware: output[rxOn: FALSE, txOn: FALSE, loopBack: FALSE, txCollision: TRUE, noWakeups: FALSE, txDataLate: TRUE, singleStep: FALSE, txFifoPE: TRUE]]; nullCompletion: CompletionStatus = LOOPHOLE[0]; Command: TYPE = RECORD [WORD]; enableInput: Command = [173377B]; enableOutput: Command = [047777B]; control: DoradoInputOutput.IOAddress = 16B; <> Shorten: PROCEDURE [iocb: IOCB] RETURNS [ShortIocb] = TRUSTED INLINE { <> RETURN[LOOPHOLE[Basics.LowHalf[LOOPHOLE[iocb]]]]; }; <> Handle: PUBLIC TYPE = LONG POINTER TO ControllerStatusBlock; ControlBlockRecord: PUBLIC TYPE = IOControlBlock; <> nullHandle: PUBLIC Handle _ NIL; controlBlockSize: PUBLIC CARDINAL _ SIZE[IOControlBlock]; hearSelf: PUBLIC BOOLEAN _ TRUE; <> QueueOutput: PUBLIC PROC [ handle: Handle, buffer: LONG POINTER, bytes: NAT, iocb: IOCB] = TRUSTED { words: CARDINAL _ (bytes+Basics.bytesPerWord-1)/2; -- Round up iocb^ _ [next: NIL, completion: nullCompletion, used: 0, load: 0, words: words, buffer: buffer]; 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[enableOutput, control]; }; handle.lastOutput _ iocb; }; QueueInput: PUBLIC PROC [ handle: Handle, buffer: LONG POINTER, bytes: NAT, iocb: IOCB] = TRUSTED { words: CARDINAL _ (bytes+Basics.bytesPerWord-1)/2; -- Round up iocb^ _ [next: NIL, completion: nullCompletion, used: 0, load: 0, words: words, buffer: buffer]; 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.microcode _ killed; }; GetStatusAndLength: PUBLIC PROC [iocb: IOCB] RETURNS [status: Status, bytes: NAT] = TRUSTED { SELECT iocb.completion.microcode FROM pending => RETURN[pending, 0]; done => { IF Basics.BITAND[ LOOPHOLE[iocb.completion], LOOPHOLE[inputErrorMask] ]=0 THEN status _ ok ELSE SELECT TRUE FROM iocb.completion.rxCollision => status _ otherError; iocb.completion.rxDataLate => status _ overrun; iocb.completion.rxCRCError => status _ IF iocb.completion.rxIncTrans THEN crcAndBadAlignment ELSE crc; iocb.completion.rxIncTrans => status _ badAlignmentButOkCrc; ENDCASE => status _ otherError; }; bufferOverflow => status _ packetTooLong; loadOverflow => status _ tooManyCollisions; ENDCASE => status _ otherError; bytes _ iocb.used*2; }; GetStatusAndCollisions: PUBLIC PROC [iocb: IOCB] RETURNS [status: Status, collisions: NAT] = TRUSTED { SELECT iocb.completion.microcode FROM pending => RETURN[pending, 0]; done => { IF Basics.BITAND[LOOPHOLE[iocb.completion], LOOPHOLE[outputErrorMask] ]=0 THEN status _ ok ELSE SELECT TRUE FROM iocb.completion.txCollision => status _ tooManyCollisions; iocb.completion.txDataLate => status _ underrun; iocb.completion.txFifoPE => status _ otherError; ENDCASE => status _ otherError; }; bufferOverflow => status _ packetTooLong; loadOverflow => status _ tooManyCollisions; ENDCASE => status _ otherError; collisions _ SELECT iocb.load 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; }; GetPacketsMissed: PUBLIC PROC [handle: Handle] RETURNS [CARDINAL] = TRUSTED { RETURN[handle.missed]; }; GetNextDevice: PUBLIC PROC [handle: Handle] RETURNS [Handle] = TRUSTED { RETURN[IF handle=nullHandle THEN csb ELSE nullHandle]; }; GetHostNumber: PUBLIC PROC [handle: Handle] RETURNS [host: BYTE] = TRUSTED { RETURN[Basics.HighByte[DoradoInputOutput.Input[control]]]; }; once: BOOL _ FALSE; TurnOn: PUBLIC PROC [handle: Handle, inInterrupt, outInterrupt: WORD] = TRUSTED { me: BYTE _ GetHostNumber[handle]; DoradoInputOutput.ResetEther[]; IF ~once THEN { once _ TRUE; handle^ _ [ nextInput: NIL, inInterruptBit: inInterrupt, host: 0FFFFH, missed: 0, lastInput: NIL, firstInterstices: ALL[0], nextOutput: NIL, outInterruptBit: outInterrupt, minPacketSpacing: 500/32, lastOutput: NIL, secondInterstices: ALL[0], hosts: ALL[FALSE] ]; }; handle.nextInput _ NIL; handle.inInterruptBit _ inInterrupt; handle.host _ 0FFFFH; handle.nextOutput _ NIL; handle.outInterruptBit _ outInterrupt; handle.minPacketSpacing _ 500/32; handle.hosts _ ALL[FALSE]; handle.hosts[0] _ TRUE; handle.hosts[me] _ TRUE; DoradoInputOutput.Output[enableInput, control]; }; TurnOff: PUBLIC PROC [handle: Handle] = TRUSTED { DoradoInputOutput.ResetEther[]; handle.nextInput _ NIL; handle.nextOutput _ NIL; }; SetOutputDelay: PUBLIC PROC [handle: Handle, microseconds: CARDINAL] = TRUSTED { handle.minPacketSpacing _ microseconds/32; }; <<>> < revert to normal mode>> SetInputHosts: PUBLIC PROC [handle: Handle, hosts: LONG POINTER TO HostArray] = TRUSTED { IF hosts = NIL THEN { me: BYTE _ GetHostNumber[handle]; handle.hosts _ ALL[FALSE]; handle.hosts[0] _ TRUE; handle.hosts[me] _ TRUE; } ELSE handle.hosts _ hosts^; }; alreadyInitializedCleanup: BOOLEAN _ FALSE; savedCSB: ControllerStatusBlock; AddCleanup: PUBLIC PROC [handle: Handle] = TRUSTED { item: DeviceCleanup.Item; originalHost: BYTE _ GetHostNumber[handle]; IF alreadyInitializedCleanup THEN RETURN; alreadyInitializedCleanup _ TRUE; DO SELECT DeviceCleanup.Await[@item] FROM kill => DoradoInputOutput.ResetEther[]; turnOff => { DoradoInputOutput.ResetEther[]; savedCSB _ handle^; handle.nextInput _ NIL; handle.nextOutput _ NIL; }; turnOn => { me: BYTE _ Basics.HighByte[DoradoInputOutput.Input[control]]; <> <> <> <> <> DoradoInputOutput.ResetEther[]; IF me # originalHost THEN ErrorHalt[MPCodes.ethernetHostChanged]; -- InLoaded on another machine? handle^ _ [ nextInput: NIL, inInterruptBit: 0, host: 0FFFFH, missed: savedCSB.missed, lastInput: NIL, firstInterstices: ALL[0], nextOutput: NIL, outInterruptBit: 0, minPacketSpacing: savedCSB.minPacketSpacing, lastOutput: NIL, secondInterstices: ALL[0], hosts: savedCSB.hosts ]; DoradoInputOutput.Output[enableInput, control]; }; ENDCASE; ENDLOOP; }; ErrorHalt: PROC [code: MPCodes.Code] = INLINE { ProcessorFace.SetMP[code]; DO ENDLOOP }; }.