DIRECTORY Process USING [MsecToTicks, Priority, SetPriority, SetTimeout], PrincOpsUtils USING [AllocateNakedCondition, DeallocateNakedCondition, DisableInterrupts, EnableInterrupts], TTYPort USING [CharacterLength, DeviceStatus, LineSpeed, Parameter, Parity, StopBits, TransferStatus], TTYPortFace USING [DeviceStatus, GetCommand, GetLineCount, GetStatus, Off, On, Parameter, PutCommand, SetParameter, TransferStatus]; -- SendBreak, -- TTYPortChannel: CEDAR MONITOR LOCKS channel USING channel: ChannelHandle IMPORTS Process, PrincOpsUtils, TTYPortFace EXPORTS TTYPort SHARES TTYPort ~ { DeviceStatus: TYPE ~ TTYPort.DeviceStatus; Parameter: TYPE ~ TTYPort.Parameter; TransferStatus: TYPE ~ TTYPort.TransferStatus; BufferRecord: TYPE ~ RECORD [ char: CHAR, stat: TransferStatus ]; ChannelHandle: PUBLIC TYPE ~ REF ChannelStatus; ChannelStatus: PUBLIC TYPE ~ MONITORED RECORD [ deleteInProgress: BOOLEAN _ FALSE, breakBeingProcessed: BOOLEAN _ FALSE, myDataLost: BOOLEAN _ FALSE, lineNumber: CARDINAL, deviceStatus: 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, statusChange: CONDITION, statusWaitCount: CARDINAL _ 0 ]; lineCount: CARDINAL ~ MIN [TTYPortFace.GetLineCount[], 16]; maxBufferSize: CARDINAL ~ 16; ConvertFaceToChannelTransferStatus: ARRAY TTYPortFace.TransferStatus OF TransferStatus ~ [success, parityError, asynchFramingError, dataLost, breakDetected, aborted]; aMoment: CONDITION; channelIsCreated: PACKED ARRAY [0..16) OF BOOLEAN _ ALL[FALSE]; csmPriority: Process.Priority _ 5; ChannelAlreadyExists: PUBLIC ERROR ~ CODE; ChannelQuiesced: PUBLIC ERROR ~ CODE; InvalidLineNumber: PUBLIC ERROR ~ CODE; NoTTYPortHardware: PUBLIC ERROR ~ CODE; DeleteChannel: ENTRY PROC [channel: ChannelHandle] ~ TRUSTED { channel.deleteInProgress _ TRUE; BROADCAST channel.wakeUpPtr^; BROADCAST channel.bufferInput; TTYPortFace.SetParameter[channel.lineNumber, [dataSetReady[FALSE]]]; AbortStatusWaits[channel]; }; Get: PUBLIC ENTRY PROC [channel: ChannelHandle] RETURNS [data: CHAR, status: TransferStatus] ~ { 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; }; GetInfo: ENTRY PROC [channel: ChannelHandle] RETURNS [number: CARDINAL, status: DeviceStatus] ~ { IF channel.deleteInProgress OR channel.deviceStatus.aborted THEN RETURN WITH ERROR ChannelQuiesced; UpdateDeviceStatus[channel]; RETURN[channel.charsInBuffer, channel.deviceStatus]; }; InterruptProcess: ENTRY PROC [channel: ChannelHandle] ~ TRUSTED { data: CHAR; faceTS: TTYPortFace.TransferStatus; status: TransferStatus; Process.SetPriority[csmPriority]; DO WAIT channel.wakeUpPtr; UpdateDeviceStatus[channel]; IF channel.deleteInProgress OR channel.deviceStatus.aborted THEN RETURN; IF channel.deviceStatus.readyToGet THEN { [data, faceTS] _ TTYPortFace.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, data, status]; IF channel.deleteInProgress OR channel.deviceStatus.aborted THEN RETURN; }; ENDLOOP; }; Put: PUBLIC ENTRY PROC [channel: ChannelHandle, data: CHAR] RETURNS [status: TransferStatus] ~ { faceTS: TTYPortFace.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 _ TTYPortFace.PutCommand[channel.lineNumber, data]; ENDLOOP; IF faceTS = breakDetected THEN channel.breakBeingProcessed _ FALSE; status _ SELECT TRUE FROM channel.deleteInProgress => abortedByDelete, channel.deviceStatus.aborted => aborted, ENDCASE => success; }; Quiesce: PUBLIC ENTRY PROC [channel: ChannelHandle] ~ { IF channel.deleteInProgress OR channel.deviceStatus.aborted THEN RETURN WITH ERROR ChannelQuiesced; AbortStatusWaits[channel] }; StatusWait: PUBLIC ENTRY PROC [channel: ChannelHandle, stat: DeviceStatus] RETURNS [newstat: DeviceStatus] ~ { 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; }; SendBreak: PUBLIC ENTRY PROC [channel: ChannelHandle] ~ { 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; }; SetParameter: PUBLIC ENTRY PROC [channel: ChannelHandle, parameter: Parameter] ~ TRUSTED { f: TTYPortFace.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 => { IF channel.breaksInBuffer = 0 THEN { channel.deviceStatus.breakDetected _ FALSE; BROADCAST channel.statusChange; }; RETURN; }; ENDCASE => RETURN; TTYPortFace.SetParameter[channel.lineNumber, f]; }; AbortStatusWaits: INTERNAL PROC [channel: ChannelHandle] ~ { channel.deviceStatus.aborted _ TRUE; WHILE channel.statusWaitCount > 0 DO BROADCAST channel.statusChange; WAIT aMoment; ENDLOOP; channel.deviceStatus _ [, FALSE, FALSE, FALSE, FALSE, FALSE]; }; CharsAvailable: PUBLIC PROC [channel: ChannelHandle] RETURNS [number: CARDINAL] ~ { RETURN[GetInfo[channel].number]; }; CheckCreated: PROC [lineNumber: CARDINAL] RETURNS [b: BOOLEAN] ~ TRUSTED { PrincOpsUtils.DisableInterrupts[]; b _ channelIsCreated[lineNumber]; channelIsCreated[lineNumber] _ TRUE; PrincOpsUtils.EnableInterrupts[]; }; Create: PUBLIC PROC [lineNumber: CARDINAL] RETURNS [ch: ChannelHandle] ~ TRUSTED { mask: WORD; IF lineCount = 0 THEN ERROR NoTTYPortHardware; IF lineNumber >= lineCount THEN ERROR InvalidLineNumber; IF CheckCreated[lineNumber] 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]]; TTYPortFace.On[lineNumber, mask]; ch.interrupt _ FORK InterruptProcess[ch]; }; Delete: PUBLIC PROC [channel: ChannelHandle] ~ TRUSTED { DeleteChannel[channel]; JOIN channel.interrupt; TTYPortFace.Off[channel.lineNumber]; PrincOpsUtils.DeallocateNakedCondition[channel.wakeUpPtr]; channelIsCreated[channel.lineNumber] _ FALSE; }; GetStatus: PUBLIC PROC [channel: ChannelHandle] RETURNS [DeviceStatus] ~ { RETURN[GetInfo[channel].status]; }; PutCharInBuff: INTERNAL PROC [channel: ChannelHandle, data: CHAR, status: TransferStatus] ~ { IF status # breakDetected THEN channel.breakBeingProcessed _ FALSE ELSE IF channel.breakBeingProcessed THEN RETURN; -- don't mess with continuing break IF channel.charsInBuffer < maxBufferSize THEN { IF status = breakDetected THEN { -- new break seen channel.breaksInBuffer _ channel.breaksInBuffer + 1; channel.breakBeingProcessed _ TRUE; channel.deviceStatus.breakDetected _ TRUE; BROADCAST channel.statusChange; }; channel.buffer[channel.topUnused] _ [data, status]; channel.charsInBuffer _ channel.charsInBuffer + 1; channel.topUnused _ (channel.topUnused + 1) MOD maxBufferSize; } ELSE { IF ~channel.myDataLost THEN { -- only do dataLost flagging once channel.myDataLost _ TRUE; channel.buffer[(channel.topUnused + maxBufferSize - 1) MOD maxBufferSize].stat _ dataLost; }; }; BROADCAST channel.bufferInput; }; UpdateDeviceStatus: INTERNAL PROC [c: ChannelHandle] ~ { faceDS: TTYPortFace.DeviceStatus = TTYPortFace.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; }; TRUSTED { Process.SetTimeout[@aMoment, Process.MsecToTicks[50]] }; }... ΜTTYPortChannel.mesa Copyright (C) 1982, 1984, 1986 by Xerox Corporation. All rights reserved. Tim Diebert: November 16, 1985 7:54:42 pm PST Bill Jackson (bj) July 31, 1986 2:08:53 am PDT Note: there is a short section in here where interrupts are off while checking to see if there is already a channel on the line. I don't know how to avoid this other than by using explicit monitorlocks throughout or using using multiple monitors. Copied Types Types wakes up when microcode does something interesting, TxRdy/RxRdy Constants Global State Signals Procs Note deletion of the channel subsequent to calling StatusWait causes StatusWait to return normally. TTYPortFace.SendBreak[channel.lineNumber]; Κ —˜codešœ™KšœK™KKšœ-™-K™.—K˜Kšœψ™ψK™šΟk ˜ Kšœœ2˜?KšœœY˜lKšœœY˜fKšœ œsΟcœ˜–—K˜š Οnœœœœ œ˜HKšœ$˜+Kšœ˜Kšœ ˜—K™šœ ™ K™Kšœ*˜*Kšœ$˜$Kšœ.˜.K™—šœ™K˜šœœœ˜Kšœœ˜ Kšœ˜Kšœ˜K˜—šœœœœ˜/K˜—š œœœ œœ˜/Kšœœœ˜"Kšœœœ˜%Kšœ œœ˜Kšœ œ˜Kš œœœœœœœ˜HKšœ  œž*˜CKšœœ˜Kšœœ˜Kšœ œ˜Kšœ œ˜Kšœœœœ˜8Kšœ œœ˜š œ œœœ œœ˜+Kšœ?™?—Kšœ œ˜Kšœœ˜Kšœ˜—K™—šœ ™ K˜Kšœ œœ!˜;Kšœœ˜KšŸ"œœœ_˜¦K™—šœ ™ K˜Kšœ  œ˜Kš œœœ œœœœ˜?Kšœ"˜"K™—šœ™K˜KšŸœœœœ˜*KšŸœœœœ˜%KšŸœœœœ˜'KšŸœœœœ˜'K˜—šœ™K˜šŸ œœœœ˜>Kšœœ˜ Kš œ˜Kš œ˜Kšœ;œ˜DKšœ˜Kšœ˜K˜—š Ÿœœœœœœ˜`šœœ˜;Kš œœœ˜'—Kšœ ˜ K˜šœ˜"Kšœ˜Kšœœœ˜DKšœœœ˜@Kšœ˜K˜—Kšœœ˜šœ7˜=Kšœ4˜4—Kšœ2˜2Kšœ2˜2Kšœ*œ˜Kšœ˜—š˜šœœž!˜@Kšœœ˜Kšœ7œ ˜ZKšœ˜—K˜K˜——Kš œ˜Kšœ˜K˜K˜—šŸœœœœ˜8KšœG˜GK˜Kšœ<˜