-- EthernetHeadDLion.mesa (last edited by: Sandman on: 18-Mar-81 17:20:56) -- EthernetHeadDLion.mesa (last edited by: Blyon on : 22-Apr-81 17:43:34) DIRECTORY Environment USING [Base, Long, wordsPerPage], Inline USING [BITAND, LowHalf], HeadStartChain USING [Start], DeviceCleanup USING [Item, Await], DLionInputOutput USING [IOPage, Input, Output], SpecialSystem USING [ProcessorID], EthernetFace; EthernetHeadDLion: PROGRAM IMPORTS DLionInputOutput, DeviceCleanup, RemainingStartChain: HeadStartChain, Inline EXPORTS EthernetFace, HeadStartChain = BEGIN OPEN Inline, EthernetFace; -- 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. CSB: TYPE = LONG POINTER TO ControllerStatusBlock; ControllerStatusBlock: TYPE = MACHINE DEPENDENT RECORD [ host: SpecialSystem.ProcessorID, input: ShortIocb, inputWakeups: WORD, output: ShortIocb, outputWakeups: WORD, missed: CARDINAL, lastInput: IOCB, -- last IOCB on input queue, valid if input#noIocb lastOutput: IOCB]; -- last IOCB on output queue, valid if output#noIocb EthernetCSBOffset: CARDINAL = 12*16 + 0; -- 0C0X IOCB: TYPE = LONG POINTER TO IOControlBlock; ShortIocb: TYPE = Environment.Base RELATIVE POINTER TO IOControlBlock; IOControlBlock: TYPE = MACHINE DEPENDENT RECORD [ length: CARDINAL, buffer: LONG POINTER, mask: WORD, -- load location for output used: CARDINAL, -- input only completion: WORD, next: ShortIocb, spare: WORD]; -- NB: QuadWord Alignment Input: PROCEDURE [Register] RETURNS [Data] = LOOPHOLE[DLionInputOutput.Input]; Output: PROCEDURE [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]; -- completion bits collision: WORD = 100B; underRun: WORD = 40B; notGoodAlign: WORD = 20B; overRun: WORD = 10B; notGoodCRC: WORD = 4B; notEvenLen: WORD = 2B; -- other status bits eRcvEnabled: WORD = 2000B; eTrnEnabled: WORD = 400B; noIocb: ShortIocb = LOOPHOLE[0]; Shorten: PROCEDURE [iocb: IOCB] RETURNS [ShortIocb] = INLINE { -- Maybe we should check to be sure that the high half is zero RETURN[Inline.LowHalf[iocb]]}; FixAlignment: PROCEDURE [iocb: IOCB] RETURNS [IOCB] = INLINE { LOOPHOLE[iocb, Environment.Long].lowbits ← BITAND[ LOOPHOLE[iocb, Environment.Long].lowbits + 7, 177770B]; RETURN[iocb]}; MakeSureOff: PROC = INLINE { Output[eTurnOff, eEICtl]; THROUGH [0..2) UNTIL BITAND[Input[eEStatus], eRcvEnabled + eTrnEnabled] = 0 DO ENDLOOP}; -- EXPORTed TYPEs DeviceHandle: PUBLIC TYPE = RECORD [CARDINAL]; ControlBlockRecord: PUBLIC TYPE = IOControlBlock; -- EXPORTed variables nullDeviceHandle: PUBLIC DeviceHandle ← [123456B]; self: DeviceHandle = [0]; globalStateSize: PUBLIC CARDINAL ← 0; controlBlockSize: PUBLIC CARDINAL; hearSelf: PUBLIC BOOLEAN ← FALSE; -- Non EXPORTed things. Note that all the state information lives in the CSBs. csb: CSB ← DLionInputOutput.IOPage + EthernetCSBOffset; haveEther: BOOLEAN; QueueOutput: PUBLIC PROCEDURE [ device: DeviceHandle, buffer: LONG POINTER, length: CARDINAL, cb: IOCB] = { count: CARDINAL; p: LONG POINTER ← buffer; IF device # self THEN RETURN; count ← length - 1; DO foo: UNSPECIFIED ← (p + count)↑; IF count < Environment.wordsPerPage THEN {foo ← p↑; EXIT}; count ← count - Environment.wordsPerPage; ENDLOOP; cb ← FixAlignment[cb]; cb↑ ← [ next: noIocb, mask: 0, spare: 1, completion: 0, used: 0, length: length - 1, buffer: buffer]; IF csb.output = noIocb THEN { -- new iocb, hardware idle csb.output ← Shorten[cb]; Output[eEnableTrn, eEOCtl]} -- poke hardware ELSE { -- output active, add to end of chain csb.lastOutput.next ← Shorten[cb]; IF csb.output = noIocb AND cb.completion = 0 THEN { -- oops, hardware went idle csb.output ← Shorten[cb]; Output[eEnableTrn, eEOCtl]}}; -- poke hardware csb.lastOutput ← cb}; QueueInput: PUBLIC PROCEDURE [ device: DeviceHandle, buffer: LONG POINTER, length: CARDINAL, cb: IOCB] = { IF device # self THEN RETURN; cb ← FixAlignment[cb]; cb↑ ← [ next: noIocb, mask: 0, spare: 0, completion: 0, used: 0, length: length, buffer: buffer]; IF csb.input # noIocb THEN csb.lastInput.next ← Shorten[cb]; IF csb.input = noIocb AND cb.completion = 0 THEN { csb.input ← Shorten[cb]; Output[eEnableRcv, eEICtl]}; csb.lastInput ← cb}; GetStatus: PUBLIC PROCEDURE [cb: IOCB] RETURNS [status: Status] = { completion: WORD; cb ← FixAlignment[cb]; IF (completion ← cb.completion) = 0 THEN RETURN[pending]; IF cb.spare = 0 THEN { IF cb.length = 177777B THEN RETURN[packetTooLong]; status ← SELECT BITAND[ completion, notGoodAlign + overRun + notGoodCRC + notEvenLen] FROM 0 => ok, notGoodAlign+notEvenLen => ok, -- dippled bit case - someday drippled should be returned notGoodAlign => badAlignmentButOkCrc, notGoodCRC => crc, notGoodCRC + notGoodAlign => crcAndBadAlignment, overRun, overRun + notGoodAlign, overRun + notGoodCRC, overRun + notGoodCRC + notGoodAlign => overrun, ENDCASE => otherError; IF status = ok THEN { count: CARDINAL ← cb.length; p: LONG POINTER ← cb.buffer; DO (p + count)↑ ← (p + count)↑; IF count < Environment.wordsPerPage THEN {p↑ ← p↑; EXIT}; count ← count - Environment.wordsPerPage; ENDLOOP}; RETURN[status]}; IF cb.spare = 1 THEN { IF cb.mask = 17777B THEN RETURN[tooManyCollisions]; RETURN[ SELECT BITAND[completion, collision + underRun] FROM 0 => ok, underRun, underRun + collision => underrun, ENDCASE => otherError]}; RETURN[otherError]}; GetRetries: PUBLIC PROCEDURE [cb: IOCB] RETURNS [CARDINAL] = { cb ← FixAlignment[cb]; 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, ENDCASE => 16]}; GetPacketLength: PUBLIC PROCEDURE [cb: IOCB] RETURNS [CARDINAL] = { cb ← FixAlignment[cb]; RETURN[cb.used]}; GetPacketsMissed: PUBLIC PROCEDURE [device: DeviceHandle] RETURNS [CARDINAL] = { RETURN[IF device # self THEN 0 ELSE csb.missed]}; GetNextDevice: PUBLIC PROCEDURE [device: DeviceHandle] RETURNS [DeviceHandle] = { IF ~haveEther THEN RETURN[nullDeviceHandle]; IF device = nullDeviceHandle THEN RETURN[self] ELSE RETURN[nullDeviceHandle]}; TurnOn: PUBLIC PROCEDURE [ device: DeviceHandle, host: SpecialSystem.ProcessorID, inInterrupt, outInterrupt: WORD, globalState: GlobalStatePtr] = { IF device # self THEN RETURN; MakeSureOff[]; csb↑ ← [ host: host, input: noIocb, inputWakeups: inInterrupt, output: noIocb, outputWakeups: outInterrupt, missed: 0, lastInput: NIL, lastOutput: NIL]; Output[eEnableRcv, eEICtl]}; TurnOff: PUBLIC PROCEDURE [device: DeviceHandle] = { IF device # self THEN RETURN; MakeSureOff[]}; -- There is no way to remove a cleanup procedure yet, so we have a flag to avoid duplicates. cleanupInitialized: BOOLEAN ← FALSE; savedCSB: ControllerStatusBlock; AddCleanup: PUBLIC PROCEDURE [device: DeviceHandle] = { OPEN DeviceCleanup; item: Item; oldHost: SpecialSystem.ProcessorID; IF cleanupInitialized OR ~haveEther THEN RETURN; cleanupInitialized ← TRUE; DO SELECT Await[@item] FROM kill => MakeSureOff[]; turnOff => {MakeSureOff[]; savedCSB ← csb↑; oldHost ← csb.host}; turnOn => { -- 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. MakeSureOff[]; csb↑ ← [ host: oldHost, -- Ugh, it would be nice if we could do something better input: noIocb, inputWakeups: 0, output: noIocb, outputWakeups: 0, missed: 0, lastInput: NIL, lastOutput: NIL]; Output[eEnableRcv, eEICtl]}; ENDCASE; ENDLOOP}; RemoveCleanup: PUBLIC PROCEDURE [device: DeviceHandle] = {}; -- Other routines to keep the rest of the world happy Start: PUBLIC PROCEDURE = { IF (haveEther ← Input[eHaveEther] = Data[1]) THEN controlBlockSize ← SIZE[IOControlBlock] + 4 ELSE controlBlockSize ← 0; RemainingStartChain.Start[]}; END. -- EthernetHeadDLion. LOG Time: October 29, 1980 11:16 AM By: Sandman, Action: create file. Time: 18-Mar-81 17:20:25 By: Sandman, Action: Removed minimum packet length test, general cleanup.