?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE CM : Manage Interface Tables' ??
MODULE cmm$manage_interface_tables;
?? RIGHT := 110 ??

{ PURPOSE:
{   This module contains the procedures to build and manage I/O interface tables.  The Physical
{   Configuration Table and the State Info Table are used to build interface tables.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc cmc$logical_unit_constants
*copyc cmc$max_pp_per_iou
*copyc cme$logical_configuration_mgr
*copyc cme$manage_interface_tables
*copyc cme$physical_configuration_mgr
*copyc cme$reserve_element
*copyc cmt$access_elements
*copyc cmt$channel_ordinal
*copyc cmt$device_information
*copyc cmt$element_connection
*copyc cmt$element_descriptor
*copyc cmt$element_reservation
*copyc cmt$element_state
*copyc cmt$hardware_address
*copyc cmt$lcu_lock
*copyc cmt$lcu_lock_operation
*copyc cmt$lcu_lock_type
*copyc cmt$physical_address_specifier
*copyc cmt$mass_storage_information
*copyc cmt$physical_configuration
*copyc cmt$physical_identification
*copyc cmt$pp_program_description
*copyc cmt$unit_type
*copyc dft$one_word_response_handler
*copyc dmc$cti_device_type_numbers
*copyc dmt$error_condition_codes
*copyc dst$device_path
*copyc dst$number_of_ious
*copyc iot$unit_interface_table
*copyc jmt$system_supplied_name
*copyc jmt$user_supplied_name
*copyc mmt$attribute_keyword
*copyc mmt$rma_list
*copyc nat$channel_descriptor
*copyc nat$network_descriptor
*copyc osd$virtual_address
*copyc ose$heap_full_exceptions
*copyc ost$spaa_entry
*copyc pmt$mainframe_id

{Debug Code begin
*copyc jmv$jcb
*copyc oss$mainframe_pageable
*copyc osc$processor_defined_registers
*copyc ost$execution_control_block
{Debug Code end

?? POP ??
*copyc clp$convert_integer_to_string
*copyc cmp$convert_channel_number
*copyc cmp$convert_channel_ordinal
*copyc cmp$convert_iou_name
*copyc cmp$convert_iou_number
*copyc cmp$format_error_message
*copyc cmp$get_channel_def
*copyc cmp$get_controller_type
*copyc cmp$get_driver_info
*copyc cmp$get_element_name_via_lun
*copyc cmp$get_element_state
*copyc cmp$get_logical_unit_number
*copyc cmp$get_response_handler
*copyc cmp$get_unit_type
*copyc cmp$load_controller_module
*copyc cmp$pc_get_element
*copyc cmp$pc_get_logical_unit
*copyc cmp$pc_get_next_channel
*copyc cmp$retrieve_iou_definition
*copyc cmp$support_redundant_channel
*copyc dsp$allocate_continuous_memory
*copyc dsp$move_pp_driver
*copyc dsp$move_pp_overlays
*copyc dsp$retrieve_iou_information
*copyc i#real_memory_address
*copyc osp$append_status_integer
*copyc osp$append_status_parameter
*copyc osp$clear_signature_lock
*copyc osp$set_locked_variable
*copyc osp$set_signature_lock
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc osp$system_error
*copyc osp$test_signature_lock
*copyc pmp$delay
*copyc pmp$get_job_names
*copyc pmp$zero_out_table
?? EJECT ??
*copyc cmv$configuration_administrator
*copyc cmv$element_reservation_lock
*copyc cmv$logical_pp_table_lock
*copyc cmv$logical_unit_lock
*copyc cmv$removable_media_operation

*copyc cmv$assignable_device
*copyc cmv$configuration_activated
*copyc cmv$default_response_handler
*copyc cmv$iou_table_p
*copyc cmv$logical_pp_table_p
*copyc cmv$logical_unit_table
*copyc cmv$max_number_of_pp
*copyc cmv$peripheral_element_table
*copyc cmv$physical_configuration
*copyc cmv$state_info_table
*copyc iov$initial_queue_lock_sc
*copyc osv$iou_external_interrupt
*copyc osv$mainframe_wired_cb_heap
*copyc osv$spi_response_processor
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  TYPE
    t$unit_descriptor = record
      configured: boolean,
      channel: cmt$physical_channel,
      controller_number: cmt$physical_equipment_number,
      storage_directory_address: 0 .. 7,
      unit_number: 0 .. 1fff(16),
    recend;

  TYPE
    t$lun_entry_lock_trace_list = array [0 .. 255] of t$lun_entry_lock_trace,
    t$lun_entry_lock_trace = record
      sjn: jmt$system_supplied_name,
      gtid: ost$global_task_id,
      lun: iot$logical_unit,
      clock: integer,
      case set_lock: boolean of
      = TRUE =
        lock_obtained: boolean,
      = FALSE =
        lock_released: boolean,
      casend,
    recend;

  VAR
    cmv$set_lun_sig_lock_set_count: [XDCL, #GATE, oss$mainframe_pageable] integer := 0,
    cmv$clear_lun_sig_lock_set_cnt: [XDCL, #GATE, oss$mainframe_pageable] integer := 0,
    cmv$lun_entry_lock_trace_index: [XDCL, #GATE, oss$mainframe_pageable] 0 .. 255 := 0,
    cmv$lun_entry_lock_trace_list: [XDCL, #GATE, oss$mainframe_pageable] t$lun_entry_lock_trace_list :=
          [REP 256 of ['', [0, 0], 0, 0, FALSE, FALSE]];

?? OLDTITLE ??
?? NEWTITLE := 'build_logical_unit_entry', EJECT ??

{ PURPOSE:
{   This procedure builds an entry in the logical unit table.  This procedure is called after the physical
{   configuration is installed and activated.

  PROCEDURE build_logical_unit_entry
    (    logical_unit: iot$logical_unit;
         unit_type: iot$unit_type;
     VAR status: ost$status);

    VAR
      ignore_status: ost$status,
      rma: integer,
      template_p: ^iot$unit_commun_buffer_template,
      unit_number_string: ost$string;

    status.normal := TRUE;

    IF logical_unit > UPPERBOUND (cmv$logical_unit_table^) THEN
      osp$set_status_abnormal (cmc$configuration_management_id, cme$it_pp_invalid_lun,
            'BUILD_LOGICAL_UNIT_ENTRY', status);
      osp$append_status_parameter (osc$status_parameter_delimiter, 'unit_number =', status);
      clp$convert_integer_to_string (logical_unit, 10, FALSE, unit_number_string, ignore_status);
      osp$append_status_parameter (osc$status_parameter_delimiter, unit_number_string.
            value (1, unit_number_string.size), status);
      osp$append_status_parameter (osc$status_parameter_delimiter, 'Logical unit out of range', status);
      RETURN; {----->
    IFEND;

    cmv$logical_unit_table^ [logical_unit].configured := TRUE;
    ALLOCATE cmv$logical_unit_table^ [logical_unit].unit_interface_table IN osv$mainframe_wired_cb_heap^;
    pmp$zero_out_table (#LOC (cmv$logical_unit_table^ [logical_unit].unit_interface_table^),
          #SIZE (cmv$logical_unit_table^ [logical_unit].unit_interface_table^));
    cmv$logical_unit_table^ [logical_unit].logical_unit_number := logical_unit;
    cmv$logical_unit_table^ [logical_unit].entry_interlock := FALSE;
    cmv$logical_unit_table^ [logical_unit].unit_interface_table^.logical_unit := logical_unit;
    cmv$logical_unit_table^ [logical_unit].unit_interface_table^.unit_type := unit_type;
    cmv$logical_unit_table^ [logical_unit].unit_interface_table^.next_request := NIL;

    { Element capability and element access needs to be initialized along with unit type by searching
    { the configuration.

    ALLOCATE template_p IN osv$mainframe_wired_cb_heap^;
    cmv$logical_unit_table^ [logical_unit].unit_communication_buffer_pva :=
          ^template_p^.unit_communication_buffer;
    pmp$zero_out_table (#LOC (cmv$logical_unit_table^ [logical_unit].unit_communication_buffer_pva^),
          #SIZE (cmv$logical_unit_table^ [logical_unit].unit_communication_buffer_pva^));

    i#real_memory_address (#LOC (cmv$logical_unit_table^ [logical_unit].unit_communication_buffer_pva^), rma);
    cmv$logical_unit_table^ [logical_unit].unit_interface_table^.unit_communication_buffer_rma := rma;
    cmv$logical_unit_table^ [logical_unit].unit_interface_table^.unit_commun_buffer_length :=
          ioc$unit_commun_buffer_length * 8;
    cmv$logical_unit_table^ [logical_unit].status.assignable_device := TRUE;
    cmv$logical_unit_table^ [logical_unit].element_access := $cmt$element_access [cmc$read, cmc$write];
    cmv$logical_unit_table^ [logical_unit].element_capability :=
          $cmt$element_capabilities [cmc$volume_assignment, cmc$io_request_submission,
          cmc$dedicated_maintenance];

  PROCEND build_logical_unit_entry;
?? OLDTITLE ??
?? NEWTITLE := 'build_pp_interface_table', EJECT ??

{ PURPOSE:
{   This procedure builds the logical pp table for the boot, system core and the Physical Configuration.

  PROCEDURE build_pp_interface_table
    (    pp_count: iot$pp_number;
         requested_unit_count: iot$logical_unit;
         allocate_entire_configuration: boolean;
         actual_logical_unit_table_p: ^cmt$logical_unit_table;
     VAR actual_logical_pp_table_p: ^cmt$logical_pp_table;
     VAR status: ost$status);

    CONST
      c$extra_logical_pp = 10,
      procedure_name = 'BUILD_PP_INTERFACE_TABLE';

    TYPE
      t$cio_channel_used = record
        configured: boolean,
        pp_index: iot$pp_number,
      recend;

    VAR
      channel_element_p: ^cmt$element_definition,
      cio_channel_used_p: ^array [0 .. * ] of array [ost$physical_channel_number] of t$cio_channel_used,
      cip_driver_name: dst$driver_name,
      controller_element_p: ^cmt$element_definition,
      controller_type: cmt$controller_type,
      current_channel: 0 .. 0ff(16),
      dual_pp: boolean,
      found: boolean,
      io_unit_type: iot$unit_type,
      iou_definition: cmt$iou_definition,
      iou_information_table: dst$iou_information_table,
      iou_number: dst$iou_number,
      iou_program_name: pmt$program_name,
      local_status: ost$status,
      logical_pp_table_entry_p: ^cmt$logical_pp_table_entry,
      logical_pp_table_p: ^cmt$logical_pp_table,
      logical_unit_number: iot$logical_unit,
      low_unit: iot$logical_unit,
      max_unit_count: iot$logical_unit,
      number_of_ious: dst$number_of_ious,
      physical_eq_number: cmt$physical_equipment_number,
      physical_unit_number: cmt$physical_unit_number,
      pp: iot$pp_number,
      pp_index: iot$pp_number,
      pps_needed: 0 .. 0ff(16),
      prev_commun_buffer_p: ^iot$communication_buffer,
      table_size: iot$pp_number,
      table_unit_desc_p: ^iot$unit_descriptors,
      temp_unit_des_p: ^array [ * ] of t$unit_descriptor,
      unit_class: cmt$unit_class,
      unit_element_p: ^cmt$element_definition,
      unit_type: cmt$unit_type,
      upper_unit: iot$logical_unit;


    status.normal := TRUE;

  /main_program/
    BEGIN
      max_unit_count := requested_unit_count + cmc$reserved_unit_count;
      PUSH temp_unit_des_p: [(cmc$reserved_unit_count + 1) .. max_unit_count];

      { Allocate the local Logical PP Table.  For the I4/I4S allocate extra logical PPs to allow PP
      { reservation if all slots are taken by system elements being turned OFF.  This is done only on I4/I4S
      { models because the maximum number of PP of 30 per iou is reached.

      IF allocate_entire_configuration THEN
        dsp$retrieve_iou_information (number_of_ious, iou_information_table);
        IF (iou_information_table [1].model_type = dsc$imn_i4_40_model) OR
              (iou_information_table [1].model_type = dsc$imn_i4_42_model) THEN
          table_size := (number_of_ious * cmc$max_pp_per_iou) + c$extra_logical_pp;
        ELSE
          table_size := number_of_ious * cmc$max_pp_per_iou;
        IFEND;
      ELSE
        table_size := pp_count;
      IFEND;
      ALLOCATE logical_pp_table_p: [1 .. table_size] IN osv$mainframe_wired_cb_heap^;
      pmp$zero_out_table (#LOC (logical_pp_table_p^), #SIZE (logical_pp_table_p^));
      FOR pp_index := 1 TO table_size DO
        logical_pp_table_entry_p := ^logical_pp_table_p^ [pp_index];
        logical_pp_table_entry_p^.pp_info.pp_interface_table_p := NIL;
        logical_pp_table_entry_p^.pp_info.pp_communication_buffer_p := NIL;
        logical_pp_table_entry_p^.pp_info.channel_interlock_p := NIL;
        logical_pp_table_entry_p^.pp_info.driver_code_p := NIL;
        logical_pp_table_entry_p^.pp_info.saved_io_request_p := NIL;
        logical_pp_table_entry_p^.handlers.response_handler_p := NIL;
        logical_pp_table_entry_p^.handlers.one_word_response_handler_p := NIL;

        logical_pp_table_entry_p^.task_info.gtid.index := 4095;
        logical_pp_table_entry_p^.task_info.gtid.seqno := 255;
        logical_pp_table_entry_p^.task_info.reserved_job_name := ' ';
        logical_pp_table_entry_p^.pp_info.physical_pp.iou_number := 0;
        logical_pp_table_entry_p^.pp_info.physical_pp.number := 33(8);
        logical_pp_table_entry_p^.pp_info.physical_pp.channel_protocol := dsc$cpt_nio;
        logical_pp_table_entry_p^.pp_info.driver_name := ' ';
        logical_pp_table_entry_p^.pp_info.cip_driver_name := ' ';
        logical_pp_table_entry_p^.controller_info.controller_type := cmc$null_controller;
      FOREND;
      cmv$logical_pp_table_lock.lock_id := 0;

      { Initialize the CIO channel used table.  This table is used to save the PP index number for CIO
      { channels with multiple ports.

      PUSH cio_channel_used_p: [0 .. UPPERBOUND (cmv$iou_table_p^)];
      pmp$zero_out_table (#LOC (cio_channel_used_p^), #SIZE (cio_channel_used_p^));

      pp := 1;
      current_channel := 0;

    /build_pp_table_loop/
      WHILE TRUE DO
        low_unit := ioc$max_unit_number;
        upper_unit := 0;

        { Retrieve a channel from the Physical Configuration, exit loop if no more were found.

        cmp$pc_get_next_channel (current_channel, channel_element_p, local_status);
        IF NOT local_status.normal THEN
          EXIT /build_pp_table_loop/; {----->
        IFEND;
        current_channel := current_channel + 1;

        cmp$retrieve_iou_definition (channel_element_p^.data_channel.iou, iou_definition, status);
        IF NOT status.normal THEN
          EXIT /main_program/; {----->
        IFEND;
        IF ((channel_element_p^.data_channel.number >= 12) AND
              (channel_element_p^.data_channel.number <= 15)) OR
              ((channel_element_p^.data_channel.concurrent) AND
              (channel_element_p^.data_channel.number > 25)) OR
              ((iou_definition.kind = dsc$imn_i4_44_model) AND
              (channel_element_p^.data_channel.number <= 1)) OR
              ((iou_definition.kind = dsc$imn_i4_46_model) AND (channel_element_p^.data_channel.number <= 1))
              THEN
          pp_interface_table_error (procedure_name, pp, channel_element_p^.data_channel.number, -1, -1, -1,
                cme$it_pp_invalid_channel, channel_element_p^.element_name, 'Invalid channel', status);
          EXIT /main_program/; {----->
        IFEND;

        FOR logical_unit_number := LOWERBOUND (temp_unit_des_p^) TO UPPERBOUND (temp_unit_des_p^) DO
          temp_unit_des_p^ [logical_unit_number].configured := FALSE;
          temp_unit_des_p^ [logical_unit_number].channel.number := channel_element_p^.data_channel.number;
          temp_unit_des_p^ [logical_unit_number].channel.port := channel_element_p^.data_channel.port;
          temp_unit_des_p^ [logical_unit_number].channel.concurrent :=
                channel_element_p^.data_channel.concurrent;
          temp_unit_des_p^ [logical_unit_number].controller_number := 0;
          temp_unit_des_p^ [logical_unit_number].storage_directory_address := 0;
          temp_unit_des_p^ [logical_unit_number].unit_number := 0;
        FOREND;

      /equipment_number_loop/
        FOR physical_eq_number := LOWERVALUE (cmt$physical_equipment_number)
              TO UPPERVALUE (cmt$physical_equipment_number) DO
          IF NOT channel_element_p^.data_channel.connection.equipment [physical_eq_number].configured THEN
            CYCLE /equipment_number_loop/; {----->
          IFEND;

          cmp$pc_get_element (channel_element_p^.data_channel.connection.equipment [physical_eq_number].
                element_name, channel_element_p^.data_channel.iou, controller_element_p, status);
          IF NOT status.normal THEN
            EXIT /main_program/; {----->
          IFEND;

          CASE controller_element_p^.element_type OF
          = cmc$controller_element, cmc$external_processor_element =
            cmp$get_controller_type (controller_element_p^.product_id, controller_type, local_status);
            IF NOT local_status.normal THEN { foreign device }
              CYCLE /build_pp_table_loop/; {----->
            IFEND;
            IF controller_element_p^.element_type = cmc$controller_element THEN
              iou_program_name := controller_element_p^.controller.peripheral_driver_name;
            ELSE { controller_element_p^.element_type = cmc$external_processor_element }
              iou_program_name := controller_element_p^.external_processor.peripheral_driver_name;
            IFEND;

          = cmc$channel_adapter_element, cmc$communications_element =

            { Do not build table entries for these devices.

            CYCLE /build_pp_table_loop/; {----->
          ELSE
          CASEND;

          { Get physical unit numbers.

          IF controller_element_p^.element_type = cmc$controller_element THEN

          /unit_number_loop/
            FOR physical_unit_number := LOWERVALUE (physical_unit_number)
                  TO UPPERVALUE (physical_unit_number) DO
              IF NOT controller_element_p^.controller.connection.unit [physical_unit_number].configured THEN
                CYCLE /unit_number_loop/; {----->
              IFEND;

              cmp$pc_get_element (controller_element_p^.controller.connection.unit [physical_unit_number].
                    element_name, channel_element_p^.data_channel.iou, unit_element_p, status);
              IF NOT status.normal THEN
                EXIT /main_program/; {----->
              IFEND;
              IF unit_element_p^.element_type <> cmc$storage_device_element THEN
                pp_interface_table_error (procedure_name, pp, channel_element_p^.data_channel.number,
                      controller_element_p^.controller.physical_equipment_number, physical_unit_number, -1,
                      cme$it_pp_not_data_type, controller_element_p^.controller.connection.
                      unit [physical_unit_number].element_name, 'Expecting data_type', status);
                EXIT /main_program/; {----->
              IFEND;

              cmp$get_unit_type (unit_element_p^.product_id, unit_type, io_unit_type, unit_class, found);
              IF NOT found AND (io_unit_type = ioc$dt_foreign_device) THEN
                CYCLE /build_pp_table_loop/; {----->
              IFEND;
              cmp$get_logical_unit_number (unit_element_p^.element_name, logical_unit_number, status);
              IF NOT status.normal THEN
                EXIT /main_program/; {----->
              IFEND;
              IF logical_unit_number > max_unit_count THEN
                pp_interface_table_error (procedure_name, pp, channel_element_p^.data_channel.number,
                      controller_element_p^.controller.physical_equipment_number, physical_unit_number,
                      logical_unit_number, cme$it_pp_invalid_lun, unit_element_p^.element_name,
                      'Logical_unit_number > max_unit_count', status);
                EXIT /main_program/; {----->
              IFEND;

              IF logical_unit_number = 0 THEN
                CYCLE /unit_number_loop/; {----->
              IFEND;

              { Update unit range.

              IF low_unit > logical_unit_number THEN
                low_unit := logical_unit_number;
              IFEND;
              IF upper_unit < logical_unit_number THEN
                upper_unit := logical_unit_number;
              IFEND;

              { Save the physical_path.

              temp_unit_des_p^ [logical_unit_number].configured := TRUE;
              temp_unit_des_p^ [logical_unit_number].controller_number :=
                    controller_element_p^.controller.physical_equipment_number;
              IF controller_type = cmc$ms7165_2x THEN
                temp_unit_des_p^ [logical_unit_number].storage_directory_address :=
                      controller_element_p^.controller.physical_equipment_number;
              ELSE
                temp_unit_des_p^ [logical_unit_number].storage_directory_address := 0;
              IFEND;
              IF (unit_element_p^.product_id.product_number = '  $895') AND
                    (unit_element_p^.product_id.model_number = '1  ') THEN
                temp_unit_des_p^ [logical_unit_number].unit_number :=
                      unit_element_p^.storage_device.physical_unit_number + 1000(16);
              ELSE
                temp_unit_des_p^ [logical_unit_number].unit_number :=
                      unit_element_p^.storage_device.physical_unit_number;
              IFEND;
            FOREND /unit_number_loop/;

          ELSE { Element is not a controller }
            cmp$get_unit_type (controller_element_p^.product_id, unit_type, io_unit_type, unit_class, found);
            IF NOT found THEN
              CYCLE /build_pp_table_loop/; {----->
            IFEND;
            cmp$get_logical_unit_number (channel_element_p^.data_channel.connection.
                  equipment [physical_eq_number].element_name, logical_unit_number, status);
            IF NOT status.normal THEN
              EXIT /main_program/; {----->
            IFEND;
            IF logical_unit_number > max_unit_count THEN
              pp_interface_table_error (procedure_name, pp, channel_element_p^.data_channel.number,
                    physical_eq_number, 0, logical_unit_number, cme$it_pp_invalid_lun,
                    controller_element_p^.element_name, 'Logical_unit_number > max_unit_count', status);
              EXIT /main_program/; {----->
            IFEND;

            IF logical_unit_number <> 0 THEN
              IF low_unit > logical_unit_number THEN
                low_unit := logical_unit_number;
              IFEND;
              IF upper_unit < logical_unit_number THEN
                upper_unit := logical_unit_number;
              IFEND;
            IFEND;

            { Save the physical_path.

            temp_unit_des_p^ [logical_unit_number].configured := TRUE;
            temp_unit_des_p^ [logical_unit_number].controller_number := physical_eq_number;
            temp_unit_des_p^ [logical_unit_number].storage_directory_address := 0;
            temp_unit_des_p^ [logical_unit_number].unit_number := 0;

            IF unit_type = cmc$mshydra THEN
              controller_type := cmc$mshydra_ct;
              iou_program_name := 'E9S887';
            IFEND;
          IFEND; {element_type check}

        FOREND /equipment_number_loop/;

        cmp$convert_iou_name (channel_element_p^.data_channel.iou, iou_number, status);
        IF NOT status.normal THEN
          EXIT /main_program/; {----->
        IFEND;

        cmp$get_driver_info (iou_program_name, channel_element_p^.data_channel.concurrent, cip_driver_name,
              dual_pp, status);
        IF NOT status.normal THEN
          EXIT /main_program/; {----->
        IFEND;

        IF dual_pp THEN
          pps_needed := 2;
        ELSE
          pps_needed := 1;
        IFEND;

      /build_table_entry/
        FOR pp_index := 1 TO pps_needed DO
          IF upper_unit = 0 THEN
            CYCLE /build_table_entry/; {----->
          IFEND;

          { If both ports of a channel exists then combine the information.

          IF channel_element_p^.data_channel.concurrent AND cio_channel_used_p^ [iou_number]
                [channel_element_p^.data_channel.number].configured THEN
            PUSH table_unit_desc_p: [low_unit .. upper_unit];
            pmp$zero_out_table (#LOC (table_unit_desc_p^), #SIZE (table_unit_desc_p^));
            build_unit_descriptors (low_unit, upper_unit, actual_logical_unit_table_p, channel_element_p,
                  temp_unit_des_p, controller_type, table_unit_desc_p);
            combine_table_entry (low_unit, upper_unit, table_unit_desc_p,
                  cio_channel_used_p^ [iou_number] [channel_element_p^.data_channel.number].pp_index,
                  logical_pp_table_p, status);
            IF NOT status.normal THEN
              EXIT /main_program/; {----->
            IFEND;
            CYCLE /build_table_entry/; {----->
          IFEND;

          logical_pp_table_p^ [pp].flags.entry_in_use := TRUE;
          logical_pp_table_p^ [pp].flags.entry_reserved_by_nosve := TRUE;
          retrieve_logical_pp_flags (cip_driver_name, pp, logical_pp_table_p);

          IF dual_pp THEN
            IF pp_index = 1 THEN
              logical_pp_table_p^ [pp].pp_info.logical_partner_pp_index := pp + 1;
            ELSE
              logical_pp_table_p^ [pp].pp_info.logical_partner_pp_index := pp - 1;
            IFEND;
          IFEND;

          logical_pp_table_p^ [pp].pp_info.channel.iou_number := iou_number;
          IF channel_element_p^.data_channel.concurrent THEN
            logical_pp_table_p^ [pp].pp_info.channel.channel_protocol := dsc$cpt_cio;
          ELSE
            logical_pp_table_p^ [pp].pp_info.channel.channel_protocol := dsc$cpt_nio;
          IFEND;
          logical_pp_table_p^ [pp].pp_info.channel.number := channel_element_p^.data_channel.number;
          logical_pp_table_p^ [pp].pp_info.channel_port := channel_element_p^.data_channel.port;

          logical_pp_table_p^ [pp].pp_info.driver_name := iou_program_name (1, 7);
          logical_pp_table_p^ [pp].pp_info.cip_driver_name := cip_driver_name;

          logical_pp_table_p^ [pp].controller_info.controller_type := controller_type;

          cmp$get_response_handler (controller_type, logical_pp_table_p^ [pp].handlers.response_handler_p);

          setup_pp_table (pp, channel_element_p^.data_channel.concurrent, TRUE, low_unit, upper_unit,
                logical_pp_table_p, status);
          IF NOT status.normal THEN
            EXIT /main_program/; {----->
          IFEND;

          logical_pp_table_p^ [pp].flags.configured := TRUE;

          IF pp_index = 1 THEN
            prev_commun_buffer_p := logical_pp_table_p^ [pp].pp_info.pp_communication_buffer_p;

          ELSEIF prev_commun_buffer_p = NIL THEN
            osp$set_status_condition (cme$no_pp_communication_buffer, status);
            osp$append_status_integer (osc$status_parameter_delimiter, pp, 16, TRUE, status);
            EXIT /main_program/; {----->

          ELSE
            prev_commun_buffer_p^.partner_pp := logical_pp_table_p^ [pp].pp_info.pp_interface_table_rma;
            logical_pp_table_p^ [pp].pp_info.pp_communication_buffer_p^.partner_pp :=
                  logical_pp_table_p^ [pp - 1].pp_info.pp_interface_table_rma;
            prev_commun_buffer_p^.slave := FALSE;
            logical_pp_table_p^ [pp].pp_info.pp_communication_buffer_p^.slave := TRUE;
          IFEND;

          { Initialize unit descriptors in the pp_interface_table_p.

          table_unit_desc_p := ^logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.unit_descriptors;
          build_unit_descriptors (low_unit, upper_unit, actual_logical_unit_table_p, channel_element_p,
                temp_unit_des_p, controller_type, table_unit_desc_p);

          IF channel_element_p^.data_channel.concurrent AND (channel_element_p^.data_channel.port <>
                cmc$unspecified_port) THEN
            cio_channel_used_p^ [iou_number] [channel_element_p^.data_channel.number].configured := TRUE;
            cio_channel_used_p^ [iou_number] [channel_element_p^.data_channel.number].pp_index := pp;
          IFEND;

          pp := pp + 1;
        FOREND /build_table_entry/;

      WHILEND /build_pp_table_loop/;

    END /main_program/;

    IF status.normal THEN
      actual_logical_pp_table_p := logical_pp_table_p;
    ELSE
      IF logical_pp_table_p <> NIL THEN
        FOR pp_index := LOWERBOUND (logical_pp_table_p^) TO UPPERBOUND (logical_pp_table_p^) DO
          IF logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_p <> NIL THEN
            IF logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_p^.response_buffer <> NIL THEN
              FREE logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_p^.response_buffer IN
                    osv$mainframe_wired_cb_heap^;
            IFEND;
            FREE logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_p IN osv$mainframe_wired_cb_heap^;
          IFEND;
          IF logical_pp_table_p^ [pp_index].pp_info.pp_communication_buffer_p <> NIL THEN
            FREE logical_pp_table_p^ [pp_index].pp_info.pp_communication_buffer_p IN
                  osv$mainframe_wired_cb_heap^;
          IFEND;
        FOREND;
        FREE logical_pp_table_p IN osv$mainframe_wired_cb_heap^;
      IFEND;
    IFEND;

  PROCEND build_pp_interface_table;
?? OLDTITLE ??
?? NEWTITLE := 'build_unit_descriptors', EJECT ??

{ PURPOSE:
{   This procedure sets up the values of the unit descriptors in the PP interface table.

  PROCEDURE build_unit_descriptors
    (    first_unit: iot$logical_unit;
         last_unit: iot$logical_unit;
         logical_unit_table_p: ^cmt$logical_unit_table;
         channel_p: ^cmt$element_definition;
         temp_unit_des_p: ^array [ * ] of t$unit_descriptor;
         controller_type: cmt$controller_type;
     VAR table_unit_desc_p: ^iot$unit_descriptors);

    VAR
      controller_element_p: ^cmt$element_definition,
      controller_number: cmt$physical_equipment_number,
      controller_state: cmt$element_state,
      ignore_status: ost$status,
      logical_unit_number: iot$logical_unit,
      redundant_path: boolean,
      redundant_path_useable: boolean,
      rma: integer,
      unit_descriptor_p: ^iot$unit_descriptor_entry;

  /build_each_unit/
    FOR logical_unit_number := first_unit TO last_unit DO
      IF logical_unit_number < cmc$job_template_unit_ordinal THEN
        CYCLE /build_each_unit/; {----->
      IFEND;

      unit_descriptor_p := ^table_unit_desc_p^ [logical_unit_number];
      unit_descriptor_p^.physical_path.channel_number := 0;
      unit_descriptor_p^.physical_path.storage_directory_address := 0;
      unit_descriptor_p^.physical_path.controller_number := 0;
      unit_descriptor_p^.physical_path.physical_unit_number := 0;
      unit_descriptor_p^.unit_interface_table_rma := 0;
      unit_descriptor_p^.unit_interface_table := NIL;
      unit_descriptor_p^.logical_unit := logical_unit_number;

      IF (logical_unit_table_p^ [logical_unit_number].unit_interface_table = NIL) OR
            NOT temp_unit_des_p^ [logical_unit_number].configured THEN
        CYCLE /build_each_unit/; {----->
      IFEND;

      i#real_memory_address (#LOC (logical_unit_table_p^ [logical_unit_number].unit_interface_table^), rma);
      controller_number := temp_unit_des_p^ [logical_unit_number].controller_number;
      controller_element_p := NIL;

      IF channel_p <> NIL THEN
        IF cmp$support_redundant_channel (controller_type) THEN
          IF (controller_type = cmc$ms7155_1) OR (controller_type = cmc$ms7155_1x) THEN
            select_7155_controller (channel_p, logical_unit_number, controller_type, controller_element_p,
                  redundant_path, ignore_status);
          ELSE
            select_controller (channel_p, logical_unit_number, controller_type, controller_element_p,
                  redundant_path, ignore_status);
          IFEND;
          IF NOT ignore_status.normal THEN
            RETURN; {----->
          IFEND;

          IF controller_element_p <> NIL THEN
            IF redundant_path THEN
              rma := 0;
            IFEND;
            controller_number := controller_element_p^.controller.physical_equipment_number;
          ELSE
            cmp$pc_get_element (channel_p^.data_channel.connection.equipment [controller_number].element_name,
                  channel_p^.data_channel.iou, controller_element_p, ignore_status);
          IFEND;
        ELSE
          cmp$pc_get_element (channel_p^.data_channel.connection.equipment [controller_number].element_name,
                channel_p^.data_channel.iou, controller_element_p, ignore_status);
        IFEND;
      IFEND;

      unit_descriptor_p^.unit_interface_table_rma := rma;
      unit_descriptor_p^.unit_interface_table := logical_unit_table_p^ [logical_unit_number].
            unit_interface_table;
      unit_descriptor_p^.unit_interface_table^.next_request := NIL;
      unit_descriptor_p^.unit_interface_table^.unit_lockword := iov$initial_queue_lock_sc;
      unit_descriptor_p^.unit_interface_table^.next_request_rma := 0;
      unit_descriptor_p^.unit_interface_table^.queue_count := 0;
{     table_unit_desc_p^ [logical_unit_number].unit_interface_table^.unit_communication_buffer_rma := 0;
{     table_unit_desc_p^ [logical_unit_number].unit_interface_table^.unit_commun_buffer_length := 0;


      unit_descriptor_p^.physical_path.channel_number := temp_unit_des_p^ [logical_unit_number].channel.
            number;
      IF controller_type <> cmc$mshydra_ct THEN
        unit_descriptor_p^.physical_path.controller_number := temp_unit_des_p^ [logical_unit_number].
              controller_number;
        IF ((controller_type = cmc$mscm3_ct)
{     } OR (controller_type = cmc$ms5831_x)
{     } OR (controller_type = cmc$msntdc_1)
{     } OR (controller_type = cmc$msntdc_2))
{    } AND (temp_unit_des_p^ [logical_unit_number].controller_number <> controller_number) THEN
          unit_descriptor_p^.physical_path.controller_number := controller_number;
        IFEND;
        unit_descriptor_p^.physical_path.physical_unit_number := temp_unit_des_p^ [logical_unit_number].
              unit_number;
      ELSE
        unit_descriptor_p^.physical_path.controller_number := temp_unit_des_p^ [logical_unit_number].
              controller_number;
        unit_descriptor_p^.physical_path.physical_unit_number := 0;
      IFEND;
      IF temp_unit_des_p^ [logical_unit_number].channel.port = cmc$port_b THEN
        unit_descriptor_p^.physical_path.port := 1;
      ELSE
        unit_descriptor_p^.physical_path.port := 0;
      IFEND;
      unit_descriptor_p^.physical_path.storage_directory_address := temp_unit_des_p^ [logical_unit_number].
            storage_directory_address;
    FOREND /build_each_unit/;

  PROCEND build_unit_descriptors;
?? OLDTITLE ??
?? NEWTITLE := 'build_unit_interface_table', EJECT ??

{ PURPOSE:
{   This procedure allocates space for the logical unit table.  It also initializes the proper pointers for
{   each entry in the table.

  PROCEDURE build_unit_interface_table
    (    requested_unit_count: iot$logical_unit;
         allocate_entire_configuration: boolean;
     VAR logical_unit_table_p: ^cmt$logical_unit_table;
     VAR status: ost$status);

    CONST
      procedure_name = 'BUILD_UNIT_INTERFACE_TABLE',
      init_offln_drv_num = 0ff(16); {NOSVE initialization value for 'off_line_drive_number'.}

    VAR
      active_path: boolean,
      found: boolean,
      index: integer,
      io_unit_type: iot$unit_type,
      iou_name: cmt$element_name,
      local_logical_unit_table_p: ^cmt$logical_unit_table,
      mainframe_element_p: ^cmt$element_definition,
      rma: integer,
      template_p: ^iot$unit_commun_buffer_template,
      total_unit_count: iot$logical_unit,
      unit_class: cmt$unit_class,
      unit_count: iot$logical_unit,
      unit_state: cmt$element_state,
      unit_type: cmt$unit_type;

    status.normal := TRUE;

    unit_count := requested_unit_count + cmc$reserved_unit_count;
    IF allocate_entire_configuration THEN
      total_unit_count := unit_count + cmc$reserved_network_unit_count;
    ELSE
      total_unit_count := unit_count;
    IFEND;

    { Allocate the local Logical Unit Table.

    ALLOCATE local_logical_unit_table_p: [1 .. total_unit_count] IN osv$mainframe_wired_cb_heap^;
    pmp$zero_out_table (#LOC (local_logical_unit_table_p^), #SIZE (local_logical_unit_table_p^));
    FOR index := LOWERBOUND (local_logical_unit_table_p^) TO UPPERBOUND (local_logical_unit_table_p^) DO
      local_logical_unit_table_p^ [index].configured := FALSE;
      local_logical_unit_table_p^ [index].logical_unit_number := 0;
      local_logical_unit_table_p^ [index].unit_interface_table := NIL;
    FOREND;

  /initialize_loop/
    FOR index := cmc$job_template_unit_ordinal TO unit_count DO
      cmp$pc_get_logical_unit (index, mainframe_element_p, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;

      { Do not build a UIT associated with the ICA or with COMMUNICATIONS_ELEMENT.

      IF ((mainframe_element_p^.element_type = cmc$channel_adapter_element) AND
            (mainframe_element_p^.product_id.product_number = ' $2629')) OR
            (mainframe_element_p^.element_type = cmc$communications_element) THEN
        local_logical_unit_table_p^ [index].configured := FALSE;
        local_logical_unit_table_p^ [index].logical_unit_number := index;
        CYCLE /initialize_loop/; {----->
      IFEND;

      cmp$get_unit_type (mainframe_element_p^.product_id, unit_type, io_unit_type, unit_class, found);
      IF NOT found AND (io_unit_type = ioc$dt_foreign_device) THEN
        local_logical_unit_table_p^ [index].configured := FALSE;
        local_logical_unit_table_p^ [index].logical_unit_number := index;
        CYCLE /initialize_loop/; {----->
      IFEND;
      cmp$get_element_state (mainframe_element_p^.element_name, {not used} iou_name, unit_state, status);
      IF NOT status.normal THEN
        unit_state := cmc$off;
        status.normal := TRUE;
      IFEND;

      { Allocate the UIT and Initialize logical_unit_table.

      local_logical_unit_table_p^ [index].configured := TRUE;
      ALLOCATE local_logical_unit_table_p^ [index].unit_interface_table IN osv$mainframe_wired_cb_heap^;

      pmp$zero_out_table (#LOC (local_logical_unit_table_p^ [index].unit_interface_table^),
            #SIZE (local_logical_unit_table_p^ [index].unit_interface_table^));
      local_logical_unit_table_p^ [index].logical_unit_number := index;
      local_logical_unit_table_p^ [index].entry_interlock := FALSE;
      local_logical_unit_table_p^ [index].element_capability := $cmt$element_capabilities [];
      local_logical_unit_table_p^ [index].element_access := $cmt$element_access [];
      local_logical_unit_table_p^ [index].unit_interface_table^.logical_unit := index;
      local_logical_unit_table_p^ [index].unit_interface_table^.next_request := NIL;

      IF unit_state = cmc$on THEN
        cmp$verify_active_path (mainframe_element_p^, active_path);
        IF NOT active_path THEN
          unit_state := cmc$off;
        IFEND;
      IFEND;

      CASE unit_state OF
      = cmc$on =

        { In the future, need to include cmc$file_allocation and cmc$job_reservation in the element
        { capability list. This must be done based on the type of devices.

        local_logical_unit_table_p^ [index].element_capability :=
              $cmt$element_capabilities [cmc$volume_assignment, cmc$io_request_submission,
              cmc$concurrent_maintenance];
        local_logical_unit_table_p^ [index].element_access := -$cmt$element_access [];
      = cmc$down =
        local_logical_unit_table_p^ [index].element_capability :=
              $cmt$element_capabilities [cmc$dedicated_maintenance, cmc$concurrent_maintenance];
        local_logical_unit_table_p^ [index].element_access := -$cmt$element_access [];
        local_logical_unit_table_p^ [index].unit_interface_table^.unit_status.disabled := TRUE;
      ELSE { = cmc$off = }
        local_logical_unit_table_p^ [index].element_capability := $cmt$element_capabilities [];
        local_logical_unit_table_p^ [index].element_access := $cmt$element_access [];
        local_logical_unit_table_p^ [index].unit_interface_table^.unit_status.disabled := TRUE;
      CASEND;

      local_logical_unit_table_p^ [index].unit_interface_table^.unit_status.parity_protection_enabled :=
            FALSE;
      local_logical_unit_table_p^ [index].unit_interface_table^.unit_status.restoring_drive := FALSE;
      local_logical_unit_table_p^ [index].unit_interface_table^.unit_status.off_line_drive_number :=
            init_offln_drv_num;

      { Determine if it is a assignable device.

      IF unit_type IN cmv$assignable_device THEN
        local_logical_unit_table_p^ [index].status.assignable_device := TRUE;
        local_logical_unit_table_p^ [index].status.assigned := FALSE;
      ELSE
        local_logical_unit_table_p^ [index].status.assignable_device := FALSE;
      IFEND;
      local_logical_unit_table_p^ [index].unit_interface_table^.unit_type := io_unit_type;

      { Allocate the unit communication buffer.

      ALLOCATE template_p IN osv$mainframe_wired_cb_heap^;
      local_logical_unit_table_p^ [index].unit_communication_buffer_pva :=
            ^template_p^.unit_communication_buffer;
      pmp$zero_out_table (#LOC (local_logical_unit_table_p^ [index].unit_communication_buffer_pva^),
            #SIZE (local_logical_unit_table_p^ [index].unit_communication_buffer_pva^));
      i#real_memory_address (#LOC (local_logical_unit_table_p^ [index].unit_communication_buffer_pva^), rma);
      local_logical_unit_table_p^ [index].unit_interface_table^.unit_communication_buffer_rma := rma;
      local_logical_unit_table_p^ [index].unit_interface_table^.unit_commun_buffer_length :=
            ioc$unit_commun_buffer_length * 8;
      local_logical_unit_table_p^ [index].unit_interface_table^.unit_shared := FALSE;
    FOREND /initialize_loop/;

    logical_unit_table_p := local_logical_unit_table_p;

  PROCEND build_unit_interface_table;
?? OLDTITLE ??
?? NEWTITLE := 'channels_equivalent', EJECT ??

  FUNCTION [INLINE] channels_equivalent
    (    channel_element_1_p: ^cmt$element_definition;
         channel_element_2_p: ^cmt$element_definition): boolean;

    channels_equivalent := (channel_element_1_p^.data_channel.number =
          channel_element_2_p^.data_channel.number) AND (channel_element_1_p^.data_channel.concurrent =
          channel_element_2_p^.data_channel.concurrent) AND (channel_element_1_p^.data_channel.iou =
          channel_element_2_p^.data_channel.iou);

  FUNCEND channels_equivalent;
?? OLDTITLE ??
?? NEWTITLE := 'combine_table_entry', EJECT ??

{ PURPOSE:
{   This procedure recombines information from the port A and port B channel into one pp interface table.
{   The reason for doing this is because CM stores channel portA and channel portB separately, thus
{   thinking that it is two separate channels.

  PROCEDURE combine_table_entry
    (    current_first_lun: iot$logical_unit;
         current_last_lun: iot$logical_unit;
         current_unit_desc_p: ^iot$unit_descriptors,
         pp_index: iot$pp_number;
     VAR logical_pp_table_p: ^cmt$logical_pp_table;
     VAR status: ost$status);

    CONST
      procedure_name = 'COMBINE_TABLE_ENTRY';

    VAR
      first_lun: iot$logical_unit,
      last_lun: iot$logical_unit,
      lun_index: iot$logical_unit,
      pp_interface_table_p: ^iot$pp_interface_table,
      ppit_seq_p: ^SEQ ( * ),
      rma: integer,
      size: integer,
      table_unit_desc_p: ^iot$unit_descriptors,
      unit_desc_p: ^iot$unit_descriptors,
      used_first_lun: iot$logical_unit,
      used_last_lun: iot$logical_unit;

    status.normal := TRUE;

    { Take smallest first logical unit and the largest last logical unit of the 2 pp tables.

    used_first_lun := logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_p^.first_logical_unit;
    IF used_first_lun <= current_first_lun THEN
      first_lun := used_first_lun;
    ELSE
      first_lun := current_first_lun;
    IFEND;

    used_last_lun := logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_p^.first_logical_unit +
          logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_p^.number_of_units - 1;
    IF used_last_lun > current_last_lun THEN
      last_lun := used_last_lun;
    ELSE
      last_lun := current_last_lun;
    IFEND;

    { Allocate and initialize new unit descriptors.

    PUSH unit_desc_p: [first_lun .. last_lun];
    FOR lun_index := first_lun TO last_lun DO
      unit_desc_p^ [lun_index].logical_unit := 0;
      unit_desc_p^ [lun_index].unit_interface_table_rma := 0;
      unit_desc_p^ [lun_index].unit_interface_table := NIL;
      unit_desc_p^ [lun_index].physical_path.channel_number := 0;
      unit_desc_p^ [lun_index].physical_path.port := 0;
      unit_desc_p^ [lun_index].physical_path.controller_number := 0;
      unit_desc_p^ [lun_index].physical_path.storage_directory_address := 0;
      unit_desc_p^ [lun_index].physical_path.physical_unit_number := 0;
    FOREND;

    { Move stuff over to the new table.

    FOR lun_index := used_first_lun TO used_last_lun DO
      unit_desc_p^ [lun_index] := logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_p^.
            unit_descriptors [lun_index];
    FOREND;

    FOR lun_index := current_first_lun TO current_last_lun DO
      IF unit_desc_p^ [lun_index].unit_interface_table_rma = 0 THEN
        unit_desc_p^ [lun_index] := current_unit_desc_p^ [lun_index];
      IFEND;
    FOREND;

    { Update PPIT of pp_index.

    size := #SIZE (iot$pp_interface_table: [first_lun .. last_lun]);
    dsp$allocate_continuous_memory (osv$mainframe_wired_cb_heap, size, ppit_seq_p);
    RESET ppit_seq_p;
    NEXT pp_interface_table_p: [first_lun .. last_lun] IN ppit_seq_p;

    pp_interface_table_p^.pp_number := pp_index;
    pp_interface_table_p^.first_logical_unit := first_lun;
    pp_interface_table_p^.number_of_units := last_lun - first_lun + 1;
    pp_interface_table_p^.interrupt_register_rma := logical_pp_table_p^ [pp_index].pp_info.
          pp_interface_table_p^.interrupt_register_rma;
    pp_interface_table_p^.channel_interlock_rma := logical_pp_table_p^ [pp_index].pp_info.
          pp_interface_table_p^.channel_interlock_rma;
    pp_interface_table_p^.communication_buffer_length := logical_pp_table_p^ [pp_index].pp_info.
          pp_interface_table_p^.communication_buffer_length;
    pp_interface_table_p^.communication_buffer_rma := logical_pp_table_p^ [pp_index].pp_info.
          pp_interface_table_p^.communication_buffer_rma;
    pp_interface_table_p^.response_buffer := logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_p^.
          response_buffer;
    pp_interface_table_p^.response_buffer_rma := logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_p^.
          response_buffer_rma;
    pp_interface_table_p^.pp_request_queue := NIL;
    pp_interface_table_p^.limit := logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_p^.limit;

    table_unit_desc_p := ^pp_interface_table_p^.unit_descriptors;
    FOR lun_index := first_lun TO last_lun DO
      table_unit_desc_p^ [lun_index] := unit_desc_p^ [lun_index];
    FOREND;

    FREE logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_p IN osv$mainframe_wired_cb_heap^;
    logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_p := pp_interface_table_p;
    i#real_memory_address (#LOC (logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_p^), rma);
    logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_rma := rma;

  PROCEND combine_table_entry;
?? OLDTITLE ??
?? NEWTITLE := 'determine_redundant_path', EJECT ??

{ PURPOSE:
{   This procedure determines if a given path channel and equipment is a redundant path.
{   A redundant path is one that in which the channel is not the first ON channel.

  PROCEDURE determine_redundant_path
    (    mainframe_id: cmt$element_name;
         channel: cmt$element_definition;
         controller_p: ^cmt$element_definition;
     VAR redundant_path: boolean;
     VAR redundant_path_useable: boolean;
     VAR status: ost$status);

    VAR
      channel_state: cmt$element_state,
      controller_state: cmt$element_state,
      current_channel_state: cmt$element_state,
      first_on_channel_found: boolean,
      first_on_channel_p: ^cmt$element_definition,
      iou_name: cmt$element_name,
      iou_number: dst$iou_number,
      physical_channel: cmt$physical_channel,
      port: integer,
      redundant: boolean;

    status.normal := TRUE;
    redundant_path := FALSE;
    redundant_path_useable := FALSE;
    IF controller_p = NIL THEN
      RETURN; {----->
    IFEND;

    cmp$get_element_state (channel.element_name, channel.data_channel.iou, current_channel_state, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;
    cmp$get_element_state (controller_p^.element_name, iou_name, controller_state, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;
    first_on_channel_found := FALSE;

  /search_primary_channel/
    FOR port := LOWERVALUE (cmt$controller_port_number) TO UPPERVALUE (cmt$controller_port_number) DO
      IF controller_p^.controller.connection.port [port].configured AND
            (controller_p^.controller.connection.port [port].mainframe_ownership = mainframe_id) THEN
        cmp$get_element_state (controller_p^.controller.connection.port [port].element_name,
              controller_p^.controller.connection.port [port].iou, channel_state, status);
        IF NOT status.normal THEN
          RETURN; {----->
        IFEND;
        cmp$pc_get_element (controller_p^.controller.connection.port [port].element_name,
              controller_p^.controller.connection.port [port].iou, first_on_channel_p, status);
        IF NOT status.normal THEN
          RETURN; {----->
        IFEND;
        IF channel_state = cmc$on THEN
          first_on_channel_found := TRUE;
          EXIT /search_primary_channel/; {----->
        IFEND;
      IFEND;
    FOREND /search_primary_channel/;

    IF first_on_channel_found THEN

      { The current channel is not the first on channel in the upline connection of this controller.  If it
      { is not redundant at all on all the other controllers then set redundant to TRUE.

      IF (channel.data_channel.number <> first_on_channel_p^.data_channel.number) OR
            (channel.data_channel.concurrent <> first_on_channel_p^.data_channel.concurrent) OR
            (channel.data_channel.iou <> first_on_channel_p^.data_channel.iou) THEN
        redundant_path := TRUE;
        redundant_path_useable := (current_channel_state = cmc$on) AND (controller_state = cmc$on);
      IFEND;
    ELSE
      redundant := (current_channel_state <> cmc$on);
    IFEND;

  PROCEND determine_redundant_path;
?? OLDTITLE ??
?? NEWTITLE := 'get_driver_name', EJECT ??

{ PURPOSE:
{   This procedure converts the IOU Program Name supplied to the driver name.  This name is determined
{   based on channel type (NIO or CIO).
{ NOTE:
{   If the driver name is not a NOS/VE driver name, then the actual driver name will be returned.

  PROCEDURE get_driver_name
    (    driver_name: string ( * );
         channel_type: dst$channel_protocol_type;
         dual_pp: boolean;
     VAR cm_driver_name: dst$driver_name);

    CONST
      table_length = 30;

    VAR
      driver_index: 1 .. table_length;

    VAR
      default_driver: [STATIC, READ] array [1 .. table_length] of array [1 .. 3] of dst$driver_name := [

            {               NIO        CIO        CIP
            {
            { 7155_1x   } ['E1C7155', 'illegal', 'DSK55A '],
            { 7155 CIO  } ['illegal', 'E1A7155', 'DSK55C7'],
            { 7154      } ['DSKE   ', 'DSKE   ', 'DSK7154'],
            { ISD       } ['E1I7255', 'illegal', 'ISD    '],
            { HYDRA     } ['illegal', 'E9S887 ', 'HYD    '],
            { 5831 I0   } ['E5P5831', 'illegal', 'E5P5831'],
            { 5831 I4   } ['illegal', 'E9P5831', 'E9P5831'],
            { NTDD I0   } ['E5PNTDD', 'illegal', 'E5PNTDD'],
            { NTDD I4   } ['illegal', 'E9PNTDD', 'E9PNTDD'],
            { PICO I0   } ['E5PPICO', 'illegal', 'E5PPICO'],
            { PICO I4   } ['illegal', 'E9PPICO', 'E9PPICO'],
            { CM3 9836  } ['E5P9836', 'illegal', 'DSKI   '],
            { CM3 9853  } ['illegal', 'E9P9853', 'E9P9853'],
            { 7165      } ['E2C7165', 'illegal', 'D895   '],
            { 7165 CIO  } ['illegal', 'E9A7165', 'D895CIO'],
            { 7021      } ['E1C7021', 'E1A7021', 'TAPE   '],
            { 7221      } ['E5I9639', 'illegal', 'TAPB   '],
            { IPI tape  } ['E5P5698', 'illegal', 'TAPC   '],
            { IPI tape  } ['illegal', 'E9P5698', 'TAPD   '],
            { IPI I4-43 } ['illegal', 'E9Q5698', 'E9Q5698'],
            { 5680      } ['E2C5680', 'E2A5680', 'E2X5680'],
            { MAP V     } ['E1C6535', 'E1A6535', 'VM5B   '],
            { NETW      } ['E1C2620', 'E1A2620', 'NETW   '],
            { ICA       } ['E1I2629', 'illegal', 'ICAD   '],
            { LCN       } ['E1C380 ', 'E1A380 ', 'NPDR   '],
            { LCN S0    } ['E1I380 ', 'illegal', 'NDI0   '],
            {EXPRESSLINK} ['E5P4000', 'illegal', 'IVB0   '],
            {EXPRESSLINK} ['illegal', 'E9P4000', 'IVB4   '],
            { ESMD      } ['E1C5380', 'E1A5380', 'ESMD   '],
            { SDPD      } ['E1CSDPD', 'E1ASDPD', 'SDPD   ']];

    cm_driver_name := driver_name;

    FOR driver_index := 1 TO table_length DO
      IF driver_name = default_driver [driver_index] [3] THEN
        IF channel_type = dsc$cpt_nio THEN
          cm_driver_name := default_driver [driver_index] [1];
        ELSE { cio channel
          cm_driver_name := default_driver [driver_index] [2];
        IFEND;
        IF (driver_name = 'TAPE') AND dual_pp THEN
          cm_driver_name (2, 1) := '2';
        IFEND;
        RETURN; {----->
      IFEND;
    FOREND;

  PROCEND get_driver_name;
?? OLDTITLE ??
?? NEWTITLE := 'pp_interface_table_error', EJECT ??

  PROCEDURE pp_interface_table_error
    (    procedure_name: string ( * );
         pp: iot$pp_number;
         channel_number: integer;
         controller_number: integer,
         unit_number: integer;
         logical_unit_number: integer;
         condition: ost$status_condition;
         element_name: string ( * );
         text: string ( * );
     VAR status: ost$status);

    VAR
      channel_string: string (14),
      controller_string: string (17),
      logical_unit_string: string (19),
      ignore_status: ost$status,
      pp_string: string (9),
      temp_string: ost$string,
      unit_string: string (20);

    osp$set_status_abnormal (cmc$configuration_management_id, condition, procedure_name, status);

    pp_string := '-----';
    IF pp >= 0 THEN
      clp$convert_integer_to_string (pp, 10, FALSE, temp_string, ignore_status);
      pp_string := temp_string.value;
    IFEND;
    osp$append_status_parameter (osc$status_parameter_delimiter, pp_string, status);

    channel_string := '-----';
    IF channel_number >= 0 THEN
      clp$convert_integer_to_string (channel_number, 10, FALSE, temp_string, ignore_status);
      channel_string := temp_string.value;
    IFEND;
    osp$append_status_parameter (osc$status_parameter_delimiter, channel_string, status);

    controller_string := '-----';
    IF controller_number >= 0 THEN
      clp$convert_integer_to_string (controller_number, 10, FALSE, temp_string, ignore_status);
      controller_string := temp_string.value;
    IFEND;
    osp$append_status_parameter (osc$status_parameter_delimiter, controller_string, status);

    unit_string := '-----';
    IF unit_number >= 0 THEN
      clp$convert_integer_to_string (unit_number, 10, FALSE, temp_string, ignore_status);
      unit_string := temp_string.value;
    IFEND;
    osp$append_status_parameter (osc$status_parameter_delimiter, unit_string, status);

    logical_unit_string := '-----';
    IF logical_unit_number >= 0 THEN
      clp$convert_integer_to_string (logical_unit_number, 10, FALSE, temp_string, ignore_status);
      logical_unit_string := temp_string.value;
    IFEND;
    osp$append_status_parameter (osc$status_parameter_delimiter, logical_unit_string, status);
    osp$append_status_parameter (osc$status_parameter_delimiter, element_name, status);
    osp$append_status_parameter (osc$status_parameter_delimiter, text, status);

  PROCEND pp_interface_table_error;
?? OLDTITLE ??
?? NEWTITLE := 'retrieve_logical_pp_flags', EJECT ??

{ PURPOSE:
{   This procedure contains the code that determines some of the flag values in the Logical PP Table.
{   When drivers add support for such things as handshaking and dynamic reload, this should be the
{   only procedure that should need the driver name added to a list.

  PROCEDURE retrieve_logical_pp_flags
    (    cip_driver_name: dst$driver_name;
         pp_index: iot$pp_number;
     VAR logical_pp_table_p: ^cmt$logical_pp_table);

{PP Type
    IF (cip_driver_name = 'E5P5831') OR (cip_driver_name = 'E9P5831') OR (cip_driver_name = 'E5PNTDD') OR
          (cip_driver_name = 'E9PNTDD') OR (cip_driver_name = 'E5PPICO') OR (cip_driver_name = 'E9PPICO') OR
          (cip_driver_name = 'DSK55A ') OR (cip_driver_name = 'DSK55C7') OR (cip_driver_name = 'DSK7154') OR
          (cip_driver_name = 'ISD    ') OR (cip_driver_name = 'HYD    ') OR (cip_driver_name = 'DSKI   ') OR
          (cip_driver_name = 'E9P9853') OR (cip_driver_name = 'D895   ') OR (cip_driver_name = 'D895CIO') THEN

      logical_pp_table_p^ [pp_index].pp_info.pp_type := cmc$lpt_disk_pp_type;

    ELSEIF (cip_driver_name = 'TAPE   ') OR (cip_driver_name = 'TAPB   ') OR (cip_driver_name = 'TAPC   ') OR
          (cip_driver_name = 'TAPD   ') OR (cip_driver_name = 'E9Q5698') OR (cip_driver_name = 'E2X5680') THEN

      logical_pp_table_p^ [pp_index].pp_info.pp_type := cmc$lpt_tape_pp_type;

    ELSEIF (cip_driver_name = 'NETW   ') OR (cip_driver_name = 'ICAD   ') OR (cip_driver_name = 'IVB0   ') OR
          (cip_driver_name = 'IVB4   ') THEN

      logical_pp_table_p^ [pp_index].pp_info.pp_type := cmc$lpt_network_pp_type;

    ELSEIF (cip_driver_name = 'NPDR   ') OR (cip_driver_name = 'NDI0   ') THEN
      logical_pp_table_p^ [pp_index].pp_info.pp_type := cmc$lpt_nad_pp_type;

    ELSE
      logical_pp_table_p^ [pp_index].pp_info.pp_type := cmc$lpt_other_pp_type;
    IFEND;

{PP_IDLE_RESUME_SUPPORTED
    IF (cip_driver_name = 'E5P5831') OR (cip_driver_name = 'E9P5831') OR (cip_driver_name = 'E5PNTDD') OR
          (cip_driver_name = 'E9PNTDD') OR (cip_driver_name = 'E5PPICO') OR (cip_driver_name = 'E9PPICO') OR
          (cip_driver_name = 'DSK55A ') OR (cip_driver_name = 'DSK55C7') OR (cip_driver_name = 'DSK7154') OR
          (cip_driver_name = 'ISD    ') OR (cip_driver_name = 'HYD    ') OR (cip_driver_name = 'DSKI   ') OR
          (cip_driver_name = 'E9P9853') OR (cip_driver_name = 'D895   ') OR (cip_driver_name = 'D895CIO') OR
          (cip_driver_name = 'SDPD   ') THEN

      logical_pp_table_p^ [pp_index].flags.pp_idle_resume_supported := TRUE;
    IFEND;

{PP_HANDSHAKING_SUPPORTED
    IF (cip_driver_name = 'E5P5831') OR (cip_driver_name = 'E9P5831') OR (cip_driver_name = 'E5PNTDD') OR
          (cip_driver_name = 'E9PNTDD') OR (cip_driver_name = 'E5PPICO') OR (cip_driver_name = 'E9PPICO') OR
          (cip_driver_name = 'DSK55A ') OR (cip_driver_name = 'DSK55C7') OR (cip_driver_name = 'DSK7154') OR
          (cip_driver_name = 'ISD    ') OR (cip_driver_name = 'HYD    ') OR (cip_driver_name = 'DSKI   ') OR
          (cip_driver_name = 'E9P9853') OR (cip_driver_name = 'D895   ') OR (cip_driver_name = 'D895CIO') OR
          (cip_driver_name = 'NETW   ') OR (cip_driver_name = 'ICAD   ') OR (cip_driver_name = 'IVB0   ') OR
          (cip_driver_name = 'IVB4   ') OR (cip_driver_name = 'NPDR   ') OR (cip_driver_name = 'NDI0   ') THEN

      logical_pp_table_p^ [pp_index].flags.pp_handshaking_supported := TRUE;
    IFEND;

{PP_RELOAD_SUPPORTED, TIMEOUT
    IF (cip_driver_name = 'E5P5831') OR (cip_driver_name = 'E9P5831') OR (cip_driver_name = 'E5PNTDD') OR
          (cip_driver_name = 'E9PNTDD') OR (cip_driver_name = 'E5PPICO') OR (cip_driver_name = 'E9PPICO') OR
          (cip_driver_name = 'DSK55A ') OR (cip_driver_name = 'DSK55C7') OR (cip_driver_name = 'DSK7154') OR
          (cip_driver_name = 'ISD    ') OR (cip_driver_name = 'HYD    ') OR (cip_driver_name = 'DSKI   ') OR
          (cip_driver_name = 'E9P9853') OR (cip_driver_name = 'D895   ') OR (cip_driver_name = 'D895CIO') OR
          (cip_driver_name = 'NETW   ') OR (cip_driver_name = 'ICAD   ') OR (cip_driver_name = 'IVB0   ') OR
          (cip_driver_name = 'IVB4   ') OR (cip_driver_name = 'NPDR   ') OR (cip_driver_name = 'NDI0   ') THEN

      logical_pp_table_p^ [pp_index].flags.pp_reload_supported := TRUE;
      logical_pp_table_p^ [pp_index].active_check.timeout := 30000000;
    IFEND;

  PROCEND retrieve_logical_pp_flags;

?? OLDTITLE ??
?? NEWTITLE := 'select_controller', EJECT ??
{ PURPOSE:
{     This procedure returns the appropriate 7155 controller, if any, to be
{     configured in the PP interface table for the specified channel and unit.
{ DESIGN:
{     The 7155 controller must be handled slightly different than the other
{     devices that support redundancy and/or alternate access.  These devices
{     support both alternate access and redundant access.  Also the controller
{     number is always 0.  An FMD spindle will support alternate access from
{     two controllers but does not support redundnant controllers.  The 7155
{     controller does not support dual channel access but does support
{     redundant channels.
{     This algorithm will locate the controller connected to the specified
{     channel and unit. The SELECTED_CONTROLLER_P parameter will contain a
{     pointer to this controller if an active path to the controller exists. The
{     REDUNDANT_PATH parameter will be set to TRUE if the specified channel
{     is the primary channel for the controller and will be set to FALSE
{     otherwise.

  PROCEDURE select_7155_controller
    (    channel_p: ^cmt$element_definition;
         logical_unit_number: iot$logical_unit;
         controller_type: cmt$controller_type;
     VAR selected_controller_p: ^cmt$element_definition;
     VAR redundant_path: boolean;
     VAR status: ost$status);

    VAR
      channel_element_p: ^cmt$element_definition,
      channel_found: boolean,
      channel_port: cmt$controller_port_number,
      channel_state: cmt$element_state,
      controller_element_p: ^cmt$element_definition,
      controller_state: cmt$element_state,
      local_status: ost$status,
      port: cmt$data_storage_port_number,
      primary_channel_found: boolean,
      primary_channel_p: ^cmt$element_definition,
      storage_device_element_p: ^cmt$element_definition;

    status.normal := TRUE;
    channel_found := FALSE;
    controller_element_p := NIL;
    primary_channel_found := FALSE;
    primary_channel_p := NIL;
    redundant_path := FALSE;
    selected_controller_p := NIL;

    IF channel_p = NIL THEN
      RETURN; {----->
    IFEND;

    cmp$pc_get_logical_unit (logical_unit_number, storage_device_element_p, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

  /search_for_primary_controller/
    FOR port := LOWERVALUE (cmt$data_storage_port_number) TO UPPERVALUE (cmt$data_storage_port_number) DO
      IF NOT storage_device_element_p^.storage_device.connection.port [port].configured THEN
        CYCLE /search_for_primary_controller/; {----->
      IFEND;

      cmp$get_element_state (storage_device_element_p^.storage_device.connection.port [port].element_name,
            {iou_name not used} osc$null_name, controller_state, local_status);
      IF NOT local_status.normal OR (controller_state <> cmc$on) THEN
        CYCLE /search_for_primary_controller/; {----->
      IFEND;

      cmp$pc_get_element (storage_device_element_p^.storage_device.connection.port [port].element_name,
            {iou_name not used} osc$null_name, controller_element_p, local_status);
      IF NOT local_status.normal THEN
        CYCLE /search_for_primary_controller/; {----->
      IFEND;

      primary_channel_found := FALSE;

    /search_for_primary_channel/
      FOR channel_port := LOWERVALUE (cmt$controller_port_number)
            TO UPPERVALUE (cmt$controller_port_number) DO
        IF NOT controller_element_p^.controller.connection.port [channel_port].configured THEN
          CYCLE /search_for_primary_channel/; {----->
        IFEND;

        cmp$get_element_state (controller_element_p^.controller.connection.port [channel_port].element_name,
              controller_element_p^.controller.connection.port [channel_port].iou, channel_state,
              local_status);
        IF NOT local_status.normal OR (channel_state <> cmc$on) THEN
          CYCLE /search_for_primary_channel/; {----->
        IFEND;

        cmp$pc_get_element (controller_element_p^.controller.connection.port [channel_port].element_name,
              controller_element_p^.controller.connection.port [channel_port].iou, channel_element_p,
              local_status);
        IF NOT local_status.normal THEN
          CYCLE /search_for_primary_channel/; {----->
        IFEND;

        IF NOT primary_channel_found THEN
          primary_channel_p := channel_element_p;
          primary_channel_found := TRUE;
        IFEND;

        IF channels_equivalent (channel_p, channel_element_p) THEN
          channel_found := TRUE;
          EXIT /search_for_primary_controller/; {----->
        IFEND;
      FOREND /search_for_primary_channel/;
    FOREND /search_for_primary_controller/;

    IF channel_found THEN
      selected_controller_p := controller_element_p;
      redundant_path := NOT channels_equivalent (channel_p, primary_channel_p);
    IFEND;

  PROCEND select_7155_controller;

?? OLDTITLE ??
?? NEWTITLE := 'select_controller', EJECT ??

{ PURPOSE:
{   This procedure returns the appropriate controller, if any, to be configured in the PP interface table
{   for the specified channel and unit.
{ DESIGN:
{   If the primary channel for all controllers connected to the specified unit is the same and the channel
{   specified is the primary channel, a pointer to the primary controller for the unit is returned in the
{   SELECTED_CONTROLLER_P parameter.  The primary controller of the unit is the first controller in the ON
{   state for which there is also a channel in the ON state.  The order the controllers and/or channels
{   connected to an element are checked is the order they are listed on the DEFINE_ELEMENT command.  If the
{   primary channel for all controllers connected to the specified unit are NOT the same this indicates an
{   alternate access scenario.  The controller selected will be the controller that has the specified channel
{   as its primary channel.  The order the controllers are specified on the DEFINE_ELEMENT command for each
{   unit is not significant in an alternate access scenario.  If the specified channel is not the primary
{   channel for any of the controllers connected to the unit a NIL pointer will be returned in the
{   SELECTED_CONTROLLER_P parameter

  PROCEDURE select_controller
    (    channel_p: ^cmt$element_definition;
         logical_unit_number: iot$logical_unit;
         controller_type: cmt$controller_type;
     VAR selected_controller_p: ^cmt$element_definition;
     VAR redundant_path: boolean;
     VAR status: ost$status);

    VAR
      alternate_access_controller_p: ^cmt$element_definition,
      channel_element_p: ^cmt$element_definition,
      channel_port: cmt$controller_port_number,
      channel_state: cmt$element_state,
      controller_element_p: ^cmt$element_definition,
      controller_state: cmt$element_state,
      local_status: ost$status,
      port: cmt$data_storage_port_number,
      primary_channels_equal: boolean,
      primary_channel_found: boolean,
      primary_channel_p: ^cmt$element_definition,
      primary_controller: boolean,
      primary_controller_found: boolean,
      primary_controller_p: ^cmt$element_definition,
      storage_device_element_p: ^cmt$element_definition,
      stored_primary_channel_p: ^cmt$element_definition;

    status.normal := TRUE;
    primary_channels_equal := FALSE;
    redundant_path := FALSE;
    alternate_access_controller_p := NIL;
    selected_controller_p := NIL;
    controller_element_p := NIL;
    IF channel_p = NIL THEN
      RETURN; {----->
    IFEND;

    cmp$pc_get_logical_unit (logical_unit_number, storage_device_element_p, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;
    primary_controller_found := FALSE;
    primary_controller_p := NIL;
    primary_channel_p := NIL;

  /search_for_primary_controller/
    FOR port := LOWERVALUE (cmt$data_storage_port_number) TO UPPERVALUE (cmt$data_storage_port_number) DO
      IF NOT storage_device_element_p^.storage_device.connection.port [port].configured THEN
        CYCLE /search_for_primary_controller/; {----->
      IFEND;

      cmp$get_element_state (storage_device_element_p^.storage_device.connection.port [port].element_name,
            {iou_name not used} osc$null_name, controller_state, local_status);
      IF NOT local_status.normal OR (controller_state <> cmc$on) THEN
        CYCLE /search_for_primary_controller/; {----->
      IFEND;

      cmp$pc_get_element (storage_device_element_p^.storage_device.connection.port [port].element_name,
            {iou_name not used} osc$null_name, controller_element_p, local_status);
      IF NOT local_status.normal THEN
        CYCLE /search_for_primary_controller/; {----->
      IFEND;

      primary_controller := FALSE;
      IF NOT primary_controller_found THEN
        primary_controller_p := controller_element_p;
        primary_controller_found := TRUE;
        primary_controller := TRUE;
      IFEND;

      primary_channel_found := FALSE;

    /search_for_primary_channel/
      FOR channel_port := LOWERVALUE (cmt$controller_port_number)
            TO UPPERVALUE (cmt$controller_port_number) DO
        IF NOT controller_element_p^.controller.connection.port [channel_port].configured THEN
          CYCLE /search_for_primary_channel/; {----->
        IFEND;
        cmp$get_element_state (controller_element_p^.controller.connection.port [channel_port].element_name,
              controller_element_p^.controller.connection.port [channel_port].iou, channel_state,
              local_status);
        IF NOT local_status.normal OR (channel_state <> cmc$on) THEN
          CYCLE /search_for_primary_channel/; {----->
        IFEND;

        cmp$pc_get_element (controller_element_p^.controller.connection.port [channel_port].element_name,
              controller_element_p^.controller.connection.port [channel_port].iou, channel_element_p,
              local_status);
        IF NOT local_status.normal THEN
          CYCLE /search_for_primary_channel/; {----->
        IFEND;

        IF NOT primary_channel_found THEN
          primary_channel_p := channel_element_p;
          primary_channel_found := TRUE;
          EXIT /search_for_primary_channel/; {----->
        IFEND;
      FOREND /search_for_primary_channel/;

      IF NOT primary_channel_found THEN
        IF primary_controller THEN
          primary_controller_found := FALSE;
        IFEND;
        CYCLE /search_for_primary_controller/; {----->
      IFEND;

      { Store this controller to use if this turns out to be an alternate access connection.

      IF channels_equivalent (channel_p, primary_channel_p) THEN
        alternate_access_controller_p := controller_element_p;
      IFEND;

      IF primary_controller THEN
        stored_primary_channel_p := primary_channel_p;
        primary_channels_equal := TRUE;
        IF (controller_type <> cmc$ms5831_x)
{     } AND (controller_type <> cmc$msntdc_1)
{     } AND (controller_type <> cmc$msntdc_2) THEN
          EXIT /search_for_primary_controller/; {----->
        IFEND;
      ELSEIF NOT channels_equivalent (stored_primary_channel_p, primary_channel_p) THEN
        primary_channels_equal := FALSE;
      IFEND;
    FOREND /search_for_primary_controller/;

    IF primary_channels_equal THEN
      selected_controller_p := primary_controller_p;
      redundant_path := NOT channels_equivalent (channel_p, primary_channel_p);
    ELSE
      selected_controller_p := alternate_access_controller_p;
    IFEND;

  PROCEND select_controller;
?? OLDTITLE ??
?? NEWTITLE := 'setup_pp_table ', EJECT ??

{ PURPOSE:
{   This procedure builds an entry in the logical pp table and allocates in contiguous memory various
{   structures such as the pp interface table and the pp communication buffer.

  PROCEDURE setup_pp_table
    (    pp: iot$pp_number;
         concurrent: boolean;
         allocate_commun_buffer: boolean;
         low_unit: iot$logical_unit;
         upper_unit: iot$logical_unit;
     VAR logical_pp_table_p: ^cmt$logical_pp_table;
     VAR status: ost$status);

    VAR
      iou_number: dst$iou_number,
      overlay_rma: ost$real_memory_address,
      response_buffer_p: ^iot$response_buffer_template,
      rma: integer,
      seq_p: ^SEQ ( * ),
      size: integer,
      upper_unit_bound: iot$logical_unit;

    status.normal := TRUE;

    IF upper_unit <= 0 THEN
      upper_unit_bound := 1;
    ELSE
      upper_unit_bound := upper_unit;
    IFEND;

    { Setup the PP Interface Table.

    size := #SIZE (iot$pp_interface_table: [low_unit .. upper_unit_bound]);
    dsp$allocate_continuous_memory (osv$mainframe_wired_cb_heap, size, seq_p);
    NEXT logical_pp_table_p^ [pp].pp_info.pp_interface_table_p: [low_unit .. upper_unit_bound] IN seq_p;
    i#real_memory_address (#LOC (logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^), rma);
    logical_pp_table_p^ [pp].pp_info.pp_interface_table_rma := rma;
    logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.pp_number := pp;
    logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.first_logical_unit := low_unit;
    logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.number_of_units := upper_unit - low_unit + 1;
    logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.pp_request_queue := NIL;
    logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.response_buffer := NIL;
    i#real_memory_address (#LOC (osv$iou_external_interrupt), rma);
    logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.interrupt_register_rma := rma;

    { Setup the Channel Interlock Pointer.

    iou_number := logical_pp_table_p^ [pp].pp_info.channel.iou_number;
    IF concurrent THEN
      IF cmv$iou_table_p^ [iou_number].cio_channel_lock_p = NIL THEN
        osp$set_status_abnormal (cmc$configuration_management_id, cme$pc_nil_cm_table,
              'CIO channel lock table is NIL.', status);
        RETURN; {----->
      IFEND;
      logical_pp_table_p^ [pp].pp_info.channel_interlock_p := cmv$iou_table_p^ [iou_number].
            cio_channel_lock_p;
      i#real_memory_address (#LOC (cmv$iou_table_p^ [iou_number].cio_channel_lock_p^), rma);
    ELSE
      IF cmv$iou_table_p^ [iou_number].nio_channel_lock_p = NIL THEN
        osp$set_status_abnormal (cmc$configuration_management_id, cme$pc_nil_cm_table,
              'NIO channel lock table is NIL.', status);
        RETURN; {----->
      IFEND;
      logical_pp_table_p^ [pp].pp_info.channel_interlock_p := cmv$iou_table_p^ [iou_number].
            nio_channel_lock_p;
      i#real_memory_address (#LOC (cmv$iou_table_p^ [iou_number].nio_channel_lock_p^), rma);
    IFEND;
    logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.channel_interlock_rma := rma;

    { Allocate and initialize the PP communication buffer.

    IF allocate_commun_buffer THEN
      IF logical_pp_table_p^ [pp].controller_info.controller_type = cmc$ms5831_x THEN
        size := 2c50(16); { Special case for HPS to allow enough space to hold data while running conf. test.
        { Based on 8192+8(sectors*tracks) which is 2bb8(16) for IBM 3.5" drives.

      ELSEIF (logical_pp_table_p^ [pp].controller_info.controller_type = cmc$msntdc_1)
{       } OR (logical_pp_table_p^ [pp].controller_info.controller_type = cmc$msntdc_2) THEN

{       size := 0;
        size := #SIZE (iot$communication_buffer);
                         {temporarly reactivate the communication buffer for pico to see if we can catch
                         {throttling at the next read
      ELSE
        size := #SIZE (iot$communication_buffer);
      IFEND;

      logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.communication_buffer_length := size;
      IF size <> 0 THEN
        dsp$allocate_continuous_memory (osv$mainframe_wired_cb_heap, size, seq_p);
        NEXT logical_pp_table_p^ [pp].pp_info.pp_communication_buffer_p IN seq_p;
        i#real_memory_address (#LOC (logical_pp_table_p^ [pp].pp_info.pp_communication_buffer_p^), rma);
        logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.communication_buffer_rma := rma;
        logical_pp_table_p^ [pp].pp_info.pp_communication_buffer_p^.controlware_command.command_code := 0;
        logical_pp_table_p^ [pp].pp_info.pp_communication_buffer_p^.controlware_command.flags.
              indirect_address := TRUE;
        logical_pp_table_p^ [pp].pp_info.pp_communication_buffer_p^.controlware_command.length := 0;
        logical_pp_table_p^ [pp].pp_info.pp_communication_buffer_p^.control_module_command.command_code := 0;
        logical_pp_table_p^ [pp].pp_info.pp_communication_buffer_p^.control_module_command.flags.
              indirect_address := TRUE;
        logical_pp_table_p^ [pp].pp_info.pp_communication_buffer_p^.control_module_command.length := 0;

        { Copy the I/O driver overlays, if any, to contiguous real memory.

        dsp$move_pp_overlays (logical_pp_table_p^ [pp].pp_info.cip_driver_name, overlay_rma, status);
        IF NOT status.normal THEN
          RETURN; {----->
        IFEND;
        logical_pp_table_p^ [pp].pp_info.pp_communication_buffer_p^.overlay_rma := overlay_rma;
      IFEND;
    IFEND;

    CASE logical_pp_table_p^ [pp].pp_info.pp_type OF
    = cmc$lpt_disk_pp_type, cmc$lpt_nad_pp_type =
      dsp$move_pp_driver (logical_pp_table_p^ [pp].pp_info.cip_driver_name,
            logical_pp_table_p^ [pp].pp_info.driver_code_p);
    ELSE
    CASEND;

    { Allocate the Response Buffer.

    dsp$allocate_continuous_memory (osv$mainframe_wired_cb_heap, #SIZE (iot$response_buffer), seq_p);
    NEXT response_buffer_p IN seq_p;
    logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.response_buffer :=
          ^response_buffer_p^.response_buffer;
    logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.limit :=
          #SIZE (logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.response_buffer^);
    i#real_memory_address (#LOC (logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.response_buffer^),
          rma);
    logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.response_buffer_rma := rma;

  PROCEND setup_pp_table;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$build_interface_tables', EJECT ??

{ PURPOSE:
{   This procedure builds the pp and unit interface tables as specified by the contents of the physical
{   configuration table and the logical configuration table.

  PROCEDURE [XDCL, #GATE] cmp$build_interface_tables
    (    pp_count: iot$pp_number;
         requested_unit_count: iot$logical_unit;
         allocate_entire_configuration: boolean;
     VAR logical_unit_table_p: ^cmt$logical_unit_table;
     VAR logical_pp_table_p: ^cmt$logical_pp_table;
     VAR status: ost$status);

    status.normal := TRUE;
    IF (pp_count < 0) OR (pp_count > UPPERVALUE (iot$pp_number)) OR
          (requested_unit_count < LOWERVALUE (requested_unit_count)) OR
          (requested_unit_count > UPPERVALUE (requested_unit_count)) THEN
      osp$set_status_abnormal (cmc$configuration_management_id, cme$it_invalid_parameter,
            'Invalid pp_count and/or unit_count in cmp$build_interface_tables', status);
      RETURN; {----->
    IFEND;

    build_unit_interface_table (requested_unit_count, allocate_entire_configuration, logical_unit_table_p,
          status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    build_pp_interface_table (pp_count, requested_unit_count, allocate_entire_configuration,
          logical_unit_table_p, logical_pp_table_p, status);

  PROCEND cmp$build_interface_tables;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$build_iou_table', EJECT ??

{ PURPOSE:
{   This routine builds the iou table and initializes the appropriate channel interlock and disk usage
{   channel tables.
{ NOTE:
{   This routine is only called twice during the deadstart process, once in the Boot and once at System Core.

  PROCEDURE [XDCL] cmp$build_iou_table
    (    number_of_ious: dst$number_of_ious;
         iou_information_table: dst$iou_information_table);

    VAR
      channel_index: 0 .. ioc$max_channel_number,
      iou_array_index: dst$number_of_ious,
      iou_index: dst$iou_number,
      iou_model_type: dst$iou_model_types,
      local_iou_table_p: ^array [ * ] of cmt$iou_table;

    ALLOCATE local_iou_table_p: [0 .. iou_information_table [number_of_ious].physical_iou_number] IN
          osv$mainframe_wired_cb_heap^;
    FOR iou_index := LOWERBOUND (local_iou_table_p^) TO UPPERBOUND (local_iou_table_p^) DO
      local_iou_table_p^ [iou_index].configured := FALSE;
      local_iou_table_p^ [iou_index].nio_channel_lock_p := NIL;
      local_iou_table_p^ [iou_index].cio_channel_lock_p := NIL;
    FOREND;

  /find_iou_info/
    FOR iou_index := LOWERBOUND (local_iou_table_p^) TO UPPERBOUND (local_iou_table_p^) DO

    /find_iou_model/
      FOR iou_array_index := 1 TO number_of_ious DO
        IF iou_information_table [iou_array_index].physical_iou_number = iou_index THEN
          local_iou_table_p^ [iou_index].configured := TRUE;
          iou_model_type := iou_information_table [iou_array_index].model_type;
          EXIT /find_iou_model/; {----->
        IFEND;
      FOREND /find_iou_model/;
      IF NOT local_iou_table_p^ [iou_index].configured THEN
        CYCLE /find_iou_info/; {----->
      IFEND;

      IF (iou_model_type = dsc$imn_i4_40_model) OR (iou_model_type = dsc$imn_i4_42_model) OR
            (iou_model_type = dsc$imn_i4_44_model) OR (iou_model_type = dsc$imn_i4_46_model) THEN
        ALLOCATE local_iou_table_p^ [iou_index].cio_channel_lock_p IN osv$mainframe_wired_cb_heap^;
        pmp$zero_out_table (#LOC (local_iou_table_p^ [iou_index].cio_channel_lock_p^),
              #SIZE (local_iou_table_p^ [iou_index].cio_channel_lock_p^));
        FOR channel_index := 0 TO ioc$max_channel_number DO
          local_iou_table_p^ [iou_index].cio_channel_lock_p^.channel_characteristics [channel_index].
                concurrent_channel := TRUE;
        FOREND;
      IFEND;

      IF iou_model_type <> dsc$imn_i4_44_model THEN
        ALLOCATE local_iou_table_p^ [iou_index].nio_channel_lock_p IN osv$mainframe_wired_cb_heap^;
        pmp$zero_out_table (#LOC (local_iou_table_p^ [iou_index].nio_channel_lock_p^),
              #SIZE (local_iou_table_p^ [iou_index].nio_channel_lock_p^));
        FOR channel_index := 0 TO ioc$max_channel_number DO
          local_iou_table_p^ [iou_index].nio_channel_lock_p^.channel_characteristics [channel_index].
                concurrent_channel := FALSE;
        FOREND;
      IFEND;
    FOREND /find_iou_info/;
    cmv$iou_table_p := local_iou_table_p;

  PROCEND cmp$build_iou_table;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$build_pp_table_entry', EJECT ??

{ PURPOSE:
{   This procedure builds an entry in the logical pp table.  This procedure is called after the physical
{   configuration is installed and activated.

  PROCEDURE [XDCL, #GATE] cmp$build_pp_table_entry
    (    pp_index: array [1 .. * ] of iot$pp_number;
     VAR active_elements: array [1 .. * ] of cmt$access_elements;
     VAR seq_p: ^SEQ ( * );
     VAR slave_seq_p: ^SEQ ( * );
     VAR program_description: array [1 .. * ] of cmt$pp_program_description;
     VAR master_pp_table_rma: ost$real_memory_address;
     VAR slave_pp_table_rma: ost$real_memory_address;
     VAR status: ost$status);

    CONST
      procedure_name = 'CMP$BUILD_PP_TABLE_ENTRY';

    VAR
      access_index: integer,
      allocate_commun_buffer: boolean,
      channel_name: cmt$element_name,
      cip_driver_name: dst$driver_name,
      cm_unit_type: cmt$unit_type,
      communication_buff_length: integer,
      concurrent: boolean,
      current_rma: integer,
      dual_pp: boolean,
      found: boolean,
      ica_element_p: ^cmt$element_definition,
      index: integer,
      io_unit_type: iot$unit_type,
      logical_unit_number: iot$logical_unit,
      low_unit: iot$logical_unit,
      peripheral_index: integer,
      pp: iot$pp_number,
      rma: integer,
      selected_pp_drivers: boolean,
      table_unit_desc_p: ^iot$unit_descriptors,
      temp_unit_des_p: ^array [ * ] of t$unit_descriptor,
      unit_class: cmt$unit_class,
      unit_ordinal: iot$logical_unit,
      unit_type: iot$unit_type,
      upper_unit: iot$logical_unit;

    status.normal := TRUE;
    low_unit := 1;
    upper_unit := 0;

    { Do NOT build UIT and PPIT again if it already exists.

    FOR pp := 1 TO UPPERBOUND (program_description) DO
      IF cmv$logical_pp_table_p^ [pp_index [pp]].flags.configured THEN
        cmv$logical_pp_table_p^ [pp_index [pp]].pp_info.pp_interface_table_p^.communication_buffer_length :=
              program_description [pp].communication_buffer_length;
        RETURN; {----->
      IFEND;
    FOREND;

    dual_pp := (UPPERBOUND (program_description) = 2);

    FOR pp := 1 TO UPPERBOUND (program_description) DO

      get_driver_name (program_description [pp].iou_program_name,
            cmv$logical_pp_table_p^ [pp_index [pp]].pp_info.channel.channel_protocol, dual_pp,
            cmv$logical_pp_table_p^ [pp_index [pp]].pp_info.driver_name);

      cip_driver_name := program_description [pp].iou_program_name (1, 7);

      cmv$logical_pp_table_p^ [pp_index [pp]].pp_info.cip_driver_name := cip_driver_name;
      retrieve_logical_pp_flags (cip_driver_name, pp_index [pp], cmv$logical_pp_table_p);

      selected_pp_drivers := ((cip_driver_name = 'ICAD') OR (cip_driver_name = 'NDI0') OR (cip_driver_name =
            'NPDR') OR (cip_driver_name = 'ESMD') OR (cip_driver_name = 'IVB0') OR
            (cip_driver_name = 'IVB4') OR (cip_driver_name = 'NETW'));

      { Build Unit Descriptors.

      IF program_description [pp].element_access <> NIL THEN
        low_unit := ioc$max_unit_number;
        upper_unit := 0;
        FOR access_index := 1 TO UPPERBOUND (program_description [pp].element_access^) DO
          unit_ordinal := 0;

        /lun_loop/
          FOR index := cmc$job_template_unit_ordinal TO UPPERBOUND (cmv$logical_unit_table^) DO
            IF active_elements [pp].accessed_elements_p^ [access_index].lun <> 0 THEN

              IF cmv$logical_unit_table^ [index].logical_unit_number =
                    active_elements [pp].accessed_elements_p^ [access_index].lun THEN

                { Build UIT for ICA, MTI, MDI, ESM or LCN but NOT for TAPE.

                IF selected_pp_drivers THEN
                  IF upper_unit < index THEN
                    upper_unit := index;
                  IFEND;
                  IF low_unit > index THEN
                    low_unit := index;
                  IFEND;
                  IF cmv$logical_unit_table^ [index].unit_interface_table = NIL THEN
                    unit_ordinal := active_elements [pp].accessed_elements_p^ [access_index].lun;
                  IFEND;
                ELSE

                  { Build UIT for Foreign Devices.

                  IF upper_unit < active_elements [pp].accessed_elements_p^ [access_index].lun THEN
                    upper_unit := active_elements [pp].accessed_elements_p^ [access_index].lun;
                  IFEND;
                  IF low_unit > active_elements [pp].accessed_elements_p^ [access_index].lun THEN
                    low_unit := active_elements [pp].accessed_elements_p^ [access_index].lun;
                  IFEND;
                  IF cmv$logical_unit_table^ [index].unit_interface_table = NIL THEN
                    unit_ordinal := active_elements [pp].accessed_elements_p^ [access_index].lun;
                  IFEND;
                IFEND;
                EXIT /lun_loop/; {----->
              IFEND;
            ELSE

              { Build UIT for device NOT in the active configuration.

              IF NOT cmv$logical_unit_table^ [index].configured AND
                    (cmv$logical_unit_table^ [index].unit_interface_table = NIL) AND
                    (cmv$logical_unit_table^ [index].logical_unit_number = 0) THEN
                unit_ordinal := index;
                active_elements [pp].accessed_elements_p^ [access_index].lun := index;
                IF upper_unit < index THEN
                  upper_unit := index;
                IFEND;
                IF low_unit > index THEN
                  low_unit := index;
                IFEND;
              ELSE
                IF upper_unit < active_elements [pp].accessed_elements_p^ [access_index].lun THEN
                  upper_unit := active_elements [pp].accessed_elements_p^ [access_index].lun;
                IFEND;
                IF low_unit > active_elements [pp].accessed_elements_p^ [access_index].lun THEN
                  low_unit := active_elements [pp].accessed_elements_p^ [access_index].lun;
                IFEND;
              IFEND;
              EXIT /lun_loop/; {----->
            IFEND;
          FOREND /lun_loop/;

          { Build UIT.

          IF unit_ordinal <> 0 THEN
            IF cip_driver_name = 'ICAD' THEN
              cmp$pc_get_logical_unit (unit_ordinal, ica_element_p, status);
              IF NOT status.normal THEN
                RETURN; {----->
              IFEND;
              cmp$get_unit_type (ica_element_p^.product_id, cm_unit_type, io_unit_type, unit_class, found);
              unit_type := io_unit_type;
            ELSEIF (cip_driver_name = 'NPDR') OR (cip_driver_name = 'NDI0') THEN
              unit_type := ioc$dt_lcn_1;
            ELSEIF cip_driver_name = 'NETW' THEN
              unit_type := ioc$dt_mdi_1;
            ELSEIF cip_driver_name = 'ESMD' THEN
              unit_type := ioc$dt_file_server;
            ELSEIF (cip_driver_name = 'IVB0') OR (cip_driver_name = 'IVB4') THEN
              unit_type := ioc$dt_expresslink;
            ELSE
              unit_type := ioc$dt_foreign_device;
            IFEND;
            build_logical_unit_entry (unit_ordinal, unit_type, status);
            IF NOT status.normal THEN
              RETURN; {----->
            IFEND;
          IFEND;
        FOREND;
      IFEND;

      IF (program_description [pp].element_access <> NIL) AND
            (cmc$channel IN program_description [pp].element_access^ [1].physical_address_specifier) THEN
        concurrent := (program_description [pp].element_access^ [1].channel.ordinal > cmc$channel27);
      ELSEIF cmv$logical_pp_table_p^ [pp_index [pp]].flags.reservd_by_other_has_ch_present THEN
        concurrent := (cmv$logical_pp_table_p^ [pp_index [pp]].pp_info.channel.channel_protocol =
              dsc$cpt_cio);
      ELSE
        concurrent := (program_description [pp].pp_identification.ordinal > cmc$pp19);
      IFEND;

      { Build UNIT_DESCRIPTORS in for PPIT.

      IF program_description [pp].element_access <> NIL THEN
        PUSH temp_unit_des_p: [low_unit .. upper_unit];
        pmp$zero_out_table (#LOC (temp_unit_des_p^), #SIZE (temp_unit_des_p^));
        FOR access_index := 1 TO UPPERBOUND (program_description [pp].element_access^) DO
          logical_unit_number := active_elements [pp].accessed_elements_p^ [access_index].lun;
          IF (logical_unit_number <> 0) AND (logical_unit_number >= low_unit) AND
                (logical_unit_number <= upper_unit) THEN
            temp_unit_des_p^ [logical_unit_number].configured := TRUE;
            cmp$convert_channel_ordinal (program_description [pp].element_access^ [access_index].
                  channel.ordinal, channel_name, temp_unit_des_p^ [logical_unit_number].channel.number,
                  temp_unit_des_p^ [logical_unit_number].channel.concurrent,
                  temp_unit_des_p^ [logical_unit_number].channel.port, status);
            IF NOT status.normal THEN
              RETURN; {----->
            IFEND;
            IF selected_pp_drivers THEN
              temp_unit_des_p^ [logical_unit_number].controller_number :=
                    program_description [pp].element_access^ [access_index].channel_address;
            ELSE
              temp_unit_des_p^ [logical_unit_number].unit_number := program_description [pp].
                    element_access^ [access_index].unit_address;
              temp_unit_des_p^ [logical_unit_number].controller_number :=
                    program_description [pp].element_access^ [access_index].channel_address;
            IFEND;
          IFEND;
        FOREND;
      IFEND;

      IF dual_pp THEN
        IF pp = 1 THEN
          cmv$logical_pp_table_p^ [pp_index [pp]].pp_info.logical_partner_pp_index := pp_index [pp] + 1;
        ELSE
          cmv$logical_pp_table_p^ [pp_index [pp]].pp_info.logical_partner_pp_index := pp_index [pp] - 1;
        IFEND;
      IFEND;

      { Get response handler pointer.

      allocate_commun_buffer := TRUE;
      IF cip_driver_name = 'ICAD' THEN
        cmp$get_response_handler (cmc$ca2629_2, cmv$logical_pp_table_p^ [pp_index [pp]].handlers.
              response_handler_p);
        cmv$logical_pp_table_p^ [pp_index [pp]].controller_info.controller_type := cmc$ca2629_2;
      ELSEIF cip_driver_name = 'SPI ' THEN
        cmv$logical_pp_table_p^ [pp_index [pp]].handlers.response_handler_p := osv$spi_response_processor;
        allocate_commun_buffer := FALSE;
      ELSEIF (cip_driver_name = 'NPDR') OR (cip_driver_name = 'NDI0') THEN
        cmp$get_response_handler (cmc$lcn380_170, cmv$logical_pp_table_p^ [pp_index [pp]].handlers.
              response_handler_p);
        cmv$logical_pp_table_p^ [pp_index [pp]].controller_info.controller_type := cmc$lcn380_170;
      ELSEIF cip_driver_name = 'ESMD' THEN
        cmp$get_response_handler (cmc$fs740_200, cmv$logical_pp_table_p^ [pp_index [pp]].handlers.
              response_handler_p);
        cmv$logical_pp_table_p^ [pp_index [pp]].controller_info.controller_type := cmc$fs740_200;
      ELSEIF (cip_driver_name = 'IVB0') OR (cip_driver_name = 'IVB4') THEN
        cmp$get_response_handler (cmc$expresslink, cmv$logical_pp_table_p^ [pp_index [pp]].handlers.
              response_handler_p);
        cmv$logical_pp_table_p^ [pp_index [pp]].controller_info.controller_type := cmc$expresslink;
      ELSEIF cip_driver_name = 'NETW' THEN

      /peripheral_loop/
        FOR peripheral_index := LOWERBOUND (cmv$peripheral_element_table.pointer^)
              TO UPPERBOUND (cmv$peripheral_element_table.pointer^) DO
          IF cmv$peripheral_element_table.pointer^ [peripheral_index].
                logical_unit_number = active_elements [pp].accessed_elements_p^ [1].lun THEN
            IF cmv$peripheral_element_table.pointer^ [peripheral_index].product_id.product_number =
                  ' $2620' THEN
              cmp$get_response_handler (cmc$mti2620_21x, cmv$logical_pp_table_p^ [pp_index [pp]].handlers.
                    response_handler_p);
              cmv$logical_pp_table_p^ [pp_index [pp]].controller_info.controller_type := cmc$mti2620_21x;
            ELSEIF cmv$peripheral_element_table.pointer^ [peripheral_index].product_id.product_number =
                  ' $2621' THEN
              cmp$get_response_handler (cmc$mdi2621_21x, cmv$logical_pp_table_p^ [pp_index [pp]].handlers.
                    response_handler_p);
              cmv$logical_pp_table_p^ [pp_index [pp]].controller_info.controller_type := cmc$mdi2621_21x;
            IFEND;
            EXIT /peripheral_loop/; {----->
          IFEND;
        FOREND /peripheral_loop/;

        { This code is only used for internal testing.

      ELSEIF cmv$logical_pp_table_p^ [pp_index [pp]].pp_info.pp_type = cmc$lpt_tape_pp_type THEN
        cmp$get_response_handler (cmc$mt5698_xx, cmv$logical_pp_table_p^ [pp_index [pp]].handlers.
              response_handler_p);
      ELSE

        { Foreign Subsystem, set ALLOCATE_COMMUN_BUFFER to FALSE assuming caller already allocate wired area
        { for PP communication buffer.

        allocate_commun_buffer := FALSE;
        cmv$logical_pp_table_p^ [pp_index [pp]].handlers.response_handler_p := cmv$default_response_handler;
      IFEND;

      setup_pp_table (pp_index [pp], concurrent, allocate_commun_buffer, low_unit, upper_unit,
            cmv$logical_pp_table_p, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;
      IF pp = 1 THEN
        master_pp_table_rma := cmv$logical_pp_table_p^ [pp_index [pp]].pp_info.pp_interface_table_rma;
      ELSE
        slave_pp_table_rma := cmv$logical_pp_table_p^ [pp_index [pp]].pp_info.pp_interface_table_rma;
      IFEND;

      IF NOT allocate_commun_buffer AND (program_description [pp].communication_buffer_length > 0) THEN
        communication_buff_length := program_description [pp].communication_buffer_length;
        IF communication_buff_length = osc$max_page_size THEN
          communication_buff_length := communication_buff_length - 1;
        IFEND;
        IF pp = 1 THEN
          i#real_memory_address (#LOC (seq_p^), current_rma);
          cmv$logical_pp_table_p^ [pp_index [pp]].pp_info.pp_communication_buffer_p := #LOC (seq_p^);
        ELSE
          i#real_memory_address (#LOC (slave_seq_p^), current_rma);
          cmv$logical_pp_table_p^ [pp_index [pp]].pp_info.pp_communication_buffer_p := #LOC (slave_seq_p^);
        IFEND;
        cmv$logical_pp_table_p^ [pp_index [pp]].pp_info.pp_interface_table_p^.communication_buffer_length :=
              communication_buff_length;
        cmv$logical_pp_table_p^ [pp_index [pp]].pp_info.pp_interface_table_p^.communication_buffer_rma :=
              current_rma;
      IFEND;

      { Initialize unit descriptors in the pp_interface_table_p.

      IF program_description [pp].element_access <> NIL THEN
        table_unit_desc_p := ^cmv$logical_pp_table_p^ [pp_index [pp]].pp_info.pp_interface_table_p^.
              unit_descriptors;
        build_unit_descriptors (low_unit, upper_unit, cmv$logical_unit_table, NIL, temp_unit_des_p,
              cmv$logical_pp_table_p^ [pp_index [pp]].controller_info.controller_type, table_unit_desc_p);
      ELSE
        cmv$logical_pp_table_p^ [pp_index [pp]].pp_info.pp_interface_table_p^.number_of_units := 0;
      IFEND;
      cmv$logical_pp_table_p^ [pp_index [pp]].flags.configured := TRUE;
    FOREND;

    cmp$get_max_number_of_pp (cmv$max_number_of_pp);

    { Load controlware for the ICA.

    FOR pp := 1 TO UPPERBOUND (program_description) DO
      IF program_description [pp].iou_program_name (1, 4) = 'ICAD' THEN
        cmp$load_controller_module (cmc$load_controlware, cmv$logical_pp_table_p, status);
        IF NOT status.normal THEN
          RETURN; {----->
        IFEND;
      IFEND;
    FOREND;

  PROCEND cmp$build_pp_table_entry;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$clear_channel_interlock', EJECT ??

{ PURPOSE:
{   This procedure clears the entry in the channel interlock table for the sepcified IOU and logical
{   PP number.

  PROCEDURE [XDCL, #GATE] cmp$clear_channel_interlock
    (    iou_number: dst$iou_number;
         logical_pp_number: iot$pp_number;
     VAR status: ost$status);

    TYPE
      t$channel_lock = record
        case (c$cpu_lock, c$pp_lock) of
        = c$cpu_lock =
          cpu_lock: integer,
        = c$pp_lock =
          pp_lock: iot$table_lock_entry,
        casend,
      recend;

    VAR
      actual: t$channel_lock,
      ch_p: ^integer,
      done: boolean,
      final: t$channel_lock,
      found: boolean,
      index: integer,
      initial: t$channel_lock;

    status.normal := TRUE;

    IF NOT cmv$iou_table_p^ [iou_number].configured THEN
      RETURN; {----->
    IFEND;

    IF cmv$iou_table_p^ [iou_number].nio_channel_lock_p <> NIL THEN
      FOR index := LOWERBOUND (cmv$iou_table_p^ [iou_number].nio_channel_lock_p^.channel_table)
            TO UPPERBOUND (cmv$iou_table_p^ [iou_number].nio_channel_lock_p^.channel_table) DO
        IF cmv$iou_table_p^ [iou_number].nio_channel_lock_p^.channel_table [index].locking_pp =
              logical_pp_number THEN
          ch_p := #LOC (cmv$iou_table_p^ [iou_number].nio_channel_lock_p^.channel_table [index]);
          actual.cpu_lock := 0;
          done := FALSE;
          REPEAT
            initial.pp_lock := actual.pp_lock;
            final.pp_lock := actual.pp_lock;
            final.pp_lock.channel_locked := FALSE;
            final.pp_lock.maintenance_need_channel := FALSE;
            osp$set_locked_variable (ch_p^, initial.cpu_lock, final.cpu_lock, actual.cpu_lock, done);
          UNTIL done;
          RETURN; {----->
        IFEND;
      FOREND;
    IFEND;

    IF cmv$iou_table_p^ [iou_number].cio_channel_lock_p <> NIL THEN
      FOR index := LOWERBOUND (cmv$iou_table_p^ [iou_number].cio_channel_lock_p^.channel_table)
            TO UPPERBOUND (cmv$iou_table_p^ [iou_number].cio_channel_lock_p^.channel_table) DO
        IF cmv$iou_table_p^ [iou_number].cio_channel_lock_p^.channel_table [index].locking_pp =
              logical_pp_number THEN
          ch_p := #LOC (cmv$iou_table_p^ [iou_number].cio_channel_lock_p^.channel_table [index]);
          actual.cpu_lock := 0;
          done := FALSE;
          REPEAT
            initial.pp_lock := actual.pp_lock;
            final.pp_lock := actual.pp_lock;
            final.pp_lock.channel_locked := FALSE;
            final.pp_lock.maintenance_need_channel := FALSE;
            osp$set_locked_variable (ch_p^, initial.cpu_lock, final.cpu_lock, actual.cpu_lock, done);
          UNTIL done;
          RETURN; {----->
        IFEND;
      FOREND;
    IFEND;

  PROCEND cmp$clear_channel_interlock;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$clear_element_lock', EJECT ??

  PROCEDURE [XDCL, #GATE] cmp$clear_element_lock
    (VAR status: ost$status);

    status.normal := TRUE;

    osp$clear_signature_lock (cmv$element_reservation_lock, status);

  PROCEND cmp$clear_element_lock;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$clear_unit_shared', EJECT ??

{ PURPOSE:
{   This procedure clears the unit shared bit of the given logical unit

  PROCEDURE [XDCL, #GATE] cmp$clear_unit_shared
    (    logical_unit: iot$logical_unit;
         set_lock: boolean);

    VAR
      index: integer,
      lock_obtained: boolean;

    IF cmv$logical_unit_table = NIL THEN
      RETURN; {----->
    IFEND;

    { Check to see if the peripheral element table is built.  If it is not build then it is before
    { transition time, go ahead and clear the unit shared.

    IF cmv$peripheral_element_table.pointer = NIL THEN
      IF NOT cmv$logical_unit_table^ [logical_unit].configured THEN
        RETURN; {----->
      IFEND;
      IF set_lock THEN
        cmp$lock_lun_entry (logical_unit, lock_obtained);
        IF NOT lock_obtained THEN
          osp$system_error (' Unable to set CMV$LOGICAL_UNIT lock', NIL);
        IFEND;
      IFEND;
      cmv$logical_unit_table^ [logical_unit].unit_interface_table^.unit_shared := FALSE;
      IF set_lock THEN
        cmp$unlock_lun_entry (logical_unit, lock_obtained);
      IFEND;
      RETURN; {----->
    IFEND;

    IF UPPERBOUND (cmv$peripheral_element_table.pointer^) <= 0 THEN
      RETURN; {----->
    IFEND;

    FOR index := 1 TO UPPERBOUND (cmv$peripheral_element_table.pointer^) DO
      IF (cmv$peripheral_element_table.pointer^ [index].logical_unit_number = logical_unit) AND
            (cmv$peripheral_element_table.pointer^ [index].maintenance_activity.con_access_job_list <>
            NIL) AND (cmv$peripheral_element_table.pointer^ [index].maintenance_activity.con_access_job_list^.
            forward_link = NIL) AND cmv$logical_unit_table^ [logical_unit].configured THEN

        IF set_lock THEN
          cmp$lock_lun_entry (logical_unit, lock_obtained);
          IF NOT lock_obtained THEN
            osp$system_error (' Unable to set CMV$LOGICAL_UNIT lock', NIL);
          IFEND;
        IFEND;
        cmv$logical_unit_table^ [logical_unit].unit_interface_table^.unit_shared := FALSE;
        IF set_lock THEN
          cmp$unlock_lun_entry (logical_unit, lock_obtained);
        IFEND;
        RETURN; {----->
      IFEND;
    FOREND;

  PROCEND cmp$clear_unit_shared;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$crack_physical_address ', EJECT ??

{ PURPOSE:
{   This procedure convert the physical address given the information about an element being reserved via
{   the CYBIL Program Interface type cmt$element_reservation.

  PROCEDURE [XDCL] cmp$crack_physical_address
    (    element_reservation: cmt$element_reservation;
     VAR iou: dst$iou_number;
     VAR channel: cmt$physical_channel;
     VAR channel_address: cmt$physical_equipment_number;
     VAR unit_address: cmt$physical_unit_number;
     VAR status: ost$status);

    VAR
      channel_definition: cmt$data_channel_definition,
      channel_name: cmt$element_name;

    status.normal := TRUE;
    iou := 0;
    channel.number := 0;
    channel.port := cmc$unspecified_port;
    channel.concurrent := FALSE;
    channel_address := 0;
    unit_address := 0;

    CASE element_reservation.element_type OF
    = cmc$data_channel_element =
      cmp$get_channel_def (element_reservation.channel_descriptor, channel_definition, status);
      IF NOT status.normal THEN
        IF status.condition = cme$lcm_element_not_found THEN
          status.normal := TRUE;
        ELSE
          RETURN; {----->
        IFEND;
      IFEND;

      cmp$convert_iou_name (channel_definition.iou, iou, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;

      channel.number := channel_definition.number;
      channel.concurrent := channel_definition.concurrent;
      channel.port := channel_definition.port;

    = cmc$controller_element, cmc$storage_device_element, cmc$external_processor_element,
          cmc$communications_element, cmc$channel_adapter_element =
      IF element_reservation.peripheral_descriptor.use_logical_identification THEN
        RETURN; {----->
      IFEND;

      IF cmc$iou IN element_reservation.peripheral_descriptor.hardware_address.physical_address_specifier THEN
        cmp$convert_iou_name (element_reservation.peripheral_descriptor.hardware_address.iou, iou, status);
        IF NOT status.normal THEN
          RETURN; {----->
        IFEND;
      IFEND;

      IF cmc$channel IN element_reservation.peripheral_descriptor.hardware_address.
            physical_address_specifier THEN
        cmp$convert_channel_ordinal (element_reservation.peripheral_descriptor.hardware_address.channel.
              ordinal, channel_name, channel.number, channel.concurrent, channel.port, status);
        IF NOT status.normal THEN
          RETURN; {----->
        IFEND;
      IFEND;

      IF cmc$channel_address IN element_reservation.peripheral_descriptor.hardware_address.
            physical_address_specifier THEN
        channel_address := element_reservation.peripheral_descriptor.hardware_address.channel_address;
      IFEND;

      IF cmc$unit_address IN element_reservation.peripheral_descriptor.hardware_address.
            physical_address_specifier THEN
        unit_address := element_reservation.peripheral_descriptor.hardware_address.unit_address;
      IFEND;

    = cmc$pp_element =
      IF element_reservation.pp_reservation.selector = cmc$choose_pp_by_channel THEN
        cmp$convert_iou_name (element_reservation.pp_reservation.channel.iou, iou, status);
        IF NOT status.normal THEN
          RETURN; {----->
        IFEND;
        cmp$convert_channel_ordinal (element_reservation.pp_reservation.channel.ordinal, channel_name,
              channel.number, channel.concurrent, channel.port, status);
        IF NOT status.normal THEN
          RETURN; {----->
        IFEND;
      IFEND;
    ELSE
      osp$set_status_abnormal (cmc$configuration_management_id, cme$cm_end_case_error,
            'CMP$CRACK_PHYSICAL_ADDRESS', status);
    CASEND;

  PROCEND cmp$crack_physical_address;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$dft_acquire_maintenance', EJECT ??

{ PURPOSE:
{   This procedure acquire maintenance access to an element for any DFT request that requires accessing the
{   deadstart sector.  This process is required to avoid conflict should MALET/VE be running at the same time
{   on the same element.

  PROCEDURE [XDCL] cmp$dft_acquire_maintenance
    (    device_path: dst$device_path;
     VAR device_information: cmt$device_information;
     VAR controller_name: cmt$element_name;
     VAR element_name: cmt$element_name;
     VAR unit_shared_interlock_set: boolean;
     VAR maintenance_acquired: boolean;
     VAR status: ost$status);

    VAR
      channel_name: cmt$element_name,
      channel_ordinal: cmt$channel_ordinal,
      channel_port: cmt$channel_port,
      concurrent: boolean,
      controller_definition_p: ^cmt$element_definition,
      iou_array_index: dst$number_of_ious,
      iou_number: dst$iou_number,
      iou_information_table: dst$iou_information_table,
      iou_model_type: dst$iou_model_types,
      controller_name_found: boolean,
      element_definition_p: ^cmt$element_definition,
      element_descriptor: cmt$element_descriptor,
      element_reservation: cmt$element_reservation,
      element_type: cmt$element_type,
      first_logical_unit: iot$logical_unit,
      iou_name: cmt$element_name,
      job_name: jmt$system_supplied_name,
      length: integer,
      number_of_ious: dst$number_of_ious,
      number_of_units: iot$logical_unit,
      number_string: string (6),
      peripheral_index: integer,
      port_number: cmt$data_storage_port_number,
      pp_number: iot$pp_number,
      unit_information_found: boolean,
      unit_number: iot$logical_unit,
      user_name: jmt$user_supplied_name,
      valid: boolean;

    status.normal := TRUE;
    unit_shared_interlock_set := FALSE;
    maintenance_acquired := FALSE;
    concurrent := FALSE;
    channel_port := cmc$unspecified_port;

    { If the configuration is not activated then it is not necessary to acquire maintenance.

    IF NOT cmv$configuration_activated THEN
      RETURN; {----->
    IFEND;

    unit_information_found := FALSE;

    { Check to see if the tables are defined.

    IF cmv$logical_pp_table_p = NIL THEN
      osp$set_status_abnormal (cmc$configuration_management_id, cme$pointer_not_defined,
            'cmv$logical_pp_table_p', status);
      RETURN; {----->
    IFEND;
    IF cmv$peripheral_element_table.pointer = NIL THEN
      osp$set_status_abnormal (cmc$configuration_management_id, cme$pointer_not_defined,
            'cmv$peripheral_element_table.pointer', status);
      RETURN; {----->
    IFEND;

    { Find the channel number and unit type.  The maintenance only needs to be acquired for 844, 885 and HPS
    { device types.

    device_information.channel_number := device_path.channel_number;
    device_information.iou_number := device_path.iou_number;
    dsp$retrieve_iou_information (number_of_ious, iou_information_table);

  /find_iou_model/
    FOR iou_array_index := LOWERBOUND (iou_information_table) TO UPPERBOUND (iou_information_table) DO
      IF iou_information_table [iou_array_index].physical_iou_number = device_information.iou_number THEN
        iou_model_type := iou_information_table [iou_array_index].model_type;
        EXIT /find_iou_model/; {----->
      IFEND;
    FOREND /find_iou_model/;

    concurrent := (iou_model_type = dsc$imn_i4_44_model) OR (iou_model_type = dsc$imn_i4_46_model);
    CASE device_path.device_type OF
    = dmc$844_double_density =
      device_information.unit_type := ioc$dt_ms844_4x;
    = dmc$885 =
      device_information.unit_type := ioc$dt_ms885_1x;
    ELSE
      RETURN; {----->
    CASEND;

    { Search the logical pp table for the correct unit.

  /search_pp_table/
    FOR pp_number := 1 TO UPPERBOUND (cmv$logical_pp_table_p^) DO
      IF NOT cmv$logical_pp_table_p^ [pp_number].flags.configured THEN
        CYCLE /search_pp_table/; {----->
      IFEND;

      first_logical_unit := cmv$logical_pp_table_p^ [pp_number].pp_info.pp_interface_table_p^.
            first_logical_unit;
      number_of_units := cmv$logical_pp_table_p^ [pp_number].pp_info.pp_interface_table_p^.number_of_units;

    /find_unit_information/
      FOR unit_number := first_logical_unit TO (first_logical_unit + number_of_units - 1) DO
        IF NOT cmv$logical_unit_table^ [unit_number].configured THEN
          CYCLE /find_unit_information/; {----->
        IFEND;
        IF cmv$logical_unit_table^ [unit_number].logical_unit_number <> unit_number THEN
          CYCLE /find_unit_information/; {----->
        IFEND;
        IF cmv$logical_unit_table^ [unit_number].unit_interface_table^.unit_type <>
              device_information.unit_type THEN
          CYCLE /find_unit_information/; {----->
        IFEND;
        IF (cmv$logical_pp_table_p^ [pp_number].pp_info.pp_interface_table_p^.unit_descriptors [unit_number].
              physical_path.channel_number <> device_path.channel_number) OR
              (cmv$logical_pp_table_p^ [pp_number].pp_info.channel.iou_number <> device_path.iou_number) OR
              (concurrent <> cmv$logical_pp_table_p^ [pp_number].pp_info.channel_interlock_p^.
              channel_characteristics [device_path.channel_number].concurrent_channel) THEN
          CYCLE /find_unit_information/; {----->
        IFEND;

        device_information.unit_number := cmv$logical_pp_table_p^ [pp_number].pp_info.pp_interface_table_p^.
              unit_descriptors [unit_number].physical_path.physical_unit_number;
        device_information.equipment_number := cmv$logical_pp_table_p^ [pp_number].pp_info.
              pp_interface_table_p^.unit_descriptors [unit_number].physical_path.controller_number;

        IF device_information.unit_number = device_path.unit_number THEN
          device_information.logical_unit := unit_number;
          unit_information_found := TRUE;
          EXIT /search_pp_table/; {----->
        IFEND;

      FOREND /find_unit_information/;
    FOREND /search_pp_table/;

    IF NOT unit_information_found THEN
      IF NOT device_path.cip_path THEN
        osp$set_status_abnormal (cmc$configuration_management_id, cme$search_not_found, 'unit information',
              status);
      IFEND;
      RETURN; {----->
    IFEND;

    { Find the element name and the controller name.

    cmp$get_element_name_via_lun (device_information.logical_unit, element_name, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;
    cmp$pc_get_element (element_name, {not used} iou_name, element_definition_p, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;
    controller_name_found := FALSE;

  /find_controller_name/
    FOR port_number := LOWERVALUE (cmt$data_storage_port_number)
          TO UPPERVALUE (cmt$data_storage_port_number) DO
      IF NOT element_definition_p^.storage_device.connection.port [port_number].configured THEN
        CYCLE /find_controller_name/; {----->
      IFEND;
      cmp$pc_get_element (element_definition_p^.storage_device.connection.port [port_number].element_name,
            iou_name, controller_definition_p, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;
      IF (controller_definition_p^.element_type = cmc$controller_element) AND
            (controller_definition_p^.controller.physical_equipment_number =
            device_information.equipment_number) THEN
        controller_name := controller_definition_p^.element_name;
        controller_name_found := TRUE;
        EXIT /find_controller_name/; {----->
      IFEND;
    FOREND /find_controller_name/;

    IF NOT controller_name_found THEN
      osp$set_status_abnormal (cmc$configuration_management_id, cme$search_not_found, 'controller name',
            status);
      RETURN; {----->
    IFEND;

    pmp$get_job_names (user_name, job_name, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;
    cmp$convert_iou_number (device_information.iou_number, iou_name, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

  /add_access_job/
    FOR element_type := LOWERVALUE (cmt$element_type) TO UPPERVALUE (cmt$element_type) DO
      element_descriptor.element_type := element_type;
      CASE element_descriptor.element_type OF
      = cmc$data_channel_element =
        element_descriptor.channel_descriptor.iou := iou_name;
        element_descriptor.channel_descriptor.use_logical_identification := TRUE;
        cmp$convert_channel_number (device_information.channel_number, concurrent, channel_port,
              channel_ordinal, channel_name, valid);
        IF valid THEN
          element_descriptor.channel_descriptor.name := channel_name;
        ELSE
          STRINGREP (number_string, length, device_information.channel_number);
          osp$set_status_abnormal (cmc$configuration_management_id, cme$invalid_channel_number, number_string,
                status);
          RETURN; {----->
        IFEND;

      = cmc$controller_element =
        element_descriptor.peripheral_descriptor.use_logical_identification := TRUE;
        element_descriptor.peripheral_descriptor.element_name := controller_name;

      = cmc$storage_device_element =
        element_descriptor.peripheral_descriptor.use_logical_identification := TRUE;
        element_descriptor.peripheral_descriptor.element_name := element_name;
      ELSE
        CYCLE /add_access_job/; {----->
      CASEND;

      cmp$search_peripheral_table (element_descriptor, element_reservation, FALSE, peripheral_index, status);
      IF NOT status.normal AND (status.condition = cme$cm_element_not_found) THEN
        status.normal := TRUE;
        CYCLE /add_access_job/; {----->
      IFEND;
      msp$add_con_access_job (peripheral_index, job_name, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;
    FOREND /add_access_job/;

    maintenance_acquired := TRUE;

    cmp$set_unit_shared (device_information.logical_unit, unit_shared_interlock_set);

  PROCEND cmp$dft_acquire_maintenance;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$dft_release_maintenance', EJECT ??

{ PURPOSE:
{   This procedure releases the maintenance access for DFT requests requiring deadstart sector access.

  PROCEDURE [XDCL] cmp$dft_release_maintenance
    (    device_information: cmt$device_information;
         controller_name: cmt$element_name;
         element_name: cmt$element_name;
         unit_shared_interlock_set: boolean;
     VAR status: ost$status);

    VAR
      channel_name: cmt$element_name,
      channel_ordinal: cmt$channel_ordinal,
      channel_port: cmt$channel_port,
      concurrent: boolean,
      element_descriptor: cmt$element_descriptor,
      element_reservation: cmt$element_reservation,
      element_type: cmt$element_type,
      iou_name: cmt$element_name,
      job_name: jmt$system_supplied_name,
      length: integer,
      number_string: string (6),
      peripheral_index: integer,
      user_name: jmt$user_supplied_name,
      valid: boolean;

    status.normal := TRUE;
    concurrent := FALSE;
    channel_port := cmc$unspecified_port;

    { If the configuration is not activated then it is not necessary to release maintenance.

    IF NOT cmv$configuration_activated THEN
      RETURN; {----->
    IFEND;

    cmp$clear_unit_shared (device_information.logical_unit, unit_shared_interlock_set);

    { Check to see if the tables are defined.

    IF cmv$peripheral_element_table.pointer = NIL THEN
      osp$set_status_abnormal (cmc$configuration_management_id, cme$pointer_not_defined,
            'cmv$peripheral_element_table.pointer', status);
      RETURN; {----->
    IFEND;

    pmp$get_job_names (user_name, job_name, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    cmp$convert_iou_number (device_information.iou_number, iou_name, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

  /delete_access_job/
    FOR element_type := LOWERVALUE (cmt$element_type) TO UPPERVALUE (cmt$element_type) DO
      element_descriptor.element_type := element_type;
      CASE element_descriptor.element_type OF
      = cmc$data_channel_element =
        element_descriptor.channel_descriptor.iou := iou_name;
        element_descriptor.channel_descriptor.use_logical_identification := TRUE;
        cmp$convert_channel_number (device_information.channel_number, concurrent, channel_port,
              channel_ordinal, channel_name, valid);
        IF valid THEN
          element_descriptor.channel_descriptor.name := channel_name;
        ELSE
          STRINGREP (number_string, length, device_information.channel_number);
          osp$set_status_abnormal (cmc$configuration_management_id, cme$invalid_channel_number, number_string,
                status);
          RETURN; {----->
        IFEND;

      = cmc$controller_element =
        element_descriptor.peripheral_descriptor.use_logical_identification := TRUE;
        element_descriptor.peripheral_descriptor.element_name := controller_name;
      = cmc$storage_device_element =
        element_descriptor.peripheral_descriptor.use_logical_identification := TRUE;
        element_descriptor.peripheral_descriptor.element_name := element_name;
      ELSE
        CYCLE /delete_access_job/; {----->
      CASEND;

      cmp$search_peripheral_table (element_descriptor, element_reservation, FALSE, peripheral_index, status);
      IF NOT status.normal AND (status.condition = cme$cm_element_not_found) THEN
        status.normal := TRUE;
        CYCLE /delete_access_job/; {----->
      IFEND;
      msp$delete_con_access_job (peripheral_index, job_name, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;
    FOREND /delete_access_job/;

  PROCEND cmp$dft_release_maintenance;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$find_primary_controller', EJECT ??

{ PURPOSE:
{   This procedure will return the primary controller for the specified logical unit.  If no controllers are
{   in the on state the first controller will be returned.

  PROCEDURE [XDCL, #GATE] cmp$find_primary_controller
    (    logical_unit_number: iot$logical_unit;
     VAR controller_element_p: ^cmt$element_definition;
     VAR status: ost$status);

    VAR
      channel_port: integer,
      channel_state: cmt$element_state,
      controller_state: cmt$element_state,
      iou_name: cmt$element_name,
      local_status: ost$status,
      port: cmt$data_storage_port_number,
      state: cmt$element_state,
      storage_device_element_p: ^cmt$element_definition;

    status.normal := TRUE;
    controller_element_p := NIL;
    cmp$pc_get_logical_unit (logical_unit_number, storage_device_element_p, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    FOR port := LOWERVALUE (cmt$data_storage_port_number) TO UPPERVALUE (cmt$data_storage_port_number) DO
      IF storage_device_element_p^.storage_device.connection.port [port].configured THEN
        cmp$get_element_state (storage_device_element_p^.storage_device.connection.port [port].element_name,
              iou_name, controller_state, local_status);

        { Find the first ON controller connected to the storage device element or if there is only one
        { controller, it will be returned.

        IF (controller_state = cmc$on) OR (controller_element_p = NIL) THEN
          cmp$pc_get_element (storage_device_element_p^.storage_device.connection.port [port].element_name,
                iou_name, controller_element_p, local_status);
          IF local_status.normal THEN
            FOR channel_port := LOWERVALUE (cmt$controller_port_number)
                  TO UPPERVALUE (cmt$controller_port_number) DO
              IF controller_element_p^.controller.connection.port [channel_port].configured THEN
                cmp$get_element_state (controller_element_p^.controller.connection.port [channel_port].
                      element_name, controller_element_p^.controller.connection.port [channel_port].iou,
                      channel_state, local_status);
                IF (channel_state = cmc$on) AND (controller_state = cmc$on) THEN
                  RETURN; {----->
                IFEND;
              IFEND;
            FOREND;
          IFEND;
        IFEND;
      IFEND;
    FOREND;

  PROCEND cmp$find_primary_controller;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$free_element_def_table', EJECT ??

  PROCEDURE [XDCL, #GATE] cmp$free_element_def_table;

    VAR
      table_index: integer;

    IF cmv$peripheral_element_table.pointer = NIL THEN
      RETURN; {----->
    IFEND;

    FOR table_index := LOWERBOUND (cmv$peripheral_element_table.pointer^)
          TO UPPERBOUND (cmv$peripheral_element_table.pointer^) DO
      CASE cmv$peripheral_element_table.pointer^ [table_index].physical_descriptor.element_type OF
      = cmc$controller_element, cmc$external_processor_element, cmc$channel_adapter_element,
            cmc$communications_element =
        IF cmv$peripheral_element_table.pointer^ [table_index].physical_descriptor.equipment_path <> NIL THEN
          FREE cmv$peripheral_element_table.pointer^ [table_index].physical_descriptor.equipment_path IN
                osv$mainframe_wired_cb_heap^;
        IFEND;
      = cmc$storage_device_element =
        IF cmv$peripheral_element_table.pointer^ [table_index].physical_descriptor.unit_path <> NIL THEN
          FREE cmv$peripheral_element_table.pointer^ [table_index].physical_descriptor.unit_path IN
                osv$mainframe_wired_cb_heap^;
        IFEND;
      ELSE
      CASEND;
    FOREND;
    FREE cmv$peripheral_element_table.pointer IN osv$mainframe_wired_cb_heap^;

  PROCEND cmp$free_element_def_table;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$get_logical_pp_index', EJECT ??

{ PURPOSE:
{   This procedure retrieves an index into the Logical PP Table using a channel element definition.

  PROCEDURE [XDCL, #GATE] cmp$get_logical_pp_index
    (    channel_element: cmt$element_definition;
     VAR logical_pp_index: iot$pp_number;
     VAR status: ost$status);

    VAR
      channel: dst$iou_resource,
      iou_number: dst$iou_number,
      pp_index: iot$pp_number;

    status.normal := TRUE;
    logical_pp_index := 0;

    cmp$convert_iou_name (channel_element.data_channel.iou, channel.iou_number, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;
    IF channel_element.data_channel.concurrent THEN
      channel.channel_protocol := dsc$cpt_cio;
    ELSE
      channel.channel_protocol := dsc$cpt_nio;
    IFEND;
    channel.number := channel_element.data_channel.number;

    FOR pp_index := LOWERBOUND (cmv$logical_pp_table_p^) TO UPPERBOUND (cmv$logical_pp_table_p^) DO
      IF cmv$logical_pp_table_p^ [pp_index].flags.configured AND
            (cmv$logical_pp_table_p^ [pp_index].pp_info.channel = channel) AND
            (cmv$logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_p^.
            unit_descriptors [cmv$logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_p^.
            first_logical_unit].unit_interface_table <> NIL) THEN
        logical_pp_index := pp_index;
        RETURN; {----->
      IFEND;
    FOREND;

    osp$set_status_abnormal (cmc$configuration_management_id, cme$pc_not_logically_conf, '', status);

  PROCEND cmp$get_logical_pp_index;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$get_logical_pp_number', EJECT ??

{ PURPOSE:
{   This procedure retrieves the logical pp number associated with a given element name.

  PROCEDURE [XDCL, #GATE] cmp$get_logical_pp_number
    (    element_name: cmt$element_name;
     VAR logical_pp_number: iot$pp_number;
     VAR status: ost$status);

    VAR
      logical_unit: iot$logical_unit,
      pp_index: iot$pp_number,
      ppit_p: ^iot$pp_interface_table,
      unit_desc: iot$unit_descriptor_entry;

    status.normal := TRUE;

    cmp$get_logical_unit_number (element_name, logical_unit, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

  /table_loop/
    FOR pp_index := LOWERBOUND (cmv$logical_pp_table_p^) TO UPPERBOUND (cmv$logical_pp_table_p^) DO
      IF NOT cmv$logical_pp_table_p^ [pp_index].flags.configured THEN
        CYCLE /table_loop/; {----->
      IFEND;

      ppit_p := cmv$logical_pp_table_p^ [pp_index].pp_info.pp_interface_table_p;

      IF (logical_unit < LOWERBOUND (ppit_p^.unit_descriptors)) OR
            (logical_unit > UPPERBOUND (ppit_p^.unit_descriptors)) THEN
        CYCLE /table_loop/; {----->
      IFEND;

      unit_desc := ppit_p^.unit_descriptors [logical_unit];

      IF (unit_desc.unit_interface_table = NIL) OR (unit_desc.unit_interface_table^.unit_status.disabled) THEN
        CYCLE /table_loop/; {----->
      IFEND;

      IF unit_desc.logical_unit = logical_unit THEN
        logical_pp_number := pp_index;
        RETURN; {----->
      IFEND;
    FOREND /table_loop/;

    osp$set_status_abnormal (cmc$configuration_management_id, cme$pc_not_logically_conf, '', status);

  PROCEND cmp$get_logical_pp_number;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$get_logical_unit_state', EJECT ??

{ PURPOSE:
{   This routine returns the state information of a logical unit by looking at the element capabilities list
{   in the logical unit table.

  PROCEDURE [XDCL, #GATE] cmp$get_logical_unit_state
    (    lun: iot$logical_unit;
         logical_unit_table_p: ^cmt$logical_unit_table;
     VAR state: cmt$element_state);

    IF logical_unit_table_p^ [lun].element_capability >= $cmt$element_capabilities
          [cmc$io_request_submission] THEN
      state := cmc$on;
    ELSEIF logical_unit_table_p^ [lun].element_capability =
          $cmt$element_capabilities [cmc$dedicated_maintenance, cmc$concurrent_maintenance] THEN
      state := cmc$down;
    ELSEIF logical_unit_table_p^ [lun].element_capability = $cmt$element_capabilities [] THEN
      state := cmc$off;
    ELSE
      state := cmc$off;
    IFEND;

  PROCEND cmp$get_logical_unit_state;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$get_max_number_of_pp', EJECT ??

{ PURPOSE:
{   This procedure computes the maximum number of logical pp present.

  PROCEDURE [XDCL, #GATE] cmp$get_max_number_of_pp
    (VAR max_number_of_pp: iot$pp_number);

    VAR
      pp_index: iot$pp_number;

    max_number_of_pp := 0;
    IF cmv$logical_pp_table_p <> NIL THEN
      FOR pp_index := UPPERBOUND (cmv$logical_pp_table_p^) DOWNTO LOWERBOUND (cmv$logical_pp_table_p^) DO
        IF cmv$logical_pp_table_p^ [pp_index].flags.configured THEN
          max_number_of_pp := pp_index;
          RETURN; {----->
        IFEND;
      FOREND;
    IFEND;

  PROCEND cmp$get_max_number_of_pp;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$lock_lun_entry', EJECT ??

  PROCEDURE [XDCL, #GATE] cmp$lock_lun_entry
    (    logical_unit_number: iot$logical_unit;
     VAR lock_obtained: boolean);

    VAR
      delay_and_try_again: boolean,
      status: ost$status;

{Debug code begin

    VAR
      xcb_p: ^ost$execution_control_block;

{Debug code end


    delay_and_try_again := TRUE;
    lock_obtained := FALSE;

    WHILE TRUE DO
      osp$set_signature_lock (cmv$logical_unit_lock, osc$wait, status);
      IF NOT status.normal THEN
{Debug code begin
        cmv$set_lun_sig_lock_set_count := cmv$set_lun_sig_lock_set_count + 1; {this might go wrong
        #SPOIL (cmv$set_lun_sig_lock_set_count);
{Debug code end
        RETURN; {----->
      IFEND;
      IF NOT cmv$logical_unit_table^ [logical_unit_number].entry_interlock THEN
        cmv$logical_unit_table^ [logical_unit_number].entry_interlock := TRUE;
        lock_obtained := TRUE;
      IFEND;

{Debug code begin
      cmv$lun_entry_lock_trace_list [cmv$lun_entry_lock_trace_index].sjn := jmv$jcb.system_name;
      xcb_p := #ADDRESS (1, osc$segnum_job_fixed_heap, #READ_REGISTER (osc$pr_base_constant));
      cmv$lun_entry_lock_trace_list [cmv$lun_entry_lock_trace_index].gtid := xcb_p^.global_task_id;
      cmv$lun_entry_lock_trace_list [cmv$lun_entry_lock_trace_index].lun := logical_unit_number;
      cmv$lun_entry_lock_trace_list [cmv$lun_entry_lock_trace_index].clock := #FREE_RUNNING_CLOCK (0);
      cmv$lun_entry_lock_trace_list [cmv$lun_entry_lock_trace_index].set_lock := TRUE;
      cmv$lun_entry_lock_trace_list [cmv$lun_entry_lock_trace_index].lock_obtained := lock_obtained;

      cmv$lun_entry_lock_trace_index := (cmv$lun_entry_lock_trace_index + 1) MOD 256;
{Debug code end

      osp$clear_signature_lock (cmv$logical_unit_lock, status);
      IF lock_obtained THEN
        RETURN; {----->
      IFEND;

      IF delay_and_try_again THEN
        pmp$delay (3000, status);
        delay_and_try_again := FALSE;
      ELSE
        RETURN; {----->
      IFEND;
    WHILEND;

  PROCEND cmp$lock_lun_entry;
?? OLDTITLE ??
?? NEWTITLE := '  cmp$lcu_lock_set_by_job', EJECT ??

{ PURPOSE:
{   This procedure determines whether the LCU locks are already set.

  PROCEDURE [XDCL, #GATE] cmp$lcu_lock_set_by_job
    (VAR job_name: jmt$system_supplied_name;
     VAR lock_set: boolean);

    VAR
      config_admin_lock_status: ost$signature_lock_status,
      removable_media_lock_status: ost$signature_lock_status;

    job_name := ' ';
    lock_set := FALSE;
    osp$test_signature_lock (cmv$configuration_administrator.lock, config_admin_lock_status);
    IF (config_admin_lock_status <> osc$sls_not_locked) THEN
      lock_set := TRUE;
      job_name := cmv$configuration_administrator.job_name
    ELSE
      osp$test_signature_lock (cmv$removable_media_operation.lock, removable_media_lock_status);
      IF (removable_media_lock_status <> osc$sls_not_locked) THEN
        lock_set := TRUE;
        job_name := cmv$removable_media_operation.job_name
      IFEND;
    IFEND;

  PROCEND cmp$lcu_lock_set_by_job;
?? OLDTITLE ??
?? NEWTITLE := '  cmp$lock_set_by_task', EJECT ??

{ PURPOSE:
{   This function determines whether the given lock type is already
{   set by the current task.

  FUNCTION [XDCL, #GATE, UNSAFE] cmp$lock_set_by_task
    (    lock_type: cmt$lcu_lock_type): boolean;

    VAR
      lock_status: ost$signature_lock_status;

    CASE lock_type OF
    = cmc$configuration_administrator =
      osp$test_signature_lock (cmv$configuration_administrator.lock, lock_status);
    = cmc$removable_media_operation =
      osp$test_signature_lock (cmv$removable_media_operation.lock, lock_status);
    ELSE
    CASEND;
    cmp$lock_set_by_task := lock_status = osc$sls_locked_by_current_task;

  FUNCEND cmp$lock_set_by_task;
?? OLDTITLE ??
?? NEWTITLE := 'CMP$MANAGE_LCU_LOCK', EJECT ??

{ PURPOSE:
{   This procedure sets and clears locks in the LCU.

  PROCEDURE [XDCL, #GATE] cmp$manage_lcu_lock
    (    lock_type: cmt$lcu_lock_type;
         lock_operation: cmt$lcu_lock_operation;
     VAR lock_processed_by_this_request: boolean;
     VAR status: ost$status);

    CONST
      c$lock_not_set = 'LCU Lock not set by current task',
      c$lock_set_by_job = 'LCU Lock set by job: ',
      c$unable_to_unlock = 'Unable to unlock LCU Lock';

    VAR
      lock_p: ^cmt$lcu_lock,
      lock_status: ost$signature_lock_status,
      system_supplied_name: jmt$system_supplied_name,
      user_supplied_name: jmt$user_supplied_name;

?? NEWTITLE := 'P$SET_SIGNATURE_LOCK', EJECT ??

    PROCEDURE p$set_signature_lock
      (VAR lock_set_by_this_request: boolean;
       VAR lock: ost$signature_lock;
       VAR status_normal: boolean);

      VAR
        actual_value: integer,
        cs_status: 0 .. 2,
        new_value: integer,
        task_id: ost$global_task_id,
        xcb_p: ^ost$execution_control_block;

      xcb_p := #ADDRESS (1, osc$segnum_job_fixed_heap, #READ_REGISTER (osc$pr_base_constant));
      task_id := xcb_p^.global_task_id;
      new_value := task_id.index * 256 * #SIZE (task_id.seqno) + task_id.seqno;

      REPEAT
        #COMPARE_SWAP (lock.lock_id, 0, new_value, actual_value, cs_status);
      UNTIL cs_status <> osc$cs_variable_locked;

      lock_set_by_this_request := cs_status = osc$cs_successful;
      status_normal := lock_set_by_this_request OR (new_value = actual_value);

    PROCEND p$set_signature_lock;
?? OLDTITLE ??
?? NEWTITLE := 'P$CLEAR_SIGNATURE_LOCK', EJECT ??

    PROCEDURE p$clear_signature_lock
      (VAR lock_cleared_by_this_request: boolean;
       VAR lock: ost$signature_lock;
       VAR status_normal: boolean);

      VAR
        actual_value: integer,
        cs_status: 0 .. 2,
        initial_value: integer,
        task_id: ost$global_task_id,
        xcb_p: ^ost$execution_control_block;

      xcb_p := #ADDRESS (1, osc$segnum_job_fixed_heap, #READ_REGISTER (osc$pr_base_constant));
      task_id := xcb_p^.global_task_id;
      initial_value := task_id.index * 256 * #SIZE (task_id.seqno) + task_id.seqno;

      REPEAT
        #COMPARE_SWAP (lock.lock_id, initial_value, 0, actual_value, cs_status);
      UNTIL cs_status <> osc$cs_variable_locked;

      lock_cleared_by_this_request := cs_status = osc$cs_successful;
      status_normal := lock_cleared_by_this_request OR (actual_value = 0);

    PROCEND p$clear_signature_lock;
?? OLDTITLE ??
?? EJECT ??

    status.normal := TRUE;
    lock_processed_by_this_request := FALSE;
    IF lock_type = cmc$configuration_administrator THEN
      lock_p := ^cmv$configuration_administrator;
    ELSE
      lock_p := ^cmv$removable_media_operation;
    IFEND;

    CASE lock_operation OF
    = cmc$llo_clear_lock =
      p$clear_signature_lock (lock_processed_by_this_request, lock_p^.lock, status.normal);
      IF lock_processed_by_this_request THEN
        lock_p^.job_name := '';

      ELSE

{The lock was set by another task or not at all. In both cases, we return abnormal status to the caller.
{So we can detect the problem (better reprot it than stop the machine)

        IF status.normal THEN
          osp$set_status_abnormal (cmc$configuration_management_id, cme$lcu_program_error, c$unable_to_unlock,
                status);
          osp$append_status_parameter (osc$status_parameter_delimiter, c$lock_not_set, status);

        ELSE
          osp$set_status_abnormal (cmc$configuration_management_id, cme$lcu_program_error, c$unable_to_unlock,
                status);
          osp$append_status_parameter (osc$status_parameter_delimiter, c$lock_set_by_job, status);
          osp$append_status_parameter (' ', lock_p^.job_name, status);
        IFEND;
      IFEND;

    = cmc$llo_set_lock =
      p$set_signature_lock (lock_processed_by_this_request, lock_p^.lock, status.normal);
      IF lock_processed_by_this_request THEN
        pmp$get_job_names (user_supplied_name, system_supplied_name, status); {status is always normal!
        lock_p^.job_name := system_supplied_name;

      ELSEIF NOT status.normal THEN

{Normal status is returned, when lock was already et by current task!
{lock already set by current task := (status.normal and not lock_processed_by_this_request)

        osp$set_status_abnormal (cmc$configuration_management_id, cme$multiple_reconf_tasks, lock_p^.job_name,
              status);
      IFEND;
    ELSE
      ;
    CASEND;

  PROCEND cmp$manage_lcu_lock;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$retrieve_logical_pp_index', EJECT ??

{ PURPOSE:
{   This procedure retrieves an index into the Logical PP Table using a channel.

  PROCEDURE [XDCL, #GATE] cmp$retrieve_logical_pp_index
    (    channel: cmt$physical_channel;
         iou_number: dst$iou_number;
         logical_pp_table_p: ^cmt$logical_pp_table;
     VAR logical_pp_index: iot$pp_number;
     VAR status: ost$status);

    VAR
      channel_data: dst$iou_resource,
      channel_element_p: ^cmt$element_definition,
      iou_name: cmt$element_name,
      pp_index: iot$pp_number,
      table_index: integer;

    status.normal := TRUE;

    cmp$convert_iou_number (iou_number, iou_name, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;
    channel_data.iou_number := iou_number;
    IF channel.concurrent THEN
      channel_data.channel_protocol := dsc$cpt_cio;
    ELSE
      channel_data.channel_protocol := dsc$cpt_nio;
    IFEND;
    channel_data.number := channel.number;

    FOR table_index := LOWERBOUND (cmv$physical_configuration^) TO UPPERBOUND (cmv$physical_configuration^) DO
      IF cmv$physical_configuration^ [table_index].element_type = cmc$data_channel_element THEN
        IF (channel.number = cmv$physical_configuration^ [table_index].data_channel.number) AND
              (channel.concurrent = cmv$physical_configuration^ [table_index].data_channel.concurrent) AND
              (channel.port = cmv$physical_configuration^ [table_index].data_channel.port) AND
              (iou_name = cmv$physical_configuration^ [table_index].data_channel.iou) THEN
          FOR pp_index := LOWERBOUND (logical_pp_table_p^) TO UPPERBOUND (logical_pp_table_p^) DO
            IF logical_pp_table_p^ [pp_index].flags.configured AND
                  (logical_pp_table_p^ [pp_index].pp_info.channel = channel_data) THEN
              logical_pp_index := pp_index;
              RETURN; {----->
            IFEND;
          FOREND;
        IFEND;
      IFEND;
    FOREND;
    osp$set_status_abnormal (cmc$configuration_management_id, cme$pc_not_logically_conf, '', status);

  PROCEND cmp$retrieve_logical_pp_index;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$search_peripheral_table', EJECT ??

{ PURPOSE:
{   Search the peripheral element table for the given element.  The index to the table is returned.

  PROCEDURE [XDCL, #GATE] cmp$search_peripheral_table
    (    element_descriptor: cmt$element_descriptor;
         element_reservation: cmt$element_reservation;
         not_in_configuration: boolean;
     VAR table_index: integer;
     VAR status: ost$status);

    VAR
      channel: cmt$physical_channel,
      channel_address: cmt$physical_equipment_number,
      channel_definition: cmt$data_channel_definition,
      found: boolean,
      index: integer,
      iou_number: dst$iou_number,
      pete_p: ^cmt$peripheral_element_entry,
      physical_id: cmt$physical_identification,
      temp_element_descriptor: cmt$element_descriptor,
      unit_address: cmt$physical_unit_number;

    status.normal := TRUE;
    found := FALSE;

    IF cmv$peripheral_element_table.pointer = NIL THEN
      osp$set_status_condition (cme$cm_table_empty, status);
      RETURN; {----->
    IFEND;

    IF not_in_configuration THEN

      { Determine physical address from element reservation. Use only physical address to search for a match
      { to eliminate confusion between channel ports.

      cmp$crack_physical_address (element_reservation, iou_number, channel, channel_address, unit_address,
            status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;

    /peripheral_table_loop_1/
      FOR index := LOWERBOUND (cmv$peripheral_element_table.pointer^)
            TO UPPERBOUND (cmv$peripheral_element_table.pointer^) DO
        IF cmv$peripheral_element_table.pointer^ [index].physical_descriptor.configured THEN
          CYCLE /peripheral_table_loop_1/; {----->
        IFEND;

        CASE element_reservation.element_type OF
        = cmc$data_channel_element =
          IF cmv$peripheral_element_table.pointer^ [index].physical_descriptor.hardware_address.
                address_specifier = $cmt$physical_address_specifier [cmc$iou, cmc$channel] THEN
            found := (cmv$peripheral_element_table.pointer^ [index].physical_descriptor.hardware_address.iou =
                  iou_number) AND (cmv$peripheral_element_table.pointer^ [index].physical_descriptor.
                  hardware_address.channel.concurrent = channel.concurrent) AND
                  (cmv$peripheral_element_table.pointer^ [index].physical_descriptor.hardware_address.channel.
                  number = channel.number);
          IFEND;
        = cmc$controller_element, cmc$storage_device_element, cmc$communications_element,
              cmc$channel_adapter_element, cmc$external_processor_element =
          IF element_reservation.peripheral_descriptor.use_logical_identification THEN
            found := element_reservation.peripheral_descriptor.element_name =
                  cmv$peripheral_element_table.pointer^ [index].element_name;
          ELSE
            IF cmv$peripheral_element_table.pointer^ [index].physical_descriptor.hardware_address.
                  address_specifier = element_reservation.peripheral_descriptor.hardware_address.
                  physical_address_specifier THEN
              CASE element_reservation.element_type OF
              = cmc$controller_element, cmc$channel_adapter_element, cmc$external_processor_element,
                    cmc$communications_element =
                found := (cmv$peripheral_element_table.pointer^ [index].physical_descriptor.hardware_address.
                      channel.number = channel.number) AND (cmv$peripheral_element_table.pointer^ [index].
                      physical_descriptor.hardware_address.channel.concurrent = channel.concurrent) AND
                      (cmv$peripheral_element_table.pointer^ [index].physical_descriptor.hardware_address.
                      iou = iou_number) AND (cmv$peripheral_element_table.pointer^ [index].
                      physical_descriptor.hardware_address.channel_address = channel_address);
              = cmc$storage_device_element =
                found := (cmv$peripheral_element_table.pointer^ [index].physical_descriptor.hardware_address.
                      channel.number = channel.number) AND (cmv$peripheral_element_table.pointer^ [index].
                      physical_descriptor.hardware_address.channel.concurrent = channel.concurrent) AND
                      (cmv$peripheral_element_table.pointer^ [index].physical_descriptor.hardware_address.
                      iou = iou_number) AND (cmv$peripheral_element_table.pointer^ [index].
                      physical_descriptor.hardware_address.channel_address = channel_address) AND
                      (cmv$peripheral_element_table.pointer^ [index].physical_descriptor.hardware_address.
                      unit_address = unit_address);
              ELSE
                osp$set_status_abnormal (cmc$configuration_management_id, cme$cm_end_case_error,
                      'CMP$SEARCH_PERIPHERAL_TABLE', status);
                RETURN; {----->
              CASEND;
            IFEND;
          IFEND;
        ELSE
          osp$set_status_abnormal (cmc$configuration_management_id, cme$cm_end_case_error,
                'CMP$SEARCH_PERIPHERAL_TABLE', status);
          RETURN; {----->
        CASEND;
        IF found THEN
          table_index := index;
          EXIT /peripheral_table_loop_1/; {----->
        IFEND;
      FOREND /peripheral_table_loop_1/;

      IF NOT found THEN

      /peripheral_table_loop_2/
        FOR index := LOWERBOUND (cmv$peripheral_element_table.pointer^)
              TO UPPERBOUND (cmv$peripheral_element_table.pointer^) DO
          IF NOT cmv$peripheral_element_table.pointer^ [index].physical_descriptor.configured AND
                NOT cmv$peripheral_element_table.pointer^ [index].entry_interlock THEN
            table_index := index;
            EXIT /peripheral_table_loop_2/; {----->
          IFEND;
        FOREND /peripheral_table_loop_2/;

        temp_element_descriptor.element_type := element_reservation.element_type;
        CASE temp_element_descriptor.element_type OF
        = cmc$data_channel_element =
          temp_element_descriptor.channel_descriptor := element_reservation.channel_descriptor;
        = cmc$controller_element, cmc$external_processor_element, cmc$channel_adapter_element,
              cmc$storage_device_element, cmc$communications_element =
          temp_element_descriptor.peripheral_descriptor := element_reservation.peripheral_descriptor;
        ELSE
        CASEND;
        cmp$format_error_message (temp_element_descriptor, {not used} physical_id, FALSE,
              cme$cm_element_not_found, status);
      IFEND;

    ELSE { in configuration }
      CASE element_descriptor.element_type OF
      = cmc$data_channel_element =
        cmp$get_channel_def (element_descriptor.channel_descriptor, channel_definition, status);
        IF NOT status.normal THEN
          IF (status.condition = cme$lcm_element_not_found) OR
                (status.condition = cme$unknown_channel_type) THEN
            status.normal := TRUE;
          ELSE
            RETURN; {----->
          IFEND;
        IFEND;
        cmp$convert_iou_name (element_descriptor.channel_descriptor.iou, iou_number, status);
        IF NOT status.normal THEN
          RETURN; {----->
        IFEND;
      ELSE
      CASEND;

    /peripheral_table_loop_3/
      FOR index := LOWERBOUND (cmv$peripheral_element_table.pointer^)
            TO UPPERBOUND (cmv$peripheral_element_table.pointer^) DO
        pete_p := ^cmv$peripheral_element_table.pointer^ [index];
        IF pete_p^.physical_descriptor.element_type <> element_descriptor.element_type THEN
          CYCLE /peripheral_table_loop_3/; {----->
        IFEND;

        CASE element_descriptor.element_type OF
        = cmc$data_channel_element =
          found := (iou_number = pete_p^.physical_descriptor.channel_path.iou) AND
                (channel_definition.number = pete_p^.physical_descriptor.channel_path.channel.number) AND
                (channel_definition.port = pete_p^.physical_descriptor.channel_path.channel.port) AND
                (channel_definition.concurrent = pete_p^.physical_descriptor.channel_path.channel.concurrent);
        = cmc$controller_element, cmc$external_processor_element, cmc$communications_element,
              cmc$channel_adapter_element, cmc$storage_device_element =
          found := element_descriptor.peripheral_descriptor.element_name = pete_p^.element_name;
        ELSE
        CASEND;

        IF found THEN
          table_index := index;
          EXIT /peripheral_table_loop_3/; {----->
        IFEND;
      FOREND /peripheral_table_loop_3/;

      IF NOT found THEN
        CASE element_descriptor.element_type OF
        = cmc$data_channel_element =
          osp$set_status_abnormal (cmc$configuration_management_id, cme$cm_element_not_found,
                element_descriptor.channel_descriptor.name, status);
        = cmc$controller_element, cmc$storage_device_element, cmc$channel_adapter_element,
              cmc$communications_element =
          osp$set_status_abnormal (cmc$configuration_management_id, cme$cm_element_not_found,
                element_descriptor.peripheral_descriptor.element_name, status);
        ELSE
        CASEND;
      IFEND;
    IFEND;

  PROCEND cmp$search_peripheral_table;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$set_element_lock', EJECT ??

  PROCEDURE [XDCL, #GATE] cmp$set_element_lock
    (VAR status: ost$status);

    status.normal := TRUE;

    osp$set_signature_lock (cmv$element_reservation_lock, osc$wait, status);

  PROCEND cmp$set_element_lock;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$set_unit_shared', EJECT ??

{ PURPOSE:
{   This procedure sets the unit shared bit of the given logical unit.

  PROCEDURE [XDCL, #GATE] cmp$set_unit_shared
    (    logical_unit: iot$logical_unit;
         set_lock: boolean);

    VAR
      lock_obtained: boolean;

    IF (cmv$logical_unit_table = NIL) OR NOT cmv$logical_unit_table^ [logical_unit].configured THEN
      RETURN; {----->
    IFEND;

    IF set_lock THEN
      cmp$lock_lun_entry (logical_unit, lock_obtained);
      IF NOT lock_obtained THEN
        osp$system_error (' Unable to set CMV$LOGICAL_UNIT lock', NIL);
      IFEND;
    IFEND;

    cmv$logical_unit_table^ [logical_unit].unit_interface_table^.unit_shared := TRUE;
    IF set_lock THEN
      cmp$unlock_lun_entry (logical_unit, lock_obtained);
    IFEND;

  PROCEND cmp$set_unit_shared;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$unit_disabled', EJECT ??

  FUNCTION [XDCL, #GATE] cmp$unit_disabled
    (    logical_unit: iot$logical_unit): boolean;

    cmp$unit_disabled := (cmv$logical_unit_table^ [logical_unit].element_capability =
          $cmt$element_capabilities []) AND cmv$logical_unit_table^ [logical_unit].unit_interface_table^.
          unit_status.disabled;

  FUNCEND cmp$unit_disabled;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$unlock_lun_entry', EJECT ??

  PROCEDURE [XDCL, #GATE] cmp$unlock_lun_entry
    (    logical_unit_number: iot$logical_unit;
     VAR lock_released: boolean);

    VAR
      status: ost$status;

{Debug code begin

    VAR
      xcb_p: ^ost$execution_control_block;

{Debug code end

    lock_released := FALSE;

  /main_program/
    BEGIN

      osp$set_signature_lock (cmv$logical_unit_lock, osc$wait, status);
      IF NOT status.normal THEN
{Debug code begin
        cmv$clear_lun_sig_lock_set_cnt := cmv$clear_lun_sig_lock_set_cnt + 1; {this might go wrong
        #SPOIL (cmv$clear_lun_sig_lock_set_cnt);
{Debug code end
        EXIT /main_program/; {----->
      IFEND;

      IF cmv$logical_unit_table^ [logical_unit_number].entry_interlock THEN
        cmv$logical_unit_table^ [logical_unit_number].entry_interlock := FALSE;
        lock_released := TRUE;
      IFEND;

{Debug code begin
      cmv$lun_entry_lock_trace_list [cmv$lun_entry_lock_trace_index].sjn := jmv$jcb.system_name;
      xcb_p := #ADDRESS (1, osc$segnum_job_fixed_heap, #READ_REGISTER (osc$pr_base_constant));
      cmv$lun_entry_lock_trace_list [cmv$lun_entry_lock_trace_index].gtid := xcb_p^.global_task_id;
      cmv$lun_entry_lock_trace_list [cmv$lun_entry_lock_trace_index].lun := logical_unit_number;
      cmv$lun_entry_lock_trace_list [cmv$lun_entry_lock_trace_index].clock := #FREE_RUNNING_CLOCK (0);
      cmv$lun_entry_lock_trace_list [cmv$lun_entry_lock_trace_index].set_lock := FALSE;
      cmv$lun_entry_lock_trace_list [cmv$lun_entry_lock_trace_index].lock_released := lock_released;

      cmv$lun_entry_lock_trace_index := (cmv$lun_entry_lock_trace_index + 1) MOD 256;
{Debug code end

      osp$clear_signature_lock (cmv$logical_unit_lock, status);

    END /main_program/;

  PROCEND cmp$unlock_lun_entry;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$verify_active_path ', EJECT ??

{ PURPOSE:
{   This procedure returns a boolean indicating whether or not there exists an active path to the mass
{   storage device, i.e at least a channel or a control module is in the on state.

  PROCEDURE [XDCL, #GATE] cmp$verify_active_path
    (    element: cmt$element_definition;
     VAR active_path: boolean);

    VAR
      element_p: ^cmt$element_definition,
      index: cmt$data_storage_port_number,
      iou_name: cmt$element_name,
      local_status: ost$status,
      port: cmt$controller_port_number,
      state: cmt$element_state;

    IF element.element_type <> cmc$storage_device_element THEN
      active_path := TRUE;
      RETURN; {----->
    IFEND;
    active_path := FALSE;

  /verify_loop/
    FOR index := LOWERVALUE (cmt$data_storage_port_number) TO UPPERVALUE (cmt$data_storage_port_number) DO
      IF NOT element.storage_device.connection.port [index].configured THEN
        CYCLE /verify_loop/; {----->
      IFEND;

      IF element.storage_device.connection.port [index].upline_connection_type = cmc$data_channel_element THEN
        iou_name := element.storage_device.connection.port [index].iou;
      IFEND;
      cmp$get_element_state (element.storage_device.connection.port [index].element_name, iou_name, state,
            local_status);
      IF state <> cmc$on THEN
        CYCLE /verify_loop/; {----->
      IFEND;

      cmp$pc_get_element (element.storage_device.connection.port [index].element_name, iou_name, element_p,
            local_status);
      CASE element_p^.element_type OF
      = cmc$data_channel_element =
        active_path := TRUE;
        RETURN; {----->
      = cmc$controller_element =
        FOR port := LOWERVALUE (cmt$controller_port_number) TO UPPERVALUE (cmt$controller_port_number) DO
          IF element_p^.controller.connection.port [port].configured THEN
            cmp$get_element_state (element_p^.controller.connection.port [port].element_name,
                  element_p^.controller.connection.port [port].iou, state, local_status);
            IF state = cmc$on THEN
              active_path := TRUE;
              RETURN; {----->
            IFEND;
          IFEND;
        FOREND;
      ELSE
      CASEND;
    FOREND /verify_loop/;

  PROCEND cmp$verify_active_path;
?? OLDTITLE ??
?? NEWTITLE := 'msp$add_con_access_job', EJECT ??

  PROCEDURE [XDCL, #GATE] msp$add_con_access_job
    (    peripheral_index: integer;
         job_name: jmt$system_supplied_name;
     VAR status: ost$status);

    VAR
      lock_status: ost$status,
      node_p: mst$con_access_job_list;

    status.normal := TRUE;

    ALLOCATE node_p IN osv$mainframe_wired_cb_heap^;
    node_p^.job_name := job_name;
    node_p^.forward_link := NIL;

    osp$set_signature_lock (cmv$peripheral_element_table.lock, osc$wait, lock_status);
    IF NOT lock_status.normal THEN
      RETURN; {----->
    IFEND;

    IF cmv$peripheral_element_table.pointer^ [peripheral_index].maintenance_activity.con_access_job_list =
          NIL THEN
      cmv$peripheral_element_table.pointer^ [peripheral_index].maintenance_activity.con_access_job_list :=
            node_p;
    ELSE
      node_p^.forward_link := cmv$peripheral_element_table.pointer^ [peripheral_index].maintenance_activity.
            con_access_job_list;
      cmv$peripheral_element_table.pointer^ [peripheral_index].maintenance_activity.con_access_job_list :=
            node_p;
    IFEND;

    osp$clear_signature_lock (cmv$peripheral_element_table.lock, lock_status);

  PROCEND msp$add_con_access_job;
?? OLDTITLE ??
?? NEWTITLE := 'msp$delete_con_access_job', EJECT ??

  PROCEDURE [XDCL, #GATE] msp$delete_con_access_job
    (    peripheral_index: integer;
         job_name: jmt$system_supplied_name;
     VAR status: ost$status);

    VAR
      before_deleted_node_p: mst$con_access_job_list,
      deleted_node_p: mst$con_access_job_list,
      found: boolean,
      lock_status: ost$status;

    status.normal := TRUE;
    found := FALSE;

    osp$set_signature_lock (cmv$peripheral_element_table.lock, osc$wait, lock_status);
    IF NOT lock_status.normal THEN
      RETURN; {----->
    IFEND;

    deleted_node_p := cmv$peripheral_element_table.pointer^ [peripheral_index].maintenance_activity.
          con_access_job_list;
    before_deleted_node_p := NIL;

    WHILE (deleted_node_p <> NIL) AND (NOT found) DO
      IF deleted_node_p^.job_name = job_name THEN
        found := TRUE;
      ELSE
        before_deleted_node_p := deleted_node_p;
        deleted_node_p := deleted_node_p^.forward_link;
      IFEND;
    WHILEND;

    IF found THEN
      IF cmv$peripheral_element_table.pointer^ [peripheral_index].maintenance_activity.con_access_job_list^.
            forward_link = NIL THEN
        FREE cmv$peripheral_element_table.pointer^ [peripheral_index].maintenance_activity.
              con_access_job_list IN osv$mainframe_wired_cb_heap^;
      ELSEIF before_deleted_node_p = NIL THEN
        cmv$peripheral_element_table.pointer^ [peripheral_index].maintenance_activity.con_access_job_list :=
              cmv$peripheral_element_table.pointer^ [peripheral_index].maintenance_activity.
              con_access_job_list^.forward_link;
        FREE deleted_node_p IN osv$mainframe_wired_cb_heap^;
      ELSE
        before_deleted_node_p^.forward_link := deleted_node_p^.forward_link;
        FREE deleted_node_p IN osv$mainframe_wired_cb_heap^;
      IFEND;
    IFEND;

    osp$clear_signature_lock (cmv$peripheral_element_table.lock, lock_status);

  PROCEND msp$delete_con_access_job;
?? OLDTITLE ??
MODEND cmm$manage_interface_tables;
