<> <> <> <> <> <> <> <> <> DIRECTORY Process USING [MsecToTicks, Priority, SetPriority, SetTimeout], PrincOpsUtils USING [AllocateNakedCondition, DeallocateNakedCondition, DisableInterrupts, EnableInterrupts], TTYPort USING [CharacterLength, DeviceStatus, LineSpeed, Parameter, Parity, StopBits, TransferStatus], TTYFace USING [DeviceStatus, GetCommand, GetLineCount, GetStatus, Off, On, Parameter, PutCommand, -- SendBreak, -- SetParameter, TransferStatus]; TTYPortChannel: MONITOR LOCKS channel USING channel: ChannelHandle IMPORTS Process, PrincOpsUtils, TTYFace EXPORTS TTYPort SHARES TTYPort = BEGIN ChannelHandle: PUBLIC TYPE = REF ChannelStatus; ChannelStatus: PUBLIC TYPE = MONITORED RECORD [ deleteInProgress: BOOLEAN _ FALSE, breakBeingProcessed: BOOLEAN _ FALSE, myDataLost: BOOLEAN _ FALSE, lineNumber: CARDINAL, deviceStatus: TTYPort.DeviceStatus _ [FALSE, FALSE, FALSE, FALSE, FALSE, FALSE], bufferInput: CONDITION, -- wakes up when character added to buffer breaksInBuffer: CARDINAL _ 0, charsInBuffer: CARDINAL _ 0, topInUse: CARDINAL _ 0, topUnused: CARDINAL _ 0, buffer: ARRAY [0..maxBufferSize) OF BufferRecord _ NULL, interrupt: PROCESS _ NIL, wakeUpPtr: LONG POINTER TO CONDITION _ NIL, -- wakes up when microcode does something interesting, TxRdy/RxRdy statusChange: CONDITION, statusWaitCount: CARDINAL _ 0]; maxBufferSize: CARDINAL = 16; BufferRecord: TYPE = RECORD [char: CHARACTER, stat: TTYPort.TransferStatus]; ChannelAlreadyExists: PUBLIC ERROR = CODE; InvalidLineNumber: PUBLIC ERROR = CODE; NoTTYPortHardware: PUBLIC ERROR = CODE; ChannelQuiesced: PUBLIC ERROR = CODE; ConvertFaceToChannelTransferStatus: ARRAY TTYFace.TransferStatus OF TTYPort.TransferStatus = [success, parityError, asynchFramingError, dataLost, breakDetected, aborted]; aMoment: CONDITION; csmPriority: Process.Priority _ 5; channelIsCreated: PACKED ARRAY [0..16) OF BOOLEAN _ ALL[FALSE]; lineCount: CARDINAL = MIN[TTYFace.GetLineCount[], 16]; -- External procs CharsAvailable: PUBLIC SAFE PROC [channel: ChannelHandle] RETURNS [number: CARDINAL] = TRUSTED {RETURN[GetInfo[channel].number]}; GetStatus: PUBLIC SAFE PROC [channel: ChannelHandle] RETURNS [TTYPort.DeviceStatus] = TRUSTED {RETURN[GetInfo[channel].status]}; -- ENTRY procs (and nested entry procs) Create: PUBLIC SAFE PROC [lineNumber: CARDINAL] RETURNS [ch: ChannelHandle] = TRUSTED BEGIN CheckCreated: --ENTRY-- PROC RETURNS [b: BOOLEAN] = INLINE BEGIN PrincOpsUtils.DisableInterrupts[]; b _ channelIsCreated[lineNumber]; channelIsCreated[lineNumber] _ TRUE; PrincOpsUtils.EnableInterrupts[]; END; mask: WORD; IF lineCount = 0 THEN ERROR NoTTYPortHardware; IF lineNumber >= lineCount THEN ERROR InvalidLineNumber; IF CheckCreated[] THEN ERROR ChannelAlreadyExists; ch _ NEW[ ChannelStatus _ [lineNumber: lineNumber, bufferInput: [timeout: Process.MsecToTicks[10000]], statusChange: [timeout: Process.MsecToTicks[10000]]]]; [cv: ch.wakeUpPtr, mask: mask] _ PrincOpsUtils.AllocateNakedCondition[]; Process.SetTimeout[ch.wakeUpPtr, Process.MsecToTicks[10000]]; TTYFace.On[lineNumber, mask]; ch.interrupt _ FORK InterruptProcess[ch]; END; Delete: PUBLIC SAFE PROC [channel: ChannelHandle] = TRUSTED BEGIN DeleteChannel: ENTRY PROC [channel: ChannelHandle] = BEGIN channel.deleteInProgress _ TRUE; BROADCAST channel.wakeUpPtr^; BROADCAST channel.bufferInput; TTYFace.SetParameter[channel.lineNumber, [dataSetReady[FALSE]]]; AbortStatusWaits[channel]; END; DeleteChannel[channel]; JOIN channel.interrupt; TTYFace.Off[channel.lineNumber]; PrincOpsUtils.DeallocateNakedCondition[channel.wakeUpPtr]; channelIsCreated[channel.lineNumber] _ FALSE; END; Get: PUBLIC ENTRY SAFE PROC [channel: ChannelHandle] RETURNS [data: CHARACTER, status: TTYPort.TransferStatus] = TRUSTED BEGIN IF channel.deleteInProgress OR channel.deviceStatus.aborted THEN RETURN WITH ERROR ChannelQuiesced; data _ 0C; WHILE channel.charsInBuffer = 0 DO WAIT channel.bufferInput; IF channel.deleteInProgress THEN {status _ abortedByDelete; RETURN}; IF channel.deviceStatus.aborted THEN {status _ aborted; RETURN}; ENDLOOP; channel.myDataLost _ FALSE; IF channel.buffer[channel.topInUse].stat = breakDetected THEN channel.breaksInBuffer _ channel.breaksInBuffer - 1; [data, status] _ channel.buffer[channel.topInUse]; channel.charsInBuffer _ channel.charsInBuffer - 1; channel.topInUse _ (channel.topInUse + 1) MOD maxBufferSize; END; GetInfo: ENTRY PROC [channel: ChannelHandle] RETURNS [number: CARDINAL, status: TTYPort.DeviceStatus] = BEGIN IF channel.deleteInProgress OR channel.deviceStatus.aborted THEN RETURN WITH ERROR ChannelQuiesced; UpdateDeviceStatus[channel]; RETURN[channel.charsInBuffer, channel.deviceStatus]; END; InterruptProcess: ENTRY PROC [channel: ChannelHandle] = BEGIN Process.SetPriority[csmPriority]; DO WAIT channel.wakeUpPtr; UpdateDeviceStatus[channel]; IF channel.deleteInProgress OR channel.deviceStatus.aborted THEN RETURN; IF channel.deviceStatus.readyToGet THEN BEGIN PutCharInBuff: INTERNAL PROC [channel: ChannelHandle, status: TTYPort.TransferStatus] = BEGIN IF status # breakDetected THEN channel.breakBeingProcessed _ FALSE ELSE IF channel.breakBeingProcessed THEN RETURN; -- don't mess with continuing break IF channel.charsInBuffer < maxBufferSize THEN BEGIN IF status = breakDetected THEN BEGIN -- new break seen channel.breaksInBuffer _ channel.breaksInBuffer + 1; channel.breakBeingProcessed _ TRUE; channel.deviceStatus.breakDetected _ TRUE; BROADCAST channel.statusChange; END; channel.buffer[channel.topUnused] _ [data, status]; channel.charsInBuffer _ channel.charsInBuffer + 1; channel.topUnused _ (channel.topUnused + 1) MOD maxBufferSize; END ELSE IF ~channel.myDataLost THEN BEGIN -- only do dataLost flagging once channel.myDataLost _ TRUE; channel.buffer[(channel.topUnused + maxBufferSize - 1) MOD maxBufferSize].stat _ dataLost; END; BROADCAST channel.bufferInput; END; data: CHARACTER; faceTS: TTYFace.TransferStatus; status: TTYPort.TransferStatus; [data, faceTS] _ TTYFace.GetCommand[channel.lineNumber]; IF faceTS = notReady THEN ERROR; status _ ConvertFaceToChannelTransferStatus[faceTS]; IF channel.deleteInProgress THEN status _ abortedByDelete; IF channel.deviceStatus.aborted THEN status _ aborted; IF (data = 0C AND channel.breakBeingProcessed) AND status # breakDetected THEN channel.breakBeingProcessed _ FALSE ELSE PutCharInBuff[channel, status]; IF channel.deleteInProgress OR channel.deviceStatus.aborted THEN RETURN; END; ENDLOOP; END; Put: PUBLIC ENTRY SAFE PROC [channel: ChannelHandle, data: CHARACTER] RETURNS [TTYPort.TransferStatus] = TRUSTED BEGIN faceTS: TTYFace.TransferStatus _ notReady; IF channel.deleteInProgress OR channel.deviceStatus.aborted THEN RETURN WITH ERROR ChannelQuiesced; WHILE faceTS = notReady DO WHILE ~channel.deviceStatus.readyToPut DO UpdateDeviceStatus[channel]; IF channel.deviceStatus.readyToPut THEN EXIT; WAIT channel.statusChange; IF channel.deleteInProgress THEN RETURN[abortedByDelete]; IF channel.deviceStatus.aborted THEN RETURN[aborted]; ENDLOOP; faceTS _ TTYFace.PutCommand[channel.lineNumber, data]; ENDLOOP; IF faceTS = breakDetected THEN channel.breakBeingProcessed _ FALSE; RETURN[IF channel.deleteInProgress THEN abortedByDelete ELSE IF channel.deviceStatus.aborted THEN aborted ELSE success]; END; SendBreak: PUBLIC ENTRY SAFE PROC [channel: ChannelHandle] = TRUSTED BEGIN IF channel.deleteInProgress OR channel.deviceStatus.aborted THEN RETURN WITH ERROR ChannelQuiesced; WHILE ~channel.deviceStatus.readyToPut DO UpdateDeviceStatus[channel]; IF channel.deviceStatus.readyToPut THEN EXIT; WAIT channel.statusChange; IF channel.deleteInProgress THEN RETURN; IF channel.deviceStatus.aborted THEN RETURN; ENDLOOP; <> END; Quiesce: PUBLIC ENTRY SAFE PROC [channel: ChannelHandle] = TRUSTED BEGIN IF channel.deleteInProgress OR channel.deviceStatus.aborted THEN RETURN WITH ERROR ChannelQuiesced; AbortStatusWaits[channel] END; SetParameter: PUBLIC ENTRY SAFE PROC [channel: ChannelHandle, parameter: TTYPort.Parameter] = TRUSTED BEGIN f: TTYFace.Parameter; IF channel.deleteInProgress OR channel.deviceStatus.aborted THEN RETURN WITH ERROR ChannelQuiesced; WITH parameter SELECT FROM characterLength => f _ [characterLength[characterLength]]; clearToSend => f _ [clearToSend[clearToSend]]; dataSetReady => f _ [dataSetReady[dataSetReady]]; lineSpeed => f _ [lineSpeed[lineSpeed]]; parity => f _ [parity[parity]]; stopBits => f _ [stopBits[stopBits]]; breakDetectedClear => BEGIN IF channel.breaksInBuffer = 0 THEN { channel.deviceStatus.breakDetected _ FALSE; BROADCAST channel.statusChange}; RETURN; END; ENDCASE => RETURN; TTYFace.SetParameter[channel.lineNumber, f]; END; -- Note deletion of the channel subsequent to calling StatusWait causes -- StatusWait to return normally. StatusWait: PUBLIC ENTRY SAFE PROC [channel: ChannelHandle, stat: TTYPort.DeviceStatus] RETURNS [newstat: TTYPort.DeviceStatus] = TRUSTED BEGIN IF channel.deleteInProgress OR channel.deviceStatus.aborted THEN RETURN WITH ERROR ChannelQuiesced; channel.statusWaitCount _ channel.statusWaitCount + 1; WHILE ~channel.deleteInProgress AND stat = channel.deviceStatus DO WAIT channel.statusChange ENDLOOP; channel.statusWaitCount _ channel.statusWaitCount - 1; newstat _ channel.deviceStatus; END; -- INTERNAL PROCEDURES. AbortStatusWaits: INTERNAL PROC [channel: ChannelHandle] = BEGIN channel.deviceStatus.aborted _ TRUE; WHILE channel.statusWaitCount > 0 DO BROADCAST channel.statusChange; WAIT aMoment ENDLOOP; channel.deviceStatus _ [, FALSE, FALSE, FALSE, FALSE, FALSE]; END; UpdateDeviceStatus: INTERNAL PROC [c: ChannelHandle] = BEGIN faceDS: TTYFace.DeviceStatus = TTYFace.GetStatus[c.lineNumber]; c.deviceStatus.dataTerminalReady _ faceDS.dataTerminalReady; c.deviceStatus.readyToGet _ faceDS.readyToGet; c.deviceStatus.readyToPut _ faceDS.readyToPut; c.deviceStatus.requestToSend _ faceDS.requestToSend; NOTIFY c.statusChange; END; -- MAIN PROGRAM Process.SetTimeout[@aMoment, Process.MsecToTicks[50]]; END.....