zephyr/drivers/modem/modem_cmd_handler.h
Marcin Niestroj 1a4cb7e0e3 drivers: modem: add modem_cmd_send_ext()
There are currently modem_cmd_send() and modem_cmd_send_nolock()
functions, each with slightly different behavior regarding acquiring (or
not) sem_tx_lock. Introduce a generic function that will allow caller to
select behavior using flags.

While at it, add possibility to disable setting and unsetting command
handlers. This will be useful in situations when there are multiple
replies expected one after the other coming as a response to single
command. This is the case for example with ESP-AT driver, which expects
following data as response to AT+CIPSEND command:

  OK
  >

where 'OK' is handled by static CMD_RESP (so releasing response
semaphore right away), while '>' is handled by dynamic
CMD_HANDLER (which is releasing another semaphore). Keeping command
handlers in place allows to receive '>' without race condition because
of small period of time where command handlers are not set.

Convert modem_cmd_send_nolock() and modem_cmd_send() to static inline
functions, as they are just a specific invocation of
modem_cmd_send_ext() function.

Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
2021-07-28 21:08:51 -04:00

294 lines
7.7 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 <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;
};
/**
* @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 *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 *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 *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 Init command handler
*
* @param *handler: command handler to initialize
* @param *data: command handler data to use
*
* @retval 0 if ok, < 0 if error.
*/
int modem_cmd_handler_init(struct modem_cmd_handler *handler,
struct modem_cmd_handler_data *data);
/**
* @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. These functions are 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 lock: set true to lock, false to unlock
* @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);
void modem_cmd_handler_tx_unlock(struct modem_cmd_handler *handler);
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_CMD_HANDLER_H_ */