From 1e8d11c459ae93a4d8d7976a8530ae5198206a26 Mon Sep 17 00:00:00 2001 From: Tilman Schmidt Date: Mon, 10 Mar 2008 14:20:26 +0100 Subject: ldattach: new command Add an ldattach(8) utility program similar to the one in OpenBSD. Signed-off-by: Tilman Schmidt --- sys-utils/ldattach.c | 327 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 327 insertions(+) create mode 100644 sys-utils/ldattach.c (limited to 'sys-utils/ldattach.c') diff --git a/sys-utils/ldattach.c b/sys-utils/ldattach.c new file mode 100644 index 000000000..99037d62b --- /dev/null +++ b/sys-utils/ldattach.c @@ -0,0 +1,327 @@ +/* line discipline loading daemon + * open a serial device and attach a line discipline on it + * + * Usage: + * ldattach GIGASET_M101 /dev/ttyS0 + * + * ===================================================================== + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * ===================================================================== + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define dbg(format, arg...) \ + do { if (debug) fprintf(stderr , "%s:" format "\n" , progname , ## arg); } while (0) + +#ifndef N_GIGASET_M101 +#define N_GIGASET_M101 16 +#endif + +#ifndef PACKAGE_STRING +#define PACKAGE_STRING "me" +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + +static const char *progname; +static int debug = 0; + +/* currently supported line disciplines, plus some aliases */ +static const struct ld_entry { const char *s; int v; } +ld_table[] = { + { "TTY", N_TTY }, + { "SLIP", N_SLIP }, + { "MOUSE", N_MOUSE }, + { "PPP", N_PPP }, + { "STRIP", N_STRIP }, + { "AX25", N_AX25 }, + { "X25", N_X25 }, + { "6PACK", N_6PACK }, + { "R3964", N_R3964 }, + { "IRDA", N_IRDA }, + { "HDLC", N_HDLC }, + { "SYNC_PPP", N_SYNC_PPP }, + { "SYNCPPP", N_SYNC_PPP }, + { "HCI", N_HCI }, + { "GIGASET_M101", N_GIGASET_M101 }, + { "GIGASET", N_GIGASET_M101 }, + { "M101", N_GIGASET_M101 } +}; + +/* look up line discipline code */ +static int lookup_ld(const char *s) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(ld_table); i++) + if (!strcasecmp(ld_table[i].s, s)) + return ld_table[i].v; + return -1; +} + +/* replacement for tcsetattr(3) and friends supporting arbitrary speed values */ + +/* some archs don't have separate struct termios2 */ +#ifndef TCGETS2 +#define termios2 termios +#define TCGETS2 TCGETS +#define TCSETS2 TCSETS +#define TCSETSW2 TCSETSW +#define TCSETSF2 TCSETSF +#endif + +static int tcgetattr2(int fd, struct termios2 *pts) +{ + return ioctl(fd, TCGETS2, pts); +} + +static int tcsetattr2(int fd, int option, const struct termios2 *pts) +{ + int request; + + switch (option) { + case TCSANOW: + request = TCSETS2; + break; + case TCSADRAIN: + request = TCSETSW2; + break; + case TCSAFLUSH: + request = TCSETSF2; + break; + default: + errno = -EINVAL; + return -1; + } + return ioctl(fd, request, pts); +} + +static void cfmakeraw2(struct termios2 *pts) +{ + pts->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + pts->c_oflag &= ~OPOST; + pts->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + pts->c_cflag &= ~(CSIZE|PARENB); + pts->c_cflag |= CS8; +} + +/* table of standard line speeds */ +static const struct speed_entry { int s; speed_t v; } +speed_table[] = { + { 50, B50 }, + { 75, B75 }, + { 110, B110 }, + { 134, B134 }, + { 150, B150 }, + { 200, B200 }, + { 300, B300 }, + { 600, B600 }, + { 1200, B1200 }, + { 1800, B1800 }, + { 2400, B2400 }, + { 4800, B4800 }, + { 9600, B9600 }, + { 19200, B19200 }, + { 38400, B38400 } +#ifdef B57600 + ,{ 57600, B57600 } +#endif +#ifdef B115200 + ,{ 115200, B115200 } +#endif +#ifdef B230400 + ,{ 230400, B230400 } +#endif +}; + +static int cfsetspeed2(struct termios2 *pts, int speed) +{ + size_t i; + + /* try POSIX method first */ + for (i = 0; i < ARRAY_SIZE(speed_table); i++) + if (speed_table[i].s == speed) { + pts->c_cflag &= ~CBAUD; + pts->c_cflag |= speed_table[i].v; + return 0; + } + +#ifdef BOTHER + /* new method available */ + pts->c_ospeed = pts->c_ispeed = speed; + pts->c_cflag &= ~CBAUD; + pts->c_cflag |= BOTHER; + return 0; +#else + /* new method not available */ + return -1; +#endif +} + +static void __attribute__((__noreturn__)) usage(int exitcode) +{ + size_t i; + + fprintf(stderr, + "\nUsage: %s [ -dhV78neo12 ] [ -s ] \n", + progname); + fprintf(stderr, "\nKnown names:\n"); + for (i = 0; i < ARRAY_SIZE(ld_table); i++) + fprintf(stderr, " %s\n", ld_table[i].s); + exit(exitcode); +} + +int main(int argc, char **argv) +{ + int tty_fd; + struct termios2 ts; + int speed = 0, bits = '-', parity = '-', stop = '-'; + int ldisc; + int optc; + char *end; + char *dev; + static const struct option opttbl[] = { + {"speed", 1, 0, 's'}, + {"sevenbits", 0, 0, '7'}, + {"eightbits", 0, 0, '8'}, + {"noparity", 0, 0, 'n'}, + {"evenparity", 0, 0, 'e'}, + {"oddparity", 0, 0, 'o'}, + {"onestopbit", 0, 0, '1'}, + {"twostopbits", 0, 0, '2'}, + {"help", 0, 0, 'h'}, + {"version", 0, 0, 'V'}, + {"debug", 0, 0, 'd'}, + {0, 0, 0, 0} + }; + + /* parse options */ + progname = argv[0]; + if (argc == 0) + usage(EXIT_SUCCESS); + while ((optc = getopt_long(argc, argv, "dhV78neo12s:", opttbl, NULL)) >= 0) { + switch (optc) { + case 'd': + debug++; + break; + case '1': + case '2': + stop = optc; + break; + case '7': + case '8': + bits = optc; + break; + case 'n': + case 'e': + case 'o': + parity = optc; + break; + case 's': + speed = strtol(optarg, &end, 10); + if (*end || speed <= 0) + errx(EXIT_FAILURE, "invalid speed: %s", optarg); + break; + case 'V': + printf("ldattach from %s\n", PACKAGE_STRING); + break; + case 'h': + usage(EXIT_SUCCESS); + default: + warnx("invalid option"); + usage(EXIT_FAILURE); + } + } + + if (argc - optind != 2) + usage(EXIT_FAILURE); + + /* parse line discipline specification */ + if ((ldisc = lookup_ld(argv[optind])) < 0) { + ldisc = strtol(argv[optind], &end, 0); + if (*end || ldisc < 0) + errx(EXIT_FAILURE, "invalid line discipline: %s", argv[optind]); + } + + /* open device */ + dev = argv[optind+1]; + if ((tty_fd = open(dev, O_RDWR|O_NOCTTY)) < 0) + err(EXIT_FAILURE, "cannot open %s", dev); + if (!isatty(tty_fd)) + errx(EXIT_FAILURE, "%s is not a serial line", dev); + + dbg("opened %s", dev); + + /* set line speed and format */ + if (tcgetattr2(tty_fd, &ts) < 0) + err(EXIT_FAILURE, "cannot get terminal attributes for %s", dev); + cfmakeraw2(&ts); + if (speed && cfsetspeed2(&ts, speed) < 0) + errx(EXIT_FAILURE, "speed %d unsupported", speed); + switch (stop) { + case '1': + ts.c_cflag &= ~CSTOPB; + break; + case '2': + ts.c_cflag |= CSTOPB; + break; + } + switch (bits) { + case '7': + ts.c_cflag = (ts.c_cflag & ~CSIZE) | CS7; + break; + case '8': + ts.c_cflag = (ts.c_cflag & ~CSIZE) | CS8; + break; + } + switch (parity) { + case 'n': + ts.c_cflag &= ~(PARENB|PARODD); + break; + case 'e': + ts.c_cflag |= PARENB; + ts.c_cflag &= ~PARODD; + break; + case 'o': + ts.c_cflag |= (PARENB|PARODD); + break; + } + ts.c_cflag |= CREAD; /* just to be on the safe side */ + if (tcsetattr2(tty_fd, TCSAFLUSH, &ts) < 0) + err(EXIT_FAILURE, "cannot set terminal attributes for %s", dev); + + dbg("set to raw %d %c%c%c: cflag=0x%x", + speed, bits, parity, stop, ts.c_cflag); + + /* Attach the line discpline. */ + if (ioctl(tty_fd, TIOCSETD, &ldisc) < 0) + err(EXIT_FAILURE, "cannot set line discipline"); + + dbg("line discipline set to %d", ldisc); + + /* Go into background if not in debug mode. */ + if (!debug && daemon(0, 0) < 0) + err(EXIT_FAILURE, "cannot daemonize"); + + /* Sleep to keep the line discipline active. */ + pause(); + + exit(EXIT_SUCCESS); +} -- cgit v1.2.3-55-g7522