<> <> <> <> <> <> DIRECTORY Basics USING [BITAND, LongNumber], BufferDefs, CommFlags USING [doDebug, doStats], DriverDefs USING [GetGiantVector, GiantVector, Glitch, GetDeviceChain, GetInputBuffer, Network, NetworkObject, AddDeviceToChain, PutOnGlobalDoneQueue, PutOnGlobalInputQueue], DriverTypes USING [Byte, ethernetEncapsulationOffset, ethernetEncapsulationBytes], EthernetOneFace, EthernetDriverStats, GermSwap USING [switches], LoadState USING [SelfDestruct], PrincOpsUtils USING [AllocateNakedCondition, DeallocateNakedCondition, GlobalFrame, HighHalf, LongCopy, LowHalf, LongZero], Process USING [Detach, DisableTimeout, MsecToTicks, SecondsToTicks, SetPriority, SetTimeout, Ticks, Yield], ProcessorFace USING [GetGreenwichMeanTime, GetClockPulses, GreenwichMeanTime, microsecondsPerHundredPulses], PupTypes USING [allHosts, PupErrorCode, PupHostID], RPCPrivate USING[ rpcSocket ], StatsDefs USING [StatBump, StatIncr, StatCounterIndex], SpecialCommunication USING [], NSAddress USING [broadcastHostNumber, ProcessorID, HostNumber, GetProcessorID, nullNetworkNumber]; EthernetOneDriver: MONITOR IMPORTS Basics, BufferDefs, DriverDefs, GermSwap, StatsDefs, EthernetOneFace, LoadState, PrincOpsUtils, Process, ProcessorFace, NSAddress EXPORTS BufferDefs, DriverDefs, EthernetDriverStats, SpecialCommunication, RPCPrivate SHARES BufferDefs, DriverTypes, NSAddress = { OPEN StatsDefs, BufferDefs, EthernetOneFace, NSAddress; <> doStats: BOOL = CommFlags.doStats AND FALSE; <> Network: PUBLIC TYPE = DriverDefs.Network; ethernetEncapsulationOffset: CARDINAL = DriverTypes.ethernetEncapsulationOffset; ethernetEncapsulationBytes: CARDINAL = DriverTypes.ethernetEncapsulationBytes; ether: DeviceHandle; 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; fiveSecondsOfPulses: Pulses; fiveHalfSecondsOfPulses: Pulses; oneSecondOfPulses: Pulses; inputQueueLength: CARDINAL _ 1; inputBuffersInQueue: CARDINAL; --RPC-- loanedToRPC: CARDINAL; -- this number from inputBuffersInQueue aren't really <> --RPC-- systemBufferSize: CARDINAL _ 0; 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: 3000, -- in kiloBits/sec buffers:, spare:, device: ethernetOne, index:, netNumber: nullNetworkNumber, hostNumber:, next: NIL, pupStats: NIL, stats: NIL]; myEar: CARDINAL; <> getGarbage: BOOL _ FALSE; -- when true, we deliver any packet FunnyRetransmissionMask: PUBLIC ERROR = CODE; MachineIDTooBigForEthernet: PUBLIC ERROR = CODE; DriverNotActive: PUBLIC ERROR = CODE; DriverAlreadyActive: PUBLIC ERROR = CODE; EthernetNetNumberScrambled: PUBLIC ERROR = CODE; CantMakImageWhileEtherentDriverIsActive: PUBLIC ERROR = CODE; OnlyTwoDriversArePossible: PUBLIC ERROR = CODE; BufferMustBeQuadWordAligned: PUBLIC ERROR = CODE; IOCBMustBeQuadWordAligned: PUBLIC ERROR = CODE; IOCBMustBeInFirstMDS: PUBLIC ERROR = CODE; etherStats: EthernetDriverStats.EtherStats _ NIL; Alignment: PROC [lp: LONG POINTER] RETURNS [CARDINAL] = INLINE { RETURN [Basics.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 doStats AND status # pending THEN StatIncr[statEtherInterruptDuringInterrupt]; UNTIL pleaseStop OR status # pending DO WAIT inWait; status _ GetStatus[this.iocbChain]; IF doStats AND status = pending THEN StatIncr[statEtherMissingStatus]; ENDLOOP; IF 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 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 { allocateAnother: BOOL; 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 doStats THEN { StatIncr[statEtherPacketsReceived]; StatBump[statEtherWordsReceived, this.length]; }; <> IF EnqueueRecvd # NIL AND this.encapsulation.ethernetOneType = pup AND 2*this.length >= this.pupLength + ethernetEncapsulationBytes AND this.dest.socket = RPCPrivate.rpcSocket THEN { this.next _ NIL -- assumed by SendBuffer --; IF EnqueueRecvd[LOOPHOLE[this,PupBuffer]] THEN { new _ NIL; allocateAnother _ (loanedToRPC+2 > inputBuffersInQueue) } ELSE { new _ this; allocateAnother _ FALSE; new.length _ systemBufferSize }; } ELSE { DriverDefs.PutOnGlobalInputQueue[this]; allocateAnother _ TRUE }; IF allocateAnother THEN { IF (new _ DriverDefs.GetInputBuffer[]) = NIL THEN { <> inputBuffersInQueue _ inputBuffersInQueue - 1; NOTIFY timer; etherStats.inputOff _ etherStats.inputOff + 1; IF doStats THEN StatIncr[statEtherEmptyFreeQueue]; }; -- cant get new input buffer clause } ELSE -- we may have loaned the buffer to RPC; don't replace it -- IF new = NIL THEN loanedToRPC _ loanedToRPC + 1; } -- accept buffer clause ELSE { new _ this; -- Some kind of error, recycle this buffer }; -- reject buffer clause <> IF new # NIL THEN { new.device _ ethernetOne; QueueInput[ ether, @new.encapsulation + ethernetEncapsulationOffset, new.length - ethernetEncapsulationOffset, new.iocbChain]; IF systemBufferSize = 0 THEN systemBufferSize _ new.length; 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; etherStats.loadTable[tries] _ etherStats.loadTable[tries] + 1; IF 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[EthernetDriverStats.MaxTries] _ etherStats.loadTable[EthernetDriverStats.MaxTries] + 1; IF doStats THEN StatIncr[statEtherSendsCollisionLoadOverflow]; }; underrun => { etherStats.overruns _ etherStats.overruns + 1; IF doStats THEN StatIncr[statEtherSendOverrun]; }; ENDCASE => { etherStats.badSendStatus _ etherStats.badSendStatus + 1; IF doStats THEN StatIncr[statEtherSendBadStatus]; }; }; <> firstOutputBuffer _ firstOutputBuffer.next; DriverDefs.PutOnGlobalDoneQueue[b]; ENDLOOP; }; GetElapsedPulses: PROC [startTime: Pulses] RETURNS [Pulses] = INLINE { RETURN[ProcessorFace.GetClockPulses[] - startTime]; }; Watcher: PROC = { 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 + ethernetEncapsulationOffset, b.length - ethernetEncapsulationOffset, b.iocbChain]; IF systemBufferSize = 0 THEN systemBufferSize _ b.length; 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 _ ethernetOne; 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 doStats THEN StatIncr[statEtherLostInterrupts]; SmashCSBs[]; -- this will leave output dangling }; FixupInput: ENTRY PROC = INLINE { IF 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 doStats THEN StatIncr[statPacketsStuckInOutput]; ENDLOOP; SmashCSBs[]; }; EnqueueRecvd: PROC[BufferDefs.PupBuffer]RETURNS[BOOL] _ NIL; GetRPCPackets: PUBLIC--RPCPrivate-- ENTRY PROC[ p: PROC[BufferDefs.PupBuffer]RETURNS[BOOL] ] = { EnqueueRecvd _ p }; ReturnBuffer: PUBLIC--RPCPrivate-- ENTRY PROC[c: PupBuffer] = { b: Buffer = c; IF loanedToRPC > 0 THEN { QueueInput[ ether, @b.encapsulation + ethernetEncapsulationOffset, (b.length _ systemBufferSize) - ethernetEncapsulationOffset, b.iocbChain]; IF firstInputBuffer = NIL THEN firstInputBuffer _ b ELSE lastInputBuffer.next _ b; lastInputBuffer _ b; loanedToRPC _ loanedToRPC-1; } ELSE ReturnFreeBuffer[b]; }; DecapsulateBuffer: PROC [b: Buffer] RETURNS [BufferType] = { SELECT b.encapsulation.ethernetOneType FROM pup => { IF 2*b.length < b.pupLength + ethernetEncapsulationBytes THEN { IF doStats THEN StatIncr[statPupsDiscarded]; RETURN[rejected]; }; RETURN[pup]; }; ois => { IF 2*b.length < b.ois.pktLength + ethernetEncapsulationBytes THEN { IF 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] = { b.encapsulation _ [ethernetOne[ etherSpare1:, etherSpare2:, etherSpare3:, etherSpare4:, etherSpare5:, translationWorked: TRUE, ethernetOneDest: destination, ethernetOneSource: myNetwork.hostNumber, ethernetOneType: pup]]; b.length _ (b.pupLength + 1 + ethernetEncapsulationBytes)/2; }; EncapsulateOis: PROC [ b: OisBuffer, destination: NSAddress.HostNumber] = { foundIt: BOOL; ethernetAddr: Ethernet1Addr; [foundIt, ethernetAddr] _ translate[destination]; IF foundIt THEN { b.encapsulation _ [ethernetOne[ etherSpare1:, etherSpare2:, etherSpare3:, etherSpare4:, etherSpare5:, translationWorked: TRUE, ethernetOneDest: ethernetAddr, ethernetOneSource: myNetwork.hostNumber, ethernetOneType: ois]]; b.length _ (b.ois.pktLength + 1 + ethernetEncapsulationBytes)/2; } ELSE { b.encapsulation _ [ethernetOne[ etherSpare1:, etherSpare2:, etherSpare3:, etherSpare4:, etherSpare5:, translationWorked: FALSE, ethernetOneDest:, ethernetOneSource:, ethernetOneType:]]; }; }; 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.translationWorked THEN { DriverDefs.PutOnGlobalDoneQueue[b]; RETURN; }; IF ~hearSelf AND (b.encapsulation.ethernetOneDest = myNetwork.hostNumber OR b.encapsulation.ethernetOneDest = PupTypes.allHosts) THEN { -- sending to ourself, copy it over since we can't hear it copy: Buffer _ DriverDefs.GetInputBuffer[]; IF copy # NIL THEN { copy.device _ ethernetOne; PrincOpsUtils.LongCopy[ from: @b.encapsulation + ethernetEncapsulationOffset, nwords: b.length, to: @copy.encapsulation + ethernetEncapsulationOffset]; copy.length _ b.length; copy.network _ LONG[@myNetwork]; -- LONG because of Mokelumne compiler bug IF doStats THEN StatIncr[statEtherPacketsLocal]; IF doStats THEN StatBump[statEtherWordsLocal, b.length]; DriverDefs.PutOnGlobalInputQueue[copy]; } ELSE IF doStats THEN StatIncr[statEtherEmptyFreeQueue]; }; SendBufferInternal[b]; }; SendBufferInternal: INTERNAL PROC [b: Buffer] = { b.device _ ethernetOne; QueueOutput[ ether, @b.encapsulation + ethernetEncapsulationOffset, b.length, b.iocbChain]; IF doStats THEN StatIncr[statEtherPacketsSent]; IF 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; }; CreateDefaultEthernetOneDrivers: PUBLIC PROC RETURNS [BOOL] = { deviceNumber: CARDINAL _ 0; etherDevice: DeviceHandle _ GetNextDevice[nullDeviceHandle]; IF GermSwap.switches[a] 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]; }; CreateAnEthernetOneDriver: PROC [ etherDevice: DeviceHandle, deviceNumber: CARDINAL] = { IF deviceNumber # 0 THEN { him: POINTER TO FRAME[EthernetOneDriver] _ NEW EthernetOneDriver; him.SetupEthernetOneDriver[etherDevice]; } ELSE SetupEthernetOneDriver[etherDevice]; }; SetupEthernetOneDriver: PROC [etherDevice: DeviceHandle] = { net, host: DriverTypes.Byte; ether _ etherDevice; [net, host] _ GetEthernet1Address[ether]; IF net # 0 AND net # myNetwork.netNumber.b AND myNetwork.netNumber # nullNetworkNumber THEN DriverDefs.Glitch[EthernetNetNumberScrambled]; IF myNetwork.netNumber = nullNetworkNumber THEN myNetwork.netNumber _ [a: 0, b: net]; 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; net, host: DriverTypes.Byte; IF ~pleaseStop THEN DriverDefs.Glitch[DriverAlreadyActive]; fiveSecondsOfPulses _ MicrosecondsToPulses[5000000]; fiveHalfSecondsOfPulses _ MicrosecondsToPulses[2500000]; oneSecondOfPulses _ MicrosecondsToPulses[1000000]; [net, host] _ GetEthernet1Address[ether]; getGarbage _ pleaseStop _ FALSE; TurnOff[ether]; AddCleanup[ether]; firstInputBuffer _ lastInputBuffer _ NIL; firstOutputBuffer _ lastOutputBuffer _ NIL; bufferAccessHandle _ NIL; myEar _ myNetwork.hostNumber _ host; inputBuffersInQueue _ loanedToRPC _ 0; THROUGH [0..myNetwork.buffers) DO IF (b _ DriverDefs.GetInputBuffer[TRUE])#NIL THEN { inputBuffersInQueue _ inputBuffersInQueue + 1; IF CommFlags.doDebug AND Alignment[@b.encapsulation + ethernetEncapsulationOffset] # 0 THEN DriverDefs.Glitch[BufferMustBeQuadWordAligned]; 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 _ ethernetOne; 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[]; }; SetEthernetOneListener: PUBLIC ENTRY PROC [physicalOrder: CARDINAL, newHostNumber: CARDINAL] RETURNS [success: BOOL] = { him: POINTER TO FRAME[EthernetOneDriver]; network: Network _ GetNthDeviceLikeMe[physicalOrder]; IF network = NIL THEN RETURN[FALSE]; him _ LOOPHOLE[PrincOpsUtils.GlobalFrame[LOOPHOLE[network.interrupt]]]; him.EthernetOneListenForHost[newHostNumber]; RETURN[TRUE]; }; EthernetOneListenForHost: PROC [newHostNumber: CARDINAL] = { myEar _ newHostNumber; SmashCSBs[]; }; 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; }; <> GetEthernetOneConfig: 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; }; GetEthernetOneStats: PUBLIC SAFE PROC [instance: NAT] RETURNS [EthernetDriverStats.EtherStats _ NIL] = TRUSTED { <<... returns the EtherStats object for the given instance of the Xerox research 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.>> net: Network _ GetNthDeviceLikeMe[instance+1]; IF net # NIL THEN RETURN [LOOPHOLE[net.stats]]; <> }; <<>> SetEthernetOneCollectGarbageToo: PUBLIC ENTRY PROC [physicalOrder: CARDINAL, collectGarbage: BOOL] RETURNS [success: BOOL] = { him: POINTER TO FRAME[EthernetOneDriver]; 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; }; SmashCSBs: PROC = { b: Buffer; TurnOn[ether, myEar, inInterruptMask, outInterruptMask, globalStatePtr]; FOR b _ firstInputBuffer, b.next UNTIL b = NIL DO QueueInput[ ether, @b.encapsulation + ethernetEncapsulationOffset, b.length - ethernetEncapsulationOffset, 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; DeleteCache[]; }; KillInterruptRoutines: ENTRY PROC = INLINE { NOTIFY inWait^; NOTIFY outWait^; }; KillDriverLocked: ENTRY PROC = INLINE { NOTIFY timer; }; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * >> <> <> <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *>> <> OisAddr: TYPE = NSAddress.HostNumber; Ethernet1Addr: TYPE = DriverTypes.Byte; 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; receiveAck: PROC [b: Buffer] _ InactiveReceiveAck; translate: PROC [OisAddr] RETURNS [BOOL, Ethernet1Addr] _ InactiveTranslate; etherHost: Ethernet1Addr; oisHost: OisAddr; <> CreateCache: ENTRY PROC = INLINE { cacheQueueHead _ broadCastPairEntry _ myAddressPairEntry _ NIL; translate _ InactiveTranslate; receiveAck _ InactiveReceiveAck; demonRunning _ FALSE; [, etherHost] _ GetEthernet1Address[ether]; oisHost _ NSAddress.GetProcessorID[]; }; StartCache: ENTRY PROC = INLINE { etherHost: Ethernet1Addr; oisHost: OisAddr _ NSAddress.GetProcessorID[]; aP: AddressPair _ [NSAddress.broadcastHostNumber, PupTypes.allHosts, 0]; [, etherHost] _ GetEthernet1Address[ether]; translate _ Translate; receiveAck _ ReceiveAck; Process.SetTimeout[@cacheEvent, demonActiveTime]; broadCastPairEntry _ AddAddressPair[aP]; aP _ [oisHost, etherHost, 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 [oisAddr: OisAddr] RETURNS [entry: CacheEntry] = { IF doStats THEN depth _ 0; entry _ cacheQueueHead; WHILE entry # NIL DO IF oisAddr = entry.addressPair.oisAddr THEN RETURN; entry _ entry.nextLink; IF 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 [oisAddr: OisAddr] RETURNS [foundIt: BOOL, ethernet1Addr: Ethernet1Addr] = { StartCache[]; [foundIt, ethernet1Addr] _ Translate[oisAddr]; }; <> Translate: ENTRY PROC [oisAddr: OisAddr] RETURNS [foundIt: BOOL, ethernet1Addr: Ethernet1Addr] = { e: CacheEntry; foundIt _ FALSE; lastTranslationTime _ ProcessorFace.GetGreenwichMeanTime[]; IF (e _ FindEntry[oisAddr]) # NIL THEN { IF e # cacheQueueHead THEN { <> IF doStats THEN StatBump[cacheDepth, depth]; RemoveEntry[e]; AddEntry[e]; }; SELECT e.status FROM active => { foundIt _ TRUE; ethernet1Addr _ e.addressPair.ethernet1Addr; e.timeStamp _ lastTranslationTime; }; zombie => { e.status _ new; e.tries _ 0; e.timeStamp _ lastTranslationTime; NOTIFY cacheEvent; }; ENDCASE => NULL; } -- of found it ELSE { <> IF doStats THEN StatIncr[cacheFault]; e _ NEW[CacheObject]; e.status _ new; e.tries _ 0; e.timeStamp _ lastTranslationTime; e.addressPair _ [oisAddr: oisAddr, ethernet1Addr:, filler:]; AddEntry[e]; NOTIFY cacheEvent; }; }; <> AddAddressPair: INTERNAL PROC [aP: AddressPair] RETURNS [e: CacheEntry] = { IF (e _ FindEntry[aP.oisAddr]) = 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 doStats THEN StatIncr[unsuccessfulTranslation]; } ELSE { IF 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 _ InactiveReceiveAck; 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[]) # NIL THEN { <> b.encapsulation _ [ ethernetOne[ etherSpare1:, etherSpare2:, etherSpare3:, etherSpare4:, etherSpare5:, translationWorked:, ethernetOneDest: PupTypes.allHosts, ethernetOneSource: myNetwork.hostNumber, ethernetOneType: translation]]; b.length _ (1 + ethernetEncapsulationBytes)/2 + 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]; }; }; <> <> InactiveReceiveAck: PROC [b: Buffer] = { b.requeueProcedure[b]; }; ReceiveAck: ENTRY PROC [b: Buffer] = { IF b.encapsulation.ethernetOneDest = myAddressPairEntry.addressPair.ethernet1Addr 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 request.oisAddr = oisHost THEN { <> requesterAddr _ request + SIZE[AddressPair]; [] _ AddAddressPair[requesterAddr^]; IF doStats THEN StatIncr[requestsForMe]; request.ethernet1Addr _ etherHost; SendAck[request^, b.encapsulation.ethernetOneSource, b]; -- we lose ownership of b } ELSE b.requeueProcedure[b]; }; <> <> SendAck: INTERNAL PROC [aP: AddressPair, to: DriverTypes.Byte, b: Buffer] = INLINE { response: LONG POINTER TO AddressPair; IF b # NIL THEN { b.encapsulation _ [ethernetOne[ etherSpare1:, etherSpare2:, etherSpare3:, etherSpare4:, etherSpare5:, translationWorked:, ethernetOneDest: to, ethernetOneSource: myNetwork.hostNumber, ethernetOneType: translation]]; b.length _ (1 + ethernetEncapsulationBytes)/2 + SIZE[AddressPair] + 1; b.rawWords[0] _ translationResponse; response _ LOOPHOLE[@b.rawWords[1]]; response^ _ aP; <> SendBufferInternal[b]; }; }; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *>> <> <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * >> <> Process.SetTimeout[@timer, Process.MsecToTicks[1000]]; IF CommFlags.doDebug THEN { debugPointer: LONG POINTER TO DriverDefs.GiantVector _ DriverDefs.GetGiantVector[]; debugPointer.currentInputBuffer _ @firstInputBuffer; debugPointer.nextInputBuffer _ @lastInputBuffer; debugPointer.currentOutputBuffer _ @firstOutputBuffer; }; }. -- EthernetOneDriver February 23, 1980 9:10 PM By forrest; Change PrincOpsUtilsDefs to PrincOpsUtils, and move EtherStatsRep Type from AltoEthernetDefs (RIP) to inside here. April 18, 1980 3:00 AM By Murray; Face changes (and other Pilotization). May 19, 1980 10:20 AM By BLyon; DisableTimeout, SetTimeout, MsecToTicks, SetPriority imported from Process instead of CommUtilDefs. May 27, 1980 11:44 AM By BLyon; Added ethernet1 translation uglyness. August 1, 1980 4:24 PM By BLyon; 8bit->32bit net number Kludge in CreateDefaultEthernetsDrivers and use NSAddress.nullNetworkNumber instead of 0. August 13, 1980 1:13 PM By BLyon; added InputChainOK and OutputChainOK to insure the bodies of the two loops in Watcher are protected by the monitor lock. August 25, 1980 1:50 PM By BLyon; multiple ethernets now possible. August 28, 1980 12:50 PM By McJones; converted to PrincOpsUtils.AllocateNakedCondition. September 5, 1980 4:44 PM By HGM; use hearSelf, a switch to ignore boards even if they are there. 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:57 AM By BLyon; let the inputter collect garbage packets. November 6, 1980 6:34 PM By BLyon; Records input time in buffer.time. November 11, 1980 10:33 AM By BLyon; No longer smashes myNetwork.netNumber in DeactivateDriver. February 13, 1981 3:46 PM By BLyon; zombie translation entries can be changed to new if needed AND Demon is started immediately and never dies AND stores the ethernetOneHostNumber. 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^.