summaryrefslogtreecommitdiffstats
path: root/sys-utils/ldattach.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys-utils/ldattach.c')
-rw-r--r--sys-utils/ldattach.c489
1 files changed, 489 insertions, 0 deletions
diff --git a/sys-utils/ldattach.c b/sys-utils/ldattach.c
new file mode 100644
index 0000000..d33d685
--- /dev/null
+++ b/sys-utils/ldattach.c
@@ -0,0 +1,489 @@
+/* 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 <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "all-io.h"
+#include "nls.h"
+#include "strutils.h"
+#include "closestream.h"
+
+#include <signal.h>
+#include <sys/socket.h>
+#include <linux/if.h>
+
+#include <linux/tty.h> /* for N_GSM0710 */
+
+#ifdef LINUX_GSMMUX_H
+# include <linux/gsmmux.h> /* Add by guowenxue */
+#else
+struct gsm_config
+{
+ unsigned int adaption;
+ unsigned int encapsulation;
+ unsigned int initiator;
+ unsigned int t1;
+ unsigned int t2;
+ unsigned int t3;
+ unsigned int n2;
+ unsigned int mru;
+ unsigned int mtu;
+ unsigned int k;
+ unsigned int i;
+ unsigned int unused[8]; /* Padding for expansion without
+ breaking stuff */
+};
+# define GSMIOC_GETCONF _IOR('G', 0, struct gsm_config)
+# define GSMIOC_SETCONF _IOW('G', 1, struct gsm_config)
+#endif
+
+#ifndef N_GIGASET_M101
+# define N_GIGASET_M101 16
+#endif
+
+#ifndef N_PPS
+# define N_PPS 18
+#endif
+
+#ifndef N_GSM0710
+# define N_GSM0710 21
+#endif
+
+#define MAXINTROPARMLEN 32
+
+/* attach a line discipline ioctl */
+#ifndef TIOCSETD
+# define TIOCSETD 0x5423
+#endif
+
+static int debug = 0;
+
+struct ld_table {
+ const char *name;
+ int value;
+};
+
+/* currently supported line disciplines, plus some aliases */
+static const struct ld_table ld_discs[] = {
+ { "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 },
+ { "M101", N_GIGASET_M101 },
+ { "GIGASET", N_GIGASET_M101 },
+ { "PPS", N_PPS },
+ { "GSM0710", N_GSM0710},
+ { NULL, 0 }
+};
+
+/* known c_iflag names */
+static const struct ld_table ld_iflags[] =
+{
+ { "IGNBRK", IGNBRK },
+ { "BRKINT", BRKINT },
+ { "IGNPAR", IGNPAR },
+ { "PARMRK", PARMRK },
+ { "INPCK", INPCK },
+ { "ISTRIP", ISTRIP },
+ { "INLCR", INLCR },
+ { "IGNCR", IGNCR },
+ { "ICRNL", ICRNL },
+ { "IUCLC", IUCLC },
+ { "IXON", IXON },
+ { "IXANY", IXANY },
+ { "IXOFF", IXOFF },
+ { "IMAXBEL", IMAXBEL },
+ { "IUTF8", IUTF8 },
+ { NULL, 0 }
+};
+
+static void dbg(char *fmt, ...)
+{
+ va_list args;
+
+ if (debug == 0)
+ return;
+ fflush(NULL);
+ va_start(args, fmt);
+#ifdef HAVE_VWARNX
+ vwarnx(fmt, args);
+#else
+ fprintf(stderr, "%s: ", program_invocation_short_name);
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+#endif
+ va_end(args);
+ fflush(NULL);
+ return;
+}
+
+static int lookup_table(const struct ld_table *tab, const char *str)
+{
+ const struct ld_table *t;
+
+ for (t = tab; t && t->name; t++)
+ if (!strcasecmp(t->name, str))
+ return t->value;
+ return -1;
+}
+
+static void print_table(FILE * out, const struct ld_table *tab)
+{
+ const struct ld_table *t;
+ int i;
+
+ for (t = tab, i = 1; t && t->name; t++, i++) {
+ fprintf(out, " %-12s", t->name);
+ if (!(i % 5))
+ fputc('\n', out);
+ }
+}
+
+static int parse_iflag(char *str, int *set_iflag, int *clr_iflag)
+{
+ int iflag;
+ char *s;
+
+ for (s = strtok(str, ","); s != NULL; s = strtok(NULL, ",")) {
+ if (*s == '-')
+ s++;
+ if ((iflag = lookup_table(ld_iflags, s)) < 0)
+ iflag = strtos32_or_err(s, _("invalid iflag"));
+ if (s > str && *(s - 1) == '-')
+ *clr_iflag |= iflag;
+ else
+ *set_iflag |= iflag;
+ }
+ dbg("iflag (set/clear): %d/%d", *set_iflag, *clr_iflag);
+ return 0;
+}
+
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s [options] <ldisc> <device>\n"), program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Attach a line discipline to a serial line.\n"), out);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -d, --debug print verbose messages to stderr\n"), out);
+ fputs(_(" -s, --speed <value> set serial line speed\n"), out);
+ fputs(_(" -c, --intro-command <string> intro sent before ldattach\n"), out);
+ fputs(_(" -p, --pause <seconds> pause between intro and ldattach\n"), out);
+ fputs(_(" -7, --sevenbits set character size to 7 bits\n"), out);
+ fputs(_(" -8, --eightbits set character size to 8 bits\n"), out);
+ fputs(_(" -n, --noparity set parity to none\n"), out);
+ fputs(_(" -e, --evenparity set parity to even\n"), out);
+ fputs(_(" -o, --oddparity set parity to odd\n"), out);
+ fputs(_(" -1, --onestopbit set stop bits to one\n"), out);
+ fputs(_(" -2, --twostopbits set stop bits to two\n"), out);
+ fputs(_(" -i, --iflag [-]<iflag> set input mode flag\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ printf(USAGE_HELP_OPTIONS(25));
+
+ fputs(_("\nKnown <ldisc> names:\n"), out);
+ print_table(out, ld_discs);
+ fputs(USAGE_SEPARATOR, out);
+
+ fputs(_("\nKnown <iflag> names:\n"), out);
+ print_table(out, ld_iflags);
+
+ printf(USAGE_MAN_TAIL("ldattach(8)"));
+ exit(EXIT_SUCCESS);
+}
+
+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
+}
+
+static void handler(int s)
+{
+ dbg("got SIG %i -> exiting", s);
+ exit(EXIT_SUCCESS);
+}
+
+static void gsm0710_set_conf(int tty_fd)
+{
+ struct gsm_config c;
+
+ /* Add by guowenxue */
+ /* get n_gsm configuration */
+ ioctl(tty_fd, GSMIOC_GETCONF, &c);
+ /* we are initiator and need encoding 0 (basic) */
+ c.initiator = 1;
+ c.encapsulation = 0;
+ /* our modem defaults to a maximum size of 127 bytes */
+ c.mru = 127;
+ c.mtu = 127;
+ /* set the new configuration */
+ ioctl(tty_fd, GSMIOC_SETCONF, &c);
+ /* Add by guowenxue end*/
+}
+
+int main(int argc, char **argv)
+{
+ int tty_fd;
+ struct termios ts;
+ int speed = 0, bits = '-', parity = '-', stop = '-';
+ int set_iflag = 0, clr_iflag = 0;
+ int ldisc;
+ int optc;
+ char *dev;
+ int intropause = 1;
+ char *introparm = NULL;
+
+ static const struct option opttbl[] = {
+ {"speed", required_argument, NULL, 's'},
+ {"sevenbits", no_argument, NULL, '7'},
+ {"eightbits", no_argument, NULL, '8'},
+ {"noparity", no_argument, NULL, 'n'},
+ {"evenparity", no_argument, NULL, 'e'},
+ {"oddparity", no_argument, NULL, 'o'},
+ {"onestopbit", no_argument, NULL, '1'},
+ {"twostopbits", no_argument, NULL, '2'},
+ {"iflag", required_argument, NULL, 'i'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {"debug", no_argument, NULL, 'd'},
+ {"intro-command", no_argument, NULL, 'c'},
+ {"pause", no_argument, NULL, 'p'},
+ {NULL, 0, NULL, 0}
+ };
+
+ signal(SIGKILL, handler);
+ signal(SIGINT, handler);
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ /* parse options */
+ if (argc == 0)
+ errx(EXIT_FAILURE, _("bad usage"));
+
+ while ((optc =
+ getopt_long(argc, argv, "dhV78neo12s:i:c:p:", opttbl,
+ NULL)) >= 0) {
+ switch (optc) {
+ case 'd':
+ debug = 1;
+ 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 = strtos32_or_err(optarg, _("invalid speed argument"));
+ break;
+ case 'p':
+ intropause = strtou32_or_err(optarg, _("invalid pause argument"));
+ if (intropause > 10)
+ errx(EXIT_FAILURE, "invalid pause: %s", optarg);
+ break;
+ case 'c':
+ introparm = optarg;
+ break;
+ case 'i':
+ parse_iflag(optarg, &set_iflag, &clr_iflag);
+ break;
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage();
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+ }
+
+ if (argc - optind != 2) {
+ warnx(_("not enough arguments"));
+ errtryhelp(EXIT_FAILURE);
+ }
+ /* parse line discipline specification */
+ ldisc = lookup_table(ld_discs, argv[optind]);
+ if (ldisc < 0)
+ ldisc = strtos32_or_err(argv[optind], _("invalid line discipline argument"));
+
+ /* ldisc specific option settings */
+ if (ldisc == N_GIGASET_M101) {
+ /* device specific defaults for line speed and data format */
+ if (speed == 0)
+ speed = 115200;
+ if (bits == '-')
+ bits = '8';
+ if (parity == '-')
+ parity = 'n';
+ if (stop == '-')
+ stop = '1';
+ }
+
+ /* 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;
+ case '-':
+ break;
+ default:
+ abort();
+ }
+ switch (bits) {
+ case '7':
+ ts.c_cflag = (ts.c_cflag & ~CSIZE) | CS7;
+ break;
+ case '8':
+ ts.c_cflag = (ts.c_cflag & ~CSIZE) | CS8;
+ break;
+ case '-':
+ break;
+ default:
+ abort();
+ }
+ 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;
+ case '-':
+ break;
+ default:
+ abort();
+ }
+
+ ts.c_cflag |= CREAD; /* just to be on the safe side */
+ ts.c_iflag |= set_iflag;
+ ts.c_iflag &= ~clr_iflag;
+
+ 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);
+
+ if (introparm && *introparm)
+ {
+ dbg("intro command is '%s'", introparm);
+ if (write_all(tty_fd, introparm, strlen(introparm)) != 0)
+ err(EXIT_FAILURE,
+ _("cannot write intro command to %s"), dev);
+
+ if (intropause) {
+ dbg("waiting for %d seconds", intropause);
+ sleep(intropause);
+ }
+ }
+
+ /* Attach the line discipline. */
+ if (ioctl(tty_fd, TIOCSETD, &ldisc) < 0)
+ err(EXIT_FAILURE, _("cannot set line discipline"));
+
+ dbg("line discipline set to %d", ldisc);
+
+ /* ldisc specific post-attach actions */
+ if (ldisc == N_GSM0710)
+ gsm0710_set_conf(tty_fd);
+
+ /* 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);
+}