Browse Source

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.
Stéphane Raimbault 15 years ago
parent
commit
815d11f056

+ 9 - 1
src/Makefile.am

@@ -2,12 +2,20 @@ lib_LTLIBRARIES = libmodbus.la
 libmodbus_la_SOURCES = \
 libmodbus_la_SOURCES = \
         modbus.c \
         modbus.c \
         modbus.h \
         modbus.h \
+        modbus-data.c \
+        modbus-private.h \
+        modbus-rtu.c \
+        modbus-rtu.h \
+        modbus-rtu-private.h \
+        modbus-tcp.c \
+        modbus-tcp.h \
+        modbus-tcp-private.h \
         modbus-version.h
         modbus-version.h
 libmodbus_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBMODBUS_LT_VERSION_INFO)
 libmodbus_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBMODBUS_LT_VERSION_INFO)
 
 
 # Header files to install
 # Header files to install
 libmodbusincludedir = $(includedir)/modbus
 libmodbusincludedir = $(includedir)/modbus
-libmodbusinclude_HEADERS = modbus.h modbus-version.h
+libmodbusinclude_HEADERS = modbus.h modbus-version.h modbus-rtu.h modbus-tcp.h
 
 
 DISTCLEANFILES = modbus-version.h
 DISTCLEANFILES = modbus-version.h
 EXTRA_DIST = modbus-version.h.in
 EXTRA_DIST = modbus-version.h.in

+ 89 - 0
src/modbus-data.c

@@ -0,0 +1,89 @@
+/*
+ * Copyright © 2010 Stéphane Raimbault <stephane.raimbault@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+/* Sets many bits from a single byte value (all 8 bits of the byte value are
+   set) */
+void modbus_set_bits_from_byte(uint8_t *dest, int address, const uint8_t value)
+{
+    int i;
+
+    for (i=0; i<8; i++) {
+        dest[address+i] = (value & (1 << i)) ? 1 : 0;
+    }
+}
+
+/* Sets many bits from a table of bytes (only the bits between address and
+   address + nb_bits are set) */
+void modbus_set_bits_from_bytes(uint8_t *dest, int address, unsigned int nb_bits,
+                                const uint8_t *tab_byte)
+{
+    int i;
+    int shift = 0;
+
+    for (i = address; i < address + nb_bits; i++) {
+        dest[i] = tab_byte[(i - address) / 8] & (1 << shift) ? 1 : 0;
+        /* gcc doesn't like: shift = (++shift) % 8; */
+        shift++;
+        shift %= 8;
+    }
+}
+
+/* Gets the byte value from many bits.
+   To obtain a full byte, set nb_bits to 8. */
+uint8_t modbus_get_byte_from_bits(const uint8_t *src, int address,
+                                  unsigned int nb_bits)
+{
+    int i;
+    uint8_t value = 0;
+
+    if (nb_bits > 8) {
+        assert(nb_bits < 8);
+        nb_bits = 8;
+    }
+
+    for (i=0; i < nb_bits; i++) {
+        value |= (src[address+i] << i);
+    }
+
+    return value;
+}
+
+/* Get a float from 4 bytes in Modbus format */
+float modbus_get_float(const uint16_t *src)
+{
+    float r = 0.0f;
+    uint32_t i;
+
+    i = (((uint32_t)src[1]) << 16) + src[0];
+    memcpy(&r, &i, sizeof (r));
+    return r;
+}
+
+/* Set a float to 4 bytes in Modbus format */
+void modbus_set_float(float real, uint16_t *dest)
+{
+    uint32_t i = 0;
+
+    memcpy(&i, &real, sizeof (i));
+    dest[0] = (uint16_t)i;
+    dest[1] = (uint16_t)(i >> 16);
+}

+ 55 - 61
src/modbus-private.h

@@ -22,17 +22,6 @@
 
 
 MODBUS_BEGIN_DECLS
 MODBUS_BEGIN_DECLS
 
 
-#define HEADER_LENGTH_RTU      1
-#define PRESET_REQ_LENGTH_RTU  6
-#define PRESET_RSP_LENGTH_RTU  2
-
-#define HEADER_LENGTH_TCP      7
-#define PRESET_REQ_LENGTH_TCP 12
-#define PRESET_RSP_LENGTH_TCP  8
-
-#define CHECKSUM_LENGTH_RTU    2
-#define CHECKSUM_LENGTH_TCP    0
-
 /* It's not really the minimal length (the real one is report slave ID
 /* It's not really the minimal length (the real one is report slave ID
  * in RTU (4 bytes)) but it's a convenient size to use in RTU or TCP
  * in RTU (4 bytes)) but it's a convenient size to use in RTU or TCP
  * communications to read many values or write a single one.
  * communications to read many values or write a single one.
@@ -40,34 +29,66 @@ MODBUS_BEGIN_DECLS
  * - HEADER_LENGTH_TCP (7) + function (1) + address (2) + number (2)
  * - HEADER_LENGTH_TCP (7) + function (1) + address (2) + number (2)
  * - HEADER_LENGTH_RTU (1) + function (1) + address (2) + number (2) + CRC (2)
  * - HEADER_LENGTH_RTU (1) + function (1) + address (2) + number (2) + CRC (2)
  */
  */
-#define MIN_REQ_LENGTH           12
+#define _MIN_REQ_LENGTH           12
 
 
-#define EXCEPTION_RSP_LENGTH_RTU  5
+#define _REPORT_SLAVE_ID_LENGTH   75
 
 
-#define REPORT_SLAVE_ID_LENGTH   75
+#define _MODBUS_EXCEPTION_RSP_LENGTH  5
 
 
 /* Time out between trames in microsecond */
 /* Time out between trames in microsecond */
-#define TIME_OUT_BEGIN_OF_TRAME    500000
-#define TIME_OUT_END_OF_TRAME      500000
+#define _TIME_OUT_BEGIN_OF_TRAME    500000
+#define _TIME_OUT_END_OF_TRAME      500000
 
 
 /* Function codes */
 /* Function codes */
-#define FC_READ_COILS                0x01
-#define FC_READ_DISCRETE_INPUTS      0x02
-#define FC_READ_HOLDING_REGISTERS    0x03
-#define FC_READ_INPUT_REGISTERS      0x04
-#define FC_WRITE_SINGLE_COIL         0x05
-#define FC_WRITE_SINGLE_REGISTER     0x06
-#define FC_READ_EXCEPTION_STATUS     0x07
-#define FC_WRITE_MULTIPLE_COILS      0x0F
-#define FC_WRITE_MULTIPLE_REGISTERS  0x10
-#define FC_REPORT_SLAVE_ID           0x11
-#define FC_READ_AND_WRITE_REGISTERS  0x17
-
-typedef enum { RTU=0, TCP } type_com_t;
+#define _FC_READ_COILS                0x01
+#define _FC_READ_DISCRETE_INPUTS      0x02
+#define _FC_READ_HOLDING_REGISTERS    0x03
+#define _FC_READ_INPUT_REGISTERS      0x04
+#define _FC_WRITE_SINGLE_COIL         0x05
+#define _FC_WRITE_SINGLE_REGISTER     0x06
+#define _FC_READ_EXCEPTION_STATUS     0x07
+#define _FC_WRITE_MULTIPLE_COILS      0x0F
+#define _FC_WRITE_MULTIPLE_REGISTERS  0x10
+#define _FC_REPORT_SLAVE_ID           0x11
+#define _FC_READ_AND_WRITE_REGISTERS  0x17
+
+typedef enum {
+    _MODBUS_BACKEND_TYPE_RTU=0,
+    _MODBUS_BACKEND_TYPE_TCP
+} modbus_bakend_type_t;
+
+/* This structure reduces the number of params in functions and so
+ * optimizes the speed of execution (~ 37%). */
+typedef struct _sft {
+    int slave;
+    int function;
+    int t_id;
+} sft_t;
+
+typedef struct _modbus_backend {
+    unsigned int backend_type;
+    unsigned int header_length;
+    unsigned int checksum_length;
+    unsigned int max_adu_length;
+    int (*set_slave) (modbus_t *ctx, int slave);
+    int (*build_request_basis) (modbus_t *ctx, int function, int addr,
+                                int nb, uint8_t *req);
+    int (*build_response_basis) (sft_t *sft, uint8_t *rsp);
+    int (*prepare_response_tid) (const uint8_t *req, int *req_length);
+    int (*send_msg_pre) (uint8_t *req, int req_length);
+    ssize_t (*send) (int s, const uint8_t *req, int req_length);
+    ssize_t (*recv) (int s, uint8_t *rsp, int rsp_length);
+    int (*check_integrity) (modbus_t *ctx, uint8_t *msg,
+                            const int msg_length);
+    int (*connect) (modbus_t *ctx);
+    void (*close) (modbus_t *ctx);
+    int (*flush) (modbus_t *ctx);
+    int (*listen) (modbus_t *ctx, int nb_connection);
+    int (*accept) (modbus_t *ctx, int *socket);
+    int (*filter_request) (modbus_t *ctx, int slave);
+} modbus_backend_t;
 
 
 struct _modbus {
 struct _modbus {
-    /* Communication mode: RTU or TCP */
-    type_com_t type_com;
     /* Slave address */
     /* Slave address */
     int slave;
     int slave;
     /* Socket or file descriptor */
     /* Socket or file descriptor */
@@ -76,38 +97,11 @@ struct _modbus {
     int error_recovery;
     int error_recovery;
     struct timeval timeout_begin;
     struct timeval timeout_begin;
     struct timeval timeout_end;
     struct timeval timeout_end;
-    void *com;
+    const modbus_backend_t *backend;
+    void *backend_data;
 };
 };
 
 
-typedef struct _modbus_rtu {
-    /* Device: "/dev/ttyS0", "/dev/ttyUSB0" or "/dev/tty.USA19*" on Mac OS X for
-       KeySpan USB<->Serial adapters this string had to be made bigger on OS X
-       as the directory+file name was bigger than 19 bytes. Making it 67 bytes
-       for now, but OS X does support 256 byte file names. May become a problem
-       in the future. */
-#ifdef __APPLE_CC__
-    char device[64];
-#else
-    char device[16];
-#endif
-    /* Bauds: 9600, 19200, 57600, 115200, etc */
-    int baud;
-    /* Data bit */
-    uint8_t data_bit;
-    /* Stop bit */
-    uint8_t stop_bit;
-    /* Parity: 'N', 'O', 'E' */
-    char parity;
-    /* Save old termios settings */
-    struct termios old_tios;
-} modbus_rtu_t;
-
-typedef struct _modbus_tcp {
-    /* TCP port */
-    int port;
-    /* IP address */
-    char ip[16];
-} modbus_tcp_t;
+void _modbus_init_common(modbus_t *ctx);
 
 
 MODBUS_END_DECLS
 MODBUS_END_DECLS
 
 

+ 50 - 0
src/modbus-rtu-private.h

@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2001-2010 Stéphane Raimbault <stephane.raimbault@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MODBUS_RTU_PRIVATE_H_
+#define _MODBUS_RTU_PRIVATE_H_
+
+#define _MODBUS_RTU_HEADER_LENGTH      1
+#define _MODBUS_RTU_PRESET_REQ_LENGTH  6
+#define _MODBUS_RTU_PRESET_RSP_LENGTH  2
+
+#define _MODBUS_RTU_CHECKSUM_LENGTH    2
+
+typedef struct _modbus_rtu {
+    /* Device: "/dev/ttyS0", "/dev/ttyUSB0" or "/dev/tty.USA19*" on Mac OS X for
+       KeySpan USB<->Serial adapters this string had to be made bigger on OS X
+       as the directory+file name was bigger than 19 bytes. Making it 67 bytes
+       for now, but OS X does support 256 byte file names. May become a problem
+       in the future. */
+#ifdef __APPLE_CC__
+    char device[64];
+#else
+    char device[16];
+#endif
+    /* Bauds: 9600, 19200, 57600, 115200, etc */
+    int baud;
+    /* Data bit */
+    uint8_t data_bit;
+    /* Stop bit */
+    uint8_t stop_bit;
+    /* Parity: 'N', 'O', 'E' */
+    char parity;
+    /* Save old termios settings */
+    struct termios old_tios;
+} modbus_rtu_t;
+
+#endif /* _MODBUS_RTU_PRIVATE_H_ */

+ 566 - 0
src/modbus-rtu.c

@@ -0,0 +1,566 @@
+/*
+ * Copyright © 2001-2010 Stéphane Raimbault <stephane.raimbault@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "modbus.h"
+#include "modbus-private.h"
+
+#include "modbus-rtu.h"
+#include "modbus-rtu-private.h"
+
+/* Table of CRC values for high-order byte */
+static const uint8_t table_crc_hi[] = {
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
+    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
+    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
+    0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
+    0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
+    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
+    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
+    0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
+    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
+    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
+    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
+    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
+    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
+    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
+    0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
+    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
+};
+
+/* Table of CRC values for low-order byte */
+static const uint8_t table_crc_lo[] = {
+    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
+    0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
+    0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
+    0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
+    0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
+    0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
+    0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
+    0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
+    0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
+    0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
+    0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
+    0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
+    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
+    0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
+    0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
+    0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
+    0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
+    0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
+    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
+    0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
+    0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
+    0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
+    0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
+    0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
+    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
+    0x43, 0x83, 0x41, 0x81, 0x80, 0x40
+};
+
+static int _modbus_set_slave(modbus_t *ctx, int slave)
+{
+    if (slave >= 1 && slave <= 247) {
+        ctx->slave = slave;
+    } else {
+        errno = EINVAL;
+        return -1;
+    }
+
+    return 0;
+}
+
+/* Builds a RTU request header */
+static int _modbus_rtu_build_request_basis(modbus_t *ctx, int function,
+                                           int addr, int nb,
+                                           uint8_t *req)
+{
+    req[0] = ctx->slave;
+    req[1] = function;
+    req[2] = addr >> 8;
+    req[3] = addr & 0x00ff;
+    req[4] = nb >> 8;
+    req[5] = nb & 0x00ff;
+
+    return _MODBUS_RTU_PRESET_REQ_LENGTH;
+}
+
+/* Builds a RTU response header */
+static int _modbus_rtu_build_response_basis(sft_t *sft, uint8_t *rsp)
+{
+    rsp[0] = sft->slave;
+    rsp[1] = sft->function;
+
+    return _MODBUS_RTU_PRESET_RSP_LENGTH;
+}
+
+static uint16_t crc16(uint8_t *buffer, uint16_t buffer_length)
+{
+    uint8_t crc_hi = 0xFF; /* high CRC byte initialized */
+    uint8_t crc_lo = 0xFF; /* low CRC byte initialized */
+    unsigned int i; /* will index into CRC lookup */
+
+    /* pass through message buffer */
+    while (buffer_length--) {
+        i = crc_hi ^ *buffer++; /* calculate the CRC  */
+        crc_hi = crc_lo ^ table_crc_hi[i];
+        crc_lo = table_crc_lo[i];
+    }
+
+    return (crc_hi << 8 | crc_lo);
+}
+
+int _modbus_rtu_prepare_response_tid(const uint8_t *req, int *req_length)
+{
+    (*req_length) -= _MODBUS_RTU_CHECKSUM_LENGTH;
+    /* No TID */
+    return 0;
+}
+
+int _modbus_rtu_send_msg_pre(uint8_t *req, int req_length)
+{
+    uint16_t crc = crc16(req, req_length);
+    req[req_length++] = crc >> 8;
+    req[req_length++] = crc & 0x00FF;
+
+    return req_length;
+}
+
+ssize_t _modbus_rtu_send(int s, const uint8_t *req, int req_length)
+{
+    return write(s, req, req_length);
+}
+
+ssize_t _modbus_rtu_recv(int s, uint8_t *rsp, int rsp_length)
+{
+    return read(s, rsp, rsp_length);
+}
+
+/* The check_crc16 function shall return the message length if the CRC is
+   valid. Otherwise it shall return -1 and set errno to EMBADCRC. */
+int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg,
+                                const int msg_length)
+{
+    uint16_t crc_calculated;
+    uint16_t crc_received;
+
+    crc_calculated = crc16(msg, msg_length - 2);
+    crc_received = (msg[msg_length - 2] << 8) | msg[msg_length - 1];
+
+    /* Check CRC of msg */
+    if (crc_calculated == crc_received) {
+        return msg_length;
+    } else {
+        if (ctx->debug) {
+            fprintf(stderr, "ERROR CRC received %0X != CRC calculated %0X\n",
+                    crc_received, crc_calculated);
+        }
+        if (ctx->error_recovery) {
+            _modbus_rtu_flush(ctx);
+        }
+        errno = EMBBADCRC;
+        return -1;
+    }
+}
+
+/* Sets up a serial port for RTU communications */
+static int _modbus_rtu_connect(modbus_t *ctx)
+{
+    struct termios tios;
+    speed_t speed;
+    modbus_rtu_t *ctx_rtu = ctx->backend_data;
+
+    if (ctx->debug) {
+        printf("Opening %s at %d bauds (%c, %d, %d)\n",
+               ctx_rtu->device, ctx_rtu->baud, ctx_rtu->parity,
+               ctx_rtu->data_bit, ctx_rtu->stop_bit);
+    }
+
+    /* The O_NOCTTY flag tells UNIX that this program doesn't want
+       to be the "controlling terminal" for that port. If you
+       don't specify this then any input (such as keyboard abort
+       signals and so forth) will affect your process
+
+       Timeouts are ignored in canonical input mode or when the
+       NDELAY option is set on the file via open or fcntl */
+    ctx->s = open(ctx_rtu->device, O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL);
+    if (ctx->s == -1) {
+        fprintf(stderr, "ERROR Can't open the device %s (%s)\n",
+                ctx_rtu->device, strerror(errno));
+        return -1;
+    }
+
+    /* Save */
+    tcgetattr(ctx->s, &(ctx_rtu->old_tios));
+
+    memset(&tios, 0, sizeof(struct termios));
+
+    /* C_ISPEED     Input baud (new interface)
+       C_OSPEED     Output baud (new interface)
+    */
+    switch (ctx_rtu->baud) {
+    case 110:
+        speed = B110;
+        break;
+    case 300:
+        speed = B300;
+        break;
+    case 600:
+        speed = B600;
+        break;
+    case 1200:
+        speed = B1200;
+        break;
+    case 2400:
+        speed = B2400;
+        break;
+    case 4800:
+        speed = B4800;
+        break;
+    case 9600:
+        speed = B9600;
+        break;
+    case 19200:
+        speed = B19200;
+        break;
+    case 38400:
+        speed = B38400;
+        break;
+    case 57600:
+        speed = B57600;
+        break;
+    case 115200:
+        speed = B115200;
+        break;
+    default:
+        speed = B9600;
+        if (ctx->debug) {
+            fprintf(stderr,
+                    "WARNING Unknown baud rate %d for %s (B9600 used)\n",
+                    ctx_rtu->baud, ctx_rtu->device);
+        }
+    }
+
+    /* Set the baud rate */
+    if ((cfsetispeed(&tios, speed) < 0) ||
+        (cfsetospeed(&tios, speed) < 0)) {
+        return -1;
+    }
+
+    /* C_CFLAG      Control options
+       CLOCAL       Local line - do not change "owner" of port
+       CREAD        Enable receiver
+    */
+    tios.c_cflag |= (CREAD | CLOCAL);
+    /* CSIZE, HUPCL, CRTSCTS (hardware flow control) */
+
+    /* Set data bits (5, 6, 7, 8 bits)
+       CSIZE        Bit mask for data bits
+    */
+    tios.c_cflag &= ~CSIZE;
+    switch (ctx_rtu->data_bit) {
+    case 5:
+        tios.c_cflag |= CS5;
+        break;
+    case 6:
+        tios.c_cflag |= CS6;
+        break;
+    case 7:
+        tios.c_cflag |= CS7;
+        break;
+    case 8:
+    default:
+        tios.c_cflag |= CS8;
+        break;
+    }
+
+    /* Stop bit (1 or 2) */
+    if (ctx_rtu->stop_bit == 1)
+        tios.c_cflag &=~ CSTOPB;
+    else /* 2 */
+        tios.c_cflag |= CSTOPB;
+
+    /* PARENB       Enable parity bit
+       PARODD       Use odd parity instead of even */
+    if (ctx_rtu->parity == 'N') {
+        /* None */
+        tios.c_cflag &=~ PARENB;
+    } else if (ctx_rtu->parity == 'E') {
+        /* Even */
+        tios.c_cflag |= PARENB;
+        tios.c_cflag &=~ PARODD;
+    } else {
+        /* Odd */
+        tios.c_cflag |= PARENB;
+        tios.c_cflag |= PARODD;
+    }
+
+    /* Read the man page of termios if you need more information. */
+
+    /* This field isn't used on POSIX systems
+       tios.c_line = 0;
+    */
+
+    /* C_LFLAG      Line options
+
+       ISIG Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals
+       ICANON       Enable canonical input (else raw)
+       XCASE        Map uppercase \lowercase (obsolete)
+       ECHO Enable echoing of input characters
+       ECHOE        Echo erase character as BS-SP-BS
+       ECHOK        Echo NL after kill character
+       ECHONL       Echo NL
+       NOFLSH       Disable flushing of input buffers after
+       interrupt or quit characters
+       IEXTEN       Enable extended functions
+       ECHOCTL      Echo control characters as ^char and delete as ~?
+       ECHOPRT      Echo erased character as character erased
+       ECHOKE       BS-SP-BS entire line on line kill
+       FLUSHO       Output being flushed
+       PENDIN       Retype pending input at next read or input char
+       TOSTOP       Send SIGTTOU for background output
+
+       Canonical input is line-oriented. Input characters are put
+       into a buffer which can be edited interactively by the user
+       until a CR (carriage return) or LF (line feed) character is
+       received.
+
+       Raw input is unprocessed. Input characters are passed
+       through exactly as they are received, when they are
+       received. Generally you'll deselect the ICANON, ECHO,
+       ECHOE, and ISIG options when using raw input
+    */
+
+    /* Raw input */
+    tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+
+    /* C_IFLAG      Input options
+
+       Constant     Description
+       INPCK        Enable parity check
+       IGNPAR       Ignore parity errors
+       PARMRK       Mark parity errors
+       ISTRIP       Strip parity bits
+       IXON Enable software flow control (outgoing)
+       IXOFF        Enable software flow control (incoming)
+       IXANY        Allow any character to start flow again
+       IGNBRK       Ignore break condition
+       BRKINT       Send a SIGINT when a break condition is detected
+       INLCR        Map NL to CR
+       IGNCR        Ignore CR
+       ICRNL        Map CR to NL
+       IUCLC        Map uppercase to lowercase
+       IMAXBEL      Echo BEL on input line too long
+    */
+    if (ctx_rtu->parity == 'N') {
+        /* None */
+        tios.c_iflag &= ~INPCK;
+    } else {
+        tios.c_iflag |= INPCK;
+    }
+
+    /* Software flow control is disabled */
+    tios.c_iflag &= ~(IXON | IXOFF | IXANY);
+
+    /* C_OFLAG      Output options
+       OPOST        Postprocess output (not set = raw output)
+       ONLCR        Map NL to CR-NL
+
+       ONCLR ant others needs OPOST to be enabled
+    */
+
+    /* Raw ouput */
+    tios.c_oflag &=~ OPOST;
+
+    /* C_CC         Control characters
+       VMIN         Minimum number of characters to read
+       VTIME        Time to wait for data (tenths of seconds)
+
+       UNIX serial interface drivers provide the ability to
+       specify character and packet timeouts. Two elements of the
+       c_cc array are used for timeouts: VMIN and VTIME. Timeouts
+       are ignored in canonical input mode or when the NDELAY
+       option is set on the file via open or fcntl.
+
+       VMIN specifies the minimum number of characters to read. If
+       it is set to 0, then the VTIME value specifies the time to
+       wait for every character read. Note that this does not mean
+       that a read call for N bytes will wait for N characters to
+       come in. Rather, the timeout will apply to the first
+       character and the read call will return the number of
+       characters immediately available (up to the number you
+       request).
+
+       If VMIN is non-zero, VTIME specifies the time to wait for
+       the first character read. If a character is read within the
+       time given, any read will block (wait) until all VMIN
+       characters are read. That is, once the first character is
+       read, the serial interface driver expects to receive an
+       entire packet of characters (VMIN bytes total). If no
+       character is read within the time allowed, then the call to
+       read returns 0. This method allows you to tell the serial
+       driver you need exactly N bytes and any read call will
+       return 0 or N bytes. However, the timeout only applies to
+       the first character read, so if for some reason the driver
+       misses one character inside the N byte packet then the read
+       call could block forever waiting for additional input
+       characters.
+
+       VTIME specifies the amount of time to wait for incoming
+       characters in tenths of seconds. If VTIME is set to 0 (the
+       default), reads will block (wait) indefinitely unless the
+       NDELAY option is set on the port with open or fcntl.
+    */
+    /* Unused because we use open with the NDELAY option */
+    tios.c_cc[VMIN] = 0;
+    tios.c_cc[VTIME] = 0;
+
+    if (tcsetattr(ctx->s, TCSANOW, &tios) < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+void _modbus_rtu_close(modbus_t *ctx)
+{
+    /* Closes the file descriptor in RTU mode */
+    modbus_rtu_t *ctx_rtu = ctx->backend_data;
+
+    tcsetattr(ctx->s, TCSANOW, &(ctx_rtu->old_tios));
+    close(ctx->s);
+}
+
+int _modbus_rtu_flush(modbus_t *ctx)
+{
+    return tcflush(ctx->s, TCIOFLUSH);
+}
+
+int _modbus_rtu_listen(modbus_t *ctx, int nb_connection)
+{
+    if (ctx->debug) {
+        fprintf(stderr, "Not implemented");
+    }
+
+    errno = EINVAL;
+    return -1;
+}
+
+int _modbus_rtu_accept(modbus_t *ctx, int *socket)
+{
+    if (ctx->debug) {
+        fprintf(stderr, "Not implemented");
+    }
+
+    errno = EINVAL;
+    return -1;
+}
+
+int _modbus_rtu_filter_request(modbus_t *ctx, int slave)
+{
+    /* Filter on the Modbus unit identifier (slave) in RTU mode */
+    if (slave != ctx->slave && slave != MODBUS_BROADCAST_ADDRESS) {
+        /* Ignores the request (not for me) */
+        if (ctx->debug) {
+            printf("Request for slave %d ignored (not %d)\n",
+                   slave, ctx->slave);
+        }
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+const modbus_backend_t _modbus_rtu_backend = {
+    _MODBUS_BACKEND_TYPE_RTU,
+    _MODBUS_RTU_HEADER_LENGTH,
+    _MODBUS_RTU_CHECKSUM_LENGTH,
+    MODBUS_RTU_MAX_ADU_LENGTH,
+    _modbus_set_slave,
+    _modbus_rtu_build_request_basis,
+    _modbus_rtu_build_response_basis,
+    _modbus_rtu_prepare_response_tid,
+    _modbus_rtu_send_msg_pre,
+    _modbus_rtu_send,
+    _modbus_rtu_recv,
+    _modbus_rtu_check_integrity,
+    _modbus_rtu_connect,
+    _modbus_rtu_close,
+    _modbus_rtu_flush,
+    _modbus_rtu_listen,
+    _modbus_rtu_accept,
+    _modbus_rtu_filter_request
+};
+
+/* Allocate and initialize the modbus_t structure for RTU
+   - device: "/dev/ttyS0"
+   - baud:   9600, 19200, 57600, 115200, etc
+   - parity: 'N' stands for None, 'E' for Even and 'O' for odd
+   - data_bits: 5, 6, 7, 8
+   - stop_bits: 1, 2
+   - slave: slave number of the caller
+*/
+modbus_t* modbus_new_rtu(const char *device,
+                         int baud, char parity, int data_bit,
+                         int stop_bit, int slave)
+{
+    modbus_t *ctx;
+    modbus_rtu_t *ctx_rtu;
+
+    ctx = (modbus_t *) malloc(sizeof(modbus_t));
+    _modbus_init_common(ctx);
+    if (modbus_set_slave(ctx, slave) == -1) {
+        return NULL;
+    }
+
+    ctx->backend = &_modbus_rtu_backend;
+
+    ctx->backend_data = (modbus_rtu_t *) malloc(sizeof(modbus_rtu_t));
+    ctx_rtu = (modbus_rtu_t *)ctx->backend_data;
+#if defined(OpenBSD)
+    strlcpy(ctx_rtu->device, device, sizeof(ctx_rtu->device));
+#else
+    strcpy(ctx_rtu->device, device);
+#endif
+    ctx_rtu->baud = baud;
+    if (parity == 'N' || parity == 'E' || parity == 'O') {
+        ctx_rtu->parity = parity;
+    } else {
+        errno = EINVAL;
+        return NULL;
+    }
+    ctx_rtu->data_bit = data_bit;
+    ctx_rtu->stop_bit = stop_bit;
+
+    return ctx;
+}

+ 29 - 0
src/modbus-rtu.h

@@ -0,0 +1,29 @@
+/*
+ * Copyright © 2001-2010 Stéphane Raimbault <stephane.raimbault@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MODBUS_RTU_H_
+#define _MODBUS_RTU_H_
+
+/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5
+ * RS232 / RS485 ADU = 253 bytes + slave (1 byte) + CRC (2 bytes) = 256 bytes
+ */
+#define MODBUS_RTU_MAX_ADU_LENGTH  256
+
+modbus_t* modbus_new_rtu(const char *device, int baud, char parity,
+                         int data_bit, int stop_bit, int slave);
+
+#endif /* _MODBUS_RTU_H_ */

+ 34 - 0
src/modbus-tcp-private.h

@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2001-2010 Stéphane Raimbault <stephane.raimbault@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MODBUS_TCP_PRIVATE_H_
+#define _MODBUS_TCP_PRIVATE_H_
+
+#define _MODBUS_TCP_HEADER_LENGTH      7
+#define _MODBUS_TCP_PRESET_REQ_LENGTH 12
+#define _MODBUS_TCP_PRESET_RSP_LENGTH  8
+
+#define _MODBUS_TCP_CHECKSUM_LENGTH    0
+
+typedef struct _modbus_tcp {
+    /* TCP port */
+    int port;
+    /* IP address */
+    char ip[16];
+} modbus_tcp_t;
+
+#endif /* _MODBUS_TCP_PRIVATE_H_ */

+ 363 - 0
src/modbus-tcp.c

@@ -0,0 +1,363 @@
+/*
+ * Copyright © 2001-2010 Stéphane Raimbault <stephane.raimbault@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#if (defined OpenBSD) || (defined(__FreeBSD__ ) && __FreeBSD__ < 5)
+#include <netinet/in_systm.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include "modbus.h"
+#include "modbus-private.h"
+
+#include "modbus-tcp.h"
+#include "modbus-tcp-private.h"
+
+static int _modbus_set_slave(modbus_t *ctx, int slave)
+{
+    if (slave >= 1 && slave <= 247) {
+        ctx->slave = slave;
+    } else if (slave == MODBUS_TCP_SLAVE) {
+        /* The special value MODBUS_TCP_SLAVE (0xFF) can be used in TCP mode to
+         * restore the default value. */
+        ctx->slave = slave;
+    } else {
+        errno = EINVAL;
+        return -1;
+    }
+
+    return 0;
+}
+
+/* Builds a TCP request header */
+int _modbus_tcp_build_request_basis(modbus_t *ctx, int function,
+                                    int addr, int nb,
+                                    uint8_t *req)
+{
+
+    /* Extract from MODBUS Messaging on TCP/IP Implementation Guide V1.0b
+       (page 23/46):
+       The transaction identifier is used to associate the future response
+       with the request. So, at a time, on a TCP connection, this identifier
+       must be unique. */
+    static uint16_t t_id = 0;
+
+    /* Transaction ID */
+    if (t_id < UINT16_MAX)
+        t_id++;
+    else
+        t_id = 0;
+    req[0] = t_id >> 8;
+    req[1] = t_id & 0x00ff;
+
+    /* Protocol Modbus */
+    req[2] = 0;
+    req[3] = 0;
+
+    /* Length will be defined later by set_req_length_tcp at offsets 4
+       and 5 */
+
+    req[6] = ctx->slave;
+    req[7] = function;
+    req[8] = addr >> 8;
+    req[9] = addr & 0x00ff;
+    req[10] = nb >> 8;
+    req[11] = nb & 0x00ff;
+
+    return _MODBUS_TCP_PRESET_REQ_LENGTH;
+}
+
+/* Builds a TCP response header */
+int _modbus_tcp_build_response_basis(sft_t *sft, uint8_t *rsp)
+{
+    /* Extract from MODBUS Messaging on TCP/IP Implementation
+       Guide V1.0b (page 23/46):
+       The transaction identifier is used to associate the future
+       response with the request. */
+    rsp[0] = sft->t_id >> 8;
+    rsp[1] = sft->t_id & 0x00ff;
+
+    /* Protocol Modbus */
+    rsp[2] = 0;
+    rsp[3] = 0;
+
+    /* Length will be set later by send_msg (4 and 5) */
+
+    rsp[6] = 0xFF;
+    rsp[7] = sft->function;
+
+    return _MODBUS_TCP_PRESET_RSP_LENGTH;
+}
+
+
+int _modbus_tcp_prepare_response_tid(const uint8_t *req, int *req_length)
+{
+    return (req[0] << 8) + req[1];
+}
+
+int _modbus_tcp_send_msg_pre(uint8_t *req, int req_length)
+{
+    /* Substract the header length to the message length */
+    int mbap_length = req_length - 6;
+
+    req[4] = mbap_length >> 8;
+    req[5] = mbap_length & 0x00FF;
+
+    return req_length;
+}
+
+ssize_t _modbus_tcp_send(int s, const uint8_t *req, int req_length)
+{
+    /* MSG_NOSIGNAL
+       Requests not to send SIGPIPE on errors on stream oriented
+       sockets when the other end breaks the connection.  The EPIPE
+       error is still returned. */
+    return send(s, req, req_length, MSG_NOSIGNAL);
+}
+
+ssize_t _modbus_tcp_recv(int s, uint8_t *rsp, int rsp_length) {
+    return recv(s, rsp, rsp_length, 0);
+}
+
+int _modbus_tcp_check_integrity(modbus_t *ctx, uint8_t *msg, const int msg_length)
+{
+    return msg_length;
+}
+
+/* Establishes a modbus TCP connection with a Modbus server. */
+static int _modbus_tcp_connect(modbus_t *ctx)
+{
+    int rc;
+    int option;
+    struct sockaddr_in addr;
+    modbus_tcp_t *ctx_tcp = ctx->backend_data;
+
+    ctx->s = socket(PF_INET, SOCK_STREAM, 0);
+    if (ctx->s == -1) {
+        return -1;
+    }
+
+    /* Set the TCP no delay flag */
+    /* SOL_TCP = IPPROTO_TCP */
+    option = 1;
+    rc = setsockopt(ctx->s, IPPROTO_TCP, TCP_NODELAY,
+                    (const void *)&option, sizeof(int));
+    if (rc == -1) {
+        close(ctx->s);
+        return -1;
+    }
+
+#if (!HAVE_DECL___CYGWIN__)
+    /**
+     * Cygwin defines IPTOS_LOWDELAY but can't handle that flag so it's
+     * necessary to workaround that problem.
+     **/
+    /* Set the IP low delay option */
+    option = IPTOS_LOWDELAY;
+    rc = setsockopt(ctx->s, IPPROTO_IP, IP_TOS,
+                    (const void *)&option, sizeof(int));
+    if (rc == -1) {
+        close(ctx->s);
+        return -1;
+    }
+#endif
+
+    if (ctx->debug) {
+        printf("Connecting to %s\n", ctx_tcp->ip);
+    }
+
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(ctx_tcp->port);
+    addr.sin_addr.s_addr = inet_addr(ctx_tcp->ip);
+    rc = connect(ctx->s, (struct sockaddr *)&addr,
+                 sizeof(struct sockaddr_in));
+    if (rc == -1) {
+        close(ctx->s);
+        return -1;
+    }
+
+    return 0;
+}
+
+/* Closes the network connection and socket in TCP mode */
+void _modbus_tcp_close(modbus_t *ctx)
+{
+    shutdown(ctx->s, SHUT_RDWR);
+    close(ctx->s);
+}
+
+int _modbus_tcp_flush(modbus_t *ctx)
+{
+    int rc;
+
+    do {
+        /* Extract the garbage from the socket */
+        char devnull[MODBUS_TCP_MAX_ADU_LENGTH];
+#if (!HAVE_DECL___CYGWIN__)
+        rc = recv(ctx->s, devnull, MODBUS_TCP_MAX_ADU_LENGTH, MSG_DONTWAIT);
+#else
+        /* On Cygwin, it's a bit more complicated to not wait */
+        fd_set rfds;
+        struct timeval tv;
+
+        tv.tv_sec = 0;
+        tv.tv_usec = 0;
+        FD_ZERO(&rfds);
+        FD_SET(ctx->s, &rfds);
+        rc = select(ctx->s+1, &rfds, NULL, NULL, &tv);
+        if (rc == -1) {
+            return -1;
+        }
+
+        rc = recv(ctx->s, devnull, MODBUS_TCP_MAX_ADU_LENGTH, 0);
+#endif
+        if (ctx->debug && rc != -1) {
+            printf("\n%d bytes flushed\n", rc);
+        }
+    } while (rc > 0);
+
+    return rc;
+}
+
+/* Listens for any request from one or many modbus masters in TCP */
+int _modbus_tcp_listen(modbus_t *ctx, int nb_connection)
+{
+    int new_socket;
+    int yes;
+    struct sockaddr_in addr;
+    modbus_tcp_t *ctx_tcp = ctx->backend_data;
+
+    new_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (new_socket == -1) {
+        return -1;
+    }
+
+    yes = 1;
+    if (setsockopt(new_socket, SOL_SOCKET, SO_REUSEADDR,
+                   (char *) &yes, sizeof(yes)) == -1) {
+        close(new_socket);
+        return -1;
+    }
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    /* If the modbus port is < to 1024, we need the setuid root. */
+    addr.sin_port = htons(ctx_tcp->port);
+    addr.sin_addr.s_addr = INADDR_ANY;
+    if (bind(new_socket, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+        close(new_socket);
+        return -1;
+    }
+
+    if (listen(new_socket, nb_connection) == -1) {
+        close(new_socket);
+        return -1;
+    }
+
+    return new_socket;
+}
+
+/* On success, the function return a non-negative integer that is a descriptor
+   for the accepted socket. On error, -1 is returned, and errno is set
+   appropriately. */
+int _modbus_tcp_accept(modbus_t *ctx, int *socket)
+{
+    struct sockaddr_in addr;
+    socklen_t addrlen;
+
+    addrlen = sizeof(struct sockaddr_in);
+    ctx->s = accept(*socket, (struct sockaddr *)&addr, &addrlen);
+    if (ctx->s == -1) {
+        close(*socket);
+        *socket = 0;
+        return -1;
+    }
+
+    if (ctx->debug) {
+        printf("The client %s is connected\n", inet_ntoa(addr.sin_addr));
+    }
+
+    return ctx->s;
+}
+
+int _modbus_tcp_filter_request(modbus_t *ctx, int slave)
+{
+    return 0;
+}
+
+const modbus_backend_t _modbus_tcp_backend = {
+    _MODBUS_BACKEND_TYPE_TCP,
+    _MODBUS_TCP_HEADER_LENGTH,
+    _MODBUS_TCP_CHECKSUM_LENGTH,
+    MODBUS_TCP_MAX_ADU_LENGTH,
+    _modbus_set_slave,
+    _modbus_tcp_build_request_basis,
+    _modbus_tcp_build_response_basis,
+    _modbus_tcp_prepare_response_tid,
+    _modbus_tcp_send_msg_pre,
+    _modbus_tcp_send,
+    _modbus_tcp_recv,
+    _modbus_tcp_check_integrity,
+    _modbus_tcp_connect,
+    _modbus_tcp_close,
+    _modbus_tcp_flush,
+    _modbus_tcp_listen,
+    _modbus_tcp_accept,
+    _modbus_tcp_filter_request
+};
+
+
+/* Allocates and initializes the modbus_t structure for TCP.
+   - ip : "192.168.0.5"
+   - port : 1099
+
+   Set the port to MODBUS_TCP_DEFAULT_PORT to use the default one
+   (502). It's convenient to use a port number greater than or equal
+   to 1024 because it's not necessary to be root to use this port
+   number.
+*/
+modbus_t* modbus_new_tcp(const char *ip, int port)
+{
+    modbus_t *ctx;
+    modbus_tcp_t *ctx_tcp;
+
+    ctx = (modbus_t *) malloc(sizeof(modbus_t));
+    _modbus_init_common(ctx);
+
+    /* Could be changed after to reach a remote serial Modbus device */
+    ctx->slave = MODBUS_TCP_SLAVE;
+
+    ctx->backend = &(_modbus_tcp_backend);
+
+    ctx->backend_data = (modbus_tcp_t *) malloc(sizeof(modbus_tcp_t));
+    ctx_tcp = (modbus_tcp_t *)ctx->backend_data;
+
+    strncpy(ctx_tcp->ip, ip, sizeof(char)*16);
+    ctx_tcp->port = port;
+
+    return ctx;
+}

+ 31 - 0
src/modbus-tcp.h

@@ -0,0 +1,31 @@
+/*
+ * Copyright © 2001-2010 Stéphane Raimbault <stephane.raimbault@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MODBUS_TCP_H_
+#define _MODBUS_TCP_H_
+
+#define MODBUS_TCP_DEFAULT_PORT   502
+#define MODBUS_TCP_SLAVE         0xFF
+
+/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5
+ * TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes
+ */
+#define MODBUS_TCP_MAX_ADU_LENGTH  260
+
+modbus_t* modbus_new_tcp(const char *ip_address, int port);
+
+#endif /* _MODBUS_TCP_H_ */

+ 147 - 1039
src/modbus.c

@@ -13,59 +13,30 @@
  *
  *
  * You should have received a copy of the GNU Lesser Public License
  * You should have received a copy of the GNU Lesser Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * This library implements the Modbus protocol.
+ * http://libmodbus.org/
  */
  */
 
 
-/*
-  The library is designed to send and receive data from a device that
-  communicate via the Modbus protocol.
-
-  The function names used are inspired by the Modicon Modbus Protocol
-  Reference Guide which can be obtained from Schneider at
-  www.schneiderautomation.com.
-
-  Documentation:
-  http://www.easysw.com/~mike/serial/serial.html
-  http://copyleft.free.fr/wordpress/index.php/libmodbus/
-*/
-
 #include <stdio.h>
 #include <stdio.h>
 #include <string.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdlib.h>
-#ifdef HAVE_INTTYPES_H
-#include <inttypes.h>
-#endif
-#ifdef HAVE_STDINT_H
 #include <stdint.h>
 #include <stdint.h>
-#endif
 #include <termios.h>
 #include <termios.h>
 #include <unistd.h>
 #include <unistd.h>
 #include <errno.h>
 #include <errno.h>
-#include <assert.h>
 #include <limits.h>
 #include <limits.h>
-#include <fcntl.h>
 
 
 /* Add this for macros that defined unix flavor */
 /* Add this for macros that defined unix flavor */
 #if (defined(__unix__) || defined(unix)) && !defined(USG)
 #if (defined(__unix__) || defined(unix)) && !defined(USG)
 #include <sys/param.h>
 #include <sys/param.h>
 #endif
 #endif
 
 
-/* TCP */
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#if (defined OpenBSD) || (defined(__FreeBSD__ ) && __FreeBSD__ < 5)
-#include <netinet/in_systm.h>
-#endif
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-
 #if !defined(UINT16_MAX)
 #if !defined(UINT16_MAX)
 #define UINT16_MAX 0xFFFF
 #define UINT16_MAX 0xFFFF
 #endif
 #endif
 
 
-#include <config.h>
 #include "modbus.h"
 #include "modbus.h"
 #include "modbus-private.h"
 #include "modbus-private.h"
 
 
@@ -77,91 +48,8 @@ const unsigned int libmodbus_version_major = LIBMODBUS_VERSION_MAJOR;
 const unsigned int libmodbus_version_minor = LIBMODBUS_VERSION_MINOR;
 const unsigned int libmodbus_version_minor = LIBMODBUS_VERSION_MINOR;
 const unsigned int libmodbus_version_micro = LIBMODBUS_VERSION_MICRO;
 const unsigned int libmodbus_version_micro = LIBMODBUS_VERSION_MICRO;
 
 
-/* This structure reduces the number of params in functions and so
- * optimizes the speed of execution (~ 37%). */
-typedef struct {
-    int slave;
-    int function;
-    int t_id;
-} sft_t;
-
-/* Table of CRC values for high-order byte */
-static uint8_t table_crc_hi[] = {
-    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
-    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
-    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
-    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
-    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
-    0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
-    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
-    0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
-    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
-    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
-    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
-    0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
-    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
-    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
-    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
-    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
-    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
-    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
-    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
-    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
-    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
-    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
-    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
-    0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
-    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
-    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
-};
-
-/* Table of CRC values for low-order byte */
-static uint8_t table_crc_lo[] = {
-    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
-    0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
-    0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
-    0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
-    0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
-    0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
-    0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
-    0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
-    0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
-    0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
-    0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
-    0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
-    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
-    0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
-    0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
-    0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
-    0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
-    0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
-    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
-    0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
-    0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
-    0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
-    0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
-    0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
-    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
-    0x43, 0x83, 0x41, 0x81, 0x80, 0x40
-};
-
-static const int TAB_HEADER_LENGTH[2] = {
-    HEADER_LENGTH_RTU,
-    HEADER_LENGTH_TCP
-};
-
-static const int TAB_CHECKSUM_LENGTH[2] = {
-    CHECKSUM_LENGTH_RTU,
-    CHECKSUM_LENGTH_TCP
-};
-
-static const int TAB_MAX_ADU_LENGTH[2] = {
-    MODBUS_MAX_ADU_LENGTH_RTU,
-    MODBUS_MAX_ADU_LENGTH_TCP,
-};
-
-/* Max between RTU and TCP max adu length */
-#define MAX_MESSAGE_LENGTH MODBUS_MAX_ADU_LENGTH_TCP
+/* Max between RTU and TCP max adu length (so TCP) */
+#define MAX_MESSAGE_LENGTH 260
 
 
 const char *modbus_strerror(int errnum) {
 const char *modbus_strerror(int errnum) {
     switch (errnum) {
     switch (errnum) {
@@ -212,39 +100,7 @@ static void error_print(modbus_t *ctx, const char *context)
 
 
 int modbus_flush(modbus_t *ctx)
 int modbus_flush(modbus_t *ctx)
 {
 {
-    int rc;
-
-    if (ctx->type_com == RTU) {
-        rc = tcflush(ctx->s, TCIOFLUSH);
-    } else {
-        do {
-            /* Extract the garbage from the socket */
-            char devnull[MODBUS_MAX_ADU_LENGTH_TCP];
-#if (!HAVE_DECL___CYGWIN__)
-            rc = recv(ctx->s, devnull, MODBUS_MAX_ADU_LENGTH_TCP, MSG_DONTWAIT);
-#else
-            /* On Cygwin, it's a bit more complicated to not wait */
-            fd_set rfds;
-            struct timeval tv;
-
-            tv.tv_sec = 0;
-            tv.tv_usec = 0;
-            FD_ZERO(&rfds);
-            FD_SET(ctx->s, &rfds);
-            rc = select(ctx->s+1, &rfds, NULL, NULL, &tv);
-            if (rc == -1) {
-                return -1;
-            }
-
-            rc = recv(ctx->s, devnull, MODBUS_MAX_ADU_LENGTH_TCP, 0);
-#endif
-            if (ctx->debug && rc != -1) {
-                printf("\n%d bytes flushed\n", rc);
-            }
-        } while (rc > 0);
-    }
-
-    return rc;
+    ctx->backend->flush(ctx);
 }
 }
 
 
 /* Computes the length of the expected response */
 /* Computes the length of the expected response */
@@ -253,26 +109,26 @@ static unsigned int compute_response_length(modbus_t *ctx, uint8_t *req)
     int length;
     int length;
     int offset;
     int offset;
 
 
-    offset = TAB_HEADER_LENGTH[ctx->type_com];
+    offset = ctx->backend->header_length;
 
 
     switch (req[offset]) {
     switch (req[offset]) {
-    case FC_READ_COILS:
-    case FC_READ_DISCRETE_INPUTS: {
+    case _FC_READ_COILS:
+    case _FC_READ_DISCRETE_INPUTS: {
         /* Header + nb values (code from write_bits) */
         /* Header + nb values (code from write_bits) */
         int nb = (req[offset + 3] << 8) | req[offset + 4];
         int nb = (req[offset + 3] << 8) | req[offset + 4];
         length = 2 + (nb / 8) + ((nb % 8) ? 1 : 0);
         length = 2 + (nb / 8) + ((nb % 8) ? 1 : 0);
     }
     }
         break;
         break;
-    case FC_READ_AND_WRITE_REGISTERS:
-    case FC_READ_HOLDING_REGISTERS:
-    case FC_READ_INPUT_REGISTERS:
+    case _FC_READ_AND_WRITE_REGISTERS:
+    case _FC_READ_HOLDING_REGISTERS:
+    case _FC_READ_INPUT_REGISTERS:
         /* Header + 2 * nb values */
         /* Header + 2 * nb values */
         length = 2 + 2 * (req[offset + 3] << 8 | req[offset + 4]);
         length = 2 + 2 * (req[offset + 3] << 8 | req[offset + 4]);
         break;
         break;
-    case FC_READ_EXCEPTION_STATUS:
+    case _FC_READ_EXCEPTION_STATUS:
         length = 3;
         length = 3;
         break;
         break;
-    case FC_REPORT_SLAVE_ID:
+    case _FC_REPORT_SLAVE_ID:
         /* The response is device specific (the header provides the
         /* The response is device specific (the header provides the
            length) */
            length) */
         return MSG_LENGTH_UNDEFINED;
         return MSG_LENGTH_UNDEFINED;
@@ -280,171 +136,16 @@ static unsigned int compute_response_length(modbus_t *ctx, uint8_t *req)
         length = 5;
         length = 5;
     }
     }
 
 
-    return length + offset + TAB_CHECKSUM_LENGTH[ctx->type_com];
+    return length + offset + ctx->backend->checksum_length;
 }
 }
 
 
-/* Builds a RTU request header */
-static int build_request_basis_rtu(int slave, int function,
-                                   int addr, int nb,
-                                   uint8_t *req)
-{
-    req[0] = slave;
-    req[1] = function;
-    req[2] = addr >> 8;
-    req[3] = addr & 0x00ff;
-    req[4] = nb >> 8;
-    req[5] = nb & 0x00ff;
-
-    return PRESET_REQ_LENGTH_RTU;
-}
-
-/* Builds a TCP request header */
-static int build_request_basis_tcp(int slave, int function,
-                                   int addr, int nb,
-                                   uint8_t *req)
-{
-
-    /* Extract from MODBUS Messaging on TCP/IP Implementation Guide V1.0b
-       (page 23/46):
-       The transaction identifier is used to associate the future response
-       with the request. So, at a time, on a TCP connection, this identifier
-       must be unique. */
-    static uint16_t t_id = 0;
-
-    /* Transaction ID */
-    if (t_id < UINT16_MAX)
-        t_id++;
-    else
-        t_id = 0;
-    req[0] = t_id >> 8;
-    req[1] = t_id & 0x00ff;
-
-    /* Protocol Modbus */
-    req[2] = 0;
-    req[3] = 0;
-
-    /* Length will be defined later by set_req_length_tcp at offsets 4
-       and 5 */
-
-    req[6] = slave;
-    req[7] = function;
-    req[8] = addr >> 8;
-    req[9] = addr & 0x00ff;
-    req[10] = nb >> 8;
-    req[11] = nb & 0x00ff;
-
-    return PRESET_REQ_LENGTH_TCP;
-}
-
-static int build_request_basis(modbus_t *ctx, int function, int addr,
-                               int nb, uint8_t *req)
-{
-    if (ctx->type_com == RTU)
-        return build_request_basis_rtu(ctx->slave, function, addr, nb, req);
-    else
-        return build_request_basis_tcp(ctx->slave, function, addr, nb, req);
-}
-
-/* Builds a RTU response header */
-static int build_response_basis_rtu(sft_t *sft, uint8_t *rsp)
-{
-    rsp[0] = sft->slave;
-    rsp[1] = sft->function;
-
-    return PRESET_RSP_LENGTH_RTU;
-}
-
-/* Builds a TCP response header */
-static int build_response_basis_tcp(sft_t *sft, uint8_t *rsp)
-{
-    /* Extract from MODBUS Messaging on TCP/IP Implementation
-       Guide V1.0b (page 23/46):
-       The transaction identifier is used to associate the future
-       response with the request. */
-    rsp[0] = sft->t_id >> 8;
-    rsp[1] = sft->t_id & 0x00ff;
-
-    /* Protocol Modbus */
-    rsp[2] = 0;
-    rsp[3] = 0;
-
-    /* Length will be set later by send_msg (4 and 5) */
-
-    rsp[6] = 0xFF;
-    rsp[7] = sft->function;
-
-    return PRESET_RSP_LENGTH_TCP;
-}
-
-static int build_response_basis(modbus_t *ctx, sft_t *sft, uint8_t *rsp)
-{
-    if (ctx->type_com == RTU)
-        return build_response_basis_rtu(sft, rsp);
-    else
-        return build_response_basis_tcp(sft, rsp);
-}
-
-/* Fast CRC */
-static uint16_t crc16(uint8_t *buffer, uint16_t buffer_length)
-{
-    uint8_t crc_hi = 0xFF; /* high CRC byte initialized */
-    uint8_t crc_lo = 0xFF; /* low CRC byte initialized */
-    unsigned int i; /* will index into CRC lookup */
-
-    /* pass through message buffer */
-    while (buffer_length--) {
-        i = crc_hi ^ *buffer++; /* calculate the CRC  */
-        crc_hi = crc_lo ^ table_crc_hi[i];
-        crc_lo = table_crc_lo[i];
-    }
-
-    return (crc_hi << 8 | crc_lo);
-}
-
-/* The check_crc16 function shall return the message length if the CRC is
-   valid. Otherwise it shall return -1 and set errno to EMBADCRC. */
-static int check_crc16(modbus_t *ctx, uint8_t *msg, const int msg_length)
-{
-    uint16_t crc_calculated;
-    uint16_t crc_received;
-
-    crc_calculated = crc16(msg, msg_length - 2);
-    crc_received = (msg[msg_length - 2] << 8) | msg[msg_length - 1];
-
-    /* Check CRC of msg */
-    if (crc_calculated == crc_received) {
-        return msg_length;
-    } else {
-        if (ctx->debug) {
-            fprintf(stderr, "ERROR CRC received %0X != CRC calculated %0X\n",
-                    crc_received, crc_calculated);
-        }
-        if (ctx->error_recovery) {
-            modbus_flush(ctx);
-        }
-        errno = EMBBADCRC;
-        return -1;
-    }
-}
-
-/* Sends a req/response over a serial or a TCP communication */
+/* Sends a request/response */
 static int send_msg(modbus_t *ctx, uint8_t *req, int req_length)
 static int send_msg(modbus_t *ctx, uint8_t *req, int req_length)
 {
 {
     int rc;
     int rc;
-    uint16_t s_crc;
     int i;
     int i;
 
 
-    if (ctx->type_com == RTU) {
-        s_crc = crc16(req, req_length);
-        req[req_length++] = s_crc >> 8;
-        req[req_length++] = s_crc & 0x00FF;
-    } else {
-        /* Substract the header length to the message length */
-        int mbap_length = req_length - 6;
-
-        req[4] = mbap_length >> 8;
-        req[5] = mbap_length & 0x00FF;
-    }
+    req_length = ctx->backend->send_msg_pre(req, req_length);
 
 
     if (ctx->debug) {
     if (ctx->debug) {
         for (i = 0; i < req_length; i++)
         for (i = 0; i < req_length; i++)
@@ -455,15 +156,7 @@ static int send_msg(modbus_t *ctx, uint8_t *req, int req_length)
     /* In recovery mode, the write command will be issued until to be
     /* In recovery mode, the write command will be issued until to be
        successful! Disabled by default. */
        successful! Disabled by default. */
     do {
     do {
-        if (ctx->type_com == RTU)
-            rc = write(ctx->s, req, req_length);
-        else
-            /* MSG_NOSIGNAL
-               Requests not to send SIGPIPE on errors on stream oriented
-               sockets when the other end breaks the connection.  The EPIPE
-               error is still returned. */
-            rc = send(ctx->s, req, req_length, MSG_NOSIGNAL);
-
+        rc = ctx->backend->send(ctx->s, req, req_length);
         if (rc == -1) {
         if (rc == -1) {
             error_print(ctx, NULL);
             error_print(ctx, NULL);
             if (ctx->error_recovery &&
             if (ctx->error_recovery &&
@@ -501,20 +194,20 @@ static uint8_t compute_header_length(int function, msg_type_t msg_type)
     int length;
     int length;
 
 
     if (msg_type == MSG_CONFIRMATION) {
     if (msg_type == MSG_CONFIRMATION) {
-        if (function == FC_REPORT_SLAVE_ID) {
+        if (function == _FC_REPORT_SLAVE_ID) {
             length = 1;
             length = 1;
         } else {
         } else {
             /* Should never happen, the other header lengths are precomputed */
             /* Should never happen, the other header lengths are precomputed */
             abort();
             abort();
         }
         }
     } else /* MSG_INDICATION */ {
     } else /* MSG_INDICATION */ {
-        if (function <= FC_WRITE_SINGLE_COIL ||
-            function == FC_WRITE_SINGLE_REGISTER) {
+        if (function <= _FC_WRITE_SINGLE_COIL ||
+            function == _FC_WRITE_SINGLE_REGISTER) {
             length = 4;
             length = 4;
-        } else if (function == FC_WRITE_MULTIPLE_COILS ||
-                   function == FC_WRITE_MULTIPLE_REGISTERS) {
+        } else if (function == _FC_WRITE_MULTIPLE_COILS ||
+                   function == _FC_WRITE_MULTIPLE_REGISTERS) {
             length = 5;
             length = 5;
-        } else if (function == FC_READ_AND_WRITE_REGISTERS) {
+        } else if (function == _FC_READ_AND_WRITE_REGISTERS) {
             length = 9;
             length = 9;
         } else {
         } else {
             length = 0;
             length = 0;
@@ -526,63 +219,62 @@ static uint8_t compute_header_length(int function, msg_type_t msg_type)
 /* Computes the length of the data to write in the request */
 /* Computes the length of the data to write in the request */
 static int compute_data_length(modbus_t *ctx, uint8_t *msg)
 static int compute_data_length(modbus_t *ctx, uint8_t *msg)
 {
 {
-    int function = msg[TAB_HEADER_LENGTH[ctx->type_com]];
+    int function = msg[ctx->backend->header_length];
     int length;
     int length;
 
 
-    if (function == FC_WRITE_MULTIPLE_COILS ||
-        function == FC_WRITE_MULTIPLE_REGISTERS) {
-        length = msg[TAB_HEADER_LENGTH[ctx->type_com] + 5];
-    } else if (function == FC_REPORT_SLAVE_ID) {
-        length = msg[TAB_HEADER_LENGTH[ctx->type_com] + 1];
-    } else if (function == FC_READ_AND_WRITE_REGISTERS) {
-        length = msg[TAB_HEADER_LENGTH[ctx->type_com] + 9];
+    if (function == _FC_WRITE_MULTIPLE_COILS ||
+        function == _FC_WRITE_MULTIPLE_REGISTERS) {
+        length = msg[ctx->backend->header_length + 5];
+    } else if (function == _FC_REPORT_SLAVE_ID) {
+        length = msg[ctx->backend->header_length + 1];
+    } else if (function == _FC_READ_AND_WRITE_REGISTERS) {
+        length = msg[ctx->backend->header_length + 9];
     } else
     } else
         length = 0;
         length = 0;
 
 
-    length += TAB_CHECKSUM_LENGTH[ctx->type_com];
+    length += ctx->backend->checksum_length;
 
 
     return length;
     return length;
 }
 }
 
 
 #define WAIT_DATA() {                                                   \
 #define WAIT_DATA() {                                                   \
-        while ((s_rc = select(ctx->s+1, &rfds, NULL, NULL, &tv)) == -1) { \
-            if (errno == EINTR) {                                       \
-                if (ctx->debug) {                                       \
-                    fprintf(stderr,                                     \
-                            "A non blocked signal was caught\n");       \
-                }                                                       \
-                /* Necessary after an error */                          \
-                FD_ZERO(&rfds);                                         \
-                FD_SET(ctx->s, &rfds);                                  \
+    while ((s_rc = select(ctx->s+1, &rfds, NULL, NULL, &tv)) == -1) {   \
+        if (errno == EINTR) {                                           \
+            if (ctx->debug) {                                           \
+                fprintf(stderr, "A non blocked signal was caught\n");   \
+            }                                                           \
+            /* Necessary after an error */                              \
+            FD_ZERO(&rfds);                                             \
+            FD_SET(ctx->s, &rfds);                                      \
+        } else {                                                        \
+            error_print(ctx, "select");                                 \
+            if (ctx->error_recovery && (errno == EBADF)) {              \
+                modbus_close(ctx);                                      \
+                modbus_connect(ctx);                                    \
+                errno = EBADF;                                          \
+                return -1;                                              \
             } else {                                                    \
             } else {                                                    \
-                error_print(ctx, "select");                             \
-                if (ctx->error_recovery && (errno == EBADF)) {          \
-                    modbus_close(ctx);                                  \
-                    modbus_connect(ctx);                                \
-                    errno = EBADF;                                      \
-                    return -1;                                          \
-                } else {                                                \
-                    return -1;                                          \
-                }                                                       \
+                return -1;                                              \
             }                                                           \
             }                                                           \
         }                                                               \
         }                                                               \
+    }                                                                   \
                                                                         \
                                                                         \
-        if (s_rc == 0) {                                                \
-            /* Timeout */                                               \
-            if (msg_length == (TAB_HEADER_LENGTH[ctx->type_com] + 2 +   \
-                               TAB_CHECKSUM_LENGTH[ctx->type_com])) {   \
-                /* Optimization allowed because exception response is   \
-                   the smallest trame in modbus protocol (3) so always  \
-                   raise a timeout error.                               \
-                   Temporary error before exception analyze. */         \
-                errno = EMBUNKEXC;                                      \
-            } else {                                                    \
-                errno = ETIMEDOUT;                                      \
-                error_print(ctx, "select");                             \
-            }                                                           \
-            return -1;                                                  \
+    if (s_rc == 0) {                                                    \
+        /* Timeout */                                                   \
+        if (msg_length == (ctx->backend->header_length + 2 +            \
+                           ctx->backend->checksum_length)) {            \
+            /* Optimization allowed because exception response is       \
+               the smallest trame in modbus protocol (3) so always      \
+               raise a timeout error.                                   \
+               Temporary error before exception analyze. */             \
+            errno = EMBUNKEXC;                                          \
+        } else {                                                        \
+            errno = ETIMEDOUT;                                          \
+            error_print(ctx, "select");                                 \
         }                                                               \
         }                                                               \
-    }
+        return -1;                                                      \
+    }                                                                   \
+}
 
 
 /* Waits a response from a modbus server or a request from a modbus client.
 /* Waits a response from a modbus server or a request from a modbus client.
    This function blocks if there is no replies (3 timeouts).
    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,
          * reach the function code because all packets contains this
          * reach the function code because all packets contains this
          * information. */
          * information. */
         state = FUNCTION;
         state = FUNCTION;
-        msg_length_computed = TAB_HEADER_LENGTH[ctx->type_com] + 1;
+        msg_length_computed = ctx->backend->header_length + 1;
     } else {
     } else {
         tv.tv_sec = ctx->timeout_begin.tv_sec;
         tv.tv_sec = ctx->timeout_begin.tv_sec;
         tv.tv_usec = ctx->timeout_begin.tv_usec;
         tv.tv_usec = ctx->timeout_begin.tv_usec;
@@ -654,11 +346,7 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed,
 
 
     p_msg = msg;
     p_msg = msg;
     while (s_rc) {
     while (s_rc) {
-        if (ctx->type_com == RTU)
-            read_rc = read(ctx->s, p_msg, length_to_read);
-        else
-            read_rc = recv(ctx->s, p_msg, length_to_read, 0);
-
+        read_rc = ctx->backend->recv(ctx->s, p_msg, length_to_read);
         if (read_rc == 0) {
         if (read_rc == 0) {
             errno = ECONNRESET;
             errno = ECONNRESET;
             read_rc = -1;
             read_rc = -1;
@@ -695,7 +383,7 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed,
             case FUNCTION:
             case FUNCTION:
                 /* Function code position */
                 /* Function code position */
                 length_to_read = compute_header_length(
                 length_to_read = compute_header_length(
-                    msg[TAB_HEADER_LENGTH[ctx->type_com]],
+                    msg[ctx->backend->header_length],
                     msg_type);
                     msg_type);
                 msg_length_computed += length_to_read;
                 msg_length_computed += length_to_read;
                 /* It's useless to check the value of
                 /* It's useless to check the value of
@@ -706,7 +394,7 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed,
             case DATA:
             case DATA:
                 length_to_read = compute_data_length(ctx, msg);
                 length_to_read = compute_data_length(ctx, msg);
                 msg_length_computed += length_to_read;
                 msg_length_computed += length_to_read;
-                if (msg_length_computed > TAB_MAX_ADU_LENGTH[ctx->type_com]) {
+                if (msg_length_computed > ctx->backend->max_adu_length) {
                     errno = EMBBADDATA;
                     errno = EMBBADDATA;
                     error_print(ctx, "too many data");
                     error_print(ctx, "too many data");
                     return -1;
                     return -1;
@@ -738,14 +426,7 @@ static int receive_msg(modbus_t *ctx, int msg_length_computed,
     if (ctx->debug)
     if (ctx->debug)
         printf("\n");
         printf("\n");
 
 
-    if (ctx->type_com == RTU) {
-        /* Returns msg_length on success and a negative value on
-           failure */
-        return check_crc16(ctx, msg, msg_length);
-    } else {
-        /* OK */
-        return msg_length;
-    }
+    return ctx->backend->check_integrity(ctx, msg, msg_length);
 }
 }
 
 
 /* Receive the request from a modbus master, requires the socket file descriptor
 /* 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)
     return receive_msg(ctx, MSG_LENGTH_UNDEFINED, req, MSG_INDICATION);
     return receive_msg(ctx, MSG_LENGTH_UNDEFINED, req, MSG_INDICATION);
 }
 }
 
 
-/* Receives the response and checks values (and checksum in RTU).
+/* Receives the response and checks values.
 
 
    The function shall return the number of values (bits or words) and the
    The function shall return the number of values (bits or words) and the
    response if successful. Otherwise, its shall return -1 and errno is set.
    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)
 {
 {
     int rc;
     int rc;
     int rsp_length_computed;
     int rsp_length_computed;
-    int offset = TAB_HEADER_LENGTH[ctx->type_com];
+    int offset = ctx->backend->header_length;
 
 
     rsp_length_computed = compute_response_length(ctx, req);
     rsp_length_computed = compute_response_length(ctx, req);
     rc = receive_msg(ctx, rsp_length_computed, rsp, MSG_CONFIRMATION);
     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)
         /* The number of values is returned if it's corresponding
         /* The number of values is returned if it's corresponding
          * to the request */
          * to the request */
         switch (rsp[offset]) {
         switch (rsp[offset]) {
-        case FC_READ_COILS:
-        case FC_READ_DISCRETE_INPUTS:
+        case _FC_READ_COILS:
+        case _FC_READ_DISCRETE_INPUTS:
             /* Read functions, 8 values in a byte (nb
             /* Read functions, 8 values in a byte (nb
              * of values in the request and byte count in
              * of values in the request and byte count in
              * the response. */
              * the response. */
@@ -796,20 +477,20 @@ static int receive_msg_req(modbus_t *ctx, uint8_t *req, uint8_t *rsp)
             req_nb_value = (req_nb_value / 8) + ((req_nb_value % 8) ? 1 : 0);
             req_nb_value = (req_nb_value / 8) + ((req_nb_value % 8) ? 1 : 0);
             rsp_nb_value = rsp[offset + 1];
             rsp_nb_value = rsp[offset + 1];
             break;
             break;
-        case FC_READ_AND_WRITE_REGISTERS:
-        case FC_READ_HOLDING_REGISTERS:
-        case FC_READ_INPUT_REGISTERS:
+        case _FC_READ_AND_WRITE_REGISTERS:
+        case _FC_READ_HOLDING_REGISTERS:
+        case _FC_READ_INPUT_REGISTERS:
             /* Read functions 1 value = 2 bytes */
             /* Read functions 1 value = 2 bytes */
             req_nb_value = (req[offset + 3] << 8) + req[offset + 4];
             req_nb_value = (req[offset + 3] << 8) + req[offset + 4];
             rsp_nb_value = (rsp[offset + 1] / 2);
             rsp_nb_value = (rsp[offset + 1] / 2);
             break;
             break;
-        case FC_WRITE_MULTIPLE_COILS:
-        case FC_WRITE_MULTIPLE_REGISTERS:
+        case _FC_WRITE_MULTIPLE_COILS:
+        case _FC_WRITE_MULTIPLE_REGISTERS:
             /* N Write functions */
             /* N Write functions */
             req_nb_value = (req[offset + 3] << 8) + req[offset + 4];
             req_nb_value = (req[offset + 3] << 8) + req[offset + 4];
             rsp_nb_value = (rsp[offset + 3] << 8) | rsp[offset + 4];
             rsp_nb_value = (rsp[offset + 3] << 8) | rsp[offset + 4];
             break;
             break;
-        case FC_REPORT_SLAVE_ID:
+        case _FC_REPORT_SLAVE_ID:
             /* Report slave ID (bytes received) */
             /* Report slave ID (bytes received) */
             req_nb_value = rsp_nb_value = rsp[offset + 1];
             req_nb_value = rsp_nb_value = rsp[offset + 1];
             break;
             break;
@@ -833,11 +514,10 @@ static int receive_msg_req(modbus_t *ctx, uint8_t *req, uint8_t *rsp)
         /* EXCEPTION CODE RECEIVED */
         /* EXCEPTION CODE RECEIVED */
 
 
         /* CRC must be checked here (not done in receive_msg) */
         /* CRC must be checked here (not done in receive_msg) */
-        if (ctx->type_com == RTU) {
-            rc = check_crc16(ctx, rsp, EXCEPTION_RSP_LENGTH_RTU);
-            if (rc == -1)
-                return -1;
-        }
+        rc = ctx->backend->check_integrity(ctx, rsp,
+                                           _MODBUS_EXCEPTION_RSP_LENGTH);
+        if (rc == -1)
+            return -1;
 
 
         /* Check for exception response.
         /* Check for exception response.
            0x80 + function is stored in the exception
            0x80 + function is stored in the exception
@@ -889,7 +569,7 @@ static int response_exception(modbus_t *ctx, sft_t *sft,
     int rsp_length;
     int rsp_length;
 
 
     sft->function = sft->function + 0x80;
     sft->function = sft->function + 0x80;
-    rsp_length = build_response_basis(ctx, sft, rsp);
+    rsp_length = ctx->backend->build_response_basis(sft, rsp);
 
 
     /* Positive exception code */
     /* Positive exception code */
     rsp[rsp_length++] = exception_code;
     rsp[rsp_length++] = exception_code;
@@ -906,7 +586,7 @@ static int response_exception(modbus_t *ctx, sft_t *sft,
 int modbus_reply(modbus_t *ctx, const uint8_t *req,
 int modbus_reply(modbus_t *ctx, const uint8_t *req,
                  int req_length, modbus_mapping_t *mb_mapping)
                  int req_length, modbus_mapping_t *mb_mapping)
 {
 {
-    int offset = TAB_HEADER_LENGTH[ctx->type_com];
+    int offset = ctx->backend->header_length;
     int slave = req[offset - 1];
     int slave = req[offset - 1];
     int function = req[offset];
     int function = req[offset];
     uint16_t address = (req[offset + 1] << 8) + req[offset + 2];
     uint16_t address = (req[offset + 1] << 8) + req[offset + 2];
@@ -914,28 +594,17 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
     int rsp_length = 0;
     int rsp_length = 0;
     sft_t sft;
     sft_t sft;
 
 
-    /* Filter on the Modbus unit identifier (slave) in RTU mode */
-    if (ctx->type_com == RTU &&
-        slave != ctx->slave && slave != MODBUS_BROADCAST_ADDRESS) {
-        /* Ignores the request (not for me) */
-        if (ctx->debug) {
-            printf("Request for slave %d ignored (not %d)\n",
-                   slave, ctx->slave);
-        }
+    if (ctx->backend->filter_request(ctx, slave) == 1) {
+        /* Filtered */
         return 0;
         return 0;
     }
     }
 
 
     sft.slave = slave;
     sft.slave = slave;
     sft.function = function;
     sft.function = function;
-    if (ctx->type_com == TCP) {
-        sft.t_id = (req[0] << 8) + req[1];
-    } else {
-        sft.t_id = 0;
-        req_length -= CHECKSUM_LENGTH_RTU;
-    }
+    sft.t_id = ctx->backend->prepare_response_tid(req, &req_length);
 
 
     switch (function) {
     switch (function) {
-    case FC_READ_COILS: {
+    case _FC_READ_COILS: {
         int nb = (req[offset + 3] << 8) + req[offset + 4];
         int nb = (req[offset + 3] << 8) + req[offset + 4];
 
 
         if ((address + nb) > mb_mapping->nb_bits) {
         if ((address + nb) > mb_mapping->nb_bits) {
@@ -947,7 +616,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
                 ctx, &sft,
                 ctx, &sft,
                 MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp);
                 MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp);
         } else {
         } else {
-            rsp_length = build_response_basis(ctx, &sft, rsp);
+            rsp_length = ctx->backend->build_response_basis(&sft, rsp);
             rsp[rsp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0);
             rsp[rsp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0);
             rsp_length = response_io_status(address, nb,
             rsp_length = response_io_status(address, nb,
                                             mb_mapping->tab_bits,
                                             mb_mapping->tab_bits,
@@ -955,7 +624,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
         }
         }
     }
     }
         break;
         break;
-    case FC_READ_DISCRETE_INPUTS: {
+    case _FC_READ_DISCRETE_INPUTS: {
         /* Similar to coil status (but too many arguments to use a
         /* Similar to coil status (but too many arguments to use a
          * function) */
          * function) */
         int nb = (req[offset + 3] << 8) + req[offset + 4];
         int nb = (req[offset + 3] << 8) + req[offset + 4];
@@ -969,7 +638,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
                 ctx, &sft,
                 ctx, &sft,
                 MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp);
                 MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp);
         } else {
         } else {
-            rsp_length = build_response_basis(ctx, &sft, rsp);
+            rsp_length = ctx->backend->build_response_basis(&sft, rsp);
             rsp[rsp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0);
             rsp[rsp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0);
             rsp_length = response_io_status(address, nb,
             rsp_length = response_io_status(address, nb,
                                             mb_mapping->tab_input_bits,
                                             mb_mapping->tab_input_bits,
@@ -977,7 +646,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
         }
         }
     }
     }
         break;
         break;
-    case FC_READ_HOLDING_REGISTERS: {
+    case _FC_READ_HOLDING_REGISTERS: {
         int nb = (req[offset + 3] << 8) + req[offset + 4];
         int nb = (req[offset + 3] << 8) + req[offset + 4];
 
 
         if ((address + nb) > mb_mapping->nb_registers) {
         if ((address + nb) > mb_mapping->nb_registers) {
@@ -991,7 +660,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
         } else {
         } else {
             int i;
             int i;
 
 
-            rsp_length = build_response_basis(ctx, &sft, rsp);
+            rsp_length = ctx->backend->build_response_basis(&sft, rsp);
             rsp[rsp_length++] = nb << 1;
             rsp[rsp_length++] = nb << 1;
             for (i = address; i < address + nb; i++) {
             for (i = address; i < address + nb; i++) {
                 rsp[rsp_length++] = mb_mapping->tab_registers[i] >> 8;
                 rsp[rsp_length++] = mb_mapping->tab_registers[i] >> 8;
@@ -1000,7 +669,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
         }
         }
     }
     }
         break;
         break;
-    case FC_READ_INPUT_REGISTERS: {
+    case _FC_READ_INPUT_REGISTERS: {
         /* Similar to holding registers (but too many arguments to use a
         /* Similar to holding registers (but too many arguments to use a
          * function) */
          * function) */
         int nb = (req[offset + 3] << 8) + req[offset + 4];
         int nb = (req[offset + 3] << 8) + req[offset + 4];
@@ -1016,7 +685,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
         } else {
         } else {
             int i;
             int i;
 
 
-            rsp_length = build_response_basis(ctx, &sft, rsp);
+            rsp_length = ctx->backend->build_response_basis(&sft, rsp);
             rsp[rsp_length++] = nb << 1;
             rsp[rsp_length++] = nb << 1;
             for (i = address; i < address + nb; i++) {
             for (i = address; i < address + nb; i++) {
                 rsp[rsp_length++] = mb_mapping->tab_input_registers[i] >> 8;
                 rsp[rsp_length++] = mb_mapping->tab_input_registers[i] >> 8;
@@ -1025,7 +694,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
         }
         }
     }
     }
         break;
         break;
-    case FC_WRITE_SINGLE_COIL:
+    case _FC_WRITE_SINGLE_COIL:
         if (address >= mb_mapping->nb_bits) {
         if (address >= mb_mapping->nb_bits) {
             if (ctx->debug) {
             if (ctx->debug) {
                 fprintf(stderr, "Illegal data address %0X in write_bit\n",
                 fprintf(stderr, "Illegal data address %0X in write_bit\n",
@@ -1039,11 +708,6 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
 
 
             if (data == 0xFF00 || data == 0x0) {
             if (data == 0xFF00 || data == 0x0) {
                 mb_mapping->tab_bits[address] = (data) ? ON : OFF;
                 mb_mapping->tab_bits[address] = (data) ? ON : OFF;
-
-                /* In RTU mode, the CRC is computed and added
-                   to the request by send_msg, the computed
-                   CRC will be same and optimisation is
-                   possible here (FIXME). */
                 memcpy(rsp, req, req_length);
                 memcpy(rsp, req, req_length);
                 rsp_length = req_length;
                 rsp_length = req_length;
             } else {
             } else {
@@ -1058,7 +722,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
             }
             }
         }
         }
         break;
         break;
-    case FC_WRITE_SINGLE_REGISTER:
+    case _FC_WRITE_SINGLE_REGISTER:
         if (address >= mb_mapping->nb_registers) {
         if (address >= mb_mapping->nb_registers) {
             if (ctx->debug) {
             if (ctx->debug) {
                 fprintf(stderr, "Illegal data address %0X in write_register\n",
                 fprintf(stderr, "Illegal data address %0X in write_register\n",
@@ -1075,7 +739,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
             rsp_length = req_length;
             rsp_length = req_length;
         }
         }
         break;
         break;
-    case FC_WRITE_MULTIPLE_COILS: {
+    case _FC_WRITE_MULTIPLE_COILS: {
         int nb = (req[offset + 3] << 8) + req[offset + 4];
         int nb = (req[offset + 3] << 8) + req[offset + 4];
 
 
         if ((address + nb) > mb_mapping->nb_bits) {
         if ((address + nb) > mb_mapping->nb_bits) {
@@ -1090,14 +754,14 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
             /* 6 = byte count */
             /* 6 = byte count */
             modbus_set_bits_from_bytes(mb_mapping->tab_bits, address, nb, &req[offset + 6]);
             modbus_set_bits_from_bytes(mb_mapping->tab_bits, address, nb, &req[offset + 6]);
 
 
-            rsp_length = build_response_basis(ctx, &sft, rsp);
+            rsp_length = ctx->backend->build_response_basis(&sft, rsp);
             /* 4 to copy the bit address (2) and the quantity of bits */
             /* 4 to copy the bit address (2) and the quantity of bits */
             memcpy(rsp + rsp_length, req + rsp_length, 4);
             memcpy(rsp + rsp_length, req + rsp_length, 4);
             rsp_length += 4;
             rsp_length += 4;
         }
         }
     }
     }
         break;
         break;
-    case FC_WRITE_MULTIPLE_REGISTERS: {
+    case _FC_WRITE_MULTIPLE_REGISTERS: {
         int nb = (req[offset + 3] << 8) + req[offset + 4];
         int nb = (req[offset + 3] << 8) + req[offset + 4];
 
 
         if ((address + nb) > mb_mapping->nb_registers) {
         if ((address + nb) > mb_mapping->nb_registers) {
@@ -1116,22 +780,22 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
                     (req[offset + j] << 8) + req[offset + j + 1];
                     (req[offset + j] << 8) + req[offset + j + 1];
             }
             }
 
 
-            rsp_length = build_response_basis(ctx, &sft, rsp);
+            rsp_length = ctx->backend->build_response_basis(&sft, rsp);
             /* 4 to copy the address (2) and the no. of registers */
             /* 4 to copy the address (2) and the no. of registers */
             memcpy(rsp + rsp_length, req + rsp_length, 4);
             memcpy(rsp + rsp_length, req + rsp_length, 4);
             rsp_length += 4;
             rsp_length += 4;
         }
         }
     }
     }
         break;
         break;
-    case FC_REPORT_SLAVE_ID:
-        rsp_length = build_response_basis(ctx, &sft, rsp);
+    case _FC_REPORT_SLAVE_ID:
+        rsp_length = ctx->backend->build_response_basis(&sft, rsp);
         /* 2 bytes */
         /* 2 bytes */
         rsp[rsp_length++] = 2;
         rsp[rsp_length++] = 2;
         rsp[rsp_length++] = ctx->slave;
         rsp[rsp_length++] = ctx->slave;
         /* Slave is ON */
         /* Slave is ON */
         rsp[rsp_length++] = 0xFF;
         rsp[rsp_length++] = 0xFF;
         break;
         break;
-    case FC_READ_EXCEPTION_STATUS:
+    case _FC_READ_EXCEPTION_STATUS:
         if (ctx->debug) {
         if (ctx->debug) {
             fprintf(stderr, "FIXME Not implemented\n");
             fprintf(stderr, "FIXME Not implemented\n");
         }
         }
@@ -1139,7 +803,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
         return -1;
         return -1;
         break;
         break;
 
 
-    case FC_READ_AND_WRITE_REGISTERS: {
+    case _FC_READ_AND_WRITE_REGISTERS: {
         int nb = (req[offset + 3] << 8) + req[offset + 4];
         int nb = (req[offset + 3] << 8) + req[offset + 4];
         uint16_t address_write = (req[offset + 5] << 8) + req[offset + 6];
         uint16_t address_write = (req[offset + 5] << 8) + req[offset + 6];
         int nb_write = (req[offset + 7] << 8) + req[offset + 8];
         int nb_write = (req[offset + 7] << 8) + req[offset + 8];
@@ -1155,7 +819,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
                                             MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp);
                                             MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp);
         } else {
         } else {
             int i, j;
             int i, j;
-            rsp_length = build_response_basis(ctx, &sft, rsp);
+            rsp_length = ctx->backend->build_response_basis(&sft, rsp);
             rsp[rsp_length++] = nb << 1;
             rsp[rsp_length++] = nb << 1;
 
 
             for (i = address; i < address + nb; i++) {
             for (i = address; i < address + nb; i++) {
@@ -1189,10 +853,10 @@ static int read_io_status(modbus_t *ctx, int function,
     int rc;
     int rc;
     int req_length;
     int req_length;
 
 
-    uint8_t req[MIN_REQ_LENGTH];
+    uint8_t req[_MIN_REQ_LENGTH];
     uint8_t rsp[MAX_MESSAGE_LENGTH];
     uint8_t rsp[MAX_MESSAGE_LENGTH];
 
 
-    req_length = build_request_basis(ctx, function, addr, nb, req);
+    req_length = ctx->backend->build_request_basis(ctx, function, addr, nb, req);
 
 
     rc = send_msg(ctx, req, req_length);
     rc = send_msg(ctx, req, req_length);
     if (rc > 0) {
     if (rc > 0) {
@@ -1205,7 +869,7 @@ static int read_io_status(modbus_t *ctx, int function,
         if (rc == -1)
         if (rc == -1)
             return -1;
             return -1;
 
 
-        offset = TAB_HEADER_LENGTH[ctx->type_com] + 2;
+        offset = ctx->backend->header_length + 2;
         offset_end = offset + rc;
         offset_end = offset + rc;
         for (i = offset; i < offset_end; i++) {
         for (i = offset; i < offset_end; i++) {
             /* Shift reg hi_byte to temp */
             /* 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)
         return -1;
         return -1;
     }
     }
 
 
-    rc = read_io_status(ctx, FC_READ_COILS, addr, nb, data_dest);
+    rc = read_io_status(ctx, _FC_READ_COILS, addr, nb, data_dest);
 
 
     if (rc == -1)
     if (rc == -1)
         return -1;
         return -1;
@@ -1262,7 +926,7 @@ int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *data_dest)
         return -1;
         return -1;
     }
     }
 
 
-    rc = read_io_status(ctx, FC_READ_DISCRETE_INPUTS, addr, nb, data_dest);
+    rc = read_io_status(ctx, _FC_READ_DISCRETE_INPUTS, addr, nb, data_dest);
 
 
     if (rc == -1)
     if (rc == -1)
         return -1;
         return -1;
@@ -1276,7 +940,7 @@ static int read_registers(modbus_t *ctx, int function, int addr, int nb,
 {
 {
     int rc;
     int rc;
     int req_length;
     int req_length;
-    uint8_t req[MIN_REQ_LENGTH];
+    uint8_t req[_MIN_REQ_LENGTH];
     uint8_t rsp[MAX_MESSAGE_LENGTH];
     uint8_t rsp[MAX_MESSAGE_LENGTH];
 
 
     if (nb > MODBUS_MAX_READ_REGISTERS) {
     if (nb > MODBUS_MAX_READ_REGISTERS) {
@@ -1289,7 +953,7 @@ static int read_registers(modbus_t *ctx, int function, int addr, int nb,
         return -1;
         return -1;
     }
     }
 
 
-    req_length = build_request_basis(ctx, function, addr, nb, req);
+    req_length = ctx->backend->build_request_basis(ctx, function, addr, nb, req);
 
 
     rc = send_msg(ctx, req, req_length);
     rc = send_msg(ctx, req, req_length);
     if (rc > 0) {
     if (rc > 0) {
@@ -1301,7 +965,7 @@ static int read_registers(modbus_t *ctx, int function, int addr, int nb,
             return -1;
             return -1;
         }
         }
 
 
-        offset = TAB_HEADER_LENGTH[ctx->type_com];
+        offset = ctx->backend->header_length;
 
 
         for (i = 0; i < rc; i++) {
         for (i = 0; i < rc; i++) {
             /* shift reg hi_byte to temp OR with lo_byte */
             /* 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)
         return -1;
         return -1;
     }
     }
 
 
-    status = read_registers(ctx, FC_READ_HOLDING_REGISTERS,
+    status = read_registers(ctx, _FC_READ_HOLDING_REGISTERS,
                             addr, nb, data_dest);
                             addr, nb, data_dest);
     return status;
     return status;
 }
 }
@@ -1348,7 +1012,7 @@ int modbus_read_input_registers(modbus_t *ctx, int addr, int nb,
         return -1;
         return -1;
     }
     }
 
 
-    status = read_registers(ctx, FC_READ_INPUT_REGISTERS,
+    status = read_registers(ctx, _FC_READ_INPUT_REGISTERS,
                             addr, nb, data_dest);
                             addr, nb, data_dest);
 
 
     return status;
     return status;
@@ -1360,14 +1024,14 @@ static int write_single(modbus_t *ctx, int function, int addr, int value)
 {
 {
     int rc;
     int rc;
     int req_length;
     int req_length;
-    uint8_t req[MIN_REQ_LENGTH];
+    uint8_t req[_MIN_REQ_LENGTH];
 
 
-    req_length = build_request_basis(ctx, function, addr, value, req);
+    req_length = ctx->backend->build_request_basis(ctx, function, addr, value, req);
 
 
     rc = send_msg(ctx, req, req_length);
     rc = send_msg(ctx, req, req_length);
     if (rc > 0) {
     if (rc > 0) {
         /* Used by write_bit and write_register */
         /* Used by write_bit and write_register */
-        uint8_t rsp[MIN_REQ_LENGTH];
+        uint8_t rsp[_MIN_REQ_LENGTH];
         rc = receive_msg_req(ctx, req, rsp);
         rc = receive_msg_req(ctx, req, rsp);
     }
     }
 
 
@@ -1382,7 +1046,7 @@ int modbus_write_bit(modbus_t *ctx, int addr, int state)
     if (state)
     if (state)
         state = 0xFF00;
         state = 0xFF00;
 
 
-    status = write_single(ctx, FC_WRITE_SINGLE_COIL, addr, state);
+    status = write_single(ctx, _FC_WRITE_SINGLE_COIL, addr, state);
 
 
     return status;
     return status;
 }
 }
@@ -1392,7 +1056,7 @@ int modbus_write_register(modbus_t *ctx, int addr, int value)
 {
 {
     int status;
     int status;
 
 
-    status = write_single(ctx, FC_WRITE_SINGLE_REGISTER, addr, value);
+    status = write_single(ctx, _FC_WRITE_SINGLE_REGISTER, addr, value);
 
 
     return status;
     return status;
 }
 }
@@ -1418,8 +1082,8 @@ int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data_src)
         return -1;
         return -1;
     }
     }
 
 
-    req_length = build_request_basis(ctx, FC_WRITE_MULTIPLE_COILS,
-                                     addr, nb, req);
+    req_length = ctx->backend->build_request_basis(ctx, _FC_WRITE_MULTIPLE_COILS,
+                                                   addr, nb, req);
     byte_count = (nb / 8) + ((nb % 8) ? 1 : 0);
     byte_count = (nb / 8) + ((nb % 8) ? 1 : 0);
     req[req_length++] = byte_count;
     req[req_length++] = byte_count;
 
 
@@ -1470,8 +1134,9 @@ int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data
         return -1;
         return -1;
     }
     }
 
 
-    req_length = build_request_basis(ctx, FC_WRITE_MULTIPLE_REGISTERS,
-                                     addr, nb, req);
+    req_length = ctx->backend->build_request_basis(ctx,
+                                                   _FC_WRITE_MULTIPLE_REGISTERS,
+                                                   addr, nb, req);
     byte_count = nb * 2;
     byte_count = nb * 2;
     req[req_length++] = byte_count;
     req[req_length++] = byte_count;
 
 
@@ -1521,8 +1186,9 @@ int modbus_read_and_write_registers(modbus_t *ctx,
         errno = EMBMDATA;
         errno = EMBMDATA;
         return -1;
         return -1;
     }
     }
-    req_length = build_request_basis(ctx, FC_READ_AND_WRITE_REGISTERS,
-                                     read_addr, read_nb, req);
+    req_length = ctx->backend->build_request_basis(ctx,
+                                                   _FC_READ_AND_WRITE_REGISTERS,
+                                                   read_addr, read_nb, req);
 
 
     req[req_length++] = write_addr >> 8;
     req[req_length++] = write_addr >> 8;
     req[req_length++] = write_addr & 0x00ff;
     req[req_length++] = write_addr & 0x00ff;
@@ -1541,7 +1207,7 @@ int modbus_read_and_write_registers(modbus_t *ctx,
         int offset;
         int offset;
 
 
         rc = receive_msg_req(ctx, req, rsp);
         rc = receive_msg_req(ctx, req, rsp);
-        offset = TAB_HEADER_LENGTH[ctx->type_com];
+        offset = ctx->backend->header_length;
 
 
         /* If rc is negative, the loop is jumped ! */
         /* If rc is negative, the loop is jumped ! */
         for (i = 0; i < rc; i++) {
         for (i = 0; i < rc; i++) {
@@ -1560,9 +1226,10 @@ int modbus_report_slave_id(modbus_t *ctx, uint8_t *data_dest)
 {
 {
     int rc;
     int rc;
     int req_length;
     int req_length;
-    uint8_t req[MIN_REQ_LENGTH];
+    uint8_t req[_MIN_REQ_LENGTH];
 
 
-    req_length = build_request_basis(ctx, FC_REPORT_SLAVE_ID, 0, 0, req);
+    req_length = ctx->backend->build_request_basis(ctx, _FC_REPORT_SLAVE_ID,
+                                                   0, 0, req);
 
 
     /* HACKISH, addr and count are not used */
     /* HACKISH, addr and count are not used */
     req_length -= 4;
     req_length -= 4;
@@ -1579,7 +1246,7 @@ int modbus_report_slave_id(modbus_t *ctx, uint8_t *data_dest)
         if (rc == -1)
         if (rc == -1)
             return -1;
             return -1;
 
 
-        offset = TAB_HEADER_LENGTH[ctx->type_com] + 2;
+        offset = ctx->backend->header_length + 2;
 
 
         for (i=0; i < rc; i++) {
         for (i=0; i < rc; i++) {
             data_dest[i] = rsp[offset + i];
             data_dest[i] = rsp[offset + i];
@@ -1589,105 +1256,22 @@ int modbus_report_slave_id(modbus_t *ctx, uint8_t *data_dest)
     return rc;
     return rc;
 }
 }
 
 
-static void init_common(modbus_t *ctx)
+void _modbus_init_common(modbus_t *ctx)
 {
 {
     ctx->timeout_begin.tv_sec = 0;
     ctx->timeout_begin.tv_sec = 0;
-    ctx->timeout_begin.tv_usec = TIME_OUT_BEGIN_OF_TRAME;
+    ctx->timeout_begin.tv_usec = _TIME_OUT_BEGIN_OF_TRAME;
 
 
     ctx->timeout_end.tv_sec = 0;
     ctx->timeout_end.tv_sec = 0;
-    ctx->timeout_end.tv_usec = TIME_OUT_END_OF_TRAME;
+    ctx->timeout_end.tv_usec = _TIME_OUT_END_OF_TRAME;
 
 
     ctx->error_recovery = FALSE;
     ctx->error_recovery = FALSE;
     ctx->debug = FALSE;
     ctx->debug = FALSE;
 }
 }
 
 
-/* Allocate and initialize the modbus_t structure for RTU
-   - device: "/dev/ttyS0"
-   - baud:   9600, 19200, 57600, 115200, etc
-   - parity: 'N' stands for None, 'E' for Even and 'O' for odd
-   - data_bits: 5, 6, 7, 8
-   - stop_bits: 1, 2
-   - slave: slave number of the caller
-*/
-modbus_t* modbus_new_rtu(const char *device,
-                         int baud, char parity, int data_bit,
-                         int stop_bit, int slave)
-{
-    modbus_t *ctx;
-    modbus_rtu_t *ctx_rtu;
-
-    ctx = (modbus_t *) malloc(sizeof(modbus_t));
-    init_common(ctx);
-    if (modbus_set_slave(ctx, slave) == -1) {
-        return NULL;
-    }
-
-    ctx->type_com = RTU;
-
-    ctx->com = (modbus_rtu_t *) malloc(sizeof(modbus_rtu_t));
-    ctx_rtu = (modbus_rtu_t *)ctx->com;
-#if defined(OpenBSD)
-    strlcpy(ctx_rtu->device, device, sizeof(ctx_rtu->device));
-#else
-    strcpy(ctx_rtu->device, device);
-#endif
-    ctx_rtu->baud = baud;
-    if (parity == 'N' || parity == 'E' || parity == 'O') {
-        ctx_rtu->parity = parity;
-    } else {
-        errno = EINVAL;
-        return NULL;
-    }
-    ctx_rtu->data_bit = data_bit;
-    ctx_rtu->stop_bit = stop_bit;
-
-    return ctx;
-}
-
-/* Allocates and initializes the modbus_t structure for TCP.
-   - ip : "192.168.0.5"
-   - port : 1099
-
-   Set the port to MODBUS_TCP_DEFAULT_PORT to use the default one
-   (502). It's convenient to use a port number greater than or equal
-   to 1024 because it's not necessary to be root to use this port
-   number.
-*/
-modbus_t* modbus_new_tcp(const char *ip, int port)
-{
-    modbus_t *ctx;
-    modbus_tcp_t *ctx_tcp;
-
-    ctx = (modbus_t *) malloc(sizeof(modbus_t));
-    init_common(ctx);
-    ctx->slave = MODBUS_TCP_SLAVE;
-
-    ctx->type_com = TCP;
-
-    ctx->com = (modbus_tcp_t *) malloc(sizeof(modbus_tcp_t));
-    ctx_tcp = (modbus_tcp_t *)ctx->com;
-
-    strncpy(ctx_tcp->ip, ip, sizeof(char)*16);
-    ctx_tcp->port = port;
-
-    /* Can be changed after to reach remote serial Modbus device */
-    return ctx;
-}
-
-/* Define the slave number, the special value MODBUS_TCP_SLAVE (0xFF) can be
- * used in TCP mode to restore the default value. */
+/* Define the slave number */
 int modbus_set_slave(modbus_t *ctx, int slave)
 int modbus_set_slave(modbus_t *ctx, int slave)
 {
 {
-    if (slave >= 1 && slave <= 247) {
-        ctx->slave = slave;
-    } else if (ctx->type_com == TCP && slave == MODBUS_TCP_SLAVE) {
-        ctx->slave = slave;
-    } else {
-        errno = EINVAL;
-        return -1;
-    }
-
-    return 0;
+    return ctx->backend->set_slave(ctx, slave);
 }
 }
 
 
 /*
 /*
@@ -1739,348 +1323,9 @@ void modbus_set_timeout_end(modbus_t *ctx, const struct timeval *timeout)
     ctx->timeout_end = *timeout;
     ctx->timeout_end = *timeout;
 }
 }
 
 
-/* Sets up a serial port for RTU communications */
-static int modbus_connect_rtu(modbus_t *ctx)
-{
-    struct termios tios;
-    speed_t speed;
-    modbus_rtu_t *ctx_rtu = ctx->com;
-
-    if (ctx->debug) {
-        printf("Opening %s at %d bauds (%c, %d, %d)\n",
-               ctx_rtu->device, ctx_rtu->baud, ctx_rtu->parity,
-               ctx_rtu->data_bit, ctx_rtu->stop_bit);
-    }
-
-    /* The O_NOCTTY flag tells UNIX that this program doesn't want
-       to be the "controlling terminal" for that port. If you
-       don't specify this then any input (such as keyboard abort
-       signals and so forth) will affect your process
-
-       Timeouts are ignored in canonical input mode or when the
-       NDELAY option is set on the file via open or fcntl */
-    ctx->s = open(ctx_rtu->device, O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL);
-    if (ctx->s == -1) {
-        fprintf(stderr, "ERROR Can't open the device %s (%s)\n",
-                ctx_rtu->device, strerror(errno));
-        return -1;
-    }
-
-    /* Save */
-    tcgetattr(ctx->s, &(ctx_rtu->old_tios));
-
-    memset(&tios, 0, sizeof(struct termios));
-
-    /* C_ISPEED     Input baud (new interface)
-       C_OSPEED     Output baud (new interface)
-    */
-    switch (ctx_rtu->baud) {
-    case 110:
-        speed = B110;
-        break;
-    case 300:
-        speed = B300;
-        break;
-    case 600:
-        speed = B600;
-        break;
-    case 1200:
-        speed = B1200;
-        break;
-    case 2400:
-        speed = B2400;
-        break;
-    case 4800:
-        speed = B4800;
-        break;
-    case 9600:
-        speed = B9600;
-        break;
-    case 19200:
-        speed = B19200;
-        break;
-    case 38400:
-        speed = B38400;
-        break;
-    case 57600:
-        speed = B57600;
-        break;
-    case 115200:
-        speed = B115200;
-        break;
-    default:
-        speed = B9600;
-        if (ctx->debug) {
-            fprintf(stderr,
-                    "WARNING Unknown baud rate %d for %s (B9600 used)\n",
-                    ctx_rtu->baud, ctx_rtu->device);
-        }
-    }
-
-    /* Set the baud rate */
-    if ((cfsetispeed(&tios, speed) < 0) ||
-        (cfsetospeed(&tios, speed) < 0)) {
-        return -1;
-    }
-
-    /* C_CFLAG      Control options
-       CLOCAL       Local line - do not change "owner" of port
-       CREAD        Enable receiver
-    */
-    tios.c_cflag |= (CREAD | CLOCAL);
-    /* CSIZE, HUPCL, CRTSCTS (hardware flow control) */
-
-    /* Set data bits (5, 6, 7, 8 bits)
-       CSIZE        Bit mask for data bits
-    */
-    tios.c_cflag &= ~CSIZE;
-    switch (ctx_rtu->data_bit) {
-    case 5:
-        tios.c_cflag |= CS5;
-        break;
-    case 6:
-        tios.c_cflag |= CS6;
-        break;
-    case 7:
-        tios.c_cflag |= CS7;
-        break;
-    case 8:
-    default:
-        tios.c_cflag |= CS8;
-        break;
-    }
-
-    /* Stop bit (1 or 2) */
-    if (ctx_rtu->stop_bit == 1)
-        tios.c_cflag &=~ CSTOPB;
-    else /* 2 */
-        tios.c_cflag |= CSTOPB;
-
-    /* PARENB       Enable parity bit
-       PARODD       Use odd parity instead of even */
-    if (ctx_rtu->parity == 'N') {
-        /* None */
-        tios.c_cflag &=~ PARENB;
-    } else if (ctx_rtu->parity == 'E') {
-        /* Even */
-        tios.c_cflag |= PARENB;
-        tios.c_cflag &=~ PARODD;
-    } else {
-        /* Odd */
-        tios.c_cflag |= PARENB;
-        tios.c_cflag |= PARODD;
-    }
-
-    /* Read the man page of termios if you need more information. */
-
-    /* This field isn't used on POSIX systems
-       tios.c_line = 0;
-    */
-
-    /* C_LFLAG      Line options
-
-       ISIG Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals
-       ICANON       Enable canonical input (else raw)
-       XCASE        Map uppercase \lowercase (obsolete)
-       ECHO Enable echoing of input characters
-       ECHOE        Echo erase character as BS-SP-BS
-       ECHOK        Echo NL after kill character
-       ECHONL       Echo NL
-       NOFLSH       Disable flushing of input buffers after
-       interrupt or quit characters
-       IEXTEN       Enable extended functions
-       ECHOCTL      Echo control characters as ^char and delete as ~?
-       ECHOPRT      Echo erased character as character erased
-       ECHOKE       BS-SP-BS entire line on line kill
-       FLUSHO       Output being flushed
-       PENDIN       Retype pending input at next read or input char
-       TOSTOP       Send SIGTTOU for background output
-
-       Canonical input is line-oriented. Input characters are put
-       into a buffer which can be edited interactively by the user
-       until a CR (carriage return) or LF (line feed) character is
-       received.
-
-       Raw input is unprocessed. Input characters are passed
-       through exactly as they are received, when they are
-       received. Generally you'll deselect the ICANON, ECHO,
-       ECHOE, and ISIG options when using raw input
-    */
-
-    /* Raw input */
-    tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
-
-    /* C_IFLAG      Input options
-
-       Constant     Description
-       INPCK        Enable parity check
-       IGNPAR       Ignore parity errors
-       PARMRK       Mark parity errors
-       ISTRIP       Strip parity bits
-       IXON Enable software flow control (outgoing)
-       IXOFF        Enable software flow control (incoming)
-       IXANY        Allow any character to start flow again
-       IGNBRK       Ignore break condition
-       BRKINT       Send a SIGINT when a break condition is detected
-       INLCR        Map NL to CR
-       IGNCR        Ignore CR
-       ICRNL        Map CR to NL
-       IUCLC        Map uppercase to lowercase
-       IMAXBEL      Echo BEL on input line too long
-    */
-    if (ctx_rtu->parity == 'N') {
-        /* None */
-        tios.c_iflag &= ~INPCK;
-    } else {
-        tios.c_iflag |= INPCK;
-    }
-
-    /* Software flow control is disabled */
-    tios.c_iflag &= ~(IXON | IXOFF | IXANY);
-
-    /* C_OFLAG      Output options
-       OPOST        Postprocess output (not set = raw output)
-       ONLCR        Map NL to CR-NL
-
-       ONCLR ant others needs OPOST to be enabled
-    */
-
-    /* Raw ouput */
-    tios.c_oflag &=~ OPOST;
-
-    /* C_CC         Control characters
-       VMIN         Minimum number of characters to read
-       VTIME        Time to wait for data (tenths of seconds)
-
-       UNIX serial interface drivers provide the ability to
-       specify character and packet timeouts. Two elements of the
-       c_cc array are used for timeouts: VMIN and VTIME. Timeouts
-       are ignored in canonical input mode or when the NDELAY
-       option is set on the file via open or fcntl.
-
-       VMIN specifies the minimum number of characters to read. If
-       it is set to 0, then the VTIME value specifies the time to
-       wait for every character read. Note that this does not mean
-       that a read call for N bytes will wait for N characters to
-       come in. Rather, the timeout will apply to the first
-       character and the read call will return the number of
-       characters immediately available (up to the number you
-       request).
-
-       If VMIN is non-zero, VTIME specifies the time to wait for
-       the first character read. If a character is read within the
-       time given, any read will block (wait) until all VMIN
-       characters are read. That is, once the first character is
-       read, the serial interface driver expects to receive an
-       entire packet of characters (VMIN bytes total). If no
-       character is read within the time allowed, then the call to
-       read returns 0. This method allows you to tell the serial
-       driver you need exactly N bytes and any read call will
-       return 0 or N bytes. However, the timeout only applies to
-       the first character read, so if for some reason the driver
-       misses one character inside the N byte packet then the read
-       call could block forever waiting for additional input
-       characters.
-
-       VTIME specifies the amount of time to wait for incoming
-       characters in tenths of seconds. If VTIME is set to 0 (the
-       default), reads will block (wait) indefinitely unless the
-       NDELAY option is set on the port with open or fcntl.
-    */
-    /* Unused because we use open with the NDELAY option */
-    tios.c_cc[VMIN] = 0;
-    tios.c_cc[VTIME] = 0;
-
-    if (tcsetattr(ctx->s, TCSANOW, &tios) < 0) {
-        return -1;
-    }
-
-    return 0;
-}
-
-/* Establishes a modbus TCP connection with a Modbus server. */
-static int modbus_connect_tcp(modbus_t *ctx)
-{
-    int rc;
-    int option;
-    struct sockaddr_in addr;
-    modbus_tcp_t *ctx_tcp = ctx->com;
-
-    ctx->s = socket(PF_INET, SOCK_STREAM, 0);
-    if (ctx->s == -1) {
-        return -1;
-    }
-
-    /* Set the TCP no delay flag */
-    /* SOL_TCP = IPPROTO_TCP */
-    option = 1;
-    rc = setsockopt(ctx->s, IPPROTO_TCP, TCP_NODELAY,
-                    (const void *)&option, sizeof(int));
-    if (rc == -1) {
-        close(ctx->s);
-        return -1;
-    }
-
-#if (!HAVE_DECL___CYGWIN__)
-    /**
-     * Cygwin defines IPTOS_LOWDELAY but can't handle that flag so it's
-     * necessary to workaround that problem.
-     **/
-    /* Set the IP low delay option */
-    option = IPTOS_LOWDELAY;
-    rc = setsockopt(ctx->s, IPPROTO_IP, IP_TOS,
-                    (const void *)&option, sizeof(int));
-    if (rc == -1) {
-        close(ctx->s);
-        return -1;
-    }
-#endif
-
-    if (ctx->debug) {
-        printf("Connecting to %s\n", ctx_tcp->ip);
-    }
-
-    addr.sin_family = AF_INET;
-    addr.sin_port = htons(ctx_tcp->port);
-    addr.sin_addr.s_addr = inet_addr(ctx_tcp->ip);
-    rc = connect(ctx->s, (struct sockaddr *)&addr,
-                 sizeof(struct sockaddr_in));
-    if (rc == -1) {
-        close(ctx->s);
-        return -1;
-    }
-
-    return 0;
-}
-
-/* Establishes a modbus connection.
-   Returns 0 on success or -1 on failure. */
 int modbus_connect(modbus_t *ctx)
 int modbus_connect(modbus_t *ctx)
 {
 {
-    int rc;
-
-    if (ctx->type_com == RTU)
-        rc = modbus_connect_rtu(ctx);
-    else
-        rc = modbus_connect_tcp(ctx);
-
-    return rc;
-}
-
-/* Closes the file descriptor in RTU mode */
-static void modbus_close_rtu(modbus_t *ctx)
-{
-    modbus_rtu_t *ctx_rtu = ctx->com;
-
-    tcsetattr(ctx->s, TCSANOW, &(ctx_rtu->old_tios));
-    close(ctx->s);
-}
-
-/* Closes the network connection and socket in TCP mode */
-static void modbus_close_tcp(modbus_t *ctx)
-{
-    shutdown(ctx->s, SHUT_RDWR);
-    close(ctx->s);
+    return ctx->backend->connect(ctx);
 }
 }
 
 
 /* Closes a  connection */
 /* Closes a  connection */
@@ -2089,10 +1334,7 @@ void modbus_close(modbus_t *ctx)
     if (ctx == NULL)
     if (ctx == NULL)
         return;
         return;
 
 
-    if (ctx->type_com == RTU)
-        modbus_close_rtu(ctx);
-    else
-        modbus_close_tcp(ctx);
+    ctx->backend->close(ctx);
 }
 }
 
 
 /* Free an initialized modbus_t */
 /* Free an initialized modbus_t */
@@ -2101,7 +1343,7 @@ void modbus_free(modbus_t *ctx)
     if (ctx == NULL)
     if (ctx == NULL)
         return;
         return;
 
 
-    free(ctx->com);
+    free(ctx->backend_data);
     free(ctx);
     free(ctx);
 }
 }
 
 
@@ -2203,146 +1445,12 @@ void modbus_mapping_free(modbus_mapping_t *mb_mapping)
     free(mb_mapping);
     free(mb_mapping);
 }
 }
 
 
-/* Listens for any request from one or many modbus masters in TCP */
 int modbus_listen(modbus_t *ctx, int nb_connection)
 int modbus_listen(modbus_t *ctx, int nb_connection)
 {
 {
-    int new_socket;
-    int yes;
-    struct sockaddr_in addr;
-    modbus_tcp_t *ctx_tcp = ctx->com;
-
-    if (ctx->type_com != TCP) {
-        if (ctx->debug)
-            fprintf(stderr, "Not implemented");
-        errno = EINVAL;
-        return -1;
-    }
-
-    new_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
-    if (new_socket == -1) {
-        return -1;
-    }
-
-    yes = 1;
-    if (setsockopt(new_socket, SOL_SOCKET, SO_REUSEADDR,
-                   (char *) &yes, sizeof(yes)) == -1) {
-        close(new_socket);
-        return -1;
-    }
-
-    memset(&addr, 0, sizeof(addr));
-    addr.sin_family = AF_INET;
-    /* If the modbus port is < to 1024, we need the setuid root. */
-    addr.sin_port = htons(ctx_tcp->port);
-    addr.sin_addr.s_addr = INADDR_ANY;
-    if (bind(new_socket, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
-        close(new_socket);
-        return -1;
-    }
-
-    if (listen(new_socket, nb_connection) == -1) {
-        close(new_socket);
-        return -1;
-    }
-
-    return new_socket;
+    return ctx->backend->listen(ctx, nb_connection);
 }
 }
 
 
-/* On success, the function return a non-negative integer that is a descriptor
-   for the accepted socket. On error, -1 is returned, and errno is set
-   appropriately. */
 int modbus_accept(modbus_t *ctx, int *socket)
 int modbus_accept(modbus_t *ctx, int *socket)
 {
 {
-    struct sockaddr_in addr;
-    socklen_t addrlen;
-
-    if (ctx->type_com != TCP) {
-        if (ctx->debug)
-            fprintf(stderr, "Not implemented");
-        errno = EINVAL;
-        return -1;
-    }
-
-    addrlen = sizeof(struct sockaddr_in);
-    ctx->s = accept(*socket, (struct sockaddr *)&addr, &addrlen);
-    if (ctx->s == -1) {
-        close(*socket);
-        *socket = 0;
-        return -1;
-    }
-
-    if (ctx->debug) {
-        printf("The client %s is connected\n", inet_ntoa(addr.sin_addr));
-    }
-
-    return ctx->s;
-}
-
-/** Utils **/
-
-/* Sets many bits from a single byte value (all 8 bits of the byte value are
-   set) */
-void modbus_set_bits_from_byte(uint8_t *dest, int address, const uint8_t value)
-{
-    int i;
-
-    for (i=0; i<8; i++) {
-        dest[address+i] = (value & (1 << i)) ? ON : OFF;
-    }
-}
-
-/* Sets many bits from a table of bytes (only the bits between address and
-   address + nb_bits are set) */
-void modbus_set_bits_from_bytes(uint8_t *dest, int address, unsigned int nb_bits,
-                                const uint8_t tab_byte[])
-{
-    int i;
-    int shift = 0;
-
-    for (i = address; i < address + nb_bits; i++) {
-        dest[i] = tab_byte[(i - address) / 8] & (1 << shift) ? ON : OFF;
-        /* gcc doesn't like: shift = (++shift) % 8; */
-        shift++;
-        shift %= 8;
-    }
-}
-
-/* Gets the byte value from many bits.
-   To obtain a full byte, set nb_bits to 8. */
-uint8_t modbus_get_byte_from_bits(const uint8_t *src, int address, unsigned int nb_bits)
-{
-    int i;
-    uint8_t value = 0;
-
-    if (nb_bits > 8) {
-        assert(nb_bits < 8);
-        nb_bits = 8;
-    }
-
-    for (i=0; i < nb_bits; i++) {
-        value |= (src[address+i] << i);
-    }
-
-    return value;
-}
-
-/* Get a float from 4 bytes in Modbus format */
-float modbus_get_float(const uint16_t *src)
-{
-    float r = 0.0f;
-    uint32_t i;
-
-    i = (((uint32_t)src[1]) << 16) + src[0];
-    memcpy(&r, &i, sizeof (r));
-    return r;
-}
-
-/* Set a float to 4 bytes in Modbus format */
-void modbus_set_float(float real, uint16_t *dest)
-{
-    uint32_t i = 0;
-
-    memcpy(&i, &real, sizeof (i));
-    dest[0] = (uint16_t)i;
-    dest[1] = (uint16_t)(i >> 16);
+    return ctx->backend->accept(ctx, socket);
 }
 }

+ 4 - 20
src/modbus.h

@@ -22,15 +22,9 @@
 #if (defined(__unix__) || defined(unix)) && !defined(USG)
 #if (defined(__unix__) || defined(unix)) && !defined(USG)
 #include <sys/param.h>
 #include <sys/param.h>
 #endif
 #endif
-
-#ifdef HAVE_INTTYPES_H
-#include <inttypes.h>
-#endif
-#ifdef HAVE_STDINT_H
 #include <stdint.h>
 #include <stdint.h>
-#endif
 #include <termios.h>
 #include <termios.h>
-#if defined(OpenBSD) || (defined(__FreeBSD__ ) && __FreeBSD__ < 5)
+#if defined(OpenBSD) || (defined(__FreeBSD__) && __FreeBSD__ < 5)
 #include <netinet/in_systm.h>
 #include <netinet/in_systm.h>
 #endif
 #endif
 #include <netinet/in.h>
 #include <netinet/in.h>
@@ -67,16 +61,7 @@ MODBUS_BEGIN_DECLS
 #define ON 1
 #define ON 1
 #endif
 #endif
 
 
-#define MODBUS_TCP_DEFAULT_PORT   502
 #define MODBUS_BROADCAST_ADDRESS    0
 #define MODBUS_BROADCAST_ADDRESS    0
-#define MODBUS_TCP_SLAVE         0xFF
-
-/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5:
- *  - RS232 / RS485 ADU = 253 bytes + slave (1 byte) + CRC (2 bytes) = 256 bytes
- *  - TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes
- */
-#define MODBUS_MAX_ADU_LENGTH_RTU  256
-#define MODBUS_MAX_ADU_LENGTH_TCP  260
 
 
 /* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 1 page 12)
 /* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 1 page 12)
  * Quantity of Coils to read (2 bytes): 1 to 2000 (0x7D0)
  * Quantity of Coils to read (2 bytes): 1 to 2000 (0x7D0)
@@ -151,12 +136,8 @@ typedef struct {
     uint16_t *tab_registers;
     uint16_t *tab_registers;
 } modbus_mapping_t;
 } modbus_mapping_t;
 
 
-modbus_t* modbus_new_rtu(const char *device, int baud, char parity, int data_bit,
-                          int stop_bit, int slave);
 int modbus_set_slave(modbus_t* ctx, int slave);
 int modbus_set_slave(modbus_t* ctx, int slave);
 
 
-modbus_t* modbus_new_tcp(const char *ip_address, int port);
-
 int modbus_set_error_recovery(modbus_t *ctx, int enabled);
 int modbus_set_error_recovery(modbus_t *ctx, int enabled);
 
 
 void modbus_get_timeout_begin(modbus_t *ctx, struct timeval *timeout);
 void modbus_get_timeout_begin(modbus_t *ctx, struct timeval *timeout);
@@ -214,4 +195,7 @@ void modbus_set_float(float real, uint16_t *dest);
 
 
 MODBUS_END_DECLS
 MODBUS_END_DECLS
 
 
+#include "modbus-tcp.h"
+#include "modbus-rtu.h"
+
 #endif  /* _MODBUS_H_ */
 #endif  /* _MODBUS_H_ */

+ 1 - 1
tests/bandwidth-server-many-up.c

@@ -108,7 +108,7 @@ int main(void)
                     }
                     }
                 } else {
                 } else {
                     /* An already connected master has sent a new query */
                     /* An already connected master has sent a new query */
-                    uint8_t query[MODBUS_MAX_ADU_LENGTH_TCP];
+                    uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
 
 
                     rc = modbus_receive(ctx, master_socket, query);
                     rc = modbus_receive(ctx, master_socket, query);
                     if (rc != -1) {
                     if (rc != -1) {

+ 1 - 1
tests/bandwidth-server-one.c

@@ -45,7 +45,7 @@ int main(void)
     modbus_accept(ctx, &socket);
     modbus_accept(ctx, &socket);
 
 
     for(;;) {
     for(;;) {
-        uint8_t query[MODBUS_MAX_ADU_LENGTH_TCP];
+        uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
 
 
         rc = modbus_receive(ctx, -1, query);
         rc = modbus_receive(ctx, -1, query);
         if (rc >= 0) {
         if (rc >= 0) {

+ 1 - 1
tests/random-test-server.c

@@ -43,7 +43,7 @@ int main(void)
     modbus_accept(ctx, &socket);
     modbus_accept(ctx, &socket);
 
 
     for (;;) {
     for (;;) {
-        uint8_t query[MODBUS_MAX_ADU_LENGTH_TCP];
+        uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
         int rc;
         int rc;
 
 
         rc = modbus_receive(ctx, -1, query);
         rc = modbus_receive(ctx, -1, query);

+ 1 - 1
tests/unit-test-server.c

@@ -69,7 +69,7 @@ int main(void)
     modbus_accept(ctx, &socket);
     modbus_accept(ctx, &socket);
 
 
     for (;;) {
     for (;;) {
-        uint8_t query[MODBUS_MAX_ADU_LENGTH_TCP];
+        uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
 
 
         rc = modbus_receive(ctx, -1, query);
         rc = modbus_receive(ctx, -1, query);
         if (rc > 0) {
         if (rc > 0) {