DIRECTORY BufferDefs, CommFlags USING [doDebug, doStats], CommUtilDefs USING [GetEthernetHostNumber], DriverTypes USING [Encapsulation, ethernetBroadcastHost], DriverDefs USING [Glitch, GetDeviceChain, GetInputBuffer, Network, NetworkObject, AddDeviceToChain, PutOnGlobalDoneQueue, PutOnGlobalInputQueue], EthernetDriverStats, EthernetFace, GermSwap USING [switches], PupTypes USING [allHosts, PupErrorCode, PupHostID], StatsDefs USING [StatBump, StatIncr, StatCounterIndex], LoadState USING [SelfDestruct], PrincOpsUtils USING [AllocateNakedCondition, BITAND, DeallocateNakedCondition, GlobalFrame, HighHalf, LongCopy, LowHalf, LongZero], Process USING [Detach, DisableTimeout, MsecToTicks, SecondsToTicks, SetPriority, SetTimeout, Ticks, Yield], ProcessorFace USING [GetGreenwichMeanTime, GetClockPulses, GreenwichMeanTime, microsecondsPerHundredPulses], SpecialCommunication USING [], NSAddress USING [broadcastHostNumber, ProcessorID, HostNumber, GetProcessorID, nullHostNumber, nullNetworkNumber]; EthernetDriver: MONITOR IMPORTS BufferDefs, CommUtilDefs, DriverDefs, StatsDefs, EthernetFace, GermSwap, LoadState, PrincOpsUtils, Process, ProcessorFace, NSAddress EXPORTS BufferDefs, DriverDefs, EthernetDriverStats, SpecialCommunication SHARES BufferDefs, NSAddress = { OPEN StatsDefs, BufferDefs, EthernetFace, NSAddress; Network: PUBLIC TYPE = DriverDefs.Network; ether: DeviceHandle; me: NSAddress.ProcessorID; myEar: NSAddress.HostNumber; getGarbage: BOOL _ FALSE; -- when true, we deliver any packet globalStatePtr: GlobalStatePtr; -- Allocate space if needed inProcess, outProcess: PROCESS; inWait, outWait: LONG POINTER TO CONDITION _ NIL; firstOutputBuffer, lastOutputBuffer: Buffer; firstInputBuffer, lastInputBuffer: Buffer; inInterruptMask, outInterruptMask: WORD; watcherProcess: PROCESS; pleaseStop: BOOL; timer: CONDITION; timeLastRecv, timeSendStarted: Pulses; oneSecondOfPulses: Pulses; inputQueueLength: CARDINAL _ 1; inputBuffersInQueue: CARDINAL; myNetwork: DriverDefs.NetworkObject _ [ decapsulateBuffer: DecapsulateBuffer, encapsulatePup: EncapsulatePup, encapsulateOis: EncapsulateOis, sendBuffer: SendBuffer, forwardBuffer: ForwardBuffer, activateDriver: ActivateDriver, deactivateDriver: DeactivateDriver, deleteDriver: DeleteDriver, interrupt: InInterrupt, changeNumberOfInputBuffers: MaybeChangeNumberOfInputBuffers, alive: TRUE, speed: 10000, -- in kiloBits/sec buffers:, spare:, device: ethernet, index:, netNumber: nullNetworkNumber, hostNumber: 0, next: NIL, pupStats: NIL, stats: NIL]; FunnyRetransmissionMask: PUBLIC ERROR = CODE; DriverNotActive: PUBLIC ERROR = CODE; DriverAlreadyActive: PUBLIC ERROR = CODE; CantMakImageWhileEtherentDriverIsActive: PUBLIC ERROR = CODE; BufferMustBeAlmostQuadWordAligned: PUBLIC ERROR = CODE; IOCBMustBeQuadWordAligned: PUBLIC ERROR = CODE; IOCBMustBeInFirstMDS: PUBLIC ERROR = CODE; etherStats: EthernetDriverStats.EtherStats _ NIL; Alignment: PROC [lp: LONG POINTER] RETURNS [CARDINAL] = INLINE { RETURN [PrincOpsUtils.BITAND[LOOPHOLE[PrincOpsUtils.LowHalf[lp]], 3]]; }; Pulses: TYPE = LONG CARDINAL; MicrosecondsToPulses: PROC[m: LONG CARDINAL] RETURNS[ Pulses] = { RETURN[ MIN[LAST[LONG CARDINAL]/100,m/ProcessorFace.microsecondsPerHundredPulses]*100 ] }; InInterrupt: ENTRY PROC = { acceptBuffer: BOOL; this, new: Buffer; status: Status; lastMissed, missed: CARDINAL _ GetPacketsMissed[ether]; Process.SetPriority[3]; DO UNTIL pleaseStop OR (this _ firstInputBuffer) # NIL DO WAIT inWait; ENDLOOP; IF pleaseStop THEN EXIT; status _ GetStatus[this.iocbChain]; IF CommFlags.doStats AND status # pending THEN StatIncr[statEtherInterruptDuringInterrupt]; UNTIL pleaseStop OR status # pending DO WAIT inWait; status _ GetStatus[this.iocbChain]; IF CommFlags.doStats AND status = pending THEN StatIncr[statEtherMissingStatus]; ENDLOOP; IF CommFlags.doStats AND (missed _ GetPacketsMissed[ether]) # lastMissed THEN { StatBump[statEtherEmptyNoBuffer, missed - lastMissed]; lastMissed _ missed; }; IF pleaseStop THEN EXIT; firstInputBuffer _ firstInputBuffer.next; SELECT status FROM ok => acceptBuffer _ TRUE; ENDCASE => { etherStats.badRecvStatus _ etherStats.badRecvStatus + 1; IF status = overrun THEN etherStats.overruns _ etherStats.overruns + 1; acceptBuffer _ getGarbage; -- we may be collecting garbage packets IF CommFlags.doStats THEN SELECT status FROM packetTooLong => StatIncr[statEtherReceivedTooLong]; badAlignmentButOkCrc => StatIncr[statEtherReceivedNot16]; crc => StatIncr[statEtherReceivedBadCRC]; crcAndBadAlignment => StatIncr[statEtherReceivedNot16BadCRC]; overrun => StatIncr[statEtherReceivedOverrun]; ENDCASE => StatIncr[statEtherReceivedBadStatus]; }; IF acceptBuffer THEN { this.time _ timeLastRecv _ ProcessorFace.GetClockPulses[]; this.length _ GetPacketLength[this.iocbChain]; this.network _ LONG[@myNetwork]; -- LONG because of Mokelumne compiler bug etherStats.packetsRecv _ etherStats.packetsRecv + 1; etherStats.wordsRecv _ etherStats.wordsRecv + this.length; IF CommFlags.doStats THEN { StatIncr[statEtherPacketsReceived]; StatBump[statEtherWordsReceived, this.length]; }; DriverDefs.PutOnGlobalInputQueue[this]; IF (new _ DriverDefs.GetInputBuffer[]) = NIL THEN { inputBuffersInQueue _ inputBuffersInQueue - 1; NOTIFY timer; etherStats.inputOff _ etherStats.inputOff + 1; IF CommFlags.doStats THEN StatIncr[statEtherEmptyFreeQueue]; }; -- cant get new buffer clause } -- acceptBuffer clause ELSE { new _ this; -- Some kind of error, recycle this buffer }; -- reject buffer clause IF new # NIL THEN { new.device _ ethernet; QueueInput[ether, @new.encapsulation, new.length, new.iocbChain]; new.next _ NIL; IF firstInputBuffer = NIL THEN firstInputBuffer _ new ELSE lastInputBuffer.next _ new; lastInputBuffer _ new; }; ENDLOOP; }; OutInterrupt: ENTRY PROC = { b: Buffer; status: Status; Process.SetPriority[3]; UNTIL pleaseStop DO DO IF pleaseStop THEN EXIT; IF (b _ firstOutputBuffer) # NIL AND (status _ GetStatus[b.iocbChain]) # pending THEN EXIT; WAIT outWait; ENDLOOP; IF pleaseStop THEN EXIT; -- so that we do not do something below SELECT status FROM ok => { tries: CARDINAL _ GetRetries[b.iocbChain]; etherStats.packetsSent _ etherStats.packetsSent + 1; etherStats.wordsSent _ etherStats.wordsSent + b.length; IF tries > EthernetDriverStats.MaxTries THEN tries _ EthernetDriverStats.MaxTries; etherStats.loadTable[tries] _ etherStats.loadTable[tries] + 1; IF CommFlags.doStats THEN { statEtherSendsCollision1: StatsDefs.StatCounterIndex = statEtherSendsCollision1; first: CARDINAL = LOOPHOLE[statEtherSendsCollision1]; IF tries # 0 THEN StatIncr[LOOPHOLE[first + tries]]; }; }; ENDCASE => { SELECT status FROM tooManyCollisions => { etherStats.loadTable[16] _ etherStats.loadTable[16] + 1; IF CommFlags.doStats THEN StatIncr[statEtherSendsCollisionLoadOverflow]; }; underrun => { etherStats.overruns _ etherStats.overruns + 1; IF CommFlags.doStats THEN StatIncr[statEtherSendOverrun]; }; ENDCASE => { etherStats.badSendStatus _ etherStats.badSendStatus + 1; IF CommFlags.doStats THEN StatIncr[statEtherSendBadStatus]; }; }; firstOutputBuffer _ firstOutputBuffer.next; DriverDefs.PutOnGlobalDoneQueue[b]; ENDLOOP; }; GetElapsedPulses: PROC [startTime: Pulses] RETURNS [Pulses] = INLINE { RETURN[ProcessorFace.GetClockPulses[] - startTime]; }; Watcher: PROC = { fiveSecondsOfPulses: Pulses _ MicrosecondsToPulses[5000000]; fiveHalfSecondsOfPulses: Pulses _ MicrosecondsToPulses[2500000]; UNTIL pleaseStop DO THROUGH [0..25) DO IF InputChainOK[] THEN EXIT; REPEAT FINISHED => { WatcherNotify[]; }; ENDLOOP; THROUGH [0..25) DO IF OutputChainOK[] THEN EXIT; REPEAT FINISHED => { WatcherNotify[]; }; ENDLOOP; IF GetElapsedPulses[timeLastRecv] > fiveSecondsOfPulses THEN FixupInput[]; IF firstOutputBuffer # NIL AND (GetElapsedPulses[timeSendStarted] > fiveHalfSecondsOfPulses) THEN ShootDownOutput[]; IF InputBufferQueueOK[] THEN WatcherWait[]; ENDLOOP; }; InputChainOK: ENTRY PROC RETURNS [BOOL] = INLINE { RETURN[ (firstInputBuffer = NIL) OR (GetStatus[firstInputBuffer.iocbChain] = pending)]; }; OutputChainOK: ENTRY PROC RETURNS [BOOL] = INLINE { RETURN[ (firstOutputBuffer = NIL) OR (GetStatus[firstOutputBuffer.iocbChain] = pending)]; }; InputBufferQueueOK: PROC RETURNS [BOOL] = INLINE { b: Buffer; enterTime: Pulses _ ProcessorFace.GetClockPulses[]; QueueInputBufferLocked: ENTRY PROC = INLINE { QueueInput[ether, @b.encapsulation, b.length, b.iocbChain]; IF firstInputBuffer = NIL THEN firstInputBuffer _ b ELSE lastInputBuffer.next _ b; lastInputBuffer _ b; inputBuffersInQueue _ inputBuffersInQueue + 1; }; WHILE (inputBuffersInQueue < myNetwork.buffers) DO IF (b _ DriverDefs.GetInputBuffer[TRUE]) # NIL THEN { b.device _ ethernet; b.next _ NIL; QueueInputBufferLocked[]; }; IF GetElapsedPulses[enterTime] > oneSecondOfPulses THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; }; WatcherWait: ENTRY PROC = INLINE { WAIT timer; }; WatcherNotify: ENTRY PROC = INLINE { IF CommFlags.doStats THEN StatIncr[statEtherLostInterrupts]; SmashCSBs[]; -- this will leave output dangling }; FixupInput: ENTRY PROC = INLINE { IF CommFlags.doStats THEN StatIncr[statInputIdle]; SmashCSBs[]; -- this will leave output dangling }; ShootDownOutput: ENTRY PROC = INLINE { b: Buffer; TurnOff[ether]; UNTIL firstOutputBuffer = NIL DO b _ firstOutputBuffer; firstOutputBuffer _ firstOutputBuffer.next; DriverDefs.PutOnGlobalDoneQueue[b]; IF CommFlags.doStats THEN StatIncr[statPacketsStuckInOutput]; ENDLOOP; SmashCSBs[]; }; DecapsulateBuffer: PROC [b: Buffer] RETURNS [BufferType] = { SELECT b.encapsulation.ethernetType FROM pup => { IF 2*b.length < b.pupLength + 2*SIZE[DriverTypes.Encapsulation] THEN { IF CommFlags.doStats THEN StatIncr[statPupsDiscarded]; RETURN[rejected]; }; RETURN[pup]; }; ois => { IF 2*b.length < b.ois.pktLength + 2*SIZE[DriverTypes.Encapsulation] THEN { IF CommFlags.doStats THEN StatIncr[statOisDiscarded]; RETURN[rejected]; }; RETURN[ois]; }; translation => { IF b.rawWords[0] = translationRequest THEN receiveRequest[b] ELSE IF b.rawWords[0] = translationResponse THEN receiveAck[b] ELSE RETURN[rejected]; RETURN[processed]; }; ENDCASE => RETURN[rejected]; }; EncapsulatePup: PROC [b: PupBuffer, destination: PupHostID] = { foundIt: BOOL; oisAddr: OisAddr; [foundIt, oisAddr] _ translate[destination]; IF foundIt THEN { b.encapsulation _ [ethernet[ethernetDest: oisAddr, ethernetSource: me, ethernetType: pup]]; b.length _ (b.pupLength + 1)/2 + SIZE[DriverTypes.Encapsulation]; } ELSE { b.encapsulation _ [ethernet[ ethernetDest:, ethernetSource: NSAddress.nullHostNumber, ethernetType: pup]]; }; }; EncapsulateOis: PROC [ b: OisBuffer, destination: NSAddress.HostNumber] = { b.encapsulation _ [ethernet[ethernetDest: destination, ethernetSource: me, ethernetType: ois]]; b.length _ (b.ois.pktLength + 1)/2 + SIZE[DriverTypes.Encapsulation]; }; ForwardBuffer: PROC [b: Buffer] RETURNS [PupTypes.PupErrorCode] = { IF FALSE THEN -- outputQueue.length>10 THEN RETURN[gatewayResourceLimitsPupErrorCode]; -- transceiver unplugged? SendBuffer[b]; RETURN[noErrorPupErrorCode]; }; SendBuffer: ENTRY PROC [b: Buffer] = { IF pleaseStop THEN DriverDefs.Glitch[DriverNotActive]; IF b.encapsulation.ethernetSource = NSAddress.nullHostNumber THEN { DriverDefs.PutOnGlobalDoneQueue[b]; RETURN; }; IF ~hearSelf AND (b.encapsulation.ethernetDest = me OR b.encapsulation.ethernetDest = DriverTypes.ethernetBroadcastHost) THEN { -- sending to ourself, copy it over since we can't hear it copy: Buffer _ DriverDefs.GetInputBuffer[]; IF copy # NIL THEN { copy.device _ ethernet; PrincOpsUtils.LongCopy[ from: @b.encapsulation, nwords: b.length, to: @copy.encapsulation]; copy.length _ b.length; copy.network _ LONG[@myNetwork]; -- LONG because of Mokelumne compiler bug IF CommFlags.doStats THEN StatIncr[statEtherPacketsLocal]; IF CommFlags.doStats THEN StatBump[statEtherWordsLocal, b.length]; DriverDefs.PutOnGlobalInputQueue[copy]; } ELSE IF CommFlags.doStats THEN StatIncr[statEtherEmptyFreeQueue]; }; SendBufferInternal[b]; }; SendBufferInternal: INTERNAL PROC [b: Buffer] = { minWordsPerEthernetPacket: CARDINAL = (64/2)-2; --*** Should move to DriverTypes words: CARDINAL _ MAX[b.length,minWordsPerEthernetPacket]; b.device _ ethernet; QueueOutput[ether, @b.encapsulation, words, b.iocbChain]; IF CommFlags.doStats THEN StatIncr[statEtherPacketsSent]; IF CommFlags.doStats THEN StatBump[statEtherWordsSent, b.length]; IF firstOutputBuffer = NIL THEN firstOutputBuffer _ b ELSE lastOutputBuffer.next _ b; lastOutputBuffer _ b; timeSendStarted _ ProcessorFace.GetClockPulses[]; }; numberOfExtraBuffer: CARDINAL = 3; bufferAccessHandle: BufferDefs.BufferAccessHandle; MaybeChangeNumberOfInputBuffers: PROC [increaseBuffers: BOOL] = { IF increaseBuffers THEN { IF bufferAccessHandle = NIL THEN { bufferAccessHandle _ BufferDefs.MakeBufferPool[numberOfExtraBuffer]; myNetwork.buffers _ myNetwork.buffers + numberOfExtraBuffer; }; } ELSE { IF bufferAccessHandle # NIL THEN { myNetwork.buffers _ myNetwork.buffers - numberOfExtraBuffer; BufferDefs.FreeBufferPool[bufferAccessHandle]; bufferAccessHandle _ NIL; }; }; }; AdjustLengtoOfD0EthernetInputQueue: PUBLIC PROC [n: CARDINAL] = { inputQueueLength _ n; }; CreateDefaultEthernetDrivers: PUBLIC PROC RETURNS [BOOL] = { deviceNumber: CARDINAL _ 0; etherDevice: DeviceHandle _ GetNextDevice[nullDeviceHandle]; IF GermSwap.switches[b] THEN RETURN[FALSE]; IF etherDevice = nullDeviceHandle THEN RETURN[FALSE]; WHILE etherDevice # nullDeviceHandle DO CreateAnEthernetDriver[etherDevice, deviceNumber]; etherDevice _ GetNextDevice[etherDevice]; deviceNumber _ deviceNumber + 1; ENDLOOP; RETURN[TRUE]; }; CreateAnEthernetDriver: PROC [ etherDevice: DeviceHandle, deviceNumber: CARDINAL] = { IF deviceNumber # 0 THEN { him: POINTER TO FRAME[EthernetDriver] _ NEW EthernetDriver; him.SetupEthernetDriver[etherDevice]; } ELSE SetupEthernetDriver[etherDevice]; }; SetupEthernetDriver: PROC [etherDevice: DeviceHandle] = { ether _ etherDevice; myEar _ me _ NSAddress.GetProcessorID[]; myNetwork.netNumber _ nullNetworkNumber; pleaseStop _ TRUE; myNetwork.buffers _ inputQueueLength; etherStats _ NEW[EthernetDriverStats.EtherStatsRep]; myNetwork.stats _ LOOPHOLE[etherStats]; PrincOpsUtils.LongZero[LOOPHOLE[etherStats], SIZE[EthernetDriverStats.EtherStatsRep]]; DriverDefs.AddDeviceToChain[@myNetwork, controlBlockSize]; }; ActivateDriver: PROC = { b: Buffer; IF ~pleaseStop THEN DriverDefs.Glitch[DriverAlreadyActive]; oneSecondOfPulses _ MicrosecondsToPulses[1000000]; getGarbage _ pleaseStop _ FALSE; TurnOff[ether]; AddCleanup[ether]; myEar _ me _ NSAddress.GetProcessorID[]; firstInputBuffer _ lastInputBuffer _ NIL; firstOutputBuffer _ lastOutputBuffer _ NIL; bufferAccessHandle _ NIL; inputBuffersInQueue _ 0; THROUGH [0..myNetwork.buffers) DO IF (b _ DriverDefs.GetInputBuffer[TRUE])#NIL THEN { inputBuffersInQueue _ inputBuffersInQueue + 1; IF CommFlags.doDebug AND Alignment[@b.encapsulation] # 3 THEN DriverDefs.Glitch[BufferMustBeAlmostQuadWordAligned]; IF CommFlags.doDebug AND Alignment[b.iocbChain] # 0 THEN DriverDefs.Glitch[IOCBMustBeQuadWordAligned]; IF CommFlags.doDebug AND PrincOpsUtils.HighHalf[b.iocbChain] # 0 THEN DriverDefs.Glitch[IOCBMustBeInFirstMDS]; b.device _ ethernet; IF firstInputBuffer = NIL THEN firstInputBuffer _ b; IF lastInputBuffer # NIL THEN lastInputBuffer.next _ b; lastInputBuffer _ b; }; ENDLOOP; [cv: inWait, mask: inInterruptMask] _ PrincOpsUtils.AllocateNakedCondition[]; Process.DisableTimeout[inWait]; [cv: outWait, mask: outInterruptMask] _ PrincOpsUtils.AllocateNakedCondition[]; Process.DisableTimeout[outWait]; SmashCSBs[]; inProcess _ FORK InInterrupt[]; outProcess _ FORK OutInterrupt[]; watcherProcess _ FORK Watcher[]; CreateCache[]; }; SetEthernetListener: PUBLIC ENTRY PROC [physicalOrder: CARDINAL, newHostNumber: NSAddress.HostNumber] RETURNS [success: BOOL] = { him: POINTER TO FRAME[EthernetDriver]; network: Network _ GetNthDeviceLikeMe[physicalOrder]; IF network = NIL THEN RETURN[FALSE]; him _ LOOPHOLE[PrincOpsUtils.GlobalFrame[LOOPHOLE[network.interrupt]]]; him.EthernetListenForHost[newHostNumber]; RETURN[TRUE]; }; EthernetListenForHost: PROC [newHostNumber: NSAddress.HostNumber] = { myEar _ newHostNumber; SmashCSBs[]; }; SetEthernetCollectGarbageToo: PUBLIC ENTRY PROC [physicalOrder: CARDINAL, collectGarbage: BOOL] RETURNS [success: BOOL] = { him: POINTER TO FRAME[EthernetDriver]; network: Network _ GetNthDeviceLikeMe[physicalOrder]; IF network = NIL THEN RETURN[FALSE]; him _ LOOPHOLE[PrincOpsUtils.GlobalFrame[LOOPHOLE[network.interrupt]]]; him.SetCollectGarbageToo[collectGarbage]; RETURN[TRUE]; }; SetCollectGarbageToo: PROC [collectGarbage: BOOL] = { getGarbage _ collectGarbage; }; GetNthDeviceLikeMe: PROC [physicalOrder: CARDINAL] RETURNS [net: Network] = { i: CARDINAL _ 0; net _ DriverDefs.GetDeviceChain[]; WHILE net # NIL DO IF net.device = myNetwork.device THEN IF (i _ i + 1) = physicalOrder THEN RETURN; net _ net.next; ENDLOOP; }; GetEthernetConfig: PUBLIC SAFE PROC RETURNS [instances: NAT _ 0] = TRUSTED { net: Network _ DriverDefs.GetDeviceChain[]; WHILE net # NIL DO IF net.device = myNetwork.device THEN instances _ instances + 1; net _ net.next; ENDLOOP; }; GetEthernetStats: PUBLIC SAFE PROC [instance: NAT] RETURNS [EthernetDriverStats.EtherStats _ NIL] = TRUSTED { net: Network _ GetNthDeviceLikeMe[instance+1]; IF net # NIL THEN RETURN [LOOPHOLE[net.stats]]; }; SmashCSBs: PROC = { b: Buffer; TurnOn[ ether, LOOPHOLE[myEar, NSAddress.ProcessorID], inInterruptMask, outInterruptMask, globalStatePtr]; FOR b _ firstInputBuffer, b.next UNTIL b = NIL DO QueueInput[ether, @b.encapsulation, b.length, b.iocbChain]; ENDLOOP; timeLastRecv _ ProcessorFace.GetClockPulses[]; }; DeleteDriver: PROC = { IF ether # GetNextDevice[nullDeviceHandle] THEN LoadState.SelfDestruct[]; }; DeactivateDriver: PROC = { b: Buffer; IF pleaseStop THEN DriverDefs.Glitch[DriverNotActive]; pleaseStop _ TRUE; KillInterruptRoutines[]; JOIN inProcess; JOIN outProcess; TurnOff[ether]; PrincOpsUtils.DeallocateNakedCondition[inWait]; PrincOpsUtils.DeallocateNakedCondition[outWait]; inWait _ outWait _ NIL; MaybeChangeNumberOfInputBuffers[FALSE]; KillDriverLocked[]; JOIN watcherProcess; RemoveCleanup[ether]; UNTIL firstInputBuffer = NIL DO b _ firstInputBuffer; firstInputBuffer _ b.next; ReturnFreeBuffer[b]; ENDLOOP; UNTIL firstOutputBuffer = NIL DO b _ firstOutputBuffer; firstOutputBuffer _ b.next; ReturnFreeBuffer[b]; ENDLOOP; myNetwork.netNumber _ nullNetworkNumber; DeleteCache[]; }; KillInterruptRoutines: ENTRY PROC = INLINE { NOTIFY inWait^; NOTIFY outWait^; }; KillDriverLocked: ENTRY PROC = INLINE { NOTIFY timer; }; OisAddr: TYPE = NSAddress.HostNumber; Ethernet1Addr: TYPE = PupTypes.PupHostID; AddressPair: TYPE = MACHINE DEPENDENT RECORD [ oisAddr: OisAddr, ethernet1Addr: Ethernet1Addr, filler: [0..377B]]; CacheEntry: TYPE = REF CacheObject; CacheObject: TYPE = MACHINE DEPENDENT RECORD [ nextLink: CacheEntry, addressPair: AddressPair, tries: CARDINAL, timeStamp: ProcessorFace.GreenwichMeanTime, status: CacheStatus, filler: [0..37777B]]; CacheStatus: TYPE = {new, pending, active, zombie}; translationRequest: CARDINAL = 10101B; translationResponse: CARDINAL = 7070B; cacheQueueHead: CacheEntry; broadCastPairEntry: CacheEntry; -- permanent myAddressPairEntry: CacheEntry; -- permanent retryLimit: CARDINAL = 10B; retryTime: LONG CARDINAL = 2; -- two seconds demonActiveTime: Process.Ticks _ Process.SecondsToTicks[1]; -- one second deactivateTime: LONG CARDINAL = 3*60; -- three minutes demonSleepTime: Process.Ticks _ Process.SecondsToTicks[60*5]; -- five minutes cacheEvent: CONDITION; demonRunning: BOOL; lastTranslationTime: ProcessorFace.GreenwichMeanTime; translate: PROC [Ethernet1Addr] RETURNS [BOOL, OisAddr] _ InactiveTranslate; receiveRequest: PROC [Buffer] _ InactiveReceiveAckOrRequest; receiveAck: PROC [Buffer] _ InactiveReceiveAckOrRequest; CreateCache: ENTRY PROC = INLINE { cacheQueueHead _ broadCastPairEntry _ myAddressPairEntry _ NIL; translate _ InactiveTranslate; receiveRequest _ InactiveReceiveAckOrRequest; receiveAck _ InactiveReceiveAckOrRequest; demonRunning _ FALSE; }; StartCache: ENTRY PROC [myEthernetOneAddr: CARDINAL] = INLINE { aP: AddressPair _ [NSAddress.broadcastHostNumber, PupTypes.allHosts, 0]; translate _ Translate; receiveRequest _ ReceiveRequest; receiveAck _ ReceiveAck; Process.SetTimeout[@cacheEvent, demonActiveTime]; broadCastPairEntry _ AddAddressPair[aP]; aP _ [me, [myNetwork.hostNumber _ myEthernetOneAddr], 0]; myAddressPairEntry _ AddAddressPair[aP]; demonRunning _ TRUE; Process.Detach[FORK Demon[]]; }; DeleteCache: PROC = INLINE { e: CacheEntry; DeleteCacheLocked: ENTRY PROC = INLINE { NOTIFY cacheEvent; }; WHILE demonRunning DO DeleteCacheLocked[]; Process.Yield[]; ENDLOOP; WHILE (cacheQueueHead # NIL) DO e _ cacheQueueHead; cacheQueueHead _ e.nextLink; ENDLOOP; }; depth: CARDINAL; FindEntry: INTERNAL PROC [ethernet1Addr: Ethernet1Addr] RETURNS [entry: CacheEntry] = { IF CommFlags.doStats THEN depth _ 0; entry _ cacheQueueHead; WHILE entry # NIL DO IF ethernet1Addr = entry.addressPair.ethernet1Addr THEN RETURN; entry _ entry.nextLink; IF CommFlags.doStats THEN depth _ depth + 1; ENDLOOP; }; AddEntry: INTERNAL PROC [entry: CacheEntry] = { entry.nextLink _ cacheQueueHead; cacheQueueHead _ entry; }; RemoveEntry: INTERNAL PROC [entry: CacheEntry] = { e, pred: CacheEntry; IF (pred _ cacheQueueHead) = entry THEN { cacheQueueHead _ cacheQueueHead.nextLink; RETURN; }; e _ pred.nextLink; WHILE e # NIL DO IF e = entry THEN { pred.nextLink _ entry.nextLink; RETURN; }; pred _ e; e _ pred.nextLink; ENDLOOP; ERROR; -- entry not found }; InactiveTranslate: PROC [ethernet1Addr: Ethernet1Addr] RETURNS [foundIt: BOOL, oisAddr: OisAddr] = { ethernetOneAddr: CARDINAL _ CommUtilDefs.GetEthernetHostNumber[]; StartCache[ethernetOneAddr]; [foundIt, oisAddr] _ Translate[ethernet1Addr]; }; Translate: ENTRY PROC [ethernet1Addr: Ethernet1Addr] RETURNS [foundIt: BOOL, oisAddr: OisAddr] = { e: CacheEntry; foundIt _ FALSE; lastTranslationTime _ ProcessorFace.GetGreenwichMeanTime[]; IF (e _ FindEntry[ethernet1Addr]) # NIL THEN { IF e # cacheQueueHead THEN { IF CommFlags.doStats THEN StatBump[cacheDepth, depth]; RemoveEntry[e]; AddEntry[e]; }; SELECT e.status FROM active => { foundIt _ TRUE; oisAddr _ e.addressPair.oisAddr; e.timeStamp _ lastTranslationTime; }; zombie => { e.status _ new; e.tries _ 0; e.timeStamp _ lastTranslationTime; NOTIFY cacheEvent; }; ENDCASE => NULL; } -- of found it ELSE { IF CommFlags.doStats THEN StatIncr[cacheFault]; e _ NEW[CacheObject]; e.status _ new; e.tries _ 0; e.timeStamp _ lastTranslationTime; e.addressPair _ [oisAddr:, ethernet1Addr: ethernet1Addr, filler:]; AddEntry[e]; NOTIFY cacheEvent; }; }; AddAddressPair: INTERNAL PROC [aP: AddressPair] RETURNS [e: CacheEntry] = { IF (e _ FindEntry[aP.ethernet1Addr]) = NIL THEN {e _ NEW[CacheObject]; AddEntry[e]}; e.addressPair _ aP; e.status _ active; e.timeStamp _ ProcessorFace.GetGreenwichMeanTime[]; }; DeallocateEntry: INTERNAL PROC [e: CacheEntry] = { IF (e = broadCastPairEntry) OR (e = myAddressPairEntry) THEN e.timeStamp _ ProcessorFace.GetGreenwichMeanTime[] ELSE { RemoveEntry[e]; --Heap.FreeNode[p: e];-- }; }; Demon: ENTRY PROC = { translationInactiveTime: LONG CARDINAL = 10*60; now: ProcessorFace.GreenwichMeanTime; t: LONG CARDINAL; e, nextE: CacheEntry; pendingEntries: BOOL; demonRunning _ TRUE; lastTranslationTime _ ProcessorFace.GetGreenwichMeanTime[]; Process.SetPriority[3]; UNTIL pleaseStop DO WAIT cacheEvent; IF (now _ ProcessorFace.GetGreenwichMeanTime[]) - lastTranslationTime > translationInactiveTime OR pleaseStop THEN EXIT; pendingEntries _ FALSE; e _ cacheQueueHead; WHILE (e # NIL) DO nextE _ e.nextLink; t _ now - e.timeStamp; SELECT e.status FROM active, zombie => { IF t > deactivateTime THEN DeallocateEntry[e]; }; pending => { pendingEntries _ TRUE; IF t > retryTime THEN { e.tries _ e.tries + 1; IF e.tries > retryLimit THEN { e.status _ zombie; IF CommFlags.doStats THEN StatIncr[unsuccessfulTranslation]; } ELSE { IF CommFlags.doStats THEN StatIncr[translationRetries]; SendRequest[e]; e.timeStamp _ ProcessorFace.GetGreenwichMeanTime[]; }; }; }; new => { pendingEntries _ TRUE; SendRequest[e]; e.status _ pending; e.timeStamp _ ProcessorFace.GetGreenwichMeanTime[]; }; ENDCASE => ERROR; e _ nextE; ENDLOOP; -- end of queue entries loop IF pendingEntries THEN Process.SetTimeout[@cacheEvent, demonActiveTime] ELSE Process.SetTimeout[@cacheEvent, demonSleepTime]; ENDLOOP; -- end of infinite loop receiveAck _ InactiveReceiveAckOrRequest; receiveRequest _ InactiveReceiveAckOrRequest; translate _ InactiveTranslate; e _ cacheQueueHead; cacheQueueHead _ myAddressPairEntry _ broadCastPairEntry _ NIL; WHILE e # NIL DO nextE _ e.nextLink; --Heap.FreeNode[p: e];-- e _ nextE; ENDLOOP; demonRunning _ FALSE; }; SendRequest: INTERNAL PROC [e: CacheEntry] = { b: Buffer; request: LONG POINTER TO AddressPair; IF (b _ DriverDefs.GetInputBuffer[TRUE]) # NIL THEN { b.encapsulation _ [ethernet[ ethernetDest: DriverTypes.ethernetBroadcastHost, ethernetSource: me, ethernetType: translation]]; b.length _ SIZE[DriverTypes.Encapsulation] + 2*SIZE[AddressPair] + 1; b.rawWords[0] _ translationRequest; request _ LOOPHOLE[@b.rawWords[1]]; request^ _ e.addressPair; request _ request + SIZE[AddressPair]; request^ _ myAddressPairEntry.addressPair; SendBufferInternal[b]; }; }; InactiveReceiveAckOrRequest: PROC [b: Buffer] = { b.requeueProcedure[b]; }; ReceiveAck: ENTRY PROC [b: Buffer] = { IF b.encapsulation.ethernetDest = myAddressPairEntry.addressPair.oisAddr THEN { receipt: LONG POINTER TO AddressPair _ LOOPHOLE[@b.rawWords[1]]; [] _ AddAddressPair[receipt^]; }; b.requeueProcedure[b]; }; ReceiveRequest: ENTRY PROC [b: Buffer] = { request, requesterAddr: LONG POINTER TO AddressPair; request _ LOOPHOLE[@b.rawWords[1]]; IF (myAddressPairEntry # NIL) AND (request.ethernet1Addr = myAddressPairEntry.addressPair.ethernet1Addr) THEN { IF CommFlags.doStats THEN StatIncr[requestsForMe]; requesterAddr _ request + SIZE[AddressPair]; [] _ AddAddressPair[requesterAddr^]; request.oisAddr _ myAddressPairEntry.addressPair.oisAddr; SendAck[request^, b.encapsulation.ethernetSource, b]; -- we lose ownership of b } ELSE b.requeueProcedure[b]; }; SendAck: INTERNAL PROC [aP: AddressPair, to: NSAddress.HostNumber, b: Buffer] = INLINE { response: LONG POINTER TO AddressPair; IF b # NIL THEN { b.encapsulation _ [ethernet[ethernetDest: to, ethernetSource: me, ethernetType: translation]]; b.length _ SIZE[DriverTypes.Encapsulation] + SIZE[AddressPair] + 1; b.rawWords[0] _ translationResponse; response _ LOOPHOLE[@b.rawWords[1]]; response^ _ aP; SendBufferInternal[b]; }; }; Process.SetTimeout[@timer, Process.MsecToTicks[1000]]; }. -- EthernetDriver September 5, 1980 1:36 AM By HGM; create from EthernetOneDriver. September 17, 1980 4:41 PM By BLyon; added myNetwork.buffers stuff. October 20, 1980 4:04 PM By BLyon; added SetYourHostNumber & myEar for Ois peeking. October 22, 1980 9:47 AM By BLyon; let the inputter collect garbage packets. November 6, 1980 6:18 PM By BLyon; records input time in buffer.time field. February 13, 1981 3:38 PM By BLyon; zombie translation entries are changed to new if needed AND Demon is immmediately started and never dies. February 24, 1981 3:28 PM By BLyon; undid February 13 - demon is created when needed and goes away when not needed AND changed WatcherNotify to SmashCSBs and to NOTIFY inWait^ and outWait^. όEthernetDriver.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. BLyon on: March 21, 1981 11:05 AM HGM on: March 14, 1981 9:10 PM Levin, June 15, 1983 11:01 am Russ Atkinson (RRA) February 19, 1985 7:44:06 pm PST EXPORTed TYPEs what address am I listening for (verses me, my real address) MachineIDTooBigForEthernet: PUBLIC ERROR = CODE; EthernetNetNumberScrambled: PUBLIC ERROR = CODE; OnlyTwoDriversArePossible: PUBLIC ERROR = CODE; Hot Procedures Rats, couldn't get a new buffer add new buffer to end of input chain forever until something interesting happens we compute the values each time around since the value of b can change if the watcher shoots down the output. We don't resend things that screwup In either case, an interrupt should be pending. 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 Check for lost output interrupt Check for stuck input Check for stuck output not MONITOR protected compair !! This happens if the transciever is unplugged Marker for translation failed for changing the number of buffers while running this should only be called from Boss No MONITOR PROTECTION here. COLD code, only used when turning things on+off Exported to EthernetDriverStats Returns how many instances are present for the Xerox product standard Ethernet. ... returns the EtherStats object for the given instance of the Xerox product standard Ethernet driver. Note that these numbers are not monitor protected, so be prepared for strange results every so often. NIL will be returned for invalid instances. Note that it is OK to do this, since we are always holding onto the REF through the global frame associated with the network driver. in case we turn it on after moving to another machine variables interface DeleteCacheLocked cleanup in case demon was never running Heap.FreeNode[p: e]; before conversion to REFs (ADB). -- put e at the head of the queue entry not found, so add a new one there are two entries that we do not want to throw out!! demon will die if no services are needed in ten minutes broadcast the translation request also send our addresses, so responder does not fault send it locks we now own buffer b we donot allow back door requests if we are not actively translating we now own buffer b locks we now own buffer b since the requester is probably going to talk to us, add his address before we take a fault send it initialization Κc˜codešœ™Kšœ Οmœ1™šžœžœ˜˜6K˜—Kšœžœžœ˜5Kšžœ žœ žœ˜4Kšœ˜—Kšœ˜—šžœ˜ šžœž˜šœ˜K˜8Kšžœžœ/˜HKšœ˜—šœ ˜ K˜.Kšžœžœ ˜9Kšœ˜—šžœ˜ K˜8Kšžœžœ"˜;Kšœ˜——Kšœ˜K˜——Kšœ#™#K˜+K˜#Kšžœ˜—Kšœ˜K˜—š œžœžœ žœ˜FKšžœ-˜3Kšœ˜K˜—š œžœ˜K˜ž˜BK˜—Kšžœžœ˜+Kšžœ˜—Kšœ˜K˜—š   œžœžœžœžœžœ˜2šžœ˜šœžœž˜K˜3——Kšœ˜K˜—š   œžœžœžœžœžœ˜3šžœ˜šœžœž˜K˜4——Kšœ˜K˜—š  œžœžœžœžœ˜2K˜ K˜3K˜š œžœžœžœ˜-K˜;Kšžœžœžœ˜3Kšžœ˜K˜K˜.Kšœ˜K˜—šžœ+ž˜2Kšœ ™ šžœ žœžœž˜3Kšœ žœ˜A—Kšžœ1žœžœžœ˜FKšžœ˜—Kšžœžœ˜ Kšœ˜K˜—Kš   œžœžœžœžœ ˜1K˜š  œžœžœžœ˜$Kšžœžœ#˜——šžœžœ ˜Kšžœ ˜Kšœ˜Kšžœžœ ˜Kšœ˜K˜——š œžœ+˜?Kšœ žœ˜K˜K˜,šžœ žœ˜˜K˜I—Kšœ!žœ˜AKšœ˜—šžœ˜˜˜ K˜8Kšœ™K˜——Kšœ˜—Kšœ˜K˜—š œžœ˜Kšœ4˜4˜K˜M—Kšœ%žœ˜EKšœ˜K˜—š  œžœ žœ˜CšžœžœžœŸ˜+Kšžœ%Ÿ˜D—K˜Kšžœ˜Kšœ˜K˜—š  œžœžœ˜&Kšžœ žœ$˜6šžœ;ž˜AKšœ&žœ˜0—šžœ ž˜šœ#ž˜%KšœBž˜F—KšœŸ:˜Ÿ˜MKšœ ž œ˜Kšœžœ˜K˜5šœ žœžœžœ ˜9K˜—Kšœžœ(˜Kšœ™K˜Kšžœžœ'žœ˜DKšœ'™'šžœžœž˜K˜K˜Kšœ8™8Kšžœ˜—Kšœ˜K˜—Kšœžœ˜š  œžœžœžœ˜WKšžœžœ ˜$K˜šžœ žœž˜Kšžœ1žœžœ˜?K˜Kšžœžœ˜,Kšžœ˜—Kšœ˜K˜—š œžœžœ˜/Kšœ;˜;K˜—š  œžœžœ˜2K˜šžœ!žœ˜)Kšœ)˜)Kšžœ˜Kšœ˜—K˜šžœžœž˜Kšžœ žœ#žœ˜>K˜ K˜Kšžœ˜—KšžœŸ˜K˜Kšœ˜K˜—š œžœžœ žœ˜dKšœžœ(˜AK˜K˜.Kšœ˜K˜—š   œžœžœžœ žœ˜bK˜Kšœ žœ˜K˜;šžœ"žœžœ˜.šžœžœ˜Kšœ™Kšžœžœ˜6K˜K˜ Kšœ˜—šžœ ž˜šœ ˜ Kšœ žœ˜K˜ K˜"Kšœ˜—šœ ˜ K˜K˜ K˜"Kšžœ ˜Kšœ˜—Kšžœžœ˜—KšœŸ˜—šžœ˜Kšœ!™!Kšžœžœ˜/Kšœžœ˜K˜K˜ K˜"K˜BK˜ Kšžœ ˜Kšœ˜—Kšœ˜K˜—š œžœžœžœ˜KKšžœ%žœžœžœ˜TK˜K˜K˜3Kšœ˜K˜—š œžœžœ˜2Kšœ8™8šžœžœž˜