2011-05-01 06:39:14 +05:30
|
|
|
/* vi: set sw=4 ts=4: */
|
|
|
|
/*
|
|
|
|
* setserial implementation for busybox
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Copyright (C) 2011 Marek Bečka <yuen@klacno.sk>
|
|
|
|
*
|
|
|
|
* 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:
|
2011-05-02 06:17:25 +05:30
|
|
|
serinfo.flags &= ~ASYNC_SPD_MASK;
|
2011-05-01 06:39:14 +05:30
|
|
|
/* 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;
|
|
|
|
}
|