1 /*
2 * Copyright (c) 2017-2020 Nordic Semiconductor ASA
3 * Copyright (c) 2015 Runtime Inc
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include "fcb_priv.h"
9
10 #include <errno.h>
11 #include <limits.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include <zephyr/device.h>
16 #include <zephyr/drivers/flash.h>
17 #include <zephyr/fs/fcb.h>
18
fcb_get_align(const struct fcb * fcbp)19 uint8_t fcb_get_align(const struct fcb *fcbp)
20 {
21 uint8_t align;
22
23 if (fcbp->fap == NULL) {
24 return 0;
25 }
26
27 align = flash_area_align(fcbp->fap);
28
29 return align;
30 }
31
fcb_flash_read(const struct fcb * fcbp,const struct flash_sector * sector,off_t off,void * dst,size_t len)32 int fcb_flash_read(const struct fcb *fcbp, const struct flash_sector *sector, off_t off,
33 void *dst, size_t len)
34 {
35 int rc;
36
37 if (off + len > sector->fs_size) {
38 return -EINVAL;
39 }
40
41 if (fcbp->fap == NULL) {
42 return -EIO;
43 }
44
45 rc = flash_area_read(fcbp->fap, sector->fs_off + off, dst, len);
46
47 if (rc != 0) {
48 return -EIO;
49 }
50
51 return 0;
52 }
53
fcb_flash_write(const struct fcb * fcbp,const struct flash_sector * sector,off_t off,const void * src,size_t len)54 int fcb_flash_write(const struct fcb *fcbp, const struct flash_sector *sector, off_t off,
55 const void *src, size_t len)
56 {
57 int rc;
58
59 if (off + len > sector->fs_size) {
60 return -EINVAL;
61 }
62
63 if (fcbp->fap == NULL) {
64 return -EIO;
65 }
66
67 rc = flash_area_write(fcbp->fap, sector->fs_off + off, src, len);
68
69 if (rc != 0) {
70 return -EIO;
71 }
72
73 return 0;
74 }
75
fcb_erase_sector(const struct fcb * fcbp,const struct flash_sector * sector)76 int fcb_erase_sector(const struct fcb *fcbp, const struct flash_sector *sector)
77 {
78 int rc;
79
80 if (fcbp->fap == NULL) {
81 return -EIO;
82 }
83
84 rc = flash_area_flatten(fcbp->fap, sector->fs_off, sector->fs_size);
85 if (rc != 0) {
86 return -EIO;
87 }
88
89 return 0;
90 }
91
fcb_init(int f_area_id,struct fcb * fcbp)92 int fcb_init(int f_area_id, struct fcb *fcbp)
93 {
94 struct flash_sector *sector;
95 int rc;
96 int i;
97 uint8_t align;
98 int oldest = -1, newest = -1;
99 struct flash_sector *oldest_sector = NULL, *newest_sector = NULL;
100 struct fcb_disk_area fda;
101 const struct flash_parameters *fparam;
102
103 if (!fcbp->f_sectors || fcbp->f_sector_cnt - fcbp->f_scratch_cnt < 1) {
104 return -EINVAL;
105 }
106
107 rc = flash_area_open(f_area_id, &fcbp->fap);
108 if (rc != 0) {
109 return -EINVAL;
110 }
111
112 fparam = flash_get_parameters(fcbp->fap->fa_dev);
113 fcbp->f_erase_value = fparam->erase_value;
114
115 align = fcb_get_align(fcbp);
116 if (align == 0U) {
117 return -EINVAL;
118 }
119
120 /* Fill last used, first used */
121 for (i = 0; i < fcbp->f_sector_cnt; i++) {
122 sector = &fcbp->f_sectors[i];
123 rc = fcb_sector_hdr_read(fcbp, sector, &fda);
124 if (rc < 0) {
125 return rc;
126 }
127 if (rc == 0) {
128 continue;
129 }
130 if (oldest < 0) {
131 oldest = newest = fda.fd_id;
132 oldest_sector = newest_sector = sector;
133 continue;
134 }
135 if (FCB_ID_GT(fda.fd_id, newest)) {
136 newest = fda.fd_id;
137 newest_sector = sector;
138 } else if (FCB_ID_GT(oldest, fda.fd_id)) {
139 oldest = fda.fd_id;
140 oldest_sector = sector;
141 }
142 }
143 if (oldest < 0) {
144 /*
145 * No initialized areas.
146 */
147 oldest_sector = newest_sector = &fcbp->f_sectors[0];
148 rc = fcb_sector_hdr_init(fcbp, oldest_sector, 0);
149 if (rc) {
150 return rc;
151 }
152 newest = oldest = 0;
153 }
154 fcbp->f_align = align;
155 fcbp->f_oldest = oldest_sector;
156 fcbp->f_active.fe_sector = newest_sector;
157 fcbp->f_active.fe_elem_off = fcb_len_in_flash(fcbp, sizeof(struct fcb_disk_area));
158 fcbp->f_active_id = newest;
159
160 while (1) {
161 rc = fcb_getnext_in_sector(fcbp, &fcbp->f_active);
162 if (rc == -ENOTSUP) {
163 rc = 0;
164 break;
165 }
166 if (rc != 0) {
167 break;
168 }
169 }
170 k_mutex_init(&fcbp->f_mtx);
171 return rc;
172 }
173
fcb_free_sector_cnt(struct fcb * fcbp)174 int fcb_free_sector_cnt(struct fcb *fcbp)
175 {
176 int i;
177 struct flash_sector *fa;
178
179 fa = fcbp->f_active.fe_sector;
180 for (i = 0; i < fcbp->f_sector_cnt; i++) {
181 fa = fcb_getnext_sector(fcbp, fa);
182 if (fa == fcbp->f_oldest) {
183 break;
184 }
185 }
186 return i;
187 }
188
fcb_is_empty(struct fcb * fcbp)189 int fcb_is_empty(struct fcb *fcbp)
190 {
191 return (fcbp->f_active.fe_sector == fcbp->f_oldest &&
192 fcbp->f_active.fe_elem_off == fcb_len_in_flash(fcbp, sizeof(struct fcb_disk_area)));
193 }
194
195 /**
196 * Length of an element is encoded in 1 or 2 bytes.
197 * 1 byte for lengths < 128 bytes, 2 bytes for < 16384.
198 *
199 * The storage of length has been originally designed to work with 0xff erasable
200 * flash devices and gives length 0xffff special meaning: that there is no value
201 * written; this is smart way to utilize value in non-written flash to figure
202 * out where data ends. Additionally it sets highest bit of first byte of
203 * the length to 1, to mark that there is second byte to be read.
204 * Above poses some problems when non-0xff erasable flash is used. To solve
205 * the problem all length values are xored with not of erase value for given
206 * flash:
207 * len' = len ^ ~erase_value;
208 * To obtain original value, the logic is reversed:
209 * len = len' ^ ~erase_value;
210 *
211 * In case of 0xff erased flash this does not modify data that is written to
212 * flash; in case of other flash devices, e.g. that erase to 0x00, it allows
213 * to correctly use the first bit of byte to figure out how many bytes are there
214 * and if there is any data at all or both bytes are equal to erase value.
215 */
fcb_put_len(const struct fcb * fcbp,uint8_t * buf,uint16_t len)216 int fcb_put_len(const struct fcb *fcbp, uint8_t *buf, uint16_t len)
217 {
218 if (len < 0x80) {
219 buf[0] = len ^ ~fcbp->f_erase_value;
220 return 1;
221 } else if (len <= FCB_MAX_LEN) {
222 buf[0] = (len | 0x80) ^ ~fcbp->f_erase_value;
223 buf[1] = (len >> 7) ^ ~fcbp->f_erase_value;
224 return 2;
225 } else {
226 return -EINVAL;
227 }
228 }
229
fcb_get_len(const struct fcb * fcbp,uint8_t * buf,uint16_t * len)230 int fcb_get_len(const struct fcb *fcbp, uint8_t *buf, uint16_t *len)
231 {
232 int rc;
233 uint8_t buf0_xor;
234 uint8_t buf1_xor;
235
236 buf0_xor = buf[0] ^ ~fcbp->f_erase_value;
237 if (buf0_xor & 0x80) {
238 if ((buf[0] == fcbp->f_erase_value) && (buf[1] == fcbp->f_erase_value)) {
239 return -ENOTSUP;
240 }
241
242 buf1_xor = buf[1] ^ ~fcbp->f_erase_value;
243 *len = (uint16_t)((buf0_xor & 0x7f) | ((uint16_t)buf1_xor << 7));
244 rc = 2;
245 } else {
246 *len = (uint16_t)(buf0_xor);
247 rc = 1;
248 }
249 return rc;
250 }
251
252 /**
253 * Initialize erased sector for use.
254 */
fcb_sector_hdr_init(struct fcb * fcbp,struct flash_sector * sector,uint16_t id)255 int fcb_sector_hdr_init(struct fcb *fcbp, struct flash_sector *sector, uint16_t id)
256 {
257 struct fcb_disk_area fda;
258 int rc;
259
260 fda.fd_magic = fcb_flash_magic(fcbp);
261 fda.fd_ver = fcbp->f_version;
262 fda._pad = fcbp->f_erase_value;
263 fda.fd_id = id;
264
265 rc = fcb_flash_write(fcbp, sector, 0, &fda, sizeof(fda));
266 if (rc != 0) {
267 return -EIO;
268 }
269 return 0;
270 }
271
272 /**
273 * Checks whether FCB sector contains data or not.
274 * Returns <0 in error.
275 * Returns 0 if sector is unused;
276 * Returns 1 if sector has data.
277 */
fcb_sector_hdr_read(struct fcb * fcbp,struct flash_sector * sector,struct fcb_disk_area * fdap)278 int fcb_sector_hdr_read(struct fcb *fcbp, struct flash_sector *sector, struct fcb_disk_area *fdap)
279 {
280 struct fcb_disk_area fda;
281 int rc;
282
283 if (!fdap) {
284 fdap = &fda;
285 }
286 rc = fcb_flash_read(fcbp, sector, 0, fdap, sizeof(*fdap));
287 if (rc) {
288 return -EIO;
289 }
290 if (fdap->fd_magic == MK32(fcbp->f_erase_value)) {
291 return 0;
292 }
293 if (fdap->fd_magic != fcb_flash_magic(fcbp)) {
294 return -ENOMSG;
295 }
296 return 1;
297 }
298
299 /**
300 * Finds the fcb entry that gives back upto n entries at the end.
301 * @param0 ptr to fcb
302 * @param1 n number of fcb entries the user wants to get
303 * @param2 ptr to the fcb_entry to be returned
304 * @return 0 on there are any fcbs aviable; -ENOENT otherwise
305 */
fcb_offset_last_n(struct fcb * fcbp,uint8_t entries,struct fcb_entry * last_n_entry)306 int fcb_offset_last_n(struct fcb *fcbp, uint8_t entries, struct fcb_entry *last_n_entry)
307 {
308 struct fcb_entry loc;
309 int i;
310 int rc;
311
312 /* assure a minimum amount of entries */
313 if (!entries) {
314 entries = 1U;
315 }
316
317 i = 0;
318 (void)memset(&loc, 0, sizeof(loc));
319 while (!fcb_getnext(fcbp, &loc)) {
320 if (i == 0) {
321 /* Start from the beginning of fcb entries */
322 *last_n_entry = loc;
323 }
324 /* Update last_n_entry after n entries and keep updating */
325 else if (i > (entries - 1)) {
326 rc = fcb_getnext(fcbp, last_n_entry);
327
328 if (rc) {
329 /* A fcb history must have been erased,
330 * wanted entry doesn't exist anymore.
331 */
332 return -ENOENT;
333 }
334 }
335 i++;
336 }
337
338 return (i == 0) ? -ENOENT : 0;
339 }
340
341 /**
342 * Clear fcb
343 * @param fcb
344 * @return 0 on success; non-zero on failure
345 */
fcb_clear(struct fcb * fcbp)346 int fcb_clear(struct fcb *fcbp)
347 {
348 int rc;
349
350 rc = 0;
351 while (!fcb_is_empty(fcbp)) {
352 rc = fcb_rotate(fcbp);
353 if (rc) {
354 break;
355 }
356 }
357 return rc;
358 }
359