| /** @file |
| * @brief Modem command handler header file. |
| * |
| * Text-based command handler implementation for modem context driver. |
| */ |
| |
| /* |
| * Copyright (c) 2019 Foundries.io |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #ifndef ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_CMD_HANDLER_H_ |
| #define ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_CMD_HANDLER_H_ |
| |
| #include <zephyr/kernel.h> |
| |
| #include "modem_context.h" |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| #define MODEM_CMD_DEFINE(name_) \ |
| static int name_(struct modem_cmd_handler_data *data, uint16_t len, \ |
| uint8_t **argv, uint16_t argc) |
| |
| #define MODEM_CMD(cmd_, func_cb_, acount_, adelim_) { \ |
| .cmd = cmd_, \ |
| .cmd_len = (uint16_t)sizeof(cmd_)-1, \ |
| .func = func_cb_, \ |
| .arg_count_min = acount_, \ |
| .arg_count_max = acount_, \ |
| .delim = adelim_, \ |
| .direct = false, \ |
| } |
| |
| #define MODEM_CMD_ARGS_MAX(cmd_, func_cb_, acount_, acountmax_, adelim_) { \ |
| .cmd = cmd_, \ |
| .cmd_len = (uint16_t)sizeof(cmd_)-1, \ |
| .func = func_cb_, \ |
| .arg_count_min = acount_, \ |
| .arg_count_max = acountmax_, \ |
| .delim = adelim_, \ |
| .direct = false, \ |
| } |
| |
| #define MODEM_CMD_DIRECT_DEFINE(name_) MODEM_CMD_DEFINE(name_) |
| |
| #define MODEM_CMD_DIRECT(cmd_, func_cb_) { \ |
| .cmd = cmd_, \ |
| .cmd_len = (uint16_t)sizeof(cmd_)-1, \ |
| .func = func_cb_, \ |
| .arg_count_min = 0, \ |
| .arg_count_max = 0, \ |
| .delim = "", \ |
| .direct = true, \ |
| } |
| |
| #define CMD_RESP 0 |
| #define CMD_UNSOL 1 |
| #define CMD_HANDLER 2 |
| #define CMD_MAX 3 |
| |
| /* |
| * Flags for modem_send_cmd_ext. |
| */ |
| #define MODEM_NO_TX_LOCK BIT(0) |
| #define MODEM_NO_SET_CMDS BIT(1) |
| #define MODEM_NO_UNSET_CMDS BIT(2) |
| |
| struct modem_cmd_handler_data; |
| |
| struct modem_cmd { |
| int (*func)(struct modem_cmd_handler_data *data, uint16_t len, |
| uint8_t **argv, uint16_t argc); |
| const char *cmd; |
| const char *delim; |
| uint16_t cmd_len; |
| uint16_t arg_count_min; |
| uint16_t arg_count_max; |
| bool direct; |
| }; |
| |
| #define SETUP_CMD(cmd_send_, match_cmd_, func_cb_, num_param_, delim_) { \ |
| .send_cmd = cmd_send_, \ |
| MODEM_CMD(match_cmd_, func_cb_, num_param_, delim_) \ |
| } |
| |
| #define SETUP_CMD_NOHANDLE(send_cmd_) \ |
| SETUP_CMD(send_cmd_, NULL, NULL, 0U, NULL) |
| |
| /* series of modem setup commands to run */ |
| struct setup_cmd { |
| const char *send_cmd; |
| struct modem_cmd handle_cmd; |
| }; |
| |
| struct modem_cmd_handler_data { |
| const struct modem_cmd *cmds[CMD_MAX]; |
| size_t cmds_len[CMD_MAX]; |
| |
| char *match_buf; |
| size_t match_buf_len; |
| |
| int last_error; |
| |
| const char *eol; |
| size_t eol_len; |
| |
| /* rx net buffer */ |
| struct net_buf *rx_buf; |
| |
| /* allocation info */ |
| struct net_buf_pool *buf_pool; |
| k_timeout_t alloc_timeout; |
| |
| /* locks */ |
| struct k_sem sem_tx_lock; |
| struct k_sem sem_parse_lock; |
| |
| /* user data */ |
| void *user_data; |
| }; |
| |
| /** |
| * @brief get the last error code |
| * |
| * @param data: command handler data reference |
| * |
| * @retval last handled error. |
| */ |
| int modem_cmd_handler_get_error(struct modem_cmd_handler_data *data); |
| |
| /** |
| * @brief set the last error code |
| * |
| * @param data: command handler data reference |
| * @param error_code: error |
| * |
| * @retval 0 if ok, < 0 if error. |
| */ |
| int modem_cmd_handler_set_error(struct modem_cmd_handler_data *data, |
| int error_code); |
| |
| /** |
| * @brief update the parser's handler commands |
| * |
| * @param data: handler data to use |
| * @param handler_cmds: commands to attach |
| * @param handler_cmds_len: size of commands array |
| * @param reset_error_flag: reset last error code |
| * |
| * @retval 0 if ok, < 0 if error. |
| */ |
| int modem_cmd_handler_update_cmds(struct modem_cmd_handler_data *data, |
| const struct modem_cmd *handler_cmds, |
| size_t handler_cmds_len, |
| bool reset_error_flag); |
| |
| /** |
| * @brief send AT command to interface with behavior defined by flags |
| * |
| * This function is similar to @ref modem_cmd_send, but it allows to choose a |
| * specific behavior regarding acquiring tx_lock, setting and unsetting |
| * @a handler_cmds. |
| * |
| * @param iface: interface to use |
| * @param handler: command handler to use |
| * @param handler_cmds: commands to attach |
| * @param handler_cmds_len: size of commands array |
| * @param buf: NULL terminated send buffer |
| * @param sem: wait for response semaphore |
| * @param timeout: timeout of command |
| * @param flags: flags which influence behavior of command sending |
| * |
| * @retval 0 if ok, < 0 if error. |
| */ |
| int modem_cmd_send_ext(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, int flags); |
| |
| /** |
| * @brief send AT command to interface w/o locking TX |
| * |
| * @param iface: interface to use |
| * @param handler: command handler to use |
| * @param handler_cmds: commands to attach |
| * @param handler_cmds_len: size of commands array |
| * @param buf: NULL terminated send buffer |
| * @param sem: wait for response semaphore |
| * @param timeout: timeout of command |
| * |
| * @retval 0 if ok, < 0 if error. |
| */ |
| static inline int 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) |
| { |
| return modem_cmd_send_ext(iface, handler, handler_cmds, |
| handler_cmds_len, buf, sem, timeout, |
| MODEM_NO_TX_LOCK); |
| } |
| |
| /** |
| * @brief send AT command to interface w/ a TX lock |
| * |
| * @param iface: interface to use |
| * @param handler: command handler to use |
| * @param handler_cmds: commands to attach |
| * @param handler_cmds_len: size of commands array |
| * @param buf: NULL terminated send buffer |
| * @param sem: wait for response semaphore |
| * @param timeout: timeout of command |
| * |
| * @retval 0 if ok, < 0 if error. |
| */ |
| static inline int 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) |
| { |
| return modem_cmd_send_ext(iface, handler, handler_cmds, |
| handler_cmds_len, buf, sem, timeout, 0); |
| } |
| |
| /** |
| * @brief send a series of AT commands w/ a TX lock |
| * |
| * @param iface: interface to use |
| * @param handler: command handler to use |
| * @param cmds: array of setup commands to send |
| * @param cmds_len: size of the setup command array |
| * @param sem: wait for response semaphore |
| * @param timeout: timeout of command |
| * |
| * @retval 0 if ok, < 0 if error. |
| */ |
| int modem_cmd_handler_setup_cmds(struct modem_iface *iface, |
| struct modem_cmd_handler *handler, |
| const struct setup_cmd *cmds, size_t cmds_len, |
| struct k_sem *sem, k_timeout_t timeout); |
| |
| /** |
| * @brief send a series of AT commands w/o locking TX |
| * |
| * @param iface: interface to use |
| * @param handler: command handler to use |
| * @param cmds: array of setup commands to send |
| * @param cmds_len: size of the setup command array |
| * @param sem: wait for response semaphore |
| * @param timeout: timeout of command |
| * |
| * @retval 0 if ok, < 0 if error. |
| */ |
| int modem_cmd_handler_setup_cmds_nolock(struct modem_iface *iface, |
| struct modem_cmd_handler *handler, |
| const struct setup_cmd *cmds, |
| size_t cmds_len, struct k_sem *sem, |
| k_timeout_t timeout); |
| |
| /** |
| * @brief Modem command handler configuration |
| * |
| * @details Contains user configuration which is used to set up |
| * command handler data context. The struct is initialized and then passed |
| * to modem_cmd_handler_init(). |
| * |
| * @retval 0 if ok, < 0 if error. |
| * @param match_buf Buffer used for matching commands |
| * @param match_buf_len Length of buffer used for matching commands |
| * @param buf_pool Initialized buffer pool used to store incoming data |
| * @param alloc_timeout Timeout for allocating data in buffer pool |
| * @param eol End of line represented as string |
| * @param user_data Free to use data which can be retrieved from within command handlers |
| * @param response_cmds Array of response command handlers |
| * @param response_cmds_len Length of response command handlers array |
| * @param unsol_cmds Array of unsolicitet command handlers |
| * @param unsol_cmds_len Length of unsolicitet command handlers array |
| */ |
| struct modem_cmd_handler_config { |
| char *match_buf; |
| size_t match_buf_len; |
| struct net_buf_pool *buf_pool; |
| k_timeout_t alloc_timeout; |
| const char *eol; |
| void *user_data; |
| const struct modem_cmd *response_cmds; |
| size_t response_cmds_len; |
| const struct modem_cmd *unsol_cmds; |
| size_t unsol_cmds_len; |
| }; |
| |
| /** |
| * @brief Initialize modem command handler |
| * |
| * @details This function is called once for each command handler, before any |
| * incoming data is processed. |
| * |
| * @note All arguments passed to this function, including the referenced data |
| * contained in the setup struct, must persist as long as the command handler itself. |
| * |
| * @param handler Command handler to initialize |
| * @param data Command handler data to use |
| * @param setup Command handler setup |
| * |
| * @return -EINVAL if any argument is invalid |
| * @return 0 if successful |
| */ |
| int modem_cmd_handler_init(struct modem_cmd_handler *handler, |
| struct modem_cmd_handler_data *data, |
| const struct modem_cmd_handler_config *config); |
| |
| /** |
| * @brief Lock the modem for sending cmds |
| * |
| * This is semaphore-based rather than mutex based, which means there's no |
| * requirements of thread ownership for the user. This function is useful |
| * when one needs to prevent threads from sending UART data to the modem for an |
| * extended period of time (for example during modem reset). |
| * |
| * @param handler: command handler to lock |
| * @param timeout: give up after timeout |
| * |
| * @retval 0 if ok, < 0 if error. |
| */ |
| int modem_cmd_handler_tx_lock(struct modem_cmd_handler *handler, |
| k_timeout_t timeout); |
| |
| /** |
| * @brief Unlock the modem for sending cmds |
| * |
| * @param handler: command handler to unlock |
| */ |
| void modem_cmd_handler_tx_unlock(struct modem_cmd_handler *handler); |
| |
| /** |
| * @brief Process incoming data |
| * |
| * @details This function will process any data available from the interface |
| * using the command handler. The command handler will invoke any matching modem |
| * command which has been registered using @ref modem_cmd_handler_init_cmds or |
| * @ref modem_cmd_handler_update_cmds. Once handled, the function will return. |
| * |
| * @note This function should be invoked from a dedicated thread, which only handles |
| * commands. |
| * |
| * @param handler The handler wich will handle the command when processed |
| * @param iface The interface which receives incoming data |
| */ |
| static inline void modem_cmd_handler_process(struct modem_cmd_handler *handler, |
| struct modem_iface *iface) |
| { |
| handler->process(handler, iface); |
| } |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif /* ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_CMD_HANDLER_H_ */ |