Root/neocon/neocon.c

Source at commit 94e955811772fe342947424bd017073aa54b34e4 created 7 years 10 months ago.
By Werner Almesberger, m1/ledravaganza/ramp.pl: exponential ramp generator for ledm
1/*
2 * neocon.c - An interface for changing tty devices
3 *
4 * Copyright (C) 2007, 2008 by OpenMoko, Inc.
5 * Copyright 2011 by Werner Almesberger
6 * Written by Werner Almesberger <werner@almesberger.net>
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24
25#include <stdlib.h>
26#include <stdio.h>
27#include <stdio.h>
28#include <unistd.h>
29#include <string.h>
30#include <termios.h>
31#include <fcntl.h>
32#include <assert.h>
33#include <sys/time.h>
34#include <sys/select.h>
35
36
37#define MAX_BUF 2048
38
39static char *const *ttys;
40static int num_ttys;
41static int curr_tty = -1; /* start with first tty */
42static speed_t speed = B115200;
43static struct termios console, tty;
44static FILE *log = NULL;
45static int timestamp = 0;
46static char escape = '~';
47
48
49static struct bps {
50    speed_t speed;
51    int bps;
52} bps_tab[] = {
53    { B300, 300 },
54    { B1200, 1200 },
55    { B2400, 2400 },
56    { B9600, 9600 },
57    { B19200, 19200 },
58    { B38400, 38400 },
59    { B115200, 115200 },
60    { 0, 0 }
61};
62
63
64static speed_t bps_to_speed(int bps)
65{
66    const struct bps *p;
67
68    for (p = bps_tab; p->bps; p++)
69        if (p->bps == bps)
70            return p->speed;
71    fprintf(stderr, "no such speed: %d bps\n", bps);
72    exit(1);
73}
74
75
76static void make_raw(int fd, struct termios *old, int cr)
77{
78    struct termios t;
79    long flags;
80
81    if (tcgetattr(fd, &t) < 0) {
82        perror("tcgetattr");
83        exit(1);
84    }
85    if (old)
86        *old = t;
87    cfmakeraw(&t);
88    if (fd) {
89        t.c_iflag &= ~(IXON | IXOFF);
90        t.c_cflag |= CLOCAL;
91        t.c_cflag &= ~CRTSCTS;
92        if (cfsetispeed(&t, speed) < 0) {
93            perror("cfsetispeed");
94            exit(1);
95        }
96        if (cfsetospeed(&t, speed) < 0) {
97            perror("cfsetospeed");
98            exit(1);
99        }
100    }
101    if (cr)
102        t.c_oflag |= OPOST | ONLCR;
103    if (tcsetattr(fd, TCSANOW, &t) < 0) {
104        perror("tcsetattr");
105        exit(1);
106    }
107    flags = fcntl(fd, F_GETFL);
108    if (flags < 0) {
109        perror("fcntl F_GETFL");
110        exit(1);
111    }
112    if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
113        perror("fcntl F_GETFL");
114        exit(1);
115    }
116}
117
118
119static int open_next_tty(void)
120{
121    int i, fd = -1;
122
123    for (i = 0; i != num_ttys; i++) {
124        curr_tty = (curr_tty+1) % num_ttys;
125        fd = open(ttys[curr_tty], O_RDWR | O_NDELAY);
126        if (fd >= 0)
127            break;
128    }
129    if (fd >= 0)
130        make_raw(fd, &tty, 0);
131        return fd;
132}
133
134
135/*
136 * Return 1 if the user manually forces a device change.
137 */
138
139
140static int scan(const char *s, size_t len)
141{
142    static int state = 0;
143    const char *p;
144    int res = 0;
145
146    for (p = s; p != s+len; p++)
147        switch (state) {
148        case 0:
149            if (*p == escape)
150                state++;
151            else
152                state = 0;
153            break;
154        case 1:
155            if (*p == '.')
156                exit(0);
157            if (*p == 'n')
158                res = 1;
159            state = 0;
160            break;
161        }
162    return res;
163}
164
165
166static int write_log(const char *buf, ssize_t len)
167{
168    size_t wrote;
169
170    wrote = fwrite(buf, 1, len, log);
171    if (wrote == len)
172        return 1;
173    fprintf(stderr, "write failed. closing log file.\n");
174    fclose(log);
175    log = NULL;
176    return 0;
177}
178
179
180static int add_timestamp(void)
181{
182    struct timeval tv;
183    char buf[40]; /* be generous */
184    int len;
185
186    if (gettimeofday(&tv, NULL) < 0) {
187        perror("gettimeofday");
188        exit(1);
189    }
190    len = sprintf(buf, "%lu.%06lu ",
191        (unsigned long) tv.tv_sec, (unsigned long) tv.tv_usec);
192    return write_log(buf, len);
193}
194
195
196static void do_log(const char *buf, ssize_t len)
197{
198    static int nl = 1; /* we're at the beginning of a new line */
199    char tmp[MAX_BUF];
200    const char *from;
201    char *to;
202
203    assert(len <= MAX_BUF);
204    from = buf;
205    to = tmp;
206    while (from != buf+len) {
207        if (*from == '\r') {
208            from++;
209            continue;
210        }
211        if (nl && timestamp)
212            if (!add_timestamp())
213                return;
214        nl = 0;
215        if (*from == '\n') {
216            *to++ = *from++;
217            if (!write_log(tmp, to-tmp))
218                return;
219            to = tmp;
220            nl = 1;
221            continue;
222        }
223        *to++ = *from < ' ' || *from > '~' ? '#' : *from;
224        from++;
225    }
226    write_log(tmp, to-tmp);
227}
228
229
230static int copy(int in, int out, int from_user, int single)
231{
232    char buffer[MAX_BUF];
233    ssize_t got, wrote, pos;
234
235    got = read(in, buffer, single ? 1 : sizeof(buffer));
236    if (got <= 0)
237        return 0;
238    if (from_user) {
239        if (scan(buffer, got))
240            return 0;
241    } else {
242        if (log)
243            do_log(buffer, got);
244    }
245    for (pos = 0; pos != got; pos += wrote) {
246        wrote = write(out, buffer+pos, got-pos);
247        if (wrote < 0)
248            return 0;
249    }
250    return 1;
251}
252
253
254static void write_string(const char *s)
255{
256    int len = strlen(s);
257
258    while (len) {
259        ssize_t wrote;
260
261        wrote = write(1, s, len);
262        if (wrote < 0) {
263            perror("write");
264            exit(1);
265        }
266        s += wrote;
267        len -= wrote;
268    }
269}
270
271
272static void cleanup(void)
273{
274    if (tcsetattr(0, TCSANOW, &console) < 0)
275        perror("tcsetattr");
276    write(1, "\n", 1);
277}
278
279
280static void usage(const char *name)
281{
282    fprintf(stderr,
283"usage: %s [-b bps] [-c] [-e escape] [-l logfile [-a] [-T]] [-t delay_ms] "
284    "tty ...\n\n"
285" -a append to the log file if it already exists\n"
286" -b bps set the TTY to the specified bit rate\n"
287" -c add carriage return before newline (on console)\n"
288" -e escape set the escape character (default: ~)\n"
289" -l logfile log all output to the specified file\n"
290" -t delay_ms wait the specified amount of time between input characters\n"
291" -T add timestamps to the log file\n"
292        , name);
293    exit(1);
294}
295
296
297int main(int argc, char *const *argv)
298{
299    char *end;
300    int c, bps, cr = 0;
301    int fd = -1;
302    int append = 0;
303    const char *logfile = NULL;
304    int throttle_us = 0;
305    int throttle = 0;
306
307    while ((c = getopt(argc, argv, "ab:ce:l:t:T")) != EOF)
308        switch (c) {
309        case 'a':
310            append = 1;
311            break;
312        case 'b':
313            bps = strtoul(optarg, &end, 0);
314            if (*end)
315                    usage(*argv);
316            speed = bps_to_speed(bps);
317            break;
318        case 'c':
319            cr = 1;
320            break;
321        case 'e':
322            if (strlen(optarg) != 1)
323                usage(*argv);
324            escape = *optarg;
325            break;
326        case 'l':
327            logfile = optarg;
328            break;
329        case 't':
330            throttle_us = strtoul(optarg, &end, 0)*1000;
331            if (*end)
332                usage(*argv);
333            break;
334        case 'T':
335            timestamp = 1;
336            break;
337        default:
338            usage(*argv);
339    }
340    num_ttys = argc-optind;
341    ttys = argv+optind;
342
343    if (logfile) {
344        log = fopen(logfile, append ? "a" : "w");
345        if (!log) {
346            perror(logfile);
347            exit(1);
348        }
349        setlinebuf(log);
350    }
351
352    make_raw(0, &console, cr);
353    atexit(cleanup);
354    while (1) {
355        struct timeval tv;
356        fd_set set;
357        int res;
358
359        if (fd < 0) {
360            fd = open_next_tty();
361            if (fd > 0) {
362                char buf[1024]; /* enough :-) */
363
364                sprintf(buf, "\r\r[Open %s]\r\n",
365                    ttys[curr_tty]);
366                write_string(buf);
367            }
368        }
369        FD_ZERO(&set);
370        if (!throttle)
371            FD_SET(0, &set);
372        if (fd >= 0)
373            FD_SET(fd, &set);
374        tv.tv_sec = 0;
375        tv.tv_usec = throttle ? throttle_us : 100000;
376        res = select(fd < 0 ? 1 : fd+1, &set, NULL, NULL, &tv);
377        if (res < 0) {
378            perror("select");
379            return 1;
380        }
381        if (!res)
382            throttle = 0;
383        if (FD_ISSET(0, &set)) {
384            if (throttle_us)
385                throttle = 1;
386            if (!copy(0, fd, 1, throttle_us != 0))
387                goto failed;
388        }
389        if (fd >= 0 && FD_ISSET(fd, &set))
390            if (!copy(fd, 1, 0, 0))
391                goto failed;
392        continue;
393
394failed:
395        write_string("\r\n[Closed]\r\n");
396        (void) close(fd);
397        fd = -1;
398    }
399    return 0;
400}
401

Archive Download this file

Branches:
master



interactive