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