├── README.md ├── include ├── mb_ip_auth.h ├── mb_log.h ├── mb_pdu.h ├── mb_rtu_adu.h ├── mb_rtu_con.h ├── mb_rtu_master.h ├── mb_rtu_slave.h ├── mb_tcp_adu.h ├── mb_tcp_client.h ├── mb_tcp_con.h └── mb_tcp_server.h ├── src ├── mb_ip_auth.c ├── mb_log.c ├── mb_pdu.c ├── mb_rtu_adu.c ├── mb_rtu_con.c ├── mb_rtu_master.c ├── mb_rtu_slave.c ├── mb_tcp_adu.c ├── mb_tcp_client.c ├── mb_tcp_con.c └── mb_tcp_server.c ├── test ├── mb_test.c └── mb_test.h ├── test_mb_ip_auth ├── Makefile └── test_mb_ip_auth.c ├── test_mb_pdu ├── Makefile └── test_mb_pdu.c ├── test_mb_rtu_adu ├── Makefile └── test_mb_rtu_adu.c ├── test_mb_rtu_master ├── Makefile └── test_mb_rtu_master.c ├── test_mb_rtu_slave ├── Makefile └── test_mb_rtu_slave.c ├── test_mb_tcp_adu ├── Makefile └── test_mb_tcp_adu.c ├── test_mb_tcp_client ├── Makefile └── test_mb_tcp_client.c └── test_mb_tcp_server ├── Makefile └── test_mb_tcp_server.c /README.md: -------------------------------------------------------------------------------- 1 | Modbus Protocol 2 | =============== 3 | 4 | Copyright (c) 2016 - 2017 Keith Cullen 5 | 6 | 7 | Test Applications 8 | ================= 9 | 10 | To test the PDU library 11 | ----------------------- 12 | 13 | $ cd test_mb_pdu 14 | 15 | $ make 16 | 17 | $ ./test_mb_pdu 18 | 19 | To test the RTU ADU library 20 | --------------------------- 21 | 22 | $ cd test_mb_rtu_adu 23 | 24 | $ make 25 | 26 | $ ./test_mb_rtu_adu 27 | 28 | To test the TCP ADU library 29 | --------------------------- 30 | 31 | $ cd test_mb_tcp_adu 32 | 33 | $ make 34 | 35 | $ ./test_mb_tcp_adu 36 | 37 | To test the IP authentication library 38 | ------------------------------------- 39 | 40 | $ cd test_mb_ip_auth 41 | 42 | $ make 43 | 44 | $ ./test_mb_ip_auth 45 | 46 | To test the RTU master/slave 47 | ---------------------------- 48 | 49 | $ socat -d -d pty,raw,echo=0 pty,raw,echo=0 50 | 51 | (In a different terminal) 52 | 53 | $ cd test_mb_rtu_slave 54 | 55 | $ make 56 | 57 | $ ./test_mb_rtu_slave /dev/pts/2 58 | 59 | (In a different terminal) 60 | 61 | $ cd test_mb_rtu_master 62 | 63 | $ make 64 | 65 | $ ./test_mb_rtu_master /dev/pts/3 66 | 67 | To test the TCP client/server 68 | ----------------------------- 69 | 70 | $ cd test_mb_tcp_server 71 | 72 | $ make 73 | 74 | $ ./test_mb_tcp_server 75 | 76 | (In a different terminal) 77 | 78 | $ cd test_mb_tcp_client 79 | 80 | $ make 81 | 82 | $ ./test_mb_tcp_client 83 | 84 | 85 | Supported Protocol Versions 86 | =========================== 87 | 88 | | Protocol Version | Supported | 89 | |---------------------------------------------------------|-----------| 90 | | Modbus TCP | yes | 91 | | Modbus RTU | yes | 92 | | Modbus ASCII | no | 93 | 94 | 95 | Supported Function Codes 96 | ======================== 97 | 98 | | Function Code | Supported | 99 | |---------------------------------------------------------|-----------| 100 | | (0x01) Read Coils | yes | 101 | | (0x02) Read Discrete Inputs | yes | 102 | | (0x03) Read Holding Registers | yes | 103 | | (0x04) Read Input Registers | yes | 104 | | (0x05) Write Single Coil | yes | 105 | | (0x06) Write Single Register | yes | 106 | | (0x07) Read Exception Status (Serial Line Only) | yes | 107 | | (0x08) Diagnostics (Serial Line Only) | yes | 108 | | (0x0b) Get Comm Event Counter (Serial Line Only) | yes | 109 | | (0x0c) Get Comm Event Log (Serial Line Only) | yes | 110 | | (0x0f) Write Multiple Coils | yes | 111 | | (0x10) Write Multiple Registers | yes | 112 | | (0x11) Report Server ID (Serial Line Only) | yes | 113 | | (0x14) Read File Record | yes | 114 | | (0x15) Write File Record | yes | 115 | | (0x16) Mask Write Register | yes | 116 | | (0x17) Read/Write Multiple Registers | yes | 117 | | (0x18) Read FIFO Queue | yes | 118 | | (0x2b) Encapsulated Interface Transport | yes | 119 | 120 | 121 | Validation History 122 | ================== 123 | 124 | v0.1 125 | ---- 126 | 127 | BeagleBone Black 128 | ---------------- 129 | Debian 9.1 130 | 131 | Linux beaglebone 4.4.88-ti-r125 #1 SMP Thu Sep 21 19:23:24 UTC 2017 armv7l GNU/Linux 132 | 133 | HP Pavilion 134 | ----------- 135 | Ubuntu 16.04 136 | 137 | Linux HighCreekLinux 4.13.0-38-generic #43~16.04.1-Ubuntu SMP Wed Mar 14 17:48:43 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux 138 | -------------------------------------------------------------------------------- /include/mb_ip_auth.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef MB_IP_AUTH_H 29 | #define MB_IP_AUTH_H 30 | 31 | #include 32 | 33 | typedef struct mb_ip_auth_node 34 | { 35 | struct in_addr addr; 36 | struct mb_ip_auth_node *next; 37 | } 38 | mb_ip_auth_node_t; 39 | 40 | typedef struct 41 | { 42 | mb_ip_auth_node_t *first; 43 | mb_ip_auth_node_t *last; 44 | } 45 | mb_ip_auth_list_t; 46 | 47 | int mb_ip_auth_node_new(mb_ip_auth_node_t **node, const struct in_addr *addr); 48 | void mb_ip_auth_node_delete(mb_ip_auth_node_t *node); 49 | 50 | void mb_ip_auth_list_create(mb_ip_auth_list_t *list); 51 | void mb_ip_auth_list_destroy(mb_ip_auth_list_t *list); 52 | int mb_ip_auth_list_add_addr(mb_ip_auth_list_t *list, const struct in_addr *addr); 53 | int mb_ip_auth_list_add_str(mb_ip_auth_list_t *list, const char *str); 54 | int mb_ip_auth_list_check_addr(mb_ip_auth_list_t *list, const struct in_addr *addr); 55 | int mb_ip_auth_list_check_str(mb_ip_auth_list_t *list, const char *str); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /include/mb_log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef MB_LOG_H 29 | #define MB_LOG_H 30 | 31 | #define MB_LOG_DEF_LEVEL MB_LOG_ERROR 32 | 33 | typedef enum 34 | { 35 | MB_LOG_ERROR = 0, 36 | MB_LOG_WARN = 1, 37 | MB_LOG_NOTICE = 2, 38 | MB_LOG_INFO = 3, 39 | MB_LOG_DEBUG = 4 40 | } 41 | mb_log_level_t; 42 | 43 | void mb_log_set_level(mb_log_level_t level); 44 | mb_log_level_t mb_log_get_level(void); 45 | void mb_log_error(const char *msg, ...); 46 | void mb_log_warn(const char *msg, ...); 47 | void mb_log_notice(const char *msg, ...); 48 | void mb_log_info(const char *msg, ...); 49 | void mb_log_debug(const char *msg, ...); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /include/mb_pdu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef MB_PDU_H 29 | #define MB_PDU_H 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | /* protocol data unit 37 | * 38 | * 0 1 N <= 253 39 | * +-----------+------+ 40 | * | func_code | data | 41 | * +-----------+------+ 42 | */ 43 | 44 | #define MB_PDU_PROTO_ID 0 45 | #define MB_PDU_MAX_DATA_LEN 252 46 | #define MB_PDU_RD_COILS_MAX_ADDR 0x0000ffff 47 | #define MB_PDU_RD_COILS_MIN_QUANT_COILS 1 48 | #define MB_PDU_RD_COILS_MAX_QUANT_COILS 2000 49 | #define MB_PDU_RD_COILS_MAX_BYTE_COUNT 250 /* MAX_QUANT_COILS / 8 */ 50 | #define MB_PDU_RD_DISC_IPS_MAX_ADDR 0x0000ffff 51 | #define MB_PDU_RD_DISC_IPS_MIN_QUANT_IPS 1 52 | #define MB_PDU_RD_DISC_IPS_MAX_QUANT_IPS 2000 53 | #define MB_PDU_RD_DISC_IPS_MAX_BYTE_COUNT 250 /* MAX_QUANT_IPS / 8 */ 54 | #define MB_PDU_RD_HOLD_REGS_MAX_ADDR 0x0000ffff 55 | #define MB_PDU_RD_HOLD_REGS_MIN_QUANT_REGS 1 56 | #define MB_PDU_RD_HOLD_REGS_MAX_QUANT_REGS 125 57 | #define MB_PDU_RD_HOLD_REGS_MAX_BYTE_COUNT 250 /* MAX_QUANT_REGS * 2 */ 58 | #define MB_PDU_RD_IP_REGS_MAX_ADDR 0x0000ffff 59 | #define MB_PDU_RD_IP_REGS_MIN_QUANT_IP_REGS 1 60 | #define MB_PDU_RD_IP_REGS_MAX_QUANT_IP_REGS 125 61 | #define MB_PDU_RD_IP_REGS_MAX_BYTE_COUNT 230 /* MAX_QUANT_IP_REGS * 2 */ 62 | #define MB_PDU_WR_SING_COIL_OFF_VAL 0x0000 63 | #define MB_PDU_WR_SING_COIL_ON_VAL 0xff00 64 | #define MB_PDU_DIAG_MAX_NUM_DATA 125 /* (MAX_DATA_LEN - 2) / 2 */ 65 | #define MB_PDU_GET_COM_EV_LOG_MIN_BYTE_COUNT 6 66 | #define MB_PDU_GET_COM_EV_LOG_MAX_NUM_EVENTS 245 /* MAX_DATA_LEN - 7 */ 67 | #define MB_PDU_WR_MULT_COILS_MAX_ADDR 0x0000ffff 68 | #define MB_PDU_WR_MULT_COILS_MIN_QUANT_OPS 1 69 | #define MB_PDU_WR_MULT_COILS_MAX_QUANT_OPS 1968 70 | #define MB_PDU_WR_MULT_COILS_MAX_BYTE_COUNT 246 71 | #define MB_PDU_WR_MULT_REGS_MAX_ADDR 0x0000ffff 72 | #define MB_PDU_WR_MULT_REGS_MIN_QUANT_REGS 1 73 | #define MB_PDU_WR_MULT_REGS_MAX_QUANT_REGS 123 74 | #define MB_PDU_REP_SERVER_ID_MIN_BYTE_COUNT 1 75 | #define MB_PDU_REP_SERVER_ID_MAX_BYTE_COUNT 251 76 | #define MB_PDU_REP_SERVER_ID_MAX_DATA_LEN 250 77 | #define MB_PDU_FILE_REC_REF_TYPE 0x06 78 | #define MB_PDU_FILE_REC_MIN_FILE_NUM 1 79 | #define MB_PDU_FILE_REC_MAX_REC_NUM 0x270f 80 | #define MB_PDU_RD_FILE_REC_REQ_SUB_REQ_NUM_BYTES 7 81 | #define MB_PDU_RD_FILE_REC_MAX_NUM_SUB_REQ 35 82 | #define MB_PDU_RD_FILE_REC_MIN_BYTE_COUNT 7 83 | #define MB_PDU_RD_FILE_REC_MAX_BYTE_COUNT 245 84 | #define MB_PDU_RD_FILE_REC_MIN_RESP_DATA_LEN 7 85 | #define MB_PDU_RD_FILE_REC_MAX_RESP_DATA_LEN 245 86 | #define MB_PDU_RD_FILE_REC_MIN_FILE_RESP_LEN 1 87 | #define MB_PDU_RD_FILE_REC_MAX_FILE_RESP_LEN 245 88 | #define MB_PDU_RD_FILE_REC_MAX_NUM_REC_DATA 122 /* (MAX_FILE_RESP_LEN - 1) / 2 */ 89 | #define MB_PDU_WR_FILE_REC_MAX_NUM_SUB_REQ 27 90 | #define MB_PDU_WR_FILE_REC_MAX_REC_LEN 122 91 | #define MB_PDU_WR_FILE_REC_MIN_REQ_DATA_LEN 9 92 | #define MB_PDU_WR_FILE_REC_MAX_REQ_DATA_LEN 251 93 | #define MB_PDU_WR_FILE_REC_MIN_RESP_DATA_LEN 9 94 | #define MB_PDU_WR_FILE_REC_MAX_RESP_DATA_LEN 251 95 | #define MB_PDU_RD_WR_MULT_REGS_MAX_ADDR 0x0000ffff 96 | #define MB_PDU_RD_WR_MULT_REGS_MIN_QUANT_RD 1 97 | #define MB_PDU_RD_WR_MULT_REGS_MAX_QUANT_RD 125 98 | #define MB_PDU_RD_WR_MULT_REGS_MAX_RD_BYTE_COUNT 250 /* (MAX_QUANT_RD * 2) */ 99 | #define MB_PDU_RD_WR_MULT_REGS_MIN_QUANT_WR 1 100 | #define MB_PDU_RD_WR_MULT_REGS_MAX_QUANT_WR 121 101 | #define MB_PDU_RD_WR_MULT_REGS_MAX_WR_BYTE_COUNT 242 /* (MAX_QUANT_WR * 2) */ 102 | #define MB_PDU_RD_FIFO_Q_MAX_FIFO_COUNT 31 103 | #define MB_PDU_RD_FIFO_Q_MAX_BYTE_COUNT 64 /* (MAX_FIFO_COUNT + 1) * 2 */ 104 | #define MB_PDU_ENC_IF_TRANS_MAX_MEI_DATA_LEN 251 /* MAX_DATA_LEN - 1 */ 105 | 106 | typedef enum 107 | { 108 | MB_PDU_EXCEPT_ILLEGAL_FUNC = 1, 109 | MB_PDU_EXCEPT_ILLEGAL_ADDR = 2, 110 | MB_PDU_EXCEPT_ILLEGAL_VAL = 3, 111 | MB_PDU_EXCEPT_SERVER_DEV_FAIL = 4, 112 | MB_PDU_EXCEPT_ACK = 5, 113 | MB_PDU_EXCEPT_SERVER_DEV_BUSY = 6, 114 | MB_PDU_EXCEPT_MEM_PARITY_ERROR = 8, 115 | MB_PDU_EXCEPT_GATEWAY_PATH_UNAVAIL = 10, 116 | MB_PDU_EXCEPT_GATEWAY_TARGET_NO_RESP = 11 117 | } 118 | mb_pdu_except_code_t; 119 | 120 | typedef enum 121 | { 122 | MB_PDU_DEF = 0, 123 | MB_PDU_REQ, 124 | MB_PDU_RESP, 125 | MB_PDU_ERR 126 | } 127 | mb_pdu_type_t; 128 | 129 | /* Modbus Application Protocol Specification V1.1b3 */ 130 | typedef enum 131 | { 132 | MB_PDU_RD_COILS = 0x01, /* Read Coils */ 133 | MB_PDU_RD_DISC_IPS = 0x02, /* Read Discrete Inputs */ 134 | MB_PDU_RD_HOLD_REGS = 0x03, /* Read Holding Registers */ 135 | MB_PDU_RD_IP_REGS = 0x04, /* Read Input Register */ 136 | MB_PDU_WR_SING_COIL = 0x05, /* Write Single Coil */ 137 | MB_PDU_WR_SING_REG = 0x06, /* Write Single Register */ 138 | MB_PDU_RD_EXCEPT_STAT = 0x07, /* Read Exception Status */ 139 | MB_PDU_DIAG = 0x08, /* Diagnostic */ 140 | MB_PDU_GET_COM_EV_CNTR = 0x0b, /* Get Com event counter */ 141 | MB_PDU_GET_COM_EV_LOG = 0x0c, /* Get Com Event Log */ 142 | MB_PDU_WR_MULT_COILS = 0x0f, /* Write Multiple Coils */ 143 | MB_PDU_WR_MULT_REGS = 0x10, /* Write Multiple Registers */ 144 | MB_PDU_REP_SERVER_ID = 0x11, /* Report Server ID */ 145 | MB_PDU_RD_FILE_REC = 0x14, /* Read File Record */ 146 | MB_PDU_WR_FILE_REC = 0x15, /* Write File Record */ 147 | MB_PDU_MASK_WR_REG = 0x16, /* Mask Write Register */ 148 | MB_PDU_RD_WR_MULT_REGS = 0x17, /* Read/Write Multiple Registers */ 149 | MB_PDU_RD_FIFO_Q = 0x18, /* Read FIFO queue */ 150 | MB_PDU_ENC_IF_TRANS = 0x2b /* Encapsulated Interface Transport */ 151 | } 152 | mb_pdu_func_code_t; 153 | 154 | /* Modbus over Serial Line Specification and Implementation Guide V1.02 */ 155 | /* Modbus Application Protocol Specification V1.1b3 */ 156 | typedef enum 157 | { 158 | MB_PDU_QUERY_DATA = 0x00, /* Return Query Data */ 159 | MB_PDU_CLEAR_COUNTERS = 0x0a, /* Clear Counters and Diagnostic Register */ 160 | MB_PDU_BUS_MSG_COUNT = 0x0b, /* Return Bus Message Count */ 161 | MB_PDU_BUS_COM_ERR_COUNT = 0x0c, /* Return Bus Communication Error Count */ 162 | MB_PDU_SLAVE_EXCEP_ERR_COUNT = 0x0d, /* Slave Exception Error Count */ 163 | MB_PDU_SLAVE_MSG_COUNT = 0x0e, /* Slave Message Count */ 164 | MB_PDU_SLAVE_NO_RESP_COUNT = 0x0f /* Slave No Response Count */ 165 | } 166 | mb_pdu_diag_sub_func_code_t; 167 | 168 | typedef struct 169 | { 170 | uint8_t buf[MB_PDU_MAX_DATA_LEN]; 171 | } 172 | mb_pdu_def_t; 173 | 174 | typedef struct 175 | { 176 | uint16_t start_addr; 177 | uint16_t quant_coils; 178 | } 179 | mb_pdu_rd_coils_req_t; 180 | 181 | typedef struct 182 | { 183 | uint8_t byte_count; 184 | uint8_t coil_stat[MB_PDU_RD_COILS_MAX_BYTE_COUNT]; 185 | } 186 | mb_pdu_rd_coils_resp_t; 187 | 188 | typedef struct 189 | { 190 | uint16_t start_addr; 191 | uint16_t quant_ips; 192 | } 193 | mb_pdu_rd_disc_ips_req_t; 194 | 195 | typedef struct 196 | { 197 | uint8_t byte_count; 198 | uint8_t ip_stat[MB_PDU_RD_DISC_IPS_MAX_BYTE_COUNT]; 199 | } 200 | mb_pdu_rd_disc_ips_resp_t; 201 | 202 | typedef struct 203 | { 204 | uint16_t start_addr; 205 | uint16_t quant_regs; 206 | } 207 | mb_pdu_rd_hold_regs_req_t; 208 | 209 | typedef struct 210 | { 211 | uint8_t byte_count; 212 | uint16_t reg_val[MB_PDU_RD_HOLD_REGS_MAX_QUANT_REGS]; 213 | } 214 | mb_pdu_rd_hold_regs_resp_t; 215 | 216 | typedef struct 217 | { 218 | uint16_t start_addr; 219 | uint16_t quant_ip_regs; 220 | } 221 | mb_pdu_rd_ip_regs_req_t; 222 | 223 | typedef struct 224 | { 225 | uint8_t byte_count; 226 | uint16_t ip_reg[MB_PDU_RD_IP_REGS_MAX_QUANT_IP_REGS]; 227 | } 228 | mb_pdu_rd_ip_regs_resp_t; 229 | 230 | typedef struct 231 | { 232 | uint16_t op_addr; 233 | bool op_val; 234 | } 235 | mb_pdu_wr_sing_coil_t; 236 | 237 | typedef struct 238 | { 239 | uint16_t reg_addr; 240 | uint16_t reg_val; 241 | } 242 | mb_pdu_wr_sing_reg_t; 243 | 244 | typedef struct 245 | { 246 | } 247 | mb_pdu_rd_except_stat_req_t; 248 | 249 | typedef struct 250 | { 251 | uint8_t out_data; 252 | } 253 | mb_pdu_rd_except_stat_resp_t; 254 | 255 | typedef struct 256 | { 257 | uint16_t sub_func; 258 | uint16_t data[MB_PDU_DIAG_MAX_NUM_DATA]; 259 | uint8_t num_data; 260 | } 261 | mb_pdu_diag_t; 262 | 263 | typedef struct 264 | { 265 | } 266 | mb_pdu_get_com_ev_cntr_req_t; 267 | 268 | typedef struct 269 | { 270 | uint16_t status; 271 | uint16_t ev_cnt; 272 | } 273 | mb_pdu_get_com_ev_cntr_resp_t; 274 | 275 | typedef struct 276 | { 277 | } 278 | mb_pdu_get_com_ev_log_req_t; 279 | 280 | typedef struct 281 | { 282 | uint8_t byte_count; 283 | uint16_t status; 284 | uint16_t ev_cnt; 285 | uint16_t msg_cnt; 286 | uint8_t event[MB_PDU_GET_COM_EV_LOG_MAX_NUM_EVENTS]; 287 | } 288 | mb_pdu_get_com_ev_log_resp_t; 289 | 290 | typedef struct 291 | { 292 | uint16_t start_addr; 293 | uint16_t quant_ops; 294 | uint8_t byte_count; 295 | uint8_t op_val[MB_PDU_WR_MULT_COILS_MAX_BYTE_COUNT]; 296 | } 297 | mb_pdu_wr_mult_coils_req_t; 298 | 299 | typedef struct 300 | { 301 | uint16_t start_addr; 302 | uint16_t quant_ops; 303 | } 304 | mb_pdu_wr_mult_coils_resp_t; 305 | 306 | typedef struct 307 | { 308 | uint16_t start_addr; 309 | uint16_t quant_regs; 310 | uint8_t byte_count; 311 | uint16_t reg_val[MB_PDU_WR_MULT_REGS_MAX_QUANT_REGS]; 312 | } 313 | mb_pdu_wr_mult_regs_req_t; 314 | 315 | typedef struct 316 | { 317 | uint16_t start_addr; 318 | uint16_t quant_regs; 319 | } 320 | mb_pdu_wr_mult_regs_resp_t; 321 | 322 | typedef struct 323 | { 324 | } 325 | mb_pdu_rep_server_id_req_t; 326 | 327 | typedef struct 328 | { 329 | uint8_t byte_count; 330 | uint8_t server_id[MB_PDU_REP_SERVER_ID_MAX_DATA_LEN - 1]; 331 | bool run_ind_status; 332 | } 333 | mb_pdu_rep_server_id_resp_t; 334 | 335 | typedef struct 336 | { 337 | uint8_t ref_type; 338 | uint16_t file_num; 339 | uint16_t rec_num; 340 | uint16_t rec_len; 341 | } 342 | mb_pdu_rd_file_rec_req_sub_req_t; 343 | 344 | typedef struct 345 | { 346 | uint8_t byte_count; 347 | mb_pdu_rd_file_rec_req_sub_req_t sub_req[MB_PDU_RD_FILE_REC_MAX_NUM_SUB_REQ]; 348 | } 349 | mb_pdu_rd_file_rec_req_t; 350 | 351 | typedef struct 352 | { 353 | uint8_t file_resp_len; 354 | uint8_t ref_type; 355 | uint16_t rec_data[MB_PDU_RD_FILE_REC_MAX_NUM_REC_DATA]; 356 | } 357 | mb_pdu_rd_file_rec_resp_sub_req_t; 358 | 359 | typedef struct 360 | { 361 | uint8_t resp_data_len; 362 | mb_pdu_rd_file_rec_resp_sub_req_t sub_req[MB_PDU_RD_FILE_REC_MAX_NUM_SUB_REQ]; 363 | } 364 | mb_pdu_rd_file_rec_resp_t; 365 | 366 | typedef struct 367 | { 368 | uint8_t ref_type; 369 | uint16_t file_num; 370 | uint16_t rec_num; 371 | uint16_t rec_len; 372 | uint16_t rec_data[MB_PDU_WR_FILE_REC_MAX_REC_LEN]; 373 | } 374 | mb_pdu_wr_file_rec_sub_req_t; 375 | 376 | typedef struct 377 | { 378 | uint8_t req_data_len; 379 | mb_pdu_wr_file_rec_sub_req_t sub_req[MB_PDU_WR_FILE_REC_MAX_NUM_SUB_REQ]; 380 | } 381 | mb_pdu_wr_file_rec_req_t; 382 | 383 | typedef struct 384 | { 385 | uint8_t resp_data_len; 386 | mb_pdu_wr_file_rec_sub_req_t sub_req[MB_PDU_WR_FILE_REC_MAX_NUM_SUB_REQ]; 387 | } 388 | mb_pdu_wr_file_rec_resp_t; 389 | 390 | typedef struct 391 | { 392 | uint16_t ref_addr; 393 | uint16_t and_mask; 394 | uint16_t or_mask; 395 | } 396 | mb_pdu_mask_wr_reg_t; 397 | 398 | typedef struct 399 | { 400 | uint16_t rd_start_addr; 401 | uint16_t quant_rd; 402 | uint16_t wr_start_addr; 403 | uint16_t quant_wr; 404 | uint8_t wr_byte_count; 405 | uint16_t wr_reg_val[MB_PDU_RD_WR_MULT_REGS_MAX_QUANT_WR]; 406 | } 407 | mb_pdu_rd_wr_mult_regs_req_t; 408 | 409 | typedef struct 410 | { 411 | uint8_t byte_count; 412 | uint16_t rd_reg_val[MB_PDU_RD_WR_MULT_REGS_MAX_QUANT_RD]; 413 | } 414 | mb_pdu_rd_wr_mult_regs_resp_t; 415 | 416 | typedef struct 417 | { 418 | uint16_t fifo_ptr_addr; 419 | } 420 | mb_pdu_rd_fifo_q_req_t; 421 | 422 | typedef struct 423 | { 424 | uint16_t byte_count; 425 | uint16_t fifo_count; 426 | uint16_t fifo_val_reg[MB_PDU_RD_FIFO_Q_MAX_FIFO_COUNT]; 427 | } 428 | mb_pdu_rd_fifo_q_resp_t; 429 | 430 | typedef struct 431 | { 432 | uint8_t mei_type; 433 | uint8_t mei_data[MB_PDU_ENC_IF_TRANS_MAX_MEI_DATA_LEN]; 434 | uint8_t mei_data_len; 435 | } 436 | mb_pdu_enc_if_trans_t; 437 | 438 | typedef struct 439 | { 440 | uint8_t except_code; 441 | } 442 | mb_pdu_err_t; 443 | 444 | typedef struct 445 | { 446 | uint8_t func_code; 447 | mb_pdu_type_t type; 448 | union 449 | { 450 | mb_pdu_def_t def; 451 | mb_pdu_rd_coils_req_t rd_coils_req; 452 | mb_pdu_rd_coils_resp_t rd_coils_resp; 453 | mb_pdu_rd_disc_ips_req_t rd_disc_ips_req; 454 | mb_pdu_rd_disc_ips_resp_t rd_disc_ips_resp; 455 | mb_pdu_rd_hold_regs_req_t rd_hold_regs_req; 456 | mb_pdu_rd_hold_regs_resp_t rd_hold_regs_resp; 457 | mb_pdu_rd_ip_regs_req_t rd_ip_regs_req; 458 | mb_pdu_rd_ip_regs_resp_t rd_ip_regs_resp; 459 | mb_pdu_wr_sing_coil_t wr_sing_coil_req; 460 | mb_pdu_wr_sing_coil_t wr_sing_coil_resp; 461 | mb_pdu_wr_sing_reg_t wr_sing_reg_req; 462 | mb_pdu_wr_sing_reg_t wr_sing_reg_resp; 463 | mb_pdu_rd_except_stat_req_t rd_except_stat_req; 464 | mb_pdu_rd_except_stat_resp_t rd_except_stat_resp; 465 | mb_pdu_diag_t diag_req; 466 | mb_pdu_diag_t diag_resp; 467 | mb_pdu_get_com_ev_cntr_req_t get_com_ev_cntr_req; 468 | mb_pdu_get_com_ev_cntr_resp_t get_com_ev_cntr_resp; 469 | mb_pdu_get_com_ev_log_req_t get_com_ev_log_req; 470 | mb_pdu_get_com_ev_log_resp_t get_com_ev_log_resp; 471 | mb_pdu_wr_mult_coils_req_t wr_mult_coils_req; 472 | mb_pdu_wr_mult_coils_resp_t wr_mult_coils_resp; 473 | mb_pdu_wr_mult_regs_req_t wr_mult_regs_req; 474 | mb_pdu_wr_mult_regs_resp_t wr_mult_regs_resp; 475 | mb_pdu_rep_server_id_req_t rep_server_id_req; 476 | mb_pdu_rep_server_id_resp_t rep_server_id_resp; 477 | mb_pdu_rd_file_rec_req_t rd_file_rec_req; 478 | mb_pdu_rd_file_rec_resp_t rd_file_rec_resp; 479 | mb_pdu_wr_file_rec_req_t wr_file_rec_req; 480 | mb_pdu_wr_file_rec_resp_t wr_file_rec_resp; 481 | mb_pdu_mask_wr_reg_t mask_wr_reg_req; 482 | mb_pdu_mask_wr_reg_t mask_wr_reg_resp; 483 | mb_pdu_rd_wr_mult_regs_req_t rd_wr_mult_regs_req; 484 | mb_pdu_rd_wr_mult_regs_resp_t rd_wr_mult_regs_resp; 485 | mb_pdu_rd_fifo_q_req_t rd_fifo_q_req; 486 | mb_pdu_rd_fifo_q_resp_t rd_fifo_q_resp; 487 | mb_pdu_enc_if_trans_t enc_if_trans_req; 488 | mb_pdu_enc_if_trans_t enc_if_trans_resp; 489 | mb_pdu_err_t err; 490 | }; 491 | uint16_t data_len; 492 | } 493 | mb_pdu_t; 494 | 495 | int mb_pdu_set(mb_pdu_t *pdu, mb_pdu_type_t type, uint8_t func_code, const uint8_t *data, uint16_t data_len); 496 | int mb_pdu_set_rd_coils_req(mb_pdu_t *pdu, uint16_t start_addr, uint16_t quant_coils); 497 | int mb_pdu_set_rd_coils_resp(mb_pdu_t *pdu, uint8_t byte_count, const uint8_t *coil_stat); 498 | int mb_pdu_set_rd_disc_ips_req(mb_pdu_t *pdu, uint16_t start_addr, uint16_t quant_ips); 499 | int mb_pdu_set_rd_disc_ips_resp(mb_pdu_t *pdu, uint8_t byte_count, const uint8_t *ip_stat); 500 | int mb_pdu_set_rd_hold_regs_req(mb_pdu_t *pdu, uint16_t start_addr, uint16_t quant_regs); 501 | int mb_pdu_set_rd_hold_regs_resp(mb_pdu_t *pdu, uint8_t byte_count, const uint16_t *reg_val); 502 | int mb_pdu_set_rd_ip_regs_req(mb_pdu_t *pdu, uint16_t start_addr, uint16_t quant_ip_regs); 503 | int mb_pdu_set_rd_ip_regs_resp(mb_pdu_t *pdu, uint8_t byte_count, const uint16_t *ip_reg); 504 | void mb_pdu_set_wr_sing_coil_req(mb_pdu_t *pdu, uint16_t op_addr, bool op_val); 505 | void mb_pdu_set_wr_sing_coil_resp(mb_pdu_t *pdu, uint16_t op_addr, bool op_val); 506 | void mb_pdu_set_wr_sing_reg_req(mb_pdu_t *pdu, uint16_t reg_addr, uint16_t reg_val); 507 | void mb_pdu_set_wr_sing_reg_resp(mb_pdu_t *pdu, uint16_t reg_addr, uint16_t reg_val); 508 | void mb_pdu_set_rd_except_stat_req(mb_pdu_t *pdu); 509 | void mb_pdu_set_rd_except_stat_resp(mb_pdu_t *pdu, uint8_t out_data); 510 | int mb_pdu_set_diag_req(mb_pdu_t *pdu, uint16_t sub_func, const uint16_t *data, uint8_t num_data); 511 | int mb_pdu_set_diag_resp(mb_pdu_t *pdu, uint16_t sub_func, const uint16_t *data, uint8_t num_data); 512 | void mb_pdu_set_get_com_ev_cntr_req(mb_pdu_t *pdu); 513 | void mb_pdu_set_get_com_ev_cntr_resp(mb_pdu_t *pdu, uint16_t status, uint16_t ev_cnt); 514 | void mb_pdu_set_get_com_ev_log_req(mb_pdu_t *pdu); 515 | int mb_pdu_set_get_com_ev_log_resp(mb_pdu_t *pdu, uint16_t status, uint16_t ev_cnt, uint16_t msg_cnt, const uint8_t *event, uint8_t num_events); 516 | int mb_pdu_set_wr_mult_coils_req(mb_pdu_t *pdu, uint16_t start_addr, uint16_t quant_ops, const uint8_t *op_val); 517 | int mb_pdu_set_wr_mult_coils_resp(mb_pdu_t *pdu, uint16_t start_addr, uint16_t quant_ops); 518 | int mb_pdu_set_wr_mult_regs_req(mb_pdu_t *pdu, uint16_t start_addr, uint16_t quant_regs, uint8_t byte_count, const uint16_t *reg_val); 519 | int mb_pdu_set_wr_mult_regs_resp(mb_pdu_t *pdu, uint16_t start_addr, uint16_t quant_regs); 520 | void mb_pdu_set_rep_server_id_req(mb_pdu_t *pdu); 521 | int mb_pdu_set_rep_server_id_resp(mb_pdu_t *pdu, uint8_t byte_count, const uint8_t *server_id, bool run_ind_status); 522 | int mb_pdu_set_rd_file_rec_req(mb_pdu_t *pdu, const mb_pdu_rd_file_rec_req_sub_req_t *sub_req, size_t num_sub_req); 523 | int mb_pdu_set_rd_file_rec_resp(mb_pdu_t *pdu, const mb_pdu_rd_file_rec_resp_sub_req_t *sub_req, size_t num_sub_req); 524 | int mb_pdu_set_wr_file_rec_req(mb_pdu_t *pdu, const mb_pdu_wr_file_rec_sub_req_t *sub_req, size_t num_sub_req); 525 | int mb_pdu_set_wr_file_rec_resp(mb_pdu_t *pdu, const mb_pdu_wr_file_rec_sub_req_t *sub_req, size_t num_sub_req); 526 | void mb_pdu_set_mask_wr_reg_req(mb_pdu_t *pdu, uint16_t ref_addr, uint16_t and_mask, uint16_t or_mask); 527 | void mb_pdu_set_mask_wr_reg_resp(mb_pdu_t *pdu, uint16_t ref_addr, uint16_t and_mask, uint16_t or_mask); 528 | int mb_pdu_set_rd_wr_mult_regs_req(mb_pdu_t *pdu, uint16_t rd_start_addr, uint16_t quant_rd, uint16_t wr_start_addr, uint16_t quant_wr, const uint16_t *wr_reg_val); 529 | int mb_pdu_set_rd_wr_mult_regs_resp(mb_pdu_t *pdu, uint8_t byte_count, const uint16_t *rd_reg_val); 530 | void mb_pdu_set_rd_fifo_q_req(mb_pdu_t *pdu, uint16_t fifo_ptr_addr); 531 | int mb_pdu_set_rd_fifo_q_resp(mb_pdu_t *pdu, uint16_t fifo_count, const uint16_t *fifo_val_reg); 532 | int mb_pdu_set_enc_if_trans_req(mb_pdu_t *pdu, uint8_t mei_type, const uint8_t *mei_data, uint8_t mei_data_len); 533 | int mb_pdu_set_enc_if_trans_resp(mb_pdu_t *pdu, uint8_t mei_type, const uint8_t *mei_data, uint8_t mei_data_len); 534 | int mb_pdu_set_err_resp(mb_pdu_t *pdu, uint8_t func_code, uint8_t except_code); 535 | 536 | ssize_t mb_pdu_format_req(mb_pdu_t *pdu, char *buf, size_t len); 537 | ssize_t mb_pdu_format_resp(mb_pdu_t *pdu, char *buf, size_t len); 538 | 539 | ssize_t mb_pdu_parse_req(mb_pdu_t *pdu, const char *buf, size_t len); 540 | ssize_t mb_pdu_parse_resp(mb_pdu_t *pdu, const char *buf, size_t len); 541 | 542 | #endif 543 | -------------------------------------------------------------------------------- /include/mb_rtu_adu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef MB_RTU_ADU_H 29 | #define MB_RTU_ADU_H 30 | 31 | #include 32 | #include 33 | #include 34 | #include "mb_pdu.h" 35 | 36 | /* RTU application data unit 37 | * 38 | * 0 1 2 N N+2 39 | * +---------+-----------+------+-----+ 40 | * | address | func_code | data | CRC | 41 | * +---------+-----------+------+-----+ 42 | * |<---->| 43 | * 0 to 252 44 | * N <= 254 45 | * N+2 <= 256 46 | */ 47 | 48 | #define MB_RTU_ADU_MIN_LEN 3 49 | #define MB_RTU_ADU_MAX_LEN 256 50 | #define MB_RTU_ADU_BROADCAST_ADDR 0 51 | #define MB_RTU_ADU_MIN_UNICAST_ADDR 1 52 | #define MB_RTU_ADU_MAX_UNICAST_ADDR 247 53 | 54 | /* Application Protocol Data Unit */ 55 | typedef struct 56 | { 57 | uint8_t addr; 58 | mb_pdu_t pdu; 59 | } 60 | mb_rtu_adu_t; 61 | 62 | int mb_rtu_adu_check_crc(const uint8_t *buf, size_t len); 63 | int mb_rtu_adu_valid_broadcast_req(mb_rtu_adu_t *adu); 64 | void mb_rtu_adu_set_header(mb_rtu_adu_t *adu, uint8_t addr); 65 | ssize_t mb_rtu_adu_format_req(mb_rtu_adu_t *adu, char *buf, size_t len); 66 | ssize_t mb_rtu_adu_format_resp(mb_rtu_adu_t *adu, char *buf, size_t len); 67 | ssize_t mb_rtu_adu_parse_req(mb_rtu_adu_t *adu, const char *buf, size_t len); 68 | ssize_t mb_rtu_adu_parse_resp(mb_rtu_adu_t *adu, const char *buf, size_t len); 69 | int mb_rtu_adu_to_str(mb_rtu_adu_t *adu, char *buf, size_t len); 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /include/mb_rtu_con.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef MB_RTU_CON_H 29 | #define MB_RTU_CON_H 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "mb_rtu_adu.h" 36 | 37 | #define MB_RTU_CON_BAUD_RATE B19200 38 | #define MB_RTU_CON_T15_SEC 0 39 | #define MB_RTU_CON_T15_NSEC 859375 40 | #define MB_RTU_CON_T35_SEC 0 41 | #define MB_RTU_CON_T35_NSEC 2005208 42 | 43 | typedef struct 44 | { 45 | int serial_fd; 46 | int t15_fd; 47 | int t35_fd; 48 | } 49 | mb_rtu_con_t; 50 | 51 | int mb_rtu_con_create(mb_rtu_con_t *con, const char *dev); 52 | void mb_rtu_con_destroy(mb_rtu_con_t *con); 53 | int mb_rtu_con_start_timer(int fd, time_t sec, long nsec); 54 | int mb_rtu_con_read_timer(int fd); 55 | ssize_t mb_rtu_con_send(mb_rtu_con_t *con, const char *buf, size_t len); 56 | ssize_t mb_rtu_con_recv(mb_rtu_con_t *con, char *buf, size_t len); 57 | ssize_t mb_rtu_con_recv_timeout(mb_rtu_con_t *con, char *buf, size_t len, int timer_fd); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /include/mb_rtu_master.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef MB_RTU_MASTER_H 29 | #define MB_RTU_MASTER_H 30 | 31 | #include "mb_rtu_con.h" 32 | #include "mb_rtu_adu.h" 33 | 34 | #define MB_RTU_MASTER_TURNAROUND_DELAY_SEC 0 35 | #define MB_RTU_MASTER_TURNAROUND_DELAY_NSEC 100000000 36 | #define MB_RTU_MASTER_RESPONSE_TIMEOUT_SEC 1 37 | #define MB_RTU_MASTER_RESPONSE_TIMEOUT_NSEC 0 38 | 39 | typedef struct 40 | { 41 | mb_rtu_con_t con; 42 | int timer_fd; 43 | } 44 | mb_rtu_master_t; 45 | 46 | int mb_rtu_master_create(mb_rtu_master_t *master, const char *dev); 47 | void mb_rtu_master_destroy(mb_rtu_master_t *master); 48 | int mb_rtu_master_exchange(mb_rtu_master_t *master, mb_rtu_adu_t *req, mb_rtu_adu_t *resp); 49 | int mb_rtu_master_broadcast(mb_rtu_master_t *master, mb_rtu_adu_t *req); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /include/mb_rtu_slave.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef MB_RTU_SLAVE_H 29 | #define MB_RTU_SLAVE_H 30 | 31 | #include "mb_rtu_con.h" 32 | #include "mb_rtu_adu.h" 33 | 34 | struct mb_rtu_slave; 35 | 36 | typedef int (*mb_rtu_slave_handler_t)(struct mb_rtu_slave *slave, mb_rtu_adu_t *req, mb_rtu_adu_t *resp); 37 | 38 | typedef struct mb_rtu_slave 39 | { 40 | int addr; 41 | mb_rtu_con_t con; 42 | mb_rtu_slave_handler_t handler; 43 | int bus_msg_count; /* Return Bus Message Count */ 44 | int bus_com_err_count; /* Return Bus Communication Error Count */ 45 | int slave_excep_err_count; /* Return Slave Exception Error Count */ 46 | int slave_msg_count; /* Return Slave Message Count */ 47 | int slave_no_resp_count; /* Return Slave No Response Count */ 48 | } 49 | mb_rtu_slave_t; 50 | 51 | int mb_rtu_slave_create(mb_rtu_slave_t *slave, const char *dev, int addr, mb_rtu_slave_handler_t handler); 52 | void mb_rtu_slave_destroy(mb_rtu_slave_t *slave); 53 | int mb_rtu_slave_run(mb_rtu_slave_t *slave); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /include/mb_tcp_adu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef MB_TCP_ADU_H 29 | #define MB_TCP_ADU_H 30 | 31 | #include 32 | #include 33 | #include 34 | #include "mb_pdu.h" 35 | 36 | /* TCP application data unit 37 | * 38 | * 0 2 4 6 7 8 N <= 260 39 | * +----------+----------+-----+---------+-----------+------+ 40 | * | trans_id | proto_id | len | unit_id | func_code | data | 41 | * +----------+----------+-----+---------+-----------+------+ 42 | * |<-------------------------->| 43 | * len 44 | */ 45 | 46 | #define MB_TCP_ADU_MAX_LEN 260 47 | #define MB_TCP_ADU_HEADER_LEN 7 48 | #define MB_TCP_ADU_LEN_OFF 4 49 | #define MB_TCP_ADU_DATA_LEN_DIFF 2 /* difference between the len field in a tcp adu and the len field in an pdu */ 50 | 51 | /* Application Protocol Data Unit */ 52 | typedef struct 53 | { 54 | uint16_t trans_id; 55 | uint16_t proto_id; 56 | uint16_t len; 57 | uint8_t unit_id; 58 | mb_pdu_t pdu; 59 | } 60 | mb_tcp_adu_t; 61 | 62 | void mb_tcp_adu_set_header(mb_tcp_adu_t *adu, uint16_t trans_id, uint16_t proto_id, uint8_t unit_id); 63 | ssize_t mb_tcp_adu_format_req(mb_tcp_adu_t *adu, char *buf, size_t len); 64 | ssize_t mb_tcp_adu_format_resp(mb_tcp_adu_t *adu, char *buf, size_t len); 65 | ssize_t mb_tcp_adu_parse_req(mb_tcp_adu_t *adu, const char *buf, size_t len); 66 | ssize_t mb_tcp_adu_parse_resp(mb_tcp_adu_t *adu, const char *buf, size_t len); 67 | int mb_tcp_adu_to_str(mb_tcp_adu_t *adu, char *buf, size_t len); 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /include/mb_tcp_client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef MB_TCP_CLIENT_H 29 | #define MB_TCP_CLIENT_H 30 | 31 | #include 32 | #include 33 | #include "mb_ip_auth.h" 34 | #include "mb_tcp_con.h" 35 | #include "mb_tcp_adu.h" 36 | 37 | #define MB_TCP_CLIENT_MAX_CON 4 38 | #define MB_TCP_CLIENT_SOCKET_CLOSED 0 39 | #define MB_TCP_CLIENT_UNIT_ID 0xff /* unit id used in client requests */ 40 | 41 | typedef struct 42 | { 43 | mb_ip_auth_list_t auth; 44 | mb_tcp_con_t con[MB_TCP_CLIENT_MAX_CON]; 45 | struct timeval timeout; 46 | } 47 | mb_tcp_client_t; 48 | 49 | void mb_tcp_client_create(mb_tcp_client_t *client, struct timeval timeout); 50 | void mb_tcp_client_destroy(mb_tcp_client_t *client); 51 | int mb_tcp_client_authorise_addr(mb_tcp_client_t *client, const char *str); 52 | int mb_tcp_client_exchange(mb_tcp_client_t *client, const char *host, in_port_t port, mb_tcp_adu_t *req, mb_tcp_adu_t *resp); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /include/mb_tcp_con.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef MB_TCP_CON_H 29 | #define MB_TCP_CON_H 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "mb_tcp_adu.h" 36 | 37 | #define MB_TCP_CON_SOCKET_CLOSED 0 38 | 39 | #define mb_tcp_con_is_active(con) ((con)->sd != MB_TCP_CON_SOCKET_CLOSED) 40 | 41 | typedef struct 42 | { 43 | int index; 44 | int sd; 45 | time_t last_use; 46 | struct sockaddr_in sin; 47 | char rx_buf[MB_TCP_ADU_MAX_LEN]; 48 | size_t rx_end; 49 | } 50 | mb_tcp_con_t; 51 | 52 | int mb_tcp_con_set_non_blocking(int sd); 53 | void mb_tcp_con_create(mb_tcp_con_t *con, int index); 54 | void mb_tcp_con_destroy(mb_tcp_con_t *con); 55 | void mb_tcp_con_open(mb_tcp_con_t *con, int sd, struct sockaddr_in *sin); 56 | void mb_tcp_con_close(mb_tcp_con_t *con); 57 | ssize_t mb_tcp_con_send(mb_tcp_con_t *con, char *buf, size_t len); 58 | ssize_t mb_tcp_con_recv(mb_tcp_con_t *con); 59 | void mb_tcp_con_consume(mb_tcp_con_t *con, size_t num); 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /include/mb_tcp_server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef MB_TCP_SERVER_H 29 | #define MB_TCP_SERVER_H 30 | 31 | #include 32 | #include "mb_ip_auth.h" 33 | #include "mb_tcp_con.h" 34 | #include "mb_tcp_adu.h" 35 | 36 | #define MB_TCP_SERVER_MAX_CON 4 37 | #define MB_TCP_SERVER_SOCKET_CLOSED 0 38 | #define MB_TCP_SERVER_UNIT_ID 0xff /* unit id used in server responses */ 39 | 40 | struct mb_tcp_server; 41 | 42 | typedef int (*mb_tcp_server_handler_t)(struct mb_tcp_server *server, mb_tcp_adu_t *req, mb_tcp_adu_t *resp); 43 | 44 | typedef struct mb_tcp_server 45 | { 46 | int sd; 47 | mb_ip_auth_list_t auth; 48 | mb_tcp_con_t con[MB_TCP_SERVER_MAX_CON]; 49 | mb_tcp_server_handler_t handler; 50 | } 51 | mb_tcp_server_t; 52 | 53 | int mb_tcp_server_create(mb_tcp_server_t *server, const char *host, in_port_t port, mb_tcp_server_handler_t handler); 54 | void mb_tcp_server_destroy(mb_tcp_server_t *server); 55 | int mb_tcp_server_authorise_addr(mb_tcp_server_t *server, const char *str); 56 | int mb_tcp_server_run(mb_tcp_server_t *server); 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/mb_ip_auth.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include "mb_ip_auth.h" 32 | 33 | int mb_ip_auth_node_new(mb_ip_auth_node_t **node, const struct in_addr *addr) 34 | { 35 | *node = (mb_ip_auth_node_t *)malloc(sizeof(mb_ip_auth_node_t)); 36 | if (*node == NULL) 37 | { 38 | return -ENOMEM; 39 | } 40 | memcpy(&(*node)->addr, addr, sizeof(struct in_addr)); 41 | (*node)->next = NULL; 42 | return 0; 43 | } 44 | 45 | void mb_ip_auth_node_delete(mb_ip_auth_node_t *node) 46 | { 47 | free(node); 48 | } 49 | 50 | void mb_ip_auth_list_create(mb_ip_auth_list_t *list) 51 | { 52 | memset(list, 0, sizeof(mb_ip_auth_list_t)); 53 | } 54 | 55 | void mb_ip_auth_list_destroy(mb_ip_auth_list_t *list) 56 | { 57 | mb_ip_auth_node_t *prev = NULL; 58 | mb_ip_auth_node_t *node = NULL; 59 | 60 | node = list->first; 61 | while (node != NULL) 62 | { 63 | prev = node; 64 | node = node->next; 65 | mb_ip_auth_node_delete(prev); 66 | } 67 | } 68 | 69 | int mb_ip_auth_list_add_addr(mb_ip_auth_list_t *list, const struct in_addr *addr) 70 | { 71 | mb_ip_auth_node_t *node = NULL; 72 | int ret = 0; 73 | 74 | ret = mb_ip_auth_node_new(&node, addr); 75 | if (ret < 0) 76 | { 77 | return ret; 78 | } 79 | if (list->first == NULL) 80 | { 81 | list->first = node; 82 | list->last = node; 83 | } 84 | else 85 | { 86 | list->last->next = node; 87 | list->last = node; 88 | } 89 | return 0; 90 | } 91 | 92 | int mb_ip_auth_list_add_str(mb_ip_auth_list_t *list, const char *str) 93 | { 94 | mb_ip_auth_node_t *node = NULL; 95 | struct in_addr addr = {0}; 96 | int ret = 0; 97 | 98 | ret = inet_pton(AF_INET, str, &addr); 99 | if (ret < 0) 100 | { 101 | return -errno; 102 | } 103 | if (ret == 0) 104 | { 105 | return -EINVAL; 106 | } 107 | ret = mb_ip_auth_node_new(&node, &addr); 108 | if (ret < 0) 109 | { 110 | return ret; 111 | } 112 | if (list->first == NULL) 113 | { 114 | list->first = node; 115 | list->last = node; 116 | } 117 | else 118 | { 119 | list->last->next = node; 120 | list->last = node; 121 | } 122 | return 0; 123 | } 124 | 125 | int mb_ip_auth_list_check_addr(mb_ip_auth_list_t *list, const struct in_addr *addr) 126 | { 127 | mb_ip_auth_node_t *node = NULL; 128 | 129 | node = list->first; 130 | while (node != NULL) 131 | { 132 | if (memcmp(addr, &node->addr, sizeof(struct in_addr)) == 0) 133 | { 134 | return 1; 135 | } 136 | node = node->next; 137 | } 138 | return 0; 139 | } 140 | 141 | int mb_ip_auth_list_check_str(mb_ip_auth_list_t *list, const char *str) 142 | { 143 | mb_ip_auth_node_t *node = NULL; 144 | struct in_addr addr = {0}; 145 | int ret = 0; 146 | 147 | ret = inet_pton(AF_INET, str, &addr); 148 | if (ret < 0) 149 | { 150 | return -errno; 151 | } 152 | if (ret == 0) 153 | { 154 | return -EINVAL; 155 | } 156 | node = list->first; 157 | while (node != NULL) 158 | { 159 | if (memcmp(&addr, &node->addr, sizeof(struct in_addr)) == 0) 160 | { 161 | return 1; 162 | } 163 | node = node->next; 164 | } 165 | return 0; 166 | } 167 | -------------------------------------------------------------------------------- /src/mb_log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include "mb_log.h" 31 | 32 | static mb_log_level_t mb_log_level = MB_LOG_DEF_LEVEL; 33 | 34 | void mb_log_set_level(mb_log_level_t level) 35 | { 36 | switch (level) { 37 | case MB_LOG_WARN: 38 | case MB_LOG_NOTICE: 39 | case MB_LOG_INFO: 40 | case MB_LOG_DEBUG: 41 | mb_log_level = level; 42 | break; 43 | default: 44 | mb_log_level = MB_LOG_DEF_LEVEL; 45 | } 46 | } 47 | 48 | mb_log_level_t mb_log_get_level(void) 49 | { 50 | return mb_log_level; 51 | } 52 | 53 | void mb_log_error(const char *msg, ...) 54 | { 55 | va_list arg_list; 56 | 57 | va_start(arg_list, msg); 58 | if (MB_LOG_ERROR <= mb_log_level) 59 | { 60 | printf("Error : "); 61 | vprintf(msg, arg_list); 62 | printf("\n"); 63 | } 64 | va_end(arg_list); 65 | } 66 | 67 | void mb_log_warn(const char *msg, ...) 68 | { 69 | va_list arg_list; 70 | 71 | va_start(arg_list, msg); 72 | if (MB_LOG_WARN <= mb_log_level) 73 | { 74 | printf("Warning: "); 75 | vprintf(msg, arg_list); 76 | printf("\n"); 77 | } 78 | va_end(arg_list); 79 | } 80 | 81 | void mb_log_notice(const char *msg, ...) 82 | { 83 | va_list arg_list; 84 | 85 | va_start(arg_list, msg); 86 | if (MB_LOG_NOTICE <= mb_log_level) 87 | { 88 | printf("Notice : "); 89 | vprintf(msg, arg_list); 90 | printf("\n"); 91 | } 92 | va_end(arg_list); 93 | } 94 | 95 | void mb_log_info(const char *msg, ...) 96 | { 97 | va_list arg_list; 98 | 99 | va_start(arg_list, msg); 100 | if (MB_LOG_INFO <= mb_log_level) 101 | { 102 | printf("Info : "); 103 | vprintf(msg, arg_list); 104 | printf("\n"); 105 | } 106 | va_end(arg_list); 107 | } 108 | 109 | void mb_log_debug(const char *msg, ...) 110 | { 111 | va_list arg_list; 112 | 113 | va_start(arg_list, msg); 114 | if (MB_LOG_DEBUG <= mb_log_level) 115 | { 116 | printf("Debug : "); 117 | vprintf(msg, arg_list); 118 | printf("\n"); 119 | } 120 | va_end(arg_list); 121 | } 122 | -------------------------------------------------------------------------------- /src/mb_rtu_adu.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include "mb_rtu_adu.h" 31 | 32 | static uint8_t mb_rtu_adu_crc_hi[] = 33 | { 34 | 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 35 | 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 36 | 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 37 | 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 38 | 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 39 | 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 40 | 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 41 | 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 42 | 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 43 | 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 44 | 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 45 | 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 46 | 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 47 | 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 48 | 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 49 | 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 50 | }; 51 | 52 | static uint8_t mb_rtu_adu_crc_lo[] = 53 | { 54 | 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 55 | 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 56 | 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 57 | 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 58 | 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 59 | 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 60 | 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 61 | 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 62 | 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 63 | 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 64 | 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 65 | 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 66 | 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 67 | 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 68 | 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 69 | 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 70 | }; 71 | 72 | static uint16_t mb_rtu_adu_calc_crc(const uint8_t *buf, size_t len) 73 | { 74 | unsigned i = 0; 75 | uint8_t crc_hi = 0xff; 76 | uint8_t crc_lo = 0xff; 77 | uint8_t t = 0; 78 | 79 | for (i = 0; i < len; i++) 80 | { 81 | t = crc_lo ^ buf[i]; 82 | crc_lo = crc_hi ^ mb_rtu_adu_crc_hi[t]; 83 | crc_hi = mb_rtu_adu_crc_lo[t]; 84 | } 85 | return ((uint16_t)crc_hi << 8) | (uint16_t)crc_lo; 86 | } 87 | 88 | int mb_rtu_adu_check_crc(const uint8_t *buf, size_t len) 89 | { 90 | uint16_t exp = 0; 91 | uint16_t crc = 0; 92 | 93 | exp = mb_rtu_adu_calc_crc(buf, len - 2); 94 | crc = ((uint16_t)(uint8_t)buf[len - 1] << 8) | ((uint16_t)(uint8_t)buf[len - 2]); 95 | return crc == exp; 96 | } 97 | 98 | int mb_rtu_adu_valid_broadcast_req(mb_rtu_adu_t *adu) 99 | { 100 | switch (adu->pdu.func_code) 101 | { 102 | case MB_PDU_WR_SING_COIL: 103 | case MB_PDU_WR_SING_REG: 104 | case MB_PDU_WR_MULT_COILS: 105 | case MB_PDU_WR_MULT_REGS: 106 | case MB_PDU_WR_FILE_REC: 107 | case MB_PDU_MASK_WR_REG: 108 | return 1; 109 | case MB_PDU_DIAG: 110 | switch (adu->pdu.diag_req.sub_func) 111 | { 112 | case MB_PDU_CLEAR_COUNTERS: 113 | return 1; 114 | } 115 | } 116 | return 0; 117 | } 118 | 119 | void mb_rtu_adu_set_header(mb_rtu_adu_t *adu, uint8_t addr) 120 | { 121 | adu->addr = addr; 122 | } 123 | 124 | ssize_t mb_rtu_adu_format_req(mb_rtu_adu_t *adu, char *buf, size_t len) 125 | { 126 | const uint8_t *start = (const uint8_t *)buf; 127 | uint16_t crc = 0; 128 | ssize_t pdu_num = 0; 129 | ssize_t num = 0; 130 | 131 | /* addr */ 132 | if (len < 1) 133 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 134 | buf[0] = adu->addr; 135 | num += 1; 136 | buf += 1; 137 | len -= 1; 138 | 139 | /* pdu */ 140 | pdu_num = mb_pdu_format_req(&adu->pdu, buf, len); 141 | if (pdu_num < 0) 142 | return pdu_num; 143 | num += pdu_num; 144 | buf += pdu_num; 145 | len -= pdu_num; 146 | 147 | /* crc */ 148 | if (len < 2) 149 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 150 | crc = mb_rtu_adu_calc_crc(start, num); 151 | buf[0] = (uint8_t)(crc); 152 | buf[1] = (uint8_t)(crc >> 8); 153 | num += 2; 154 | buf += 2; 155 | len -= 2; 156 | 157 | return num; 158 | } 159 | 160 | ssize_t mb_rtu_adu_format_resp(mb_rtu_adu_t *adu, char *buf, size_t len) 161 | { 162 | const uint8_t *start = (const uint8_t *)buf; 163 | uint16_t crc = 0; 164 | ssize_t pdu_num = 0; 165 | ssize_t num = 0; 166 | 167 | /* addr */ 168 | if (len < 1) 169 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 170 | buf[0] = adu->addr; 171 | num += 1; 172 | buf += 1; 173 | len -= 1; 174 | 175 | /* pdu */ 176 | pdu_num = mb_pdu_format_resp(&adu->pdu, buf, len); 177 | if (pdu_num < 0) 178 | return pdu_num; 179 | num += pdu_num; 180 | buf += pdu_num; 181 | len -= pdu_num; 182 | 183 | /* crc */ 184 | if (len < 2) 185 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 186 | crc = mb_rtu_adu_calc_crc(start, num); 187 | buf[0] = (uint8_t)(crc); 188 | buf[1] = (uint8_t)(crc >> 8); 189 | num += 2; 190 | buf += 2; 191 | len -= 2; 192 | 193 | return num; 194 | } 195 | 196 | ssize_t mb_rtu_adu_parse_req(mb_rtu_adu_t *adu, const char *buf, size_t len) 197 | { 198 | const uint8_t *start = (const uint8_t *)buf; 199 | ssize_t pdu_num = 0; 200 | ssize_t num = 0; 201 | 202 | memset(adu, 0, sizeof(mb_rtu_adu_t)); 203 | 204 | /* addr */ 205 | if (len < 1) 206 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 207 | adu->addr = buf[0]; 208 | num += 1; 209 | buf += 1; 210 | len -= 1; 211 | 212 | /* pdu */ 213 | if (len < 2) 214 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 215 | pdu_num = mb_pdu_parse_req(&adu->pdu, buf, len - 2); 216 | if (pdu_num < 0) 217 | return pdu_num; 218 | num += pdu_num; 219 | buf += pdu_num; 220 | len -= pdu_num; 221 | 222 | /* crc */ 223 | if (len < 2) 224 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 225 | if (!mb_rtu_adu_check_crc(start, num + 2)) 226 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 227 | num += 2; 228 | buf += 2; 229 | len -= 2; 230 | 231 | return num; 232 | } 233 | 234 | ssize_t mb_rtu_adu_parse_resp(mb_rtu_adu_t *adu, const char *buf, size_t len) 235 | { 236 | const uint8_t *start = (const uint8_t *)buf; 237 | ssize_t pdu_num = 0; 238 | ssize_t num = 0; 239 | 240 | memset(adu, 0, sizeof(mb_rtu_adu_t)); 241 | 242 | /* addr */ 243 | if (len < 1) 244 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 245 | adu->addr = buf[0]; 246 | num += 1; 247 | buf += 1; 248 | len -= 1; 249 | 250 | /* pdu */ 251 | if (len < 2) 252 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 253 | pdu_num = mb_pdu_parse_resp(&adu->pdu, buf, len - 2); 254 | if (pdu_num < 0) 255 | return pdu_num; 256 | num += pdu_num; 257 | buf += pdu_num; 258 | len -= pdu_num; 259 | 260 | /* crc */ 261 | if (len < 2) 262 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 263 | if (!mb_rtu_adu_check_crc(start, num + 2)) 264 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 265 | num += 2; 266 | buf += 2; 267 | len -= 2; 268 | 269 | return num; 270 | } 271 | 272 | int mb_rtu_adu_to_str(mb_rtu_adu_t *adu, char *buf, size_t len) 273 | { 274 | unsigned i = 0; 275 | uint16_t data_len = 0; 276 | uint8_t *data = NULL; 277 | int count = 0; 278 | int num = 0; 279 | 280 | num = snprintf(buf, len, "{addr: 0x%02x", adu->addr); 281 | buf += num; 282 | len = (num > len) ? 0 : len - num; 283 | count += num; 284 | 285 | num = snprintf(buf, len, ", func_code: 0x%02x", adu->pdu.func_code); 286 | buf += num; 287 | len = (num > len) ? 0 : len - num; 288 | count += num; 289 | 290 | data = adu->pdu.def.buf + 1; /* skip over func_code in pdu */ 291 | data_len = adu->pdu.data_len; 292 | 293 | if (data_len > 0) 294 | { 295 | num = snprintf(buf, len, ", data: ["); 296 | buf += num; 297 | len = (num > len) ? 0 : len - num; 298 | count += num; 299 | 300 | for (i = 0; i < data_len; i++) 301 | { 302 | if (i == 0) 303 | num = snprintf(buf, len, "0x%02x", data[i]); 304 | else 305 | num = snprintf(buf, len, " 0x%02x", data[i]); 306 | buf += num; 307 | len = (num > len) ? 0 : len - num; 308 | count += num; 309 | } 310 | 311 | num = snprintf(buf, len, "]"); 312 | buf += num; 313 | len = (num > len) ? 0 : len - num; 314 | count += num; 315 | } 316 | num = snprintf(buf, len, "}"); 317 | buf += num; 318 | len = (num > len) ? 0 : len - num; 319 | count += num; 320 | return count; 321 | } 322 | -------------------------------------------------------------------------------- /src/mb_rtu_con.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "mb_rtu_con.h" 36 | #include "mb_log.h" 37 | 38 | static int mb_rtu_con_set_non_blocking(int fd) 39 | { 40 | int flags = 0; 41 | int ret = 0; 42 | 43 | flags = fcntl(fd, F_GETFL, 0); 44 | if (flags < 0) 45 | { 46 | return -errno; 47 | } 48 | ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK); 49 | if (ret < 0) 50 | { 51 | return -errno; 52 | } 53 | return 0; 54 | } 55 | 56 | int mb_rtu_con_start_timer(int fd, time_t sec, long nsec) 57 | { 58 | struct itimerspec its = 59 | { 60 | .it_interval = 61 | { 62 | .tv_sec = 0, 63 | .tv_nsec = 0 64 | }, 65 | .it_value = 66 | { 67 | .tv_sec = sec, 68 | .tv_nsec = nsec 69 | } 70 | }; 71 | int ret = 0; 72 | 73 | ret = timerfd_settime(fd, 0, &its, NULL); 74 | if (ret < 0) 75 | { 76 | return -errno; 77 | } 78 | return 0; 79 | } 80 | 81 | int mb_rtu_con_read_timer(int fd) 82 | { 83 | uint64_t count = 0; 84 | ssize_t num = 0; 85 | 86 | num = read(fd, &count, sizeof(count)); 87 | if ((num < 0) && (errno != EAGAIN)) 88 | { 89 | return -errno; 90 | } 91 | return count ? 1 : 0; 92 | } 93 | 94 | int mb_rtu_con_create(mb_rtu_con_t *con, const char *dev) 95 | { 96 | struct termios options = {0}; 97 | fd_set readfds = {{0}}; 98 | int max_fd = 0; 99 | int ret = 0; 100 | 101 | memset(con, 0, sizeof(mb_rtu_con_t)); 102 | con->serial_fd = open(dev, O_RDWR | O_NOCTTY); 103 | if (con->serial_fd < 0) 104 | { 105 | memset(con, 0, sizeof(mb_rtu_con_t)); 106 | return -errno; 107 | } 108 | ret = mb_rtu_con_set_non_blocking(con->serial_fd); 109 | if (ret < 0) 110 | { 111 | close(con->serial_fd); 112 | memset(con, 0, sizeof(mb_rtu_con_t)); 113 | return ret; 114 | } 115 | tcflush(con->serial_fd, TCIFLUSH); 116 | cfmakeraw(&options); 117 | options.c_cflag |= CREAD | CLOCAL | PARENB; 118 | ret = cfsetispeed(&options, MB_RTU_CON_BAUD_RATE); 119 | if (ret < 0) 120 | { 121 | close(con->serial_fd); 122 | memset(con, 0, sizeof(mb_rtu_con_t)); 123 | return -errno; 124 | } 125 | ret = cfsetospeed(&options, MB_RTU_CON_BAUD_RATE); 126 | if (ret < 0) 127 | { 128 | close(con->serial_fd); 129 | memset(con, 0, sizeof(mb_rtu_con_t)); 130 | return -errno; 131 | } 132 | tcsetattr(con->serial_fd, TCSANOW, &options); 133 | con->t15_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); 134 | if (con->t15_fd < 0) 135 | { 136 | close(con->serial_fd); 137 | memset(con, 0, sizeof(mb_rtu_con_t)); 138 | return -errno; 139 | } 140 | con->t35_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); 141 | if (con->t35_fd < 0) 142 | { 143 | close(con->t15_fd); 144 | close(con->serial_fd); 145 | memset(con, 0, sizeof(mb_rtu_con_t)); 146 | return -errno; 147 | } 148 | while (1) 149 | { 150 | ret = mb_rtu_con_start_timer(con->t35_fd, MB_RTU_CON_T35_SEC, MB_RTU_CON_T35_NSEC); 151 | if (ret < 0) 152 | { 153 | return ret; 154 | } 155 | FD_ZERO(&readfds); 156 | FD_SET(con->serial_fd, &readfds); 157 | FD_SET(con->t35_fd, &readfds); 158 | max_fd = con->serial_fd; 159 | if (con->t35_fd > max_fd) 160 | max_fd = con->t35_fd; 161 | ret = select(max_fd + 1, &readfds, NULL, NULL, NULL); 162 | if (ret < 0) 163 | { 164 | return -errno; 165 | } 166 | ret = mb_rtu_con_read_timer(con->t35_fd); 167 | if (ret < 0) 168 | { 169 | return ret; 170 | } 171 | if (ret) 172 | { 173 | break; /* timer expired */ 174 | } 175 | } 176 | return 0; 177 | } 178 | 179 | void mb_rtu_con_destroy(mb_rtu_con_t *con) 180 | { 181 | close(con->t35_fd); 182 | close(con->t15_fd); 183 | close(con->serial_fd); 184 | memset(con, 0, sizeof(mb_rtu_con_t)); 185 | } 186 | 187 | ssize_t mb_rtu_con_send(mb_rtu_con_t *con, const char *buf, size_t len) 188 | { 189 | ssize_t num = 0; 190 | fd_set readfds = {{0}}; 191 | int ret = 0; 192 | 193 | num = write(con->serial_fd, buf, len); 194 | if (num < 0) 195 | { 196 | return -errno; 197 | } 198 | ret = mb_rtu_con_start_timer(con->t35_fd, MB_RTU_CON_T35_SEC, MB_RTU_CON_T35_NSEC); 199 | if (ret < 0) 200 | { 201 | return ret; 202 | } 203 | FD_ZERO(&readfds); 204 | FD_SET(con->t35_fd, &readfds); 205 | ret = select(con->t35_fd + 1, &readfds, NULL, NULL, NULL); 206 | if (ret < 0) 207 | { 208 | return -errno; 209 | } 210 | ret = mb_rtu_con_read_timer(con->t35_fd); 211 | if (ret < 0) 212 | { 213 | return ret; 214 | } 215 | mb_log_debug("sent %d bytes", num); 216 | return num; 217 | } 218 | 219 | static int mb_rtu_con_recv_wait(mb_rtu_con_t *con) 220 | { 221 | fd_set readfds = {{0}}; 222 | int ret = 0; 223 | 224 | FD_ZERO(&readfds); 225 | FD_SET(con->serial_fd, &readfds); 226 | ret = select(con->serial_fd + 1, &readfds, NULL, NULL, NULL); 227 | if (ret < 0) 228 | { 229 | return -errno; 230 | } 231 | return 0; 232 | } 233 | 234 | static int mb_rtu_con_recv_wait_timeout(mb_rtu_con_t *con, int timer_fd) 235 | { 236 | fd_set readfds = {{0}}; 237 | int max_fd = 0; 238 | int ret = 0; 239 | 240 | FD_ZERO(&readfds); 241 | FD_SET(con->serial_fd, &readfds); 242 | FD_SET(timer_fd, &readfds); 243 | max_fd = con->serial_fd; 244 | if (timer_fd > max_fd) 245 | max_fd = timer_fd; 246 | ret = select(max_fd + 1, &readfds, NULL, NULL, NULL); 247 | if (ret < 0) 248 | { 249 | return -errno; 250 | } 251 | ret = mb_rtu_con_read_timer(timer_fd); 252 | if (ret < 0) 253 | { 254 | return ret; 255 | } 256 | if (ret) 257 | { 258 | return -ETIMEDOUT; 259 | } 260 | return 0; 261 | } 262 | 263 | static ssize_t mb_rtu_con_recv_data(mb_rtu_con_t *con, char *buf, size_t len) 264 | { 265 | ssize_t count = 0; 266 | ssize_t num = 0; 267 | fd_set readfds = {{0}}; 268 | int max_fd = 0; 269 | int nok = 0; 270 | int ret = 0; 271 | 272 | while (1) 273 | { 274 | ret = mb_rtu_con_start_timer(con->t15_fd, MB_RTU_CON_T15_SEC, MB_RTU_CON_T15_NSEC); 275 | if (ret < 0) 276 | { 277 | return ret; 278 | } 279 | ret = mb_rtu_con_start_timer(con->t35_fd, MB_RTU_CON_T35_SEC, MB_RTU_CON_T35_NSEC); 280 | if (ret < 0) 281 | { 282 | return ret; 283 | } 284 | FD_ZERO(&readfds); 285 | FD_SET(con->serial_fd, &readfds); 286 | FD_SET(con->t15_fd, &readfds); 287 | FD_SET(con->t35_fd, &readfds); 288 | max_fd = con->serial_fd; 289 | if (con->t15_fd > max_fd) 290 | max_fd = con->t15_fd; 291 | if (con->t35_fd > max_fd) 292 | max_fd = con->t35_fd; 293 | ret = select(max_fd + 1, &readfds, NULL, NULL, NULL); 294 | if (ret < 0) 295 | { 296 | return -errno; 297 | } 298 | ret = mb_rtu_con_read_timer(con->t15_fd); 299 | if (ret < 0) 300 | { 301 | return ret; 302 | } 303 | if (ret) 304 | { 305 | break; /* timer expired */ 306 | } 307 | num = read(con->serial_fd, buf, len); 308 | if (num < 0) 309 | { 310 | return -errno; 311 | } 312 | buf += num; 313 | len -= num; 314 | count += num; 315 | } 316 | while (1) 317 | { 318 | FD_ZERO(&readfds); 319 | FD_SET(con->t35_fd, &readfds); 320 | max_fd = con->t35_fd; 321 | if (!nok) 322 | { 323 | FD_SET(con->serial_fd, &readfds); 324 | if (con->serial_fd > max_fd) 325 | max_fd = con->serial_fd; 326 | } 327 | ret = select(max_fd + 1, &readfds, NULL, NULL, NULL); 328 | if (ret < 0) 329 | { 330 | return -errno; 331 | } 332 | ret = mb_rtu_con_read_timer(con->t35_fd); 333 | if (ret < 0) 334 | { 335 | return ret; 336 | } 337 | if (ret) 338 | { 339 | break; /* timer expired */ 340 | } 341 | nok = 1; 342 | mb_log_debug("received data in between t1.5 and t3.5"); 343 | } 344 | if (nok) 345 | { 346 | return -EBADMSG; /* received data in between t1.5 and t3.5 */ 347 | } 348 | mb_log_debug("received %d bytes", count); 349 | return count; 350 | } 351 | 352 | ssize_t mb_rtu_con_recv(mb_rtu_con_t *con, char *buf, size_t len) 353 | { 354 | int ret = 0; 355 | 356 | ret = mb_rtu_con_recv_wait(con); 357 | if (ret < 0) 358 | { 359 | return ret; 360 | } 361 | return mb_rtu_con_recv_data(con, buf, len); 362 | } 363 | 364 | ssize_t mb_rtu_con_recv_timeout(mb_rtu_con_t *con, char *buf, size_t len, int timer_fd) 365 | { 366 | int ret = 0; 367 | 368 | ret = mb_rtu_con_recv_wait_timeout(con, timer_fd); 369 | if (ret < 0) 370 | { 371 | return ret; 372 | } 373 | return mb_rtu_con_recv_data(con, buf, len); 374 | } 375 | -------------------------------------------------------------------------------- /src/mb_rtu_master.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "mb_rtu_master.h" 33 | #include "mb_log.h" 34 | 35 | int mb_rtu_master_create(mb_rtu_master_t *master, const char *dev) 36 | { 37 | int ret = 0; 38 | 39 | memset(master, 0, sizeof(mb_rtu_master_t)); 40 | ret = mb_rtu_con_create(&master->con, dev); 41 | if (ret < 0) 42 | { 43 | return ret; 44 | } 45 | master->timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); 46 | if (master->timer_fd < 0) 47 | { 48 | mb_rtu_con_destroy(&master->con); 49 | memset(master, 0, sizeof(mb_rtu_master_t)); 50 | return -errno; 51 | } 52 | mb_log_notice("master bound to '%s'", dev); 53 | mb_log_notice("idle"); 54 | return 0; 55 | } 56 | 57 | void mb_rtu_master_destroy(mb_rtu_master_t *master) 58 | { 59 | close(master->timer_fd); 60 | mb_rtu_con_destroy(&master->con); 61 | memset(master, 0, sizeof(mb_rtu_master_t)); 62 | } 63 | 64 | int mb_rtu_master_exchange(mb_rtu_master_t *master, mb_rtu_adu_t *req, mb_rtu_adu_t *resp) 65 | { 66 | ssize_t num = 0; 67 | char msg_buf[256] = {0}; 68 | char buf[MB_RTU_ADU_MAX_LEN] = {0}; 69 | int ret = 0; 70 | 71 | if ((req->addr < MB_RTU_ADU_MIN_UNICAST_ADDR) || (req->addr > MB_RTU_ADU_MAX_UNICAST_ADDR)) 72 | { 73 | return -EINVAL; 74 | } 75 | num = mb_rtu_adu_format_req(req, buf, sizeof(buf)); 76 | if (num < 0) 77 | { 78 | return -EBADMSG; /* convert modbus error to errno value */ 79 | } 80 | mb_rtu_adu_to_str(req, msg_buf, sizeof(msg_buf)); 81 | mb_log_info("sending unicast request: %s", msg_buf); 82 | num = mb_rtu_con_send(&master->con, buf, num); 83 | if (num < 0) 84 | { 85 | return num; 86 | } 87 | mb_log_debug("starting response timer"); 88 | ret = mb_rtu_con_start_timer(master->timer_fd, MB_RTU_MASTER_RESPONSE_TIMEOUT_SEC, MB_RTU_MASTER_RESPONSE_TIMEOUT_NSEC); 89 | if (ret < 0) 90 | { 91 | return ret; 92 | } 93 | num = mb_rtu_con_recv_timeout(&master->con, buf, sizeof(buf), master->timer_fd); 94 | if (num < 0) 95 | { 96 | return num; 97 | } 98 | num = mb_rtu_adu_parse_resp(resp, buf, num); 99 | if (num < 0) 100 | { 101 | return -EBADMSG; /* convert modbus error to errno value */ 102 | } 103 | mb_rtu_adu_to_str(resp, msg_buf, sizeof(msg_buf)); 104 | mb_log_info("received response: %s", msg_buf); 105 | mb_log_notice("idle"); 106 | return 0; 107 | } 108 | 109 | int mb_rtu_master_broadcast(mb_rtu_master_t *master, mb_rtu_adu_t *req) 110 | { 111 | ssize_t num = 0; 112 | fd_set readfds = {{0}}; 113 | char msg_buf[256] = {0}; 114 | char buf[MB_RTU_ADU_MAX_LEN] = {0}; 115 | int ret = 0; 116 | 117 | if (req->addr != MB_RTU_ADU_BROADCAST_ADDR) 118 | { 119 | return -EINVAL; 120 | } 121 | num = mb_rtu_adu_format_req(req, buf, sizeof(buf)); 122 | if (num < 0) 123 | { 124 | return -EBADMSG; /* convert modbus error to errno value */ 125 | } 126 | mb_rtu_adu_to_str(req, msg_buf, sizeof(msg_buf)); 127 | mb_log_info("sending broadcast request: %s", msg_buf); 128 | num = mb_rtu_con_send(&master->con, buf, num); 129 | if (num < 0) 130 | { 131 | return num; 132 | } 133 | ret = mb_rtu_con_start_timer(master->timer_fd, MB_RTU_MASTER_TURNAROUND_DELAY_SEC, MB_RTU_MASTER_TURNAROUND_DELAY_NSEC); 134 | if (ret < 0) 135 | { 136 | return ret; 137 | } 138 | FD_ZERO(&readfds); 139 | FD_SET(master->timer_fd, &readfds); 140 | ret = select(master->timer_fd + 1, &readfds, NULL, NULL, NULL); 141 | if (ret < 0) 142 | { 143 | return -errno; 144 | } 145 | ret = mb_rtu_con_read_timer(master->timer_fd); 146 | if (ret < 0) 147 | { 148 | return ret; 149 | } 150 | mb_log_notice("idle"); 151 | return 0; 152 | } 153 | -------------------------------------------------------------------------------- /src/mb_rtu_slave.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include "mb_rtu_slave.h" 32 | #include "mb_log.h" 33 | 34 | static ssize_t mb_rtu_slave_send_resp(mb_rtu_slave_t *slave, mb_rtu_adu_t *resp) 35 | { 36 | ssize_t num = 0; 37 | char msg_buf[256] = {0}; 38 | char buf[MB_RTU_ADU_MAX_LEN] = {0}; 39 | 40 | num = mb_rtu_adu_format_resp(resp, buf, sizeof(buf)); 41 | if (num < 0) 42 | { 43 | return -EBADMSG; /* convert modbus error to errno value */ 44 | } 45 | mb_rtu_adu_to_str(resp, msg_buf, sizeof(msg_buf)); 46 | mb_log_info("sending response: %s", msg_buf); 47 | return mb_rtu_con_send(&slave->con, buf, num); 48 | } 49 | 50 | static ssize_t mb_rtu_slave_send_err_resp(mb_rtu_slave_t *slave, mb_rtu_adu_t *req, mb_rtu_adu_t *resp, int error) 51 | { 52 | int ret = 0; 53 | 54 | mb_rtu_adu_set_header(resp, slave->addr); 55 | ret = mb_pdu_set_err_resp(&resp->pdu, req->pdu.func_code + 0x80, error); 56 | if (ret < 0) 57 | { 58 | return -EBADMSG; /* convert modbus error to errno value */ 59 | } 60 | return mb_rtu_slave_send_resp(slave, resp); 61 | } 62 | 63 | static int mb_rtu_slave_handle_diag(mb_rtu_slave_t *slave, mb_rtu_adu_t *req, mb_rtu_adu_t *resp) 64 | { 65 | uint16_t val16 = 0; 66 | int ret = 0; 67 | 68 | switch (req->pdu.diag_req.sub_func) 69 | { 70 | case MB_PDU_QUERY_DATA: 71 | mb_rtu_adu_set_header(resp, slave->addr); 72 | ret = mb_pdu_set_diag_resp(&resp->pdu, MB_PDU_QUERY_DATA, req->pdu.diag_req.data, req->pdu.diag_req.num_data); 73 | if (ret < 0) 74 | { 75 | return -MB_PDU_EXCEPT_SERVER_DEV_FAIL; 76 | } 77 | return 0; 78 | case MB_PDU_CLEAR_COUNTERS: 79 | slave->bus_msg_count = 0; 80 | slave->bus_com_err_count = 0; 81 | slave->slave_excep_err_count = 0; 82 | slave->slave_msg_count = 0; 83 | slave->slave_no_resp_count = 0; 84 | mb_rtu_adu_set_header(resp, slave->addr); 85 | ret = mb_pdu_set_diag_resp(&resp->pdu, MB_PDU_CLEAR_COUNTERS, req->pdu.diag_req.data, req->pdu.diag_req.num_data); 86 | if (ret < 0) 87 | { 88 | return -MB_PDU_EXCEPT_SERVER_DEV_FAIL; 89 | } 90 | return 0; 91 | case MB_PDU_BUS_MSG_COUNT: 92 | val16 = slave->bus_msg_count; 93 | mb_rtu_adu_set_header(resp, slave->addr); 94 | ret = mb_pdu_set_diag_resp(&resp->pdu, MB_PDU_BUS_MSG_COUNT, &val16, 1); 95 | if (ret < 0) 96 | { 97 | return -MB_PDU_EXCEPT_SERVER_DEV_FAIL; 98 | } 99 | return 0; 100 | case MB_PDU_BUS_COM_ERR_COUNT: 101 | val16 = slave->bus_com_err_count; 102 | mb_rtu_adu_set_header(resp, slave->addr); 103 | ret = mb_pdu_set_diag_resp(&resp->pdu, MB_PDU_BUS_COM_ERR_COUNT, &val16, 1); 104 | if (ret < 0) 105 | { 106 | return -MB_PDU_EXCEPT_SERVER_DEV_FAIL; 107 | } 108 | return 0; 109 | case MB_PDU_SLAVE_EXCEP_ERR_COUNT: 110 | val16 = slave->slave_excep_err_count; 111 | mb_rtu_adu_set_header(resp, slave->addr); 112 | ret = mb_pdu_set_diag_resp(&resp->pdu, MB_PDU_SLAVE_EXCEP_ERR_COUNT, &val16, 1); 113 | if (ret < 0) 114 | { 115 | return -MB_PDU_EXCEPT_SERVER_DEV_FAIL; 116 | } 117 | return 0; 118 | case MB_PDU_SLAVE_MSG_COUNT: 119 | val16 = slave->slave_msg_count; 120 | mb_rtu_adu_set_header(resp, slave->addr); 121 | ret = mb_pdu_set_diag_resp(&resp->pdu, MB_PDU_SLAVE_MSG_COUNT, &val16, 1); 122 | if (ret < 0) 123 | { 124 | return -MB_PDU_EXCEPT_SERVER_DEV_FAIL; 125 | } 126 | return 0; 127 | case MB_PDU_SLAVE_NO_RESP_COUNT: 128 | val16 = slave->slave_no_resp_count; 129 | mb_rtu_adu_set_header(resp, slave->addr); 130 | ret = mb_pdu_set_diag_resp(&resp->pdu, MB_PDU_SLAVE_NO_RESP_COUNT, &val16, 1); 131 | if (ret < 0) 132 | { 133 | return -MB_PDU_EXCEPT_SERVER_DEV_FAIL; 134 | } 135 | return 0; 136 | default: 137 | return -MB_PDU_EXCEPT_ILLEGAL_FUNC; 138 | } 139 | } 140 | 141 | static int mb_rtu_slave_con_exchange(mb_rtu_slave_t *slave) 142 | { 143 | mb_rtu_adu_t resp = {0}; 144 | mb_rtu_adu_t req = {0}; 145 | ssize_t num = 0; 146 | char msg_buf[256] = {0}; 147 | char buf[MB_RTU_ADU_MAX_LEN] = {0}; 148 | int ret = 0; 149 | 150 | num = mb_rtu_con_recv(&slave->con, buf, sizeof(buf)); 151 | if (num < 0) 152 | { 153 | if (num == -EBADMSG) 154 | { 155 | slave->bus_com_err_count++; 156 | mb_log_debug("bus communication error: %d", slave->bus_com_err_count); 157 | } 158 | return num; 159 | } 160 | if ((num < MB_RTU_ADU_MIN_LEN) || (!mb_rtu_adu_check_crc((const uint8_t *)buf, num))) 161 | { 162 | slave->bus_com_err_count++; 163 | mb_log_debug("bus communication error: %d", slave->bus_com_err_count); 164 | return -EBADMSG; 165 | } 166 | slave->bus_msg_count++; 167 | mb_log_debug("bus message count: %d", slave->bus_msg_count); 168 | if ((buf[0] != slave->addr) && (buf[0] != MB_RTU_ADU_BROADCAST_ADDR)) 169 | { 170 | return 0; 171 | } 172 | slave->slave_msg_count++; 173 | mb_log_debug("slave message count: %d", slave->slave_msg_count); 174 | if (buf[0] == MB_RTU_ADU_BROADCAST_ADDR) 175 | { 176 | slave->slave_no_resp_count++; 177 | mb_log_debug("slave no response count: %d", slave->slave_no_resp_count); 178 | if (!mb_rtu_adu_valid_broadcast_req(&req)) 179 | { 180 | slave->slave_excep_err_count++; 181 | mb_log_debug("slave exception error count: %d", slave->slave_excep_err_count); 182 | return -EBADMSG; /* convert modbus error to errno value */ 183 | } 184 | } 185 | num = mb_rtu_adu_parse_req(&req, buf, num); 186 | if (num < 0) 187 | { 188 | slave->slave_excep_err_count++; 189 | mb_log_debug("slave exception error count: %d", slave->slave_excep_err_count); 190 | return -EBADMSG; /* convert modbus error to errno value */ 191 | } 192 | mb_rtu_adu_to_str(&req, msg_buf, sizeof(msg_buf)); 193 | if (req.addr == slave->addr) 194 | mb_log_info("received unicast request: %s", msg_buf); 195 | else 196 | mb_log_info("received broadcast request: %s", msg_buf); 197 | if (req.pdu.func_code == MB_PDU_DIAG) 198 | { 199 | mb_log_info("calling internal diagnostics handler"); 200 | ret = mb_rtu_slave_handle_diag(slave, &req, &resp); 201 | } 202 | else 203 | { 204 | mb_log_info("calling handler callback"); 205 | ret = (*slave->handler)(slave, &req, &resp); 206 | } 207 | if (ret < 0) 208 | { 209 | slave->slave_excep_err_count++; 210 | mb_log_debug("slave exception error count: %d", slave->slave_excep_err_count); 211 | } 212 | if (req.addr == MB_RTU_ADU_BROADCAST_ADDR) 213 | { 214 | if (ret < 0) 215 | { 216 | return -EBADMSG; /* convert modbus error to errno value */ 217 | } 218 | return 0; 219 | } 220 | else 221 | { 222 | if (ret < 0) 223 | { 224 | mb_rtu_slave_send_err_resp(slave, &req, &resp, -ret); 225 | return -EBADMSG; /* convert modbus error to errno value */ 226 | } 227 | return mb_rtu_slave_send_resp(slave, &resp); 228 | } 229 | } 230 | 231 | int mb_rtu_slave_create(mb_rtu_slave_t *slave, const char *dev, int addr, mb_rtu_slave_handler_t handler) 232 | { 233 | int ret = 0; 234 | 235 | memset(slave, 0, sizeof(mb_rtu_slave_t)); 236 | if ((addr < MB_RTU_ADU_MIN_UNICAST_ADDR) || (addr > MB_RTU_ADU_MAX_UNICAST_ADDR)) 237 | { 238 | return -EINVAL; 239 | } 240 | slave->addr = addr; 241 | ret = mb_rtu_con_create(&slave->con, dev); 242 | if (ret < 0) 243 | { 244 | memset(slave, 0, sizeof(mb_rtu_slave_t)); 245 | return ret; 246 | } 247 | slave->handler = handler; 248 | mb_log_notice("slave bound to '%s'", dev); 249 | mb_log_notice("idle"); 250 | return 0; 251 | } 252 | 253 | void mb_rtu_slave_destroy(mb_rtu_slave_t *slave) 254 | { 255 | mb_rtu_con_destroy(&slave->con); 256 | memset(slave, 0, sizeof(mb_rtu_slave_t)); 257 | } 258 | 259 | int mb_rtu_slave_run(mb_rtu_slave_t *slave) 260 | { 261 | int ret = 0; 262 | 263 | while (1) 264 | { 265 | ret = mb_rtu_slave_con_exchange(slave); 266 | if (ret < 0) 267 | { 268 | mb_log_warn("exchange: %s", strerror(-ret)); 269 | } 270 | mb_log_notice("idle"); 271 | } 272 | return 0; 273 | } 274 | -------------------------------------------------------------------------------- /src/mb_tcp_adu.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include "mb_tcp_adu.h" 32 | 33 | void mb_tcp_adu_set_header(mb_tcp_adu_t *adu, uint16_t trans_id, uint16_t proto_id, uint8_t unit_id) 34 | { 35 | adu->trans_id = trans_id; 36 | adu->proto_id = proto_id; 37 | adu->unit_id = unit_id; 38 | } 39 | 40 | ssize_t mb_tcp_adu_format_req(mb_tcp_adu_t *adu, char *buf, size_t len) 41 | { 42 | uint16_t *len_field = NULL; 43 | uint16_t val16 = 0; 44 | ssize_t pdu_num = 0; 45 | ssize_t num = 0; 46 | 47 | /* trans_id */ 48 | if (len < 2) 49 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 50 | val16 = htons(adu->trans_id); 51 | memcpy(buf, &val16, sizeof(val16)); 52 | num += 2; 53 | buf += 2; 54 | len -= 2; 55 | 56 | /* proto_id */ 57 | if (len < 2) 58 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 59 | val16 = htons(adu->proto_id); 60 | memcpy(buf, &val16, sizeof(val16)); 61 | num += 2; 62 | buf += 2; 63 | len -= 2; 64 | 65 | /* len */ 66 | if (len < 2) 67 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 68 | len_field = (uint16_t *)buf; 69 | num += 2; 70 | buf += 2; 71 | len -= 2; 72 | 73 | /* unit_id */ 74 | if (len < 1) 75 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 76 | memcpy(buf, &adu->unit_id, sizeof(adu->unit_id)); 77 | num += 1; 78 | buf += 1; 79 | len -= 1; 80 | 81 | /* pdu */ 82 | pdu_num = mb_pdu_format_req(&adu->pdu, buf, len); 83 | if (pdu_num < 0) 84 | return pdu_num; 85 | num += pdu_num; 86 | buf += pdu_num; 87 | len -= pdu_num; 88 | 89 | /* len */ 90 | adu->len = sizeof(uint8_t) + pdu_num; 91 | val16 = htons(adu->len); 92 | memcpy(len_field, &val16, sizeof(val16)); 93 | 94 | return num; 95 | } 96 | 97 | /* also used to format an error response */ 98 | ssize_t mb_tcp_adu_format_resp(mb_tcp_adu_t *adu, char *buf, size_t len) 99 | { 100 | uint16_t *len_field = NULL; 101 | uint16_t val16 = 0; 102 | ssize_t pdu_num = 0; 103 | ssize_t num = 0; 104 | 105 | /* trans_id */ 106 | if (len < 2) 107 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 108 | val16 = htons(adu->trans_id); 109 | memcpy(buf, &val16, sizeof(val16)); 110 | num += 2; 111 | buf += 2; 112 | len -= 2; 113 | 114 | /* proto_id */ 115 | if (len < 2) 116 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 117 | val16 = htons(adu->proto_id); 118 | memcpy(buf, &val16, sizeof(val16)); 119 | num += 2; 120 | buf += 2; 121 | len -= 2; 122 | 123 | /* len */ 124 | if (len < 2) 125 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 126 | len_field = (uint16_t *)buf; 127 | num += 2; 128 | buf += 2; 129 | len -= 2; 130 | 131 | /* unit_id */ 132 | if (len < 1) 133 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 134 | memcpy(buf, &adu->unit_id, sizeof(adu->unit_id)); 135 | num += 1; 136 | buf += 1; 137 | len -= 1; 138 | 139 | /* pdu */ 140 | pdu_num = mb_pdu_format_resp(&adu->pdu, buf, len); 141 | if (pdu_num < 0) 142 | return pdu_num; 143 | num += pdu_num; 144 | buf += pdu_num; 145 | len -= pdu_num; 146 | 147 | /* len */ 148 | adu->len = sizeof(uint8_t) + pdu_num; 149 | val16 = htons(adu->len); 150 | memcpy(len_field, &val16, sizeof(val16)); 151 | 152 | return num; 153 | } 154 | 155 | ssize_t mb_tcp_adu_parse_req(mb_tcp_adu_t *adu, const char *buf, size_t len) 156 | { 157 | ssize_t pdu_num = 0; 158 | ssize_t num = 0; 159 | 160 | memset(adu, 0, sizeof(mb_tcp_adu_t)); 161 | 162 | /* trans_id */ 163 | if (len < 2) 164 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 165 | adu->trans_id = ntohs(*(uint16_t *)buf); 166 | num += 2; 167 | buf += 2; 168 | len -= 2; 169 | 170 | /* proto_id */ 171 | if (len < 2) 172 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 173 | adu->proto_id = ntohs(*(uint16_t *)buf); 174 | num += 2; 175 | buf += 2; 176 | len -= 2; 177 | 178 | /* len */ 179 | if (len < 2) 180 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 181 | adu->len = ntohs(*(uint16_t *)buf); 182 | num += 2; 183 | buf += 2; 184 | len -= 2; 185 | 186 | /* unit_id */ 187 | if (len < 1) 188 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 189 | adu->unit_id = *buf; 190 | num += 1; 191 | buf += 1; 192 | len -= 1; 193 | 194 | /* pdu */ 195 | pdu_num = mb_pdu_parse_req(&adu->pdu, buf, len); 196 | if (pdu_num < 0) 197 | return pdu_num; 198 | num += pdu_num; 199 | buf += pdu_num; 200 | len -= pdu_num; 201 | 202 | if (adu->len != sizeof(uint8_t) + pdu_num) 203 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 204 | 205 | return num; 206 | } 207 | 208 | ssize_t mb_tcp_adu_parse_resp(mb_tcp_adu_t *adu, const char *buf, size_t len) 209 | { 210 | ssize_t pdu_num = 0; 211 | ssize_t num = 0; 212 | 213 | memset(adu, 0, sizeof(mb_tcp_adu_t)); 214 | 215 | /* trans_id */ 216 | if (len < 2) 217 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 218 | adu->trans_id = ntohs(*(uint16_t *)buf); 219 | num += 2; 220 | buf += 2; 221 | len -= 2; 222 | 223 | /* proto_id */ 224 | if (len < 2) 225 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 226 | adu->proto_id = ntohs(*(uint16_t *)buf); 227 | num += 2; 228 | buf += 2; 229 | len -= 2; 230 | 231 | /* len */ 232 | if (len < 2) 233 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 234 | adu->len = ntohs(*(uint16_t *)buf); 235 | num += 2; 236 | buf += 2; 237 | len -= 2; 238 | 239 | /* unit_id */ 240 | if (len < 1) 241 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 242 | adu->unit_id = *buf; 243 | num += 1; 244 | buf += 1; 245 | len -= 1; 246 | 247 | /* pdu */ 248 | pdu_num = mb_pdu_parse_resp(&adu->pdu, buf, len); 249 | if (pdu_num < 0) 250 | return pdu_num; 251 | num += pdu_num; 252 | buf += pdu_num; 253 | len -= pdu_num; 254 | 255 | if (adu->len != sizeof(uint8_t) + pdu_num) 256 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 257 | 258 | return num; 259 | } 260 | 261 | int mb_tcp_adu_to_str(mb_tcp_adu_t *adu, char *buf, size_t len) 262 | { 263 | unsigned i = 0; 264 | uint16_t data_len = 0; 265 | uint8_t *data = NULL; 266 | int count = 0; 267 | int num = 0; 268 | 269 | num = snprintf(buf, len, "{trans_id: 0x%04x", adu->trans_id); 270 | buf += num; 271 | len = (num > len) ? 0 : len - num; 272 | count += num; 273 | 274 | num = snprintf(buf, len, ", proto_id: 0x%04x", adu->proto_id); 275 | buf += num; 276 | len = (num > len) ? 0 : len - num; 277 | count += num; 278 | 279 | num = snprintf(buf, len, ", len: 0x%04x", adu->len); 280 | buf += num; 281 | len = (num > len) ? 0 : len - num; 282 | count += num; 283 | 284 | num = snprintf(buf, len, ", unit_id: 0x%02x", adu->unit_id); 285 | buf += num; 286 | len = (num > len) ? 0 : len - num; 287 | count += num; 288 | 289 | num = snprintf(buf, len, ", func_code: 0x%02x", adu->pdu.func_code); 290 | buf += num; 291 | len = (num > len) ? 0 : len - num; 292 | count += num; 293 | 294 | data = adu->pdu.def.buf + 1; /* skip over func_code in pdu */ 295 | data_len = adu->pdu.data_len; 296 | 297 | if (data_len > 0) 298 | { 299 | num = snprintf(buf, len, ", data: ["); 300 | buf += num; 301 | len = (num > len) ? 0 : len - num; 302 | count += num; 303 | 304 | for (i = 0; i < data_len; i++) 305 | { 306 | if (i == 0) 307 | num = snprintf(buf, len, "0x%02x", data[i]); 308 | else 309 | num = snprintf(buf, len, " 0x%02x", data[i]); 310 | buf += num; 311 | len = (num > len) ? 0 : len - num; 312 | count += num; 313 | } 314 | 315 | num = snprintf(buf, len, "]"); 316 | buf += num; 317 | len = (num > len) ? 0 : len - num; 318 | count += num; 319 | } 320 | num = snprintf(buf, len, "}"); 321 | buf += num; 322 | len = (num > len) ? 0 : len - num; 323 | count += num; 324 | return count; 325 | } 326 | -------------------------------------------------------------------------------- /src/mb_tcp_client.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "mb_tcp_client.h" 33 | #include "mb_log.h" 34 | 35 | static ssize_t mb_tcp_client_con_exchange(mb_tcp_client_t *client, int index, mb_tcp_adu_t *req, mb_tcp_adu_t *resp) 36 | { 37 | struct timeval timeout = {0}; 38 | mb_tcp_con_t *con = NULL; 39 | fd_set read_fds = {{0}}; 40 | ssize_t num = 0; 41 | char msg_buf[256] = {0}; 42 | char buf[MB_TCP_ADU_MAX_LEN] = {0}; 43 | int ret = 0; 44 | 45 | con = &client->con[index]; 46 | num = mb_tcp_adu_format_req(req, buf, sizeof(buf)); 47 | if (num < 0) 48 | { 49 | return -EBADMSG; /* convert modbus error to errno value */ 50 | } 51 | mb_tcp_adu_to_str(req, msg_buf, sizeof(msg_buf)); 52 | mb_log_info("[%d] sending: %s", index, msg_buf); 53 | num = mb_tcp_con_send(con, buf, num); 54 | if (num <= 0) 55 | { 56 | return num; 57 | } 58 | FD_ZERO(&read_fds); 59 | FD_SET(con->sd, &read_fds); 60 | timeout = client->timeout; 61 | while (1) 62 | { 63 | ret = select(con->sd + 1, &read_fds, NULL, NULL, &timeout); 64 | if (ret < 0) 65 | { 66 | return -errno; 67 | } 68 | if (ret == 0) 69 | { 70 | return -ETIMEDOUT; 71 | } 72 | if (!FD_ISSET(con->sd, &read_fds)) 73 | { 74 | continue; 75 | } 76 | num = mb_tcp_con_recv(con); 77 | if (num > 0) 78 | { 79 | break; 80 | } 81 | else if (num != -EAGAIN) 82 | { 83 | return num; 84 | } 85 | } 86 | num = mb_tcp_adu_parse_resp(resp, con->rx_buf, con->rx_end); 87 | if (num < 0) 88 | { 89 | return -EBADMSG; /* convert modbus error to errno value */ 90 | } 91 | mb_tcp_con_consume(con, num); 92 | mb_tcp_adu_to_str(resp, msg_buf, sizeof(msg_buf)); 93 | mb_log_info("[%d] received: %s", index, msg_buf); 94 | return num; 95 | } 96 | 97 | static int mb_tcp_client_find_con(mb_tcp_client_t *client, struct sockaddr_in *sin) 98 | { 99 | mb_tcp_con_t *con = NULL; 100 | int i = 0; 101 | 102 | for (i = 0; i < MB_TCP_CLIENT_MAX_CON; i++) 103 | { 104 | con = &client->con[i]; 105 | if (memcmp(&con->sin, sin, sizeof(struct sockaddr_in)) == 0) 106 | { 107 | mb_log_debug("found existing connection %d", i); 108 | return i; 109 | } 110 | } 111 | mb_log_debug("no existing connection found"); 112 | return -1; 113 | } 114 | 115 | static int mb_tcp_client_find_empty_con(mb_tcp_client_t *client) 116 | { 117 | mb_tcp_con_t *oldest = NULL; 118 | mb_tcp_con_t *con = NULL; 119 | int i = 0; 120 | int j = 0; 121 | 122 | for (i = 0; i < MB_TCP_CLIENT_MAX_CON; i++) 123 | { 124 | con = &client->con[i]; 125 | if (!mb_tcp_con_is_active(con)) 126 | { 127 | mb_log_debug("found empty connection %d", i); 128 | return i; 129 | } 130 | else if ((oldest == NULL) || (con->last_use < oldest->last_use)) 131 | { 132 | oldest = con; 133 | j = i; 134 | } 135 | } 136 | mb_log_debug("closing oldest connection %d", j); 137 | mb_tcp_con_close(con); 138 | return j; 139 | } 140 | 141 | void mb_tcp_client_create(mb_tcp_client_t *client, struct timeval timeout) 142 | { 143 | int i = 0; 144 | 145 | memset(client, 0, sizeof(mb_tcp_client_t)); 146 | mb_ip_auth_list_create(&client->auth); 147 | for (i = 0; i < MB_TCP_CLIENT_MAX_CON; i++) 148 | mb_tcp_con_create(&client->con[i], i); 149 | client->timeout = timeout; 150 | } 151 | 152 | void mb_tcp_client_destroy(mb_tcp_client_t *client) 153 | { 154 | int i = 0; 155 | 156 | for (i = 0; i < MB_TCP_CLIENT_MAX_CON; i++) 157 | mb_tcp_con_destroy(&client->con[i]); 158 | mb_ip_auth_list_destroy(&client->auth); 159 | memset(client, 0, sizeof(mb_tcp_client_t)); 160 | } 161 | 162 | int mb_tcp_client_authorise_addr(mb_tcp_client_t *client, const char *str) 163 | { 164 | mb_log_debug("authorising address %s", str); 165 | return mb_ip_auth_list_add_str(&client->auth, str); 166 | } 167 | 168 | static int mb_tcp_client_con_open(mb_tcp_client_t *client, int index, struct sockaddr_in *sin) 169 | { 170 | mb_tcp_con_t *con = NULL; 171 | int ret = 0; 172 | int sd = 0; 173 | 174 | con = &client->con[index]; 175 | sd = socket(PF_INET, SOCK_STREAM, 0); 176 | if (sd < 0) 177 | { 178 | return -errno; 179 | } 180 | ret = connect(sd, (struct sockaddr *)sin, sizeof(struct sockaddr_in)); 181 | if (ret < 0) 182 | { 183 | return -errno; 184 | } 185 | ret = mb_tcp_con_set_non_blocking(con->sd); 186 | if (ret < 0) 187 | { 188 | return ret; 189 | } 190 | mb_tcp_con_open(con, sd, sin); 191 | return 0; 192 | } 193 | 194 | int mb_tcp_client_exchange(mb_tcp_client_t *client, const char *host, in_port_t port, mb_tcp_adu_t *req, mb_tcp_adu_t *resp) 195 | { 196 | struct sockaddr_in server_sin = {0}; 197 | ssize_t num = 0; 198 | int index = 0; 199 | int ret = 0; 200 | 201 | server_sin.sin_family = AF_INET; 202 | server_sin.sin_port = htons(port); 203 | ret = inet_pton(AF_INET, host, &server_sin.sin_addr); 204 | if (ret < 0) 205 | { 206 | return -errno; 207 | } 208 | if (ret == 0) 209 | { 210 | return -EINVAL; 211 | } 212 | index = mb_tcp_client_find_con(client, &server_sin); 213 | if (index >= 0) 214 | { 215 | num = mb_tcp_client_con_exchange(client, index, req, resp); 216 | if (num > 0) 217 | { 218 | return num; 219 | } 220 | else if (num < 0) 221 | { 222 | mb_log_warn("exchange: %s", strerror(-num)); 223 | } 224 | mb_tcp_con_close(&client->con[index]); 225 | } 226 | mb_log_debug("attempting to establish new connection"); 227 | ret = mb_ip_auth_list_check_addr(&client->auth, &server_sin.sin_addr); 228 | if (ret < 0) 229 | { 230 | return ret; 231 | } 232 | if (ret == 0) 233 | { 234 | mb_log_warn("rejecting unauthorised connection to address %s and port %u", host, port); 235 | return -EACCES; 236 | } 237 | mb_log_info("connection with address %s and port %u authorised", host, port); 238 | index = mb_tcp_client_find_empty_con(client); 239 | ret = mb_tcp_client_con_open(client, index, &server_sin); 240 | if (ret < 0) 241 | { 242 | return ret; 243 | } 244 | num = mb_tcp_client_con_exchange(client, index, req, resp); 245 | if (num == 0) 246 | { 247 | mb_log_info("[%d] connection closed remotely", index); 248 | mb_tcp_con_close(&client->con[index]); 249 | } 250 | else if (num < 0) 251 | { 252 | mb_tcp_con_close(&client->con[index]); 253 | } 254 | return num; 255 | } 256 | -------------------------------------------------------------------------------- /src/mb_tcp_con.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "mb_tcp_con.h" 34 | #include "mb_log.h" 35 | 36 | static int mb_tcp_con_rx_complete(mb_tcp_con_t *con) 37 | { 38 | uint16_t len = 0; 39 | 40 | if (con->rx_end < MB_TCP_ADU_LEN_OFF + sizeof(uint16_t)) 41 | return 0; 42 | len = ntohs(*(uint16_t *)(con->rx_buf + MB_TCP_ADU_LEN_OFF)); 43 | if (MB_TCP_ADU_LEN_OFF + sizeof(uint16_t) + len > con->rx_end) 44 | return 0; 45 | return 1; 46 | } 47 | 48 | int mb_tcp_con_set_non_blocking(int sd) 49 | { 50 | int flags = 0; 51 | int ret = 0; 52 | 53 | flags = fcntl(sd, F_GETFL, 0); 54 | if (flags < 0) 55 | { 56 | return -errno; 57 | } 58 | ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); 59 | if (ret < 0) 60 | { 61 | return -errno; 62 | } 63 | return 0; 64 | } 65 | 66 | void mb_tcp_con_create(mb_tcp_con_t *con, int index) 67 | { 68 | memset(con, 0, sizeof(mb_tcp_con_t)); 69 | con->index = index; 70 | con->sd = MB_TCP_CON_SOCKET_CLOSED; 71 | } 72 | 73 | void mb_tcp_con_destroy(mb_tcp_con_t *con) 74 | { 75 | if (con->sd != MB_TCP_CON_SOCKET_CLOSED) 76 | { 77 | mb_tcp_con_close(con); 78 | } 79 | memset(con, 0, sizeof(mb_tcp_con_t)); 80 | } 81 | 82 | void mb_tcp_con_open(mb_tcp_con_t *con, int sd, struct sockaddr_in *sin) 83 | { 84 | con->sd = sd; 85 | con->last_use = time(NULL); 86 | memcpy(&con->sin, sin, sizeof(struct sockaddr_in)); 87 | mb_log_info("[%d] connection opened", con->index); 88 | } 89 | 90 | void mb_tcp_con_close(mb_tcp_con_t *con) 91 | { 92 | close(con->sd); 93 | con->sd = MB_TCP_CON_SOCKET_CLOSED; 94 | mb_log_info("[%d] connection closed locally", con->index); 95 | } 96 | 97 | ssize_t mb_tcp_con_send(mb_tcp_con_t *con, char *buf, size_t len) 98 | { 99 | ssize_t num = 0; 100 | 101 | con->last_use = time(NULL); 102 | num = send(con->sd, buf, len, 0); 103 | if (num == 0) 104 | { 105 | mb_log_info("[%d] connection closed remotely", con->index); 106 | return 0; 107 | } 108 | else if (num < 0) 109 | { 110 | return -errno; 111 | } 112 | mb_log_debug("[%d] sent %d bytes", con->index, num); 113 | return num; 114 | } 115 | 116 | ssize_t mb_tcp_con_recv(mb_tcp_con_t *con) 117 | { 118 | ssize_t num = 0; 119 | 120 | con->last_use = time(NULL); 121 | num = recv(con->sd, con->rx_buf + con->rx_end, sizeof(con->rx_buf) - con->rx_end, 0); 122 | if (num == 0) 123 | { 124 | mb_log_info("[%d] connection closed remotely", con->index); 125 | return 0; 126 | } 127 | else if (num < 0) 128 | { 129 | return -errno; 130 | } 131 | mb_log_debug("[%d] received %d bytes", con->index, num); 132 | con->rx_end += num; 133 | if (!mb_tcp_con_rx_complete(con)) 134 | { 135 | mb_log_debug("[%d] buffering received message fragment", con->index); 136 | return -EAGAIN; /* need to receive more data before processing a complete message */ 137 | } 138 | mb_log_debug("[%d] received complete message", con->index); 139 | return num; 140 | } 141 | 142 | void mb_tcp_con_consume(mb_tcp_con_t *con, size_t num) 143 | { 144 | /* shift receive buffer left in case of back-to-back messages */ 145 | memmove(con->rx_buf, con->rx_buf + num, con->rx_end - num); 146 | con->rx_end -= num; 147 | if (con->rx_end > 0) 148 | { 149 | mb_log_debug("[%d] buffering received back-to-back message", con->index); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/mb_tcp_server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "mb_tcp_server.h" 34 | #include "mb_log.h" 35 | 36 | #define MB_TCP_SERVER_BUF_LEN 128 37 | #define MB_TCP_SERVER_BACKLOG 10 38 | 39 | static ssize_t mb_tcp_server_send_resp(mb_tcp_server_t *server, int index, mb_tcp_adu_t *resp) 40 | { 41 | ssize_t num = 0; 42 | char msg_buf[256] = {0}; 43 | char buf[MB_TCP_ADU_MAX_LEN] = {0}; 44 | 45 | num = mb_tcp_adu_format_resp(resp, buf, sizeof(buf)); 46 | if (num < 0) 47 | { 48 | return -EBADMSG; 49 | } 50 | mb_tcp_adu_to_str(resp, msg_buf, sizeof(msg_buf)); 51 | mb_log_info("[%d] sending: %s", index, msg_buf); 52 | return mb_tcp_con_send(&server->con[index], buf, num); 53 | } 54 | 55 | static ssize_t mb_tcp_server_send_err_resp(mb_tcp_server_t *server, int index, mb_tcp_adu_t *req, mb_tcp_adu_t *resp, int error) 56 | { 57 | int ret = 0; 58 | 59 | mb_tcp_adu_set_header(resp, req->trans_id, req->proto_id, MB_TCP_SERVER_UNIT_ID); 60 | ret = mb_pdu_set_err_resp(&resp->pdu, req->pdu.func_code + 0x80, error); 61 | if (ret < 0) 62 | { 63 | return -EBADMSG; 64 | } 65 | return mb_tcp_server_send_resp(server, index, resp); 66 | } 67 | 68 | static size_t mb_tcp_server_con_exchange(mb_tcp_server_t *server, int index) 69 | { 70 | mb_tcp_con_t *con = NULL; 71 | mb_tcp_adu_t resp = {0}; 72 | mb_tcp_adu_t req = {0}; 73 | char msg_buf[256] = {0}; 74 | ssize_t num = 0; 75 | int ret = 0; 76 | 77 | con = &server->con[index]; 78 | num = mb_tcp_con_recv(con); 79 | if (num <= 0) 80 | { 81 | return num; 82 | } 83 | num = mb_tcp_adu_parse_req(&req, con->rx_buf, con->rx_end); 84 | if (num < 0) 85 | { 86 | mb_tcp_server_send_err_resp(server, index, &req, &resp, -num); 87 | return -EBADMSG; 88 | } 89 | mb_tcp_adu_to_str(&req, msg_buf, sizeof(msg_buf)); 90 | mb_log_info("[%d] received: %s", index, msg_buf); 91 | mb_tcp_con_consume(con, num); 92 | mb_log_info("[%d] calling handler callback", index); 93 | ret = (*server->handler)(server, &req, &resp); 94 | if (ret < 0) 95 | { 96 | mb_tcp_server_send_err_resp(server, index, &req, &resp, -ret); 97 | return -EBADMSG; 98 | } 99 | return mb_tcp_server_send_resp(server, index, &resp); 100 | } 101 | 102 | static int mb_tcp_server_find_empty_con(mb_tcp_server_t *server) 103 | { 104 | mb_tcp_con_t *oldest = NULL; 105 | mb_tcp_con_t *con = NULL; 106 | int j = 0; 107 | int i = 0; 108 | 109 | for (i = 0; i < MB_TCP_SERVER_MAX_CON; i++) 110 | { 111 | con = &server->con[i]; 112 | if (!mb_tcp_con_is_active(con)) 113 | { 114 | mb_log_debug("found empty connection %d", i); 115 | return i; 116 | } 117 | else if ((oldest == NULL) || (con->last_use < oldest->last_use)) 118 | { 119 | oldest = con; 120 | j = i; 121 | } 122 | } 123 | mb_log_debug("closing oldest connection %d", j); 124 | mb_tcp_con_close(oldest); 125 | return j; 126 | } 127 | 128 | int mb_tcp_server_create(mb_tcp_server_t *server, const char *host, uint16_t port, mb_tcp_server_handler_t handler) 129 | { 130 | struct sockaddr_in server_sin = {0}; 131 | int opt_val = 0; 132 | int ret = 0; 133 | int i = 0; 134 | 135 | memset(server, 0, sizeof(mb_tcp_server_t)); 136 | server->sd = MB_TCP_SERVER_SOCKET_CLOSED; 137 | mb_ip_auth_list_create(&server->auth); 138 | for (i = 0; i < MB_TCP_SERVER_MAX_CON; i++) 139 | mb_tcp_con_create(&server->con[i], i); 140 | server->handler = handler; 141 | server->sd = socket(PF_INET, SOCK_STREAM, 0); 142 | if (server->sd == -1) 143 | { 144 | mb_tcp_server_destroy(server); 145 | return -errno; 146 | } 147 | opt_val = 1; 148 | ret = setsockopt(server->sd, SOL_SOCKET, SO_REUSEADDR, &opt_val, (socklen_t)sizeof(opt_val)); 149 | if (ret < 0) 150 | { 151 | mb_tcp_server_destroy(server); 152 | return -errno; 153 | } 154 | server_sin.sin_family = AF_INET; 155 | server_sin.sin_port = htons(port); 156 | ret = inet_pton(AF_INET, host, &server_sin.sin_addr); 157 | if (ret < 0) 158 | { 159 | mb_tcp_server_destroy(server); 160 | return -errno; 161 | } 162 | if (ret == 0) 163 | { 164 | mb_tcp_server_destroy(server); 165 | return -EINVAL; 166 | } 167 | ret = bind(server->sd, (struct sockaddr *)&server_sin, sizeof(server_sin)); 168 | if (ret < 0) 169 | { 170 | mb_tcp_server_destroy(server); 171 | return -errno; 172 | } 173 | ret = mb_tcp_con_set_non_blocking(server->sd); 174 | if (ret < 0) 175 | { 176 | mb_tcp_server_destroy(server); 177 | return ret; 178 | } 179 | mb_log_info("bound to address %s and port %d", host, port); 180 | return 0; 181 | } 182 | 183 | void mb_tcp_server_destroy(mb_tcp_server_t *server) 184 | { 185 | int i = 0; 186 | 187 | for (i = 0; i < MB_TCP_SERVER_MAX_CON; i++) 188 | mb_tcp_con_destroy(&server->con[i]); 189 | mb_ip_auth_list_destroy(&server->auth); 190 | if (server->sd != MB_TCP_SERVER_SOCKET_CLOSED) 191 | close(server->sd); 192 | memset(server, 0, sizeof(mb_tcp_server_t)); 193 | } 194 | 195 | int mb_tcp_server_authorise_addr(mb_tcp_server_t *server, const char *str) 196 | { 197 | mb_log_debug("authorising address %s", str); 198 | return mb_ip_auth_list_add_str(&server->auth, str); 199 | } 200 | 201 | static int mb_tcp_server_handle_new_con(mb_tcp_server_t *server) 202 | { 203 | struct sockaddr_in client_sin = {0}; 204 | const char *p = NULL; 205 | socklen_t client_sin_len = 0; 206 | char buf[MB_TCP_SERVER_BUF_LEN] = {0}; 207 | int index = 0; 208 | int ret = 0; 209 | int sd = 0; 210 | 211 | client_sin_len = sizeof(struct sockaddr_in); 212 | sd = accept(server->sd, (struct sockaddr *)&client_sin, &client_sin_len); 213 | if (sd < 0) 214 | { 215 | return -errno; 216 | } 217 | ret = mb_tcp_con_set_non_blocking(sd); 218 | if (ret < 0) 219 | { 220 | close(sd); 221 | return -errno; 222 | } 223 | ret = mb_ip_auth_list_check_addr(&server->auth, &client_sin.sin_addr); 224 | if (ret < 0) 225 | { 226 | close(sd); 227 | return ret; 228 | } 229 | if (ret == 0) 230 | { 231 | close(sd); 232 | mb_log_warn("rejecting unauthorised connection with address %s and port %u", buf, ntohs(client_sin.sin_port)); 233 | return -EACCES; 234 | } 235 | p = inet_ntop(AF_INET, &client_sin.sin_addr, buf, sizeof(buf)); 236 | if (p == NULL) 237 | { 238 | close(sd); 239 | return -errno; 240 | } 241 | mb_log_info("connection with address %s and port %u authorised", buf, ntohs(client_sin.sin_port)); 242 | index = mb_tcp_server_find_empty_con(server); 243 | mb_tcp_con_open(&server->con[index], sd, &client_sin); 244 | return 0; 245 | } 246 | 247 | int mb_tcp_server_run(mb_tcp_server_t *server) 248 | { 249 | mb_tcp_con_t *con = NULL; 250 | fd_set read_fds = {{0}}; 251 | ssize_t num = 0; 252 | int max_fd = 0; 253 | int ret = 0; 254 | int i = 0; 255 | 256 | ret = listen(server->sd, MB_TCP_SERVER_BACKLOG); 257 | if (ret < 0) 258 | { 259 | return -errno; 260 | } 261 | mb_log_notice("listening..."); 262 | while (1) 263 | { 264 | FD_ZERO(&read_fds); 265 | FD_SET(server->sd, &read_fds); 266 | max_fd = server->sd; 267 | for (i = 0; i < MB_TCP_SERVER_MAX_CON; i++) 268 | { 269 | con = &server->con[i]; 270 | if (mb_tcp_con_is_active(con)) 271 | { 272 | FD_SET(con->sd, &read_fds); 273 | if (con->sd > max_fd) 274 | max_fd = con->sd; 275 | } 276 | } 277 | ret = select(max_fd + 1, &read_fds, NULL, NULL, NULL); 278 | if (ret < 0) 279 | { 280 | return -errno; 281 | } 282 | for (i = 0; i < MB_TCP_SERVER_MAX_CON; i++) 283 | { 284 | con = &server->con[i]; 285 | if ((mb_tcp_con_is_active(con)) && (FD_ISSET(con->sd, &read_fds))) 286 | { 287 | num = mb_tcp_server_con_exchange(server, i); 288 | if (num == 0) 289 | { 290 | mb_tcp_con_close(con); 291 | } 292 | else if ((num < 0) && (num != -EAGAIN)) 293 | { 294 | mb_log_warn("[%d] exchange: %s", i, strerror(-num)); 295 | mb_tcp_con_close(con); 296 | } 297 | } 298 | } 299 | if (FD_ISSET(server->sd, &read_fds)) 300 | { 301 | ret = mb_tcp_server_handle_new_con(server); 302 | if (ret < 0) 303 | { 304 | return ret; 305 | } 306 | } 307 | } 308 | return 0; 309 | } 310 | -------------------------------------------------------------------------------- /test/mb_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include "mb_test.h" 30 | 31 | int mb_test_run(mb_test_func_t *func, size_t num) 32 | { 33 | mb_test_result_t result = 0; 34 | unsigned count = 0; 35 | unsigned pass = 0; 36 | unsigned i = 0; 37 | 38 | printf("----------------------------------------------------------------------------------------------------\n"); 39 | for (i = 0; i < num; i++) 40 | { 41 | result = (*func[i])(); 42 | if (result == PASS) 43 | { 44 | pass++; 45 | printf(" - Pass\n"); 46 | } 47 | else 48 | { 49 | printf(" - FAIL\n"); 50 | } 51 | count++; 52 | } 53 | printf("----------------------------------------------------------------------------------------------------\n"); 54 | printf("Pass %u/%u\n", pass, count); 55 | printf("----------------------------------------------------------------------------------------------------\n"); 56 | return (pass == count) ? 1 : 0; 57 | } 58 | -------------------------------------------------------------------------------- /test/mb_test.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef MB_TEST_H 29 | #define MB_TEST_H 30 | 31 | #include 32 | 33 | typedef enum 34 | { 35 | FAIL = 0, 36 | PASS = 1 37 | } 38 | mb_test_result_t; 39 | 40 | typedef mb_test_result_t (*mb_test_func_t)(void); 41 | 42 | int mb_test_run(mb_test_func_t *func, size_t num); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /test_mb_ip_auth/Makefile: -------------------------------------------------------------------------------- 1 | I=../include 2 | S=../src 3 | T=../test 4 | 5 | CC = gcc 6 | CFLAGS = -Wall -g -I$(I) -I$(T) 7 | LD = gcc 8 | LDFLAGS = 9 | INCS = $(I)/mb_ip_auth.h $(T)/mb_test.h 10 | OBJS = test_mb_ip_auth.o mb_ip_auth.o mb_test.o 11 | LIBS = 12 | PROG = test_mb_ip_auth 13 | RM = /bin/rm -f 14 | 15 | $(PROG): $(OBJS) 16 | $(LD) $(LDFLAGS) $(OBJS) -o $(PROG) $(LIBS) 17 | 18 | test_mb_ip_auth.o: test_mb_ip_auth.c $(INCS) 19 | $(CC) $(CFLAGS) -c test_mb_ip_auth.c 20 | 21 | mb_ip_auth.o: $(S)/mb_ip_auth.c $(INCS) 22 | $(CC) $(CFLAGS) -c $(S)/mb_ip_auth.c 23 | 24 | mb_test.o: $(T)/mb_test.c $(INCS) 25 | $(CC) $(CFLAGS) -c $(T)/mb_test.c 26 | 27 | clean: 28 | $(RM) $(PROG) $(OBJS) 29 | -------------------------------------------------------------------------------- /test_mb_ip_auth/test_mb_ip_auth.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include "mb_ip_auth.h" 31 | #include "mb_test.h" 32 | 33 | int print_cols = 93; 34 | 35 | mb_test_result_t test_mb_ip_auth_addr(void) 36 | { 37 | mb_ip_auth_list_t auth = {0}; 38 | struct in_addr addr1 = {0}; 39 | struct in_addr addr2 = {0}; 40 | const char *str1 = "127.0.0.1"; 41 | const char *str2 = "10.10.10.10"; 42 | int ret = 0; 43 | 44 | printf("%-*s", print_cols, "test 1: add addresses and authorise addresses"); 45 | 46 | ret = inet_pton(AF_INET, str1, &addr1); 47 | if (ret <= 0) 48 | { 49 | return FAIL; 50 | } 51 | ret = inet_pton(AF_INET, str2, &addr2); 52 | if (ret <= 0) 53 | { 54 | return FAIL; 55 | } 56 | 57 | mb_ip_auth_list_create(&auth); 58 | 59 | /* check for first address */ 60 | ret = mb_ip_auth_list_check_addr(&auth, &addr1); 61 | if (ret != 0) 62 | { 63 | mb_ip_auth_list_destroy(&auth); 64 | return FAIL; 65 | } 66 | 67 | /* add first address */ 68 | ret = mb_ip_auth_list_add_addr(&auth, &addr1); 69 | if (ret < 0) 70 | { 71 | mb_ip_auth_list_destroy(&auth); 72 | return FAIL; 73 | } 74 | 75 | /* check for first address */ 76 | ret = mb_ip_auth_list_check_addr(&auth, &addr1); 77 | if (ret != 1) 78 | { 79 | mb_ip_auth_list_destroy(&auth); 80 | return FAIL; 81 | } 82 | 83 | /* check for second address */ 84 | ret = mb_ip_auth_list_check_addr(&auth, &addr2); 85 | if (ret != 0) 86 | { 87 | mb_ip_auth_list_destroy(&auth); 88 | return FAIL; 89 | } 90 | 91 | /* add second address */ 92 | ret = mb_ip_auth_list_add_addr(&auth, &addr2); 93 | if (ret < 0) 94 | { 95 | mb_ip_auth_list_destroy(&auth); 96 | return FAIL; 97 | } 98 | 99 | /* check for first address */ 100 | ret = mb_ip_auth_list_check_addr(&auth, &addr1); 101 | if (ret != 1) 102 | { 103 | mb_ip_auth_list_destroy(&auth); 104 | return FAIL; 105 | } 106 | 107 | /* check for second address */ 108 | ret = mb_ip_auth_list_check_addr(&auth, &addr2); 109 | if (ret != 1) 110 | { 111 | mb_ip_auth_list_destroy(&auth); 112 | return FAIL; 113 | } 114 | 115 | mb_ip_auth_list_destroy(&auth); 116 | 117 | return PASS; 118 | } 119 | 120 | mb_test_result_t test_mb_ip_auth_str(void) 121 | { 122 | mb_ip_auth_list_t auth = {0}; 123 | const char *str1 = "127.0.0.1"; 124 | const char *str2 = "10.10.10.10"; 125 | int ret = 0; 126 | 127 | printf("%-*s", print_cols, "test 2: add address strings and authorise addresses"); 128 | 129 | mb_ip_auth_list_create(&auth); 130 | 131 | /* check for first address */ 132 | ret = mb_ip_auth_list_check_str(&auth, str1); 133 | if (ret != 0) 134 | { 135 | mb_ip_auth_list_destroy(&auth); 136 | return FAIL; 137 | } 138 | 139 | /* add first address */ 140 | ret = mb_ip_auth_list_add_str(&auth, str1); 141 | if (ret < 0) 142 | { 143 | mb_ip_auth_list_destroy(&auth); 144 | return FAIL; 145 | } 146 | 147 | /* check for first address */ 148 | ret = mb_ip_auth_list_check_str(&auth, str1); 149 | if (ret != 1) 150 | { 151 | mb_ip_auth_list_destroy(&auth); 152 | return FAIL; 153 | } 154 | 155 | /* check for second address */ 156 | ret = mb_ip_auth_list_check_str(&auth, str2); 157 | if (ret != 0) 158 | { 159 | mb_ip_auth_list_destroy(&auth); 160 | return FAIL; 161 | } 162 | 163 | /* add second address */ 164 | ret = mb_ip_auth_list_add_str(&auth, str2); 165 | if (ret < 0) 166 | { 167 | mb_ip_auth_list_destroy(&auth); 168 | return FAIL; 169 | } 170 | 171 | /* check for first address */ 172 | ret = mb_ip_auth_list_check_str(&auth, str1); 173 | if (ret != 1) 174 | { 175 | mb_ip_auth_list_destroy(&auth); 176 | return FAIL; 177 | } 178 | 179 | /* check for second address */ 180 | ret = mb_ip_auth_list_check_str(&auth, str2); 181 | if (ret != 1) 182 | { 183 | mb_ip_auth_list_destroy(&auth); 184 | return FAIL; 185 | } 186 | 187 | mb_ip_auth_list_destroy(&auth); 188 | 189 | return PASS; 190 | } 191 | 192 | int main(void) 193 | { 194 | mb_test_func_t func[] = {test_mb_ip_auth_addr, 195 | test_mb_ip_auth_str}; 196 | 197 | return mb_test_run(func, sizeof(func) / sizeof(func[0])); 198 | } 199 | -------------------------------------------------------------------------------- /test_mb_pdu/Makefile: -------------------------------------------------------------------------------- 1 | I=../include 2 | S=../src 3 | T=../test 4 | 5 | CC = gcc 6 | CFLAGS = -Wall -g -I$(I) -I$(T) 7 | LD = gcc 8 | LDFLAGS = 9 | INCS = $(I)/mb_pdu.h $(T)/mb_test.h 10 | OBJS = test_mb_pdu.o mb_pdu.o mb_test.o 11 | LIBS = 12 | PROG = test_mb_pdu 13 | RM = /bin/rm -f 14 | 15 | $(PROG): $(OBJS) 16 | $(LD) $(LDFLAGS) $(OBJS) -o $(PROG) $(LIBS) 17 | 18 | test_mb_pdu.o: test_mb_pdu.c $(INCS) 19 | $(CC) $(CFLAGS) -c test_mb_pdu.c 20 | 21 | mb_pdu.o: $(S)/mb_pdu.c $(INCS) 22 | $(CC) $(CFLAGS) -c $(S)/mb_pdu.c 23 | 24 | mb_test.o: $(T)/mb_test.c $(INCS) 25 | $(CC) $(CFLAGS) -c $(T)/mb_test.c 26 | 27 | clean: 28 | $(RM) $(PROG) $(OBJS) 29 | -------------------------------------------------------------------------------- /test_mb_rtu_adu/Makefile: -------------------------------------------------------------------------------- 1 | I=../include 2 | S=../src 3 | T=../test 4 | 5 | CC = gcc 6 | CFLAGS = -Wall -g -I$(I) -I$(T) 7 | LD = gcc 8 | LDFLAGS = 9 | INCS = $(I)/mb_rtu_adu.h $(I)/mb_pdu.h $(T)/mb_test.h 10 | OBJS = test_mb_rtu_adu.o mb_rtu_adu.o mb_pdu.o mb_test.o 11 | LIBS = 12 | PROG = test_mb_rtu_adu 13 | RM = /bin/rm -f 14 | 15 | $(PROG): $(OBJS) 16 | $(LD) $(LDFLAGS) $(OBJS) -o $(PROG) $(LIBS) 17 | 18 | test_mb_rtu_adu.o: test_mb_rtu_adu.c $(INCS) 19 | $(CC) $(CFLAGS) -c test_mb_rtu_adu.c 20 | 21 | mb_rtu_adu.o: $(S)/mb_rtu_adu.c $(INCS) 22 | $(CC) $(CFLAGS) -c $(S)/mb_rtu_adu.c 23 | 24 | mb_pdu.o: $(S)/mb_pdu.c $(INCS) 25 | $(CC) $(CFLAGS) -c $(S)/mb_pdu.c 26 | 27 | mb_test.o: $(T)/mb_test.c $(INCS) 28 | $(CC) $(CFLAGS) -c $(T)/mb_test.c 29 | 30 | clean: 31 | $(RM) $(PROG) $(OBJS) 32 | -------------------------------------------------------------------------------- /test_mb_rtu_master/Makefile: -------------------------------------------------------------------------------- 1 | I=../include 2 | S=../src 3 | 4 | CC = gcc 5 | CFLAGS = -Wall -g -I$(I) 6 | LD = gcc 7 | LDFLAGS = 8 | INCS = $(I)/mb_rtu_master.h $(I)/mb_rtu_con.h $(I)/mb_rtu_adu.h $(I)/mb_pdu.h $(I)/mb_log.h 9 | OBJS = test_mb_rtu_master.o mb_rtu_master.o mb_rtu_con.o mb_rtu_adu.o mb_pdu.o mb_log.o 10 | LIBS = 11 | PROG = test_mb_rtu_master 12 | RM = /bin/rm -f 13 | 14 | $(PROG): $(OBJS) 15 | $(LD) $(LDFLAGS) $(OBJS) -o $(PROG) $(LIBS) 16 | 17 | test_mb_rtu_master.o: test_mb_rtu_master.c $(INCS) 18 | $(CC) $(CFLAGS) -c test_mb_rtu_master.c 19 | 20 | mb_rtu_master.o: $(S)/mb_rtu_master.c $(INCS) 21 | $(CC) $(CFLAGS) -c $(S)/mb_rtu_master.c 22 | 23 | mb_rtu_con.o: $(S)/mb_rtu_con.c $(INCS) 24 | $(CC) $(CFLAGS) -c $(S)/mb_rtu_con.c 25 | 26 | mb_rtu_adu.o: $(S)/mb_rtu_adu.c $(INCS) 27 | $(CC) $(CFLAGS) -c $(S)/mb_rtu_adu.c 28 | 29 | mb_pdu.o: $(S)/mb_pdu.c $(INCS) 30 | $(CC) $(CFLAGS) -c $(S)/mb_pdu.c 31 | 32 | mb_log.o: $(S)/mb_log.c $(INCS) 33 | $(CC) $(CFLAGS) -c $(S)/mb_log.c 34 | 35 | clean: 36 | $(RM) $(PROG) $(OBJS) 37 | -------------------------------------------------------------------------------- /test_mb_rtu_master/test_mb_rtu_master.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "mb_rtu_master.h" 33 | #include "mb_rtu_adu.h" 34 | #include "mb_log.h" 35 | 36 | #define SLAVE_ADDR 1 37 | #define START_ADDR 0x0 38 | #define QUANT_REGS 1 39 | 40 | int main(int argc, char **argv) 41 | { 42 | mb_rtu_master_t master = {{0}}; 43 | mb_rtu_adu_t resp = {0}; 44 | mb_rtu_adu_t req = {0}; 45 | const char *dev = NULL; 46 | int ret = 0; 47 | 48 | mb_log_set_level(MB_LOG_DEBUG); 49 | if (argc != 2) 50 | { 51 | mb_log_info("usage: test_mb_rtu_master dev\n"); 52 | return EXIT_FAILURE; 53 | } 54 | dev = argv[1]; 55 | ret = mb_rtu_master_create(&master, dev); 56 | if (ret < 0) 57 | { 58 | mb_log_error("failed to create master: %s", strerror(-ret)); 59 | mb_rtu_master_destroy(&master); 60 | return EXIT_FAILURE; 61 | } 62 | mb_log_notice("reading holding register[%d]", START_ADDR + 1); 63 | mb_rtu_adu_set_header(&req, SLAVE_ADDR); 64 | ret = mb_pdu_set_rd_hold_regs_req(&req.pdu, START_ADDR, QUANT_REGS); 65 | if (ret < 0) 66 | { 67 | mb_log_error("failed to set RTU ADU, ret: %d", ret); 68 | mb_rtu_master_destroy(&master); 69 | return EXIT_FAILURE; 70 | } 71 | ret = mb_rtu_master_exchange(&master, &req, &resp); 72 | if (ret < 0) 73 | { 74 | mb_log_error("failed to exchange with slave: %s", strerror(-ret)); 75 | mb_rtu_master_destroy(&master); 76 | return EXIT_FAILURE; 77 | } 78 | mb_log_notice("holding register[%d]: 0x%04x", 79 | req.pdu.rd_hold_regs_req.start_addr + 1, 80 | resp.pdu.rd_hold_regs_resp.reg_val[0]); 81 | mb_rtu_master_destroy(&master); 82 | return EXIT_SUCCESS; 83 | } 84 | -------------------------------------------------------------------------------- /test_mb_rtu_slave/Makefile: -------------------------------------------------------------------------------- 1 | I=../include 2 | S=../src 3 | 4 | CC = gcc 5 | CFLAGS = -Wall -g -I$(I) 6 | LD = gcc 7 | LDFLAGS = 8 | INCS = $(I)/mb_rtu_slave.h $(I)/mb_rtu_con.h $(I)/mb_rtu_adu.h $(I)/mb_pdu.h $(I)/mb_log.h 9 | OBJS = test_mb_rtu_slave.o mb_rtu_slave.o mb_rtu_con.o mb_rtu_adu.o mb_pdu.o mb_log.o 10 | LIBS = 11 | PROG = test_mb_rtu_slave 12 | RM = /bin/rm -f 13 | 14 | $(PROG): $(OBJS) 15 | $(LD) $(LDFLAGS) $(OBJS) -o $(PROG) $(LIBS) 16 | 17 | test_mb_rtu_slave.o: test_mb_rtu_slave.c $(INCS) 18 | $(CC) $(CFLAGS) -c test_mb_rtu_slave.c 19 | 20 | mb_rtu_slave.o: $(S)/mb_rtu_slave.c $(INCS) 21 | $(CC) $(CFLAGS) -c $(S)/mb_rtu_slave.c 22 | 23 | mb_rtu_con.o: $(S)/mb_rtu_con.c $(INCS) 24 | $(CC) $(CFLAGS) -c $(S)/mb_rtu_con.c 25 | 26 | mb_rtu_adu.o: $(S)/mb_rtu_adu.c $(INCS) 27 | $(CC) $(CFLAGS) -c $(S)/mb_rtu_adu.c 28 | 29 | mb_pdu.o: $(S)/mb_pdu.c $(INCS) 30 | $(CC) $(CFLAGS) -c $(S)/mb_pdu.c 31 | 32 | mb_log.o: $(S)/mb_log.c $(INCS) 33 | $(CC) $(CFLAGS) -c $(S)/mb_log.c 34 | 35 | clean: 36 | $(RM) $(PROG) $(OBJS) 37 | -------------------------------------------------------------------------------- /test_mb_rtu_slave/test_mb_rtu_slave.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include "mb_rtu_slave.h" 31 | #include "mb_rtu_adu.h" 32 | #include "mb_log.h" 33 | 34 | #define SLAVE_ADDR 1 35 | #define HOLD_REG_ADDR 0x0 36 | #define HOLD_REG_QUANT 1 37 | 38 | uint16_t hold_reg = 0x1234; 39 | 40 | static int handle(mb_rtu_slave_t *slave, mb_rtu_adu_t *req, mb_rtu_adu_t *resp) 41 | { 42 | int ret = 0; 43 | 44 | switch (req->pdu.func_code) 45 | { 46 | case MB_PDU_RD_HOLD_REGS: 47 | if (req->pdu.rd_hold_regs_req.start_addr != HOLD_REG_ADDR) 48 | { 49 | return -MB_PDU_EXCEPT_ILLEGAL_ADDR; 50 | } 51 | if (req->pdu.rd_hold_regs_req.quant_regs != HOLD_REG_QUANT) 52 | { 53 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 54 | } 55 | mb_rtu_adu_set_header(resp, req->addr); 56 | ret = mb_pdu_set_rd_hold_regs_resp(&resp->pdu, HOLD_REG_QUANT * 2, &hold_reg); 57 | if (ret < 0) 58 | { 59 | return -MB_PDU_EXCEPT_SERVER_DEV_FAIL; 60 | } 61 | return 0; 62 | default: 63 | return -MB_PDU_EXCEPT_ILLEGAL_FUNC; 64 | } 65 | return 0; /* should never reach here */ 66 | } 67 | 68 | int main(int argc, char **argv) 69 | { 70 | mb_rtu_slave_t slave = {0}; 71 | const char *dev = NULL; 72 | int ret = 0; 73 | 74 | mb_log_set_level(MB_LOG_DEBUG); 75 | if (argc != 2) 76 | { 77 | mb_log_info("usage: test_mb_rtu_slave dev\n"); 78 | return EXIT_FAILURE; 79 | } 80 | dev = argv[1]; 81 | ret = mb_rtu_slave_create(&slave, dev, SLAVE_ADDR, handle); 82 | if (ret < 0) 83 | { 84 | mb_log_error("failed to create slave: %s\n", strerror(-ret)); 85 | return EXIT_FAILURE; 86 | } 87 | ret = mb_rtu_slave_run(&slave); 88 | if (ret < 0) 89 | { 90 | mb_log_error("failed to run slave: %s\n", strerror(-ret)); 91 | mb_rtu_slave_destroy(&slave); 92 | return EXIT_FAILURE; 93 | } 94 | mb_rtu_slave_destroy(&slave); 95 | return EXIT_SUCCESS; 96 | } 97 | -------------------------------------------------------------------------------- /test_mb_tcp_adu/Makefile: -------------------------------------------------------------------------------- 1 | I=../include 2 | S=../src 3 | T=../test 4 | 5 | CC = gcc 6 | CFLAGS = -Wall -g -I$(I) -I$(T) 7 | LD = gcc 8 | LDFLAGS = 9 | INCS = $(I)/mb_tcp_adu.h $(I)/mb_pdu.h $(T)/mb_test.h 10 | OBJS = test_mb_tcp_adu.o mb_tcp_adu.o mb_pdu.o mb_test.o 11 | LIBS = 12 | PROG = test_mb_tcp_adu 13 | RM = /bin/rm -f 14 | 15 | $(PROG): $(OBJS) 16 | $(LD) $(LDFLAGS) $(OBJS) -o $(PROG) $(LIBS) 17 | 18 | test_mb_tcp_adu.o: test_mb_tcp_adu.c $(INCS) 19 | $(CC) $(CFLAGS) -c test_mb_tcp_adu.c 20 | 21 | mb_tcp_adu.o: $(S)/mb_tcp_adu.c $(INCS) 22 | $(CC) $(CFLAGS) -c $(S)/mb_tcp_adu.c 23 | 24 | mb_pdu.o: $(S)/mb_pdu.c $(INCS) 25 | $(CC) $(CFLAGS) -c $(S)/mb_pdu.c 26 | 27 | mb_test.o: $(T)/mb_test.c $(INCS) 28 | $(CC) $(CFLAGS) -c $(T)/mb_test.c 29 | 30 | clean: 31 | $(RM) $(PROG) $(OBJS) 32 | -------------------------------------------------------------------------------- /test_mb_tcp_client/Makefile: -------------------------------------------------------------------------------- 1 | I=../include 2 | S=../src 3 | 4 | CC = gcc 5 | CFLAGS = -Wall -g -I$(I) 6 | LD = gcc 7 | LDFLAGS = 8 | INCS = $(I)/mb_tcp_client.h $(I)/mb_tcp_con.h $(I)/mb_ip_auth.h $(I)/mb_tcp_adu.h $(I)/mb_pdu.h $(I)/mb_log.h 9 | OBJS = test_mb_tcp_client.o mb_tcp_client.o mb_tcp_con.o mb_ip_auth.o mb_tcp_adu.o mb_pdu.o mb_log.o 10 | LIBS = 11 | PROG = test_mb_tcp_client 12 | RM = /bin/rm -f 13 | 14 | $(PROG): $(OBJS) 15 | $(LD) $(LDFLAGS) $(OBJS) -o $(PROG) $(LIBS) 16 | 17 | test_mb_tcp_client.o: test_mb_tcp_client.c $(INCS) 18 | $(CC) $(CFLAGS) -c test_mb_tcp_client.c 19 | 20 | mb_tcp_client.o: $(S)/mb_tcp_client.c $(INCS) 21 | $(CC) $(CFLAGS) -c $(S)/mb_tcp_client.c 22 | 23 | mb_tcp_con.o: $(S)/mb_tcp_con.c $(INCS) 24 | $(CC) $(CFLAGS) -c $(S)/mb_tcp_con.c 25 | 26 | mb_ip_auth.o: $(S)/mb_ip_auth.c $(INCS) 27 | $(CC) $(CFLAGS) -c $(S)/mb_ip_auth.c 28 | 29 | mb_tcp_adu.o: $(S)/mb_tcp_adu.c $(INCS) 30 | $(CC) $(CFLAGS) -c $(S)/mb_tcp_adu.c 31 | 32 | mb_pdu.o: $(S)/mb_pdu.c $(INCS) 33 | $(CC) $(CFLAGS) -c $(S)/mb_pdu.c 34 | 35 | mb_log.o: $(S)/mb_log.c $(INCS) 36 | $(CC) $(CFLAGS) -c $(S)/mb_log.c 37 | 38 | clean: 39 | $(RM) $(PROG) $(OBJS) 40 | -------------------------------------------------------------------------------- /test_mb_tcp_client/test_mb_tcp_client.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include "mb_tcp_client.h" 32 | #include "mb_tcp_adu.h" 33 | #include "mb_log.h" 34 | 35 | #define SERVER_ADDR "127.0.0.1" 36 | #define SERVER_PORT 10000 37 | #define AUTH_ADDR "127.0.0.1" /* authorised server address */ 38 | #define TIMEOUT_SEC 0 39 | #define TIMEOUT_USEC 500000 40 | #define TRANS_ID 1 41 | #define PROTO_ID 0 42 | #define START_ADDR 0x0 43 | #define QUANT_REGS 1 44 | 45 | int main(void) 46 | { 47 | struct timeval timeout = {TIMEOUT_SEC, TIMEOUT_USEC}; 48 | mb_tcp_client_t client = {{0}}; 49 | mb_tcp_adu_t resp = {0}; 50 | mb_tcp_adu_t req = {0}; 51 | int ret = 0; 52 | 53 | mb_log_set_level(MB_LOG_DEBUG); 54 | mb_tcp_client_create(&client, timeout); 55 | ret = mb_tcp_client_authorise_addr(&client, AUTH_ADDR); 56 | if (ret < 0) 57 | { 58 | mb_log_error("failed to authorise server address: %s", strerror(-ret)); 59 | mb_tcp_client_destroy(&client); 60 | return EXIT_FAILURE; 61 | } 62 | mb_log_notice("reading holding register[%d]", START_ADDR + 1); 63 | mb_tcp_adu_set_header(&req, TRANS_ID, PROTO_ID, MB_TCP_CLIENT_UNIT_ID); 64 | ret = mb_pdu_set_rd_hold_regs_req(&req.pdu, START_ADDR, QUANT_REGS); 65 | if (ret < 0) 66 | { 67 | mb_log_error("failed to set TCP ADU, ret: %d", ret); 68 | mb_tcp_client_destroy(&client); 69 | return EXIT_FAILURE; 70 | } 71 | ret = mb_tcp_client_exchange(&client, SERVER_ADDR, SERVER_PORT, &req, &resp); 72 | if (ret < 0) 73 | { 74 | mb_log_error("failed to exchange with server: %s", strerror(-ret)); 75 | mb_tcp_client_destroy(&client); 76 | return EXIT_FAILURE; 77 | } 78 | mb_log_notice("holding register[%d]: 0x%04x", 79 | req.pdu.rd_hold_regs_req.start_addr + 1, 80 | resp.pdu.rd_hold_regs_resp.reg_val[0]); 81 | mb_tcp_client_destroy(&client); 82 | return EXIT_SUCCESS; 83 | } 84 | -------------------------------------------------------------------------------- /test_mb_tcp_server/Makefile: -------------------------------------------------------------------------------- 1 | I=../include 2 | S=../src 3 | 4 | CC = gcc 5 | CFLAGS = -Wall -g -I$(I) 6 | LD = gcc 7 | LDFLAGS = 8 | INCS = $(I)/mb_tcp_server.h $(I)/mb_tcp_con.h $(I)/mb_ip_auth.h $(I)/mb_tcp_adu.h $(I)/mb_pdu.h $(I)/mb_log.h 9 | OBJS = test_mb_tcp_server.o mb_tcp_server.o mb_tcp_con.o mb_ip_auth.o mb_tcp_adu.o mb_pdu.o mb_log.o 10 | LIBS = 11 | PROG = test_mb_tcp_server 12 | RM = /bin/rm -f 13 | 14 | $(PROG): $(OBJS) 15 | $(LD) $(LDFLAGS) $(OBJS) -o $(PROG) $(LIBS) 16 | 17 | test_mb_tcp_server.o: test_mb_tcp_server.c $(INCS) 18 | $(CC) $(CFLAGS) -c test_mb_tcp_server.c 19 | 20 | mb_tcp_server.o: $(S)/mb_tcp_server.c $(INCS) 21 | $(CC) $(CFLAGS) -c $(S)/mb_tcp_server.c 22 | 23 | mb_tcp_con.o: $(S)/mb_tcp_con.c $(INCS) 24 | $(CC) $(CFLAGS) -c $(S)/mb_tcp_con.c 25 | 26 | mb_ip_auth.o: $(S)/mb_ip_auth.c $(INCS) 27 | $(CC) $(CFLAGS) -c $(S)/mb_ip_auth.c 28 | 29 | mb_tcp_adu.o: $(S)/mb_tcp_adu.c $(INCS) 30 | $(CC) $(CFLAGS) -c $(S)/mb_tcp_adu.c 31 | 32 | mb_pdu.o: $(S)/mb_pdu.c $(INCS) 33 | $(CC) $(CFLAGS) -c $(S)/mb_pdu.c 34 | 35 | mb_log.o: $(S)/mb_log.c $(INCS) 36 | $(CC) $(CFLAGS) -c $(S)/mb_log.c 37 | 38 | clean: 39 | $(RM) $(PROG) $(OBJS) 40 | -------------------------------------------------------------------------------- /test_mb_tcp_server/test_mb_tcp_server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Keith Cullen. 3 | * All Rights Reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include "mb_tcp_server.h" 32 | #include "mb_tcp_adu.h" 33 | #include "mb_log.h" 34 | 35 | #define HOST_ADDR "127.0.0.1" 36 | #define HOST_PORT 10000 /* using the standard port 502 requires root privileges */ 37 | #define AUTH_ADDR "127.0.0.1" /* authorised client address */ 38 | 39 | #define HOLD_REG_ADDR 0x0 40 | #define HOLD_REG_QUANT 1 41 | 42 | uint16_t hold_reg = 0x1234; 43 | 44 | static int handle(mb_tcp_server_t *server, mb_tcp_adu_t *req, mb_tcp_adu_t *resp) 45 | { 46 | int ret = 0; 47 | 48 | switch (req->pdu.func_code) 49 | { 50 | case MB_PDU_RD_HOLD_REGS: 51 | if (req->pdu.rd_hold_regs_req.start_addr != HOLD_REG_ADDR) 52 | { 53 | return -MB_PDU_EXCEPT_ILLEGAL_ADDR; 54 | } 55 | if (req->pdu.rd_hold_regs_req.quant_regs != HOLD_REG_QUANT) 56 | { 57 | return -MB_PDU_EXCEPT_ILLEGAL_VAL; 58 | } 59 | mb_tcp_adu_set_header(resp, req->trans_id, req->proto_id, MB_TCP_SERVER_UNIT_ID); 60 | ret = mb_pdu_set_rd_hold_regs_resp(&resp->pdu, HOLD_REG_QUANT * 2, &hold_reg); 61 | if (ret < 0) 62 | { 63 | return -MB_PDU_EXCEPT_SERVER_DEV_FAIL; 64 | } 65 | return ret; 66 | default: 67 | return -MB_PDU_EXCEPT_ILLEGAL_FUNC; 68 | } 69 | return 0; /* should never reach here */ 70 | } 71 | 72 | int main(void) 73 | { 74 | mb_tcp_server_t server = {0}; 75 | int ret = 0; 76 | 77 | mb_log_set_level(MB_LOG_DEBUG); 78 | ret = mb_tcp_server_create(&server, HOST_ADDR, HOST_PORT, handle); 79 | if (ret < 0) 80 | { 81 | mb_log_error("failed to create server: %s", strerror(-ret)); 82 | return EXIT_FAILURE; 83 | } 84 | ret = mb_tcp_server_authorise_addr(&server, AUTH_ADDR); 85 | if (ret < 0) 86 | { 87 | mb_log_error("failed to authorise client address: %s", strerror(-ret)); 88 | mb_tcp_server_destroy(&server); 89 | return EXIT_FAILURE; 90 | } 91 | ret = mb_tcp_server_run(&server); 92 | if (ret < 0) 93 | { 94 | mb_log_error("failed to run server: %s", strerror(-ret)); 95 | mb_tcp_server_destroy(&server); 96 | return EXIT_FAILURE; 97 | } 98 | mb_tcp_server_destroy(&server); 99 | return EXIT_SUCCESS; 100 | } 101 | --------------------------------------------------------------------------------