1 /*
2  * Copyright (c) 2022 Lukasz Majewski, DENX Software Engineering GmbH
3  * Copyright (c) 2019 Peter Bigot Consulting, LLC
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /* Sample which uses the filesystem API with littlefs */
9 
10 #include <stdio.h>
11 
12 #include <zephyr/kernel.h>
13 #include <zephyr/device.h>
14 #include <zephyr/fs/fs.h>
15 #include <zephyr/fs/littlefs.h>
16 #include <zephyr/logging/log.h>
17 #include <zephyr/storage/flash_map.h>
18 
19 LOG_MODULE_REGISTER(main);
20 
21 /* Matches LFS_NAME_MAX */
22 #define MAX_PATH_LEN 255
23 #define TEST_FILE_SIZE 547
24 
25 static uint8_t file_test_pattern[TEST_FILE_SIZE];
lsdir(const char * path)26 static int lsdir(const char *path)
27 {
28 	int res;
29 	struct fs_dir_t dirp;
30 	static struct fs_dirent entry;
31 
32 	fs_dir_t_init(&dirp);
33 
34 	/* Verify fs_opendir() */
35 	res = fs_opendir(&dirp, path);
36 	if (res) {
37 		LOG_ERR("Error opening dir %s [%d]\n", path, res);
38 		return res;
39 	}
40 
41 	LOG_PRINTK("\nListing dir %s ...\n", path);
42 	for (;;) {
43 		/* Verify fs_readdir() */
44 		res = fs_readdir(&dirp, &entry);
45 
46 		/* entry.name[0] == 0 means end-of-dir */
47 		if (res || entry.name[0] == 0) {
48 			if (res < 0) {
49 				LOG_ERR("Error reading dir [%d]\n", res);
50 			}
51 			break;
52 		}
53 
54 		if (entry.type == FS_DIR_ENTRY_DIR) {
55 			LOG_PRINTK("[DIR ] %s\n", entry.name);
56 		} else {
57 			LOG_PRINTK("[FILE] %s (size = %zu)\n",
58 				   entry.name, entry.size);
59 		}
60 	}
61 
62 	/* Verify fs_closedir() */
63 	fs_closedir(&dirp);
64 
65 	return res;
66 }
67 
littlefs_increase_infile_value(char * fname)68 static int littlefs_increase_infile_value(char *fname)
69 {
70 	uint8_t boot_count = 0;
71 	struct fs_file_t file;
72 	int rc, ret;
73 
74 	fs_file_t_init(&file);
75 	rc = fs_open(&file, fname, FS_O_CREATE | FS_O_RDWR);
76 	if (rc < 0) {
77 		LOG_ERR("FAIL: open %s: %d", fname, rc);
78 		return rc;
79 	}
80 
81 	rc = fs_read(&file, &boot_count, sizeof(boot_count));
82 	if (rc < 0) {
83 		LOG_ERR("FAIL: read %s: [rd:%d]", fname, rc);
84 		goto out;
85 	}
86 	LOG_PRINTK("%s read count:%u (bytes: %d)\n", fname, boot_count, rc);
87 
88 	rc = fs_seek(&file, 0, FS_SEEK_SET);
89 	if (rc < 0) {
90 		LOG_ERR("FAIL: seek %s: %d", fname, rc);
91 		goto out;
92 	}
93 
94 	boot_count += 1;
95 	rc = fs_write(&file, &boot_count, sizeof(boot_count));
96 	if (rc < 0) {
97 		LOG_ERR("FAIL: write %s: %d", fname, rc);
98 		goto out;
99 	}
100 
101 	LOG_PRINTK("%s write new boot count %u: [wr:%d]\n", fname,
102 		   boot_count, rc);
103 
104  out:
105 	ret = fs_close(&file);
106 	if (ret < 0) {
107 		LOG_ERR("FAIL: close %s: %d", fname, ret);
108 		return ret;
109 	}
110 
111 	return (rc < 0 ? rc : 0);
112 }
113 
incr_pattern(uint8_t * p,uint16_t size,uint8_t inc)114 static void incr_pattern(uint8_t *p, uint16_t size, uint8_t inc)
115 {
116 	uint8_t fill = 0x55;
117 
118 	if (p[0] % 2 == 0) {
119 		fill = 0xAA;
120 	}
121 
122 	for (int i = 0; i < (size - 1); i++) {
123 		if (i % 8 == 0) {
124 			p[i] += inc;
125 		} else {
126 			p[i] = fill;
127 		}
128 	}
129 
130 	p[size - 1] += inc;
131 }
132 
init_pattern(uint8_t * p,uint16_t size)133 static void init_pattern(uint8_t *p, uint16_t size)
134 {
135 	uint8_t v = 0x1;
136 
137 	memset(p, 0x55, size);
138 
139 	for (int i = 0; i < size; i += 8) {
140 		p[i] = v++;
141 	}
142 
143 	p[size - 1] = 0xAA;
144 }
145 
print_pattern(uint8_t * p,uint16_t size)146 static void print_pattern(uint8_t *p, uint16_t size)
147 {
148 	int i, j = size / 16, k;
149 
150 	for (k = 0, i = 0; k < j; i += 16, k++) {
151 		LOG_PRINTK("%02x %02x %02x %02x %02x %02x %02x %02x ",
152 			   p[i], p[i+1], p[i+2], p[i+3],
153 			   p[i+4], p[i+5], p[i+6], p[i+7]);
154 		LOG_PRINTK("%02x %02x %02x %02x %02x %02x %02x %02x\n",
155 			   p[i+8], p[i+9], p[i+10], p[i+11],
156 			   p[i+12], p[i+13], p[i+14], p[i+15]);
157 
158 		/* Mark 512B (sector) chunks of the test file */
159 		if ((k + 1) % 32 == 0) {
160 			LOG_PRINTK("\n");
161 		}
162 	}
163 
164 	for (; i < size; i++) {
165 		LOG_PRINTK("%02x ", p[i]);
166 	}
167 
168 	LOG_PRINTK("\n");
169 }
170 
littlefs_binary_file_adj(char * fname)171 static int littlefs_binary_file_adj(char *fname)
172 {
173 	struct fs_dirent dirent;
174 	struct fs_file_t file;
175 	int rc, ret;
176 
177 	/*
178 	 * Uncomment below line to force re-creation of the test pattern
179 	 * file on the littlefs FS.
180 	 */
181 	/* fs_unlink(fname); */
182 	fs_file_t_init(&file);
183 
184 	rc = fs_open(&file, fname, FS_O_CREATE | FS_O_RDWR);
185 	if (rc < 0) {
186 		LOG_ERR("FAIL: open %s: %d", fname, rc);
187 		return rc;
188 	}
189 
190 	rc = fs_stat(fname, &dirent);
191 	if (rc < 0) {
192 		LOG_ERR("FAIL: stat %s: %d", fname, rc);
193 		goto out;
194 	}
195 
196 	/* Check if the file exists - if not just write the pattern */
197 	if (rc == 0 && dirent.type == FS_DIR_ENTRY_FILE && dirent.size == 0) {
198 		LOG_INF("Test file: %s not found, create one!",
199 			fname);
200 		init_pattern(file_test_pattern, sizeof(file_test_pattern));
201 	} else {
202 		rc = fs_read(&file, file_test_pattern,
203 			     sizeof(file_test_pattern));
204 		if (rc < 0) {
205 			LOG_ERR("FAIL: read %s: [rd:%d]",
206 				fname, rc);
207 			goto out;
208 		}
209 		incr_pattern(file_test_pattern, sizeof(file_test_pattern), 0x1);
210 	}
211 
212 	LOG_PRINTK("------ FILE: %s ------\n", fname);
213 	print_pattern(file_test_pattern, sizeof(file_test_pattern));
214 
215 	rc = fs_seek(&file, 0, FS_SEEK_SET);
216 	if (rc < 0) {
217 		LOG_ERR("FAIL: seek %s: %d", fname, rc);
218 		goto out;
219 	}
220 
221 	rc = fs_write(&file, file_test_pattern, sizeof(file_test_pattern));
222 	if (rc < 0) {
223 		LOG_ERR("FAIL: write %s: %d", fname, rc);
224 	}
225 
226  out:
227 	ret = fs_close(&file);
228 	if (ret < 0) {
229 		LOG_ERR("FAIL: close %s: %d", fname, ret);
230 		return ret;
231 	}
232 
233 	return (rc < 0 ? rc : 0);
234 }
235 
236 #ifdef CONFIG_APP_LITTLEFS_STORAGE_FLASH
littlefs_flash_erase(unsigned int id)237 static int littlefs_flash_erase(unsigned int id)
238 {
239 	const struct flash_area *pfa;
240 	int rc;
241 
242 	rc = flash_area_open(id, &pfa);
243 	if (rc < 0) {
244 		LOG_ERR("FAIL: unable to find flash area %u: %d\n",
245 			id, rc);
246 		return rc;
247 	}
248 
249 	LOG_PRINTK("Area %u at 0x%x on %s for %u bytes\n",
250 		   id, (unsigned int)pfa->fa_off, pfa->fa_dev->name,
251 		   (unsigned int)pfa->fa_size);
252 
253 	/* Optional wipe flash contents */
254 	if (IS_ENABLED(CONFIG_APP_WIPE_STORAGE)) {
255 		rc = flash_area_flatten(pfa, 0, pfa->fa_size);
256 		LOG_ERR("Erasing flash area ... %d", rc);
257 	}
258 
259 	flash_area_close(pfa);
260 	return rc;
261 }
262 #define PARTITION_NODE DT_NODELABEL(lfs1)
263 
264 #if DT_NODE_EXISTS(PARTITION_NODE)
265 FS_FSTAB_DECLARE_ENTRY(PARTITION_NODE);
266 #else /* PARTITION_NODE */
267 FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage);
268 static struct fs_mount_t lfs_storage_mnt = {
269 	.type = FS_LITTLEFS,
270 	.fs_data = &storage,
271 	.storage_dev = (void *)FIXED_PARTITION_ID(storage_partition),
272 	.mnt_point = "/lfs",
273 };
274 #endif /* PARTITION_NODE */
275 
276 	struct fs_mount_t *mountpoint =
277 #if DT_NODE_EXISTS(PARTITION_NODE)
278 		&FS_FSTAB_ENTRY(PARTITION_NODE)
279 #else
280 		&lfs_storage_mnt
281 #endif
282 		;
283 
littlefs_mount(struct fs_mount_t * mp)284 static int littlefs_mount(struct fs_mount_t *mp)
285 {
286 	int rc;
287 
288 	rc = littlefs_flash_erase((uintptr_t)mp->storage_dev);
289 	if (rc < 0) {
290 		return rc;
291 	}
292 
293 	/* Do not mount if auto-mount has been enabled */
294 #if !DT_NODE_EXISTS(PARTITION_NODE) ||						\
295 	!(FSTAB_ENTRY_DT_MOUNT_FLAGS(PARTITION_NODE) & FS_MOUNT_FLAG_AUTOMOUNT)
296 	rc = fs_mount(mp);
297 	if (rc < 0) {
298 		LOG_PRINTK("FAIL: mount id %" PRIuPTR " at %s: %d\n",
299 		       (uintptr_t)mp->storage_dev, mp->mnt_point, rc);
300 		return rc;
301 	}
302 	LOG_PRINTK("%s mount: %d\n", mp->mnt_point, rc);
303 #else
304 	LOG_PRINTK("%s automounted\n", mp->mnt_point);
305 #endif
306 
307 	return 0;
308 }
309 #endif /* CONFIG_APP_LITTLEFS_STORAGE_FLASH */
310 
311 #ifdef CONFIG_APP_LITTLEFS_STORAGE_BLK_SDMMC
312 
313 #if defined(CONFIG_DISK_DRIVER_SDMMC)
314 #define DISK_NAME "SD"
315 #elif defined(CONFIG_DISK_DRIVER_MMC)
316 #define DISK_NAME "SD2"
317 #else
318 #error "No disk device defined, is your board supported?"
319 #endif
320 
321 struct fs_littlefs lfsfs;
322 static struct fs_mount_t __mp = {
323 	.type = FS_LITTLEFS,
324 	.fs_data = &lfsfs,
325 	.flags = FS_MOUNT_FLAG_USE_DISK_ACCESS,
326 };
327 struct fs_mount_t *mountpoint = &__mp;
328 
littlefs_mount(struct fs_mount_t * mp)329 static int littlefs_mount(struct fs_mount_t *mp)
330 {
331 	static const char *disk_mount_pt = "/"DISK_NAME":";
332 	static const char *disk_pdrv = DISK_NAME;
333 
334 	mp->storage_dev = (void *)disk_pdrv;
335 	mp->mnt_point = disk_mount_pt;
336 
337 	return fs_mount(mp);
338 }
339 #endif /* CONFIG_APP_LITTLEFS_STORAGE_BLK_SDMMC */
340 
main(void)341 int main(void)
342 {
343 	char fname1[MAX_PATH_LEN];
344 	char fname2[MAX_PATH_LEN];
345 	struct fs_statvfs sbuf;
346 	int rc;
347 
348 	LOG_PRINTK("Sample program to r/w files on littlefs\n");
349 
350 	rc = littlefs_mount(mountpoint);
351 	if (rc < 0) {
352 		return 0;
353 	}
354 
355 	snprintf(fname1, sizeof(fname1), "%s/boot_count", mountpoint->mnt_point);
356 	snprintf(fname2, sizeof(fname2), "%s/pattern.bin", mountpoint->mnt_point);
357 
358 	rc = fs_statvfs(mountpoint->mnt_point, &sbuf);
359 	if (rc < 0) {
360 		LOG_PRINTK("FAIL: statvfs: %d\n", rc);
361 		goto out;
362 	}
363 
364 	LOG_PRINTK("%s: bsize = %lu ; frsize = %lu ;"
365 		   " blocks = %lu ; bfree = %lu\n",
366 		   mountpoint->mnt_point,
367 		   sbuf.f_bsize, sbuf.f_frsize,
368 		   sbuf.f_blocks, sbuf.f_bfree);
369 
370 	rc = lsdir(mountpoint->mnt_point);
371 	if (rc < 0) {
372 		LOG_PRINTK("FAIL: lsdir %s: %d\n", mountpoint->mnt_point, rc);
373 		goto out;
374 	}
375 
376 	rc = littlefs_increase_infile_value(fname1);
377 	if (rc) {
378 		goto out;
379 	}
380 
381 	rc = littlefs_binary_file_adj(fname2);
382 	if (rc) {
383 		goto out;
384 	}
385 
386 out:
387 	rc = fs_unmount(mountpoint);
388 	LOG_PRINTK("%s unmount: %d\n", mountpoint->mnt_point, rc);
389 	return 0;
390 }
391