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.
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 ~ {
Copied Types
DeviceStatus: TYPE ~ TTYPort.DeviceStatus;
Parameter: TYPE ~ TTYPort.Parameter;
TransferStatus: TYPE ~ TTYPort.TransferStatus;
Types
BufferRecord: TYPE ~ RECORD [
char: CHAR,
stat: TransferStatus
];
ChannelHandle: PUBLIC TYPE ~ REF ChannelStatus;
ChannelStatus: PUBLIC TYPE ~ MONITORED RECORD [
deleteInProgress: BOOLEANFALSE,
breakBeingProcessed: BOOLEANFALSE,
myDataLost: BOOLEANFALSE,
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: PROCESSNIL,
wakeUpPtr: LONG POINTER TO CONDITIONNIL,
wakes up when microcode does something interesting, TxRdy/RxRdy
statusChange: CONDITION,
statusWaitCount: CARDINAL ← 0
];
Constants
lineCount: CARDINAL ~ MIN [TTYPortFace.GetLineCount[], 16];
maxBufferSize: CARDINAL ~ 16;
ConvertFaceToChannelTransferStatus: ARRAY TTYPortFace.TransferStatus OF TransferStatus ~ [success, parityError, asynchFramingError, dataLost, breakDetected, aborted];
Global State
aMoment: CONDITION;
channelIsCreated: PACKED ARRAY [0..16) OF BOOLEANALL[FALSE];
csmPriority: Process.Priority ← 5;
Signals
ChannelAlreadyExists: PUBLIC ERROR ~ CODE;
ChannelQuiesced: PUBLIC ERROR ~ CODE;
InvalidLineNumber: PUBLIC ERROR ~ CODE;
NoTTYPortHardware: PUBLIC ERROR ~ CODE;
Procs
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]
};
Note deletion of the channel subsequent to calling StatusWait causes StatusWait to return normally.
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;
TTYPortFace.SendBreak[channel.lineNumber];
};
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]] };
}...