This source file includes following definitions.
- tmxr_init_line
- tmxr_report_connection
- tmxr_report_disconnection
- loop_write_ex
- loop_write
- loop_read_ex
- loop_read
- tmxr_read
- tmxr_write
- tmxr_rmvrc
- tmxr_find_ldsc
- tmxr_get_ldsc
- growstring
- tmxr_mux_attach_string
- tmxr_line_attach_string
- tmxr_poll_conn
- tmxr_reset_ln_ex
- tmxr_close_ln
- tmxr_reset_ln
- tmxr_set_get_modem_bits
- tmxr_set_line_loopback
- tmxr_get_line_loopback
- tmxr_set_line_halfduplex
- tmxr_get_line_halfduplex
- tmxr_input_pending_ln
- tmxr_getc_ln
- tmxr_get_packet_ln
- tmxr_get_packet_ln_ex
- tmxr_poll_rx
- tmxr_rqln_bare
- tmxr_rqln
- tmxr_putc_ln
- tmxr_put_packet_ln
- tmxr_put_packet_ln_ex
- tmxr_poll_tx
- tmxr_send_buffered_data
- tmxr_tqln
- tmxr_tpqln
- tmxr_tpbusyln
- _mux_detach_line
- tmxr_detach_ln
- _tmln_speed_delta
- tmxr_set_line_speed
- tmxr_open_master
- tmxr_set_line_unit
- tmxr_set_line_output_unit
- tmxr_start_poll
- tmxr_stop_poll
- tmxr_add_to_open_list
- _tmxr_remove_from_open_list
- _tmxr_locate_line_send_expect
- tmxr_locate_line_send
- tmxr_locate_line_expect
- tmxr_change_async
- tmxr_attach_ex
- tmxr_startup
- tmxr_shutdown
- tmxr_show_open_devices
- tmxr_close_master
- tmxr_detach
- tmxr_activate
- tmxr_activate_after
- tmxr_attach_help
- tmxr_ex
- tmxr_dep
- tmxr_msg
- tmxr_linemsg
- tmxr_linemsgf
- tmxr_linemsgvf
- tmxr_fconns
- tmxr_fstats
- tmxr_dscln
- tmxr_set_log
- tmxr_set_nolog
- tmxr_show_log
- tmxr_set_lnorder
- tmxr_show_lnorder
- tmxr_show_summ
- tmxr_show_cstat
- tmxr_show_lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93 #include "sim_defs.h"
94 #include "sim_sock.h"
95 #include "sim_timer.h"
96 #include "sim_tmxr.h"
97 #include "scp.h"
98
99 #define DBG_CTR 0
100
101 #include "../dps8/dps8.h"
102
103 #include <ctype.h>
104 #include <time.h>
105 #include <sys/time.h>
106 #include <signal.h>
107 #include <unistd.h>
108 #include <math.h>
109
110 #ifdef TESTING
111 # include "../dps8/dps8_cpu.h"
112 # undef realloc
113 # undef FREE
114 # define FREE(p) free(p)
115 # define realloc trealloc
116 #endif
117
118
119
120
121 #define TN_IAC 0xFFu
122 #define TN_DONT 0xFEu
123 #define TN_DO 0xFDu
124 #define TN_WONT 0xFCu
125 #define TN_WILL 0xFBu
126 #define TN_SB 0xFAu
127 #define TN_GA 0xF9u
128 #define TN_EL 0xF8u
129 #define TN_EC 0xF7u
130 #define TN_AYT 0xF6u
131 #define TN_AO 0xF5u
132 #define TN_IP 0xF4u
133 #define TN_BRK 0xF3u
134 #define TN_DATAMK 0xF2u
135 #define TN_NOP 0xF1u
136 #define TN_SE 0xF0u
137
138
139
140 #define TN_BIN 0
141 #define TN_ECHO 1
142 #define TN_SGA 3
143 #define TN_STATUS 5
144 #define TN_TIMING 6
145 #define TN_NAOCRD 10
146 #define TN_NAOHTS 11
147 #define TN_NAOHTD 12
148 #define TN_NAOFFD 13
149 #define TN_NAOVTS 14
150 #define TN_NAOVTD 15
151 #define TN_NAOLFD 16
152 #define TN_EXTEND 17
153 #define TN_LOGOUT 18
154 #define TN_BM 19
155 #define TN_DET 20
156 #define TN_SENDLO 23
157 #define TN_TERMTY 24
158 #define TN_ENDREC 25
159 #define TN_TUID 26
160 #define TN_OUTMRK 27
161 #define TN_TTYLOC 28
162 #define TN_3270 29
163 #define TN_X3PAD 30
164 #define TN_NAWS 31
165 #define TN_TERMSP 32
166 #define TN_TOGFLO 33
167 #define TN_LINE 34
168 #define TN_XDISPL 35
169 #define TN_ENVIRO 36
170 #define TN_AUTH 37
171 #define TN_ENCRYP 38
172 #define TN_NEWENV 39
173 #define TN_TN3270 40
174 #define TN_CHARST 42
175 #define TN_COMPRT 44
176 #define TN_KERMIT 47
177
178 #define TN_CR 015
179 #define TN_LF 012
180 #define TN_NUL 000
181
182
183
184 #define TNS_NORM 000
185 #define TNS_IAC 001
186 #define TNS_WILL 002
187 #define TNS_WONT 003
188 #define TNS_SKIP 004
189 #define TNS_CRPAD 005
190 #define TNS_DO 006
191
192
193
194 #define TNOS_DONT 001
195 #define TNOS_WONT 002
196
197 static BITFIELD tmxr_modem_bits[] = {
198 BIT(DTR),
199 BIT(RTS),
200 BIT(DCD),
201 BIT(RNG),
202 BIT(CTS),
203 BIT(DSR),
204 ENDBITS
205 };
206
207 static u_char mantra[] = {
208 TN_IAC, TN_WILL, TN_LINE,
209 TN_IAC, TN_WILL, TN_SGA,
210 TN_IAC, TN_WILL, TN_ECHO,
211 TN_IAC, TN_WILL, TN_BIN,
212 TN_IAC, TN_DO, TN_BIN
213 };
214
215 #define TMXR_GUARD ((sizeof(mantra)))
216
217
218
219 static void tmxr_add_to_open_list (TMXR* mux);
220
221
222
223
224
225
226
227
228
229
230
231
232 static void tmxr_init_line (TMLN *lp)
233 {
234 lp->tsta = 0;
235 lp->xmte = 1;
236 lp->dstb = 0;
237 lp->rxbpr = lp->rxbpi = lp->rxcnt = lp->rxpcnt = 0;
238 if (!lp->txbfd || lp->notelnet)
239 lp->txbpr = lp->txbpi = lp->txcnt = lp->txpcnt = 0;
240 lp->txdrp = 0;
241 tmxr_set_get_modem_bits (lp, 0, 0, NULL);
242 if ((!lp->mp->buffered) && (!lp->txbfd)) {
243 lp->txbfd = 0;
244 lp->txbsz = TMXR_MAXBUF;
245 lp->txb = (char *)realloc (lp->txb, lp->txbsz);
246 if (!lp->txb)
247 {
248 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
249 __func__, __FILE__, __LINE__);
250 #if defined(USE_BACKTRACE)
251 # ifdef SIGUSR2
252 (void)raise(SIGUSR2);
253
254 # endif
255 #endif
256 abort();
257 }
258 lp->rxbsz = TMXR_MAXBUF;
259 lp->rxb = (char *)realloc(lp->rxb, lp->rxbsz);
260 if (!lp->rxb)
261 {
262 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
263 __func__, __FILE__, __LINE__);
264 #if defined(USE_BACKTRACE)
265 # ifdef SIGUSR2
266 (void)raise(SIGUSR2);
267
268 # endif
269 #endif
270 abort();
271 }
272 lp->rbr = (char *)realloc(lp->rbr, lp->rxbsz);
273 if (!lp->rbr)
274 {
275 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
276 __func__, __FILE__, __LINE__);
277 #if defined(USE_BACKTRACE)
278 # ifdef SIGUSR2
279 (void)raise(SIGUSR2);
280
281 # endif
282 #endif
283 abort();
284 }
285 }
286 if (lp->loopback) {
287 lp->lpbsz = lp->rxbsz;
288 lp->lpb = (char *)realloc(lp->lpb, lp->lpbsz);
289 if (!lp->lpb)
290 {
291 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
292 __func__, __FILE__, __LINE__);
293 #if defined(USE_BACKTRACE)
294 # ifdef SIGUSR2
295 (void)raise(SIGUSR2);
296
297 # endif
298 #endif
299 abort();
300 }
301 lp->lpbcnt = lp->lpbpi = lp->lpbpr = 0;
302 }
303 if (lp->rxpb) {
304 lp->rxpboffset = lp->rxpbsize = 0;
305 FREE (lp->rxpb);
306 lp->rxpb = NULL;
307 }
308 if (lp->txpb) {
309 lp->txpbsize = lp->txppsize = lp->txppoffset = 0;
310 FREE (lp->txpb);
311 lp->txpb = NULL;
312 }
313 memset (lp->rbr, 0, lp->rxbsz);
314 return;
315 }
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330 static void tmxr_report_connection (TMXR *mp, TMLN *lp)
331 {
332 int32 unwritten, psave;
333 char cmsg[160];
334 char dmsg[80] = "";
335 char lmsg[80] = "";
336 char msgbuf[512] = "";
337
338 if ((!lp->notelnet) || (sim_switches & SWMASK ('V'))) {
339 sprintf (cmsg, "\n\r\nConnected to the %s simulator ", sim_name);
340
341 if (mp->dptr) {
342 sprintf (dmsg, "%s device",
343 sim_dname (mp->dptr));
344
345 if (mp->lines > 1)
346 sprintf (lmsg, ", line %d", (int)(lp-mp->ldsc));
347 }
348
349 sprintf (msgbuf, "%s%s%s\r\n\n", cmsg, dmsg, lmsg);
350 }
351
352 if (!mp->buffered) {
353 lp->txbpi = 0;
354 lp->txbpr = (int32)(lp->txbsz - strlen (msgbuf));
355 lp->rxcnt = lp->txcnt = lp->txdrp = 0;
356 lp->rxpcnt = lp->txpcnt = 0;
357 }
358 else
359 if (lp->txcnt > lp->txbsz)
360 lp->txbpr = (lp->txbpi + 1) % lp->txbsz;
361 else
362 lp->txbpr = (int32)(lp->txbsz - strlen (msgbuf));
363
364 psave = lp->txbpi;
365 lp->txbpi = lp->txbpr;
366 tmxr_linemsg (lp, msgbuf);
367 lp->txbpi = psave;
368
369 unwritten = tmxr_send_buffered_data (lp);
370
371 if (unwritten == 0)
372 lp->xmte = 1;
373
374 lp->txcnt -= (int32)strlen (msgbuf);
375 return;
376 }
377
378
379
380
381
382
383
384
385
386
387
388 static void tmxr_report_disconnection (TMLN *lp)
389 {
390 if (lp->notelnet)
391 return;
392 tmxr_linemsgf (lp, "\r\nDisconnected from the %s simulator\r\n\n", sim_name);
393 return;
394 }
395
396 static int32 loop_write_ex (TMLN *lp, char *buf, int32 length, t_bool prefix_datagram)
397 {
398 int32 written = 0;
399 int32 loopfree = lp->lpbsz - lp->lpbcnt;
400
401 if (lp->datagram && prefix_datagram) {
402 if ((size_t)loopfree < (size_t)(length + sizeof(length)))
403 return written;
404 loop_write_ex (lp, (char *)&length, sizeof(length), FALSE);
405 }
406 while (length) {
407 int32 chunksize;
408
409 loopfree = lp->lpbsz - lp->lpbcnt;
410 if (loopfree == 0)
411 break;
412 if (loopfree < length)
413 length = loopfree;
414 if (lp->lpbpi >= lp->lpbpr)
415 chunksize = lp->lpbsz - lp->lpbpi;
416 else
417 chunksize = lp->lpbpr - lp->lpbpi;
418 if (chunksize > length)
419 chunksize = length;
420 memcpy (&lp->lpb[lp->lpbpi], buf, chunksize);
421 buf += chunksize;
422 length -= chunksize;
423 written += chunksize;
424 lp->lpbpi = (lp->lpbpi + chunksize) % lp->lpbsz;
425 }
426 lp->lpbcnt += written;
427 return written;
428 }
429
430 static int32 loop_write (TMLN *lp, char *buf, int32 length)
431 {
432 return loop_write_ex (lp, buf, length, TRUE);
433 }
434
435 static int32 loop_read_ex (TMLN *lp, char *buf, int32 bufsize)
436 {
437 int32 bytesread = 0;
438
439 while (bufsize > 0) {
440 int32 chunksize;
441 int32 loopused = lp->lpbcnt;
442
443 if (loopused < bufsize)
444 bufsize = loopused;
445 if (loopused == 0)
446 break;
447 if (lp->lpbpi > lp->lpbpr)
448 chunksize = lp->lpbpi - lp->lpbpr;
449 else
450 chunksize = lp->lpbsz - lp->lpbpr;
451 if (chunksize > bufsize)
452 chunksize = bufsize;
453 memcpy (buf, &lp->lpb[lp->lpbpr], chunksize);
454 buf += chunksize;
455 bufsize -= chunksize;
456 bytesread += chunksize;
457 lp->lpbpr = (lp->lpbpr + chunksize) % lp->lpbsz;
458 }
459 lp->lpbcnt -= bytesread;
460 return bytesread;
461 }
462
463 static int32 loop_read (TMLN *lp, char *buf, int32 bufsize)
464 {
465 if (lp->datagram) {
466 int32 pktsize;
467
468 if (lp->lpbcnt < (int32)sizeof(pktsize))
469 return 0;
470 if ((sizeof(pktsize) != loop_read_ex (lp, (char *)&pktsize, sizeof(pktsize))) ||
471 (pktsize > bufsize))
472 return -1;
473 bufsize = pktsize;
474 }
475 return loop_read_ex (lp, buf, bufsize);
476 }
477
478
479
480
481
482
483
484
485
486 static int32 tmxr_read (TMLN *lp, int32 length)
487 {
488 int32 i = lp->rxbpi;
489
490 if (lp->loopback)
491 return loop_read (lp, &(lp->rxb[i]), length);
492 else
493 return sim_read_sock (lp->sock, &(lp->rxb[i]), length);
494 }
495
496
497
498
499
500
501
502
503 static int32 tmxr_write (TMLN *lp, int32 length)
504 {
505 int32 written;
506 int32 i = lp->txbpr;
507
508 if (lp->loopback)
509 return loop_write (lp, &(lp->txb[i]), length);
510
511 written = sim_write_sock (lp->sock, &(lp->txb[i]), length);
512
513 if (written == SOCKET_ERROR)
514 if (lp->datagram)
515 return written;
516 else
517 return -1;
518 else
519 return written;
520 }
521
522
523
524
525
526
527
528
529 static void tmxr_rmvrc (TMLN *lp, int32 p)
530 {
531 for ( ; p < lp->rxbpi; p++) {
532 lp->rxb[p] = lp->rxb[p + 1];
533 lp->rbr[p] = lp->rbr[p + 1];
534 }
535
536 lp->rbr[p] = 0;
537 lp->rxbpi = lp->rxbpi - 1;
538 return;
539 }
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556 static TMLN *tmxr_find_ldsc (UNIT *uptr, int32 val, const TMXR *mp)
557 {
558 if (mp == NULL)
559 return NULL;
560 if (uptr) {
561 DEVICE *dptr = find_dev_from_unit (uptr);
562 if (dptr == NULL)
563 return NULL;
564 val = (int32) (uptr - dptr->units);
565 }
566 if ((val < 0) || (val >= mp->lines))
567 return NULL;
568 return mp->ldsc + val;
569 }
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587 static TMLN *tmxr_get_ldsc (UNIT *uptr, const char *cptr, TMXR *mp, t_stat *status)
588 {
589 t_value ln;
590 TMLN *lp = NULL;
591 t_stat code = SCPE_OK;
592
593 if (mp == NULL)
594 code = SCPE_IERR;
595
596 else if (uptr) {
597 lp = tmxr_find_ldsc (uptr, mp->lines, mp);
598
599 if (lp == NULL)
600 code = SCPE_IERR;
601 }
602
603 else if (cptr == NULL)
604 code = SCPE_MISVAL;
605
606 else {
607 ln = get_uint (cptr, 10, mp->lines - 1, &code);
608
609 if (code == SCPE_OK)
610 lp = mp->ldsc + (int32) ln;
611 }
612
613 if (status)
614 *status = code;
615
616 return lp;
617 }
618
619
620
621
622
623
624
625
626
627
628
629 static char *growstring(char **string, size_t growth)
630 {
631 if (!*string)
632 {
633 fprintf(stderr, "\rFATAL: Bugcheck! Aborting at %s[%s:%d]\r\n",
634 __func__, __FILE__, __LINE__);
635 #if defined(USE_BACKTRACE)
636 # ifdef SIGUSR2
637 (void)raise(SIGUSR2);
638
639 # endif
640 #endif
641 abort();
642 }
643 *string = (char *)realloc (*string, 1 + (*string ? strlen (*string) : 0) + growth);
644 if (!*string)
645 {
646 fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
647 __func__, __FILE__, __LINE__);
648 #if defined(USE_BACKTRACE)
649 # ifdef SIGUSR2
650 (void)raise(SIGUSR2);
651
652 # endif
653 #endif
654 abort();
655 }
656 return *string + strlen(*string);
657 }
658
659 static char *tmxr_mux_attach_string(char *old, TMXR *mp)
660 {
661 char* tptr = NULL;
662 int32 i;
663 TMLN *lp;
664
665 FREE (old);
666 tptr = (char *) calloc (1, 1);
667 if (tptr == NULL)
668 return tptr;
669
670 if (mp->port)
671 sprintf (growstring(&tptr, 33 + strlen (mp->port)), "%s%s", mp->port, mp->notelnet ? ";notelnet" : "");
672 if (mp->logfiletmpl[0])
673 sprintf (growstring(&tptr, 7 + strlen (mp->logfiletmpl)), ",Log=%s", mp->logfiletmpl);
674 while ((*tptr == ',') || (*tptr == ' '))
675 memcpy (tptr, tptr+1, strlen(tptr+1)+1);
676 for (i=0; i<mp->lines; ++i) {
677 char *lptr;
678 lp = mp->ldsc + i;
679
680 lptr = tmxr_line_attach_string(lp);
681 if (lptr) {
682 sprintf (growstring(&tptr, 10+strlen(lptr)), "%s%s", *tptr ? "," : "", lptr);
683 FREE (lptr);
684 }
685 }
686 if (mp->lines == 1)
687 while ((*tptr == ',') || (*tptr == ' '))
688 memcpy (tptr, tptr+1, strlen(tptr+1)+1);
689 if (*tptr == '\0') {
690 FREE (tptr);
691 tptr = NULL;
692 }
693 return tptr;
694 }
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711 char *tmxr_line_attach_string(TMLN *lp)
712 {
713 char* tptr = NULL;
714
715 tptr = (char *) calloc (1, 1);
716 if (tptr == NULL)
717 return tptr;
718
719 if (lp->destination || lp->port || lp->txlogname) {
720 if ((lp->mp->lines > 1) || (lp->port))
721 sprintf (growstring(&tptr, 32), "Line=%d", (int)(lp-lp->mp->ldsc));
722 if (lp->modem_control != lp->mp->modem_control)
723 sprintf (growstring(&tptr, 32), ",%s", lp->modem_control ? "Modem" : "NoModem");
724 if (lp->txbfd && (lp->txbsz != lp->mp->buffered))
725 sprintf (growstring(&tptr, 32), ",Buffered=%d", lp->txbsz);
726 if (!lp->txbfd && (lp->mp->buffered > 0))
727 sprintf (growstring(&tptr, 32), ",UnBuffered");
728 if (lp->mp->datagram != lp->datagram)
729 sprintf (growstring(&tptr, 8), ",%s", lp->datagram ? "UDP" : "TCP");
730 if (lp->mp->packet != lp->packet)
731 sprintf (growstring(&tptr, 8), ",Packet");
732 if (lp->port)
733 sprintf (growstring(&tptr, 32 + strlen (lp->port)), ",%s%s", lp->port, ((lp->mp->notelnet != lp->notelnet) && (!lp->datagram)) ? (lp->notelnet ? ";notelnet" : ";telnet") : "");
734 if (lp->destination) {
735 sprintf (growstring(&tptr, 25 + strlen (lp->destination)), ",Connect=%s%s", lp->destination, ((lp->mp->notelnet != lp->notelnet) && (!lp->datagram)) ? (lp->notelnet ? ";notelnet" : ";telnet") : "");
736 }
737 if (lp->txlogname)
738 sprintf (growstring(&tptr, 12 + strlen (lp->txlogname)), ",Log=%s", lp->txlogname);
739 if (lp->loopback)
740 sprintf (growstring(&tptr, 12 ), ",Loopback");
741 }
742 if (*tptr == '\0') {
743 FREE (tptr);
744 tptr = NULL;
745 }
746 return tptr;
747 }
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763 int32 tmxr_poll_conn (TMXR *mp)
764 {
765 SOCKET newsock;
766 TMLN *lp = NULL;
767 int32 *op;
768 int32 i, j;
769 int st1ret;
770 char *address;
771 char msg[512];
772 uint32 poll_time = sim_os_msec ();
773 struct timespec ts;
774
775 (void)lp;
776 memset (msg, 0, sizeof (msg));
777 if (mp->last_poll_time == 0) {
778 UNIT *uptr = mp->uptr;
779
780 if (!uptr)
781 return -1;
782
783 if (mp->poll_interval == 0)
784 mp->poll_interval = TMXR_DEFAULT_CONNECT_POLL_INTERVAL;
785
786 if (!(uptr->dynflags & TMUF_NOASYNCH)) {
787 uptr->dynflags |= UNIT_TM_POLL;
788 sim_cancel (uptr);
789 }
790 for (i=0; i < mp->lines; i++) {
791 uptr = mp->ldsc[i].uptr ? mp->ldsc[i].uptr : mp->uptr;
792
793 if (!(mp->uptr->dynflags & TMUF_NOASYNCH)) {
794 uptr->dynflags |= UNIT_TM_POLL;
795 sim_cancel (uptr);
796 }
797 }
798 }
799
800 if ((poll_time - mp->last_poll_time) < mp->poll_interval*1000)
801 return -1;
802
803 #ifdef USE_MONOTONIC
804 st1ret = clock_gettime(CLOCK_MONOTONIC, &ts);
805 #else
806 st1ret = clock_gettime(CLOCK_REALTIME, &ts);
807 #endif
808 if (st1ret != 0)
809 {
810 fprintf (stderr, "\rFATAL: clock_gettime failure! Aborting at %s[%s:%d]\r\n",
811 __func__, __FILE__, __LINE__);
812 #if defined(USE_BACKTRACE)
813 # ifdef SIGUSR2
814 (void)raise(SIGUSR2);
815
816 # endif
817 #endif
818 abort();
819 }
820
821 srand((unsigned int)((poll_time + getpid()) ^ (long)((1LL + (long long)ts.tv_nsec) * (1LL + (long long)ts.tv_sec))));
822
823 mp->last_poll_time = poll_time;
824
825
826
827 if (mp->master) {
828 if (mp->ring_sock != INVALID_SOCKET) {
829 newsock = mp->ring_sock;
830 mp->ring_sock = INVALID_SOCKET;
831 address = mp->ring_ipad;
832 mp->ring_ipad = NULL;
833 }
834 else
835 newsock = sim_accept_conn_ex (mp->master, &address, (mp->packet ? SIM_SOCK_OPT_NODELAY : 0));
836
837 if (newsock != INVALID_SOCKET) {
838 snprintf (msg, sizeof (msg)-1, "tmxr_poll_conn() - Connection from %s", address);
839 op = mp->lnorder;
840 i = mp->lines;
841 ++mp->sessions;
842
843 for (j = 0; j < mp->lines; j++, i++) {
844 if (op && (*op >= 0) && (*op < mp->lines))
845 i = *op++;
846 else
847 i = j;
848
849 lp = mp->ldsc + i;
850 if ((lp->conn == FALSE) &&
851 (lp->destination == NULL) &&
852 (lp->master == 0) &&
853 (lp->ser_connect_pending == FALSE) &&
854 (lp->modem_control ? ((lp->modembits & TMXR_MDM_DTR) != 0) : TRUE))
855 break;
856 }
857
858 if (i >= mp->lines) {
859 int32 ringable_count = 0;
860
861 for (j = 0; j < mp->lines; j++, i++) {
862 lp = mp->ldsc + j;
863 if ((lp->conn == FALSE) &&
864 (lp->destination == NULL) &&
865 (lp->master == 0) &&
866 (lp->ser_connect_pending == FALSE) &&
867 ((lp->modembits & TMXR_MDM_DTR) == 0)) {
868 ++ringable_count;
869 tmxr_set_get_modem_bits (lp, TMXR_MDM_RNG, 0, NULL);
870 }
871 }
872 if (ringable_count > 0) {
873 if (mp->ring_start_time == 0) {
874 mp->ring_start_time = poll_time;
875 mp->ring_sock = newsock;
876 mp->ring_ipad = address;
877 }
878 else {
879 if ((poll_time - mp->ring_start_time) < TMXR_MODEM_RING_TIME*1000) {
880 mp->ring_sock = newsock;
881 mp->ring_ipad = address;
882 }
883 else {
884 int ln;
885
886
887 for (ln = 0; ln < lp->mp->lines; ln++) {
888 TMLN *tlp = lp->mp->ldsc + ln;
889 if (((tlp->destination == NULL) && (tlp->master == 0)) &&
890 (tlp->modembits & TMXR_MDM_RNG) && (tlp->conn == FALSE))
891 tlp->modembits &= ~TMXR_MDM_RNG;
892 }
893 mp->ring_start_time = 0;
894 tmxr_msg (newsock, "No answer on any connection\r\n");
895 sim_close_sock (newsock);
896 FREE (address);
897 }
898 }
899 }
900 else {
901 tmxr_msg (newsock, "All connections busy\r\n");
902 sim_close_sock (newsock);
903 FREE (address);
904 }
905 }
906 else {
907 lp = mp->ldsc + i;
908 lp->conn = TRUE;
909 lp->sock = newsock;
910 lp->ipad = address;
911 tmxr_init_line (lp);
912 lp->notelnet = mp->notelnet;
913 if (!lp->notelnet) {
914 sim_write_sock (newsock, (char *)mantra, sizeof(mantra));
915 lp->telnet_sent_opts = (uint8 *)realloc (lp->telnet_sent_opts, 256);
916 if (!lp->telnet_sent_opts)
917 {
918 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
919 __func__, __FILE__, __LINE__);
920 #if defined(USE_BACKTRACE)
921 # ifdef SIGUSR2
922 (void)raise(SIGUSR2);
923
924 # endif
925 #endif
926 abort();
927 }
928 memset (lp->telnet_sent_opts, 0, 256);
929 }
930 tmxr_report_connection (mp, lp);
931 lp->cnms = sim_os_msec ();
932 return i;
933 }
934 }
935 }
936
937
938 for (i = 0; i < mp->lines; i++) {
939 int j, r = rand();
940 lp = mp->ldsc + i;
941
942 if (lp->ser_connect_pending) {
943 lp->ser_connect_pending = FALSE;
944 lp->conn = TRUE;
945 return i;
946 }
947
948
949
950 if (lp->loopback)
951 continue;
952
953
954
955
956
957
958
959
960
961
962 for (j=0; j<2; j++)
963 switch (((unsigned)j+(unsigned)r)&1) {
964 case 0:
965 if (lp->connecting) {
966 char *sockname, *peername;
967
968 switch (sim_check_conn(lp->connecting, FALSE))
969 {
970 case 1:
971 lp->conn = TRUE;
972 lp->sock = lp->connecting;
973 lp->connecting = 0;
974 int lpdlen = 1;
975 if (lp->destination != NULL)
976 lpdlen = 1+strlen (lp->destination);
977 lp->ipad = (char *)realloc (lp->ipad, lpdlen);
978 if (!lp->ipad)
979 {
980 fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
981 __func__, __FILE__, __LINE__);
982 #if defined(USE_BACKTRACE)
983 # ifdef SIGUSR2
984 (void)raise(SIGUSR2);
985
986 # endif
987 #endif
988 abort();
989 }
990 if (lp->destination != NULL)
991 strcpy (lp->ipad, lp->destination);
992 lp->cnms = sim_os_msec ();
993 sim_getnames_sock (lp->sock, &sockname, &peername);
994 if (lp->destination)
995 snprintf (msg, sizeof(msg)-1, "tmxr_poll_conn() - Outgoing Line Connection to %s (%s->%s) established", lp->destination, sockname, peername);
996 FREE (sockname);
997 FREE (peername);
998 return i;
999 case -1:
1000 snprintf (msg, sizeof(msg)-1, "tmxr_poll_conn() - Outgoing Line Connection to %s failed", lp->destination);
1001 tmxr_reset_ln (lp);
1002 break;
1003 }
1004 }
1005 break;
1006 case 1:
1007 if (lp->master) {
1008 while (INVALID_SOCKET != (newsock = sim_accept_conn_ex (lp->master, &address, (lp->packet ? SIM_SOCK_OPT_NODELAY : 0)))) {
1009 char *sockname, *peername;
1010
1011 sim_getnames_sock (newsock, &sockname, &peername);
1012 snprintf (msg, sizeof(msg)-1, "tmxr_poll_conn() - Incoming Line Connection from %s (%s->%s)", address, peername, sockname);
1013 FREE (sockname);
1014 FREE (peername);
1015 ++mp->sessions;
1016
1017 if (lp->destination) {
1018 char host[sizeof(msg) - 64];
1019
1020 if (sim_parse_addr (lp->destination, host, sizeof(host), NULL, NULL, 0, NULL, address)) {
1021 tmxr_msg (newsock, "Rejecting connection from unexpected source\r\n");
1022 snprintf (msg, sizeof(msg)-1, "tmxr_poll_conn() - Rejecting line connection from: %s, Expected: %s", address, host);
1023 sim_close_sock (newsock);
1024 FREE (address);
1025 continue;
1026 }
1027 if (lp->connecting) {
1028 snprintf (msg, sizeof(msg)-1, "tmxr_poll_conn() - aborting outgoing line connection attempt to: %s", lp->destination);
1029 sim_close_sock (lp->connecting);
1030 lp->connecting = 0;
1031 }
1032 }
1033 if (lp->conn == FALSE) {
1034 if ((!lp->modem_control) || (lp->modembits & TMXR_MDM_DTR)) {
1035 lp->conn = TRUE;
1036 lp->sock = newsock;
1037 lp->ipad = address;
1038 tmxr_init_line (lp);
1039 if (!lp->notelnet) {
1040 sim_write_sock (newsock, (char *)mantra, sizeof(mantra));
1041 lp->telnet_sent_opts = (uint8 *)realloc (lp->telnet_sent_opts, 256);
1042 if (!lp->telnet_sent_opts)
1043 {
1044 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1045 __func__, __FILE__, __LINE__);
1046 #if defined(USE_BACKTRACE)
1047 # ifdef SIGUSR2
1048 (void)raise(SIGUSR2);
1049
1050 # endif
1051 #endif
1052 abort();
1053 }
1054 memset (lp->telnet_sent_opts, 0, 256);
1055 }
1056 tmxr_report_connection (mp, lp);
1057 lp->cnms = sim_os_msec ();
1058 return i;
1059 }
1060 else {
1061 tmxr_msg (newsock, "Line connection not available\r\n");
1062 sim_close_sock (newsock);
1063 FREE (address);
1064 }
1065 }
1066 else {
1067 tmxr_msg (newsock, "Line connection busy\r\n");
1068 sim_close_sock (newsock);
1069 FREE (address);
1070 }
1071 }
1072 }
1073 break;
1074 }
1075
1076
1077
1078 if (lp->destination && (!lp->sock) && (!lp->connecting) &&
1079 (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR))) {
1080 snprintf (msg, sizeof(msg)-1, "tmxr_poll_conn() - establishing outgoing connection to: %s", lp->destination);
1081 lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL, lp->destination, "localhost", NULL, (lp->datagram ? SIM_SOCK_OPT_DATAGRAM : 0) |
1082 (lp->mp->packet ? SIM_SOCK_OPT_NODELAY : 0) |
1083 SIM_SOCK_OPT_BLOCKING);
1084 }
1085
1086 }
1087
1088 return -1;
1089 }
1090
1091
1092
1093 static t_stat tmxr_reset_ln_ex (TMLN *lp, t_bool closeserial)
1094 {
1095 char msg[512];
1096
1097 if (lp->txlog)
1098 fflush (lp->txlog);
1099
1100 tmxr_send_buffered_data (lp);
1101
1102 snprintf (msg, sizeof(msg)-1, "tmxr_reset_ln_ex(%s)", closeserial ? "TRUE" : "FALSE");
1103
1104 if (lp->sock) {
1105 sim_close_sock (lp->sock);
1106 FREE (lp->telnet_sent_opts);
1107 lp->telnet_sent_opts = NULL;
1108 lp->sock = 0;
1109 lp->conn = FALSE;
1110 lp->cnms = 0;
1111 lp->xmte = 1;
1112 }
1113 FREE(lp->ipad);
1114 lp->ipad = NULL;
1115 if ((lp->destination)
1116 ) {
1117 if (lp->connecting) {
1118 sim_close_sock (lp->connecting);
1119 lp->connecting = 0;
1120 }
1121 if ((!lp->modem_control) || (lp->modembits & TMXR_MDM_DTR)) {
1122 snprintf (msg, sizeof(msg)-1, "tmxr_reset_ln_ex() - connecting to %s", lp->destination);
1123 lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL, lp->destination, "localhost", NULL, (lp->datagram ? SIM_SOCK_OPT_DATAGRAM : 0) |
1124 (lp->packet ? SIM_SOCK_OPT_NODELAY : 0) |
1125 SIM_SOCK_OPT_BLOCKING);
1126 }
1127 }
1128 tmxr_init_line (lp);
1129 return SCPE_OK;
1130 }
1131
1132 t_stat tmxr_close_ln (TMLN *lp)
1133 {
1134 return tmxr_reset_ln_ex (lp, TRUE);
1135 }
1136
1137 t_stat tmxr_reset_ln (TMLN *lp)
1138 {
1139 return tmxr_reset_ln_ex (lp, FALSE);
1140 }
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154 t_stat tmxr_set_get_modem_bits (TMLN *lp, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits)
1155 {
1156 int32 before_modem_bits, incoming_state;
1157 DEVICE *dptr;
1158
1159 if ((bits_to_set & ~(TMXR_MDM_OUTGOING)) ||
1160 (bits_to_clear & ~(TMXR_MDM_OUTGOING)) ||
1161 (bits_to_set & bits_to_clear))
1162 return SCPE_ARG;
1163 before_modem_bits = lp->modembits;
1164 lp->modembits |= bits_to_set;
1165 lp->modembits &= ~(bits_to_clear | TMXR_MDM_INCOMING);
1166 if ((lp->sock)
1167 || (lp->loopback)) {
1168 if (lp->modembits & TMXR_MDM_DTR) {
1169 incoming_state = TMXR_MDM_DSR;
1170 if (lp->modembits & TMXR_MDM_RTS)
1171 incoming_state |= TMXR_MDM_CTS;
1172 if (lp->halfduplex) {
1173 if (incoming_state & TMXR_MDM_CTS)
1174 incoming_state |= TMXR_MDM_DCD;
1175 }
1176 else
1177 incoming_state |= TMXR_MDM_DCD;
1178 }
1179 else
1180 incoming_state = TMXR_MDM_DCD | TMXR_MDM_DSR | ((lp->modembits & TMXR_MDM_DTR) ? 0 : TMXR_MDM_RNG);
1181 }
1182 else {
1183 if (((before_modem_bits & TMXR_MDM_DTR) == 0) &&
1184 ((lp->modembits & TMXR_MDM_DTR) != 0) &&
1185 (lp->conn == FALSE) &&
1186 (lp->modembits & TMXR_MDM_RNG)) {
1187 if ((lp->destination == NULL) &&
1188 (lp->master == 0) &&
1189 (lp->mp && (lp->mp->ring_sock))) {
1190 int ln;
1191
1192 lp->conn = TRUE;
1193 lp->sock = lp->mp->ring_sock;
1194 lp->mp->ring_sock = INVALID_SOCKET;
1195 lp->ipad = lp->mp->ring_ipad;
1196 lp->mp->ring_ipad = NULL;
1197 lp->mp->ring_start_time = 0;
1198 tmxr_init_line (lp);
1199 lp->notelnet = lp->mp->notelnet;
1200 if (!lp->notelnet) {
1201 sim_write_sock (lp->sock, (char *)mantra, sizeof(mantra));
1202 lp->telnet_sent_opts = (uint8 *)realloc (lp->telnet_sent_opts, 256);
1203 if (!lp->telnet_sent_opts)
1204 {
1205 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1206 __func__, __FILE__, __LINE__);
1207 #if defined(USE_BACKTRACE)
1208 # ifdef SIGUSR2
1209 (void)raise(SIGUSR2);
1210
1211 # endif
1212 #endif
1213 abort();
1214 }
1215 memset (lp->telnet_sent_opts, 0, 256);
1216 }
1217 tmxr_report_connection (lp->mp, lp);
1218 lp->cnms = sim_os_msec ();
1219 lp->modembits &= ~TMXR_MDM_RNG;
1220
1221 for (ln = 0; ln < lp->mp->lines; ln++) {
1222 TMLN *tlp = lp->mp->ldsc + ln;
1223 if (((tlp->destination == NULL) && (tlp->master == 0)) &&
1224 (tlp->modembits & TMXR_MDM_RNG) && (tlp->conn == FALSE))
1225 tlp->modembits &= ~TMXR_MDM_RNG;
1226 }
1227 }
1228 }
1229 if ((lp->master) || (lp->mp && lp->mp->master) ||
1230 (lp->port && lp->destination))
1231 incoming_state = TMXR_MDM_DSR;
1232 else
1233 incoming_state = 0;
1234 }
1235 lp->modembits |= incoming_state;
1236 dptr = (lp->dptr ? lp->dptr : (lp->mp ? lp->mp->dptr : NULL));
1237 if ((lp->modembits != before_modem_bits) && (sim_deb && lp->mp && dptr)) {
1238 sim_debug_bits (TMXR_DBG_MDM, dptr, tmxr_modem_bits, before_modem_bits, lp->modembits, FALSE);
1239 sim_debug (TMXR_DBG_MDM, dptr, " - Line %d - %p\n", (int)(lp-lp->mp->ldsc), lp->txb);
1240 }
1241 if (incoming_bits)
1242 *incoming_bits = lp->modembits;
1243 if (lp->mp && lp->modem_control) {
1244 if (bits_to_set | bits_to_clear) {
1245 if (lp->loopback) {
1246 if ((lp->modembits ^ before_modem_bits) & TMXR_MDM_DTR) {
1247 lp->ser_connect_pending = (lp->modembits & TMXR_MDM_DTR);
1248 lp->conn = !(lp->modembits & TMXR_MDM_DTR);
1249 }
1250 return SCPE_OK;
1251 }
1252 if ((lp->sock) || (lp->connecting)) {
1253 if ((before_modem_bits & bits_to_clear & TMXR_MDM_DTR) != 0) {
1254 if (lp->sock)
1255 tmxr_report_disconnection (lp);
1256 tmxr_reset_ln (lp);
1257 }
1258 }
1259 else {
1260 if ((lp->destination) &&
1261 (bits_to_set & ~before_modem_bits &
1262 TMXR_MDM_DTR)) {
1263 char msg[512];
1264
1265 snprintf (msg, sizeof(msg)-1, "tmxr_set_get_modem_bits() - establishing outgoing connection to: %s", lp->destination);
1266 lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL, lp->destination, "localhost", NULL, (lp->datagram ? SIM_SOCK_OPT_DATAGRAM : 0) |
1267 (lp->packet ? SIM_SOCK_OPT_NODELAY : 0) |
1268 SIM_SOCK_OPT_BLOCKING);
1269 }
1270 }
1271 }
1272 return SCPE_OK;
1273 }
1274 if ((lp->sock) || (lp->connecting)) {
1275 if ((before_modem_bits & bits_to_clear & TMXR_MDM_DTR) != 0) {
1276 if (lp->sock)
1277 tmxr_report_disconnection (lp);
1278 tmxr_reset_ln (lp);
1279 }
1280 }
1281 return SCPE_INCOMP;
1282 }
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293 t_stat tmxr_set_line_loopback (TMLN *lp, t_bool enable_loopback)
1294 {
1295 if (lp->loopback == (enable_loopback != FALSE))
1296 return SCPE_OK;
1297 lp->loopback = (enable_loopback != FALSE);
1298 if (lp->loopback) {
1299 lp->lpbsz = lp->rxbsz;
1300 lp->lpb = (char *)realloc(lp->lpb, lp->lpbsz);
1301 if (!lp->lpb)
1302 {
1303 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1304 __func__, __FILE__, __LINE__);
1305 #if defined(USE_BACKTRACE)
1306 # ifdef SIGUSR2
1307 (void)raise(SIGUSR2);
1308
1309 # endif
1310 #endif
1311 abort();
1312 }
1313 lp->lpbcnt = lp->lpbpi = lp->lpbpr = 0;
1314 if (!lp->conn)
1315 lp->ser_connect_pending = TRUE;
1316 }
1317 else {
1318 FREE (lp->lpb);
1319 lp->lpb = NULL;
1320 lp->lpbsz = 0;
1321 }
1322 return SCPE_OK;
1323 }
1324
1325 t_bool tmxr_get_line_loopback (TMLN *lp)
1326 {
1327 return (lp->loopback != FALSE);
1328 }
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344 t_stat tmxr_set_line_halfduplex (TMLN *lp, t_bool enable_halfduplex)
1345 {
1346 if (lp->halfduplex == (enable_halfduplex != FALSE))
1347 return SCPE_OK;
1348 lp->halfduplex = (enable_halfduplex != FALSE);
1349 return SCPE_OK;
1350 }
1351
1352 t_bool tmxr_get_line_halfduplex (TMLN *lp)
1353 {
1354 return (lp->halfduplex != FALSE);
1355 }
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371 int32 tmxr_input_pending_ln (TMLN *lp)
1372 {
1373 return (lp->rxbpi - lp->rxbpr);
1374 }
1375
1376 int32 tmxr_getc_ln (TMLN *lp)
1377 {
1378 int32 j;
1379 t_stat val = 0;
1380 uint32 tmp;
1381
1382 if ((lp->conn && lp->rcve) &&
1383 ((!lp->rxbps) ||
1384 (sim_gtime () >= lp->rxnexttime))) {
1385 if (!sim_send_poll_data (&lp->send, &val)) {
1386 j = lp->rxbpi - lp->rxbpr;
1387 if (j) {
1388 tmp = lp->rxb[lp->rxbpr];
1389 val = TMXR_VALID | (tmp & 0377);
1390 if (lp->rbr[lp->rxbpr]) {
1391 lp->rbr[lp->rxbpr] = 0;
1392 val = val | SCPE_BREAK;
1393 }
1394 lp->rxbpr = lp->rxbpr + 1;
1395 }
1396 }
1397 }
1398 if (lp->rxbpi == lp->rxbpr)
1399 lp->rxbpi = lp->rxbpr = 0;
1400 if (lp->rxbps) {
1401 if (val)
1402 lp->rxnexttime = floor (sim_gtime () + ((lp->rxdelta * sim_timer_inst_per_sec ())/lp->rxbpsfactor));
1403 }
1404 return val;
1405 }
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426 t_stat tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, size_t *psize)
1427 {
1428 return tmxr_get_packet_ln_ex (lp, pbuf, psize, 0);
1429 }
1430
1431 t_stat tmxr_get_packet_ln_ex (TMLN *lp, const uint8 **pbuf, size_t *psize, uint8 frame_byte)
1432 {
1433 int32 c;
1434 size_t pktsize;
1435 size_t fc_size = (frame_byte ? 1 : 0);
1436
1437 while (TMXR_VALID & (c = tmxr_getc_ln (lp))) {
1438 if (lp->rxpboffset + 3 > lp->rxpbsize) {
1439 lp->rxpbsize += 512;
1440 lp->rxpb = (uint8 *)realloc (lp->rxpb, lp->rxpbsize);
1441 if (!lp->rxpb)
1442 {
1443 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1444 __func__, __FILE__, __LINE__);
1445 #if defined(USE_BACKTRACE)
1446 # ifdef SIGUSR2
1447 (void)raise(SIGUSR2);
1448
1449 # endif
1450 #endif
1451 abort();
1452 }
1453 }
1454 if ((lp->rxpboffset == 0) && (fc_size) && (c != frame_byte)) {
1455 continue;
1456 }
1457 if ((lp->datagram) && (lp->rxpboffset == fc_size)) {
1458
1459
1460
1461
1462 lp->rxpb[lp->rxpboffset++] = (uint8)(((1 + lp->rxbpi - lp->rxbpr) >> 8) & 0xFF);
1463 lp->rxpb[lp->rxpboffset++] = (uint8)((1 + lp->rxbpi - lp->rxbpr) & 0xFF);
1464 }
1465 lp->rxpb[lp->rxpboffset++] = c & 0xFF;
1466 if (lp->rxpboffset >= (2 + fc_size)) {
1467 pktsize = (lp->rxpb[0+fc_size] << 8) | lp->rxpb[1+fc_size];
1468 if (pktsize == (lp->rxpboffset - 2)) {
1469 ++lp->rxpcnt;
1470 *pbuf = &lp->rxpb[2+fc_size];
1471 *psize = pktsize;
1472 lp->rxpboffset = 0;
1473 return SCPE_OK;
1474 }
1475 }
1476 }
1477 *pbuf = NULL;
1478 *psize = 0;
1479 if (lp->conn)
1480 return SCPE_OK;
1481 return SCPE_LOST;
1482 }
1483
1484
1485
1486
1487
1488
1489
1490
1491 void tmxr_poll_rx (TMXR *mp)
1492 {
1493 int32 i, nbytes, j;
1494 TMLN *lp;
1495
1496 for (i = 0; i < mp->lines; i++) {
1497 lp = mp->ldsc + i;
1498 if (!(lp->sock
1499 || lp->loopback) ||
1500 !(lp->rcve))
1501 continue;
1502
1503 nbytes = 0;
1504 if (lp->rxbpi == 0)
1505 nbytes = tmxr_read (lp,
1506 lp->rxbsz - TMXR_GUARD);
1507 else if (lp->tsta)
1508 nbytes = tmxr_read (lp,
1509 lp->rxbsz - lp->rxbpi);
1510
1511 if (nbytes < 0) {
1512 if (!lp->datagram) {
1513 if (!lp->txbfd || lp->notelnet)
1514 lp->txbpi = lp->txbpr = 0;
1515 tmxr_close_ln (lp);
1516 }
1517 }
1518
1519 else if (nbytes > 0) {
1520
1521 j = lp->rxbpi;
1522 lp->rxbpi = lp->rxbpi + nbytes;
1523 lp->rxcnt = lp->rxcnt + nbytes;
1524
1525
1526
1527 if (!lp->notelnet) {
1528 for (; j < lp->rxbpi; ) {
1529 u_char tmp = (u_char)lp->rxb[j];
1530 switch (lp->tsta) {
1531
1532 case TNS_NORM:
1533 if (tmp == TN_IAC) {
1534 lp->tsta = TNS_IAC;
1535 tmxr_rmvrc (lp, j);
1536 break;
1537 }
1538 if ((tmp == TN_CR) && lp->dstb)
1539 lp->tsta = TNS_CRPAD;
1540 j = j + 1;
1541 break;
1542
1543 case TNS_IAC:
1544 if (tmp == TN_IAC) {
1545 lp->tsta = TNS_NORM;
1546 j = j + 1;
1547 break;
1548 }
1549 if (tmp == TN_BRK) {
1550 lp->tsta = TNS_NORM;
1551 lp->rxb[j] = 0;
1552 lp->rbr[j] = 1;
1553 j = j + 1;
1554 break;
1555 }
1556 switch (tmp) {
1557 case TN_WILL:
1558 lp->tsta = TNS_WILL;
1559 break;
1560 case TN_WONT:
1561 lp->tsta = TNS_WONT;
1562 break;
1563 case TN_DO:
1564 lp->tsta = TNS_DO;
1565 break;
1566 case TN_DONT:
1567 lp->tsta = TNS_SKIP;
1568 break;
1569 case TN_GA: case TN_EL:
1570 case TN_EC: case TN_AYT:
1571 case TN_AO: case TN_IP:
1572 case TN_NOP:
1573 lp->tsta = TNS_NORM;
1574 break;
1575 case TN_SB:
1576 case TN_DATAMK:
1577 case TN_SE:
1578 lp->tsta = TNS_NORM;
1579 break;
1580 }
1581 tmxr_rmvrc (lp, j);
1582 break;
1583
1584 case TNS_WILL:
1585 if ((tmp == TN_STATUS) ||
1586 (tmp == TN_TIMING) ||
1587 (tmp == TN_NAOCRD) ||
1588 (tmp == TN_NAOHTS) ||
1589 (tmp == TN_NAOHTD) ||
1590 (tmp == TN_NAOFFD) ||
1591 (tmp == TN_NAOVTS) ||
1592 (tmp == TN_NAOVTD) ||
1593 (tmp == TN_NAOLFD) ||
1594 (tmp == TN_EXTEND) ||
1595 (tmp == TN_LOGOUT) ||
1596 (tmp == TN_BM) ||
1597 (tmp == TN_DET) ||
1598 (tmp == TN_SENDLO) ||
1599 (tmp == TN_TERMTY) ||
1600 (tmp == TN_ENDREC) ||
1601 (tmp == TN_TUID) ||
1602 (tmp == TN_OUTMRK) ||
1603 (tmp == TN_TTYLOC) ||
1604 (tmp == TN_3270) ||
1605 (tmp == TN_X3PAD) ||
1606 (tmp == TN_NAWS) ||
1607 (tmp == TN_TERMSP) ||
1608 (tmp == TN_TOGFLO) ||
1609 (tmp == TN_XDISPL) ||
1610 (tmp == TN_ENVIRO) ||
1611 (tmp == TN_AUTH) ||
1612 (tmp == TN_ENCRYP) ||
1613 (tmp == TN_NEWENV) ||
1614 (tmp == TN_TN3270) ||
1615 (tmp == TN_CHARST) ||
1616 (tmp == TN_COMPRT) ||
1617 (tmp == TN_KERMIT)) {
1618
1619 if (0 == (lp->telnet_sent_opts[tmp] & TNOS_DONT)) {
1620 lp->notelnet = TRUE;
1621 tmxr_putc_ln (lp, TN_IAC);
1622 lp->notelnet = FALSE;
1623 tmxr_putc_ln (lp, TN_DONT);
1624 tmxr_putc_ln (lp, tmp);
1625 lp->telnet_sent_opts[tmp] |= TNOS_DONT;
1626 }
1627 }
1628
1629 case TNS_WONT:
1630 if (tmp == TN_BIN) {
1631 if (lp->tsta == TNS_WILL) {
1632 lp->dstb = 0;
1633 }
1634 else {
1635 lp->dstb = 1;
1636 }
1637 }
1638 tmxr_rmvrc (lp, j);
1639 lp->tsta = TNS_NORM;
1640 break;
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656 case TNS_CRPAD:
1657 lp->tsta = TNS_NORM;
1658 if ((tmp == TN_LF) ||
1659 (tmp == TN_NUL))
1660 tmxr_rmvrc (lp, j);
1661 break;
1662
1663 case TNS_DO:
1664 if ((tmp == TN_STATUS) ||
1665 (tmp == TN_TIMING) ||
1666 (tmp == TN_NAOCRD) ||
1667 (tmp == TN_NAOHTS) ||
1668 (tmp == TN_NAOHTD) ||
1669 (tmp == TN_NAOFFD) ||
1670 (tmp == TN_NAOVTS) ||
1671 (tmp == TN_NAOVTD) ||
1672 (tmp == TN_NAOLFD) ||
1673 (tmp == TN_EXTEND) ||
1674 (tmp == TN_LOGOUT) ||
1675 (tmp == TN_BM) ||
1676 (tmp == TN_DET) ||
1677 (tmp == TN_SENDLO) ||
1678 (tmp == TN_TERMTY) ||
1679 (tmp == TN_ENDREC) ||
1680 (tmp == TN_TUID) ||
1681 (tmp == TN_OUTMRK) ||
1682 (tmp == TN_TTYLOC) ||
1683 (tmp == TN_3270) ||
1684 (tmp == TN_X3PAD) ||
1685 (tmp == TN_NAWS) ||
1686 (tmp == TN_TERMSP) ||
1687 (tmp == TN_TOGFLO) ||
1688 (tmp == TN_XDISPL) ||
1689 (tmp == TN_ENVIRO) ||
1690 (tmp == TN_AUTH) ||
1691 (tmp == TN_ENCRYP) ||
1692 (tmp == TN_NEWENV) ||
1693 (tmp == TN_TN3270) ||
1694 (tmp == TN_CHARST) ||
1695 (tmp == TN_COMPRT) ||
1696 (tmp == TN_KERMIT)) {
1697
1698 if (0 == (lp->telnet_sent_opts[tmp] & TNOS_WONT)) {
1699 lp->notelnet = TRUE;
1700 tmxr_putc_ln (lp, TN_IAC);
1701 lp->notelnet = FALSE;
1702 tmxr_putc_ln (lp, TN_WONT);
1703 tmxr_putc_ln (lp, tmp);
1704 lp->telnet_sent_opts[tmp] |= TNOS_WONT;
1705 }
1706 }
1707
1708 case TNS_SKIP: default:
1709 tmxr_rmvrc (lp, j);
1710 lp->tsta = TNS_NORM;
1711 break;
1712 }
1713 }
1714 }
1715 }
1716 }
1717 for (i = 0; i < mp->lines; i++) {
1718 lp = mp->ldsc + i;
1719 if (lp->rxbpi == lp->rxbpr)
1720 lp->rxbpi = lp->rxbpr = 0;
1721 }
1722 return;
1723 }
1724
1725
1726
1727 int32 tmxr_rqln_bare (const TMLN *lp, t_bool speed)
1728 {
1729 if ((speed) && (lp->rxbps)) {
1730 if (sim_gtime () < lp->rxnexttime)
1731 return 0;
1732 else
1733 return (lp->rxbpi - lp->rxbpr + ((lp->rxbpi < lp->rxbpr)? lp->rxbsz : 0)) ? 1 : 0;
1734 }
1735 return (lp->rxbpi - lp->rxbpr + ((lp->rxbpi < lp->rxbpr)? lp->rxbsz: 0));
1736 }
1737
1738 int32 tmxr_rqln (const TMLN *lp)
1739 {
1740 return tmxr_rqln_bare (lp, TRUE);
1741 }
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756 t_stat tmxr_putc_ln (TMLN *lp, int32 chr)
1757 {
1758 if ((lp->conn == FALSE) &&
1759 (!lp->txbfd || lp->notelnet)) {
1760 ++lp->txdrp;
1761 return SCPE_LOST;
1762 }
1763
1764 #define TXBUF_AVAIL(lp) (lp->txbsz - tmxr_tqln (lp))
1765
1766 #define TXBUF_CHAR(lp, c) { \
1767 lp->txb[lp->txbpi++] = (char)(c); \
1768 lp->txbpi %= lp->txbsz; \
1769 if (lp->txbpi == lp->txbpr) \
1770 lp->txbpr = (1+lp->txbpr)%lp->txbsz, ++lp->txdrp; \
1771 }
1772
1773 if ((lp->txbfd && !lp->notelnet) || (TXBUF_AVAIL(lp) > 1)) {
1774 if ((TN_IAC == (u_char) chr) && (!lp->notelnet))
1775 TXBUF_CHAR (lp, TN_IAC);
1776 TXBUF_CHAR (lp, chr);
1777 if ((!lp->txbfd) && ((unsigned long int)TXBUF_AVAIL (lp) <= TMXR_GUARD))
1778 lp->xmte = 0;
1779 if (lp->txlog)
1780 fputc (chr, lp->txlog);
1781 sim_exp_check (&lp->expect, chr);
1782 return SCPE_OK;
1783 }
1784 ++lp->txdrp; lp->xmte = 0;
1785 return SCPE_STALL;
1786 }
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805 t_stat tmxr_put_packet_ln (TMLN *lp, const uint8 *buf, size_t size)
1806 {
1807 return tmxr_put_packet_ln_ex (lp, buf, size, 0);
1808 }
1809
1810 t_stat tmxr_put_packet_ln_ex (TMLN *lp, const uint8 *buf, size_t size, uint8 frame_byte)
1811 {
1812 t_stat r;
1813 size_t fc_size = (frame_byte ? 1 : 0);
1814 size_t pktlen_size = (lp->datagram ? 0 : 2);
1815
1816 if ((!lp->conn) && (!lp->loopback))
1817 return SCPE_LOST;
1818 if (lp->txppoffset < lp->txppsize) {
1819 return SCPE_STALL;
1820 }
1821 if (lp->txpbsize < size + pktlen_size + fc_size) {
1822 lp->txpbsize = size + pktlen_size + fc_size;
1823 lp->txpb = (uint8 *)realloc (lp->txpb, lp->txpbsize);
1824 if (!lp->txpb)
1825 {
1826 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1827 __func__, __FILE__, __LINE__);
1828 #if defined(USE_BACKTRACE)
1829 # ifdef SIGUSR2
1830 (void)raise(SIGUSR2);
1831
1832 # endif
1833 #endif
1834 abort();
1835 }
1836 }
1837 lp->txpb[0] = frame_byte;
1838 if (!lp->datagram) {
1839 lp->txpb[0+fc_size] = (size >> 8) & 0xFF;
1840 lp->txpb[1+fc_size] = size & 0xFF;
1841 }
1842 memcpy (lp->txpb + pktlen_size + fc_size, buf, size);
1843 lp->txppsize = size + pktlen_size + fc_size;
1844 lp->txppoffset = 0;
1845 ++lp->txpcnt;
1846 while ((lp->txppoffset < lp->txppsize) &&
1847 (SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset]))))
1848 ++lp->txppoffset;
1849 (void)r;
1850 tmxr_send_buffered_data (lp);
1851 return (lp->conn || lp->loopback) ? SCPE_OK : SCPE_LOST;
1852 }
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862 void tmxr_poll_tx (TMXR *mp)
1863 {
1864 int32 i, nbytes;
1865 TMLN *lp;
1866
1867 for (i = 0; i < mp->lines; i++) {
1868 lp = mp->ldsc + i;
1869 if (!lp->conn)
1870 continue;
1871 nbytes = tmxr_send_buffered_data (lp);
1872 if (nbytes == 0) {
1873 lp->xmte = 1;
1874 }
1875 }
1876 return;
1877 }
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887 int32 tmxr_send_buffered_data (TMLN *lp)
1888 {
1889 int32 nbytes, sbytes;
1890 t_stat r;
1891
1892 nbytes = tmxr_tqln(lp);
1893 if (nbytes) {
1894 if (lp->txbpr < lp->txbpi)
1895 sbytes = tmxr_write (lp, nbytes);
1896 else
1897 sbytes = tmxr_write (lp, lp->txbsz - lp->txbpr);
1898 if (sbytes >= 0) {
1899 lp->txbpr = (lp->txbpr + sbytes);
1900 if (lp->txbpr >= lp->txbsz)
1901 lp->txbpr = 0;
1902 lp->txcnt = lp->txcnt + sbytes;
1903 nbytes = nbytes - sbytes;
1904 if ((nbytes == 0) && (lp->datagram))
1905 lp->txbpi = lp->txbpr = 0;
1906 }
1907 if (sbytes < 0) {
1908 lp->txbpi = lp->txbpr = 0;
1909 lp->rxpboffset = lp->txppoffset = lp->txppsize = 0;
1910 tmxr_close_ln (lp);
1911 return nbytes;
1912 }
1913 if (nbytes && (lp->txbpr == 0)) {
1914 sbytes = tmxr_write (lp, nbytes);
1915 if (sbytes > 0) {
1916 lp->txbpr = (lp->txbpr + sbytes);
1917 if (lp->txbpr >= lp->txbsz)
1918 lp->txbpr = 0;
1919 lp->txcnt = lp->txcnt + sbytes;
1920 nbytes = nbytes - sbytes;
1921 }
1922 }
1923 }
1924 while ((lp->txppoffset < lp->txppsize) &&
1925 (lp->txbsz > nbytes) &&
1926 (SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset]))))
1927 ++lp->txppoffset;
1928 (void)r;
1929 if ((nbytes == 0) && (tmxr_tqln(lp) > 0))
1930 return tmxr_send_buffered_data (lp);
1931 return tmxr_tqln(lp) + tmxr_tpqln(lp);
1932 }
1933
1934
1935
1936 int32 tmxr_tqln (const TMLN *lp)
1937 {
1938 return (lp->txbpi - lp->txbpr + ((lp->txbpi < lp->txbpr)? lp->txbsz: 0));
1939 }
1940
1941
1942
1943 int32 tmxr_tpqln (const TMLN *lp)
1944 {
1945 return (lp->txppsize - lp->txppoffset);
1946 }
1947
1948
1949
1950 t_bool tmxr_tpbusyln (const TMLN *lp)
1951 {
1952 return (0 != (lp->txppsize - lp->txppoffset));
1953 }
1954
1955 static void _mux_detach_line (TMLN *lp, t_bool close_listener, t_bool close_connecting)
1956 {
1957 if (close_listener && lp->master) {
1958 sim_close_sock (lp->master);
1959 lp->master = 0;
1960 FREE (lp->port);
1961 lp->port = NULL;
1962 }
1963 if (lp->sock) {
1964 tmxr_report_disconnection (lp);
1965 tmxr_reset_ln (lp);
1966 }
1967 if (close_connecting) {
1968 FREE (lp->destination);
1969 lp->destination = NULL;
1970 if (lp->connecting) {
1971 lp->sock = lp->connecting;
1972 lp->connecting = 0;
1973 tmxr_reset_ln (lp);
1974 }
1975 }
1976 tmxr_set_line_loopback (lp, FALSE);
1977 }
1978
1979 t_stat tmxr_detach_ln (TMLN *lp)
1980 {
1981 UNIT *uptr = NULL;
1982
1983 _mux_detach_line (lp, TRUE, TRUE);
1984 if (lp->mp) {
1985 if (lp->uptr)
1986 uptr = lp->uptr;
1987 else
1988 uptr = lp->mp->uptr;
1989 }
1990 if (uptr && uptr->filename) {
1991
1992 uptr->filename = tmxr_mux_attach_string (uptr->filename, lp->mp);
1993
1994 if (uptr->filename == NULL)
1995 tmxr_detach (lp->mp, uptr);
1996 }
1997 return SCPE_OK;
1998 }
1999
2000 static int32 _tmln_speed_delta (CONST char *cptr)
2001 {
2002 struct {
2003 const char *bps;
2004 int32 delta;
2005 } *spd, speeds[] = {
2006 { "50", TMLN_SPD_50_BPS },
2007 { "75", TMLN_SPD_75_BPS },
2008 { "110", TMLN_SPD_110_BPS },
2009 { "134", TMLN_SPD_134_BPS },
2010 { "150", TMLN_SPD_150_BPS },
2011 { "300", TMLN_SPD_300_BPS },
2012 { "600", TMLN_SPD_600_BPS },
2013 { "1200", TMLN_SPD_1200_BPS },
2014 { "1800", TMLN_SPD_1800_BPS },
2015 { "2000", TMLN_SPD_2000_BPS },
2016 { "2400", TMLN_SPD_2400_BPS },
2017 { "3600", TMLN_SPD_3600_BPS },
2018 { "4800", TMLN_SPD_4800_BPS },
2019 { "7200", TMLN_SPD_7200_BPS },
2020 { "9600", TMLN_SPD_9600_BPS },
2021 { "19200", TMLN_SPD_19200_BPS },
2022 { "38400", TMLN_SPD_38400_BPS },
2023 { "57600", TMLN_SPD_57600_BPS },
2024 { "76800", TMLN_SPD_76800_BPS },
2025 { "115200", TMLN_SPD_115200_BPS },
2026 { "0", 0 } };
2027 int nspeed;
2028 char speed[24];
2029
2030 nspeed = (uint32)strtotv (cptr, &cptr, 10);
2031 if ((*cptr != '\0') && (*cptr != '-') && (*cptr != '*'))
2032 return -1;
2033 sprintf (speed, "%d", nspeed);
2034
2035 spd = speeds;
2036 while (1) {
2037 if (0 == strcmp(spd->bps, speed))
2038 return spd->delta;
2039 if (spd->delta == 0)
2040 break;
2041 ++spd;
2042 }
2043 return -1;
2044 }
2045
2046 t_stat tmxr_set_line_speed (TMLN *lp, CONST char *speed)
2047 {
2048 UNIT *uptr;
2049 CONST char *cptr;
2050 t_stat r;
2051
2052 if (!speed || !*speed)
2053 return SCPE_2FARG;
2054 if (_tmln_speed_delta (speed) < 0)
2055 return SCPE_ARG;
2056 if (lp == NULL)
2057 return SCPE_ARG;
2058 lp->rxbps = (uint32)strtotv (speed, &cptr, 10);
2059 if (*cptr == '*') {
2060 uint32 rxbpsfactor = (uint32) get_uint (cptr+1, 10, 32, &r);
2061 if (r != SCPE_OK)
2062 return r;
2063 lp->rxbpsfactor = TMXR_RX_BPS_UNIT_SCALE * rxbpsfactor;
2064 }
2065 lp->rxdelta = _tmln_speed_delta (speed);
2066 lp->rxnexttime = 0.0;
2067 uptr = lp->uptr;
2068 if ((!uptr) && (lp->mp))
2069 uptr = lp->mp->uptr;
2070 if (uptr)
2071 uptr->wait = lp->rxdelta;
2072 if (lp->rxbpsfactor == 0.0)
2073 lp->rxbpsfactor = TMXR_RX_BPS_UNIT_SCALE;
2074 lp->txbps = lp->rxbps;
2075 lp->txdelta = lp->rxdelta;
2076 lp->txnexttime = lp->rxnexttime;
2077 return SCPE_OK;
2078 }
2079
2080
2081
2082
2083
2084
2085
2086
2087 t_stat tmxr_open_master (TMXR *mp, CONST char *cptr)
2088 {
2089 int32 i, line, nextline = -1;
2090 char tbuf[CBUFSIZE], listen[CBUFSIZE], destination[CBUFSIZE],
2091 logfiletmpl[CBUFSIZE], buffered[CBUFSIZE], hostport[CBUFSIZE*2],
2092 port[CBUFSIZE], option[CBUFSIZE], speed[CBUFSIZE];
2093 SOCKET sock;
2094 CONST char *tptr = cptr;
2095 t_bool nolog, notelnet, listennotelnet, modem_control, loopback, datagram, packet;
2096 TMLN *lp = NULL;
2097 t_stat r = SCPE_OK;
2098
2099 if (*tptr == '\0')
2100 return SCPE_ARG;
2101 for (i = 0; i < mp->lines; i++) {
2102 lp = mp->ldsc + i;
2103 lp->mp = mp;
2104 lp->modem_control = mp->modem_control;
2105 if (lp->rxbpsfactor == 0.0)
2106 lp->rxbpsfactor = TMXR_RX_BPS_UNIT_SCALE;
2107 }
2108 mp->ring_sock = INVALID_SOCKET;
2109 FREE (mp->ring_ipad);
2110 mp->ring_ipad = NULL;
2111 mp->ring_start_time = 0;
2112 while (*tptr) {
2113 line = nextline;
2114 memset(logfiletmpl, '\0', sizeof(logfiletmpl));
2115 memset(listen, '\0', sizeof(listen));
2116 memset(destination, '\0', sizeof(destination));
2117 memset(buffered, '\0', sizeof(buffered));
2118 memset(port, '\0', sizeof(port));
2119 memset(option, '\0', sizeof(option));
2120 memset(speed, '\0', sizeof(speed));
2121 nolog = notelnet = listennotelnet = loopback = FALSE;
2122 datagram = mp->datagram;
2123 packet = mp->packet;
2124 if (mp->buffered)
2125 sprintf(buffered, "%d", mp->buffered);
2126 if (line != -1)
2127 notelnet = listennotelnet = mp->notelnet;
2128 modem_control = mp->modem_control;
2129 while (*tptr) {
2130 tptr = get_glyph_nc (tptr, tbuf, ',');
2131 if (!tbuf[0])
2132 break;
2133 cptr = tbuf;
2134 if (!isdigit((unsigned char)*cptr)) {
2135 char gbuf[CBUFSIZE];
2136 CONST char *init_cptr = cptr;
2137
2138 cptr = get_glyph (cptr, gbuf, '=');
2139 if (0 == MATCH_CMD (gbuf, "LINE")) {
2140 if ((NULL == cptr) || ('\0' == *cptr))
2141 return sim_messagef (SCPE_2FARG, "Missing Line Specifier\n");
2142 nextline = (int32) get_uint (cptr, 10, mp->lines-1, &r);
2143 if (r)
2144 return sim_messagef (SCPE_ARG, "Invalid Line Specifier: %s\n", cptr);
2145 break;
2146 }
2147 if (0 == MATCH_CMD (gbuf, "LOG")) {
2148 if ((NULL == cptr) || ('\0' == *cptr))
2149 return sim_messagef (SCPE_2FARG, "Missing Log Specifier\n");
2150 strncpy(logfiletmpl, cptr, sizeof(logfiletmpl)-1);
2151 continue;
2152 }
2153 if (0 == MATCH_CMD (gbuf, "LOOPBACK")) {
2154 if ((NULL != cptr) && ('\0' != *cptr))
2155 return sim_messagef (SCPE_2MARG, "Unexpected Loopback Specifier: %s\n", cptr);
2156 loopback = TRUE;
2157 continue;
2158 }
2159 if ((0 == MATCH_CMD (gbuf, "NOBUFFERED")) || (0 == MATCH_CMD (gbuf, "UNBUFFERED"))) {
2160 if ((NULL != cptr) && ('\0' != *cptr))
2161 return sim_messagef (SCPE_2MARG, "Unexpected Unbuffered Specifier: %s\n", cptr);
2162 buffered[0] = '\0';
2163 continue;
2164 }
2165 if (0 == MATCH_CMD (gbuf, "BUFFERED")) {
2166 if ((NULL == cptr) || ('\0' == *cptr))
2167 strcpy (buffered, "32768");
2168 else {
2169 i = (int32) get_uint (cptr, 10, 1024*1024, &r);
2170 if (r || (i == 0))
2171 return sim_messagef (SCPE_ARG, "Invalid Buffered Specifier: %s\n", cptr);
2172 sprintf(buffered, "%d", i);
2173 }
2174 continue;
2175 }
2176 if (0 == MATCH_CMD (gbuf, "NOLOG")) {
2177 if ((NULL != cptr) && ('\0' != *cptr))
2178 return sim_messagef (SCPE_2MARG, "Unexpected NoLog Specifier: %s\n", cptr);
2179 nolog = TRUE;
2180 continue;
2181 }
2182 if (0 == MATCH_CMD (gbuf, "NOMODEM")) {
2183 if ((NULL != cptr) && ('\0' != *cptr))
2184 return sim_messagef (SCPE_2MARG, "Unexpected NoModem Specifier: %s\n", cptr);
2185 modem_control = FALSE;
2186 continue;
2187 }
2188 if (0 == MATCH_CMD (gbuf, "MODEM")) {
2189 if ((NULL != cptr) && ('\0' != *cptr))
2190 return sim_messagef (SCPE_2MARG, "Unexpected Modem Specifier: %s\n", cptr);
2191 modem_control = TRUE;
2192 continue;
2193 }
2194 if ((0 == MATCH_CMD (gbuf, "DATAGRAM")) || (0 == MATCH_CMD (gbuf, "UDP"))) {
2195 if ((NULL != cptr) && ('\0' != *cptr))
2196 return sim_messagef (SCPE_2MARG, "Unexpected Datagram Specifier: %s\n", cptr);
2197 notelnet = datagram = TRUE;
2198 continue;
2199 }
2200 if (0 == MATCH_CMD (gbuf, "PACKET")) {
2201 if ((NULL != cptr) && ('\0' != *cptr))
2202 return sim_messagef (SCPE_2MARG, "Unexpected Packet Specifier: %s\n", cptr);
2203 packet = TRUE;
2204 continue;
2205 }
2206 if ((0 == MATCH_CMD (gbuf, "STREAM")) || (0 == MATCH_CMD (gbuf, "TCP"))) {
2207 if ((NULL != cptr) && ('\0' != *cptr))
2208 return sim_messagef (SCPE_2MARG, "Unexpected Stream Specifier: %s\n", cptr);
2209 datagram = FALSE;
2210 continue;
2211 }
2212 if (0 == MATCH_CMD (gbuf, "CONNECT")) {
2213 if ((NULL == cptr) || ('\0' == *cptr))
2214 return sim_messagef (SCPE_2FARG, "Missing Connect Specifier\n");
2215 strcpy (destination, cptr);
2216 continue;
2217 }
2218 if (0 == MATCH_CMD (gbuf, "SPEED")) {
2219 if ((NULL == cptr) || ('\0' == *cptr) ||
2220 (_tmln_speed_delta (cptr) < 0))
2221 return sim_messagef (SCPE_ARG, "Invalid Speed Specifier: %s\n", (cptr ? cptr : ""));
2222 strcpy (speed, cptr);
2223 continue;
2224 }
2225 cptr = get_glyph (gbuf, port, ';');
2226 if (sim_parse_addr (port, NULL, 0, NULL, NULL, 0, NULL, NULL))
2227 return sim_messagef (SCPE_ARG, "Invalid Port Specifier: %s\n", port);
2228 if (cptr) {
2229 char *tptr = gbuf + (cptr - gbuf);
2230 (void)get_glyph (cptr, tptr, 0);
2231 if (0 == MATCH_CMD (cptr, "NOTELNET"))
2232 listennotelnet = TRUE;
2233 else
2234 if (0 == MATCH_CMD (cptr, "TELNET"))
2235 listennotelnet = FALSE;
2236 else
2237 return sim_messagef (SCPE_ARG, "Invalid Specifier: %s\n", tptr);
2238 }
2239 cptr = init_cptr;
2240 }
2241 cptr = get_glyph_nc (cptr, port, ';');
2242 sock = sim_master_sock (port, &r);
2243 if (r)
2244 return sim_messagef (SCPE_ARG, "Invalid Port Specifier: %s\n", port);
2245 if (sock == INVALID_SOCKET)
2246 return sim_messagef (SCPE_OPENERR, "Can't open network port: %s\n", port);
2247 sim_close_sock (sock);
2248 sim_os_ms_sleep (2);
2249 strcpy (listen, port);
2250 cptr = get_glyph (cptr, option, ';');
2251 (void)cptr;
2252 if (option[0]) {
2253 if (0 == MATCH_CMD (option, "NOTELNET"))
2254 listennotelnet = TRUE;
2255 else
2256 if (0 == MATCH_CMD (option, "TELNET"))
2257 listennotelnet = FALSE;
2258 else {
2259 if (*tptr)
2260 return sim_messagef (SCPE_ARG, "Invalid Specifier: %s\n", tptr);
2261 }
2262 }
2263 }
2264 if (destination[0]) {
2265
2266 char *eptr;
2267
2268 memset (hostport, '\0', sizeof(hostport));
2269 strncpy (hostport, destination, sizeof(hostport));
2270 if ((eptr = strchr (hostport, ';')))
2271 *(eptr++) = '\0';
2272 if (eptr) {
2273 (void)get_glyph (eptr, eptr, 0);
2274 if (0 == MATCH_CMD (eptr, "NOTELNET"))
2275 notelnet = TRUE;
2276 else
2277 if (0 == MATCH_CMD (eptr, "TELNET"))
2278 if (datagram)
2279 return sim_messagef (SCPE_ARG, "Telnet invalid on Datagram socket\n");
2280 else
2281 notelnet = FALSE;
2282 else
2283 return sim_messagef (SCPE_ARG, "Unexpected specifier: %s\n", eptr);
2284 }
2285 sock = sim_connect_sock_ex (NULL, hostport, "localhost", NULL, (datagram ? SIM_SOCK_OPT_DATAGRAM : 0) |
2286 (packet ? SIM_SOCK_OPT_NODELAY : 0) |
2287 SIM_SOCK_OPT_BLOCKING);
2288 if (sock != INVALID_SOCKET)
2289 sim_close_sock (sock);
2290 else
2291 return sim_messagef (SCPE_ARG, "Invalid destination: %s\n", hostport);
2292 }
2293 if (line == -1) {
2294 if (modem_control != mp->modem_control)
2295 return SCPE_ARG;
2296 if (logfiletmpl[0]) {
2297 #ifdef __GNUC__
2298 # ifndef __clang_version__
2299 # ifndef __INTEL_COMPILER
2300 # if __GNUC__ > 7
2301 # pragma GCC diagnostic push
2302 # pragma GCC diagnostic ignored "-Wstringop-truncation"
2303 # endif
2304 # endif
2305 # endif
2306 #endif
2307 strncpy(mp->logfiletmpl, logfiletmpl, sizeof(mp->logfiletmpl)-1);
2308 #ifdef __GNUC__
2309 # ifndef __clang_version__
2310 # ifndef __INTEL_COMPILER
2311 # if __GNUC__ > 7
2312 # pragma GCC diagnostic pop
2313 # endif
2314 # endif
2315 # endif
2316 #endif
2317 for (i = 0; i < mp->lines; i++) {
2318 lp = mp->ldsc + i;
2319 sim_close_logfile (&lp->txlogref);
2320 lp->txlog = NULL;
2321 lp->txlogname = (char *)realloc(lp->txlogname, CBUFSIZE);
2322 if (!lp->txlogname)
2323 {
2324 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2325 __func__, __FILE__, __LINE__);
2326 #if defined(USE_BACKTRACE)
2327 # ifdef SIGUSR2
2328 (void)raise(SIGUSR2);
2329
2330 # endif
2331 #endif
2332 abort();
2333 }
2334 if (mp->lines > 1)
2335 sprintf(lp->txlogname, "%s_%d", mp->logfiletmpl, i);
2336 else
2337 strcpy (lp->txlogname, mp->logfiletmpl);
2338 r = sim_open_logfile (lp->txlogname, TRUE, &lp->txlog, &lp->txlogref);
2339 if (r == SCPE_OK)
2340 setvbuf (lp->txlog, NULL, _IOFBF, 65536);
2341 else {
2342 FREE (lp->txlogname);
2343 lp->txlogname = NULL;
2344 break;
2345 }
2346 }
2347 }
2348 mp->buffered = atoi(buffered);
2349 for (i = 0; i < mp->lines; i++) {
2350 lp = mp->ldsc + i;
2351 if (mp->buffered) {
2352 lp->txbsz = mp->buffered;
2353 lp->txbfd = 1;
2354 lp->rxbsz = mp->buffered;
2355 }
2356 else {
2357 lp->txbsz = TMXR_MAXBUF;
2358 lp->txbfd = 0;
2359 lp->rxbsz = TMXR_MAXBUF;
2360 }
2361 lp->txbpi = lp->txbpr = 0;
2362 lp->txb = (char *)realloc(lp->txb, lp->txbsz);
2363 if (!lp->txb)
2364 {
2365 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2366 __func__, __FILE__, __LINE__);
2367 #if defined(USE_BACKTRACE)
2368 # ifdef SIGUSR2
2369 (void)raise(SIGUSR2);
2370
2371 # endif
2372 #endif
2373 abort();
2374 }
2375 lp->rxb = (char *)realloc(lp->rxb, lp->rxbsz);
2376 if (!lp->rxb)
2377 {
2378 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2379 __func__, __FILE__, __LINE__);
2380 #if defined(USE_BACKTRACE)
2381 # ifdef SIGUSR2
2382 (void)raise(SIGUSR2);
2383
2384 # endif
2385 #endif
2386 abort();
2387 }
2388 lp->rbr = (char *)realloc(lp->rbr, lp->rxbsz);
2389 if (!lp->rbr)
2390 {
2391 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2392 __func__, __FILE__, __LINE__);
2393 #if defined(USE_BACKTRACE)
2394 # ifdef SIGUSR2
2395 (void)raise(SIGUSR2);
2396
2397 # endif
2398 #endif
2399 abort();
2400 }
2401 }
2402 if (nolog) {
2403 mp->logfiletmpl[0] = '\0';
2404 for (i = 0; i < mp->lines; i++) {
2405 lp = mp->ldsc + i;
2406 FREE(lp->txlogname);
2407 lp->txlogname = NULL;
2408 if (lp->txlog) {
2409 sim_close_logfile (&lp->txlogref);
2410 lp->txlog = NULL;
2411 }
2412 }
2413 }
2414 if ((listen[0]) && (!datagram)) {
2415 sock = sim_master_sock (listen, &r);
2416 if (r)
2417 return sim_messagef (SCPE_ARG, "Invalid network listen port: %s\n", listen);
2418 if (sock == INVALID_SOCKET)
2419 return sim_messagef (SCPE_OPENERR, "Can't open network socket for listen port: %s\n", listen);
2420 if (mp->port) {
2421 sim_close_sock (mp->master);
2422 mp->master = 0;
2423 FREE (mp->port);
2424 mp->port = NULL;
2425 }
2426 sim_printf ("Listening on port %s\n", listen);
2427 mp->port = (char *)realloc (mp->port, 1 + strlen (listen));
2428 if (!mp->port)
2429 {
2430 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2431 __func__, __FILE__, __LINE__);
2432 #if defined(USE_BACKTRACE)
2433 # ifdef SIGUSR2
2434 (void)raise(SIGUSR2);
2435
2436 # endif
2437 #endif
2438 abort();
2439 }
2440 strcpy (mp->port, listen);
2441 mp->master = sock;
2442 mp->ring_sock = INVALID_SOCKET;
2443 if (mp->ring_ipad) FREE (mp->ring_ipad);
2444 mp->ring_ipad = NULL;
2445 mp->ring_start_time = 0;
2446 mp->notelnet = listennotelnet;
2447 for (i = 0; i < mp->lines; i++) {
2448 lp = mp->ldsc + i;
2449 lp->mp = mp;
2450 lp->packet = mp->packet;
2451 if (speed[0])
2452 tmxr_set_line_speed (lp, speed);
2453 tmxr_init_line (lp);
2454 lp->sock = 0;
2455 }
2456 }
2457 if (loopback) {
2458 if (mp->lines > 1)
2459 return sim_messagef (SCPE_ARG, "Ambiguous Loopback specification\n");
2460 sim_printf ("Operating in loopback mode\n");
2461 for (i = 0; i < mp->lines; i++) {
2462 lp = mp->ldsc + i;
2463 tmxr_set_line_loopback (lp, loopback);
2464 if (speed[0])
2465 tmxr_set_line_speed (lp, speed);
2466 }
2467 }
2468 if (destination[0]) {
2469 if (mp->lines > 1)
2470 return sim_messagef (SCPE_ARG, "Ambiguous Destination specification\n");
2471 lp = &mp->ldsc[0];
2472 lp->datagram = datagram;
2473 if (datagram) {
2474 if (listen[0]) {
2475 lp->port = (char *)realloc (lp->port, 1 + strlen (listen));
2476 if (!lp->port)
2477 {
2478 fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2479 __func__, __FILE__, __LINE__);
2480 #if defined(USE_BACKTRACE)
2481 # ifdef SIGUSR2
2482 (void)raise(SIGUSR2);
2483
2484 # endif
2485 #endif
2486 abort();
2487 }
2488 strcpy (lp->port, listen);
2489 }
2490 else
2491 return sim_messagef (SCPE_ARG, "Missing listen port for Datagram socket\n");
2492 }
2493 lp->packet = packet;
2494 sock = sim_connect_sock_ex (datagram ? listen : NULL, hostport, "localhost", NULL, (datagram ? SIM_SOCK_OPT_DATAGRAM : 0) |
2495 (packet ? SIM_SOCK_OPT_NODELAY : 0) |
2496 SIM_SOCK_OPT_BLOCKING);
2497 if (sock != INVALID_SOCKET) {
2498 _mux_detach_line (lp, FALSE, TRUE);
2499 lp->destination = (char *)malloc(1+strlen(hostport));
2500 if (!lp->destination)
2501 {
2502 fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2503 __func__, __FILE__, __LINE__);
2504 #if defined(USE_BACKTRACE)
2505 # ifdef SIGUSR2
2506 (void)raise(SIGUSR2);
2507
2508 # endif
2509 #endif
2510 abort();
2511 }
2512 strcpy (lp->destination, hostport);
2513 lp->mp = mp;
2514 if (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR)) {
2515 lp->connecting = sock;
2516 lp->ipad = (char *)malloc (1 + strlen (lp->destination));
2517 if (!lp->ipad)
2518 {
2519 fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2520 __func__, __FILE__, __LINE__);
2521 #if defined(USE_BACKTRACE)
2522 # ifdef SIGUSR2
2523 (void)raise(SIGUSR2);
2524
2525 # endif
2526 #endif
2527 abort();
2528 }
2529 strcpy (lp->ipad, lp->destination);
2530 }
2531 else
2532 sim_close_sock (sock);
2533 lp->notelnet = notelnet;
2534 tmxr_init_line (lp);
2535 if (speed[0] && (!datagram))
2536 tmxr_set_line_speed (lp, speed);
2537 return SCPE_OK;
2538 }
2539 else
2540 return sim_messagef (SCPE_ARG, "Can't open %s socket on %s%s%s\n", datagram ? "Datagram" : "Stream", datagram ? listen : "", datagram ? "<->" : "", hostport);
2541 }
2542 }
2543 else {
2544 lp = &mp->ldsc[line];
2545 lp->mp = mp;
2546 if (logfiletmpl[0]) {
2547 sim_close_logfile (&lp->txlogref);
2548 lp->txlog = NULL;
2549 lp->txlogname = (char *)realloc (lp->txlogname, 1 + strlen (logfiletmpl));
2550 if (!lp->txlogname)
2551 {
2552 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2553 __func__, __FILE__, __LINE__);
2554 #if defined(USE_BACKTRACE)
2555 # ifdef SIGUSR2
2556 (void)raise(SIGUSR2);
2557
2558 # endif
2559 #endif
2560 abort();
2561 }
2562 strcpy (lp->txlogname, logfiletmpl);
2563 r = sim_open_logfile (lp->txlogname, TRUE, &lp->txlog, &lp->txlogref);
2564 if (r == SCPE_OK)
2565 setvbuf(lp->txlog, NULL, _IOFBF, 65536);
2566 else {
2567 FREE (lp->txlogname);
2568 lp->txlogname = NULL;
2569 return sim_messagef (r, "Can't open log file: %s\n", logfiletmpl);
2570 }
2571 }
2572 if (buffered[0] == '\0') {
2573 lp->rxbsz = lp->txbsz = TMXR_MAXBUF;
2574 lp->txbfd = 0;
2575 }
2576 else {
2577 lp->rxbsz = lp->txbsz = atoi(buffered);
2578 lp->txbfd = 1;
2579 }
2580 lp->txbpi = lp->txbpr = 0;
2581 lp->txb = (char *)realloc (lp->txb, lp->txbsz);
2582 if (!lp->txb)
2583 {
2584 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2585 __func__, __FILE__, __LINE__);
2586 #if defined(USE_BACKTRACE)
2587 # ifdef SIGUSR2
2588 (void)raise(SIGUSR2);
2589
2590 # endif
2591 #endif
2592 abort();
2593 }
2594 lp->rxb = (char *)realloc (lp->rxb, lp->rxbsz);
2595 if (!lp->rxb)
2596 {
2597 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2598 __func__, __FILE__, __LINE__);
2599 #if defined(USE_BACKTRACE)
2600 # ifdef SIGUSR2
2601 (void)raise(SIGUSR2);
2602
2603 # endif
2604 #endif
2605 abort();
2606 }
2607 lp->rbr = (char *)realloc (lp->rbr, lp->rxbsz);
2608 if (!lp->rbr)
2609 {
2610 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2611 __func__, __FILE__, __LINE__);
2612 #if defined(USE_BACKTRACE)
2613 # ifdef SIGUSR2
2614 (void)raise(SIGUSR2);
2615
2616 # endif
2617 #endif
2618 abort();
2619 }
2620 lp->packet = packet;
2621 if (nolog) {
2622 FREE(lp->txlogname);
2623 lp->txlogname = NULL;
2624 if (lp->txlog) {
2625 sim_close_logfile (&lp->txlogref);
2626 lp->txlog = NULL;
2627 }
2628 }
2629 if ((listen[0]) && (!datagram)) {
2630 if ((mp->lines == 1) && (mp->master))
2631 return sim_messagef (SCPE_ARG, "Single Line MUX can have either line specific OR MUS listener but NOT both\n");
2632 sock = sim_master_sock (listen, &r);
2633 if (r)
2634 return sim_messagef (SCPE_ARG, "Invalid Listen Specification: %s\n", listen);
2635 if (sock == INVALID_SOCKET)
2636 return sim_messagef (SCPE_OPENERR, "Can't listen on port: %s\n", listen);
2637 _mux_detach_line (lp, TRUE, FALSE);
2638 sim_printf ("Line %d Listening on port %s\n", line, listen);
2639 lp->port = (char *)realloc (lp->port, 1 + strlen (listen));
2640 if (!lp->port)
2641 {
2642 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2643 __func__, __FILE__, __LINE__);
2644 #if defined(USE_BACKTRACE)
2645 # ifdef SIGUSR2
2646 (void)raise(SIGUSR2);
2647
2648 # endif
2649 #endif
2650 abort();
2651 }
2652 strcpy (lp->port, listen);
2653 lp->master = sock;
2654 if (listennotelnet != mp->notelnet)
2655 lp->notelnet = listennotelnet;
2656 else
2657 lp->notelnet = mp->notelnet;
2658 }
2659 if (destination[0]) {
2660 lp->datagram = datagram;
2661 if (datagram) {
2662 if (listen[0]) {
2663 lp->port = (char *)realloc (lp->port, 1 + strlen (listen));
2664 if (!lp->port)
2665 {
2666 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2667 __func__, __FILE__, __LINE__);
2668 #if defined(USE_BACKTRACE)
2669 # ifdef SIGUSR2
2670 (void)raise(SIGUSR2);
2671
2672 # endif
2673 #endif
2674 abort();
2675 }
2676 strcpy (lp->port, listen);
2677 }
2678 else
2679 return sim_messagef (SCPE_ARG, "Missing listen port for Datagram socket\n");
2680 }
2681 sock = sim_connect_sock_ex (datagram ? listen : NULL, hostport, "localhost", NULL, (datagram ? SIM_SOCK_OPT_DATAGRAM : 0) | (packet ? SIM_SOCK_OPT_NODELAY : 0));
2682 if (sock != INVALID_SOCKET) {
2683 _mux_detach_line (lp, FALSE, TRUE);
2684 lp->destination = (char *)malloc(1+strlen(hostport));
2685 if (!lp->destination)
2686 {
2687 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2688 __func__, __FILE__, __LINE__);
2689 #if defined(USE_BACKTRACE)
2690 # ifdef SIGUSR2
2691 (void)raise(SIGUSR2);
2692
2693 # endif
2694 #endif
2695 abort();
2696 }
2697 strcpy (lp->destination, hostport);
2698 if (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR)) {
2699 lp->connecting = sock;
2700 lp->ipad = (char *)malloc (1 + strlen (lp->destination));
2701 if (!lp->ipad)
2702 {
2703 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2704 __func__, __FILE__, __LINE__);
2705 #if defined(USE_BACKTRACE)
2706 # ifdef SIGUSR2
2707 (void)raise(SIGUSR2);
2708
2709 # endif
2710 #endif
2711 abort();
2712 }
2713 strcpy (lp->ipad, lp->destination);
2714 }
2715 else
2716 sim_close_sock (sock);
2717 lp->notelnet = notelnet;
2718 tmxr_init_line (lp);
2719 }
2720 else
2721 return sim_messagef (SCPE_ARG, "Can't open %s socket on %s%s%s\n", datagram ? "Datagram" : "Stream", datagram ? listen : "", datagram ? "<->" : "", hostport);
2722 }
2723 }
2724 if (loopback) {
2725 if (lp != NULL) {
2726 tmxr_set_line_loopback (lp, loopback);
2727 sim_printf ("Line %d operating in loopback mode\n", line);
2728 }
2729 }
2730 if (lp != NULL) lp->modem_control = modem_control;
2731 if (speed[0] && (!datagram)
2732 )
2733 tmxr_set_line_speed (lp, speed);
2734 r = SCPE_OK;
2735 }
2736 if (r == SCPE_OK)
2737 tmxr_add_to_open_list (mp);
2738 return r;
2739 }
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760 t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll)
2761 {
2762 if ((line < 0) || (line >= mp->lines))
2763 return SCPE_ARG;
2764 mp->ldsc[line].uptr = uptr_poll;
2765 return SCPE_OK;
2766 }
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787 t_stat tmxr_set_line_output_unit (TMXR *mp, int line, UNIT *uptr_poll)
2788 {
2789 if ((line < 0) || (line >= mp->lines))
2790 return SCPE_ARG;
2791 mp->ldsc[line].o_uptr = uptr_poll;
2792 return SCPE_OK;
2793 }
2794
2795 static TMXR **tmxr_open_devices = NULL;
2796 static int tmxr_open_device_count = 0;
2797
2798 t_stat tmxr_start_poll (void)
2799 {
2800 return SCPE_OK;
2801 }
2802
2803 t_stat tmxr_stop_poll (void)
2804 {
2805 return SCPE_OK;
2806 }
2807
2808 static void tmxr_add_to_open_list (TMXR* mux)
2809 {
2810 int i;
2811 t_bool found = FALSE;
2812
2813 for (i=0; i<tmxr_open_device_count; ++i)
2814 if (tmxr_open_devices[i] == mux) {
2815 found = TRUE;
2816 break;
2817 }
2818 if (!found) {
2819 tmxr_open_devices = (TMXR **)realloc(tmxr_open_devices, (tmxr_open_device_count+1)*sizeof(*tmxr_open_devices));
2820 if (!tmxr_open_devices)
2821 {
2822 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2823 __func__, __FILE__, __LINE__);
2824 #if defined(USE_BACKTRACE)
2825 # ifdef SIGUSR2
2826 (void)raise(SIGUSR2);
2827
2828 # endif
2829 #endif
2830 abort();
2831 }
2832 tmxr_open_devices[tmxr_open_device_count++] = mux;
2833 for (i=0; i<mux->lines; i++)
2834 if (0 == mux->ldsc[i].send.delay)
2835 mux->ldsc[i].send.delay = SEND_DEFAULT_DELAY;
2836 }
2837 }
2838
2839 static void _tmxr_remove_from_open_list (const TMXR* mux)
2840 {
2841 int i, j;
2842
2843 for (i=0; i<tmxr_open_device_count; ++i)
2844 if (tmxr_open_devices[i] == mux) {
2845 for (j=i+1; j<tmxr_open_device_count; ++j)
2846 tmxr_open_devices[j-1] = tmxr_open_devices[j];
2847 --tmxr_open_device_count;
2848 break;
2849 }
2850 }
2851
2852 static t_stat _tmxr_locate_line_send_expect (const char *cptr, SEND **snd, EXPECT **exp)
2853 {
2854 char gbuf[CBUFSIZE];
2855 DEVICE *dptr;
2856 int i;
2857 t_stat r;
2858
2859 if (snd)
2860 *snd = NULL;
2861 if (exp)
2862 *exp = NULL;
2863 cptr = get_glyph(cptr, gbuf, ':');
2864 dptr = find_dev (gbuf);
2865 if (!dptr)
2866 return SCPE_ARG;
2867
2868 for (i=0; i<tmxr_open_device_count; ++i)
2869 if (tmxr_open_devices[i]->dptr == dptr) {
2870 int line = (int)get_uint (cptr, 10, tmxr_open_devices[i]->lines, &r);
2871 if (r != SCPE_OK)
2872 return r;
2873 if (snd)
2874 *snd = &tmxr_open_devices[i]->ldsc[line].send;
2875 if (exp)
2876 *exp = &tmxr_open_devices[i]->ldsc[line].expect;
2877 return SCPE_OK;
2878 }
2879 return SCPE_ARG;
2880 }
2881
2882 t_stat tmxr_locate_line_send (const char *cptr, SEND **snd)
2883 {
2884 return _tmxr_locate_line_send_expect (cptr, snd, NULL);
2885 }
2886
2887 t_stat tmxr_locate_line_expect (const char *cptr, EXPECT **exp)
2888 {
2889 return _tmxr_locate_line_send_expect (cptr, NULL, exp);
2890 }
2891
2892 t_stat tmxr_change_async (void)
2893 {
2894 return SCPE_OK;
2895 }
2896
2897
2898
2899 t_stat tmxr_attach_ex (TMXR *mp, UNIT *uptr, CONST char *cptr, t_bool async)
2900 {
2901 t_stat r;
2902 int32 i;
2903
2904 r = tmxr_open_master (mp, cptr);
2905 if (r != SCPE_OK)
2906 return r;
2907 mp->uptr = uptr;
2908 uptr->filename = tmxr_mux_attach_string (uptr->filename, mp);
2909 uptr->flags = uptr->flags | UNIT_ATT;
2910 uptr->tmxr = (void *)mp;
2911 if ((mp->lines > 1) ||
2912 ((mp->master == 0) &&
2913 (mp->ldsc[0].connecting == 0)
2914 ))
2915 uptr->dynflags = uptr->dynflags | UNIT_ATTMULT;
2916
2917 uptr->dynflags |= TMUF_NOASYNCH;
2918
2919 if (mp->dptr == NULL)
2920 mp->dptr = find_dev_from_unit (uptr);
2921
2922 if (mp->dptr) {
2923 for (i=0; i<mp->lines; i++) {
2924 mp->ldsc[i].expect.dptr = mp->dptr;
2925 mp->ldsc[i].expect.dbit = TMXR_DBG_EXP;
2926 mp->ldsc[i].send.dptr = mp->dptr;
2927 mp->ldsc[i].send.dbit = TMXR_DBG_SEND;
2928 }
2929 }
2930 tmxr_add_to_open_list (mp);
2931 return SCPE_OK;
2932 }
2933
2934 t_stat tmxr_startup (void)
2935 {
2936 return SCPE_OK;
2937 }
2938
2939 t_stat tmxr_shutdown (void)
2940 {
2941 if (tmxr_open_device_count)
2942 return SCPE_IERR;
2943 return SCPE_OK;
2944 }
2945
2946 t_stat tmxr_show_open_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc)
2947 {
2948 int i, j;
2949
2950 if (0 == tmxr_open_device_count)
2951 fprintf(st, "No Attached Multiplexer Devices\n");
2952 else {
2953 for (i=0; i<tmxr_open_device_count; ++i) {
2954 TMXR *mp = tmxr_open_devices[i];
2955 TMLN *lp;
2956 char *attach;
2957
2958 fprintf(st, "Multiplexer device: %s", (mp->dptr ? sim_dname (mp->dptr) : ""));
2959 if (mp->lines > 1) {
2960 fprintf(st, ", ");
2961 tmxr_show_lines(st, NULL, 0, mp);
2962 }
2963 if (mp->packet)
2964 fprintf(st, ", Packet");
2965 if (mp->datagram)
2966 fprintf(st, ", UDP");
2967 if (mp->notelnet)
2968 fprintf(st, ", Telnet=disabled");
2969 if (mp->modem_control)
2970 fprintf(st, ", ModemControl=enabled");
2971 if (mp->buffered)
2972 fprintf(st, ", Buffered=%d", mp->buffered);
2973 attach = tmxr_mux_attach_string (NULL, mp);
2974 if (attach)
2975 fprintf(st, ",\n attached to %s, ", attach);
2976 FREE (attach);
2977 tmxr_show_summ(st, NULL, 0, mp);
2978 fprintf(st, ", sessions=%d", mp->sessions);
2979 if (mp->lines == 1) {
2980 if (mp->ldsc->rxbps) {
2981 fprintf(st, ", Speed=%lu", (unsigned long)mp->ldsc->rxbps);
2982 if (mp->ldsc->rxbpsfactor != TMXR_RX_BPS_UNIT_SCALE)
2983 fprintf(st, "*%.0f", mp->ldsc->rxbpsfactor/TMXR_RX_BPS_UNIT_SCALE);
2984 fprintf(st, " bps");
2985 }
2986 }
2987 fprintf(st, "\n");
2988 if (mp->ring_start_time) {
2989 fprintf (st, " incoming Connection from: %s ringing for %lu milliseconds\n", mp->ring_ipad, (unsigned long)sim_os_msec () - (unsigned long)mp->ring_start_time);
2990 }
2991 for (j = 0; j < mp->lines; j++) {
2992 lp = mp->ldsc + j;
2993 if (mp->lines > 1) {
2994 if (lp->dptr && (mp->dptr != lp->dptr))
2995 fprintf (st, "Device: %s ", sim_dname(lp->dptr));
2996 fprintf (st, "Line: %d", j);
2997 if (mp->notelnet != lp->notelnet)
2998 fprintf (st, " - %stelnet", lp->notelnet ? "no" : "");
2999 if (lp->uptr && (lp->uptr != lp->mp->uptr))
3000 fprintf (st, " - Unit: %s", sim_uname (lp->uptr));
3001 if (mp->modem_control != lp->modem_control)
3002 fprintf(st, ", ModemControl=%s", lp->modem_control ? "enabled" : "disabled");
3003 if (lp->loopback)
3004 fprintf(st, ", Loopback");
3005 if (lp->rxbps) {
3006 fprintf(st, ", Speed=%lu", (unsigned long)lp->rxbps);
3007 if (lp->rxbpsfactor != TMXR_RX_BPS_UNIT_SCALE)
3008 fprintf(st, "*%.0f", lp->rxbpsfactor/TMXR_RX_BPS_UNIT_SCALE);
3009 fprintf(st, " bps");
3010 }
3011 fprintf (st, "\n");
3012 }
3013 if ((!lp->sock) && (!lp->connecting)
3014 && (!lp->master)) {
3015 if (lp->modem_control)
3016 tmxr_fconns (st, lp, -1);
3017 continue;
3018 }
3019 tmxr_fconns (st, lp, -1);
3020 tmxr_fstats (st, lp, -1);
3021 }
3022 }
3023 }
3024 return SCPE_OK;
3025 }
3026
3027
3028
3029
3030
3031
3032
3033
3034 t_stat tmxr_close_master (TMXR *mp)
3035 {
3036 int32 i;
3037 TMLN *lp;
3038
3039 for (i = 0; i < mp->lines; i++) {
3040 lp = mp->ldsc + i;
3041
3042 if (!lp->destination && lp->sock) {
3043 tmxr_report_disconnection (lp);
3044 tmxr_reset_ln (lp);
3045 }
3046 else {
3047 if (lp->sock) {
3048 tmxr_report_disconnection (lp);
3049 tmxr_reset_ln (lp);
3050 }
3051 FREE (lp->destination);
3052 lp->destination = NULL;
3053 if (lp->connecting) {
3054 lp->sock = lp->connecting;
3055 lp->connecting = 0;
3056 tmxr_reset_ln (lp);
3057 }
3058 lp->conn = FALSE;
3059 }
3060 if (lp->master) {
3061 sim_close_sock (lp->master);
3062 lp->master = 0;
3063 FREE (lp->port);
3064 lp->port = NULL;
3065 }
3066 lp->txbfd = 0;
3067 FREE (lp->txb);
3068 lp->txb = NULL;
3069 FREE (lp->rxb);
3070 lp->rxb = NULL;
3071 FREE (lp->rbr);
3072 lp->rbr = NULL;
3073 lp->modembits = 0;
3074 }
3075
3076 if (mp->master)
3077 sim_close_sock (mp->master);
3078 mp->master = 0;
3079 FREE (mp->port);
3080 mp->port = NULL;
3081 if (mp->ring_sock != INVALID_SOCKET) {
3082 sim_close_sock (mp->ring_sock);
3083 mp->ring_sock = INVALID_SOCKET;
3084 FREE (mp->ring_ipad);
3085 mp->ring_ipad = NULL;
3086 mp->ring_start_time = 0;
3087 }
3088 _tmxr_remove_from_open_list (mp);
3089 return SCPE_OK;
3090 }
3091
3092
3093
3094
3095
3096
3097 t_stat tmxr_detach (TMXR *mp, UNIT *uptr)
3098 {
3099 int32 i;
3100
3101 if (!(uptr->flags & UNIT_ATT))
3102 return SCPE_OK;
3103 tmxr_close_master (mp);
3104 FREE (uptr->filename);
3105 uptr->filename = NULL;
3106 uptr->tmxr = NULL;
3107 mp->last_poll_time = 0;
3108 for (i=0; i < mp->lines; i++) {
3109 UNIT *uptr = mp->ldsc[i].uptr ? mp->ldsc[i].uptr : mp->uptr;
3110 UNIT *o_uptr = mp->ldsc[i].o_uptr ? mp->ldsc[i].o_uptr : mp->uptr;
3111
3112 uptr->dynflags &= ~UNIT_TM_POLL;
3113 o_uptr->dynflags &= ~UNIT_TM_POLL;
3114 }
3115 uptr->flags &= ~(UNIT_ATT);
3116 uptr->dynflags &= ~(UNIT_TM_POLL|TMUF_NOASYNCH);
3117 return SCPE_OK;
3118 }
3119
3120 t_stat tmxr_activate (UNIT *uptr, int32 interval)
3121 {
3122 if (uptr->dynflags & UNIT_TMR_UNIT)
3123 return sim_timer_activate (uptr, interval);
3124 return _sim_activate (uptr, interval);
3125 }
3126
3127 t_stat tmxr_activate_after (UNIT *uptr, uint32 usecs_walltime)
3128 {
3129 return _sim_activate_after (uptr, usecs_walltime);
3130 }
3131
3132
3133
3134 t_stat tmxr_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
3135 {
3136 TMXR *mux = (TMXR *)dptr->help_ctx;
3137 t_bool single_line = FALSE;
3138
3139 if (mux)
3140 single_line = (mux->lines == 1);
3141
3142 if (!flag)
3143 fprintf (st, "%s Multiplexer Attach Help\n\n", dptr->name);
3144 if (single_line) {
3145 fprintf (st, "The %s multiplexer may be connected to terminal emulators supporting the\n", dptr->name);
3146 fprintf (st, "Telnet protocol via sockets.\n\n");
3147 if (mux->modem_control) {
3148 fprintf (st, "The %s device is a full modem control device and therefore is capable of\n", dptr->name);
3149 fprintf (st, "passing port configuration information and modem signals.\n");
3150 }
3151 fprintf (st, "A Telnet listening port can be configured with:\n\n");
3152 fprintf (st, " sim> ATTACH %s {interface:}port\n\n", dptr->name);
3153 fprintf (st, "Line buffering can be enabled for the %s device with:\n\n", dptr->name);
3154 fprintf (st, " sim> ATTACH %s Buffer{=bufsize}\n\n", dptr->name);
3155 fprintf (st, "Line buffering can be disabled for the %s device with:\n\n", dptr->name);
3156 fprintf (st, " sim> ATTACH %s NoBuffer\n\n", dptr->name);
3157 fprintf (st, "The default buffer size is 32k bytes, the max buffer size is 1024k bytes\n\n");
3158 fprintf (st, "The outbound traffic the %s device can be logged to a file with:\n", dptr->name);
3159 fprintf (st, " sim> ATTACH %s Log=LogFileName\n\n", dptr->name);
3160 fprintf (st, "File logging can be disabled for the %s device with:\n\n", dptr->name);
3161 fprintf (st, " sim> ATTACH %s NoLog\n\n", dptr->name);
3162 fprintf (st, "The %s device may be connected to a serial port on the host system.\n", dptr->name);
3163 }
3164 else {
3165 fprintf (st, "%s multiplexer lines may be connected to terminal emulators supporting the\n", dptr->name);
3166 fprintf (st, "Telnet protocol via sockets, or to hardware terminals via host serial\n");
3167 fprintf (st, "ports. Concurrent Telnet and serial connections may be mixed on a given\n");
3168 fprintf (st, "multiplexer.\n\n");
3169 if (mux && mux->modem_control) {
3170 fprintf (st, "The %s device is a full modem control device and therefore is capable of\n", dptr->name);
3171 fprintf (st, "passing port configuration information and modem signals on all lines.\n");
3172 }
3173 fprintf (st, "Modem Control signalling behaviors can be enabled/disabled on a specific\n");
3174 fprintf (st, "multiplexer line with:\n\n");
3175 fprintf (st, " sim> ATTACH %s Line=n,Modem\n", dptr->name);
3176 fprintf (st, " sim> ATTACH %s Line=n,NoModem\n\n", dptr->name);
3177 fprintf (st, "A Telnet listening port can be configured with:\n\n");
3178 fprintf (st, " sim> ATTACH %s {interface:}port\n\n", dptr->name);
3179 if (mux)
3180 fprintf (st, "Line buffering for all %d lines on the %s device can be configured with:\n\n", mux->lines, dptr->name);
3181 else
3182 fprintf (st, "Line buffering for all lines on the %s device can be configured with:\n\n", dptr->name);
3183 fprintf (st, " sim> ATTACH %s Buffer{=bufsize}\n\n", dptr->name);
3184 if (mux)
3185 fprintf (st, "Line buffering for all %d lines on the %s device can be disabled with:\n\n", mux->lines, dptr->name);
3186 else
3187 fprintf (st, "Line buffering for all lines on the %s device can be disabled with:\n\n", dptr->name);
3188 fprintf (st, " sim> ATTACH %s NoBuffer\n\n", dptr->name);
3189 fprintf (st, "The default buffer size is 32k bytes, the max buffer size is 1024k bytes\n\n");
3190 fprintf (st, "The outbound traffic for the lines of the %s device can be logged to files\n", dptr->name);
3191 fprintf (st, "with:\n\n");
3192 fprintf (st, " sim> ATTACH %s Log=LogFileName\n\n", dptr->name);
3193 fprintf (st, "The log file name for each line uses the above LogFileName as a template\n");
3194 fprintf (st, "for the actual file name which will be LogFileName_n where n is the line\n");
3195 fprintf (st, "number.\n\n");
3196 fprintf (st, "Multiplexer lines may be connected to serial ports on the host system.\n");
3197 }
3198 fprintf (st, "Serial ports may be specified as an operating system specific device names\n");
3199 fprintf (st, "or using simh generic serial names. simh generic names are of the form\n");
3200 fprintf (st, "serN, where N is from 0 thru one less than the maximum number of serial\n");
3201 fprintf (st, "ports on the local system. The mapping of simh generic port names to OS \n");
3202 fprintf (st, "specific names can be displayed using the following command:\n\n");
3203 fprintf (st, " sim> SHOW SERIAL\n");
3204 fprintf (st, " Serial devices:\n");
3205 fprintf (st, " ser0 COM1 (\\Device\\Serial0)\n");
3206 fprintf (st, " ser1 COM3 (Winachcf0)\n\n");
3207 if (single_line) {
3208 fprintf (st, " sim> ATTACH %s Connect=ser0\n\n", dptr->name);
3209 fprintf (st, "or equivalently:\n\n");
3210 fprintf (st, " sim> ATTACH %s Connect=COM1\n\n", dptr->name);
3211 }
3212 else {
3213 fprintf (st, " sim> ATTACH %s Line=n,Connect=ser0\n\n", dptr->name);
3214 fprintf (st, "or equivalently:\n\n");
3215 fprintf (st, " sim> ATTACH %s Line=n,Connect=COM1\n\n", dptr->name);
3216 if (mux)
3217 fprintf (st, "Valid line numbers are from 0 thru %d\n\n", mux->lines-1);
3218 }
3219 if (single_line) {
3220 fprintf (st, "The input data rate for the %s device can be controlled by\n", dptr->name);
3221 fprintf (st, "specifying SPEED=nnn{*fac} on the ATTACH command.\n");
3222 }
3223 else {
3224 fprintf (st, "The input data rate for all lines or a particular line of a the %s\n", dptr->name);
3225 fprintf (st, "device can be controlled by specifying SPEED=nnn{*fac} on the ATTACH command.\n");
3226 }
3227 fprintf (st, "SPEED values can be any one of:\n\n");
3228 fprintf (st, " 0 50 75 110 134 150 300 600 1200 1800 2000 2400\n");
3229 fprintf (st, " 3600 4800 7200 9600 19200 38400 57600 76800 115200\n\n");
3230 fprintf (st, "A SPEED value of 0 causes input data to be delivered to the simulated\n");
3231 fprintf (st, "port as fast as it arrives.\n\n");
3232 fprintf (st, "If a simulated multiplexor devices can programmatically set a serial\n");
3233 fprintf (st, "port line speed, the programmatically specified speed will take precedence\n");
3234 fprintf (st, "over any input speed specified on an attach command.\n");
3235 fprintf (st, "Some simulated systems run very much faster than the original system\n");
3236 fprintf (st, "which is being simulated. To accommodate this, the speed specified may\n");
3237 fprintf (st, "include a factor which will increase the input data delivery rate by\n");
3238 fprintf (st, "the specified factor. A factor is specified with a speed value of the\n");
3239 fprintf (st, "form \"speed*factor\". Factor values can range from 1 thru 32.\n");
3240 fprintf (st, "Example:\n\n");
3241 fprintf (st, " sim> ATTACH %s 1234,SPEED=2400\n", dptr->name);
3242 fprintf (st, " sim> ATTACH %s 1234,SPEED=9600*8\n", dptr->name);
3243 if (!single_line)
3244 fprintf (st, " sim> ATTACH %s Line=2,SPEED=2400\n", dptr->name);
3245 fprintf (st, "\n");
3246 fprintf (st, "The SPEED parameter only influences the rate at which data is delivered\n");
3247 fprintf (st, "into the simulated multiplexor port. Output data rates are unaffected\n");
3248 fprintf (st, "If an attach command specifies a speed multiply factor, that value will\n");
3249 fprintf (st, "persist independent of any programmatic action by the simulated system to\n");
3250 fprintf (st, "change the port speed.\n\n");
3251 fprintf (st, "An optional serial port configuration string may be present after the port\n");
3252 fprintf (st, "name. If present, it must be separated from the port name with a semicolon\n");
3253 fprintf (st, "and has this form:\n\n");
3254 fprintf (st, " <rate>-<charsize><parity><stopbits>\n\n");
3255 fprintf (st, "where:\n");
3256 fprintf (st, " rate = communication rate in bits per second\n");
3257 fprintf (st, " charsize = character size in bits (5-8, including optional parity)\n");
3258 fprintf (st, " parity = parity designator (N/E/O/M/S for no/even/odd/mark/space parity)\n");
3259 fprintf (st, " stopbits = number of stop bits (1, 1.5, or 2)\n\n");
3260 fprintf (st, "As an example:\n\n");
3261 fprintf (st, " 9600-8n1\n\n");
3262 fprintf (st, "The supported rates, sizes, and parity options are host-specific. If\n");
3263 fprintf (st, "a configuration string is not supplied, then the default of 9600-8N1\n");
3264 fprintf (st, "is used.\n");
3265 fprintf (st, "Note: The serial port configuration option is only available on multiplexer\n");
3266 fprintf (st, " lines which are not operating with full modem control behaviors enabled.\n");
3267 fprintf (st, " Lines with full modem control behaviors enabled have all of their\n");
3268 fprintf (st, " configuration managed by the Operating System running within the\n");
3269 fprintf (st, " simulator.\n\n");
3270 fprintf (st, "An attachment to a serial port with the '-V' switch will cause a\n");
3271 fprintf (st, "connection message to be output to the connected serial port.\n");
3272 fprintf (st, "This will help to confirm the correct port has been connected and\n");
3273 fprintf (st, "that the port settings are reasonable for the connected device.\n");
3274 fprintf (st, "This would be done as:\n\n");
3275 if (single_line)
3276 fprintf (st, " sim> ATTACH -V %s Connect=SerN\n", dptr->name);
3277 else {
3278 fprintf (st, " sim> ATTACH -V %s Line=n,Connect=SerN\n\n", dptr->name);
3279 fprintf (st, "Line specific tcp listening ports are supported. These are configured\n");
3280 fprintf (st, "using commands of the form:\n\n");
3281 fprintf (st, " sim> ATTACH %s Line=n,{interface:}port{;notelnet}\n\n", dptr->name);
3282 }
3283 fprintf (st, "Direct computer to computer connections (Virtual Null Modem cables) may\n");
3284 fprintf (st, "be established using the telnet protocol or via raw tcp sockets.\n\n");
3285 fprintf (st, " sim> ATTACH %s Line=n,Connect=host:port{;notelnet}\n\n", dptr->name);
3286 fprintf (st, "Computer to computer virtual connections can be one way (as illustrated\n");
3287 fprintf (st, "above) or symmetric. A symmetric connection is configured by combining\n");
3288 if (single_line) {
3289 fprintf (st, "a one way connection with a tcp listening port on the same line:\n\n");
3290 fprintf (st, " sim> ATTACH %s listenport,Connect=host:port\n\n", dptr->name);
3291 }
3292 else {
3293 fprintf (st, "a one way connection with a tcp listening port on the same line:\n\n");
3294 fprintf (st, " sim> ATTACH %s Line=n,listenport,Connect=host:port\n\n", dptr->name);
3295 }
3296 fprintf (st, "When symmetric virtual connections are configured, incoming connections\n");
3297 fprintf (st, "on the specified listening port are checked to assure that they actually\n");
3298 fprintf (st, "come from the specified connection destination host system.\n\n");
3299 if (single_line) {
3300 fprintf (st, "The %s device can be attached in LOOPBACK mode:\n\n", dptr->name);
3301 fprintf (st, " sim> ATTACH %s Loopback\n\n", dptr->name);
3302 }
3303 else {
3304 fprintf (st, "A line on the %s device can be attached in LOOPBACK mode:\n\n", dptr->name);
3305 fprintf (st, " sim> ATTACH %s Line=n,Loopback\n\n", dptr->name);
3306 }
3307 fprintf (st, "When operating in LOOPBACK mode, all outgoing data arrives as input and\n");
3308 fprintf (st, "outgoing modem signals (if enabled) (DTR and RTS) are reflected in the\n");
3309 fprintf (st, "incoming modem signals (DTR->(DCD and DSR), RTS->CTS)\n\n");
3310 if (single_line)
3311 fprintf (st, "The connection configured for the %s device is unconfigured by:\n\n", dptr->name);
3312 else
3313 fprintf (st, "All connections configured for the %s device are unconfigured by:\n\n", dptr->name);
3314 fprintf (st, " sim> DETACH %s\n\n", dptr->name);
3315 if (dptr->modifiers) {
3316 MTAB *mptr;
3317
3318 for (mptr = dptr->modifiers; mptr->mask != 0; mptr++)
3319 if (mptr->valid == &tmxr_dscln) {
3320 fprintf (st, "A specific line on the %s device can be disconnected with:\n\n", dptr->name);
3321 fprintf (st, " sim> SET %s %s=n\n\n", dptr->name, mptr->mstring);
3322 fprintf (st, "This will cause a telnet connection to be closed, but a serial port will\n");
3323 fprintf (st, "normally have DTR dropped for 500ms and raised again (thus hanging up a\n");
3324 fprintf (st, "modem on that serial port).\n\n");
3325 fprintf (st, "A line which is connected to a serial port can be manually closed by\n");
3326 fprintf (st, "adding the -C switch to a %s command.\n\n", mptr->mstring);
3327 fprintf (st, " sim> SET -C %s %s=n\n\n", dptr->name, mptr->mstring);
3328 }
3329 }
3330 return SCPE_OK;
3331 }
3332
3333
3334
3335 t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
3336 {
3337 return SCPE_NOFNC;
3338 }
3339
3340 t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
3341 {
3342 return SCPE_NOFNC;
3343 }
3344
3345
3346
3347 void tmxr_msg (SOCKET sock, const char *msg)
3348 {
3349 if ((sock) && (sock != INVALID_SOCKET))
3350 sim_write_sock (sock, msg, (int32)strlen (msg));
3351 return;
3352 }
3353
3354
3355
3356 void tmxr_linemsg (TMLN *lp, const char *msg)
3357 {
3358 while (*msg) {
3359 while (SCPE_STALL == tmxr_putc_ln (lp, (int32)(*msg)))
3360 if (lp->txbsz == tmxr_send_buffered_data (lp))
3361 sim_os_ms_sleep (10);
3362 ++msg;
3363 }
3364 return;
3365 }
3366
3367
3368
3369 void tmxr_linemsgf (TMLN *lp, const char *fmt, ...)
3370 {
3371 va_list arglist;
3372
3373 va_start (arglist, fmt);
3374 tmxr_linemsgvf (lp, fmt, arglist);
3375 va_end (arglist);
3376 }
3377
3378 void tmxr_linemsgvf (TMLN *lp, const char *fmt, va_list arglist)
3379 {
3380 char stackbuf[STACKBUFSIZE];
3381 int32 bufsize = sizeof(stackbuf);
3382 char *buf = stackbuf;
3383 int32 i, len;
3384
3385 buf[bufsize-1] = '\0';
3386 while (1) {
3387 len = vsnprintf (buf, bufsize-1, fmt, arglist);
3388
3389
3390
3391 if ((len < 0) || (len >= bufsize-1)) {
3392 if (buf != stackbuf)
3393 FREE (buf);
3394 bufsize = bufsize * 2;
3395 if (bufsize < len + 2)
3396 bufsize = len + 2;
3397 buf = (char *) malloc (bufsize);
3398 if (!buf)
3399 {
3400 fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
3401 __func__, __FILE__, __LINE__);
3402 #if defined(USE_BACKTRACE)
3403 # ifdef SIGUSR2
3404 (void)raise(SIGUSR2);
3405
3406 # endif
3407 #endif
3408 abort();
3409 }
3410 buf[bufsize-1] = '\0';
3411 continue;
3412 }
3413 break;
3414 }
3415
3416
3417
3418 for (i = 0; i < len; ++i) {
3419 if (('\n' == buf[i]) && ((i == 0) || ('\r' != buf[i-1]))) {
3420 while (SCPE_STALL == tmxr_putc_ln (lp, '\r'))
3421 if (lp->txbsz == tmxr_send_buffered_data (lp))
3422 sim_os_ms_sleep (10);
3423 }
3424 while (SCPE_STALL == tmxr_putc_ln (lp, buf[i]))
3425 if (lp->txbsz == tmxr_send_buffered_data (lp))
3426 sim_os_ms_sleep (10);
3427 }
3428 if (buf != stackbuf)
3429 FREE (buf);
3430 return;
3431 }
3432
3433
3434
3435 void tmxr_fconns (FILE *st, const TMLN *lp, int32 ln)
3436 {
3437 int32 hr, mn, sc;
3438 uint32 tctime;
3439
3440 if (ln >= 0)
3441 fprintf (st, "line %d: ", ln);
3442
3443 if ((lp->sock) || (lp->connecting)) {
3444 if (lp->destination)
3445 if (lp->datagram)
3446 fprintf (st, "Datagram Connection from %s to remote port %s\n", lp->port, lp->destination);
3447 else
3448 fprintf (st, "Connection to remote port %s\n", lp->destination);
3449 else
3450 fprintf (st, "Connection from IP address %s\n", lp->ipad);
3451 }
3452 else
3453 if (lp->destination)
3454 fprintf (st, "Connecting to remote port %s\n", lp->destination);
3455 if (lp->sock) {
3456 char *sockname, *peername;
3457
3458 sim_getnames_sock (lp->sock, &sockname, &peername);
3459 fprintf (st, "Connection %s->%s\n", sockname, peername);
3460 FREE (sockname);
3461 FREE (peername);
3462 }
3463
3464 if ((lp->port) && (!lp->datagram))
3465 fprintf (st, "Listening on port %s\n", lp->port);
3466 if (lp->cnms) {
3467 tctime = (sim_os_msec () - lp->cnms) / 1000;
3468 hr = tctime / 3600;
3469 mn = (tctime / 60) % 60;
3470 sc = tctime % 60;
3471 if (tctime)
3472 fprintf (st, " %s %02d:%02d:%02d\n", lp->connecting ? "Connecting for" : "Connected", hr, mn, sc);
3473 }
3474 else
3475 fprintf (st, " Line disconnected\n");
3476
3477 if (lp->modem_control) {
3478 fprintf (st, " Modem Bits: %s%s%s%s%s%s\n", (lp->modembits & TMXR_MDM_DTR) ? "DTR " : "",
3479 (lp->modembits & TMXR_MDM_RTS) ? "RTS " : "",
3480 (lp->modembits & TMXR_MDM_DCD) ? "DCD " : "",
3481 (lp->modembits & TMXR_MDM_RNG) ? "RNG " : "",
3482 (lp->modembits & TMXR_MDM_CTS) ? "CTS " : "",
3483 (lp->modembits & TMXR_MDM_DSR) ? "DSR " : "");
3484 }
3485
3486 if (
3487 (lp->sock) && (!lp->datagram))
3488 fprintf (st, " %s\n", (lp->notelnet) ? "Telnet disabled (RAW data)" : "Telnet protocol");
3489 if (lp->send.buffer)
3490 sim_show_send_input (st, &lp->send);
3491 if (lp->expect.buf)
3492 sim_exp_showall (st, &lp->expect);
3493 if (lp->txlog)
3494 fprintf (st, " Logging to %s\n", lp->txlogname);
3495 return;
3496 }
3497
3498
3499
3500 void tmxr_fstats (FILE *st, const TMLN *lp, int32 ln)
3501 {
3502 static const char *enab = "on";
3503 static const char *dsab = "off";
3504
3505 if (ln >= 0)
3506 fprintf (st, "Line %d:", ln);
3507 if ((!lp->sock) && (!lp->connecting)
3508 )
3509 fprintf (st, " not connected\n");
3510 else {
3511 if (ln >= 0)
3512 fprintf (st, "\n");
3513 fprintf (st, " input (%s)", (lp->rcve? enab: dsab));
3514 if (lp->rxcnt)
3515 fprintf (st, " queued/total = %d/%d", tmxr_rqln (lp), lp->rxcnt);
3516 if (lp->rxpcnt)
3517 fprintf (st, " packets = %d", lp->rxpcnt);
3518 fprintf (st, "\n output (%s)", (lp->xmte? enab: dsab));
3519 if (lp->txcnt || lp->txbpi)
3520 fprintf (st, " queued/total = %d/%d", tmxr_tqln (lp), lp->txcnt);
3521 if (lp->txpcnt || tmxr_tpqln (lp))
3522 fprintf (st, " packet data queued/packets sent = %d/%d",
3523 tmxr_tpqln (lp), lp->txpcnt);
3524 fprintf (st, "\n");
3525 }
3526 if (lp->txbfd)
3527 fprintf (st, " output buffer size = %d\n", lp->txbsz);
3528 if (lp->txcnt || lp->txbpi)
3529 fprintf (st, " bytes in buffer = %d\n",
3530 ((lp->txcnt > 0) && (lp->txcnt > lp->txbsz)) ? lp->txbsz : lp->txbpi);
3531 if (lp->txdrp)
3532 fprintf (st, " dropped = %d\n", lp->txdrp);
3533 return;
3534 }
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562 t_stat tmxr_dscln (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
3563 {
3564 TMXR *mp = (TMXR *) desc;
3565 TMLN *lp;
3566 t_stat status;
3567
3568 if (val)
3569 uptr = NULL;
3570
3571 lp = tmxr_get_ldsc (uptr, cptr, mp, &status);
3572
3573 if (lp == NULL)
3574 return status;
3575
3576 if ((lp->sock)
3577 ) {
3578 if (!lp->notelnet)
3579 tmxr_linemsg (lp, "\r\nOperator disconnected line\r\n\n");
3580 tmxr_reset_ln_ex (lp, (sim_switches & SWMASK ('C')));
3581 }
3582
3583 return SCPE_OK;
3584 }
3585
3586
3587
3588 t_stat tmxr_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
3589 {
3590 TMXR *mp = (TMXR *) desc;
3591 TMLN *lp;
3592
3593 if (cptr == NULL)
3594 return SCPE_2FARG;
3595 lp = tmxr_find_ldsc (uptr, val, mp);
3596 if (lp == NULL)
3597 return SCPE_IERR;
3598 if (lp->txlog)
3599 tmxr_set_nolog (NULL, val, NULL, desc);
3600 lp->txlogname = (char *) calloc (CBUFSIZE, sizeof (char));
3601 if (lp->txlogname == NULL)
3602 return SCPE_MEM;
3603 strncpy (lp->txlogname, cptr, CBUFSIZE-1);
3604 sim_open_logfile (cptr, TRUE, &lp->txlog, &lp->txlogref);
3605 if (lp->txlog == NULL) {
3606 FREE (lp->txlogname);
3607 return SCPE_OPENERR;
3608 }
3609 if (mp->uptr)
3610 lp->mp->uptr->filename = tmxr_mux_attach_string (lp->mp->uptr->filename, lp->mp);
3611 return SCPE_OK;
3612 }
3613
3614
3615
3616 t_stat tmxr_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
3617 {
3618 TMXR *mp = (TMXR *) desc;
3619 TMLN *lp;
3620
3621 if (cptr)
3622 return SCPE_2MARG;
3623 lp = tmxr_find_ldsc (uptr, val, mp);
3624 if (lp == NULL)
3625 return SCPE_IERR;
3626 if (lp->txlog) {
3627 sim_close_logfile (&lp->txlogref);
3628 FREE (lp->txlogname);
3629 lp->txlog = NULL;
3630 lp->txlogname = NULL;
3631 }
3632 if (mp->uptr)
3633 lp->mp->uptr->filename = tmxr_mux_attach_string (lp->mp->uptr->filename, lp->mp);
3634 return SCPE_OK;
3635 }
3636
3637
3638
3639 t_stat tmxr_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
3640 {
3641 const TMXR *mp = (const TMXR *) desc;
3642 TMLN *lp;
3643
3644 lp = tmxr_find_ldsc (uptr, val, mp);
3645 if (lp == NULL)
3646 return SCPE_IERR;
3647 if (lp->txlog)
3648 fprintf (st, "logging to %s", lp->txlogname);
3649 else fprintf (st, "no logging");
3650 return SCPE_OK;
3651 }
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681 t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, CONST char *carg, void *desc)
3682 {
3683 TMXR *mp = (TMXR *) desc;
3684 char *tbuf;
3685 char *tptr;
3686 CONST char *cptr;
3687 t_addr low, high, max = (t_addr) mp->lines - 1;
3688 int32 *list;
3689 t_bool *set;
3690 uint32 line, idx = 0;
3691 t_stat result = SCPE_OK;
3692
3693 if (mp->lnorder == NULL)
3694 return SCPE_NXPAR;
3695
3696 else if ((carg == NULL) || (*carg == '\0'))
3697 return SCPE_MISVAL;
3698
3699 list = (int32 *) calloc (mp->lines, sizeof (int32));
3700 if (list == NULL)
3701 return SCPE_MEM;
3702
3703 set = (t_bool *) calloc (mp->lines, sizeof (t_bool));
3704 if (set == NULL) {
3705 FREE (list);
3706 return SCPE_MEM;
3707 }
3708
3709 tbuf = (char *) calloc (strlen(carg)+2, sizeof(*carg));
3710 if (tbuf == NULL) {
3711 FREE(set);
3712 FREE(list);
3713 return SCPE_MEM;
3714 }
3715 strcpy (tbuf, carg);
3716 tptr = tbuf + strlen (tbuf);
3717 *tptr++ = ';';
3718 *tptr = '\0';
3719 cptr = tbuf;
3720
3721 while (*cptr) {
3722 cptr = get_range (NULL, cptr, &low, &high, 10, max, ';');
3723
3724 if (cptr == NULL) {
3725 result = SCPE_ARG;
3726 break;
3727 }
3728
3729 else if ((low > max) || (high > max)) {
3730 result = SCPE_SUB;
3731 break;
3732 }
3733
3734 else if ((low == 0) && (high == max)) {
3735 list [0] = -1;
3736 idx = (uint32) max + 1;
3737 break;
3738 }
3739
3740 else
3741 for (line = (uint32) low; line <= (uint32) high; line++)
3742 if (set [line] == FALSE) {
3743 set [line] = TRUE;
3744 list [idx] = line;
3745 idx = idx + 1;
3746 }
3747 }
3748
3749 if (result == SCPE_OK) {
3750 if (idx <= max)
3751 for (line = 0; line <= max; line++)
3752 if (set [line] == FALSE) {
3753 list [idx] = line;
3754 idx = idx + 1;
3755 }
3756
3757 memcpy (mp->lnorder, list, mp->lines * sizeof (int32));
3758 }
3759
3760 FREE (list);
3761 FREE (set);
3762 FREE (tbuf);
3763
3764 return result;
3765 }
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782 t_stat tmxr_show_lnorder (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
3783 {
3784 int32 i, j, low, last;
3785 const TMXR *mp = (const TMXR *) desc;
3786 int32 *iptr = mp->lnorder;
3787 t_bool first = TRUE;
3788
3789 if (iptr == NULL)
3790 return SCPE_NXPAR;
3791
3792 if (*iptr < 0)
3793 fprintf (st, "Order=0-%d\n", mp->lines - 1);
3794
3795 else {
3796 low = last = *iptr++;
3797
3798 for (j = 1; j <= mp->lines; j++) {
3799 if (j < mp->lines)
3800 i = *iptr++;
3801 else
3802 i = -1;
3803
3804 if (i != last + 1) {
3805 if (first) {
3806 fputs ("Order=", st);
3807 first = FALSE;
3808 }
3809
3810 else
3811 fputc (';', st);
3812
3813 if (low == last)
3814 fprintf (st, "%d", last);
3815
3816 else
3817 fprintf (st, "%d-%d", low, last);
3818
3819 low = i;
3820 }
3821
3822 last = i;
3823 }
3824 }
3825
3826 if (first == FALSE)
3827 fputc ('\n', st);
3828
3829 return SCPE_OK;
3830 }
3831
3832
3833
3834 t_stat tmxr_show_summ (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
3835 {
3836 const TMXR *mp = (const TMXR *) desc;
3837 int32 i, t;
3838
3839 if (mp == NULL)
3840 return SCPE_IERR;
3841 for (i = t = 0; i < mp->lines; i++)
3842 if ((mp->ldsc[i].sock != 0)
3843 )
3844 t = t + 1;
3845 if (mp->lines > 1)
3846 fprintf (st, "%d current connection%s", t, (t != 1) ? "s" : "");
3847 else
3848 fprintf (st, "%s", (t == 1) ? "connected" : "disconnected");
3849 return SCPE_OK;
3850 }
3851
3852
3853
3854 t_stat tmxr_show_cstat (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
3855 {
3856 const TMXR *mp = (const TMXR *) desc;
3857 int32 i, any;
3858
3859 if (mp == NULL)
3860 return SCPE_IERR;
3861 for (i = any = 0; i < mp->lines; i++) {
3862 if ((mp->ldsc[i].sock != 0)
3863 || mp->ldsc[i].modem_control) {
3864 if ((mp->ldsc[i].sock != 0)
3865 )
3866 any++;
3867 if (val)
3868 tmxr_fconns (st, &mp->ldsc[i], i);
3869 else
3870 if ((mp->ldsc[i].sock != 0)
3871 )
3872 tmxr_fstats (st, &mp->ldsc[i], i);
3873 }
3874 }
3875 if (any == 0)
3876 fprintf (st, (mp->lines == 1? "disconnected\n": "all disconnected\n"));
3877 return SCPE_OK;
3878 }
3879
3880
3881
3882 t_stat tmxr_show_lines (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
3883 {
3884 const TMXR *mp = (const TMXR *) desc;
3885
3886 if (mp == NULL)
3887 return SCPE_IERR;
3888 fprintf (st, "lines=%d", mp->lines);
3889 return SCPE_OK;
3890 }