zephyr/drivers/modem/modem_cmd_handler.h
Bjarki Arge Andreasen aa6ecc59d1 drivers/modem/modem_cmd_handler: Update API
There is currently not a clear separation between
user configuration and internal context when using the
modem_cmd_handler library. This update adds a clear
separation, placing user configuration in a seperate
struct passed to modem_cmd_handler_init alongside the
internal context modem_cmd_handler_data.

There is also a lack of documentation of the user
configurations, these have been added to the new config
struct.

The new API function modem_cmd_handler_process has been
added to remove the need for the user to directly access
the process member of the internal context. This ensures
that the user is not encouraged to access any internal
context members.

Some whitespace errors exist in the modem_cmd_handler.c
file, these are outside of the scope of this PR. These
can be addressed in a later PR as they are not functional
changes.

Signed-off-by: Bjarki Arge Andreasen <baa@trackunit.com>
2023-04-11 11:42:00 +02:00

368 lines
10 KiB
C

/** @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_ */