Skip to content

Commit fb455f6

Browse files
committed
app: Async poll and idle handlers for AT contexts
Move struct async_poll_ctx into AT host context. Sockets use sm_at_host_get_async_poll_ctx(pipe) API to get the associated poll context. Each pipe can have different poll settings. Socket binds to a pipe where it is created. The sm_at_host_queue_idle_work() should be used for purposes where application needs to send URC message to a specific pipe. For example socket handlers, like #XAPOLL, etc.. These are not send through urc_send() as it should go to only one channel. Signed-off-by: Seppo Takalo <seppo.takalo@nordicsemi.no>
1 parent 83d01f9 commit fb455f6

4 files changed

Lines changed: 317 additions & 85 deletions

File tree

app/src/sm_at_host.c

Lines changed: 123 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010
#include "sm_util.h"
1111
#include "sm_ctrl_pin.h"
1212
#include "sm_at_dfu.h"
13-
#if defined(CONFIG_SM_PPP)
1413
#include "sm_ppp.h"
15-
#endif
14+
#include "sm_at_socket.h"
1615
#include <assert.h>
1716
#include <stdio.h>
1817
#include <string.h>
@@ -46,7 +45,8 @@ LOG_MODULE_REGISTER(sm_at_host, CONFIG_SM_LOG_LEVEL);
4645
enum sm_operation_mode {
4746
SM_AT_COMMAND_MODE, /* AT command host or bridge */
4847
SM_DATA_MODE, /* Raw data sending */
49-
SM_NULL_MODE /* Discard incoming until next command */
48+
SM_NULL_MODE, /* Discard incoming until next command */
49+
SM_INVALID_MODE /* Invalid mode, used for error handling */
5050
};
5151

5252
enum sm_debug_print {
@@ -71,6 +71,8 @@ static void at_pipe_event_handler(struct modem_pipe *pipe, enum modem_pipe_event
7171
void *user_data);
7272
static size_t sm_at_receive(struct sm_at_host_ctx *ctx, uint8_t c);
7373
static void sm_at_host_work_fn(struct k_work *work);
74+
static enum sm_operation_mode get_sm_mode(struct sm_at_host_ctx *ctx);
75+
static bool sm_at_ctx_check(struct sm_at_host_ctx *ctx);
7476

7577
/**
7678
* @brief AT host context structure.
@@ -146,10 +148,15 @@ struct sm_at_host_ctx {
146148
} echo_ctx;
147149

148150
/* Event callback mechanism */
151+
/* TODO: combine event work and idle_work_list */
149152
struct {
150153
sys_slist_t event_cbs;
151154
atomic_t events;
152155
} event_ctx;
156+
sys_slist_t idle_work_list;
157+
158+
/* Asynchronous poll context for sockets */
159+
struct async_poll_ctx poll_ctx;
153160
};
154161

155162
enum sm_pipe_event {
@@ -172,7 +179,7 @@ RING_BUF_DECLARE(urc_buf, CONFIG_SM_URC_BUFFER_SIZE);
172179
uint8_t sm_response_buf[CONFIG_SM_AT_BUF_SIZE + 1];
173180
/* Current executing context (set by entry points) */
174181
static struct sm_at_host_ctx *current_ctx;
175-
182+
static struct k_spinlock sm_at_host_lock;
176183
uint16_t sm_datamode_time_limit;
177184

178185
/**
@@ -293,12 +300,14 @@ struct sm_at_host_ctx *sm_at_host_get_ctx_from(struct modem_pipe *pipe)
293300
return NULL;
294301
}
295302

296-
SYS_SLIST_FOR_EACH_CONTAINER(&instance_list, ctx, node) {
297-
if (atomic_ptr_get(&ctx->pipe) == pipe) {
298-
return ctx;
303+
K_SPINLOCK(&sm_at_host_lock) {
304+
SYS_SLIST_FOR_EACH_CONTAINER(&instance_list, ctx, node) {
305+
if (atomic_ptr_get(&ctx->pipe) == pipe) {
306+
break;
307+
}
299308
}
300309
}
301-
return NULL;
310+
return ctx;
302311
}
303312

304313
struct sm_at_host_ctx *sm_at_host_get_urc_ctx(void)
@@ -314,6 +323,13 @@ struct modem_pipe *sm_at_host_get_pipe(struct sm_at_host_ctx *ctx)
314323
return ctx ? atomic_ptr_get(&ctx->pipe) : NULL;
315324
}
316325

326+
struct async_poll_ctx *sm_at_host_get_async_poll_ctx(struct modem_pipe *pipe)
327+
{
328+
struct sm_at_host_ctx *ctx = sm_at_host_get_ctx_from(pipe);
329+
330+
return sm_at_ctx_check(ctx) ? &ctx->poll_ctx : NULL;
331+
}
332+
317333
/**
318334
* @brief Check if ctx pointer is still valid.
319335
*
@@ -324,17 +340,21 @@ struct modem_pipe *sm_at_host_get_pipe(struct sm_at_host_ctx *ctx)
324340
static bool sm_at_ctx_check(struct sm_at_host_ctx *ctx)
325341
{
326342
struct sm_at_host_ctx *p;
343+
bool found = false;
327344

328345
if (!ctx) {
329346
return false;
330347
}
331348

332-
SYS_SLIST_FOR_EACH_CONTAINER(&instance_list, p, node) {
333-
if (p == ctx) {
334-
return true;
349+
K_SPINLOCK(&sm_at_host_lock) {
350+
SYS_SLIST_FOR_EACH_CONTAINER(&instance_list, p, node) {
351+
if (p == ctx) {
352+
found = true;
353+
break;
354+
}
335355
}
336356
}
337-
return false;
357+
return found;
338358
}
339359

340360
/* Process received data from pipe */
@@ -373,6 +393,12 @@ static void at_pipe_rx_work_fn(struct k_work *work)
373393
}
374394
} while (ret > 0 && atomic_ptr_get(&ctx->pipe) == pipe);
375395

396+
/* Allow idle work to run immediately, don't need to wait for timer */
397+
if (atomic_ptr_get(&ctx->pipe) == pipe && ctx->at_cmd_len == 0 &&
398+
get_sm_mode(ctx) == SM_AT_COMMAND_MODE) {
399+
event_work_fn(ctx);
400+
}
401+
376402
/* Clear current context */
377403
sm_at_host_set_current_ctx(NULL);
378404
}
@@ -521,6 +547,9 @@ void sm_at_host_attach(struct modem_pipe *pipe)
521547

522548
static enum sm_operation_mode get_sm_mode(struct sm_at_host_ctx *ctx)
523549
{
550+
if (!ctx || !sm_at_ctx_check(ctx)) {
551+
return SM_INVALID_MODE;
552+
}
524553
return ctx->at_mode;
525554
}
526555

@@ -969,7 +998,7 @@ static int sm_at_send_internal(struct sm_at_host_ctx *ctx, const uint8_t *data,
969998
if (ret < len) {
970999
LOG_ERR("URC buffer full, dropped %d bytes", len - ret);
9711000
}
972-
if (ctx->at_cmd_len > 0) {
1001+
if (!in_idle(ctx)) {
9731002
LOG_DBG("AT command in progress, delaying URC processing");
9741003
k_timer_start(&ctx->echo_ctx.timer, URC_RETRY_DELAY, K_NO_WAIT);
9751004
} else {
@@ -1324,6 +1353,8 @@ static size_t sm_at_receive(struct sm_at_host_ctx *ctx, uint8_t c)
13241353
case SM_NULL_MODE:
13251354
ret = null_handler(ctx, c);
13261355
break;
1356+
default:
1357+
break;
13271358
}
13281359

13291360
/* start inactivity timer in datamode */
@@ -1446,20 +1477,42 @@ int enter_datamode(sm_datamode_handler_t handler, size_t data_len)
14461477
return 0;
14471478
}
14481479

1449-
bool in_datamode(void)
1480+
bool in_datamode_ctx(struct sm_at_host_ctx *ctx)
14501481
{
1451-
struct sm_at_host_ctx *ctx = sm_at_host_get_current();
1452-
14531482
return (get_sm_mode(ctx) == SM_DATA_MODE);
14541483
}
14551484

1456-
bool in_at_mode(void)
1485+
bool in_datamode_pipe(struct modem_pipe *pipe)
14571486
{
1458-
struct sm_at_host_ctx *ctx = sm_at_host_get_current();
1487+
struct sm_at_host_ctx *ctx = sm_at_host_get_ctx_from(pipe);
14591488

1489+
return in_datamode_ctx(ctx);
1490+
}
1491+
1492+
bool in_at_mode_ctx(struct sm_at_host_ctx *ctx)
1493+
{
14601494
return (get_sm_mode(ctx) == SM_AT_COMMAND_MODE);
14611495
}
14621496

1497+
bool in_at_mode_pipe(struct modem_pipe *pipe)
1498+
{
1499+
struct sm_at_host_ctx *ctx = sm_at_host_get_ctx_from(pipe);
1500+
1501+
return in_at_mode_ctx(ctx);
1502+
}
1503+
1504+
bool in_idle_ctx(struct sm_at_host_ctx *ctx)
1505+
{
1506+
return (in_at_mode_ctx(ctx) && ctx->at_cmd_len == 0);
1507+
}
1508+
1509+
bool in_idle_pipe(struct modem_pipe *pipe)
1510+
{
1511+
struct sm_at_host_ctx *ctx = sm_at_host_get_ctx_from(pipe);
1512+
1513+
return in_idle_ctx(ctx);
1514+
}
1515+
14631516
void exit_datamode_handler(int result)
14641517
{
14651518
struct sm_at_host_ctx *ctx = sm_at_host_get_current();
@@ -1592,13 +1645,6 @@ void sm_at_host_echo(bool enable)
15921645
k_timer_stop(&ctx->echo_ctx.timer);
15931646
}
15941647

1595-
bool sm_at_host_echo_urc_delay(void)
1596-
{
1597-
struct sm_at_host_ctx *ctx = sm_at_host_get_current();
1598-
1599-
return k_timer_remaining_get(&ctx->echo_ctx.timer) > 0;
1600-
}
1601-
16021648
static void echo_timer_handler(struct k_timer *timer)
16031649
{
16041650
struct sm_at_host_ctx *ctx = CONTAINER_OF(timer, struct sm_at_host_ctx, echo_ctx.timer);
@@ -1643,6 +1689,19 @@ static void event_work_fn(struct sm_at_host_ctx *ctx)
16431689
}
16441690
}
16451691

1692+
do {
1693+
K_SPINLOCK(&sm_at_host_lock) {
1694+
node = sys_slist_get(&ctx->idle_work_list);
1695+
}
1696+
if (!node) {
1697+
break;
1698+
}
1699+
struct k_work *work = CONTAINER_OF(node, struct k_work, node);
1700+
1701+
/* Don't submit, just run directly, because we are already in right context */
1702+
work->handler(work);
1703+
} while (true);
1704+
16461705
/* Clear current context */
16471706
sm_at_host_set_current_ctx(NULL);
16481707
}
@@ -1679,8 +1738,10 @@ static int sm_at_host_ctx_init(struct sm_at_host_ctx *ctx, struct modem_pipe *pi
16791738
/* Initialize work items and timers */
16801739
k_work_init(&ctx->raw_send_scheduled_work, raw_send_scheduled);
16811740
k_work_init(&ctx->rx_work, at_pipe_rx_work_fn);
1741+
k_work_init(&ctx->poll_ctx.poll_work, sm_at_socket_poll_work_handler);
16821742
k_timer_init(&ctx->inactivity_timer, inactivity_timer_handler, NULL);
16831743
k_timer_init(&ctx->echo_ctx.timer, echo_timer_handler, NULL);
1744+
sys_slist_init(&ctx->idle_work_list);
16841745

16851746
/* Initialize event context */
16861747
sys_slist_init(&ctx->event_ctx.event_cbs);
@@ -1724,7 +1785,9 @@ static struct sm_at_host_ctx *sm_at_host_create(struct modem_pipe *pipe)
17241785
}
17251786

17261787
/* Add to instance list */
1727-
sys_slist_append(&instance_list, &ctx->node);
1788+
K_SPINLOCK(&sm_at_host_lock) {
1789+
sys_slist_append(&instance_list, &ctx->node);
1790+
}
17281791

17291792
modem_pipe_attach(pipe, at_pipe_event_handler, ctx);
17301793

@@ -1813,7 +1876,7 @@ static void sm_at_host_work_fn(struct k_work *work)
18131876
switch (msg.sm_event) {
18141877
case SM_EVENT_URC:
18151878
/* Don't interrupt AT command execution */
1816-
if (msg.ctx->at_cmd_len == 0) {
1879+
if (in_idle(msg.ctx)) {
18171880
while (!ring_buf_is_empty(&urc_buf)) {
18181881
uint8_t *p;
18191882
size_t len = ring_buf_get_claim(&urc_buf, &p, UINT16_MAX);
@@ -1831,7 +1894,7 @@ static void sm_at_host_work_fn(struct k_work *work)
18311894
}
18321895
break;
18331896
case SM_EVENT_AT_MODE:
1834-
if (msg.ctx->at_cmd_len == 0) {
1897+
if (in_idle(msg.ctx)) {
18351898
event_work_fn(msg.ctx);
18361899
} else {
18371900
k_timer_start(&msg.ctx->echo_ctx.timer, URC_RETRY_DELAY, K_NO_WAIT);
@@ -1843,6 +1906,35 @@ static void sm_at_host_work_fn(struct k_work *work)
18431906
}
18441907
}
18451908

1909+
void sm_at_host_queue_idle_work(struct modem_pipe *pipe, struct k_work *work)
1910+
{
1911+
struct sm_at_host_ctx *ctx = sm_at_host_get_ctx_from(pipe);
1912+
1913+
if (!ctx) {
1914+
LOG_ERR("No AT host context found for pipe %p", (void *)pipe);
1915+
return;
1916+
}
1917+
/*
1918+
* TODO: A serious race condition exists where a work item
1919+
* might have already been added to a idle_work_list of another socket
1920+
* context. This is because sm_at_socket.c uses a single async_poll_ctx but
1921+
* multiple sockets. So maybe the poll_ctx should be moved into sm_at_host_ctx?
1922+
*/
1923+
if (k_work_is_pending(work)) {
1924+
LOG_ERR("Work %p is already pending, cannot queue", (void *)work);
1925+
return;
1926+
}
1927+
1928+
K_SPINLOCK(&sm_at_host_lock) {
1929+
if (!sys_slist_find(&ctx->idle_work_list, &work->node, &(sys_snode_t *){0})) {
1930+
sys_slist_append(&ctx->idle_work_list, &work->node);
1931+
}
1932+
}
1933+
if (in_idle(ctx)) {
1934+
sm_at_host_event_notify(ctx, SM_EVENT_URC);
1935+
}
1936+
}
1937+
18461938
static int sm_at_host_destroy(struct sm_at_host_ctx *ctx)
18471939
{
18481940
struct k_work_sync sync;
@@ -1875,7 +1967,9 @@ static int sm_at_host_destroy(struct sm_at_host_ctx *ctx)
18751967
k_work_cancel_sync(&ctx->raw_send_scheduled_work, &sync);
18761968

18771969
/* Remove from instance list */
1878-
sys_slist_find_and_remove(&instance_list, &ctx->node);
1970+
K_SPINLOCK(&sm_at_host_lock) {
1971+
sys_slist_find_and_remove(&instance_list, &ctx->node);
1972+
}
18791973

18801974
LOG_INF("Destroyed AT host instance %p", (void *)ctx);
18811975

0 commit comments

Comments
 (0)