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 CARDINAL ← SIZE[IOControlBlock];
hearSelf: PUBLIC BOOLEAN ← TRUE;
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: BOOL ← FALSE;
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: 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 => {
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.