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, -- SendBreak, -- SetParameter, TransferStatus]; TTYPortChannel: MONITOR LOCKS channel USING channel: ChannelHandle IMPORTS Process, PrincOpsUtils, TTYPortFace 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 TTYPortFace.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[TTYPortFace.GetLineCount[], 16]; -- External procs CharsAvailable: PUBLIC PROC [channel: ChannelHandle] RETURNS [number: CARDINAL] = {RETURN[GetInfo[channel].number]}; GetStatus: PUBLIC PROC [channel: ChannelHandle] RETURNS [TTYPort.DeviceStatus] = {RETURN[GetInfo[channel].status]}; -- ENTRY procs (and nested entry procs) Create: PUBLIC PROC [lineNumber: CARDINAL] RETURNS [ch: ChannelHandle] = 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]]; TTYPortFace.On[lineNumber, mask]; ch.interrupt _ FORK InterruptProcess[ch]; END; Delete: PUBLIC PROC [channel: ChannelHandle] = BEGIN DeleteChannel: ENTRY PROC [channel: ChannelHandle] = BEGIN channel.deleteInProgress _ TRUE; BROADCAST channel.wakeUpPtr^; BROADCAST channel.bufferInput; TTYPortFace.SetParameter[channel.lineNumber, [dataSetReady[FALSE]]]; AbortStatusWaits[channel]; END; DeleteChannel[channel]; JOIN channel.interrupt; TTYPortFace.Off[channel.lineNumber]; PrincOpsUtils.DeallocateNakedCondition[channel.wakeUpPtr]; channelIsCreated[channel.lineNumber] _ FALSE; END; Get: PUBLIC ENTRY PROC [channel: ChannelHandle] RETURNS [data: CHARACTER, status: TTYPort.TransferStatus] = 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: TTYPortFace.TransferStatus; status: TTYPort.TransferStatus; [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, status]; IF channel.deleteInProgress OR channel.deviceStatus.aborted THEN RETURN; END; ENDLOOP; END; Put: PUBLIC ENTRY PROC [channel: ChannelHandle, data: CHARACTER] RETURNS [TTYPort.TransferStatus] = BEGIN 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; RETURN[IF channel.deleteInProgress THEN abortedByDelete ELSE IF channel.deviceStatus.aborted THEN aborted ELSE success]; END; SendBreak: PUBLIC ENTRY PROC [channel: ChannelHandle] = 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 PROC [channel: ChannelHandle] = BEGIN IF channel.deleteInProgress OR channel.deviceStatus.aborted THEN RETURN WITH ERROR ChannelQuiesced; AbortStatusWaits[channel] END; SetParameter: PUBLIC ENTRY PROC [channel: ChannelHandle, parameter: TTYPort.Parameter] = BEGIN 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 => BEGIN IF channel.breaksInBuffer = 0 THEN { channel.deviceStatus.breakDetected _ FALSE; BROADCAST channel.statusChange}; RETURN; END; ENDCASE => RETURN; TTYPortFace.SetParameter[channel.lineNumber, f]; END; -- Note deletion of the channel subsequent to calling StatusWait causes -- StatusWait to return normally. StatusWait: PUBLIC ENTRY PROC [channel: ChannelHandle, stat: TTYPort.DeviceStatus] RETURNS [newstat: TTYPort.DeviceStatus] = 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: 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; END; -- MAIN PROGRAM Process.SetTimeout[@aMoment, Process.MsecToTicks[50]]; END..... TTYPortChannel.mesa Copyright (C) 1982, 1984 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: November 16, 1985 7:54:42 pm PST TTYPortFace.SendBreak[channel.lineNumber]; Ê ½˜codešœ™KšœE™EKšœ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šœ œœ!˜:K˜KšŸ˜Kš Ïnœœœœ œœ˜tK˜Kš   œœœœœ˜sK˜KšŸ'˜'š  œœœœœ˜Nš   œŸ œœœœ ˜@Kšœ"˜"Kšœ!˜!Kšœœ˜$Kšœ!˜!Kšœ˜—Kšœœ˜ Kšœœœ˜.Kšœœœ˜8Kšœœœ˜2Kšœœ˜ Kšœ“˜“KšœH˜HKšœ=˜=Kšœ!˜!Kšœœ˜)Kšœ˜—K˜š œœœ˜4š  œœœ˜:Kšœœ˜ Kš œ˜Kš œ˜Kšœ;œ˜DKšœ˜Kšœ˜—Kšœ˜Kšœ˜Kšœ$˜$Kšœ:˜:Kšœ'œ˜-Kšœ˜K˜—š  œœœœœ œ#˜qšœœ˜@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šœ<˜