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