<> <> <> DIRECTORY BufferDefs USING [Buffer], CommFlags USING [doStats], DriverTypes USING [Encapsulation, PhonePacketType, phoneEncapsulationOffset, phoneEncapsulationBytes], HalfDuplex USING [], Process USING [SetTimeout, MsecToTicks], ProcessorFace USING [microsecondsPerHundredPulses, GetClockPulses], RS232C USING [ChannelHandle, LineSpeed, DeviceStatus, CompletionHandle, PhysicalRecord, ChannelSuspended, Put, TransmitNow, SetParameter, GetStatus, StatusWait], NSAddress USING [ProcessorID]; HalfDuplexImpl: MONITOR IMPORTS RS232C, Process, ProcessorFace EXPORTS HalfDuplex SHARES BufferDefs = BEGIN <> ControlMode: TYPE = {unknown, master, slave}; TurnAroundState: TYPE = {sending, receiving}; Timer: TYPE = { turnAround, -- max time sending in one direction noMoreToSend, -- idle sender becomes receiver after this time masterResponse}; <> StatsRecord: TYPE = RECORD [ turnArndSent, turnArndRcvd, masterTOs, noSendTOs, turnTOs: CARDINAL]; <> timerProcess: PROCESS; turnAroundState: TurnAroundState; controlMode: ControlMode; timer: ARRAY Timer OF LONG CARDINAL; -- in pulses timeout: ARRAY Timer OF LONG CARDINAL _ [ turnAround:, noMoreToSend: nothingSentNMTSPulses, masterResponse: initialMasterResponsePulses]; stopTimer: BOOLEAN; timeoutTimer: CONDITION; turnAroundArrived: CONDITION; ourTurnToSend: BOOLEAN; driverSending: BOOLEAN; senderHasMore: BOOLEAN; sendFinished: CONDITION; channel: RS232C.ChannelHandle; clearToSendUp: BOOLEAN; ourProcessorID: NSAddress.ProcessorID; statsRec: StatsRecord; <> timerWakeup: CARDINAL = 250; <> bps1200TurnAroundTimeout: CARDINAL = 13000; -- msecs bps2400TurnAroundTimeout: CARDINAL = bps1200TurnAroundTimeout/2; -- msecs bps4800TurnAroundTimeout: CARDINAL = bps1200TurnAroundTimeout/4; -- msecs bps9600TurnAroundTimeout: CARDINAL = bps1200TurnAroundTimeout/8; -- msecs <> nothingSentNMTSPulses: LONG CARDINAL _ MilliSecondsToPulses[ nothingSentNMTSTimeout]; sentSomethingNMTSPulses: LONG CARDINAL _ MilliSecondsToPulses[ sentSomethingNMTSTimeout]; remoteAnxiousNMTSPulses: LONG CARDINAL _ MilliSecondsToPulses[ remoteAnxiousNMTSTimeout]; initialMasterResponsePulses: LONG CARDINAL _ MilliSecondsToPulses[ initialMasterResponseTimeout]; nothingSentNMTSTimeout: CARDINAL = 500; -- msecs sentSomethingNMTSTimeout: CARDINAL = 250; -- msecs remoteAnxiousNMTSTimeout: CARDINAL = 250; -- msecs initialMasterResponseTimeout: CARDINAL = 5000; -- msecs <> <<********Init/termination********>> Initialize: PUBLIC PROCEDURE [ chHandle: RS232C.ChannelHandle, lineSpeed: RS232C.LineSpeed, ourHostNumber: NSAddress.ProcessorID] = <> BEGIN <> timeInMilliSecs: CARDINAL; channel _ chHandle; -- save channel handle <> timeInMilliSecs _ SELECT lineSpeed FROM bps2400 => bps2400TurnAroundTimeout, bps4800 => bps4800TurnAroundTimeout, bps9600 => bps9600TurnAroundTimeout, ENDCASE => bps1200TurnAroundTimeout; timeout[turnAround] _ MilliSecondsToPulses[timeInMilliSecs]; stopTimer _ FALSE; clearToSendUp _ FALSE; controlMode _ unknown; turnAroundState _ receiving; ourProcessorID _ ourHostNumber; ourTurnToSend _ FALSE; driverSending _ FALSE; senderHasMore _ FALSE; StartTimer[masterResponse]; Process.SetTimeout[@timeoutTimer, Process.MsecToTicks[timerWakeup]]; timerProcess _ FORK TimerProcess[]; IF CommFlags.doStats THEN statsRec _ [0, 0, 0, 0, 0]; END; Destroy: PUBLIC PROCEDURE = <> BEGIN <> NotifyConditions: ENTRY PROCEDURE = BEGIN stopTimer _ TRUE; ourTurnToSend _ TRUE; driverSending _ FALSE; NOTIFY timeoutTimer; NOTIFY turnAroundArrived; NOTIFY sendFinished; END; NotifyConditions[]; JOIN timerProcess; END; <<********timers********>> TimerProcess: PROCEDURE = <> BEGIN CheckIfTimedOut: PROCEDURE [timerSelect: Timer] RETURNS [timedOut: BOOLEAN] = BEGIN timedOut _ (GetClock[] - timer[timerSelect] > timeout[timerSelect]); IF CommFlags.doStats THEN IF timedOut THEN SELECT timerSelect FROM masterResponse => StatIncr[@statsRec.masterTOs]; turnAround => StatIncr[@statsRec.turnTOs]; noMoreToSend => StatIncr[@statsRec.noSendTOs]; ENDCASE => ERROR; END; RandomWaitTime: PROCEDURE RETURNS [CARDINAL] = INLINE <> BEGIN LowExtractor: TYPE = MACHINE DEPENDENT RECORD [ highBitsLowWord: [0..32), lowTenBits: [0..2048), secondWord: CARDINAL]; RETURN[(LOOPHOLE[GetClock[], LowExtractor]).lowTenBits + 1000]; END; UNTIL stopTimer DO SELECT turnAroundState FROM sending => IF CheckIfTimedOut[noMoreToSend] OR CheckIfTimedOut[turnAround] THEN BEGIN SendLTA[]; -- will happen after any SendFrame in progress IF controlMode = unknown THEN SetTimer[masterResponse, RandomWaitTime[]]; IF controlMode # slave THEN StartTimer[masterResponse]; turnAroundState _ receiving; END; receiving => -- slave side has no timeout, it just awaits turnaround IF controlMode # slave THEN BEGIN IF CheckIfTimedOut[masterResponse] THEN BEGIN -- assume some packet lost StartTimer[turnAround]; SetTimer[noMoreToSend, nothingSentNMTSPulses]; <> StartTimer[noMoreToSend]; turnAroundState _ sending; senderHasMore _ FALSE; IF controlMode # unknown THEN NotifyOurTurnToSend[]; END; END; ENDCASE => ERROR; WaitTimer[]; ENDLOOP; END; WaitTimer: ENTRY PROCEDURE = BEGIN WAIT timeoutTimer; END; StartTimer: PROCEDURE [timerSelect: Timer] = INLINE <> BEGIN timer[timerSelect] _ ProcessorFace.GetClockPulses[]; END; SetTimer: PROCEDURE [timer: Timer, pulses: LONG CARDINAL] = INLINE BEGIN timeout[timer] _ pulses; END; GetClock: PROCEDURE RETURNS [LONG CARDINAL] = INLINE <> BEGIN RETURN[ProcessorFace.GetClockPulses[]]; END; <<********Turn-around********>> WaitToSend: PUBLIC PROCEDURE = <> BEGIN <> AwaitLineTurnAround: ENTRY PROCEDURE = BEGIN UNTIL ourTurnToSend DO WAIT turnAroundArrived; ENDLOOP; driverSending _ TRUE; END; AwaitLineTurnAround[]; AwaitCTS[]; -- set RTS if necessary, wait for CTS END; SendCompleted: PUBLIC ENTRY PROCEDURE [moreToSend: BOOLEAN] = <> BEGIN ENABLE UNWIND => NULL; driverSending _ FALSE; NOTIFY sendFinished; SetTimer[noMoreToSend, sentSomethingNMTSPulses]; -- reduce timeout StartTimer[noMoreToSend]; senderHasMore _ moreToSend; END; CheckForTurnAround: PUBLIC PROCEDURE [buffer: BufferDefs.Buffer] RETURNS [throwAway: BOOLEAN] = <> BEGIN OPEN phoneEncap: buffer.encapsulation; <> remoteProcessorID: NSAddress.ProcessorID; IF controlMode = unknown THEN BEGIN remoteProcessorID _ LOOPHOLE[phoneEncap.pnSrcID]; SELECT ourProcessorID.a FROM -- determine mode by comparing processorIDs = remoteProcessorID.a => SELECT ourProcessorID.b FROM = remoteProcessorID.b => controlMode _ (IF ourProcessorID.c > remoteProcessorID.c THEN master ELSE slave); > remoteProcessorID.b => controlMode _ master; ENDCASE => controlMode _ slave; > remoteProcessorID.a => controlMode _ master; ENDCASE => controlMode _ slave; IF controlMode = master THEN SetTimer[masterResponse, initialMasterResponsePulses]; END; IF phoneEncap.pnType IN [turnAroundPhonePacket..turnAroundMTSPhonePacket] THEN BEGIN -- a driver turn-around packet (no piggybacking yet) IF phoneEncap.pnType = turnAroundMTSPhonePacket THEN SetTimer[noMoreToSend, remoteAnxiousNMTSTimeout]; IF CommFlags.doStats THEN StatIncr[@statsRec.turnArndRcvd]; StartTimer[turnAround]; StartTimer[noMoreToSend]; turnAroundState _ sending; NotifyOurTurnToSend[]; -- tell anyone waiting to send real packets senderHasMore _ FALSE; throwAway _ TRUE; END ELSE BEGIN throwAway _ FALSE; StartTimer[masterResponse]; -- time only from last thing heard END; END; NotifyOurTurnToSend: ENTRY PROCEDURE = <> BEGIN ourTurnToSend _ TRUE; NOTIFY turnAroundArrived; END; AwaitCTS: PROCEDURE = <> BEGIN <> status: RS232C.DeviceStatus; IF ~clearToSendUp THEN BEGIN ENABLE RS232C.ChannelSuspended => GOTO fatalPlace; RS232C.SetParameter[channel, [requestToSend[TRUE]]]; status _ RS232C.GetStatus[channel]; UNTIL status.clearToSend DO status _ RS232C.StatusWait[channel, status]; ENDLOOP; clearToSendUp _ TRUE; END; EXITS fatalPlace => NULL; END; SendLTA: PROCEDURE = <> BEGIN <> complHandle: RS232C.CompletionHandle; rec: RS232C.PhysicalRecord _ [ header: [NIL, 0, 0], body:, trailer: [NIL, 0, 0]]; buffer: DriverTypes.Encapsulation _ [ phonenet[ framing0:, framing1:, framing2:, framing3:, framing4:, framing5:, recognition: 0, pnType: (IF senderHasMore THEN turnAroundMTSPhonePacket ELSE turnAroundPhonePacket), pnSrcID: LOOPHOLE[ourProcessorID]]]; ClearOurTurn[]; AwaitNoSending[]; AwaitCTS[]; <> rec.body.blockPointer _ @buffer + DriverTypes.phoneEncapsulationOffset; <> rec.body.startIndex _ 0; rec.body.stopIndexPlusOne _ DriverTypes.phoneEncapsulationBytes; BEGIN ENABLE RS232C.ChannelSuspended => GOTO fatalPlace; complHandle _ RS232C.Put[channel, @rec]; [, ] _ RS232C.TransmitNow[channel, complHandle]; RS232C.SetParameter[channel, [requestToSend[FALSE]]]; IF CommFlags.doStats THEN StatIncr[@statsRec.turnArndSent]; END; -- ENABLE clearToSendUp _ FALSE; EXITS fatalPlace => NULL; END; ClearOurTurn: ENTRY PROCEDURE = BEGIN ourTurnToSend _ FALSE; END; AwaitNoSending: ENTRY PROCEDURE = BEGIN UNTIL ~driverSending DO WAIT sendFinished; ENDLOOP; END; <<**************** Pulse conversion ****************>> Pulses: TYPE = RECORD[LONG CARDINAL]; MilliSecondsToPulses: PROCEDURE [ms: LONG CARDINAL] RETURNS [pulses: Pulses] = BEGIN <> <> micro: LONG CARDINAL = MIN[LAST[LONG CARDINAL]/1000,ms] * 1000; hundreds: LONG CARDINAL = micro / ProcessorFace.microsecondsPerHundredPulses; pulses _ [MIN[LAST[LONG CARDINAL]/100,hundreds]*100]; END; -- end MilliSecondsToPulses <<**************** Statistics ****************>> StatIncr: PROCEDURE [counter: POINTER TO CARDINAL] = <> BEGIN <> counter^ _ (counter^ + 1) MOD (LAST[CARDINAL] - 1); END; StatBump: PROCEDURE [counter: POINTER TO CARDINAL, bumpAmount: CARDINAL] = <> BEGIN <> counter^ _ (counter^ + bumpAmount) MOD (LAST[CARDINAL] - 10000); END; <
> END. LOG Time: July 11, 1980 3:46 PM By: Garlick Action: Created. Time: August 11, 1980 3:12 PM By: Garlick Action: Added notification of the turnAroundArrived and sendFinished conditions in Destroy. Time: January 23, 1981 3:06 PM By: Garlick Action: Converted all timers to pulses. Pulse arithmetic works correctly with overflow and clock wraparound. Time: February 27, 1981 5:22 PM By: Garlick Action: Fixed a couple of places that didn't get all timers converted to pulses.