From b7f1289847a4257fe92007905dd83d338b867238 Mon Sep 17 00:00:00 2001 From: Andrzej Puzdrowski Date: Wed, 13 Dec 2017 15:53:20 +0100 Subject: [PATCH] subsys: fs: Add Flash Circular Buffer Zephyr already supports NFFS as a storage layer, but it might be a little bit too heavyweight for certain applications in memory-restricted ICs. This module is response for need of Lightweight flash storage capability. FCB is ported form MyNewt as native zephyr module. Signed-off-by: Andrzej Puzdrowski --- include/fcb.h | 134 ++++++++++++++++++ subsys/CMakeLists.txt | 2 +- subsys/fs/CMakeLists.txt | 20 +-- subsys/fs/Kconfig | 2 + subsys/fs/fcb/CMakeLists.txt | 8 ++ subsys/fs/fcb/Kconfig | 18 +++ subsys/fs/fcb/fcb.c | 252 ++++++++++++++++++++++++++++++++++ subsys/fs/fcb/fcb_append.c | 133 ++++++++++++++++++ subsys/fs/fcb/fcb_elem_info.c | 88 ++++++++++++ subsys/fs/fcb/fcb_getnext.c | 121 ++++++++++++++++ subsys/fs/fcb/fcb_priv.h | 53 +++++++ subsys/fs/fcb/fcb_rotate.c | 45 ++++++ subsys/fs/fcb/fcb_walk.c | 41 ++++++ 13 files changed, 908 insertions(+), 9 deletions(-) create mode 100644 include/fcb.h create mode 100644 subsys/fs/fcb/CMakeLists.txt create mode 100644 subsys/fs/fcb/Kconfig create mode 100644 subsys/fs/fcb/fcb.c create mode 100644 subsys/fs/fcb/fcb_append.c create mode 100644 subsys/fs/fcb/fcb_elem_info.c create mode 100644 subsys/fs/fcb/fcb_getnext.c create mode 100644 subsys/fs/fcb/fcb_priv.h create mode 100644 subsys/fs/fcb/fcb_rotate.c create mode 100644 subsys/fs/fcb/fcb_walk.c diff --git a/include/fcb.h b/include/fcb.h new file mode 100644 index 00000000000..138b3d92e2c --- /dev/null +++ b/include/fcb.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __FCB_H_ +#define __FCB_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Flash circular buffer. + */ +#include +#include + +#include "flash_map.h" + +#include + +#define FCB_MAX_LEN (CHAR_MAX | CHAR_MAX << 7) /* Max length of element */ + +/* + * Entry location is pointer to area (within fcb->f_sectors), and offset + * within that area. + */ +struct fcb_entry { + struct flash_area *fe_area; /* ptr to area within fcb->f_sectors */ + u32_t fe_elem_off; /* start of entry */ + u32_t fe_data_off; /* start of data */ + u16_t fe_data_len; /* size of data area */ +}; + +struct fcb { + /* Caller of fcb_init fills this in */ + u32_t f_magic; /* As placed on the disk */ + u8_t f_version; /* Current version number of the data */ + u8_t f_sector_cnt; /* Number of elements in sector array */ + u8_t f_scratch_cnt; /* How many sectors should be kept empty */ + struct flash_area *f_sectors; /* Array of sectors, must be contiguous */ + + /* Flash circular buffer internal state */ + struct k_mutex f_mtx; /* Locking for accessing the FCB data */ + struct flash_area *f_oldest; + struct fcb_entry f_active; + u16_t f_active_id; + u8_t f_align; /* writes to flash have to aligned to this */ +}; + +/* + * Error codes. + */ +#define FCB_OK 0 +#define FCB_ERR_ARGS -1 +#define FCB_ERR_FLASH -2 +#define FCB_ERR_NOVAR -3 +#define FCB_ERR_NOSPACE -4 +#define FCB_ERR_NOMEM -5 +#define FCB_ERR_CRC -6 +#define FCB_ERR_MAGIC -7 + +int fcb_init(struct fcb *fcb); + +/* + * fcb_log is needed as the number of entries in a log + */ +struct fcb_log { + struct fcb fl_fcb; + u8_t fl_entries; +}; + +int log_fcb_init(struct fcb_log *fcblog, struct fcb *fcb, u16_t entries); + +/* + * fcb_append() appends an entry to circular buffer. When writing the + * contents for the entry, use loc->fl_area and loc->fl_data_off with + * flash_area_write(). When you're finished, call fcb_append_finish() with + * loc as argument. + */ +int fcb_append(struct fcb *fcb, u16_t len, struct fcb_entry *loc); +int fcb_append_finish(struct fcb *fcb, struct fcb_entry *append_loc); + +/* + * Walk over all log entries in FCB, or entries in a given flash_area. + * cb gets called for every entry. If cb wants to stop the walk, it should + * return non-zero value. + * + * Entry data can be read using flash_area_read(), using + * loc->fe_area, loc->fe_data_off, and loc->fe_data_len as arguments. + */ +typedef int (*fcb_walk_cb)(struct fcb_entry *loc, void *arg); +int fcb_walk(struct fcb *fcb, struct flash_area *fa, fcb_walk_cb cb, + void *cb_arg); +int fcb_getnext(struct fcb *fcb, struct fcb_entry *loc); + +/* + * Erases the data from oldest sector. + */ +int fcb_rotate(struct fcb *fcb); + +/* + * Start using the scratch block. + */ +int fcb_append_to_scratch(struct fcb *fcb); + +/* + * How many sectors are unused. + */ +int fcb_free_sector_cnt(struct fcb *fcb); + +/* + * Whether FCB has any data. + */ +int fcb_is_empty(struct fcb *fcb); + +/* + * Element at offset *entries* from last position (backwards). + */ +int fcb_offset_last_n(struct fcb *fcb, u8_t entries, + struct fcb_entry *last_n_entry); + +/* + * Clears FCB passed to it + */ +int fcb_clear(struct fcb *fcb); + +#ifdef __cplusplus +} +#endif + +#endif /* __FCB_H_ */ diff --git a/subsys/CMakeLists.txt b/subsys/CMakeLists.txt index f04039e3faf..f80221a6f6c 100644 --- a/subsys/CMakeLists.txt +++ b/subsys/CMakeLists.txt @@ -5,7 +5,7 @@ add_subdirectory_ifdef(CONFIG_CONSOLE_PULL console) add_subdirectory_ifdef(CONFIG_CONSOLE_SHELL shell) add_subdirectory_ifdef(CONFIG_CPLUSPLUS cpp) add_subdirectory_ifdef(CONFIG_DISK_ACCESS disk) -add_subdirectory_ifdef(CONFIG_FILE_SYSTEM fs) +add_subdirectory(fs) add_subdirectory_ifdef(CONFIG_MCUBOOT_IMG_MANAGER dfu) add_subdirectory_ifdef(CONFIG_NET_BUF net) add_subdirectory_ifdef(CONFIG_USB usb) diff --git a/subsys/fs/CMakeLists.txt b/subsys/fs/CMakeLists.txt index cb2d299c38f..5f2be846ac8 100644 --- a/subsys/fs/CMakeLists.txt +++ b/subsys/fs/CMakeLists.txt @@ -1,10 +1,14 @@ -zephyr_link_interface_ifdef(CONFIG_FAT_FILESYSTEM_ELM ELMFAT) -zephyr_link_interface_ifdef(CONFIG_FILE_SYSTEM_NFFS NFFS) +if(CONFIG_FILE_SYSTEM) + zephyr_link_interface_ifdef(CONFIG_FAT_FILESYSTEM_ELM ELMFAT) + zephyr_link_interface_ifdef(CONFIG_FILE_SYSTEM_NFFS NFFS) -zephyr_library() -zephyr_library_sources_ifdef(CONFIG_FAT_FILESYSTEM_ELM fat_fs.c) -zephyr_library_sources_ifdef(CONFIG_FILE_SYSTEM_NFFS nffs_fs.c) -zephyr_library_sources_ifdef(CONFIG_FILE_SYSTEM_SHELL shell.c) + zephyr_library() + zephyr_library_sources_ifdef(CONFIG_FAT_FILESYSTEM_ELM fat_fs.c) + zephyr_library_sources_ifdef(CONFIG_FILE_SYSTEM_NFFS nffs_fs.c) + zephyr_library_sources_ifdef(CONFIG_FILE_SYSTEM_SHELL shell.c) -zephyr_library_link_libraries_ifdef(CONFIG_FAT_FILESYSTEM_ELM ELMFAT) -zephyr_library_link_libraries_ifdef(CONFIG_FILE_SYSTEM_NFFS NFFS) + zephyr_library_link_libraries_ifdef(CONFIG_FAT_FILESYSTEM_ELM ELMFAT) + zephyr_library_link_libraries_ifdef(CONFIG_FILE_SYSTEM_NFFS NFFS) +endif() + +add_subdirectory_ifdef(CONFIG_FCB ./fcb) diff --git a/subsys/fs/Kconfig b/subsys/fs/Kconfig index b6d8526ed77..9c56364e90b 100644 --- a/subsys/fs/Kconfig +++ b/subsys/fs/Kconfig @@ -90,4 +90,6 @@ endmenu endif # FILE_SYSTEM +source "subsys/fs/fcb/Kconfig" + endmenu diff --git a/subsys/fs/fcb/CMakeLists.txt b/subsys/fs/fcb/CMakeLists.txt new file mode 100644 index 00000000000..432d99c1ac8 --- /dev/null +++ b/subsys/fs/fcb/CMakeLists.txt @@ -0,0 +1,8 @@ +zephyr_sources( + fcb_append.c + fcb.c + fcb_elem_info.c + fcb_getnext.c + fcb_rotate.c + fcb_walk.c + ) diff --git a/subsys/fs/fcb/Kconfig b/subsys/fs/fcb/Kconfig new file mode 100644 index 00000000000..089d710aac9 --- /dev/null +++ b/subsys/fs/fcb/Kconfig @@ -0,0 +1,18 @@ +# Kconfig - Flash Circular Buffer module +# +# Copyright (c) 2017 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +# +# Flash Circular Buffer +# + +config FCB + bool + prompt "Flash Circular Buffer support" + default n + depends on FLASH_MAP + help + Enable support of Flash Circular Buffer. diff --git a/subsys/fs/fcb/fcb.c b/subsys/fs/fcb/fcb.c new file mode 100644 index 00000000000..01bb0387866 --- /dev/null +++ b/subsys/fs/fcb/fcb.c @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "fcb.h" +#include "fcb_priv.h" +#include "string.h" + +int +fcb_init(struct fcb *fcb) +{ + struct flash_area *fap; + int rc; + int i; + int max_align = 1; + int align; + int oldest = -1, newest = -1; + struct flash_area *oldest_fap = NULL, *newest_fap = NULL; + struct fcb_disk_area fda; + + if (!fcb->f_sectors || fcb->f_sector_cnt - fcb->f_scratch_cnt < 1) { + return FCB_ERR_ARGS; + } + + /* Fill last used, first used */ + for (i = 0; i < fcb->f_sector_cnt; i++) { + fap = &fcb->f_sectors[i]; + align = flash_area_align(fap); + if (align > max_align) { + max_align = flash_area_align(fap); + } + rc = fcb_sector_hdr_read(fcb, fap, &fda); + if (rc < 0) { + return rc; + } + if (rc == 0) { + continue; + } + if (oldest < 0) { + oldest = newest = fda.fd_id; + oldest_fap = newest_fap = fap; + continue; + } + if (FCB_ID_GT(fda.fd_id, newest)) { + newest = fda.fd_id; + newest_fap = fap; + } else if (FCB_ID_GT(oldest, fda.fd_id)) { + oldest = fda.fd_id; + oldest_fap = fap; + } + } + if (oldest < 0) { + /* + * No initialized areas. + */ + oldest_fap = newest_fap = &fcb->f_sectors[0]; + rc = fcb_sector_hdr_init(fcb, oldest_fap, 0); + if (rc) { + return rc; + } + newest = oldest = 0; + } + fcb->f_align = max_align; + fcb->f_oldest = oldest_fap; + fcb->f_active.fe_area = newest_fap; + fcb->f_active.fe_elem_off = sizeof(struct fcb_disk_area); + fcb->f_active_id = newest; + + while (1) { + rc = fcb_getnext_in_area(fcb, &fcb->f_active); + if (rc == FCB_ERR_NOVAR) { + rc = FCB_OK; + break; + } + if (rc != 0) { + break; + } + } + k_mutex_init(&fcb->f_mtx); + return rc; +} + +int +fcb_free_sector_cnt(struct fcb *fcb) +{ + int i; + struct flash_area *fa; + + fa = fcb->f_active.fe_area; + for (i = 0; i < fcb->f_sector_cnt; i++) { + fa = fcb_getnext_area(fcb, fa); + if (fa == fcb->f_oldest) { + break; + } + } + return i; +} + +int +fcb_is_empty(struct fcb *fcb) +{ + return (fcb->f_active.fe_area == fcb->f_oldest && + fcb->f_active.fe_elem_off == sizeof(struct fcb_disk_area)); +} + +/** + * Length of an element is encoded in 1 or 2 bytes. + * 1 byte for lengths < 128 bytes, and 2 bytes for < 16384. + */ +int +fcb_put_len(u8_t *buf, u16_t len) +{ + if (len < 0x80) { + buf[0] = len; + return 1; + } else if (len < FCB_MAX_LEN) { + buf[0] = (len & 0x7f) | 0x80; + buf[1] = len >> 7; + return 2; + } else { + return FCB_ERR_ARGS; + } +} + +int +fcb_get_len(u8_t *buf, u16_t *len) +{ + int rc; + + if (buf[0] & 0x80) { + if (buf[0] == 0xff && buf[1] == 0xff) { + return FCB_ERR_NOVAR; + } + *len = (buf[0] & 0x7f) | (buf[1] << 7); + rc = 2; + } else { + *len = buf[0]; + rc = 1; + } + return rc; +} + +/** + * Initialize erased sector for use. + */ +int +fcb_sector_hdr_init(struct fcb *fcb, struct flash_area *fap, u16_t id) +{ + struct fcb_disk_area fda; + int rc; + + fda.fd_magic = fcb->f_magic; + fda.fd_ver = fcb->f_version; + fda._pad = 0xff; + fda.fd_id = id; + + rc = flash_area_write(fap, 0, &fda, sizeof(fda)); + if (rc) { + return FCB_ERR_FLASH; + } + return 0; +} + +/** + * Checks whether FCB sector contains data or not. + * Returns <0 in error. + * Returns 0 if sector is unused; + * Returns 1 if sector has data. + */ +int fcb_sector_hdr_read(struct fcb *fcb, struct flash_area *fap, + struct fcb_disk_area *fdap) +{ + struct fcb_disk_area fda; + int rc; + + if (!fdap) { + fdap = &fda; + } + rc = flash_area_read(fap, 0, fdap, sizeof(*fdap)); + if (rc) { + return FCB_ERR_FLASH; + } + if (fdap->fd_magic == 0xffffffff) { + return 0; + } + if (fdap->fd_magic != fcb->f_magic) { + return FCB_ERR_MAGIC; + } + return 1; +} + +/** + * Finds the fcb entry that gives back upto n entries at the end. + * @param0 ptr to fcb + * @param1 n number of fcb entries the user wants to get + * @param2 ptr to the fcb_entry to be returned + * @return 0 on there are any fcbs aviable; -ENOENT otherwise + */ +int +fcb_offset_last_n(struct fcb *fcb, u8_t entries, + struct fcb_entry *last_n_entry) +{ + struct fcb_entry loc; + int i; + + /* assure a minimum amount of entries */ + if (!entries) { + entries = 1; + } + + i = 0; + memset(&loc, 0, sizeof(loc)); + while (!fcb_getnext(fcb, &loc)) { + if (i == 0) { + /* Start from the beginning of fcb entries */ + *last_n_entry = loc; + } + /* Update last_n_entry after n entries and keep updating */ + else if (i > (entries - 1)) { + fcb_getnext(fcb, last_n_entry); + } + i++; + } + + return (i == 0) ? -ENOENT : 0; +} + +/** + * Clear fcb + * @param fcb + * @return 0 on success; non-zero on failure + */ +int +fcb_clear(struct fcb *fcb) +{ + int rc; + + rc = 0; + while (!fcb_is_empty(fcb)) { + rc = fcb_rotate(fcb); + if (rc) { + break; + } + } + return rc; +} diff --git a/subsys/fs/fcb/fcb_append.c b/subsys/fs/fcb/fcb_append.c new file mode 100644 index 00000000000..e1b2090af30 --- /dev/null +++ b/subsys/fs/fcb/fcb_append.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "fcb.h" +#include "fcb_priv.h" + +static struct flash_area * +fcb_new_area(struct fcb *fcb, int cnt) +{ + struct flash_area *fa; + struct flash_area *rfa; + int i; + + rfa = NULL; + i = 0; + fa = fcb->f_active.fe_area; + do { + fa = fcb_getnext_area(fcb, fa); + if (!rfa) { + rfa = fa; + } + if (fa == fcb->f_oldest) { + return NULL; + } + } while (i++ < cnt); + return rfa; +} + +/* + * Take one of the scratch blocks into use, if at all possible. + */ +int +fcb_append_to_scratch(struct fcb *fcb) +{ + struct flash_area *fa; + int rc; + + fa = fcb_new_area(fcb, 0); + if (!fa) { + return FCB_ERR_NOSPACE; + } + rc = fcb_sector_hdr_init(fcb, fa, fcb->f_active_id + 1); + if (rc) { + return rc; + } + fcb->f_active.fe_area = fa; + fcb->f_active.fe_elem_off = sizeof(struct fcb_disk_area); + fcb->f_active_id++; + return FCB_OK; +} + +int +fcb_append(struct fcb *fcb, u16_t len, struct fcb_entry *append_loc) +{ + struct fcb_entry *active; + struct flash_area *fa; + u8_t tmp_str[2]; + int cnt; + int rc; + + cnt = fcb_put_len(tmp_str, len); + if (cnt < 0) { + return cnt; + } + cnt = fcb_len_in_flash(fcb, cnt); + len = fcb_len_in_flash(fcb, len) + fcb_len_in_flash(fcb, FCB_CRC_SZ); + + rc = k_mutex_lock(&fcb->f_mtx, K_FOREVER); + if (rc) { + return FCB_ERR_ARGS; + } + active = &fcb->f_active; + if (active->fe_elem_off + len + cnt > active->fe_area->fa_size) { + fa = fcb_new_area(fcb, fcb->f_scratch_cnt); + if (!fa || (fa->fa_size < + sizeof(struct fcb_disk_area) + len + cnt)) { + rc = FCB_ERR_NOSPACE; + goto err; + } + rc = fcb_sector_hdr_init(fcb, fa, fcb->f_active_id + 1); + if (rc) { + goto err; + } + fcb->f_active.fe_area = fa; + fcb->f_active.fe_elem_off = sizeof(struct fcb_disk_area); + fcb->f_active_id++; + } + + rc = flash_area_write(active->fe_area, active->fe_elem_off, tmp_str, + cnt); + if (rc) { + rc = FCB_ERR_FLASH; + goto err; + } + append_loc->fe_area = active->fe_area; + append_loc->fe_elem_off = active->fe_elem_off; + append_loc->fe_data_off = active->fe_elem_off + cnt; + + active->fe_elem_off = append_loc->fe_data_off + len; + + k_mutex_unlock(&fcb->f_mtx); + + return FCB_OK; +err: + k_mutex_unlock(&fcb->f_mtx); + return rc; +} + +int +fcb_append_finish(struct fcb *fcb, struct fcb_entry *loc) +{ + int rc; + u8_t crc8; + off_t off; + + rc = fcb_elem_crc8(fcb, loc, &crc8); + if (rc) { + return rc; + } + off = loc->fe_data_off + fcb_len_in_flash(fcb, loc->fe_data_len); + + rc = flash_area_write(loc->fe_area, off, &crc8, sizeof(crc8)); + if (rc) { + return FCB_ERR_FLASH; + } + return 0; +} diff --git a/subsys/fs/fcb/fcb_elem_info.c b/subsys/fs/fcb/fcb_elem_info.c new file mode 100644 index 00000000000..922ebbaa118 --- /dev/null +++ b/subsys/fs/fcb/fcb_elem_info.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "fcb.h" +#include "fcb_priv.h" + +/* + * Given offset in flash area, fill in rest of the fcb_entry, and crc8 over + * the data. + */ +int +fcb_elem_crc8(struct fcb *fcb, struct fcb_entry *loc, u8_t *c8p) +{ + u8_t tmp_str[FCB_TMP_BUF_SZ]; + int cnt; + int blk_sz; + u8_t crc8; + u16_t len; + u32_t off; + u32_t end; + int rc; + + if (loc->fe_elem_off + 2 > loc->fe_area->fa_size) { + return FCB_ERR_NOVAR; + } + rc = flash_area_read(loc->fe_area, loc->fe_elem_off, tmp_str, 2); + if (rc) { + return FCB_ERR_FLASH; + } + + cnt = fcb_get_len(tmp_str, &len); + if (cnt < 0) { + return cnt; + } + loc->fe_data_off = loc->fe_elem_off + fcb_len_in_flash(fcb, cnt); + loc->fe_data_len = len; + + crc8 = CRC8_CCITT_INITIAL_VALUE; + crc8 = crc8_ccitt(crc8, tmp_str, cnt); + + off = loc->fe_data_off; + end = loc->fe_data_off + len; + for (; off < end; off += blk_sz) { + blk_sz = end - off; + if (blk_sz > sizeof(tmp_str)) { + blk_sz = sizeof(tmp_str); + } + + rc = flash_area_read(loc->fe_area, off, tmp_str, blk_sz); + if (rc) { + return FCB_ERR_FLASH; + } + crc8 = crc8_ccitt(crc8, tmp_str, blk_sz); + } + *c8p = crc8; + + return 0; +} + +int fcb_elem_info(struct fcb *fcb, struct fcb_entry *loc) +{ + int rc; + u8_t crc8; + u8_t fl_crc8; + off_t off; + + rc = fcb_elem_crc8(fcb, loc, &crc8); + if (rc) { + return rc; + } + off = loc->fe_data_off + fcb_len_in_flash(fcb, loc->fe_data_len); + + rc = flash_area_read(loc->fe_area, off, &fl_crc8, sizeof(fl_crc8)); + if (rc) { + return FCB_ERR_FLASH; + } + + if (fl_crc8 != crc8) { + return FCB_ERR_CRC; + } + return 0; +} diff --git a/subsys/fs/fcb/fcb_getnext.c b/subsys/fs/fcb/fcb_getnext.c new file mode 100644 index 00000000000..81784d7c810 --- /dev/null +++ b/subsys/fs/fcb/fcb_getnext.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "fcb.h" +#include "fcb_priv.h" + +int +fcb_getnext_in_area(struct fcb *fcb, struct fcb_entry *loc) +{ + int rc; + + rc = fcb_elem_info(fcb, loc); + if (rc == 0 || rc == FCB_ERR_CRC) { + do { + loc->fe_elem_off = loc->fe_data_off + + fcb_len_in_flash(fcb, loc->fe_data_len) + + fcb_len_in_flash(fcb, FCB_CRC_SZ); + rc = fcb_elem_info(fcb, loc); + if (rc != FCB_ERR_CRC) { + break; + } + } while (rc == FCB_ERR_CRC); + } + return rc; +} + +struct flash_area * +fcb_getnext_area(struct fcb *fcb, struct flash_area *fap) +{ + fap++; + if (fap >= &fcb->f_sectors[fcb->f_sector_cnt]) { + fap = &fcb->f_sectors[0]; + } + return fap; +} + +int +fcb_getnext_nolock(struct fcb *fcb, struct fcb_entry *loc) +{ + int rc; + + if (loc->fe_area == NULL) { + /* + * Find the first one we have in flash. + */ + loc->fe_area = fcb->f_oldest; + } + if (loc->fe_elem_off == 0) { + /* + * If offset is zero, we serve the first entry from the area. + */ + loc->fe_elem_off = sizeof(struct fcb_disk_area); + rc = fcb_elem_info(fcb, loc); + switch (rc) { + case 0: + return 0; + case FCB_ERR_CRC: + break; + default: + goto next_sector; + } + } else { + rc = fcb_getnext_in_area(fcb, loc); + if (rc == 0) { + return 0; + } + if (rc == FCB_ERR_NOVAR) { + goto next_sector; + } + } + while (rc == FCB_ERR_CRC) { + rc = fcb_getnext_in_area(fcb, loc); + if (rc == 0) { + return 0; + } + + if (rc != FCB_ERR_CRC) { + /* + * Moving to next sector. + */ +next_sector: + if (loc->fe_area == fcb->f_active.fe_area) { + return FCB_ERR_NOVAR; + } + loc->fe_area = fcb_getnext_area(fcb, loc->fe_area); + loc->fe_elem_off = sizeof(struct fcb_disk_area); + rc = fcb_elem_info(fcb, loc); + switch (rc) { + case 0: + return 0; + case FCB_ERR_CRC: + break; + default: + goto next_sector; + } + } + } + + return 0; +} + +int +fcb_getnext(struct fcb *fcb, struct fcb_entry *loc) +{ + int rc; + + rc = k_mutex_lock(&fcb->f_mtx, K_FOREVER); + if (rc) { + return FCB_ERR_ARGS; + } + rc = fcb_getnext_nolock(fcb, loc); + k_mutex_unlock(&fcb->f_mtx); + + return rc; +} diff --git a/subsys/fs/fcb/fcb_priv.h b/subsys/fs/fcb/fcb_priv.h new file mode 100644 index 00000000000..106cae7e58f --- /dev/null +++ b/subsys/fs/fcb/fcb_priv.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __FCB_PRIV_H_ +#define __FCB_PRIV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define FCB_CRC_SZ sizeof(u8_t) +#define FCB_TMP_BUF_SZ 32 + +#define FCB_ID_GT(a, b) (((int16_t)(a) - (int16_t)(b)) > 0) + +struct fcb_disk_area { + u32_t fd_magic; + u8_t fd_ver; + u8_t _pad; + u16_t fd_id; +}; + +int fcb_put_len(u8_t *buf, u16_t len); +int fcb_get_len(u8_t *buf, u16_t *len); + +static inline int fcb_len_in_flash(struct fcb *fcb, u16_t len) +{ + if (fcb->f_align <= 1) { + return len; + } + return (len + (fcb->f_align - 1)) & ~(fcb->f_align - 1); +} + +int fcb_getnext_in_area(struct fcb *fcb, struct fcb_entry *loc); +struct flash_area *fcb_getnext_area(struct fcb *fcb, struct flash_area *fap); +int fcb_getnext_nolock(struct fcb *fcb, struct fcb_entry *loc); + +int fcb_elem_info(struct fcb *fcb, struct fcb_entry *loc); +int fcb_elem_crc8(struct fcb *fcb, struct fcb_entry *loc, u8_t *crc8p); + +int fcb_sector_hdr_init(struct fcb *fcb, struct flash_area *fap, u16_t id); +int fcb_sector_hdr_read(struct fcb *fcb, struct flash_area *fap, + struct fcb_disk_area *fdap); + +#ifdef __cplusplus +} +#endif + +#endif /* __FCB_PRIV_H_ */ diff --git a/subsys/fs/fcb/fcb_rotate.c b/subsys/fs/fcb/fcb_rotate.c new file mode 100644 index 00000000000..82c08b82b27 --- /dev/null +++ b/subsys/fs/fcb/fcb_rotate.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "fcb.h" +#include "fcb_priv.h" + +int +fcb_rotate(struct fcb *fcb) +{ + struct flash_area *fap; + int rc = 0; + + rc = k_mutex_lock(&fcb->f_mtx, K_FOREVER); + if (rc) { + return FCB_ERR_ARGS; + } + + rc = flash_area_erase(fcb->f_oldest, 0, fcb->f_oldest->fa_size); + if (rc) { + rc = FCB_ERR_FLASH; + goto out; + } + if (fcb->f_oldest == fcb->f_active.fe_area) { + /* + * Need to create a new active area, as we're wiping + * the current. + */ + fap = fcb_getnext_area(fcb, fcb->f_oldest); + rc = fcb_sector_hdr_init(fcb, fap, fcb->f_active_id + 1); + if (rc) { + goto out; + } + fcb->f_active.fe_area = fap; + fcb->f_active.fe_elem_off = sizeof(struct fcb_disk_area); + fcb->f_active_id++; + } + fcb->f_oldest = fcb_getnext_area(fcb, fcb->f_oldest); +out: + k_mutex_unlock(&fcb->f_mtx); + return rc; +} diff --git a/subsys/fs/fcb/fcb_walk.c b/subsys/fs/fcb/fcb_walk.c new file mode 100644 index 00000000000..f73733ad913 --- /dev/null +++ b/subsys/fs/fcb/fcb_walk.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "fcb.h" +#include "fcb_priv.h" + +/* + * Call 'cb' for every element in flash circular buffer. If fap is specified, + * only elements with that flash_area are reported. + */ +int +fcb_walk(struct fcb *fcb, struct flash_area *fap, fcb_walk_cb cb, void *cb_arg) +{ + struct fcb_entry loc; + int rc; + + loc.fe_area = fap; + loc.fe_elem_off = 0; + + rc = k_mutex_lock(&fcb->f_mtx, K_FOREVER); + if (rc) { + return FCB_ERR_ARGS; + } + while ((rc = fcb_getnext_nolock(fcb, &loc)) != FCB_ERR_NOVAR) { + k_mutex_unlock(&fcb->f_mtx); + if (fap && loc.fe_area != fap) { + return 0; + } + rc = cb(&loc, cb_arg); + if (rc) { + return rc; + } + k_mutex_lock(&fcb->f_mtx, K_FOREVER); + } + k_mutex_unlock(&fcb->f_mtx); + return 0; +}