| /* 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); |
| } |