mirror of
https://github.com/eddyem/tty_term.git
synced 2026-06-21 11:26:24 +03:00
fixed some bugs, switching to actual usefull_macros library
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 4.0)
|
||||||
set(PROJ tty_term)
|
set(PROJ tty_term)
|
||||||
set(MINOR_VERSION "1")
|
set(MINOR_VERSION "1")
|
||||||
set(MID_VERSION "2")
|
set(MID_VERSION "3")
|
||||||
set(MAJOR_VERSION "0")
|
set(MAJOR_VERSION "0")
|
||||||
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
|
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ option(DEBUG "Compile in debug mode" OFF)
|
|||||||
# default flags
|
# default flags
|
||||||
set(CMAKE_C_FLAGS_RELEASE "")
|
set(CMAKE_C_FLAGS_RELEASE "")
|
||||||
set(CMAKE_C_FLAGS_DEBUG "")
|
set(CMAKE_C_FLAGS_DEBUG "")
|
||||||
set(CMAKE_C_FLAGS "-O3 -std=gnu99 -D_XOPEN_SOURCE=12345 -D_DEFAULT_SOURCE")
|
set(CMAKE_C_FLAGS "-O3 -std=c23 -D_XOPEN_SOURCE=12345 -D_DEFAULT_SOURCE")
|
||||||
|
|
||||||
set(CMAKE_COLOR_MAKEFILE ON)
|
set(CMAKE_COLOR_MAKEFILE ON)
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ endif()
|
|||||||
|
|
||||||
###### pkgconfig ######
|
###### pkgconfig ######
|
||||||
# pkg-config modules (for pkg-check-modules)
|
# pkg-config modules (for pkg-check-modules)
|
||||||
set(MODULES ncurses readline usefull_macros>=0.3.2)
|
set(MODULES ncurses readline usefull_macros>=0.3.5)
|
||||||
|
|
||||||
# find packages:
|
# find packages:
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
Tue 19 May 10:56:54 MSK 2026
|
||||||
|
ver. 0.3.1.
|
||||||
|
|
||||||
|
Changed to libusefull_macros v.0.3.5. All sockets and device operations now are through its API.
|
||||||
|
|
||||||
|
|
||||||
Mon Apr 28 12:33:57 MSK 2025
|
Mon Apr 28 12:33:57 MSK 2025
|
||||||
|
|
||||||
Changed to libusefull_macros v.0.3.2.
|
Changed to libusefull_macros v.0.3.2.
|
||||||
|
|||||||
239
Readme.md
239
Readme.md
@@ -1,16 +1,231 @@
|
|||||||
Very simple terminal and socket text client
|
# tty_term – Terminal Serial/Socket Communication Tool
|
||||||
===========================
|
|
||||||
|
|
||||||
|
`tty_term` is a terminal-based application for communicating with serial ports, TCP sockets, and
|
||||||
|
UNIX domain sockets. It provides a split-screen interface with an ncurses display area for received
|
||||||
|
data and a readline-powered command line for sending data.
|
||||||
|
|
||||||
Usage: tty_term [args]
|
## Features
|
||||||
|
|
||||||
Where args are:
|
- Connect to:
|
||||||
|
- Serial TTY devices (with configurable baud rate, parity, data bits, stop bits)
|
||||||
|
- Network sockets (TCP client)
|
||||||
|
- UNIX domain sockets
|
||||||
|
- Multiple display modes:
|
||||||
|
- **Text** – printable characters shown as-is, non‑printable as `\xXX`
|
||||||
|
- **Raw** – each byte as a hexadecimal value (`XX`)
|
||||||
|
- **Hexdump** – classic hexdump format (address + hex bytes + ASCII)
|
||||||
|
- Multiple input modes:
|
||||||
|
- **Text** – free text, supports C‑style escape sequences
|
||||||
|
- **Raw** – numbers in decimal, hex (`0x…`), binary (`0b…`), octal (`0…`)
|
||||||
|
- **Hex** – hexadecimal bytes (with or without spaces)
|
||||||
|
- **Modbus RTU (raw/hex)** – automatically appends CRC‑16 (Modbus)
|
||||||
|
- Scrollable output buffer, mouse wheel support
|
||||||
|
- Configurable end‑of‑line sequence (LF, CR, CR+LF, LF+CR)
|
||||||
|
- Dump all traffic to a file
|
||||||
|
- Exclusive mode for serial devices
|
||||||
|
|
||||||
- `-S, --socket` open socket
|
## Requirements
|
||||||
- `-d, --dumpfile=arg` dump data to this file
|
|
||||||
- `-e, --eol=arg` end of line: n (default), r, nr or rn
|
- Linux (or POSIX system with termios)
|
||||||
- `-h, --help` show this help
|
- C23 compiler (GCC or Clang)
|
||||||
- `-n, --name=arg` serial device path or server name/IP
|
- Libraries:
|
||||||
- `-p, --port=arg` socket port (none for UNIX)
|
- `ncurses`
|
||||||
- `-s, --speed=arg` baudrate (default: 9600)
|
- `readline`
|
||||||
- `-t, --timeout=arg` timeout for select() in ms (default: 100)
|
- `usefull_macros` (≥0.3.5) – [snippets_library](https://github.com/eddyem/snippets_library)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone --depth=1 https://github.com/eddyem/tty_term.git
|
||||||
|
cd tty_term
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
sudo make install
|
||||||
|
```
|
||||||
|
|
||||||
|
By default the program is installed into `/usr/local/bin`.
|
||||||
|
Use `-DDEBUG=ON` with CMake to build a debug version with extra logging.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
ttyterm [options]
|
||||||
|
```
|
||||||
|
|
||||||
|
You **must** specify the device/socket path with `-n` (or `--name`).
|
||||||
|
All other options are optional.
|
||||||
|
|
||||||
|
### Command‑line options
|
||||||
|
|
||||||
|
| Option | Argument | Default | Description |
|
||||||
|
| ----------------- | ---------------------- | -------------- | ----------------------------------------------------------------------- |
|
||||||
|
| `-h`, `--help` | – | – | Show help |
|
||||||
|
| `-s`, `--speed` | integer | 9600 | Baud rate (serial only) |
|
||||||
|
| `-n`, `--name` | string | – | Serial device (e.g. `/dev/ttyUSB0`), server `host:port`, or socket path |
|
||||||
|
| `-e`, `--eol` | `n`, `r`, `rn`, `nr` | `n` | End‑of‑line sequence when sending in text mode |
|
||||||
|
| `-t`, `--timeout` | milliseconds (float) | 100 | Timeout for `select()` on read |
|
||||||
|
| `-S`, `--socket` | – | – | Treat `-n` as a network socket (TCP client) |
|
||||||
|
| `-d`, `--dumpfile`| filename | – | Append all received and sent data to this file |
|
||||||
|
| `-f`, `--format` | string (e.g. `8N1`) | `8N1` | Serial line format (data bits, parity, stop bits) |
|
||||||
|
| `-U`, `--unix` | – | – | Treat `-n` as a UNIX domain socket path |
|
||||||
|
| `-x`, `--exclusive`| – | – | Open serial device in exclusive mode |
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Serial port at 115200, 8N1, CR+LF as EOL
|
||||||
|
ttyterm -n /dev/ttyUSB0 -s 115200 -e rn
|
||||||
|
|
||||||
|
# TCP connection to a Modbus server
|
||||||
|
ttyterm -n 192.168.1.100:502 -S
|
||||||
|
|
||||||
|
# UNIX socket
|
||||||
|
ttyterm -n /tmp/mysocket -U
|
||||||
|
|
||||||
|
# Log all communication to a file
|
||||||
|
ttyterm -n /dev/ttyACM0 -d session.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## User Interface
|
||||||
|
|
||||||
|
The screen is divided into three areas:
|
||||||
|
|
||||||
|
1. **Message window** (top, scrolling) – displays received data in the selected display mode.
|
||||||
|
2. **Status bar** (middle) – shows connection details, current display mode, and whether you are in
|
||||||
|
**Insert** (editing) or **Scroll** mode.
|
||||||
|
3. **Command line** (bottom) – where you type data to send.
|
||||||
|
|
||||||
|
### Keyboard Controls
|
||||||
|
|
||||||
|
#### Global (always active)
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
| -------------- | -------------------------------------------- |
|
||||||
|
| `F1` | Show poput help window |
|
||||||
|
| `F2` | Switch input/output mode to **Text** |
|
||||||
|
| `F3` | Switch input/output mode to **Raw** |
|
||||||
|
| `F4` | Switch input/output mode to **Hexdump** |
|
||||||
|
| `F5` | Switch **input** mode to Modbus RTU (raw) |
|
||||||
|
| `F6` | Switch **input** mode to Modbus RTU (hex) |
|
||||||
|
| `Tab` | Toggle between **Insert** and **Scroll** mode|
|
||||||
|
| `Ctrl`+`C` / `Q` / `Ctrl`+`D` | Quit program |
|
||||||
|
| Mouse wheel | Scroll output up/down |
|
||||||
|
|
||||||
|
The action of `F2`-`F6` keys depends on current mode: **Insert** — change input mode,
|
||||||
|
**Scroll** — change output mode.
|
||||||
|
|
||||||
|
#### Insert mode (for typing commands)
|
||||||
|
|
||||||
|
- Regular typing works as in `readline` (supports line editing, history, arrow keys).
|
||||||
|
- `Enter` sends the current line.
|
||||||
|
- `Ctrl`+`D` on an empty line quits.
|
||||||
|
- Escape sequences are supported when input mode is **Text** (see below).
|
||||||
|
|
||||||
|
#### Scroll mode (navigation)
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
| ------------------ | -------------------------------- |
|
||||||
|
| `↑` / `Ctrl`+`P` | Scroll up one line |
|
||||||
|
| `↓` / `Ctrl`+`N` | Scroll down one line |
|
||||||
|
| `PageUp` / `Ctrl`+`B` | Scroll up one page |
|
||||||
|
| `PageDown` / `Ctrl`+`F` | Scroll down one page |
|
||||||
|
| `Home` | Jump to the very top of the buffer |
|
||||||
|
| `End` | Jump to the very bottom (follow new data) |
|
||||||
|
| `←` / `Ctrl`+`L` | Scroll left (if lines exceed screen width) |
|
||||||
|
| `→` / `Ctrl`+`R` | Scroll right |
|
||||||
|
| `Q` | Quit (same as `Ctrl`+`C`) |
|
||||||
|
|
||||||
|
### Data Display Modes
|
||||||
|
|
||||||
|
- **Text** (`F2`):
|
||||||
|
Printable ASCII characters (32–126) are shown as‑is.
|
||||||
|
Line feed (`\n`) ends the current line.
|
||||||
|
Non‑printable bytes and control characters are displayed as `\xHH`.
|
||||||
|
|
||||||
|
- **Raw** (`F3`):
|
||||||
|
Every byte is shown as two hexadecimal digits, separated by a space.
|
||||||
|
Example: `41 42 0D 0A`
|
||||||
|
|
||||||
|
- **Hexdump** (`F4`):
|
||||||
|
Classic hexdump format:
|
||||||
|
`address hex bytes (8‑byte groups) |ASCII representation|`
|
||||||
|
Lines are automatically adjusted to the terminal width.
|
||||||
|
|
||||||
|
### Input Modes and Data Formatting
|
||||||
|
|
||||||
|
The **input mode** determines how the text you type is converted into raw bytes before
|
||||||
|
transmission.
|
||||||
|
|
||||||
|
#### Text mode (default after `F2`)
|
||||||
|
|
||||||
|
- Most characters are sent as their ASCII/UTF‑8 bytes.
|
||||||
|
- **Escape sequences** (C‑style) are recognised after a backslash `\`:
|
||||||
|
|
||||||
|
| Sequence | Meaning | Byte value |
|
||||||
|
| -------- | ----------------- | ---------- |
|
||||||
|
| `\a` | Bell (alert) | 0x07 |
|
||||||
|
| `\b` | Backspace | 0x08 |
|
||||||
|
| `\e` | Escape | 0x1B |
|
||||||
|
| `\f` | Form feed | 0x0C |
|
||||||
|
| `\n` | Line feed (LF) | 0x0A |
|
||||||
|
| `\r` | Carriage return | 0x0D |
|
||||||
|
| `\t` | Horizontal tab | 0x09 |
|
||||||
|
| `\v` | Vertical tab | 0x0B |
|
||||||
|
| `\xHH` | Hexadecimal byte | 0xHH |
|
||||||
|
| `\0`–`\377` | Octal byte | up to 0xFF |
|
||||||
|
|
||||||
|
- After processing escapes, the configured EOL sequence (e.g. `\r\n`) is **automatically appended**
|
||||||
|
to every line you send.
|
||||||
|
|
||||||
|
#### Raw mode (`F3` for display, but also used as input)
|
||||||
|
|
||||||
|
Input is a sequence of numbers, each representing one byte.
|
||||||
|
Numbers can be:
|
||||||
|
- Decimal: `65` (space‑separated)
|
||||||
|
- Hexadecimal: `0x41` or `0X41`
|
||||||
|
- Binary: `0b01000001`
|
||||||
|
- Octal: `0101` (leading zero)
|
||||||
|
|
||||||
|
Spaces are ignored, text and other characters treating "as is".
|
||||||
|
Example: `hel 0x6c 0x6f 0x0A`
|
||||||
|
|
||||||
|
The EOL is **not** added automatically in raw mode.
|
||||||
|
|
||||||
|
#### Hex mode (`F4` for display, also used as input)
|
||||||
|
|
||||||
|
Input must consist of hexadecimal digits (pairs are merged into bytes).
|
||||||
|
Spaces are ignored.
|
||||||
|
Example: `41420D0A` → sends four bytes: `A`, `B`, CR, LF.
|
||||||
|
|
||||||
|
#### Modbus RTU modes (`F5` – raw, `F6` – hex)
|
||||||
|
|
||||||
|
- Same input syntax as **Raw** or **Hex**, respectively.
|
||||||
|
- After converting the user‑typed numbers/hex, the program **calculates a 16‑bit Modbus CRC‑16**
|
||||||
|
(polynomial 0xA001) and appends it (low byte first).
|
||||||
|
|
||||||
|
Example (raw): `1 3 0 0 0 10`
|
||||||
|
→ sends: `01 03 00 00 00 0A C5 CD`
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
If `-d <filename>` is given, all data is appended to that file.
|
||||||
|
Received lines are prefixed with `< `, sent lines with `> `.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The program uses the library `usefull_macros` for low‑level terminal/socket operations and
|
||||||
|
logging.
|
||||||
|
- Debug builds (`cmake -DDEBUG=ON`) produce a `debug.log` file with verbose diagnostic output.
|
||||||
|
- The terminal must support at least 80 columns for comfortable hexdump viewing.
|
||||||
|
- When resizing the terminal window, the output is automatically reformatted.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
GNU General Public License version 3 (or later).
|
||||||
|
See the `LICENSE` file or [https://www.gnu.org/licenses/gpl-3.0.html](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
Edward V. Emelianov <edward.emelianoff@gmail.com>
|
||||||
|
Project page: [https://github.com/eddyem/tty_term](https://github.com/eddyem/tty_term)
|
||||||
|
|||||||
10
cmdlnopts.c
10
cmdlnopts.c
@@ -28,11 +28,10 @@
|
|||||||
* here are global parameters initialisation
|
* here are global parameters initialisation
|
||||||
*/
|
*/
|
||||||
static int help;
|
static int help;
|
||||||
static glob_pars G;
|
|
||||||
|
|
||||||
// DEFAULTS
|
// DEFAULTS
|
||||||
// default global parameters
|
// default global parameters
|
||||||
glob_pars const Gdefault = {
|
static glob_pars G = {
|
||||||
.speed = 9600,
|
.speed = 9600,
|
||||||
.eol = "n",
|
.eol = "n",
|
||||||
.tmoutms = 100,
|
.tmoutms = 100,
|
||||||
@@ -47,13 +46,13 @@ static sl_option_t cmdlnopts[] = {
|
|||||||
// set 1 to param despite of its repeating number:
|
// set 1 to param despite of its repeating number:
|
||||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
|
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
|
||||||
{"speed", NEED_ARG, NULL, 's', arg_int, APTR(&G.speed), _("baudrate (default: 9600)")},
|
{"speed", NEED_ARG, NULL, 's', arg_int, APTR(&G.speed), _("baudrate (default: 9600)")},
|
||||||
{"name", NEED_ARG, NULL, 'n', arg_string, APTR(&G.ttyname), _("serial device path or server name/IP or socket path")},
|
{"name", NEED_ARG, NULL, 'n', arg_string, APTR(&G.node), _("path to serial device, server name:IP or socket path")},
|
||||||
{"eol", NEED_ARG, NULL, 'e', arg_string, APTR(&G.eol), _("end of line: n (default), r, nr or rn")},
|
{"eol", NEED_ARG, NULL, 'e', arg_string, APTR(&G.eol), _("end of line: n (default), r, nr or rn")},
|
||||||
{"timeout", NEED_ARG, NULL, 't', arg_int, APTR(&G.tmoutms), _("timeout for select() in ms (default: 100)")},
|
{"timeout", NEED_ARG, NULL, 't', arg_int, APTR(&G.tmoutms), _("timeout for select() in ms (default: 100)")},
|
||||||
{"port", NEED_ARG, NULL, 'p', arg_string, APTR(&G.port), _("socket port (none for UNIX)")},
|
{"socket", NO_ARGS, NULL, 'S', arg_int, APTR(&G.socket), _("open INET socket")},
|
||||||
{"socket", NO_ARGS, NULL, 'S', arg_int, APTR(&G.socket), _("open socket")},
|
|
||||||
{"dumpfile",NEED_ARG, NULL, 'd', arg_string, APTR(&G.dumpfile), _("dump data to this file")},
|
{"dumpfile",NEED_ARG, NULL, 'd', arg_string, APTR(&G.dumpfile), _("dump data to this file")},
|
||||||
{"format", NEED_ARG, NULL, 'f', arg_string, APTR(&G.serformat), _("tty format (default: 8N1)")},
|
{"format", NEED_ARG, NULL, 'f', arg_string, APTR(&G.serformat), _("tty format (default: 8N1)")},
|
||||||
|
{"unix", NO_ARGS, NULL, 'U', arg_int, APTR(&G.unixsock), _("open UNIX-socket")},
|
||||||
{"exclusive",NO_ARGS, NULL, 'x', arg_int, APTR(&G.exclusive), _("open serial in exclusive mode")},
|
{"exclusive",NO_ARGS, NULL, 'x', arg_int, APTR(&G.exclusive), _("open serial in exclusive mode")},
|
||||||
end_option
|
end_option
|
||||||
};
|
};
|
||||||
@@ -66,7 +65,6 @@ static sl_option_t cmdlnopts[] = {
|
|||||||
* @return allocated structure with global parameters
|
* @return allocated structure with global parameters
|
||||||
*/
|
*/
|
||||||
glob_pars *parse_args(int argc, char **argv){
|
glob_pars *parse_args(int argc, char **argv){
|
||||||
void *ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr);
|
|
||||||
// format of help: "Usage: progname [args]\n"
|
// format of help: "Usage: progname [args]\n"
|
||||||
sl_helpstring(_(PROJECT " version " PACKAGE_VERSION "\nUsage: %s [args]\n\n\tWhere args are:\n"));
|
sl_helpstring(_(PROJECT " version " PACKAGE_VERSION "\nUsage: %s [args]\n\n\tWhere args are:\n"));
|
||||||
// parse arguments
|
// parse arguments
|
||||||
|
|||||||
13
cmdlnopts.h
13
cmdlnopts.h
@@ -17,27 +17,22 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef CMDLNOPTS_H__
|
|
||||||
#define CMDLNOPTS_H__
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* here are some typedef's for global data
|
* here are some typedef's for global data
|
||||||
*/
|
*/
|
||||||
typedef struct{
|
typedef struct{
|
||||||
int speed; // baudrate
|
int speed; // baudrate
|
||||||
int tmoutms; // timeout for select() in ms
|
|
||||||
int socket; // open socket
|
int socket; // open socket
|
||||||
|
int unixsock; // UNIX-socket instead of INET
|
||||||
int exclusive; // open serial in exclusive mode
|
int exclusive; // open serial in exclusive mode
|
||||||
|
double tmoutms; // timeout for select() in ms
|
||||||
char *dumpfile; // file to save dump
|
char *dumpfile; // file to save dump
|
||||||
char *ttyname; // device name
|
char *node; // node name / device path
|
||||||
char *eol; // end of line: \r (CR), \rn (CR+LF) or \n (LF): "r", "rn", "n"
|
char *eol; // end of line: \r (CR), \rn (CR+LF) or \n (LF): "r", "rn", "n"
|
||||||
char *port; // socket port
|
|
||||||
char *serformat; // format of serial line
|
char *serformat; // format of serial line
|
||||||
} glob_pars;
|
} glob_pars;
|
||||||
|
|
||||||
|
|
||||||
// default & global parameters
|
|
||||||
extern glob_pars const Gdefault;
|
|
||||||
|
|
||||||
glob_pars *parse_args(int argc, char **argv);
|
glob_pars *parse_args(int argc, char **argv);
|
||||||
#endif // CMDLNOPTS_H__
|
|
||||||
|
|||||||
7
dbg.h
7
dbg.h
@@ -17,8 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef DBG_H__
|
|
||||||
#define DBG_H__
|
|
||||||
|
|
||||||
// dirty trick
|
// dirty trick
|
||||||
#define termios xxtermios
|
#define termios xxtermios
|
||||||
@@ -35,10 +33,9 @@
|
|||||||
#define FNAME() do{LOGDBG("%s (%s, line %d)", __func__, __FILE__, __LINE__);}while(0)
|
#define FNAME() do{LOGDBG("%s (%s, line %d)", __func__, __FILE__, __LINE__);}while(0)
|
||||||
#define DBG(...) do{LOGDBG("%s (%s, line %d):", __func__, __FILE__, __LINE__); \
|
#define DBG(...) do{LOGDBG("%s (%s, line %d):", __func__, __FILE__, __LINE__); \
|
||||||
LOGDBGADD(__VA_ARGS__);} while(0)
|
LOGDBGADD(__VA_ARGS__);} while(0)
|
||||||
#define ERR(...) do{red(__VA_ARGS__); printf("\n"); LOGERR(__VA_ARGS__); signals(9);}while(0)
|
#define ERR(...) do{red(__VA_ARGS__); printf("\n"); LOGERR(__VA_ARGS__); signals(-1);}while(0)
|
||||||
#define ERRX(...) do{red(__VA_ARGS__); printf("\n"); LOGERR(__VA_ARGS__); signals(9);}while(0)
|
#define ERRX(...) do{red(__VA_ARGS__); printf("\n"); LOGERR(__VA_ARGS__); signals(-1);}while(0)
|
||||||
//#define WARN(...) do{LOGWARN(__VA_ARGS__);}while(0)
|
//#define WARN(...) do{LOGWARN(__VA_ARGS__);}while(0)
|
||||||
#define WARNX(...) do{red(__VA_ARGS__); printf("\n"); LOGWARN(__VA_ARGS__);}while(0)
|
#define WARNX(...) do{red(__VA_ARGS__); printf("\n"); LOGWARN(__VA_ARGS__);}while(0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // DBG_H__
|
|
||||||
|
|||||||
114
main.c
114
main.c
@@ -19,62 +19,85 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h> // strcmp
|
#include <string.h> // strcmp
|
||||||
|
#include <strings.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "cmdlnopts.h"
|
#include "cmdlnopts.h"
|
||||||
#include "ncurses_and_readline.h"
|
#include "ncurses_and_readline.h"
|
||||||
#include "ttysocket.h"
|
#include "ttysocket.h"
|
||||||
|
|
||||||
#include "dbg.h"
|
#include "dbg.h"
|
||||||
|
|
||||||
static chardevice conndev = {.dev = NULL, .mutex = PTHREAD_MUTEX_INITIALIZER, .name = NULL, .type = DEV_TTY};
|
static int should_exit = 0;
|
||||||
|
|
||||||
void signals(int signo){
|
void signals(int signo){
|
||||||
signal(signo, SIG_IGN);
|
DBG("signo = %d", signo);
|
||||||
closedev();
|
should_exit = 1;
|
||||||
deinit_ncurses();
|
if(signo > 0){
|
||||||
deinit_readline();
|
|
||||||
DBG("Exit by signal %d", signo);
|
DBG("Exit by signal %d", signo);
|
||||||
exit(signo);
|
}else if(signo < 0){
|
||||||
|
should_exit = -1;
|
||||||
|
DBG("exit by disconnection");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv){
|
int main(int argc, char **argv){
|
||||||
|
chardevice *conndev = MALLOC(chardevice, 1); // should be allocated to FREE later
|
||||||
|
pthread_mutex_init(&conndev->mutex, NULL);
|
||||||
|
conndev->type = DEV_TTY;
|
||||||
glob_pars *G = NULL; // default parameters see in cmdlnopts.c
|
glob_pars *G = NULL; // default parameters see in cmdlnopts.c
|
||||||
sl_init();
|
sl_init();
|
||||||
#ifdef EBUG
|
#ifdef EBUG
|
||||||
OPENLOG("debug.log", LOGLEVEL_ANY, 1);
|
OPENLOG("debug.log", LOGLEVEL_ANY, 1);
|
||||||
#endif
|
#endif
|
||||||
|
DBG("Parsing");
|
||||||
G = parse_args(argc, argv);
|
G = parse_args(argc, argv);
|
||||||
if(G->tmoutms < 0) ERRX("Timeout should be >= 0");
|
if(G->tmoutms < 0.){
|
||||||
|
ERRX("Timeout should be >= 0");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(sl_tty_tmout(G->tmoutms) < 0){
|
||||||
|
ERRX("Can't set timeout to %g ms", G->tmoutms);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(!G->node){
|
||||||
|
ERRX("You should point node name");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
const char *EOL = "\n", *seol = "\\n";
|
const char *EOL = "\n", *seol = "\\n";
|
||||||
if(strcasecmp(G->eol, "n")){
|
if(strcasecmp(G->eol, "n")){
|
||||||
if(strcasecmp(G->eol, "r") == 0){ EOL = "\r"; seol = "\\r"; }
|
if(strcasecmp(G->eol, "r") == 0){ EOL = "\r"; seol = "\\r"; }
|
||||||
else if(strcasecmp(G->eol, "rn") == 0){ EOL = "\r\n"; seol = "\\r\\n"; }
|
else if(strcasecmp(G->eol, "rn") == 0){ EOL = "\r\n"; seol = "\\r\\n"; }
|
||||||
else if(strcasecmp(G->eol, "nr") == 0){ EOL = "\n\r"; seol = "\\n\\r"; }
|
else if(strcasecmp(G->eol, "nr") == 0){ EOL = "\n\r"; seol = "\\n\\r"; }
|
||||||
else ERRX("End of line should be \"r\", \"n\" or \"rn\" or \"nr\"");
|
|
||||||
}
|
|
||||||
strcpy(conndev.eol, EOL);
|
|
||||||
strcpy(conndev.seol, seol);
|
|
||||||
DBG("eol: %s, seol: %s", conndev.eol, conndev.seol);
|
|
||||||
if(!G->ttyname){
|
|
||||||
WARNX("You should point name");
|
|
||||||
signals(0);
|
|
||||||
}
|
|
||||||
conndev.name = strdup(G->ttyname);
|
|
||||||
DBG("device name: %s", conndev.name);
|
|
||||||
if(G->socket){
|
|
||||||
if(!G->port) conndev.type = DEV_UNIXSOCKET;
|
|
||||||
else{
|
else{
|
||||||
conndev.port = strdup(G->port);
|
ERRX("End of line should be \"r\", \"n\", \"rn\" or \"nr\"");
|
||||||
conndev.type = DEV_NETSOCKET;
|
return 1;
|
||||||
}
|
}
|
||||||
DBG("socket port=%s, type=%d", conndev.port, conndev.type);
|
|
||||||
}else{
|
|
||||||
conndev.speed = G->speed;
|
|
||||||
conndev.port = strdup(G->serformat); // `port` of tty is serial format
|
|
||||||
DBG("speed=%d, format=%s", conndev.speed, conndev.port);
|
|
||||||
conndev.exclusive = G->exclusive;
|
|
||||||
}
|
}
|
||||||
if(!opendev(&conndev, G->dumpfile)){
|
strcpy(conndev->eol, EOL);
|
||||||
signals(0);
|
strcpy(conndev->seol, seol);
|
||||||
|
DBG("eol: %s, seol: %s", conndev->eol, conndev->seol);
|
||||||
|
conndev->node = strdup(G->node);
|
||||||
|
if(!conndev->node){
|
||||||
|
ERR("strdup()");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
DBG("node: %s", conndev->node);
|
||||||
|
if(G->socket || G->unixsock){ // INET/UNIX socket
|
||||||
|
if(G->unixsock) conndev->type = DEV_UNIXSOCKET;
|
||||||
|
else conndev->type = DEV_NETSOCKET;
|
||||||
|
}else{ // serial device
|
||||||
|
conndev->speed = G->speed;
|
||||||
|
conndev->serformat = strdup(G->serformat); // `port` of tty is serial format
|
||||||
|
if(!conndev->serformat){
|
||||||
|
ERR("strdup()");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
DBG("speed=%d, format=%s", conndev->speed, conndev->serformat);
|
||||||
|
conndev->exclusive = G->exclusive;
|
||||||
|
}
|
||||||
|
if(!opendev(conndev, G->dumpfile)){
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
init_ncurses();
|
init_ncurses();
|
||||||
init_readline();
|
init_readline();
|
||||||
@@ -84,23 +107,26 @@ int main(int argc, char **argv){
|
|||||||
signal(SIGQUIT, signals); // ctrl+\ - quit
|
signal(SIGQUIT, signals); // ctrl+\ - quit
|
||||||
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
|
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
|
||||||
pthread_t writer;
|
pthread_t writer;
|
||||||
if(pthread_create(&writer, NULL, cmdline, (void*)&conndev)) ERR("pthread_create()");
|
if(pthread_create(&writer, NULL, cmdline, (void*)conndev)) ERR("pthread_create()");
|
||||||
settimeout(G->tmoutms);
|
uint8_t buf[BUFSIZ];
|
||||||
while(1){
|
while(conndev && conndev->fd > -1 && !should_exit){
|
||||||
if(0 == pthread_mutex_lock(&conndev.mutex)){
|
ssize_t got = ReadData(buf, BUFSIZ);
|
||||||
int l;
|
if(got > 0){
|
||||||
uint8_t *buf = ReadData(&l);
|
AddData(buf, got);
|
||||||
if(buf && l > 0){
|
}else if(got < 0){
|
||||||
AddData(buf, l);
|
WARNX("Device disconnected -> exit");
|
||||||
}else if(l < 0){
|
signals(-1);
|
||||||
pthread_mutex_unlock(&conndev.mutex);
|
|
||||||
ERRX("Device disconnected");
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&conndev.mutex);
|
|
||||||
}
|
}
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
}
|
}
|
||||||
// never reached
|
DBG("Exit writer");
|
||||||
|
exit_writer();
|
||||||
|
usleep(1000);
|
||||||
|
deinit_ncurses();
|
||||||
|
deinit_readline();
|
||||||
|
closedev();
|
||||||
|
if(should_exit < 0) red("\nExit with error (disconnected?)\n\n");
|
||||||
|
else DBG("main(): Normal exit");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,11 +25,10 @@
|
|||||||
#include <curses.h>
|
#include <curses.h>
|
||||||
#include <readline/history.h>
|
#include <readline/history.h>
|
||||||
#include <readline/readline.h>
|
#include <readline/readline.h>
|
||||||
#include <stdbool.h>
|
#include <stdatomic.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
//#include <signal.h>
|
//#include <signal.h>
|
||||||
|
|
||||||
#include "dbg.h"
|
#include "dbg.h"
|
||||||
@@ -47,18 +46,19 @@ enum { // using colors
|
|||||||
};
|
};
|
||||||
#define COLOR(x) COLOR_PAIR(x ## _NO)
|
#define COLOR(x) COLOR_PAIR(x ## _NO)
|
||||||
|
|
||||||
|
|
||||||
// Keeps track of the terminal mode so we can reset the terminal if needed on errors
|
// Keeps track of the terminal mode so we can reset the terminal if needed on errors
|
||||||
static bool visual_mode = false;
|
static atomic_bool visual_mode = false;
|
||||||
// insert commands when true; roll upper screen when false
|
// insert commands when true; roll upper screen when false
|
||||||
static bool insert_mode = true;
|
static atomic_bool insert_mode = true;
|
||||||
static bool should_exit = false;
|
static atomic_bool should_exit = false;
|
||||||
|
|
||||||
static disptype disp_type = DISP_TEXT; // type of displaying data
|
static disptype disp_type = DISP_TEXT; // type of displaying data
|
||||||
static disptype input_type = DISP_TEXT; // parsing type of input data
|
static disptype input_type = DISP_TEXT; // parsing type of input data
|
||||||
const char *dispnames[DISP_SIZE] = {"TEXT", "RAW", "HEX", "RTU (RAW)", "RTU (HEX)", "Error"};
|
const char *dispnames[DISP_SIZE] = {"TEXT", "RAW", "HEX", "RTU (RAW)", "RTU (HEX)", "Error"};
|
||||||
|
|
||||||
static chardevice *dtty = NULL;
|
static chardevice *dtty = NULL;
|
||||||
|
// ring buffer for new data
|
||||||
|
static sl_ringbuffer_t *incoming_data = NULL;
|
||||||
|
|
||||||
static void fail_exit(const char *msg){
|
static void fail_exit(const char *msg){
|
||||||
// Make sure endwin() is only called in visual mode. As a note, calling it
|
// Make sure endwin() is only called in visual mode. As a note, calling it
|
||||||
@@ -104,6 +104,10 @@ static unsigned char input; // Input character for readline
|
|||||||
// Used to signal "no more input" after feeding a character to readline
|
// Used to signal "no more input" after feeding a character to readline
|
||||||
static bool input_avail = false;
|
static bool input_avail = false;
|
||||||
|
|
||||||
|
void exit_writer(){
|
||||||
|
should_exit = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Not bothering with 'input_avail' and just returning 0 here seems to do the
|
// Not bothering with 'input_avail' and just returning 0 here seems to do the
|
||||||
// right thing too, but this might be safer across readline versions
|
// right thing too, but this might be safer across readline versions
|
||||||
static int readline_input_avail(){
|
static int readline_input_avail(){
|
||||||
@@ -134,7 +138,7 @@ static void show_err(const char *text){
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief ptrtobuf - get n'th string of `formatted_buffer`
|
* @brief ptrtobuf - get n'th string of `formatted_buffer`; called when mutex locked
|
||||||
* @param lineno - line number
|
* @param lineno - line number
|
||||||
* @return pointer to n'th string in formatted buffer
|
* @return pointer to n'th string in formatted buffer
|
||||||
*/
|
*/
|
||||||
@@ -149,35 +153,10 @@ static char *ptrtobuf(size_t lineno){
|
|||||||
return (linebuffer->formatted_buffer + idx);
|
return (linebuffer->formatted_buffer + idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
// functions to modify output data
|
|
||||||
static char *text_putchar(char *next){
|
|
||||||
char c = *next++;
|
|
||||||
DBG("put 0x%02X (%c)", c, c);
|
|
||||||
if(c < 31 || c > 126){
|
|
||||||
wattron(msg_win, COLOR(MARKED));
|
|
||||||
//waddch(msg_win, c);
|
|
||||||
wprintw(msg_win, "%02X", (uint8_t)c);
|
|
||||||
wattroff(msg_win, COLOR(MARKED));
|
|
||||||
}else{
|
|
||||||
//wprintw(msg_win, "%c" COLOR_GREEN "green" COLOR_RED "red" COLOR_OLD, c);
|
|
||||||
waddch(msg_win, c);
|
|
||||||
}
|
|
||||||
return next;
|
|
||||||
}
|
|
||||||
static char *raw_putchar(char *next){
|
|
||||||
waddch(msg_win, *next);
|
|
||||||
return next+1;
|
|
||||||
}
|
|
||||||
static char *hex_putchar(char *next){
|
|
||||||
waddch(msg_win, *next);
|
|
||||||
return next+1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief msg_win_redisplay - redisplay message window
|
* @brief msg_win_redisplay - redisplay message window
|
||||||
* @param group_refresh - true for grouping refresh (don't call doupdate())
|
* @param group_refresh - true for grouping refresh (don't call doupdate())
|
||||||
|
* // called with mutex locked
|
||||||
*/
|
*/
|
||||||
static void msg_win_redisplay(bool group_refresh){
|
static void msg_win_redisplay(bool group_refresh){
|
||||||
if(!linebuffer) return;
|
if(!linebuffer) return;
|
||||||
@@ -237,20 +216,20 @@ static void show_mode(bool group_refresh){
|
|||||||
if(dtty){
|
if(dtty){
|
||||||
switch(dtty->type){
|
switch(dtty->type){
|
||||||
case DEV_NETSOCKET:
|
case DEV_NETSOCKET:
|
||||||
snprintf(buf, 127, "%s HOST: %s, ENDLINE: %s, PORT: %s",
|
snprintf(buf, 127, "%s NODE: %s, ENDLINE: %s",
|
||||||
insmodetext, dtty->name, dtty->seol, dtty->port);
|
insmodetext, dtty->node, dtty->seol);
|
||||||
break;
|
break;
|
||||||
case DEV_UNIXSOCKET:
|
case DEV_UNIXSOCKET:
|
||||||
if(*dtty->name)
|
if(*dtty->node)
|
||||||
snprintf(buf, 127, "%s PATH: %s, ENDLINE: %s",
|
snprintf(buf, 127, "%s PATH: %s, ENDLINE: %s",
|
||||||
insmodetext, dtty->name, dtty->seol);
|
insmodetext, dtty->node, dtty->seol);
|
||||||
else // name starting from \0
|
else // name starting from \0
|
||||||
snprintf(buf, 127, "%s PATH: \\0%s, ENDLINE: %s",
|
snprintf(buf, 127, "%s PATH: @%s, ENDLINE: %s",
|
||||||
insmodetext, dtty->name+1, dtty->seol);
|
insmodetext, dtty->node+1, dtty->seol);
|
||||||
break;
|
break;
|
||||||
case DEV_TTY:
|
case DEV_TTY:
|
||||||
snprintf(buf, 127, "%s DEV: %s, ENDLINE: %s, SPEED: %d, FORMAT: %s",
|
snprintf(buf, 127, "%s DEV: %s, ENDLINE: %s, SPEED: %d, FORMAT: %s",
|
||||||
insmodetext, dtty->name, dtty->seol, dtty->speed, dtty->port);
|
insmodetext, dtty->node, dtty->seol, dtty->speed, dtty->serformat ? dtty->serformat : "8N1");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -258,7 +237,7 @@ static void show_mode(bool group_refresh){
|
|||||||
snprintf(buf, 127, "INSERT (TAB to switch, ctrl+D to quit) NOT INITIALIZED");
|
snprintf(buf, 127, "INSERT (TAB to switch, ctrl+D to quit) NOT INITIALIZED");
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
snprintf(buf, 127, "SCROLL (F1 - help) ENDLINE: %s", dtty?dtty->seol:"n");
|
snprintf(buf, 127, "SCROLL (F1 - help) ENDLINE: %s", dtty ? dtty->seol : "n");
|
||||||
}
|
}
|
||||||
wattron(sep_win, COLOR(BKGMARKED));
|
wattron(sep_win, COLOR(BKGMARKED));
|
||||||
wprintw(sep_win, "%s ", dispnames[disp_type]);
|
wprintw(sep_win, "%s ", dispnames[disp_type]);
|
||||||
@@ -272,6 +251,7 @@ static void show_mode(bool group_refresh){
|
|||||||
/**
|
/**
|
||||||
* @brief redisplay_addline - redisplay after line adding
|
* @brief redisplay_addline - redisplay after line adding
|
||||||
* @param group_refresh - true for grouping refresh (don't call doupdate())
|
* @param group_refresh - true for grouping refresh (don't call doupdate())
|
||||||
|
* called with mutex locked
|
||||||
*/
|
*/
|
||||||
static void redisplay_addline(){
|
static void redisplay_addline(){
|
||||||
// redisplay only if previous line was on screen
|
// redisplay only if previous line was on screen
|
||||||
@@ -299,6 +279,7 @@ static void linebuf_free(){
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief chksizes - check sizes of buffers and enlarge them if need
|
* @brief chksizes - check sizes of buffers and enlarge them if need
|
||||||
|
* called with locked mutex
|
||||||
*/
|
*/
|
||||||
static void chksizes(){
|
static void chksizes(){
|
||||||
size_t addportion = MAXCOLS * LINEARRSZ;
|
size_t addportion = MAXCOLS * LINEARRSZ;
|
||||||
@@ -357,6 +338,7 @@ static void linebuf_new(){
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief finalize_line - finalize last line in linebuffer & increase buffer sizes if nesessary
|
* @brief finalize_line - finalize last line in linebuffer & increase buffer sizes if nesessary
|
||||||
|
* // called when mutex locked
|
||||||
*/
|
*/
|
||||||
static void finalize_line(){
|
static void finalize_line(){
|
||||||
chksizes();
|
chksizes();
|
||||||
@@ -476,18 +458,15 @@ void FormatData(const uint8_t *data, int len){
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief AddData - add new data buffer to global buffer and last displayed string
|
* @brief AddData - add new data to temporary ring buffer
|
||||||
* @param data - data
|
* @param data - data
|
||||||
* @param len - length of `data`
|
* @param len - length of `data`
|
||||||
*/
|
*/
|
||||||
void AddData(const uint8_t *data, int len){
|
void AddData(const uint8_t *data, int len){
|
||||||
// now print all symbols into buff
|
while(len > 0){
|
||||||
chksizes();
|
size_t written = sl_RB_write(incoming_data, data, len);
|
||||||
memcpy(raw_buffer + rawbufcur, data, len);
|
len -= written;
|
||||||
DBG("Got %d bytes, now buffer have %d", len, rawbufcur+len);
|
}
|
||||||
FormatData(raw_buffer + rawbufcur, len);
|
|
||||||
rawbufcur += len;
|
|
||||||
redisplay_addline(); // display last symbols if can
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void resize(){
|
static void resize(){
|
||||||
@@ -499,20 +478,18 @@ static void resize(){
|
|||||||
mvwin(sep_win, LINES - 2, 0);
|
mvwin(sep_win, LINES - 2, 0);
|
||||||
mvwin(cmd_win, LINES - 1, 0);
|
mvwin(cmd_win, LINES - 1, 0);
|
||||||
}
|
}
|
||||||
pthread_mutex_lock(&dtty->mutex);
|
|
||||||
linebuf_new(); // free old and alloc new
|
linebuf_new(); // free old and alloc new
|
||||||
FormatData(raw_buffer, rawbufcur); // reformat all data
|
FormatData(raw_buffer, rawbufcur); // reformat all data
|
||||||
pthread_mutex_unlock(&dtty->mutex);
|
|
||||||
msg_win_redisplay(true);
|
msg_win_redisplay(true);
|
||||||
show_mode(true);
|
show_mode(true);
|
||||||
doupdate();
|
doupdate();
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
void swinch(_U_ int sig){
|
void swinch(_U_ int sig){
|
||||||
//signal(SIGWINCH, swinch);
|
signal(SIGWINCH, swinch);
|
||||||
DBG("got resize");
|
DBG("got resize");
|
||||||
}*/
|
}
|
||||||
|
*/
|
||||||
void init_ncurses(){
|
void init_ncurses(){
|
||||||
if (!initscr())
|
if (!initscr())
|
||||||
fail_exit("Failed to initialize ncurses");
|
fail_exit("Failed to initialize ncurses");
|
||||||
@@ -556,16 +533,20 @@ void init_ncurses(){
|
|||||||
rawbufsz = RBUFSIZ;
|
rawbufsz = RBUFSIZ;
|
||||||
raw_buffer = MALLOC(uint8_t, rawbufsz);
|
raw_buffer = MALLOC(uint8_t, rawbufsz);
|
||||||
linebuf_new();
|
linebuf_new();
|
||||||
|
if(!incoming_data) incoming_data = sl_RB_new(BUFSIZ);
|
||||||
|
if(!incoming_data) ERRX("Can't create input ring buffer");
|
||||||
//signal(SIGWINCH, swinch);
|
//signal(SIGWINCH, swinch);
|
||||||
}
|
}
|
||||||
|
|
||||||
void deinit_ncurses(){
|
void deinit_ncurses(){
|
||||||
|
FNAME();
|
||||||
visual_mode = false;
|
visual_mode = false;
|
||||||
linebuf_free();
|
|
||||||
delwin(msg_win);
|
delwin(msg_win);
|
||||||
delwin(sep_win);
|
delwin(sep_win);
|
||||||
delwin(cmd_win);
|
delwin(cmd_win);
|
||||||
endwin();
|
endwin();
|
||||||
|
linebuf_free();
|
||||||
|
sl_RB_delete(&incoming_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *previous_line = NULL; // previous line in readline input
|
static char *previous_line = NULL; // previous line in readline input
|
||||||
@@ -613,6 +594,7 @@ static void change_disp(disptype in, disptype out){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void deinit_readline(){
|
void deinit_readline(){
|
||||||
|
FNAME();
|
||||||
rl_callback_handler_remove();
|
rl_callback_handler_remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -682,9 +664,20 @@ static const char *help[] = {
|
|||||||
*/
|
*/
|
||||||
void *cmdline(void* arg){
|
void *cmdline(void* arg){
|
||||||
MEVENT event;
|
MEVENT event;
|
||||||
|
uint8_t locbuff[MAXCOLS];
|
||||||
dtty = (chardevice*)arg;
|
dtty = (chardevice*)arg;
|
||||||
show_mode(false);
|
show_mode(false);
|
||||||
do{
|
do{
|
||||||
|
// check for incoming data
|
||||||
|
size_t got = sl_RB_read(incoming_data, locbuff, MAXCOLS);
|
||||||
|
if(got){
|
||||||
|
chksizes();
|
||||||
|
memcpy(raw_buffer + rawbufcur, locbuff, got);
|
||||||
|
DBG("Got %zd bytes, now buffer have %d", got, rawbufcur+got);
|
||||||
|
FormatData(raw_buffer + rawbufcur, got);
|
||||||
|
rawbufcur += got;
|
||||||
|
redisplay_addline(); // display last symbols if can
|
||||||
|
}
|
||||||
int c = wgetch(cmd_win);
|
int c = wgetch(cmd_win);
|
||||||
if(c < 0) continue;
|
if(c < 0) continue;
|
||||||
bool processed = true;
|
bool processed = true;
|
||||||
@@ -808,6 +801,7 @@ void *cmdline(void* arg){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}while(!should_exit);
|
}while(!should_exit);
|
||||||
|
DBG("should_exit == 0");
|
||||||
signals(0);
|
signals(0);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef NCURSES_AND_READLINE_H__
|
|
||||||
#define NCURSES_AND_READLINE_H__
|
|
||||||
|
|
||||||
#include "dbg.h"
|
#include "dbg.h"
|
||||||
#include "ttysocket.h"
|
#include "ttysocket.h"
|
||||||
@@ -34,9 +32,9 @@ typedef enum{ // display/input data as
|
|||||||
|
|
||||||
void init_readline();
|
void init_readline();
|
||||||
void deinit_readline();
|
void deinit_readline();
|
||||||
|
void exit_writer();
|
||||||
void init_ncurses();
|
void init_ncurses();
|
||||||
void deinit_ncurses();
|
void deinit_ncurses();
|
||||||
void *cmdline(void* arg);
|
void *cmdline(void* arg);
|
||||||
void AddData(const uint8_t *data, int len);
|
void AddData(const uint8_t *data, int len);
|
||||||
|
|
||||||
#endif // NCURSES_AND_READLINE_H__
|
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ static inline const char *gethex(const char *line, int *ch){
|
|||||||
static inline const char *getspec(const char *line, int *ch){
|
static inline const char *getspec(const char *line, int *ch){
|
||||||
if(!*line){ *ch = -1; return line; }
|
if(!*line){ *ch = -1; return line; }
|
||||||
int got = -1, s = *line++; // next symbol after '\'
|
int got = -1, s = *line++; // next symbol after '\'
|
||||||
if(s >= '0' && s <= '7'){ // octal symbol
|
if(s >= '0' && s <= '7'){ // octal symbol like \127
|
||||||
line = getoct(line-1, &got);
|
line = getoct(line-1, &got);
|
||||||
}else switch(s){
|
}else switch(s){
|
||||||
case 'a': got = '\a'; break;
|
case 'a': got = '\a'; break;
|
||||||
@@ -186,6 +186,7 @@ int convert_and_send(disptype input_type, const char *line){
|
|||||||
if(curpos + sz >= bufsiz){ // out ouptut buffer can't be larger than input
|
if(curpos + sz >= bufsiz){ // out ouptut buffer can't be larger than input
|
||||||
bufsiz += BUFSIZ;
|
bufsiz += BUFSIZ;
|
||||||
buf = realloc(buf, bufsiz);
|
buf = realloc(buf, bufsiz);
|
||||||
|
if(!buf) ERR("realloc()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while(*line){
|
while(*line){
|
||||||
|
|||||||
357
ttysocket.c
357
ttysocket.c
@@ -16,6 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if 0
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
@@ -27,297 +28,128 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/un.h> // unix socket
|
#include <sys/un.h> // unix socket
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <usefull_macros.h>
|
||||||
|
|
||||||
#include "dbg.h"
|
#include "dbg.h"
|
||||||
#include "string_functions.h"
|
#include "string_functions.h"
|
||||||
#include "ttysocket.h"
|
#include "ttysocket.h"
|
||||||
|
|
||||||
static int sec = 0, usec = 100; // timeout
|
|
||||||
static FILE *dupfile = NULL; // file for output
|
static FILE *dupfile = NULL; // file for output
|
||||||
static chardevice *device = NULL; // current opened device
|
static chardevice *device = NULL; // current opened device
|
||||||
|
|
||||||
// TODO: if unix socket name starts with \0 translate it as \\0 to d->name!
|
|
||||||
|
|
||||||
// set Read_tty timeout in milliseconds
|
|
||||||
void settimeout(int tmout){
|
|
||||||
sec = 0;
|
|
||||||
if(tmout > 999){
|
|
||||||
sec = tmout / 1000;
|
|
||||||
tmout -= sec * 1000;
|
|
||||||
}
|
|
||||||
usec = tmout * 1000L;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* wait for answer from socket
|
|
||||||
* @param sock - socket fd
|
|
||||||
* @return 0 in case of timeout, 1 in case of socket ready, -1 if error
|
|
||||||
*/
|
|
||||||
static int waittoread(int fd){
|
|
||||||
fd_set fds;
|
|
||||||
struct timeval timeout;
|
|
||||||
timeout.tv_sec = sec;
|
|
||||||
timeout.tv_usec = usec;
|
|
||||||
FD_ZERO(&fds);
|
|
||||||
FD_SET(fd, &fds);
|
|
||||||
do{
|
|
||||||
int rc = select(fd+1, &fds, NULL, NULL, &timeout);
|
|
||||||
if(rc < 0){
|
|
||||||
if(errno != EINTR){
|
|
||||||
WARN("select()");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}while(1);
|
|
||||||
if(FD_ISSET(fd, &fds)){
|
|
||||||
//DBG("FD_ISSET");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get data drom TTY
|
|
||||||
static uint8_t *getttydata(int *len){
|
|
||||||
if(!device || !device->dev) return NULL;
|
|
||||||
sl_tty_t *D = device->dev;
|
|
||||||
if(D->comfd < 0) return NULL;
|
|
||||||
int L = 0;
|
|
||||||
int length = D->bufsz - 1; // -1 for terminating zero
|
|
||||||
uint8_t *ptr = (uint8_t*)D->buf;
|
|
||||||
int s = 0;
|
|
||||||
do{
|
|
||||||
if(!(s = waittoread(D->comfd))) break;
|
|
||||||
if(s < 0){
|
|
||||||
if(len) *len = 0;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
int l = read(D->comfd, ptr, length);
|
|
||||||
if(l < 1){ // disconnected
|
|
||||||
if(len) *len = -1;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
ptr += l; L += l;
|
|
||||||
length -= l;
|
|
||||||
}while(length);
|
|
||||||
D->buflen = L;
|
|
||||||
D->buf[L] = 0; // for text buffers
|
|
||||||
if(len) *len = L;
|
|
||||||
if(!L) return NULL;
|
|
||||||
DBG("buffer len: %zd, content: =%s=", D->buflen, D->buf);
|
|
||||||
return (uint8_t*)D->buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t *getsockdata(int *len){
|
|
||||||
if(!device || !device->dev) return NULL;
|
|
||||||
sl_tty_t *D = device->dev;
|
|
||||||
if(D->comfd < 0) return NULL;
|
|
||||||
uint8_t *ptr = NULL;
|
|
||||||
int n = waittoread(D->comfd);
|
|
||||||
if(n == 1){
|
|
||||||
n = read(D->comfd, D->buf, D->bufsz-1);
|
|
||||||
if(n > 0){
|
|
||||||
ptr = (uint8_t*)D->buf;
|
|
||||||
ptr[n] = 0;
|
|
||||||
D->buflen = n;
|
|
||||||
DBG("got %d: ..%s..", n, ptr);
|
|
||||||
}else{
|
|
||||||
DBG("Got nothing");
|
|
||||||
n = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(len) *len = n;
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief ReadData - get data from serial device or socket
|
* @brief ReadData - get data from serial device or socket
|
||||||
* @param d - device
|
* @param buf - buffer
|
||||||
* @param len (o) - length of data read (-1 if device disconnected)
|
* @param len - buffer length
|
||||||
* @return NULL or string
|
* @return amount of read bytes or -1 in case of error / disconnection
|
||||||
*/
|
*/
|
||||||
uint8_t *ReadData(int *len){
|
ssize_t ReadData(uint8_t *buf, size_t len){
|
||||||
if(!device || !device->dev) return NULL;
|
if(!device || device->fd < 0){
|
||||||
if(len) *len = -1;
|
WARNX("ReadData(): No connection");
|
||||||
uint8_t *r = NULL;
|
return -1;
|
||||||
switch(device->type){
|
|
||||||
case DEV_TTY:
|
|
||||||
r = getttydata(len);
|
|
||||||
break;
|
|
||||||
case DEV_NETSOCKET:
|
|
||||||
case DEV_UNIXSOCKET:
|
|
||||||
r = getsockdata(len);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if(r && dupfile){
|
if(!buf || len == 0){
|
||||||
|
WARNX("ReadData(): Bad receive buffer");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ssize_t l = -1;
|
||||||
|
static int errctr = 0;
|
||||||
|
if(0 == pthread_mutex_lock(&device->mutex)){
|
||||||
|
int s = sl_canread(device->fd);
|
||||||
|
if(s < 1){
|
||||||
|
pthread_mutex_unlock(&device->mutex);
|
||||||
|
return (ssize_t)s;
|
||||||
|
}
|
||||||
|
l = read(device->fd, buf, len);
|
||||||
|
if(l > 0){
|
||||||
|
DBG("receive: %zd", l);
|
||||||
|
if(dupfile){
|
||||||
fwrite("< ", 1, 2, dupfile);
|
fwrite("< ", 1, 2, dupfile);
|
||||||
fwrite(r, 1, *len, dupfile);
|
fwrite(buf, 1, l, dupfile);
|
||||||
}
|
}
|
||||||
return r;
|
errctr = 0;
|
||||||
|
}else{
|
||||||
|
DBG("Disconnected? Got %zd bytes", l);
|
||||||
|
if(++errctr > 3){
|
||||||
|
l = -1;
|
||||||
|
errctr = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&device->mutex);
|
||||||
|
}else WARN("ReadData(): pthread_mutex_lock()");
|
||||||
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief SendData - send data to tty or socket
|
* @brief SendData - send data to tty or socket
|
||||||
* @param d - device
|
* @param d - device
|
||||||
* @param data - buffer with data
|
* @param data - buffer with data
|
||||||
* @return 0 if error or empty string, -1 if disconnected
|
* @return 0 if error or empty string, -1 if disconnected or error
|
||||||
*/
|
*/
|
||||||
int SendData(const uint8_t *data, size_t len){
|
ssize_t SendData(const uint8_t *data, size_t len){
|
||||||
if(!device) return -1;
|
if(!device || device->fd < 0){
|
||||||
if(!data || len == 0) return 0;
|
WARNX("SendData(): No connection");
|
||||||
int ret = 0;
|
return -1;
|
||||||
DBG("Send %d bytes", len);
|
|
||||||
if(0 == pthread_mutex_lock(&device->mutex)){
|
|
||||||
switch(device->type){
|
|
||||||
case DEV_TTY:
|
|
||||||
if(sl_tty_write(device->dev->comfd, (const char*)data, len)) ret = 0;
|
|
||||||
else ret = len;
|
|
||||||
break;
|
|
||||||
case DEV_NETSOCKET:
|
|
||||||
case DEV_UNIXSOCKET:
|
|
||||||
if(len != (size_t)send(device->dev->comfd, data, len, MSG_NOSIGNAL)) ret = 0;
|
|
||||||
else ret = len;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
data = NULL;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if(!data || len == 0){
|
||||||
|
WARNX("SendData(): Bad data to send");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ssize_t ret = -1;
|
||||||
|
DBG("Send %d bytes", len);
|
||||||
|
DBG("lock");
|
||||||
|
if(0 == pthread_mutex_lock(&device->mutex)){
|
||||||
|
DBG("write");
|
||||||
|
ret = write(device->fd, data, len);
|
||||||
if(data && dupfile){
|
if(data && dupfile){
|
||||||
fwrite("> ", 1, 2, dupfile);
|
fwrite("> ", 1, 2, dupfile);
|
||||||
fwrite(data, 1, len, dupfile);
|
fwrite(data, 1, len, dupfile);
|
||||||
}
|
}
|
||||||
|
DBG("unlock");
|
||||||
pthread_mutex_unlock(&device->mutex);
|
pthread_mutex_unlock(&device->mutex);
|
||||||
}else ret = -1;
|
}else WARN("SendData(): pthread_mutex_lock()");
|
||||||
DBG("ret=%d", ret);
|
DBG("send: %zd", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const int socktypes[] = {SOCK_STREAM, SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET, SOCK_DCCP, SOCK_PACKET, SOCK_DGRAM, 0};
|
|
||||||
|
|
||||||
static sl_tty_t* opensocket(){
|
|
||||||
if(!device) return FALSE;
|
|
||||||
sl_tty_t *descr = MALLOC(sl_tty_t, 1); // only for `buf` and bufsz/buflen
|
|
||||||
descr->buf = MALLOC(char, BUFSIZ);
|
|
||||||
descr->bufsz = BUFSIZ;
|
|
||||||
// now try to open a socket
|
|
||||||
descr->comfd = -1;
|
|
||||||
struct hostent *host;
|
|
||||||
struct sockaddr_in addr = {0};
|
|
||||||
struct sockaddr_un saddr = {0};
|
|
||||||
struct sockaddr *sa = NULL;
|
|
||||||
socklen_t addrlen = 0;
|
|
||||||
int domain = -1;
|
|
||||||
if(device->type == DEV_NETSOCKET){
|
|
||||||
DBG("NETSOCK to %s", device->name);
|
|
||||||
sa = (struct sockaddr*) &addr;
|
|
||||||
addrlen = sizeof(addr);
|
|
||||||
if((host = gethostbyname(device->name)) == NULL ){
|
|
||||||
WARN("gethostbyname()");
|
|
||||||
FREE(descr->buf);
|
|
||||||
FREE(descr);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
struct in_addr *ia = (struct in_addr*)host->h_addr_list[0];
|
|
||||||
DBG("addr: %s", inet_ntoa(*ia));
|
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
int p = atoi(device->port); DBG("PORT: %s - %d", device->port, p);
|
|
||||||
addr.sin_port = htons(p);
|
|
||||||
//addr.sin_addr.s_addr = *(long*)(host->h_addr);
|
|
||||||
addr.sin_addr.s_addr = ia->s_addr;
|
|
||||||
domain = AF_INET;
|
|
||||||
}else{
|
|
||||||
DBG("UNSOCK");
|
|
||||||
sa = (struct sockaddr*) &saddr;
|
|
||||||
addrlen = sizeof(saddr);
|
|
||||||
saddr.sun_family = AF_UNIX;
|
|
||||||
if(*(device->name) == 0){ // if sun_path[0] == 0 then don't create a file
|
|
||||||
DBG("convert name");
|
|
||||||
saddr.sun_path[0] = 0;
|
|
||||||
strncpy(saddr.sun_path+1, device->name+1, 105);
|
|
||||||
}
|
|
||||||
else if(strncmp("\\0", device->name, 2) == 0){
|
|
||||||
DBG("convert name");
|
|
||||||
saddr.sun_path[0] = 0;
|
|
||||||
strncpy(saddr.sun_path+1, device->name+2, 105);
|
|
||||||
}else strncpy(saddr.sun_path, device->name, 106);
|
|
||||||
domain = AF_UNIX;
|
|
||||||
}
|
|
||||||
const int *type = socktypes;
|
|
||||||
while(*type){
|
|
||||||
DBG("type = %d", *type);
|
|
||||||
if((descr->comfd = socket(domain, *type, 0)) > -1){
|
|
||||||
if(connect(descr->comfd, sa, addrlen) < 0){
|
|
||||||
DBG("CANT connect");
|
|
||||||
close(descr->comfd);
|
|
||||||
}else break;
|
|
||||||
}
|
|
||||||
++type;
|
|
||||||
}
|
|
||||||
if(descr->comfd < 0){
|
|
||||||
DBG("NO types");
|
|
||||||
WARNX("No types can be choosen");
|
|
||||||
FREE(descr->buf);
|
|
||||||
FREE(descr);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return descr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static sl_tty_t* opentty(){
|
|
||||||
if(!device->name){
|
|
||||||
/// ïÔÓÕÔÓÔ×ÕÅÔ ÉÍÑ ÐÏÒÔÁ
|
|
||||||
WARNX(_("Port name is missing"));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
sl_tty_t *descr = sl_tty_new(device->name, device->speed, 4096);
|
|
||||||
if(!descr){
|
|
||||||
WARN("Can't init %s", device->name);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
descr->format = device->port;
|
|
||||||
descr = sl_tty_open(descr, device->exclusive);
|
|
||||||
return descr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief opendev - open TTY or socket output device
|
* @brief opendev - open TTY or socket output device
|
||||||
* @param d - device type
|
* @param d - device type
|
||||||
|
* @param path - path to dump file or NULL
|
||||||
* @return FALSE if failed
|
* @return FALSE if failed
|
||||||
*/
|
*/
|
||||||
int opendev(chardevice *d, char *path){
|
int opendev(chardevice *d, char *path){
|
||||||
if(!d) return FALSE;
|
if(!d) return FALSE;
|
||||||
DBG("Try to open device");
|
DBG("Try to open device");
|
||||||
device = MALLOC(chardevice, 1);
|
device = d;//MALLOC(chardevice, 1);
|
||||||
memcpy(device, d, sizeof(chardevice));
|
//memcpy(device, d, sizeof(chardevice));
|
||||||
device->name = strdup(d->name);
|
//device->node = strdup(d->node);
|
||||||
if(d->port) device->port = strdup(d->port);
|
//if(d->serformat) device->serformat = strdup(d->serformat);
|
||||||
DBG("devtype=%d", device->type);
|
DBG("devtype=%d", device->type);
|
||||||
switch(device->type){
|
switch(device->type){
|
||||||
case DEV_TTY:
|
case DEV_TTY:
|
||||||
DBG("Serial");
|
DBG("Serial");
|
||||||
device->dev = opentty();
|
device->fd = sl_tty_fdescr(device->node, device->serformat, device->speed, device->exclusive);
|
||||||
if(!device->dev){
|
|
||||||
WARN("Can't open device %s", device->name);
|
|
||||||
DBG("CANT OPEN");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case DEV_NETSOCKET:
|
case DEV_NETSOCKET:
|
||||||
|
DBG("INET socket");
|
||||||
|
device->fd = sl_sock_open(SOCKT_NET, device->node, 0, 0);
|
||||||
|
break;
|
||||||
case DEV_UNIXSOCKET:
|
case DEV_UNIXSOCKET:
|
||||||
DBG("Socket");
|
DBG("UNIX-socket");
|
||||||
device->dev = opensocket();
|
device->fd = sl_sock_open(SOCKT_UNIX, device->node, 0, 0);
|
||||||
if(!device->dev){
|
|
||||||
WARNX("Can't open socket");
|
|
||||||
DBG("CANT OPEN");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
if(device->fd < 0){
|
||||||
|
WARN("Can't open %s", device->node);
|
||||||
|
DBG("CANT OPEN");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
if(path){ // open logging file
|
if(path){ // open logging file
|
||||||
dupfile = fopen(path, "a");
|
dupfile = fopen(path, "a");
|
||||||
if(!dupfile){
|
if(!dupfile){
|
||||||
@@ -327,42 +159,31 @@ int opendev(chardevice *d, char *path){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
changeeol(device->eol); // allow string functions to know EOL
|
changeeol(device->eol); // allow string functions to know EOL
|
||||||
memcpy(d, device, sizeof(chardevice));
|
//d->fd = device->fd;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void closedev(){
|
void closedev(){
|
||||||
|
FNAME();
|
||||||
if(!device) return;
|
if(!device) return;
|
||||||
pthread_mutex_unlock(&device->mutex);
|
// avoid dead-locks
|
||||||
pthread_mutex_trylock(&device->mutex);
|
pthread_mutex_trylock(&device->mutex);
|
||||||
|
int fd = device->fd;
|
||||||
|
device->fd = -1;
|
||||||
|
usleep(1000); // wait a little for threads
|
||||||
|
close(fd);
|
||||||
|
DBG("Device closed");
|
||||||
|
pthread_mutex_unlock(&device->mutex);
|
||||||
if(dupfile){
|
if(dupfile){
|
||||||
|
DBG("Dupfile closed");
|
||||||
fclose(dupfile);
|
fclose(dupfile);
|
||||||
dupfile = NULL;
|
dupfile = NULL;
|
||||||
}
|
}
|
||||||
switch(device->type){
|
DBG("free node");
|
||||||
case DEV_TTY:
|
FREE(device->node);
|
||||||
if(device->dev){
|
DBG("free serformat");
|
||||||
sl_tty_t *t = device->dev;
|
FREE(device->serformat);
|
||||||
ioctl(t->comfd, TCSETS2, &t->oldtty); // return TTY to previous state
|
DBG("free device");
|
||||||
close(t->comfd);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DEV_NETSOCKET:
|
|
||||||
if(device->dev){
|
|
||||||
close(device->dev->comfd);
|
|
||||||
FREE(device->dev);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(device->dev){
|
|
||||||
FREE(device->dev->format);
|
|
||||||
FREE(device->dev->portname);
|
|
||||||
FREE(device->dev->buf);
|
|
||||||
FREE(device->dev);
|
|
||||||
}
|
|
||||||
FREE(device->name);
|
|
||||||
FREE(device);
|
FREE(device);
|
||||||
DBG("Device closed");
|
DBG("Device closed");
|
||||||
}
|
}
|
||||||
|
|||||||
14
ttysocket.h
14
ttysocket.h
@@ -17,8 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef TTY_H__
|
|
||||||
#define TTY_H__
|
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <asm-generic/termbits.h>
|
#include <asm-generic/termbits.h>
|
||||||
@@ -34,9 +32,9 @@ typedef enum{ // device: tty terminal, network socket or UNIX socket
|
|||||||
|
|
||||||
typedef struct{
|
typedef struct{
|
||||||
devtype type; // type
|
devtype type; // type
|
||||||
char *name; // filename (dev or UNIX socket) or server name/IP
|
char *node; // filename (dev or UNIX socket) or server name:IP
|
||||||
sl_tty_t *dev; // tty serial device
|
char *serformat; // serial format like "8N1"
|
||||||
char *port; // port to connect
|
int fd; // file descriptor
|
||||||
int speed; // tty speed
|
int speed; // tty speed
|
||||||
pthread_mutex_t mutex; // reading/writing mutex
|
pthread_mutex_t mutex; // reading/writing mutex
|
||||||
char eol[3]; // end of line
|
char eol[3]; // end of line
|
||||||
@@ -44,10 +42,8 @@ typedef struct{
|
|||||||
int exclusive;
|
int exclusive;
|
||||||
} chardevice;
|
} chardevice;
|
||||||
|
|
||||||
uint8_t *ReadData(int *l);
|
ssize_t ReadData(uint8_t *buf, size_t len);
|
||||||
int SendData(const uint8_t *data, size_t len);
|
ssize_t SendData(const uint8_t *data, size_t len);
|
||||||
void settimeout(int tms);
|
|
||||||
int opendev(chardevice *d, char *path);
|
int opendev(chardevice *d, char *path);
|
||||||
void closedev();
|
void closedev();
|
||||||
|
|
||||||
#endif // TTY_H__
|
|
||||||
|
|||||||
Reference in New Issue
Block a user