Skip to content

Commit 0b46bf3

Browse files
committed
lib: sm_at_client: Multiple fixes
- Fix MON_ANY usage. - Fix monitor matching multiple URCs if they come in the same UART RX buffer. - Fix uninit. - Fix operations before init is done. Signed-off-by: Markus Lassila <markus.lassila@nordicsemi.no>
1 parent 3d21ef1 commit 0b46bf3

2 files changed

Lines changed: 101 additions & 43 deletions

File tree

lib/sm_at_client/sm_at_client.c

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,14 @@ LOG_MODULE_REGISTER(sm_at_client, CONFIG_SM_AT_CLIENT_LOG_LEVEL);
3131

3232
static const struct device *uart_dev = DEVICE_DT_GET(DT_CHOSEN(ncs_sm_uart));
3333

34-
static struct k_work_delayable rx_process_work;
34+
static void rx_process(struct k_work *work);
35+
static void dtr_uart_enable_work_fn(struct k_work *work);
36+
static void dtr_uart_disable_work_fn(struct k_work *work);
37+
38+
K_WORK_DELAYABLE_DEFINE(rx_process_work, rx_process);
39+
K_WORK_DEFINE(dtr_uart_enable_work, dtr_uart_enable_work_fn);
40+
K_WORK_DELAYABLE_DEFINE(dtr_uart_disable_work, dtr_uart_disable_work_fn);
41+
3542
struct rx_buf_t {
3643
atomic_t ref_counter;
3744
uint8_t buf[CONFIG_SM_AT_CLIENT_UART_RX_BUF_SIZE];
@@ -73,6 +80,7 @@ static K_SEM_DEFINE(at_rsp, 0, 1);
7380

7481
static sm_data_handler_t data_handler;
7582
static enum at_cmd_state sm_at_state;
83+
static bool initialized;
7684

7785
#if defined(CONFIG_SM_AT_CLIENT_SHELL)
7886
static const struct shell *global_shell;
@@ -97,10 +105,6 @@ struct dtr_config {
97105

98106
/* Current DTR state. */
99107
bool active;
100-
101-
/* Work items for enabling and disabling DTR UART. */
102-
struct k_work dtr_uart_enable_work;
103-
struct k_work_delayable dtr_uart_disable_work;
104108
};
105109

106110
static struct dtr_config dtr_config = {
@@ -446,10 +450,10 @@ static void reschedule_disable(void)
446450
{
447451
if (dtr_config.active && dtr_config.automatic) {
448452
/* Restart the inactivity timer. */
449-
k_work_reschedule(&dtr_config.dtr_uart_disable_work, dtr_config.inactivity);
453+
k_work_reschedule(&dtr_uart_disable_work, dtr_config.inactivity);
450454
} else {
451455
/* Stop the inactivity timer. */
452-
k_work_cancel_delayable(&dtr_config.dtr_uart_disable_work);
456+
k_work_cancel_delayable(&dtr_uart_disable_work);
453457
}
454458
}
455459

@@ -713,7 +717,7 @@ static void gpio_cb_func(const struct device *dev, struct gpio_callback *gpio_cb
713717

714718
if (dtr_config.automatic && !dtr_config.active) {
715719
/* Wake up the application */
716-
k_work_submit(&dtr_config.dtr_uart_enable_work);
720+
k_work_submit(&dtr_uart_enable_work);
717721
}
718722

719723
if (ri_handler) {
@@ -787,7 +791,6 @@ static int gpio_init(void)
787791
int sm_at_client_init(sm_data_handler_t handler, bool automatic, k_timeout_t inactivity)
788792
{
789793
int err;
790-
static bool initialized;
791794

792795
if (initialized) {
793796
return -EALREADY;
@@ -810,11 +813,6 @@ int sm_at_client_init(sm_data_handler_t handler, bool automatic, k_timeout_t ina
810813
return -EFAULT;
811814
}
812815

813-
k_work_init_delayable(&rx_process_work, rx_process);
814-
815-
k_work_init(&dtr_config.dtr_uart_enable_work, dtr_uart_enable_work_fn);
816-
k_work_init_delayable(&dtr_config.dtr_uart_disable_work, dtr_uart_disable_work_fn);
817-
818816
/* Initialize shell pointer so it's available for printing in callbacks */
819817
#if defined(CONFIG_SHELL_BACKEND_SERIAL)
820818
global_shell = shell_backend_uart_get_ptr();
@@ -848,6 +846,7 @@ int sm_at_client_uninit(void)
848846
data_handler = NULL;
849847
ri_handler = NULL;
850848
sm_at_state = AT_CMD_OK;
849+
initialized = false;
851850

852851
return 0;
853852
}
@@ -868,6 +867,11 @@ int sm_at_client_send_cmd(const char *const command, uint32_t timeout)
868867
{
869868
int ret;
870869

870+
if (!initialized) {
871+
LOG_ERR("AT client not initialized");
872+
return -EPERM;
873+
}
874+
871875
sm_at_state = AT_CMD_PENDING;
872876
ret = tx_write(command, strlen(command), false);
873877
if (ret < 0) {
@@ -900,32 +904,52 @@ int sm_at_client_send_cmd(const char *const command, uint32_t timeout)
900904

901905
int sm_at_client_send_data(const uint8_t *const data, size_t datalen)
902906
{
907+
if (!initialized) {
908+
LOG_ERR("AT client not initialized");
909+
return -EPERM;
910+
}
911+
903912
return tx_write(data, datalen, true);
904913
}
905914

906915
void sm_at_client_configure_dtr_uart(bool automatic, k_timeout_t inactivity)
907916
{
917+
if (!initialized) {
918+
LOG_ERR("AT client not initialized");
919+
return;
920+
}
921+
908922
dtr_config.automatic = automatic;
909923
dtr_config.inactivity = inactivity;
910924

911925
if (dtr_config.automatic && !dtr_config.active && !ring_buf_is_empty(&tx_buf)) {
912926
/* If automatic DTR UART is enabled and there is data to send, enable DTR UART. */
913-
k_work_submit(&dtr_config.dtr_uart_enable_work);
927+
k_work_submit(&dtr_uart_enable_work);
914928
} else {
915929
reschedule_disable();
916930
}
917931
}
918932

919933
void sm_at_client_disable_dtr_uart(void)
920934
{
935+
if (!initialized) {
936+
LOG_ERR("AT client not initialized");
937+
return;
938+
}
939+
921940
sm_at_client_configure_dtr_uart(false, K_NO_WAIT);
922-
k_work_reschedule(&dtr_config.dtr_uart_disable_work, K_NO_WAIT);
941+
k_work_reschedule(&dtr_uart_disable_work, K_NO_WAIT);
923942
}
924943

925944
void sm_at_client_enable_dtr_uart(void)
926945
{
946+
if (!initialized) {
947+
LOG_ERR("AT client not initialized");
948+
return;
949+
}
950+
927951
sm_at_client_configure_dtr_uart(false, K_NO_WAIT);
928-
k_work_submit(&dtr_config.dtr_uart_enable_work);
952+
k_work_submit(&dtr_uart_enable_work);
929953
}
930954

931955
#if defined(CONFIG_SM_AT_CLIENT_SHELL)

lib/sm_at_client/sm_at_client_monitor.c

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -39,42 +39,76 @@ void sm_monitor_dispatch(const char *notif, size_t len)
3939
struct at_notif_fifo *at_notif;
4040
size_t sz_needed;
4141
size_t notif_len = 0;
42-
char *match = NULL;
42+
const char *current = notif;
43+
const char *end = notif + len;
44+
bool queued_any = false;
4345

4446
/* TODO:
45-
* To reliably separate AT notifications from AT commands, data and other
46-
* AT notifications would require that the AT notifications are separated with control
47-
* characters or possibly sent with a separate CMUX channel.
47+
* - Not called if URC comes immediately after sending an AT command.
48+
* ATE1 should be used, and echoed AT-command should be matched to received data to
49+
* deduct the start of the AT response.
50+
* - Cannot handle the case where URC is split over multiple UART RX buffers.
51+
* - Cannot separate URC from data mode data which may also contain \r\n\ sequences.
4852
*/
49-
STRUCT_SECTION_FOREACH(sm_monitor_entry, e) {
50-
if (!is_paused(e)) {
51-
match = strnstr(notif, e->filter, len);
52-
if (match) {
53-
notif_len = len - (size_t)(match - notif);
53+
54+
while (current < end) {
55+
/* Process each notification in the buffer.
56+
* Notifications are delimited by \r\n\r\n (end of one + start of next).
57+
*/
58+
const char *next_delim = strnstr(current, "\r\n\r\n", end - current);
59+
bool has_monitor_match = false;
60+
61+
if (next_delim) {
62+
/* Include the trailing \r\n in this notification */
63+
notif_len = (next_delim - current) + 2;
64+
} else {
65+
/* Last notification in buffer */
66+
notif_len = end - current;
67+
}
68+
69+
if (notif_len == 0) {
70+
break;
71+
}
72+
73+
/* Check if any monitor is interested in this notification. */
74+
STRUCT_SECTION_FOREACH(sm_monitor_entry, e) {
75+
if (!is_paused(e)) {
76+
if (e->filter == MON_ANY) {
77+
has_monitor_match = true;
78+
break;
79+
}
80+
if (strnstr(current, e->filter, notif_len)) {
81+
has_monitor_match = true;
82+
break;
83+
}
84+
}
85+
}
86+
87+
if (has_monitor_match) {
88+
sz_needed = sizeof(struct at_notif_fifo) + notif_len + sizeof(char);
89+
at_notif = k_heap_alloc(&at_monitor_heap, sz_needed, K_NO_WAIT);
90+
if (!at_notif) {
91+
LOG_WRN("No heap space for incoming notification");
92+
/* Submit work for already queued notifications and stop. */
5493
break;
5594
}
95+
strncpy(at_notif->data, current, notif_len);
96+
at_notif->data[notif_len] = '\0';
97+
k_fifo_put(&at_monitor_fifo, at_notif);
98+
queued_any = true;
5699
}
57-
}
58100

59-
if (!match) {
60-
/* Only copy monitored notifications to save heap */
61-
return;
101+
/* Move to next notification (skip the \r\n\ delimiter from the current). */
102+
if (next_delim) {
103+
current = next_delim + 2;
104+
} else {
105+
break;
106+
}
62107
}
63108

64-
sz_needed = sizeof(struct at_notif_fifo) + notif_len + sizeof(char);
65-
66-
at_notif = k_heap_alloc(&at_monitor_heap, sz_needed, K_NO_WAIT);
67-
if (!at_notif) {
68-
LOG_WRN("No heap space for incoming notification: %s",
69-
notif);
70-
return;
109+
if (queued_any) {
110+
k_work_submit(&at_monitor_work);
71111
}
72-
73-
strncpy(at_notif->data, match, notif_len);
74-
at_notif->data[notif_len] = '\0';
75-
76-
k_fifo_put(&at_monitor_fifo, at_notif);
77-
k_work_submit(&at_monitor_work);
78112
}
79113

80114
static void sm_monitor_task(struct k_work *work)

0 commit comments

Comments
 (0)