1 /*
2  * Copyright 2017-2020 NXP
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <stddef.h>
8 #include <stdbool.h>
9 #include <stdint.h>
10 #include <string.h>
11 
12 #ifdef SDK_OS_FREE_RTOS
13 #include "FreeRTOS.h"
14 #endif
15 
16 #include "mflash_file.h"
17 #include "mflash_drv.h"
18 #include "fsl_common.h"
19 
20 /* Magic numbers to check for presence of the structures below */
21 #define MFLASH_DIR_MAGIC_NO  (0xF17E07ABu)
22 #define MFLASH_META_MAGIC_NO (0xABECEDA8u)
23 #define MFLASH_FS_VERSION    (0x00010000u)
24 #define MFLASH_BLANK_PATTERN (0xFFu)
25 
26 #if defined(__CC_ARM) || defined(__ARMCC_VERSION)
27 // linker symbols imported as described in https://www.keil.com/support/man/docs/armlink/armlink_pge1362065952432.htm
28 extern char Image$$mflash_fs$$Base[];
29 #define MFLASH_FS_START ((void *)Image$$mflash_fs$$Base)
30 #else
31 extern char __MFLASH_FS_START[];
32 #define MFLASH_FS_START ((void *)__MFLASH_FS_START)
33 #endif
34 
35 /*
36  * The table header and table record structures have to be aligned
37  * with pages/sectors that are expected to be of 2**n size, hence there is some padding
38  */
39 typedef struct
40 {
41     uint32_t file_offset;
42     uint32_t alloc_size;
43     uint8_t path[MFLASH_MAX_PATH_LEN];
44 } mflash_dir_record_t;
45 
46 typedef struct
47 {
48     uint32_t magic_no;
49     uint32_t version;
50     uint32_t page_size;
51     uint32_t sector_size;
52     uint32_t file_count;
53     uint32_t total_size;
54     uint8_t padding[sizeof(mflash_dir_record_t) - 6 * sizeof(uint32_t)];
55 } mflash_fs_header_t;
56 
57 typedef struct
58 {
59     mflash_fs_header_t header;
60     mflash_dir_record_t records[];
61 } mflash_fs_t;
62 
63 /* Metadata prepended to the file itself to identify valid (already written) file and keep actual length of the file */
64 typedef struct
65 {
66     uint32_t file_size;
67     uint32_t magic_no;
68 } mflash_file_meta_t;
69 
70 /* Pointer to the filesystem */
71 static mflash_fs_t *g_mflash_fs = NULL;
72 
73 /* API - True if mflash is already initialized */
mflash_is_initialized(void)74 bool mflash_is_initialized(void)
75 {
76     return (g_mflash_fs != NULL);
77 }
78 
79 /* Store path string to directory record structure */
dir_path_store(mflash_dir_record_t * dr,char * path)80 static bool dir_path_store(mflash_dir_record_t *dr, char *path)
81 {
82     assert(dr);
83     assert(path);
84 
85     for (int i = 0; i < MFLASH_MAX_PATH_LEN; i++)
86     {
87         dr->path[i] = (uint8_t)(*path);
88 
89         /* End of string, exit the loop */
90         if (*path == '\0')
91         {
92             break;
93         }
94 
95         path++;
96     }
97 
98     /* Check whether the whole given path string was processed */
99     if (*path != '\0')
100     {
101         return false;
102     }
103 
104     return true;
105 }
106 
107 /* Match path string against directory record */
dir_path_match(mflash_dir_record_t * dr,char * path)108 static bool dir_path_match(mflash_dir_record_t *dr, char *path)
109 {
110     assert(dr);
111     assert(path);
112 
113     for (int i = 0; i < MFLASH_MAX_PATH_LEN; i++)
114     {
115         if (dr->path[i] != (uint8_t)(*path))
116         {
117             return false;
118         }
119 
120         /* End of string, there is match */
121         if (*path == '\0')
122         {
123             return true;
124         }
125 
126         path++;
127     }
128 
129     /* Check whether the whole given path string was processed */
130     if (*path != '\0')
131         return false;
132 
133     return true;
134 }
135 
136 /* Buffer allocation wrapper */
mflash_page_buf_get(void)137 static void *mflash_page_buf_get(void)
138 {
139     void *page_buf;
140 #ifdef SDK_OS_FREE_RTOS
141     page_buf = pvPortMalloc(MFLASH_PAGE_SIZE);
142 #else
143     page_buf = malloc(MFLASH_PAGE_SIZE);
144 #endif
145     return page_buf;
146 }
147 
148 /* Buffer allocation wrapper */
mflash_page_buf_release(void * page_buf)149 static void mflash_page_buf_release(void *page_buf)
150 {
151 #ifdef SDK_OS_FREE_RTOS
152     vPortFree(page_buf);
153 #else
154     free(page_buf);
155 #endif
156 }
157 
158 /* Low level abstraction - erase sector of the filesystem */
mflash_fs_sector_erase(mflash_fs_t * fs,uint32_t sector_offset)159 static status_t mflash_fs_sector_erase(mflash_fs_t *fs, uint32_t sector_offset)
160 {
161     uint32_t phys_addr;
162 
163     /* Translate filesystem offset to physical address in FLASH */
164     phys_addr = mflash_drv_log2phys((uint8_t *)fs + sector_offset, MFLASH_SECTOR_SIZE);
165     if (phys_addr == MFLASH_INVALID_ADDRESS)
166     {
167         return kStatus_Fail;
168     }
169 
170     return mflash_drv_sector_erase(phys_addr);
171 }
172 
173 /* Low level abstraction - program page of the filesystem */
mflash_fs_page_program(mflash_fs_t * fs,uint32_t page_offset,uint32_t * data)174 static status_t mflash_fs_page_program(mflash_fs_t *fs, uint32_t page_offset, uint32_t *data)
175 {
176     uint32_t phys_addr;
177 
178     /* Translate filesystem offset to physical address in FLASH */
179     phys_addr = mflash_drv_log2phys((uint8_t *)fs + page_offset, MFLASH_PAGE_SIZE);
180     if (phys_addr == MFLASH_INVALID_ADDRESS)
181     {
182         return kStatus_Fail;
183     }
184 
185     return mflash_drv_page_program(phys_addr, data);
186 }
187 
188 /* Low level abstraction - get pointer to filesystem location specified by offset */
mflash_fs_get_ptr(mflash_fs_t * fs,uint32_t offset)189 static inline void *mflash_fs_get_ptr(mflash_fs_t *fs, uint32_t offset)
190 {
191     return (void *)((uint8_t *)fs + offset);
192 }
193 
194 /*
195  * Check whether give area of FLASH is readable by direct pointer access.
196  * This is necessary on plaforms featuring page checksums as access to page containing invalid data may result in a
197  * hardfault.
198  */
mflash_readable_check(void * ptr,uint32_t size)199 static status_t mflash_readable_check(void *ptr, uint32_t size)
200 {
201 #if defined(MFLASH_PAGE_INTEGRITY_CHECKS) && MFLASH_PAGE_INTEGRITY_CHECKS
202     status_t status;
203 
204     uintptr_t start_addr = (uintptr_t)ptr - (uintptr_t)ptr % MFLASH_PAGE_SIZE;
205     uintptr_t end_addr   = (uintptr_t)ptr + size;
206 
207     for (uintptr_t check_addr = start_addr; check_addr < end_addr; check_addr += MFLASH_PAGE_SIZE)
208     {
209         status = mflash_drv_is_readable(check_addr);
210         if (status != kStatus_Success)
211         {
212             return status;
213         }
214     }
215 
216     return kStatus_Success;
217 #else
218     return kStatus_Success;
219 #endif
220 }
221 
222 /* Check for filesystem presence and validity */
mflash_fs_check(mflash_fs_t * fs)223 static status_t mflash_fs_check(mflash_fs_t *fs)
224 {
225     status_t status;
226 
227     /* Check params */
228     if (fs == NULL)
229     {
230         return kStatus_InvalidArgument;
231     }
232 
233     /* Check readability before accessing filesystem structure by pointer */
234     status = mflash_readable_check(fs, sizeof(mflash_fs_header_t));
235     if (status != kStatus_Success)
236     {
237         return status;
238     }
239 
240     /* Check magic */
241     if (fs->header.magic_no != MFLASH_DIR_MAGIC_NO)
242     {
243         return kStatus_Fail;
244     }
245 
246     /* Check major version */
247     if ((fs->header.version & 0xFFFF0000u) != (MFLASH_FS_VERSION & 0xFFFF0000u))
248     {
249         return kStatus_Fail;
250     }
251 
252     /* Check FLASH memory characteristics */
253     if (fs->header.page_size != MFLASH_PAGE_SIZE || fs->header.sector_size != MFLASH_SECTOR_SIZE)
254     {
255         return kStatus_Fail;
256     }
257 
258     /* Check readability of the whole directory */
259     status =
260         mflash_readable_check(fs, sizeof(mflash_fs_header_t) + fs->header.file_count * sizeof(mflash_dir_record_t));
261 
262     return status;
263 }
264 
265 /* Check for presence of a file data */
mflash_file_check(mflash_fs_t * fs,mflash_dir_record_t * dr)266 static status_t mflash_file_check(mflash_fs_t *fs, mflash_dir_record_t *dr)
267 {
268     status_t status;
269     mflash_file_meta_t *meta;
270 
271     /* Check params */
272     if (fs == NULL)
273     {
274         return kStatus_InvalidArgument;
275     }
276 
277     if (dr == NULL)
278     {
279         return kStatus_InvalidArgument;
280     }
281 
282     /* Get pointer to file meta structure */
283     meta = mflash_fs_get_ptr(fs, dr->file_offset);
284 
285     /* Check readability before accessing file meta structure */
286     status = mflash_readable_check(meta, sizeof(mflash_file_meta_t));
287     if (status != kStatus_Success)
288     {
289         return status;
290     }
291 
292     /* Check magic signature */
293     if (meta->magic_no != MFLASH_META_MAGIC_NO)
294     {
295         return kStatus_Fail;
296     }
297 
298     /* Check wheter actual file size in meta fits the pre-allocated area */
299     if (meta->file_size + sizeof(mflash_file_meta_t) > dr->alloc_size)
300     {
301         return kStatus_Fail;
302     }
303 
304     /* Check readability of the whole file */
305     status = mflash_readable_check(meta, sizeof(mflash_file_meta_t) + meta->file_size);
306 
307     return kStatus_Success;
308 }
309 
310 /* Searches for directory record with given path and retrieves a copy of it */
mflash_dir_lookup(mflash_fs_t * fs,char * path,mflash_dir_record_t * dr_ptr)311 static status_t mflash_dir_lookup(mflash_fs_t *fs, char *path, mflash_dir_record_t *dr_ptr)
312 {
313     uint32_t file_count     = fs->header.file_count;
314     mflash_dir_record_t *dr = fs->records;
315 
316     for (int i = 0; i < file_count; i++)
317     {
318         if (dir_path_match(dr, path))
319         {
320             if (NULL != dr_ptr)
321             {
322                 *dr_ptr = *dr;
323             }
324             return kStatus_Success;
325         }
326         dr++;
327     }
328 
329     return kStatus_Fail;
330 }
331 
332 /* Create filesystem structure in FLASH according to given directory template */
mflash_format_internal(mflash_fs_t * fs,void * page_buf,uint32_t fs_size_limit,const mflash_file_t * dir_template)333 static status_t mflash_format_internal(mflash_fs_t *fs,
334                                        void *page_buf,
335                                        uint32_t fs_size_limit,
336                                        const mflash_file_t *dir_template)
337 {
338     status_t status;
339 
340     uint32_t file_count;
341     uint32_t total_sectors;
342 
343     uint32_t dir_size;
344     uint32_t dir_sectors;
345 
346     uint32_t file_offset;
347     uint32_t dir_offset;
348 
349     mflash_fs_header_t *fsh;
350 
351     /* The directory records shall be aligned to page size */
352     assert((MFLASH_PAGE_SIZE % sizeof(mflash_dir_record_t)) == 0u);
353 
354     /* Count the files and calculate number of FLASH sectors to be occupied by the filesystem */
355     file_count    = 0;
356     total_sectors = 0;
357     for (const mflash_file_t *dt = dir_template; (NULL != dt->path) && ('\0' != dt->path[0]) && (0 != dt->max_size);
358          dt++)
359     {
360         /* Calculate number of sectors to be occupied by the file */
361         uint32_t file_sectors = (dt->max_size + MFLASH_SECTOR_SIZE - 1) / MFLASH_SECTOR_SIZE;
362         total_sectors += file_sectors;
363         file_count++;
364     }
365 
366     dir_size    = file_count * sizeof(mflash_dir_record_t) + sizeof(mflash_fs_header_t);
367     dir_sectors = (dir_size + MFLASH_SECTOR_SIZE - 1) / MFLASH_SECTOR_SIZE;
368     total_sectors += dir_sectors;
369 
370     /* Check whether the filestytem fits into the given FLASH area */
371     if ((0 != fs_size_limit) && (fs_size_limit < total_sectors * MFLASH_SECTOR_SIZE))
372     {
373         return kStatus_OutOfRange;
374     }
375 
376     /* Erase the whole FLASH area to be occupied by the filesystem */
377     for (int i = 0; i < total_sectors; i++)
378     {
379         status = mflash_fs_sector_erase(fs, i * MFLASH_SECTOR_SIZE);
380         if (status != kStatus_Success)
381         {
382             return status;
383         }
384     }
385 
386     /* Clear the page buffer and set inital values for offsets */
387     memset(page_buf, MFLASH_BLANK_PATTERN, MFLASH_PAGE_SIZE);
388     dir_offset  = file_count * sizeof(mflash_dir_record_t) + sizeof(mflash_fs_header_t);
389     file_offset = total_sectors * MFLASH_SECTOR_SIZE;
390 
391     /* Create directory entries in reverse order so that programming of the page containing the dir header is the last
392      * step */
393     for (int fi = file_count; 0 != fi--;)
394     {
395         /* Check for enough space for the directory record */
396         assert(dir_offset >= sizeof(mflash_dir_record_t));
397 
398         dir_offset -= sizeof(mflash_dir_record_t);
399 
400         mflash_dir_record_t *dr = (mflash_dir_record_t *)((uint8_t *)page_buf + (dir_offset % MFLASH_PAGE_SIZE));
401         const mflash_file_t *dt = &dir_template[fi];
402 
403         /* Calculate number of sectors to be occupied by the file */
404         uint32_t file_sectors = (dt->max_size + MFLASH_SECTOR_SIZE - 1) / MFLASH_SECTOR_SIZE;
405 
406         /* Fill in directory record */
407         dr->alloc_size  = file_sectors * MFLASH_SECTOR_SIZE;
408         dr->file_offset = (file_offset -= dr->alloc_size);
409         dir_path_store(dr, dt->path);
410 
411         if (dir_offset % MFLASH_PAGE_SIZE == 0u)
412         {
413             /* We reached the beginning of a page, program it and start over */
414             status = mflash_fs_page_program(fs, dir_offset, page_buf);
415             if (status != kStatus_Success)
416             {
417                 return status;
418             }
419 
420             /* Clear the page buffer */
421             memset(page_buf, MFLASH_BLANK_PATTERN, MFLASH_PAGE_SIZE);
422         }
423     }
424 
425     /* There should be space left exactly for the filesystem header */
426     assert(dir_offset == sizeof(mflash_fs_header_t));
427 
428     /* Create filesystem header at the very beginning of the first page */
429     fsh              = (mflash_fs_header_t *)page_buf;
430     fsh->magic_no    = MFLASH_DIR_MAGIC_NO;
431     fsh->version     = MFLASH_FS_VERSION;
432     fsh->page_size   = MFLASH_PAGE_SIZE;
433     fsh->sector_size = MFLASH_SECTOR_SIZE;
434     fsh->total_size  = total_sectors * MFLASH_SECTOR_SIZE;
435     fsh->file_count  = file_count;
436 
437     /* Programming of the first page puts header into place marking the filesystem as valid */
438     status = mflash_fs_page_program(fs, 0, page_buf);
439 
440     return status;
441 }
442 
443 /* Create filesystem structure in FLASH according to given directory template */
mflash_format(mflash_fs_t * fs,uint32_t fs_size_limit,const mflash_file_t * dir_template)444 static status_t mflash_format(mflash_fs_t *fs, uint32_t fs_size_limit, const mflash_file_t *dir_template)
445 {
446     status_t status;
447     void *page_buf;
448 
449     /* Check parameters */
450     if (dir_template == NULL)
451     {
452         return kStatus_InvalidArgument;
453     }
454 
455     /* Get page buffer for FLASH writes */
456     page_buf = mflash_page_buf_get();
457     if (page_buf == NULL)
458     {
459         return kStatus_Fail;
460     }
461 
462     /* Actual formatting of the filesystem */
463     status = mflash_format_internal(fs, page_buf, fs_size_limit, dir_template);
464 
465     /* Release page buffer */
466     mflash_page_buf_release(page_buf);
467 
468     return status;
469 }
470 
471 /* Match dir against given template. Checks whether all files defined in the template are pre-allocsated in the fs
472  * directory */
mflash_template_match(mflash_fs_t * fs,const mflash_file_t * dir_template)473 static status_t mflash_template_match(mflash_fs_t *fs, const mflash_file_t *dir_template)
474 {
475     status_t status;
476 
477     for (const mflash_file_t *dt = dir_template; (NULL != dt->path) && ('\0' != dt->path[0]) && (0 != dt->max_size);
478          dt++)
479     {
480         mflash_dir_record_t dr;
481 
482         /* Lookup directory record */
483         status = mflash_dir_lookup(fs, dt->path, &dr);
484         if (status != kStatus_Success)
485         {
486             return status;
487         }
488 
489         /* Check whether pre-allocated size is sufficient */
490         if (dr.alloc_size < dt->max_size)
491         {
492             return kStatus_Fail;
493         }
494     }
495 
496     return kStatus_Success;
497 }
498 
499 /* Initialize mflash filesystem */
mflash_fs_init(mflash_fs_t * fs,uint32_t fs_size_limit,const mflash_file_t * dir_template)500 static status_t mflash_fs_init(mflash_fs_t *fs, uint32_t fs_size_limit, const mflash_file_t *dir_template)
501 {
502     status_t status;
503 
504     /* Check whether there is a filesystem header and directory already in place */
505     status = mflash_fs_check(fs);
506 
507     /* Filesystem is valid, check whether its directory provides records for all required files */
508     if (status == kStatus_Success)
509     {
510         status = mflash_template_match(fs, dir_template);
511     }
512 
513     /* The filesystem not present or does not fit the template, create a new one */
514     if (status == kStatus_Fail) /* Error codes other then 'Fail' are not captured here but rather intentinally passed to
515                                    the caller */
516     {
517         status = mflash_format(fs, fs_size_limit, dir_template); /* Format the filestem */
518     }
519 
520     if (status == kStatus_Success)
521     {
522         g_mflash_fs = fs; /* If all went ok, keep pointer to the filesytem */
523     }
524 
525     return status;
526 }
527 
528 /* API - Initialize mflash driver and filesystem at default address specified by linker symbol */
mflash_init(const mflash_file_t * dir_template,bool init_drv)529 status_t mflash_init(const mflash_file_t *dir_template, bool init_drv)
530 {
531     status_t status;
532     mflash_fs_t *fs;
533 
534     /* Initialize the driver */
535     if (init_drv)
536     {
537         status = mflash_drv_init();
538         if (status == kStatus_Fail)
539         {
540             return status;
541         }
542     }
543 
544 #ifdef MFLASH_FILE_BASEADDR
545     /* Convert physical address in FLASH to memory pointer */
546     fs = (mflash_fs_t *)mflash_drv_phys2log(MFLASH_FILE_BASEADDR, 0);
547 #else
548     /* Otherwise take address from linker file */
549     fs = (mflash_fs_t *)MFLASH_FS_START;
550 #endif
551 
552     if (fs == NULL)
553     {
554         return kStatus_Fail;
555     }
556 
557     return mflash_fs_init(fs, 0, dir_template);
558 }
559 
560 /* Save file */
mflash_file_save_internal(mflash_fs_t * fs,void * page_buf,mflash_dir_record_t * dr,uint8_t * data,uint32_t size)561 static status_t mflash_file_save_internal(
562     mflash_fs_t *fs, void *page_buf, mflash_dir_record_t *dr, uint8_t *data, uint32_t size)
563 {
564     status_t status;
565 
566     /* Check whether the data + meta fits into the pre-allocated file area */
567     if (size + sizeof(mflash_file_meta_t) > dr->alloc_size)
568     {
569         return kStatus_OutOfRange;
570     }
571 
572     /* Erase the whole file area sector by sector */
573     for (uint32_t sector_offset = 0; sector_offset < dr->alloc_size; sector_offset += MFLASH_SECTOR_SIZE)
574     {
575         /* Erase the sector */
576         status = mflash_fs_sector_erase(fs, dr->file_offset + sector_offset);
577         if (status != kStatus_Success)
578         {
579             return status;
580         }
581     }
582 
583     /* Program the file data page by page, skipping the first page containing meta that is going to be programmed in the
584      * last step */
585     for (int data_offset = MFLASH_PAGE_SIZE - sizeof(mflash_file_meta_t); data_offset < size;
586          data_offset += MFLASH_PAGE_SIZE)
587     {
588         /* Pointer and size of the data portion to be programmed */
589         void *copy_ptr     = data + data_offset;
590         uint32_t copy_size = size - data_offset;
591         if (copy_size > MFLASH_PAGE_SIZE)
592         {
593             copy_size = MFLASH_PAGE_SIZE;
594         }
595 
596         memset(page_buf, MFLASH_BLANK_PATTERN, MFLASH_PAGE_SIZE);
597         memcpy(page_buf, copy_ptr, copy_size);
598 
599         /* Data offset is off by sizeof(mflash_file_meta_t) as this structure occupies the very beginning of the first
600          * page */
601         status = mflash_fs_page_program(fs, dr->file_offset + data_offset + sizeof(mflash_file_meta_t), page_buf);
602         if (status != kStatus_Success)
603         {
604             return status;
605         }
606     }
607 
608     /* Prepare the missing portion of data to be programme to the first page */
609     uint32_t copy_size = size;
610     if (copy_size > MFLASH_PAGE_SIZE - sizeof(mflash_file_meta_t))
611     {
612         copy_size = MFLASH_PAGE_SIZE - sizeof(mflash_file_meta_t);
613     }
614 
615     memset(page_buf, MFLASH_BLANK_PATTERN, MFLASH_PAGE_SIZE);
616     memcpy((uint8_t *)page_buf + sizeof(mflash_file_meta_t), data, copy_size);
617 
618     /* Set file metadata */
619     mflash_file_meta_t *meta = (mflash_file_meta_t *)page_buf;
620     meta->file_size          = size;
621     meta->magic_no           = MFLASH_META_MAGIC_NO;
622 
623     /* Program the first page putting the metadata in place which marks the file as valid */
624     status = mflash_fs_page_program(fs, dr->file_offset, page_buf);
625 
626     return status;
627 }
628 
629 /* API, save data to file with given path */
mflash_file_save(char * path,uint8_t * data,uint32_t size)630 status_t mflash_file_save(char *path, uint8_t *data, uint32_t size)
631 {
632     status_t status;
633     mflash_dir_record_t dr;
634     mflash_fs_t *fs = g_mflash_fs;
635     void *page_buf;
636 
637     if (path == NULL)
638     {
639         return kStatus_InvalidArgument;
640     }
641 
642     if (data == NULL && size != 0)
643     {
644         return kStatus_InvalidArgument;
645     }
646 
647     /* Lookup directory record */
648     status = mflash_dir_lookup(fs, path, &dr);
649     if (status != kStatus_Success)
650     {
651         return status;
652     }
653 
654     /* Get page buffer for FLASH writes */
655     page_buf = mflash_page_buf_get();
656     if (page_buf == NULL)
657     {
658         return kStatus_Fail;
659     }
660 
661     /* Save the file */
662     status = mflash_file_save_internal(fs, page_buf, &dr, data, size);
663 
664     /* Release page buffer */
665     mflash_page_buf_release(page_buf);
666 
667     return status;
668 }
669 
670 /* Get direct pointer to file data */
mflash_file_mmap_internal(mflash_fs_t * fs,mflash_dir_record_t * dr,uint8_t ** pdata,uint32_t * psize)671 static status_t mflash_file_mmap_internal(mflash_fs_t *fs, mflash_dir_record_t *dr, uint8_t **pdata, uint32_t *psize)
672 {
673     status_t status;
674     mflash_file_meta_t *meta;
675 
676     status = mflash_file_check(fs, dr);
677     if (status != kStatus_Success)
678     {
679         return status;
680     }
681 
682     meta = mflash_fs_get_ptr(fs, dr->file_offset);
683 
684     *pdata = (uint8_t *)meta + sizeof(*meta);
685     *psize = meta->file_size;
686 
687     return kStatus_Success;
688 }
689 
690 /* API, get direct pointer to data of file with given path */
mflash_file_mmap(char * path,uint8_t ** pdata,uint32_t * psize)691 status_t mflash_file_mmap(char *path, uint8_t **pdata, uint32_t *psize)
692 {
693     status_t status;
694     mflash_dir_record_t dr;
695     mflash_fs_t *fs = g_mflash_fs;
696 
697     if (path == NULL)
698     {
699         return kStatus_InvalidArgument;
700     }
701 
702     if (pdata == NULL || psize == NULL)
703     {
704         return kStatus_InvalidArgument;
705     }
706 
707     /* Lookup directory record */
708     status = mflash_dir_lookup(fs, path, &dr);
709     if (status != kStatus_Success)
710     {
711         return status;
712     }
713 
714     status = mflash_file_mmap_internal(fs, &dr, pdata, psize);
715 
716     return status;
717 }
718