This is my first busybox applet, so comments are welcome.
>From 8b1296124152c768f0da0c41b9025b4f0f4592bd Mon Sep 17 00:00:00 2001
From: Marek Becka <[email protected]>
Date: Wed, 20 Apr 2011 06:31:55 +0200
Subject: [PATCH] New applet: setserial

---
 include/applets.src.h        |    1 +
 include/libbb.h              |    1 +
 libbb/compare_string_array.c |   14 +
 miscutils/Config.src         |    7 +
 miscutils/Kbuild.src         |    1 +
 miscutils/setserial.c        |  743 ++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 767 insertions(+), 0 deletions(-)
 create mode 100644 miscutils/setserial.c

diff --git a/include/applets.src.h b/include/applets.src.h
index 133f376..4ad2907 100644
--- a/include/applets.src.h
+++ b/include/applets.src.h
@@ -328,6 +328,7 @@ IF_SETFONT(APPLET(setfont, BB_DIR_USR_SBIN, BB_SUID_DROP))
 IF_SETKEYCODES(APPLET(setkeycodes, BB_DIR_USR_BIN, BB_SUID_DROP))
 IF_SETLOGCONS(APPLET(setlogcons, BB_DIR_USR_SBIN, BB_SUID_DROP))
 IF_SETSEBOOL(APPLET(setsebool, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_SETSERIAL(APPLET(setserial, BB_DIR_BIN, BB_SUID_DROP))
 IF_SETSID(APPLET(setsid, BB_DIR_USR_BIN, BB_SUID_DROP))
 IF_SETUIDGID(APPLET_ODDNAME(setuidgid, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, setuidgid))
 IF_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum))
diff --git a/include/libbb.h b/include/libbb.h
index 34f7f6a..202e126 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1274,6 +1274,7 @@ extern int update_passwd(const char *filename,
 
 int index_in_str_array(const char *const string_array[], const char *key) FAST_FUNC;
 int index_in_strings(const char *strings, const char *key) FAST_FUNC;
+int index_in_strings_case(const char *strings, const char *key) FAST_FUNC;
 int index_in_substr_array(const char *const string_array[], const char *key) FAST_FUNC;
 int index_in_substrings(const char *strings, const char *key) FAST_FUNC;
 const char *nth_string(const char *strings, int n) FAST_FUNC;
diff --git a/libbb/compare_string_array.c b/libbb/compare_string_array.c
index 4b10cc1..9c2625d 100644
--- a/libbb/compare_string_array.c
+++ b/libbb/compare_string_array.c
@@ -33,6 +33,20 @@ int FAST_FUNC index_in_strings(const char *strings, const char *key)
 	return -1;
 }
 
+int FAST_FUNC index_in_strings_case(const char *strings, const char *key)
+{
+	int idx = 0;
+
+	while (*strings) {
+		if (strcasecmp(strings, key) == 0) {
+			return idx;
+		}
+		strings += strlen(strings) + 1; /* skip NUL */
+		idx++;
+	}
+	return -1;
+}
+
 /* returns the array index of the string, even if it matches only a beginning */
 /* (index of first match is returned, or -1) */
 #ifdef UNUSED
diff --git a/miscutils/Config.src b/miscutils/Config.src
index 6152914..afb00ca 100644
--- a/miscutils/Config.src
+++ b/miscutils/Config.src
@@ -598,6 +598,13 @@ config RX
 	help
 	  Receive files using the Xmodem protocol.
 
+config SETSERIAL
+	bool "setserial"
+	default y
+	depends on PLATFORM_LINUX
+	help
+	  Retrieve or set Linux serial port.
+
 config SETSID
 	bool "setsid"
 	default y
diff --git a/miscutils/Kbuild.src b/miscutils/Kbuild.src
index 8c49864..3bc04e2 100644
--- a/miscutils/Kbuild.src
+++ b/miscutils/Kbuild.src
@@ -39,6 +39,7 @@ lib-$(CONFIG_READAHEAD)   += readahead.o
 lib-$(CONFIG_RFKILL)      += rfkill.o
 lib-$(CONFIG_RUNLEVEL)    += runlevel.o
 lib-$(CONFIG_RX)          += rx.o
+lib-$(CONFIG_SETSERIAL)   += setserial.o
 lib-$(CONFIG_SETSID)      += setsid.o
 lib-$(CONFIG_STRINGS)     += strings.o
 lib-$(CONFIG_TASKSET)     += taskset.o
diff --git a/miscutils/setserial.c b/miscutils/setserial.c
new file mode 100644
index 0000000..8618067
--- /dev/null
+++ b/miscutils/setserial.c
@@ -0,0 +1,743 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * setserial implementation for busybox
+ *
+ *
+ * Copyright (C) 2011 Marek Bečka <[email protected]>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include <assert.h>
+
+//usage:#define setserial_trivial_usage
+//usage:	"[-gabGvzV] DEVICE [PARAMETER [ARG]] ..."
+//usage:#define setserial_full_usage "\n\n"
+//usage:	"Request or set Linux serial port information.\n\n"
+//usage:	"Options:\n"
+//usage:	"	-g	Interpret parameters as list of devices for "
+//usage:	"reporting.\n"
+//usage:	"	-a	When reporting, print all available information.\n"
+//usage:	"	-b	When reporting, print summary information.\n"
+//usage:	"	-G	When reporting, print in form which can be fed back\n"
+//usage:	"		to setserial as command line parameters.\n"
+//usage:	"	-v	Be verbose.\n"
+//usage:	"	-z	Zero out serial flags before starting to set flags.\n"
+//usage:	"	-V	Display program version and exit.\n\n"
+//usage:	"Parameters:	(* = Takes an argument, ^ = Can be turned off by preceding '^')\n"
+//usage:	"	*port, *irq, *divisor, *uart, *baund_base, *close_delay, *closing_wait,\n"
+//usage:	"	^fourport, ^auto_irq, ^skip_test, ^sak, ^session_lockout, ^pgrp_lockout,\n"
+//usage:	"	^callout_nohup, ^split_termios, ^hup_notify, ^low_latency, autoconfig\n"
+//usage:	"	spd_normal, spd_hi, spd_vhi, spd_shi, spd_warp, spd_cust\n\n"
+//usage:	"UART types:\n"
+//usage:	"	unknown, 8250, 16450, 16550, 16550A, Cirrus, 16650, 16650V2, 16750,\n"
+//usage:	"	16950, 16954, 16654, 16850, RSA, NS16550A, XSCALE, RM9000, OCTEON, AR7\n"
+//usage:	"	U6_16550A"
+
+#ifndef PORT_UNKNOWN
+# define PORT_UNKNOWN		 0
+#endif
+#ifndef PORT_8250
+# define PORT_8250		 1
+#endif
+#ifndef PORT_16450
+# define PORT_16450		 2
+#endif
+#ifndef PORT_16550
+# define PORT_16550		 3
+#endif
+#ifndef PORT_16550A
+# define PORT_16550A		 4
+#endif
+#ifndef PORT_CIRRUS
+# define PORT_CIRRUS		 5
+#endif
+#ifndef PORT_16650
+# define PORT_16650		 6
+#endif
+#ifndef PORT_16650V2
+# define PORT_16650V2		 7
+#endif
+#ifndef PORT_16750
+# define PORT_16750		 8
+#endif
+#ifndef PORT_STARTECH
+# define PORT_STARTECH		 9
+#endif
+#ifndef PORT_16C950
+# define PORT_16C950		10
+#endif
+#ifndef PORT_16654
+# define PORT_16654		11
+#endif
+#ifndef PORT_16850
+# define PORT_16850		12
+#endif
+#ifndef PORT_RSA
+# define PORT_RSA		13
+#endif
+#ifndef PORT_NS16550A
+# define PORT_NS16550A		14
+#endif
+#ifndef PORT_XSCALE
+# define PORT_XSCALE		15
+#endif
+#ifndef PORT_RM9000
+# define PORT_RM9000		16
+#endif
+#ifndef PORT_OCTEON
+# define PORT_OCTEON		17
+#endif
+#ifndef PORT_AR7
+# define PORT_AR7		18
+#endif
+#ifndef PORT_U6_16550A
+# define PORT_U6_16550A		19
+#endif
+
+#ifndef ASYNCB_HUP_NOTIFY
+# define ASYNCB_HUP_NOTIFY	 0
+#endif
+#ifndef ASYNCB_FOURPORT
+# define ASYNCB_FOURPORT	 1
+#endif
+#ifndef ASYNCB_SAK
+# define ASYNCB_SAK		 2
+#endif
+#ifndef ASYNCB_SPLIT_TERMIOS
+# define ASYNCB_SPLIT_TERMIOS	 3
+#endif
+#ifndef ASYNCB_SPD_HI
+# define ASYNCB_SPD_HI		 4
+#endif
+#ifndef ASYNCB_SPD_VHI
+# define ASYNCB_SPD_VHI		 5
+#endif
+#ifndef ASYNCB_SKIP_TEST
+# define ASYNCB_SKIP_TEST	 6
+#endif
+#ifndef ASYNCB_AUTO_IRQ
+# define ASYNCB_AUTO_IRQ	 7
+#endif
+#ifndef ASYNCB_SESSION_LOCKOUT
+# define ASYNCB_SESSION_LOCKOUT	 8
+#endif
+#ifndef ASYNCB_PGRP_LOCKOUT
+# define ASYNCB_PGRP_LOCKOUT	 9
+#endif
+#ifndef ASYNCB_CALLOUT_NOHUP
+# define ASYNCB_CALLOUT_NOHUP	10
+#endif
+#ifndef ASYNCB_SPD_SHI
+# define ASYNCB_SPD_SHI		12
+#endif
+#ifndef ASYNCB_LOW_LATENCY
+# define ASYNCB_LOW_LATENCY	13
+#endif
+#ifndef ASYNCB_BUGGY_UART
+# define ASYNCB_BUGGY_UART	14
+#endif
+
+#ifndef ASYNC_HUP_NOTIFY
+# define ASYNC_HUP_NOTIFY	(1U << ASYNCB_HUP_NOTIFY)
+#endif
+#ifndef ASYNC_FOURPORT
+# define ASYNC_FOURPORT		(1U << ASYNCB_FOURPORT)
+#endif
+#ifndef ASYNC_SAK
+# define ASYNC_SAK		(1U << ASYNCB_SAK)
+#endif
+#ifndef ASYNC_SPLIT_TERMIOS
+# define ASYNC_SPLIT_TERMIOS	(1U << ASYNCB_SPLIT_TERMIOS)
+#endif
+#ifndef ASYNC_SPD_HI
+# define ASYNC_SPD_HI		(1U << ASYNCB_SPD_HI)
+#endif
+#ifndef ASYNC_SPD_VHI
+# define ASYNC_SPD_VHI		(1U << ASYNCB_SPD_VHI)
+#endif
+#ifndef ASYNC_SKIP_TEST
+# define ASYNC_SKIP_TEST	(1U << ASYNCB_SKIP_TEST)
+#endif
+#ifndef ASYNC_AUTO_IRQ
+# define ASYNC_AUTO_IRQ		(1U << ASYNCB_AUTO_IRQ)
+#endif
+#ifndef ASYNC_SESSION_LOCKOUT
+# define ASYNC_SESSION_LOCKOUT	(1U << ASYNCB_SESSION_LOCKOUT)
+#endif
+#ifndef ASYNC_PGRP_LOCKOUT
+# define ASYNC_PGRP_LOCKOUT	(1U << ASYNCB_PGRP_LOCKOUT)
+#endif
+#ifndef ASYNC_CALLOUT_NOHUP
+# define ASYNC_CALLOUT_NOHUP	(1U << ASYNCB_CALLOUT_NOHUP)
+#endif
+#ifndef ASYNC_SPD_SHI
+# define ASYNC_SPD_SHI		(1U << ASYNCB_SPD_SHI)
+#endif
+#ifndef ASYNC_LOW_LATENCY
+# define ASYNC_LOW_LATENCY	(1U << ASYNCB_LOW_LATENCY)
+#endif
+#ifndef ASYNC_BUGGY_UART
+# define ASYNC_BUGGY_UART	(1U << ASYNCB_BUGGY_UART)
+#endif
+
+#ifndef ASYNC_SPD_CUST
+# define ASYNC_SPD_CUST		(ASYNC_SPD_HI|ASYNC_SPD_VHI)
+#endif
+#ifndef ASYNC_SPD_WARP
+# define ASYNC_SPD_WARP		(ASYNC_SPD_HI|ASYNC_SPD_SHI)
+#endif
+#ifndef ASYNC_SPD_MASK
+# define ASYNC_SPD_MASK		(ASYNC_SPD_HI|ASYNC_SPD_VHI|ASYNC_SPD_SHI)
+#endif
+
+#ifndef ASYNC_CLOSING_WAIT_INF
+# define ASYNC_CLOSING_WAIT_INF		0
+#endif
+#ifndef ASYNC_CLOSING_WAIT_NONE
+# define ASYNC_CLOSING_WAIT_NONE	65535
+#endif
+
+#ifndef _LINUX_SERIAL_H
+struct serial_struct {
+	int	type;
+	int	line;
+	unsigned int	port;
+	int	irq;
+	int	flags;
+	int	xmit_fifo_size;
+	int	custom_divisor;
+	int	baud_base;
+	unsigned short	close_delay;
+	char	io_type;
+	char	reserved_char[1];
+	int	hub6;
+	unsigned short	closing_wait; /* time to wait before closing */
+	unsigned short	closing_wait2; /* no longer used... */
+	unsigned char	*iomem_base;
+	unsigned short	iomem_reg_shift;
+	unsigned int	port_high;
+	unsigned long	iomap_base;	/* cookie passed into ioremap */
+};
+#endif
+
+#define OPT_PRINT_SUMMARY	(1 << 0)
+#define OPT_PRINT_FEDBACK	(1 << 1)
+#define OPT_PRINT_ALL		(1 << 2)
+#define OPT_VERBOSE		(1 << 3)
+#define OPT_ZERO		(1 << 4)
+#define OPT_GET			(1 << 5)
+
+#define OPT_MODE_MASK \
+(OPT_PRINT_ALL | OPT_PRINT_SUMMARY | OPT_PRINT_FEDBACK)
+
+#define	CTL_OPEN		(1 << 0)
+#define CTL_GET			(1 << 1)
+#define CTL_CONFIG		(1 << 2)
+#define CTL_SET			(1 << 3)
+#define CTL_CLOSE		(1 << 4)
+#define CTL_NODIE		(1 << 5)
+
+static const char *serial_types =
+	"unknown\0"		/* 0 */
+	"8250\0"		/* 1 */
+	"16450\0"		/* 2 */
+	"16550\0"		/* 3 */
+	"16550A\0"		/* 4 */
+	"Cirrus\0"		/* 5 */
+	"16650\0"		/* 6 */
+	"16650V2\0"		/* 7 */
+	"16750\0"		/* 8 */
+	"16950\0"		/* 9 UNIMPLEMENTED: also know as "16950/954" */
+	"16954\0"		/* 10 */
+	"16654\0"		/* 11 */
+	"16850\0"		/* 12 */
+	"RSA\0"			/* 13 */
+#ifndef SETSERIAL_BASE
+	"NS16550A\0"		/* 14 */
+	"XSCALE\0"		/* 15 */
+	"RM9000\0"		/* 16 */
+	"OCTEON\0"		/* 17 */
+	"AR7\0"			/* 18 */
+	"U6_16550A\0"		/* 19 */
+#endif
+;
+
+#ifndef SETSERIAL_BASE
+#define MAX_SERIAL_TYPE	19
+#else
+#define MAX_SERIAL_TYPE	13
+#endif
+
+enum print_mode
+{
+	PRINT_NORMAL	= 0,
+	PRINT_SUMMARY	= 1,
+	PRINT_FEDBACK	= 2,
+	PRINT_ALL	= 4
+};
+
+static const char *commands = 
+	"spd_normal\0"
+	"spd_hi\0"
+	"spd_vhi\0"
+	"spd_shi\0"
+	"spd_warp\0"
+	"spd_cust\0"
+
+	"sak\0"
+	"fourport\0"
+	"hup_notify\0"
+	"skip_test\0"
+	"auto_irq\0"
+	"split_termios\0"
+	"session_lockout\0"
+	"pgrp_lockout\0"
+	"callout_nohup\0"
+	"low_latency\0"
+
+	"port\0"
+	"irq\0"
+	"divisor\0"
+	"uart\0"
+	"baund_base\0"
+	"close_delay\0"
+	"closing_wait\0"
+
+	"autoconfig\0"
+;
+
+enum commands
+{
+	CMD_SPD_NORMAL = 0,
+	CMD_SPD_HI,
+	CMD_SPD_VHI,
+	CMD_SPD_SHI,
+	CMD_SPD_WARP,
+	CMD_SPD_CUST,
+
+	CMD_FLAG_SAK,
+	CMD_FLAG_FOURPORT,
+	CMD_FLAG_NUP_NOTIFY,
+	CMD_FLAG_SKIP_TEST,
+	CMD_FLAG_AUTO_IRQ,
+	CMD_FLAG_SPLIT_TERMIOS,
+	CMD_FLAG_SESSION_LOCKOUT,
+	CMD_FLAG_PGRP_LOCKOUT,
+	CMD_FLAG_CALLOUT_NOHUP,
+	CMD_FLAG_LOW_LATENCY,
+
+	CMD_PORT,
+	CMD_IRQ,
+	CMD_DIVISOR,
+	CMD_UART,
+	CMD_BASE,
+	CMD_DELAY,
+	CMD_WAIT,
+
+	CMD_AUTOCONFIG
+};
+
+#define _CMD_MAX		CMD_AUTOCONFIG
+#define _CMD_FLAG_FIRST		CMD_FLAG_SAK
+#define _CMD_FLAG_LAST		CMD_FLAG_LOW_LATENCY
+
+static bool cmd_noprint(enum commands cmd)
+{
+	return (cmd >= CMD_FLAG_SKIP_TEST && cmd <= CMD_FLAG_CALLOUT_NOHUP);
+}
+
+static bool cmd_is_flag(enum commands cmd)
+{
+	return (cmd >= _CMD_FLAG_FIRST && cmd <= _CMD_FLAG_LAST);
+}
+
+static bool cmd_need_arg(enum commands cmd)
+{
+	return (cmd >= CMD_PORT && cmd <= CMD_WAIT);
+}
+
+#define ALL_SPD		(ASYNC_SPD_HI | ASYNC_SPD_VHI | ASYNC_SPD_SHI | \
+	ASYNC_SPD_WARP | ASYNC_SPD_CUST)
+
+#define ALL_FLAGS	(ASYNC_SAK | ASYNC_FOURPORT | ASYNC_HUP_NOTIFY | \
+	ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ | ASYNC_SPLIT_TERMIOS | \
+	ASYNC_SESSION_LOCKOUT | ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP | \
+	ASYNC_LOW_LATENCY)
+
+#if (ALL_SPD | ALL_FLAGS) > 0xffff
+#error "Unexpected flags size"	
+#endif
+
+static const uint16_t setbits[_CMD_FLAG_LAST + 1] =
+{
+	0,
+	ASYNC_SPD_HI,
+	ASYNC_SPD_VHI,
+	ASYNC_SPD_SHI,
+	ASYNC_SPD_WARP,
+	ASYNC_SPD_CUST,
+
+	ASYNC_SAK,
+	ASYNC_FOURPORT,
+	ASYNC_HUP_NOTIFY,
+	ASYNC_SKIP_TEST,
+	ASYNC_AUTO_IRQ,
+	ASYNC_SPLIT_TERMIOS,
+	ASYNC_SESSION_LOCKOUT,
+	ASYNC_PGRP_LOCKOUT,
+	ASYNC_CALLOUT_NOHUP,
+	ASYNC_LOW_LATENCY
+};
+
+static const char const *STR_INFINITE = "infinite";
+static const char const *STR_NONE = "none";
+static const char const *STR_UNDEFINED = "undefined";
+
+static const char *uart_type(int type)
+{
+	if (type > MAX_SERIAL_TYPE)
+		return STR_UNDEFINED;
+
+	return nth_string(serial_types, type);
+}
+
+static int uart_id(const char *name)
+{
+	return index_in_strings_case(serial_types, name);
+}
+
+static const char *get_spd(int flags, enum print_mode mode)
+{
+	int idx;
+
+	switch (flags & ASYNC_SPD_MASK) {
+	case ASYNC_SPD_HI:
+		idx = CMD_SPD_HI;
+		break;
+	case ASYNC_SPD_VHI:
+		idx = CMD_SPD_VHI;
+		break;
+	case ASYNC_SPD_SHI:
+		idx = CMD_SPD_SHI;
+		break;
+	case ASYNC_SPD_WARP:
+		idx = CMD_SPD_WARP;
+		break;
+	case ASYNC_SPD_CUST:
+		idx = CMD_SPD_CUST;
+		break;
+	default:
+		if (mode < PRINT_FEDBACK)
+			return NULL;
+		idx = CMD_SPD_NORMAL;	
+	}
+
+	return nth_string(commands, idx);
+}
+
+static int get_numeric(const char *arg)
+{
+	return bb_strtol(arg, NULL, 0);
+}
+
+static int get_wait(const char *arg)
+{
+	if (strcasecmp(arg, STR_NONE) == 0)
+		return ASYNC_CLOSING_WAIT_NONE; 
+
+	if (strcasecmp(arg, STR_INFINITE) == 0)
+		return ASYNC_CLOSING_WAIT_INF; 
+
+	return get_numeric(arg);
+}
+
+static int get_uart(const char *arg)
+{
+	int uart = uart_id(arg);
+	
+	if (uart < 0)
+		bb_error_msg_and_die("Illegal UART type: %s", arg);
+
+	return uart;
+}
+
+static int serial_open(const char *dev)
+{
+	int fd;
+
+	fd = device_open(dev, O_RDWR | O_NONBLOCK);
+	if (fd < 0)
+		bb_simple_perror_msg(dev);
+	
+	return fd;
+}
+
+static int serial_ctl(int fd, int ops, struct serial_struct *serinfo)
+{
+	int ret;
+	const char *err;
+	
+	if (ops & CTL_SET) {
+		ret = ioctl(fd, TIOCSSERIAL, serinfo);
+		if (ret < 0) {
+			err = "Cannot set serial info";
+			goto fail;
+		}
+	}
+
+	if (ops & CTL_CONFIG) {
+		ret = ioctl(fd, TIOCSERCONFIG);
+		if (ret < 0) {
+			err = "Cannot autoconfigure port";
+			goto fail;
+		}
+	}
+
+	if (ops & CTL_GET) {
+		ret = ioctl(fd, TIOCGSERIAL, serinfo);
+		if (ret < 0) {
+			err = "Cannot get serial info";
+			goto fail;
+		}
+	}
+
+	if (ops & CTL_CLOSE)
+		close(fd);
+
+	return 0;
+fail:
+	bb_simple_perror_msg(err);
+	if (ops & CTL_NODIE)
+		return ret;
+	exit(EXIT_FAILURE);
+}
+
+static void print_flag(const char **prefix, const char *flag)
+{
+	fputs(*prefix, stdout);
+	fputs(flag, stdout);
+	*prefix = " ";
+}
+
+static void print_serial_flags(int serial_flags, enum print_mode mode, 
+                               const char *prefix, const char *postfix)
+{
+	int i;
+	const char *spd, *pr;
+	
+	pr = prefix;
+
+	spd = get_spd(serial_flags, mode);
+	if (spd)
+		print_flag(&pr, spd); 
+
+	for (i = _CMD_FLAG_FIRST; i <= _CMD_FLAG_LAST; i++)
+		if (serial_flags & setbits[i]
+		    && (mode > PRINT_SUMMARY || !cmd_noprint(i)))
+			print_flag(&pr, nth_string(commands, i));
+
+	puts(pr == prefix ? "" : postfix);
+}
+
+static void print_closing_wait(unsigned int closing_wait)
+{
+	switch (closing_wait) {
+	case ASYNC_CLOSING_WAIT_NONE:
+		puts(STR_NONE);
+		break;
+	case ASYNC_CLOSING_WAIT_INF:
+		puts(STR_INFINITE);
+		break;
+	default:
+		printf("%u\n", closing_wait);
+	}
+}
+
+static void serial_get(const char *device, enum print_mode mode)
+{
+	int fd, ret;
+	const char *uart, *prefix, *postfix;
+	struct serial_struct serinfo;
+
+	fd = serial_open(device);
+	if (fd < 0)
+		return;
+
+	ret = serial_ctl(fd, CTL_GET | CTL_CLOSE | CTL_NODIE, &serinfo);
+	if (ret < 0)
+		return;
+
+	uart = uart_type(serinfo.type);
+	prefix = ", Flags: ";
+	postfix = "";
+	
+	switch (mode) {
+	case PRINT_NORMAL:
+		printf("%s, UART: %s, Port: 0x%.4x, IRQ: %d",
+			device, uart, serinfo.port, serinfo.irq);
+		break;
+	case PRINT_SUMMARY:
+		if (!serinfo.type)
+			return;
+		printf("%s at 0x%.4x (irq = %d) is a %s",
+			device, serinfo.port, serinfo.irq, uart);
+		prefix = " (";
+		postfix = ")";
+		break;
+	case PRINT_FEDBACK:
+		printf("%s uart %s port 0x%.4x irq %d baud_base %d", device,
+			uart, serinfo.port, serinfo.irq, serinfo.baud_base);
+		prefix = " ";
+		break;
+	case PRINT_ALL:
+		printf("%s, Line %d, UART: %s, Port: 0x%.4x, IRQ: %d\n",
+			device, serinfo.line, uart, serinfo.port, serinfo.irq);
+		printf("\tBaud_base: %d, close_delay: %u, divisor: %d\n",
+			serinfo.baud_base, serinfo.close_delay,
+			serinfo.custom_divisor);
+		printf("\tclosing_wait: ");
+		print_closing_wait(serinfo.closing_wait);
+		prefix = "\tFlags: ";
+		postfix = "\n";
+		break;
+	default:
+		assert(0);
+	}
+	
+	print_serial_flags(serinfo.flags, mode, prefix, postfix);
+}
+
+static enum commands find_cmd(const char *cmd)
+{
+	int idx;
+	
+	idx = index_in_strings_case(commands, cmd);
+	if (idx < 0)
+		bb_error_msg_and_die("Invalid flag: %s", cmd);
+
+	return idx;
+}
+
+static void serial_set(char **arg, int opts)
+{
+	struct serial_struct serinfo;
+	enum commands cmd;
+	const char *word;
+	bool invert;
+	int fd;
+
+	fd = serial_open(*arg++);
+	if (fd < 0)
+		exit(201);
+
+	serial_ctl(fd, CTL_GET, &serinfo);
+	
+	if (opts & OPT_ZERO)
+		serinfo.flags = 0;
+
+	while (*arg) {
+		invert = false;
+		word = *arg++;
+
+		if (*word == '^') {
+			invert = true;
+			word++;
+		}
+
+		cmd = find_cmd(word);
+
+		if (*arg == NULL && cmd_need_arg(cmd)) 
+			bb_error_msg_and_die("Missing argument for %s", word);
+
+		if (invert && !cmd_is_flag(cmd)) 
+			bb_error_msg_and_die(
+				"This flag can not be inverted: %s", word);
+
+		switch (cmd) {
+		case CMD_SPD_NORMAL:
+		case CMD_SPD_HI:
+		case CMD_SPD_VHI:
+		case CMD_SPD_SHI:
+		case CMD_SPD_WARP:
+		case CMD_SPD_CUST:
+			serinfo.flags &= ASYNC_SPD_MASK;
+			/* fallthrough */
+		case CMD_FLAG_SAK:
+		case CMD_FLAG_FOURPORT:
+		case CMD_FLAG_NUP_NOTIFY:
+		case CMD_FLAG_SKIP_TEST:
+		case CMD_FLAG_AUTO_IRQ:
+		case CMD_FLAG_SPLIT_TERMIOS:
+		case CMD_FLAG_SESSION_LOCKOUT:
+		case CMD_FLAG_PGRP_LOCKOUT:
+		case CMD_FLAG_CALLOUT_NOHUP:
+		case CMD_FLAG_LOW_LATENCY:
+			if (invert)
+				serinfo.flags &= ~setbits[cmd];
+			else
+				serinfo.flags |= setbits[cmd]; 
+			break;
+		case CMD_PORT:
+			serinfo.port = get_numeric(*arg++);
+			break;
+		case CMD_IRQ:
+			serinfo.irq = get_numeric(*arg++);
+			break;
+		case CMD_DIVISOR:
+			serinfo.custom_divisor = get_numeric(*arg++);
+			break;
+		case CMD_UART:
+			serinfo.type = get_uart(*arg++);
+			break;
+		case CMD_BASE:
+			serinfo.baud_base = get_numeric(*arg++);
+			break;
+		case CMD_DELAY:
+			serinfo.close_delay = get_numeric(*arg++);
+			break;
+		case CMD_WAIT:
+			serinfo.closing_wait = get_wait(*arg++);
+			break;
+		case CMD_AUTOCONFIG:
+			serial_ctl(fd, CTL_SET | CTL_CONFIG | CTL_GET, 
+				&serinfo);
+			break;
+		default:
+			assert(0);
+		}
+	}
+
+	serial_ctl(fd, CTL_SET | CTL_CLOSE, &serinfo);
+}
+
+int setserial_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int setserial_main(int argc, char **argv)
+{
+	int opts;
+
+	opt_complementary = "b-aG:G-ab:a-bG:V-";
+	opts = getopt32(argv, "bGavzgV");
+	argv += optind;
+	argc -= optind;
+
+	if (argc == 0)
+		bb_show_usage();
+	if (argc == 1)
+		opts |= OPT_GET;
+
+	if (!(opts & OPT_GET)) {
+		serial_set(argv, opts);
+		argc = 1;
+	}
+
+	if (opts & (OPT_VERBOSE | OPT_GET)) {
+		do {
+			serial_get(*argv++, opts & OPT_MODE_MASK);
+		} while (--argc);
+	}
+
+	return EXIT_SUCCESS;
+}
+
-- 
1.7.2.2
_______________________________________________
busybox mailing list
[email protected]
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to