zephyr/subsys/fs/fcb/fcb.c
Chris Friedt 3b54d2b4b9 fs: fcb: rename variables differently than struct tag
Even just making minor edits to fcb, warnings are triggered
from Coding Guidelines of the form

Violation to rule 5.7 (Tag name should be unique) tag: fcb

Rename pointer variable names to fcbp.

Signed-off-by: Chris Friedt <cfriedt@tenstorrent.com>
2024-07-11 16:14:55 +02:00

359 lines
7.5 KiB
C

/*
* Copyright (c) 2017-2020 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "fcb_priv.h"
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <zephyr/device.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/fs/fcb.h>
uint8_t fcb_get_align(const struct fcb *fcbp)
{
uint8_t align;
if (fcbp->fap == NULL) {
return 0;
}
align = flash_area_align(fcbp->fap);
return align;
}
int fcb_flash_read(const struct fcb *fcbp, const struct flash_sector *sector, off_t off,
void *dst, size_t len)
{
int rc;
if (off + len > sector->fs_size) {
return -EINVAL;
}
if (fcbp->fap == NULL) {
return -EIO;
}
rc = flash_area_read(fcbp->fap, sector->fs_off + off, dst, len);
if (rc != 0) {
return -EIO;
}
return 0;
}
int fcb_flash_write(const struct fcb *fcbp, const struct flash_sector *sector, off_t off,
const void *src, size_t len)
{
int rc;
if (off + len > sector->fs_size) {
return -EINVAL;
}
if (fcbp->fap == NULL) {
return -EIO;
}
rc = flash_area_write(fcbp->fap, sector->fs_off + off, src, len);
if (rc != 0) {
return -EIO;
}
return 0;
}
int fcb_erase_sector(const struct fcb *fcbp, const struct flash_sector *sector)
{
int rc;
if (fcbp->fap == NULL) {
return -EIO;
}
rc = flash_area_flatten(fcbp->fap, sector->fs_off, sector->fs_size);
if (rc != 0) {
return -EIO;
}
return 0;
}
int fcb_init(int f_area_id, struct fcb *fcbp)
{
struct flash_sector *sector;
int rc;
int i;
uint8_t align;
int oldest = -1, newest = -1;
struct flash_sector *oldest_sector = NULL, *newest_sector = NULL;
struct fcb_disk_area fda;
const struct flash_parameters *fparam;
if (!fcbp->f_sectors || fcbp->f_sector_cnt - fcbp->f_scratch_cnt < 1) {
return -EINVAL;
}
rc = flash_area_open(f_area_id, &fcbp->fap);
if (rc != 0) {
return -EINVAL;
}
fparam = flash_get_parameters(fcbp->fap->fa_dev);
fcbp->f_erase_value = fparam->erase_value;
align = fcb_get_align(fcbp);
if (align == 0U) {
return -EINVAL;
}
/* Fill last used, first used */
for (i = 0; i < fcbp->f_sector_cnt; i++) {
sector = &fcbp->f_sectors[i];
rc = fcb_sector_hdr_read(fcbp, sector, &fda);
if (rc < 0) {
return rc;
}
if (rc == 0) {
continue;
}
if (oldest < 0) {
oldest = newest = fda.fd_id;
oldest_sector = newest_sector = sector;
continue;
}
if (FCB_ID_GT(fda.fd_id, newest)) {
newest = fda.fd_id;
newest_sector = sector;
} else if (FCB_ID_GT(oldest, fda.fd_id)) {
oldest = fda.fd_id;
oldest_sector = sector;
}
}
if (oldest < 0) {
/*
* No initialized areas.
*/
oldest_sector = newest_sector = &fcbp->f_sectors[0];
rc = fcb_sector_hdr_init(fcbp, oldest_sector, 0);
if (rc) {
return rc;
}
newest = oldest = 0;
}
fcbp->f_align = align;
fcbp->f_oldest = oldest_sector;
fcbp->f_active.fe_sector = newest_sector;
fcbp->f_active.fe_elem_off = fcb_len_in_flash(fcbp, sizeof(struct fcb_disk_area));
fcbp->f_active_id = newest;
while (1) {
rc = fcb_getnext_in_sector(fcbp, &fcbp->f_active);
if (rc == -ENOTSUP) {
rc = 0;
break;
}
if (rc != 0) {
break;
}
}
k_mutex_init(&fcbp->f_mtx);
return rc;
}
int fcb_free_sector_cnt(struct fcb *fcbp)
{
int i;
struct flash_sector *fa;
fa = fcbp->f_active.fe_sector;
for (i = 0; i < fcbp->f_sector_cnt; i++) {
fa = fcb_getnext_sector(fcbp, fa);
if (fa == fcbp->f_oldest) {
break;
}
}
return i;
}
int fcb_is_empty(struct fcb *fcbp)
{
return (fcbp->f_active.fe_sector == fcbp->f_oldest &&
fcbp->f_active.fe_elem_off == fcb_len_in_flash(fcbp, sizeof(struct fcb_disk_area)));
}
/**
* Length of an element is encoded in 1 or 2 bytes.
* 1 byte for lengths < 128 bytes, 2 bytes for < 16384.
*
* The storage of length has been originally designed to work with 0xff erasable
* flash devices and gives length 0xffff special meaning: that there is no value
* written; this is smart way to utilize value in non-written flash to figure
* out where data ends. Additionally it sets highest bit of first byte of
* the length to 1, to mark that there is second byte to be read.
* Above poses some problems when non-0xff erasable flash is used. To solve
* the problem all length values are xored with not of erase value for given
* flash:
* len' = len ^ ~erase_value;
* To obtain original value, the logic is reversed:
* len = len' ^ ~erase_value;
*
* In case of 0xff erased flash this does not modify data that is written to
* flash; in case of other flash devices, e.g. that erase to 0x00, it allows
* to correctly use the first bit of byte to figure out how many bytes are there
* and if there is any data at all or both bytes are equal to erase value.
*/
int fcb_put_len(const struct fcb *fcbp, uint8_t *buf, uint16_t len)
{
if (len < 0x80) {
buf[0] = len ^ ~fcbp->f_erase_value;
return 1;
} else if (len <= FCB_MAX_LEN) {
buf[0] = (len | 0x80) ^ ~fcbp->f_erase_value;
buf[1] = (len >> 7) ^ ~fcbp->f_erase_value;
return 2;
} else {
return -EINVAL;
}
}
int fcb_get_len(const struct fcb *fcbp, uint8_t *buf, uint16_t *len)
{
int rc;
uint8_t buf0_xor;
uint8_t buf1_xor;
buf0_xor = buf[0] ^ ~fcbp->f_erase_value;
if (buf0_xor & 0x80) {
if ((buf[0] == fcbp->f_erase_value) && (buf[1] == fcbp->f_erase_value)) {
return -ENOTSUP;
}
buf1_xor = buf[1] ^ ~fcbp->f_erase_value;
*len = (uint16_t)((buf0_xor & 0x7f) | ((uint16_t)buf1_xor << 7));
rc = 2;
} else {
*len = (uint16_t)(buf0_xor);
rc = 1;
}
return rc;
}
/**
* Initialize erased sector for use.
*/
int fcb_sector_hdr_init(struct fcb *fcbp, struct flash_sector *sector, uint16_t id)
{
struct fcb_disk_area fda;
int rc;
fda.fd_magic = fcb_flash_magic(fcbp);
fda.fd_ver = fcbp->f_version;
fda._pad = fcbp->f_erase_value;
fda.fd_id = id;
rc = fcb_flash_write(fcbp, sector, 0, &fda, sizeof(fda));
if (rc != 0) {
return -EIO;
}
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 *fcbp, struct flash_sector *sector, struct fcb_disk_area *fdap)
{
struct fcb_disk_area fda;
int rc;
if (!fdap) {
fdap = &fda;
}
rc = fcb_flash_read(fcbp, sector, 0, fdap, sizeof(*fdap));
if (rc) {
return -EIO;
}
if (fdap->fd_magic == MK32(fcbp->f_erase_value)) {
return 0;
}
if (fdap->fd_magic != fcb_flash_magic(fcbp)) {
return -ENOMSG;
}
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 *fcbp, uint8_t entries, struct fcb_entry *last_n_entry)
{
struct fcb_entry loc;
int i;
int rc;
/* assure a minimum amount of entries */
if (!entries) {
entries = 1U;
}
i = 0;
(void)memset(&loc, 0, sizeof(loc));
while (!fcb_getnext(fcbp, &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)) {
rc = fcb_getnext(fcbp, last_n_entry);
if (rc) {
/* A fcb history must have been erased,
* wanted entry doesn't exist anymore.
*/
return -ENOENT;
}
}
i++;
}
return (i == 0) ? -ENOENT : 0;
}
/**
* Clear fcb
* @param fcb
* @return 0 on success; non-zero on failure
*/
int fcb_clear(struct fcb *fcbp)
{
int rc;
rc = 0;
while (!fcb_is_empty(fcbp)) {
rc = fcb_rotate(fcbp);
if (rc) {
break;
}
}
return rc;
}