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 
92 struct modbus_context {
93 	/* Interface name */
94 	const char *iface_name;
95 	union {
96 		/* Serial line configuration */
97 		struct modbus_serial_config *cfg;
98 		/* RAW TX callback */
99 		struct modbus_raw_cb rawcb;
100 	};
101 	/* MODBUS mode */
102 	enum modbus_mode mode;
103 	/* True if interface is configured as client */
104 	bool client;
105 	/* Amount of time client is willing to wait for response from server */
106 	uint32_t rxwait_to;
107 	/* Pointer to user server callbacks */
108 	struct modbus_user_callbacks *mbs_user_cb;
109 	/* Interface state */
110 	atomic_t state;
111 
112 	/* Client's mutually exclusive access */
113 	struct k_mutex iface_lock;
114 	/* Wait for response semaphore */
115 	struct k_sem client_wait_sem;
116 	/* Server work item */
117 	struct k_work server_work;
118 	/* Received frame */
119 	struct modbus_adu rx_adu;
120 	/* Frame to transmit */
121 	struct modbus_adu tx_adu;
122 
123 	/* Records error from frame reception, e.g. CRC error */
124 	int rx_adu_err;
125 
126 #ifdef CONFIG_MODBUS_FC08_DIAGNOSTIC
127 	uint16_t mbs_msg_ctr;
128 	uint16_t mbs_crc_err_ctr;
129 	uint16_t mbs_except_ctr;
130 	uint16_t mbs_server_msg_ctr;
131 	uint16_t mbs_noresp_ctr;
132 #endif
133 	/* A linked list of function code, handler pairs */
134 	sys_slist_t user_defined_cbs;
135 	/* Unit ID */
136 	uint8_t unit_id;
137 
138 };
139 
140 /**
141  * @brief Get Modbus interface context.
142  *
143  * @param ctx        Modbus interface context
144  *
145  * @retval           Pointer to interface context or NULL
146  *                   if interface not available or not configured;
147  */
148 struct modbus_context *modbus_get_context(const uint8_t iface);
149 
150 /**
151  * @brief Get Modbus interface index.
152  *
153  * @param ctx        Pointer to Modbus interface context
154  *
155  * @retval           Interface index or negative error value.
156  */
157 int modbus_iface_get_by_ctx(const struct modbus_context *ctx);
158 
159 /**
160  * @brief Send ADU.
161  *
162  * @param ctx        Modbus interface context
163  */
164 void modbus_tx_adu(struct modbus_context *ctx);
165 
166 /**
167  * @brief Send ADU and wait certain time for response.
168  *
169  * @param ctx        Modbus interface context
170  *
171  * @retval           0 If the function was successful,
172  *                   -ENOTSUP if Modbus mode is not supported,
173  *                   -ETIMEDOUT on timeout,
174  *                   -EMSGSIZE on length error,
175  *                   -EIO on CRC error.
176  */
177 int modbus_tx_wait_rx_adu(struct modbus_context *ctx);
178 
179 /**
180  * @brief Let server handle the received ADU.
181  *
182  * @param ctx        Modbus interface context
183  *
184  * @retval           True if the server has prepared a response ADU
185  *                   that should be sent.
186  */
187 bool modbus_server_handler(struct modbus_context *ctx);
188 
189 /**
190  * @brief Reset server stats.
191  *
192  * @param ctx        Modbus interface context
193  */
194 void modbus_reset_stats(struct modbus_context *ctx);
195 
196 /**
197  * @brief Disable serial line reception.
198  *
199  * @param ctx        Modbus interface context
200  */
201 void modbus_serial_rx_disable(struct modbus_context *ctx);
202 
203 /**
204  * @brief Enable serial line reception.
205  *
206  * @param ctx        Modbus interface context
207  */
208 void modbus_serial_rx_enable(struct modbus_context *ctx);
209 
210 /**
211  * @brief Assemble ADU from serial line RX buffer
212  *
213  * @param ctx        Modbus interface context
214  *
215  * @retval           0 If the function was successful,
216  *                   -ENOTSUP if serial line mode is not supported,
217  *                   -EMSGSIZE on length error,
218  *                   -EIO on CRC error.
219  */
220 int modbus_serial_rx_adu(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  */
230 int modbus_serial_tx_adu(struct modbus_context *ctx);
231 
232 /**
233  * @brief Initialize serial line support.
234  *
235  * @param ctx        Modbus interface context
236  * @param param      Configuration parameter of the interface
237  *
238  * @retval           0 If the function was successful.
239  */
240 int modbus_serial_init(struct modbus_context *ctx,
241 		       struct modbus_iface_param param);
242 
243 /**
244  * @brief Disable serial line support.
245  *
246  * @param ctx        Modbus interface context
247  */
248 void modbus_serial_disable(struct modbus_context *ctx);
249 
250 int modbus_raw_rx_adu(struct modbus_context *ctx);
251 int modbus_raw_tx_adu(struct modbus_context *ctx);
252 int modbus_raw_init(struct modbus_context *ctx,
253 		    struct modbus_iface_param param);
254 
255 #endif /* ZEPHYR_INCLUDE_MODBUS_INTERNAL_H_ */
256