764 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			764 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* vi: set sw=4 ts=4: */
 | |
| /*
 | |
|  * setserial implementation for busybox
 | |
|  *
 | |
|  *
 | |
|  * Copyright (C) 2011 Marek Bečka <yuen@klacno.sk>
 | |
|  *
 | |
|  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 | |
|  */
 | |
| //config:config SETSERIAL
 | |
| //config:	bool "setserial (6.9 kb)"
 | |
| //config:	default y
 | |
| //config:	select PLATFORM_LINUX
 | |
| //config:	help
 | |
| //config:	Retrieve or set Linux serial port.
 | |
| 
 | |
| //applet:IF_SETSERIAL(APPLET_NOEXEC(setserial, setserial, BB_DIR_BIN, BB_SUID_DROP, setserial))
 | |
| 
 | |
| //kbuild:lib-$(CONFIG_SETSERIAL) += setserial.o
 | |
| 
 | |
| #include "libbb.h"
 | |
| #include <assert.h>
 | |
| 
 | |
| #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
 | |
| 
 | |
| //usage:#define setserial_trivial_usage
 | |
| //usage:	"[-abGvz] { DEVICE [PARAMETER [ARG]]... | -g DEVICE... }"
 | |
| //usage:#define setserial_full_usage "\n\n"
 | |
| //usage:	"Print or set serial port parameters"
 | |
| //usage:   "\n"
 | |
| //usage:   "\n""	-a	Print all"
 | |
| //usage:   "\n""	-b	Print summary"
 | |
| //usage:   "\n""	-G	Print as setserial PARAMETERs"
 | |
| //usage:   "\n""	-v	Verbose"
 | |
| //usage:   "\n""	-z	Zero out serial flags before setting"
 | |
| //usage:   "\n""	-g	All args are device names"
 | |
| //usage:   "\n"
 | |
| //usage:   "\n""PARAMETERs: (* = takes ARG, ^ = can be turned off by preceding ^)"
 | |
| //usage:   "\n""	*port, *irq, *divisor, *uart, *baud_base, *close_delay, *closing_wait,"
 | |
| //usage:   "\n""	^fourport, ^auto_irq, ^skip_test, ^sak, ^session_lockout, ^pgrp_lockout,"
 | |
| //usage:   "\n""	^callout_nohup, ^split_termios, ^hup_notify, ^low_latency, autoconfig,"
 | |
| //usage:   "\n""	spd_normal, spd_hi, spd_vhi, spd_shi, spd_warp, spd_cust"
 | |
| //usage:   "\n""ARG for uart:"
 | |
| //usage:   "\n""	unknown, 8250, 16450, 16550, 16550A, Cirrus, 16650, 16650V2, 16750,"
 | |
| //usage:   "\n""	16950, 16954, 16654, 16850, RSA, NS16550A, XSCALE, RM9000, OCTEON, AR7,"
 | |
| //usage:   "\n""	U6_16550A"
 | |
| 
 | |
| // option string is "bGavzgq". "q" is accepted but ignored.
 | |
| #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_LIST_OF_DEVS        (1 << 5)
 | |
| /*#define OPT_QUIET             (1 << 6)*/
 | |
| 
 | |
| #define OPT_MODE_MASK \
 | |
| 	(OPT_PRINT_ALL | OPT_PRINT_SUMMARY | OPT_PRINT_FEDBACK)
 | |
| 
 | |
| enum print_mode
 | |
| {
 | |
| 	PRINT_NORMAL  = 0,
 | |
| 	PRINT_SUMMARY = (1 << 0),
 | |
| 	PRINT_FEDBACK = (1 << 1),
 | |
| 	PRINT_ALL     = (1 << 2),
 | |
| };
 | |
| 
 | |
| #define CTL_SET                 (1 << 0)
 | |
| #define CTL_CONFIG              (1 << 1)
 | |
| #define CTL_GET                 (1 << 2)
 | |
| #define CTL_CLOSE               (1 << 3)
 | |
| #define CTL_NODIE               (1 << 4)
 | |
| 
 | |
| static const char serial_types[] ALIGN1 =
 | |
| 	"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
 | |
| 
 | |
| static const char commands[] ALIGN1 =
 | |
| 	"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"
 | |
| 	"baud_base\0"
 | |
| 	"close_delay\0"
 | |
| 	"closing_wait\0"
 | |
| 
 | |
| 	"autoconfig\0"
 | |
| ;
 | |
| 
 | |
| enum
 | |
| {
 | |
| 	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,
 | |
| 
 | |
| 	CMD_FLAG_FIRST = CMD_FLAG_SAK,
 | |
| 	CMD_FLAG_LAST  = CMD_FLAG_LOW_LATENCY,
 | |
| };
 | |
| 
 | |
| static bool cmd_noprint(int cmd)
 | |
| {
 | |
| 	return (cmd >= CMD_FLAG_SKIP_TEST && cmd <= CMD_FLAG_CALLOUT_NOHUP);
 | |
| }
 | |
| 
 | |
| static bool cmd_is_flag(int cmd)
 | |
| {
 | |
| 	return (cmd >= CMD_FLAG_FIRST && cmd <= CMD_FLAG_LAST);
 | |
| }
 | |
| 
 | |
| static bool cmd_needs_arg(int 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
 | |
| };
 | |
| 
 | |
| #define STR_INFINITE "infinite"
 | |
| #define STR_NONE     "none"
 | |
| 
 | |
| static const char *uart_type(int type)
 | |
| {
 | |
| 	if (type > MAX_SERIAL_TYPE)
 | |
| 		return "undefined";
 | |
| 
 | |
| 	return nth_string(serial_types, type);
 | |
| }
 | |
| 
 | |
| /* libbb candidate */
 | |
| static int index_in_strings_case_insensitive(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;
 | |
| }
 | |
| 
 | |
| static int uart_id(const char *name)
 | |
| {
 | |
| 	return index_in_strings_case_insensitive(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, bool quiet)
 | |
| {
 | |
| 	int fd;
 | |
| 
 | |
| 	fd = device_open(dev, O_RDWR | O_NONBLOCK);
 | |
| 	if (fd < 0 && !quiet)
 | |
| 		bb_simple_perror_msg(dev);
 | |
| 
 | |
| 	return fd;
 | |
| }
 | |
| 
 | |
| static int serial_ctl(int fd, int ops, struct serial_struct *serinfo)
 | |
| {
 | |
| 	int ret = 0;
 | |
| 	const char *err;
 | |
| 
 | |
| 	if (ops & CTL_SET) {
 | |
| 		ret = ioctl(fd, TIOCSSERIAL, serinfo);
 | |
| 		if (ret < 0) {
 | |
| 			err = "can't set serial info";
 | |
| 			goto fail;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (ops & CTL_CONFIG) {
 | |
| 		ret = ioctl(fd, TIOCSERCONFIG);
 | |
| 		if (ret < 0) {
 | |
| 			err = "can't autoconfigure port";
 | |
| 			goto fail;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (ops & CTL_GET) {
 | |
| 		ret = ioctl(fd, TIOCGSERIAL, serinfo);
 | |
| 		if (ret < 0) {
 | |
| 			err = "can't get serial info";
 | |
| 			goto fail;
 | |
| 		}
 | |
| 	}
 | |
|  nodie:
 | |
| 	if (ops & CTL_CLOSE)
 | |
| 		close(fd);
 | |
| 
 | |
| 	return ret;
 | |
|  fail:
 | |
| 	bb_simple_perror_msg(err);
 | |
| 	if (ops & CTL_NODIE)
 | |
| 		goto nodie;
 | |
| 	exit(EXIT_FAILURE);
 | |
| }
 | |
| 
 | |
| static void print_flag(const char **prefix, const char *flag)
 | |
| {
 | |
| 	printf("%s%s", *prefix, flag);
 | |
| 	*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, /*quiet:*/ mode == PRINT_SUMMARY);
 | |
| 	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 int find_cmd(const char *cmd)
 | |
| {
 | |
| 	int idx;
 | |
| 
 | |
| 	idx = index_in_strings_case_insensitive(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;
 | |
| 	int fd;
 | |
| 
 | |
| 	fd = serial_open(*arg, /*quiet:*/ false);
 | |
| 	if (fd < 0)
 | |
| 		exit(201);
 | |
| 
 | |
| 	serial_ctl(fd, CTL_GET, &serinfo);
 | |
| 
 | |
| 	if (opts & OPT_ZERO)
 | |
| 		serinfo.flags = 0;
 | |
| 
 | |
| 	while (*++arg) {
 | |
| 		const char *word;
 | |
| 		int invert;
 | |
| 		int cmd;
 | |
| 
 | |
| 		word = *arg;
 | |
| 		invert = (word[0] == '^');
 | |
| 		word += invert;
 | |
| 
 | |
| 		cmd = find_cmd(word);
 | |
| 
 | |
| 		if (cmd_needs_arg(cmd))
 | |
| 			if (*++arg == NULL)
 | |
| 				bb_error_msg_and_die(bb_msg_requires_arg, word);
 | |
| 
 | |
| 		if (invert && !cmd_is_flag(cmd))
 | |
| 			bb_error_msg_and_die("can't invert %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 UNUSED_PARAM, char **argv)
 | |
| {
 | |
| 	int opts;
 | |
| 
 | |
| 	opts = getopt32(argv, "^" "bGavzgq" "\0" "-1:b-aG:G-ab:a-bG");
 | |
| 	argv += optind;
 | |
| 
 | |
| 	if (!argv[1]) /* one arg only? (nothing to change?) */
 | |
| 		opts |= OPT_LIST_OF_DEVS; /* force display */
 | |
| 
 | |
| 	if (!(opts & OPT_LIST_OF_DEVS)) {
 | |
| 		serial_set(argv, opts);
 | |
| 		argv[1] = NULL;
 | |
| 	}
 | |
| 
 | |
| 	/* -v effect: "after setting params, do not be silent, show them" */
 | |
| 	if (opts & (OPT_VERBOSE | OPT_LIST_OF_DEVS)) {
 | |
| 		do {
 | |
| 			serial_get(*argv, opts & OPT_MODE_MASK);
 | |
| 		} while (*++argv);
 | |
| 	}
 | |
| 
 | |
| 	return EXIT_SUCCESS;
 | |
| }
 | 
