EthernetHeadDLion.mesa
Copyright © 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.
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 = {
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.
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];
completion bits
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;
other status bits
eRcvEnabled: WORD = 2000B;
eTrnEnabled: WORD = 400B;
The microcode assumes that NIL is 0.
Shorten: PROC [iocb: IOCB] RETURNS [ShortIocb] = INLINE {
Maybe we should check to be sure that the high half is zero
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 CARDINALSIZE[IOControlBlock] + 4;
hearSelf: PUBLIC BOOLEANFALSE;
Non EXPORTed things. Note that all the state information lives in the CSBs.
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];
Bug in old microcode will prevent this from happening
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];
};
There is no way to remove a cleanup procedure yet, so we have a flag to avoid duplicates.
cleanupInitialized: BOOLEANFALSE;
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 => {
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[];
handle^ ← [
host: savedCSB.host,
nextInput: NIL,
inputWakeups: 0,
nextOutput: NIL,
outputWakeups: 0,
missed: savedCSB.missed,
lastInput: NIL,
lastOutput: NIL];
Output[eEnableRcv, eEICtl]};
ENDCASE;
ENDLOOP;
};
}.