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