mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-18 19:13:51 +01:00
drivers/at: parse +CME/+CMS responses and save error value
This commit is contained in:
parent
d83ec632e3
commit
c58b71b899
306
drivers/at/at.c
306
drivers/at/at.c
@ -41,6 +41,7 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "at.h"
|
#include "at.h"
|
||||||
@ -77,6 +78,18 @@ static void _event_process_urc(event_t *_event)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static ssize_t at_readline_skip_empty_stop_at_str(at_dev_t *dev, char *resp_buf,
|
||||||
|
size_t len, bool keep_eol,
|
||||||
|
char const *substr, uint32_t timeout);
|
||||||
|
static size_t at_readline_stop_at_str(at_dev_t *dev, char *resp_buf, size_t len,
|
||||||
|
bool keep_eol, char const *substr,
|
||||||
|
uint32_t timeout);
|
||||||
|
|
||||||
|
static inline bool starts_with(char const *str, char const *prefix)
|
||||||
|
{
|
||||||
|
return strncmp(str, prefix, strlen(prefix)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void _isrpipe_write_one_wrapper(void *_dev, uint8_t data)
|
static void _isrpipe_write_one_wrapper(void *_dev, uint8_t data)
|
||||||
{
|
{
|
||||||
at_dev_t *dev = (at_dev_t *) _dev;
|
at_dev_t *dev = (at_dev_t *) _dev;
|
||||||
@ -88,18 +101,50 @@ static void _isrpipe_write_one_wrapper(void *_dev, uint8_t data)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int at_dev_init(at_dev_t *dev, uart_t uart, uint32_t baudrate, char *buf, size_t bufsize)
|
int at_dev_init(at_dev_t *dev, at_dev_init_t const *init)
|
||||||
{
|
{
|
||||||
dev->uart = uart;
|
dev->uart = init->uart;
|
||||||
|
assert(init->rp_buf_size >= 16);
|
||||||
|
dev->rp_buf = init->rp_buf;
|
||||||
|
dev->rp_buf_size = init->rp_buf_size;
|
||||||
|
|
||||||
#if IS_USED(MODULE_AT_URC_ISR)
|
#if IS_USED(MODULE_AT_URC_ISR)
|
||||||
dev->awaiting_response = false;
|
dev->awaiting_response = false;
|
||||||
dev->event.handler = _event_process_urc;
|
dev->event.handler = _event_process_urc;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
isrpipe_init(&dev->isrpipe, (uint8_t *)buf, bufsize);
|
isrpipe_init(&dev->isrpipe, (uint8_t *)init->rx_buf, init->rx_buf_size);
|
||||||
|
|
||||||
return uart_init(uart, baudrate, _isrpipe_write_one_wrapper, dev);
|
return uart_init(init->uart, init->baudrate, _isrpipe_write_one_wrapper, dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
int at_parse_resp(at_dev_t *dev, char const *resp)
|
||||||
|
{
|
||||||
|
if (*resp == '\0') {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (starts_with(resp, CONFIG_AT_RECV_OK)) {
|
||||||
|
dev->rp_buf[0] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (starts_with(resp, CONFIG_AT_RECV_ERROR)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* A specific command may return either CME or CMS, we need not differentiate */
|
||||||
|
if (!starts_with(resp, "+CME ERROR: ") &&
|
||||||
|
!starts_with(resp, "+CMS ERROR: ")) {
|
||||||
|
/* neither `OK` nor error, must be a response or URC */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
resp += strlen("+CMx ERROR: ");
|
||||||
|
size_t resp_len = strlen(resp);
|
||||||
|
if (resp_len + 1 > dev->rp_buf_size) {
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
/* dev->rp_buf and resp may overlap */
|
||||||
|
memmove(dev->rp_buf, resp, resp_len + 1);
|
||||||
|
return -AT_ERR_EXTENDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
int at_expect_bytes(at_dev_t *dev, const char *bytes, uint32_t timeout)
|
int at_expect_bytes(at_dev_t *dev, const char *bytes, uint32_t timeout)
|
||||||
@ -224,8 +269,7 @@ int at_send_cmd(at_dev_t *dev, const char *command, uint32_t timeout)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (at_expect_bytes(dev, CONFIG_AT_SEND_EOL AT_RECV_EOL_1 AT_RECV_EOL_2,
|
if (at_expect_bytes(dev, CONFIG_AT_SEND_EOL, timeout)) {
|
||||||
timeout)) {
|
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -275,53 +319,64 @@ ssize_t at_send_cmd_get_resp_wait_ok(at_dev_t *dev, const char *command, const c
|
|||||||
{
|
{
|
||||||
ssize_t res;
|
ssize_t res;
|
||||||
ssize_t res_ok;
|
ssize_t res_ok;
|
||||||
char ok_buf[64];
|
|
||||||
|
|
||||||
at_drain(dev);
|
at_drain(dev);
|
||||||
|
|
||||||
res = at_send_cmd(dev, command, timeout);
|
res = at_send_cmd(dev, command, timeout);
|
||||||
if (res) {
|
if (res) {
|
||||||
goto out;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* URCs may occur right after the command has been sent and before the
|
/* URCs may occur right after the command has been sent and before the
|
||||||
* expected response */
|
* expected response */
|
||||||
do {
|
while ((res = at_readline_skip_empty(dev, resp_buf, len, false, timeout)) >= 0) {
|
||||||
res = at_readline_skip_empty(dev, resp_buf, len, false, timeout);
|
if (!resp_prefix || *resp_prefix == '\0') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
/* Strip the expected prefix */
|
/* Strip the expected prefix */
|
||||||
if (res > 0 && resp_prefix && *resp_prefix) {
|
|
||||||
size_t prefix_len = strlen(resp_prefix);
|
size_t prefix_len = strlen(resp_prefix);
|
||||||
if (strncmp(resp_buf, resp_prefix, prefix_len) == 0) {
|
if (starts_with(resp_buf, resp_prefix)) {
|
||||||
size_t remaining_len = strlen(resp_buf) - prefix_len;
|
size_t remaining_len = strlen(resp_buf) - prefix_len;
|
||||||
/* The one extra byte in the copy is the terminating nul byte */
|
/* The one extra byte in the copy is the terminating nul byte */
|
||||||
memmove(resp_buf, resp_buf + prefix_len, remaining_len + 1);
|
memmove(resp_buf, resp_buf + prefix_len, remaining_len + 1);
|
||||||
res -= prefix_len;
|
res -= prefix_len;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
res = at_parse_resp(dev, resp_buf);
|
||||||
|
if (res == 0) {
|
||||||
|
/* empty response */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (res < 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#if IS_USED(MODULE_AT_URC)
|
||||||
|
else {
|
||||||
|
clist_foreach(&dev->urc_list, _check_urc, resp_buf);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (res < 0) {
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
} while (res >= 0);
|
|
||||||
|
|
||||||
/* wait for OK */
|
/* wait for OK */
|
||||||
if (res >= 0) {
|
res_ok = at_readline_skip_empty(dev, dev->rp_buf, dev->rp_buf_size, false, timeout);
|
||||||
res_ok = at_readline_skip_empty(dev, ok_buf, sizeof(ok_buf), false, timeout);
|
|
||||||
if (res_ok < 0) {
|
if (res_ok < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
ssize_t len_ok = sizeof(CONFIG_AT_RECV_OK) - 1;
|
res_ok = at_parse_resp(dev, dev->rp_buf);
|
||||||
if ((len_ok != 0) && (strcmp(ok_buf, CONFIG_AT_RECV_OK) == 0)) {
|
if (res_ok == 0) {
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* Something else than OK */
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
return res;
|
return res;
|
||||||
|
}
|
||||||
|
if (res_ok < 0) {
|
||||||
|
return res_ok;
|
||||||
|
}
|
||||||
|
/* Neither OK nor error, go figure... */
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t at_send_cmd_get_lines(at_dev_t *dev, const char *command,
|
ssize_t at_send_cmd_get_lines(at_dev_t *dev, const char *command, char *resp_buf,
|
||||||
char *resp_buf, size_t len, bool keep_eol, uint32_t timeout)
|
size_t len, bool keep_eol, uint32_t timeout)
|
||||||
{
|
{
|
||||||
const char eol[] = AT_RECV_EOL_1 AT_RECV_EOL_2;
|
const char eol[] = AT_RECV_EOL_1 AT_RECV_EOL_2;
|
||||||
assert(sizeof(eol) > 1);
|
assert(sizeof(eol) > 1);
|
||||||
@ -334,123 +389,113 @@ ssize_t at_send_cmd_get_lines(at_dev_t *dev, const char *command,
|
|||||||
|
|
||||||
res = at_send_cmd(dev, command, timeout);
|
res = at_send_cmd(dev, command, timeout);
|
||||||
if (res) {
|
if (res) {
|
||||||
goto out;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(resp_buf, '\0', len);
|
memset(resp_buf, '\0', len);
|
||||||
|
|
||||||
while (1) {
|
bool first_line = true;
|
||||||
|
while (bytes_left) {
|
||||||
|
if (first_line) {
|
||||||
|
res = at_readline_skip_empty(dev, pos, bytes_left, keep_eol, timeout);
|
||||||
|
first_line = false;
|
||||||
|
} else {
|
||||||
|
/* keep subsequent empty lines, for whatever reason */
|
||||||
res = at_readline(dev, pos, bytes_left, keep_eol, timeout);
|
res = at_readline(dev, pos, bytes_left, keep_eol, timeout);
|
||||||
|
}
|
||||||
if (res == 0) {
|
if (res == 0) {
|
||||||
if (bytes_left) {
|
|
||||||
*pos++ = eol[sizeof(eol) - 2];
|
*pos++ = eol[sizeof(eol) - 2];
|
||||||
bytes_left--;
|
bytes_left--;
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (res > 0) {
|
else if (res > 0) {
|
||||||
bytes_left -= res;
|
size_t const res_len = res;
|
||||||
size_t len_ok = sizeof(CONFIG_AT_RECV_OK) - 1;
|
bytes_left -= res_len;
|
||||||
size_t len_error = sizeof(CONFIG_AT_RECV_ERROR) - 1;
|
res = at_parse_resp(dev, pos);
|
||||||
if (((size_t )res == (len_ok + keep_eol)) &&
|
|
||||||
(len_ok != 0) &&
|
switch (res) {
|
||||||
(strncmp(pos, CONFIG_AT_RECV_OK, len_ok) == 0)) {
|
case 0: /* OK */
|
||||||
res = len - bytes_left;
|
res = len - bytes_left;
|
||||||
break;
|
return res;
|
||||||
|
case 1: /* response or URC */
|
||||||
|
pos += res_len;
|
||||||
|
if (bytes_left == 0) {
|
||||||
|
return -ENOBUFS;
|
||||||
}
|
}
|
||||||
else if (((size_t )res == (len_error + keep_eol)) &&
|
|
||||||
(len_error != 0) &&
|
|
||||||
(strncmp(pos, CONFIG_AT_RECV_ERROR, len_error) == 0)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else if (strncmp(pos, "+CME ERROR:", 11) == 0) {
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
else if (strncmp(pos, "+CMS ERROR:", 11) == 0) {
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pos += res;
|
|
||||||
if (bytes_left) {
|
|
||||||
*pos++ = eol[sizeof(eol) - 2];
|
*pos++ = eol[sizeof(eol) - 2];
|
||||||
bytes_left--;
|
bytes_left--;
|
||||||
}
|
continue;
|
||||||
else {
|
default: /* <0 */
|
||||||
return -1;
|
return res;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
break;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
return -ENOBUFS;
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int at_send_cmd_wait_prompt(at_dev_t *dev, const char *command, uint32_t timeout)
|
int at_send_cmd_wait_prompt(at_dev_t *dev, const char *command, uint32_t timeout)
|
||||||
{
|
{
|
||||||
unsigned cmdlen = strlen(command);
|
|
||||||
|
|
||||||
at_drain(dev);
|
at_drain(dev);
|
||||||
|
|
||||||
uart_write(dev->uart, (const uint8_t *)command, cmdlen);
|
int res = at_send_cmd(dev, command, timeout);
|
||||||
uart_write(dev->uart, (const uint8_t *)CONFIG_AT_SEND_EOL, AT_SEND_EOL_LEN);
|
if (res) {
|
||||||
|
return res;
|
||||||
if (!IS_ACTIVE(CONFIG_AT_SEND_SKIP_ECHO)) {
|
|
||||||
if (at_wait_bytes(dev, command, timeout)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (at_expect_bytes(dev, CONFIG_AT_SEND_EOL, timeout)) {
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return at_wait_bytes(dev, ">", timeout);
|
do {
|
||||||
|
res = at_readline_skip_empty_stop_at_str(dev, dev->rp_buf, dev->rp_buf_size,
|
||||||
|
false, ">", timeout);
|
||||||
|
if (res < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (strstr(dev->rp_buf, ">")) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
res = at_parse_resp(dev, dev->rp_buf);
|
||||||
|
#ifdef MODULE_AT_URC
|
||||||
|
if (res == 1) {
|
||||||
|
clist_foreach(&dev->urc_list, _check_urc, dev->rp_buf);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} while (res >= 0);
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int at_send_cmd_wait_ok(at_dev_t *dev, const char *command, uint32_t timeout)
|
int at_send_cmd_wait_ok(at_dev_t *dev, const char *command, uint32_t timeout)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
char resp_buf[64];
|
|
||||||
|
|
||||||
res = at_send_cmd_get_resp(dev, command, resp_buf, sizeof(resp_buf),
|
res = at_send_cmd_get_resp(dev, command, dev->rp_buf, dev->rp_buf_size, timeout);
|
||||||
timeout);
|
|
||||||
|
|
||||||
size_t const len_ok = sizeof(CONFIG_AT_RECV_OK) - 1;
|
|
||||||
size_t const len_err = sizeof(CONFIG_AT_RECV_ERROR) - 1;
|
|
||||||
size_t const len_cme_cms = sizeof("+CME ERROR:") - 1;
|
|
||||||
|
|
||||||
while (res >= 0) {
|
while (res >= 0) {
|
||||||
if (strncmp(resp_buf, CONFIG_AT_RECV_OK, len_ok) == 0) {
|
res = at_parse_resp(dev, dev->rp_buf);
|
||||||
return 0;
|
if (res < 1) {
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
else if (strncmp(resp_buf, CONFIG_AT_RECV_ERROR, len_err) == 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else if (strncmp(resp_buf, "+CME ERROR:", len_cme_cms) == 0) {
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
else if (strncmp(resp_buf, "+CMS ERROR:", len_cme_cms) == 0) {
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
/* probably a sneaky URC */
|
|
||||||
#ifdef MODULE_AT_URC
|
#ifdef MODULE_AT_URC
|
||||||
clist_foreach(&dev->urc_list, _check_urc, resp_buf);
|
clist_foreach(&dev->urc_list, _check_urc, dev->rp_buf);
|
||||||
#endif
|
#endif
|
||||||
res = at_readline_skip_empty(dev, resp_buf, sizeof(resp_buf), false, timeout);
|
res = at_readline_skip_empty(dev, dev->rp_buf, dev->rp_buf_size, false, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t at_readline(at_dev_t *dev, char *resp_buf, size_t len, bool keep_eol, uint32_t timeout)
|
/* Used to detect a substring that may happen before the EOL. For example,
|
||||||
|
* Ublox LTE modules don't add EOL after the prompt character `>`. */
|
||||||
|
static size_t at_readline_stop_at_str(at_dev_t *dev, char *resp_buf, size_t len,
|
||||||
|
bool keep_eol, char const *substr,
|
||||||
|
uint32_t timeout)
|
||||||
{
|
{
|
||||||
const char eol[] = AT_RECV_EOL_1 AT_RECV_EOL_2;
|
const char eol[] = AT_RECV_EOL_1 AT_RECV_EOL_2;
|
||||||
assert(sizeof(eol) > 1);
|
assert(sizeof(eol) > 1);
|
||||||
|
|
||||||
ssize_t res = -1;
|
ssize_t res = 0;
|
||||||
char *resp_pos = resp_buf;
|
char *resp_pos = resp_buf;
|
||||||
|
|
||||||
#if IS_USED(MODULE_AT_URC_ISR)
|
#if IS_USED(MODULE_AT_URC_ISR)
|
||||||
@ -458,8 +503,15 @@ ssize_t at_readline(at_dev_t *dev, char *resp_buf, size_t len, bool keep_eol, ui
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
memset(resp_buf, 0, len);
|
memset(resp_buf, 0, len);
|
||||||
|
size_t substr_len = 0;
|
||||||
while (len) {
|
if (substr) {
|
||||||
|
substr_len = strlen(substr);
|
||||||
|
if (substr_len == 0) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char const *substr_p = resp_buf;
|
||||||
|
while (len > 1) {
|
||||||
int read_res;
|
int read_res;
|
||||||
if ((read_res = isrpipe_read_timeout(&dev->isrpipe, (uint8_t *)resp_pos,
|
if ((read_res = isrpipe_read_timeout(&dev->isrpipe, (uint8_t *)resp_pos,
|
||||||
1, timeout)) == 1) {
|
1, timeout)) == 1) {
|
||||||
@ -473,12 +525,19 @@ ssize_t at_readline(at_dev_t *dev, char *resp_buf, size_t len, bool keep_eol, ui
|
|||||||
}
|
}
|
||||||
if (*resp_pos == eol[sizeof(eol) - 2]) {
|
if (*resp_pos == eol[sizeof(eol) - 2]) {
|
||||||
*resp_pos = '\0';
|
*resp_pos = '\0';
|
||||||
res = resp_pos - resp_buf;
|
break;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resp_pos += read_res;
|
resp_pos += read_res;
|
||||||
len -= read_res;
|
len -= read_res;
|
||||||
|
|
||||||
|
if (substr && (size_t)(resp_pos - resp_buf) >= substr_len) {
|
||||||
|
if (strncmp(substr_p, substr, substr_len) == 0) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
substr_p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (read_res == -ETIMEDOUT) {
|
else if (read_res == -ETIMEDOUT) {
|
||||||
res = -ETIMEDOUT;
|
res = -ETIMEDOUT;
|
||||||
@ -486,28 +545,59 @@ ssize_t at_readline(at_dev_t *dev, char *resp_buf, size_t len, bool keep_eol, ui
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
|
||||||
#if IS_USED(MODULE_AT_URC_ISR)
|
#if IS_USED(MODULE_AT_URC_ISR)
|
||||||
dev->awaiting_response = false;
|
dev->awaiting_response = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
*resp_buf = '\0';
|
*resp_buf = '\0';
|
||||||
|
} else {
|
||||||
|
res = resp_pos - resp_buf;
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ssize_t at_readline(at_dev_t *dev, char *resp_buf, size_t len, bool keep_eol,
|
||||||
|
uint32_t timeout)
|
||||||
|
{
|
||||||
|
return at_readline_stop_at_str(dev, resp_buf, len, keep_eol, NULL, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t at_readline_skip_empty_stop_at_str(at_dev_t *dev, char *resp_buf,
|
||||||
|
size_t len, bool keep_eol,
|
||||||
|
char const *substr, uint32_t timeout)
|
||||||
|
{
|
||||||
|
ssize_t res = at_readline_stop_at_str(dev, resp_buf, len, keep_eol, substr, timeout);
|
||||||
|
if (res == 0) {
|
||||||
|
/* skip possible empty line */
|
||||||
|
res = at_readline_stop_at_str(dev, resp_buf, len, keep_eol, substr, timeout);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
|
||||||
|
}
|
||||||
ssize_t at_readline_skip_empty(at_dev_t *dev, char *resp_buf, size_t len,
|
ssize_t at_readline_skip_empty(at_dev_t *dev, char *resp_buf, size_t len,
|
||||||
bool keep_eol, uint32_t timeout)
|
bool keep_eol, uint32_t timeout)
|
||||||
{
|
{
|
||||||
ssize_t res = at_readline(dev, resp_buf, len, keep_eol, timeout);
|
return at_readline_skip_empty_stop_at_str(dev, resp_buf, len, keep_eol, NULL, timeout);
|
||||||
if (res == 0) {
|
|
||||||
/* skip possible empty line */
|
|
||||||
res = at_readline(dev, resp_buf, len, keep_eol, timeout);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int at_wait_ok(at_dev_t *dev, uint32_t timeout)
|
||||||
|
{
|
||||||
|
while (1) {
|
||||||
|
ssize_t res = at_readline_skip_empty(dev, dev->rp_buf, dev->rp_buf_size,
|
||||||
|
false, timeout);
|
||||||
|
if (res < 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
res = at_parse_resp(dev, dev->rp_buf);
|
||||||
|
if (res < 1) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#ifdef MODULE_AT_URC
|
||||||
|
clist_foreach(&dev->urc_list, _check_urc, dev->rp_buf);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
#ifdef MODULE_AT_URC
|
#ifdef MODULE_AT_URC
|
||||||
void at_add_urc(at_dev_t *dev, at_urc_t *urc)
|
void at_add_urc(at_dev_t *dev, at_urc_t *urc)
|
||||||
{
|
{
|
||||||
@ -531,7 +621,7 @@ static int _check_urc(clist_node_t *node, void *arg)
|
|||||||
|
|
||||||
DEBUG("Trying to match with %s\n", urc->code);
|
DEBUG("Trying to match with %s\n", urc->code);
|
||||||
|
|
||||||
if (strncmp(buf, urc->code, strlen(urc->code)) == 0) {
|
if (starts_with(buf, urc->code)) {
|
||||||
urc->cb(urc->arg, buf);
|
urc->cb(urc->arg, buf);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,10 +17,10 @@
|
|||||||
* intended to send, and bail out if there's no match.
|
* intended to send, and bail out if there's no match.
|
||||||
*
|
*
|
||||||
* Furthermore, the library tries to cope with difficulties regarding different
|
* Furthermore, the library tries to cope with difficulties regarding different
|
||||||
* line endings. It usually sends "<command><CR>", but expects
|
* line endings. It usually sends `<command><CR>`, but expects
|
||||||
* "<command>\LF\CR" as echo.
|
* `<command>\LF\CR` as echo.
|
||||||
*
|
*
|
||||||
* As a debugging aid, when compiled with "-DAT_PRINT_INCOMING=1", every input
|
* As a debugging aid, when compiled with `-DAT_PRINT_INCOMING=1`, every input
|
||||||
* byte gets printed.
|
* byte gets printed.
|
||||||
*
|
*
|
||||||
* ## Unsolicited Result Codes (URC) ##
|
* ## Unsolicited Result Codes (URC) ##
|
||||||
@ -44,6 +44,24 @@
|
|||||||
* character is detected and there is no pending response. This works by posting
|
* character is detected and there is no pending response. This works by posting
|
||||||
* an @ref sys_event "event" to an event thread that processes the URCs.
|
* an @ref sys_event "event" to an event thread that processes the URCs.
|
||||||
*
|
*
|
||||||
|
* ## Error reporting ##
|
||||||
|
* Most DCEs (Data Circuit-terminating Equipment, aka modem) can return extra error
|
||||||
|
* information instead of the rather opaque "ERROR" message. They have the form:
|
||||||
|
* - `+CMS ERROR: err_code>` for SMS-related commands
|
||||||
|
* - `+CME ERROR: <err_code>` for other commands
|
||||||
|
*
|
||||||
|
* For `+CME`, this behavior is usually off by default and can be toggled with:
|
||||||
|
* `AT+CMEE=<type>`
|
||||||
|
* where `<type>` may be:
|
||||||
|
* - 0 disable extended error reporting, return `ERROR`
|
||||||
|
* - 1 enable extended error reporting, with `<err_code>` integer
|
||||||
|
* - 2 enable extended error reporting, with `<err_code>` as string
|
||||||
|
* Check your DCE's manual for more information.
|
||||||
|
*
|
||||||
|
* Some of the API calls below support detailed error reporting. Whenever they
|
||||||
|
* detect extended error responses, -AT_ERR_EXTENDED is returned and `<err_code>`
|
||||||
|
* can be retrieved by calling @ref at_get_err_info().
|
||||||
|
*
|
||||||
* @{
|
* @{
|
||||||
*
|
*
|
||||||
* @file
|
* @file
|
||||||
@ -168,6 +186,9 @@ typedef struct {
|
|||||||
|
|
||||||
#endif /* MODULE_AT_URC */
|
#endif /* MODULE_AT_URC */
|
||||||
|
|
||||||
|
/** Error cause can be further investigated. */
|
||||||
|
#define AT_ERR_EXTENDED 200
|
||||||
|
|
||||||
/** Shortcut for getting send end of line length */
|
/** Shortcut for getting send end of line length */
|
||||||
#define AT_SEND_EOL_LEN (sizeof(CONFIG_AT_SEND_EOL) - 1)
|
#define AT_SEND_EOL_LEN (sizeof(CONFIG_AT_SEND_EOL) - 1)
|
||||||
|
|
||||||
@ -177,6 +198,8 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
isrpipe_t isrpipe; /**< isrpipe used for getting data from uart */
|
isrpipe_t isrpipe; /**< isrpipe used for getting data from uart */
|
||||||
uart_t uart; /**< UART device where the AT device is attached */
|
uart_t uart; /**< UART device where the AT device is attached */
|
||||||
|
char *rp_buf; /**< response parsing buffer */
|
||||||
|
size_t rp_buf_size; /**< response parsing buffer size */
|
||||||
#ifdef MODULE_AT_URC
|
#ifdef MODULE_AT_URC
|
||||||
clist_node_t urc_list; /**< list to keep track of all registered urc's */
|
clist_node_t urc_list; /**< list to keep track of all registered urc's */
|
||||||
#ifdef MODULE_AT_URC_ISR
|
#ifdef MODULE_AT_URC_ISR
|
||||||
@ -186,19 +209,49 @@ typedef struct {
|
|||||||
#endif
|
#endif
|
||||||
} at_dev_t;
|
} at_dev_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief AT device initialization parameters
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uart_t uart; /**< UART device where the AT device is attached */
|
||||||
|
uint32_t baudrate; /**< UART device baudrate */
|
||||||
|
char *rx_buf; /**< UART rx buffer */
|
||||||
|
size_t rx_buf_size; /**< UART rx buffer size */
|
||||||
|
/**
|
||||||
|
* Response parsing buffer - used for classifying DCE responses and holding
|
||||||
|
* detailed error information. Must be at least 16 bytes.
|
||||||
|
* If you don't care about URCs (MODULE_AT_URC is undefined) this must only
|
||||||
|
* be large enough to hold responses like `OK`, `ERROR` or `+CME ERROR: <err_code>`.
|
||||||
|
* Otherwise adapt its size to the maximum length of the URCs you are expecting
|
||||||
|
* and actually care about. */
|
||||||
|
char *rp_buf;
|
||||||
|
size_t rp_buf_size; /**< response parsing buffer size */
|
||||||
|
} at_dev_init_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get extended error information of the last command sent.
|
||||||
|
*
|
||||||
|
* If a previous at_* method returned with -AT_ERR_EXTENDED, you can retrieve
|
||||||
|
* a pointer to the error string with this.
|
||||||
|
*
|
||||||
|
* @param[in] dev device to operate on
|
||||||
|
*
|
||||||
|
* @retval string containing the error value.
|
||||||
|
*/
|
||||||
|
static inline char const *at_get_err_info(at_dev_t *dev)
|
||||||
|
{
|
||||||
|
return dev->rp_buf;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @brief Initialize AT device struct
|
* @brief Initialize AT device struct
|
||||||
*
|
*
|
||||||
* @param[in] dev struct to initialize
|
* @param[in] dev struct to initialize
|
||||||
* @param[in] uart UART the device is connected to
|
* @param[in] init init struct, may be destroyed after return
|
||||||
* @param[in] baudrate baudrate of the device
|
|
||||||
* @param[in] buf input buffer
|
|
||||||
* @param[in] bufsize size of @p buf
|
|
||||||
*
|
*
|
||||||
* @retval success code UART_OK on success
|
* @retval success code UART_OK on success
|
||||||
* @retval error code UART_NODEV or UART_NOBAUD otherwise
|
* @retval error code UART_NODEV or UART_NOBAUD otherwise
|
||||||
*/
|
*/
|
||||||
int at_dev_init(at_dev_t *dev, uart_t uart, uint32_t baudrate, char *buf, size_t bufsize);
|
int at_dev_init(at_dev_t *dev, at_dev_init_t const *init);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Simple command helper
|
* @brief Simple command helper
|
||||||
@ -210,7 +263,9 @@ int at_dev_init(at_dev_t *dev, uart_t uart, uint32_t baudrate, char *buf, size_t
|
|||||||
* @param[in] timeout timeout (in usec)
|
* @param[in] timeout timeout (in usec)
|
||||||
*
|
*
|
||||||
* @retval 0 when device answers "OK"
|
* @retval 0 when device answers "OK"
|
||||||
* @retval <0 otherwise
|
* @retval -AT_ERR_EXTENDED if failed and a error code can be retrieved with
|
||||||
|
* @ref at_get_err_info() (i.e. DCE answered with `CMx ERROR`)
|
||||||
|
* @retval <0 other failures
|
||||||
*/
|
*/
|
||||||
int at_send_cmd_wait_ok(at_dev_t *dev, const char *command, uint32_t timeout);
|
int at_send_cmd_wait_ok(at_dev_t *dev, const char *command, uint32_t timeout);
|
||||||
|
|
||||||
@ -225,7 +280,9 @@ int at_send_cmd_wait_ok(at_dev_t *dev, const char *command, uint32_t timeout);
|
|||||||
* @param[in] timeout timeout (in usec)
|
* @param[in] timeout timeout (in usec)
|
||||||
*
|
*
|
||||||
* @retval 0 when prompt is received
|
* @retval 0 when prompt is received
|
||||||
* @retval <0 otherwise
|
* @retval -AT_ERR_EXTENDED if failed and a error code can be retrieved with
|
||||||
|
* @ref at_get_err_info() (i.e. DCE answered with `CMx ERROR`)
|
||||||
|
* @retval <0 other failures
|
||||||
*/
|
*/
|
||||||
int at_send_cmd_wait_prompt(at_dev_t *dev, const char *command, uint32_t timeout);
|
int at_send_cmd_wait_prompt(at_dev_t *dev, const char *command, uint32_t timeout);
|
||||||
|
|
||||||
@ -246,7 +303,8 @@ int at_send_cmd_wait_prompt(at_dev_t *dev, const char *command, uint32_t timeout
|
|||||||
* @retval n length of response on success
|
* @retval n length of response on success
|
||||||
* @retval <0 on error
|
* @retval <0 on error
|
||||||
*/
|
*/
|
||||||
ssize_t at_send_cmd_get_resp(at_dev_t *dev, const char *command, char *resp_buf, size_t len, uint32_t timeout);
|
ssize_t at_send_cmd_get_resp(at_dev_t *dev, const char *command, char *resp_buf,
|
||||||
|
size_t len, uint32_t timeout);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send AT command, wait for response plus OK
|
* @brief Send AT command, wait for response plus OK
|
||||||
@ -264,7 +322,9 @@ ssize_t at_send_cmd_get_resp(at_dev_t *dev, const char *command, char *resp_buf,
|
|||||||
* @param[in] timeout timeout (in usec)
|
* @param[in] timeout timeout (in usec)
|
||||||
*
|
*
|
||||||
* @retval n length of response on success
|
* @retval n length of response on success
|
||||||
* @retval <0 on error
|
* @retval -AT_ERR_EXTENDED if failed and a error code can be retrieved with
|
||||||
|
* @ref at_get_err_info() (i.e. DCE answered with `CMx ERROR`)
|
||||||
|
* @retval <0 other failures
|
||||||
*/
|
*/
|
||||||
ssize_t at_send_cmd_get_resp_wait_ok(at_dev_t *dev, const char *command, const char *resp_prefix,
|
ssize_t at_send_cmd_get_resp_wait_ok(at_dev_t *dev, const char *command, const char *resp_prefix,
|
||||||
char *resp_buf, size_t len, uint32_t timeout);
|
char *resp_buf, size_t len, uint32_t timeout);
|
||||||
@ -275,9 +335,8 @@ ssize_t at_send_cmd_get_resp_wait_ok(at_dev_t *dev, const char *command, const c
|
|||||||
* This function sends the supplied @p command, then returns all response
|
* This function sends the supplied @p command, then returns all response
|
||||||
* lines until the device sends "OK".
|
* lines until the device sends "OK".
|
||||||
*
|
*
|
||||||
* If a line starts with "ERROR" or the buffer is full, the function returns -1.
|
* If a line contains a DTE error response, this function stops and returns
|
||||||
* If a line starts with "+CME ERROR" or +CMS ERROR", the function returns -2.
|
* accordingly.
|
||||||
* In this case resp_buf contains the error string.
|
|
||||||
*
|
*
|
||||||
* @param[in] dev device to operate on
|
* @param[in] dev device to operate on
|
||||||
* @param[in] command command to send
|
* @param[in] command command to send
|
||||||
@ -287,8 +346,9 @@ ssize_t at_send_cmd_get_resp_wait_ok(at_dev_t *dev, const char *command, const c
|
|||||||
* @param[in] timeout timeout (in usec)
|
* @param[in] timeout timeout (in usec)
|
||||||
*
|
*
|
||||||
* @retval n length of response on success
|
* @retval n length of response on success
|
||||||
* @retval -1 on error
|
* @retval -AT_ERR_EXTENDED if failed and a error code can be retrieved with
|
||||||
* @retval -2 on CMS or CME error
|
* @ref at_get_err_info() (i.e. DCE answered with `CMx ERROR`)
|
||||||
|
* @retval <0 other failures
|
||||||
*/
|
*/
|
||||||
ssize_t at_send_cmd_get_lines(at_dev_t *dev, const char *command, char *resp_buf,
|
ssize_t at_send_cmd_get_lines(at_dev_t *dev, const char *command, char *resp_buf,
|
||||||
size_t len, bool keep_eol, uint32_t timeout);
|
size_t len, bool keep_eol, uint32_t timeout);
|
||||||
@ -369,6 +429,22 @@ ssize_t at_recv_bytes(at_dev_t *dev, char *bytes, size_t len, uint32_t timeout);
|
|||||||
*/
|
*/
|
||||||
int at_send_cmd(at_dev_t *dev, const char *command, uint32_t timeout);
|
int at_send_cmd(at_dev_t *dev, const char *command, uint32_t timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parse a response from the device.
|
||||||
|
*
|
||||||
|
* This is always called automatically in functions that may return -AT_ERR_EXTENDED.
|
||||||
|
* However, if you read the response by other methods (e.g. with @ref at_recv_bytes()),
|
||||||
|
* you might want to call this on the response so that you don't have to parse it yourself.
|
||||||
|
*
|
||||||
|
* @retval 0 if the response is "OK"
|
||||||
|
* @retval -AT_ERR_EXTENDED if the response is `+CMx ERROR: <err>`, and `<err>`
|
||||||
|
* has been successfully copied to @p dev->rp_buf
|
||||||
|
* @retval -1 if the response is "ERROR", or `+CMx ERROR: <err>` but `<err>`
|
||||||
|
* could not be copied
|
||||||
|
* @retval 1 otherwise
|
||||||
|
*/
|
||||||
|
int at_parse_resp(at_dev_t *dev, char const *resp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read a line from device
|
* @brief Read a line from device
|
||||||
*
|
*
|
||||||
@ -398,6 +474,21 @@ ssize_t at_readline(at_dev_t *dev, char *resp_buf, size_t len, bool keep_eol, ui
|
|||||||
ssize_t at_readline_skip_empty(at_dev_t *dev, char *resp_buf, size_t len,
|
ssize_t at_readline_skip_empty(at_dev_t *dev, char *resp_buf, size_t len,
|
||||||
bool keep_eol, uint32_t timeout);
|
bool keep_eol, uint32_t timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Wait for an OK response.
|
||||||
|
*
|
||||||
|
* Useful when crafting the command-response sequence by yourself.
|
||||||
|
*
|
||||||
|
* @param[in] dev device to operate on
|
||||||
|
* @param[in] timeout timeout (in usec)
|
||||||
|
*
|
||||||
|
* @retval 0 when device answers "OK"
|
||||||
|
* @retval -AT_ERR_EXTENDED if failed and a error code can be retrieved with
|
||||||
|
* @ref at_get_err_info() (i.e. DCE answered with `CMx ERROR`)
|
||||||
|
* @retval <0 other failures
|
||||||
|
*/
|
||||||
|
int at_wait_ok(at_dev_t *dev, uint32_t timeout);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Drain device input buffer
|
* @brief Drain device input buffer
|
||||||
*
|
*
|
||||||
|
|||||||
@ -2,7 +2,14 @@ include ../Makefile.drivers_common
|
|||||||
|
|
||||||
USEMODULE += shell
|
USEMODULE += shell
|
||||||
USEMODULE += at
|
USEMODULE += at
|
||||||
USEMODULE += at_urc_isr_medium
|
USEMODULE += at_urc
|
||||||
|
|
||||||
|
# Enable if the DCE is sending only \n for EOL
|
||||||
|
# CFLAGS += -DAT_RECV_EOL_1=""
|
||||||
|
|
||||||
|
# Enable this to test with echo off. Don't forget to disable echo in
|
||||||
|
# 'tests-with-config/emulated_dce.py' too!
|
||||||
|
# CFLAGS += -DCONFIG_AT_SEND_SKIP_ECHO=1
|
||||||
|
|
||||||
# we are printing from the event thread, we need more stack
|
# we are printing from the event thread, we need more stack
|
||||||
CFLAGS += -DEVENT_THREAD_MEDIUM_STACKSIZE=1024
|
CFLAGS += -DEVENT_THREAD_MEDIUM_STACKSIZE=1024
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
* @}
|
* @}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -33,6 +34,7 @@
|
|||||||
static at_dev_t at_dev;
|
static at_dev_t at_dev;
|
||||||
static char buf[256];
|
static char buf[256];
|
||||||
static char resp[1024];
|
static char resp[1024];
|
||||||
|
static char rp_buf[256];
|
||||||
|
|
||||||
static int init(int argc, char **argv)
|
static int init(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@ -51,9 +53,15 @@ static int init(int argc, char **argv)
|
|||||||
printf("Wrong UART device number - should be in range 0-%d.\n", UART_NUMOF - 1);
|
printf("Wrong UART device number - should be in range 0-%d.\n", UART_NUMOF - 1);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
at_dev_init_t at_init_params = {
|
||||||
int res = at_dev_init(&at_dev, UART_DEV(uart), baudrate, buf, sizeof(buf));
|
.baudrate = baudrate,
|
||||||
|
.rp_buf = rp_buf,
|
||||||
|
.rp_buf_size = sizeof(rp_buf),
|
||||||
|
.rx_buf = buf,
|
||||||
|
.rx_buf_size = sizeof(buf),
|
||||||
|
.uart = UART_DEV(uart),
|
||||||
|
};
|
||||||
|
int res = at_dev_init(&at_dev, &at_init_params);
|
||||||
/* check the UART initialization return value and respond as needed */
|
/* check the UART initialization return value and respond as needed */
|
||||||
if (res == UART_NODEV) {
|
if (res == UART_NODEV) {
|
||||||
puts("Invalid UART device given!");
|
puts("Invalid UART device given!");
|
||||||
@ -110,7 +118,8 @@ static int send_lines(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
if ((len = at_send_cmd_get_lines(&at_dev, argv[1], resp, sizeof(resp), true, 10 * US_PER_SEC)) < 0) {
|
if ((len = at_send_cmd_get_lines(&at_dev, argv[1], resp, sizeof(resp),
|
||||||
|
true, 10 * US_PER_SEC)) < 0) {
|
||||||
puts("Error");
|
puts("Error");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -285,7 +294,7 @@ static int remove_urc(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int sneaky_urc(int argc, char **argv)
|
static int emulate_dce(int argc, char **argv)
|
||||||
{
|
{
|
||||||
(void)argc;
|
(void)argc;
|
||||||
(void)argv;
|
(void)argv;
|
||||||
@ -299,9 +308,21 @@ static int sneaky_urc(int argc, char **argv)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
res = at_send_cmd_wait_ok(&at_dev, "AT+CFUN=1", US_PER_SEC);
|
res = at_send_cmd_wait_ok(&at_dev, "AT+CFUN=1", US_PER_SEC);
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
puts("Error AT+CFUN=1");
|
printf("%u: Error AT+CFUN=1: %d\n", __LINE__, res);
|
||||||
|
res = 1;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = at_send_cmd(&at_dev, "AT+CFUN=1", US_PER_SEC);
|
||||||
|
if (res) {
|
||||||
|
printf("%u: Error AT+CFUN=1: %d\n", __LINE__, res);
|
||||||
|
res = 1;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
res = at_wait_ok(&at_dev, US_PER_SEC);
|
||||||
|
if (res) {
|
||||||
|
printf("%u: Error AT+CFUN=1: %d\n", __LINE__, res);
|
||||||
res = 1;
|
res = 1;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@ -310,30 +331,62 @@ static int sneaky_urc(int argc, char **argv)
|
|||||||
"+CEREG:", resp_buf,
|
"+CEREG:", resp_buf,
|
||||||
sizeof(resp_buf), US_PER_SEC);
|
sizeof(resp_buf), US_PER_SEC);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
puts("Error AT+CEREG?");
|
printf("%u: Error AT+CEREG?: %d\n", __LINE__, res);
|
||||||
res = 1;
|
res = 1;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = at_send_cmd_wait_prompt(&at_dev, "AT+USECMNG=0,0,\"cert\",128", US_PER_SEC);
|
res = at_send_cmd_wait_prompt(&at_dev, "AT+USECMNG=0,0,\"cert\",128", US_PER_SEC);
|
||||||
if (res) {
|
if (res) {
|
||||||
puts("Error AT+USECMNG");
|
printf("%u: Error AT+USECMNG: %d\n", __LINE__, res);
|
||||||
|
res = 1;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = at_send_cmd_wait_prompt(&at_dev, "AT+PROMPTERROR", US_PER_SEC);
|
||||||
|
if (res != -AT_ERR_EXTENDED) {
|
||||||
|
printf("%u: Error AT+PROMPTERROR: %d\n", __LINE__, res);
|
||||||
|
res = 1;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
res = atol(at_get_err_info(&at_dev));
|
||||||
|
if (res != 1984) {
|
||||||
|
printf("%u: Error AT+PROMPTERROR: %d\n", __LINE__, res);
|
||||||
res = 1;
|
res = 1;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = at_send_cmd_wait_ok(&at_dev, "AT+CFUN=8", US_PER_SEC);
|
res = at_send_cmd_wait_ok(&at_dev, "AT+CFUN=8", US_PER_SEC);
|
||||||
|
|
||||||
if (res != -1) {
|
if (res != -1) {
|
||||||
puts("Error AT+CFUN=8");
|
printf("%u: Error AT+CFUN=8: %d\n", __LINE__, res);
|
||||||
res = 1;
|
res = 1;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = at_send_cmd_wait_ok(&at_dev, "AT+CFUN=9", US_PER_SEC);
|
res = at_send_cmd_wait_ok(&at_dev, "AT+CFUN=9", US_PER_SEC);
|
||||||
|
if (res != -AT_ERR_EXTENDED) {
|
||||||
|
printf("%u: Error AT+CFUN=9: %d\n", __LINE__, res);
|
||||||
|
res = 1;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
if (res != -2) {
|
res = atol(at_get_err_info(&at_dev));
|
||||||
puts("Error AT+CFUN=9");
|
if (res != 666) {
|
||||||
|
printf("%u: Error AT+CFUN=9: %d\n", __LINE__, res);
|
||||||
|
res = 1;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = at_send_cmd_get_lines(&at_dev, "AT+GETTWOLINES", resp_buf,
|
||||||
|
sizeof(resp_buf), false, US_PER_SEC);
|
||||||
|
if (res < 0) {
|
||||||
|
printf("%u: Error AT+GETTWOLINES: %d\n", __LINE__, res);
|
||||||
|
res = 1;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(resp_buf, "first_line\nsecond_line\nOK")) {
|
||||||
|
printf("%u: Error AT+GETTWOLINES: response not matching\n", __LINE__);
|
||||||
res = 1;
|
res = 1;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@ -343,6 +396,7 @@ exit:
|
|||||||
#ifdef MODULE_AT_URC
|
#ifdef MODULE_AT_URC
|
||||||
at_remove_urc(&at_dev, &urc);
|
at_remove_urc(&at_dev, &urc);
|
||||||
#endif
|
#endif
|
||||||
|
printf("%s finished with %d\n", __func__, res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,7 +411,7 @@ static const shell_command_t shell_commands[] = {
|
|||||||
{ "drain", "Drain AT device", drain },
|
{ "drain", "Drain AT device", drain },
|
||||||
{ "power_on", "Power on AT device", power_on },
|
{ "power_on", "Power on AT device", power_on },
|
||||||
{ "power_off", "Power off AT device", power_off },
|
{ "power_off", "Power off AT device", power_off },
|
||||||
{ "sneaky_urc", "Test sneaky URC interference", sneaky_urc},
|
{ "emulate_dce", "Test against the DCE emulation script.", emulate_dce},
|
||||||
#ifdef MODULE_AT_URC
|
#ifdef MODULE_AT_URC
|
||||||
{ "add_urc", "Register an URC", add_urc },
|
{ "add_urc", "Register an URC", add_urc },
|
||||||
{ "remove_urc", "De-register an URC", remove_urc },
|
{ "remove_urc", "De-register an URC", remove_urc },
|
||||||
|
|||||||
@ -13,17 +13,27 @@
|
|||||||
# 1. Adapt the `EOL_IN`, `EOL_OUT`, `ECHO_ON` variables below to match your use case
|
# 1. Adapt the `EOL_IN`, `EOL_OUT`, `ECHO_ON` variables below to match your use case
|
||||||
# 2. Run this script with the baud rate and the serial dev the device is connected
|
# 2. Run this script with the baud rate and the serial dev the device is connected
|
||||||
# to, e.g.:
|
# to, e.g.:
|
||||||
# $ ./sneaky_urc 115200 /dev/ttyUSB0
|
# $ ./emulate_dce.py 115200 /dev/ttyUSB0
|
||||||
# 4. run the test (e.g. make term)
|
# 4. run the test (e.g. make term)
|
||||||
# 5. inside the test console:
|
# 5. inside the test console:
|
||||||
# a) run the `init` command (e.g. init 0 115200)
|
# a) run the `init` command (e.g. init 0 115200)
|
||||||
# b) run `sneaky_urc` command
|
# b) run `emulate_dce` command
|
||||||
#
|
#
|
||||||
# If the command echoing is enabled, you will miss the URCs, but the commands
|
# If the command echoing is enabled, you will miss the URCs, but the commands
|
||||||
# should work (e.g. the URC should not interfere with response parsing).
|
# should work (e.g. the URC should not interfere with response parsing).
|
||||||
#
|
#
|
||||||
# If command echoing is enabled AND `MODULE_AT_URC` is defined, you should see
|
# If command echoing is enabled AND `MODULE_AT_URC` is defined, you should see
|
||||||
# *some* of the URCs being parsed.
|
# *some* of the URCs being parsed.
|
||||||
|
#
|
||||||
|
# The easiest way is to run this test on native, with an socat tty bridge:
|
||||||
|
# a) run `$ socat -d -d pty,raw,echo=0 pty,raw,echo=0`
|
||||||
|
# socat will display the pty endpoints it just created, e.g:
|
||||||
|
# 2024/01/23 10:24:56 socat[45360] N PTY is /dev/pts/9
|
||||||
|
# 2024/01/23 10:24:56 socat[45360] N PTY is /dev/pts/10
|
||||||
|
# b) pick one pty endpoint (e.g. /dev/pts/9) and run this script:
|
||||||
|
# `$ emulate_dce.py 115200 /dev/pts/9`
|
||||||
|
# c) pick the other endpoint and run the compiled binary
|
||||||
|
# ` $ ./bin/native/tests_at.elf -c /dev/pts/10 < tests-with-config/native_stdin`
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import pexpect
|
import pexpect
|
||||||
@ -37,17 +47,20 @@ EOL_IN = '\r'
|
|||||||
# EOL to send back to the device
|
# EOL to send back to the device
|
||||||
EOL_OUT = '\r\n'
|
EOL_OUT = '\r\n'
|
||||||
# Command echoing enabled
|
# Command echoing enabled
|
||||||
ECHO_ON = False
|
ECHO_ON = True
|
||||||
|
|
||||||
CFUN_CMD = "AT+CFUN=1" + EOL_IN
|
CFUN_CMD = "AT+CFUN=1" + EOL_IN
|
||||||
CFUN_ERR_CMD = "AT+CFUN=8" + EOL_IN
|
CFUN_ERR_CMD = "AT+CFUN=8" + EOL_IN
|
||||||
CFUN_CME_CMD = "AT+CFUN=9" + EOL_IN
|
CFUN_CME_CMD = "AT+CFUN=9" + EOL_IN
|
||||||
CEREG_CMD = "AT+CEREG?" + EOL_IN
|
CEREG_CMD = "AT+CEREG?" + EOL_IN
|
||||||
USECMNG_CMD = "AT+USECMNG=0,0,\"cert\",128" + EOL_IN
|
USECMNG_CMD = "AT+USECMNG=0,0,\"cert\",128" + EOL_IN
|
||||||
|
GETTWOLINES_CMD = "AT+GETTWOLINES" + EOL_IN
|
||||||
|
PROMPTERROR_CMD = "AT+PROMPTERROR" + EOL_IN
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
idx = tty.expect_exact([CFUN_CMD, CFUN_ERR_CMD, CFUN_CME_CMD, CEREG_CMD, USECMNG_CMD])
|
idx = tty.expect_exact([CFUN_CMD, CFUN_ERR_CMD, CFUN_CME_CMD, CEREG_CMD, USECMNG_CMD,
|
||||||
|
GETTWOLINES_CMD, PROMPTERROR_CMD])
|
||||||
if idx == 0:
|
if idx == 0:
|
||||||
print(CFUN_CMD)
|
print(CFUN_CMD)
|
||||||
tty.send(EOL_OUT + "+CSCON: 1" + EOL_OUT)
|
tty.send(EOL_OUT + "+CSCON: 1" + EOL_OUT)
|
||||||
@ -65,7 +78,7 @@ while True:
|
|||||||
tty.send(EOL_OUT + "+CSCON: 1" + EOL_OUT)
|
tty.send(EOL_OUT + "+CSCON: 1" + EOL_OUT)
|
||||||
if ECHO_ON:
|
if ECHO_ON:
|
||||||
tty.send(CFUN_CME_CMD)
|
tty.send(CFUN_CME_CMD)
|
||||||
tty.send(EOL_OUT + "+CME ERROR:" + EOL_OUT)
|
tty.send(EOL_OUT + "+CME ERROR: 666" + EOL_OUT)
|
||||||
elif idx == 3:
|
elif idx == 3:
|
||||||
print(CEREG_CMD)
|
print(CEREG_CMD)
|
||||||
tty.send(EOL_OUT + "+CSCON: 1" + EOL_OUT)
|
tty.send(EOL_OUT + "+CSCON: 1" + EOL_OUT)
|
||||||
@ -79,6 +92,16 @@ while True:
|
|||||||
if ECHO_ON:
|
if ECHO_ON:
|
||||||
tty.send(USECMNG_CMD)
|
tty.send(USECMNG_CMD)
|
||||||
tty.send(">")
|
tty.send(">")
|
||||||
|
elif idx == 5:
|
||||||
|
print(GETTWOLINES_CMD)
|
||||||
|
if ECHO_ON:
|
||||||
|
tty.send(GETTWOLINES_CMD)
|
||||||
|
tty.send(EOL_OUT + "first_line" + EOL_OUT + "second_line" + EOL_OUT + "OK" + EOL_OUT)
|
||||||
|
elif idx == 6:
|
||||||
|
print(PROMPTERROR_CMD)
|
||||||
|
if ECHO_ON:
|
||||||
|
tty.send(PROMPTERROR_CMD)
|
||||||
|
tty.send(EOL_OUT + "+CME ERROR: 1984" + EOL_OUT)
|
||||||
except pexpect.EOF:
|
except pexpect.EOF:
|
||||||
print("ERROR: EOF")
|
print("ERROR: EOF")
|
||||||
except pexpect.TIMEOUT:
|
except pexpect.TIMEOUT:
|
||||||
2
tests/drivers/at/tests-with-config/native_stdin
Normal file
2
tests/drivers/at/tests-with-config/native_stdin
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
init 0 115200
|
||||||
|
emulate_dce
|
||||||
Loading…
x
Reference in New Issue
Block a user