1 /** @file
2 * @brief Modem command handler header file.
3 *
4 * Text-based command handler implementation for modem context driver.
5 */
6
7 /*
8 * Copyright (c) 2019 Foundries.io
9 *
10 * SPDX-License-Identifier: Apache-2.0
11 */
12
13 #ifndef ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_CMD_HANDLER_H_
14 #define ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_CMD_HANDLER_H_
15
16 #include <zephyr/kernel.h>
17
18 #include "modem_context.h"
19
20 #ifdef __cplusplus
21 extern "C" {
22 #endif
23
24 #define MODEM_CMD_DEFINE(name_) \
25 static int name_(struct modem_cmd_handler_data *data, uint16_t len, \
26 uint8_t **argv, uint16_t argc)
27
28 #define MODEM_CMD(cmd_, func_cb_, acount_, adelim_) { \
29 .cmd = cmd_, \
30 .cmd_len = (uint16_t)sizeof(cmd_)-1, \
31 .func = func_cb_, \
32 .arg_count_min = acount_, \
33 .arg_count_max = acount_, \
34 .delim = adelim_, \
35 .direct = false, \
36 }
37
38 #define MODEM_CMD_ARGS_MAX(cmd_, func_cb_, acount_, acountmax_, adelim_) { \
39 .cmd = cmd_, \
40 .cmd_len = (uint16_t)sizeof(cmd_)-1, \
41 .func = func_cb_, \
42 .arg_count_min = acount_, \
43 .arg_count_max = acountmax_, \
44 .delim = adelim_, \
45 .direct = false, \
46 }
47
48 #define MODEM_CMD_DIRECT_DEFINE(name_) MODEM_CMD_DEFINE(name_)
49
50 #define MODEM_CMD_DIRECT(cmd_, func_cb_) { \
51 .cmd = cmd_, \
52 .cmd_len = (uint16_t)sizeof(cmd_)-1, \
53 .func = func_cb_, \
54 .arg_count_min = 0, \
55 .arg_count_max = 0, \
56 .delim = "", \
57 .direct = true, \
58 }
59
60 #define CMD_RESP 0
61 #define CMD_UNSOL 1
62 #define CMD_HANDLER 2
63 #define CMD_MAX 3
64
65 /*
66 * Flags for modem_send_cmd_ext.
67 */
68 #define MODEM_NO_TX_LOCK BIT(0)
69 #define MODEM_NO_SET_CMDS BIT(1)
70 #define MODEM_NO_UNSET_CMDS BIT(2)
71
72 struct modem_cmd_handler_data;
73
74 struct modem_cmd {
75 int (*func)(struct modem_cmd_handler_data *data, uint16_t len,
76 uint8_t **argv, uint16_t argc);
77 const char *cmd;
78 const char *delim;
79 uint16_t cmd_len;
80 uint16_t arg_count_min;
81 uint16_t arg_count_max;
82 bool direct;
83 };
84
85 #define SETUP_CMD(cmd_send_, match_cmd_, func_cb_, num_param_, delim_) { \
86 .send_cmd = cmd_send_, \
87 MODEM_CMD(match_cmd_, func_cb_, num_param_, delim_) \
88 }
89
90 #define SETUP_CMD_NOHANDLE(send_cmd_) \
91 SETUP_CMD(send_cmd_, NULL, NULL, 0U, NULL)
92
93 /* series of modem setup commands to run */
94 struct setup_cmd {
95 const char *send_cmd;
96 struct modem_cmd handle_cmd;
97 };
98
99 struct modem_cmd_handler_data {
100 const struct modem_cmd *cmds[CMD_MAX];
101 size_t cmds_len[CMD_MAX];
102
103 char *match_buf;
104 size_t match_buf_len;
105
106 int last_error;
107
108 const char *eol;
109 size_t eol_len;
110
111 /* rx net buffer */
112 struct net_buf *rx_buf;
113
114 /* allocation info */
115 struct net_buf_pool *buf_pool;
116 k_timeout_t alloc_timeout;
117
118 /* locks */
119 struct k_sem sem_tx_lock;
120 struct k_sem sem_parse_lock;
121
122 /* user data */
123 void *user_data;
124 };
125
126 /**
127 * @brief get the last error code
128 *
129 * @param data: command handler data reference
130 *
131 * @retval last handled error.
132 */
133 int modem_cmd_handler_get_error(struct modem_cmd_handler_data *data);
134
135 /**
136 * @brief set the last error code
137 *
138 * @param data: command handler data reference
139 * @param error_code: error
140 *
141 * @retval 0 if ok, < 0 if error.
142 */
143 int modem_cmd_handler_set_error(struct modem_cmd_handler_data *data,
144 int error_code);
145
146 /**
147 * @brief update the parser's handler commands
148 *
149 * @param data: handler data to use
150 * @param handler_cmds: commands to attach
151 * @param handler_cmds_len: size of commands array
152 * @param reset_error_flag: reset last error code
153 *
154 * @retval 0 if ok, < 0 if error.
155 */
156 int modem_cmd_handler_update_cmds(struct modem_cmd_handler_data *data,
157 const struct modem_cmd *handler_cmds,
158 size_t handler_cmds_len,
159 bool reset_error_flag);
160
161 /**
162 * @brief Wait until semaphore is given
163 *
164 * This function does the same wait behavior as @ref modem_cmd_send, but can wait without sending
165 * any command first. Useful for waiting for asynchronous responses.
166 *
167 * @param data: handler data to use
168 * @param sem: wait for semaphore.
169 * @param timeout: wait timeout.
170 *
171 * @retval 0 if ok, < 0 if error.
172 */
173 int modem_cmd_handler_await(struct modem_cmd_handler_data *data,
174 struct k_sem *sem, k_timeout_t timeout);
175
176 /**
177 * @brief send data directly to interface w/o TX lock.
178 *
179 * This function just writes directly to the modem interface.
180 * Recommended to use to get verbose logging for all data sent to the interface.
181 * @param iface: interface to use
182 * @param buf: send buffer (not NULL terminated)
183 * @param len: length of send buffer.
184 */
185 int modem_cmd_send_data_nolock(struct modem_iface *iface,
186 const uint8_t *buf, size_t len);
187
188 /**
189 * @brief send AT command to interface with behavior defined by flags
190 *
191 * This function is similar to @ref modem_cmd_send, but it allows to choose a
192 * specific behavior regarding acquiring tx_lock, setting and unsetting
193 * @a handler_cmds.
194 *
195 * @param iface: interface to use
196 * @param handler: command handler to use
197 * @param handler_cmds: commands to attach
198 * @param handler_cmds_len: size of commands array
199 * @param buf: NULL terminated send buffer
200 * @param sem: wait for response semaphore
201 * @param timeout: timeout of command
202 * @param flags: flags which influence behavior of command sending
203 *
204 * @retval 0 if ok, < 0 if error.
205 */
206 int modem_cmd_send_ext(struct modem_iface *iface,
207 struct modem_cmd_handler *handler,
208 const struct modem_cmd *handler_cmds,
209 size_t handler_cmds_len, const uint8_t *buf,
210 struct k_sem *sem, k_timeout_t timeout, int flags);
211
212 /**
213 * @brief send AT command to interface w/o locking TX
214 *
215 * @param iface: interface to use
216 * @param handler: command handler to use
217 * @param handler_cmds: commands to attach
218 * @param handler_cmds_len: size of commands array
219 * @param buf: NULL terminated send buffer
220 * @param sem: wait for response semaphore
221 * @param timeout: timeout of command
222 *
223 * @retval 0 if ok, < 0 if error.
224 */
modem_cmd_send_nolock(struct modem_iface * iface,struct modem_cmd_handler * handler,const struct modem_cmd * handler_cmds,size_t handler_cmds_len,const uint8_t * buf,struct k_sem * sem,k_timeout_t timeout)225 static inline int modem_cmd_send_nolock(struct modem_iface *iface,
226 struct modem_cmd_handler *handler,
227 const struct modem_cmd *handler_cmds,
228 size_t handler_cmds_len,
229 const uint8_t *buf, struct k_sem *sem,
230 k_timeout_t timeout)
231 {
232 return modem_cmd_send_ext(iface, handler, handler_cmds,
233 handler_cmds_len, buf, sem, timeout,
234 MODEM_NO_TX_LOCK);
235 }
236
237 /**
238 * @brief send AT command to interface w/ a TX lock
239 *
240 * @param iface: interface to use
241 * @param handler: command handler to use
242 * @param handler_cmds: commands to attach
243 * @param handler_cmds_len: size of commands array
244 * @param buf: NULL terminated send buffer
245 * @param sem: wait for response semaphore
246 * @param timeout: timeout of command
247 *
248 * @retval 0 if ok, < 0 if error.
249 */
modem_cmd_send(struct modem_iface * iface,struct modem_cmd_handler * handler,const struct modem_cmd * handler_cmds,size_t handler_cmds_len,const uint8_t * buf,struct k_sem * sem,k_timeout_t timeout)250 static inline int modem_cmd_send(struct modem_iface *iface,
251 struct modem_cmd_handler *handler,
252 const struct modem_cmd *handler_cmds,
253 size_t handler_cmds_len, const uint8_t *buf,
254 struct k_sem *sem, k_timeout_t timeout)
255 {
256 return modem_cmd_send_ext(iface, handler, handler_cmds,
257 handler_cmds_len, buf, sem, timeout, 0);
258 }
259
260 /**
261 * @brief send a series of AT commands w/ a TX lock
262 *
263 * @param iface: interface to use
264 * @param handler: command handler to use
265 * @param cmds: array of setup commands to send
266 * @param cmds_len: size of the setup command array
267 * @param sem: wait for response semaphore
268 * @param timeout: timeout of command
269 *
270 * @retval 0 if ok, < 0 if error.
271 */
272 int modem_cmd_handler_setup_cmds(struct modem_iface *iface,
273 struct modem_cmd_handler *handler,
274 const struct setup_cmd *cmds, size_t cmds_len,
275 struct k_sem *sem, k_timeout_t timeout);
276
277 /**
278 * @brief send a series of AT commands w/o locking TX
279 *
280 * @param iface: interface to use
281 * @param handler: command handler to use
282 * @param cmds: array of setup commands to send
283 * @param cmds_len: size of the setup command array
284 * @param sem: wait for response semaphore
285 * @param timeout: timeout of command
286 *
287 * @retval 0 if ok, < 0 if error.
288 */
289 int modem_cmd_handler_setup_cmds_nolock(struct modem_iface *iface,
290 struct modem_cmd_handler *handler,
291 const struct setup_cmd *cmds,
292 size_t cmds_len, struct k_sem *sem,
293 k_timeout_t timeout);
294
295 /**
296 * @brief Modem command handler configuration
297 *
298 * @details Contains user configuration which is used to set up
299 * command handler data context. The struct is initialized and then passed
300 * to modem_cmd_handler_init().
301 *
302 * @retval 0 if ok, < 0 if error.
303 * @param match_buf Buffer used for matching commands
304 * @param match_buf_len Length of buffer used for matching commands
305 * @param buf_pool Initialized buffer pool used to store incoming data
306 * @param alloc_timeout Timeout for allocating data in buffer pool
307 * @param eol End of line represented as string
308 * @param user_data Free to use data which can be retrieved from within command handlers
309 * @param response_cmds Array of response command handlers
310 * @param response_cmds_len Length of response command handlers array
311 * @param unsol_cmds Array of unsolicitet command handlers
312 * @param unsol_cmds_len Length of unsolicitet command handlers array
313 */
314 struct modem_cmd_handler_config {
315 char *match_buf;
316 size_t match_buf_len;
317 struct net_buf_pool *buf_pool;
318 k_timeout_t alloc_timeout;
319 const char *eol;
320 void *user_data;
321 const struct modem_cmd *response_cmds;
322 size_t response_cmds_len;
323 const struct modem_cmd *unsol_cmds;
324 size_t unsol_cmds_len;
325 };
326
327 /**
328 * @brief Initialize modem command handler
329 *
330 * @details This function is called once for each command handler, before any
331 * incoming data is processed.
332 *
333 * @note All arguments passed to this function, including the referenced data
334 * contained in the setup struct, must persist as long as the command handler itself.
335 *
336 * @param handler Command handler to initialize
337 * @param data Command handler data to use
338 * @param setup Command handler setup
339 *
340 * @return -EINVAL if any argument is invalid
341 * @return 0 if successful
342 */
343 int modem_cmd_handler_init(struct modem_cmd_handler *handler,
344 struct modem_cmd_handler_data *data,
345 const struct modem_cmd_handler_config *config);
346
347 /**
348 * @brief Lock the modem for sending cmds
349 *
350 * This is semaphore-based rather than mutex based, which means there's no
351 * requirements of thread ownership for the user. This function is useful
352 * when one needs to prevent threads from sending UART data to the modem for an
353 * extended period of time (for example during modem reset).
354 *
355 * @param handler: command handler to lock
356 * @param timeout: give up after timeout
357 *
358 * @retval 0 if ok, < 0 if error.
359 */
360 int modem_cmd_handler_tx_lock(struct modem_cmd_handler *handler,
361 k_timeout_t timeout);
362
363 /**
364 * @brief Unlock the modem for sending cmds
365 *
366 * @param handler: command handler to unlock
367 */
368 void modem_cmd_handler_tx_unlock(struct modem_cmd_handler *handler);
369
370 /**
371 * @brief Process incoming data
372 *
373 * @details This function will process any data available from the interface
374 * using the command handler. The command handler will invoke any matching modem
375 * command which has been registered using @ref modem_cmd_handler_init_cmds or
376 * @ref modem_cmd_handler_update_cmds. Once handled, the function will return.
377 *
378 * @note This function should be invoked from a dedicated thread, which only handles
379 * commands.
380 *
381 * @param handler The handler wich will handle the command when processed
382 * @param iface The interface which receives incoming data
383 */
modem_cmd_handler_process(struct modem_cmd_handler * handler,struct modem_iface * iface)384 static inline void modem_cmd_handler_process(struct modem_cmd_handler *handler,
385 struct modem_iface *iface)
386 {
387 handler->process(handler, iface);
388 }
389
390 #ifdef __cplusplus
391 }
392 #endif
393
394 #endif /* ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_CMD_HANDLER_H_ */
395