Commit 500c015abdaf00ae2c91824050f119b0263c0288

Authored by Peter M. Groen
1 parent c5fb1247

Setting up working version

.gitignore 0 โ†’ 100644
  1 +/submodules/
... ...
3rdparty/libmodbus/config.h 0 โ†’ 100644
  1 +/* config.h. Generated from config.h.in by configure. */
  2 +/* config.h.in. Generated from configure.ac by autoheader. */
  3 +
  4 +/* Define to 1 if you have the <arpa/inet.h> header file. */
  5 +#define HAVE_ARPA_INET_H 1
  6 +
  7 +/* Define to 1 if you have the declaration of `TIOCSRS485', and to 0 if you
  8 + don't. */
  9 +#define HAVE_DECL_TIOCSRS485 0
  10 +
  11 +/* Define to 1 if you have the declaration of `TIOCM_RTS', and to 0 if you
  12 + don't. */
  13 +#define HAVE_DECL_TIOCM_RTS 0
  14 +
  15 +/* Define to 1 if you have the declaration of `__CYGWIN__', and to 0 if you
  16 + don't. */
  17 +#define HAVE_DECL___CYGWIN__ 0
  18 +
  19 +/* Define to 1 if you have the <dlfcn.h> header file. */
  20 +#define HAVE_DLFCN_H 1
  21 +
  22 +/* Define to 1 if you have the <errno.h> header file. */
  23 +#define HAVE_ERRNO_H 1
  24 +
  25 +/* Define to 1 if you have the <fcntl.h> header file. */
  26 +#define HAVE_FCNTL_H 1
  27 +
  28 +/* Define to 1 if you have the `fork' function. */
  29 +#define HAVE_FORK 1
  30 +
  31 +/* Define to 1 if you have the `getaddrinfo' function. */
  32 +#define HAVE_GETADDRINFO 1
  33 +
  34 +/* Define to 1 if you have the `gettimeofday' function. */
  35 +#define HAVE_GETTIMEOFDAY 1
  36 +
  37 +/* Define to 1 if you have the `inet_ntoa' function. */
  38 +#define HAVE_INET_NTOA 1
  39 +
  40 +/* Define to 1 if you have the <inttypes.h> header file. */
  41 +#define HAVE_INTTYPES_H 1
  42 +
  43 +/* Define to 1 if you have the <limits.h> header file. */
  44 +#define HAVE_LIMITS_H 1
  45 +
  46 +/* Define to 1 if you have the <linux/serial.h> header file. */
  47 +#define HAVE_LINUX_SERIAL_H 1
  48 +
  49 +/* Define to 1 if you have the <memory.h> header file. */
  50 +#define HAVE_MEMORY_H 1
  51 +
  52 +/* Define to 1 if you have the `memset' function. */
  53 +#define HAVE_MEMSET 1
  54 +
  55 +/* Define to 1 if you have the <netdb.h> header file. */
  56 +#define HAVE_NETDB_H 1
  57 +
  58 +/* Define to 1 if you have the <netinet/in.h> header file. */
  59 +#define HAVE_NETINET_IN_H 1
  60 +
  61 +/* Define to 1 if you have the <netinet/tcp.h> header file. */
  62 +#define HAVE_NETINET_TCP_H 1
  63 +
  64 +/* Define to 1 if you have the `select' function. */
  65 +#define HAVE_SELECT 1
  66 +
  67 +/* Define to 1 if you have the `socket' function. */
  68 +#define HAVE_SOCKET 1
  69 +
  70 +/* Define to 1 if you have the <stdint.h> header file. */
  71 +#define HAVE_STDINT_H 1
  72 +
  73 +/* Define to 1 if you have the <stdlib.h> header file. */
  74 +#define HAVE_STDLIB_H 1
  75 +
  76 +/* Define to 1 if you have the `strerror' function. */
  77 +#define HAVE_STRERROR 1
  78 +
  79 +/* Define to 1 if you have the <strings.h> header file. */
  80 +#define HAVE_STRINGS_H 1
  81 +
  82 +/* Define to 1 if you have the <string.h> header file. */
  83 +#define HAVE_STRING_H 1
  84 +
  85 +/* Define to 1 if you have the `strlcpy' function. */
  86 +/* #undef HAVE_STRLCPY */
  87 +
  88 +/* Define to 1 if you have the <sys/ioctl.h> header file. */
  89 +#define HAVE_SYS_IOCTL_H 1
  90 +
  91 +/* Define to 1 if you have the <sys/socket.h> header file. */
  92 +#define HAVE_SYS_SOCKET_H 1
  93 +
  94 +/* Define to 1 if you have the <sys/stat.h> header file. */
  95 +#define HAVE_SYS_STAT_H 1
  96 +
  97 +/* Define to 1 if you have the <sys/time.h> header file. */
  98 +#define HAVE_SYS_TIME_H 1
  99 +
  100 +/* Define to 1 if you have the <sys/types.h> header file. */
  101 +#define HAVE_SYS_TYPES_H 1
  102 +
  103 +/* Define to 1 if you have the <termios.h> header file. */
  104 +#define HAVE_TERMIOS_H 1
  105 +
  106 +/* Define to 1 if you have the <time.h> header file. */
  107 +#define HAVE_TIME_H 1
  108 +
  109 +/* Define to 1 if you have the <unistd.h> header file. */
  110 +#define HAVE_UNISTD_H 1
  111 +
  112 +/* Define to 1 if you have the `vfork' function. */
  113 +#define HAVE_VFORK 1
  114 +
  115 +/* Define to 1 if you have the <vfork.h> header file. */
  116 +/* #undef HAVE_VFORK_H */
  117 +
  118 +/* Define to 1 if you have the <winsock2.h> header file. */
  119 +/* #undef HAVE_WINSOCK2_H */
  120 +
  121 +/* Define to 1 if `fork' works. */
  122 +#define HAVE_WORKING_FORK 1
  123 +
  124 +/* Define to 1 if `vfork' works. */
  125 +#define HAVE_WORKING_VFORK 1
  126 +
  127 +/* Define to the sub-directory in which libtool stores uninstalled libraries.
  128 + */
  129 +#define LT_OBJDIR ".libs/"
  130 +
  131 +/* Name of package */
  132 +#define PACKAGE "libmodbus"
  133 +
  134 +/* Define to the address where bug reports for this package should be sent. */
  135 +#define PACKAGE_BUGREPORT "https://github.com/stephane/libmodbus/issues"
  136 +
  137 +/* Define to the full name of this package. */
  138 +#define PACKAGE_NAME "libmodbus"
  139 +
  140 +/* Define to the full name and version of this package. */
  141 +#define PACKAGE_STRING "libmodbus 3.1.0-1"
  142 +
  143 +/* Define to the one symbol short name of this package. */
  144 +#define PACKAGE_TARNAME "libmodbus"
  145 +
  146 +/* Define to the home page for this package. */
  147 +#define PACKAGE_URL ""
  148 +
  149 +/* Define to the version of this package. */
  150 +#define PACKAGE_VERSION "3.1.0-1"
  151 +
  152 +/* Define to 1 if you have the ANSI C header files. */
  153 +#define STDC_HEADERS 1
  154 +
  155 +/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
  156 +#define TIME_WITH_SYS_TIME 1
  157 +
  158 +/* Version number of package */
  159 +#define VERSION "3.1.0-1"
  160 +
  161 +/* Define to empty if `const' does not conform to ANSI C. */
  162 +/* #undef const */
  163 +
  164 +/* Define to `int' if <sys/types.h> does not define. */
  165 +/* #undef pid_t */
  166 +
  167 +/* Define to `unsigned int' if <sys/types.h> does not define. */
  168 +/* #undef size_t */
  169 +
  170 +/* Define as `fork' if `vfork' does not work. */
  171 +/* #undef vfork */
... ...
3rdparty/libmodbus/modbus-data.c 0 โ†’ 100644
  1 +/*
  2 + * Copyright ยฉ 2010-2014 Stรฉphane Raimbault <stephane.raimbault@gmail.com>
  3 + *
  4 + * SPDX-License-Identifier: LGPL-2.1+
  5 + */
  6 +
  7 +#include <stdlib.h>
  8 +
  9 +#ifndef _MSC_VER
  10 +# include <stdint.h>
  11 +#else
  12 +# include "stdint.h"
  13 +#endif
  14 +
  15 +#include <string.h>
  16 +#include <assert.h>
  17 +
  18 +#if defined(_WIN32)
  19 +# include <winsock2.h>
  20 +#else
  21 +# include <arpa/inet.h>
  22 +#endif
  23 +
  24 +#include <config.h>
  25 +
  26 +#include "modbus.h"
  27 +
  28 +#if defined(HAVE_BYTESWAP_H)
  29 +# include <byteswap.h>
  30 +#endif
  31 +
  32 +#if defined(__APPLE__)
  33 +# include <libkern/OSByteOrder.h>
  34 +# define bswap_16 OSSwapInt16
  35 +# define bswap_32 OSSwapInt32
  36 +# define bswap_64 OSSwapInt64
  37 +#endif
  38 +
  39 +#if defined(__GNUC__)
  40 +# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__ * 10)
  41 +# if GCC_VERSION >= 430
  42 +// Since GCC >= 4.30, GCC provides __builtin_bswapXX() alternatives so we switch to them
  43 +# undef bswap_32
  44 +# define bswap_32 __builtin_bswap32
  45 +# endif
  46 +#endif
  47 +
  48 +#if defined(_MSC_VER) && (_MSC_VER >= 1400)
  49 +# define bswap_32 _byteswap_ulong
  50 +# define bswap_16 _byteswap_ushort
  51 +#endif
  52 +
  53 +#if !defined(__CYGWIN__) && !defined(bswap_16)
  54 +# warning "Fallback on C functions for bswap_16"
  55 +static inline uint16_t bswap_16(uint16_t x)
  56 +{
  57 + return (x >> 8) | (x << 8);
  58 +}
  59 +#endif
  60 +
  61 +#if !defined(bswap_32)
  62 +# warning "Fallback on C functions for bswap_32"
  63 +static inline uint32_t bswap_32(uint32_t x)
  64 +{
  65 + return (bswap_16(x & 0xffff) << 16) | (bswap_16(x >> 16));
  66 +}
  67 +#endif
  68 +
  69 +/* Sets many bits from a single byte value (all 8 bits of the byte value are
  70 + set) */
  71 +void modbus_set_bits_from_byte(uint8_t *dest, int idx, const uint8_t value)
  72 +{
  73 + int i;
  74 +
  75 + for (i=0; i < 8; i++) {
  76 + dest[idx+i] = (value & (1 << i)) ? 1 : 0;
  77 + }
  78 +}
  79 +
  80 +/* Sets many bits from a table of bytes (only the bits between idx and
  81 + idx + nb_bits are set) */
  82 +void modbus_set_bits_from_bytes(uint8_t *dest, int idx, unsigned int nb_bits,
  83 + const uint8_t *tab_byte)
  84 +{
  85 + unsigned int i;
  86 + int shift = 0;
  87 +
  88 + for (i = idx; i < idx + nb_bits; i++) {
  89 + dest[i] = tab_byte[(i - idx) / 8] & (1 << shift) ? 1 : 0;
  90 + /* gcc doesn't like: shift = (++shift) % 8; */
  91 + shift++;
  92 + shift %= 8;
  93 + }
  94 +}
  95 +
  96 +/* Gets the byte value from many bits.
  97 + To obtain a full byte, set nb_bits to 8. */
  98 +uint8_t modbus_get_byte_from_bits(const uint8_t *src, int idx,
  99 + unsigned int nb_bits)
  100 +{
  101 + unsigned int i;
  102 + uint8_t value = 0;
  103 +
  104 + if (nb_bits > 8) {
  105 + /* Assert is ignored if NDEBUG is set */
  106 + assert(nb_bits < 8);
  107 + nb_bits = 8;
  108 + }
  109 +
  110 + for (i=0; i < nb_bits; i++) {
  111 + value |= (src[idx+i] << i);
  112 + }
  113 +
  114 + return value;
  115 +}
  116 +
  117 +/* Get a float from 4 bytes (Modbus) without any conversion (ABCD) */
  118 +float modbus_get_float_abcd(const uint16_t *src)
  119 +{
  120 + float f;
  121 + uint32_t i;
  122 +
  123 + i = ntohl(((uint32_t)src[0] << 16) + src[1]);
  124 + memcpy(&f, &i, sizeof(float));
  125 +
  126 + return f;
  127 +}
  128 +
  129 +/* Get a float from 4 bytes (Modbus) in inversed format (DCBA) */
  130 +float modbus_get_float_dcba(const uint16_t *src)
  131 +{
  132 + float f;
  133 + uint32_t i;
  134 +
  135 + i = ntohl(bswap_32((((uint32_t)src[0]) << 16) + src[1]));
  136 + memcpy(&f, &i, sizeof(float));
  137 +
  138 + return f;
  139 +}
  140 +
  141 +/* Get a float from 4 bytes (Modbus) with swapped bytes (BADC) */
  142 +float modbus_get_float_badc(const uint16_t *src)
  143 +{
  144 + float f;
  145 + uint32_t i;
  146 +
  147 + i = ntohl((uint32_t)(bswap_16(src[0]) << 16) + bswap_16(src[1]));
  148 + memcpy(&f, &i, sizeof(float));
  149 +
  150 + return f;
  151 +}
  152 +
  153 +/* Get a float from 4 bytes (Modbus) with swapped words (CDAB) */
  154 +float modbus_get_float_cdab(const uint16_t *src)
  155 +{
  156 + float f;
  157 + uint32_t i;
  158 +
  159 + i = ntohl((((uint32_t)src[1]) << 16) + src[0]);
  160 + memcpy(&f, &i, sizeof(float));
  161 +
  162 + return f;
  163 +}
  164 +
  165 +/* DEPRECATED - Get a float from 4 bytes in sort of Modbus format */
  166 +float modbus_get_float(const uint16_t *src)
  167 +{
  168 + float f;
  169 + uint32_t i;
  170 +
  171 + i = (((uint32_t)src[1]) << 16) + src[0];
  172 + memcpy(&f, &i, sizeof(float));
  173 +
  174 + return f;
  175 +}
  176 +
  177 +/* Set a float to 4 bytes for Modbus w/o any conversion (ABCD) */
  178 +void modbus_set_float_abcd(float f, uint16_t *dest)
  179 +{
  180 + uint32_t i;
  181 +
  182 + memcpy(&i, &f, sizeof(uint32_t));
  183 + i = htonl(i);
  184 + dest[0] = (uint16_t)(i >> 16);
  185 + dest[1] = (uint16_t)i;
  186 +}
  187 +
  188 +/* Set a float to 4 bytes for Modbus with byte and word swap conversion (DCBA) */
  189 +void modbus_set_float_dcba(float f, uint16_t *dest)
  190 +{
  191 + uint32_t i;
  192 +
  193 + memcpy(&i, &f, sizeof(uint32_t));
  194 + i = bswap_32(htonl(i));
  195 + dest[0] = (uint16_t)(i >> 16);
  196 + dest[1] = (uint16_t)i;
  197 +}
  198 +
  199 +/* Set a float to 4 bytes for Modbus with byte swap conversion (BADC) */
  200 +void modbus_set_float_badc(float f, uint16_t *dest)
  201 +{
  202 + uint32_t i;
  203 +
  204 + memcpy(&i, &f, sizeof(uint32_t));
  205 + i = htonl(i);
  206 + dest[0] = (uint16_t)bswap_16(i >> 16);
  207 + dest[1] = (uint16_t)bswap_16(i & 0xFFFF);
  208 +}
  209 +
  210 +/* Set a float to 4 bytes for Modbus with word swap conversion (CDAB) */
  211 +void modbus_set_float_cdab(float f, uint16_t *dest)
  212 +{
  213 + uint32_t i;
  214 +
  215 + memcpy(&i, &f, sizeof(uint32_t));
  216 + i = htonl(i);
  217 + dest[0] = (uint16_t)i;
  218 + dest[1] = (uint16_t)(i >> 16);
  219 +}
  220 +
  221 +/* DEPRECATED - Set a float to 4 bytes in a sort of Modbus format! */
  222 +void modbus_set_float(float f, uint16_t *dest)
  223 +{
  224 + uint32_t i;
  225 +
  226 + memcpy(&i, &f, sizeof(uint32_t));
  227 + dest[0] = (uint16_t)i;
  228 + dest[1] = (uint16_t)(i >> 16);
  229 +}
... ...
3rdparty/libmodbus/modbus-private.h 0 โ†’ 100644
  1 +/*
  2 + * Copyright ยฉ 2010-2012 Stรฉphane Raimbault <stephane.raimbault@gmail.com>
  3 + *
  4 + * SPDX-License-Identifier: LGPL-2.1+
  5 + */
  6 +
  7 +#ifndef MODBUS_PRIVATE_H
  8 +#define MODBUS_PRIVATE_H
  9 +
  10 +#ifndef _MSC_VER
  11 +# include <stdint.h>
  12 +# include <sys/time.h>
  13 +#else
  14 +# include "stdint.h"
  15 +# include <time.h>
  16 +typedef int ssize_t;
  17 +#endif
  18 +#include <sys/types.h>
  19 +#include <config.h>
  20 +
  21 +#include "modbus.h"
  22 +
  23 +MODBUS_BEGIN_DECLS
  24 +
  25 +/* It's not really the minimal length (the real one is report slave ID
  26 + * in RTU (4 bytes)) but it's a convenient size to use in RTU or TCP
  27 + * communications to read many values or write a single one.
  28 + * Maximum between :
  29 + * - HEADER_LENGTH_TCP (7) + function (1) + address (2) + number (2)
  30 + * - HEADER_LENGTH_RTU (1) + function (1) + address (2) + number (2) + CRC (2)
  31 + */
  32 +#define _MIN_REQ_LENGTH 12
  33 +
  34 +#define _REPORT_SLAVE_ID 180
  35 +
  36 +#define _MODBUS_EXCEPTION_RSP_LENGTH 5
  37 +
  38 +/* Timeouts in microsecond (0.5 s) */
  39 +#define _RESPONSE_TIMEOUT 500000
  40 +#define _BYTE_TIMEOUT 500000
  41 +
  42 +typedef enum {
  43 + _MODBUS_BACKEND_TYPE_RTU=0,
  44 + _MODBUS_BACKEND_TYPE_TCP
  45 +} modbus_backend_type_t;
  46 +
  47 +/*
  48 + * ---------- Request Indication ----------
  49 + * | Client | ---------------------->| Server |
  50 + * ---------- Confirmation Response ----------
  51 + */
  52 +typedef enum {
  53 + /* Request message on the server side */
  54 + MSG_INDICATION,
  55 + /* Request message on the client side */
  56 + MSG_CONFIRMATION
  57 +} msg_type_t;
  58 +
  59 +/* This structure reduces the number of params in functions and so
  60 + * optimizes the speed of execution (~ 37%). */
  61 +typedef struct _sft {
  62 + int slave;
  63 + int function;
  64 + int t_id;
  65 +} sft_t;
  66 +
  67 +typedef struct _modbus_backend {
  68 + unsigned int backend_type;
  69 + unsigned int header_length;
  70 + unsigned int checksum_length;
  71 + unsigned int max_adu_length;
  72 + int (*set_slave) (modbus_t *ctx, int slave);
  73 + int (*build_request_basis) (modbus_t *ctx, int function, int addr,
  74 + int nb, uint8_t *req);
  75 + int (*build_response_basis) (sft_t *sft, uint8_t *rsp);
  76 + int (*prepare_response_tid) (const uint8_t *req, int *req_length);
  77 + int (*send_msg_pre) (uint8_t *req, int req_length);
  78 + ssize_t (*send) (modbus_t *ctx, const uint8_t *req, int req_length);
  79 + int (*receive) (modbus_t *ctx, uint8_t *req);
  80 + ssize_t (*recv) (modbus_t *ctx, uint8_t *rsp, int rsp_length);
  81 + int (*check_integrity) (modbus_t *ctx, uint8_t *msg,
  82 + const int msg_length);
  83 + int (*pre_check_confirmation) (modbus_t *ctx, const uint8_t *req,
  84 + const uint8_t *rsp, int rsp_length);
  85 + int (*connect) (modbus_t *ctx);
  86 + void (*close) (modbus_t *ctx);
  87 + int (*flush) (modbus_t *ctx);
  88 + int (*select) (modbus_t *ctx, fd_set *rset, struct timeval *tv, int msg_length);
  89 + void (*free) (modbus_t *ctx);
  90 +} modbus_backend_t;
  91 +
  92 +struct _modbus {
  93 + /* Slave address */
  94 + int slave;
  95 + /* Socket or file descriptor */
  96 + int s;
  97 + int debug;
  98 + int error_recovery;
  99 + struct timeval response_timeout;
  100 + struct timeval byte_timeout;
  101 + const modbus_backend_t *backend;
  102 + void *backend_data;
  103 +};
  104 +
  105 +void _modbus_init_common(modbus_t *ctx);
  106 +void _error_print(modbus_t *ctx, const char *context);
  107 +int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type);
  108 +
  109 +#ifndef HAVE_STRLCPY
  110 +size_t strlcpy(char *dest, const char *src, size_t dest_size);
  111 +#endif
  112 +
  113 +MODBUS_END_DECLS
  114 +
  115 +#endif /* MODBUS_PRIVATE_H */
... ...
3rdparty/libmodbus/modbus-rtu-private.h 0 โ†’ 100644
  1 +/*
  2 + * Copyright ยฉ 2001-2011 Stรฉphane Raimbault <stephane.raimbault@gmail.com>
  3 + *
  4 + * SPDX-License-Identifier: LGPL-2.1+
  5 + */
  6 +
  7 +#ifndef MODBUS_RTU_PRIVATE_H
  8 +#define MODBUS_RTU_PRIVATE_H
  9 +
  10 +#ifndef _MSC_VER
  11 +#include <stdint.h>
  12 +#else
  13 +#include "stdint.h"
  14 +#endif
  15 +
  16 +#if defined(_WIN32)
  17 +#include <windows.h>
  18 +#else
  19 +#include <termios.h>
  20 +#endif
  21 +
  22 +#define _MODBUS_RTU_HEADER_LENGTH 1
  23 +#define _MODBUS_RTU_PRESET_REQ_LENGTH 6
  24 +#define _MODBUS_RTU_PRESET_RSP_LENGTH 2
  25 +
  26 +#define _MODBUS_RTU_CHECKSUM_LENGTH 2
  27 +
  28 +#if defined(_WIN32)
  29 +#if !defined(ENOTSUP)
  30 +#define ENOTSUP WSAEOPNOTSUPP
  31 +#endif
  32 +
  33 +/* WIN32: struct containing serial handle and a receive buffer */
  34 +#define PY_BUF_SIZE 512
  35 +struct win32_ser {
  36 + /* File handle */
  37 + HANDLE fd;
  38 + /* Receive buffer */
  39 + uint8_t buf[PY_BUF_SIZE];
  40 + /* Received chars */
  41 + DWORD n_bytes;
  42 +};
  43 +#endif /* _WIN32 */
  44 +
  45 +typedef struct _modbus_rtu {
  46 + /* Device: "/dev/ttyS0", "/dev/ttyUSB0" or "/dev/tty.USA19*" on Mac OS X. */
  47 + char *device;
  48 + /* Bauds: 9600, 19200, 57600, 115200, etc */
  49 + int baud;
  50 + /* Data bit */
  51 + uint8_t data_bit;
  52 + /* Stop bit */
  53 + uint8_t stop_bit;
  54 + /* Parity: 'N', 'O', 'E' */
  55 + char parity;
  56 +#if defined(_WIN32)
  57 + struct win32_ser w_ser;
  58 + DCB old_dcb;
  59 +#else
  60 + /* Save old termios settings */
  61 + struct termios old_tios;
  62 +#endif
  63 +#if HAVE_DECL_TIOCSRS485
  64 + int serial_mode;
  65 +#endif
  66 +//***Not part of libmodbus - added for QModMaster***//
  67 +#if defined(_WIN32) || HAVE_DECL_TIOCM_RTS
  68 + int rts;
  69 + int rts_delay;
  70 + int onebyte_time;
  71 + void (*set_rts) (modbus_t *ctx, int on);
  72 +#endif
  73 + /* To handle many slaves on the same link */
  74 + int confirmation_to_ignore;
  75 +} modbus_rtu_t;
  76 +
  77 +#endif /* MODBUS_RTU_PRIVATE_H */
... ...
3rdparty/libmodbus/modbus-rtu.c 0 โ†’ 100644
  1 +/*
  2 + * Copyright ยฉ 2001-2011 Stรฉphane Raimbault <stephane.raimbault@gmail.com>
  3 + *
  4 + * SPDX-License-Identifier: LGPL-2.1+
  5 + */
  6 +
  7 +#include <stdio.h>
  8 +#include <stdlib.h>
  9 +#include <errno.h>
  10 +#include <fcntl.h>
  11 +#include <string.h>
  12 +#ifndef _MSC_VER
  13 +#include <unistd.h>
  14 +#endif
  15 +#include <assert.h>
  16 +
  17 +#include "modbus-private.h"
  18 +
  19 +#include "modbus-rtu.h"
  20 +#include "modbus-rtu-private.h"
  21 +
  22 +#if HAVE_DECL_TIOCSRS485 || HAVE_DECL_TIOCM_RTS
  23 +#include <sys/ioctl.h>
  24 +#endif
  25 +
  26 +#if HAVE_DECL_TIOCSRS485
  27 +#include <linux/serial.h>
  28 +#endif
  29 +
  30 +/* Table of CRC values for high-order byte */
  31 +static const uint8_t table_crc_hi[] = {
  32 + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
  33 + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
  34 + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
  35 + 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
  36 + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
  37 + 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
  38 + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
  39 + 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
  40 + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
  41 + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
  42 + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
  43 + 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
  44 + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
  45 + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
  46 + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
  47 + 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
  48 + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
  49 + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
  50 + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
  51 + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
  52 + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
  53 + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
  54 + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
  55 + 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
  56 + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
  57 + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
  58 +};
  59 +
  60 +/* Table of CRC values for low-order byte */
  61 +static const uint8_t table_crc_lo[] = {
  62 + 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
  63 + 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
  64 + 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
  65 + 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
  66 + 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
  67 + 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
  68 + 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
  69 + 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
  70 + 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
  71 + 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
  72 + 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
  73 + 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
  74 + 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
  75 + 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
  76 + 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
  77 + 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
  78 + 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
  79 + 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
  80 + 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
  81 + 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
  82 + 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
  83 + 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
  84 + 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
  85 + 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
  86 + 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
  87 + 0x43, 0x83, 0x41, 0x81, 0x80, 0x40
  88 +};
  89 +
  90 +/* Define the slave ID of the remote device to talk in master mode or set the
  91 + * internal slave ID in slave mode */
  92 +static int _modbus_set_slave(modbus_t *ctx, int slave)
  93 +{
  94 + /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */
  95 + if (slave >= 0 && slave <= 247) {
  96 + ctx->slave = slave;
  97 + } else {
  98 + errno = EINVAL;
  99 + return -1;
  100 + }
  101 +
  102 + return 0;
  103 +}
  104 +
  105 +/* Builds a RTU request header */
  106 +static int _modbus_rtu_build_request_basis(modbus_t *ctx, int function,
  107 + int addr, int nb,
  108 + uint8_t *req)
  109 +{
  110 + assert(ctx->slave != -1);
  111 + req[0] = ctx->slave;
  112 + req[1] = function;
  113 + req[2] = addr >> 8;
  114 + req[3] = addr & 0x00ff;
  115 + req[4] = nb >> 8;
  116 + req[5] = nb & 0x00ff;
  117 +
  118 + return _MODBUS_RTU_PRESET_REQ_LENGTH;
  119 +}
  120 +
  121 +/* Builds a RTU response header */
  122 +static int _modbus_rtu_build_response_basis(sft_t *sft, uint8_t *rsp)
  123 +{
  124 + /* In this case, the slave is certainly valid because a check is already
  125 + * done in _modbus_rtu_listen */
  126 + rsp[0] = sft->slave;
  127 + rsp[1] = sft->function;
  128 +
  129 + return _MODBUS_RTU_PRESET_RSP_LENGTH;
  130 +}
  131 +
  132 +static uint16_t crc16(uint8_t *buffer, uint16_t buffer_length)
  133 +{
  134 + uint8_t crc_hi = 0xFF; /* high CRC byte initialized */
  135 + uint8_t crc_lo = 0xFF; /* low CRC byte initialized */
  136 + unsigned int i; /* will index into CRC lookup */
  137 +
  138 + /* pass through message buffer */
  139 + while (buffer_length--) {
  140 + i = crc_hi ^ *buffer++; /* calculate the CRC */
  141 + crc_hi = crc_lo ^ table_crc_hi[i];
  142 + crc_lo = table_crc_lo[i];
  143 + }
  144 +
  145 + return (crc_hi << 8 | crc_lo);
  146 +}
  147 +
  148 +static int _modbus_rtu_prepare_response_tid(const uint8_t *req, int *req_length)
  149 +{
  150 + (*req_length) -= _MODBUS_RTU_CHECKSUM_LENGTH;
  151 + /* No TID */
  152 + return 0;
  153 +}
  154 +
  155 +static int _modbus_rtu_send_msg_pre(uint8_t *req, int req_length)
  156 +{
  157 + uint16_t crc = crc16(req, req_length);
  158 + req[req_length++] = crc >> 8;
  159 + req[req_length++] = crc & 0x00FF;
  160 +
  161 + return req_length;
  162 +}
  163 +
  164 +#if defined(_WIN32)
  165 +
  166 +/* This simple implementation is sort of a substitute of the select() call,
  167 + * working this way: the win32_ser_select() call tries to read some data from
  168 + * the serial port, setting the timeout as the select() call would. Data read is
  169 + * stored into the receive buffer, that is then consumed by the win32_ser_read()
  170 + * call. So win32_ser_select() does both the event waiting and the reading,
  171 + * while win32_ser_read() only consumes the receive buffer.
  172 + */
  173 +
  174 +static void win32_ser_init(struct win32_ser *ws)
  175 +{
  176 + /* Clear everything */
  177 + memset(ws, 0x00, sizeof(struct win32_ser));
  178 +
  179 + /* Set file handle to invalid */
  180 + ws->fd = INVALID_HANDLE_VALUE;
  181 +}
  182 +
  183 +/* FIXME Try to remove length_to_read -> max_len argument, only used by win32 */
  184 +static int win32_ser_select(struct win32_ser *ws, int max_len,
  185 + const struct timeval *tv)
  186 +{
  187 + COMMTIMEOUTS comm_to;
  188 + unsigned int msec = 0;
  189 +
  190 + /* Check if some data still in the buffer to be consumed */
  191 + if (ws->n_bytes > 0) {
  192 + return 1;
  193 + }
  194 +
  195 + /* Setup timeouts like select() would do.
  196 + FIXME Please someone on Windows can look at this?
  197 + Does it possible to use WaitCommEvent?
  198 + When tv is NULL, MAXDWORD isn't infinite!
  199 + */
  200 + if (tv == NULL) {
  201 + msec = MAXDWORD;
  202 + } else {
  203 + msec = tv->tv_sec * 1000 + tv->tv_usec / 1000;
  204 + if (msec < 1)
  205 + msec = 1;
  206 + }
  207 +
  208 + comm_to.ReadIntervalTimeout = msec;
  209 + comm_to.ReadTotalTimeoutMultiplier = 0;
  210 + comm_to.ReadTotalTimeoutConstant = msec;
  211 + comm_to.WriteTotalTimeoutMultiplier = 0;
  212 + comm_to.WriteTotalTimeoutConstant = 1000;
  213 + SetCommTimeouts(ws->fd, &comm_to);
  214 +
  215 + /* Read some bytes */
  216 + if ((max_len > PY_BUF_SIZE) || (max_len < 0)) {
  217 + max_len = PY_BUF_SIZE;
  218 + }
  219 +
  220 + if (ReadFile(ws->fd, &ws->buf, max_len, &ws->n_bytes, NULL)) {
  221 + /* Check if some bytes available */
  222 + if (ws->n_bytes > 0) {
  223 + /* Some bytes read */
  224 + return 1;
  225 + } else {
  226 + /* Just timed out */
  227 + return 0;
  228 + }
  229 + } else {
  230 + /* Some kind of error */
  231 + return -1;
  232 + }
  233 +}
  234 +
  235 +static int win32_ser_read(struct win32_ser *ws, uint8_t *p_msg,
  236 + unsigned int max_len)
  237 +{
  238 + unsigned int n = ws->n_bytes;
  239 +
  240 + if (max_len < n) {
  241 + n = max_len;
  242 + }
  243 +
  244 + if (n > 0) {
  245 + memcpy(p_msg, ws->buf, n);
  246 + }
  247 +
  248 + ws->n_bytes -= n;
  249 +
  250 + return n;
  251 +}
  252 +#endif
  253 +
  254 +#if HAVE_DECL_TIOCM_RTS
  255 +static void _modbus_rtu_ioctl_rts(modbus_t *ctx, int on)
  256 +{
  257 + int fd = ctx->s;
  258 + int flags;
  259 +
  260 + ioctl(fd, TIOCMGET, &flags);
  261 + if (on) {
  262 + flags |= TIOCM_RTS;
  263 + } else {
  264 + flags &= ~TIOCM_RTS;
  265 + }
  266 + ioctl(fd, TIOCMSET, &flags);
  267 +}
  268 +#endif
  269 +
  270 +static ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_length)
  271 +{
  272 +#if defined(_WIN32)
  273 + modbus_rtu_t *ctx_rtu = ctx->backend_data;
  274 + DWORD n_bytes = 0;
  275 + return (WriteFile(ctx_rtu->w_ser.fd, req, req_length, &n_bytes, NULL)) ? (ssize_t)n_bytes : -1;
  276 +#else
  277 +#if HAVE_DECL_TIOCM_RTS
  278 + modbus_rtu_t *ctx_rtu = ctx->backend_data;
  279 + if (ctx_rtu->rts != MODBUS_RTU_RTS_NONE) {
  280 + ssize_t size;
  281 +
  282 + if (ctx->debug) {
  283 + fprintf(stderr, "Sending request using RTS signal\n");
  284 + }
  285 +
  286 + ctx_rtu->set_rts(ctx, ctx_rtu->rts == MODBUS_RTU_RTS_UP);
  287 + usleep(ctx_rtu->rts_delay);
  288 +
  289 + size = write(ctx->s, req, req_length);
  290 +
  291 + usleep(ctx_rtu->onebyte_time * req_length + ctx_rtu->rts_delay);
  292 + ctx_rtu->set_rts(ctx, ctx_rtu->rts != MODBUS_RTU_RTS_UP);
  293 +
  294 + return size;
  295 + } else {
  296 +#endif
  297 + return write(ctx->s, req, req_length);
  298 +#if HAVE_DECL_TIOCM_RTS
  299 + }
  300 +#endif
  301 +#endif
  302 +}
  303 +
  304 +static int _modbus_rtu_receive(modbus_t *ctx, uint8_t *req)
  305 +{
  306 + int rc;
  307 + modbus_rtu_t *ctx_rtu = ctx->backend_data;
  308 +
  309 + if (ctx_rtu->confirmation_to_ignore) {
  310 + _modbus_receive_msg(ctx, req, MSG_CONFIRMATION);
  311 + /* Ignore errors and reset the flag */
  312 + ctx_rtu->confirmation_to_ignore = FALSE;
  313 + rc = 0;
  314 + if (ctx->debug) {
  315 + printf("Confirmation to ignore\n");
  316 + }
  317 + } else {
  318 + rc = _modbus_receive_msg(ctx, req, MSG_INDICATION);
  319 + if (rc == 0) {
  320 + /* The next expected message is a confirmation to ignore */
  321 + ctx_rtu->confirmation_to_ignore = TRUE;
  322 + }
  323 + }
  324 + return rc;
  325 +}
  326 +
  327 +static ssize_t _modbus_rtu_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length)
  328 +{
  329 +#if defined(_WIN32)
  330 + return win32_ser_read(&((modbus_rtu_t *)ctx->backend_data)->w_ser, rsp, rsp_length);
  331 +#else
  332 + return read(ctx->s, rsp, rsp_length);
  333 +#endif
  334 +}
  335 +
  336 +static int _modbus_rtu_flush(modbus_t *);
  337 +
  338 +static int _modbus_rtu_pre_check_confirmation(modbus_t *ctx, const uint8_t *req,
  339 + const uint8_t *rsp, int rsp_length)
  340 +{
  341 + /* Check responding slave is the slave we requested (except for broacast
  342 + * request) */
  343 + if (req[0] != rsp[0] && req[0] != MODBUS_BROADCAST_ADDRESS) {
  344 + if (ctx->debug) {
  345 + fprintf(stderr,
  346 + "The responding slave %d isn't the requested slave %d\n",
  347 + rsp[0], req[0]);
  348 + }
  349 + errno = EMBBADSLAVE;
  350 + return -1;
  351 + } else {
  352 + return 0;
  353 + }
  354 +}
  355 +
  356 +/* The check_crc16 function shall return 0 is the message is ignored and the
  357 + message length if the CRC is valid. Otherwise it shall return -1 and set
  358 + errno to EMBADCRC. */
  359 +static int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg,
  360 + const int msg_length)
  361 +{
  362 + uint16_t crc_calculated;
  363 + uint16_t crc_received;
  364 + int slave = msg[0];
  365 +
  366 + /* Filter on the Modbus unit identifier (slave) in RTU mode to avoid useless
  367 + * CRC computing. */
  368 + if (slave != ctx->slave && slave != MODBUS_BROADCAST_ADDRESS) {
  369 + if (ctx->debug) {
  370 + printf("Request for slave %d ignored (not %d)\n", slave, ctx->slave);
  371 + }
  372 + /* Following call to check_confirmation handles this error */
  373 + return 0;
  374 + }
  375 +
  376 + crc_calculated = crc16(msg, msg_length - 2);
  377 + crc_received = (msg[msg_length - 2] << 8) | msg[msg_length - 1];
  378 +
  379 + /* Check CRC of msg */
  380 + if (crc_calculated == crc_received) {
  381 + return msg_length;
  382 + } else {
  383 + if (ctx->debug) {
  384 + fprintf(stderr, "ERROR CRC received 0x%0X != CRC calculated 0x%0X\n",
  385 + crc_received, crc_calculated);
  386 + }
  387 +
  388 + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) {
  389 + _modbus_rtu_flush(ctx);
  390 + }
  391 + errno = EMBBADCRC;
  392 + return -1;
  393 + }
  394 +}
  395 +
  396 +/* Sets up a serial port for RTU communications */
  397 +static int _modbus_rtu_connect(modbus_t *ctx)
  398 +{
  399 +#if defined(_WIN32)
  400 + DCB dcb;
  401 +#else
  402 + struct termios tios;
  403 + speed_t speed;
  404 + int flags;
  405 +#endif
  406 + modbus_rtu_t *ctx_rtu = ctx->backend_data;
  407 +
  408 + if (ctx->debug) {
  409 + printf("Opening %s at %d bauds (%c, %d, %d)\n",
  410 + ctx_rtu->device, ctx_rtu->baud, ctx_rtu->parity,
  411 + ctx_rtu->data_bit, ctx_rtu->stop_bit);
  412 + }
  413 +
  414 +#if defined(_WIN32)
  415 + /* Some references here:
  416 + * http://msdn.microsoft.com/en-us/library/aa450602.aspx
  417 + */
  418 + win32_ser_init(&ctx_rtu->w_ser);
  419 +
  420 + /* ctx_rtu->device should contain a string like "COMxx:" xx being a decimal
  421 + * number */
  422 + ctx_rtu->w_ser.fd = CreateFileA(ctx_rtu->device,
  423 + GENERIC_READ | GENERIC_WRITE,
  424 + 0,
  425 + NULL,
  426 + OPEN_EXISTING,
  427 + 0,
  428 + NULL);
  429 +
  430 + /* Error checking */
  431 + if (ctx_rtu->w_ser.fd == INVALID_HANDLE_VALUE) {
  432 + if (ctx->debug) {
  433 + fprintf(stderr, "ERROR Can't open the device %s (LastError %d)\n",
  434 + ctx_rtu->device, (int)GetLastError());
  435 + }
  436 + return -1;
  437 + }
  438 +
  439 + /* Save params */
  440 + ctx_rtu->old_dcb.DCBlength = sizeof(DCB);
  441 + if (!GetCommState(ctx_rtu->w_ser.fd, &ctx_rtu->old_dcb)) {
  442 + if (ctx->debug) {
  443 + fprintf(stderr, "ERROR Error getting configuration (LastError %d)\n",
  444 + (int)GetLastError());
  445 + }
  446 + CloseHandle(ctx_rtu->w_ser.fd);
  447 + ctx_rtu->w_ser.fd = INVALID_HANDLE_VALUE;
  448 + return -1;
  449 + }
  450 +
  451 + /* Build new configuration (starting from current settings) */
  452 + dcb = ctx_rtu->old_dcb;
  453 +
  454 + /* Speed setting */
  455 + switch (ctx_rtu->baud) {
  456 + case 110:
  457 + dcb.BaudRate = CBR_110;
  458 + break;
  459 + case 300:
  460 + dcb.BaudRate = CBR_300;
  461 + break;
  462 + case 600:
  463 + dcb.BaudRate = CBR_600;
  464 + break;
  465 + case 1200:
  466 + dcb.BaudRate = CBR_1200;
  467 + break;
  468 + case 2400:
  469 + dcb.BaudRate = CBR_2400;
  470 + break;
  471 + case 4800:
  472 + dcb.BaudRate = CBR_4800;
  473 + break;
  474 + case 9600:
  475 + dcb.BaudRate = CBR_9600;
  476 + break;
  477 + case 14400:
  478 + dcb.BaudRate = CBR_14400;
  479 + break;
  480 + case 19200:
  481 + dcb.BaudRate = CBR_19200;
  482 + break;
  483 + case 38400:
  484 + dcb.BaudRate = CBR_38400;
  485 + break;
  486 + case 57600:
  487 + dcb.BaudRate = CBR_57600;
  488 + break;
  489 + case 115200:
  490 + dcb.BaudRate = CBR_115200;
  491 + break;
  492 + case 230400:
  493 + /* CBR_230400 - not defined */
  494 + dcb.BaudRate = 230400;
  495 + break;
  496 + case 250000:
  497 + dcb.BaudRate = 250000;
  498 + break;
  499 + case 460800:
  500 + dcb.BaudRate = 460800;
  501 + break;
  502 + case 500000:
  503 + dcb.BaudRate = 500000;
  504 + break;
  505 + case 921600:
  506 + dcb.BaudRate = 921600;
  507 + break;
  508 + case 1000000:
  509 + dcb.BaudRate = 1000000;
  510 + break;
  511 + default:
  512 + dcb.BaudRate = CBR_9600;
  513 + if (ctx->debug) {
  514 + fprintf(stderr, "WARNING Unknown baud rate %d for %s (B9600 used)\n",
  515 + ctx_rtu->baud, ctx_rtu->device);
  516 + }
  517 + }
  518 +
  519 + /* Data bits */
  520 + switch (ctx_rtu->data_bit) {
  521 + case 5:
  522 + dcb.ByteSize = 5;
  523 + break;
  524 + case 6:
  525 + dcb.ByteSize = 6;
  526 + break;
  527 + case 7:
  528 + dcb.ByteSize = 7;
  529 + break;
  530 + case 8:
  531 + default:
  532 + dcb.ByteSize = 8;
  533 + break;
  534 + }
  535 +
  536 + /* Stop bits */
  537 + if (ctx_rtu->stop_bit == 1)
  538 + dcb.StopBits = ONESTOPBIT;
  539 + else /* 2 */
  540 + dcb.StopBits = TWOSTOPBITS;
  541 +
  542 + /* Parity */
  543 + if (ctx_rtu->parity == 'N') {
  544 + dcb.Parity = NOPARITY;
  545 + dcb.fParity = FALSE;
  546 + } else if (ctx_rtu->parity == 'E') {
  547 + dcb.Parity = EVENPARITY;
  548 + dcb.fParity = TRUE;
  549 + } else {
  550 + /* odd */
  551 + dcb.Parity = ODDPARITY;
  552 + dcb.fParity = TRUE;
  553 + }
  554 +
  555 + /* Hardware handshaking left as default settings retrieved */
  556 +
  557 + /* No software handshaking */
  558 + dcb.fTXContinueOnXoff = TRUE;
  559 + dcb.fOutX = FALSE;
  560 + dcb.fInX = FALSE;
  561 +
  562 + /* Binary mode (it's the only supported on Windows anyway) */
  563 + dcb.fBinary = TRUE;
  564 +
  565 + /* Don't want errors to be blocking */
  566 + dcb.fAbortOnError = FALSE;
  567 +
  568 + //***Not part of libmodbus - added for QModMaster***//
  569 + dcb.fRtsControl = ctx_rtu->rts;
  570 +
  571 + /* Setup port */
  572 + if (!SetCommState(ctx_rtu->w_ser.fd, &dcb)) {
  573 + if (ctx->debug) {
  574 + fprintf(stderr, "ERROR Error setting new configuration (LastError %d)\n",
  575 + (int)GetLastError());
  576 + }
  577 + CloseHandle(ctx_rtu->w_ser.fd);
  578 + ctx_rtu->w_ser.fd = INVALID_HANDLE_VALUE;
  579 + return -1;
  580 + }
  581 +#else
  582 + /* The O_NOCTTY flag tells UNIX that this program doesn't want
  583 + to be the "controlling terminal" for that port. If you
  584 + don't specify this then any input (such as keyboard abort
  585 + signals and so forth) will affect your process
  586 +
  587 + Timeouts are ignored in canonical input mode or when the
  588 + NDELAY option is set on the file via open or fcntl */
  589 + flags = O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL;
  590 +#ifdef O_CLOEXEC
  591 + flags |= O_CLOEXEC;
  592 +#endif
  593 +
  594 + ctx->s = open(ctx_rtu->device, flags);
  595 + if (ctx->s == -1) {
  596 + if (ctx->debug) {
  597 + fprintf(stderr, "ERROR Can't open the device %s (%s)\n",
  598 + ctx_rtu->device, strerror(errno));
  599 + }
  600 + return -1;
  601 + }
  602 +
  603 + /* Save */
  604 + tcgetattr(ctx->s, &ctx_rtu->old_tios);
  605 +
  606 + memset(&tios, 0, sizeof(struct termios));
  607 +
  608 + /* C_ISPEED Input baud (new interface)
  609 + C_OSPEED Output baud (new interface)
  610 + */
  611 + switch (ctx_rtu->baud) {
  612 + case 110:
  613 + speed = B110;
  614 + break;
  615 + case 300:
  616 + speed = B300;
  617 + break;
  618 + case 600:
  619 + speed = B600;
  620 + break;
  621 + case 1200:
  622 + speed = B1200;
  623 + break;
  624 + case 2400:
  625 + speed = B2400;
  626 + break;
  627 + case 4800:
  628 + speed = B4800;
  629 + break;
  630 + case 9600:
  631 + speed = B9600;
  632 + break;
  633 + case 19200:
  634 + speed = B19200;
  635 + break;
  636 + case 38400:
  637 + speed = B38400;
  638 + break;
  639 +#ifdef B57600
  640 + case 57600:
  641 + speed = B57600;
  642 + break;
  643 +#endif
  644 +#ifdef B115200
  645 + case 115200:
  646 + speed = B115200;
  647 + break;
  648 +#endif
  649 +#ifdef B230400
  650 + case 230400:
  651 + speed = B230400;
  652 + break;
  653 +#endif
  654 +#ifdef B460800
  655 + case 460800:
  656 + speed = B460800;
  657 + break;
  658 +#endif
  659 +#ifdef B500000
  660 + case 500000:
  661 + speed = B500000;
  662 + break;
  663 +#endif
  664 +#ifdef B576000
  665 + case 576000:
  666 + speed = B576000;
  667 + break;
  668 +#endif
  669 +#ifdef B921600
  670 + case 921600:
  671 + speed = B921600;
  672 + break;
  673 +#endif
  674 +#ifdef B1000000
  675 + case 1000000:
  676 + speed = B1000000;
  677 + break;
  678 +#endif
  679 +#ifdef B1152000
  680 + case 1152000:
  681 + speed = B1152000;
  682 + break;
  683 +#endif
  684 +#ifdef B1500000
  685 + case 1500000:
  686 + speed = B1500000;
  687 + break;
  688 +#endif
  689 +#ifdef B2500000
  690 + case 2500000:
  691 + speed = B2500000;
  692 + break;
  693 +#endif
  694 +#ifdef B3000000
  695 + case 3000000:
  696 + speed = B3000000;
  697 + break;
  698 +#endif
  699 +#ifdef B3500000
  700 + case 3500000:
  701 + speed = B3500000;
  702 + break;
  703 +#endif
  704 +#ifdef B4000000
  705 + case 4000000:
  706 + speed = B4000000;
  707 + break;
  708 +#endif
  709 + default:
  710 + speed = B9600;
  711 + if (ctx->debug) {
  712 + fprintf(stderr,
  713 + "WARNING Unknown baud rate %d for %s (B9600 used)\n",
  714 + ctx_rtu->baud, ctx_rtu->device);
  715 + }
  716 + }
  717 +
  718 + /* Set the baud rate */
  719 + if ((cfsetispeed(&tios, speed) < 0) ||
  720 + (cfsetospeed(&tios, speed) < 0)) {
  721 + close(ctx->s);
  722 + ctx->s = -1;
  723 + return -1;
  724 + }
  725 +
  726 + /* C_CFLAG Control options
  727 + CLOCAL Local line - do not change "owner" of port
  728 + CREAD Enable receiver
  729 + */
  730 + tios.c_cflag |= (CREAD | CLOCAL);
  731 + /* CSIZE, HUPCL, CRTSCTS (hardware flow control) */
  732 +
  733 + /* Set data bits (5, 6, 7, 8 bits)
  734 + CSIZE Bit mask for data bits
  735 + */
  736 + tios.c_cflag &= ~CSIZE;
  737 + switch (ctx_rtu->data_bit) {
  738 + case 5:
  739 + tios.c_cflag |= CS5;
  740 + break;
  741 + case 6:
  742 + tios.c_cflag |= CS6;
  743 + break;
  744 + case 7:
  745 + tios.c_cflag |= CS7;
  746 + break;
  747 + case 8:
  748 + default:
  749 + tios.c_cflag |= CS8;
  750 + break;
  751 + }
  752 +
  753 + /* Stop bit (1 or 2) */
  754 + if (ctx_rtu->stop_bit == 1)
  755 + tios.c_cflag &=~ CSTOPB;
  756 + else /* 2 */
  757 + tios.c_cflag |= CSTOPB;
  758 +
  759 + /* PARENB Enable parity bit
  760 + PARODD Use odd parity instead of even */
  761 + if (ctx_rtu->parity == 'N') {
  762 + /* None */
  763 + tios.c_cflag &=~ PARENB;
  764 + } else if (ctx_rtu->parity == 'E') {
  765 + /* Even */
  766 + tios.c_cflag |= PARENB;
  767 + tios.c_cflag &=~ PARODD;
  768 + } else {
  769 + /* Odd */
  770 + tios.c_cflag |= PARENB;
  771 + tios.c_cflag |= PARODD;
  772 + }
  773 +
  774 + /* Read the man page of termios if you need more information. */
  775 +
  776 + /* This field isn't used on POSIX systems
  777 + tios.c_line = 0;
  778 + */
  779 +
  780 + /* C_LFLAG Line options
  781 +
  782 + ISIG Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals
  783 + ICANON Enable canonical input (else raw)
  784 + XCASE Map uppercase \lowercase (obsolete)
  785 + ECHO Enable echoing of input characters
  786 + ECHOE Echo erase character as BS-SP-BS
  787 + ECHOK Echo NL after kill character
  788 + ECHONL Echo NL
  789 + NOFLSH Disable flushing of input buffers after
  790 + interrupt or quit characters
  791 + IEXTEN Enable extended functions
  792 + ECHOCTL Echo control characters as ^char and delete as ~?
  793 + ECHOPRT Echo erased character as character erased
  794 + ECHOKE BS-SP-BS entire line on line kill
  795 + FLUSHO Output being flushed
  796 + PENDIN Retype pending input at next read or input char
  797 + TOSTOP Send SIGTTOU for background output
  798 +
  799 + Canonical input is line-oriented. Input characters are put
  800 + into a buffer which can be edited interactively by the user
  801 + until a CR (carriage return) or LF (line feed) character is
  802 + received.
  803 +
  804 + Raw input is unprocessed. Input characters are passed
  805 + through exactly as they are received, when they are
  806 + received. Generally you'll deselect the ICANON, ECHO,
  807 + ECHOE, and ISIG options when using raw input
  808 + */
  809 +
  810 + /* Raw input */
  811 + tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
  812 +
  813 + /* C_IFLAG Input options
  814 +
  815 + Constant Description
  816 + INPCK Enable parity check
  817 + IGNPAR Ignore parity errors
  818 + PARMRK Mark parity errors
  819 + ISTRIP Strip parity bits
  820 + IXON Enable software flow control (outgoing)
  821 + IXOFF Enable software flow control (incoming)
  822 + IXANY Allow any character to start flow again
  823 + IGNBRK Ignore break condition
  824 + BRKINT Send a SIGINT when a break condition is detected
  825 + INLCR Map NL to CR
  826 + IGNCR Ignore CR
  827 + ICRNL Map CR to NL
  828 + IUCLC Map uppercase to lowercase
  829 + IMAXBEL Echo BEL on input line too long
  830 + */
  831 + if (ctx_rtu->parity == 'N') {
  832 + /* None */
  833 + tios.c_iflag &= ~INPCK;
  834 + } else {
  835 + tios.c_iflag |= INPCK;
  836 + }
  837 +
  838 + /* Software flow control is disabled */
  839 + tios.c_iflag &= ~(IXON | IXOFF | IXANY);
  840 +
  841 + /* C_OFLAG Output options
  842 + OPOST Postprocess output (not set = raw output)
  843 + ONLCR Map NL to CR-NL
  844 +
  845 + ONCLR ant others needs OPOST to be enabled
  846 + */
  847 +
  848 + /* Raw ouput */
  849 + tios.c_oflag &=~ OPOST;
  850 +
  851 + /* C_CC Control characters
  852 + VMIN Minimum number of characters to read
  853 + VTIME Time to wait for data (tenths of seconds)
  854 +
  855 + UNIX serial interface drivers provide the ability to
  856 + specify character and packet timeouts. Two elements of the
  857 + c_cc array are used for timeouts: VMIN and VTIME. Timeouts
  858 + are ignored in canonical input mode or when the NDELAY
  859 + option is set on the file via open or fcntl.
  860 +
  861 + VMIN specifies the minimum number of characters to read. If
  862 + it is set to 0, then the VTIME value specifies the time to
  863 + wait for every character read. Note that this does not mean
  864 + that a read call for N bytes will wait for N characters to
  865 + come in. Rather, the timeout will apply to the first
  866 + character and the read call will return the number of
  867 + characters immediately available (up to the number you
  868 + request).
  869 +
  870 + If VMIN is non-zero, VTIME specifies the time to wait for
  871 + the first character read. If a character is read within the
  872 + time given, any read will block (wait) until all VMIN
  873 + characters are read. That is, once the first character is
  874 + read, the serial interface driver expects to receive an
  875 + entire packet of characters (VMIN bytes total). If no
  876 + character is read within the time allowed, then the call to
  877 + read returns 0. This method allows you to tell the serial
  878 + driver you need exactly N bytes and any read call will
  879 + return 0 or N bytes. However, the timeout only applies to
  880 + the first character read, so if for some reason the driver
  881 + misses one character inside the N byte packet then the read
  882 + call could block forever waiting for additional input
  883 + characters.
  884 +
  885 + VTIME specifies the amount of time to wait for incoming
  886 + characters in tenths of seconds. If VTIME is set to 0 (the
  887 + default), reads will block (wait) indefinitely unless the
  888 + NDELAY option is set on the port with open or fcntl.
  889 + */
  890 + /* Unused because we use open with the NDELAY option */
  891 + tios.c_cc[VMIN] = 0;
  892 + tios.c_cc[VTIME] = 0;
  893 +
  894 + if (tcsetattr(ctx->s, TCSANOW, &tios) < 0) {
  895 + close(ctx->s);
  896 + ctx->s = -1;
  897 + return -1;
  898 + }
  899 +#endif
  900 +
  901 + return 0;
  902 +}
  903 +
  904 +int modbus_rtu_set_serial_mode(modbus_t *ctx, int mode)
  905 +{
  906 + if (ctx == NULL) {
  907 + errno = EINVAL;
  908 + return -1;
  909 + }
  910 +
  911 + if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {
  912 +#if HAVE_DECL_TIOCSRS485
  913 + modbus_rtu_t *ctx_rtu = ctx->backend_data;
  914 + struct serial_rs485 rs485conf;
  915 + memset(&rs485conf, 0x0, sizeof(struct serial_rs485));
  916 +
  917 + if (mode == MODBUS_RTU_RS485) {
  918 + rs485conf.flags = SER_RS485_ENABLED;
  919 + if (ioctl(ctx->s, TIOCSRS485, &rs485conf) < 0) {
  920 + return -1;
  921 + }
  922 +
  923 + ctx_rtu->serial_mode = MODBUS_RTU_RS485;
  924 + return 0;
  925 + } else if (mode == MODBUS_RTU_RS232) {
  926 + /* Turn off RS485 mode only if required */
  927 + if (ctx_rtu->serial_mode == MODBUS_RTU_RS485) {
  928 + /* The ioctl call is avoided because it can fail on some RS232 ports */
  929 + if (ioctl(ctx->s, TIOCSRS485, &rs485conf) < 0) {
  930 + return -1;
  931 + }
  932 + }
  933 + ctx_rtu->serial_mode = MODBUS_RTU_RS232;
  934 + return 0;
  935 + }
  936 +#else
  937 + if (ctx->debug) {
  938 + fprintf(stderr, "This function isn't supported on your platform\n");
  939 + }
  940 + errno = ENOTSUP;
  941 + return -1;
  942 +#endif
  943 + }
  944 +
  945 + /* Wrong backend and invalid mode specified */
  946 + errno = EINVAL;
  947 + return -1;
  948 +}
  949 +
  950 +int modbus_rtu_get_serial_mode(modbus_t *ctx)
  951 +{
  952 + if (ctx == NULL) {
  953 + errno = EINVAL;
  954 + return -1;
  955 + }
  956 +
  957 + if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {
  958 +#if HAVE_DECL_TIOCSRS485
  959 + modbus_rtu_t *ctx_rtu = ctx->backend_data;
  960 + return ctx_rtu->serial_mode;
  961 +#else
  962 + if (ctx->debug) {
  963 + fprintf(stderr, "This function isn't supported on your platform\n");
  964 + }
  965 + errno = ENOTSUP;
  966 + return -1;
  967 +#endif
  968 + } else {
  969 + errno = EINVAL;
  970 + return -1;
  971 + }
  972 +}
  973 +
  974 +int modbus_rtu_get_rts(modbus_t *ctx)
  975 +{
  976 + if (ctx == NULL) {
  977 + errno = EINVAL;
  978 + return -1;
  979 + }
  980 +
  981 + if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {
  982 +#if HAVE_DECL_TIOCM_RTS
  983 + modbus_rtu_t *ctx_rtu = ctx->backend_data;
  984 + return ctx_rtu->rts;
  985 +#else
  986 + if (ctx->debug) {
  987 + fprintf(stderr, "This function isn't supported on your platform\n");
  988 + }
  989 + errno = ENOTSUP;
  990 + return -1;
  991 +#endif
  992 + } else {
  993 + errno = EINVAL;
  994 + return -1;
  995 + }
  996 +}
  997 +
  998 +int modbus_rtu_set_rts(modbus_t *ctx, int mode)
  999 +{
  1000 + if (ctx == NULL) {
  1001 + errno = EINVAL;
  1002 + return -1;
  1003 + }
  1004 +
  1005 + if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {
  1006 +#if HAVE_DECL_TIOCM_RTS
  1007 + modbus_rtu_t *ctx_rtu = ctx->backend_data;
  1008 +
  1009 + if (mode == MODBUS_RTU_RTS_NONE || mode == MODBUS_RTU_RTS_UP ||
  1010 + mode == MODBUS_RTU_RTS_DOWN) {
  1011 + ctx_rtu->rts = mode;
  1012 +
  1013 + /* Set the RTS bit in order to not reserve the RS485 bus */
  1014 + ctx_rtu->set_rts(ctx, ctx_rtu->rts != MODBUS_RTU_RTS_UP);
  1015 +
  1016 + return 0;
  1017 + } else {
  1018 + errno = EINVAL;
  1019 + return -1;
  1020 + }
  1021 +#else
  1022 + if (ctx->debug) {
  1023 + fprintf(stderr, "This function isn't supported on your platform\n");
  1024 + }
  1025 + errno = ENOTSUP;
  1026 + return -1;
  1027 +#endif
  1028 + }
  1029 + /* Wrong backend or invalid mode specified */
  1030 + errno = EINVAL;
  1031 + return -1;
  1032 +}
  1033 +
  1034 +int modbus_rtu_set_custom_rts(modbus_t *ctx, void (*set_rts) (modbus_t *ctx, int on))
  1035 +{
  1036 + if (ctx == NULL) {
  1037 + errno = EINVAL;
  1038 + return -1;
  1039 + }
  1040 +
  1041 + if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {
  1042 +#if HAVE_DECL_TIOCM_RTS
  1043 + modbus_rtu_t *ctx_rtu = ctx->backend_data;
  1044 + ctx_rtu->set_rts = set_rts;
  1045 + return 0;
  1046 +#else
  1047 + if (ctx->debug) {
  1048 + fprintf(stderr, "This function isn't supported on your platform\n");
  1049 + }
  1050 + errno = ENOTSUP;
  1051 + return -1;
  1052 +#endif
  1053 + } else {
  1054 + errno = EINVAL;
  1055 + return -1;
  1056 + }
  1057 +}
  1058 +
  1059 +int modbus_rtu_get_rts_delay(modbus_t *ctx)
  1060 +{
  1061 + if (ctx == NULL) {
  1062 + errno = EINVAL;
  1063 + return -1;
  1064 + }
  1065 +
  1066 + if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {
  1067 +#if HAVE_DECL_TIOCM_RTS
  1068 + modbus_rtu_t *ctx_rtu;
  1069 + ctx_rtu = (modbus_rtu_t *)ctx->backend_data;
  1070 + return ctx_rtu->rts_delay;
  1071 +#else
  1072 + if (ctx->debug) {
  1073 + fprintf(stderr, "This function isn't supported on your platform\n");
  1074 + }
  1075 + errno = ENOTSUP;
  1076 + return -1;
  1077 +#endif
  1078 + } else {
  1079 + errno = EINVAL;
  1080 + return -1;
  1081 + }
  1082 +}
  1083 +
  1084 +int modbus_rtu_set_rts_delay(modbus_t *ctx, int us)
  1085 +{
  1086 + if (ctx == NULL || us < 0) {
  1087 + errno = EINVAL;
  1088 + return -1;
  1089 + }
  1090 +
  1091 + if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {
  1092 +#if HAVE_DECL_TIOCM_RTS
  1093 + modbus_rtu_t *ctx_rtu;
  1094 + ctx_rtu = (modbus_rtu_t *)ctx->backend_data;
  1095 + ctx_rtu->rts_delay = us;
  1096 + return 0;
  1097 +#else
  1098 + if (ctx->debug) {
  1099 + fprintf(stderr, "This function isn't supported on your platform\n");
  1100 + }
  1101 + errno = ENOTSUP;
  1102 + return -1;
  1103 +#endif
  1104 + } else {
  1105 + errno = EINVAL;
  1106 + return -1;
  1107 + }
  1108 +}
  1109 +
  1110 +static void _modbus_rtu_close(modbus_t *ctx)
  1111 +{
  1112 + /* Restore line settings and close file descriptor in RTU mode */
  1113 + modbus_rtu_t *ctx_rtu = ctx->backend_data;
  1114 +
  1115 +#if defined(_WIN32)
  1116 + /* Revert settings */
  1117 + if (!SetCommState(ctx_rtu->w_ser.fd, &ctx_rtu->old_dcb) && ctx->debug) {
  1118 + fprintf(stderr, "ERROR Couldn't revert to configuration (LastError %d)\n",
  1119 + (int)GetLastError());
  1120 + }
  1121 +
  1122 + if (!CloseHandle(ctx_rtu->w_ser.fd) && ctx->debug) {
  1123 + fprintf(stderr, "ERROR Error while closing handle (LastError %d)\n",
  1124 + (int)GetLastError());
  1125 + }
  1126 +#else
  1127 + if (ctx->s != -1) {
  1128 + tcsetattr(ctx->s, TCSANOW, &ctx_rtu->old_tios);
  1129 + close(ctx->s);
  1130 + ctx->s = -1;
  1131 + }
  1132 +#endif
  1133 +}
  1134 +
  1135 +static int _modbus_rtu_flush(modbus_t *ctx)
  1136 +{
  1137 +#if defined(_WIN32)
  1138 + modbus_rtu_t *ctx_rtu = ctx->backend_data;
  1139 + ctx_rtu->w_ser.n_bytes = 0;
  1140 + return (PurgeComm(ctx_rtu->w_ser.fd, PURGE_RXCLEAR) == FALSE);
  1141 +#else
  1142 + return tcflush(ctx->s, TCIOFLUSH);
  1143 +#endif
  1144 +}
  1145 +
  1146 +static int _modbus_rtu_select(modbus_t *ctx, fd_set *rset,
  1147 + struct timeval *tv, int length_to_read)
  1148 +{
  1149 + int s_rc;
  1150 +#if defined(_WIN32)
  1151 + s_rc = win32_ser_select(&((modbus_rtu_t *)ctx->backend_data)->w_ser,
  1152 + length_to_read, tv);
  1153 + if (s_rc == 0) {
  1154 + errno = ETIMEDOUT;
  1155 + return -1;
  1156 + }
  1157 +
  1158 + if (s_rc < 0) {
  1159 + return -1;
  1160 + }
  1161 +#else
  1162 + while ((s_rc = select(ctx->s+1, rset, NULL, NULL, tv)) == -1) {
  1163 + if (errno == EINTR) {
  1164 + if (ctx->debug) {
  1165 + fprintf(stderr, "A non blocked signal was caught\n");
  1166 + }
  1167 + /* Necessary after an error */
  1168 + FD_ZERO(rset);
  1169 + FD_SET(ctx->s, rset);
  1170 + } else {
  1171 + return -1;
  1172 + }
  1173 + }
  1174 +
  1175 + if (s_rc == 0) {
  1176 + /* Timeout */
  1177 + errno = ETIMEDOUT;
  1178 + return -1;
  1179 + }
  1180 +#endif
  1181 +
  1182 + return s_rc;
  1183 +}
  1184 +
  1185 +static void _modbus_rtu_free(modbus_t *ctx) {
  1186 + free(((modbus_rtu_t*)ctx->backend_data)->device);
  1187 + free(ctx->backend_data);
  1188 + free(ctx);
  1189 +}
  1190 +
  1191 +const modbus_backend_t _modbus_rtu_backend = {
  1192 + _MODBUS_BACKEND_TYPE_RTU,
  1193 + _MODBUS_RTU_HEADER_LENGTH,
  1194 + _MODBUS_RTU_CHECKSUM_LENGTH,
  1195 + MODBUS_RTU_MAX_ADU_LENGTH,
  1196 + _modbus_set_slave,
  1197 + _modbus_rtu_build_request_basis,
  1198 + _modbus_rtu_build_response_basis,
  1199 + _modbus_rtu_prepare_response_tid,
  1200 + _modbus_rtu_send_msg_pre,
  1201 + _modbus_rtu_send,
  1202 + _modbus_rtu_receive,
  1203 + _modbus_rtu_recv,
  1204 + _modbus_rtu_check_integrity,
  1205 + _modbus_rtu_pre_check_confirmation,
  1206 + _modbus_rtu_connect,
  1207 + _modbus_rtu_close,
  1208 + _modbus_rtu_flush,
  1209 + _modbus_rtu_select,
  1210 + _modbus_rtu_free
  1211 +};
  1212 +
  1213 +//***Not part of libmodbus - rts param added for QModMaster***//
  1214 +modbus_t* modbus_new_rtu(const char *device,
  1215 + int baud, char parity, int data_bit,
  1216 + int stop_bit, int rts)
  1217 +{
  1218 + modbus_t *ctx;
  1219 + modbus_rtu_t *ctx_rtu;
  1220 +
  1221 + /* Check device argument */
  1222 + if (device == NULL || *device == 0) {
  1223 + fprintf(stderr, "The device string is empty\n");
  1224 + errno = EINVAL;
  1225 + return NULL;
  1226 + }
  1227 +
  1228 + /* Check baud argument */
  1229 + if (baud == 0) {
  1230 + fprintf(stderr, "The baud rate value must not be zero\n");
  1231 + errno = EINVAL;
  1232 + return NULL;
  1233 + }
  1234 +
  1235 + ctx = (modbus_t *)malloc(sizeof(modbus_t));
  1236 + _modbus_init_common(ctx);
  1237 + ctx->backend = &_modbus_rtu_backend;
  1238 + ctx->backend_data = (modbus_rtu_t *)malloc(sizeof(modbus_rtu_t));
  1239 + ctx_rtu = (modbus_rtu_t *)ctx->backend_data;
  1240 + ctx_rtu->device = NULL;
  1241 +
  1242 + /* Device name and \0 */
  1243 + ctx_rtu->device = (char *)malloc((strlen(device) + 1) * sizeof(char));
  1244 + strcpy(ctx_rtu->device, device);
  1245 +
  1246 + ctx_rtu->baud = baud;
  1247 + if (parity == 'N' || parity == 'E' || parity == 'O') {
  1248 + ctx_rtu->parity = parity;
  1249 + } else {
  1250 + modbus_free(ctx);
  1251 + errno = EINVAL;
  1252 + return NULL;
  1253 + }
  1254 + ctx_rtu->data_bit = data_bit;
  1255 + ctx_rtu->stop_bit = stop_bit;
  1256 +
  1257 +#if HAVE_DECL_TIOCSRS485
  1258 + /* The RS232 mode has been set by default */
  1259 + ctx_rtu->serial_mode = MODBUS_RTU_RS232;
  1260 +#endif
  1261 +
  1262 +#if HAVE_DECL_TIOCM_RTS
  1263 + /* The RTS use has been set by default */
  1264 + ctx_rtu->rts = MODBUS_RTU_RTS_NONE;
  1265 +
  1266 + /* Calculate estimated time in micro second to send one byte */
  1267 + ctx_rtu->onebyte_time = 1000000 * (1 + data_bit + (parity == 'N' ? 0 : 1) + stop_bit) / baud;
  1268 +
  1269 + /* The internal function is used by default to set RTS */
  1270 + ctx_rtu->set_rts = _modbus_rtu_ioctl_rts;
  1271 +
  1272 + /* The delay before and after transmission when toggling the RTS pin */
  1273 + ctx_rtu->rts_delay = ctx_rtu->onebyte_time;
  1274 +#endif
  1275 +
  1276 +//***Not part of libmodbus - added for QModMaster***//
  1277 +#if defined(_WIN32)
  1278 + ctx_rtu->rts = rts;
  1279 +#endif
  1280 +
  1281 + ctx_rtu->confirmation_to_ignore = FALSE;
  1282 +
  1283 + return ctx;
  1284 +}
... ...
3rdparty/libmodbus/modbus-rtu.h 0 โ†’ 100644
  1 +/*
  2 + * Copyright ยฉ 2001-2011 Stรฉphane Raimbault <stephane.raimbault@gmail.com>
  3 + *
  4 + * SPDX-License-Identifier: LGPL-2.1+
  5 + */
  6 +
  7 +#ifndef MODBUS_RTU_H
  8 +#define MODBUS_RTU_H
  9 +
  10 +#include "modbus.h"
  11 +
  12 +MODBUS_BEGIN_DECLS
  13 +
  14 +/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5
  15 + * RS232 / RS485 ADU = 253 bytes + slave (1 byte) + CRC (2 bytes) = 256 bytes
  16 + */
  17 +#define MODBUS_RTU_MAX_ADU_LENGTH 256
  18 +
  19 +//***Not part of libmodbus - rts param added for QModMaster***//
  20 +MODBUS_API modbus_t* modbus_new_rtu(const char *device, int baud, char parity,
  21 + int data_bit, int stop_bit, int rts);
  22 +
  23 +#define MODBUS_RTU_RS232 0
  24 +#define MODBUS_RTU_RS485 1
  25 +
  26 +MODBUS_API int modbus_rtu_set_serial_mode(modbus_t *ctx, int mode);
  27 +MODBUS_API int modbus_rtu_get_serial_mode(modbus_t *ctx);
  28 +
  29 +#define MODBUS_RTU_RTS_NONE 0
  30 +#define MODBUS_RTU_RTS_UP 1
  31 +#define MODBUS_RTU_RTS_DOWN 2
  32 +
  33 +MODBUS_API int modbus_rtu_set_rts(modbus_t *ctx, int mode);
  34 +MODBUS_API int modbus_rtu_get_rts(modbus_t *ctx);
  35 +
  36 +MODBUS_API int modbus_rtu_set_custom_rts(modbus_t *ctx, void (*set_rts) (modbus_t *ctx, int on));
  37 +
  38 +MODBUS_API int modbus_rtu_set_rts_delay(modbus_t *ctx, int us);
  39 +MODBUS_API int modbus_rtu_get_rts_delay(modbus_t *ctx);
  40 +
  41 +MODBUS_END_DECLS
  42 +
  43 +#endif /* MODBUS_RTU_H */
... ...
3rdparty/libmodbus/modbus-tcp-private.h 0 โ†’ 100644
  1 +/*
  2 + * Copyright ยฉ 2001-2011 Stรฉphane Raimbault <stephane.raimbault@gmail.com>
  3 + *
  4 + * SPDX-License-Identifier: LGPL-2.1+
  5 + */
  6 +
  7 +#ifndef MODBUS_TCP_PRIVATE_H
  8 +#define MODBUS_TCP_PRIVATE_H
  9 +
  10 +#define _MODBUS_TCP_HEADER_LENGTH 7
  11 +#define _MODBUS_TCP_PRESET_REQ_LENGTH 12
  12 +#define _MODBUS_TCP_PRESET_RSP_LENGTH 8
  13 +
  14 +#define _MODBUS_TCP_CHECKSUM_LENGTH 0
  15 +
  16 +/* In both structures, the transaction ID must be placed on first position
  17 + to have a quick access not dependant of the TCP backend */
  18 +typedef struct _modbus_tcp {
  19 + /* Extract from MODBUS Messaging on TCP/IP Implementation Guide V1.0b
  20 + (page 23/46):
  21 + The transaction identifier is used to associate the future response
  22 + with the request. This identifier is unique on each TCP connection. */
  23 + uint16_t t_id;
  24 + /* TCP port */
  25 + int port;
  26 + /* IP address */
  27 + char ip[16];
  28 +} modbus_tcp_t;
  29 +
  30 +#define _MODBUS_TCP_PI_NODE_LENGTH 1025
  31 +#define _MODBUS_TCP_PI_SERVICE_LENGTH 32
  32 +
  33 +typedef struct _modbus_tcp_pi {
  34 + /* Transaction ID */
  35 + uint16_t t_id;
  36 + /* TCP port */
  37 + int port;
  38 + /* Node */
  39 + char node[_MODBUS_TCP_PI_NODE_LENGTH];
  40 + /* Service */
  41 + char service[_MODBUS_TCP_PI_SERVICE_LENGTH];
  42 +} modbus_tcp_pi_t;
  43 +
  44 +#endif /* MODBUS_TCP_PRIVATE_H */
... ...
3rdparty/libmodbus/modbus-tcp.c 0 โ†’ 100644
  1 +/*
  2 + * Copyright ยฉ 2001-2013 Stรฉphane Raimbault <stephane.raimbault@gmail.com>
  3 + *
  4 + * SPDX-License-Identifier: LGPL-2.1+
  5 + */
  6 +
  7 +#include <stdio.h>
  8 +#include <stdlib.h>
  9 +#include <string.h>
  10 +#include <errno.h>
  11 +#ifndef _MSC_VER
  12 +#include <unistd.h>
  13 +#endif
  14 +#include <signal.h>
  15 +#include <sys/types.h>
  16 +
  17 +#if defined(_WIN32)
  18 +# define OS_WIN32
  19 +/* ws2_32.dll has getaddrinfo and freeaddrinfo on Windows XP and later.
  20 + * minwg32 headers check WINVER before allowing the use of these */
  21 +# ifndef WINVER
  22 +# define WINVER 0x0501
  23 +# endif
  24 +/* Already set in modbus-tcp.h but it seems order matters in VS2005 */
  25 +# include <winsock2.h>
  26 +# include <ws2tcpip.h>
  27 +# define SHUT_RDWR 2
  28 +# define close closesocket
  29 +#else
  30 +# include <sys/socket.h>
  31 +# include <sys/ioctl.h>
  32 +
  33 +#if defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD__ < 5)
  34 +# define OS_BSD
  35 +# include <netinet/in_systm.h>
  36 +#endif
  37 +
  38 +# include <netinet/in.h>
  39 +# include <netinet/ip.h>
  40 +# include <netinet/tcp.h>
  41 +# include <arpa/inet.h>
  42 +# include <netdb.h>
  43 +#endif
  44 +
  45 +#if !defined(MSG_NOSIGNAL)
  46 +#define MSG_NOSIGNAL 0
  47 +#endif
  48 +
  49 +#if defined(_AIX) && !defined(MSG_DONTWAIT)
  50 +#define MSG_DONTWAIT MSG_NONBLOCK
  51 +#endif
  52 +
  53 +#include "modbus-private.h"
  54 +
  55 +#include "modbus-tcp.h"
  56 +#include "modbus-tcp-private.h"
  57 +
  58 +#ifdef OS_WIN32
  59 +static int _modbus_tcp_init_win32(void)
  60 +{
  61 + /* Initialise Windows Socket API */
  62 + WSADATA wsaData;
  63 +
  64 + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
  65 + fprintf(stderr, "WSAStartup() returned error code %d\n",
  66 + (unsigned int)GetLastError());
  67 + errno = EIO;
  68 + return -1;
  69 + }
  70 + return 0;
  71 +}
  72 +#endif
  73 +
  74 +static int _modbus_set_slave(modbus_t *ctx, int slave)
  75 +{
  76 + /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */
  77 + if (slave >= 0 && slave <= 247) {
  78 + ctx->slave = slave;
  79 + } else if (slave == MODBUS_TCP_SLAVE) {
  80 + /* The special value MODBUS_TCP_SLAVE (0xFF) can be used in TCP mode to
  81 + * restore the default value. */
  82 + ctx->slave = slave;
  83 + } else {
  84 + errno = EINVAL;
  85 + return -1;
  86 + }
  87 +
  88 + return 0;
  89 +}
  90 +
  91 +/* Builds a TCP request header */
  92 +static int _modbus_tcp_build_request_basis(modbus_t *ctx, int function,
  93 + int addr, int nb,
  94 + uint8_t *req)
  95 +{
  96 + modbus_tcp_t *ctx_tcp = ctx->backend_data;
  97 +
  98 + /* Increase transaction ID */
  99 + if (ctx_tcp->t_id < UINT16_MAX)
  100 + ctx_tcp->t_id++;
  101 + else
  102 + ctx_tcp->t_id = 0;
  103 + req[0] = ctx_tcp->t_id >> 8;
  104 + req[1] = ctx_tcp->t_id & 0x00ff;
  105 +
  106 + /* Protocol Modbus */
  107 + req[2] = 0;
  108 + req[3] = 0;
  109 +
  110 + /* Length will be defined later by set_req_length_tcp at offsets 4
  111 + and 5 */
  112 +
  113 + req[6] = ctx->slave;
  114 + req[7] = function;
  115 + req[8] = addr >> 8;
  116 + req[9] = addr & 0x00ff;
  117 + req[10] = nb >> 8;
  118 + req[11] = nb & 0x00ff;
  119 +
  120 + return _MODBUS_TCP_PRESET_REQ_LENGTH;
  121 +}
  122 +
  123 +/* Builds a TCP response header */
  124 +static int _modbus_tcp_build_response_basis(sft_t *sft, uint8_t *rsp)
  125 +{
  126 + /* Extract from MODBUS Messaging on TCP/IP Implementation
  127 + Guide V1.0b (page 23/46):
  128 + The transaction identifier is used to associate the future
  129 + response with the request. */
  130 + rsp[0] = sft->t_id >> 8;
  131 + rsp[1] = sft->t_id & 0x00ff;
  132 +
  133 + /* Protocol Modbus */
  134 + rsp[2] = 0;
  135 + rsp[3] = 0;
  136 +
  137 + /* Length will be set later by send_msg (4 and 5) */
  138 +
  139 + /* The slave ID is copied from the indication */
  140 + rsp[6] = sft->slave;
  141 + rsp[7] = sft->function;
  142 +
  143 + return _MODBUS_TCP_PRESET_RSP_LENGTH;
  144 +}
  145 +
  146 +
  147 +static int _modbus_tcp_prepare_response_tid(const uint8_t *req, int *req_length)
  148 +{
  149 + return (req[0] << 8) + req[1];
  150 +}
  151 +
  152 +static int _modbus_tcp_send_msg_pre(uint8_t *req, int req_length)
  153 +{
  154 + /* Substract the header length to the message length */
  155 + int mbap_length = req_length - 6;
  156 +
  157 + req[4] = mbap_length >> 8;
  158 + req[5] = mbap_length & 0x00FF;
  159 +
  160 + return req_length;
  161 +}
  162 +
  163 +static ssize_t _modbus_tcp_send(modbus_t *ctx, const uint8_t *req, int req_length)
  164 +{
  165 + /* MSG_NOSIGNAL
  166 + Requests not to send SIGPIPE on errors on stream oriented
  167 + sockets when the other end breaks the connection. The EPIPE
  168 + error is still returned. */
  169 + return send(ctx->s, (const char *)req, req_length, MSG_NOSIGNAL);
  170 +}
  171 +
  172 +static int _modbus_tcp_receive(modbus_t *ctx, uint8_t *req) {
  173 + return _modbus_receive_msg(ctx, req, MSG_INDICATION);
  174 +}
  175 +
  176 +static ssize_t _modbus_tcp_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length) {
  177 + return recv(ctx->s, (char *)rsp, rsp_length, 0);
  178 +}
  179 +
  180 +static int _modbus_tcp_check_integrity(modbus_t *ctx, uint8_t *msg, const int msg_length)
  181 +{
  182 + return msg_length;
  183 +}
  184 +
  185 +static int _modbus_tcp_pre_check_confirmation(modbus_t *ctx, const uint8_t *req,
  186 + const uint8_t *rsp, int rsp_length)
  187 +{
  188 + /* Check transaction ID */
  189 + if (req[0] != rsp[0] || req[1] != rsp[1]) {
  190 + if (ctx->debug) {
  191 + fprintf(stderr, "Invalid transaction ID received 0x%X (not 0x%X)\n",
  192 + (rsp[0] << 8) + rsp[1], (req[0] << 8) + req[1]);
  193 + }
  194 + errno = EMBBADDATA;
  195 + return -1;
  196 + }
  197 +
  198 + /* Check protocol ID */
  199 + if (rsp[2] != 0x0 && rsp[3] != 0x0) {
  200 + if (ctx->debug) {
  201 + fprintf(stderr, "Invalid protocol ID received 0x%X (not 0x0)\n",
  202 + (rsp[2] << 8) + rsp[3]);
  203 + }
  204 + errno = EMBBADDATA;
  205 + return -1;
  206 + }
  207 +
  208 + return 0;
  209 +}
  210 +
  211 +static int _modbus_tcp_set_ipv4_options(int s)
  212 +{
  213 + int rc;
  214 + int option;
  215 +
  216 + /* Set the TCP no delay flag */
  217 + /* SOL_TCP = IPPROTO_TCP */
  218 + option = 1;
  219 + rc = setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
  220 + (const void *)&option, sizeof(int));
  221 + if (rc == -1) {
  222 + return -1;
  223 + }
  224 +
  225 + /* If the OS does not offer SOCK_NONBLOCK, fall back to setting FIONBIO to
  226 + * make sockets non-blocking */
  227 + /* Do not care about the return value, this is optional */
  228 +#if !defined(SOCK_NONBLOCK) && defined(FIONBIO)
  229 +#ifdef OS_WIN32
  230 + {
  231 + /* Setting FIONBIO expects an unsigned long according to MSDN */
  232 + u_long loption = 1;
  233 + ioctlsocket(s, FIONBIO, &loption);
  234 + }
  235 +#else
  236 + option = 1;
  237 + ioctl(s, FIONBIO, &option);
  238 +#endif
  239 +#endif
  240 +
  241 +#ifndef OS_WIN32
  242 + /**
  243 + * Cygwin defines IPTOS_LOWDELAY but can't handle that flag so it's
  244 + * necessary to workaround that problem.
  245 + **/
  246 + /* Set the IP low delay option */
  247 + option = IPTOS_LOWDELAY;
  248 + rc = setsockopt(s, IPPROTO_IP, IP_TOS,
  249 + (const void *)&option, sizeof(int));
  250 + if (rc == -1) {
  251 + return -1;
  252 + }
  253 +#endif
  254 +
  255 + return 0;
  256 +}
  257 +
  258 +static int _connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen,
  259 + const struct timeval *ro_tv)
  260 +{
  261 + int rc = connect(sockfd, addr, addrlen);
  262 +
  263 +#ifdef OS_WIN32
  264 + int wsaError = 0;
  265 + if (rc == -1) {
  266 + wsaError = WSAGetLastError();
  267 + }
  268 +
  269 + if (wsaError == WSAEWOULDBLOCK || wsaError == WSAEINPROGRESS) {
  270 +#else
  271 + if (rc == -1 && errno == EINPROGRESS) {
  272 +#endif
  273 + fd_set wset;
  274 + int optval;
  275 + socklen_t optlen = sizeof(optval);
  276 + struct timeval tv = *ro_tv;
  277 +
  278 + /* Wait to be available in writing */
  279 + FD_ZERO(&wset);
  280 + FD_SET(sockfd, &wset);
  281 + rc = select(sockfd + 1, NULL, &wset, NULL, &tv);
  282 + if (rc <= 0) {
  283 + /* Timeout or fail */
  284 + return -1;
  285 + }
  286 +
  287 + /* The connection is established if SO_ERROR and optval are set to 0 */
  288 + rc = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&optval, &optlen);
  289 + if (rc == 0 && optval == 0) {
  290 + return 0;
  291 + } else {
  292 + errno = ECONNREFUSED;
  293 + return -1;
  294 + }
  295 + }
  296 + return rc;
  297 +}
  298 +
  299 +/* Establishes a modbus TCP connection with a Modbus server. */
  300 +static int _modbus_tcp_connect(modbus_t *ctx)
  301 +{
  302 + int rc;
  303 + /* Specialized version of sockaddr for Internet socket address (same size) */
  304 + struct sockaddr_in addr;
  305 + modbus_tcp_t *ctx_tcp = ctx->backend_data;
  306 + int flags = SOCK_STREAM;
  307 +
  308 +#ifdef OS_WIN32
  309 + if (_modbus_tcp_init_win32() == -1) {
  310 + return -1;
  311 + }
  312 +#endif
  313 +
  314 +#ifdef SOCK_CLOEXEC
  315 + flags |= SOCK_CLOEXEC;
  316 +#endif
  317 +
  318 +#ifdef SOCK_NONBLOCK
  319 + flags |= SOCK_NONBLOCK;
  320 +#endif
  321 +
  322 + ctx->s = socket(PF_INET, flags, 0);
  323 + if (ctx->s == -1) {
  324 + return -1;
  325 + }
  326 +
  327 + rc = _modbus_tcp_set_ipv4_options(ctx->s);
  328 + if (rc == -1) {
  329 + close(ctx->s);
  330 + ctx->s = -1;
  331 + return -1;
  332 + }
  333 +
  334 + if (ctx->debug) {
  335 + printf("Connecting to %s:%d\n", ctx_tcp->ip, ctx_tcp->port);
  336 + }
  337 +
  338 + addr.sin_family = AF_INET;
  339 + addr.sin_port = htons(ctx_tcp->port);
  340 + addr.sin_addr.s_addr = inet_addr(ctx_tcp->ip);
  341 + rc = _connect(ctx->s, (struct sockaddr *)&addr, sizeof(addr), &ctx->response_timeout);
  342 + if (rc == -1) {
  343 + close(ctx->s);
  344 + ctx->s = -1;
  345 + return -1;
  346 + }
  347 +
  348 + return 0;
  349 +}
  350 +
  351 +/* Establishes a modbus TCP PI connection with a Modbus server. */
  352 +static int _modbus_tcp_pi_connect(modbus_t *ctx)
  353 +{
  354 + int rc;
  355 + struct addrinfo *ai_list;
  356 + struct addrinfo *ai_ptr;
  357 + struct addrinfo ai_hints;
  358 + modbus_tcp_pi_t *ctx_tcp_pi = ctx->backend_data;
  359 +
  360 +#ifdef OS_WIN32
  361 + if (_modbus_tcp_init_win32() == -1) {
  362 + return -1;
  363 + }
  364 +#endif
  365 +
  366 + memset(&ai_hints, 0, sizeof(ai_hints));
  367 +#ifdef AI_ADDRCONFIG
  368 + ai_hints.ai_flags |= AI_ADDRCONFIG;
  369 +#endif
  370 + ai_hints.ai_family = AF_UNSPEC;
  371 + ai_hints.ai_socktype = SOCK_STREAM;
  372 + ai_hints.ai_addr = NULL;
  373 + ai_hints.ai_canonname = NULL;
  374 + ai_hints.ai_next = NULL;
  375 +
  376 + ai_list = NULL;
  377 + rc = getaddrinfo(ctx_tcp_pi->node, ctx_tcp_pi->service,
  378 + &ai_hints, &ai_list);
  379 + if (rc != 0) {
  380 + if (ctx->debug) {
  381 + fprintf(stderr, "Error returned by getaddrinfo: %s\n", gai_strerror(rc));
  382 + }
  383 + errno = ECONNREFUSED;
  384 + return -1;
  385 + }
  386 +
  387 + for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) {
  388 + int flags = ai_ptr->ai_socktype;
  389 + int s;
  390 +
  391 +#ifdef SOCK_CLOEXEC
  392 + flags |= SOCK_CLOEXEC;
  393 +#endif
  394 +
  395 +#ifdef SOCK_NONBLOCK
  396 + flags |= SOCK_NONBLOCK;
  397 +#endif
  398 +
  399 + s = socket(ai_ptr->ai_family, flags, ai_ptr->ai_protocol);
  400 + if (s < 0)
  401 + continue;
  402 +
  403 + if (ai_ptr->ai_family == AF_INET)
  404 + _modbus_tcp_set_ipv4_options(s);
  405 +
  406 + if (ctx->debug) {
  407 + printf("Connecting to [%s]:%s\n", ctx_tcp_pi->node, ctx_tcp_pi->service);
  408 + }
  409 +
  410 + rc = _connect(s, ai_ptr->ai_addr, ai_ptr->ai_addrlen, &ctx->response_timeout);
  411 + if (rc == -1) {
  412 + close(s);
  413 + continue;
  414 + }
  415 +
  416 + ctx->s = s;
  417 + break;
  418 + }
  419 +
  420 + freeaddrinfo(ai_list);
  421 +
  422 + if (ctx->s < 0) {
  423 + return -1;
  424 + }
  425 +
  426 + return 0;
  427 +}
  428 +
  429 +/* Closes the network connection and socket in TCP mode */
  430 +static void _modbus_tcp_close(modbus_t *ctx)
  431 +{
  432 + if (ctx->s != -1) {
  433 + shutdown(ctx->s, SHUT_RDWR);
  434 + close(ctx->s);
  435 + ctx->s = -1;
  436 + }
  437 +}
  438 +
  439 +static int _modbus_tcp_flush(modbus_t *ctx)
  440 +{
  441 + int rc;
  442 + int rc_sum = 0;
  443 +
  444 + do {
  445 + /* Extract the garbage from the socket */
  446 + char devnull[MODBUS_TCP_MAX_ADU_LENGTH];
  447 +#ifndef OS_WIN32
  448 + rc = recv(ctx->s, devnull, MODBUS_TCP_MAX_ADU_LENGTH, MSG_DONTWAIT);
  449 +#else
  450 + /* On Win32, it's a bit more complicated to not wait */
  451 + fd_set rset;
  452 + struct timeval tv;
  453 +
  454 + tv.tv_sec = 0;
  455 + tv.tv_usec = 0;
  456 + FD_ZERO(&rset);
  457 + FD_SET(ctx->s, &rset);
  458 + rc = select(ctx->s+1, &rset, NULL, NULL, &tv);
  459 + if (rc == -1) {
  460 + return -1;
  461 + }
  462 +
  463 + if (rc == 1) {
  464 + /* There is data to flush */
  465 + rc = recv(ctx->s, devnull, MODBUS_TCP_MAX_ADU_LENGTH, 0);
  466 + }
  467 +#endif
  468 + if (rc > 0) {
  469 + rc_sum += rc;
  470 + }
  471 + } while (rc == MODBUS_TCP_MAX_ADU_LENGTH);
  472 +
  473 + return rc_sum;
  474 +}
  475 +
  476 +/* Listens for any request from one or many modbus masters in TCP */
  477 +int modbus_tcp_listen(modbus_t *ctx, int nb_connection)
  478 +{
  479 + int new_s;
  480 + int enable;
  481 + struct sockaddr_in addr;
  482 + modbus_tcp_t *ctx_tcp;
  483 +
  484 + if (ctx == NULL) {
  485 + errno = EINVAL;
  486 + return -1;
  487 + }
  488 +
  489 + ctx_tcp = ctx->backend_data;
  490 +
  491 +#ifdef OS_WIN32
  492 + if (_modbus_tcp_init_win32() == -1) {
  493 + return -1;
  494 + }
  495 +#endif
  496 +
  497 + new_s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  498 + if (new_s == -1) {
  499 + return -1;
  500 + }
  501 +
  502 + enable = 1;
  503 + if (setsockopt(new_s, SOL_SOCKET, SO_REUSEADDR,
  504 + (char *)&enable, sizeof(enable)) == -1) {
  505 + close(new_s);
  506 + return -1;
  507 + }
  508 +
  509 + memset(&addr, 0, sizeof(addr));
  510 + addr.sin_family = AF_INET;
  511 + /* If the modbus port is < to 1024, we need the setuid root. */
  512 + addr.sin_port = htons(ctx_tcp->port);
  513 + if (ctx_tcp->ip[0] == '0') {
  514 + /* Listen any addresses */
  515 + addr.sin_addr.s_addr = htonl(INADDR_ANY);
  516 + } else {
  517 + /* Listen only specified IP address */
  518 + addr.sin_addr.s_addr = inet_addr(ctx_tcp->ip);
  519 + }
  520 + if (bind(new_s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
  521 + close(new_s);
  522 + return -1;
  523 + }
  524 +
  525 + if (listen(new_s, nb_connection) == -1) {
  526 + close(new_s);
  527 + return -1;
  528 + }
  529 +
  530 + return new_s;
  531 +}
  532 +
  533 +int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection)
  534 +{
  535 + int rc;
  536 + struct addrinfo *ai_list;
  537 + struct addrinfo *ai_ptr;
  538 + struct addrinfo ai_hints;
  539 + const char *node;
  540 + const char *service;
  541 + int new_s;
  542 + modbus_tcp_pi_t *ctx_tcp_pi;
  543 +
  544 + if (ctx == NULL) {
  545 + errno = EINVAL;
  546 + return -1;
  547 + }
  548 +
  549 + ctx_tcp_pi = ctx->backend_data;
  550 +
  551 +#ifdef OS_WIN32
  552 + if (_modbus_tcp_init_win32() == -1) {
  553 + return -1;
  554 + }
  555 +#endif
  556 +
  557 + if (ctx_tcp_pi->node[0] == 0) {
  558 + node = NULL; /* == any */
  559 + } else {
  560 + node = ctx_tcp_pi->node;
  561 + }
  562 +
  563 + if (ctx_tcp_pi->service[0] == 0) {
  564 + service = "502";
  565 + } else {
  566 + service = ctx_tcp_pi->service;
  567 + }
  568 +
  569 + memset(&ai_hints, 0, sizeof (ai_hints));
  570 + /* If node is not NULL, than the AI_PASSIVE flag is ignored. */
  571 + ai_hints.ai_flags |= AI_PASSIVE;
  572 +#ifdef AI_ADDRCONFIG
  573 + ai_hints.ai_flags |= AI_ADDRCONFIG;
  574 +#endif
  575 + ai_hints.ai_family = AF_UNSPEC;
  576 + ai_hints.ai_socktype = SOCK_STREAM;
  577 + ai_hints.ai_addr = NULL;
  578 + ai_hints.ai_canonname = NULL;
  579 + ai_hints.ai_next = NULL;
  580 +
  581 + ai_list = NULL;
  582 + rc = getaddrinfo(node, service, &ai_hints, &ai_list);
  583 + if (rc != 0) {
  584 + if (ctx->debug) {
  585 + fprintf(stderr, "Error returned by getaddrinfo: %s\n", gai_strerror(rc));
  586 + }
  587 + errno = ECONNREFUSED;
  588 + return -1;
  589 + }
  590 +
  591 + new_s = -1;
  592 + for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) {
  593 + int s;
  594 +
  595 + s = socket(ai_ptr->ai_family, ai_ptr->ai_socktype,
  596 + ai_ptr->ai_protocol);
  597 + if (s < 0) {
  598 + if (ctx->debug) {
  599 + perror("socket");
  600 + }
  601 + continue;
  602 + } else {
  603 + int enable = 1;
  604 + rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
  605 + (void *)&enable, sizeof (enable));
  606 + if (rc != 0) {
  607 + close(s);
  608 + if (ctx->debug) {
  609 + perror("setsockopt");
  610 + }
  611 + continue;
  612 + }
  613 + }
  614 +
  615 + rc = bind(s, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
  616 + if (rc != 0) {
  617 + close(s);
  618 + if (ctx->debug) {
  619 + perror("bind");
  620 + }
  621 + continue;
  622 + }
  623 +
  624 + rc = listen(s, nb_connection);
  625 + if (rc != 0) {
  626 + close(s);
  627 + if (ctx->debug) {
  628 + perror("listen");
  629 + }
  630 + continue;
  631 + }
  632 +
  633 + new_s = s;
  634 + break;
  635 + }
  636 + freeaddrinfo(ai_list);
  637 +
  638 + if (new_s < 0) {
  639 + return -1;
  640 + }
  641 +
  642 + return new_s;
  643 +}
  644 +
  645 +int modbus_tcp_accept(modbus_t *ctx, int *s)
  646 +{
  647 + struct sockaddr_in addr;
  648 + socklen_t addrlen;
  649 +
  650 + if (ctx == NULL) {
  651 + errno = EINVAL;
  652 + return -1;
  653 + }
  654 +
  655 + addrlen = sizeof(addr);
  656 +#ifdef HAVE_ACCEPT4
  657 + /* Inherit socket flags and use accept4 call */
  658 + ctx->s = accept4(*s, (struct sockaddr *)&addr, &addrlen, SOCK_CLOEXEC);
  659 +#else
  660 + ctx->s = accept(*s, (struct sockaddr *)&addr, &addrlen);
  661 +#endif
  662 +
  663 + if (ctx->s == -1) {
  664 + close(*s);
  665 + *s = -1;
  666 + return -1;
  667 + }
  668 +
  669 + if (ctx->debug) {
  670 + printf("The client connection from %s is accepted\n",
  671 + inet_ntoa(addr.sin_addr));
  672 + }
  673 +
  674 + return ctx->s;
  675 +}
  676 +
  677 +int modbus_tcp_pi_accept(modbus_t *ctx, int *s)
  678 +{
  679 + struct sockaddr_storage addr;
  680 + socklen_t addrlen;
  681 +
  682 + if (ctx == NULL) {
  683 + errno = EINVAL;
  684 + return -1;
  685 + }
  686 +
  687 + addrlen = sizeof(addr);
  688 +#ifdef HAVE_ACCEPT4
  689 + /* Inherit socket flags and use accept4 call */
  690 + ctx->s = accept4(*s, (struct sockaddr *)&addr, &addrlen, SOCK_CLOEXEC);
  691 +#else
  692 + ctx->s = accept(*s, (struct sockaddr *)&addr, &addrlen);
  693 +#endif
  694 + if (ctx->s == -1) {
  695 + close(*s);
  696 + *s = -1;
  697 + }
  698 +
  699 + if (ctx->debug) {
  700 + printf("The client connection is accepted.\n");
  701 + }
  702 +
  703 + return ctx->s;
  704 +}
  705 +
  706 +static int _modbus_tcp_select(modbus_t *ctx, fd_set *rset, struct timeval *tv, int length_to_read)
  707 +{
  708 + int s_rc;
  709 + while ((s_rc = select(ctx->s+1, rset, NULL, NULL, tv)) == -1) {
  710 + if (errno == EINTR) {
  711 + if (ctx->debug) {
  712 + fprintf(stderr, "A non blocked signal was caught\n");
  713 + }
  714 + /* Necessary after an error */
  715 + FD_ZERO(rset);
  716 + FD_SET(ctx->s, rset);
  717 + } else {
  718 + return -1;
  719 + }
  720 + }
  721 +
  722 + if (s_rc == 0) {
  723 + errno = ETIMEDOUT;
  724 + return -1;
  725 + }
  726 +
  727 + return s_rc;
  728 +}
  729 +
  730 +static void _modbus_tcp_free(modbus_t *ctx) {
  731 + free(ctx->backend_data);
  732 + free(ctx);
  733 +}
  734 +
  735 +const modbus_backend_t _modbus_tcp_backend = {
  736 + _MODBUS_BACKEND_TYPE_TCP,
  737 + _MODBUS_TCP_HEADER_LENGTH,
  738 + _MODBUS_TCP_CHECKSUM_LENGTH,
  739 + MODBUS_TCP_MAX_ADU_LENGTH,
  740 + _modbus_set_slave,
  741 + _modbus_tcp_build_request_basis,
  742 + _modbus_tcp_build_response_basis,
  743 + _modbus_tcp_prepare_response_tid,
  744 + _modbus_tcp_send_msg_pre,
  745 + _modbus_tcp_send,
  746 + _modbus_tcp_receive,
  747 + _modbus_tcp_recv,
  748 + _modbus_tcp_check_integrity,
  749 + _modbus_tcp_pre_check_confirmation,
  750 + _modbus_tcp_connect,
  751 + _modbus_tcp_close,
  752 + _modbus_tcp_flush,
  753 + _modbus_tcp_select,
  754 + _modbus_tcp_free
  755 +};
  756 +
  757 +
  758 +const modbus_backend_t _modbus_tcp_pi_backend = {
  759 + _MODBUS_BACKEND_TYPE_TCP,
  760 + _MODBUS_TCP_HEADER_LENGTH,
  761 + _MODBUS_TCP_CHECKSUM_LENGTH,
  762 + MODBUS_TCP_MAX_ADU_LENGTH,
  763 + _modbus_set_slave,
  764 + _modbus_tcp_build_request_basis,
  765 + _modbus_tcp_build_response_basis,
  766 + _modbus_tcp_prepare_response_tid,
  767 + _modbus_tcp_send_msg_pre,
  768 + _modbus_tcp_send,
  769 + _modbus_tcp_receive,
  770 + _modbus_tcp_recv,
  771 + _modbus_tcp_check_integrity,
  772 + _modbus_tcp_pre_check_confirmation,
  773 + _modbus_tcp_pi_connect,
  774 + _modbus_tcp_close,
  775 + _modbus_tcp_flush,
  776 + _modbus_tcp_select,
  777 + _modbus_tcp_free
  778 +};
  779 +
  780 +modbus_t* modbus_new_tcp(const char *ip, int port)
  781 +{
  782 + modbus_t *ctx;
  783 + modbus_tcp_t *ctx_tcp;
  784 + size_t dest_size;
  785 + size_t ret_size;
  786 +
  787 +#if defined(OS_BSD)
  788 + /* MSG_NOSIGNAL is unsupported on *BSD so we install an ignore
  789 + handler for SIGPIPE. */
  790 + struct sigaction sa;
  791 +
  792 + sa.sa_handler = SIG_IGN;
  793 + if (sigaction(SIGPIPE, &sa, NULL) < 0) {
  794 + /* The debug flag can't be set here... */
  795 + fprintf(stderr, "Coud not install SIGPIPE handler.\n");
  796 + return NULL;
  797 + }
  798 +#endif
  799 +
  800 + ctx = (modbus_t *)malloc(sizeof(modbus_t));
  801 + _modbus_init_common(ctx);
  802 +
  803 + /* Could be changed after to reach a remote serial Modbus device */
  804 + ctx->slave = MODBUS_TCP_SLAVE;
  805 +
  806 + ctx->backend = &_modbus_tcp_backend;
  807 +
  808 + ctx->backend_data = (modbus_tcp_t *)malloc(sizeof(modbus_tcp_t));
  809 + ctx_tcp = (modbus_tcp_t *)ctx->backend_data;
  810 +
  811 + if (ip != NULL) {
  812 + dest_size = sizeof(char) * 16;
  813 + ret_size = strlcpy(ctx_tcp->ip, ip, dest_size);
  814 + if (ret_size == 0) {
  815 + fprintf(stderr, "The IP string is empty\n");
  816 + modbus_free(ctx);
  817 + errno = EINVAL;
  818 + return NULL;
  819 + }
  820 +
  821 + if (ret_size >= dest_size) {
  822 + fprintf(stderr, "The IP string has been truncated\n");
  823 + modbus_free(ctx);
  824 + errno = EINVAL;
  825 + return NULL;
  826 + }
  827 + } else {
  828 + ctx_tcp->ip[0] = '0';
  829 + }
  830 + ctx_tcp->port = port;
  831 + ctx_tcp->t_id = 0;
  832 +
  833 + return ctx;
  834 +}
  835 +
  836 +
  837 +modbus_t* modbus_new_tcp_pi(const char *node, const char *service)
  838 +{
  839 + modbus_t *ctx;
  840 + modbus_tcp_pi_t *ctx_tcp_pi;
  841 + size_t dest_size;
  842 + size_t ret_size;
  843 +
  844 + ctx = (modbus_t *)malloc(sizeof(modbus_t));
  845 + _modbus_init_common(ctx);
  846 +
  847 + /* Could be changed after to reach a remote serial Modbus device */
  848 + ctx->slave = MODBUS_TCP_SLAVE;
  849 +
  850 + ctx->backend = &_modbus_tcp_pi_backend;
  851 +
  852 + ctx->backend_data = (modbus_tcp_pi_t *)malloc(sizeof(modbus_tcp_pi_t));
  853 + ctx_tcp_pi = (modbus_tcp_pi_t *)ctx->backend_data;
  854 +
  855 + if (node == NULL) {
  856 + /* The node argument can be empty to indicate any hosts */
  857 + ctx_tcp_pi->node[0] = 0;
  858 + } else {
  859 + dest_size = sizeof(char) * _MODBUS_TCP_PI_NODE_LENGTH;
  860 + ret_size = strlcpy(ctx_tcp_pi->node, node, dest_size);
  861 + if (ret_size == 0) {
  862 + fprintf(stderr, "The node string is empty\n");
  863 + modbus_free(ctx);
  864 + errno = EINVAL;
  865 + return NULL;
  866 + }
  867 +
  868 + if (ret_size >= dest_size) {
  869 + fprintf(stderr, "The node string has been truncated\n");
  870 + modbus_free(ctx);
  871 + errno = EINVAL;
  872 + return NULL;
  873 + }
  874 + }
  875 +
  876 + if (service != NULL) {
  877 + dest_size = sizeof(char) * _MODBUS_TCP_PI_SERVICE_LENGTH;
  878 + ret_size = strlcpy(ctx_tcp_pi->service, service, dest_size);
  879 + } else {
  880 + /* Empty service is not allowed, error catched below. */
  881 + ret_size = 0;
  882 + }
  883 +
  884 + if (ret_size == 0) {
  885 + fprintf(stderr, "The service string is empty\n");
  886 + modbus_free(ctx);
  887 + errno = EINVAL;
  888 + return NULL;
  889 + }
  890 +
  891 + if (ret_size >= dest_size) {
  892 + fprintf(stderr, "The service string has been truncated\n");
  893 + modbus_free(ctx);
  894 + errno = EINVAL;
  895 + return NULL;
  896 + }
  897 +
  898 + ctx_tcp_pi->t_id = 0;
  899 +
  900 + return ctx;
  901 +}
... ...
3rdparty/libmodbus/modbus-tcp.h 0 โ†’ 100644
  1 +/*
  2 + * Copyright ยฉ 2001-2010 Stรฉphane Raimbault <stephane.raimbault@gmail.com>
  3 + *
  4 + * SPDX-License-Identifier: LGPL-2.1+
  5 + */
  6 +
  7 +#ifndef MODBUS_TCP_H
  8 +#define MODBUS_TCP_H
  9 +
  10 +#include "modbus.h"
  11 +
  12 +MODBUS_BEGIN_DECLS
  13 +
  14 +#if defined(_WIN32) && !defined(__CYGWIN__)
  15 +/* Win32 with MinGW, supplement to <errno.h> */
  16 +#include <winsock2.h>
  17 +#if !defined(ECONNRESET)
  18 +#define ECONNRESET WSAECONNRESET
  19 +#endif
  20 +#if !defined(ECONNREFUSED)
  21 +#define ECONNREFUSED WSAECONNREFUSED
  22 +#endif
  23 +#if !defined(ETIMEDOUT)
  24 +#define ETIMEDOUT WSAETIMEDOUT
  25 +#endif
  26 +#if !defined(ENOPROTOOPT)
  27 +#define ENOPROTOOPT WSAENOPROTOOPT
  28 +#endif
  29 +#if !defined(EINPROGRESS)
  30 +#define EINPROGRESS WSAEINPROGRESS
  31 +#endif
  32 +#endif
  33 +
  34 +#define MODBUS_TCP_DEFAULT_PORT 502
  35 +#define MODBUS_TCP_SLAVE 0xFF
  36 +
  37 +/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5
  38 + * TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes
  39 + */
  40 +#define MODBUS_TCP_MAX_ADU_LENGTH 260
  41 +
  42 +MODBUS_API modbus_t* modbus_new_tcp(const char *ip_address, int port);
  43 +MODBUS_API int modbus_tcp_listen(modbus_t *ctx, int nb_connection);
  44 +MODBUS_API int modbus_tcp_accept(modbus_t *ctx, int *s);
  45 +
  46 +MODBUS_API modbus_t* modbus_new_tcp_pi(const char *node, const char *service);
  47 +MODBUS_API int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection);
  48 +MODBUS_API int modbus_tcp_pi_accept(modbus_t *ctx, int *s);
  49 +
  50 +MODBUS_END_DECLS
  51 +
  52 +#endif /* MODBUS_TCP_H */
... ...
3rdparty/libmodbus/modbus-version.h 0 โ†’ 100644
  1 +/*
  2 + * Copyright ยฉ 2010-2014 Stรฉphane Raimbault <stephane.raimbault@gmail.com>
  3 + *
  4 + * This library is free software; you can redistribute it and/or
  5 + * modify it under the terms of the GNU Lesser General Public
  6 + * License as published by the Free Software Foundation; either
  7 + * version 2.1 of the License, or (at your option) any later version.
  8 + *
  9 + * This library is distributed in the hope that it will be useful,
  10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12 + * Lesser General Public License for more details.
  13 + *
  14 + * You should have received a copy of the GNU Lesser General Public
  15 + * License along with this library; if not, write to the Free Software
  16 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17 + */
  18 +
  19 +#ifndef MODBUS_VERSION_H
  20 +#define MODBUS_VERSION_H
  21 +
  22 +/* The major version, (1, if %LIBMODBUS_VERSION is 1.2.3) */
  23 +#define LIBMODBUS_VERSION_MAJOR (3)
  24 +
  25 +/* The minor version (2, if %LIBMODBUS_VERSION is 1.2.3) */
  26 +#define LIBMODBUS_VERSION_MINOR (1)
  27 +
  28 +/* The micro version (3, if %LIBMODBUS_VERSION is 1.2.3) */
  29 +#define LIBMODBUS_VERSION_MICRO (4)
  30 +
  31 +/* The full version, like 1.2.3 */
  32 +#define LIBMODBUS_VERSION 3.1.4
  33 +
  34 +/* The full version, in string form (suited for string concatenation)
  35 + */
  36 +#define LIBMODBUS_VERSION_STRING "3.1.4"
  37 +
  38 +/* Numerically encoded version, like 0x010203 */
  39 +#define LIBMODBUS_VERSION_HEX ((LIBMODBUS_VERSION_MAJOR << 24) | \
  40 + (LIBMODBUS_VERSION_MINOR << 16) | \
  41 + (LIBMODBUS_VERSION_MICRO << 8))
  42 +
  43 +/* Evaluates to True if the version is greater than @major, @minor and @micro
  44 + */
  45 +#define LIBMODBUS_VERSION_CHECK(major,minor,micro) \
  46 + (LIBMODBUS_VERSION_MAJOR > (major) || \
  47 + (LIBMODBUS_VERSION_MAJOR == (major) && \
  48 + LIBMODBUS_VERSION_MINOR > (minor)) || \
  49 + (LIBMODBUS_VERSION_MAJOR == (major) && \
  50 + LIBMODBUS_VERSION_MINOR == (minor) && \
  51 + LIBMODBUS_VERSION_MICRO >= (micro)))
  52 +
  53 +#endif /* MODBUS_VERSION_H */
... ...
3rdparty/libmodbus/modbus.c 0 โ†’ 100644
  1 +/*
  2 + * Copyright ยฉ 2001-2011 Stรฉphane Raimbault <stephane.raimbault@gmail.com>
  3 + *
  4 + * SPDX-License-Identifier: LGPL-2.1+
  5 + *
  6 + * This library implements the Modbus protocol.
  7 + * http://libmodbus.org/
  8 + */
  9 +
  10 +#include <stdio.h>
  11 +#include <string.h>
  12 +#include <stdlib.h>
  13 +#include <stdarg.h>
  14 +#include <errno.h>
  15 +#include <limits.h>
  16 +#include <time.h>
  17 +#ifndef _MSC_VER
  18 +#include <unistd.h>
  19 +#endif
  20 +
  21 +#include <config.h>
  22 +
  23 +#include "modbus.h"
  24 +#include "modbus-private.h"
  25 +
  26 +/* Internal use */
  27 +#define MSG_LENGTH_UNDEFINED -1
  28 +
  29 +/* Exported version */
  30 +const unsigned int libmodbus_version_major = LIBMODBUS_VERSION_MAJOR;
  31 +const unsigned int libmodbus_version_minor = LIBMODBUS_VERSION_MINOR;
  32 +const unsigned int libmodbus_version_micro = LIBMODBUS_VERSION_MICRO;
  33 +
  34 +/* Max between RTU and TCP max adu length (so TCP) */
  35 +#define MAX_MESSAGE_LENGTH 260
  36 +
  37 +/* 3 steps are used to parse the query */
  38 +typedef enum {
  39 + _STEP_FUNCTION,
  40 + _STEP_META,
  41 + _STEP_DATA
  42 +} _step_t;
  43 +
  44 +const char *modbus_strerror(int errnum) {
  45 + switch (errnum) {
  46 + case EMBXILFUN:
  47 + return "Illegal function";
  48 + case EMBXILADD:
  49 + return "Illegal data address";
  50 + case EMBXILVAL:
  51 + return "Illegal data value";
  52 + case EMBXSFAIL:
  53 + return "Slave device or server failure";
  54 + case EMBXACK:
  55 + return "Acknowledge";
  56 + case EMBXSBUSY:
  57 + return "Slave device or server is busy";
  58 + case EMBXNACK:
  59 + return "Negative acknowledge";
  60 + case EMBXMEMPAR:
  61 + return "Memory parity error";
  62 + case EMBXGPATH:
  63 + return "Gateway path unavailable";
  64 + case EMBXGTAR:
  65 + return "Target device failed to respond";
  66 + case EMBBADCRC:
  67 + return "Invalid CRC";
  68 + case EMBBADDATA:
  69 + return "Invalid data";
  70 + case EMBBADEXC:
  71 + return "Invalid exception code";
  72 + case EMBMDATA:
  73 + return "Too many data";
  74 + case EMBBADSLAVE:
  75 + return "Response not from requested slave";
  76 + default:
  77 + return strerror(errnum);
  78 + }
  79 +}
  80 +
  81 +void _error_print(modbus_t *ctx, const char *context)
  82 +{
  83 + if (ctx->debug) {
  84 + fprintf(stderr, "ERROR %s", modbus_strerror(errno));
  85 + if (context != NULL) {
  86 + fprintf(stderr, ": %s\n", context);
  87 + } else {
  88 + fprintf(stderr, "\n");
  89 + }
  90 + }
  91 +}
  92 +
  93 +static void _sleep_response_timeout(modbus_t *ctx)
  94 +{
  95 + /* Response timeout is always positive */
  96 +#ifdef _WIN32
  97 + /* usleep doesn't exist on Windows */
  98 + Sleep((ctx->response_timeout.tv_sec * 1000) +
  99 + (ctx->response_timeout.tv_usec / 1000));
  100 +#else
  101 + /* usleep source code */
  102 + struct timespec request, remaining;
  103 + request.tv_sec = ctx->response_timeout.tv_sec;
  104 + request.tv_nsec = ((long int)ctx->response_timeout.tv_usec) * 1000;
  105 + while (nanosleep(&request, &remaining) == -1 && errno == EINTR) {
  106 + request = remaining;
  107 + }
  108 +#endif
  109 +}
  110 +
  111 +int modbus_flush(modbus_t *ctx)
  112 +{
  113 + int rc;
  114 +
  115 + if (ctx == NULL) {
  116 + errno = EINVAL;
  117 + return -1;
  118 + }
  119 +
  120 + rc = ctx->backend->flush(ctx);
  121 + if (rc != -1 && ctx->debug) {
  122 + /* Not all backends are able to return the number of bytes flushed */
  123 + printf("Bytes flushed (%d)\n", rc);
  124 + }
  125 + return rc;
  126 +}
  127 +
  128 +/* Computes the length of the expected response */
  129 +static unsigned int compute_response_length_from_request(modbus_t *ctx, uint8_t *req)
  130 +{
  131 + int length;
  132 + const int offset = ctx->backend->header_length;
  133 +
  134 + switch (req[offset]) {
  135 + case MODBUS_FC_READ_COILS:
  136 + case MODBUS_FC_READ_DISCRETE_INPUTS: {
  137 + /* Header + nb values (code from write_bits) */
  138 + int nb = (req[offset + 3] << 8) | req[offset + 4];
  139 + length = 2 + (nb / 8) + ((nb % 8) ? 1 : 0);
  140 + }
  141 + break;
  142 + case MODBUS_FC_WRITE_AND_READ_REGISTERS:
  143 + case MODBUS_FC_READ_HOLDING_REGISTERS:
  144 + case MODBUS_FC_READ_INPUT_REGISTERS:
  145 + /* Header + 2 * nb values */
  146 + length = 2 + 2 * (req[offset + 3] << 8 | req[offset + 4]);
  147 + break;
  148 + case MODBUS_FC_READ_EXCEPTION_STATUS:
  149 + length = 3;
  150 + break;
  151 + case MODBUS_FC_REPORT_SLAVE_ID:
  152 + /* The response is device specific (the header provides the
  153 + length) */
  154 + return MSG_LENGTH_UNDEFINED;
  155 + case MODBUS_FC_MASK_WRITE_REGISTER:
  156 + length = 7;
  157 + break;
  158 + default:
  159 + length = 5;
  160 + }
  161 +
  162 + return offset + length + ctx->backend->checksum_length;
  163 +}
  164 +
  165 +/* Sends a request/response */
  166 +static int send_msg(modbus_t *ctx, uint8_t *msg, int msg_length)
  167 +{
  168 + int rc;
  169 + int i;
  170 +
  171 + msg_length = ctx->backend->send_msg_pre(msg, msg_length);
  172 +
  173 + if (ctx->debug) {
  174 + for (i = 0; i < msg_length; i++)
  175 + printf("[%.2X]", msg[i]);
  176 + printf("\n");
  177 + }
  178 +
  179 + /* In recovery mode, the write command will be issued until to be
  180 + successful! Disabled by default. */
  181 + do {
  182 + rc = ctx->backend->send(ctx, msg, msg_length);
  183 + if (rc == -1) {
  184 + _error_print(ctx, NULL);
  185 + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) {
  186 + int saved_errno = errno;
  187 +
  188 + if ((errno == EBADF || errno == ECONNRESET || errno == EPIPE)) {
  189 + modbus_close(ctx);
  190 + _sleep_response_timeout(ctx);
  191 + modbus_connect(ctx);
  192 + } else {
  193 + _sleep_response_timeout(ctx);
  194 + modbus_flush(ctx);
  195 + }
  196 + errno = saved_errno;
  197 + }
  198 + }
  199 + } while ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) &&
  200 + rc == -1);
  201 +
  202 + if (rc > 0 && rc != msg_length) {
  203 + errno = EMBBADDATA;
  204 + return -1;
  205 + }
  206 +
  207 + return rc;
  208 +}
  209 +
  210 +int modbus_send_raw_request(modbus_t *ctx, uint8_t *raw_req, int raw_req_length)
  211 +{
  212 + sft_t sft;
  213 + uint8_t req[MAX_MESSAGE_LENGTH];
  214 + int req_length;
  215 +
  216 + if (ctx == NULL) {
  217 + errno = EINVAL;
  218 + return -1;
  219 + }
  220 +
  221 + if (raw_req_length < 2 || raw_req_length > (MODBUS_MAX_PDU_LENGTH + 1)) {
  222 + /* The raw request must contain function and slave at least and
  223 + must not be longer than the maximum pdu length plus the slave
  224 + address. */
  225 + errno = EINVAL;
  226 + return -1;
  227 + }
  228 +
  229 + sft.slave = raw_req[0];
  230 + sft.function = raw_req[1];
  231 + /* The t_id is left to zero */
  232 + sft.t_id = 0;
  233 + /* This response function only set the header so it's convenient here */
  234 + req_length = ctx->backend->build_response_basis(&sft, req);
  235 +
  236 + if (raw_req_length > 2) {
  237 + /* Copy data after function code */
  238 + memcpy(req + req_length, raw_req + 2, raw_req_length - 2);
  239 + req_length += raw_req_length - 2;
  240 + }
  241 +
  242 + return send_msg(ctx, req, req_length);
  243 +}
  244 +
  245 +/*
  246 + * ---------- Request Indication ----------
  247 + * | Client | ---------------------->| Server |
  248 + * ---------- Confirmation Response ----------
  249 + */
  250 +
  251 +/* Computes the length to read after the function received */
  252 +static uint8_t compute_meta_length_after_function(int function,
  253 + msg_type_t msg_type)
  254 +{
  255 + int length;
  256 +
  257 + if (msg_type == MSG_INDICATION) {
  258 + if (function <= MODBUS_FC_WRITE_SINGLE_REGISTER) {
  259 + length = 4;
  260 + } else if (function == MODBUS_FC_WRITE_MULTIPLE_COILS ||
  261 + function == MODBUS_FC_WRITE_MULTIPLE_REGISTERS) {
  262 + length = 5;
  263 + } else if (function == MODBUS_FC_MASK_WRITE_REGISTER) {
  264 + length = 6;
  265 + } else if (function == MODBUS_FC_WRITE_AND_READ_REGISTERS) {
  266 + length = 9;
  267 + } else {
  268 + /* MODBUS_FC_READ_EXCEPTION_STATUS, MODBUS_FC_REPORT_SLAVE_ID */
  269 + length = 0;
  270 + }
  271 + } else {
  272 + /* MSG_CONFIRMATION */
  273 + switch (function) {
  274 + case MODBUS_FC_WRITE_SINGLE_COIL:
  275 + case MODBUS_FC_WRITE_SINGLE_REGISTER:
  276 + case MODBUS_FC_WRITE_MULTIPLE_COILS:
  277 + case MODBUS_FC_WRITE_MULTIPLE_REGISTERS:
  278 + length = 4;
  279 + break;
  280 + case MODBUS_FC_MASK_WRITE_REGISTER:
  281 + length = 6;
  282 + break;
  283 + default:
  284 + length = 1;
  285 + }
  286 + }
  287 +
  288 + return length;
  289 +}
  290 +
  291 +/* Computes the length to read after the meta information (address, count, etc) */
  292 +static int compute_data_length_after_meta(modbus_t *ctx, uint8_t *msg,
  293 + msg_type_t msg_type)
  294 +{
  295 + int function = msg[ctx->backend->header_length];
  296 + int length;
  297 +
  298 + if (msg_type == MSG_INDICATION) {
  299 + switch (function) {
  300 + case MODBUS_FC_WRITE_MULTIPLE_COILS:
  301 + case MODBUS_FC_WRITE_MULTIPLE_REGISTERS:
  302 + length = msg[ctx->backend->header_length + 5];
  303 + break;
  304 + case MODBUS_FC_WRITE_AND_READ_REGISTERS:
  305 + length = msg[ctx->backend->header_length + 9];
  306 + break;
  307 + default:
  308 + length = 0;
  309 + }
  310 + } else {
  311 + /* MSG_CONFIRMATION */
  312 + if (function <= MODBUS_FC_READ_INPUT_REGISTERS ||
  313 + function == MODBUS_FC_REPORT_SLAVE_ID ||
  314 + function == MODBUS_FC_WRITE_AND_READ_REGISTERS) {
  315 + length = msg[ctx->backend->header_length + 1];
  316 + } else {
  317 + length = 0;
  318 + }
  319 + }
  320 +
  321 + length += ctx->backend->checksum_length;
  322 +
  323 + return length;
  324 +}
  325 +
  326 +
  327 +/* Waits a response from a modbus server or a request from a modbus client.
  328 + This function blocks if there is no replies (3 timeouts).
  329 +
  330 + The function shall return the number of received characters and the received
  331 + message in an array of uint8_t if successful. Otherwise it shall return -1
  332 + and errno is set to one of the values defined below:
  333 + - ECONNRESET
  334 + - EMBBADDATA
  335 + - EMBUNKEXC
  336 + - ETIMEDOUT
  337 + - read() or recv() error codes
  338 +*/
  339 +
  340 +int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
  341 +{
  342 + int rc;
  343 + fd_set rset;
  344 + struct timeval tv;
  345 + struct timeval *p_tv;
  346 + int length_to_read;
  347 + int msg_length = 0;
  348 + _step_t step;
  349 +
  350 + if (ctx->debug) {
  351 + if (msg_type == MSG_INDICATION) {
  352 + printf("Waiting for a indication...\n");
  353 + } else {
  354 + printf("Waiting for a confirmation...\n");
  355 + }
  356 + }
  357 +
  358 + /* Add a file descriptor to the set */
  359 + FD_ZERO(&rset);
  360 + FD_SET(ctx->s, &rset);
  361 +
  362 + /* We need to analyse the message step by step. At the first step, we want
  363 + * to reach the function code because all packets contain this
  364 + * information. */
  365 + step = _STEP_FUNCTION;
  366 + length_to_read = ctx->backend->header_length + 1;
  367 +
  368 + if (msg_type == MSG_INDICATION) {
  369 + /* Wait for a message, we don't know when the message will be
  370 + * received */
  371 + p_tv = NULL;
  372 + } else {
  373 + tv.tv_sec = ctx->response_timeout.tv_sec;
  374 + tv.tv_usec = ctx->response_timeout.tv_usec;
  375 + p_tv = &tv;
  376 + }
  377 +
  378 + while (length_to_read != 0) {
  379 + rc = ctx->backend->select(ctx, &rset, p_tv, length_to_read);
  380 + if (rc == -1) {
  381 + _error_print(ctx, "select");
  382 + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) {
  383 + int saved_errno = errno;
  384 +
  385 + if (errno == ETIMEDOUT) {
  386 + _sleep_response_timeout(ctx);
  387 + modbus_flush(ctx);
  388 + } else if (errno == EBADF) {
  389 + modbus_close(ctx);
  390 + modbus_connect(ctx);
  391 + }
  392 + errno = saved_errno;
  393 + }
  394 + return -1;
  395 + }
  396 +
  397 + rc = ctx->backend->recv(ctx, msg + msg_length, length_to_read);
  398 + if (rc == 0) {
  399 + errno = ECONNRESET;
  400 + rc = -1;
  401 + }
  402 +
  403 + if (rc == -1) {
  404 + _error_print(ctx, "read");
  405 + if ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) &&
  406 + (errno == ECONNRESET || errno == ECONNREFUSED ||
  407 + errno == EBADF)) {
  408 + int saved_errno = errno;
  409 + modbus_close(ctx);
  410 + modbus_connect(ctx);
  411 + /* Could be removed by previous calls */
  412 + errno = saved_errno;
  413 + }
  414 + return -1;
  415 + }
  416 +
  417 + /* Display the hex code of each character received */
  418 + if (ctx->debug) {
  419 + int i;
  420 + for (i=0; i < rc; i++)
  421 + printf("<%.2X>", msg[msg_length + i]);
  422 + }
  423 +
  424 + /* Sums bytes received */
  425 + msg_length += rc;
  426 + /* Computes remaining bytes */
  427 + length_to_read -= rc;
  428 +
  429 + if (length_to_read == 0) {
  430 + switch (step) {
  431 + case _STEP_FUNCTION:
  432 + /* Function code position */
  433 + length_to_read = compute_meta_length_after_function(
  434 + msg[ctx->backend->header_length],
  435 + msg_type);
  436 + if (length_to_read != 0) {
  437 + step = _STEP_META;
  438 + break;
  439 + } /* else switches straight to the next step */
  440 + case _STEP_META:
  441 + length_to_read = compute_data_length_after_meta(
  442 + ctx, msg, msg_type);
  443 + if ((msg_length + length_to_read) > (int)ctx->backend->max_adu_length) {
  444 + errno = EMBBADDATA;
  445 + _error_print(ctx, "too many data");
  446 + return -1;
  447 + }
  448 + step = _STEP_DATA;
  449 + break;
  450 + default:
  451 + break;
  452 + }
  453 + }
  454 +
  455 + if (length_to_read > 0 &&
  456 + (ctx->byte_timeout.tv_sec > 0 || ctx->byte_timeout.tv_usec > 0)) {
  457 + /* If there is no character in the buffer, the allowed timeout
  458 + interval between two consecutive bytes is defined by
  459 + byte_timeout */
  460 + tv.tv_sec = ctx->byte_timeout.tv_sec;
  461 + tv.tv_usec = ctx->byte_timeout.tv_usec;
  462 + p_tv = &tv;
  463 + }
  464 + /* else timeout isn't set again, the full response must be read before
  465 + expiration of response timeout (for CONFIRMATION only) */
  466 + }
  467 +
  468 + if (ctx->debug)
  469 + printf("\n");
  470 +
  471 + return ctx->backend->check_integrity(ctx, msg, msg_length);
  472 +}
  473 +
  474 +/* Receive the request from a modbus master */
  475 +int modbus_receive(modbus_t *ctx, uint8_t *req)
  476 +{
  477 + if (ctx == NULL) {
  478 + errno = EINVAL;
  479 + return -1;
  480 + }
  481 +
  482 + return ctx->backend->receive(ctx, req);
  483 +}
  484 +
  485 +/* Receives the confirmation.
  486 +
  487 + The function shall store the read response in rsp and return the number of
  488 + values (bits or words). Otherwise, its shall return -1 and errno is set.
  489 +
  490 + The function doesn't check the confirmation is the expected response to the
  491 + initial request.
  492 +*/
  493 +int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp)
  494 +{
  495 + if (ctx == NULL) {
  496 + errno = EINVAL;
  497 + return -1;
  498 + }
  499 +
  500 + return _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
  501 +}
  502 +
  503 +static int check_confirmation(modbus_t *ctx, uint8_t *req,
  504 + uint8_t *rsp, int rsp_length)
  505 +{
  506 + int rc;
  507 + int rsp_length_computed;
  508 + const int offset = ctx->backend->header_length;
  509 + const int function = rsp[offset];
  510 +
  511 + if (ctx->backend->pre_check_confirmation) {
  512 + rc = ctx->backend->pre_check_confirmation(ctx, req, rsp, rsp_length);
  513 + if (rc == -1) {
  514 + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) {
  515 + _sleep_response_timeout(ctx);
  516 + modbus_flush(ctx);
  517 + }
  518 + return -1;
  519 + }
  520 + }
  521 +
  522 + rsp_length_computed = compute_response_length_from_request(ctx, req);
  523 +
  524 + /* Exception code */
  525 + if (function >= 0x80) {
  526 + if (rsp_length == (offset + 2 + (int)ctx->backend->checksum_length) &&
  527 + req[offset] == (rsp[offset] - 0x80)) {
  528 + /* Valid exception code received */
  529 +
  530 + int exception_code = rsp[offset + 1];
  531 + if (exception_code < MODBUS_EXCEPTION_MAX) {
  532 + errno = MODBUS_ENOBASE + exception_code;
  533 + } else {
  534 + errno = EMBBADEXC;
  535 + }
  536 + _error_print(ctx, NULL);
  537 + return -1;
  538 + } else {
  539 + errno = EMBBADEXC;
  540 + _error_print(ctx, NULL);
  541 + return -1;
  542 + }
  543 + }
  544 +
  545 + /* Check length */
  546 + if ((rsp_length == rsp_length_computed ||
  547 + rsp_length_computed == MSG_LENGTH_UNDEFINED) &&
  548 + function < 0x80) {
  549 + int req_nb_value;
  550 + int rsp_nb_value;
  551 +
  552 + /* Check function code */
  553 + if (function != req[offset]) {
  554 + if (ctx->debug) {
  555 + fprintf(stderr,
  556 + "Received function not corresponding to the request (0x%X != 0x%X)\n",
  557 + function, req[offset]);
  558 + }
  559 + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) {
  560 + _sleep_response_timeout(ctx);
  561 + modbus_flush(ctx);
  562 + }
  563 + errno = EMBBADDATA;
  564 + return -1;
  565 + }
  566 +
  567 + /* Check the number of values is corresponding to the request */
  568 + switch (function) {
  569 + case MODBUS_FC_READ_COILS:
  570 + case MODBUS_FC_READ_DISCRETE_INPUTS:
  571 + /* Read functions, 8 values in a byte (nb
  572 + * of values in the request and byte count in
  573 + * the response. */
  574 + req_nb_value = (req[offset + 3] << 8) + req[offset + 4];
  575 + req_nb_value = (req_nb_value / 8) + ((req_nb_value % 8) ? 1 : 0);
  576 + rsp_nb_value = rsp[offset + 1];
  577 + break;
  578 + case MODBUS_FC_WRITE_AND_READ_REGISTERS:
  579 + case MODBUS_FC_READ_HOLDING_REGISTERS:
  580 + case MODBUS_FC_READ_INPUT_REGISTERS:
  581 + /* Read functions 1 value = 2 bytes */
  582 + req_nb_value = (req[offset + 3] << 8) + req[offset + 4];
  583 + rsp_nb_value = (rsp[offset + 1] / 2);
  584 + break;
  585 + case MODBUS_FC_WRITE_MULTIPLE_COILS:
  586 + case MODBUS_FC_WRITE_MULTIPLE_REGISTERS:
  587 + /* N Write functions */
  588 + req_nb_value = (req[offset + 3] << 8) + req[offset + 4];
  589 + rsp_nb_value = (rsp[offset + 3] << 8) | rsp[offset + 4];
  590 + break;
  591 + case MODBUS_FC_REPORT_SLAVE_ID:
  592 + /* Report slave ID (bytes received) */
  593 + req_nb_value = rsp_nb_value = rsp[offset + 1];
  594 + break;
  595 + default:
  596 + /* 1 Write functions & others */
  597 + req_nb_value = rsp_nb_value = 1;
  598 + }
  599 +
  600 + if (req_nb_value == rsp_nb_value) {
  601 + rc = rsp_nb_value;
  602 + } else {
  603 + if (ctx->debug) {
  604 + fprintf(stderr,
  605 + "Quantity not corresponding to the request (%d != %d)\n",
  606 + rsp_nb_value, req_nb_value);
  607 + }
  608 +
  609 + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) {
  610 + _sleep_response_timeout(ctx);
  611 + modbus_flush(ctx);
  612 + }
  613 +
  614 + errno = EMBBADDATA;
  615 + rc = -1;
  616 + }
  617 + } else {
  618 + if (ctx->debug) {
  619 + fprintf(stderr,
  620 + "Message length not corresponding to the computed length (%d != %d)\n",
  621 + rsp_length, rsp_length_computed);
  622 + }
  623 + if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) {
  624 + _sleep_response_timeout(ctx);
  625 + modbus_flush(ctx);
  626 + }
  627 + errno = EMBBADDATA;
  628 + rc = -1;
  629 + }
  630 +
  631 + return rc;
  632 +}
  633 +
  634 +static int response_io_status(uint8_t *tab_io_status,
  635 + int address, int nb,
  636 + uint8_t *rsp, int offset)
  637 +{
  638 + int shift = 0;
  639 + /* Instead of byte (not allowed in Win32) */
  640 + int one_byte = 0;
  641 + int i;
  642 +
  643 + for (i = address; i < address + nb; i++) {
  644 + one_byte |= tab_io_status[i] << shift;
  645 + if (shift == 7) {
  646 + /* Byte is full */
  647 + rsp[offset++] = one_byte;
  648 + one_byte = shift = 0;
  649 + } else {
  650 + shift++;
  651 + }
  652 + }
  653 +
  654 + if (shift != 0)
  655 + rsp[offset++] = one_byte;
  656 +
  657 + return offset;
  658 +}
  659 +
  660 +/* Build the exception response */
  661 +static int response_exception(modbus_t *ctx, sft_t *sft,
  662 + int exception_code, uint8_t *rsp,
  663 + unsigned int to_flush,
  664 + const char* template, ...)
  665 +{
  666 + int rsp_length;
  667 +
  668 + /* Print debug message */
  669 + if (ctx->debug) {
  670 + va_list ap;
  671 +
  672 + va_start(ap, template);
  673 + vfprintf(stderr, template, ap);
  674 + va_end(ap);
  675 + }
  676 +
  677 + /* Flush if required */
  678 + if (to_flush) {
  679 + _sleep_response_timeout(ctx);
  680 + modbus_flush(ctx);
  681 + }
  682 +
  683 + /* Build exception response */
  684 + sft->function = sft->function + 0x80;
  685 + rsp_length = ctx->backend->build_response_basis(sft, rsp);
  686 + rsp[rsp_length++] = exception_code;
  687 +
  688 + return rsp_length;
  689 +}
  690 +
  691 +/* Send a response to the received request.
  692 + Analyses the request and constructs a response.
  693 +
  694 + If an error occurs, this function construct the response
  695 + accordingly.
  696 +*/
  697 +int modbus_reply(modbus_t *ctx, const uint8_t *req,
  698 + int req_length, modbus_mapping_t *mb_mapping)
  699 +{
  700 + int offset;
  701 + int slave;
  702 + int function;
  703 + uint16_t address;
  704 + uint8_t rsp[MAX_MESSAGE_LENGTH];
  705 + int rsp_length = 0;
  706 + sft_t sft;
  707 +
  708 + if (ctx == NULL) {
  709 + errno = EINVAL;
  710 + return -1;
  711 + }
  712 +
  713 + offset = ctx->backend->header_length;
  714 + slave = req[offset - 1];
  715 + function = req[offset];
  716 + address = (req[offset + 1] << 8) + req[offset + 2];
  717 +
  718 + sft.slave = slave;
  719 + sft.function = function;
  720 + sft.t_id = ctx->backend->prepare_response_tid(req, &req_length);
  721 +
  722 + /* Data are flushed on illegal number of values errors. */
  723 + switch (function) {
  724 + case MODBUS_FC_READ_COILS:
  725 + case MODBUS_FC_READ_DISCRETE_INPUTS: {
  726 + unsigned int is_input = (function == MODBUS_FC_READ_DISCRETE_INPUTS);
  727 + int start_bits = is_input ? mb_mapping->start_input_bits : mb_mapping->start_bits;
  728 + int nb_bits = is_input ? mb_mapping->nb_input_bits : mb_mapping->nb_bits;
  729 + uint8_t *tab_bits = is_input ? mb_mapping->tab_input_bits : mb_mapping->tab_bits;
  730 + const char * const name = is_input ? "read_input_bits" : "read_bits";
  731 + int nb = (req[offset + 3] << 8) + req[offset + 4];
  732 + /* The mapping can be shifted to reduce memory consumption and it
  733 + doesn't always start at address zero. */
  734 + int mapping_address = address - start_bits;
  735 +
  736 + if (nb < 1 || MODBUS_MAX_READ_BITS < nb) {
  737 + rsp_length = response_exception(
  738 + ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE,
  739 + "Illegal nb of values %d in %s (max %d)\n",
  740 + nb, name, MODBUS_MAX_READ_BITS);
  741 + } else if (mapping_address < 0 || (mapping_address + nb) > nb_bits) {
  742 + rsp_length = response_exception(
  743 + ctx, &sft,
  744 + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
  745 + "Illegal data address 0x%0X in %s\n",
  746 + mapping_address < 0 ? address : address + nb, name);
  747 + } else {
  748 + rsp_length = ctx->backend->build_response_basis(&sft, rsp);
  749 + rsp[rsp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0);
  750 + rsp_length = response_io_status(tab_bits, mapping_address, nb,
  751 + rsp, rsp_length);
  752 + }
  753 + }
  754 + break;
  755 + case MODBUS_FC_READ_HOLDING_REGISTERS:
  756 + case MODBUS_FC_READ_INPUT_REGISTERS: {
  757 + unsigned int is_input = (function == MODBUS_FC_READ_INPUT_REGISTERS);
  758 + int start_registers = is_input ? mb_mapping->start_input_registers : mb_mapping->start_registers;
  759 + int nb_registers = is_input ? mb_mapping->nb_input_registers : mb_mapping->nb_registers;
  760 + uint16_t *tab_registers = is_input ? mb_mapping->tab_input_registers : mb_mapping->tab_registers;
  761 + const char * const name = is_input ? "read_input_registers" : "read_registers";
  762 + int nb = (req[offset + 3] << 8) + req[offset + 4];
  763 + /* The mapping can be shifted to reduce memory consumption and it
  764 + doesn't always start at address zero. */
  765 + int mapping_address = address - start_registers;
  766 +
  767 + if (nb < 1 || MODBUS_MAX_READ_REGISTERS < nb) {
  768 + rsp_length = response_exception(
  769 + ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE,
  770 + "Illegal nb of values %d in %s (max %d)\n",
  771 + nb, name, MODBUS_MAX_READ_REGISTERS);
  772 + } else if (mapping_address < 0 || (mapping_address + nb) > nb_registers) {
  773 + rsp_length = response_exception(
  774 + ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
  775 + "Illegal data address 0x%0X in %s\n",
  776 + mapping_address < 0 ? address : address + nb, name);
  777 + } else {
  778 + int i;
  779 +
  780 + rsp_length = ctx->backend->build_response_basis(&sft, rsp);
  781 + rsp[rsp_length++] = nb << 1;
  782 + for (i = mapping_address; i < mapping_address + nb; i++) {
  783 + rsp[rsp_length++] = tab_registers[i] >> 8;
  784 + rsp[rsp_length++] = tab_registers[i] & 0xFF;
  785 + }
  786 + }
  787 + }
  788 + break;
  789 + case MODBUS_FC_WRITE_SINGLE_COIL: {
  790 + int mapping_address = address - mb_mapping->start_bits;
  791 +
  792 + if (mapping_address < 0 || mapping_address >= mb_mapping->nb_bits) {
  793 + rsp_length = response_exception(
  794 + ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
  795 + "Illegal data address 0x%0X in write_bit\n",
  796 + address);
  797 + } else {
  798 + int data = (req[offset + 3] << 8) + req[offset + 4];
  799 +
  800 + if (data == 0xFF00 || data == 0x0) {
  801 + mb_mapping->tab_bits[mapping_address] = data ? ON : OFF;
  802 + memcpy(rsp, req, req_length);
  803 + rsp_length = req_length;
  804 + } else {
  805 + rsp_length = response_exception(
  806 + ctx, &sft,
  807 + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, FALSE,
  808 + "Illegal data value 0x%0X in write_bit request at address %0X\n",
  809 + data, address);
  810 + }
  811 + }
  812 + }
  813 + break;
  814 + case MODBUS_FC_WRITE_SINGLE_REGISTER: {
  815 + int mapping_address = address - mb_mapping->start_registers;
  816 +
  817 + if (mapping_address < 0 || mapping_address >= mb_mapping->nb_registers) {
  818 + rsp_length = response_exception(
  819 + ctx, &sft,
  820 + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
  821 + "Illegal data address 0x%0X in write_register\n",
  822 + address);
  823 + } else {
  824 + int data = (req[offset + 3] << 8) + req[offset + 4];
  825 +
  826 + mb_mapping->tab_registers[mapping_address] = data;
  827 + memcpy(rsp, req, req_length);
  828 + rsp_length = req_length;
  829 + }
  830 + }
  831 + break;
  832 + case MODBUS_FC_WRITE_MULTIPLE_COILS: {
  833 + int nb = (req[offset + 3] << 8) + req[offset + 4];
  834 + int mapping_address = address - mb_mapping->start_bits;
  835 +
  836 + if (nb < 1 || MODBUS_MAX_WRITE_BITS < nb) {
  837 + /* May be the indication has been truncated on reading because of
  838 + * invalid address (eg. nb is 0 but the request contains values to
  839 + * write) so it's necessary to flush. */
  840 + rsp_length = response_exception(
  841 + ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE,
  842 + "Illegal number of values %d in write_bits (max %d)\n",
  843 + nb, MODBUS_MAX_WRITE_BITS);
  844 + } else if (mapping_address < 0 ||
  845 + (mapping_address + nb) > mb_mapping->nb_bits) {
  846 + rsp_length = response_exception(
  847 + ctx, &sft,
  848 + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
  849 + "Illegal data address 0x%0X in write_bits\n",
  850 + mapping_address < 0 ? address : address + nb);
  851 + } else {
  852 + /* 6 = byte count */
  853 + modbus_set_bits_from_bytes(mb_mapping->tab_bits, mapping_address, nb,
  854 + &req[offset + 6]);
  855 +
  856 + rsp_length = ctx->backend->build_response_basis(&sft, rsp);
  857 + /* 4 to copy the bit address (2) and the quantity of bits */
  858 + memcpy(rsp + rsp_length, req + rsp_length, 4);
  859 + rsp_length += 4;
  860 + }
  861 + }
  862 + break;
  863 + case MODBUS_FC_WRITE_MULTIPLE_REGISTERS: {
  864 + int nb = (req[offset + 3] << 8) + req[offset + 4];
  865 + int mapping_address = address - mb_mapping->start_registers;
  866 +
  867 + if (nb < 1 || MODBUS_MAX_WRITE_REGISTERS < nb) {
  868 + rsp_length = response_exception(
  869 + ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE,
  870 + "Illegal number of values %d in write_registers (max %d)\n",
  871 + nb, MODBUS_MAX_WRITE_REGISTERS);
  872 + } else if (mapping_address < 0 ||
  873 + (mapping_address + nb) > mb_mapping->nb_registers) {
  874 + rsp_length = response_exception(
  875 + ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
  876 + "Illegal data address 0x%0X in write_registers\n",
  877 + mapping_address < 0 ? address : address + nb);
  878 + } else {
  879 + int i, j;
  880 + for (i = mapping_address, j = 6; i < mapping_address + nb; i++, j += 2) {
  881 + /* 6 and 7 = first value */
  882 + mb_mapping->tab_registers[i] =
  883 + (req[offset + j] << 8) + req[offset + j + 1];
  884 + }
  885 +
  886 + rsp_length = ctx->backend->build_response_basis(&sft, rsp);
  887 + /* 4 to copy the address (2) and the no. of registers */
  888 + memcpy(rsp + rsp_length, req + rsp_length, 4);
  889 + rsp_length += 4;
  890 + }
  891 + }
  892 + break;
  893 + case MODBUS_FC_REPORT_SLAVE_ID: {
  894 + int str_len;
  895 + int byte_count_pos;
  896 +
  897 + rsp_length = ctx->backend->build_response_basis(&sft, rsp);
  898 + /* Skip byte count for now */
  899 + byte_count_pos = rsp_length++;
  900 + rsp[rsp_length++] = _REPORT_SLAVE_ID;
  901 + /* Run indicator status to ON */
  902 + rsp[rsp_length++] = 0xFF;
  903 + /* LMB + length of LIBMODBUS_VERSION_STRING */
  904 + str_len = 3 + strlen(LIBMODBUS_VERSION_STRING);
  905 + memcpy(rsp + rsp_length, "LMB" LIBMODBUS_VERSION_STRING, str_len);
  906 + rsp_length += str_len;
  907 + rsp[byte_count_pos] = rsp_length - byte_count_pos - 1;
  908 + }
  909 + break;
  910 + case MODBUS_FC_READ_EXCEPTION_STATUS:
  911 + if (ctx->debug) {
  912 + fprintf(stderr, "FIXME Not implemented\n");
  913 + }
  914 + errno = ENOPROTOOPT;
  915 + return -1;
  916 + break;
  917 + case MODBUS_FC_MASK_WRITE_REGISTER: {
  918 + int mapping_address = address - mb_mapping->start_registers;
  919 +
  920 + if (mapping_address < 0 || mapping_address >= mb_mapping->nb_registers) {
  921 + rsp_length = response_exception(
  922 + ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
  923 + "Illegal data address 0x%0X in write_register\n",
  924 + address);
  925 + } else {
  926 + uint16_t data = mb_mapping->tab_registers[mapping_address];
  927 + uint16_t and = (req[offset + 3] << 8) + req[offset + 4];
  928 + uint16_t or = (req[offset + 5] << 8) + req[offset + 6];
  929 +
  930 + data = (data & and) | (or & (~and));
  931 + mb_mapping->tab_registers[mapping_address] = data;
  932 + memcpy(rsp, req, req_length);
  933 + rsp_length = req_length;
  934 + }
  935 + }
  936 + break;
  937 + case MODBUS_FC_WRITE_AND_READ_REGISTERS: {
  938 + int nb = (req[offset + 3] << 8) + req[offset + 4];
  939 + uint16_t address_write = (req[offset + 5] << 8) + req[offset + 6];
  940 + int nb_write = (req[offset + 7] << 8) + req[offset + 8];
  941 + int nb_write_bytes = req[offset + 9];
  942 + int mapping_address = address - mb_mapping->start_registers;
  943 + int mapping_address_write = address_write - mb_mapping->start_registers;
  944 +
  945 + if (nb_write < 1 || MODBUS_MAX_WR_WRITE_REGISTERS < nb_write ||
  946 + nb < 1 || MODBUS_MAX_WR_READ_REGISTERS < nb ||
  947 + nb_write_bytes != nb_write * 2) {
  948 + rsp_length = response_exception(
  949 + ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE,
  950 + "Illegal nb of values (W%d, R%d) in write_and_read_registers (max W%d, R%d)\n",
  951 + nb_write, nb, MODBUS_MAX_WR_WRITE_REGISTERS, MODBUS_MAX_WR_READ_REGISTERS);
  952 + } else if (mapping_address < 0 ||
  953 + (mapping_address + nb) > mb_mapping->nb_registers ||
  954 + mapping_address < 0 ||
  955 + (mapping_address_write + nb_write) > mb_mapping->nb_registers) {
  956 + rsp_length = response_exception(
  957 + ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
  958 + "Illegal data read address 0x%0X or write address 0x%0X write_and_read_registers\n",
  959 + mapping_address < 0 ? address : address + nb,
  960 + mapping_address_write < 0 ? address_write : address_write + nb_write);
  961 + } else {
  962 + int i, j;
  963 + rsp_length = ctx->backend->build_response_basis(&sft, rsp);
  964 + rsp[rsp_length++] = nb << 1;
  965 +
  966 + /* Write first.
  967 + 10 and 11 are the offset of the first values to write */
  968 + for (i = mapping_address_write, j = 10;
  969 + i < mapping_address_write + nb_write; i++, j += 2) {
  970 + mb_mapping->tab_registers[i] =
  971 + (req[offset + j] << 8) + req[offset + j + 1];
  972 + }
  973 +
  974 + /* and read the data for the response */
  975 + for (i = mapping_address; i < mapping_address + nb; i++) {
  976 + rsp[rsp_length++] = mb_mapping->tab_registers[i] >> 8;
  977 + rsp[rsp_length++] = mb_mapping->tab_registers[i] & 0xFF;
  978 + }
  979 + }
  980 + }
  981 + break;
  982 +
  983 + default:
  984 + rsp_length = response_exception(
  985 + ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_FUNCTION, rsp, TRUE,
  986 + "Unknown Modbus function code: 0x%0X\n", function);
  987 + break;
  988 + }
  989 +
  990 + /* Suppress any responses when the request was a broadcast */
  991 + return (slave == MODBUS_BROADCAST_ADDRESS) ? 0 : send_msg(ctx, rsp, rsp_length);
  992 +}
  993 +
  994 +int modbus_reply_exception(modbus_t *ctx, const uint8_t *req,
  995 + unsigned int exception_code)
  996 +{
  997 + int offset;
  998 + int slave;
  999 + int function;
  1000 + uint8_t rsp[MAX_MESSAGE_LENGTH];
  1001 + int rsp_length;
  1002 + int dummy_length = 99;
  1003 + sft_t sft;
  1004 +
  1005 + if (ctx == NULL) {
  1006 + errno = EINVAL;
  1007 + return -1;
  1008 + }
  1009 +
  1010 + offset = ctx->backend->header_length;
  1011 + slave = req[offset - 1];
  1012 + function = req[offset];
  1013 +
  1014 + sft.slave = slave;
  1015 + sft.function = function + 0x80;;
  1016 + sft.t_id = ctx->backend->prepare_response_tid(req, &dummy_length);
  1017 + rsp_length = ctx->backend->build_response_basis(&sft, rsp);
  1018 +
  1019 + /* Positive exception code */
  1020 + if (exception_code < MODBUS_EXCEPTION_MAX) {
  1021 + rsp[rsp_length++] = exception_code;
  1022 + return send_msg(ctx, rsp, rsp_length);
  1023 + } else {
  1024 + errno = EINVAL;
  1025 + return -1;
  1026 + }
  1027 +}
  1028 +
  1029 +/* Reads IO status */
  1030 +static int read_io_status(modbus_t *ctx, int function,
  1031 + int addr, int nb, uint8_t *dest)
  1032 +{
  1033 + int rc;
  1034 + int req_length;
  1035 +
  1036 + uint8_t req[_MIN_REQ_LENGTH];
  1037 + uint8_t rsp[MAX_MESSAGE_LENGTH];
  1038 +
  1039 + req_length = ctx->backend->build_request_basis(ctx, function, addr, nb, req);
  1040 +
  1041 + rc = send_msg(ctx, req, req_length);
  1042 + if (rc > 0) {
  1043 + int i, temp, bit;
  1044 + int pos = 0;
  1045 + int offset;
  1046 + int offset_end;
  1047 +
  1048 + rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
  1049 + if (rc == -1)
  1050 + return -1;
  1051 +
  1052 + rc = check_confirmation(ctx, req, rsp, rc);
  1053 + if (rc == -1)
  1054 + return -1;
  1055 +
  1056 + offset = ctx->backend->header_length + 2;
  1057 + offset_end = offset + rc;
  1058 + for (i = offset; i < offset_end; i++) {
  1059 + /* Shift reg hi_byte to temp */
  1060 + temp = rsp[i];
  1061 +
  1062 + for (bit = 0x01; (bit & 0xff) && (pos < nb);) {
  1063 + dest[pos++] = (temp & bit) ? TRUE : FALSE;
  1064 + bit = bit << 1;
  1065 + }
  1066 +
  1067 + }
  1068 + }
  1069 +
  1070 + return rc;
  1071 +}
  1072 +
  1073 +/* Reads the boolean status of bits and sets the array elements
  1074 + in the destination to TRUE or FALSE (single bits). */
  1075 +int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)
  1076 +{
  1077 + int rc;
  1078 +
  1079 + if (ctx == NULL) {
  1080 + errno = EINVAL;
  1081 + return -1;
  1082 + }
  1083 +
  1084 + if (nb > MODBUS_MAX_READ_BITS) {
  1085 + if (ctx->debug) {
  1086 + fprintf(stderr,
  1087 + "ERROR Too many bits requested (%d > %d)\n",
  1088 + nb, MODBUS_MAX_READ_BITS);
  1089 + }
  1090 + errno = EMBMDATA;
  1091 + return -1;
  1092 + }
  1093 +
  1094 + rc = read_io_status(ctx, MODBUS_FC_READ_COILS, addr, nb, dest);
  1095 +
  1096 + if (rc == -1)
  1097 + return -1;
  1098 + else
  1099 + return nb;
  1100 +}
  1101 +
  1102 +
  1103 +/* Same as modbus_read_bits but reads the remote device input table */
  1104 +int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)
  1105 +{
  1106 + int rc;
  1107 +
  1108 + if (ctx == NULL) {
  1109 + errno = EINVAL;
  1110 + return -1;
  1111 + }
  1112 +
  1113 + if (nb > MODBUS_MAX_READ_BITS) {
  1114 + if (ctx->debug) {
  1115 + fprintf(stderr,
  1116 + "ERROR Too many discrete inputs requested (%d > %d)\n",
  1117 + nb, MODBUS_MAX_READ_BITS);
  1118 + }
  1119 + errno = EMBMDATA;
  1120 + return -1;
  1121 + }
  1122 +
  1123 + rc = read_io_status(ctx, MODBUS_FC_READ_DISCRETE_INPUTS, addr, nb, dest);
  1124 +
  1125 + if (rc == -1)
  1126 + return -1;
  1127 + else
  1128 + return nb;
  1129 +}
  1130 +
  1131 +/* Reads the data from a remove device and put that data into an array */
  1132 +static int read_registers(modbus_t *ctx, int function, int addr, int nb,
  1133 + uint16_t *dest)
  1134 +{
  1135 + int rc;
  1136 + int req_length;
  1137 + uint8_t req[_MIN_REQ_LENGTH];
  1138 + uint8_t rsp[MAX_MESSAGE_LENGTH];
  1139 +
  1140 + if (nb > MODBUS_MAX_READ_REGISTERS) {
  1141 + if (ctx->debug) {
  1142 + fprintf(stderr,
  1143 + "ERROR Too many registers requested (%d > %d)\n",
  1144 + nb, MODBUS_MAX_READ_REGISTERS);
  1145 + }
  1146 + errno = EMBMDATA;
  1147 + return -1;
  1148 + }
  1149 +
  1150 + req_length = ctx->backend->build_request_basis(ctx, function, addr, nb, req);
  1151 +
  1152 + rc = send_msg(ctx, req, req_length);
  1153 + if (rc > 0) {
  1154 + int offset;
  1155 + int i;
  1156 +
  1157 + rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
  1158 + if (rc == -1)
  1159 + return -1;
  1160 +
  1161 + rc = check_confirmation(ctx, req, rsp, rc);
  1162 + if (rc == -1)
  1163 + return -1;
  1164 +
  1165 + offset = ctx->backend->header_length;
  1166 +
  1167 + for (i = 0; i < rc; i++) {
  1168 + /* shift reg hi_byte to temp OR with lo_byte */
  1169 + dest[i] = (rsp[offset + 2 + (i << 1)] << 8) |
  1170 + rsp[offset + 3 + (i << 1)];
  1171 + }
  1172 + }
  1173 +
  1174 + return rc;
  1175 +}
  1176 +
  1177 +/* Reads the holding registers of remote device and put the data into an
  1178 + array */
  1179 +int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)
  1180 +{
  1181 + int status;
  1182 +
  1183 + if (ctx == NULL) {
  1184 + errno = EINVAL;
  1185 + return -1;
  1186 + }
  1187 +
  1188 + if (nb > MODBUS_MAX_READ_REGISTERS) {
  1189 + if (ctx->debug) {
  1190 + fprintf(stderr,
  1191 + "ERROR Too many registers requested (%d > %d)\n",
  1192 + nb, MODBUS_MAX_READ_REGISTERS);
  1193 + }
  1194 + errno = EMBMDATA;
  1195 + return -1;
  1196 + }
  1197 +
  1198 + status = read_registers(ctx, MODBUS_FC_READ_HOLDING_REGISTERS,
  1199 + addr, nb, dest);
  1200 + return status;
  1201 +}
  1202 +
  1203 +/* Reads the input registers of remote device and put the data into an array */
  1204 +int modbus_read_input_registers(modbus_t *ctx, int addr, int nb,
  1205 + uint16_t *dest)
  1206 +{
  1207 + int status;
  1208 +
  1209 + if (ctx == NULL) {
  1210 + errno = EINVAL;
  1211 + return -1;
  1212 + }
  1213 +
  1214 + if (nb > MODBUS_MAX_READ_REGISTERS) {
  1215 + fprintf(stderr,
  1216 + "ERROR Too many input registers requested (%d > %d)\n",
  1217 + nb, MODBUS_MAX_READ_REGISTERS);
  1218 + errno = EMBMDATA;
  1219 + return -1;
  1220 + }
  1221 +
  1222 + status = read_registers(ctx, MODBUS_FC_READ_INPUT_REGISTERS,
  1223 + addr, nb, dest);
  1224 +
  1225 + return status;
  1226 +}
  1227 +
  1228 +/* Write a value to the specified register of the remote device.
  1229 + Used by write_bit and write_register */
  1230 +static int write_single(modbus_t *ctx, int function, int addr, int value)
  1231 +{
  1232 + int rc;
  1233 + int req_length;
  1234 + uint8_t req[_MIN_REQ_LENGTH];
  1235 +
  1236 + if (ctx == NULL) {
  1237 + errno = EINVAL;
  1238 + return -1;
  1239 + }
  1240 +
  1241 + req_length = ctx->backend->build_request_basis(ctx, function, addr, value, req);
  1242 +
  1243 + rc = send_msg(ctx, req, req_length);
  1244 + if (rc > 0) {
  1245 + /* Used by write_bit and write_register */
  1246 + uint8_t rsp[MAX_MESSAGE_LENGTH];
  1247 +
  1248 + rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
  1249 + if (rc == -1)
  1250 + return -1;
  1251 +
  1252 + rc = check_confirmation(ctx, req, rsp, rc);
  1253 + }
  1254 +
  1255 + return rc;
  1256 +}
  1257 +
  1258 +/* Turns ON or OFF a single bit of the remote device */
  1259 +int modbus_write_bit(modbus_t *ctx, int addr, int status)
  1260 +{
  1261 + if (ctx == NULL) {
  1262 + errno = EINVAL;
  1263 + return -1;
  1264 + }
  1265 +
  1266 + return write_single(ctx, MODBUS_FC_WRITE_SINGLE_COIL, addr,
  1267 + status ? 0xFF00 : 0);
  1268 +}
  1269 +
  1270 +/* Writes a value in one register of the remote device */
  1271 +int modbus_write_register(modbus_t *ctx, int addr, int value)
  1272 +{
  1273 + if (ctx == NULL) {
  1274 + errno = EINVAL;
  1275 + return -1;
  1276 + }
  1277 +
  1278 + return write_single(ctx, MODBUS_FC_WRITE_SINGLE_REGISTER, addr, value);
  1279 +}
  1280 +
  1281 +/* Write the bits of the array in the remote device */
  1282 +int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src)
  1283 +{
  1284 + int rc;
  1285 + int i;
  1286 + int byte_count;
  1287 + int req_length;
  1288 + int bit_check = 0;
  1289 + int pos = 0;
  1290 + uint8_t req[MAX_MESSAGE_LENGTH];
  1291 +
  1292 + if (ctx == NULL) {
  1293 + errno = EINVAL;
  1294 + return -1;
  1295 + }
  1296 +
  1297 + if (nb > MODBUS_MAX_WRITE_BITS) {
  1298 + if (ctx->debug) {
  1299 + fprintf(stderr, "ERROR Writing too many bits (%d > %d)\n",
  1300 + nb, MODBUS_MAX_WRITE_BITS);
  1301 + }
  1302 + errno = EMBMDATA;
  1303 + return -1;
  1304 + }
  1305 +
  1306 + req_length = ctx->backend->build_request_basis(ctx,
  1307 + MODBUS_FC_WRITE_MULTIPLE_COILS,
  1308 + addr, nb, req);
  1309 + byte_count = (nb / 8) + ((nb % 8) ? 1 : 0);
  1310 + req[req_length++] = byte_count;
  1311 +
  1312 + for (i = 0; i < byte_count; i++) {
  1313 + int bit;
  1314 +
  1315 + bit = 0x01;
  1316 + req[req_length] = 0;
  1317 +
  1318 + while ((bit & 0xFF) && (bit_check++ < nb)) {
  1319 + if (src[pos++])
  1320 + req[req_length] |= bit;
  1321 + else
  1322 + req[req_length] &=~ bit;
  1323 +
  1324 + bit = bit << 1;
  1325 + }
  1326 + req_length++;
  1327 + }
  1328 +
  1329 + rc = send_msg(ctx, req, req_length);
  1330 + if (rc > 0) {
  1331 + uint8_t rsp[MAX_MESSAGE_LENGTH];
  1332 +
  1333 + rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
  1334 + if (rc == -1)
  1335 + return -1;
  1336 +
  1337 + rc = check_confirmation(ctx, req, rsp, rc);
  1338 + }
  1339 +
  1340 +
  1341 + return rc;
  1342 +}
  1343 +
  1344 +/* Write the values from the array to the registers of the remote device */
  1345 +int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src)
  1346 +{
  1347 + int rc;
  1348 + int i;
  1349 + int req_length;
  1350 + int byte_count;
  1351 + uint8_t req[MAX_MESSAGE_LENGTH];
  1352 +
  1353 + if (ctx == NULL) {
  1354 + errno = EINVAL;
  1355 + return -1;
  1356 + }
  1357 +
  1358 + if (nb > MODBUS_MAX_WRITE_REGISTERS) {
  1359 + if (ctx->debug) {
  1360 + fprintf(stderr,
  1361 + "ERROR Trying to write to too many registers (%d > %d)\n",
  1362 + nb, MODBUS_MAX_WRITE_REGISTERS);
  1363 + }
  1364 + errno = EMBMDATA;
  1365 + return -1;
  1366 + }
  1367 +
  1368 + req_length = ctx->backend->build_request_basis(ctx,
  1369 + MODBUS_FC_WRITE_MULTIPLE_REGISTERS,
  1370 + addr, nb, req);
  1371 + byte_count = nb * 2;
  1372 + req[req_length++] = byte_count;
  1373 +
  1374 + for (i = 0; i < nb; i++) {
  1375 + req[req_length++] = src[i] >> 8;
  1376 + req[req_length++] = src[i] & 0x00FF;
  1377 + }
  1378 +
  1379 + rc = send_msg(ctx, req, req_length);
  1380 + if (rc > 0) {
  1381 + uint8_t rsp[MAX_MESSAGE_LENGTH];
  1382 +
  1383 + rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
  1384 + if (rc == -1)
  1385 + return -1;
  1386 +
  1387 + rc = check_confirmation(ctx, req, rsp, rc);
  1388 + }
  1389 +
  1390 + return rc;
  1391 +}
  1392 +
  1393 +int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask)
  1394 +{
  1395 + int rc;
  1396 + int req_length;
  1397 + /* The request length can not exceed _MIN_REQ_LENGTH - 2 and 4 bytes to
  1398 + * store the masks. The ugly substraction is there to remove the 'nb' value
  1399 + * (2 bytes) which is not used. */
  1400 + uint8_t req[_MIN_REQ_LENGTH + 2];
  1401 +
  1402 + req_length = ctx->backend->build_request_basis(ctx,
  1403 + MODBUS_FC_MASK_WRITE_REGISTER,
  1404 + addr, 0, req);
  1405 +
  1406 + /* HACKISH, count is not used */
  1407 + req_length -= 2;
  1408 +
  1409 + req[req_length++] = and_mask >> 8;
  1410 + req[req_length++] = and_mask & 0x00ff;
  1411 + req[req_length++] = or_mask >> 8;
  1412 + req[req_length++] = or_mask & 0x00ff;
  1413 +
  1414 + rc = send_msg(ctx, req, req_length);
  1415 + if (rc > 0) {
  1416 + /* Used by write_bit and write_register */
  1417 + uint8_t rsp[MAX_MESSAGE_LENGTH];
  1418 +
  1419 + rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
  1420 + if (rc == -1)
  1421 + return -1;
  1422 +
  1423 + rc = check_confirmation(ctx, req, rsp, rc);
  1424 + }
  1425 +
  1426 + return rc;
  1427 +}
  1428 +
  1429 +/* Write multiple registers from src array to remote device and read multiple
  1430 + registers from remote device to dest array. */
  1431 +int modbus_write_and_read_registers(modbus_t *ctx,
  1432 + int write_addr, int write_nb,
  1433 + const uint16_t *src,
  1434 + int read_addr, int read_nb,
  1435 + uint16_t *dest)
  1436 +
  1437 +{
  1438 + int rc;
  1439 + int req_length;
  1440 + int i;
  1441 + int byte_count;
  1442 + uint8_t req[MAX_MESSAGE_LENGTH];
  1443 + uint8_t rsp[MAX_MESSAGE_LENGTH];
  1444 +
  1445 + if (ctx == NULL) {
  1446 + errno = EINVAL;
  1447 + return -1;
  1448 + }
  1449 +
  1450 + if (write_nb > MODBUS_MAX_WR_WRITE_REGISTERS) {
  1451 + if (ctx->debug) {
  1452 + fprintf(stderr,
  1453 + "ERROR Too many registers to write (%d > %d)\n",
  1454 + write_nb, MODBUS_MAX_WR_WRITE_REGISTERS);
  1455 + }
  1456 + errno = EMBMDATA;
  1457 + return -1;
  1458 + }
  1459 +
  1460 + if (read_nb > MODBUS_MAX_WR_READ_REGISTERS) {
  1461 + if (ctx->debug) {
  1462 + fprintf(stderr,
  1463 + "ERROR Too many registers requested (%d > %d)\n",
  1464 + read_nb, MODBUS_MAX_WR_READ_REGISTERS);
  1465 + }
  1466 + errno = EMBMDATA;
  1467 + return -1;
  1468 + }
  1469 + req_length = ctx->backend->build_request_basis(ctx,
  1470 + MODBUS_FC_WRITE_AND_READ_REGISTERS,
  1471 + read_addr, read_nb, req);
  1472 +
  1473 + req[req_length++] = write_addr >> 8;
  1474 + req[req_length++] = write_addr & 0x00ff;
  1475 + req[req_length++] = write_nb >> 8;
  1476 + req[req_length++] = write_nb & 0x00ff;
  1477 + byte_count = write_nb * 2;
  1478 + req[req_length++] = byte_count;
  1479 +
  1480 + for (i = 0; i < write_nb; i++) {
  1481 + req[req_length++] = src[i] >> 8;
  1482 + req[req_length++] = src[i] & 0x00FF;
  1483 + }
  1484 +
  1485 + rc = send_msg(ctx, req, req_length);
  1486 + if (rc > 0) {
  1487 + int offset;
  1488 +
  1489 + rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
  1490 + if (rc == -1)
  1491 + return -1;
  1492 +
  1493 + rc = check_confirmation(ctx, req, rsp, rc);
  1494 + if (rc == -1)
  1495 + return -1;
  1496 +
  1497 + offset = ctx->backend->header_length;
  1498 + for (i = 0; i < rc; i++) {
  1499 + /* shift reg hi_byte to temp OR with lo_byte */
  1500 + dest[i] = (rsp[offset + 2 + (i << 1)] << 8) |
  1501 + rsp[offset + 3 + (i << 1)];
  1502 + }
  1503 + }
  1504 +
  1505 + return rc;
  1506 +}
  1507 +
  1508 +/* Send a request to get the slave ID of the device (only available in serial
  1509 + communication). */
  1510 +int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest)
  1511 +{
  1512 + int rc;
  1513 + int req_length;
  1514 + uint8_t req[_MIN_REQ_LENGTH];
  1515 +
  1516 + if (ctx == NULL || max_dest <= 0) {
  1517 + errno = EINVAL;
  1518 + return -1;
  1519 + }
  1520 +
  1521 + req_length = ctx->backend->build_request_basis(ctx, MODBUS_FC_REPORT_SLAVE_ID,
  1522 + 0, 0, req);
  1523 +
  1524 + /* HACKISH, addr and count are not used */
  1525 + req_length -= 4;
  1526 +
  1527 + rc = send_msg(ctx, req, req_length);
  1528 + if (rc > 0) {
  1529 + int i;
  1530 + int offset;
  1531 + uint8_t rsp[MAX_MESSAGE_LENGTH];
  1532 +
  1533 + rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
  1534 + if (rc == -1)
  1535 + return -1;
  1536 +
  1537 + rc = check_confirmation(ctx, req, rsp, rc);
  1538 + if (rc == -1)
  1539 + return -1;
  1540 +
  1541 + offset = ctx->backend->header_length + 2;
  1542 +
  1543 + /* Byte count, slave id, run indicator status and
  1544 + additional data. Truncate copy to max_dest. */
  1545 + for (i=0; i < rc && i < max_dest; i++) {
  1546 + dest[i] = rsp[offset + i];
  1547 + }
  1548 + }
  1549 +
  1550 + return rc;
  1551 +}
  1552 +
  1553 +void _modbus_init_common(modbus_t *ctx)
  1554 +{
  1555 + /* Slave and socket are initialized to -1 */
  1556 + ctx->slave = -1;
  1557 + ctx->s = -1;
  1558 +
  1559 + ctx->debug = FALSE;
  1560 + ctx->error_recovery = MODBUS_ERROR_RECOVERY_NONE;
  1561 +
  1562 + ctx->response_timeout.tv_sec = 0;
  1563 + ctx->response_timeout.tv_usec = _RESPONSE_TIMEOUT;
  1564 +
  1565 + ctx->byte_timeout.tv_sec = 0;
  1566 + ctx->byte_timeout.tv_usec = _BYTE_TIMEOUT;
  1567 +}
  1568 +
  1569 +/* Define the slave number */
  1570 +int modbus_set_slave(modbus_t *ctx, int slave)
  1571 +{
  1572 + if (ctx == NULL) {
  1573 + errno = EINVAL;
  1574 + return -1;
  1575 + }
  1576 +
  1577 + return ctx->backend->set_slave(ctx, slave);
  1578 +}
  1579 +
  1580 +int modbus_set_error_recovery(modbus_t *ctx,
  1581 + modbus_error_recovery_mode error_recovery)
  1582 +{
  1583 + if (ctx == NULL) {
  1584 + errno = EINVAL;
  1585 + return -1;
  1586 + }
  1587 +
  1588 + /* The type of modbus_error_recovery_mode is unsigned enum */
  1589 + ctx->error_recovery = (uint8_t) error_recovery;
  1590 + return 0;
  1591 +}
  1592 +
  1593 +int modbus_set_socket(modbus_t *ctx, int s)
  1594 +{
  1595 + if (ctx == NULL) {
  1596 + errno = EINVAL;
  1597 + return -1;
  1598 + }
  1599 +
  1600 + ctx->s = s;
  1601 + return 0;
  1602 +}
  1603 +
  1604 +int modbus_get_socket(modbus_t *ctx)
  1605 +{
  1606 + if (ctx == NULL) {
  1607 + errno = EINVAL;
  1608 + return -1;
  1609 + }
  1610 +
  1611 + return ctx->s;
  1612 +}
  1613 +
  1614 +/* Get the timeout interval used to wait for a response */
  1615 +int modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec)
  1616 +{
  1617 + if (ctx == NULL) {
  1618 + errno = EINVAL;
  1619 + return -1;
  1620 + }
  1621 +
  1622 + *to_sec = ctx->response_timeout.tv_sec;
  1623 + *to_usec = ctx->response_timeout.tv_usec;
  1624 + return 0;
  1625 +}
  1626 +
  1627 +int modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec)
  1628 +{
  1629 + if (ctx == NULL ||
  1630 + (to_sec == 0 && to_usec == 0) || to_usec > 999999) {
  1631 + errno = EINVAL;
  1632 + return -1;
  1633 + }
  1634 +
  1635 + ctx->response_timeout.tv_sec = to_sec;
  1636 + ctx->response_timeout.tv_usec = to_usec;
  1637 + return 0;
  1638 +}
  1639 +
  1640 +/* Get the timeout interval between two consecutive bytes of a message */
  1641 +int modbus_get_byte_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec)
  1642 +{
  1643 + if (ctx == NULL) {
  1644 + errno = EINVAL;
  1645 + return -1;
  1646 + }
  1647 +
  1648 + *to_sec = ctx->byte_timeout.tv_sec;
  1649 + *to_usec = ctx->byte_timeout.tv_usec;
  1650 + return 0;
  1651 +}
  1652 +
  1653 +int modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec)
  1654 +{
  1655 + /* Byte timeout can be disabled when both values are zero */
  1656 + if (ctx == NULL || to_usec > 999999) {
  1657 + errno = EINVAL;
  1658 + return -1;
  1659 + }
  1660 +
  1661 + ctx->byte_timeout.tv_sec = to_sec;
  1662 + ctx->byte_timeout.tv_usec = to_usec;
  1663 + return 0;
  1664 +}
  1665 +
  1666 +int modbus_get_header_length(modbus_t *ctx)
  1667 +{
  1668 + if (ctx == NULL) {
  1669 + errno = EINVAL;
  1670 + return -1;
  1671 + }
  1672 +
  1673 + return ctx->backend->header_length;
  1674 +}
  1675 +
  1676 +int modbus_connect(modbus_t *ctx)
  1677 +{
  1678 + if (ctx == NULL) {
  1679 + errno = EINVAL;
  1680 + return -1;
  1681 + }
  1682 +
  1683 + return ctx->backend->connect(ctx);
  1684 +}
  1685 +
  1686 +void modbus_close(modbus_t *ctx)
  1687 +{
  1688 + if (ctx == NULL)
  1689 + return;
  1690 +
  1691 + ctx->backend->close(ctx);
  1692 +}
  1693 +
  1694 +void modbus_free(modbus_t *ctx)
  1695 +{
  1696 + if (ctx == NULL)
  1697 + return;
  1698 +
  1699 + ctx->backend->free(ctx);
  1700 +}
  1701 +
  1702 +int modbus_set_debug(modbus_t *ctx, int flag)
  1703 +{
  1704 + if (ctx == NULL) {
  1705 + errno = EINVAL;
  1706 + return -1;
  1707 + }
  1708 +
  1709 + ctx->debug = flag;
  1710 + return 0;
  1711 +}
  1712 +
  1713 +/* Allocates 4 arrays to store bits, input bits, registers and inputs
  1714 + registers. The pointers are stored in modbus_mapping structure.
  1715 +
  1716 + The modbus_mapping_new_ranges() function shall return the new allocated
  1717 + structure if successful. Otherwise it shall return NULL and set errno to
  1718 + ENOMEM. */
  1719 +modbus_mapping_t* modbus_mapping_new_start_address(
  1720 + unsigned int start_bits, unsigned int nb_bits,
  1721 + unsigned int start_input_bits, unsigned int nb_input_bits,
  1722 + unsigned int start_registers, unsigned int nb_registers,
  1723 + unsigned int start_input_registers, unsigned int nb_input_registers)
  1724 +{
  1725 + modbus_mapping_t *mb_mapping;
  1726 +
  1727 + mb_mapping = (modbus_mapping_t *)malloc(sizeof(modbus_mapping_t));
  1728 + if (mb_mapping == NULL) {
  1729 + return NULL;
  1730 + }
  1731 +
  1732 + /* 0X */
  1733 + mb_mapping->nb_bits = nb_bits;
  1734 + mb_mapping->start_bits = start_bits;
  1735 + if (nb_bits == 0) {
  1736 + mb_mapping->tab_bits = NULL;
  1737 + } else {
  1738 + /* Negative number raises a POSIX error */
  1739 + mb_mapping->tab_bits =
  1740 + (uint8_t *) malloc(nb_bits * sizeof(uint8_t));
  1741 + if (mb_mapping->tab_bits == NULL) {
  1742 + free(mb_mapping);
  1743 + return NULL;
  1744 + }
  1745 + memset(mb_mapping->tab_bits, 0, nb_bits * sizeof(uint8_t));
  1746 + }
  1747 +
  1748 + /* 1X */
  1749 + mb_mapping->nb_input_bits = nb_input_bits;
  1750 + mb_mapping->start_input_bits = start_input_bits;
  1751 + if (nb_input_bits == 0) {
  1752 + mb_mapping->tab_input_bits = NULL;
  1753 + } else {
  1754 + mb_mapping->tab_input_bits =
  1755 + (uint8_t *) malloc(nb_input_bits * sizeof(uint8_t));
  1756 + if (mb_mapping->tab_input_bits == NULL) {
  1757 + free(mb_mapping->tab_bits);
  1758 + free(mb_mapping);
  1759 + return NULL;
  1760 + }
  1761 + memset(mb_mapping->tab_input_bits, 0, nb_input_bits * sizeof(uint8_t));
  1762 + }
  1763 +
  1764 + /* 4X */
  1765 + mb_mapping->nb_registers = nb_registers;
  1766 + mb_mapping->start_registers = start_registers;
  1767 + if (nb_registers == 0) {
  1768 + mb_mapping->tab_registers = NULL;
  1769 + } else {
  1770 + mb_mapping->tab_registers =
  1771 + (uint16_t *) malloc(nb_registers * sizeof(uint16_t));
  1772 + if (mb_mapping->tab_registers == NULL) {
  1773 + free(mb_mapping->tab_input_bits);
  1774 + free(mb_mapping->tab_bits);
  1775 + free(mb_mapping);
  1776 + return NULL;
  1777 + }
  1778 + memset(mb_mapping->tab_registers, 0, nb_registers * sizeof(uint16_t));
  1779 + }
  1780 +
  1781 + /* 3X */
  1782 + mb_mapping->nb_input_registers = nb_input_registers;
  1783 + mb_mapping->start_input_registers = start_input_registers;
  1784 + if (nb_input_registers == 0) {
  1785 + mb_mapping->tab_input_registers = NULL;
  1786 + } else {
  1787 + mb_mapping->tab_input_registers =
  1788 + (uint16_t *) malloc(nb_input_registers * sizeof(uint16_t));
  1789 + if (mb_mapping->tab_input_registers == NULL) {
  1790 + free(mb_mapping->tab_registers);
  1791 + free(mb_mapping->tab_input_bits);
  1792 + free(mb_mapping->tab_bits);
  1793 + free(mb_mapping);
  1794 + return NULL;
  1795 + }
  1796 + memset(mb_mapping->tab_input_registers, 0,
  1797 + nb_input_registers * sizeof(uint16_t));
  1798 + }
  1799 +
  1800 + return mb_mapping;
  1801 +}
  1802 +
  1803 +modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits,
  1804 + int nb_registers, int nb_input_registers)
  1805 +{
  1806 + return modbus_mapping_new_start_address(
  1807 + 0, nb_bits, 0, nb_input_bits, 0, nb_registers, 0, nb_input_registers);
  1808 +}
  1809 +
  1810 +/* Frees the 4 arrays */
  1811 +void modbus_mapping_free(modbus_mapping_t *mb_mapping)
  1812 +{
  1813 + if (mb_mapping == NULL) {
  1814 + return;
  1815 + }
  1816 +
  1817 + free(mb_mapping->tab_input_registers);
  1818 + free(mb_mapping->tab_registers);
  1819 + free(mb_mapping->tab_input_bits);
  1820 + free(mb_mapping->tab_bits);
  1821 + free(mb_mapping);
  1822 +}
  1823 +
  1824 +#ifndef HAVE_STRLCPY
  1825 +/*
  1826 + * Function strlcpy was originally developed by
  1827 + * Todd C. Miller <Todd.Miller@courtesan.com> to simplify writing secure code.
  1828 + * See ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/strlcpy.3
  1829 + * for more information.
  1830 + *
  1831 + * Thank you Ulrich Drepper... not!
  1832 + *
  1833 + * Copy src to string dest of size dest_size. At most dest_size-1 characters
  1834 + * will be copied. Always NUL terminates (unless dest_size == 0). Returns
  1835 + * strlen(src); if retval >= dest_size, truncation occurred.
  1836 + */
  1837 +size_t strlcpy(char *dest, const char *src, size_t dest_size)
  1838 +{
  1839 + register char *d = dest;
  1840 + register const char *s = src;
  1841 + register size_t n = dest_size;
  1842 +
  1843 + /* Copy as many bytes as will fit */
  1844 + if (n != 0 && --n != 0) {
  1845 + do {
  1846 + if ((*d++ = *s++) == 0)
  1847 + break;
  1848 + } while (--n != 0);
  1849 + }
  1850 +
  1851 + /* Not enough room in dest, add NUL and traverse rest of src */
  1852 + if (n == 0) {
  1853 + if (dest_size != 0)
  1854 + *d = '\0'; /* NUL-terminate dest */
  1855 + while (*s++)
  1856 + ;
  1857 + }
  1858 +
  1859 + return (s - src - 1); /* count does not include NUL */
  1860 +}
  1861 +#endif
... ...
3rdparty/libmodbus/modbus.h 0 โ†’ 100644
  1 +/*
  2 + * Copyright ยฉ 2001-2013 Stรฉphane Raimbault <stephane.raimbault@gmail.com>
  3 + *
  4 + * SPDX-License-Identifier: LGPL-2.1+
  5 + */
  6 +
  7 +#ifndef MODBUS_H
  8 +#define MODBUS_H
  9 +
  10 +/* Add this for macros that defined unix flavor */
  11 +#if (defined(__unix__) || defined(unix)) && !defined(USG)
  12 +#include <sys/param.h>
  13 +#endif
  14 +
  15 +#ifndef _MSC_VER
  16 +#include <stdint.h>
  17 +#else
  18 +#include "stdint.h"
  19 +#endif
  20 +
  21 +#include "modbus-version.h"
  22 +
  23 +#if defined(_MSC_VER)
  24 +# if defined(DLLBUILD)
  25 +/* define DLLBUILD when building the DLL */
  26 +# define MODBUS_API __declspec(dllexport)
  27 +# else
  28 +# define MODBUS_API __declspec(dllimport)
  29 +# endif
  30 +#else
  31 +# define MODBUS_API
  32 +#endif
  33 +
  34 +#ifdef __cplusplus
  35 +# define MODBUS_BEGIN_DECLS extern "C" {
  36 +# define MODBUS_END_DECLS }
  37 +#else
  38 +# define MODBUS_BEGIN_DECLS
  39 +# define MODBUS_END_DECLS
  40 +#endif
  41 +
  42 +MODBUS_BEGIN_DECLS
  43 +
  44 +#ifndef FALSE
  45 +#define FALSE 0
  46 +#endif
  47 +
  48 +#ifndef TRUE
  49 +#define TRUE 1
  50 +#endif
  51 +
  52 +#ifndef OFF
  53 +#define OFF 0
  54 +#endif
  55 +
  56 +#ifndef ON
  57 +#define ON 1
  58 +#endif
  59 +
  60 +/* Modbus function codes */
  61 +#define MODBUS_FC_READ_COILS 0x01
  62 +#define MODBUS_FC_READ_DISCRETE_INPUTS 0x02
  63 +#define MODBUS_FC_READ_HOLDING_REGISTERS 0x03
  64 +#define MODBUS_FC_READ_INPUT_REGISTERS 0x04
  65 +#define MODBUS_FC_WRITE_SINGLE_COIL 0x05
  66 +#define MODBUS_FC_WRITE_SINGLE_REGISTER 0x06
  67 +#define MODBUS_FC_READ_EXCEPTION_STATUS 0x07
  68 +#define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F
  69 +#define MODBUS_FC_WRITE_MULTIPLE_REGISTERS 0x10
  70 +#define MODBUS_FC_REPORT_SLAVE_ID 0x11
  71 +#define MODBUS_FC_MASK_WRITE_REGISTER 0x16
  72 +#define MODBUS_FC_WRITE_AND_READ_REGISTERS 0x17
  73 +
  74 +#define MODBUS_BROADCAST_ADDRESS 0
  75 +
  76 +/* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 1 page 12)
  77 + * Quantity of Coils to read (2 bytes): 1 to 2000 (0x7D0)
  78 + * (chapter 6 section 11 page 29)
  79 + * Quantity of Coils to write (2 bytes): 1 to 1968 (0x7B0)
  80 + */
  81 +#define MODBUS_MAX_READ_BITS 2000
  82 +#define MODBUS_MAX_WRITE_BITS 1968
  83 +
  84 +/* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 3 page 15)
  85 + * Quantity of Registers to read (2 bytes): 1 to 125 (0x7D)
  86 + * (chapter 6 section 12 page 31)
  87 + * Quantity of Registers to write (2 bytes) 1 to 123 (0x7B)
  88 + * (chapter 6 section 17 page 38)
  89 + * Quantity of Registers to write in R/W registers (2 bytes) 1 to 121 (0x79)
  90 + */
  91 +#define MODBUS_MAX_READ_REGISTERS 125
  92 +#define MODBUS_MAX_WRITE_REGISTERS 123
  93 +#define MODBUS_MAX_WR_WRITE_REGISTERS 121
  94 +#define MODBUS_MAX_WR_READ_REGISTERS 125
  95 +
  96 +/* The size of the MODBUS PDU is limited by the size constraint inherited from
  97 + * the first MODBUS implementation on Serial Line network (max. RS485 ADU = 256
  98 + * bytes). Therefore, MODBUS PDU for serial line communication = 256 - Server
  99 + * address (1 byte) - CRC (2 bytes) = 253 bytes.
  100 + */
  101 +#define MODBUS_MAX_PDU_LENGTH 253
  102 +
  103 +/* Consequently:
  104 + * - RTU MODBUS ADU = 253 bytes + Server address (1 byte) + CRC (2 bytes) = 256
  105 + * bytes.
  106 + * - TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes.
  107 + * so the maximum of both backend in 260 bytes. This size can used to allocate
  108 + * an array of bytes to store responses and it will be compatible with the two
  109 + * backends.
  110 + */
  111 +#define MODBUS_MAX_ADU_LENGTH 260
  112 +
  113 +/* Random number to avoid errno conflicts */
  114 +#define MODBUS_ENOBASE 112345678
  115 +
  116 +/* Protocol exceptions */
  117 +enum {
  118 + MODBUS_EXCEPTION_ILLEGAL_FUNCTION = 0x01,
  119 + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS,
  120 + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE,
  121 + MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE,
  122 + MODBUS_EXCEPTION_ACKNOWLEDGE,
  123 + MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY,
  124 + MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE,
  125 + MODBUS_EXCEPTION_MEMORY_PARITY,
  126 + MODBUS_EXCEPTION_NOT_DEFINED,
  127 + MODBUS_EXCEPTION_GATEWAY_PATH,
  128 + MODBUS_EXCEPTION_GATEWAY_TARGET,
  129 + MODBUS_EXCEPTION_MAX
  130 +};
  131 +
  132 +#define EMBXILFUN (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_FUNCTION)
  133 +#define EMBXILADD (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS)
  134 +#define EMBXILVAL (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE)
  135 +#define EMBXSFAIL (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE)
  136 +#define EMBXACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_ACKNOWLEDGE)
  137 +#define EMBXSBUSY (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY)
  138 +#define EMBXNACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE)
  139 +#define EMBXMEMPAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_MEMORY_PARITY)
  140 +#define EMBXGPATH (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_PATH)
  141 +#define EMBXGTAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_TARGET)
  142 +
  143 +/* Native libmodbus error codes */
  144 +#define EMBBADCRC (EMBXGTAR + 1)
  145 +#define EMBBADDATA (EMBXGTAR + 2)
  146 +#define EMBBADEXC (EMBXGTAR + 3)
  147 +#define EMBUNKEXC (EMBXGTAR + 4)
  148 +#define EMBMDATA (EMBXGTAR + 5)
  149 +#define EMBBADSLAVE (EMBXGTAR + 6)
  150 +
  151 +extern const unsigned int libmodbus_version_major;
  152 +extern const unsigned int libmodbus_version_minor;
  153 +extern const unsigned int libmodbus_version_micro;
  154 +
  155 +typedef struct _modbus modbus_t;
  156 +
  157 +typedef struct {
  158 + int nb_bits;
  159 + int start_bits;
  160 + int nb_input_bits;
  161 + int start_input_bits;
  162 + int nb_input_registers;
  163 + int start_input_registers;
  164 + int nb_registers;
  165 + int start_registers;
  166 + uint8_t *tab_bits;
  167 + uint8_t *tab_input_bits;
  168 + uint16_t *tab_input_registers;
  169 + uint16_t *tab_registers;
  170 +} modbus_mapping_t;
  171 +
  172 +typedef enum
  173 +{
  174 + MODBUS_ERROR_RECOVERY_NONE = 0,
  175 + MODBUS_ERROR_RECOVERY_LINK = (1<<1),
  176 + MODBUS_ERROR_RECOVERY_PROTOCOL = (1<<2)
  177 +} modbus_error_recovery_mode;
  178 +
  179 +MODBUS_API int modbus_set_slave(modbus_t* ctx, int slave);
  180 +MODBUS_API int modbus_set_error_recovery(modbus_t *ctx, modbus_error_recovery_mode error_recovery);
  181 +MODBUS_API int modbus_set_socket(modbus_t *ctx, int s);
  182 +MODBUS_API int modbus_get_socket(modbus_t *ctx);
  183 +
  184 +MODBUS_API int modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
  185 +MODBUS_API int modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
  186 +
  187 +MODBUS_API int modbus_get_byte_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
  188 +MODBUS_API int modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
  189 +
  190 +MODBUS_API int modbus_get_header_length(modbus_t *ctx);
  191 +
  192 +MODBUS_API int modbus_connect(modbus_t *ctx);
  193 +MODBUS_API void modbus_close(modbus_t *ctx);
  194 +
  195 +MODBUS_API void modbus_free(modbus_t *ctx);
  196 +
  197 +MODBUS_API int modbus_flush(modbus_t *ctx);
  198 +MODBUS_API int modbus_set_debug(modbus_t *ctx, int flag);
  199 +
  200 +MODBUS_API const char *modbus_strerror(int errnum);
  201 +
  202 +MODBUS_API int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
  203 +MODBUS_API int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
  204 +MODBUS_API int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
  205 +MODBUS_API int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
  206 +MODBUS_API int modbus_write_bit(modbus_t *ctx, int coil_addr, int status);
  207 +MODBUS_API int modbus_write_register(modbus_t *ctx, int reg_addr, int value);
  208 +MODBUS_API int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data);
  209 +MODBUS_API int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data);
  210 +MODBUS_API int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask);
  211 +MODBUS_API int modbus_write_and_read_registers(modbus_t *ctx, int write_addr, int write_nb,
  212 + const uint16_t *src, int read_addr, int read_nb,
  213 + uint16_t *dest);
  214 +MODBUS_API int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest);
  215 +
  216 +MODBUS_API modbus_mapping_t* modbus_mapping_new_start_address(
  217 + unsigned int start_bits, unsigned int nb_bits,
  218 + unsigned int start_input_bits, unsigned int nb_input_bits,
  219 + unsigned int start_registers, unsigned int nb_registers,
  220 + unsigned int start_input_registers, unsigned int nb_input_registers);
  221 +
  222 +MODBUS_API modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits,
  223 + int nb_registers, int nb_input_registers);
  224 +MODBUS_API void modbus_mapping_free(modbus_mapping_t *mb_mapping);
  225 +
  226 +MODBUS_API int modbus_send_raw_request(modbus_t *ctx, uint8_t *raw_req, int raw_req_length);
  227 +
  228 +MODBUS_API int modbus_receive(modbus_t *ctx, uint8_t *req);
  229 +
  230 +MODBUS_API int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp);
  231 +
  232 +MODBUS_API int modbus_reply(modbus_t *ctx, const uint8_t *req,
  233 + int req_length, modbus_mapping_t *mb_mapping);
  234 +MODBUS_API int modbus_reply_exception(modbus_t *ctx, const uint8_t *req,
  235 + unsigned int exception_code);
  236 +
  237 +/**
  238 + * UTILS FUNCTIONS
  239 + **/
  240 +
  241 +#define MODBUS_GET_HIGH_BYTE(data) (((data) >> 8) & 0xFF)
  242 +#define MODBUS_GET_LOW_BYTE(data) ((data) & 0xFF)
  243 +#define MODBUS_GET_INT64_FROM_INT16(tab_int16, index) \
  244 + (((int64_t)tab_int16[(index) ] << 48) + \
  245 + ((int64_t)tab_int16[(index) + 1] << 32) + \
  246 + ((int64_t)tab_int16[(index) + 2] << 16) + \
  247 + (int64_t)tab_int16[(index) + 3])
  248 +#define MODBUS_GET_INT32_FROM_INT16(tab_int16, index) ((tab_int16[(index)] << 16) + tab_int16[(index) + 1])
  249 +#define MODBUS_GET_INT16_FROM_INT8(tab_int8, index) ((tab_int8[(index)] << 8) + tab_int8[(index) + 1])
  250 +#define MODBUS_SET_INT16_TO_INT8(tab_int8, index, value) \
  251 + do { \
  252 + tab_int8[(index)] = (value) >> 8; \
  253 + tab_int8[(index) + 1] = (value) & 0xFF; \
  254 + } while (0)
  255 +#define MODBUS_SET_INT32_TO_INT16(tab_int16, index, value) \
  256 + do { \
  257 + tab_int16[(index) ] = (value) >> 16; \
  258 + tab_int16[(index) + 1] = (value); \
  259 + } while (0)
  260 +#define MODBUS_SET_INT64_TO_INT16(tab_int16, index, value) \
  261 + do { \
  262 + tab_int16[(index) ] = (value) >> 48; \
  263 + tab_int16[(index) + 1] = (value) >> 32; \
  264 + tab_int16[(index) + 2] = (value) >> 16; \
  265 + tab_int16[(index) + 3] = (value); \
  266 + } while (0)
  267 +
  268 +MODBUS_API void modbus_set_bits_from_byte(uint8_t *dest, int idx, const uint8_t value);
  269 +MODBUS_API void modbus_set_bits_from_bytes(uint8_t *dest, int idx, unsigned int nb_bits,
  270 + const uint8_t *tab_byte);
  271 +MODBUS_API uint8_t modbus_get_byte_from_bits(const uint8_t *src, int idx, unsigned int nb_bits);
  272 +MODBUS_API float modbus_get_float(const uint16_t *src);
  273 +MODBUS_API float modbus_get_float_abcd(const uint16_t *src);
  274 +MODBUS_API float modbus_get_float_dcba(const uint16_t *src);
  275 +MODBUS_API float modbus_get_float_badc(const uint16_t *src);
  276 +MODBUS_API float modbus_get_float_cdab(const uint16_t *src);
  277 +
  278 +MODBUS_API void modbus_set_float(float f, uint16_t *dest);
  279 +MODBUS_API void modbus_set_float_abcd(float f, uint16_t *dest);
  280 +MODBUS_API void modbus_set_float_dcba(float f, uint16_t *dest);
  281 +MODBUS_API void modbus_set_float_badc(float f, uint16_t *dest);
  282 +MODBUS_API void modbus_set_float_cdab(float f, uint16_t *dest);
  283 +
  284 +#include "modbus-tcp.h"
  285 +#include "modbus-rtu.h"
  286 +
  287 +MODBUS_END_DECLS
  288 +
  289 +#endif /* MODBUS_H */
... ...
CMakeLists.txt 0 โ†’ 100644
  1 +cmake_minimum_required(VERSION 3.2)
  2 +project(modbus)
  3 +
  4 +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
  5 +
  6 +aux_source_directory(3rdparty/libmodbus DIRSRCS)
  7 +include_directories(3rdparty/libmodbus include)
  8 +
  9 +set(SOURCE_FILES tests/main.cpp src/modbusadapter.cpp)
  10 +add_executable(main ${SOURCE_FILES} ${DIRSRCS})
... ...
scripts/setup_submodules 0 โ†’ 100755
  1 +#!/bin/bash
  2 +
  3 +# ===============================================
  4 +# == Setting some environment variables
  5 +# ===============================================
  6 +GIT_URL_OPEN="http://gitlab.osdev.nl/open_source"
  7 +GIT_URL_CLOSED="git@gitlab.osdev.nl:closed_source"
  8 +
  9 +FUNC_RESULT="-1"
  10 +
  11 +# Name : print_usage_exit()
  12 +# Description : Print the way this script is intended to be used and exit.
  13 +# Parameters : None.
  14 +# Returns : err_code 1 to the Operating System
  15 +# --------------------------------------------------------------------------------------
  16 +function print_usage_exit()
  17 +{
  18 + echo "Usage $0 -i|--install|-u|--update"
  19 + echo " -i or --install Install the submodules mentioned in the submodules.list"
  20 + echo " -u or --update Update the submodules mentioned in the submodules.list"
  21 + echo " "
  22 + exit 1
  23 +}
  24 +
  25 +# Name : check_top_or_sub
  26 +# Description : Determine if we're running in a "single" lib-build or part of a
  27 +# "meta"-repository ( submodule ).
  28 +# Parameters : None
  29 +# Returns : Updates the value FUNC_RESULT.
  30 +# -1 - We're neither a git-repo or submodule.
  31 +# 0 - We're a submodule
  32 +# 1 - We're a top-repo ( Single library )
  33 +# --------------------------------------------------------------------------------------
  34 +function check_top_or_sub()
  35 +{
  36 + # This function checks if we're the top-repository.
  37 + # In that case we need the submodules.. If we're already a submodule,
  38 + # we simply exit this script with a message
  39 + if [ -e ./.git ]; then
  40 + FUNC_RESULT="1"
  41 + return
  42 + elif [ -e ../.git ]; then
  43 + if [ -e ../.submodules ]; then
  44 + echo "Seems like we're already a submodule. Nothing to do here."
  45 + FUNC_RESULT="0"
  46 + return
  47 + fi
  48 + fi
  49 + FUNC_RESULT="-1"
  50 + return
  51 +}
  52 +
  53 +# Name : check_working_dir
  54 +# Description : If we're in the top of our repo, we can run this script further.
  55 +# Parameters : None.
  56 +# Returns : Updates the value FUNC_RESULT.
  57 +# -1 - Not used.
  58 +# 0 - We're not on the top-level
  59 +# 1 - We're at the top-level. Good to go.
  60 +# --------------------------------------------------------------------------------------
  61 +function check_working_dir()
  62 +{
  63 + FUNC_RESULT="-1"
  64 + # Check if we're in the top-level directory of our repository.
  65 + if [ -f ./scripts/submodules.list ]; then
  66 + # We're good to go
  67 + FUNC_RESULT="1"
  68 + return
  69 + fi
  70 + FUNC_RESULT="0"
  71 + return
  72 +}
  73 +
  74 +# Name : read_submodules
  75 +# Description : Read the list of submodules needed for this project
  76 +# Parameters : None
  77 +# Returns : Updates the value FUNC_RESULT
  78 +# 0 - Module list was not found
  79 +# 1 - Module list was found and read.
  80 +# --------------------------------------------------------------------------------------
  81 +function read_submodules()
  82 +{
  83 + FUNC_RESULT="-1"
  84 + if [ -e ./scripts/submodules.list ]; then
  85 + source ./scripts/submodules.list
  86 + FUNC_RESULT="1"
  87 + return
  88 + fi
  89 +
  90 + echo "Submodules list not found...."
  91 + FUNC_RESULT="0"
  92 + return
  93 +}
  94 +
  95 +# Name : add_submodules
  96 +# Description : Configure the repo to add the submodules.
  97 +# Parameters : None.
  98 +# Returns : None.
  99 +# --------------------------------------------------------------------------------------
  100 +function add_submodules()
  101 +{
  102 + echo -e "Adding SubModule(s)."
  103 + for SUB_MODULE in ${SUB_MODULES_OPEN}
  104 + do
  105 + git submodule add -f ${GIT_URL_OPEN}/${SUB_MODULE}.git submodules/${SUB_MODULE}
  106 + git config submodule.${SUB_MODULE}.url ${GIT_URL_OPEN}/${SUB_MODULE}.git
  107 + done
  108 +
  109 + for SUB_MODULE in ${SUB_MODULES_CLOSED}
  110 + do
  111 + echo {GIT_URL_CLOSED}/${SUB_MODULE}.git
  112 + git submodule add -f ${GIT_URL_CLOSED}/${SUB_MODULE}.git submodules/${SUB_MODULE}
  113 + git config submodule.${SUB_MODULE}.url ${GIT_URL_CLOSED}/${SUB_MODULE}.git
  114 + done
  115 +
  116 +}
  117 +
  118 +# Name : get_submodules
  119 +# Description : Actually get the submodules from gitlab and add them.
  120 +# Parameters : None
  121 +# Returns : None
  122 +# --------------------------------------------------------------------------------------
  123 +function get_submodules()
  124 +{
  125 + git submodule update --init --recursive
  126 +}
  127 +
  128 +# Name : update_submodules
  129 +# Description : Update the submodules already added.
  130 +# Parameters : None
  131 +# Returns : None
  132 +# --------------------------------------------------------------------------------------
  133 +function update_submodules()
  134 +{
  135 + git submodule update --recursive
  136 +}
  137 +
  138 +# =============================================================================
  139 +# == T H E M A I N E N T R Y O F T H I S S C R I P T ==
  140 +# =============================================================================
  141 +check_top_or_sub
  142 +if [ "${FUNC_RESULT}" == "0" ]; then
  143 + echo "Seems like we're a submodule already or not part of a repository."
  144 + exit 0
  145 +fi
  146 +
  147 +check_working_dir
  148 +if [ "${FUNC_RESULT}" == "0" ]; then
  149 + echo "Go to the top of this repository and type : scripts/setup_submodules [-i|--install]"
  150 + exit 0
  151 +fi
  152 +
  153 +read_submodules
  154 +
  155 +case "$1" in
  156 + -i*|--install*)
  157 + echo "Installing submodules for this repository ( ${PWD} )"
  158 + add_submodules
  159 + get_submodules
  160 + ;;
  161 + -u*|--update*)
  162 + echo "Update submodules : ${SUB_MODULES}"
  163 + update_submodules
  164 + ;;
  165 + *)
  166 + echo "No parameters found..."
  167 + print_usage_exit
  168 + ;;
  169 +esac
  170 +
... ...
scripts/submodules.list 0 โ†’ 100644
  1 +SUB_MODULES_OPEN="cmake"
  2 +
  3 +SUB_MODULES_CLOSED=""
... ...
src/modbus.cpp deleted
src/modbus.h deleted
1   -#pragma once
2   -
3   -
src/modbus_defines.h deleted
1   -/****************************************************************************
2   - * Copyright (c) 2022 Priva B.V.
3   - ****************************************************************************/
4   -
5   -#pragma once
6   -
7   -/* Modbus Function codes as defined in the protocol standard */
8   -enum class E_MB_FUNCTION_CODES
9   -{
10   - E_MODBUS_FC_READ_COILS 0x01,
11   - E_MODBUS_FC_READ_DISCRETE_INPUTS 0x02,
12   - E_MODBUS_FC_READ_HOLDING_REGISTERS 0x03,
13   - E_MODBUS_FC_READ_INPUT_REGISTERS 0x04,
14   - E_MODBUS_FC_WRITE_SINGLE_COIL 0x05,
15   - E_MODBUS_FC_WRITE_SINGLE_REGISTER 0x06,
16   - E_MODBUS_FC_READ_EXCEPTION_STATUS 0x07,
17   - E_MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F,
18   - E_MODBUS_FC_WRITE_MULTIPLE_REGISTERS 0x10,
19   - E_MODBUS_FC_REPORT_SLAVE_ID 0x11,
20   - E_MODBUS_FC_MASK_WRITE_REGISTERS 0x16,
21   - E_MODBUS_FC_WRITE_AND_READ_REGISTERS 0x17
22   -};
23   -
24   -#define MODBUS_BROADCAST_ADDRESS 0
25   -
26   -/* Modbus Application Protocol V1.1b ( Chapter 6 section 1 page 12 )
27   - * Quantity of Coils to read ( 2 bytes ): 1 to 2000 ( 0x7D0 )
28   - * ( chapter 6 section 11 page 29 )
29   - * Quantity of Coils to write ( 2 bytes ): 1 to 1968 ( 0x7B0 )
30   - */
31   -#define MODBUS_MAX_READ_BITS 2000
32   -#define MODBUS_MAX_WRITE_BITS 1968
33   -
34   -/* Modbus Application Protocol V1.1b ( Chapter 6 section 3 page 12 )
35   - * Quantity of Registers to read ( 2 bytes ): 1 to 125 ( 0x7D )
36   - * ( chapter 6 section 12 page 31 )
37   - * Quantity of Registers to write ( 2 bytes ): 1 to 123 ( 0x7B )
38   - * ( chapter 6 section 17 page 38 )
39   - * Quantity of Registers to write in R/W registers ( 2 bytes ) 1 to 123 ( 0x79 )
40   - */
41   -#define MODBUS_MAX_READ_REGISTERS 125
42   -#define MODBUS_MAX_WRITE_REGISTERS 123
43   -#define MODBUS_MAX_WR_WRITE_REGISTERS 121
44   -#define MODBUS_MAX_WR_READ_REGISTERS 125
45   -
46   -/* The MODBUS PDU is 256 bytes maximum. Therefore MODBUS PDU for serial line
47   - * communication = 256 - Server address ( 1 byte ) - CRC ( 2 bytes ) = 253 bytes
48   - */
49   -#define MODBUS_MAX_PDU_LENGTH 253
50   -
51   -/* Consequently :
52   - * - RTU MODBUS_ADU = 253 bytes + Server address ( 1 byte ) + CRC ( 2 bytes ) = 256
53   - * bytes
54   - * - TCP MODBUS ADU = 253 bytes + MBAP ( 7 bytes ) = 260 bytes
55   - * so the maximum of both backends is 260 bytes. This size can be used to allocate
56   - * an array of bytes to store responses and it will be compatible with the two
57   - * backends.
58   - */
59   -#define MODBUS_MAX_ADU_LENGTH 260
60   -
61   -/* Random number to avoid errno conflicts */
62   -#define MODBUS_ENOBASE 31428571
63   -
64   -/* Protocol exceptions */
65   -enum class E_MODBUS_EXCEPTIONS
66   -{
67   - MODBUS_EXCEPTION_ILLEGAL_FUNCTION 0x01,
68   - MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS,
69   - MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE,
70   - MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE,
71   - MODBUS_EXCEPTION_ACKNOWLEDGE,
72   - MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY,
73   - MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE,
74   - MODBUS_EXCEPTION_MEMORY_PARITY,
75   - MODBUS_EXCEPTION_NOT_DEFINED,
76   - MODBUS_EXCEPTION_GATEWAY_PATH,
77   - MODBUS_EXCEPTION_GATEWAY_TARGET,
78   - MODBUS_EXCEPTION_MAX
79   -};
80   -
81   -/* MODBUS Error codes */
82   -#define EMBXILFUN ( MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_FUNCTION )
83   -#define EMBXILADD ( MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS )
84   -#define EMBXILVAL ( MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE )
85   -#define EMBXSFAIL ( MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE )
86   -#define EMBXACK ( MODBUS_ENOBASE + MODBUS_EXCEPTION_ACKNOWLEDGE )
87   -#define EMBXSBUSY ( MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY )
88   -#define EMBXNACK ( MODBUS_ENOBASE + MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE )
89   -#define EMBXMEMPAR ( MODBUS_ENOBASE + MODBUS_EXCEPTION_MEMORY_PARITY )
90   -#define EMBXGPATH ( MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_PATH )
91   -#define EMBXGTAR ( MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_TARGET )
92   -
93   -/* Native modbus error codes */
94   -#define EMBBADCRC ( EMBXGTAR + 1 )
95   -#define EMBBADDATA ( EMBXGTAR + 2 )
96   -#define EMBBADEXC ( EMBXGTAR + 3 )
97   -#define EMBUNKEXC ( EMBXGTAR + 4 )
98   -#define EMBMDATA ( EMBXGTAR + 5 )
99   -#define EMBBADSLAVE ( EMBXGTAR + 6 )
100   -
101   -/* Recovery Modes */
102   -enum class MODBUS_ERROR_RECOVERY_MODE
103   -{
104   - MODBUS_ERROR_RECOVERY_NONE = 0,
105   - MODBUS_ERROR_RECOVERY_LINK = ( 1 << 1 ),
106   - MODBUS_ERROR_RECOVERY_PROTOCOL = ( 1 << 2 )
107   -};
tests/main.cpp 0 โ†’ 100644
  1 +#include <iostream>
  2 +
  3 +int main( int argc, char* argv[] )
  4 +{
  5 +
  6 +}
... ...