<> <> <> <> <> <<>> DIRECTORY Basics USING [bytesPerWord], BasicTime USING [GetClockPulses, MicrosecondsToPulses, Pulses], Booting USING [RegisterProcs, RollbackProc, switches], CommBuffer USING [], Driver USING [AllocBuffer, AddNetwork, Buffer, bytesToRead, FreeBuffer, Network, NetworkObject, NoThankYou, recvPriority, sendPriority, watcherPriority, wordsInIocb], DriverType USING [Encapsulation, ethernetEncapsulationBytes, ethernetEncapsulationOffset, ethernetMinBytes, IsMulticastHost], EthernetFace USING [AddCleanup, controlBlockSize, GetNextDevice, GetStatusAndCollisions, GetStatusAndLength, GetPacketsMissed, Handle, hearSelf, IOCB, MarkKilled, nullHandle, QueueInput, QueueOutput, SetPromiscuous, Status, TurnOff, TurnOn], EthernetDriverStats USING [EtherStats, EtherStatsRep, MaxTries], GermSwap USING [], -- Needed by Booting.switches IP USING [nullAddress], NS USING [broadcastHost, GetThisHost, Host, unknownNet], PrincOpsUtils USING [AllocateNakedCondition, LongCopy], Process USING [Detach, DisableTimeout, GetPriority, MsecToTicks, Priority, SetPriority, SetTimeout], Pup USING [Host, nullHost, nullNet], SafeStorage USING [PinObject]; EthernetDriver: CEDAR MONITOR LOCKS data USING data: InstanceData IMPORTS BasicTime, Booting, Driver, DriverType, EthernetFace, NS, PrincOpsUtils, Process, SafeStorage EXPORTS CommBuffer = { Buffer: TYPE = Driver.Buffer; Network: TYPE = Driver.Network; Encapsulation: PUBLIC TYPE = DriverType.Encapsulation; Next: PROC [b: Buffer] RETURNS [Buffer] = TRUSTED INLINE { RETURN[LOOPHOLE[b.ovh.next]]; }; Data: PROC [b: Buffer] RETURNS [LONG POINTER] = TRUSTED INLINE { RETURN[@b.ovh.encap + DriverType.ethernetEncapsulationOffset]; }; Bytes: PROC [bytes: NAT] RETURNS [NAT] = TRUSTED INLINE { RETURN[bytes + DriverType.ethernetEncapsulationBytes]; }; BytesToRead: PROC RETURNS [NAT] = INLINE { RETURN[Bytes[Driver.bytesToRead]]; }; Iocb: PROC [b: Buffer] RETURNS [EthernetFace.IOCB] = TRUSTED INLINE { RETURN[LOOPHOLE[b.ovh.iocb]]; }; ElapsedPulses: PROC [startTime: BasicTime.Pulses] RETURNS [BasicTime.Pulses] = INLINE { RETURN[BasicTime.GetClockPulses[] - startTime]; }; InstanceData: TYPE = REF InstanceDataRep; InstanceDataRep: TYPE = MONITORED RECORD [ ether: EthernetFace.Handle, timer: CONDITION, inWait, outWait: LONG POINTER TO CONDITION _ NIL, inHickup, outHickup: CONDITION, inHickups, inBurps: INT _ 0, outHickups, outBurps: INT _ 0, inInterruptMask, outInterruptMask: WORD, firstInputBuffer, lastInputBuffer: Driver.Buffer, firstOutputBuffer, lastOutputBuffer: Driver.Buffer, timeLastRecv, timeSendStarted: BasicTime.Pulses, lastMissed: CARDINAL, numberOfInputBuffers: CARDINAL, me: NS.Host, promiscuous: BOOL, stats: EthernetDriverStats.EtherStats ]; defaultNumberOfInputBuffers: NAT _ 5; thirtySecondsOfPulses: BasicTime.Pulses _ BasicTime.MicrosecondsToPulses[30000000]; fiveSecondsOfPulses: BasicTime.Pulses _ BasicTime.MicrosecondsToPulses[5000000]; <> GetNSEncapsulation: PROC [network: Network, dest: NS.Host] RETURNS [encap: Encapsulation] = TRUSTED { data: InstanceData = NARROW[network.instanceData]; encap _ [ ethernet[ ethernetDest: dest, ethernetSource: data.me, ethernetType: ns ]]; }; Return: PROC [network: Network, buffer: Buffer, bytes: NAT] = { data: InstanceData = NARROW[network.instanceData]; buffer.ovh.encap.ethernetDest _ buffer.ovh.encap.ethernetSource; buffer.ovh.encap.ethernetSource _ data.me; Send[network, buffer, bytes]; }; unknownDest: LONG CARDINAL _ 0; Send: PROC [network: Network, buffer: Buffer, bytes: NAT] = { data: InstanceData = NARROW[network.instanceData]; priority: Process.Priority = Process.GetPriority[]; dest: NS.Host _ buffer.ovh.encap.ethernetDest; IF network.dead THEN RETURN; IF buffer.ovh.encap.ethernetType = translationFailed THEN { unknownDest _ unknownDest.SUCC; RETURN; }; IF priority # Driver.sendPriority THEN Process.SetPriority[Driver.sendPriority]; IF ~EthernetFace.hearSelf AND (dest = data.me OR DriverType.IsMulticastHost[dest] OR data.promiscuous) THEN { <> copy: Buffer _ Driver.AllocBuffer[]; words: NAT _ (Bytes[bytes]+Basics.bytesPerWord-1) / Basics.bytesPerWord; copy.ovh.network _ network; copy.ovh.next _ NIL; copy.ovh.direction _ none; TRUSTED { PrincOpsUtils.LongCopy[from: Data[buffer], nwords: words, to: Data[copy]]; }; SELECT copy.ovh.encap.ethernetType FROM ns => copy _ network.ns.recv[network, copy, bytes]; oldPup => copy _ network.pup.recv[network, copy, bytes]; oldPupTranslation => copy _ network.pup.recvTranslate[network, copy, bytes]; ip => copy _ network.ip.recv[network, copy, bytes]; arp => copy _ network.ip.recvTranslate[network, copy, bytes]; ENDCASE => copy _ network.other.recv[network, copy, bytes]; IF copy # NIL THEN Driver.FreeBuffer[copy]; }; buffer.ovh.network _ network; buffer.ovh.next _ NIL; SendInner[data, buffer, bytes]; IF priority # Driver.sendPriority THEN Process.SetPriority[priority]; }; <> SendInner: ENTRY PROC [data: InstanceData, b: Buffer, bytes: NAT] = { stats: EthernetDriverStats.EtherStats = data.stats; status: EthernetFace.Status; collisions: NAT; bytes _ MAX[bytes, DriverType.ethernetMinBytes]; EthernetFace.QueueOutput[data.ether, Data[b], Bytes[bytes], Iocb[b]]; IF data.firstOutputBuffer # NIL AND data.lastOutputBuffer.ovh.next # NIL THEN ERROR; IF data.firstOutputBuffer = NIL THEN data.firstOutputBuffer _ b ELSE data.lastOutputBuffer.ovh.next _ b; data.lastOutputBuffer _ b; data.timeSendStarted _ BasicTime.GetClockPulses[]; DO TRUSTED { WAIT data.outWait^; }; [status, collisions] _ EthernetFace.GetStatusAndCollisions[Iocb[b]]; IF status # pending THEN EXIT; data.outBurps _ data.outBurps.SUCC; BROADCAST data.outHickup; ENDLOOP; UNTIL b = data.firstOutputBuffer DO data.outHickups _ data.outHickups.SUCC; WAIT data.outHickup; ENDLOOP; data.firstOutputBuffer _ Next[data.firstOutputBuffer]; SELECT status FROM ok => { stats.packetsSent _ stats.packetsSent + 1; stats.wordsSent _ stats.wordsSent + bytes/Basics.bytesPerWord; stats.loadTable[collisions] _ stats.loadTable[collisions] + 1; }; ENDCASE => { SELECT status FROM tooManyCollisions => { tooMany: NAT = EthernetDriverStats.MaxTries; stats.loadTable[tooMany] _ stats.loadTable[tooMany] + 1; }; underrun => stats.overruns _ stats.overruns + 1; ENDCASE => stats.badSendStatus _ stats.badSendStatus + 1; }; BROADCAST data.outHickup; }; <> Recv: PROC [network: Network] = { data: InstanceData = NARROW[network.instanceData]; b: Buffer _ Driver.AllocBuffer[]; Process.SetPriority[Driver.recvPriority]; DO good: BOOL; bytes: NAT; b.ovh.network _ network; b.ovh.next _ NIL; b.ovh.direction _ none; [good, bytes] _ RecvInner[data, b]; b.ovh.next _ NIL; IF bytes < DriverType.ethernetEncapsulationBytes THEN good _ FALSE ELSE bytes _ bytes - DriverType.ethernetEncapsulationBytes; IF good THEN SELECT b.ovh.encap.ethernetType FROM ns => b _ network.ns.recv[network, b, bytes]; oldPup => b _ network.pup.recv[network, b, bytes]; oldPupTranslation => b _ network.pup.recvTranslate[network, b, bytes]; ip => b _ network.ip.recv[network, b, bytes]; arp => b _ network.ip.recvTranslate[network, b, bytes]; ENDCASE => b _ network.other.recv[network, b, bytes] ELSE b _ network.error.recv[network, b, bytes]; IF b = NIL THEN b _ Driver.AllocBuffer[]; ENDLOOP; }; <> RecvInner: ENTRY PROC [data: InstanceData, b: Buffer] RETURNS [good: BOOL, bytes: NAT] = { stats: EthernetDriverStats.EtherStats = data.stats; status: EthernetFace.Status; EthernetFace.QueueInput[data.ether, Data[b], BytesToRead[], Iocb[b]]; IF data.firstInputBuffer # NIL AND data.lastInputBuffer.ovh.next # NIL THEN ERROR; IF data.firstInputBuffer = NIL THEN data.firstInputBuffer _ b ELSE data.lastInputBuffer.ovh.next _ b; data.lastInputBuffer _ b; DO TRUSTED { WAIT data.inWait^; }; [status, bytes] _ EthernetFace.GetStatusAndLength[Iocb[b]]; IF status # pending THEN EXIT; data.inBurps _ data.inBurps.SUCC; BROADCAST data.inHickup; ENDLOOP; UNTIL b = data.firstInputBuffer DO data.inHickups _ data.inHickups.SUCC; WAIT data.inHickup; ENDLOOP; data.firstInputBuffer _ Next[data.firstInputBuffer]; data.timeLastRecv _ BasicTime.GetClockPulses[]; SELECT status FROM ok => { good _ TRUE; stats.packetsRecv _ stats.packetsRecv + 1; stats.wordsRecv _ stats.wordsRecv + bytes/Basics.bytesPerWord; }; ENDCASE => { good _ FALSE; stats.badRecvStatus _ stats.badRecvStatus + 1; IF status = overrun THEN stats.overruns _ stats.overruns + 1; }; BROADCAST data.inHickup; }; <> Rollback: Booting.RollbackProc = { <<[clientData: REF ANY]>> network: Network = NARROW[clientData]; data: InstanceData = NARROW[network.instanceData]; SmashCSB[data]; }; Watcher: PROC [network: Network] = { data: InstanceData = NARROW[network.instanceData]; stats: EthernetDriverStats.EtherStats = NARROW[network.stats]; missedIn: NAT _ 0; missedOut: NAT _ 0; inputNotifys: INT _ 0; outputNotifys: INT _ 0; fixupInputs: INT _ 0; shootDownOutputs: INT _ 0; Process.SetPriority[Driver.watcherPriority]; DO missed: CARDINAL _ EthernetFace.GetPacketsMissed[data.ether]; newMissed: CARDINAL _ (missed - data.lastMissed); <> IF newMissed < 10000 THEN stats.inputOff _ stats.inputOff + newMissed; data.lastMissed _ missed; <> FOR i: NAT IN [0..25) DO -- Check for lost input interrupts IF InputChainOK[data] THEN { missedIn _ 0; EXIT; }; REPEAT FINISHED => { missedIn _ missedIn.SUCC; inputNotifys _ inputNotifys.SUCC; WatcherNotifyInput[data]; }; ENDLOOP; FOR i: NAT IN [0..25) DO -- Check for lost output interrupts IF OutputChainOK[data] THEN { missedOut _ 0; EXIT; }; REPEAT FINISHED => { missedOut _ missedOut.SUCC; outputNotifys _ outputNotifys.SUCC; WatcherNotifyOutput[data]; }; ENDLOOP; IF (missedIn > 10) -- Check for input confusion OR (ElapsedPulses[data.timeLastRecv] > thirtySecondsOfPulses) THEN { missedIn _ 0; fixupInputs _ fixupInputs.SUCC; SmashCSB[data]; }; IF (missedOut > 10) -- Check for output confusion OR (data.firstOutputBuffer # NIL AND (ElapsedPulses[data.timeSendStarted] > fiveSecondsOfPulses)) THEN { missedOut _ 0; shootDownOutputs _ shootDownOutputs.SUCC; SmashCSB[data]; }; WatcherWait[data]; ENDLOOP; }; InputChainOK: ENTRY PROC [data: InstanceData] RETURNS [BOOL] = { status: EthernetFace.Status; IF data.firstInputBuffer = NIL THEN RETURN[TRUE]; status _ EthernetFace.GetStatusAndLength[Iocb[data.firstInputBuffer]].status; IF status = pending THEN RETURN[TRUE]; RETURN[FALSE]; }; OutputChainOK: ENTRY PROC [data: InstanceData] RETURNS [BOOL] = { status: EthernetFace.Status; IF data.firstOutputBuffer = NIL THEN RETURN[TRUE]; status _ EthernetFace.GetStatusAndCollisions[Iocb[data.firstOutputBuffer]].status; IF status = pending THEN RETURN[TRUE]; RETURN[FALSE]; }; WatcherNotifyInput: ENTRY PROC [data: InstanceData] = TRUSTED { NOTIFY data.inWait^; }; WatcherNotifyOutput: ENTRY PROC [data: InstanceData] = TRUSTED { NOTIFY data.outWait^; }; SmashCSB: ENTRY PROC [data: InstanceData] = { EthernetFace.TurnOff[data.ether]; FOR b: Buffer _ data.firstInputBuffer, Next[b] UNTIL b = NIL DO status: EthernetFace.Status; status _ EthernetFace.GetStatusAndLength[Iocb[b]].status; IF status = pending THEN EthernetFace.MarkKilled[Iocb[b]]; TRUSTED { NOTIFY data.inWait^; }; ENDLOOP; FOR b: Buffer _ data.firstOutputBuffer, Next[b] UNTIL b = NIL DO status: EthernetFace.Status; status _ EthernetFace.GetStatusAndCollisions[Iocb[b]].status; IF status = pending THEN EthernetFace.MarkKilled[Iocb[b]]; TRUSTED { NOTIFY data.outWait^; }; ENDLOOP; BROADCAST data.inHickup; BROADCAST data.outHickup; EthernetFace.TurnOn[data.ether, data.inInterruptMask, data.outInterruptMask]; data.lastMissed _ EthernetFace.GetPacketsMissed[data.ether]; data.timeLastRecv _ BasicTime.GetClockPulses[]; IF data.promiscuous THEN EthernetFace.SetPromiscuous[data.ether, TRUE]; }; WatcherWait: ENTRY PROC [data: InstanceData] = { WAIT data.timer; }; <> CreateDefaultDrivers: PROC = { nullHandle: EthernetFace.Handle = EthernetFace.nullHandle; ether: EthernetFace.Handle _ EthernetFace.GetNextDevice[nullHandle]; WHILE ether # nullHandle DO me: NS.Host _ NS.GetThisHost[]; stats: EthernetDriverStats.EtherStats _ NEW[EthernetDriverStats.EtherStatsRep]; data: InstanceData _ NEW[InstanceDataRep _ [ ether: ether, timer: , inWait: NIL, outWait: NIL, inHickup: , outHickup: , inHickups: 0, inBurps: 0, outHickups: 0, outBurps: 0, inInterruptMask: 0, outInterruptMask: 0, firstInputBuffer: NIL, lastInputBuffer: NIL, firstOutputBuffer: NIL, lastOutputBuffer: NIL, timeLastRecv: BasicTime.GetClockPulses[], timeSendStarted: BasicTime.GetClockPulses[], lastMissed: 0, numberOfInputBuffers: defaultNumberOfInputBuffers, me: me, promiscuous: FALSE, stats: stats ]]; network: Network _ NEW [Driver.NetworkObject _ [ next: NIL, ip: [ host: IP.nullAddress, getEncapsulation: NIL, send: Send, return: Return, recv: Driver.NoThankYou, sendTranslate: Send, recvTranslate: Driver.NoThankYou, translation: NIL ], ns: [ net: NS.unknownNet, getEncapsulation: GetNSEncapsulation, send: Send, return: Return, recv: Driver.NoThankYou, sendTranslate: Send, recvTranslate: Driver.NoThankYou, translation: NIL ], pup: [ net: Pup.nullNet, host: Pup.nullHost, getEncapsulation: NIL, send: Send, return: Return, recv: Driver.NoThankYou, sendTranslate: Send, recvTranslate: Driver.NoThankYou, translation: NIL ], other: [ netHostOther: NIL, getEncapsulation: NIL, send: Send, return: Return, recv: Driver.NoThankYou, sendTranslate: Send, recvTranslate: Driver.NoThankYou, translation: NIL ], raw: [send: Send], error: [recv: Driver.NoThankYou], setPromiscuous: SetPromiscuous, isThisForMe: IsThisForMe, toBroadcast: ToBroadcast, moreBuffers: MoreBuffers, interceptor: NIL, stats: data.stats, instanceData: data, type: ethernet, speed: 10000000, index: 0, hearSelf: EthernetFace.hearSelf, recvSick: FALSE, sendSick: FALSE, dead: FALSE ]]; SafeStorage.PinObject[stats]; -- Not normally needed, but it might help EtherSwapping SafeStorage.PinObject[data]; SafeStorage.PinObject[network]; EthernetFace.TurnOff[ether]; EthernetFace.AddCleanup[ether]; TRUSTED { Process.SetTimeout[@data.timer, Process.MsecToTicks[1000]]; Process.DisableTimeout[@data.inHickup]; Process.DisableTimeout[@data.outHickup]; [cv: data.inWait, mask: data.inInterruptMask] _ PrincOpsUtils.AllocateNakedCondition[]; InitCond[data, data.inWait]; [cv: data.outWait, mask: data.outInterruptMask] _ PrincOpsUtils.AllocateNakedCondition[]; InitCond[data, data.outWait]; }; EthernetFace.TurnOn[data.ether, data.inInterruptMask, data.outInterruptMask]; data.lastMissed _ EthernetFace.GetPacketsMissed[data.ether]; FOR i: NAT IN [0..data.numberOfInputBuffers) DO TRUSTED { Process.Detach[FORK Recv[network]]; }; ENDLOOP; TRUSTED { Process.Detach[FORK Watcher[network]]; }; Booting.RegisterProcs[r: Rollback, clientData: network]; Driver.AddNetwork[network]; ether _ EthernetFace.GetNextDevice[ether]; ENDLOOP; }; InitCond: ENTRY PROC [data: InstanceData, cond: LONG POINTER TO CONDITION] = TRUSTED { Process.SetTimeout[cond, 1]; WAIT cond^; -- Eat up any wakeups waiting Process.DisableTimeout[cond]; }; <> <<>> SetPromiscuous: PROC [network: Network, promiscuous: BOOL] = { data: InstanceData = NARROW[network.instanceData]; EthernetFace.SetPromiscuous[data.ether, promiscuous]; data.promiscuous _ promiscuous; }; skipTheBugs: INT _ 0; IsThisForMe: PROC [network: Network, buffer: Buffer] RETURNS [yes: BOOL] = { data: InstanceData = NARROW[network.instanceData]; dest: NS.Host _ buffer.ovh.encap.ethernetDest; IF dest = data.me THEN RETURN[TRUE]; IF dest = NS.broadcastHost THEN RETURN[TRUE]; IF DriverType.IsMulticastHost[dest] THEN skipTheBugs _ skipTheBugs.SUCC; <> RETURN[FALSE]; }; ToBroadcast: PROC [network: Network, buffer: Buffer] RETURNS [yes: BOOL] = { dest: NS.Host _ buffer.ovh.encap.ethernetDest; IF DriverType.IsMulticastHost[dest] THEN RETURN[TRUE]; RETURN[FALSE]; }; MoreBuffers: PROC [network: Network, total: NAT] = { data: InstanceData = NARROW[network.instanceData]; IF total < data.numberOfInputBuffers THEN RETURN; FOR i: NAT IN [data.numberOfInputBuffers..total) DO TRUSTED { Process.Detach[FORK Recv[network]]; }; ENDLOOP; data.numberOfInputBuffers _ total; }; <> <<>> IF Driver.wordsInIocb < EthernetFace.controlBlockSize THEN ERROR; IF ~Booting.switches[b] THEN CreateDefaultDrivers[]; }. If you are interested in the interrupt tangle, look at the comments at the end of EthernetOneDriver.mesa.