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