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
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: BOOLEANFALSE,
breakBeingProcessed: BOOLEANFALSE,
myDataLost: BOOLEANFALSE,
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: PROCESSNIL,
wakeUpPtr: LONG POINTER TO CONDITIONNIL,
-- 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 BOOLEANALL[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;
TTYPortFace.SendBreak[channel.lineNumber];
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.....