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.
11 //config:config SETSERIAL
12 //config: bool "setserial"
14 //config: select PLATFORM_LINUX
16 //config: Retrieve or set Linux serial port.
18 //applet:IF_SETSERIAL(APPLET(setserial, BB_DIR_BIN, BB_SUID_DROP))
20 //kbuild:lib-$(CONFIG_SETSERIAL) += setserial.o
26 # define PORT_UNKNOWN 0
38 # define PORT_16550A 4
41 # define PORT_CIRRUS 5
47 # define PORT_16650V2 7
53 # define PORT_STARTECH 9
56 # define PORT_16C950 10
59 # define PORT_16654 11
62 # define PORT_16850 12
68 # define PORT_NS16550A 14
71 # define PORT_XSCALE 15
74 # define PORT_RM9000 16
77 # define PORT_OCTEON 17
82 #ifndef PORT_U6_16550A
83 # define PORT_U6_16550A 19
86 #ifndef ASYNCB_HUP_NOTIFY
87 # define ASYNCB_HUP_NOTIFY 0
89 #ifndef ASYNCB_FOURPORT
90 # define ASYNCB_FOURPORT 1
95 #ifndef ASYNCB_SPLIT_TERMIOS
96 # define ASYNCB_SPLIT_TERMIOS 3
99 # define ASYNCB_SPD_HI 4
101 #ifndef ASYNCB_SPD_VHI
102 # define ASYNCB_SPD_VHI 5
104 #ifndef ASYNCB_SKIP_TEST
105 # define ASYNCB_SKIP_TEST 6
107 #ifndef ASYNCB_AUTO_IRQ
108 # define ASYNCB_AUTO_IRQ 7
110 #ifndef ASYNCB_SESSION_LOCKOUT
111 # define ASYNCB_SESSION_LOCKOUT 8
113 #ifndef ASYNCB_PGRP_LOCKOUT
114 # define ASYNCB_PGRP_LOCKOUT 9
116 #ifndef ASYNCB_CALLOUT_NOHUP
117 # define ASYNCB_CALLOUT_NOHUP 10
119 #ifndef ASYNCB_SPD_SHI
120 # define ASYNCB_SPD_SHI 12
122 #ifndef ASYNCB_LOW_LATENCY
123 # define ASYNCB_LOW_LATENCY 13
125 #ifndef ASYNCB_BUGGY_UART
126 # define ASYNCB_BUGGY_UART 14
129 #ifndef ASYNC_HUP_NOTIFY
130 # define ASYNC_HUP_NOTIFY (1U << ASYNCB_HUP_NOTIFY)
132 #ifndef ASYNC_FOURPORT
133 # define ASYNC_FOURPORT (1U << ASYNCB_FOURPORT)
136 # define ASYNC_SAK (1U << ASYNCB_SAK)
138 #ifndef ASYNC_SPLIT_TERMIOS
139 # define ASYNC_SPLIT_TERMIOS (1U << ASYNCB_SPLIT_TERMIOS)
142 # define ASYNC_SPD_HI (1U << ASYNCB_SPD_HI)
144 #ifndef ASYNC_SPD_VHI
145 # define ASYNC_SPD_VHI (1U << ASYNCB_SPD_VHI)
147 #ifndef ASYNC_SKIP_TEST
148 # define ASYNC_SKIP_TEST (1U << ASYNCB_SKIP_TEST)
150 #ifndef ASYNC_AUTO_IRQ
151 # define ASYNC_AUTO_IRQ (1U << ASYNCB_AUTO_IRQ)
153 #ifndef ASYNC_SESSION_LOCKOUT
154 # define ASYNC_SESSION_LOCKOUT (1U << ASYNCB_SESSION_LOCKOUT)
156 #ifndef ASYNC_PGRP_LOCKOUT
157 # define ASYNC_PGRP_LOCKOUT (1U << ASYNCB_PGRP_LOCKOUT)
159 #ifndef ASYNC_CALLOUT_NOHUP
160 # define ASYNC_CALLOUT_NOHUP (1U << ASYNCB_CALLOUT_NOHUP)
162 #ifndef ASYNC_SPD_SHI
163 # define ASYNC_SPD_SHI (1U << ASYNCB_SPD_SHI)
165 #ifndef ASYNC_LOW_LATENCY
166 # define ASYNC_LOW_LATENCY (1U << ASYNCB_LOW_LATENCY)
168 #ifndef ASYNC_BUGGY_UART
169 # define ASYNC_BUGGY_UART (1U << ASYNCB_BUGGY_UART)
172 #ifndef ASYNC_SPD_CUST
173 # define ASYNC_SPD_CUST (ASYNC_SPD_HI|ASYNC_SPD_VHI)
175 #ifndef ASYNC_SPD_WARP
176 # define ASYNC_SPD_WARP (ASYNC_SPD_HI|ASYNC_SPD_SHI)
178 #ifndef ASYNC_SPD_MASK
179 # define ASYNC_SPD_MASK (ASYNC_SPD_HI|ASYNC_SPD_VHI|ASYNC_SPD_SHI)
182 #ifndef ASYNC_CLOSING_WAIT_INF
183 # define ASYNC_CLOSING_WAIT_INF 0
185 #ifndef ASYNC_CLOSING_WAIT_NONE
186 # define ASYNC_CLOSING_WAIT_NONE 65535
189 #ifndef _LINUX_SERIAL_H
190 struct serial_struct
{
199 unsigned short close_delay
;
201 char reserved_char
[1];
203 unsigned short closing_wait
; /* time to wait before closing */
204 unsigned short closing_wait2
; /* no longer used... */
205 unsigned char *iomem_base
;
206 unsigned short iomem_reg_shift
;
207 unsigned int port_high
;
208 unsigned long iomap_base
; /* cookie passed into ioremap */
212 //usage:#define setserial_trivial_usage
213 //usage: "[-gabGvzV] DEVICE [PARAMETER [ARG]]..."
214 //usage:#define setserial_full_usage "\n\n"
215 //usage: "Request or set Linux serial port information\n"
217 //usage: " -g Interpret parameters as list of devices for reporting\n"
218 //usage: " -a Print all available information\n"
219 //usage: " -b Print summary information\n"
220 //usage: " -G Print in form which can be fed back\n"
221 //usage: " to setserial as command line parameters\n"
222 //usage: " -z Zero out serial flags before setting\n"
223 //usage: " -v Verbose\n"
225 //usage: "Parameters: (* = takes an argument, ^ = can be turned off by preceding ^)\n"
226 //usage: " *port, *irq, *divisor, *uart, *baud_base, *close_delay, *closing_wait,\n"
227 //usage: " ^fourport, ^auto_irq, ^skip_test, ^sak, ^session_lockout, ^pgrp_lockout,\n"
228 //usage: " ^callout_nohup, ^split_termios, ^hup_notify, ^low_latency, autoconfig,\n"
229 //usage: " spd_normal, spd_hi, spd_vhi, spd_shi, spd_warp, spd_cust\n"
231 //usage: "UART types:\n"
232 //usage: " unknown, 8250, 16450, 16550, 16550A, Cirrus, 16650, 16650V2, 16750,\n"
233 //usage: " 16950, 16954, 16654, 16850, RSA, NS16550A, XSCALE, RM9000, OCTEON, AR7,\n"
234 //usage: " U6_16550A"
236 #define OPT_PRINT_SUMMARY (1 << 0)
237 #define OPT_PRINT_FEDBACK (1 << 1)
238 #define OPT_PRINT_ALL (1 << 2)
239 #define OPT_VERBOSE (1 << 3)
240 #define OPT_ZERO (1 << 4)
241 #define OPT_GET (1 << 5)
243 #define OPT_MODE_MASK \
244 (OPT_PRINT_ALL | OPT_PRINT_SUMMARY | OPT_PRINT_FEDBACK)
249 PRINT_SUMMARY
= (1 << 0),
250 PRINT_FEDBACK
= (1 << 1),
251 PRINT_ALL
= (1 << 2),
254 #define CTL_SET (1 << 0)
255 #define CTL_CONFIG (1 << 1)
256 #define CTL_GET (1 << 2)
257 #define CTL_CLOSE (1 << 3)
258 #define CTL_NODIE (1 << 4)
260 static const char serial_types
[] =
270 "16950\0" /* 9 UNIMPLEMENTED: also know as "16950/954" */
275 #ifndef SETSERIAL_BASE
276 "NS16550A\0" /* 14 */
281 "U6_16550A\0" /* 19 */
285 #ifndef SETSERIAL_BASE
286 # define MAX_SERIAL_TYPE 19
288 # define MAX_SERIAL_TYPE 13
291 static const char commands
[] =
335 CMD_FLAG_SPLIT_TERMIOS
,
336 CMD_FLAG_SESSION_LOCKOUT
,
337 CMD_FLAG_PGRP_LOCKOUT
,
338 CMD_FLAG_CALLOUT_NOHUP
,
339 CMD_FLAG_LOW_LATENCY
,
351 CMD_FLAG_FIRST
= CMD_FLAG_SAK
,
352 CMD_FLAG_LAST
= CMD_FLAG_LOW_LATENCY
,
355 static bool cmd_noprint(int cmd
)
357 return (cmd
>= CMD_FLAG_SKIP_TEST
&& cmd
<= CMD_FLAG_CALLOUT_NOHUP
);
360 static bool cmd_is_flag(int cmd
)
362 return (cmd
>= CMD_FLAG_FIRST
&& cmd
<= CMD_FLAG_LAST
);
365 static bool cmd_need_arg(int cmd
)
367 return (cmd
>= CMD_PORT
&& cmd
<= CMD_WAIT
);
371 ASYNC_SPD_HI | ASYNC_SPD_VHI | ASYNC_SPD_SHI | \
372 ASYNC_SPD_WARP | ASYNC_SPD_CUST \
375 #define ALL_FLAGS ( \
376 ASYNC_SAK | ASYNC_FOURPORT | ASYNC_HUP_NOTIFY | \
377 ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ | ASYNC_SPLIT_TERMIOS | \
378 ASYNC_SESSION_LOCKOUT | ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP | \
382 #if (ALL_SPD | ALL_FLAGS) > 0xffff
383 # error "Unexpected flags size"
386 static const uint16_t setbits
[CMD_FLAG_LAST
+ 1] =
401 ASYNC_SESSION_LOCKOUT
,
407 static const char STR_INFINITE
[] = "infinite";
408 static const char STR_NONE
[] = "none";
410 static const char *uart_type(int type
)
412 if (type
> MAX_SERIAL_TYPE
)
415 return nth_string(serial_types
, type
);
418 /* libbb candidate */
419 static int index_in_strings_case_insensitive(const char *strings
, const char *key
)
424 if (strcasecmp(strings
, key
) == 0) {
427 strings
+= strlen(strings
) + 1; /* skip NUL */
433 static int uart_id(const char *name
)
435 return index_in_strings_case_insensitive(serial_types
, name
);
438 static const char *get_spd(int flags
, enum print_mode mode
)
442 switch (flags
& ASYNC_SPD_MASK
) {
459 if (mode
< PRINT_FEDBACK
)
461 idx
= CMD_SPD_NORMAL
;
464 return nth_string(commands
, idx
);
467 static int get_numeric(const char *arg
)
469 return bb_strtol(arg
, NULL
, 0);
472 static int get_wait(const char *arg
)
474 if (strcasecmp(arg
, STR_NONE
) == 0)
475 return ASYNC_CLOSING_WAIT_NONE
;
477 if (strcasecmp(arg
, STR_INFINITE
) == 0)
478 return ASYNC_CLOSING_WAIT_INF
;
480 return get_numeric(arg
);
483 static int get_uart(const char *arg
)
485 int uart
= uart_id(arg
);
488 bb_error_msg_and_die("illegal UART type: %s", arg
);
493 static int serial_open(const char *dev
, bool quiet
)
497 fd
= device_open(dev
, O_RDWR
| O_NONBLOCK
);
498 if (fd
< 0 && !quiet
)
499 bb_simple_perror_msg(dev
);
504 static int serial_ctl(int fd
, int ops
, struct serial_struct
*serinfo
)
510 ret
= ioctl(fd
, TIOCSSERIAL
, serinfo
);
512 err
= "can't set serial info";
517 if (ops
& CTL_CONFIG
) {
518 ret
= ioctl(fd
, TIOCSERCONFIG
);
520 err
= "can't autoconfigure port";
526 ret
= ioctl(fd
, TIOCGSERIAL
, serinfo
);
528 err
= "can't get serial info";
538 bb_simple_perror_msg(err
);
544 static void print_flag(const char **prefix
, const char *flag
)
546 printf("%s%s", *prefix
, flag
);
550 static void print_serial_flags(int serial_flags
, enum print_mode mode
,
551 const char *prefix
, const char *postfix
)
554 const char *spd
, *pr
;
558 spd
= get_spd(serial_flags
, mode
);
560 print_flag(&pr
, spd
);
562 for (i
= CMD_FLAG_FIRST
; i
<= CMD_FLAG_LAST
; i
++) {
563 if ((serial_flags
& setbits
[i
])
564 && (mode
> PRINT_SUMMARY
|| !cmd_noprint(i
))
566 print_flag(&pr
, nth_string(commands
, i
));
570 puts(pr
== prefix
? "" : postfix
);
573 static void print_closing_wait(unsigned int closing_wait
)
575 switch (closing_wait
) {
576 case ASYNC_CLOSING_WAIT_NONE
:
579 case ASYNC_CLOSING_WAIT_INF
:
583 printf("%u\n", closing_wait
);
587 static void serial_get(const char *device
, enum print_mode mode
)
590 const char *uart
, *prefix
, *postfix
;
591 struct serial_struct serinfo
;
593 fd
= serial_open(device
, /*quiet:*/ mode
== PRINT_SUMMARY
);
597 ret
= serial_ctl(fd
, CTL_GET
| CTL_CLOSE
| CTL_NODIE
, &serinfo
);
601 uart
= uart_type(serinfo
.type
);
602 prefix
= ", Flags: ";
607 printf("%s, UART: %s, Port: 0x%.4x, IRQ: %d",
608 device
, uart
, serinfo
.port
, serinfo
.irq
);
613 printf("%s at 0x%.4x (irq = %d) is a %s",
614 device
, serinfo
.port
, serinfo
.irq
, uart
);
619 printf("%s uart %s port 0x%.4x irq %d baud_base %d", device
,
620 uart
, serinfo
.port
, serinfo
.irq
, serinfo
.baud_base
);
624 printf("%s, Line %d, UART: %s, Port: 0x%.4x, IRQ: %d\n",
625 device
, serinfo
.line
, uart
, serinfo
.port
, serinfo
.irq
);
626 printf("\tBaud_base: %d, close_delay: %u, divisor: %d\n",
627 serinfo
.baud_base
, serinfo
.close_delay
,
628 serinfo
.custom_divisor
);
629 printf("\tclosing_wait: ");
630 print_closing_wait(serinfo
.closing_wait
);
631 prefix
= "\tFlags: ";
638 print_serial_flags(serinfo
.flags
, mode
, prefix
, postfix
);
641 static int find_cmd(const char *cmd
)
645 idx
= index_in_strings_case_insensitive(commands
, cmd
);
647 bb_error_msg_and_die("invalid flag: %s", cmd
);
652 static void serial_set(char **arg
, int opts
)
654 struct serial_struct serinfo
;
659 fd
= serial_open(*arg
++, /*quiet:*/ false);
663 serial_ctl(fd
, CTL_GET
, &serinfo
);
672 invert
= (*word
== '^');
675 cmd
= find_cmd(word
);
677 if (*arg
== NULL
&& cmd_need_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 opt_complementary
= "-1:b-aG:G-ab:a-bG";
745 opts
= getopt32(argv
, "bGavzg");
748 if (!argv
[1]) /* one arg only? */
751 if (!(opts
& OPT_GET
)) {
752 serial_set(argv
, opts
);
756 if (opts
& (OPT_VERBOSE
| OPT_GET
)) {
758 serial_get(*argv
++, opts
& OPT_MODE_MASK
);