-- File: RS232CDriverA.mesa
-- LastEdited: 20-Jan-82 12:42:17   By: Danielson  
-- This is the Monitor for RS232C Channel creation/deletion


DIRECTORY
  Heap USING [FreeNode, MakeNode],
  OISTransporter USING [Destroy, Initialize],
  Process USING [InitializeMonitor],
  RS232C USING [
    ChannelUseType, CommParamHandle, nullLineNumber, ReserveType, Suspend],
  RS232CControl USING [],
  RS232CFace USING [GetNextLine, GetStatus, Off, On, ResetLine],
  RS232CInternal USING [
    asynchronousDefaults, autoRecognitionDefaults, bitSynchronousDefaults,
    byteSynchronousDefaults, ChannelStatus, ChannelStatusHandle, DeleteChannel];

RS232CDriverA: MONITOR
  IMPORTS Heap, OISTransporter, Process, RS232CFace, RS232CInternal, RS232C
  EXPORTS RS232C, RS232CControl =
  BEGIN

  ChannelHandle: PUBLIC TYPE = RS232CInternal.ChannelStatusHandle;
  firstChannel: ChannelHandle ← NIL;
  channelCreateDelete: CONDITION;
  driverStarted: BOOLEAN ← TRUE;

  ChannelAlreadyExists: PUBLIC ERROR = CODE;
  ChannelInUse: PUBLIC ERROR = CODE;
  InvalidLineNumber: PUBLIC ERROR = CODE;
  NoRS232CHardware: PUBLIC ERROR = CODE;
  UnimplementedFeature: PUBLIC ERROR = CODE;

  --                                  Old Channel Owner's preemptMe
  --                                Never     If Inactive   Always
  --   New           Never          Don't        Don't      Don't
  -- preempt-    if Inactive        Don't       Preempt    Preempt
  --  Others        Always          Don't       Preempt    Preempt


  Create: PUBLIC ENTRY PROCEDURE [
    lineNumber: CARDINAL, commParams: RS232C.CommParamHandle,
    useType: RS232C.ChannelUseType,
    preemptOthers, preemptMe: RS232C.ReserveType]
    RETURNS [channel: ChannelHandle] =
    BEGIN
    ENABLE UNWIND => IF channel # NIL THEN FreeChannelHandle[channel];
    nextLine: CARDINAL ← RS232C.nullLineNumber;
    prevChannel: ChannelHandle;
    channel ← NIL;
    IF ~driverStarted OR RS232CFace.GetNextLine[nextLine] = nextLine THEN
      ERROR NoRS232CHardware;
    -- Check if line number valid
    UNTIL (nextLine ← RS232CFace.GetNextLine[nextLine]) = RS232C.nullLineNumber
      DO
      IF nextLine = lineNumber THEN EXIT;
      REPEAT FINISHED => ERROR InvalidLineNumber;
      ENDLOOP;
    -- If others in process of preempting, wait for them to finish
    UNTIL (prevChannel ← GetChannelHandle[lineNumber]) = NIL
      OR ~prevChannel.preemptInProgress DO WAIT channelCreateDelete; ENDLOOP;
    IF ~driverStarted THEN ERROR NoRS232CHardware;
    IF prevChannel # NIL THEN
      BEGIN
      preempt: BOOLEAN ← FALSE;
      SELECT preemptOthers FROM
        preemptNever => NULL;  -- fail
        preemptInactive =>
          SELECT prevChannel.lastPreemptMe FROM
            preemptInactive =>  -- if inactive, delete the old channel
              IF ~RS232CFace.GetStatus[prevChannel.face].dataSetReady THEN
                preempt ← TRUE;
            preemptAlways => preempt ← TRUE;
            ENDCASE => NULL;  --fail
        preemptAlways =>
          IF prevChannel.lastPreemptMe # preemptNever THEN preempt ← TRUE;
        ENDCASE;
      IF preempt THEN
        BEGIN
        ENABLE UNWIND => prevChannel.preemptInProgress ← FALSE;
        -- Give channel client a STRONG HINT to delete the channel at once.
        prevChannel.preemptInProgress ← TRUE;
        RS232C.Suspend[prevChannel, all];
        UNTIL prevChannel.deleted DO WAIT channelCreateDelete ENDLOOP;
        prevChannel.preemptInProgress ← FALSE;
        END
      ELSE ERROR ChannelInUse;
      -- Copy previously used channel handle so we don't allocate a 
      -- second one later
      channel ← prevChannel;
      END;
    -- Allocate and initialize a ChannelStatus record
    IF ~driverStarted THEN ERROR NoRS232CHardware;
    IF channel = NIL THEN
      BEGIN  -- Allocate channel handle and add it to queue of channel handles
      channel ← Heap.MakeNode[, SIZE[RS232CInternal.ChannelStatus]];
      channel.next ← firstChannel;
      firstChannel ← channel;
      END;
    -- set preemptInProgress first so any SIGNALS handle correctly
    channel.preemptInProgress ← FALSE;
    Process.InitializeMonitor[@channel.LOCK];
    channel.parameterRecord ←
      SELECT commParams.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.lineSpeed ← commParams.lineSpeed;
    channel.inputSuspended ← channel.outputSuspended ← channel.otherSuspended ←
      channel.deleted ← FALSE;
    channel.lastPreemptMe ← preemptMe;
    channel.lineNumber ← lineNumber;
    channel.statusWaitCount ← 0;
    channel.face ← RS232CFace.On[lineNumber];
    IF RS232CFace.ResetLine[channel.face, @channel.parameterRecord] # success
      THEN
      BEGIN
      RS232CFace.Off[channel.face];
      ERROR UnimplementedFeature;  -- (e.g. autoRecognition)
      END;
    IF (channel.useType ← useType) = oisCommunication THEN
      OISTransporter.Initialize[lineNumber, channel, commParams↑];
    BROADCAST channelCreateDelete;
    END;

  Delete: PUBLIC PROCEDURE [channel: ChannelHandle] =
    BEGIN
    -- Client is responsible for avoiding the passing of an OLD channel handle.
    IF channel.useType = oisCommunication THEN OISTransporter.Destroy[channel];
    RS232CInternal.DeleteChannel[channel];
    -- modify/release channel status record under object monitor
    RS232CFace.Off[channel.face];
    -- Release block to heap (cannot be done in DeleteChannel,
    -- since the MONITOR LOCK, which is in channel record, must still
    -- exist to be released at exit from DeleteChannel)
    GetLockAndFreeChannelHandle[channel];
    END;

  RedefineChannelUse: PUBLIC ENTRY PROCEDURE [
    channel: ChannelHandle, commParams: RS232C.CommParamHandle,
    useType: RS232C.ChannelUseType,
    preemptOthers, preemptMe: RS232C.ReserveType] =
    BEGIN
    ENABLE UNWIND => NULL;  -- release monitor lock
    channel.lastPreemptMe ← preemptMe;
    -- we rely on client to set the line type correctly !!
    IF (channel.useType ← useType) = oisCommunication THEN
      OISTransporter.Initialize[channel.lineNumber, channel, commParams↑];
    END;

  GetNextLine: PUBLIC PROCEDURE [lineNumber: CARDINAL]
    RETURNS [nextLineNumber: CARDINAL] =
    BEGIN RETURN[RS232CFace.GetNextLine[lineNumber]]; END;

  GetChannelHandle: INTERNAL PROCEDURE [lineNumber: CARDINAL]
    RETURNS [channel: ChannelHandle] =
    BEGIN
    channel ← firstChannel;
    FOR channel ← firstChannel, channel.next UNTIL channel = NIL DO
      IF channel.lineNumber = lineNumber THEN EXIT;
      ENDLOOP;
    END;

  Start: PUBLIC ENTRY PROCEDURE = BEGIN driverStarted ← TRUE END;
  
  Stop: PUBLIC ENTRY PROCEDURE [suspendActiveChannels: BOOLEAN] =
    BEGIN
    channel: ChannelHandle ← NIL;
    driverStarted ← FALSE;
    IF suspendActiveChannels THEN
      FOR channel ← firstChannel, channel.next UNTIL channel = NIL DO
        RS232C.Suspend[channel, all];
	ENDLOOP;
    UNTIL firstChannel = NIL DO WAIT channelCreateDelete ENDLOOP;
    END;
      
  GetLockAndFreeChannelHandle: ENTRY PROCEDURE [channel: ChannelHandle] =
    BEGIN
    ENABLE UNWIND => NULL;
    FreeChannelHandle[channel];
    END;
  
  FreeChannelHandle: INTERNAL PROCEDURE [channel: ChannelHandle] =
    BEGIN
    IF channel.preemptInProgress THEN channel.deleted ← TRUE
    ELSE
      BEGIN
      prevChannel: ChannelHandle ← firstChannel;
      IF prevChannel = channel THEN firstChannel ← channel.next
      ELSE
        BEGIN
        UNTIL prevChannel.next = channel DO
          prevChannel ← prevChannel.next ENDLOOP;
        prevChannel.next ← channel.next;
        END;
      Heap.FreeNode[, channel];
      END;
    BROADCAST channelCreateDelete;
    END;

  -- MAIN PROGRAM --

  END. -- RS232CDriverA

LOG

Time: 			 By: 			Action: Rubicon release
Time: 20-Jul-81 17:22:01 By: Danielson		Action: Combined RS232CManager and RS232C channel.
Time: 19-Aug-81 16:48:50 By: Danielson		Action: Hold monitor for less time (i.e. not
						 during all of Delete and BROADCAST on
						 channelCreateDelete whenever a channel
						 is created or deleted.
Time: 14-Sep-81 19:26:51 By: Danielson		Action: RS232CControl.Start and .Stop
Time: 13-Oct-81 16:32:54 By: Danielson		Action: Make Stop wait until no more channels
Time:  7-Jan-82 14:58:00 By: Danielson		Action: Set line speed on Create
Time: 20-Jan-82 11:41:49 By: Danielson		Action: Pass lineNumber to Router