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, --
Types
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,
wakes up when microcode does something interesting, TxRdy/RxRdy
statusChange: CONDITION,
statusWaitCount: CARDINAL ← 0
];
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;
};
}...