11 #include <sys/ioctl.h>
14 #include <asm/termbits.h>
15 #include <asm/ioctls.h>
16 #else /* needed for termios2, which is itself needed for non-standard bauds */
27 #if defined(CCDTR_IFLOW) && defined(CDSR_OFLOW)
28 #define CDTRDSR (CDTR_IFLOW | CDSR_OFLOW)
30 #define IXONXOFF (IXON | IXOFF)
39 static int gettermattr(int dv
, void *strct
);
40 static int settermattr(int dv
, const void *strct
);
41 static void settermspd(unsigned int lispeed
, unsigned int lospeed
, const void *strct
);
42 static int openport();
43 static unsigned int strtoui(const char *str
, const char *msg
, unsigned int min
);
44 static int troptions(CRLFOpt
*options
, const char *str
);
45 static int setroptions();
46 static inline int termchck(const void *term
);
47 static void interchck();
49 static void *writeport(void *unused
);
50 static void *readport(void *unused
);
51 static void getcmd(int escape
);
52 static inline void replacechar(char *buff
, ssize_t sread
, const Args
*args
);
53 static inline ssize_t
addchar(char *buff
, ssize_t sread
, int *scratch
, const Args
*args
);
54 static inline ssize_t
rmchar(char *buff
, ssize_t sread
, int *scratch
, const Args
*args
);
55 static void sighandl(int signo
);
56 static void die(int code
, const char *msg
, ...);
59 static struct termios2 cntrl
, origterm
= {0}, newterm
= {0};
61 static struct termios cntrl
, origterm
= {0}, newterm
= {0};
64 static const unsigned int uintmax
= ~(unsigned int)0; /* unsigned -1 to return on error */
65 static struct timespec wts
;
66 static char *writebuff
= NULL
;
67 static char *readbuff
= NULL
;
68 static int *scratchr
= NULL
;
69 static int *scratchw
= NULL
;
70 static char backspc
= DEL
,tbackspc
= DEL
;
72 static int interactive
= 0;
75 gettermattr(int dv
, void *strct
)
78 struct termios2
*optst
= (struct termios2
*)strct
;
79 return ioctl(dv
, TCGETS2
, optst
);
81 struct termios
*optst
= (struct termios
*)strct
;
82 return tcgetattr(dv
, optst
);
87 settermattr(int dv
, const void *strct
)
90 struct termios2
*optst
= (struct termios2
*)strct
;
91 return ioctl(dv
, TCSETS2
, optst
);
93 struct termios
*optst
= (struct termios
*)strct
;
94 return tcsetattr(dv
, TCSANOW
, optst
);
99 settermspd(unsigned int lispeed
, unsigned int lospeed
, const void *strct
)
102 struct termios2
*optst
= (struct termios2
*)strct
;
104 optst
->c_cflag
&= ~CBAUD
;
105 optst
->c_cflag
|= BOTHER
;
106 optst
->c_ispeed
= ispeed
;
107 optst
->c_ospeed
= ospeed
;
109 struct termios
*optst
= (struct termios
*)strct
;
110 cfsetispeed(optst
, ispeed
);
111 cfsetospeed(optst
, ospeed
);
118 if (verbose
) fprintf(stderr
, "opening \"%s\"\n", line
);
120 fd
= open(line
, O_RDWR
| O_NOCTTY
| O_NDELAY
);
122 perror("error opening device");
126 if (verbose
) fprintf(stderr
, "checking if \"%s\" is a TTY\n", line
);
129 die(2, "device \"%s\" is not a TTY\n", line
);
132 flags
= fcntl(fd
, F_GETFL
, 0);
133 /* opened to check with non-blocking mode, now set to blocking */
134 flags
&= ~(O_NONBLOCK
| O_NDELAY
);
135 if (fcntl(fd
, F_SETFL
, flags
) == -1)
136 die(1, "failed to set the device to blocking mode\n");
138 if (gettermattr(fd
, &cntrl
) == -1)
139 die(1, "failed to get device attributes\n");
141 if (verbose
) fprintf(stderr
, "setting baudrate [RX:%u | TX:%u]\n", ispeed
, ospeed
);
143 settermspd(ispeed
, ospeed
, &cntrl
);
144 if (settermattr(fd
, &cntrl
) == -1)
145 die(2,"failed to set baudrate [RX:%u | TX:%u]\n", ispeed
, ospeed
);
148 cntrl
.c_iflag
&= ~(ISTRIP
| BRKINT
);
149 cntrl
.c_cflag
&= ~(PARENB
| PARODD
);
151 if (verbose
) fprintf(stderr
, "setting parity [even: %d, odd: %d]\n", parity
& 1, (parity
& 2) >> 1);
154 cntrl
.c_cflag
|= PARENB
;
156 cntrl
.c_cflag
|= PARENB
| PARODD
;
158 if (settermattr(fd
, &cntrl
) == -1)
159 die(2, "failed to set parity [even: %d, odd: %d]\n", parity
& 1, (parity
& 2) >> 1);
162 /* it's so ugly and beautiful at the same time */
163 int t
= (((hard
& 1) << 1) & (hard
& 2)) >> 1;
164 fprintf(stderr
, "setting flow control [XON/XOFF: %d, RTS/CTS: %d, DTR/DSR: %d, DCD: %d]\n",\
165 soft
, t
^ ((hard
& 1) << 1) >> 1, t
^ (hard
& 2) >> 1, t
);
168 cntrl
.c_cflag
|= CLOCAL
;
169 cntrl
.c_iflag
&= ~IXONXOFF
;
172 cntrl
.c_iflag
|= IXONXOFF
;
175 cntrl
.c_cflag
|= CRTSCTS
;
176 } else if (hard
== 2) {
178 fprintf(stderr
, "DTR/DSR flow control is not supported on this platform\n"
179 "enabling this option does nothing!\n");
181 cntrl
.c_lflag
|= CDTRDSR
;
183 } else if (hard
== 3) {
184 cntrl
.c_cflag
&= ~CLOCAL
;
186 cntrl
.c_lflag
|= CCAR_OFLOW
;
190 if (settermattr(fd
, &cntrl
) == -1)
191 die(2, "failed to set flow control\n");
194 die(2, "failed to set cr-lf translation options\n");
196 if (verbose
) fprintf(stderr
, "setting data bits [%c]\n", datab
);
209 cntrl
.c_cflag
&= ~CSIZE
;
212 if (settermattr(fd
, &cntrl
) == -1)
213 die(2, "failed to set data bits [%c]\n", datab
);
215 if (verbose
) fprintf(stderr
, "setting stop bits [%d]\n", stopb
+1);
218 cntrl
.c_cflag
|= CSTOPB
;
220 cntrl
.c_cflag
&= ~CSTOPB
;
222 if (settermattr(fd
, &cntrl
) == -1)
223 die(1, "failed to set stop bits [%d]\n", stopb
+1);
226 ioctl(fd
, TCFLSH
, TCIOFLUSH
);
228 tcflush(fd
, TCIOFLUSH
);
234 strtoui(const char *str
, const char *msg
, unsigned int min
)
239 t
= strtol(str
, &endptr
, 10);
240 if (errno
!= 0 || *endptr
!= '\0' || t
< min
) {
241 fprintf(stderr
, msg
, str
);
245 * conversion like this results in 'undefined behavior' according to C spec,
246 * but almost all compilers will just truncate the value so it's OK
248 return (unsigned int)t
;
249 /* termios's `speed_t` is a typedef for `unsigned int` */
253 troptions(CRLFOpt
*options
, const char *str
)
257 if (strcmp(str
, "lf-in-cr") == 0) {
258 options
->lfincr
= !options
->lfincr
; return 0;
259 } else if (strcmp(str
, "lf-to-cr") == 0) {
260 options
->lftocr
= !options
->lftocr
; return 0;
266 if (strcmp(str
, "cr-in-lf") == 0) {
267 options
->crinlf
= !options
->crinlf
; return 0;
268 } else if (strcmp(str
, "cr-to-lf") == 0) {
269 options
->crtolf
= !options
->crtolf
; return 0;
275 if (strcmp(str
, "no-lf") == 0) {
276 options
->nolf
= !options
->nolf
; return 0;
277 } else if (strcmp(str
, "no-cr") == 0) {
278 options
->nocr
= !options
->nocr
; return 0;
285 fprintf(stderr
, "invalid translation option: %s\n", str
);
294 cntrl
.c_iflag
|= ICRNL
;
296 cntrl
.c_iflag
&= ~ICRNL
;
299 cntrl
.c_iflag
|= INLCR
;
301 cntrl
.c_iflag
&= ~INLCR
;
304 cntrl
.c_oflag
|= OCRNL
;
306 cntrl
.c_oflag
&= ~OCRNL
;
309 cntrl
.c_oflag
|= ONLRET
;
311 cntrl
.c_oflag
&= ~ONLRET
;
314 cntrl
.c_oflag
|= ONLCR
;
316 cntrl
.c_oflag
&= ~ONLCR
;
318 return settermattr(fd
, &cntrl
);
322 termchck(const void *term
)
325 struct termios2
*iterm
= (struct termios2
*)term
;
326 #define TERMIOS_STRUCT termios2
328 struct termios
*iterm
= (struct termios
*)term
;
329 #define TERMIOS_STRUCT termios
331 for (size_t i
= 0; i
< sizeof(struct TERMIOS_STRUCT
); i
++)
332 if (((char*)iterm
)[i
] != 0)
339 writeport(void *unused
)
342 ts
.tv_sec
= swritedelay
;
343 ts
.tv_nsec
= nswritedelay
;
345 wts
.tv_nsec
= chardelay
;
346 writebuff
= malloc(wbuffsz
* sizeof(char));
347 scratchw
= malloc(scratchwsz
* sizeof(int));
349 if (writebuff
== NULL
)
350 die(1, "buffer allocation failed\n");
351 if (scratchw
== NULL
)
352 die(1, "scratch buffer allocation failed\n");
356 Args bsargs
, nocrargs
, nolfargs
, lfincrargs
;
357 bsargs
.sizem
= wbuffsz
;
358 nocrargs
= nolfargs
= lfincrargs
= bsargs
;
360 bsargs
.find
=backspc
; bsargs
.input
=tbackspc
;
365 lfincrargs
.find
= CR
; lfincrargs
.input
= LF
; lfincrargs
.offset
= 1;
368 ssize_t inln
= read(STDIN_FILENO
, writebuff
, wbuffsz
- 1);
374 if (writebuff
[0] == escapechar
) {
381 if (backspc
!= tbackspc
)
382 replacechar(writebuff
, inln
, &bsargs
);
385 inln
= rmchar(writebuff
, inln
, scratchw
, &nocrargs
);
387 inln
= rmchar(writebuff
, inln
, scratchw
, &nolfargs
);
389 inln
= addchar(writebuff
, inln
, scratchw
, &lfincrargs
);
392 for (int i
= 0; i
<= inln
; i
++) {
393 write(fd
, &writebuff
[i
], 1);
394 nanosleep(&wts
, NULL
);
397 write(fd
, writebuff
, 1);
400 nanosleep(&ts
, NULL
);
406 readport(void *unused
)
409 ts
.tv_sec
= sreaddelay
;
410 ts
.tv_nsec
= nsreaddelay
;
411 readbuff
= malloc(rbuffsz
* sizeof(char));
412 scratchr
= malloc(scratchrsz
* sizeof(int));
414 if (readbuff
== NULL
)
415 die(1, "buffer allocation failed\n");
416 if (scratchr
== NULL
)
417 die(1, "scratch buffer allocation failed\n");
421 Args nocrargs
, nolfargs
, crinlfargs
, lfincrargs
;
422 nocrargs
.sizem
= rbuffsz
;
423 nolfargs
= crinlfargs
= lfincrargs
= nocrargs
;
428 lfincrargs
.find
= CR
; lfincrargs
.input
= LF
; lfincrargs
.offset
= 1;
429 crinlfargs
.find
= LF
; crinlfargs
.input
= CR
; crinlfargs
.offset
= -1;
431 /* disable stdout buffering */
432 setvbuf(stdout
, NULL
, _IONBF
, 0);
435 ssize_t outln
= read(fd
, readbuff
, rbuffsz
- 1);
438 outln
= rmchar(readbuff
, outln
, scratchr
, &nocrargs
);
440 outln
= rmchar(readbuff
, outln
, scratchr
, &nolfargs
);
442 outln
= addchar(readbuff
, outln
, scratchr
, &crinlfargs
);
444 outln
= addchar(readbuff
, outln
, scratchr
, &lfincrargs
);
446 write(STDOUT_FILENO
, readbuff
, outln
);
448 nanosleep(&ts
, NULL
);
450 if (isatty(STDIN_FILENO
))
451 settermattr(STDIN_FILENO
, &origterm
);
455 inline void __attribute__((hot
))
456 replacechar(char *buff
, ssize_t size
, const Args
*args
)
458 for (int i
= 0; i
< size
; i
++)
459 if (buff
[i
] == args
->find
)
460 buff
[i
] = args
->input
;
463 /* TODO: optimize the function and allow for offsets greater than 1 */
464 inline ssize_t
__attribute__((hot
))
465 addchar(char *buff
, ssize_t size
, int *scratch
, const Args
*args
)
469 for (int i
= 0; i
< size
; i
++) {
470 if (buff
[i
] == args
->find
) {
477 if ((size
+ c
) > args
->sizem
)
478 c
= args
->sizem
- size
;
480 if (scratch
[0] == 0 && args
->offset
< 0) {
481 memmove(&buff
[0]+1, &buff
[0], size
);
484 for (int i
= c
; i
> 0; i
--) {
486 for (int s
= size
; s
>= t
;s
--)
487 buff
[s
+ args
->offset
] = buff
[s
];
488 buff
[t
] = args
->input
;
493 inline ssize_t
__attribute__((hot
))
494 rmchar(char *buff
, ssize_t size
, int *scratch
, const Args
*args
)
498 for (int i
= 0; i
< size
; i
++) {
499 if (buff
[i
] == args
->find
) {
507 for (int i
= c
; i
> 0; i
--)
508 for (int s
= size
; s
>= scratch
[i
]; s
--)
509 buff
[s
] = buff
[s
+ 1];
518 if (gettermattr(STDIN_FILENO
, &origterm
) < 0 )
519 die(1, "failed to get terminal attributes\n");
524 newterm
.c_lflag
&= ~ECHO
;
526 newterm
.c_lflag
&= ~ICANON
;
529 newterm
.c_iflag
|= IXONXOFF
;
531 newterm
.c_iflag
|= INLCR
;
532 newterm
.c_cc
[VMIN
] = minchars
;
533 newterm
.c_cc
[VINTR
] = _POSIX_VDISABLE
;
534 newterm
.c_cc
[VSUSP
] = _POSIX_VDISABLE
;
535 newterm
.c_cc
[VQUIT
] = _POSIX_VDISABLE
;
542 backspc
= origterm
.c_cc
[VERASE
];
544 if (settermattr(STDOUT_FILENO
, &newterm
) < 0)
545 die(1, "failed to set terminal attributes\n");
553 if ((!half
|| !canonical
) && interactive
) {
554 newterm
.c_lflag
|= ECHO
;
555 newterm
.c_lflag
|= ICANON
;
556 if (settermattr(STDIN_FILENO
, &newterm
) < 0)
557 fprintf(stderr
, "failed to enable echo and/or canonical mode\n");
570 rmlf
.find
= LF
; rmlf
.input
= '\0';
572 if (isatty(STDIN_FILENO
) || isatty(STDOUT_FILENO
))
578 cmdchar
= writebuff
[0];
580 cmdchar
= writebuff
[1];
591 backspace
= !backspace
;
595 newterm
.c_lflag
&= ~ECHO
;
597 newterm
.c_lflag
|= ECHO
;
599 if (settermattr(STDOUT_FILENO
, &newterm
) < 0)
600 die(1, "failed to set terminal attributes\n");
604 newterm
.c_lflag
&= ~ICANON
;
606 newterm
.c_lflag
|= ICANON
;
607 canonical
= !canonical
;
608 if (settermattr(STDOUT_FILENO
, &newterm
) < 0)
609 die(1, "failed to set terminal attributes\n");
613 if (fgets(ttr
, sizeof(ttr
), stdin
) != NULL
) {
614 replacechar(ttr
, 63, &rmlf
);
616 int wfd
= open(ttr
, O_RDONLY
);
618 perror("error opening file");
621 while ((frln
= read(wfd
, writebuff
, wbuffsz
- 1)) > 0) {
622 for (int i
= 0; i
<= frln
; i
++) {
623 write(fd
, &writebuff
[i
], 1);
624 nanosleep(&wts
, NULL
);
631 if (fgets(ttr
, sizeof(ttr
), stdin
) != NULL
) {
632 replacechar(ttr
, 63, &rmlf
);
633 tspd
= strtoui(ttr
, "invalid speed\n", 0);
634 if (tspd
!= uintmax
) {
635 ospeed
= ispeed
= tspd
;
636 settermspd(ispeed
, ospeed
, &cntrl
);
637 if (settermattr(fd
, &cntrl
) != 0)
638 fprintf(stderr
, "failed to set baudrate "
639 "[RX:%u | TX:%u]\n", ispeed
, ospeed
);
645 if (fgets(ttr
, sizeof(ttr
), stdin
) != NULL
) {
646 replacechar(ttr
, 63, &rmlf
);
647 chardelay
= strtoui(ttr
, "invalid delay\n", 0);
648 if (chardelay
!= uintmax
) {
650 wts
.tv_nsec
= chardelay
;
656 fprintf(stderr
, "additional output translation option: ");
657 if (fgets(ttr
, sizeof(ttr
), stdin
) != NULL
) {
658 replacechar(ttr
, 63, &rmlf
);
659 if(troptions(&tropts
, ttr
))
662 fprintf(stderr
, "could not set new options\n");
667 printf("additional input translation option: ");
668 if (fgets(ttr
, sizeof(ttr
), stdin
) != NULL
) {
669 replacechar(ttr
, 63, &rmlf
);
670 if (troptions(&itropts
, ttr
))
673 fprintf(stderr
, "could not set new options\n");
676 if (!half
&& interactive
)
677 newterm
.c_lflag
&= ~ECHO
;
678 if (!canonical
&& interactive
)
679 newterm
.c_lflag
&= ~ICANON
;
680 if (settermattr(STDIN_FILENO
, &newterm
) == -1)
681 fprintf(stderr
, "failed to restore echo and/or canonical mode\n");
686 ts
.tv_sec
= spulsedelay
;
687 ts
.tv_nsec
= nspulsedelay
;
689 if (ioctl(fd
, TIOCMGET
, &st
) != 0) {
690 fprintf(stderr
, "failed to get port status\n");
694 if (ioctl(fd
, TIOCMSET
, &st
) != 0) {
695 fprintf(stderr
, "failed to set DTR [assertion]\n");
698 nanosleep(&ts
, NULL
);
701 if (ioctl(fd
, TIOCMSET
, &st
) != 0)
702 fprintf(stderr
, "failed to set DTR [negation]\n");
704 case BS
: /* FALLTHROUGH */
709 fprintf(stderr
, "not a valid command [%c]\n", cmdchar
);
718 die(128 + signo
, "\nrecieved signal [%d], exiting\n", signo
);
722 die(int code
, const char *msg
, ...)
737 if (termchck(&newterm
))
738 settermattr(STDIN_FILENO
, &origterm
);
741 vfprintf(stderr
, msg
, fpa
);
748 main(int argc
, char **argv
)
751 /* glibc's `asprintf()` won't set the string to NULL on failure automatically */
752 for (int i
= 1; i
< argc
; i
++ ) {
753 if (argv
[i
][0] == '-' && argv
[i
][1] >= '0' && argv
[i
][1] <= '9') {
754 asprintf(&t
, "-s%s", argv
[i
] + 1);
756 fprintf(stderr
, "cannot convert -# to -s#\n");
763 static struct option longopt
[] = {
764 {"line", required_argument
, NULL
, 'l'},
765 {"speed", required_argument
, NULL
, 's'},
766 {"rx-speed", required_argument
, NULL
, 'i'},
767 {"echo", no_argument
, NULL
, 'h'},
768 {"canonical", no_argument
, NULL
, 'c'},
769 {"odd", no_argument
, NULL
, 'o'},
770 {"even", no_argument
, NULL
, 'e'},
771 {"hardware-rts-cts", no_argument
, NULL
, 'R'},
772 {"hardware-dtr-dsr", no_argument
, NULL
, 'r'},
773 {"software", no_argument
, NULL
, 'X'},
774 {"data", required_argument
, NULL
, 'D'},
775 {"delay", required_argument
, NULL
, 'd'},
776 {"min-chars", required_argument
, NULL
, 'm'},
777 {"stop-bits", no_argument
, NULL
, 'S'},
778 {"backspace", no_argument
, NULL
, 'b'},
779 {"translation", required_argument
, NULL
, 't'},
780 {"input-translation", required_argument
, NULL
, 'T'},
781 {"verbose", no_argument
, NULL
, 'v'},
786 int oind
, rxspdset
, devset
, c
;
787 oind
= rxspdset
= devset
= 0;
788 while ((c
= getopt_long(argc
, argv
, "bcd:D:ehi:l:m:oRrs:t:T:vX", longopt
, &oind
)) != -1) {
793 backspace
^= 1; break;
795 canonical
^= 1; break;
805 t
= strtol(optarg
, &endptr
, 10);
806 if (errno
!= 0 || *endptr
!= '\0' || t
< 0) {
807 fprintf(stderr
, "invalid character delay\n");
814 if (strlen(optarg
) != 1 || !(optarg
[0] >= '5' && optarg
[0] <= '8')) {
815 fprintf(stderr
, "invalid number of data bits: %s\n", optarg
);
828 tui
= strtoui(optarg
, "invalid rx speed: %s\n", 1);
835 tui
= strtoui(optarg
, "invalid speed: %s\n", 1);
844 fprintf(stderr
, "cannot specify multiple devices\n");
847 if (strlen(optarg
) > 10) {
848 fprintf(stderr
, "device name too long\n");
851 if (strchr(optarg
, '/')) {
852 strcpy(line
, optarg
);
855 sprintf(line
, "/dev/%s", optarg
);
860 tui
= strtoui(optarg
, "invalid number of characters: %s\n", 1);
866 if (troptions(&tropts
, optarg
))
870 if (troptions(&itropts
, optarg
))
877 die(2, "usage: %s [--line|-l line] [--speed|-s #|-#] [--rx-speed|-i #]\n"
878 " [--data-bits|-D #] [--stop-bits|-S]"
879 " [--even|-e] [--odd|-o]\n"
880 " [--hardware-rts-cts|-R]"
881 " [--hardware-dtr-dsr|-r] [--software|-X]\n"
882 " [--delay|-d #] [--min-chars|-m #]"
883 " [--canonical|-c] [--echo|-h]\n"
884 " [--translation|-t tropt]"
885 " [--input-translation|-T tropt]\n"
886 " [--verbose|-v] [--backspace|-b]\n",
892 * not the best practice, but in order for this `free()`
893 * to not get executed ust needs to be killed,
894 * which means the OS *should* free the memory
901 if (isatty(STDIN_FILENO
) || isatty(STDOUT_FILENO
))
904 signal(SIGHUP
, sighandl
);
905 signal(SIGINT
, sighandl
);
906 signal(SIGQUIT
, sighandl
);
907 signal(SIGTERM
, sighandl
);
911 pthread_t readthread
, writethread
;
912 pthread_create(&writethread
, NULL
, writeport
, NULL
);
913 pthread_create(&readthread
, NULL
, readport
, NULL
);
914 pthread_join(writethread
, NULL
);
915 pthread_join(readthread
, NULL
);