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