This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Models and Subscription Lists are written to flash on every start-up

When starting up the device with the nRF5 SDK 3.2.0 for mesh, even though the model and subscription list state is loaded from flash, it is stored again right afterwards.

I think the problem is in the access_model_add and the mesh_stack_init functions:
The access_model_add function allocates a new model in case it is not recovered from flash and sets it as outdated, so its state is eventually written to flash. The function fails, if m_access_model_config_frozen is true.

uint32_t access_model_add(const access_model_add_params_t *p_model_params,
                          access_model_handle_t *p_model_handle) {
/* ... */
if (0 == p_model_params->opcode_count && NULL != p_model_params->p_opcode_handlers) {
    return NRF_ERROR_INVALID_LENGTH;
  } else if (p_model_params->element_index >= ACCESS_ELEMENT_COUNT) {
    return NRF_ERROR_NOT_FOUND;
  } else if (m_access_model_config_frozen || (element_has_model_id(p_model_params->element_index, p_model_params->model_id, p_model_handle) && ACCESS_INTERNAL_STATE_IS_ALLOCATED(m_model_pool[*p_model_handle].internal_state))) {
    return NRF_ERROR_FORBIDDEN;
  } else if (!opcodes_are_valid(p_model_params->p_opcode_handlers, p_model_params->opcode_count)) {
    return NRF_ERROR_INVALID_PARAM;
  } else if (*p_model_handle == ACCESS_HANDLE_INVALID) /* The model was not recovered from the flash */
  {
    *p_model_handle = find_available_model();
    if (ACCESS_HANDLE_INVALID == *p_model_handle) {
      return NRF_ERROR_NO_MEM;
    }

    m_model_pool[*p_model_handle].model_info.publish_address_handle = DSM_HANDLE_INVALID;
    m_model_pool[*p_model_handle].model_info.publish_appkey_handle = DSM_HANDLE_INVALID;
    m_model_pool[*p_model_handle].model_info.element_index = p_model_params->element_index;
    m_model_pool[*p_model_handle].model_info.model_id.model_id = p_model_params->model_id.model_id;
    m_model_pool[*p_model_handle].model_info.model_id.company_id = p_model_params->model_id.company_id;
    m_model_pool[*p_model_handle].model_info.publish_ttl = ACCESS_TTL_USE_DEFAULT;
    increment_model_count(p_model_params->element_index, p_model_params->model_id.company_id);
    ACCESS_INTERNAL_STATE_OUTDATED_SET(m_model_pool[*p_model_handle].internal_state);
  }
}

However, in the mesh_stack_init function, the models are first initialized (which call access_model_add internally) and then the state is recovered from flash:

uint32_t mesh_stack_init(const mesh_stack_init_params_t * p_init_params,
                         bool * p_device_provisioned)
{
    /* ... */
    
    /* Initialize the access layer */
    dsm_init();
    access_init();

    /* Initialize the configuration server */
    status = config_server_init(p_init_params->models.config_server_cb);
    if (status != NRF_SUCCESS)
    {
        return status;
    }

    /* Initialize the health server for the primary element */
    status = health_server_init(&m_health_server, 0, DEVICE_COMPANY_ID,
                                p_init_params->models.health_server_attention_cb,
                                p_init_params->models.p_health_server_selftest_array,
                                p_init_params->models.health_server_num_selftests);
    if (status != NRF_SUCCESS)
    {
        return status;
    }

    /* Give application opportunity to initialize application specific models */
    if (p_init_params->models.models_init_cb != NULL)
    {
        p_init_params->models.models_init_cb();
    }

    /* Load configuration, and check if the device has already been provisioned */
    mesh_config_load();

    (void) dsm_flash_config_load();

    if (access_flash_config_load())
    {
        access_flash_config_store();
    }

    /* ... */
    return NRF_SUCCESS;
}


The access_flash_config_load function will set m_access_model_config_frozen to true, which means, it has to be called after the models were initialized.

In consequence, access_model_add will always allocate a new model and assume its state has not been recovered from flash. It will then set the outdated flag which stores the model to flash again when access_flash_config_store is called. The internal restore_acquired_model function only sets the restored flag, but does not remove the outdated flag, which was set before.

I guess the current implementation is wrongly assuming that access_model_add could be called after the model state has been restored, which can never be the case as with the current implementation of mesh_stack_init. I think this can be fixed in SDK version 3.2.0 in the restore_acquired_model, restore_acquired_element, restore_acquired_subscription_list functions by calling ACCESS_INTERNAL_STATE_OUTDATED_CLR in each of the functions.

I did also take a quick look a the current nRF5 Mesh SDK 4.0.0 where the above code has been refactored. I guess the issue is even worse for this version of the SDK as the model state is stored directly in the access_model_add function which definitely happens before the mesh_stack_init function could load model data from flash.

Is this a bug in the SDKs or am I doing something wrong here?

Parents Reply Children
No Data
Related