125 lines
3.0 KiB
C
125 lines
3.0 KiB
C
/*
|
|
* Copyright (c) 2017-2021 Nordic Semiconductor ASA
|
|
* Copyright (c) 2015 Runtime Inc
|
|
* Copyright (c) 2017 Linaro Ltd
|
|
* Copyright (c) 2020 Gerson Fernando Budke <nandojve@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/types.h>
|
|
#include <stddef.h>
|
|
#include <sys/types.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/storage/flash_map.h>
|
|
#include <zephyr/drivers/flash.h>
|
|
#include <soc.h>
|
|
#include <zephyr/init.h>
|
|
|
|
struct layout_data {
|
|
uint32_t area_idx;
|
|
uint32_t area_off;
|
|
uint32_t area_len;
|
|
void *ret; /* struct flash_area* or struct flash_sector* */
|
|
uint32_t ret_idx;
|
|
uint32_t ret_len;
|
|
int status;
|
|
};
|
|
|
|
/*
|
|
* Check if a flash_page_foreach() callback should exit early, due to
|
|
* one of the following conditions:
|
|
*
|
|
* - The flash page described by "info" is before the area of interest
|
|
* described in "data"
|
|
* - The flash page is after the end of the area
|
|
* - There are too many flash pages on the device to fit in the array
|
|
* held in data->ret. In this case, data->status is set to -ENOMEM.
|
|
*
|
|
* The value to return to flash_page_foreach() is stored in
|
|
* "bail_value" if the callback should exit early.
|
|
*/
|
|
static bool should_bail(const struct flash_pages_info *info,
|
|
struct layout_data *data,
|
|
bool *bail_value)
|
|
{
|
|
if (info->start_offset < data->area_off) {
|
|
*bail_value = true;
|
|
return true;
|
|
} else if (info->start_offset >= data->area_off + data->area_len) {
|
|
*bail_value = false;
|
|
return true;
|
|
} else if (data->ret_idx >= data->ret_len) {
|
|
data->status = -ENOMEM;
|
|
*bail_value = false;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Generic page layout discovery routine. This is kept separate to
|
|
* support both the deprecated flash_area_to_sectors() and the current
|
|
* flash_area_get_sectors(). A lot of this can be inlined once
|
|
* flash_area_to_sectors() is removed.
|
|
*/
|
|
static int flash_area_layout(int idx, uint32_t *cnt, void *ret,
|
|
flash_page_cb cb, struct layout_data *cb_data)
|
|
{
|
|
const struct device *flash_dev;
|
|
const struct flash_area *fa;
|
|
int rc = flash_area_open(idx, &fa);
|
|
|
|
if (rc < 0 || fa == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
cb_data->area_idx = idx;
|
|
cb_data->area_off = fa->fa_off;
|
|
cb_data->area_len = fa->fa_size;
|
|
|
|
cb_data->ret = ret;
|
|
cb_data->ret_idx = 0U;
|
|
cb_data->ret_len = *cnt;
|
|
cb_data->status = 0;
|
|
|
|
flash_dev = device_get_binding(fa->fa_dev_name);
|
|
flash_area_close(fa);
|
|
if (flash_dev == NULL) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
flash_page_foreach(flash_dev, cb, cb_data);
|
|
|
|
if (cb_data->status == 0) {
|
|
*cnt = cb_data->ret_idx;
|
|
}
|
|
|
|
return cb_data->status;
|
|
}
|
|
|
|
static bool get_sectors_cb(const struct flash_pages_info *info, void *datav)
|
|
{
|
|
struct layout_data *data = datav;
|
|
struct flash_sector *ret = data->ret;
|
|
bool bail;
|
|
|
|
if (should_bail(info, data, &bail)) {
|
|
return bail;
|
|
}
|
|
|
|
ret[data->ret_idx].fs_off = info->start_offset - data->area_off;
|
|
ret[data->ret_idx].fs_size = info->size;
|
|
data->ret_idx++;
|
|
|
|
return true;
|
|
}
|
|
|
|
int flash_area_get_sectors(int idx, uint32_t *cnt, struct flash_sector *ret)
|
|
{
|
|
struct layout_data data;
|
|
|
|
return flash_area_layout(idx, cnt, ret, get_sectors_cb, &data);
|
|
}
|