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