1 /*
2 * Copyright (c) 2020 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/bluetooth/mesh.h>
8 #include <zephyr/storage/flash_map.h>
9 #include <zephyr/drivers/flash.h>
10 #include <assert.h>
11 #include "blob.h"
12 #include "net.h"
13 #include "transport.h"
14
15 #define WRITE_BLOCK_SIZE DT_PROP(DT_INST(0, soc_nv_flash), write_block_size)
16
17 #define FLASH_IO(_io) CONTAINER_OF(_io, struct bt_mesh_blob_io_flash, io)
18
test_flash_area(uint8_t area_id)19 static int test_flash_area(uint8_t area_id)
20 {
21 const struct flash_area *area;
22 uint8_t align;
23 int err;
24
25 err = flash_area_open(area_id, &area);
26 if (err) {
27 return err;
28 }
29
30 align = flash_area_align(area);
31 flash_area_close(area);
32
33 if (align > WRITE_BLOCK_SIZE) {
34 return -EINVAL;
35 }
36
37 return 0;
38 }
39
io_open(const struct bt_mesh_blob_io * io,const struct bt_mesh_blob_xfer * xfer,enum bt_mesh_blob_io_mode mode)40 static int io_open(const struct bt_mesh_blob_io *io,
41 const struct bt_mesh_blob_xfer *xfer,
42 enum bt_mesh_blob_io_mode mode)
43 {
44 struct bt_mesh_blob_io_flash *flash = FLASH_IO(io);
45
46 flash->mode = mode;
47
48 return flash_area_open(flash->area_id, &flash->area);
49 }
50
io_close(const struct bt_mesh_blob_io * io,const struct bt_mesh_blob_xfer * xfer)51 static void io_close(const struct bt_mesh_blob_io *io,
52 const struct bt_mesh_blob_xfer *xfer)
53 {
54 struct bt_mesh_blob_io_flash *flash = FLASH_IO(io);
55
56 flash_area_close(flash->area);
57 }
58
block_start(const struct bt_mesh_blob_io * io,const struct bt_mesh_blob_xfer * xfer,const struct bt_mesh_blob_block * block)59 static int block_start(const struct bt_mesh_blob_io *io,
60 const struct bt_mesh_blob_xfer *xfer,
61 const struct bt_mesh_blob_block *block)
62 {
63 struct bt_mesh_blob_io_flash *flash = FLASH_IO(io);
64 size_t erase_size;
65
66 if (flash->mode == BT_MESH_BLOB_READ) {
67 return 0;
68 }
69
70 #if defined(CONFIG_FLASH_PAGE_LAYOUT)
71 struct flash_pages_info page;
72 const struct device *flash_dev;
73 int err;
74
75 flash_dev = flash_area_get_device(flash->area);
76 if (!flash_dev) {
77 return -ENODEV;
78 }
79
80 err = flash_get_page_info_by_offs(flash_dev,
81 flash->offset + block->offset, &page);
82 if (err) {
83 return err;
84 }
85
86 erase_size = page.size * DIV_ROUND_UP(block->size, page.size);
87 #else
88 erase_size = block->size;
89 #endif
90
91 return flash_area_flatten(flash->area, flash->offset + block->offset,
92 erase_size);
93 }
94
rd_chunk(const struct bt_mesh_blob_io * io,const struct bt_mesh_blob_xfer * xfer,const struct bt_mesh_blob_block * block,const struct bt_mesh_blob_chunk * chunk)95 static int rd_chunk(const struct bt_mesh_blob_io *io,
96 const struct bt_mesh_blob_xfer *xfer,
97 const struct bt_mesh_blob_block *block,
98 const struct bt_mesh_blob_chunk *chunk)
99 {
100 struct bt_mesh_blob_io_flash *flash = FLASH_IO(io);
101
102 return flash_area_read(flash->area,
103 flash->offset + block->offset + chunk->offset,
104 chunk->data, chunk->size);
105 }
106
wr_chunk(const struct bt_mesh_blob_io * io,const struct bt_mesh_blob_xfer * xfer,const struct bt_mesh_blob_block * block,const struct bt_mesh_blob_chunk * chunk)107 static int wr_chunk(const struct bt_mesh_blob_io *io,
108 const struct bt_mesh_blob_xfer *xfer,
109 const struct bt_mesh_blob_block *block,
110 const struct bt_mesh_blob_chunk *chunk)
111 {
112 struct bt_mesh_blob_io_flash *flash = FLASH_IO(io);
113
114 if (IS_ENABLED(CONFIG_SOC_FLASH_NRF_RRAM)) {
115 return flash_area_write(flash->area,
116 flash->offset + block->offset + chunk->offset,
117 chunk->data, chunk->size);
118 }
119
120 uint8_t buf[ROUND_UP(BLOB_RX_CHUNK_SIZE, WRITE_BLOCK_SIZE)];
121 off_t area_offset = flash->offset + block->offset + chunk->offset;
122 int i = 0;
123
124 /* Write block align the chunk data */
125 memset(&buf[i], 0xff, area_offset % WRITE_BLOCK_SIZE);
126 i += area_offset % WRITE_BLOCK_SIZE;
127
128 memcpy(&buf[i], chunk->data, chunk->size);
129 i += chunk->size;
130
131 memset(&buf[i], 0xff, ROUND_UP(i, WRITE_BLOCK_SIZE) - i);
132 i = ROUND_UP(i, WRITE_BLOCK_SIZE);
133
134 return flash_area_write(flash->area,
135 ROUND_DOWN(area_offset, WRITE_BLOCK_SIZE),
136 buf, i);
137 }
138
bt_mesh_blob_io_flash_init(struct bt_mesh_blob_io_flash * flash,uint8_t area_id,off_t offset)139 int bt_mesh_blob_io_flash_init(struct bt_mesh_blob_io_flash *flash,
140 uint8_t area_id, off_t offset)
141 {
142 int err;
143
144 err = test_flash_area(area_id);
145 if (err) {
146 return err;
147 }
148
149 flash->area_id = area_id;
150 flash->offset = offset;
151 flash->io.open = io_open;
152 flash->io.close = io_close;
153 flash->io.block_start = block_start;
154 flash->io.block_end = NULL;
155 flash->io.rd = rd_chunk;
156 flash->io.wr = wr_chunk;
157
158 return 0;
159 }
160