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
  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
 190struct 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
 246enum 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
 260static const char serial_types[] ALIGN1 =
 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
 291static const char commands[] ALIGN1 =
 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
 321enum
 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
 355static bool cmd_noprint(int cmd)
 356{
 357        return (cmd >= CMD_FLAG_SKIP_TEST && cmd <= CMD_FLAG_CALLOUT_NOHUP);
 358}
 359
 360static bool cmd_is_flag(int cmd)
 361{
 362        return (cmd >= CMD_FLAG_FIRST && cmd <= CMD_FLAG_LAST);
 363}
 364
 365static 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
 386static 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#define STR_INFINITE "infinite"
 408#define STR_NONE     "none"
 409
 410static 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 */
 419static 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
 433static int uart_id(const char *name)
 434{
 435        return index_in_strings_case_insensitive(serial_types, name);
 436}
 437
 438static 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
 467static int get_numeric(const char *arg)
 468{
 469        return bb_strtol(arg, NULL, 0);
 470}
 471
 472static 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
 483static 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
 493static 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
 504static 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
 544static void print_flag(const char **prefix, const char *flag)
 545{
 546        printf("%s%s", *prefix, flag);
 547        *prefix = " ";
 548}
 549
 550static 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
 573static 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
 587static 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
 641static 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
 652static 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
 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        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