1 /*
2  * Copyright (c) 2020 PHYTEC Messtechnik GmbH
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*
8  * This file is based on mbm_core.c from uC/Modbus Stack.
9  *
10  *                                uC/Modbus
11  *                         The Embedded Modbus Stack
12  *
13  *      Copyright 2003-2020 Silicon Laboratories Inc. www.silabs.com
14  *
15  *                   SPDX-License-Identifier: APACHE-2.0
16  *
17  * This software is subject to an open source license and is distributed by
18  *  Silicon Laboratories Inc. pursuant to the terms of the Apache License,
19  *      Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
20  */
21 
22 #include <string.h>
23 #include <zephyr/sys/byteorder.h>
24 #include <modbus_internal.h>
25 
26 #include <zephyr/logging/log.h>
27 LOG_MODULE_REGISTER(modbus_c, CONFIG_MODBUS_LOG_LEVEL);
28 
mbc_validate_response_fc(struct modbus_context * ctx,const uint8_t unit_id,uint8_t fc)29 static int mbc_validate_response_fc(struct modbus_context *ctx,
30 				    const uint8_t unit_id,
31 				    uint8_t fc)
32 {
33 	uint8_t resp_fc = ctx->rx_adu.fc;
34 	uint8_t excep_code = ctx->rx_adu.data[0];
35 	const uint8_t excep_bit = BIT(7);
36 	const uint8_t excep_mask = BIT_MASK(7);
37 
38 	if (unit_id != ctx->rx_adu.unit_id) {
39 		return -EIO;
40 	}
41 
42 	if (fc != (resp_fc & excep_mask)) {
43 		return -EIO;
44 	}
45 
46 	if (resp_fc & excep_bit) {
47 		if (excep_code > MODBUS_EXC_NONE) {
48 			return excep_code;
49 		}
50 
51 		return -EIO;
52 	}
53 
54 	return 0;
55 }
56 
mbc_validate_fc03fp_response(struct modbus_context * ctx,float * ptbl)57 static int mbc_validate_fc03fp_response(struct modbus_context *ctx, float *ptbl)
58 {
59 	size_t resp_byte_cnt;
60 	size_t req_byte_cnt;
61 	uint16_t req_qty;
62 	uint8_t *resp_data;
63 
64 	resp_byte_cnt = ctx->rx_adu.data[0];
65 	resp_data = &ctx->rx_adu.data[1];
66 	req_qty = sys_get_be16(&ctx->tx_adu.data[2]);
67 	req_byte_cnt = req_qty * sizeof(uint16_t);
68 
69 	if (req_byte_cnt != resp_byte_cnt) {
70 		LOG_ERR("Mismatch in the number of registers");
71 		return -EINVAL;
72 	}
73 
74 	for (uint16_t i = 0; i < req_qty / 2; i++) {
75 		uint32_t reg_val = sys_get_be32(resp_data);
76 
77 		memcpy(&ptbl[i], &reg_val, sizeof(float));
78 		resp_data += sizeof(uint32_t);
79 	}
80 
81 	return 0;
82 }
83 
mbc_validate_rd_response(struct modbus_context * ctx,const uint8_t unit_id,uint8_t fc,uint8_t * data)84 static int mbc_validate_rd_response(struct modbus_context *ctx,
85 				    const uint8_t unit_id,
86 				    uint8_t fc,
87 				    uint8_t *data)
88 {
89 	int err;
90 	size_t resp_byte_cnt;
91 	size_t req_byte_cnt;
92 	uint16_t req_qty;
93 	uint16_t req_addr;
94 	uint8_t *resp_data;
95 	uint16_t *data_p16 = (uint16_t *)data;
96 
97 	if (data == NULL) {
98 		return -EINVAL;
99 	}
100 
101 	resp_byte_cnt = ctx->rx_adu.data[0];
102 	resp_data = &ctx->rx_adu.data[1];
103 	req_qty = sys_get_be16(&ctx->tx_adu.data[2]);
104 	req_addr = sys_get_be16(&ctx->tx_adu.data[0]);
105 
106 	if ((resp_byte_cnt + 1) > sizeof(ctx->rx_adu.data)) {
107 		LOG_ERR("Byte count exceeds buffer length");
108 		return -EINVAL;
109 	}
110 
111 	switch (fc) {
112 	case MODBUS_FC01_COIL_RD:
113 	case MODBUS_FC02_DI_RD:
114 		req_byte_cnt = ((req_qty - 1) / 8) + 1;
115 		if (req_byte_cnt != resp_byte_cnt) {
116 			LOG_ERR("Mismatch in the number of coils or inputs");
117 			err = -EINVAL;
118 		} else {
119 			for (uint8_t i = 0; i < resp_byte_cnt; i++) {
120 				data[i] = resp_data[i];
121 			}
122 
123 			err = 0;
124 		}
125 		break;
126 
127 	case MODBUS_FC03_HOLDING_REG_RD:
128 		if (IS_ENABLED(CONFIG_MODBUS_FP_EXTENSIONS) &&
129 		    (req_addr >= MODBUS_FP_EXTENSIONS_ADDR)) {
130 			err = mbc_validate_fc03fp_response(ctx, (float *)data);
131 			break;
132 		}
133 		__fallthrough;
134 	case MODBUS_FC04_IN_REG_RD:
135 		req_byte_cnt = req_qty * sizeof(uint16_t);
136 		if (req_byte_cnt != resp_byte_cnt) {
137 			LOG_ERR("Mismatch in the number of registers");
138 			err = -EINVAL;
139 		} else {
140 			for (uint16_t i = 0; i < req_qty; i++) {
141 				data_p16[i] = sys_get_be16(resp_data);
142 				resp_data += sizeof(uint16_t);
143 			}
144 
145 			err = 0;
146 		}
147 		break;
148 
149 	default:
150 		LOG_ERR("Validation not implemented for FC 0x%02x", fc);
151 		err = -ENOTSUP;
152 	}
153 
154 	return err;
155 }
156 
mbc_validate_fc08_response(struct modbus_context * ctx,const uint8_t unit_id,uint16_t * data)157 static int mbc_validate_fc08_response(struct modbus_context *ctx,
158 				      const uint8_t unit_id,
159 				      uint16_t *data)
160 {
161 	int err;
162 	uint16_t resp_sfunc;
163 	uint16_t resp_data;
164 	uint16_t req_sfunc;
165 	uint16_t req_data;
166 
167 	if (data == NULL) {
168 		return -EINVAL;
169 	}
170 
171 	req_sfunc = sys_get_be16(&ctx->tx_adu.data[0]);
172 	req_data = sys_get_be16(&ctx->tx_adu.data[2]);
173 	resp_sfunc = sys_get_be16(&ctx->rx_adu.data[0]);
174 	resp_data = sys_get_be16(&ctx->rx_adu.data[2]);
175 
176 	if (req_sfunc != resp_sfunc) {
177 		LOG_ERR("Mismatch in the sub-function code");
178 		return -EINVAL;
179 	}
180 
181 	switch (resp_sfunc) {
182 	case MODBUS_FC08_SUBF_QUERY:
183 	case MODBUS_FC08_SUBF_CLR_CTR:
184 		if (req_data != resp_data) {
185 			LOG_ERR("Request and response data are different");
186 			err = -EINVAL;
187 		} else {
188 			*data = resp_data;
189 			err = 0;
190 		}
191 		break;
192 
193 	case MODBUS_FC08_SUBF_BUS_MSG_CTR:
194 	case MODBUS_FC08_SUBF_BUS_CRC_CTR:
195 	case MODBUS_FC08_SUBF_BUS_EXCEPT_CTR:
196 	case MODBUS_FC08_SUBF_SERVER_MSG_CTR:
197 	case MODBUS_FC08_SUBF_SERVER_NO_RESP_CTR:
198 		*data = resp_data;
199 		err = 0;
200 		break;
201 
202 	default:
203 		err = -EINVAL;
204 	}
205 
206 	return err;
207 }
208 
mbc_validate_wr_response(struct modbus_context * ctx,const uint8_t unit_id,uint8_t fc)209 static int mbc_validate_wr_response(struct modbus_context *ctx,
210 				    const uint8_t unit_id,
211 				    uint8_t fc)
212 {
213 	int err;
214 	uint16_t req_addr;
215 	uint16_t req_value;
216 	uint16_t resp_addr;
217 	uint16_t resp_value;
218 
219 	req_addr = sys_get_be16(&ctx->tx_adu.data[0]);
220 	req_value = sys_get_be16(&ctx->tx_adu.data[2]);
221 	resp_addr = sys_get_be16(&ctx->rx_adu.data[0]);
222 	resp_value = sys_get_be16(&ctx->rx_adu.data[2]);
223 
224 	switch (fc) {
225 	case MODBUS_FC05_COIL_WR:
226 	case MODBUS_FC06_HOLDING_REG_WR:
227 	case MODBUS_FC15_COILS_WR:
228 	case MODBUS_FC16_HOLDING_REGS_WR:
229 		if (req_addr != resp_addr || req_value != resp_value) {
230 			err = ENXIO;
231 		} else {
232 			err = 0;
233 		}
234 		break;
235 
236 	default:
237 		LOG_ERR("Validation not implemented for FC 0x%02x", fc);
238 		err = -ENOTSUP;
239 	}
240 
241 	return err;
242 }
243 
mbc_send_cmd(struct modbus_context * ctx,const uint8_t unit_id,uint8_t fc,void * data)244 static int mbc_send_cmd(struct modbus_context *ctx, const uint8_t unit_id,
245 			uint8_t fc, void *data)
246 {
247 	int err;
248 
249 	ctx->tx_adu.unit_id = unit_id;
250 	ctx->tx_adu.fc = fc;
251 
252 	err = modbus_tx_wait_rx_adu(ctx);
253 	if (err != 0) {
254 		return err;
255 	}
256 
257 	err = mbc_validate_response_fc(ctx, unit_id, fc);
258 	if (err < 0) {
259 		LOG_ERR("Failed to validate unit ID or function code");
260 		return err;
261 	} else if (err > 0) {
262 		LOG_INF("Modbus FC %u, error code %u", fc, err);
263 		return err;
264 	}
265 
266 	switch (fc) {
267 	case MODBUS_FC01_COIL_RD:
268 	case MODBUS_FC02_DI_RD:
269 	case MODBUS_FC03_HOLDING_REG_RD:
270 	case MODBUS_FC04_IN_REG_RD:
271 		err = mbc_validate_rd_response(ctx, unit_id, fc, data);
272 		break;
273 
274 	case MODBUS_FC08_DIAGNOSTICS:
275 		err = mbc_validate_fc08_response(ctx, unit_id, data);
276 		break;
277 
278 	case MODBUS_FC05_COIL_WR:
279 	case MODBUS_FC06_HOLDING_REG_WR:
280 	case MODBUS_FC15_COILS_WR:
281 	case MODBUS_FC16_HOLDING_REGS_WR:
282 		err = mbc_validate_wr_response(ctx, unit_id, fc);
283 		break;
284 
285 	default:
286 		LOG_ERR("FC 0x%02x not implemented", fc);
287 		err = -ENOTSUP;
288 	}
289 
290 	return err;
291 }
292 
modbus_read_coils(const int iface,const uint8_t unit_id,const uint16_t start_addr,uint8_t * const coil_tbl,const uint16_t num_coils)293 int modbus_read_coils(const int iface,
294 		      const uint8_t unit_id,
295 		      const uint16_t start_addr,
296 		      uint8_t *const coil_tbl,
297 		      const uint16_t num_coils)
298 {
299 	struct modbus_context *ctx = modbus_get_context(iface);
300 	int err;
301 
302 	if (ctx == NULL) {
303 		return -ENODEV;
304 	}
305 
306 	k_mutex_lock(&ctx->iface_lock, K_FOREVER);
307 
308 	ctx->tx_adu.length = 4;
309 	sys_put_be16(start_addr, &ctx->tx_adu.data[0]);
310 	sys_put_be16(num_coils, &ctx->tx_adu.data[2]);
311 
312 	err = mbc_send_cmd(ctx, unit_id, MODBUS_FC01_COIL_RD, coil_tbl);
313 	k_mutex_unlock(&ctx->iface_lock);
314 
315 	return err;
316 }
317 
modbus_read_dinputs(const int iface,const uint8_t unit_id,const uint16_t start_addr,uint8_t * const di_tbl,const uint16_t num_di)318 int modbus_read_dinputs(const int iface,
319 			const uint8_t unit_id,
320 			const uint16_t start_addr,
321 			uint8_t *const di_tbl,
322 			const uint16_t num_di)
323 {
324 	struct modbus_context *ctx = modbus_get_context(iface);
325 	int err;
326 
327 	if (ctx == NULL) {
328 		return -ENODEV;
329 	}
330 
331 	k_mutex_lock(&ctx->iface_lock, K_FOREVER);
332 
333 	ctx->tx_adu.length = 4;
334 	sys_put_be16(start_addr, &ctx->tx_adu.data[0]);
335 	sys_put_be16(num_di, &ctx->tx_adu.data[2]);
336 
337 	err = mbc_send_cmd(ctx, unit_id, MODBUS_FC02_DI_RD, di_tbl);
338 	k_mutex_unlock(&ctx->iface_lock);
339 
340 	return err;
341 }
342 
modbus_read_holding_regs(const int iface,const uint8_t unit_id,const uint16_t start_addr,uint16_t * const reg_buf,const uint16_t num_regs)343 int modbus_read_holding_regs(const int iface,
344 			     const uint8_t unit_id,
345 			     const uint16_t start_addr,
346 			     uint16_t *const reg_buf,
347 			     const uint16_t num_regs)
348 {
349 	struct modbus_context *ctx = modbus_get_context(iface);
350 	int err;
351 
352 	if (ctx == NULL) {
353 		return -ENODEV;
354 	}
355 
356 	k_mutex_lock(&ctx->iface_lock, K_FOREVER);
357 
358 	ctx->tx_adu.length = 4;
359 	sys_put_be16(start_addr, &ctx->tx_adu.data[0]);
360 	sys_put_be16(num_regs, &ctx->tx_adu.data[2]);
361 
362 	err = mbc_send_cmd(ctx, unit_id, MODBUS_FC03_HOLDING_REG_RD, reg_buf);
363 	k_mutex_unlock(&ctx->iface_lock);
364 
365 	return err;
366 }
367 
368 
369 #ifdef CONFIG_MODBUS_FP_EXTENSIONS
modbus_read_holding_regs_fp(const int iface,const uint8_t unit_id,const uint16_t start_addr,float * const reg_buf,const uint16_t num_regs)370 int modbus_read_holding_regs_fp(const int iface,
371 			       const uint8_t unit_id,
372 			       const uint16_t start_addr,
373 			       float *const reg_buf,
374 			       const uint16_t num_regs)
375 {
376 	struct modbus_context *ctx = modbus_get_context(iface);
377 	int err;
378 
379 	if (ctx == NULL) {
380 		return -ENODEV;
381 	}
382 
383 	k_mutex_lock(&ctx->iface_lock, K_FOREVER);
384 
385 	ctx->tx_adu.length = 4;
386 	sys_put_be16(start_addr, &ctx->tx_adu.data[0]);
387 	/* A 32-bit float is mapped to two 16-bit registers */
388 	sys_put_be16(num_regs * 2, &ctx->tx_adu.data[2]);
389 
390 	err = mbc_send_cmd(ctx, unit_id, MODBUS_FC03_HOLDING_REG_RD, reg_buf);
391 	k_mutex_unlock(&ctx->iface_lock);
392 
393 	return err;
394 }
395 #endif
396 
modbus_read_input_regs(const int iface,const uint8_t unit_id,const uint16_t start_addr,uint16_t * const reg_buf,const uint16_t num_regs)397 int modbus_read_input_regs(const int iface,
398 			   const uint8_t unit_id,
399 			   const uint16_t start_addr,
400 			   uint16_t *const reg_buf,
401 			   const uint16_t num_regs)
402 {
403 	struct modbus_context *ctx = modbus_get_context(iface);
404 	int err;
405 
406 	if (ctx == NULL) {
407 		return -ENODEV;
408 	}
409 
410 	k_mutex_lock(&ctx->iface_lock, K_FOREVER);
411 
412 	ctx->tx_adu.length = 4;
413 	sys_put_be16(start_addr, &ctx->tx_adu.data[0]);
414 	sys_put_be16(num_regs, &ctx->tx_adu.data[2]);
415 
416 	err = mbc_send_cmd(ctx, unit_id, MODBUS_FC04_IN_REG_RD, reg_buf);
417 	k_mutex_unlock(&ctx->iface_lock);
418 
419 	return err;
420 }
421 
modbus_write_coil(const int iface,const uint8_t unit_id,const uint16_t coil_addr,const bool coil_state)422 int modbus_write_coil(const int iface,
423 		      const uint8_t unit_id,
424 		      const uint16_t coil_addr,
425 		      const bool coil_state)
426 {
427 	struct modbus_context *ctx = modbus_get_context(iface);
428 	int err;
429 	uint16_t coil_val;
430 
431 	if (ctx == NULL) {
432 		return -ENODEV;
433 	}
434 
435 	k_mutex_lock(&ctx->iface_lock, K_FOREVER);
436 
437 	if (coil_state == false) {
438 		coil_val = MODBUS_COIL_OFF_CODE;
439 	} else {
440 		coil_val = MODBUS_COIL_ON_CODE;
441 	}
442 
443 	ctx->tx_adu.length = 4;
444 	sys_put_be16(coil_addr, &ctx->tx_adu.data[0]);
445 	sys_put_be16(coil_val, &ctx->tx_adu.data[2]);
446 
447 	err = mbc_send_cmd(ctx, unit_id, MODBUS_FC05_COIL_WR, NULL);
448 	k_mutex_unlock(&ctx->iface_lock);
449 
450 	return err;
451 }
452 
modbus_write_holding_reg(const int iface,const uint8_t unit_id,const uint16_t start_addr,const uint16_t reg_val)453 int modbus_write_holding_reg(const int iface,
454 			     const uint8_t unit_id,
455 			     const uint16_t start_addr,
456 			     const uint16_t reg_val)
457 {
458 	struct modbus_context *ctx = modbus_get_context(iface);
459 	int err;
460 
461 	if (ctx == NULL) {
462 		return -ENODEV;
463 	}
464 
465 	k_mutex_lock(&ctx->iface_lock, K_FOREVER);
466 
467 	ctx->tx_adu.length = 4;
468 	sys_put_be16(start_addr, &ctx->tx_adu.data[0]);
469 	sys_put_be16(reg_val, &ctx->tx_adu.data[2]);
470 
471 	err = mbc_send_cmd(ctx, unit_id, MODBUS_FC06_HOLDING_REG_WR, NULL);
472 	k_mutex_unlock(&ctx->iface_lock);
473 
474 	return err;
475 }
476 
modbus_request_diagnostic(const int iface,const uint8_t unit_id,const uint16_t sfunc,const uint16_t data,uint16_t * const data_out)477 int modbus_request_diagnostic(const int iface,
478 			      const uint8_t unit_id,
479 			      const uint16_t sfunc,
480 			      const uint16_t data,
481 			      uint16_t *const data_out)
482 {
483 	struct modbus_context *ctx = modbus_get_context(iface);
484 	int err;
485 
486 	if (ctx == NULL) {
487 		return -ENODEV;
488 	}
489 
490 	k_mutex_lock(&ctx->iface_lock, K_FOREVER);
491 
492 	ctx->tx_adu.length = 4;
493 	sys_put_be16(sfunc, &ctx->tx_adu.data[0]);
494 	sys_put_be16(data, &ctx->tx_adu.data[2]);
495 
496 	err = mbc_send_cmd(ctx, unit_id, MODBUS_FC08_DIAGNOSTICS, data_out);
497 	k_mutex_unlock(&ctx->iface_lock);
498 
499 	return err;
500 }
501 
modbus_write_coils(const int iface,const uint8_t unit_id,const uint16_t start_addr,uint8_t * const coil_tbl,const uint16_t num_coils)502 int modbus_write_coils(const int iface,
503 		       const uint8_t unit_id,
504 		       const uint16_t start_addr,
505 		       uint8_t *const coil_tbl,
506 		       const uint16_t num_coils)
507 {
508 	struct modbus_context *ctx = modbus_get_context(iface);
509 	size_t length = 0;
510 	uint8_t *data_ptr;
511 	size_t num_bytes;
512 	int err;
513 
514 	if (ctx == NULL) {
515 		return -ENODEV;
516 	}
517 
518 	k_mutex_lock(&ctx->iface_lock, K_FOREVER);
519 
520 	sys_put_be16(start_addr, &ctx->tx_adu.data[0]);
521 	length += sizeof(start_addr);
522 	sys_put_be16(num_coils, &ctx->tx_adu.data[2]);
523 	length += sizeof(num_coils);
524 
525 	num_bytes = (uint8_t)(((num_coils - 1) / 8) + 1);
526 	ctx->tx_adu.data[4] = num_bytes;
527 	length += num_bytes + 1;
528 
529 	if (length > sizeof(ctx->tx_adu.data)) {
530 		LOG_ERR("Length of data buffer is not sufficient");
531 		k_mutex_unlock(&ctx->iface_lock);
532 		return -ENOBUFS;
533 	}
534 
535 	ctx->tx_adu.length = length;
536 	data_ptr = &ctx->tx_adu.data[5];
537 
538 	memcpy(data_ptr, coil_tbl, num_bytes);
539 
540 	err = mbc_send_cmd(ctx, unit_id, MODBUS_FC15_COILS_WR, NULL);
541 	k_mutex_unlock(&ctx->iface_lock);
542 
543 	return err;
544 }
545 
modbus_write_holding_regs(const int iface,const uint8_t unit_id,const uint16_t start_addr,uint16_t * const reg_buf,const uint16_t num_regs)546 int modbus_write_holding_regs(const int iface,
547 			      const uint8_t unit_id,
548 			      const uint16_t start_addr,
549 			      uint16_t *const reg_buf,
550 			      const uint16_t num_regs)
551 {
552 	struct modbus_context *ctx = modbus_get_context(iface);
553 	size_t length = 0;
554 	uint8_t *data_ptr;
555 	size_t num_bytes;
556 	int err;
557 
558 	if (ctx == NULL) {
559 		return -ENODEV;
560 	}
561 
562 	k_mutex_lock(&ctx->iface_lock, K_FOREVER);
563 
564 	sys_put_be16(start_addr, &ctx->tx_adu.data[0]);
565 	length += sizeof(start_addr);
566 	sys_put_be16(num_regs, &ctx->tx_adu.data[2]);
567 	length += sizeof(num_regs);
568 
569 	num_bytes = num_regs * sizeof(uint16_t);
570 	ctx->tx_adu.data[4] = num_bytes;
571 	length += num_bytes + 1;
572 
573 	if (length > sizeof(ctx->tx_adu.data)) {
574 		LOG_ERR("Length of data buffer is not sufficient");
575 		k_mutex_unlock(&ctx->iface_lock);
576 		return -ENOBUFS;
577 	}
578 
579 	ctx->tx_adu.length = length;
580 	data_ptr = &ctx->tx_adu.data[5];
581 
582 	for (uint16_t i = 0; i < num_regs; i++) {
583 		sys_put_be16(reg_buf[i], data_ptr);
584 		data_ptr += sizeof(uint16_t);
585 	}
586 
587 	err = mbc_send_cmd(ctx, unit_id, MODBUS_FC16_HOLDING_REGS_WR, NULL);
588 	k_mutex_unlock(&ctx->iface_lock);
589 
590 	return err;
591 }
592 
593 #ifdef CONFIG_MODBUS_FP_EXTENSIONS
modbus_write_holding_regs_fp(const int iface,const uint8_t unit_id,const uint16_t start_addr,float * const reg_buf,const uint16_t num_regs)594 int modbus_write_holding_regs_fp(const int iface,
595 				 const uint8_t unit_id,
596 				 const uint16_t start_addr,
597 				 float *const reg_buf,
598 				 const uint16_t num_regs)
599 {
600 	struct modbus_context *ctx = modbus_get_context(iface);
601 	size_t length = 0;
602 	uint8_t *data_ptr;
603 	size_t num_bytes;
604 	int err;
605 
606 	if (ctx == NULL) {
607 		return -ENODEV;
608 	}
609 
610 	k_mutex_lock(&ctx->iface_lock, K_FOREVER);
611 
612 	sys_put_be16(start_addr, &ctx->tx_adu.data[0]);
613 	length += sizeof(start_addr);
614 	/* A 32-bit float is mapped to two 16-bit registers */
615 	sys_put_be16(num_regs * 2, &ctx->tx_adu.data[2]);
616 	length += sizeof(num_regs);
617 
618 	num_bytes = num_regs * sizeof(float);
619 	ctx->tx_adu.data[4] = num_bytes;
620 	length += num_bytes + 1;
621 
622 	if (length > sizeof(ctx->tx_adu.data)) {
623 		LOG_ERR("Length of data buffer is not sufficient");
624 		k_mutex_unlock(&ctx->iface_lock);
625 		return -ENOBUFS;
626 	}
627 
628 	ctx->tx_adu.length = length;
629 	data_ptr = &ctx->tx_adu.data[5];
630 
631 	for (uint16_t i = 0; i < num_regs; i++) {
632 		uint32_t reg_val;
633 
634 		memcpy(&reg_val, &reg_buf[i], sizeof(reg_val));
635 		sys_put_be32(reg_val, data_ptr);
636 		data_ptr += sizeof(uint32_t);
637 	}
638 
639 	err = mbc_send_cmd(ctx, unit_id, MODBUS_FC16_HOLDING_REGS_WR, NULL);
640 	k_mutex_unlock(&ctx->iface_lock);
641 
642 	return err;
643 }
644 #endif
645