AT Host Refactoring for pipe based architecture#133
AT Host Refactoring for pipe based architecture#133SeppoTakalo wants to merge 14 commits intonrfconnect:mainfrom
Conversation
9a46611 to
c35d677
Compare
acc43df to
110fe70
Compare
7fe62f8 to
7bb88dd
Compare
|
|
||
| .. note:: | ||
|
|
||
| |SM| does not have an equivalent to the ``AT+CMUX`` command described in `3GPP TS 27.007`_. |
There was a problem hiding this comment.
The bullets in the note come a bit surprisingly without any intro
| PPP is enabled in |SM| by compiling it with the appropriate configuration files, depending on your use case (with or without CMUX). | ||
| See the :ref:`sm_config_files` section for more information. | ||
|
|
||
| Enter data state +CGDATA |
There was a problem hiding this comment.
Remember to add all new AT+ AT commands to #XCLAC handling. Own function for them would likely be nice now that the list starts growing.
1105136 to
f865b46
Compare
| /* When PPP is stopped from first DLCI, move AT channel there. | ||
| * First open DLCI should always be the AT channel. | ||
| /* | ||
| * This is the legacy behavior of AT#XCMUX=2 that was implemented |
There was a problem hiding this comment.
Should we deprecate the AT#XCMUX and AT#XPPP with this PR?
There was a problem hiding this comment.
No.
I think breaking the AT compatibility is a bit pointless.
We can emulate previous functions, so we don't need to force host to rewrite the existing driver.
| if (ctx->owner == owner) { | ||
| ctx->owner = SM_URC_OWNER_NONE; | ||
| /* Allocate new instance from heap */ | ||
| ctx = malloc(sizeof(*ctx)); |
There was a problem hiding this comment.
Is there any way of getting the available heap? It might make sense to fail in here if the heap is near it's limit after this reservation.
There was a problem hiding this comment.
I don't think there is API for that.
Why would we want to fail it before we have used all of the heap? There are no other users of the heap.
| } | ||
|
|
||
| void sm_at_host_reset(void) | ||
| static void send_urcs(void) |
There was a problem hiding this comment.
Does this handle sending for both URC's received from modem and our own? So currently our own stuff would also go to all open AT-channels?
There was a problem hiding this comment.
Its for the developer to choose.
I would assume that important URCs, like #XPPP are supposed to go to all channels.
But socket specific once use the rsp_send_to()
3b23537 to
6820373
Compare
7a94b7d to
2794836
Compare
Refactored the UART handler to initialize the modem_pipe infrastructure at startup rather than only when switching to CMUX mode. This creates a more consistent architecture where the pipe abstraction is always present. Refactored AT Host module to encapsule global variables into one struct that has a pipe reference it is attached to. Introduce dynamic memory allocation for AT host instances to enable multi-instance support required by CMUX multiplexing. Key changes: - Add instance_list (sys_slist_t) for managing all AT host instances - Add current_ctx tracking. Current context is the one that is executing the AT command. AT commands can only be executed from Serial Modem work queue context, so this pointer should be valid while executing from the same queue. Invalid, if called elsewhere. - Destroy unused AT instances, but not the last one - Allow re-attaching AT instance to a pipe - CMUX module is not handling instances anymore. Each module should attach to a given pipe and when releasing, release AT instance, but keep monitoring the pipe for PIPE_OPEN event. - Move all buffers into system heap. - Only allocate buffers on need. - Increase AT command parser buffer in steps, until max 8 kB. Release extra, once cmd executed. - Don't allocate data ringbuffer, until datamode requested - Parse pipe input one character at a time, no dual buffering New APIs: - sm_at_host_create(): Allocate and initialize new AT host instance - sm_at_host_destroy(): Clean up and free AT host instance - sm_at_host_get_*(): Get current AT instance, pipe or URC instances - sm_at_host_set_pipe(): Switch given AT host to a new pipe Memory allocation: - Use C library malloc(),realloc() and free() - First instance cannot be destroyed (has application lifetime) - Max instances determined by CMUX channel count When following Kconfig variables are set: CONFIG_COMMON_LIBC_MALLOC=y CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=-1 It uses all remaining RAM for heap that is used by C library. Signed-off-by: Seppo Takalo <seppo.takalo@nordicsemi.no>
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>
Send URC messages to all pipes that are open and are in AT mode. Signed-off-by: Seppo Takalo <seppo.takalo@nordicsemi.no>
* Define rsp_send_to() that takes a pipe pointer * Also add pipe pointer to data_send(). * Refactor socket's to use the pipe they are created on. * Refactor MQTT to store the pipe pointer as well. Signed-off-by: Seppo Takalo <seppo.takalo@nordicsemi.no>
When AT#XCMUX and AT#XPPP are used, emulate the previous behavior with statically assigned channel numbers. Signed-off-by: Seppo Takalo <seppo.takalo@nordicsemi.no>
Add partial implementation of standard AT+CMUX command. Only following commands are supported: AT+CMUX=0 AT+CMUX=0,0 AT+CMUX? AT+CMUX=? Baud rate switch is not supported. Signed-off-by: Seppo Takalo <seppo.takalo@nordicsemi.no>
AT+CGDATA per 3GPP TS 27.007 section 10.1.12. Supported forms: AT+CGDATA - Start PPP with default CID AT+CGDATA="PPP" - Start PPP with default CID (L2P must be "PPP") AT+CGDATA="PPP",<cid> - Start PPP with specified CID AT+CGDATA=? - Report supported L2P values Signed-off-by: Seppo Takalo <seppo.takalo@nordicsemi.no>
Add AT#XCMUXTRACE command to start CMUX modem traces on a specified channel. This is required when using AT+CMUX which does not do hard coded channel assignments. Signed-off-by: Seppo Takalo <seppo.takalo@nordicsemi.no>
Jira: SM-126
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:
uart driver and CMUX should use system work queue, or another work queue.SM work queue should be allowed to block for a short whileImplement blocking pipe send inside AT host, so that all other modules can rely on messages going to at least CMUX or UART buffers.First pipe in the list if not necessary the DLC1, or first open DLC. Maybe OK?Socket operations from AT commands should be OK already (untested)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()DONE:ATD*to dial up PPP from this pipeAT+CGDATAAT+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.