Line data Source code
1 0 : /*
2 : * Copyright (c) 2022 Trackunit Corporation
3 : *
4 : * SPDX-License-Identifier: Apache-2.0
5 : */
6 :
7 : /*
8 : * This library uses CMUX to create multiple data channels, called DLCIs, on a single serial bus.
9 : * Each DLCI has an address from 1 to 63. DLCI address 0 is reserved for control commands.
10 : *
11 : * Design overview:
12 : *
13 : * DLCI1 <-----------+ +-------> DLCI1
14 : * v v
15 : * DLCI2 <---> CMUX instance <--> Serial bus <--> Client <--> DLCI2
16 : * ^ ^
17 : * DLCI3 <-----------+ +-------> DLCI3
18 : *
19 : * Writing to and from the CMUX instances is done using the modem_pipe API.
20 : */
21 :
22 : #include <zephyr/kernel.h>
23 : #include <zephyr/types.h>
24 : #include <zephyr/sys/ring_buffer.h>
25 : #include <zephyr/sys/atomic.h>
26 :
27 : #include <zephyr/modem/pipe.h>
28 : #include <zephyr/modem/stats.h>
29 :
30 : #ifndef ZEPHYR_MODEM_CMUX_
31 : #define ZEPHYR_MODEM_CMUX_
32 :
33 : #ifdef __cplusplus
34 : extern "C" {
35 : #endif
36 :
37 : /**
38 : * @brief Modem CMUX
39 : * @defgroup modem_cmux Modem CMUX
40 : * @since 3.5
41 : * @version 1.0.0
42 : * @ingroup modem
43 : * @{
44 : */
45 :
46 : struct modem_cmux;
47 :
48 0 : enum modem_cmux_event {
49 : MODEM_CMUX_EVENT_CONNECTED = 0,
50 : MODEM_CMUX_EVENT_DISCONNECTED,
51 : };
52 :
53 0 : typedef void (*modem_cmux_callback)(struct modem_cmux *cmux, enum modem_cmux_event event,
54 : void *user_data);
55 :
56 : /**
57 : * @cond INTERNAL_HIDDEN
58 : */
59 :
60 : #if CONFIG_MODEM_CMUX_MTU > 127
61 : #define MODEM_CMUX_HEADER_SIZE 7
62 : #else
63 : #define MODEM_CMUX_HEADER_SIZE 6
64 : #endif
65 :
66 :
67 : /* Total size of the CMUX work buffers */
68 : #define MODEM_CMUX_WORK_BUFFER_SIZE (CONFIG_MODEM_CMUX_MTU + MODEM_CMUX_HEADER_SIZE + \
69 : CONFIG_MODEM_CMUX_WORK_BUFFER_SIZE_EXTRA)
70 :
71 : enum modem_cmux_state {
72 : MODEM_CMUX_STATE_DISCONNECTED = 0,
73 : MODEM_CMUX_STATE_CONNECTING,
74 : MODEM_CMUX_STATE_CONNECTED,
75 : MODEM_CMUX_STATE_DISCONNECTING,
76 : };
77 :
78 : enum modem_cmux_receive_state {
79 : MODEM_CMUX_RECEIVE_STATE_SOF = 0,
80 : MODEM_CMUX_RECEIVE_STATE_RESYNC,
81 : MODEM_CMUX_RECEIVE_STATE_ADDRESS,
82 : MODEM_CMUX_RECEIVE_STATE_ADDRESS_CONT,
83 : MODEM_CMUX_RECEIVE_STATE_CONTROL,
84 : MODEM_CMUX_RECEIVE_STATE_LENGTH,
85 : MODEM_CMUX_RECEIVE_STATE_LENGTH_CONT,
86 : MODEM_CMUX_RECEIVE_STATE_DATA,
87 : MODEM_CMUX_RECEIVE_STATE_FCS,
88 : MODEM_CMUX_RECEIVE_STATE_EOF,
89 : };
90 :
91 : enum modem_cmux_dlci_state {
92 : MODEM_CMUX_DLCI_STATE_CLOSED,
93 : MODEM_CMUX_DLCI_STATE_OPENING,
94 : MODEM_CMUX_DLCI_STATE_OPEN,
95 : MODEM_CMUX_DLCI_STATE_CLOSING,
96 : };
97 :
98 : struct modem_cmux_dlci {
99 : sys_snode_t node;
100 :
101 : /* Pipe */
102 : struct modem_pipe pipe;
103 :
104 : /* Context */
105 : uint16_t dlci_address;
106 : struct modem_cmux *cmux;
107 :
108 : /* Receive buffer */
109 : struct ring_buf receive_rb;
110 : struct k_mutex receive_rb_lock;
111 :
112 : /* Work */
113 : struct k_work_delayable open_work;
114 : struct k_work_delayable close_work;
115 :
116 : /* State */
117 : enum modem_cmux_dlci_state state;
118 :
119 : /* Statistics */
120 : #if CONFIG_MODEM_STATS
121 : struct modem_stats_buffer receive_buf_stats;
122 : #endif
123 : };
124 :
125 : struct modem_cmux_frame {
126 : uint8_t dlci_address;
127 : bool cr;
128 : bool pf;
129 : uint8_t type;
130 : const uint8_t *data;
131 : uint16_t data_len;
132 : };
133 :
134 : struct modem_cmux_work {
135 : struct k_work_delayable dwork;
136 : struct modem_cmux *cmux;
137 : };
138 :
139 : struct modem_cmux {
140 : /* Bus pipe */
141 : struct modem_pipe *pipe;
142 :
143 : /* Event handler */
144 : modem_cmux_callback callback;
145 : void *user_data;
146 :
147 : /* DLCI channel contexts */
148 : sys_slist_t dlcis;
149 :
150 : /* State */
151 : enum modem_cmux_state state;
152 : bool flow_control_on : 1;
153 : bool initiator : 1;
154 :
155 : /* Work lock */
156 : bool attached : 1;
157 : struct k_spinlock work_lock;
158 :
159 : /* Receive state*/
160 : enum modem_cmux_receive_state receive_state;
161 :
162 : /* Receive buffer */
163 : uint8_t *receive_buf;
164 : uint16_t receive_buf_size;
165 : uint16_t receive_buf_len;
166 :
167 : uint8_t work_buf[MODEM_CMUX_WORK_BUFFER_SIZE];
168 :
169 : /* Transmit buffer */
170 : struct ring_buf transmit_rb;
171 : struct k_mutex transmit_rb_lock;
172 :
173 : /* Received frame */
174 : struct modem_cmux_frame frame;
175 : uint8_t frame_header[5];
176 : uint16_t frame_header_len;
177 :
178 : /* Work */
179 : struct k_work_delayable receive_work;
180 : struct k_work_delayable transmit_work;
181 : struct k_work_delayable connect_work;
182 : struct k_work_delayable disconnect_work;
183 :
184 : /* Synchronize actions */
185 : struct k_event event;
186 :
187 : /* Statistics */
188 : #if CONFIG_MODEM_STATS
189 : struct modem_stats_buffer receive_buf_stats;
190 : struct modem_stats_buffer transmit_buf_stats;
191 : #endif
192 : };
193 :
194 : /**
195 : * @endcond
196 : */
197 :
198 : /**
199 : * @brief Contains CMUX instance configuration data
200 : */
201 1 : struct modem_cmux_config {
202 : /** Invoked when event occurs */
203 1 : modem_cmux_callback callback;
204 : /** Free to use pointer passed to event handler when invoked */
205 1 : void *user_data;
206 : /** Receive buffer */
207 1 : uint8_t *receive_buf;
208 : /** Size of receive buffer in bytes [127, ...] */
209 1 : uint16_t receive_buf_size;
210 : /** Transmit buffer */
211 1 : uint8_t *transmit_buf;
212 : /** Size of transmit buffer in bytes [149, ...] */
213 1 : uint16_t transmit_buf_size;
214 : };
215 :
216 : /**
217 : * @brief Initialize CMUX instance
218 : * @param cmux CMUX instance
219 : * @param config Configuration to apply to CMUX instance
220 : */
221 1 : void modem_cmux_init(struct modem_cmux *cmux, const struct modem_cmux_config *config);
222 :
223 : /**
224 : * @brief CMUX DLCI configuration
225 : */
226 1 : struct modem_cmux_dlci_config {
227 : /** DLCI channel address */
228 1 : uint8_t dlci_address;
229 : /** Receive buffer used by pipe */
230 1 : uint8_t *receive_buf;
231 : /** Size of receive buffer used by pipe [127, ...] */
232 1 : uint16_t receive_buf_size;
233 : };
234 :
235 : /**
236 : * @brief Initialize DLCI instance and register it with CMUX instance
237 : *
238 : * @param cmux CMUX instance which the DLCI will be registered to
239 : * @param dlci DLCI instance which will be registered and configured
240 : * @param config Configuration to apply to DLCI instance
241 : */
242 1 : struct modem_pipe *modem_cmux_dlci_init(struct modem_cmux *cmux, struct modem_cmux_dlci *dlci,
243 : const struct modem_cmux_dlci_config *config);
244 :
245 : /**
246 : * @brief Attach CMUX instance to pipe
247 : *
248 : * @param cmux CMUX instance
249 : * @param pipe Pipe instance to attach CMUX instance to
250 : */
251 1 : int modem_cmux_attach(struct modem_cmux *cmux, struct modem_pipe *pipe);
252 :
253 : /**
254 : * @brief Connect CMUX instance
255 : *
256 : * @details This will send a CMUX connect request to target on the serial bus. If successful,
257 : * DLCI channels can be now be opened using modem_pipe_open()
258 : *
259 : * @param cmux CMUX instance
260 : *
261 : * @note When connected, the bus pipe must not be used directly
262 : */
263 1 : int modem_cmux_connect(struct modem_cmux *cmux);
264 :
265 : /**
266 : * @brief Connect CMUX instance asynchronously
267 : *
268 : * @details This will send a CMUX connect request to target on the serial bus. If successful,
269 : * DLCI channels can be now be opened using modem_pipe_open().
270 : *
271 : * @param cmux CMUX instance
272 : *
273 : * @note When connected, the bus pipe must not be used directly
274 : */
275 1 : int modem_cmux_connect_async(struct modem_cmux *cmux);
276 :
277 : /**
278 : * @brief Close down and disconnect CMUX instance
279 : *
280 : * @details This will close all open DLCI channels, and close down the CMUX connection.
281 : *
282 : * @param cmux CMUX instance
283 : *
284 : * @note The bus pipe must be released using modem_cmux_release() after disconnecting
285 : * before being reused.
286 : */
287 1 : int modem_cmux_disconnect(struct modem_cmux *cmux);
288 :
289 : /**
290 : * @brief Close down and disconnect CMUX instance asynchronously
291 : *
292 : * @details This will close all open DLCI channels, and close down the CMUX connection.
293 : *
294 : * @param cmux CMUX instance
295 : *
296 : * @note The bus pipe must be released using modem_cmux_release() after disconnecting
297 : * before being reused.
298 : */
299 1 : int modem_cmux_disconnect_async(struct modem_cmux *cmux);
300 :
301 : /**
302 : * @brief Release CMUX instance from pipe
303 : *
304 : * @details Releases the pipe and hard resets the CMUX instance internally. CMUX should
305 : * be disconnected using modem_cmux_disconnect().
306 : *
307 : * @param cmux CMUX instance
308 : *
309 : * @note The bus pipe can be used directly again after CMUX instance is released.
310 : */
311 1 : void modem_cmux_release(struct modem_cmux *cmux);
312 :
313 : /**
314 : * @}
315 : */
316 :
317 : #ifdef __cplusplus
318 : }
319 : #endif
320 :
321 : #endif /* ZEPHYR_MODEM_CMUX_ */
|