Commit 815d11f0568fdf7a9032051cbb84b24b8b7f4345
1 parent
dfff6b35
MAJOR Split RTU and TCP code in two backends
All private functions and constants are now prefixed by _. The modbus protocol uses entry points to call specific functions.
Showing
15 changed files
with
1381 additions
and
1125 deletions
src/Makefile.am
| @@ -2,12 +2,20 @@ lib_LTLIBRARIES = libmodbus.la | @@ -2,12 +2,20 @@ lib_LTLIBRARIES = libmodbus.la | ||
| 2 | libmodbus_la_SOURCES = \ | 2 | libmodbus_la_SOURCES = \ |
| 3 | modbus.c \ | 3 | modbus.c \ |
| 4 | modbus.h \ | 4 | modbus.h \ |
| 5 | + modbus-data.c \ | ||
| 6 | + modbus-private.h \ | ||
| 7 | + modbus-rtu.c \ | ||
| 8 | + modbus-rtu.h \ | ||
| 9 | + modbus-rtu-private.h \ | ||
| 10 | + modbus-tcp.c \ | ||
| 11 | + modbus-tcp.h \ | ||
| 12 | + modbus-tcp-private.h \ | ||
| 5 | modbus-version.h | 13 | modbus-version.h |
| 6 | libmodbus_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBMODBUS_LT_VERSION_INFO) | 14 | libmodbus_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBMODBUS_LT_VERSION_INFO) |
| 7 | 15 | ||
| 8 | # Header files to install | 16 | # Header files to install |
| 9 | libmodbusincludedir = $(includedir)/modbus | 17 | libmodbusincludedir = $(includedir)/modbus |
| 10 | -libmodbusinclude_HEADERS = modbus.h modbus-version.h | 18 | +libmodbusinclude_HEADERS = modbus.h modbus-version.h modbus-rtu.h modbus-tcp.h |
| 11 | 19 | ||
| 12 | DISTCLEANFILES = modbus-version.h | 20 | DISTCLEANFILES = modbus-version.h |
| 13 | EXTRA_DIST = modbus-version.h.in | 21 | EXTRA_DIST = modbus-version.h.in |
src/modbus-data.c
0 → 100644
| 1 | +/* | ||
| 2 | + * Copyright © 2010 Stéphane Raimbault <stephane.raimbault@gmail.com> | ||
| 3 | + * | ||
| 4 | + * This program is free software: you can redistribute it and/or modify | ||
| 5 | + * it under the terms of the GNU Lesser Public License as published by | ||
| 6 | + * the Free Software Foundation; either version 3 of the License, or | ||
| 7 | + * (at your option) any later version. | ||
| 8 | + * | ||
| 9 | + * This program 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 | ||
| 12 | + * GNU Lesser Public License for more details. | ||
| 13 | + * | ||
| 14 | + * You should have received a copy of the GNU Lesser Public License | ||
| 15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 16 | + */ | ||
| 17 | + | ||
| 18 | +#include <stdlib.h> | ||
| 19 | +#include <stdint.h> | ||
| 20 | +#include <string.h> | ||
| 21 | +#include <assert.h> | ||
| 22 | + | ||
| 23 | +/* Sets many bits from a single byte value (all 8 bits of the byte value are | ||
| 24 | + set) */ | ||
| 25 | +void modbus_set_bits_from_byte(uint8_t *dest, int address, const uint8_t value) | ||
| 26 | +{ | ||
| 27 | + int i; | ||
| 28 | + | ||
| 29 | + for (i=0; i<8; i++) { | ||
| 30 | + dest[address+i] = (value & (1 << i)) ? 1 : 0; | ||
| 31 | + } | ||
| 32 | +} | ||
| 33 | + | ||
| 34 | +/* Sets many bits from a table of bytes (only the bits between address and | ||
| 35 | + address + nb_bits are set) */ | ||
| 36 | +void modbus_set_bits_from_bytes(uint8_t *dest, int address, unsigned int nb_bits, | ||
| 37 | + const uint8_t *tab_byte) | ||
| 38 | +{ | ||
| 39 | + int i; | ||
| 40 | + int shift = 0; | ||
| 41 | + | ||
| 42 | + for (i = address; i < address + nb_bits; i++) { | ||
| 43 | + dest[i] = tab_byte[(i - address) / 8] & (1 << shift) ? 1 : 0; | ||
| 44 | + /* gcc doesn't like: shift = (++shift) % 8; */ | ||
| 45 | + shift++; | ||
| 46 | + shift %= 8; | ||
| 47 | + } | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +/* Gets the byte value from many bits. | ||
| 51 | + To obtain a full byte, set nb_bits to 8. */ | ||
| 52 | +uint8_t modbus_get_byte_from_bits(const uint8_t *src, int address, | ||
| 53 | + unsigned int nb_bits) | ||
| 54 | +{ | ||
| 55 | + int i; | ||
| 56 | + uint8_t value = 0; | ||
| 57 | + | ||
| 58 | + if (nb_bits > 8) { | ||
| 59 | + assert(nb_bits < 8); | ||
| 60 | + nb_bits = 8; | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + for (i=0; i < nb_bits; i++) { | ||
| 64 | + value |= (src[address+i] << i); | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + return value; | ||
| 68 | +} | ||
| 69 | + | ||
| 70 | +/* Get a float from 4 bytes in Modbus format */ | ||
| 71 | +float modbus_get_float(const uint16_t *src) | ||
| 72 | +{ | ||
| 73 | + float r = 0.0f; | ||
| 74 | + uint32_t i; | ||
| 75 | + | ||
| 76 | + i = (((uint32_t)src[1]) << 16) + src[0]; | ||
| 77 | + memcpy(&r, &i, sizeof (r)); | ||
| 78 | + return r; | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | +/* Set a float to 4 bytes in Modbus format */ | ||
| 82 | +void modbus_set_float(float real, uint16_t *dest) | ||
| 83 | +{ | ||
| 84 | + uint32_t i = 0; | ||
| 85 | + | ||
| 86 | + memcpy(&i, &real, sizeof (i)); | ||
| 87 | + dest[0] = (uint16_t)i; | ||
| 88 | + dest[1] = (uint16_t)(i >> 16); | ||
| 89 | +} |
src/modbus-private.h
| @@ -22,17 +22,6 @@ | @@ -22,17 +22,6 @@ | ||
| 22 | 22 | ||
| 23 | MODBUS_BEGIN_DECLS | 23 | MODBUS_BEGIN_DECLS |
| 24 | 24 | ||
| 25 | -#define HEADER_LENGTH_RTU 1 | ||
| 26 | -#define PRESET_REQ_LENGTH_RTU 6 | ||
| 27 | -#define PRESET_RSP_LENGTH_RTU 2 | ||
| 28 | - | ||
| 29 | -#define HEADER_LENGTH_TCP 7 | ||
| 30 | -#define PRESET_REQ_LENGTH_TCP 12 | ||
| 31 | -#define PRESET_RSP_LENGTH_TCP 8 | ||
| 32 | - | ||
| 33 | -#define CHECKSUM_LENGTH_RTU 2 | ||
| 34 | -#define CHECKSUM_LENGTH_TCP 0 | ||
| 35 | - | ||
| 36 | /* It's not really the minimal length (the real one is report slave ID | 25 | /* It's not really the minimal length (the real one is report slave ID |
| 37 | * in RTU (4 bytes)) but it's a convenient size to use in RTU or TCP | 26 | * in RTU (4 bytes)) but it's a convenient size to use in RTU or TCP |
| 38 | * communications to read many values or write a single one. | 27 | * communications to read many values or write a single one. |
| @@ -40,34 +29,66 @@ MODBUS_BEGIN_DECLS | @@ -40,34 +29,66 @@ MODBUS_BEGIN_DECLS | ||
| 40 | * - HEADER_LENGTH_TCP (7) + function (1) + address (2) + number (2) | 29 | * - HEADER_LENGTH_TCP (7) + function (1) + address (2) + number (2) |
| 41 | * - HEADER_LENGTH_RTU (1) + function (1) + address (2) + number (2) + CRC (2) | 30 | * - HEADER_LENGTH_RTU (1) + function (1) + address (2) + number (2) + CRC (2) |
| 42 | */ | 31 | */ |
| 43 | -#define MIN_REQ_LENGTH 12 | 32 | +#define _MIN_REQ_LENGTH 12 |
| 44 | 33 | ||
| 45 | -#define EXCEPTION_RSP_LENGTH_RTU 5 | 34 | +#define _REPORT_SLAVE_ID_LENGTH 75 |
| 46 | 35 | ||
| 47 | -#define REPORT_SLAVE_ID_LENGTH 75 | 36 | +#define _MODBUS_EXCEPTION_RSP_LENGTH 5 |
| 48 | 37 | ||
| 49 | /* Time out between trames in microsecond */ | 38 | /* Time out between trames in microsecond */ |
| 50 | -#define TIME_OUT_BEGIN_OF_TRAME 500000 | ||
| 51 | -#define TIME_OUT_END_OF_TRAME 500000 | 39 | +#define _TIME_OUT_BEGIN_OF_TRAME 500000 |
| 40 | +#define _TIME_OUT_END_OF_TRAME 500000 | ||
| 52 | 41 | ||
| 53 | /* Function codes */ | 42 | /* Function codes */ |
| 54 | -#define FC_READ_COILS 0x01 | ||
| 55 | -#define FC_READ_DISCRETE_INPUTS 0x02 | ||
| 56 | -#define FC_READ_HOLDING_REGISTERS 0x03 | ||
| 57 | -#define FC_READ_INPUT_REGISTERS 0x04 | ||
| 58 | -#define FC_WRITE_SINGLE_COIL 0x05 | ||
| 59 | -#define FC_WRITE_SINGLE_REGISTER 0x06 | ||
| 60 | -#define FC_READ_EXCEPTION_STATUS 0x07 | ||
| 61 | -#define FC_WRITE_MULTIPLE_COILS 0x0F | ||
| 62 | -#define FC_WRITE_MULTIPLE_REGISTERS 0x10 | ||
| 63 | -#define FC_REPORT_SLAVE_ID 0x11 | ||
| 64 | -#define FC_READ_AND_WRITE_REGISTERS 0x17 | ||
| 65 | - | ||
| 66 | -typedef enum { RTU=0, TCP } type_com_t; | 43 | +#define _FC_READ_COILS 0x01 |
| 44 | +#define _FC_READ_DISCRETE_INPUTS 0x02 | ||
| 45 | +#define _FC_READ_HOLDING_REGISTERS 0x03 | ||
| 46 | +#define _FC_READ_INPUT_REGISTERS 0x04 | ||
| 47 | +#define _FC_WRITE_SINGLE_COIL 0x05 | ||
| 48 | +#define _FC_WRITE_SINGLE_REGISTER 0x06 | ||
| 49 | +#define _FC_READ_EXCEPTION_STATUS 0x07 | ||
| 50 | +#define _FC_WRITE_MULTIPLE_COILS 0x0F | ||
| 51 | +#define _FC_WRITE_MULTIPLE_REGISTERS 0x10 | ||
| 52 | +#define _FC_REPORT_SLAVE_ID 0x11 | ||
| 53 | +#define _FC_READ_AND_WRITE_REGISTERS 0x17 | ||
| 54 | + | ||
| 55 | +typedef enum { | ||
| 56 | + _MODBUS_BACKEND_TYPE_RTU=0, | ||
| 57 | + _MODBUS_BACKEND_TYPE_TCP | ||
| 58 | +} modbus_bakend_type_t; | ||
| 59 | + | ||
| 60 | +/* This structure reduces the number of params in functions and so | ||
| 61 | + * optimizes the speed of execution (~ 37%). */ | ||
| 62 | +typedef struct _sft { | ||
| 63 | + int slave; | ||
| 64 | + int function; | ||
| 65 | + int t_id; | ||
| 66 | +} sft_t; | ||
| 67 | + | ||
| 68 | +typedef struct _modbus_backend { | ||
| 69 | + unsigned int backend_type; | ||
| 70 | + unsigned int header_length; | ||
| 71 | + unsigned int checksum_length; | ||
| 72 | + unsigned int max_adu_length; | ||
| 73 | + int (*set_slave) (modbus_t *ctx, int slave); | ||
| 74 | + int (*build_request_basis) (modbus_t *ctx, int function, int addr, | ||
| 75 | + int nb, uint8_t *req); | ||
| 76 | + int (*build_response_basis) (sft_t *sft, uint8_t *rsp); | ||
| 77 | + int (*prepare_response_tid) (const uint8_t *req, int *req_length); | ||
| 78 | + int (*send_msg_pre) (uint8_t *req, int req_length); | ||
| 79 | + ssize_t (*send) (int s, const uint8_t *req, int req_length); | ||
| 80 | + ssize_t (*recv) (int s, uint8_t *rsp, int rsp_length); | ||
| 81 | + int (*check_integrity) (modbus_t *ctx, uint8_t *msg, | ||
| 82 | + const int msg_length); | ||
| 83 | + int (*connect) (modbus_t *ctx); | ||
| 84 | + void (*close) (modbus_t *ctx); | ||
| 85 | + int (*flush) (modbus_t *ctx); | ||
| 86 | + int (*listen) (modbus_t *ctx, int nb_connection); | ||
| 87 | + int (*accept) (modbus_t *ctx, int *socket); | ||
| 88 | + int (*filter_request) (modbus_t *ctx, int slave); | ||
| 89 | +} modbus_backend_t; | ||
| 67 | 90 | ||
| 68 | struct _modbus { | 91 | struct _modbus { |
| 69 | - /* Communication mode: RTU or TCP */ | ||
| 70 | - type_com_t type_com; | ||
| 71 | /* Slave address */ | 92 | /* Slave address */ |
| 72 | int slave; | 93 | int slave; |
| 73 | /* Socket or file descriptor */ | 94 | /* Socket or file descriptor */ |
| @@ -76,38 +97,11 @@ struct _modbus { | @@ -76,38 +97,11 @@ struct _modbus { | ||
| 76 | int error_recovery; | 97 | int error_recovery; |
| 77 | struct timeval timeout_begin; | 98 | struct timeval timeout_begin; |
| 78 | struct timeval timeout_end; | 99 | struct timeval timeout_end; |
| 79 | - void *com; | 100 | + const modbus_backend_t *backend; |
| 101 | + void *backend_data; | ||
| 80 | }; | 102 | }; |
| 81 | 103 | ||
| 82 | -typedef struct _modbus_rtu { | ||
| 83 | - /* Device: "/dev/ttyS0", "/dev/ttyUSB0" or "/dev/tty.USA19*" on Mac OS X for | ||
| 84 | - KeySpan USB<->Serial adapters this string had to be made bigger on OS X | ||
| 85 | - as the directory+file name was bigger than 19 bytes. Making it 67 bytes | ||
| 86 | - for now, but OS X does support 256 byte file names. May become a problem | ||
| 87 | - in the future. */ | ||
| 88 | -#ifdef __APPLE_CC__ | ||
| 89 | - char device[64]; | ||
| 90 | -#else | ||
| 91 | - char device[16]; | ||
| 92 | -#endif | ||
| 93 | - /* Bauds: 9600, 19200, 57600, 115200, etc */ | ||
| 94 | - int baud; | ||
| 95 | - /* Data bit */ | ||
| 96 | - uint8_t data_bit; | ||
| 97 | - /* Stop bit */ | ||
| 98 | - uint8_t stop_bit; | ||
| 99 | - /* Parity: 'N', 'O', 'E' */ | ||
| 100 | - char parity; | ||
| 101 | - /* Save old termios settings */ | ||
| 102 | - struct termios old_tios; | ||
| 103 | -} modbus_rtu_t; | ||
| 104 | - | ||
| 105 | -typedef struct _modbus_tcp { | ||
| 106 | - /* TCP port */ | ||
| 107 | - int port; | ||
| 108 | - /* IP address */ | ||
| 109 | - char ip[16]; | ||
| 110 | -} modbus_tcp_t; | 104 | +void _modbus_init_common(modbus_t *ctx); |
| 111 | 105 | ||
| 112 | MODBUS_END_DECLS | 106 | MODBUS_END_DECLS |
| 113 | 107 |
src/modbus-rtu-private.h
0 → 100644
| 1 | +/* | ||
| 2 | + * Copyright © 2001-2010 Stéphane Raimbault <stephane.raimbault@gmail.com> | ||
| 3 | + * | ||
| 4 | + * This program is free software: you can redistribute it and/or modify | ||
| 5 | + * it under the terms of the GNU Lesser Public License as published by | ||
| 6 | + * the Free Software Foundation; either version 3 of the License, or | ||
| 7 | + * (at your option) any later version. | ||
| 8 | + * | ||
| 9 | + * This program 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 | ||
| 12 | + * GNU Lesser Public License for more details. | ||
| 13 | + * | ||
| 14 | + * You should have received a copy of the GNU Lesser Public License | ||
| 15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 16 | + */ | ||
| 17 | + | ||
| 18 | +#ifndef _MODBUS_RTU_PRIVATE_H_ | ||
| 19 | +#define _MODBUS_RTU_PRIVATE_H_ | ||
| 20 | + | ||
| 21 | +#define _MODBUS_RTU_HEADER_LENGTH 1 | ||
| 22 | +#define _MODBUS_RTU_PRESET_REQ_LENGTH 6 | ||
| 23 | +#define _MODBUS_RTU_PRESET_RSP_LENGTH 2 | ||
| 24 | + | ||
| 25 | +#define _MODBUS_RTU_CHECKSUM_LENGTH 2 | ||
| 26 | + | ||
| 27 | +typedef struct _modbus_rtu { | ||
| 28 | + /* Device: "/dev/ttyS0", "/dev/ttyUSB0" or "/dev/tty.USA19*" on Mac OS X for | ||
| 29 | + KeySpan USB<->Serial adapters this string had to be made bigger on OS X | ||
| 30 | + as the directory+file name was bigger than 19 bytes. Making it 67 bytes | ||
| 31 | + for now, but OS X does support 256 byte file names. May become a problem | ||
| 32 | + in the future. */ | ||
| 33 | +#ifdef __APPLE_CC__ | ||
| 34 | + char device[64]; | ||
| 35 | +#else | ||
| 36 | + char device[16]; | ||
| 37 | +#endif | ||
| 38 | + /* Bauds: 9600, 19200, 57600, 115200, etc */ | ||
| 39 | + int baud; | ||
| 40 | + /* Data bit */ | ||
| 41 | + uint8_t data_bit; | ||
| 42 | + /* Stop bit */ | ||
| 43 | + uint8_t stop_bit; | ||
| 44 | + /* Parity: 'N', 'O', 'E' */ | ||
| 45 | + char parity; | ||
| 46 | + /* Save old termios settings */ | ||
| 47 | + struct termios old_tios; | ||
| 48 | +} modbus_rtu_t; | ||
| 49 | + | ||
| 50 | +#endif /* _MODBUS_RTU_PRIVATE_H_ */ |
src/modbus-rtu.c
0 → 100644
| 1 | +/* | ||
| 2 | + * Copyright © 2001-2010 Stéphane Raimbault <stephane.raimbault@gmail.com> | ||
| 3 | + * | ||
| 4 | + * This program is free software: you can redistribute it and/or modify | ||
| 5 | + * it under the terms of the GNU Lesser Public License as published by | ||
| 6 | + * the Free Software Foundation; either version 3 of the License, or | ||
| 7 | + * (at your option) any later version. | ||
| 8 | + * | ||
| 9 | + * This program 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 | ||
| 12 | + * GNU Lesser Public License for more details. | ||
| 13 | + * | ||
| 14 | + * You should have received a copy of the GNU Lesser Public License | ||
| 15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 16 | + */ | ||
| 17 | + | ||
| 18 | +#include <stdio.h> | ||
| 19 | +#include <stdlib.h> | ||
| 20 | +#include <errno.h> | ||
| 21 | +#include <termios.h> | ||
| 22 | +#include <fcntl.h> | ||
| 23 | +#include <string.h> | ||
| 24 | + | ||
| 25 | +#include "modbus.h" | ||
| 26 | +#include "modbus-private.h" | ||
| 27 | + | ||
| 28 | +#include "modbus-rtu.h" | ||
| 29 | +#include "modbus-rtu-private.h" | ||
| 30 | + | ||
| 31 | +/* Table of CRC values for high-order byte */ | ||
| 32 | +static const uint8_t table_crc_hi[] = { | ||
| 33 | + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, | ||
| 34 | + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, | ||
| 35 | + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, | ||
| 36 | + 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, | ||
| 37 | + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, | ||
| 38 | + 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, | ||
| 39 | + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, | ||
| 40 | + 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, | ||
| 41 | + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, | ||
| 42 | + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, | ||
| 43 | + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, | ||
| 44 | + 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, | ||
| 45 | + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, | ||
| 46 | + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, | ||
| 47 | + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, | ||
| 48 | + 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, | ||
| 49 | + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, | ||
| 50 | + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, | ||
| 51 | + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, | ||
| 52 | + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, | ||
| 53 | + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, | ||
| 54 | + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, | ||
| 55 | + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, | ||
| 56 | + 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, | ||
| 57 | + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, | ||
| 58 | + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | ||
| 59 | +}; | ||
| 60 | + | ||
| 61 | +/* Table of CRC values for low-order byte */ | ||
| 62 | +static const uint8_t table_crc_lo[] = { | ||
| 63 | + 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, | ||
| 64 | + 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, | ||
| 65 | + 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, | ||
| 66 | + 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, | ||
| 67 | + 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, | ||
| 68 | + 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, | ||
| 69 | + 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, | ||
| 70 | + 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, | ||
| 71 | + 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, | ||
| 72 | + 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, | ||
| 73 | + 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, | ||
| 74 | + 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, | ||
| 75 | + 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, | ||
| 76 | + 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, | ||
| 77 | + 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, | ||
| 78 | + 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, | ||
| 79 | + 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, | ||
| 80 | + 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, | ||
| 81 | + 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, | ||
| 82 | + 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, | ||
| 83 | + 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, | ||
| 84 | + 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, | ||
| 85 | + 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, | ||
| 86 | + 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, | ||
| 87 | + 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, | ||
| 88 | + 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 | ||
| 89 | +}; | ||
| 90 | + | ||
| 91 | +static int _modbus_set_slave(modbus_t *ctx, int slave) | ||
| 92 | +{ | ||
| 93 | + if (slave >= 1 && slave <= 247) { | ||
| 94 | + ctx->slave = slave; | ||
| 95 | + } else { | ||
| 96 | + errno = EINVAL; | ||
| 97 | + return -1; | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + return 0; | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +/* Builds a RTU request header */ | ||
| 104 | +static int _modbus_rtu_build_request_basis(modbus_t *ctx, int function, | ||
| 105 | + int addr, int nb, | ||
| 106 | + uint8_t *req) | ||
| 107 | +{ | ||
| 108 | + req[0] = ctx->slave; | ||
| 109 | + req[1] = function; | ||
| 110 | + req[2] = addr >> 8; | ||
| 111 | + req[3] = addr & 0x00ff; | ||
| 112 | + req[4] = nb >> 8; | ||
| 113 | + req[5] = nb & 0x00ff; | ||
| 114 | + | ||
| 115 | + return _MODBUS_RTU_PRESET_REQ_LENGTH; | ||
| 116 | +} | ||
| 117 | + | ||
| 118 | +/* Builds a RTU response header */ | ||
| 119 | +static int _modbus_rtu_build_response_basis(sft_t *sft, uint8_t *rsp) | ||
| 120 | +{ | ||
| 121 | + rsp[0] = sft->slave; | ||
| 122 | + rsp[1] = sft->function; | ||
| 123 | + | ||
| 124 | + return _MODBUS_RTU_PRESET_RSP_LENGTH; | ||
| 125 | +} | ||
| 126 | + | ||
| 127 | +static uint16_t crc16(uint8_t *buffer, uint16_t buffer_length) | ||
| 128 | +{ | ||
| 129 | + uint8_t crc_hi = 0xFF; /* high CRC byte initialized */ | ||
| 130 | + uint8_t crc_lo = 0xFF; /* low CRC byte initialized */ | ||
| 131 | + unsigned int i; /* will index into CRC lookup */ | ||
| 132 | + | ||
| 133 | + /* pass through message buffer */ | ||
| 134 | + while (buffer_length--) { | ||
| 135 | + i = crc_hi ^ *buffer++; /* calculate the CRC */ | ||
| 136 | + crc_hi = crc_lo ^ table_crc_hi[i]; | ||
| 137 | + crc_lo = table_crc_lo[i]; | ||
| 138 | + } | ||
| 139 | + | ||
| 140 | + return (crc_hi << 8 | crc_lo); | ||
| 141 | +} | ||
| 142 | + | ||
| 143 | +int _modbus_rtu_prepare_response_tid(const uint8_t *req, int *req_length) | ||
| 144 | +{ | ||
| 145 | + (*req_length) -= _MODBUS_RTU_CHECKSUM_LENGTH; | ||
| 146 | + /* No TID */ | ||
| 147 | + return 0; | ||
| 148 | +} | ||
| 149 | + | ||
| 150 | +int _modbus_rtu_send_msg_pre(uint8_t *req, int req_length) | ||
| 151 | +{ | ||
| 152 | + uint16_t crc = crc16(req, req_length); | ||
| 153 | + req[req_length++] = crc >> 8; | ||
| 154 | + req[req_length++] = crc & 0x00FF; | ||
| 155 | + | ||
| 156 | + return req_length; | ||
| 157 | +} | ||
| 158 | + | ||
| 159 | +ssize_t _modbus_rtu_send(int s, const uint8_t *req, int req_length) | ||
| 160 | +{ | ||
| 161 | + return write(s, req, req_length); | ||
| 162 | +} | ||
| 163 | + | ||
| 164 | +ssize_t _modbus_rtu_recv(int s, uint8_t *rsp, int rsp_length) | ||
| 165 | +{ | ||
| 166 | + return read(s, rsp, rsp_length); | ||
| 167 | +} | ||
| 168 | + | ||
| 169 | +/* The check_crc16 function shall return the message length if the CRC is | ||
| 170 | + valid. Otherwise it shall return -1 and set errno to EMBADCRC. */ | ||
| 171 | +int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg, | ||
| 172 | + const int msg_length) | ||
| 173 | +{ | ||
| 174 | + uint16_t crc_calculated; | ||
| 175 | + uint16_t crc_received; | ||
| 176 | + | ||
| 177 | + crc_calculated = crc16(msg, msg_length - 2); | ||
| 178 | + crc_received = (msg[msg_length - 2] << 8) | msg[msg_length - 1]; | ||
| 179 | + | ||
| 180 | + /* Check CRC of msg */ | ||
| 181 | + if (crc_calculated == crc_received) { | ||
| 182 | + return msg_length; | ||
| 183 | + } else { | ||
| 184 | + if (ctx->debug) { | ||
| 185 | + fprintf(stderr, "ERROR CRC received %0X != CRC calculated %0X\n", | ||
| 186 | + crc_received, crc_calculated); | ||
| 187 | + } | ||
| 188 | + if (ctx->error_recovery) { | ||
| 189 | + _modbus_rtu_flush(ctx); | ||
| 190 | + } | ||
| 191 | + errno = EMBBADCRC; | ||
| 192 | + return -1; | ||
| 193 | + } | ||
| 194 | +} | ||
| 195 | + | ||
| 196 | +/* Sets up a serial port for RTU communications */ | ||
| 197 | +static int _modbus_rtu_connect(modbus_t *ctx) | ||
| 198 | +{ | ||
| 199 | + struct termios tios; | ||
| 200 | + speed_t speed; | ||
| 201 | + modbus_rtu_t *ctx_rtu = ctx->backend_data; | ||
| 202 | + | ||
| 203 | + if (ctx->debug) { | ||
| 204 | + printf("Opening %s at %d bauds (%c, %d, %d)\n", | ||
| 205 | + ctx_rtu->device, ctx_rtu->baud, ctx_rtu->parity, | ||
| 206 | + ctx_rtu->data_bit, ctx_rtu->stop_bit); | ||
| 207 | + } | ||
| 208 | + | ||
| 209 | + /* The O_NOCTTY flag tells UNIX that this program doesn't want | ||
| 210 | + to be the "controlling terminal" for that port. If you | ||
| 211 | + don't specify this then any input (such as keyboard abort | ||
| 212 | + signals and so forth) will affect your process | ||
| 213 | + | ||
| 214 | + Timeouts are ignored in canonical input mode or when the | ||
| 215 | + NDELAY option is set on the file via open or fcntl */ | ||
| 216 | + ctx->s = open(ctx_rtu->device, O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL); | ||
| 217 | + if (ctx->s == -1) { | ||
| 218 | + fprintf(stderr, "ERROR Can't open the device %s (%s)\n", | ||
| 219 | + ctx_rtu->device, strerror(errno)); | ||
| 220 | + return -1; | ||
| 221 | + } | ||
| 222 | + | ||
| 223 | + /* Save */ | ||
| 224 | + tcgetattr(ctx->s, &(ctx_rtu->old_tios)); | ||
| 225 | + | ||
| 226 | + memset(&tios, 0, sizeof(struct termios)); | ||
| 227 | + | ||
| 228 | + /* C_ISPEED Input baud (new interface) | ||
| 229 | + C_OSPEED Output baud (new interface) | ||
| 230 | + */ | ||
| 231 | + switch (ctx_rtu->baud) { | ||
| 232 | + case 110: | ||
| 233 | + speed = B110; | ||
| 234 | + break; | ||
| 235 | + case 300: | ||
| 236 | + speed = B300; | ||
| 237 | + break; | ||
| 238 | + case 600: | ||
| 239 | + speed = B600; | ||
| 240 | + break; | ||
| 241 | + case 1200: | ||
| 242 | + speed = B1200; | ||
| 243 | + break; | ||
| 244 | + case 2400: | ||
| 245 | + speed = B2400; | ||
| 246 | + break; | ||
| 247 | + case 4800: | ||
| 248 | + speed = B4800; | ||
| 249 | + break; | ||
| 250 | + case 9600: | ||
| 251 | + speed = B9600; | ||
| 252 | + break; | ||
| 253 | + case 19200: | ||
| 254 | + speed = B19200; | ||
| 255 | + break; | ||
| 256 | + case 38400: | ||
| 257 | + speed = B38400; | ||
| 258 | + break; | ||
| 259 | + case 57600: | ||
| 260 | + speed = B57600; | ||
| 261 | + break; | ||
| 262 | + case 115200: | ||
| 263 | + speed = B115200; | ||
| 264 | + break; | ||
| 265 | + default: | ||
| 266 | + speed = B9600; | ||
| 267 | + if (ctx->debug) { | ||
| 268 | + fprintf(stderr, | ||
| 269 | + "WARNING Unknown baud rate %d for %s (B9600 used)\n", | ||
| 270 | + ctx_rtu->baud, ctx_rtu->device); | ||
| 271 | + } | ||
| 272 | + } | ||
| 273 | + | ||
| 274 | + /* Set the baud rate */ | ||
| 275 | + if ((cfsetispeed(&tios, speed) < 0) || | ||
| 276 | + (cfsetospeed(&tios, speed) < 0)) { | ||
| 277 | + return -1; | ||
| 278 | + } | ||
| 279 | + | ||
| 280 | + /* C_CFLAG Control options | ||
| 281 | + CLOCAL Local line - do not change "owner" of port | ||
| 282 | + CREAD Enable receiver | ||
| 283 | + */ | ||
| 284 | + tios.c_cflag |= (CREAD | CLOCAL); | ||
| 285 | + /* CSIZE, HUPCL, CRTSCTS (hardware flow control) */ | ||
| 286 | + | ||
| 287 | + /* Set data bits (5, 6, 7, 8 bits) | ||
| 288 | + CSIZE Bit mask for data bits | ||
| 289 | + */ | ||
| 290 | + tios.c_cflag &= ~CSIZE; | ||
| 291 | + switch (ctx_rtu->data_bit) { | ||
| 292 | + case 5: | ||
| 293 | + tios.c_cflag |= CS5; | ||
| 294 | + break; | ||
| 295 | + case 6: | ||
| 296 | + tios.c_cflag |= CS6; | ||
| 297 | + break; | ||
| 298 | + case 7: | ||
| 299 | + tios.c_cflag |= CS7; | ||
| 300 | + break; | ||
| 301 | + case 8: | ||
| 302 | + default: | ||
| 303 | + tios.c_cflag |= CS8; | ||
| 304 | + break; | ||
| 305 | + } | ||
| 306 | + | ||
| 307 | + /* Stop bit (1 or 2) */ | ||
| 308 | + if (ctx_rtu->stop_bit == 1) | ||
| 309 | + tios.c_cflag &=~ CSTOPB; | ||
| 310 | + else /* 2 */ | ||
| 311 | + tios.c_cflag |= CSTOPB; | ||
| 312 | + | ||
| 313 | + /* PARENB Enable parity bit | ||
| 314 | + PARODD Use odd parity instead of even */ | ||
| 315 | + if (ctx_rtu->parity == 'N') { | ||
| 316 | + /* None */ | ||
| 317 | + tios.c_cflag &=~ PARENB; | ||
| 318 | + } else if (ctx_rtu->parity == 'E') { | ||
| 319 | + /* Even */ | ||
| 320 | + tios.c_cflag |= PARENB; | ||
| 321 | + tios.c_cflag &=~ PARODD; | ||
| 322 | + } else { | ||
| 323 | + /* Odd */ | ||
| 324 | + tios.c_cflag |= PARENB; | ||
| 325 | + tios.c_cflag |= PARODD; | ||
| 326 | + } | ||
| 327 | + | ||
| 328 | + /* Read the man page of termios if you need more information. */ | ||
| 329 | + | ||
| 330 | + /* This field isn't used on POSIX systems | ||
| 331 | + tios.c_line = 0; | ||
| 332 | + */ | ||
| 333 | + | ||
| 334 | + /* C_LFLAG Line options | ||
| 335 | + | ||
| 336 | + ISIG Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals | ||
| 337 | + ICANON Enable canonical input (else raw) | ||
| 338 | + XCASE Map uppercase \lowercase (obsolete) | ||
| 339 | + ECHO Enable echoing of input characters | ||
| 340 | + ECHOE Echo erase character as BS-SP-BS | ||
| 341 | + ECHOK Echo NL after kill character | ||
| 342 | + ECHONL Echo NL | ||
| 343 | + NOFLSH Disable flushing of input buffers after | ||
| 344 | + interrupt or quit characters | ||
| 345 | + IEXTEN Enable extended functions | ||
| 346 | + ECHOCTL Echo control characters as ^char and delete as ~? | ||
| 347 | + ECHOPRT Echo erased character as character erased | ||
| 348 | + ECHOKE BS-SP-BS entire line on line kill | ||
| 349 | + FLUSHO Output being flushed | ||
| 350 | + PENDIN Retype pending input at next read or input char | ||
| 351 | + TOSTOP Send SIGTTOU for background output | ||
| 352 | + | ||
| 353 | + Canonical input is line-oriented. Input characters are put | ||
| 354 | + into a buffer which can be edited interactively by the user | ||
| 355 | + until a CR (carriage return) or LF (line feed) character is | ||
| 356 | + received. | ||
| 357 | + | ||
| 358 | + Raw input is unprocessed. Input characters are passed | ||
| 359 | + through exactly as they are received, when they are | ||
| 360 | + received. Generally you'll deselect the ICANON, ECHO, | ||
| 361 | + ECHOE, and ISIG options when using raw input | ||
| 362 | + */ | ||
| 363 | + | ||
| 364 | + /* Raw input */ | ||
| 365 | + tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); | ||
| 366 | + | ||
| 367 | + /* C_IFLAG Input options | ||
| 368 | + | ||
| 369 | + Constant Description | ||
| 370 | + INPCK Enable parity check | ||
| 371 | + IGNPAR Ignore parity errors | ||
| 372 | + PARMRK Mark parity errors | ||
| 373 | + ISTRIP Strip parity bits | ||
| 374 | + IXON Enable software flow control (outgoing) | ||
| 375 | + IXOFF Enable software flow control (incoming) | ||
| 376 | + IXANY Allow any character to start flow again | ||
| 377 | + IGNBRK Ignore break condition | ||
| 378 | + BRKINT Send a SIGINT when a break condition is detected | ||
| 379 | + INLCR Map NL to CR | ||
| 380 | + IGNCR Ignore CR | ||
| 381 | + ICRNL Map CR to NL | ||
| 382 | + IUCLC Map uppercase to lowercase | ||
| 383 | + IMAXBEL Echo BEL on input line too long | ||
| 384 | + */ | ||
| 385 | + if (ctx_rtu->parity == 'N') { | ||
| 386 | + /* None */ | ||
| 387 | + tios.c_iflag &= ~INPCK; | ||
| 388 | + } else { | ||
| 389 | + tios.c_iflag |= INPCK; | ||
| 390 | + } | ||
| 391 | + | ||
| 392 | + /* Software flow control is disabled */ | ||
| 393 | + tios.c_iflag &= ~(IXON | IXOFF | IXANY); | ||
| 394 | + | ||
| 395 | + /* C_OFLAG Output options | ||
| 396 | + OPOST Postprocess output (not set = raw output) | ||
| 397 | + ONLCR Map NL to CR-NL | ||
| 398 | + | ||
| 399 | + ONCLR ant others needs OPOST to be enabled | ||
| 400 | + */ | ||
| 401 | + | ||
| 402 | + /* Raw ouput */ | ||
| 403 | + tios.c_oflag &=~ OPOST; | ||
| 404 | + | ||
| 405 | + /* C_CC Control characters | ||
| 406 | + VMIN Minimum number of characters to read | ||
| 407 | + VTIME Time to wait for data (tenths of seconds) | ||
| 408 | + | ||
| 409 | + UNIX serial interface drivers provide the ability to | ||
| 410 | + specify character and packet timeouts. Two elements of the | ||
| 411 | + c_cc array are used for timeouts: VMIN and VTIME. Timeouts | ||
| 412 | + are ignored in canonical input mode or when the NDELAY | ||
| 413 | + option is set on the file via open or fcntl. | ||
| 414 | + | ||
| 415 | + VMIN specifies the minimum number of characters to read. If | ||
| 416 | + it is set to 0, then the VTIME value specifies the time to | ||
| 417 | + wait for every character read. Note that this does not mean | ||
| 418 | + that a read call for N bytes will wait for N characters to | ||
| 419 | + come in. Rather, the timeout will apply to the first | ||
| 420 | + character and the read call will return the number of | ||
| 421 | + characters immediately available (up to the number you | ||
| 422 | + request). | ||
| 423 | + | ||
| 424 | + If VMIN is non-zero, VTIME specifies the time to wait for | ||
| 425 | + the first character read. If a character is read within the | ||
| 426 | + time given, any read will block (wait) until all VMIN | ||
| 427 | + characters are read. That is, once the first character is | ||
| 428 | + read, the serial interface driver expects to receive an | ||
| 429 | + entire packet of characters (VMIN bytes total). If no | ||
| 430 | + character is read within the time allowed, then the call to | ||
| 431 | + read returns 0. This method allows you to tell the serial | ||
| 432 | + driver you need exactly N bytes and any read call will | ||
| 433 | + return 0 or N bytes. However, the timeout only applies to | ||
| 434 | + the first character read, so if for some reason the driver | ||
| 435 | + misses one character inside the N byte packet then the read | ||
| 436 | + call could block forever waiting for additional input | ||
| 437 | + characters. | ||
| 438 | + | ||
| 439 | + VTIME specifies the amount of time to wait for incoming | ||
| 440 | + characters in tenths of seconds. If VTIME is set to 0 (the | ||
| 441 | + default), reads will block (wait) indefinitely unless the | ||
| 442 | + NDELAY option is set on the port with open or fcntl. | ||
| 443 | + */ | ||
| 444 | + /* Unused because we use open with the NDELAY option */ | ||
| 445 | + tios.c_cc[VMIN] = 0; | ||
| 446 | + tios.c_cc[VTIME] = 0; | ||
| 447 | + | ||
| 448 | + if (tcsetattr(ctx->s, TCSANOW, &tios) < 0) { | ||
| 449 | + return -1; | ||
| 450 | + } | ||
| 451 | + | ||
| 452 | + return 0; | ||
| 453 | +} | ||
| 454 | + | ||
| 455 | +void _modbus_rtu_close(modbus_t *ctx) | ||
| 456 | +{ | ||
| 457 | + /* Closes the file descriptor in RTU mode */ | ||
| 458 | + modbus_rtu_t *ctx_rtu = ctx->backend_data; | ||
| 459 | + | ||
| 460 | + tcsetattr(ctx->s, TCSANOW, &(ctx_rtu->old_tios)); | ||
| 461 | + close(ctx->s); | ||
| 462 | +} | ||
| 463 | + | ||
| 464 | +int _modbus_rtu_flush(modbus_t *ctx) | ||
| 465 | +{ | ||
| 466 | + return tcflush(ctx->s, TCIOFLUSH); | ||
| 467 | +} | ||
| 468 | + | ||
| 469 | +int _modbus_rtu_listen(modbus_t *ctx, int nb_connection) | ||
| 470 | +{ | ||
| 471 | + if (ctx->debug) { | ||
| 472 | + fprintf(stderr, "Not implemented"); | ||
| 473 | + } | ||
| 474 | + | ||
| 475 | + errno = EINVAL; | ||
| 476 | + return -1; | ||
| 477 | +} | ||
| 478 | + | ||
| 479 | +int _modbus_rtu_accept(modbus_t *ctx, int *socket) | ||
| 480 | +{ | ||
| 481 | + if (ctx->debug) { | ||
| 482 | + fprintf(stderr, "Not implemented"); | ||
| 483 | + } | ||
| 484 | + | ||
| 485 | + errno = EINVAL; | ||
| 486 | + return -1; | ||
| 487 | +} | ||
| 488 | + | ||
| 489 | +int _modbus_rtu_filter_request(modbus_t *ctx, int slave) | ||
| 490 | +{ | ||
| 491 | + /* Filter on the Modbus unit identifier (slave) in RTU mode */ | ||
| 492 | + if (slave != ctx->slave && slave != MODBUS_BROADCAST_ADDRESS) { | ||
| 493 | + /* Ignores the request (not for me) */ | ||
| 494 | + if (ctx->debug) { | ||
| 495 | + printf("Request for slave %d ignored (not %d)\n", | ||
| 496 | + slave, ctx->slave); | ||
| 497 | + } | ||
| 498 | + return 1; | ||
| 499 | + } else { | ||
| 500 | + return 0; | ||
| 501 | + } | ||
| 502 | +} | ||
| 503 | + | ||
| 504 | +const modbus_backend_t _modbus_rtu_backend = { | ||
| 505 | + _MODBUS_BACKEND_TYPE_RTU, | ||
| 506 | + _MODBUS_RTU_HEADER_LENGTH, | ||
| 507 | + _MODBUS_RTU_CHECKSUM_LENGTH, | ||
| 508 | + MODBUS_RTU_MAX_ADU_LENGTH, | ||
| 509 | + _modbus_set_slave, | ||
| 510 | + _modbus_rtu_build_request_basis, | ||
| 511 | + _modbus_rtu_build_response_basis, | ||
| 512 | + _modbus_rtu_prepare_response_tid, | ||
| 513 | + _modbus_rtu_send_msg_pre, | ||
| 514 | + _modbus_rtu_send, | ||
| 515 | + _modbus_rtu_recv, | ||
| 516 | + _modbus_rtu_check_integrity, | ||
| 517 | + _modbus_rtu_connect, | ||
| 518 | + _modbus_rtu_close, | ||
| 519 | + _modbus_rtu_flush, | ||
| 520 | + _modbus_rtu_listen, | ||
| 521 | + _modbus_rtu_accept, | ||
| 522 | + _modbus_rtu_filter_request | ||
| 523 | +}; | ||
| 524 | + | ||
| 525 | +/* Allocate and initialize the modbus_t structure for RTU | ||
| 526 | + - device: "/dev/ttyS0" | ||
| 527 | + - baud: 9600, 19200, 57600, 115200, etc | ||
| 528 | + - parity: 'N' stands for None, 'E' for Even and 'O' for odd | ||
| 529 | + - data_bits: 5, 6, 7, 8 | ||
| 530 | + - stop_bits: 1, 2 | ||
| 531 | + - slave: slave number of the caller | ||
| 532 | +*/ | ||
| 533 | +modbus_t* modbus_new_rtu(const char *device, | ||
| 534 | + int baud, char parity, int data_bit, | ||
| 535 | + int stop_bit, int slave) | ||
| 536 | +{ | ||
| 537 | + modbus_t *ctx; | ||
| 538 | + modbus_rtu_t *ctx_rtu; | ||
| 539 | + | ||
| 540 | + ctx = (modbus_t *) malloc(sizeof(modbus_t)); | ||
| 541 | + _modbus_init_common(ctx); | ||
| 542 | + if (modbus_set_slave(ctx, slave) == -1) { | ||
| 543 | + return NULL; | ||
| 544 | + } | ||
| 545 | + | ||
| 546 | + ctx->backend = &_modbus_rtu_backend; | ||
| 547 | + | ||
| 548 | + ctx->backend_data = (modbus_rtu_t *) malloc(sizeof(modbus_rtu_t)); | ||
| 549 | + ctx_rtu = (modbus_rtu_t *)ctx->backend_data; | ||
| 550 | +#if defined(OpenBSD) | ||
| 551 | + strlcpy(ctx_rtu->device, device, sizeof(ctx_rtu->device)); | ||
| 552 | +#else | ||
| 553 | + strcpy(ctx_rtu->device, device); | ||
| 554 | +#endif | ||
| 555 | + ctx_rtu->baud = baud; | ||
| 556 | + if (parity == 'N' || parity == 'E' || parity == 'O') { | ||
| 557 | + ctx_rtu->parity = parity; | ||
| 558 | + } else { | ||
| 559 | + errno = EINVAL; | ||
| 560 | + return NULL; | ||
| 561 | + } | ||
| 562 | + ctx_rtu->data_bit = data_bit; | ||
| 563 | + ctx_rtu->stop_bit = stop_bit; | ||
| 564 | + | ||
| 565 | + return ctx; | ||
| 566 | +} |
src/modbus-rtu.h
0 → 100644
| 1 | +/* | ||
| 2 | + * Copyright © 2001-2010 Stéphane Raimbault <stephane.raimbault@gmail.com> | ||
| 3 | + * | ||
| 4 | + * This program is free software: you can redistribute it and/or modify | ||
| 5 | + * it under the terms of the GNU Lesser Public License as published by | ||
| 6 | + * the Free Software Foundation; either version 3 of the License, or | ||
| 7 | + * (at your option) any later version. | ||
| 8 | + * | ||
| 9 | + * This program 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 | ||
| 12 | + * GNU Lesser Public License for more details. | ||
| 13 | + * | ||
| 14 | + * You should have received a copy of the GNU Lesser Public License | ||
| 15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 16 | + */ | ||
| 17 | + | ||
| 18 | +#ifndef _MODBUS_RTU_H_ | ||
| 19 | +#define _MODBUS_RTU_H_ | ||
| 20 | + | ||
| 21 | +/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5 | ||
| 22 | + * RS232 / RS485 ADU = 253 bytes + slave (1 byte) + CRC (2 bytes) = 256 bytes | ||
| 23 | + */ | ||
| 24 | +#define MODBUS_RTU_MAX_ADU_LENGTH 256 | ||
| 25 | + | ||
| 26 | +modbus_t* modbus_new_rtu(const char *device, int baud, char parity, | ||
| 27 | + int data_bit, int stop_bit, int slave); | ||
| 28 | + | ||
| 29 | +#endif /* _MODBUS_RTU_H_ */ |
src/modbus-tcp-private.h
0 → 100644
| 1 | +/* | ||
| 2 | + * Copyright © 2001-2010 Stéphane Raimbault <stephane.raimbault@gmail.com> | ||
| 3 | + * | ||
| 4 | + * This program is free software: you can redistribute it and/or modify | ||
| 5 | + * it under the terms of the GNU Lesser Public License as published by | ||
| 6 | + * the Free Software Foundation; either version 3 of the License, or | ||
| 7 | + * (at your option) any later version. | ||
| 8 | + * | ||
| 9 | + * This program 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 | ||
| 12 | + * GNU Lesser Public License for more details. | ||
| 13 | + * | ||
| 14 | + * You should have received a copy of the GNU Lesser Public License | ||
| 15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 16 | + */ | ||
| 17 | + | ||
| 18 | +#ifndef _MODBUS_TCP_PRIVATE_H_ | ||
| 19 | +#define _MODBUS_TCP_PRIVATE_H_ | ||
| 20 | + | ||
| 21 | +#define _MODBUS_TCP_HEADER_LENGTH 7 | ||
| 22 | +#define _MODBUS_TCP_PRESET_REQ_LENGTH 12 | ||
| 23 | +#define _MODBUS_TCP_PRESET_RSP_LENGTH 8 | ||
| 24 | + | ||
| 25 | +#define _MODBUS_TCP_CHECKSUM_LENGTH 0 | ||
| 26 | + | ||
| 27 | +typedef struct _modbus_tcp { | ||
| 28 | + /* TCP port */ | ||
| 29 | + int port; | ||
| 30 | + /* IP address */ | ||
| 31 | + char ip[16]; | ||
| 32 | +} modbus_tcp_t; | ||
| 33 | + | ||
| 34 | +#endif /* _MODBUS_TCP_PRIVATE_H_ */ |
src/modbus-tcp.c
0 → 100644
| 1 | +/* | ||
| 2 | + * Copyright © 2001-2010 Stéphane Raimbault <stephane.raimbault@gmail.com> | ||
| 3 | + * | ||
| 4 | + * This program is free software: you can redistribute it and/or modify | ||
| 5 | + * it under the terms of the GNU Lesser Public License as published by | ||
| 6 | + * the Free Software Foundation; either version 3 of the License, or | ||
| 7 | + * (at your option) any later version. | ||
| 8 | + * | ||
| 9 | + * This program 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 | ||
| 12 | + * GNU Lesser Public License for more details. | ||
| 13 | + * | ||
| 14 | + * You should have received a copy of the GNU Lesser Public License | ||
| 15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 16 | + */ | ||
| 17 | + | ||
| 18 | +#include <stdio.h> | ||
| 19 | +#include <stdlib.h> | ||
| 20 | +#include <string.h> | ||
| 21 | +#include <errno.h> | ||
| 22 | + | ||
| 23 | +#include <sys/types.h> | ||
| 24 | +#include <sys/socket.h> | ||
| 25 | +#include <sys/ioctl.h> | ||
| 26 | +#if (defined OpenBSD) || (defined(__FreeBSD__ ) && __FreeBSD__ < 5) | ||
| 27 | +#include <netinet/in_systm.h> | ||
| 28 | +#endif | ||
| 29 | +#include <netinet/in.h> | ||
| 30 | +#include <netinet/ip.h> | ||
| 31 | +#include <netinet/tcp.h> | ||
| 32 | +#include <arpa/inet.h> | ||
| 33 | + | ||
| 34 | +#include "modbus.h" | ||
| 35 | +#include "modbus-private.h" | ||
| 36 | + | ||
| 37 | +#include "modbus-tcp.h" | ||
| 38 | +#include "modbus-tcp-private.h" | ||
| 39 | + | ||
| 40 | +static int _modbus_set_slave(modbus_t *ctx, int slave) | ||
| 41 | +{ | ||
| 42 | + if (slave >= 1 && slave <= 247) { | ||
| 43 | + ctx->slave = slave; | ||
| 44 | + } else if (slave == MODBUS_TCP_SLAVE) { | ||
| 45 | + /* The special value MODBUS_TCP_SLAVE (0xFF) can be used in TCP mode to | ||
| 46 | + * restore the default value. */ | ||
| 47 | + ctx->slave = slave; | ||
| 48 | + } else { | ||
| 49 | + errno = EINVAL; | ||
| 50 | + return -1; | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + return 0; | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +/* Builds a TCP request header */ | ||
| 57 | +int _modbus_tcp_build_request_basis(modbus_t *ctx, int function, | ||
| 58 | + int addr, int nb, | ||
| 59 | + uint8_t *req) | ||
| 60 | +{ | ||
| 61 | + | ||
| 62 | + /* Extract from MODBUS Messaging on TCP/IP Implementation Guide V1.0b | ||
| 63 | + (page 23/46): | ||
| 64 | + The transaction identifier is used to associate the future response | ||
| 65 | + with the request. So, at a time, on a TCP connection, this identifier | ||
| 66 | + must be unique. */ | ||
| 67 | + static uint16_t t_id = 0; | ||
| 68 | + | ||
| 69 | + /* Transaction ID */ | ||
| 70 | + if (t_id < UINT16_MAX) | ||
| 71 | + t_id++; | ||
| 72 | + else | ||
| 73 | + t_id = 0; | ||
| 74 | + req[0] = t_id >> 8; | ||
| 75 | + req[1] = t_id & 0x00ff; | ||
| 76 | + | ||
| 77 | + /* Protocol Modbus */ | ||
| 78 | + req[2] = 0; | ||
| 79 | + req[3] = 0; | ||
| 80 | + | ||
| 81 | + /* Length will be defined later by set_req_length_tcp at offsets 4 | ||
| 82 | + and 5 */ | ||
| 83 | + | ||
| 84 | + req[6] = ctx->slave; | ||
| 85 | + req[7] = function; | ||
| 86 | + req[8] = addr >> 8; | ||
| 87 | + req[9] = addr & 0x00ff; | ||
| 88 | + req[10] = nb >> 8; | ||
| 89 | + req[11] = nb & 0x00ff; | ||
| 90 | + | ||
| 91 | + return _MODBUS_TCP_PRESET_REQ_LENGTH; | ||
| 92 | +} | ||
| 93 | + | ||
| 94 | +/* Builds a TCP response header */ | ||
| 95 | +int _modbus_tcp_build_response_basis(sft_t *sft, uint8_t *rsp) | ||
| 96 | +{ | ||
| 97 | + /* Extract from MODBUS Messaging on TCP/IP Implementation | ||
| 98 | + Guide V1.0b (page 23/46): | ||
| 99 | + The transaction identifier is used to associate the future | ||
| 100 | + response with the request. */ | ||
| 101 | + rsp[0] = sft->t_id >> 8; | ||
| 102 | + rsp[1] = sft->t_id & 0x00ff; | ||
| 103 | + | ||
| 104 | + /* Protocol Modbus */ | ||
| 105 | + rsp[2] = 0; | ||
| 106 | + rsp[3] = 0; | ||
| 107 | + | ||
| 108 | + /* Length will be set later by send_msg (4 and 5) */ | ||
| 109 | + | ||
| 110 | + rsp[6] = 0xFF; | ||
| 111 | + rsp[7] = sft->function; | ||
| 112 | + | ||
| 113 | + return _MODBUS_TCP_PRESET_RSP_LENGTH; | ||
| 114 | +} | ||
| 115 | + | ||
| 116 | + | ||
| 117 | +int _modbus_tcp_prepare_response_tid(const uint8_t *req, int *req_length) | ||
| 118 | +{ | ||
| 119 | + return (req[0] << 8) + req[1]; | ||
| 120 | +} | ||
| 121 | + | ||
| 122 | +int _modbus_tcp_send_msg_pre(uint8_t *req, int req_length) | ||
| 123 | +{ | ||
| 124 | + /* Substract the header length to the message length */ | ||
| 125 | + int mbap_length = req_length - 6; | ||
| 126 | + | ||
| 127 | + req[4] = mbap_length >> 8; | ||
| 128 | + req[5] = mbap_length & 0x00FF; | ||
| 129 | + | ||
| 130 | + return req_length; | ||
| 131 | +} | ||
| 132 | + | ||
| 133 | +ssize_t _modbus_tcp_send(int s, const uint8_t *req, int req_length) | ||
| 134 | +{ | ||
| 135 | + /* MSG_NOSIGNAL | ||
| 136 | + Requests not to send SIGPIPE on errors on stream oriented | ||
| 137 | + sockets when the other end breaks the connection. The EPIPE | ||
| 138 | + error is still returned. */ | ||
| 139 | + return send(s, req, req_length, MSG_NOSIGNAL); | ||
| 140 | +} | ||
| 141 | + | ||
| 142 | +ssize_t _modbus_tcp_recv(int s, uint8_t *rsp, int rsp_length) { | ||
| 143 | + return recv(s, rsp, rsp_length, 0); | ||
| 144 | +} | ||
| 145 | + | ||
| 146 | +int _modbus_tcp_check_integrity(modbus_t *ctx, uint8_t *msg, const int msg_length) | ||
| 147 | +{ | ||
| 148 | + return msg_length; | ||
| 149 | +} | ||
| 150 | + | ||
| 151 | +/* Establishes a modbus TCP connection with a Modbus server. */ | ||
| 152 | +static int _modbus_tcp_connect(modbus_t *ctx) | ||
| 153 | +{ | ||
| 154 | + int rc; | ||
| 155 | + int option; | ||
| 156 | + struct sockaddr_in addr; | ||
| 157 | + modbus_tcp_t *ctx_tcp = ctx->backend_data; | ||
| 158 | + | ||
| 159 | + ctx->s = socket(PF_INET, SOCK_STREAM, 0); | ||
| 160 | + if (ctx->s == -1) { | ||
| 161 | + return -1; | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + /* Set the TCP no delay flag */ | ||
| 165 | + /* SOL_TCP = IPPROTO_TCP */ | ||
| 166 | + option = 1; | ||
| 167 | + rc = setsockopt(ctx->s, IPPROTO_TCP, TCP_NODELAY, | ||
| 168 | + (const void *)&option, sizeof(int)); | ||
| 169 | + if (rc == -1) { | ||
| 170 | + close(ctx->s); | ||
| 171 | + return -1; | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | +#if (!HAVE_DECL___CYGWIN__) | ||
| 175 | + /** | ||
| 176 | + * Cygwin defines IPTOS_LOWDELAY but can't handle that flag so it's | ||
| 177 | + * necessary to workaround that problem. | ||
| 178 | + **/ | ||
| 179 | + /* Set the IP low delay option */ | ||
| 180 | + option = IPTOS_LOWDELAY; | ||
| 181 | + rc = setsockopt(ctx->s, IPPROTO_IP, IP_TOS, | ||
| 182 | + (const void *)&option, sizeof(int)); | ||
| 183 | + if (rc == -1) { | ||
| 184 | + close(ctx->s); | ||
| 185 | + return -1; | ||
| 186 | + } | ||
| 187 | +#endif | ||
| 188 | + | ||
| 189 | + if (ctx->debug) { | ||
| 190 | + printf("Connecting to %s\n", ctx_tcp->ip); | ||
| 191 | + } | ||
| 192 | + | ||
| 193 | + addr.sin_family = AF_INET; | ||
| 194 | + addr.sin_port = htons(ctx_tcp->port); | ||
| 195 | + addr.sin_addr.s_addr = inet_addr(ctx_tcp->ip); | ||
| 196 | + rc = connect(ctx->s, (struct sockaddr *)&addr, | ||
| 197 | + sizeof(struct sockaddr_in)); | ||
| 198 | + if (rc == -1) { | ||
| 199 | + close(ctx->s); | ||
| 200 | + return -1; | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + return 0; | ||
| 204 | +} | ||
| 205 | + | ||
| 206 | +/* Closes the network connection and socket in TCP mode */ | ||
| 207 | +void _modbus_tcp_close(modbus_t *ctx) | ||
| 208 | +{ | ||
| 209 | + shutdown(ctx->s, SHUT_RDWR); | ||
| 210 | + close(ctx->s); | ||
| 211 | +} | ||
| 212 | + | ||
| 213 | +int _modbus_tcp_flush(modbus_t *ctx) | ||
| 214 | +{ | ||
| 215 | + int rc; | ||
| 216 | + | ||
| 217 | + do { | ||
| 218 | + /* Extract the garbage from the socket */ | ||
| 219 | + char devnull[MODBUS_TCP_MAX_ADU_LENGTH]; | ||
| 220 | +#if (!HAVE_DECL___CYGWIN__) | ||
| 221 | + rc = recv(ctx->s, devnull, MODBUS_TCP_MAX_ADU_LENGTH, MSG_DONTWAIT); | ||
| 222 | +#else | ||
| 223 | + /* On Cygwin, it's a bit more complicated to not wait */ | ||
| 224 | + fd_set rfds; | ||
| 225 | + struct timeval tv; | ||
| 226 | + | ||
| 227 | + tv.tv_sec = 0; | ||
| 228 | + tv.tv_usec = 0; | ||
| 229 | + FD_ZERO(&rfds); | ||
| 230 | + FD_SET(ctx->s, &rfds); | ||
| 231 | + rc = select(ctx->s+1, &rfds, NULL, NULL, &tv); | ||
| 232 | + if (rc == -1) { | ||
| 233 | + return -1; | ||
| 234 | + } | ||
| 235 | + | ||
| 236 | + rc = recv(ctx->s, devnull, MODBUS_TCP_MAX_ADU_LENGTH, 0); | ||
| 237 | +#endif | ||
| 238 | + if (ctx->debug && rc != -1) { | ||
| 239 | + printf("\n%d bytes flushed\n", rc); | ||
| 240 | + } | ||
| 241 | + } while (rc > 0); | ||
| 242 | + | ||
| 243 | + return rc; | ||
| 244 | +} | ||
| 245 | + | ||
| 246 | +/* Listens for any request from one or many modbus masters in TCP */ | ||
| 247 | +int _modbus_tcp_listen(modbus_t *ctx, int nb_connection) | ||
| 248 | +{ | ||
| 249 | + int new_socket; | ||
| 250 | + int yes; | ||
| 251 | + struct sockaddr_in addr; | ||
| 252 | + modbus_tcp_t *ctx_tcp = ctx->backend_data; | ||
| 253 | + | ||
| 254 | + new_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
| 255 | + if (new_socket == -1) { | ||
| 256 | + return -1; | ||
| 257 | + } | ||
| 258 | + | ||
| 259 | + yes = 1; | ||
| 260 | + if (setsockopt(new_socket, SOL_SOCKET, SO_REUSEADDR, | ||
| 261 | + (char *) &yes, sizeof(yes)) == -1) { | ||
| 262 | + close(new_socket); | ||
| 263 | + return -1; | ||
| 264 | + } | ||
| 265 | + | ||
| 266 | + memset(&addr, 0, sizeof(addr)); | ||
| 267 | + addr.sin_family = AF_INET; | ||
| 268 | + /* If the modbus port is < to 1024, we need the setuid root. */ | ||
| 269 | + addr.sin_port = htons(ctx_tcp->port); | ||
| 270 | + addr.sin_addr.s_addr = INADDR_ANY; | ||
| 271 | + if (bind(new_socket, (struct sockaddr *)&addr, sizeof(addr)) == -1) { | ||
| 272 | + close(new_socket); | ||
| 273 | + return -1; | ||
| 274 | + } | ||
| 275 | + | ||
| 276 | + if (listen(new_socket, nb_connection) == -1) { | ||
| 277 | + close(new_socket); | ||
| 278 | + return -1; | ||
| 279 | + } | ||
| 280 | + | ||
| 281 | + return new_socket; | ||
| 282 | +} | ||
| 283 | + | ||
| 284 | +/* On success, the function return a non-negative integer that is a descriptor | ||
| 285 | + for the accepted socket. On error, -1 is returned, and errno is set | ||
| 286 | + appropriately. */ | ||
| 287 | +int _modbus_tcp_accept(modbus_t *ctx, int *socket) | ||
| 288 | +{ | ||
| 289 | + struct sockaddr_in addr; | ||
| 290 | + socklen_t addrlen; | ||
| 291 | + | ||
| 292 | + addrlen = sizeof(struct sockaddr_in); | ||
| 293 | + ctx->s = accept(*socket, (struct sockaddr *)&addr, &addrlen); | ||
| 294 | + if (ctx->s == -1) { | ||
| 295 | + close(*socket); | ||
| 296 | + *socket = 0; | ||
| 297 | + return -1; | ||
| 298 | + } | ||
| 299 | + | ||
| 300 | + if (ctx->debug) { | ||
| 301 | + printf("The client %s is connected\n", inet_ntoa(addr.sin_addr)); | ||
| 302 | + } | ||
| 303 | + | ||
| 304 | + return ctx->s; | ||
| 305 | +} | ||
| 306 | + | ||
| 307 | +int _modbus_tcp_filter_request(modbus_t *ctx, int slave) | ||
| 308 | +{ | ||
| 309 | + return 0; | ||
| 310 | +} | ||
| 311 | + | ||
| 312 | +const modbus_backend_t _modbus_tcp_backend = { | ||
| 313 | + _MODBUS_BACKEND_TYPE_TCP, | ||
| 314 | + _MODBUS_TCP_HEADER_LENGTH, | ||
| 315 | + _MODBUS_TCP_CHECKSUM_LENGTH, | ||
| 316 | + MODBUS_TCP_MAX_ADU_LENGTH, | ||
| 317 | + _modbus_set_slave, | ||
| 318 | + _modbus_tcp_build_request_basis, | ||
| 319 | + _modbus_tcp_build_response_basis, | ||
| 320 | + _modbus_tcp_prepare_response_tid, | ||
| 321 | + _modbus_tcp_send_msg_pre, | ||
| 322 | + _modbus_tcp_send, | ||
| 323 | + _modbus_tcp_recv, | ||
| 324 | + _modbus_tcp_check_integrity, | ||
| 325 | + _modbus_tcp_connect, | ||
| 326 | + _modbus_tcp_close, | ||
| 327 | + _modbus_tcp_flush, | ||
| 328 | + _modbus_tcp_listen, | ||
| 329 | + _modbus_tcp_accept, | ||
| 330 | + _modbus_tcp_filter_request | ||
| 331 | +}; | ||
| 332 | + | ||
| 333 | + | ||
| 334 | +/* Allocates and initializes the modbus_t structure for TCP. | ||
| 335 | + - ip : "192.168.0.5" | ||
| 336 | + - port : 1099 | ||
| 337 | + | ||
| 338 | + Set the port to MODBUS_TCP_DEFAULT_PORT to use the default one | ||
| 339 | + (502). It's convenient to use a port number greater than or equal | ||
| 340 | + to 1024 because it's not necessary to be root to use this port | ||
| 341 | + number. | ||
| 342 | +*/ | ||
| 343 | +modbus_t* modbus_new_tcp(const char *ip, int port) | ||
| 344 | +{ | ||
| 345 | + modbus_t *ctx; | ||
| 346 | + modbus_tcp_t *ctx_tcp; | ||
| 347 | + | ||
| 348 | + ctx = (modbus_t *) malloc(sizeof(modbus_t)); | ||
| 349 | + _modbus_init_common(ctx); | ||
| 350 | + | ||
| 351 | + /* Could be changed after to reach a remote serial Modbus device */ | ||
| 352 | + ctx->slave = MODBUS_TCP_SLAVE; | ||
| 353 | + | ||
| 354 | + ctx->backend = &(_modbus_tcp_backend); | ||
| 355 | + | ||
| 356 | + ctx->backend_data = (modbus_tcp_t *) malloc(sizeof(modbus_tcp_t)); | ||
| 357 | + ctx_tcp = (modbus_tcp_t *)ctx->backend_data; | ||
| 358 | + | ||
| 359 | + strncpy(ctx_tcp->ip, ip, sizeof(char)*16); | ||
| 360 | + ctx_tcp->port = port; | ||
| 361 | + | ||
| 362 | + return ctx; | ||
| 363 | +} |
src/modbus-tcp.h
0 → 100644
| 1 | +/* | ||
| 2 | + * Copyright © 2001-2010 Stéphane Raimbault <stephane.raimbault@gmail.com> | ||
| 3 | + * | ||
| 4 | + * This program is free software: you can redistribute it and/or modify | ||
| 5 | + * it under the terms of the GNU Lesser Public License as published by | ||
| 6 | + * the Free Software Foundation; either version 3 of the License, or | ||
| 7 | + * (at your option) any later version. | ||
| 8 | + * | ||
| 9 | + * This program 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 | ||
| 12 | + * GNU Lesser Public License for more details. | ||
| 13 | + * | ||
| 14 | + * You should have received a copy of the GNU Lesser Public License | ||
| 15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 16 | + */ | ||
| 17 | + | ||
| 18 | +#ifndef _MODBUS_TCP_H_ | ||
| 19 | +#define _MODBUS_TCP_H_ | ||
| 20 | + | ||
| 21 | +#define MODBUS_TCP_DEFAULT_PORT 502 | ||
| 22 | +#define MODBUS_TCP_SLAVE 0xFF | ||
| 23 | + | ||
| 24 | +/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5 | ||
| 25 | + * TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes | ||
| 26 | + */ | ||
| 27 | +#define MODBUS_TCP_MAX_ADU_LENGTH 260 | ||
| 28 | + | ||
| 29 | +modbus_t* modbus_new_tcp(const char *ip_address, int port); | ||
| 30 | + | ||
| 31 | +#endif /* _MODBUS_TCP_H_ */ |
src/modbus.c
| @@ -13,59 +13,30 @@ | @@ -13,59 +13,30 @@ | ||
| 13 | * | 13 | * |
| 14 | * You should have received a copy of the GNU Lesser Public License | 14 | * You should have received a copy of the GNU Lesser Public License |
| 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 16 | + * | ||
| 17 | + * | ||
| 18 | + * This library implements the Modbus protocol. | ||
| 19 | + * http://libmodbus.org/ | ||
| 16 | */ | 20 | */ |
| 17 | 21 | ||
| 18 | -/* | ||
| 19 | - The library is designed to send and receive data from a device that | ||
| 20 | - communicate via the Modbus protocol. | ||
| 21 | - | ||
| 22 | - The function names used are inspired by the Modicon Modbus Protocol | ||
| 23 | - Reference Guide which can be obtained from Schneider at | ||
| 24 | - www.schneiderautomation.com. | ||
| 25 | - | ||
| 26 | - Documentation: | ||
| 27 | - http://www.easysw.com/~mike/serial/serial.html | ||
| 28 | - http://copyleft.free.fr/wordpress/index.php/libmodbus/ | ||
| 29 | -*/ | ||
| 30 | - | ||
| 31 | #include <stdio.h> | 22 | #include <stdio.h> |
| 32 | #include <string.h> | 23 | #include <string.h> |
| 33 | #include <stdlib.h> | 24 | #include <stdlib.h> |
| 34 | -#ifdef HAVE_INTTYPES_H | ||
| 35 | -#include <inttypes.h> | ||
| 36 | -#endif | ||
| 37 | -#ifdef HAVE_STDINT_H | ||
| 38 | #include <stdint.h> | 25 | #include <stdint.h> |
| 39 | -#endif | ||
| 40 | #include <termios.h> | 26 | #include <termios.h> |
| 41 | #include <unistd.h> | 27 | #include <unistd.h> |
| 42 | #include <errno.h> | 28 | #include <errno.h> |
| 43 | -#include <assert.h> | ||
| 44 | #include <limits.h> | 29 | #include <limits.h> |
| 45 | -#include <fcntl.h> | ||
| 46 | 30 | ||
| 47 | /* Add this for macros that defined unix flavor */ | 31 | /* Add this for macros that defined unix flavor */ |
| 48 | #if (defined(__unix__) || defined(unix)) && !defined(USG) | 32 | #if (defined(__unix__) || defined(unix)) && !defined(USG) |
| 49 | #include <sys/param.h> | 33 | #include <sys/param.h> |
| 50 | #endif | 34 | #endif |
| 51 | 35 | ||
| 52 | -/* TCP */ | ||
| 53 | -#include <sys/types.h> | ||
| 54 | -#include <sys/socket.h> | ||
| 55 | -#include <sys/ioctl.h> | ||
| 56 | -#if (defined OpenBSD) || (defined(__FreeBSD__ ) && __FreeBSD__ < 5) | ||
| 57 | -#include <netinet/in_systm.h> | ||
| 58 | -#endif | ||
| 59 | -#include <netinet/in.h> | ||
| 60 | -#include <netinet/ip.h> | ||
| 61 | -#include <netinet/tcp.h> | ||
| 62 | -#include <arpa/inet.h> | ||
| 63 | - | ||
| 64 | #if !defined(UINT16_MAX) | 36 | #if !defined(UINT16_MAX) |
| 65 | #define UINT16_MAX 0xFFFF | 37 | #define UINT16_MAX 0xFFFF |
| 66 | #endif | 38 | #endif |
| 67 | 39 | ||
| 68 | -#include <config.h> | ||
| 69 | #include "modbus.h" | 40 | #include "modbus.h" |
| 70 | #include "modbus-private.h" | 41 | #include "modbus-private.h" |
| 71 | 42 | ||
| @@ -77,91 +48,8 @@ const unsigned int libmodbus_version_major = LIBMODBUS_VERSION_MAJOR; | @@ -77,91 +48,8 @@ const unsigned int libmodbus_version_major = LIBMODBUS_VERSION_MAJOR; | ||
| 77 | const unsigned int libmodbus_version_minor = LIBMODBUS_VERSION_MINOR; | 48 | const unsigned int libmodbus_version_minor = LIBMODBUS_VERSION_MINOR; |
| 78 | const unsigned int libmodbus_version_micro = LIBMODBUS_VERSION_MICRO; | 49 | const unsigned int libmodbus_version_micro = LIBMODBUS_VERSION_MICRO; |
| 79 | 50 | ||
| 80 | -/* This structure reduces the number of params in functions and so | ||
| 81 | - * optimizes the speed of execution (~ 37%). */ | ||
| 82 | -typedef struct { | ||
| 83 | - int slave; | ||
| 84 | - int function; | ||
| 85 | - int t_id; | ||
| 86 | -} sft_t; | ||
| 87 | - | ||
| 88 | -/* Table of CRC values for high-order byte */ | ||
| 89 | -static uint8_t table_crc_hi[] = { | ||
| 90 | - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, | ||
| 91 | - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, | ||
| 92 | - 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, | ||
| 93 | - 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, | ||
| 94 | - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, | ||
| 95 | - 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, | ||
| 96 | - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, | ||
| 97 | - 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, | ||
| 98 | - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, | ||
| 99 | - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, | ||
| 100 | - 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, | ||
| 101 | - 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, | ||
| 102 | - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, | ||
| 103 | - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, | ||
| 104 | - 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, | ||
| 105 | - 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, | ||
| 106 | - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, | ||
| 107 | - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, | ||
| 108 | - 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, | ||
| 109 | - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, | ||
| 110 | - 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, | ||
| 111 | - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, | ||
| 112 | - 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, | ||
| 113 | - 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, | ||
| 114 | - 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, | ||
| 115 | - 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | ||
| 116 | -}; | ||
| 117 | - | ||
| 118 | -/* Table of CRC values for low-order byte */ | ||
| 119 | -static uint8_t table_crc_lo[] = { | ||
| 120 | - 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, | ||
| 121 | - 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, | ||
| 122 | - 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, | ||
| 123 | - 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, | ||
| 124 | - 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, | ||
| 125 | - 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, | ||
| 126 | - 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, | ||
| 127 | - 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, | ||
| 128 | - 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, | ||
| 129 | - 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, | ||
| 130 | - 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, | ||
| 131 | - 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, | ||
| 132 | - 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, | ||
| 133 | - 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, | ||
| 134 | - 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, | ||
| 135 | - 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, | ||
| 136 | - 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, | ||
| 137 | - 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, | ||
| 138 | - 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, | ||
| 139 | - 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, | ||
| 140 | - 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, | ||
| 141 | - 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, | ||
| 142 | - 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, | ||
| 143 | - 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, | ||
| 144 | - 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, | ||
| 145 | - 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 | ||
| 146 | -}; | ||
| 147 | - | ||
| 148 | -static const int TAB_HEADER_LENGTH[2] = { | ||
| 149 | - HEADER_LENGTH_RTU, | ||
| 150 | - HEADER_LENGTH_TCP | ||
| 151 | -}; | ||
| 152 | - | ||
| 153 | -static const int TAB_CHECKSUM_LENGTH[2] = { | ||
| 154 | - CHECKSUM_LENGTH_RTU, | ||
| 155 | - CHECKSUM_LENGTH_TCP | ||
| 156 | -}; | ||
| 157 | - | ||
| 158 | -static const int TAB_MAX_ADU_LENGTH[2] = { | ||
| 159 | - MODBUS_MAX_ADU_LENGTH_RTU, | ||
| 160 | - MODBUS_MAX_ADU_LENGTH_TCP, | ||
| 161 | -}; | ||
| 162 | - | ||
| 163 | -/* Max between RTU and TCP max adu length */ | ||
| 164 | -#define MAX_MESSAGE_LENGTH MODBUS_MAX_ADU_LENGTH_TCP | 51 | +/* Max between RTU and TCP max adu length (so TCP) */ |
| 52 | +#define MAX_MESSAGE_LENGTH 260 | ||
| 165 | 53 | ||
| 166 | const char *modbus_strerror(int errnum) { | 54 | const char *modbus_strerror(int errnum) { |
| 167 | switch (errnum) { | 55 | switch (errnum) { |
| @@ -212,39 +100,7 @@ static void error_print(modbus_t *ctx, const char *context) | @@ -212,39 +100,7 @@ static void error_print(modbus_t *ctx, const char *context) | ||
| 212 | 100 | ||
| 213 | int modbus_flush(modbus_t *ctx) | 101 | int modbus_flush(modbus_t *ctx) |
| 214 | { | 102 | { |
| 215 | - int rc; | ||
| 216 | - | ||
| 217 | - if (ctx->type_com == RTU) { | ||
| 218 | - rc = tcflush(ctx->s, TCIOFLUSH); | ||
| 219 | - } else { | ||
| 220 | - do { | ||
| 221 | - /* Extract the garbage from the socket */ | ||
| 222 | - char devnull[MODBUS_MAX_ADU_LENGTH_TCP]; | ||
| 223 | -#if (!HAVE_DECL___CYGWIN__) | ||
| 224 | - rc = recv(ctx->s, devnull, MODBUS_MAX_ADU_LENGTH_TCP, MSG_DONTWAIT); | ||
| 225 | -#else | ||
| 226 | - /* On Cygwin, it's a bit more complicated to not wait */ | ||
| 227 | - fd_set rfds; | ||
| 228 | - struct timeval tv; | ||
| 229 | - | ||
| 230 | - tv.tv_sec = 0; | ||
| 231 | - tv.tv_usec = 0; | ||
| 232 | - FD_ZERO(&rfds); | ||
| 233 | - FD_SET(ctx->s, &rfds); | ||
| 234 | - rc = select(ctx->s+1, &rfds, NULL, NULL, &tv); | ||
| 235 | - if (rc == -1) { | ||
| 236 | - return -1; | ||
| 237 | - } | ||
| 238 | - | ||
| 239 | - rc = recv(ctx->s, devnull, MODBUS_MAX_ADU_LENGTH_TCP, 0); | ||
| 240 | -#endif | ||
| 241 | - if (ctx->debug && rc != -1) { | ||
| 242 | - printf("\n%d bytes flushed\n", rc); | ||
| 243 | - } | ||
| 244 | - } while (rc > 0); | ||
| 245 | - } | ||
| 246 | - | ||
| 247 | - return rc; | 103 | + ctx->backend->flush(ctx); |
| 248 | } | 104 | } |
| 249 | 105 | ||
| 250 | /* Computes the length of the expected response */ | 106 | /* Computes the length of the expected response */ |
| @@ -253,26 +109,26 @@ static unsigned int compute_response_length(modbus_t *ctx, uint8_t *req) | @@ -253,26 +109,26 @@ static unsigned int compute_response_length(modbus_t *ctx, uint8_t *req) | ||
| 253 | int length; | 109 | int length; |
| 254 | int offset; | 110 | int offset; |
| 255 | 111 | ||
| 256 | - offset = TAB_HEADER_LENGTH[ctx->type_com]; | 112 | + offset = ctx->backend->header_length; |
| 257 | 113 | ||
| 258 | switch (req[offset]) { | 114 | switch (req[offset]) { |
| 259 | - case FC_READ_COILS: | ||
| 260 | - case FC_READ_DISCRETE_INPUTS: { | 115 | + case _FC_READ_COILS: |
| 116 | + case _FC_READ_DISCRETE_INPUTS: { | ||
| 261 | /* Header + nb values (code from write_bits) */ | 117 | /* Header + nb values (code from write_bits) */ |
| 262 | int nb = (req[offset + 3] << 8) | req[offset + 4]; | 118 | int nb = (req[offset + 3] << 8) | req[offset + 4]; |
| 263 | length = 2 + (nb / 8) + ((nb % 8) ? 1 : 0); | 119 | length = 2 + (nb / 8) + ((nb % 8) ? 1 : 0); |
| 264 | } | 120 | } |
| 265 | break; | 121 | break; |
| 266 | - case FC_READ_AND_WRITE_REGISTERS: | ||
| 267 | - case FC_READ_HOLDING_REGISTERS: | ||
| 268 | - case FC_READ_INPUT_REGISTERS: | 122 | + case _FC_READ_AND_WRITE_REGISTERS: |
| 123 | + case _FC_READ_HOLDING_REGISTERS: | ||
| 124 | + case _FC_READ_INPUT_REGISTERS: | ||
| 269 | /* Header + 2 * nb values */ | 125 | /* Header + 2 * nb values */ |
| 270 | length = 2 + 2 * (req[offset + 3] << 8 | req[offset + 4]); | 126 | length = 2 + 2 * (req[offset + 3] << 8 | req[offset + 4]); |
| 271 | break; | 127 | break; |
| 272 | - case FC_READ_EXCEPTION_STATUS: | 128 | + case _FC_READ_EXCEPTION_STATUS: |
| 273 | length = 3; | 129 | length = 3; |
| 274 | break; | 130 | break; |
| 275 | - case FC_REPORT_SLAVE_ID: | 131 | + case _FC_REPORT_SLAVE_ID: |
| 276 | /* The response is device specific (the header provides the | 132 | /* The response is device specific (the header provides the |
| 277 | length) */ | 133 | length) */ |
| 278 | return MSG_LENGTH_UNDEFINED; | 134 | return MSG_LENGTH_UNDEFINED; |
| @@ -280,171 +136,16 @@ static unsigned int compute_response_length(modbus_t *ctx, uint8_t *req) | @@ -280,171 +136,16 @@ static unsigned int compute_response_length(modbus_t *ctx, uint8_t *req) | ||
| 280 | length = 5; | 136 | length = 5; |
| 281 | } | 137 | } |
| 282 | 138 | ||
| 283 | - return length + offset + TAB_CHECKSUM_LENGTH[ctx->type_com]; | 139 | + return length + offset + ctx->backend->checksum_length; |
| 284 | } | 140 | } |
| 285 | 141 | ||
| 286 | -/* Builds a RTU request header */ | ||
| 287 | -static int build_request_basis_rtu(int slave, int function, | ||
| 288 | - int addr, int nb, | ||
| 289 | - uint8_t *req) | ||
| 290 | -{ | ||
| 291 | - req[0] = slave; | ||
| 292 | - req[1] = function; | ||
| 293 | - req[2] = addr >> 8; | ||
| 294 | - req[3] = addr & 0x00ff; | ||
| 295 | - req[4] = nb >> 8; | ||
| 296 | - req[5] = nb & 0x00ff; | ||
| 297 | - | ||
| 298 | - return PRESET_REQ_LENGTH_RTU; | ||
| 299 | -} | ||
| 300 | - | ||
| 301 | -/* Builds a TCP request header */ | ||
| 302 | -static int build_request_basis_tcp(int slave, int function, | ||
| 303 | - int addr, int nb, | ||
| 304 | - uint8_t *req) | ||
| 305 | -{ | ||
| 306 | - | ||
| 307 | - /* Extract from MODBUS Messaging on TCP/IP Implementation Guide V1.0b | ||
| 308 | - (page 23/46): | ||
| 309 | - The transaction identifier is used to associate the future response | ||
| 310 | - with the request. So, at a time, on a TCP connection, this identifier | ||
| 311 | - must be unique. */ | ||
| 312 | - static uint16_t t_id = 0; | ||
| 313 | - | ||
| 314 | - /* Transaction ID */ | ||
| 315 | - if (t_id < UINT16_MAX) | ||
| 316 | - t_id++; | ||
| 317 | - else | ||
| 318 | - t_id = 0; | ||
| 319 | - req[0] = t_id >> 8; | ||
| 320 | - req[1] = t_id & 0x00ff; | ||
| 321 | - | ||
| 322 | - /* Protocol Modbus */ | ||
| 323 | - req[2] = 0; | ||
| 324 | - req[3] = 0; | ||
| 325 | - | ||
| 326 | - /* Length will be defined later by set_req_length_tcp at offsets 4 | ||
| 327 | - and 5 */ | ||
| 328 | - | ||
| 329 | - req[6] = slave; | ||
| 330 | - req[7] = function; | ||
| 331 | - req[8] = addr >> 8; | ||
| 332 | - req[9] = addr & 0x00ff; | ||
| 333 | - req[10] = nb >> 8; | ||
| 334 | - req[11] = nb & 0x00ff; | ||
| 335 | - | ||
| 336 | - return PRESET_REQ_LENGTH_TCP; | ||
| 337 | -} | ||
| 338 | - | ||
| 339 | -static int build_request_basis(modbus_t *ctx, int function, int addr, | ||
| 340 | - int nb, uint8_t *req) | ||
| 341 | -{ | ||
| 342 | - if (ctx->type_com == RTU) | ||
| 343 | - return build_request_basis_rtu(ctx->slave, function, addr, nb, req); | ||
| 344 | - else | ||
| 345 | - return build_request_basis_tcp(ctx->slave, function, addr, nb, req); | ||
| 346 | -} | ||
| 347 | - | ||
| 348 | -/* Builds a RTU response header */ | ||
| 349 | -static int build_response_basis_rtu(sft_t *sft, uint8_t *rsp) | ||
| 350 | -{ | ||
| 351 | - rsp[0] = sft->slave; | ||
| 352 | - rsp[1] = sft->function; | ||
| 353 | - | ||
| 354 | - return PRESET_RSP_LENGTH_RTU; | ||
| 355 | -} | ||
| 356 | - | ||
| 357 | -/* Builds a TCP response header */ | ||
| 358 | -static int build_response_basis_tcp(sft_t *sft, uint8_t *rsp) | ||
| 359 | -{ | ||
| 360 | - /* Extract from MODBUS Messaging on TCP/IP Implementation | ||
| 361 | - Guide V1.0b (page 23/46): | ||
| 362 | - The transaction identifier is used to associate the future | ||
| 363 | - response with the request. */ | ||
| 364 | - rsp[0] = sft->t_id >> 8; | ||
| 365 | - rsp[1] = sft->t_id & 0x00ff; | ||
| 366 | - | ||
| 367 | - /* Protocol Modbus */ | ||
| 368 | - rsp[2] = 0; | ||
| 369 | - rsp[3] = 0; | ||
| 370 | - | ||
| 371 | - /* Length will be set later by send_msg (4 and 5) */ | ||
| 372 | - | ||
| 373 | - rsp[6] = 0xFF; | ||
| 374 | - rsp[7] = sft->function; | ||
| 375 | - | ||
| 376 | - return PRESET_RSP_LENGTH_TCP; | ||
| 377 | -} | ||
| 378 | - | ||
| 379 | -static int build_response_basis(modbus_t *ctx, sft_t *sft, uint8_t *rsp) | ||
| 380 | -{ | ||
| 381 | - if (ctx->type_com == RTU) | ||
| 382 | - return build_response_basis_rtu(sft, rsp); | ||
| 383 | - else | ||
| 384 | - return build_response_basis_tcp(sft, rsp); | ||
| 385 | -} | ||
| 386 | - | ||
| 387 | -/* Fast CRC */ | ||
| 388 | -static uint16_t crc16(uint8_t *buffer, uint16_t buffer_length) | ||
| 389 | -{ | ||
| 390 | - uint8_t crc_hi = 0xFF; /* high CRC byte initialized */ | ||
| 391 | - uint8_t crc_lo = 0xFF; /* low CRC byte initialized */ | ||
| 392 | - unsigned int i; /* will index into CRC lookup */ | ||
| 393 | - | ||
| 394 | - /* pass through message buffer */ | ||
| 395 | - while (buffer_length--) { | ||
| 396 | - i = crc_hi ^ *buffer++; /* calculate the CRC */ | ||
| 397 | - crc_hi = crc_lo ^ table_crc_hi[i]; | ||
| 398 | - crc_lo = table_crc_lo[i]; | ||
| 399 | - } | ||
| 400 | - | ||
| 401 | - return (crc_hi << 8 | crc_lo); | ||
| 402 | -} | ||
| 403 | - | ||
| 404 | -/* The check_crc16 function shall return the message length if the CRC is | ||
| 405 | - valid. Otherwise it shall return -1 and set errno to EMBADCRC. */ | ||
| 406 | -static int check_crc16(modbus_t *ctx, uint8_t *msg, const int msg_length) | ||
| 407 | -{ | ||
| 408 | - uint16_t crc_calculated; | ||
| 409 | - uint16_t crc_received; | ||
| 410 | - | ||
| 411 | - crc_calculated = crc16(msg, msg_length - 2); | ||
| 412 | - crc_received = (msg[msg_length - 2] << 8) | msg[msg_length - 1]; | ||
| 413 | - | ||
| 414 | - /* Check CRC of msg */ | ||
| 415 | - if (crc_calculated == crc_received) { | ||
| 416 | - return msg_length; | ||
| 417 | - } else { | ||
| 418 | - if (ctx->debug) { | ||
| 419 | - fprintf(stderr, "ERROR CRC received %0X != CRC calculated %0X\n", | ||
| 420 | - crc_received, crc_calculated); | ||
| 421 | - } | ||
| 422 | - if (ctx->error_recovery) { | ||
| 423 | - modbus_flush(ctx); | ||
| 424 | - } | ||
| 425 | - errno = EMBBADCRC; | ||
| 426 | - return -1; | ||
| 427 | - } | ||
| 428 | -} | ||
| 429 | - | ||
| 430 | -/* Sends a req/response over a serial or a TCP communication */ | 142 | +/* Sends a request/response */ |
| 431 | static int send_msg(modbus_t *ctx, uint8_t *req, int req_length) | 143 | static int send_msg(modbus_t *ctx, uint8_t *req, int req_length) |
| 432 | { | 144 | { |
| 433 | int rc; | 145 | int rc; |
| 434 | - uint16_t s_crc; | ||
| 435 | int i; | 146 | int i; |
| 436 | 147 | ||
| 437 | - if (ctx->type_com == RTU) { | ||
| 438 | - s_crc = crc16(req, req_length); | ||
| 439 | - req[req_length++] = s_crc >> 8; | ||
| 440 | - req[req_length++] = s_crc & 0x00FF; | ||
| 441 | - } else { | ||
| 442 | - /* Substract the header length to the message length */ | ||
| 443 | - int mbap_length = req_length - 6; | ||
| 444 | - | ||
| 445 | - req[4] = mbap_length >> 8; | ||
| 446 | - req[5] = mbap_length & 0x00FF; | ||
| 447 | - } | 148 | + req_length = ctx->backend->send_msg_pre(req, req_length); |
| 448 | 149 | ||
| 449 | if (ctx->debug) { | 150 | if (ctx->debug) { |
| 450 | for (i = 0; i < req_length; i++) | 151 | for (i = 0; i < req_length; i++) |
| @@ -455,15 +156,7 @@ static int send_msg(modbus_t *ctx, uint8_t *req, int req_length) | @@ -455,15 +156,7 @@ static int send_msg(modbus_t *ctx, uint8_t *req, int req_length) | ||
| 455 | /* In recovery mode, the write command will be issued until to be | 156 | /* In recovery mode, the write command will be issued until to be |
| 456 | successful! Disabled by default. */ | 157 | successful! Disabled by default. */ |
| 457 | do { | 158 | do { |
| 458 | - if (ctx->type_com == RTU) | ||
| 459 | - rc = write(ctx->s, req, req_length); | ||
| 460 | - else | ||
| 461 | - /* MSG_NOSIGNAL | ||
| 462 | - Requests not to send SIGPIPE on errors on stream oriented | ||
| 463 | - sockets when the other end breaks the connection. The EPIPE | ||
| 464 | - error is still returned. */ | ||
| 465 | - rc = send(ctx->s, req, req_length, MSG_NOSIGNAL); | ||
| 466 | - | 159 | + rc = ctx->backend->send(ctx->s, req, req_length); |
| 467 | if (rc == -1) { | 160 | if (rc == -1) { |
| 468 | error_print(ctx, NULL); | 161 | error_print(ctx, NULL); |
| 469 | if (ctx->error_recovery && | 162 | if (ctx->error_recovery && |
| @@ -501,20 +194,20 @@ static uint8_t compute_header_length(int function, msg_type_t msg_type) | @@ -501,20 +194,20 @@ static uint8_t compute_header_length(int function, msg_type_t msg_type) | ||
| 501 | int length; | 194 | int length; |
| 502 | 195 | ||
| 503 | if (msg_type == MSG_CONFIRMATION) { | 196 | if (msg_type == MSG_CONFIRMATION) { |
| 504 | - if (function == FC_REPORT_SLAVE_ID) { | 197 | + if (function == _FC_REPORT_SLAVE_ID) { |
| 505 | length = 1; | 198 | length = 1; |
| 506 | } else { | 199 | } else { |
| 507 | /* Should never happen, the other header lengths are precomputed */ | 200 | /* Should never happen, the other header lengths are precomputed */ |
| 508 | abort(); | 201 | abort(); |
| 509 | } | 202 | } |
| 510 | } else /* MSG_INDICATION */ { | 203 | } else /* MSG_INDICATION */ { |
| 511 | - if (function <= FC_WRITE_SINGLE_COIL || | ||
| 512 | - function == FC_WRITE_SINGLE_REGISTER) { | 204 | + if (function <= _FC_WRITE_SINGLE_COIL || |
| 205 | + function == _FC_WRITE_SINGLE_REGISTER) { | ||
| 513 | length = 4; | 206 | length = 4; |
| 514 | - } else if (function == FC_WRITE_MULTIPLE_COILS || | ||
| 515 | - function == FC_WRITE_MULTIPLE_REGISTERS) { | 207 | + } else if (function == _FC_WRITE_MULTIPLE_COILS || |
| 208 | + function == _FC_WRITE_MULTIPLE_REGISTERS) { | ||
| 516 | length = 5; | 209 | length = 5; |
| 517 | - } else if (function == FC_READ_AND_WRITE_REGISTERS) { | 210 | + } else if (function == _FC_READ_AND_WRITE_REGISTERS) { |
| 518 | length = 9; | 211 | length = 9; |
| 519 | } else { | 212 | } else { |
| 520 | length = 0; | 213 | length = 0; |
| @@ -526,63 +219,62 @@ static uint8_t compute_header_length(int function, msg_type_t msg_type) | @@ -526,63 +219,62 @@ static uint8_t compute_header_length(int function, msg_type_t msg_type) | ||
| 526 | /* Computes the length of the data to write in the request */ | 219 | /* Computes the length of the data to write in the request */ |
| 527 | static int compute_data_length(modbus_t *ctx, uint8_t *msg) | 220 | static int compute_data_length(modbus_t *ctx, uint8_t *msg) |
| 528 | { | 221 | { |
| 529 | - int function = msg[TAB_HEADER_LENGTH[ctx->type_com]]; | 222 | + int function = msg[ctx->backend->header_length]; |
| 530 | int length; | 223 | int length; |
| 531 | 224 | ||
| 532 | - if (function == FC_WRITE_MULTIPLE_COILS || | ||
| 533 | - function == FC_WRITE_MULTIPLE_REGISTERS) { | ||
| 534 | - length = msg[TAB_HEADER_LENGTH[ctx->type_com] + 5]; | ||
| 535 | - } else if (function == FC_REPORT_SLAVE_ID) { | ||
| 536 | - length = msg[TAB_HEADER_LENGTH[ctx->type_com] + 1]; | ||
| 537 | - } else if (function == FC_READ_AND_WRITE_REGISTERS) { | ||
| 538 | - length = msg[TAB_HEADER_LENGTH[ctx->type_com] + 9]; | 225 | + if (function == _FC_WRITE_MULTIPLE_COILS || |
| 226 | + function == _FC_WRITE_MULTIPLE_REGISTERS) { | ||
| 227 | + length = msg[ctx->backend->header_length + 5]; | ||
| 228 | + } else if (function == _FC_REPORT_SLAVE_ID) { | ||
| 229 | + length = msg[ctx->backend->header_length + 1]; | ||
| 230 | + } else if (function == _FC_READ_AND_WRITE_REGISTERS) { | ||
| 231 | + length = msg[ctx->backend->header_length + 9]; | ||
| 539 | } else | 232 | } else |
| 540 | length = 0; | 233 | length = 0; |
| 541 | 234 | ||
| 542 | - length += TAB_CHECKSUM_LENGTH[ctx->type_com]; | 235 | + length += ctx->backend->checksum_length; |
| 543 | 236 | ||
| 544 | return length; | 237 | return length; |
| 545 | } | 238 | } |
| 546 | 239 | ||
| 547 | #define WAIT_DATA() { \ | 240 | #define WAIT_DATA() { \ |
| 548 | - while ((s_rc = select(ctx->s+1, &rfds, NULL, NULL, &tv)) == -1) { \ | ||
| 549 | - if (errno == EINTR) { \ | ||
| 550 | - if (ctx->debug) { \ | ||
| 551 | - fprintf(stderr, \ | ||
| 552 | - "A non blocked signal was caught\n"); \ | ||
| 553 | - } \ | ||
| 554 | - /* Necessary after an error */ \ | ||
| 555 | - FD_ZERO(&rfds); \ | ||
| 556 | - FD_SET(ctx->s, &rfds); \ | 241 | + while ((s_rc = select(ctx->s+1, &rfds, NULL, NULL, &tv)) == -1) { \ |
| 242 | + if (errno == EINTR) { \ | ||
| 243 | + if (ctx->debug) { \ | ||
| 244 | + fprintf(stderr, "A non blocked signal was caught\n"); \ | ||
| 245 | + } \ | ||
| 246 | + /* Necessary after an error */ \ | ||
| 247 | + FD_ZERO(&rfds); \ | ||
| 248 | + FD_SET(ctx->s, &rfds); \ | ||
| 249 | + } else { \ | ||
| 250 | + error_print(ctx, "select"); \ | ||
| 251 | + if (ctx->error_recovery && (errno == EBADF)) { \ | ||
| 252 | + modbus_close(ctx); \ | ||
| 253 | + modbus_connect(ctx); \ | ||
| 254 | + errno = EBADF; \ | ||
| 255 | + return -1; \ | ||
| 557 | } else { \ | 256 | } else { \ |
| 558 | - error_print(ctx, "select"); \ | ||
| 559 | - if (ctx->error_recovery && (errno == EBADF)) { \ | ||
| 560 | - modbus_close(ctx); \ | ||
| 561 | - modbus_connect(ctx); \ | ||
| 562 | - errno = EBADF; \ | ||
| 563 | - return -1; \ | ||
| 564 | - } else { \ | ||
| 565 | - return -1; \ | ||
| 566 | - } \ | 257 | + return -1; \ |
| 567 | } \ | 258 | } \ |
| 568 | } \ | 259 | } \ |
| 260 | + } \ | ||
| 569 | \ | 261 | \ |
| 570 | - if (s_rc == 0) { \ | ||
| 571 | - /* Timeout */ \ | ||
| 572 | - if (msg_length == (TAB_HEADER_LENGTH[ctx->type_com] + 2 + \ | ||
| 573 | - TAB_CHECKSUM_LENGTH[ctx->type_com])) { \ | ||
| 574 | - /* Optimization allowed because exception response is \ | ||
| 575 | - the smallest trame in modbus protocol (3) so always \ | ||
| 576 | - raise a timeout error. \ | ||
| 577 | - Temporary error before exception analyze. */ \ | ||
| 578 | - errno = EMBUNKEXC; \ | ||
| 579 | - } else { \ | ||
| 580 | - errno = ETIMEDOUT; \ | ||
| 581 | - error_print(ctx, "select"); \ | ||
| 582 | - } \ | ||
| 583 | - return -1; \ | 262 | + if (s_rc == 0) { \ |
| 263 | + /* Timeout */ \ | ||
| 264 | + if (msg_length == (ctx->backend->header_length + 2 + \ | ||
| 265 | + ctx->backend->checksum_length)) { \ | ||
| 266 | + /* Optimization allowed because exception response is \ | ||
| 267 | + the smallest trame in modbus protocol (3) so always \ | ||
| 268 | + raise a timeout error. \ | ||
| 269 | + Temporary error before exception analyze. */ \ | ||
| 270 | + errno = EMBUNKEXC; \ | ||
| 271 | + } else { \ | ||
| 272 | + errno = ETIMEDOUT; \ | ||
| 273 | + error_print(ctx, "select"); \ | ||
| 584 | } \ | 274 | } \ |
| 585 | - } | 275 | + return -1; \ |
| 276 | + } \ | ||
| 277 | +} | ||
| 586 | 278 | ||
| 587 | /* Waits a response from a modbus server or a request from a modbus client. | 279 | /* Waits a response from a modbus server or a request from a modbus client. |
| 588 | This function blocks if there is no replies (3 timeouts). | 280 | This function blocks if there is no replies (3 timeouts). |
| @@ -640,7 +332,7 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed, | @@ -640,7 +332,7 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed, | ||
| 640 | * reach the function code because all packets contains this | 332 | * reach the function code because all packets contains this |
| 641 | * information. */ | 333 | * information. */ |
| 642 | state = FUNCTION; | 334 | state = FUNCTION; |
| 643 | - msg_length_computed = TAB_HEADER_LENGTH[ctx->type_com] + 1; | 335 | + msg_length_computed = ctx->backend->header_length + 1; |
| 644 | } else { | 336 | } else { |
| 645 | tv.tv_sec = ctx->timeout_begin.tv_sec; | 337 | tv.tv_sec = ctx->timeout_begin.tv_sec; |
| 646 | tv.tv_usec = ctx->timeout_begin.tv_usec; | 338 | tv.tv_usec = ctx->timeout_begin.tv_usec; |
| @@ -654,11 +346,7 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed, | @@ -654,11 +346,7 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed, | ||
| 654 | 346 | ||
| 655 | p_msg = msg; | 347 | p_msg = msg; |
| 656 | while (s_rc) { | 348 | while (s_rc) { |
| 657 | - if (ctx->type_com == RTU) | ||
| 658 | - read_rc = read(ctx->s, p_msg, length_to_read); | ||
| 659 | - else | ||
| 660 | - read_rc = recv(ctx->s, p_msg, length_to_read, 0); | ||
| 661 | - | 349 | + read_rc = ctx->backend->recv(ctx->s, p_msg, length_to_read); |
| 662 | if (read_rc == 0) { | 350 | if (read_rc == 0) { |
| 663 | errno = ECONNRESET; | 351 | errno = ECONNRESET; |
| 664 | read_rc = -1; | 352 | read_rc = -1; |
| @@ -695,7 +383,7 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed, | @@ -695,7 +383,7 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed, | ||
| 695 | case FUNCTION: | 383 | case FUNCTION: |
| 696 | /* Function code position */ | 384 | /* Function code position */ |
| 697 | length_to_read = compute_header_length( | 385 | length_to_read = compute_header_length( |
| 698 | - msg[TAB_HEADER_LENGTH[ctx->type_com]], | 386 | + msg[ctx->backend->header_length], |
| 699 | msg_type); | 387 | msg_type); |
| 700 | msg_length_computed += length_to_read; | 388 | msg_length_computed += length_to_read; |
| 701 | /* It's useless to check the value of | 389 | /* It's useless to check the value of |
| @@ -706,7 +394,7 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed, | @@ -706,7 +394,7 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed, | ||
| 706 | case DATA: | 394 | case DATA: |
| 707 | length_to_read = compute_data_length(ctx, msg); | 395 | length_to_read = compute_data_length(ctx, msg); |
| 708 | msg_length_computed += length_to_read; | 396 | msg_length_computed += length_to_read; |
| 709 | - if (msg_length_computed > TAB_MAX_ADU_LENGTH[ctx->type_com]) { | 397 | + if (msg_length_computed > ctx->backend->max_adu_length) { |
| 710 | errno = EMBBADDATA; | 398 | errno = EMBBADDATA; |
| 711 | error_print(ctx, "too many data"); | 399 | error_print(ctx, "too many data"); |
| 712 | return -1; | 400 | return -1; |
| @@ -738,14 +426,7 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed, | @@ -738,14 +426,7 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed, | ||
| 738 | if (ctx->debug) | 426 | if (ctx->debug) |
| 739 | printf("\n"); | 427 | printf("\n"); |
| 740 | 428 | ||
| 741 | - if (ctx->type_com == RTU) { | ||
| 742 | - /* Returns msg_length on success and a negative value on | ||
| 743 | - failure */ | ||
| 744 | - return check_crc16(ctx, msg, msg_length); | ||
| 745 | - } else { | ||
| 746 | - /* OK */ | ||
| 747 | - return msg_length; | ||
| 748 | - } | 429 | + return ctx->backend->check_integrity(ctx, msg, msg_length); |
| 749 | } | 430 | } |
| 750 | 431 | ||
| 751 | /* Receive the request from a modbus master, requires the socket file descriptor | 432 | /* Receive the request from a modbus master, requires the socket file descriptor |
| @@ -764,7 +445,7 @@ int modbus_receive(modbus_t *ctx, int sockfd, uint8_t *req) | @@ -764,7 +445,7 @@ int modbus_receive(modbus_t *ctx, int sockfd, uint8_t *req) | ||
| 764 | return receive_msg(ctx, MSG_LENGTH_UNDEFINED, req, MSG_INDICATION); | 445 | return receive_msg(ctx, MSG_LENGTH_UNDEFINED, req, MSG_INDICATION); |
| 765 | } | 446 | } |
| 766 | 447 | ||
| 767 | -/* Receives the response and checks values (and checksum in RTU). | 448 | +/* Receives the response and checks values. |
| 768 | 449 | ||
| 769 | The function shall return the number of values (bits or words) and the | 450 | The function shall return the number of values (bits or words) and the |
| 770 | response if successful. Otherwise, its shall return -1 and errno is set. | 451 | response if successful. Otherwise, its shall return -1 and errno is set. |
| @@ -775,7 +456,7 @@ static int receive_msg_req(modbus_t *ctx, uint8_t *req, uint8_t *rsp) | @@ -775,7 +456,7 @@ static int receive_msg_req(modbus_t *ctx, uint8_t *req, uint8_t *rsp) | ||
| 775 | { | 456 | { |
| 776 | int rc; | 457 | int rc; |
| 777 | int rsp_length_computed; | 458 | int rsp_length_computed; |
| 778 | - int offset = TAB_HEADER_LENGTH[ctx->type_com]; | 459 | + int offset = ctx->backend->header_length; |
| 779 | 460 | ||
| 780 | rsp_length_computed = compute_response_length(ctx, req); | 461 | rsp_length_computed = compute_response_length(ctx, req); |
| 781 | rc = receive_msg(ctx, rsp_length_computed, rsp, MSG_CONFIRMATION); | 462 | rc = receive_msg(ctx, rsp_length_computed, rsp, MSG_CONFIRMATION); |
| @@ -787,8 +468,8 @@ static int receive_msg_req(modbus_t *ctx, uint8_t *req, uint8_t *rsp) | @@ -787,8 +468,8 @@ static int receive_msg_req(modbus_t *ctx, uint8_t *req, uint8_t *rsp) | ||
| 787 | /* The number of values is returned if it's corresponding | 468 | /* The number of values is returned if it's corresponding |
| 788 | * to the request */ | 469 | * to the request */ |
| 789 | switch (rsp[offset]) { | 470 | switch (rsp[offset]) { |
| 790 | - case FC_READ_COILS: | ||
| 791 | - case FC_READ_DISCRETE_INPUTS: | 471 | + case _FC_READ_COILS: |
| 472 | + case _FC_READ_DISCRETE_INPUTS: | ||
| 792 | /* Read functions, 8 values in a byte (nb | 473 | /* Read functions, 8 values in a byte (nb |
| 793 | * of values in the request and byte count in | 474 | * of values in the request and byte count in |
| 794 | * the response. */ | 475 | * the response. */ |
| @@ -796,20 +477,20 @@ static int receive_msg_req(modbus_t *ctx, uint8_t *req, uint8_t *rsp) | @@ -796,20 +477,20 @@ static int receive_msg_req(modbus_t *ctx, uint8_t *req, uint8_t *rsp) | ||
| 796 | req_nb_value = (req_nb_value / 8) + ((req_nb_value % 8) ? 1 : 0); | 477 | req_nb_value = (req_nb_value / 8) + ((req_nb_value % 8) ? 1 : 0); |
| 797 | rsp_nb_value = rsp[offset + 1]; | 478 | rsp_nb_value = rsp[offset + 1]; |
| 798 | break; | 479 | break; |
| 799 | - case FC_READ_AND_WRITE_REGISTERS: | ||
| 800 | - case FC_READ_HOLDING_REGISTERS: | ||
| 801 | - case FC_READ_INPUT_REGISTERS: | 480 | + case _FC_READ_AND_WRITE_REGISTERS: |
| 481 | + case _FC_READ_HOLDING_REGISTERS: | ||
| 482 | + case _FC_READ_INPUT_REGISTERS: | ||
| 802 | /* Read functions 1 value = 2 bytes */ | 483 | /* Read functions 1 value = 2 bytes */ |
| 803 | req_nb_value = (req[offset + 3] << 8) + req[offset + 4]; | 484 | req_nb_value = (req[offset + 3] << 8) + req[offset + 4]; |
| 804 | rsp_nb_value = (rsp[offset + 1] / 2); | 485 | rsp_nb_value = (rsp[offset + 1] / 2); |
| 805 | break; | 486 | break; |
| 806 | - case FC_WRITE_MULTIPLE_COILS: | ||
| 807 | - case FC_WRITE_MULTIPLE_REGISTERS: | 487 | + case _FC_WRITE_MULTIPLE_COILS: |
| 488 | + case _FC_WRITE_MULTIPLE_REGISTERS: | ||
| 808 | /* N Write functions */ | 489 | /* N Write functions */ |
| 809 | req_nb_value = (req[offset + 3] << 8) + req[offset + 4]; | 490 | req_nb_value = (req[offset + 3] << 8) + req[offset + 4]; |
| 810 | rsp_nb_value = (rsp[offset + 3] << 8) | rsp[offset + 4]; | 491 | rsp_nb_value = (rsp[offset + 3] << 8) | rsp[offset + 4]; |
| 811 | break; | 492 | break; |
| 812 | - case FC_REPORT_SLAVE_ID: | 493 | + case _FC_REPORT_SLAVE_ID: |
| 813 | /* Report slave ID (bytes received) */ | 494 | /* Report slave ID (bytes received) */ |
| 814 | req_nb_value = rsp_nb_value = rsp[offset + 1]; | 495 | req_nb_value = rsp_nb_value = rsp[offset + 1]; |
| 815 | break; | 496 | break; |
| @@ -833,11 +514,10 @@ static int receive_msg_req(modbus_t *ctx, uint8_t *req, uint8_t *rsp) | @@ -833,11 +514,10 @@ static int receive_msg_req(modbus_t *ctx, uint8_t *req, uint8_t *rsp) | ||
| 833 | /* EXCEPTION CODE RECEIVED */ | 514 | /* EXCEPTION CODE RECEIVED */ |
| 834 | 515 | ||
| 835 | /* CRC must be checked here (not done in receive_msg) */ | 516 | /* CRC must be checked here (not done in receive_msg) */ |
| 836 | - if (ctx->type_com == RTU) { | ||
| 837 | - rc = check_crc16(ctx, rsp, EXCEPTION_RSP_LENGTH_RTU); | ||
| 838 | - if (rc == -1) | ||
| 839 | - return -1; | ||
| 840 | - } | 517 | + rc = ctx->backend->check_integrity(ctx, rsp, |
| 518 | + _MODBUS_EXCEPTION_RSP_LENGTH); | ||
| 519 | + if (rc == -1) | ||
| 520 | + return -1; | ||
| 841 | 521 | ||
| 842 | /* Check for exception response. | 522 | /* Check for exception response. |
| 843 | 0x80 + function is stored in the exception | 523 | 0x80 + function is stored in the exception |
| @@ -889,7 +569,7 @@ static int response_exception(modbus_t *ctx, sft_t *sft, | @@ -889,7 +569,7 @@ static int response_exception(modbus_t *ctx, sft_t *sft, | ||
| 889 | int rsp_length; | 569 | int rsp_length; |
| 890 | 570 | ||
| 891 | sft->function = sft->function + 0x80; | 571 | sft->function = sft->function + 0x80; |
| 892 | - rsp_length = build_response_basis(ctx, sft, rsp); | 572 | + rsp_length = ctx->backend->build_response_basis(sft, rsp); |
| 893 | 573 | ||
| 894 | /* Positive exception code */ | 574 | /* Positive exception code */ |
| 895 | rsp[rsp_length++] = exception_code; | 575 | rsp[rsp_length++] = exception_code; |
| @@ -906,7 +586,7 @@ static int response_exception(modbus_t *ctx, sft_t *sft, | @@ -906,7 +586,7 @@ static int response_exception(modbus_t *ctx, sft_t *sft, | ||
| 906 | int modbus_reply(modbus_t *ctx, const uint8_t *req, | 586 | int modbus_reply(modbus_t *ctx, const uint8_t *req, |
| 907 | int req_length, modbus_mapping_t *mb_mapping) | 587 | int req_length, modbus_mapping_t *mb_mapping) |
| 908 | { | 588 | { |
| 909 | - int offset = TAB_HEADER_LENGTH[ctx->type_com]; | 589 | + int offset = ctx->backend->header_length; |
| 910 | int slave = req[offset - 1]; | 590 | int slave = req[offset - 1]; |
| 911 | int function = req[offset]; | 591 | int function = req[offset]; |
| 912 | uint16_t address = (req[offset + 1] << 8) + req[offset + 2]; | 592 | uint16_t address = (req[offset + 1] << 8) + req[offset + 2]; |
| @@ -914,28 +594,17 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | @@ -914,28 +594,17 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | ||
| 914 | int rsp_length = 0; | 594 | int rsp_length = 0; |
| 915 | sft_t sft; | 595 | sft_t sft; |
| 916 | 596 | ||
| 917 | - /* Filter on the Modbus unit identifier (slave) in RTU mode */ | ||
| 918 | - if (ctx->type_com == RTU && | ||
| 919 | - slave != ctx->slave && slave != MODBUS_BROADCAST_ADDRESS) { | ||
| 920 | - /* Ignores the request (not for me) */ | ||
| 921 | - if (ctx->debug) { | ||
| 922 | - printf("Request for slave %d ignored (not %d)\n", | ||
| 923 | - slave, ctx->slave); | ||
| 924 | - } | 597 | + if (ctx->backend->filter_request(ctx, slave) == 1) { |
| 598 | + /* Filtered */ | ||
| 925 | return 0; | 599 | return 0; |
| 926 | } | 600 | } |
| 927 | 601 | ||
| 928 | sft.slave = slave; | 602 | sft.slave = slave; |
| 929 | sft.function = function; | 603 | sft.function = function; |
| 930 | - if (ctx->type_com == TCP) { | ||
| 931 | - sft.t_id = (req[0] << 8) + req[1]; | ||
| 932 | - } else { | ||
| 933 | - sft.t_id = 0; | ||
| 934 | - req_length -= CHECKSUM_LENGTH_RTU; | ||
| 935 | - } | 604 | + sft.t_id = ctx->backend->prepare_response_tid(req, &req_length); |
| 936 | 605 | ||
| 937 | switch (function) { | 606 | switch (function) { |
| 938 | - case FC_READ_COILS: { | 607 | + case _FC_READ_COILS: { |
| 939 | int nb = (req[offset + 3] << 8) + req[offset + 4]; | 608 | int nb = (req[offset + 3] << 8) + req[offset + 4]; |
| 940 | 609 | ||
| 941 | if ((address + nb) > mb_mapping->nb_bits) { | 610 | if ((address + nb) > mb_mapping->nb_bits) { |
| @@ -947,7 +616,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | @@ -947,7 +616,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | ||
| 947 | ctx, &sft, | 616 | ctx, &sft, |
| 948 | MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp); | 617 | MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp); |
| 949 | } else { | 618 | } else { |
| 950 | - rsp_length = build_response_basis(ctx, &sft, rsp); | 619 | + rsp_length = ctx->backend->build_response_basis(&sft, rsp); |
| 951 | rsp[rsp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0); | 620 | rsp[rsp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0); |
| 952 | rsp_length = response_io_status(address, nb, | 621 | rsp_length = response_io_status(address, nb, |
| 953 | mb_mapping->tab_bits, | 622 | mb_mapping->tab_bits, |
| @@ -955,7 +624,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | @@ -955,7 +624,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | ||
| 955 | } | 624 | } |
| 956 | } | 625 | } |
| 957 | break; | 626 | break; |
| 958 | - case FC_READ_DISCRETE_INPUTS: { | 627 | + case _FC_READ_DISCRETE_INPUTS: { |
| 959 | /* Similar to coil status (but too many arguments to use a | 628 | /* Similar to coil status (but too many arguments to use a |
| 960 | * function) */ | 629 | * function) */ |
| 961 | int nb = (req[offset + 3] << 8) + req[offset + 4]; | 630 | int nb = (req[offset + 3] << 8) + req[offset + 4]; |
| @@ -969,7 +638,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | @@ -969,7 +638,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | ||
| 969 | ctx, &sft, | 638 | ctx, &sft, |
| 970 | MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp); | 639 | MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp); |
| 971 | } else { | 640 | } else { |
| 972 | - rsp_length = build_response_basis(ctx, &sft, rsp); | 641 | + rsp_length = ctx->backend->build_response_basis(&sft, rsp); |
| 973 | rsp[rsp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0); | 642 | rsp[rsp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0); |
| 974 | rsp_length = response_io_status(address, nb, | 643 | rsp_length = response_io_status(address, nb, |
| 975 | mb_mapping->tab_input_bits, | 644 | mb_mapping->tab_input_bits, |
| @@ -977,7 +646,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | @@ -977,7 +646,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | ||
| 977 | } | 646 | } |
| 978 | } | 647 | } |
| 979 | break; | 648 | break; |
| 980 | - case FC_READ_HOLDING_REGISTERS: { | 649 | + case _FC_READ_HOLDING_REGISTERS: { |
| 981 | int nb = (req[offset + 3] << 8) + req[offset + 4]; | 650 | int nb = (req[offset + 3] << 8) + req[offset + 4]; |
| 982 | 651 | ||
| 983 | if ((address + nb) > mb_mapping->nb_registers) { | 652 | if ((address + nb) > mb_mapping->nb_registers) { |
| @@ -991,7 +660,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | @@ -991,7 +660,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | ||
| 991 | } else { | 660 | } else { |
| 992 | int i; | 661 | int i; |
| 993 | 662 | ||
| 994 | - rsp_length = build_response_basis(ctx, &sft, rsp); | 663 | + rsp_length = ctx->backend->build_response_basis(&sft, rsp); |
| 995 | rsp[rsp_length++] = nb << 1; | 664 | rsp[rsp_length++] = nb << 1; |
| 996 | for (i = address; i < address + nb; i++) { | 665 | for (i = address; i < address + nb; i++) { |
| 997 | rsp[rsp_length++] = mb_mapping->tab_registers[i] >> 8; | 666 | rsp[rsp_length++] = mb_mapping->tab_registers[i] >> 8; |
| @@ -1000,7 +669,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | @@ -1000,7 +669,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | ||
| 1000 | } | 669 | } |
| 1001 | } | 670 | } |
| 1002 | break; | 671 | break; |
| 1003 | - case FC_READ_INPUT_REGISTERS: { | 672 | + case _FC_READ_INPUT_REGISTERS: { |
| 1004 | /* Similar to holding registers (but too many arguments to use a | 673 | /* Similar to holding registers (but too many arguments to use a |
| 1005 | * function) */ | 674 | * function) */ |
| 1006 | int nb = (req[offset + 3] << 8) + req[offset + 4]; | 675 | int nb = (req[offset + 3] << 8) + req[offset + 4]; |
| @@ -1016,7 +685,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | @@ -1016,7 +685,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | ||
| 1016 | } else { | 685 | } else { |
| 1017 | int i; | 686 | int i; |
| 1018 | 687 | ||
| 1019 | - rsp_length = build_response_basis(ctx, &sft, rsp); | 688 | + rsp_length = ctx->backend->build_response_basis(&sft, rsp); |
| 1020 | rsp[rsp_length++] = nb << 1; | 689 | rsp[rsp_length++] = nb << 1; |
| 1021 | for (i = address; i < address + nb; i++) { | 690 | for (i = address; i < address + nb; i++) { |
| 1022 | rsp[rsp_length++] = mb_mapping->tab_input_registers[i] >> 8; | 691 | rsp[rsp_length++] = mb_mapping->tab_input_registers[i] >> 8; |
| @@ -1025,7 +694,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | @@ -1025,7 +694,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | ||
| 1025 | } | 694 | } |
| 1026 | } | 695 | } |
| 1027 | break; | 696 | break; |
| 1028 | - case FC_WRITE_SINGLE_COIL: | 697 | + case _FC_WRITE_SINGLE_COIL: |
| 1029 | if (address >= mb_mapping->nb_bits) { | 698 | if (address >= mb_mapping->nb_bits) { |
| 1030 | if (ctx->debug) { | 699 | if (ctx->debug) { |
| 1031 | fprintf(stderr, "Illegal data address %0X in write_bit\n", | 700 | fprintf(stderr, "Illegal data address %0X in write_bit\n", |
| @@ -1039,11 +708,6 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | @@ -1039,11 +708,6 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | ||
| 1039 | 708 | ||
| 1040 | if (data == 0xFF00 || data == 0x0) { | 709 | if (data == 0xFF00 || data == 0x0) { |
| 1041 | mb_mapping->tab_bits[address] = (data) ? ON : OFF; | 710 | mb_mapping->tab_bits[address] = (data) ? ON : OFF; |
| 1042 | - | ||
| 1043 | - /* In RTU mode, the CRC is computed and added | ||
| 1044 | - to the request by send_msg, the computed | ||
| 1045 | - CRC will be same and optimisation is | ||
| 1046 | - possible here (FIXME). */ | ||
| 1047 | memcpy(rsp, req, req_length); | 711 | memcpy(rsp, req, req_length); |
| 1048 | rsp_length = req_length; | 712 | rsp_length = req_length; |
| 1049 | } else { | 713 | } else { |
| @@ -1058,7 +722,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | @@ -1058,7 +722,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | ||
| 1058 | } | 722 | } |
| 1059 | } | 723 | } |
| 1060 | break; | 724 | break; |
| 1061 | - case FC_WRITE_SINGLE_REGISTER: | 725 | + case _FC_WRITE_SINGLE_REGISTER: |
| 1062 | if (address >= mb_mapping->nb_registers) { | 726 | if (address >= mb_mapping->nb_registers) { |
| 1063 | if (ctx->debug) { | 727 | if (ctx->debug) { |
| 1064 | fprintf(stderr, "Illegal data address %0X in write_register\n", | 728 | fprintf(stderr, "Illegal data address %0X in write_register\n", |
| @@ -1075,7 +739,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | @@ -1075,7 +739,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | ||
| 1075 | rsp_length = req_length; | 739 | rsp_length = req_length; |
| 1076 | } | 740 | } |
| 1077 | break; | 741 | break; |
| 1078 | - case FC_WRITE_MULTIPLE_COILS: { | 742 | + case _FC_WRITE_MULTIPLE_COILS: { |
| 1079 | int nb = (req[offset + 3] << 8) + req[offset + 4]; | 743 | int nb = (req[offset + 3] << 8) + req[offset + 4]; |
| 1080 | 744 | ||
| 1081 | if ((address + nb) > mb_mapping->nb_bits) { | 745 | if ((address + nb) > mb_mapping->nb_bits) { |
| @@ -1090,14 +754,14 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | @@ -1090,14 +754,14 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | ||
| 1090 | /* 6 = byte count */ | 754 | /* 6 = byte count */ |
| 1091 | modbus_set_bits_from_bytes(mb_mapping->tab_bits, address, nb, &req[offset + 6]); | 755 | modbus_set_bits_from_bytes(mb_mapping->tab_bits, address, nb, &req[offset + 6]); |
| 1092 | 756 | ||
| 1093 | - rsp_length = build_response_basis(ctx, &sft, rsp); | 757 | + rsp_length = ctx->backend->build_response_basis(&sft, rsp); |
| 1094 | /* 4 to copy the bit address (2) and the quantity of bits */ | 758 | /* 4 to copy the bit address (2) and the quantity of bits */ |
| 1095 | memcpy(rsp + rsp_length, req + rsp_length, 4); | 759 | memcpy(rsp + rsp_length, req + rsp_length, 4); |
| 1096 | rsp_length += 4; | 760 | rsp_length += 4; |
| 1097 | } | 761 | } |
| 1098 | } | 762 | } |
| 1099 | break; | 763 | break; |
| 1100 | - case FC_WRITE_MULTIPLE_REGISTERS: { | 764 | + case _FC_WRITE_MULTIPLE_REGISTERS: { |
| 1101 | int nb = (req[offset + 3] << 8) + req[offset + 4]; | 765 | int nb = (req[offset + 3] << 8) + req[offset + 4]; |
| 1102 | 766 | ||
| 1103 | if ((address + nb) > mb_mapping->nb_registers) { | 767 | if ((address + nb) > mb_mapping->nb_registers) { |
| @@ -1116,22 +780,22 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | @@ -1116,22 +780,22 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | ||
| 1116 | (req[offset + j] << 8) + req[offset + j + 1]; | 780 | (req[offset + j] << 8) + req[offset + j + 1]; |
| 1117 | } | 781 | } |
| 1118 | 782 | ||
| 1119 | - rsp_length = build_response_basis(ctx, &sft, rsp); | 783 | + rsp_length = ctx->backend->build_response_basis(&sft, rsp); |
| 1120 | /* 4 to copy the address (2) and the no. of registers */ | 784 | /* 4 to copy the address (2) and the no. of registers */ |
| 1121 | memcpy(rsp + rsp_length, req + rsp_length, 4); | 785 | memcpy(rsp + rsp_length, req + rsp_length, 4); |
| 1122 | rsp_length += 4; | 786 | rsp_length += 4; |
| 1123 | } | 787 | } |
| 1124 | } | 788 | } |
| 1125 | break; | 789 | break; |
| 1126 | - case FC_REPORT_SLAVE_ID: | ||
| 1127 | - rsp_length = build_response_basis(ctx, &sft, rsp); | 790 | + case _FC_REPORT_SLAVE_ID: |
| 791 | + rsp_length = ctx->backend->build_response_basis(&sft, rsp); | ||
| 1128 | /* 2 bytes */ | 792 | /* 2 bytes */ |
| 1129 | rsp[rsp_length++] = 2; | 793 | rsp[rsp_length++] = 2; |
| 1130 | rsp[rsp_length++] = ctx->slave; | 794 | rsp[rsp_length++] = ctx->slave; |
| 1131 | /* Slave is ON */ | 795 | /* Slave is ON */ |
| 1132 | rsp[rsp_length++] = 0xFF; | 796 | rsp[rsp_length++] = 0xFF; |
| 1133 | break; | 797 | break; |
| 1134 | - case FC_READ_EXCEPTION_STATUS: | 798 | + case _FC_READ_EXCEPTION_STATUS: |
| 1135 | if (ctx->debug) { | 799 | if (ctx->debug) { |
| 1136 | fprintf(stderr, "FIXME Not implemented\n"); | 800 | fprintf(stderr, "FIXME Not implemented\n"); |
| 1137 | } | 801 | } |
| @@ -1139,7 +803,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | @@ -1139,7 +803,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | ||
| 1139 | return -1; | 803 | return -1; |
| 1140 | break; | 804 | break; |
| 1141 | 805 | ||
| 1142 | - case FC_READ_AND_WRITE_REGISTERS: { | 806 | + case _FC_READ_AND_WRITE_REGISTERS: { |
| 1143 | int nb = (req[offset + 3] << 8) + req[offset + 4]; | 807 | int nb = (req[offset + 3] << 8) + req[offset + 4]; |
| 1144 | uint16_t address_write = (req[offset + 5] << 8) + req[offset + 6]; | 808 | uint16_t address_write = (req[offset + 5] << 8) + req[offset + 6]; |
| 1145 | int nb_write = (req[offset + 7] << 8) + req[offset + 8]; | 809 | int nb_write = (req[offset + 7] << 8) + req[offset + 8]; |
| @@ -1155,7 +819,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | @@ -1155,7 +819,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, | ||
| 1155 | MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp); | 819 | MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp); |
| 1156 | } else { | 820 | } else { |
| 1157 | int i, j; | 821 | int i, j; |
| 1158 | - rsp_length = build_response_basis(ctx, &sft, rsp); | 822 | + rsp_length = ctx->backend->build_response_basis(&sft, rsp); |
| 1159 | rsp[rsp_length++] = nb << 1; | 823 | rsp[rsp_length++] = nb << 1; |
| 1160 | 824 | ||
| 1161 | for (i = address; i < address + nb; i++) { | 825 | for (i = address; i < address + nb; i++) { |
| @@ -1189,10 +853,10 @@ static int read_io_status(modbus_t *ctx, int function, | @@ -1189,10 +853,10 @@ static int read_io_status(modbus_t *ctx, int function, | ||
| 1189 | int rc; | 853 | int rc; |
| 1190 | int req_length; | 854 | int req_length; |
| 1191 | 855 | ||
| 1192 | - uint8_t req[MIN_REQ_LENGTH]; | 856 | + uint8_t req[_MIN_REQ_LENGTH]; |
| 1193 | uint8_t rsp[MAX_MESSAGE_LENGTH]; | 857 | uint8_t rsp[MAX_MESSAGE_LENGTH]; |
| 1194 | 858 | ||
| 1195 | - req_length = build_request_basis(ctx, function, addr, nb, req); | 859 | + req_length = ctx->backend->build_request_basis(ctx, function, addr, nb, req); |
| 1196 | 860 | ||
| 1197 | rc = send_msg(ctx, req, req_length); | 861 | rc = send_msg(ctx, req, req_length); |
| 1198 | if (rc > 0) { | 862 | if (rc > 0) { |
| @@ -1205,7 +869,7 @@ static int read_io_status(modbus_t *ctx, int function, | @@ -1205,7 +869,7 @@ static int read_io_status(modbus_t *ctx, int function, | ||
| 1205 | if (rc == -1) | 869 | if (rc == -1) |
| 1206 | return -1; | 870 | return -1; |
| 1207 | 871 | ||
| 1208 | - offset = TAB_HEADER_LENGTH[ctx->type_com] + 2; | 872 | + offset = ctx->backend->header_length + 2; |
| 1209 | offset_end = offset + rc; | 873 | offset_end = offset + rc; |
| 1210 | for (i = offset; i < offset_end; i++) { | 874 | for (i = offset; i < offset_end; i++) { |
| 1211 | /* Shift reg hi_byte to temp */ | 875 | /* Shift reg hi_byte to temp */ |
| @@ -1238,7 +902,7 @@ int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *data_dest) | @@ -1238,7 +902,7 @@ int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *data_dest) | ||
| 1238 | return -1; | 902 | return -1; |
| 1239 | } | 903 | } |
| 1240 | 904 | ||
| 1241 | - rc = read_io_status(ctx, FC_READ_COILS, addr, nb, data_dest); | 905 | + rc = read_io_status(ctx, _FC_READ_COILS, addr, nb, data_dest); |
| 1242 | 906 | ||
| 1243 | if (rc == -1) | 907 | if (rc == -1) |
| 1244 | return -1; | 908 | return -1; |
| @@ -1262,7 +926,7 @@ int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *data_dest) | @@ -1262,7 +926,7 @@ int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *data_dest) | ||
| 1262 | return -1; | 926 | return -1; |
| 1263 | } | 927 | } |
| 1264 | 928 | ||
| 1265 | - rc = read_io_status(ctx, FC_READ_DISCRETE_INPUTS, addr, nb, data_dest); | 929 | + rc = read_io_status(ctx, _FC_READ_DISCRETE_INPUTS, addr, nb, data_dest); |
| 1266 | 930 | ||
| 1267 | if (rc == -1) | 931 | if (rc == -1) |
| 1268 | return -1; | 932 | return -1; |
| @@ -1276,7 +940,7 @@ static int read_registers(modbus_t *ctx, int function, int addr, int nb, | @@ -1276,7 +940,7 @@ static int read_registers(modbus_t *ctx, int function, int addr, int nb, | ||
| 1276 | { | 940 | { |
| 1277 | int rc; | 941 | int rc; |
| 1278 | int req_length; | 942 | int req_length; |
| 1279 | - uint8_t req[MIN_REQ_LENGTH]; | 943 | + uint8_t req[_MIN_REQ_LENGTH]; |
| 1280 | uint8_t rsp[MAX_MESSAGE_LENGTH]; | 944 | uint8_t rsp[MAX_MESSAGE_LENGTH]; |
| 1281 | 945 | ||
| 1282 | if (nb > MODBUS_MAX_READ_REGISTERS) { | 946 | if (nb > MODBUS_MAX_READ_REGISTERS) { |
| @@ -1289,7 +953,7 @@ static int read_registers(modbus_t *ctx, int function, int addr, int nb, | @@ -1289,7 +953,7 @@ static int read_registers(modbus_t *ctx, int function, int addr, int nb, | ||
| 1289 | return -1; | 953 | return -1; |
| 1290 | } | 954 | } |
| 1291 | 955 | ||
| 1292 | - req_length = build_request_basis(ctx, function, addr, nb, req); | 956 | + req_length = ctx->backend->build_request_basis(ctx, function, addr, nb, req); |
| 1293 | 957 | ||
| 1294 | rc = send_msg(ctx, req, req_length); | 958 | rc = send_msg(ctx, req, req_length); |
| 1295 | if (rc > 0) { | 959 | if (rc > 0) { |
| @@ -1301,7 +965,7 @@ static int read_registers(modbus_t *ctx, int function, int addr, int nb, | @@ -1301,7 +965,7 @@ static int read_registers(modbus_t *ctx, int function, int addr, int nb, | ||
| 1301 | return -1; | 965 | return -1; |
| 1302 | } | 966 | } |
| 1303 | 967 | ||
| 1304 | - offset = TAB_HEADER_LENGTH[ctx->type_com]; | 968 | + offset = ctx->backend->header_length; |
| 1305 | 969 | ||
| 1306 | for (i = 0; i < rc; i++) { | 970 | for (i = 0; i < rc; i++) { |
| 1307 | /* shift reg hi_byte to temp OR with lo_byte */ | 971 | /* shift reg hi_byte to temp OR with lo_byte */ |
| @@ -1329,7 +993,7 @@ int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *data_dest) | @@ -1329,7 +993,7 @@ int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *data_dest) | ||
| 1329 | return -1; | 993 | return -1; |
| 1330 | } | 994 | } |
| 1331 | 995 | ||
| 1332 | - status = read_registers(ctx, FC_READ_HOLDING_REGISTERS, | 996 | + status = read_registers(ctx, _FC_READ_HOLDING_REGISTERS, |
| 1333 | addr, nb, data_dest); | 997 | addr, nb, data_dest); |
| 1334 | return status; | 998 | return status; |
| 1335 | } | 999 | } |
| @@ -1348,7 +1012,7 @@ int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, | @@ -1348,7 +1012,7 @@ int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, | ||
| 1348 | return -1; | 1012 | return -1; |
| 1349 | } | 1013 | } |
| 1350 | 1014 | ||
| 1351 | - status = read_registers(ctx, FC_READ_INPUT_REGISTERS, | 1015 | + status = read_registers(ctx, _FC_READ_INPUT_REGISTERS, |
| 1352 | addr, nb, data_dest); | 1016 | addr, nb, data_dest); |
| 1353 | 1017 | ||
| 1354 | return status; | 1018 | return status; |
| @@ -1360,14 +1024,14 @@ static int write_single(modbus_t *ctx, int function, int addr, int value) | @@ -1360,14 +1024,14 @@ static int write_single(modbus_t *ctx, int function, int addr, int value) | ||
| 1360 | { | 1024 | { |
| 1361 | int rc; | 1025 | int rc; |
| 1362 | int req_length; | 1026 | int req_length; |
| 1363 | - uint8_t req[MIN_REQ_LENGTH]; | 1027 | + uint8_t req[_MIN_REQ_LENGTH]; |
| 1364 | 1028 | ||
| 1365 | - req_length = build_request_basis(ctx, function, addr, value, req); | 1029 | + req_length = ctx->backend->build_request_basis(ctx, function, addr, value, req); |
| 1366 | 1030 | ||
| 1367 | rc = send_msg(ctx, req, req_length); | 1031 | rc = send_msg(ctx, req, req_length); |
| 1368 | if (rc > 0) { | 1032 | if (rc > 0) { |
| 1369 | /* Used by write_bit and write_register */ | 1033 | /* Used by write_bit and write_register */ |
| 1370 | - uint8_t rsp[MIN_REQ_LENGTH]; | 1034 | + uint8_t rsp[_MIN_REQ_LENGTH]; |
| 1371 | rc = receive_msg_req(ctx, req, rsp); | 1035 | rc = receive_msg_req(ctx, req, rsp); |
| 1372 | } | 1036 | } |
| 1373 | 1037 | ||
| @@ -1382,7 +1046,7 @@ int modbus_write_bit(modbus_t *ctx, int addr, int state) | @@ -1382,7 +1046,7 @@ int modbus_write_bit(modbus_t *ctx, int addr, int state) | ||
| 1382 | if (state) | 1046 | if (state) |
| 1383 | state = 0xFF00; | 1047 | state = 0xFF00; |
| 1384 | 1048 | ||
| 1385 | - status = write_single(ctx, FC_WRITE_SINGLE_COIL, addr, state); | 1049 | + status = write_single(ctx, _FC_WRITE_SINGLE_COIL, addr, state); |
| 1386 | 1050 | ||
| 1387 | return status; | 1051 | return status; |
| 1388 | } | 1052 | } |
| @@ -1392,7 +1056,7 @@ int modbus_write_register(modbus_t *ctx, int addr, int value) | @@ -1392,7 +1056,7 @@ int modbus_write_register(modbus_t *ctx, int addr, int value) | ||
| 1392 | { | 1056 | { |
| 1393 | int status; | 1057 | int status; |
| 1394 | 1058 | ||
| 1395 | - status = write_single(ctx, FC_WRITE_SINGLE_REGISTER, addr, value); | 1059 | + status = write_single(ctx, _FC_WRITE_SINGLE_REGISTER, addr, value); |
| 1396 | 1060 | ||
| 1397 | return status; | 1061 | return status; |
| 1398 | } | 1062 | } |
| @@ -1418,8 +1082,8 @@ int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data_src) | @@ -1418,8 +1082,8 @@ int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data_src) | ||
| 1418 | return -1; | 1082 | return -1; |
| 1419 | } | 1083 | } |
| 1420 | 1084 | ||
| 1421 | - req_length = build_request_basis(ctx, FC_WRITE_MULTIPLE_COILS, | ||
| 1422 | - addr, nb, req); | 1085 | + req_length = ctx->backend->build_request_basis(ctx, _FC_WRITE_MULTIPLE_COILS, |
| 1086 | + addr, nb, req); | ||
| 1423 | byte_count = (nb / 8) + ((nb % 8) ? 1 : 0); | 1087 | byte_count = (nb / 8) + ((nb % 8) ? 1 : 0); |
| 1424 | req[req_length++] = byte_count; | 1088 | req[req_length++] = byte_count; |
| 1425 | 1089 | ||
| @@ -1470,8 +1134,9 @@ int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data | @@ -1470,8 +1134,9 @@ int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data | ||
| 1470 | return -1; | 1134 | return -1; |
| 1471 | } | 1135 | } |
| 1472 | 1136 | ||
| 1473 | - req_length = build_request_basis(ctx, FC_WRITE_MULTIPLE_REGISTERS, | ||
| 1474 | - addr, nb, req); | 1137 | + req_length = ctx->backend->build_request_basis(ctx, |
| 1138 | + _FC_WRITE_MULTIPLE_REGISTERS, | ||
| 1139 | + addr, nb, req); | ||
| 1475 | byte_count = nb * 2; | 1140 | byte_count = nb * 2; |
| 1476 | req[req_length++] = byte_count; | 1141 | req[req_length++] = byte_count; |
| 1477 | 1142 | ||
| @@ -1521,8 +1186,9 @@ int modbus_read_and_write_registers(modbus_t *ctx, | @@ -1521,8 +1186,9 @@ int modbus_read_and_write_registers(modbus_t *ctx, | ||
| 1521 | errno = EMBMDATA; | 1186 | errno = EMBMDATA; |
| 1522 | return -1; | 1187 | return -1; |
| 1523 | } | 1188 | } |
| 1524 | - req_length = build_request_basis(ctx, FC_READ_AND_WRITE_REGISTERS, | ||
| 1525 | - read_addr, read_nb, req); | 1189 | + req_length = ctx->backend->build_request_basis(ctx, |
| 1190 | + _FC_READ_AND_WRITE_REGISTERS, | ||
| 1191 | + read_addr, read_nb, req); | ||
| 1526 | 1192 | ||
| 1527 | req[req_length++] = write_addr >> 8; | 1193 | req[req_length++] = write_addr >> 8; |
| 1528 | req[req_length++] = write_addr & 0x00ff; | 1194 | req[req_length++] = write_addr & 0x00ff; |
| @@ -1541,7 +1207,7 @@ int modbus_read_and_write_registers(modbus_t *ctx, | @@ -1541,7 +1207,7 @@ int modbus_read_and_write_registers(modbus_t *ctx, | ||
| 1541 | int offset; | 1207 | int offset; |
| 1542 | 1208 | ||
| 1543 | rc = receive_msg_req(ctx, req, rsp); | 1209 | rc = receive_msg_req(ctx, req, rsp); |
| 1544 | - offset = TAB_HEADER_LENGTH[ctx->type_com]; | 1210 | + offset = ctx->backend->header_length; |
| 1545 | 1211 | ||
| 1546 | /* If rc is negative, the loop is jumped ! */ | 1212 | /* If rc is negative, the loop is jumped ! */ |
| 1547 | for (i = 0; i < rc; i++) { | 1213 | for (i = 0; i < rc; i++) { |
| @@ -1560,9 +1226,10 @@ int modbus_report_slave_id(modbus_t *ctx, uint8_t *data_dest) | @@ -1560,9 +1226,10 @@ int modbus_report_slave_id(modbus_t *ctx, uint8_t *data_dest) | ||
| 1560 | { | 1226 | { |
| 1561 | int rc; | 1227 | int rc; |
| 1562 | int req_length; | 1228 | int req_length; |
| 1563 | - uint8_t req[MIN_REQ_LENGTH]; | 1229 | + uint8_t req[_MIN_REQ_LENGTH]; |
| 1564 | 1230 | ||
| 1565 | - req_length = build_request_basis(ctx, FC_REPORT_SLAVE_ID, 0, 0, req); | 1231 | + req_length = ctx->backend->build_request_basis(ctx, _FC_REPORT_SLAVE_ID, |
| 1232 | + 0, 0, req); | ||
| 1566 | 1233 | ||
| 1567 | /* HACKISH, addr and count are not used */ | 1234 | /* HACKISH, addr and count are not used */ |
| 1568 | req_length -= 4; | 1235 | req_length -= 4; |
| @@ -1579,7 +1246,7 @@ int modbus_report_slave_id(modbus_t *ctx, uint8_t *data_dest) | @@ -1579,7 +1246,7 @@ int modbus_report_slave_id(modbus_t *ctx, uint8_t *data_dest) | ||
| 1579 | if (rc == -1) | 1246 | if (rc == -1) |
| 1580 | return -1; | 1247 | return -1; |
| 1581 | 1248 | ||
| 1582 | - offset = TAB_HEADER_LENGTH[ctx->type_com] + 2; | 1249 | + offset = ctx->backend->header_length + 2; |
| 1583 | 1250 | ||
| 1584 | for (i=0; i < rc; i++) { | 1251 | for (i=0; i < rc; i++) { |
| 1585 | data_dest[i] = rsp[offset + i]; | 1252 | data_dest[i] = rsp[offset + i]; |
| @@ -1589,105 +1256,22 @@ int modbus_report_slave_id(modbus_t *ctx, uint8_t *data_dest) | @@ -1589,105 +1256,22 @@ int modbus_report_slave_id(modbus_t *ctx, uint8_t *data_dest) | ||
| 1589 | return rc; | 1256 | return rc; |
| 1590 | } | 1257 | } |
| 1591 | 1258 | ||
| 1592 | -static void init_common(modbus_t *ctx) | 1259 | +void _modbus_init_common(modbus_t *ctx) |
| 1593 | { | 1260 | { |
| 1594 | ctx->timeout_begin.tv_sec = 0; | 1261 | ctx->timeout_begin.tv_sec = 0; |
| 1595 | - ctx->timeout_begin.tv_usec = TIME_OUT_BEGIN_OF_TRAME; | 1262 | + ctx->timeout_begin.tv_usec = _TIME_OUT_BEGIN_OF_TRAME; |
| 1596 | 1263 | ||
| 1597 | ctx->timeout_end.tv_sec = 0; | 1264 | ctx->timeout_end.tv_sec = 0; |
| 1598 | - ctx->timeout_end.tv_usec = TIME_OUT_END_OF_TRAME; | 1265 | + ctx->timeout_end.tv_usec = _TIME_OUT_END_OF_TRAME; |
| 1599 | 1266 | ||
| 1600 | ctx->error_recovery = FALSE; | 1267 | ctx->error_recovery = FALSE; |
| 1601 | ctx->debug = FALSE; | 1268 | ctx->debug = FALSE; |
| 1602 | } | 1269 | } |
| 1603 | 1270 | ||
| 1604 | -/* Allocate and initialize the modbus_t structure for RTU | ||
| 1605 | - - device: "/dev/ttyS0" | ||
| 1606 | - - baud: 9600, 19200, 57600, 115200, etc | ||
| 1607 | - - parity: 'N' stands for None, 'E' for Even and 'O' for odd | ||
| 1608 | - - data_bits: 5, 6, 7, 8 | ||
| 1609 | - - stop_bits: 1, 2 | ||
| 1610 | - - slave: slave number of the caller | ||
| 1611 | -*/ | ||
| 1612 | -modbus_t* modbus_new_rtu(const char *device, | ||
| 1613 | - int baud, char parity, int data_bit, | ||
| 1614 | - int stop_bit, int slave) | ||
| 1615 | -{ | ||
| 1616 | - modbus_t *ctx; | ||
| 1617 | - modbus_rtu_t *ctx_rtu; | ||
| 1618 | - | ||
| 1619 | - ctx = (modbus_t *) malloc(sizeof(modbus_t)); | ||
| 1620 | - init_common(ctx); | ||
| 1621 | - if (modbus_set_slave(ctx, slave) == -1) { | ||
| 1622 | - return NULL; | ||
| 1623 | - } | ||
| 1624 | - | ||
| 1625 | - ctx->type_com = RTU; | ||
| 1626 | - | ||
| 1627 | - ctx->com = (modbus_rtu_t *) malloc(sizeof(modbus_rtu_t)); | ||
| 1628 | - ctx_rtu = (modbus_rtu_t *)ctx->com; | ||
| 1629 | -#if defined(OpenBSD) | ||
| 1630 | - strlcpy(ctx_rtu->device, device, sizeof(ctx_rtu->device)); | ||
| 1631 | -#else | ||
| 1632 | - strcpy(ctx_rtu->device, device); | ||
| 1633 | -#endif | ||
| 1634 | - ctx_rtu->baud = baud; | ||
| 1635 | - if (parity == 'N' || parity == 'E' || parity == 'O') { | ||
| 1636 | - ctx_rtu->parity = parity; | ||
| 1637 | - } else { | ||
| 1638 | - errno = EINVAL; | ||
| 1639 | - return NULL; | ||
| 1640 | - } | ||
| 1641 | - ctx_rtu->data_bit = data_bit; | ||
| 1642 | - ctx_rtu->stop_bit = stop_bit; | ||
| 1643 | - | ||
| 1644 | - return ctx; | ||
| 1645 | -} | ||
| 1646 | - | ||
| 1647 | -/* Allocates and initializes the modbus_t structure for TCP. | ||
| 1648 | - - ip : "192.168.0.5" | ||
| 1649 | - - port : 1099 | ||
| 1650 | - | ||
| 1651 | - Set the port to MODBUS_TCP_DEFAULT_PORT to use the default one | ||
| 1652 | - (502). It's convenient to use a port number greater than or equal | ||
| 1653 | - to 1024 because it's not necessary to be root to use this port | ||
| 1654 | - number. | ||
| 1655 | -*/ | ||
| 1656 | -modbus_t* modbus_new_tcp(const char *ip, int port) | ||
| 1657 | -{ | ||
| 1658 | - modbus_t *ctx; | ||
| 1659 | - modbus_tcp_t *ctx_tcp; | ||
| 1660 | - | ||
| 1661 | - ctx = (modbus_t *) malloc(sizeof(modbus_t)); | ||
| 1662 | - init_common(ctx); | ||
| 1663 | - ctx->slave = MODBUS_TCP_SLAVE; | ||
| 1664 | - | ||
| 1665 | - ctx->type_com = TCP; | ||
| 1666 | - | ||
| 1667 | - ctx->com = (modbus_tcp_t *) malloc(sizeof(modbus_tcp_t)); | ||
| 1668 | - ctx_tcp = (modbus_tcp_t *)ctx->com; | ||
| 1669 | - | ||
| 1670 | - strncpy(ctx_tcp->ip, ip, sizeof(char)*16); | ||
| 1671 | - ctx_tcp->port = port; | ||
| 1672 | - | ||
| 1673 | - /* Can be changed after to reach remote serial Modbus device */ | ||
| 1674 | - return ctx; | ||
| 1675 | -} | ||
| 1676 | - | ||
| 1677 | -/* Define the slave number, the special value MODBUS_TCP_SLAVE (0xFF) can be | ||
| 1678 | - * used in TCP mode to restore the default value. */ | 1271 | +/* Define the slave number */ |
| 1679 | int modbus_set_slave(modbus_t *ctx, int slave) | 1272 | int modbus_set_slave(modbus_t *ctx, int slave) |
| 1680 | { | 1273 | { |
| 1681 | - if (slave >= 1 && slave <= 247) { | ||
| 1682 | - ctx->slave = slave; | ||
| 1683 | - } else if (ctx->type_com == TCP && slave == MODBUS_TCP_SLAVE) { | ||
| 1684 | - ctx->slave = slave; | ||
| 1685 | - } else { | ||
| 1686 | - errno = EINVAL; | ||
| 1687 | - return -1; | ||
| 1688 | - } | ||
| 1689 | - | ||
| 1690 | - return 0; | 1274 | + return ctx->backend->set_slave(ctx, slave); |
| 1691 | } | 1275 | } |
| 1692 | 1276 | ||
| 1693 | /* | 1277 | /* |
| @@ -1739,348 +1323,9 @@ void modbus_set_timeout_end(modbus_t *ctx, const struct timeval *timeout) | @@ -1739,348 +1323,9 @@ void modbus_set_timeout_end(modbus_t *ctx, const struct timeval *timeout) | ||
| 1739 | ctx->timeout_end = *timeout; | 1323 | ctx->timeout_end = *timeout; |
| 1740 | } | 1324 | } |
| 1741 | 1325 | ||
| 1742 | -/* Sets up a serial port for RTU communications */ | ||
| 1743 | -static int modbus_connect_rtu(modbus_t *ctx) | ||
| 1744 | -{ | ||
| 1745 | - struct termios tios; | ||
| 1746 | - speed_t speed; | ||
| 1747 | - modbus_rtu_t *ctx_rtu = ctx->com; | ||
| 1748 | - | ||
| 1749 | - if (ctx->debug) { | ||
| 1750 | - printf("Opening %s at %d bauds (%c, %d, %d)\n", | ||
| 1751 | - ctx_rtu->device, ctx_rtu->baud, ctx_rtu->parity, | ||
| 1752 | - ctx_rtu->data_bit, ctx_rtu->stop_bit); | ||
| 1753 | - } | ||
| 1754 | - | ||
| 1755 | - /* The O_NOCTTY flag tells UNIX that this program doesn't want | ||
| 1756 | - to be the "controlling terminal" for that port. If you | ||
| 1757 | - don't specify this then any input (such as keyboard abort | ||
| 1758 | - signals and so forth) will affect your process | ||
| 1759 | - | ||
| 1760 | - Timeouts are ignored in canonical input mode or when the | ||
| 1761 | - NDELAY option is set on the file via open or fcntl */ | ||
| 1762 | - ctx->s = open(ctx_rtu->device, O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL); | ||
| 1763 | - if (ctx->s == -1) { | ||
| 1764 | - fprintf(stderr, "ERROR Can't open the device %s (%s)\n", | ||
| 1765 | - ctx_rtu->device, strerror(errno)); | ||
| 1766 | - return -1; | ||
| 1767 | - } | ||
| 1768 | - | ||
| 1769 | - /* Save */ | ||
| 1770 | - tcgetattr(ctx->s, &(ctx_rtu->old_tios)); | ||
| 1771 | - | ||
| 1772 | - memset(&tios, 0, sizeof(struct termios)); | ||
| 1773 | - | ||
| 1774 | - /* C_ISPEED Input baud (new interface) | ||
| 1775 | - C_OSPEED Output baud (new interface) | ||
| 1776 | - */ | ||
| 1777 | - switch (ctx_rtu->baud) { | ||
| 1778 | - case 110: | ||
| 1779 | - speed = B110; | ||
| 1780 | - break; | ||
| 1781 | - case 300: | ||
| 1782 | - speed = B300; | ||
| 1783 | - break; | ||
| 1784 | - case 600: | ||
| 1785 | - speed = B600; | ||
| 1786 | - break; | ||
| 1787 | - case 1200: | ||
| 1788 | - speed = B1200; | ||
| 1789 | - break; | ||
| 1790 | - case 2400: | ||
| 1791 | - speed = B2400; | ||
| 1792 | - break; | ||
| 1793 | - case 4800: | ||
| 1794 | - speed = B4800; | ||
| 1795 | - break; | ||
| 1796 | - case 9600: | ||
| 1797 | - speed = B9600; | ||
| 1798 | - break; | ||
| 1799 | - case 19200: | ||
| 1800 | - speed = B19200; | ||
| 1801 | - break; | ||
| 1802 | - case 38400: | ||
| 1803 | - speed = B38400; | ||
| 1804 | - break; | ||
| 1805 | - case 57600: | ||
| 1806 | - speed = B57600; | ||
| 1807 | - break; | ||
| 1808 | - case 115200: | ||
| 1809 | - speed = B115200; | ||
| 1810 | - break; | ||
| 1811 | - default: | ||
| 1812 | - speed = B9600; | ||
| 1813 | - if (ctx->debug) { | ||
| 1814 | - fprintf(stderr, | ||
| 1815 | - "WARNING Unknown baud rate %d for %s (B9600 used)\n", | ||
| 1816 | - ctx_rtu->baud, ctx_rtu->device); | ||
| 1817 | - } | ||
| 1818 | - } | ||
| 1819 | - | ||
| 1820 | - /* Set the baud rate */ | ||
| 1821 | - if ((cfsetispeed(&tios, speed) < 0) || | ||
| 1822 | - (cfsetospeed(&tios, speed) < 0)) { | ||
| 1823 | - return -1; | ||
| 1824 | - } | ||
| 1825 | - | ||
| 1826 | - /* C_CFLAG Control options | ||
| 1827 | - CLOCAL Local line - do not change "owner" of port | ||
| 1828 | - CREAD Enable receiver | ||
| 1829 | - */ | ||
| 1830 | - tios.c_cflag |= (CREAD | CLOCAL); | ||
| 1831 | - /* CSIZE, HUPCL, CRTSCTS (hardware flow control) */ | ||
| 1832 | - | ||
| 1833 | - /* Set data bits (5, 6, 7, 8 bits) | ||
| 1834 | - CSIZE Bit mask for data bits | ||
| 1835 | - */ | ||
| 1836 | - tios.c_cflag &= ~CSIZE; | ||
| 1837 | - switch (ctx_rtu->data_bit) { | ||
| 1838 | - case 5: | ||
| 1839 | - tios.c_cflag |= CS5; | ||
| 1840 | - break; | ||
| 1841 | - case 6: | ||
| 1842 | - tios.c_cflag |= CS6; | ||
| 1843 | - break; | ||
| 1844 | - case 7: | ||
| 1845 | - tios.c_cflag |= CS7; | ||
| 1846 | - break; | ||
| 1847 | - case 8: | ||
| 1848 | - default: | ||
| 1849 | - tios.c_cflag |= CS8; | ||
| 1850 | - break; | ||
| 1851 | - } | ||
| 1852 | - | ||
| 1853 | - /* Stop bit (1 or 2) */ | ||
| 1854 | - if (ctx_rtu->stop_bit == 1) | ||
| 1855 | - tios.c_cflag &=~ CSTOPB; | ||
| 1856 | - else /* 2 */ | ||
| 1857 | - tios.c_cflag |= CSTOPB; | ||
| 1858 | - | ||
| 1859 | - /* PARENB Enable parity bit | ||
| 1860 | - PARODD Use odd parity instead of even */ | ||
| 1861 | - if (ctx_rtu->parity == 'N') { | ||
| 1862 | - /* None */ | ||
| 1863 | - tios.c_cflag &=~ PARENB; | ||
| 1864 | - } else if (ctx_rtu->parity == 'E') { | ||
| 1865 | - /* Even */ | ||
| 1866 | - tios.c_cflag |= PARENB; | ||
| 1867 | - tios.c_cflag &=~ PARODD; | ||
| 1868 | - } else { | ||
| 1869 | - /* Odd */ | ||
| 1870 | - tios.c_cflag |= PARENB; | ||
| 1871 | - tios.c_cflag |= PARODD; | ||
| 1872 | - } | ||
| 1873 | - | ||
| 1874 | - /* Read the man page of termios if you need more information. */ | ||
| 1875 | - | ||
| 1876 | - /* This field isn't used on POSIX systems | ||
| 1877 | - tios.c_line = 0; | ||
| 1878 | - */ | ||
| 1879 | - | ||
| 1880 | - /* C_LFLAG Line options | ||
| 1881 | - | ||
| 1882 | - ISIG Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals | ||
| 1883 | - ICANON Enable canonical input (else raw) | ||
| 1884 | - XCASE Map uppercase \lowercase (obsolete) | ||
| 1885 | - ECHO Enable echoing of input characters | ||
| 1886 | - ECHOE Echo erase character as BS-SP-BS | ||
| 1887 | - ECHOK Echo NL after kill character | ||
| 1888 | - ECHONL Echo NL | ||
| 1889 | - NOFLSH Disable flushing of input buffers after | ||
| 1890 | - interrupt or quit characters | ||
| 1891 | - IEXTEN Enable extended functions | ||
| 1892 | - ECHOCTL Echo control characters as ^char and delete as ~? | ||
| 1893 | - ECHOPRT Echo erased character as character erased | ||
| 1894 | - ECHOKE BS-SP-BS entire line on line kill | ||
| 1895 | - FLUSHO Output being flushed | ||
| 1896 | - PENDIN Retype pending input at next read or input char | ||
| 1897 | - TOSTOP Send SIGTTOU for background output | ||
| 1898 | - | ||
| 1899 | - Canonical input is line-oriented. Input characters are put | ||
| 1900 | - into a buffer which can be edited interactively by the user | ||
| 1901 | - until a CR (carriage return) or LF (line feed) character is | ||
| 1902 | - received. | ||
| 1903 | - | ||
| 1904 | - Raw input is unprocessed. Input characters are passed | ||
| 1905 | - through exactly as they are received, when they are | ||
| 1906 | - received. Generally you'll deselect the ICANON, ECHO, | ||
| 1907 | - ECHOE, and ISIG options when using raw input | ||
| 1908 | - */ | ||
| 1909 | - | ||
| 1910 | - /* Raw input */ | ||
| 1911 | - tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); | ||
| 1912 | - | ||
| 1913 | - /* C_IFLAG Input options | ||
| 1914 | - | ||
| 1915 | - Constant Description | ||
| 1916 | - INPCK Enable parity check | ||
| 1917 | - IGNPAR Ignore parity errors | ||
| 1918 | - PARMRK Mark parity errors | ||
| 1919 | - ISTRIP Strip parity bits | ||
| 1920 | - IXON Enable software flow control (outgoing) | ||
| 1921 | - IXOFF Enable software flow control (incoming) | ||
| 1922 | - IXANY Allow any character to start flow again | ||
| 1923 | - IGNBRK Ignore break condition | ||
| 1924 | - BRKINT Send a SIGINT when a break condition is detected | ||
| 1925 | - INLCR Map NL to CR | ||
| 1926 | - IGNCR Ignore CR | ||
| 1927 | - ICRNL Map CR to NL | ||
| 1928 | - IUCLC Map uppercase to lowercase | ||
| 1929 | - IMAXBEL Echo BEL on input line too long | ||
| 1930 | - */ | ||
| 1931 | - if (ctx_rtu->parity == 'N') { | ||
| 1932 | - /* None */ | ||
| 1933 | - tios.c_iflag &= ~INPCK; | ||
| 1934 | - } else { | ||
| 1935 | - tios.c_iflag |= INPCK; | ||
| 1936 | - } | ||
| 1937 | - | ||
| 1938 | - /* Software flow control is disabled */ | ||
| 1939 | - tios.c_iflag &= ~(IXON | IXOFF | IXANY); | ||
| 1940 | - | ||
| 1941 | - /* C_OFLAG Output options | ||
| 1942 | - OPOST Postprocess output (not set = raw output) | ||
| 1943 | - ONLCR Map NL to CR-NL | ||
| 1944 | - | ||
| 1945 | - ONCLR ant others needs OPOST to be enabled | ||
| 1946 | - */ | ||
| 1947 | - | ||
| 1948 | - /* Raw ouput */ | ||
| 1949 | - tios.c_oflag &=~ OPOST; | ||
| 1950 | - | ||
| 1951 | - /* C_CC Control characters | ||
| 1952 | - VMIN Minimum number of characters to read | ||
| 1953 | - VTIME Time to wait for data (tenths of seconds) | ||
| 1954 | - | ||
| 1955 | - UNIX serial interface drivers provide the ability to | ||
| 1956 | - specify character and packet timeouts. Two elements of the | ||
| 1957 | - c_cc array are used for timeouts: VMIN and VTIME. Timeouts | ||
| 1958 | - are ignored in canonical input mode or when the NDELAY | ||
| 1959 | - option is set on the file via open or fcntl. | ||
| 1960 | - | ||
| 1961 | - VMIN specifies the minimum number of characters to read. If | ||
| 1962 | - it is set to 0, then the VTIME value specifies the time to | ||
| 1963 | - wait for every character read. Note that this does not mean | ||
| 1964 | - that a read call for N bytes will wait for N characters to | ||
| 1965 | - come in. Rather, the timeout will apply to the first | ||
| 1966 | - character and the read call will return the number of | ||
| 1967 | - characters immediately available (up to the number you | ||
| 1968 | - request). | ||
| 1969 | - | ||
| 1970 | - If VMIN is non-zero, VTIME specifies the time to wait for | ||
| 1971 | - the first character read. If a character is read within the | ||
| 1972 | - time given, any read will block (wait) until all VMIN | ||
| 1973 | - characters are read. That is, once the first character is | ||
| 1974 | - read, the serial interface driver expects to receive an | ||
| 1975 | - entire packet of characters (VMIN bytes total). If no | ||
| 1976 | - character is read within the time allowed, then the call to | ||
| 1977 | - read returns 0. This method allows you to tell the serial | ||
| 1978 | - driver you need exactly N bytes and any read call will | ||
| 1979 | - return 0 or N bytes. However, the timeout only applies to | ||
| 1980 | - the first character read, so if for some reason the driver | ||
| 1981 | - misses one character inside the N byte packet then the read | ||
| 1982 | - call could block forever waiting for additional input | ||
| 1983 | - characters. | ||
| 1984 | - | ||
| 1985 | - VTIME specifies the amount of time to wait for incoming | ||
| 1986 | - characters in tenths of seconds. If VTIME is set to 0 (the | ||
| 1987 | - default), reads will block (wait) indefinitely unless the | ||
| 1988 | - NDELAY option is set on the port with open or fcntl. | ||
| 1989 | - */ | ||
| 1990 | - /* Unused because we use open with the NDELAY option */ | ||
| 1991 | - tios.c_cc[VMIN] = 0; | ||
| 1992 | - tios.c_cc[VTIME] = 0; | ||
| 1993 | - | ||
| 1994 | - if (tcsetattr(ctx->s, TCSANOW, &tios) < 0) { | ||
| 1995 | - return -1; | ||
| 1996 | - } | ||
| 1997 | - | ||
| 1998 | - return 0; | ||
| 1999 | -} | ||
| 2000 | - | ||
| 2001 | -/* Establishes a modbus TCP connection with a Modbus server. */ | ||
| 2002 | -static int modbus_connect_tcp(modbus_t *ctx) | ||
| 2003 | -{ | ||
| 2004 | - int rc; | ||
| 2005 | - int option; | ||
| 2006 | - struct sockaddr_in addr; | ||
| 2007 | - modbus_tcp_t *ctx_tcp = ctx->com; | ||
| 2008 | - | ||
| 2009 | - ctx->s = socket(PF_INET, SOCK_STREAM, 0); | ||
| 2010 | - if (ctx->s == -1) { | ||
| 2011 | - return -1; | ||
| 2012 | - } | ||
| 2013 | - | ||
| 2014 | - /* Set the TCP no delay flag */ | ||
| 2015 | - /* SOL_TCP = IPPROTO_TCP */ | ||
| 2016 | - option = 1; | ||
| 2017 | - rc = setsockopt(ctx->s, IPPROTO_TCP, TCP_NODELAY, | ||
| 2018 | - (const void *)&option, sizeof(int)); | ||
| 2019 | - if (rc == -1) { | ||
| 2020 | - close(ctx->s); | ||
| 2021 | - return -1; | ||
| 2022 | - } | ||
| 2023 | - | ||
| 2024 | -#if (!HAVE_DECL___CYGWIN__) | ||
| 2025 | - /** | ||
| 2026 | - * Cygwin defines IPTOS_LOWDELAY but can't handle that flag so it's | ||
| 2027 | - * necessary to workaround that problem. | ||
| 2028 | - **/ | ||
| 2029 | - /* Set the IP low delay option */ | ||
| 2030 | - option = IPTOS_LOWDELAY; | ||
| 2031 | - rc = setsockopt(ctx->s, IPPROTO_IP, IP_TOS, | ||
| 2032 | - (const void *)&option, sizeof(int)); | ||
| 2033 | - if (rc == -1) { | ||
| 2034 | - close(ctx->s); | ||
| 2035 | - return -1; | ||
| 2036 | - } | ||
| 2037 | -#endif | ||
| 2038 | - | ||
| 2039 | - if (ctx->debug) { | ||
| 2040 | - printf("Connecting to %s\n", ctx_tcp->ip); | ||
| 2041 | - } | ||
| 2042 | - | ||
| 2043 | - addr.sin_family = AF_INET; | ||
| 2044 | - addr.sin_port = htons(ctx_tcp->port); | ||
| 2045 | - addr.sin_addr.s_addr = inet_addr(ctx_tcp->ip); | ||
| 2046 | - rc = connect(ctx->s, (struct sockaddr *)&addr, | ||
| 2047 | - sizeof(struct sockaddr_in)); | ||
| 2048 | - if (rc == -1) { | ||
| 2049 | - close(ctx->s); | ||
| 2050 | - return -1; | ||
| 2051 | - } | ||
| 2052 | - | ||
| 2053 | - return 0; | ||
| 2054 | -} | ||
| 2055 | - | ||
| 2056 | -/* Establishes a modbus connection. | ||
| 2057 | - Returns 0 on success or -1 on failure. */ | ||
| 2058 | int modbus_connect(modbus_t *ctx) | 1326 | int modbus_connect(modbus_t *ctx) |
| 2059 | { | 1327 | { |
| 2060 | - int rc; | ||
| 2061 | - | ||
| 2062 | - if (ctx->type_com == RTU) | ||
| 2063 | - rc = modbus_connect_rtu(ctx); | ||
| 2064 | - else | ||
| 2065 | - rc = modbus_connect_tcp(ctx); | ||
| 2066 | - | ||
| 2067 | - return rc; | ||
| 2068 | -} | ||
| 2069 | - | ||
| 2070 | -/* Closes the file descriptor in RTU mode */ | ||
| 2071 | -static void modbus_close_rtu(modbus_t *ctx) | ||
| 2072 | -{ | ||
| 2073 | - modbus_rtu_t *ctx_rtu = ctx->com; | ||
| 2074 | - | ||
| 2075 | - tcsetattr(ctx->s, TCSANOW, &(ctx_rtu->old_tios)); | ||
| 2076 | - close(ctx->s); | ||
| 2077 | -} | ||
| 2078 | - | ||
| 2079 | -/* Closes the network connection and socket in TCP mode */ | ||
| 2080 | -static void modbus_close_tcp(modbus_t *ctx) | ||
| 2081 | -{ | ||
| 2082 | - shutdown(ctx->s, SHUT_RDWR); | ||
| 2083 | - close(ctx->s); | 1328 | + return ctx->backend->connect(ctx); |
| 2084 | } | 1329 | } |
| 2085 | 1330 | ||
| 2086 | /* Closes a connection */ | 1331 | /* Closes a connection */ |
| @@ -2089,10 +1334,7 @@ void modbus_close(modbus_t *ctx) | @@ -2089,10 +1334,7 @@ void modbus_close(modbus_t *ctx) | ||
| 2089 | if (ctx == NULL) | 1334 | if (ctx == NULL) |
| 2090 | return; | 1335 | return; |
| 2091 | 1336 | ||
| 2092 | - if (ctx->type_com == RTU) | ||
| 2093 | - modbus_close_rtu(ctx); | ||
| 2094 | - else | ||
| 2095 | - modbus_close_tcp(ctx); | 1337 | + ctx->backend->close(ctx); |
| 2096 | } | 1338 | } |
| 2097 | 1339 | ||
| 2098 | /* Free an initialized modbus_t */ | 1340 | /* Free an initialized modbus_t */ |
| @@ -2101,7 +1343,7 @@ void modbus_free(modbus_t *ctx) | @@ -2101,7 +1343,7 @@ void modbus_free(modbus_t *ctx) | ||
| 2101 | if (ctx == NULL) | 1343 | if (ctx == NULL) |
| 2102 | return; | 1344 | return; |
| 2103 | 1345 | ||
| 2104 | - free(ctx->com); | 1346 | + free(ctx->backend_data); |
| 2105 | free(ctx); | 1347 | free(ctx); |
| 2106 | } | 1348 | } |
| 2107 | 1349 | ||
| @@ -2203,146 +1445,12 @@ void modbus_mapping_free(modbus_mapping_t *mb_mapping) | @@ -2203,146 +1445,12 @@ void modbus_mapping_free(modbus_mapping_t *mb_mapping) | ||
| 2203 | free(mb_mapping); | 1445 | free(mb_mapping); |
| 2204 | } | 1446 | } |
| 2205 | 1447 | ||
| 2206 | -/* Listens for any request from one or many modbus masters in TCP */ | ||
| 2207 | int modbus_listen(modbus_t *ctx, int nb_connection) | 1448 | int modbus_listen(modbus_t *ctx, int nb_connection) |
| 2208 | { | 1449 | { |
| 2209 | - int new_socket; | ||
| 2210 | - int yes; | ||
| 2211 | - struct sockaddr_in addr; | ||
| 2212 | - modbus_tcp_t *ctx_tcp = ctx->com; | ||
| 2213 | - | ||
| 2214 | - if (ctx->type_com != TCP) { | ||
| 2215 | - if (ctx->debug) | ||
| 2216 | - fprintf(stderr, "Not implemented"); | ||
| 2217 | - errno = EINVAL; | ||
| 2218 | - return -1; | ||
| 2219 | - } | ||
| 2220 | - | ||
| 2221 | - new_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
| 2222 | - if (new_socket == -1) { | ||
| 2223 | - return -1; | ||
| 2224 | - } | ||
| 2225 | - | ||
| 2226 | - yes = 1; | ||
| 2227 | - if (setsockopt(new_socket, SOL_SOCKET, SO_REUSEADDR, | ||
| 2228 | - (char *) &yes, sizeof(yes)) == -1) { | ||
| 2229 | - close(new_socket); | ||
| 2230 | - return -1; | ||
| 2231 | - } | ||
| 2232 | - | ||
| 2233 | - memset(&addr, 0, sizeof(addr)); | ||
| 2234 | - addr.sin_family = AF_INET; | ||
| 2235 | - /* If the modbus port is < to 1024, we need the setuid root. */ | ||
| 2236 | - addr.sin_port = htons(ctx_tcp->port); | ||
| 2237 | - addr.sin_addr.s_addr = INADDR_ANY; | ||
| 2238 | - if (bind(new_socket, (struct sockaddr *)&addr, sizeof(addr)) == -1) { | ||
| 2239 | - close(new_socket); | ||
| 2240 | - return -1; | ||
| 2241 | - } | ||
| 2242 | - | ||
| 2243 | - if (listen(new_socket, nb_connection) == -1) { | ||
| 2244 | - close(new_socket); | ||
| 2245 | - return -1; | ||
| 2246 | - } | ||
| 2247 | - | ||
| 2248 | - return new_socket; | 1450 | + return ctx->backend->listen(ctx, nb_connection); |
| 2249 | } | 1451 | } |
| 2250 | 1452 | ||
| 2251 | -/* On success, the function return a non-negative integer that is a descriptor | ||
| 2252 | - for the accepted socket. On error, -1 is returned, and errno is set | ||
| 2253 | - appropriately. */ | ||
| 2254 | int modbus_accept(modbus_t *ctx, int *socket) | 1453 | int modbus_accept(modbus_t *ctx, int *socket) |
| 2255 | { | 1454 | { |
| 2256 | - struct sockaddr_in addr; | ||
| 2257 | - socklen_t addrlen; | ||
| 2258 | - | ||
| 2259 | - if (ctx->type_com != TCP) { | ||
| 2260 | - if (ctx->debug) | ||
| 2261 | - fprintf(stderr, "Not implemented"); | ||
| 2262 | - errno = EINVAL; | ||
| 2263 | - return -1; | ||
| 2264 | - } | ||
| 2265 | - | ||
| 2266 | - addrlen = sizeof(struct sockaddr_in); | ||
| 2267 | - ctx->s = accept(*socket, (struct sockaddr *)&addr, &addrlen); | ||
| 2268 | - if (ctx->s == -1) { | ||
| 2269 | - close(*socket); | ||
| 2270 | - *socket = 0; | ||
| 2271 | - return -1; | ||
| 2272 | - } | ||
| 2273 | - | ||
| 2274 | - if (ctx->debug) { | ||
| 2275 | - printf("The client %s is connected\n", inet_ntoa(addr.sin_addr)); | ||
| 2276 | - } | ||
| 2277 | - | ||
| 2278 | - return ctx->s; | ||
| 2279 | -} | ||
| 2280 | - | ||
| 2281 | -/** Utils **/ | ||
| 2282 | - | ||
| 2283 | -/* Sets many bits from a single byte value (all 8 bits of the byte value are | ||
| 2284 | - set) */ | ||
| 2285 | -void modbus_set_bits_from_byte(uint8_t *dest, int address, const uint8_t value) | ||
| 2286 | -{ | ||
| 2287 | - int i; | ||
| 2288 | - | ||
| 2289 | - for (i=0; i<8; i++) { | ||
| 2290 | - dest[address+i] = (value & (1 << i)) ? ON : OFF; | ||
| 2291 | - } | ||
| 2292 | -} | ||
| 2293 | - | ||
| 2294 | -/* Sets many bits from a table of bytes (only the bits between address and | ||
| 2295 | - address + nb_bits are set) */ | ||
| 2296 | -void modbus_set_bits_from_bytes(uint8_t *dest, int address, unsigned int nb_bits, | ||
| 2297 | - const uint8_t tab_byte[]) | ||
| 2298 | -{ | ||
| 2299 | - int i; | ||
| 2300 | - int shift = 0; | ||
| 2301 | - | ||
| 2302 | - for (i = address; i < address + nb_bits; i++) { | ||
| 2303 | - dest[i] = tab_byte[(i - address) / 8] & (1 << shift) ? ON : OFF; | ||
| 2304 | - /* gcc doesn't like: shift = (++shift) % 8; */ | ||
| 2305 | - shift++; | ||
| 2306 | - shift %= 8; | ||
| 2307 | - } | ||
| 2308 | -} | ||
| 2309 | - | ||
| 2310 | -/* Gets the byte value from many bits. | ||
| 2311 | - To obtain a full byte, set nb_bits to 8. */ | ||
| 2312 | -uint8_t modbus_get_byte_from_bits(const uint8_t *src, int address, unsigned int nb_bits) | ||
| 2313 | -{ | ||
| 2314 | - int i; | ||
| 2315 | - uint8_t value = 0; | ||
| 2316 | - | ||
| 2317 | - if (nb_bits > 8) { | ||
| 2318 | - assert(nb_bits < 8); | ||
| 2319 | - nb_bits = 8; | ||
| 2320 | - } | ||
| 2321 | - | ||
| 2322 | - for (i=0; i < nb_bits; i++) { | ||
| 2323 | - value |= (src[address+i] << i); | ||
| 2324 | - } | ||
| 2325 | - | ||
| 2326 | - return value; | ||
| 2327 | -} | ||
| 2328 | - | ||
| 2329 | -/* Get a float from 4 bytes in Modbus format */ | ||
| 2330 | -float modbus_get_float(const uint16_t *src) | ||
| 2331 | -{ | ||
| 2332 | - float r = 0.0f; | ||
| 2333 | - uint32_t i; | ||
| 2334 | - | ||
| 2335 | - i = (((uint32_t)src[1]) << 16) + src[0]; | ||
| 2336 | - memcpy(&r, &i, sizeof (r)); | ||
| 2337 | - return r; | ||
| 2338 | -} | ||
| 2339 | - | ||
| 2340 | -/* Set a float to 4 bytes in Modbus format */ | ||
| 2341 | -void modbus_set_float(float real, uint16_t *dest) | ||
| 2342 | -{ | ||
| 2343 | - uint32_t i = 0; | ||
| 2344 | - | ||
| 2345 | - memcpy(&i, &real, sizeof (i)); | ||
| 2346 | - dest[0] = (uint16_t)i; | ||
| 2347 | - dest[1] = (uint16_t)(i >> 16); | 1455 | + return ctx->backend->accept(ctx, socket); |
| 2348 | } | 1456 | } |
src/modbus.h
| @@ -22,15 +22,9 @@ | @@ -22,15 +22,9 @@ | ||
| 22 | #if (defined(__unix__) || defined(unix)) && !defined(USG) | 22 | #if (defined(__unix__) || defined(unix)) && !defined(USG) |
| 23 | #include <sys/param.h> | 23 | #include <sys/param.h> |
| 24 | #endif | 24 | #endif |
| 25 | - | ||
| 26 | -#ifdef HAVE_INTTYPES_H | ||
| 27 | -#include <inttypes.h> | ||
| 28 | -#endif | ||
| 29 | -#ifdef HAVE_STDINT_H | ||
| 30 | #include <stdint.h> | 25 | #include <stdint.h> |
| 31 | -#endif | ||
| 32 | #include <termios.h> | 26 | #include <termios.h> |
| 33 | -#if defined(OpenBSD) || (defined(__FreeBSD__ ) && __FreeBSD__ < 5) | 27 | +#if defined(OpenBSD) || (defined(__FreeBSD__) && __FreeBSD__ < 5) |
| 34 | #include <netinet/in_systm.h> | 28 | #include <netinet/in_systm.h> |
| 35 | #endif | 29 | #endif |
| 36 | #include <netinet/in.h> | 30 | #include <netinet/in.h> |
| @@ -67,16 +61,7 @@ MODBUS_BEGIN_DECLS | @@ -67,16 +61,7 @@ MODBUS_BEGIN_DECLS | ||
| 67 | #define ON 1 | 61 | #define ON 1 |
| 68 | #endif | 62 | #endif |
| 69 | 63 | ||
| 70 | -#define MODBUS_TCP_DEFAULT_PORT 502 | ||
| 71 | #define MODBUS_BROADCAST_ADDRESS 0 | 64 | #define MODBUS_BROADCAST_ADDRESS 0 |
| 72 | -#define MODBUS_TCP_SLAVE 0xFF | ||
| 73 | - | ||
| 74 | -/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5: | ||
| 75 | - * - RS232 / RS485 ADU = 253 bytes + slave (1 byte) + CRC (2 bytes) = 256 bytes | ||
| 76 | - * - TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes | ||
| 77 | - */ | ||
| 78 | -#define MODBUS_MAX_ADU_LENGTH_RTU 256 | ||
| 79 | -#define MODBUS_MAX_ADU_LENGTH_TCP 260 | ||
| 80 | 65 | ||
| 81 | /* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 1 page 12) | 66 | /* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 1 page 12) |
| 82 | * Quantity of Coils to read (2 bytes): 1 to 2000 (0x7D0) | 67 | * Quantity of Coils to read (2 bytes): 1 to 2000 (0x7D0) |
| @@ -151,12 +136,8 @@ typedef struct { | @@ -151,12 +136,8 @@ typedef struct { | ||
| 151 | uint16_t *tab_registers; | 136 | uint16_t *tab_registers; |
| 152 | } modbus_mapping_t; | 137 | } modbus_mapping_t; |
| 153 | 138 | ||
| 154 | -modbus_t* modbus_new_rtu(const char *device, int baud, char parity, int data_bit, | ||
| 155 | - int stop_bit, int slave); | ||
| 156 | int modbus_set_slave(modbus_t* ctx, int slave); | 139 | int modbus_set_slave(modbus_t* ctx, int slave); |
| 157 | 140 | ||
| 158 | -modbus_t* modbus_new_tcp(const char *ip_address, int port); | ||
| 159 | - | ||
| 160 | int modbus_set_error_recovery(modbus_t *ctx, int enabled); | 141 | int modbus_set_error_recovery(modbus_t *ctx, int enabled); |
| 161 | 142 | ||
| 162 | void modbus_get_timeout_begin(modbus_t *ctx, struct timeval *timeout); | 143 | void modbus_get_timeout_begin(modbus_t *ctx, struct timeval *timeout); |
| @@ -214,4 +195,7 @@ void modbus_set_float(float real, uint16_t *dest); | @@ -214,4 +195,7 @@ void modbus_set_float(float real, uint16_t *dest); | ||
| 214 | 195 | ||
| 215 | MODBUS_END_DECLS | 196 | MODBUS_END_DECLS |
| 216 | 197 | ||
| 198 | +#include "modbus-tcp.h" | ||
| 199 | +#include "modbus-rtu.h" | ||
| 200 | + | ||
| 217 | #endif /* _MODBUS_H_ */ | 201 | #endif /* _MODBUS_H_ */ |
tests/bandwidth-server-many-up.c
| @@ -108,7 +108,7 @@ int main(void) | @@ -108,7 +108,7 @@ int main(void) | ||
| 108 | } | 108 | } |
| 109 | } else { | 109 | } else { |
| 110 | /* An already connected master has sent a new query */ | 110 | /* An already connected master has sent a new query */ |
| 111 | - uint8_t query[MODBUS_MAX_ADU_LENGTH_TCP]; | 111 | + uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH]; |
| 112 | 112 | ||
| 113 | rc = modbus_receive(ctx, master_socket, query); | 113 | rc = modbus_receive(ctx, master_socket, query); |
| 114 | if (rc != -1) { | 114 | if (rc != -1) { |
tests/bandwidth-server-one.c
| @@ -45,7 +45,7 @@ int main(void) | @@ -45,7 +45,7 @@ int main(void) | ||
| 45 | modbus_accept(ctx, &socket); | 45 | modbus_accept(ctx, &socket); |
| 46 | 46 | ||
| 47 | for(;;) { | 47 | for(;;) { |
| 48 | - uint8_t query[MODBUS_MAX_ADU_LENGTH_TCP]; | 48 | + uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH]; |
| 49 | 49 | ||
| 50 | rc = modbus_receive(ctx, -1, query); | 50 | rc = modbus_receive(ctx, -1, query); |
| 51 | if (rc >= 0) { | 51 | if (rc >= 0) { |
tests/random-test-server.c
| @@ -43,7 +43,7 @@ int main(void) | @@ -43,7 +43,7 @@ int main(void) | ||
| 43 | modbus_accept(ctx, &socket); | 43 | modbus_accept(ctx, &socket); |
| 44 | 44 | ||
| 45 | for (;;) { | 45 | for (;;) { |
| 46 | - uint8_t query[MODBUS_MAX_ADU_LENGTH_TCP]; | 46 | + uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH]; |
| 47 | int rc; | 47 | int rc; |
| 48 | 48 | ||
| 49 | rc = modbus_receive(ctx, -1, query); | 49 | rc = modbus_receive(ctx, -1, query); |
tests/unit-test-server.c
| @@ -69,7 +69,7 @@ int main(void) | @@ -69,7 +69,7 @@ int main(void) | ||
| 69 | modbus_accept(ctx, &socket); | 69 | modbus_accept(ctx, &socket); |
| 70 | 70 | ||
| 71 | for (;;) { | 71 | for (;;) { |
| 72 | - uint8_t query[MODBUS_MAX_ADU_LENGTH_TCP]; | 72 | + uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH]; |
| 73 | 73 | ||
| 74 | rc = modbus_receive(ctx, -1, query); | 74 | rc = modbus_receive(ctx, -1, query); |
| 75 | if (rc > 0) { | 75 | if (rc > 0) { |