EthernetHeadDLion.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Sandman on: 18-Mar-81 17:20:56
Blyon on : 22-Apr-81 17:43:34
Russ Atkinson (RRA) February 19, 1985 3:33:17 pm PST
DIRECTORY
Basics USING [LongNumber],
PrincOps USING [wordsPerPage],
PrincOpsUtils USING [BITAND, LowHalf],
DeviceCleanup USING [Item, Await],
DLionInputOutput USING [IOPage, Input, Output],
EthernetFace;
EthernetHeadDLion: PROGRAM
IMPORTS DLionInputOutput, DeviceCleanup, PrincOpsUtils
EXPORTS EthernetFace =
BEGIN OPEN EthernetFace;
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: physical 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
EthernetCSBOffset: CARDINAL = 12*16 + 0; -- 0C0X
IOCB: TYPE = LONG POINTER TO IOControlBlock;
Base: TYPE = LONG BASE POINTER;
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: QuadWord Alignment
Input: PROC [Register] RETURNS [Data] = LOOPHOLE[DLionInputOutput.Input];
Output: PROC [Command, Register] = LOOPHOLE[DLionInputOutput.Output];
Data: TYPE = RECORD [WORD];
Command: TYPE = RECORD [WORD];
eOff: Command = [0B];
eEnableTrn: Command = [1B];
eEnableRcv: Command = [1B];
eTurnOff: Command = [2B];
Register: TYPE = RECORD [[0..17B]];
eEOCtl: Register = [14B];
eEICtl: Register = [5B];
eEStatus: Register = [1B];
eHaveEther: Register = [12B];
completion bits
collision: WORD = 100B;
underRun: WORD = 40B;
notGoodAlign: WORD = 20B;
overRun: WORD = 10B;
notGoodCRC: WORD = 4B;
notEvenLen: WORD = 2B;
other status bits
eRcvEnabled: WORD = 2000B;
eTrnEnabled: WORD = 400B;
noIocb: ShortIocb = LOOPHOLE[0];
Shorten: PROC [iocb: IOCB] RETURNS [ShortIocb] = INLINE {
Maybe we should check to be sure that the high half is zero
RETURN[LOOPHOLE[PrincOpsUtils.LowHalf[iocb]]]};
FixAlignment: PROC [iocb: IOCB] RETURNS [IOCB] = INLINE {
LOOPHOLE[iocb, Basics.LongNumber].lowbits ← PrincOpsUtils.BITAND[
LOOPHOLE[iocb, Basics.LongNumber].lowbits + 7, 177770B];
RETURN[iocb]};
MakeSureOff: PROC = INLINE {
Output[eTurnOff, eEICtl];
THROUGH [0..2) UNTIL PrincOpsUtils.BITAND[Input[eEStatus], 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;
hearSelf: PUBLIC BOOLEANFALSE;
Non EXPORTed things. Note that all the state information lives in the CSBs.
csb: CSB ← DLionInputOutput.IOPage + EthernetCSBOffset;
haveEther: BOOLEAN;
QueueOutput: PUBLIC PROC [device: DeviceHandle, buffer: LONG POINTER, length: CARDINAL, cb: IOCB] = {
count: CARDINAL;
p: LONG POINTER ← buffer;
IF device # self THEN RETURN;
count ← length - 1;
DO
foo: UNSPECIFIED ← (p + count)^;
IF count < PrincOps.wordsPerPage THEN {foo ← p^; EXIT};
count ← count - PrincOps.wordsPerPage;
ENDLOOP;
cb ← FixAlignment[cb];
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];
Output[eEnableTrn, eEOCtl]} -- 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];
Output[eEnableTrn, eEOCtl]}}; -- poke hardware
csb.lastOutput ← cb};
QueueInput: PUBLIC PROC [
device: DeviceHandle, buffer: LONG POINTER, length: CARDINAL, cb: IOCB] = {
IF device # self THEN RETURN;
cb ← FixAlignment[cb];
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]; Output[eEnableRcv, eEICtl]};
csb.lastInput ← cb};
GetStatus: PUBLIC PROC [cb: IOCB] RETURNS [status: Status] = {
completion: WORD;
cb ← FixAlignment[cb];
IF (completion ← cb.completion) = 0 THEN RETURN[pending];
IF cb.spare = 0 THEN {
IF cb.length = 177777B THEN RETURN[packetTooLong];
status ←
SELECT PrincOpsUtils.BITAND[
completion, notGoodAlign + overRun + notGoodCRC + notEvenLen] FROM
0 => ok,
notGoodAlign+notEvenLen => ok, -- dippled bit case - someday drippled should be returned
notGoodAlign => badAlignmentButOkCrc,
notGoodCRC => crc,
notGoodCRC + notGoodAlign => crcAndBadAlignment,
overRun, overRun + notGoodAlign, overRun + notGoodCRC,
overRun + notGoodCRC + notGoodAlign => overrun,
ENDCASE => otherError;
IF status = ok THEN {
count: CARDINAL ← cb.length;
p: LONG POINTER ← cb.buffer;
DO
(p + count)^ ← (p + count)^;
IF count < PrincOps.wordsPerPage THEN {p^ ← p^; EXIT};
count ← count - PrincOps.wordsPerPage;
ENDLOOP};
RETURN[status]};
IF cb.spare = 1 THEN {
IF cb.mask = 17777B THEN RETURN[tooManyCollisions];
RETURN[
SELECT PrincOpsUtils.BITAND[completion, collision + underRun] FROM
0 => ok,
underRun, underRun + collision => underrun,
ENDCASE => otherError]};
RETURN[otherError]};
GetRetries: PUBLIC PROC [cb: IOCB] RETURNS [CARDINAL] = {
cb ← FixAlignment[cb];
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,
ENDCASE => 16]};
GetPacketLength: PUBLIC PROC [cb: IOCB] RETURNS [CARDINAL] = {
cb ← FixAlignment[cb]; RETURN[cb.used]};
GetPacketsMissed: PUBLIC PROC [device: DeviceHandle] RETURNS [CARDINAL] = {
RETURN[IF device # self THEN 0 ELSE csb.missed]};
GetNextDevice: PUBLIC PROC [device: DeviceHandle] RETURNS [DeviceHandle] = {
IF ~haveEther THEN RETURN[nullDeviceHandle];
IF device = nullDeviceHandle THEN RETURN[self] ELSE RETURN[nullDeviceHandle]};
TurnOn: PUBLIC PROC [
device: DeviceHandle, host: physical 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];
Output[eEnableRcv, eEICtl]};
TurnOff: PUBLIC PROC [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: BOOLEANFALSE;
savedCSB: ControllerStatusBlock;
AddCleanup: PUBLIC PROC [device: DeviceHandle] = {
OPEN DeviceCleanup;
item: Item;
oldHost: physical HostNumber;
IF cleanupInitialized OR ~haveEther 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];
Output[eEnableRcv, eEICtl]};
ENDCASE;
ENDLOOP};
RemoveCleanup: PUBLIC PROC [device: DeviceHandle] = {};
Other routines to keep the rest of the world happy
Start: PROC = {
IF (haveEther ← Input[eHaveEther] = Data[1]) THEN
controlBlockSize ← SIZE[IOControlBlock] + 4
ELSE controlBlockSize ← 0
};
Start[];
END.
LOG
Time: October 29, 1980 11:16 AM By: Sandman, Action: create file.
Time: 18-Mar-81 17:20:25 By: Sandman, Action: Removed minimum packet length test, general cleanup.