?? NEWTITLE := 'NOS/VE Network Access : Link Access Agent' ??
MODULE nlm$link_access_agent;
?? RIGHT := 110 ??

{ PURPOSE:
{  This module contains the procedures which allow a NOS/VE user entity access to the services
{  of an OSI Link entity residing in an OSI communications device. These procedures are collectively
{  referred to as the Link Access Agent (LAA).
{
{ DESIGN:
{  The Link Access protocol is exchanged over a channel connection between the Link Access Agent (LAA)
{  residing in NOS/VE and a Link Access Provider (LAP) residing in an OSI communications device.
{  The LAP serves as a "gateway" between a channel connection and a Link Layer.

?? NEWTITLE := 'Global Declarations Referenced by this Module' ??
?? PUSH (LISTEXT := ON) ??
*copyc nac$null_connection_id
*copyc nae$link_access_agent
*copyc nae$namve_conditions
*copyc nlt$cc_device_and_data_list
*copyc nlt$cc_interface
*copyc nlt$device_count
*copyc nlt$device_ids
*copyc nlt$la_connection
*copyc nlt$la_protocol_data_unit
*copyc nlt$la_sap_descriptor
?? POP ??
*copyc nap$namve_system_error
*copyc nlp$bm_add_message_prefix
*copyc nlp$bm_create_message
*copyc nlp$bm_extract_message_prefix
*copyc nlp$bm_flush_message
*copyc nlp$bm_get_message_length
*copyc nlp$bm_release_message
*copyc nlp$cc_disconnect
*copyc nlp$cc_initialize_template
*copyc nlp$cc_request_connection
*copyc nlp$cc_send_data
*copyc nlp$cl_activate_layer
*copyc nlp$cl_create_connection
*copyc nlp$cl_deactivate_layer
*copyc nlp$cl_get_exclusive_via_cid
*copyc nlp$cl_get_layer_connection
*copyc nlp$cl_initialize_template
*copyc nlp$cl_release_exclusive_access
*copyc nlp$get_nonexclusive_access
*copyc nlp$release_nonexclusive_access
*copyc osp$add_to_locked_variable
*copyc osp$append_status_integer
*copyc osp$clear_job_signature_lock
*copyc osp$decrement_locked_variable
*copyc osp$increment_locked_variable
*copyc osp$set_job_signature_lock
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc syp$cycle

*copyc nav$global_osi_statistics
*copyc nav$network_paged_heap
*copyc nav$network_procedures
*copyc nav$system_id
*copyc nlv$configured_network_devices
*copyc nlv$la_open_sap_list
*copyc nlv$sm_devices
*copyc oss$task_private
*copyc osv$task_private_heap
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$la_close_sap', EJECT ??

*copy nlh$la_close_sap

  PROCEDURE [XDCL] nlp$la_close_sap
    (    sap_id: nat$cn_sap_id;
     VAR status: ost$status);

    VAR
      cl_connection: ^nlt$cl_connection,
      connection: ^nlt$la_connection,
      connection_exists: boolean,
      current_subnet_entry: ^nlt$la_subnet_entry,
      layer_active: boolean,
      previous_sap_descriptor: ^^nlt$la_sap_descriptor,
      sap_descriptor: ^nlt$la_sap_descriptor,
      subnet_entry: ^nlt$la_subnet_entry;

    status.normal := TRUE;
    osp$set_job_signature_lock (nlv$la_open_sap_list.lock);
    previous_sap_descriptor := ^nlv$la_open_sap_list.sap_list;
    WHILE (previous_sap_descriptor^ <> NIL) AND (previous_sap_descriptor^^.sap_id <> sap_id) DO
      previous_sap_descriptor := ^previous_sap_descriptor^^.nextt;
    WHILEND;

    IF previous_sap_descriptor^ <> NIL THEN
      sap_descriptor := previous_sap_descriptor^;
      previous_sap_descriptor^ := sap_descriptor^.nextt;
      osp$clear_job_signature_lock (nlv$la_open_sap_list.lock);
      subnet_entry := sap_descriptor^.subnet_list;
      FREE sap_descriptor IN nav$network_paged_heap^;

      WHILE subnet_entry <> NIL DO
        IF (subnet_entry^.status = nlc$la_sap_open) OR (subnet_entry^.status = nlc$la_open_sap_confirm_wait)
              THEN
          nlp$cl_get_exclusive_via_cid (subnet_entry^.connection_id, connection_exists, cl_connection);
          IF connection_exists THEN
            nlp$cl_get_layer_connection (nlc$osi_link_access_agent, cl_connection, layer_active, connection);
            IF layer_active THEN
              disconnect_la_connection (cl_connection, sap_id, subnet_entry^.subnet_id, nlc$la_user_request);
            IFEND;
            nlp$cl_release_exclusive_access (cl_connection);
          IFEND;
        IFEND;
        current_subnet_entry := subnet_entry;
        subnet_entry := subnet_entry^.nextt;
        FREE current_subnet_entry IN nav$network_paged_heap^;
      WHILEND;
    ELSE
      osp$clear_job_signature_lock (nlv$la_open_sap_list.lock);
      osp$set_status_condition (nae$la_sap_not_open, status);
      osp$append_status_integer (osc$status_parameter_delimiter, sap_id, 10, TRUE, status);
    IFEND;

  PROCEND nlp$la_close_sap;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$la_connect_event_processor', EJECT ??

*copy nlh$la_connect_event_processor

  PROCEDURE [XDCL] nlp$la_connect_event_processor
    (    cl_connection: ^nlt$cl_connection;
         event: nlt$cc_event;
     VAR inventory_report: integer);

    VAR
      data: nlt$bm_message_id,
      data_fragment: array [1 .. 1] of nat$data_fragment,
      disconnect_request: nlt$la_close_sap_request,
      ignore_status: ost$status;

    inventory_report := 0;
    data := event.connect.data;
    nlp$bm_release_message (data);
    disconnect_request.header.length := #SIZE (disconnect_request);
    disconnect_request.header.kind := nlc$la_close_sap_request;
    disconnect_request.reason := nlc$la_laa_detects_protocol_err;
    data_fragment [1].address := ^disconnect_request;
    data_fragment [1].length := disconnect_request.header.length;
    nlp$bm_create_message (data_fragment, data, ignore_status);
    nlp$cc_disconnect (cl_connection, data, ignore_status);

  PROCEND nlp$la_connect_event_processor;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$la_event_processor', EJECT ??

*copy nlh$la_event_processor

  PROCEDURE [XDCL] nlp$la_event_processor
    (    cl_connection: ^nlt$cl_connection;
         event: nlt$cc_event;
     VAR inventory_report: integer);

    VAR
      actual: integer,
      close_sap_pdu_received: boolean,
      connection: ^nlt$la_connection,
      data: nlt$bm_message_id,
      data_fragment: array [1 .. 1] of nat$data_fragment,
      data_indication: nlt$la_data_indication,
      data_length: integer,
      device: nlt$device_identifier,
      disconnect_indication: nlt$la_disconnect_indication,
      ignore_bytes_moved: nat$data_length,
      ignore_error: boolean,
      layer_active: boolean,
      local_status: ost$status,
      log_message: string (70),
      message_length: integer,
      system_management_list: ^nlt$sm_device_list,
      open_sap_confirm: nlt$la_open_sap_confirm,
      sap_descriptor: ^nlt$la_sap_descriptor,
      source_address: nat$system_address,
      subnet_entry: ^nlt$la_subnet_entry;

    inventory_report := 0;
    nlp$cl_get_layer_connection (nlc$osi_link_access_agent, cl_connection, layer_active, connection);
    IF layer_active THEN
      CASE event.kind OF
      = nlc$cc_data_event =
        data := event.data.data;
        nlp$bm_get_message_length (data, data_length);
        IF data_length >= #SIZE (data_indication) THEN
          nlp$bm_extract_message_prefix (^data_indication, #SIZE (data_indication), data, ignore_bytes_moved);
          IF ((data_indication.header.length = data_length) AND
                (data_indication.header.kind = nlc$la_data_indication) AND
                (data_indication.priority_length = 0) AND (data_indication.header_format_length = 1) AND
                (data_indication.header_format = nlc$la_standard_header) AND
                (data_indication.source_subnet_addr_length = #SIZE (nat$system_identifier)) AND
                (data_indication.destination_subnet_addr_length = #SIZE (nat$system_identifier))) THEN
            source_address.network := connection^.subnet_id;
            source_address.system := data_indication.source_subnet_address;
            nav$network_procedures [connection^.event_processor].
                  cn_event_processor^ (connection^.sap_id, connection^.device_id, source_address, data);

{! statistics begin}
            osp$increment_locked_variable (nav$global_osi_statistics.link_access_agent.pdus_received, 0,
                  actual);
            osp$add_to_locked_variable (nav$global_osi_statistics.link_access_agent.total_bytes_received, 0,
                  data_length, actual);
{! statistics end}

          ELSE { LAP protocol error }
            nlp$bm_release_message (data);
            disconnect_la_connection (cl_connection, connection^.sap_id, connection^.subnet_id,
                  nlc$la_laa_detects_protocol_err);
          IFEND;
        ELSE { insufficient data }
          nlp$bm_release_message (data);
          disconnect_la_connection (cl_connection, connection^.sap_id, connection^.subnet_id,
                nlc$la_laa_detects_protocol_err);
        IFEND;

      = nlc$cc_accept_event =
        data := event.accept.data;
        nlp$bm_get_message_length (data, data_length);
        IF data_length = #SIZE (open_sap_confirm) THEN
          data_fragment [1].address := ^open_sap_confirm;
          data_fragment [1].length := #SIZE (open_sap_confirm);
          nlp$bm_flush_message (data_fragment, data, data_length, {ignore} local_status);
          IF (open_sap_confirm.header.length = data_length) AND
                (open_sap_confirm.header.kind = nlc$la_open_sap_confirm) THEN
            connection^.state := nlc$la_open;
            osp$set_job_signature_lock (nlv$la_open_sap_list.lock);
            get_sap_and_subnet_info (connection^.sap_id, connection^.subnet_id, sap_descriptor, subnet_entry);
            IF (subnet_entry <> NIL) AND (subnet_entry^.status = nlc$la_open_sap_confirm_wait) THEN
              subnet_entry^.status := nlc$la_sap_open;
              osp$clear_job_signature_lock (nlv$la_open_sap_list.lock);

{! statistics begin}
              osp$increment_locked_variable (nav$global_osi_statistics.link_access_agent.current_saps_open, 0,
                    actual);
{! statistics end}

            ELSE { SAP not open }
              osp$clear_job_signature_lock (nlv$la_open_sap_list.lock);
              disconnect_la_connection (cl_connection, connection^.sap_id, connection^.subnet_id,
                    nlc$la_laa_detects_protocol_err);
            IFEND;
          ELSE { Length mismatch or invalid PDU kind }
            disconnect_la_connection (cl_connection, connection^.sap_id, connection^.subnet_id,
                  nlc$la_laa_detects_protocol_err);
          IFEND;
        ELSE { Insufficient data }
          nlp$bm_release_message (data);
          disconnect_la_connection (cl_connection, connection^.sap_id, connection^.subnet_id,
                nlc$la_laa_detects_protocol_err);
        IFEND;

      = nlc$cc_disconnect_event =
        close_sap_pdu_received := FALSE;
        IF event.disconnect.reason = nlc$cc_dr_normal_disconnect THEN
          data := event.disconnect.data;
          nlp$bm_get_message_length (data, data_length);
          IF data_length = #SIZE (disconnect_indication) THEN
            data_fragment [1].address := ^disconnect_indication;
            data_fragment [1].length := #SIZE (disconnect_indication);
            nlp$bm_flush_message (data_fragment, data, data_length, {ignore} local_status);
            IF disconnect_indication.header.length = data_length THEN
              IF disconnect_indication.header.kind = nlc$la_close_sap_indication THEN
                close_sap_pdu_received := TRUE;
              ELSEIF disconnect_indication.header.kind = nlc$la_open_sap_reject THEN
                IF NOT (disconnect_indication.reason = nlc$la_invalid_sap_id) THEN
                  nap$namve_system_error (TRUE, 'LAA - Unexpected reject reason', NIL);
                IFEND;
              ELSE
                nap$namve_system_error (TRUE, 'LAA - Unknown PDU kind on disconnect indication', NIL);
              IFEND;
            ELSE { Length mismatch }
              nap$namve_system_error (TRUE, 'LAA - Length mismatch on disconnect indication', NIL);
            IFEND;
          ELSE { Insufficient length }
            nap$namve_system_error (TRUE, 'LAA - Invalid length on disconnect indication', NIL);
            nlp$bm_release_message (data);
          IFEND;
        IFEND;
        osp$set_job_signature_lock (nlv$la_open_sap_list.lock);
        get_sap_and_subnet_info (connection^.sap_id, connection^.subnet_id, sap_descriptor, subnet_entry);
        IF subnet_entry <> NIL THEN

{! statistics begin}
          IF subnet_entry^.status = nlc$la_sap_open THEN
            osp$decrement_locked_variable (nav$global_osi_statistics.link_access_agent.current_saps_open, 1,
                  actual, ignore_error);
          IFEND;

{! statistics end}

          subnet_entry^.status := nlc$la_sap_closed;
          subnet_entry^.connection_id := nac$null_connection_id;

          IF close_sap_pdu_received THEN
            subnet_entry^.status := nlc$la_resource_constraint;
          IFEND;

        IFEND;
        osp$clear_job_signature_lock (nlv$la_open_sap_list.lock);
        nlp$cl_deactivate_layer (nlc$osi_link_access_agent, cl_connection);
      ELSE
        nap$namve_system_error (TRUE, 'LAA - Unexpected CC event', NIL);
        disconnect_la_connection (cl_connection, connection^.sap_id, connection^.subnet_id,
              nlc$la_laa_detects_protocol_err);
      CASEND;
    ELSE
      nap$namve_system_error (TRUE, 'LAA - Event received when layer inactive', NIL);
    IFEND;

  PROCEND nlp$la_event_processor;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$la_initialize', EJECT ??

*copy nlh$la_initialize

  PROCEDURE [XDCL] nlp$la_initialize;

    VAR
      nil_event_processor: nlt$cl_event_processor;

    nlp$cl_initialize_template (nlc$osi_link_access_agent, nlc$osi_link_access_agent,
          #SIZE (nlt$la_connection), 0, nil_event_processor, nlc$la_retry_constrained_saps,
          nil_event_processor, nac$nil);
    nlp$cc_initialize_template (nlc$osi_link_access_agent);

  PROCEND nlp$la_initialize;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$la_open_sap', EJECT ??

*copy nlh$la_open_sap

  PROCEDURE [XDCL] nlp$la_open_sap
    (    sap_id: nat$cn_sap_id;
         device_count: nlt$device_identifier;
         device_list: ^array [1 .. * ] of nlt$device_identifier;
         connection_class: nlt$cc_connection_class;
         event_processor: nat$network_procedure;
     VAR status: ost$status);

    VAR
      cl_connection: ^nlt$cl_connection,
      device: nlt$device_identifier,
      system_management_list: ^nlt$sm_device_list,
      request_issued: boolean,
      sap_descriptor: ^nlt$la_sap_descriptor,
      subnet: nat$subnet_identifier,
      subnet_attribute: ^nlt$subnet_attributes,
      subnet_entry: ^nlt$la_subnet_entry;

    status.normal := TRUE;

    osp$set_job_signature_lock (nlv$la_open_sap_list.lock);
    sap_descriptor := nlv$la_open_sap_list.sap_list;
    WHILE sap_descriptor <> NIL DO
      IF sap_descriptor^.sap_id = sap_id THEN
        osp$set_status_condition (nae$la_sap_already_open, status);
        osp$append_status_integer (osc$status_parameter_delimiter, sap_id, 10, TRUE, status);
        osp$clear_job_signature_lock (nlv$la_open_sap_list.lock);
        RETURN; {----->
      IFEND;
      sap_descriptor := sap_descriptor^.nextt;
    WHILEND;

    ALLOCATE sap_descriptor IN nav$network_paged_heap^;
    IF sap_descriptor <> NIL THEN
      sap_descriptor^.subnet_list := NIL;
      sap_descriptor^.sap_id := sap_id;
      sap_descriptor^.event_processor := event_processor;
      sap_descriptor^.connection_class := connection_class;
      sap_descriptor^.devices := $nlt$device_ids [];
      nlp$get_nonexclusive_access (nlv$sm_devices.access_control);
      system_management_list := nlv$sm_devices.list;

    /open_device_sap/
      FOR device := LOWERBOUND (system_management_list^) TO UPPERBOUND (system_management_list^) DO
        sap_descriptor^.devices := sap_descriptor^.devices + $nlt$device_ids [device_list^ [device]];
        IF (nlv$configured_network_devices.network_device_list^ [device].kind = nac$ica_2) AND
              (system_management_list^ [device].state = nlc$sm_initialized) THEN
          subnet_attribute := system_management_list^ [device].subnet_list;

        /find_subnet/
          WHILE subnet_attribute <> NIL DO
            IF (subnet_attribute^.directly_connected) THEN
              get_subnet_entry (subnet_attribute^.subnet_id, ^sap_descriptor^.subnet_list, subnet_entry);
              IF (subnet_entry^.status = nlc$la_sap_closed) OR
                    (subnet_entry^.status = nlc$la_resource_constraint) THEN
                issue_open_sap_request (sap_descriptor, device, subnet_entry, request_issued);
              IFEND;
            IFEND;
            subnet_attribute := subnet_attribute^.next_entry;
          WHILEND /find_subnet/;
        IFEND;
      FOREND /open_device_sap/;

      nlp$release_nonexclusive_access (nlv$sm_devices.access_control);

{ Link new SAP descriptor into open SAP list.

      sap_descriptor^.nextt := nlv$la_open_sap_list.sap_list;
      nlv$la_open_sap_list.sap_list := sap_descriptor;
    ELSE { sap_descriptor = NIL }
      osp$set_status_abnormal (nac$status_id, nae$allocation_failed, 'Link Access SAP', status);
    IFEND;

    osp$clear_job_signature_lock (nlv$la_open_sap_list.lock);

  PROCEND nlp$la_open_sap;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$la_open_saps', EJECT ??

*copy nlh$la_open_saps

  PROCEDURE [XDCL] nlp$la_open_saps
    (    device_id: nlt$device_identifier;
         subnet_count: nat$subnet_identifier;
         subnet_list: ^array [1 .. * ] of nat$subnet_identifier);

    VAR
      cl_connection: ^nlt$cl_connection,
      ignore_request_issued: boolean,
      ignore_status: ost$status,
      sap_descriptor: ^nlt$la_sap_descriptor,
      subnet: nat$subnet_identifier,
      subnet_entry: ^nlt$la_subnet_entry;


    osp$set_job_signature_lock (nlv$la_open_sap_list.lock);
    sap_descriptor := nlv$la_open_sap_list.sap_list;
    WHILE sap_descriptor <> NIL DO
      IF device_id IN sap_descriptor^.devices THEN

      /find_subnet/
        FOR subnet := 1 TO subnet_count DO
          get_subnet_entry (subnet_list^ [subnet], ^sap_descriptor^.subnet_list, subnet_entry);
          IF (subnet_entry^.status = nlc$la_sap_closed) OR (subnet_entry^.status = nlc$la_resource_constraint)
                THEN
            issue_open_sap_request (sap_descriptor, device_id, subnet_entry, ignore_request_issued);
          IFEND;
        FOREND /find_subnet/;
      IFEND;
      sap_descriptor := sap_descriptor^.nextt;
    WHILEND;
    osp$clear_job_signature_lock (nlv$la_open_sap_list.lock);

  PROCEND nlp$la_open_saps;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$la_retry_constrained_saps', EJECT ??

*copy nlh$la_retry_constrained_saps

  PROCEDURE [XDCL] nlp$la_retry_constrained_saps
    (    current_time: integer);

    VAR
      cl_connection: ^nlt$cl_connection,
      device: nlt$device_identifier,
      ignore_status: ost$status,
      system_management_list: ^nlt$sm_device_list,
      request_issued: boolean,
      sap_descriptor: ^nlt$la_sap_descriptor,
      subnet: nat$subnet_identifier,
      subnet_attribute: ^nlt$subnet_attributes,
      subnet_entry: ^nlt$la_subnet_entry;


    osp$set_job_signature_lock (nlv$la_open_sap_list.lock);
    sap_descriptor := nlv$la_open_sap_list.sap_list;
    nlp$get_nonexclusive_access (nlv$sm_devices.access_control);
    system_management_list := nlv$sm_devices.list;
    WHILE sap_descriptor <> NIL DO
      subnet_entry := sap_descriptor^.subnet_list;
      WHILE subnet_entry <> NIL DO
        IF subnet_entry^.status = nlc$la_resource_constraint THEN
          subnet_entry^.status := nlc$la_sap_closed;

        /open_device_sap/
          FOR device := LOWERBOUND (system_management_list^) TO UPPERBOUND (system_management_list^) DO
            IF (nlv$configured_network_devices.network_device_list^ [device].kind = nac$ica_2) AND
                  (system_management_list^ [device].device_id IN sap_descriptor^.devices) AND
                  (system_management_list^ [device].state = nlc$sm_initialized) THEN
              subnet_attribute := system_management_list^ [device].subnet_list;

            /find_subnet/
              WHILE subnet_attribute <> NIL DO
                IF (subnet_attribute^.directly_connected) AND (subnet_attribute^.subnet_id =
                      subnet_entry^.subnet_id) THEN
                  issue_open_sap_request (sap_descriptor, device, subnet_entry, request_issued);
                  IF request_issued THEN
                    EXIT /open_device_sap/; {----->
                  ELSE
                    EXIT /find_subnet/; {----->
                  IFEND;
                IFEND;
                subnet_attribute := subnet_attribute^.next_entry;
              WHILEND /find_subnet/;
            IFEND;
          FOREND /open_device_sap/;
        IFEND;
        subnet_entry := subnet_entry^.nextt;
      WHILEND;
      sap_descriptor := sap_descriptor^.nextt;
    WHILEND;
    nlp$release_nonexclusive_access (nlv$sm_devices.access_control);
    osp$clear_job_signature_lock (nlv$la_open_sap_list.lock);

  PROCEND nlp$la_retry_constrained_saps;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nlp$la_send_data', EJECT ??

*copy nlh$la_send_data

  PROCEDURE [XDCL] nlp$la_send_data
    (    sap_id: nat$cn_sap_id;
         subnet_id: nat$subnet_identifier;
         destination_subnet_address: nat$system_identifier;
         header_format: nlt$la_header_format;
         priority: nlt$la_priority;
         data: nlt$bm_message_id;
     VAR status: ost$status);

    VAR
      actual: integer,
      cl_connection: ^nlt$cl_connection,
      connection: ^nlt$la_connection,
      connection_exists: boolean,
      data_length: integer,
      data_request: nlt$la_data_request,
      layer_active: boolean,
      local_status: ost$status,
      release_message: boolean,
      sap_descriptor: ^nlt$la_sap_descriptor,
      send_data: nlt$bm_message_id,
      subnet_entry: ^nlt$la_subnet_entry;

    status.normal := TRUE;
    release_message := TRUE;
    send_data := data;

    osp$set_job_signature_lock (nlv$la_open_sap_list.lock);
    get_sap_and_subnet_info (sap_id, subnet_id, sap_descriptor, subnet_entry);
    IF subnet_entry <> NIL THEN
      IF subnet_entry^.status = nlc$la_sap_open THEN
        osp$clear_job_signature_lock (nlv$la_open_sap_list.lock);
        nlp$cl_get_exclusive_via_cid (subnet_entry^.connection_id, connection_exists, cl_connection);
        IF connection_exists THEN
          nlp$bm_get_message_length (send_data, data_length);
          data_request.header.length := data_length + #SIZE (data_request);
          nlp$cl_get_layer_connection (nlc$osi_link_access_agent, cl_connection, layer_active, connection);
          IF layer_active THEN
            data_request.header.kind := nlc$la_data_request;
            data_request.priority_length := 1;
            data_request.priority := priority;
            data_request.header_format_length := 1;
            data_request.header_format := header_format;
            data_request.destination_sap := sap_id;
            data_request.destination_subnet_addr_length := #SIZE (destination_subnet_address);
            data_request.destination_subnet_address := destination_subnet_address;
            nlp$bm_add_message_prefix (^data_request, #SIZE (data_request), send_data);
            nlp$cc_send_data (cl_connection, send_data, {ignore} status);
            release_message := FALSE;

{! statistics begin}
            osp$increment_locked_variable (nav$global_osi_statistics.link_access_agent.pdus_sent, 0, actual);
            osp$add_to_locked_variable (nav$global_osi_statistics.link_access_agent.total_bytes_sent, 0,
                  #SIZE (data_request), actual);
{! statistics end}

          IFEND;
          nlp$cl_release_exclusive_access (cl_connection);
        IFEND;
      ELSE { subnet_entry^.status <> nlc$la_sap_open }
        osp$clear_job_signature_lock (nlv$la_open_sap_list.lock);
      IFEND;
    ELSE { SAP not open }
      osp$clear_job_signature_lock (nlv$la_open_sap_list.lock);
      osp$set_status_condition (nae$la_sap_not_open, status);
      osp$append_status_integer (osc$status_parameter_delimiter, sap_id, 10, TRUE, status);
    IFEND;
    IF release_message THEN
      nlp$bm_release_message (send_data);
    IFEND;

  PROCEND nlp$la_send_data;
?? OLDTITLE ??
?? NEWTITLE := 'disconnect_la_connection', EJECT ??

{ PURPOSE:
{   The purpose of this request is to disconnect the specified
{   Link Access (LA) connection, deactivate the LAA layer, and
{   close the device SAP.

  PROCEDURE [INLINE] disconnect_la_connection
    (    cl_connection: ^nlt$cl_connection;
         sap_id: nat$cn_sap_id;
         subnet_id: nat$subnet_identifier;
         close_sap_reason: nlt$la_close_request_reason);

    VAR
      actual: integer,
      close_sap_request: nlt$la_close_sap_request,
      data_fragment: array [1 .. 1] of nat$data_fragment,
      data: nlt$bm_message_id,
      ignore_error: boolean,
      ignore_status: ost$status,
      sap_descriptor: ^nlt$la_sap_descriptor,
      subnet_entry: ^nlt$la_subnet_entry;

    osp$set_job_signature_lock (nlv$la_open_sap_list.lock);
    get_sap_and_subnet_info (sap_id, subnet_id, sap_descriptor, subnet_entry);
    IF subnet_entry <> NIL THEN

{! statistics begin}
      IF subnet_entry^.status = nlc$la_sap_open THEN
        osp$decrement_locked_variable (nav$global_osi_statistics.link_access_agent.current_saps_open, 1,
              actual, ignore_error);
      IFEND;
{! statistics end}

      subnet_entry^.status := nlc$la_sap_closed;
      subnet_entry^.connection_id := nac$null_connection_id;
    IFEND;
    osp$clear_job_signature_lock (nlv$la_open_sap_list.lock);
    close_sap_request.header.length := #SIZE (close_sap_request);
    close_sap_request.header.kind := nlc$la_close_sap_request;
    close_sap_request.reason := close_sap_reason;
    data_fragment [1].address := ^close_sap_request;
    data_fragment [1].length := close_sap_request.header.length;
    nlp$bm_create_message (data_fragment, data, ignore_status);
    nlp$cc_disconnect (cl_connection, data, ignore_status);
    nlp$cl_deactivate_layer (nlc$osi_link_access_agent, cl_connection);

  PROCEND disconnect_la_connection;
?? OLDTITLE ??
?? NEWTITLE := 'get_sap_and_subnet_info', EJECT ??

{ PURPOSE:
{   The purpose of this request is to find and return the Link Access (LA)
{   SAP descriptor and the subnet entry.
{
{  NOTE: At least non-exclusive access to NLV$LA_OPEN_SAP_LIST is required upon entry.

  PROCEDURE [INLINE] get_sap_and_subnet_info
    (    sap_id: nat$cn_sap_id;
         subnet_id: nat$subnet_identifier;
     VAR sap_descriptor: ^nlt$la_sap_descriptor;
     VAR subnet_entry: ^nlt$la_subnet_entry);

    sap_descriptor := nlv$la_open_sap_list.sap_list;
    WHILE (sap_descriptor <> NIL) AND (sap_descriptor^.sap_id <> sap_id) DO
      sap_descriptor := sap_descriptor^.nextt;
    WHILEND;

    IF sap_descriptor <> NIL THEN
      subnet_entry := sap_descriptor^.subnet_list;
      WHILE (subnet_entry <> NIL) AND (subnet_entry^.subnet_id <> subnet_id) DO
        subnet_entry := subnet_entry^.nextt;
      WHILEND;
    ELSE
      subnet_entry := NIL;
    IFEND;

  PROCEND get_sap_and_subnet_info;
?? OLDTITLE ??
?? NEWTITLE := 'get_subnet_entry', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to return the subnet entry
{   corresponding to the specified subnet identifier. If the
{   requested subnet entry does not exists it will be allocated.

  PROCEDURE [INLINE] get_subnet_entry
    (    subnet_id: nat$subnet_identifier;
         subnet_list {input, output} : ^^nlt$la_subnet_entry;
     VAR subnet_entry: ^nlt$la_subnet_entry);

    VAR
      current_subnet_entry: ^^nlt$la_subnet_entry;

    current_subnet_entry := subnet_list;
    WHILE (current_subnet_entry^ <> NIL) AND (current_subnet_entry^^.subnet_id <> subnet_id) DO
      current_subnet_entry := ^current_subnet_entry^^.nextt;
    WHILEND;

    IF current_subnet_entry^ <> NIL THEN
      subnet_entry := current_subnet_entry^;
    ELSE
      REPEAT
        ALLOCATE subnet_entry IN nav$network_paged_heap^;
        IF subnet_entry = NIL THEN
          syp$cycle;
        IFEND;
      UNTIL subnet_entry <> NIL;
      subnet_entry^.status := nlc$la_sap_closed;
      subnet_entry^.subnet_id := subnet_id;
      subnet_entry^.connection_id := nac$null_connection_id;
      subnet_entry^.nextt := NIL;
      current_subnet_entry^ := subnet_entry;
    IFEND;

  PROCEND get_subnet_entry;
?? OLDTITLE ??
?? NEWTITLE := 'issue_open_sap_request', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to send a Link Access (LA) open SAP
{   request over a Channel Connection to the Link Access Provider (LAP) in the
{   specified device. This procedure only issues the request (or at least attempts
{   to issue a request), it does not wait for an accept or reject from LAP.

  PROCEDURE [INLINE] issue_open_sap_request
    (    sap_descriptor: ^nlt$la_sap_descriptor;
         device_id: nlt$device_identifier;
         subnet_entry: ^nlt$la_subnet_entry;
     VAR request_issued: boolean);

    VAR
      cl_connection: ^nlt$cl_connection,
      connection: ^nlt$la_connection,
      data_fragment: array [1 .. 1] of nat$data_fragment,
      device_and_data_list: ^nlt$cc_device_and_data_list,
      ignore_layer_active: boolean,
      local_status: ost$status,
      open_sap_request: nlt$la_open_sap_request;

    local_status.normal := TRUE;
    request_issued := FALSE;
    nlp$cl_create_connection (nlc$osi_link_access_agent, cl_connection);
    IF cl_connection <> NIL THEN
      open_sap_request.header.length := #SIZE (open_sap_request);
      open_sap_request.header.kind := nlc$la_open_sap_request;
      open_sap_request.sap_id := sap_descriptor^.sap_id;
      open_sap_request.subnet_id := subnet_entry^.subnet_id;
      data_fragment [1].address := ^open_sap_request;
      data_fragment [1].length := open_sap_request.header.length;
      PUSH device_and_data_list: [1 .. 1];
      device_and_data_list^ [1].device_id := device_id;
      nlp$bm_create_message (data_fragment, device_and_data_list^ [1].data, {ignore} local_status);
      nlp$cc_request_connection (cl_connection, device_and_data_list^, nlc$link_access_address,
            sap_descriptor^.connection_class, local_status);
      IF local_status.normal THEN
        request_issued := TRUE;
        nlp$cl_activate_layer (nlc$osi_link_access_agent, cl_connection);
        nlp$cl_get_layer_connection (nlc$osi_link_access_agent, cl_connection, ignore_layer_active,
              connection);
        subnet_entry^.status := nlc$la_open_sap_confirm_wait;
        subnet_entry^.connection_id := cl_connection^.identifier;
        connection^.sap_id := sap_descriptor^.sap_id;
        connection^.subnet_id := subnet_entry^.subnet_id;
        connection^.device_id := device_id;
        connection^.state := nlc$la_connect_confirm_wait;
        connection^.event_processor := sap_descriptor^.event_processor;
      ELSEIF local_status.condition = nae$resources_unavailable THEN
        subnet_entry^.status := nlc$la_resource_constraint;
      IFEND;
      nlp$cl_release_exclusive_access (cl_connection);
    ELSE { cl_connection = NIL }
      subnet_entry^.status := nlc$la_resource_constraint;
    IFEND;

  PROCEND issue_open_sap_request;
?? OLDTITLE ??
MODEND nlm$link_access_agent;
