<> <> <> <> <> <> <> DIRECTORY Basics USING [BITAND, LongNumber, LowHalf], DeviceCleanup USING [Item, Await], DLionInputOutput USING [IOPage, Input, Output], EthernetFace USING [Status], ProcessorFace USING [ProcessorID, processorID]; EthernetHeadDLion: CEDAR PROGRAM IMPORTS Basics, DLionInputOutput, DeviceCleanup, ProcessorFace EXPORTS EthernetFace SHARES ProcessorFace = { <> ControlBlockRecord: PUBLIC TYPE = IOControlBlock; Handle: PUBLIC TYPE = LONG POINTER TO ControllerStatusBlock; Status: PUBLIC TYPE = EthernetFace.Status; ControllerStatusBlock: TYPE = MACHINE DEPENDENT RECORD [ host: ProcessorFace.ProcessorID, nextInput: ShortIocb, inputWakeups: WORD, nextOutput: ShortIocb, outputWakeups: WORD, missed: CARDINAL, lastInput: IOCB, -- last IOCB on nextInput queue, valid if nextInput#NIL lastOutput: IOCB]; -- last IOCB on nextOutput queue, valid if nextOutput#NIL ethernetCSBOffset: CARDINAL = 12*16 + 0; -- 0C0X IOCB: TYPE = LONG POINTER TO IOControlBlock; Base: TYPE = LONG BASE POINTER; ShortIocb: TYPE = -- BASE RELATIVE-- POINTER TO IOControlBlock; IOControlBlock: TYPE = MACHINE DEPENDENT RECORD [ words: CARDINAL, buffer: LONG POINTER, mask: WORD, -- load location for nextOutput used: CARDINAL, -- nextInput only completion: WORD, next: ShortIocb, spare: WORD]; -- NB: QuadWord Alignment Input: PROC [Register] RETURNS [Data] = LOOPHOLE[DLionInputOutput.Input]; Output: PROC [Command, Register] = LOOPHOLE[DLionInputOutput.Output]; Data: TYPE = RECORD [WORD]; Command: TYPE = RECORD [WORD]; eOff: Command = [0B]; eEnableTrn: Command = [1B]; eEnableRcv: Command = [1B]; eTurnOff: Command = [2B]; Register: TYPE = RECORD [[0..17B]]; eEOCtl: Register = [14B]; eEICtl: Register = [5B]; eEStatus: Register = [1B]; eHaveEther: Register = [12B]; <> collision: WORD = 100B; underRun: WORD = 40B; notGoodAlign: WORD = 20B; overRun: WORD = 10B; notGoodCRC: WORD = 4B; notEvenLen: WORD = 2B; killed: WORD = 0FFFFH; recvStatusMask: WORD = notGoodAlign + overRun + notGoodCRC + notEvenLen; tranStatusMask: WORD = collision + underRun; <> eRcvEnabled: WORD = 2000B; eTrnEnabled: WORD = 400B; <> Shorten: PROC [iocb: IOCB] RETURNS [ShortIocb] = INLINE { <> RETURN[LOOPHOLE[Basics.LowHalf[LOOPHOLE[iocb]]]]}; MakeSureOff: PROC = INLINE { Output[eTurnOff, eEICtl]; THROUGH [0..2) UNTIL Basics.BITAND[Input[eEStatus], eRcvEnabled + eTrnEnabled] = 0 DO ENDLOOP}; nullHandle: PUBLIC Handle _ NIL; controlBlockSize: PUBLIC CARDINAL _ SIZE[IOControlBlock] + 4; hearSelf: PUBLIC BOOLEAN _ FALSE; <> csb: Handle _ LOOPHOLE[DLionInputOutput.IOPage + ethernetCSBOffset]; initialMask: WORD _ 0; QueueOutput: PUBLIC PROC [ handle: Handle, buffer: LONG POINTER, bytes: NAT, iocb: IOCB] = TRUSTED { iocb^ _ [ words: bytes/2 - 1, buffer: buffer, mask: initialMask, used: 0, completion: 0, next: NIL, spare: 1]; IF handle.nextOutput # NIL THEN handle.lastOutput.next _ Shorten[iocb]; IF handle.nextOutput = NIL AND iocb.completion = 0 THEN { handle.nextOutput _ Shorten[iocb]; Output[eEnableTrn, eEOCtl]; }; -- poke hardware handle.lastOutput _ iocb; }; QueueInput: PUBLIC PROC [handle: Handle, buffer: LONG POINTER, bytes: NAT, iocb: IOCB] = TRUSTED { iocb^ _ [ words: bytes/2, buffer: buffer, mask: 0, used: 0, completion: 0, next: NIL, spare: 0 ]; IF handle.nextInput # NIL THEN handle.lastInput.next _ Shorten[iocb]; IF handle.nextInput = NIL AND iocb.completion = 0 THEN handle.nextInput _ Shorten[iocb]; handle.lastInput _ iocb; }; MarkKilled: PUBLIC PROC [iocb: IOCB] = TRUSTED { iocb.completion _ killed; }; GetStatusAndLength: PUBLIC PROC [iocb: IOCB] RETURNS [status: Status, bytes: NAT] = TRUSTED { completion: WORD _ iocb.completion; IF completion = 0 THEN RETURN[pending, 0]; IF completion = killed THEN RETURN[otherError, 0]; IF iocb.used = 177777B THEN RETURN[packetTooLong, 0]; <> bytes _ iocb.used*2; SELECT Basics.BITAND[completion, recvStatusMask] FROM 0 => status _ ok; notGoodAlign+notEvenLen => status _ ok; notGoodAlign => status _ badAlignmentButOkCrc; notGoodCRC => status _ crc; notGoodCRC + notGoodAlign => status _ crcAndBadAlignment; overRun, overRun + notGoodAlign, overRun + notGoodCRC, overRun + notGoodCRC + notGoodAlign => status _ overrun; ENDCASE => status _ otherError; }; GetStatusAndCollisions: PUBLIC PROC [iocb: IOCB] RETURNS [status: Status, collisions: NAT] = TRUSTED { completion: WORD _ iocb.completion; IF completion = 0 THEN RETURN[pending, 0]; IF completion = killed THEN RETURN[otherError, 0]; IF iocb.mask = 0 THEN RETURN[tooManyCollisions, 16]; -- New microcode IF iocb.mask = 17777B THEN RETURN[tooManyCollisions, 16]; -- Old microcode SELECT Basics.BITAND[completion, tranStatusMask] FROM 0 => status _ ok; underRun, underRun + collision => status _ underrun; ENDCASE => status _ otherError; collisions _ SELECT iocb.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; }; GetPacketsMissed: PUBLIC PROC [handle: Handle] RETURNS [CARDINAL] = TRUSTED { RETURN[handle.missed]; }; GetNextDevice: PUBLIC PROC [handle: Handle] RETURNS [Handle] = { IF handle = nullHandle THEN RETURN[csb] ELSE RETURN[nullHandle]; }; 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]; Output[eEnableRcv, eEICtl]; }; TurnOff: PUBLIC PROC [handle: Handle] = TRUSTED { MakeSureOff[]; handle.nextInput _ NIL; handle.nextOutput _ NIL; }; SetPromiscuous: PUBLIC PROC [handle: Handle, promiscuous: BOOL] = TRUSTED { IF promiscuous THEN handle.host _ [0FFFFH, 0FFFFH, 0FFFFH] ELSE handle.host _ ProcessorFace.processorID; MakeSureOff[]; Output[eEnableRcv, eEICtl]; }; <<>> <> cleanupInitialized: BOOLEAN _ FALSE; savedCSB: ControllerStatusBlock; AddCleanup: PUBLIC PROC [handle: Handle] = TRUSTED { 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 => { <> MakeSureOff[]; handle^ _ [ host: savedCSB.host, nextInput: NIL, inputWakeups: 0, nextOutput: NIL, outputWakeups: 0, missed: savedCSB.missed, lastInput: NIL, lastOutput: NIL]; Output[eEnableRcv, eEICtl]}; ENDCASE; ENDLOOP; }; }.