setserial: new applet
No binary (build failed)
—
(log yok)
| @@ -0,0 +1,763 @@ | |
| + | /* vi: set sw=4 ts=4: */ |
| + | /* |
| + | * setserial implementation for busybox |
| + | * |
| + | * |
| + | * Copyright (C) 2011 Marek Bečka <[email protected]> |
| + | * |
| + | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
| + | */ |
| + | |
| + | //config:config SETSERIAL |
| + | //config: bool "setserial" |
| + | //config: default y |
| + | //config: depends on PLATFORM_LINUX |
| + | //config: help |
| + | //config: Retrieve or set Linux serial port. |
| + | |
| + | //applet:IF_SETSERIAL(APPLET(setserial, BB_DIR_BIN, BB_SUID_DROP)) |
| + | |
| + | //kbuild:lib-$(CONFIG_SETSERIAL) += setserial.o |
| + | |
| + | #include "libbb.h" |
| + | #include <assert.h> |
| + | |
| + | #ifndef PORT_UNKNOWN |
| + | # define PORT_UNKNOWN 0 |
| + | #endif |
| + | #ifndef PORT_8250 |
| + | # define PORT_8250 1 |
| + | #endif |
| + | #ifndef PORT_16450 |
| + | # define PORT_16450 2 |
| + | #endif |
| + | #ifndef PORT_16550 |
| + | # define PORT_16550 3 |
| + | #endif |
| + | #ifndef PORT_16550A |
| + | # define PORT_16550A 4 |
| + | #endif |
| + | #ifndef PORT_CIRRUS |
| + | # define PORT_CIRRUS 5 |
| + | #endif |
| + | #ifndef PORT_16650 |
| + | # define PORT_16650 6 |
| + | #endif |
| + | #ifndef PORT_16650V2 |
| + | # define PORT_16650V2 7 |
| + | #endif |
| + | #ifndef PORT_16750 |
| + | # define PORT_16750 8 |
| + | #endif |
| + | #ifndef PORT_STARTECH |
| + | # define PORT_STARTECH 9 |
| + | #endif |
| + | #ifndef PORT_16C950 |
| + | # define PORT_16C950 10 |
| + | #endif |
| + | #ifndef PORT_16654 |
| + | # define PORT_16654 11 |
| + | #endif |
| + | #ifndef PORT_16850 |
| + | # define PORT_16850 12 |
| + | #endif |
| + | #ifndef PORT_RSA |
| + | # define PORT_RSA 13 |
| + | #endif |
| + | #ifndef PORT_NS16550A |
| + | # define PORT_NS16550A 14 |
| + | #endif |
| + | #ifndef PORT_XSCALE |
| + | # define PORT_XSCALE 15 |
| + | #endif |
| + | #ifndef PORT_RM9000 |
| + | # define PORT_RM9000 16 |
| + | #endif |
| + | #ifndef PORT_OCTEON |
| + | # define PORT_OCTEON 17 |
| + | #endif |
| + | #ifndef PORT_AR7 |
| + | # define PORT_AR7 18 |
| + | #endif |
| + | #ifndef PORT_U6_16550A |
| + | # define PORT_U6_16550A 19 |
| + | #endif |
| + | |
| + | #ifndef ASYNCB_HUP_NOTIFY |
| + | # define ASYNCB_HUP_NOTIFY 0 |
| + | #endif |
| + | #ifndef ASYNCB_FOURPORT |
| + | # define ASYNCB_FOURPORT 1 |
| + | #endif |
| + | #ifndef ASYNCB_SAK |
| + | # define ASYNCB_SAK 2 |
| + | #endif |
| + | #ifndef ASYNCB_SPLIT_TERMIOS |
| + | # define ASYNCB_SPLIT_TERMIOS 3 |
| + | #endif |
| + | #ifndef ASYNCB_SPD_HI |
| + | # define ASYNCB_SPD_HI 4 |
| + | #endif |
| + | #ifndef ASYNCB_SPD_VHI |
| + | # define ASYNCB_SPD_VHI 5 |
| + | #endif |
| + | #ifndef ASYNCB_SKIP_TEST |
| + | # define ASYNCB_SKIP_TEST 6 |
| + | #endif |
| + | #ifndef ASYNCB_AUTO_IRQ |
| + | # define ASYNCB_AUTO_IRQ 7 |
| + | #endif |
| + | #ifndef ASYNCB_SESSION_LOCKOUT |
| + | # define ASYNCB_SESSION_LOCKOUT 8 |
| + | #endif |
| + | #ifndef ASYNCB_PGRP_LOCKOUT |
| + | # define ASYNCB_PGRP_LOCKOUT 9 |
| + | #endif |
| + | #ifndef ASYNCB_CALLOUT_NOHUP |
| + | # define ASYNCB_CALLOUT_NOHUP 10 |
| + | #endif |
| + | #ifndef ASYNCB_SPD_SHI |
| + | # define ASYNCB_SPD_SHI 12 |
| + | #endif |
| + | #ifndef ASYNCB_LOW_LATENCY |
| + | # define ASYNCB_LOW_LATENCY 13 |
| + | #endif |
| + | #ifndef ASYNCB_BUGGY_UART |
| + | # define ASYNCB_BUGGY_UART 14 |
| + | #endif |
| + | |
| + | #ifndef ASYNC_HUP_NOTIFY |
| + | # define ASYNC_HUP_NOTIFY (1U << ASYNCB_HUP_NOTIFY) |
| + | #endif |
| + | #ifndef ASYNC_FOURPORT |
| + | # define ASYNC_FOURPORT (1U << ASYNCB_FOURPORT) |
| + | #endif |
| + | #ifndef ASYNC_SAK |
| + | # define ASYNC_SAK (1U << ASYNCB_SAK) |
| + | #endif |
| + | #ifndef ASYNC_SPLIT_TERMIOS |
| + | # define ASYNC_SPLIT_TERMIOS (1U << ASYNCB_SPLIT_TERMIOS) |
| + | #endif |
| + | #ifndef ASYNC_SPD_HI |
| + | # define ASYNC_SPD_HI (1U << ASYNCB_SPD_HI) |
| + | #endif |
| + | #ifndef ASYNC_SPD_VHI |
| + | # define ASYNC_SPD_VHI (1U << ASYNCB_SPD_VHI) |
| + | #endif |
| + | #ifndef ASYNC_SKIP_TEST |
| + | # define ASYNC_SKIP_TEST (1U << ASYNCB_SKIP_TEST) |
| + | #endif |
| + | #ifndef ASYNC_AUTO_IRQ |
| + | # define ASYNC_AUTO_IRQ (1U << ASYNCB_AUTO_IRQ) |
| + | #endif |
| + | #ifndef ASYNC_SESSION_LOCKOUT |
| + | # define ASYNC_SESSION_LOCKOUT (1U << ASYNCB_SESSION_LOCKOUT) |
| + | #endif |
| + | #ifndef ASYNC_PGRP_LOCKOUT |
| + | # define ASYNC_PGRP_LOCKOUT (1U << ASYNCB_PGRP_LOCKOUT) |
| + | #endif |
| + | #ifndef ASYNC_CALLOUT_NOHUP |
| + | # define ASYNC_CALLOUT_NOHUP (1U << ASYNCB_CALLOUT_NOHUP) |
| + | #endif |
| + | #ifndef ASYNC_SPD_SHI |
| + | # define ASYNC_SPD_SHI (1U << ASYNCB_SPD_SHI) |
| + | #endif |
| + | #ifndef ASYNC_LOW_LATENCY |
| + | # define ASYNC_LOW_LATENCY (1U << ASYNCB_LOW_LATENCY) |
| + | #endif |
| + | #ifndef ASYNC_BUGGY_UART |
| + | # define ASYNC_BUGGY_UART (1U << ASYNCB_BUGGY_UART) |
| + | #endif |
| + | |
| + | #ifndef ASYNC_SPD_CUST |
| + | # define ASYNC_SPD_CUST (ASYNC_SPD_HI|ASYNC_SPD_VHI) |
| + | #endif |
| + | #ifndef ASYNC_SPD_WARP |
| + | # define ASYNC_SPD_WARP (ASYNC_SPD_HI|ASYNC_SPD_SHI) |
| + | #endif |
| + | #ifndef ASYNC_SPD_MASK |
| + | # define ASYNC_SPD_MASK (ASYNC_SPD_HI|ASYNC_SPD_VHI|ASYNC_SPD_SHI) |
| + | #endif |
| + | |
| + | #ifndef ASYNC_CLOSING_WAIT_INF |
| + | # define ASYNC_CLOSING_WAIT_INF 0 |
| + | #endif |
| + | #ifndef ASYNC_CLOSING_WAIT_NONE |
| + | # define ASYNC_CLOSING_WAIT_NONE 65535 |
| + | #endif |
| + | |
| + | #ifndef _LINUX_SERIAL_H |
| + | struct serial_struct { |
| + | int type; |
| + | int line; |
| + | unsigned int port; |
| + | int irq; |
| + | int flags; |
| + | int xmit_fifo_size; |
| + | int custom_divisor; |
| + | int baud_base; |
| + | unsigned short close_delay; |
| + | char io_type; |
| + | char reserved_char[1]; |
| + | int hub6; |
| + | unsigned short closing_wait; /* time to wait before closing */ |
| + | unsigned short closing_wait2; /* no longer used... */ |
| + | unsigned char *iomem_base; |
| + | unsigned short iomem_reg_shift; |
| + | unsigned int port_high; |
| + | unsigned long iomap_base; /* cookie passed into ioremap */ |
| + | }; |
| + | #endif |
| + | |
| + | //usage:#define setserial_trivial_usage |
| + | //usage: "[-gabGvzV] DEVICE [PARAMETER [ARG]]..." |
| + | //usage:#define setserial_full_usage "\n\n" |
| + | //usage: "Request or set Linux serial port information\n\n" |
| + | //usage: "Options:\n" |
| + | //usage: " -g Interpret parameters as list of devices for reporting" |
| + | //usage: " -a Print all available information\n" |
| + | //usage: " -b Print summary information\n" |
| + | //usage: " -G Print in form which can be fed back\n" |
| + | //usage: " to setserial as command line parameters\n" |
| + | //usage: " -z Zero out serial flags before setting\n" |
| + | //usage: " -v Verbose\n" |
| + | //usage: "\n" |
| + | //usage: "Parameters: (* = takes an argument, ^ = can be turned off by preceding ^)\n" |
| + | //usage: " *port, *irq, *divisor, *uart, *baund_base, *close_delay, *closing_wait,\n" |
| + | //usage: " ^fourport, ^auto_irq, ^skip_test, ^sak, ^session_lockout, ^pgrp_lockout,\n" |
| + | //usage: " ^callout_nohup, ^split_termios, ^hup_notify, ^low_latency, autoconfig,\n" |
| + | //usage: " spd_normal, spd_hi, spd_vhi, spd_shi, spd_warp, spd_cust\n" |
| + | //usage: "\n" |
| + | //usage: "UART types:\n" |
| + | //usage: " unknown, 8250, 16450, 16550, 16550A, Cirrus, 16650, 16650V2, 16750,\n" |
| + | //usage: " 16950, 16954, 16654, 16850, RSA, NS16550A, XSCALE, RM9000, OCTEON, AR7,\n" |
| + | //usage: " U6_16550A" |
| + | |
| + | #define OPT_PRINT_SUMMARY (1 << 0) |
| + | #define OPT_PRINT_FEDBACK (1 << 1) |
| + | #define OPT_PRINT_ALL (1 << 2) |
| + | #define OPT_VERBOSE (1 << 3) |
| + | #define OPT_ZERO (1 << 4) |
| + | #define OPT_GET (1 << 5) |
| + | |
| + | #define OPT_MODE_MASK \ |
| + | (OPT_PRINT_ALL | OPT_PRINT_SUMMARY | OPT_PRINT_FEDBACK) |
| + | |
| + | enum print_mode |
| + | { |
| + | PRINT_NORMAL = 0, |
| + | PRINT_SUMMARY = (1 << 0), |
| + | PRINT_FEDBACK = (1 << 1), |
| + | PRINT_ALL = (1 << 2), |
| + | }; |
| + | |
| + | #define CTL_SET (1 << 0) |
| + | #define CTL_CONFIG (1 << 1) |
| + | #define CTL_GET (1 << 2) |
| + | #define CTL_CLOSE (1 << 3) |
| + | #define CTL_NODIE (1 << 4) |
| + | |
| + | static const char serial_types[] = |
| + | "unknown\0" /* 0 */ |
| + | "8250\0" /* 1 */ |
| + | "16450\0" /* 2 */ |
| + | "16550\0" /* 3 */ |
| + | "16550A\0" /* 4 */ |
| + | "Cirrus\0" /* 5 */ |
| + | "16650\0" /* 6 */ |
| + | "16650V2\0" /* 7 */ |
| + | "16750\0" /* 8 */ |
| + | "16950\0" /* 9 UNIMPLEMENTED: also know as "16950/954" */ |
| + | "16954\0" /* 10 */ |
| + | "16654\0" /* 11 */ |
| + | "16850\0" /* 12 */ |
| + | "RSA\0" /* 13 */ |
| + | #ifndef SETSERIAL_BASE |
| + | "NS16550A\0" /* 14 */ |
| + | "XSCALE\0" /* 15 */ |
| + | "RM9000\0" /* 16 */ |
| + | "OCTEON\0" /* 17 */ |
| + | "AR7\0" /* 18 */ |
| + | "U6_16550A\0" /* 19 */ |
| + | #endif |
| + | ; |
| + | |
| + | #ifndef SETSERIAL_BASE |
| + | # define MAX_SERIAL_TYPE 19 |
| + | #else |
| + | # define MAX_SERIAL_TYPE 13 |
| + | #endif |
| + | |
| + | static const char commands[] = |
| + | "spd_normal\0" |
| + | "spd_hi\0" |
| + | "spd_vhi\0" |
| + | "spd_shi\0" |
| + | "spd_warp\0" |
| + | "spd_cust\0" |
| + | |
| + | "sak\0" |
| + | "fourport\0" |
| + | "hup_notify\0" |
| + | "skip_test\0" |
| + | "auto_irq\0" |
| + | "split_termios\0" |
| + | "session_lockout\0" |
| + | "pgrp_lockout\0" |
| + | "callout_nohup\0" |
| + | "low_latency\0" |
| + | |
| + | "port\0" |
| + | "irq\0" |
| + | "divisor\0" |
| + | "uart\0" |
| + | "baund_base\0" |
| + | "close_delay\0" |
| + | "closing_wait\0" |
| + | |
| + | "autoconfig\0" |
| + | ; |
| + | |
| + | enum |
| + | { |
| + | CMD_SPD_NORMAL = 0, |
| + | CMD_SPD_HI, |
| + | CMD_SPD_VHI, |
| + | CMD_SPD_SHI, |
| + | CMD_SPD_WARP, |
| + | CMD_SPD_CUST, |
| + | |
| + | CMD_FLAG_SAK, |
| + | CMD_FLAG_FOURPORT, |
| + | CMD_FLAG_NUP_NOTIFY, |
| + | CMD_FLAG_SKIP_TEST, |
| + | CMD_FLAG_AUTO_IRQ, |
| + | CMD_FLAG_SPLIT_TERMIOS, |
| + | CMD_FLAG_SESSION_LOCKOUT, |
| + | CMD_FLAG_PGRP_LOCKOUT, |
| + | CMD_FLAG_CALLOUT_NOHUP, |
| + | CMD_FLAG_LOW_LATENCY, |
| + | |
| + | CMD_PORT, |
| + | CMD_IRQ, |
| + | CMD_DIVISOR, |
| + | CMD_UART, |
| + | CMD_BASE, |
| + | CMD_DELAY, |
| + | CMD_WAIT, |
| + | |
| + | CMD_AUTOCONFIG, |
| + | |
| + | CMD_FLAG_FIRST = CMD_FLAG_SAK, |
| + | CMD_FLAG_LAST = CMD_FLAG_LOW_LATENCY, |
| + | }; |
| + | |
| + | static bool cmd_noprint(int cmd) |
| + | { |
| + | return (cmd >= CMD_FLAG_SKIP_TEST && cmd <= CMD_FLAG_CALLOUT_NOHUP); |
| + | } |
| + | |
| + | static bool cmd_is_flag(int cmd) |
| + | { |
| + | return (cmd >= CMD_FLAG_FIRST && cmd <= CMD_FLAG_LAST); |
| + | } |
| + | |
| + | static bool cmd_need_arg(int cmd) |
| + | { |
| + | return (cmd >= CMD_PORT && cmd <= CMD_WAIT); |
| + | } |
| + | |
| + | #define ALL_SPD ( \ |
| + | ASYNC_SPD_HI | ASYNC_SPD_VHI | ASYNC_SPD_SHI | \ |
| + | ASYNC_SPD_WARP | ASYNC_SPD_CUST \ |
| + | ) |
| + | |
| + | #define ALL_FLAGS ( \ |
| + | ASYNC_SAK | ASYNC_FOURPORT | ASYNC_HUP_NOTIFY | \ |
| + | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ | ASYNC_SPLIT_TERMIOS | \ |
| + | ASYNC_SESSION_LOCKOUT | ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP | \ |
| + | ASYNC_LOW_LATENCY \ |
| + | ) |
| + | |
| + | #if (ALL_SPD | ALL_FLAGS) > 0xffff |
| + | # error "Unexpected flags size" |
| + | #endif |
| + | |
| + | static const uint16_t setbits[CMD_FLAG_LAST + 1] = |
| + | { |
| + | 0, |
| + | ASYNC_SPD_HI, |
| + | ASYNC_SPD_VHI, |
| + | ASYNC_SPD_SHI, |
| + | ASYNC_SPD_WARP, |
| + | ASYNC_SPD_CUST, |
| + | |
| + | ASYNC_SAK, |
| + | ASYNC_FOURPORT, |
| + | ASYNC_HUP_NOTIFY, |
| + | ASYNC_SKIP_TEST, |
| + | ASYNC_AUTO_IRQ, |
| + | ASYNC_SPLIT_TERMIOS, |
| + | ASYNC_SESSION_LOCKOUT, |
| + | ASYNC_PGRP_LOCKOUT, |
| + | ASYNC_CALLOUT_NOHUP, |
| + | ASYNC_LOW_LATENCY |
| + | }; |
| + | |
| + | static const char STR_INFINITE[] = "infinite"; |
| + | static const char STR_NONE[] = "none"; |
| + | |
| + | static const char *uart_type(int type) |
| + | { |
| + | if (type > MAX_SERIAL_TYPE) |
| + | return "undefined"; |
| + | |
| + | return nth_string(serial_types, type); |
| + | } |
| + | |
| + | /* libbb candidate */ |
| + | static int index_in_strings_case_insensitive(const char *strings, const char *key) |
| + | { |
| + | int idx = 0; |
| + | |
| + | while (*strings) { |
| + | if (strcasecmp(strings, key) == 0) { |
| + | return idx; |
| + | } |
| + | strings += strlen(strings) + 1; /* skip NUL */ |
| + | idx++; |
| + | } |
| + | return -1; |
| + | } |
| + | |
| + | static int uart_id(const char *name) |
| + | { |
| + | return index_in_strings_case_insensitive(serial_types, name); |
| + | } |
| + | |
| + | static const char *get_spd(int flags, enum print_mode mode) |
| + | { |
| + | int idx; |
| + | |
| + | switch (flags & ASYNC_SPD_MASK) { |
| + | case ASYNC_SPD_HI: |
| + | idx = CMD_SPD_HI; |
| + | break; |
| + | case ASYNC_SPD_VHI: |
| + | idx = CMD_SPD_VHI; |
| + | break; |
| + | case ASYNC_SPD_SHI: |
| + | idx = CMD_SPD_SHI; |
| + | break; |
| + | case ASYNC_SPD_WARP: |
| + | idx = CMD_SPD_WARP; |
| + | break; |
| + | case ASYNC_SPD_CUST: |
| + | idx = CMD_SPD_CUST; |
| + | break; |
| + | default: |
| + | if (mode < PRINT_FEDBACK) |
| + | return NULL; |
| + | idx = CMD_SPD_NORMAL; |
| + | } |
| + | |
| + | return nth_string(commands, idx); |
| + | } |
| + | |
| + | static int get_numeric(const char *arg) |
| + | { |
| + | return bb_strtol(arg, NULL, 0); |
| + | } |
| + | |
| + | static int get_wait(const char *arg) |
| + | { |
| + | if (strcasecmp(arg, STR_NONE) == 0) |
| + | return ASYNC_CLOSING_WAIT_NONE; |
| + | |
| + | if (strcasecmp(arg, STR_INFINITE) == 0) |
| + | return ASYNC_CLOSING_WAIT_INF; |
| + | |
| + | return get_numeric(arg); |
| + | } |
| + | |
| + | static int get_uart(const char *arg) |
| + | { |
| + | int uart = uart_id(arg); |
| + | |
| + | if (uart < 0) |
| + | bb_error_msg_and_die("illegal UART type: %s", arg); |
| + | |
| + | return uart; |
| + | } |
| + | |
| + | static int serial_open(const char *dev, bool quiet) |
| + | { |
| + | int fd; |
| + | |
| + | fd = device_open(dev, O_RDWR | O_NONBLOCK); |
| + | if (fd < 0 && !quiet) |
| + | bb_simple_perror_msg(dev); |
| + | |
| + | return fd; |
| + | } |
| + | |
| + | static int serial_ctl(int fd, int ops, struct serial_struct *serinfo) |
| + | { |
| + | int ret = 0; |
| + | const char *err; |
| + | |
| + | if (ops & CTL_SET) { |
| + | ret = ioctl(fd, TIOCSSERIAL, serinfo); |
| + | if (ret < 0) { |
| + | err = "can't set serial info"; |
| + | goto fail; |
| + | } |
| + | } |
| + | |
| + | if (ops & CTL_CONFIG) { |
| + | ret = ioctl(fd, TIOCSERCONFIG); |
| + | if (ret < 0) { |
| + | err = "can't autoconfigure port"; |
| + | goto fail; |
| + | } |
| + | } |
| + | |
| + | if (ops & CTL_GET) { |
| + | ret = ioctl(fd, TIOCGSERIAL, serinfo); |
| + | if (ret < 0) { |
| + | err = "can't get serial info"; |
| + | goto fail; |
| + | } |
| + | } |
| + | nodie: |
| + | if (ops & CTL_CLOSE) |
| + | close(fd); |
| + | |
| + | return ret; |
| + | fail: |
| + | bb_simple_perror_msg(err); |
| + | if (ops & CTL_NODIE) |
| + | goto nodie; |
| + | exit(EXIT_FAILURE); |
| + | } |
| + | |
| + | static void print_flag(const char **prefix, const char *flag) |
| + | { |
| + | printf("%s%s", *prefix, flag); |
| + | *prefix = " "; |
| + | } |
| + | |
| + | static void print_serial_flags(int serial_flags, enum print_mode mode, |
| + | const char *prefix, const char *postfix) |
| + | { |
| + | int i; |
| + | const char *spd, *pr; |
| + | |
| + | pr = prefix; |
| + | |
| + | spd = get_spd(serial_flags, mode); |
| + | if (spd) |
| + | print_flag(&pr, spd); |
| + | |
| + | for (i = CMD_FLAG_FIRST; i <= CMD_FLAG_LAST; i++) { |
| + | if ((serial_flags & setbits[i]) |
| + | && (mode > PRINT_SUMMARY || !cmd_noprint(i)) |
| + | ) { |
| + | print_flag(&pr, nth_string(commands, i)); |
| + | } |
| + | } |
| + | |
| + | puts(pr == prefix ? "" : postfix); |
| + | } |
| + | |
| + | static void print_closing_wait(unsigned int closing_wait) |
| + | { |
| + | switch (closing_wait) { |
| + | case ASYNC_CLOSING_WAIT_NONE: |
| + | puts(STR_NONE); |
| + | break; |
| + | case ASYNC_CLOSING_WAIT_INF: |
| + | puts(STR_INFINITE); |
| + | break; |
| + | default: |
| + | printf("%u\n", closing_wait); |
| + | } |
| + | } |
| + | |
| + | static void serial_get(const char *device, enum print_mode mode) |
| + | { |
| + | int fd, ret; |
| + | const char *uart, *prefix, *postfix; |
| + | struct serial_struct serinfo; |
| + | |
| + | fd = serial_open(device, /*quiet:*/ mode == PRINT_SUMMARY); |
| + | if (fd < 0) |
| + | return; |
| + | |
| + | ret = serial_ctl(fd, CTL_GET | CTL_CLOSE | CTL_NODIE, &serinfo); |
| + | if (ret < 0) |
| + | return; |
| + | |
| + | uart = uart_type(serinfo.type); |
| + | prefix = ", Flags: "; |
| + | postfix = ""; |
| + | |
| + | switch (mode) { |
| + | case PRINT_NORMAL: |
| + | printf("%s, UART: %s, Port: 0x%.4x, IRQ: %d", |
| + | device, uart, serinfo.port, serinfo.irq); |
| + | break; |
| + | case PRINT_SUMMARY: |
| + | if (!serinfo.type) |
| + | return; |
| + | printf("%s at 0x%.4x (irq = %d) is a %s", |
| + | device, serinfo.port, serinfo.irq, uart); |
| + | prefix = " ("; |
| + | postfix = ")"; |
| + | break; |
| + | case PRINT_FEDBACK: |
| + | printf("%s uart %s port 0x%.4x irq %d baud_base %d", device, |
| + | uart, serinfo.port, serinfo.irq, serinfo.baud_base); |
| + | prefix = " "; |
| + | break; |
| + | case PRINT_ALL: |
| + | printf("%s, Line %d, UART: %s, Port: 0x%.4x, IRQ: %d\n", |
| + | device, serinfo.line, uart, serinfo.port, serinfo.irq); |
| + | printf("\tBaud_base: %d, close_delay: %u, divisor: %d\n", |
| + | serinfo.baud_base, serinfo.close_delay, |
| + | serinfo.custom_divisor); |
| + | printf("\tclosing_wait: "); |
| + | print_closing_wait(serinfo.closing_wait); |
| + | prefix = "\tFlags: "; |
| + | postfix = "\n"; |
| + | break; |
| + | default: |
| + | assert(0); |
| + | } |
| + | |
| + | print_serial_flags(serinfo.flags, mode, prefix, postfix); |
| + | } |
| + | |
| + | static int find_cmd(const char *cmd) |
| + | { |
| + | int idx; |
| + | |
| + | idx = index_in_strings_case_insensitive(commands, cmd); |
| + | if (idx < 0) |
| + | bb_error_msg_and_die("invalid flag: %s", cmd); |
| + | |
| + | return idx; |
| + | } |
| + | |
| + | static void serial_set(char **arg, int opts) |
| + | { |
| + | struct serial_struct serinfo; |
| + | int cmd; |
| + | const char *word; |
| + | int fd; |
| + | |
| + | fd = serial_open(*arg++, /*quiet:*/ false); |
| + | if (fd < 0) |
| + | exit(201); |
| + | |
| + | serial_ctl(fd, CTL_GET, &serinfo); |
| + | |
| + | if (opts & OPT_ZERO) |
| + | serinfo.flags = 0; |
| + | |
| + | while (*arg) { |
| + | int invert; |
| + | |
| + | word = *arg++; |
| + | invert = (*word == '^'); |
| + | word += invert; |
| + | |
| + | cmd = find_cmd(word); |
| + | |
| + | if (*arg == NULL && cmd_need_arg(cmd)) |
| + | bb_error_msg_and_die(bb_msg_requires_arg, word); |
| + | |
| + | if (invert && !cmd_is_flag(cmd)) |
| + | bb_error_msg_and_die("can't invert %s", word); |
| + | |
| + | switch (cmd) { |
| + | case CMD_SPD_NORMAL: |
| + | case CMD_SPD_HI: |
| + | case CMD_SPD_VHI: |
| + | case CMD_SPD_SHI: |
| + | case CMD_SPD_WARP: |
| + | case CMD_SPD_CUST: |
| + | serinfo.flags &= ASYNC_SPD_MASK; |
| + | /* fallthrough */ |
| + | case CMD_FLAG_SAK: |
| + | case CMD_FLAG_FOURPORT: |
| + | case CMD_FLAG_NUP_NOTIFY: |
| + | case CMD_FLAG_SKIP_TEST: |
| + | case CMD_FLAG_AUTO_IRQ: |
| + | case CMD_FLAG_SPLIT_TERMIOS: |
| + | case CMD_FLAG_SESSION_LOCKOUT: |
| + | case CMD_FLAG_PGRP_LOCKOUT: |
| + | case CMD_FLAG_CALLOUT_NOHUP: |
| + | case CMD_FLAG_LOW_LATENCY: |
| + | if (invert) |
| + | serinfo.flags &= ~setbits[cmd]; |
| + | else |
| + | serinfo.flags |= setbits[cmd]; |
| + | break; |
| + | case CMD_PORT: |
| + | serinfo.port = get_numeric(*arg++); |
| + | break; |
| + | case CMD_IRQ: |
| + | serinfo.irq = get_numeric(*arg++); |
| + | break; |
| + | case CMD_DIVISOR: |
| + | serinfo.custom_divisor = get_numeric(*arg++); |
| + | break; |
| + | case CMD_UART: |
| + | serinfo.type = get_uart(*arg++); |
| + | break; |
| + | case CMD_BASE: |
| + | serinfo.baud_base = get_numeric(*arg++); |
| + | break; |
| + | case CMD_DELAY: |
| + | serinfo.close_delay = get_numeric(*arg++); |
| + | break; |
| + | case CMD_WAIT: |
| + | serinfo.closing_wait = get_wait(*arg++); |
| + | break; |
| + | case CMD_AUTOCONFIG: |
| + | serial_ctl(fd, CTL_SET | CTL_CONFIG | CTL_GET, &serinfo); |
| + | break; |
| + | default: |
| + | assert(0); |
| + | } |
| + | } |
| + | |
| + | serial_ctl(fd, CTL_SET | CTL_CLOSE, &serinfo); |
| + | } |
| + | |
| + | int setserial_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| + | int setserial_main(int argc UNUSED_PARAM, char **argv) |
| + | { |
| + | int opts; |
| + | |
| + | opt_complementary = "-1:b-aG:G-ab:a-bG"; |
| + | opts = getopt32(argv, "bGavzg"); |
| + | argv += optind; |
| + | |
| + | if (!argv[1]) /* one arg only? */ |
| + | opts |= OPT_GET; |
| + | |
| + | if (!(opts & OPT_GET)) { |
| + | serial_set(argv, opts); |
| + | argv[1] = NULL; |
| + | } |
| + | |
| + | if (opts & (OPT_VERBOSE | OPT_GET)) { |
| + | do { |
| + | serial_get(*argv++, opts & OPT_MODE_MASK); |
| + | } while (*argv); |
| + | } |
| + | |
| + | return EXIT_SUCCESS; |
| + | } |