-- File: EthernetOneDriver.mesa - last edit: -- AOF 17-Feb-88 18:57:53 -- HGM 5-Oct-85 13:42:20 -- Copyright (C) 1983, 1984, 1985, 1988 by Xerox Corporation. All rights reserved. -- NB: Allocates 10 buffers for Dicentras DIRECTORY Buffer USING [ AccessHandle, Buffer, DataBytesPerRawBuffer, dataLinkReserve, DestroyPool, MakePool, Type], CommFlags USING [doDebug, doStats, driverStats], CommHeap USING [zone], CommunicationInternal USING [NSPackageDestroy, NSPackageMake], CommUtil USING [AllocateIocbs, FreeIocbs], Driver USING [ Glitch, GetDeviceChain, GetInputBuffer, Device, DeviceObject, AddDeviceToChain, PutOnGlobalDoneQueue, PutOnGlobalInputQueue, ReturnFreeBuffer], EthernetOneDriverTypes USING [Byte, Encapsulation], Environment USING [Byte, bytesPerWord], EthernetOneFace, EthernetDriverFriends USING [EtherStatsInfo], Inline USING [LowHalf, HighHalf, BITAND, LongCOPY], PilotSwitches USING [noEthernetOne], Process USING [ Abort, Detach, DisableTimeout, EnableAborts, GetPriority, Priority, SecondsToTicks, SetPriority, SetTimeout, Yield], ProcessPriorities USING [priorityIOHigh], SpecialRuntime USING [AllocateNakedCondition, DeallocateNakedCondition], Protocol1 USING [AddFamilyMember, Family, MatrixRecord], NS3MBit USING [SetFaceStatus], NSBuffer USING [Body], PupDefs USING [Body, GetPupPackageUseCount, PupPackageDestroy, PupPackageMake], PupRouterDefs USING [ContextObject], PupTypes USING [allHosts, PupHostID], RoutingTable USING [ContextObject], Runtime USING [GlobalFrame, SelfDestruct], Stats USING [StatBump, StatIncr, StatCounterIndex], SpecialCommunication USING [], SpecialSystem USING [HostNumber, GetProcessorID, NetworkNumber], System USING [ broadcastHostNumber, GetClockPulses, HostNumber, MicrosecondsToPulses, nullHostNumber, nullNetworkNumber, Pulses, switches--['<]--]; EthernetOneDriver: MONITOR IMPORTS Buffer, CommHeap, CommunicationInternal, CommUtil, Driver, Stats, EthernetOneFace, Inline, NS3MBit, Process, Protocol1, PupDefs, Runtime, SpecialRuntime, SpecialSystem, System EXPORTS Buffer, NS3MBit, SpecialCommunication, System = BEGIN OPEN EthernetOneFace; --EXPORTed TYPEs Device: PUBLIC TYPE = Driver.Device; HostNumber: PUBLIC TYPE = SpecialSystem.HostNumber; NetworkNumber: PUBLIC TYPE = SpecialSystem.NetworkNumber; ether: DeviceHandle; myEar: CARDINAL; -- address am I actually listening for getGarbage: BOOLEAN ¬ FALSE; --when true, we deliver any packet setupEthernetOneDriver: PROC[etherDevice: DeviceHandle]; ethernetOneListenForHost: PROC[newHostNumber: CARDINAL]; ns: RoutingTable.ContextObject ¬ [ netNumber: System.nullNetworkNumber, network: @myDevice, stats: NIL]; pup: PupRouterDefs.ContextObject ¬ [protocol: NIL, network: @myDevice, pupNetNumber: 0, pupHostNumber: 377B]; inputState: RECORD[ process: PROCESS, mask: WORD, access: Buffer.AccessHandle, inWait: LONG POINTER TO CONDITION, firstBuffer, lastBuffer: Buffer.Buffer, queueAllowed, queueLength: CARDINAL, timeLastRecv: LONG CARDINAL, lastMissed: CARDINAL]; outputState: RECORD[ process: PROCESS, mask: WORD, outWait: LONG POINTER TO CONDITION, firstBuffer, lastBuffer: Buffer.Buffer, timeSendDone: LONG CARDINAL]; watcherState: RECORD[ pleaseStop: BOOLEAN, process: PROCESS, timer: CONDITION]; --IOCBs iocbState: RECORD[wait: CONDITION, first, free: FreeIocb]; FreeIocb: TYPE = LONG POINTER TO IocbObject; IocbObject: TYPE = RECORD[ next: FreeIocb, rest: SEQUENCE COMPUTED CARDINAL OF WORD]; seconds: RECORD[one, fiveHalf, forty: LONG CARDINAL]; myDevice: Driver.DeviceObject ¬ [ matrix: DESCRIPTOR[NIL, 0], sendRawBuffer: SendRawBuffer, activateDriver: ActivateDriver, deactivateDriver: DeactivateDriver, deleteDriver: DeleteDriver, buffers:, device: ethernetOne, receiveBufferLen: 1500, changeNumberOfInputBuffers: MaybeChangeNumberOfInputBuffers, alive: TRUE, index: , next: NIL, lineSpeed: 3000, lineNumber: 0, stats: NIL]; LostAllIocbs: PUBLIC ERROR = CODE; IOCBSizeIsZero: PUBLIC ERROR = CODE; DriverNotActive: PUBLIC ERROR = CODE; DriverAlreadyActive: PUBLIC ERROR = CODE; IOCBMustBeInFirstMDS: PUBLIC ERROR = CODE; IOCBMustBeQuadWordAligned: PUBLIC ERROR = CODE; EthernetNetNumberScrambled: PUBLIC ERROR = CODE; BufferMustBeAlmostQuadWordAligned: PUBLIC ERROR = CODE; etherStats: EthernetDriverFriends.EtherStatsInfo; bpw: NATURAL = Environment.bytesPerWord; << The physical overhead is the number of bytes used by the MAC layer to encapsulate a Network Protocol Data Unit. This physical overhead includes EthernetOne encapsulation. >> physOverhead: NATURAL = bpw * (SIZE[EthernetOneDriverTypes.Encapsulation] + SIZE[WORD--EthernetOneDriverTypes.EthernetOneCRC--]); encapBytes: CARDINAL = bpw * SIZE[EthernetOneDriverTypes.Encapsulation]; << This driver starts the reception of physical frame offset into the space reserved by the buffer manager for the LLC. This is so frames received using one LLC (eg. EthernetOne) can be encapsulated using another LLC (eg. IEEE 802.2) without wiping out the FixedOverhead portion of the buffer. errorCheck: NATURAL[22..1518] = Buffer.dataLinkReserve; -- this must be true >> offsetToDataUnit: NATURAL = Buffer.dataLinkReserve / bpw - SIZE[EthernetOneDriverTypes.Encapsulation]; --words --Hot Procedures GetBufferAndIocb: INTERNAL PROC RETURNS[b: Buffer.Buffer] = BEGIN receiveLength: NATURAL = myDevice.receiveBufferLen + physOverhead; SELECT TRUE FROM (iocbState.free = NIL) => {IF CommFlags.doStats THEN Stats.StatIncr[statsIocbWait]; RETURN[NIL]}; ((b ¬ Driver.GetInputBuffer[FALSE, receiveLength]) = NIL) => RETURN; ENDCASE; b.fo.driver.iocb ¬ iocbState.free; iocbState.free ¬ iocbState.free.next; b.fo.driver.length ¬ receiveLength; b.fo.driver.faceStatus ¬ ethernet[pending]; END; --GetBufferAndIocb FreeBufferAndIocb: INTERNAL PROC[ queueProc: PROC[b: Buffer.Buffer], b: Buffer.Buffer] = BEGIN iocb: FreeIocb ¬ b.fo.driver.iocb; IF iocb # NIL THEN BEGIN iocb.next ¬ iocbState.free; iocbState.free ¬ iocb; b.fo.driver.iocb ¬ NIL; END; queueProc[b]; END; --FreeBufferAndIocb InInterrupt: ENTRY PROC = BEGIN status: Status; acceptBuffer: BOOLEAN; this, new: Buffer.Buffer; DO ENABLE ABORTED => EXIT; UNTIL (this ¬ inputState.firstBuffer) # NIL DO WAIT inputState.inWait; ENDLOOP; status ¬ GetStatus[this.fo.driver.iocb]; IF CommFlags.doStats AND status # pending THEN Stats.StatIncr[statEtherInterruptDuringInterrupt]; UNTIL status # pending DO WAIT inputState.inWait; status ¬ GetStatus[this.fo.driver.iocb]; IF CommFlags.doStats AND status = pending THEN Stats.StatIncr[statEtherMissingStatus]; ENDLOOP; inputState.firstBuffer ¬ inputState.firstBuffer.fo.next; IF status = ok THEN acceptBuffer ¬ TRUE ELSE BEGIN acceptBuffer ¬ getGarbage; --we may be collecting garbage packets IF CommFlags.driverStats THEN etherStats.badRecvStatus ¬ etherStats.badRecvStatus + 1; IF CommFlags.doStats THEN Stats.StatIncr[statEtherReceivedBadStatus]; IF CommFlags.doStats OR CommFlags.driverStats THEN SELECT status FROM packetTooLong => BEGIN IF CommFlags.driverStats THEN etherStats.packetTooLong ¬ etherStats.packetTooLong + 1; IF CommFlags.doStats THEN Stats.StatIncr[statEtherReceivedTooLong]; END; badAlignmentButOkCrc => BEGIN IF CommFlags.driverStats THEN etherStats.badAlignmentButOkCrc ¬ etherStats.badAlignmentButOkCrc + 1; IF CommFlags.doStats THEN Stats.StatIncr[statEtherReceivedNot16]; END; crc => BEGIN IF CommFlags.driverStats THEN etherStats.badCrc ¬ etherStats.badCrc + 1; IF CommFlags.doStats THEN Stats.StatIncr[statEtherReceivedBadCRC]; END; crcAndBadAlignment => BEGIN IF CommFlags.driverStats THEN etherStats.crcAndBadAlignment ¬ etherStats.crcAndBadAlignment+1; IF CommFlags.doStats THEN Stats.StatIncr[statEtherReceivedNot16BadCRC]; END; overrun => BEGIN IF CommFlags.driverStats THEN etherStats.overrun ¬ etherStats.overrun + 1; IF CommFlags.doStats THEN Stats.StatIncr[statEtherReceivedOverrun]; END; ENDCASE; END; IF ~acceptBuffer THEN {new ¬ this; new.fo.next ¬ NIL} --recycle this buffer ELSE BEGIN inputState.timeLastRecv ¬ this.fo.time ¬ System.GetClockPulses[]; this.fo.driver.length ¬ GetPacketLength[this.fo.driver.iocb]; NS3MBit.SetFaceStatus[this, status]; this.fo.network ¬ LONG[@myDevice]; IF CommFlags.driverStats THEN BEGIN etherStats.packetsRecv ¬ etherStats.packetsRecv + 1; etherStats.wordsRecv ¬ etherStats.wordsRecv + this.fo.driver.length; END; IF CommFlags.doStats THEN BEGIN Stats.StatIncr[statEtherPacketsReceived]; Stats.StatBump[statEtherWordsReceived, this.fo.driver.length]; END; FreeBufferAndIocb[Driver.PutOnGlobalInputQueue, this]; SELECT TRUE FROM (inputState.queueLength > inputState.queueAllowed) => BEGIN new ¬ NIL; inputState.queueLength ¬ inputState.queueLength - 1; NOTIFY watcherState.timer; IF CommFlags.doStats THEN Stats.StatIncr[statEtherEmptyFreeQueue]; END; ((new ¬ GetBufferAndIocb[]) = NIL) => BEGIN --Rats, couldn't or didn't want a new buffer inputState.queueLength ¬ inputState.queueLength - 1; NOTIFY watcherState.timer; IF CommFlags.doStats THEN Stats.StatIncr[statEtherEmptyFreeQueue]; END; ENDCASE; END; --acceptBuffer clause --add new buffer to end of input chain IF new # NIL THEN BEGIN NS3MBit.SetFaceStatus[new, pending]; QueueInput[ ether, (new.linkLayer.blockPointer + offsetToDataUnit), (new.fo.driver.length / bpw) - offsetToDataUnit, new.fo.driver.iocb]; IF inputState.firstBuffer = NIL THEN inputState.firstBuffer ¬ new ELSE inputState.lastBuffer.fo.next ¬ new; inputState.lastBuffer ¬ new; END; ENDLOOP; inputState.process ¬ NIL; END; OutInterrupt: ENTRY PROC = BEGIN b: Buffer.Buffer; status: Status; DO ENABLE ABORTED => EXIT; DO --we compute the values each time around since the value of b can --change if the watcher shoots down the output. SELECT TRUE FROM ((b ¬ outputState.firstBuffer) = NIL) => NULL; ((status ¬ GetStatus[b.fo.driver.iocb]) # pending) => EXIT; ENDCASE; WAIT outputState.outWait; ENDLOOP; outputState.timeSendDone ¬ System.GetClockPulses[]; --still transmiting NS3MBit.SetFaceStatus[b, status]; IF status = ok THEN BEGIN IF CommFlags.doStats OR CommFlags.driverStats THEN BEGIN tries: CARDINAL ¬ GetRetries[b.fo.driver.iocb]; statEtherSendsCollision1: Stats.StatCounterIndex = statEtherSendsCollision1; first: CARDINAL = LOOPHOLE[statEtherSendsCollision1]; IF CommFlags.driverStats THEN BEGIN etherStats.packetsSent ¬ etherStats.packetsSent + 1; etherStats.wordsSent ¬ etherStats.wordsSent + b.fo.driver.length; etherStats.loadTable[tries] ¬ etherStats.loadTable[tries] + 1; END; IF CommFlags.doStats AND tries # 0 THEN Stats.StatIncr[LOOPHOLE[first + tries]]; END; END ELSE BEGIN IF CommFlags.driverStats THEN etherStats.badSendStatus ¬ etherStats.badSendStatus + 1; IF CommFlags.doStats THEN Stats.StatIncr[statEtherSendBadStatus]; IF CommFlags.doStats OR CommFlags.driverStats THEN SELECT status FROM tooManyCollisions => BEGIN IF CommFlags.driverStats THEN etherStats.tooManyCollisions ¬ etherStats.tooManyCollisions + 1; IF CommFlags.doStats THEN Stats.StatIncr[statEtherSendsCollisionLoadOverflow]; END; underrun => BEGIN IF CommFlags.driverStats THEN etherStats.underrun ¬ etherStats.underrun + 1; IF CommFlags.doStats THEN Stats.StatIncr[statEtherSendOverrun]; END; ENDCASE; END; --We don't resend things that screwup outputState.firstBuffer ¬ outputState.firstBuffer.fo.next; FreeBufferAndIocb[Driver.PutOnGlobalDoneQueue, b]; ENDLOOP; outputState.process ¬ NIL; END; Watcher: PROC = BEGIN CheckForIdleInput: ENTRY PROC = INLINE BEGIN IF (System.GetClockPulses[] - inputState.timeLastRecv) < seconds.forty THEN RETURN; IF CommFlags.doStats THEN Stats.StatIncr[statInputIdle]; IF CommFlags.driverStats THEN etherStats.idleInput ¬ etherStats.idleInput + 1; SmashCSBs[]; --this will leave output dangling END; --CheckForIdleInput CheckForStuckOutput: ENTRY PROC = INLINE BEGIN SELECT TRUE FROM outputState.firstBuffer = NIL => NULL; ((System.GetClockPulses[] - outputState.timeSendDone) < seconds.fiveHalf) => RETURN; ENDCASE => BEGIN --This happens if the transciever is unplugged TurnOff[ether]; UNTIL outputState.firstBuffer = NIL DO b ¬ outputState.firstBuffer; outputState.firstBuffer ¬ outputState.firstBuffer.fo.next; FreeBufferAndIocb[Driver.PutOnGlobalDoneQueue, b]; IF CommFlags.doStats THEN Stats.StatIncr[statPacketsStuckInOutput]; IF CommFlags.driverStats THEN etherStats.stuckOutput ¬ etherStats.stuckOutput + 1; ENDLOOP; SmashCSBs[]; END; outputState.timeSendDone ¬ System.GetClockPulses[]; END; --CheckForStuckOutput QueueInputBufferLocked: ENTRY PROC = INLINE BEGIN ENABLE UNWIND => NULL; SELECT TRUE FROM (b = NIL) => WAIT watcherState.timer; --this is an alternate WAIT ((b.fo.driver.iocb ¬ iocbState.free) # NIL) => BEGIN iocbState.free ¬ iocbState.free.next; NS3MBit.SetFaceStatus[b, pending]; QueueInput[ ether, (b.linkLayer.blockPointer + offsetToDataUnit), (b.fo.driver.length / bpw) - offsetToDataUnit, b.fo.driver.iocb]; IF inputState.firstBuffer = NIL THEN inputState.firstBuffer ¬ b ELSE inputState.lastBuffer.fo.next ¬ b; inputState.lastBuffer ¬ b; inputState.queueLength ¬ inputState.queueLength + 1; END; ENDCASE => --the ENDCASE better be rare! BEGIN IF CommFlags.doStats THEN Stats.StatIncr[statsIocbWait]; Driver.ReturnFreeBuffer[b]; --give the buffer back (ARGH!!) WAIT watcherState.timer; --this is an alternate WAIT END; END; --QueueInputBufferLocked WaitForTimer: ENTRY PROC = INLINE BEGIN ENABLE UNWIND => NULL; WAIT watcherState.timer; END; b: Buffer.Buffer; enterLoop: LONG CARDINAL; inputState.lastMissed ¬ GetPacketsMissed[ether]; DO ENABLE ABORTED => EXIT; enterLoop ¬ System.GetClockPulses[]; --Check for lost interrupts IF CheckBuffer[@inputState.firstBuffer] OR CheckBuffer[@outputState.firstBuffer] THEN WatchCarefully[]; CheckForIdleInput[]; CheckForStuckOutput[]; WHILE (inputState.queueLength < inputState.queueAllowed) DO --don't lock monitor and wait for a buffer b ¬ Driver.GetInputBuffer[ TRUE, myDevice.receiveBufferLen + physOverhead]; QueueInputBufferLocked[]; --give it a chance to wait IF b = NIL THEN EXIT; --but don't futz around forever ENDLOOP; IF CommFlags.doStats OR CommFlags.driverStats THEN BEGIN missed: CARDINAL ¬ GetPacketsMissed[ether]; lost: CARDINAL ¬ missed - inputState.lastMissed; IF lost # 0 THEN BEGIN IF CommFlags.doStats THEN Stats.StatBump[statEtherEmptyNoBuffer, lost]; IF CommFlags.driverStats THEN etherStats.packetsMissed ¬ etherStats.packetsMissed + lost; inputState.lastMissed ¬ missed; END; END; IF (System.GetClockPulses[] - enterLoop) < seconds.one THEN WaitForTimer[]; ENDLOOP; END; --Watcher CheckBuffer: ENTRY PROC[p: POINTER TO Buffer.Buffer] RETURNS [trouble: BOOLEAN] = BEGIN b: Buffer.Buffer ¬ p­; IF b = NIL THEN RETURN[FALSE]; RETURN[(GetStatus[b.fo.driver.iocb] # pending)]; END; WatchCarefully: PROC = BEGIN << In status # pending, an interrupt should have happened. Since the interrupt routine is higher priority than we are, it should get processed before we can see it. If we get here, an interrupt has probably been lost. It could have been generated between the time we started decoding the instruction and the time that the data is actually fetched. That is why we look several times. Of course, if it is still not zero when we look again, it could be a new interrupt that has just arrived. Check for lost input interrupt. >> THROUGH [0..25) DO IF ~CheckBuffer[@inputState.firstBuffer] THEN EXIT; REPEAT FINISHED => WatcherNotify[]; ENDLOOP; --Check for lost output interrupt THROUGH [0..25) DO IF ~CheckBuffer[@outputState.firstBuffer] THEN EXIT; REPEAT FINISHED => WatcherNotify[]; ENDLOOP; END; WatcherNotify: ENTRY PROC = BEGIN IF CommFlags.doStats THEN Stats.StatIncr[statEtherLostInterrupts]; SmashCSBs[]; --this will leave output dangling END; DecapsulateNS: PROC [b: Buffer.Buffer] RETURNS [type: Buffer.Type] = BEGIN dll: LONG POINTER TO EthernetOneDriverTypes.Encapsulation; bytes: CARDINAL ¬ b.fo.driver.length; IF bytes < encapBytes THEN GOTO Rejected; dll ¬ LOOPHOLE[b.linkLayer.blockPointer]; bytes ¬ bytes - encapBytes; SELECT dll.ethernetOneType FROM ns => BEGIN ndu: NSBuffer.Body = LOOPHOLE[ dll + SIZE[EthernetOneDriverTypes.Encapsulation]]; IF bytes < ndu.pktLength THEN GOTO Rejected; type ¬ ns; b.highLayer ¬ [LOOPHOLE[dll], 0, bytes - encapBytes]; b.linkLayer.stopIndexPlusOne ¬ encapBytes; END; translation => BEGIN ReceiveTranslate[b]; type ¬ orphan; END; ENDCASE => type ¬ vagrant; EXITS Rejected => BEGIN type ¬ orphan; IF CommFlags.driverStats THEN Stats.StatIncr[statPacketsDiscarded]; END; END; decapsulatePup: PROC [b: Buffer.Buffer] RETURNS [type: Buffer.Type] ¬ DecapsulatePup; DecapsulatePup: PROC [b: Buffer.Buffer] RETURNS [type: Buffer.Type] = BEGIN dll: LONG POINTER TO EthernetOneDriverTypes.Encapsulation; bytes: CARDINAL ¬ b.fo.driver.length; IF bytes < encapBytes THEN GOTO Rejected; bytes ¬ bytes - encapBytes; dll ¬ LOOPHOLE[b.linkLayer.blockPointer]; SELECT dll.ethernetOneType FROM pup => BEGIN ndu: PupDefs.Body = LOOPHOLE[ dll + SIZE[EthernetOneDriverTypes.Encapsulation]]; IF bytes < ndu.pupLength THEN GOTO Rejected; type ¬ pup; b.highLayer ¬ [LOOPHOLE[dll], 0, bytes - encapBytes]; b.linkLayer.stopIndexPlusOne ¬ encapBytes; END; ENDCASE => type ¬ vagrant; EXITS Rejected => BEGIN type ¬ orphan; IF CommFlags.driverStats THEN Stats.StatIncr[statPacketsDiscarded]; END; END; encapsulatePup: PROC [ b: Buffer.Buffer, immediate: LONG POINTER TO PupTypes.PupHostID] ¬ EncapsulatePup; EncapsulatePup: PROC [ b: Buffer.Buffer, immediate: LONG POINTER TO PupTypes.PupHostID] = BEGIN body: PupDefs.Body = LOOPHOLE[b.highLayer.blockPointer]; dll: LONG POINTER TO EthernetOneDriverTypes.Encapsulation; dll ¬ LOOPHOLE[body - SIZE[EthernetOneDriverTypes.Encapsulation]]; dll­ ¬ [ ethernetOne[ethernetOneDest: immediate­, ethernetOneSource: pup.pupHostNumber, ethernetOneType: pup]]; b.fo.driver.length ¬ Inline.BITAND[ body.pupLength + encapBytes + bpw - 1, 177776B]; END; EncapsulateNS: PROC [b: Buffer.Buffer, immediate: LONG POINTER TO HostNumber] = BEGIN foundIt: BOOLEAN; ethernetAddr: PupTypes.PupHostID; dll: LONG POINTER TO EthernetOneDriverTypes.Encapsulation; body: NSBuffer.Body = LOOPHOLE[b.highLayer.blockPointer]; dll ¬ LOOPHOLE[body - SIZE[EthernetOneDriverTypes.Encapsulation]]; [foundIt, ethernetAddr] ¬ Translate[immediate­]; IF foundIt THEN BEGIN dll­ ¬ [ ethernetOne[ethernetOneDest: ethernetAddr, ethernetOneSource: pup.pupHostNumber, ethernetOneType: ns]]; b.fo.driver.length ¬ Inline.BITAND[ body.pktLength + encapBytes + bpw - 1, 177776B]; END ELSE BEGIN --If the translation doesn't work, the source will be set to a broadcast --address. That is always wrong, so it should be detectable. dll­ ¬ [ ethernetOne[ethernetOneDest: , ethernetOneType: , ethernetOneSource: PupTypes.allHosts]]; END; END; SendRawBuffer: ENTRY PROC[b: Buffer.Buffer] = BEGIN dll: LONG POINTER TO EthernetOneDriverTypes.Encapsulation = LOOPHOLE[ b.linkLayer.blockPointer]; IF watcherState.pleaseStop THEN Driver.Glitch[DriverNotActive]; IF dll.ethernetOneSource = PupTypes.allHosts THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END; IF ~hearSelf AND (dll.ethernetOneDest = pup.pupHostNumber OR dll.ethernetOneDest = PupTypes.allHosts) THEN BEGIN --sending to ourself, copy it over since we can't hear it copy: Buffer.Buffer ¬ Driver.GetInputBuffer[ FALSE, Buffer.DataBytesPerRawBuffer[b]]; IF copy # NIL THEN BEGIN words: NATURAL = (b.fo.driver.length + bpw - 1) / bpw; Inline.LongCOPY[ from: @b.linkLayer.blockPointer, nwords: words, to: @copy.linkLayer.blockPointer]; copy.linkLayer.startIndex ¬ 0; copy.linkLayer.stopIndexPlusOne ¬ b.fo.driver.length; --copy length copy.fo.driver ¬ b.fo.driver; copy.fo.time ¬ System.GetClockPulses[]; --pretty close to this time copy.fo.network ¬ LONG[@myDevice]; --LONG because of Mokelumne compiler bug IF CommFlags.doStats THEN Stats.StatIncr[statEtherPacketsLocal]; IF CommFlags.doStats THEN Stats.StatBump[statEtherWordsLocal, b.fo.driver.length]; Driver.PutOnGlobalInputQueue[copy]; END ELSE IF CommFlags.doStats THEN Stats.StatIncr[statEtherEmptyFreeQueue]; END; SendBufferInternal[b]; END; SendBufferInternal: INTERNAL PROC[b: Buffer.Buffer] = BEGIN NS3MBit.SetFaceStatus[b, pending]; b.fo.next ¬ NIL; UNTIL (b.fo.driver.iocb ¬ iocbState.free) # NIL DO IF CommFlags.doStats THEN Stats.StatIncr[statsIocbWait]; WAIT iocbState.wait; ENDLOOP; iocbState.free ¬ iocbState.free.next; QueueOutput[ ether, @b.linkLayer.blockPointer, b.fo.driver.length / bpw, b.fo.driver.iocb]; IF CommFlags.doStats THEN Stats.StatIncr[statEtherPacketsSent]; IF CommFlags.doStats THEN Stats.StatBump[statEtherWordsSent, b.fo.driver.length]; IF outputState.firstBuffer = NIL THEN BEGIN outputState.firstBuffer ¬ b; outputState.timeSendDone ¬ System.GetClockPulses[]; END ELSE outputState.lastBuffer.fo.next ¬ b; outputState.lastBuffer ¬ b; END; --for changing the number of buffers while running numberOfExtraBuffer: CARDINAL = 2; --this should only be called from Boss --No MONITOR PROTECTION here. MaybeChangeNumberOfInputBuffers: PROC[increaseBuffers: BOOLEAN] = BEGIN IF increaseBuffers THEN BEGIN IF inputState.access = NIL THEN inputState.access ¬ Buffer.MakePool[0, numberOfExtraBuffer]; IF inputState.queueAllowed < numberOfExtraBuffer THEN inputState.queueAllowed ¬ myDevice.buffers ¬ myDevice.buffers + numberOfExtraBuffer; END ELSE BEGIN IF inputState.access # NIL THEN {Buffer.DestroyPool[inputState.access]; inputState.access ¬ NIL}; IF inputState.queueAllowed >= numberOfExtraBuffer THEN inputState.queueAllowed ¬ myDevice.buffers ¬ myDevice.buffers - numberOfExtraBuffer; END; END; --COLD code, only used when turning things on+off CreateDefaultEthernetOneDrivers: PUBLIC PROC RETURNS [BOOLEAN] = BEGIN deviceNumber: CARDINAL ¬ 0; etherDevice: DeviceHandle ¬ GetNextDevice[nullDeviceHandle]; IF System.switches[PilotSwitches.noEthernetOne] = down THEN RETURN[FALSE]; IF etherDevice = nullDeviceHandle THEN RETURN[FALSE]; WHILE etherDevice # nullDeviceHandle DO CreateAnEthernetOneDriver[etherDevice, deviceNumber]; etherDevice ¬ GetNextDevice[etherDevice]; deviceNumber ¬ deviceNumber + 1; ENDLOOP; RETURN[TRUE]; END; CreateAnEthernetOneDriver: PROC [ etherDevice: DeviceHandle, deviceNumber: CARDINAL] = BEGIN IF deviceNumber # 0 THEN BEGIN him: LONG POINTER TO FRAME[EthernetOneDriver] ¬ NEW EthernetOneDriver; START him; --so he'll initialize the procedure him.setupEthernetOneDriver[etherDevice]; END ELSE SetupEthernetOneDriver[etherDevice]; END; firstTime: BOOL ¬ TRUE; firstPool: Buffer.AccessHandle; SetupEthernetOneDriver: PROC[etherDevice: DeviceHandle] = BEGIN ether ¬ etherDevice; [, pup.pupHostNumber] ¬ GetEthernet1Address[ether]; watcherState.pleaseStop ¬ TRUE; myDevice.buffers ¬ inputState.queueLength ¬ 10; Driver.AddDeviceToChain[@myDevice]; IF TRUE THEN BEGIN -- Startup BUGs in Boss firstPool ¬ Buffer.MakePool[0, myDevice.buffers + numberOfExtraBuffer]; END; IF CommFlags.doStats OR CommFlags.driverStats THEN BEGIN myDevice.stats ¬ @etherStats; etherStats ¬ []; END; IF PupDefs.GetPupPackageUseCount[] > 0 THEN AdoptForPup[]; AdoptForNS[]; END; AdoptForNS: PROC = BEGIN family: Protocol1.Family ¬ CommunicationInternal.NSPackageMake[]; matrix: Protocol1.MatrixRecord ¬ [ --AddFamilyMember copies fields family: family, context: @ns, encapsulator: EncapsulateNS, decapsulator: DecapsulateNS]; Protocol1.AddFamilyMember[LONG[@myDevice], @matrix]; CommunicationInternal.NSPackageDestroy[]; -- Didn't really want to turn it on END; AdoptForPup: PROC = BEGIN family: Protocol1.Family ¬ PupDefs.PupPackageMake[]; matrix: Protocol1.MatrixRecord ¬ [ --AddFamilyMember copies fields family: family, context: @pup, encapsulator: EncapsulatePup, decapsulator: DecapsulatePup]; Protocol1.AddFamilyMember[LONG[@myDevice], @matrix]; PupDefs.PupPackageDestroy[]; -- Beware: This undoes everything if the PupPackage wasn't on END; ActivateDriver: PROC = BEGIN b: Buffer.Buffer; iocbs: CARDINAL = (myDevice.buffers + numberOfExtraBuffer) * 2; size: CARDINAL = Inline.BITAND[(controlBlockSize + 3), 0FFFCH]; IF ~watcherState.pleaseStop THEN Driver.Glitch[DriverAlreadyActive]; seconds.forty ¬ System.MicrosecondsToPulses[40000000]; seconds.fiveHalf ¬ System.MicrosecondsToPulses[2500000]; seconds.one ¬ System.MicrosecondsToPulses[1000000]; [, pup.pupHostNumber] ¬ GetEthernet1Address[ether]; getGarbage ¬ watcherState.pleaseStop ¬ FALSE; TurnOff[ether]; AddCleanup[ether]; inputState.access ¬ NIL; inputState.queueLength ¬ 0; inputState.queueAllowed ¬ myDevice.buffers; inputState.firstBuffer ¬ inputState.lastBuffer ¬ NIL; outputState.firstBuffer ¬ outputState.lastBuffer ¬ NIL; myEar ¬ pup.pupHostNumber; IF controlBlockSize = 0 THEN Driver.Glitch[IOCBSizeIsZero]; iocbState.free ¬ NIL; Process.EnableAborts[@iocbState.wait]; Process.DisableTimeout[@iocbState.wait]; iocbState.first ¬ iocbState.free ¬ CommUtil.AllocateIocbs[size * iocbs]; FOR i: CARDINAL IN[0..iocbs - 1) DO iocbState.first ¬ iocbState.first.next ¬ iocbState.first + size; REPEAT FINISHED => {iocbState.first.next ¬ NIL; iocbState.first ¬ iocbState.free}; ENDLOOP; THROUGH [0..myDevice.buffers) DO IF (b ¬ Driver.GetInputBuffer[ TRUE, myDevice.receiveBufferLen + physOverhead]) # NIL THEN BEGIN IF iocbState.free = NIL THEN Driver.Glitch[LostAllIocbs]; b.fo.driver.iocb ¬ iocbState.free; iocbState.free ¬ iocbState.free.next; inputState.queueLength ¬ inputState.queueLength + 1; <> IF CommFlags.doDebug AND Inline.BITAND[Inline.LowHalf[b.fo.driver.iocb], 3] # 0 THEN Driver.Glitch[IOCBMustBeQuadWordAligned]; IF CommFlags.doDebug AND Inline.HighHalf[b.fo.driver.iocb] # 0 THEN Driver.Glitch[IOCBMustBeInFirstMDS]; NS3MBit.SetFaceStatus[b, pending]; IF inputState.firstBuffer = NIL THEN inputState.firstBuffer ¬ b; IF inputState.lastBuffer # NIL THEN inputState.lastBuffer.fo.next ¬ b; inputState.lastBuffer ¬ b; END; ENDLOOP; [cv: inputState.inWait, mask: inputState.mask] ¬ SpecialRuntime.AllocateNakedCondition[]; Process.DisableTimeout[inputState.inWait]; Process.EnableAborts[inputState.inWait]; [cv: outputState.outWait, mask: outputState.mask] ¬ SpecialRuntime.AllocateNakedCondition[]; Process.DisableTimeout[outputState.outWait]; Process.EnableAborts[outputState.outWait]; SmashCSBs[]; BEGIN priority: Process.Priority ¬ Process.GetPriority[]; Process.SetPriority[ProcessPriorities.priorityIOHigh]; inputState.process ¬ FORK InInterrupt[]; outputState.process ¬ FORK OutInterrupt[]; Process.SetPriority[priority]; END; Process.EnableAborts[@watcherState.timer]; Process.SetTimeout[@watcherState.timer, Process.SecondsToTicks[1]]; watcherState.process ¬ FORK Watcher[]; CreateCache[]; myDevice.alive ¬ TRUE; --hope I ain't lying. END; SetEthernetOneListener: PUBLIC ENTRY PROC[ physicalOrder: CARDINAL, newHostNumber: CARDINAL] RETURNS [success: BOOLEAN] = BEGIN him: LONG POINTER TO FRAME[EthernetOneDriver]; network: Device ¬ GetNthDeviceLikeMe[physicalOrder]; IF network = NIL THEN RETURN[FALSE]; him ¬ LOOPHOLE[Runtime.GlobalFrame[LOOPHOLE[network.sendRawBuffer]]]; him.ethernetOneListenForHost[newHostNumber]; RETURN[TRUE]; END; EthernetOneListenForHost: PROC[newHostNumber: CARDINAL] = BEGIN myEar ¬ newHostNumber; SmashCSBs[]; END; GetNthDeviceLikeMe: PROC[physicalOrder: CARDINAL] RETURNS [net: Device] = BEGIN i: CARDINAL ¬ 0; net ¬ Driver.GetDeviceChain[]; WHILE net # NIL DO IF net.device = myDevice.device THEN IF (i ¬ i + 1) = physicalOrder THEN RETURN; net ¬ net.next; ENDLOOP; END; SetEthernetOneCollectGarbageToo: PUBLIC ENTRY PROC[ physicalOrder: CARDINAL, collectGarbage: BOOLEAN] RETURNS [success: BOOLEAN] = BEGIN him: LONG POINTER TO FRAME[EthernetOneDriver]; network: Device ¬ GetNthDeviceLikeMe[physicalOrder]; IF network = NIL THEN RETURN[FALSE]; him ¬ LOOPHOLE[Runtime.GlobalFrame[LOOPHOLE[network.sendRawBuffer]]]; him.getGarbage ¬ collectGarbage; RETURN[TRUE]; END; SmashCSBs: PROC = BEGIN TurnOn[ether, myEar, inputState.mask, outputState.mask, LOOPHOLE[0]]; inputState.lastMissed ¬ GetPacketsMissed[ether]; FOR b: Buffer.Buffer ¬ inputState.firstBuffer, b.fo.next UNTIL b = NIL DO QueueInput[ ether, LOOPHOLE[b.linkLayer.blockPointer + offsetToDataUnit], (b.fo.driver.length / bpw) - offsetToDataUnit, b.fo.driver.iocb]; ENDLOOP; inputState.timeLastRecv ¬ System.GetClockPulses[]; END; DeleteDriver: PROC = BEGIN IF ether # GetNextDevice[nullDeviceHandle] THEN Runtime.SelfDestruct[]; END; DeactivateDriver: PROC = BEGIN process: PROCESS; b: Buffer.Buffer; IF watcherState.pleaseStop THEN Driver.Glitch[DriverNotActive]; myDevice.alive ¬ FALSE; --stop all outgoing traffic watcherState.pleaseStop ¬ TRUE; process ¬ inputState.process; UNTIL inputState.process = NIL DO Process.EnableAborts[inputState.inWait]; Process.Abort[process]; REPEAT FINISHED => JOIN process; ENDLOOP; process ¬ outputState.process; UNTIL outputState.process = NIL DO Process.EnableAborts[outputState.outWait]; Process.Abort[process]; REPEAT FINISHED => JOIN process; ENDLOOP; Process.Abort[watcherState.process]; JOIN watcherState.process; TurnOff[ether]; SpecialRuntime.DeallocateNakedCondition[inputState.inWait]; SpecialRuntime.DeallocateNakedCondition[outputState.outWait]; inputState.inWait ¬ outputState.outWait ¬ NIL; MaybeChangeNumberOfInputBuffers[FALSE]; IF firstTime THEN BEGIN firstTime¬ FALSE; Buffer.DestroyPool[firstPool]; END; RemoveCleanup[ether]; UNTIL inputState.firstBuffer = NIL DO b ¬ inputState.firstBuffer; inputState.firstBuffer ¬ b.fo.next; Driver.ReturnFreeBuffer[b]; ENDLOOP; UNTIL outputState.firstBuffer = NIL DO b ¬ outputState.firstBuffer; outputState.firstBuffer ¬ b.fo.next; Driver.PutOnGlobalDoneQueue[b]; ENDLOOP; CommUtil.FreeIocbs[iocbState.first]; iocbState.free ¬ iocbState.first ¬ NIL; DeleteCache[]; END; AddressPair: TYPE = MACHINE DEPENDENT RECORD [ nsAddr: HostNumber, ethernet1Addr: PupTypes.PupHostID, filler: [0..377B]]; TranslationDataUnit: TYPE = LONG POINTER TO TranslationObject; TranslationObject: TYPE = MACHINE DEPENDENT RECORD [ function: TranslationFunction, his, mine: AddressPair]; TranslationFunction: TYPE = MACHINE DEPENDENT { (0), translationResponse(7070B), translationRequest(10101B), (WORD.LAST)}; CacheEntry: TYPE = LONG POINTER TO CacheObject; CacheObject: TYPE = MACHINE DEPENDENT RECORD [ nextLink: CacheEntry, addressPair: AddressPair, tries: CARDINAL, timeStamp: System.Pulses, status: CacheStatus, filler: [0..37777B]]; CacheStatus: TYPE = {new, pending, active, zombie}; --variables translationRequest: CARDINAL = 10101B; translationResponse: CARDINAL = 7070B; cacheQueueHead: CacheEntry; broadCastPairEntry: CacheEntry; --permanent myAddressPairEntry: CacheEntry; --permanent retryLimit: CARDINAL ¬ 10B; depth: CARDINAL; --debugging retryPulses: System.Pulses ¬ System.MicrosecondsToPulses[2000000]; --two seconds deactivatePulses: System.Pulses ¬ System.MicrosecondsToPulses[180000000]; --three minutes cacheEvent: CONDITION; demonRunning: BOOLEAN; etherHost: PupTypes.PupHostID; nsHost: HostNumber ¬ SpecialSystem.GetProcessorID[]; CreateCache: PROC = BEGIN cacheQueueHead ¬ NIL; etherHost ¬ [GetEthernet1Address[ether].host]; Process.SetTimeout[@cacheEvent, Process.SecondsToTicks[1]]; broadCastPairEntry ¬ AddAddressPair[[System.broadcastHostNumber, PupTypes.allHosts, 0]]; myAddressPairEntry ¬ AddAddressPair[[nsHost, etherHost, 0]]; demonRunning ¬ TRUE; Process.Detach[FORK Demon[]]; END; DeleteCache: PROC = BEGIN DeleteCacheLocked: ENTRY PROC = BEGIN NOTIFY cacheEvent; END; e: CacheEntry; WHILE demonRunning DO DeleteCacheLocked[]; Process.Yield[]; ENDLOOP; --cleanup in case demon was never running WHILE (cacheQueueHead # NIL) DO e ¬ cacheQueueHead; cacheQueueHead ¬ e.nextLink; CommHeap.zone.FREE[@e]; ENDLOOP; END; FindEntry: INTERNAL PROC[nsAddr: HostNumber] RETURNS [entry: CacheEntry] = BEGIN IF CommFlags.doStats THEN depth ¬ 0; entry ¬ cacheQueueHead; WHILE entry # NIL DO IF nsAddr = entry.addressPair.nsAddr THEN RETURN; entry ¬ entry.nextLink; IF CommFlags.doStats THEN depth ¬ depth + 1; ENDLOOP; END; AddEntry: INTERNAL PROC [entry: CacheEntry] = {entry.nextLink ¬ cacheQueueHead; cacheQueueHead ¬ entry}; RemoveEntry: INTERNAL PROC [entry: CacheEntry] = BEGIN e, pred: CacheEntry; IF (pred ¬ cacheQueueHead) = entry THEN BEGIN cacheQueueHead ¬ cacheQueueHead.nextLink; RETURN; END; e ¬ pred.nextLink; WHILE e # NIL DO IF e = entry THEN BEGIN pred.nextLink ¬ entry.nextLink; RETURN; END; pred ¬ e; e ¬ pred.nextLink; ENDLOOP; ERROR; END; Translate: ENTRY PROC [nsAddr: HostNumber] RETURNS [foundIt: BOOLEAN, ethernet1Addr: PupTypes.PupHostID] = BEGIN e: CacheEntry; foundIt ¬ FALSE; IF (e ¬ FindEntry[nsAddr]) # NIL THEN BEGIN IF e # cacheQueueHead THEN --put e at the head of the queue BEGIN IF CommFlags.doStats THEN Stats.StatBump[cacheDepth, depth]; RemoveEntry[e]; AddEntry[e]; END; SELECT e.status FROM active => BEGIN foundIt ¬ TRUE; ethernet1Addr ¬ e.addressPair.ethernet1Addr; e.timeStamp ¬ System.GetClockPulses[]; END; zombie => BEGIN e.status ¬ new; e.tries ¬ 0; e.timeStamp ¬ System.GetClockPulses[]; NOTIFY cacheEvent; END; ENDCASE => NULL; END --of found it ELSE --entry not found, so add a new one BEGIN IF CommFlags.doStats THEN Stats.StatIncr[cacheFault]; e ¬ CommHeap.zone.NEW[CacheObject]; e.status ¬ new; e.tries ¬ 0; e.timeStamp ¬ System.GetClockPulses[]; e.addressPair ¬ [nsAddr: nsAddr, ethernet1Addr: [0], filler: 0]; AddEntry[e]; NOTIFY cacheEvent; END; END; AddAddressPair: ENTRY PROC[aP: AddressPair] RETURNS [e: CacheEntry] = BEGIN e ¬ FindEntry[aP.nsAddr]; SELECT e FROM NIL => { e ¬ CommHeap.zone.NEW[CacheObject]; AddEntry[e]; }; broadCastPairEntry, myAddressPairEntry => RETURN; ENDCASE; e.addressPair ¬ aP; e.status ¬ active; e.timeStamp ¬ System.GetClockPulses[]; END; DeallocateEntry: INTERNAL PROC[e: CacheEntry] = BEGIN IF (e = broadCastPairEntry) OR (e = myAddressPairEntry) THEN e.timeStamp ¬ System.GetClockPulses[] ELSE { RemoveEntry[e]; CommHeap.zone.FREE[@e]; }; END; Demon: ENTRY PROC = BEGIN demonRunning ¬ TRUE; Process.SetPriority[ProcessPriorities.priorityIOHigh]; UNTIL watcherState.pleaseStop DO ENABLE ABORTED => EXIT; pendingEntries: BOOLEAN ¬ FALSE; e: CacheEntry; WAIT cacheEvent; e ¬ cacheQueueHead; WHILE (e # NIL) DO age: System.Pulses ¬ [System.GetClockPulses[] - e.timeStamp]; nextE: CacheEntry ¬ e.nextLink; SELECT e.status FROM active, zombie => { IF age > deactivatePulses THEN DeallocateEntry[e]}; pending => BEGIN pendingEntries ¬ TRUE; IF age > retryPulses THEN BEGIN e.tries ¬ e.tries + 1; IF e.tries > retryLimit THEN BEGIN e.status ¬ zombie; IF CommFlags.doStats THEN Stats.StatIncr[unsuccessfulTranslation]; END ELSE BEGIN IF CommFlags.doStats THEN Stats.StatIncr[translationRetries]; SendRequest[e]; e.timeStamp ¬ System.GetClockPulses[]; END; END; END; new => BEGIN pendingEntries ¬ TRUE; SendRequest[e]; e.status ¬ pending; e.timeStamp ¬ System.GetClockPulses[]; END; ENDCASE => ERROR; e ¬ nextE; ENDLOOP; --end of queue entries loop IF pendingEntries THEN Process.SetTimeout[@cacheEvent, Process.SecondsToTicks[1]] ELSE Process.SetTimeout[@cacheEvent, Process.SecondsToTicks[60*5]]; ENDLOOP; --end of infinite loop BEGIN e, nextE: CacheEntry; e ¬ cacheQueueHead; cacheQueueHead ¬ myAddressPairEntry ¬ broadCastPairEntry ¬ NIL; WHILE e # NIL DO nextE ¬ e.nextLink; CommHeap.zone.FREE[@e]; e ¬ nextE; ENDLOOP; END; demonRunning ¬ FALSE; END; bytesPerTranslationResponse: NATURAL = physOverhead + bpw*SIZE[AddressPair]; bytesPerTranslationRequest: NATURAL = physOverhead + 2*bpw*SIZE[AddressPair]; SendRequest: INTERNAL PROC[e: CacheEntry] = BEGIN b: Buffer.Buffer; IF (b ¬ Driver.GetInputBuffer[FALSE, bytesPerTranslationRequest]) # NIL THEN BEGIN dll: LONG POINTER TO EthernetOneDriverTypes.Encapsulation = LOOPHOLE[ b.linkLayer.blockPointer]; tdu: TranslationDataUnit = LOOPHOLE[ dll + SIZE[EthernetOneDriverTypes.Encapsulation]]; dll­ ¬ [ ethernetOne[ethernetOneDest: PupTypes.allHosts, ethernetOneSource: pup.pupHostNumber, ethernetOneType: translation]]; tdu­ ¬ [function: translationRequest, his: e.addressPair, mine: myAddressPairEntry.addressPair]; tdu.his.filler ¬ e.tries; b.fo.driver.length ¬ bytesPerTranslationRequest; --longer than response SendBufferInternal[b]; --okay, it should be ready END; END; ReceiveTranslate: PROC [b: Buffer.Buffer] = BEGIN dll: LONG POINTER TO EthernetOneDriverTypes.Encapsulation = LOOPHOLE[ b.linkLayer.blockPointer]; tdu: TranslationDataUnit = LOOPHOLE[ dll + SIZE[EthernetOneDriverTypes.Encapsulation]]; SELECT tdu.function FROM translationRequest => BEGIN IF b.fo.driver.length < bytesPerTranslationRequest THEN RETURN; IF tdu.his.nsAddr = nsHost THEN BEGIN a: Buffer.Buffer; atdu: TranslationDataUnit; adll: LONG POINTER TO EthernetOneDriverTypes.Encapsulation; -- since the requester is probably going to talk to us, -- add his address before we take a fault [] ¬ AddAddressPair[tdu.mine]; tdu.mine.ethernet1Addr ¬ [pup.pupHostNumber]; IF CommFlags.doStats THEN Stats.StatIncr[requestsForMe]; a ¬ Driver.GetInputBuffer[FALSE, bytesPerTranslationRequest]; IF a = NIL THEN RETURN; adll ¬ LOOPHOLE[a.linkLayer.blockPointer]; atdu ¬ LOOPHOLE[dll + SIZE[EthernetOneDriverTypes.Encapsulation]]; adll­ ¬ [ ethernetOne[ ethernetOneDest: dll.ethernetOneSource, ethernetOneSource: pup.pupHostNumber, ethernetOneType: translation]]; atdu­ ¬ [function: translationResponse, his: tdu.his, mine: ]; a.fo.driver.length ¬ bytesPerTranslationResponse; --shorter than req. SendRawBuffer[a]; END; END; translationResponse => BEGIN IF b.fo.driver.length < bytesPerTranslationResponse THEN RETURN; IF dll.ethernetOneDest # etherHost THEN RETURN; [] ¬ AddAddressPair[tdu.his]; END; ENDCASE; END; getInfo: PROC [pup: PupTypes.PupHostID, network: Device] RETURNS [ns: System.HostNumber] ¬ GetInfo; GetInfo: PUBLIC ENTRY PROC [pup: PupTypes.PupHostID, network: Device] RETURNS [ns: System.HostNumber] = BEGIN ns ¬ System.nullHostNumber; IF network = @myDevice THEN BEGIN FOR entry: CacheEntry ¬ cacheQueueHead, entry.nextLink UNTIL entry = NIL DO IF entry.status # active THEN LOOP; IF pup # entry.addressPair.ethernet1Addr THEN LOOP; ns ¬ entry.addressPair.nsAddr; RETURN; ENDLOOP; RETURN; END; FOR finger: Device ¬ myDevice.next, finger.next UNTIL finger = NIL DO him: LONG POINTER TO FRAME[EthernetOneDriver]; IF finger.device # ethernetOne THEN LOOP; him ¬ LOOPHOLE[Runtime.GlobalFrame[LOOPHOLE[finger.sendRawBuffer]]]; ns ¬ him.getInfo[pup, network]; EXIT; ENDLOOP; END; --* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * --End of Ethernet1 uglyness --* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * --initialization setupEthernetOneDriver ¬ SetupEthernetOneDriver; --for multi instances ethernetOneListenForHost ¬ EthernetOneListenForHost; END. --EthernetOneDriver