?? NEWTITLE := 'NOS/VE Network Access : Channel Connection Entity' ??
MODULE nlm$channel_connection_entity;
?? RIGHT := 110 ??

{ PURPOSE:
{   The purpose of this module is to contain all NAM/VE Channel Connection
{   internal requests.
{
{ DESIGN:
{   This module is designed to be contained on the OSF$JOB_TEMPLATE_23D library
{   and may execute in any task.
{
{   The Channel Connection protocol multiplexes a set of independent connections
{   over a common physical channel. Each connection may be used by a pair of peer
{   processes (one residing in NOS/VE, and the other in an OSI communications device)
{   to exchange messages. Each Channel Connection provides a reliable data path,
{   ensuring both data integrity as well as proper data sequencing. Data transfer
{   on any one connection is regulated via flow control mechanisms to prevent any
{   one connection from consuming an inordinate amount of system resources. The
{   protocol also provides for two connection classes: 'normal' and 'priority', with
{   priority class connections receiving preferential treatment. The complete
{   Channel Connection protocol specification can be found in DCS document A7882.
{
{   The Channel Connection Entity performs the following services for its users:
{
{      - Connection Establishment
{
{      - Data Transfer
{
{      - Expedited Data Transfer
{
{      - Connection Termination
{
?? NEWTITLE := 'Global Declarations Referenced by this Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc nae$application_interfaces
*copyc nae$namve_conditions
*copyc nlt$bm_message_id
*copyc nlt$cc_address
*copyc nlt$cc_aggregate_message
*copyc nlt$cc_connection
*copyc nlt$cc_connection_class
*copyc nlt$cc_device_and_data_list
*copyc nlt$cl_connection
*copyc nlt$device_list
*copyc ost$status
?? POP ??
*copyc nap$send_network_packet
*copyc nlp$bm_add_message_prefix
*copyc nlp$bm_build_pva_list
*copyc nlp$bm_copy_message
*copyc nlp$bm_create_message
*copyc nlp$bm_get_message_length
*copyc nlp$bm_release_message
*copyc nlp$cc_get_event_processor
*copyc nlp$cc_incr_connection_count
*copyc nlp$cc_obtain_credits
*copyc nlp$cc_shut_down_connection
*copyc nlp$cc_user_data_pad_size
*copyc nlp$cl_activate_layer
*copyc nlp$cl_add_device_to_connection
*copyc nlp$cl_deactivate_layer
*copyc nlp$cl_decr_priority_connection
*copyc nlp$cl_get_layer_connection
*copyc nlp$cl_incr_priority_connection
*copyc nlp$cl_initialize_template
*copyc nlp$get_nonexclusive_access
*copyc nlp$release_nonexclusive_access
*copyc osp$append_status_integer
*copyc osp$decrement_locked_variable
*copyc osp$increment_locked_variable
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc syp$cycle
*copyc oss$job_paged_literal
*copyc nav$global_osi_statistics
*copyc nav$network_paged_heap
*copyc nav$statistics_enabled
*copyc nlv$bm_large_buffer_size
*copyc nlv$configured_network_devices
*copyc nlv$cc_initialize_connection
?? TITLE := 'Global Declarations Declared by this Module', EJECT ??

  TYPE
    connection_states = set of nlt$cc_connection_state;

  CONST
    cc_max_send_buffer_size = 500;

  VAR
    channel_connection: [READ, oss$job_paged_literal] string (18) := 'Channel Connection';

?? TITLE := '[XDCL] nlp$cc_accept_connection', EJECT ??
?? NEWTITLE := '12.  <nlc$cc_connect_response_wait>  --->  <nlc$cc_open>' ??
*copy nlh$cc_accept_connection

  PROCEDURE [XDCL] nlp$cc_accept_connection
    (    cl_connection: ^nlt$cl_connection;
         connection_class: nlt$cc_connection_class;
         data: nlt$bm_message_id;
     VAR status: ost$status);

    VAR
      accept_data: nlt$bm_message_id,
      actual: integer,
      cc_header: nlt$cc_protocol_header_with_pad,
      cc_header_and_pad_length: nlt$cc_pdu_size,
      connection: ^nlt$cc_connection,
      data_length: integer,
      layer_active: boolean,
      statistic1: ^integer,
      statistic2: ^integer;

    status.normal := TRUE;
    accept_data := data;
    nlp$cl_get_layer_connection (nlc$channel_connection_layer, cl_connection, layer_active, connection);
    IF (layer_active) AND (connection^.device_specific_attributes.state = nlc$cc_connect_response_wait) THEN
      nlp$bm_get_message_length (accept_data, data_length);
      IF data_length <= connection^.device_specific_attributes.maximum_data_length THEN
        IF connection_class <> connection^.class THEN
          IF connection_class = nlc$cc_normal_class THEN
            nlp$cl_decr_priority_connection;
          ELSE
            nlp$cl_incr_priority_connection;
          IFEND;
          connection^.class := connection_class;
        IFEND;
        connection^.receive_credits := nlp$cc_obtain_credits (connection);
        IF connection^.receive_credits = 0 THEN

{  Give at least one credit to the peer.

          connection^.receive_credits := 1;
        IFEND;
        connection^.device_specific_attributes.state := nlc$cc_open;
        cc_header.kind := nlc$cc_connect_confirm;
        cc_header.connect_confirm.user_data_pad_size := nlp$cc_user_data_pad_size (data_length);
        cc_header_and_pad_length := #SIZE (nlt$cc_protocol_header) +
              cc_header.connect_confirm.user_data_pad_size;
        cc_header.length := data_length + cc_header_and_pad_length;
        cc_header.connect_confirm.destination_reference := connection^.peer_reference_number;
        cc_header.connect_confirm.initial_credit_allocation := connection^.receive_credits;
        cc_header.connect_confirm.source_reference := connection^.connection_id.reference_number;
        cc_header.connect_confirm.header_pad := 0;
        cc_header.connect_confirm.class := connection_class;
        nlp$bm_add_message_prefix (^cc_header, cc_header_and_pad_length, accept_data);
        nlp$cc_send_pdu (connection^.device_specific_attributes.device_id, connection_class, accept_data);

{! statistics begin}

        IF nav$statistics_enabled THEN
          IF connection^.class = nlc$cc_normal_class THEN
            statistic1 := ^nav$global_osi_statistics.channel_connection_device^
                  [connection^.device_specific_attributes.device_id].current_normal_connections;
            statistic2 := ^nav$global_osi_statistics.channel_connection.normal_connections;
          ELSE { IF connection^.class = nlc$cc_priority_class THEN
            statistic1 := ^nav$global_osi_statistics.channel_connection_device^
                  [connection^.device_specific_attributes.device_id].current_priority_connections;
            statistic2 := ^nav$global_osi_statistics.channel_connection.priority_connections;
          IFEND;
          osp$increment_locked_variable (statistic1^, 0, actual);
          osp$increment_locked_variable (statistic2^, 0, actual);
        IFEND;

{! statistics end}

      ELSE
        nlp$bm_release_message (accept_data);
        osp$set_status_condition (nae$max_data_length_exceeded, status);
        osp$append_status_integer (osc$status_parameter_delimiter,
              connection^.device_specific_attributes.maximum_data_length, 10, FALSE, status);
      IFEND;
    ELSE
      nlp$bm_release_message (accept_data);
      osp$set_status_abnormal (nac$status_id, nae$connection_not_proposed, channel_connection, status);
    IFEND;

  PROCEND nlp$cc_accept_connection;
?? OLDTITLE ??
?? TITLE := '[XDCL] nlp$cc_disconnect', EJECT ??
?? NEWTITLE := '5.  <nlc$cc_connect_confirm_wait>  --->  <nlc$cc_closing>' ??
?? NEWTITLE := '11.  <nlc$cc_connect_response_wait>  --->  <nlc$cc_closed>' ??
?? NEWTITLE := '25.  <nlc$cc_open>  --->  <nlc$cc_closing>' ??
*copy nlh$cc_disconnect

  PROCEDURE [XDCL] nlp$cc_disconnect
    (    cl_connection: ^nlt$cl_connection;
         data: nlt$bm_message_id;
     VAR status: ost$status);

    VAR
      actual: integer,
      cc_header: nlt$cc_protocol_header_with_pad,
      cc_header_and_pad_length: nlt$cc_pdu_size,
      connection: ^nlt$cc_connection,
      data_length: integer,
      disconnect_data: nlt$bm_message_id,
      disconnect_pdu: nlt$bm_message_id,
      i: integer,
      ignore_error: boolean,
      layer_active: boolean,
      statistic1: ^integer,
      statistic2: ^integer;

    status.normal := TRUE;
    disconnect_data := data;
    nlp$cl_get_layer_connection (nlc$channel_connection_layer, cl_connection, layer_active, connection);
    IF (layer_active) AND (connection^.device_specific_attributes.state IN
          $connection_states [nlc$cc_connect_confirm_wait, nlc$cc_connect_response_wait, nlc$cc_open]) THEN
      nlp$bm_get_message_length (disconnect_data, data_length);
      IF data_length <= connection^.device_specific_attributes.maximum_data_length THEN
        cc_header.kind := nlc$cc_disconnect_request;
        cc_header.disconnect_request.class := connection^.class;
        cc_header.disconnect_request.user_data_pad_size := nlp$cc_user_data_pad_size (data_length);
        cc_header_and_pad_length := #SIZE (nlt$cc_protocol_header) +
              cc_header.disconnect_request.user_data_pad_size;
        cc_header.length := data_length + cc_header_and_pad_length;
        cc_header.disconnect_request.destination_reference := connection^.peer_reference_number;
        cc_header.disconnect_request.reason := nlc$cc_dr_normal_disconnect;
        cc_header.disconnect_request.source_reference := connection^.connection_id.reference_number;
        nlp$bm_add_message_prefix (^cc_header, cc_header_and_pad_length, disconnect_data);
        CASE connection^.device_specific_attributes.state OF
        = nlc$cc_open =
          nlp$cc_send_pdu (connection^.device_specific_attributes.device_id, connection^.class,
                disconnect_data);
          connection^.device_specific_attributes.state := nlc$cc_closing;

{! statistics begin}

          IF nav$statistics_enabled THEN
            IF connection^.class = nlc$cc_normal_class THEN
              statistic1 := ^nav$global_osi_statistics.channel_connection_device^
                    [connection^.device_specific_attributes.device_id].current_normal_connections;
              statistic2 := ^nav$global_osi_statistics.channel_connection.normal_connections;
            ELSE { IF connection^.class = nlc$cc_priority_class THEN
              statistic1 := ^nav$global_osi_statistics.channel_connection_device^
                    [connection^.device_specific_attributes.device_id].current_priority_connections;
              statistic2 := ^nav$global_osi_statistics.channel_connection.priority_connections;
            IFEND;
            osp$decrement_locked_variable (statistic1^, 1, actual, ignore_error);
            osp$decrement_locked_variable (statistic2^, 1, actual, ignore_error);
          IFEND;

{! statistics end}

        = nlc$cc_connect_response_wait =
          connection^.device_specific_attributes.state := nlc$cc_closed;
          nlp$cc_send_pdu (connection^.device_specific_attributes.device_id, connection^.class,
                disconnect_data);
          nlp$cc_shut_down_connection (connection, cl_connection,
                connection^.device_specific_attributes.device_id);

        = nlc$cc_connect_confirm_wait =
          IF connection^.sub_connection_count = 0 THEN
            nlp$cc_send_pdu (connection^.device_specific_attributes.device_id, connection^.class,
                  disconnect_data);
            connection^.device_specific_attributes.state := nlc$cc_closing;
          ELSE

{  The sub_connection_count is non-zero, therefore the "main" connection is not in use
{  and will be marked as CLOSED. All waiting "subconnections" will be disconnected.

            connection^.device_specific_attributes.state := nlc$cc_closed;
            FOR i := 1 TO UPPERBOUND (connection^.sub_connections^) DO
              IF connection^.sub_connections^ [i].state = nlc$cc_connect_confirm_wait THEN
                nlp$bm_copy_message (disconnect_data, disconnect_pdu);
                nlp$cc_send_pdu (connection^.sub_connections^ [i].device_id, connection^.class,
                      disconnect_pdu);
                connection^.sub_connections^ [i].state := nlc$cc_closing;
              IFEND;
            FOREND;
            nlp$bm_release_message (disconnect_data);
          IFEND;
        ELSE
        CASEND;
      ELSE
        nlp$bm_release_message (disconnect_data);
        osp$set_status_condition (nae$max_data_length_exceeded, status);
        osp$append_status_integer (osc$status_parameter_delimiter,
              connection^.device_specific_attributes.maximum_data_length, 10, FALSE, status);
      IFEND;
    ELSE
      nlp$bm_release_message (disconnect_data);
      osp$set_status_abnormal (nac$status_id, nae$connection_not_open, channel_connection, status);
    IFEND;

  PROCEND nlp$cc_disconnect;
?? OLDTITLE, OLDTITLE, OLDTITLE ??
?? TITLE := '[XDCL] nlp$cc_initialize_template', EJECT ??
*copy nlh$cc_initialize_template

  PROCEDURE [XDCL] nlp$cc_initialize_template
    (    application_layer: nlt$cl_application_layer);

    VAR
      nil_event_processor: nlt$cl_event_processor;

    nil_event_processor.layer := nlc$channel_connection_layer;
    nil_event_processor.cc := nac$nil;
    nlp$cl_initialize_template (application_layer, nlc$channel_connection_layer, #SIZE (nlt$cc_connection), 0,
          nil_event_processor, nac$nil, nil_event_processor, nlc$cc_monitor_timers);

  PROCEND nlp$cc_initialize_template;
?? TITLE := '[XDCL] nlp$cc_request_connection', EJECT ??
?? NEWTITLE := '1.  <nlc$cc_closed>  --->  <nlc$cc_connect_confirm_wait>' ??
*copy nlh$cc_request_connection

  PROCEDURE [XDCL] nlp$cc_request_connection
    (    cl_connection: ^nlt$cl_connection;
         device_and_data_list: nlt$cc_device_and_data_list;
         destination_address: nlt$cc_address;
         connection_class: nlt$cc_connection_class;
     VAR status: ost$status);

    VAR
      actual: integer,
      cc_header: nlt$cc_protocol_header_with_pad,
      cc_header_and_pad_length: nlt$cc_pdu_size,
      connection: ^nlt$cc_connection,
      connect_data: nlt$bm_message_id,
      data_length: integer,
      data_lengths: ^array [1 .. * ] of integer,
      device_count: nlt$device_count,
      i: integer,
      ignore_layer_active: boolean,
      minimum_device_max_data_length: nlt$cc_pdu_size,
      network_device_list: ^nlt$network_device_list;

    status.normal := TRUE;
    device_count := UPPERBOUND (device_and_data_list);
    nlp$cl_get_layer_connection (nlc$channel_connection_layer, cl_connection, ignore_layer_active,
          connection);
    connection^ := nlv$cc_initialize_connection;
    connection^.class := connection_class;
    connection^.receive_credits := nlp$cc_obtain_credits (connection);
    IF connection^.receive_credits > 0 THEN
      connection^.connection_id := cl_connection^.identifier;
      nlp$cc_get_event_processor (destination_address, connection^.event_processor);
      cc_header.kind := nlc$cc_connect_request;
      cc_header.connect_request.destination_address := destination_address;
      cc_header.connect_request.initial_credit_allocation := connection^.receive_credits;
      cc_header.connect_request.source_reference := connection^.connection_id.reference_number;
      cc_header.connect_request.header_pad := 0;
      cc_header.connect_request.class := connection_class;
      IF device_count = 1 THEN
        connect_data := device_and_data_list [1].data;
        nlp$bm_get_message_length (connect_data, data_length);
        cc_header.connect_request.user_data_pad_size := nlp$cc_user_data_pad_size (data_length);
        cc_header_and_pad_length := #SIZE (nlt$cc_protocol_header) +
              cc_header.connect_request.user_data_pad_size;
        cc_header.length := data_length + cc_header_and_pad_length;
        nlp$bm_add_message_prefix (^cc_header, cc_header_and_pad_length, connect_data);

        nlp$get_nonexclusive_access (nlv$configured_network_devices.access_control);
        network_device_list := nlv$configured_network_devices.network_device_list;
        connection^.device_specific_attributes.maximum_data_length :=
              network_device_list^ [device_and_data_list [1].device_id].
              maximum_pdu_size - #SIZE (nlt$cc_protocol_header);
        connection^.buffers_per_credit := connection^.device_specific_attributes.maximum_data_length DIV
              nlv$bm_large_buffer_size;
        IF (data_length <= connection^.device_specific_attributes.maximum_data_length) AND
              (network_device_list^ [device_and_data_list [1].device_id].path_status =
              nlc$path_available) THEN
          connection^.device_specific_attributes.state := nlc$cc_connect_confirm_wait;
          connection^.device_specific_attributes.device_id := device_and_data_list [1].device_id;
          nlp$cl_add_device_to_connection (device_and_data_list [1].device_id, cl_connection);
          nlp$cc_incr_connection_count (device_and_data_list [1].device_id);
          nlp$cl_activate_layer (nlc$channel_connection_layer, cl_connection);
          send_cc_connect_request (device_and_data_list [1].device_id, connection_class, connect_data);
        ELSEIF (network_device_list^ [device_and_data_list [1].device_id].path_status <>
              nlc$path_available) THEN
          nlp$bm_release_message (connect_data);
          osp$set_status_condition (nae$destination_not_reachable, status);
        ELSE
          nlp$bm_release_message (connect_data);
          osp$set_status_condition (nae$max_data_length_exceeded, status);
          osp$append_status_integer (osc$status_parameter_delimiter,
                connection^.device_specific_attributes.maximum_data_length, 10, FALSE, status);
        IFEND;
        nlp$release_nonexclusive_access (nlv$configured_network_devices.access_control);
      ELSE { Broadcast connect request.
        ALLOCATE connection^.sub_connections: [1 .. device_count] IN nav$network_paged_heap^;
        IF connection^.sub_connections <> NIL THEN
          PUSH data_lengths: [1 .. device_count];
          nlp$get_nonexclusive_access (nlv$configured_network_devices.access_control);
          network_device_list := nlv$configured_network_devices.network_device_list;

{   Before any connect requests are sent, each device's maximum CCPDU size must
{   be checked to ensure the connect request does not exceed the maximum. If the
{   outgoing connect request is larger than ANY one device's maximum, then the request
{   will be aborted and no connect requests will be sent.

        /check_device_pdu_maximum/
          FOR i := 1 TO device_count DO
            connection^.sub_connections^ [i].maximum_data_length :=
                  network_device_list^ [device_and_data_list [i].device_id].
                  maximum_pdu_size - #SIZE (nlt$cc_protocol_header);
            connection^.sub_connections^ [i].state := nlc$cc_closed;
            nlp$bm_get_message_length (device_and_data_list [i].data, data_lengths^ [i]);
            IF data_lengths^ [i] > connection^.sub_connections^ [i].maximum_data_length THEN
              FREE connection^.sub_connections IN nav$network_paged_heap^;
              osp$set_status_condition (nae$max_data_length_exceeded, status);
              osp$append_status_integer (osc$status_parameter_delimiter,
                    connection^.sub_connections^ [i].maximum_data_length, 10, FALSE, status);
              EXIT /check_device_pdu_maximum/; {----->
            IFEND;
          FOREND /check_device_pdu_maximum/;

          IF status.normal THEN
            nlp$cl_activate_layer (nlc$channel_connection_layer, cl_connection);
            minimum_device_max_data_length := nlc$cc_max_pdu_size;
            FOR i := 1 TO device_count DO
              connect_data := device_and_data_list [i].data;
              cc_header.connect_request.user_data_pad_size := nlp$cc_user_data_pad_size (data_lengths^ [i]);
              cc_header_and_pad_length := #SIZE (nlt$cc_protocol_header) +
                    cc_header.connect_request.user_data_pad_size;
              cc_header.length := data_lengths^ [i] + cc_header_and_pad_length;
              nlp$bm_add_message_prefix (^cc_header, cc_header_and_pad_length, connect_data);
              IF (network_device_list^ [device_and_data_list [i].device_id].path_status =
                    nlc$path_available) THEN
                connection^.sub_connections^ [i].state := nlc$cc_connect_confirm_wait;
                connection^.sub_connections^ [i].device_id := device_and_data_list [i].device_id;
                nlp$cl_add_device_to_connection (device_and_data_list [i].device_id, cl_connection);
                connection^.sub_connection_count := connection^.sub_connection_count + 1;
                IF connection^.sub_connections^ [i].maximum_data_length < minimum_device_max_data_length THEN
                  minimum_device_max_data_length := connection^.sub_connections^ [i].maximum_data_length;
                IFEND;
                nlp$cc_incr_connection_count (device_and_data_list [i].device_id);
                send_cc_connect_request (device_and_data_list [i].device_id, connection_class, connect_data);
              ELSE
                nlp$bm_release_message (connect_data);
                osp$set_status_condition (nae$destination_not_reachable, status);
              IFEND;
            FOREND;
            IF connection^.sub_connection_count > 0 THEN
              connection^.device_specific_attributes.state := nlc$cc_connect_confirm_wait;
              connection^.device_specific_attributes.maximum_data_length := minimum_device_max_data_length;
              status.normal := TRUE;

{! statistics begin}

              IF nav$statistics_enabled THEN
                osp$increment_locked_variable (nav$global_osi_statistics.channel_connection.
                      broadcast_connect_requests, 0, actual);
              IFEND;

{! statistics end}

            ELSE
              nlp$cl_deactivate_layer (nlc$channel_connection_layer, cl_connection);
              FREE connection^.sub_connections IN nav$network_paged_heap^;
            IFEND;
          ELSE
            FOR i := 1 TO device_count DO
              connect_data := device_and_data_list [i].data;
              nlp$bm_release_message (connect_data);
            FOREND;
          IFEND;
          nlp$release_nonexclusive_access (nlv$configured_network_devices.access_control);
        ELSE { connection^.sub_connections = NIL }
          FOR i := 1 TO device_count DO
            connect_data := device_and_data_list [i].data;
            nlp$bm_release_message (connect_data);
          FOREND;
          osp$set_status_condition (nae$resources_unavailable, status);
        IFEND;
      IFEND;
      IF status.normal AND (connection_class = nlc$cc_priority_class) THEN
        nlp$cl_incr_priority_connection;
      IFEND;
    ELSE { No credits available for peer. }
      FOR i := 1 TO device_count DO
        connect_data := device_and_data_list [i].data;
        nlp$bm_release_message (connect_data);
      FOREND;
      osp$set_status_condition (nae$resources_unavailable, status);
    IFEND;

  PROCEND nlp$cc_request_connection;
?? OLDTITLE ??
?? TITLE := '[XDCL] nlp$cc_send_data', EJECT ??
?? NEWTITLE := '17.  <nlc$cc_open>  --->  <nlc$cc_open>' ??
*copy nlh$cc_send_data

  PROCEDURE [XDCL] nlp$cc_send_data
    (    cl_connection: ^nlt$cl_connection;
         data: nlt$bm_message_id;
     VAR status: ost$status);

    VAR
      actual: integer,
      cc_header: nlt$cc_protocol_header_with_pad,
      cc_header_and_pad_length: nlt$cc_pdu_size,
      connection: ^nlt$cc_connection,
      data_length: integer,
      layer_active: boolean,
      send_data: nlt$bm_message_id,
      statistic: ^integer;

    status.normal := TRUE;
    send_data := data;
    nlp$cl_get_layer_connection (nlc$channel_connection_layer, cl_connection, layer_active, connection);
    IF (layer_active) AND (connection^.device_specific_attributes.state = nlc$cc_open) THEN
      nlp$bm_get_message_length (send_data, data_length);
      IF data_length <= connection^.device_specific_attributes.maximum_data_length THEN
        IF connection^.send_credits > 0 THEN
          cc_header.kind := nlc$cc_data;
          cc_header.data.user_data_pad_size := nlp$cc_user_data_pad_size (data_length);
          cc_header_and_pad_length := #SIZE (nlt$cc_protocol_header) + cc_header.data.user_data_pad_size;
          cc_header.length := data_length + cc_header_and_pad_length;
          cc_header.data.destination_reference := connection^.peer_reference_number;
          cc_header.data.class := connection^.class;
          cc_header.data.credits_granted := nlp$cc_obtain_credits (connection);
          connection^.receive_credits := connection^.receive_credits + cc_header.data.credits_granted;

{ Store the current send credits value in the CC header, this is NOT part of the CC protocol and is done
{ ONLY to provide more information in the event of a peer detected flow control violation.

          cc_header.data.header_pad := connection^.send_credits;

          nlp$bm_add_message_prefix (^cc_header, cc_header_and_pad_length, send_data);
          nlp$cc_send_pdu (connection^.device_specific_attributes.device_id, connection^.class, send_data);
          connection^.send_credits := connection^.send_credits - 1;
        ELSE
          store_cc_pdu (cl_connection^.layers_active, send_data, connection^.send_buffer);

{! statistics begin}

          IF nav$statistics_enabled THEN
            IF connection^.class = nlc$cc_normal_class THEN
              statistic := ^nav$global_osi_statistics.channel_connection_device^
                    [connection^.device_specific_attributes.device_id].normal_send_pdus_queued;
            ELSE { IF connection^.class = nlc$cc_priority_class THEN
              statistic := ^nav$global_osi_statistics.channel_connection_device^
                    [connection^.device_specific_attributes.device_id].priority_send_pdus_queued;
            IFEND;
            osp$increment_locked_variable (statistic^, 0, actual);
          IFEND;

{! statistics end}

        IFEND;
      ELSE
        nlp$bm_release_message (send_data);
        osp$set_status_condition (nae$max_data_length_exceeded, status);
        osp$append_status_integer (osc$status_parameter_delimiter,
              connection^.device_specific_attributes.maximum_data_length, 10, FALSE, status);
      IFEND;
    ELSE
      nlp$bm_release_message (send_data);
      osp$set_status_abnormal (nac$status_id, nae$connection_not_open, channel_connection, status);
    IFEND;

  PROCEND nlp$cc_send_data;
?? OLDTITLE ??
?? TITLE := '[XDCL] nlp$cc_send_data_fragments', EJECT ??
?? NEWTITLE := '17.  <nlc$cc_open>  --->  <nlc$cc_open>' ??
*copy nlh$cc_send_data_fragments

  PROCEDURE [XDCL] nlp$cc_send_data_fragments
    (    cl_connection: ^nlt$cl_connection;
         data: nat$data_fragments;
     VAR status: ost$status);

    VAR
      actual: integer,
      cc_header: nlt$cc_protocol_header_with_pad,
      cc_header_and_pad_length: nlt$cc_pdu_size,
      connection: ^nlt$cc_connection,
      data_length: integer,
      i: integer,
      layer_active: boolean,
      output_message: ^nat$data_fragments,
      send_data: nlt$bm_message_id,
      statistic: ^integer;

    status.normal := TRUE;
    nlp$cl_get_layer_connection (nlc$channel_connection_layer, cl_connection, layer_active, connection);
    IF (layer_active) AND (connection^.device_specific_attributes.state = nlc$cc_open) THEN
      data_length := 0;
      FOR i := LOWERBOUND (data) TO UPPERBOUND (data) DO
        IF (data [i].address <> NIL) THEN
          data_length := data_length + data [i].length;
        IFEND;
      FOREND;
      IF data_length <= connection^.device_specific_attributes.maximum_data_length THEN
        IF connection^.send_credits > 0 THEN
          cc_header.kind := nlc$cc_data;
          cc_header.data.user_data_pad_size := nlp$cc_user_data_pad_size (data_length);
          cc_header_and_pad_length := #SIZE (nlt$cc_protocol_header) + cc_header.data.user_data_pad_size;
          cc_header.length := data_length + cc_header_and_pad_length;
          cc_header.data.destination_reference := connection^.peer_reference_number;
          cc_header.data.class := connection^.class;
          cc_header.data.credits_granted := nlp$cc_obtain_credits (connection);
          connection^.receive_credits := connection^.receive_credits + cc_header.data.credits_granted;

{ Store the current send credits value in the CC header, this is NOT part of the CC protocol and is done
{ ONLY to provide more information in the event of a peer detected flow control violation.

          cc_header.data.header_pad := connection^.send_credits;

          PUSH output_message: [1 .. (UPPERBOUND (data) + 1)];
          output_message^ [1].address := ^cc_header;
          output_message^ [1].length := cc_header_and_pad_length;

          FOR i := 2 TO UPPERBOUND (output_message^) DO
            output_message^ [i] := data [i - 1];
          FOREND;
          nlp$bm_create_message (output_message^, send_data, status);
          nlp$cc_send_pdu (connection^.device_specific_attributes.device_id, connection^.class, send_data);
          connection^.send_credits := connection^.send_credits - 1;
        ELSE
          nlp$bm_create_message (data, send_data, status);
          store_cc_pdu (cl_connection^.layers_active, send_data, connection^.send_buffer);

{! statistics begin}

          IF nav$statistics_enabled THEN
            IF connection^.class = nlc$cc_normal_class THEN
              statistic := ^nav$global_osi_statistics.channel_connection_device^
                    [connection^.device_specific_attributes.device_id].normal_send_pdus_queued;
            ELSE { IF connection^.class = nlc$cc_priority_class THEN
              statistic := ^nav$global_osi_statistics.channel_connection_device^
                    [connection^.device_specific_attributes.device_id].priority_send_pdus_queued;
            IFEND;
            osp$increment_locked_variable (statistic^, 0, actual);
          IFEND;

{! statistics end}

        IFEND;
      ELSE
        osp$set_status_condition (nae$max_data_length_exceeded, status);
        osp$append_status_integer (osc$status_parameter_delimiter,
              connection^.device_specific_attributes.maximum_data_length, 10, FALSE, status);
      IFEND;
    ELSE
      osp$set_status_abnormal (nac$status_id, nae$connection_not_open, channel_connection, status);
    IFEND;

  PROCEND nlp$cc_send_data_fragments;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$cc_send_aggregate_message', EJECT ??

  PROCEDURE [XDCL] nlp$cc_send_aggregate_message
    (    cl_connection: ^nlt$cl_connection;
         message: nlt$cc_aggregate_message;
     VAR status: ost$status);

*copy nlh$cc_send_aggregate_message

    VAR
      actual: integer,
      cc_header: nlt$cc_protocol_header_with_pad,
      cc_header_and_pad_length: nlt$cc_pdu_size,
      connection: ^nlt$cc_connection,
      data: nlt$bm_message_id,
      data_length: integer,
      i: integer,
      layer_active: boolean,
      statistic: ^integer;

?? NEWTITLE := 'release_aggregates', EJECT ??

    PROCEDURE release_aggregates
      (    message: nlt$cc_aggregate_message);

      VAR
        i: integer,
        release_message: nlt$bm_message_id;

      FOR i := LOWERBOUND (message) TO UPPERBOUND (message) DO
        CASE message [i].kind OF
        = nlc$cc_data_event =
          release_message := message [i].data;
          nlp$bm_release_message (release_message);
        = nlc$cc_expedited_data_event =
          release_message := message [i].expedited_data;
          nlp$bm_release_message (release_message);
        ELSE
          ;
        CASEND;
      FOREND;

    PROCEND release_aggregates;
?? OLDTITLE ??
?? EJECT ??
    status.normal := TRUE;
    nlp$cl_get_layer_connection (nlc$channel_connection_layer, cl_connection, layer_active, connection);
    IF (layer_active) AND (connection^.device_specific_attributes.state = nlc$cc_open) THEN
      i := 1;
      WHILE (status.normal AND (i <= UPPERBOUND (message))) DO
        CASE message [i].kind OF
        = nlc$cc_data_event =
          nlp$bm_get_message_length (message [i].data, data_length);
          IF data_length > connection^.device_specific_attributes.maximum_data_length THEN
            osp$set_status_condition (nae$max_data_length_exceeded, status);
            osp$append_status_integer (osc$status_parameter_delimiter,
                  connection^.device_specific_attributes.maximum_data_length, 10, FALSE, status);
          IFEND;
        = nlc$cc_expedited_data_event =
          nlp$bm_get_message_length (message [i].expedited_data, data_length);
          IF data_length > connection^.device_specific_attributes.maximum_data_length THEN
            osp$set_status_condition (nae$max_data_length_exceeded, status);
            osp$append_status_integer (osc$status_parameter_delimiter,
                  connection^.device_specific_attributes.maximum_data_length, 10, FALSE, status);
          IFEND;
        ELSE
          osp$set_status_condition (nae$improper_aggregate_kind, status);
        CASEND;
        i := i + 1;
      WHILEND;

      IF status.normal THEN
        FOR i := 1 TO UPPERBOUND (message) DO
          CASE message [i].kind OF
          = nlc$cc_data_event =
            data := message [i].data;
            IF connection^.send_credits > 0 THEN
              nlp$bm_get_message_length (message [i].data, data_length);
              cc_header.kind := nlc$cc_data;
              cc_header.data.user_data_pad_size := nlp$cc_user_data_pad_size (data_length);
              cc_header_and_pad_length := #SIZE (nlt$cc_protocol_header) + cc_header.data.user_data_pad_size;
              cc_header.length := data_length + cc_header_and_pad_length;
              cc_header.data.destination_reference := connection^.peer_reference_number;
              cc_header.data.class := connection^.class;
              cc_header.data.credits_granted := nlp$cc_obtain_credits (connection);
              connection^.receive_credits := connection^.receive_credits + cc_header.data.credits_granted;

{ Store the current send credits value in the CC header, this is NOT part of the CC protocol and is done
{ ONLY to provide more information in the event of a peer detected flow control violation.

              cc_header.data.header_pad := connection^.send_credits;

              nlp$bm_add_message_prefix (^cc_header, cc_header_and_pad_length, data);
              nlp$cc_send_pdu (connection^.device_specific_attributes.device_id, connection^.class, data);
              connection^.send_credits := connection^.send_credits - 1;
            ELSE
              store_cc_pdu (cl_connection^.layers_active, data, connection^.send_buffer);

{! statistics begin}

              IF nav$statistics_enabled THEN
                IF connection^.class = nlc$cc_normal_class THEN
                  statistic := ^nav$global_osi_statistics.channel_connection_device^
                        [connection^.device_specific_attributes.device_id].normal_send_pdus_queued;
                ELSE { IF connection^.class = nlc$cc_priority_class THEN
                  statistic := ^nav$global_osi_statistics.channel_connection_device^
                        [connection^.device_specific_attributes.device_id].priority_send_pdus_queued;
                IFEND;
                osp$increment_locked_variable (statistic^, 0, actual);
              IFEND;

{! statistics end}

            IFEND;
          = nlc$cc_expedited_data_event =
            nlp$cc_send_expedited_data (cl_connection, message [i].expedited_data, status);

{! statistics begin}

            IF nav$statistics_enabled THEN
              IF connection^.class = nlc$cc_normal_class THEN
                statistic := ^nav$global_osi_statistics.channel_connection_device^
                      [connection^.device_specific_attributes.device_id].send_expedited_pdus;
              ELSE { IF connection^.class = nlc$cc_priority_class THEN
                statistic := ^nav$global_osi_statistics.channel_connection_device^
                      [connection^.device_specific_attributes.device_id].priority_send_expedited_pdus;
              IFEND;
              osp$increment_locked_variable (statistic^, 0, actual);
            IFEND;

{! statistics end}

          CASEND;
        FOREND;
      ELSE
        release_aggregates (message);
      IFEND;
    ELSE
      release_aggregates (message);
      osp$set_status_condition (nae$connection_not_open, status);
    IFEND;
  PROCEND nlp$cc_send_aggregate_message;
?? TITLE := '[XDCL] nlp$cc_send_expedited_data', EJECT ??
?? NEWTITLE := '18.  <nlc$cc_open>  --->  <nlc$cc_open>' ??
*copy nlh$cc_send_expedited_data

  PROCEDURE [XDCL] nlp$cc_send_expedited_data
    (    cl_connection: ^nlt$cl_connection;
         data: nlt$bm_message_id;
     VAR status: ost$status);

    VAR
      actual: integer,
      cc_header: nlt$cc_protocol_header_with_pad,
      cc_header_and_pad_length: nlt$cc_pdu_size,
      connection: ^nlt$cc_connection,
      data_length: integer,
      expedited_data: nlt$bm_message_id,
      layer_active: boolean,
      statistic: ^integer;

    status.normal := TRUE;
    expedited_data := data;
    nlp$cl_get_layer_connection (nlc$channel_connection_layer, cl_connection, layer_active, connection);
    IF (layer_active) AND (connection^.device_specific_attributes.state = nlc$cc_open) THEN
      nlp$bm_get_message_length (expedited_data, data_length);
      IF data_length <= connection^.device_specific_attributes.maximum_data_length THEN
        cc_header.kind := nlc$cc_expedited_data;
        cc_header.expedited_data.user_data_pad_size := nlp$cc_user_data_pad_size (data_length);
        cc_header_and_pad_length := #SIZE (nlt$cc_protocol_header) +
              cc_header.expedited_data.user_data_pad_size;
        cc_header.length := data_length + cc_header_and_pad_length;
        cc_header.expedited_data.destination_reference := connection^.peer_reference_number;
        cc_header.expedited_data.class := connection^.class;
        nlp$bm_add_message_prefix (^cc_header, cc_header_and_pad_length, expedited_data);
        nlp$cc_send_pdu (connection^.device_specific_attributes.device_id, connection^.class, expedited_data);

{! statistics begin}

        IF nav$statistics_enabled THEN
          IF connection^.class = nlc$cc_normal_class THEN
            statistic := ^nav$global_osi_statistics.channel_connection_device^
                  [connection^.device_specific_attributes.device_id].send_expedited_pdus;
          ELSE { IF connection^.class = nlc$cc_priority_class THEN
            statistic := ^nav$global_osi_statistics.channel_connection_device^
                  [connection^.device_specific_attributes.device_id].priority_send_expedited_pdus;
          IFEND;
          osp$increment_locked_variable (statistic^, 0, actual);
        IFEND;

{! statistics end}

      ELSE
        nlp$bm_release_message (expedited_data);
        osp$set_status_condition (nae$max_data_length_exceeded, status);
        osp$append_status_integer (osc$status_parameter_delimiter,
              connection^.device_specific_attributes.maximum_data_length, 10, FALSE, status);
      IFEND;
    ELSE
      nlp$bm_release_message (expedited_data);
      osp$set_status_abnormal (nac$status_id, nae$connection_not_open, channel_connection, status);
    IFEND;

  PROCEND nlp$cc_send_expedited_data;
?? OLDTITLE ??
?? TITLE := '[XDCL] nlp$cc_send_pdu', EJECT ??
*copy nlh$cc_send_pdu

  PROCEDURE [XDCL] nlp$cc_send_pdu
    (    device_id: nlt$device_identifier;
         class: nlt$cc_connection_class;
     VAR cc_pdu {INPUT,OUTPUT} : nlt$bm_message_id);

    VAR
      data_length: integer,
      network_device_list: ^nlt$network_device_list,
      pva_list: ^nat$data_fragments;

    nlp$get_nonexclusive_access (nlv$configured_network_devices.access_control);
    network_device_list := nlv$configured_network_devices.network_device_list;
    IF network_device_list^ [device_id].path_status = nlc$path_available THEN
      nlp$bm_build_pva_list (cc_pdu, pva_list);

{! statistics begin}

      IF nav$statistics_enabled THEN
        nlp$bm_get_message_length (cc_pdu, data_length);
        IF data_length > 0 THEN
          IF class = nlc$cc_normal_class THEN
            increment_send_statistic (data_length, nav$global_osi_statistics.
                  channel_connection_device^ [device_id].send);
          ELSE { IF class = nlc$cc_priority_class THEN
            increment_send_statistic (data_length, nav$global_osi_statistics.
                  channel_connection_device^ [device_id].priority_send);
          IFEND;
        IFEND;
      IFEND;

{! statistics end}

      nap$send_network_packet (class, device_id, cc_pdu, network_device_list^ [device_id].logical_unit,
            pva_list);
    ELSE
      nlp$bm_release_message (cc_pdu);
    IFEND;
    nlp$release_nonexclusive_access (nlv$configured_network_devices.access_control);

  PROCEND nlp$cc_send_pdu;
?? TITLE := ' increment_send_statistic', EJECT ??

  PROCEDURE increment_send_statistic
    (    pdu_size: integer;
     VAR statistic: osi_send_pdu);

    VAR
      actual: osi_send_pdu,
      compare_swap_status: osc$cs_successful .. osc$cs_variable_locked,
      expected: osi_send_pdu,
      i: integer,
      new: osi_send_pdu;

{ The expected_value is initialized to zero instead of the value of the
{ statistic.  If another cpu has contol of the word i.e. the left half of the word would
{ be all ones.

    expected.value := 0;
    new.pdu_total := 1;
    new.pdu_average := pdu_size;
    #SPOIL (new);

  /write_statistics/
    REPEAT
      #COMPARE_SWAP (statistic.value, expected.value, new.value, actual.value, compare_swap_status);
      IF compare_swap_status = osc$cs_successful THEN
        EXIT /write_statistics/; {----->
      ELSEIF compare_swap_status = osc$cs_failed THEN
        {expected_value is different than the actual}
        #SPOIL (actual);
        expected.value := actual.value;
        new.pdu_total := actual.pdu_total + 1;
        new.pdu_average := ((actual.pdu_average * actual.pdu_total) + pdu_size) DIV new.pdu_total;
        #SPOIL (new);
      ELSE {another cpu has the word}
        syp$cycle;
      IFEND;
    UNTIL compare_swap_status = osc$cs_successful;

  PROCEND increment_send_statistic;
?? TITLE := '[INLINE] send_cc_connect_request', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to send a Channel Connection
{   Connect request to the specified network device.
{
{ NOTES: This procedure assumes that the network_device_list has been locked
{        by the caller.
{

  PROCEDURE [INLINE] send_cc_connect_request
    (    device_id: nlt$device_identifier;
         class: nlt$cc_connection_class;
     VAR data {INPUT, OUTPUT} : nlt$bm_message_id);

    VAR
      network_device_list: ^nlt$network_device_list,
      pva_list: ^nat$data_fragments;

    network_device_list := nlv$configured_network_devices.network_device_list;
    nlp$bm_build_pva_list (data, pva_list);
    nap$send_network_packet (class, device_id, data, network_device_list^ [device_id].logical_unit, pva_list);

  PROCEND send_cc_connect_request;
?? TITLE := '[INLINE] store_cc_pdu', EJECT ??

  PROCEDURE [INLINE] store_cc_pdu
    (    layers_active: nlt$cl_layers;
     VAR data: nlt$bm_message_id;
     VAR send_buffer: nlt$cc_send_buffer);

    VAR
      data_extension: ^nlt$cc_send_buffer_extension,
      extension: ^^nlt$cc_send_buffer_extension,
      message_count: integer;

    IF NOT (((send_buffer.out + (nlc$cc_send_buffer_limit - 1)) MOD nlc$cc_send_buffer_limit) =
          send_buffer.inn) THEN

{ Space is available in the send buffer.

      send_buffer.cc_pdu [send_buffer.inn].data := data;
      send_buffer.inn := (send_buffer.inn + 1) MOD nlc$cc_send_buffer_limit;
    ELSE { send buffer full
      message_count := 0;
      extension := ^send_buffer.extension;
      WHILE (extension^ <> NIL) DO
        message_count := message_count + 1;
        extension := ^extension^^.nextt;
      WHILEND;
      IF (message_count < cc_max_send_buffer_size) OR NOT ((nlc$osi_network_access_agent IN layers_active) OR
            (nlc$osi_link_access_agent IN layers_active)) THEN
        REPEAT
          ALLOCATE data_extension IN nav$network_paged_heap^;
          IF (data_extension = NIL) THEN
            syp$cycle;
          IFEND;
        UNTIL (data_extension <> NIL);
        data_extension^.cc_pdu.data := data;
        data_extension^.nextt := NIL;
        extension^ := data_extension;
      ELSE
        nlp$bm_release_message (data);
      IFEND;
    IFEND;

  PROCEND store_cc_pdu;
?? OLDTITLE ??
MODEND nlm$channel_connection_entity;
