CubeMX: SPI4 + SDMMC1/FatFs + I2S6, audio BSP et debug VS Code (Linux)

Regeneration CubeMX (Appli) preparant le driver ST67W611M1 et la carte SD :
- SPI4 (PE12/13/14, CS PF8) + DMA GPDMA1 canaux 4/5, SPI_RDY (PD12/EXTI12),
  CHIP_EN (PD13) et BOOT (PD15) pour la X-NUCLEO-67W61M1 sur l'Arduino
- SDMMC1 + FatFs (appels d'init non generes dans main, IRQ SDMMC1 prio 14),
  PM14 en open-drain ; I2S6_CK sur PA5 pour liberer PC12 (SDMMC1_CK)
- HAL UART retire ; USE_BSP_COM_FEATURE=0
- Audio preserve : ADF 4/16/24, prio IRQ 2, DMA canal 0 en priorite haute,
  GPDMA1_Channel2_IRQHandler deplace dans audio_irq.c (hors fichiers generes),
  BSP_MX_I2S6_Init (renommage dans le BSP pour eviter le conflit avec CubeMX)
- Middlewares/ST/ST67W6X_Network_Driver, FatFs et sources HAL SPI/SD ajoutes
  (driver ST67 pas encore integre au build)

Build et debug sous Linux :
- gcc/toolchain-env.sh, gdbserver.sh, arm-gdb.sh : outils de STM32CubeIDE par
  glob ; flash.sh les retrouve ; ruby_compat.rb pour Ruby >= 3.2 (TouchGFX)
- makefile_appli : sources SD/SPI/FatFs/audio_irq ; chmod +x des .out TouchGFX
- Objets de build (*.o, *.d, *.su) retires de l'index (deja dans .gitignore)
- AGENTS.md : toolchain Linux, configs VS Code (cppdbg et ST stlinkgdbtarget),
  pieges de l'extension ST (.settings/ide.store.json, preRunCommands reset)

Verifie sur carte : audio OK, debug Appli/Boot OK.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
thomasandClaude Sonnet 5.5 committed 2026-10-23 22:49:06 +02:00
1 parent b119a6abd7
commit 66bb319423
1092 files changed
+172728 -133236

No files matched your search

@@ -0,0 +1,957 @@
/**
******************************************************************************
* @file modem_cmd_handler.c
* @author ST67 Application Team
* @brief Modem command handler for modem context driver
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/**
* Portions of this file are based on Zephyr Project,
* which is licensed under the Apache-2.0 license as indicated below.
* See https://github.com/zephyrproject-rtos/zephyr for more information.
*
* Reference source:
* https://github.com/zephyrproject-rtos/zephyr/blob/main/drivers/modem/modem_cmd_handler.c
*/
/*
* Copyright (c) 2019-2020 Foundries.io
*
* SPDX-License-Identifier: Apache-2.0
*/
/* Includes ------------------------------------------------------------------*/
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include "FreeRTOS.h"
#include "semphr.h"
#include "modem_cmd_handler.h"
#include "w61_default_config.h"
#include "logging.h"
/* Private macros ------------------------------------------------------------*/
/** @addtogroup ST67W61_AT_Modem_Cmd_Handler_Macros
* @{
*/
/**
* \def LOG_ERR( ... )
* Send a message to the log with level ::LOG_ERR.
*/
/**
* \def LOG_DBG( ... )
* Send a message to the log with level ::LOG_DEBUG.
*/
/**
* \def LOG_HEXDUMP_DBG( ... )
* Send a hex dump message to the log with level ::LOG_DEBUG.
*/
#if (MDM_CMD_LOG_ENABLE == 1)
#define LOG_ERR(...) LogError(__VA_ARGS__)
#define LOG_DBG(...) LogDebug(__VA_ARGS__)
#define LOG_HEXDUMP_DBG(...)
#else
#define LOG_ERR(...)
#define LOG_DBG(...)
#define LOG_HEXDUMP_DBG(...)
#endif /* MDM_CMD_LOG_ENABLE */
/** @} */
/* Private function prototypes -----------------------------------------------*/
/** @addtogroup ST67W61_AT_Modem_Cmd_Handler_Functions
* @{
*/
/**
* @brief Checks if the given character is a carriage return (CR) or line feed (LF)
* @param c: The character to check
* @return true if the character is either (CR) or (LF), false otherwise
*/
static bool is_crlf(uint8_t c);
/**
* @brief Skips carriage return and line feed characters in the modem command handler data
* @details This function advances the internal pointer or index within the provided
* modem_cmd_handler_data structure to bypass any CR or LF characters.
* It is typically used to ignore line endings when parsing modem responses or commands.
* @param data: Pointer to the modem_cmd_handler_data structure containing the buffer
* and current parsing position
*/
static void skipcrlf(struct modem_cmd_handler_data *data);
/**
* @brief Finds the position of the next carriage return (CR) or line feed (LF) character
* @details This function scans the modem command handler data buffer for the next occurrence
* of a CR or LF character and returns its position.
* @param data: Pointer to the modem_cmd_handler_data structure containing the buffer
* and current parsing position
* @param offset: Pointer to a variable to store the offset of the found character
* @return uint16_t: The position of the found character, or 0 if not found
*/
static uint16_t findcrlf(struct modem_cmd_handler_data *data, uint16_t *offset);
/**
* @brief Checks if the given buffer starts with the specified string
* @details This function compares the beginning of the buffer with the provided
* null-terminated string and returns true if the buffer starts with the string.
*
* @param buf: Pointer to the buffer to check
* @param buf_len: Length of the buffer in bytes
* @param str: Null-terminated string to compare with the start of the buffer
* @return true if the buffer starts with the specified string, false otherwise
*/
static bool starts_with(const uint8_t *buf, size_t buf_len, const char *str);
/**
* @brief Skips the first n bytes in the given linear buffer
* @details This function advances the buffer pointer by n bytes and decreases the buffer length accordingly.
* @param rx_buf: Pointer to the buffer to be modified
* @param rx_buf_len: Pointer to the length of the buffer; will be updated after skipping
* @param n: Number of bytes to skip from the beginning of the buffer
*/
static void linear_buf_skip(uint8_t *rx_buf, size_t *rx_buf_len, size_t n);
/**
* @brief Parses parameters from the matched command in the modem command handler data
* @details This function extracts parameters from the matched command string in the
* modem_cmd_handler_data structure, based on the specified command definition.
* It populates the argv array with pointers to the parsed parameters and updates
* the argc count. The function handles quoted parameters and ensures that the
* number of parsed parameters falls within the defined minimum and maximum limits.
* @param data: Pointer to the modem_cmd_handler_data structure containing the matched command
* @param match_len: Length of the matched command string
* @param cmd: Pointer to the modem_cmd structure defining the command and its parameters
* @param argv: Array of pointers to store the addresses of parsed parameters
* @param argv_len: Length of the argv array (maximum number of parameters it can hold)
* @param argc: Pointer to a variable to store the count of parsed parameters
* @return On success, returns the number of bytes parsed for parameters;
* On error, returns a negative error code (e.g., -EINVAL for invalid arguments)
*/
static int32_t parse_params(struct modem_cmd_handler_data *data, size_t match_len,
const struct modem_cmd *cmd, uint8_t **argv, size_t argv_len, uint16_t *argc);
/**
* @brief Processes a matched command in the modem command handler
* @details This function is responsible for executing the matched command
* and handling its parameters. It validates the command and
* invokes the appropriate command handler function.
* @param cmd: Pointer to the modem_cmd structure defining the command
* @param match_len: Length of the matched command string
* @param data: Pointer to the modem_cmd_handler_data structure containing
* the current state and data for command processing
* @return On success, returns 0; on error, returns a negative error code
*/
static int32_t process_cmd(const struct modem_cmd *cmd, size_t match_len, struct modem_cmd_handler_data *data);
/**
* @brief Finds a matching command in the modem command handler data
* @details This function searches through the command lists in the modem_cmd_handler_data
* structure to find a command that matches the current input buffer (match_buf).
* It checks the response handlers, unsolicited handlers, and currently assigned handlers.
* @param data: Pointer to the modem_cmd_handler_data structure containing the command lists
* @return Pointer to the matched modem_cmd structure if a match is found; NULL otherwise
*/
static const struct modem_cmd *find_cmd_match(struct modem_cmd_handler_data *data);
/**
* @brief Finds a direct match command in the modem command handler data
* @details This function searches through the command lists in the modem_cmd_handler_data
* structure to find a command that directly matches the start of the current
* input buffer (rx_buf). It checks for commands marked with the 'direct' flag.
* @param data: Pointer to the modem_cmd_handler_data structure containing the command lists
* @return Pointer to the matched modem_cmd structure if a direct match is found; NULL otherwise
*/
static const struct modem_cmd *find_cmd_direct_match(struct modem_cmd_handler_data *data);
/**
* @brief Process incoming data from the modem interface
* @details This function reads data from the modem interface and appends it to the
* receive buffer in the modem_cmd_handler_data structure. It handles reading
* as much data as possible without exceeding the buffer size.
* @param data: Pointer to the modem_cmd_handler_data structure containing the receive buffer
* @param iface: Pointer to the modem_iface structure representing the modem interface
* @return On success, returns 0; on error, returns a negative error code
*/
static int32_t cmd_handler_process_iface_data(struct modem_cmd_handler_data *data, struct modem_iface *iface);
/**
* @brief Process the received data in the modem command handler
* @details This function processes the data in the receive buffer of the
* modem_cmd_handler_data structure. It looks for complete commands
* terminated by CR/LF, matches them against known commands, and
* invokes the corresponding command handlers.
* @param data: Pointer to the modem_cmd_handler_data structure containing the receive buffer
*/
static void cmd_handler_process_rx_buf(struct modem_cmd_handler_data *data);
/**
* @brief Process the modem command handler
* @details This function processes the modem command handler by invoking the
* appropriate command handler functions based on the current state
* and received data.
* @param cmd_handler: Pointer to the modem_cmd_handler structure representing the command handler
* @param iface: Pointer to the modem_iface structure representing the modem interface
*/
static void cmd_handler_process(struct modem_cmd_handler *cmd_handler, struct modem_iface *iface);
/* Private Functions Definition ----------------------------------------------*/
static bool is_crlf(uint8_t c)
{
if ((c == 0x0AU) || (c == 0x0DU))
{
return true;
}
else
{
return false;
}
}
static void skipcrlf(struct modem_cmd_handler_data *data)
{
size_t skip_count = 0;
if ((data == NULL) || (data->rx_buf == NULL) || (data->rx_buf_len == 0U))
{
return;
}
while ((skip_count < data->rx_buf_len) && is_crlf(data->rx_buf[skip_count]))
{
skip_count++;
}
if (skip_count > 0U)
{
linear_buf_skip(data->rx_buf, &data->rx_buf_len, skip_count);
}
}
static uint16_t findcrlf(struct modem_cmd_handler_data *data, uint16_t *offset)
{
if ((data == NULL) || (data->rx_buf == NULL) || (data->rx_buf_len == 0U) || (offset == NULL))
{
return 0;
}
for (size_t i = 0; i < data->rx_buf_len; ++i)
{
if (is_crlf(data->rx_buf[i]))
{
*offset = (uint16_t)i;
return (uint16_t)i;
}
}
return 0;
}
static bool starts_with(const uint8_t *buf, size_t buf_len, const char *str)
{
size_t i = 0;
while ((i < buf_len) && (*str != '\0'))
{
if (buf[i] != (uint8_t)*str)
{
return false;
}
i++;
str++;
}
return *str == '\0';
}
static void linear_buf_skip(uint8_t *rx_buf, size_t *rx_buf_len, size_t n)
{
if (n >= *rx_buf_len)
{
*rx_buf_len = 0;
}
else
{
for (size_t i = 0; i < (*rx_buf_len - n); ++i)
{
rx_buf[i] = rx_buf[i + n];
}
*rx_buf_len -= n;
}
}
static int32_t parse_params(struct modem_cmd_handler_data *data, size_t match_len,
const struct modem_cmd *cmd, uint8_t **argv, size_t argv_len, uint16_t *argc)
{
uint32_t count = 0;
size_t delim_len, begin, end, i;
bool quoted = false;
if ((data == NULL) || (data->match_buf == NULL) || (match_len == 0U) || (cmd == NULL) ||
(argv == NULL) || (argc == NULL))
{
return -EINVAL;
}
begin = cmd->cmd_len;
end = cmd->cmd_len;
delim_len = strlen(cmd->delim);
while (end < match_len)
{
/* Don't look for delimiters in the middle of a quoted parameter */
if (data->match_buf[end] == '"')
{
quoted = !quoted;
}
if (quoted)
{
end++;
continue;
}
/* Look for delimiter characters */
for (i = 0; i < delim_len; i++)
{
if (data->match_buf[end] == cmd->delim[i])
{
/* Mark a parameter beginning */
argv[*argc] = (uint8_t *)&data->match_buf[begin];
/* End parameter with NUL char */
data->match_buf[end] = '\0';
/* Bump begin */
begin = end + 1U;
count += 1U;
(*argc)++;
break;
}
}
if (count >= cmd->arg_count_max)
{
break;
}
if (*argc == argv_len)
{
break;
}
end++;
}
/* Consider the ending portion a param if end > begin */
if (end > begin)
{
/* Mark a parameter beginning */
argv[*argc] = (uint8_t *)&data->match_buf[begin];
/* End parameter with NUL char
* NOTE: if this is at the end of match_len will probably
* be overwriting a NUL that's already there
*/
data->match_buf[end] = '\0';
(*argc)++;
}
/* Missing arguments */
if (*argc < cmd->arg_count_min)
{
/* Do not return -EAGAIN here as there is no way new argument
* can be parsed later because match_len is computed to be
* the minimum of the distance to the first CRLF and the size
* of the buffer.
* Therefore, waiting more data on the interface won't change
* match_len value, which mean there is no point in waiting
* for more arguments, this will just end in a infinite loop
* parsing data and finding that some arguments are missing.
*/
return -EINVAL;
}
/*
* Return the beginning of the next unfinished param so don't
* "skip" any data that could be parsed later.
*/
return begin - cmd->cmd_len;
}
static int32_t process_cmd(const struct modem_cmd *cmd, size_t match_len, struct modem_cmd_handler_data *data)
{
int32_t parsed_len = 0;
int32_t ret = 0;
uint8_t *argv[CONFIG_MODEM_CMD_HANDLER_MAX_PARAM_COUNT];
uint16_t argc = 0U;
/* Reset params */
(void)memset(argv, 0, sizeof(argv[0]) * ARRAY_SIZE(argv));
/* Needed to parse arguments? */
if (cmd->arg_count_max > 0U)
{
/* Returns < 0 on error and > 0 for parsed len */
parsed_len = parse_params(data, match_len, cmd,
argv, ARRAY_SIZE(argv), &argc);
if (parsed_len < 0)
{
return parsed_len;
}
}
/* Skip cmd_len + parsed len */
size_t skip_len = cmd->cmd_len + parsed_len;
/* Store original rx_buf */
uint8_t *orig_rx_buf = data->rx_buf;
size_t orig_rx_buf_len = data->rx_buf_len;
if ((skip_len > 0U) && (skip_len <= data->rx_buf_len))
{
data->rx_buf += skip_len;
data->rx_buf_len -= skip_len;
}
/* Call handler */
if (cmd->func != NULL)
{
ret = cmd->func(data, match_len - cmd->cmd_len - parsed_len,
argv, argc);
}
/* Restore rx_buf */
data->rx_buf = orig_rx_buf;
data->rx_buf_len = orig_rx_buf_len;
if ((ret != -EAGAIN) && (data->rx_buf != NULL))
{
/* Skip in case consumed */
linear_buf_skip(data->rx_buf, &data->rx_buf_len, skip_len);
}
return ret;
}
static const struct modem_cmd *find_cmd_match(struct modem_cmd_handler_data *data)
{
for (uint32_t j = 0; j < ARRAY_SIZE(data->cmds); j++)
{
if ((data->cmds[j] == NULL) || (data->cmds_len[j] == 0U))
{
continue;
}
for (size_t i = 0; i < data->cmds_len[j]; i++)
{
/* Match on "empty" cmd */
if (((data->cmds[j][i].cmd != NULL) && (data->match_buf != NULL)) &&
((strlen(data->cmds[j][i].cmd) == 0U) ||
(strncmp(data->match_buf, data->cmds[j][i].cmd, data->cmds[j][i].cmd_len) == 0)))
{
return &data->cmds[j][i];
}
}
}
return NULL;
}
static const struct modem_cmd *find_cmd_direct_match(struct modem_cmd_handler_data *data)
{
if (data->rx_buf == NULL)
{
return NULL;
}
for (size_t j = 0; j < ARRAY_SIZE(data->cmds); j++)
{
if ((data->cmds[j] == NULL) || (data->cmds_len[j] == 0U))
{
continue;
}
for (size_t i = 0; i < data->cmds_len[j]; i++)
{
/* Match start of cmd */
if ((data->cmds[j][i].cmd != NULL) &&
data->cmds[j][i].direct &&
((data->cmds[j][i].cmd[0] == '\0') ||
starts_with(data->rx_buf, data->rx_buf_len, data->cmds[j][i].cmd)))
{
return &data->cmds[j][i];
}
}
}
return NULL;
}
static int32_t cmd_handler_process_iface_data(struct modem_cmd_handler_data *data, struct modem_iface *iface)
{
size_t bytes_read = 0;
int32_t ret;
if ((data->rx_buf == NULL) || (iface == NULL) || (iface->mdm_read == NULL))
{
return -EINVAL;
}
/* Read as much as possible into the linear buffer */
size_t room = RX_BUF_SIZE - data->rx_buf_len;
if (room == 0U)
{
return -ENOMEM;
}
ret = iface->mdm_read(iface, data->rx_buf + data->rx_buf_len, room, &bytes_read);
if ((ret < 0) || (bytes_read == 0U))
{
return 0;
}
data->rx_buf_len += bytes_read;
return 0;
}
static void cmd_handler_process_rx_buf(struct modem_cmd_handler_data *data)
{
const struct modem_cmd *cmd;
size_t match_len;
int32_t ret;
uint16_t offset;
uint16_t len;
if ((data->rx_buf == NULL) || (data->match_buf == NULL))
{
return;
}
while (data->rx_buf_len > 0U)
{
skipcrlf(data);
if (data->rx_buf_len == 0U)
{
break;
}
/* Direct match */
cmd = find_cmd_direct_match(data);
if ((cmd != NULL) && (cmd->func != NULL))
{
ret = cmd->func(data, cmd->cmd_len, NULL, 0);
if (ret == -EAGAIN)
{
break;
}
else if (ret > 0)
{
LOG_DBG("match direct cmd [%s] (ret:%" PRIi32 ")\n", cmd->cmd, ret);
linear_buf_skip(data->rx_buf, &data->rx_buf_len, ret);
}
else
{
/* Unhandled case */
}
continue;
}
/* Find CRLF */
len = findcrlf(data, &offset);
if (len == 0U)
{
break;
}
/* Copy up to CRLF into match_buf */
match_len = (len < data->match_buf_len - 1) ? len : data->match_buf_len - 1;
(void)memcpy(data->match_buf, data->rx_buf, match_len);
data->match_buf[match_len] = '\0';
#if defined(CONFIG_MODEM_CONTEXT_VERBOSE_DEBUG)
LOG_HEXDUMP_DBG(data->match_buf, match_len, "RECV");
#endif /* CONFIG_MODEM_CONTEXT_VERBOSE_DEBUG */
(void)xSemaphoreTake(data->sem_parse_lock, portMAX_DELAY);
cmd = find_cmd_match(data);
if (cmd != NULL)
{
LOG_DBG("match cmd [%s] (len:%" PRIu32 ")\n", cmd->cmd, match_len);
ret = process_cmd(cmd, match_len, data);
if (ret == -EAGAIN)
{
(void)xSemaphoreGive(data->sem_parse_lock);
break;
}
else if (ret < 0)
{
LOG_ERR("process cmd [%s] (len:%" PRIu32 ", ret:%" PRIi32 ")\n", cmd->cmd, match_len, ret);
}
else
{
/* Unhandled case */
}
if (data->rx_buf_len == 0U)
{
(void)xSemaphoreGive(data->sem_parse_lock);
break;
}
/* Find CRLF again to skip processed line */
(void)findcrlf(data, &offset);
}
(void)xSemaphoreGive(data->sem_parse_lock);
if ((data->rx_buf != NULL) && (data->rx_buf_len != 0U))
{
linear_buf_skip(data->rx_buf, &data->rx_buf_len, offset + 1U);
}
}
}
static void cmd_handler_process(struct modem_cmd_handler *cmd_handler, struct modem_iface *iface)
{
struct modem_cmd_handler_data *data;
int32_t err;
if ((cmd_handler == NULL) || (cmd_handler->cmd_handler_data == NULL) ||
(iface == NULL) || (iface->mdm_read == NULL))
{
return;
}
data = (struct modem_cmd_handler_data *)(cmd_handler->cmd_handler_data);
do
{
err = cmd_handler_process_iface_data(data, iface);
cmd_handler_process_rx_buf(data);
} while (err == 0);
}
/* Functions Definition ------------------------------------------------------*/
int32_t modem_cmd_handler_get_error(struct modem_cmd_handler_data *data)
{
if (data == NULL)
{
return -EINVAL;
}
return data->last_error;
}
int32_t modem_cmd_handler_set_error(struct modem_cmd_handler_data *data, int32_t error_code)
{
if (data == NULL)
{
return -EINVAL;
}
data->last_error = error_code;
return 0;
}
int32_t 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)
{
if (data == NULL)
{
return -EINVAL;
}
data->cmds[CMD_HANDLER] = handler_cmds;
data->cmds_len[CMD_HANDLER] = handler_cmds_len;
if (reset_error_flag)
{
data->last_error = 0;
}
return 0;
}
int32_t modem_cmd_handler_await(struct modem_cmd_handler_data *data,
SemaphoreHandle_t sem, TickType_t timeout)
{
int32_t ret = xSemaphoreTake(sem, timeout);
if (ret == pdTRUE)
{
ret = modem_cmd_handler_get_error(data);
}
else
{
ret = -ETIMEDOUT;
}
return ret;
}
int32_t modem_cmd_send_data_nolock(struct modem_iface *iface,
const uint8_t *buf, size_t len)
{
if ((iface == NULL) || (iface->mdm_write == NULL) || (buf == NULL))
{
return -EINVAL;
}
#if defined(CONFIG_MODEM_CONTEXT_VERBOSE_DEBUG)
if (len > 256)
{
/* Truncate the message, since too long log messages gets dropped somewhere. */
LOG_HEXDUMP_DBG(buf, 256, "SENT DIRECT DATA (truncated)");
}
else
{
LOG_HEXDUMP_DBG(buf, len, "SENT DIRECT DATA");
}
#endif /* CONFIG_MODEM_CONTEXT_VERBOSE_DEBUG */
return iface->mdm_write(iface, buf, len);
}
int32_t 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,
SemaphoreHandle_t sem, TickType_t timeout, uint32_t flags)
{
struct modem_cmd_handler_data *data;
int32_t ret = 0;
if ((iface == NULL) || (iface->mdm_write == NULL) ||
(handler == NULL) || (handler->cmd_handler_data == NULL) ||
(buf == NULL))
{
return -EINVAL;
}
if (timeout == 0U)
{
/* Semaphore is not needed if there is no timeout */
sem = NULL;
}
else
{
if (sem == NULL)
{
/* Cannot respect timeout without semaphore */
return -EINVAL;
}
}
data = (struct modem_cmd_handler_data *)(handler->cmd_handler_data);
if ((flags & MODEM_NO_TX_LOCK) == 0U)
{
(void)xSemaphoreTake(data->sem_tx_lock, portMAX_DELAY);
}
if ((flags & MODEM_NO_SET_CMDS) == 0U)
{
ret = modem_cmd_handler_update_cmds(data, handler_cmds,
handler_cmds_len, true);
if (ret < 0)
{
goto unlock_tx_lock;
}
}
#if defined(CONFIG_MODEM_CONTEXT_VERBOSE_DEBUG)
LOG_HEXDUMP_DBG(buf, strlen(buf), "SENT DATA");
if (data->eol_len > 0)
{
if (data->eol[0] != '\r')
{
LOG_HEXDUMP_DBG(data->eol, data->eol_len, "SENT EOL");
}
}
else
{
LOG_DBG("EOL not set!!!");
}
#endif /* CONFIG_MODEM_CONTEXT_VERBOSE_DEBUG */
if (sem != NULL)
{
(void)xSemaphoreTake(sem, 0); /* Reset semaphore (binary) */
}
(void)iface->mdm_write(iface, buf, strlen((char *)buf));
(void)iface->mdm_write(iface, (uint8_t const *)data->eol, data->eol_len);
if (sem != NULL)
{
ret = modem_cmd_handler_await(data, sem, timeout);
}
if ((flags & MODEM_NO_UNSET_CMDS) == 0U)
{
(void)modem_cmd_handler_update_cmds(data, NULL, 0U, false);
}
unlock_tx_lock:
if ((flags & MODEM_NO_TX_LOCK) == 0U)
{
(void)xSemaphoreGive(data->sem_tx_lock);
}
return ret;
}
/* Run a set of AT commands */
int32_t modem_cmd_handler_setup_cmds(struct modem_iface *iface,
struct modem_cmd_handler *handler,
const struct setup_cmd *cmds, size_t cmds_len,
SemaphoreHandle_t sem, TickType_t timeout)
{
int32_t ret = 0;
size_t i;
for (i = 0; i < cmds_len; i++)
{
if ((cmds[i].handle_cmd.cmd != NULL) && (cmds[i].handle_cmd.func != NULL))
{
ret = modem_cmd_send(iface, handler,
&cmds[i].handle_cmd, 1U,
(uint8_t const *)cmds[i].send_cmd,
sem, timeout);
}
else
{
ret = modem_cmd_send(iface, handler,
NULL, 0, (uint8_t const *)cmds[i].send_cmd,
sem, timeout);
}
vTaskDelay(pdMS_TO_TICKS(50));
if (ret < 0)
{
LOG_ERR("command %s ret:%" PRIi32 "\n", cmds[i].send_cmd, ret);
break;
}
}
return ret;
}
/* Run a set of AT commands, without lock */
int32_t modem_cmd_handler_setup_cmds_nolock(struct modem_iface *iface,
struct modem_cmd_handler *handler,
const struct setup_cmd *cmds,
size_t cmds_len, SemaphoreHandle_t sem,
TickType_t timeout)
{
int32_t ret = 0;
size_t i;
for (i = 0; i < cmds_len; i++)
{
if ((cmds[i].handle_cmd.cmd != NULL) && (cmds[i].handle_cmd.func != NULL))
{
ret = modem_cmd_send_nolock(iface, handler,
&cmds[i].handle_cmd, 1U,
(uint8_t const *)cmds[i].send_cmd,
sem, timeout);
}
else
{
ret = modem_cmd_send_nolock(iface, handler,
NULL, 0, (uint8_t const *)cmds[i].send_cmd,
sem, timeout);
}
vTaskDelay(pdMS_TO_TICKS(50));
if (ret < 0)
{
LOG_ERR("command %s ret:%" PRIi32 "\n", cmds[i].send_cmd, ret);
break;
}
}
return ret;
}
int32_t modem_cmd_handler_tx_lock(struct modem_cmd_handler *handler,
TickType_t timeout)
{
struct modem_cmd_handler_data *data;
data = (struct modem_cmd_handler_data *)(handler->cmd_handler_data);
if (data == NULL)
{
return -EINVAL;
}
return xSemaphoreTake(data->sem_tx_lock, timeout) == pdTRUE ? 0 : -ETIMEDOUT;
}
void modem_cmd_handler_tx_unlock(struct modem_cmd_handler *handler)
{
struct modem_cmd_handler_data *data;
data = (struct modem_cmd_handler_data *)(handler->cmd_handler_data);
if (data == NULL)
{
return;
}
(void)xSemaphoreGive(data->sem_tx_lock);
}
int32_t modem_cmd_handler_init(struct modem_cmd_handler *handler,
struct modem_cmd_handler_data *data,
const struct modem_cmd_handler_config *config)
{
/* Verify arguments */
if ((handler == NULL) || (data == NULL) || (config == NULL))
{
return -EINVAL;
}
/* Verify config */
if ((config->match_buf == NULL) || (config->match_buf_len == 0U) ||
((NULL != config->response_cmds) && (0U == config->response_cmds_len)) ||
((NULL != config->unsol_cmds) && (0U == config->unsol_cmds_len)))
{
return -EINVAL;
}
/* Assign data to command handler */
handler->cmd_handler_data = data;
/* Init rx_buf_len */
data->rx_buf_len = 0U;
/* Assign command process implementation to command handler */
handler->process = cmd_handler_process;
/* Store arguments */
data->match_buf = config->match_buf;
data->match_buf_len = config->match_buf_len;
data->eol = config->eol;
data->cmds[CMD_RESP] = config->response_cmds;
data->cmds_len[CMD_RESP] = config->response_cmds_len;
data->cmds[CMD_UNSOL] = config->unsol_cmds;
data->cmds_len[CMD_UNSOL] = config->unsol_cmds_len;
/* Process end of line */
data->eol_len = (data->eol == NULL) ? 0U : strlen(data->eol);
/* Store optional user data */
data->user_data = config->user_data;
/* Initialize command handler data members */
data->sem_tx_lock = xSemaphoreCreateMutex();
data->sem_parse_lock = xSemaphoreCreateMutex();
return 0;
}
/** @} */
@@ -0,0 +1,497 @@
/**
******************************************************************************
* @file modem_cmd_handler.h
* @author ST67 Application Team
* @brief Modem command handler header file
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/**
* Portions of this file are based on Zephyr Project,
* which is licensed under the Apache-2.0 license as indicated below.
* See https://github.com/zephyrproject-rtos/zephyr for more information.
*
* Reference source:
* https://github.com/zephyrproject-rtos/zephyr/blob/main/drivers/modem/modem_cmd_handler.h
*/
/*
* Copyright (c) 2019 Foundries.io
*
* SPDX-License-Identifier: Apache-2.0
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef MODEM_MODEM_CMD_HANDLER_H
#define MODEM_MODEM_CMD_HANDLER_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include "FreeRTOS.h"
#include "semphr.h"
/** @defgroup ST67W61_AT_Modem_Cmd_Handler ST67W61 AT Modem Command Handler
* @ingroup ST67W61_AT
*/
/* Exported constants --------------------------------------------------------*/
/** @defgroup ST67W61_AT_Modem_Cmd_Handler_Constants ST67W61 AT Modem Command Handler Constants
* @ingroup ST67W61_AT_Modem_Cmd_Handler
* @{
*/
#ifndef CONFIG_MODEM_CMD_HANDLER_MAX_PARAM_COUNT
/** Maximum number of parameters for modem commands */
#define CONFIG_MODEM_CMD_HANDLER_MAX_PARAM_COUNT 11
#endif /* CONFIG_MODEM_CMD_HANDLER_MAX_PARAM_COUNT */
/** Size of the receive buffer */
#define RX_BUF_SIZE W61_MAX_SPI_XFER
#define EIO 5 /*!< I/O error */
#define EAGAIN 11 /*!< No more contexts */
#define ENOMEM 12 /*!< Not enough core */
#define EBUSY 16 /*!< Mount device busy */
#define EINVAL 22 /*!< Invalid argument */
#ifndef ETIMEDOUT
#define ETIMEDOUT 116 /*!< Connection timed out */
#endif /* ETIMEDOUT */
#define CMD_RESP 0 /*!< Command response */
#define CMD_UNSOL 1 /*!< Unsolicited command */
#define CMD_HANDLER 2 /*!< Command handler */
#define CMD_MAX 3 /*!< Maximum command types */
/* Flags for modem_send_cmd_ext */
#define MODEM_NO_TX_LOCK (1UL << 0U) /*!< No TX lock flag send */
#define MODEM_NO_SET_CMDS (1UL << 1U) /*!< No set commands flag send */
#define MODEM_NO_UNSET_CMDS (1UL << 2U) /*!< No unset commands flag send */
/** @} */
/* Exported types ------------------------------------------------------------*/
/** @defgroup ST67W61_AT_Modem_Cmd_Handler_Types ST67W61 AT Modem Command Handler Types
* @ingroup ST67W61_AT_Modem_Cmd_Handler
* @{
*/
/**
* @brief Modem interface structure
*/
struct modem_iface
{
/** Callback to read data from the modem interface */
int32_t (*mdm_read)(struct modem_iface *iface, uint8_t *buf, size_t size, size_t *bytes_read);
/** Callback to write data to the modem interface */
int32_t (*mdm_write)(struct modem_iface *iface, const uint8_t *buf, size_t size);
};
/**
* @brief Modem command handler structure
*/
struct modem_cmd_handler
{
/** Callback to process commands */
void (*process)(struct modem_cmd_handler *cmd_handler, struct modem_iface *iface);
/** Data based on modem_cmd_handler_data */
void *cmd_handler_data;
};
/**
* @brief Modem context structure
*/
struct modem_context
{
/** Interface configuration */
struct modem_iface iface;
/** Command handler configuration */
struct modem_cmd_handler cmd_handler;
};
/**
* @brief Modem command handler data
*/
struct modem_cmd_handler_data;
/**
* @brief Modem command structure
*/
struct modem_cmd
{
/** Callback to process commands */
int32_t (*func)(struct modem_cmd_handler_data *data, uint16_t len, uint8_t **argv, uint16_t argc);
/** Command string to match */
const char *cmd;
/** Command delimiter */
const char *delim;
/** Command length */
uint16_t cmd_len;
/** Minimum argument count */
uint16_t arg_count_min;
/** Maximum argument count */
uint16_t arg_count_max;
/** Command direct match flag */
bool direct;
};
/**
* @brief Series of modem setup commands to run
*/
struct setup_cmd
{
/** Command to send */
const char *send_cmd;
/** Handle command */
struct modem_cmd handle_cmd;
};
/**
* @brief Modem command handler data
*/
struct modem_cmd_handler_data
{
/** Command list */
const struct modem_cmd *cmds[CMD_MAX];
/** Command list lengths */
size_t cmds_len[CMD_MAX];
/** Buffer for matching commands */
char *match_buf;
/** Length of buffer for matching commands */
size_t match_buf_len;
/** Last error code */
int32_t last_error;
/** End of line string */
const char *eol;
/** Length of end of line string */
size_t eol_len;
/** RX net buffer */
uint8_t *rx_buf;
/** Length of RX net buffer */
size_t rx_buf_len;
/** TX lock */
SemaphoreHandle_t sem_tx_lock;
/** Parse lock */
SemaphoreHandle_t sem_parse_lock;
/** User data */
void *user_data;
};
/**
* @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().
*/
struct modem_cmd_handler_config
{
/** Buffer used for matching commands */
char *match_buf;
/** Length of buffer used for matching commands */
size_t match_buf_len;
/** End of line represented as string */
const char *eol;
/** Length of end of line string */
size_t eol_len;
/** Free to use data which can be retrieved from within command handlers */
void *user_data;
/** Array of response command handlers */
const struct modem_cmd *response_cmds;
/** Length of response command handlers array */
size_t response_cmds_len;
/** Array of unsolicited command handlers */
const struct modem_cmd *unsol_cmds;
/** Length of unsolicited command handlers array */
size_t unsol_cmds_len;
};
/** @} */
/* Exported macros -----------------------------------------------------------*/
/** @defgroup ST67W61_AT_Modem_Cmd_Handler_Macros ST67W61 AT Modem Command Handler Macros
* @ingroup ST67W61_AT_Modem_Cmd_Handler
* @{
*/
#ifndef ARRAY_SIZE
/** Get the number of elements in an array */
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
#endif /* ARRAY_SIZE */
/** Macro to declare a modem command handler function */
#define MODEM_CMD_DECLARE(name_) \
static int32_t name_(struct modem_cmd_handler_data *data, uint16_t len, \
uint8_t **argv, uint16_t argc)
/** Macro to declare a direct modem command handler function */
#define MODEM_CMD_DIRECT_DECLARE(name_) MODEM_CMD_DECLARE(name_)
/** Macro to define a modem command handler function */
#define MODEM_CMD_DEFINE(name_) \
static int32_t name_(struct modem_cmd_handler_data *data, uint16_t len, \
uint8_t **argv, uint16_t argc)
/** Macro to define a direct modem command handler function */
#define MODEM_CMD_DIRECT_DEFINE(name_) MODEM_CMD_DEFINE(name_)
/** Macro to call a direct modem command handler function */
#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, \
}
/** Macro to call a modem command handler function with fixed arguments number */
#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, \
}
/** Macro to call a modem command handler function with variable arguments range */
#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, \
}
/** @} */
/* Exported functions ------------------------------------------------------- */
/** @defgroup ST67W61_AT_Modem_Cmd_Handler_Functions ST67W61 AT Modem Command Handler Functions
* @ingroup ST67W61_AT_Modem_Cmd_Handler
* @{
*/
/**
* @brief get the last error code
* @param data: command handler data reference
* @return last handled error
*/
int32_t 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
* @return 0 if ok, < 0 if error
*/
int32_t modem_cmd_handler_set_error(struct modem_cmd_handler_data *data, int32_t 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
* @return 0 if ok, < 0 if error
*/
int32_t 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 Wait until semaphore is given
* @details This function does the same wait behavior as @ref modem_cmd_send_ext, but can wait without sending
* any command first. Useful for waiting for asynchronous responses.
* @param data: handler data to use
* @param sem: wait for semaphore
* @param timeout: wait timeout
* @return 0 if ok, < 0 if error
*/
int32_t modem_cmd_handler_await(struct modem_cmd_handler_data *data,
SemaphoreHandle_t sem, TickType_t timeout);
/**
* @brief send data directly to interface w/o TX lock
* @details This function just writes directly to the modem interface.
* Recommended to use to get verbose logging for all data sent to the interface.
* @param iface: interface to use
* @param buf: send buffer (not NULL terminated)
* @param len: length of send buffer
* @return 0 if ok, < 0 if error
*/
int32_t modem_cmd_send_data_nolock(struct modem_iface *iface,
const uint8_t *buf, size_t len);
/**
* @brief send AT command to interface with behavior defined by flags
* @details This function is similar to @ref modem_cmd_send_ext, 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
* @return 0 if ok, < 0 if error
*/
int32_t 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,
SemaphoreHandle_t sem, TickType_t timeout, uint32_t 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
* @return 0 if ok, < 0 if error
*/
static inline int32_t 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, SemaphoreHandle_t sem,
TickType_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
* @return 0 if ok, < 0 if error
*/
static inline int32_t 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,
SemaphoreHandle_t sem, TickType_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
* @return 0 if ok, < 0 if error
*/
int32_t modem_cmd_handler_setup_cmds(struct modem_iface *iface,
struct modem_cmd_handler *handler,
const struct setup_cmd *cmds, size_t cmds_len,
SemaphoreHandle_t sem, TickType_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
* @return 0 if ok, < 0 if error
*/
int32_t modem_cmd_handler_setup_cmds_nolock(struct modem_iface *iface,
struct modem_cmd_handler *handler,
const struct setup_cmd *cmds,
size_t cmds_len, SemaphoreHandle_t sem,
TickType_t timeout);
/**
* @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 config: Command handler configuration
* @return -EINVAL if any argument is invalid
* @return 0 if successful
*/
int32_t 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
* @details 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
* @return 0 if ok, < 0 if error
*/
int32_t modem_cmd_handler_tx_lock(struct modem_cmd_handler *handler,
TickType_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_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 which 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 /* MODEM_MODEM_CMD_HANDLER_H */
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,945 @@
/**
******************************************************************************
* @file w61_at_common.c
* @author ST67 Application Team
* @brief This file provides the common implementations of the AT driver
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "w61_at_common.h"
#include "w61_at_internal.h"
#include "w61_io.h"
#include <stdlib.h>
#include "modem_cmd_handler.h"
#include "stdio.h"
#if (defined(SYS_DBG_ENABLE_TA4) && (SYS_DBG_ENABLE_TA4 >= 1))
#include "trcRecorder.h"
#endif /* SYS_DBG_ENABLE_TA4 */
/* Global variables ----------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private defines -----------------------------------------------------------*/
/** @defgroup ST67W61_AT_Common_Defines ST67W61 AT Driver Common Defines
* @ingroup ST67W61_AT_Common
* @{
*/
/** Timeout for io send operation */
#define IO_SEND_TIMEOUT 2000U
/** Timeout for io receive operation */
#define IO_RECEIVE_TIMEOUT portMAX_DELAY
#ifndef IO_AT_CMDQ_DEPTH
/** IO queue depth for AT CMD */
#define IO_AT_CMDQ_DEPTH 16
#endif /* IO_AT_CMDQ_DEPTH */
/** @} */
/* Private macros ------------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/** @addtogroup ST67W61_AT_Common_Functions
* @{
*/
/**
* @brief Initialize the IO interface
* @param iface: pointer to the modem interface
* @return 0 on success, negative value on error
*/
static int32_t io_init(struct modem_iface *iface);
/**
* @brief Deinitialize the IO interface
* @param iface: pointer to the modem interface
* @return 0 on success, negative value on error
*/
static int32_t io_deinit(struct modem_iface *iface);
/**
* @brief Modem process task
* @param arg: pointer to the task argument
*/
static void W61_Modem_Process_task(void *arg);
/**
* @brief Write data to the modem interface
* @param iface: pointer to the modem interface
* @param buf: pointer to the data buffer
* @param size: size of the data to write
* @return 0 on success, negative value on error
*/
static int32_t modem_iface_spi_write(struct modem_iface *iface,
const uint8_t *buf, size_t size);
/**
* @brief Read data from the modem interface
* @param iface: pointer to the modem interface
* @param buf: pointer to the buffer to store read data
* @param size: size of the buffer
* @param bytes_read: pointer to store the number of bytes read
* @return 0 on success, negative value on error
*/
static int32_t modem_iface_spi_read(struct modem_iface *iface,
uint8_t *buf, size_t size, size_t *bytes_read);
/**
* @brief Callback function to handle query events
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings
* @param argc: number of argument
* @return 0 on success, negative value on error
*/
MODEM_CMD_DECLARE(on_cmd_query);
/**
* @brief Callback function to handle "OK" events
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings (unused)
* @param argc: number of arguments (unused)
* @return 0 on success, negative value on error
*/
MODEM_CMD_DECLARE(on_cmd_ok);
/**
* @brief Callback function to handle "ERROR" events
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings (unused)
* @param argc: number of arguments (unused)
* @return 0 on success, negative value on error
*/
MODEM_CMD_DECLARE(on_cmd_error);
/**
* @brief Callback function to handle "ready" events
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings (unused)
* @param argc: number of arguments (unused)
* @return 0 on success, negative value on error
*/
MODEM_CMD_DECLARE(on_cmd_ready);
/**
* @brief Callback function to handle "+CW" events
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings
* @param argc: number of arguments
* @return process length on success, negative value on error
*/
MODEM_CMD_DECLARE(on_cmd_wifi_event);
/**
* @brief Callback function to handle "+BLE" events
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings
* @param argc: number of arguments
* @return process length on success, negative value on error
*/
MODEM_CMD_DECLARE(on_cmd_ble_event);
/**
* @brief Callback function to handle "+CIP" events
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings (unused)
* @param argc: number of arguments (unused)
* @return process length on success, negative value on error
*/
MODEM_CMD_DECLARE(on_cmd_net_event);
/**
* @brief Callback function to handle "+MQTT" events
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings
* @param argc: number of arguments
* @return process length on success, negative value on error
*/
MODEM_CMD_DECLARE(on_cmd_mqtt_event);
/**
* @brief Callback function to handle ">" events
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings (unused)
* @param argc: number of arguments (unused)
* @return 0 on success, negative value on error
*/
MODEM_CMD_DIRECT_DECLARE(on_cmd_tx_ready);
/**
* @brief Callback function to handle "RECV " events
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings (unused)
* @param argc: number of arguments (unused)
* @return 0 on success, negative value on error
*/
MODEM_CMD_DECLARE(on_cmd_recv);
/**
* @brief Callback function to handle MQTT data events
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings (unused)
* @param argc: number of arguments (unused)
* @return process length on success, negative value on error
*/
MODEM_CMD_DIRECT_DECLARE(on_cmd_mqtt_data_event);
/**
* @brief Callback function to handle Net data events
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings
* @param argc: number of arguments
* @return process length on success, negative value on error
*/
MODEM_CMD_DECLARE(on_cmd_net_data_event);
/**
* @brief Callback function to handle BLE Write data events
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings (unused)
* @param argc: number of arguments (unused)
* @return process length on success, negative value on error
*/
MODEM_CMD_DIRECT_DECLARE(on_cmd_ble_write_data_event);
/**
* @brief Callback function to handle BLE Read data events
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings (unused)
* @param argc: number of arguments (unused)
* @return process length on success, negative value on error
*/
MODEM_CMD_DIRECT_DECLARE(on_cmd_ble_read_data_event);
/**
* @brief Callback function to handle BLE Notification data events
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings (unused)
* @param argc: number of arguments (unused)
* @return process length on success, negative value on error
*/
MODEM_CMD_DIRECT_DECLARE(on_cmd_ble_noti_data_event);
/**
* @brief Callback function to handle ping events
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings (unused)
* @param argc: number of arguments (unused)
* @return process length on success, negative value on error
*/
MODEM_CMD_DECLARE(on_cmd_net_ping_event);
/**
* @brief Callback function to handle Wi-Fi scan events
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings (unused)
* @param argc: number of arguments (unused)
* @return process length on success, negative value on error
*/
MODEM_CMD_DECLARE(on_cmd_wifi_scan_event);
/** @} */
/* Private variables ---------------------------------------------------------*/
/** @defgroup ST67W61_AT_Common_Variables ST67W61 AT Driver Common Variables
* @ingroup ST67W61_AT_Common
* @{
*/
/** List of response commands for the modem */
static const struct modem_cmd response_cmds_list[] =
{
MODEM_CMD("OK", on_cmd_ok, 0U, ""),
MODEM_CMD("ERROR", on_cmd_error, 0U, ""),
};
/** List of unsolicited commands for the modem */
static const struct modem_cmd unsol_cmds_list[] =
{
MODEM_CMD("ready", on_cmd_ready, 0U, ""),
MODEM_CMD("+CWLAP:", on_cmd_wifi_scan_event, 8U, ","),
MODEM_CMD_DIRECT("+BLE:GATTWRITE:", on_cmd_ble_write_data_event),
MODEM_CMD_DIRECT("+BLE:GATTREAD:", on_cmd_ble_read_data_event),
MODEM_CMD_DIRECT("+BLE:NOTIDATA:", on_cmd_ble_noti_data_event),
MODEM_CMD_DIRECT("+MQTT:SUBRECV:", on_cmd_mqtt_data_event),
MODEM_CMD_ARGS_MAX("+IPD:", on_cmd_net_data_event, 1U, 10U, ","),
MODEM_CMD_ARGS_MAX("+CW:", on_cmd_wifi_event, 1U, 10U, ", "),
MODEM_CMD_ARGS_MAX("+BLE:", on_cmd_ble_event, 1U, 11U, ",:()"),
MODEM_CMD_ARGS_MAX("+CIP:", on_cmd_net_event, 1U, 10U, ","),
MODEM_CMD_ARGS_MAX("+MQTT:", on_cmd_mqtt_event, 1U, 10U, ","),
MODEM_CMD("+PING:", on_cmd_net_ping_event, 1U, ""),
};
/** @} */
/* Functions Definition ------------------------------------------------------*/
/** @addtogroup ST67W61_AT_Common_Functions
* @{
*/
int32_t W61_AT_ModemInit(W61_Object_t *Obj)
{
BaseType_t xReturned;
int32_t ret = -1;
struct modem *mdm = (struct modem *) &Obj->Modem;
/* Cmd handler */
const struct modem_cmd_handler_config cmd_handler_config =
{
.match_buf = (char *) &mdm->cmd_match_buf[0],
.match_buf_len = sizeof(mdm->cmd_match_buf),
.eol = "", /* CRLF sent within the command string to avoid 2 successive write */
.user_data = mdm,
.response_cmds = response_cmds_list,
.response_cmds_len = ARRAY_SIZE(response_cmds_list),
.unsol_cmds = unsol_cmds_list,
.unsol_cmds_len = ARRAY_SIZE(unsol_cmds_list),
};
mdm->sem_response = xSemaphoreCreateBinary();
if (mdm->sem_response == NULL)
{
goto __err;
}
mdm->sem_if_ready = xSemaphoreCreateBinary();
if (mdm->sem_if_ready == NULL)
{
goto __err;
}
mdm->sem_tx_ready = xSemaphoreCreateBinary();
if (mdm->sem_tx_ready == NULL)
{
goto __err;
}
/* Assign rx_buff */
/** Linear RX buffer to process */
mdm->handler_data.rx_buf = pvPortMalloc(RX_BUF_SIZE);
if (mdm->handler_data.rx_buf == NULL)
{
goto __err;
}
ret = modem_cmd_handler_init(&mdm->handler, &mdm->handler_data,
&cmd_handler_config);
if (ret < 0)
{
goto __err;
}
ret = io_init(&mdm->iface);
if (ret < 0)
{
goto __err;
}
xReturned = xTaskCreate(W61_Modem_Process_task,
(char *)"Modem_Process",
W61_MDM_RX_TASK_STACK_SIZE_BYTES >> 2U,
mdm,
W61_MDM_RX_TASK_PRIO,
&mdm->modem_task_handle);
if (xReturned != pdPASS)
{
SYS_LOG_ERROR("xTaskCreate failed to create\n");
ret = -1;
goto __err;
}
/* Wait for the module to be ready */
if (W61_WaitForReady(Obj, W61_READY_DEFAULT_TIMEOUT_MS) != W61_STATUS_OK)
{
SYS_LOG_ERROR("sem_if_ready not received\n");
ret = -1;
goto __err;
}
(void)W61_AT_Common_SetExecute(Obj, (uint8_t *)"AT\r\n", W61_NCP_TIMEOUT);
return ret;
__err:
if (mdm->handler_data.rx_buf != NULL)
{
vPortFree(mdm->handler_data.rx_buf);
mdm->handler_data.rx_buf = NULL;
}
if (mdm->sem_response != NULL)
{
vSemaphoreDelete(mdm->sem_response);
mdm->sem_response = NULL;
}
if (mdm->sem_if_ready != NULL)
{
vSemaphoreDelete(mdm->sem_if_ready);
mdm->sem_if_ready = NULL;
}
if (mdm->sem_tx_ready != NULL)
{
vSemaphoreDelete(mdm->sem_tx_ready);
mdm->sem_tx_ready = NULL;
}
if (mdm->modem_task_handle != NULL)
{
vTaskDelete(mdm->modem_task_handle);
mdm->modem_task_handle = NULL;
}
return ret;
}
void W61_AT_ModemDeInit(W61_Object_t *Obj)
{
struct modem *mdm = (struct modem *) &Obj->Modem;
(void)io_deinit(&mdm->iface);
if (mdm->handler_data.rx_buf != NULL)
{
vPortFree(mdm->handler_data.rx_buf);
mdm->handler_data.rx_buf = NULL;
}
if (mdm->sem_response != NULL)
{
vSemaphoreDelete(mdm->sem_response);
mdm->sem_response = NULL;
}
if (mdm->sem_if_ready != NULL)
{
vSemaphoreDelete(mdm->sem_if_ready);
mdm->sem_if_ready = NULL;
}
if (mdm->sem_tx_ready != NULL)
{
vSemaphoreDelete(mdm->sem_tx_ready);
mdm->sem_tx_ready = NULL;
}
if (mdm->modem_task_handle != NULL)
{
vTaskDelete(mdm->modem_task_handle);
mdm->modem_task_handle = NULL;
}
}
W61_Status_t W61_Status(int32_t ret)
{
W61_Status_t status;
switch (ret)
{
case 0:
status = W61_STATUS_OK;
break;
case -ETIMEDOUT:
status = W61_STATUS_TIMEOUT;
break;
case -EIO:
status = W61_STATUS_IO_ERROR;
break;
default:
status = W61_STATUS_ERROR;
break;
}
return status;
}
W61_Status_t W61_AT_Common_SetExecute(W61_Object_t *Obj, uint8_t *p_cmd, uint32_t timeout_ms)
{
struct modem *mdm = &Obj->Modem;
return W61_Status(modem_cmd_send(&mdm->iface,
&mdm->handler,
NULL,
0,
p_cmd,
mdm->sem_response,
timeout_ms));
}
W61_Status_t W61_AT_Common_Query_Parse(W61_Object_t *Obj, char *p_cmd, char *p_resp,
uint16_t *argc, char **argv, uint32_t timeout_ms)
{
struct modem *mdm = (struct modem *) &Obj->Modem;
struct modem_cmd_handler_data *data = (struct modem_cmd_handler_data *)mdm->handler.cmd_handler_data;
W61_Status_t ret;
if (data == NULL)
{
return W61_STATUS_ERROR;
}
(void)xSemaphoreTake(data->sem_tx_lock, portMAX_DELAY);
/* **argv reference p_cmd to ensure re-entrance */
mdm->rx_data = p_cmd;
mdm->argc = argc;
mdm->argv = argv;
struct modem_cmd handlers[] = {{
.cmd = p_resp,
.cmd_len = (uint16_t)strlen(p_resp),
.func = on_cmd_query,
.arg_count_min = 1,
.arg_count_max = CONFIG_MODEM_CMD_HANDLER_MAX_PARAM_COUNT,
.delim = ",:",
.direct = false,
}
};
ret = W61_Status(modem_cmd_send_ext(&mdm->iface,
&mdm->handler,
handlers,
ARRAY_SIZE(handlers),
(const uint8_t *)p_cmd,
mdm->sem_response,
timeout_ms,
MODEM_NO_TX_LOCK));
(void)xSemaphoreGive(data->sem_tx_lock);
return ret;
}
W61_Status_t W61_AT_Common_RequestSendData(W61_Object_t *Obj, uint8_t *p_cmd, uint8_t *pdata, uint32_t len,
uint32_t timeout_ms, bool check_resp)
{
struct modem *mdm = (struct modem *) &Obj->Modem;
int32_t ret;
int32_t bytes_consumed_by_the_bus = 0;
int32_t bytes_to_send;
static const struct modem_cmd cmds[] =
{
MODEM_CMD_DIRECT(">", on_cmd_tx_ready),
MODEM_CMD("Recv ", on_cmd_recv, 1U, " "),
};
(void)xSemaphoreTake(mdm->handler_data.sem_tx_lock, portMAX_DELAY);
/*reset mdm->sem_tx_read */
(void)xSemaphoreTake(mdm->sem_tx_ready, 0);
ret = modem_cmd_send_ext(&mdm->iface, &mdm->handler,
cmds, ARRAY_SIZE(cmds), p_cmd, mdm->sem_response,
check_resp ? pdMS_TO_TICKS(timeout_ms) : 0U, /* If check_resp is false don't wait for OK */
MODEM_NO_TX_LOCK | MODEM_NO_UNSET_CMDS);
if (ret < 0)
{
SYS_LOG_DEBUG("Failed to send command\n");
goto out;
}
/* Reset semaphore that will be released by "Recv " */
/*reset mdm->sem_response */
(void)xSemaphoreTake(mdm->sem_response, 0);
/* Set rx_data_len to be checked during "Recv " event */
mdm->rx_data_len = len;
/* Wait for '>' */
if (xSemaphoreTake(mdm->sem_tx_ready, pdMS_TO_TICKS(5000)) != pdPASS)
{
SYS_LOG_DEBUG("Timeout waiting for tx\n");
ret = -ETIMEDOUT;
goto out;
}
while (bytes_consumed_by_the_bus < len)
{
bytes_to_send = len - bytes_consumed_by_the_bus;
bytes_consumed_by_the_bus += modem_cmd_send_data_nolock(&mdm->iface,
&pdata[bytes_consumed_by_the_bus],
bytes_to_send);
}
/* Wait for "Recv " */
if (xSemaphoreTake(mdm->sem_response, pdMS_TO_TICKS(timeout_ms)) != pdPASS)
{
SYS_LOG_DEBUG("No send response\n");
ret = -ETIMEDOUT;
goto out;
}
ret = modem_cmd_handler_get_error(&mdm->handler_data);
if (ret != 0)
{
SYS_LOG_DEBUG("Failed to send data\n");
}
out:
(void)modem_cmd_handler_update_cmds(&mdm->handler_data,
NULL, 0U, false);
(void)xSemaphoreGive(mdm->handler_data.sem_tx_lock);
return W61_Status(ret);
}
void W61_AT_RemoveStrQuotes(char *inbuf)
{
int32_t len = strlen(inbuf);
if (len < 2U)
{
return; /* Nothing to do */
}
inbuf[len - 1U] = '\0'; /* Ensure the last character is null-terminated */
/* Ensure the first character is not a double quote */
(void)memmove(inbuf, inbuf + 1, len);
}
void W61_AT_Logger(uint8_t *pBuf, uint32_t len, char *inOut)
{
char log_message[W61_MAX_AT_LOG_LENGTH];
uint32_t message_len = W61_MAX_AT_LOG_LENGTH - 1U;
if (len < (W61_MAX_AT_LOG_LENGTH - 1U))
{
message_len = len;
}
(void)memcpy(log_message, pBuf, message_len);
log_message[message_len] = '\0';
if (message_len == (W61_MAX_AT_LOG_LENGTH - 1U))
{
log_message[message_len - 1U] = '.';
log_message[message_len - 2U] = '.';
log_message[message_len - 3U] = '.';
}
SYS_LOG_DEBUG("AT%s %s\n", inOut, log_message);
}
#if defined(__ICCARM__) || defined(__ICCRX__) || defined(__ARMCC_VERSION) /* For IAR/MDK Compiler */
char *strnstr(const char *big, const char *little, size_t len)
{
size_t i;
size_t j;
if (little[0] == '\0')
{
return ((char *)big);
}
j = 0;
while ((j < len) && (big[j] != '\0'))
{
i = 0;
while ((j < len) && (little[i] != '\0') && (big[j] != '\0') && (little[i] == big[j]))
{
++i;
++j;
}
if (little[i] == '\0')
{
return ((char *)&big[j - i]);
}
j = j - i + 1U;
}
return NULL;
}
#endif /* __ICCARM__ || __ARMCC_VERSION */
/* Private Functions Definition ----------------------------------------------*/
static int32_t io_init(struct modem_iface *iface)
{
if (iface == NULL)
{
return -EINVAL;
}
if (BusIo_SPI_Init() != 0)
{
return -1;
}
if (BusIo_SPI_Bind(SPI_MSG_CTRL_TRAFFIC_AT_CMD, (int32_t)IO_AT_CMDQ_DEPTH, NULL) != 0)
{
return -1;
}
iface->mdm_read = modem_iface_spi_read;
iface->mdm_write = modem_iface_spi_write;
return 0;
}
static int32_t io_deinit(struct modem_iface *iface)
{
if (iface == NULL)
{
return -EINVAL;
}
if (BusIo_SPI_DeInit() != 0)
{
return -1;
}
iface->mdm_read = NULL;
iface->mdm_write = NULL;
return 0;
}
static void W61_Modem_Process_task(void *arg)
{
struct modem *mdm = (struct modem *) arg;
while (true)
{
modem_cmd_handler_process(&mdm->handler,
&mdm->iface);
}
}
static int32_t modem_iface_spi_write(struct modem_iface *iface,
const uint8_t *buf, size_t size)
{
if (size == 0U)
{
return 0;
}
AT_LOG_HOST_OUT((uint8_t *)buf, size);
return BusIo_SPI_SendData(SPI_MSG_CTRL_TRAFFIC_AT_CMD, (uint8_t *)buf, size, IO_SEND_TIMEOUT);
}
static int32_t modem_iface_spi_read(struct modem_iface *iface,
uint8_t *buf, size_t size, size_t *bytes_read)
{
int32_t received = BusIo_SPI_ReceiveData(SPI_MSG_CTRL_TRAFFIC_AT_CMD, buf, size, portMAX_DELAY);
AT_LOG_HOST_IN(buf, received);
if (received < 0)
{
*bytes_read = 0;
return received;
}
*bytes_read = received;
return 0;
}
MODEM_CMD_DEFINE(on_cmd_query)
{
struct modem *mdm = (struct modem *) data->user_data;
int32_t offset = ((uint8_t *) mdm->rx_data) - mdm->cmd_match_buf;
/*len of the current mdm->cmd_match_buf + '\0'*/
int32_t mlen = (argv[argc - 1] - mdm->cmd_match_buf) + strlen((char *) argv[argc - 1]) + 1;
(void)memcpy(mdm->rx_data, (char *) mdm->cmd_match_buf, mlen);
/* Record and offset argv to mdm->rx_data */
for (int32_t i = 0; i < argc; i++)
{
mdm->argv[i] = (char *)(argv[i] + offset);
}
*mdm->argc = argc;
return 0;
}
MODEM_CMD_DIRECT_DEFINE(on_cmd_tx_ready)
{
struct modem *mdm = (struct modem *) data->user_data;
(void)xSemaphoreGive(mdm->sem_tx_ready);
return len;
}
MODEM_CMD_DEFINE(on_cmd_recv)
{
struct modem *mdm = (struct modem *) data->user_data;
int32_t recv_len = 0;
if (argc > 0U)
{
recv_len = atoi((char *) argv[0]);
/*check if length received by modem is matching the sent value */
if (recv_len != mdm->rx_data_len)
{
(void)modem_cmd_handler_set_error(data, -EIO);
}
else
{
(void)modem_cmd_handler_set_error(data, 0);
}
}
else
{
(void)modem_cmd_handler_set_error(data, -EIO);
}
(void)xSemaphoreGive(mdm->sem_response);
return 0;
}
/* Handler: OK */
MODEM_CMD_DEFINE(on_cmd_ok)
{
struct modem *mdm = (struct modem *) data->user_data;
(void)modem_cmd_handler_set_error(data, 0);
(void)xSemaphoreGive(mdm->sem_response);
return 0;
}
/* Handler: ERROR */
MODEM_CMD_DEFINE(on_cmd_error)
{
struct modem *mdm = (struct modem *) data->user_data;
(void)modem_cmd_handler_set_error(data, -EIO);
(void)xSemaphoreGive(mdm->sem_response);
return 0;
}
MODEM_CMD_DEFINE(on_cmd_ready)
{
struct modem *mdm = (struct modem *) data->user_data;
(void)xSemaphoreGive(mdm->sem_if_ready);
return 0;
}
MODEM_CMD_DEFINE(on_cmd_wifi_event)
{
struct modem *mdm = (struct modem *) data->user_data;
W61_Object_t *Obj = CONTAINER_OF(mdm, W61_Object_t, Modem);
if (Obj->Callbacks.WiFi_event_cb != NULL)
{
Obj->Callbacks.WiFi_event_cb(Obj, &argc, (char **)argv);
}
return 0;
}
MODEM_CMD_DEFINE(on_cmd_wifi_scan_event)
{
struct modem *mdm = (struct modem *) data->user_data;
W61_Object_t *Obj = CONTAINER_OF(mdm, W61_Object_t, Modem);
if (Obj->Callbacks.WiFi_event_scan_cb != NULL)
{
Obj->Callbacks.WiFi_event_scan_cb(Obj, &argc, (char **)argv);
}
return 0;
}
MODEM_CMD_DEFINE(on_cmd_ble_event)
{
struct modem *mdm = (struct modem *) data->user_data;
W61_Object_t *Obj = CONTAINER_OF(mdm, W61_Object_t, Modem);
if (Obj->Callbacks.Ble_event_cb != NULL)
{
Obj->Callbacks.Ble_event_cb(Obj, &argc, (char **)argv);
}
return 0;
}
MODEM_CMD_DIRECT_DEFINE(on_cmd_ble_write_data_event)
{
struct modem *mdm = (struct modem *) data->user_data;
W61_Object_t *Obj = CONTAINER_OF(mdm, W61_Object_t, Modem);
if (Obj->Callbacks.Ble_event_data_cb != NULL)
{
return Obj->Callbacks.Ble_event_data_cb(W61_BLE_EVT_WRITE_ID, data, len);
}
return 0;
}
MODEM_CMD_DIRECT_DEFINE(on_cmd_ble_read_data_event)
{
struct modem *mdm = (struct modem *) data->user_data;
W61_Object_t *Obj = CONTAINER_OF(mdm, W61_Object_t, Modem);
if (Obj->Callbacks.Ble_event_data_cb != NULL)
{
return Obj->Callbacks.Ble_event_data_cb(W61_BLE_EVT_READ_ID, data, len);
}
return 0;
}
MODEM_CMD_DIRECT_DEFINE(on_cmd_ble_noti_data_event)
{
struct modem *mdm = (struct modem *) data->user_data;
W61_Object_t *Obj = CONTAINER_OF(mdm, W61_Object_t, Modem);
if (Obj->Callbacks.Ble_event_data_cb != NULL)
{
return Obj->Callbacks.Ble_event_data_cb(W61_BLE_EVT_NOTIFICATION_DATA_ID, data, len);
}
return 0;
}
MODEM_CMD_DEFINE(on_cmd_net_event)
{
struct modem *mdm = (struct modem *) data->user_data;
W61_Object_t *Obj = CONTAINER_OF(mdm, W61_Object_t, Modem);
if (Obj->Callbacks.Net_event_cb != NULL)
{
Obj->Callbacks.Net_event_cb(Obj, &argc, (char **)argv);
}
return 0;
}
MODEM_CMD_DEFINE(on_cmd_net_ping_event)
{
struct modem *mdm = (struct modem *) data->user_data;
W61_Object_t *Obj = CONTAINER_OF(mdm, W61_Object_t, Modem);
if (Obj->Callbacks.Net_event_ping_cb != NULL)
{
Obj->Callbacks.Net_event_ping_cb(Obj, &argc, (char **)argv);
}
return 0;
}
MODEM_CMD_DEFINE(on_cmd_net_data_event)
{
struct modem *mdm = (struct modem *) data->user_data;
W61_Object_t *Obj = CONTAINER_OF(mdm, W61_Object_t, Modem);
if (Obj->Callbacks.Net_event_data_cb != NULL)
{
Obj->Callbacks.Net_event_data_cb(Obj, &argc, (char **)argv);
}
return 0;
}
MODEM_CMD_DEFINE(on_cmd_mqtt_event)
{
struct modem *mdm = (struct modem *) data->user_data;
W61_Object_t *Obj = CONTAINER_OF(mdm, W61_Object_t, Modem);
if (Obj->Callbacks.MQTT_event_cb != NULL)
{
Obj->Callbacks.MQTT_event_cb(Obj, &argc, (char **)argv);
}
return 0;
}
MODEM_CMD_DIRECT_DEFINE(on_cmd_mqtt_data_event)
{
struct modem *mdm = (struct modem *) data->user_data;
W61_Object_t *Obj = CONTAINER_OF(mdm, W61_Object_t, Modem);
if (Obj->Callbacks.MQTT_event_data_cb != NULL)
{
return Obj->Callbacks.MQTT_event_data_cb(0, data, len);
}
return 0;
}
/** @} */
@@ -0,0 +1,215 @@
/**
******************************************************************************
* @file w61_at_common.h
* @author ST67 Application Team
* @brief This file provides the common definitions of the AT driver
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef W61_AT_COMMON_H
#define W61_AT_COMMON_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Includes ------------------------------------------------------------------*/
#include "w61_at_api.h"
/** @defgroup ST67W61_AT_Common ST67W61 AT Driver Common
* @ingroup ST67W61_AT
*/
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/** @defgroup ST67W61_AT_Common_Constants ST67W61 AT Driver Common Constants
* @ingroup ST67W61_AT_Common
* @{
*/
#ifndef W61_CMDRSP_STRING_SIZE
/** W61 AT command response string size */
#define W61_CMDRSP_STRING_SIZE 192U
#endif /* W61_CMDRSP_STRING_SIZE */
#ifndef W61_MDM_RX_TASK_STACK_SIZE_BYTES
/** Stack required especially for Log messages */
#define W61_MDM_RX_TASK_STACK_SIZE_BYTES 2048U
#endif /* W61_MDM_RX_TASK_STACK_SIZE_BYTES */
#ifndef W61_MDM_RX_TASK_PRIO
/** W61 W61_Modem_Process task priority, recommended to be higher than application tasks */
#define W61_MDM_RX_TASK_PRIO 54
#endif /* W61_MDM_RX_TASK_PRIO */
#ifndef W61_MAX_AT_LOG_LENGTH
/** Maximum size of AT log */
#define W61_MAX_AT_LOG_LENGTH 300U
#endif /* W61_MAX_AT_LOG_LENGTH */
/** Cmd/query default timeouts */
#ifndef W61_NCP_TIMEOUT
/** Timeout for reply/execute of NCP */
#define W61_NCP_TIMEOUT 2000U
#endif /* W61_NCP_TIMEOUT */
#ifndef W61_SYS_TIMEOUT
/** Timeout for special cases like flash write, firmware updates, etc */
#define W61_SYS_TIMEOUT 2000U
#endif /* W61_SYS_TIMEOUT */
#ifndef W61_WIFI_TIMEOUT
/** Timeout for remote WIFI device operation (e.g. AP) */
#define W61_WIFI_TIMEOUT 6000U
#endif /* W61_WIFI_TIMEOUT */
#ifndef W61_NET_TIMEOUT
/** Timeout for remote network operation (e.g. server) */
#define W61_NET_TIMEOUT 6000U
#endif /* W61_NET_TIMEOUT */
#ifndef W61_BLE_TIMEOUT
/** Timeout for remote BLE device operation */
#define W61_BLE_TIMEOUT 2000U
#endif /* W61_BLE_TIMEOUT */
/** @} */
/* Exported variables --------------------------------------------------------*/
/* Exported macros -----------------------------------------------------------*/
/** @defgroup ST67W61_AT_Common_Macros ST67W61 AT Driver Common Macros
* @ingroup ST67W61_AT_Common
* @{
*/
#ifndef CONTAINER_OF
/** Return the pointer to the structure containing the specified member */
#define CONTAINER_OF(ptr, type, field) \
({ \
((type *)(((char *)(ptr)) - offsetof(type, field)));\
})
#endif
/**
* \def AT_LOG_HOST_OUT(pBuf, len)
* AT log trace to announce an AT command sent to the module
*/
/**
* \def AT_LOG_HOST_IN(pBuf, len)
* AT log trace to announce an AT message received from the module
*/
#if (W61_AT_LOG_ENABLE == 1)
#define AT_LOG_HOST_OUT(pBuf, len) W61_AT_Logger( pBuf, len, ">")
#define AT_LOG_HOST_IN(pBuf, len) W61_AT_Logger( pBuf, len, "<")
#else
#define AT_LOG_HOST_OUT(pBuf, len)
#define AT_LOG_HOST_IN(pBuf, len)
#endif /* W61_AT_LOG_ENABLE */
/** @} */
/* Exported functions ------------------------------------------------------- */
/** @defgroup ST67W61_AT_Common_Functions ST67W61 AT Driver Common Functions
* @ingroup ST67W61_AT_Common
* @{
*/
/**
* @brief Initialize the W61 AT modem
* @param Obj: pointer to module handle
* @return Operation status
*/
int32_t W61_AT_ModemInit(W61_Object_t *Obj);
/**
* @brief Deinitialize the W61 AT modem
* @param Obj: pointer to module handle
*/
void W61_AT_ModemDeInit(W61_Object_t *Obj);
/**
* @brief Translate modem_cmd status into W61_Status_t
* @param ret status
* @return Operation status W61_Status_t
*/
W61_Status_t W61_Status(int32_t ret);
/**
* @brief Send the AT command for Set and Execute mode, and check the status response
* @param Obj: pointer to module handle
* @param p_cmd: pointer to pass command string
* @param timeout_ms: timeout for the W61_SetExecute
* @return Operation status
*/
W61_Status_t W61_AT_Common_SetExecute(W61_Object_t *Obj, uint8_t *p_cmd, uint32_t timeout_ms);
/**
* @brief Send the AT command for Queries, wait Query response and check the status response
* @param Obj: pointer to module handle
* @param p_cmd: pointer to pass command string. Reused as rx buffer to store the argv content
* @param p_resp: pointer to pass expected response string. \
* Must be provided as buffer allocated locally to ensure re-entrance
* @param argc: pointer to number of found argument
* @param argv: pointer to argument table
* @param timeout_ms: timeout for the first W61_AT_Common_Query_Parse
* @return Operation status
*/
W61_Status_t W61_AT_Common_Query_Parse(W61_Object_t *Obj, char *p_cmd, char *p_resp,
uint16_t *argc, char **argv, uint32_t timeout_ms);
/**
* @brief Send the AT command for Set and Execute mode and send data
* @param Obj: pointer to module handle
* @param p_cmd: pointer to pass command string
* @param pdata: pointer to data to send
* @param len: binary data length
* @param timeout_ms: timeout
* @param check_resp: if true, check the response status
* @return Operation status
*/
W61_Status_t W61_AT_Common_RequestSendData(W61_Object_t *Obj, uint8_t *p_cmd, uint8_t *pdata, uint32_t len,
uint32_t timeout_ms, bool check_resp);
/**
* @brief Remove the double quotes at the beginning and the end of the string
* @param inbuf: input string
*/
void W61_AT_RemoveStrQuotes(char *inbuf);
/**
* @brief Log AT command
* @param pBuf: pointer to buffer
* @param len: buffer length
* @param inOut: pointer to string
*/
void W61_AT_Logger(uint8_t *pBuf, uint32_t len, char *inOut);
#if defined(__ICCARM__) || defined(__ICCRX__) || defined(__ARMCC_VERSION) /* For IAR/MDK Compiler */
/**
* @brief Function locates the first occurrence of the null-terminated string little in the string big
*/
char *strnstr(const char *big, const char *little, size_t len);
#endif /* __ICCARM__ || __ARMCC_VERSION */
/** @} */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* W61_AT_COMMON_H */
@@ -0,0 +1,78 @@
/**
******************************************************************************
* @file w61_at_internal.h
* @author ST67 Application Team
* @brief This file provides the internal definitions of the AT driver
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef W61_AT_INTERNAL_H
#define W61_AT_INTERNAL_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Includes ------------------------------------------------------------------*/
#include <string.h>
#include "w61_default_config.h"
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Exported variables --------------------------------------------------------*/
/* Exported macros -----------------------------------------------------------*/
/** @addtogroup ST67W61_AT_Common_Macros
* @{
*/
/**
* \def W61_ASSERT(p)
* Macro to check if the expression is true and call the assert function if it is false
*/
/**
* \def W61_NULL_ASSERT(p)
* Macro to check if the pointer is NULL and return the error code
*/
/**
* \def W61_NULL_ASSERT_VOID(p)
* Macro to check if the pointer is NULL does not return the error code
*/
/**
* \def W61_NULL_ASSERT_STR(p, s)
* Macro to check if the pointer is NULL and return the error code with error string log
*/
#if (W61_ASSERT_ENABLE == 1)
/** Macro to check if the pointer is NULL and return the error code with error string log */
#define W61_ASSERT(expr) if (!(expr)) { LogError("Assert failed: file: %s, line: %d", __FILE__, __LINE__); while(1); }
#define W61_NULL_ASSERT(p) if ((p) == NULL) { return W61_STATUS_ERROR; }
#define W61_NULL_ASSERT_VOID(p) if ((p) == NULL) { return; }
#define W61_NULL_ASSERT_STR(p, s) if ((p) == NULL) { LogError("%s\n", (s)); return W61_STATUS_ERROR; }
#else
#define W61_ASSERT(expr)
#define W61_NULL_ASSERT(p)
#define W61_NULL_ASSERT_VOID(p)
#define W61_NULL_ASSERT_STR(p, s)
#endif /* W61_ASSERT_ENABLE */
/** @} */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /*W61_AT_INTERNAL_H */
@@ -0,0 +1,544 @@
/**
******************************************************************************
* @file w61_at_mqtt.c
* @author ST67 Application Team
* @brief This file provides code for W61 MQTT AT module
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include <inttypes.h>
#include <stdio.h>
#include "w61_at_api.h"
#include "w61_at_common.h"
#include "w61_at_internal.h"
#include "common_parser.h" /* Common Parser functions */
#if (defined(SYS_DBG_ENABLE_TA4) && (SYS_DBG_ENABLE_TA4 >= 1))
#include "trcRecorder.h"
#endif /* SYS_DBG_ENABLE_TA4 */
/* Global variables ----------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private defines -----------------------------------------------------------*/
/** @addtogroup ST67W61_AT_MQTT_Constants
* @{
*/
#define W61_MQTT_CONNECT_TIMEOUT 10000U /*!< MQTT connect timeout in ms */
/** @} */
/* Private macros ------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/** @addtogroup ST67W61_AT_MQTT_Functions
* @{
*/
/**
* @brief Callback function to handle MQTT get subscription responses
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings
* @param argc: number of argument
* @return 0 on success, negative value on error
*/
MODEM_CMD_DECLARE(on_cmd_getsub);
/**
* @brief Callback function to handle MQTT unsubscribe responses
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @param argv: array of argument strings
* @param argc: number of argument
* @return 0 on success, negative value on error
*/
MODEM_CMD_DECLARE(on_cmd_getunsub);
/**
* @brief Parses MQTT event and call related callback
* @param hObj: pointer to module handle
* @param argc: pointer to argument count
* @param argv: pointer to argument values
*/
static void W61_MQTT_AT_Event(void *hObj, uint16_t *argc, char **argv);
/**
* @brief Callback function to handle MQTT data events
* @param event_id: event ID
* @param data: pointer to the modem_cmd_handler_data structure
* @param len: length of the data
* @return data length on success, negative value on error
*/
static int32_t W61_MQTT_Data_Event(uint32_t event_id, struct modem_cmd_handler_data *data, uint16_t len);
/* Functions Definition ------------------------------------------------------*/
W61_Status_t W61_MQTT_Init(W61_Object_t *Obj, uint8_t *p_recv_data, uint32_t recv_data_buf_len)
{
W61_NULL_ASSERT(Obj);
W61_NULL_ASSERT(p_recv_data);
/* Initialize the MQTT data buffer. Used to store received MQTT messages */
Obj->MQTTCtx.AppBuffRecvData = p_recv_data;
Obj->MQTTCtx.AppBuffRecvDataSize = recv_data_buf_len;
/* Register the MQTT event callbacks */
Obj->Callbacks.MQTT_event_cb = W61_MQTT_AT_Event;
Obj->Callbacks.MQTT_event_data_cb = W61_MQTT_Data_Event;
return W61_STATUS_OK;
}
W61_Status_t W61_MQTT_DeInit(W61_Object_t *Obj)
{
W61_NULL_ASSERT(Obj);
Obj->MQTTCtx.AppBuffRecvData = NULL;
Obj->MQTTCtx.AppBuffRecvDataSize = 0;
Obj->Callbacks.MQTT_event_cb = NULL;
Obj->Callbacks.MQTT_event_data_cb = NULL;
return W61_STATUS_OK;
}
W61_Status_t W61_MQTT_SetUserConfiguration(W61_Object_t *Obj, uint32_t Scheme, uint8_t *ClientId, uint8_t *Username,
uint8_t *Password, uint8_t *Certificate, uint8_t *PrivateKey,
uint8_t *CaCertificate)
{
char cmd[W61_CMDRSP_STRING_SIZE];
W61_NULL_ASSERT(Obj);
W61_NULL_ASSERT(ClientId);
W61_NULL_ASSERT(Username);
W61_NULL_ASSERT(Password);
W61_NULL_ASSERT(Certificate);
W61_NULL_ASSERT(PrivateKey);
W61_NULL_ASSERT(CaCertificate);
if (ClientId[0] == '\0')
{
return W61_STATUS_ERROR; /* ClientId must not be empty */
}
(void)snprintf(cmd, W61_CMDRSP_STRING_SIZE,
"AT+MQTTUSERCFG=0,%" PRIu32 ",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"\r\n",
Scheme, ClientId, Username, Password, Certificate, PrivateKey, CaCertificate);
return W61_AT_Common_SetExecute(Obj, (uint8_t *)cmd, W61_NET_TIMEOUT);
}
W61_Status_t W61_MQTT_GetUserConfiguration(W61_Object_t *Obj, uint8_t ClientId[32], uint8_t Username[32],
uint8_t Password[32], uint8_t Certificate[32], uint8_t PrivateKey[32],
uint8_t CaCertificate[32])
{
char *argv[CONFIG_MODEM_CMD_HANDLER_MAX_PARAM_COUNT];
char cmd[W61_CMD_MATCH_BUFF_SIZE];
uint16_t argc = 0;
W61_Status_t ret;
W61_NULL_ASSERT(Obj);
W61_NULL_ASSERT(ClientId);
W61_NULL_ASSERT(Username);
W61_NULL_ASSERT(Password);
W61_NULL_ASSERT(Certificate);
W61_NULL_ASSERT(PrivateKey);
W61_NULL_ASSERT(CaCertificate);
/* Initialize the output parameters */
ClientId[0] = '\0';
Username[0] = '\0';
Password[0] = '\0';
Certificate[0] = '\0';
PrivateKey[0] = '\0';
CaCertificate[0] = '\0';
(void)strncpy(cmd, "AT+MQTTUSERCFG?\r\n", sizeof(cmd));
ret = W61_AT_Common_Query_Parse(Obj, cmd, "+MQTTUSERCFG:", &argc, argv, W61_NCP_TIMEOUT);
if (ret != W61_STATUS_OK)
{
return ret;
}
if (argc < 8U)
{
return W61_STATUS_ERROR;
}
W61_AT_RemoveStrQuotes(argv[2]); /* ClientId */
(void)strncpy((char *)ClientId, argv[2], 31);
ClientId[31] = '\0'; /* Ensure null termination */
W61_AT_RemoveStrQuotes(argv[3]); /* Username */
(void)strncpy((char *)Username, argv[3], 31);
Username[31] = '\0'; /* Ensure null termination */
W61_AT_RemoveStrQuotes(argv[4]); /* Password */
(void)strncpy((char *)Password, argv[4], 31);
Password[31] = '\0'; /* Ensure null termination */
W61_AT_RemoveStrQuotes(argv[5]); /* Certificate */
(void)strncpy((char *)Certificate, argv[5], 31);
Certificate[31] = '\0'; /* Ensure null termination */
W61_AT_RemoveStrQuotes(argv[6]); /* Private Key */
(void)strncpy((char *)PrivateKey, argv[6], 31);
PrivateKey[31] = '\0'; /* Ensure null termination */
W61_AT_RemoveStrQuotes(argv[7]); /* CA Certificate */
(void)strncpy((char *)CaCertificate, argv[7], 31);
CaCertificate[31] = '\0'; /* Ensure null termination */
return ret;
}
W61_Status_t W61_MQTT_SetConfiguration(W61_Object_t *Obj, uint32_t KeepAlive, uint32_t DisableCleanSession,
uint8_t *WillTopic, uint8_t *WillMessage, uint32_t WillQos,
uint32_t WillRetain)
{
char cmd[W61_CMDRSP_STRING_SIZE];
W61_NULL_ASSERT(Obj);
(void)snprintf(cmd, W61_CMDRSP_STRING_SIZE,
"AT+MQTTCONNCFG=0,%" PRIu32 ",%" PRIu32 ",\"%s\",\"%s\",%" PRIu32 ",%" PRIu32 "\r\n",
KeepAlive, DisableCleanSession, WillTopic, WillMessage, WillQos, WillRetain);
return W61_AT_Common_SetExecute(Obj, (uint8_t *)cmd, W61_NET_TIMEOUT);
}
W61_Status_t W61_MQTT_GetConfiguration(W61_Object_t *Obj, uint32_t *KeepAlive, uint32_t *DisableCleanSession,
uint8_t WillTopic[64], uint8_t WillMessage[64], uint32_t *WillQos,
uint32_t *WillRetain)
{
char *argv[CONFIG_MODEM_CMD_HANDLER_MAX_PARAM_COUNT];
uint16_t argc = 0;
char cmd[W61_CMD_MATCH_BUFF_SIZE];
W61_Status_t ret;
W61_NULL_ASSERT(Obj);
W61_NULL_ASSERT(KeepAlive);
W61_NULL_ASSERT(DisableCleanSession);
W61_NULL_ASSERT(WillTopic);
W61_NULL_ASSERT(WillMessage);
W61_NULL_ASSERT(WillQos);
W61_NULL_ASSERT(WillRetain);
(void)strncpy(cmd, "AT+MQTTCONNCFG?\r\n", sizeof(cmd));
ret = W61_AT_Common_Query_Parse(Obj, cmd, "+MQTTCONNCFG:", &argc, argv, W61_NET_TIMEOUT);
if (ret != W61_STATUS_OK)
{
return ret;
}
if (argc < 7U)
{
return W61_STATUS_ERROR;
}
*KeepAlive = atoi(argv[1]);
*DisableCleanSession = atoi(argv[2]);
W61_AT_RemoveStrQuotes(argv[3]); /* WillTopic */
(void)strncpy((char *)WillTopic, argv[3], 63);
WillTopic[63] = 0; /* Ensure null termination */
W61_AT_RemoveStrQuotes(argv[4]); /* WillMessage */
(void)strncpy((char *)WillMessage, argv[4], 63);
WillMessage[63] = 0; /* Ensure null termination */
*WillQos = atoi(argv[5]);
*WillRetain = atoi(argv[6]);
return ret;
}
W61_Status_t W61_MQTT_SetSNI(W61_Object_t *Obj, uint8_t *SNI)
{
char cmd[W61_CMDRSP_STRING_SIZE];
W61_NULL_ASSERT(Obj);
W61_NULL_ASSERT(SNI);
(void)snprintf(cmd, W61_CMDRSP_STRING_SIZE, "AT+MQTTSNI=0,\"%s\"\r\n", SNI);
return W61_AT_Common_SetExecute(Obj, (uint8_t *)cmd, W61_NET_TIMEOUT);
}
W61_Status_t W61_MQTT_GetSNI(W61_Object_t *Obj, uint8_t SNI[128])
{
char *argv[CONFIG_MODEM_CMD_HANDLER_MAX_PARAM_COUNT];
char cmd[W61_CMD_MATCH_BUFF_SIZE];
uint16_t argc = 0;
W61_Status_t ret;
W61_NULL_ASSERT(Obj);
W61_NULL_ASSERT(SNI);
(void)strncpy(cmd, "AT+MQTTSNI?\r\n", sizeof(cmd));
ret = W61_AT_Common_Query_Parse(Obj, cmd, "+MQTTSNI:", &argc, argv, W61_NET_TIMEOUT);
if (ret != W61_STATUS_OK)
{
return ret;
}
if (argc < 2U)
{
return W61_STATUS_ERROR;
}
/* Verify the SNI is valid */
if (strncmp(argv[1], "\"", 1) == 0)
{
W61_AT_RemoveStrQuotes(argv[1]); /* Remove quotes from the string */
(void)strncpy((char *)SNI, argv[1], 127);
SNI[127] = '\0'; /* Ensure null termination */
}
else
{
ret = W61_STATUS_ERROR;
}
return ret;
}
W61_Status_t W61_MQTT_Connect(W61_Object_t *Obj, uint8_t *Host, uint16_t Port)
{
uint8_t reconnect = 0;
char cmd[W61_CMDRSP_STRING_SIZE];
W61_NULL_ASSERT(Obj);
W61_NULL_ASSERT(Host);
(void)snprintf(cmd, W61_CMDRSP_STRING_SIZE,
"AT+MQTTCONN=0,\"%s\",%" PRIu16 ",%" PRIu16 "\r\n",
Host, Port, reconnect);
return W61_AT_Common_SetExecute(Obj, (uint8_t *)cmd, W61_MQTT_CONNECT_TIMEOUT);
}
W61_Status_t W61_MQTT_GetConnectionStatus(W61_Object_t *Obj, uint8_t *Host, uint32_t *Port,
uint32_t *Scheme, uint32_t *State)
{
char *argv[CONFIG_MODEM_CMD_HANDLER_MAX_PARAM_COUNT];
char cmd[W61_CMD_MATCH_BUFF_SIZE];
uint16_t argc = 0;
W61_Status_t ret;
W61_NULL_ASSERT(Obj);
W61_NULL_ASSERT(Host);
W61_NULL_ASSERT(Port);
W61_NULL_ASSERT(Scheme);
W61_NULL_ASSERT(State);
(void)strncpy(cmd, "AT+MQTTCONN?\r\n", sizeof(cmd));
ret = W61_AT_Common_Query_Parse(Obj, cmd, "+MQTTCONN:", &argc, argv, W61_NET_TIMEOUT);
if (ret != W61_STATUS_OK)
{
return ret;
}
if (argc < 6U)
{
return W61_STATUS_ERROR;
}
*State = atoi(argv[1]);
*Scheme = atoi(argv[2]);
W61_AT_RemoveStrQuotes(argv[3]);
(void)strncpy((char *)Host, argv[3], 63);
*Port = atoi(argv[4]);
return ret;
}
W61_Status_t W61_MQTT_Disconnect(W61_Object_t *Obj)
{
W61_NULL_ASSERT(Obj);
return W61_AT_Common_SetExecute(Obj, (uint8_t *)"AT+MQTTCLEAN=0\r\n", W61_NET_TIMEOUT);
}
W61_Status_t W61_MQTT_Subscribe(W61_Object_t *Obj, uint8_t *Topic)
{
uint32_t qos = 0;
char cmd[W61_CMDRSP_STRING_SIZE];
W61_NULL_ASSERT(Obj);
W61_NULL_ASSERT(Topic);
(void)snprintf(cmd, W61_CMDRSP_STRING_SIZE, "AT+MQTTSUB=0,\"%s\",%" PRIu32 "\r\n", Topic, qos);
return W61_AT_Common_SetExecute(Obj, (uint8_t *)cmd, W61_NET_TIMEOUT);
}
W61_Status_t W61_MQTT_GetSubscribedTopics(W61_Object_t *Obj)
{
struct modem *mdm = (struct modem *) &Obj->Modem;
W61_NULL_ASSERT(Obj);
struct modem_cmd handlers[] =
{
MODEM_CMD("+MQTTSUB:", on_cmd_getsub, 4U, ","),
};
return W61_Status(modem_cmd_send(&mdm->iface,
&mdm->handler,
handlers,
ARRAY_SIZE(handlers),
(const uint8_t *)"AT+MQTTSUB?\r\n",
mdm->sem_response,
W61_NET_TIMEOUT));
}
W61_Status_t W61_MQTT_Unsubscribe(W61_Object_t *Obj, uint8_t *Topic)
{
struct modem *mdm = (struct modem *) &Obj->Modem;
char cmd[W61_CMDRSP_STRING_SIZE];
W61_NULL_ASSERT(Obj);
W61_NULL_ASSERT(Topic);
struct modem_cmd handlers[] =
{
MODEM_CMD("+MQTTUNSUB:", on_cmd_getunsub, 1U, ""),
};
(void)snprintf(cmd, W61_CMDRSP_STRING_SIZE, "AT+MQTTUNSUB=0,\"%s\"\r\n", Topic);
return W61_Status(modem_cmd_send(&mdm->iface,
&mdm->handler,
handlers,
ARRAY_SIZE(handlers),
(const uint8_t *)cmd,
mdm->sem_response,
W61_NET_TIMEOUT));
}
W61_Status_t W61_MQTT_Publish(W61_Object_t *Obj, uint8_t *Topic, uint8_t *Message, uint32_t Message_len, uint32_t Qos,
uint32_t Retain)
{
char cmd[W61_CMDRSP_STRING_SIZE];
W61_NULL_ASSERT(Obj);
W61_NULL_ASSERT(Topic);
W61_NULL_ASSERT(Message);
if (Message_len > 0U)
{
(void)snprintf(cmd, W61_CMDRSP_STRING_SIZE, "AT+MQTTPUBRAW=0,\"%s\",%" PRIu32 ",%" PRIu32 ",%" PRIu32 "\r\n",
Topic, Message_len, Qos, Retain);
return W61_AT_Common_RequestSendData(Obj, (uint8_t *)cmd, Message, Message_len, W61_NET_TIMEOUT, true);
}
else
{
(void)snprintf(cmd, W61_CMDRSP_STRING_SIZE,
"AT+MQTTPUB=0,\"%s\",\"\",%" PRIu32 ",%" PRIu32 "\r\n",
Topic, Qos, Retain);
return W61_AT_Common_SetExecute(Obj, (uint8_t *)cmd, W61_NET_TIMEOUT);
}
}
/* Private Functions Definition ----------------------------------------------*/
MODEM_CMD_DEFINE(on_cmd_getsub)
{
if (argc >= 4U)
{
MQTT_LOG_DEBUG("LinkID: %" PRIu32 ", state: %" PRIu32 ", topic: %s, qos: %" PRIu32 "\n",
atoi((char *)argv[0]), atoi((char *)argv[1]), argv[2], atoi((char *)argv[3]));
}
return 0;
}
MODEM_CMD_DEFINE(on_cmd_getunsub)
{
if ((argc >= 1U) && (strcmp((char *)argv[0], "NO_UNSUBSCRIBE") == 0))
{
MQTT_LOG_WARN("No topic found\n");
}
return 0;
}
static void W61_MQTT_AT_Event(void *hObj, uint16_t *argc, char **argv)
{
W61_Object_t *Obj = (W61_Object_t *)hObj;
if ((Obj == NULL) || (Obj->ulcbs.UL_mqtt_cb == NULL) || (*argc < 2U))
{
return;
}
/* Argv[0] = link id. not used */
if (strcmp(argv[0], "CONNECTED") == 0)
{
Obj->ulcbs.UL_mqtt_cb(W61_MQTT_EVT_CONNECTED_ID, NULL);
return;
}
if (strcmp(argv[0], "DISCONNECTED") == 0)
{
Obj->ulcbs.UL_mqtt_cb(W61_MQTT_EVT_DISCONNECTED_ID, NULL);
return;
}
}
static int32_t W61_MQTT_Data_Event(uint32_t event_id, struct modem_cmd_handler_data *data, uint16_t len)
{
struct modem *mdm = (struct modem *)data->user_data;
W61_Object_t *Obj = CONTAINER_OF(mdm, W61_Object_t, Modem);
W61_MQTT_CbParamData_t cb_param_mqtt_data;
uint8_t *ptr;
uint8_t *endptr;
uint32_t header_len;
uint32_t topic_len;
uint32_t message_len;
uint16_t rx_data_len;
if ((data->rx_buf == NULL) || (Obj->MQTTCtx.AppBuffRecvData == NULL))
{
return -EINVAL;
}
ptr = &data->rx_buf[len + 2U]; /* Skip the link id and the comma */
data->rx_buf[data->rx_buf_len] = 0;
/* Get the topic length */
topic_len = strtol((char *)ptr, (char **)&endptr, 10);
if ((endptr == ptr) || (*endptr != ','))
{
return -EINVAL;
}
topic_len += 2; /* Include double-quotes */
endptr++; /* Skip the comma */
/* Get the message length */
message_len = strtol((char *)endptr, (char **)&endptr, 10);
if ((endptr == ptr) || (*endptr != ','))
{
return -EINVAL;
}
endptr++; /* Skip the comma */
/* Check if the buffer is large enough to hold the topic and message */
if ((topic_len + message_len + 1) > Obj->MQTTCtx.AppBuffRecvDataSize) /* +1 for the intermediate comma */
{
return -ENOMEM;
}
header_len = endptr - data->rx_buf;
rx_data_len = header_len + topic_len + message_len + 1; /* +1 for the intermediate comma */
if (data->rx_buf_len >= rx_data_len)
{
/* Copy the topic */
(void)memcpy(Obj->MQTTCtx.AppBuffRecvData, endptr, topic_len);
/* Null-terminate the topic */
Obj->MQTTCtx.AppBuffRecvData[topic_len] = 0;
endptr += topic_len + 1; /* Skip the comma after topic */
/* Copy the message */
(void)memcpy(&Obj->MQTTCtx.AppBuffRecvData[topic_len + 1], endptr, message_len);
cb_param_mqtt_data.topic_length = topic_len;
cb_param_mqtt_data.message_length = message_len;
if (Obj->ulcbs.UL_mqtt_cb != NULL)
{
Obj->ulcbs.UL_mqtt_cb(W61_MQTT_EVT_SUBSCRIPTION_RECEIVED_ID, &cb_param_mqtt_data);
}
return rx_data_len;
}
else
{
return -EAGAIN;
}
}
/** @} */
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,155 @@
/**
******************************************************************************
* @file w61_default_config.h
* @author ST67 Application Team
* @brief Header file for the W61 configuration module
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef W61_DEFAULT_CONFIG_H
#define W61_DEFAULT_CONFIG_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Includes ------------------------------------------------------------------*/
#include "w61_driver_config.h"
/* Exported constants --------------------------------------------------------*/
/** @addtogroup ST67W61_AT_WiFi_Constants
* @{
*/
#ifndef W61_WIFI_MAX_DETECTED_AP
/** Maximum number of detected AP during the scan. Cannot be greater than 50 */
#define W61_WIFI_MAX_DETECTED_AP 20U
#endif /* W61_WIFI_MAX_DETECTED_AP */
#ifndef WIFI_LOG_ENABLE
/** Enable/Disable Wi-Fi module logging */
#define WIFI_LOG_ENABLE 0
#endif /* WIFI_LOG_ENABLE */
/** @} */
/** @addtogroup ST67W61_AT_BLE_Constants
* @{
*/
#ifndef W61_BLE_MAX_CONN_NBR
/** Maximum number of BLE connections. Must not be greater than 10.
* The maximum connections depend on the mode as follows:
* - Server mode (10 connection max)
* - Client mode (9 connection max)
* - Dual Mode (10 connections max with up to 9 as Client)
*/
#define W61_BLE_MAX_CONN_NBR 2U
#endif /* W61_BLE_MAX_CONN_NBR */
/** Maximum number of BLE application services that can be created */
#define W61_BLE_MAX_CREATED_SERVICE_NBR 3U
/** Maximum number of BLE services supported including Generic access and Generic attributes predefined services */
#define W61_BLE_MAX_SERVICE_NBR (W61_BLE_MAX_CREATED_SERVICE_NBR + 2U)
/** Maximum number of BLE characteristics per service */
#define W61_BLE_MAX_CHAR_NBR 5U
#ifndef W61_BLE_MAX_DETECTED_PERIPHERAL
/** Maximum number of detected peripheral during the scan. Cannot be greater than 50 */
#define W61_BLE_MAX_DETECTED_PERIPHERAL 10U
#endif /* W61_BLE_MAX_DETECTED_PERIPHERAL */
/** BLE Service/Characteristic UUID maximum size size */
#define W61_BLE_MAX_UUID_SIZE 17U
/** Maximum number of bonded devices */
#define W61_BLE_MAX_BONDED_DEVICES 2U
#ifndef BLE_LOG_ENABLE
/** Enable/Disable BLE module logging */
#define BLE_LOG_ENABLE 0
#endif /* BLE_LOG_ENABLE */
/** @} */
/** @addtogroup ST67W61_AT_Net_Constants
* @{
*/
#ifndef W61_NET_IPV6_ENABLE
/** Enable IPv6 support : 0: Disabled, 1: Enabled */
#define W61_NET_IPV6_ENABLE 0
#endif /* W61_NET_IPV6_ENABLE */
#ifndef NET_LOG_ENABLE
/** Enable/Disable Network module logging */
#define NET_LOG_ENABLE 0
#endif /* NET_LOG_ENABLE */
/** @} */
/** @addtogroup ST67W61_AT_MQTT_Constants
* @{
*/
#ifndef MQTT_LOG_ENABLE
/** Enable/Disable MQTT module logging */
#define MQTT_LOG_ENABLE 0
#endif /* MQTT_LOG_ENABLE */
/** @} */
/** @addtogroup ST67W61_AT_Common_Constants
* @{
*/
#ifndef W61_MAX_SPI_XFER
/** Maximum SPI buffer size */
#define W61_MAX_SPI_XFER 1520U
#endif /* W61_MAX_SPI_XFER */
#if ((W61_MAX_SPI_XFER < 1520U) || (W61_MAX_SPI_XFER > (6U * 1024U)))
#error "W6X_MAX_SPI_XFER must be between 1520 and (6*1024)"
#endif /* W61_MAX_SPI_XFER */
#ifndef SYS_LOG_ENABLE
/** Enable/Disable System module logging */
#define SYS_LOG_ENABLE 0
#endif /* SYS_LOG_ENABLE */
#ifndef W61_AT_LOG_ENABLE
/** Debugging only: Enable/Disable AT log, i.e. logs the AT commands incoming/outcoming from/to the NCP */
#define W61_AT_LOG_ENABLE 0
#endif /* W61_AT_LOG_ENABLE */
#ifndef MDM_CMD_LOG_ENABLE
/** Enable/Disable Modem command log */
#define MDM_CMD_LOG_ENABLE W61_AT_LOG_ENABLE
#endif /* MDM_CMD_LOG_ENABLE */
#ifndef W61_ASSERT_ENABLE
/** Enable/Disable NULL pointer check in the AT functions.
* 0: Disabled, 1: Enabled */
#define W61_ASSERT_ENABLE 0
#endif /* W61_ASSERT_ENABLE */
/** @} */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* W61_DEFAULT_CONFIG_H */