26902a27328bb2cd4e0212d3cd4476853c3f2564
[platform/upstream/busybox.git] / miscutils / setserial.c
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:       depends on 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, *baund_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         "baund_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 }