EthernetOneHeadD0.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Swinehart modified September 23, 1982 1:35 pm
Birrell, November 3, 1983 11:20 am
Russ Atkinson (RRA) February 27, 1985 8:33:14 pm PST
DIRECTORY
Basics USING [DivMod],
D0InputOutput USING [CSB, IOPage, ethernet1In, ethernet1Out, ControllerNumber, GetNextController, nullControllerNumber],
DeviceCleanup USING [Item, Await],
EthernetOneFace,
MPCodes USING [Code, ethernetHostChanged],
PrincOps USING [zMISC, zSTARTIO],
PrincOpsUtils USING [LowHalf],
ProcessorFace USING [SetMP];
EthernetOneHeadD0:
PROGRAM
IMPORTS Basics, D0InputOutput, DeviceCleanup, PrincOpsUtils, ProcessorFace
EXPORTS EthernetOneFace = { OPEN EthernetOneFace;
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.
OCSB: TYPE = LONG POINTER TO OutputControllerStatusBlock;
OutputControllerStatusBlock:
TYPE =
MACHINE
DEPENDENT
RECORD [
reserved: WORD,
next: ShortIOCB,
unused1: WORD,
unused2: WORD,
interruptBit: WORD, -- words after here are unused by microcode
last: IOCB ]; -- last IOCB on output queue, valid if next#noIOCB
ICSB: TYPE = LONG POINTER TO InputControllerStatusBlock;
InputControllerStatusBlock:
TYPE =
MACHINE
DEPENDENT
RECORD [
reserved: WORD,
next: ShortIOCB,
host: LONG CARDINAL,
interruptBit: WORD,
missed: WORD, -- for debugging only
spare1: WORD,
spare2: WORD,
buffer: ARRAY [0..4) OF CARDINAL, -- words after here are unused by microcode
last: IOCB ]; -- last IOCB on input queue, valid if next#noIOCB
IOCB: TYPE = LONG POINTER TO IOControlBlock;
Beware that you don't automatically lengthen one of these. If you do you will end up with a pointer into your MDS rather then the first 64K where the IOCBs live. That won't work unless your MDS is also the first 64K.
ShortIOCB: TYPE = POINTER TO IOControlBlock;
IOControlBlock:
TYPE =
MACHINE
DEPENDENT
RECORD [
next: ShortIOCB,
mask: WORD,
spare: WORD,
completion: WORD,
used: CARDINAL, -- input only
length: CARDINAL,
buffer: LONG POINTER ]; -- NB: Must be QuadWord Aligned
StartIO:
PROC [SioParameter] =
MACHINE
CODE { PrincOps.zSTARTIO };
SioParameter:
TYPE =
RECORD [
WORD];
firstFixupOutput: SioParameter = [1B];
firstFixupInput: SioParameter = [2B];
firstReset: SioParameter = [3B];
secondFixupOutput: SioParameter = [4B];
secondFixupInput: SioParameter = [10B];
secondReset: SioParameter = [14B];
On old boards, output reg 0 of either task is control word for both tasks
On new boards, you must direct the output to the correct half
Input from reg 0 is device id
Input from reg 1 is net&host number switches
Output: PROC [Command, Register] = MACHINE CODE { PrincOps.zMISC, 6; };
Input:
PROC [Register]
RETURNS [
WORD] =
MACHINE
CODE { PrincOps.zMISC, 5; };
Command:
TYPE =
RECORD [
WORD];
enableInput: Command = [220B];
enableOutput: Command = [103B];
Register:
TYPE =
MACHINE
DEPENDENT
RECORD [
zero: [0..377B] ← 0,
controller: D0InputOutput.ControllerNumber,
register: [0..17B]];
completion bits
processed: WORD = 040000B;
error: WORD = 020000B;
hardwareError: WORD = 010000B;
fragment, tooLong: WORD = 004000B;
loadOverflow: WORD = 002000B;
nothingYet: WORD = 0;
001000 and 000400 are unused so far
Hardware error bits:
001: Bad Alignment
002: Bad Parity (between mem and shifter)
004: Memory Data Fault
010: CRC
020: Collision
040: Input Overrun
100: Output Overrun
200: Jam
noIOCB: ShortIOCB = LOOPHOLE[0];
Device:
TYPE =
RECORD [
board: Board,
in, out: D0InputOutput.ControllerNumber];
Board: TYPE = [0..2);
ICSBFronDevice:
PROC [device: Device]
RETURNS [
ICSB] =
INLINE {
RETURN[LOOPHOLE[@D0InputOutput.IOPage[device.in]]];
};
OCSBFronDevice:
PROC [device: Device]
RETURNS [
OCSB] =
INLINE {
RETURN[LOOPHOLE[@D0InputOutput.IOPage[device.out]]];
};
ControlRegister:
PROC [c: D0InputOutput.ControllerNumber]
RETURNS [Register] =
INLINE {
RETURN[[0,c,0]]; -- Register 0 is the control register
};
Shorten:
PROC [iocb:
IOCB]
RETURNS [ShortIOCB] =
INLINE {
Maybe we should check to be sure that the high half is zero
RETURN[PrincOpsUtils.LowHalf[iocb]];
};
EXPORTed TYPEs
DeviceHandle: PUBLIC TYPE = Device;
ControlBlockRecord: PUBLIC TYPE = IOControlBlock;
EXPORTed variables
nullDeviceHandle: PUBLIC DeviceHandle ← LOOPHOLE[123456B];
globalStateSize: PUBLIC CARDINAL ← 0;
controlBlockSize: PUBLIC CARDINAL ← SIZE[IOControlBlock];
hearSelf: PUBLIC BOOL ← TRUE;
Non EXPORTed things. Note that all the state information lives in the CSBs.
fixupInputBits: ARRAY Board OF SioParameter = [firstFixupInput, secondFixupInput];
fixupOutputBits: ARRAY Board OF SioParameter = [firstFixupOutput, secondFixupOutput];
resetBits: ARRAY Board OF SioParameter = [firstReset, secondReset];
QueueOutput:
PUBLIC
PROC [device: Device, buffer:
LONG
POINTER, length:
CARDINAL, cb:
IOCB] = {
out: OCSB = OCSBFronDevice[device];
cb^ ← [
next: noIOCB,
mask: 0,
spare: 0,
completion: 0,
used: 0,
length: length,
buffer: buffer ];
IF out.next=noIOCB
THEN {
new iocb, hardware idle
out.next ← Shorten[cb];
Output[enableOutput,ControlRegister[device.out]]; -- poke hardware
}
ELSE {
output active, add to end of chain
out.last.next ← Shorten[cb];
IF out.next=noIOCB
AND cb.completion=0
THEN {
oops, hardware went idle
out.next ← Shorten[cb];
Output[enableOutput,ControlRegister[device.out]]; -- poke hardware
};
};
out.last ← cb;
};
QueueInput:
PUBLIC
PROC [device: Device, buffer:
LONG
POINTER, length:
CARDINAL, cb:
IOCB] = {
in: ICSB = ICSBFronDevice[device];
cb^ ← [
next: noIOCB,
mask: 0,
spare: 0,
completion: 0,
used: 0,
length: length,
buffer: buffer ];
IF in.next#noIOCB THEN in.last.next ← Shorten[cb];
IF in.next=noIOCB
AND cb.completion=0
THEN
{
in.next ← Shorten[cb];
Poke the microcode so it will notice this buffer, delete the next line when the microcode is fixed
StartIO[fixupInputBits[device.board]];
Output[enableInput,ControlRegister[device.in]];
};
in.last ← cb;
};
GetStatus:
PUBLIC
PROC [cb:
IOCB]
RETURNS [status: Status] = {
RETURN [
SELECT cb.completion
FROM
0 => pending,
40000B => ok,
62000B => tooManyCollisions,
61000B => packetTooLong,
70001B => badAlignmentButOkCrc,
70010B => crc,
70011B => crcAndBadAlignment,
70040B, 70041B, 70050B, 70051B => overrun,
70100B, 70120B => underrun,
ENDCASE => otherError ];
};
GetRetries:
PUBLIC
PROC [cb:
IOCB]
RETURNS [
CARDINAL] = {
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,
17777B => 12,
37777B => 13,
77777B => 14,
177777B => 15,
ENDCASE => 16 ];
};
GetPacketLength:
PUBLIC
PROC [cb:
IOCB]
RETURNS [
CARDINAL] = {
RETURN [cb.used];
};
GetPacketsMissed:
PUBLIC
PROC [device: Device]
RETURNS [
CARDINAL] = {
RETURN [ICSBFronDevice[device].missed];
};
GetNextDevice:
PUBLIC
PROC [device: Device]
RETURNS [Device] = {
OPEN D0InputOutput;
IF device=nullDeviceHandle THEN device ← [0,nullControllerNumber,nullControllerNumber]
ELSE device.board ← device.board + 1;
device.in ← GetNextController[ethernet1In,device.in];
device.out ← GetNextController[ethernet1Out,device.out];
IF device.in=nullControllerNumber
OR device.out=nullControllerNumber
THEN
RETURN[nullDeviceHandle];
RETURN[device];
};
GetEthernet1Address:
PUBLIC
PROC [device: Device]
RETURNS [net: [0..256), host: HostAddress] = {
reg: Register = [controller: device.in, register: 1];
[net,host] ← Basics.DivMod[Input[reg],400B];
};
TurnOn:
PUBLIC
PROC [device: Device, host: HostAddress, inInterrupt, outInterrupt:
WORD, globalState: GlobalStatePtr] = {
board: Board = device.board;
out: OCSB = OCSBFronDevice[device];
in: ICSB = ICSBFronDevice[device];
StartIO[resetBits[board]];
out^ ← [
reserved: 0,
next: noIOCB,
unused1: 0,
unused2: 0,
interruptBit: outInterrupt,
last: NIL ];
in^ ← [
reserved: 0,
next: noIOCB,
host: host,
interruptBit: inInterrupt,
missed: 0,
spare1: 0,
spare2: 0,
buffer: [0,0,0,0],
last: NIL ];
StartIO[fixupInputBits[board]];
StartIO[fixupOutputBits[board]];
Output[enableInput,ControlRegister[device.in]];
};
TurnOff:
PUBLIC
PROC [device: Device] = {
StartIO[resetBits[device.board]];
};
InputHosts and InputHost would support input packets from multiple hosts. They are not
supported in this implementation.
InputHosts:
PUBLIC
PROC [device: DeviceHandle, inputHosts:
LONG
POINTER
TO HostArray] = {
ERROR;
};
InputHost:
PUBLIC
PROC[device: DeviceHandle, host: HostAddress] = {
ERROR;
};
MulticastCapabilities:
PUBLIC
PROC[device: DeviceHandle]
RETURNS [ canDo:
BOOL, multicastsEnabled:
BOOL] = {
RETURN [ canDo: FALSE, multicastsEnabled: FALSE]; };
There is no way to remove a cleanup procedure yet, so we have a flag to avoid duplicates.
alreadyInitializedCleanup: ARRAY Board OF BOOL ← ALL[FALSE];
savedICSB: InputControllerStatusBlock;
savedOCSB: OutputControllerStatusBlock;
AddCleanup:
PUBLIC
PROC [device: Device] = {
OPEN DeviceCleanup;
item: Item;
board: Board = device.board;
out: OCSB = OCSBFronDevice[device];
in: ICSB = ICSBFronDevice[device];
oldHost: LONG CARDINAL;
originalHost: HostAddress;
IF alreadyInitializedCleanup[device.board] THEN RETURN;
alreadyInitializedCleanup[device.board] ← TRUE;
originalHost ← GetEthernet1Address[device].host;
DO
SELECT Await[@item]
FROM
kill => {
StartIO[resetBits[board]];
};
turnOff => {
StartIO[resetBits[board]];
savedICSB ← in^;
savedOCSB ← out^;
oldHost ← in.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.
StartIO[resetBits[board]];
IF GetEthernet1Address[device].host#originalHost
THEN
ErrorHalt[MPCodes.ethernetHostChanged]; -- perhaps InLoaded on another machine
out^ ← [
reserved: 0,
next: noIOCB,
unused1: 0,
unused2: 0,
interruptBit: 0,
last: NIL ];
in^ ← [
reserved: 0,
next: noIOCB,
host: oldHost, -- Ugh, it would be nice if we could do something better
interruptBit: 0,
missed: 0,
spare1: 0,
spare2: 0,
buffer: [0,0,0,0],
last: NIL ];
StartIO[fixupInputBits[board]];
StartIO[fixupOutputBits[board]];
Output[enableInput,ControlRegister[device.in]];
};
ENDCASE;
ENDLOOP;
};
RemoveCleanup: PUBLIC PROC [device: Device] = { };
ErrorHalt: PROC [code: MPCodes.Code] = INLINE { ProcessorFace.SetMP[code]; DO ENDLOOP };
}.
LOG
Time: August 20, 1980 2:01 PM By: BLyon, Action: renamed EthernetFace and EthernetHeadD0 to EthernetOneFace and EthernetOneHeadD0, resp.
Time: August 25, 1980 5:36 PM By: BLyon, Action: added [ ELSE h.board ← h.board + 1] clause to GetNextDevice.
Time: September 4, 1980 11:02 PM By: HGM, Action: add hearSelf, use exported types.
Time: September 14, 1980 6:36
AM By:
HGM, Action: bug in bufferOverflow.
8-Jul-82 14:50:19 Taft Cleanup routine checks for Ethernet address changing at turnOn time.
Time: September 23, 1982 1:42 pm By: Swinehart, Action: Add Dummy multicast functions.