| /* |
| * 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; |
| } |