EthernetOneHeadDorado.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Taft, February 13, 1983 1:20 pm
Levin, August 8, 1983 3:04 pm
Russ Atkinson (RRA) February 19, 1985 2:07:23 pm PST
Some modifications should be made to clean up multicast receiving, but they would cause
incompatibilities in existing volumes (Germ, Othello, CoPilot, etc.)
DIRECTORY
Basics USING [HighByte],
DeviceCleanup USING [Item, Await],
DoradoInputOutput USING [Input, IOAddress, Output, ResetEther],
EthernetOneFace,
PrincOpsUtils USING [BITAND, LowHalf],
MPCodes USING [Code, ethernetHostChanged],
ProcessorFace USING [SetMP];
EthernetOneHeadDorado: PROGRAM
IMPORTS Basics, DeviceCleanup, DoradoInputOutput, PrincOpsUtils, ProcessorFace
EXPORTS EthernetOneFace =
BEGIN OPEN EthernetOneFace;
OCSB: TYPE = LONG POINTER TO OutputControllerStatusBlock;
OutputControllerStatusBlock: TYPE = MACHINE DEPENDENT RECORD [
next: ShortIOCB,
interruptBit: WORD,
minPacketSpacing: CARDINAL,
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 [
next: ShortIOCB,
interruptBit: WORD,
host: WORD,  -- if >=0, old-style single-host receive microcode will be used.
missed: CARDINAL,
words after here (except "hosts") are unused by microcode
last: IOCB, -- last IOCB on input queue, valid if next#noIOCB
interstices: ARRAY [6..30B) OF WORDNULL, -- hosts start 30B beyond next
for compatibility with existing location of oCSB.
hosts: HostArray]; -- current set of hosts to which receiver code responds
IOCB: TYPE = LONG POINTER TO IOControlBlock;
ShortIOCB: TYPE = POINTER TO IOControlBlock;
IOControlBlock: TYPE = MACHINE DEPENDENT RECORD [
next: ShortIOCB,
completion: CompletionStatus,
used: CARDINAL, -- input only
load: CARDINAL, -- output only
length: CARDINAL,
buffer: LONG POINTER,
words after here are unused by microcode
direction: {input, output},
fill: [0..77777B] ← NULL];
CompletionStatus: TYPE = MACHINE DEPENDENT RECORD [
microcode (0: 0..7): {pending (0), done (1), bufferOverflow (2), loadOverflow (3)},
hardware (0: 8..15): SELECT OVERLAID * FROM
input => [
rxCollision (0: 8..8),
fill1 (0: 9..9),
rxDataLate (0: 10..10),
fill2 (0: 11..11),
rxCRCError (0: 12..12),
fill3 (0: 13..14),
rxIncTrans (0: 15..15): BOOLEAN],
output => [
rxOn (0: 8..8),
txOn (0: 9..9),
loopBack (0: 10..10),
txCollision (0: 11..11),
noWakeups (0: 12..12),
txDataLate (0: 13..13),
singleStep (0: 14..14),
txFifoPE (0: 15..15): BOOLEAN],
ENDCASE];
multiHosts: CARDINAL = 177777B;
inputErrorMask: input CompletionStatus = [
microcode: pending,
hardware: input[rxCollision: TRUE, fill1: FALSE, rxDataLate: TRUE, fill2: FALSE,
rxCRCError: TRUE, fill3: FALSE, rxIncTrans: TRUE]];
outputErrorMask: output CompletionStatus = [
microcode: pending,
hardware: output[rxOn: FALSE, txOn: FALSE, loopBack: FALSE, txCollision: TRUE,
noWakeups: FALSE, txDataLate: TRUE, singleStep: FALSE, txFifoPE: TRUE]];
nullCompletion: CompletionStatus = LOOPHOLE[0];
Command: TYPE = RECORD [WORD];
enableInput: Command = [173377B];
enableOutput: Command = [047777B];
control: DoradoInputOutput.IOAddress = 16B;
noIOCB: ShortIOCB = LOOPHOLE[0];
This implementation has no concept of a "device", but something has to
be exported to EthernetOneFace (see below)
Device: TYPE = RECORD [WORD];
Shorten: PROCEDURE [iocb: IOCB] RETURNS [ShortIOCB] = INLINE
BEGIN
Maybe we should check to be sure that the high half is zero
RETURN[PrincOpsUtils.LowHalf[iocb]];
END;
EXPORTed TYPEs
DeviceHandle: PUBLIC TYPE = Device;
ControlBlockRecord: PUBLIC TYPE = IOControlBlock;
EXPORTed variables
nullDeviceHandle: PUBLIC DeviceHandle ← LOOPHOLE[123456B];
globalStateSize: PUBLIC CARDINAL ← 0;
controlBlockSize: PUBLIC CARDINALSIZE[IOControlBlock];
hearSelf: PUBLIC BOOLEANTRUE;
Non EXPORTed things. Note that all the state information lives in the CSBs.
oCSB: OCSB = LOOPHOLE[LONG[177610B]];
iCSB: ICSB = LOOPHOLE[LONG[177600B]];
QueueOutput: PUBLIC PROCEDURE [
device: Device, buffer: LONG POINTER, length: CARDINAL, cb: IOCB] =
BEGIN
cb^ ← [next: noIOCB, completion: nullCompletion, used: 0, load: 0, length: length,
buffer: buffer, direction: output];
IF oCSB.next # noIOCB THEN oCSB.last.next ← Shorten[cb];
IF oCSB.next = noIOCB AND cb.completion = nullCompletion THEN
BEGIN
oCSB.next ← Shorten[cb];
DoradoInputOutput.Output[enableOutput, control];
END;
oCSB.last ← cb;
END;
QueueInput: PUBLIC PROCEDURE [
device: Device, buffer: LONG POINTER, length: CARDINAL, cb: IOCB] =
BEGIN
cb^ ← [next: noIOCB, completion: nullCompletion, used: 0, load: 0, length: length,
buffer: buffer, direction: input];
IF iCSB.next # noIOCB THEN iCSB.last.next ← Shorten[cb];
IF iCSB.next = noIOCB AND cb.completion = nullCompletion THEN
iCSB.next ← Shorten[cb];
iCSB.last ← cb;
END;
GetStatus: PUBLIC PROCEDURE [cb: IOCB] RETURNS [status: Status] =
BEGIN OPEN cb.completion;
RETURN[
SELECT microcode FROM
pending => pending,
done => SELECT cb.direction FROM
input => IF PrincOpsUtils.BITAND[
LOOPHOLE[cb.completion], LOOPHOLE[inputErrorMask] ]=0 THEN ok
ELSE SELECT TRUE FROM
rxCollision => otherError,
rxDataLate => overrun,
rxCRCError => IF rxIncTrans THEN crcAndBadAlignment ELSE crc,
rxIncTrans => badAlignmentButOkCrc,
ENDCASE => otherError,
output => IF PrincOpsUtils.BITAND[
LOOPHOLE[cb.completion], LOOPHOLE[outputErrorMask] ]=0 THEN ok
ELSE SELECT TRUE FROM
txCollision => tooManyCollisions,
txDataLate => underrun,
txFifoPE => otherError,
ENDCASE => otherError,
ENDCASE => otherError,
bufferOverflow => packetTooLong,
loadOverflow => tooManyCollisions,
ENDCASE => otherError];
END;
GetRetries: PUBLIC PROCEDURE [cb: IOCB] RETURNS [CARDINAL] =
BEGIN
RETURN[
SELECT cb.load 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];
END;
GetPacketLength: PUBLIC PROCEDURE [cb: IOCB] RETURNS [CARDINAL] =
BEGIN RETURN[cb.used]; END;
GetPacketsMissed: PUBLIC PROCEDURE [device: Device] RETURNS [CARDINAL] =
BEGIN RETURN[iCSB.missed]; END;
nonNullDeviceHandle: DeviceHandle = LOOPHOLE[22334]; -- # nullDeviceHandle
GetNextDevice: PUBLIC PROCEDURE [device: Device] RETURNS [Device] =
BEGIN
RETURN[IF device=nullDeviceHandle THEN nonNullDeviceHandle ELSE nullDeviceHandle];
END;
GetEthernet1Address: PUBLIC PROCEDURE [device: Device]
RETURNS [net: [0..256), host: HostAddress] =
BEGIN
RETURN[net: 0, host: Basics.HighByte[DoradoInputOutput.Input[control]]];
END;
TurnOn: PUBLIC PROCEDURE [
device: Device, host: HostAddress, inInterrupt, outInterrupt: WORD,
globalState: GlobalStatePtr] =
BEGIN
DoradoInputOutput.ResetEther[];
iCSB.next ← noIOCB;
iCSB.interruptBit←inInterrupt;
iCSB.missed𡤀
iCSB.last←NIL;
If in multiHost mode, set all hosts true for promiscuous mode, or add specified
host to currently-accepted list.
IF iCSB.host#multiHosts THEN iCSB.host←host
ELSE IF host = 0 THEN iCSB.hosts←ALL[TRUE]
ELSE iCSB.hosts[host]←TRUE;
oCSB^ ← [next: noIOCB, interruptBit: outInterrupt, last: NIL,
for now, always space output packets at least 500 microseconds apart
minPacketSpacing: 500/32];
DoradoInputOutput.Output[enableInput, control];
END;
TurnOff: PUBLIC PROCEDURE [device: Device] =
BEGIN DoradoInputOutput.ResetEther[]; END;
The next three procedure should probably be monitored, or be called from properly
locked places.
inputHosts is complete specification (including 0, if broadcasts are to be accepted),
of the hosts whose input packets the microcode will accept.
InputHosts: PUBLIC PROCEDURE [device: DeviceHandle,
inputHosts: LONG POINTER TO HostArray] = {
iCSB.hosts←inputHosts^;
iCSB.host←multiHosts; }; -- enable multicast host array
InputHost: PUBLIC PROCEDURE[device: DeviceHandle, host: HostAddress] = {
iCSB.host←host; }; -- disable multicast host array, specify host.
MulticastCapabilities: PUBLIC PROCEDURE[device: DeviceHandle]
RETURNS [ canDo: BOOLEAN, multicastsEnabled: BOOLEAN] = {
RETURN [ canDo: TRUE, multicastsEnabled: iCSB.host = multiHosts]; };
There is no way to remove a cleanup procedure yet, so we have a flag to avoid duplicates.
alreadyInitializedCleanup: BOOLEANFALSE;
savedICSB: InputControllerStatusBlock;
savedOCSB: OutputControllerStatusBlock;
AddCleanup: PUBLIC PROCEDURE [device: Device] =
BEGIN OPEN DeviceCleanup;
item: Item;
originalHost: HostAddress ← GetEthernet1Address[device].host;
IF alreadyInitializedCleanup THEN RETURN;
alreadyInitializedCleanup ← TRUE;
DO
SELECT Await[@item] FROM
kill => BEGIN DoradoInputOutput.ResetEther[]; END;
turnOff =>
BEGIN
DoradoInputOutput.ResetEther[];
savedICSB ← iCSB^;
savedOCSB ← oCSB^;
END;
turnOn =>
BEGIN
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.
DoradoInputOutput.ResetEther[];
IF GetEthernet1Address[device].host#originalHost THEN
ErrorHalt[MPCodes.ethernetHostChanged]; -- InLoaded on another machine?
iCSB^ ← [
next: noIOCB, interruptBit: 0, host: savedICSB.host, hosts: savedICSB.hosts,
Ugh, it would be nice if we could do something better
missed: 0, last: NIL];
oCSB^ ← [
next: noIOCB, interruptBit: 0,
minPacketSpacing: savedOCSB.minPacketSpacing, last: NIL];
DoradoInputOutput.Output[enableInput, control];
END;
ENDCASE;
ENDLOOP;
END;
RemoveCleanup: PUBLIC PROCEDURE [device: Device] = BEGIN END;
ErrorHalt: PROC [code: MPCodes.Code] = INLINE { ProcessorFace.SetMP[code]; DO ENDLOOP };
Move this to PilotMP.mesa next time we get a chance to change it:
ethernetHostChanged: MPCodes.Code = 933;
Other routines to keep the rest of the world happy
END. -- EthernetOneHeadDorado.
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.
Time: December 5, 1980 9:50 PM By: Taft, Action: convert for Dorado.
8-Jul-82 14:19:21 Taft Cleanup routine checks for Ethernet address changing at turnOn time.
Time: September 17, 1982 1:08 pm By: Swinehart, Action: add multicast input
Requires PilotEther microcode that knows about multicast as well.