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