blob: 59d4054f21a2351a06ddfc928eccf96e65554005 [file] [log] [blame]
/*
* 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_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;
/* Require alignment to be a power of two. Some code depends on this
* assumption.
*/
assert((fcb->f_align & (fcb->f_align - 1)) == 0);
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;
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(uint8_t *buf, uint16_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(uint8_t *buf, uint16_t *len)
{
int rc;
if (buf[0] & 0x80) {
*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_is_empty(fap, 0, 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;
}