*DRAFT:* AT Host Refactoring for pipe based architecture #133
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
AT Host Refactoring for pipe based architecture
This PR refactors Serial Modem to convert all UART and AT Host modules
to use Zephyr's modem_pipe API instead of build-time and run-time switches
between different modes.
This simplifies the design as all modules that deal with serial traffic
now use the same API. Modules do not need any knowledge if they are running
from CMUX pipe or directly at UART.
This can be demonstrated by running PPP without CMUX and then PPP with CMUX using the same
build configuration.
This work is still largely in progress, but this PR already offers the overview of the architecture.
Overview
modem_pipearchitecture.sm_uart_pipe_get()CONFIG_MODEM_PIPE=y, but does not depend on CMUX.sm_ppp_attach()struct sm_at_host_ctx):sm_at_host_get_current()orsm_at_host_get_current_pipe()provides a way to push data to specific instance.
sm_at_host_get_urc_ctx()orsm_at_host_get_urc_pipe()provides a wayto push data to a pipe that is for URC messages.
AT_BUF_MIN_SIZE), expands in 128B steps up to8kB (
AT_BUF_MAX_SIZE), and compresses back to 128B after command completion.CONFIG_SM_DATAMODE_BUF_SIZE) is allocated only when entering data mode.New APIs
int sm_at_host_set_pipe(struct sm_at_host_ctx *ctx, struct modem_pipe *pipe)struct modem_pipe *sm_at_host_get_pipe(struct sm_at_host_ctx *ctx)void sm_at_host_attach(struct modem_pipe *pipe)void sm_at_host_release(struct sm_at_host_ctx *ctx)struct sm_at_host_ctx *sm_at_host_get_ctx_from(struct modem_pipe *pipe)struct sm_at_host_ctx *sm_at_host_get_urc_ctx(void)struct sm_at_host_ctx *sm_at_host_get_current(void)struct modem_pipe *sm_at_host_get_current_pipe(voidstruct modem_pipe *sm_at_host_get_urc_pipe(voidTypical use cases are as follows:
Context lifetime
AT context is attached to a pipe using
sm_at_host_attach()or existing context is switched to anew pipe using
sm_at_host_set_pipe().AT context is created when pipe opens.
AT context is destroyed when pipe closes.
AT host does not open or close pipes.
Concurrency and modem_pipe Integration
atomic_ptr_t:at_pipe_event_handler()/null_pipe_handler()):null_pipe_handler()which allows us tore-open the AT context if the pipe is re-opened.
MODEM_PIPE_EVENT_RECEIVE_READY, AT Host verifies the event pipe matchesctx->pipebefore scheduling RX work.at_pipe_rx_work_fn()):ctxwas destroyed or the pipe changed.sm_at_host_work_fn()):pipeso no transmissions occur.Modem Pipe Handling: Corner Cases and Races
This section outlines observed and mitigated race scenarios around
modem_pipeevents and callbacks.Receive while re-attaching:
MODEM_PIPE_EVENT_RECEIVE_READYis raised whilectx->pipeis transitioning to a new pipe (e.g., CMUX channel switch).atomic_ptr_get(ctx->pipe) == pipebefore acting. Mismatched events are ignored.Stale CLOSE after re-open:
MODEM_PIPE_EVENT_CLOSEDwas queued, but before the work item executes, the context has been re-attached to a new pipe.sm_at_host_work_fn(), the CLOSED handler usesatomic_ptr_cas()to confirm the context still refers to the closing pipe. If not, the event is ignored to avoid destroying the wrong context.Detach gap before new owner attaches:
null_pipe_handler()ignores all other that OPENED events. In case of buffer full, the UART might stall.Context destruction races:
sm_at_ctx_check()validates the context againstinstance_listin all work handlers and skips work if destroyed.ISR/spinlock constraints:
sm_at_send_internal()warns on ISR), and pipe mutations are deferred tosm_at_host_work_fn()via message queue.Data-mode termination while pipe breaks:
SM_NULL_MODE/null_handler()recognizes the terminator and callsexit_datamode(), reporting dropped bytes;raw_send_scheduleduses timers to flush incomplete quit sequences safely.Atomic pointer sentinel during close:
(void *)0xdeadbeefis used transiently withatomic_ptr_cas()to mark a pipe as processed in the CLOSED path beforesm_at_host_destroy().TODO
Remaining work:
pipe might change between waiting. Need to store the context pointer.
rsp_send()and similar, are not used consistentlyrsp_send()is mean to send as a response for AT commandor if it was mean to be send as URC message. Example
sm_ppp.c:send_status_notification()ATD*to dial up PPP from this pipeAT+CMUXstandard commandFuture work
Possible ideas for future work:
AT+CMUXto allow changing baud rate..Testing the PPP module with Linux
As the PPP module now directly uses pipe interface, it can attach the UART or CMUX channel
on runtime.
Build the app using:
west build -b nrf9151/nrf9151/ns -- -DEXTRA_CONF_FILE="overlay-ppp.conf;overlay-cmux.conf"When testing the PPP with CMUX, use the
scripts/sm_start_ppp.shWhen testing the PPP without CMUX, create following file to
/etc/chatscripts/nrf91Then start the PPP directly on UART with:
Stopping the PPP drops the channel back to AT command mode.