/* 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 "c.h" #include "all-io.h" #include "nls.h" #include "strutils.h" #include "closestream.h" #include #include #include #include /* for N_GSM0710 */ #ifdef LINUX_GSMMUX_H # include /* 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] \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 set serial line speed\n"), out); fputs(_(" -c, --intro-command intro sent before ldattach\n"), out); fputs(_(" -p, --pause 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 [-] set input mode flag\n"), out); fputs(USAGE_SEPARATOR, out); printf(USAGE_HELP_OPTIONS(25)); fputs(_("\nKnown names:\n"), out); print_table(out, ld_discs); fputs(USAGE_SEPARATOR, out); fputs(_("\nKnown 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)... */ #if _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); close_stdout_atexit(); /* 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': print_version(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); }