1 /****^  ***********************************************************
   2         *                                                         *
   3         * Copyright, (C) Honeywell Bull Inc., 1987                *
   4         *                                                         *
   5         * Copyright, (C) Honeywell Information Systems Inc., 1982 *
   6         *                                                         *
   7         * Copyright (c) 1972 by Massachusetts Institute of        *
   8         * Technology and Honeywell Information Systems, Inc.      *
   9         *                                                         *
  10         *********************************************************** */
  11 
  12 /* format: style4,delnl,insnl,^ifthendo */
  13 fnp_multiplexer:
  14      proc;
  15 
  16 /* This is the called multiplexer module for FNP channels. It calls dn355
  17    *  to pass mailboxes on to the FNP. Important data structures are:
  18    *      fnp_info (in dn355_data) : info about the FNP as a whole
  19    *      pcb (physical channel block) : allocated in tty_buf. Contains per-channel info
  20    *
  21 */
  22 
  23 /* Written 08/15/78 by Robert Coren */
  24 /* Modified 04/11/79 by Robert Coren to handle all modes at once */
  25 /* Modified 06/29/79 by Bernard Greenberg for FNP echo negotiation */
  26 /* Modified 79 Aug 21 by Art Beattie to support 64K DN6670s. */
  27 /* Modified various times in 1980 by Robert Coren to add metering */
  28 /* Modified May 1981 by Robert Coren to keep get_meters order from using user-supplied
  29    pointer while FNP channel is locked */
  30 /* Modified late summer 1981 by Robert Coren to handle tandd_attach order and COLTS channel */
  31 /* Modified fall 1981 by Robert Coren to assign smaller buffer sizes */
  32 /* Modified November 1981 by Robert Coren to fix bug whereby terminate_multiplexer
  33    didn't initialize ttybp */
  34 /* Modified June 1982 by Robert Coren to correct precision of FNP addresses. */
  35 /* Modified November 1983 by Robert Coren to make priv_control check the pointer set by get_fnp_meters */
  36 /* Modified 83-12-16 BIM for io_manager calls */
  37 /* Modified 84-05-18 BIM for correction to ioa_ strings */
  38 /* Modified 1984-08-02 BIM for bug in shutdown_mpx handling of booting fnp */
  39 /* Modified 1984-09-25 BIM for paged mode fnp load. (page table filling) */
  40 /* Modified September 1984 by Robert Coren to free copied meters in
  41    terminate_multiplexer and to exit loop in send_global if FNP is down */
  42 /* Modified November 1984 by Robert Coren to zero pad fields in copied meters
  43    so random junk from the FNP doesn't get left in tty_area */
  44 /* Modified November 1984 by Robert Coren to use tty_area_manager to allocate and free saved meters */
  45 
  46 /****^  HISTORY COMMENTS:
  47   1) change(86-04-23,Coren), approve(86-04-23,MCR7300),
  48      audit(86-06-19,Beattie), install(86-07-08,MR12.0-1089):
  49      To process 256-bit echo negotiation break tables.
  50   2) change(86-12-12,Beattie), approve(86-12-17,MECR0005),
  51      audit(86-12-12,Brunelle), install(86-12-17,MR12.0-1250):
  52      Declare an argument in call to fnp_util$free_page_table to accept the
  53      error code being returned. (phx20712)
  54   3) change(86-12-12,Beattie), approve(86-12-29,MCR7598),
  55      audit(87-01-07,Brunelle), install(87-01-12,MR12.0-1268):
  56      Declare an argument in call to fnp_util$free_page_table to accept the
  57      error code being returned. (phx20712)
  58                                                    END HISTORY COMMENTS */
  59 
  60 %page;
  61 /* PARAMETERS */
  62 
  63 dcl  a_devx fixed bin;                                      /* devx of FNP channel */
  64 dcl  a_init_info_ptr ptr;
  65 dcl  a_fnpp ptr;
  66 dcl  a_subchan fixed bin;
  67 dcl  a_chainp ptr;
  68 dcl  a_mi_flag bit (1) aligned;
  69 dcl  a_code fixed bin (35);
  70 dcl  a_output_ptr ptr;
  71 dcl  a_order char (*);
  72 dcl  a_data_ptr ptr;
  73 dcl  a_mode_list_ptr ptr;
  74 dcl  a_modes char (*);
  75 
  76 
  77 /* AUTOMATIC */
  78 
  79 dcl  code fixed bin (35);                                   /* standard system error code */
  80 dcl  devx fixed bin;                                        /* of FNP channel */
  81 dcl  my_chan_name char (1);
  82 dcl  dno fixed bin;                                         /* FNP number */
  83 dcl  pcb_space fixed bin;
  84 dcl  space_needed fixed bin;
  85 dcl  output_ptr ptr;                                        /* pointer to caller's output data */
  86 dcl  chanx fixed bin;                                       /* index of PCB */
  87 dcl  output_length fixed bin;                               /* number of output characters */
  88 dcl  sourcep ptr;
  89 dcl  (i, j) fixed bin;
  90 dcl  lastp ptr;                                             /* pointer to last buffer in already-exisitng chain */
  91 
  92 dcl  order char (32);
  93 dcl  data_ptr ptr;                                          /* pointer to order info structure */
  94 dcl  set_write_status bit (1);
  95 dcl  locked bit (1);
  96 dcl  old_mask fixed bin (71);
  97 dcl  mask_ptwp ptr;
  98 dcl  queue_locked bit (1);
  99 dcl  mylock bit (1);
 100 dcl  opcode fixed bin (8);                                  /* mailbox opcode */
 101 dcl  alter_type fixed bin (8);                              /* alter parameters subop */
 102 dcl  check bit (1);
 103 dcl  mbx_data_len fixed bin;                                /* in bits */
 104 dcl  mbx_data bit (4 * 36) based (addr (mbx_data_long));
 105 dcl  mbx_data_long bit (8 * 36);
 106 dcl  alter_data bit (4 * 36) varying;
 107 dcl  dumpin bit (1);
 108 dcl  dumpout bit (1);
 109 dcl  get_meters bit (1);
 110 dcl  temp_saved_meters_ptr ptr;
 111 dcl  meter_ptr ptr;
 112 dcl  lcmp ptr;
 113 dcl  fnp_meters_ptr ptr;
 114 dcl  ret_meters_ptr ptr;
 115 dcl  local_line_type fixed bin;
 116 dcl  phone_no_len fixed bin;                                /* in bits */
 117 dcl  phone_digits (32) bit (6);
 118 dcl  next_digit fixed bin (6) unsigned;                     /* value of next dialout digit */
 119 dcl  digit_pos fixed bin;                                   /* how far along we are in phone number */
 120 dcl  opend bit (1);                                         /* whether or not output is pending */
 121 
 122 dcl  modex fixed bin;
 123 dcl  mode_name char (8);
 124 dcl  mode_on bit (1);                                       /* mode to be turned on or off */
 125 dcl  mode_set (36) bit (1);
 126 dcl  hndlquit_set bit (1);
 127 dcl  base_len fixed bin;
 128 dcl  block_len fixed bin;
 129 dcl  chars_per_buf fixed bin;
 130 dcl  chars_per_sec fixed bin;
 131 
 132 dcl  wire_arg fixed bin (71);
 133 dcl  wire_ptr ptr;
 134 dcl  hsla_flag bit (1);
 135 dcl  old_flag bit (1);
 136 dcl  pcb_space_ptr ptr;
 137 dcl  prev_la_no fixed bin;
 138 dcl  la_no fixed bin;
 139 dcl  subchan fixed bin;
 140 dcl  his_fnp_no fixed bin;                                  /* FNP number in supplied channel name */
 141 dcl  pcbx fixed bin;
 142 dcl  found bit (1);
 143 dcl  past bit (1);
 144 dcl  n_fnp_words fixed bin;
 145 dcl  ignore bit (1);
 146 dcl  name char (32);
 147 dcl  temp_addr fixed bin;
 148 dcl  fnp_dump_ptr ptr;
 149 dcl  dump_patch_space fixed bin;                            /* amount of space required by an fnp_(dump patch) order */
 150 dcl  dump_patch_time fixed bin (71);                        /* clock time when a dump or patch order was initiated */
 151 
 152 
 153 dcl  1 dump_fnp_data aligned,                               /* command data for dump_fnp & patch_fnp */
 154        2 abs_addr fixed bin (24),                           /* absolute address of ring-zero buffer */
 155        2 fnp_addr fixed bin (18) unsigned unaligned,        /* address in FNP */
 156        2 fnp_len fixed bin (18) unsigned unaligned;         /* number of 18-bit words */
 157 
 158 dcl  1 fnp_break_data aligned,                              /* command data for fnp_break order */
 159        2 lineno fixed bin (17) unal,                        /* line number, derived from tty name */
 160        2 fnp_addr fixed bin (18) unsigned unal,
 161        2 action fixed bin (17) unal,
 162        2 flags bit (18) unal;
 163 
 164 dcl  1 echnego_break_table aligned,
 165        2 words (0:15) unaligned,
 166          3 bits bit (16) unaligned,
 167          3 pad bit (2) unaligned;
 168 
 169 /* BASED */
 170 
 171 dcl  based_fb_word fixed bin based;
 172 dcl  based_bit1 bit (1) based;
 173 dcl  based_bit2 bit (2) based;
 174 dcl  based_bit18 bit (18) based;
 175 dcl  based_bit72 bit (72) based;
 176 dcl  based_bit108 bit (108) based;
 177 dcl  based_echo_table_bits bit (WIRED_ECHO_BREAK_SIZE + 1) based;
 178 dcl  fnp_data (n_fnp_words) bit (18) based;
 179 
 180 dcl  phone_chars char (32) varying based;                   /* phone number passed with dial_out order */
 181 
 182 dcl  1 wr_stat aligned based,                               /* for write_status */
 183        2 ev_chan fixed bin (71),
 184        2 output_pending bit (1);
 185 
 186 dcl  1 rd_stat aligned based,                               /* for read_status */
 187        2 ev_chan fixed bin (71),
 188        2 input_available bit (1);
 189 
 190 dcl  1 dump_fnp_info based (data_ptr) aligned,              /* structure passed for dump_fnp and patch_fnp */
 191        2 fnp_address fixed bin (24),
 192        2 fnp_len fixed bin,                                 /* number of 18=bit words */
 193        2 bufp ptr,                                          /* pointer to caller's buffer */
 194        2 old_value_ptr ptr;                                 /* pointer to previous values (patch only) */
 195 
 196 dcl  1 fnp_break_info aligned based (data_ptr),             /* structure passed on fnp_break order */
 197        2 chan_name char (6),                                /* tty name, optional */
 198        2 fnp_addr fixed bin,                                /* addr in fnp to set break */
 199        2 action fixed bin,                                  /* request type */
 200        2 flags bit (36);                                    /* special action flags */
 201 
 202 dcl  1 echo_start_data aligned based (data_ptr),            /* Echo starting data */
 203        2 ctr fixed bin (35),                                /* Synchronization counter */
 204        2 screenleft fixed bin (35);                         /* Length left on screen */
 205 
 206 /* BUILTINS & CONDITIONS */
 207 
 208 dcl  (addr, addrel, bin, bit, clock, divide, hbound, length, null, ptr, rel, rtrim, size, stac, stacq, string, substr,
 209      unspec) builtin;
 210 
 211 dcl  area condition;
 212 
 213 
 214 /* ENTRIES */
 215 
 216 dcl  pxss$notify entry (fixed bin);
 217 dcl  dn355$send_wcd entry (ptr, ptr, fixed bin (8), fixed bin, bit (*));
 218 dcl  dn355$send_global_wcd entry (ptr, fixed bin (8), fixed bin, bit (*));
 219 dcl  dn355$hangup_fnp_lines entry (fixed bin);
 220 dcl  dn355$process_interrupt_queue entry (fixed bin);
 221 dcl  dn355$interrupt entry;
 222 dcl  fnp_util$fill_page_table entry (fixed bin, fixed bin (35));
 223 dcl  fnp_util$free_page_table entry (fixed bin, fixed bin (35));
 224 dcl  fnp_util$unwire entry (fixed bin, fixed bin (35));
 225 dcl  tty_lock$lock_lcte entry (ptr, fixed bin (35));
 226 dcl  tty_area_manager$allocate entry (fixed bin, ptr);
 227 dcl  tty_area_manager$free entry (fixed bin, ptr);
 228 dcl  lock$lock_fast entry (pointer);
 229 dcl  lock$unlock_fast entry (pointer);
 230 dcl  syserr entry options (variable);
 231 dcl  syserr$error_code entry options (variable);
 232 dcl  parse_tty_name_ entry (char (*), fixed bin, bit (1), fixed bin, fixed bin);
 233 dcl  parse_fnp_name_ entry (char (*), fixed bin);
 234 dcl  pxss$addevent entry (fixed bin);
 235 dcl  pxss$delevent entry (fixed bin);
 236 dcl  pxss$wait entry;
 237 
 238 /* EXTERNAL STATIC */
 239 
 240 dcl  (
 241      error_table_$noalloc,
 242      error_table_$undefined_order_request,
 243      error_table_$bad_mode,
 244      error_table_$bad_channel,
 245      error_table_$buffer_big,
 246      error_table_$invalid_write,
 247      error_table_$dev_offset_out_of_bounds,
 248      error_table_$seglock,
 249      error_table_$mpx_down,
 250      error_table_$timeout,
 251      error_table_$unimplemented_version,
 252      error_table_$no_channel_meters,
 253      error_table_$resource_not_free,
 254      error_table_$io_assigned,
 255      error_table_$io_not_assigned,
 256      error_table_$io_not_configured,
 257      error_table_$io_not_available,
 258      error_table_$invalid_state
 259      ) ext static fixed bin (35);
 260 
 261 dcl  pds$processid ext static bit (36) aligned;
 262 dcl  pds$process_group_id ext static char (32) aligned;
 263 
 264 /* INTERNAL STATIC */
 265 
 266 /* The following are declared here because syserr_constants.incl.pl1 cannot
 267    be used, owing to a naming conflict with mcs_interrupt_info.incl.pl1.
 268 */
 269 
 270 dcl  ANNOUNCE fixed bin internal static options (constant) init (0);
 271 dcl  CRASH_SYSTEM fixed bin internal static options (constant) init (1);
 272 
 273 dcl  DCW_LIST_SIZE fixed bin int static options (constant) init (16);
 274 dcl  DUMP_PATCH_LIMIT fixed bin (35) int static options (constant) init (10000000);
 275                                                             /* i.e., 10 seconds */
 276 
 277 /* The following facts about the the lists of modes below are IMPORTANT.
 278    *  The modes which have corresponding alter_parameters subtypes are the same as the modes
 279    *  that are valid for asynchronous lines only, and no data is associated with the
 280    *  alter_parameters other than on/off, with the following exceptions:
 281    *      blk_xfer and iflow require additional data (buffer sizes)
 282    *      hndlquit is valid for any line, but is expressed by alter_parameters
 283    *
 284    *  Therefore, hndlquit is handled explicitly, and blk_xfer and iflow must come after those modes having alter_paramters
 285    *  subop types. Anyone modifying these lists should be aware of this circumstance.
 286 */
 287 
 288 dcl  good_modes (1) char (8) int static options (constant)  /* modes recognized for all lines */
 289           init ("hndlquit");
 290 
 291 dcl  async_only_modes (15) char (8) int static options (constant)
 292                                                             /* modes recognized for async lines only */
 293           init ("crecho", "tabecho", "lfecho", "echoplex", "fulldpx", "replay", "polite", "breakall", "prefixnl",
 294           "no_outp", "8bit", "oddp", "oflow", "iflow", "blk_xfer");
 295 
 296 dcl  IFLOW_INDEX fixed bin internal static options (constant) init (14);
 297 dcl  BLK_XFER_INDEX fixed bin internal static options (constant) init (15);
 298 
 299 dcl  full_dpx_modes (7) char (8) int static options (constant)
 300                                                             /* modes requiring full duplex line type */
 301           init ("crecho", "tabecho", "lfecho", "echoplex", "fulldpx", "iflow", "oflow");
 302 
 303 dcl  mode_alter_types (13) fixed bin (8) int static options (constant)
 304                                                             /* alter_paramters subops corresponding to modes */
 305           init (8,                                          /* crecho */
 306           14,                                               /* tabecho */
 307           9,                                                /* lfecho */
 308           20,                                               /* echoplex */
 309           3,                                                /* fulldpx */
 310           23,                                               /* replay */
 311           24,                                               /* polite */
 312           27,                                               /* breakall */
 313           28,                                               /* prefixnl */
 314           33,                                               /* no_outp */
 315           32,                                               /* 8bit */
 316           31,                                               /* oddp */
 317           30);                                              /* oflow */
 318 ^L
 319 /* INCLUDE FILES */
 320 
 321 %page;
 322 %include tty_buf;
 323 %page;
 324 %include tty_buffer_block;
 325 %page;
 326 %include lct;
 327 %page;
 328 %include dn355_data;
 329 %page;
 330 %include pcb;
 331 %page;
 332 %include mailbox_ops;
 333 %page;
 334 %include tty_space_man_dcls;
 335 %page;
 336 %include line_types;
 337 %page;
 338 %include mux_init_info;
 339 %page;
 340 %include io_chnl_util_dcls;
 341 %include mcs_modes_change_list;
 342 %include flow_control_info;
 343 %include channel_manager_dcls;
 344 %include mcs_interrupt_info;
 345 %include fnp_meters;
 346 %include fnp_channel_meters;
 347 %include get_comm_meters_info;
 348 %include io_manager_dcls;
 349 %include mcs_echo_neg_sys;
 350 ^L
 351 init_multiplexer:
 352      entry (a_devx, a_init_info_ptr, a_fnpp, a_code);
 353 
 354 /* This entry is called to initialize data bases preparatory to loading an FNP
 355    *  In particular, it initializes the appropriate entry in fnp_info, and allocates
 356    *   and initializes PCBs
 357 */
 358 
 359 
 360           devx = a_devx;
 361           miip = a_init_info_ptr;
 362           mii_chan_count = mux_init_info.no_channels;
 363           pcb_space_ptr = null ();                          /* make cleanup safe */
 364           infop = addr (dn355_data$);
 365           ttybp = addr (tty_buf$);
 366           lctp = tty_buf.lct_ptr;
 367 
 368           lcntp = lct.lcnt_ptr;                             /* get channel name */
 369           if length (rtrim (lcnt.names (devx))) ^= 1
 370           then go to bad_channel;
 371           my_chan_name = rtrim (lcnt.names (devx));
 372           call parse_fnp_name_ (my_chan_name, dno);
 373           if dno < 0                                        /* unreasonable name */
 374           then do;
 375 bad_channel:
 376                a_code = error_table_$bad_channel;
 377                go to init_exit;
 378           end;
 379 
 380           fnpp = addr (datanet_info.per_datanet (dno));
 381           call TRACE ("init_multiplexer");                  /* only error trace if bad devx */
 382 
 383           if my_chan_name ^= fnp_info.fnp_tag
 384           then go to bad_channel;
 385           if ^tty_buf.fnp_config_flags (dno)
 386           then go to bad_channel;
 387 
 388           call lock$lock_fast (addr (datanet_info.configuration_lock));
 389                                                             /* noone else can configure */
 390           fnp_info.lcte_ptr = addr (lct.lcte_array (devx));
 391 
 392           if fnp_info.t_and_d_in_progress                   /* lcte will be invalid, but still */
 393           then do;
 394                code = error_table_$io_not_available;
 395                go to init_abort;
 396           end;
 397 
 398           call assign_channel (code);                       /* under config lock */
 399           if code ^= 0
 400           then go to init_abort;                            /* it may have been deconfigured while we were farting around */
 401 
 402           call fnp_util$fill_page_table ((fnp_info.fnp_number), code);
 403           if code ^= 0
 404           then go to init_abort;                            /* IOI has problems? */
 405 
 406           do i = 0 to 2;                                    /* initialize line-number indexes */
 407                fnp_info.hsla_idx (i) = -1;
 408                fnp_info.lsla_idx (i) = -1;
 409           end;
 410           do i = 3 to 5;                                    /* 3 more for LSLAs */
 411                fnp_info.lsla_idx (i) = -1;
 412           end;
 413 
 414           pcb_space = size (pcb) * mii_chan_count;          /* get enough space for an array of PCBs */
 415           space_needed = pcb_space + 8 * DCW_LIST_SIZE;
 416           call tty_space_man$get_space (space_needed, pcb_space_ptr);
 417           if pcb_space_ptr = null                           /* this would be unfortunate */
 418           then do;
 419                a_code = error_table_$noalloc;
 420                go to init_abort;
 421           end;
 422           n_pcbs, fnp_info.no_of_channels = mii_chan_count;
 423           pcb_space_ptr -> pcb_array (*).saved_meters_ptr = null ();
 424                                                             /* for cleanup */
 425           fnp_info.pcb_array_ptr = pcb_space_ptr;
 426           fnp_info.dcw_list_array_ptr = addrel (pcb_space_ptr, pcb_space);
 427           string (fnp_info.flags) = "0"b;
 428           prev_la_no = -1;                                  /* so test will work right the first time */
 429           old_flag = "1"b;                                  /* HSLA channels (if any) are always first */
 430 
 431 /*
 432    * The following code assigns line numbers and sets the adapter indexes
 433    * It assumes that channels in mux_init_info are sorted in ascending order
 434 */
 435 
 436           do pcbx = 1 to n_pcbs;
 437                pcbp = addr (pcb_space_ptr -> pcb_array (pcbx));
 438                unspec (pcb) = "0"b;
 439                pcb.saved_meters_ptr = null ();              /* for cleanup dept */
 440                pcb.devx = mux_init_info.channels (pcbx).devx;
 441                lctep = addr (lct.lcte_array (pcb.devx));
 442                lcte.subchannel = pcbx;
 443                name = mux_init_info.channels (pcbx).name;
 444                call parse_tty_name_ (name, his_fnp_no, hsla_flag, la_no, subchan);
 445                if his_fnp_no ^= dno
 446                then do;
 447                     code = error_table_$bad_channel;
 448                     go to init_abort;
 449                end;
 450                if la_no = 7
 451                then fnp_info.tandd_pcbx = pcbx;
 452                else if (la_no ^= prev_la_no | hsla_flag ^= old_flag)
 453                                                             /* first subchannel on this adapter */
 454                then do;
 455                     if hsla_flag
 456                     then fnp_info.hsla_idx (la_no) = pcbx;
 457                     else fnp_info.lsla_idx (la_no) = pcbx;
 458                     prev_la_no = la_no;
 459                     old_flag = hsla_flag;
 460                end;
 461 
 462                pcb.subchan = subchan;
 463                pcb.is_hsla = hsla_flag;
 464                pcb.la_no = bit (bin (la_no, 3), 3);
 465                if hsla_flag
 466                then pcb.slot_no = bit (bin (subchan, 6), 6);
 467 
 468 
 469 /*             * lsla slot number has to wait for baud rate supplied at bootload time */
 470 
 471                on area
 472                     begin;
 473                     code = error_table_$noalloc;
 474                     go to init_abort;
 475                end;
 476 
 477                call tty_area_manager$allocate (size (fnp_channel_meters), temp_saved_meters_ptr);
 478                pcb.saved_meters_ptr = temp_saved_meters_ptr;
 479           end;
 480 
 481           call lock$unlock_fast (addr (datanet_info.configuration_lock));
 482 
 483           a_fnpp = fnpp;                                    /* pass this back */
 484           a_code = 0;
 485 init_exit:
 486           return;
 487 
 488 init_abort:
 489           call TRACE_ERROR ("init_multiplexer", code);
 490           call lock$unlock_fast (addr (datanet_info.configuration_lock));
 491           if pcb_space_ptr ^= null
 492           then do;
 493                do pcbx = 1 to n_pcbs;
 494                     pcbp = addr (pcb_space_ptr -> pcb_array (pcbx));
 495                     if pcb.saved_meters_ptr ^= null ()
 496                     then call tty_area_manager$free (size (fnp_channel_meters), (pcb.saved_meters_ptr));
 497                end;
 498                call tty_space_man$free_space (space_needed, pcb_space_ptr);
 499           end;
 500           a_code = code;
 501           return;
 502 ^L
 503 terminate_multiplexer:
 504      entry (a_fnpp, a_code);
 505 
 506 /* This entry is called after FNP crash or shutdown in order to free PCBs */
 507 
 508           fnpp = a_fnpp;
 509           ttybp = addr (tty_buf$);
 510           infop = addr (dn355_data$);
 511           locked = "0"b;
 512           call TRACE ("terminate_multiplexer");
 513           call lock;
 514           if code ^= 0
 515           then go to terminate_return;
 516 
 517           if fnp_info.bootloading | fnp_info.wired | fnp_info.running
 518                                                             /* bad time to terminate */
 519           then code = error_table_$invalid_state;
 520 
 521           else do;
 522                do i = 1 to fnp_info.no_of_channels;
 523                     pcbp = addr (fnp_info.pcb_array_ptr -> pcb_array (i));
 524                     if pcb.write_first ^= 0
 525                     then call tty_space_man$free_chain ((pcb.devx), OUTPUT, ptr (ttybp, pcb.write_first));
 526                     if pcb.read_first ^= 0
 527                     then call tty_space_man$free_chain ((pcb.devx), INPUT, ptr (ttybp, pcb.read_first));
 528                     call tty_area_manager$free (size (fnp_channel_meters), (pcb.saved_meters_ptr));
 529                     if pcb.copied_meters_offset ^= 0        /* free this if it's there */
 530                     then do;
 531                          call tty_space_man$free_space (size (fnp_channel_meters), ptr (ttybp, pcb.copied_meters_offset));
 532                          pcb.copied_meters_offset = 0;
 533                     end;
 534                end;
 535 
 536                string (fnp_info.flags) = "0"b;
 537                call tty_space_man$free_space (size (pcb) * fnp_info.no_of_channels + 8 * DCW_LIST_SIZE,
 538                     fnp_info.pcb_array_ptr);
 539                fnp_info.pcb_array_ptr = null;
 540                code = 0;
 541           end;
 542 
 543           if fnp_info.io_manager_assigned
 544           then call unassign_channel (code);                /* not deconfigured on us */
 545           call fnp_util$free_page_table ((fnp_info.fnp_number), (0));
 546                                                             /* even if we lost the assignment ... */
 547           call unlock;
 548 
 549 terminate_return:
 550           if code ^= 0
 551           then call TRACE_ERROR ("terminate_multiplexer", code);
 552           a_code = code;
 553           return;
 554 ^L
 555 start:
 556      entry (a_fnpp, a_code);
 557 
 558 /* entry to enable an FNP by sending "accept_calls" order */
 559 
 560           fnpp = a_fnpp;
 561           infop = addr (dn355_data$);
 562           call TRACE ("start");
 563           chanx = 1;                                        /* this is irrelevant, but will make setup happy */
 564           call setup;
 565           if code = 0
 566           then do;
 567                call dn355$send_global_wcd (fnpp, accept_calls, 18,
 568                     bit (bin (bin (rel (addr (tty_buf.free_space)), 18) + tty_buf.absorig, 18), 18));
 569                call unlock;                                 /* setup locked */
 570           end;
 571           if code ^= 0
 572           then call TRACE_ERROR ("start", code);
 573           a_code = code;
 574           return;
 575 
 576 
 577 stop:
 578      entry (a_fnpp, a_code);
 579 
 580 /* entry to disable an FNP from further dialups (by sending dont_accept_calls order) */
 581 
 582           fnpp = a_fnpp;
 583           infop = addr (dn355_data$);
 584           call TRACE ("stop");
 585           chanx = 1;                                        /* as for start entry */
 586           call setup;
 587           if code = 0
 588           then do;
 589                call dn355$send_global_wcd (fnpp, dont_accept_calls, 0, ""b);
 590                call unlock;                                 /* setup locked */
 591           end;
 592           if code ^= 0
 593           then call TRACE_ERROR ("stop", code);
 594           a_code = code;
 595           return;
 596 
 597 
 598 shutdown:
 599      entry (a_fnpp, a_code);
 600 
 601 /* This entry simulates an FNP crash; if the FNP is up, all lines will be hung up */
 602 
 603           infop = addr (dn355_data$);
 604           fnpp = a_fnpp;
 605           if fnpp = null ()
 606           then do;
 607                if datanet_info.trace
 608                then call syserr (ANNOUNCE, "fnp_multiplexer$shutdown: Called with null fnp_ptr");
 609                go to shutdown_return;
 610           end;
 611           call TRACE ("shutdown");
 612           infop = addr (dn355_data$);
 613 
 614           if fnp_info.wired | fnp_info.bootloading          /* do the user ring a favor */
 615           then do;
 616                if datanet_info.trace
 617                then call syserr (ANNOUNCE, "fnp_multiplexer$shutdown: Called with FNP wired.");
 618                call fnp_util$unwire ((fnp_info.fnp_number), code);
 619                if code ^= 0
 620                then call syserr$error_code (ANNOUNCE, code, "fnp_multiplexer$shutdown: Failed to unwire fnp.");
 621           end;
 622 
 623           locked = "0"b;
 624           if fnp_info.running                               /* if it's up now */
 625           then do;
 626                call lock;
 627                call dn355$hangup_fnp_lines ((fnp_info.fnp_number));
 628                fnp_info.running = "0"b;
 629                call unlock;
 630           end;
 631 
 632 
 633 shutdown_return:
 634           a_code = 0;
 635           return;
 636 ^L
 637 
 638 read:
 639      entry (a_fnpp, a_subchan, a_chainp, a_mi_flag, a_code);
 640 
 641 /* this is a dummy entry, dn355 never holds input at interrupt time */
 642 
 643           a_chainp = null;
 644           a_mi_flag = "0"b;
 645           a_code = 0;
 646           return;
 647 
 648 
 649 write:
 650      entry (a_fnpp, a_subchan, a_output_ptr, a_code);
 651 
 652           fnpp = a_fnpp;
 653           chanx = a_subchan;
 654           output_ptr = a_output_ptr;
 655 
 656           call setup;
 657           if code ^= 0
 658           then do;
 659                a_code = code;
 660                return;
 661           end;
 662 
 663 /* figure out length of chain */
 664 
 665           blockp = output_ptr;
 666           output_length = buffer.tally;                     /* to start with */
 667 
 668           do while (buffer.next ^= 0);
 669                blockp = ptr (ttybp, buffer.next);
 670                output_length = output_length + buffer.tally;
 671           end;
 672 
 673           if pcb.write_last ^= 0                            /* existing write chain */
 674           then do;
 675                lastp = ptr (ttybp, pcb.write_last);
 676                lastp -> buffer.next = bin (rel (output_ptr));
 677           end;
 678 
 679           else pcb.write_first = bin (rel (output_ptr));
 680 
 681           pcb.write_last = bin (rel (blockp));              /* in any case */
 682           pcb.write_cnt = pcb.write_cnt + output_length;
 683 
 684           if pcb.send_output                                /* if the FNP is ready for it */
 685           then call dn355$send_wcd (fnpp, pcbp, accept_direct_output, 0, ""b);
 686 
 687           code = 0;
 688 write_exit:
 689           call unlock;
 690           if code = 0
 691           then a_output_ptr = null ();                      /* so caller will know we took it all */
 692           a_code = code;
 693           return;
 694 ^L
 695 control:
 696      entry (a_fnpp, a_subchan, a_order, a_data_ptr, a_code);
 697 
 698           fnpp = a_fnpp;
 699           chanx = a_subchan;
 700           order = a_order;
 701           data_ptr = a_data_ptr;
 702 
 703           dumpin, dumpout, set_write_status, get_meters = "0"b;
 704                                                             /* initialize local variables */
 705           opcode, alter_type = -1;
 706           check = "0"b;
 707 
 708           if order = "read_status"                          /* there's never any at this level */
 709           then do;
 710                data_ptr -> rd_stat.input_available = "0"b;
 711                a_code = 0;
 712                return;
 713           end;
 714 
 715           else if order = "hangup"
 716           then do;
 717                mbx_data_len = 0;
 718                mbx_data = ""b;
 719                opcode = disconnect_this_line;
 720           end;
 721 
 722           else if order = "wru"
 723           then do;
 724                alter_type = Wru;
 725                alter_data = ""b;
 726           end;
 727 
 728           else if order = "interrupt"
 729           then do;
 730                alter_type = Break;
 731                alter_data = ""b;
 732           end;
 733 
 734           else if order = "start_xmit_hd" | order = "stop_xmit_hd"
 735           then do;
 736                alter_type = Xmit_hold;
 737                alter_data = "00000000"b || (order = "start_xmit_hd");
 738           end;
 739 
 740           else if order = "set_input_message_size"
 741           then do;
 742                mbx_data = bit (bin (data_ptr -> based_fb_word, 18), 18);
 743                opcode = sync_msg_size;
 744           end;
 745 
 746           else if order = "line_control"
 747           then do;
 748                mbx_data_len = 72;
 749                mbx_data = data_ptr -> based_bit72;
 750                opcode = line_control;
 751           end;
 752 
 753           else if order = "set_framing_chars"
 754           then do;
 755                mbx_data_len = 18;
 756                mbx_data = data_ptr -> based_bit18;          /* two characters are packed in halfword */
 757                opcode = set_framing_chars;
 758           end;
 759 
 760           else if order = "set_delay"
 761           then do;
 762                mbx_data_len = 108;
 763                mbx_data = data_ptr -> based_bit108;         /* 6 18-bit values */
 764                opcode = set_delay_table;
 765           end;
 766 
 767           else if order = "abort"                           /* i.e., resetread or resetwrite */
 768           then do;
 769                dumpin = substr (data_ptr -> based_bit2, 2, 1);
 770                                                             /* we'll simply save this info for later */
 771                dumpout = substr (data_ptr -> based_bit2, 1, 1);
 772           end;
 773 
 774           else if order = "set_line_type"
 775           then do;
 776                mbx_data_len = 18;
 777                local_line_type = data_ptr -> based_fb_word;
 778                if local_line_type <= 0 | local_line_type > max_line_type
 779                then go to order_error;
 780                check = "1"b;                                /* we'll have to look at PCB (after locking) */
 781                opcode = set_line_type;
 782           end;
 783 
 784           else if order = "dial_out"
 785           then do;                                          /* we have to convert digits (in char. form) to 6-bit BCD */
 786                digit_pos = 0;
 787                do i = 1 to length (data_ptr -> phone_chars);/* should never see "X" in phone number */
 788                     next_digit = index ("0123456789XXX!", substr (data_ptr -> phone_chars, i, 1)) - 1;
 789                                                             /* a value of 13 tells autocall unit to wait for a */
 790                                                             /* dial tone before asking for another dialing digit */
 791                     if next_digit >= 0
 792                     then if next_digit < 10 | next_digit = 13
 793                          then do;                           /* it's actually a digit */
 794                               digit_pos = digit_pos + 1;
 795                               phone_digits (digit_pos) = bit (next_digit, 6);
 796                          end;
 797                end;
 798 
 799                phone_no_len = 6 * digit_pos;
 800                opcode = dial;
 801                check = "1"b;                                /* special stuff required here too */
 802           end;
 803 
 804           else if order = "listen"
 805           then do;
 806                alter_type = Listen;
 807                alter_data = "000000001"b;
 808           end;
 809 
 810           else if order = "write_status"
 811           then set_write_status = "1"b;
 812 
 813           else if order = "enter_receive"
 814           then do;
 815                mbx_data_len = 0;
 816                mbx_data = ""b;
 817                opcode = enter_receive;
 818           end;
 819           else if order = "start_negotiated_echo"
 820           then do;
 821                mbx_data_len = 36;
 822                mbx_data =
 823                     bit (fixed (data_ptr -> echo_start_data.ctr, 18), 18)
 824                     || bit (fixed (data_ptr -> echo_start_data.screenleft, 18), 18);
 825                opcode = start_negotiated_echo;
 826           end;
 827           else if order = "set_echnego_break_table"
 828           then do;
 829                mbx_data_len = length (unspec (echnego_break_table));
 830                unspec (echnego_break_table) = ""b;          /* Get pads */
 831                do i = 0 to hbound (echnego_break_table.words, 1);
 832                     echnego_break_table.bits (i) = substr (data_ptr -> based_echo_table_bits, 1 + 16 * i, 16);
 833                end;
 834                mbx_data_long = unspec (echnego_break_table);
 835                opcode = set_echnego_break_table;
 836           end;
 837           else if order = "init_echo_negotiation"
 838           then do;
 839                mbx_data_len = 0;
 840                mbx_data = ""b;
 841                opcode = init_echo_negotiation;
 842           end;
 843           else if order = "stop_negotiated_echo"
 844           then do;
 845                mbx_data_len = 0;
 846                mbx_data = ""b;
 847                opcode = stop_negotiated_echo;
 848           end;
 849           else if order = "input_flow_control_chars"
 850           then do;
 851                mbx_data_len = 36;
 852                if data_ptr -> input_flow_control_info.resume_seq.count = 0
 853                                                             /* turning it all off */
 854                then mbx_data = ""b;
 855                else do;
 856                     mbx_data =
 857                          unspec (substr (data_ptr -> input_flow_control_info.suspend_seq.chars, 1, 1))
 858                          || unspec (substr (data_ptr -> input_flow_control_info.resume_seq.chars, 1, 1))
 859                          || data_ptr -> input_flow_control_info.timeout;
 860                     if data_ptr -> input_flow_control_info.suspend_seq.count = 0
 861                     then substr (mbx_data, 1, 9) = "0"b;    /* don't send suspend char if there isn't one */
 862                end;
 863                opcode = input_fc_chars;
 864           end;
 865           else if order = "output_flow_control_chars"
 866           then do;
 867                mbx_data_len = 36;
 868                if data_ptr -> output_flow_control_info.suspend_or_etb_seq.count = 0
 869                                                             /* no chars */
 870                then mbx_data = "0"b;
 871                else mbx_data =
 872                          unspec (substr (data_ptr -> output_flow_control_info.suspend_or_etb_seq.chars, 1, 1))
 873                          || unspec (substr (data_ptr -> output_flow_control_info.resume_or_ack_seq.chars, 1, 1))
 874                          || data_ptr -> output_flow_control_info.block_acknowledge;
 875                opcode = output_fc_chars;
 876           end;
 877 
 878           else if order = "copy_meters"
 879           then do;
 880                opcode = report_meters;
 881                check = "1"b;
 882           end;
 883 
 884           else if order = "get_meters"
 885           then do;
 886                ret_meters_ptr = data_ptr -> get_comm_meters_info.parent_ptr;
 887                if ret_meters_ptr = null ()
 888                then return;
 889                else if ret_meters_ptr -> fnp_chan_meter_struc.version ^= FNP_CHANNEL_METERS_VERSION_1
 890                then do;
 891                     a_code = error_table_$unimplemented_version;
 892                     return;
 893                end;
 894 
 895                else get_meters = "1"b;
 896           end;
 897 
 898           else if order = "tandd_attach"
 899           then do;                                          /* simulate a dialup without bothering the FNP (channel is hung up already) */
 900                call setup;
 901                if code ^= 0
 902                then do;
 903                     a_code = code;
 904                     return;
 905                end;
 906 
 907                if pcb.listen | pcb.dialed                   /* can't have this */
 908                then do;
 909                     call unlock;
 910                     a_code = error_table_$resource_not_free;
 911                     return;
 912                end;
 913 
 914                pcb.dialed = "1"b;
 915                pcb.tandd_attached = "1"b;
 916                unspec (dialup_info) = ""b;
 917                dialup_info.baud_rate = 1200;                /* just so it's something */
 918                dialup_info.line_type = LINE_ASCII;          /* make everyone's life easier */
 919                dialup_info.max_buf_size = 16;               /* COLTS wants small buffers */
 920                call channel_manager$interrupt ((pcb.devx), DIALUP, unspec (dialup_info));
 921                call unlock;
 922                a_code = 0;
 923                return;
 924           end;
 925 
 926           else do;
 927 order_error:
 928                a_code = error_table_$undefined_order_request;
 929                return;
 930           end;
 931 
 932           code = 0;
 933           call setup;
 934           if code ^= 0
 935           then do;
 936                a_code = code;
 937                return;
 938           end;
 939 
 940           if opcode = disconnect_this_line                  /* hangup */
 941           then do;
 942                pcb.listen, pcb.tandd_attached = "0"b;
 943           end;
 944 
 945           if opcode = start_negotiated_echo & (pcb.write_first ^= 0
 946                                                             /* We have queued output */
 947                | pcb.output_mbx_pending)
 948           then do;                                          /* The FNP has not take the mbx. */
 949                                                             /* handler re-do it when he sees this. */
 950                call unlock;
 951                a_code = error_table_$invalid_write;
 952                return;
 953           end;
 954 
 955 
 956           if alter_type ^= -1                               /* alter_parameters required */
 957           then do;
 958                if alter_type = Listen
 959                then do;                                     /* need to tell it buffer size */
 960                     alter_data = alter_data || fnp_buf_size ();
 961                     pcb.listen = "1"b;
 962                end;
 963 
 964                mbx_data_len = length (alter_data) + 9;      /* 9 bits for subop type */
 965                mbx_data = bit (bin (alter_type, 9), 9) || alter_data;
 966                opcode = alter_parameters;
 967           end;
 968 
 969           if opcode ^= -1                                   /* we do have to send the FNP something */
 970           then do;
 971                if check                                     /* anything special about it */
 972                then do;
 973                     if opcode = set_line_type               /* make sure this is OK */
 974                     then if pcb.listen
 975                          then do;                           /* it isn't */
 976                               call unlock;
 977                               go to order_error;
 978                          end;
 979 
 980                          else do;
 981                               mbx_data = bit (bin (local_line_type, 18), 18);
 982                               do i = 1 to n_sync_line_types while (local_line_type ^= sync_line_type (i));
 983                               end;
 984 
 985                               pcb.sync_line = (i <= n_sync_line_types);
 986                          end;
 987 
 988                     else if opcode = dial                   /* in this case we have to supply buffer size first */
 989                     then do;                                /* because no listen was done */
 990                          mbx_data_len = 36;
 991                          alter_data = bit (bin (Set_buffer_size, 9), 9) || "000000001"b;
 992                          mbx_data = alter_data || fnp_buf_size ();
 993                          call dn355$send_wcd (fnpp, pcbp, alter_parameters, mbx_data_len, mbx_data);
 994 
 995                          mbx_data_len = phone_no_len;
 996                          mbx_data_long = string (phone_digits);
 997                     end;
 998                     else if opcode = report_meters
 999                     then do;
1000                          call tty_space_man$get_space (size (fnp_channel_meters), meter_ptr);
1001                                                             /* get a buffer for the FNP meters */
1002                          if meter_ptr = null ()             /* couldn't get it */
1003                          then do;
1004                               call unlock;
1005                               a_code = error_table_$noalloc;
1006                               return;
1007                          end;
1008 
1009                          pcb.copied_meters_offset = bin (rel (meter_ptr), 18);
1010                          mbx_data = bit (bin (tty_buf.absorig + pcb.copied_meters_offset, 18), 18);
1011                          mbx_data_len = 18;
1012                     end;
1013                end;
1014 
1015                call dn355$send_wcd (fnpp, pcbp, opcode, mbx_data_len, mbx_data_long);
1016           end;
1017 
1018           else do;
1019                if dumpin
1020                then call dn355$send_wcd (fnpp, pcbp, alter_parameters, 9, bit (bin (Dumpinput, 9), 9));
1021 
1022                if dumpout
1023                then do;                                     /* first get rid of any ring 0 output */
1024                     if pcb.write_first ^= 0
1025                     then do;
1026                          call tty_space_man$free_chain ((pcb.devx), OUTPUT, ptr (ttybp, pcb.write_first));
1027                          pcb.write_first, pcb.write_last, pcb.write_cnt = 0;
1028                     end;
1029 
1030 
1031                     call dn355$send_wcd (fnpp, pcbp, alter_parameters, 9, bit (bin (Dumpoutput, 9), 9));
1032 
1033                     if pcb.end_frame
1034                     then do;
1035                          pcb.end_frame = "0"b;
1036                          if pcb.send_output
1037                          then call channel_manager$interrupt ((pcb.devx), SEND_OUTPUT, ""b);
1038                     end;
1039                end;
1040 
1041                if set_write_status
1042                then opend = (pcb.write_first ^= 0);         /* this has to be in automatic, return structure isn't wired */
1043 
1044                if get_meters
1045                then do;
1046                     call get_fnp_meters ("0"b);
1047                     call unlock;
1048 
1049                     if code = 0
1050                     then ret_meters_ptr -> fnp_chan_meter_struc.synchronous = pcb.sync_line;
1051                     if fnp_meters_ptr ^= null ()
1052                     then do;
1053                          if unspec (fnp_meters_ptr -> fnp_channel_meters) = "0"b
1054                          then code = error_table_$no_channel_meters;
1055                          else do;
1056                               ret_meters_ptr -> fnp_chan_meter_struc.current_meters =
1057                                    fnp_meters_ptr -> fnp_channel_meters;
1058                               ret_meters_ptr -> fnp_chan_meter_struc.saved_meters =
1059                                    pcb.saved_meters_ptr -> fnp_channel_meters;
1060                          end;
1061 
1062                          call tty_space_man$free_space (size (fnp_channel_meters), fnp_meters_ptr);
1063                     end;
1064                end;
1065           end;
1066 
1067           call unlock;
1068           if set_write_status
1069           then data_ptr -> wr_stat.output_pending = opend;
1070           a_code = code;
1071 
1072           return;
1073 ^L
1074 check_modes:
1075      entry (a_fnpp, a_subchan, a_mode_list_ptr, a_code);
1076 
1077 /* this entry is used to determine if this multiplexer understands or accepts a given set of modes */
1078 
1079           fnpp = a_fnpp;
1080           chanx = a_subchan;
1081           mclp = a_mode_list_ptr;
1082           if mcl.version ^= mcl_version_2
1083           then do;
1084                a_code = error_table_$unimplemented_version;
1085                return;
1086           end;
1087 
1088           call setup;                                       /* now we need PCB pointer */
1089           if code ^= 0
1090           then do;
1091                a_code = code;
1092                return;
1093           end;
1094 
1095           do modex = 1 to mcl.n_entries;
1096                mclep = addr (mcl.entries (modex));
1097                mode_name = substr (mcle.mode_name, 1, 8);
1098                mode_on = mcle.mode_switch;
1099 
1100                do i = 1 to hbound (good_modes, 1) while (mode_name ^= good_modes (i));
1101                end;
1102 
1103                if i <= hbound (good_modes, 1)               /* tree */
1104                                                             /* it's one of the ones we always recognize */
1105                then mcle.mpx_mode = "1"b;
1106                else do;
1107                     do i = 1 to hbound (async_only_modes, 1) while (mode_name ^= async_only_modes (i));
1108                     end;
1109 
1110                     if i > hbound (async_only_modes, 1)     /* we've never heard of this one at all */
1111                     then mcle.mpx_mode = "0"b;
1112                     else do;
1113                          mcle.mpx_mode = ^pcb.sync_line;    /* this mode is meaningful for asynchronous lines only */
1114 
1115                          do i = 1 to hbound (full_dpx_modes, 1) while (mode_name ^= full_dpx_modes (i));
1116                          end;
1117 
1118                          if (mode_name = "no_outp" | mode_name = "8bit" | mode_name = "oddp") & mode_on
1119                          then if ^pcb.is_hsla
1120                               then go to bad_mode;
1121 
1122                          if i <= hbound (full_dpx_modes, 1) /* if this was a mode requiring full duplex capability */
1123                          then if mode_on
1124                               then if pcb.line_type ^= LINE_ASCII & pcb.line_type ^= LINE_ASYNC1
1125                                         & pcb.line_type ^= LINE_ASYNC2 & pcb.line_type ^= LINE_ASYNC3
1126                                    then do;
1127 bad_mode:
1128                                         if mcle.force
1129                                         then mcle.mpx_mode = "0"b;
1130                                         else do;
1131                                              code = error_table_$bad_mode;
1132                                              mcle.error = "1"b;
1133                                         end;
1134                                    end;
1135 
1136                     end;
1137                end;
1138           end;
1139 
1140           call unlock;                                      /* setup locked */
1141           a_code = code;
1142           return;
1143 ^L
1144 set_modes:
1145      entry (a_fnpp, a_subchan, a_mode_list_ptr, a_code);
1146 
1147 /* this entry sets a specified set of mode (probably by calling dn355$send_wcd) */
1148 
1149           fnpp = a_fnpp;
1150           chanx = a_subchan;
1151           mclp = a_mode_list_ptr;
1152           if mcl.version ^= mcl_version_2
1153           then do;
1154                a_code = error_table_$unimplemented_version;
1155                return;
1156           end;
1157 
1158           call setup;
1159           if code ^= 0
1160           then do;
1161                a_code = code;
1162                return;
1163           end;
1164 
1165           hndlquit_set = "0"b;
1166           string (mode_set) = "0"b;                         /* nothing set yet */
1167 
1168           do modex = 1 to mcl.n_entries;
1169                mclep = addr (mcl.entries (modex));
1170                if mcle.mpx_mode                             /* if this is one we're interested in */
1171                then call process_mode (mcle.mode_name, mcle.mode_switch);
1172           end;
1173 
1174           if mcl.init
1175           then do;                                          /* if "init" we must turn off the ones that weren't mentioned */
1176                if ^hndlquit_set
1177                then call process_mode ("hndlquit", "0"b);
1178 
1179                do modex = 1 to hbound (async_only_modes, 1);
1180                     if ^mode_set (modex)
1181                     then call process_mode (async_only_modes (modex), "0"b);
1182                end;
1183           end;
1184 
1185           call unlock;
1186           a_code = code;
1187           return;
1188 
1189 
1190 
1191 get_modes:
1192      entry (a_fnpp, a_subchan, a_modes, a_code);
1193 
1194 /* this is a dummy, we don't keep records of modes at this level */
1195 
1196           a_modes = "";
1197           a_code = 0;
1198           return;
1199 ^L
1200 priv_control:
1201      entry (a_fnpp, a_order, a_data_ptr, a_code);
1202 
1203 /* entry for privileged global orders */
1204 
1205           fnpp = a_fnpp;
1206           order = a_order;
1207           data_ptr = a_data_ptr;
1208 
1209           if order = "dump_fnp"
1210           then do;
1211                call setup_fnp;
1212                if code ^= 0
1213                then do;
1214                     a_code = code;
1215                     return;
1216                end;
1217 
1218                locked = "0"b;
1219                call send_global (dump_mem);
1220                if code ^= 0
1221                then go to end_dump_mem;
1222 
1223 /* send_global will wait; come back here after notify */
1224 
1225                n_fnp_words = dump_fnp_info.fnp_len;
1226                dump_fnp_info.bufp -> fnp_data = fnp_dump_ptr -> fnp_data;
1227 
1228 end_dump_mem:
1229                if code ^= error_table_$timeout              /* else we have to abandon the buffer */
1230                then call tty_space_man$free_space (dump_patch_space, fnp_dump_ptr);
1231                                                             /* this was allocated by setup_fnp */
1232                ignore = stacq (fnp_info.dump_patch_lock, "0"b, pds$processid);
1233           end;
1234 
1235           else if order = "get_meters"
1236           then do;
1237                fnp_meterp = data_ptr -> get_comm_meters_info.subchan_ptr;
1238                if fnp_meterp ^= null
1239                then do;
1240                     if fnp_meters.version ^= FNP_METERS_VERSION_2
1241                     then code = error_table_$unimplemented_version;
1242                     else do;
1243                          ttybp = addr (tty_buf$);           /* we'll need this */
1244                          call lock;
1245                          if code ^= 0
1246                          then do;
1247                               a_code = code;
1248                               return;
1249                          end;
1250 
1251                          call get_fnp_meters ("1"b);
1252 
1253                          if code = 0
1254                          then do;
1255                               fnp_meters.n_channels = fnp_info.no_of_channels;
1256                               fnp_meters.output_mbx_in_use_cum = fnp_info.cumulative_mbx_in_use;
1257                               fnp_meters.output_mbx_updates = fnp_info.mbx_in_use_updated;
1258                               fnp_meters.output_mbx_unavailable = fnp_info.mbx_unavailable;
1259                               fnp_meters.max_output_mbx_in_use = fnp_info.max_mbx_in_use;
1260                               fnp_meters.queue_entries_made = fnp_info.q_entries_made;
1261                               fnp_meters.input_rejects = fnp_info.input_reject_count;
1262                               fnp_meters.processed_from_q = fnp_info.processed_from_q;
1263                               fnp_meters.fnp_channel_locked = fnp_info.fnp_channel_locked;
1264                               fnp_meters.input_data_transactions = fnp_info.input_data_transactions;
1265                               fnp_meters.output_data_transactions = fnp_info.output_data_transactions;
1266                               fnp_meters.input_control_transactions = fnp_info.input_control_transactions;
1267                               fnp_meters.output_control_transactions = fnp_info.output_control_transactions;
1268                               fnp_meters.fnp_space_restricted_output = fnp_info.fnp_space_restricted_output;
1269                               fnp_meters.fnp_mem_size = fnp_info.fnp_mem_size;
1270                               begin;
1271 declare  iom fixed bin (3);
1272 declare  chan fixed bin (7);
1273                                    call io_chnl_util$name_to_iom (fnp_info.io_chanid, iom, chan, (0));
1274                                    fnp_meters.iom_number = iom;
1275                                    fnp_meters.iom_chan_no = chan;
1276                               end;
1277 
1278                          end;
1279 
1280                          call unlock;
1281 
1282                          if fnp_meters_ptr ^= null ()       /* let's make sure this is for real */
1283                          then do;
1284                               data_ptr -> get_comm_meters_info.subchan_ptr -> fnp_meters.from_fnp =
1285                                    fnp_meters_ptr -> fnp_global_meters;
1286 
1287                               call tty_space_man$free_space (size (fnp_global_meters), fnp_meters_ptr);
1288                          end;
1289 
1290                          lctep = fnp_info.lcte_ptr;         /* since we don't call channel_manager, */
1291                          lcmp = data_ptr -> get_comm_meters_info.logical_chan_ptr;
1292                                                             /* we have to copy logical channel data ourselves */
1293                          if lcmp ^= null ()
1294                          then do;
1295                               lcmp -> logical_chan_meters.current_meters = lcte.meters;
1296                               unspec (lcmp -> logical_chan_meters.saved_meters) = "0"b;
1297                                                             /* no saved meters for an FNP */
1298                          end;
1299                     end;
1300                end;
1301           end;
1302 
1303           else code = error_table_$undefined_order_request;
1304 
1305           a_code = code;
1306           return;
1307 ^L
1308 hpriv_control:
1309      entry (a_fnpp, a_order, a_data_ptr, a_code);
1310 
1311 /* entry for highly-privileged global orders */
1312 
1313           fnpp = a_fnpp;
1314           order = a_order;
1315           data_ptr = a_data_ptr;
1316           locked = "0"b;
1317           code = 0;
1318 
1319           if order = "patch_fnp"
1320           then do;
1321                call setup_fnp;
1322                if code ^= 0
1323                then do;
1324                     a_code = code;
1325                     return;
1326                end;
1327                n_fnp_words = dump_fnp_data.fnp_len;
1328                sourcep = dump_fnp_info.bufp;
1329 
1330                fnp_dump_ptr -> fnp_data = sourcep -> fnp_data;
1331                call syserr (ANNOUNCE, "fnp_multiplexer: patching FNP ^a for ^a:", fnp_info.fnp_tag, pds$process_group_id);
1332                                                             /* tell operator about it */
1333 
1334                temp_addr = dump_fnp_data.fnp_addr;
1335                do i = 1 to dump_fnp_data.fnp_len;
1336                     call syserr (ANNOUNCE, "^6w from ^6.3b to ^6.3b", temp_addr,
1337                          dump_fnp_info.old_value_ptr -> fnp_data (i), dump_fnp_info.bufp -> fnp_data (i));
1338                     temp_addr = temp_addr + 1;
1339                end;
1340 
1341                call send_global (patch_mem);                /* send it off and wait */
1342                if code ^= error_table_$timeout              /* else we have to abandon the buffer */
1343                then call tty_space_man$free_space (dump_patch_space, fnp_dump_ptr);
1344                                                             /* this was allocated by setup_fnp */
1345                ignore = stacq (fnp_info.dump_patch_lock, "0"b, pds$processid);
1346           end;
1347 
1348           else if order = "fnp_break"
1349           then do;
1350                call setup_fnp;
1351                if code ^= 0
1352                then do;
1353                     a_code = code;
1354                     return;
1355                end;
1356                fnp_break_data.action = fnp_break_info.action;
1357                                                             /* copy info */
1358                fnp_break_data.fnp_addr = fnp_break_info.fnp_addr;
1359                fnp_break_data.flags = substr (fnp_break_info.flags, 1, 18);
1360                name = fnp_break_info.chan_name;
1361                if name = ""
1362                then fnp_break_data.lineno = -1;             /* no line, i.e. any line */
1363                else do;
1364                     call name_to_pcb (name);
1365                     if code ^= 0
1366                     then do;
1367                          a_code = code;
1368                          return;
1369                     end;
1370                     fnp_break_data.lineno = bin (string (pcb.line_number));
1371                end;
1372 
1373                mbx_data = addr (fnp_break_data) -> based_bit72;
1374                if ^locked
1375                then call lock;
1376                if code = 0
1377                then do;
1378                     call dn355$send_global_wcd (fnpp, fnp_break, 72, mbx_data);
1379                     call unlock;
1380                end;
1381           end;
1382 
1383           else if order = "enable_breakall_mode"
1384           then ;
1385 
1386           else if order = "disable_breakall_mode"
1387           then ;
1388 
1389           else code = error_table_$undefined_order_request;
1390 
1391 hpriv_exit:
1392           a_code = code;
1393           return;
1394 ^L
1395 fnp_lock:
1396      entry (a_fnpp, a_code);                                /* Non-wired lock entry */
1397 
1398           fnpp = a_fnpp;
1399           call lock;
1400           a_code = code;
1401           return;
1402 
1403 fnp_unlock:
1404      entry (a_fnpp);
1405 
1406           fnpp = a_fnpp;
1407           mylock = "0"b;
1408           locked = "1"b;
1409           call unlock;
1410           return;
1411 ^L
1412 setup:
1413      proc;
1414 
1415 /* initial setup for per-channel stuff */
1416 
1417 
1418           code = 0;                                         /* innocent until proven guilty */
1419           ttybp = addr (tty_buf$);
1420           infop = addr (dn355_data$);
1421           locked, queue_locked = "0"b;
1422           call lock;
1423           if code ^= 0
1424           then return;
1425 
1426           if fnp_info.running
1427           then pcbp = addr (fnp_info.pcb_array_ptr -> pcb_array (chanx));
1428           else do;
1429                call unlock;
1430                code = error_table_$mpx_down;
1431                return;
1432           end;
1433 
1434           if pcb.copied_meters_ready                        /* dn355 left them for us */
1435           then if ^lcte.locked_for_interrupt                /* make sure we're on call side */
1436                then call save_copied_meters;
1437 
1438           return;
1439      end setup;
1440 
1441 
1442 
1443 setup_fnp:
1444      proc;
1445 
1446 dcl  (fnp_address, fnp_len) fixed bin (18);
1447 
1448 /* this procedure is used instead of setup for privileged global orders */
1449 
1450           if fnpp = null ()
1451           then go to setup_fnp_down;
1452           code = 0;
1453           if fnp_info.mbx_pt = null ()                      /* this one isn't configured */
1454                | ^fnp_info.running                          /* or it isn't up */
1455           then do;
1456 setup_fnp_down:
1457                code = error_table_$mpx_down;
1458                return;
1459           end;
1460 
1461           ttybp = addr (tty_buf$);
1462           infop = addr (dn355_data$);
1463 
1464           if order = "fnp_break"
1465           then return;                                      /* done if break order */
1466           if fnp_info.dump_patch_disabled
1467           then do;
1468                code = error_table_$timeout;
1469                return;
1470           end;
1471 
1472           fnp_address = dump_fnp_info.fnp_address;
1473           fnp_len = dump_fnp_info.fnp_len;
1474           if order = "dump_fnp"
1475           then do;                                          /* check dump params */
1476                if fnp_len <= 0 | fnp_len > 64
1477                then do;
1478 bad_fnp_len:
1479                     code = error_table_$buffer_big;
1480                     return;
1481                end;
1482           end;
1483           else if order = "patch_fnp"
1484           then if fnp_len <= 0 | fnp_len > 32
1485                then go to bad_fnp_len;
1486 
1487           if (fnp_address < 0) | ((fnp_address + fnp_len) > fnp_info.fnp_mem_size)
1488           then do;
1489                code = error_table_$dev_offset_out_of_bounds;
1490                return;
1491           end;
1492 
1493           if ^stac (addr (fnp_info.dump_patch_lock), pds$processid)
1494                                                             /* lock the dump_patch function */
1495           then do;                                          /* if possible */
1496                code = error_table_$seglock;
1497                return;
1498           end;
1499 
1500           dump_patch_space = divide (fnp_len + 1, 2, 17, 0);
1501           call tty_space_man$get_space (dump_patch_space, fnp_dump_ptr);
1502           if fnp_dump_ptr = null                            /* couldn't get the space */
1503           then do;
1504                code = error_table_$noalloc;
1505                ignore = stacq (fnp_info.dump_patch_lock, "0"b, pds$processid);
1506                return;
1507           end;
1508 
1509           dump_patch_time = clock ();
1510           fnp_info.dump_patch_in_progress = "1"b;
1511           dump_fnp_data.abs_addr = bin (rel (fnp_dump_ptr)) + tty_buf.absorig;
1512                                                             /* dump/patch is paged too ... */
1513           dump_fnp_data.fnp_addr = fnp_address;
1514           dump_fnp_data.fnp_len = fnp_len;
1515           return;
1516 
1517      end setup_fnp;
1518 ^L
1519 save_copied_meters:
1520      proc;
1521 
1522 /* internal procedure called  to pick up copied meters left in tty_buf by FNP */
1523 
1524 dcl  copied_meters_ptr ptr;
1525 
1526           if pcb.copied_meters_offset ^= 0                  /* make sure it's legit */
1527           then do;
1528                copied_meters_ptr = ptr (ttybp, pcb.copied_meters_offset);
1529 
1530 /* zero out pad fields, which contain random junk (possibly input) from the FNP */
1531 
1532                if pcb.sync_line
1533                then copied_meters_ptr -> fnp_sync_meters.pad (*) = 0;
1534                else copied_meters_ptr -> fnp_async_meters.pad (*) = 0;
1535 
1536                pcb.saved_meters_ptr -> fnp_channel_meters = copied_meters_ptr -> fnp_channel_meters;
1537                call tty_space_man$free_space (size (fnp_channel_meters), copied_meters_ptr);
1538                                                             /* through with buffer now */
1539                pcb.copied_meters_offset = 0;
1540                pcb.copied_meters_ready = "0"b;
1541           end;
1542 
1543           return;
1544      end save_copied_meters;
1545 ^L
1546 process_mode:
1547      proc (mode_name, mode_on);
1548 
1549 dcl  mode_name char (*);
1550 dcl  mode_on bit (1);
1551 dcl  mode_name_index fixed bin;
1552 
1553           alter_data = "00000000"b || mode_on;
1554 
1555           if mode_name = "hndlquit"
1556           then do;
1557                alter_type = Hndlquit;
1558                pcb.hndlquit = mode_on;
1559                hndlquit_set = "1"b;
1560           end;
1561 
1562           else if ^pcb.sync_line                            /* if we haven't already decided what to do */
1563           then do;
1564                if mode_name = "blk_xfer" | mode_name = "iflow"
1565                                                             /* special stuff here */
1566                then do;
1567                     if mode_name = "blk_xfer"
1568                     then do;
1569                          mode_name_index = BLK_XFER_INDEX;
1570                          alter_type = Block_xfer;
1571                     end;
1572                     else do;
1573                          mode_name_index = IFLOW_INDEX;
1574                          alter_type = Input_flow_control;
1575                     end;
1576 
1577                     if mode_on
1578                     then do;                                /* we have to tell it buffer sizes */
1579                          chars_per_sec = divide (pcb.baud_rate, 10, 17, 0);
1580                          base_len, block_len = divide (chars_per_sec, buf_per_second, 17, 0);
1581                                                             /* and 1/2 second thereafter */
1582                     end;
1583                     else do;
1584                          base_len = 56;
1585                          block_len = 0;
1586                     end;
1587 
1588                     alter_data = alter_data || bit (bin (base_len, 18), 18) || bit (bin (block_len, 18), 18);
1589                     mode_set (mode_name_index) = "1"b;
1590                end;
1591 
1592                else do;
1593                     do i = 1 to hbound (mode_alter_types, 1) while (mode_name ^= async_only_modes (i));
1594                     end;                                    /* note that blk_xfer is the last async_mode */
1595 
1596                     if i > hbound (mode_alter_types, 1)
1597                     then code = error_table_$bad_mode;
1598 
1599                     else do;
1600                          alter_type = mode_alter_types (i);
1601                          mode_set (i) = "1"b;               /* this one is set now */
1602                     end;
1603                end;
1604           end;
1605 
1606           if code = 0
1607           then do;
1608                mbx_data = bit (bin (alter_type, 9), 9) || alter_data;
1609                call dn355$send_wcd (fnpp, pcbp, alter_parameters, length (alter_data) + 9, mbx_data);
1610           end;
1611           return;
1612      end;
1613 ^L
1614 send_global:
1615      proc (opcode);
1616 
1617 /* this procedure calls dn355$send_global_wcd for the dump_fnp and patch_fnp orders */
1618 
1619 dcl  opcode fixed bin (8);
1620 
1621           call pxss$addevent (FNP_DUMP_PATCH_EVENT);        /* so we'll be able to wait */
1622           mbx_data = addr (dump_fnp_data) -> based_bit72;
1623           call lock;
1624           if code ^= 0
1625           then return;
1626 
1627           call dn355$send_global_wcd (fnpp, opcode, 72, mbx_data);
1628           call unlock;
1629 
1630           call pxss$wait;                                   /* mustn't do anything till it's done */
1631 
1632           do while (fnp_info.dump_patch_in_progress);       /* didn't complete yet */
1633                if ^fnp_info.running                         /* FNP crashed out from under us */
1634                then do;
1635                     code = error_table_$mpx_down;
1636                     fnp_info.dump_patch_in_progress = "0"b; /* so we'll get out of loop */
1637                end;
1638 
1639                else if clock () - dump_patch_time > DUMP_PATCH_LIMIT
1640                                                             /* time's up! */
1641                then do;
1642                     code = error_table_$timeout;            /* can this operation */
1643                     fnp_info.dump_patch_disabled = "1"b;
1644                     fnp_info.dump_patch_in_progress = "0"b;
1645                     call syserr (ANNOUNCE, "fnp_multiplexer: ^[dump^;patch^]_fnp order to FNP ^a timed out.",
1646                          opcode = dump_mem, fnp_info.fnp_tag);
1647                end;
1648 
1649                else do;                                     /* must be someone else's notify */
1650                     call pxss$addevent (FNP_DUMP_PATCH_EVENT);
1651                     if fnp_info.dump_patch_in_progress      /* make sure it still hasn't happened */
1652                     then call pxss$wait;
1653                     else call pxss$delevent (FNP_DUMP_PATCH_EVENT);
1654                                                             /* never mind, it's done */
1655                end;
1656 
1657           end;
1658 
1659           return;                                           /* all right, we're done */
1660 
1661      end send_global;
1662 ^L
1663 get_fnp_meters:
1664      proc (global);
1665 
1666 /* subroutine to issue request for meters from FNP and wait for them to arrive */
1667 
1668 dcl  global bit (1) parameter;                              /* indicates whether subchannel or whole FNP */
1669 dcl  space_size fixed bin;
1670 dcl  fnp_meter_wait_start fixed bin (71);
1671 
1672           if fnp_info.dump_patch_disabled
1673           then do;
1674                code = error_table_$timeout;                 /* don't even try */
1675                fnp_meters_ptr = null ();                    /* so caller won't try to free space */
1676                return;
1677           end;
1678 
1679           if global
1680           then space_size = size (fnp_global_meters);
1681           else space_size = size (fnp_channel_meters);
1682 
1683           call tty_space_man$get_space (space_size, fnp_meters_ptr);
1684           if fnp_meters_ptr = null ()
1685           then do;
1686                code = error_table_$noalloc;
1687                return;
1688           end;
1689 
1690           mbx_data = bit (bin (tty_buf.absorig + bin (rel (fnp_meters_ptr)), 18), 18);
1691           call pxss$addevent (FNP_METER_EVENT);
1692           fnp_meter_wait_start = clock ();
1693 
1694           if global                                         /* it's for whole FNP */
1695           then do;
1696                if fnp_info.get_meters_waiting
1697                then do;
1698                     code = error_table_$seglock;            /* can't have two going at once */
1699                     return;
1700                end;
1701 
1702                fnp_info.get_meters_waiting = "1"b;
1703                call dn355$send_global_wcd (fnpp, report_meters, 18, mbx_data);
1704                pcbp = fnpp;                                 /* to avoid faults in loop test */
1705           end;
1706 
1707           else do;
1708                pcb.get_meters_waiting = "1"b;
1709                call dn355$send_wcd (fnpp, pcbp, report_meters, 18, mbx_data);
1710           end;
1711 
1712           call unlock;                                      /* while waiting */
1713           call pxss$wait;
1714           call lock;                                        /* while checking */
1715 
1716           do while ((global & fnp_info.get_meters_waiting) | (^global & pcb.get_meters_waiting));
1717                if ^fnp_info.running
1718                then do;
1719                     code = error_table_$mpx_down;
1720                     go to abort_get_meters;
1721                end;
1722 
1723                else if clock () - fnp_meter_wait_start > DUMP_PATCH_LIMIT
1724                then do;
1725                     code = error_table_$timeout;
1726                     fnp_info.dump_patch_disabled = "1"b;
1727                     call syserr (ANNOUNCE,
1728                          "fnp_multiplexer: get_meters order for FNP ^a^[^s^;, line ^o,^] timed out.", fnp_info.fnp_tag,
1729                          global, string (pcb.line_number));
1730 abort_get_meters:
1731                     if global
1732                     then fnp_info.get_meters_waiting = "0"b;
1733                     else pcb.get_meters_waiting = "0"b;
1734                end;
1735 
1736                else do;
1737                     call unlock;                            /* in case we wait some more */
1738                     call pxss$addevent (FNP_METER_EVENT);
1739                     if (global & fnp_info.get_meters_waiting) | (^global & pcb.get_meters_waiting)
1740                                                             /* check if it happened since we checked */
1741                     then call pxss$wait;
1742                     else call pxss$delevent (FNP_METER_EVENT);
1743                     call lock;
1744                end;
1745           end;
1746 
1747           return;
1748      end get_fnp_meters;
1749 ^L
1750 name_to_pcb:
1751      proc (name);
1752 
1753 dcl  name char (*);
1754 
1755           code = 0;
1756           call parse_tty_name_ (name, his_fnp_no, hsla_flag, la_no, subchan);
1757           call lock;
1758           if code ^= 0
1759           then return;
1760 
1761           if his_fnp_no ^= fnp_info.fnp_number
1762           then go to bad_device;
1763 
1764           if hsla_flag
1765           then pcbx = fnp_info.hsla_idx (la_no);
1766           else pcbx = fnp_info.lsla_idx (la_no);
1767           if pcbx = -1
1768           then go to bad_device;
1769 
1770           found, past = "0"b;
1771           do j = pcbx to fnp_info.no_of_channels while (^past & ^found);
1772                pcbp = addr (fnp_info.pcb_array_ptr -> pcb_array (j));
1773                if pcb.la_no ^= bit (bin (la_no, 3), 3)
1774                then past = "1"b;
1775                else if pcb.slot_no = bit (bin (subchan, 6), 6)
1776                then found = "1"b;
1777           end;
1778 
1779           if ^found
1780           then do;
1781 bad_device:
1782                call unlock;
1783                code = error_table_$bad_channel;
1784                return;
1785           end;
1786 
1787           return;
1788      end name_to_pcb;
1789 ^L
1790 lock:
1791      proc;
1792 
1793 /* subroutine to lock the mailbox lock (which incidentally protects PCBs too) */
1794 
1795           if fnpp = null ()
1796           then do;
1797                code = error_table_$mpx_down;
1798                return;
1799           end;
1800 
1801           code = 0;
1802 
1803           lctep = fnp_info.lcte_ptr;
1804           if lcte.lock = pds$processid                      /* called as result of our own interrupt? */
1805           then if lcte.locked_for_interrupt
1806                then mylock = "1"b;                          /* remember not to unlock it */
1807                else call syserr (CRASH_SYSTEM, "fnp_multiplexer: mylock error");
1808           else do;
1809                mylock = "0"b;
1810                call tty_lock$lock_lcte (lctep, code);
1811                locked = (code = 0);
1812           end;
1813           return;
1814      end lock;
1815 
1816 
1817 
1818 unlock:
1819      proc;
1820 
1821 /* subroutine to release mailbox lock and process queued interrupts */
1822 
1823           if locked
1824           then if ^mylock
1825                then do;
1826 
1827                     call dn355$process_interrupt_queue ((fnp_info.fnp_number));
1828                     locked = "0"b;                          /* it unlocks the channel lock when it's done */
1829 
1830                end;
1831           return;
1832 
1833      end unlock;
1834 ^L
1835 
1836 fnp_buf_size:
1837      proc returns (bit (18));
1838 
1839 /* internal procedure returns correct buffer size for FNP to use, based on baud rate
1840    *  and synchronous/asynchronous
1841 */
1842 
1843           do i = 1 to n_sync_line_types while (pcb.line_type ^= sync_line_type (i));
1844           end;
1845 
1846           if i <= n_sync_line_types
1847           then do;
1848                pcb.sync_line = "1"b;
1849                chars_per_buf = divide (divide (pcb.baud_rate, 8, 17, 0), buf_per_second, 17, 0);
1850           end;
1851 
1852           else do;
1853                pcb.sync_line = "0"b;
1854                chars_per_buf = 56;                          /* always minimum for asynchronous */
1855           end;
1856 
1857           return (bit (bin (chars_per_buf, 18), 18));
1858      end fnp_buf_size;
1859 ^L
1860 
1861 /**** Wired entrypoints to talk to io_manager for both fnp_multiplexer
1862       and the fnp_util TandD code. These should be called under
1863       the FNP lcte lock. */
1864 
1865 declare  a_fnp_no fixed bin;
1866 declare  fnp_no fixed bin;
1867 
1868 assign:
1869      entry (a_fnp_no, a_code);
1870 
1871           infop = addr (dn355_data$);
1872           call TRACE ("assign");
1873           fnp_no = a_fnp_no;
1874           fnpp = addr (datanet_info.per_datanet (fnp_no));
1875           call assign_channel (code);
1876           a_code = code;
1877           return;
1878 ^L
1879 
1880 unassign:
1881      entry (a_fnp_no, a_code);
1882 
1883           fnp_no = a_fnp_no;
1884           infop = addr (dn355_data$);
1885           call TRACE ("unassign");
1886           fnpp = addr (datanet_info.per_datanet (fnp_no));
1887 
1888           call unassign_channel (code);
1889           a_code = code;
1890           return;
1891 
1892 
1893 assign_channel:
1894      procedure (code);
1895 declare  code fixed bin (35);
1896 
1897           code = 0;
1898           if ^fnp_info.available
1899           then do;
1900                code = error_table_$io_not_configured;       /* "not available" */
1901                go to assign_channel_return;
1902           end;
1903 
1904           if fnp_info.io_manager_assigned
1905           then do;
1906                code = error_table_$io_assigned;
1907                go to assign_channel_return;
1908           end;
1909 
1910           call io_manager$assign (fnp_info.io_manager_chx, fnp_info.io_chanid, dn355$interrupt, (fnp_info.fnp_number),
1911                (null ()), code);
1912           fnp_info.io_manager_assigned = (code = 0);
1913 assign_channel_return:
1914           if datanet_info.trace | datanet_info.debug_stop
1915           then call syserr (ANNOUNCE, "fnp_multiplexer$assign_channel: Assignment of FNP ^a ^[succeeded^;failed^].",
1916                     fnp_info.fnp_tag, (code = 0));
1917           if code ^= 0
1918           then call TRACE_ERROR ("assign_channel", code);
1919           return;
1920      end assign_channel;
1921 
1922 unassign_channel:
1923      procedure (code);
1924 declare  code fixed bin (35);
1925 
1926 
1927           if ^fnp_info.io_manager_assigned
1928           then do;
1929                code = error_table_$io_not_assigned;
1930                go to unassign_return;
1931           end;
1932           call io_manager$unassign (fnp_info.io_manager_chx, code);
1933           if code = 0
1934           then fnp_info.io_manager_assigned = "0"b;
1935 unassign_return:
1936           if datanet_info.trace | datanet_info.debug_stop
1937           then call syserr$error_code (ANNOUNCE, code,
1938                     "fnp_multiplexer$unassign_channel: Unassignment of FNP ^a ^[failed^;succeeded^].", fnp_info.fnp_tag,
1939                     (code ^= 0));
1940           if code ^= 0
1941           then call TRACE_ERROR ("unassign_channel", code);
1942           return;
1943      end unassign_channel;
1944 
1945 TRACE:
1946      procedure (Entry);
1947 
1948 declare  Entry char (32);
1949 
1950           if datanet_info.trace
1951           then call syserr (ANNOUNCE, "fnp_multiplexer$^a: Tracing call.", Entry);
1952           return;
1953 
1954 
1955 
1956 TRACE_ERROR:
1957      entry (Entry, Code);
1958 
1959 declare  Code fixed bin (35);
1960 
1961           if datanet_info.trace | datanet_info.debug_stop
1962           then call syserr$error_code (ANNOUNCE, Code, "fnp_multiplexer$^a: Tracing error.", Entry);
1963           if datanet_info.debug_stop
1964           then call syserr (CRASH_SYSTEM, "fnp_multiplexer: debugging stop (type go to continue).");
1965           return;
1966      end TRACE;
1967 ^L
1968 /* BEGIN MESSAGE DOCUMENTATION
1969 
1970    Message:
1971    fnp_multiplexer: patching FNP X for USER:
1972    ADDR from XXX to YYY
1973 
1974    S:     $info
1975 
1976    T:     $run
1977 
1978    M:     The memory of FNP X is being patched by the privileged
1979    user whose user_id is USER. ADDR is the absolute location in FNP memory that is being
1980    patched (in octal); XXX and YYY are the old and new values of the location
1981    respectively (also in octal).
1982    The second line may be repeated (with different values) if more than one word
1983    is being patched.
1984 
1985    A:     This information is for logging purposes.
1986 
1987 
1988    Message:
1989    fnp_multiplexer: mylock error
1990 
1991    S:     $crash
1992 
1993    T:     $run
1994 
1995    M:     An attempt has been made to lock an FNP channel lock to a process
1996    that already has it locked.
1997 
1998    A:     $inform
1999 
2000 
2001    Message:
2002    fnp_multiplexer: NAME order to FNP X timed out.
2003 
2004    S:     $info
2005 
2006    T:     $run
2007 
2008    M:     NAME is "get_meters", "dump_fnp", or "patch_fnp". The named order to
2009    FNP X failed to complete within 10 seconds. The buffer space associated
2010    with the order has been abandoned, and get_meters, dump, and patch orders
2011    to that FNP are disabled until the FNP is reloaded.
2012 
2013    A:     $inform
2014 
2015 
2016    Message:
2017    fnp_multiplexer: get_meters order for FNP X, line N, timed out.
2018 
2019    S:     $info
2020 
2021    T:     $run
2022 
2023    M:     A get_meters order for line N of FNP X failed to complete within 10
2024    seconds. The buffer space associated with the order has been abandoned, and
2025    get_meters, dump, and patch orders to that FNP are disabled until the FNP
2026    is reloaded.
2027 
2028 
2029    Message:
2030    fnp_multiplexer$shutdown: Failed to unwire fnp. ERROR.
2031 
2032    S:     $info
2033 
2034    T:     $run
2035 
2036    M:     An attempt to unwire the pages used for I/O to an FNP failed at FNP
2037    shutdown. ERROR contains the message derived from a standard system error
2038    code.
2039 
2040    A:     $inform
2041 
2042 
2043    Message:
2044    fnp_multiplexer$shutdown: Called with null fnp_ptr
2045 
2046    S:     $info
2047 
2048    T:     $run
2049 
2050    M:     A call was made to the shutdown entry with a null pointer to
2051    fnp_info. This message only appears if tracing is enabled for the specified
2052    FNP.
2053 
2054    A:     $inform
2055 
2056 
2057    Message:
2058    fnp_multiplexer$shutdown: Called with FNP wired.
2059 
2060    S:     $info
2061 
2062    T:     $run
2063 
2064    M:     A call was made to the shutdown entry while the pages for I/O for an
2065    FNP were still wired.
2066    This message only appears if tracing is enabled for the specified FNP.
2067 
2068    A:     none required.
2069 
2070 
2071    Message:
2072    fnp_multiplexer$assign_channel: Assignment of FNP X {succeeded | failed}.
2073 
2074    S:     $info
2075 
2076    T:     $run
2077 
2078    M:     Indicates the result of a call to io_manager$assign for FNP X.
2079    This message only appears if tracing is enabled for the specified FNP.
2080 
2081    A:     none required.
2082 
2083 
2084    Message:
2085    fnp_multiplexer$unassign_channel: Unassignment of FNP X {succeeded | failed}.
2086 
2087    S:     $info
2088 
2089    T:     $run
2090 
2091    M:     Indicates the result of a call to io_manager$unassign for FNP X.
2092    This message only appears if tracing is enabled for the specified FNP.
2093 
2094    A:     none required.
2095 
2096 
2097    Message:
2098    fnp_multiplexer$ENTRY: Tracing call.
2099 
2100    S:     $info
2101 
2102    T:     $run
2103 
2104    M:     A call was made to the ENTRY entry.
2105    This message only appears if tracing is enabled for the specified FNP.
2106 
2107    A:     none required.
2108 
2109 
2110    Message:
2111    fnp_multiplexer$ENTRY: Tracing error. ERROR.
2112 
2113    S:     $info
2114 
2115    T:     $run
2116 
2117    M:     The error code represented by ERROR was encountered by ENTRY.
2118    This message only appears if tracing is enabled for the specified FNP.
2119 
2120    A:     none required.
2121 
2122 
2123    Message:
2124    fnp_multiplexer: debugging stop (type go to continue).
2125 
2126    S:     $crash
2127 
2128    T:     $run
2129 
2130    M:     An error has been encountered in setting up an FNP, and debugging
2131    mode is turned on.
2132 
2133    A:     Use BCE commands to analyze the condition, if necessary; type "go"
2134    to resume system operation.
2135 
2136    END MESSAGE DOCUMENTATION */
2137 
2138 
2139 
2140      end fnp_multiplexer;