?? RIGHT := 110 ??
?? TITLE := 'CM Monitor and Job Mode interfaces' ??
MODULE cmm$monitor_job_mode_interfaces;

{ PURPOSE:
{   This module contains interfaces executing both in Monitor and Job mode
{   to retrieve configuration information.
{ NOTE:
{   Care should be taken to access only Mainframe Wired data structures from this module.

?? NEWTITLE := 'Global Declarations Referenced By This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc cmt$connection
*copyc cmt$element_definition
*copyc ost$hardware_subranges
?? POP ??
*copyc cmp$support_redundant_access
*copyc mtp$error_stop
*copyc cmv$controller_address
*copyc cmv$data_channel_address
*copyc cmv$hydra_mass_storage_address
*copyc cmv$logical_pp_table_p
*copyc cmv$mass_storage_address
*copyc cmv$peripheral_element_table

  VAR
    cmv$debug: [XREF] 0 .. 255,
    cmv$debug_stop: [XREF] boolean;

?? TITLE := ' cmp$find_redundant_path', EJECT ??

{ PURPOSE:
{   This procedure searches for an available redundant channel given the
{   primary channel address.  It returns the driver name, the controller type,
{   and the RMA to the PPIT of the redundant channel.
{
{ NOTE:
{   A channel is redundant if it is connected to a certain group of disk and
{   tape subsystems and if it is not the first on channel in the connection.
{

  PROCEDURE [XDCL] cmp$find_redundant_path
    (    primary_path_element: cmt$physical_address;
         new_state: cmt$element_state;
     VAR redundant_path_available: boolean;
     VAR update_controller_address: boolean;
     VAR number_of_path: integer;
     VAR redundant_channel_list: array [cmt$physical_equipment_number] of cmt$physical_address;
     VAR redundant_path_pp_list: array [cmt$physical_equipment_number] of iot$pp_number;
     VAR driver_name: pmt$program_name;
     VAR pp_table_rma_list: array [cmt$physical_equipment_number] of ost$real_memory_address);

    CONST
      max_number_of_entries = 15;

    VAR
      channel: cmt$physical_address,
      channel_element_p: ^cmt$peripheral_element_entry,
      connection_status: cmt$connection_status,
      controller: cmt$physical_address,
      controller_element_p: ^cmt$peripheral_element_entry,
      element_list_p: ^array [ * ] of ^cmt$peripheral_element_entry,
      i: integer,
      j: integer,
      index: integer,
      pp_available: boolean,
      primary_controller: cmt$physical_address,
      unit_element_list_p: ^array [ * ] of ^cmt$peripheral_element_entry,
      unit_element_p: ^cmt$peripheral_element_entry,
      unit_path: cmt$physical_address;

?? NEWTITLE := 'F$CHANNEL_IN_LIST', EJECT ??

{ PURPOSE:
{   Function to return whether or not a channel is present in a list.

    FUNCTION [inline] f$channel_in_list
      (    channel: cmt$physical_address;
           redundant_channel_list: array [cmt$physical_equipment_number] of cmt$physical_address;
           number_of_path: integer): boolean;

      VAR
        list_index: integer;

      f$channel_in_list := FALSE;
      IF number_of_path >= 0 THEN
        list_index := LOWERBOUND (redundant_channel_list);
        WHILE (list_index <= number_of_path) DO
          IF channel = redundant_channel_list [list_index] THEN
            f$channel_in_list := TRUE;
            RETURN; {----->
          IFEND;
          list_index := list_index + 1;
        WHILEND;
      IFEND;

    FUNCEND f$channel_in_list;
?? OLDTITLE ??
?? EJECT ??
    redundant_path_available := FALSE;
    update_controller_address := FALSE;

    FOR index := LOWERVALUE (cmt$physical_equipment_number) TO UPPERVALUE (cmt$physical_equipment_number) DO
      redundant_path_pp_list [index] := 0;
    FOREND;
    number_of_path := -1;

    IF cmv$debug = 01(16) THEN
      mtp$error_stop ('Debug stop in cmp$find_redundant_path.');
    IFEND;

    IF primary_path_element.address_specifier = cmv$data_channel_address THEN
      {
      { Get list of controllers connected to the specified channel.
      {
      PUSH element_list_p: [0 .. max_number_of_entries];
      cmp$get_element_list (primary_path_element, cmc$controller_element, element_list_p);

    /controller_element_loop_1/
      FOR index := LOWERBOUND (element_list_p^) TO UPPERBOUND (element_list_p^) DO
        IF element_list_p^ [index] = NIL THEN
          CYCLE /controller_element_loop_1/;
        IFEND;

        controller_element_p := element_list_p^ [index];
        IF NOT controller_element_p^.physical_descriptor.configured THEN
          CYCLE /controller_element_loop_1/;
        IFEND;

        IF controller_element_p^.element_status.state <> cmc$on THEN
          CYCLE /controller_element_loop_1/;
        IFEND;

        IF NOT cmp$support_redundant_access (cmc$controller_element, controller_element_p^.product_id) THEN
          RETURN;
        IFEND;
        {
        {See if the controller connected to the channel has any redundant path(s).
        {Search for an available redundant channel by going to all active units and then
        {look at their upline connections.  If there is a different channel on any of
        {the controllers connected to these units, it can be a redundant channel.
        {
        controller.address_specifier := cmv$controller_address;
        controller.iou := primary_path_element.iou;
        controller.channel := primary_path_element.channel;
        controller.channel_address := controller_element_p^.physical_descriptor.equipment_path^ [1].
              channel_address;

        PUSH unit_element_list_p: [0 .. max_number_of_entries];
        cmp$get_element_list (controller, cmc$storage_device_element, unit_element_list_p);

      /unit_element_loop_1/
        FOR i := LOWERBOUND (unit_element_list_p^) TO UPPERBOUND (unit_element_list_p^) DO
          IF unit_element_list_p^ [i] = NIL THEN
            CYCLE /unit_element_loop_1/;
          IFEND;

          unit_element_p := unit_element_list_p^ [i];
          IF NOT unit_element_p^.physical_descriptor.configured THEN
            CYCLE /unit_element_loop_1/;
          IFEND;

          IF unit_element_p^.element_status.state <> cmc$on THEN
            CYCLE /unit_element_loop_1/;
          IFEND;

        /unit_path_loop_1/
          FOR j := LOWERBOUND (unit_element_p^.physical_descriptor.unit_path^)
                TO UPPERBOUND (unit_element_p^.physical_descriptor.unit_path^) DO
            unit_path := unit_element_p^.physical_descriptor.unit_path^ [j];
            IF ((unit_path.iou = controller.iou) AND (unit_path.channel = controller.channel) AND
                  (unit_path.channel_address = controller.channel_address)) THEN
              CYCLE /unit_path_loop_1/;
            IFEND;

            get_controller_physical_address (unit_path, controller);
            cmp$locate_element_via_adr (controller, controller_element_p);
            IF (controller_element_p = NIL) OR (controller_element_p^.element_status.state <> cmc$on) THEN
              CYCLE /unit_path_loop_1/;
            IFEND;
            {
            { If the connection status is not active this path should not be checked.
            {
            get_connection_status (controller_element_p, unit_element_p^.element_name, connection_status);
            IF connection_status <> cmc$active THEN
              CYCLE /unit_path_loop_1/;
            IFEND;

            get_channel_physical_address (unit_path, channel);
            cmp$locate_element_via_adr (channel, channel_element_p);
            IF (channel_element_p = NIL) OR (channel_element_p^.element_status.state <> cmc$on) THEN
              CYCLE /unit_path_loop_1/;
            IFEND;

            {
            { If the connection status is not active we shouldn't have to check this path,
            { however, if the new_state is cmc$on, the connection status will change before
            { we actually build the PP tables.
            {
            get_connection_status (channel_element_p, controller_element_p^.element_name, connection_status);
            IF (new_state <> cmc$on) AND (connection_status <> cmc$active) THEN
              CYCLE /unit_path_loop_1/;
            IFEND;

            IF f$channel_in_list (channel, redundant_channel_list, number_of_path) THEN
              CYCLE /unit_path_loop_1/;
            IFEND;

            setup_redundant_path_info (channel, number_of_path, pp_available, pp_table_rma_list,
                  redundant_path_pp_list, redundant_channel_list);
            IF pp_available THEN
              update_controller_address := TRUE;
              redundant_path_available := TRUE;
              driver_name := channel_element_p^.physical_descriptor.peripheral_driver_name;
            IFEND;
          FOREND /unit_path_loop_1/;
        FOREND /unit_element_loop_1/;
      FOREND /controller_element_loop_1/;

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

    ELSEIF primary_path_element.address_specifier = cmv$controller_address THEN
      {
      {Controller address
      {
      cmp$locate_element_via_adr (primary_path_element, controller_element_p);
      IF controller_element_p = NIL THEN
        RETURN;
      IFEND;

      IF NOT cmp$support_redundant_access (cmc$controller_element, controller_element_p^.product_id) THEN
        RETURN;
      IFEND;

      update_controller_address := TRUE;
      PUSH unit_element_list_p: [0 .. max_number_of_entries];
      cmp$get_element_list (primary_path_element, cmc$storage_device_element, unit_element_list_p);

    /unit_element_loop_2/
      FOR index := LOWERBOUND (unit_element_list_p^) TO UPPERBOUND (unit_element_list_p^) DO
        IF unit_element_list_p^ [index] = NIL THEN
          CYCLE /unit_element_loop_2/;
        IFEND;

        unit_element_p := unit_element_list_p^ [index];
        IF NOT unit_element_p^.physical_descriptor.configured THEN
          CYCLE /unit_element_loop_2/;
        IFEND;

        IF unit_element_p^.element_status.state <> cmc$on THEN
          CYCLE /unit_element_loop_2/;
        IFEND;

      /unit_path_loop_2/
        FOR j := LOWERBOUND (unit_element_p^.physical_descriptor.unit_path^)
              TO UPPERBOUND (unit_element_p^.physical_descriptor.unit_path^) DO
          unit_path := unit_element_p^.physical_descriptor.unit_path^ [j];

          get_controller_physical_address (unit_path, controller);
          IF controller = primary_path_element THEN
            CYCLE /unit_path_loop_2/;
          IFEND;

          cmp$locate_element_via_adr (controller, controller_element_p);
          IF (controller_element_p = NIL) OR (controller_element_p^.element_status.state <> cmc$on) THEN
            CYCLE /unit_path_loop_2/;
          IFEND;
          {
          { If the connection status is not active we shouldn't have to check this path,
          { however, if the new_state is cmc$on, the connection status will change before
          { we actually build the PP tables.
          {
          get_connection_status (controller_element_p, unit_element_p^.element_name, connection_status);
          IF (new_state <> cmc$on) AND (connection_status <> cmc$active) THEN
            CYCLE /unit_path_loop_2/;
          IFEND;

          get_channel_physical_address (unit_path, channel);
          cmp$locate_element_via_adr (channel, channel_element_p);
          IF (channel_element_p = NIL) OR (channel_element_p^.element_status.state <> cmc$on) THEN
            CYCLE /unit_path_loop_2/;
          IFEND;
          {
          { If the connection status is not active we shouldn't have to check this path,
          { however, if the new_state is cmc$on, the connection status will change before
          { we actually build the PP tables.
          {
          get_connection_status (channel_element_p, controller_element_p^.element_name, connection_status);
          IF (new_state <> cmc$on) AND (connection_status <> cmc$active) THEN
            CYCLE /unit_path_loop_2/;
          IFEND;

          IF f$channel_in_list (channel, redundant_channel_list, number_of_path) THEN
            CYCLE /unit_path_loop_2/;
          IFEND;

          setup_redundant_path_info (channel, number_of_path, pp_available, pp_table_rma_list,
                redundant_path_pp_list, redundant_channel_list);
          IF pp_available THEN
            update_controller_address := TRUE;
            redundant_path_available := TRUE;
            driver_name := channel_element_p^.physical_descriptor.peripheral_driver_name;
          IFEND;
        FOREND /unit_path_loop_2/;
      FOREND /unit_element_loop_2/;

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

    ELSEIF primary_path_element.address_specifier = cmv$mass_storage_address THEN
      {
      {Storage device element
      {
      cmp$locate_element_via_adr (primary_path_element, unit_element_p);
      IF unit_element_p = NIL THEN
        RETURN;
      IFEND;

    /unit_path_loop_3/
      FOR index := LOWERBOUND (unit_element_p^.physical_descriptor.unit_path^)
            TO UPPERBOUND (unit_element_p^.physical_descriptor.unit_path^) DO
        unit_path := unit_element_p^.physical_descriptor.unit_path^ [index];
        get_controller_physical_address (unit_path, controller);

        IF unit_path = primary_path_element THEN
          CYCLE /unit_path_loop_3/;
        IFEND;

        cmp$locate_element_via_adr (controller, controller_element_p);
        IF (controller_element_p = NIL) OR (controller_element_p^.element_status.state <> cmc$on) THEN
          CYCLE /unit_path_loop_3/;
        IFEND;
        {
        { If the connection status is not active we shouldn't have to check this path,
        { however, if the new_state is cmc$on, the connection status will change before
        { we actually build the PP tables.
        {
        get_connection_status (controller_element_p, unit_element_p^.element_name, connection_status);
        IF (new_state <> cmc$on) AND (connection_status <> cmc$active) THEN
          CYCLE /unit_path_loop_3/;
        IFEND;

        get_channel_physical_address (unit_path, channel);
        cmp$locate_element_via_adr (channel, channel_element_p);
        IF (channel_element_p = NIL) OR (channel_element_p^.element_status.state <> cmc$on) THEN
          CYCLE /unit_path_loop_3/;
        IFEND;

        get_connection_status (channel_element_p, controller_element_p^.element_name, connection_status);
        IF connection_status <> cmc$active THEN
          CYCLE /unit_path_loop_3/;
        IFEND;

        IF f$channel_in_list (channel, redundant_channel_list, number_of_path) THEN
          CYCLE /unit_path_loop_3/;
        IFEND;

        setup_redundant_path_info (channel, number_of_path, pp_available, pp_table_rma_list,
              redundant_path_pp_list, redundant_channel_list);
        IF pp_available THEN
          update_controller_address := TRUE;
          redundant_path_available := TRUE;
          driver_name := channel_element_p^.physical_descriptor.peripheral_driver_name;
        IFEND;
      FOREND /unit_path_loop_3/;

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

    ELSEIF primary_path_element.address_specifier = cmv$hydra_mass_storage_address THEN
      {
      { This should not occur.
      {
    IFEND;
  PROCEND cmp$find_redundant_path;

?? TITLE := ' cmp$get_element_list', EJECT ??

{ PURPOSE:
{   This procedure returns a list of element entries whose upline connection
{   matches the given upline physical address.

  PROCEDURE cmp$get_element_list
    (    upline_path: cmt$physical_address;
         element_type_sought: cmt$element_type;
         element_list_p: ^array [ * ] of ^cmt$peripheral_element_entry);

    VAR
      element_p: ^cmt$peripheral_element_entry,
      list_index: integer,
      path_index: integer,
      table_index: integer;

    IF element_list_p = NIL THEN
      RETURN;
    IFEND;

    FOR list_index := LOWERBOUND (element_list_p^) TO UPPERBOUND (element_list_p^) DO
      element_list_p^ [list_index] := NIL;
    FOREND;
    list_index := LOWERBOUND (element_list_p^);

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

      IF element_p^.physical_descriptor.element_type <> element_type_sought THEN
        CYCLE /scan_table/;
      IFEND;

      CASE element_type_sought OF
      = cmc$storage_device_element =
        FOR path_index := LOWERBOUND (element_p^.physical_descriptor.unit_path^)
              TO UPPERBOUND (element_p^.physical_descriptor.unit_path^) DO
          IF (element_p^.physical_descriptor.unit_path^ [path_index].iou = upline_path.iou) AND
                (element_p^.physical_descriptor.unit_path^ [path_index].channel = upline_path.channel) AND
                (element_p^.physical_descriptor.unit_path^ [path_index].channel_address =
                upline_path.channel_address) THEN
            element_list_p^ [list_index] := element_p;
            IF list_index < UPPERBOUND (element_list_p^) THEN
              list_index := list_index + 1;
            IFEND;
            CYCLE /scan_table/;
          IFEND;
        FOREND;

      = cmc$controller_element =
        FOR path_index := LOWERBOUND (element_p^.physical_descriptor.equipment_path^)
              TO UPPERBOUND (element_p^.physical_descriptor.equipment_path^) DO
          IF (element_p^.physical_descriptor.equipment_path^ [path_index].iou = upline_path.iou) AND
                (element_p^.physical_descriptor.equipment_path^ [path_index].channel =
                upline_path.channel) THEN
            element_list_p^ [list_index] := element_p;
            IF list_index < UPPERBOUND (element_list_p^) THEN
              list_index := list_index + 1;
            IFEND;
            CYCLE /scan_table/;
          IFEND;
        FOREND;
      ELSE
        ;
      CASEND;
    FOREND /scan_table/;
  PROCEND cmp$get_element_list;

?? TITLE := ' get_logical_pp', EJECT ??

{ PURPOSE:
{   This procedure retrieves the logical PP number associated with a channel.

  PROCEDURE get_logical_pp
    (    channel: cmt$physical_address;
     VAR logical_pp: iot$pp_number;
     VAR found: boolean);

    VAR
      channel_resource: dst$iou_resource;

    found := FALSE;
    channel_resource.iou_number := channel.iou;
    IF channel.channel.concurrent THEN
      channel_resource.channel_protocol := dsc$cpt_cio;
    ELSE
      channel_resource.channel_protocol := dsc$cpt_nio;
    IFEND;
    channel_resource.number := channel.channel.number;

    FOR logical_pp := LOWERBOUND (cmv$logical_pp_table_p^) TO UPPERBOUND (cmv$logical_pp_table_p^) DO
      IF cmv$logical_pp_table_p^ [logical_pp].pp_info.channel = channel_resource THEN
        found := TRUE;
        RETURN;
      IFEND;
    FOREND;

  PROCEND get_logical_pp;

?? TITLE := ' cmp$locate_element_via_adr', EJECT ??

{ PURPOSE:
{     This procedure will search the peripheral element table for the
{     specified physical address and return a pointer to the element if it is found.
{     If the element is not found a NIL pointer will be returned.
{

  PROCEDURE [XDCL] cmp$locate_element_via_adr
    (    physical_address: cmt$physical_address;
     VAR peripheral_element_p: ^cmt$peripheral_element_entry);

    VAR
      current_path: cmt$physical_address,
      i: integer,
      table_index: integer;

    peripheral_element_p := NIL;
    IF cmv$peripheral_element_table.pointer = NIL THEN
      RETURN;
    IFEND;

  /pet_loop/
    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 /pet_loop/;
      IFEND;

      peripheral_element_p := ^cmv$peripheral_element_table.pointer^ [table_index];

      IF physical_address.address_specifier = cmv$data_channel_address THEN
        IF peripheral_element_p^.physical_descriptor.element_type <> cmc$data_channel_element THEN
          CYCLE /pet_loop/;
        IFEND;

        IF (peripheral_element_p^.physical_descriptor.channel_path.channel = physical_address.channel) AND
              (peripheral_element_p^.physical_descriptor.channel_path.iou = physical_address.iou) THEN
          RETURN;
        IFEND;

      ELSEIF physical_address.address_specifier = cmv$controller_address THEN
        IF (peripheral_element_p^.physical_descriptor.element_type <> cmc$controller_element) AND
              (peripheral_element_p^.physical_descriptor.element_type <> cmc$external_processor_element) AND
              (peripheral_element_p^.physical_descriptor.element_type <> cmc$channel_adapter_element) AND
              (peripheral_element_p^.physical_descriptor.element_type <> cmc$communications_element) THEN
          CYCLE /pet_loop/;
        IFEND;

        FOR i := LOWERBOUND (peripheral_element_p^.physical_descriptor.equipment_path^)
              TO UPPERBOUND (peripheral_element_p^.physical_descriptor.equipment_path^) DO
          current_path := peripheral_element_p^.physical_descriptor.equipment_path^ [i];
          IF (current_path.iou = physical_address.iou) AND
                (current_path.channel = physical_address.channel) AND
                (current_path.channel_address = physical_address.channel_address) THEN
            RETURN;
          IFEND;
        FOREND;

      ELSEIF physical_address.address_specifier = cmv$mass_storage_address THEN
        IF peripheral_element_p^.physical_descriptor.element_type <> cmc$storage_device_element THEN
          CYCLE /pet_loop/;
        IFEND;

        FOR i := LOWERBOUND (peripheral_element_p^.physical_descriptor.unit_path^) TO
              UPPERBOUND (peripheral_element_p^.physical_descriptor.unit_path^) DO
          current_path := peripheral_element_p^.physical_descriptor.unit_path^ [i];
          IF (current_path.iou = physical_address.iou) AND
                (current_path.channel = physical_address.channel) AND
                (current_path.channel_address = physical_address.channel_address) AND
                (current_path.unit_address = physical_address.unit_address) THEN
            RETURN;
          IFEND;
        FOREND;

      ELSEIF physical_address.address_specifier = cmv$hydra_mass_storage_address THEN
        IF peripheral_element_p^.physical_descriptor.element_type <> cmc$storage_device_element THEN
          CYCLE /pet_loop/;
        IFEND;

        FOR i := LOWERBOUND (peripheral_element_p^.physical_descriptor.unit_path^) TO
              UPPERBOUND (peripheral_element_p^.physical_descriptor.unit_path^) DO
          current_path := peripheral_element_p^.physical_descriptor.unit_path^ [i];
          IF (current_path.iou = physical_address.iou) AND
                (current_path.channel = physical_address.channel) AND
                (current_path.unit_address = physical_address.unit_address) THEN
            RETURN;
          IFEND;
        FOREND;
      IFEND;
    FOREND /pet_loop/;

    peripheral_element_p := NIL;
    IF cmv$debug = 07(16) THEN
      mtp$error_stop ('Debug stop 07(16) cmp$locate_element_via_adr.');
    IFEND;

  PROCEND cmp$locate_element_via_adr;

?? TITLE := ' cmp$locate_element_via_lun', EJECT ??

{ PURPOSE:
{     This procedure will search the peripheral element table for the
{     specified logical unit number and return a pointer to the element if it
{     is found.  If the element is not found a NIL pointer will be returned.
{

  PROCEDURE [XDCL] cmp$locate_element_via_lun
    (    logical_unit_number: iot$logical_unit;
     VAR element_p: ^cmt$peripheral_element_entry);

    VAR
      pet_index: integer;

    element_p := NIL;
    IF cmv$peripheral_element_table.pointer = NIL THEN
      RETURN;
    IFEND;

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

      IF element_p^.physical_descriptor.element_type <> cmc$storage_device_element THEN
        CYCLE /lun_loop/;
      IFEND;

      IF element_p^.logical_unit_number = logical_unit_number THEN
        RETURN;
      IFEND;
    FOREND /lun_loop/;
    {
    {Logical unit number not found.
    {
    element_p := NIL;

  PROCEND cmp$locate_element_via_lun;

?? TITLE := ' cmp$locate_element_via_name', EJECT ??

{ PURPOSE:
{     This procedure will search the peripheral element table for the
{     specified element and return a pointer to the element if it is found.
{     If the element is not found a NIL pointer will be returned.
{     The IOU_NUMBER parameter is used only when the specified element is
{     a data channel element.

  PROCEDURE [XDCL] cmp$locate_element_via_name
    (    element_name: cmt$element_name;
         iou_number: dst$iou_number;
     VAR element_p: ^cmt$peripheral_element_entry);

    VAR
      pet_index: integer;

    element_p := NIL;
    IF cmv$peripheral_element_table.pointer = NIL THEN
      RETURN;
    IFEND;

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

      IF element_p^.element_name = element_name THEN
        IF element_p^.physical_descriptor.element_type = cmc$data_channel_element THEN
          IF element_p^.physical_descriptor.channel_path.iou <> iou_number THEN
            CYCLE /pet_loop/;
          IFEND;
        IFEND;
        RETURN;
      IFEND;

    FOREND /pet_loop/;
    {
    { Element name not found.
    {
    element_p := NIL;

  PROCEND cmp$locate_element_via_name;

?? TITLE := ' cmp$select_primary_controller', EJECT ??

{ PURPOSE:
{   This procedure uses information in the peripheral element table to determine
{   the correct controller to configure (if any) and if the unit is active on the
{   specified channel.


  PROCEDURE [XDCL] cmp$select_primary_controller
    (    pp: iot$pp_number;
         logical_unit_number: iot$logical_unit;
     VAR controller_p: ^cmt$peripheral_element_entry;
     VAR channel_p: ^cmt$peripheral_element_entry;
     VAR redundant_path: boolean);

    VAR
      alternate_access_controller_p: ^cmt$peripheral_element_entry,
      channel: cmt$physical_address,
      channel_element_p: ^cmt$peripheral_element_entry,
      connection_status: cmt$connection_status,
      controller: cmt$physical_address,
      controller_element_p: ^cmt$peripheral_element_entry,
      path_index: integer,
      pp_channel: cmt$physical_address,
      primary_channels_equal: boolean,
      primary_channel_p: ^cmt$peripheral_element_entry,
      primary_controller: cmt$physical_address,
      primary_controller_found: boolean,
      primary_controller_p: ^cmt$peripheral_element_entry,
      storage_device_element_p: ^cmt$peripheral_element_entry,
      stored_primary_channel: cmt$physical_address,
      unit_path: cmt$physical_address,
      unit_p: ^cmt$peripheral_element_entry;

    alternate_access_controller_p := NIL;
    controller_p := NIL;
    channel_p := NIL;
    primary_channels_equal := FALSE;
    primary_controller_found := FALSE;
    redundant_path := FALSE;

    pp_channel.address_specifier := cmv$data_channel_address;
    pp_channel.iou := cmv$logical_pp_table_p^ [pp].pp_info.channel.iou_number;
    pp_channel.channel.number := cmv$logical_pp_table_p^ [pp].pp_info.channel.number;
    pp_channel.channel.port := cmv$logical_pp_table_p^ [pp].pp_info.channel_port;
    pp_channel.channel.concurrent :=
          (cmv$logical_pp_table_p^ [pp].pp_info.channel.channel_protocol = dsc$cpt_cio);
    pp_channel.channel_address := 0;
    pp_channel.unit_address := 0;

    IF pp_channel.channel.concurrent THEN
      IF cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p^.unit_descriptors [logical_unit_number].
            physical_path.port = 0 THEN
        pp_channel.channel.port := cmc$port_a;
      ELSE
        pp_channel.channel.port := cmc$port_b;
      IFEND;
    IFEND;

    cmp$locate_element_via_lun (logical_unit_number, unit_p);
    IF unit_p = NIL THEN
      RETURN;
    IFEND;

    IF unit_p^.product_id.product_number = '  $887' THEN
      RETURN;
    ELSEIF unit_p^.product_id.product_number = '  $885' THEN
      select_7155_controller(pp, unit_p, controller_p, redundant_path);
      RETURN;
    IFEND;

    path_index := LOWERBOUND (unit_p^.physical_descriptor.unit_path^);

  /find_primary_path/
    WHILE path_index <= UPPERBOUND (unit_p^.physical_descriptor.unit_path^) DO
      unit_path := unit_p^.physical_descriptor.unit_path^ [path_index];
      path_index := path_index + 1;

      get_controller_physical_address (unit_path, controller);
      cmp$locate_element_via_adr (controller, primary_controller_p);
      IF (primary_controller_p = NIL) OR (primary_controller_p^.element_status.state <> cmc$on) THEN
        CYCLE /find_primary_path/;
      IFEND;
      {
      { Check the controller/unit connection status.
      {
      get_connection_status (primary_controller_p, unit_p^.element_name, connection_status);
      IF connection_status <> cmc$active THEN
        CYCLE /find_primary_path/;
      IFEND;

      get_channel_physical_address (unit_path, channel);
      cmp$locate_element_via_adr (channel, primary_channel_p);
      IF (primary_channel_p = NIL) OR (primary_channel_p^.element_status.state <> cmc$on) THEN
        CYCLE /find_primary_path/;
      IFEND;
      {
      { Check the channel/controller connection status.
      {
      get_connection_status (primary_channel_p, primary_controller_p^.element_name, connection_status);
      IF connection_status <> cmc$active THEN
        CYCLE /find_primary_path/;
      IFEND;

      primary_controller_found := TRUE;
      primary_controller := controller;
      stored_primary_channel := channel;
      primary_channels_equal := TRUE;

      IF channel = pp_channel THEN
        alternate_access_controller_p := primary_controller_p;
      IFEND;

      EXIT /find_primary_path/;
    WHILEND /find_primary_path/;

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

    IF NOT primary_controller_found THEN
      RETURN;
    IFEND;

    IF (primary_controller_p^.product_id.product_number = ' $5831') OR
       (primary_controller_p^.product_id.product_number = ' $NTDC') THEN
      {
      { For DAS devices we must determine if it is alternate or redundant access.
      { If redundant return the primary controller.
      { If alternate return the controller with this channel configured as its
      { primary controller.
      {

    /find_secondary_path/
      WHILE path_index <= UPPERBOUND (unit_p^.physical_descriptor.unit_path^) DO
        unit_path := unit_p^.physical_descriptor.unit_path^ [path_index];
        path_index := path_index + 1;
        IF unit_path.channel_address = primary_controller.channel_address THEN
          CYCLE /find_secondary_path/;
        IFEND;

        get_controller_physical_address (unit_path, controller);
        cmp$locate_element_via_adr (controller, controller_element_p);
        IF (controller_element_p = NIL) OR (controller_element_p^.element_status.state <> cmc$on) THEN
          CYCLE /find_secondary_path/;
        IFEND;
        {
        { Check the controller/unit connection status.
        {
        get_connection_status (controller_element_p, unit_p^.element_name, connection_status);
        IF connection_status <> cmc$active THEN
          CYCLE /find_secondary_path/;
        IFEND;

        get_channel_physical_address (unit_path, channel);
        cmp$locate_element_via_adr (channel, channel_element_p);
        IF (channel_element_p = NIL) OR (channel_element_p^.element_status.state <> cmc$on) THEN
          CYCLE /find_secondary_path/;
        IFEND;
        {
        { Check the channel/controller connection status.
        {
        get_connection_status (channel_element_p, controller_element_p^.element_name, connection_status);
        IF connection_status <> cmc$active THEN
          CYCLE /find_secondary_path/;
        IFEND;

        primary_channels_equal := (channel = stored_primary_channel);
        IF channel = pp_channel THEN
          alternate_access_controller_p := controller_element_p;
        IFEND;

        EXIT /find_secondary_path/;
      WHILEND /find_secondary_path/;
    IFEND;

    IF primary_channels_equal THEN
      controller_p := primary_controller_p;
      channel_p := primary_channel_p;
      redundant_path := (pp_channel <> stored_primary_channel);
    ELSE
      controller_p := alternate_access_controller_p;
    IFEND;

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

  PROCEND cmp$select_primary_controller;

?? TITLE := ' cmp$verify_active_path_exists  ', EJECT ??

{ PURPOSE:
{   This procedure will determine if an active path for the element exists.
{   For a channel, if any active downline connection exists, TRUE will be returned.
{   For a controller, if either all downline connections are inactive or all upline
{   connections are inactive FALSE will be returned.
{   For a mass storage device if any upline connection is active TRUE will be returned.
{

  PROCEDURE [XDCL] cmp$verify_active_path_exists
    (    physical_address: cmt$physical_address;
     VAR active_path_exists: boolean);

    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,
      j: integer,
      unit_element_p: ^cmt$peripheral_element_entry;

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

    active_path_exists := FALSE;

    IF physical_address.address_specifier = cmv$data_channel_address THEN
      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
          active_path_exists := TRUE;
          RETURN;
        IFEND;
      FOREND

    ELSEIF physical_address.address_specifier = cmv$controller_address THEN
      cmp$locate_element_via_adr (physical_address, controller_element_p);
      IF controller_element_p = NIL THEN
        RETURN;
      IFEND;

    /search_downline_connections/
      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
          active_path_exists := TRUE;
          EXIT /search_downline_connections/;
        IFEND;
      FOREND /search_downline_connections/;

      IF NOT active_path_exists THEN
        RETURN;
      IFEND;
      active_path_exists := FALSE;

      FOR i := LOWERBOUND (controller_element_p^.physical_descriptor.equipment_path^)
            TO UPPERBOUND (controller_element_p^.physical_descriptor.equipment_path^) DO
        get_channel_physical_address (controller_element_p^.physical_descriptor.equipment_path^ [i], channel);
        cmp$locate_element_via_adr (channel, channel_element_p);
        IF channel_element_p = NIL THEN
          RETURN;
        IFEND;

        FOR j := 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^ [j].downline_element =
                controller_element_p^.element_name THEN
            IF channel_element_p^.physical_descriptor.channel_connection^ [j].status = cmc$active THEN
              active_path_exists := TRUE;
              RETURN;
            IFEND;
          IFEND;
        FOREND;
      FOREND;

    ELSEIF physical_address.address_specifier = cmv$mass_storage_address THEN
      cmp$locate_element_via_adr (physical_address, unit_element_p);
      IF unit_element_p = NIL THEN
        RETURN;
      IFEND;

      FOR i := LOWERBOUND (unit_element_p^.physical_descriptor.unit_path^)
            TO UPPERBOUND (unit_element_p^.physical_descriptor.unit_path^) DO
        get_controller_physical_address (unit_element_p^.physical_descriptor.unit_path^ [i], controller);
        cmp$locate_element_via_adr (controller, controller_element_p);
        IF controller_element_p = NIL THEN
          RETURN;
        IFEND;

        FOR j := 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^ [j].downline_element =
                unit_element_p^.element_name THEN
            IF controller_element_p^.physical_descriptor.equipment_connection^ [j].status = cmc$active THEN
              active_path_exists := TRUE;
              RETURN;
            IFEND;
          IFEND;
        FOREND;
      FOREND;

    ELSEIF physical_address.address_specifier = cmv$hydra_mass_storage_address THEN
      cmp$locate_element_via_adr (physical_address, unit_element_p);
      IF unit_element_p = NIL THEN
        RETURN;
      IFEND;

      FOR i := LOWERBOUND (unit_element_p^.physical_descriptor.unit_path^)
            TO UPPERBOUND (unit_element_p^.physical_descriptor.unit_path^) DO
        get_channel_physical_address (unit_element_p^.physical_descriptor.unit_path^ [i], channel);
        cmp$locate_element_via_adr (channel, channel_element_p);
        IF channel_element_p = NIL THEN
          RETURN;
        IFEND;

        FOR j := 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^ [j].downline_element =
                unit_element_p^.element_name THEN
            IF channel_element_p^.physical_descriptor.channel_connection^ [j].status = cmc$active THEN
              active_path_exists := TRUE;
              RETURN;
            IFEND;
          IFEND;
        FOREND;
      FOREND;
    IFEND;

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

  PROCEND cmp$verify_active_path_exists;

?? TITLE := ' 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;

?? TITLE := ' get_connection_status', EJECT ??

{ PURPOSE:
{   This procedure returns the status of a connection between two elements.
{

  PROCEDURE get_connection_status
    (    upline_element_p: ^cmt$peripheral_element_entry;
         downline_element: cmt$element_name;
     VAR connection_status: cmt$connection_status);

    VAR
      i: integer;

    connection_status := cmc$inactive;
    IF NOT upline_element_p^.physical_descriptor.configured THEN
      RETURN;
    IFEND;

    CASE upline_element_p^.physical_descriptor.element_type OF
    = cmc$data_channel_element =
      FOR i := LOWERBOUND (upline_element_p^.physical_descriptor.channel_connection^)
            TO UPPERBOUND (upline_element_p^.physical_descriptor.channel_connection^) DO
        IF upline_element_p^.physical_descriptor.channel_connection^ [i].downline_element =
              downline_element THEN
          connection_status := upline_element_p^.physical_descriptor.channel_connection^ [i].status;
          RETURN;
        IFEND;
      FOREND;

    = cmc$controller_element, cmc$channel_adapter_element =
      FOR i := LOWERBOUND (upline_element_p^.physical_descriptor.equipment_connection^)
            TO UPPERBOUND (upline_element_p^.physical_descriptor.equipment_connection^) DO
        IF upline_element_p^.physical_descriptor.equipment_connection^ [i].downline_element =
              downline_element THEN
          connection_status := upline_element_p^.physical_descriptor.equipment_connection^ [i].status;
          RETURN;
        IFEND;
      FOREND;
    ELSE
    CASEND;

  PROCEND get_connection_status;

?? TITLE := ' 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;

?? TITLE := ' select_7155_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 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
    (    pp: iot$pp_number;
         unit_p: ^cmt$peripheral_element_entry;
     VAR controller_p: ^cmt$peripheral_element_entry;
     VAR redundant_path: boolean);

    VAR
      channel: cmt$physical_address,
      channel_element_p: ^cmt$peripheral_element_entry,
      channel_found: boolean,
      connection_status: cmt$connection_status,
      controller: cmt$physical_address,
      controller_element_p: ^cmt$peripheral_element_entry,
      path_index: integer,
      pp_channel: cmt$physical_address,
      primary_channel: cmt$physical_address,
      primary_channel_found: boolean,
      primary_controller_p: ^cmt$peripheral_element_entry,
      storage_device_element_p: ^cmt$peripheral_element_entry,
      stored_primary_channel: cmt$physical_address,
      unit_path: cmt$physical_address;

    channel_found := FALSE;
    controller_p := NIL;
    primary_channel_found := FALSE;
    primary_controller_p := NIL;
    redundant_path := FALSE;

    pp_channel.address_specifier := cmv$data_channel_address;
    pp_channel.iou := cmv$logical_pp_table_p^ [pp].pp_info.channel.iou_number;
    pp_channel.channel.number := cmv$logical_pp_table_p^ [pp].pp_info.channel.number;
    pp_channel.channel.port := cmv$logical_pp_table_p^ [pp].pp_info.channel_port;
    pp_channel.channel.concurrent :=
          (cmv$logical_pp_table_p^ [pp].pp_info.channel.channel_protocol = dsc$cpt_cio);
    pp_channel.channel_address := 0;
    pp_channel.unit_address := 0;
    IF unit_p = NIL THEN
      RETURN;
    IFEND;

    path_index := LOWERBOUND (unit_p^.physical_descriptor.unit_path^);

  /find_primary_path/
    WHILE path_index <= UPPERBOUND (unit_p^.physical_descriptor.unit_path^) DO
      unit_path := unit_p^.physical_descriptor.unit_path^ [path_index];
      path_index := path_index + 1;

      get_controller_physical_address (unit_path, controller);
      cmp$locate_element_via_adr (controller, controller_element_p);
      IF (controller_element_p = NIL) OR (controller_element_p^.element_status.state <> cmc$on) THEN
        CYCLE /find_primary_path/;
      IFEND;
      {
      { Check the controller/unit connection status.
      {
      get_connection_status (controller_element_p, unit_p^.element_name, connection_status);
      IF connection_status <> cmc$active THEN
        CYCLE /find_primary_path/;
      IFEND;

      get_channel_physical_address (unit_path, channel);
      cmp$locate_element_via_adr (channel, channel_element_p);
      IF (channel_element_p = NIL) OR (channel_element_p^.element_status.state <> cmc$on) THEN
        CYCLE /find_primary_path/;
      IFEND;
      {
      { Check the channel/controller connection status.
      {
      get_connection_status (channel_element_p, controller_element_p^.element_name, connection_status);
      IF connection_status <> cmc$active THEN
        CYCLE /find_primary_path/;
      IFEND;

      IF NOT primary_channel_found THEN
        primary_channel_found := TRUE;
        primary_channel := channel;
        primary_controller_p := controller_element_p;
      IFEND;

      IF controller_element_p^.element_name <> primary_controller_p^.element_name THEN
        primary_channel := channel;
        primary_controller_p := controller_element_p;
      IFEND;

      IF channel = pp_channel THEN
        channel_found := TRUE;
        EXIT /find_primary_path/;
      IFEND;
    WHILEND /find_primary_path/;

    IF channel_found THEN
      controller_p := controller_element_p;
      redundant_path := (pp_channel <> primary_channel);
    IFEND;

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

  PROCEND select_7155_controller;

?? TITLE := ' setup_redundant_path_info', EJECT ??

{ PURPOSE:
{   This procedure sets up information about a redundant path.

  PROCEDURE setup_redundant_path_info
    (    channel: cmt$physical_address;
     VAR number_of_path: integer;
     VAR pp_available: boolean;
     VAR pp_table_rma_list: array [cmt$physical_equipment_number] of ost$real_memory_address;
     VAR redundant_path_pp_list: array [cmt$physical_equipment_number] of iot$pp_number;
     VAR redundant_channel_list: array [cmt$physical_equipment_number] of cmt$physical_address);

    VAR
      found_pp: boolean,
      logical_pp: iot$pp_number;

    pp_available := FALSE;
    get_logical_pp (channel, logical_pp, found_pp);
    IF found_pp AND cmv$logical_pp_table_p^ [logical_pp].flags.pp_loaded THEN
      pp_available := TRUE;
      number_of_path := number_of_path + 1;
      pp_table_rma_list [number_of_path] :=
            cmv$logical_pp_table_p^ [logical_pp].pp_info.pp_interface_table_rma;
      redundant_path_pp_list [number_of_path] := logical_pp;
      redundant_channel_list [number_of_path] := channel;
    IFEND;
  PROCEND setup_redundant_path_info;

MODEND cmm$monitor_job_mode_interfaces;
