xref: /btstack/platform/embedded/btstack_tlv_flash_bank.c (revision 26c447f6532db1fac97a31f7ee907145bb770321)
1 /*
2  * Copyright (C) 2017 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
21  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
24  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  */
31 
32 #define BTSTACK_FILE__ "btstack_tlv_flash_bank.c"
33 
34 #include "btstack_tlv.h"
35 #include "btstack_tlv_flash_bank.h"
36 #include "btstack_debug.h"
37 #include "btstack_util.h"
38 #include "btstack_debug.h"
39 
40 #include <string.h>
41 #include <inttypes.h>
42 
43 // Header:
44 // - Magic: 'BTstack'
45 // - Status:
46 //   - bits 765432: reserved
47 //	 - bits 10:     epoch
48 
49 // Entries
50 // - Tag: 32 bit
51 // - Len: 32 bit
52 // - Delete: 32 delete field - only used with ENABLE_TLV_FLASH_EXPLICIT_DELETE_FIELD
53 // - Value: Len in bytes
54 
55 // ENABLE_TLV_FLASH_EXPLICIT_DELETE_FIELD
56 //
57 // Most Flash implementations allow to:
58 // - erase sector -> all values are 0xff
59 // - write value (1s -> 0s)
60 // - overwrite value with zero (remaining 1s -> 0s)
61 //
62 // We use the ability to overwrite a value with zeros to mark deleted enttries (by writing zero into the tag field).
63 // Some targets, E.g. Kinetix K64F, do enot allow for that.
64 //
65 // With ENABLE_TLV_FLASH_EXPLICIT_DELETE_FIELD an extra field is reserved to indicate a deleted tag, while keeping main logic
66 //
67 // With ENABLE_TLV_FLASH_WRITE_ONCE, tags are never marked as deleted. Instead, an emtpy tag will be written instead.
68 //     Also, lookup and migrate requires to always search until the end of the valid bank
69 
70 #if defined (ENABLE_TLV_FLASH_EXPLICIT_DELETE_FIELD) && defined (ENABLE_TLV_FLASH_WRITE_ONCE)
71 #error "Please define either ENABLE_TLV_FLASH_EXPLICIT_DELETE_FIELD or ENABLE_TLV_FLASH_WRITE_ONCE"
72 #endif
73 
74 #define BTSTACK_TLV_HEADER_LEN 8
75 
76 #ifndef BTSTACK_FLASH_ALIGNMENT_MAX
77 #define BTSTACK_FLASH_ALIGNMENT_MAX 8
78 #endif
79 
80 static const char * btstack_tlv_header_magic = "BTstack";
81 
82 // TLV Iterator
83 typedef struct {
84 	int 	 bank;
85 	uint32_t offset;
86 	uint32_t tag;
87 	uint32_t len;
88 } tlv_iterator_t;
89 
90 static uint32_t btstack_tlv_flash_bank_align_size(btstack_tlv_flash_bank_t * self, uint32_t size){
91 	uint32_t aligment = self->hal_flash_bank_impl->get_alignment(self->hal_flash_bank_context);
92 	return (size + aligment - 1) & ~(aligment - 1);
93 }
94 
95 // support unaligned flash read/writes
96 // strategy: increase size to meet alignment, perform unaligned read/write of last chunk with helper buffer
97 
98 static void btstack_tlv_flash_bank_read(btstack_tlv_flash_bank_t * self, int bank, uint32_t offset, uint8_t * buffer, uint32_t size){
99 
100 	// read main data
101 	uint32_t aligment = self->hal_flash_bank_impl->get_alignment(self->hal_flash_bank_context);
102 	uint32_t lower_bits = size & (aligment - 1);
103 	uint32_t size_aligned = size - lower_bits;
104 	if (size_aligned){
105 		self->hal_flash_bank_impl->read(self->hal_flash_bank_context, bank, offset, buffer, size_aligned);
106 		buffer += size_aligned;
107 		offset += size_aligned;
108 		size   -= size_aligned;
109 	}
110 
111 	// read last part
112 	if (size == 0) return;
113 	uint8_t aligment_block[BTSTACK_FLASH_ALIGNMENT_MAX];
114 	self->hal_flash_bank_impl->read(self->hal_flash_bank_context, bank, offset, aligment_block, aligment);
115 	uint32_t bytes_to_copy = btstack_min(aligment - lower_bits, size);
116 	memcpy(buffer, aligment_block, bytes_to_copy);
117 }
118 
119 static void btstack_tlv_flash_bank_write(btstack_tlv_flash_bank_t * self, int bank, uint32_t offset, const uint8_t * buffer, uint32_t size){
120 
121 	// write main data
122 	uint32_t aligment = self->hal_flash_bank_impl->get_alignment(self->hal_flash_bank_context);
123 	uint32_t lower_bits = size & (aligment - 1);
124 	uint32_t size_aligned = size - lower_bits;
125 	if (size_aligned){
126 		self->hal_flash_bank_impl->write(self->hal_flash_bank_context, bank, offset, buffer, size_aligned);
127 		buffer += size_aligned;
128 		offset += size_aligned;
129 		size   -= size_aligned;
130 	}
131 
132 	// write last part
133 	if (size == 0) return;
134 	uint8_t aligment_block[BTSTACK_FLASH_ALIGNMENT_MAX];
135 	memset(aligment_block, 0xff, aligment);
136 	memcpy(aligment_block, buffer, lower_bits);
137 	self->hal_flash_bank_impl->write(self->hal_flash_bank_context, bank, offset, aligment_block, aligment);
138 }
139 
140 
141 // iterator
142 
143 static void btstack_tlv_flash_bank_iterator_fetch_tag_len(btstack_tlv_flash_bank_t * self, tlv_iterator_t * it){
144     if (it->offset + 8 + self->delete_tag_len >= self->hal_flash_bank_impl->get_size(self->hal_flash_bank_context)){
145         it->tag = 0xffffffff;
146         return;
147     }
148 
149 	uint8_t entry[8];
150 	btstack_tlv_flash_bank_read(self, it->bank, it->offset, entry, 8);
151 	it->tag = big_endian_read_32(entry, 0);
152 	it->len = big_endian_read_32(entry, 4);
153 
154 #ifdef ENABLE_TLV_FLASH_EXPLICIT_DELETE_FIELD
155 	// clear tag, if delete field is set
156 	uint32_t delete_tag;
157 	btstack_tlv_flash_bank_read(self, it->bank, it->offset + 8, (uint8_t *) &delete_tag, 4);
158 	if (delete_tag == 0){
159 		it->tag = 0;
160 	}
161 #endif
162 }
163 
164 static void btstack_tlv_flash_bank_iterator_init(btstack_tlv_flash_bank_t * self, tlv_iterator_t * it, int bank){
165 	memset(it, 0, sizeof(tlv_iterator_t));
166 	it->bank = bank;
167 	it->offset = BTSTACK_TLV_HEADER_LEN;
168 	btstack_tlv_flash_bank_iterator_fetch_tag_len(self, it);
169 }
170 
171 static int btstack_tlv_flash_bank_iterator_has_next(btstack_tlv_flash_bank_t * self, tlv_iterator_t * it){
172 	UNUSED(self);
173 	if (it->tag == 0xffffffff) return 0;
174 	return 1;
175 }
176 
177 static void tlv_iterator_fetch_next(btstack_tlv_flash_bank_t * self, tlv_iterator_t * it){
178 	it->offset += 8 + btstack_tlv_flash_bank_align_size(self, it->len);
179 
180 #ifdef ENABLE_TLV_FLASH_EXPLICIT_DELETE_FIELD
181 	// skip delete field
182 	it->offset += self->delete_tag_len;
183 #endif
184 
185 	if (it->offset >= self->hal_flash_bank_impl->get_size(self->hal_flash_bank_context)) {
186 		it->tag = 0xffffffff;
187 		it->len = 0;
188 		return;
189 	}
190 	btstack_tlv_flash_bank_iterator_fetch_tag_len(self, it);
191 }
192 
193 //
194 
195 // check both banks for headers and pick the one with the higher epoch % 4
196 // @returns bank or -1 if something is invalid
197 static int btstack_tlv_flash_bank_get_latest_bank(btstack_tlv_flash_bank_t * self){
198  	uint8_t header0[BTSTACK_TLV_HEADER_LEN];
199  	uint8_t header1[BTSTACK_TLV_HEADER_LEN];
200  	btstack_tlv_flash_bank_read(self, 0, 0, &header0[0], BTSTACK_TLV_HEADER_LEN);
201  	btstack_tlv_flash_bank_read(self, 1, 0, &header1[0], BTSTACK_TLV_HEADER_LEN);
202  	int valid0 = memcmp(header0, btstack_tlv_header_magic, BTSTACK_TLV_HEADER_LEN-1) == 0;
203  	int valid1 = memcmp(header1, btstack_tlv_header_magic, BTSTACK_TLV_HEADER_LEN-1) == 0;
204 	if (!valid0 && !valid1) return -1;
205 	if ( valid0 && !valid1) return 0;
206 	if (!valid0 &&  valid1) return 1;
207 	int epoch0 = header0[BTSTACK_TLV_HEADER_LEN-1] & 0x03;
208 	int epoch1 = header1[BTSTACK_TLV_HEADER_LEN-1] & 0x03;
209 	if (epoch0 == ((epoch1 + 1) & 0x03)) return 0;
210 	if (epoch1 == ((epoch0 + 1) & 0x03)) return 1;
211 	return -1;	// invalid, must not happen
212 }
213 
214 static void btstack_tlv_flash_bank_write_header(btstack_tlv_flash_bank_t * self, int bank, int epoch){
215 	uint8_t header[BTSTACK_TLV_HEADER_LEN];
216 	memcpy(&header[0], btstack_tlv_header_magic, BTSTACK_TLV_HEADER_LEN-1);
217 	header[BTSTACK_TLV_HEADER_LEN-1] = epoch;
218 	btstack_tlv_flash_bank_write(self, bank, 0, header, BTSTACK_TLV_HEADER_LEN);
219 }
220 
221 /**
222  * @brief Check if erased from offset
223  */
224 static int btstack_tlv_flash_bank_test_erased(btstack_tlv_flash_bank_t * self, int bank, uint32_t offset){
225 	log_info("test erased: bank %u, offset %u", bank, (unsigned int) offset);
226 	uint32_t size = self->hal_flash_bank_impl->get_size(self->hal_flash_bank_context);
227 	uint8_t buffer[16];
228 	uint8_t empty16[16];
229 	memset(empty16, 0xff, sizeof(empty16));
230 	while (offset < size){
231 		uint32_t copy_size = (offset + sizeof(empty16) < size) ? sizeof(empty16) : (size - offset);
232 		btstack_tlv_flash_bank_read(self, bank, offset, buffer, copy_size);
233 		if (memcmp(buffer, empty16, copy_size)) {
234 			log_info("not erased %x - %x", (unsigned int) offset, (unsigned int) (offset + copy_size));
235 			return 0;
236 		}
237 		offset += copy_size;
238 	}
239 	return 1;
240 }
241 
242 /**
243  * @brief erase bank (only if not already erased)
244  */
245 static void btstack_tlv_flash_bank_erase_bank(btstack_tlv_flash_bank_t * self, int bank){
246 	if (btstack_tlv_flash_bank_test_erased(self, bank, 0)){
247 		log_info("bank %u already erased", bank);
248 	} else {
249 		log_info("bank %u not empty, erase bank", bank);
250 		self->hal_flash_bank_impl->erase(self->hal_flash_bank_context, bank);
251 	}
252 }
253 
254 static void btstack_tlv_flash_bank_migrate(btstack_tlv_flash_bank_t * self){
255 
256 	int next_bank = 1 - self->current_bank;
257 	log_info("migrate bank %u -> bank %u", self->current_bank, next_bank);
258 	// erase bank (if needed)
259 	btstack_tlv_flash_bank_erase_bank(self, next_bank);
260 	int next_write_pos = 8;
261 
262 	tlv_iterator_t it;
263 	btstack_tlv_flash_bank_iterator_init(self, &it, self->current_bank);
264 	while (btstack_tlv_flash_bank_iterator_has_next(self, &it)){
265 		// skip deleted entries
266 		if (it.tag) {
267 			uint32_t tag_len = it.len;
268 			uint32_t tag_index = it.offset;
269 
270             bool tag_valid = true;
271 
272 #ifdef ENABLE_TLV_FLASH_WRITE_ONCE
273             // search until end for newer entry of same tag
274             tlv_iterator_t it2;
275             memcpy(&it2, &it, sizeof(tlv_iterator_t));
276             while (btstack_tlv_flash_bank_iterator_has_next(self, &it2)){
277                 if ((it2.offset != it.offset) && (it2.tag == it.tag)){
278                     tag_valid = false;
279                     break;
280                 }
281                 tlv_iterator_fetch_next(self, &it2);
282             }
283             if (tag_valid == false){
284 			    log_info("skip pos %u, tag '%x' as newer entry found at %u", (unsigned int) tag_index, (unsigned int) it.tag,
285                     (unsigned int) it2.offset);
286             }
287 #endif
288 
289             if (tag_valid) {
290 
291                 log_info("migrate pos %u, tag '%x' len %u -> new pos %u",
292                          (unsigned int) tag_index, (unsigned int) it.tag, (unsigned int) tag_len, next_write_pos);
293 
294                 // copy header
295                 uint8_t header_buffer[8];
296                 btstack_tlv_flash_bank_read(self, self->current_bank, tag_index, header_buffer, 8);
297                 btstack_tlv_flash_bank_write(self, next_bank, next_write_pos, header_buffer, 8);
298                 tag_index += 8;
299                 next_write_pos += 8;
300 
301 #ifdef ENABLE_TLV_FLASH_EXPLICIT_DELETE_FIELD
302                 // skip delete field
303                 tag_index      += self->delete_tag_len;
304                 next_write_pos += self->delete_tag_len;
305 #endif
306                 // copy value
307                 int bytes_to_copy = tag_len;
308                 uint8_t copy_buffer[32];
309                 while (bytes_to_copy) {
310                     int bytes_this_iteration = btstack_min(bytes_to_copy, sizeof(copy_buffer));
311                     btstack_tlv_flash_bank_read(self, self->current_bank, tag_index, copy_buffer, bytes_this_iteration);
312                     btstack_tlv_flash_bank_write(self, next_bank, next_write_pos, copy_buffer, bytes_this_iteration);
313                     tag_index += bytes_this_iteration;
314                     next_write_pos += bytes_this_iteration;
315                     bytes_to_copy -= bytes_this_iteration;
316                 }
317             }
318 		}
319 		tlv_iterator_fetch_next(self, &it);
320 	}
321 
322 	// prepare new one
323 	uint8_t epoch_buffer;
324 	btstack_tlv_flash_bank_read(self, self->current_bank, BTSTACK_TLV_HEADER_LEN-1, &epoch_buffer, 1);
325 	btstack_tlv_flash_bank_write_header(self, next_bank, (epoch_buffer + 1) & 3);
326 	self->current_bank = next_bank;
327 	self->write_offset = next_write_pos;
328 }
329 
330 #ifndef ENABLE_TLV_FLASH_WRITE_ONCE
331 static void btstack_tlv_flash_bank_delete_tag_until_offset(btstack_tlv_flash_bank_t * self, uint32_t tag, uint32_t offset){
332 	tlv_iterator_t it;
333 	btstack_tlv_flash_bank_iterator_init(self, &it, self->current_bank);
334 	while (btstack_tlv_flash_bank_iterator_has_next(self, &it) && it.offset < offset){
335 		if (it.tag == tag){
336 			log_info("Erase tag '%x' at position %u", (unsigned int) tag, (unsigned int) it.offset);
337 
338 			// mark entry as invalid
339 			uint32_t zero_value = 0;
340 #ifdef ENABLE_TLV_FLASH_EXPLICIT_DELETE_FIELD
341 			// write delete field at offset 8
342 			btstack_tlv_flash_bank_write(self, self->current_bank, it.offset+8, (uint8_t*) &zero_value, sizeof(zero_value));
343 #else
344 			// overwrite tag with zero value
345 			btstack_tlv_flash_bank_write(self, self->current_bank, it.offset, (uint8_t*) &zero_value, sizeof(zero_value));
346 #endif
347 
348 		}
349 		tlv_iterator_fetch_next(self, &it);
350 	}
351 }
352 #endif
353 
354 /**
355  * Get Value for Tag
356  * @param tag
357  * @param buffer
358  * @param buffer_size
359  * @returns size of value
360  */
361 static int btstack_tlv_flash_bank_get_tag(void * context, uint32_t tag, uint8_t * buffer, uint32_t buffer_size){
362 
363 	btstack_tlv_flash_bank_t * self = (btstack_tlv_flash_bank_t *) context;
364 
365 	uint32_t tag_index = 0;
366 	uint32_t tag_len   = 0;
367 	tlv_iterator_t it;
368 	btstack_tlv_flash_bank_iterator_init(self, &it, self->current_bank);
369 	while (btstack_tlv_flash_bank_iterator_has_next(self, &it)){
370 		if (it.tag == tag){
371 			log_info("Found tag '%x' at position %u", (unsigned int) tag, (unsigned int) it.offset);
372 			tag_index = it.offset;
373 			tag_len   = it.len;
374 #ifndef ENABLE_TLV_FLASH_WRITE_ONCE
375 			break;
376 #endif
377 		}
378 		tlv_iterator_fetch_next(self, &it);
379 	}
380 	if (tag_index == 0) return 0;
381 	if (!buffer) return tag_len;
382 	int copy_size = btstack_min(buffer_size, tag_len);
383 	uint32_t value_offset = tag_index + 8;
384 #ifdef ENABLE_TLV_FLASH_EXPLICIT_DELETE_FIELD
385 	// skip delete field
386 	value_offset += self->delete_tag_len;
387 #endif
388 	btstack_tlv_flash_bank_read(self, self->current_bank, value_offset, buffer, copy_size);
389 	return copy_size;
390 }
391 
392 /**
393  * Store Tag
394  * @param tag
395  * @param data
396  * @param data_size
397  */
398 static int btstack_tlv_flash_bank_store_tag(void * context, uint32_t tag, const uint8_t * data, uint32_t data_size){
399 
400 	btstack_tlv_flash_bank_t * self = (btstack_tlv_flash_bank_t *) context;
401 
402 	// trigger migration if not enough space
403 	uint32_t required_space = 8 + self->delete_tag_len + data_size;
404 	if (self->write_offset + required_space > self->hal_flash_bank_impl->get_size(self->hal_flash_bank_context)){
405 		btstack_tlv_flash_bank_migrate(self);
406 	}
407 
408 	if (self->write_offset + required_space > self->hal_flash_bank_impl->get_size(self->hal_flash_bank_context)){
409 		log_error("couldn't write entry, not enough space left");
410 		return 2;
411 	}
412 
413 	// prepare entry
414 	uint8_t entry[8];
415 	big_endian_store_32(entry, 0, tag);
416 	big_endian_store_32(entry, 4, data_size);
417 
418 	log_info("write '%" PRIx32 "', len %" PRIu32 " at %" PRIx32, tag, data_size, self->write_offset);
419 
420 	uint32_t value_offset = self->write_offset + 8;
421 #ifdef ENABLE_TLV_FLASH_EXPLICIT_DELETE_FIELD
422 	// skip delete field
423 	value_offset += self->delete_tag_len;
424 #endif
425 
426 	// write value first
427 	btstack_tlv_flash_bank_write(self, self->current_bank, value_offset, data, data_size);
428 
429 	// then entry
430 	btstack_tlv_flash_bank_write(self, self->current_bank, self->write_offset, entry, sizeof(entry));
431 
432 #ifndef ENABLE_TLV_FLASH_WRITE_ONCE
433 	// overwrite old entries (if exists)
434 	btstack_tlv_flash_bank_delete_tag_until_offset(self, tag, self->write_offset);
435 #endif
436 
437 	// done
438 	self->write_offset += sizeof(entry) + btstack_tlv_flash_bank_align_size(self, data_size);
439 
440 #ifdef ENABLE_TLV_FLASH_EXPLICIT_DELETE_FIELD
441 	// skip delete field
442 	self->write_offset += self->delete_tag_len;
443 #endif
444 
445 	return 0;
446 }
447 
448 /**
449  * Delete Tag
450  * @param tag
451  */
452 static void btstack_tlv_flash_bank_delete_tag(void * context, uint32_t tag){
453 #ifdef ENABLE_TLV_FLASH_WRITE_ONCE
454     btstack_tlv_flash_bank_store_tag(context, tag, NULL, 0);
455 #else
456     btstack_tlv_flash_bank_t * self = (btstack_tlv_flash_bank_t *) context;
457 	btstack_tlv_flash_bank_delete_tag_until_offset(self, tag, self->write_offset);
458 #endif
459 }
460 
461 static const btstack_tlv_t btstack_tlv_flash_bank = {
462 	/* int  (*get_tag)(..);     */ &btstack_tlv_flash_bank_get_tag,
463 	/* int (*store_tag)(..);    */ &btstack_tlv_flash_bank_store_tag,
464 	/* void (*delete_tag)(v..); */ &btstack_tlv_flash_bank_delete_tag,
465 };
466 
467 /**
468  * Init Tag Length Value Store
469  */
470 const btstack_tlv_t * btstack_tlv_flash_bank_init_instance(btstack_tlv_flash_bank_t * self, const hal_flash_bank_t * hal_flash_bank_impl, void * hal_flash_bank_context){
471 
472 	self->hal_flash_bank_impl    = hal_flash_bank_impl;
473 	self->hal_flash_bank_context = hal_flash_bank_context;
474 	self->delete_tag_len = 0;
475 
476 #ifdef ENABLE_TLV_FLASH_EXPLICIT_DELETE_FIELD
477 	if (hal_flash_bank_impl->get_alignment(hal_flash_bank_context) > 8){
478 		log_error("Flash alignment > 8 with ENABLE_TLV_FLASH_EXPLICIT_DELETE_FIELD not supported");
479 		return NULL;
480 	}
481 	// set delete tag len
482 	uint32_t aligment = self->hal_flash_bank_impl->get_alignment(self->hal_flash_bank_context);
483 	self->delete_tag_len = (uint8_t) btstack_max(4, aligment);
484 	log_info("delete tag len %u", self->delete_tag_len);
485 #endif
486 
487 	// try to find current bank
488 	self->current_bank = btstack_tlv_flash_bank_get_latest_bank(self);
489 	log_info("found bank %d", self->current_bank);
490 	if (self->current_bank >= 0){
491 
492 		// find last entry and write offset
493 		tlv_iterator_t it;
494 #ifndef ENABLE_TLV_FLASH_WRITE_ONCE
495 		uint32_t last_tag = 0;
496 		uint32_t last_offset = 0;
497 #endif
498         btstack_tlv_flash_bank_iterator_init(self, &it, self->current_bank);
499 		while (btstack_tlv_flash_bank_iterator_has_next(self, &it)){
500 #ifndef ENABLE_TLV_FLASH_WRITE_ONCE
501 			last_tag = it.tag;
502 			last_offset = it.offset;
503 #endif
504 			tlv_iterator_fetch_next(self, &it);
505 		}
506 		self->write_offset = it.offset;
507 
508 		if (self->write_offset <= self->hal_flash_bank_impl->get_size(self->hal_flash_bank_context)){
509 
510 #ifndef ENABLE_TLV_FLASH_WRITE_ONCE
511 			// delete older instances of last_tag
512 			// this handles the unlikely case where MCU did reset after new value + header was written but before delete did complete
513 			if (last_tag){
514 				btstack_tlv_flash_bank_delete_tag_until_offset(self, last_tag, last_offset);
515 			}
516 #endif
517 
518 			// verify that rest of bank is empty
519 			// this handles the unlikely case where MCU did reset after new value was written, but not the tag
520 			if (!btstack_tlv_flash_bank_test_erased(self, self->current_bank, self->write_offset)){
521 				log_info("Flash not empty after last found tag -> migrate");
522 				btstack_tlv_flash_bank_migrate(self);
523 			} else {
524 				log_info("Flash clean after last found tag");
525 			}
526 		} else {
527 			// failure!
528 			self->current_bank = -1;
529 		}
530 	}
531 
532 	if (self->current_bank < 0) {
533 		btstack_tlv_flash_bank_erase_bank(self, 0);
534 		self->current_bank = 0;
535 		btstack_tlv_flash_bank_write_header(self, self->current_bank, 0);	// epoch = 0;
536 		self->write_offset = 8;
537 	}
538 
539 	log_info("write offset %" PRIx32, self->write_offset);
540 	return &btstack_tlv_flash_bank;
541 }
542 
543