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.c File Reference

Enhanced implementation for MeshX Portable Key-Value Engine with GC. More...

#include "meshx_kv_engine.h"
#include "interface/logging/meshx_log.h"
#include <string.h>
#include <stdlib.h>

Data Structures

struct  kv_header_t
struct  kv_pending_node

Macros

#define KV_MAGIC   0x4D58
#define KV_STATUS_VALID   0xEE
#define KV_STATUS_OBSOLETE   0x00
#define KV_STATUS_EMPTY   0xFF
#define KV_MAX_KEY_LEN   32
#define KV_ALIGN_SIZE   4

Typedefs

typedef struct kv_pending_node kv_pending_t

Functions

static uint16_t kv_calc_crc (const uint8_t *data, uint32_t len)
static uint32_t kv_align (uint32_t size)
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.
static meshx_err_t kv_engine_gc (void)
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.

Variables

static const meshx_fal_partition_tkv_part = NULL
static uint32_t kv_write_pos = 0
static uint32_t kv_next_seq_id = 0
static kv_pending_tpending_list = NULL

Detailed Description

Enhanced implementation for MeshX Portable Key-Value Engine with GC.

Copyright © 2024 - 2025 MeshX

Author
Pranjal Chanda

Macro Definition Documentation

◆ KV_ALIGN_SIZE

#define KV_ALIGN_SIZE   4

◆ KV_MAGIC

#define KV_MAGIC   0x4D58

"MX"

◆ KV_MAX_KEY_LEN

#define KV_MAX_KEY_LEN   32

◆ KV_STATUS_EMPTY

#define KV_STATUS_EMPTY   0xFF

◆ KV_STATUS_OBSOLETE

#define KV_STATUS_OBSOLETE   0x00

◆ KV_STATUS_VALID

#define KV_STATUS_VALID   0xEE

Typedef Documentation

◆ kv_pending_t

typedef struct kv_pending_node kv_pending_t

Function Documentation

◆ kv_align()

uint32_t kv_align ( uint32_t size)
static
62{
63 return (size + (KV_ALIGN_SIZE - 1)) & ~(KV_ALIGN_SIZE - 1);
64}
#define KV_ALIGN_SIZE
Definition meshx_kv_engine.c:21

◆ kv_calc_crc()

uint16_t kv_calc_crc ( const uint8_t * data,
uint32_t len )
static
49{
50 uint16_t crc = 0xFFFF;
51 for (uint32_t i = 0; i < len; i++) {
52 crc ^= data[i];
53 for (int j = 0; j < 8; j++) {
54 if (crc & 1) crc = (crc >> 1) ^ 0xA001;
55 else crc >>= 1;
56 }
57 }
58 return crc;
59}

◆ kv_engine_gc()

meshx_err_t kv_engine_gc ( void )
static
171{
172 // Simplified GC: Erase all and re-write only the latest values
173 // In a production system, this would move data sector by sector
174 MESHX_LOGW(MODULE_ID_COMPONENT_MESHX_NVS, "KV GC triggered (Simplified)");
175
176 // 1. Scan and find latest version of all unique keys (requires RAM or multiple scans)
177 // For now, we'll just return error to signify "Partition Full" until we implement full GC
178 return MESHX_FAIL;
179}
@ MESHX_FAIL
Definition meshx_err.h:45
#define MESHX_LOGW(module_id, format,...)
Definition meshx_log.h:120
@ MODULE_ID_COMPONENT_MESHX_NVS
Definition module_id.h:31

◆ 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
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

Variable Documentation

◆ kv_next_seq_id

uint32_t kv_next_seq_id = 0
static

◆ kv_part

const meshx_fal_partition_t* kv_part = NULL
static

◆ kv_write_pos

uint32_t kv_write_pos = 0
static

◆ pending_list

kv_pending_t* pending_list = NULL
static