Modem library integration layer
The Modem library integration layer handles the integration of the Modem library into nRF Connect SDK. The integration layer is constituted by the library wrapper and functionalities like socket offloading, OS abstraction, memory reservation by the Partition manager, handling modem traces, and diagnostics.
Library wrapper
The library wrapper provides an encapsulation over the core Modem library functions such as initialization and shutdown.
The library wrapper is implemented in nrf/lib/nrf_modem_lib/nrf_modem_lib.c
.
The library wrapper encapsulates the nrf_modem_init()
and nrf_modem_shutdown()
calls of the Modem library with nrf_modem_lib_init()
and nrf_modem_lib_shutdown()
calls, respectively.
The library wrapper eases the task of initializing the Modem library by automatically passing the size and address of all the shared memory regions of the Modem library to the nrf_modem_init()
call.
Partition Manager is the component that reserves the RAM memory for the shared memory regions used by the Modem library. For more information, see Partition manager integration.
The library wrapper also provides callbacks for the initialization and shutdown operations.
The application can set up a callback for nrf_modem_lib_init()
function using the NRF_MODEM_LIB_ON_INIT
macro, and a callback for nrf_modem_lib_shutdown()
function using the NRF_MODEM_LIB_ON_SHUTDOWN
macro.
These compile-time callbacks allow any part of the application to perform any setup steps that require the modem to be in a certain state.
Furthermore, the callbacks ensure that the setup steps are repeated whenever another part of the application turns the modem on or off.
The callbacks registered using NRF_MODEM_LIB_ON_INIT
are executed after the library is initialized.
The result of the initialization and the callback context are provided to these callbacks.
Callbacks for the macro NRF_MODEM_LIB_ON_INIT
must have the signature void callback_name(int ret, void *ctx)
, where ret
is the result of the initialization and ctx
is the context passed to the macro.
The callbacks registered using NRF_MODEM_LIB_ON_SHUTDOWN
are executed before the library is shut down.
The callback context is provided to these callbacks.
Callbacks for the macro NRF_MODEM_LIB_ON_SHUTDOWN
must have the signature void callback_name(void *ctx)
, where ctx
is the context passed to the macro.
See the nRF9160: Modem callbacks sample for more information.
The library wrapper can also initialize the Modem library during system initialization using SYS_INIT
.
The CONFIG_NRF_MODEM_LIB_SYS_INIT
Kconfig option can be used to control the initialization.
Some libraries in nRF Connect SDK, such as the LTE link controller have similar configuration options to initialize during system initialization and these options depend on the configuration option of the integration layer.
If your application performs an update of the nRF9160 modem firmware, you must disable this functionality to have full control on the initialization of the library.
The library wrapper also coordinates the shutdown operation among different parts of the application that use the Modem library.
This is done by the nrf_modem_lib_shutdown()
function call, by waking the sleeping threads when the modem is being shut down.
When CONFIG_NRF_MODEM_LIB_TRACE
Kconfig option is enabled, the modem traces are enabled in the modem and are forwarded to the Modem trace module.
When using the Modem library in nRF Connect SDK, the library must be initialized and shutdown using the nrf_modem_lib_init()
and nrf_modem_lib_shutdown()
function calls, respectively.
CONFIG_NRF_MODEM_LIB_LOG_FW_VERSION_UUID
can be enabled for printing logs of both FW version and UUID at the end of the library initialization step.
Socket offloading
Zephyr Socket API offers the socket offloading functionality to redirect or offload function calls to BSD socket APIs such as socket()
and send()
.
The integration layer utilizes this functionality to offload the socket API calls to the Modem library and thus eases the task of porting the networking code to the nRF9160 by providing a wrapper for Modem library’s native socket API such as nrf_socket()
and nrf_send()
.
The socket offloading functionality in the integration layer is implemented in nrf/lib/nrf_modem_lib/nrf91_sockets.c
.
Modem library socket API sets errnos as defined in nrf_errno.h
.
The socket offloading support in the integration layer in nRF Connect SDK converts those errnos to the errnos that adhere to the selected C library implementation.
The socket offloading functionality is enabled by default.
To disable the functionality, set the CONFIG_NET_SOCKETS_OFFLOAD
Kconfig option to n
in your project configuration.
If you disable the socket offloading functionality, the socket calls will no longer be offloaded to the nRF9160 modem firmware.
Instead, the calls will be relayed to the native Zephyr TCP/IP implementation.
This can be useful to switch between an emulator and a real device while running networking code on these devices.
Note that the even if the socket offloading is disabled, Modem library’s own socket APIs such as nrf_socket()
and nrf_send()
remain available.
OS abstraction layer
For functioning, the Modem library requires the implementation of an OS abstraction layer, which is an interface over the operating system functionalities such as interrupt setup, threads, and heap.
The integration layer provides an implementation of the OS abstraction layer using nRF Connect SDK components.
The OS abstraction layer is implemented in the nrfxlib/nrf_modem/include/nrf_modem_os.c
.
The behavior of the functions in the OS abstraction layer is dependent on the nRF Connect SDK components that are used in their implementation.
This is relevant for functions such as nrf_modem_os_shm_tx_alloc()
, which uses Zephyr’s Heap implementation to dynamically allocate memory.
In this case, the characteristics of the allocations made by these functions depend on the heap implementation by Zephyr.
Modem trace module
The modem trace module is implemented in nrf/lib/nrf_modem_lib/nrf_modem_lib_trace.c
.
To enable the module and start tracing, set the CONFIG_NRF_MODEM_LIB_TRACE
Kconfig option to y
in your project configuration.
The module implements a thread that initializes, deinitializes, and forwards modem traces to a backend that can be selected by enabling any one of the following Kconfig options:
CONFIG_NRF_MODEM_LIB_TRACE_BACKEND_UART
to send modem traces over UARTE1CONFIG_NRF_MODEM_LIB_TRACE_BACKEND_RTT
to send modem traces over SEGGER RTT
To reduce the amount of trace data sent from the modem, a different trace level can be selected. Complete the following steps to configure the modem trace level at compile time:
Set
CONFIG_NRF_MODEM_LIB_TRACE_LEVEL_OVERRIDE
toy
in your project configuration.Enable any one of the following Kconfig options by setting it to
y
in your project configuration:
The application can use the nrf_modem_lib_trace_level_set()
function to set the desired trace level.
Passing NRF_MODEM_LIB_TRACE_LEVEL_OFF
to the nrf_modem_lib_trace_level_set()
function disables trace output.
During tracing, the integration layer ensures that modem traces are always flushed before the Modem library is re-initialized (including when the modem has crashed).
The application can synchronize with the flushing of modem traces by calling the nrf_modem_lib_trace_processing_done_wait()
function.
Adding custom trace backends
You can add custom trace backends if the existing trace backends are not sufficient.
At any time, only one trace backend can be compiled with the application.
The value of the CONFIG_NRF_MODEM_LIB_TRACE_BACKEND
Kconfig option determines which trace backend is compiled.
The nRF9160: Modem trace backend sample demonstrates how a custom trace backend can be added to an application.
Complete the following steps to add a custom trace backend:
Place the files that have the custom trace backend implementation in a library or an application you create. For example, the implementation of the UART trace backend (default) can be found in the
nrf/lib/nrf_modem_lib/trace_backends/uart/uart.c
file.Add a C file implementing the interface in
nrf/include/modem/trace_backend.h
header file./* my_trace_medium.c */ #include <modem/trace_medium.h> int trace_medium_init(void) { /* initialize transport medium here */ return 0; } int trace_medium_deinit(void) { /* optional deinitialization code here */ return 0; } int trace_medium_write(const void *data, size_t len) { /* forward data over custom transport here */ /* return number of bytes written or negative error code on failure */ return 0; }
Create or modify a
Kconfig
file to extend the choiceNRF_MODEM_LIB_TRACE_BACKEND
with another option.if NRF_MODEM_LIB_TRACE # Extends the choice with another backend choice NRF_MODEM_LIB_TRACE_BACKEND config NRF_MODEM_LIB_TRACE_BACKEND_MY_TRACE_BACKEND bool "My trace backend" help Optional description of my trace backend. endchoice endif
Create or modify a
CMakeLists.txt
file, adding the custom trace backend sources only if the custom trace backend option has been chosen.if(CONFIG_NRF_MODEM_LIB_TRACE) zephyr_library() # Only add 'custom' backend to compilation when selected. zephyr_library_sources_ifdef( CONFIG_NRF_MODEM_LIB_TRACE_BACKEND_MY_TRACE_BACKEND path/to/my_trace_backend.c ) endif()
Include the
Kconfig
file and theCMakeLists.txt
file to the build.Add the following Kconfig options to your application’s
prj.conf
file to use the custom modem trace backend:CONFIG_NRF_MODEM_LIB_TRACE=y CONFIG_NRF_MODEM_LIB_TRACE_BACKEND_MY_TRACE_BACKEND=y
Sending traces over UART1 on a custom board
When sending modem traces over UART1 on a custom board, configuration must be added for the UART1 device in the devicetree.
This is done by adding the following code snippet to the board devicetree or overlay file, where the pin numbers (0
, 1
, 14
, and 15
) must be updated to match your board.
&pinctrl {
uart1_default: uart1_default {
group1 {
psels = <NRF_PSEL(UART_TX, 0, 1)>,
<NRF_PSEL(UART_RTS, 0, 14)>;
};
group2 {
psels = <NRF_PSEL(UART_RX, 0, 0)>,
<NRF_PSEL(UART_CTS, 0, 15)>;
bias-pull-up;
};
};
uart1_sleep: uart1_sleep {
group1 {
psels = <NRF_PSEL(UART_TX, 0, 1)>,
<NRF_PSEL(UART_RX, 0, 0)>,
<NRF_PSEL(UART_RTS, 0, 14)>,
<NRF_PSEL(UART_CTS, 0, 15)>;
low-power-enable;
};
};
};
&uart1 {
...
pinctrl-0 = <&uart1_default>;
pinctrl-1 = <&uart1_sleep>;
pinctrl-names = "default", "sleep";
...
};
The UART trace backends allow the pins and UART1 interrupt priority to be set using the devicetree. Other configurations set in the devicetree, such as the current speed, are overwritten by the UART trace backends.
Note
When one of the UART trace backends is enabled by either the Kconfig option CONFIG_NRF_MODEM_LIB_TRACE_BACKEND_UART
or CONFIG_NRF_MODEM_LIB_TRACE_BACKEND_UART_SYNC
, it initializes the UART1 driver, regardless of its status in the devicetree.
Modem fault handling
If a fault occurs in the modem, the application is notified through the fault handler function that is registered with the Modem library during initialization. This enables the application to read the fault reason (in some cases the modem’s program counter) and take appropriate action.
On initialization, the Modem library integration layer registers the nrf_modem_fault_handler()
function through the Modem library initialization parameters.
The behavior of the nrf_modem_fault_handler()
function is controlled with the CONFIG_NRF_MODEM_LIB_ON_FAULT
Kconfig option.
The Modem library integration layer provides the following three options for CONFIG_NRF_MODEM_LIB_ON_FAULT
Kconfig option:
CONFIG_NRF_MODEM_LIB_ON_FAULT_DO_NOTHING
- This is the default Kconfig option that lets the fault handler log the Modem fault and return.CONFIG_NRF_MODEM_LIB_ON_FAULT_RESET_MODEM
- This Kconfig option schedules a workqueue task to reinitialize the modem and Modem library.CONFIG_NRF_MODEM_LIB_ON_FAULT_APPLICATION_SPECIFIC
- This Kconfig option results in a call to thenrf_modem_fault_handler()
function that is defined in the application, outside of the Modem library integration layer.
Implementing a custom fault handler
If you want to implement a custom fault handler, consider the following points:
The fault handler is called in an interrupt context and must be as short as possible.
Reinitialization of the Modem library must be done outside of the fault handler.
If the modem trace is enabled, the modem sends a coredump through the trace backend on modem failure. To ensure correct trace output, the modem must not be reinitialized before all trace data is handled.
Partition manager integration
The Modem library, which runs on the application core, shares an area of RAM memory with the nRF9160 modem core. During the initialization, the Modem library accepts the boundaries of this area of RAM and configures the communication with the modem core accordingly.
However, it is the responsibility of the application to reserve that RAM during linking, so that this memory area is not used for other purposes and remain dedicated for use by the Modem library.
In nRF Connect SDK, the application can configure the size of the memory area dedicated to the Modem library through the integration layer. The integration layer provides a set of Kconfig options that help the application reserve the required amount of memory for the Modem library by integrating with another nRF Connect SDK component, the Partition Manager.
The RAM area that the Modem library shares with the nRF9160 modem core is divided into the following four regions:
Control
RX
TX
Trace
The size of the RX, TX and the Trace regions can be configured by the following Kconfig options of the integration layer:
CONFIG_NRF_MODEM_LIB_SHMEM_RX_SIZE
for the RX regionCONFIG_NRF_MODEM_LIB_SHMEM_TX_SIZE
for the TX regionCONFIG_NRF_MODEM_LIB_SHMEM_TRACE_SIZE
for the Trace region
The size of the Control region is fixed.
The Modem library exports the size value through CONFIG_NRF_MODEM_SHMEM_CTRL_SIZE
.
This value is automatically passed by the integration layer to the library during the initialization through nrf_modem_lib_init()
.
When the application is built using CMake, the Partition Manager automatically reads the Kconfig options of the integration layer. Partition manager decides about the placement of the regions in RAM and reserves memory according to the given size. As a result, the Partition manager generates the following parameters:
PM_NRF_MODEM_LIB_CTRL_ADDRESS
- Address of the Control regionPM_NRF_MODEM_LIB_TX_ADDRESS
- Address of the TX regionPM_NRF_MODEM_LIB_RX_ADDRESS
- Address of the RX regionPM_NRF_MODEM_LIB_TRACE_ADDRESS
- Address of the Trace region
Partition manager also generates the following additional parameters:
PM_NRF_MODEM_LIB_CTRL_SIZE
- Size of the Control regionPM_NRF_MODEM_LIB_TX_SIZE
- Size of the TX regionPM_NRF_MODEM_LIB_RX_SIZE
- Size of the RX regionPM_NRF_MODEM_LIB_TRACE_SIZE
- Size of the Trace region
These parameters will have identical values as the CONFIG_NRF_MODEM_LIB_SHMEM_*_SIZE
configuration options.
When the Modem library is initialized by the integration layer in nRF Connect SDK, the integration layer automatically passes the boundaries of each shared memory region to the Modem library during the nrf_modem_lib_init()
call.
Diagnostic functionality
The Modem library integration layer in nRF Connect SDK provides some memory diagnostic functionality that is enabled by the CONFIG_NRF_MODEM_LIB_MEM_DIAG
option.
The application can retrieve runtime statistics for the library and TX memory region heaps by enabling the CONFIG_NRF_MODEM_LIB_MEM_DIAG
option and calling the nrf_modem_lib_diag_stats_get()
function.
The application can schedule a periodic report of the runtime statistics of the library and TX memory region heaps, by enabling the CONFIG_NRF_MODEM_LIB_MEM_DIAG_DUMP
option.
The application can log the allocations on the Modem library heap and the TX memory region by enabling the CONFIG_NRF_MODEM_LIB_MEM_DIAG_ALLOC
option.
API documentation
include/modem/nrf_modem_lib.h
, include/modem/nrf_modem_lib_trace.h
lib/nrf_modem_lib.c
- group nrf_modem_lib
API of the SMS nRF Modem library wrapper module.
Defines
-
NRF_MODEM_LIB_ON_INIT(name, _callback, _context)
Define a callback for nrf_modem_lib_init calls.
The callback function
_callback
is invoked after the library has been initialized.- Parameters
name – Callback name
_callback – Callback function name
_context – User-defined context for the callback
-
NRF_MODEM_LIB_ON_SHUTDOWN(name, _callback, _context)
Define a callback for nrf_modem_lib_shutdown calls.
The callback function
_callback
is invoked before the library is shutdown.- Parameters
name – Callback name
_callback – Callback function name
_context – User-defined context for the callback
Functions
-
int nrf_modem_lib_init(enum nrf_modem_mode mode)
Initialize the Modem library.
This function synchronously turns on the modem; it could block for a few minutes when the modem firmware is being updated.
If your application supports modem firmware updates, consider initializing the library manually to have control of what the application should do while initialization is ongoing.
The library has two operation modes, normal mode and full DFU mode. The full DFU mode is used to update the whole modem firmware.
When the library is initialized in full DFU mode, all shared memory regions are reserved for the firmware update operation, and no other functionality can be used. In particular, sockets won’t be available to the application.
To switch between the full DFU mode and normal mode, shutdown the modem with nrf_modem_lib_shutdown() and re-initialize it in the desired operation mode.
- Parameters
mode – [in] Library mode.
- Returns
int Zero on success, non-zero otherwise.
-
void nrf_modem_lib_shutdown_wait(void)
Makes a thread sleep until next time nrf_modem_lib_init() is called.
When nrf_modem_lib_shutdown() is called a thread can call this function to be woken up next time nrf_modem_lib_init() is called.
-
int nrf_modem_lib_get_init_ret(void)
Get the last return value of nrf_modem_lib_init.
This function can be used to access the last return value of nrf_modem_lib_init. This can be used to check the state of a modem firmware exchange when the Modem library was initialized at boot-time.
- Returns
int The last return value of nrf_modem_lib_init.
-
int nrf_modem_lib_shutdown(void)
Shutdown the Modem library, releasing its resources.
- Returns
int Zero on success, non-zero otherwise.
-
void nrf_modem_fault_handler(struct nrf_modem_fault_info *fault_info)
Modem fault handler.
- Parameters
fault_info – [in] Modem fault information. Contains the fault reason and, in some cases, the modem program counter.
-
struct nrf_modem_lib_init_cb
- #include <nrf_modem_lib.h>
Modem library initialization callback struct.
-
struct nrf_modem_lib_shutdown_cb
- #include <nrf_modem_lib.h>
Modem library shutdown callback struct.
-
NRF_MODEM_LIB_ON_INIT(name, _callback, _context)
- group nrf_modem_lib_trace
Enums
-
enum nrf_modem_lib_trace_level
Trace level.
The trace level can be used to filter the traces.
Values:
-
enumerator NRF_MODEM_LIB_TRACE_LEVEL_OFF
Disable output.
-
enumerator NRF_MODEM_LIB_TRACE_LEVEL_COREDUMP_ONLY
Coredump only.
-
enumerator NRF_MODEM_LIB_TRACE_LEVEL_FULL
LTE, IP, GNSS, and coredump.
-
enumerator NRF_MODEM_LIB_TRACE_LEVEL_IP_ONLY
IP.
-
enumerator NRF_MODEM_LIB_TRACE_LEVEL_LTE_AND_IP
LTE and IP.
-
enumerator NRF_MODEM_LIB_TRACE_LEVEL_OFF
Functions
-
int nrf_modem_lib_trace_processing_done_wait(k_timeout_t timeout)
Wait for trace to have finished processing after coredump or shutdown.
This function blocks until the trace module has finished processing data after a modem fault (coredump) or modem shutdown.
- Parameters
timeout – Time to wait for trace processing to be done.
- Returns
Zero on success, non-zero otherwise.
-
int nrf_modem_lib_trace_level_set(enum nrf_modem_lib_trace_level trace_level)
Set trace level.
- Parameters
trace_level – Trace level
- Returns
Zero on success, non-zero otherwise.
-
enum nrf_modem_lib_trace_level