1 /* vi: set sw=4 ts=4: */
2 /*
3  * setserial implementation for busybox
4  *
5  *
6  * Copyright (C) 2011 Marek Bečka <yuen@klacno.sk>
7  *
8  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
9  */
10 //config:config SETSERIAL
11 //config:	bool "setserial (6.9 kb)"
12 //config:	default y
13 //config:	help
14 //config:	Retrieve or set Linux serial port.
15 
16 //applet:IF_SETSERIAL(APPLET_NOEXEC(setserial, setserial, BB_DIR_BIN, BB_SUID_DROP, setserial))
17 
18 //kbuild:lib-$(CONFIG_SETSERIAL) += setserial.o
19 
20 #include "libbb.h"
21 #include <assert.h>
22 
23 #ifndef PORT_UNKNOWN
24 # define PORT_UNKNOWN            0
25 #endif
26 #ifndef PORT_8250
27 # define PORT_8250               1
28 #endif
29 #ifndef PORT_16450
30 # define PORT_16450              2
31 #endif
32 #ifndef PORT_16550
33 # define PORT_16550              3
34 #endif
35 #ifndef PORT_16550A
36 # define PORT_16550A             4
37 #endif
38 #ifndef PORT_CIRRUS
39 # define PORT_CIRRUS             5
40 #endif
41 #ifndef PORT_16650
42 # define PORT_16650              6
43 #endif
44 #ifndef PORT_16650V2
45 # define PORT_16650V2            7
46 #endif
47 #ifndef PORT_16750
48 # define PORT_16750              8
49 #endif
50 #ifndef PORT_STARTECH
51 # define PORT_STARTECH           9
52 #endif
53 #ifndef PORT_16C950
54 # define PORT_16C950            10
55 #endif
56 #ifndef PORT_16654
57 # define PORT_16654             11
58 #endif
59 #ifndef PORT_16850
60 # define PORT_16850             12
61 #endif
62 #ifndef PORT_RSA
63 # define PORT_RSA               13
64 #endif
65 #ifndef PORT_NS16550A
66 # define PORT_NS16550A          14
67 #endif
68 #ifndef PORT_XSCALE
69 # define PORT_XSCALE            15
70 #endif
71 #ifndef PORT_RM9000
72 # define PORT_RM9000            16
73 #endif
74 #ifndef PORT_OCTEON
75 # define PORT_OCTEON            17
76 #endif
77 #ifndef PORT_AR7
78 # define PORT_AR7               18
79 #endif
80 #ifndef PORT_U6_16550A
81 # define PORT_U6_16550A         19
82 #endif
83 
84 #ifndef ASYNCB_HUP_NOTIFY
85 # define ASYNCB_HUP_NOTIFY       0
86 #endif
87 #ifndef ASYNCB_FOURPORT
88 # define ASYNCB_FOURPORT         1
89 #endif
90 #ifndef ASYNCB_SAK
91 # define ASYNCB_SAK              2
92 #endif
93 #ifndef ASYNCB_SPLIT_TERMIOS
94 # define ASYNCB_SPLIT_TERMIOS    3
95 #endif
96 #ifndef ASYNCB_SPD_HI
97 # define ASYNCB_SPD_HI           4
98 #endif
99 #ifndef ASYNCB_SPD_VHI
100 # define ASYNCB_SPD_VHI          5
101 #endif
102 #ifndef ASYNCB_SKIP_TEST
103 # define ASYNCB_SKIP_TEST        6
104 #endif
105 #ifndef ASYNCB_AUTO_IRQ
106 # define ASYNCB_AUTO_IRQ         7
107 #endif
108 #ifndef ASYNCB_SESSION_LOCKOUT
109 # define ASYNCB_SESSION_LOCKOUT  8
110 #endif
111 #ifndef ASYNCB_PGRP_LOCKOUT
112 # define ASYNCB_PGRP_LOCKOUT     9
113 #endif
114 #ifndef ASYNCB_CALLOUT_NOHUP
115 # define ASYNCB_CALLOUT_NOHUP   10
116 #endif
117 #ifndef ASYNCB_SPD_SHI
118 # define ASYNCB_SPD_SHI         12
119 #endif
120 #ifndef ASYNCB_LOW_LATENCY
121 # define ASYNCB_LOW_LATENCY     13
122 #endif
123 #ifndef ASYNCB_BUGGY_UART
124 # define ASYNCB_BUGGY_UART      14
125 #endif
126 
127 #ifndef ASYNC_HUP_NOTIFY
128 # define ASYNC_HUP_NOTIFY       (1U << ASYNCB_HUP_NOTIFY)
129 #endif
130 #ifndef ASYNC_FOURPORT
131 # define ASYNC_FOURPORT         (1U << ASYNCB_FOURPORT)
132 #endif
133 #ifndef ASYNC_SAK
134 # define ASYNC_SAK              (1U << ASYNCB_SAK)
135 #endif
136 #ifndef ASYNC_SPLIT_TERMIOS
137 # define ASYNC_SPLIT_TERMIOS    (1U << ASYNCB_SPLIT_TERMIOS)
138 #endif
139 #ifndef ASYNC_SPD_HI
140 # define ASYNC_SPD_HI           (1U << ASYNCB_SPD_HI)
141 #endif
142 #ifndef ASYNC_SPD_VHI
143 # define ASYNC_SPD_VHI          (1U << ASYNCB_SPD_VHI)
144 #endif
145 #ifndef ASYNC_SKIP_TEST
146 # define ASYNC_SKIP_TEST        (1U << ASYNCB_SKIP_TEST)
147 #endif
148 #ifndef ASYNC_AUTO_IRQ
149 # define ASYNC_AUTO_IRQ         (1U << ASYNCB_AUTO_IRQ)
150 #endif
151 #ifndef ASYNC_SESSION_LOCKOUT
152 # define ASYNC_SESSION_LOCKOUT  (1U << ASYNCB_SESSION_LOCKOUT)
153 #endif
154 #ifndef ASYNC_PGRP_LOCKOUT
155 # define ASYNC_PGRP_LOCKOUT     (1U << ASYNCB_PGRP_LOCKOUT)
156 #endif
157 #ifndef ASYNC_CALLOUT_NOHUP
158 # define ASYNC_CALLOUT_NOHUP    (1U << ASYNCB_CALLOUT_NOHUP)
159 #endif
160 #ifndef ASYNC_SPD_SHI
161 # define ASYNC_SPD_SHI          (1U << ASYNCB_SPD_SHI)
162 #endif
163 #ifndef ASYNC_LOW_LATENCY
164 # define ASYNC_LOW_LATENCY      (1U << ASYNCB_LOW_LATENCY)
165 #endif
166 #ifndef ASYNC_BUGGY_UART
167 # define ASYNC_BUGGY_UART       (1U << ASYNCB_BUGGY_UART)
168 #endif
169 
170 #ifndef ASYNC_SPD_CUST
171 # define ASYNC_SPD_CUST         (ASYNC_SPD_HI|ASYNC_SPD_VHI)
172 #endif
173 #ifndef ASYNC_SPD_WARP
174 # define ASYNC_SPD_WARP         (ASYNC_SPD_HI|ASYNC_SPD_SHI)
175 #endif
176 #ifndef ASYNC_SPD_MASK
177 # define ASYNC_SPD_MASK         (ASYNC_SPD_HI|ASYNC_SPD_VHI|ASYNC_SPD_SHI)
178 #endif
179 
180 #ifndef ASYNC_CLOSING_WAIT_INF
181 # define ASYNC_CLOSING_WAIT_INF         0
182 #endif
183 #ifndef ASYNC_CLOSING_WAIT_NONE
184 # define ASYNC_CLOSING_WAIT_NONE        65535
185 #endif
186 
187 #ifndef _LINUX_SERIAL_H
188 struct serial_struct {
189 	int	type;
190 	int	line;
191 	unsigned int	port;
192 	int	irq;
193 	int	flags;
194 	int	xmit_fifo_size;
195 	int	custom_divisor;
196 	int	baud_base;
197 	unsigned short	close_delay;
198 	char	io_type;
199 	char	reserved_char[1];
200 	int	hub6;
201 	unsigned short	closing_wait; /* time to wait before closing */
202 	unsigned short	closing_wait2; /* no longer used... */
203 	unsigned char	*iomem_base;
204 	unsigned short	iomem_reg_shift;
205 	unsigned int	port_high;
206 	unsigned long	iomap_base;	/* cookie passed into ioremap */
207 };
208 #endif
209 
210 //usage:#define setserial_trivial_usage
211 //usage:	"[-abGvz] { DEVICE [PARAMETER [ARG]]... | -g DEVICE... }"
212 //usage:#define setserial_full_usage "\n\n"
213 //usage:	"Print or set serial port parameters"
214 //usage:   "\n"
215 //usage:   "\n""	-a	Print all"
216 //usage:   "\n""	-b	Print summary"
217 //usage:   "\n""	-G	Print as setserial PARAMETERs"
218 //usage:   "\n""	-v	Verbose"
219 //usage:   "\n""	-z	Zero out serial flags before setting"
220 //usage:   "\n""	-g	All args are device names"
221 //usage:   "\n"
222 //usage:   "\n""PARAMETERs: (* = takes ARG, ^ = can be turned off by preceding ^)"
223 //usage:   "\n""	*port, *irq, *divisor, *uart, *baud_base, *close_delay, *closing_wait,"
224 //usage:   "\n""	^fourport, ^auto_irq, ^skip_test, ^sak, ^session_lockout, ^pgrp_lockout,"
225 //usage:   "\n""	^callout_nohup, ^split_termios, ^hup_notify, ^low_latency, autoconfig,"
226 //usage:   "\n""	spd_normal, spd_hi, spd_vhi, spd_shi, spd_warp, spd_cust"
227 //usage:   "\n""ARG for uart:"
228 //usage:   "\n""	unknown, 8250, 16450, 16550, 16550A, Cirrus, 16650, 16650V2, 16750,"
229 //usage:   "\n""	16950, 16954, 16654, 16850, RSA, NS16550A, XSCALE, RM9000, OCTEON, AR7,"
230 //usage:   "\n""	U6_16550A"
231 
232 // option string is "bGavzgq". "q" is accepted but ignored.
233 #define OPT_PRINT_SUMMARY       (1 << 0)
234 #define OPT_PRINT_FEDBACK       (1 << 1)
235 #define OPT_PRINT_ALL           (1 << 2)
236 #define OPT_VERBOSE             (1 << 3)
237 #define OPT_ZERO                (1 << 4)
238 #define OPT_LIST_OF_DEVS        (1 << 5)
239 /*#define OPT_QUIET             (1 << 6)*/
240 
241 #define OPT_MODE_MASK \
242 	(OPT_PRINT_ALL | OPT_PRINT_SUMMARY | OPT_PRINT_FEDBACK)
243 
244 enum print_mode
245 {
246 	PRINT_NORMAL  = 0,
247 	PRINT_SUMMARY = (1 << 0),
248 	PRINT_FEDBACK = (1 << 1),
249 	PRINT_ALL     = (1 << 2),
250 };
251 
252 #define CTL_SET                 (1 << 0)
253 #define CTL_CONFIG              (1 << 1)
254 #define CTL_GET                 (1 << 2)
255 #define CTL_CLOSE               (1 << 3)
256 #define CTL_NODIE               (1 << 4)
257 
258 static const char serial_types[] ALIGN1 =
259 	"unknown\0"		/* 0 */
260 	"8250\0"		/* 1 */
261 	"16450\0"		/* 2 */
262 	"16550\0"		/* 3 */
263 	"16550A\0"		/* 4 */
264 	"Cirrus\0"		/* 5 */
265 	"16650\0"		/* 6 */
266 	"16650V2\0"		/* 7 */
267 	"16750\0"		/* 8 */
268 	"16950\0"		/* 9 UNIMPLEMENTED: also know as "16950/954" */
269 	"16954\0"		/* 10 */
270 	"16654\0"		/* 11 */
271 	"16850\0"		/* 12 */
272 	"RSA\0"			/* 13 */
273 #ifndef SETSERIAL_BASE
274 	"NS16550A\0"		/* 14 */
275 	"XSCALE\0"		/* 15 */
276 	"RM9000\0"		/* 16 */
277 	"OCTEON\0"		/* 17 */
278 	"AR7\0"			/* 18 */
279 	"U6_16550A\0"		/* 19 */
280 #endif
281 ;
282 
283 #ifndef SETSERIAL_BASE
284 # define MAX_SERIAL_TYPE	19
285 #else
286 # define MAX_SERIAL_TYPE	13
287 #endif
288 
289 static const char commands[] ALIGN1 =
290 	"spd_normal\0"
291 	"spd_hi\0"
292 	"spd_vhi\0"
293 	"spd_shi\0"
294 	"spd_warp\0"
295 	"spd_cust\0"
296 
297 	"sak\0"
298 	"fourport\0"
299 	"hup_notify\0"
300 	"skip_test\0"
301 	"auto_irq\0"
302 	"split_termios\0"
303 	"session_lockout\0"
304 	"pgrp_lockout\0"
305 	"callout_nohup\0"
306 	"low_latency\0"
307 
308 	"port\0"
309 	"irq\0"
310 	"divisor\0"
311 	"uart\0"
312 	"baud_base\0"
313 	"close_delay\0"
314 	"closing_wait\0"
315 
316 	"autoconfig\0"
317 ;
318 
319 enum
320 {
321 	CMD_SPD_NORMAL = 0,
322 	CMD_SPD_HI,
323 	CMD_SPD_VHI,
324 	CMD_SPD_SHI,
325 	CMD_SPD_WARP,
326 	CMD_SPD_CUST,
327 
328 	CMD_FLAG_SAK,
329 	CMD_FLAG_FOURPORT,
330 	CMD_FLAG_NUP_NOTIFY,
331 	CMD_FLAG_SKIP_TEST,
332 	CMD_FLAG_AUTO_IRQ,
333 	CMD_FLAG_SPLIT_TERMIOS,
334 	CMD_FLAG_SESSION_LOCKOUT,
335 	CMD_FLAG_PGRP_LOCKOUT,
336 	CMD_FLAG_CALLOUT_NOHUP,
337 	CMD_FLAG_LOW_LATENCY,
338 
339 	CMD_PORT,
340 	CMD_IRQ,
341 	CMD_DIVISOR,
342 	CMD_UART,
343 	CMD_BASE,
344 	CMD_DELAY,
345 	CMD_WAIT,
346 
347 	CMD_AUTOCONFIG,
348 
349 	CMD_FLAG_FIRST = CMD_FLAG_SAK,
350 	CMD_FLAG_LAST  = CMD_FLAG_LOW_LATENCY,
351 };
352 
cmd_noprint(int cmd)353 static bool cmd_noprint(int cmd)
354 {
355 	return (cmd >= CMD_FLAG_SKIP_TEST && cmd <= CMD_FLAG_CALLOUT_NOHUP);
356 }
357 
cmd_is_flag(int cmd)358 static bool cmd_is_flag(int cmd)
359 {
360 	return (cmd >= CMD_FLAG_FIRST && cmd <= CMD_FLAG_LAST);
361 }
362 
cmd_needs_arg(int cmd)363 static bool cmd_needs_arg(int cmd)
364 {
365 	return (cmd >= CMD_PORT && cmd <= CMD_WAIT);
366 }
367 
368 #define ALL_SPD ( \
369 	ASYNC_SPD_HI | ASYNC_SPD_VHI | ASYNC_SPD_SHI | \
370 	ASYNC_SPD_WARP | ASYNC_SPD_CUST \
371 	)
372 
373 #define ALL_FLAGS ( \
374 	ASYNC_SAK | ASYNC_FOURPORT | ASYNC_HUP_NOTIFY | \
375 	ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ | ASYNC_SPLIT_TERMIOS | \
376 	ASYNC_SESSION_LOCKOUT | ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP | \
377 	ASYNC_LOW_LATENCY \
378 	)
379 
380 #if (ALL_SPD | ALL_FLAGS) > 0xffff
381 # error "Unexpected flags size"
382 #endif
383 
384 static const uint16_t setbits[CMD_FLAG_LAST + 1] ALIGN2 = {
385 	0,
386 	ASYNC_SPD_HI,
387 	ASYNC_SPD_VHI,
388 	ASYNC_SPD_SHI,
389 	ASYNC_SPD_WARP,
390 	ASYNC_SPD_CUST,
391 
392 	ASYNC_SAK,
393 	ASYNC_FOURPORT,
394 	ASYNC_HUP_NOTIFY,
395 	ASYNC_SKIP_TEST,
396 	ASYNC_AUTO_IRQ,
397 	ASYNC_SPLIT_TERMIOS,
398 	ASYNC_SESSION_LOCKOUT,
399 	ASYNC_PGRP_LOCKOUT,
400 	ASYNC_CALLOUT_NOHUP,
401 	ASYNC_LOW_LATENCY
402 };
403 
404 #define STR_INFINITE "infinite"
405 #define STR_NONE     "none"
406 
uart_type(int type)407 static const char *uart_type(int type)
408 {
409 	if (type > MAX_SERIAL_TYPE)
410 		return "undefined";
411 
412 	return nth_string(serial_types, type);
413 }
414 
415 /* libbb candidate */
index_in_strings_case_insensitive(const char * strings,const char * key)416 static int index_in_strings_case_insensitive(const char *strings, const char *key)
417 {
418 	int idx = 0;
419 
420 	while (*strings) {
421 		if (strcasecmp(strings, key) == 0) {
422 			return idx;
423 		}
424 		strings += strlen(strings) + 1; /* skip NUL */
425 		idx++;
426 	}
427 	return -1;
428 }
429 
uart_id(const char * name)430 static int uart_id(const char *name)
431 {
432 	return index_in_strings_case_insensitive(serial_types, name);
433 }
434 
get_spd(int flags,enum print_mode mode)435 static const char *get_spd(int flags, enum print_mode mode)
436 {
437 	int idx;
438 
439 	switch (flags & ASYNC_SPD_MASK) {
440 	case ASYNC_SPD_HI:
441 		idx = CMD_SPD_HI;
442 		break;
443 	case ASYNC_SPD_VHI:
444 		idx = CMD_SPD_VHI;
445 		break;
446 	case ASYNC_SPD_SHI:
447 		idx = CMD_SPD_SHI;
448 		break;
449 	case ASYNC_SPD_WARP:
450 		idx = CMD_SPD_WARP;
451 		break;
452 	case ASYNC_SPD_CUST:
453 		idx = CMD_SPD_CUST;
454 		break;
455 	default:
456 		if (mode < PRINT_FEDBACK)
457 			return NULL;
458 		idx = CMD_SPD_NORMAL;
459 	}
460 
461 	return nth_string(commands, idx);
462 }
463 
get_numeric(const char * arg)464 static int get_numeric(const char *arg)
465 {
466 	return bb_strtol(arg, NULL, 0);
467 }
468 
get_wait(const char * arg)469 static int get_wait(const char *arg)
470 {
471 	if (strcasecmp(arg, STR_NONE) == 0)
472 		return ASYNC_CLOSING_WAIT_NONE;
473 
474 	if (strcasecmp(arg, STR_INFINITE) == 0)
475 		return ASYNC_CLOSING_WAIT_INF;
476 
477 	return get_numeric(arg);
478 }
479 
get_uart(const char * arg)480 static int get_uart(const char *arg)
481 {
482 	int uart = uart_id(arg);
483 
484 	if (uart < 0)
485 		bb_error_msg_and_die("illegal UART type: %s", arg);
486 
487 	return uart;
488 }
489 
serial_open(const char * dev,bool quiet)490 static int serial_open(const char *dev, bool quiet)
491 {
492 	int fd;
493 
494 	fd = device_open(dev, O_RDWR | O_NONBLOCK);
495 	if (fd < 0 && !quiet)
496 		bb_simple_perror_msg(dev);
497 
498 	return fd;
499 }
500 
serial_ctl(int fd,int ops,struct serial_struct * serinfo)501 static int serial_ctl(int fd, int ops, struct serial_struct *serinfo)
502 {
503 	int ret = 0;
504 	const char *err;
505 
506 	if (ops & CTL_SET) {
507 		ret = ioctl(fd, TIOCSSERIAL, serinfo);
508 		if (ret < 0) {
509 			err = "can't set serial info";
510 			goto fail;
511 		}
512 	}
513 
514 	if (ops & CTL_CONFIG) {
515 		ret = ioctl(fd, TIOCSERCONFIG);
516 		if (ret < 0) {
517 			err = "can't autoconfigure port";
518 			goto fail;
519 		}
520 	}
521 
522 	if (ops & CTL_GET) {
523 		ret = ioctl(fd, TIOCGSERIAL, serinfo);
524 		if (ret < 0) {
525 			err = "can't get serial info";
526 			goto fail;
527 		}
528 	}
529  nodie:
530 	if (ops & CTL_CLOSE)
531 		close(fd);
532 
533 	return ret;
534  fail:
535 	bb_simple_perror_msg(err);
536 	if (ops & CTL_NODIE)
537 		goto nodie;
538 	exit(EXIT_FAILURE);
539 }
540 
print_flag(const char ** prefix,const char * flag)541 static void print_flag(const char **prefix, const char *flag)
542 {
543 	printf("%s%s", *prefix, flag);
544 	*prefix = " ";
545 }
546 
print_serial_flags(int serial_flags,enum print_mode mode,const char * prefix,const char * postfix)547 static void print_serial_flags(int serial_flags, enum print_mode mode,
548 				const char *prefix, const char *postfix)
549 {
550 	int i;
551 	const char *spd, *pr;
552 
553 	pr = prefix;
554 
555 	spd = get_spd(serial_flags, mode);
556 	if (spd)
557 		print_flag(&pr, spd);
558 
559 	for (i = CMD_FLAG_FIRST; i <= CMD_FLAG_LAST; i++) {
560 		if ((serial_flags & setbits[i])
561 		 && (mode > PRINT_SUMMARY || !cmd_noprint(i))
562 		) {
563 			print_flag(&pr, nth_string(commands, i));
564 		}
565 	}
566 
567 	puts(pr == prefix ? "" : postfix);
568 }
569 
print_closing_wait(unsigned int closing_wait)570 static void print_closing_wait(unsigned int closing_wait)
571 {
572 	switch (closing_wait) {
573 	case ASYNC_CLOSING_WAIT_NONE:
574 		puts(STR_NONE);
575 		break;
576 	case ASYNC_CLOSING_WAIT_INF:
577 		puts(STR_INFINITE);
578 		break;
579 	default:
580 		printf("%u\n", closing_wait);
581 	}
582 }
583 
serial_get(const char * device,enum print_mode mode)584 static void serial_get(const char *device, enum print_mode mode)
585 {
586 	int fd, ret;
587 	const char *uart, *prefix, *postfix;
588 	struct serial_struct serinfo;
589 
590 	fd = serial_open(device, /*quiet:*/ mode == PRINT_SUMMARY);
591 	if (fd < 0)
592 		return;
593 
594 	ret = serial_ctl(fd, CTL_GET | CTL_CLOSE | CTL_NODIE, &serinfo);
595 	if (ret < 0)
596 		return;
597 
598 	uart = uart_type(serinfo.type);
599 	prefix = ", Flags: ";
600 	postfix = "";
601 
602 	switch (mode) {
603 	case PRINT_NORMAL:
604 		printf("%s, UART: %s, Port: 0x%.4x, IRQ: %d",
605 			device, uart, serinfo.port, serinfo.irq);
606 		break;
607 	case PRINT_SUMMARY:
608 		if (!serinfo.type)
609 			return;
610 		printf("%s at 0x%.4x (irq = %d) is a %s",
611 			device, serinfo.port, serinfo.irq, uart);
612 		prefix = " (";
613 		postfix = ")";
614 		break;
615 	case PRINT_FEDBACK:
616 		printf("%s uart %s port 0x%.4x irq %d baud_base %d", device,
617 			uart, serinfo.port, serinfo.irq, serinfo.baud_base);
618 		prefix = " ";
619 		break;
620 	case PRINT_ALL:
621 		printf("%s, Line %d, UART: %s, Port: 0x%.4x, IRQ: %d\n",
622 			device, serinfo.line, uart, serinfo.port, serinfo.irq);
623 		printf("\tBaud_base: %d, close_delay: %u, divisor: %d\n",
624 			serinfo.baud_base, serinfo.close_delay,
625 			serinfo.custom_divisor);
626 		printf("\tclosing_wait: ");
627 		print_closing_wait(serinfo.closing_wait);
628 		prefix = "\tFlags: ";
629 		postfix = "\n";
630 		break;
631 	default:
632 		assert(0);
633 	}
634 
635 	print_serial_flags(serinfo.flags, mode, prefix, postfix);
636 }
637 
find_cmd(const char * cmd)638 static int find_cmd(const char *cmd)
639 {
640 	int idx;
641 
642 	idx = index_in_strings_case_insensitive(commands, cmd);
643 	if (idx < 0)
644 		bb_error_msg_and_die("invalid flag: %s", cmd);
645 
646 	return idx;
647 }
648 
serial_set(char ** arg,int opts)649 static void serial_set(char **arg, int opts)
650 {
651 	struct serial_struct serinfo;
652 	int fd;
653 
654 	fd = serial_open(*arg, /*quiet:*/ false);
655 	if (fd < 0)
656 		exit(201);
657 
658 	serial_ctl(fd, CTL_GET, &serinfo);
659 
660 	if (opts & OPT_ZERO)
661 		serinfo.flags = 0;
662 
663 	while (*++arg) {
664 		const char *word;
665 		int invert;
666 		int cmd;
667 
668 		word = *arg;
669 		invert = (word[0] == '^');
670 		word += invert;
671 
672 		cmd = find_cmd(word);
673 
674 		if (cmd_needs_arg(cmd))
675 			if (*++arg == NULL)
676 				bb_error_msg_and_die(bb_msg_requires_arg, word);
677 
678 		if (invert && !cmd_is_flag(cmd))
679 			bb_error_msg_and_die("can't invert %s", word);
680 
681 		switch (cmd) {
682 		case CMD_SPD_NORMAL:
683 		case CMD_SPD_HI:
684 		case CMD_SPD_VHI:
685 		case CMD_SPD_SHI:
686 		case CMD_SPD_WARP:
687 		case CMD_SPD_CUST:
688 			serinfo.flags &= ~ASYNC_SPD_MASK;
689 			/* fallthrough */
690 		case CMD_FLAG_SAK:
691 		case CMD_FLAG_FOURPORT:
692 		case CMD_FLAG_NUP_NOTIFY:
693 		case CMD_FLAG_SKIP_TEST:
694 		case CMD_FLAG_AUTO_IRQ:
695 		case CMD_FLAG_SPLIT_TERMIOS:
696 		case CMD_FLAG_SESSION_LOCKOUT:
697 		case CMD_FLAG_PGRP_LOCKOUT:
698 		case CMD_FLAG_CALLOUT_NOHUP:
699 		case CMD_FLAG_LOW_LATENCY:
700 			if (invert)
701 				serinfo.flags &= ~setbits[cmd];
702 			else
703 				serinfo.flags |= setbits[cmd];
704 			break;
705 		case CMD_PORT:
706 			serinfo.port = get_numeric(*arg);
707 			break;
708 		case CMD_IRQ:
709 			serinfo.irq = get_numeric(*arg);
710 			break;
711 		case CMD_DIVISOR:
712 			serinfo.custom_divisor = get_numeric(*arg);
713 			break;
714 		case CMD_UART:
715 			serinfo.type = get_uart(*arg);
716 			break;
717 		case CMD_BASE:
718 			serinfo.baud_base = get_numeric(*arg);
719 			break;
720 		case CMD_DELAY:
721 			serinfo.close_delay = get_numeric(*arg);
722 			break;
723 		case CMD_WAIT:
724 			serinfo.closing_wait = get_wait(*arg);
725 			break;
726 		case CMD_AUTOCONFIG:
727 			serial_ctl(fd, CTL_SET | CTL_CONFIG | CTL_GET, &serinfo);
728 			break;
729 		default:
730 			assert(0);
731 		}
732 	}
733 
734 	serial_ctl(fd, CTL_SET | CTL_CLOSE, &serinfo);
735 }
736 
737 int setserial_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
setserial_main(int argc UNUSED_PARAM,char ** argv)738 int setserial_main(int argc UNUSED_PARAM, char **argv)
739 {
740 	int opts;
741 
742 	opts = getopt32(argv, "^" "bGavzgq" "\0" "-1:b-aG:G-ab:a-bG");
743 	argv += optind;
744 
745 	if (!argv[1]) /* one arg only? (nothing to change?) */
746 		opts |= OPT_LIST_OF_DEVS; /* force display */
747 
748 	if (!(opts & OPT_LIST_OF_DEVS)) {
749 		serial_set(argv, opts);
750 		argv[1] = NULL;
751 	}
752 
753 	/* -v effect: "after setting params, do not be silent, show them" */
754 	if (opts & (OPT_VERBOSE | OPT_LIST_OF_DEVS)) {
755 		do {
756 			serial_get(*argv, opts & OPT_MODE_MASK);
757 		} while (*++argv);
758 	}
759 
760 	return EXIT_SUCCESS;
761 }
762