-- EthernetOneHeadDorado.mesa
-- Last Edited by: Taft, February 13, 1983 1:20 pm
-- Some modifications should be made to clean up multicast receiving, but they would cause
-- incompatibilities in existing volumes (Germ, Othello, CoPilot, etc.)
DIRECTORY
DeviceCleanup USING [Item, Await],
DoradoInputOutput USING [Input, IOAddress, Output, ResetEther],
Environment USING [Byte],
EthernetOneFace,
EthernetOneFaceExtras USING [ HostArray ],
HeadStartChain USING [Start],
Inline USING [BITAND, HighByte, LowHalf],
PilotMP USING [Code],
ProcessorFace USING [SetMP];
EthernetOneHeadDorado: PROGRAM
IMPORTS DeviceCleanup, DoradoInputOutput, RemainingStartChain: HeadStartChain, Inline,
ProcessorFace
EXPORTS EthernetOneFace, EthernetOneFaceExtras, HeadStartChain =
BEGIN OPEN EthernetOneFace, EthernetOneFaceExtras;
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 WORD←NULL, -- 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[Inline.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 CARDINAL ← SIZE[IOControlBlock];
hearSelf: PUBLIC BOOLEAN ← TRUE;
-- 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 Inline.BITAND[cb.completion, 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 Inline.BITAND[cb.completion, 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, host: Environment.Byte] =
BEGIN
RETURN[net: 0, host: Inline.HighByte[DoradoInputOutput.Input[control]]];
END;
TurnOn: PUBLIC PROCEDURE [
device: Device, host: Environment.Byte, inInterrupt, outInterrupt: WORD,
globalState: GlobalStatePtr] =
BEGIN
DoradoInputOutput.ResetEther[];
iCSB.next ← noIOCB;
iCSB.interruptBit←inInterrupt;
iCSB.missed←0;
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: Environment.Byte] = {
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: BOOLEAN ← FALSE;
savedICSB: InputControllerStatusBlock;
savedOCSB: OutputControllerStatusBlock;
AddCleanup: PUBLIC PROCEDURE [device: Device] =
BEGIN OPEN DeviceCleanup;
item: Item;
originalHost: Environment.Byte ← 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[cHardwareConfigChanged]; -- perhaps 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: PilotMP.Code] = INLINE { ProcessorFace.SetMP[code]; DO ENDLOOP };
-- Move this to PilotMP.mesa next time we get a chance to change it:
cHardwareConfigChanged: PilotMP.Code = 933;
-- Other routines to keep the rest of the world happy
Start: PUBLIC PROCEDURE =
BEGIN -- Start this module (and rest of chain)
RemainingStartChain.Start[]
END;
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.