|  | /* | 
|  | * Copyright (c) 2016 Intel Corporation. | 
|  | * Copyright 2024 NXP | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <zephyr/kernel.h> | 
|  | #include <zephyr/types.h> | 
|  | #include <errno.h> | 
|  | #include <zephyr/init.h> | 
|  | #include <zephyr/fs/fs.h> | 
|  | #include <zephyr/fs/fs_sys.h> | 
|  | #include <zephyr/sys/__assert.h> | 
|  | #include <ff.h> | 
|  | #include <diskio.h> | 
|  | #include <zfs_diskio.h> /* Zephyr specific FatFS API */ | 
|  | #include <zephyr/logging/log.h> | 
|  | LOG_MODULE_DECLARE(fs, CONFIG_FS_LOG_LEVEL); | 
|  |  | 
|  | #define FATFS_MAX_FILE_NAME 12 /* Uses 8.3 SFN */ | 
|  |  | 
|  | /* Memory pool for FatFs directory objects */ | 
|  | K_MEM_SLAB_DEFINE(fatfs_dirp_pool, sizeof(DIR), | 
|  | CONFIG_FS_FATFS_NUM_DIRS, 4); | 
|  |  | 
|  | /* Memory pool for FatFs file objects */ | 
|  | K_MEM_SLAB_DEFINE(fatfs_filep_pool, sizeof(FIL), | 
|  | CONFIG_FS_FATFS_NUM_FILES, 4); | 
|  |  | 
|  | static int translate_error(int error) | 
|  | { | 
|  | switch (error) { | 
|  | case FR_OK: | 
|  | return 0; | 
|  | case FR_NO_FILE: | 
|  | case FR_NO_PATH: | 
|  | case FR_INVALID_NAME: | 
|  | return -ENOENT; | 
|  | case FR_DENIED: | 
|  | return -EACCES; | 
|  | case FR_EXIST: | 
|  | return -EEXIST; | 
|  | case FR_INVALID_OBJECT: | 
|  | return -EBADF; | 
|  | case FR_WRITE_PROTECTED: | 
|  | return -EROFS; | 
|  | case FR_INVALID_DRIVE: | 
|  | case FR_NOT_ENABLED: | 
|  | case FR_NO_FILESYSTEM: | 
|  | return -ENODEV; | 
|  | case FR_NOT_ENOUGH_CORE: | 
|  | return -ENOMEM; | 
|  | case FR_TOO_MANY_OPEN_FILES: | 
|  | return -EMFILE; | 
|  | case FR_INVALID_PARAMETER: | 
|  | return -EINVAL; | 
|  | case FR_LOCKED: | 
|  | case FR_TIMEOUT: | 
|  | case FR_MKFS_ABORTED: | 
|  | case FR_DISK_ERR: | 
|  | case FR_INT_ERR: | 
|  | case FR_NOT_READY: | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | static int translate_disk_error(int error) | 
|  | { | 
|  | switch (error) { | 
|  | case RES_OK: | 
|  | return 0; | 
|  | case RES_WRPRT: | 
|  | return -EPERM; | 
|  | case RES_PARERR: | 
|  | return -EINVAL; | 
|  | case RES_NOTRDY: | 
|  | case RES_ERROR: | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | /* Converts a zephyr path like /SD:/foo into a path digestible by FATFS by stripping the | 
|  | * leading slash, i.e. SD:/foo. | 
|  | */ | 
|  | static const char *translate_path(const char *path) | 
|  | { | 
|  | /* this is guaranteed by the fs subsystem */ | 
|  | __ASSERT_NO_MSG(path[0] == '/'); | 
|  |  | 
|  | return &path[1]; | 
|  | } | 
|  |  | 
|  | static uint8_t translate_flags(fs_mode_t flags) | 
|  | { | 
|  | uint8_t fat_mode = 0; | 
|  |  | 
|  | fat_mode |= (flags & FS_O_READ) ? FA_READ : 0; | 
|  | fat_mode |= (flags & FS_O_WRITE) ? FA_WRITE : 0; | 
|  | fat_mode |= (flags & FS_O_CREATE) ? FA_OPEN_ALWAYS : 0; | 
|  | /* NOTE: FA_APPEND is not translated because FAT FS does not | 
|  | * support append semantics of the Zephyr, where file position | 
|  | * is forwarded to the end before each write, the fatfs_write | 
|  | * will be tasked with setting a file position to the end, | 
|  | * if FA_APPEND flag is present. | 
|  | */ | 
|  |  | 
|  | return fat_mode; | 
|  | } | 
|  |  | 
|  | static int fatfs_open(struct fs_file_t *zfp, const char *file_name, | 
|  | fs_mode_t mode) | 
|  | { | 
|  | FRESULT res; | 
|  | uint8_t fs_mode; | 
|  | void *ptr; | 
|  |  | 
|  | if (k_mem_slab_alloc(&fatfs_filep_pool, &ptr, K_NO_WAIT) == 0) { | 
|  | (void)memset(ptr, 0, sizeof(FIL)); | 
|  | zfp->filep = ptr; | 
|  | } else { | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | fs_mode = translate_flags(mode); | 
|  |  | 
|  | res = f_open(zfp->filep, translate_path(file_name), fs_mode); | 
|  |  | 
|  | if (res != FR_OK) { | 
|  | k_mem_slab_free(&fatfs_filep_pool, ptr); | 
|  | zfp->filep = NULL; | 
|  | } | 
|  |  | 
|  | return translate_error(res); | 
|  | } | 
|  |  | 
|  | static int fatfs_close(struct fs_file_t *zfp) | 
|  | { | 
|  | FRESULT res; | 
|  |  | 
|  | res = f_close(zfp->filep); | 
|  |  | 
|  | /* Free file ptr memory */ | 
|  | k_mem_slab_free(&fatfs_filep_pool, zfp->filep); | 
|  | zfp->filep = NULL; | 
|  |  | 
|  | return translate_error(res); | 
|  | } | 
|  |  | 
|  | static int fatfs_unlink(struct fs_mount_t *mountp, const char *path) | 
|  | { | 
|  | int res = -ENOTSUP; | 
|  |  | 
|  | #if !defined(CONFIG_FS_FATFS_READ_ONLY) | 
|  | res = f_unlink(translate_path(path)); | 
|  |  | 
|  | res = translate_error(res); | 
|  | #endif | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | static int fatfs_rename(struct fs_mount_t *mountp, const char *from, | 
|  | const char *to) | 
|  | { | 
|  | int res = -ENOTSUP; | 
|  |  | 
|  | #if !defined(CONFIG_FS_FATFS_READ_ONLY) | 
|  | FILINFO fno; | 
|  |  | 
|  | /* Check if 'to' path exists; remove it if it does */ | 
|  | res = f_stat(translate_path(to), &fno); | 
|  | if (res == FR_OK) { | 
|  | res = f_unlink(translate_path(to)); | 
|  | if (res != FR_OK) { | 
|  | return translate_error(res); | 
|  | } | 
|  | } | 
|  |  | 
|  | res = f_rename(translate_path(from), translate_path(to)); | 
|  | res = translate_error(res); | 
|  | #endif | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | static ssize_t fatfs_read(struct fs_file_t *zfp, void *ptr, size_t size) | 
|  | { | 
|  | FRESULT res; | 
|  | unsigned int br; | 
|  |  | 
|  | res = f_read(zfp->filep, ptr, size, &br); | 
|  | if (res != FR_OK) { | 
|  | return translate_error(res); | 
|  | } | 
|  |  | 
|  | return br; | 
|  | } | 
|  |  | 
|  | static ssize_t fatfs_write(struct fs_file_t *zfp, const void *ptr, size_t size) | 
|  | { | 
|  | int res = -ENOTSUP; | 
|  |  | 
|  | #if !defined(CONFIG_FS_FATFS_READ_ONLY) | 
|  | unsigned int bw; | 
|  | off_t pos = f_size((FIL *)zfp->filep); | 
|  | res = FR_OK; | 
|  |  | 
|  | /* FA_APPEND flag means that file has been opened for append. | 
|  | * The FAT FS write does not support the POSIX append semantics, | 
|  | * to always write at the end of file, so set file position | 
|  | * at the end before each write if FA_APPEND is set. | 
|  | */ | 
|  | if (zfp->flags & FS_O_APPEND) { | 
|  | res = f_lseek(zfp->filep, pos); | 
|  | } | 
|  |  | 
|  | if (res == FR_OK) { | 
|  | res = f_write(zfp->filep, ptr, size, &bw); | 
|  | } | 
|  |  | 
|  | if (res != FR_OK) { | 
|  | res = translate_error(res); | 
|  | } else { | 
|  | res = bw; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | static int fatfs_seek(struct fs_file_t *zfp, off_t offset, int whence) | 
|  | { | 
|  | FRESULT res = FR_OK; | 
|  | off_t pos; | 
|  |  | 
|  | switch (whence) { | 
|  | case FS_SEEK_SET: | 
|  | pos = offset; | 
|  | break; | 
|  | case FS_SEEK_CUR: | 
|  | pos = f_tell((FIL *)zfp->filep) + offset; | 
|  | break; | 
|  | case FS_SEEK_END: | 
|  | pos = f_size((FIL *)zfp->filep) + offset; | 
|  | break; | 
|  | default: | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if ((pos < 0) || (pos > f_size((FIL *)zfp->filep))) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | res = f_lseek(zfp->filep, pos); | 
|  |  | 
|  | return translate_error(res); | 
|  | } | 
|  |  | 
|  | static off_t fatfs_tell(struct fs_file_t *zfp) | 
|  | { | 
|  | return f_tell((FIL *)zfp->filep); | 
|  | } | 
|  |  | 
|  | static int fatfs_truncate(struct fs_file_t *zfp, off_t length) | 
|  | { | 
|  | int res = -ENOTSUP; | 
|  |  | 
|  | #if !defined(CONFIG_FS_FATFS_READ_ONLY) | 
|  | off_t cur_length = f_size((FIL *)zfp->filep); | 
|  |  | 
|  | /* f_lseek expands file if new position is larger than file size */ | 
|  | res = f_lseek(zfp->filep, length); | 
|  | if (res != FR_OK) { | 
|  | return translate_error(res); | 
|  | } | 
|  |  | 
|  | if (length < cur_length) { | 
|  | res = f_truncate(zfp->filep); | 
|  | } else { | 
|  | /* | 
|  | * Get actual length after expansion. This could be | 
|  | * less if there was not enough space in the volume | 
|  | * to expand to the requested length | 
|  | */ | 
|  | length = f_tell((FIL *)zfp->filep); | 
|  |  | 
|  | res = f_lseek(zfp->filep, cur_length); | 
|  | if (res != FR_OK) { | 
|  | return translate_error(res); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * The FS module does caching and optimization of | 
|  | * writes. Here we write 1 byte at a time to avoid | 
|  | * using additional code and memory for doing any | 
|  | * optimization. | 
|  | */ | 
|  | unsigned int bw; | 
|  | uint8_t c = 0U; | 
|  |  | 
|  | for (int i = cur_length; i < length; i++) { | 
|  | res = f_write(zfp->filep, &c, 1, &bw); | 
|  | if (res != FR_OK) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | res = translate_error(res); | 
|  | #endif | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | static int fatfs_sync(struct fs_file_t *zfp) | 
|  | { | 
|  | int res = -ENOTSUP; | 
|  |  | 
|  | #if !defined(CONFIG_FS_FATFS_READ_ONLY) | 
|  | res = f_sync(zfp->filep); | 
|  | res = translate_error(res); | 
|  | #endif | 
|  | return res; | 
|  | } | 
|  |  | 
|  | static int fatfs_mkdir(struct fs_mount_t *mountp, const char *path) | 
|  | { | 
|  | int res = -ENOTSUP; | 
|  |  | 
|  | #if !defined(CONFIG_FS_FATFS_READ_ONLY) | 
|  | res = f_mkdir(translate_path(path)); | 
|  | res = translate_error(res); | 
|  | #endif | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | static int fatfs_opendir(struct fs_dir_t *zdp, const char *path) | 
|  | { | 
|  | FRESULT res; | 
|  | void *ptr; | 
|  |  | 
|  | if (k_mem_slab_alloc(&fatfs_dirp_pool, &ptr, K_NO_WAIT) == 0) { | 
|  | (void)memset(ptr, 0, sizeof(DIR)); | 
|  | zdp->dirp = ptr; | 
|  | } else { | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | res = f_opendir(zdp->dirp, translate_path(path)); | 
|  |  | 
|  | if (res != FR_OK) { | 
|  | k_mem_slab_free(&fatfs_dirp_pool, ptr); | 
|  | zdp->dirp = NULL; | 
|  | } | 
|  |  | 
|  | return translate_error(res); | 
|  | } | 
|  |  | 
|  | static int fatfs_readdir(struct fs_dir_t *zdp, struct fs_dirent *entry) | 
|  | { | 
|  | FRESULT res; | 
|  | FILINFO fno; | 
|  |  | 
|  | res = f_readdir(zdp->dirp, &fno); | 
|  | if (res == FR_OK) { | 
|  | strcpy(entry->name, fno.fname); | 
|  | if (entry->name[0] != 0) { | 
|  | entry->type = ((fno.fattrib & AM_DIR) ? | 
|  | FS_DIR_ENTRY_DIR : FS_DIR_ENTRY_FILE); | 
|  | entry->size = fno.fsize; | 
|  | } | 
|  | } | 
|  |  | 
|  | return translate_error(res); | 
|  | } | 
|  |  | 
|  | static int fatfs_closedir(struct fs_dir_t *zdp) | 
|  | { | 
|  | FRESULT res; | 
|  |  | 
|  | res = f_closedir(zdp->dirp); | 
|  |  | 
|  | /* Free file ptr memory */ | 
|  | k_mem_slab_free(&fatfs_dirp_pool, zdp->dirp); | 
|  |  | 
|  | return translate_error(res); | 
|  | } | 
|  |  | 
|  | static int fatfs_stat(struct fs_mount_t *mountp, | 
|  | const char *path, struct fs_dirent *entry) | 
|  | { | 
|  | FRESULT res; | 
|  | FILINFO fno; | 
|  |  | 
|  | res = f_stat(translate_path(path), &fno); | 
|  | if (res == FR_OK) { | 
|  | entry->type = ((fno.fattrib & AM_DIR) ? | 
|  | FS_DIR_ENTRY_DIR : FS_DIR_ENTRY_FILE); | 
|  | strcpy(entry->name, fno.fname); | 
|  | entry->size = fno.fsize; | 
|  | } | 
|  |  | 
|  | return translate_error(res); | 
|  | } | 
|  |  | 
|  | static int fatfs_statvfs(struct fs_mount_t *mountp, | 
|  | const char *path, struct fs_statvfs *stat) | 
|  | { | 
|  | int res = -ENOTSUP; | 
|  | #if !defined(CONFIG_FS_FATFS_READ_ONLY) | 
|  | FATFS *fs; | 
|  | DWORD f_bfree = 0; | 
|  |  | 
|  | res = f_getfree(translate_path(mountp->mnt_point), &f_bfree, &fs); | 
|  | if (res != FR_OK) { | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | stat->f_bfree = f_bfree; | 
|  |  | 
|  | /* | 
|  | * If FF_MIN_SS and FF_MAX_SS differ, variable sector size support is | 
|  | * enabled and the file system object structure contains the actual sector | 
|  | * size, otherwise it is configured to a fixed value give by FF_MIN_SS. | 
|  | */ | 
|  | #if FF_MAX_SS != FF_MIN_SS | 
|  | stat->f_bsize = fs->ssize; | 
|  | #else | 
|  | stat->f_bsize = FF_MIN_SS; | 
|  | #endif | 
|  | stat->f_frsize = fs->csize * stat->f_bsize; | 
|  | stat->f_blocks = (fs->n_fatent - 2); | 
|  |  | 
|  | res = translate_error(res); | 
|  | #endif | 
|  | return res; | 
|  | } | 
|  |  | 
|  | static int fatfs_mount(struct fs_mount_t *mountp) | 
|  | { | 
|  | FRESULT res; | 
|  |  | 
|  | res = f_mount((FATFS *)mountp->fs_data, translate_path(mountp->mnt_point), 1); | 
|  |  | 
|  | #if defined(CONFIG_FS_FATFS_MOUNT_MKFS) | 
|  | if (res == FR_NO_FILESYSTEM && | 
|  | (mountp->flags & FS_MOUNT_FLAG_READ_ONLY) != 0) { | 
|  | return -EROFS; | 
|  | } | 
|  | /* If no file system found then create one */ | 
|  | if (res == FR_NO_FILESYSTEM && | 
|  | (mountp->flags & FS_MOUNT_FLAG_NO_FORMAT) == 0) { | 
|  | uint8_t work[FF_MAX_SS]; | 
|  | MKFS_PARM mkfs_opt = { | 
|  | .fmt = FM_ANY | FM_SFD,	/* Any suitable FAT */ | 
|  | .n_fat = 1,		/* One FAT fs table */ | 
|  | .align = 0,		/* Get sector size via diskio query */ | 
|  | .n_root = CONFIG_FS_FATFS_MAX_ROOT_ENTRIES, | 
|  | .au_size = 0		/* Auto calculate cluster size */ | 
|  | }; | 
|  |  | 
|  | res = f_mkfs(translate_path(mountp->mnt_point), &mkfs_opt, work, sizeof(work)); | 
|  | if (res == FR_OK) { | 
|  | res = f_mount((FATFS *)mountp->fs_data, | 
|  | translate_path(mountp->mnt_point), 1); | 
|  | } | 
|  | } | 
|  | #endif /* CONFIG_FS_FATFS_MOUNT_MKFS */ | 
|  |  | 
|  | if (res == FR_OK) { | 
|  | mountp->flags |= FS_MOUNT_FLAG_USE_DISK_ACCESS; | 
|  | } | 
|  |  | 
|  | return translate_error(res); | 
|  |  | 
|  | } | 
|  |  | 
|  | static int fatfs_unmount(struct fs_mount_t *mountp) | 
|  | { | 
|  | FRESULT res; | 
|  | DRESULT disk_res; | 
|  | uint8_t param = DISK_IOCTL_POWER_OFF; | 
|  |  | 
|  | res = f_mount(NULL, translate_path(mountp->mnt_point), 0); | 
|  | if (res != FR_OK) { | 
|  | LOG_ERR("Unmount failed (%d)", res); | 
|  | return translate_error(res); | 
|  | } | 
|  |  | 
|  | /* Make direct disk IOCTL call to deinit disk */ | 
|  | disk_res = disk_ioctl(((FATFS *)mountp->fs_data)->pdrv, CTRL_POWER, ¶m); | 
|  | if (disk_res != RES_OK) { | 
|  | LOG_ERR("Could not power off disk (%d)", disk_res); | 
|  | return translate_disk_error(disk_res); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #if defined(CONFIG_FILE_SYSTEM_MKFS) && defined(CONFIG_FS_FATFS_MKFS) | 
|  |  | 
|  | static MKFS_PARM def_cfg = { | 
|  | .fmt = FM_ANY | FM_SFD,	/* Any suitable FAT */ | 
|  | .n_fat = 1,		/* One FAT fs table */ | 
|  | .align = 0,		/* Get sector size via diskio query */ | 
|  | .n_root = CONFIG_FS_FATFS_MAX_ROOT_ENTRIES, | 
|  | .au_size = 0		/* Auto calculate cluster size */ | 
|  | }; | 
|  |  | 
|  | static int fatfs_mkfs(uintptr_t dev_id, void *cfg, int flags) | 
|  | { | 
|  | FRESULT res; | 
|  | uint8_t work[FF_MAX_SS]; | 
|  | MKFS_PARM *mkfs_opt = &def_cfg; | 
|  |  | 
|  | if (cfg != NULL) { | 
|  | mkfs_opt = (MKFS_PARM *)cfg; | 
|  | } | 
|  |  | 
|  | res = f_mkfs((char *)dev_id, mkfs_opt, work, sizeof(work)); | 
|  |  | 
|  | return translate_error(res); | 
|  | } | 
|  |  | 
|  | #endif /* CONFIG_FILE_SYSTEM_MKFS && FS_FATFS_MKFS */ | 
|  |  | 
|  | /* File system interface */ | 
|  | static const struct fs_file_system_t fatfs_fs = { | 
|  | .open = fatfs_open, | 
|  | .close = fatfs_close, | 
|  | .read = fatfs_read, | 
|  | .write = fatfs_write, | 
|  | .lseek = fatfs_seek, | 
|  | .tell = fatfs_tell, | 
|  | .truncate = fatfs_truncate, | 
|  | .sync = fatfs_sync, | 
|  | .opendir = fatfs_opendir, | 
|  | .readdir = fatfs_readdir, | 
|  | .closedir = fatfs_closedir, | 
|  | .mount = fatfs_mount, | 
|  | .unmount = fatfs_unmount, | 
|  | .unlink = fatfs_unlink, | 
|  | .rename = fatfs_rename, | 
|  | .mkdir = fatfs_mkdir, | 
|  | .stat = fatfs_stat, | 
|  | .statvfs = fatfs_statvfs, | 
|  | #if defined(CONFIG_FILE_SYSTEM_MKFS) && defined(CONFIG_FS_FATFS_MKFS) | 
|  | .mkfs = fatfs_mkfs, | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | #define DT_DRV_COMPAT zephyr_fstab_fatfs | 
|  |  | 
|  | #define DEFINE_FS(inst)                                                                            \ | 
|  | BUILD_ASSERT(DT_INST_PROP(inst, disk_access), "FATFS needs disk-access");                  \ | 
|  | BUILD_ASSERT(!DT_INST_PROP(inst, read_only),                                               \ | 
|  | "READ_ONLY not supported for individual instances see FS_FATFS_READ_ONLY");   \ | 
|  | BUILD_ASSERT(!DT_INST_PROP(inst, no_format),                                               \ | 
|  | "NO_FORMAT not supported for individual instanzes FS_FATFS_MKFS");            \ | 
|  | static FATFS fs_data_##inst;                                                               \ | 
|  | struct fs_mount_t FS_FSTAB_ENTRY(DT_DRV_INST(inst)) = {                                    \ | 
|  | .type = FS_FATFS,                                                                  \ | 
|  | .mnt_point = FSTAB_ENTRY_DT_INST_MOUNT_POINT(inst),                                \ | 
|  | .fs_data = &fs_data_##inst,                                                        \ | 
|  | .storage_dev = NULL,                                                               \ | 
|  | .flags = FSTAB_ENTRY_DT_MOUNT_FLAGS(DT_DRV_INST(inst)),                            \ | 
|  | }; | 
|  |  | 
|  | DT_INST_FOREACH_STATUS_OKAY(DEFINE_FS); | 
|  |  | 
|  | #ifdef CONFIG_FS_FATFS_FSTAB_AUTOMOUNT | 
|  | #define REFERENCE_MOUNT(inst) (&FS_FSTAB_ENTRY(DT_DRV_INST(inst))), | 
|  |  | 
|  | static void automount_if_enabled(struct fs_mount_t *mountp) | 
|  | { | 
|  | int ret = 0; | 
|  |  | 
|  | if ((mountp->flags & FS_MOUNT_FLAG_AUTOMOUNT) != 0) { | 
|  | ret = fs_mount(mountp); | 
|  | if (ret < 0) { | 
|  | LOG_ERR("Error mounting filesystem: at %s: %d", mountp->mnt_point, ret); | 
|  | } else { | 
|  | LOG_DBG("FATFS Filesystem \"%s\" initialized", mountp->mnt_point); | 
|  | } | 
|  | } | 
|  | } | 
|  | #endif /* CONFIG_FS_FATFS_FSTAB_AUTOMOUNT */ | 
|  |  | 
|  | #if CONFIG_FS_FATFS_CUSTOM_MOUNT_POINT_COUNT | 
|  | const char *VolumeStr[CONFIG_FS_FATFS_CUSTOM_MOUNT_POINT_COUNT]; | 
|  | #endif /* CONFIG_FS_FATFS_CUSTOM_MOUNT_POINT_COUNT */ | 
|  |  | 
|  | static int fatfs_init(void) | 
|  | { | 
|  | #if CONFIG_FS_FATFS_CUSTOM_MOUNT_POINT_COUNT | 
|  | static char mount_points[] = CONFIG_FS_FATFS_CUSTOM_MOUNT_POINTS; | 
|  | int mount_point_count = 0; | 
|  |  | 
|  | VolumeStr[0] = mount_points; | 
|  | for (int i = 0; i < ARRAY_SIZE(mount_points) - 1; i++) { | 
|  | if (mount_points[i] == ',') { | 
|  | mount_points[i] = 0; | 
|  | mount_point_count++; | 
|  | if (mount_point_count >= ARRAY_SIZE(VolumeStr)) { | 
|  | LOG_ERR("Mount point count not sufficient for defined mount " | 
|  | "points."); | 
|  | return -1; | 
|  | } | 
|  | VolumeStr[mount_point_count] = &mount_points[i + 1]; | 
|  | } | 
|  | } | 
|  | #endif /* CONFIG_FS_FATFS_CUSTOM_MOUNT_POINT_COUNT */ | 
|  | int rc = fs_register(FS_FATFS, &fatfs_fs); | 
|  |  | 
|  | #ifdef CONFIG_FS_FATFS_FSTAB_AUTOMOUNT | 
|  | if (rc == 0) { | 
|  | struct fs_mount_t *partitions[] = {DT_INST_FOREACH_STATUS_OKAY(REFERENCE_MOUNT)}; | 
|  |  | 
|  | for (size_t i = 0; i < ARRAY_SIZE(partitions); i++) { | 
|  | struct fs_mount_t *mpi = partitions[i]; | 
|  |  | 
|  | automount_if_enabled(mpi); | 
|  | } | 
|  | } | 
|  | #endif /* CONFIG_FS_FATFS_FSTAB_AUTOMOUNT */ | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | SYS_INIT(fatfs_init, POST_KERNEL, CONFIG_FILE_SYSTEM_INIT_PRIORITY); |