diff --git a/drivers/at/at.c b/drivers/at/at.c index f74b5e370211f3e6141d9fe09ce403ca4919bc08..dfbff87cf767e83e81ae4f5cbdb5ef316415ad48 100644 --- a/drivers/at/at.c +++ b/drivers/at/at.c @@ -266,3 +266,52 @@ out: } return res; } + +#ifdef MODULE_AT_URC +void at_add_urc(at_dev_t *dev, at_urc_t *urc) +{ + assert(urc); + assert(urc->code); + assert(strlen(urc->code) != 0); + assert(urc->cb); + + clist_rpush(&dev->urc_list, &urc->list_node); +} + +void at_remove_urc(at_dev_t *dev, at_urc_t *urc) +{ + clist_remove(&dev->urc_list, &urc->list_node); +} + +static int _check_urc(clist_node_t *node, void *arg) +{ + const char *buf = arg; + at_urc_t *urc = container_of(node, at_urc_t, list_node); + + DEBUG("Trying to match with %s\n", urc->code); + + if (strncmp(buf, urc->code, strlen(urc->code)) == 0) { + urc->cb(urc->arg, buf); + return 1; + } + + return 0; +} + +void at_process_urc(at_dev_t *dev, uint32_t timeout) +{ + char buf[AT_BUF_SIZE]; + + DEBUG("Processing URC (timeout=%" PRIu32 "us)\n", timeout); + + ssize_t res; + /* keep reading while received data are shorter than EOL */ + while ((res = at_readline(dev, buf, sizeof(buf), true, timeout)) < + (ssize_t)sizeof(AT_RECV_EOL_1 AT_RECV_EOL_2) - 1) { + if (res < 0) { + return; + } + } + clist_foreach(&dev->urc_list, _check_urc, buf); +} +#endif diff --git a/drivers/include/at.h b/drivers/include/at.h index 7f8d02848f9d690be03e4336ec1d3b85bde8d4c8..3d1808d7f2353a4e9d08b3ea9c22e8c57629e02f 100644 --- a/drivers/include/at.h +++ b/drivers/include/at.h @@ -39,6 +39,7 @@ #include "isrpipe.h" #include "periph/uart.h" +#include "clist.h" #ifdef __cplusplus extern "C" { @@ -77,12 +78,41 @@ extern "C" { #define AT_RECV_ERROR "ERROR" #endif +#if defined(MODULE_AT_URC) || DOXYGEN +#ifndef AT_BUF_SIZE +/** Internal buffer size used to process unsolicited result code data */ +#define AT_BUF_SIZE (128) +#endif + +/** + * @brief Unsolicited result code callback + * + * @param[in] arg optional argument + * @param[in] code urc string received from the device + */ +typedef void (*at_urc_cb_t)(void *arg, const char *code); + +/** + * @brief Unsolicited result code data structure + */ +typedef struct { + clist_node_t list_node; /**< node list */ + at_urc_cb_t cb; /**< callback */ + const char *code; /**< URC string which must match */ + void *arg; /**< optional argument */ +} at_urc_t; + +#endif /* MODULE_AT_URC */ + /** * @brief AT device structure */ typedef struct { isrpipe_t isrpipe; /**< isrpipe used for getting data from uart */ uart_t uart; /**< UART device where the AT device is attached */ +#ifdef MODULE_AT_URC + clist_node_t urc_list; /**< list to keep track of all registered urc's */ +#endif } at_dev_t; /** @@ -226,6 +256,32 @@ ssize_t at_readline(at_dev_t *dev, char *resp_buf, size_t len, bool keep_eol, ui */ void at_drain(at_dev_t *dev); +#if defined(MODULE_AT_URC) || DOXYGEN +/** + * @brief Add a callback for an unsolicited response code + * + * @param[in] dev device to operate on + * @param[in] urc unsolicited result code to register + */ +void at_add_urc(at_dev_t *dev, at_urc_t *urc); + +/** + * @brief Remove an unsolicited response code from the list + * + * @param[in] dev device to operate on + * @param[in] urc unsolicited result code to remove + */ +void at_remove_urc(at_dev_t *dev, at_urc_t *urc); + +/** + * @brief Process out-of-band data received from the device + * + * @param[in] dev device to operate on + * @param[in] timeout timeout (in usec) + */ +void at_process_urc(at_dev_t *dev, uint32_t timeout); +#endif + #ifdef __cplusplus } #endif diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 0d04d3cec7c6e1f84dd6ae2aa2a37df5051c9e69..1b4ca0337e2da2da8a556793bbfa4535b008ef00 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -1,3 +1,4 @@ +PSEUDOMODULES += at_urc PSEUDOMODULES += auto_init_gnrc_rpl PSEUDOMODULES += can_mbox PSEUDOMODULES += can_pm diff --git a/tests/driver_at/Makefile b/tests/driver_at/Makefile index 43ed6a582c3816f37beb3b3c10260436b0e85c79..f68ba0642896a78dc0ff4282fa92c50d7b7d4fb1 100644 --- a/tests/driver_at/Makefile +++ b/tests/driver_at/Makefile @@ -4,5 +4,6 @@ BOARD_INSUFFICIENT_MEMORY += nucleo-f031k6 USEMODULE += shell USEMODULE += at +USEMODULE += at_urc include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_at/main.c b/tests/driver_at/main.c index 026d966d1b85375f0b90465b421a965d52668f76..7353bb5bec5a29ed86f0396e3bc50a5ed3358933 100644 --- a/tests/driver_at/main.c +++ b/tests/driver_at/main.c @@ -22,6 +22,7 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> #include "at.h" #include "shell.h" @@ -114,12 +115,101 @@ static int drain(int argc, char **argv) return 0; } +#ifdef MODULE_AT_URC +#ifndef MAX_URC_NB +#define MAX_URC_NB 5 +#endif + +#ifndef MAX_URC_LEN +#define MAX_URC_LEN 32 +#endif + +static at_urc_t urc_list[MAX_URC_NB]; +static char urc_str[MAX_URC_NB][MAX_URC_LEN]; +static bool urc_used[MAX_URC_NB]; + +static void _urc_cb(void *arg, const char *urc) +{ + (void)arg; + printf("urc received: %s\n", urc); +} + +static int add_urc(int argc, char **argv) +{ + if (argc < 2) { + printf("Usage: %s <urc>\n", argv[0]); + return 1; + } + + if (strlen(argv[1]) > MAX_URC_LEN - 1) { + puts("urc is too long"); + return 1; + } + + for (size_t i = 0; i < MAX_URC_NB; i++) { + if (!urc_used[i]) { + strcpy(urc_str[i], argv[1]); + urc_list[i].code = urc_str[i]; + urc_list[i].arg = NULL; + urc_list[i].cb = _urc_cb; + urc_used[i] = true; + at_add_urc(&at_dev, &urc_list[i]); + puts("urc registered"); + return 0; + } + } + + puts("Not enough memory, urc is not registered"); + return 1; +} + +static int process_urc(int argc, char **argv) +{ + if (argc < 2) { + printf("Usage: %s <timeout>\n", argv[0]); + return 1; + } + + uint32_t timeout = strtoul(argv[1], NULL, 0); + at_process_urc(&at_dev, timeout); + + puts("urc processed"); + + return 0; +} + +static int remove_urc(int argc, char **argv) +{ + if (argc < 2) { + printf("Usage: %s <urc>\n", argv[0]); + return 1; + } + + for (size_t i = 0; i < MAX_URC_NB; i++) { + if (urc_used[i] && strcmp(urc_list[i].code, argv[1]) == 0) { + at_remove_urc(&at_dev, &urc_list[i]); + urc_used[i] = false; + puts("urc removed"); + return 0; + } + } + + puts("urc not found"); + return 1; +} +#endif + static const shell_command_t shell_commands[] = { { "init", "Initialize AT device", init }, { "send", "Send a command and wait response", send }, { "send_ok", "Send a command and wait OK", send_ok }, { "send_lines", "Send a command and wait lines", send_lines }, { "drain", "Drain AT device", drain }, +#ifdef MODULE_AT_URC + { "add_urc", "Register an URC", add_urc }, + { "remove_urc", "De-register an URC", remove_urc }, + { "process_urc", "Process the URCs", process_urc }, +#endif { NULL, NULL, NULL }, };