?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE: Network Access : Channelnet External Interface' ??
MODULE nam$external_cn_interface;

{
{    PURPOSE:
{      The purpose of this module is to contain all NAM/VE Channelnet Application Layer interfaces.
{      The interfaces include application request interfaces and the "event processor" interface
{      which receives network events from the Channelnet Protocol Layer.
{
{    DESIGN:
{      This module is designed to be contained on the OSF$JOB_TEMPLATE_236 library and may execute
{      in any task.  Residence on the OSF$JOB_TEMPLATE_236 library restricts the use of the interfaces
{      to callers executing in rings 6 and below.
{
?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc nae$application_interfaces
*copyc nae$namve_conditions
*copyc nat$data_fragments
*copyc nat$network_address
*copyc nat$cn_interface
*copyc nat$open_cn_sap_descriptor
*copyc nlt$device_identifier
*copyc oss$job_paged_literal
*copyc oss$network_paged
?? POP ??
*copyc nlp$bm_create_message
*copyc nlp$bm_flush_message
*copyc nlp$bm_release_message
*copyc nlp$al_get_data_length
*copyc nlp$cn_open_sap
*copyc nlp$cn_send_datagram
*copyc nlp$cn_close_sap
*copyc osp$clear_job_signature_lock
*copyc osp$begin_subsystem_activity
*copyc osp$end_subsystem_activity
*copyc osp$append_status_integer
*copyc osp$set_status_abnormal
*copyc osp$set_job_signature_lock
*copyc pmp$wait
*copyc pmp$get_executing_task_gtid
*copyc pmp$get_microsecond_clock
*copyc pmp$ready_task
*copyc syp$cycle
*copyc nav$network_paged_heap
*copyc nav$namve_active
*copyc nav$open_cn_sap_list_lock
*copyc nav$open_cn_sap_list
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??
  VAR
    channelnet: [READ, oss$job_paged_literal] string (29) := 'Channelnet External Interface';

  CONST
    nac$cn_queue_limit = 256;

?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$cn_open_sap', EJECT ??

  PROCEDURE [XDCL, #GATE] nap$cn_open_sap
    (    sap: nat$cn_sap_id;
     VAR maximum_data_length: nat$data_length;
     VAR status: ost$status);

    VAR
      new_sap_desc,
      sap_desc: ^nat$open_cn_sap_descriptor,
      internal_status: ost$status;

    IF NOT nav$namve_active THEN
      osp$set_status_abnormal (nac$status_id, nae$network_inactive, '', status);
      RETURN;
    IFEND;

    status.normal := TRUE;
    osp$begin_subsystem_activity;
    ALLOCATE new_sap_desc IN nav$network_paged_heap^;
    IF new_sap_desc <> NIL THEN
      new_sap_desc^.waiting_task := FALSE;
      new_sap_desc^.sap_id := sap;
      new_sap_desc^.queued_messages := 0;
      new_sap_desc^.event_queue := NIL;
      pmp$get_executing_task_gtid (new_sap_desc^.sap_owner);
      nlp$cn_open_sap (sap, nac$cn_deliver_datagram, maximum_data_length, internal_status);
      IF internal_status.normal THEN
        new_sap_desc^.max_data_length := maximum_data_length;
        osp$set_job_signature_lock (nav$open_cn_sap_list_lock);
        new_sap_desc^.link := nav$open_cn_sap_list;
        nav$open_cn_sap_list := new_sap_desc;
        osp$clear_job_signature_lock (nav$open_cn_sap_list_lock);
      ELSE
        FREE new_sap_desc IN nav$network_paged_heap^;
        status := internal_status;
      IFEND;
    ELSE
      osp$set_status_abnormal (nac$status_id, nae$allocation_failed, channelnet, status);
    IFEND;
    osp$end_subsystem_activity;
  PROCEND nap$cn_open_sap;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$cn_send_datagram', EJECT ??

  PROCEDURE [XDCL, #GATE] nap$cn_send_datagram
    (    sap: nat$cn_sap_id;
         device: nlt$device_identifier;
         destination: nat$system_address;
         data: nat$data_fragments;
     VAR status: ost$status);

    VAR
      data_length: nat$data_length,
      message_id: nlt$bm_message_id,
      sap_desc: ^nat$open_cn_sap_descriptor,
      task_id: ost$global_task_id,
      internal_status: ost$status;

    status.normal := TRUE;
    nlp$al_get_data_length (data, data_length);
    osp$begin_subsystem_activity;
    osp$set_job_signature_lock (nav$open_cn_sap_list_lock);
    find_sap_descriptor (sap, sap_desc);
    osp$clear_job_signature_lock (nav$open_cn_sap_list_lock);
    IF sap_desc <> NIL THEN
      IF data_length <= sap_desc^.max_data_length THEN
        pmp$get_executing_task_gtid (task_id);
        IF sap_desc^.sap_owner = task_id THEN
          nlp$bm_create_message (data, message_id, internal_status);
          IF internal_status.normal THEN
            nlp$cn_send_datagram (sap, device, destination, message_id, internal_status);
            IF NOT internal_status.normal THEN
              status := internal_status;
            IFEND;
          ELSE
            status := internal_status;
          IFEND;
        ELSE
          osp$set_status_abnormal (nac$status_id, nae$invalid_task,
                'Task which does send must be task which opened sap', status);
        IFEND;
      ELSE
        osp$set_status_abnormal (nac$status_id, nae$max_data_length_exceeded, channelnet, status);
        osp$append_status_integer (osc$status_parameter_delimiter, sap_desc^.max_data_length, 10, TRUE,
              status);
      IFEND;
    ELSE
      osp$set_status_abnormal (nac$status_id, nae$sap_not_open, channelnet, status);
      osp$append_status_integer (osc$status_parameter_delimiter, sap, 16, TRUE, status);
    IFEND;
    osp$end_subsystem_activity;
  PROCEND nap$cn_send_datagram;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$cn_close_sap', EJECT ??

  PROCEDURE [XDCL, #GATE] nap$cn_close_sap
    (    sap: nat$cn_sap_id;
     VAR status: ost$status);

    VAR
      closed_sap_desc: ^nat$open_cn_sap_descriptor,
      event,
      next_event: ^nat$cn_event,
      next_sap_desc: ^nat$open_cn_sap_descriptor,
      sap_desc: ^^nat$open_cn_sap_descriptor,
      task_id: ost$global_task_id,
      internal_status: ost$status;

    status.normal := TRUE;
    osp$begin_subsystem_activity;
    osp$set_job_signature_lock (nav$open_cn_sap_list_lock);
    sap_desc := ^nav$open_cn_sap_list;
    WHILE (sap_desc^ <> NIL) AND (sap_desc^^.sap_id <> sap) DO
      sap_desc := ^sap_desc^^.link;
    WHILEND;
    IF sap_desc^ <> NIL THEN
      pmp$get_executing_task_gtid (task_id);
      IF sap_desc^^.sap_owner = task_id THEN
        event := sap_desc^^.event_queue;
        next_sap_desc := sap_desc^^.link;
        closed_sap_desc := sap_desc^;
        sap_desc^ := next_sap_desc;
        osp$clear_job_signature_lock (nav$open_cn_sap_list_lock);
        FREE closed_sap_desc IN nav$network_paged_heap^;
        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$cn_close_sap (sap, internal_status);
        IF NOT internal_status.normal THEN
          status := internal_status;
        IFEND;
      ELSE
        osp$clear_job_signature_lock (nav$open_cn_sap_list_lock);
        osp$set_status_abnormal (nac$status_id, nae$invalid_task,
              'Task which does close must be task which did open', status);
      IFEND;
    ELSE
      osp$clear_job_signature_lock (nav$open_cn_sap_list_lock);
      osp$set_status_abnormal (nac$status_id, nae$sap_not_open, channelnet, status);
      osp$append_status_integer (osc$status_parameter_delimiter, sap, 16, TRUE, status);
    IFEND;
    osp$end_subsystem_activity;
  PROCEND nap$cn_close_sap;
?? OLDTITLE ??
?? NEWTITLE := 'nap$cn_deliver_datagram', EJECT ??

  PROCEDURE [XDCL] nap$cn_deliver_datagram
    (    sap_id: nat$cn_sap_id;
         device: nlt$device_identifier;
         source: nat$system_address;
         datagram: nlt$bm_message_id);

    VAR
      event: ^^nat$cn_event,
      message_id: nlt$bm_message_id,
      new_event: ^nat$cn_event,
      sap_desc: ^nat$open_cn_sap_descriptor,
      ignore_status: ost$status;

    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^.device := device;
    new_event^.source := source;
    new_event^.message_id := datagram;
    osp$set_job_signature_lock (nav$open_cn_sap_list_lock);
    find_sap_descriptor (sap_id, sap_desc);
    IF (sap_desc <> NIL) AND (sap_desc^.queued_messages < nac$cn_queue_limit) THEN
      event := ^sap_desc^.event_queue;
      WHILE event^ <> NIL DO
        event := ^event^^.link;
      WHILEND;

      event^ := new_event;
      sap_desc^.queued_messages := sap_desc^.queued_messages + 1;
      IF sap_desc^.waiting_task THEN
        pmp$ready_task (sap_desc^.sap_owner, ignore_status);
        { ready_task status ignored - called for performance only }
        sap_desc^.waiting_task := FALSE;
      IFEND;
      osp$clear_job_signature_lock (nav$open_cn_sap_list_lock);
    ELSE
      {eat data }
      osp$clear_job_signature_lock (nav$open_cn_sap_list_lock);
      FREE new_event IN nav$network_paged_heap^;
      message_id := datagram;
      nlp$bm_release_message (message_id);
    IFEND;
  PROCEND nap$cn_deliver_datagram;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$cn_receive_datagram', EJECT ??

  PROCEDURE [XDCL, #GATE] nap$cn_receive_datagram
    (    sap: nat$cn_sap_id;
         data_area: nat$data_fragments;
         wait_time: 0 .. 0ffffffff(16);
     VAR device: nlt$device_identifier;
     VAR source: nat$system_address;
     VAR received_data_length: integer;
     VAR status: ost$status);

    VAR
      event: ^nat$cn_event,
      sap_desc: ^nat$open_cn_sap_descriptor,
      task_id: ost$global_task_id,
      wait_timer: 0 .. 0ffffffff(16),
      internal_status: ost$status;

    status.normal := TRUE;
    internal_status.normal := TRUE;
    wait_timer := wait_time;
    pmp$get_executing_task_gtid (task_id);

  /check_for_datagram/
    REPEAT
      osp$begin_subsystem_activity;
      osp$set_job_signature_lock (nav$open_cn_sap_list_lock);
      find_sap_descriptor (sap, sap_desc);
      IF sap_desc <> NIL THEN
        IF sap_desc^.sap_owner = task_id THEN
          IF sap_desc^.event_queue <> NIL THEN
            event := sap_desc^.event_queue;
            sap_desc^.event_queue := event^.link;
            sap_desc^.queued_messages := sap_desc^.queued_messages - 1;
            device := event^.device;
            source := event^.source;
            osp$clear_job_signature_lock (nav$open_cn_sap_list_lock);
            nlp$bm_flush_message (data_area, event^.message_id, received_data_length, internal_status);
            IF internal_status.normal THEN
              FREE event IN nav$network_paged_heap^;
            IFEND;
            osp$end_subsystem_activity;
            EXIT /check_for_datagram/
          ELSE
            IF wait_timer > 0 THEN
              sap_desc^.waiting_task := TRUE;
              osp$clear_job_signature_lock (nav$open_cn_sap_list_lock);
              osp$end_subsystem_activity;
              pmp$wait (wait_timer, wait_timer);
              wait_timer := 0;
              CYCLE /check_for_datagram/
            ELSE { no datagrams }
              sap_desc^.waiting_task := FALSE;
              osp$set_status_abnormal (nac$status_id, nae$no_datagram_available, channelnet, internal_status);
              osp$append_status_integer (osc$status_parameter_delimiter, sap, 16, TRUE, internal_status);
            IFEND;
          IFEND;
        ELSE
          osp$set_status_abnormal (nac$status_id, nae$invalid_task,
                'Task which does receive must be task which did open', internal_status);
        IFEND;
      ELSE
        osp$set_status_abnormal (nac$status_id, nae$sap_not_open, channelnet, internal_status);
        osp$append_status_integer (osc$status_parameter_delimiter, sap, 16, TRUE, internal_status);
      IFEND;
      osp$clear_job_signature_lock (nav$open_cn_sap_list_lock);
      osp$end_subsystem_activity;
    UNTIL NOT internal_status.normal;
    IF NOT internal_status.normal THEN
      status := internal_status;
    IFEND;
  PROCEND nap$cn_receive_datagram;
?? OLDTITLE ??
?? NEWTITLE := '[INLINE] find_sap_descriptor', EJECT ??

  PROCEDURE [INLINE] find_sap_descriptor
    (    sap: nat$cn_sap_id;
     VAR sap_desc: ^nat$open_cn_sap_descriptor);

    sap_desc := nav$open_cn_sap_list;
    WHILE ((sap_desc <> NIL) AND (sap_desc^.sap_id <> sap)) DO
      sap_desc := sap_desc^.link;
    WHILEND;

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