-- 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.