?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE CM : Monitor Routines' ??
MODULE cmm$monitor_routines;

{ PURPOSE:
{   This module contains the interfaces executing in the monitor environment, performing various
{   configuration management tasks such as idle/resume of a pp, reload of the system device driver
{   and changing the state of an element.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc cme$physical_configuration_mgr
*copyc cmt$connection
*copyc cmt$enable_head_shift_message
*copyc cmt$iou_table
*copyc cmt$lcu_lock
*copyc cmt$logical_pp_table
*copyc cmt$logical_unit_table
*copyc cmt$peripheral_element_table
*copyc cmt$request_block
*copyc cmt$system_device_pp
*copyc ioe$st_errors
*copyc iot$channel_interlock_table
*copyc iot$cio_channel_interlock_table
*copyc iot$command
*copyc iot$disk_request
*copyc oss$mainframe_wired
*copyc ost$exchange_package
*copyc ost$monitor_stack
*copyc ost$spaa_entry
*copyc syt$smu_request_response_block
?? POP ??
*copyc cmp$find_redundant_path
*copyc cmp$locate_element_via_adr
*copyc cmp$locate_element_via_name
*copyc cmp$select_primary_controller
*copyc cmp$support_redundant_channel
*copyc cmp$verify_active_path_exists
*copyc dmp$volume_up
*copyc dpp$display_error
*copyc dsp$advance_ds_sequence_in_mtr
*copyc dsp$perform_cpu_pp_handshaking
*copyc i#real_memory_address
*copyc i#test_set_bit
*copyc iop$change_disk_channel
*copyc iop$change_disk_controller
*copyc iop$change_disk_unit
*copyc iop$check_idle_pps
*copyc iop$idle_path
*copyc iop$idle_resume
*copyc iop$process_io_completions
*copyc iop$queue_pp_request
*copyc mmp$assign_page_to_monitor
*copyc mmp$xtask_pva_to_sva
*copyc mtp$error_stop
*copyc mtp$set_status_abnormal
*copyc osp$set_locked_variable
?? EJECT ??
*copyc iov$initial_queue_lock
*copyc mtv$cst0
*copyc mtv$time_to_call_handshaking
*copyc osv$initial_monitor_xp
*copyc osv$page_size
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  VAR
    cmv$data_channel_address: [XDCL, #GATE] cmt$physical_address_specifier := [cmc$iou, cmc$channel],
    cmv$controller_address: [XDCL, #GATE] cmt$physical_address_specifier :=
          [cmc$iou, cmc$channel, cmc$channel_address],
    cmv$mass_storage_address: [XDCL, #GATE] cmt$physical_address_specifier :=
          [cmc$iou, cmc$channel, cmc$channel_address, cmc$unit_address],
    cmv$hydra_mass_storage_address: [XDCL, #GATE] cmt$physical_address_specifier :=
          [cmc$iou, cmc$channel, cmc$unit_address];

  VAR
    cmv$acquire_pp_for_redundant_ch: [XDCL, #GATE] boolean := TRUE,
    cmv$debug: [XDCL, #GATE] 0..255 := 0,
    cmv$debug_stop: [XDCL, #GATE] boolean := FALSE,
    cmv$display_config_debug_msg: [XDCL, #GATE] boolean := FALSE,
    cmv$default_response_handler: [XDCL, #GATE] iot$response_processor := ^cmp$default_response_handler,
    cmv$enable_auto_reconfiguration: [XDCL, #GATE] boolean := TRUE,
    cmv$enable_head_shift_message: [XDCL, #GATE] cmt$enable_head_shift_message :=
          cmc$cwl_new_head_shift_message,
    cmv$iou_table_p: [XDCL, #GATE] ^ARRAY [ * ] OF cmt$iou_table := NIL,
    cmv$logical_pp_table_p: [XDCL, #GATE] ^cmt$logical_pp_table := NIL,
    cmv$logical_unit_table: [XDCL, #GATE] ^cmt$logical_unit_table := NIL,
    cmv$max_number_of_pp: [XDCL, #GATE] iot$pp_number := 0,
    cmv$new_logical_pp_table_p: [XDCL, #GATE] ^cmt$logical_pp_table := NIL,
    cmv$new_logical_unit_table: [XDCL, #GATE] ^cmt$logical_unit_table := NIL,
    cmv$peripheral_element_table: [XDCL, #GATE] cmt$peripheral_element_table := [[0], NIL],
    cmv$system_device_pp: [XDCL, #GATE] cmt$system_device_pp := [FALSE, [0, dsc$cpt_nio, 31(8)], FALSE];

{The following definitions were moved from CMM$MANAGE_INTERFACE_TABLES to be able to be placed into
{OSS$MAINFRAME_WIRED.

  VAR
    cmv$configuration_administrator: [XDCL, #GATE, oss$mainframe_wired] cmt$lcu_lock := [[0],
          jmc$blank_system_supplied_name],
    cmv$element_reservation_lock: [XDCL, #GATE, oss$mainframe_wired] ost$signature_lock,
    cmv$logical_pp_table_lock: [XDCL, #GATE, oss$mainframe_wired] ost$signature_lock,
    cmv$logical_unit_lock: [XDCL, #GATE, oss$mainframe_wired] ost$signature_lock,
    cmv$removable_media_operation: [XDCL, #GATE, oss$mainframe_wired] cmt$lcu_lock := [[0],
          jmc$blank_system_supplied_name];

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

  PROCEDURE assign_stack_segment
    (    fba_p: ^cell;
     VAR rma: integer;
     VAR status: syt$monitor_status);

    VAR
      initxp_p: ^ost$exchange_package,
      stk_p: ^ost$monitor_stack;

    mmp$assign_page_to_monitor (fba_p, 1, FALSE, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    IF rma = 0 THEN
      i#real_memory_address (fba_p, rma);
      stk_p := fba_p;
      initxp_p := ^osv$initial_monitor_xp;
      stk_p^.xp := initxp_p^;
      stk_p^.xp.a1_current_stack_frame := ^stk_p^.stack;
      stk_p^.xp.a0_dynamic_space_pointer := ^stk_p^.csf;
      stk_p^.xp.tos_registers [1].pva.ring := #RING (^stk_p^.stack);
      stk_p^.xp.tos_registers [1].pva.seg := #SEGMENT (^stk_p^.stack);
      stk_p^.xp.tos_registers [1].pva.offset := #OFFSET (^stk_p^.stack);
    IFEND;

  PROCEND assign_stack_segment;
?? OLDTITLE ??
?? NEWTITLE := 'build_affected_pp_tables', EJECT ??

{ PURPOSE:
{   This procedure rebuilds the pp_interface table for the PP's that service the specified element.  If a
{   channel is specified only one PP is affected, however; if a controller is specified the PP for each
{   channel connected to the controller is affected.  The interface cmp$change_state is used to rebuild the
{   PP interface tables. The state of the element is not actually changed and is assumed to be ON.
{ NOTE:
{   This code is executed only during the UNSTEP_SYSTEM monitor command.

  PROCEDURE build_affected_pp_tables
    (    element_entry_p: ^cmt$peripheral_element_entry);

    VAR
      channel_address: cmt$physical_address,
      disabled_connection_found: boolean,
      driver_name: pmt$program_name,
      first_unit: iot$logical_unit,
      found: boolean,
      i: integer,
      lun_list_p: ^ARRAY [ * ] OF cmt$rb_logical_unit_address,
      monitor_status: syt$monitor_status,
      number_of_path: integer,
      pete_p: ^cmt$peripheral_element_entry,
      physical_address: cmt$physical_address,
      pp: iot$pp_number,
      pp_count: iot$pp_number,
      pp_table_rma_list: ARRAY [cmt$physical_equipment_number] OF ost$real_memory_address,
      redundant_channel_list: ARRAY [cmt$physical_equipment_number] OF cmt$physical_address,
      redundant_path_available: boolean,
      redundant_path_pp_list: ARRAY [cmt$physical_equipment_number] OF iot$pp_number,
      request_block: cmt$request_block,
      unit_count: integer,
      update_controller_address: boolean;

    dsp$perform_cpu_pp_handshaking;
    dpp$display_error(element_entry_p^.element_name);

    CASE element_entry_p^.physical_descriptor.element_type OF
    = cmc$data_channel_element =

      { Search Logical pp table to find pp servicing this channel.  Build monitor_request for this PP.
      { Call change_state.

      physical_address := element_entry_p^.physical_descriptor.channel_path;

    /search_pp_table/
      FOR pp := LOWERBOUND (cmv$logical_pp_table_p^) TO UPPERBOUND (cmv$logical_pp_table_p^) DO
        IF NOT cmv$logical_pp_table_p^ [pp].flags.configured THEN
          CYCLE /search_pp_table/;
        IFEND;

        IF cmv$logical_pp_table_p^ [pp].pp_info.channel.iou_number <> physical_address.iou THEN
          CYCLE /search_pp_table/;
        IFEND;

        first_unit := cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.first_logical_unit;
        unit_count := cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.number_of_units;

        IF cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.unit_descriptors [first_unit].
              physical_path.channel_number <> physical_address.channel.number THEN
          CYCLE /search_pp_table/;
        IFEND;

        IF cmv$logical_pp_table_p^ [pp].pp_info.channel_interlock_p^.
              channel_characteristics [physical_address.channel.number].concurrent_channel <>
              physical_address.channel.concurrent THEN
          CYCLE /search_pp_table/;
        IFEND;

        found := TRUE;
        EXIT /search_pp_table/;

      FOREND /search_pp_table/;

      IF NOT found THEN
        RETURN;
      IFEND;

      request_block.status.normal := TRUE;
      request_block.kind := cmc$rbk_change_state;
      request_block.iou := physical_address.iou;
      request_block.element_name := element_entry_p^.element_name;
      request_block.new_state := cmc$on;
      request_block.update_controller_address := TRUE;
      request_block.redundant_path_available :=
            cmp$support_redundant_channel (cmv$logical_pp_table_p^ [pp].controller_info.controller_type);
      request_block.update_controller_address := TRUE;
      request_block.element_type := cmc$data_channel_element;
      request_block.channel_pp := pp;
      request_block.channel := physical_address.channel.number;

      cmp$find_redundant_path (physical_address, cmc$on, redundant_path_available, update_controller_address,
            number_of_path, redundant_channel_list, redundant_path_pp_list, driver_name, pp_table_rma_list);
      IF redundant_path_available THEN
        PUSH request_block.redundant_path_pp_list_p: [0 .. number_of_path];
        FOR i := 0 to number_of_path DO
          request_block.redundant_path_pp_list_p^ [i] := redundant_path_pp_list[i];
        FOREND;
      ELSE
        PUSH request_block.redundant_path_pp_list_p: [0 .. 0];
        request_block.redundant_path_pp_list_p^ [0] := pp;
      IFEND;

      PUSH lun_list_p: [1 .. unit_count];
      setup_lun_list (pp, physical_address, {update_controller_address} FALSE, lun_list_p, unit_count);

      PUSH request_block.logical_unit_list_p: [1 .. unit_count];
      FOR i := 1 TO unit_count DO
        request_block.logical_unit_list_p^ [i] := lun_list_p^ [i];
      FOREND;

      change_state (request_block, monitor_status);

    = cmc$controller_element =

      { Search Logical pp table to find pp(s) for all channels connected to this controller.
      { Build monitor_request for these PPs.  Call change_state.

      physical_address := element_entry_p^.physical_descriptor.equipment_path^ [1];
      request_block.status.normal := TRUE;
      request_block.kind := cmc$rbk_change_state;
      request_block.iou := physical_address.iou;
      request_block.element_name := element_entry_p^.element_name;
      request_block.new_state := cmc$on;
      request_block.redundant_path_available := TRUE;
      request_block.update_controller_address := TRUE;
      request_block.element_type := cmc$controller_element;

      pp_count := UPPERBOUND (element_entry_p^.physical_descriptor.equipment_path^);
      PUSH request_block.redundant_path_pp_list_p: [0 .. (pp_count - 1)];

    /process_channel/
      FOR i := 1 TO pp_count DO
        physical_address := element_entry_p^.physical_descriptor.equipment_path^ [i];
        channel_address := physical_address;
        channel_address.address_specifier := cmv$data_channel_address;
        channel_address.channel_address := 0;

      /search_pp_table_2/
        FOR pp := LOWERBOUND (cmv$logical_pp_table_p^) TO UPPERBOUND (cmv$logical_pp_table_p^) DO
          IF NOT cmv$logical_pp_table_p^ [pp].flags.configured THEN
            CYCLE /search_pp_table_2/;
          IFEND;

          IF cmv$logical_pp_table_p^ [pp].pp_info.channel.iou_number <> physical_address.iou THEN
            CYCLE /search_pp_table_2/;
          IFEND;

          first_unit := cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.first_logical_unit;
          unit_count := cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.number_of_units;

          IF cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.unit_descriptors [first_unit].
                physical_path.channel_number <> physical_address.channel.number THEN
            CYCLE /search_pp_table_2/;
          IFEND;

          IF cmv$logical_pp_table_p^ [pp].pp_info.channel_interlock_p^.
                channel_characteristics [physical_address.channel.number].concurrent_channel <>
                physical_address.channel.concurrent THEN
            CYCLE /search_pp_table_2/;
          IFEND;

          found := TRUE;
          EXIT /search_pp_table_2/;

        FOREND /search_pp_table_2/;

        IF NOT found THEN
          RETURN;
        IFEND;

        IF i = 1 THEN
          request_block.controller_pp := pp;
          request_block.controller_channel := physical_address.channel.number;
          request_block.controller := physical_address.channel_address;
          request_block.redundant_path_pp_list_p^ [i - 1] := pp;
        ELSE
          request_block.redundant_path_pp_list_p^ [i - 1] := pp;
        IFEND;

      FOREND /process_channel/;

      PUSH lun_list_p: [1 .. unit_count];
      setup_lun_list (request_block.controller_pp, physical_address, {update_controller_address} FALSE,
            lun_list_p, unit_count);

      PUSH request_block.logical_unit_list_p: [1 .. unit_count];
      FOR i := 1 TO unit_count DO
        request_block.logical_unit_list_p^ [i] := lun_list_p^ [i];
      FOREND;

      change_state (request_block, monitor_status);
    ELSE
    CASEND;

  PROCEND build_affected_pp_tables;
?? OLDTITLE ??
?? NEWTITLE := 'change_state', EJECT ??

{ PURPOSE:
{   This procedure performs the state change of a mass storage channel, controller, or unit.

  PROCEDURE change_state
    (    request_block: cmt$request_block;
     VAR status: syt$monitor_status);

    VAR
      found: boolean,
      ignore_state: cmt$element_state,
      index_1: integer,
      index_2: integer,
      logical_pp: iot$pp_number,
      lppit_p: ^iot$pp_interface_table,
      lun: iot$logical_unit,
      lun2: iot$logical_unit,
      old_state: cmt$element_state,
      physical_channel: cmt$physical_channel,
      pp: iot$pp_number,
      ppit_p: ^iot$pp_interface_table,
      pp_list: ARRAY [0 .. 0] OF iot$pp_number,
      rma: integer;

    status.normal := TRUE;

    IF cmv$debug = 010(16) THEN
      mtp$error_stop (' Debug stop 010(16) change_state.');
    IFEND;

    update_peripheral_element_table (request_block, request_block.new_state, old_state);

    CASE request_block.element_type OF
    = cmc$data_channel_element =
      pp := request_block.channel_pp;
    = cmc$controller_element =
      pp := request_block.controller_pp;
    = cmc$storage_device_element =
      pp := request_block.unit_pp;
    ELSE
    CASEND;

    IF request_block.redundant_path_available THEN
      IF cmv$logical_pp_table_p^ [pp].flags.pp_loaded THEN
        idle_pp_in_mtr (pp, TRUE, status);
      IFEND;

      { Idle all redundant pp. Also if any PP is disabled because one of the controller on the channel is
      { being DOWN/OFF, it should be re-enabled to allow access to the units on the primary channel,
      { accessible possibly from different controllers.

      FOR index_1 := LOWERBOUND (request_block.redundant_path_pp_list_p^)
            TO UPPERBOUND (request_block.redundant_path_pp_list_p^) DO
        IF cmv$logical_pp_table_p^ [request_block.redundant_path_pp_list_p^ [index_1]].flags.disabled THEN
          cmv$logical_pp_table_p^ [request_block.redundant_path_pp_list_p^ [index_1]].flags.disabled := FALSE;
        IFEND;
        idle_pp_in_mtr (request_block.redundant_path_pp_list_p^ [index_1], TRUE, status);
      FOREND;

      pp_list [0] := pp;
      IF request_block.new_state = cmc$on THEN
        IF cmv$logical_pp_table_p^ [pp].flags.pp_loaded THEN
          change_unit_descriptors (request_block.update_controller_address,
                request_block.redundant_path_pp_list_p^, pp_list, request_block.logical_unit_list_p);
        IFEND;
      ELSE
        change_unit_descriptors (request_block.update_controller_address, pp_list,
              request_block.redundant_path_pp_list_p^, request_block.logical_unit_list_p);
      IFEND;

      { Ignore the status for the current channel. This pp could be disabled as a
      { result of a channel failure, therefore a RESUME request will return an error.

      IF cmv$logical_pp_table_p^ [pp].flags.pp_loaded THEN
        resume_pp_in_mtr (pp, TRUE, status);
      IFEND;

      FOR index_1 := LOWERBOUND (request_block.redundant_path_pp_list_p^)
            TO UPPERBOUND (request_block.redundant_path_pp_list_p^) DO
        IF cmv$logical_pp_table_p^ [request_block.redundant_path_pp_list_p^ [index_1]].flags.pp_loaded THEN
          resume_pp_in_mtr (request_block.redundant_path_pp_list_p^ [index_1], TRUE, status);
        IFEND;
      FOREND;

      RETURN;
    IFEND;

    IF cmv$debug = 011(16) THEN
      mtp$error_stop (' Debug stop 011(16) change_state.');
    IFEND;

    IF ((request_block.element_type = cmc$data_channel_element) OR
          (request_block.element_type = cmc$controller_element)) AND (request_block.new_state = cmc$on) THEN

      { Make sure all unit descriptors have valid RMA if channel is turned ON.

      ppit_p := cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p;
    /lun_loop/
      FOR lun := LOWERBOUND (ppit_p^.unit_descriptors) TO UPPERBOUND (ppit_p^.unit_descriptors) DO
        IF ppit_p^.unit_descriptors[lun].unit_interface_table = NIL THEN
          CYCLE /lun_loop/;
        IFEND;

        IF ppit_p^.unit_descriptors[lun].unit_interface_table_rma <> 0 THEN
          CYCLE /lun_loop/;
        IFEND;

        CASE request_block.element_type OF
        = cmc$data_channel_element =
          i#real_memory_address (#LOC (cmv$logical_unit_table^ [lun].unit_interface_table^), rma);
          ppit_p^.unit_descriptors [lun].unit_interface_table_rma := rma;
        = cmc$controller_element =
          IF ppit_p^.unit_descriptors [lun].physical_path.controller_number = request_block.controller THEN
            i#real_memory_address (#LOC (cmv$logical_unit_table^ [lun].unit_interface_table^), rma);
            ppit_p^.unit_descriptors [lun].unit_interface_table_rma := rma;
          IFEND;
        ELSE
        CASEND;

        { Now make sure Unit descriptors of any other redundant channel have a zero value
        { RMA for its Unit interface table, if there is no PP assigned.

      /search_lun/
        FOR logical_pp := LOWERBOUND (cmv$logical_pp_table_p^) TO UPPERBOUND (cmv$logical_pp_table_p^) DO
          IF logical_pp = pp THEN
            CYCLE /search_lun/;
          IFEND;

          IF NOT cmv$logical_pp_table_p^ [logical_pp].flags.configured THEN
            CYCLE /search_lun/;
          IFEND;

          IF cmv$logical_pp_table_p^ [logical_pp].flags.pp_loaded THEN
            CYCLE /search_lun/;
          IFEND;

          lppit_p := cmv$logical_pp_table_p^ [logical_pp].pp_info.pp_interface_table_p;
          IF lun < LOWERBOUND(lppit_p^.unit_descriptors) THEN
            CYCLE /search_lun/;
          IFEND;

          IF lun > UPPERBOUND(lppit_p^.unit_descriptors) THEN
            CYCLE /search_lun/;
          IFEND;

        /lun_loop_2/
          FOR lun2 := LOWERBOUND(lppit_p^.unit_descriptors) TO UPPERBOUND(lppit_p^.unit_descriptors) DO
            IF lppit_p^.unit_descriptors[lun2].unit_interface_table = NIL THEN
              CYCLE /lun_loop_2/;
            IFEND;

            IF lppit_p^.unit_descriptors[lun2].unit_interface_table_rma = 0 THEN
              CYCLE /lun_loop_2/;
            IFEND;

            lppit_p^.unit_descriptors[lun2].unit_interface_table_rma := 0;
            EXIT /lun_loop_2/;
          FOREND /lun_loop_2/;
        FOREND /search_lun/;

      FOREND /lun_loop/;
    IFEND;

    IF cmv$debug = 012(16) THEN
      mtp$error_stop (' Debug stop 012(16) change_state.');
    IFEND;

    CASE request_block.element_type OF
    = cmc$data_channel_element =
      iop$change_disk_channel (request_block.new_state, request_block.channel_pp, request_block.channel,
            status);
      IF NOT status.normal THEN
        update_peripheral_element_table (request_block, old_state, ignore_state);
        RETURN;
      IFEND;

    = cmc$controller_element =
      iop$change_disk_controller (request_block.new_state, request_block.controller_pp,
            request_block.controller_channel, request_block.controller, status);
      IF NOT status.normal THEN
        update_peripheral_element_table (request_block, old_state, ignore_state);
        RETURN;
      IFEND;

    = cmc$storage_device_element =
      iop$change_disk_unit (request_block.new_state, request_block.logical_unit, status);
      IF NOT status.normal THEN
        update_peripheral_element_table (request_block, old_state, ignore_state);
        RETURN;
      IFEND;
    ELSE
    CASEND;

  PROCEND change_state;
?? OLDTITLE ??
?? NEWTITLE := 'change_unit_descriptors', EJECT ??

{ PURPOSE:
{   This procedure updates the Unit Interface Table's RMA of unit descriptors to enable or disable access to
{   these units.

  PROCEDURE change_unit_descriptors
    (    update_controller_address: boolean;
         pp_to_clear_list: ARRAY [ * ] OF iot$pp_number;
         pp_to_set_list: ARRAY [ * ] OF iot$pp_number;
         logical_unit_list_p: ^ARRAY [ * ] OF cmt$rb_logical_unit_address);

    VAR
      found: boolean,
      ignore_status: syt$monitor_status,
      logical_unit: iot$logical_unit,
      lun_index: integer,
      mass_storage_device: boolean,
      pp: iot$pp_number,
      ppit_p: ^iot$pp_interface_table,
      pp_index: integer,
      primary_channel_p: ^cmt$peripheral_element_entry,
      primary_controller_p: ^cmt$peripheral_element_entry,
      redundant_path: boolean,
      rma: integer,
      ud: iot$logical_unit;

    mass_storage_device := cmv$logical_pp_table_p^ [pp_to_set_list [0]].controller_info.controller_type <>
          cmc$mt5698_xx;
    IF logical_unit_list_p = NIL THEN
      RETURN;
    IFEND;

    IF cmv$debug = 013(16) THEN
      mtp$error_stop (' Debug stop 013(16) change_unit_descriptors.');
    IFEND;

  /lun_loop_1/
    FOR lun_index := LOWERBOUND (logical_unit_list_p^) TO UPPERBOUND (logical_unit_list_p^) DO
      logical_unit := logical_unit_list_p^ [lun_index].logical_unit;

    /pp_loop_1/
      FOR pp_index := LOWERBOUND (pp_to_clear_list) TO UPPERBOUND (pp_to_clear_list) DO
        pp := pp_to_clear_list [pp_index];
        ppit_p := cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p;

        IF logical_unit < LOWERBOUND(ppit_p^.unit_descriptors) THEN
          CYCLE /pp_loop_1/;
        IFEND;

        IF logical_unit > UPPERBOUND(ppit_p^.unit_descriptors) THEN
          CYCLE /pp_loop_1/;
        IFEND;

        IF ppit_p^.unit_descriptors [logical_unit].unit_interface_table = NIL THEN
          CYCLE /pp_loop_1/;
        IFEND;

        IF ppit_p^.unit_descriptors [logical_unit].logical_unit <> logical_unit THEN
          CYCLE /pp_loop_1/;
        IFEND;

        cmp$select_primary_controller (pp, logical_unit, primary_controller_p, primary_channel_p,
              redundant_path);
        IF primary_controller_p <> NIL THEN
          ppit_p^.unit_descriptors [logical_unit].physical_path.controller_number :=
                primary_controller_p^.physical_descriptor.equipment_path^ [1].channel_address;
          IF redundant_path THEN
            ppit_p^.unit_descriptors [logical_unit].unit_interface_table_rma := 0;
          ELSE
            reenable_unit (logical_unit);
            i#real_memory_address (#LOC (cmv$logical_unit_table^ [logical_unit].unit_interface_table^), rma);
            ppit_p^.unit_descriptors [logical_unit].unit_interface_table_rma := rma;
          IFEND;
        ELSE
          ppit_p^.unit_descriptors [logical_unit].unit_interface_table_rma := 0;
          CYCLE /pp_loop_1/;
        IFEND;

        IF primary_channel_p <> NIL THEN
          IF primary_channel_p^.physical_descriptor.channel_path.channel.port = cmc$port_b THEN
            ppit_p^.unit_descriptors [logical_unit].physical_path.port := 1;
          ELSE
            ppit_p^.unit_descriptors [logical_unit].physical_path.port := 0;
          IFEND;
        IFEND;

        IF cmv$debug = 014(16) THEN
          mtp$error_stop (' Debug stop 014(16) cmp$change_unit_descriptors.');
        IFEND;

      FOREND /pp_loop_1/;
    FOREND /lun_loop_1/;

  /lun_loop_2/
    FOR lun_index := LOWERBOUND (logical_unit_list_p^) TO UPPERBOUND (logical_unit_list_p^) DO
      logical_unit := logical_unit_list_p^ [lun_index].logical_unit;
      found := FALSE;

    /pp_loop_2/
      FOR pp_index := LOWERBOUND (pp_to_set_list) TO UPPERBOUND (pp_to_set_list) DO
        pp := pp_to_set_list [pp_index];
        ppit_p := cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p;

        IF logical_unit < LOWERBOUND(ppit_p^.unit_descriptors) THEN
          CYCLE /pp_loop_2/;
        IFEND;

        IF logical_unit > UPPERBOUND(ppit_p^.unit_descriptors) THEN
          CYCLE /pp_loop_2/;
        IFEND;

        IF ppit_p^.unit_descriptors [logical_unit].unit_interface_table = NIL THEN
          CYCLE /pp_loop_2/;
        IFEND;

        IF ppit_p^.unit_descriptors [logical_unit].logical_unit <> logical_unit THEN
          CYCLE /pp_loop_2/;
        IFEND;

        reenable_unit (logical_unit);
        i#real_memory_address (#LOC (cmv$logical_unit_table^ [logical_unit].unit_interface_table^), rma);

        cmp$select_primary_controller (pp, logical_unit, primary_controller_p, primary_channel_p,
              redundant_path);
        IF primary_controller_p <> NIL THEN
          ppit_p^.unit_descriptors [logical_unit].physical_path.controller_number :=
                primary_controller_p^.physical_descriptor.equipment_path^ [1].channel_address;
          IF redundant_path THEN
            ppit_p^.unit_descriptors [logical_unit].unit_interface_table_rma := 0;
          ELSE
            ppit_p^.unit_descriptors [logical_unit].unit_interface_table_rma := rma;
          IFEND;
        ELSE
          ppit_p^.unit_descriptors [logical_unit].unit_interface_table_rma := 0;
          CYCLE /pp_loop_2/;
        IFEND;

        IF primary_channel_p <> NIL THEN
          IF primary_channel_p^.physical_descriptor.channel_path.channel.port = cmc$port_b THEN
            ppit_p^.unit_descriptors [logical_unit].physical_path.port := 1;
          ELSE
            ppit_p^.unit_descriptors [logical_unit].physical_path.port := 0;
          IFEND;
        IFEND;

        IF cmv$debug = 015(16) THEN
          mtp$error_stop (' Debug stop 015(16) cmp$change_unit_descriptors.');
        IFEND;

        found := TRUE;
      FOREND /pp_loop_2/;

      { A logical unit in the list was not found. This means that the unit has only one path
      { and that path was disabled. The unit must be disabled. Job mode routine CMP$CHANGE_STATE_R1
      { has already verified that this is a non critical device.

      IF (NOT found) AND mass_storage_device THEN
        iop$change_disk_unit (cmc$down, logical_unit, ignore_status);
      IFEND;
    FOREND /lun_loop_2/;

  PROCEND change_unit_descriptors;
?? OLDTITLE ??
?? NEWTITLE := 'enable_unit', EJECT ??

  PROCEDURE enable_unit
    (    unit_interface_table_p: ^iot$unit_interface_table);

    IF unit_interface_table_p <> NIL THEN
      unit_interface_table_p^.unit_lockword := iov$initial_queue_lock;
      unit_interface_table_p^.unit_q_lockword := iov$initial_queue_lock;
      unit_interface_table_p^.next_request := NIL;
      unit_interface_table_p^.next_request_rma := 0;
      unit_interface_table_p^.unit_status.disabled := FALSE;
    IFEND;

  PROCEND enable_unit;
?? OLDTITLE ??
?? NEWTITLE := 'get_channel_physical_address', EJECT ??

{ PURPOSE:
{   This procedure sets up the physical address specifier and physical address
{   of the channel element given the controller address or unit address of an element.

  PROCEDURE [INLINE] get_channel_physical_address
    (    controller_physical_address: cmt$physical_address;
     VAR channel_physical_address: cmt$physical_address);

    channel_physical_address.address_specifier := cmv$data_channel_address;
    channel_physical_address.iou := controller_physical_address.iou;
    channel_physical_address.channel := controller_physical_address.channel;
    channel_physical_address.channel_address := 0;
    channel_physical_address.unit_address := 0;

  PROCEND get_channel_physical_address;
?? OLDTITLE ??
?? NEWTITLE := 'get_controller_address', EJECT ??

{ PURPOSE:
{   This procedure returns the redundant controller number given the primary path address and the logical
{   unit.

  PROCEDURE get_controller_address
    (    primary_path: cmt$physical_address;
         logical_unit: iot$logical_unit;
     VAR controller_address: cmt$physical_equipment_number);

    VAR
      channel: cmt$physical_address,
      channel_element_p: ^cmt$peripheral_element_entry,
      controller: cmt$physical_address,
      controller_element_p: ^cmt$peripheral_element_entry,
      entry: cmt$peripheral_element_entry,
      path_index: integer,
      table_index: integer;

    controller.address_specifier := cmv$controller_address;
    channel.address_specifier := cmv$data_channel_address;

  /search_unit/
    FOR table_index := LOWERBOUND (cmv$peripheral_element_table.pointer^)
          TO UPPERBOUND (cmv$peripheral_element_table.pointer^) DO
      IF NOT cmv$peripheral_element_table.pointer^ [table_index].physical_descriptor.configured THEN
        CYCLE /search_unit/;
      IFEND;

      IF logical_unit = cmv$peripheral_element_table.pointer^ [table_index].logical_unit_number THEN
        entry := cmv$peripheral_element_table.pointer^ [table_index];
        EXIT /search_unit/;
      IFEND;
    FOREND /search_unit/;

    IF primary_path.address_specifier = cmv$data_channel_address THEN
      FOR path_index := LOWERBOUND (entry.physical_descriptor.unit_path^)
            TO UPPERBOUND (entry.physical_descriptor.unit_path^) DO
        IF NOT ((entry.physical_descriptor.unit_path^ [path_index].iou = primary_path.iou) AND
              (entry.physical_descriptor.unit_path^ [path_index].channel = primary_path.channel)) THEN
          controller.iou := entry.physical_descriptor.unit_path^ [path_index].iou;
          controller.channel := entry.physical_descriptor.unit_path^ [path_index].channel;
          controller.channel_address := entry.physical_descriptor.unit_path^ [path_index].channel_address;

          cmp$locate_element_via_adr (controller, controller_element_p);
          IF (controller_element_p <> NIL) AND (controller_element_p^.element_status.state = cmc$on) THEN
            channel.iou := entry.physical_descriptor.unit_path^ [path_index].iou;
            channel.channel := entry.physical_descriptor.unit_path^ [path_index].channel;

            cmp$locate_element_via_adr (channel, channel_element_p);
            IF (channel_element_p <> NIL) AND (channel_element_p^.element_status.state = cmc$on) THEN
              controller_address := controller.channel_address;
              RETURN;
            IFEND;
          IFEND;
        IFEND;
      FOREND;
    ELSE

      { For controller and unit, look for different controller address connected to the unit and check both
      { channel/controller state.

      FOR path_index := LOWERBOUND (entry.physical_descriptor.unit_path^)
            TO UPPERBOUND (entry.physical_descriptor.unit_path^) DO
        IF NOT (entry.physical_descriptor.unit_path^ [path_index].channel_address =
              primary_path.channel_address) THEN
          controller.iou := entry.physical_descriptor.unit_path^ [path_index].iou;
          controller.channel := entry.physical_descriptor.unit_path^ [path_index].channel;
          controller.channel_address := entry.physical_descriptor.unit_path^ [path_index].channel_address;
          cmp$locate_element_via_adr (controller, controller_element_p);
          IF (controller_element_p <> NIL) AND (controller_element_p^.element_status.state = cmc$on) THEN
            channel.iou := entry.physical_descriptor.unit_path^ [path_index].iou;
            channel.channel := entry.physical_descriptor.unit_path^ [path_index].channel;
            cmp$locate_element_via_adr (channel, channel_element_p);
            IF (channel_element_p <> NIL) AND (channel_element_p^.element_status.state = cmc$on) THEN
              controller_address := controller.channel_address;
              RETURN;
            IFEND;
          IFEND;
        IFEND;
      FOREND;
    IFEND;

  PROCEND get_controller_address;
?? OLDTITLE ??
?? NEWTITLE := 'get_controller_physical_address', EJECT ??

{ PURPOSE:
{   This procedure sets up the physical address specifier and physical address
{   path of the controller element given the full path of the unit.

  PROCEDURE [INLINE] get_controller_physical_address
    (    unit_physical_address: cmt$physical_address;
     VAR controller_physical_address: cmt$physical_address);

    controller_physical_address.address_specifier := cmv$controller_address;
    controller_physical_address.iou := unit_physical_address.iou;
    controller_physical_address.channel := unit_physical_address.channel;
    controller_physical_address.channel_address := unit_physical_address.channel_address;
    controller_physical_address.unit_address := 0;

  PROCEND get_controller_physical_address;
?? OLDTITLE ??
?? NEWTITLE := 'idle_pp_in_mtr', EJECT ??

{ PURPOSE:
{   This procedure sends a soft IDLE command to the PP, otherwise it only checks if the PP responded.

  PROCEDURE idle_pp_in_mtr
    (    logical_pp: iot$pp_number;
         send_idle: boolean;
     VAR status: syt$monitor_status);

    status.normal := TRUE;

    IF send_idle THEN
      iop$idle_path (logical_pp, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
    IFEND;

    iop$check_idle_pps;

  PROCEND idle_pp_in_mtr;
?? OLDTITLE ??
?? NEWTITLE := 'queue_pp_request', EJECT ??

{ PURPOSE:
{    This procedure queues PP request such as IDLE and RESUME.

  PROCEDURE queue_pp_request
    (    pp: iot$pp_number;
         io_request_p: ^iot$io_request;
     VAR status: syt$monitor_status);

    VAR
      count: 0 .. 0ff(16),
      disk_request_p: ^iot$disk_request,
      done: boolean,
      previously_set: boolean,
      ppit_p: ^iot$pp_interface_table,
      retry: integer,
      time: integer,
      timeout: integer;

    ppit_p := cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p;
    ppit_p^.pp_request_queue := NIL;
    ppit_p^.pp_request_queue_rma := 0;
    io_request_p^.response_processor_p := ^cmp$process_pp_response;
    disk_request_p := io_request_p^.device_request_p;

    { Interlock the pp_interface_table.

    time := #FREE_RUNNING_CLOCK (0);
    timeout := time + 2000000;
    count := 0;

    REPEAT
      i#test_set_bit (^ppit_p^, ioc$pp_interface_table_lock_bit, previously_set);
      count := count + 1;
      IF count >= 100 THEN
        time := #FREE_RUNNING_CLOCK (0);
        count := 0;
      IFEND;
    UNTIL (NOT previously_set) OR (time > timeout);

    IF previously_set THEN
      mtp$set_status_abnormal (ioc$subsystem_io_manager, ioc$pp_interlock_set, status);
      RETURN;
    IFEND;

    IF disk_request_p^.request.command [1].command_code = ioc$cc_idle THEN
      ppit_p^.idle_status := FALSE;
    ELSEIF disk_request_p^.request.command [1].command_code = ioc$cc_resume THEN
      ppit_p^.idle_status := TRUE;
    IFEND;

    ppit_p^.lock := FALSE;

    retry := 1;

  /queue_request/
    WHILE TRUE DO
      iop$queue_pp_request (ppit_p, io_request_p, status);

      IF NOT status.normal THEN
        IF status.condition = ioc$pp_interlock_set THEN
          retry := retry + 1;
          IF retry > 20 THEN
            EXIT /queue_request/;
          IFEND;
        ELSE

          EXIT /queue_request/;
        IFEND;
      ELSE
        EXIT /queue_request/;
      IFEND;
    WHILEND /queue_request/;

    time := #FREE_RUNNING_CLOCK (0);
    timeout := timeout + 1000;
    count := 0;
    done := FALSE;

    REPEAT
      iop$process_io_completions;
      IF disk_request_p^.request.command [1].command_code = ioc$cc_idle THEN
        done := cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.idle_status;
      ELSEIF disk_request_p^.request.command [1].command_code = ioc$cc_resume THEN
        done := NOT cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.idle_status;
      IFEND;
      count := count + 1;
      IF count >= 100 THEN
        time := #FREE_RUNNING_CLOCK (0);
        count := 0;
      IFEND;
    UNTIL (done) OR (time >= timeout);

  PROCEND queue_pp_request;
?? OLDTITLE ??
?? NEWTITLE := 'reenable_unit', EJECT ??

  PROCEDURE reenable_unit
    (    logical_unit: iot$logical_unit);

    cmv$logical_unit_table^ [logical_unit].unit_interface_table^.unit_status.disabled := FALSE;

    { Set element capabilities.

    cmv$logical_unit_table^ [logical_unit].element_capability :=
          $cmt$element_capabilities [cmc$volume_assignment, cmc$io_request_submission,
          cmc$concurrent_maintenance];
    cmv$logical_unit_table^ [logical_unit].element_access := $cmt$element_access [cmc$read, cmc$write];
    dmp$volume_up (logical_unit);

  PROCEND reenable_unit;
?? OLDTITLE ??
?? NEWTITLE := 'resume_pp_in_mtr', EJECT ??

{ PURPOSE:
{   This procedure sends a soft resume to the PP otherwise it only checks the pp response.

  PROCEDURE resume_pp_in_mtr
    (    resumed_pp: iot$pp_number;
         send_resume: boolean;
     VAR status: syt$monitor_status);


    status.normal := TRUE;

    IF send_resume THEN
      iop$idle_resume (resumed_pp, ioc$ira_resume, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
    IFEND;

    IF cmv$logical_pp_table_p^ [resumed_pp].pp_info.pp_type <> cmc$lpt_disk_pp_type THEN
      iop$process_io_completions;
    IFEND;

  PROCEND resume_pp_in_mtr;
?? OLDTITLE ??
?? NEWTITLE := 'setup_lun_list', EJECT ??

{ PURPOSE:
{   This procedure sets up a list of logical_units accessible from the given primary path element.

  PROCEDURE setup_lun_list
    (    pp: iot$pp_number;
         primary_path: cmt$physical_address;
         update_controller_address: boolean;
         logical_unit_list_p: ^ARRAY [ * ] OF cmt$rb_logical_unit_address;
     VAR number_of_units: integer);

    VAR
      logical_unit: iot$logical_unit,
      storage_device: boolean,
      ppit_p: ^iot$pp_interface_table,
      lun: iot$logical_unit,
      unit_element_p: ^cmt$peripheral_element_entry;

    number_of_units := 0;
    storage_device := FALSE;
    ppit_p := cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p;

    IF ((primary_path.address_specifier = cmv$mass_storage_address) OR
          (primary_path.address_specifier = cmv$hydra_mass_storage_address)) THEN
      cmp$locate_element_via_adr (primary_path, unit_element_p);
      IF unit_element_p <> NIL THEN
        logical_unit := unit_element_p^.logical_unit_number;
        storage_device := TRUE;
      IFEND;
    IFEND;

  /loop/
    FOR lun := LOWERBOUND (ppit_p^.unit_descriptors) TO UPPERBOUND (ppit_p^.unit_descriptors) DO
      IF ppit_p^.unit_descriptors [lun].unit_interface_table = NIL THEN
        CYCLE /loop/;
      IFEND;

      IF storage_device AND (logical_unit <> lun) THEN
        CYCLE /loop/;
      IFEND;

      number_of_units := number_of_units + 1;
      logical_unit_list_p^ [number_of_units].logical_unit := lun;
      logical_unit_list_p^ [number_of_units].controller :=
            ppit_p^.unit_descriptors [lun].physical_path.controller_number;

      IF update_controller_address THEN

        { Find the controller number by taking the first ON controller other than the one connected to the
        { primary unit.

        get_controller_address (primary_path, lun, logical_unit_list_p^ [number_of_units].controller);
      IFEND;
    FOREND /loop/;
  PROCEND setup_lun_list;
?? OLDTITLE ??
?? NEWTITLE := 'update_logical_unit_state', EJECT ??

{ PURPOSE:
{   This procedure update the state in the logical unit table and also attempts to up a volume.

  PROCEDURE update_logical_unit_state
    (    logical_unit: iot$logical_unit;
         pp: iot$pp_number);

    VAR
      controller_path: cmt$physical_address,
      eq: integer,
      index: integer,
      table_index: integer;

    controller_path.address_specifier :=
          $cmt$physical_address_specifier [cmc$iou, cmc$channel, cmc$channel_address];
    controller_path.iou := cmv$logical_pp_table_p^ [pp].pp_info.channel.iou_number;
    controller_path.channel.number := cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.
          unit_descriptors [logical_unit].physical_path.channel_number;
    controller_path.channel.concurrent := cmv$logical_pp_table_p^ [pp].pp_info.channel_interlock_p^.
          channel_characteristics [controller_path.channel.number].concurrent_channel;
    IF (controller_path.channel.concurrent AND (cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.
          unit_descriptors [logical_unit].unit_interface_table^.unit_type <> ioc$dt_ms895_2)) THEN
      IF (cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.unit_descriptors [logical_unit].
            physical_path.port = 1) THEN
        controller_path.channel.port := cmc$port_b;
      ELSE
        controller_path.channel.port := cmc$port_a;
      IFEND;
    ELSE
      controller_path.channel.port := cmc$unspecified_port;
    IFEND;

    controller_path.channel_address := cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.
          unit_descriptors [logical_unit].physical_path.controller_number;

    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
        IF cmv$peripheral_element_table.pointer^ [index].physical_descriptor.element_type =
              cmc$storage_device_element THEN
          IF (cmv$peripheral_element_table.pointer^ [index].logical_unit_number = logical_unit) THEN

            { Only process units in the ON state or units whose controller is ON.

            IF (cmv$peripheral_element_table.pointer^ [index].element_status.state = cmc$on) THEN

              { Check state of controller.

            /peripheral_loop/
              FOR table_index := LOWERBOUND (cmv$peripheral_element_table.pointer^)
                    TO UPPERBOUND (cmv$peripheral_element_table.pointer^) DO
                IF cmv$peripheral_element_table.pointer^ [table_index].physical_descriptor.configured
                      AND (cmv$peripheral_element_table.pointer^ [table_index].
                      physical_descriptor.element_type = cmc$controller_element) THEN
                  FOR eq := LOWERBOUND (cmv$peripheral_element_table.pointer^ [table_index].
                        physical_descriptor.equipment_path^) TO UPPERBOUND (cmv$peripheral_element_table.
                        pointer^ [table_index].physical_descriptor.equipment_path^) DO
                    IF (cmv$peripheral_element_table.pointer^ [table_index].physical_descriptor.
                          equipment_path^ [eq].iou = controller_path.iou) AND
                          (cmv$peripheral_element_table.pointer^ [table_index].physical_descriptor.
                          equipment_path^ [eq].channel = controller_path.channel) AND
                          (cmv$peripheral_element_table.pointer^ [table_index].physical_descriptor.
                          equipment_path^ [eq].channel_address = controller_path.channel_address) THEN

                      IF cmv$peripheral_element_table.pointer^ [table_index].element_status.state =
                            cmc$on THEN
                        EXIT /peripheral_loop/;
                      ELSE
                        RETURN;
                      IFEND;
                    IFEND;
                  FOREND;
                IFEND;
              FOREND /peripheral_loop/;

              cmv$logical_unit_table^ [logical_unit].unit_interface_table^.unit_status.disabled := FALSE;
              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$concurrent_maintenance];
              dmp$volume_up (logical_unit);
            IFEND;
            RETURN;
          IFEND;
        IFEND;
      IFEND;
    FOREND;

  PROCEND update_logical_unit_state;
?? OLDTITLE ??
?? NEWTITLE := 'update_peripheral_element_table', EJECT ??

{ PURPOSE:
{   This procedure will update the element_state and connection status information for the affected elements
{   as part of a state change.

  PROCEDURE update_peripheral_element_table
    (    request_block: cmt$request_block;
         new_state: cmt$element_state;
     VAR old_state: cmt$element_state);

    VAR
      downline_element: cmt$element_name,
      element_p: ^cmt$peripheral_element_entry,
      element2_p: ^cmt$peripheral_element_entry,
      i: integer,
      j: integer,
      new_status: cmt$connection_status,
      physical_channel: cmt$physical_channel;

    { Change the state in the peripheral_element_table.

  /peripheral_loop_1/
    FOR i := LOWERBOUND (cmv$peripheral_element_table.pointer^)
          TO UPPERBOUND (cmv$peripheral_element_table.pointer^) DO
      element_p := ^cmv$peripheral_element_table.pointer^ [i];
      IF NOT element_p^.physical_descriptor.configured THEN
        CYCLE /peripheral_loop_1/;
      IFEND;

      IF element_p^.element_name <> request_block.element_name THEN
        CYCLE /peripheral_loop_1/;
      IFEND;

      IF request_block.element_type = cmc$data_channel_element THEN
        IF element_p^.physical_descriptor.channel_path.iou <> request_block.iou THEN
          CYCLE /peripheral_loop_1/;
        ELSE
          physical_channel := element_p^.physical_descriptor.channel_path.channel;
        IFEND;
      IFEND;

      old_state := element_p^.element_status.state;
      element_p^.element_status.state := new_state;
      EXIT /peripheral_loop_1/;
    FOREND /peripheral_loop_1/;

    { Change state in the peripheral_element_table of the other port on an IPI channel

    IF (request_block.element_type = cmc$data_channel_element) AND
          (physical_channel.port <> cmc$unspecified_port) THEN

    /peripheral_loop_2/
      FOR j := i + 1 TO UPPERBOUND (cmv$peripheral_element_table.pointer^) DO
        element2_p := ^cmv$peripheral_element_table.pointer^ [j];
        IF NOT element2_p^.physical_descriptor.configured THEN
          CYCLE /peripheral_loop_2/;
        IFEND;

        IF element2_p^.physical_descriptor.element_type <> cmc$data_channel_element THEN
          CYCLE /peripheral_loop_2/;
        IFEND;

        IF (physical_channel.number = element2_p^.physical_descriptor.channel_path.channel.number) AND
              (physical_channel.concurrent = element2_p^.physical_descriptor.channel_path.channel.
              concurrent) AND (request_block.iou = element2_p^.physical_descriptor.channel_path.iou) THEN
          element2_p^.element_status.state := new_state;
          EXIT /peripheral_loop_2/;
        IFEND;
      FOREND /peripheral_loop_2/;
    IFEND;

    { Update connection_status in peripheral_element_table.

    IF new_state = cmc$on THEN
      new_status := cmc$active;
    ELSE
      new_status := cmc$inactive;
    IFEND;

    CASE request_block.element_type OF
    = cmc$data_channel_element =
      FOR i := LOWERBOUND (element_p^.physical_descriptor.channel_connection^)
            TO UPPERBOUND (element_p^.physical_descriptor.channel_connection^) DO
        IF new_state = cmc$on THEN
          cmp$locate_element_via_name (element_p^.physical_descriptor.channel_connection^ [i].
                downline_element, {iou_number} 0, element2_p);
          IF (element2_p <> NIL) AND (element2_p^.element_status.state = cmc$on) THEN
            element_p^.physical_descriptor.channel_connection^ [i].status := new_status;
          IFEND;
        ELSE
          element_p^.physical_descriptor.channel_connection^ [i].status := new_status;
        IFEND;
      FOREND;

    = cmc$controller_element, cmc$channel_adapter_element =
      FOR i := LOWERBOUND (element_p^.physical_descriptor.equipment_connection^)
            TO UPPERBOUND (element_p^.physical_descriptor.equipment_connection^) DO
        IF new_state = cmc$on THEN
          cmp$locate_element_via_name (element_p^.physical_descriptor.equipment_connection^ [i].
                downline_element, {iou_number} 0, element2_p);
          IF (element2_p <> NIL) AND (element2_p^.element_status.state = cmc$on) THEN
            element_p^.physical_descriptor.equipment_connection^ [i].status := new_status;
          IFEND;
        ELSE
          element_p^.physical_descriptor.equipment_connection^ [i].status := new_status;
        IFEND;
      FOREND;

      downline_element := element_p^.element_name;

    /pet_loop_1/
      FOR i := LOWERBOUND (cmv$peripheral_element_table.pointer^)
            TO UPPERBOUND (cmv$peripheral_element_table.pointer^) DO
        element_p := ^cmv$peripheral_element_table.pointer^ [i];
        IF NOT element_p^.physical_descriptor.configured THEN
          CYCLE /pet_loop_1/;
        IFEND;

        IF element_p^.physical_descriptor.element_type <> cmc$data_channel_element THEN
          CYCLE /pet_loop_1/;
        IFEND;

        IF (new_state = cmc$on) AND (element_p^.element_status.state <> cmc$on) THEN
          CYCLE /pet_loop_1/;
        IFEND;

      /downline_loop_1/
        FOR j := LOWERBOUND (element_p^.physical_descriptor.channel_connection^)
              TO UPPERBOUND (element_p^.physical_descriptor.channel_connection^) DO
          IF element_p^.physical_descriptor.channel_connection^ [j].downline_element = downline_element THEN
            IF new_state = cmc$on THEN
              IF element_p^.physical_descriptor.channel_connection^ [j].status <> cmc$disabled THEN
                element_p^.physical_descriptor.channel_connection^ [j].status := new_status;
              IFEND;
            ELSE
              element_p^.physical_descriptor.channel_connection^ [j].status := new_status;
            IFEND;
            EXIT /downline_loop_1/;
          IFEND;
        FOREND /downline_loop_1/;
      FOREND /pet_loop_1/;

    = cmc$storage_device_element =
      downline_element := element_p^.element_name;

      IF element_p^.product_id.product_number <> '  $887' THEN

      /pet_loop_2/
        FOR i := LOWERBOUND (cmv$peripheral_element_table.pointer^)
              TO UPPERBOUND (cmv$peripheral_element_table.pointer^) DO
          element_p := ^cmv$peripheral_element_table.pointer^ [i];
          IF NOT element_p^.physical_descriptor.configured THEN
            CYCLE /pet_loop_2/;
          IFEND;

          IF (element_p^.physical_descriptor.element_type <> cmc$controller_element) AND
                (element_p^.physical_descriptor.element_type <> cmc$channel_adapter_element) THEN
            CYCLE /pet_loop_2/;
          IFEND;

          IF (new_state = cmc$on) AND (element_p^.element_status.state <> cmc$on) THEN
            CYCLE /pet_loop_2/;
          IFEND;

        /downline_loop_2/
          FOR j := LOWERBOUND (element_p^.physical_descriptor.equipment_connection^)
                TO UPPERBOUND (element_p^.physical_descriptor.equipment_connection^) DO
            IF element_p^.physical_descriptor.equipment_connection^ [j].downline_element =
                  downline_element THEN
              element_p^.physical_descriptor.equipment_connection^ [j].status := new_status;
              EXIT /downline_loop_2/;
            IFEND;
          FOREND /downline_loop_2/;
        FOREND /pet_loop_2/;
      ELSE

      /pet_loop_3/
        FOR i := LOWERBOUND (cmv$peripheral_element_table.pointer^)
              TO UPPERBOUND (cmv$peripheral_element_table.pointer^) DO
          element_p := ^cmv$peripheral_element_table.pointer^ [i];
          IF NOT element_p^.physical_descriptor.configured THEN
            CYCLE /pet_loop_3/;
          IFEND;

          IF element_p^.physical_descriptor.element_type <> cmc$data_channel_element THEN
            CYCLE /pet_loop_3/;
          IFEND;

          IF (new_state = cmc$on) AND (element_p^.element_status.state <> cmc$on) THEN
            CYCLE /pet_loop_3/;
          IFEND;

        /downline_loop_3/
          FOR j := LOWERBOUND (element_p^.physical_descriptor.channel_connection^)
                TO UPPERBOUND (element_p^.physical_descriptor.channel_connection^) DO
            IF element_p^.physical_descriptor.channel_connection^ [j].downline_element = downline_element THEN
              element_p^.physical_descriptor.channel_connection^ [j].status := new_status;
              EXIT /downline_loop_3/;
            IFEND;
          FOREND /downline_loop_3/;
        FOREND /pet_loop_3/;
      IFEND;

    = cmc$communications_element, cmc$external_processor_element =

    /pet_loop_4/
      FOR i := LOWERBOUND (cmv$peripheral_element_table.pointer^)
            TO UPPERBOUND (cmv$peripheral_element_table.pointer^) DO
        element_p := ^cmv$peripheral_element_table.pointer^ [i];
        IF NOT element_p^.physical_descriptor.configured THEN
          CYCLE /pet_loop_4/;
        IFEND;

        IF element_p^.physical_descriptor.element_type <> cmc$data_channel_element THEN
          CYCLE /pet_loop_4/;
        IFEND;

        IF (new_state = cmc$on) AND (element_p^.element_status.state <> cmc$on) THEN
          CYCLE /pet_loop_4/;
        IFEND;

      /downline_loop_4/
        FOR j := LOWERBOUND (element_p^.physical_descriptor.channel_connection^)
              TO UPPERBOUND (element_p^.physical_descriptor.channel_connection^) DO
          IF element_p^.physical_descriptor.channel_connection^ [j].downline_element = downline_element THEN
            element_p^.physical_descriptor.channel_connection^ [j].status := new_status;
            EXIT /downline_loop_4/;
          IFEND;
        FOREND /downline_loop_4/;
      FOREND /pet_loop_4/;

    ELSE
    CASEND;

  PROCEND update_peripheral_element_table;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$change_connection_status', EJECT ??

{ PURPOSE:
{   This procedure modifies the status of a connection between elements.  If a channel is specified the
{   connection status for all downline connections of the specified channel are set to the new value.  If a
{   controller is specified the connection status for all downline connections of the specified controller
{   are set to the new value.  If a storage_device is specified the connection status between the specified
{   element and it's upline element is set to the new value.  Currently this interface is only called when
{   disabling connections.  The procedure UPDATE_PERIPHERAL_ELEMENT_TABLE changes the connection status of
{   elements when a state change is being processed.

  PROCEDURE [XDCL] cmp$change_connection_status
    (    physical_address: cmt$physical_address;
         new_connection_status: cmt$connection_status);

    VAR
      channel: cmt$physical_address,
      channel_element_p: ^cmt$peripheral_element_entry,
      controller: cmt$physical_address,
      controller_element_p: ^cmt$peripheral_element_entry,
      i: integer,
      unit_element_p: ^cmt$peripheral_element_entry;

    IF physical_address.address_specifier = cmv$data_channel_address THEN

      { Update all ACTIVE downline connections from the channel to the new connection status.

      cmp$locate_element_via_adr (physical_address, channel_element_p);
      IF channel_element_p = NIL THEN
        RETURN;
      IFEND;

      FOR i := LOWERBOUND (channel_element_p^.physical_descriptor.channel_connection^)
            TO UPPERBOUND (channel_element_p^.physical_descriptor.channel_connection^) DO
        IF channel_element_p^.physical_descriptor.channel_connection^ [i].status = cmc$active THEN
          channel_element_p^.physical_descriptor.channel_connection^ [i].status := new_connection_status;
        IFEND;
      FOREND;

    ELSEIF physical_address.address_specifier = cmv$controller_address THEN

      { Update all ACTIVE downline connections from the controller to the new connection status.

      cmp$locate_element_via_adr (physical_address, controller_element_p);
      IF controller_element_p = NIL THEN
        RETURN;
      IFEND;

      FOR i := LOWERBOUND (controller_element_p^.physical_descriptor.equipment_connection^)
            TO UPPERBOUND (controller_element_p^.physical_descriptor.equipment_connection^) DO
        IF controller_element_p^.physical_descriptor.equipment_connection^ [i].status = cmc$active THEN
          controller_element_p^.physical_descriptor.equipment_connection^ [i].status := new_connection_status;
        IFEND;
      FOREND;

    ELSEIF physical_address.address_specifier = cmv$mass_storage_address THEN

      { Get the unit and controller.

      cmp$locate_element_via_adr (physical_address, unit_element_p);
      IF unit_element_p = NIL THEN
        RETURN;
      IFEND;

      get_controller_physical_address (physical_address, controller);
      cmp$locate_element_via_adr (controller, controller_element_p);
      IF controller_element_p = NIL THEN
        RETURN;
      IFEND;

      IF cmv$debug = 08(16) THEN
        mtp$error_stop ('Debug stop 08(16) - cmp$change_connection_status');
      IFEND;

      { Locate the unit in the controllers downline connections and insert the new value.

      FOR i := LOWERBOUND (controller_element_p^.physical_descriptor.equipment_connection^)
            TO UPPERBOUND (controller_element_p^.physical_descriptor.equipment_connection^) DO
        IF controller_element_p^.physical_descriptor.equipment_connection^ [i].downline_element =
              unit_element_p^.element_name THEN
          controller_element_p^.physical_descriptor.equipment_connection^ [i].status := new_connection_status;
          RETURN;
        IFEND;
      FOREND;

      IF cmv$debug = 09(16) THEN
        mtp$error_stop ('Debug stop 09(16) - cmp$change_connection_status');
      IFEND;

    ELSEIF physical_address.address_specifier = cmv$hydra_mass_storage_address THEN

      { Get the unit and channel.

      cmp$locate_element_via_adr (physical_address, unit_element_p);
      IF unit_element_p = NIL THEN
        RETURN;
      IFEND;

      get_channel_physical_address (physical_address, channel);
      cmp$locate_element_via_adr (channel, channel_element_p);
      IF channel_element_p = NIL THEN
        RETURN;
      IFEND;

      { Locate the unit in the channels downline connections and insert the new value.

      FOR i := LOWERBOUND (channel_element_p^.physical_descriptor.channel_connection^)
            TO UPPERBOUND (channel_element_p^.physical_descriptor.channel_connection^) DO
        IF channel_element_p^.physical_descriptor.channel_connection^ [i].downline_element =
              unit_element_p^.element_name THEN
          channel_element_p^.physical_descriptor.channel_connection^ [i].status := new_connection_status;
          RETURN;
        IFEND;
      FOREND;
    ELSE

      { This should never occur.

    IFEND;

  PROCEND cmp$change_connection_status;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$default_response_handler', EJECT ??

{ PURPOSE:
{   This procedure is a dummy response handler procedure used for foreign pp subsystem.

  PROCEDURE [XDCL] cmp$default_response_handler
    (    pp_response_header_p: ^iot$pp_response;
         detailed_status_p: ^iot$detailed_status;
         pp_number: 1 .. ioc$pp_count;
     VAR status: syt$monitor_status);

    status.normal := TRUE;

  PROCEND cmp$default_response_handler;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$enable_all_connections', EJECT ??

{ PURPOSE:
{   This procedure will search the peripheral element table and change the connection status of all DISABLED
{   connection to ACTIVE.  For each element whose connection status is modified, the code to rebuild the
{   affected PPs PP interface table will be executed.
{ NOTE:
{   This code is executed only during the UNSTEP_SYSTEM monitor command.

  PROCEDURE [XDCL] cmp$enable_all_connections;

    VAR
      disabled_connection_found: boolean,
      display_header: boolean,
      i: integer,
      j: integer,
      pete_p: ^cmt$peripheral_element_entry,
      status: ost$status;

    display_header := TRUE;

  /search_pet/
    FOR i := LOWERBOUND (cmv$peripheral_element_table.pointer^)
          TO UPPERBOUND (cmv$peripheral_element_table.pointer^) DO
      pete_p := ^cmv$peripheral_element_table.pointer^ [i];
      IF NOT pete_p^.physical_descriptor.configured THEN
        CYCLE /search_pet/;
      IFEND;

      disabled_connection_found := FALSE;
      CASE pete_p^.physical_descriptor.element_type OF
      = cmc$data_channel_element =
        FOR j := LOWERBOUND (pete_p^.physical_descriptor.channel_connection^)
              TO UPPERBOUND (pete_p^.physical_descriptor.channel_connection^) DO
          IF pete_p^.physical_descriptor.channel_connection^[j].status = cmc$disabled THEN
            pete_p^.physical_descriptor.channel_connection^[j].status := cmc$active;
            disabled_connection_found := TRUE;
          IFEND;
        FOREND;
        IF disabled_connection_found THEN
          IF display_header THEN
            display_header := FALSE;
            dpp$display_error('Enable elements:');
          IFEND;
          build_affected_pp_tables (pete_p);
        IFEND;

      = cmc$controller_element =
        FOR j := LOWERBOUND (pete_p^.physical_descriptor.equipment_connection^)
              TO UPPERBOUND (pete_p^.physical_descriptor.equipment_connection^) DO
          IF pete_p^.physical_descriptor.equipment_connection^[j].status = cmc$disabled THEN
            pete_p^.physical_descriptor.equipment_connection^[j].status := cmc$active;
            disabled_connection_found := TRUE;
          IFEND;
        FOREND;
        IF disabled_connection_found THEN
          IF display_header THEN
            display_header := FALSE;
            dpp$display_error('Enable elements:');
          IFEND;
          build_affected_pp_tables (pete_p);
        IFEND;

      ELSE
      CASEND;

    FOREND /search_pet/;

  PROCEND cmp$enable_all_connections;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$idle_system_device_driver', EJECT ??

{ PURPOSE:
{   This procedure is called to software idle the system device pp and return information about the
{   partner pp if it exists.

  PROCEDURE [XDCL] cmp$idle_system_device_driver
    (    pp: dst$iou_resource;
     VAR dual_pp: boolean;
     VAR pp_interface_table_rma: ost$real_memory_address;
     VAR partner_pp: dst$iou_resource);

    VAR
      count: 0 .. 0ff(16),
      pp_index: iot$pp_number,
      status: syt$monitor_status,
      time: integer,
      timeout: integer;

    dual_pp := FALSE;
    IF NOT cmv$system_device_pp.software_idle THEN
      RETURN;
    IFEND;

    IF (cmv$system_device_pp.primary_pp.number <> pp.number) AND
          (cmv$system_device_pp.primary_pp.iou_number <> pp.iou_number) AND
          (cmv$system_device_pp.primary_pp.channel_protocol <> pp.channel_protocol) THEN
      RETURN;
    IFEND;

    iop$idle_resume (LOWERBOUND (cmv$logical_pp_table_p^), ioc$ira_idle, status);

    time := #FREE_RUNNING_CLOCK (0);
    timeout := time + 2000000;
    count := 0;

    REPEAT
      iop$process_io_completions;
      count := count + 1;
      IF count >= 100 THEN
        time := #FREE_RUNNING_CLOCK (0);
        count := 0;
      IFEND;
    UNTIL (cmv$logical_pp_table_p^ [1].pp_info.pp_interface_table_p^.idle_status) OR (time > timeout);

    cmp$manage_channel_lock ({Set lock =} FALSE,
          cmv$logical_pp_table_p^ [1].pp_info.channel.iou_number,
          cmv$logical_pp_table_p^ [1].pp_info.pp_interface_table_p^.
          unit_descriptors [2].physical_path.channel_number, (pp.channel_protocol = dsc$cpt_cio), status);

    IF (cmv$new_logical_unit_table <> NIL) AND (cmv$new_logical_pp_table_p <> NIL) THEN

      { Change the pointer values of the old tables to the new tables.  The new tables are being built based
      { on the full configuration.

      cmv$logical_unit_table := cmv$new_logical_unit_table;
      cmv$logical_pp_table_p := cmv$new_logical_pp_table_p;
      cmv$system_device_pp.software_idle := FALSE;
      cmv$max_number_of_pp := 0;

    /find_last_pp/
      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
          cmv$max_number_of_pp := pp_index;
          EXIT /find_last_pp/;
        IFEND;
      FOREND /find_last_pp/;
      dsp$advance_ds_sequence_in_mtr (dsc$dss_system_core_idled);
    IFEND;

    dual_pp := cmv$system_device_pp.dual_pp;
    IF dual_pp THEN
      partner_pp := cmv$system_device_pp.partner_pp;
      pp_interface_table_rma := cmv$system_device_pp.ppit_rma;
    IFEND;

  PROCEND cmp$idle_system_device_driver;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$manage_channel_lock', EJECT ??

{ PURPOSE:
{   This procedure locks OR unlocks the channel.

  PROCEDURE [XDCL] cmp$manage_channel_lock
    (    set_lock: boolean;
         iou_number: dst$iou_number;
         channel_number: dst$physical_resource_number;
         concurrent: boolean;
     VAR status: syt$monitor_status);

    TYPE
      t$lock = RECORD
        CASE (c$idr_io, c$idr_compare_swap) OF
        = c$idr_io =
          io: iot$table_lock_entry,
        = c$idr_compare_swap =
          compare_swap: integer,
        CASEND,
      RECEND;

    VAR
      actual: t$lock,
      channel_lock_p: ^integer,
      channel_lock_seq_p: ^SEQ ( * ),
      final: t$lock,
      initial: t$lock,
      succeeded: boolean;

    status.normal := TRUE;

    IF cmv$iou_table_p = NIL THEN
      RETURN;
    IFEND;

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

    IF NOT concurrent THEN
      IF (cmv$iou_table_p^ [iou_number].nio_channel_lock_p <> NIL) THEN
        channel_lock_seq_p := #SEQ (cmv$iou_table_p^ [iou_number].
              nio_channel_lock_p^.channel_table [channel_number]);
      ELSE

        { No NIO channel lock assume we are running on I4C, remove codes if DS passes in correct NIO/CIO
        { channel.

        IF (cmv$iou_table_p^ [iou_number].cio_channel_lock_p <> NIL) THEN
          channel_lock_seq_p := #SEQ (cmv$iou_table_p^ [iou_number].
                cio_channel_lock_p^.channel_table [channel_number]);
        ELSE
          RETURN;
        IFEND;
      IFEND;
    ELSE
      IF (cmv$iou_table_p^ [iou_number].cio_channel_lock_p <> NIL) THEN
        channel_lock_seq_p := #SEQ (cmv$iou_table_p^ [iou_number].
              cio_channel_lock_p^.channel_table [channel_number]);
      ELSE
        RETURN;
      IFEND;
    IFEND;

    RESET channel_lock_seq_p;
    NEXT channel_lock_p IN channel_lock_seq_p;

    CASE set_lock OF
    = TRUE =
      REPEAT
        actual.compare_swap := 0;
        REPEAT
          initial.io := actual.io;
          final.io := actual.io;
          final.io.maintenance_need_channel := TRUE;
          osp$set_locked_variable (channel_lock_p^, initial.compare_swap, final.compare_swap,
                actual.compare_swap, succeeded);
        UNTIL succeeded;

        initial.io := final.io;
        initial.io.channel_locked := FALSE;
        final.io.channel_locked := TRUE;
        osp$set_locked_variable (channel_lock_p^, initial.compare_swap, final.compare_swap,
              actual.compare_swap, succeeded);
        IF NOT succeeded THEN
          IF (mtv$time_to_call_handshaking - #FREE_RUNNING_CLOCK (0)) < 0 THEN
            dsp$perform_cpu_pp_handshaking;

            { Ensure that the disk driver pp that has the channel locked is not waiting for it's response
            { buffer to be emptied before it can clear the channel lock.

            iop$process_io_completions;
          IFEND;
        IFEND;
      UNTIL succeeded;

    = FALSE =
      actual.compare_swap := 0;
      REPEAT
        initial.io := actual.io;
        final.io := actual.io;
        final.io.channel_locked := FALSE;
        final.io.maintenance_need_channel := FALSE;
        osp$set_locked_variable (channel_lock_p^, initial.compare_swap, final.compare_swap,
              actual.compare_swap, succeeded);
      UNTIL succeeded;

    ELSE
      mtp$error_stop (' Illegal function in cmp$manage_channel_lock');
    CASEND;

  PROCEND cmp$manage_channel_lock;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$monitor_routines', EJECT ??

{ PURPOSE:
{   This procedure process Configuration Management monitor requests.

  PROCEDURE [XDCL] cmp$monitor_routines
    (VAR request_block: cmt$request_block);

    request_block.status.normal := TRUE;

    CASE request_block.kind OF
    = cmc$rbk_assign_pp =
      cmv$logical_pp_table_p^ [request_block.assigned_pp].flags.pp_loaded := request_block.assigned;

    = cmc$rbk_change_state =
      change_state (request_block, request_block.status);

    = cmc$rbk_idle_pp =
      idle_pp_in_mtr (request_block.idled_pp, request_block.send_idle, request_block.status);

    = cmc$rbk_queue_pp_request =
      queue_pp_request (request_block.queued_pp, request_block.request_p, request_block.status);

    = cmc$rbk_request_stack_memory =
      assign_stack_segment (request_block.first_byte_address_p, request_block.rma, request_block.status);

    = cmc$rbk_resume_pp =
      resume_pp_in_mtr (request_block.resumed_pp, request_block.send_resume, request_block.status);

    ELSE
    CASEND;

  PROCEND cmp$monitor_routines;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$process_pp_response', EJECT ??

{ PURPOSE:
{   This procedure processes a PP response.

  PROCEDURE [XDCL] cmp$process_pp_response
    (    pp_response_p: ^iot$pp_response;
         detailed_status_p: ^iot$detailed_status;
         pp: 1 .. ioc$pp_count;
     VAR status: syt$monitor_status);

    VAR
      completed_request_p: ^iot$disk_request,
      count: 0 .. 0ff(16),
      previously_set: boolean,
      pp_interface_table_p: ^iot$pp_interface_table,
      time: integer,
      timeout: integer;

    status.normal := TRUE;

    IF pp_response_p^.response_code.primary_response = ioc$intermediate_response THEN
      RETURN;
    IFEND;
    completed_request_p := pp_response_p^.request^.device_request_p;

    { Set idle_status flag in pp_interface_table.

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

    { Interlock the pp_interface_table.

    time := #FREE_RUNNING_CLOCK (0);
    timeout := time + 2000000;
    count := 0;

    REPEAT
      i#test_set_bit (^pp_interface_table_p^, ioc$pp_interface_table_lock_bit, previously_set);
      count := count + 1;
      IF count >= 100 THEN
        time := #FREE_RUNNING_CLOCK (0);
        count := 0;
      IFEND;
    UNTIL (NOT previously_set) OR (time > timeout);
    IF previously_set THEN
      mtp$set_status_abnormal (ioc$subsystem_io_manager, ioc$pp_interlock_set, status);
      RETURN;
    IFEND;

    CASE completed_request_p^.request.command [1].command_code OF
    = ioc$cc_idle =
      pp_interface_table_p^.idle_status := TRUE;
    = ioc$cc_resume =
      pp_interface_table_p^.idle_status := FALSE;
    ELSE
    CASEND;

    pp_interface_table_p^.lock := FALSE;

  PROCEND cmp$process_pp_response;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$reenable_unit', EJECT ??

{ PURPOSE:
{   This function determines whether or not a given path should be reenabled.

  FUNCTION [XDCL, UNSAFE] cmp$reenable_unit
    (    path: cmt$physical_address): boolean;

    VAR
      channel_element_p: ^cmt$peripheral_element_entry,
      channel_address: cmt$physical_address,
      unit_element_p: ^cmt$peripheral_element_entry,
      unit_address: cmt$physical_address,
      controller_element_p: ^cmt$peripheral_element_entry,
      controller_address: cmt$physical_address,
      unit_on: boolean,
      channel_on: boolean,
      equipment_on: boolean,
      unit: integer,
      eq: integer,
      index: integer;

    cmp$reenable_unit := FALSE;
    IF cmv$peripheral_element_table.pointer = NIL THEN
      RETURN;
    IFEND;

    unit_on := FALSE;
    equipment_on := FALSE;
    channel_on := FALSE;

    IF path.address_specifier = cmv$mass_storage_address THEN
      cmp$locate_element_via_adr (path, unit_element_p);
      IF unit_element_p <> NIL THEN
        unit_on := unit_element_p^.element_status.state = cmc$on;
      IFEND;

      IF unit_on THEN
        get_controller_physical_address (path, controller_address);
        cmp$locate_element_via_adr (controller_address, controller_element_p);
        IF controller_element_p <> NIL THEN
          equipment_on := controller_element_p^.element_status.state = cmc$on;
        IFEND;
      IFEND;

      IF unit_on AND equipment_on THEN
        get_channel_physical_address (path, channel_address);
        cmp$locate_element_via_adr (channel_address, channel_element_p);
        IF channel_element_p <> NIL THEN
          channel_on := channel_element_p^.element_status.state = cmc$on;
        IFEND;
      IFEND;

      cmp$reenable_unit := channel_on AND equipment_on AND unit_on;

    ELSEIF path.address_specifier = cmv$controller_address THEN
      cmp$locate_element_via_adr (path, controller_element_p);
      IF controller_element_p <> NIL THEN
        equipment_on := controller_element_p^.element_status.state = cmc$on;
      IFEND;

      IF equipment_on THEN
        get_channel_physical_address (path, channel_address);
        cmp$locate_element_via_adr (channel_address, channel_element_p);
        IF channel_element_p <> NIL THEN
          channel_on := channel_element_p^.element_status.state = cmc$on;
        IFEND;
      IFEND;

      cmp$reenable_unit := channel_on AND equipment_on;

    ELSEIF path.address_specifier = cmv$hydra_mass_storage_address THEN
      cmp$locate_element_via_adr (path, unit_element_p);
      IF unit_element_p <> NIL THEN
        unit_on := unit_element_p^.element_status.state = cmc$on;
      IFEND;

      IF unit_on THEN
        get_channel_physical_address (path, channel_address);
        cmp$locate_element_via_adr (channel_address, channel_element_p);
        IF channel_element_p <> NIL THEN
          channel_on := channel_element_p^.element_status.state = cmc$on;
        IFEND;
      IFEND;

      cmp$reenable_unit := channel_on AND unit_on;
    IFEND;

  FUNCEND cmp$reenable_unit;
?? OLDTITLE ??
?? NEWTITLE := 'cmp$switch_to_redundant_path', EJECT ??

{ PURPOSE:
{   This procedure will search an available redundant path given a primary path (i.e. iou/channel or
{   iou/channel/controller).  If found, an attempt will be made to change access to all units from the
{   primary path to the redundant path.  The SUCCESSFUL parameter will be set to TRUE if the request
{   succeeded.

  PROCEDURE [XDCL] cmp$switch_to_redundant_path
    (    pp: iot$pp_number;
         primary_path: cmt$physical_address;
     VAR successful: boolean);

    VAR
      driver_name: pmt$program_name,
      list_index: integer,
      logical_unit_list_p: ^ARRAY [ * ] OF cmt$rb_logical_unit_address,
      number_of_path: integer,
      number_of_units: integer,
      pp_table_rma_list: ARRAY [cmt$physical_equipment_number] OF ost$real_memory_address,
      pp_to_clear_list_p: ^ARRAY [ * ] OF iot$pp_number,
      pp_to_set_list_p: ^ARRAY [ * ] OF iot$pp_number,
      redundant_channel_list: ARRAY [cmt$physical_equipment_number] OF cmt$physical_address,
      redundant_path_available: boolean,
      redundant_path_pp_list: ARRAY [cmt$physical_equipment_number] OF iot$pp_number,
      status: syt$monitor_status,
      temp_unit_list_p: ^ARRAY [ * ] OF cmt$rb_logical_unit_address,
      update_controller_address: boolean;

    successful := FALSE;

    cmp$find_redundant_path (primary_path, cmc$down, redundant_path_available, update_controller_address,
          number_of_path, redundant_channel_list, redundant_path_pp_list, driver_name, pp_table_rma_list);
    IF cmv$debug = 016(16) THEN
      mtp$error_stop (' Debug stop 016(16) cmp$switch_to_redundant_path.');
    IFEND;
    IF NOT redundant_path_available THEN
      RETURN;
    IFEND;

    { Update handshaking value to prevent timeout.

    dsp$perform_cpu_pp_handshaking;

    { Find all logical_units accessible from the primary path element.

    PUSH temp_unit_list_p: [1 .. UPPERVALUE (cmt$physical_unit_number)];
    setup_lun_list (pp, primary_path, update_controller_address, temp_unit_list_p, number_of_units);

    PUSH logical_unit_list_p: [1 .. number_of_units];
    FOR list_index := LOWERBOUND (logical_unit_list_p^) TO number_of_units DO
      logical_unit_list_p^ [list_index] := temp_unit_list_p^ [list_index];
    FOREND;

    PUSH pp_to_clear_list_p: [0 .. 0];
    pp_to_clear_list_p^ [0] := pp;
    PUSH pp_to_set_list_p: [0 .. number_of_path];
    FOR list_index := 0 TO number_of_path DO
      pp_to_set_list_p^ [list_index] := redundant_path_pp_list [list_index];
    FOREND;

    { Idle primary pp and redundant pp if not identical.

    iop$idle_path (pp, status);
    FOR list_index := LOWERBOUND (redundant_path_pp_list) TO number_of_path DO
      IF pp <> redundant_path_pp_list [list_index] THEN
        iop$idle_path (redundant_path_pp_list [list_index], status);
      IFEND;
    FOREND;

    { Update unit descriptors.

    change_unit_descriptors (update_controller_address, pp_to_clear_list_p^, pp_to_set_list_p^,
          logical_unit_list_p);

    iop$idle_resume (pp, ioc$ira_resume, status);
    FOR list_index := LOWERBOUND (redundant_path_pp_list) TO number_of_path DO
      IF pp <> redundant_path_pp_list [list_index] THEN
        iop$idle_resume (redundant_path_pp_list [list_index], ioc$ira_resume, status);
      IFEND;
    FOREND;
    successful := TRUE;

  PROCEND cmp$switch_to_redundant_path;
?? OLDTITLE ??
MODEND cmm$monitor_routines;
