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..... TTYPortChannel.mesa Copyright Σ 1982, 1984, 1987 by Xerox Corporation. All rights reserved. Last edited: 17-Aug-82 16:03:41 By: Jim Frandeen Last edited: January 7, 1981 8:43 PM By: Forrest 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. sigh Tim Diebert: February 3, 1987 10:12:08 am PST TTYFace.SendBreak[channel.lineNumber]; Κ ˜codešœ™KšœI™IKšœ2™2Kšœ3™3KšœD™DKšœH™HKšœP™PKšœ™Kšœ-™-—K˜šΟk ˜ Kšœœ2˜?KšœœY˜lKšœœY˜fKšœœ„˜‘—K˜šΠlnœœœ œ˜BKšœ ˜'Kšœ˜Kšœ ˜Kš˜—˜Kšœœœœ˜/š œœœ œœ˜/Kšœœœ˜"Kšœœœ˜%Kšœ œœ˜Kšœ œ˜Kš œ&œœœœœœ˜PKšœ  œΟc*˜CKšœœ˜Kšœœ˜Kšœ œ˜Kšœ œ˜Kšœœœœ˜8Kšœ œœ˜š œ œœœ œœ˜+KšŸB˜B—Kšœ œ˜Kšœœ˜—K˜Kšœœ˜K˜Kšœœœ œ ˜LK˜Kšœœœœ˜*Kšœœœœ˜'Kšœœœœ˜'Kšœœœœ˜%K˜Kšœ$œœg˜ͺK˜Kšœ  œ˜Kšœ"˜"Kš œœœ œœœœ˜?Kšœ œœ˜6K˜KšŸ˜KšΟnœœœœœ œœœ˜K˜Kš  œœœœœœœ˜€K˜KšŸ'˜'š œœœœœœœ˜[š   œŸ œœœœ ˜@Kšœ"˜"Kšœ!˜!Kšœœ˜$Kšœ!˜!Kšœ˜—Kšœœ˜ Kšœœœ˜.Kšœœœ˜8Kšœœœ˜2Kšœœ˜ Kšœ“˜“KšœH˜HKšœ=˜=Kšœ˜Kšœœ˜)Kšœ˜—K˜š  œœœœœ˜Aš  œœœ˜:Kšœœ˜ Kš œ˜Kš œ˜Kšœ7œ˜@Kšœ˜Kšœ˜—Kšœ˜Kšœ˜Kšœ ˜ Kšœ:˜:Kšœ'œ˜-Kšœ˜K˜—š œœœœœœ œ$œ˜~šœœ˜@Kšœœœ˜"—Kšœ ˜ šœ˜"Kšœ˜Kšœœœ˜DKšœœœ˜@Kšœ˜—Kšœœ˜šœ7˜=Kšœ4˜4—Kšœ2˜2Kšœ2˜2Kšœ*œ˜Kš˜—š˜šœ œŸ!˜DKšœœ˜Kšœ7œ ˜ZKšœ˜——Kš œ˜Kšœ˜—Kšœ œ˜Kšœ˜Kšœ˜K˜Kšœ8˜8Kšœœœ˜ Kšœ4˜4Kšœœ˜:Kšœœ˜6šœ œœ˜IKšœ˜(Kšœ ˜$—Kšœœœœ˜HKšœ˜—Kšœ˜—Kšœ˜—K˜š œœœœœ  œœœ˜vKšœ*˜*šœœ˜@Kšœœœ˜"—šœ˜šœ"˜)Kšœ˜Kšœ!œœ˜-Kšœ˜Kšœœœ˜9Kšœœœ ˜5Kšœ˜—Kšœ6˜6Kšœ˜—Kšœœœ˜Cšœœœ˜7Kšœœœ œ ˜@—Kšœ˜—K˜š   œœœœœœ˜Jšœœ˜@Kšœœœ˜"—šœ"˜)Kšœ˜Kšœ!œœ˜-Kšœ˜Kšœœœ˜(Kšœœœ˜,Kšœ˜—Kšœ&™&Kšœ˜—Kšœ˜š  œœœœœœ˜Hšœœ˜@Kšœœœ˜"—Kšœ˜Kšœ˜—K˜š   œœœœœ:œ˜kKšœ˜šœœ˜@Kšœœœ˜"—šœ œ˜Kšœ:˜:Kšœ.˜.Kšœ1˜1Kšœ(˜(Kšœ˜Kšœ%˜%šœ˜Kšœœ˜$Kšœ%œ˜+Kš œ˜ Kšœ˜Kšœ˜—Kšœœ˜—Kšœ,˜,Kšœ˜—K˜KšŸG˜GKšŸ!˜!š  œœœœœ6œ#œ˜šœœ˜@Kšœœœ˜"—Kšœ6˜6šœœ˜?Kšœœ˜%—Kšœ6˜6Kšœ˜Kšœ˜—K˜KšŸ˜š œœœ˜@Kšœœ˜$šœ˜!Kš œœ œ˜8—Kš œœœœœœ˜=Kšœ˜—K˜š œœœ˜