fixed some bugs, switching to actual usefull_macros library

This commit is contained in:
2026-05-19 17:37:45 +03:00
parent 9c2b6aeebf
commit a6978dd84a
12 changed files with 465 additions and 418 deletions

View File

@@ -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)

View File

@@ -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
View File

@@ -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, nonprintable 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 Cstyle 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 CRC16 (Modbus)
- Scrollable output buffer, mouse wheel support
- Configurable endofline 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.
### Commandline 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` | Endofline 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 (32126) are shown asis.
Line feed (`\n`) ends the current line.
Nonprintable 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 (8byte 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/UTF8 bytes.
- **Escape sequences** (Cstyle) 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` (spaceseparated)
- 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 usertyped numbers/hex, the program **calculates a 16bit Modbus CRC16**
(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 lowlevel 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)

View File

@@ -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

View File

@@ -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
View File

@@ -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__

116
main.c
View File

@@ -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); }else if(signo < 0){
exit(signo); 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;
} }

View File

@@ -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;
@@ -755,7 +748,7 @@ void *cmdline(void* arg){
break; break;
case KEY_BACKSPACE: case KEY_BACKSPACE:
forward_to_readline(127); // ^? forward_to_readline(127); // ^?
break; break;
case KEY_IC: // ^[[2~ case KEY_IC: // ^[[2~
DBG("key insert"); DBG("key insert");
ptr = "2~"; ptr = "2~";
@@ -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;
} }

View File

@@ -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__

View File

@@ -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){

View File

@@ -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){
fwrite("< ", 1, 2, dupfile); WARNX("ReadData(): Bad receive buffer");
fwrite(r, 1, *len, dupfile); return 0;
} }
return r; 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(buf, 1, l, dupfile);
}
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;
}
if(!data || len == 0){
WARNX("SendData(): Bad data to send");
return 0;
}
ssize_t ret = -1;
DBG("Send %d bytes", len); DBG("Send %d bytes", len);
DBG("lock");
if(0 == pthread_mutex_lock(&device->mutex)){ if(0 == pthread_mutex_lock(&device->mutex)){
switch(device->type){ DBG("write");
case DEV_TTY: ret = write(device->fd, data, len);
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 && 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");
} }

View File

@@ -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__