1 /*
2 * Copyright (c) 2020 PHYTEC Messtechnik GmbH
3 * Copyright (c) 2021 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_REGISTER(modbus, CONFIG_MODBUS_LOG_LEVEL);
10
11 #include <zephyr/kernel.h>
12 #include <string.h>
13 #include <zephyr/sys/byteorder.h>
14 #include <modbus_internal.h>
15
16 #define DT_DRV_COMPAT zephyr_modbus_serial
17
18 #define MB_RTU_DEFINE_GPIO_CFG(inst, prop) \
19 static struct gpio_dt_spec prop##_cfg_##inst = { \
20 .port = DEVICE_DT_GET(DT_INST_PHANDLE(inst, prop)), \
21 .pin = DT_INST_GPIO_PIN(inst, prop), \
22 .dt_flags = DT_INST_GPIO_FLAGS(inst, prop), \
23 };
24
25 #define MB_RTU_DEFINE_GPIO_CFGS(inst) \
26 COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, de_gpios), \
27 (MB_RTU_DEFINE_GPIO_CFG(inst, de_gpios)), ()) \
28 COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, re_gpios), \
29 (MB_RTU_DEFINE_GPIO_CFG(inst, re_gpios)), ())
30
31 DT_INST_FOREACH_STATUS_OKAY(MB_RTU_DEFINE_GPIO_CFGS)
32
33 #define MB_RTU_ASSIGN_GPIO_CFG(inst, prop) \
34 COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, prop), \
35 (&prop##_cfg_##inst), (NULL))
36
37 #define MODBUS_DT_GET_SERIAL_DEV(inst) { \
38 .dev = DEVICE_DT_GET(DT_INST_PARENT(inst)), \
39 .de = MB_RTU_ASSIGN_GPIO_CFG(inst, de_gpios), \
40 .re = MB_RTU_ASSIGN_GPIO_CFG(inst, re_gpios), \
41 },
42
43 #ifdef CONFIG_MODBUS_SERIAL
44 static struct modbus_serial_config modbus_serial_cfg[] = {
45 DT_INST_FOREACH_STATUS_OKAY(MODBUS_DT_GET_SERIAL_DEV)
46 };
47 #endif
48
49 #define MODBUS_DT_GET_DEV(inst) { \
50 .iface_name = DEVICE_DT_NAME(DT_DRV_INST(inst)),\
51 .cfg = &modbus_serial_cfg[inst], \
52 },
53
54 #define DEFINE_MODBUS_RAW_ADU(x, _) { \
55 .iface_name = "RAW_"#x, \
56 .rawcb.raw_tx_cb = NULL, \
57 .mode = MODBUS_MODE_RAW, \
58 }
59
60
61 static struct modbus_context mb_ctx_tbl[] = {
62 DT_INST_FOREACH_STATUS_OKAY(MODBUS_DT_GET_DEV)
63 #ifdef CONFIG_MODBUS_RAW_ADU
64 LISTIFY(CONFIG_MODBUS_NUMOF_RAW_ADU, DEFINE_MODBUS_RAW_ADU, (,), _)
65 #endif
66 };
67
modbus_rx_handler(struct k_work * item)68 static void modbus_rx_handler(struct k_work *item)
69 {
70 struct modbus_context *ctx;
71
72 ctx = CONTAINER_OF(item, struct modbus_context, server_work);
73
74 switch (ctx->mode) {
75 case MODBUS_MODE_RTU:
76 case MODBUS_MODE_ASCII:
77 if (IS_ENABLED(CONFIG_MODBUS_SERIAL)) {
78 modbus_serial_rx_disable(ctx);
79 ctx->rx_adu_err = modbus_serial_rx_adu(ctx);
80 }
81 break;
82 case MODBUS_MODE_RAW:
83 if (IS_ENABLED(CONFIG_MODBUS_RAW_ADU)) {
84 ctx->rx_adu_err = modbus_raw_rx_adu(ctx);
85 }
86 break;
87 default:
88 LOG_ERR("Unknown MODBUS mode");
89 return;
90 }
91
92 if (ctx->client == true) {
93 k_sem_give(&ctx->client_wait_sem);
94 } else if (IS_ENABLED(CONFIG_MODBUS_SERVER)) {
95 bool respond = modbus_server_handler(ctx);
96
97 if (respond) {
98 modbus_tx_adu(ctx);
99 } else {
100 LOG_DBG("Server has dropped frame");
101 }
102
103 switch (ctx->mode) {
104 case MODBUS_MODE_RTU:
105 case MODBUS_MODE_ASCII:
106 if (IS_ENABLED(CONFIG_MODBUS_SERIAL) &&
107 respond == false) {
108 modbus_serial_rx_enable(ctx);
109 }
110 break;
111 default:
112 break;
113 }
114 }
115 }
116
modbus_tx_adu(struct modbus_context * ctx)117 void modbus_tx_adu(struct modbus_context *ctx)
118 {
119 switch (ctx->mode) {
120 case MODBUS_MODE_RTU:
121 case MODBUS_MODE_ASCII:
122 if (IS_ENABLED(CONFIG_MODBUS_SERIAL) &&
123 modbus_serial_tx_adu(ctx)) {
124 LOG_ERR("Unsupported MODBUS serial mode");
125 }
126 break;
127 case MODBUS_MODE_RAW:
128 if (IS_ENABLED(CONFIG_MODBUS_RAW_ADU) &&
129 modbus_raw_tx_adu(ctx)) {
130 LOG_ERR("Unsupported MODBUS raw mode");
131 }
132 break;
133 default:
134 LOG_ERR("Unknown MODBUS mode");
135 }
136 }
137
modbus_tx_wait_rx_adu(struct modbus_context * ctx)138 int modbus_tx_wait_rx_adu(struct modbus_context *ctx)
139 {
140 k_sem_reset(&ctx->client_wait_sem);
141
142 modbus_tx_adu(ctx);
143
144 if (k_sem_take(&ctx->client_wait_sem, K_USEC(ctx->rxwait_to)) != 0) {
145 LOG_WRN("Client wait-for-RX timeout");
146 return -ETIMEDOUT;
147 }
148
149 return ctx->rx_adu_err;
150 }
151
modbus_get_context(const uint8_t iface)152 struct modbus_context *modbus_get_context(const uint8_t iface)
153 {
154 struct modbus_context *ctx;
155
156 if (iface >= ARRAY_SIZE(mb_ctx_tbl)) {
157 LOG_ERR("Interface %u not available", iface);
158 return NULL;
159 }
160
161 ctx = &mb_ctx_tbl[iface];
162
163 if (!atomic_test_bit(&ctx->state, MODBUS_STATE_CONFIGURED)) {
164 LOG_ERR("Interface not configured");
165 return NULL;
166 }
167
168 return ctx;
169 }
170
modbus_iface_get_by_ctx(const struct modbus_context * ctx)171 int modbus_iface_get_by_ctx(const struct modbus_context *ctx)
172 {
173 for (int i = 0; i < ARRAY_SIZE(mb_ctx_tbl); i++) {
174 if (&mb_ctx_tbl[i] == ctx) {
175 return i;
176 }
177 }
178
179 return -ENODEV;
180 }
181
modbus_iface_get_by_name(const char * iface_name)182 int modbus_iface_get_by_name(const char *iface_name)
183 {
184 for (int i = 0; i < ARRAY_SIZE(mb_ctx_tbl); i++) {
185 if (strcmp(iface_name, mb_ctx_tbl[i].iface_name) == 0) {
186 return i;
187 }
188 }
189
190 return -ENODEV;
191 }
192
modbus_init_iface(const uint8_t iface)193 static struct modbus_context *modbus_init_iface(const uint8_t iface)
194 {
195 struct modbus_context *ctx;
196
197 if (iface >= ARRAY_SIZE(mb_ctx_tbl)) {
198 LOG_ERR("Interface %u not available", iface);
199 return NULL;
200 }
201
202 ctx = &mb_ctx_tbl[iface];
203
204 if (atomic_test_and_set_bit(&ctx->state, MODBUS_STATE_CONFIGURED)) {
205 LOG_ERR("Interface already used");
206 return NULL;
207 }
208
209 k_mutex_init(&ctx->iface_lock);
210 k_sem_init(&ctx->client_wait_sem, 0, 1);
211 k_work_init(&ctx->server_work, modbus_rx_handler);
212
213 return ctx;
214 }
215
modbus_user_fc_init(struct modbus_context * ctx,struct modbus_iface_param param)216 static int modbus_user_fc_init(struct modbus_context *ctx, struct modbus_iface_param param)
217 {
218 sys_slist_init(&ctx->user_defined_cbs);
219 LOG_DBG("Initializing user-defined function code support.");
220
221 return 0;
222 }
223
modbus_init_server(const int iface,struct modbus_iface_param param)224 int modbus_init_server(const int iface, struct modbus_iface_param param)
225 {
226 struct modbus_context *ctx = NULL;
227 int rc = 0;
228
229 if (!IS_ENABLED(CONFIG_MODBUS_SERVER)) {
230 LOG_ERR("Modbus server support is not enabled");
231 rc = -ENOTSUP;
232 goto init_server_error;
233 }
234
235 if (param.server.user_cb == NULL) {
236 LOG_ERR("User callbacks should be available");
237 rc = -EINVAL;
238 goto init_server_error;
239 }
240
241 ctx = modbus_init_iface(iface);
242 if (ctx == NULL) {
243 rc = -EINVAL;
244 goto init_server_error;
245 }
246
247 ctx->client = false;
248
249 if (modbus_user_fc_init(ctx, param) != 0) {
250 LOG_ERR("Failed to init MODBUS user defined function codes");
251 rc = -EINVAL;
252 goto init_server_error;
253 }
254
255 switch (param.mode) {
256 case MODBUS_MODE_RTU:
257 case MODBUS_MODE_ASCII:
258 if (IS_ENABLED(CONFIG_MODBUS_SERIAL) &&
259 modbus_serial_init(ctx, param) != 0) {
260 LOG_ERR("Failed to init MODBUS over serial line");
261 rc = -EINVAL;
262 goto init_server_error;
263 }
264 break;
265 case MODBUS_MODE_RAW:
266 if (IS_ENABLED(CONFIG_MODBUS_RAW_ADU) &&
267 modbus_raw_init(ctx, param) != 0) {
268 LOG_ERR("Failed to init MODBUS raw ADU support");
269 rc = -EINVAL;
270 goto init_server_error;
271 }
272 break;
273 default:
274 LOG_ERR("Unknown MODBUS mode");
275 rc = -ENOTSUP;
276 goto init_server_error;
277 }
278
279 ctx->unit_id = param.server.unit_id;
280 ctx->mbs_user_cb = param.server.user_cb;
281 if (IS_ENABLED(CONFIG_MODBUS_FC08_DIAGNOSTIC)) {
282 modbus_reset_stats(ctx);
283 }
284
285 LOG_DBG("Modbus interface %s initialized", ctx->iface_name);
286
287 return 0;
288
289 init_server_error:
290 if (ctx != NULL) {
291 atomic_clear_bit(&ctx->state, MODBUS_STATE_CONFIGURED);
292 }
293
294 return rc;
295 }
296
modbus_register_user_fc(const int iface,struct modbus_custom_fc * custom_fc)297 int modbus_register_user_fc(const int iface, struct modbus_custom_fc *custom_fc)
298 {
299 struct modbus_context *ctx = modbus_get_context(iface);
300
301 if (!custom_fc) {
302 LOG_ERR("Provided function code handler was NULL");
303 return -EINVAL;
304 }
305
306 if (custom_fc->fc & BIT(7)) {
307 LOG_ERR("Function codes must have MSB of 0");
308 return -EINVAL;
309 }
310
311 custom_fc->excep_code = MODBUS_EXC_NONE;
312
313 LOG_DBG("Registered new custom function code %d", custom_fc->fc);
314 sys_slist_append(&ctx->user_defined_cbs, &custom_fc->node);
315
316 return 0;
317 }
318
modbus_init_client(const int iface,struct modbus_iface_param param)319 int modbus_init_client(const int iface, struct modbus_iface_param param)
320 {
321 struct modbus_context *ctx = NULL;
322 int rc = 0;
323
324 if (!IS_ENABLED(CONFIG_MODBUS_CLIENT)) {
325 LOG_ERR("Modbus client support is not enabled");
326 rc = -ENOTSUP;
327 goto init_client_error;
328 }
329
330 ctx = modbus_init_iface(iface);
331 if (ctx == NULL) {
332 rc = -EINVAL;
333 goto init_client_error;
334 }
335
336 ctx->client = true;
337
338 switch (param.mode) {
339 case MODBUS_MODE_RTU:
340 case MODBUS_MODE_ASCII:
341 if (IS_ENABLED(CONFIG_MODBUS_SERIAL) &&
342 modbus_serial_init(ctx, param) != 0) {
343 LOG_ERR("Failed to init MODBUS over serial line");
344 rc = -EINVAL;
345 goto init_client_error;
346 }
347 break;
348 case MODBUS_MODE_RAW:
349 if (IS_ENABLED(CONFIG_MODBUS_RAW_ADU) &&
350 modbus_raw_init(ctx, param) != 0) {
351 LOG_ERR("Failed to init MODBUS raw ADU support");
352 rc = -EINVAL;
353 goto init_client_error;
354 }
355 break;
356 default:
357 LOG_ERR("Unknown MODBUS mode");
358 rc = -ENOTSUP;
359 goto init_client_error;
360 }
361
362 ctx->unit_id = 0;
363 ctx->mbs_user_cb = NULL;
364 ctx->rxwait_to = param.rx_timeout;
365
366 return 0;
367
368 init_client_error:
369 if (ctx != NULL) {
370 atomic_clear_bit(&ctx->state, MODBUS_STATE_CONFIGURED);
371 }
372
373 return rc;
374 }
375
modbus_disable(const uint8_t iface)376 int modbus_disable(const uint8_t iface)
377 {
378 struct modbus_context *ctx;
379 struct k_work_sync work_sync;
380
381 ctx = modbus_get_context(iface);
382 if (ctx == NULL) {
383 LOG_ERR("Interface %u not initialized", iface);
384 return -EINVAL;
385 }
386
387 switch (ctx->mode) {
388 case MODBUS_MODE_RTU:
389 case MODBUS_MODE_ASCII:
390 if (IS_ENABLED(CONFIG_MODBUS_SERIAL)) {
391 modbus_serial_disable(ctx);
392 }
393 break;
394 case MODBUS_MODE_RAW:
395 break;
396 default:
397 LOG_ERR("Unknown MODBUS mode");
398 }
399
400 k_work_cancel_sync(&ctx->server_work, &work_sync);
401 ctx->rxwait_to = 0;
402 ctx->unit_id = 0;
403 ctx->mbs_user_cb = NULL;
404 atomic_clear_bit(&ctx->state, MODBUS_STATE_CONFIGURED);
405
406 LOG_INF("Modbus interface %u disabled", iface);
407
408 return 0;
409 }
410