Files
EPD-nRF5/components/ble/peer_manager/peer_data_storage.c
Shuanglei Tao f353d23368 Initial commit
2024-11-11 15:35:36 +08:00

670 lines
19 KiB
C

/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#include "peer_data_storage.h"
#include <stdint.h>
#include <string.h>
#include "sdk_errors.h"
#include "peer_manager_types.h"
#include "peer_id.h"
#include "peer_data.h"
#include "fds.h"
#include "nrf_log.h"
#define MAX_REGISTRANTS 6 /**< The number of user that can register with the module. */
#define MODULE_INITIALIZED (m_pds.n_registrants > 0) /**< Expression which is true when the module is initialized. */
/**@brief Macro for verifying that the module is initialized. It will cause the function to return
* @ref NRF_ERROR_INVALID_STATE if not.
*/
#define VERIFY_MODULE_INITIALIZED() \
do \
{ \
if (!MODULE_INITIALIZED) \
{ \
return NRF_ERROR_INVALID_STATE; \
} \
} while(0)
/**@brief Macro for verifying that the module is initialized. It will cause the function to return
* if not.
*/
#define VERIFY_MODULE_INITIALIZED_VOID() \
do \
{ \
if (!MODULE_INITIALIZED) \
{ \
return; \
} \
} while(0)
/**@brief Macro for verifying that the param is not NULL. It will cause the function to return
* if not.
*
* @param[in] param The variable to check if is NULL.
*/
#define VERIFY_PARAM_NOT_NULL(param) \
do \
{ \
if (param == NULL) \
{ \
return NRF_ERROR_NULL; \
} \
} while(0)
/**@brief Macro for verifying that param is not zero. It will cause the function to return
* if not.
*
* @param[in] param The variable to check if is zero.
*/
#define VERIFY_PARAM_NOT_ZERO(param) \
do \
{ \
if (param == 0) \
{ \
return NRF_ERROR_NULL; \
} \
} while(0)
/**@brief Macro for verifying that the peer id is within a valid range
*
* @param[in] id The peer data id to check.
*/
#define VERIFY_PEER_ID_IN_RANGE(id) \
do \
{ \
if ((id >= PM_PEER_ID_N_AVAILABLE_IDS)) \
{ \
return NRF_ERROR_INVALID_PARAM; \
} \
} while (0)
/**@brief Macro for verifying that the peer data id is withing a valid range
*
* @param[in] id The peer data id to check.
*/
#define VERIFY_PEER_DATA_ID_IN_RANGE(id) \
do \
{ \
if (!PM_PEER_DATA_ID_IS_VALID(id)) \
{ \
return NRF_ERROR_INVALID_PARAM; \
} \
} while (0)
#define PEER_IDS_INITIALIZE() \
do \
{ \
if (!m_pds.peer_ids_initialized) \
{ \
peer_ids_init(); \
} \
} while (0)
typedef struct
{
bool peer_ids_initialized;
pds_evt_handler_t evt_handlers[MAX_REGISTRANTS];
uint8_t n_registrants;
} pds_t;
static pds_t m_pds = {.n_registrants = 0};
static void internal_state_reset(pds_t * p_pds)
{
memset(p_pds, 0, sizeof(pds_t));
}
/**@brief Function for dispatching outbound events to all registered event handlers.
*
* @param[in] p_event The event to dispatch.
*/
static void pds_evt_send(pds_evt_t * p_event)
{
for (int i = 0; i < m_pds.n_registrants; i++)
{
m_pds.evt_handlers[i](p_event);
}
}
/**@brief Function to convert peer id to instance id
*
* @param[in] peer_id Peer id to convert to instance id
*
* @return Value as instance id
*/
static fds_instance_id_t convert_peer_id_to_instance_id(pm_peer_id_t peer_id)
{
return (fds_instance_id_t)(peer_id + peer_id_to_instance_id);
}
/**@brief Function to convert peer data id to type id
*
* @param[in] peer_data_id Peer data id to convert to type_id
*
* @return Value as type id
*/
static fds_type_id_t convert_peer_data_id_to_type_id(pm_peer_data_id_t peer_data_id)
{
return (fds_type_id_t)peer_data_id + (fds_type_id_t)peer_data_id_to_type_id;
}
/**@brief Function to convert peer data id to type id
*
* @param[in] peer_data_id Peer data id to convert to type_id
*
* @return Value as type id
*/
static pm_peer_id_t convert_instance_id_to_peer_id(fds_instance_id_t instance_id)
{
return (pm_peer_id_t)(instance_id + instance_id_to_peer_id);
}
/**@brief Function to type id to peer data id
*
* @param[in] type_id Type id to convert to peer data id
*
* @return Value as peer data id
*/
static pm_peer_data_id_t convert_type_id_to_peer_data_id(fds_type_id_t type_id)
{
return (pm_peer_data_id_t)(type_id + instance_id_to_peer_id);
}
static ret_code_t find_fds_item(pm_peer_id_t peer_id,
pm_peer_data_id_t data_id,
fds_record_desc_t * const p_desc)
{
fds_find_token_t find_tok;
VERIFY_PEER_ID_IN_RANGE(peer_id);
VERIFY_PEER_DATA_ID_IN_RANGE(data_id);
// pp_record verified outside
fds_type_id_t type_id = convert_peer_data_id_to_type_id(data_id);
fds_instance_id_t instance_id = convert_peer_id_to_instance_id(peer_id);
return fds_find(type_id, instance_id, p_desc, &find_tok);
}
static void peer_ids_init()
{
fds_record_t record;
fds_record_desc_t record_desc;
fds_find_token_t find_tok;
fds_type_id_t const type_id = convert_peer_data_id_to_type_id(PM_PEER_DATA_ID_BONDING);
pm_peer_id_t peer_id;
if (!m_pds.peer_ids_initialized)
{
while(fds_find_by_type(type_id, &record_desc, &find_tok) == NRF_SUCCESS)
{
fds_open(&record_desc, &record);
fds_close(&record_desc);
peer_id = convert_instance_id_to_peer_id(record.header.ic.instance);
peer_id_allocate(peer_id);
}
m_pds.peer_ids_initialized = true;
}
}
//uint32_t size_pad_to_mult_of_four(uint32_t unpadded_size)
//{
// return (unpadded_size + 3) & 3;
//}
static void fds_evt_handler(ret_code_t result,
fds_cmd_id_t cmd,
fds_record_id_t record_id,
fds_record_key_t record_key
/*fds_record_t const * const p_record*/)
{
pds_evt_t evt;
switch(cmd)
{
case FDS_CMD_INIT:
break;
case FDS_CMD_UPDATE:
case FDS_CMD_WRITE:
evt.peer_id = convert_instance_id_to_peer_id(record_key.instance);
evt.evt_id = (result == NRF_SUCCESS) ? PDS_EVT_STORED : PDS_EVT_ERROR_STORE;
evt.data_id = convert_type_id_to_peer_data_id(record_key.type);
evt.store_token = record_id;
pds_evt_send(&evt);
break;
case FDS_CMD_CLEAR:
evt.peer_id = convert_instance_id_to_peer_id(record_key.instance);
evt.evt_id = (result == NRF_SUCCESS) ? PDS_EVT_CLEARED : PDS_EVT_ERROR_CLEAR;
evt.data_id = convert_type_id_to_peer_data_id(record_key.type);
evt.store_token = record_id;
pds_evt_send(&evt);
break;
case FDS_CMD_CLEAR_INST:
{
if ((record_key.type == FDS_TYPE_ID_INVALID) &&
(record_key.instance != FDS_TYPE_ID_INVALID))
{
pm_peer_id_t peer_id = convert_instance_id_to_peer_id(record_key.instance);
evt.peer_id = peer_id;
evt.data_id = PM_PEER_DATA_ID_INVALID;
if (result == NRF_SUCCESS)
{
evt.evt_id = PDS_EVT_PEER_ID_CLEAR;
peer_id_free(peer_id);
}
else
{
evt.evt_id = PDS_EVT_ERROR_PEER_ID_CLEAR;
}
}
else
{
// TODO: Not supported yet (clear many without clearing peer_id)
}
pds_evt_send(&evt);
}
break;
case FDS_CMD_GC:
evt.peer_id = convert_instance_id_to_peer_id(record_key.instance);
evt.evt_id = PDS_EVT_COMPRESSED;
evt.data_id = convert_type_id_to_peer_data_id(record_key.type);
evt.store_token = record_id;
pds_evt_send(&evt);
break;
default:
break;
}
}
ret_code_t pds_register(pds_evt_handler_t evt_handler)
{
if (m_pds.n_registrants >= MAX_REGISTRANTS)
{
return NRF_ERROR_NO_MEM;
}
VERIFY_PARAM_NOT_NULL(evt_handler);
if (!MODULE_INITIALIZED)
{
ret_code_t retval;
internal_state_reset(&m_pds);
peer_id_init();
fds_cb_t cb = fds_evt_handler;
retval = fds_register(cb);
if(retval != NRF_SUCCESS)
{
return retval;
}
retval = fds_init();
if(retval != NRF_SUCCESS)
{
return retval;
}
}
m_pds.evt_handlers[m_pds.n_registrants] = evt_handler;
m_pds.n_registrants += 1;
return NRF_SUCCESS;
}
ret_code_t pds_peer_data_read_ptr_get(pm_peer_id_t peer_id,
pm_peer_data_id_t data_id,
pm_peer_data_flash_t * p_data,
pm_store_token_t * p_token)
{
ret_code_t retval;
fds_record_t record;
fds_record_desc_t record_desc;
VERIFY_MODULE_INITIALIZED();
VERIFY_PEER_ID_IN_RANGE(peer_id);
VERIFY_PEER_DATA_ID_IN_RANGE(data_id);
retval = find_fds_item(peer_id, data_id, &record_desc);
if (retval != NRF_SUCCESS)
{
return retval;
}
// Shouldn't fail, unless record is cleared.
fds_open(&record_desc, &record);
// No need to keep it open, since we are not reading.
fds_close(&record_desc);
//NRF_LOG_PRINTF("Found item with peer_id: %d, data_id: %d, Address: %p\r\n", record.p_data);
if (p_data != NULL)
{
p_data->data_type = data_id;
p_data->length_words = record.header.tl.length_words;
p_data->data.p_application_data = (uint8_t const*)record.p_data;
}
if (p_token != NULL)
{
*p_token = (uint32_t)record.header.id;
}
return retval;
}
ret_code_t pds_peer_data_lock(pm_store_token_t store_token)
{
VERIFY_MODULE_INITIALIZED();
VERIFY_PARAM_NOT_ZERO(store_token);
// TODO: Not implemented yet in fds
return NRF_SUCCESS;
}
ret_code_t pds_peer_data_verify(pm_store_token_t store_token)
{
VERIFY_MODULE_INITIALIZED();
VERIFY_PARAM_NOT_ZERO(store_token);
// TODO: Not implemented yet in fds
return NRF_SUCCESS;
}
ret_code_t pds_peer_data_read(pm_peer_id_t peer_id,
pm_peer_data_id_t data_id,
pm_peer_data_t * p_data,
fds_length_t * p_len_words)
{
VERIFY_PEER_ID_IN_RANGE(peer_id);
VERIFY_PEER_DATA_ID_IN_RANGE(data_id);
VERIFY_PARAM_NOT_NULL(p_len_words);
VERIFY_PARAM_NOT_NULL(p_data);
ret_code_t err_code;
pm_peer_data_flash_t peer_data_flash;
err_code = pds_peer_data_read_ptr_get(peer_id, data_id, &peer_data_flash, NULL);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
if ((*p_len_words) == 0)
{
(*p_len_words) = peer_data_flash.length_words;
return NRF_SUCCESS;
}
else if ((*p_len_words) < peer_data_flash.length_words)
{
return NRF_ERROR_NO_MEM;
}
VERIFY_PARAM_NOT_NULL(p_data->data.p_application_data);
err_code = peer_data_deserialize(&peer_data_flash, p_data);
return err_code;
}
ret_code_t pds_peer_data_write_prepare(pm_peer_data_const_t const * p_peer_data,
pm_prepare_token_t * p_prepare_token)
{
ret_code_t retval;
VERIFY_MODULE_INITIALIZED();
VERIFY_PARAM_NOT_NULL(p_peer_data);
VERIFY_PARAM_NOT_NULL(p_prepare_token);
VERIFY_PEER_DATA_ID_IN_RANGE(p_peer_data->data_type);
retval = fds_reserve((fds_write_token_t*)p_prepare_token, p_peer_data->length_words);
return retval;
}
ret_code_t pds_peer_data_write_prepare_cancel(pm_prepare_token_t prepare_token)
{
ret_code_t retval;
VERIFY_MODULE_INITIALIZED();
VERIFY_PARAM_NOT_ZERO(prepare_token);
retval = fds_reserve_cancel((fds_write_token_t*)&prepare_token);
return retval;
}
ret_code_t pds_peer_data_write_prepared(pm_peer_id_t peer_id,
pm_peer_data_const_t const * p_peer_data,
pm_prepare_token_t prepare_token,
pm_store_token_t * p_store_token)
{
ret_code_t retval;
fds_record_desc_t record_desc;
fds_record_key_t record_key;
fds_record_chunk_t chunks[2];
uint16_t n_chunks;
VERIFY_MODULE_INITIALIZED();
//VERIFY_PARAM_NOT_ZERO(prepare_token);
VERIFY_PARAM_NOT_NULL(p_peer_data);
VERIFY_PEER_ID_IN_RANGE(peer_id);
VERIFY_PEER_DATA_ID_IN_RANGE(p_peer_data->data_type);
// Fill in the keys.
record_key.type = convert_peer_data_id_to_type_id(p_peer_data->data_type);
record_key.instance = convert_peer_id_to_instance_id(peer_id);
// Create chunks.
peer_data_parts_get(p_peer_data, chunks, &n_chunks);
retval = fds_write_reserved((fds_write_token_t*)&prepare_token, &record_desc,
record_key, n_chunks, chunks);
if ((retval == NRF_SUCCESS) && (p_store_token != NULL))
{
fds_record_id_from_desc(&record_desc, (fds_record_id_t*)p_store_token);
}
return retval;
}
ret_code_t pds_peer_data_write(pm_peer_id_t peer_id,
pm_peer_data_const_t const * p_peer_data,
pm_store_token_t * p_store_token)
{
ret_code_t retval;
fds_record_desc_t record_desc;
fds_record_key_t record_key;
fds_record_chunk_t chunks[2];
uint16_t n_chunks;
VERIFY_MODULE_INITIALIZED();
VERIFY_PEER_ID_IN_RANGE(peer_id);
VERIFY_PEER_DATA_ID_IN_RANGE(p_peer_data->data_type);
// Fill in the keys.
record_key.type = convert_peer_data_id_to_type_id(p_peer_data->data_type);
record_key.instance = convert_peer_id_to_instance_id(peer_id);
// Create chunks
peer_data_parts_get(p_peer_data, chunks, &n_chunks);
// Request write
retval = fds_write(&record_desc, record_key, n_chunks, chunks);
if ((retval == NRF_SUCCESS) && (p_store_token != NULL))
{
fds_record_id_from_desc(&record_desc, (fds_record_id_t*)p_store_token);
}
return retval;
}
ret_code_t pds_peer_data_update(pm_peer_id_t peer_id,
pm_peer_data_const_t const * p_peer_data,
pm_store_token_t old_token,
pm_store_token_t * p_store_token)
{
ret_code_t retval;
fds_record_desc_t record_desc;
fds_record_key_t record_key;
fds_record_chunk_t chunks[2];
uint16_t n_chunks;
VERIFY_MODULE_INITIALIZED();
VERIFY_PEER_DATA_ID_IN_RANGE(p_peer_data->data_type);
VERIFY_PARAM_NOT_NULL(p_peer_data);
record_key.type = convert_peer_data_id_to_type_id(p_peer_data->data_type);
record_key.instance = convert_peer_id_to_instance_id(peer_id);
// Create chunks
peer_data_parts_get(p_peer_data, chunks, &n_chunks);
fds_descriptor_from_rec_id(&record_desc, (fds_record_id_t)old_token);
retval = fds_update(&record_desc, record_key, n_chunks, chunks);
if ((retval == NRF_SUCCESS) && (p_store_token != NULL))
{
fds_record_id_from_desc(&record_desc, (fds_record_id_t*)p_store_token);
}
return retval;
}
ret_code_t pds_peer_data_clear(pm_peer_id_t peer_id, pm_peer_data_id_t data_id)
{
ret_code_t retval;
fds_type_id_t type_id;
fds_instance_id_t instance_id;
fds_record_desc_t record_desc;
fds_find_token_t find_tok;
VERIFY_MODULE_INITIALIZED();
VERIFY_PEER_ID_IN_RANGE(peer_id);
VERIFY_PEER_DATA_ID_IN_RANGE(data_id);
type_id = convert_peer_data_id_to_type_id(data_id);
instance_id = convert_peer_id_to_instance_id(peer_id);
retval = fds_find(type_id, instance_id, &record_desc, &find_tok);
if(retval != NRF_SUCCESS)
{
return retval;
}
retval = fds_clear(&record_desc);
return retval;
}
pm_peer_id_t pds_peer_id_allocate(void)
{
if (!MODULE_INITIALIZED)
{
return PM_PEER_ID_INVALID;
}
PEER_IDS_INITIALIZE();
return peer_id_allocate(PM_PEER_ID_INVALID);
}
ret_code_t pds_peer_id_free(pm_peer_id_t peer_id)
{
ret_code_t retval;
fds_instance_id_t instance_id;
VERIFY_MODULE_INITIALIZED();
VERIFY_PEER_ID_IN_RANGE(peer_id);
PEER_IDS_INITIALIZE();
instance_id = convert_peer_id_to_instance_id(peer_id);
retval = fds_clear_by_instance(instance_id);
return retval;
}
bool pds_peer_id_is_allocated(pm_peer_id_t peer_id)
{
if (!MODULE_INITIALIZED)
{
return false;
}
PEER_IDS_INITIALIZE();
return peer_id_is_allocated(peer_id);
}
pm_peer_id_t pds_next_peer_id_get(pm_peer_id_t prev_peer_id)
{
if (!MODULE_INITIALIZED)
{
return PM_PEER_ID_INVALID;
}
PEER_IDS_INITIALIZE();
return peer_id_next_id_get(prev_peer_id);
}
uint32_t pds_n_peers(void)
{
if (!MODULE_INITIALIZED)
{
return 0;
}
PEER_IDS_INITIALIZE();
return peer_id_n_ids();
}