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  * Parts of this file are based on mb.h 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 #ifndef ZEPHYR_INCLUDE_MODBUS_INTERNAL_H_
24 #define ZEPHYR_INCLUDE_MODBUS_INTERNAL_H_
25 
26 #include <zephyr/kernel.h>
27 #include <zephyr/drivers/gpio.h>
28 #include <zephyr/modbus/modbus.h>
29 
30 #ifdef CONFIG_MODBUS_FP_EXTENSIONS
31 #define MODBUS_FP_EXTENSIONS_ADDR		5000
32 #else
33 #define MODBUS_FP_EXTENSIONS_ADDR		UINT16_MAX
34 #endif
35 
36 #define MODBUS_RTU_MTU				256
37 
38 /* Modbus function codes */
39 #define	MODBUS_FC01_COIL_RD			1
40 #define	MODBUS_FC02_DI_RD			2
41 #define	MODBUS_FC03_HOLDING_REG_RD		3
42 #define	MODBUS_FC04_IN_REG_RD			4
43 #define	MODBUS_FC05_COIL_WR			5
44 #define	MODBUS_FC06_HOLDING_REG_WR		6
45 #define	MODBUS_FC08_DIAGNOSTICS			8
46 #define	MODBUS_FC15_COILS_WR			15
47 #define	MODBUS_FC16_HOLDING_REGS_WR		16
48 
49 /* Diagnostic sub-function codes */
50 #define MODBUS_FC08_SUBF_QUERY			0
51 #define MODBUS_FC08_SUBF_CLR_CTR		10
52 #define MODBUS_FC08_SUBF_BUS_MSG_CTR		11
53 #define MODBUS_FC08_SUBF_BUS_CRC_CTR		12
54 #define MODBUS_FC08_SUBF_BUS_EXCEPT_CTR		13
55 #define MODBUS_FC08_SUBF_SERVER_MSG_CTR		14
56 #define MODBUS_FC08_SUBF_SERVER_NO_RESP_CTR	15
57 
58 /* Modbus RTU (ASCII) constants */
59 #define MODBUS_COIL_OFF_CODE			0x0000
60 #define MODBUS_COIL_ON_CODE			0xFF00
61 #define MODBUS_RTU_MIN_MSG_SIZE			4
62 #define MODBUS_CRC16_POLY			0xA001
63 #define MODBUS_ASCII_MIN_MSG_SIZE		11
64 #define MODBUS_ASCII_START_FRAME_CHAR		':'
65 #define MODBUS_ASCII_END_FRAME_CHAR1		'\r'
66 #define MODBUS_ASCII_END_FRAME_CHAR2		'\n'
67 
68 /* Modbus ADU constants */
69 #define MODBUS_ADU_PROTO_ID			0x0000
70 
71 struct modbus_serial_config {
72 	/* UART device */
73 	const struct device *dev;
74 	/* RTU timeout (maximum inter-frame delay) */
75 	uint32_t rtu_timeout;
76 	/* Pointer to current position in buffer */
77 	uint8_t *uart_buf_ptr;
78 	/* Pointer to driver enable (DE) pin config */
79 	struct gpio_dt_spec *de;
80 	/* Pointer to receiver enable (nRE) pin config */
81 	struct gpio_dt_spec *re;
82 	/* RTU timer to detect frame end point */
83 	struct k_timer rtu_timer;
84 	/* Number of bytes received or to send */
85 	uint16_t uart_buf_ctr;
86 	/* Storage of received characters or characters to send */
87 	uint8_t uart_buf[CONFIG_MODBUS_BUFFER_SIZE];
88 };
89 
90 #define MODBUS_STATE_CONFIGURED		0
91 #define MODBUS_STATE_RX_ENABLED		1
92 
93 struct modbus_context {
94 	/* Interface name */
95 	const char *iface_name;
96 	union {
97 		/* Serial line configuration */
98 		struct modbus_serial_config *cfg;
99 		/* RAW TX callback */
100 		struct modbus_raw_cb rawcb;
101 	};
102 	/* MODBUS mode */
103 	enum modbus_mode mode;
104 	/* True if interface is configured as client */
105 	bool client;
106 	/* Amount of time client is willing to wait for response from server */
107 	uint32_t rxwait_to;
108 	/* Pointer to user server callbacks */
109 	struct modbus_user_callbacks *mbs_user_cb;
110 	/* Interface state */
111 	atomic_t state;
112 
113 	/* Client's mutually exclusive access */
114 	struct k_mutex iface_lock;
115 	/* Wait for response semaphore */
116 	struct k_sem client_wait_sem;
117 	/* Server work item */
118 	struct k_work server_work;
119 	/* Received frame */
120 	struct modbus_adu rx_adu;
121 	/* Frame to transmit */
122 	struct modbus_adu tx_adu;
123 
124 	/* Records error from frame reception, e.g. CRC error */
125 	int rx_adu_err;
126 
127 #ifdef CONFIG_MODBUS_FC08_DIAGNOSTIC
128 	uint16_t mbs_msg_ctr;
129 	uint16_t mbs_crc_err_ctr;
130 	uint16_t mbs_except_ctr;
131 	uint16_t mbs_server_msg_ctr;
132 	uint16_t mbs_noresp_ctr;
133 #endif
134 	/* A linked list of function code, handler pairs */
135 	sys_slist_t user_defined_cbs;
136 	/* Unit ID */
137 	uint8_t unit_id;
138 
139 };
140 
141 /**
142  * @brief Get Modbus interface context.
143  *
144  * @param ctx        Modbus interface context
145  *
146  * @retval           Pointer to interface context or NULL
147  *                   if interface not available or not configured;
148  */
149 struct modbus_context *modbus_get_context(const uint8_t iface);
150 
151 /**
152  * @brief Get Modbus interface index.
153  *
154  * @param ctx        Pointer to Modbus interface context
155  *
156  * @retval           Interface index or negative error value.
157  */
158 int modbus_iface_get_by_ctx(const struct modbus_context *ctx);
159 
160 /**
161  * @brief Send ADU.
162  *
163  * @param ctx        Modbus interface context
164  */
165 void modbus_tx_adu(struct modbus_context *ctx);
166 
167 /**
168  * @brief Send ADU and wait certain time for response.
169  *
170  * @param ctx        Modbus interface context
171  *
172  * @retval           0 If the function was successful,
173  *                   -ENOTSUP if Modbus mode is not supported,
174  *                   -ETIMEDOUT on timeout,
175  *                   -EMSGSIZE on length error,
176  *                   -EIO on CRC error.
177  */
178 int modbus_tx_wait_rx_adu(struct modbus_context *ctx);
179 
180 /**
181  * @brief Let server handle the received ADU.
182  *
183  * @param ctx        Modbus interface context
184  *
185  * @retval           True if the server has prepared a response ADU
186  *                   that should be sent.
187  */
188 bool modbus_server_handler(struct modbus_context *ctx);
189 
190 /**
191  * @brief Reset server stats.
192  *
193  * @param ctx        Modbus interface context
194  */
195 void modbus_reset_stats(struct modbus_context *ctx);
196 
197 /**
198  * @brief Disable serial line reception.
199  *
200  * @param ctx        Modbus interface context
201  */
202 void modbus_serial_rx_disable(struct modbus_context *ctx);
203 
204 /**
205  * @brief Enable serial line reception.
206  *
207  * @param ctx        Modbus interface context
208  */
209 void modbus_serial_rx_enable(struct modbus_context *ctx);
210 
211 /**
212  * @brief Assemble ADU from serial line RX buffer
213  *
214  * @param ctx        Modbus interface context
215  *
216  * @retval           0 If the function was successful,
217  *                   -ENOTSUP if serial line mode is not supported,
218  *                   -EMSGSIZE on length error,
219  *                   -EIO on CRC error.
220  */
221 int modbus_serial_rx_adu(struct modbus_context *ctx);
222 
223 /**
224  * @brief Assemble ADU from serial line RX buffer
225  *
226  * @param ctx        Modbus interface context
227  *
228  * @retval           0 If the function was successful,
229  *                   -ENOTSUP if serial line mode is not supported.
230  */
231 int modbus_serial_tx_adu(struct modbus_context *ctx);
232 
233 /**
234  * @brief Initialize serial line support.
235  *
236  * @param ctx        Modbus interface context
237  * @param param      Configuration parameter of the interface
238  *
239  * @retval           0 If the function was successful.
240  */
241 int modbus_serial_init(struct modbus_context *ctx,
242 		       struct modbus_iface_param param);
243 
244 /**
245  * @brief Disable serial line support.
246  *
247  * @param ctx        Modbus interface context
248  */
249 void modbus_serial_disable(struct modbus_context *ctx);
250 
251 int modbus_raw_rx_adu(struct modbus_context *ctx);
252 int modbus_raw_tx_adu(struct modbus_context *ctx);
253 int modbus_raw_init(struct modbus_context *ctx,
254 		    struct modbus_iface_param param);
255 
256 #endif /* ZEPHYR_INCLUDE_MODBUS_INTERNAL_H_ */
257