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