?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Network Access : Intranet Layer Management R3' ??
?? NEWTITLE := 'Global Declarations Referenced by This Module' ??
MODULE nam$intranet_layer_mgmt_r3;

{
{ PURPOSE:
{         The purpose of this module is to provide support in ring 3 for INTRANET
{         LAYER MGMT FUNCTIONS.
{
{ DESIGN:
{         This module is designed to reside in OSF$JOB_TEMPLATE_23D library.
{         It contains code that runs in the system job except for the i/f to
{         change ica state which can also be invoked in a user task. It contains
{         the procedures that execute in the intranet layer mgmt task.
{

?? PUSH (LISTEXT := ON) ??
*copyc cme$reserve_element
*copyc cme$physical_configuration_mgr
*copyc cml$ica_failure_data
*copyc cml$ica_usage_data
*copyc cml$ivb_failure_data
*copyc cml$ivb_usage_data
*copyc cml$mdi_failure_data
*copyc cml$mdi_usage_data
*copyc cmt$element_definition
*copyc cmt$element_descriptor
*copyc cmt$hardware_address
*copyc cmt$physical_equipment_number
*copyc cmt$physical_identification
*copyc cmt$pp_program_description
*copyc cmt$pp_identification
*copyc cmt$element_reservation
*copyc ioc$unsolicited_response_codes
*copyc nae$ica_conditions
*copyc nat$ica_log_msg_constants
*copyc nat$ivb_log_msg_constants
*copyc nat$mdi_log_msg_constants
*copyc nat$ica_pp_log_response
*copyc nat$ivb_pp_log_response
*copyc nat$mdi_pp_log_response
*copyc nat$network_driver_response
*copyc nat$device_type
*copyc nat$request_block_list
*copyc nlc$bm_small_buffer_size
*copyc nlt$network_device
*copyc nlt$network_device_list
*copyc ost$global_task_id
*copyc pmt$program_name
*copyc pmt$program_parameters
?? POP ??
*copyc cmp$convert_channel_ordinal
*copyc cmp$get_element_type
*copyc pmp$get_mainframe_id
*copyc cmp$search_pp_table
*copyc cmp$convert_pp_ordinal
*copyc cmp$convert_iou_name
*copyc cmp$execute_pp_program
*copyc cmp$get_logical_unit_number
*copyc cmp$pc_get_element
*copyc cmp$process_state_change
*copyc cmp$reserve_element
*copyc cmp$release_element
*copyc cmp$return_desc_data_by_lun_lpn
*copyc dpp$put_critical_message
*copyc nap$build_master_control_table
*copyc nap$compute_ethernet_checksum
*copyc nap$display_message
*copyc nap$flush_unit_queue
*copyc nap$idle_pp
*copyc nap$issue_pp_request
*copyc nap$namve_system_error
*copyc nlp$bm_release_messages
*copyc nlp$cc_terminate_connections
*copyc nlp$get_exclusive_access
*copyc nlp$get_nonexclusive_access
*copyc nlp$release_exclusive_access
*copyc nlp$release_nonexclusive_access
*copyc osp$append_status_integer
*copyc osp$set_status_abnormal
*copyc pmp$get_executing_task_gtid
*copyc pmp$ready_task
*copyc pmp$wait
*copyc sfp$emit_statistic
*copyc tmp$save_system_task_id
*copyc jmv$executing_within_system_job
*copyc nav$ica_reset_down_threshold
*copyc nav$intranet_layer_mgmt_taskid
*copyc nav$intranet_mgmt_timer_active
*copyc nav$intranet_mgmt_work_list
*copyc nav$mci_reset_down_threshold
*copyc nav$namve_active
*copyc nav$network_paged_heap
*copyc nlv$bm_large_buffer_size
*copyc nlv$configured_network_devices
*copyc nlv$device_usage_data
*copyc osv$page_size
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  CONST
    null_counter = -1;

  TYPE
    channel_counter = packed record
      channel_error_status: 0 .. 0ffff(16),
      fill: 0 .. 3fffffff(16),
      iou: 0 .. 3f(16),
      fill1: 0 .. 1f(16),
      concurrent: boolean,
      fill2: 0 .. 1,
      channel: ost$physical_channel_number,
    recend,

    network_device_log_reason = (device_reset_down_thresh_exceed, device_reset_timeout),

    operational_device_attributes = record
      channel_interface_protocol: nlt$channel_interface_protocol,
      fill: 0 .. 0ffff(16),
      maximum_pdu_size: nlt$cc_pdu_size,
      system_id: nat$system_identifier,
    recend,

    pp_counter = packed record
      fill: 0 .. 3fffffffffff(16),
      iou: 0 .. 3f(16),
      fill1: 0 .. 1f(16),
      concurrent: boolean,
      pp: 0 .. 03f(16),
    recend;


  CONST
    max_ica_2_down_time = 60000000, { 60 seconds in micro sec }
    log_msg_interval = 30 * 60 * 1000000, { 30 min in micro sec }
    max_symptom_length = 80,
    ten_min = 600000000, { 10 min in micro sec }
    nac$communication_buffer_size = 4096;

?? OLDTITLE ??
?? NEWTITLE := 'nap$change_network_device_state', EJECT ??

  PROCEDURE [XDCL] nap$change_network_device_state
    (    element: cmt$element_name;
         new_state: cmt$element_state;
         old_state: cmt$element_state;
     VAR status: ost$status);

*copyc nah$change_network_device_state

    VAR
      executing_taskid: ost$global_task_id,
      network_device: ^nlt$network_device;

  /main_block/
    BEGIN

      status.normal := TRUE;
      IF NOT nav$namve_active THEN
        RETURN;
      IFEND;

      nlp$get_exclusive_access (nlv$configured_network_devices.access_control);
      get_network_device (element, network_device);
      IF network_device = NIL THEN
        osp$set_status_abnormal (nac$status_id, nae$unknown_element, element, status);
        EXIT /main_block/;
      IFEND;

      pmp$get_executing_task_gtid (executing_taskid);

{      IF (network_device^.state = nlc$state_change_pending) AND
{            (executing_taskid <> nav$intranet_layer_mgmt_taskid) THEN
{        osp$set_status_abnormal (nac$status_id, nae$state_change_in_progress, element, status);
{        EXIT /main_block/;
{      IFEND;

      CASE new_state OF
      = cmc$off, cmc$down =

        IF executing_taskid <> nav$intranet_layer_mgmt_taskid THEN

{ If the task switch is initiated by the INTRANET_MGMT_TASK, it is assumed
{ that the network PP has been idled. When invoked in a user task, the PP is
{ idled and the task waits for the PP queues to be flushed. The intranet layer
{ mgmt task flushes the queues on receipt of the idle response from the PP
{ and readies the waiting user task.

          nap$idle_pp (network_device^.pp_number);
          network_device^.state := nlc$state_change_pending;
          network_device^.task_waiting_for_state_change := executing_taskid;
          network_device^.path_status := nlc$path_unavailable;

          REPEAT
            nlp$release_exclusive_access (nlv$configured_network_devices.access_control);
            pmp$wait (2000, 2000);
            nlp$get_exclusive_access (nlv$configured_network_devices.access_control);
            get_network_device (element, network_device);
            IF network_device = NIL THEN
              nap$namve_system_error (FALSE, 'Network Device deconfigured.', NIL);
            IFEND;
          UNTIL network_device^.task_waiting_for_state_change.index = 0;
        IFEND;

        nap$return_network_elements (network_device^.element, network_device^.channel,
              network_device^.pp_identification);

        network_device^.path_status := nlc$path_down;
        network_device^.logical_unit := 0;

        network_device^.reset_down_count := 0;
        network_device^.reset_down_count_intervl := 0;

      = cmc$on =

        nap$reserve_network_elements (network_device^.element, network_device^.channel,
              network_device^.channel_address, network_device^.driver_name, network_device^.logical_unit,
              network_device^.pp_identification, network_device^.pp_number, status);
        IF status.normal THEN
          nap$build_master_control_table (network_device^.logical_unit, network_device^.device_id);
          nap$initialize_network_pp (network_device);
          network_device^.path_status := nlc$path_unavailable;
        IFEND;

      ELSE
      CASEND;

      IF network_device^.path_status <> nlc$path_down THEN

{ Activate timers.

        network_device^.reset_down_count := 0;
        network_device^.reset_down_count_intervl := #FREE_RUNNING_CLOCK (0) + ten_min;
        network_device^.reset_timestamp := #FREE_RUNNING_CLOCK (0);
        nav$intranet_mgmt_timer_active := TRUE;
      IFEND;
      network_device^.state := nlc$normal;

    END /main_block/;

    nlp$release_exclusive_access (nlv$configured_network_devices.access_control);

  PROCEND nap$change_network_device_state;
?? OLDTITLE ??
?? NEWTITLE := 'nap$initialize_network_pp', EJECT ??

  PROCEDURE [XDCL] nap$initialize_network_pp
    (    network_device: ^nlt$network_device);

{ The purpose of this procedure is to communicate all the required addresses to
{ the network PP. This request is issued after the PP has been deadstarted.

    VAR
      command: iot$command,
      ethernet_address: nlt$ethernet_addr_and_checksum,
      real_memory_address: integer;

    command.flags.store_response := TRUE;

    IF network_device^.kind = nac$ica_2 THEN
      command.flags.indirect_address := TRUE;
      command.command_code := ioc$cc_define_ethernet_address;
      command.length := 0;
      ethernet_address.system_identifier := network_device^.system_id;
      nap$compute_ethernet_checksum (ethernet_address.system_identifier, ethernet_address.checksum);
      nap$issue_pp_request (network_device^.pp_number, command, ^ethernet_address);
    IFEND;

    command.flags.indirect_address := FALSE;
    command.command_code := ioc$cc_resume;
    command.length := 0;
    command.address := 0;
    nap$issue_pp_request (network_device^.pp_number, command, NIL);

  PROCEND nap$initialize_network_pp;
?? OLDTITLE ??
?? NEWTITLE := 'nap$manage_intranet_layer', EJECT ??

  PROCEDURE [XDCL, #GATE] nap$manage_intranet_layer
    (    task_parameters: pmt$program_parameters;
     VAR status: ost$status);

{ This procedure executes in the intranet layer mgmt task. It periodically
{ scans the queue of outstanding PP responses and processes them. In its
{ main loop it also keeps track of the network device reset timers and
{ initiates a state change in case of timer expiration.

    CONST
      long_wait = 30 * 60 * 1000; { 30 min in milli sec }

    VAR
      active_timer_count: integer,
      actual: nat$response_queue_access,
      command: iot$command,
      count: integer,
      cs_status: osc$cs_successful .. osc$cs_variable_locked,
      current_outstanding_response: ^nat$network_driver_response,
      down_timestamp: integer,
      element_access: array [1 .. 1] of cmt$hardware_address,
      element_definition: cmt$element_definition,
      i: integer,
      network_device_list: ^nlt$network_device_list,
      next_network_device: ^nlt$network_device,
      next_outstanding_response: ^nat$network_driver_response,
      new: nat$response_queue_access,
      old: nat$response_queue_access,
      outstanding_response: ^nat$network_driver_response,
      previous_outstanding_response: ^nat$network_driver_response,
      remaining_time: integer,
      program_description: array [1 .. 1] of cmt$pp_program_description,
      wait_time: integer;

    IF NOT jmv$executing_within_system_job THEN
      RETURN;
    IFEND;

    tmp$save_system_task_id (tmc$stid_intranet_layer_mgmt, FALSE, {ignore} status);
    pmp$get_executing_task_gtid (nav$intranet_layer_mgmt_taskid);
    wait_time := long_wait;
    nav$intranet_mgmt_timer_active := TRUE;

{ Activate timers in all the configured network devices.

    nlp$get_nonexclusive_access (nlv$configured_network_devices.access_control);
    nlv$configured_network_devices.next_log_msg_time := #FREE_RUNNING_CLOCK (0) + log_msg_interval;
    network_device_list := nlv$configured_network_devices.network_device_list;
    FOR i := 1 TO UPPERBOUND (network_device_list^) DO
      IF network_device_list^ [i].path_status <> nlc$path_down THEN
        network_device_list^ [i].reset_down_count_intervl := #FREE_RUNNING_CLOCK (0) + ten_min;
        network_device_list^ [i].reset_timestamp := #FREE_RUNNING_CLOCK (0);
      IFEND;
    FOREND;
    nlp$release_nonexclusive_access (nlv$configured_network_devices.access_control);

    WHILE TRUE DO

      IF nav$intranet_mgmt_work_list.outstanding_responses.next_entry <> NIL THEN
        REPEAT

{ Retrieve the entire list of outstanding responses.

          new.sequence := 0;
          new.next_entry := NIL;
          old := new;
          REPEAT
            #COMPARE_SWAP (nav$intranet_mgmt_work_list.outstanding_responses, old, new, actual, cs_status);
            IF cs_status = osc$cs_failed THEN
              old := actual;
              new.sequence := (actual.sequence + 1) MOD 10000(16);
            IFEND;
          UNTIL cs_status = osc$cs_successful;

          outstanding_response := actual.next_entry;
          IF outstanding_response <> NIL THEN

{ Reverse the outstanding response list to process the responses in FIFO order.

            previous_outstanding_response := NIL;
            current_outstanding_response := outstanding_response;
            WHILE current_outstanding_response <> NIL DO
              next_outstanding_response := current_outstanding_response^.next_entry;
              current_outstanding_response^.next_entry := previous_outstanding_response;
              previous_outstanding_response := current_outstanding_response;
              current_outstanding_response := next_outstanding_response;
            WHILEND;
            outstanding_response := previous_outstanding_response;
          IFEND;

          WHILE outstanding_response <> NIL DO

            CASE outstanding_response^.pp_response.response_code.primary_response OF
            = ioc$unsolicited_response =
              process_unsolicited_response (outstanding_response);

            = ioc$normal_response, ioc$abnormal_response =
              process_solicited_response (outstanding_response);

            ELSE
            CASEND;

            next_outstanding_response := outstanding_response^.next_entry;

{ Insert the processed response into the free queue.

            new.sequence := 0;
            new.next_entry := outstanding_response;
            old := new;
            REPEAT
              #COMPARE_SWAP (nav$intranet_mgmt_work_list.free_responses, old, new, actual, cs_status);
              IF cs_status = osc$cs_failed THEN
                old := actual;
                new.sequence := (actual.sequence + 1) MOD 10000(16);
                outstanding_response^.next_entry := actual.next_entry;
              IFEND;
            UNTIL cs_status = osc$cs_successful;

            outstanding_response := next_outstanding_response;
          WHILEND;

        UNTIL nav$intranet_mgmt_work_list.outstanding_responses.next_entry = NIL;

      IFEND;

      wait_time := long_wait;
      IF nav$intranet_mgmt_timer_active THEN

{ Check for expired timers.

        active_timer_count := 0;
        nlp$get_nonexclusive_access (nlv$configured_network_devices.access_control);
        network_device_list := nlv$configured_network_devices.network_device_list;
        FOR i := 1 TO UPPERBOUND (network_device_list^) DO
          IF (network_device_list^ [i].path_status = nlc$path_unavailable) AND
                (network_device_list^ [i].state <> nlc$state_change_pending) AND
                (network_device_list^ [i].kind = nac$ica_2) THEN
            down_timestamp := network_device_list^ [i].reset_timestamp + max_ica_2_down_time;

            remaining_time := (down_timestamp - #FREE_RUNNING_CLOCK (0)) DIV 1000;
            IF remaining_time <= 0 THEN

{ Initiate state change.

              osp$set_status_abnormal (nac$status_id, nae$net_device_reset_timeout,
                    network_device_list^ [i].element, status);
              nap$display_message (status);
              network_device_list^ [i].state := nlc$state_change_pending;
              nap$idle_pp (network_device_list^ [i].pp_number);
              log_cpu_message (network_device_list^ [i].kind, network_device_list^ [i].logical_unit,
                    network_device_list^ [i].pp_number, network_device_list^ [i].channel,
                    device_reset_timeout);
            ELSE
              active_timer_count := active_timer_count + 1;
              IF wait_time > remaining_time THEN
                wait_time := remaining_time;
              IFEND;
            IFEND;
          IFEND;

          remaining_time := (nlv$configured_network_devices.next_log_msg_time - #FREE_RUNNING_CLOCK (0)) DIV
                1000;
          IF remaining_time <= 0 THEN
            log_device_usage_statistics;
            nlv$configured_network_devices.next_log_msg_time := #FREE_RUNNING_CLOCK (0) + log_msg_interval;
            remaining_time := log_msg_interval DIV 1000;
          IFEND;

          IF wait_time > remaining_time THEN
            wait_time := remaining_time;
          IFEND;
          active_timer_count := active_timer_count + 1;
        FOREND;

        nlp$release_nonexclusive_access (nlv$configured_network_devices.access_control);
        nav$intranet_mgmt_timer_active := active_timer_count > 0;
      IFEND;

      pmp$wait (wait_time, wait_time);
    WHILEND;

  PROCEND nap$manage_intranet_layer;
?? OLDTITLE ??
?? NEWTITLE := 'nap$reload_network_pp', EJECT ??

  PROCEDURE [XDCL] nap$reload_network_pp
    (    element: cmt$element_name;
     VAR status: ost$status);

*copyc nah$reload_network_pp

    VAR
      element_descriptor: cmt$element_descriptor,
      executing_taskid: ost$global_task_id,
      iou_name: cmt$element_name,
      ignore_status: ost$status,
      network_device: ^nlt$network_device;


    status.normal := TRUE;
    IF NOT nav$namve_active THEN
      RETURN;
    IFEND;

    nlp$get_exclusive_access (nlv$configured_network_devices.access_control);
    get_network_device (element, network_device);
    IF network_device <> NIL THEN
      CASE network_device^.state OF
      = nlc$normal =
        network_device^.path_status := nlc$path_unavailable;
        flush_unit_queue (network_device);

{ Terminate all active connections.

        nlp$release_exclusive_access (nlv$configured_network_devices.access_control);
        nlp$cc_terminate_connections (network_device^.device_id);
        nlp$get_exclusive_access (nlv$configured_network_devices.access_control);
        nap$return_network_elements (network_device^.element, network_device^.channel,
              network_device^.pp_identification);
        network_device^.logical_unit := 0;

        nap$reserve_network_elements (network_device^.element, network_device^.channel,
              network_device^.channel_address, network_device^.driver_name, network_device^.logical_unit,
              network_device^.pp_identification, network_device^.pp_number, status);
        IF status.normal THEN
          nap$build_master_control_table (network_device^.logical_unit, network_device^.device_id);
          nap$initialize_network_pp (network_device);
          network_device^.path_status := nlc$path_unavailable;
          network_device^.reset_down_count := 0;
          network_device^.reset_down_count_intervl := #FREE_RUNNING_CLOCK (0) + ten_min;
          network_device^.reset_timestamp := #FREE_RUNNING_CLOCK (0);
          nav$intranet_mgmt_timer_active := TRUE;
        ELSE
          network_device^.path_status := nlc$path_down;
          network_device^.reset_down_count := 0;
          network_device^.reset_down_count_intervl := 0;
        IFEND;
        nlp$release_exclusive_access (nlv$configured_network_devices.access_control);

      = nlc$state_change_pending =
        network_device^.path_status := nlc$path_unavailable;
        flush_unit_queue (network_device);

{ Terminate all active connections.

        nlp$release_exclusive_access (nlv$configured_network_devices.access_control);
        nlp$cc_terminate_connections (network_device^.device_id);
        nlp$get_exclusive_access (nlv$configured_network_devices.access_control);
        IF network_device^.task_waiting_for_state_change.index <> 0 THEN
          pmp$ready_task (network_device^.task_waiting_for_state_change, ignore_status);
          network_device^.task_waiting_for_state_change.index := 0;
          nlp$release_exclusive_access (nlv$configured_network_devices.access_control);
        ELSE
          cmp$get_element_type (network_device^.element, {not used} iou_name, element_descriptor.element_type,
                ignore_status);
          element_descriptor.peripheral_descriptor.use_logical_identification := TRUE;
          element_descriptor.peripheral_descriptor.element_name := network_device^.element;
          nlp$release_exclusive_access (nlv$configured_network_devices.access_control);
          cmp$process_state_change ({tape_element=} FALSE, TRUE, element_descriptor,
                {System critical element} FALSE, cmc$on, cmc$down, status);
        IFEND;

      = nlc$closed =
        nlp$release_exclusive_access (nlv$configured_network_devices.access_control);
        osp$set_status_abnormal (nac$status_id, nae$device_not_active, network_device^.element, status);
      ELSE
        osp$set_status_abnormal (nac$status_id, nae$unexpected_device_state, network_device^.element,
              ignore_status);
        nap$namve_system_error (TRUE, 'Invalid device state', ^ignore_status);
        nlp$release_exclusive_access (nlv$configured_network_devices.access_control);
      CASEND;
    ELSE
      nlp$release_exclusive_access (nlv$configured_network_devices.access_control);
      osp$set_status_abnormal (nac$status_id, nae$unknown_element, element, status);
    IFEND;

  PROCEND nap$reload_network_pp;
?? OLDTITLE ??
?? NEWTITLE := 'nap$reserve_network_elements', EJECT ??

  PROCEDURE [XDCL] nap$reserve_network_elements
    (    element: cmt$element_name;
         channel: cmt$channel_ordinal;
         channel_address: cmt$physical_equipment_number,
         driver_name: pmt$program_name;
     VAR logical_unit: iot$logical_unit;
     VAR pp_identification: cmt$pp_identification;
     VAR pp_number: iot$pp_number;
     VAR status: ost$status);

{ This purpose of this procedure is to reserve the specified element and a PP
{ and to load and initialize the pp.

    VAR
      channel_name: cmt$element_name,
      physical_pp: dst$iou_resource,
      found: boolean,
      element_access: array [1 .. 1] of cmt$hardware_address,
      element_definition: ^cmt$element_definition,
      iou_name: cmt$element_name,
      mainframe_id: pmt$mainframe_id,
      port: cmt$communications_port_number,
      program_description: array [1 .. 1] of cmt$pp_program_description,
      physical_identification: cmt$physical_identification,
      reserve_element: array [1 .. 3] of cmt$element_reservation;

    status.normal := TRUE;
    cmp$pc_get_element (element, {not used} iou_name, element_definition, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    pmp$get_mainframe_id (mainframe_id, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{   Get channel IOU name from the upline connection information of the element.
{   Also get channel name from upline connection information.

    IF element_definition^.element_type = cmc$communications_element THEN

    /search_iou_loop/
      FOR port := LOWERVALUE (cmt$communications_port_number)
            TO UPPERVALUE (cmt$communications_port_number) DO
        IF (element_definition^.communications_element.connection.port [port].configured) AND
              (element_definition^.communications_element.connection.port [port].mainframe_ownership =
              mainframe_id) THEN
          iou_name := element_definition^.communications_element.connection.port [port].iou;
          channel_name := element_definition^.communications_element.connection.port [port].element_name;
          EXIT /search_iou_loop/;
        IFEND;
      FOREND /search_iou_loop/;
    ELSE {ica}
      iou_name := element_definition^.channel_adapter.connection.channel.iou;
      channel_name := element_definition^.channel_adapter.connection.channel.element_name;
    IFEND;

    status.normal := TRUE;
    reserve_element [1].element_type := element_definition^.element_type;
    reserve_element [1].peripheral_descriptor.use_logical_identification := TRUE;
    reserve_element [1].peripheral_descriptor.element_name := element;
    reserve_element [2].element_type := cmc$data_channel_element;
    reserve_element [2].channel_descriptor.use_logical_identification := TRUE;
    reserve_element [2].channel_descriptor.name := channel_name;
    reserve_element [2].channel_descriptor.iou := iou_name;
    reserve_element [3].element_type := cmc$pp_element;
    reserve_element [3].pp_reservation.selector := cmc$choose_pp_by_channel;
    reserve_element [3].pp_reservation.channel.ordinal := channel;
    reserve_element [3].pp_reservation.channel.iou := iou_name;
    cmp$reserve_element (reserve_element, status);
    IF NOT status.normal THEN
      IF (status.condition = cme$cm_element_not_found) OR (status.condition = cme$element_already_reserved)
            THEN
        nap$namve_system_error (FALSE, 'Unable to reserve network elements.', ^status);
      IFEND;

      RETURN;
    IFEND;

    pp_identification := reserve_element [3].pp_reservation.acquired_pp_identification;

    program_description [1].pp_identification := pp_identification;
    program_description [1].iou_program_name := driver_name;
    program_description [1].pp_program := NIL;
    program_description [1].master_pp := TRUE;
    program_description [1].communication_buffer_length := nac$communication_buffer_size;
    program_description [1].communication_buffer := NIL;
    element_access [1].physical_address_specifier := $cmt$physical_address_specifier
          [cmc$iou, cmc$channel, cmc$channel_address];
    element_access [1].iou := iou_name;
    element_access [1].channel.ordinal := channel;
    element_access [1].channel.iou := iou_name;
    element_access [1].channel_address := channel_address;
    program_description [1].element_access := ^element_access;
    cmp$execute_pp_program (program_description, status);
    IF NOT status.normal THEN

{ Return the reserved elements.

      nap$return_network_elements (element, channel, pp_identification);
      RETURN;
    IFEND;
    cmp$convert_pp_ordinal (pp_identification.ordinal, physical_pp);
    cmp$convert_iou_name (pp_identification.iou, physical_pp.iou_number, status);
    IF NOT status.normal THEN
      nap$return_network_elements (element, channel, pp_identification);
      RETURN;
    IFEND;
    cmp$search_pp_table (physical_pp, pp_number, found, status);
    IF NOT status.normal THEN
      nap$return_network_elements (element, channel, pp_identification);
      RETURN;
    IFEND;
    IF NOT found THEN
      nap$namve_system_error (FALSE, 'Logical PP not found in PP Table.', NIL);
    IFEND;
    cmp$get_logical_unit_number (element, logical_unit, status);
    IF NOT status.normal THEN
      nap$namve_system_error (FALSE, 'Unable to get logical unit id.', ^status);
      RETURN;
    IFEND;

  PROCEND nap$reserve_network_elements;
?? OLDTITLE ??
?? NEWTITLE := 'nap$return_network_elements', EJECT ??

  PROCEDURE [XDCL] nap$return_network_elements
    (    element: cmt$element_name;
         channel: cmt$channel_ordinal;
         pp_identification: cmt$pp_identification);

    VAR
      channel_name: cmt$element_name,
      iou_name: cmt$element_name,
      element_definition: ^cmt$element_definition,
      mainframe_id: pmt$mainframe_id,
      port: cmt$communications_port_number,
      reserved_elements: array [1 .. 3] of cmt$element_reservation,
      local_status: ost$status;

{ ***** WARNING: It is assumed that the PP has been idled by the caller.

    cmp$pc_get_element (element, {not_used} iou_name, element_definition, local_status);
    IF NOT local_status.normal THEN
      nap$namve_system_error (FALSE, 'Unable to get element definition.', ^local_status);
      RETURN;
    IFEND;
    pmp$get_mainframe_id (mainframe_id, local_status);
    IF NOT local_status.normal THEN
      RETURN;
    IFEND;

    IF element_definition^.element_type = cmc$communications_element THEN

    /search_iou_loop/
      FOR port := LOWERVALUE (cmt$communications_port_number)
            TO UPPERVALUE (cmt$communications_port_number) DO
        IF (element_definition^.communications_element.connection.port [port].configured) AND
              (element_definition^.communications_element.connection.port [port].mainframe_ownership =
              mainframe_id) THEN
          iou_name := element_definition^.communications_element.connection.port [port].iou;
          channel_name := element_definition^.communications_element.connection.port [port].element_name;
          EXIT /search_iou_loop/;
        IFEND;
      FOREND /search_iou_loop/;
    ELSE {ica}
      iou_name := element_definition^.channel_adapter.connection.channel.iou;
      channel_name := element_definition^.channel_adapter.connection.channel.element_name;
    IFEND;

    reserved_elements [1].element_type := cmc$data_channel_element;
    reserved_elements [1].channel_descriptor.use_logical_identification := TRUE;
    reserved_elements [1].channel_descriptor.name := channel_name;
    reserved_elements [1].channel_descriptor.iou := iou_name;
    reserved_elements [2].element_type := element_definition^.element_type;
    reserved_elements [2].peripheral_descriptor.use_logical_identification := TRUE;
    reserved_elements [2].peripheral_descriptor.element_name := element;
    reserved_elements [3].element_type := cmc$pp_element;
    reserved_elements [3].pp_reservation.acquired_pp_identification := pp_identification;
    reserved_elements [3].pp_reservation.selector := cmc$choose_pp_by_channel;
    reserved_elements [3].pp_reservation.channel.ordinal := channel;
    reserved_elements [3].pp_reservation.channel.iou := iou_name;
    cmp$release_element (reserved_elements, local_status);
    IF NOT local_status.normal THEN
      nap$namve_system_error (FALSE, 'Unable to return reserved elements.', ^local_status);
    IFEND;

  PROCEND nap$return_network_elements;
?? OLDTITLE ??
?? NEWTITLE := 'analyze_ica_2_detailed_status', EJECT ??

  PROCEDURE analyze_ica_2_detailed_status
    (    error_summary: ^nat$ica_pp_log_response;
         general_status: ^nat$ica_general_status;
         ica_osi_detailed_status: ^nat$ica_osi_detailed_status;
         network_device: ^nlt$network_device;
     VAR symptom_code: integer;
     VAR symptom_descriptor: string (max_symptom_length);
     VAR symptom_descriptor_length: integer);

{ The purpose of this routine is to return the symptom code and
{ descriptive data associated with a particular error, when the
{ device is an ICA-II in osi mode.


    IF error_summary^.detailed_status_included THEN
      IF ica_osi_detailed_status^.error_detail.write_parity_error THEN
        IF error_summary^.channel_error_flag THEN
          symptom_code := nac$sc_ica_iou_output_parity;
          symptom_descriptor := 'IOU OUTPUT PARITY';
        ELSE
          symptom_code := nac$sc_ica_output_ch_parity;
          symptom_descriptor := 'OUTPUT CHANNEL PARITY';
        IFEND;
      ELSEIF ica_osi_detailed_status^.error_detail.channel_active_timeout THEN
        symptom_code := nac$sc_ica_channel_timeout;
        symptom_descriptor := 'CHANNEL TIMEOUT';
      ELSEIF ica_osi_detailed_status^.error_detail.read_truncated_by_pp THEN
        symptom_code := nac$sc_ica_input_truncated;
        symptom_descriptor := 'INPUT TRUNCATED';
      ELSEIF ica_osi_detailed_status^.error_detail.write_overrun THEN
        symptom_code := nac$sc_ica_pp_overrun;
        symptom_descriptor := 'PP OVER RUN';
      ELSEIF ica_osi_detailed_status^.error_detail.write_format_error THEN
        symptom_code := nac$sc_ica_formatted_output_err;
        symptom_descriptor := 'FORMATTED OUTPUT ERROR';
      ELSEIF ica_osi_detailed_status^.error_detail.write_length_error THEN
        symptom_code := nac$sc_ica_output_length_error;
        symptom_descriptor := 'OUTPUT LENGTH ERROR';
      ELSEIF ica_osi_detailed_status^.error_detail.memory_parity_error THEN
        symptom_code := nac$sc_ica_memory_parity_error;
        symptom_descriptor := 'ICA MEMORY PARITY ERROR';
      ELSEIF ica_osi_detailed_status^.error_detail.memory_address_error THEN
        symptom_code := nac$sc_ica_memory_address_error;
        symptom_descriptor := 'ICA MEMORY ADDRESS ERROR';
      IFEND;
    IFEND;
  PROCEND analyze_ica_2_detailed_status;
?? OLDTITLE ??
?? NEWTITLE := 'flush_unit_queue', EJECT ??

  PROCEDURE flush_unit_queue
    (    network_device: ^nlt$network_device);

    VAR
      message_id_array: ^array [1 .. * ] of nlt$bm_message_id;

    nap$flush_unit_queue (network_device, message_id_array);
    IF message_id_array <> NIL THEN
      nlp$bm_release_messages (message_id_array^);
      FREE message_id_array IN nav$network_paged_heap^;
    IFEND;

  PROCEND flush_unit_queue;
?? OLDTITLE ??
?? NEWTITLE := 'get_network_device', EJECT ??

  PROCEDURE [INLINE] get_network_device
    (    element: cmt$element_name;
     VAR network_device: ^nlt$network_device);

{ The purpose of this procedure is to search the network device list for an
{ entry for the given element.  It is assumed that the network device list
{ has been locked by the caller.

    VAR
      i: integer,
      network_device_list: ^nlt$network_device_list;

    network_device := NIL;
    network_device_list := nlv$configured_network_devices.network_device_list;

  /search/
    FOR i := LOWERBOUND (network_device_list^) TO UPPERBOUND (network_device_list^) DO
      IF (network_device_list^ [i].element = element) THEN
        network_device := ^network_device_list^ [i];
        EXIT /search/;
      IFEND;
    FOREND /search/;

  PROCEND get_network_device;
?? OLDTITLE ??
?? NEWTITLE := 'get_ica_symptom_code', EJECT ??

  PROCEDURE get_ica_symptom_code
    (    unsolicited_response: ^nat$network_driver_response;
         network_device: ^nlt$network_device;
     VAR symptom_code: integer;
     VAR symptom_descriptor: string (max_symptom_length);
     VAR symptom_descriptor_length: integer);

{ The purpose of this routine is to return the symptom code and
{ descriptive data associated with a particular error.

    VAR
      detailed_status: ^iot$detailed_status,
      error_summary: ^nat$ica_pp_log_response,
      general_status_contents: 0 .. 0ffff(16),
      general_status: ^nat$ica_general_status,
      ica_detailed_status: ^nat$ica_detailed_status,
      ica_osi_detailed_status: ^nat$ica_osi_detailed_status;

    detailed_status := unsolicited_response^.detailed_status_pointer;
    RESET detailed_status;
    NEXT error_summary IN detailed_status;
    IF error_summary^.detailed_status_included THEN
      IF network_device^.kind = nac$ica_2 THEN
        NEXT ica_osi_detailed_status IN detailed_status;
      ELSE
        NEXT ica_detailed_status IN detailed_status;
      IFEND;
    IFEND;
    general_status_contents := error_summary^.general_status;
    general_status := #LOC (general_status_contents);
    symptom_code := nac$sc_ica_indeterminate;
    symptom_descriptor := 'INDETERMINATE ERROR';

{ **** WARNING
{ The following symptom descriptor length will have to be changed whenever
{ a new symptom descriptor longer that this value is returned from this routine.

    symptom_descriptor_length := 50;

    CASE error_summary^.error_id OF
    = nac$ica_function_timeout =
      IF error_summary^.channel_error_flag THEN
        symptom_code := nac$sc_ica_funct_timeout_cef;
        symptom_descriptor := 'FUNCTION TIMEOUT AND CHANNEL ERROR FLAG';
      ELSE
        symptom_code := nac$sc_ica_funct_timeout;
        symptom_descriptor := 'FUNCTION TIMEOUT';
      IFEND;

    = nac$ica_state_transition_fail =
      symptom_code := nac$sc_ica_state_transition_err;
      symptom_descriptor := 'STATE TRANSITION FAILURE';

    = nac$ica_invalid_state_change =
      symptom_code := nac$sc_ica_invalid_state_trans;
      symptom_descriptor := 'INVALID STATE TRANSITION';

    = nac$ica_gen_status_busy_timeout =
      symptom_code := nac$sc_ica_general_status_busy;
      symptom_descriptor := 'GENERAL STATUS BUSY TIMEOUT';

    = nac$ica_reset_busy_timeout =
      symptom_code := nac$sc_ica_reset_busy;
      symptom_descriptor := 'RESET BUSY TIMEOUT';

    = nac$ica_diagnostic_failure =
      CASE general_status^.symptom_code OF

      = nac$ica_sc_board_failure =
        symptom_code := nac$sc_ica_board_failure;
        symptom_descriptor := 'ICA BOARD FAILURE';

      = nac$ica_sc_no_power =
        symptom_code := nac$sc_ica_no_transiever_power;
        symptom_descriptor := 'NO ETHERNET TRANSEIVER POWER';

      = nac$ica_sc_ethernet_failure =
        symptom_code := nac$sc_ica_transiever_failure;
        symptom_descriptor := 'ETHERNET TRANSEIVER FAILURE';
      ELSE
      CASEND;

    = nac$ica_gen_status_send_timeout =
      symptom_code := nac$sc_ica_no_send_data;
      symptom_descriptor := 'GENERAL STATUS SEND DATA TIMEOUT';

    = nac$ica_gen_status_avai_timeout =
      symptom_code := nac$sc_ica_status_avail_timeout;
      symptom_descriptor := 'GENERAL STATUS AVAILABLE TIMEOUT';

    = nac$ica_gen_status_content_fail =
      CASE error_summary^.error_word1 OF

      = nac$ica_unf_write_gs_failure =
        symptom_code := nac$sc_ica_unf_write_status;
        symptom_descriptor := 'UNFORMATTED WRITE GENERAL STATUS CONTENT FAILURE';

      = nac$ica_for_write_gs_failure =
        symptom_code := nac$sc_ica_f_write_status;
        symptom_descriptor := 'FORMATTED WRITE GENERAL STATUS CONTENT FAILURE';

      = nac$ica_read_gs_failure =
        symptom_code := nac$sc_ica_read_status;
        symptom_descriptor := 'READ GENERAL STATUS CONTENT FAILURE';

      = nac$ica_echo_status_gs_failure =
        symptom_code := nac$sc_ica_echo_status;
        symptom_descriptor := 'ECHO STATUS GENERAL STATUS CONTENT FAILURE';

      ELSE
      CASEND;

    = nac$ica_reset =
      symptom_code := nac$sc_ica_reset_state;
      symptom_descriptor := 'ICA RESET';

    = nac$ica_operational =
      symptom_code := nac$sc_ica_operational_state;
      symptom_descriptor := 'ICA OPERATIONAL';

    = nac$ica_general_status_reject =
      symptom_code := nac$sc_ica_gen_status_reject;
      symptom_descriptor := 'ICA GENERAL STATUS REJECT';

    = nac$ica_indet_output_parity =
      symptom_code := nac$sc_ica_indet_output_parity;
      symptom_descriptor := 'INDETERMINATE OUTPUT PARITY';

    = nac$ica_channel_protocol_error =
      symptom_code := nac$sc_ica_channel_protocol_err;
      symptom_descriptor := 'CHANNEL PROTOCOL NOT SUPPORTED';

    = nac$ica_invalid_flow_control =
      symptom_code := nac$sc_ica_invalid_flow_control;
      symptom_descriptor := 'INVALID FLOW CONTROL';

    = nac$ica_operation_failure =
      IF error_summary^.maximum_size_exceeded THEN
        symptom_code := nac$sc_ica_max_size_exceeded;
        symptom_descriptor := 'MAXIMUM RECORD SIZE EXCEEDED';
      ELSEIF error_summary^.general_status_error THEN
        CASE general_status^.state OF
        = nac$ica_diagnostic_state =
          IF general_status^.channel_error THEN
            IF error_summary^.channel_error_flag THEN
              symptom_code := nac$sc_ica_iou_output_parity;
              symptom_descriptor := 'IOU OUTPUT PARITY';
            ELSE
              symptom_code := nac$sc_ica_output_ch_parity;
              symptom_descriptor := 'OUTPUT CHANNEL PARITY';
            IFEND;
          ELSEIF general_status^.symptom_code > 0 THEN
            CASE general_status^.symptom_code OF
            = nac$ica_sc_board_failure =
              symptom_code := nac$sc_ica_board_failure;
              symptom_descriptor := 'ICA BOARD FAILURE';

            = nac$ica_sc_unex_function =
              symptom_code := nac$sc_ica_unex_xparent_funct;
              symptom_descriptor := 'UNEXPECTED TRANSPARENT FUNCTION';

            = nac$ica_sc_forced_error =
              symptom_code := nac$sc_ica_forced_err_not_det;
              symptom_descriptor := 'FORCED ERROR NOT DETECTED';

            = nac$ica_sc_cif =
              symptom_code := nac$sc_ica_ch_interface_error;
              symptom_descriptor := 'CHANNEL INTERFACE ERROR';
            ELSE
            CASEND;
          ELSEIF error_summary^.message_content_error THEN
            IF error_summary^.operation_kind = nac$ica_op_read_diagnostic_cmd THEN
              symptom_code := nac$sc_ica_read_diag_cmd_cont;
              symptom_descriptor := 'READ DIAGNOSTIC COMMAND MESSAGE CONTENT ERROR';
            ELSE
              symptom_code := nac$sc_ica_read_conf_content;
              symptom_descriptor := 'READ CONFIDENCE TEST MESSAGE CONTENT ERROR';
            IFEND;
          ELSEIF error_summary^.channel_active THEN
            symptom_code := nac$sc_ica_channel_active;
            symptom_descriptor := 'CHANNEL ACTIVE';
          IFEND;

        = nac$ica_idle_state =
          IF general_status^.channel_error THEN
            IF error_summary^.channel_error_flag THEN
              symptom_code := nac$sc_ica_iou_output_parity;
              symptom_descriptor := 'IOU OUTPUT PARITY';
            ELSE
              symptom_code := nac$sc_ica_output_ch_parity;
              symptom_descriptor := 'OUTPUT CHANNEL PARITY';
            IFEND;
          ELSE
            CASE general_status^.symptom_code OF
            = nac$ica_checksum =
              symptom_code := nac$sc_ica_checksum_error;
              symptom_descriptor := 'CHECKSUM ERROR';

            = nac$ica_invalid_transfer =
              symptom_code := nac$sc_ica_invalid_xfer_address;
              symptom_descriptor := 'INVALID TRANSFER ADDRESS';

            = nac$ica_load_file_length =
              symptom_code := nac$sc_ica_invalid_data_packet;
              symptom_descriptor := 'INVALID DATA PACKET';

            = nac$ica_pp_overrun =
              symptom_code := nac$sc_ica_pp_overrun;
              symptom_descriptor := 'PP OVER RUN';

            = nac$ica_mismatch_hardware =
              symptom_code := nac$sc_ica_mismatch_hardware;
              symptom_descriptor := 'MISMATCH HARDWARE TYPE';
            = nac$ica_ethernet_checksum =
              symptom_code := nac$sc_ica_ethernet_checksum;
              symptom_descriptor := 'ETHERNET ADDRESS CHECKSUM ERROR';
            ELSE
              analyze_ica_2_detailed_status (error_summary, general_status, ica_osi_detailed_status,
                    network_device, symptom_code, symptom_descriptor, symptom_descriptor_length);
            CASEND;
          IFEND;

        = nac$ica_operational_state =
          IF network_device^.kind = nac$ica_2 THEN
            analyze_ica_2_detailed_status (error_summary, general_status, ica_osi_detailed_status,
                  network_device, symptom_code, symptom_descriptor, symptom_descriptor_length);
          ELSE
            IF error_summary^.detailed_status_included THEN
              IF general_status^.channel_error THEN
                IF ica_detailed_status^.error_1.channel_parity_error THEN
                  IF error_summary^.channel_error_flag THEN
                    symptom_code := nac$sc_ica_iou_output_parity;
                    symptom_descriptor := 'IOU OUTPUT PARITY';
                  ELSE
                    symptom_code := nac$sc_ica_output_ch_parity;
                    symptom_descriptor := 'OUTPUT CHANNEL PARITY';
                  IFEND;
                ELSEIF ica_detailed_status^.error_1.channel_timeout THEN
                  symptom_code := nac$sc_ica_channel_timeout;
                  symptom_descriptor := 'CHANNEL TIMEOUT';
                ELSEIF ica_detailed_status^.error_1.input_data_truncated THEN
                  symptom_code := nac$sc_ica_input_truncated;
                  symptom_descriptor := 'INPUT TRUNCATED';
                ELSEIF ica_detailed_status^.error_1.pp_overrun THEN
                  symptom_code := nac$sc_ica_pp_overrun;
                  symptom_descriptor := 'PP OVER RUN';
                ELSEIF ica_detailed_status^.error_1.formatted_output_error THEN
                  symptom_code := nac$sc_ica_formatted_output_err;
                  symptom_descriptor := 'FORMATTED OUTPUT ERROR';
                IFEND;
              IFEND;
              IF general_status^.ica_error AND ica_detailed_status^.error_1.dma_controller_error THEN
                CASE ica_detailed_status^.error_1.type_of_dma_error OF
                = nac$ica_dma_config_error =
                  symptom_code := nac$sc_ica_dma_config_error;
                  symptom_descriptor := 'ICA DMA CONFIGURATION ERROR';

                = nac$ica_dma_timing_error =
                  symptom_code := nac$sc_ica_dma_timing_error;
                  symptom_descriptor := 'ICA DMA TIMING ERROR';

                = nac$ica_dma_count_error =
                  symptom_code := nac$sc_ica_dma_count_error;
                  symptom_descriptor := 'ICA DMA COUNT ERROR';

                = nac$ica_dma_external_abort =
                  symptom_code := nac$sc_ica_dma_external_abort;
                  symptom_descriptor := 'ICA DMA EXTERNAL ABORT';

                = nac$ica_dma_software_abort =
                  symptom_code := nac$sc_ica_dma_software_abort;
                  symptom_descriptor := 'ICA DMA SOFTWARE ABORT';
                ELSE
                CASEND;
              IFEND;
              IF general_status^.pp_error THEN
                IF ica_detailed_status^.error_2.invalid_data_packet THEN
                  symptom_code := nac$sc_ica_invalid_data_packet;
                  symptom_descriptor := 'INVALID DATA PACKET';
                ELSEIF ica_detailed_status^.error_2.configuration_packet_error THEN
                  CASE ica_detailed_status^.error_2.configuration_error_reason OF
                  = nac$ica_config_no_system_addr =
                    symptom_code := nac$sc_ica_no_system_address;
                    symptom_descriptor := 'SYSTEM ADDRESS ERROR';

                  = nac$ica_config_multicast_error =
                    symptom_code := nac$sc_ica_multicast_addr_error;
                    symptom_descriptor := 'MULTICAST ADDRESS ERROR';

                  = nac$ica_config_queue_length =
                    symptom_code := nac$sc_ica_queue_length_error;
                    symptom_descriptor := 'QUEUE LENGTH ERROR';

                  = nac$ica_config_statistic_type =
                    symptom_code := nac$sc_ica_inv_statistics_type;
                    symptom_descriptor := 'INVALID STATISTICS TYPE';

                  = nac$ica_config_single_bit_thr =
                    symptom_code := nac$sc_ica_invalid_thresholds;
                    symptom_descriptor := 'INVALID THRESHOLDS';

                  = nac$ica_config_reporting_int =
                    symptom_code := nac$sc_ica_inv_reporting_inter;
                    symptom_descriptor := 'INVALID REPORTING INTERVAL';
                  ELSE
                  CASEND;
                IFEND;
              IFEND;
            IFEND;
          IFEND;
        ELSE
        CASEND;
      ELSEIF error_summary^.channel_error_flag THEN
        symptom_code := nac$sc_ica_input_ch_parity;
        symptom_descriptor := 'INPUT CHANNEL PARITY';
      ELSEIF error_summary^.channel_deactivation_error THEN
        symptom_code := nac$sc_ica_channel_inactive;
        symptom_descriptor := 'CHANNEL INACTIVE';
      ELSEIF error_summary^.message_length_verify_error THEN
        symptom_code := nac$sc_ica_message_length_error;
        symptom_descriptor := 'MESSAGE LENGTH ERROR';
      ELSEIF error_summary^.channel_full THEN
        symptom_code := nac$sc_ica_channel_full;
        symptom_descriptor := 'CHANNEL FULL';
      ELSEIF error_summary^.channel_empty THEN
        symptom_code := nac$sc_ica_channel_empty;
        symptom_descriptor := 'CHANNEL EMPTY';
      ELSEIF error_summary^.incomplete_transfer THEN
        symptom_code := nac$sc_ica_incomplete_transfer;
        symptom_descriptor := 'INCOMPLETE TRANSFER';
      IFEND;
    ELSE
    CASEND;

  PROCEND get_ica_symptom_code;

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

  PROCEDURE get_ivb_symptom_code
    (    unsolicited_response: ^nat$network_driver_response;
     VAR symptom_code: integer;
     VAR symptom_descriptor: string (max_symptom_length);
     VAR symptom_descriptor_length: integer);

{ The purpose of this routine is to return the symptom code and
{ descriptive data associated with a particular IVB error.


    VAR
      detailed_status: ^iot$detailed_status,
      error_summary: ^nat$ivb_pp_log_response;

    detailed_status := unsolicited_response^.detailed_status_pointer;
    RESET detailed_status;
    NEXT error_summary IN detailed_status;
    symptom_code := nac$sc_ivb_indeterminate;
    symptom_descriptor := 'INDETERMINATE ERROR';

{ **** WARNING
{ The following symptom descriptor length will have to be changed whenever
{ a new symptom descriptor longer that this value is returned from this
{ routine.

    symptom_descriptor_length := 50;

    CASE error_summary^.symptom_code OF

    = nac$sc_ivb_funct_timeout =
      symptom_code := nac$sc_ivb_funct_timeout;
      symptom_descriptor := 'FUNCTION TIMEOUT';
    = nac$sc_ivb_channel_empty =
      symptom_code := nac$sc_ivb_channel_empty;
      symptom_descriptor := 'CHANNEL EMPTY';
    = nac$sc_ivb_period_cnter_parity =
      symptom_code := nac$sc_ivb_period_cnter_parity;
      symptom_descriptor := 'PERIOD COUNTER PARITY ERROR';
    = nac$sc_ivb_upper_ici_parity =
      symptom_code := nac$sc_ivb_upper_ici_parity;
      symptom_descriptor := 'UPPER ICI PARITY ERROR';
    = nac$sc_ivb_lower_ici_parity =
      symptom_code := nac$sc_ivb_lower_ici_parity;
      symptom_descriptor := 'LOWER ICI PARITY ERROR';
    = nac$sc_ivb_iou_error =
      symptom_code := nac$sc_ivb_iou_error;
      symptom_descriptor := 'IOU ERROR';
    = nac$sc_ivb_incomplete_transfer =
      symptom_code := nac$sc_ivb_incomplete_transfer;
      symptom_descriptor := 'INCOMPETE TRANSFER';
    = nac$sc_ivb_channel_not_empty =
      symptom_code := nac$sc_ivb_channel_not_empty;
      symptom_descriptor := 'CHANNEL NOT EMPTY';
    = nac$sc_ivb_central_mem_error =
      symptom_code := nac$sc_ivb_central_mem_error;
      symptom_descriptor := 'CENTRAL MEMORY ERROR';
    = nac$sc_ivb_invalid_cm_response =
      symptom_code := nac$sc_ivb_invalid_cm_response;
      symptom_descriptor := 'INVALID CENTRAL MEMORY RESPONSE CODE';
    = nac$sc_ivb_cm_resp_code_parity =
      symptom_code := nac$sc_ivb_cm_resp_code_parity;
      symptom_descriptor := 'CM RESPONSE CODE PARITY ERROR';
    = nac$sc_ivb_cmi_read_data_parity =
      symptom_code := nac$sc_ivb_cmi_read_data_parity;
      symptom_descriptor := 'CMI READ PARITY';
    = nac$sc_ivb_jy_data_error =
      symptom_code := nac$sc_ivb_jy_data_error;
      symptom_descriptor := 'JY DATA ERROR';
    = nac$sc_ivb_bas_parity =
      symptom_code := nac$sc_ivb_bas_parity;
      symptom_descriptor := 'BAS PARITY ERROR';
    = nac$sc_ivb_lz_error =
      symptom_code := nac$sc_ivb_lz_error;
      symptom_descriptor := 'LZ ERROR';
    = nac$sc_ivb_jy_error =
      symptom_code := nac$sc_ivb_jy_error;
      symptom_descriptor := 'JY ERROR';
    = nac$sc_ivb_lx_error =
      symptom_code := nac$sc_ivb_lx_error;
      symptom_descriptor := 'LX ERROR';
    = nac$sc_ivb_cant_select =
      symptom_code := nac$sc_ivb_cant_select;
      symptom_descriptor := 'CAN NOT SELECT IVB';
    = nac$sc_ivb_bit_sign_resp_err =
      symptom_code := nac$sc_ivb_bit_sign_resp_err;
      symptom_descriptor := 'BIT SIGNIFICANT RESPONSE ERROR';
    = nac$sc_ivb_no_sync_in =
      symptom_code := nac$sc_ivb_no_sync_in;
      symptom_descriptor := 'NO SYNC IN';
    = nac$sc_ivb_sync_in_did_not_drop =
      symptom_code := nac$sc_ivb_sync_in_did_not_drop;
      symptom_descriptor := 'SYNC IN DID NOT DROP';
    = nac$sc_ivb_ipi_sequence_error =
      symptom_code := nac$sc_ivb_ipi_sequence_error;
      symptom_descriptor := 'IPI SEQUENCE ERROR';
    = nac$sc_ivb_upper_ipi_parity =
      symptom_code := nac$sc_ivb_upper_ipi_parity;
      symptom_descriptor := 'UPPER IPI CHANNEL PARITY';
    = nac$sc_ivb_lower_ipi_parity =
      symptom_code := nac$sc_ivb_lower_ipi_parity;
      symptom_descriptor := 'LOWER IPI PARITY ERROR';
    = nac$sc_ivb_slave_in_not_set =
      symptom_code := nac$sc_ivb_slave_in_not_set;
      symptom_descriptor := 'SLAVE IN DID NOT SET';
    = nac$sc_ivb_slave_in_didnt_drop =
      symptom_code := nac$sc_ivb_slave_in_didnt_drop;
      symptom_descriptor := 'SLAVE IN DID NOT DROP';
    = nac$sc_ivb_channel_error =
      symptom_code := nac$sc_ivb_channel_error;
      symptom_descriptor := 'CHANNEL ERROR';
    = nac$sc_ivb_channel_active =
      symptom_code := nac$sc_ivb_channel_active;
      symptom_descriptor := 'CHANNEL STAYED ACTIVE';
    = nac$sc_ivb_buffer_cnt_parity =
      symptom_code := nac$sc_ivb_buffer_cnt_parity;
      symptom_descriptor := 'BUFFER COUNTER PARITY';
    = nac$sc_ivb_sync_counter_parity =
      symptom_code := nac$sc_ivb_sync_counter_parity;
      symptom_descriptor := 'SYNC COUNTER PARITY';
    = nac$sc_ivb_lost_data =
      symptom_code := nac$sc_ivb_lost_data;
      symptom_descriptor := 'LOST DATA';
    = nac$sc_ivb_bus_parity =
      symptom_code := nac$sc_ivb_bus_parity;
      symptom_descriptor := 'BUS PARITY';
    = nac$sc_ivb_command_reject =
      symptom_code := nac$sc_ivb_command_reject;
      symptom_descriptor := 'COMMAND REJECT';
    = nac$sc_ivb_sync_out_ne_sync_in =
      symptom_code := nac$sc_ivb_sync_out_ne_sync_in;
      symptom_descriptor := 'SYNC OUT NOT EQUAL SYNC IN';
    = nac$sc_ivb_bus_b_ack_error =
      symptom_code := nac$sc_ivb_bus_b_ack_error;
      symptom_descriptor := 'BUS B ACK INCORRECT';
    = nac$sc_ivb_ending_status_wrong =
      symptom_code := nac$sc_ivb_ending_status_wrong;
      symptom_descriptor := 'ENDING STATUS WRONG';
    = nac$sc_ivb_available =
      symptom_code := nac$sc_ivb_available;
      symptom_descriptor := 'IVB AVAILABLE';
    = nac$sc_ivb_reset =
      symptom_code := nac$sc_ivb_reset;
      symptom_descriptor := 'IVB RESET';
    = nac$sc_ivb_no_forced_error =
      symptom_code := nac$sc_ivb_no_forced_error;
      symptom_descriptor := 'FORCED ERROR DID NOT OCCUR';
    = nac$sc_ivb_ipi_read_resp_error =
      symptom_code := nac$sc_ivb_ipi_read_resp_error;
      symptom_descriptor := 'INVALID IPI READ RESPONSE';
    = nac$sc_ivb_ipi_param_len_error =
      symptom_code := nac$sc_ivb_ipi_param_len_error;
      symptom_descriptor := 'INVALID IPI PARAMETER LENGTH';
    = nac$sc_ivb_sequence_number_err =
      symptom_code := nac$sc_ivb_sequence_number_err;
      symptom_descriptor := 'SEQUENCE NUMBER ERROR';
    = nac$sc_ivb_status_mismatch =
      symptom_code := nac$sc_ivb_status_mismatch;
      symptom_descriptor := 'STATUS MISMATCH';
    = nac$sc_ivb_ipi_resp_code_err =
      symptom_code := nac$sc_ivb_ipi_resp_code_err;
      symptom_descriptor := 'INVALID IPI RESPONSE CODE';
    = nac$sc_ivb_ipi_response_len_err =
      symptom_code := nac$sc_ivb_ipi_response_len_err;
      symptom_descriptor := 'INVALID IPI RESPONSE LENGTH';
    = nac$sc_ivb_ipi_resp_param_err =
      symptom_code := nac$sc_ivb_ipi_resp_param_err;
      symptom_descriptor := 'INVALID READ RESPONSE PARAMETER';
    = nac$sc_ivb_diag_resp_error =
      symptom_code := nac$sc_ivb_diag_resp_error;
      symptom_descriptor := 'INVALID DIAGNOSTIC RESPONSE';
    = nac$sc_ivb_max_ccpdu_size_err =
      symptom_code := nac$sc_ivb_max_ccpdu_size_err;
      symptom_descriptor := 'MAXIMUM CCPDU SIZE EXCEEDED';
    = nac$sc_ivb_buffers_exceeded =
      symptom_code := nac$sc_ivb_buffers_exceeded;
      symptom_descriptor := 'BUFFER REQUIRMENTS EXCEEDED';
    = nac$sc_ivb_rma_not_on_word =
      symptom_code := nac$sc_ivb_rma_not_on_word;
      symptom_descriptor := 'RMA NOT ON A WORD BOUNDARY';
    = nac$sc_ivb_ccpdu_header_error =
      symptom_code := nac$sc_ivb_ccpdu_header_error;
      symptom_descriptor := 'CCPDU HEADER ERROR';
    = nac$sc_ivb_unit_request_err =
      symptom_code := nac$sc_ivb_unit_request_err;
      symptom_descriptor := 'INVALID UNIT REQUEST';
    = nac$sc_ivb_request_len_error =
      symptom_code := nac$sc_ivb_request_len_error;
      symptom_descriptor := 'WRITE REQUEST LENGTH ERROR';
    = nac$sc_ivb_protocol_neg_failed =
      symptom_code := nac$sc_ivb_protocol_neg_failed;
      symptom_descriptor := 'IVB PROTOCOL NEGOTIATION FAILED';
    = nac$sc_ivb_invalid_pp_command =
      symptom_code := nac$sc_ivb_invalid_pp_command;
      symptom_descriptor := 'INVALID PP COMMAND';
    = nac$sc_ivb_unexpected_cpu_ack =
      symptom_code := nac$sc_ivb_unexpected_cpu_ack;
      symptom_descriptor := 'UNEXPECTED CPU ACK';
    = nac$sc_ivb_cant_clear_ch_lock =
      symptom_code := nac$sc_ivb_cant_clear_ch_lock;
      symptom_descriptor := 'UNABLE TO CLEAR CHANNEL LOCK';
    = nac$sc_ivb_buffer_pool_error =
      symptom_code := nac$sc_ivb_buffer_pool_error;
      symptom_descriptor := 'INVALID BUFFER POOL DESCRIPTOR';
    = nac$sc_ivb_inv_max_ccpdu_size =
      symptom_code := nac$sc_ivb_inv_max_ccpdu_size;
      symptom_descriptor := 'MAXIMUM CCPDU SIZE REQUESTED ON SUSPEND LINK';
    ELSE
    CASEND;

  PROCEND get_ivb_symptom_code;
?? OLDTITLE ??
?? NEWTITLE := 'get_mdi_symptom_code', EJECT ??

  PROCEDURE get_mdi_symptom_code
    (    unsolicited_response: ^nat$network_driver_response;
     VAR symptom_code: integer;
     VAR symptom_descriptor: string (max_symptom_length);
     VAR symptom_descriptor_length: integer);

{ The purpose of this routine is to return the symptom code and
{ descriptive data associated with a particular MDI error.

    TYPE
      general_status_16_bit = packed record
        fill: 0 .. 0f(16),
        general_status: nat$mdi_general_status,
      recend;

    VAR
      detailed_status: ^iot$detailed_status,
      error_summary: ^nat$mdi_pp_log_response,
      general_status: ^general_status_16_bit,
      general_status_contents: 0 .. 0ffff(16),
      mdi_detailed_status: ^nat$mdi_detailed_status;

    detailed_status := unsolicited_response^.detailed_status_pointer;
    RESET detailed_status;
    NEXT error_summary IN detailed_status;
    IF error_summary^.detailed_status_included THEN
      NEXT mdi_detailed_status IN detailed_status;
    IFEND;
    general_status_contents := error_summary^.general_status;
    general_status := #LOC (general_status_contents);
    symptom_code := nac$sc_mdi_indeterminate;
    symptom_descriptor := 'INDETERMINATE ERROR';

{ **** WARNING
{ The following symptom descriptor length will have to be changed whenever
{ a new symptom descriptor longer that this value is returned from this
{ routine.

    symptom_descriptor_length := 50;

    CASE error_summary^.error_id OF
    = nac$mdi_function_timeout =
      IF error_summary^.channel_error_flag THEN
        symptom_code := nac$sc_mdi_funct_timeout_cef;
        symptom_descriptor := 'FUNCTION TIMEOUT AND CHANNEL ERROR FLAG';
      ELSE
        symptom_code := nac$sc_mdi_funct_timeout;
        symptom_descriptor := 'FUNCTION TIMEOUT';
      IFEND;

    = nac$mdi_invalid_state_change =
      symptom_code := nac$sc_mdi_invalid_state_trans;
      symptom_descriptor := 'INVALID STATE TRANSITION';

    = nac$mdi_gen_status_busy_timeout =
      symptom_code := nac$sc_mdi_general_status_busy;
      symptom_descriptor := 'GENERAL STATUS BUSY TIMEOUT';

    = nac$mdi_gen_status_send_timeout =
      symptom_code := nac$sc_mdi_no_send_data;
      symptom_descriptor := 'GENERAL STATUS SEND DATA TIMEOUT';

    = nac$mdi_gen_status_data_avail =
      symptom_code := nac$sc_mdi_no_data_avail;
      symptom_descriptor := 'GENERAL STATUS DATA AVAILABLE TIMEOUT';

    = nac$mdi_gen_status_avai_timeout =
      symptom_code := nac$sc_mdi_status_avail_timeout;
      symptom_descriptor := 'GENERAL STATUS AVAILABLE TIMEOUT';

    = nac$mdi_gen_status_content_fail =
      symptom_code := nac$sc_mdi_status_content_error;
      symptom_descriptor := 'DIAGNOSTIC MODE GENERAL STATUS CONTENT FAILURE';

    = nac$mdi_reset =
      symptom_code := nac$sc_mdi_reset_state;
      symptom_descriptor := 'MDI RESET';

    = nac$mdi_available =
      symptom_code := nac$sc_mdi_available;
      symptom_descriptor := 'MDI AVAILABLE';

    = nac$mdi_channel_protocol_error =
      symptom_code := nac$sc_mdi_channel_protocol_err;
      symptom_descriptor := 'CHANNEL PROTOCOL NOT SUPPORTED';

    = nac$mdi_master_clear_failure =
      symptom_code := nac$sc_mdi_master_clear_failure;
      symptom_descriptor := 'MASTER CLEAR FAILURE';

    = nac$mdi_invalid_message_type =
      symptom_code := nac$sc_mdi_invalid_message_type;
      symptom_descriptor := 'INVALID MESSAGE TYPE';

    = nac$mdi_operation_failure =
      IF error_summary^.channel_error_flag THEN
        IF ((error_summary^.operation_kind = nac$mdi_op_write) OR
              (error_summary^.operation_kind = nac$mdi_op_inline_write)) THEN
          IF general_status^.general_status.general_error THEN
            IF error_summary^.detailed_status_included THEN
              IF mdi_detailed_status^.error_1.channel_parity_error THEN
                symptom_code := nac$sc_mdi_iou_output_parity;
                symptom_descriptor := 'IOU OUTPUT PARITY';
              ELSE
                symptom_code := nac$sc_mdi_indet_output_parity;
                symptom_descriptor := 'INDETERMINATE OUTPUT PARITY';
              IFEND;
            ELSE
              symptom_code := nac$sc_mdi_iou_output_parity;
              symptom_descriptor := 'IOU OUTPUT PARITY';
            IFEND;
          ELSE
            symptom_code := nac$sc_mdi_indet_output_parity;
            symptom_descriptor := 'INDETERMINATE OUTPUT PARITY';
          IFEND;
        ELSE
          symptom_code := nac$sc_mdi_input_ch_parity;
          symptom_descriptor := 'INPUT CHANNEL PARITY';
        IFEND;
      ELSEIF error_summary^.maximum_size_exceeded THEN
        symptom_code := nac$sc_mdi_max_size_exceeded;
        symptom_descriptor := 'MAXIMUM RECORD SIZE EXCEEDED';
      ELSEIF general_status^.general_status.general_error AND error_summary^.detailed_status_included THEN
        IF mdi_detailed_status^.error_1.channel_parity_error AND
              ((error_summary^.operation_kind = nac$mdi_op_write) OR
              (error_summary^.operation_kind = nac$mdi_op_inline_write)) THEN
          symptom_code := nac$sc_mdi_output_ch_parity;
          symptom_descriptor := 'OUTPUT CHANNEL PARITY';
        ELSEIF mdi_detailed_status^.error_1.itb_error THEN
          symptom_code := nac$sc_mdi_itb_error;
          symptom_descriptor := 'ITB ERROR';
        ELSEIF mdi_detailed_status^.error_1.itb_parity_error THEN
          symptom_code := nac$sc_mdi_itb_parity_error;
          symptom_descriptor := 'ITB PARITY ERROR';
        ELSEIF mdi_detailed_status^.error_1.channel_timeout THEN
          symptom_code := nac$sc_mdi_channel_timeout;
          symptom_descriptor := 'CHANNEL TIMEOUT';
        ELSEIF mdi_detailed_status^.error_1.input_truncated THEN
          symptom_code := nac$sc_mdi_input_truncated;
          symptom_descriptor := 'INPUT TRUNCATED';
        ELSEIF mdi_detailed_status^.error_1.pp_overrun THEN
          symptom_code := nac$sc_mdi_pp_overrun;
          symptom_descriptor := 'PP OVERRUN';
        ELSEIF mdi_detailed_status^.software_status.length_error THEN
          symptom_code := nac$sc_mdi_length_error;
          symptom_descriptor := 'MCI DETECTED LENGTH ERROR';
        ELSE
          EXIT get_mdi_symptom_code;
        IFEND;
      ELSEIF error_summary^.message_content_error THEN
        symptom_code := nac$sc_mdi_content_error;
        symptom_descriptor := 'MESSAGE_CONTENT_ERROR';
      ELSEIF error_summary^.channel_deactivation_error THEN
        symptom_code := nac$sc_mdi_channel_inactive;
        symptom_descriptor := 'CHANNEL INACTIVE';
      ELSEIF error_summary^.message_length_verify_error THEN
        symptom_code := nac$sc_mdi_message_length_error;
        symptom_descriptor := 'MESSAGE LENGTH ERROR';
      ELSEIF error_summary^.channel_full THEN
        symptom_code := nac$sc_mdi_channel_full;
        symptom_descriptor := 'CHANNEL FULL';
      ELSEIF error_summary^.channel_empty THEN
        symptom_code := nac$sc_mdi_channel_empty;
        symptom_descriptor := 'CHANNEL EMPTY';
      ELSEIF error_summary^.incomplete_transfer THEN
        symptom_code := nac$sc_mdi_incomplete_transfer;
        symptom_descriptor := 'INCOMPLETE TRANSFER';
      IFEND;
    ELSE
    CASEND;

  PROCEND get_mdi_symptom_code;
?? OLDTITLE ??
?? NEWTITLE := 'log_cpu_message', EJECT ??

  PROCEDURE log_cpu_message
    (    device_kind: nat$device_type;
         logical_unit: iot$logical_unit;
         pp_number: iot$pp_number;
         channel: cmt$channel_ordinal;
         log_reason: network_device_log_reason);

{ The purpose of this procedure is to log significant events received or detected by the
{ intranet layer mgmt task.

    CONST
      symptom_length = 28;

    VAR
      channel_counter_word: ^channel_counter,
      channel_name: cmt$element_name,
      channel_number: ost$physical_channel_number,
      channel_port: cmt$channel_port,
      concurrent: boolean,
      counters: ^array [1 .. * ] of sft$counter,
      descriptive_data: ost$string,
      error_kind: integer,
      i: integer,
      iou_number: dst$iou_number,
      local_status: ost$status,
      message: ^string ( * ),
      number_of_counters: integer,
      physical_pp_number: 0 .. 31,
      pp_counter_word: ^pp_counter,
      size: integer,
      statistic_code: sft$statistic_code,
      symptom_code: integer,
      symptom_text: string (symptom_length);

    cmp$return_desc_data_by_lun_lpn (logical_unit, pp_number, iou_number, descriptive_data,
          physical_pp_number);
    CASE device_kind OF
    = nac$di =
      number_of_counters := 14;
      statistic_code := cml$mdi_failure_data;
      error_kind := $INTEGER (nac$mdi_unrecovered_error);
    = nac$ica_2 =
      number_of_counters := 15;
      statistic_code := cml$ica_failure_data;
      error_kind := $INTEGER (nac$ica_unrecovered_error);
    = nac$expresslink =
      number_of_counters := 40;
      statistic_code := cml$ivb_failure_data;
      error_kind := $INTEGER (nac$ivb_unrecovered_error);
    ELSE
    CASEND;

    PUSH counters: [1 .. number_of_counters];
    pp_counter_word := #LOC (counters^ [1]);
    pp_counter_word^.fill := 0;
    pp_counter_word^.iou := iou_number;
    pp_counter_word^.fill1 := 0;
    pp_counter_word^.pp := physical_pp_number;
    channel_counter_word := #LOC (counters^ [2]);
    channel_counter_word^.channel_error_status := 0;
    channel_counter_word^.fill := 0;
    channel_counter_word^.iou := iou_number;
    channel_counter_word^.fill1 := 0;
    channel_counter_word^.fill2 := 0;
    cmp$convert_channel_ordinal (channel, channel_name, channel_number, concurrent, channel_port,
          local_status);
    IF NOT local_status.normal THEN
      nap$namve_system_error (FALSE, 'CMP$CONVERT_CHANNEL_ORDINAL error.', NIL);
    IFEND;
    pp_counter_word^.concurrent := concurrent;
    channel_counter_word^.concurrent := concurrent;
    channel_counter_word^.channel := channel_number;
    counters^ [3] := null_counter;
    counters^ [4] := error_kind;

    CASE log_reason OF
    = device_reset_down_thresh_exceed =
      CASE device_kind OF
      = nac$di =
        symptom_text := '*UF*MDI RESET DOWN THRESHOLD';
        counters^ [5] := nac$sc_mdi_reset_freq_thresh;
      = nac$ica_2 =
        symptom_text := '*UF*ICA RESET DOWN THRESHOLD';
        counters^ [5] := nac$sc_ica_reset_freq_thresh;
      = nac$expresslink =
        symptom_text := '*UF*IVB RESET DOWN THRESHOLD';
        counters^ [5] := nac$sc_ivb_reset_freq_thresh;
      ELSE
      CASEND;
    = device_reset_timeout =
      CASE device_kind OF
      = nac$ica_2 =
        symptom_text := '*UF*ICA RESET TIMEOUT';
        counters^ [5] := nac$sc_ica_not_ready_timeout;
      ELSE
      CASEND;
    ELSE
    CASEND;

    FOR i := 6 TO UPPERBOUND (counters^) DO
      counters^ [i] := null_counter;
    FOREND;

    IF descriptive_data.size <= (252 - symptom_length) THEN
      size := descriptive_data.size;
    ELSE
      size := 252 - symptom_length;
    IFEND;
    PUSH message: [size + symptom_length];
    message^ (1, size) := descriptive_data.value;
    message^ (size + 1, * ) := symptom_text;
    sfp$emit_statistic (statistic_code, message^, counters, {ignore} local_status);

  PROCEND log_cpu_message;
?? OLDTITLE ??
?? NEWTITLE := 'log_device_usage_statistics', EJECT ??

  PROCEDURE log_device_usage_statistics;

{ PURPOSE:
{   The purpose of this procedure is to log the usage data for all
{   configured network devices. The usage data logged represents the
{   amount of usage since the previous log entry. If there has been
{   no usage of this device since the last log entry, no entry will
{   be made.
{
{ NOTE: The caller is assumed to have obtained at least non-exclusive
{   access to the network device list.


    VAR
      channel_counter_word: ^channel_counter,
      channel_name: cmt$element_name,
      channel_number: ost$physical_channel_number,
      channel_port: cmt$channel_port,
      concurrent: boolean,
      counters: array [1 .. 4] of sft$counter,
      current_usage_data: nlt$device_usage_data,
      descriptor_data: ost$string,
      i: integer,
      iou_number: dst$iou_number,
      local_status: ost$status,
      message: ^string ( * ),
      message_length: integer,
      network_device_list: ^nlt$network_device_list,
      physical_pp_number: 0 .. 31,
      pp_counter_word: ^pp_counter,
      size: integer,
      symptom_descriptor_length: integer;

    network_device_list := nlv$configured_network_devices.network_device_list;

    FOR i := 1 TO UPPERBOUND (network_device_list^) DO
      current_usage_data := nlv$device_usage_data^ [i];
      IF current_usage_data <> network_device_list^ [i].last_usage_data THEN
        cmp$return_desc_data_by_lun_lpn (network_device_list^ [i].logical_unit,
              network_device_list^ [i].pp_number, iou_number, descriptor_data, physical_pp_number);
        pp_counter_word := #LOC (counters [1]);
        pp_counter_word^.fill := 0;
        pp_counter_word^.iou := iou_number;
        pp_counter_word^.fill1 := 0;
        pp_counter_word^.pp := physical_pp_number;
        channel_counter_word := #LOC (counters [2]);
        channel_counter_word^.channel_error_status := 0;
        channel_counter_word^.fill := 0;
        channel_counter_word^.iou := iou_number;
        channel_counter_word^.fill1 := 0;
        channel_counter_word^.fill2 := 0;
        cmp$convert_channel_ordinal (network_device_list^ [i].channel, channel_name, channel_number,
              concurrent, channel_port, local_status);
        IF NOT local_status.normal THEN
          nap$namve_system_error (FALSE, 'CMP$CONVERT_CHANNEL_ORDINAL error.', NIL);
        IFEND;
        pp_counter_word^.concurrent := concurrent;
        channel_counter_word^.concurrent := concurrent;
        channel_counter_word^.channel := channel_number;
        counters [3] := current_usage_data.bytes_received -
              network_device_list^ [i].last_usage_data.bytes_received;
        counters [4] := current_usage_data.bytes_transmitted -
              network_device_list^ [i].last_usage_data.bytes_transmitted;
        network_device_list^ [i].last_usage_data := current_usage_data;

{ The following line should be changed if the maximum symptom descriptor length
{ increases.

        symptom_descriptor_length := 16;

        IF ((descriptor_data.size + 1) <= (252 - symptom_descriptor_length)) THEN
          size := descriptor_data.size + 1;
        ELSE
          size := 252 - symptom_descriptor_length;
        IFEND;

        message_length := size + symptom_descriptor_length;
        PUSH message: [message_length];
        message^ (1, size - 1) := descriptor_data.value;
        message^ (size, 1) := '*';
        CASE network_device_list^ [i].kind OF
        = nac$di =
          message^ (size + 1, * ) := 'MDI USAGE DATA';
          sfp$emit_statistic (cml$mdi_usage_data, message^, ^counters, {ignore} local_status);
        = nac$ica_2 =
          message^ (size + 1, * ) := 'ICA USAGE DATA';
          sfp$emit_statistic (cml$ica_usage_data, message^, ^counters, {ignore} local_status);
        = nac$expresslink =
          message^ (size + 1, * ) := 'IVB USAGE DATA';
          sfp$emit_statistic (cml$ivb_usage_data, message^, ^counters, {ignore} local_status);
        ELSE
        CASEND;
      IFEND;
    FOREND;

  PROCEND log_device_usage_statistics;
?? OLDTITLE ??
?? NEWTITLE := 'log_ica_pp_message', EJECT ??

  PROCEDURE log_ica_pp_message
    (    network_device: ^nlt$network_device;
         unsolicited_response: ^nat$network_driver_response;
     VAR ica_down: boolean);

{ The purpose of this procedure is to log the messages generated by the ICA PP.
{ The log information is setup in the detailed status portion of the PP response.



    TYPE
      split_counter = record
        fill: ost$halfword,
        first_half: 0 .. 0ffff(16),
        second_half: 0 .. 0ffff(16),
      recend;

    VAR
      assemble_counter: ^split_counter,
      channel_counter_word: ^channel_counter,
      channel_name: cmt$element_name,
      channel_number: ost$physical_channel_number,
      channel_port: cmt$channel_port,
      concurrent: boolean,
      counters: ^array [1 .. * ] of sft$counter,
      descriptor_data: ost$string,
      detailed_status: ^iot$detailed_status,
      detailed_status_word: ^integer,
      detailed_status_length: 2 .. 3,
      error_summary: ^nat$ica_pp_log_response,
      i,
      k: integer,
      iou_number: dst$iou_number,
      local_status: ost$status,
      message: ^string ( * ),
      message_length: integer,
      number_of_counters: integer,
      physical_pp_number: 0 .. 31,
      pp_counter_word: ^pp_counter,
      symptom_code: integer,
      symptom_descriptor: string (max_symptom_length),
      symptom_descriptor_length: integer,
      size: integer;

    get_ica_symptom_code (unsolicited_response, network_device, symptom_code, symptom_descriptor,
          symptom_descriptor_length);
    cmp$return_desc_data_by_lun_lpn (network_device^.logical_unit, network_device^.pp_number, iou_number,
          descriptor_data, physical_pp_number);
    detailed_status := unsolicited_response^.detailed_status_pointer;
    RESET detailed_status;
    NEXT error_summary IN detailed_status;
    IF error_summary^.detailed_status_included THEN
      number_of_counters := 17;
      detailed_status_length := 2;
    ELSE
      number_of_counters := 15;
    IFEND;

    PUSH counters: [1 .. number_of_counters];
    pp_counter_word := #LOC (counters^ [1]);
    pp_counter_word^.fill := 0;
    pp_counter_word^.iou := iou_number;
    pp_counter_word^.fill1 := 0;
    pp_counter_word^.pp := physical_pp_number;
    channel_counter_word := #LOC (counters^ [2]);
    channel_counter_word^.channel_error_status := 0;
    channel_counter_word^.fill := 0;
    channel_counter_word^.iou := iou_number;
    channel_counter_word^.fill1 := 0;
    channel_counter_word^.fill2 := 0;
    cmp$convert_channel_ordinal (network_device^.channel, channel_name, channel_number, concurrent,
          channel_port, local_status);
    IF NOT local_status.normal THEN
      nap$namve_system_error (FALSE, 'CMP$CONVERT_CHANNEL_ORDINAL error.', NIL);
    IFEND;
    pp_counter_word^.concurrent := concurrent;
    channel_counter_word^.concurrent := concurrent;
    channel_counter_word^.channel := channel_number;
    ica_down := FALSE;

    FOR k := 3 TO number_of_counters DO
      counters^ [k] := null_counter;
    FOREND;

    IF error_summary^.operation_kind <> nac$ica_null_op_kind THEN
      counters^ [3] := $INTEGER (error_summary^.operation_kind);
    IFEND;

    counters^ [4] := $INTEGER (error_summary^.error_kind);
    counters^ [5] := symptom_code;

    IF error_summary^.error_kind <> nac$ica_informative_message THEN
      counters^ [6] := error_summary^.retry_count;
    IFEND;

    IF error_summary^.error_id = nac$ica_function_timeout THEN
      counters^ [7] := error_summary^.timed_out_function;
    IFEND;

    IF (error_summary^.error_id = nac$ica_gen_status_busy_timeout) OR
          (symptom_code = nac$sc_ica_status_avail_timeout) THEN
      counters^ [8] := error_summary^.previous_function;
    IFEND;

    IF (symptom_code = nac$sc_ica_read_diag_cmd_cont) OR (symptom_code = nac$sc_ica_echo_status) THEN
      counters^ [9] := error_summary^.error_word1;
    ELSEIF symptom_code = nac$sc_ica_read_conf_content THEN
      assemble_counter := #LOC (counters^ [9]);
      assemble_counter^.fill := 0;
      assemble_counter^.first_half := error_summary^.error_word1;
      assemble_counter^.second_half := error_summary^.error_word2;
    IFEND;

    IF error_summary^.error_id = nac$ica_state_transition_fail THEN
      counters^ [10] := error_summary^.transition_state;
    IFEND;

    IF error_summary^.error_id = nac$ica_invalid_state_change THEN
      counters^ [11] := error_summary^.error_word1;
      counters^ [12] := error_summary^.error_word2;
    IFEND;

    IF error_summary^.message_length_verify_error THEN
      counters^ [13] := error_summary^.expected_length;
      counters^ [14] := error_summary^.actual_length;
    IFEND;

    IF error_summary^.general_status_included THEN
      counters^ [15] := error_summary^.general_status;
    IFEND;

    IF error_summary^.detailed_status_included THEN
      FOR i := 1 TO detailed_status_length DO
        NEXT detailed_status_word IN detailed_status;
        counters^ [15 + i] := detailed_status_word^;
      FOREND;
    IFEND;


{ Combine descriptor data with symptom message.

    IF (descriptor_data.size <= (252 - 4 - symptom_descriptor_length)) THEN
      size := descriptor_data.size;
    ELSE
      size := 252 - 4 - symptom_descriptor_length;
    IFEND;
    message_length := size + 4 + symptom_descriptor_length;
    PUSH message: [message_length];
    message^ (1, size) := descriptor_data.value;
    k := size + 1;
    CASE error_summary^.error_kind OF
    = nac$ica_unrecovered_error =
      message^ (k, 4) := '*UF*';
    = nac$ica_recovered_error =
      message^ (k, 4) := '*RF*';
    = nac$ica_intermediate_error =
      message^ (k, 4) := '*IF*';
    = nac$ica_informative_message =
      message^ (k, 4) := '*IM*';
    ELSE
    CASEND;
    k := k + 4;
    message^ (k, * ) := symptom_descriptor;
    sfp$emit_statistic (cml$ica_failure_data, message^, counters, {ignore} local_status);

    ica_down := error_summary^.ica_is_down;

  PROCEND log_ica_pp_message;

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

  PROCEDURE log_ivb_pp_message
    (    network_device: ^nlt$network_device;
         unsolicited_response: ^nat$network_driver_response;
     VAR ivb_down: boolean);

{ The purpose of this procedure is to log the messages generated by the IVB PP.
{ The log information is setup in the detailed status portion of the PP
{ response.


    CONST
      null_counter = -1;

    TYPE
      split_counter = record
        fill: 0 .. 0ffffffffffff(16),
        remaining_bytes: 0 .. 0ffff(16),
      recend;

    VAR
      assemble_counter: ^split_counter,
      channel_counter_word: ^channel_counter,
      channel_name: cmt$element_name,
      channel_number: ost$physical_channel_number,
      channel_port: cmt$channel_port,
      concurrent: boolean,
      counters: ^array [1 .. * ] of sft$counter,
      descriptor_data: ost$string,
      detailed_status: ^iot$detailed_status,
      error_summary: ^nat$ivb_pp_log_response,
      k: integer,
      iou_number: dst$iou_number,
      local_status: ost$status,
      message: ^string ( * ),
      message_length: integer,
      number_of_counters: integer,
      pp_counter_word: ^pp_counter,
      physical_pp_number: 0 .. 31,
      symptom_code: integer,
      symptom_descriptor: string (max_symptom_length),
      symptom_descriptor_length: integer,
      size: integer;

    get_ivb_symptom_code (unsolicited_response, symptom_code, symptom_descriptor, symptom_descriptor_length);
    cmp$return_desc_data_by_lun_lpn (network_device^.logical_unit, network_device^.pp_number, iou_number,
          descriptor_data, physical_pp_number);
    detailed_status := unsolicited_response^.detailed_status_pointer;
    RESET detailed_status;
    NEXT error_summary IN detailed_status;
    number_of_counters := 38;
    ivb_down := FALSE;
    PUSH counters: [1 .. number_of_counters];
    pp_counter_word := #LOC (counters^ [1]);
    pp_counter_word^.fill := 0;
    pp_counter_word^.iou := iou_number;
    pp_counter_word^.fill1 := 0;
    pp_counter_word^.pp := physical_pp_number;
    channel_counter_word := #LOC (counters^ [2]);
    channel_counter_word^.channel_error_status := 0;
    channel_counter_word^.fill := 0;
    channel_counter_word^.iou := iou_number;
    channel_counter_word^.fill1 := 0;
    channel_counter_word^.fill2 := 0;
    cmp$convert_channel_ordinal (network_device^.channel, channel_name, channel_number, concurrent,
          channel_port, local_status);
    IF NOT local_status.normal THEN
      nap$namve_system_error (FALSE, 'CMP$CONVERT_CHANNEL_ORDINAL error.', NIL);
    IFEND;
    pp_counter_word^.concurrent := concurrent;
    channel_counter_word^.concurrent := concurrent;
    channel_counter_word^.channel := channel_number;
    FOR k := 3 TO number_of_counters DO
      counters^ [k] := null_counter;
    FOREND;

    counters^ [4] := $INTEGER (error_summary^.error_kind);
    counters^ [5] := symptom_code;

    IF NOT (error_summary^.error_kind = nac$ivb_informative_message) THEN
      counters^ [6] := error_summary^.retry_count;
      counters^ [7] := error_summary^.last_function;
      counters^ [8] := error_summary^.last_1_function;
      counters^ [9] := error_summary^.last_2_function;
      counters^ [10] := error_summary^.last_3_function;
      counters^ [11] := error_summary^.last_4_function;
      counters^ [12] := error_summary^.last_5_function;
      counters^ [13] := error_summary^.last_6_function;
      counters^ [14] := error_summary^.last_7_function;
    IFEND;

    IF error_summary^.master_status_included THEN
      counters^ [15] := error_summary^.master_status;
    IFEND;

    IF error_summary^.slave_status_included THEN
      counters^ [16] := error_summary^.slave_status;
    IFEND;

    IF error_summary^.ipi_dma_registers_included THEN
      counters^ [26] := error_summary^.ipi_status_register;
      counters^ [27] := error_summary^.ipi_error_register;
      counters^ [28] := error_summary^.dma_error_register;
      counters^ [29] := error_summary^.dma_operation_register;
      counters^ [30] := error_summary^.dma_control_register;
    IFEND;

    counters^ [34] := error_summary^.pp_word_1;
    counters^ [35] := error_summary^.pp_word_2;
    counters^ [36] := error_summary^.pp_word_3;
    counters^ [37] := error_summary^.pp_word_4;
    counters^ [38] := error_summary^.pp_word_5;

    IF (symptom_code = nac$sc_ivb_ipi_read_resp_error) OR (symptom_code = nac$sc_ivb_ipi_param_len_error) OR
          (symptom_code = nac$sc_ivb_ipi_response_len_err) OR
          (symptom_code = nac$sc_ivb_unit_request_err) THEN
      counters^ [25] := error_summary^.actual_data;
    IFEND;

    IF (symptom_code = nac$sc_ivb_ipi_param_len_error) OR (symptom_code = nac$sc_ivb_ipi_resp_param_err) THEN
      counters^ [18] := error_summary^.parameter_id;
    IFEND;

    IF (symptom_code = nac$sc_ivb_sequence_number_err) THEN
      counters^ [19] := error_summary^.expected_data;
      counters^ [20] := error_summary^.actual_data;
    IFEND;

    IF (symptom_code = nac$sc_ivb_available) THEN
      counters^ [33] := error_summary^.expected_data;
      counters^ [22] := error_summary^.actual_data;
    IFEND;

    IF (symptom_code = nac$sc_ivb_ipi_resp_code_err) OR (symptom_code = nac$sc_ivb_ipi_response_len_err) OR
          (symptom_code = nac$sc_ivb_ipi_resp_param_err) OR (symptom_code = nac$sc_ivb_diag_resp_error) OR
          (symptom_code = nac$sc_ivb_unit_request_err) THEN
      counters^ [17] := error_summary^.operation_code;
    IFEND;

    IF (symptom_code = nac$sc_ivb_max_ccpdu_size_err) THEN
      counters^ [32] := error_summary^.actual_data;
    IFEND;

    IF (symptom_code = nac$sc_ivb_buffers_exceeded) THEN
      counters^ [23] := error_summary^.expected_data;
      counters^ [24] := error_summary^.actual_data;
    IFEND;

    IF (symptom_code = nac$sc_ivb_protocol_neg_failed) THEN
      counters^ [21] := error_summary^.expected_data;
      counters^ [22] := error_summary^.actual_data;
    IFEND;

    IF (symptom_code = nac$sc_ivb_incomplete_transfer) THEN
      counters^ [31] := error_summary^.expected_data;
      counters^ [32] := error_summary^.actual_data;
    IFEND;

{ Combine descriptor data with symptom message.

    IF (descriptor_data.size <= (252 - 4 - symptom_descriptor_length)) THEN
      size := descriptor_data.size;
    ELSE
      size := 252 - 4 - symptom_descriptor_length;
    IFEND;
    message_length := size + 4 + symptom_descriptor_length;
    PUSH message: [message_length];
    message^ (1, size) := descriptor_data.value;
    k := size + 1;
    CASE error_summary^.error_kind OF
    = nac$ivb_unrecovered_error =
      message^ (k, 4) := '*UF*';
    = nac$ivb_recovered_error =
      message^ (k, 4) := '*RF*';
    = nac$ivb_intermediate_error =
      message^ (k, 4) := '*IF*';
    = nac$ivb_informative_message =
      message^ (k, 4) := '*IM*';
    ELSE
    CASEND;
    k := k + 4;
    message^ (k, * ) := symptom_descriptor;
    sfp$emit_statistic (cml$ivb_failure_data, message^, counters, {ignore} local_status);

    ivb_down := error_summary^.ivb_is_down;

  PROCEND log_ivb_pp_message;
?? OLDTITLE ??
?? NEWTITLE := 'log_mdi_pp_message', EJECT ??

  PROCEDURE log_mdi_pp_message
    (    network_device: ^nlt$network_device;
         unsolicited_response: ^nat$network_driver_response;
     VAR mdi_down: boolean);

{ The purpose of this procedure is to log the messages generated by the MDI PP.
{ The log information is setup in the detailed status portion of the PP response.


    CONST
      null_counter = -1;

    TYPE
      split_counter = record
        fill: 0 .. 0ffffffffffff(16),
        remaining_bytes: 0 .. 0ffff(16),
      recend;

    VAR
      assemble_counter: ^split_counter,
      channel_counter_word: ^channel_counter,
      channel_name: cmt$element_name,
      channel_number: ost$physical_channel_number,
      channel_port: cmt$channel_port,
      concurrent: boolean,
      counters: ^array [1 .. * ] of sft$counter,
      descriptor_data: ost$string,
      detailed_status: ^iot$detailed_status,
      detailed_status_bytes: ^0 .. 0ffff(16),
      detailed_status_word: ^integer,
      error_summary: ^nat$mdi_pp_log_response,
      i,
      k: integer,
      iou_number: dst$iou_number,
      local_status: ost$status,
      message: ^string ( * ),
      message_length: integer,
      number_of_counters: integer,
      pp_counter_word: ^pp_counter,
      physical_pp_number: 0 .. 31,
      symptom_code: integer,
      symptom_descriptor: string (max_symptom_length),
      symptom_descriptor_length: integer,
      size: integer;

    get_mdi_symptom_code (unsolicited_response, symptom_code, symptom_descriptor, symptom_descriptor_length);
    cmp$return_desc_data_by_lun_lpn (network_device^.logical_unit, network_device^.pp_number, iou_number,
          descriptor_data, physical_pp_number);
    detailed_status := unsolicited_response^.detailed_status_pointer;
    RESET detailed_status;
    NEXT error_summary IN detailed_status;
    IF error_summary^.detailed_status_included THEN
      number_of_counters := 19;
    ELSE
      number_of_counters := 15;
    IFEND;

    mdi_down := FALSE;
    PUSH counters: [1 .. number_of_counters];
    pp_counter_word := #LOC (counters^ [1]);
    pp_counter_word^.fill := 0;
    pp_counter_word^.iou := iou_number;
    pp_counter_word^.fill1 := 0;
    pp_counter_word^.pp := physical_pp_number;
    channel_counter_word := #LOC (counters^ [2]);
    channel_counter_word^.channel_error_status := 0;
    channel_counter_word^.fill := 0;
    channel_counter_word^.iou := iou_number;
    channel_counter_word^.fill1 := 0;
    channel_counter_word^.fill2 := 0;
    cmp$convert_channel_ordinal (network_device^.channel, channel_name, channel_number, concurrent,
          channel_port, local_status);
    IF NOT local_status.normal THEN
      nap$namve_system_error (FALSE, 'CMP$CONVERT_CHANNEL_ORDINAL error.', NIL);
    IFEND;
    pp_counter_word^.concurrent := concurrent;
    channel_counter_word^.concurrent := concurrent;
    channel_counter_word^.channel := channel_number;

    IF error_summary^.channel_error_flag THEN
      channel_counter_word^.channel_error_status := error_summary^.error_word1;
    IFEND;

    FOR k := 3 TO number_of_counters DO
      counters^ [k] := null_counter;
    FOREND;

    IF error_summary^.operation_kind <> nac$mdi_null_op_kind THEN
      counters^ [3] := $INTEGER (error_summary^.operation_kind);
    IFEND;

    counters^ [4] := $INTEGER (error_summary^.error_kind);
    counters^ [5] := symptom_code;

    IF error_summary^.error_kind <> nac$mdi_informative_message THEN
      counters^ [6] := error_summary^.retry_count;
    IFEND;

    IF (symptom_code = nac$sc_mdi_funct_timeout) OR (symptom_code = nac$sc_mdi_funct_timeout_cef) THEN
      counters^ [7] := error_summary^.timed_out_function;
    IFEND;

    IF (error_summary^.error_id = nac$mdi_gen_status_busy_timeout) OR
          (symptom_code = nac$sc_mdi_status_avail_timeout) THEN
      counters^ [8] := error_summary^.previous_function;
    IFEND;

    IF symptom_code = nac$sc_mdi_invalid_state_trans THEN
      counters^ [9] := error_summary^.error_word1;
    IFEND;

    IF (symptom_code = nac$sc_mdi_invalid_state_trans) OR
          (symptom_code = nac$sc_mdi_status_avail_timeout) THEN
      counters^ [10] := error_summary^.error_word2;
    IFEND;

    IF (symptom_code = nac$sc_mdi_message_length_error) OR (symptom_code = nac$sc_mdi_max_size_exceeded) THEN
      counters^ [11] := error_summary^.length_1;
      counters^ [12] := error_summary^.length_2;
    IFEND;

    IF (symptom_code = nac$sc_mdi_no_data_avail) OR (symptom_code = nac$sc_mdi_content_error) OR
          (symptom_code = nac$sc_mdi_status_content_error) THEN
      counters^ [13] := error_summary^.error_word2;
    IFEND;

    IF error_summary^.general_status_included THEN
      counters^ [14] := error_summary^.general_status;
    IFEND;

    IF symptom_code = nac$sc_mdi_channel_protocol_err THEN
      counters^ [15] := error_summary^.error_word1;
    IFEND;

    IF error_summary^.detailed_status_included THEN
      assemble_counter := #LOC (counters^ [16]);
      assemble_counter^.fill := 0;
      NEXT detailed_status_bytes IN detailed_status;
      assemble_counter^.remaining_bytes := detailed_status_bytes^;
      FOR i := 1 TO 3 DO
        NEXT detailed_status_word IN detailed_status;
        counters^ [16 + i] := detailed_status_word^;
      FOREND;
    IFEND;


{ Combine descriptor data with symptom message.

    IF (descriptor_data.size <= (252 - 4 - symptom_descriptor_length)) THEN
      size := descriptor_data.size;
    ELSE
      size := 252 - 4 - symptom_descriptor_length;
    IFEND;
    message_length := size + 4 + symptom_descriptor_length;
    PUSH message: [message_length];
    message^ (1, size) := descriptor_data.value;
    k := size + 1;
    CASE error_summary^.error_kind OF
    = nac$mdi_unrecovered_error =
      message^ (k, 4) := '*UF*';
    = nac$mdi_recovered_error =
      message^ (k, 4) := '*RF*';
    = nac$mdi_intermediate_error =
      message^ (k, 4) := '*IF*';
    = nac$mdi_informative_message =
      message^ (k, 4) := '*IM*';
    ELSE
    CASEND;
    k := k + 4;
    message^ (k, * ) := symptom_descriptor;
    sfp$emit_statistic (cml$mdi_failure_data, message^, counters, {ignore} local_status);

    mdi_down := error_summary^.mdi_is_down;

  PROCEND log_mdi_pp_message;
?? OLDTITLE ??
?? NEWTITLE := 'process_solicited_response', EJECT ??

  PROCEDURE process_solicited_response
    (    solicited_response: ^nat$network_driver_response);

{ The purpose of this procedure is to process the solicited responses from
{ the network PP.
{ NOTE: A local or permanent file must not be created or extended while
{       holding an exclusive access lock to the configured network device
{       list. This is to avoid NAM/VE users from being blocked in case the
{       local or permanent device is full.

    VAR
      element_descriptor: cmt$element_descriptor,
      iou_name: cmt$element_name,
      local_status: ost$status,
      network_device: ^nlt$network_device,
      synch_dump_sub_cmd_code: integer;

    CASE solicited_response^.command.command_code OF

    = ioc$cc_synchronize_pp, ioc$cc_resume =

{   Do nothing. These responses are directed to the completed output task so that the request blocks
{   associated with the requests can be freed in ring 1.

    = ioc$cc_idle =

      nlp$get_exclusive_access (nlv$configured_network_devices.access_control);

{ The 'priority' field in the pp response contains the network device identifier.

      network_device := ^nlv$configured_network_devices.network_device_list^
            [solicited_response^.pp_response.priority];

      CASE network_device^.state OF
      = nlc$normal =
        osp$set_status_abnormal (nac$status_id, nae$unexpected_pp_response, 'IDLE', local_status);
        nap$namve_system_error (FALSE, 'CPU-PP device state mismatch', ^local_status);
        nlp$release_exclusive_access (nlv$configured_network_devices.access_control);

      = nlc$state_change_pending =
        network_device^.path_status := nlc$path_unavailable;
        flush_unit_queue (network_device);

{ Terminate all active connections.

        nlp$release_exclusive_access (nlv$configured_network_devices.access_control);
        nlp$cc_terminate_connections (solicited_response^.pp_response.priority);
        nlp$get_exclusive_access (nlv$configured_network_devices.access_control);

{ Initiate state switch.

        IF network_device^.task_waiting_for_state_change.index <> 0 THEN
          pmp$ready_task (network_device^.task_waiting_for_state_change, {ignore} local_status);
          network_device^.task_waiting_for_state_change.index := 0;
          nlp$release_exclusive_access (nlv$configured_network_devices.access_control);
        ELSE
          cmp$get_element_type (network_device^.element, {not used} iou_name, element_descriptor.element_type,
                {ignore} local_status);
          element_descriptor.peripheral_descriptor.use_logical_identification := TRUE;
          element_descriptor.peripheral_descriptor.element_name := network_device^.element;
          nlp$release_exclusive_access (nlv$configured_network_devices.access_control);
          cmp$process_state_change ({tape_element=} FALSE, TRUE, element_descriptor,
                {System critical element} FALSE, cmc$on, cmc$down, local_status);
        IFEND;

      = nlc$closed =
        nlp$release_exclusive_access (nlv$configured_network_devices.access_control);
      ELSE
        osp$set_status_abnormal (nac$status_id, nae$unexpected_device_state, network_device^.element,
              local_status);
        nap$namve_system_error (FALSE, 'Invalid device state', ^local_status);
        nlp$release_exclusive_access (nlv$configured_network_devices.access_control);
      CASEND;

    ELSE
      osp$set_status_abnormal (nac$status_id, nae$unexpected_pp_response, 'SOLICITED', local_status);
      nap$namve_system_error (FALSE, 'Unexpected PP response', ^local_status);
    CASEND;

  PROCEND process_solicited_response;
?? OLDTITLE ??
?? NEWTITLE := 'process_unsolicited_response', EJECT ??

  PROCEDURE process_unsolicited_response
    (    unsolicited_response: ^nat$network_driver_response);

{ The purpose of this procedure is to process the unsolicited responses from
{ the network drivers i.e ICA and MDI PPs.

    VAR
      channel: cmt$channel_ordinal,
      command: iot$command,
      critical_msg: string (71),
      current_time: integer,
      detailed_status: ^iot$detailed_status,
      device_attributes: ^operational_device_attributes,
      device_kind: nat$device_type,
      device_reset_down_threshold: integer,
      down_device: boolean,
      element: cmt$element_name,
      i: integer,
      local_status: ost$status,
      logical_unit: iot$logical_unit,
      max_device_down_time: integer,
      max_supported_pdu: nlt$cc_pdu_size,
      network_device: ^nlt$network_device,
      pp_number: iot$pp_number;

    CASE unsolicited_response^.pp_response.unsolicited_response_code OF
    = ioc$unit_ready_to_not_ready, ioc$unit_not_ready_to_ready, ioc$device_operational =
      nlp$get_exclusive_access (nlv$configured_network_devices.access_control);
    ELSE
      nlp$get_nonexclusive_access (nlv$configured_network_devices.access_control);
    CASEND;

{ *** NOTE: the PRIORITY field in the pp response contains the network device identifier.

    network_device := ^nlv$configured_network_devices.network_device_list^
          [unsolicited_response^.pp_response.priority];

    IF (unsolicited_response^.pp_response.unsolicited_response_code = ioc$log_pp_message) OR
          ((network_device^.state <> nlc$state_change_pending) AND (network_device^.state <> nlc$closed)) THEN

      CASE unsolicited_response^.pp_response.unsolicited_response_code OF
      = ioc$unit_ready_to_not_ready =

{ The network access field in the configured dcn entry is changed via an exclusive access to the
{ configured dcn list.

        IF network_device^.state = nlc$normal THEN
          network_device^.path_status := nlc$path_unavailable;
          down_device := FALSE;
          network_device^.reset_timestamp := #FREE_RUNNING_CLOCK (0);

          CASE network_device^.kind OF
          = nac$di, nac$expresslink =

{ Display message in the critical window of the NOS/VE system console.

            critical_msg (1, 15) := 'Network Device ';
            critical_msg (16, * ) := network_device^.element;
            i := 15;
            REPEAT
              i := i + 1;
            UNTIL critical_msg (i, 1) = ' ';

            critical_msg (i + 1, 14) := 'is unavailable';
            dpp$put_critical_message (critical_msg, {ignore} local_status);

            device_reset_down_threshold := nav$mci_reset_down_threshold;

          = nac$ica_2 =
            device_reset_down_threshold := nav$ica_reset_down_threshold;
          ELSE
          CASEND;

          IF device_reset_down_threshold > 0 THEN
            network_device^.reset_down_count := network_device^.reset_down_count + 1;
            current_time := #FREE_RUNNING_CLOCK (0);
            down_device := (current_time <= network_device^.reset_down_count_intervl) AND
                  (network_device^.reset_down_count >= device_reset_down_threshold);
            IF (current_time >= network_device^.reset_down_count_intervl) AND (NOT down_device) THEN
              network_device^.reset_down_count_intervl := #FREE_RUNNING_CLOCK (0) + ten_min;
              network_device^.reset_down_count := 1;
            IFEND;
          IFEND;

          IF down_device THEN
            nap$idle_pp (network_device^.pp_number);
            network_device^.state := nlc$state_change_pending;
            device_kind := network_device^.kind;
            logical_unit := network_device^.logical_unit;
            pp_number := network_device^.pp_number;
            channel := network_device^.channel;
            nlp$release_exclusive_access (nlv$configured_network_devices.access_control);
            log_cpu_message (device_kind, logical_unit, pp_number, channel, device_reset_down_thresh_exceed);

{ Display message in the job log.

            osp$set_status_abnormal (nac$status_id, nae$device_reset_thresh_exceed, network_device^.element,
                  local_status);
            nap$display_message (local_status);
          ELSE

            flush_unit_queue (network_device);

{ Terminate all active channel connections.

            nlp$release_exclusive_access (nlv$configured_network_devices.access_control);
            nlp$cc_terminate_connections (unsolicited_response^.pp_response.priority);
            nlp$get_nonexclusive_access (nlv$configured_network_devices.access_control);

            command.command_code := ioc$cc_synchronize_pp;
            command.flags.store_response := TRUE;
            command.flags.indirect_address := FALSE;
            command.length := 0;
            command.address := 0;
            nap$issue_pp_request (network_device^.pp_number, command, NIL);
            nav$intranet_mgmt_timer_active := TRUE;
            nlp$release_nonexclusive_access (nlv$configured_network_devices.access_control);
          IFEND;
        ELSE { Kill the system - unrecoverable error
          nap$namve_system_error (FALSE, 'Unexpected PP response', NIL);
        IFEND;

      = ioc$unit_not_ready_to_ready =

{ Cancel the timer.

        network_device^.path_status := nlc$path_available;
        IF (network_device^.kind = nac$di) OR (network_device^.kind = nac$expresslink) THEN

{ Display message in the critical window of the NOS/VE system console.

          critical_msg (1, 15) := 'Network Device ';
          critical_msg (16, * ) := network_device^.element;
          i := 15;
          REPEAT
            i := i + 1;
          UNTIL critical_msg (i, 1) = ' ';

          critical_msg (i + 1, 12) := 'is available';
          dpp$put_critical_message (critical_msg, {ignore} local_status);
        IFEND;
        nlp$release_exclusive_access (nlv$configured_network_devices.access_control);

      = ioc$device_operational =
        IF network_device^.path_status <> nlc$path_down THEN
          IF (((network_device^.kind = nac$di) OR (network_device^.kind = nac$expresslink)) AND
                (network_device^.path_status = nlc$path_unavailable)) THEN

{ Display message in the critical window of the NOS/VE system console.

            critical_msg (1, 15) := 'Network Device ';
            critical_msg (16, * ) := network_device^.element;
            i := 15;
            REPEAT
              i := i + 1;
            UNTIL critical_msg (i, 1) = ' ';

            critical_msg (i + 1, 12) := 'is available';
            dpp$put_critical_message (critical_msg, {ignore} local_status);
          IFEND;

          network_device^.path_status := nlc$path_available;

{ Extract the channel interface protocol and the maximum channel connection PDU size.

          detailed_status := unsolicited_response^.detailed_status_pointer;
          RESET detailed_status;
          NEXT device_attributes IN detailed_status;
          network_device^.channel_interface_protocol := device_attributes^.channel_interface_protocol;
          network_device^.maximum_pdu_size := device_attributes^.maximum_pdu_size;
          IF (network_device^.kind = nac$di) OR (network_device^.kind = nac$expresslink) THEN
            network_device^.system_id := device_attributes^.system_id;
          IFEND;

          command.command_code := ioc$cc_synchronize_pp;
          command.flags.store_response := TRUE;
          command.flags.indirect_address := FALSE;
          command.length := 0;
          command.address := 0;
          nap$issue_pp_request (network_device^.pp_number, command, NIL);

          CASE network_device^.kind OF
          = nac$di, nac$ica_2 = { Support 6 buffers, possibly 4 large and 2 small
            max_supported_pdu := (4 * nlv$bm_large_buffer_size) + (2 * nlc$bm_small_buffer_size);
          = nac$expresslink = { Supports 8 large and 1 small buffers
            max_supported_pdu := (8 * nlv$bm_large_buffer_size) + (1 * nlc$bm_small_buffer_size);
          ELSE
            max_supported_pdu := 0;
          CASEND;
          IF network_device^.maximum_pdu_size > max_supported_pdu THEN
            osp$set_status_abnormal (nac$status_id, nae$data_unit_size_too_big, network_device^.element,
                  local_status);
            osp$append_status_integer (osc$status_parameter_delimiter, network_device^.maximum_pdu_size,
                  10, false, local_status);
            osp$append_status_integer (osc$status_parameter_delimiter, max_supported_pdu, 10, false,
                  local_status);
            osp$append_status_integer (osc$status_parameter_delimiter, osv$page_size, 10, false,
                  local_status);
            nap$display_message (local_status);
          IFEND;

          nlp$release_exclusive_access (nlv$configured_network_devices.access_control);

        ELSE { Kill the system - unrecoverable error
          nap$namve_system_error (FALSE, 'Unexpected PP response', NIL);
        IFEND;

      = ioc$log_pp_message =
        CASE network_device^.kind OF
        = nac$di =
          log_mdi_pp_message (network_device, unsolicited_response, down_device);
          device_reset_down_threshold := nav$mci_reset_down_threshold;
        = nac$ica_2 =
          log_ica_pp_message (network_device, unsolicited_response, down_device);
          device_reset_down_threshold := nav$ica_reset_down_threshold;
        = nac$expresslink =
          log_ivb_pp_message (network_device, unsolicited_response, down_device);
          device_reset_down_threshold := nav$mci_reset_down_threshold;
        ELSE
        CASEND;

        IF down_device THEN

{ Display message in the job log.

          osp$set_status_abnormal (nac$status_id, nae$device_down_via_pp, network_device^.element,
                local_status);
          nap$display_message (local_status);

{ Since the PP can generate a down indication for a device due to an ungraceful shutdown, couunt this
{ down request against the reset down threshold and then decide if the device should be downed.

          IF device_reset_down_threshold > 0 THEN
            network_device^.reset_down_count := network_device^.reset_down_count + 1;
            current_time := #FREE_RUNNING_CLOCK (0);
            down_device := (current_time <= network_device^.reset_down_count_intervl) AND
                  (network_device^.reset_down_count >= device_reset_down_threshold);
            IF (current_time >= network_device^.reset_down_count_intervl) AND (NOT down_device) THEN
              network_device^.reset_down_count_intervl := #FREE_RUNNING_CLOCK (0) + ten_min;
              network_device^.reset_down_count := 1;
            IFEND;
          ELSE
            down_device := FALSE;
          IFEND;

          IF down_device THEN
            IF (network_device^.state <> nlc$closed) AND (network_device^.state <> nlc$state_change_pending)
                  THEN

{ Display message in the job log.

              osp$set_status_abnormal (nac$status_id, nae$device_reset_thresh_exceed, network_device^.element,
                    local_status);
              nap$display_message (local_status);
              nap$idle_pp (network_device^.pp_number);
              network_device^.state := nlc$state_change_pending;
            IFEND;
          ELSE

            flush_unit_queue (network_device);

{ Terminate all active channel connections.

            nlp$release_nonexclusive_access (nlv$configured_network_devices.access_control);
            nlp$cc_terminate_connections (unsolicited_response^.pp_response.priority);
            nlp$get_nonexclusive_access (nlv$configured_network_devices.access_control);

{ Restart the PP.

            command.flags.store_response := TRUE;
            command.flags.indirect_address := FALSE;
            command.command_code := ioc$cc_resume;
            command.length := 0;
            command.address := 0;
            nap$issue_pp_request (network_device^.pp_number, command, NIL);
          IFEND;
        IFEND;
        nlp$release_nonexclusive_access (nlv$configured_network_devices.access_control);

      ELSE { Should never end up here - unrecoverable error
        nap$namve_system_error (FALSE, 'Unexpected unsolicited response.', NIL);
      CASEND;
    ELSE { Ignore the PP response
      CASE unsolicited_response^.pp_response.unsolicited_response_code OF
      = ioc$unit_not_ready_to_ready, ioc$unit_ready_to_not_ready, ioc$device_operational =
        nlp$release_exclusive_access (nlv$configured_network_devices.access_control);
      ELSE
        nlp$release_nonexclusive_access (nlv$configured_network_devices.access_control);
      CASEND;
    IFEND;

  PROCEND process_unsolicited_response;
?? OLDTITLE ??
 MODEND nam$intranet_layer_mgmt_r3;
