?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Network Access : Network Application Layer' ??
MODULE nam$network_external_interface;

{ PURPOSE:
{   The purpose of this module is to contain all NAM/VE external interfaces to the Network
{   Access Agent and the Internet Protocol Layer.
{   The module contains request interfaces and the "event processor" interface
{   which sends/receives network events to/from both the Internet Protocol Layer and the
{   Network Access Agent.
{
{ DESIGN:
{   This module is designed to be contained on the OSF$JOB_TEMPLATE_23D library and may execute
{   in any task.  These interfaces are not gated and are meant to be called by internal NAM/VE
{   code.
?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc nac$xi_maximum_data_length
*copyc nae$application_interfaces
*copyc nae$namve_conditions
*copyc nat$data_fragments
*copyc nat$internet_address
*copyc nat$network_layer_address
*copyc nat$network_message_priority
*copyc nat$network_sap_identifier
*copyc nat$open_network_sap_descriptor
*copyc nat$osi_network_address
*copyc nat$user_interface
*copyc nlt$bm_message_id
*copyc oss$job_paged_literal
?? POP ??
*copyc nlp$al_get_data_length
*copyc nlp$bm_flush_message
*copyc nlp$bm_release_message
*copyc nlp$na_close_sap
*copyc nlp$na_open_sap
*copyc nlp$na_send_data
*copyc osp$append_status_integer
*copyc osp$clear_job_signature_lock
*copyc osp$set_job_signature_lock
*copyc osp$set_status_abnormal
*copyc pmp$get_executing_task_gtid
*copyc pmp$get_microsecond_clock
*copyc pmp$ready_task
*copyc pmp$wait
*copyc syp$cycle
*copyc nav$network_paged_heap
*copyc nav$open_network_sap_list
*copyc nav$open_network_sap_list_lock
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  CONST
    nac$network_queue_limit = 100;

  VAR
    network: [READ, oss$job_paged_literal] string (26) := 'Network External Interface';

?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nap$close_network_sap', EJECT ??
*copy nah$close_network_sap

  PROCEDURE [XDCL] nap$close_network_sap
    (    sap: nat$network_sap_identifier;
     VAR status: ost$status);

    VAR
      closed_sap_descriptor: ^nat$open_network_sap_descriptor,
      event: ^nat$network_event,
      next_event: ^nat$network_event,
      sap_descriptor: ^^nat$open_network_sap_descriptor;

    status.normal := TRUE;

{ Find the open sap descriptor.

    osp$set_job_signature_lock (nav$open_network_sap_list_lock);
    sap_descriptor := ^nav$open_network_sap_list;
    WHILE (sap_descriptor^ <> NIL) AND (sap_descriptor^^.sap <> sap) DO
      sap_descriptor := ^sap_descriptor^^.link;
    WHILEND;
    IF sap_descriptor^ <> NIL THEN

{ Delink the open sap descriptor.

      closed_sap_descriptor := sap_descriptor^;
      sap_descriptor^ := closed_sap_descriptor^.link;
      osp$clear_job_signature_lock (nav$open_network_sap_list_lock);
      event := closed_sap_descriptor^.event_queue;
      FREE closed_sap_descriptor IN nav$network_paged_heap^;

{ Free any undelivered queued messages.

      WHILE event <> NIL DO
        next_event := event^.link;
        nlp$bm_release_message (event^.message_id);
        FREE event IN nav$network_paged_heap^;
        event := next_event;
      WHILEND;
      nlp$na_close_sap (sap, status);
    ELSE { Sap not open
      osp$clear_job_signature_lock (nav$open_network_sap_list_lock);
      osp$set_status_abnormal (nac$status_id, nae$sap_not_open, network, status);
      osp$append_status_integer (osc$status_parameter_delimiter, sap, 10, TRUE, status);
    IFEND;

  PROCEND nap$close_network_sap;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nap$deliver_network_event', EJECT ??
*copy nah$deliver_network_event

  PROCEDURE [XDCL] nap$deliver_network_event
    (    sap: nat$network_selector;
         source_address: nat$osi_network_address;
         device_id: nlt$device_identifier;
         data: nlt$bm_message_id);

    VAR
      event: ^^nat$network_event,
      ignore_status: ost$status,
      message_id: nlt$bm_message_id,
      new_event: ^nat$network_event,
      network_address: ^nat$osi_network_address,
      network_address_seq: ^SEQ ( * ),
      sap_descriptor: ^nat$open_network_sap_descriptor;

    REPEAT
      ALLOCATE new_event IN nav$network_paged_heap^;
      IF new_event = NIL THEN
        syp$cycle;
      IFEND;
    UNTIL new_event <> NIL;
    new_event^.link := NIL;
    new_event^.source.kind := nac$osi_network_address;
    new_event^.source.device_id := device_id;
    new_event^.source.network_address_length := #SIZE (source_address);
    network_address_seq := ^new_event^.source.network_address;
    RESET network_address_seq;
    NEXT network_address: [[REP #SIZE (source_address) OF cell]] IN network_address_seq;
    network_address^ := source_address;
    new_event^.message_id := data;
    osp$set_job_signature_lock (nav$open_network_sap_list_lock);
    find_sap_descriptor (sap, sap_descriptor);
    IF (sap_descriptor <> NIL) AND (sap_descriptor^.queued_messages < nac$network_queue_limit) THEN
      event := ^sap_descriptor^.event_queue;
      WHILE event^ <> NIL DO
        event := ^event^^.link;
      WHILEND;
      event^ := new_event;
      sap_descriptor^.queued_messages := sap_descriptor^.queued_messages + 1;
      IF sap_descriptor^.waiting_task_specified THEN

{ Ready the waiting task - called for performance only.

        pmp$ready_task (sap_descriptor^.waiting_task_id, ignore_status);
        sap_descriptor^.waiting_task_specified := FALSE;
      IFEND;
    ELSE { Unknown sap or max queue limit reached, release the data
      FREE new_event IN nav$network_paged_heap^;
      message_id := data;
      nlp$bm_release_message (message_id);
    IFEND;
    osp$clear_job_signature_lock (nav$open_network_sap_list_lock);

  PROCEND nap$deliver_network_event;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nap$open_network_sap', EJECT ??
*copy nah$open_network_sap

  PROCEDURE [XDCL] nap$open_network_sap
    (    sap_priority: nat$network_message_priority;
         sap: nat$network_sap_identifier;
     VAR status: ost$status);

    VAR
      sap_descriptor: ^nat$open_network_sap_descriptor;

    status.normal := TRUE;

{ Verify that the sap is not already open.

    osp$set_job_signature_lock (nav$open_network_sap_list_lock);
    find_sap_descriptor (sap, sap_descriptor);
    osp$clear_job_signature_lock (nav$open_network_sap_list_lock);
    IF sap_descriptor = NIL THEN
      ALLOCATE sap_descriptor IN nav$network_paged_heap^;
      IF sap_descriptor <> NIL THEN
        sap_descriptor^.waiting_task_specified := FALSE;
        sap_descriptor^.queued_messages := 0;
        sap_descriptor^.event_queue := NIL;
          nlp$na_open_sap (sap_priority, nac$deliver_network_event, sap, status);
          IF status.normal THEN
            sap_descriptor^.sap := sap;
            osp$set_job_signature_lock (nav$open_network_sap_list_lock);
            sap_descriptor^.link := nav$open_network_sap_list;
            nav$open_network_sap_list := sap_descriptor;
            osp$clear_job_signature_lock (nav$open_network_sap_list_lock);
          ELSE
            FREE sap_descriptor IN nav$network_paged_heap^;
          IFEND;
      ELSE
        osp$set_status_abnormal (nac$status_id, nae$allocation_failed, network, status);
      IFEND;
    ELSE { Sap already open
      osp$set_status_abnormal (nac$status_id, nae$sap_already_open, '', status);
      osp$append_status_integer (osc$status_parameter_delimiter, sap, 10, TRUE, status);
    IFEND;

  PROCEND nap$open_network_sap;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nap$receive_network_data', EJECT ??
*copy nah$receive_network_data

  PROCEDURE [XDCL] nap$receive_network_data
    (    sap: nat$network_sap_identifier;
         data_area: nat$data_fragments;
         wait_time: 0 .. 0ffffffff(16);
     VAR source: nat$network_layer_address;
     VAR received_data_length: integer;
     VAR status: ost$status);

    VAR
      event: ^nat$network_event,
      sap_descriptor: ^nat$open_network_sap_descriptor,
      task_id: ost$global_task_id,
      wait_timer: 0 .. 0ffffffff(16);

    status.normal := TRUE;
    wait_timer := wait_time;

  /check_for_datagram/
    REPEAT
      osp$set_job_signature_lock (nav$open_network_sap_list_lock);
      find_sap_descriptor (sap, sap_descriptor);
      IF sap_descriptor <> NIL THEN
        IF sap_descriptor^.event_queue <> NIL THEN
          event := sap_descriptor^.event_queue;
          sap_descriptor^.event_queue := event^.link;
          sap_descriptor^.queued_messages := sap_descriptor^.queued_messages - 1;
          osp$clear_job_signature_lock (nav$open_network_sap_list_lock);
          source := event^.source;
          nlp$bm_flush_message (data_area, event^.message_id, received_data_length, status);
          FREE event IN nav$network_paged_heap^;
          EXIT /check_for_datagram/
        ELSE
          IF wait_timer > 0 THEN
            IF NOT sap_descriptor^.waiting_task_specified THEN
              pmp$get_executing_task_gtid (sap_descriptor^.waiting_task_id);
              sap_descriptor^.waiting_task_specified := TRUE;
            IFEND;
            osp$clear_job_signature_lock (nav$open_network_sap_list_lock);
            pmp$wait (wait_timer, wait_timer);
            wait_timer := 0;
            CYCLE /check_for_datagram/
          ELSE { Timer expired and no datagrams were received
            pmp$get_executing_task_gtid (task_id);
            IF sap_descriptor^.waiting_task_specified AND (task_id = sap_descriptor^.waiting_task_id) THEN
              sap_descriptor^.waiting_task_specified := FALSE;
            IFEND;
            osp$clear_job_signature_lock (nav$open_network_sap_list_lock);
            osp$set_status_abnormal (nac$status_id, nae$no_datagram_available, network, status);
            osp$append_status_integer (osc$status_parameter_delimiter, sap, 10, TRUE, status);
          IFEND;
        IFEND;
      ELSE { Unknown sap
        osp$clear_job_signature_lock (nav$open_network_sap_list_lock);
        osp$set_status_abnormal (nac$status_id, nae$sap_not_open, network, status);
        osp$append_status_integer (osc$status_parameter_delimiter, sap, 10, TRUE, status);
      IFEND;
    UNTIL NOT status.normal;

  PROCEND nap$receive_network_data;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nap$send_network_data', EJECT ??
*copy nah$send_network_data

  PROCEDURE [XDCL] nap$send_network_data
    (    sap: nat$network_sap_identifier;
         destination: nat$network_layer_address;
         data: nat$data_fragments;
     VAR status: ost$status);

    VAR
      data_length: nat$data_length,
      ignore_route_congested: boolean,
      network_address: ^nat$osi_network_address,
      network_address_seq: ^SEQ ( * ),
      sap_descriptor: ^nat$open_network_sap_descriptor;

    status.normal := TRUE;
    nlp$al_get_data_length (data, data_length);
    IF data_length <= nac$xi_maximum_data_length THEN

{ Verify that the sap is open.

      osp$set_job_signature_lock (nav$open_network_sap_list_lock);
      find_sap_descriptor (sap, sap_descriptor);
      osp$clear_job_signature_lock (nav$open_network_sap_list_lock);
      IF sap_descriptor <> NIL THEN
          network_address_seq := ^destination.network_address;
          RESET network_address_seq;
          NEXT network_address: [[REP destination.network_address_length OF cell]] IN network_address_seq;
          nlp$na_send_data (sap, destination.device_id, network_address^, data, status);
      ELSE { Sap not open
        osp$set_status_abnormal (nac$status_id, nae$sap_not_open, network, status);
        osp$append_status_integer (osc$status_parameter_delimiter, sap, 10, TRUE, status);
      IFEND;
    ELSE { Max data length exceeded
      osp$set_status_abnormal (nac$status_id, nae$max_data_length_exceeded, network, status);
      osp$append_status_integer (osc$status_parameter_delimiter, data_length, 10, TRUE, status);
    IFEND;

  PROCEND nap$send_network_data;
?? OLDTITLE ??
?? NEWTITLE := '[INLINE] find_sap_descriptor', EJECT ??
{ PURPOSE:
{   The purpose of this procedure is to find the sap descriptor for the
{   specified network sap identifier.
{   The sap list must be locked by the caller.

  PROCEDURE [INLINE] find_sap_descriptor
    (    sap: nat$network_sap_identifier;
     VAR sap_descriptor: ^nat$open_network_sap_descriptor);

    sap_descriptor := nav$open_network_sap_list;
    WHILE ((sap_descriptor <> NIL) AND (sap_descriptor^.sap <> sap)) DO
      sap_descriptor := sap_descriptor^.link;
    WHILEND;

  PROCEND find_sap_descriptor;
?? OLDTITLE ??
MODEND nam$network_external_interface;
