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 send AT command to interface with behavior defined by flags
163 *
164 * This function is similar to @ref modem_cmd_send, but it allows to choose a
165 * specific behavior regarding acquiring tx_lock, setting and unsetting
166 * @a handler_cmds.
167 *
168 * @param iface: interface to use
169 * @param handler: command handler to use
170 * @param handler_cmds: commands to attach
171 * @param handler_cmds_len: size of commands array
172 * @param buf: NULL terminated send buffer
173 * @param sem: wait for response semaphore
174 * @param timeout: timeout of command
175 * @param flags: flags which influence behavior of command sending
176 *
177 * @retval 0 if ok, < 0 if error.
178 */
179 int modem_cmd_send_ext(struct modem_iface *iface,
180 struct modem_cmd_handler *handler,
181 const struct modem_cmd *handler_cmds,
182 size_t handler_cmds_len, const uint8_t *buf,
183 struct k_sem *sem, k_timeout_t timeout, int flags);
184
185 /**
186 * @brief send AT command to interface w/o locking TX
187 *
188 * @param iface: interface to use
189 * @param handler: command handler to use
190 * @param handler_cmds: commands to attach
191 * @param handler_cmds_len: size of commands array
192 * @param buf: NULL terminated send buffer
193 * @param sem: wait for response semaphore
194 * @param timeout: timeout of command
195 *
196 * @retval 0 if ok, < 0 if error.
197 */
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)198 static inline int modem_cmd_send_nolock(struct modem_iface *iface,
199 struct modem_cmd_handler *handler,
200 const struct modem_cmd *handler_cmds,
201 size_t handler_cmds_len,
202 const uint8_t *buf, struct k_sem *sem,
203 k_timeout_t timeout)
204 {
205 return modem_cmd_send_ext(iface, handler, handler_cmds,
206 handler_cmds_len, buf, sem, timeout,
207 MODEM_NO_TX_LOCK);
208 }
209
210 /**
211 * @brief send AT command to interface w/ a TX lock
212 *
213 * @param iface: interface to use
214 * @param handler: command handler to use
215 * @param handler_cmds: commands to attach
216 * @param handler_cmds_len: size of commands array
217 * @param buf: NULL terminated send buffer
218 * @param sem: wait for response semaphore
219 * @param timeout: timeout of command
220 *
221 * @retval 0 if ok, < 0 if error.
222 */
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)223 static inline int modem_cmd_send(struct modem_iface *iface,
224 struct modem_cmd_handler *handler,
225 const struct modem_cmd *handler_cmds,
226 size_t handler_cmds_len, const uint8_t *buf,
227 struct k_sem *sem, k_timeout_t timeout)
228 {
229 return modem_cmd_send_ext(iface, handler, handler_cmds,
230 handler_cmds_len, buf, sem, timeout, 0);
231 }
232
233 /**
234 * @brief send a series of AT commands w/ a TX lock
235 *
236 * @param iface: interface to use
237 * @param handler: command handler to use
238 * @param cmds: array of setup commands to send
239 * @param cmds_len: size of the setup command array
240 * @param sem: wait for response semaphore
241 * @param timeout: timeout of command
242 *
243 * @retval 0 if ok, < 0 if error.
244 */
245 int modem_cmd_handler_setup_cmds(struct modem_iface *iface,
246 struct modem_cmd_handler *handler,
247 const struct setup_cmd *cmds, size_t cmds_len,
248 struct k_sem *sem, k_timeout_t timeout);
249
250 /**
251 * @brief send a series of AT commands w/o locking TX
252 *
253 * @param iface: interface to use
254 * @param handler: command handler to use
255 * @param cmds: array of setup commands to send
256 * @param cmds_len: size of the setup command array
257 * @param sem: wait for response semaphore
258 * @param timeout: timeout of command
259 *
260 * @retval 0 if ok, < 0 if error.
261 */
262 int modem_cmd_handler_setup_cmds_nolock(struct modem_iface *iface,
263 struct modem_cmd_handler *handler,
264 const struct setup_cmd *cmds,
265 size_t cmds_len, struct k_sem *sem,
266 k_timeout_t timeout);
267
268 /**
269 * @brief Modem command handler configuration
270 *
271 * @details Contains user configuration which is used to set up
272 * command handler data context. The struct is initialized and then passed
273 * to modem_cmd_handler_init().
274 *
275 * @retval 0 if ok, < 0 if error.
276 * @param match_buf Buffer used for matching commands
277 * @param match_buf_len Length of buffer used for matching commands
278 * @param buf_pool Initialized buffer pool used to store incoming data
279 * @param alloc_timeout Timeout for allocating data in buffer pool
280 * @param eol End of line represented as string
281 * @param user_data Free to use data which can be retrieved from within command handlers
282 * @param response_cmds Array of response command handlers
283 * @param response_cmds_len Length of response command handlers array
284 * @param unsol_cmds Array of unsolicitet command handlers
285 * @param unsol_cmds_len Length of unsolicitet command handlers array
286 */
287 struct modem_cmd_handler_config {
288 char *match_buf;
289 size_t match_buf_len;
290 struct net_buf_pool *buf_pool;
291 k_timeout_t alloc_timeout;
292 const char *eol;
293 void *user_data;
294 const struct modem_cmd *response_cmds;
295 size_t response_cmds_len;
296 const struct modem_cmd *unsol_cmds;
297 size_t unsol_cmds_len;
298 };
299
300 /**
301 * @brief Initialize modem command handler
302 *
303 * @details This function is called once for each command handler, before any
304 * incoming data is processed.
305 *
306 * @note All arguments passed to this function, including the referenced data
307 * contained in the setup struct, must persist as long as the command handler itself.
308 *
309 * @param handler Command handler to initialize
310 * @param data Command handler data to use
311 * @param setup Command handler setup
312 *
313 * @return -EINVAL if any argument is invalid
314 * @return 0 if successful
315 */
316 int modem_cmd_handler_init(struct modem_cmd_handler *handler,
317 struct modem_cmd_handler_data *data,
318 const struct modem_cmd_handler_config *config);
319
320 /**
321 * @brief Lock the modem for sending cmds
322 *
323 * This is semaphore-based rather than mutex based, which means there's no
324 * requirements of thread ownership for the user. This function is useful
325 * when one needs to prevent threads from sending UART data to the modem for an
326 * extended period of time (for example during modem reset).
327 *
328 * @param handler: command handler to lock
329 * @param timeout: give up after timeout
330 *
331 * @retval 0 if ok, < 0 if error.
332 */
333 int modem_cmd_handler_tx_lock(struct modem_cmd_handler *handler,
334 k_timeout_t timeout);
335
336 /**
337 * @brief Unlock the modem for sending cmds
338 *
339 * @param handler: command handler to unlock
340 */
341 void modem_cmd_handler_tx_unlock(struct modem_cmd_handler *handler);
342
343 /**
344 * @brief Process incoming data
345 *
346 * @details This function will process any data available from the interface
347 * using the command handler. The command handler will invoke any matching modem
348 * command which has been registered using @ref modem_cmd_handler_init_cmds or
349 * @ref modem_cmd_handler_update_cmds. Once handled, the function will return.
350 *
351 * @note This function should be invoked from a dedicated thread, which only handles
352 * commands.
353 *
354 * @param handler The handler wich will handle the command when processed
355 * @param iface The interface which receives incoming data
356 */
modem_cmd_handler_process(struct modem_cmd_handler * handler,struct modem_iface * iface)357 static inline void modem_cmd_handler_process(struct modem_cmd_handler *handler,
358 struct modem_iface *iface)
359 {
360 handler->process(handler, iface);
361 }
362
363 #ifdef __cplusplus
364 }
365 #endif
366
367 #endif /* ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_CMD_HANDLER_H_ */
368