nrf_modem_init() returns -22 (EINVAL) on bare-metal nRF9160 app using nrf_modem from nrfxlib v2.9.0

Hello Nordic Support,

I am developing a bare-metal, non-secure firmware for nRF9160 using nrf_modem from SDK nrfxlib v2.9.0, without Zephyr or RTOS.

I followed the porting guide here:
https://docs.nordicsemi.com/bundle/ncs-latest/page/nrfxlib/nrf_modem/doc/ug_nrf_modem_porting_os.html

However, calling nrf_modem_init() consistently fails with:

Modem init failed: -22

which maps to -EINVAL.

/*********************************************************************
*                    SEGGER Microcontroller GmbH                     *
*                        The Embedded Experts                        *
**********************************************************************

-------------------------- END-OF-HEADER -----------------------------

File    : main_ns.c
Purpose : Generic application start

*/


#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <nrfx.h>
#include <nrf_gpio.h>
#include <nrf_errno.h>
#include <nrf.h>

#include <pins_def.h>

#include <nrfx_ipc.h>
#include <nrf_modem.h>
#include <nrf_modem_os.h>

#include "NSCFunctions.h"


#define SHMEM_TX_SIZE     ( 0x3000 )   // 12 KB
#define SHMEM_RX_SIZE     ( 0x3000 )
#define SHMEM_TRACE_SIZE  ( 0x3000 )
#define SHMEM_SIZE        (NRF_MODEM_CELLULAR_SHMEM_CTRL_SIZE + SHMEM_TX_SIZE + SHMEM_RX_SIZE + SHMEM_TRACE_SIZE)

// Create one contiguous memory space for the buffers required by the modem driver
typedef struct {
    uint8_t nrf_modem_ctrl[NRF_MODEM_CELLULAR_SHMEM_CTRL_SIZE];
    uint8_t nrf_modem_tx[SHMEM_TX_SIZE];
    uint8_t nrf_modem_rx[SHMEM_RX_SIZE];
    uint8_t nrf_modem_trace[SHMEM_TRACE_SIZE];
} nrf_modem_bufs_t;

static nrf_modem_bufs_t modem_shm __attribute__(( section(".modem_shm"), aligned(4)));

const struct nrf_modem_shmem_cfg modem_shmem_cfg = {
    .ctrl = {
        .base = (uint32_t)&modem_shm.nrf_modem_ctrl,
        .size = NRF_MODEM_CELLULAR_SHMEM_CTRL_SIZE,
    },
    .tx = {
        .base = (uint32_t)&modem_shm.nrf_modem_tx,
        .size = SHMEM_TX_SIZE,
    },
    .rx = {
        .base = (uint32_t)&modem_shm.nrf_modem_rx,
        .size = SHMEM_RX_SIZE,
    },
    .trace = {
        .base = (uint32_t)&modem_shm.nrf_modem_trace,
        .size = SHMEM_TRACE_SIZE,
    }
  };


//***********************************
// modem_fault_handler
//***********************************
void modem_fault_handler(struct nrf_modem_fault_info *info) 
{
    printf("MODEM FAULT! PC=0x%08X Reason=%u\n", info->program_counter, info->reason);
}

void nrf_modem_os_init(void)
{
    /* Initialize the glue layer and required peripherals. */
}

void nrf_modem_os_shutdown(void)
{
    /* Deinitialize the glue layer.
       When shutdown is called, all pending calls to nrf_modem_os_timedwait
       shall exit and return -NRF_ESHUTDOWN. */
}

void *nrf_modem_os_shm_tx_alloc(size_t bytes)
{
    /* Allocate a buffer on the TX area of shared memory. */
}

void nrf_modem_os_shm_tx_free(void *mem)
{
    /* Free a shared memory buffer in the TX area. */
}

void *nrf_modem_os_alloc(size_t bytes)
{
    /* Allocate a buffer on the library heap. */
}

void nrf_modem_os_free(void *mem)
{
    /* Free a memory buffer in the library heap. */
}

void nrf_modem_os_busywait(int32_t usec)
{
    /* Busy wait for a given number of microseconds. */
}

int32_t nrf_modem_os_timedwait(uint32_t context, int32_t *timeout)
{
    if (!nrf_modem_is_initialized())
    {
        return -NRF_ESHUTDOWN;
    }

    /* Put a thread to sleep for a specific time or until an event occurs.
       Wait for the timeout.
       All waiting threads shall be woken by nrf_modem_event_notify.
       A blind return value of zero will cause a blocking wait. */

    if (!nrf_modem_is_initialized())
    {
        return -NRF_ESHUTDOWN;
    }

    return 0;
}

void nrf_modem_os_event_notify(uint32_t context)
{
    // Notify the application that an event has occurred.
    //   This shall wake all threads sleeping in nrf_modem_os_timedwait.
}

void nrf_modem_os_errno_set(int errno_val)
{
    /* Set OS errno. */
}

bool nrf_modem_os_is_in_isr(void)
{
    // Check if executing in interrupt context.
    return false;
}

int nrf_modem_os_sem_init(void **sem, unsigned int initial_count, unsigned int limit)
{
    /* If multithreaded access to modem functionalities is needed, the function must allocate
     * and initialize a semaphore and return its address through the `sem` parameter. If the
     * address of an already allocated semaphore is provided as an input, the allocation part is
     * skipped and the semaphore is only reinitialized.
     */
    return 0;
}

void nrf_modem_os_sem_give(void *sem)
{
    /* Give a semaphore. */
}

int nrf_modem_os_sem_take(void *sem, int timeout)
{
    /* Try to take a semaphore with the given timeout. */
    return 0;
}

unsigned int nrf_modem_os_sem_count_get(void *sem)
{
    /* Get a semaphore's count. */
    return 0;
}

 int nrf_modem_os_mutex_init(void **mutex)
{
    /* If multithreaded access to modem functionalities is needed, the function must allocate
     * and initialize a reentrant mutex and return its address through the `mutex` parameter.
     * If the address of an already allocated mutex is provided as an input, the allocation part
     * is skipped and the mutex is only reinitialized.
     * Mutexes are not required if multithreaded access to modem functionalities is not needed.
     * In this case, the function must blindly return ``0``.
     */
    return 0;
}
/*
void nrf_modem_os_mutex_unlock(void *sem)
{
    // Unlock a mutex.
}
*/
int nrf_modem_os_mutex_lock(void *sem, int timeout)
{
    /* Try to lock a reentrant mutex with the given timeout. */
    return 0;
}

void nrf_modem_os_log(int level, const char *fmt, ...)
{
    /* Generic logging procedure. */
  //  va_list ap;
  //  va_start(ap, fmt);
  //  vprintf(fmt, ap);
  //  printf("\n");
  //  va_end(ap);
}

void nrf_modem_os_logdump(int level, const char *str, const void *data, size_t len)
{
    /* Log hex representation of object. */
}



// Configure non-secure peripherals.

void nrf_spu_periph_set(uint16_t id, uint32_t flags);
void nrf_spu_periph_clear(uint16_t id, uint32_t flags);
void nrf_spu_gpio_set(uint16_t id);
void nrf_spu_gpio_clear(uint16_t id);

// Non-secure callable function
extern uint32_t pxNSCFunctionHandler( uint32_t value );

//***********************************
// Print modem SHMEM CFG
//***********************************
void print_modem_shmem_cfg(void)
{
  printf("======== Modem SHMEM Config ========\n");

  printf("CTRL  : base = 0x%08X, size = %u bytes\n",
        (unsigned int)(uintptr_t)modem_shmem_cfg.ctrl.base,
        (unsigned int)modem_shmem_cfg.ctrl.size);

  printf("TX    : base = 0x%08X, size = %u bytes\n",
        (unsigned int)(uintptr_t)modem_shmem_cfg.tx.base,
        (unsigned int)modem_shmem_cfg.tx.size);

  printf("RX    : base = 0x%08X, size = %u bytes\n",
        (unsigned int)(uintptr_t)modem_shmem_cfg.rx.base,
        (unsigned int)modem_shmem_cfg.rx.size);

  printf("TRACE : base = 0x%08X, size = %u bytes\n",
        (unsigned int)(uintptr_t)modem_shmem_cfg.trace.base,
        (unsigned int)modem_shmem_cfg.trace.size);

  printf("====================================\n");
}

//*********************************************************************
//       main()
//*********************************************************************
int main(void) 
{
 int i, j, k = 0;

 printf("Hello World!\n");
 printf(nrf_modem_build_version());
 printf("\n");

 print_modem_shmem_cfg();

 struct nrf_modem_init_params init_params = {
        .shmem = modem_shmem_cfg,
        .ipc_irq_prio = 0,               // 0 ... 7
        .fault_handler = modem_fault_handler,
        .dfu_handler = NULL,             // optional
  };

  int err = nrf_modem_init(&init_params);
  if (err < 0) 
     {
        printf("Modem init failed: %d\n", err);
     }
 
  nrf_gpio_cfg_output(2);
  nrf_gpio_pin_set(2);
  nrf_gpio_cfg_output(3);
  nrf_gpio_pin_set(3);
  nrf_gpio_cfg_output(4);
  nrf_gpio_pin_set(4);
  nrf_gpio_cfg_output(5);
  nrf_gpio_pin_set(5);

  printf("NSC function 1 call result: %d\n", entry1(2));
  printf("NSC function 2 call result: %d\n", entry2(2));

  j = 0;

  do 
    {
      for (i = 0; i < 10000; i++) 
          {
            printf("Hello World %d!\n", i);;
          }

      nrf_gpio_pin_toggle( j + 2 );
      j = ( j + 1 ) % 4;
    } while (1);
}

/*************************** End of file ****************************/

Given that:

  • SHMEM areas are correctly aligned

  • Memory is below 0x20020000

  • Sizes are valid (4K + 3×12K = 40K)

  • Fault handler is defined correctly

  • nrf_modem_os_* stubs are implemented

I cannot identify why nrf_modem_init() returns -EINVAL. Could you help confirm whether this is a modem validation bug, a missing requirement, or a configuration issue?

Best regards,

Neculai

Parents
  • Hi,

     

    It looks like you are lacking porting of several functions. Please see our documentation for porting guidelines on nrf_modem:

    https://docs.nordicsemi.com/bundle/ncs-latest/page/nrfxlib/nrf_modem/doc/ug_nrf_modem_porting_os.html

    Try setting the .dfu handler member to a non-null value to avoid the -22 return value.

     

    Afterwards, you will fault in your secure fw, where the non-secure part is unable to access 0x40005000 (NRF_POWER_NS / NRF_CLOCK_NS area). Looks like you have a secure side permission problem.

      

    Kind regards,

    Håkon

  • Hi,

    I have implemented the changes you previously suggested and also went through all the documented steps for integrating the `modem-lib` library in my bare-metal application. While there seems to be some improvement, I am still getting the error code `-110` during modem initialization, which I couldn't find documented anywhere.

    Here is the output from the console:

    ```
    Hello World!
    3.0.0-cellular-5dc5bc768dda
    ======== Modem SHMEM Config ========
    CTRL : base = 0x20010000, size = 1256 bytes
    TX : base = 0x200104E8, size = 12288 bytes
    RX : base = 0x200134E8, size = 12288 bytes
    TRACE : base = 0x200164E8, size = 12288 bytes
    ====================================
    Modem init failed: -110
    ```

    To help you better understand the issue, I’ve also attached a minimal project archive that reproduces the problem — it only attempts to initialize the modem.

    Could you please clarify what error code `-110` means and what may cause this failure?

    Thank you in advance for your support.

    Neculai

    4621.nRF9160.zip

  • Hi,

     

    You need to port your functions in nrf_modem_os.

    And also be aware of this requirement:

    https://docs.nordicsemi.com/bundle/ncs-latest/page/nrfxlib/nrf_modem/doc/ug_nrf_modem_porting_os.html#ipc_driver_and_interrupt

    Declare the IRQHandler:

     

    #include <nrfx_ipc.h>
    
    ...
    
    void IPC_IRQHandler()
    {
        nrfx_ipc_irq_handler();
        ...
    }

    And remember to enable it:

    NVIC_SetPriority(IPC_IRQn, priority);
    NVIC_EnableIRQ(IPC_IRQn);

    Neculai Agavriloaie said:

    To help you better understand the issue, I’ve also attached a minimal project archive that reproduces the problem — it only attempts to initialize the modem.

    Could you please clarify what error code `-110` means and what may cause this failure?

    return codes are defined here: https://github.com/nrfconnect/sdk-nrfxlib/blob/v3.0-branch/nrf_modem/include/nrf_errno.h

     

    PS: you can have a look at the nrf_modem_os port file in this thread to get an idea which functions needs to be implemented:  nRF9160: Porting the modem library to work with bare metal application 

      

    Kind regards,

    Håkon

  • Dear Nordic Semiconductor Support Team,

    I am writing to express my dissatisfaction with the support I have received regarding the integration of the Nordic modem in a bare-metal environment without an operating system.

    On one hand, Nordic officially promotes the ability to integrate the modem independently of any RTOS, which is a critical requirement for my project. On the other hand, the available documentation for this type of integration is extremely ambiguous and lacks the clarity needed for a proper implementation.

    Furthermore, the support responses I've received have referred me to outdated examples from over two years ago, which no longer align with the current version of the modem library. This approach is unhelpful and hinders progress on a system that Nordic claims to support.

    I kindly ask for up-to-date and concrete guidance, or at the very least, a minimal working example that reflects the current state of the modem library in a bare-metal context.

    Thank you,

    Neculai

Reply
  • Dear Nordic Semiconductor Support Team,

    I am writing to express my dissatisfaction with the support I have received regarding the integration of the Nordic modem in a bare-metal environment without an operating system.

    On one hand, Nordic officially promotes the ability to integrate the modem independently of any RTOS, which is a critical requirement for my project. On the other hand, the available documentation for this type of integration is extremely ambiguous and lacks the clarity needed for a proper implementation.

    Furthermore, the support responses I've received have referred me to outdated examples from over two years ago, which no longer align with the current version of the modem library. This approach is unhelpful and hinders progress on a system that Nordic claims to support.

    I kindly ask for up-to-date and concrete guidance, or at the very least, a minimal working example that reflects the current state of the modem library in a bare-metal context.

    Thank you,

    Neculai

Children
  • Dear Neculai,

     

    I am sorry to hear that this has been dis-satisfactory.

     

    Neculai Agavriloaie said:
    On one hand, Nordic officially promotes the ability to integrate the modem independently of any RTOS, which is a critical requirement for my project.

    This is correct. we do provide this option.

    Neculai Agavriloaie said:
    On the other hand, the available documentation for this type of integration is extremely ambiguous and lacks the clarity needed for a proper implementation.

    I suspect that the naming convention of the file is causing some confusion here. This is a "port.c" file, which requires your system functions for alloc/free of memory, as well as delay functionality and semaphore/mutex implementation, among others.

    The reason why I shared a link to an older case to you, so that you can see the amount of functions that needs implementation.

    The file itself is a port file, and you cannot leave functions empty, as they are required to be implemented in order for the library to work as intended.

     

    Kind regards,

    Håkon

  • Dear Håkon,

    Thank you for your response.

    However, it remains clear that the available information for performing a proper bare-metal port of the modem library is insufficient for third-party developers. It seems that only certain engineers within Nordic have the complete understanding required to implement a functional and correct port.

    Could you please check internally with the Nordic engineering team if there is a minimal and fully functional example project — ready to be flashed and tested directly on an nRF9160 DK — that demonstrates a correct integration of the modem library in a bare-metal environment (i.e., without an RTOS)?

    Such an example would be extremely helpful for ensuring the porting is done in compliance with Nordic’s expectations and avoiding undefined behavior.

    Thank you again for your support.

    Kind regards,
    Neculai 

Related