2 ******************************************************************************
3 * @file pios_flashfs_logfs.c
4 * @author PhoenixPilot, http://github.com/PhoenixPilot, Copyright (C) 2012
5 * @addtogroup PIOS PIOS Core hardware abstraction layer
7 * @addtogroup PIOS_FLASHFS Flash Filesystem Function
9 * @brief Log Structured Filesystem for internal or external NOR Flash
10 *****************************************************************************/
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * You should have received a copy of the GNU General Public License along
23 * with this program; if not, write to the Free Software Foundation, Inc.,
24 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 #ifdef PIOS_INCLUDE_FLASH
33 #include <openpilot.h>
34 #include <pios_math.h>
36 #include "pios_flashfs_logfs_priv.h"
39 * Filesystem state data tracked in RAM
42 enum pios_flashfs_logfs_dev_magic
{
43 PIOS_FLASHFS_LOGFS_DEV_MAGIC
= 0x94938201,
47 enum pios_flashfs_logfs_dev_magic magic
;
48 const struct flashfs_logfs_cfg
*cfg
;
50 uint8_t active_arena_id
;
52 /* NOTE: num_active_slots + num_free_slots will not typically add
53 * up to the number of slots in the arena since some of the
54 * slots will be obsolete or otherwise invalidated
56 uint16_t num_free_slots
; /* slots in free state */
57 uint16_t num_active_slots
; /* slots in active state */
59 /* Underlying flash driver glue */
60 const struct pios_flash_driver
*driver
;
65 * Internal Utility functions
69 * @brief Return the offset in flash of a particular slot within an arena
70 * @return address of the requested slot
72 static uintptr_t logfs_get_addr(const struct logfs_state
*logfs
, uint8_t arena_id
, uint16_t slot_id
)
74 PIOS_Assert(arena_id
< (logfs
->cfg
->total_fs_size
/ logfs
->cfg
->arena_size
));
75 PIOS_Assert(slot_id
< (logfs
->cfg
->arena_size
/ logfs
->cfg
->slot_size
));
77 return logfs
->cfg
->start_offset
+
78 (arena_id
* logfs
->cfg
->arena_size
) +
79 (slot_id
* logfs
->cfg
->slot_size
);
83 * The bits within these enum values must progress ONLY
84 * from 1 -> 0 so that we can write later ones on top
85 * of earlier ones in NOR flash without an erase cycle.
89 * The STM32F30X flash subsystem is only capable of
90 * writing words or halfwords. In this case we use halfwords.
91 * In addition to that it is only capable to write to erased
92 * cells (0xffff) or write a cell from anything to (0x0000).
93 * To cope with this, the F3 needs carefully crafted enum values.
94 * For this to work the underlying flash driver has to
95 * check each halfword if it has changed before writing.
97 ARENA_STATE_ERASED
= 0xFFFFFFFF,
98 ARENA_STATE_RESERVED
= 0xE6E6FFFF,
99 ARENA_STATE_ACTIVE
= 0xE6E66666,
100 ARENA_STATE_OBSOLETE
= 0x00000000,
103 struct arena_header
{
105 enum arena_state state
;
106 } __attribute__((packed
));
109 /****************************************
110 * Arena life-cycle transition functions
111 ****************************************/
114 * @brief Erases all sectors within the given arena and sets arena to erased state.
115 * @return 0 if success, < 0 on failure
116 * @note Must be called while holding the flash transaction lock
118 static int32_t logfs_erase_arena(const struct logfs_state
*logfs
, uint8_t arena_id
)
120 uintptr_t arena_addr
= logfs_get_addr(logfs
, arena_id
, 0);
122 /* Erase all of the sectors in the arena */
123 for (uint8_t sector_id
= 0;
124 sector_id
< (logfs
->cfg
->arena_size
/ logfs
->cfg
->sector_size
);
126 if (logfs
->driver
->erase_sector(logfs
->flash_id
,
127 arena_addr
+ (sector_id
* logfs
->cfg
->sector_size
))) {
132 /* Mark this arena as fully erased */
133 struct arena_header arena_hdr
= {
134 .magic
= logfs
->cfg
->fs_magic
,
135 .state
= ARENA_STATE_ERASED
,
138 if (logfs
->driver
->write_data(logfs
->flash_id
,
140 (uint8_t *)&arena_hdr
,
141 sizeof(arena_hdr
)) != 0) {
145 /* Arena is ready to be activated */
150 * @brief Marks the given arena as reserved so it can be filled.
151 * @return 0 if success, < 0 on failure
152 * @note Arena must have been previously erased before calling this
153 * @note Must be called while holding the flash transaction lock
155 static int32_t logfs_reserve_arena(const struct logfs_state
*logfs
, uint8_t arena_id
)
157 uintptr_t arena_addr
= logfs_get_addr(logfs
, arena_id
, 0);
159 /* Read in the current arena header */
160 struct arena_header arena_hdr
;
162 if (logfs
->driver
->read_data(logfs
->flash_id
,
164 (uint8_t *)&arena_hdr
,
165 sizeof(arena_hdr
)) != 0) {
168 if (arena_hdr
.state
!= ARENA_STATE_ERASED
) {
169 /* Arena was not erased, can't reserve it */
173 /* Set the arena state to reserved */
174 arena_hdr
.state
= ARENA_STATE_RESERVED
;
176 /* Write the arena header back to flash */
177 if (logfs
->driver
->write_data(logfs
->flash_id
,
179 (uint8_t *)&arena_hdr
,
180 sizeof(arena_hdr
)) != 0) {
184 /* Arena is ready to be filled */
189 * @brief Erases all arenas available to this filesystem instance
190 * @return 0 if success, < 0 on failure
191 * @note Must be called while holding the flash transaction lock
193 static int32_t logfs_erase_all_arenas(const struct logfs_state
*logfs
)
195 uint16_t num_arenas
= logfs
->cfg
->total_fs_size
/ logfs
->cfg
->arena_size
;
197 for (uint16_t arena
= 0; arena
< num_arenas
; arena
++) {
198 #ifdef PIOS_LED_HEARTBEAT
199 PIOS_LED_Toggle(PIOS_LED_HEARTBEAT
);
201 #ifdef PIOS_INCLUDE_WDG
204 if (logfs_erase_arena(logfs
, arena
) != 0) {
213 * @brief Marks the given arena as active so it can be mounted.
214 * @return 0 if success, < 0 on failure
215 * @note Arena must have been previously erased or reserved before calling this
216 * @note Must be called while holding the flash transaction lock
218 static int32_t logfs_activate_arena(const struct logfs_state
*logfs
, uint8_t arena_id
)
220 uintptr_t arena_addr
= logfs_get_addr(logfs
, arena_id
, 0);
222 /* Make sure this arena has been previously erased */
223 struct arena_header arena_hdr
;
225 if (logfs
->driver
->read_data(logfs
->flash_id
,
227 (uint8_t *)&arena_hdr
,
228 sizeof(arena_hdr
)) != 0) {
229 /* Failed to read arena header */
232 if ((arena_hdr
.state
!= ARENA_STATE_RESERVED
) &&
233 (arena_hdr
.state
!= ARENA_STATE_ERASED
)) {
234 /* Arena was not erased or reserved, can't activate it */
238 /* Mark this arena as active */
239 arena_hdr
.state
= ARENA_STATE_ACTIVE
;
240 if (logfs
->driver
->write_data(logfs
->flash_id
,
242 (uint8_t *)&arena_hdr
,
243 sizeof(arena_hdr
)) != 0) {
247 /* The arena is now activated and the log may be mounted */
252 * @brief Marks the given arena as obsolete.
253 * @return 0 if success, < 0 on failure
254 * @note Arena must have been previously active before calling this
255 * @note Must be called while holding the flash transaction lock
257 static int32_t logfs_obsolete_arena(const struct logfs_state
*logfs
, uint8_t arena_id
)
259 uintptr_t arena_addr
= logfs_get_addr(logfs
, arena_id
, 0);
261 /* We shouldn't be retiring the currently active arena */
262 PIOS_Assert(!logfs
->mounted
);
264 /* Make sure this arena was previously active */
265 struct arena_header arena_hdr
;
266 if (logfs
->driver
->read_data(logfs
->flash_id
,
268 (uint8_t *)&arena_hdr
,
269 sizeof(arena_hdr
)) != 0) {
270 /* Failed to read arena header */
274 if (arena_hdr
.state
!= ARENA_STATE_ACTIVE
) {
275 /* Arena was not previously active, can't obsolete it */
279 /* Mark this arena as obsolete */
280 arena_hdr
.state
= ARENA_STATE_OBSOLETE
;
281 if (logfs
->driver
->write_data(logfs
->flash_id
,
283 (uint8_t *)&arena_hdr
,
284 sizeof(arena_hdr
)) != 0) {
288 /* Arena is now obsoleted */
293 * @brief Find the first active arena in flash
294 * @return arena_id (>=0) of first active arena
295 * @return -1 if no active arena is found
296 * @return -2 if failed to read arena header
297 * @note Must be called while holding the flash transaction lock
299 static int32_t logfs_find_active_arena(const struct logfs_state
*logfs
)
301 /* Search for the lowest numbered active arena */
302 for (uint8_t arena_id
= 0;
303 arena_id
< logfs
->cfg
->total_fs_size
/ logfs
->cfg
->arena_size
;
305 uintptr_t arena_addr
= logfs_get_addr(logfs
, arena_id
, 0);
306 /* Load the arena header */
307 struct arena_header arena_hdr
;
308 if (logfs
->driver
->read_data(logfs
->flash_id
,
310 (uint8_t *)&arena_hdr
,
311 sizeof(arena_hdr
)) != 0) {
314 if ((arena_hdr
.state
== ARENA_STATE_ACTIVE
) &&
315 (arena_hdr
.magic
== logfs
->cfg
->fs_magic
)) {
316 /* This is the first active arena */
319 #ifdef PIOS_INCLUDE_WDG
324 /* Didn't find an active arena */
329 * The bits within these enum values must progress ONLY
330 * from 1 -> 0 so that we can write later ones on top
331 * of earlier ones in NOR flash without an erase cycle.
335 * The STM32F30X flash subsystem is only capable of
336 * writing words or halfwords. In this case we use halfwords.
337 * In addition to that it is only capable to write to erased
338 * cells (0xffff) or write a cell from anything to (0x0000).
339 * To cope with this, the F3 needs carfully crafted enum values.
340 * For this to work the underlying flash driver has to
341 * check each halfword if it has changed before writing.
343 SLOT_STATE_EMPTY
= 0xFFFFFFFF,
344 SLOT_STATE_RESERVED
= 0xFAFAFFFF,
345 SLOT_STATE_ACTIVE
= 0xFAFAAAAA,
346 SLOT_STATE_OBSOLETE
= 0x00000000,
350 enum slot_state state
;
352 uint16_t obj_inst_id
;
354 } __attribute__((packed
));
356 /* NOTE: Must be called while holding the flash transaction lock */
357 static int32_t logfs_raw_copy_bytes(const struct logfs_state
*logfs
, uintptr_t src_addr
, uint16_t src_size
, uintptr_t dst_addr
)
359 #define RAW_COPY_BLOCK_SIZE 16
360 uint8_t data_block
[RAW_COPY_BLOCK_SIZE
];
364 if (src_size
>= RAW_COPY_BLOCK_SIZE
) {
365 /* Copy a full block */
366 blk_size
= RAW_COPY_BLOCK_SIZE
;
368 /* Copy the remainder */
372 /* Read a block of data from source */
373 if (logfs
->driver
->read_data(logfs
->flash_id
,
377 /* Failed to read next chunk from source */
381 /* Write a block of data to destination */
382 if (logfs
->driver
->write_data(logfs
->flash_id
,
386 /* Failed to write chunk to destination */
390 /* Update the src/dst pointers */
391 src_size
-= blk_size
;
392 src_addr
+= blk_size
;
393 dst_addr
+= blk_size
;
400 * Is the entire filesystem full?
401 * true = all slots in the arena are in the ACTIVE state (ie. garbage collection won't free anything)
402 * false = some slots in the arena are either currently free or could be free'd by garbage collection
404 static bool logfs_fs_is_full(const struct logfs_state
*logfs
)
406 return logfs
->num_active_slots
== (logfs
->cfg
->arena_size
/ logfs
->cfg
->slot_size
) - 1;
411 * true = there are no unwritten slots left in the log (garbage collection may or may not help)
412 * false = there are still some entirely unused slots left in the log
414 static bool logfs_log_is_full(const struct logfs_state
*logfs
)
416 return logfs
->num_free_slots
== 0;
419 static int32_t logfs_unmount_log(struct logfs_state
*logfs
)
421 PIOS_Assert(logfs
->mounted
);
423 logfs
->num_active_slots
= 0;
424 logfs
->num_free_slots
= 0;
425 logfs
->mounted
= false;
430 static int32_t logfs_mount_log(struct logfs_state
*logfs
, uint8_t arena_id
)
432 PIOS_Assert(!logfs
->mounted
);
434 logfs
->num_active_slots
= 0;
435 logfs
->num_free_slots
= 0;
436 logfs
->active_arena_id
= arena_id
;
438 /* Scan the log to find out how full it is */
439 for (uint16_t slot_id
= 1;
440 slot_id
< (logfs
->cfg
->arena_size
/ logfs
->cfg
->slot_size
);
442 struct slot_header slot_hdr
;
443 uintptr_t slot_addr
= logfs_get_addr(logfs
, logfs
->active_arena_id
, slot_id
);
444 if (logfs
->driver
->read_data(logfs
->flash_id
,
446 (uint8_t *)&slot_hdr
,
447 sizeof(slot_hdr
)) != 0) {
452 * Empty slots must be in a continguous block at the
455 PIOS_Assert(slot_hdr
.state
== SLOT_STATE_EMPTY
||
456 logfs
->num_free_slots
== 0);
458 switch (slot_hdr
.state
) {
459 case SLOT_STATE_EMPTY
:
460 logfs
->num_free_slots
++;
462 case SLOT_STATE_ACTIVE
:
463 logfs
->num_active_slots
++;
465 case SLOT_STATE_RESERVED
:
466 case SLOT_STATE_OBSOLETE
:
471 /* Scan is complete, mark the arena mounted */
472 logfs
->active_arena_id
= arena_id
;
473 logfs
->mounted
= true;
478 static bool PIOS_FLASHFS_Logfs_validate(const struct logfs_state
*logfs
)
480 return logfs
&& (logfs
->magic
== PIOS_FLASHFS_LOGFS_DEV_MAGIC
);
483 #if defined(PIOS_INCLUDE_FREERTOS)
484 static struct logfs_state
*PIOS_FLASHFS_Logfs_alloc(void)
486 struct logfs_state
*logfs
;
488 logfs
= (struct logfs_state
*)pios_malloc(sizeof(*logfs
));
493 logfs
->magic
= PIOS_FLASHFS_LOGFS_DEV_MAGIC
;
496 static void PIOS_FLASHFS_Logfs_free(struct logfs_state
*logfs
)
498 /* Invalidate the magic */
499 logfs
->magic
= ~PIOS_FLASHFS_LOGFS_DEV_MAGIC
;
503 static struct logfs_state pios_flashfs_logfs_devs
[PIOS_FLASHFS_LOGFS_MAX_DEVS
];
504 static uint8_t pios_flashfs_logfs_num_devs
;
505 static struct logfs_state
*PIOS_FLASHFS_Logfs_alloc(void)
507 struct logfs_state
*logfs
;
509 if (pios_flashfs_logfs_num_devs
>= PIOS_FLASHFS_LOGFS_MAX_DEVS
) {
513 logfs
= &pios_flashfs_logfs_devs
[pios_flashfs_logfs_num_devs
++];
514 logfs
->magic
= PIOS_FLASHFS_LOGFS_DEV_MAGIC
;
518 static void PIOS_FLASHFS_Logfs_free(struct logfs_state
*logfs
)
520 /* Invalidate the magic */
521 logfs
->magic
= ~PIOS_FLASHFS_LOGFS_DEV_MAGIC
;
523 /* Can't free the resources with this simple allocator */
525 #endif /* if defined(PIOS_INCLUDE_FREERTOS) */
528 * @brief Initialize the flash object setting FS
529 * @return 0 if success, -1 if failure
531 int32_t PIOS_FLASHFS_Logfs_Init(uintptr_t *fs_id
, const struct flashfs_logfs_cfg
*cfg
, const struct pios_flash_driver
*driver
, uintptr_t flash_id
)
537 /* We must have at least 2 arenas for garbage collection to work */
538 PIOS_Assert((cfg
->total_fs_size
/ cfg
->arena_size
> 1));
540 /* Make sure the underlying flash driver provides the minimal set of required methods */
541 PIOS_Assert(driver
->start_transaction
);
542 PIOS_Assert(driver
->end_transaction
);
543 PIOS_Assert(driver
->erase_sector
);
544 PIOS_Assert(driver
->write_data
);
545 PIOS_Assert(driver
->read_data
);
549 struct logfs_state
*logfs
;
551 logfs
= (struct logfs_state
*)PIOS_FLASHFS_Logfs_alloc();
557 /* Bind configuration parameters to this filesystem instance */
558 logfs
->cfg
= cfg
; /* filesystem configuration */
559 logfs
->driver
= driver
; /* lower-level flash driver */
560 logfs
->flash_id
= flash_id
; /* lower-level flash device id */
561 logfs
->mounted
= false;
563 if (logfs
->driver
->start_transaction(logfs
->flash_id
) != 0) {
570 for (uint8_t try = 0; !found
&& try < 2; try++) {
571 /* Find the active arena */
572 arena_id
= logfs_find_active_arena(logfs
);
574 /* Found the active arena */
578 /* No active arena found, erase and activate arena 0 */
579 if (logfs_erase_arena(logfs
, 0) != 0) {
582 if (logfs_activate_arena(logfs
, 0) != 0) {
589 /* Still no active arena, something is broken */
594 /* We've found an active arena, mount it */
595 if (logfs_mount_log(logfs
, arena_id
) != 0) {
596 /* Failed to mount the log, something is broken */
601 /* Log has been mounted */
604 *fs_id
= (uintptr_t)logfs
;
607 logfs
->driver
->end_transaction(logfs
->flash_id
);
613 int32_t PIOS_FLASHFS_Logfs_Destroy(uintptr_t fs_id
)
617 struct logfs_state
*logfs
= (struct logfs_state
*)fs_id
;
619 if (!PIOS_FLASHFS_Logfs_validate(logfs
)) {
624 PIOS_FLASHFS_Logfs_free(logfs
);
631 /* NOTE: Must be called while holding the flash transaction lock */
632 static int32_t logfs_garbage_collect(struct logfs_state
*logfs
)
634 PIOS_Assert(logfs
->mounted
);
636 /* Source arena is the active arena */
637 uint8_t src_arena_id
= logfs
->active_arena_id
;
639 /* Compute destination arena */
640 uint8_t dst_arena_id
= (logfs
->active_arena_id
+ 1) % (logfs
->cfg
->total_fs_size
/ logfs
->cfg
->arena_size
);
642 /* Erase destination arena */
643 if (logfs_erase_arena(logfs
, dst_arena_id
) != 0) {
647 /* Reserve the destination arena so we can start filling it */
648 if (logfs_reserve_arena(logfs
, dst_arena_id
) != 0) {
649 /* Unable to reserve the arena */
653 /* Copy active slots from active arena to destination arena */
654 uint16_t dst_slot_id
= 1;
655 for (uint16_t src_slot_id
= 1;
656 src_slot_id
< (logfs
->cfg
->arena_size
/ logfs
->cfg
->slot_size
);
658 struct slot_header slot_hdr
;
659 uintptr_t src_addr
= logfs_get_addr(logfs
, src_arena_id
, src_slot_id
);
660 if (logfs
->driver
->read_data(logfs
->flash_id
,
662 (uint8_t *)&slot_hdr
,
663 sizeof(slot_hdr
)) != 0) {
667 if (slot_hdr
.state
== SLOT_STATE_ACTIVE
) {
668 uintptr_t dst_addr
= logfs_get_addr(logfs
, dst_arena_id
, dst_slot_id
);
669 if (logfs_raw_copy_bytes(logfs
,
671 sizeof(slot_hdr
) + slot_hdr
.obj_size
,
673 /* Failed to copy all bytes */
678 #ifdef PIOS_INCLUDE_WDG
683 /* Activate the destination arena */
684 if (logfs_activate_arena(logfs
, dst_arena_id
) != 0) {
688 /* Unmount the source arena */
689 if (logfs_unmount_log(logfs
) != 0) {
693 /* Obsolete the source arena */
694 if (logfs_obsolete_arena(logfs
, src_arena_id
) != 0) {
698 /* Mount the new arena */
699 if (logfs_mount_log(logfs
, dst_arena_id
) != 0) {
706 /* NOTE: Must be called while holding the flash transaction lock */
707 static int16_t logfs_object_find_next(const struct logfs_state
*logfs
, struct slot_header
*slot_hdr
, uint16_t *curr_slot
, uint32_t obj_id
, uint16_t obj_inst_id
)
709 PIOS_Assert(slot_hdr
);
710 PIOS_Assert(curr_slot
);
712 /* First slot in the arena is reserved for arena header, skip it. */
713 if (*curr_slot
== 0) {
717 for (uint16_t slot_id
= *curr_slot
;
718 slot_id
< (logfs
->cfg
->arena_size
/ logfs
->cfg
->slot_size
);
720 uintptr_t slot_addr
= logfs_get_addr(logfs
, logfs
->active_arena_id
, slot_id
);
722 if (logfs
->driver
->read_data(logfs
->flash_id
,
725 sizeof(*slot_hdr
)) != 0) {
728 if (slot_hdr
->state
== SLOT_STATE_EMPTY
) {
729 /* We hit the end of the log */
732 if (slot_hdr
->state
== SLOT_STATE_ACTIVE
&&
733 slot_hdr
->obj_id
== obj_id
&&
734 slot_hdr
->obj_inst_id
== obj_inst_id
) {
735 /* Found what we were looking for */
736 *curr_slot
= slot_id
;
739 #ifdef PIOS_INCLUDE_WDG
744 /* No matching entry was found */
748 /* NOTE: Must be called while holding the flash transaction lock */
749 /* OPTIMIZE: could trust that there is at most one active version of every object and terminate the search when we find one */
750 static int8_t logfs_delete_object(struct logfs_state
*logfs
, uint32_t obj_id
, uint16_t obj_inst_id
)
755 uint16_t curr_slot_id
= 0;
758 struct slot_header slot_hdr
;
759 switch (logfs_object_find_next(logfs
, &slot_hdr
, &curr_slot_id
, obj_id
, obj_inst_id
)) {
761 /* Found a matching slot. Obsolete it. */
762 slot_hdr
.state
= SLOT_STATE_OBSOLETE
;
763 uintptr_t slot_addr
= logfs_get_addr(logfs
, logfs
->active_arena_id
, curr_slot_id
);
765 if (logfs
->driver
->write_data(logfs
->flash_id
,
767 (uint8_t *)&slot_hdr
,
768 sizeof(slot_hdr
)) != 0) {
772 /* Object has been successfully obsoleted and is no longer active */
773 logfs
->num_active_slots
--;
776 /* Search completed, object not found */
781 /* Error occurred during search */
791 /* NOTE: Must be called while holding the flash transaction lock */
792 static int8_t logfs_reserve_free_slot(struct logfs_state
*logfs
, uint16_t *slot_id
, struct slot_header
*slot_hdr
, uint32_t obj_id
, uint16_t obj_inst_id
, uint16_t obj_size
)
794 PIOS_Assert(slot_id
);
795 PIOS_Assert(slot_hdr
);
797 if (logfs
->num_free_slots
< 1) {
798 /* No free slots to allocate */
802 if (obj_size
> (logfs
->cfg
->slot_size
- sizeof(slot_hdr
))) {
803 /* This object is too big for the slot */
807 uint16_t candidate_slot_id
= (logfs
->cfg
->arena_size
/ logfs
->cfg
->slot_size
) - logfs
->num_free_slots
;
808 PIOS_Assert(candidate_slot_id
> 0);
810 uintptr_t slot_addr
= logfs_get_addr(logfs
, logfs
->active_arena_id
, candidate_slot_id
);
812 if (logfs
->driver
->read_data(logfs
->flash_id
,
815 sizeof(*slot_hdr
)) != 0) {
816 /* Failed to read slot header for candidate slot */
820 if (slot_hdr
->state
!= SLOT_STATE_EMPTY
) {
821 /* Candidate slot isn't empty! Something is broken. */
822 PIOS_DEBUG_Assert(0);
826 /* Mark this slot as RESERVED */
827 slot_hdr
->state
= SLOT_STATE_RESERVED
;
828 slot_hdr
->obj_id
= obj_id
;
829 slot_hdr
->obj_inst_id
= obj_inst_id
;
830 slot_hdr
->obj_size
= obj_size
;
832 if (logfs
->driver
->write_data(logfs
->flash_id
,
835 sizeof(*slot_hdr
)) != 0) {
836 /* Failed to write the slot header */
840 /* FIXME: If the header write (above) failed, may have partially written data, thus corrupting that slot but we would have missed decrementing this counter */
841 logfs
->num_free_slots
--;
843 *slot_id
= candidate_slot_id
;
847 /* NOTE: Must be called while holding the flash transaction lock */
848 static int8_t logfs_append_to_log(struct logfs_state
*logfs
, uint32_t obj_id
, uint16_t obj_inst_id
, uint8_t *obj_data
, uint16_t obj_size
)
850 /* Reserve a free slot for our new object */
851 uint16_t free_slot_id
;
852 struct slot_header slot_hdr
;
854 if (logfs_reserve_free_slot(logfs
, &free_slot_id
, &slot_hdr
, obj_id
, obj_inst_id
, obj_size
) != 0) {
855 /* Failed to reserve a free slot */
859 /* Compute slot address */
860 uintptr_t slot_addr
= logfs_get_addr(logfs
, logfs
->active_arena_id
, free_slot_id
);
862 /* Write the data into the reserved slot, starting after the slot header */
863 uintptr_t slot_offset
= sizeof(slot_hdr
);
864 while (obj_size
> 0) {
865 /* Individual writes must fit entirely within a single page buffer. */
866 uint16_t page_remaining
= logfs
->cfg
->page_size
- (slot_offset
% logfs
->cfg
->page_size
);
867 uint16_t write_size
= MIN(obj_size
, page_remaining
);
868 if (logfs
->driver
->write_data(logfs
->flash_id
,
869 slot_addr
+ slot_offset
,
872 /* Failed to write the object data to the slot */
876 /* Update our accounting */
877 obj_data
+= write_size
;
878 slot_offset
+= write_size
;
879 obj_size
-= write_size
;
882 /* Mark this slot active in one atomic step */
883 slot_hdr
.state
= SLOT_STATE_ACTIVE
;
884 if (logfs
->driver
->write_data(logfs
->flash_id
,
886 (uint8_t *)&slot_hdr
,
887 sizeof(slot_hdr
)) != 0) {
888 /* Failed to mark the slot active */
892 /* Object has been successfully written to the slot */
893 logfs
->num_active_slots
++;
898 /**********************************
900 * Provide a PIOS_FLASHFS_* driver
902 *********************************/
903 #include "pios_flashfs.h" /* API for flash filesystem */
906 * @brief Saves one object instance to the filesystem
907 * @param[in] fs_id The filesystem to use for this action
908 * @param[in] obj UAVObject ID of the object to save
909 * @param[in] obj_inst_id The instance number of the object being saved
910 * @param[in] obj_data Contents of the object being saved
911 * @param[in] obj_size Size of the object being saved
912 * @return 0 if success or error code
913 * @retval -1 if fs_id is not a valid filesystem instance
914 * @retval -2 if failed to start transaction
915 * @retval -3 if failure to delete any previous versions of the object
916 * @retval -4 if filesystem is entirely full and garbage collection won't help
917 * @retval -5 if garbage collection failed
918 * @retval -6 if filesystem is full even after garbage collection should have freed space
919 * @retval -7 if writing the new object to the filesystem failed
921 int32_t PIOS_FLASHFS_ObjSave(uintptr_t fs_id
, uint32_t obj_id
, uint16_t obj_inst_id
, uint8_t *obj_data
, uint16_t obj_size
)
925 struct logfs_state
*logfs
= (struct logfs_state
*)fs_id
;
927 if (!PIOS_FLASHFS_Logfs_validate(logfs
)) {
932 PIOS_Assert(obj_size
<= (logfs
->cfg
->slot_size
- sizeof(struct slot_header
)));
934 if (logfs
->driver
->start_transaction(logfs
->flash_id
) != 0) {
939 if (logfs_delete_object(logfs
, obj_id
, obj_inst_id
) != 0) {
945 * All old versions of this object + instance have been invalidated.
946 * Write the new object.
949 /* Check if the arena is entirely full. */
950 if (logfs_fs_is_full(logfs
)) {
951 /* Note: Filesystem Full means we're full of *active* records so gc won't help at all. */
956 /* Is garbage collection required? */
957 if (logfs_log_is_full(logfs
)) {
958 /* Note: Log Full means the log is full but may contain obsolete slots so gc may free some space */
959 if (logfs_garbage_collect(logfs
) != 0) {
963 /* Check one more time just to be sure we actually free'd some space */
964 if (logfs_log_is_full(logfs
)) {
966 * Log is still full even after gc!
967 * NOTE: This should not happen since the filesystem wasn't full
968 * when we checked above so gc should have helped.
970 PIOS_DEBUG_Assert(0);
976 /* We have room for our new object. Append it to the log. */
977 if (logfs_append_to_log(logfs
, obj_id
, obj_inst_id
, obj_data
, obj_size
) != 0) {
978 /* Error during append */
983 /* Object successfully written to the log */
987 logfs
->driver
->end_transaction(logfs
->flash_id
);
994 * @brief Load one object instance from the filesystem
995 * @param[in] fs_id The filesystem to use for this action
996 * @param[in] obj UAVObject ID of the object to load
997 * @param[in] obj_inst_id The instance of the object to load
998 * @param[in] obj_data Buffer to hold the contents of the loaded object
999 * @param[in] obj_size Size of the object to be loaded
1000 * @return 0 if success or error code
1001 * @retval -1 if fs_id is not a valid filesystem instance
1002 * @retval -2 if failed to start transaction
1003 * @retval -3 if object not found in filesystem
1004 * @retval -4 if object size in filesystem does not exactly match buffer size
1005 * @retval -5 if reading the object data from flash fails
1007 int32_t PIOS_FLASHFS_ObjLoad(uintptr_t fs_id
, uint32_t obj_id
, uint16_t obj_inst_id
, uint8_t *obj_data
, uint16_t obj_size
)
1011 struct logfs_state
*logfs
= (struct logfs_state
*)fs_id
;
1013 if (!PIOS_FLASHFS_Logfs_validate(logfs
)) {
1018 PIOS_Assert(obj_size
<= (logfs
->cfg
->slot_size
- sizeof(struct slot_header
)));
1020 if (logfs
->driver
->start_transaction(logfs
->flash_id
) != 0) {
1025 /* Find the object in the log */
1026 uint16_t slot_id
= 0;
1027 struct slot_header slot_hdr
;
1028 if (logfs_object_find_next(logfs
, &slot_hdr
, &slot_id
, obj_id
, obj_inst_id
) != 0) {
1029 /* Object does not exist in fs */
1034 /* Sanity check what we've found */
1035 if (slot_hdr
.obj_size
!= obj_size
) {
1036 /* Object sizes don't match. Not safe to copy contents. */
1041 /* Read the contents of the object from the log */
1043 uintptr_t slot_addr
= logfs_get_addr(logfs
, logfs
->active_arena_id
, slot_id
);
1044 if (logfs
->driver
->read_data(logfs
->flash_id
,
1045 slot_addr
+ sizeof(slot_hdr
),
1046 (uint8_t *)obj_data
,
1048 /* Failed to read object data from the log */
1054 /* Object successfully loaded */
1058 logfs
->driver
->end_transaction(logfs
->flash_id
);
1065 * @brief Delete one instance of an object from the filesystem
1066 * @param[in] fs_id The filesystem to use for this action
1067 * @param[in] obj UAVObject ID of the object to delete
1068 * @param[in] obj_inst_id The instance of the object to delete
1069 * @return 0 if success or error code
1070 * @retval -1 if fs_id is not a valid filesystem instance
1071 * @retval -2 if failed to start transaction
1072 * @retval -3 if failed to delete the object from the filesystem
1074 int32_t PIOS_FLASHFS_ObjDelete(uintptr_t fs_id
, uint32_t obj_id
, uint16_t obj_inst_id
)
1078 struct logfs_state
*logfs
= (struct logfs_state
*)fs_id
;
1080 if (!PIOS_FLASHFS_Logfs_validate(logfs
)) {
1085 if (logfs
->driver
->start_transaction(logfs
->flash_id
) != 0) {
1090 if (logfs_delete_object(logfs
, obj_id
, obj_inst_id
) != 0) {
1095 /* Object successfully deleted from the log */
1099 logfs
->driver
->end_transaction(logfs
->flash_id
);
1106 * @brief Erases all filesystem arenas and activate the first arena
1107 * @param[in] fs_id The filesystem to use for this action
1108 * @return 0 if success or error code
1109 * @retval -1 if fs_id is not a valid filesystem instance
1110 * @retval -2 if failed to start transaction
1111 * @retval -3 if failed to erase all arenas
1112 * @retval -4 if failed to activate arena 0
1113 * @retval -5 if failed to mount arena 0
1115 int32_t PIOS_FLASHFS_Format(uintptr_t fs_id
)
1119 struct logfs_state
*logfs
= (struct logfs_state
*)fs_id
;
1121 if (!PIOS_FLASHFS_Logfs_validate(logfs
)) {
1126 if (logfs
->mounted
) {
1127 logfs_unmount_log(logfs
);
1130 if (logfs
->driver
->start_transaction(logfs
->flash_id
) != 0) {
1135 if (logfs_erase_all_arenas(logfs
) != 0) {
1140 /* Reinitialize arena 0 */
1141 if (logfs_activate_arena(logfs
, 0) != 0) {
1147 if (logfs_mount_log(logfs
, 0) != 0) {
1152 /* Chip erased and log remounted successfully */
1156 logfs
->driver
->end_transaction(logfs
->flash_id
);
1162 * @brief Returs stats for the filesystems
1163 * @param[in] fs_id The filesystem to use for this action
1164 * @return 0 if success or error code
1165 * @retval -1 if fs_id is not a valid filesystem instance
1167 int32_t PIOS_FLASHFS_GetStats(uintptr_t fs_id
, struct PIOS_FLASHFS_Stats
*stats
)
1170 struct logfs_state
*logfs
= (struct logfs_state
*)fs_id
;
1172 if (!PIOS_FLASHFS_Logfs_validate(logfs
)) {
1175 stats
->num_active_slots
= logfs
->num_active_slots
;
1176 stats
->num_free_slots
= logfs
->num_free_slots
;
1179 #endif /* PIOS_INCLUDE_FLASH */