-- File: RS232CDriverB.mesa
-- LastEdited:  7-Jan-82 14:57:16   By: Danielson  
-- This is the Object Monitor for RS232C ChannelStatus records  

DIRECTORY
  Process USING [SetTimeout],
  RS232C USING [
    AutoRecognitionOutcome, CompletionHandle, DeviceStatus, LineType,
    OperationClass, Parameter, PhysicalRecordHandle, TransferStatus,
    UnimplementedFeature],
  RS232CFace USING [
    AutoRecognitionWait, AbortInput, AbortOutput, AbortStatus, DeviceStatus,
    Get, GetStatus, Put, ResetLine, SendBreak, SetParameters, StatusWait,
    TransferStatus, TransferWait, TransmitNow],
  RS232CInternal USING [
    asynchronousDefaults, autoRecognitionDefaults, bitSynchronousDefaults,
    byteSynchronousDefaults, ChannelStatusHandle];

RS232CDriverB: MONITOR LOCKS channel USING channel: ChannelHandle
  IMPORTS Process, RS232C, RS232CFace EXPORTS RS232C, RS232CInternal =
  BEGIN
  ChannelHandle: PUBLIC TYPE = RS232CInternal.ChannelStatusHandle;
  ChannelSuspended: PUBLIC ERROR = CODE;
  InvalidParameter: PUBLIC ERROR = CODE;
  SendBreakIllegal: PUBLIC ERROR = CODE;
  aMoment: CONDITION;
  ConvertFaceToChannelTransferStatus: ARRAY RS232CFace.TransferStatus OF
    RS232C.TransferStatus = [
    success, dataLost, deviceError, frameTimeout, checksumError, parityError,
    asynchFramingError, invalidChar, invalidFrame, aborted, disaster];
  
   AutoRecognitionWait: PUBLIC PROCEDURE [channel: ChannelHandle]
    RETURNS [outcome: RS232C.AutoRecognitionOutcome] =
    BEGIN
    IF channel.otherSuspended THEN ERROR ChannelSuspended;
    outcome ← RS232CFace.AutoRecognitionWait[channel.face];
    END;

  DecrementStatusWaitCount: PRIVATE ENTRY PROCEDURE [channel: ChannelHandle] =
    BEGIN
    ENABLE UNWIND => NULL;  -- Required in order to release monitor lock
    channel.statusWaitCount ← channel.statusWaitCount - 1;
    END;

  DeleteChannel: PUBLIC ENTRY PROCEDURE [channel: ChannelHandle] =
    BEGIN
    ENABLE UNWIND => NULL;  -- Required in order to release monitor lock
    channel.inputSuspended ← channel.outputSuspended ← channel.otherSuspended ←
      TRUE;
    RS232CFace.AbortOutput[channel.face];
    RS232CFace.AbortInput[channel.face];
    RS232CFace.AbortStatus[channel.face];
    --This will complete calls to RS232CFace.StatusWait,
    --and consequently, calls to RS232C.StatusWait as well.
    channel.parameterRecord.dataTerminalReady ← FALSE;
    IF RS232CFace.SetParameters[channel.face, @channel.parameterRecord] #
      success THEN ERROR;  -- This should be logged as a system error;
    UNTIL channel.statusWaitCount = 0 DO WAIT aMoment; ENDLOOP;
    -- This guaranteees that Delete has completed.

    END;

  Get: PUBLIC ENTRY PROCEDURE [
    channel: ChannelHandle, rec: RS232C.PhysicalRecordHandle]
    RETURNS [RS232C.CompletionHandle] =
    BEGIN ENABLE UNWIND => NULL;
    IF channel.inputSuspended THEN ERROR ChannelSuspended;
    RETURN[RS232CFace.Get[channel.face, rec]];
    END;

  GetStatus: PUBLIC PROCEDURE [channel: ChannelHandle]
    RETURNS [stat: RS232C.DeviceStatus] =
    BEGIN
    faceStat: RS232CFace.DeviceStatus;
    IF channel.otherSuspended THEN ERROR ChannelSuspended;
    faceStat ← RS232CFace.GetStatus[channel.face];
    RETURN[
      [
        FALSE, faceStat.dataLost, faceStat.breakDetected, faceStat.clearToSend,
        faceStat.dataSetReady, faceStat.carrierDetect, faceStat.ringHeard,
        faceStat.ringIndicator, faceStat.deviceError]];
    END;

  IncrementStatusWaitCount: PRIVATE ENTRY PROCEDURE [channel: ChannelHandle] =
    BEGIN
    ENABLE UNWIND => NULL;  -- Required in order to release monitor lock
    channel.statusWaitCount ← channel.statusWaitCount + 1;
    END;

  Put: PUBLIC ENTRY PROCEDURE [
    channel: ChannelHandle, rec: RS232C.PhysicalRecordHandle]
    RETURNS [RS232C.CompletionHandle] =
    BEGIN ENABLE UNWIND => NULL;
    IF channel.outputSuspended THEN ERROR ChannelSuspended;
    RETURN[RS232CFace.Put[channel.face, rec]]
    END;

  Restart: PUBLIC ENTRY PROCEDURE [
    channel: ChannelHandle, class: RS232C.OperationClass] =
    BEGIN
    ENABLE UNWIND => NULL;  -- Required in order to release monitor lock
    SELECT class FROM
      input => channel.inputSuspended ← FALSE;
      output => channel.outputSuspended ← FALSE;
      other => channel.otherSuspended ← FALSE;
      all =>
        channel.inputSuspended ← channel.inputSuspended ← channel.otherSuspended
          ← FALSE;
      ENDCASE => ERROR;  -- This should be logged as a system error.

    END;

  SendBreak: PUBLIC PROCEDURE [channel: ChannelHandle] =
    BEGIN
    IF channel.outputSuspended THEN ERROR ChannelSuspended;
    IF channel.parameterRecord.lineType = byteSynchronous THEN
      ERROR SendBreakIllegal;
    RS232CFace.SendBreak[channel.face];
    END;

  SetLineType: PUBLIC ENTRY PROCEDURE [
    channel: ChannelHandle, lineType: RS232C.LineType] =
    BEGIN
    ENABLE UNWIND => NULL;  -- Required in order to release monitor lock
    holdDataTerminalReady: BOOLEAN;
    holdDataTerminalReady ← channel.parameterRecord.dataTerminalReady;
    channel.parameterRecord ←
      SELECT lineType FROM
        bitSynchronous => RS232CInternal.bitSynchronousDefaults,
        byteSynchronous => RS232CInternal.byteSynchronousDefaults,
        asynchronous => RS232CInternal.asynchronousDefaults,
        autoRecognition => RS232CInternal.autoRecognitionDefaults,
        ENDCASE => ERROR;  -- This should be logged as a system error.
    channel.parameterRecord.dataTerminalReady ← holdDataTerminalReady;
    --  This will prevent disconnect, if DTR had been set prior to this call
    IF RS232CFace.ResetLine[channel.face, @channel.parameterRecord] # success
      THEN ERROR RS232C.UnimplementedFeature;
    END;

  SetParameter: PUBLIC ENTRY PROCEDURE [
    channel: ChannelHandle, parameter: RS232C.Parameter] =
    BEGIN
    ENABLE UNWIND => NULL;  -- Required in order to release monitor lock
    WITH parameter SELECT FROM
      charLength => channel.parameterRecord.charLength ← charLength;
      correspondent => channel.parameterRecord.correspondent ← correspondent;
      dataTerminalReady =>
        channel.parameterRecord.dataTerminalReady ← dataTerminalReady;
      frameTimeout => channel.parameterRecord.frameTimeout ← frameTimeout;
      latchBitClear =>
        BEGIN
        IF latchBitClearMask.dataLost THEN
          channel.parameterRecord.resetDataLost ← TRUE;
        IF latchBitClearMask.breakDetected THEN
          channel.parameterRecord.resetBreakDetected ← TRUE;
        IF latchBitClearMask.ringHeard THEN
          channel.parameterRecord.resetRingHeard ← TRUE;
        END;
      lineSpeed => channel.parameterRecord.lineSpeed ← lineSpeed;
      parity => channel.parameterRecord.parity ← parity;
      requestToSend => channel.parameterRecord.requestToSend ← requestToSend;
      stopBits => channel.parameterRecord.stopBits ← stopBits;
      syncChar => channel.parameterRecord.syncChar ← syncChar;
      syncCount => channel.parameterRecord.syncCount ← syncCount;
      ENDCASE => ERROR InvalidParameter;
    IF RS232CFace.SetParameters[channel.face, @channel.parameterRecord] #
      success THEN ERROR RS232C.UnimplementedFeature;
    -- for next call to SetParameters, make certain that latched bits
    -- don't get cleared again
    channel.parameterRecord.resetDataLost ←
      channel.parameterRecord.resetBreakDetected ←
      channel.parameterRecord.resetRingHeard ← FALSE;
    END;

  StatusWait: PUBLIC PROCEDURE [
    channel: ChannelHandle, stat: RS232C.DeviceStatus]
    RETURNS [newstat: RS232C.DeviceStatus] =
    BEGIN
    faceStat: RS232CFace.DeviceStatus;
    -- Note that the deletion of the channel subsequent to the call
    -- to StatusWait will cause StatusWait to return normally.
    IF channel.otherSuspended THEN ERROR ChannelSuspended;
    stat.statusAborted ← FALSE;
    IncrementStatusWaitCount[channel];
    DO
      --until newstat#stat
      faceStat ← RS232CFace.StatusWait[
        channel.face, [
        stat.dataLost, stat.breakDetected, stat.clearToSend, stat.dataSetReady,
        stat.carrierDetect, stat.ringHeard, stat.ringIndicator,
        stat.deviceError]];
      newstat ← [
        channel.otherSuspended, faceStat.dataLost, faceStat.breakDetected,
        faceStat.clearToSend, faceStat.dataSetReady, faceStat.carrierDetect,
        faceStat.ringHeard, faceStat.ringIndicator, faceStat.deviceError];
      IF newstat # stat THEN EXIT;
      ENDLOOP;
    DecrementStatusWaitCount[channel];
    RETURN;
    END;

  Suspend: PUBLIC ENTRY PROCEDURE [
    channel: ChannelHandle, class: RS232C.OperationClass] =
    BEGIN
    ENABLE UNWIND => NULL;  -- Required in order to release monitor lock
    IF class = input OR class = all THEN
      BEGIN
      channel.inputSuspended ← TRUE;
      RS232CFace.AbortInput[channel.face];
      END;
    IF class = output OR class = all THEN
      BEGIN
      channel.outputSuspended ← TRUE;
      RS232CFace.AbortOutput[channel.face];
      END;
    IF class = other OR class = all THEN
      BEGIN
      channel.otherSuspended ← TRUE;
      UNTIL channel.statusWaitCount = 0 DO
        RS232CFace.AbortStatus[channel.face];
	WAIT aMoment;
	ENDLOOP;
      END;
    END;

  TransferWait: PUBLIC PROCEDURE [
    channel: ChannelHandle, event: RS232C.CompletionHandle]
    RETURNS [byteCount: CARDINAL, status: RS232C.TransferStatus] =
    BEGIN
    faceStatus: RS232CFace.TransferStatus;
    [byteCount, faceStatus] ← RS232CFace.TransferWait[channel.face, event];
    status ← ConvertFaceToChannelTransferStatus[faceStatus];
    RETURN;
    END;

  TransmitNow: PUBLIC PROCEDURE [
    channel: ChannelHandle, event: RS232C.CompletionHandle]
    RETURNS [byteCount: CARDINAL, status: RS232C.TransferStatus] =
    BEGIN
    faceStatus: RS232CFace.TransferStatus;
    [byteCount, faceStatus] ← RS232CFace.TransmitNow[channel.face, event];
    status ← ConvertFaceToChannelTransferStatus[faceStatus];
    RETURN;
    END;
  
  -- MAIN PROGRAM --

  Process.SetTimeout[@aMoment, 1];
  
  END. -- RS232CDriverB

LOG
Time: May 23, 1980  12:01 PM 	By: Victor Schwartz	Action: Pre-Amargosa log entries deleted.
Time: May 23, 1980  12:01 PM 	By: Victor Schwartz	Action: Start of modifications to
  handle multiple RS232C Channels.
Time: June 20, 1980  2:45 PM 	By: Victor Schwartz	Action: Change semantics of
  Suspend/Restart.  Remove Abort.
Time: August 4, 1980  2:53 PM 	By: Victor Schwartz	Action: Use Heap, rather than
  RS232CHeap to allocate storage.
Time: January 20, 1981  3:54 PM 	By: Danielson	Action: Changes required to support
  deviceError bit in deviceStatus.
Time:  3-Aug-81 18:23:06 	By: Danielson	Action: Pescadero changes for new 
  RS232CFace interface.
Time: 19-Aug-81 16:41:09 	By: Danielson	Action: Made Get and Put be entries
  and changed Suspend code to catch races between Get/Put and Suspend. 
Time: 14-Oct-81 19:13:38 	By: Danielson	Action: Ignore suspend on SetParameter and
  SetLineType