You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by ma...@apache.org on 2016/03/21 17:29:35 UTC

[1/2] incubator-mynewt-core git commit: HAL flash, add api to query what the alignment restriction for a given flash_area is.

Repository: incubator-mynewt-core
Updated Branches:
  refs/heads/develop 8b183c1c3 -> 6a12536f6


HAL flash, add api to query what the alignment restriction for a
given flash_area is.


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/7c6b7500
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/7c6b7500
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/7c6b7500

Branch: refs/heads/develop
Commit: 7c6b7500cc8a6366bdc8999211769701ae4685ee
Parents: 8b183c1
Author: Marko Kiiskila <ma...@runtime.io>
Authored: Mon Mar 21 09:24:52 2016 -0700
Committer: Marko Kiiskila <ma...@runtime.io>
Committed: Mon Mar 21 09:24:52 2016 -0700

----------------------------------------------------------------------
 hw/hal/include/hal/flash_map.h | 5 +++++
 hw/hal/src/flash_map.c         | 6 ++++++
 2 files changed, 11 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/7c6b7500/hw/hal/include/hal/flash_map.h
----------------------------------------------------------------------
diff --git a/hw/hal/include/hal/flash_map.h b/hw/hal/include/hal/flash_map.h
index a75c54d..426a549 100644
--- a/hw/hal/include/hal/flash_map.h
+++ b/hw/hal/include/hal/flash_map.h
@@ -78,6 +78,11 @@ int flash_area_write(const struct flash_area *, uint32_t off, void *src,
 int flash_area_erase(const struct flash_area *, uint32_t off, uint32_t len);
 
 /*
+ * Alignment restriction for flash writes.
+ */
+uint8_t flash_area_align(const struct flash_area *);
+
+/*
  * Given flash map index, return info about sectors within the area.
  */
 int flash_area_to_sectors(int idx, int *cnt, struct flash_area *ret);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/7c6b7500/hw/hal/src/flash_map.c
----------------------------------------------------------------------
diff --git a/hw/hal/src/flash_map.c b/hw/hal/src/flash_map.c
index f2f8016..8cae5cf 100644
--- a/hw/hal/src/flash_map.c
+++ b/hw/hal/src/flash_map.c
@@ -180,3 +180,9 @@ flash_area_erase(const struct flash_area *fa, uint32_t off, uint32_t len)
     }
     return hal_flash_erase(fa->fa_flash_id, fa->fa_off + off, len);
 }
+
+uint8_t
+flash_area_align(const struct flash_area *fa)
+{
+    return hal_flash_align(fa->fa_flash_id);
+}


[2/2] incubator-mynewt-core git commit: Flash circular buffer.

Posted by ma...@apache.org.
Flash circular buffer.


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/6a12536f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/6a12536f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/6a12536f

Branch: refs/heads/develop
Commit: 6a12536f62b93f3d8c2aa480ab0f6de58b348f61
Parents: 7c6b750
Author: Marko Kiiskila <ma...@runtime.io>
Authored: Mon Mar 21 09:28:56 2016 -0700
Committer: Marko Kiiskila <ma...@runtime.io>
Committed: Mon Mar 21 09:28:56 2016 -0700

----------------------------------------------------------------------
 sys/fcb/README.md           |  58 ++++
 sys/fcb/include/fcb/fcb.h   |  96 +++++++
 sys/fcb/pkg.yml             |  23 ++
 sys/fcb/src/fcb.c           | 172 ++++++++++++
 sys/fcb/src/fcb_append.c    | 106 ++++++++
 sys/fcb/src/fcb_elem_info.c |  99 +++++++
 sys/fcb/src/fcb_getnext.c   | 132 +++++++++
 sys/fcb/src/fcb_priv.h      |  60 +++++
 sys/fcb/src/fcb_rotate.c    |  56 ++++
 sys/fcb/src/fcb_walk.c      |  53 ++++
 sys/fcb/src/test/fcb_test.c | 562 +++++++++++++++++++++++++++++++++++++++
 11 files changed, 1417 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a12536f/sys/fcb/README.md
----------------------------------------------------------------------
diff --git a/sys/fcb/README.md b/sys/fcb/README.md
new file mode 100644
index 0000000..5d13ccd
--- /dev/null
+++ b/sys/fcb/README.md
@@ -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.
+#
+-->
+
+# 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_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 flash_area_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 flash_area_read(),
+   call fcb_rotate() when all elements from a given sector have been read

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a12536f/sys/fcb/include/fcb/fcb.h
----------------------------------------------------------------------
diff --git a/sys/fcb/include/fcb/fcb.h b/sys/fcb/include/fcb/fcb.h
new file mode 100644
index 0000000..50bbeeb
--- /dev/null
+++ b/sys/fcb/include/fcb/fcb.h
@@ -0,0 +1,96 @@
+/**
+ * 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_
+
+/*
+ * Flash circular buffer.
+ */
+#include <inttypes.h>
+#include <limits.h>
+
+#include <hal/flash_map.h>
+
+#include <os/os_mutex.h>
+
+#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 */
+    uint32_t fe_elem_off;	/* start of entry */
+    uint32_t fe_data_off;	/* start of data */
+    uint16_t fe_data_len;	/* size of data area */
+};
+
+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 */
+    struct flash_area *f_sectors; /* Array of sectors, must be contiguous */
+    int f_sector_cnt;		/* Number of elements in aforementioned array */
+
+    /* Flash circular buffer internal state */
+    struct os_mutex f_mtx;	/* Locking for accessing the FCB data */
+    struct flash_area *f_oldest;
+    struct fcb_entry f_active;
+    uint16_t f_active_id;
+    uint8_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
+
+int fcb_init(struct fcb *fcb);
+
+/*
+ * 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 *, uint16_t len, struct fcb_entry *loc);
+int fcb_append_finish(struct 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 *, struct flash_area *, fcb_walk_cb cb, void *cb_arg);
+int fcb_getnext(struct fcb *, struct fcb_entry *loc);
+
+int fcb_rotate(struct fcb *);
+
+#endif /* __SYS_FLASHVAR_H_ */

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a12536f/sys/fcb/pkg.yml
----------------------------------------------------------------------
diff --git a/sys/fcb/pkg.yml b/sys/fcb/pkg.yml
new file mode 100644
index 0000000..5f8f190
--- /dev/null
+++ b/sys/fcb/pkg.yml
@@ -0,0 +1,23 @@
+#
+# 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: sys/fcb
+pkg.deps:
+    - libs/os
+    - libs/testutil

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a12536f/sys/fcb/src/fcb.c
----------------------------------------------------------------------
diff --git a/sys/fcb/src/fcb.c b/sys/fcb/src/fcb.c
new file mode 100644
index 0000000..6efe928
--- /dev/null
+++ b/sys/fcb/src/fcb.c
@@ -0,0 +1,172 @@
+/**
+ * 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"
+
+int
+fcb_init(struct fcb *fcb)
+{
+    struct flash_area *fap;
+    int rc;
+    int i;
+    int max_align = 1;
+    int align;
+    int oldest = -1, newest;
+    struct flash_area *oldest_fap, *newest_fap;
+    struct fcb_disk_area fda;
+
+    if (!fcb->f_sectors || fcb->f_sector_cnt < 2) {
+        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) {
+            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;
+        } else if (FCB_ID_GT(oldest, fda.fd_id)) {
+            oldest = fda.fd_id;
+        }
+    }
+    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) {
+            break;
+        }
+    }
+    os_mutex_init(&fcb->f_mtx);
+    return FCB_OK;
+}
+
+/*
+ * 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(uint8_t *buf, uint16_t len)
+{
+    if (len < CHAR_MAX) {
+        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(uint8_t *buf, uint16_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, uint16_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;
+    }
+    return 1;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a12536f/sys/fcb/src/fcb_append.c
----------------------------------------------------------------------
diff --git a/sys/fcb/src/fcb_append.c b/sys/fcb/src/fcb_append.c
new file mode 100644
index 0000000..c65aa8a
--- /dev/null
+++ b/sys/fcb/src/fcb_append.c
@@ -0,0 +1,106 @@
+/**
+ * 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"
+
+static struct flash_area *
+fcb_new_area(struct fcb *fcb)
+{
+    struct flash_area *fa;
+
+    fa = fcb_getnext_area(fcb, fcb->f_active.fe_area);
+    if (fa == fcb->f_oldest) {
+        return NULL;
+    }
+    return fa;
+}
+
+int
+fcb_append(struct fcb *fcb, uint16_t len, struct fcb_entry *append_loc)
+{
+    struct fcb_entry *active;
+    struct flash_area *fa;
+    uint8_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 = os_mutex_pend(&fcb->f_mtx, OS_WAIT_FOREVER);
+    if (rc != OS_NOT_STARTED) {
+        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);
+        if (!fa || (fa->fa_size <
+            sizeof(struct fcb_disk_area) + len + cnt)) {
+            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++;
+    }
+
+    rc = flash_area_write(active->fe_area, active->fe_elem_off, tmp_str, cnt);
+    if (rc) {
+        os_mutex_release(&fcb->f_mtx);
+        return FCB_ERR_FLASH;
+    }
+    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;
+
+    os_mutex_release(&fcb->f_mtx);
+
+    return FCB_OK;
+}
+
+int
+fcb_append_finish(struct fcb *fcb, struct fcb_entry *loc)
+{
+    int rc;
+    uint8_t crc8;
+    uint32_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;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a12536f/sys/fcb/src/fcb_elem_info.c
----------------------------------------------------------------------
diff --git a/sys/fcb/src/fcb_elem_info.c b/sys/fcb/src/fcb_elem_info.c
new file mode 100644
index 0000000..2fc9428
--- /dev/null
+++ b/sys/fcb/src/fcb_elem_info.c
@@ -0,0 +1,99 @@
+/**
+ * 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"
+
+/*
+ * 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, uint8_t *c8p)
+{
+    uint8_t tmp_str[FCB_TMP_BUF_SZ];
+    int cnt;
+    int blk_sz;
+    uint8_t crc8;
+    uint16_t len;
+    uint32_t off;
+    uint32_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_init();
+    crc8 = crc8_calc(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_calc(crc8, tmp_str, blk_sz);
+    }
+    *c8p = crc8;
+
+    return 0;
+}
+
+int
+fcb_elem_info(struct fcb *fcb, struct fcb_entry *loc)
+{
+    int rc;
+    uint8_t crc8;
+    uint8_t fl_crc8;
+    uint32_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;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a12536f/sys/fcb/src/fcb_getnext.c
----------------------------------------------------------------------
diff --git a/sys/fcb/src/fcb_getnext.c b/sys/fcb/src/fcb_getnext.c
new file mode 100644
index 0000000..7cd903b
--- /dev/null
+++ b/sys/fcb/src/fcb_getnext.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 <stddef.h>
+
+#include "fcb/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 = os_mutex_pend(&fcb->f_mtx, OS_WAIT_FOREVER);
+    if (rc != OS_NOT_STARTED) {
+        return FCB_ERR_ARGS;
+    }
+    rc = fcb_getnext_nolock(fcb, loc);
+    os_mutex_release(&fcb->f_mtx);
+
+    return rc;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a12536f/sys/fcb/src/fcb_priv.h
----------------------------------------------------------------------
diff --git a/sys/fcb/src/fcb_priv.h b/sys/fcb/src/fcb_priv.h
new file mode 100644
index 0000000..8aaac4d
--- /dev/null
+++ b/sys/fcb/src/fcb_priv.h
@@ -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.
+ */
+#ifndef __SYS_FCB_PRIV_H_
+#define __SYS_FCB_PRIV_H_
+
+#define FCB_CRC_SZ	sizeof(uint8_t)
+#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;
+};
+
+int fcb_put_len(uint8_t *buf, uint16_t len);
+int fcb_get_len(uint8_t *buf, uint16_t *len);
+
+static inline int
+fcb_len_in_flash(struct fcb *fcb, uint16_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 *, struct fcb_entry *);
+int fcb_elem_crc8(struct fcb *, struct fcb_entry *loc, uint8_t *crc8p);
+
+int fcb_sector_hdr_init(struct fcb *, struct flash_area *fap, uint16_t id);
+int fcb_sector_hdr_read(struct fcb *, struct flash_area *fap,
+  struct fcb_disk_area *fdap);
+
+#define crc8_init() 0
+#define crc8_calc(a,b,c) 0
+
+#endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a12536f/sys/fcb/src/fcb_rotate.c
----------------------------------------------------------------------
diff --git a/sys/fcb/src/fcb_rotate.c b/sys/fcb/src/fcb_rotate.c
new file mode 100644
index 0000000..77af31d
--- /dev/null
+++ b/sys/fcb/src/fcb_rotate.c
@@ -0,0 +1,56 @@
+/**
+ * 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)
+{
+    struct flash_area *fap;
+    int rc = 0;
+
+    rc = os_mutex_pend(&fcb->f_mtx, OS_WAIT_FOREVER);
+    if (rc != OS_NOT_STARTED) {
+        return FCB_ERR_ARGS;
+    }
+
+    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++;
+    }
+    rc = flash_area_erase(fcb->f_oldest, 0, fcb->f_oldest->fa_size);
+    if (rc) {
+        rc = FCB_ERR_FLASH;
+        goto out;
+    }
+    fcb->f_oldest = fcb_getnext_area(fcb, fcb->f_oldest);
+out:
+    os_mutex_release(&fcb->f_mtx);
+    return rc;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a12536f/sys/fcb/src/fcb_walk.c
----------------------------------------------------------------------
diff --git a/sys/fcb/src/fcb_walk.c b/sys/fcb/src/fcb_walk.c
new file mode 100644
index 0000000..a4e7320
--- /dev/null
+++ b/sys/fcb/src/fcb_walk.c
@@ -0,0 +1,53 @@
+/**
+ * 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"
+
+/*
+ * 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 = os_mutex_pend(&fcb->f_mtx, OS_WAIT_FOREVER);
+    if (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 (fap && loc.fe_area != fap) {
+            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;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a12536f/sys/fcb/src/test/fcb_test.c
----------------------------------------------------------------------
diff --git a/sys/fcb/src/test/fcb_test.c b/sys/fcb/src/test/fcb_test.c
new file mode 100644
index 0000000..c4bc558
--- /dev/null
+++ b/sys/fcb/src/test/fcb_test.c
@@ -0,0 +1,562 @@
+/**
+ * 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/os.h>
+#include <testutil/testutil.h>
+
+#include "fcb/fcb.h"
+#include "fcb/../../src/fcb_priv.h"
+
+static struct fcb test_fcb;
+
+static struct flash_area test_fcb_area[] = {
+    [0] = {
+        .fa_flash_id = 0,
+        .fa_off = 0,
+        .fa_size = 0x4000, /* 16K */
+    },
+    [1] = {
+        .fa_flash_id = 0,
+        .fa_off = 0x4000,
+        .fa_size = 0x4000
+    }
+};
+
+static void
+fcb_test_wipe(void)
+{
+    int i;
+    int rc;
+    struct flash_area *fap;
+
+    for (i = 0; i < sizeof(test_fcb_area) / sizeof(test_fcb_area[0]); i++) {
+        fap = &test_fcb_area[i];
+        rc = flash_area_erase(fap, 0, fap->fa_size);
+        TEST_ASSERT(rc == 0);
+    }
+}
+
+TEST_CASE(fcb_test_len)
+{
+    uint8_t buf[3];
+    uint16_t len;
+    uint16_t len2;
+    int rc;
+
+    for (len = 0; len < FCB_MAX_LEN; len++) {
+        rc = fcb_put_len(buf, len);
+        TEST_ASSERT(rc == 1 || rc == 2);
+
+        rc = fcb_get_len(buf, &len2);
+        TEST_ASSERT(rc == 1 || rc == 2);
+
+        TEST_ASSERT(len == len2);
+    }
+}
+
+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_sectors = test_fcb_area;
+    fcb->f_sector_cnt = 1;
+
+    rc = fcb_init(fcb);
+    TEST_ASSERT(rc == FCB_ERR_ARGS);
+
+    fcb->f_sector_cnt = 2;
+    rc = fcb_init(fcb);
+    TEST_ASSERT(rc == 0);
+}
+
+static int
+fcb_test_empty_walk_cb(struct fcb_entry *loc, void *arg)
+{
+    TEST_ASSERT(0);
+    return 0;
+}
+
+TEST_CASE(fcb_test_empty_walk)
+{
+    int rc;
+    struct fcb *fcb;
+
+    fcb_test_wipe();
+    fcb = &test_fcb;
+    memset(fcb, 0, sizeof(*fcb));
+
+    fcb->f_sector_cnt = 2;
+    fcb->f_sectors = test_fcb_area;
+
+    rc = fcb_init(fcb);
+    TEST_ASSERT(rc == 0);
+
+    rc = fcb_walk(fcb, 0, fcb_test_empty_walk_cb, NULL);
+    TEST_ASSERT(rc == 0);
+}
+
+static uint8_t
+fcb_test_append_data(int msg_len, int off)
+{
+    return (msg_len ^ off);
+}
+
+static 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 = flash_area_read(loc->fe_area, loc->fe_data_off, 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;
+}
+
+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_wipe();
+    fcb = &test_fcb;
+    memset(fcb, 0, sizeof(*fcb));
+    fcb->f_sector_cnt = 2;
+    fcb->f_sectors = test_fcb_area;
+
+    rc = fcb_init(fcb);
+    TEST_ASSERT(rc == 0);
+
+    for (i = 0; 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 = flash_area_write(loc.fe_area, loc.fe_data_off, test_data, i);
+        TEST_ASSERT(rc == 0);
+        rc = fcb_append_finish(fcb, &loc);
+        TEST_ASSERT(rc == 0);
+    }
+
+    var_cnt = 0;
+    rc = fcb_walk(fcb, 0, fcb_test_data_walk_cb, &var_cnt);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(var_cnt == sizeof(test_data));
+}
+
+TEST_CASE(fcb_test_append_too_big)
+{
+    struct fcb *fcb;
+    int rc;
+    int len;
+    struct fcb_entry elem_loc;
+
+    fcb_test_wipe();
+    fcb = &test_fcb;
+    memset(fcb, 0, sizeof(*fcb));
+    fcb->f_sector_cnt = 2;
+    fcb->f_sectors = test_fcb_area;
+
+    rc = fcb_init(fcb);
+    TEST_ASSERT(rc == 0);
+
+    /*
+     * Max element which fits inside sector is
+     * sector size - (disk header + crc + 1-2 bytes of length).
+     */
+    len = fcb->f_active.fe_area->fa_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_area->fa_size -
+      (sizeof(struct fcb_disk_area) + 1 + 2);
+    rc = fcb_append(fcb, len, &elem_loc);
+    TEST_ASSERT(rc == 0);
+
+    rc = fcb_append_finish(fcb, &elem_loc);
+    TEST_ASSERT(rc == 0);
+
+    rc = fcb_elem_info(fcb, &elem_loc);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(elem_loc.fe_data_len == len);
+}
+
+struct append_arg {
+    int elem_cnts[2];
+};
+
+static int
+fcb_test_append_fill_walk_cb(struct fcb_entry *loc, void *arg)
+{
+    struct append_arg *aa = (struct append_arg *)arg;
+
+    if (loc->fe_area == &test_fcb_area[0]) {
+        aa->elem_cnts[0]++;
+    } else if (loc->fe_area == &test_fcb_area[1]) {
+        aa->elem_cnts[1]++;
+    }
+    return 0;
+}
+
+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};
+    struct append_arg aa_together;
+    struct append_arg aa_separate;
+
+    fcb_test_wipe();
+    fcb = &test_fcb;
+    memset(fcb, 0, sizeof(*fcb));
+    fcb->f_sector_cnt = 2;
+    fcb->f_sectors = test_fcb_area;
+
+    rc = fcb_init(fcb);
+    TEST_ASSERT(rc == 0);
+
+    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_area == &test_fcb_area[0]) {
+            elem_cnts[0]++;
+        } else if (loc.fe_area == &test_fcb_area[1]) {
+            elem_cnts[1]++;
+        } else {
+            TEST_ASSERT(0);
+        }
+
+        rc = flash_area_write(loc.fe_area, loc.fe_data_off, test_data,
+          sizeof(test_data));
+        TEST_ASSERT(rc == 0);
+
+        rc = fcb_append_finish(fcb, &loc);
+        TEST_ASSERT(rc == 0);
+    }
+    TEST_ASSERT(elem_cnts[0] > 0);
+    TEST_ASSERT(elem_cnts[0] == elem_cnts[1]);
+
+    memset(&aa_together, 0, sizeof(aa_together));
+    rc = fcb_walk(fcb, NULL, fcb_test_append_fill_walk_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, 0, sizeof(aa_separate));
+    rc = fcb_walk(fcb, &test_fcb_area[0], fcb_test_append_fill_walk_cb,
+      &aa_separate);
+    TEST_ASSERT(rc == 0);
+    rc = fcb_walk(fcb, &test_fcb_area[1], fcb_test_append_fill_walk_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]);
+
+}
+
+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_wipe();
+    fcb = &test_fcb;
+    memset(fcb, 0, sizeof(*fcb));
+    fcb->f_sector_cnt = 2;
+    fcb->f_sectors = test_fcb_area;
+
+    rc = fcb_init(fcb);
+    TEST_ASSERT(rc == 0);
+
+    var_cnt = 0;
+    rc = fcb_walk(fcb, 0, 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, 0, 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 = flash_area_write(loc.fe_area, loc.fe_data_off, test_data, 32);
+    TEST_ASSERT(rc == 0);
+
+    rc = fcb_append_finish(fcb, &loc);
+    TEST_ASSERT(rc == 0);
+
+    /*
+     * one entry
+     */
+    var_cnt = 32;
+    rc = fcb_walk(fcb, 0, 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_sector_cnt = 2;
+    fcb->f_sectors = test_fcb_area;
+
+    rc = fcb_init(fcb);
+    TEST_ASSERT(rc == 0);
+
+    var_cnt = 32;
+    rc = fcb_walk(fcb, 0, 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 = flash_area_write(loc.fe_area, loc.fe_data_off, test_data, 33);
+    TEST_ASSERT(rc == 0);
+
+    rc = fcb_append_finish(fcb, &loc);
+    TEST_ASSERT(rc == 0);
+
+    var_cnt = 32;
+    rc = fcb_walk(fcb, 0, 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_sector_cnt = 2;
+    fcb->f_sectors = test_fcb_area;
+
+    rc = fcb_init(fcb);
+    TEST_ASSERT(rc == 0);
+
+    /*
+     * Walk should skip that.
+     */
+    var_cnt = 32;
+    rc = fcb_walk(fcb, 0, 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 = flash_area_write(loc.fe_area, loc.fe_data_off, test_data, 34);
+    TEST_ASSERT(rc == 0);
+
+    rc = fcb_append_finish(fcb, &loc);
+    TEST_ASSERT(rc == 0);
+
+    /*
+     * Walk should skip corrupt entry, but report the next one.
+     */
+    var_cnt = 32;
+    rc = fcb_walk(fcb, 0, fcb_test_data_walk_cb, &var_cnt);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(var_cnt == 35);
+}
+
+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};
+    struct append_arg aa_arg;
+
+    fcb_test_wipe();
+    fcb = &test_fcb;
+    memset(fcb, 0, sizeof(*fcb));
+    fcb->f_sector_cnt = 2;
+    fcb->f_sectors = test_fcb_area;
+
+    rc = fcb_init(fcb);
+    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 + 1);
+
+    /*
+     * Now fill up the
+     */
+    while (1) {
+        rc = fcb_append(fcb, sizeof(test_data), &loc);
+        if (rc == FCB_ERR_NOSPACE) {
+            break;
+        }
+        if (loc.fe_area == &test_fcb_area[0]) {
+            elem_cnts[0]++;
+        } else if (loc.fe_area == &test_fcb_area[1]) {
+            elem_cnts[1]++;
+        } else {
+            TEST_ASSERT(0);
+        }
+
+        rc = flash_area_write(loc.fe_area, loc.fe_data_off, test_data,
+          sizeof(test_data));
+        TEST_ASSERT(rc == 0);
+
+        rc = fcb_append_finish(fcb, &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(&aa_arg, 0, sizeof(aa_arg));
+    rc = fcb_walk(fcb, NULL, fcb_test_append_fill_walk_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 = flash_area_write(loc.fe_area, loc.fe_data_off, test_data,
+      sizeof(test_data));
+    TEST_ASSERT(rc == 0);
+
+    rc = fcb_append_finish(fcb, &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(&aa_arg, 0, sizeof(aa_arg));
+    rc = fcb_walk(fcb, NULL, fcb_test_append_fill_walk_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);
+}
+
+TEST_SUITE(fcb_test_all)
+{
+    fcb_test_len();
+
+    fcb_test_init();
+
+    fcb_test_empty_walk();
+
+    fcb_test_append();
+
+    fcb_test_append_too_big();
+
+    fcb_test_append_fill();
+
+    fcb_test_reset();
+
+    fcb_test_rotate();
+}
+
+#ifdef MYNEWT_SELFTEST
+
+int
+main(int argc, char **argv)
+{
+    tu_config.tc_print_results = 1;
+    tu_init();
+
+    fcb_test_all();
+
+    return tu_any_failed;
+}
+#endif
+