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