1*cf5a6c84SAndroid Build Coastguard Worker /* microcom.c - Simple serial console.
2*cf5a6c84SAndroid Build Coastguard Worker *
3*cf5a6c84SAndroid Build Coastguard Worker * Copyright 2017 The Android Open Source Project.
4*cf5a6c84SAndroid Build Coastguard Worker
5*cf5a6c84SAndroid Build Coastguard Worker USE_MICROCOM(NEWTOY(microcom, "<1>1s#X", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_NOBUF))
6*cf5a6c84SAndroid Build Coastguard Worker
7*cf5a6c84SAndroid Build Coastguard Worker config MICROCOM
8*cf5a6c84SAndroid Build Coastguard Worker bool "microcom"
9*cf5a6c84SAndroid Build Coastguard Worker default y
10*cf5a6c84SAndroid Build Coastguard Worker help
11*cf5a6c84SAndroid Build Coastguard Worker usage: microcom [-s SPEED] [-X] DEVICE
12*cf5a6c84SAndroid Build Coastguard Worker
13*cf5a6c84SAndroid Build Coastguard Worker Simple serial console. Hit CTRL-] for menu.
14*cf5a6c84SAndroid Build Coastguard Worker
15*cf5a6c84SAndroid Build Coastguard Worker -s Set baud rate to SPEED
16*cf5a6c84SAndroid Build Coastguard Worker -X Ignore ^] menu escape
17*cf5a6c84SAndroid Build Coastguard Worker */
18*cf5a6c84SAndroid Build Coastguard Worker
19*cf5a6c84SAndroid Build Coastguard Worker #define FOR_microcom
20*cf5a6c84SAndroid Build Coastguard Worker #include "toys.h"
21*cf5a6c84SAndroid Build Coastguard Worker
GLOBALS(long s;int fd,stok;struct termios old_stdin,old_fd;)22*cf5a6c84SAndroid Build Coastguard Worker GLOBALS(
23*cf5a6c84SAndroid Build Coastguard Worker long s;
24*cf5a6c84SAndroid Build Coastguard Worker
25*cf5a6c84SAndroid Build Coastguard Worker int fd, stok;
26*cf5a6c84SAndroid Build Coastguard Worker struct termios old_stdin, old_fd;
27*cf5a6c84SAndroid Build Coastguard Worker )
28*cf5a6c84SAndroid Build Coastguard Worker
29*cf5a6c84SAndroid Build Coastguard Worker // TODO: tty_sigreset outputs ansi escape sequences, how to disable?
30*cf5a6c84SAndroid Build Coastguard Worker static void restore_states(int i)
31*cf5a6c84SAndroid Build Coastguard Worker {
32*cf5a6c84SAndroid Build Coastguard Worker if (TT.stok) tcsetattr(0, TCSANOW, &TT.old_stdin);
33*cf5a6c84SAndroid Build Coastguard Worker tcsetattr(TT.fd, FLAG(s)*TCSAFLUSH, &TT.old_fd);
34*cf5a6c84SAndroid Build Coastguard Worker }
35*cf5a6c84SAndroid Build Coastguard Worker
36*cf5a6c84SAndroid Build Coastguard Worker // TODO bookmark, jump to bottom line (if !end), clear and return afterwards
handle_esc(void)37*cf5a6c84SAndroid Build Coastguard Worker static void handle_esc(void)
38*cf5a6c84SAndroid Build Coastguard Worker {
39*cf5a6c84SAndroid Build Coastguard Worker char input;
40*cf5a6c84SAndroid Build Coastguard Worker
41*cf5a6c84SAndroid Build Coastguard Worker xputsn("\r\n[b]reak, [p]aste file, [q]uit: ");
42*cf5a6c84SAndroid Build Coastguard Worker if (read(0, &input, 1)<1 || input == CTRL('D') || input == 'q') {
43*cf5a6c84SAndroid Build Coastguard Worker xputs("exit\r");
44*cf5a6c84SAndroid Build Coastguard Worker xexit();
45*cf5a6c84SAndroid Build Coastguard Worker }
46*cf5a6c84SAndroid Build Coastguard Worker if (input == 'b') tcsendbreak(TT.fd, 0);
47*cf5a6c84SAndroid Build Coastguard Worker else if (input == 'p') {
48*cf5a6c84SAndroid Build Coastguard Worker long long written = 0, size;
49*cf5a6c84SAndroid Build Coastguard Worker char* filename;
50*cf5a6c84SAndroid Build Coastguard Worker int len = 0, fd;
51*cf5a6c84SAndroid Build Coastguard Worker
52*cf5a6c84SAndroid Build Coastguard Worker // TODO: share code with hexedit's prompt() and vi's ex mode.
53*cf5a6c84SAndroid Build Coastguard Worker // TODO: tab completion!
54*cf5a6c84SAndroid Build Coastguard Worker memset(toybuf, 0, sizeof(toybuf));
55*cf5a6c84SAndroid Build Coastguard Worker while (1) {
56*cf5a6c84SAndroid Build Coastguard Worker xprintf("\r\e[2K\e[1mFilename: \e[0m%s", toybuf);
57*cf5a6c84SAndroid Build Coastguard Worker if (read(0, &input, 1) <= 0 || input == CTRL('[')) {
58*cf5a6c84SAndroid Build Coastguard Worker len = 0;
59*cf5a6c84SAndroid Build Coastguard Worker break;
60*cf5a6c84SAndroid Build Coastguard Worker }
61*cf5a6c84SAndroid Build Coastguard Worker if (input == '\r') break;
62*cf5a6c84SAndroid Build Coastguard Worker if (input == 0x7f && len > 0) toybuf[--len] = 0;
63*cf5a6c84SAndroid Build Coastguard Worker else if (input == CTRL('U')) while (len > 0) toybuf[--len] = 0;
64*cf5a6c84SAndroid Build Coastguard Worker else if (input >= ' ' && input <= 0x7f && len < sizeof(toybuf))
65*cf5a6c84SAndroid Build Coastguard Worker toybuf[len++] = input;
66*cf5a6c84SAndroid Build Coastguard Worker }
67*cf5a6c84SAndroid Build Coastguard Worker xputsn("\r\e[2K");
68*cf5a6c84SAndroid Build Coastguard Worker toybuf[len] = 0;
69*cf5a6c84SAndroid Build Coastguard Worker if (!len) return;
70*cf5a6c84SAndroid Build Coastguard Worker if ((fd = xopen(toybuf, O_RDONLY | WARN_ONLY)) < 0) {
71*cf5a6c84SAndroid Build Coastguard Worker // xopen() warning message ends with a LF without CR, so manually print a
72*cf5a6c84SAndroid Build Coastguard Worker // CR here to move the cursor back to the front.
73*cf5a6c84SAndroid Build Coastguard Worker fputc('\r', stderr);
74*cf5a6c84SAndroid Build Coastguard Worker return;
75*cf5a6c84SAndroid Build Coastguard Worker }
76*cf5a6c84SAndroid Build Coastguard Worker // TODO xsendfile with progress indicator
77*cf5a6c84SAndroid Build Coastguard Worker filename = xstrdup(toybuf);
78*cf5a6c84SAndroid Build Coastguard Worker size = fdlength(fd);
79*cf5a6c84SAndroid Build Coastguard Worker // The alternative would be to just feed this fd into the usual loop,
80*cf5a6c84SAndroid Build Coastguard Worker // so we're reading back these characters if they're being echoed, but
81*cf5a6c84SAndroid Build Coastguard Worker // for my specific use case of pasting into `base64 -d -i > foo`, this
82*cf5a6c84SAndroid Build Coastguard Worker // is a much more convenient UI.
83*cf5a6c84SAndroid Build Coastguard Worker while ((len = read(fd, toybuf, sizeof(toybuf))) > 0) {
84*cf5a6c84SAndroid Build Coastguard Worker written += len;
85*cf5a6c84SAndroid Build Coastguard Worker xprintf("\r\e[2KPasting '%s' %lld/%lld (%lld%%)...", filename, written,
86*cf5a6c84SAndroid Build Coastguard Worker size, written*100/size);
87*cf5a6c84SAndroid Build Coastguard Worker xwrite(TT.fd, toybuf, len);
88*cf5a6c84SAndroid Build Coastguard Worker }
89*cf5a6c84SAndroid Build Coastguard Worker free(filename);
90*cf5a6c84SAndroid Build Coastguard Worker close(fd);
91*cf5a6c84SAndroid Build Coastguard Worker } else xprintf("Ignoring unknown command.");
92*cf5a6c84SAndroid Build Coastguard Worker
93*cf5a6c84SAndroid Build Coastguard Worker xprintf("\r\n");
94*cf5a6c84SAndroid Build Coastguard Worker }
95*cf5a6c84SAndroid Build Coastguard Worker
microcom_main(void)96*cf5a6c84SAndroid Build Coastguard Worker void microcom_main(void)
97*cf5a6c84SAndroid Build Coastguard Worker {
98*cf5a6c84SAndroid Build Coastguard Worker struct termios tio;
99*cf5a6c84SAndroid Build Coastguard Worker struct pollfd fds[2];
100*cf5a6c84SAndroid Build Coastguard Worker int i;
101*cf5a6c84SAndroid Build Coastguard Worker
102*cf5a6c84SAndroid Build Coastguard Worker // Open with O_NDELAY, but switch back to blocking for reads.
103*cf5a6c84SAndroid Build Coastguard Worker TT.fd = xopen(*toys.optargs, O_RDWR | O_NOCTTY | O_NDELAY);
104*cf5a6c84SAndroid Build Coastguard Worker if (-1==(i = fcntl(TT.fd, F_GETFL, 0)) || fcntl(TT.fd, F_SETFL, i&~O_NDELAY)
105*cf5a6c84SAndroid Build Coastguard Worker || tcgetattr(TT.fd, &TT.old_fd))
106*cf5a6c84SAndroid Build Coastguard Worker perror_exit_raw(*toys.optargs);
107*cf5a6c84SAndroid Build Coastguard Worker
108*cf5a6c84SAndroid Build Coastguard Worker // Set both input and output to raw mode.
109*cf5a6c84SAndroid Build Coastguard Worker memcpy(&tio, &TT.old_fd, sizeof(struct termios));
110*cf5a6c84SAndroid Build Coastguard Worker cfmakeraw(&tio);
111*cf5a6c84SAndroid Build Coastguard Worker if (FLAG(s)) xsetspeed(&tio, TT.s);
112*cf5a6c84SAndroid Build Coastguard Worker if (tcsetattr(TT.fd, FLAG(s)*TCSAFLUSH, &tio)) perror_exit("set speed");
113*cf5a6c84SAndroid Build Coastguard Worker if (!set_terminal(0, 1, 0, &TT.old_stdin)) TT.stok++;
114*cf5a6c84SAndroid Build Coastguard Worker // ...and arrange to restore things, however we may exit.
115*cf5a6c84SAndroid Build Coastguard Worker sigatexit(restore_states);
116*cf5a6c84SAndroid Build Coastguard Worker
117*cf5a6c84SAndroid Build Coastguard Worker fds[0].fd = TT.fd;
118*cf5a6c84SAndroid Build Coastguard Worker fds[1].fd = 0;
119*cf5a6c84SAndroid Build Coastguard Worker fds[0].events = fds[1].events = POLLIN;
120*cf5a6c84SAndroid Build Coastguard Worker
121*cf5a6c84SAndroid Build Coastguard Worker if (!FLAG(X)) xputs("Escape character is '^]'.\r");
122*cf5a6c84SAndroid Build Coastguard Worker while (poll(fds, 2, -1) > 0) {
123*cf5a6c84SAndroid Build Coastguard Worker
124*cf5a6c84SAndroid Build Coastguard Worker // Read from connection, write to stdout.
125*cf5a6c84SAndroid Build Coastguard Worker if (fds[0].revents) {
126*cf5a6c84SAndroid Build Coastguard Worker if (0 < (i = read(TT.fd, toybuf, sizeof(toybuf)))) xwrite(0, toybuf, i);
127*cf5a6c84SAndroid Build Coastguard Worker else break;
128*cf5a6c84SAndroid Build Coastguard Worker }
129*cf5a6c84SAndroid Build Coastguard Worker
130*cf5a6c84SAndroid Build Coastguard Worker // Read from stdin, write to connection.
131*cf5a6c84SAndroid Build Coastguard Worker if (fds[1].revents) {
132*cf5a6c84SAndroid Build Coastguard Worker if (read(0, toybuf, 1) != 1) break;
133*cf5a6c84SAndroid Build Coastguard Worker if (!FLAG(X) && *toybuf == CTRL(']')) handle_esc();
134*cf5a6c84SAndroid Build Coastguard Worker else xwrite(TT.fd, toybuf, 1);
135*cf5a6c84SAndroid Build Coastguard Worker }
136*cf5a6c84SAndroid Build Coastguard Worker }
137*cf5a6c84SAndroid Build Coastguard Worker }
138