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, 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, last: IOCB, -- last IOCB on input queue, valid if next#noIOCB interstices: ARRAY [6..30B) OF WORD_NULL, -- hosts start 30B beyond next 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, 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]; Device: TYPE = RECORD [WORD]; Shorten: PROCEDURE [iocb: IOCB] RETURNS [ShortIOCB] = INLINE BEGIN RETURN[PrincOpsUtils.LowHalf[iocb]]; END; DeviceHandle: PUBLIC TYPE = Device; ControlBlockRecord: PUBLIC TYPE = IOControlBlock; nullDeviceHandle: PUBLIC DeviceHandle _ LOOPHOLE[123456B]; globalStateSize: PUBLIC CARDINAL _ 0; controlBlockSize: PUBLIC CARDINAL _ SIZE[IOControlBlock]; hearSelf: PUBLIC BOOLEAN _ TRUE; 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_0; iCSB.last_NIL; 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, minPacketSpacing: 500/32]; DoradoInputOutput.Output[enableInput, control]; END; TurnOff: PUBLIC PROCEDURE [device: Device] = BEGIN DoradoInputOutput.ResetEther[]; END; 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]; }; alreadyInitializedCleanup: BOOLEAN _ FALSE; 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 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, 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 }; ethernetHostChanged: MPCodes.Code = 933; 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. XEthernetOneHeadDorado.mesa Copyright c 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.) words after here are unused by microcode words after here (except "hosts") are unused by microcode for compatibility with existing location of oCSB. words after here are unused by microcode This implementation has no concept of a "device", but something has to be exported to EthernetOneFace (see below) Maybe we should check to be sure that the high half is zero EXPORTed TYPEs EXPORTed variables Non EXPORTed things. Note that all the state information lives in the CSBs. If in multiHost mode, set all hosts true for promiscuous mode, or add specified host to currently-accepted list. for now, always space output packets at least 500 microseconds apart 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. There is no way to remove a cleanup procedure yet, so we have a flag to avoid duplicates. 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. Ugh, it would be nice if we could do something better Move this to PilotMP.mesa next time we get a chance to change it: Other routines to keep the rest of the world happy Κ ^˜codešœ™Kšœ Οmœ1™K˜Kšœžœ˜Kšœžœ˜Kšœ(™(KšœžœΟc2˜?K˜—Kš žœžœžœžœžœ˜8š œžœžœž œžœ˜=K˜Kšœžœ˜KšœžœŸ@˜MKšœžœ˜Kšœ9™9KšœžœŸ1˜=š œ žœ žœžœžœŸ˜HKšœ1™1—KšœŸ7˜JK˜—Kš žœžœžœžœžœ˜,Kšœ žœžœžœ˜,š œžœžœž œžœ˜1K˜K˜KšœžœŸ ˜KšœžœŸ˜Kšœžœ˜Kšœžœžœ˜Kšœ(™(K˜Kšœžœ˜K˜—š œžœžœž œžœ˜3K˜Sšœžœžœž˜+˜ K˜K˜K˜K˜K˜K˜Kšœžœ˜!—˜ K˜K˜K˜K˜K˜K˜K˜Kšœžœ˜—Kšžœ˜ K˜——Kšœ žœ ˜K˜˜*K˜š œžœ žœžœ žœ˜PKšœ žœ žœžœ˜3——˜,K˜š œžœžœ žœžœ˜NKš œ žœžœžœ žœ˜H——Kšœ#žœ˜/K˜Kšœ žœžœžœ˜K˜!K˜"K˜+K˜Kšœžœ˜ K˜KšœF™FKšœ*™*Kšœžœžœžœ˜K˜š Οnœž œžœžœž˜šžœžœžœž˜K˜!K˜K˜Kšžœ˜——Kšžœ˜—K˜ K˜"Kšžœ˜——Kšžœ˜K˜—š   œžœž œžœžœžœ˜žœ˜CK˜Kšž˜K˜K˜K˜K˜Kšœ žœ˜KšœO™OKšœ ™ Kšžœžœ˜+Kš žœžœ žœ žœžœ˜*Kšžœžœ˜šœ9žœ˜=KšœD™DK˜—K˜/Kšžœ˜K˜—š œžœž œ˜,Kšžœ!žœ˜*K˜—KšœQ™QKšœ™KšœU™UKšœ;™;š  œžœž œ˜3Kšœ žœžœžœ˜*K˜KšœŸ˜7K˜—š  œžœž œ-˜HKšœŸ.˜EK˜—š œžœž œ˜=Kšžœ žœžœ˜9Kšžœ žœ0˜DK˜——KšœY™Y˜Kšœžœžœ˜+K˜K˜&K˜'K˜š  œžœž œ˜/Kšžœžœ˜K˜ K˜=Kšžœžœžœ˜)Kšœžœ˜!šž˜šžœž˜Kšœžœ!žœ˜2˜ Kšž˜K˜K˜K˜Kšžœ˜—˜ Kšž˜Kšœ8™8KšœK™KKšœK™KKšœJ™JKšœD™DK˜šžœ/ž˜5Kšœ)Ÿ˜H—˜ K˜MKšœ5™5Kšœžœ˜—˜ K˜Kšœ4žœ˜9—K˜/Kšžœ˜—Kšžœ˜—Kšžœ˜—Kšžœ˜K˜—Kš   œžœž œžœžœ˜=K˜Kš   œžœžœžœžœ˜XK˜KšœA™AK˜(K˜Kšœ2™2K˜KšžœŸ˜K˜—Kšž˜Kšœžœk˜‰Kšœžœžœ1˜oKšœžœžœ,˜UKšœžœžœ!˜Jšœžœ'˜FK˜^—˜KK˜AK˜K˜——…—"€56