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);
4645enum 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
5252enum 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 );
7272static size_t sm_at_receive (struct sm_at_host_ctx * ctx , uint8_t c );
7373static 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
155162enum sm_pipe_event {
@@ -172,7 +179,7 @@ RING_BUF_DECLARE(urc_buf, CONFIG_SM_URC_BUFFER_SIZE);
172179uint8_t sm_response_buf [CONFIG_SM_AT_BUF_SIZE + 1 ];
173180/* Current executing context (set by entry points) */
174181static struct sm_at_host_ctx * current_ctx ;
175-
182+ static struct k_spinlock sm_at_host_lock ;
176183uint16_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
304313struct 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)
324340static 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
522548static 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+
14631516void 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-
16021648static 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+
18461938static 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