EthernetHeadDorado.mesa, HGM, 19-Sep-82 19:14:40
Last Edited by: Willie-Sue, July 12, 1984 1:09:54 pm PDT
Hal Murray, May 24, 1986 6:52:00 pm PDT
DIRECTORY
DeviceCleanup USING [Item, Await],
DoradoInputOutput USING [InputNoPE, IOAddress, Output],
EthernetFace USING [Status],
MPCodes USING [Code, ethernetHostChanged],
PrincOps USING [alpha, SD, zMISC],
PrincOpsUtils USING [BITAND, BITOR, LowHalf],
ProcessorFace USING [SetMP, ProcessorID, processorID],
TrapSupport USING [BumpPC, OpTrapTable];
EthernetHeadDorado: CEDAR PROGRAM
IMPORTS DeviceCleanup, DoradoInputOutput, PrincOpsUtils, ProcessorFace, TrapSupport
EXPORTS EthernetFace
SHARES ProcessorFace =
BEGIN
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.
If the first word of the HostNumber is all ones then the input mode will accept all packets!!
CSB: TYPE = LONG POINTER TO ControllerStatusBlock;
ControllerStatusBlock: TYPE = MACHINE DEPENDENT RECORD [
host: ProcessorFace.ProcessorID,
nextInput: ShortIocb,
inputWakeups: WORD,
nextOutput: ShortIocb,
outputWakeups: WORD,
missed: CARDINAL,
lastInput: IOCB, -- last IOCB on input queue, valid if nextInput#NIL
lastOutput: IOCB]; -- last IOCB on output queue, valid if nextOutput#NIL
IOCB: TYPE = LONG POINTER TO IOControlBlock;
ShortIocb: TYPE = --Base RELATIVE-- POINTER TO IOControlBlock;
IOControlBlock: TYPE = MACHINE DEPENDENT RECORD [
bytes: CARDINAL,
buffer: LONG POINTER,
retries: WORD,   -- set by handle
used: CARDINAL,  -- input only, length of the received packet, in bytes
completion: CompletionStatus nullCompletion, -- raw status from microcode, nonzero at end
next: ShortIocb ];
The 10MB Ethernet uses TIOA = 26b as Control when output and as Status when it is read. The data reg is TIOA=25b.
ECtl: DoradoInputOutput.IOAddress = 26B;
CompletionStatus: TYPE = MACHINE DEPENDENT RECORD [
hardware (0: 0..15): SELECT OVERLAID * FROM
input => [
overflow (0: 0..0): BOOLEAN,
noDribble (0: 1..1): BOOLEAN,
fill1 (0: 2..2): [0..1) ← 0,
goodCRC (0: 3..3): BOOLEAN,
fill2 (0: 4..5): [0..3) ← 0,
oddByte (0: 6..6): BOOLEAN,
overRun (0: 7..7): BOOLEAN,
fill3 (0: 8..15): [0..400B) ← 0],
output => [
fill4 (0: 0..7): [0..400B) ← 0,
rxOn (0: 8..8): BOOLEAN,
txOn (0: 9..9): BOOLEAN,
loopBack (0: 10..10): BOOLEAN,
IECByPass (0: 11..11): BOOLEAN, -- error if found one by the head
noWakeups (0: 12..12): BOOLEAN, -- error if found one by the head
txStatus (0: 13..14): {txOK (0), underRun (1), collision (2), maxCollisions (3)},
txDataParityError (0: 15..15): BOOLEAN],
ENDCASE];
nullCompletion: CompletionStatus = LOOPHOLE[0];
killedCompletion: CompletionStatus = LOOPHOLE[0FFFFH];
inputStatusMask: input CompletionStatus = LOOPHOLE[176400B]; -- oddByte OK
inputGoodStatus: input CompletionStatus =
[input[overflow: FALSE, noDribble: TRUE, goodCRC: TRUE, oddByte: FALSE, overRun: FALSE]];
outputErrorMask: output CompletionStatus =
[output[rxOn: FALSE, txOn: FALSE, loopBack: FALSE, IECByPass: TRUE,
noWakeups: TRUE, txStatus: maxCollisions, txDataParityError: TRUE]];
EtherNetOne uses 177600B for input and 177610B for output
csb: CSB LOOPHOLE[LONG[177700B]];
Shorten: PROCEDURE [iocb: IOCB] RETURNS [ShortIocb] = TRUSTED INLINE {
Maybe we should check to be sure that the high half is zero
RETURN[LOOPHOLE[PrincOpsUtils.LowHalf[iocb]]]; };
aOff: PrincOps.alpha = 250B;
MakeSureOff: PROC = TRUSTED MACHINE CODE { PrincOps.zMISC, aOff};
Reset 10 MB Ethernet hardware and tasks (should be in DoradoInputOutput) Does NOT turn on either the transmittor or the receiver
EXPORTed TYPEs
Handle: PUBLIC TYPE = LONG POINTER TO ControllerStatusBlock;
Status: PUBLIC TYPE = EthernetFace.Status;
ControlBlockRecord: PUBLIC TYPE = IOControlBlock;
EXPORTed variables
nullHandle: PUBLIC Handle ← NIL;
controlBlockSize: PUBLIC CARDINALSIZE[IOControlBlock];
hearSelf: PUBLIC BOOLEANTRUE;
private stuff
the following HWMode values match those in PilotNSEther.mc, for turning on the 10MB receiver in either regular or loopback mode
HWMode: TYPE = MACHINE DEPENDENT {hwNormal (173000B), hwLoopBack (173100B)};
ResetBits: WORD = 40B + 20B;
EnableTx: WORD = 47777B;
hwMode: HWMode ← hwNormal;
LoopbackMode: TYPE = {normal, loopBack};
SetLoopbackMode: PROC [mode: LoopbackMode] = {
hwMode ← SELECT mode FROM
normal => hwNormal,
loopBack => hwLoopBack,
ENDCASE => ERROR;
};
QueueOutput: PUBLIC PROC [
handle: Handle, buffer: LONG POINTER, bytes: NAT, iocb: IOCB] = TRUSTED {
iocb^ ← [
bytes: bytes, buffer: buffer, retries: 0, used: 0, completion: nullCompletion, next: NIL];
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[EnableTx, ECtl]; }; -- poke hardware
handle.lastOutput ← iocb;
};
QueueInput: PUBLIC PROC [
  handle: Handle, buffer: LONG POINTER, bytes: NAT, iocb: IOCB] = TRUSTED {
iocb^ ← [
bytes: bytes, buffer: buffer, retries: 0, used: 0, completion: nullCompletion, next: NIL];
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 ← killedCompletion;
};
GetStatusAndLength: PUBLIC PROC [iocb: IOCB] RETURNS [status: Status, bytes: NAT] = TRUSTED {
IF iocb.completion = nullCompletion THEN RETURN[pending, 0];
IF iocb.completion = killedCompletion THEN RETURN[killedByDriver, 0];
IF LOOPHOLE[PrincOpsUtils.BITAND[LOOPHOLE[iocb.completion], LOOPHOLE[inputStatusMask]], CompletionStatus] = inputGoodStatus THEN
status ← ok
ELSE
SELECT TRUE FROM
iocb.completion.overflow => status ← packetTooLong; -- iocb.used = 0 => bounds fault
iocb.completion.overRun => status ← overrun;
~iocb.completion.goodCRC => status ← crc;
~iocb.completion.noDribble, iocb.completion.oddByte => status ← badAlignmentButOkCrc;
ENDCASE => status ← otherError;
IF iocb.used = 0 THEN bytes ← 0 ELSE bytes ← iocb.used - 2;
};
GetStatusAndCollisions: PUBLIC PROC [iocb: IOCB] RETURNS [status: Status, collisions: NAT] = TRUSTED {
IF iocb.completion = nullCompletion THEN RETURN[pending, 0];
IF iocb.completion = killedCompletion THEN RETURN[killedByDriver, 0];
collisions ← IF iocb.retries = 17 THEN iocb.retries-1 ELSE iocb.retries;
IF PrincOpsUtils.BITAND[LOOPHOLE[iocb.completion], LOOPHOLE[outputErrorMask]] = 0 THEN
status ← ok
ELSE
SELECT iocb.completion.txStatus FROM
txOK => status ← ok;
underRun, collision => status ← underrun;
maxCollisions => status ← tooManyCollisions;
ENDCASE => status ← otherError;
};
GetPacketsMissed: PUBLIC PROC [handle: Handle] RETURNS [CARDINAL] = TRUSTED {
RETURN[handle.missed];
};
unimplemented: BOOLFALSE;
FakeMakeSureOff: PROC = TRUSTED {
TrapSupport.BumpPC[2];
unimplemented ← TRUE;
};
GetNextDevice: PUBLIC PROC [handle: Handle] RETURNS [new: Handle ← nullHandle] = TRUSTED {
opTrapTable: POINTER TO POINTER TO TrapSupport.OpTrapTable = LOOPHOLE[PrincOps.SD+137B];
foo: WORD;
IF handle # nullHandle THEN RETURN;
opTrapTable.misc[aOff] ← LOOPHOLE[FakeMakeSureOff];
MakeSureOff[]; -- Bogus microcode => UnimplementedInstruction
IF unimplemented THEN RETURN; -- Bogus microcode
DoradoInputOutput.Output[PrincOpsUtils.BITOR[LOOPHOLE[hwMode], ResetBits], ECtl];
foo ← DoradoInputOutput.InputNoPE[ECtl];
IF foo = 0 THEN RETURN; -- No Hardware (trial and error, 1 test case)
new ← csb;
};
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];
DoradoInputOutput.Output[PrincOpsUtils.BITOR[LOOPHOLE[hwMode], ResetBits], ECtl];
DoradoInputOutput.Output[LOOPHOLE[hwMode], ECtl]; -- turns on receiver
};
TurnOff: PUBLIC PROC [handle: Handle] = {
MakeSureOff[];
};
SetPromiscuous: PUBLIC PROC [handle: Handle, promiscuous: BOOL] = TRUSTED {
SELECT promiscuous FROM
TRUE => handle.host ← [0FFFFH, 0FFFFH, 0FFFFH];
FALSE => handle.host ← ProcessorFace.processorID;
ENDCASE => ERROR;
};
SetOutputDelay: PUBLIC PROC [handle: Handle, microseconds: CARDINAL] = {};
?? I don't know if the hardware supports this. CSB change may be needed.
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 => {
foo: WORD;
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[];
IF unimplemented THEN
ErrorHalt[MPCodes.ethernetHostChanged]; -- Changed to wrong microcode
DoradoInputOutput.Output[PrincOpsUtils.BITOR[LOOPHOLE[hwMode], ResetBits], ECtl];
foo ← DoradoInputOutput.InputNoPE[ECtl];
IF foo = 0 THEN
ErrorHalt[MPCodes.ethernetHostChanged]; -- Hardware vanished
handle^ ← [
host: savedCSB.host,
nextInput: NIL, inputWakeups: 0, nextOutput: NIL, outputWakeups: 0,
missed: 0, lastInput: NIL, lastOutput: NIL];
DoradoInputOutput.Output[PrincOpsUtils.BITOR[LOOPHOLE[hwMode], ResetBits], ECtl];
DoradoInputOutput.Output[LOOPHOLE[hwMode], ECtl]; };
ENDCASE;
ENDLOOP};
RemoveCleanup: PUBLIC PROC [handle: Handle] = {};
ErrorHalt: PROC [code: MPCodes.Code] = INLINE { ProcessorFace.SetMP[code]; DO ENDLOOP };
END. -- EthernetHeadDorado.