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 /*
9  * Client API in this file is based on mbm_core.c from uC/Modbus Stack.
10  *
11  *                                uC/Modbus
12  *                         The Embedded Modbus Stack
13  *
14  *      Copyright 2003-2020 Silicon Laboratories Inc. www.silabs.com
15  *
16  *                   SPDX-License-Identifier: APACHE-2.0
17  *
18  * This software is subject to an open source license and is distributed by
19  *  Silicon Laboratories Inc. pursuant to the terms of the Apache License,
20  *      Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
21  */
22 
23 /**
24  * @brief MODBUS transport protocol API
25  * @defgroup modbus MODBUS
26  * @ingroup io_interfaces
27  * @{
28  */
29 
30 #ifndef ZEPHYR_INCLUDE_MODBUS_H_
31 #define ZEPHYR_INCLUDE_MODBUS_H_
32 
33 #include <zephyr/drivers/uart.h>
34 #include <zephyr/sys/slist.h>
35 #ifdef __cplusplus
36 extern "C" {
37 #endif
38 
39 /** Length of MBAP Header */
40 #define MODBUS_MBAP_LENGTH		7
41 /** Length of MBAP Header plus function code */
42 #define MODBUS_MBAP_AND_FC_LENGTH	(MODBUS_MBAP_LENGTH + 1)
43 
44 /** @name Modbus exception codes
45  *  @{
46  */
47 /** No exception */
48 #define MODBUS_EXC_NONE				0
49 /** Illegal function code */
50 #define MODBUS_EXC_ILLEGAL_FC			1
51 /** Illegal data address */
52 #define MODBUS_EXC_ILLEGAL_DATA_ADDR		2
53 /** Illegal data value */
54 #define MODBUS_EXC_ILLEGAL_DATA_VAL		3
55 /** Server device failure */
56 #define MODBUS_EXC_SERVER_DEVICE_FAILURE	4
57 /** Acknowledge */
58 #define MODBUS_EXC_ACK				5
59 /** Server device busy */
60 #define MODBUS_EXC_SERVER_DEVICE_BUSY		6
61 /** Memory parity error */
62 #define MODBUS_EXC_MEM_PARITY_ERROR		8
63 /** Gateway path unavailable */
64 #define MODBUS_EXC_GW_PATH_UNAVAILABLE		10
65 /** Gateway target device failed to respond */
66 #define MODBUS_EXC_GW_TARGET_FAILED_TO_RESP	11
67 /** @} */
68 
69 /**
70  * @brief Frame struct used internally and for raw ADU support.
71  */
72 struct modbus_adu {
73 	/** Transaction Identifier */
74 	uint16_t trans_id;
75 	/** Protocol Identifier */
76 	uint16_t proto_id;
77 	/** Length of the data only (not the length of unit ID + PDU) */
78 	uint16_t length;
79 	/** Unit Identifier */
80 	uint8_t unit_id;
81 	/** Function Code */
82 	uint8_t fc;
83 	/** Transaction Data */
84 	uint8_t data[CONFIG_MODBUS_BUFFER_SIZE - 4];
85 	/** RTU CRC */
86 	uint16_t crc;
87 };
88 
89 /**
90  * @brief Coil read (FC01)
91  *
92  * Sends a Modbus message to read the status of coils from a server.
93  *
94  * @param iface      Modbus interface index
95  * @param unit_id    Modbus unit ID of the server
96  * @param start_addr Coil starting address
97  * @param coil_tbl   Pointer to an array of bytes containing the value
98  *                   of the coils read.
99  *                   The format is:
100  *
101  *                                       MSB                               LSB
102  *                                       B7   B6   B5   B4   B3   B2   B1   B0
103  *                                       -------------------------------------
104  *                       coil_tbl[0]     #8   #7                            #1
105  *                       coil_tbl[1]     #16  #15                           #9
106  *                            :
107  *                            :
108  *
109  *                   Note that the array that will be receiving the coil
110  *                   values must be greater than or equal to:
111  *                   (num_coils - 1) / 8 + 1
112  * @param num_coils  Quantity of coils to read
113  *
114  * @retval           0 If the function was successful
115  */
116 int modbus_read_coils(const int iface,
117 		      const uint8_t unit_id,
118 		      const uint16_t start_addr,
119 		      uint8_t *const coil_tbl,
120 		      const uint16_t num_coils);
121 
122 /**
123  * @brief Read discrete inputs (FC02)
124  *
125  * Sends a Modbus message to read the status of discrete inputs from
126  * a server.
127  *
128  * @param iface      Modbus interface index
129  * @param unit_id    Modbus unit ID of the server
130  * @param start_addr Discrete input starting address
131  * @param di_tbl     Pointer to an array that will receive the state
132  *                   of the discrete inputs.
133  *                   The format of the array is as follows:
134  *
135  *                                     MSB                               LSB
136  *                                     B7   B6   B5   B4   B3   B2   B1   B0
137  *                                     -------------------------------------
138  *                       di_tbl[0]     #8   #7                            #1
139  *                       di_tbl[1]     #16  #15                           #9
140  *                            :
141  *                            :
142  *
143  *                   Note that the array that will be receiving the discrete
144  *                   input values must be greater than or equal to:
145  *                        (num_di - 1) / 8 + 1
146  * @param num_di     Quantity of discrete inputs to read
147  *
148  * @retval           0 If the function was successful
149  */
150 int modbus_read_dinputs(const int iface,
151 			const uint8_t unit_id,
152 			const uint16_t start_addr,
153 			uint8_t *const di_tbl,
154 			const uint16_t num_di);
155 
156 /**
157  * @brief Read holding registers (FC03)
158  *
159  * Sends a Modbus message to read the value of holding registers
160  * from a server.
161  *
162  * @param iface      Modbus interface index
163  * @param unit_id    Modbus unit ID of the server
164  * @param start_addr Register starting address
165  * @param reg_buf    Is a pointer to an array that will receive
166  *                   the current values of the holding registers from
167  *                   the server.  The array pointed to by 'reg_buf' needs
168  *                   to be able to hold at least 'num_regs' entries.
169  * @param num_regs   Quantity of registers to read
170  *
171  * @retval           0 If the function was successful
172  */
173 int modbus_read_holding_regs(const int iface,
174 			     const uint8_t unit_id,
175 			     const uint16_t start_addr,
176 			     uint16_t *const reg_buf,
177 			     const uint16_t num_regs);
178 
179 /**
180  * @brief Read input registers (FC04)
181  *
182  * Sends a Modbus message to read the value of input registers from
183  * a server.
184  *
185  * @param iface      Modbus interface index
186  * @param unit_id    Modbus unit ID of the server
187  * @param start_addr Register starting address
188  * @param reg_buf    Is a pointer to an array that will receive
189  *                   the current value of the holding registers
190  *                   from the server.  The array pointed to by 'reg_buf'
191  *                   needs to be able to hold at least 'num_regs' entries.
192  * @param num_regs   Quantity of registers to read
193  *
194  * @retval           0 If the function was successful
195  */
196 int modbus_read_input_regs(const int iface,
197 			   const uint8_t unit_id,
198 			   const uint16_t start_addr,
199 			   uint16_t *const reg_buf,
200 			   const uint16_t num_regs);
201 
202 /**
203  * @brief Write single coil (FC05)
204  *
205  * Sends a Modbus message to write the value of single coil to a server.
206  *
207  * @param iface      Modbus interface index
208  * @param unit_id    Modbus unit ID of the server
209  * @param coil_addr  Coils starting address
210  * @param coil_state Is the desired state of the coil
211  *
212  * @retval           0 If the function was successful
213  */
214 int modbus_write_coil(const int iface,
215 		      const uint8_t unit_id,
216 		      const uint16_t coil_addr,
217 		      const bool coil_state);
218 
219 /**
220  * @brief Write single holding register (FC06)
221  *
222  * Sends a Modbus message to write the value of single holding register
223  * to a server unit.
224  *
225  * @param iface      Modbus interface index
226  * @param unit_id    Modbus unit ID of the server
227  * @param start_addr Coils starting address
228  * @param reg_val    Desired value of the holding register
229  *
230  * @retval           0 If the function was successful
231  */
232 int modbus_write_holding_reg(const int iface,
233 			     const uint8_t unit_id,
234 			     const uint16_t start_addr,
235 			     const uint16_t reg_val);
236 
237 /**
238  * @brief Read diagnostic (FC08)
239  *
240  * Sends a Modbus message to perform a diagnostic function of a server unit.
241  *
242  * @param iface      Modbus interface index
243  * @param unit_id    Modbus unit ID of the server
244  * @param sfunc      Diagnostic sub-function code
245  * @param data       Sub-function data
246  * @param data_out   Pointer to the data value
247  *
248  * @retval           0 If the function was successful
249  */
250 int modbus_request_diagnostic(const int iface,
251 			      const uint8_t unit_id,
252 			      const uint16_t sfunc,
253 			      const uint16_t data,
254 			      uint16_t *const data_out);
255 
256 /**
257  * @brief Write coils (FC15)
258  *
259  * Sends a Modbus message to write to coils on a server unit.
260  *
261  * @param iface      Modbus interface index
262  * @param unit_id    Modbus unit ID of the server
263  * @param start_addr Coils starting address
264  * @param coil_tbl   Pointer to an array of bytes containing the value
265  *                   of the coils to write.
266  *                   The format is:
267  *
268  *                                       MSB                               LSB
269  *                                       B7   B6   B5   B4   B3   B2   B1   B0
270  *                                       -------------------------------------
271  *                       coil_tbl[0]     #8   #7                            #1
272  *                       coil_tbl[1]     #16  #15                           #9
273  *                            :
274  *                            :
275  *
276  *                   Note that the array that will be receiving the coil
277  *                   values must be greater than or equal to:
278  *                   (num_coils - 1) / 8 + 1
279  * @param num_coils  Quantity of coils to write
280  *
281  * @retval           0 If the function was successful
282  */
283 int modbus_write_coils(const int iface,
284 		       const uint8_t unit_id,
285 		       const uint16_t start_addr,
286 		       uint8_t *const coil_tbl,
287 		       const uint16_t num_coils);
288 
289 /**
290  * @brief Write holding registers (FC16)
291  *
292  * Sends a Modbus message to write to integer holding registers
293  * to a server unit.
294  *
295  * @param iface      Modbus interface index
296  * @param unit_id    Modbus unit ID of the server
297  * @param start_addr Register starting address
298  * @param reg_buf    Is a pointer to an array containing
299  *                   the value of the holding registers to write.
300  *                   Note that the array containing the register values must
301  *                   be greater than or equal to 'num_regs'
302  * @param num_regs   Quantity of registers to write
303  *
304  * @retval           0 If the function was successful
305  */
306 int modbus_write_holding_regs(const int iface,
307 			      const uint8_t unit_id,
308 			      const uint16_t start_addr,
309 			      uint16_t *const reg_buf,
310 			      const uint16_t num_regs);
311 
312 /**
313  * @brief Read floating-point holding registers (FC03)
314  *
315  * Sends a Modbus message to read the value of floating-point
316  * holding registers from a server unit.
317  *
318  * @param iface      Modbus interface index
319  * @param unit_id    Modbus unit ID of the server
320  * @param start_addr Register starting address
321  * @param reg_buf    Is a pointer to an array that will receive
322  *                   the current values of the holding registers from
323  *                   the server.  The array pointed to by 'reg_buf' needs
324  *                   to be able to hold at least 'num_regs' entries.
325  * @param num_regs   Quantity of registers to read
326  *
327  * @retval           0 If the function was successful
328  */
329 int modbus_read_holding_regs_fp(const int iface,
330 				const uint8_t unit_id,
331 				const uint16_t start_addr,
332 				float *const reg_buf,
333 				const uint16_t num_regs);
334 
335 /**
336  * @brief Write floating-point holding registers (FC16)
337  *
338  * Sends a Modbus message to write to floating-point holding registers
339  * to a server unit.
340  *
341  * @param iface      Modbus interface index
342  * @param unit_id    Modbus unit ID of the server
343  * @param start_addr Register starting address
344  * @param reg_buf    Is a pointer to an array containing
345  *                   the value of the holding registers to write.
346  *                   Note that the array containing the register values must
347  *                   be greater than or equal to 'num_regs'
348  * @param num_regs   Quantity of registers to write
349  *
350  * @retval           0 If the function was successful
351  */
352 int modbus_write_holding_regs_fp(const int iface,
353 				 const uint8_t unit_id,
354 				 const uint16_t start_addr,
355 				 float *const reg_buf,
356 				 const uint16_t num_regs);
357 
358 /** Modbus Server User Callback structure */
359 struct modbus_user_callbacks {
360 	/** Coil read callback */
361 	int (*coil_rd)(uint16_t addr, bool *state);
362 
363 	/** Coil write callback */
364 	int (*coil_wr)(uint16_t addr, bool state);
365 
366 	/** Discrete Input read callback */
367 	int (*discrete_input_rd)(uint16_t addr, bool *state);
368 
369 	/** Input Register read callback */
370 	int (*input_reg_rd)(uint16_t addr, uint16_t *reg);
371 
372 	/** Floating Point Input Register read callback */
373 	int (*input_reg_rd_fp)(uint16_t addr, float *reg);
374 
375 	/** Holding Register read callback */
376 	int (*holding_reg_rd)(uint16_t addr, uint16_t *reg);
377 
378 	/** Holding Register write callback */
379 	int (*holding_reg_wr)(uint16_t addr, uint16_t reg);
380 
381 	/** Floating Point Holding Register read callback */
382 	int (*holding_reg_rd_fp)(uint16_t addr, float *reg);
383 
384 	/** Floating Point Holding Register write callback */
385 	int (*holding_reg_wr_fp)(uint16_t addr, float reg);
386 };
387 
388 /**
389  * @brief Get Modbus interface index according to interface name
390  *
391  * If there is more than one interface, it can be used to clearly
392  * identify interfaces in the application.
393  *
394  * @param iface_name Modbus interface name
395  *
396  * @retval           Modbus interface index or negative error value.
397  */
398 int modbus_iface_get_by_name(const char *iface_name);
399 
400 /**
401  * @brief ADU raw callback function signature
402  *
403  * @param iface      Modbus RTU interface index
404  * @param adu        Pointer to the RAW ADU struct to send
405  * @param user_data  Pointer to the user data
406  *
407  * @retval           0 If transfer was successful
408  */
409 typedef int (*modbus_raw_cb_t)(const int iface, const struct modbus_adu *adu,
410 				void *user_data);
411 
412 /**
413  * @brief Custom function code handler function signature.
414  *
415  * Modbus allows user defined function codes which can be used to extend
416  * the base protocol. These callbacks can also be used to implement
417  * function codes currently not supported by Zephyr's Modbus subsystem.
418  *
419  * If an error occurs during the handling of the request, the handler should
420  * signal this by setting excep_code to a modbus exception code.
421  *
422  * User data pointer can be used to pass state between subsequent calls to
423  * the handler.
424  *
425  * @param iface      Modbus interface index
426  * @param rx_adu     Pointer to the received ADU struct
427  * @param tx_adu     Pointer to the outgoing ADU struct
428  * @param excep_code Pointer to possible exception code
429  * @param user_data  Pointer to user data
430  *
431  * @retval           true If response should be sent, false otherwise
432  */
433 typedef bool (*modbus_custom_cb_t)(const int iface,
434 				const struct modbus_adu *const rx_adu,
435 				struct modbus_adu *const tx_adu,
436 				uint8_t *const excep_code,
437 				void *const user_data);
438 
439 /** @cond INTERNAL_HIDDEN */
440 /**
441  * @brief Custom function code definition.
442  */
443 struct modbus_custom_fc {
444 	sys_snode_t node;
445 	modbus_custom_cb_t cb;
446 	void *user_data;
447 	uint8_t fc;
448 	uint8_t excep_code;
449 };
450 /** @endcond INTERNAL_HIDDEN */
451 
452 /**
453  * @brief Helper macro for initializing custom function code structs
454  */
455 #define MODBUS_CUSTOM_FC_DEFINE(name, user_cb, user_fc, userdata)	\
456 	static struct modbus_custom_fc modbus_cfg_##name = {		\
457 		.cb = user_cb,						\
458 		.user_data = userdata,					\
459 		.fc = user_fc,						\
460 		.excep_code = MODBUS_EXC_NONE,				\
461 	}
462 
463 /**
464  * @brief Modbus interface mode
465  */
466 enum modbus_mode {
467 	/** Modbus over serial line RTU mode */
468 	MODBUS_MODE_RTU,
469 	/** Modbus over serial line ASCII mode */
470 	MODBUS_MODE_ASCII,
471 	/** Modbus raw ADU mode */
472 	MODBUS_MODE_RAW,
473 };
474 
475 /**
476  * @brief Modbus serial line parameter
477  */
478 struct modbus_serial_param {
479 	/** Baudrate of the serial line */
480 	uint32_t baud;
481 	/** parity UART's parity setting:
482 	 *    UART_CFG_PARITY_NONE,
483 	 *    UART_CFG_PARITY_EVEN,
484 	 *    UART_CFG_PARITY_ODD
485 	 */
486 	enum uart_config_parity parity;
487 	/** stop_bits_client UART's stop bits setting if in client mode:
488 	 *    UART_CFG_STOP_BITS_0_5,
489 	 *    UART_CFG_STOP_BITS_1,
490 	 *    UART_CFG_STOP_BITS_1_5,
491 	 *    UART_CFG_STOP_BITS_2,
492 	 */
493 	enum uart_config_stop_bits stop_bits_client;
494 };
495 
496 /**
497  * @brief Modbus server parameter
498  */
499 struct modbus_server_param {
500 	/** Pointer to the User Callback structure */
501 	struct modbus_user_callbacks *user_cb;
502 	/** Modbus unit ID of the server */
503 	uint8_t unit_id;
504 };
505 
506 struct modbus_raw_cb {
507 	modbus_raw_cb_t raw_tx_cb;
508 	void *user_data;
509 };
510 
511 /**
512  * @brief User parameter structure to configure Modbus interface
513  *        as client or server.
514  */
515 struct modbus_iface_param {
516 	/** Mode of the interface */
517 	enum modbus_mode mode;
518 	union {
519 		struct modbus_server_param server;
520 		/** Amount of time client will wait for
521 		 *  a response from the server.
522 		 */
523 		uint32_t rx_timeout;
524 	};
525 	union {
526 		/** Serial support parameter of the interface */
527 		struct modbus_serial_param serial;
528 		/** Pointer to raw ADU callback function */
529 		struct modbus_raw_cb rawcb;
530 	};
531 };
532 
533 /**
534  * @brief Configure Modbus Interface as raw ADU server
535  *
536  * @param iface      Modbus RTU interface index
537  * @param param      Configuration parameter of the server interface
538  *
539  * @retval           0 If the function was successful
540  */
541 int modbus_init_server(const int iface, struct modbus_iface_param param);
542 
543 /**
544  * @brief Configure Modbus Interface as raw ADU client
545  *
546  * @param iface      Modbus RTU interface index
547  * @param param      Configuration parameter of the client interface
548  *
549  * @retval           0 If the function was successful
550  */
551 int modbus_init_client(const int iface, struct modbus_iface_param param);
552 
553 /**
554  * @brief Disable Modbus Interface
555  *
556  * This function is called to disable Modbus interface.
557  *
558  * @param iface      Modbus interface index
559  *
560  * @retval           0 If the function was successful
561  */
562 int modbus_disable(const uint8_t iface);
563 
564 /**
565  * @brief Submit raw ADU
566  *
567  * @param iface      Modbus RTU interface index
568  * @param adu        Pointer to the RAW ADU struct that is received
569  *
570  * @retval           0 If transfer was successful
571  */
572 int modbus_raw_submit_rx(const int iface, const struct modbus_adu *adu);
573 
574 /**
575  * @brief Put MBAP header into a buffer
576  *
577  * @param adu        Pointer to the RAW ADU struct
578  * @param header     Pointer to the buffer in which MBAP header
579  *                   will be placed.
580  */
581 void modbus_raw_put_header(const struct modbus_adu *adu, uint8_t *header);
582 
583 /**
584  * @brief Get MBAP header from a buffer
585  *
586  * @param adu        Pointer to the RAW ADU struct
587  * @param header     Pointer to the buffer containing MBAP header
588  */
589 void modbus_raw_get_header(struct modbus_adu *adu, const uint8_t *header);
590 
591 /**
592  * @brief Set Server Device Failure exception
593  *
594  * This function modifies ADU passed by the pointer.
595  *
596  * @param adu        Pointer to the RAW ADU struct
597  */
598 void modbus_raw_set_server_failure(struct modbus_adu *adu);
599 
600 /**
601  * @brief Use interface as backend to send and receive ADU
602  *
603  * This function overwrites ADU passed by the pointer and generates
604  * exception responses if backend interface is misconfigured or
605  * target device is unreachable.
606  *
607  * @param iface      Modbus client interface index
608  * @param adu        Pointer to the RAW ADU struct
609  *
610  * @retval           0 If transfer was successful
611  */
612 int modbus_raw_backend_txn(const int iface, struct modbus_adu *adu);
613 
614 /**
615  * @brief Register a user-defined function code handler.
616  *
617  * The Modbus specification allows users to define standard function codes
618  * missing from Zephyr's Modbus implementation as well as add non-standard
619  * function codes in the ranges 65 to 72 and 100 to 110 (decimal), as per
620  * specification.
621  *
622  * This function registers a new handler at runtime for the given
623  * function code.
624  *
625  * @param iface        Modbus client interface index
626  * @param custom_fc    User defined function code and callback pair
627  *
628  * @retval           0 on success
629  */
630 int modbus_register_user_fc(const int iface, struct modbus_custom_fc *custom_fc);
631 
632 #ifdef __cplusplus
633 }
634 #endif
635 
636 /**
637  * @}
638  */
639 
640 #endif /* ZEPHYR_INCLUDE_MODBUS_H_ */
641