1 /* vi: set sw=4 ts=4: */
3 * setserial implementation for busybox
6 * Copyright (C) 2011 Marek Bečka <yuen@klacno.sk>
8 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10 //config:config SETSERIAL
11 //config: bool "setserial (6.9 kb)"
13 //config: select PLATFORM_LINUX
15 //config: Retrieve or set Linux serial port.
17 //applet:IF_SETSERIAL(APPLET_NOEXEC(setserial, setserial, BB_DIR_BIN, BB_SUID_DROP, setserial))
19 //kbuild:lib-$(CONFIG_SETSERIAL) += setserial.o
25 # define PORT_UNKNOWN 0
37 # define PORT_16550A 4
40 # define PORT_CIRRUS 5
46 # define PORT_16650V2 7
52 # define PORT_STARTECH 9
55 # define PORT_16C950 10
58 # define PORT_16654 11
61 # define PORT_16850 12
67 # define PORT_NS16550A 14
70 # define PORT_XSCALE 15
73 # define PORT_RM9000 16
76 # define PORT_OCTEON 17
81 #ifndef PORT_U6_16550A
82 # define PORT_U6_16550A 19
85 #ifndef ASYNCB_HUP_NOTIFY
86 # define ASYNCB_HUP_NOTIFY 0
88 #ifndef ASYNCB_FOURPORT
89 # define ASYNCB_FOURPORT 1
94 #ifndef ASYNCB_SPLIT_TERMIOS
95 # define ASYNCB_SPLIT_TERMIOS 3
98 # define ASYNCB_SPD_HI 4
100 #ifndef ASYNCB_SPD_VHI
101 # define ASYNCB_SPD_VHI 5
103 #ifndef ASYNCB_SKIP_TEST
104 # define ASYNCB_SKIP_TEST 6
106 #ifndef ASYNCB_AUTO_IRQ
107 # define ASYNCB_AUTO_IRQ 7
109 #ifndef ASYNCB_SESSION_LOCKOUT
110 # define ASYNCB_SESSION_LOCKOUT 8
112 #ifndef ASYNCB_PGRP_LOCKOUT
113 # define ASYNCB_PGRP_LOCKOUT 9
115 #ifndef ASYNCB_CALLOUT_NOHUP
116 # define ASYNCB_CALLOUT_NOHUP 10
118 #ifndef ASYNCB_SPD_SHI
119 # define ASYNCB_SPD_SHI 12
121 #ifndef ASYNCB_LOW_LATENCY
122 # define ASYNCB_LOW_LATENCY 13
124 #ifndef ASYNCB_BUGGY_UART
125 # define ASYNCB_BUGGY_UART 14
128 #ifndef ASYNC_HUP_NOTIFY
129 # define ASYNC_HUP_NOTIFY (1U << ASYNCB_HUP_NOTIFY)
131 #ifndef ASYNC_FOURPORT
132 # define ASYNC_FOURPORT (1U << ASYNCB_FOURPORT)
135 # define ASYNC_SAK (1U << ASYNCB_SAK)
137 #ifndef ASYNC_SPLIT_TERMIOS
138 # define ASYNC_SPLIT_TERMIOS (1U << ASYNCB_SPLIT_TERMIOS)
141 # define ASYNC_SPD_HI (1U << ASYNCB_SPD_HI)
143 #ifndef ASYNC_SPD_VHI
144 # define ASYNC_SPD_VHI (1U << ASYNCB_SPD_VHI)
146 #ifndef ASYNC_SKIP_TEST
147 # define ASYNC_SKIP_TEST (1U << ASYNCB_SKIP_TEST)
149 #ifndef ASYNC_AUTO_IRQ
150 # define ASYNC_AUTO_IRQ (1U << ASYNCB_AUTO_IRQ)
152 #ifndef ASYNC_SESSION_LOCKOUT
153 # define ASYNC_SESSION_LOCKOUT (1U << ASYNCB_SESSION_LOCKOUT)
155 #ifndef ASYNC_PGRP_LOCKOUT
156 # define ASYNC_PGRP_LOCKOUT (1U << ASYNCB_PGRP_LOCKOUT)
158 #ifndef ASYNC_CALLOUT_NOHUP
159 # define ASYNC_CALLOUT_NOHUP (1U << ASYNCB_CALLOUT_NOHUP)
161 #ifndef ASYNC_SPD_SHI
162 # define ASYNC_SPD_SHI (1U << ASYNCB_SPD_SHI)
164 #ifndef ASYNC_LOW_LATENCY
165 # define ASYNC_LOW_LATENCY (1U << ASYNCB_LOW_LATENCY)
167 #ifndef ASYNC_BUGGY_UART
168 # define ASYNC_BUGGY_UART (1U << ASYNCB_BUGGY_UART)
171 #ifndef ASYNC_SPD_CUST
172 # define ASYNC_SPD_CUST (ASYNC_SPD_HI|ASYNC_SPD_VHI)
174 #ifndef ASYNC_SPD_WARP
175 # define ASYNC_SPD_WARP (ASYNC_SPD_HI|ASYNC_SPD_SHI)
177 #ifndef ASYNC_SPD_MASK
178 # define ASYNC_SPD_MASK (ASYNC_SPD_HI|ASYNC_SPD_VHI|ASYNC_SPD_SHI)
181 #ifndef ASYNC_CLOSING_WAIT_INF
182 # define ASYNC_CLOSING_WAIT_INF 0
184 #ifndef ASYNC_CLOSING_WAIT_NONE
185 # define ASYNC_CLOSING_WAIT_NONE 65535
188 #ifndef _LINUX_SERIAL_H
189 struct serial_struct {
198 unsigned short close_delay;
200 char reserved_char[1];
202 unsigned short closing_wait; /* time to wait before closing */
203 unsigned short closing_wait2; /* no longer used... */
204 unsigned char *iomem_base;
205 unsigned short iomem_reg_shift;
206 unsigned int port_high;
207 unsigned long iomap_base; /* cookie passed into ioremap */
211 //usage:#define setserial_trivial_usage
212 //usage: "[-abGvz] { DEVICE [PARAMETER [ARG]]... | -g DEVICE... }"
213 //usage:#define setserial_full_usage "\n\n"
214 //usage: "Print or set serial port parameters"
216 //usage: "\n"" -a Print all"
217 //usage: "\n"" -b Print summary"
218 //usage: "\n"" -G Print as setserial PARAMETERs"
219 //usage: "\n"" -v Verbose"
220 //usage: "\n"" -z Zero out serial flags before setting"
221 //usage: "\n"" -g All args are device names"
223 //usage: "\n""PARAMETERs: (* = takes ARG, ^ = can be turned off by preceding ^)"
224 //usage: "\n"" *port, *irq, *divisor, *uart, *baud_base, *close_delay, *closing_wait,"
225 //usage: "\n"" ^fourport, ^auto_irq, ^skip_test, ^sak, ^session_lockout, ^pgrp_lockout,"
226 //usage: "\n"" ^callout_nohup, ^split_termios, ^hup_notify, ^low_latency, autoconfig,"
227 //usage: "\n"" spd_normal, spd_hi, spd_vhi, spd_shi, spd_warp, spd_cust"
228 //usage: "\n""ARG for uart:"
229 //usage: "\n"" unknown, 8250, 16450, 16550, 16550A, Cirrus, 16650, 16650V2, 16750,"
230 //usage: "\n"" 16950, 16954, 16654, 16850, RSA, NS16550A, XSCALE, RM9000, OCTEON, AR7,"
231 //usage: "\n"" U6_16550A"
233 // option string is "bGavzgq". "q" is accepted but ignored.
234 #define OPT_PRINT_SUMMARY (1 << 0)
235 #define OPT_PRINT_FEDBACK (1 << 1)
236 #define OPT_PRINT_ALL (1 << 2)
237 #define OPT_VERBOSE (1 << 3)
238 #define OPT_ZERO (1 << 4)
239 #define OPT_LIST_OF_DEVS (1 << 5)
240 /*#define OPT_QUIET (1 << 6)*/
242 #define OPT_MODE_MASK \
243 (OPT_PRINT_ALL | OPT_PRINT_SUMMARY | OPT_PRINT_FEDBACK)
248 PRINT_SUMMARY = (1 << 0),
249 PRINT_FEDBACK = (1 << 1),
250 PRINT_ALL = (1 << 2),
253 #define CTL_SET (1 << 0)
254 #define CTL_CONFIG (1 << 1)
255 #define CTL_GET (1 << 2)
256 #define CTL_CLOSE (1 << 3)
257 #define CTL_NODIE (1 << 4)
259 static const char serial_types[] ALIGN1 =
269 "16950\0" /* 9 UNIMPLEMENTED: also know as "16950/954" */
274 #ifndef SETSERIAL_BASE
275 "NS16550A\0" /* 14 */
280 "U6_16550A\0" /* 19 */
284 #ifndef SETSERIAL_BASE
285 # define MAX_SERIAL_TYPE 19
287 # define MAX_SERIAL_TYPE 13
290 static const char commands[] ALIGN1 =
334 CMD_FLAG_SPLIT_TERMIOS,
335 CMD_FLAG_SESSION_LOCKOUT,
336 CMD_FLAG_PGRP_LOCKOUT,
337 CMD_FLAG_CALLOUT_NOHUP,
338 CMD_FLAG_LOW_LATENCY,
350 CMD_FLAG_FIRST = CMD_FLAG_SAK,
351 CMD_FLAG_LAST = CMD_FLAG_LOW_LATENCY,
354 static bool cmd_noprint(int cmd)
356 return (cmd >= CMD_FLAG_SKIP_TEST && cmd <= CMD_FLAG_CALLOUT_NOHUP);
359 static bool cmd_is_flag(int cmd)
361 return (cmd >= CMD_FLAG_FIRST && cmd <= CMD_FLAG_LAST);
364 static bool cmd_needs_arg(int cmd)
366 return (cmd >= CMD_PORT && cmd <= CMD_WAIT);
370 ASYNC_SPD_HI | ASYNC_SPD_VHI | ASYNC_SPD_SHI | \
371 ASYNC_SPD_WARP | ASYNC_SPD_CUST \
374 #define ALL_FLAGS ( \
375 ASYNC_SAK | ASYNC_FOURPORT | ASYNC_HUP_NOTIFY | \
376 ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ | ASYNC_SPLIT_TERMIOS | \
377 ASYNC_SESSION_LOCKOUT | ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP | \
381 #if (ALL_SPD | ALL_FLAGS) > 0xffff
382 # error "Unexpected flags size"
385 static const uint16_t setbits[CMD_FLAG_LAST + 1] =
400 ASYNC_SESSION_LOCKOUT,
406 #define STR_INFINITE "infinite"
407 #define STR_NONE "none"
409 static const char *uart_type(int type)
411 if (type > MAX_SERIAL_TYPE)
414 return nth_string(serial_types, type);
417 /* libbb candidate */
418 static int index_in_strings_case_insensitive(const char *strings, const char *key)
423 if (strcasecmp(strings, key) == 0) {
426 strings += strlen(strings) + 1; /* skip NUL */
432 static int uart_id(const char *name)
434 return index_in_strings_case_insensitive(serial_types, name);
437 static const char *get_spd(int flags, enum print_mode mode)
441 switch (flags & ASYNC_SPD_MASK) {
458 if (mode < PRINT_FEDBACK)
460 idx = CMD_SPD_NORMAL;
463 return nth_string(commands, idx);
466 static int get_numeric(const char *arg)
468 return bb_strtol(arg, NULL, 0);
471 static int get_wait(const char *arg)
473 if (strcasecmp(arg, STR_NONE) == 0)
474 return ASYNC_CLOSING_WAIT_NONE;
476 if (strcasecmp(arg, STR_INFINITE) == 0)
477 return ASYNC_CLOSING_WAIT_INF;
479 return get_numeric(arg);
482 static int get_uart(const char *arg)
484 int uart = uart_id(arg);
487 bb_error_msg_and_die("illegal UART type: %s", arg);
492 static int serial_open(const char *dev, bool quiet)
496 fd = device_open(dev, O_RDWR | O_NONBLOCK);
497 if (fd < 0 && !quiet)
498 bb_simple_perror_msg(dev);
503 static int serial_ctl(int fd, int ops, struct serial_struct *serinfo)
509 ret = ioctl(fd, TIOCSSERIAL, serinfo);
511 err = "can't set serial info";
516 if (ops & CTL_CONFIG) {
517 ret = ioctl(fd, TIOCSERCONFIG);
519 err = "can't autoconfigure port";
525 ret = ioctl(fd, TIOCGSERIAL, serinfo);
527 err = "can't get serial info";
537 bb_simple_perror_msg(err);
543 static void print_flag(const char **prefix, const char *flag)
545 printf("%s%s", *prefix, flag);
549 static void print_serial_flags(int serial_flags, enum print_mode mode,
550 const char *prefix, const char *postfix)
553 const char *spd, *pr;
557 spd = get_spd(serial_flags, mode);
559 print_flag(&pr, spd);
561 for (i = CMD_FLAG_FIRST; i <= CMD_FLAG_LAST; i++) {
562 if ((serial_flags & setbits[i])
563 && (mode > PRINT_SUMMARY || !cmd_noprint(i))
565 print_flag(&pr, nth_string(commands, i));
569 puts(pr == prefix ? "" : postfix);
572 static void print_closing_wait(unsigned int closing_wait)
574 switch (closing_wait) {
575 case ASYNC_CLOSING_WAIT_NONE:
578 case ASYNC_CLOSING_WAIT_INF:
582 printf("%u\n", closing_wait);
586 static void serial_get(const char *device, enum print_mode mode)
589 const char *uart, *prefix, *postfix;
590 struct serial_struct serinfo;
592 fd = serial_open(device, /*quiet:*/ mode == PRINT_SUMMARY);
596 ret = serial_ctl(fd, CTL_GET | CTL_CLOSE | CTL_NODIE, &serinfo);
600 uart = uart_type(serinfo.type);
601 prefix = ", Flags: ";
606 printf("%s, UART: %s, Port: 0x%.4x, IRQ: %d",
607 device, uart, serinfo.port, serinfo.irq);
612 printf("%s at 0x%.4x (irq = %d) is a %s",
613 device, serinfo.port, serinfo.irq, uart);
618 printf("%s uart %s port 0x%.4x irq %d baud_base %d", device,
619 uart, serinfo.port, serinfo.irq, serinfo.baud_base);
623 printf("%s, Line %d, UART: %s, Port: 0x%.4x, IRQ: %d\n",
624 device, serinfo.line, uart, serinfo.port, serinfo.irq);
625 printf("\tBaud_base: %d, close_delay: %u, divisor: %d\n",
626 serinfo.baud_base, serinfo.close_delay,
627 serinfo.custom_divisor);
628 printf("\tclosing_wait: ");
629 print_closing_wait(serinfo.closing_wait);
630 prefix = "\tFlags: ";
637 print_serial_flags(serinfo.flags, mode, prefix, postfix);
640 static int find_cmd(const char *cmd)
644 idx = index_in_strings_case_insensitive(commands, cmd);
646 bb_error_msg_and_die("invalid flag: %s", cmd);
651 static void serial_set(char **arg, int opts)
653 struct serial_struct serinfo;
656 fd = serial_open(*arg, /*quiet:*/ false);
660 serial_ctl(fd, CTL_GET, &serinfo);
671 invert = (word[0] == '^');
674 cmd = find_cmd(word);
676 if (cmd_needs_arg(cmd))
678 bb_error_msg_and_die(bb_msg_requires_arg, word);
680 if (invert && !cmd_is_flag(cmd))
681 bb_error_msg_and_die("can't invert %s", word);
690 serinfo.flags &= ~ASYNC_SPD_MASK;
693 case CMD_FLAG_FOURPORT:
694 case CMD_FLAG_NUP_NOTIFY:
695 case CMD_FLAG_SKIP_TEST:
696 case CMD_FLAG_AUTO_IRQ:
697 case CMD_FLAG_SPLIT_TERMIOS:
698 case CMD_FLAG_SESSION_LOCKOUT:
699 case CMD_FLAG_PGRP_LOCKOUT:
700 case CMD_FLAG_CALLOUT_NOHUP:
701 case CMD_FLAG_LOW_LATENCY:
703 serinfo.flags &= ~setbits[cmd];
705 serinfo.flags |= setbits[cmd];
708 serinfo.port = get_numeric(*arg);
711 serinfo.irq = get_numeric(*arg);
714 serinfo.custom_divisor = get_numeric(*arg);
717 serinfo.type = get_uart(*arg);
720 serinfo.baud_base = get_numeric(*arg);
723 serinfo.close_delay = get_numeric(*arg);
726 serinfo.closing_wait = get_wait(*arg);
729 serial_ctl(fd, CTL_SET | CTL_CONFIG | CTL_GET, &serinfo);
736 serial_ctl(fd, CTL_SET | CTL_CLOSE, &serinfo);
739 int setserial_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
740 int setserial_main(int argc UNUSED_PARAM, char **argv)
744 opts = getopt32(argv, "^" "bGavzgq" "\0" "-1:b-aG:G-ab:a-bG");
747 if (!argv[1]) /* one arg only? (nothing to change?) */
748 opts |= OPT_LIST_OF_DEVS; /* force display */
750 if (!(opts & OPT_LIST_OF_DEVS)) {
751 serial_set(argv, opts);
755 /* -v effect: "after setting params, do not be silent, show them" */
756 if (opts & (OPT_VERBOSE | OPT_LIST_OF_DEVS)) {
758 serial_get(*argv, opts & OPT_MODE_MASK);