1 /****^  *********************************************************
   2         *                                                       *
   3         * Copyright, (C) BULL HN Information Systems Inc., 1989 *
   4         *                                                       *
   5         ********************************************************* */
   6 
   7 
   8 
   9 /****^  HISTORY COMMENTS:
  10   1) change(89-02-21,Parisek), approve(89-10-25,MECR0012),
  11      audit(89-10-25,Farley), install(89-10-25,MR12.3-1100):
  12      Implement protocol multiplexer.
  13                                                    END HISTORY COMMENTS */
  14 
  15 /* Pseudo-multiplexor implementing the interface between the MCS environment and the */
  16 /*                           ISO Transport level                                 */
  17 /*                 C. Claveleira - CICB - 18 september 1985                       */
  18 
  19 
  20 /* format: style4,delnl,insnl,^ifthendo,indattr */
  21 
  22 protocol_mpx:
  23      proc;
  24 
  25 /*        Date Last Modified and Reason
  26 
  27 
  28    7  oct 1985 : C. Claveleira - Version 0.0
  29    19 oct 1985 : C. Claveleira - Version 0.1
  30    30 oct 1985 : C. Claveleira - Version 0.2
  31    05 nov 1985 : C. Claveleira - Version 0.3
  32    12 nov 1985 : C. Claveleira - Version 0.4
  33    27 nov 1985 : C. Claveleira - Version 0.5
  34    27 dec 1985 : C. Claveleira - Version 0.6 (Changes for padding x25 packets and using USER_INTERRUPT for n_con_conf)
  35    14 fev 1986 : C. Claveleira - Version 0.7
  36    14 mar 1986 : C. Claveleira - Version 0.8
  37    20 mar 1986 : C. Claveleira - Version 1.0 (multiprotocole version)
  38    02 avr 1986 : C. Claveleira - Version 1.1 (correction bug liee au timers)
  39    21 avr 1986 : C. Claveleira - Version 1.2 (     "      "  lors needs_space
  40    31 mai 1986 : C. Claveleira - Version 1.3 (modifs de ntd_req et ndis_req, ajout de write et read)
  41    10 jui 1986 : C. Claveleira - Version 1.4 (correction passage lastp lors appel copy_chars dans send_common)
  42    11 aou 1986 : C. Claveleira - Version 1.5 (decompte de buffers ds write/ndt_req, modif. max_chain_len et
  43    check-up connections)
  44    02 sep 1986 : C. Claveleira - Version 1.6 (test taille nsdu, modif. decl. user_index, cor. bug
  45    check_orphan_connections et timer)
  46    19 sep 1986 : C. Claveleira - Version 1.7 (correction bug remplissage paquets x25)
  47    26 nov 1986 : C. Claveleira - Version 1.8 (ajouts/modifs concernant le listening)
  48 */
  49 
  50 /* PARAMETRES */
  51 dcl  a_argptr               ptr;
  52 dcl  a_data_base_ptr        ptr;
  53 dcl  a_partial              bit (1);
  54 dcl  a_event                fixed bin (71);                 /* event channel name */
  55 dcl  a_from_na              char (15) varying;              /* address of calling network */
  56 dcl  a_to_na                char (15) varying;              /* addresse of network called */
  57 dcl  a_user_index           fixed bin (17);                 /* user index */
  58 dcl  a_call_data            char (48) varying;              /* call data during NCON_REQ */
  59 dcl  a_order                char (*);
  60 dcl  a_scp                  ptr;                            /* pointer to buffers chain of T */
  61 dcl  a_pinfop               ptr;
  62 dcl  a_int_type             fixed bin;
  63 dcl  a_type                 fixed bin;
  64 dcl  a_first_entry          fixed bin;
  65 dcl  a_bytes_processed      fixed bin (21);                 /* number of octets sent/read by ndt_req,send/read */
  66 dcl  a_offset               fixed bin (21);
  67 dcl  a_protocol_id          fixed bin;                      /* identification of protocol */
  68 dcl  a_info                 bit (72) aligned;
  69 dcl  a_code                 fixed bin (35);
  70 
  71 /* VARIABLES AUTOMATIQUES */
  72 dcl  from_na                char (15) varying;
  73 dcl  to_na                  char (15) varying;
  74 dcl  user_index             fixed bin (17);
  75 dcl  call_data              char (48) varying;
  76 dcl  bytes_processed        fixed bin (21);
  77 dcl  scx                    fixed bin;
  78 dcl  miip                   ptr;
  79 dcl  order                  char (32);
  80 dcl  scp                    ptr;                            /* source_chain_pointer */
  81 dcl  tcp                    ptr;                            /* target_chain_ptr */
  82 dcl  scl                    fixed bin;                      /* source_chain_length */
  83 dcl  cscp                   ptr;                            /* current_source_chain_ptr */
  84 dcl  length_to_copy         fixed bin;
  85 dcl  sci                    fixed bin (21);                 /* source_chain_index */
  86 dcl  (scidx, tidx)          fixed bin;                      /* index into the list of user pointers */
  87 dcl  (break, stop, end_chain, write_entry, read_entry)
  88                             bit (1);
  89 dcl  buf_size               fixed bin;
  90 dcl  protocol_id            fixed bin;
  91 dcl  orig_buf_size          fixed bin;
  92 dcl  lchar                  fixed bin;
  93 dcl  cur_space              fixed bin;
  94 dcl  cur_chain_len          fixed bin;
  95 dcl  max_space              fixed bin;                      /* maximum number of words this guy can have */
  96 dcl  max_chars              fixed bin;                      /* maximun number of character of output */
  97 dcl  max_chars_in_buf       fixed bin;                      /* number of characters in maximum-size buffer */
  98 dcl  chars_in_buf           fixed bin;
  99 dcl  (new_bufp, headp, lastp, prevp)
 100                             ptr;
 101 dcl  new_buf                fixed bin;
 102 dcl  rest                   fixed bin (21);
 103 dcl  twx                    fixed bin;                      /* tty index  */
 104 dcl  code                   fixed bin (35);                 /* error code */
 105 dcl  event                  fixed bin (71);
 106 dcl  cleanup                condition;
 107 dcl  (i, m, n)              fixed bin;
 108 dcl  devx                   fixed bin (17);
 109 dcl  locked                 bit (1);
 110 dcl  1 CON_REQ_info         aligned like NCON_REQ_info;
 111 dcl  1 N_I_i                aligned based (addr (buffer.chars)) like NCON_IND_info;
 112 dcl  1 net_infos            aligned like network_infos;
 113 dcl  ignore_code            fixed bin (35);
 114 dcl  int_type               fixed bin;
 115 dcl  inchain                fixed bin (18);
 116 dcl  next_offset            fixed bin;
 117 dcl  last_offset            fixed bin;
 118 dcl  new_headp              ptr;
 119 dcl  old_tailp              ptr;
 120 dcl  new_first_tally        fixed bin;
 121 dcl  old_last_tally         fixed bin;
 122 dcl  max_tally              fixed bin;
 123 dcl  filled                 bit (1);
 124 dcl  source_ptr             ptr;
 125 dcl  target_ptr             ptr;
 126 dcl  pxss_status            fixed bin (35);
 127 dcl  process_id             bit (36) aligned;
 128 dcl  partial                bit (1);
 129 
 130 dcl  1 CON_IND_info         aligned like NCON_IND_info based (pinfop);
 131 dcl  1 ndis_ind_reason      aligned like NDIS_IND_REASON based (pinfop);
 132 dcl  new_chars              char (new_first_tally) based;
 133 
 134 
 135 dcl  STANDARD_SERVICE       char (16) int static init ("^@^@@^@^@^A");
 136                                                             /* indicates DDN/X.25 standard service */
 137 dcl  ever_initialized       bit (1) int static init ("0"b); /* indicates whether init_multiplexer ever called before */
 138 
 139 
 140 dcl  (
 141      error_table_$action_not_performed,
 142      error_table_$bad_arg,
 143      error_table_$buffer_big,
 144      error_table_$io_no_permission,
 145      error_table_$invalid_state,
 146      error_table_$invalid_device,
 147      error_table_$invalid_write,
 148      error_table_$noalloc,
 149      error_table_$resource_unavailable,
 150      error_table_$undefined_order_request
 151      )                      ext fixed bin (35);
 152 
 153 dcl  no_write_code          fixed bin (35) internal static;
 154 dcl  noalloc_code           fixed bin (35) internal static; /* copy of code to be used at interrupt time */
 155 
 156 
 157 dcl  pds$processid          ext static bit (36) aligned;
 158 
 159 dcl  tty_lock$lock_channel  entry (fixed bin, fixed bin (35));
 160 dcl  tty_lock$unlock_channel
 161                             entry (fixed bin);
 162 dcl  pxss$ring_0_wakeup     entry (bit (36) aligned, fixed bin (71), fixed bin (71), fixed bin (35));
 163 dcl  pxss$unique_ring_0_wakeup
 164                             entry (bit (36) aligned, fixed bin (71), fixed bin (71), fixed bin (35));
 165 dcl  tc_util$validate_processid
 166                             entry (bit (36) aligned, fixed bin (35));
 167 dcl  syserr                 entry options (variable);
 168 dcl  wire_proc$wire_me      entry;
 169 
 170 dcl  DIAG_71                fixed bin int static init (71) options (constant);
 171 dcl  LONGEST_POSSIBLE_STRING
 172                             fixed bin init (8128) int static options (constant);
 173 dcl  MILLISECONDS_2         fixed bin (21) int static init (2000000) options (constant);
 174 dcl  SIZE4                  fixed bin int static init (4) options (constant);
 175 dcl  SIZE16                 fixed bin int static init (16) options (constant);
 176 dcl  WAKEUP_CODE_0          fixed bin int static init (0) options (constant);
 177 dcl  WAKEUP_CODE_5          fixed bin int static init (5) options (constant);
 178 dcl  WAKEUP_CODE_100        fixed bin int static init (100) options (constant);
 179 dcl  max_chain_len          fixed bin int static init (64) options (constant);
 180                                                             /* permitted to have NSDU of 4096 octets max with buffers of 124 characters (2 buffers/packet) */
 181 
 182 dcl  (addr, bin, ceil, clock, divide, hbound, lbound, length, min, null, ptr, rel, string, substr, unspec)
 183                             builtin;
 184 %page;
 185 
 186           return;
 187 
 188 start:
 189      entry (a_data_base_ptr, a_code);
 190 
 191           a_code = 0;
 192           channel_ptr = a_data_base_ptr;
 193           devx = channel.devx;
 194           locked = "0"b;
 195           channel.flags.started = "1"b;
 196           if channel.state = HUNGUP
 197           then call channel_manager$control (devx, "listen", null (), code);
 198           a_code = code;
 199           return;
 200 
 201 
 202 stop:
 203      entry (a_data_base_ptr, a_code);
 204           channel_ptr = a_data_base_ptr;
 205           channel.flags.started = "0"b;
 206           a_code = 0;
 207           locked = "0"b;
 208           return;
 209 
 210 
 211 shutdown:
 212      entry (a_data_base_ptr, a_code);
 213           channel_ptr = a_data_base_ptr;
 214           locked = "0"b;
 215           devx = channel.devx;
 216           if channel.state > HUNGUP
 217           then call channel_manager$control (devx, "hangup", null (), code);
 218           a_code = code;
 219           return;
 220 
 221 
 222 priv_control:
 223      entry (a_data_base_ptr, a_order, a_pinfop, a_code);
 224 
 225           channel_ptr = a_data_base_ptr;
 226           order = a_order;
 227           pinfop = a_pinfop;                                /* nothing for time */
 228           a_code = 0;
 229           locked = "0"b;
 230           return;
 231 
 232 
 233 hpriv_control:
 234      entry (a_data_base_ptr, a_order, a_pinfop, a_code);
 235           channel_ptr = a_data_base_ptr;
 236           order = a_order;
 237           pinfop = a_pinfop;
 238           locked = "0"b;
 239           devx = channel.devx;
 240           if order = "load_mpx"
 241           then if channel.flags.initialized & channel.state = INACTIVE
 242                then do;
 243                     call channel_manager$control (devx, "get_network_infos", addr (net_infos), code);
 244                     if code = 0
 245                     then do;
 246                          channel.our_network_address = net_infos.network_address;
 247                          channel.max_packet_size = net_infos.max_packet_size;
 248                          channel.load_proc_id = pds$processid;
 249                          channel.state = HUNGUP;
 250                     end;
 251                end;
 252                else code = error_table_$invalid_state;
 253           else code = error_table_$undefined_order_request;
 254           a_code = code;
 255           return;
 256 
 257 %page;
 258 init_multiplexer:                                           /* do the work of an init_channel, in fact */
 259      entry (twx, a_argptr, a_data_base_ptr, a_code);
 260 
 261           devx = twx;
 262           infop = addr (dn355_data$);
 263           protocol_data_ptr = datanet_info.protocol_datap;
 264           ttybp = addr (tty_buf$);
 265           lctp = tty_buf.lct_ptr;
 266           lcntp = lct.lcnt_ptr;
 267           miip = a_argptr;
 268           a_data_base_ptr = null;
 269           a_code = 0;
 270           locked = "0"b;
 271 
 272           if ^ever_initialized
 273           then do;                                          /* initialization of the data base */
 274                do i = 1 to protocol_data.max_channels;
 275                     unspec (protocol_data.channels (i)) = ""b;
 276                     protocol_data.channels (i).name = "";
 277                     protocol_data.channels (i).our_network_address = "";
 278                     protocol_data.channels (i).his_network_address = "";
 279                     protocol_data.channels (i).call_data = "";
 280                     protocol_data.channels (i).facilities = "";
 281                end;
 282                protocol_data.n_channels = 0;
 283                protocol_data.init_time = clock ();
 284                unspec (listeners (*)) = ""b;
 285                unspec (protocol_data.special_listeners (*)) = ""b;
 286                protocol_data.special_listeners.datas (*) = "";
 287                call set_static;
 288                ever_initialized = "1"b;
 289           end;
 290 
 291           if protocol_data.n_channels >= protocol_data.max_channels
 292           then do;                                          /* more channels declared than you have  ? */
 293                call syserr (Log_message, "protocol_mpx$init_multiplexer : not enough channels configured on PROT PARM.");
 294                a_code = noalloc_code;
 295                return;
 296           end;
 297           protocol_data.n_channels = protocol_data.n_channels + 1;
 298           do i = 1 to protocol_data.max_channels;
 299                channel_ptr = addr (protocol_data.channels (i));
 300                if ^channel.flags.initialized
 301                then do;
 302                     channel.name = lcnt (devx).names;
 303                     channel.devx = devx;
 304                     channel.state = INACTIVE;
 305                     channel.flags.initialized = "1"b;
 306                     a_data_base_ptr = channel_ptr;
 307                     return;
 308                end;
 309           end;
 310           call syserr (Log_message, "protocol_mpx$init_multiplexer : no free channel found");
 311           a_code = noalloc_code;
 312           return;
 313 
 314 %page;
 315 
 316 terminate_multiplexer:
 317      entry (a_data_base_ptr, a_code);
 318 
 319           a_code = 0;
 320           locked = "0"b;
 321           channel_ptr = a_data_base_ptr;
 322           infop = addr (dn355_data$);
 323           protocol_data_ptr = datanet_info.protocol_datap;
 324           call mcs_timer$reset_all (channel.devx);          /* purge the event timers */
 325           protocol_data.n_channels = protocol_data.n_channels - 1;
 326           if protocol_data.n_channels < 0
 327           then call syserr (Log_message,
 328                     "protocol_mpx$terminate_multiplexer : more terminate_multiplexer calls than init_multiplexer calls on ^a",
 329                     channel.name);
 330           unspec (channel) = ""b;
 331           a_data_base_ptr = null ();
 332           return;
 333 %page;
 334 set_listener:
 335      entry (a_protocol_id, a_event, a_code);
 336           event = a_event;
 337           protocol_id = a_protocol_id;
 338           locked = "0"b;
 339           infop = addr (dn355_data$);
 340           protocol_data_ptr = datanet_info.protocol_datap;
 341           if protocol_id < lbound (protocol_data.listeners, 1) | protocol_id > hbound (protocol_data.listeners, 1)
 342           then do;
 343                a_code = error_table_$bad_arg;
 344                return;
 345           end;                                              /* Not test to learn if there is already a listener : use the last one. */
 346           protocol_data.listeners (protocol_id).proc_id = pds$processid;
 347           protocol_data.listeners (protocol_id).event_id = event;
 348           a_code = 0;
 349           return;
 350 
 351 set_special_listener:
 352      entry (a_call_data, a_partial, a_event, a_code);
 353           call_data = a_call_data;
 354           event = a_event;
 355           partial = a_partial;
 356           a_code = 0;
 357           locked = "0"b;
 358           do i = 1 to hbound (layer3_call_datas, 1) while (call_data ^= layer3_call_datas (i));
 359           end;
 360           if i <= hbound (layer3_call_datas, 1) | call_data = ""
 361           then do;
 362                a_code = error_table_$bad_arg;
 363                return;
 364           end;
 365           infop = addr (dn355_data$);
 366           protocol_data_ptr = datanet_info.protocol_datap;
 367           n = hbound (protocol_data.special_listeners, 1);
 368           do i = 1 to n while (protocol_data.special_listeners.call_datas.datas (i) ^= call_data);
 369           end;
 370           if i > n
 371           then                                              /* find a slot */
 372                do i = 1 to n while (protocol_data.special_listeners.call_datas.datas (i) ^= "");
 373           end;
 374           if i > n
 375           then do;
 376                a_code = error_table_$action_not_performed;
 377                return;
 378           end;
 379           protocol_data.special_listeners.call_datas.partial (i) = partial;
 380           protocol_data.special_listeners.call_datas.datas (i) = call_data;
 381           protocol_data.special_listeners.proc_id (i) = pds$processid;
 382           protocol_data.special_listeners.event_id (i) = event;
 383           return;
 384 
 385 remove_listener:
 386      entry (a_protocol_id, a_code);
 387           protocol_id = a_protocol_id;
 388           locked = "0"b;
 389           infop = addr (dn355_data$);
 390           protocol_data_ptr = datanet_info.protocol_datap;
 391           if protocol_id < lbound (protocol_data.listeners, 1) | protocol_id > hbound (protocol_data.listeners, 1)
 392           then do;
 393                a_code = error_table_$bad_arg;
 394                return;
 395           end;
 396           a_code = 0;
 397           if protocol_data.listeners (protocol_id).proc_id ^= pds$processid
 398           then a_code = error_table_$io_no_permission;
 399           else unspec (protocol_data.listeners (protocol_id)) = ""b;
 400           return;
 401 
 402 remove_special_listener:
 403      entry (a_call_data, a_code);
 404           call_data = a_call_data;
 405           locked = "0"b;
 406           a_code = 0;
 407           if call_data = ""
 408           then do;
 409                a_code = error_table_$bad_arg;
 410                return;
 411           end;
 412           infop = addr (dn355_data$);
 413           protocol_data_ptr = datanet_info.protocol_datap;
 414           do i = 1 to hbound (protocol_data.special_listeners, 1)
 415                while (protocol_data.special_listeners.call_datas (i).datas ^= call_data);
 416           end;
 417           if i > hbound (protocol_data.special_listeners, 1)
 418           then do;
 419                a_code = error_table_$action_not_performed;
 420                return;
 421           end;
 422           if protocol_data.special_listeners (i).proc_id ^= pds$processid
 423           then a_code = error_table_$io_no_permission;
 424           else do;
 425                protocol_data.special_listeners (i).call_datas.datas = "";
 426                protocol_data.special_listeners (i).call_datas.partial = "0"b;
 427                protocol_data.special_listeners (i).proc_id = ""b;
 428                protocol_data.special_listeners (i).event_id = 0;
 429           end;
 430           return;
 431 
 432 %page;
 433 ncon_req:                                                   /* Request a network connection - N_CON_REQ */
 434      entry (a_user_index, twx, a_event, a_from_na, a_to_na, a_call_data, a_code);
 435 
 436           user_index = a_user_index;
 437           event = a_event;
 438           from_na = a_from_na;
 439           to_na = a_to_na;
 440           call_data = a_call_data;
 441 
 442           ttybp = addr (tty_buf$);
 443           lctp = tty_buf.lct_ptr;
 444           infop = addr (dn355_data$);
 445           protocol_data_ptr = datanet_info.protocol_datap;
 446           call check_orphan_connections;                    /* clean up abandoned connections */
 447           twx = 0;
 448           scx = get_channel (from_na);                      /* find an available channel */
 449           if scx = 0
 450           then do;
 451                a_code = error_table_$resource_unavailable;
 452                return;
 453           end;
 454           channel_ptr = addr (protocol_data.channels (scx));
 455           devx = channel.devx;
 456           locked = "0"b;
 457           on cleanup call cleaner;
 458           call tty_lock$lock_channel (devx, code);
 459           if code ^= 0
 460           then goto unlock;
 461           locked = "1"b;
 462 
 463           channel.user_proc_id, channel.listener_proc_id = pds$processid;
 464           channel.user_event, channel.listener_event = event;
 465           channel.user_ref = user_index;
 466           channel.flags.in_use = "1"b;
 467           channel.his_network_address = to_na;
 468           channel.space_left_in_packet = channel.max_packet_size;
 469 
 470           CON_REQ_info.mbz = 0;
 471           CON_REQ_info.to_address = to_na;
 472           CON_REQ_info.facilities = STANDARD_SERVICE;
 473           CON_REQ_info.data = call_data;
 474           call channel_manager$control (devx, "dial_out", addr (CON_REQ_info), code);
 475           if code = 0
 476           then channel.state = DIALING;
 477           else call reset_channel;
 478           twx = devx;
 479           goto unlock;                                      /* unlock and return */
 480 
 481 
 482 
 483 ncon_resp:                                                  /* reponse to request for an network connection - N_CON_RESP */
 484      entry (a_user_index, twx, a_event, a_code);
 485           locked = "0"b;
 486           on cleanup call cleaner;
 487           call setup;
 488           if channel.state ^= DIALING
 489           then do;
 490                code = error_table_$invalid_state;
 491                goto unlock;
 492           end;
 493           channel.user_ref = a_user_index;
 494           channel.user_event = a_event;
 495           channel.user_proc_id = pds$processid;
 496           call channel_manager$control (devx, "connect_response", null (), code);
 497           if code = 0
 498           then do;
 499                channel.state = DIALED;
 500                channel.rflag = "1"b;
 501           end;
 502           goto unlock;                                      /* unlock and return */
 503 
 504 write:
 505      entry (twx, a_pinfop, a_first_entry, a_offset, a_bytes_processed, a_code);
 506           pinfop = a_pinfop;
 507           scidx = a_first_entry;
 508           sci = a_offset;
 509           a_bytes_processed = 0;
 510           locked = "0"b;
 511           on cleanup call cleaner;
 512           call setup;
 513           if channel.state ^= DIALED
 514           then goto inv_state;
 515           if pinfop ^= null ()
 516           then if scidx < 0 | scidx > transmit_info.n_entries | sci < 0 | sci > transmit_info (scidx).size - 1
 517                then goto bad_args;
 518                else ;
 519           else goto bad_args;
 520           scl = 0;
 521           do i = scidx to transmit_info.n_entries;
 522                scl = scl + transmit_info (i).size;
 523           end;
 524           scl = scl - sci;
 525           if scl <= 0
 526           then goto unlock;
 527           write_entry = "1"b;
 528           break = "1"b;
 529           goto send_common;
 530 
 531 ndt_req:                                                    /* request to send network data - N_DATA_REQ */
 532      entry (twx, a_scp, a_offset, a_bytes_processed, a_code);
 533 
 534           scp = a_scp;
 535           sci = a_offset;
 536           scl = 0;
 537           a_bytes_processed = 0;
 538           locked = "0"b;
 539           on cleanup call cleaner;
 540           call setup;
 541           if channel.state ^= DIALED
 542           then do;
 543 inv_state:
 544                code = error_table_$invalid_state;
 545                goto unlock;
 546           end;
 547 
 548           if scp = null ()
 549           then do;
 550 bad_args:
 551                code = error_table_$bad_arg;
 552                goto unlock;
 553           end;
 554           if sci < 0 | sci >= scp -> buffer.tally
 555           then goto bad_args;
 556           blockp = scp;
 557           call check_length (break, scl);                   /* calculate the length of the user chain */
 558           scl = scl - sci;
 559           if scl = 0
 560           then goto unlock;
 561           cscp = scp;
 562           write_entry = "0"b;
 563 send_common:
 564           bytes_processed = 0;
 565           if scl > channel.max_nsdu_size
 566           then do;
 567                code = error_table_$buffer_big;
 568                goto unlock;
 569           end;
 570           cur_space = 0;                                    /* calculate the space which you are able to claim */
 571           cur_chain_len = 0;
 572           if channel.write_first ^= 0
 573           then do;
 574                blockp = ptr (ttybp, channel.write_first);
 575                end_chain = "0"b;
 576                do while (^end_chain);
 577                     cur_space = cur_space + SIZE16 * (buffer.size_code + 1);
 578                     cur_chain_len = cur_chain_len + 1;
 579                     if buffer.next = 0
 580                     then end_chain = "1"b;
 581                     else blockp = ptr (ttybp, buffer.next);
 582                end;
 583           end;
 584           max_space =
 585                min (divide (tty_buf.bleft, output_bpart, 17, 0) - cur_space,
 586                (max_chain_len - cur_chain_len) * (channel.max_buf_size - 1));
 587 
 588           max_chars_in_buf = SIZE4 * (channel.max_buf_size - 1) - channel.buffer_pad;
 589 
 590           if max_space <= 0                                 /* if unfortunately, you can do nothing else... */
 591           then do;
 592                if channel.send_output
 593                then call tty_space_man$needs_space (devx);
 594                else channel.flags.wflag = "1"b;
 595                goto unlock;
 596           end;
 597           max_chars = min (SIZE4 * max_space, LONGEST_POSSIBLE_STRING);
 598           cur_chain_len = max_chain_len - cur_chain_len;    /* prepare a breakdown of the buffers */
 599           length_to_copy = min (max_chars, scl);
 600           if length_to_copy < scl
 601           then break = "0"b;
 602 
 603           if channel.write_last ^= 0                        /* is there a chain in stock ? */
 604           then do;
 605                lastp, blockp = ptr (ttybp, channel.write_last);
 606                if buffer.tally < max_chars_in_buf & ^buffer.flags.break
 607                     & channel.space_left_in_packet ^= channel.max_packet_size
 608                                                             /* if yes can you use the last buffer ? */
 609                then do;
 610                     buf_size, orig_buf_size = SIZE16 * (buffer.size_code + 1);
 611                     lchar = buffer.tally;
 612                     stop = "0"b;
 613                     do while (^stop);                       /* should you enlarge this buffer ? */
 614                          chars_in_buf = SIZE4 * (buf_size - 1) - channel.buffer_pad;
 615                          if lchar + length_to_copy <= chars_in_buf | chars_in_buf = max_chars_in_buf
 616                               | lchar + channel.space_left_in_packet <= chars_in_buf
 617                          then stop = "1"b;
 618                          else buf_size = buf_size + SIZE16;
 619                     end;
 620 
 621                     if buf_size ^= orig_buf_size            /* find a bigger buffer */
 622                     then do;
 623                          call tty_space_man$get_buffer (devx, buf_size, OUTPUT, new_bufp);
 624                          if new_bufp ^= null ()
 625                          then do;
 626                               call copy_chars ((lastp), 0, (lastp -> buffer.tally), new_bufp, 0);
 627                                                             /* copy the old buffer into the new one */
 628                               new_buf = bin (rel (new_bufp), 18);
 629                               new_bufp -> buffer.tally = lastp -> buffer.tally;
 630                               channel.write_last = new_buf; /* thread new buffer onto end of chain in place of old one */
 631 
 632                               prevp = ptr (ttybp, channel.write_first);
 633                                                             /* start at head */
 634                               if prevp = lastp              /* is it tail also? */
 635                               then channel.write_first = channel.write_last;
 636                                                             /* that's simple */
 637                               else do;                      /* else we'll scan the chain */
 638                                    do prevp = prevp repeat ptr (ttybp, prevp -> buffer.next)
 639                                         while (prevp -> buffer.next ^= bin (rel (lastp), 18) & prevp -> buffer.next ^= 0);
 640                                    end;
 641                                    prevp -> buffer.next = channel.write_last;
 642                                                             /* found the next-to-last one */
 643                               end;
 644 
 645                               call tty_space_man$free_buffer (devx, OUTPUT, lastp);
 646                               lastp = new_bufp;
 647 
 648                          end;
 649                          else chars_in_buf = SIZE4 * (orig_buf_size - 1) - channel.buffer_pad;
 650                     end;
 651                     n = min (length_to_copy, chars_in_buf - lchar, channel.space_left_in_packet);
 652                     if write_entry
 653                     then call copy_from_user_to_us (scidx, sci, n, lastp, (lastp -> buffer.tally));
 654                     else call copy_chars (cscp, sci, n, lastp, (lastp -> buffer.tally));
 655                     bytes_processed = bytes_processed + n;
 656                     channel.space_left_in_packet = channel.space_left_in_packet - n;
 657                     if channel.space_left_in_packet = 0
 658                     then channel.space_left_in_packet = channel.max_packet_size;
 659                     length_to_copy = length_to_copy - n;
 660                     lastp -> buffer.tally = lastp -> buffer.tally + n;
 661                     string (lastp -> buffer.flags) = "0"b;
 662                     lastp -> buffer.next = 0;
 663                end;
 664 
 665           end;
 666 
 667           do length_to_copy = length_to_copy repeat (length_to_copy - n) while (length_to_copy > 0);
 668                m = min (length_to_copy, channel.space_left_in_packet);
 669                if m >= max_chars_in_buf
 670                then do;
 671                     n = max_chars_in_buf;
 672                     buf_size = channel.max_buf_size;
 673                end;
 674                else do;                                     /* find a buffer of appropriate size */
 675                     stop = "0"b;
 676                     buf_size = SIZE16;
 677                     do while (^stop);
 678                          chars_in_buf = SIZE4 * (buf_size - 1) - channel.buffer_pad;
 679                          if m <= chars_in_buf
 680                          then stop = "1"b;
 681                          else buf_size = buf_size + SIZE16;
 682                     end;
 683                     n = m;
 684                end;
 685                if cur_chain_len = 0
 686                then new_bufp = null ();
 687                else call tty_space_man$get_buffer (devx, buf_size, OUTPUT, new_bufp);
 688                if new_bufp = null ()
 689                then goto try_to_send;
 690                cur_chain_len = cur_chain_len - 1;           /* update the breakdown of the buffers */
 691                if write_entry
 692                then call copy_from_user_to_us (scidx, sci, n, new_bufp, 0);
 693                else call copy_chars (cscp, sci, n, new_bufp, 0);
 694                bytes_processed = bytes_processed + n;
 695                new_bufp -> buffer.tally = n;
 696                new_buf = bin (rel (new_bufp), 18);
 697                channel.space_left_in_packet = channel.space_left_in_packet - n;
 698                if channel.space_left_in_packet = 0
 699                then channel.space_left_in_packet = channel.max_packet_size;
 700                if channel.write_last = 0
 701                then                                         /* chain the new buffer */
 702                     channel.write_first = new_buf;
 703                else lastp -> buffer.next = new_buf;
 704                channel.write_last = new_buf;
 705                lastp = new_bufp;
 706                new_bufp -> buffer.next = 0;
 707                string (new_bufp -> buffer.flags) = "0"b;
 708           end;
 709 
 710           if break & length_to_copy = 0
 711           then do;
 712                lastp -> buffer.flags.break = "1"b;
 713                channel.space_left_in_packet = channel.max_packet_size;
 714                if write_entry
 715                then scidx = 0;                              /* in case the caller structure should terminate with some entries with  size = 0 */
 716           end;
 717 
 718 try_to_send:
 719           if channel.send_output
 720           then call send_next_nsdu;
 721           if write_entry & scidx ^= 0 | ^write_entry & cscp ^= null ()
 722           then do;                                          /* if all of the user chain has not been sent */
 723                                                             /* (in fact one can stop with a break (?) ) */
 724                channel.flags.wflag = "1"b;
 725                if channel.flags.send_output
 726                then call tty_space_man$needs_space (devx);  /* if send_next_nsdu has not called channel_manager$write then you ought to reclaim the space! */
 727           end;
 728           a_offset = sci;
 729           a_bytes_processed = bytes_processed;
 730           if write_entry
 731           then a_first_entry = scidx;
 732           else a_scp = cscp;
 733           goto unlock;
 734 
 735 
 736 ndis_req:                                                   /* request for a network disconnection - N_DIS_REQ */
 737      entry (twx, a_pinfop, a_code);
 738           locked = "0"b;
 739           on cleanup call cleaner;
 740           call setup;
 741           if channel.state > HUNGUP
 742           then do;
 743                call channel_manager$control (devx, "hangup", a_pinfop, code);
 744                channel.state = HUNGUP;
 745                call reset_channel;
 746           end;
 747           else code = error_table_$invalid_state;
 748           goto unlock;                                      /* unlock and return */
 749 
 750 read:
 751      entry (twx, a_pinfop, rest, a_bytes_processed, a_code);
 752           read_entry = "1"b;
 753           int_type = NDTIND;
 754           goto g_i;
 755 
 756 get_info:                                                   /* this entrypoint is called by the user before each wakeup */
 757      entry (twx, a_int_type, a_pinfop, rest, a_bytes_processed, a_code);
 758 
 759           int_type = a_int_type;
 760           read_entry = "0"b;
 761 g_i:
 762           pinfop = a_pinfop;
 763           rest, bytes_processed, a_bytes_processed = 0;
 764           locked = "0"b;
 765           on cleanup call cleaner;
 766           call setup;
 767 
 768           if int_type = NCONIND                             /* request for info in an N_CON_IND */
 769           then do;
 770                if channel.state >= DIALING
 771                then do;
 772                     CON_IND_info.our_address = channel.our_network_address;
 773                     CON_IND_info.his_address = channel.his_network_address;
 774                     CON_IND_info.data = channel.call_data;
 775                     CON_IND_info.facilities = channel.facilities;
 776                     CON_IND_info.dial_info = ""b;
 777                     rest = channel.max_nsdu_size;
 778                     code = 0;
 779                end;
 780                else code = error_table_$invalid_state;
 781           end;
 782 
 783           else if int_type = NCONCONF
 784           then rest = channel.max_nsdu_size;
 785 
 786           else if int_type = NDTIND                         /* recuperation of data. */
 787           then do;
 788                if channel.state = DIALED
 789                then do;
 790                     if ^read_entry
 791                     then do;
 792                          tcp = pinfop;
 793                          if tcp = null ()
 794                          then goto bad_args;
 795                          n, i = 0;                          /* calculate available space in the user chain */
 796                          do blockp = tcp repeat (ptr (blockp, buffer.next)) while (rel (blockp) ^= "0"b);
 797                               i = i + 1;                    /* safeguard if the user chain is munged ! */
 798                               if i > 512
 799                               then goto bad_args;
 800                               n = n + max_buffer_tally (buffer.size_code);
 801                          end;
 802                     end;
 803                     else do;
 804                          if pinfop = null ()
 805                          then goto bad_args;
 806                          n = 0;
 807                          do i = 1 to transmit_info.n_entries;
 808                               n = n + transmit_info (i).size;
 809                               if transmit_info (i).data_ptr = null ()
 810                               then goto bad_args;
 811                          end;
 812                          tidx = 1;
 813                     end;
 814                     headp, cscp, blockp = ptr (ttybp, channel.fblock);
 815                     if channel.fblock ^= 0                  /* you can't give what you don't have ! */
 816                     then do;
 817                          call check_length (break, length_to_copy);
 818                          if ^break
 819                          then goto check_rest;              /* if there is not a complete NSDU... */
 820                          if length_to_copy > n              /* if you can't go at all (pass it all ?)... */
 821                          then do;
 822                               length_to_copy = n;
 823                               break = "0"b;
 824                          end;
 825                          sci = 0;
 826                          do length_to_copy = length_to_copy repeat (length_to_copy - n) while (length_to_copy > 0);
 827                               if read_entry
 828                               then do;
 829                                    n = min (length_to_copy, transmit_info (tidx).size);
 830                                    call copy_from_us_to_user (cscp, sci, n, tidx, 0);
 831                                    tidx = tidx + 1;
 832                               end;
 833                               else do;
 834                                    n = min (length_to_copy, max_buffer_tally (tcp -> buffer.size_code));
 835                                    call copy_chars (cscp, sci, n, tcp, 0);
 836                                    string (tcp -> buffer.flags) = "0"b;
 837                                    tcp -> buffer.tally = n;
 838                                    prevp = tcp;             /* remind us of the last buffer used. */
 839                                    tcp = ptr (tcp, tcp -> buffer.next);
 840                               end;
 841                               bytes_processed = bytes_processed + n;
 842                          end;
 843                          if ^read_entry
 844                          then prevp -> buffer.flags.break = break;
 845                          if cscp = null                     /* we've exhausted our chain */
 846                          then do;
 847                               call tty_space_man$free_chain (devx, INPUT, headp);
 848                                                             /* if yes, then free it. */
 849                               channel.fblock, channel.lblock = 0;
 850                          end;
 851                          else do;                           /* if no, detach the used part */
 852                               n = cscp -> buffer.tally - sci;
 853                                                             /* align the rest of the buffer where it stopped */
 854                               prevp = cscp;
 855                               call copy_chars (prevp, sci, n, prevp, 0);
 856                               cscp -> buffer.tally = n;     /* update the tally */
 857                               i = bin (rel (cscp), 18);
 858                               if channel.fblock ^= i        /* if we have buffers to detach */
 859                               then do;                      /* find the last buffer seen */
 860                                    do blockp = headp repeat (ptr (blockp, buffer.next)) while (buffer.next ^= i);
 861                                    end;
 862                                    buffer.next = 0;         /* cut here */
 863                                    channel.fblock = i;      /* render to Caesar... */
 864                                    call tty_space_man$free_chain (devx, INPUT, headp);
 865                               end;
 866                          end;
 867                     end;
 868 check_rest:
 869                     if channel.fblock ^= 0
 870                     then do;
 871                          blockp = ptr (ttybp, channel.fblock);
 872                          call check_length (break, n);      /* what is left to us ? */
 873                          rest = 0;
 874                          channel.flags.rflag = "0"b;
 875                          if break
 876                          then rest = n;
 877                          else channel.flags.rflag = "1"b;
 878                     end;
 879                     else channel.flags.rflag = "1"b;        /* warn the user that ther's something new */
 880                     a_bytes_processed = bytes_processed;
 881                end;
 882                else code = error_table_$invalid_state;
 883           end;
 884 
 885           else code = error_table_$bad_arg;
 886           goto unlock;
 887 %page;
 888 interrupt:
 889      entry (a_data_base_ptr, a_type, a_info);
 890 
 891 /* "interrupt_side" of the protocol_mpx pseudo-multiplexer interfacing the MCS environment with the user                                */
 892 
 893           channel_ptr = a_data_base_ptr;
 894           int_type = a_type;
 895           interrupt_info = a_info;
 896           locked = "0"b;
 897           devx = channel.devx;
 898           ttybp = addr (tty_buf$);
 899           infop = addr (dn355_data$);
 900           protocol_data_ptr = datanet_info.protocol_datap;
 901 
 902           if int_type = DIALUP
 903           then do;                                          /* network connections - N_CON_IND */
 904                unspec (DIALUP_info) = interrupt_info;
 905                if channel.state ^= HUNGUP
 906                then goto bad_int;
 907                blockp = ptr (ttybp, DIALUP_info.info_relp); /* recover the info transmitted by x25_mpx... */
 908                NCON_IND_info = N_I_i;
 909                unspec (dialup_info) = unspec (NCON_IND_info.dial_info);
 910                channel.buffer_pad = dialup_info.buffer_pad;
 911                channel.max_buf_size = dialup_info.max_buf_size;
 912                channel.space_left_in_packet = channel.max_packet_size;
 913                channel.his_network_address = NCON_IND_info.his_address;
 914                channel.call_data = NCON_IND_info.data;
 915                channel.facilities = NCON_IND_info.facilities;
 916                call compute_max_nsdu_size;
 917                call tty_space_man$free_buffer (devx, INPUT, blockp);
 918                                                             /* return the buffer that had contained the info */
 919                process_id = ""b;
 920                event = 0;
 921                if length (channel.call_data) = 0
 922                then protocol_id = layer3_ISO;
 923                else do protocol_id = lbound (protocol_data.listeners, 1) to hbound (protocol_data.listeners, 1)
 924                          while (channel.call_data ^= layer3_call_datas (protocol_id));
 925                end;
 926                ndis_ind_reason.cause = 0;
 927                ndis_ind_reason.diag = DIAG_71;
 928                if protocol_id > hbound (protocol_data.listeners, 1)
 929                then do;                                     /*  not a standard protocol */
 930                     n = hbound (protocol_data.special_listeners, 1);
 931                     stop = "0"b;
 932                     do i = 1 to n while (^stop);
 933                          m = length (protocol_data.special_listeners (i).datas);
 934                          if protocol_data.special_listeners (i).call_datas.partial & length (channel.call_data) >= m
 935                          then if substr (channel.call_data, 1, m) = protocol_data.special_listeners (i).call_datas.datas
 936                               then stop = "1"b;
 937                          if ^protocol_data.special_listeners (i).call_datas.partial
 938                               & channel.call_data = protocol_data.special_listeners (i).call_datas.datas
 939                          then stop = "1"b;
 940                     end;
 941                     if stop
 942                     then do;
 943                          i = i - 1;
 944                          process_id = protocol_data.special_listeners (i).proc_id;
 945                          event = protocol_data.special_listeners (i).event_id;
 946                     end;
 947                end;
 948                else do;
 949                     process_id = protocol_data.listeners (protocol_id).proc_id;
 950                     event = protocol_data.listeners (protocol_id).event_id;
 951                end;
 952                if process_id ^= ""b
 953                then do;
 954                     protocol_msg.ev_devx = devx;
 955                     protocol_msg.ev_type = NCONIND;
 956                     protocol_msg.ev_user_index = 0;
 957                     protocol_msg.infos = ""b;
 958                     call pxss$ring_0_wakeup (process_id, event, protocol_event_message, pxss_status);
 959                     if bad_pxss_status ()
 960                     then call channel_manager$control (devx, "hangup", addr (ndis_ind_reason), ignore_code);
 961                     else do;
 962                          channel.state = DIALING;
 963                          channel.flags.in_use = "1"b;
 964                          channel.listener_proc_id = process_id;
 965                          channel.listener_event = event;
 966                     end;
 967                end;
 968                else call channel_manager$control (devx, "hangup", addr (ndis_ind_reason), ignore_code);
 969           end;
 970           else if int_type = USER_INTERRUPT
 971           then do;                                          /* confirm network connection - N_CON_CONF */
 972                if channel.state = DIALING
 973                then do;
 974                     unspec (dialup_info) = unspec (interrupt_info);
 975                     channel.buffer_pad = dialup_info.buffer_pad;
 976                     channel.max_buf_size = dialup_info.max_buf_size;
 977                     call compute_max_nsdu_size;
 978                     protocol_msg.ev_devx = devx;
 979                     protocol_msg.ev_type = NCONCONF;
 980                     protocol_msg.ev_user_index = channel.user_ref;
 981                     channel.state = DIALED;
 982                     channel.rflag = "1"b;
 983                     call pxss$ring_0_wakeup (channel.user_proc_id, channel.user_event, protocol_event_message,
 984                          pxss_status);
 985                     if pxss_status = WAKEUP_CODE_0 | pxss_status = WAKEUP_CODE_5 | pxss_status = WAKEUP_CODE_100
 986                     then call channel_manager$control (devx, "hangup", null (), ignore_code);
 987                end;
 988                else call syserr (Log_message, "protocol_mpx: bad CON_CONF received in state ^d on devx ^d", channel.state,
 989                          devx);
 990           end;
 991 
 992           else if int_type = HANGUP | int_type = CRASH | int_type = DIAL_STATUS
 993                                                             /* network disconnect - N_DIS_IND */
 994           then do;
 995                if channel.state = HUNGUP
 996                then ;                                       /* if already HUNGUP it's an n_dis_conf */
 997                else do;
 998                     unspec (protocol_msg.infos) = substr (interrupt_info, 37, 18);
 999                     protocol_msg.ev_devx = devx;
1000                     protocol_msg.ev_type = NDISIND;
1001                     protocol_msg.ev_user_index = channel.user_ref;
1002                     channel.state = HUNGUP;
1003                     call pxss$ring_0_wakeup (channel.user_proc_id, channel.user_event, protocol_event_message,
1004                          pxss_status);
1005                     if channel.listener_proc_id ^= ""b
1006                     then do;
1007                          call pxss$ring_0_wakeup (channel.listener_proc_id, channel.listener_event,
1008                               protocol_event_message, pxss_status);
1009                          if bad_pxss_status ()
1010                          then ;                             /* for noting in the log */
1011                     end;
1012                     call reset_channel;
1013                end;
1014                if channel.flags.started & ^channel.flags.timer_set
1015                                                             /* we're not able to recall x25_mpx directly */
1016                then do;
1017                     call mcs_timer$set (devx, 0, clock () + MILLISECONDS_2, ""b);
1018                                                             /* then we recall it later... */
1019                     channel.flags.timer_set = "1"b;
1020                end;
1021           end;
1022 
1023           else if int_type = SEND_OUTPUT & channel.state > HUNGUP
1024                                                             /* x25_mpx round robin */
1025           then do;
1026                channel.flags.send_output = "1"b;
1027                if channel.write_first ^= 0                  /* do we have anything left to send ? */
1028                then call send_next_nsdu;
1029 
1030                if channel.write_first = 0 |                 /* if there's nothing left to send or if send_next_nsdu  */
1031                     channel.flags.send_output               /* nothing to send (no complete NSDU in stock) */
1032                then if channel.wflag                        /* user is waiting to be told when output is done */
1033                     then do;
1034                          protocol_msg.ev_devx = devx;
1035                          protocol_msg.ev_type = NDTRDYIND;
1036                          protocol_msg.ev_user_index = channel.user_ref;
1037                          call pxss$ring_0_wakeup (channel.user_proc_id, channel.user_event, protocol_event_message,
1038                               pxss_status);
1039                          if pxss_status = WAKEUP_CODE_0 | pxss_status = WAKEUP_CODE_5 | pxss_status = WAKEUP_CODE_100
1040                          then call channel_manager$control (devx, "hangup", null (), ignore_code);
1041                          channel.wflag = "0"b;
1042                     end;
1043           end;
1044 
1045           else if int_type = ACCEPT_INPUT                   /* data indication - N_DATA_IND */
1046           then do;
1047                unspec (rtx_info) = interrupt_info;
1048                inchain = bin (rtx_info.chain_head);
1049                if inchain = 0
1050                then return;
1051 
1052                last_offset = bin (rtx_info.chain_tail);     /* initialize end_of_chain pointer */
1053                if channel.fblock = 0
1054                then do;                                     /* no existing blocks */
1055                     channel.fblock = inchain;               /* set offset to first block */
1056                end;
1057                else do;
1058                     old_tailp = ptr (ttybp, channel.lblock);
1059                     next_offset = bin (rtx_info.chain_head);
1060                     if ^old_tailp -> buffer.flags.break
1061                     then do;
1062 
1063                          old_last_tally = old_tailp -> buffer.tally;
1064 
1065                          max_tally = max_buffer_tally (old_tailp -> buffer.size_code);
1066                                                             /* number of characters this buffer will hold */
1067                          filled = "0"b;
1068                          do while ((next_offset ^= 0) & ^filled);
1069                                                             /* put as much as possible of input into last old buffer */
1070                               new_headp = ptr (ttybp, next_offset);
1071                               new_first_tally = new_headp -> buffer.tally;
1072 
1073                               if (old_last_tally + new_first_tally <= max_tally)
1074                               then do;
1075                                    source_ptr = addr (new_headp -> buffer.chars (0));
1076                                    target_ptr = addr (old_tailp -> buffer.chars (old_last_tally));
1077                                    target_ptr -> new_chars = source_ptr -> new_chars;
1078                                    old_last_tally = old_last_tally + new_first_tally;
1079                                    old_tailp -> buffer.flags.break = new_headp -> buffer.flags.break;
1080                                    if new_headp -> buffer.flags.break
1081                                    then filled = "1"b;
1082                                    next_offset = new_headp -> buffer.next;
1083                                                             /* move on to next buffer */
1084                                    call tty_space_man$free_buffer (devx, INPUT, new_headp);
1085                                                             /* through with this one */
1086                               end;
1087 
1088                               else filled = "1"b;           /* no more room in last old buffer */
1089                          end;
1090 
1091                          old_tailp -> buffer.tally = old_last_tally;
1092                     end;
1093                     old_tailp -> buffer.next = next_offset;
1094                     if next_offset = 0
1095                     then last_offset = 0;                   /* took care of entire new chain */
1096                end;
1097 
1098                if last_offset ^= 0
1099                then channel.lblock = last_offset;
1100                if rtx_info.break_char & channel.flags.rflag
1101                then do;
1102                     protocol_msg.ev_devx = devx;
1103                     protocol_msg.ev_type = NDTIND;
1104                     protocol_msg.ev_user_index = channel.user_ref;
1105                     call pxss$ring_0_wakeup (channel.user_proc_id, channel.user_event, protocol_event_message,
1106                          pxss_status);                      /* wakeup the user */
1107                     if pxss_status = WAKEUP_CODE_0 | pxss_status = WAKEUP_CODE_5 | pxss_status = WAKEUP_CODE_100
1108                     then call channel_manager$control (devx, "hangup", null (), ignore_code);
1109                     channel.rflag = "0"b;                   /* we've taken care of this now */
1110                end;
1111           end;
1112 
1113           else if int_type = LINE_STATUS                    /* N_RESET_IND */
1114           then do;
1115                if channel.user_proc_id ^= "0"b
1116                then do;
1117                     protocol_msg.ev_devx = devx;
1118                     protocol_msg.ev_type = NRESETIND;
1119                     protocol_msg.ev_user_index = channel.user_ref;
1120                     call pxss$ring_0_wakeup (channel.user_proc_id, channel.user_event, protocol_event_message,
1121                          pxss_status);
1122                     if pxss_status = WAKEUP_CODE_0 | pxss_status = WAKEUP_CODE_5 | pxss_status = WAKEUP_CODE_100
1123                     then call channel_manager$control (devx, "hangup", null (), ignore_code);
1124                end;
1125 
1126                return;
1127 
1128           end;
1129 
1130           else if int_type = TIMER
1131           then do;
1132                channel.flags.timer_set = "0"b;
1133                if channel.flags.started & channel.state = HUNGUP
1134                then call channel_manager$control (devx, "listen", null (), ignore_code);
1135           end;
1136 
1137           else if int_type = SPACE_AVAILABLE                /* we were waiting for space */
1138           then do;
1139                if channel.write_first ^= 0                  /* we've got more output */
1140                then call send_next_nsdu;
1141                if channel.write_first = 0 | channel.send_output
1142                then do;
1143                     protocol_msg.ev_devx = devx;
1144                     protocol_msg.ev_type = NDTRDYIND;
1145                     protocol_msg.ev_user_index = channel.user_ref;
1146                     call pxss$unique_ring_0_wakeup (channel.user_proc_id, channel.user_event, protocol_event_message,
1147                          pxss_status);
1148                     if pxss_status = WAKEUP_CODE_0 | pxss_status = WAKEUP_CODE_5 | pxss_status = WAKEUP_CODE_100
1149                     then call channel_manager$control (devx, "hangup", null (), ignore_code);
1150                end;
1151           end;
1152 
1153           else
1154 bad_int:
1155                call syserr (Log_message, "protocol_mpx: unexpected interrupt type (^d) for devx ^d in state ^d", int_type,
1156                     devx, channel.state);
1157 
1158           return;
1159 %page;
1160 setup:
1161      proc;
1162 
1163           devx = twx;                                       /* pull devx from user */
1164           ttybp = addr (tty_buf$);
1165           lctp = tty_buf.lct_ptr;
1166           lcntp = lct.lcnt_ptr;
1167           infop = addr (dn355_data$);
1168           protocol_data_ptr = datanet_info.protocol_datap;
1169           if devx < 1 | devx > lct.max_no_lctes
1170           then do;
1171                code = error_table_$invalid_device;
1172                goto unlock;                                 /* return error */
1173           end;
1174 
1175           call tty_lock$lock_channel (devx, code);          /* lock the channel */
1176           if code ^= 0
1177           then goto unlock;
1178           locked = "1"b;
1179 
1180           lctep = addr (lct.lcte_array (devx));
1181           if lcte.channel_type ^= PROTOCOL_MPX              /* not our cup of tea */
1182           then goto illdet;
1183 
1184           channel_ptr = lcte.data_base_ptr;                 /* pointer to perm info */
1185           if channel.user_proc_id ^= ""b & channel.listener_proc_id ^= pds$processid
1186                & channel.user_proc_id ^= pds$processid
1187           then goto illdet;
1188           return;
1189      end setup;
1190 
1191 illdet:                                                     /* illegal messing with channel */
1192           code = error_table_$io_no_permission;
1193 unlock:
1194           a_code = code;
1195           call cleaner;
1196           return;
1197 %page;
1198 get_channel:
1199      proc (addr) returns (fixed bin);
1200 
1201 dcl  addr                   char (15) varying,
1202      i                      fixed bin;
1203 
1204           do i = protocol_data.max_channels to 1 by -1
1205                while (protocol_data.channels (i).flags.in_use | ^protocol_data.channels (i).flags.started
1206                | (protocol_data.channels (i).our_network_address ^= addr & addr ^= ""));
1207           end;
1208           return (i);
1209      end get_channel;
1210 
1211 
1212 check_length:
1213      proc (break_flag, length);
1214 
1215 /* resend the length of the chain pointed to by blockp just at next
1216    break or just at the end of the chain if there is no break */
1217 
1218 dcl  break_flag             bit (1);
1219 dcl  length                 fixed bin;
1220 dcl  stop                   bit (1);
1221 
1222           length = 0;
1223           stop, break_flag = "0"b;
1224           do blockp = blockp repeat (ptr (blockp, buffer.next)) while (^stop);
1225                if buffer.break
1226                then break_flag = "1"b;
1227                if buffer.next = 0 | break_flag
1228                then stop = "1"b;
1229                length = length + buffer.tally;
1230           end;
1231      end check_length;
1232 
1233 
1234 copy_chars:
1235      proc (source_ptr, source_offset, n_chars_to_copy, target_ptr, a_target_offset);
1236 
1237 /*
1238    Procedure  copying  n_chars_to_copy characters from the chain pointed to
1239    by source_ptr to the offset  source_offset in the buffer pointed to by
1240    target_ptr to the offset  target_offset. No overflow test is made. On
1241    return, source_ptr and source_offset indicate the next character in the
1242    chain or  source_ptr  =  null () if  the chain is exhausted.
1243    target_offset is unchanged.
1244 */
1245 
1246 dcl  (source_ptr, target_ptr)
1247                             ptr;
1248 dcl  (a_target_offset, n_chars_to_copy)
1249                             fixed bin;
1250 dcl  source_offset          fixed bin (21);
1251 dcl  (n, nctc, target_offset)
1252                             fixed bin;
1253 dcl  n_chars_in_source_buffer
1254                             fixed bin;
1255 dcl  source_chars           char (n) based (addr (source_ptr -> buffer.chars (source_offset)));
1256 dcl  target_chars           char (n) based (addr (target_ptr -> buffer.chars (target_offset)));
1257 
1258           target_offset = a_target_offset;
1259 
1260           do nctc = n_chars_to_copy repeat (nctc - n) while (nctc > 0);
1261                n_chars_in_source_buffer = source_ptr -> buffer.tally - source_offset;
1262                if n_chars_in_source_buffer > nctc
1263                then do;
1264                     n = nctc;
1265                     target_chars = source_chars;
1266                     source_offset = source_offset + n;
1267                end;
1268                else if n_chars_in_source_buffer < nctc
1269                then do;
1270                     n = n_chars_in_source_buffer;
1271                     target_chars = source_chars;
1272                     if source_ptr -> buffer.next = 0
1273                     then source_ptr = null;
1274                     else source_ptr = ptr (source_ptr, source_ptr -> buffer.next);
1275                     source_offset = 0;
1276                end;
1277                else do;
1278                     n = nctc;
1279                     target_chars = source_chars;
1280                     if source_ptr -> buffer.next = 0
1281                     then source_ptr = null;
1282                     else source_ptr = ptr (source_ptr, source_ptr -> buffer.next);
1283                     source_offset = 0;
1284                end;
1285                target_offset = target_offset + n;
1286           end;
1287      end copy_chars;
1288 
1289 
1290 
1291 reset_channel:
1292      proc;
1293 
1294 
1295           if channel.fblock ^= 0                            /* free read chain */
1296           then call tty_space_man$free_chain (devx, INPUT, ptr (ttybp, channel.fblock));
1297 
1298           if channel.write_first ^= 0                       /* free send chain */
1299           then call tty_space_man$free_chain (devx, OUTPUT, ptr (ttybp, channel.write_first));
1300 
1301           channel.flags.in_use, channel.flags.send_output, channel.flags.wflag, channel.flags.rflag = "0"b;
1302 
1303           channel.fblock, channel.lblock = 0;
1304           channel.write_first, channel.write_last = 0;
1305           channel.space_left_in_packet = channel.max_packet_size;
1306           channel.listener_event = 0;
1307           channel.listener_proc_id = ""b;
1308           channel.user_event = 0;
1309           channel.user_proc_id = ""b;
1310           channel.user_ref = 0;
1311           channel.his_network_address = "";
1312           channel.call_data = "";
1313           channel.facilities = "";
1314           channel.max_buf_size = 0;
1315           channel.buffer_pad = 0;
1316           channel.max_nsdu_size = 0;
1317 
1318      end reset_channel;
1319 
1320 %page;
1321 
1322 send_next_nsdu:
1323      proc;
1324 
1325 dcl  headp                  ptr;
1326 dcl  next_head              fixed bin;
1327 
1328           if channel.flags.send_output
1329           then do;
1330                headp, blockp = ptr (ttybp, channel.write_first);
1331 
1332                do while (buffer.next ^= 0 & ^buffer.flags.break);
1333                                                             /* find the end of the chain or limit of NSDU */
1334                     blockp = ptr (ttybp, buffer.next);
1335                end;
1336                if ^buffer.flags.break
1337                then return;                                 /* if no complete NSDU then return  */
1338                next_head = buffer.next;
1339                buffer.next = 0;                             /* break chain here */
1340                call channel_manager$write (devx, headp, code);
1341                if code = noalloc_code
1342                then do;
1343                     call tty_space_man$needs_space (devx);
1344                     code = 0;                               /* don't treat like other error codes */
1345                end;
1346 
1347 
1348                if headp ^= null                             /* didn't take it all */
1349                then do;
1350                     if code = 0
1351                     then do;
1352                          blockp = headp;
1353                          do while (buffer.next ^= 0);
1354                               blockp = ptr (ttybp, buffer.next);
1355                          end;
1356 
1357                          buffer.next = next_head;           /* found the end of the returned chain, reconnect it */
1358                          if next_head = 0                   /* if we weren't hanging on to one */
1359                          then channel.write_last = bin (rel (blockp));
1360                                                             /* this is the end */
1361                          next_head = bin (rel (headp));     /* this is now head of the chain */
1362                     end;
1363 
1364                     else do;
1365                          call tty_space_man$free_chain (devx, OUTPUT, headp);
1366                                                             /* all output to be discarded */
1367                          protocol_msg.ev_devx = devx;
1368                          protocol_msg.ev_type = NRESETIND;  /* treat this like an n_reset_ind */
1369                          call pxss$unique_ring_0_wakeup (channel.user_proc_id, channel.user_event, protocol_event_message,
1370                               (0));
1371                     end;
1372                end;
1373 
1374                channel.write_first = next_head;
1375                if channel.write_first = 0
1376                then channel.write_last = 0;                 /* this must be true */
1377                else if code ^= 0                            /* in this case we'll throw away all output anyway */
1378                then do;
1379                     call tty_space_man$free_chain (devx, OUTPUT, ptr (ttybp, channel.write_first));
1380                     channel.write_first, channel.write_last = 0;
1381                end;
1382                channel.flags.send_output = "0"b;
1383           end;
1384 
1385           return;
1386      end send_next_nsdu;
1387 
1388 set_static:
1389      proc;
1390 
1391 /* entry called once per bootload to copy error codes into internal static */
1392 
1393           noalloc_code = error_table_$noalloc;
1394           no_write_code = error_table_$invalid_write;
1395           call wire_proc$wire_me;
1396      end set_static;
1397 
1398 
1399 cleaner:
1400      proc;
1401 
1402           if locked
1403           then do;
1404                call tty_lock$unlock_channel (devx);
1405                locked = "0"b;
1406           end;
1407 
1408      end cleaner;
1409 
1410 
1411 bad_pxss_status:
1412      proc returns (bit (1));
1413 
1414           if pxss_status = WAKEUP_CODE_0 | pxss_status = WAKEUP_CODE_5 | pxss_status = WAKEUP_CODE_100
1415           then do;
1416                call syserr (Log_message,
1417                     "protocol_mpx: Error ^d waking up listening process for channel ^a, it has probably crashed!",
1418                     pxss_status, channel.name);
1419                return ("1"b);
1420           end;
1421           else return ("0"b);
1422      end bad_pxss_status;
1423 
1424 
1425 /**** procedure to examine the state of current connections:
1426       if the user process of a connection has departed this
1427       one receives hangup */
1428 
1429 check_orphan_connections:
1430      proc;
1431 
1432           do i = 1 to protocol_data.max_channels;
1433                channel_ptr = addr (protocol_data.channels (i));
1434                if channel.state > DIALING
1435                then do;
1436                     call tc_util$validate_processid (channel.user_proc_id, code);
1437                     if code ^= 0
1438                     then do;
1439                          call syserr (Log_message, "protocol_mpx: orphan channel found, ^a. Hanging it up...",
1440                               channel.name);
1441                          ndis_ind_reason.cause = 0;
1442                          ndis_ind_reason.diag = DIAG_71;
1443                          call tty_lock$lock_channel (channel.devx, code);
1444                          if code = 0
1445                          then do;
1446                               call channel_manager$control (channel.devx, "hangup", addr (ndis_ind_reason), code);
1447                               call tty_lock$unlock_channel (channel.devx);
1448                          end;
1449                     end;
1450                end;
1451           end;
1452      end check_orphan_connections;
1453 
1454 
1455 copy_from_user_to_us:
1456      proc (source_index, source_offset, n_chars_to_copy, target_ptr, a_target_offset);
1457 
1458 /*
1459    Procedure copies n_chars_to_copy  characters from the chain
1460    pointed to by source_index to the offset source_offset in the buffer (MCS
1461    type) pointed to by target_ptr to the offset target_offset. No overflow test
1462    is made. On return, source_index and source_offset indicate the next
1463    character in the source chain or source_index = 0 if the chain is exhausted.
1464    target_offset is unchanged.
1465 */
1466 
1467 dcl  (target_ptr, source_chars_p)
1468                             ptr;
1469 dcl  source_offset          fixed bin (21);
1470 dcl  (source_index, a_target_offset, n_chars_to_copy)
1471                             fixed bin;
1472 dcl  (n, nctc, target_offset)
1473                             fixed bin;
1474 dcl  n_chars_in_source_buffer
1475                             fixed bin (21);
1476 dcl  total_string           char (transmit_info (source_index).size) based (transmit_info (source_index).data_ptr);
1477 dcl  source_chars           char (n) based (source_chars_p);
1478 dcl  target_chars           char (n) based (addr (target_ptr -> buffer.chars (target_offset)));
1479 
1480           target_offset = a_target_offset;
1481 
1482           do nctc = n_chars_to_copy repeat (nctc - n) while (nctc > 0);
1483                source_chars_p = addr (substr (total_string, source_offset + 1, 1));
1484                n_chars_in_source_buffer = transmit_info (source_index).size - source_offset;
1485                if n_chars_in_source_buffer > nctc
1486                then do;
1487                     n = nctc;
1488                     target_chars = source_chars;
1489                     source_offset = source_offset + n;
1490                end;
1491                else if n_chars_in_source_buffer < nctc
1492                then do;
1493                     n = n_chars_in_source_buffer;
1494                     target_chars = source_chars;
1495                     if source_index = transmit_info.n_entries
1496                     then source_index = 0;
1497                     else source_index = source_index + 1;
1498                     source_offset = 0;
1499                end;
1500                else do;
1501                     n = nctc;
1502                     target_chars = source_chars;
1503                     if source_index = transmit_info.n_entries
1504                     then source_index = 0;
1505                     else source_index = source_index + 1;
1506                     source_offset = 0;
1507                end;
1508                target_offset = target_offset + n;
1509           end;
1510      end copy_from_user_to_us;
1511 
1512 
1513 copy_from_us_to_user:
1514      proc (source_ptr, source_offset, n_chars_to_copy, target_index, a_target_offset);
1515 
1516 /*
1517    Procedure copies n_chars_to_copy  characters from the chain (MCS type)
1518    pointed to by source_ptr to the offset  source_offset in the buffer pointed to
1519    by target_index to the offset target_offset. No overflow test is made. On
1520    return source_ptr and source_offset indicate the next character in the source
1521    chain or source_ptr = null () if the chain is exhausted. target_offset is
1522    unchanged.
1523 
1524 */
1525 
1526 dcl  (source_ptr, user_chars_p)
1527                             ptr;
1528 dcl  (source_offset, a_target_offset, target_offset)
1529                             fixed bin (21);
1530 dcl  (target_index, n_chars_to_copy)
1531                             fixed bin;
1532 dcl  (n, nctc)              fixed bin;
1533 dcl  n_chars_in_source_buffer
1534                             fixed bin;
1535 dcl  source_chars           char (n) based (addr (source_ptr -> buffer.chars (source_offset)));
1536 dcl  total_string           char (transmit_info (target_index).size) based (transmit_info (target_index).data_ptr);
1537 dcl  target_chars           char (n) based (user_chars_p);
1538 
1539           target_offset = a_target_offset;
1540 
1541           do nctc = n_chars_to_copy repeat (nctc - n) while (nctc > 0);
1542                user_chars_p = addr (substr (total_string, target_offset + 1, 1));
1543                n_chars_in_source_buffer = source_ptr -> buffer.tally - source_offset;
1544                if n_chars_in_source_buffer > nctc
1545                then do;
1546                     n = nctc;
1547                     target_chars = source_chars;
1548                     source_offset = source_offset + n;
1549                end;
1550                else if n_chars_in_source_buffer < nctc
1551                then do;
1552                     n = n_chars_in_source_buffer;
1553                     target_chars = source_chars;
1554                     if source_ptr -> buffer.next = 0
1555                     then source_ptr = null ();
1556                     else source_ptr = ptr (source_ptr, source_ptr -> buffer.next);
1557                     source_offset = 0;
1558                end;
1559                else do;
1560                     n = nctc;
1561                     target_chars = source_chars;
1562                     if source_ptr -> buffer.next = 0
1563                     then source_ptr = null ();
1564                     else source_ptr = ptr (source_ptr, source_ptr -> buffer.next);
1565                     source_offset = 0;
1566                end;
1567                target_offset = target_offset + n;
1568           end;
1569      end copy_from_us_to_user;
1570 
1571 compute_max_nsdu_size:
1572      proc;
1573 
1574 dcl  i                      fixed bin;
1575 
1576           max_chars_in_buf = SIZE4 * (channel.max_buf_size - 1) - channel.buffer_pad;
1577           i = ceil (channel.max_packet_size / max_chars_in_buf);
1578                                                             /* number of buffers/x25 packet */
1579           channel.max_nsdu_size = min (LONGEST_POSSIBLE_STRING, (max_chain_len / i) * channel.max_packet_size);
1580      end compute_max_nsdu_size;
1581 
1582 %page;
1583 %include protocol_data;
1584 %page;
1585 %include protocol_infos;
1586 %page;
1587 %include protocols;
1588 %page;
1589 %include multiplexer_types;
1590 %page;
1591 %include tty_buf;
1592 %page;
1593 %include tty_buffer_block;
1594 %page;
1595 %include tty_space_man_dcls;
1596 %page;
1597 %include lct;
1598 %page;
1599 %include channel_manager_dcls;
1600 %page;
1601 %include mcs_interrupt_info;
1602 %page;
1603 %include line_types;
1604 %page;
1605 %include syserr_codes;
1606 %page;
1607 %include response_transitions;
1608 %page;
1609 %include mcs_timer_dcls;
1610 %page;
1611 %include dn355_data;
1612 
1613      end protocol_mpx;