(root)/
util-linux-2.39/
sys-utils/
ldattach.c
       1  /* line discipline loading daemon
       2   * open a serial device and attach a line discipline on it
       3   *
       4   * Usage:
       5   *	ldattach GIGASET_M101 /dev/ttyS0
       6   *
       7   * =====================================================================
       8   * This program is free software; you can redistribute it and/or
       9   * modify it under the terms of the GNU General Public License as
      10   * published by the Free Software Foundation; either version 2 of
      11   * the License, or (at your option) any later version.
      12   * =====================================================================
      13   */
      14  
      15  #include <errno.h>
      16  #include <fcntl.h>
      17  #include <getopt.h>
      18  #include <stdio.h>
      19  #include <stdlib.h>
      20  #include <string.h>
      21  #include <sys/ioctl.h>
      22  #include <sys/stat.h>
      23  #include <sys/types.h>
      24  #include <termios.h>
      25  #include <unistd.h>
      26  
      27  #include "c.h"
      28  #include "all-io.h"
      29  #include "nls.h"
      30  #include "strutils.h"
      31  #include "closestream.h"
      32  
      33  #include <signal.h>
      34  #include <sys/socket.h>
      35  #include <linux/if.h>
      36  
      37  #include <linux/tty.h>		/* for N_GSM0710 */
      38  
      39  #ifdef LINUX_GSMMUX_H
      40  # include <linux/gsmmux.h>	/* Add by guowenxue */
      41  #else
      42  struct gsm_config
      43  {
      44  	unsigned int adaption;
      45  	unsigned int encapsulation;
      46  	unsigned int initiator;
      47  	unsigned int t1;
      48  	unsigned int t2;
      49  	unsigned int t3;
      50  	unsigned int n2;
      51  	unsigned int mru;
      52  	unsigned int mtu;
      53  	unsigned int k;
      54  	unsigned int i;
      55  	unsigned int unused[8];		/* Padding for expansion without
      56  					   breaking stuff */
      57  };
      58  # define GSMIOC_GETCONF		_IOR('G', 0, struct gsm_config)
      59  # define GSMIOC_SETCONF		_IOW('G', 1, struct gsm_config)
      60  #endif
      61  
      62  #ifndef N_GIGASET_M101
      63  # define N_GIGASET_M101 16
      64  #endif
      65  
      66  #ifndef N_PPS
      67  # define N_PPS 18
      68  #endif
      69  
      70  #ifndef N_GSM0710
      71  # define N_GSM0710 21
      72  #endif
      73  
      74  #define MAXINTROPARMLEN 32
      75  
      76  /* attach a line discipline ioctl */
      77  #ifndef TIOCSETD
      78  # define TIOCSETD   0x5423
      79  #endif
      80  
      81  static int debug = 0;
      82  
      83  struct ld_table {
      84  	const char *name;
      85  	int value;
      86  };
      87  
      88  /* currently supported line disciplines, plus some aliases */
      89  static const struct ld_table ld_discs[] = {
      90  	{ "TTY",		N_TTY },
      91  	{ "SLIP",		N_SLIP },
      92  	{ "MOUSE",		N_MOUSE },
      93  	{ "PPP",		N_PPP },
      94  	{ "STRIP",		N_STRIP },
      95  	{ "AX25",		N_AX25 },
      96  	{ "X25",		N_X25 },
      97  	{ "6PACK",		N_6PACK },
      98  	{ "R3964",		N_R3964 },
      99  	{ "IRDA",		N_IRDA },
     100  	{ "HDLC",		N_HDLC },
     101  	{ "SYNC_PPP",		N_SYNC_PPP },
     102  	{ "SYNCPPP",		N_SYNC_PPP },
     103  	{ "HCI",		N_HCI },
     104  	{ "GIGASET_M101",	N_GIGASET_M101 },
     105  	{ "M101",		N_GIGASET_M101 },
     106  	{ "GIGASET",		N_GIGASET_M101 },
     107  	{ "PPS",		N_PPS },
     108  	{ "GSM0710",		N_GSM0710},
     109  	{ NULL,	0 }
     110  };
     111  
     112  /* known c_iflag names */
     113  static const struct ld_table ld_iflags[] =
     114  {
     115  	{ "IGNBRK",	IGNBRK },
     116  	{ "BRKINT",	BRKINT },
     117  	{ "IGNPAR",	IGNPAR },
     118  	{ "PARMRK",	PARMRK },
     119  	{ "INPCK",	INPCK },
     120  	{ "ISTRIP",	ISTRIP },
     121  	{ "INLCR",	INLCR },
     122  	{ "IGNCR",	IGNCR },
     123  	{ "ICRNL",	ICRNL },
     124  	{ "IUCLC",	IUCLC },
     125  	{ "IXON",	IXON },
     126  	{ "IXANY",	IXANY },
     127  	{ "IXOFF",	IXOFF },
     128  	{ "IMAXBEL",	IMAXBEL },
     129  	{ "IUTF8",	IUTF8 },
     130  	{ NULL,		0 }
     131  };
     132  
     133  static void __attribute__((__format__ (__printf__, 1, 2)))
     134  	dbg(char *fmt, ...)
     135  {
     136  	va_list args;
     137  
     138  	if (debug == 0)
     139  		return;
     140  	fflush(NULL);
     141  	va_start(args, fmt);
     142  #ifdef HAVE_VWARNX
     143  	vwarnx(fmt, args);
     144  #else
     145  	fprintf(stderr, "%s: ", program_invocation_short_name);
     146  	vfprintf(stderr, fmt, args);
     147  	fprintf(stderr, "\n");
     148  #endif
     149  	va_end(args);
     150  	fflush(NULL);
     151  }
     152  
     153  static int lookup_table(const struct ld_table *tab, const char *str)
     154  {
     155  	const struct ld_table *t;
     156  
     157  	for (t = tab; t && t->name; t++)
     158  		if (!strcasecmp(t->name, str))
     159  			return t->value;
     160  	return -1;
     161  }
     162  
     163  static void print_table(FILE * out, const struct ld_table *tab)
     164  {
     165  	const struct ld_table *t;
     166  	int i;
     167  
     168  	for (t = tab, i = 1; t && t->name; t++, i++) {
     169  		fprintf(out, "  %-12s", t->name);
     170  		if (!(i % 5))
     171  			fputc('\n', out);
     172  	}
     173  }
     174  
     175  static int parse_iflag(char *str, int *set_iflag, int *clr_iflag)
     176  {
     177  	int iflag;
     178  	char *s;
     179  
     180  	for (s = strtok(str, ","); s != NULL; s = strtok(NULL, ",")) {
     181  		if (*s == '-')
     182  			s++;
     183  		if ((iflag = lookup_table(ld_iflags, s)) < 0)
     184  			iflag = strtos32_or_err(s, _("invalid iflag"));
     185  		if (s > str && *(s - 1) == '-')
     186  			*clr_iflag |= iflag;
     187  		else
     188  			*set_iflag |= iflag;
     189  	}
     190  	dbg("iflag (set/clear): %d/%d", *set_iflag, *clr_iflag);
     191  	return 0;
     192  }
     193  
     194  
     195  static void __attribute__((__noreturn__)) usage(void)
     196  {
     197  	FILE *out = stdout;
     198  
     199  	fputs(USAGE_HEADER, out);
     200  	fprintf(out, _(" %s [options] <ldisc> <device>\n"), program_invocation_short_name);
     201  
     202  	fputs(USAGE_SEPARATOR, out);
     203  	fputs(_("Attach a line discipline to a serial line.\n"), out);
     204  
     205  	fputs(USAGE_OPTIONS, out);
     206  	fputs(_(" -d, --debug             print verbose messages to stderr\n"), out);
     207  	fputs(_(" -s, --speed <value>     set serial line speed\n"), out);
     208  	fputs(_(" -c, --intro-command <string> intro sent before ldattach\n"), out);
     209  	fputs(_(" -p, --pause <seconds>   pause between intro and ldattach\n"), out);
     210  	fputs(_(" -7, --sevenbits         set character size to 7 bits\n"), out);
     211  	fputs(_(" -8, --eightbits         set character size to 8 bits\n"), out);
     212  	fputs(_(" -n, --noparity          set parity to none\n"), out);
     213  	fputs(_(" -e, --evenparity        set parity to even\n"), out);
     214  	fputs(_(" -o, --oddparity         set parity to odd\n"), out);
     215  	fputs(_(" -1, --onestopbit        set stop bits to one\n"), out);
     216  	fputs(_(" -2, --twostopbits       set stop bits to two\n"), out);
     217  	fputs(_(" -i, --iflag [-]<iflag>  set input mode flag\n"), out);
     218  
     219  	fputs(USAGE_SEPARATOR, out);
     220  	printf(USAGE_HELP_OPTIONS(25));
     221  
     222  	fputs(_("\nKnown <ldisc> names:\n"), out);
     223  	print_table(out, ld_discs);
     224  	fputs(USAGE_SEPARATOR, out);
     225  
     226  	fputs(_("\nKnown <iflag> names:\n"), out);
     227  	print_table(out, ld_iflags);
     228  
     229  	printf(USAGE_MAN_TAIL("ldattach(8)"));
     230  	exit(EXIT_SUCCESS);
     231  }
     232  
     233  static int my_cfsetspeed(struct termios *ts, int speed)
     234  {
     235  	/* Standard speeds
     236  	 * -- cfsetspeed() is able to translate number to Bxxx constants
     237  	 */
     238  	if (cfsetspeed(ts, speed) == 0)
     239  		return 0;
     240  
     241  	/* Nonstandard speeds
     242  	 * -- we have to bypass glibc and set the speed manually (because glibc
     243  	 *    checks for speed and supports Bxxx bit rates only)...
     244  	 */
     245  #if _HAVE_STRUCT_TERMIOS_C_ISPEED
     246  # define BOTHER 0010000		/* non standard rate */
     247  	dbg("using non-standard speeds");
     248  	ts->c_ospeed = ts->c_ispeed = speed;
     249  	ts->c_cflag &= ~CBAUD;
     250  	ts->c_cflag |= BOTHER;
     251  	return 0;
     252  #else
     253  	return -1;
     254  #endif
     255  }
     256  
     257  static void handler(int s)
     258  {
     259  	dbg("got SIG %i -> exiting", s);
     260  	exit(EXIT_SUCCESS);
     261  }
     262  
     263  static void gsm0710_set_conf(int tty_fd)
     264  {
     265  	struct gsm_config c;
     266  
     267  	/* Add by guowenxue */
     268  	/*  get n_gsm configuration */
     269  	ioctl(tty_fd, GSMIOC_GETCONF, &c);
     270  	/*  we are initiator and need encoding 0 (basic) */
     271  	c.initiator = 1;
     272  	c.encapsulation = 0;
     273  	/*  our modem defaults to a maximum size of 127 bytes */
     274  	c.mru = 127;
     275  	c.mtu = 127;
     276  	/*  set the new configuration */
     277  	ioctl(tty_fd, GSMIOC_SETCONF, &c);
     278  	/* Add by guowenxue end*/
     279  }
     280  
     281  int main(int argc, char **argv)
     282  {
     283  	int tty_fd;
     284  	struct termios ts;
     285  	int speed = 0, bits = '-', parity = '-', stop = '-';
     286  	int set_iflag = 0, clr_iflag = 0;
     287  	int ldisc;
     288  	int optc;
     289  	char *dev;
     290  	int intropause = 1;
     291  	char *introparm = NULL;
     292  
     293  	static const struct option opttbl[] = {
     294  		{"speed", required_argument, NULL, 's'},
     295  		{"sevenbits", no_argument, NULL, '7'},
     296  		{"eightbits", no_argument, NULL, '8'},
     297  		{"noparity", no_argument, NULL, 'n'},
     298  		{"evenparity", no_argument, NULL, 'e'},
     299  		{"oddparity", no_argument, NULL, 'o'},
     300  		{"onestopbit", no_argument, NULL, '1'},
     301  		{"twostopbits", no_argument, NULL, '2'},
     302  		{"iflag", required_argument, NULL, 'i'},
     303  		{"help", no_argument, NULL, 'h'},
     304  		{"version", no_argument, NULL, 'V'},
     305  		{"debug", no_argument, NULL, 'd'},
     306  	        {"intro-command", required_argument, NULL, 'c'},
     307  	        {"pause", required_argument, NULL, 'p'},
     308  		{NULL, 0, NULL, 0}
     309  	};
     310  
     311  	signal(SIGKILL, handler);
     312  	signal(SIGINT, handler);
     313  
     314  	setlocale(LC_ALL, "");
     315  	bindtextdomain(PACKAGE, LOCALEDIR);
     316  	textdomain(PACKAGE);
     317  	close_stdout_atexit();
     318  
     319  	/* parse options */
     320  	if (argc == 0)
     321  		errx(EXIT_FAILURE, _("bad usage"));
     322  
     323  	while ((optc =
     324  		getopt_long(argc, argv, "dhV78neo12s:i:c:p:", opttbl,
     325  			    NULL)) >= 0) {
     326  		switch (optc) {
     327  		case 'd':
     328  			debug = 1;
     329  			break;
     330  		case '1':
     331  		case '2':
     332  			stop = optc;
     333  			break;
     334  		case '7':
     335  		case '8':
     336  			bits = optc;
     337  			break;
     338  		case 'n':
     339  		case 'e':
     340  		case 'o':
     341  			parity = optc;
     342  			break;
     343  		case 's':
     344  			speed = strtos32_or_err(optarg, _("invalid speed argument"));
     345  			break;
     346  		case 'p':
     347  			intropause = strtou32_or_err(optarg, _("invalid pause argument"));
     348  			if (intropause > 10)
     349  				errx(EXIT_FAILURE, "invalid pause: %s", optarg);
     350  			break;
     351  		case 'c':
     352  			introparm = optarg;
     353  			break;
     354  		case 'i':
     355  			parse_iflag(optarg, &set_iflag, &clr_iflag);
     356  			break;
     357  
     358  		case 'V':
     359  			print_version(EXIT_SUCCESS);
     360  		case 'h':
     361  			usage();
     362  		default:
     363  			errtryhelp(EXIT_FAILURE);
     364  		}
     365  	}
     366  
     367  	if (argc - optind != 2) {
     368  		warnx(_("not enough arguments"));
     369  		errtryhelp(EXIT_FAILURE);
     370  	}
     371  	/* parse line discipline specification */
     372  	ldisc = lookup_table(ld_discs, argv[optind]);
     373  	if (ldisc < 0)
     374  		ldisc = strtos32_or_err(argv[optind], _("invalid line discipline argument"));
     375  
     376  	/* ldisc specific option settings */
     377  	if (ldisc == N_GIGASET_M101) {
     378  		/* device specific defaults for line speed and data format */
     379  		if (speed == 0)
     380  			speed = 115200;
     381  		if (bits == '-')
     382  			bits = '8';
     383  		if (parity == '-')
     384  			parity = 'n';
     385  		if (stop == '-')
     386  			stop = '1';
     387  	}
     388  
     389  	/* open device */
     390  	dev = argv[optind + 1];
     391  	if ((tty_fd = open(dev, O_RDWR | O_NOCTTY)) < 0)
     392  		err(EXIT_FAILURE, _("cannot open %s"), dev);
     393  	if (!isatty(tty_fd))
     394  		errx(EXIT_FAILURE, _("%s is not a serial line"), dev);
     395  
     396  	dbg("opened %s", dev);
     397  
     398  	/* set line speed and format */
     399  	if (tcgetattr(tty_fd, &ts) < 0)
     400  		err(EXIT_FAILURE,
     401  		    _("cannot get terminal attributes for %s"), dev);
     402  	cfmakeraw(&ts);
     403  	if (speed && my_cfsetspeed(&ts, speed) < 0)
     404  		errx(EXIT_FAILURE, _("speed %d unsupported"), speed);
     405  
     406  	switch (stop) {
     407  	case '1':
     408  		ts.c_cflag &= ~CSTOPB;
     409  		break;
     410  	case '2':
     411  		ts.c_cflag |= CSTOPB;
     412  		break;
     413  	case '-':
     414  		break;
     415  	default:
     416  		abort();
     417  	}
     418  	switch (bits) {
     419  	case '7':
     420  		ts.c_cflag = (ts.c_cflag & ~CSIZE) | CS7;
     421  		break;
     422  	case '8':
     423  		ts.c_cflag = (ts.c_cflag & ~CSIZE) | CS8;
     424  		break;
     425  	case '-':
     426  		break;
     427  	default:
     428  		abort();
     429  	}
     430  	switch (parity) {
     431  	case 'n':
     432  		ts.c_cflag &= ~(PARENB | PARODD);
     433  		break;
     434  	case 'e':
     435  		ts.c_cflag |= PARENB;
     436  		ts.c_cflag &= ~PARODD;
     437  		break;
     438  	case 'o':
     439  		ts.c_cflag |= (PARENB | PARODD);
     440  		break;
     441  	case '-':
     442  		break;
     443  	default:
     444  		abort();
     445  	}
     446  
     447  	ts.c_cflag |= CREAD;	/* just to be on the safe side */
     448  	ts.c_iflag |= set_iflag;
     449  	ts.c_iflag &= ~clr_iflag;
     450  
     451  	if (tcsetattr(tty_fd, TCSAFLUSH, &ts) < 0)
     452  		err(EXIT_FAILURE,
     453  		    _("cannot set terminal attributes for %s"), dev);
     454  
     455  	dbg("set to raw %d %c%c%c: cflag=0x%x",
     456  	    speed, bits, parity, stop, ts.c_cflag);
     457  
     458  	if (introparm && *introparm)
     459  	{
     460  		dbg("intro command is '%s'", introparm);
     461  		if (write_all(tty_fd, introparm, strlen(introparm)) != 0)
     462  			err(EXIT_FAILURE,
     463  			    _("cannot write intro command to %s"), dev);
     464  
     465  		if (intropause) {
     466  			dbg("waiting for %d seconds", intropause);
     467  			sleep(intropause);
     468  		}
     469  	}
     470  
     471  	/* Attach the line discipline. */
     472  	if (ioctl(tty_fd, TIOCSETD, &ldisc) < 0)
     473  		err(EXIT_FAILURE, _("cannot set line discipline"));
     474  
     475  	dbg("line discipline set to %d", ldisc);
     476  
     477  	/* ldisc specific post-attach actions */
     478  	if (ldisc == N_GSM0710)
     479  		gsm0710_set_conf(tty_fd);
     480  
     481  	/* Go into background if not in debug mode. */
     482  	if (!debug && daemon(0, 0) < 0)
     483  		err(EXIT_FAILURE, _("cannot daemonize"));
     484  
     485  	/* Sleep to keep the line discipline active. */
     486  	pause();
     487  
     488  	exit(EXIT_SUCCESS);
     489  }