#include "os/os.h" #include "os/str.h" #include "os/mem.h" #include #if CONFIG_FATFS #include #include #include #include "fatfs_adapter.h" #include "bk_filesystem.h" #include "bk_partition.h" #include "ff.h" #define FS_FATFS "fatfs" typedef struct bk_fatfs { FATFS ffs; char vol_str[3]; } BK_FATFS; static const char *devices [] = { //keep in sync with disk_io.c FATFS_DEV_RAM, FATFS_DEV_SDCARD, FATFS_DEV_UDISK, FATFS_DEV_FLASH, NULL }; static int _get_device_idx(const char *device_name) { int i = 0; while(devices[i] != NULL) { if (strcmp(device_name, devices[i]) == 0) return i; i++; } return -1; } static int _bk_fatfs_mount(struct bk_filesystem *fs, unsigned long mount_flags, const void *data) { BK_FATFS *bk_ffs; FATFS *ffs; struct bk_fatfs_partition *part; int ret; int idx; part = (struct bk_fatfs_partition *)data; idx = _get_device_idx(part->part_dev.device_name); if (idx < 0) return -1; bk_ffs = (BK_FATFS *)os_malloc(sizeof(BK_FATFS)); if (!bk_ffs) return -1; ffs = (FATFS *)bk_ffs; bk_ffs->vol_str[0] = idx + '0'; bk_ffs->vol_str[1] = ':'; bk_ffs->vol_str[2] = '\0'; ret = f_mount(ffs, bk_ffs->vol_str, 1); if (ret) { os_free(bk_ffs); return -1; } fs->fs_data = bk_ffs; return 0; } static int _bk_fatfs_unmount(struct bk_filesystem *fs) { BK_FATFS *bk_ffs; int ret; bk_ffs = (BK_FATFS *)fs->fs_data; //ret = f_unmount(bk_ffs->vol_str); // TODO : FR_NOT_ENABLED ret = f_unmount(1, bk_ffs->vol_str, 0); os_free(bk_ffs); fs->fs_data = NULL; return ret; } static int _bk_fatfs_unmount2(struct bk_filesystem *fs, int flag) { return _bk_fatfs_unmount(fs); } static int _bk_fatfs_mkfs(const char *partition_name, const void *data) { #define TEMP_SIZE 1024 struct bk_fatfs_partition *part; char *temp; char vol_str[3]; int ret; int idx; part = (struct bk_fatfs_partition *)data; idx = _get_device_idx(part->part_dev.device_name); if (idx < 0) return -1; temp = os_malloc(TEMP_SIZE); if (!temp) return -1; vol_str[0] = idx + '0'; vol_str[1] = ':'; vol_str[2] = '\0'; //ret = f_mkfs(vol_str, NULL, temp, TEMP_SIZE); ret = f_mkfs(vol_str, FM_ANY, 65536, temp, TEMP_SIZE); os_free(temp); return ret; } static int _bk_fatfs_statfs(struct bk_filesystem *fs, struct statfs *buf) { BK_FATFS *bk_ffs; FATFS *ffs; uint32_t free_clust; int ret; bk_ffs = (BK_FATFS *)fs->fs_data; ret = f_getfree(bk_ffs->vol_str, &free_clust, &ffs); if (ret) return -1; #if FF_MAX_SS != FF_MIN_SS buf->f_bsize = ffs->ssize * ffs->csize; #else buf->f_bsize = FF_MIN_SS * ffs->csize; #endif buf->f_blocks = ffs->n_fatent - 2; buf->f_bfree = free_clust; buf->f_bavail = free_clust; return 0; } static struct bk_filesystem_ops g_fatfs_fs_ops = { .mount = _bk_fatfs_mount, .unmount = _bk_fatfs_unmount, .unmount2 = _bk_fatfs_unmount2, .mkfs = _bk_fatfs_mkfs, .statfs = _bk_fatfs_statfs, }; static int file_flags_to_fatfs(int oflags) { int fatfs_open_flags = 0; fatfs_open_flags |= FA_READ; if (oflags & O_WRONLY) { fatfs_open_flags |= FA_WRITE; } if ((oflags & O_ACCMODE) & O_RDWR) { fatfs_open_flags |= FA_WRITE; } /* Creates a new file if the file is not existing, otherwise, just open it. */ if (oflags & O_CREAT) { fatfs_open_flags |= FA_OPEN_ALWAYS; /* Creates a new file. If the file already exists, the function shall fail. */ if (oflags & O_EXCL) { fatfs_open_flags |= FA_CREATE_NEW; } } /* Creates a new file. If the file already exists, its length shall be truncated to 0. */ if (oflags & O_TRUNC) { fatfs_open_flags |= FA_CREATE_ALWAYS; } if (oflags & O_APPEND) { fatfs_open_flags |= FA_OPEN_APPEND; } return fatfs_open_flags; } static char *get_full_name(struct bk_filesystem *fs, const char *path) { BK_FATFS *bk_ffs; char *full_path; bk_ffs = (BK_FATFS *)fs->fs_data; full_path = os_malloc(strlen(path) + 2 + 1); // 2 for "x:", 1 for '\0' if (!full_path) return NULL; strcpy(full_path, bk_ffs->vol_str); strcpy(full_path + 2, path); return full_path; } static int _bk_fatfs_open(struct bk_file *file, const char *path, int oflag) { FIL *fil; int ret; char *full_name; fil = (FIL *)os_malloc(sizeof(FIL)); if (!fil) return -1; full_name = get_full_name(file->filesystem, path); if (!full_name) return -1; oflag = file_flags_to_fatfs(oflag); ret = f_open(fil, full_name, oflag); os_free(full_name); if (ret) { os_free(fil); return -1; } file->f_data = fil; return 0; } static int _bk_fatfs_close(struct bk_file *file) { FIL *fil; int ret; fil = (FIL *)file->f_data; ret = f_close(fil); os_free(file->f_data); file->f_data = NULL; return ret; } static ssize_t _bk_fatfs_read(struct bk_file *file, void *buf, size_t count) { FIL *fil; int ret; UINT len; fil = (FIL *)file->f_data; ret = f_read(fil, buf, count, &len); if (ret) return -1; return len; } static ssize_t _bk_fatfs_write(struct bk_file *file, const void *buf, size_t count) { FIL *fil; int ret; UINT len; fil = (FIL *)file->f_data; ret = f_write(fil, buf, count, &len); if (ret) return -1; return len; } static off_t _bk_fatfs_lseek(struct bk_file *file, off_t offset, int whence) { FIL *fil; int ret; off_t pos; fil = (FIL *)file->f_data; if (whence == SEEK_SET) { pos = 0; } else if (whence == SEEK_CUR) { pos = f_tell(fil); } else if (whence == SEEK_END) { pos = f_size(fil); } else { return -1; } ret = f_lseek(fil, pos + offset); if (ret) return -1; pos = f_tell(fil); return pos; } #if CONFIG_STARBURST_AIDIALOG_SDK static off_t _bk_fatfs_ftell(struct bk_file *file) { FIL *fil = (FIL *)file->f_data; return f_tell(fil); } static int _bk_fatfs_feof(struct bk_file *file) { FIL *fil = (FIL *)file->f_data; return f_EOF(fil); } #endif static int _bk_fatfs_unlink(struct bk_filesystem *fs, const char *pathname) { int ret; char *full_name; full_name = get_full_name(fs, pathname); if (!full_name) return -1; ret = f_unlink(full_name); os_free(full_name); return ret; } static int _bk_fatfs_stat(struct bk_filesystem *fs, const char *pathname, struct stat *statbuf) { int ret; char *full_name; FILINFO file_info = {0}; full_name = get_full_name(fs, pathname); if (!full_name) return -1; ret = f_stat(full_name, &file_info); os_free(full_name); if (ret == 0) { statbuf->st_size = file_info.fsize; if (file_info.fattrib & AM_DIR) statbuf->st_mode = S_IFDIR; else statbuf->st_mode = S_IFREG; } return 0; } static int _bk_fatfs_rename(struct bk_filesystem *fs, const char *oldpath, const char *newpath) { int ret; char *full_name_old; char *full_name_new; full_name_old = get_full_name(fs, oldpath); if (!full_name_old) return -1; full_name_new = get_full_name(fs, newpath); if (!full_name_new) { os_free(full_name_old); return -1; } ret = f_rename(full_name_old, full_name_new); os_free(full_name_old); os_free(full_name_new); return ret; } static int _bk_fatfs_fsync(struct bk_file *file) { FIL *fil; int ret; fil = (FIL *)file->f_data; ret = f_sync(fil); return ret; } static int _bk_fatfs_ftruncate(struct bk_file *file, off_t offset) { // TODO FIL *fil; int ret; struct stat statbuf; fil = (FIL *)file->f_data; ret = _bk_fatfs_stat(file->filesystem, file->path, &statbuf); if (ret) return -1; if (offset > statbuf.st_size) { #if FF_USE_EXPAND ret = f_expand(fil, offset, 1); #else ret = -1; #endif } else if (offset < statbuf.st_size) { fil->fptr = offset; ret = f_truncate(fil); } else { ret = 0; } return ret; } static int _bk_fatfs_fcntl(struct bk_file *file, int cmd, void *args) { return -1; } static int _bk_fatfs_opendir(bk_dir *dir, const char *pathname) { DIR *dp; int ret; char *full_name; full_name = get_full_name(dir->filesystem, pathname); if (!full_name) return -1; dp = (DIR *)os_malloc(sizeof(DIR)); if (!dp) return -1; ret = f_opendir(dp, full_name); os_free(full_name); if (ret) { os_free(dp); return -1; } dir->dir_data = dp; return 0; } static int _bk_fatfs_readdir(bk_dir *dir, struct dirent *entry) { DIR *dp; int ret; FILINFO file_info = {0}; int len; dp = (DIR *)dir->dir_data; ret = f_readdir(dp, &file_info); if (ret || (file_info.fname[0] == '\0')) return -1; entry->d_ino = 0; if (file_info.fattrib & AM_DIR) entry->d_type = DT_DIR; else entry->d_type = DT_REG; entry->d_reclen = sizeof(struct dirent); if (sizeof(entry->d_name) - 1 < strlen(file_info.fname)) len = sizeof(entry->d_name) - 1; else len = strlen(file_info.fname); strncpy(entry->d_name, file_info.fname, len + 1); return 0; } static int _bk_fatfs_closedir(bk_dir *dir) { DIR *dp; int ret; dp = (DIR *)dir->dir_data; ret = f_closedir(dp); os_free(dp); dir->dir_data = NULL; return ret; } static int _bk_fatfs_mkdir(struct bk_filesystem *fs, const char *pathname) { int ret; char *full_name; full_name = get_full_name(fs, pathname); if (!full_name) return -1; ret = f_mkdir(full_name); os_free(full_name); return ret; } static int _bk_fatfs_rmdir(struct bk_filesystem *fs, const char *pathname) { int ret; char *full_name; full_name = get_full_name(fs, pathname); if (!full_name) return -1; ret = f_rmdir(full_name); os_free(full_name); return ret; } static struct bk_file_ops g_fatfs_file_ops = { .open = _bk_fatfs_open, .close = _bk_fatfs_close, .read = _bk_fatfs_read, .write = _bk_fatfs_write, .lseek = _bk_fatfs_lseek, .unlink = _bk_fatfs_unlink, .stat = _bk_fatfs_stat, .rename = _bk_fatfs_rename, .fsync = _bk_fatfs_fsync, .ftruncate = _bk_fatfs_ftruncate, .fcntl = _bk_fatfs_fcntl, .opendir = _bk_fatfs_opendir, .readdir = _bk_fatfs_readdir, .closedir = _bk_fatfs_closedir, .mkdir = _bk_fatfs_mkdir, .rmdir = _bk_fatfs_rmdir, #if CONFIG_STARBURST_AIDIALOG_SDK .ftell = _bk_fatfs_ftell, .feof = _bk_fatfs_feof, #endif }; int bk_fatfs_init(void) { return bk_register_filesystem(FS_FATFS, &g_fatfs_fs_ops, &g_fatfs_file_ops); } #else int bk_fatfs_init(void) { return 0; } #endif