/* 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 #include "nls.h" #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 N_PPS # define N_PPS 18 #endif #ifndef ARRAY_SIZE # define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #endif /* attach a line discipline ioctl */ #ifndef TIOCSETD # define TIOCSETD 0x5423 #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 }, { "PPS", N_PPS }, }; /* 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; } static void __attribute__((__noreturn__)) usage(int exitcode) { size_t i; fprintf(stderr, _("\nUsage: %s [ -dhV78neo12 ] [ -s ] \n"), progname); fputs(_("\nKnown names:\n"), stderr); for (i = 0; i < ARRAY_SIZE(ld_table); i++) fprintf(stderr, " %s\n", ld_table[i].s); exit(exitcode); } static int my_cfsetspeed(struct termios *ts, int speed) { /* Standard speeds * -- cfsetspeed() is able to translate number to Bxxx constants */ if (cfsetspeed(ts, speed) == 0) return 0; /* Nonstandard speeds * -- we have to bypass glibc and set the speed manually (because * glibc checks for speed and supports Bxxx bit rates only)... */ #ifdef _HAVE_STRUCT_TERMIOS_C_ISPEED # define BOTHER 0010000 /* non standard rate */ dbg("using non-standard speeds"); ts->c_ospeed = ts->c_ispeed = speed; ts->c_cflag &= ~CBAUD; ts->c_cflag |= BOTHER; return 0; #else return -1; #endif } int main(int argc, char **argv) { int tty_fd; struct termios 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} }; setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); /* parse options */ progname = program_invocation_short_name; 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 (tcgetattr(tty_fd, &ts) < 0) err(EXIT_FAILURE, _("cannot get terminal attributes for %s"), dev); cfmakeraw(&ts); if (speed && my_cfsetspeed(&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 (tcsetattr(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); }