-- File: AltoEthernetDriver.mesa, Last Edit: -- MAS August 20, 1980 12:59 PM -- HGM August 23, 1980 7:14 PM -- Copyright Xerox Corporation 1979, 1980 DIRECTORY InlineDefs: FROM "InlineDefs" USING [BITSHIFT, COPY], StatsDefs: FROM "StatsDefs" USING [StatBump, StatIncr, StatCounterIndex], CommUtilDefs: FROM "CommUtilDefs" USING [ GetTicks, msPerTick, MyGlobalFrame, Copy, Zero, DisableTimeout, SetTimeout, MsecToTicks, InterruptLevel, AssignInterruptLevel, SetPriority, AddInterruptHandler, RemoveInterruptHandler, GetEthernetHostNumber, AllocateLockedNode, CleanupItem, CleanupReason, AllReasons, AddCleanupProcedure, RemoveCleanupProcedure], AltoEthernetDefs: FROM "AltoEthernetDefs", DriverDefs: FROM "DriverDefs" USING [ giantVector, GiantVector, doCheck, doDebug, doStats, Glitch, GetInputBuffer, NetworkObject, AddDeviceToChain, PutOnGlobalDoneQueue, PutOnGlobalInputQueue], BufferDefs: FROM "BufferDefs", PupTypes: FROM "PupTypes" USING [ PupErrorCode, noErrorPupErrorCode, gatewayResourceLimitsPupErrorCode], DriverTypes: FROM "DriverTypes" USING [ ethernetEncapsulationOffset, ethernetEncapsulationBytes, pupEthernetPacket, ethernetBroadcastHost]; AltoEthernetDriver: MONITOR IMPORTS InlineDefs, CommUtilDefs, StatsDefs, DriverDefs, BufferDefs, AltoEthernetDefs EXPORTS DriverDefs SHARES BufferDefs, DriverTypes = BEGIN OPEN StatsDefs, BufferDefs, DriverDefs, AltoEthernetDefs; ethernetEncapsulationOffset: CARDINAL = DriverTypes.ethernetEncapsulationOffset; ethernetEncapsulationBytes: CARDINAL = DriverTypes.ethernetEncapsulationBytes; myDevice: EthernetDeviceBlockHandle _ NIL; cleanupItem: CommUtilDefs.CleanupItem _ [,CommUtilDefs.AllReasons,Broom]; hardProcess: PROCESS; hardware: CONDITION; watcherProcess: PROCESS; pleaseStop: BOOLEAN; timer: CONDITION; nextBufferPointer: POINTER; currentInputBuffer, nextInputBuffer: Buffer; outputQueue: QueueObject; currentOutputBuffer: Buffer; timeSendStarted: CARDINAL; timeLastRecv: CARDINAL; myNetwork: DriverDefs.NetworkObject _ [ decapsulateBuffer: DecapsulateBuffer, encapsulatePup: EncapsulatePup, encapsulateRpp: EncapsulateRpp, sendBuffer: SendBuffer, forwardBuffer: ForwardBuffer, activateDriver: ActivateDriver, deactivateDriver: DeactivateDriver, deleteDriver: DeleteDriver, interrupt: Interrupt, device: ethernet, alive: TRUE, speed: 3000, index: , netNumber: , hostNumber: , next: NIL, pupStats: NIL, stats: NIL ]; ImpossibleEndcase: PUBLIC ERROR = CODE; FunnyRetransmissionMask: PUBLIC ERROR = CODE; ZeroLengthBuffer: PUBLIC ERROR = CODE; UnreasonableHardwareStatus: PUBLIC ERROR = CODE; QueueScrambled: PUBLIC ERROR = CODE; MachineIDTooBigForEthernet: PUBLIC ERROR = CODE; DriverNotActive: PUBLIC ERROR = CODE; DriverAlreadyActive: PUBLIC ERROR = CODE; NoEthernetBoard: PUBLIC ERROR = CODE; CantSwitchMachinesWhileEtherentDriverIsActive: PUBLIC ERROR = CODE; CantMakImageWhileEtherentDriverIsActive: PUBLIC ERROR = CODE; OnlyThreeDriversArePossible: PUBLIC ERROR = CODE; -- things needed for chained input first, last: EthernetDeviceBlockHandle; headBuffer, tailBuffer: Buffer; notChained: BOOLEAN; interruptLevel: CommUtilDefs.InterruptLevel; interruptBit: WORD; -- BITSHIFT[1,interruptLevel]; inputCommand: SioParameter; outputCommand: SioParameter; resetCommand: SioParameter; etherStats: POINTER TO EtherStatsInfo; Interrupt: ENTRY PROCEDURE = BEGIN b, temporaryBuffer: Buffer; savedWordsLeft: CARDINAL; savedPostData: EthernetPost; device: EthernetDeviceBlockHandle = myDevice; -- things needed for chained input doMoreInput: BOOLEAN; CommUtilDefs.SetPriority[4]; UNTIL pleaseStop DO IF device.postData#EthernetNotPosted OR (~notChained AND first.postData#EthernetNotPosted) THEN BEGIN IF doStats THEN StatIncr[statEtherInterruptDuringInterrupt]; END ELSE BEGIN DO WAIT hardware; IF device.postData#EthernetNotPosted OR (~notChained AND first.postData#EthernetNotPosted) THEN EXIT; IF doStats THEN StatIncr[statEtherMissingStatus]; ENDLOOP; END; SELECT TRUE FROM (~notChained AND first.postData#EthernetNotPosted) => BEGIN savedPostData _ first.postData; savedWordsLeft _ first.wordsLeft; first _ first.inputControlBlock; currentInputBuffer _ headBuffer; headBuffer _ headBuffer.next; doMoreInput _ TRUE; END; device.postData#EthernetNotPosted => BEGIN savedPostData _ device.postData; savedWordsLeft _ device.wordsLeft; device.postData _ EthernetNotPosted; doMoreInput _ notChained; END; ENDCASE => Glitch[ImpossibleEndcase]; IF notChained THEN BEGIN -- Turn on input as soon as we can so we don't drop as many packets. device.inputBuffer.count _ nextInputBuffer.length-ethernetEncapsulationOffset; device.inputBuffer.pointer _ nextBufferPointer; StartIO[inputCommand]; -- input now running, can relax now END; SELECT savedPostData.microcodeStatus FROM inputDone => IF savedPostData.hardwareStatus=hardwareAOK THEN BEGIN IF (temporaryBuffer_GetInputBuffer[])#NIL THEN BEGIN temporaryBuffer.device _ ethernet; -- should unwiredown packet, not now under interface b _ currentInputBuffer; b.length _ (b.length-ethernetEncapsulationOffset)-savedWordsLeft; b.network _ @myNetwork; IF doStats THEN BEGIN etherStats.packetsRecv _ etherStats.packetsRecv+1; StatIncr[statEtherPacketsReceived]; StatBump[statEtherWordsReceived,b.length]; END; PutOnGlobalInputQueue[b]; currentInputBuffer _ temporaryBuffer; IF doStats AND currentOutputBuffer#NIL THEN StatIncr[statEtherInUnderOut]; END ELSE IF doStats THEN BEGIN etherStats.inputOff _ etherStats.inputOff+1; StatIncr[statEtherEmptyFreeQueue]; END; END ELSE IF doStats THEN BEGIN etherStats.badRecvStatus _ etherStats.badRecvStatus+1; SELECT 377B-savedPostData.hardwareStatus FROM 1B => StatIncr[statEtherReceivedNot16]; 10B => StatIncr[statEtherReceivedBadCRC]; 11B => StatIncr[statEtherReceivedNot16BadCRC]; 40B, 50B => BEGIN etherStats.overruns _ etherStats.overruns+1; StatIncr[statEtherReceivedOverrun]; END; 6B, 7B, 16B, 17B => StatIncr[statEtherReceivedKlobberedByReset]; ENDCASE => StatIncr[statEtherReceivedBadStatus]; END; outputDone => IF savedPostData.hardwareStatus=hardwareAOK THEN BEGIN device.outputBuffer _ [0,NIL0]; PutOnGlobalDoneQueue[currentOutputBuffer]; currentOutputBuffer _ NIL; IF doStats THEN BEGIN tries: CARDINAL; statEtherSendsCollision1: StatsDefs.StatCounterIndex = statEtherSendsCollision1; first: CARDINAL = LOOPHOLE[statEtherSendsCollision1]; etherStats.packetsSent _ etherStats.packetsSent+1; SELECT (tries_device.retransmissionMask) FROM 1 => tries _ 0; 3 => tries _ 1; 7 => tries _ 2; 17B => tries _ 3; 37B => tries _ 4; 77B => tries _ 5; 177B => tries _ 6; 377B => tries _ 7; 777B => tries _ 8; 1777B => tries _ 9; 3777B => tries _ 10; 7777B => tries _ 11; 17777B => tries _ 12; 37777B => tries _ 13; 77777B => tries _ 14; 177777B => tries _ 15; ENDCASE => Glitch[FunnyRetransmissionMask]; IF tries#0 THEN StatIncr[LOOPHOLE[first+tries]]; etherStats.loadTable[tries] _ etherStats.loadTable[tries]+1; END; END ELSE BEGIN IF doStats THEN etherStats.badSendSatus _ etherStats.badSendSatus+1; IF (b_currentOutputBuffer)#NIL AND (CommUtilDefs.GetTicks[]-timeSendStarted)>500/CommUtilDefs.msPerTick THEN BEGIN -- requeue it so one packet won't hog the interface device.outputBuffer _ [0,NIL0]; PutOnGlobalDoneQueue[currentOutputBuffer]; currentOutputBuffer _ NIL; IF doStats THEN StatIncr[statPacketsStuckInOutput]; END ELSE IF doStats THEN StatIncr[statEtherSendBadStatus]; END; inputBufferOverflow => IF doStats THEN StatIncr[statEtherReceivedTooLong]; outputLoadOverflow => BEGIN -- requeue it so one packet won't hog the interface device.outputBuffer _ [0,NIL0]; PutOnGlobalDoneQueue[currentOutputBuffer]; currentOutputBuffer _ NIL; IF doStats THEN BEGIN etherStats.loadTable[16] _ etherStats.loadTable[16]+1; StatIncr[statEtherSendsCollisionLoadOverflow]; END; END; zeroLengthBuffer => Glitch[ZeroLengthBuffer]; hardwareReset => -- three reasons for getting here: -- 1) getting back from debugger -- 2) lost interrupt -- 3) stuck output packet (transciever not plugged in) BEGIN IF (b_currentOutputBuffer)#NIL AND (CommUtilDefs.GetTicks[]-timeSendStarted)>500/CommUtilDefs.msPerTick THEN BEGIN -- requeue it so one packet won't hog the interface -- Watch the order of these stores device.outputBuffer.pointer _ NIL0; device.outputBuffer.count _ 0; PutOnGlobalDoneQueue[currentOutputBuffer]; currentOutputBuffer _ NIL; IF doStats THEN StatIncr[statPacketsStuckInOutput]; END ELSE IF doStats THEN StatIncr[statInterfaceReset]; IF ~notChained AND device.inputControlBlock#NIL0 THEN BEGIN -- restart input which got shot down -- this could screwup if the microcode got restarted device.inputControlBlock.inputBuffer.pointer^ _ 0; StartIO[inputCommand]; END; END; interfaceBroken => Glitch[UnreasonableHardwareStatus]; ENDCASE => Glitch[ImpossibleEndcase]; IF doMoreInput THEN BEGIN -- The normal mode uses two buffers for input. The current one, and a hot standby. -- Way up at the beginning of this loop, a read was started into the standby buffer. -- At this point, currentInputBuffer has a buffer to be setup for use next time. -- If we just finished a read, -- then it is a new one left there by the inputDone processing, -- (or the old one if we are recycling it because we couldn't get a new one) -- otherwise, we are reusing the previous one. temporaryBuffer _ nextInputBuffer; nextInputBuffer _ currentInputBuffer; currentInputBuffer _ temporaryBuffer; nextBufferPointer _ ShortenData[ @nextInputBuffer.encapsulation+ethernetEncapsulationOffset]; nextBufferPointer^ _ 0; IF ~notChained THEN BEGIN temp: EthernetDeviceBlockHandle; temp _ ShortenIocb[nextInputBuffer.iocbChain]; temp^ _ [ postData: EthernetNotPosted, interruptBit: interruptBit, wordsLeft: 0, retransmissionMask: 0, inputBuffer: [ nextInputBuffer.length-ethernetEncapsulationOffset, ShortenData[ @nextInputBuffer.encapsulation+ethernetEncapsulationOffset] ], outputBuffer: [0,NIL0], hostNumber: 0, inputControlBlock: NIL0]; last.inputControlBlock _ temp; last _ temp; tailBuffer.next _ nextInputBuffer; tailBuffer _ nextInputBuffer; IF device.inputControlBlock=NIL0 AND temp.postData=EthernetNotPosted THEN BEGIN -- adding a new buffer device.inputControlBlock _ temp; StartIO[inputCommand]; StatIncr[statEtherEmptyInputChain]; END; END; END; -- see if there is output to do, there will be some for several reasons: -- 1 output finished, and another buffer was queued -- 2 input finished, and leftover outputBuffer (in under out) -- 3 input finished, and somebody didn't kick us because a packet was pouring in -- 4 (chained) we got some input first BEGIN IF currentOutputBuffer#NIL THEN GOTO SendThisOne; IF outputQueue.length=0 THEN GOTO NothingToSend; currentOutputBuffer _ Dequeue[@outputQueue]; timeSendStarted _ CommUtilDefs.GetTicks[]; IF doCheck AND currentOutputBuffer=NIL THEN Glitch[QueueScrambled]; IF doStats THEN StatIncr[statEtherSendFromOutputQueue]; GOTO SendThisOne; EXITS SendThisOne => BEGIN -- start output if not already input coming in IF notChained THEN BEGIN IF device.inputBuffer.pointer^#0 THEN GOTO PouringIn; device.interruptBit _ 0; -- inhibit interrupts for reset device.postData _ EthernetNotPosted; -- Beware of hardware/microcode screwup -- it seems to hang while sending, after a collision, until a gateway packet arrives IF doStats THEN StartIO[resetCommand]; -- should post immediately UNTIL device.postData#EthernetNotPosted DO IF doStats THEN StatIncr[statResetDidntPost]; StartIO[resetCommand]; ENDLOOP; device.interruptBit _ interruptBit; --interrupts back on device.postData _ EthernetNotPosted; END ELSE BEGIN -- Don't reverse the order of these tests. It might be about ready to post. IF device.outputBuffer.pointer#NIL THEN GOTO AlreadySending; IF device.postData#EthernetNotPosted THEN GOTO DontSendAgain; END; device.retransmissionMask _ 0; device.outputBuffer.count _ currentOutputBuffer.length; device.outputBuffer.pointer _ ShortenData[ @currentOutputBuffer.encapsulation+ethernetEncapsulationOffset]; IF ~notChained THEN BEGIN now: EthernetDeviceBlockHandle = device.inputControlBlock; IF now#NIL AND now.inputBuffer.pointer^#0 THEN GOTO PouringIn; IF device.retransmissionMask#0 THEN GOTO AlreadySending; END; StartIO[outputCommand]; EXITS PouringIn, AlreadySending, DontSendAgain => NULL; END; NothingToSend => NULL; END; ENDLOOP; END; -- Interrupt Watcher: PROCEDURE = BEGIN UNTIL pleaseStop DO THROUGH [0..25) DO IF myDevice.postData=EthernetNotPosted AND (notChained OR first.postData=EthernetNotPosted) THEN EXIT; -- If the post location is not zero, 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 at the post location several times. Of course, if it is still not zero when we look again, it could be a new interrupt that has just arrived. REPEAT FINISHED => BEGIN IF doStats THEN StatIncr[statEtherLostInterrupts]; WatcherNotify[]; END; ENDLOOP; IF currentOutputBuffer#NIL AND (CommUtilDefs.GetTicks[]-timeSendStarted)>250/CommUtilDefs.msPerTick THEN StartIO[resetCommand]; -- interrupt code will flush it IF (CommUtilDefs.GetTicks[]-timeLastRecv)>5000/CommUtilDefs.msPerTick THEN BEGIN -- Blast receiver since it may be stuck. This might kill a packet. -- This shouldn't hurt (much) if it is ok since this doesn't happen very often. timeLastRecv _ CommUtilDefs.GetTicks[]; IF doStats THEN StatIncr[statMouseTrap]; StartIO[resetCommand]; END; WatcherWait[]; ENDLOOP; END; WatcherWait: ENTRY PROCEDURE = BEGIN WAIT timer; END; WatcherNotify: ENTRY PROCEDURE = BEGIN NOTIFY hardware; END; DecapsulateBuffer: PROCEDURE [b: Buffer] RETURNS [BufferType] = BEGIN timeLastRecv _ CommUtilDefs.GetTicks[]; SELECT b.encapsulation.ethernetType FROM DriverTypes.pupEthernetPacket => BEGIN IF b.length#((b.pupLength+1+ethernetEncapsulationBytes)/2) THEN BEGIN IF doStats THEN StatIncr[statPupsDiscarded]; RETURN[rejected]; END; RETURN[pup]; END; ENDCASE => RETURN[rejected]; END; EncapsulateRpp: PROCEDURE [RppBuffer, PupHostID] = LOOPHOLE[EncapsulatePup]; EncapsulatePup: PROCEDURE [b: PupBuffer, destination: PupHostID] = BEGIN b.encapsulation _ [ ethernet [ etherSpare1:, etherSpare2:, etherSpare3:, etherSpare4:, etherDest: destination, etherSource: myNetwork.hostNumber, ethernetType: DriverTypes.pupEthernetPacket ] ]; b.length _ (b.pupLength+1+ethernetEncapsulationBytes)/2; END; ForwardBuffer: PROCEDURE [b: Buffer] RETURNS [PupTypes.PupErrorCode] = BEGIN IF outputQueue.length>10 THEN RETURN[PupTypes.gatewayResourceLimitsPupErrorCode]; -- transciever unpluged? SendBuffer[b]; RETURN[PupTypes.noErrorPupErrorCode]; END; SendBuffer: ENTRY PROCEDURE [b: Buffer] = BEGIN device: EthernetDeviceBlockHandle = myDevice; copy: Buffer; IF pleaseStop THEN Glitch[DriverNotActive]; b.device _ ethernet; IF b.encapsulation.etherDest=myNetwork.hostNumber OR b.encapsulation.etherDest=DriverTypes.ethernetBroadcastHost THEN BEGIN -- sending to ourself, copy it over copy _ GetInputBuffer[]; IF copy#NIL THEN BEGIN copy.device _ ethernet; InlineDefs.COPY[ from: @b.encapsulation+ethernetEncapsulationOffset, nwords: b.length, to: @copy.encapsulation+ethernetEncapsulationOffset ]; copy.length _ b.length; copy.network _ @myNetwork; PutOnGlobalInputQueue[copy]; IF doStats THEN StatIncr[statEtherPacketsLocal]; IF doStats THEN StatBump[statEtherWordsLocal,b.length]; END ELSE IF doStats THEN StatIncr[statEtherEmptyFreeQueue]; END; IF currentOutputBuffer=NIL THEN BEGIN currentOutputBuffer _ b; timeSendStarted _ CommUtilDefs.GetTicks[]; device.retransmissionMask _ 0; device.outputBuffer.count _ b.length; device.outputBuffer.pointer _ ShortenData[ @b.encapsulation+ethernetEncapsulationOffset]; IF notChained THEN BEGIN IF device.inputBuffer.pointer^#0 THEN GOTO PouringIn; device.interruptBit _ 0; -- inhibit interrupts for reset device.postData _ EthernetNotPosted; -- beware of hardware/microcode screwup -- it seems to hang while sending, after a collision, until a gateway packet arrives IF doStats THEN StartIO[resetCommand]; -- should post immediately UNTIL device.postData#EthernetNotPosted DO IF doStats THEN StatIncr[statResetDidntPost]; StartIO[resetCommand]; ENDLOOP; device.interruptBit _ interruptBit; --interrupts back on device.postData _ EthernetNotPosted; END; IF ~notChained THEN BEGIN now: EthernetDeviceBlockHandle = device.inputControlBlock; IF now#NIL AND now.inputBuffer.pointer^#0 THEN GOTO PouringIn; IF device.retransmissionMask#0 THEN GOTO AlreadySending; END; StartIO[outputCommand]; -- This could possibly clobber a packet that just started EXITS AlreadySending => NULL; PouringIn => BEGIN -- data already arriving, don't klobber it IF doStats THEN StatIncr[statEtherSendWhileReceiving]; END; END ELSE Enqueue[@outputQueue,b]; -- output already in progress, don't klobber it IF doStats THEN StatIncr[statEtherPacketsSent]; IF doStats THEN StatBump[statEtherWordsSent,b.length]; END; -- Saving the satus is helpful when debugging. Comment it out to save space. ethernetStatus: EthernetDeviceBlockHandle; Broom: PROCEDURE [why: CommUtilDefs.CleanupReason] = BEGIN IF doDebug THEN ethernetStatus^ _ myDevice^; SELECT why FROM Finish, Abort, OutLd => myDevice.interruptBit _ 0; InLd => BEGIN IF myNetwork.hostNumber#CommUtilDefs.GetEthernetHostNumber[] THEN Glitch[CantSwitchMachinesWhileEtherentDriverIsActive]; myDevice.interruptBit _ interruptBit; END; Save, Checkpoint => Glitch[CantMakImageWhileEtherentDriverIsActive]; ENDCASE; StartIO[resetCommand]; END; -- COLD code, only used when turning things on+off CreateDefaultEthernetDriver: PUBLIC PROCEDURE RETURNS [BOOLEAN] = BEGIN OPEN AltoEthernetDefs; SetupEthernetDriver[0,5,standardInput,standardOutput,standardEthernet,FALSE]; RETURN[TRUE]; END; -- NB: All non-Default drivers assume the Chained microcode CreateEthernetDriver: PUBLIC PROCEDURE [ netNumber: CARDINAL, deviceNumber: [0..3) ] RETURNS [BOOLEAN] = BEGIN OPEN AltoEthernetDefs; him: POINTER TO FRAME[AltoEthernetDriver]; -- There is no way to delete the new frame. IF deviceNumber#0 THEN him _ CommUtilDefs.Copy[CommUtilDefs.MyGlobalFrame[]]; SELECT deviceNumber FROM 0 => SetupEthernetDriver[netNumber,5, standardInput,standardOutput,standardEthernet,TRUE]; 1 => him.SetupEthernetDriver[netNumber,6, secondInput,secondOutput,secondEthernet,TRUE]; 2 => him.SetupEthernetDriver[netNumber,8, -- 7 used by keyboard thirdInput,thirdOutput,thirdEthernet,TRUE]; ENDCASE => Glitch[OnlyThreeDriversArePossible]; RETURN[TRUE]; END; SetupEthernetDriver: PROCEDURE [ netNumber: CARDINAL, intLevel: WORD, inputBit, outputBit: WORD, deviceBlock: EthernetDeviceBlockHandle, chained: BOOLEAN] = BEGIN size: CARDINAL _ IF chained THEN SIZE[EthernetDeviceBlock] ELSE 0; notChained _ ~chained; myNetwork.netNumber _ netNumber; inputCommand _ [inputBit]; outputCommand _ [outputBit]; interruptLevel _ CommUtilDefs.AssignInterruptLevel[intLevel]; myDevice _ deviceBlock; resetCommand _ [inputCommand+outputCommand]; interruptBit _ InlineDefs.BITSHIFT[1,interruptLevel]; pleaseStop _ TRUE; myDevice.postData _ EthernetNotPosted; AddDeviceToChain[@myNetwork,size]; IF doStats THEN BEGIN myNetwork.stats _ etherStats _ CommUtilDefs.AllocateLockedNode[SIZE[EtherStatsInfo]]; CommUtilDefs.Zero[etherStats,SIZE[EtherStatsInfo]]; END; END; DeleteDriver: PROCEDURE = BEGIN END; -- Be sure the microcode has been loaded by now if this is a second Ethernet Board ActivateDriver: PROCEDURE = BEGIN IF ~pleaseStop THEN Glitch[DriverAlreadyActive]; pleaseStop _ FALSE; StartIO[resetCommand]; StartIO[resetCommand]; -- sometimes it doesn't work IF myDevice.postData=EthernetNotPosted THEN Glitch[NoEthernetBoard]; QueueInitialize[@outputQueue]; currentInputBuffer _ nextInputBuffer _ currentOutputBuffer _ NIL; myNetwork.hostNumber _ CommUtilDefs.GetEthernetHostNumber[]; myDevice.hostNumber _ myNetwork.hostNumber; myDevice.inputBuffer _ [0,NIL0]; myDevice.outputBuffer _ [0,NIL0]; IF notChained THEN BEGIN nextInputBuffer _ GetInputBuffer[]; currentInputBuffer _ GetInputBuffer[]; nextInputBuffer.device _ currentInputBuffer.device _ ethernet; nextBufferPointer _ ShortenData[ @nextInputBuffer.encapsulation+ethernetEncapsulationOffset]; nextBufferPointer^ _ 0; -- show no input in yet myDevice.inputControlBlock _ NIL0; END ELSE BEGIN temp: EthernetDeviceBlockHandle; first _ last _ NIL; headBuffer _ tailBuffer _ NIL; THROUGH [0..4) DO -- This should be a parameter currentInputBuffer _ GetInputBuffer[]; temp _ ShortenIocb[currentInputBuffer.iocbChain]; IF first=NIL THEN BEGIN first _ temp; headBuffer _ currentInputBuffer; END; temp^ _ [ postData: EthernetNotPosted, interruptBit: interruptBit, wordsLeft: 0, retransmissionMask: 0, inputBuffer: [ currentInputBuffer.length-ethernetEncapsulationOffset, ShortenData[ @currentInputBuffer.encapsulation+ethernetEncapsulationOffset] ], outputBuffer: [0,NIL0], hostNumber: 0, inputControlBlock: NIL0]; temp.inputBuffer.pointer^ _ 0; IF last#NIL THEN BEGIN last.inputControlBlock _ temp; tailBuffer.next _ currentInputBuffer; END; last _ temp; currentInputBuffer.next _ NIL; tailBuffer _ currentInputBuffer; ENDLOOP; myDevice.inputControlBlock _ first; END; CommUtilDefs.AddCleanupProcedure[@cleanupItem]; CommUtilDefs.AddInterruptHandler[interruptLevel,@hardware,resetCommand]; hardProcess _ FORK Interrupt[]; -- The first interrupt will set things up. myDevice.interruptBit _ interruptBit; StartIO[resetCommand]; watcherProcess _ FORK Watcher[]; END; DeactivateDriver: PROCEDURE = BEGIN IF pleaseStop THEN Glitch[DriverNotActive]; pleaseStop _ TRUE; StartIO[resetCommand]; -- includes (naked)NOTIFY JOIN hardProcess; CommUtilDefs.RemoveCleanupProcedure[@cleanupItem]; myDevice.interruptBit _ 0; CommUtilDefs.RemoveInterruptHandler[interruptLevel]; StartIO[resetCommand]; KillDriverLocked[]; JOIN watcherProcess; IF currentInputBuffer#NIL THEN ReturnFreeBuffer[currentInputBuffer]; IF nextInputBuffer#NIL THEN ReturnFreeBuffer[nextInputBuffer]; IF currentOutputBuffer#NIL THEN PutOnGlobalDoneQueue[currentOutputBuffer]; QueueCleanup[@outputQueue]; myNetwork.netNumber _ 0; -- in case we turn it on after moving to another machine -- maybe we should turn off the SD bits if we are an extra board END; KillDriverLocked: ENTRY PROCEDURE = INLINE BEGIN NOTIFY timer; END; -- initialization CommUtilDefs.DisableTimeout[@hardware]; CommUtilDefs.SetTimeout[@timer,CommUtilDefs.MsecToTicks[1000]]; IF doDebug THEN BEGIN debugPointer: LONG POINTER TO GiantVector _ giantVector; debugPointer.ethernetOutputQueue _ @outputQueue; debugPointer.currentInputBuffer _ @currentInputBuffer; debugPointer.nextInputBuffer _ @nextInputBuffer; debugPointer.currentOutputBuffer _ @currentOutputBuffer; ethernetStatus _ CommUtilDefs.AllocateLockedNode[SIZE[EthernetDeviceBlock]]; END; END. -- AltoEthernetDriver