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