-- Copyright (C) 1983, 1984, 1985 by Xerox Corporation. All rights reserved. -- EthernetOneDriver.mesa, HGM, 5-Oct-85 13:42:20 -- (last edit by: AOF on: 15-Jun-83 11:35:16) -- NB: Allocates 10 buffers for Dicentras DIRECTORY Buffer USING [AccessHandle, Buffer, DestroyPool, GetBuffer, MakePool, Type], CommFlags USING [doDebug, doStats, driverStats], CommHeap USING [zone], CommunicationInternal USING [NSPackageDestroy, NSPackageMake], CommUtil USING [AllocateIocbs, FreeIocbs], Driver USING [ Glitch, GetDeviceChain, GetInputBuffer, Network, NetworkObject, AddDeviceToChain, PutOnGlobalDoneQueue, PutOnGlobalInputQueue, ReturnFreeBuffer], DriverTypes USING [ Byte, Encapsulation, ethernetOneEncapsulationOffset, ethernetOneEncapsulationBytes], Environment USING [Byte], 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 [], PupDefs USING [GetPupPackageUseCount, PupPackageDestroy, PupPackageMake], PupPktOps USING [pupBuffers], 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, Process, Protocol1, PupDefs, PupPktOps, Runtime, SpecialRuntime, SpecialSystem, System EXPORTS Buffer, Driver, NS3MBit, SpecialCommunication, System = BEGIN OPEN EthernetOneFace; --EXPORTed TYPEs Network: PUBLIC TYPE = Driver.Network; HostNumber: PUBLIC TYPE = SpecialSystem.HostNumber; NetworkNumber: PUBLIC TYPE = SpecialSystem.NetworkNumber; encapOffset: CARDINAL = DriverTypes.ethernetOneEncapsulationOffset; encapBytes: CARDINAL = DriverTypes.ethernetOneEncapsulationBytes; 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: @myNetwork, stats: NIL]; pup: PupRouterDefs.ContextObject ← [protocol: NIL, network: @myNetwork, 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]; myNetwork: Driver.NetworkObject ← [ matrix: DESCRIPTOR[NIL, 0], sendRawBuffer: SendRawBuffer, activateDriver: ActivateDriver, deactivateDriver: DeactivateDriver, deleteDriver: DeleteDriver, buffers:, device: ethernetOne, 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; --Hot Procedures GetBufferAndIocb: INTERNAL PROC RETURNS[b: Buffer.Buffer] = BEGIN SELECT TRUE FROM (iocbState.free = NIL) => {IF CommFlags.doStats THEN Stats.StatIncr[statsIocbWait]; RETURN[NIL]}; ((b ← Driver.GetInputBuffer[FALSE]) = NIL) => RETURN; ENDCASE; b.driver.iocb ← iocbState.free; iocbState.free ← iocbState.free.next; END; --GetBufferAndIocb FreeBufferAndIocb: INTERNAL PROC[ queueProc: PROC[b: Buffer.Buffer], b: Buffer.Buffer] = BEGIN iocb: FreeIocb ← b.driver.iocb; IF iocb # NIL THEN BEGIN iocb.next ← iocbState.free; iocbState.free ← iocb; b.driver.iocb ← NIL; NOTIFY iocbState.wait; 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.driver.iocb]; IF CommFlags.doStats AND status # pending THEN Stats.StatIncr[statEtherInterruptDuringInterrupt]; UNTIL status # pending DO WAIT inputState.inWait; status ← GetStatus[this.driver.iocb]; IF CommFlags.doStats AND status = pending THEN Stats.StatIncr[statEtherMissingStatus]; ENDLOOP; inputState.firstBuffer ← inputState.firstBuffer.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.next ← NIL} --recycle this buffer ELSE BEGIN inputState.timeLastRecv ← this.time ← System.GetClockPulses[]; this.driver.length ← GetPacketLength[this.driver.iocb]; this.driver.faceStatus ← ethernetOne[status]; this.network ← LONG[@myNetwork]; IF CommFlags.driverStats THEN BEGIN etherStats.packetsRecv ← etherStats.packetsRecv + 1; etherStats.wordsRecv ← etherStats.wordsRecv + this.driver.length; END; IF CommFlags.doStats THEN BEGIN Stats.StatIncr[statEtherPacketsReceived]; Stats.StatBump[statEtherWordsReceived, this.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 new.driver.faceStatus ← ethernetOne[pending]; QueueInput[ ether, @new.encapsulation + encapOffset, new.driver.length - encapOffset, new.driver.iocb]; IF inputState.firstBuffer = NIL THEN inputState.firstBuffer ← new ELSE inputState.lastBuffer.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.driver.iocb]) # pending) => EXIT; ENDCASE; WAIT outputState.outWait; ENDLOOP; outputState.timeSendDone ← System.GetClockPulses[]; --still transmiting b.driver.faceStatus ← ethernetOne[status]; IF status = ok THEN BEGIN IF CommFlags.doStats OR CommFlags.driverStats THEN BEGIN tries: CARDINAL ← GetRetries[b.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.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.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.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.driver.iocb ← iocbState.free) # NIL) => BEGIN iocbState.free ← iocbState.free.next; b.driver.faceStatus ← ethernetOne[pending]; QueueInput[ ether, @b.encapsulation + encapOffset, b.driver.length - encapOffset, b.driver.iocb]; IF inputState.firstBuffer = NIL THEN inputState.firstBuffer ← b ELSE inputState.lastBuffer.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[tryToWaitForBuffer: TRUE]; 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.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 bytes: CARDINAL ← 2*b.driver.length; IF bytes < encapBytes THEN GOTO Rejected; bytes ← bytes - encapBytes; SELECT b.encapsulation.ethernetOneType FROM ns => BEGIN IF bytes < b.ns.pktLength THEN GOTO Rejected; type ← ns; 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 bytes: CARDINAL ← 2*b.driver.length; IF bytes < encapBytes THEN GOTO Rejected; bytes ← bytes - encapBytes; SELECT b.encapsulation.ethernetOneType FROM pup => BEGIN IF bytes < b.pup.pupLength THEN GOTO Rejected; type ← pup; 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 b.encapsulation ← [ ethernetOne[ etherSpare1:, etherSpare2:, etherSpare3:, etherSpare4:, etherSpare5:, translationWorked: TRUE, ethernetOneDest: immediate↑, ethernetOneSource: pup.pupHostNumber, ethernetOneType: pup]]; b.driver.length ← (b.pup.pupLength + 1 + encapBytes)/2; END; EncapsulateNS: PROC [b: Buffer.Buffer, immediate: LONG POINTER TO HostNumber] = BEGIN foundIt: BOOLEAN; ethernetAddr: PupTypes.PupHostID; [foundIt, ethernetAddr] ← Translate[immediate↑]; IF foundIt THEN BEGIN b.encapsulation ← [ ethernetOne[ etherSpare1:, etherSpare2:, etherSpare3:, etherSpare4:, etherSpare5:, translationWorked: TRUE, ethernetOneDest: ethernetAddr, ethernetOneSource: pup.pupHostNumber, ethernetOneType: ns]]; b.driver.length ← (b.ns.pktLength + 1 + encapBytes)/2; END ELSE BEGIN b.encapsulation ← [ ethernetOne[ etherSpare1:, etherSpare2:, etherSpare3:, etherSpare4:, etherSpare5:, translationWorked: FALSE, ethernetOneDest:, ethernetOneSource:, ethernetOneType:]]; END; END; SendRawBuffer: ENTRY PROC[b: Buffer.Buffer] = BEGIN IF watcherState.pleaseStop THEN Driver.Glitch[DriverNotActive]; IF ~b.encapsulation.translationWorked THEN BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END; IF ~hearSelf AND (b.encapsulation.ethernetOneDest = pup.pupHostNumber OR b.encapsulation.ethernetOneDest = PupTypes.allHosts) THEN BEGIN --sending to ourself, copy it over since we can't hear it copy: Buffer.Buffer ← Driver.GetInputBuffer[]; IF copy # NIL THEN BEGIN Inline.LongCOPY[ from: @b.encapsulation + encapOffset, nwords: b.driver.length, to: @copy.encapsulation + encapOffset]; copy.driver ← b.driver; copy.network ← LONG[@myNetwork]; --LONG because of Mokelumne compiler bug IF CommFlags.doStats THEN Stats.StatIncr[statEtherPacketsLocal]; IF CommFlags.doStats THEN Stats.StatBump[statEtherWordsLocal, b.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 b.driver.faceStatus ← ethernetOne[pending]; b.next ← NIL; UNTIL (b.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.encapsulation + encapOffset, b.driver.length, b.driver.iocb]; IF CommFlags.doStats THEN Stats.StatIncr[statEtherPacketsSent]; IF CommFlags.doStats THEN Stats.StatBump[statEtherWordsSent, b.driver.length]; IF outputState.firstBuffer = NIL THEN BEGIN outputState.firstBuffer ← b; outputState.timeSendDone ← System.GetClockPulses[]; END ELSE outputState.lastBuffer.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 ← myNetwork.buffers ← myNetwork.buffers + numberOfExtraBuffer; END ELSE BEGIN IF inputState.access # NIL THEN {Buffer.DestroyPool[inputState.access]; inputState.access ← NIL}; IF inputState.queueAllowed >= numberOfExtraBuffer THEN inputState.queueAllowed ← myNetwork.buffers ← myNetwork.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; myNetwork.buffers ← inputState.queueLength ← 10; Driver.AddDeviceToChain[@myNetwork]; IF TRUE THEN BEGIN -- Startup BUGs in Boss firstPool ← Buffer.MakePool[0, myNetwork.buffers + numberOfExtraBuffer]; END; IF CommFlags.doStats OR CommFlags.driverStats THEN BEGIN myNetwork.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[@myNetwork], @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[@myNetwork], @matrix]; PupDefs.PupPackageDestroy[]; -- Beware: This undoes everything if the PupPackage wasn't on END; ActivateDriver: PROC = BEGIN b: Buffer.Buffer; iocbs: CARDINAL = (myNetwork.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 ← myNetwork.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..myNetwork.buffers) DO IF (b ← Driver.GetInputBuffer[TRUE]) # NIL THEN BEGIN IF iocbState.free = NIL THEN Driver.Glitch[LostAllIocbs]; b.driver.iocb ← iocbState.free; iocbState.free ← iocbState.free.next; inputState.queueLength ← inputState.queueLength + 1; IF CommFlags.doDebug AND Inline.BITAND[Inline.LowHalf[ @b.encapsulation + encapOffset], 3] # 0 THEN Driver.Glitch[BufferMustBeAlmostQuadWordAligned]; IF CommFlags.doDebug AND Inline.BITAND[Inline.LowHalf[b.driver.iocb], 3] # 0 THEN Driver.Glitch[IOCBMustBeQuadWordAligned]; IF CommFlags.doDebug AND Inline.HighHalf[b.driver.iocb] # 0 THEN Driver.Glitch[IOCBMustBeInFirstMDS]; b.driver.faceStatus ← ethernetOne[pending]; IF inputState.firstBuffer = NIL THEN inputState.firstBuffer ← b; IF inputState.lastBuffer # NIL THEN inputState.lastBuffer.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[]; END; SetEthernetOneListener: PUBLIC ENTRY PROC[ physicalOrder: CARDINAL, newHostNumber: CARDINAL] RETURNS [success: BOOLEAN] = BEGIN him: LONG POINTER TO FRAME[EthernetOneDriver]; network: Network ← 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: Network] = BEGIN i: CARDINAL ← 0; net ← Driver.GetDeviceChain[]; WHILE net # NIL DO IF net.device = myNetwork.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: Network ← 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.next UNTIL b = NIL DO QueueInput[ ether, @b.encapsulation + encapOffset, b.driver.length - encapOffset, b.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]; 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.next; Driver.ReturnFreeBuffer[b]; ENDLOOP; UNTIL outputState.firstBuffer = NIL DO b ← outputState.firstBuffer; outputState.firstBuffer ← b.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]]; 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; wordsPerTranslationRequest: NAT = (1 + encapBytes)/2 + 1 + 2*SIZE[AddressPair]; wordsPerTranslationResponse: NAT = (1 + encapBytes)/2 + 1 + SIZE[AddressPair]; SendRequest: INTERNAL PROC[e: CacheEntry] = BEGIN b: Buffer.Buffer; request: LONG POINTER TO AddressPair; IF (b ← Driver.GetInputBuffer[FALSE]) # NIL THEN BEGIN --broadcast the translation request b.encapsulation ← [ ethernetOne[ etherSpare1:, etherSpare2:, etherSpare3:, etherSpare4:, etherSpare5:, translationWorked: TRUE, ethernetOneDest: PupTypes.allHosts, ethernetOneSource: pup.pupHostNumber, ethernetOneType: translation]]; b.driver.length ← wordsPerTranslationRequest; b.rawWords[0] ← translationRequest; request ← LOOPHOLE[@b.rawWords[1]]; request↑ ← e.addressPair; request.filler ← e.tries; --also send our addresses, so responder does not fault request ← request + SIZE[AddressPair]; request↑ ← myAddressPairEntry.addressPair; --send it SendBufferInternal[b]; END; END; ReceiveTranslate: PROC [b: Buffer.Buffer] = BEGIN SELECT b.rawWords[0] FROM translationRequest => { request: LONG POINTER TO AddressPair = LOOPHOLE[@b.rawWords[1]]; IF b.driver.length < wordsPerTranslationRequest THEN RETURN; IF request.nsAddr = nsHost THEN { a: Buffer.Buffer; requesterAddr: LONG POINTER TO AddressPair = request + SIZE[AddressPair]; response: LONG POINTER TO AddressPair; -- since the requester is probably going to talk to us, -- add his address before we take a fault [] ← AddAddressPair[requesterAddr↑]; request.ethernet1Addr ← [pup.pupHostNumber]; IF CommFlags.doStats THEN Stats.StatIncr[requestsForMe]; a ← Buffer.GetBuffer[, PupPktOps.pupBuffers, send, smallBuffer, FALSE]; IF a = NIL THEN RETURN; a.encapsulation ← [ ethernetOne[ etherSpare1:, etherSpare2:, etherSpare3:, etherSpare4:, etherSpare5:, translationWorked: TRUE, ethernetOneDest: b.encapsulation.ethernetOneSource, ethernetOneSource: pup.pupHostNumber, ethernetOneType: translation]]; a.driver.length ← wordsPerTranslationResponse; a.rawWords[0] ← translationResponse; response ← LOOPHOLE[@a.rawWords[1]]; response↑ ← request↑; SendRawBuffer[a]; }; }; translationResponse => { receipt: LONG POINTER TO AddressPair ← LOOPHOLE[@b.rawWords[1]]; IF b.driver.length < wordsPerTranslationResponse THEN RETURN; IF b.encapsulation.ethernetOneDest # etherHost THEN RETURN; [] ← AddAddressPair[receipt↑]; }; ENDCASE; END; getInfo: PROC [pup: PupTypes.PupHostID, network: Driver.Network] RETURNS [ns: System.HostNumber] ← GetInfo; GetInfo: PUBLIC ENTRY PROC [pup: PupTypes.PupHostID, network: Driver.Network] RETURNS [ns: System.HostNumber] = BEGIN ns ← System.nullHostNumber; IF network = @myNetwork 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: Driver.Network ← myNetwork.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