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; }; }. lEthernetHeadDLion.mesa Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. Sandman on: 18-Mar-81 17:20:56 Blyon on : 22-Apr-81 17:43:34 Russ Atkinson (RRA) February 19, 1985 3:33:17 pm PST Hal Murray, May 24, 1986 6:58:43 pm PDT Beware: This module doesn't play the keep-the-map-honest game. (The microcode doesn't update the dirty or referenced bits.) That code was just too ugly. These are the data structures that the microcode knows about. If some other module (for example, a diagnostic) ever needs this info, it should probably get split out into a separate module. For now, it is lumped in here to avoid cluttering up the world with yet another file. completion bits other status bits The microcode assumes that NIL is 0. Maybe we should check to be sure that the high half is zero Non EXPORTed things. Note that all the state information lives in the CSBs. Bug in old microcode will prevent this from happening There is no way to remove a cleanup procedure yet, so we have a flag to avoid duplicates. Note that this does NOT really put things back together. It simply smashes things to a safe state. The intention is that the driver will notice that nothing is happening and then call TurnOff+TurnOn to reset things. That allows Pilot to reset the GMT clock on the way back from the debugger without getting tangled up with the normal Ethernet driver. Κ e˜codešœ™Kšœ Οmœ7™BKšœ™Kšœ™K™4K™'—K˜Kšœ˜™˜K˜šΟk ˜ Kšœžœžœ˜+Kšœžœ˜"Kšœžœ˜/Kšœ žœ ˜Kšœžœ˜/K˜—šœžœž˜ Kšžœ7˜>Kšžœ ˜Kšžœ˜K˜Kšœ•™•K˜Kšœžœžœ˜1Kš œžœžœžœžœžœ˜