You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by GitBox <gi...@apache.org> on 2018/11/13 12:41:30 UTC

[GitHub] kasjer closed pull request #1498: fcb2: second generation of fcb

kasjer closed pull request #1498: fcb2: second generation of fcb
URL: https://github.com/apache/mynewt-core/pull/1498
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/fs/fcb2/README.md b/fs/fcb2/README.md
new file mode 100644
index 0000000000..a21eca600e
--- /dev/null
+++ b/fs/fcb2/README.md
@@ -0,0 +1,60 @@
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+-->
+
+# Flash circular buffer
+
+# Overview
+
+Storage of elements in flash in FIFO fashion. Elements are appended to the of the area until storage space is exhausted. Then the oldest sector should be erased and that can be used in storing new entries.
+
+# API
+
+fcb_init()
+  - initialize fcb for a given array of flash sectors
+
+fcb_append()
+  - reserve space to store an element
+fcb_write()
+  - write element data to flash
+fcb_append_finish()
+  - storage of the element is finished; can calculate CRC for it
+
+fcb_walk(cb, sector)
+  - call cb for every element in the buffer. Or for every element in
+    a particular flash sector, if sector is specified
+fcb_getnext(elem)
+  - return element following elem
+
+fcb_rotate()
+  - erase oldest used sector, and make it current
+
+# Usage
+
+To add an element to circular buffer:
+1. call fcb_append() to get location; if this fails due to lack of space,
+   call fcb_rotate()
+2. use fcb_write() to write contents
+3. call fcb_append_finish() when done
+
+To read contents of the circular buffer:
+1. call fcb_walk() with callback
+2. within callback: copy in data from the element using fcb_read(),
+   call fcb_rotate() when all elements from a given sector have been read
diff --git a/fs/fcb2/include/fcb/fcb.h b/fs/fcb2/include/fcb/fcb.h
new file mode 100644
index 0000000000..9be26305eb
--- /dev/null
+++ b/fs/fcb2/include/fcb/fcb.h
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef __SYS_FCB_H_
+#define __SYS_FCB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \defgroup FCB Flash circular buffer.
+ * @{
+ */
+
+#include <inttypes.h>
+#include <limits.h>
+
+#include "os/mynewt.h"
+#include "flash_map/flash_map.h"
+
+#define FCB_MAX_LEN	(CHAR_MAX | CHAR_MAX << 7) /* Max length of element */
+
+#define FCB_SECTOR_OLDEST UINT16_MAX
+
+struct fcb;
+
+/**
+ * Entry location point to sector, and offset
+ * within that sector.
+ */
+struct fcb_entry {
+    struct fcb *fe_fcb;
+    struct flash_sector_range *fe_range;  /* ptr to area within fcb->f_ranages */
+    uint16_t fe_sector;     /* sector number in fcb flash */
+    uint16_t fe_data_len;   /* size of data area */
+    uint32_t fe_data_off;   /* start of data in sector */
+    uint16_t fe_entry_num;  /* entry number in sector */
+};
+
+/* Number of bytes needed for fcb_sector_entry on flash */
+#define FCB_ENTRY_SIZE          6
+#define FCB_CRC_LEN             2
+
+struct fcb {
+    /* Caller of fcb_init fills this in */
+    uint32_t f_magic;       /* As placed on the disk */
+    uint8_t f_version;      /* Current version number of the data */
+    uint8_t f_scratch_cnt;  /* How many sectors should be kept empty */
+    uint8_t f_range_cnt;    /* Number of elements in range array */
+    uint16_t f_sector_cnt;  /* Number of sectors used by fcb */
+    struct flash_sector_range *f_ranges;
+
+    /* Flash circular buffer internal state */
+    struct os_mutex f_mtx;	/* Locking for accessing the FCB data */
+    uint16_t f_oldest_sec;
+    struct fcb_entry f_active;
+    uint16_t f_active_id;
+    uint16_t f_sector_entries; /* Number of entries in current sector */
+};
+
+struct fcb_sector_info {
+    struct flash_sector_range *si_range;  /* Sector range */
+    uint32_t si_sector_offset;            /* Sector offset in fcb */
+    uint16_t si_sector_in_range;          /* Sector number relative to si_range */
+};
+
+/**
+ * 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
+#define FCB_ERR_VERSION -8
+
+int fcb_init(struct fcb *fcb);
+
+/*
+ * Initialize fcb for specific flash area
+ *
+ * Function initializes FCB structure with data taken from specified flash
+ * area.
+ * If FCB was not initialized before in this area, area will be erased.
+ *
+ * @param fcb            Fcb to initialize
+ * @param flash_area_id  flash area for this fcb
+ * @param magic          User defined magic value that is stored in each
+ *                       flash sector used by fcb
+ * @param version        version of fcb
+ *
+ */
+int fcb_init_flash_area(struct fcb *fcb, int flash_area_id, uint32_t magic,
+    uint8_t version);
+
+/**
+ * fcb_log is needed as the number of entries in a log
+ */
+struct fcb_log {
+    struct fcb fl_fcb;
+    uint8_t fl_entries;
+
+#if MYNEWT_VAL(LOG_STORAGE_WATERMARK)
+    /* Internal - tracking storage use */
+    uint32_t fl_watermark_off;
+#endif
+};
+
+/**
+ * fcb_append() appends an entry to circular buffer. When writing the
+ * contents for the entry, fcb_write() with fcb_entry filled by.
+ * fcb_append(). When you're finished, call fcb_append_finish() with
+ * loc as argument.
+ */
+int fcb_append(struct fcb *fcb, uint16_t len, struct fcb_entry *loc);
+int fcb_write(struct fcb_entry *loc, uint16_t off, void *buf, uint16_t len);
+int fcb_append_finish(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 fcb_read().
+ */
+typedef int (*fcb_walk_cb)(struct fcb_entry *loc, void *arg);
+int fcb_walk(struct fcb *, int sector, fcb_walk_cb cb, void *cb_arg);
+int fcb_getnext(struct fcb *fcb, struct fcb_entry *loc);
+int fcb_read(struct fcb_entry *loc, uint16_t off, void *buf, uint16_t len);
+
+/**
+ * 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, uint8_t entries,
+        struct fcb_entry *last_n_entry);
+
+/**
+ * Get total size of FCB
+ *
+ * @param fcb     FCB to use
+ *
+ * return FCB's size in bytes
+ */
+int fcb_get_total_size(const struct fcb *fcb);
+
+/**
+ * Clears FCB passed to it
+ */
+int fcb_clear(struct fcb *fcb);
+
+/**
+ * Usage report for a given FCB sector. Returns number of elements and the
+ * number of bytes stored in them.
+ */
+int fcb_area_info(struct fcb *fcb, int sector, int *elemsp, int *bytesp);
+
+#ifdef __cplusplus
+}
+
+/**
+ * @} FCB
+ */
+
+#endif
+
+#endif /* __SYS_FLASHVAR_H_ */
diff --git a/fs/fcb2/pkg.yml b/fs/fcb2/pkg.yml
new file mode 100644
index 0000000000..7db43f0cc7
--- /dev/null
+++ b/fs/fcb2/pkg.yml
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: fs/fcb2
+pkg.description: Flash circular buffer.
+pkg.keywords:
+    - flash
+    - storage
+    - log
+
+pkg.deps:
+    - "@apache-mynewt-core/kernel/os"
+    - "@apache-mynewt-core/util/crc"
+    - "@apache-mynewt-core/sys/flash_map"
diff --git a/fs/fcb2/src/fcb.c b/fs/fcb2/src/fcb.c
new file mode 100644
index 0000000000..3ec9cf2434
--- /dev/null
+++ b/fs/fcb2/src/fcb.c
@@ -0,0 +1,371 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include <limits.h>
+#include <stdlib.h>
+
+#include "fcb/fcb.h"
+#include "fcb_priv.h"
+#include "string.h"
+
+int
+fcb_init(struct fcb *fcb)
+{
+    struct flash_sector_range *range;
+    struct flash_sector_range *newest_srp = NULL;
+    int rc;
+    int i;
+    int oldest = -1, newest = -1;
+    int oldest_sec = -1, newest_sec = -1;
+    struct fcb_disk_area fda;
+
+    if (!fcb->f_ranges || 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++) {
+        range = fcb_get_sector_range(fcb, i);
+        /* Require alignment to be a power of two.  Some code depends on this
+         * assumption.
+         */
+        assert((range->fsr_align & (range->fsr_align - 1)) == 0);
+        rc = fcb_sector_hdr_read(fcb, range, i, &fda);
+        if (rc < 0) {
+            return rc;
+        }
+        if (rc == 0) {
+            continue;
+        }
+        if (oldest < 0) {
+            oldest = newest = fda.fd_id;
+            oldest_sec = newest_sec = i;
+            newest_srp = range;
+            continue;
+        }
+        if (FCB_ID_GT(fda.fd_id, newest)) {
+            newest = fda.fd_id;
+            newest_sec = i;
+            newest_srp = range;
+        } else if (FCB_ID_GT(oldest, fda.fd_id)) {
+            oldest = fda.fd_id;
+            oldest_sec = i;
+        }
+    }
+    if (oldest < 0) {
+        /*
+         * No initialized areas.
+         */
+        oldest_sec = newest_sec = 0;
+        newest_srp = fcb->f_ranges;
+        rc = fcb_sector_hdr_init(fcb, newest_sec, 0);
+        if (rc) {
+            return rc;
+        }
+        newest = oldest = 0;
+    }
+    fcb->f_oldest_sec = oldest_sec;
+    fcb->f_active.fe_range = newest_srp;
+    fcb->f_active.fe_sector = newest_sec;
+    fcb->f_active.fe_data_off =
+        fcb_len_in_flash(newest_srp, sizeof(struct fcb_disk_area));
+    fcb->f_active.fe_entry_num = 0;
+    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;
+        }
+    }
+    os_mutex_init(&fcb->f_mtx);
+    return rc;
+}
+
+int
+fcb_free_sector_cnt(struct fcb *fcb)
+{
+    int i;
+    int sector;
+
+    sector = fcb->f_active.fe_sector;
+    for (i = 0; i < fcb->f_sector_cnt; i++) {
+        sector = fcb_getnext_sector(fcb, sector);
+        if (sector == fcb->f_oldest_sec) {
+            break;
+        }
+    }
+    return i;
+}
+
+int
+fcb_is_empty(struct fcb *fcb)
+{
+    return (fcb->f_active.fe_sector == fcb->f_oldest_sec &&
+        fcb->f_active.fe_data_off ==
+            fcb_len_in_flash(fcb->f_active.fe_range, sizeof(struct fcb_disk_area)));
+}
+
+struct flash_sector_range *
+fcb_get_sector_range(const struct fcb *fcb, int sector)
+{
+    int i;
+    struct flash_sector_range *srp = fcb->f_ranges;
+
+    if (FCB_SECTOR_OLDEST == sector) {
+        sector = fcb->f_oldest_sec;
+    }
+    for (i = 0; i < fcb->f_range_cnt; ++i, ++srp) {
+        if (srp->fsr_sector_count <= sector) {
+            sector -= srp->fsr_sector_count;
+            continue;
+        }
+        return srp;
+    }
+    return NULL;
+}
+
+/**
+ * Initialize erased sector for use.
+ */
+int
+fcb_sector_hdr_init(struct fcb *fcb, int sector, uint16_t id)
+{
+    struct fcb_disk_area fda;
+    struct fcb_sector_info info;
+    struct flash_sector_range *range;
+    int sector_in_range;
+    int rc;
+
+    rc = fcb_get_sector_info(fcb, sector, &info);
+    if (rc) {
+        return rc;
+    }
+    range = info.si_range;
+    sector_in_range = sector - range->fsr_first_sector;
+
+    fda.fd_magic = fcb->f_magic;
+    fda.fd_ver = fcb->f_version;
+    fda._pad = 0xff;
+    fda.fd_id = id;
+
+    assert(sector_in_range >= 0 && sector_in_range < range->fsr_sector_count);
+    rc = flash_area_write(&range->fsr_flash_area,
+        sector_in_range * range->fsr_sector_size, &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_sector_range *srp,
+    uint16_t sec, struct fcb_disk_area *fdap)
+{
+    struct fcb_disk_area fda;
+    int rc;
+    uint32_t off = (sec - srp->fsr_first_sector) * srp->fsr_sector_size;
+
+    if (!fdap) {
+        fdap = &fda;
+    }
+    rc = flash_area_read_is_empty(&srp->fsr_flash_area, off, fdap, sizeof(*fdap));
+    if (rc < 0) {
+        return FCB_ERR_FLASH;
+    } else if (rc == 1) {
+        return 0;
+    }
+    if (fdap->fd_magic != fcb->f_magic) {
+        return FCB_ERR_MAGIC;
+    }
+    if (fdap->fd_ver != fcb->f_version) {
+        return FCB_ERR_VERSION;
+    }
+    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; OS_ENOENT otherwise
+ */
+int
+fcb_offset_last_n(struct fcb *fcb, uint8_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;
+        } else if (i > (entries - 1)) {
+            /* Update last_n_entry after n entries and keep updating */
+            fcb_getnext(fcb, last_n_entry);
+        }
+        i++;
+    }
+
+    return (i == 0) ? OS_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;
+}
+
+int
+fcb_init_flash_area(struct fcb *fcb, int flash_area_id, uint32_t magic,
+    uint8_t version)
+{
+    const struct flash_area *fa;
+    struct flash_sector_range *sector_ranges;
+    int sector_range_cnt = 0;
+    int rc;
+
+    /*
+     * We don't know how big the area is so need to check how many sectors are
+     * there and then read information about all sectors - this is needed to
+     * properly initialize FCB. Critical log is somewhat important and shall be
+     * created, so we just assert on any error.
+     *
+     * XXX Should we do something else here?
+     */
+    rc = flash_area_to_sector_ranges(flash_area_id, &sector_range_cnt, NULL);
+    assert(rc == 0 && sector_range_cnt > 0);
+    sector_ranges = malloc(sizeof(struct flash_sector_range) * sector_range_cnt);
+    assert(sector_ranges);
+    rc = flash_area_to_sector_ranges(flash_area_id, &sector_range_cnt, sector_ranges);
+    assert(rc == 0 && sector_range_cnt > 0);
+
+    fcb->f_ranges = sector_ranges;
+    fcb->f_range_cnt = sector_range_cnt;
+    fcb->f_sector_cnt = sector_ranges[sector_range_cnt - 1].fsr_first_sector +
+        sector_ranges[sector_range_cnt - 1].fsr_sector_count;
+    fcb->f_magic = magic;
+    fcb->f_version = version;
+
+    /*
+     * Initialize log in dedicated flash area. This has to succeed since it
+     * should be in dedicated flash area and nothing should prevent us from
+     * creating log there.
+     */
+    rc = fcb_init(fcb);
+    if (rc) {
+        /* Need to erase full area here */
+        rc = flash_area_open(flash_area_id, &fa);
+        assert(rc == 0);
+
+        flash_area_erase(fa, 0, fa->fa_size);
+        rc = fcb_init(fcb);
+        assert(rc == 0);
+    }
+
+    return rc;
+}
+
+int
+fcb_get_sector_info(const struct fcb *fcb, int sector,
+    struct fcb_sector_info *info)
+{
+    struct flash_sector_range *srp = fcb->f_ranges;
+    int i;
+
+    if (sector == FCB_SECTOR_OLDEST) {
+        sector = fcb->f_oldest_sec;
+    }
+
+    for (i = 0; i < fcb->f_range_cnt; ++i, ++srp) {
+        if (srp->fsr_sector_count <= sector) {
+            sector -= srp->fsr_sector_count;
+            continue;
+        }
+        info->si_range = srp;
+        info->si_sector_in_range = sector;
+        info->si_sector_offset = srp->fsr_range_start +
+            sector * srp->fsr_sector_size;
+        return 0;
+    }
+    return FCB_ERR_ARGS;
+}
+
+int
+fcb_get_total_size(const struct fcb *fcb)
+{
+    struct flash_sector_range *srp = fcb->f_ranges;
+    int size = 0;
+    int i;
+
+    for (i = 0; i < fcb->f_range_cnt; ++i, ++srp) {
+        size += srp->fsr_sector_count * srp->fsr_sector_count;
+    }
+    return size;
+}
+
+int
+fcb_sector_erase(const struct fcb *fcb, int sector)
+{
+    struct fcb_sector_info info;
+    int rc;
+
+    rc = fcb_get_sector_info(fcb, sector, &info);
+    if (rc) {
+        goto end;
+    }
+
+    rc = flash_area_erase(&info.si_range->fsr_flash_area,
+        info.si_sector_in_range * info.si_range->fsr_sector_size,
+        info.si_range->fsr_sector_size);
+end:
+    return rc;
+}
diff --git a/fs/fcb2/src/fcb_append.c b/fs/fcb2/src/fcb_append.c
new file mode 100644
index 0000000000..616d62ba44
--- /dev/null
+++ b/fs/fcb2/src/fcb_append.c
@@ -0,0 +1,256 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include <stddef.h>
+
+#include "fcb/fcb.h"
+#include "fcb_priv.h"
+#include "crc/crc8.h"
+
+int
+fcb_new_sector(struct fcb *fcb, int cnt)
+{
+
+    int new_sector = -1;
+    int sector = fcb->f_active.fe_sector;
+    do {
+        sector = fcb_getnext_sector(fcb, sector);
+        if (new_sector < 0) {
+            new_sector = sector;
+        }
+        if (sector == fcb->f_oldest_sec) {
+            new_sector = -1;
+            break;
+        }
+    } while (--cnt >= 0);
+
+    return new_sector;
+}
+
+/*
+ * Take one of the scratch blocks into use, if at all possible.
+ */
+int
+fcb_append_to_scratch(struct fcb *fcb)
+{
+    int sector;
+    int rc;
+    struct flash_sector_range *range;
+
+    sector = fcb_new_sector(fcb, 0);
+    if (sector < 0) {
+        return FCB_ERR_NOSPACE;
+    }
+    rc = fcb_sector_hdr_init(fcb, sector, fcb->f_active_id + 1);
+    if (rc) {
+        return rc;
+    }
+    range = fcb_get_sector_range(fcb, sector);
+    fcb->f_active.fe_range = range;
+    fcb->f_active.fe_sector = sector;
+    fcb->f_active.fe_data_off =
+        fcb_len_in_flash(range, sizeof(struct fcb_disk_area));
+    fcb->f_active.fe_entry_num = 1;
+    fcb->f_active_id++;
+    return FCB_OK;
+}
+
+static inline int
+fcb_sector_flash_offset(const struct fcb_entry *loc)
+{
+    return (loc->fe_sector - loc->fe_range->fsr_first_sector) *
+        loc->fe_range->fsr_sector_size;
+}
+
+int
+fcb_write_to_sector(struct fcb_entry *loc, int off, const void *buf, int len)
+{
+    /* For negative offsets write from the end of sector */
+    if (off < 0) {
+        off += loc->fe_range->fsr_sector_size;
+    }
+    /* Truncate writes beyond sector */
+    if (off + len > loc->fe_range->fsr_sector_size) {
+        len = loc->fe_range->fsr_sector_size - off;
+    }
+    return flash_area_write(&loc->fe_range->fsr_flash_area,
+        fcb_sector_flash_offset(loc) + off, buf, len);
+}
+
+int
+fcb_read_from_sector(struct fcb_entry *loc, int off, void *buf, int len)
+{
+    /* For negative offsets read from the end of sector */
+    if (off < 0) {
+        off += loc->fe_range->fsr_sector_size;
+    }
+    /* Truncate read beyond sector */
+    if (off + len > loc->fe_range->fsr_sector_size) {
+        len = loc->fe_range->fsr_sector_size - off;
+    }
+    return flash_area_read(&loc->fe_range->fsr_flash_area,
+        fcb_sector_flash_offset(loc) + off, buf, len);
+}
+
+int
+fcb_entry_location_in_range(const struct fcb_entry *loc)
+{
+    const struct flash_sector_range *range = loc->fe_range;
+
+    return range->fsr_sector_size * (1 + loc->fe_sector - range->fsr_first_sector) -
+        (loc->fe_entry_num * fcb_len_in_flash(loc->fe_range, FCB_ENTRY_SIZE));
+}
+
+int
+fcb_active_sector_free_space(const struct fcb *fcb)
+{
+    const struct fcb_entry *active = &fcb->f_active;
+    const struct flash_sector_range *range = active->fe_range;
+
+    return range->fsr_sector_size - active->fe_data_off -
+        (active->fe_entry_num * fcb_len_in_flash(range, FCB_ENTRY_SIZE));
+}
+
+int
+fcb_write(struct fcb_entry *loc, uint16_t off, void *buf, uint16_t len)
+{
+    int pos = loc->fe_data_off + off;
+    /* Make sure tha write does not exceed lenght declared in fcb_append */
+    if (off + len > loc->fe_data_len) {
+        len = loc->fe_data_len - off;
+    }
+    return fcb_write_to_sector(loc, pos, buf, len);
+}
+
+int
+fcb_read(struct fcb_entry *loc, uint16_t off, void *buf, uint16_t len)
+{
+    int pos = loc->fe_data_off + off;
+    /* Make sure tha read is only from entry data */
+    if (off + len > loc->fe_data_len) {
+        len = loc->fe_data_len - off;
+    }
+    return fcb_read_from_sector(loc, pos, buf, len);
+}
+
+int
+fcb_element_length_in_flash(const struct fcb_entry *loc, int len)
+{
+    return fcb_len_in_flash(loc->fe_range, len) +
+        fcb_len_in_flash(loc->fe_range, FCB_CRC_LEN);
+}
+
+int
+fcb_append(struct fcb *fcb, uint16_t len, struct fcb_entry *append_loc)
+{
+    struct fcb_entry *active;
+    struct flash_sector_range *range;
+    uint8_t flash_entry[FCB_ENTRY_SIZE];
+    int sector;
+    int rc;
+
+    if (len == 0 || len >= FCB_MAX_LEN) {
+        return FCB_ERR_ARGS;
+    }
+
+    rc = os_mutex_pend(&fcb->f_mtx, OS_WAIT_FOREVER);
+    if (rc && rc != OS_NOT_STARTED) {
+        return FCB_ERR_ARGS;
+    }
+    active = &fcb->f_active;
+    if (fcb_active_sector_free_space(fcb) < fcb_element_length_in_flash(active, len)) {
+        sector = fcb_new_sector(fcb, fcb->f_scratch_cnt);
+        if (sector >= 0) {
+            range = fcb_get_sector_range(fcb, sector);
+        }
+        if (sector < 0 || (range->fsr_sector_size <
+            fcb_len_in_flash(range, sizeof(struct fcb_disk_area)) +
+            fcb_len_in_flash(range, len) +
+            fcb_len_in_flash(range, FCB_CRC_LEN))) {
+            rc = FCB_ERR_NOSPACE;
+            goto err;
+        }
+        rc = fcb_sector_hdr_init(fcb, sector, fcb->f_active_id + 1);
+        if (rc) {
+            goto err;
+        }
+        fcb->f_active.fe_range = range;
+        fcb->f_active.fe_sector = sector;
+        /* Start with offset just after sector header */
+        fcb->f_active.fe_data_off =
+            fcb_len_in_flash(range, sizeof(struct fcb_disk_area));
+        /* No entries as yet */
+        fcb->f_active.fe_entry_num = 1;
+        fcb->f_active.fe_data_len = 0;
+        fcb->f_active_id++;
+    } else {
+        range = active->fe_range;
+    }
+
+    /* Write new entry at the end of the sector */
+    flash_entry[0] = (uint8_t)(fcb->f_active.fe_data_off >> 16);
+    flash_entry[1] = (uint8_t)(fcb->f_active.fe_data_off >> 8);
+    flash_entry[2] = (uint8_t)(fcb->f_active.fe_data_off >> 0);
+    flash_entry[3] = (uint8_t)(len >> 8);
+    flash_entry[4] = (uint8_t)(len >> 0);
+    flash_entry[5] = crc8_calc(crc8_init(), flash_entry, FCB_ENTRY_SIZE - 1);
+
+    rc = fcb_write_to_sector(active,
+        active->fe_entry_num * -fcb_len_in_flash(range, FCB_ENTRY_SIZE),
+        flash_entry, FCB_ENTRY_SIZE);
+    if (rc) {
+        rc = FCB_ERR_FLASH;
+        goto err;
+    }
+    *append_loc = *active;
+    /* Active element had everything ready except lenght */
+    append_loc->fe_data_len = len;
+
+    /* Prepare active element num and offset for new append */
+    active->fe_data_off += fcb_element_length_in_flash(active, len);
+    active->fe_entry_num++;
+
+    os_mutex_release(&fcb->f_mtx);
+
+    return FCB_OK;
+err:
+    os_mutex_release(&fcb->f_mtx);
+    return rc;
+}
+
+int
+fcb_append_finish(struct fcb_entry *loc)
+{
+    int rc;
+    uint16_t crc;
+    uint8_t fl_crc[2];
+    uint32_t off;
+
+    rc = fcb_elem_crc16(loc, &crc);
+    if (rc) {
+        return rc;
+    }
+    put_be16(fl_crc, crc);
+    off = loc->fe_data_off + fcb_len_in_flash(loc->fe_range, loc->fe_data_len);
+
+    rc = fcb_write_to_sector(loc, off, fl_crc, sizeof(fl_crc));
+    if (rc) {
+        return FCB_ERR_FLASH;
+    }
+    return 0;
+}
diff --git a/fs/fcb2/src/fcb_area_info.c b/fs/fcb2/src/fcb_area_info.c
new file mode 100644
index 0000000000..80c41c4d06
--- /dev/null
+++ b/fs/fcb2/src/fcb_area_info.c
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "fcb/fcb.h"
+#include "fcb_priv.h"
+
+int
+fcb_area_info(struct fcb *fcb, int sector, int *elemsp, int *bytesp)
+{
+    struct fcb_entry loc;
+    struct fcb_sector_info info;
+    int rc;
+    int elems = 0;
+    int bytes = 0;
+
+    rc = fcb_get_sector_info(fcb, sector, &info);
+    if (rc) {
+        return rc;
+    }
+    loc.fe_range = info.si_range;
+    loc.fe_sector = info.si_sector_in_range + loc.fe_range->fsr_first_sector;
+    loc.fe_entry_num = 0;
+    /* In case caller passed oldest, get real sector number */
+    if (sector == FCB_SECTOR_OLDEST) {
+        sector =  loc.fe_sector;
+    }
+
+    while (1) {
+        rc = fcb_getnext(fcb, &loc);
+        if (rc) {
+            break;
+        }
+        if (sector != loc.fe_sector) {
+            break;
+        }
+        elems++;
+        bytes += loc.fe_data_len;
+    }
+    if (elemsp) {
+        *elemsp = elems;
+    }
+    if (bytesp) {
+        *bytesp = bytes;
+    }
+    return 0;
+}
diff --git a/fs/fcb2/src/fcb_elem_info.c b/fs/fcb2/src/fcb_elem_info.c
new file mode 100644
index 0000000000..413eea2663
--- /dev/null
+++ b/fs/fcb2/src/fcb_elem_info.c
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <crc/crc8.h>
+#include <crc/crc16.h>
+
+#include "fcb/fcb.h"
+#include "fcb_priv.h"
+
+/*
+ * Given offset in flash area, compute crc16 over the data.
+ */
+int
+fcb_elem_crc16(struct fcb_entry *loc, uint16_t *c16p)
+{
+    uint8_t tmp_str[FCB_TMP_BUF_SZ];
+    int blk_sz;
+    uint16_t crc16;
+    uint32_t off;
+    uint32_t end;
+    int rc;
+
+    crc16 = 0xFFFF;
+
+    off = loc->fe_data_off;
+    end = loc->fe_data_off + loc->fe_data_len;
+    for (; off < end; off += blk_sz) {
+        blk_sz = end - off;
+        if (blk_sz > sizeof(tmp_str)) {
+            blk_sz = sizeof(tmp_str);
+        }
+
+        rc = fcb_read_from_sector(loc, off, tmp_str, blk_sz);
+        if (rc) {
+            return FCB_ERR_FLASH;
+        }
+        crc16 = crc16_ccitt(crc16, tmp_str, blk_sz);
+    }
+    *c16p = crc16;
+
+    return 0;
+}
+
+int
+fcb_read_entry(struct fcb_entry *loc)
+{
+    uint8_t buf[FCB_ENTRY_SIZE];
+    uint8_t entry_crc;
+    uint32_t entry_offset;
+    uint32_t offset;
+    uint16_t len;
+    int rc;
+
+    assert(loc != NULL);
+    entry_offset = fcb_entry_location_in_range(loc);
+    rc = flash_area_read_is_empty(&loc->fe_range->fsr_flash_area,
+        entry_offset, buf, sizeof(buf));
+    if (rc < 0) {
+        /* Error reading from flash */
+        return FCB_ERR_FLASH;
+    } else if (rc == 1) {
+        /* Entry not filled on flash */
+        return FCB_ERR_NOVAR;
+    }
+    /* Check entry CRC first */
+    entry_crc = crc8_calc(crc8_init(), buf, FCB_ENTRY_SIZE - 1);
+    if (entry_crc != buf[FCB_ENTRY_SIZE - 1]) {
+        return FCB_ERR_CRC;
+    }
+    offset = (buf[0] << 16) | (buf[1] << 8) | (buf[2] << 0);
+    len = (buf[3] << 8) | (buf[4] << 0);
+    /* Sanity check for entry */
+    if (offset < fcb_len_in_flash(loc->fe_range, sizeof(struct fcb_disk_area)) ||
+        len > FCB_MAX_LEN ||
+        offset + len > entry_offset) {
+        /* Entry was found but data stored does not make any sense
+         * report as CRC error so it can be skipped */
+        return FCB_ERR_CRC;
+    }
+
+    /* Entry looks decent, pass to the caller */
+    loc->fe_data_off = offset;
+    loc->fe_data_len = len;
+
+    return 0;
+}
+
+int
+fcb_elem_info(struct fcb_entry *loc)
+{
+    int rc;
+    uint16_t crc16;
+    uint8_t fl_crc16[2];
+
+    /* Read entry from the end of the sector */
+    rc = fcb_read_entry(loc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    /* Read actual data and calculate CRC */
+    rc = fcb_elem_crc16(loc, &crc16);
+    if (rc) {
+        return rc;
+    }
+
+    /* Read CRC from flash */
+    rc = fcb_read_from_sector(loc,
+        loc->fe_data_off + fcb_len_in_flash(loc->fe_range, loc->fe_data_len),
+        &fl_crc16, 2);
+    if (rc || get_be16(fl_crc16) != crc16) {
+        return FCB_ERR_CRC;
+    }
+
+    return 0;
+}
diff --git a/fs/fcb2/src/fcb_getnext.c b/fs/fcb2/src/fcb_getnext.c
new file mode 100644
index 0000000000..4284afe0a3
--- /dev/null
+++ b/fs/fcb2/src/fcb_getnext.c
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include <stddef.h>
+
+#include "fcb/fcb.h"
+#include "fcb_priv.h"
+
+int
+fcb_getnext_in_area(struct fcb *fcb, struct fcb_entry *loc)
+{
+    int rc = FCB_ERR_CRC;
+    int off;
+    int len;
+
+    while (rc == FCB_ERR_CRC) {
+        len = loc->fe_data_len;
+        off = loc->fe_data_off;
+        loc->fe_data_len = 0;
+        loc->fe_entry_num++;
+        rc = fcb_elem_info(loc);
+        if (len) {
+            loc->fe_data_off = off + fcb_len_in_flash(loc->fe_range, len) +
+                fcb_len_in_flash(loc->fe_range, 2);
+        }
+    }
+    return rc;
+}
+
+int
+fcb_getnext_nolock(struct fcb *fcb, struct fcb_entry *loc)
+{
+    int rc;
+
+    if (loc->fe_range == NULL) {
+        /*
+         * Find the first one we have in flash.
+         */
+        loc->fe_sector = fcb->f_oldest_sec;
+        loc->fe_range = fcb_get_sector_range(fcb, loc->fe_sector);
+    }
+    if (loc->fe_entry_num == 0) {
+        /*
+         * If offset is zero, we serve the first entry from the area.
+         */
+        loc->fe_entry_num = 1;
+        loc->fe_data_off =
+            fcb_len_in_flash(loc->fe_range, sizeof(struct fcb_disk_area));
+        loc->fe_data_len = 0;
+        rc = fcb_elem_info(loc);
+    } else {
+        rc = fcb_getnext_in_area(fcb, loc);
+    }
+    switch (rc) {
+    case 0:
+        return 0;
+    case FCB_ERR_CRC:
+        break;
+    default:
+        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_sector == fcb->f_active.fe_sector) {
+                return FCB_ERR_NOVAR;
+            }
+            loc->fe_sector = fcb_getnext_sector(fcb, loc->fe_sector);
+            loc->fe_range = fcb_get_sector_range(fcb, loc->fe_sector);
+            loc->fe_entry_num = 1;
+            loc->fe_data_off =
+                fcb_len_in_flash(loc->fe_range, sizeof(struct fcb_disk_area));
+            loc->fe_data_len = 0;
+            rc = fcb_elem_info(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 = os_mutex_pend(&fcb->f_mtx, OS_WAIT_FOREVER);
+    if (rc && rc != OS_NOT_STARTED) {
+        return FCB_ERR_ARGS;
+    }
+    rc = fcb_getnext_nolock(fcb, loc);
+    os_mutex_release(&fcb->f_mtx);
+
+    return rc;
+}
diff --git a/fs/fcb2/src/fcb_priv.h b/fs/fcb2/src/fcb_priv.h
new file mode 100644
index 0000000000..e87a9444d1
--- /dev/null
+++ b/fs/fcb2/src/fcb_priv.h
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef __SYS_FCB_PRIV_H_
+#define __SYS_FCB_PRIV_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FCB_TMP_BUF_SZ  32
+
+#define FCB_ID_GT(a, b) (((int16_t)(a) - (int16_t)(b)) > 0)
+
+struct fcb_disk_area {
+    uint32_t fd_magic;
+    uint8_t  fd_ver;
+    uint8_t  _pad;
+    uint16_t fd_id;
+};
+
+static inline int
+fcb_len_in_flash(const struct flash_sector_range *range, uint16_t len)
+{
+    if (range->fsr_align <= 1) {
+        return len;
+    }
+    return (len + (range->fsr_align - 1)) & ~(range->fsr_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);
+
+static inline int
+fcb_getnext_sector(struct fcb *fcb, int sector)
+{
+    if (++sector >= fcb->f_sector_cnt) {
+        sector = 0;
+    }
+    return sector;
+}
+
+int fcb_getnext_nolock(struct fcb *fcb, struct fcb_entry *loc);
+
+int fcb_elem_info(struct fcb_entry *loc);
+int fcb_elem_crc8(struct fcb_entry *loc, uint8_t *crc8p);
+int fcb_elem_crc16(struct fcb_entry *loc, uint16_t *c16p);
+int fcb_sector_hdr_init(struct fcb *fcb, int sector, uint16_t id);
+int fcb_entry_location_in_range(const struct fcb_entry *loc);
+
+
+struct flash_sector_range *fcb_get_sector_range(const struct fcb *fcb,
+    int sector);
+
+int fcb_sector_hdr_read(struct fcb *, struct flash_sector_range *srp,
+    uint16_t sec, struct fcb_disk_area *fdap);
+
+/**
+ * Finds sector range for given fcb sector.
+ */
+struct flash_sector_range *fcb_get_sector_range(const struct fcb *fcb,
+    int sector);
+
+/**
+ * @brief Get information about sector from fcb.
+ *
+ * @param fcb     fcb to check sector
+ * @param sector  fcb sector number
+ *                sector can be specified as FCB_SECTOR_OLDEST.
+ * @param info    pointer to structure that will receive sector information
+ *
+ * @return 0 if sector information was extracted correctly
+ *        FCB_ERR_ARGS if sector number was outside fcb range.
+ */
+int fcb_get_sector_info(const struct fcb *fcb, int sector,
+    struct fcb_sector_info *info);
+
+/**
+ * @brief Write data to fcb sector.
+ *
+ * @param loc     location of the sector from fcb_get_sector_loc().
+ * @param off     offset from the beginning of the sector to start write
+ * @param buf     pointer to data to be written
+ * @param len     number of bytes to write to the fcb sector
+ *
+ * @return 0 if write was successful non zero otherwise.
+ */
+int fcb_write_to_sector(struct fcb_entry *loc, int off,
+    const void *buf, int len);
+
+/**
+ * @brief Read data from fcb sector.
+ *
+ * @param loc     location of the sector from fcb_get_sector_loc().
+ * @param off     offset from the beginning of the sector to start read
+ * @param buf     pointer to output buffer
+ * @param len     number of bytes to read from the fcb sector
+ *
+ * @return 0 if read was successful non zero otherwise.
+ */
+int fcb_read_from_sector(struct fcb_entry *loc, int off, void *buf, int len);
+
+/**
+ * Erase sector in FCB
+ *
+ * @param fcb     FCB to use
+ * @param sector  sector number to erase 0..f_sector_cnt
+ *
+ * return 0 on success, error code on failure
+ */
+int fcb_sector_erase(const struct fcb *fcb, int sector);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/fs/fcb2/src/fcb_rotate.c b/fs/fcb2/src/fcb_rotate.c
new file mode 100644
index 0000000000..53364e5308
--- /dev/null
+++ b/fs/fcb2/src/fcb_rotate.c
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "fcb/fcb.h"
+#include "fcb_priv.h"
+
+int
+fcb_rotate(struct fcb *fcb)
+{
+    int sector;
+    int rc = 0;
+    struct flash_sector_range *range;
+
+    rc = os_mutex_pend(&fcb->f_mtx, OS_WAIT_FOREVER);
+    if (rc && rc != OS_NOT_STARTED) {
+        return FCB_ERR_ARGS;
+    }
+
+    rc = fcb_sector_erase(fcb, fcb->f_oldest_sec);
+    if (rc) {
+        rc = FCB_ERR_FLASH;
+        goto out;
+    }
+    if (fcb->f_oldest_sec == fcb->f_active.fe_sector) {
+        /*
+         * Need to create a new active sector, as we're wiping the current.
+         */
+        sector = fcb_getnext_sector(fcb, fcb->f_oldest_sec);
+
+        rc = fcb_sector_hdr_init(fcb, sector, fcb->f_active_id + 1);
+        if (rc) {
+            goto out;
+        }
+        range = fcb_get_sector_range(fcb, sector);
+        fcb->f_active.fe_sector = sector;
+        fcb->f_active.fe_range = range;
+        fcb->f_active.fe_data_off =
+            fcb_len_in_flash(range, sizeof(struct fcb_disk_area));
+        fcb->f_active.fe_entry_num = 1;
+        fcb->f_active.fe_data_len = 0;
+        fcb->f_active_id++;
+    }
+    fcb->f_oldest_sec = fcb_getnext_sector(fcb, fcb->f_oldest_sec);
+out:
+    os_mutex_release(&fcb->f_mtx);
+    return rc;
+}
diff --git a/fs/fcb2/src/fcb_walk.c b/fs/fcb2/src/fcb_walk.c
new file mode 100644
index 0000000000..e6e1084ae1
--- /dev/null
+++ b/fs/fcb2/src/fcb_walk.c
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "fcb/fcb.h"
+#include "fcb_priv.h"
+
+int
+fcb_get_sector_loc(const struct fcb *fcb, int sector, struct fcb_entry *entry)
+{
+    struct fcb_sector_info info;
+    int rc = 0;
+
+    rc = fcb_get_sector_info(fcb, sector, &info);
+    if (rc == 0) {
+        entry->fe_range = info.si_range;
+        entry->fe_sector = info.si_sector_in_range +
+            info.si_range->fsr_first_sector;
+    }
+
+    return rc;
+}
+
+/*
+ * 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, int sector, fcb_walk_cb cb, void *cb_arg)
+{
+    struct fcb_entry loc;
+    int rc;
+
+    fcb_get_sector_loc(fcb, sector, &loc);
+    loc.fe_data_off = 0;
+    loc.fe_entry_num = 0;
+
+    rc = os_mutex_pend(&fcb->f_mtx, OS_WAIT_FOREVER);
+    if (rc && rc != OS_NOT_STARTED) {
+        return FCB_ERR_ARGS;
+    }
+    while ((rc = fcb_getnext_nolock(fcb, &loc)) != FCB_ERR_NOVAR) {
+        os_mutex_release(&fcb->f_mtx);
+        if (sector != FCB_SECTOR_OLDEST && loc.fe_sector != sector) {
+            return 0;
+        }
+        rc = cb(&loc, cb_arg);
+        if (rc) {
+            return rc;
+        }
+        os_mutex_pend(&fcb->f_mtx, OS_WAIT_FOREVER);
+    }
+    os_mutex_release(&fcb->f_mtx);
+    return 0;
+}
diff --git a/fs/fcb2/test/pkg.yml b/fs/fcb2/test/pkg.yml
new file mode 100644
index 0000000000..1fb6dc11ab
--- /dev/null
+++ b/fs/fcb2/test/pkg.yml
@@ -0,0 +1,30 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+pkg.name: fs/fcb2/test
+pkg.type: unittest
+pkg.description: "FCB unit tests."
+pkg.author: "Apache Mynewt <de...@mynewt.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps: 
+    - "@apache-mynewt-core/test/testutil"
+    - "@apache-mynewt-core/fs/fcb2"
+
+pkg.deps.SELFTEST:
+    - "@apache-mynewt-core/sys/console/stub"
diff --git a/fs/fcb2/test/src/fcb_test.c b/fs/fcb2/test/src/fcb_test.c
new file mode 100644
index 0000000000..bdbdba5f50
--- /dev/null
+++ b/fs/fcb2/test/src/fcb_test.c
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "os/mynewt.h"
+#include "testutil/testutil.h"
+
+#include "fcb/fcb.h"
+#include "fcb/../../src/fcb_priv.h"
+
+#include "fcb_test.h"
+
+#include "flash_map/flash_map.h"
+
+#if MYNEWT_VAL(SELFTEST)
+
+struct fcb test_fcb;
+
+#if MYNEWT_VAL(SELFTEST)
+struct flash_sector_range test_fcb_ranges[] = {
+    [0] = {
+        .fsr_flash_area = {
+            .fa_device_id = 0,
+            .fa_off = 0,
+            .fa_size = 0x10000, /* 64K */
+        },
+        .fsr_range_start = 0,
+        .fsr_first_sector = 0,
+        .fsr_sector_size = 0x4000, /* 16 K */
+        .fsr_sector_count = 4,
+        .fsr_align = 1,
+    },
+};
+
+void
+fcb_test_wipe(void)
+{
+    int i;
+    int rc;
+    struct flash_area *fap;
+
+    for (i = 0; i < sizeof(test_fcb_ranges) / sizeof(test_fcb_ranges[0]); i++) {
+        fap = &test_fcb_ranges[i].fsr_flash_area;
+        rc = flash_area_erase(fap, 0, fap->fa_size);
+        TEST_ASSERT(rc == 0);
+    }
+}
+#endif
+
+int
+fcb_test_empty_walk_cb(struct fcb_entry *loc, void *arg)
+{
+    TEST_ASSERT(0);
+    return 0;
+}
+
+uint8_t
+fcb_test_append_data(int msg_len, int off)
+{
+    return (msg_len ^ off);
+}
+
+int
+fcb_test_data_walk_cb(struct fcb_entry *loc, void *arg)
+{
+    uint16_t len;
+    uint8_t test_data[128];
+    int rc;
+    int i;
+    int *var_cnt = (int *)arg;
+
+    len = loc->fe_data_len;
+
+    TEST_ASSERT(len == *var_cnt);
+
+    rc = fcb_read(loc, 0, test_data, len);
+    TEST_ASSERT(rc == 0);
+
+    for (i = 0; i < len; i++) {
+        TEST_ASSERT(test_data[i] == fcb_test_append_data(len, i));
+    }
+    (*var_cnt)++;
+    return 0;
+}
+
+int
+fcb_test_cnt_elems_cb(struct fcb_entry *loc, void *arg)
+{
+    struct append_arg *aa = (struct append_arg *)arg;
+    int idx;
+
+    idx = loc->fe_sector;
+    aa->elem_cnts[idx]++;
+    return 0;
+}
+
+void
+fcb_tc_pretest(void* arg)
+{
+    struct fcb *fcb;
+    int rc = 0;
+
+    fcb_test_wipe();
+    fcb = &test_fcb;
+    memset(fcb, 0, sizeof(*fcb));
+    fcb->f_sector_cnt = (int)arg;
+    fcb->f_ranges = test_fcb_ranges; /* XXX */
+    fcb->f_range_cnt = 1;
+    test_fcb_ranges[0].fsr_sector_count = (int)arg;
+    test_fcb_ranges[0].fsr_flash_area.fa_size = test_fcb_ranges[0].fsr_sector_size * (int)arg;
+
+    rc = fcb_init(fcb);
+    if (rc != 0) {
+        printf("fcb_tc_pretest rc == %x, %d\n", rc, rc);
+        TEST_ASSERT(rc == 0);
+    }
+
+    return;
+}
+
+void
+fcb_ts_init(void *arg)
+{
+    return;
+}
+
+TEST_CASE_DECL(fcb_test_init)
+TEST_CASE_DECL(fcb_test_empty_walk)
+TEST_CASE_DECL(fcb_test_append)
+TEST_CASE_DECL(fcb_test_append_too_big)
+TEST_CASE_DECL(fcb_test_append_fill)
+TEST_CASE_DECL(fcb_test_reset)
+TEST_CASE_DECL(fcb_test_rotate)
+TEST_CASE_DECL(fcb_test_multiple_scratch)
+TEST_CASE_DECL(fcb_test_last_of_n)
+TEST_CASE_DECL(fcb_test_area_info)
+
+TEST_SUITE(fcb_test_all)
+{
+    tu_case_set_pre_cb(fcb_tc_pretest, (void*)2);
+
+    /* pretest not needed */
+    fcb_test_init();
+
+    tu_case_set_pre_cb(fcb_tc_pretest, (void*)2);
+    fcb_test_empty_walk();
+
+    tu_case_set_pre_cb(fcb_tc_pretest, (void*)2);
+    fcb_test_append();
+
+    tu_case_set_pre_cb(fcb_tc_pretest, (void*)2);
+    fcb_test_append_too_big();
+
+    tu_case_set_pre_cb(fcb_tc_pretest, (void*)2);
+    fcb_test_append_fill();
+
+    tu_case_set_pre_cb(fcb_tc_pretest, (void*)2);
+    fcb_test_reset();
+
+    tu_case_set_pre_cb(fcb_tc_pretest, (void*)2);
+    fcb_test_rotate();
+
+    tu_case_set_pre_cb(fcb_tc_pretest, (void*)4);
+    fcb_test_multiple_scratch();
+
+    tu_case_set_pre_cb(fcb_tc_pretest, (void*)4);
+    fcb_test_last_of_n();
+
+    tu_case_set_pre_cb(fcb_tc_pretest, (void*)2);
+    fcb_test_area_info();
+
+}
+
+#if MYNEWT_VAL(SELFTEST)
+int
+main(int argc, char **argv)
+{
+    sysinit();
+
+    tu_suite_set_init_cb(fcb_ts_init, NULL);
+    fcb_test_all();
+
+    return tu_any_failed;
+}
+#endif
+
+#endif /* MYNEWT_VAL(SELFTEST) */
diff --git a/fs/fcb2/test/src/fcb_test.h b/fs/fcb2/test/src/fcb_test.h
new file mode 100644
index 0000000000..b25d4b73a2
--- /dev/null
+++ b/fs/fcb2/test/src/fcb_test.h
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef _FCB_TEST_H
+#define _FCB_TEST_H
+
+#include <stdio.h>
+#include <string.h>
+
+#include "os/mynewt.h"
+#include "testutil/testutil.h"
+
+#include "fcb/fcb.h"
+#include "fcb_priv.h"
+
+#ifdef __cplusplus
+#extern "C" {
+#endif
+
+extern struct fcb test_fcb;
+
+extern struct flash_sector_range test_fcb_ranges[];
+
+struct append_arg {
+    int *elem_cnts;
+};
+
+void fcb_test_wipe(void);
+int fcb_test_empty_walk_cb(struct fcb_entry *loc, void *arg);
+uint8_t fcb_test_append_data(int msg_len, int off);
+int fcb_test_data_walk_cb(struct fcb_entry *loc, void *arg);
+int fcb_test_cnt_elems_cb(struct fcb_entry *loc, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _FCB_TEST_H */
diff --git a/fs/fcb2/test/src/testcases/fcb_test_append.c b/fs/fcb2/test/src/testcases/fcb_test_append.c
new file mode 100644
index 0000000000..9b4e9b941d
--- /dev/null
+++ b/fs/fcb2/test/src/testcases/fcb_test_append.c
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "fcb_test.h"
+
+TEST_CASE(fcb_test_append)
+{
+    int rc;
+    struct fcb *fcb;
+    struct fcb_entry loc;
+    uint8_t test_data[128];
+    int i;
+    int j;
+    int var_cnt;
+
+    fcb = &test_fcb;
+
+    for (i = 1; i < sizeof(test_data); i++) {
+        for (j = 0; j < i; j++) {
+            test_data[j] = fcb_test_append_data(i, j);
+        }
+        rc = fcb_append(fcb, i, &loc);
+        TEST_ASSERT_FATAL(rc == 0);
+        rc = fcb_write(&loc, 0, test_data, i);
+        TEST_ASSERT(rc == 0);
+        rc = fcb_append_finish(&loc);
+        TEST_ASSERT(rc == 0);
+    }
+
+    var_cnt = 1;
+    rc = fcb_walk(fcb, 0, fcb_test_data_walk_cb, &var_cnt);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(var_cnt == sizeof(test_data));
+}
diff --git a/fs/fcb2/test/src/testcases/fcb_test_append_fill.c b/fs/fcb2/test/src/testcases/fcb_test_append_fill.c
new file mode 100644
index 0000000000..f514e003fe
--- /dev/null
+++ b/fs/fcb2/test/src/testcases/fcb_test_append_fill.c
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "fcb_test.h"
+
+TEST_CASE(fcb_test_append_fill)
+{
+    struct fcb *fcb;
+    int rc;
+    int i;
+    struct fcb_entry loc;
+    uint8_t test_data[128];
+    int elem_cnts[2] = {0, 0};
+    int aa_together_cnts[2];
+    struct append_arg aa_together = {
+        .elem_cnts = aa_together_cnts
+    };
+    int aa_separate_cnts[2];
+    struct append_arg aa_separate = {
+        .elem_cnts = aa_separate_cnts
+    };
+
+    fcb = &test_fcb;
+
+    for (i = 0; i < sizeof(test_data); i++) {
+        test_data[i] = fcb_test_append_data(sizeof(test_data), i);
+    }
+
+    while (1) {
+        rc = fcb_append(fcb, sizeof(test_data), &loc);
+        if (rc == FCB_ERR_NOSPACE) {
+            break;
+        }
+        if (loc.fe_sector == 0) {
+            elem_cnts[0]++;
+        } else if (loc.fe_sector == 1) {
+            elem_cnts[1]++;
+        } else {
+            TEST_ASSERT(0);
+        }
+
+        rc = fcb_write(&loc, 0, test_data, sizeof(test_data));
+        TEST_ASSERT(rc == 0);
+
+        rc = fcb_append_finish(&loc);
+        TEST_ASSERT(rc == 0);
+    }
+    TEST_ASSERT(elem_cnts[0] > 0);
+    TEST_ASSERT(elem_cnts[0] == elem_cnts[1]);
+
+    memset(&aa_together_cnts, 0, sizeof(aa_together_cnts));
+    rc = fcb_walk(fcb, FCB_SECTOR_OLDEST, fcb_test_cnt_elems_cb, &aa_together);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(aa_together.elem_cnts[0] == elem_cnts[0]);
+    TEST_ASSERT(aa_together.elem_cnts[1] == elem_cnts[1]);
+
+    memset(&aa_separate_cnts, 0, sizeof(aa_separate_cnts));
+    rc = fcb_walk(fcb, 0, fcb_test_cnt_elems_cb,
+      &aa_separate);
+    TEST_ASSERT(rc == 0);
+    rc = fcb_walk(fcb, 1, fcb_test_cnt_elems_cb,
+      &aa_separate);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(aa_separate.elem_cnts[0] == elem_cnts[0]);
+    TEST_ASSERT(aa_separate.elem_cnts[1] == elem_cnts[1]);
+
+}
diff --git a/fs/fcb2/test/src/testcases/fcb_test_append_too_big.c b/fs/fcb2/test/src/testcases/fcb_test_append_too_big.c
new file mode 100644
index 0000000000..990e6d1a5f
--- /dev/null
+++ b/fs/fcb2/test/src/testcases/fcb_test_append_too_big.c
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "fcb_test.h"
+
+TEST_CASE(fcb_test_append_too_big)
+{
+    struct fcb *fcb;
+    int rc;
+    int len;
+    struct fcb_entry elem_loc;
+
+    fcb = &test_fcb;
+
+    /*
+     * Max element which fits inside sector is
+     * sector size - (disk header + crc + 6 bytes of entry).
+     */
+    len = fcb->f_active.fe_range->fsr_sector_size;
+
+    rc = fcb_append(fcb, len, &elem_loc);
+    TEST_ASSERT(rc != 0);
+
+    len--;
+    rc = fcb_append(fcb, len, &elem_loc);
+    TEST_ASSERT(rc != 0);
+
+    len -= sizeof(struct fcb_disk_area);
+    rc = fcb_append(fcb, len, &elem_loc);
+    TEST_ASSERT(rc != 0);
+
+    len = fcb->f_active.fe_range->fsr_sector_size -
+      (sizeof(struct fcb_disk_area) + 2 + 6);
+    rc = fcb_append(fcb, len, &elem_loc);
+    TEST_ASSERT(rc == 0);
+
+    rc = fcb_append_finish(&elem_loc);
+    TEST_ASSERT(rc == 0);
+
+    rc = fcb_elem_info(&elem_loc);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(elem_loc.fe_data_len == len);
+}
diff --git a/fs/fcb2/test/src/testcases/fcb_test_area_info.c b/fs/fcb2/test/src/testcases/fcb_test_area_info.c
new file mode 100644
index 0000000000..d8dd223fc5
--- /dev/null
+++ b/fs/fcb2/test/src/testcases/fcb_test_area_info.c
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "fcb_test.h"
+
+TEST_CASE(fcb_test_area_info)
+{
+    struct fcb *fcb;
+    int rc;
+    int i;
+    struct fcb_entry loc;
+    uint8_t test_data[128];
+    int elem_cnts[2] = {0, 0};
+    int area_elems[2];
+    int area_bytes[2];
+
+    fcb = &test_fcb;
+
+    /* check that sector outside of range fails */
+    rc = fcb_area_info(fcb, fcb->f_sector_cnt, area_elems, area_bytes);
+    TEST_ASSERT(rc == FCB_ERR_ARGS);
+    rc = fcb_area_info(fcb, fcb->f_sector_cnt + 1, area_elems, area_bytes);
+    TEST_ASSERT(rc == FCB_ERR_ARGS);
+
+    /* output arguments are optional */
+    rc = fcb_area_info(fcb, 0, NULL, NULL);
+    TEST_ASSERT(rc == 0);
+
+    /*
+     * Should be empty, with elem count and byte count zero.
+     */
+    for (i = 0; i < 2; i++) {
+        rc = fcb_area_info(fcb, 0, &area_elems[i],
+                           &area_bytes[i]);
+        TEST_ASSERT(rc == 0);
+        TEST_ASSERT(area_elems[i] == 0);
+        TEST_ASSERT(area_bytes[i] == 0);
+    }
+
+    /*
+     * Fill up the areas, make sure that reporting is ok.
+     */
+    while (1) {
+        rc = fcb_append(fcb, sizeof(test_data), &loc);
+        if (rc == FCB_ERR_NOSPACE) {
+            break;
+        }
+        if (loc.fe_sector == 0) {
+            elem_cnts[0]++;
+        } else if (loc.fe_sector == 1) {
+            elem_cnts[1]++;
+        } else {
+            TEST_ASSERT(0);
+        }
+
+        rc = fcb_write(&loc, 0, test_data, sizeof(test_data));
+        TEST_ASSERT(rc == 0);
+
+        rc = fcb_append_finish(&loc);
+        TEST_ASSERT(rc == 0);
+
+        for (i = 0; i < 2; i++) {
+            rc = fcb_area_info(fcb, i, &area_elems[i],
+                               &area_bytes[i]);
+            TEST_ASSERT(rc == 0);
+            TEST_ASSERT(area_elems[i] == elem_cnts[i]);
+            TEST_ASSERT(area_bytes[i] == elem_cnts[i] * sizeof(test_data));
+        }
+    }
+
+    /*
+     * Wipe out the oldest, should report zeroes for that area now.
+     */
+    rc = fcb_rotate(fcb);
+    TEST_ASSERT(rc == 0);
+
+    rc = fcb_area_info(fcb, 0, &area_elems[0], &area_bytes[0]);
+    TEST_ASSERT(rc == 0);
+    rc = fcb_area_info(fcb, 1, &area_elems[1], &area_bytes[1]);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(area_elems[0] == 0);
+    TEST_ASSERT(area_bytes[0] == 0);
+    TEST_ASSERT(area_elems[1] == elem_cnts[1]);
+    TEST_ASSERT(area_bytes[1] == elem_cnts[1] * sizeof(test_data));
+
+    /*
+     * Test oldest sector should be sector area 1.
+     */
+    rc = fcb_area_info(fcb, FCB_SECTOR_OLDEST, &area_elems[0], &area_bytes[0]);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(area_elems[0] == elem_cnts[1]);
+    TEST_ASSERT(area_bytes[0] == elem_cnts[1] * sizeof(test_data));
+}
diff --git a/fs/fcb2/test/src/testcases/fcb_test_empty_walk.c b/fs/fcb2/test/src/testcases/fcb_test_empty_walk.c
new file mode 100644
index 0000000000..dccf4bfbdd
--- /dev/null
+++ b/fs/fcb2/test/src/testcases/fcb_test_empty_walk.c
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "fcb_test.h"
+
+TEST_CASE(fcb_test_empty_walk)
+{
+    int rc;
+    struct fcb *fcb;
+
+    fcb = &test_fcb;
+
+    rc = fcb_walk(fcb, 0, fcb_test_empty_walk_cb, NULL);
+    TEST_ASSERT(rc == 0);
+}
diff --git a/fs/fcb2/test/src/testcases/fcb_test_init.c b/fs/fcb2/test/src/testcases/fcb_test_init.c
new file mode 100644
index 0000000000..1103ca59b4
--- /dev/null
+++ b/fs/fcb2/test/src/testcases/fcb_test_init.c
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "fcb_test.h"
+
+TEST_CASE(fcb_test_init)
+{
+    int rc;
+    struct fcb *fcb;
+
+    fcb = &test_fcb;
+    memset(fcb, 0, sizeof(*fcb));
+
+    rc = fcb_init(fcb);
+    TEST_ASSERT(rc == FCB_ERR_ARGS);
+
+    fcb->f_ranges = test_fcb_ranges;
+
+    rc = fcb_init(fcb);
+    TEST_ASSERT(rc == FCB_ERR_ARGS);
+
+    fcb->f_sector_cnt = 2;
+    fcb->f_magic = 0x12345678;
+    fcb->f_range_cnt = 1;
+    fcb->f_ranges[0].fsr_flash_area.fa_size = 2 * fcb->f_ranges[0].fsr_sector_size;
+    rc = fcb_init(fcb);
+    TEST_ASSERT(rc == FCB_ERR_MAGIC);
+
+    fcb->f_magic = 0;
+    rc = fcb_init(fcb);
+    TEST_ASSERT(rc == 0);
+}
diff --git a/fs/fcb2/test/src/testcases/fcb_test_last_of_n.c b/fs/fcb2/test/src/testcases/fcb_test_last_of_n.c
new file mode 100644
index 0000000000..cbe489d2c7
--- /dev/null
+++ b/fs/fcb2/test/src/testcases/fcb_test_last_of_n.c
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "fcb_test.h"
+
+TEST_CASE(fcb_test_last_of_n)
+{
+    const uint8_t ENTRIES = 5;
+    struct fcb *fcb;
+    int rc;
+    struct fcb_entry loc;
+    struct fcb_entry areas[ENTRIES];
+    uint8_t test_data[128];
+    uint8_t i;
+
+    fcb = &test_fcb;
+    fcb->f_scratch_cnt = 1;
+
+    /* No fcbs available */
+    rc = fcb_offset_last_n(fcb, 1, &loc);
+    assert (rc != 0);
+
+    /*
+     * Add some fcbs.
+     */
+    for (i = 0; i < ENTRIES; i++) {
+        rc = fcb_append(fcb, sizeof(test_data), &loc);
+        if (rc == FCB_ERR_NOSPACE) {
+            break;
+        }
+
+        rc = fcb_write(&loc, 0, test_data, sizeof(test_data));
+        TEST_ASSERT(rc == 0);
+
+        rc = fcb_append_finish(&loc);
+        TEST_ASSERT(rc == 0);
+
+        areas[i] = loc;
+    }
+
+    /* last entry */
+    rc = fcb_offset_last_n(fcb, 1, &loc);
+    assert (rc == 0);
+    assert (areas[4].fe_sector == loc.fe_sector);
+    assert (areas[4].fe_data_off == loc.fe_data_off);
+    assert (areas[4].fe_data_len == loc.fe_data_len);
+
+    /* somewhere in the middle */
+    rc = fcb_offset_last_n(fcb, 3, &loc);
+    assert (rc == 0);
+    assert (areas[2].fe_sector == loc.fe_sector);
+    assert (areas[2].fe_data_off == loc.fe_data_off);
+    assert (areas[2].fe_data_len == loc.fe_data_len);
+
+    /* first entry */
+    rc = fcb_offset_last_n(fcb, 5, &loc);
+    assert (rc == 0);
+    assert (areas[0].fe_sector == loc.fe_sector);
+    assert (areas[0].fe_data_off == loc.fe_data_off);
+    assert (areas[0].fe_data_len == loc.fe_data_len);
+
+    /* after last valid entry, returns the first one like for 5 */
+    rc = fcb_offset_last_n(fcb, 6, &loc);
+    assert (rc == 0);
+    assert (areas[0].fe_sector == loc.fe_sector);
+    assert (areas[0].fe_data_off == loc.fe_data_off);
+    assert (areas[0].fe_data_len == loc.fe_data_len);
+}
diff --git a/fs/fcb2/test/src/testcases/fcb_test_multiple_scratch.c b/fs/fcb2/test/src/testcases/fcb_test_multiple_scratch.c
new file mode 100644
index 0000000000..6403dbe2aa
--- /dev/null
+++ b/fs/fcb2/test/src/testcases/fcb_test_multiple_scratch.c
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "fcb_test.h"
+
+TEST_CASE(fcb_test_multiple_scratch)
+{
+    struct fcb *fcb;
+    int rc;
+    struct fcb_entry loc;
+    uint8_t test_data[128];
+    int elem_cnts[4];
+    int idx;
+    int cnts[4];
+    struct append_arg aa_arg = {
+        .elem_cnts = cnts
+    };
+
+    fcb = &test_fcb;
+    fcb->f_scratch_cnt = 1;
+
+    /*
+     * Now fill up everything. We should be able to get 3 of the sectors
+     * full.
+     */
+    memset(elem_cnts, 0, sizeof(elem_cnts));
+    while (1) {
+        rc = fcb_append(fcb, sizeof(test_data), &loc);
+        if (rc == FCB_ERR_NOSPACE) {
+            break;
+        }
+        idx = loc.fe_sector;
+        elem_cnts[idx]++;
+
+        rc = fcb_write(&loc, 0, test_data, sizeof(test_data));
+        TEST_ASSERT(rc == 0);
+
+        rc = fcb_append_finish(&loc);
+        TEST_ASSERT(rc == 0);
+    }
+
+    TEST_ASSERT(elem_cnts[0] > 0);
+    TEST_ASSERT(elem_cnts[0] == elem_cnts[1] && elem_cnts[0] == elem_cnts[2]);
+    TEST_ASSERT(elem_cnts[3] == 0);
+
+    /*
+     * Ask to use scratch block, then fill it up.
+     */
+    rc = fcb_append_to_scratch(fcb);
+    TEST_ASSERT(rc == 0);
+
+    while (1) {
+        rc = fcb_append(fcb, sizeof(test_data), &loc);
+        if (rc == FCB_ERR_NOSPACE) {
+            break;
+        }
+        idx = loc.fe_sector;
+        elem_cnts[idx]++;
+
+        rc = fcb_write(&loc, 0, test_data, sizeof(test_data));
+        TEST_ASSERT(rc == 0);
+
+        rc = fcb_append_finish(&loc);
+        TEST_ASSERT(rc == 0);
+    }
+    TEST_ASSERT(elem_cnts[3] == elem_cnts[0]);
+
+    /*
+     * Rotate
+     */
+    rc = fcb_rotate(fcb);
+    TEST_ASSERT(rc == 0);
+
+    memset(&cnts, 0, sizeof(cnts));
+    rc = fcb_walk(fcb, FCB_SECTOR_OLDEST, fcb_test_cnt_elems_cb, &aa_arg);
+    TEST_ASSERT(rc == 0);
+
+    TEST_ASSERT(cnts[0] == 0);
+    TEST_ASSERT(cnts[1] > 0);
+    TEST_ASSERT(cnts[1] == cnts[2] && cnts[1] == cnts[3]);
+
+    rc = fcb_append_to_scratch(fcb);
+    TEST_ASSERT(rc == 0);
+    rc = fcb_append_to_scratch(fcb);
+    TEST_ASSERT(rc != 0);
+}
diff --git a/fs/fcb2/test/src/testcases/fcb_test_reset.c b/fs/fcb2/test/src/testcases/fcb_test_reset.c
new file mode 100644
index 0000000000..706245d486
--- /dev/null
+++ b/fs/fcb2/test/src/testcases/fcb_test_reset.c
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "fcb_test.h"
+
+TEST_CASE(fcb_test_reset)
+{
+    struct fcb *fcb;
+    int rc;
+    int i;
+    struct fcb_entry loc;
+    uint8_t test_data[128];
+    int var_cnt;
+
+    fcb = &test_fcb;
+
+    var_cnt = 0;
+    rc = fcb_walk(fcb, FCB_SECTOR_OLDEST, fcb_test_data_walk_cb, &var_cnt);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(var_cnt == 0);
+
+    rc = fcb_append(fcb, 32, &loc);
+    TEST_ASSERT(rc == 0);
+
+    /*
+     * No ready ones yet. CRC should not match.
+     */
+    var_cnt = 0;
+    rc = fcb_walk(fcb, FCB_SECTOR_OLDEST, fcb_test_data_walk_cb, &var_cnt);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(var_cnt == 0);
+
+    for (i = 0; i < sizeof(test_data); i++) {
+        test_data[i] = fcb_test_append_data(32, i);
+    }
+    rc = fcb_write(&loc, 0, test_data, 32);
+    TEST_ASSERT(rc == 0);
+
+    rc = fcb_append_finish(&loc);
+    TEST_ASSERT(rc == 0);
+
+    /*
+     * one entry
+     */
+    var_cnt = 32;
+    rc = fcb_walk(fcb, FCB_SECTOR_OLDEST, fcb_test_data_walk_cb, &var_cnt);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(var_cnt == 33);
+
+    /*
+     * Pretend reset
+     */
+    memset(fcb, 0, sizeof(*fcb));
+    fcb->f_range_cnt = 1;
+    fcb->f_sector_cnt = 2;
+    fcb->f_ranges = test_fcb_ranges;
+
+    rc = fcb_init(fcb);
+    TEST_ASSERT(rc == 0);
+
+    var_cnt = 32;
+    rc = fcb_walk(fcb, FCB_SECTOR_OLDEST, fcb_test_data_walk_cb, &var_cnt);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(var_cnt == 33);
+
+    rc = fcb_append(fcb, 33, &loc);
+    TEST_ASSERT(rc == 0);
+
+    for (i = 0; i < sizeof(test_data); i++) {
+        test_data[i] = fcb_test_append_data(33, i);
+    }
+    rc = fcb_write(&loc, 0, test_data, 33);
+    TEST_ASSERT(rc == 0);
+
+    rc = fcb_append_finish(&loc);
+    TEST_ASSERT(rc == 0);
+
+    var_cnt = 32;
+    rc = fcb_walk(fcb, FCB_SECTOR_OLDEST, fcb_test_data_walk_cb, &var_cnt);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(var_cnt == 34);
+
+    /*
+     * Add partial one, make sure that we survive reset then.
+     */
+    rc = fcb_append(fcb, 34, &loc);
+    TEST_ASSERT(rc == 0);
+
+    memset(fcb, 0, sizeof(*fcb));
+    fcb->f_range_cnt = 1;
+    fcb->f_sector_cnt = 2;
+    fcb->f_ranges = test_fcb_ranges;
+
+    rc = fcb_init(fcb);
+    TEST_ASSERT(rc == 0);
+
+    /*
+     * Walk should skip that.
+     */
+    var_cnt = 32;
+    rc = fcb_walk(fcb, FCB_SECTOR_OLDEST, fcb_test_data_walk_cb, &var_cnt);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(var_cnt == 34);
+
+    /* Add a 3rd one, should go behind corrupt entry */
+    rc = fcb_append(fcb, 34, &loc);
+    TEST_ASSERT(rc == 0);
+
+    for (i = 0; i < sizeof(test_data); i++) {
+        test_data[i] = fcb_test_append_data(34, i);
+    }
+    rc = fcb_write(&loc, 0, test_data, 34);
+    TEST_ASSERT(rc == 0);
+
+    rc = fcb_append_finish(&loc);
+    TEST_ASSERT(rc == 0);
+
+    /*
+     * Walk should skip corrupt entry, but report the next one.
+     */
+    var_cnt = 32;
+    rc = fcb_walk(fcb, FCB_SECTOR_OLDEST, fcb_test_data_walk_cb, &var_cnt);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(var_cnt == 35);
+}
diff --git a/fs/fcb2/test/src/testcases/fcb_test_rotate.c b/fs/fcb2/test/src/testcases/fcb_test_rotate.c
new file mode 100644
index 0000000000..ac1b4c6008
--- /dev/null
+++ b/fs/fcb2/test/src/testcases/fcb_test_rotate.c
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "fcb_test.h"
+
+TEST_CASE(fcb_test_rotate)
+{
+    struct fcb *fcb;
+    int rc;
+    int old_id;
+    struct fcb_entry loc;
+    uint8_t test_data[128];
+    int elem_cnts[2] = {0, 0};
+    int cnts[2];
+    struct append_arg aa_arg = {
+        .elem_cnts = cnts
+    };
+
+    fcb = &test_fcb;
+
+    old_id = fcb->f_active_id;
+    rc = fcb_rotate(fcb);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(fcb->f_active_id == old_id + 1);
+
+    /*
+     * Now fill up the
+     */
+    while (1) {
+        rc = fcb_append(fcb, sizeof(test_data), &loc);
+        if (rc == FCB_ERR_NOSPACE) {
+            break;
+        }
+        if (loc.fe_sector == 0) {
+            elem_cnts[0]++;
+        } else if (loc.fe_sector == 1) {
+            elem_cnts[1]++;
+        } else {
+            TEST_ASSERT(0);
+        }
+
+        rc = fcb_write(&loc, 0, test_data, sizeof(test_data));
+        TEST_ASSERT(rc == 0);
+
+        rc = fcb_append_finish(&loc);
+        TEST_ASSERT(rc == 0);
+    }
+    TEST_ASSERT(elem_cnts[0] > 0 && elem_cnts[0] == elem_cnts[1]);
+
+    old_id = fcb->f_active_id;
+    rc = fcb_rotate(fcb);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(fcb->f_active_id == old_id); /* no new area created */
+
+    memset(cnts, 0, sizeof(cnts));
+    rc = fcb_walk(fcb, FCB_SECTOR_OLDEST, fcb_test_cnt_elems_cb, &aa_arg);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(aa_arg.elem_cnts[0] == elem_cnts[0] ||
+      aa_arg.elem_cnts[1] == elem_cnts[1]);
+    TEST_ASSERT(aa_arg.elem_cnts[0] == 0 || aa_arg.elem_cnts[1] == 0);
+
+    /*
+     * One sector is full. The other one should have one entry in it.
+     */
+    rc = fcb_append(fcb, sizeof(test_data), &loc);
+    TEST_ASSERT(rc == 0);
+
+    rc = fcb_write(&loc, 0, test_data,
+      sizeof(test_data));
+    TEST_ASSERT(rc == 0);
+
+    rc = fcb_append_finish(&loc);
+    TEST_ASSERT(rc == 0);
+
+    old_id = fcb->f_active_id;
+    rc = fcb_rotate(fcb);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(fcb->f_active_id == old_id);
+
+    memset(cnts, 0, sizeof(cnts));
+    rc = fcb_walk(fcb, FCB_SECTOR_OLDEST, fcb_test_cnt_elems_cb, &aa_arg);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(aa_arg.elem_cnts[0] == 1 || aa_arg.elem_cnts[1] == 1);
+    TEST_ASSERT(aa_arg.elem_cnts[0] == 0 || aa_arg.elem_cnts[1] == 0);
+}
diff --git a/sys/flash_map/include/flash_map/flash_map.h b/sys/flash_map/include/flash_map/flash_map.h
index 4590cc43c6..4cc5b42b90 100644
--- a/sys/flash_map/include/flash_map/flash_map.h
+++ b/sys/flash_map/include/flash_map/flash_map.h
@@ -52,6 +52,15 @@ struct flash_area {
     uint32_t fa_size;
 };
 
+struct flash_sector_range {
+    struct flash_area fsr_flash_area;
+    uint32_t fsr_range_start;
+    uint16_t fsr_first_sector;
+    uint16_t fsr_sector_count;
+    uint32_t fsr_sector_size;
+    uint8_t fsr_align;
+};
+
 extern const struct flash_area *flash_map;
 extern int flash_map_entries;
 
@@ -106,6 +115,12 @@ uint32_t flash_area_erased_val(const struct flash_area *fa);
  */
 int flash_area_to_sectors(int idx, int *cnt, struct flash_area *ret);
 
+/*
+ * Given flash map area id, return info about sectors within the area.
+ */
+int flash_area_to_sector_ranges(int id, int *cnt,
+  struct flash_sector_range *ret);
+
 /*
  * Get-next interface for obtaining info about sectors.
  * To start the get-next walk, call with *sec_id set to -1.
diff --git a/sys/flash_map/src/flash_map.c b/sys/flash_map/src/flash_map.c
index 1a10c546a8..9575aa540f 100644
--- a/sys/flash_map/src/flash_map.c
+++ b/sys/flash_map/src/flash_map.c
@@ -93,6 +93,93 @@ flash_area_to_sectors(int id, int *cnt, struct flash_area *ret)
     return 0;
 }
 
+static inline int
+flash_range_end(const struct flash_sector_range *range)
+{
+    return range->fsr_flash_area.fa_off +
+        range->fsr_sector_count * range->fsr_sector_size;
+}
+
+int
+flash_area_to_sector_ranges(int id, int *cnt, struct flash_sector_range *ret)
+{
+    const struct flash_area *fa;
+    const struct hal_flash *hf;
+    struct flash_sector_range sr = { 0 };
+    struct flash_sector_range *current;
+    uint32_t start;      /* Sector start in flash */
+    uint32_t size;       /* Sector size */
+    uint32_t offset = 0; /* Address inside flash area */
+    int rc;
+    int i;
+    int allowed_ranges = UINT16_MAX;
+    int range_count = 0;
+    int sector_in_ranges = 0;
+
+    rc = flash_area_open(id, &fa);
+    if (rc != 0) {
+        return rc;
+    }
+
+    /* Respect maximum number of allowed ranges if specified and
+     * ret was given. Otherwise count required number of sector
+     * ranges.
+     */
+    if (*cnt > 0 && ret != NULL) {
+        allowed_ranges = *cnt;
+    }
+    if (ret) {
+        current = ret;
+    } else {
+        current = &sr;
+    }
+
+    hf = hal_bsp_flash_dev(fa->fa_device_id);
+    for (i = 0; i < hf->hf_sector_cnt; i++) {
+        hf->hf_itf->hff_sector_info(hf, i, &start, &size);
+        if (start >= fa->fa_off && start < fa->fa_off + fa->fa_size) {
+            if (range_count) {
+                /*
+                 * Extend range if sector is adjacent to previous one.
+                 */
+                if (flash_range_end(current) == start &&
+                    current->fsr_sector_size == size) {
+                    current->fsr_flash_area.fa_size += size;
+                    offset += size;
+                    current->fsr_sector_count++;
+                    sector_in_ranges++;
+                    continue;
+                } else if (ret != NULL) {
+                    if (range_count < allowed_ranges) {
+                        current = ++ret;
+                    } else {
+                        /* Found non-adjacent sector, but there is no
+                         * space for it, just stop looking and return
+                         * what was already found.
+                         */
+                        break;
+                    }
+                }
+            }
+            /* New sector range */
+            range_count++;
+            current->fsr_flash_area.fa_device_id = fa->fa_device_id;
+            current->fsr_flash_area.fa_id = id;
+            current->fsr_flash_area.fa_off = start;
+            current->fsr_flash_area.fa_size = size;
+            current->fsr_sector_size = size;
+            current->fsr_sector_count = 1;
+            current->fsr_first_sector = (uint16_t)sector_in_ranges;
+            current->fsr_range_start = offset;
+            current->fsr_align = hal_flash_align(fa->fa_device_id);
+        }
+    }
+    *cnt = range_count;
+
+    flash_area_close(fa);
+    return 0;
+}
+
 int
 flash_area_getnext_sector(int id, int *sec_id, struct flash_area *ret)
 {


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services