From 65b86d1bfb6a4d6aa6aa37baeb751c8076057d82 Mon Sep 17 00:00:00 2001 From: Jonathan Fleming Date: Mon, 21 Jul 2025 15:22:25 -0700 Subject: [PATCH 1/2] Adds validation for echo suppression --- src/modbus-rtu-private.h | 3 ++ src/modbus-rtu.c | 95 +++++++++++++++++++++++++++++++++++++++- src/modbus-rtu.h | 4 ++ 3 files changed, 100 insertions(+), 2 deletions(-) diff --git a/src/modbus-rtu-private.h b/src/modbus-rtu-private.h index 01e6a9188..08d48020d 100644 --- a/src/modbus-rtu-private.h +++ b/src/modbus-rtu-private.h @@ -72,6 +72,9 @@ typedef struct _modbus_rtu { #endif /* To handle many slaves on the same link */ int confirmation_to_ignore; + /* software-side local echo suppression of sent bytes since hardware + * does not support it or is configured to not do it */ + bool is_echo_suppressing; } modbus_rtu_t; #endif /* MODBUS_RTU_PRIVATE_H */ diff --git a/src/modbus-rtu.c b/src/modbus-rtu.c index ebef9347f..b61cab31e 100644 --- a/src/modbus-rtu.c +++ b/src/modbus-rtu.c @@ -9,6 +9,7 @@ #include #include #include +#include #ifndef _MSC_VER #include #endif @@ -251,6 +252,56 @@ static void _modbus_rtu_ioctl_rts(modbus_t *ctx, int on) } #endif +static ssize_t _modbus_rtu_suppress_echo_write(modbus_t *ctx, const uint8_t *req, int req_length) { + ssize_t write_size, read_size, count; + uint8_t req_echo[MODBUS_RTU_MAX_ADU_LENGTH]; + time_t start_time; + + write_size = write(ctx->s, req, req_length); + + read_size = 0; + count = 0; + start_time = time(NULL); + // Time limit the loop to 3 seconds in case the read continuously returns 0 bytes read + while (read_size < write_size && difftime(time(NULL), start_time) < 3) + { + count += read(ctx->s, &req_echo[read_size], write_size - read_size); + + // return immediately on error + if (count < 0) { + return -1; + } + + read_size += count; + } + + if (ctx->debug) + { + printf("Read back %d bytes echoed from the socket\n", read_size); + for (int i = 0; i < read_size; i++) + { + fprintf(stderr, "|%02X|", req_echo[i]); + } + fprintf(stderr, "\n"); + } + + for (int i = 0; i < read_size; i++) + { + if (req[i] != req_echo[i]) + { + fprintf(stderr, + "ERROR: during echo suppression, sent 0x%02X for byte req[%d] of the request, read back 0x%02X for byte echo[%d] of the echo\n", + req[i], + i, + req_echo[i], + i); + return -1; + } + } + + return write_size; +} + static ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_length) { #if defined(_WIN32) @@ -272,7 +323,11 @@ static ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_lengt ctx_rtu->set_rts(ctx, ctx_rtu->rts == MODBUS_RTU_RTS_UP); usleep(ctx_rtu->rts_delay); - size = write(ctx->s, req, req_length); + if (!ctx_rtu->is_echo_suppressing) { + size = write(ctx->s, req, req_length); + } else { + size = _modbus_rtu_suppress_echo_write(ctx, req, req_length); + } usleep(ctx_rtu->onebyte_time * req_length + ctx_rtu->rts_delay); ctx_rtu->set_rts(ctx, ctx_rtu->rts != MODBUS_RTU_RTS_UP); @@ -280,7 +335,11 @@ static ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_lengt return size; } else { #endif - return write(ctx->s, req, req_length); + if (!ctx_rtu->is_echo_suppressing) { + return write(ctx->s, req, req_length); + } else { + return _modbus_rtu_suppress_echo_write(ctx, req, req_length); + } #if HAVE_DECL_TIOCM_RTS } #endif @@ -1089,6 +1148,37 @@ int modbus_rtu_set_rts_delay(modbus_t *ctx, int us) } } +int modbus_rtu_set_suppress_echo(modbus_t* ctx, bool on) { + if (ctx == NULL) { + errno = EINVAL; + return -1; + } + + if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) { + modbus_rtu_t* rtu = (modbus_rtu_t*) ctx->backend_data; + rtu->is_echo_suppressing = on; + return 0; + } + + errno = EINVAL; + return -1; +} + +int modbus_rtu_get_suppress_echo(modbus_t* ctx) { + if (ctx == NULL) { + errno = EINVAL; + return -1; + } + + if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) { + modbus_rtu_t* rtu = (modbus_rtu_t*) ctx->backend_data; + return rtu->is_echo_suppressing; + } + + errno = EINVAL; + return -1; +} + static void _modbus_rtu_close(modbus_t *ctx) { /* Restore line settings and close file descriptor in RTU mode */ @@ -1283,6 +1373,7 @@ modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop #endif ctx_rtu->confirmation_to_ignore = FALSE; + ctx_rtu->is_echo_suppressing = FALSE; return ctx; } diff --git a/src/modbus-rtu.h b/src/modbus-rtu.h index 8e89e7304..615495f15 100644 --- a/src/modbus-rtu.h +++ b/src/modbus-rtu.h @@ -7,6 +7,7 @@ #ifndef MODBUS_RTU_H #define MODBUS_RTU_H +#include #include "modbus.h" MODBUS_BEGIN_DECLS @@ -40,4 +41,7 @@ MODBUS_API int modbus_rtu_get_rts_delay(modbus_t *ctx); MODBUS_END_DECLS +int modbus_rtu_set_suppress_echo(modbus_t *ctx, bool on); +int modbus_rtu_get_suppress_echo(modbus_t *ctx); + #endif /* MODBUS_RTU_H */ From 24ae9a099809e1b5297f7aaf89208a2e6a88f21b Mon Sep 17 00:00:00 2001 From: Jonathan Fleming Date: Tue, 22 Jul 2025 09:16:01 -0700 Subject: [PATCH 2/2] API to suppress echo: formatting --- src/modbus-rtu-private.h | 2 +- src/modbus-rtu.c | 29 ++++++++++++++++++++++------- src/modbus-rtu.h | 3 +-- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/modbus-rtu-private.h b/src/modbus-rtu-private.h index 08d48020d..550661fc9 100644 --- a/src/modbus-rtu-private.h +++ b/src/modbus-rtu-private.h @@ -74,7 +74,7 @@ typedef struct _modbus_rtu { int confirmation_to_ignore; /* software-side local echo suppression of sent bytes since hardware * does not support it or is configured to not do it */ - bool is_echo_suppressing; + int is_echo_suppressing; } modbus_rtu_t; #endif /* MODBUS_RTU_PRIVATE_H */ diff --git a/src/modbus-rtu.c b/src/modbus-rtu.c index b61cab31e..834501803 100644 --- a/src/modbus-rtu.c +++ b/src/modbus-rtu.c @@ -252,12 +252,12 @@ static void _modbus_rtu_ioctl_rts(modbus_t *ctx, int on) } #endif -static ssize_t _modbus_rtu_suppress_echo_write(modbus_t *ctx, const uint8_t *req, int req_length) { +static ssize_t _modbus_rtu_suppress_echo_write(int fd, int debug, const uint8_t *req, int req_length) { ssize_t write_size, read_size, count; uint8_t req_echo[MODBUS_RTU_MAX_ADU_LENGTH]; time_t start_time; - write_size = write(ctx->s, req, req_length); + write_size = write(fd, req, req_length); read_size = 0; count = 0; @@ -265,7 +265,7 @@ static ssize_t _modbus_rtu_suppress_echo_write(modbus_t *ctx, const uint8_t *req // Time limit the loop to 3 seconds in case the read continuously returns 0 bytes read while (read_size < write_size && difftime(time(NULL), start_time) < 3) { - count += read(ctx->s, &req_echo[read_size], write_size - read_size); + count += read(fd, &req_echo[read_size], write_size - read_size); // return immediately on error if (count < 0) { @@ -275,7 +275,7 @@ static ssize_t _modbus_rtu_suppress_echo_write(modbus_t *ctx, const uint8_t *req read_size += count; } - if (ctx->debug) + if (debug) { printf("Read back %d bytes echoed from the socket\n", read_size); for (int i = 0; i < read_size; i++) @@ -285,6 +285,15 @@ static ssize_t _modbus_rtu_suppress_echo_write(modbus_t *ctx, const uint8_t *req fprintf(stderr, "\n"); } + if (read_size != write_size) + { + fprintf(stderr, + "ERROR: during echo suppression, sent %d bytes, read back %d bytes\n", + write_size, + read_size); + return -1; + } + for (int i = 0; i < read_size; i++) { if (req[i] != req_echo[i]) @@ -326,7 +335,7 @@ static ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_lengt if (!ctx_rtu->is_echo_suppressing) { size = write(ctx->s, req, req_length); } else { - size = _modbus_rtu_suppress_echo_write(ctx, req, req_length); + size = _modbus_rtu_suppress_echo_write(ctx->s, ctx->debug, req, req_length); } usleep(ctx_rtu->onebyte_time * req_length + ctx_rtu->rts_delay); @@ -338,7 +347,7 @@ static ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_lengt if (!ctx_rtu->is_echo_suppressing) { return write(ctx->s, req, req_length); } else { - return _modbus_rtu_suppress_echo_write(ctx, req, req_length); + return _modbus_rtu_suppress_echo_write(ctx->s, ctx->debug, req, req_length); } #if HAVE_DECL_TIOCM_RTS } @@ -1148,7 +1157,7 @@ int modbus_rtu_set_rts_delay(modbus_t *ctx, int us) } } -int modbus_rtu_set_suppress_echo(modbus_t* ctx, bool on) { +int modbus_rtu_set_suppress_echo(modbus_t* ctx, int on) { if (ctx == NULL) { errno = EINVAL; return -1; @@ -1158,6 +1167,9 @@ int modbus_rtu_set_suppress_echo(modbus_t* ctx, bool on) { modbus_rtu_t* rtu = (modbus_rtu_t*) ctx->backend_data; rtu->is_echo_suppressing = on; return 0; + } else { + errno = EINVAL; + return -1; } errno = EINVAL; @@ -1173,6 +1185,9 @@ int modbus_rtu_get_suppress_echo(modbus_t* ctx) { if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) { modbus_rtu_t* rtu = (modbus_rtu_t*) ctx->backend_data; return rtu->is_echo_suppressing; + } else { + errno = EINVAL; + return -1; } errno = EINVAL; diff --git a/src/modbus-rtu.h b/src/modbus-rtu.h index 615495f15..bc4c61206 100644 --- a/src/modbus-rtu.h +++ b/src/modbus-rtu.h @@ -7,7 +7,6 @@ #ifndef MODBUS_RTU_H #define MODBUS_RTU_H -#include #include "modbus.h" MODBUS_BEGIN_DECLS @@ -41,7 +40,7 @@ MODBUS_API int modbus_rtu_get_rts_delay(modbus_t *ctx); MODBUS_END_DECLS -int modbus_rtu_set_suppress_echo(modbus_t *ctx, bool on); +int modbus_rtu_set_suppress_echo(modbus_t *ctx, int on); int modbus_rtu_get_suppress_echo(modbus_t *ctx); #endif /* MODBUS_RTU_H */