blob: 2ac090c50099acbce3b43f75cdbcf96a68ce8d10 [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 <string.h>
#include "os/mynewt.h"
#if MYNEWT_VAL(LOG_FCB_BOOKMARKS)
#include "log/log.h"
#include "log/log_fcb.h"
#if MYNEWT_VAL(LOG_FCB_SECTOR_BOOKMARKS)
static int
log_fcb_init_sector_bmarks(struct fcb_log *fcb_log)
{
int rc = 0;
int i = 0;
int j = 0;
struct log_fcb_bset *bset = &fcb_log->fl_bset;
struct log_entry_hdr ueh = {0};
#if MYNEWT_VAL(LOG_FCB)
struct fcb_entry loc = {0};
struct flash_area *fa = NULL;
rc = fcb_getnext(&fcb_log->fl_fcb, &loc);
if (rc) {
return SYS_EOK;
}
#else
struct flash_sector_range *range = NULL;
struct fcb2_entry loc = {0};
rc = fcb2_getnext(&fcb_log->fl_fcb, &loc);
if (rc) {
return SYS_EOK;
}
#endif
/* Start adding a bookmark from the end of array just before
* non-sector bookmarks
*/
bset->lfs_next_sect = bset->lfs_sect_cap - 1;
for (i = 0; i < bset->lfs_sect_cap; i++) {
rc = log_read_hdr(fcb_log->fl_log, &loc, &ueh);
if (rc) {
/* Read failed, don't add a bookmark, done adding bookmarks */
rc = SYS_EOK;
break;
}
rc = log_fcb_add_bmark(fcb_log, &loc, ueh.ue_index, true);
if (rc) {
return rc;
}
#if MYNEWT_VAL(LOG_FCB)
j = 0;
fa = loc.fe_area;
/* Keep skipping sectors until lfs_sect_bmark_itvl is reached */
do {
fa = fcb_getnext_area(&fcb_log->fl_fcb, fa);
if (!fa) {
break;
}
j++;
} while (j < bset->lfs_sect_bmark_itvl);
if (!fa) {
break;
}
/* First entry in the next area */
rc = fcb_getnext_in_area(&fcb_log->fl_fcb, fa, &loc);
if (rc) {
rc = SYS_EOK;
break;
}
#else
j = 0;
/* Keep skipping rangess until lfs_sect_bmark_itvl is reached */
do {
/* Get next range */
range = fcb2_getnext_range(&fcb_log->fl_fcb, &loc);
if (!range) {
break;
}
j++;
} while (j < bset->lfs_sect_bmark_itvl);
if (!range) {
break;
}
/* First entry in the next area */
rc = fcb2_getnext_in_area(&fcb_log->fl_fcb, range, &loc);
if (rc) {
rc = SYS_EOK;
break;
}
#endif
}
return rc;
}
#endif
int
log_fcb_init_bmarks(struct fcb_log *fcb_log,
struct log_fcb_bmark *buf, int avl_bmark_cnt,
bool en_sect_bmarks)
{
struct log_fcb_bset *bset = &fcb_log->fl_bset;
/* Required bookmark count */
int reqd_bmark_cnt = MYNEWT_VAL(LOG_FCB_NUM_ABS_BOOKMARKS);
(void)reqd_bmark_cnt;
if (!bset || !buf || !avl_bmark_cnt) {
return SYS_EINVAL;
}
memset(buf, 0, sizeof(struct log_fcb_bmark) * avl_bmark_cnt);
*bset = (struct log_fcb_bset) {
.lfs_bmarks = buf,
.lfs_cap = avl_bmark_cnt,
.lfs_en_sect_bmarks = en_sect_bmarks
};
#if MYNEWT_VAL(LOG_FCB_SECTOR_BOOKMARKS)
if (en_sect_bmarks) {
/* Default sector bookmark interval is 1 */
bset->lfs_sect_bmark_itvl = 1;
reqd_bmark_cnt += fcb_log->fl_fcb.f_sector_cnt;
/* Make sure we have allocated the exact number of bookmarks
* compare available bookmark count Vs required bookmark count
*/
if (avl_bmark_cnt < reqd_bmark_cnt) {
/* Not enough space allocated for sector bookmarks,
* add bookmarks at sector intervals
*/
bset->lfs_sect_bmark_itvl =
fcb_log->fl_fcb.f_sector_cnt / avl_bmark_cnt;
bset->lfs_sect_cap = avl_bmark_cnt -
MYNEWT_VAL(LOG_FCB_NUM_ABS_BOOKMARKS);
} else {
bset->lfs_sect_cap = fcb_log->fl_fcb.f_sector_cnt;
}
return log_fcb_init_sector_bmarks(fcb_log);
}
#endif
return 0;
}
void
log_fcb_rotate_bmarks(struct fcb_log *fcb_log)
{
int i;
struct log_fcb_bmark *bmark;
struct log_fcb_bset *bset;
bset = &fcb_log->fl_bset;
for (i = 0; i < bset->lfs_size; i++) {
bmark = &bset->lfs_bmarks[i];
#if MYNEWT_VAL(LOG_FCB)
if (bmark->lfb_entry.fe_area != fcb_log->fl_fcb.f_oldest) {
continue;
}
#endif
#if MYNEWT_VAL(LOG_FCB2)
if (bmark->lfb_entry.fe_sector != fcb_log->fl_fcb.f_oldest_sec) {
continue;
}
#endif
if (i != bset->lfs_size - 1) {
*bmark = bset->lfs_bmarks[bset->lfs_size - 1];
i--;
}
bset->lfs_size--;
}
}
void
log_fcb_clear_bmarks(struct fcb_log *fcb_log)
{
fcb_log->fl_bset.lfs_size = 0;
fcb_log->fl_bset.lfs_next_non_sect = 0;
fcb_log->fl_bset.lfs_non_sect_size = 0;
#if MYNEWT_VAL(LOG_FCB_SECTOR_BOOKMARKS)
if (fcb_log->fl_bset.lfs_en_sect_bmarks) {
fcb_log->fl_bset.lfs_next_sect = fcb_log->fl_bset.lfs_sect_cap - 1;
}
#endif
memset(fcb_log->fl_bset.lfs_bmarks, 0,
sizeof(struct log_fcb_bmark) *
fcb_log->fl_bset.lfs_cap);
}
struct log_fcb_bmark *
log_fcb_get_bmarks(struct log *log, uint32_t *bmarks_size)
{
struct fcb_log *fcb_log = (struct fcb_log *)log->l_arg;
*bmarks_size = fcb_log->fl_bset.lfs_cap;
return fcb_log->fl_bset.lfs_bmarks;
}
const struct log_fcb_bmark *
log_fcb_closest_bmark(const struct fcb_log *fcb_log, uint32_t index,
int *min_diff)
{
const struct log_fcb_bmark *closest;
const struct log_fcb_bmark *bmark;
uint32_t diff;
int i = 0;
uint32_t start_idx = 0;
*min_diff = -1;
closest = NULL;
if (!fcb_log->fl_bset.lfs_bmarks) {
return closest;
}
#if MYNEWT_VAL(LOG_FCB_SECTOR_BOOKMARKS)
#if MYNEWT_VAL(LOG_FCB)
/* Reason for this is that we want to iterate to the end of the
* bmarks sector section of the array where a bookmark is valid,
* this is only for the case where the sector bookmarks have not
* been fully filled up, if they are filled up, we can iterate
* normally to the end of the array
*/
if (!fcb_log->fl_bset.lfs_bmarks[i].lfb_entry.fe_area &&
fcb_log->fl_bset.lfs_next_sect < (fcb_log->fl_bset.lfs_sect_cap - 1)) {
start_idx = fcb_log->fl_bset.lfs_next_sect + 1;
}
#endif
#endif
/* This works for both sector as well as non-sector bmarks
* because we calculate the min diff and iterate to the end
* of the bmarks array keeping track of min diff
*/
for (i = start_idx; i < (start_idx + fcb_log->fl_bset.lfs_size); i++) {
bmark = &fcb_log->fl_bset.lfs_bmarks[i];
#if MYNEWT_VAL(LOG_FCB)
if (!fcb_log->fl_bset.lfs_bmarks[i].lfb_entry.fe_area) {
#if !MYNEWT_VAL(LOG_FCB_SECTOR_BOOKMARKS)
/* Empty non-sector bookmark, nothing more to do
* Previous closest bookmark is the closest one */
break;
#endif
}
#elif MYNEWT_VAL(LOG_FCB2)
if (!fcb_log->fl_bset.lfs_bmarks[i].lfb_entry.fe_range) {
#if !MYNEWT_VAL(LOG_FCB_SECTOR_BOOKMARKS)
/* Empty non-sector bookmark, nothing more to do
* Previous closest bookmark is the closest one */
break;
#endif
}
#endif
if (bmark->lfb_index <= index) {
diff = index - bmark->lfb_index;
if (diff < *min_diff) {
*min_diff = diff;
closest = bmark;
MODLOG_DEBUG(LOG_MODULE_DEFAULT,
"index: %u, closest bmark idx: %u, \n",
(unsigned int)index,
(unsigned int)bmark->lfb_index);
/* We found the exact match, no need to keep searching for a
* better match
*/
if (*min_diff == 0) {
break;
}
}
}
}
return closest;
}
#if MYNEWT_VAL(LOG_FCB_SECTOR_BOOKMARKS)
static int
log_fcb_insert_sect_bmark(struct fcb_log *fcb_log, struct fcb_entry *entry,
uint32_t index)
{
struct log_fcb_bset *bset;
bset = &fcb_log->fl_bset;
if (bset->lfs_size < fcb_log->fl_bset.lfs_sect_cap) {
bset->lfs_bmarks[bset->lfs_next_sect] = (struct log_fcb_bmark) {
.lfb_entry = *entry,
.lfb_index = index,
};
bset->lfs_size++;
bset->lfs_next_sect--;
if (bset->lfs_next_sect >= fcb_log->fl_bset.lfs_sect_cap) {
bset->lfs_next_sect = fcb_log->fl_bset.lfs_sect_cap - 1;
}
}
return 0;
}
#endif
#if MYNEWT_VAL(LOG_FCB)
static int
log_fcb_replace_non_sect_bmark(struct fcb_log *fcb_log, struct fcb_entry *entry,
uint32_t index, int pos)
#elif MYNEWT_VAL(LOG_FCB2)
static int
log_fcb_replace_non_sect_bmark(struct fcb_log *fcb_log, struct fcb2_entry *entry,
uint32_t index, int pos)
#endif
{
int i = 0;
struct log_fcb_bset *bset = &fcb_log->fl_bset;
#if MYNEWT_VAL(LOG_FCB) && MYNEWT_VAL(LOG_FCB_SECTOR_BOOKMARKS)
if (bset->lfs_en_sect_bmarks) {
for (i = bset->lfs_sect_cap;
i < (bset->lfs_non_sect_size + bset->lfs_sect_cap);
i++) {
if (index == bset->lfs_bmarks[i].lfb_index) {
/* If index matches, no need to replace */
return SYS_EALREADY;
}
}
} else
#endif
{
for (i = 0; i < bset->lfs_non_sect_size; i++) {
if (index == bset->lfs_bmarks[i].lfb_index) {
/* If index matches, no need to replace */
return SYS_EALREADY;
}
}
}
bset->lfs_bmarks[pos] = (struct log_fcb_bmark) {
.lfb_entry = *entry,
.lfb_index = index,
};
return SYS_EOK;
}
#if MYNEWT_VAL(LOG_FCB)
int
log_fcb_add_bmark(struct fcb_log *fcb_log, struct fcb_entry *entry,
uint32_t index, bool sect_bmark)
#elif MYNEWT_VAL(LOG_FCB2)
int
log_fcb_add_bmark(struct fcb_log *fcb_log, struct fcb2_entry *entry,
uint32_t index, bool sect_bmark)
#endif
{
struct log_fcb_bset *bset = &fcb_log->fl_bset;
int rc = 0;
if (bset->lfs_cap == 0) {
return SYS_ENOMEM;
}
#if MYNEWT_VAL(LOG_FCB) && MYNEWT_VAL(LOG_FCB_SECTOR_BOOKMARKS)
if (sect_bmark & bset->lfs_en_sect_bmarks) {
rc = log_fcb_insert_sect_bmark(fcb_log, entry, index);
if (rc) {
MODLOG_DEBUG(LOG_MODULE_DEFAULT, "insert bmark failure: %u\n",
index);
}
MODLOG_DEBUG(LOG_MODULE_DEFAULT, "insert bmark index: %u, pos: %u\n",
index, bset->lfs_next_sect);
} else {
/* Replace oldest non-sector bmark */
rc = log_fcb_replace_non_sect_bmark(fcb_log, entry, index,
bset->lfs_next_non_sect +
(bset->lfs_en_sect_bmarks ?
bset->lfs_sect_cap : 0));
if (rc == SYS_EOK) {
MODLOG_DEBUG(LOG_MODULE_DEFAULT, "replace bmark index: %u, pos: %u\n",
index, bset->lfs_next_non_sect +
(bset->lfs_en_sect_bmarks ?
bset->lfs_sect_cap : 0));
if (bset->lfs_non_sect_size < MYNEWT_VAL(LOG_FCB_NUM_ABS_BOOKMARKS)) {
bset->lfs_non_sect_size++;
bset->lfs_size++;
}
bset->lfs_next_non_sect = (bset->lfs_next_non_sect + 1) %
MYNEWT_VAL(LOG_FCB_NUM_ABS_BOOKMARKS);
}
}
#else
if (!sect_bmark) {
if (bset->lfs_size >= MYNEWT_VAL(LOG_FCB_NUM_ABS_BOOKMARKS)) {
/* Replace oldest non-sector bmark */
rc = log_fcb_replace_non_sect_bmark(fcb_log, entry, index,
bset->lfs_next_non_sect);
if (rc == SYS_EOK) {
MODLOG_DEBUG(LOG_MODULE_DEFAULT, "replace bmark index: %u, pos: %u\n",
index, bset->lfs_next_non_sect);
bset->lfs_next_non_sect = (bset->lfs_next_non_sect + 1) %
MYNEWT_VAL(LOG_FCB_NUM_ABS_BOOKMARKS);
}
} else {
rc = log_fcb_replace_non_sect_bmark(fcb_log, entry, index,
bset->lfs_size);
if (rc == SYS_EOK) {
MODLOG_DEBUG(LOG_MODULE_DEFAULT, "replace bmark index: %u, pos: %u\n",
index, bset->lfs_size);
if (!bset->lfs_size) {
/* First non-sector bmark position */
bset->lfs_next_non_sect = 0;
}
bset->lfs_size++;
}
}
assert(bset->lfs_size <= MYNEWT_VAL(LOG_FCB_NUM_ABS_BOOKMARKS));
}
#endif
return SYS_EOK;
}
#endif /* MYNEWT_VAL(LOG_FCB_BOOKMARKS) */