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 : enum modem_cmux_state {
61 : MODEM_CMUX_STATE_DISCONNECTED = 0,
62 : MODEM_CMUX_STATE_CONNECTING,
63 : MODEM_CMUX_STATE_CONNECTED,
64 : MODEM_CMUX_STATE_DISCONNECTING,
65 : };
66 :
67 : enum modem_cmux_receive_state {
68 : MODEM_CMUX_RECEIVE_STATE_SOF = 0,
69 : MODEM_CMUX_RECEIVE_STATE_RESYNC,
70 : MODEM_CMUX_RECEIVE_STATE_ADDRESS,
71 : MODEM_CMUX_RECEIVE_STATE_ADDRESS_CONT,
72 : MODEM_CMUX_RECEIVE_STATE_CONTROL,
73 : MODEM_CMUX_RECEIVE_STATE_LENGTH,
74 : MODEM_CMUX_RECEIVE_STATE_LENGTH_CONT,
75 : MODEM_CMUX_RECEIVE_STATE_DATA,
76 : MODEM_CMUX_RECEIVE_STATE_FCS,
77 : MODEM_CMUX_RECEIVE_STATE_DROP,
78 : MODEM_CMUX_RECEIVE_STATE_EOF,
79 : };
80 :
81 : enum modem_cmux_dlci_state {
82 : MODEM_CMUX_DLCI_STATE_CLOSED,
83 : MODEM_CMUX_DLCI_STATE_OPENING,
84 : MODEM_CMUX_DLCI_STATE_OPEN,
85 : MODEM_CMUX_DLCI_STATE_CLOSING,
86 : };
87 :
88 : struct modem_cmux_dlci {
89 : sys_snode_t node;
90 :
91 : /* Pipe */
92 : struct modem_pipe pipe;
93 :
94 : /* Context */
95 : uint16_t dlci_address;
96 : struct modem_cmux *cmux;
97 :
98 : /* Receive buffer */
99 : struct ring_buf receive_rb;
100 : struct k_mutex receive_rb_lock;
101 :
102 : /* Work */
103 : struct k_work_delayable open_work;
104 : struct k_work_delayable close_work;
105 :
106 : /* State */
107 : enum modem_cmux_dlci_state state;
108 :
109 : /* Statistics */
110 : #if CONFIG_MODEM_STATS
111 : struct modem_stats_buffer receive_buf_stats;
112 : #endif
113 : };
114 :
115 : struct modem_cmux_frame {
116 : uint8_t dlci_address;
117 : bool cr;
118 : bool pf;
119 : uint8_t type;
120 : const uint8_t *data;
121 : uint16_t data_len;
122 : };
123 :
124 : struct modem_cmux_work {
125 : struct k_work_delayable dwork;
126 : struct modem_cmux *cmux;
127 : };
128 :
129 : struct modem_cmux {
130 : /* Bus pipe */
131 : struct modem_pipe *pipe;
132 :
133 : /* Event handler */
134 : modem_cmux_callback callback;
135 : void *user_data;
136 :
137 : /* DLCI channel contexts */
138 : sys_slist_t dlcis;
139 :
140 : /* State */
141 : enum modem_cmux_state state;
142 : bool flow_control_on;
143 :
144 : /* Work lock */
145 : bool attached;
146 : struct k_spinlock work_lock;
147 :
148 : /* Receive state*/
149 : enum modem_cmux_receive_state receive_state;
150 :
151 : /* Receive buffer */
152 : uint8_t *receive_buf;
153 : uint16_t receive_buf_size;
154 : uint16_t receive_buf_len;
155 :
156 : uint8_t work_buf[CONFIG_MODEM_CMUX_WORK_BUFFER_SIZE];
157 :
158 : /* Transmit buffer */
159 : struct ring_buf transmit_rb;
160 : struct k_mutex transmit_rb_lock;
161 :
162 : /* Received frame */
163 : struct modem_cmux_frame frame;
164 : uint8_t frame_header[5];
165 : uint16_t frame_header_len;
166 :
167 : /* Work */
168 : struct k_work_delayable receive_work;
169 : struct k_work_delayable transmit_work;
170 : struct k_work_delayable connect_work;
171 : struct k_work_delayable disconnect_work;
172 :
173 : /* Synchronize actions */
174 : struct k_event event;
175 :
176 : /* Statistics */
177 : #if CONFIG_MODEM_STATS
178 : struct modem_stats_buffer receive_buf_stats;
179 : struct modem_stats_buffer transmit_buf_stats;
180 : #endif
181 : };
182 :
183 : /**
184 : * @endcond
185 : */
186 :
187 : /**
188 : * @brief Contains CMUX instance configuration data
189 : */
190 1 : struct modem_cmux_config {
191 : /** Invoked when event occurs */
192 1 : modem_cmux_callback callback;
193 : /** Free to use pointer passed to event handler when invoked */
194 1 : void *user_data;
195 : /** Receive buffer */
196 1 : uint8_t *receive_buf;
197 : /** Size of receive buffer in bytes [127, ...] */
198 1 : uint16_t receive_buf_size;
199 : /** Transmit buffer */
200 1 : uint8_t *transmit_buf;
201 : /** Size of transmit buffer in bytes [149, ...] */
202 1 : uint16_t transmit_buf_size;
203 : };
204 :
205 : /**
206 : * @brief Initialize CMUX instance
207 : * @param cmux CMUX instance
208 : * @param config Configuration to apply to CMUX instance
209 : */
210 1 : void modem_cmux_init(struct modem_cmux *cmux, const struct modem_cmux_config *config);
211 :
212 : /**
213 : * @brief CMUX DLCI configuration
214 : */
215 1 : struct modem_cmux_dlci_config {
216 : /** DLCI channel address */
217 1 : uint8_t dlci_address;
218 : /** Receive buffer used by pipe */
219 1 : uint8_t *receive_buf;
220 : /** Size of receive buffer used by pipe [127, ...] */
221 1 : uint16_t receive_buf_size;
222 : };
223 :
224 : /**
225 : * @brief Initialize DLCI instance and register it with CMUX instance
226 : *
227 : * @param cmux CMUX instance which the DLCI will be registered to
228 : * @param dlci DLCI instance which will be registered and configured
229 : * @param config Configuration to apply to DLCI instance
230 : */
231 1 : struct modem_pipe *modem_cmux_dlci_init(struct modem_cmux *cmux, struct modem_cmux_dlci *dlci,
232 : const struct modem_cmux_dlci_config *config);
233 :
234 : /**
235 : * @brief Attach CMUX instance to pipe
236 : *
237 : * @param cmux CMUX instance
238 : * @param pipe Pipe instance to attach CMUX instance to
239 : */
240 1 : int modem_cmux_attach(struct modem_cmux *cmux, struct modem_pipe *pipe);
241 :
242 : /**
243 : * @brief Connect CMUX instance
244 : *
245 : * @details This will send a CMUX connect request to target on the serial bus. If successful,
246 : * DLCI channels can be now be opened using modem_pipe_open()
247 : *
248 : * @param cmux CMUX instance
249 : *
250 : * @note When connected, the bus pipe must not be used directly
251 : */
252 1 : int modem_cmux_connect(struct modem_cmux *cmux);
253 :
254 : /**
255 : * @brief Connect CMUX instance asynchronously
256 : *
257 : * @details This will send a CMUX connect request to target on the serial bus. If successful,
258 : * DLCI channels can be now be opened using modem_pipe_open().
259 : *
260 : * @param cmux CMUX instance
261 : *
262 : * @note When connected, the bus pipe must not be used directly
263 : */
264 1 : int modem_cmux_connect_async(struct modem_cmux *cmux);
265 :
266 : /**
267 : * @brief Close down and disconnect CMUX instance
268 : *
269 : * @details This will close all open DLCI channels, and close down the CMUX connection.
270 : *
271 : * @param cmux CMUX instance
272 : *
273 : * @note The bus pipe must be released using modem_cmux_release() after disconnecting
274 : * before being reused.
275 : */
276 1 : int modem_cmux_disconnect(struct modem_cmux *cmux);
277 :
278 : /**
279 : * @brief Close down and disconnect CMUX instance asynchronously
280 : *
281 : * @details This will close all open DLCI channels, and close down the CMUX connection.
282 : *
283 : * @param cmux CMUX instance
284 : *
285 : * @note The bus pipe must be released using modem_cmux_release() after disconnecting
286 : * before being reused.
287 : */
288 1 : int modem_cmux_disconnect_async(struct modem_cmux *cmux);
289 :
290 : /**
291 : * @brief Release CMUX instance from pipe
292 : *
293 : * @details Releases the pipe and hard resets the CMUX instance internally. CMUX should
294 : * be disconnected using modem_cmux_disconnect().
295 : *
296 : * @param cmux CMUX instance
297 : *
298 : * @note The bus pipe can be used directly again after CMUX instance is released.
299 : */
300 1 : void modem_cmux_release(struct modem_cmux *cmux);
301 :
302 : /**
303 : * @}
304 : */
305 :
306 : #ifdef __cplusplus
307 : }
308 : #endif
309 :
310 : #endif /* ZEPHYR_MODEM_CMUX_ */
|