| /* |
| * Copyright (c) 2017-2020 Nordic Semiconductor ASA |
| * Copyright (c) 2015 Runtime Inc |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <stddef.h> |
| #include <string.h> |
| |
| #include <zephyr/fs/fcb.h> |
| #include "fcb_priv.h" |
| |
| static struct flash_sector * |
| fcb_new_sector(struct fcb *fcb, int cnt) |
| { |
| struct flash_sector *prev; |
| struct flash_sector *cur; |
| int i; |
| |
| prev = NULL; |
| i = 0; |
| cur = fcb->f_active.fe_sector; |
| do { |
| cur = fcb_getnext_sector(fcb, cur); |
| if (!prev) { |
| prev = cur; |
| } |
| if (cur == fcb->f_oldest) { |
| return NULL; |
| } |
| } while (i++ < cnt); |
| return prev; |
| } |
| |
| /* |
| * Take one of the scratch blocks into use, if at all possible. |
| */ |
| int |
| fcb_append_to_scratch(struct fcb *fcb) |
| { |
| struct flash_sector *sector; |
| int rc; |
| |
| sector = fcb_new_sector(fcb, 0); |
| if (!sector) { |
| return -ENOSPC; |
| } |
| rc = fcb_sector_hdr_init(fcb, sector, fcb->f_active_id + 1); |
| if (rc) { |
| return rc; |
| } |
| fcb->f_active.fe_sector = sector; |
| fcb->f_active.fe_elem_off = fcb_len_in_flash(fcb, sizeof(struct fcb_disk_area)); |
| fcb->f_active_id++; |
| return 0; |
| } |
| |
| int |
| fcb_append(struct fcb *fcb, uint16_t len, struct fcb_entry *append_loc) |
| { |
| struct flash_sector *sector; |
| struct fcb_entry *active; |
| int cnt; |
| int rc; |
| uint8_t tmp_str[MAX(8, fcb->f_align)]; |
| |
| /* Ensure defined value of padding bytes */ |
| memset(tmp_str, fcb->f_erase_value, sizeof(tmp_str)); |
| |
| cnt = fcb_put_len(fcb, 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); |
| |
| __ASSERT_NO_MSG(cnt <= sizeof(tmp_str)); |
| |
| rc = k_mutex_lock(&fcb->f_mtx, K_FOREVER); |
| if (rc) { |
| return -EINVAL; |
| } |
| active = &fcb->f_active; |
| if (active->fe_elem_off + len + cnt > active->fe_sector->fs_size) { |
| sector = fcb_new_sector(fcb, fcb->f_scratch_cnt); |
| if (!sector || (sector->fs_size < |
| fcb_len_in_flash(fcb, sizeof(struct fcb_disk_area)) + len + cnt)) { |
| rc = -ENOSPC; |
| goto err; |
| } |
| rc = fcb_sector_hdr_init(fcb, sector, fcb->f_active_id + 1); |
| if (rc) { |
| goto err; |
| } |
| fcb->f_active.fe_sector = sector; |
| fcb->f_active.fe_elem_off = fcb_len_in_flash(fcb, sizeof(struct fcb_disk_area)); |
| fcb->f_active_id++; |
| } |
| |
| rc = fcb_flash_write(fcb, active->fe_sector, active->fe_elem_off, tmp_str, cnt); |
| if (rc) { |
| rc = -EIO; |
| goto err; |
| } |
| append_loc->fe_sector = active->fe_sector; |
| 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 0; |
| err: |
| k_mutex_unlock(&fcb->f_mtx); |
| return rc; |
| } |
| |
| int |
| fcb_append_finish(struct fcb *fcb, struct fcb_entry *loc) |
| { |
| int rc; |
| uint8_t em[fcb->f_align]; |
| off_t off; |
| |
| (void)memset(em, 0xFF, sizeof(em)); |
| |
| rc = fcb_elem_endmarker(fcb, loc, &em[0]); |
| if (rc) { |
| return rc; |
| } |
| off = loc->fe_data_off + fcb_len_in_flash(fcb, loc->fe_data_len); |
| |
| rc = fcb_flash_write(fcb, loc->fe_sector, off, em, fcb->f_align); |
| if (rc) { |
| return -EIO; |
| } |
| return 0; |
| } |