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