-- NewEthernetHeadDLion.mesa, HGM, 19-Sep-82 19:14:40
-- fix GetRetries and GetStatus for 16 retries, DLionInputOutput cleanup too,
-- NewEthernetHeadDLion.mesa, HGM, 15-Mar-82 17:47:31
-- EthernetHeadDLion.mesa (last edited by: Sandman on: 18-Mar-81 17:20:56)
-- EthernetHeadDLion.mesa (last edited by: Blyon on: 22-Apr-81 17:43:34)
-- Willie-sue, April 7, 1988 2:41:04 pm PDT
-- Last Edited by: Willie-Sue, July 2, 1984 5:09:18 pm PDT

-- Need to keep map up to date!!!

-- BEWARE:
-- This version assumes that the IOCBs are 8 word aligned.
-- It also assumes that the driver will keep the map up to date if necessary.

DIRECTORY
DeviceCleanup USING [Item, Await],
DLionInputOutput USING [IOPage, Input, Output, InputReg, OutputReg],
NSPilotSystem USING [HostNumber],
PrincOpsUtils USING [BITAND, LowHalf],
NewEthernetFace,
NewEthernetFaceExtras;

NewEthernetHeadDLion: PROGRAM
IMPORTS PrincOpsUtils, DLionInputOutput, DeviceCleanup
EXPORTS NewEthernetFace, NewEthernetFaceExtras =
BEGIN OPEN PrincOpsUtils, NewEthernetFace;
copied from [Idun]<APilot>11.0>Faces>Friends>DLionInputOutput.mesa
Unused Space
unusedSpaceOffset: CARDINAL = 111; -- 6F hex
unusedSpaceMax: CARDINAL = magTapeCSBOffset - unusedSpaceOffset;
MagTape CSB
magTapeCSBOffset: CARDINAL = 188; -- C0 hex
magTapeCSBMax: CARDINAL = ethernetCSBOffset - magTapeCSBOffset;
Ethernet CSB
ethernetCSBOffset: CARDINAL = 192; -- C0 hex
ethernetCSBMax: CARDINAL = moreUnusedSpaceOffset - ethernetCSBOffset;
More Unused Space
moreUnusedSpaceOffset: CARDINAL = 204; -- CC hex
moreUnusedSpaceMax: CARDINAL = displayCSBOffset - moreUnusedSpaceOffset;
Display CSB
displayCSBOffset: CARDINAL = 231; -- E7 hex
InputReg: TYPE = MACHINE DEPENDENT{
EIData(0), EStatus(1), KIData(2), KStatus(3), MStatus(5), KTest(6), IOPIData(8), IOPStatus(9), TStatus(10), (177777B)};
OutputReg: TYPE = MACHINE DEPENDENT{
IOPOData(0), IOPCtl(1), KOData(2), KCtl(3), EOData(4), EICtl(5), TCtlOrPCtl(9), MCtl(10), EOCtl(12), KCmd(13), TAddrOrClDPRq(14), TODataOrPOData(15), (177777B)};
end of copied stuff
-- 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.

CSB: TYPE = LONG POINTER TO ControllerStatusBlock;
ControllerStatusBlock: TYPE = MACHINE DEPENDENT RECORD [
host: NSPilotSystem.HostNumber,
input: ShortIocb,
inputWakeups: WORD,
output: ShortIocb,
outputWakeups: WORD,
missed: CARDINAL,
lastInput: IOCB, -- last IOCB on input queue, valid if input#noIocb
lastOutput: IOCB]; -- last IOCB on output queue, valid if output#noIocb

Base: TYPE = LONG BASE POINTER--Environment.Base--;

compileCheckEthernetCSBSize: BOOLEAN[TRUE..TRUE] =
-- (DLionInputOutput.ethernetCSBMax >= SIZE[ControllerStatusBlock]);
(ethernetCSBMax >= SIZE[ControllerStatusBlock]);

IOCB: TYPE = LONG POINTER TO IOControlBlock;
ShortIocb: TYPE = Base RELATIVE POINTER TO IOControlBlock;
IOControlBlock: TYPE = MACHINE DEPENDENT RECORD [
length: CARDINAL,
buffer: LONG POINTER,
mask: WORD, -- load location for output
used: CARDINAL, -- input only
completion: WORD,
next: ShortIocb,
spare: WORD]; -- NB: Can't cross page boundry

Command: TYPE = RECORD [WORD];
eEnableTrn: Command = [1B];
eEnableRcv: Command = [1B];
eTurnOff: Command = [2B];

-- completion bits
collision: WORD = 100B;
underRun: WORD = 40B;
dribble: WORD = 20B;
overRun: WORD = 10B;
notGoodCRC: WORD = 4B;
oddByte: WORD = 2B;

-- other status bits
eRcvEnabled: WORD = 2000B;
eTrnEnabled: WORD = 400B;

noIocb: ShortIocb = LOOPHOLE[0];

SetModeFor10MBEther: PUBLIC PROC[
 mode: NewEthernetFaceExtras.ModeFor10MBEther← normal]
  RETURNS[NewEthernetFaceExtras.ModeFor10MBEther] = { RETURN[mode]};

Shorten: PROCEDURE [iocb: IOCB] RETURNS [ShortIocb] = INLINE {
-- Maybe we should check to be sure that the high half is zero
RETURN[LOOPHOLE[PrincOpsUtils.LowHalf[iocb]]]};

MakeSureOff: PROC = INLINE {
DLionInputOutput.Output[eTurnOff, EICtl];
THROUGH [0..2) UNTIL BITAND[DLionInputOutput.Input[EStatus], eRcvEnabled + eTrnEnabled] = 0 DO
ENDLOOP};

-- EXPORTed TYPEs

DeviceHandle: PUBLIC TYPE = RECORD [CARDINAL];
ControlBlockRecord: PUBLIC TYPE = IOControlBlock;

-- EXPORTed variables

nullDeviceHandle: PUBLIC DeviceHandle ← [123456B];
self: DeviceHandle = [0];
globalStateSize: PUBLIC CARDINAL ← 0;
controlBlockSize: PUBLIC CARDINAL ← SIZE[IOControlBlock];
hearSelf: PUBLIC BOOLEAN ← FALSE;


-- Non EXPORTed things. Note that all the state information lives in the CSBs.
-- csb: CSB ← DLionInputOutput.IOPage + DLionInputOutput.ethernetCSBOffset;
csb: CSB ← DLionInputOutput.IOPage + ethernetCSBOffset;


QueueOutput: PUBLIC PROCEDURE [
device: DeviceHandle, buffer: LONG POINTER, length: CARDINAL, cb: IOCB] = {
p: LONG POINTER ← buffer;
IF device # self THEN RETURN;
cb^ ← [
next: noIocb, mask: 0, spare: 1, completion: 0, used: 0, length: length - 1,
buffer: buffer];
IF csb.output = noIocb THEN {
-- new iocb, hardware idle
csb.output ← Shorten[cb]; DLionInputOutput.Output[eEnableTrn, EOCtl]} -- poke hardware
ELSE {
-- output active, add to end of chain
csb.lastOutput.next ← Shorten[cb];
IF csb.output = noIocb AND cb.completion = 0 THEN {
-- oops, hardware went idle
csb.output ← Shorten[cb]; DLionInputOutput.Output[eEnableTrn, EOCtl]}}; -- poke hardware
csb.lastOutput ← cb};

QueueInput: PUBLIC PROCEDURE [
device: DeviceHandle, buffer: LONG POINTER, length: CARDINAL, cb: IOCB] = {
IF device # self THEN RETURN;
cb^ ← [
next: noIocb, mask: 0, spare: 0, completion: 0, used: 0, length: length,
buffer: buffer];
IF csb.input # noIocb THEN csb.lastInput.next ← Shorten[cb];
IF csb.input = noIocb AND cb.completion = 0 THEN {
csb.input ← Shorten[cb]; DLionInputOutput.Output[eEnableRcv, EICtl]};
csb.lastInput ← cb};

GetStatus: PUBLIC PROCEDURE [cb: IOCB] RETURNS [status: Status] = {
completion: WORD;
IF (completion ← cb.completion) = 0 THEN RETURN[pending];
IF cb.spare = 0 THEN {
IF cb.length = 177777B THEN RETURN[packetTooLong];
status ←
SELECT BITAND[completion, dribble + overRun + notGoodCRC + oddByte] FROM
0 => ok,
dribble, oddByte + dribble => badAlignmentButOkCrc,
notGoodCRC => crc,
notGoodCRC + dribble, notGoodCRC + oddByte + dribble,
notGoodCRC + oddByte => crcAndBadAlignment,
overRun, overRun + dribble, overRun + notGoodCRC,
overRun + notGoodCRC + dribble => overrun,
ENDCASE => otherError;
RETURN[status]};
IF cb.spare = 1 THEN {
IF cb.mask = 0 THEN RETURN[tooManyCollisions];
RETURN[
SELECT BITAND[completion, collision + underRun] FROM
0 => ok,
underRun, underRun + collision => underrun,
ENDCASE => otherError]};
RETURN[otherError]};

GetRetries: PUBLIC PROCEDURE [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 PROCEDURE [cb: IOCB] RETURNS [used: CARDINAL] =
BEGIN
used ← cb.used;
IF BITAND[cb.completion, dribble + oddByte] = dribble THEN
used ← used - 1;
END;

GetPacketsMissed: PUBLIC PROCEDURE [device: DeviceHandle] RETURNS [CARDINAL] = {
RETURN[IF device # self THEN 0 ELSE csb.missed]};

GetNextDevice: PUBLIC PROCEDURE [device: DeviceHandle] RETURNS [DeviceHandle] =
{
IF device = nullDeviceHandle THEN RETURN[self] ELSE RETURN[nullDeviceHandle]};

TurnOn: PUBLIC PROCEDURE [
device: DeviceHandle, host: NSPilotSystem.HostNumber,
inInterrupt, outInterrupt: WORD, globalState: GlobalStatePtr] = {
IF device # self THEN RETURN;
MakeSureOff[];
csb^ ← [
host: host, input: noIocb, inputWakeups: inInterrupt, output: noIocb,
outputWakeups: outInterrupt, missed: 0, lastInput: NIL, lastOutput: NIL];
DLionInputOutput.Output[eEnableRcv, EICtl]};

TurnOff: PUBLIC PROCEDURE [device: DeviceHandle] = {
IF device # self THEN RETURN; MakeSureOff[]};

-- 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 PROCEDURE [device: DeviceHandle] = {
OPEN DeviceCleanup;
item: Item;
oldHost: NSPilotSystem.HostNumber;
IF cleanupInitialized THEN RETURN;
cleanupInitialized ← TRUE;
DO
SELECT Await[@item] FROM
kill => MakeSureOff[];
turnOff => {MakeSureOff[]; savedCSB ← csb^; oldHost ← csb.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.
MakeSureOff[];
csb^ ← [
host: oldHost, -- Ugh, it would be nice if we could do something better
input: noIocb, inputWakeups: 0, output: noIocb, outputWakeups: 0,
missed: 0, lastInput: NIL, lastOutput: NIL];
DLionInputOutput.Output[eEnableRcv, EICtl]};
ENDCASE;
ENDLOOP};

RemoveCleanup: PUBLIC PROCEDURE [device: DeviceHandle] = {};


END. -- EthernetHeadDLion.