MeshX 0.4
This repository provides an implementation for Bluetooth Low Energy (BLE) Mesh network nodes. The project allows you to create BLE mesh nodes that can communicate with each other, enabling the development of smart home solutions or other IoT-based applications.
Loading...
Searching...
No Matches
meshx_kv_engine.h File Reference

Portable Key-Value Engine for MeshX. More...

#include "meshx_err.h"
#include "interface/utils/meshx_fal_interface.h"
#include <stdint.h>
#include <stdbool.h>

Go to the source code of this file.

Functions

meshx_err_t meshx_kv_engine_init (const meshx_fal_partition_t *part)
 Initialize the KV engine.
meshx_err_t meshx_kv_engine_read (const char *key, void *buf, uint16_t len)
 Read a value from the KV engine.
meshx_err_t meshx_kv_engine_set (const char *key, const void *buf, uint16_t len)
 Buffer a write operation in RAM.
meshx_err_t meshx_kv_engine_commit (void)
 Commit all buffered changes to flash.
meshx_err_t meshx_kv_engine_remove (const char *key)
 Remove a key from the KV engine.
meshx_err_t meshx_kv_engine_erase_all (void)
 Erase the entire KV partition.

Detailed Description

Portable Key-Value Engine for MeshX.

Copyright © 2024 - 2025 MeshX

This engine provides a lightweight KV store with built-in wear-leveling and power-fail safety, designed to run on top of the FAL interface.

Author
Pranjal Chanda

Function Documentation

◆ meshx_kv_engine_commit()

meshx_err_t meshx_kv_engine_commit ( void )

Commit all buffered changes to flash.

Returns
meshx_err_t MESHX_SUCCESS on success, or error code.

< "MX"

< "MX"

182{
184 while (cur) {
185 kv_header_t header = {
186 .magic = KV_MAGIC,
187 .status = KV_STATUS_VALID,
188 .key_len = (uint8_t)strlen(cur->key),
189 .val_len = cur->len,
190 .seq_id = kv_next_seq_id++,
191 .crc = kv_calc_crc(cur->data, cur->len)
192 };
193
194 uint32_t size = kv_align(sizeof(header) + header.key_len + header.val_len);
195 if (kv_write_pos + size > kv_part->size) {
197 if (err != MESHX_SUCCESS) return err;
198 }
199
200 meshx_fal_write(kv_part, kv_write_pos, &header, sizeof(header));
201 meshx_fal_write(kv_part, kv_write_pos + sizeof(header), cur->key, header.key_len);
202 meshx_fal_write(kv_part, kv_write_pos + sizeof(header) + header.key_len, cur->data, header.val_len);
203
204 // Verification
205 kv_header_t v_header;
206 meshx_fal_read(kv_part, kv_write_pos, &v_header, sizeof(v_header));
207 if (v_header.magic != KV_MAGIC) {
208 MESHX_LOGE(MODULE_ID_COMPONENT_MESHX_NVS, "KV Verify FAILED at pos %u! (read: 0x%04x)", kv_write_pos, v_header.magic);
209 }
210
211 MESHX_LOGD(MODULE_ID_COMPONENT_MESHX_NVS, "KV Commit: key=%s, seq=%u, pos=%u", cur->key, header.seq_id, kv_write_pos);
212 kv_write_pos += size;
213
214 kv_pending_t *prev = cur;
215 cur = cur->next;
216 free(prev->data);
217 free(prev);
218 }
219 pending_list = NULL;
220 return MESHX_SUCCESS;
221}
meshx_err_t
MeshX Error Codes.
Definition meshx_err.h:43
meshx_err_t meshx_fal_read(const meshx_fal_partition_t *part, uint32_t offset, void *buf, size_t len)
Read data from a flash partition.
meshx_err_t meshx_fal_write(const meshx_fal_partition_t *part, uint32_t offset, const void *buf, size_t len)
Write data to a flash partition.
static kv_pending_t * pending_list
Definition meshx_kv_engine.c:46
static uint32_t kv_align(uint32_t size)
Definition meshx_kv_engine.c:61
static meshx_err_t kv_engine_gc(void)
Definition meshx_kv_engine.c:170
static uint16_t kv_calc_crc(const uint8_t *data, uint32_t len)
Definition meshx_kv_engine.c:48
static const meshx_fal_partition_t * kv_part
Definition meshx_kv_engine.c:43
struct kv_pending_node kv_pending_t
#define KV_MAGIC
Definition meshx_kv_engine.c:15
static uint32_t kv_next_seq_id
Definition meshx_kv_engine.c:45
static uint32_t kv_write_pos
Definition meshx_kv_engine.c:44
#define KV_STATUS_VALID
Definition meshx_kv_engine.c:16
#define MESHX_LOGE(module_id, format,...)
Definition meshx_log.h:114
#define MESHX_LOGD(module_id, format,...)
Definition meshx_log.h:132
**This function is called by the parent element when a state change request *is received It validates the request and returns a result to the element not the model layer **return * MESHX_SUCCESS
Definition meshx_model_level.cpp:385
@ MODULE_ID_COMPONENT_MESHX_NVS
Definition module_id.h:31
Definition meshx_kv_engine.c:27
uint16_t val_len
Definition meshx_kv_engine.c:31
uint32_t seq_id
Definition meshx_kv_engine.c:32
uint16_t magic
Definition meshx_kv_engine.c:28
uint8_t key_len
Definition meshx_kv_engine.c:30
char key[32]
Definition meshx_kv_engine.c:37
struct kv_pending_node * next
Definition meshx_kv_engine.c:40
uint16_t len
Definition meshx_kv_engine.c:39
uint8_t * data
Definition meshx_kv_engine.c:38

◆ meshx_kv_engine_erase_all()

meshx_err_t meshx_kv_engine_erase_all ( void )

Erase the entire KV partition.

Returns
meshx_err_t MESHX_SUCCESS on success, or error code.
233{
235 if (err == MESHX_SUCCESS) {
236 kv_write_pos = 0;
237 kv_next_seq_id = 1;
238 }
239 return err;
240}
meshx_err_t meshx_fal_erase(const meshx_fal_partition_t *part, uint32_t offset, size_t len)
Erase a range of a flash partition.

◆ meshx_kv_engine_init()

meshx_err_t meshx_kv_engine_init ( const meshx_fal_partition_t * part)

Initialize the KV engine.

Parameters
[in]partPointer to the flash partition to use.
Returns
meshx_err_t MESHX_SUCCESS on success, or error code.

< "MX"

67{
68 if (!part) return MESHX_INVALID_ARG;
69 kv_part = part;
70 kv_write_pos = 0;
72
73 // Scan to find write head and max seq_id
74 kv_header_t header;
75 while (kv_write_pos + sizeof(header) <= kv_part->size) {
76 meshx_err_t err = meshx_fal_read(kv_part, kv_write_pos, &header, sizeof(header));
77 if (err) return err;
78
79 if (header.magic != KV_MAGIC || header.status == KV_STATUS_EMPTY) {
80 break;
81 }
82
83 if (header.key_len > KV_MAX_KEY_LEN || header.val_len > 256) {
84 MESHX_LOGE(MODULE_ID_COMPONENT_MESHX_NVS, "KV Init: Invalid lengths at pos %u (key:%d, val:%d)", kv_write_pos, header.key_len, header.val_len);
85 break;
86 }
87
88 if (header.seq_id >= kv_next_seq_id) {
89 kv_next_seq_id = header.seq_id + 1;
90 }
91
92 char kbuf[KV_MAX_KEY_LEN];
93 meshx_fal_read(kv_part, kv_write_pos + sizeof(header), kbuf, header.key_len < KV_MAX_KEY_LEN ? header.key_len : KV_MAX_KEY_LEN-1);
94 kbuf[header.key_len < KV_MAX_KEY_LEN ? header.key_len : KV_MAX_KEY_LEN-1] = '\0';
95#ifdef KV_DEBUG_EN
96 MESHX_LOGD(MODULE_ID_COMPONENT_MESHX_NVS, " Found Record: key=%s, seq=%u, pos=%u", kbuf, header.seq_id, kv_write_pos);
97#endif /* KV_DEBUG_EN */
98 kv_write_pos += kv_align(sizeof(header) + header.key_len + header.val_len);
99 }
100
101 MESHX_LOGD(MODULE_ID_COMPONENT_MESHX_NVS, "KV Engine Init: Pos=%u, SeqID=%u", kv_write_pos, kv_next_seq_id);
102 return MESHX_SUCCESS;
103}
@ MESHX_INVALID_ARG
Definition meshx_err.h:46
#define KV_MAX_KEY_LEN
Definition meshx_kv_engine.c:20
#define KV_STATUS_EMPTY
Definition meshx_kv_engine.c:18
uint8_t status
Definition meshx_kv_engine.c:29

◆ meshx_kv_engine_read()

meshx_err_t meshx_kv_engine_read ( const char * key,
void * buf,
uint16_t len )

Read a value from the KV engine.

Parameters
[in]keyKey name.
[out]bufBuffer to store read data.
[in]lenLength of the data to read.
Returns
meshx_err_t MESHX_SUCCESS on success, or error code.

< "MX"

106{
107 if (!kv_part || !key || !buf) return MESHX_INVALID_ARG;
108
109 // Check pending writes first (most recent)
110 kv_pending_t *pending = pending_list;
111 while (pending) {
112 if (strncmp(pending->key, key, KV_MAX_KEY_LEN - 1) == 0) {
113 uint16_t to_copy = (len < pending->len) ? len : pending->len;
114 memcpy(buf, pending->data, to_copy);
115 return MESHX_SUCCESS;
116 }
117 pending = pending->next;
118 }
119
120 uint32_t pos = 0;
121 kv_header_t header;
122 uint32_t best_pos = 0xFFFFFFFF;
123 uint32_t max_seq = 0;
124 uint16_t found_val_len = 0;
125
126 while (pos < kv_write_pos) {
127 if (meshx_fal_read(kv_part, pos, &header, sizeof(header)) != MESHX_SUCCESS) break;
128 if (header.magic != KV_MAGIC) break;
129
130 if (header.status == KV_STATUS_VALID && header.key_len == strlen(key)) {
131 char found_key[KV_MAX_KEY_LEN];
132 meshx_fal_read(kv_part, pos + sizeof(header), found_key, header.key_len);
133 if (strncmp(found_key, key, header.key_len) == 0) {
134 if (header.seq_id >= max_seq) {
135 max_seq = header.seq_id;
136 best_pos = pos;
137 found_val_len = header.val_len;
138 }
139 }
140 }
141 pos += kv_align(sizeof(header) + header.key_len + header.val_len);
142 }
143
144 if (best_pos != 0xFFFFFFFF) {
145 uint16_t to_read = (len < found_val_len) ? len : found_val_len;
146 kv_header_t h;
147 meshx_fal_read(kv_part, best_pos, &h, sizeof(h));
148 meshx_fal_read(kv_part, best_pos + sizeof(h) + h.key_len, buf, to_read);
149 MESHX_LOGD(MODULE_ID_COMPONENT_MESHX_NVS, "KV Read Success: key=%s, seq=%u, len=%d", key, max_seq, to_read);
150 return MESHX_SUCCESS;
151 }
152
153 MESHX_LOGD(MODULE_ID_COMPONENT_MESHX_NVS, "KV Read Not Found: key=%s", key);
154 return MESHX_NOT_FOUND;
155}
@ MESHX_NOT_FOUND
Definition meshx_err.h:50

◆ meshx_kv_engine_remove()

meshx_err_t meshx_kv_engine_remove ( const char * key)

Remove a key from the KV engine.

Parameters
[in]keyKey name.
Returns
meshx_err_t MESHX_SUCCESS on success, or error code.
224{
225 // Removing in log-structured means appending a "Deleted" record or marking status
226 // Here we append a record with status OBSOLETE or just set length to 0
227 uint8_t dummy = 0;
228 return meshx_kv_engine_set(key, &dummy, 0);
229 // The next read will find this (seq_id will be higher) and we can treat len=0 as deleted
230}
meshx_err_t meshx_kv_engine_set(const char *key, const void *buf, uint16_t len)
Buffer a write operation in RAM.
Definition meshx_kv_engine.c:157

◆ meshx_kv_engine_set()

meshx_err_t meshx_kv_engine_set ( const char * key,
const void * buf,
uint16_t len )

Buffer a write operation in RAM.

Parameters
[in]keyKey name.
[in]bufBuffer containing data to write.
[in]lenLength of the data.
Returns
meshx_err_t MESHX_SUCCESS on success, or error code.
158{
159 kv_pending_t *node = malloc(sizeof(kv_pending_t));
160 if (!node) return MESHX_NO_MEM;
161 strncpy(node->key, key, KV_MAX_KEY_LEN - 1);
162 node->data = malloc(len);
163 memcpy(node->data, buf, len);
164 node->len = len;
165 node->next = pending_list;
166 pending_list = node;
167 return MESHX_SUCCESS;
168}
@ MESHX_NO_MEM
Definition meshx_err.h:48