1 /*
2 * Copyright (c) 2020 PHYTEC Messtechnik GmbH
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "test_modbus.h"
8
9 #include <zephyr/sys/util.h>
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(mbs_test, LOG_LEVEL_INF);
12
13 const static uint16_t fp_offset = MB_TEST_FP_OFFSET;
14 static uint16_t coils;
15 static uint16_t holding_reg[8];
16 static float holding_fp[4];
17
18 uint8_t server_iface;
19
test_get_server_iface(void)20 uint8_t test_get_server_iface(void)
21 {
22 return server_iface;
23 }
24
coil_rd(uint16_t addr,bool * state)25 static int coil_rd(uint16_t addr, bool *state)
26 {
27 if (addr >= (sizeof(coils) * 8)) {
28 return -ENOTSUP;
29 }
30
31 if (coils & BIT(addr)) {
32 *state = true;
33 } else {
34 *state = false;
35 }
36
37 LOG_DBG("Coil read, addr %u, %d", addr, (int)*state);
38
39 return 0;
40 }
41
coil_wr(uint16_t addr,bool state)42 static int coil_wr(uint16_t addr, bool state)
43 {
44 if (addr >= (sizeof(coils) * 8)) {
45 return -ENOTSUP;
46 }
47
48 if (state == true) {
49 coils |= BIT(addr);
50 } else {
51 coils &= ~BIT(addr);
52 }
53
54 LOG_DBG("Coil write, addr %u, %d", addr, (int)state);
55
56 return 0;
57 }
58
discrete_input_rd(uint16_t addr,bool * state)59 static int discrete_input_rd(uint16_t addr, bool *state)
60 {
61 if (addr >= (sizeof(coils) * 8)) {
62 return -ENOTSUP;
63 }
64
65 if (coils & BIT(addr)) {
66 *state = true;
67 } else {
68 *state = false;
69 }
70
71 LOG_DBG("Discrete input read, addr %u, %d", addr, (int)*state);
72
73 return 0;
74 }
75
input_reg_rd(uint16_t addr,uint16_t * reg)76 static int input_reg_rd(uint16_t addr, uint16_t *reg)
77 {
78 if (addr >= ARRAY_SIZE(holding_reg)) {
79 return -ENOTSUP;
80 }
81
82 *reg = holding_reg[addr];
83
84 LOG_DBG("Input register read, addr %u, 0x%04x", addr, *reg);
85
86 return 0;
87 }
88
input_reg_rd_fp(uint16_t addr,float * reg)89 static int input_reg_rd_fp(uint16_t addr, float *reg)
90 {
91 if (!IN_RANGE(addr, fp_offset, sizeof(holding_fp) / 2 + fp_offset)) {
92 return -ENOTSUP;
93 }
94
95 *reg = holding_fp[(addr - fp_offset) / 2];
96
97 LOG_DBG("FP input register read, addr %u", addr);
98
99 return 0;
100 }
101
holding_reg_rd(uint16_t addr,uint16_t * reg)102 static int holding_reg_rd(uint16_t addr, uint16_t *reg)
103 {
104 if (addr >= ARRAY_SIZE(holding_reg)) {
105 return -ENOTSUP;
106 }
107
108 *reg = holding_reg[addr];
109
110 LOG_DBG("Holding register read, addr %u", addr);
111
112 return 0;
113 }
114
holding_reg_wr(uint16_t addr,uint16_t reg)115 static int holding_reg_wr(uint16_t addr, uint16_t reg)
116 {
117 if (addr >= ARRAY_SIZE(holding_reg)) {
118 return -ENOTSUP;
119 }
120
121 holding_reg[addr] = reg;
122
123 LOG_DBG("Holding register write, addr %u", addr);
124
125 return 0;
126 }
127
holding_reg_rd_fp(uint16_t addr,float * reg)128 static int holding_reg_rd_fp(uint16_t addr, float *reg)
129 {
130 if (!IN_RANGE(addr, fp_offset, sizeof(holding_fp) / 2 + fp_offset)) {
131 return -ENOTSUP;
132 }
133
134 *reg = holding_fp[(addr - fp_offset) / 2];
135
136 LOG_DBG("FP holding register read, addr %u", addr);
137
138 return 0;
139 }
140
holding_reg_wr_fp(uint16_t addr,float reg)141 static int holding_reg_wr_fp(uint16_t addr, float reg)
142 {
143 if (!IN_RANGE(addr, fp_offset, sizeof(holding_fp) / 2 + fp_offset)) {
144 return -ENOTSUP;
145 }
146
147 holding_fp[(addr - fp_offset) / 2] = reg;
148
149 LOG_DBG("FP holding register write, addr %u", addr);
150
151 return 0;
152 }
153
154 static struct modbus_user_callbacks mbs_cbs = {
155 /** Coil read/write callback */
156 .coil_rd = coil_rd,
157 .coil_wr = coil_wr,
158 /* Discrete Input read callback */
159 .discrete_input_rd = discrete_input_rd,
160 /* Input Register read callback */
161 .input_reg_rd = input_reg_rd,
162 /* Floating Point Input Register read callback */
163 .input_reg_rd_fp = input_reg_rd_fp,
164 /* Holding Register read/write callback */
165 .holding_reg_rd = holding_reg_rd,
166 .holding_reg_wr = holding_reg_wr,
167 /* Floating Point Holding Register read/write callback */
168 .holding_reg_rd_fp = holding_reg_rd_fp,
169 .holding_reg_wr_fp = holding_reg_wr_fp,
170 };
171
172 static struct modbus_iface_param server_param = {
173 .mode = MODBUS_MODE_RTU,
174 .server = {
175 .user_cb = &mbs_cbs,
176 .unit_id = MB_TEST_NODE_ADDR,
177 },
178 .serial = {
179 .baud = MB_TEST_BAUDRATE_LOW,
180 .parity = UART_CFG_PARITY_ODD,
181 },
182 };
183
184 /*
185 * This test performed on hardware requires two UART controllers
186 * on the board (with RX/TX lines connected crosswise).
187 * The exact mapping is not required, we assume that both controllers
188 * have similar capabilities and use the instance with index 1
189 * as interface for the server.
190 */
191 #if DT_NODE_EXISTS(DT_INST(1, zephyr_modbus_serial))
192 static const char rtu_iface_name[] = {DEVICE_DT_NAME(DT_INST(1, zephyr_modbus_serial))};
193 #else
194 static const char rtu_iface_name[] = "";
195 #endif
196
test_server_setup_low_odd(void)197 void test_server_setup_low_odd(void)
198 {
199 int err;
200
201 server_iface = modbus_iface_get_by_name(rtu_iface_name);
202 server_param.mode = MODBUS_MODE_RTU;
203 server_param.serial.baud = MB_TEST_BAUDRATE_LOW;
204 server_param.serial.parity = UART_CFG_PARITY_ODD;
205
206 if (IS_ENABLED(CONFIG_MODBUS_SERVER)) {
207 err = modbus_init_server(server_iface, server_param);
208 zassert_equal(err, 0, "Failed to configure RTU server");
209 } else {
210 ztest_test_skip();
211 }
212 }
213
test_server_setup_low_none(void)214 void test_server_setup_low_none(void)
215 {
216 int err;
217
218 server_iface = modbus_iface_get_by_name(rtu_iface_name);
219 server_param.mode = MODBUS_MODE_RTU;
220 server_param.serial.baud = MB_TEST_BAUDRATE_LOW;
221 server_param.serial.parity = UART_CFG_PARITY_NONE;
222
223 if (IS_ENABLED(CONFIG_MODBUS_SERVER)) {
224 err = modbus_init_server(server_iface, server_param);
225 zassert_equal(err, 0, "Failed to configure RTU server");
226 } else {
227 ztest_test_skip();
228 }
229 }
230
test_server_setup_high_even(void)231 void test_server_setup_high_even(void)
232 {
233 int err;
234
235 server_iface = modbus_iface_get_by_name(rtu_iface_name);
236 server_param.mode = MODBUS_MODE_RTU;
237 server_param.serial.baud = MB_TEST_BAUDRATE_HIGH;
238 server_param.serial.parity = UART_CFG_PARITY_EVEN;
239
240 if (IS_ENABLED(CONFIG_MODBUS_SERVER)) {
241 err = modbus_init_server(server_iface, server_param);
242 zassert_equal(err, 0, "Failed to configure RTU server");
243 } else {
244 ztest_test_skip();
245 }
246 }
247
test_server_setup_ascii(void)248 void test_server_setup_ascii(void)
249 {
250 int err;
251
252 server_iface = modbus_iface_get_by_name(rtu_iface_name);
253 server_param.mode = MODBUS_MODE_ASCII;
254 server_param.serial.baud = MB_TEST_BAUDRATE_HIGH;
255 server_param.serial.parity = UART_CFG_PARITY_EVEN;
256
257 if (IS_ENABLED(CONFIG_MODBUS_SERVER)) {
258 err = modbus_init_server(server_iface, server_param);
259 zassert_equal(err, 0, "Failed to configure RTU server");
260 } else {
261 ztest_test_skip();
262 }
263 }
264
test_server_setup_raw(void)265 void test_server_setup_raw(void)
266 {
267 char iface_name[] = "RAW_1";
268 int err;
269
270 server_iface = modbus_iface_get_by_name(iface_name);
271 server_param.mode = MODBUS_MODE_RAW;
272 server_param.rawcb.raw_tx_cb = server_raw_cb;
273
274 if (IS_ENABLED(CONFIG_MODBUS_SERVER)) {
275 err = modbus_init_server(server_iface, server_param);
276 zassert_equal(err, 0, "Failed to configure RAW server");
277 } else {
278 ztest_test_skip();
279 }
280 }
281
test_server_disable(void)282 void test_server_disable(void)
283 {
284 int err;
285
286 if (IS_ENABLED(CONFIG_MODBUS_SERVER)) {
287 err = modbus_disable(server_iface);
288 zassert_equal(err, 0, "Failed to disable RTU server");
289 } else {
290 ztest_test_skip();
291 }
292 }
293