1 /*
2 * Copyright (c) 2017-2021 Nordic Semiconductor ASA
3 * Copyright (c) 2015 Runtime Inc
4 * Copyright (c) 2017 Linaro Ltd
5 * Copyright (c) 2020 Gerson Fernando Budke <nandojve@gmail.com>
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9
10 #include <errno.h>
11
12 #include <zephyr/types.h>
13 #include <stddef.h>
14 #include <sys/types.h>
15 #include <zephyr/device.h>
16 #include <zephyr/storage/flash_map.h>
17 #include <zephyr/drivers/flash.h>
18 #include <zephyr/init.h>
19
20 struct layout_data {
21 uint32_t area_idx;
22 uint32_t area_off;
23 uint32_t area_len;
24 void *ret; /* struct flash_area* or struct flash_sector* */
25 uint32_t ret_idx;
26 uint32_t ret_len;
27 int status;
28 };
29
30 /*
31 * Check if a flash_page_foreach() callback should exit early, due to
32 * one of the following conditions:
33 *
34 * - The flash page described by "info" is before the area of interest
35 * described in "data"
36 * - The flash page is after the end of the area
37 * - There are too many flash pages on the device to fit in the array
38 * held in data->ret. In this case, data->status is set to -ENOMEM.
39 *
40 * The value to return to flash_page_foreach() is stored in
41 * "bail_value" if the callback should exit early.
42 */
should_bail(const struct flash_pages_info * info,struct layout_data * data,bool * bail_value)43 static bool should_bail(const struct flash_pages_info *info,
44 struct layout_data *data,
45 bool *bail_value)
46 {
47 if (info->start_offset < data->area_off) {
48 *bail_value = true;
49 return true;
50 } else if (info->start_offset >= data->area_off + data->area_len) {
51 *bail_value = false;
52 return true;
53 } else if (data->ret_idx >= data->ret_len) {
54 data->status = -ENOMEM;
55 *bail_value = false;
56 return true;
57 }
58
59 return false;
60 }
61
get_sectors_cb(const struct flash_pages_info * info,void * datav)62 static bool get_sectors_cb(const struct flash_pages_info *info, void *datav)
63 {
64 struct layout_data *data = datav;
65 struct flash_sector *ret = data->ret;
66 bool bail;
67
68 if (should_bail(info, data, &bail)) {
69 return bail;
70 }
71
72 ret[data->ret_idx].fs_off = info->start_offset - data->area_off;
73 ret[data->ret_idx].fs_size = info->size;
74 data->ret_idx++;
75
76 return true;
77 }
78
flash_area_get_sectors(int idx,uint32_t * cnt,struct flash_sector * ret)79 int flash_area_get_sectors(int idx, uint32_t *cnt, struct flash_sector *ret)
80 {
81 const struct flash_area *fa;
82 int rc = flash_area_open(idx, &fa);
83
84 if (rc < 0 || fa == NULL) {
85 return -EINVAL;
86 }
87
88 rc = flash_area_sectors(fa, cnt, ret);
89 flash_area_close(fa);
90
91 return rc;
92 }
93
flash_area_sectors(const struct flash_area * fa,uint32_t * cnt,struct flash_sector * ret)94 int flash_area_sectors(const struct flash_area *fa, uint32_t *cnt, struct flash_sector *ret)
95 {
96 struct layout_data data;
97 const struct device *flash_dev;
98
99 data.area_off = fa->fa_off;
100 data.area_len = fa->fa_size;
101
102 data.ret = ret;
103 data.ret_idx = 0U;
104 data.ret_len = *cnt;
105 data.status = 0;
106
107 flash_dev = fa->fa_dev;
108
109 flash_page_foreach(flash_dev, get_sectors_cb, &data);
110
111 if (data.status == 0) {
112 *cnt = data.ret_idx;
113 }
114
115 return data.status;
116 }
117