Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
mbed-os/features/filesystem/fat/FATFileSystem.cpp@0:9b3d4731edbb, 2018-06-21 (annotated)
- Committer:
- WFKnight
- Date:
- Thu Jun 21 13:51:43 2018 +0000
- Revision:
- 0:9b3d4731edbb
UART, RTOS, LED
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| WFKnight | 0:9b3d4731edbb | 1 | /* mbed Microcontroller Library |
| WFKnight | 0:9b3d4731edbb | 2 | * Copyright (c) 2006-2012 ARM Limited |
| WFKnight | 0:9b3d4731edbb | 3 | * |
| WFKnight | 0:9b3d4731edbb | 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| WFKnight | 0:9b3d4731edbb | 5 | * of this software and associated documentation files (the "Software"), to deal |
| WFKnight | 0:9b3d4731edbb | 6 | * in the Software without restriction, including without limitation the rights |
| WFKnight | 0:9b3d4731edbb | 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| WFKnight | 0:9b3d4731edbb | 8 | * copies of the Software, and to permit persons to whom the Software is |
| WFKnight | 0:9b3d4731edbb | 9 | * furnished to do so, subject to the following conditions: |
| WFKnight | 0:9b3d4731edbb | 10 | * |
| WFKnight | 0:9b3d4731edbb | 11 | * The above copyright notice and this permission notice shall be included in |
| WFKnight | 0:9b3d4731edbb | 12 | * all copies or substantial portions of the Software. |
| WFKnight | 0:9b3d4731edbb | 13 | * |
| WFKnight | 0:9b3d4731edbb | 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| WFKnight | 0:9b3d4731edbb | 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| WFKnight | 0:9b3d4731edbb | 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| WFKnight | 0:9b3d4731edbb | 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| WFKnight | 0:9b3d4731edbb | 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| WFKnight | 0:9b3d4731edbb | 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| WFKnight | 0:9b3d4731edbb | 20 | * SOFTWARE. |
| WFKnight | 0:9b3d4731edbb | 21 | */ |
| WFKnight | 0:9b3d4731edbb | 22 | #include "mbed.h" |
| WFKnight | 0:9b3d4731edbb | 23 | |
| WFKnight | 0:9b3d4731edbb | 24 | #include "diskio.h" |
| WFKnight | 0:9b3d4731edbb | 25 | #include "ffconf.h" |
| WFKnight | 0:9b3d4731edbb | 26 | #include "mbed_debug.h" |
| WFKnight | 0:9b3d4731edbb | 27 | #include "mbed_critical.h" |
| WFKnight | 0:9b3d4731edbb | 28 | #include <errno.h> |
| WFKnight | 0:9b3d4731edbb | 29 | |
| WFKnight | 0:9b3d4731edbb | 30 | #include "FATFileSystem.h" |
| WFKnight | 0:9b3d4731edbb | 31 | |
| WFKnight | 0:9b3d4731edbb | 32 | |
| WFKnight | 0:9b3d4731edbb | 33 | ////// Error handling ///// |
| WFKnight | 0:9b3d4731edbb | 34 | |
| WFKnight | 0:9b3d4731edbb | 35 | static int fat_error_remap(FRESULT res) |
| WFKnight | 0:9b3d4731edbb | 36 | { |
| WFKnight | 0:9b3d4731edbb | 37 | switch(res) { |
| WFKnight | 0:9b3d4731edbb | 38 | case FR_OK: // (0) Succeeded |
| WFKnight | 0:9b3d4731edbb | 39 | return 0; |
| WFKnight | 0:9b3d4731edbb | 40 | case FR_DISK_ERR: // (1) A hard error occurred in the low level disk I/O layer |
| WFKnight | 0:9b3d4731edbb | 41 | return -EIO; |
| WFKnight | 0:9b3d4731edbb | 42 | case FR_INT_ERR: // (2) Assertion failed |
| WFKnight | 0:9b3d4731edbb | 43 | return -1; |
| WFKnight | 0:9b3d4731edbb | 44 | case FR_NOT_READY: // (3) The physical drive cannot work |
| WFKnight | 0:9b3d4731edbb | 45 | return -EIO; |
| WFKnight | 0:9b3d4731edbb | 46 | case FR_NO_FILE: // (4) Could not find the file |
| WFKnight | 0:9b3d4731edbb | 47 | return -ENOENT; |
| WFKnight | 0:9b3d4731edbb | 48 | case FR_NO_PATH: // (5) Could not find the path |
| WFKnight | 0:9b3d4731edbb | 49 | return -ENOTDIR; |
| WFKnight | 0:9b3d4731edbb | 50 | case FR_INVALID_NAME: // (6) The path name format is invalid |
| WFKnight | 0:9b3d4731edbb | 51 | return -EINVAL; |
| WFKnight | 0:9b3d4731edbb | 52 | case FR_DENIED: // (7) Access denied due to prohibited access or directory full |
| WFKnight | 0:9b3d4731edbb | 53 | return -EACCES; |
| WFKnight | 0:9b3d4731edbb | 54 | case FR_EXIST: // (8) Access denied due to prohibited access |
| WFKnight | 0:9b3d4731edbb | 55 | return -EEXIST; |
| WFKnight | 0:9b3d4731edbb | 56 | case FR_INVALID_OBJECT: // (9) The file/directory object is invalid |
| WFKnight | 0:9b3d4731edbb | 57 | return -EBADF; |
| WFKnight | 0:9b3d4731edbb | 58 | case FR_WRITE_PROTECTED: // (10) The physical drive is write protected |
| WFKnight | 0:9b3d4731edbb | 59 | return -EACCES; |
| WFKnight | 0:9b3d4731edbb | 60 | case FR_INVALID_DRIVE: // (11) The logical drive number is invalid |
| WFKnight | 0:9b3d4731edbb | 61 | return -ENODEV; |
| WFKnight | 0:9b3d4731edbb | 62 | case FR_NOT_ENABLED: // (12) The volume has no work area |
| WFKnight | 0:9b3d4731edbb | 63 | return -ENODEV; |
| WFKnight | 0:9b3d4731edbb | 64 | case FR_NO_FILESYSTEM: // (13) There is no valid FAT volume |
| WFKnight | 0:9b3d4731edbb | 65 | return -EINVAL; |
| WFKnight | 0:9b3d4731edbb | 66 | case FR_MKFS_ABORTED: // (14) The f_mkfs() aborted due to any problem |
| WFKnight | 0:9b3d4731edbb | 67 | return -EIO; |
| WFKnight | 0:9b3d4731edbb | 68 | case FR_TIMEOUT: // (15) Could not get a grant to access the volume within defined period |
| WFKnight | 0:9b3d4731edbb | 69 | return -ETIMEDOUT; |
| WFKnight | 0:9b3d4731edbb | 70 | case FR_LOCKED: // (16) The operation is rejected according to the file sharing policy |
| WFKnight | 0:9b3d4731edbb | 71 | return -EBUSY; |
| WFKnight | 0:9b3d4731edbb | 72 | case FR_NOT_ENOUGH_CORE: // (17) LFN working buffer could not be allocated |
| WFKnight | 0:9b3d4731edbb | 73 | return -ENOMEM; |
| WFKnight | 0:9b3d4731edbb | 74 | case FR_TOO_MANY_OPEN_FILES: // (18) Number of open files > FF_FS_LOCK |
| WFKnight | 0:9b3d4731edbb | 75 | return -ENFILE; |
| WFKnight | 0:9b3d4731edbb | 76 | case FR_INVALID_PARAMETER: // (19) Given parameter is invalid |
| WFKnight | 0:9b3d4731edbb | 77 | return -EINVAL; |
| WFKnight | 0:9b3d4731edbb | 78 | default: |
| WFKnight | 0:9b3d4731edbb | 79 | return -res; |
| WFKnight | 0:9b3d4731edbb | 80 | } |
| WFKnight | 0:9b3d4731edbb | 81 | } |
| WFKnight | 0:9b3d4731edbb | 82 | |
| WFKnight | 0:9b3d4731edbb | 83 | // Helper class for deferring operations when variable falls out of scope |
| WFKnight | 0:9b3d4731edbb | 84 | template <typename T> |
| WFKnight | 0:9b3d4731edbb | 85 | class Deferred { |
| WFKnight | 0:9b3d4731edbb | 86 | public: |
| WFKnight | 0:9b3d4731edbb | 87 | T _t; |
| WFKnight | 0:9b3d4731edbb | 88 | Callback<void(T)> _ondefer; |
| WFKnight | 0:9b3d4731edbb | 89 | |
| WFKnight | 0:9b3d4731edbb | 90 | Deferred(const Deferred&); |
| WFKnight | 0:9b3d4731edbb | 91 | Deferred &operator=(const Deferred&); |
| WFKnight | 0:9b3d4731edbb | 92 | |
| WFKnight | 0:9b3d4731edbb | 93 | public: |
| WFKnight | 0:9b3d4731edbb | 94 | Deferred(T t, Callback<void(T)> ondefer = NULL) |
| WFKnight | 0:9b3d4731edbb | 95 | : _t(t), _ondefer(ondefer) |
| WFKnight | 0:9b3d4731edbb | 96 | { |
| WFKnight | 0:9b3d4731edbb | 97 | } |
| WFKnight | 0:9b3d4731edbb | 98 | |
| WFKnight | 0:9b3d4731edbb | 99 | operator T() |
| WFKnight | 0:9b3d4731edbb | 100 | { |
| WFKnight | 0:9b3d4731edbb | 101 | return _t; |
| WFKnight | 0:9b3d4731edbb | 102 | } |
| WFKnight | 0:9b3d4731edbb | 103 | |
| WFKnight | 0:9b3d4731edbb | 104 | ~Deferred() |
| WFKnight | 0:9b3d4731edbb | 105 | { |
| WFKnight | 0:9b3d4731edbb | 106 | if (_ondefer) { |
| WFKnight | 0:9b3d4731edbb | 107 | _ondefer(_t); |
| WFKnight | 0:9b3d4731edbb | 108 | } |
| WFKnight | 0:9b3d4731edbb | 109 | } |
| WFKnight | 0:9b3d4731edbb | 110 | }; |
| WFKnight | 0:9b3d4731edbb | 111 | |
| WFKnight | 0:9b3d4731edbb | 112 | static void dodelete(const char *data) |
| WFKnight | 0:9b3d4731edbb | 113 | { |
| WFKnight | 0:9b3d4731edbb | 114 | delete[] data; |
| WFKnight | 0:9b3d4731edbb | 115 | } |
| WFKnight | 0:9b3d4731edbb | 116 | |
| WFKnight | 0:9b3d4731edbb | 117 | // Adds prefix needed internally by fatfs, this can be avoided for the first fatfs |
| WFKnight | 0:9b3d4731edbb | 118 | // (id 0) otherwise a prefix of "id:/" is inserted in front of the string. |
| WFKnight | 0:9b3d4731edbb | 119 | static Deferred<const char*> fat_path_prefix(int id, const char *path) |
| WFKnight | 0:9b3d4731edbb | 120 | { |
| WFKnight | 0:9b3d4731edbb | 121 | // We can avoid dynamic allocation when only on fatfs is in use |
| WFKnight | 0:9b3d4731edbb | 122 | if (id == 0) { |
| WFKnight | 0:9b3d4731edbb | 123 | return path; |
| WFKnight | 0:9b3d4731edbb | 124 | } |
| WFKnight | 0:9b3d4731edbb | 125 | |
| WFKnight | 0:9b3d4731edbb | 126 | // Prefix path with id, will look something like 2:/hi/hello/filehere.txt |
| WFKnight | 0:9b3d4731edbb | 127 | char *buffer = new char[strlen("0:/") + strlen(path) + 1]; |
| WFKnight | 0:9b3d4731edbb | 128 | if (!buffer) { |
| WFKnight | 0:9b3d4731edbb | 129 | return NULL; |
| WFKnight | 0:9b3d4731edbb | 130 | } |
| WFKnight | 0:9b3d4731edbb | 131 | |
| WFKnight | 0:9b3d4731edbb | 132 | buffer[0] = '0' + id; |
| WFKnight | 0:9b3d4731edbb | 133 | buffer[1] = ':'; |
| WFKnight | 0:9b3d4731edbb | 134 | buffer[2] = '/'; |
| WFKnight | 0:9b3d4731edbb | 135 | strcpy(buffer + strlen("0:/"), path); |
| WFKnight | 0:9b3d4731edbb | 136 | return Deferred<const char*>(buffer, dodelete); |
| WFKnight | 0:9b3d4731edbb | 137 | } |
| WFKnight | 0:9b3d4731edbb | 138 | |
| WFKnight | 0:9b3d4731edbb | 139 | |
| WFKnight | 0:9b3d4731edbb | 140 | ////// Disk operations ////// |
| WFKnight | 0:9b3d4731edbb | 141 | |
| WFKnight | 0:9b3d4731edbb | 142 | // Global access to block device from FAT driver |
| WFKnight | 0:9b3d4731edbb | 143 | static BlockDevice *_ffs[FF_VOLUMES] = {0}; |
| WFKnight | 0:9b3d4731edbb | 144 | static SingletonPtr<PlatformMutex> _ffs_mutex; |
| WFKnight | 0:9b3d4731edbb | 145 | |
| WFKnight | 0:9b3d4731edbb | 146 | |
| WFKnight | 0:9b3d4731edbb | 147 | // FAT driver functions |
| WFKnight | 0:9b3d4731edbb | 148 | DWORD get_fattime(void) |
| WFKnight | 0:9b3d4731edbb | 149 | { |
| WFKnight | 0:9b3d4731edbb | 150 | time_t rawtime; |
| WFKnight | 0:9b3d4731edbb | 151 | time(&rawtime); |
| WFKnight | 0:9b3d4731edbb | 152 | struct tm *ptm = localtime(&rawtime); |
| WFKnight | 0:9b3d4731edbb | 153 | return (DWORD)(ptm->tm_year - 80) << 25 |
| WFKnight | 0:9b3d4731edbb | 154 | | (DWORD)(ptm->tm_mon + 1 ) << 21 |
| WFKnight | 0:9b3d4731edbb | 155 | | (DWORD)(ptm->tm_mday ) << 16 |
| WFKnight | 0:9b3d4731edbb | 156 | | (DWORD)(ptm->tm_hour ) << 11 |
| WFKnight | 0:9b3d4731edbb | 157 | | (DWORD)(ptm->tm_min ) << 5 |
| WFKnight | 0:9b3d4731edbb | 158 | | (DWORD)(ptm->tm_sec/2 ); |
| WFKnight | 0:9b3d4731edbb | 159 | } |
| WFKnight | 0:9b3d4731edbb | 160 | |
| WFKnight | 0:9b3d4731edbb | 161 | void *ff_memalloc(UINT size) |
| WFKnight | 0:9b3d4731edbb | 162 | { |
| WFKnight | 0:9b3d4731edbb | 163 | return malloc(size); |
| WFKnight | 0:9b3d4731edbb | 164 | } |
| WFKnight | 0:9b3d4731edbb | 165 | |
| WFKnight | 0:9b3d4731edbb | 166 | void ff_memfree(void *p) |
| WFKnight | 0:9b3d4731edbb | 167 | { |
| WFKnight | 0:9b3d4731edbb | 168 | free(p); |
| WFKnight | 0:9b3d4731edbb | 169 | } |
| WFKnight | 0:9b3d4731edbb | 170 | |
| WFKnight | 0:9b3d4731edbb | 171 | // Implementation of diskio functions (see ChaN/diskio.h) |
| WFKnight | 0:9b3d4731edbb | 172 | static WORD disk_get_sector_size(BYTE pdrv) |
| WFKnight | 0:9b3d4731edbb | 173 | { |
| WFKnight | 0:9b3d4731edbb | 174 | WORD ssize = _ffs[pdrv]->get_erase_size(); |
| WFKnight | 0:9b3d4731edbb | 175 | if (ssize < 512) { |
| WFKnight | 0:9b3d4731edbb | 176 | ssize = 512; |
| WFKnight | 0:9b3d4731edbb | 177 | } |
| WFKnight | 0:9b3d4731edbb | 178 | |
| WFKnight | 0:9b3d4731edbb | 179 | MBED_ASSERT(ssize >= FF_MIN_SS && ssize <= FF_MAX_SS); |
| WFKnight | 0:9b3d4731edbb | 180 | MBED_ASSERT(_ffs[pdrv]->get_read_size() <= _ffs[pdrv]->get_erase_size()); |
| WFKnight | 0:9b3d4731edbb | 181 | MBED_ASSERT(_ffs[pdrv]->get_program_size() <= _ffs[pdrv]->get_erase_size()); |
| WFKnight | 0:9b3d4731edbb | 182 | return ssize; |
| WFKnight | 0:9b3d4731edbb | 183 | } |
| WFKnight | 0:9b3d4731edbb | 184 | |
| WFKnight | 0:9b3d4731edbb | 185 | static DWORD disk_get_sector_count(BYTE pdrv) |
| WFKnight | 0:9b3d4731edbb | 186 | { |
| WFKnight | 0:9b3d4731edbb | 187 | DWORD scount = _ffs[pdrv]->size() / disk_get_sector_size(pdrv); |
| WFKnight | 0:9b3d4731edbb | 188 | MBED_ASSERT(scount >= 64); |
| WFKnight | 0:9b3d4731edbb | 189 | return scount; |
| WFKnight | 0:9b3d4731edbb | 190 | } |
| WFKnight | 0:9b3d4731edbb | 191 | |
| WFKnight | 0:9b3d4731edbb | 192 | DSTATUS disk_status(BYTE pdrv) |
| WFKnight | 0:9b3d4731edbb | 193 | { |
| WFKnight | 0:9b3d4731edbb | 194 | debug_if(FFS_DBG, "disk_status on pdrv [%d]\n", pdrv); |
| WFKnight | 0:9b3d4731edbb | 195 | return RES_OK; |
| WFKnight | 0:9b3d4731edbb | 196 | } |
| WFKnight | 0:9b3d4731edbb | 197 | |
| WFKnight | 0:9b3d4731edbb | 198 | DSTATUS disk_initialize(BYTE pdrv) |
| WFKnight | 0:9b3d4731edbb | 199 | { |
| WFKnight | 0:9b3d4731edbb | 200 | debug_if(FFS_DBG, "disk_initialize on pdrv [%d]\n", pdrv); |
| WFKnight | 0:9b3d4731edbb | 201 | return (DSTATUS)_ffs[pdrv]->init(); |
| WFKnight | 0:9b3d4731edbb | 202 | } |
| WFKnight | 0:9b3d4731edbb | 203 | |
| WFKnight | 0:9b3d4731edbb | 204 | DRESULT disk_read(BYTE pdrv, BYTE *buff, DWORD sector, UINT count) |
| WFKnight | 0:9b3d4731edbb | 205 | { |
| WFKnight | 0:9b3d4731edbb | 206 | debug_if(FFS_DBG, "disk_read(sector %d, count %d) on pdrv [%d]\n", sector, count, pdrv); |
| WFKnight | 0:9b3d4731edbb | 207 | DWORD ssize = disk_get_sector_size(pdrv); |
| WFKnight | 0:9b3d4731edbb | 208 | bd_addr_t addr = (bd_addr_t)sector*ssize; |
| WFKnight | 0:9b3d4731edbb | 209 | bd_size_t size = (bd_size_t)count*ssize; |
| WFKnight | 0:9b3d4731edbb | 210 | int err = _ffs[pdrv]->read(buff, addr, size); |
| WFKnight | 0:9b3d4731edbb | 211 | return err ? RES_PARERR : RES_OK; |
| WFKnight | 0:9b3d4731edbb | 212 | } |
| WFKnight | 0:9b3d4731edbb | 213 | |
| WFKnight | 0:9b3d4731edbb | 214 | DRESULT disk_write(BYTE pdrv, const BYTE *buff, DWORD sector, UINT count) |
| WFKnight | 0:9b3d4731edbb | 215 | { |
| WFKnight | 0:9b3d4731edbb | 216 | debug_if(FFS_DBG, "disk_write(sector %d, count %d) on pdrv [%d]\n", sector, count, pdrv); |
| WFKnight | 0:9b3d4731edbb | 217 | DWORD ssize = disk_get_sector_size(pdrv); |
| WFKnight | 0:9b3d4731edbb | 218 | bd_addr_t addr = (bd_addr_t)sector*ssize; |
| WFKnight | 0:9b3d4731edbb | 219 | bd_size_t size = (bd_size_t)count*ssize; |
| WFKnight | 0:9b3d4731edbb | 220 | int err = _ffs[pdrv]->erase(addr, size); |
| WFKnight | 0:9b3d4731edbb | 221 | if (err) { |
| WFKnight | 0:9b3d4731edbb | 222 | return RES_PARERR; |
| WFKnight | 0:9b3d4731edbb | 223 | } |
| WFKnight | 0:9b3d4731edbb | 224 | |
| WFKnight | 0:9b3d4731edbb | 225 | err = _ffs[pdrv]->program(buff, addr, size); |
| WFKnight | 0:9b3d4731edbb | 226 | if (err) { |
| WFKnight | 0:9b3d4731edbb | 227 | return RES_PARERR; |
| WFKnight | 0:9b3d4731edbb | 228 | } |
| WFKnight | 0:9b3d4731edbb | 229 | |
| WFKnight | 0:9b3d4731edbb | 230 | return RES_OK; |
| WFKnight | 0:9b3d4731edbb | 231 | } |
| WFKnight | 0:9b3d4731edbb | 232 | |
| WFKnight | 0:9b3d4731edbb | 233 | DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) |
| WFKnight | 0:9b3d4731edbb | 234 | { |
| WFKnight | 0:9b3d4731edbb | 235 | debug_if(FFS_DBG, "disk_ioctl(%d)\n", cmd); |
| WFKnight | 0:9b3d4731edbb | 236 | switch (cmd) { |
| WFKnight | 0:9b3d4731edbb | 237 | case CTRL_SYNC: |
| WFKnight | 0:9b3d4731edbb | 238 | if (_ffs[pdrv] == NULL) { |
| WFKnight | 0:9b3d4731edbb | 239 | return RES_NOTRDY; |
| WFKnight | 0:9b3d4731edbb | 240 | } else { |
| WFKnight | 0:9b3d4731edbb | 241 | return RES_OK; |
| WFKnight | 0:9b3d4731edbb | 242 | } |
| WFKnight | 0:9b3d4731edbb | 243 | case GET_SECTOR_COUNT: |
| WFKnight | 0:9b3d4731edbb | 244 | if (_ffs[pdrv] == NULL) { |
| WFKnight | 0:9b3d4731edbb | 245 | return RES_NOTRDY; |
| WFKnight | 0:9b3d4731edbb | 246 | } else { |
| WFKnight | 0:9b3d4731edbb | 247 | *((DWORD*)buff) = disk_get_sector_count(pdrv); |
| WFKnight | 0:9b3d4731edbb | 248 | return RES_OK; |
| WFKnight | 0:9b3d4731edbb | 249 | } |
| WFKnight | 0:9b3d4731edbb | 250 | case GET_SECTOR_SIZE: |
| WFKnight | 0:9b3d4731edbb | 251 | if (_ffs[pdrv] == NULL) { |
| WFKnight | 0:9b3d4731edbb | 252 | return RES_NOTRDY; |
| WFKnight | 0:9b3d4731edbb | 253 | } else { |
| WFKnight | 0:9b3d4731edbb | 254 | *((WORD*)buff) = disk_get_sector_size(pdrv); |
| WFKnight | 0:9b3d4731edbb | 255 | return RES_OK; |
| WFKnight | 0:9b3d4731edbb | 256 | } |
| WFKnight | 0:9b3d4731edbb | 257 | case GET_BLOCK_SIZE: |
| WFKnight | 0:9b3d4731edbb | 258 | *((DWORD*)buff) = 1; // default when not known |
| WFKnight | 0:9b3d4731edbb | 259 | return RES_OK; |
| WFKnight | 0:9b3d4731edbb | 260 | case CTRL_TRIM: |
| WFKnight | 0:9b3d4731edbb | 261 | if (_ffs[pdrv] == NULL) { |
| WFKnight | 0:9b3d4731edbb | 262 | return RES_NOTRDY; |
| WFKnight | 0:9b3d4731edbb | 263 | } else { |
| WFKnight | 0:9b3d4731edbb | 264 | DWORD *sectors = (DWORD*)buff; |
| WFKnight | 0:9b3d4731edbb | 265 | DWORD ssize = disk_get_sector_size(pdrv); |
| WFKnight | 0:9b3d4731edbb | 266 | bd_addr_t addr = (bd_addr_t)sectors[0]*ssize; |
| WFKnight | 0:9b3d4731edbb | 267 | bd_size_t size = (bd_size_t)(sectors[1]-sectors[0]+1)*ssize; |
| WFKnight | 0:9b3d4731edbb | 268 | int err = _ffs[pdrv]->trim(addr, size); |
| WFKnight | 0:9b3d4731edbb | 269 | return err ? RES_PARERR : RES_OK; |
| WFKnight | 0:9b3d4731edbb | 270 | } |
| WFKnight | 0:9b3d4731edbb | 271 | } |
| WFKnight | 0:9b3d4731edbb | 272 | |
| WFKnight | 0:9b3d4731edbb | 273 | return RES_PARERR; |
| WFKnight | 0:9b3d4731edbb | 274 | } |
| WFKnight | 0:9b3d4731edbb | 275 | |
| WFKnight | 0:9b3d4731edbb | 276 | |
| WFKnight | 0:9b3d4731edbb | 277 | ////// Generic filesystem operations ////// |
| WFKnight | 0:9b3d4731edbb | 278 | |
| WFKnight | 0:9b3d4731edbb | 279 | // Filesystem implementation (See FATFilySystem.h) |
| WFKnight | 0:9b3d4731edbb | 280 | FATFileSystem::FATFileSystem(const char *name, BlockDevice *bd) |
| WFKnight | 0:9b3d4731edbb | 281 | : FileSystem(name), _id(-1) { |
| WFKnight | 0:9b3d4731edbb | 282 | if (bd) { |
| WFKnight | 0:9b3d4731edbb | 283 | mount(bd); |
| WFKnight | 0:9b3d4731edbb | 284 | } |
| WFKnight | 0:9b3d4731edbb | 285 | } |
| WFKnight | 0:9b3d4731edbb | 286 | |
| WFKnight | 0:9b3d4731edbb | 287 | FATFileSystem::~FATFileSystem() |
| WFKnight | 0:9b3d4731edbb | 288 | { |
| WFKnight | 0:9b3d4731edbb | 289 | // nop if unmounted |
| WFKnight | 0:9b3d4731edbb | 290 | unmount(); |
| WFKnight | 0:9b3d4731edbb | 291 | } |
| WFKnight | 0:9b3d4731edbb | 292 | |
| WFKnight | 0:9b3d4731edbb | 293 | int FATFileSystem::mount(BlockDevice *bd) |
| WFKnight | 0:9b3d4731edbb | 294 | { |
| WFKnight | 0:9b3d4731edbb | 295 | // requires duplicate definition to allow virtual overload to work |
| WFKnight | 0:9b3d4731edbb | 296 | return mount(bd, true); |
| WFKnight | 0:9b3d4731edbb | 297 | } |
| WFKnight | 0:9b3d4731edbb | 298 | |
| WFKnight | 0:9b3d4731edbb | 299 | int FATFileSystem::mount(BlockDevice *bd, bool mount) |
| WFKnight | 0:9b3d4731edbb | 300 | { |
| WFKnight | 0:9b3d4731edbb | 301 | lock(); |
| WFKnight | 0:9b3d4731edbb | 302 | if (_id != -1) { |
| WFKnight | 0:9b3d4731edbb | 303 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 304 | return -EINVAL; |
| WFKnight | 0:9b3d4731edbb | 305 | } |
| WFKnight | 0:9b3d4731edbb | 306 | |
| WFKnight | 0:9b3d4731edbb | 307 | for (int i = 0; i < FF_VOLUMES; i++) { |
| WFKnight | 0:9b3d4731edbb | 308 | if (!_ffs[i]) { |
| WFKnight | 0:9b3d4731edbb | 309 | _id = i; |
| WFKnight | 0:9b3d4731edbb | 310 | _ffs[_id] = bd; |
| WFKnight | 0:9b3d4731edbb | 311 | _fsid[0] = '0' + _id; |
| WFKnight | 0:9b3d4731edbb | 312 | _fsid[1] = ':'; |
| WFKnight | 0:9b3d4731edbb | 313 | _fsid[2] = '\0'; |
| WFKnight | 0:9b3d4731edbb | 314 | debug_if(FFS_DBG, "Mounting [%s] on ffs drive [%s]\n", getName(), _fsid); |
| WFKnight | 0:9b3d4731edbb | 315 | FRESULT res = f_mount(&_fs, _fsid, mount); |
| WFKnight | 0:9b3d4731edbb | 316 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 317 | return fat_error_remap(res); |
| WFKnight | 0:9b3d4731edbb | 318 | } |
| WFKnight | 0:9b3d4731edbb | 319 | } |
| WFKnight | 0:9b3d4731edbb | 320 | |
| WFKnight | 0:9b3d4731edbb | 321 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 322 | return -ENOMEM; |
| WFKnight | 0:9b3d4731edbb | 323 | } |
| WFKnight | 0:9b3d4731edbb | 324 | |
| WFKnight | 0:9b3d4731edbb | 325 | int FATFileSystem::unmount() |
| WFKnight | 0:9b3d4731edbb | 326 | { |
| WFKnight | 0:9b3d4731edbb | 327 | lock(); |
| WFKnight | 0:9b3d4731edbb | 328 | if (_id == -1) { |
| WFKnight | 0:9b3d4731edbb | 329 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 330 | return -EINVAL; |
| WFKnight | 0:9b3d4731edbb | 331 | } |
| WFKnight | 0:9b3d4731edbb | 332 | |
| WFKnight | 0:9b3d4731edbb | 333 | FRESULT res = f_mount(NULL, _fsid, 0); |
| WFKnight | 0:9b3d4731edbb | 334 | _ffs[_id] = NULL; |
| WFKnight | 0:9b3d4731edbb | 335 | _id = -1; |
| WFKnight | 0:9b3d4731edbb | 336 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 337 | return fat_error_remap(res); |
| WFKnight | 0:9b3d4731edbb | 338 | } |
| WFKnight | 0:9b3d4731edbb | 339 | |
| WFKnight | 0:9b3d4731edbb | 340 | /* See http://elm-chan.org/fsw/ff/en/mkfs.html for details of f_mkfs() and |
| WFKnight | 0:9b3d4731edbb | 341 | * associated arguments. */ |
| WFKnight | 0:9b3d4731edbb | 342 | int FATFileSystem::format(BlockDevice *bd, bd_size_t cluster_size) |
| WFKnight | 0:9b3d4731edbb | 343 | { |
| WFKnight | 0:9b3d4731edbb | 344 | FATFileSystem fs; |
| WFKnight | 0:9b3d4731edbb | 345 | fs.lock(); |
| WFKnight | 0:9b3d4731edbb | 346 | |
| WFKnight | 0:9b3d4731edbb | 347 | int err = bd->init(); |
| WFKnight | 0:9b3d4731edbb | 348 | if (err) { |
| WFKnight | 0:9b3d4731edbb | 349 | fs.unlock(); |
| WFKnight | 0:9b3d4731edbb | 350 | return err; |
| WFKnight | 0:9b3d4731edbb | 351 | } |
| WFKnight | 0:9b3d4731edbb | 352 | |
| WFKnight | 0:9b3d4731edbb | 353 | // erase first handful of blocks |
| WFKnight | 0:9b3d4731edbb | 354 | bd_size_t header = 2*bd->get_erase_size(); |
| WFKnight | 0:9b3d4731edbb | 355 | err = bd->erase(0, header); |
| WFKnight | 0:9b3d4731edbb | 356 | if (err) { |
| WFKnight | 0:9b3d4731edbb | 357 | bd->deinit(); |
| WFKnight | 0:9b3d4731edbb | 358 | fs.unlock(); |
| WFKnight | 0:9b3d4731edbb | 359 | return err; |
| WFKnight | 0:9b3d4731edbb | 360 | } |
| WFKnight | 0:9b3d4731edbb | 361 | |
| WFKnight | 0:9b3d4731edbb | 362 | if (bd->get_erase_value() < 0) { |
| WFKnight | 0:9b3d4731edbb | 363 | // erase is unknown, need to write 1s |
| WFKnight | 0:9b3d4731edbb | 364 | bd_size_t program_size = bd->get_program_size(); |
| WFKnight | 0:9b3d4731edbb | 365 | void *buf = malloc(program_size); |
| WFKnight | 0:9b3d4731edbb | 366 | if (!buf) { |
| WFKnight | 0:9b3d4731edbb | 367 | bd->deinit(); |
| WFKnight | 0:9b3d4731edbb | 368 | fs.unlock(); |
| WFKnight | 0:9b3d4731edbb | 369 | return -ENOMEM; |
| WFKnight | 0:9b3d4731edbb | 370 | } |
| WFKnight | 0:9b3d4731edbb | 371 | |
| WFKnight | 0:9b3d4731edbb | 372 | memset(buf, 0xff, program_size); |
| WFKnight | 0:9b3d4731edbb | 373 | |
| WFKnight | 0:9b3d4731edbb | 374 | for (bd_addr_t i = 0; i < header; i += program_size) { |
| WFKnight | 0:9b3d4731edbb | 375 | err = bd->program(buf, i, program_size); |
| WFKnight | 0:9b3d4731edbb | 376 | if (err) { |
| WFKnight | 0:9b3d4731edbb | 377 | free(buf); |
| WFKnight | 0:9b3d4731edbb | 378 | bd->deinit(); |
| WFKnight | 0:9b3d4731edbb | 379 | fs.unlock(); |
| WFKnight | 0:9b3d4731edbb | 380 | return err; |
| WFKnight | 0:9b3d4731edbb | 381 | } |
| WFKnight | 0:9b3d4731edbb | 382 | } |
| WFKnight | 0:9b3d4731edbb | 383 | |
| WFKnight | 0:9b3d4731edbb | 384 | free(buf); |
| WFKnight | 0:9b3d4731edbb | 385 | } |
| WFKnight | 0:9b3d4731edbb | 386 | |
| WFKnight | 0:9b3d4731edbb | 387 | // trim entire device to indicate it is unneeded |
| WFKnight | 0:9b3d4731edbb | 388 | err = bd->trim(0, bd->size()); |
| WFKnight | 0:9b3d4731edbb | 389 | if (err) { |
| WFKnight | 0:9b3d4731edbb | 390 | bd->deinit(); |
| WFKnight | 0:9b3d4731edbb | 391 | fs.unlock(); |
| WFKnight | 0:9b3d4731edbb | 392 | return err; |
| WFKnight | 0:9b3d4731edbb | 393 | } |
| WFKnight | 0:9b3d4731edbb | 394 | |
| WFKnight | 0:9b3d4731edbb | 395 | err = bd->deinit(); |
| WFKnight | 0:9b3d4731edbb | 396 | if (err) { |
| WFKnight | 0:9b3d4731edbb | 397 | fs.unlock(); |
| WFKnight | 0:9b3d4731edbb | 398 | return err; |
| WFKnight | 0:9b3d4731edbb | 399 | } |
| WFKnight | 0:9b3d4731edbb | 400 | |
| WFKnight | 0:9b3d4731edbb | 401 | err = fs.mount(bd, false); |
| WFKnight | 0:9b3d4731edbb | 402 | if (err) { |
| WFKnight | 0:9b3d4731edbb | 403 | fs.unlock(); |
| WFKnight | 0:9b3d4731edbb | 404 | return err; |
| WFKnight | 0:9b3d4731edbb | 405 | } |
| WFKnight | 0:9b3d4731edbb | 406 | |
| WFKnight | 0:9b3d4731edbb | 407 | // Logical drive number, Partitioning rule, Allocation unit size (bytes per cluster) |
| WFKnight | 0:9b3d4731edbb | 408 | FRESULT res = f_mkfs(fs._fsid, FM_ANY | FM_SFD, cluster_size, NULL, 0); |
| WFKnight | 0:9b3d4731edbb | 409 | if (res != FR_OK) { |
| WFKnight | 0:9b3d4731edbb | 410 | fs.unmount(); |
| WFKnight | 0:9b3d4731edbb | 411 | fs.unlock(); |
| WFKnight | 0:9b3d4731edbb | 412 | return fat_error_remap(res); |
| WFKnight | 0:9b3d4731edbb | 413 | } |
| WFKnight | 0:9b3d4731edbb | 414 | |
| WFKnight | 0:9b3d4731edbb | 415 | err = fs.unmount(); |
| WFKnight | 0:9b3d4731edbb | 416 | if (err) { |
| WFKnight | 0:9b3d4731edbb | 417 | fs.unlock(); |
| WFKnight | 0:9b3d4731edbb | 418 | return err; |
| WFKnight | 0:9b3d4731edbb | 419 | } |
| WFKnight | 0:9b3d4731edbb | 420 | |
| WFKnight | 0:9b3d4731edbb | 421 | fs.unlock(); |
| WFKnight | 0:9b3d4731edbb | 422 | return 0; |
| WFKnight | 0:9b3d4731edbb | 423 | } |
| WFKnight | 0:9b3d4731edbb | 424 | |
| WFKnight | 0:9b3d4731edbb | 425 | int FATFileSystem::reformat(BlockDevice *bd, int allocation_unit) |
| WFKnight | 0:9b3d4731edbb | 426 | { |
| WFKnight | 0:9b3d4731edbb | 427 | lock(); |
| WFKnight | 0:9b3d4731edbb | 428 | if (_id != -1) { |
| WFKnight | 0:9b3d4731edbb | 429 | if (!bd) { |
| WFKnight | 0:9b3d4731edbb | 430 | bd = _ffs[_id]; |
| WFKnight | 0:9b3d4731edbb | 431 | } |
| WFKnight | 0:9b3d4731edbb | 432 | |
| WFKnight | 0:9b3d4731edbb | 433 | int err = unmount(); |
| WFKnight | 0:9b3d4731edbb | 434 | if (err) { |
| WFKnight | 0:9b3d4731edbb | 435 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 436 | return err; |
| WFKnight | 0:9b3d4731edbb | 437 | } |
| WFKnight | 0:9b3d4731edbb | 438 | } |
| WFKnight | 0:9b3d4731edbb | 439 | |
| WFKnight | 0:9b3d4731edbb | 440 | if (!bd) { |
| WFKnight | 0:9b3d4731edbb | 441 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 442 | return -ENODEV; |
| WFKnight | 0:9b3d4731edbb | 443 | } |
| WFKnight | 0:9b3d4731edbb | 444 | |
| WFKnight | 0:9b3d4731edbb | 445 | int err = FATFileSystem::format(bd, allocation_unit); |
| WFKnight | 0:9b3d4731edbb | 446 | if (err) { |
| WFKnight | 0:9b3d4731edbb | 447 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 448 | return err; |
| WFKnight | 0:9b3d4731edbb | 449 | } |
| WFKnight | 0:9b3d4731edbb | 450 | |
| WFKnight | 0:9b3d4731edbb | 451 | err = mount(bd); |
| WFKnight | 0:9b3d4731edbb | 452 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 453 | return err; |
| WFKnight | 0:9b3d4731edbb | 454 | } |
| WFKnight | 0:9b3d4731edbb | 455 | |
| WFKnight | 0:9b3d4731edbb | 456 | int FATFileSystem::remove(const char *path) |
| WFKnight | 0:9b3d4731edbb | 457 | { |
| WFKnight | 0:9b3d4731edbb | 458 | Deferred<const char*> fpath = fat_path_prefix(_id, path); |
| WFKnight | 0:9b3d4731edbb | 459 | |
| WFKnight | 0:9b3d4731edbb | 460 | lock(); |
| WFKnight | 0:9b3d4731edbb | 461 | FRESULT res = f_unlink(fpath); |
| WFKnight | 0:9b3d4731edbb | 462 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 463 | |
| WFKnight | 0:9b3d4731edbb | 464 | if (res != FR_OK) { |
| WFKnight | 0:9b3d4731edbb | 465 | debug_if(FFS_DBG, "f_unlink() failed: %d\n", res); |
| WFKnight | 0:9b3d4731edbb | 466 | if (res == FR_DENIED) { |
| WFKnight | 0:9b3d4731edbb | 467 | printf("hi %d -> %d\n", FR_DENIED, -ENOTEMPTY); |
| WFKnight | 0:9b3d4731edbb | 468 | return -ENOTEMPTY; |
| WFKnight | 0:9b3d4731edbb | 469 | } |
| WFKnight | 0:9b3d4731edbb | 470 | } |
| WFKnight | 0:9b3d4731edbb | 471 | return fat_error_remap(res); |
| WFKnight | 0:9b3d4731edbb | 472 | } |
| WFKnight | 0:9b3d4731edbb | 473 | |
| WFKnight | 0:9b3d4731edbb | 474 | int FATFileSystem::rename(const char *oldpath, const char *newpath) |
| WFKnight | 0:9b3d4731edbb | 475 | { |
| WFKnight | 0:9b3d4731edbb | 476 | Deferred<const char*> oldfpath = fat_path_prefix(_id, oldpath); |
| WFKnight | 0:9b3d4731edbb | 477 | Deferred<const char*> newfpath = fat_path_prefix(_id, newpath); |
| WFKnight | 0:9b3d4731edbb | 478 | |
| WFKnight | 0:9b3d4731edbb | 479 | lock(); |
| WFKnight | 0:9b3d4731edbb | 480 | FRESULT res = f_rename(oldfpath, newfpath); |
| WFKnight | 0:9b3d4731edbb | 481 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 482 | |
| WFKnight | 0:9b3d4731edbb | 483 | if (res != FR_OK) { |
| WFKnight | 0:9b3d4731edbb | 484 | debug_if(FFS_DBG, "f_rename() failed: %d\n", res); |
| WFKnight | 0:9b3d4731edbb | 485 | } |
| WFKnight | 0:9b3d4731edbb | 486 | return fat_error_remap(res); |
| WFKnight | 0:9b3d4731edbb | 487 | } |
| WFKnight | 0:9b3d4731edbb | 488 | |
| WFKnight | 0:9b3d4731edbb | 489 | int FATFileSystem::mkdir(const char *path, mode_t mode) |
| WFKnight | 0:9b3d4731edbb | 490 | { |
| WFKnight | 0:9b3d4731edbb | 491 | Deferred<const char*> fpath = fat_path_prefix(_id, path); |
| WFKnight | 0:9b3d4731edbb | 492 | |
| WFKnight | 0:9b3d4731edbb | 493 | lock(); |
| WFKnight | 0:9b3d4731edbb | 494 | FRESULT res = f_mkdir(fpath); |
| WFKnight | 0:9b3d4731edbb | 495 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 496 | |
| WFKnight | 0:9b3d4731edbb | 497 | if (res != FR_OK) { |
| WFKnight | 0:9b3d4731edbb | 498 | debug_if(FFS_DBG, "f_mkdir() failed: %d\n", res); |
| WFKnight | 0:9b3d4731edbb | 499 | } |
| WFKnight | 0:9b3d4731edbb | 500 | return fat_error_remap(res); |
| WFKnight | 0:9b3d4731edbb | 501 | } |
| WFKnight | 0:9b3d4731edbb | 502 | |
| WFKnight | 0:9b3d4731edbb | 503 | int FATFileSystem::stat(const char *path, struct stat *st) |
| WFKnight | 0:9b3d4731edbb | 504 | { |
| WFKnight | 0:9b3d4731edbb | 505 | Deferred<const char*> fpath = fat_path_prefix(_id, path); |
| WFKnight | 0:9b3d4731edbb | 506 | |
| WFKnight | 0:9b3d4731edbb | 507 | lock(); |
| WFKnight | 0:9b3d4731edbb | 508 | FILINFO f; |
| WFKnight | 0:9b3d4731edbb | 509 | memset(&f, 0, sizeof(f)); |
| WFKnight | 0:9b3d4731edbb | 510 | |
| WFKnight | 0:9b3d4731edbb | 511 | FRESULT res = f_stat(fpath, &f); |
| WFKnight | 0:9b3d4731edbb | 512 | if (res != FR_OK) { |
| WFKnight | 0:9b3d4731edbb | 513 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 514 | return fat_error_remap(res); |
| WFKnight | 0:9b3d4731edbb | 515 | } |
| WFKnight | 0:9b3d4731edbb | 516 | |
| WFKnight | 0:9b3d4731edbb | 517 | /* ARMCC doesnt support stat(), and these symbols are not defined by the toolchain. */ |
| WFKnight | 0:9b3d4731edbb | 518 | #ifdef TOOLCHAIN_GCC |
| WFKnight | 0:9b3d4731edbb | 519 | st->st_size = f.fsize; |
| WFKnight | 0:9b3d4731edbb | 520 | st->st_mode = 0; |
| WFKnight | 0:9b3d4731edbb | 521 | st->st_mode |= (f.fattrib & AM_DIR) ? S_IFDIR : S_IFREG; |
| WFKnight | 0:9b3d4731edbb | 522 | st->st_mode |= (f.fattrib & AM_RDO) ? |
| WFKnight | 0:9b3d4731edbb | 523 | (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) : |
| WFKnight | 0:9b3d4731edbb | 524 | (S_IRWXU | S_IRWXG | S_IRWXO); |
| WFKnight | 0:9b3d4731edbb | 525 | #endif /* TOOLCHAIN_GCC */ |
| WFKnight | 0:9b3d4731edbb | 526 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 527 | |
| WFKnight | 0:9b3d4731edbb | 528 | return 0; |
| WFKnight | 0:9b3d4731edbb | 529 | } |
| WFKnight | 0:9b3d4731edbb | 530 | |
| WFKnight | 0:9b3d4731edbb | 531 | int FATFileSystem::statvfs(const char *path, struct statvfs *buf) |
| WFKnight | 0:9b3d4731edbb | 532 | { |
| WFKnight | 0:9b3d4731edbb | 533 | |
| WFKnight | 0:9b3d4731edbb | 534 | memset(buf, 0, sizeof(struct statvfs)); |
| WFKnight | 0:9b3d4731edbb | 535 | FATFS *fs; |
| WFKnight | 0:9b3d4731edbb | 536 | DWORD fre_clust; |
| WFKnight | 0:9b3d4731edbb | 537 | |
| WFKnight | 0:9b3d4731edbb | 538 | lock(); |
| WFKnight | 0:9b3d4731edbb | 539 | FRESULT res = f_getfree(_fsid, &fre_clust, &fs); |
| WFKnight | 0:9b3d4731edbb | 540 | if (res != FR_OK) { |
| WFKnight | 0:9b3d4731edbb | 541 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 542 | return fat_error_remap(res); |
| WFKnight | 0:9b3d4731edbb | 543 | } |
| WFKnight | 0:9b3d4731edbb | 544 | |
| WFKnight | 0:9b3d4731edbb | 545 | buf->f_bsize = fs->ssize; |
| WFKnight | 0:9b3d4731edbb | 546 | buf->f_frsize = fs->ssize; |
| WFKnight | 0:9b3d4731edbb | 547 | buf->f_blocks = (fs->n_fatent - 2) * fs->csize; |
| WFKnight | 0:9b3d4731edbb | 548 | buf->f_bfree = fre_clust * fs->csize; |
| WFKnight | 0:9b3d4731edbb | 549 | buf->f_bavail = buf->f_bfree; |
| WFKnight | 0:9b3d4731edbb | 550 | #if FF_USE_LFN |
| WFKnight | 0:9b3d4731edbb | 551 | buf->f_namemax = FF_LFN_BUF; |
| WFKnight | 0:9b3d4731edbb | 552 | #else |
| WFKnight | 0:9b3d4731edbb | 553 | buf->f_namemax = FF_SFN_BUF; |
| WFKnight | 0:9b3d4731edbb | 554 | #endif |
| WFKnight | 0:9b3d4731edbb | 555 | |
| WFKnight | 0:9b3d4731edbb | 556 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 557 | return 0; |
| WFKnight | 0:9b3d4731edbb | 558 | } |
| WFKnight | 0:9b3d4731edbb | 559 | |
| WFKnight | 0:9b3d4731edbb | 560 | void FATFileSystem::lock() |
| WFKnight | 0:9b3d4731edbb | 561 | { |
| WFKnight | 0:9b3d4731edbb | 562 | _ffs_mutex->lock(); |
| WFKnight | 0:9b3d4731edbb | 563 | } |
| WFKnight | 0:9b3d4731edbb | 564 | |
| WFKnight | 0:9b3d4731edbb | 565 | void FATFileSystem::unlock() |
| WFKnight | 0:9b3d4731edbb | 566 | { |
| WFKnight | 0:9b3d4731edbb | 567 | _ffs_mutex->unlock(); |
| WFKnight | 0:9b3d4731edbb | 568 | } |
| WFKnight | 0:9b3d4731edbb | 569 | |
| WFKnight | 0:9b3d4731edbb | 570 | |
| WFKnight | 0:9b3d4731edbb | 571 | ////// File operations ////// |
| WFKnight | 0:9b3d4731edbb | 572 | int FATFileSystem::file_open(fs_file_t *file, const char *path, int flags) |
| WFKnight | 0:9b3d4731edbb | 573 | { |
| WFKnight | 0:9b3d4731edbb | 574 | debug_if(FFS_DBG, "open(%s) on filesystem [%s], drv [%s]\n", path, getName(), _id); |
| WFKnight | 0:9b3d4731edbb | 575 | |
| WFKnight | 0:9b3d4731edbb | 576 | FIL *fh = new FIL; |
| WFKnight | 0:9b3d4731edbb | 577 | Deferred<const char*> fpath = fat_path_prefix(_id, path); |
| WFKnight | 0:9b3d4731edbb | 578 | |
| WFKnight | 0:9b3d4731edbb | 579 | /* POSIX flags -> FatFS open mode */ |
| WFKnight | 0:9b3d4731edbb | 580 | BYTE openmode; |
| WFKnight | 0:9b3d4731edbb | 581 | if (flags & O_RDWR) { |
| WFKnight | 0:9b3d4731edbb | 582 | openmode = FA_READ | FA_WRITE; |
| WFKnight | 0:9b3d4731edbb | 583 | } else if (flags & O_WRONLY) { |
| WFKnight | 0:9b3d4731edbb | 584 | openmode = FA_WRITE; |
| WFKnight | 0:9b3d4731edbb | 585 | } else { |
| WFKnight | 0:9b3d4731edbb | 586 | openmode = FA_READ; |
| WFKnight | 0:9b3d4731edbb | 587 | } |
| WFKnight | 0:9b3d4731edbb | 588 | |
| WFKnight | 0:9b3d4731edbb | 589 | if (flags & O_CREAT) { |
| WFKnight | 0:9b3d4731edbb | 590 | if (flags & O_TRUNC) { |
| WFKnight | 0:9b3d4731edbb | 591 | openmode |= FA_CREATE_ALWAYS; |
| WFKnight | 0:9b3d4731edbb | 592 | } else { |
| WFKnight | 0:9b3d4731edbb | 593 | openmode |= FA_OPEN_ALWAYS; |
| WFKnight | 0:9b3d4731edbb | 594 | } |
| WFKnight | 0:9b3d4731edbb | 595 | } |
| WFKnight | 0:9b3d4731edbb | 596 | |
| WFKnight | 0:9b3d4731edbb | 597 | if (flags & O_APPEND) { |
| WFKnight | 0:9b3d4731edbb | 598 | openmode |= FA_OPEN_APPEND; |
| WFKnight | 0:9b3d4731edbb | 599 | } |
| WFKnight | 0:9b3d4731edbb | 600 | |
| WFKnight | 0:9b3d4731edbb | 601 | lock(); |
| WFKnight | 0:9b3d4731edbb | 602 | FRESULT res = f_open(fh, fpath, openmode); |
| WFKnight | 0:9b3d4731edbb | 603 | |
| WFKnight | 0:9b3d4731edbb | 604 | if (res != FR_OK) { |
| WFKnight | 0:9b3d4731edbb | 605 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 606 | debug_if(FFS_DBG, "f_open('w') failed: %d\n", res); |
| WFKnight | 0:9b3d4731edbb | 607 | delete fh; |
| WFKnight | 0:9b3d4731edbb | 608 | return fat_error_remap(res); |
| WFKnight | 0:9b3d4731edbb | 609 | } |
| WFKnight | 0:9b3d4731edbb | 610 | |
| WFKnight | 0:9b3d4731edbb | 611 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 612 | |
| WFKnight | 0:9b3d4731edbb | 613 | *file = fh; |
| WFKnight | 0:9b3d4731edbb | 614 | return 0; |
| WFKnight | 0:9b3d4731edbb | 615 | } |
| WFKnight | 0:9b3d4731edbb | 616 | |
| WFKnight | 0:9b3d4731edbb | 617 | int FATFileSystem::file_close(fs_file_t file) |
| WFKnight | 0:9b3d4731edbb | 618 | { |
| WFKnight | 0:9b3d4731edbb | 619 | FIL *fh = static_cast<FIL*>(file); |
| WFKnight | 0:9b3d4731edbb | 620 | |
| WFKnight | 0:9b3d4731edbb | 621 | lock(); |
| WFKnight | 0:9b3d4731edbb | 622 | FRESULT res = f_close(fh); |
| WFKnight | 0:9b3d4731edbb | 623 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 624 | |
| WFKnight | 0:9b3d4731edbb | 625 | delete fh; |
| WFKnight | 0:9b3d4731edbb | 626 | return fat_error_remap(res); |
| WFKnight | 0:9b3d4731edbb | 627 | } |
| WFKnight | 0:9b3d4731edbb | 628 | |
| WFKnight | 0:9b3d4731edbb | 629 | ssize_t FATFileSystem::file_read(fs_file_t file, void *buffer, size_t len) |
| WFKnight | 0:9b3d4731edbb | 630 | { |
| WFKnight | 0:9b3d4731edbb | 631 | FIL *fh = static_cast<FIL*>(file); |
| WFKnight | 0:9b3d4731edbb | 632 | |
| WFKnight | 0:9b3d4731edbb | 633 | lock(); |
| WFKnight | 0:9b3d4731edbb | 634 | UINT n; |
| WFKnight | 0:9b3d4731edbb | 635 | FRESULT res = f_read(fh, buffer, len, &n); |
| WFKnight | 0:9b3d4731edbb | 636 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 637 | |
| WFKnight | 0:9b3d4731edbb | 638 | if (res != FR_OK) { |
| WFKnight | 0:9b3d4731edbb | 639 | debug_if(FFS_DBG, "f_read() failed: %d\n", res); |
| WFKnight | 0:9b3d4731edbb | 640 | return fat_error_remap(res); |
| WFKnight | 0:9b3d4731edbb | 641 | } else { |
| WFKnight | 0:9b3d4731edbb | 642 | return n; |
| WFKnight | 0:9b3d4731edbb | 643 | } |
| WFKnight | 0:9b3d4731edbb | 644 | } |
| WFKnight | 0:9b3d4731edbb | 645 | |
| WFKnight | 0:9b3d4731edbb | 646 | ssize_t FATFileSystem::file_write(fs_file_t file, const void *buffer, size_t len) |
| WFKnight | 0:9b3d4731edbb | 647 | { |
| WFKnight | 0:9b3d4731edbb | 648 | FIL *fh = static_cast<FIL*>(file); |
| WFKnight | 0:9b3d4731edbb | 649 | |
| WFKnight | 0:9b3d4731edbb | 650 | lock(); |
| WFKnight | 0:9b3d4731edbb | 651 | UINT n; |
| WFKnight | 0:9b3d4731edbb | 652 | FRESULT res = f_write(fh, buffer, len, &n); |
| WFKnight | 0:9b3d4731edbb | 653 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 654 | |
| WFKnight | 0:9b3d4731edbb | 655 | if (res != FR_OK) { |
| WFKnight | 0:9b3d4731edbb | 656 | debug_if(FFS_DBG, "f_write() failed: %d", res); |
| WFKnight | 0:9b3d4731edbb | 657 | return fat_error_remap(res); |
| WFKnight | 0:9b3d4731edbb | 658 | } else { |
| WFKnight | 0:9b3d4731edbb | 659 | return n; |
| WFKnight | 0:9b3d4731edbb | 660 | } |
| WFKnight | 0:9b3d4731edbb | 661 | } |
| WFKnight | 0:9b3d4731edbb | 662 | |
| WFKnight | 0:9b3d4731edbb | 663 | int FATFileSystem::file_sync(fs_file_t file) |
| WFKnight | 0:9b3d4731edbb | 664 | { |
| WFKnight | 0:9b3d4731edbb | 665 | FIL *fh = static_cast<FIL*>(file); |
| WFKnight | 0:9b3d4731edbb | 666 | |
| WFKnight | 0:9b3d4731edbb | 667 | lock(); |
| WFKnight | 0:9b3d4731edbb | 668 | FRESULT res = f_sync(fh); |
| WFKnight | 0:9b3d4731edbb | 669 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 670 | |
| WFKnight | 0:9b3d4731edbb | 671 | if (res != FR_OK) { |
| WFKnight | 0:9b3d4731edbb | 672 | debug_if(FFS_DBG, "f_sync() failed: %d\n", res); |
| WFKnight | 0:9b3d4731edbb | 673 | } |
| WFKnight | 0:9b3d4731edbb | 674 | return fat_error_remap(res); |
| WFKnight | 0:9b3d4731edbb | 675 | } |
| WFKnight | 0:9b3d4731edbb | 676 | |
| WFKnight | 0:9b3d4731edbb | 677 | off_t FATFileSystem::file_seek(fs_file_t file, off_t offset, int whence) |
| WFKnight | 0:9b3d4731edbb | 678 | { |
| WFKnight | 0:9b3d4731edbb | 679 | FIL *fh = static_cast<FIL*>(file); |
| WFKnight | 0:9b3d4731edbb | 680 | |
| WFKnight | 0:9b3d4731edbb | 681 | lock(); |
| WFKnight | 0:9b3d4731edbb | 682 | if (whence == SEEK_END) { |
| WFKnight | 0:9b3d4731edbb | 683 | offset += f_size(fh); |
| WFKnight | 0:9b3d4731edbb | 684 | } else if(whence==SEEK_CUR) { |
| WFKnight | 0:9b3d4731edbb | 685 | offset += f_tell(fh); |
| WFKnight | 0:9b3d4731edbb | 686 | } |
| WFKnight | 0:9b3d4731edbb | 687 | |
| WFKnight | 0:9b3d4731edbb | 688 | FRESULT res = f_lseek(fh, offset); |
| WFKnight | 0:9b3d4731edbb | 689 | off_t noffset = fh->fptr; |
| WFKnight | 0:9b3d4731edbb | 690 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 691 | |
| WFKnight | 0:9b3d4731edbb | 692 | if (res != FR_OK) { |
| WFKnight | 0:9b3d4731edbb | 693 | debug_if(FFS_DBG, "lseek failed: %d\n", res); |
| WFKnight | 0:9b3d4731edbb | 694 | return fat_error_remap(res); |
| WFKnight | 0:9b3d4731edbb | 695 | } else { |
| WFKnight | 0:9b3d4731edbb | 696 | return noffset; |
| WFKnight | 0:9b3d4731edbb | 697 | } |
| WFKnight | 0:9b3d4731edbb | 698 | } |
| WFKnight | 0:9b3d4731edbb | 699 | |
| WFKnight | 0:9b3d4731edbb | 700 | off_t FATFileSystem::file_tell(fs_file_t file) |
| WFKnight | 0:9b3d4731edbb | 701 | { |
| WFKnight | 0:9b3d4731edbb | 702 | FIL *fh = static_cast<FIL*>(file); |
| WFKnight | 0:9b3d4731edbb | 703 | |
| WFKnight | 0:9b3d4731edbb | 704 | lock(); |
| WFKnight | 0:9b3d4731edbb | 705 | off_t res = f_tell(fh); |
| WFKnight | 0:9b3d4731edbb | 706 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 707 | |
| WFKnight | 0:9b3d4731edbb | 708 | return res; |
| WFKnight | 0:9b3d4731edbb | 709 | } |
| WFKnight | 0:9b3d4731edbb | 710 | |
| WFKnight | 0:9b3d4731edbb | 711 | off_t FATFileSystem::file_size(fs_file_t file) |
| WFKnight | 0:9b3d4731edbb | 712 | { |
| WFKnight | 0:9b3d4731edbb | 713 | FIL *fh = static_cast<FIL*>(file); |
| WFKnight | 0:9b3d4731edbb | 714 | |
| WFKnight | 0:9b3d4731edbb | 715 | lock(); |
| WFKnight | 0:9b3d4731edbb | 716 | off_t res = f_size(fh); |
| WFKnight | 0:9b3d4731edbb | 717 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 718 | |
| WFKnight | 0:9b3d4731edbb | 719 | return res; |
| WFKnight | 0:9b3d4731edbb | 720 | } |
| WFKnight | 0:9b3d4731edbb | 721 | |
| WFKnight | 0:9b3d4731edbb | 722 | |
| WFKnight | 0:9b3d4731edbb | 723 | ////// Dir operations ////// |
| WFKnight | 0:9b3d4731edbb | 724 | int FATFileSystem::dir_open(fs_dir_t *dir, const char *path) |
| WFKnight | 0:9b3d4731edbb | 725 | { |
| WFKnight | 0:9b3d4731edbb | 726 | FATFS_DIR *dh = new FATFS_DIR; |
| WFKnight | 0:9b3d4731edbb | 727 | Deferred<const char*> fpath = fat_path_prefix(_id, path); |
| WFKnight | 0:9b3d4731edbb | 728 | |
| WFKnight | 0:9b3d4731edbb | 729 | lock(); |
| WFKnight | 0:9b3d4731edbb | 730 | FRESULT res = f_opendir(dh, fpath); |
| WFKnight | 0:9b3d4731edbb | 731 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 732 | |
| WFKnight | 0:9b3d4731edbb | 733 | if (res != FR_OK) { |
| WFKnight | 0:9b3d4731edbb | 734 | debug_if(FFS_DBG, "f_opendir() failed: %d\n", res); |
| WFKnight | 0:9b3d4731edbb | 735 | delete dh; |
| WFKnight | 0:9b3d4731edbb | 736 | return fat_error_remap(res); |
| WFKnight | 0:9b3d4731edbb | 737 | } |
| WFKnight | 0:9b3d4731edbb | 738 | |
| WFKnight | 0:9b3d4731edbb | 739 | *dir = dh; |
| WFKnight | 0:9b3d4731edbb | 740 | return 0; |
| WFKnight | 0:9b3d4731edbb | 741 | } |
| WFKnight | 0:9b3d4731edbb | 742 | |
| WFKnight | 0:9b3d4731edbb | 743 | int FATFileSystem::dir_close(fs_dir_t dir) |
| WFKnight | 0:9b3d4731edbb | 744 | { |
| WFKnight | 0:9b3d4731edbb | 745 | FATFS_DIR *dh = static_cast<FATFS_DIR*>(dir); |
| WFKnight | 0:9b3d4731edbb | 746 | |
| WFKnight | 0:9b3d4731edbb | 747 | lock(); |
| WFKnight | 0:9b3d4731edbb | 748 | FRESULT res = f_closedir(dh); |
| WFKnight | 0:9b3d4731edbb | 749 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 750 | |
| WFKnight | 0:9b3d4731edbb | 751 | delete dh; |
| WFKnight | 0:9b3d4731edbb | 752 | return fat_error_remap(res); |
| WFKnight | 0:9b3d4731edbb | 753 | } |
| WFKnight | 0:9b3d4731edbb | 754 | |
| WFKnight | 0:9b3d4731edbb | 755 | ssize_t FATFileSystem::dir_read(fs_dir_t dir, struct dirent *ent) |
| WFKnight | 0:9b3d4731edbb | 756 | { |
| WFKnight | 0:9b3d4731edbb | 757 | FATFS_DIR *dh = static_cast<FATFS_DIR*>(dir); |
| WFKnight | 0:9b3d4731edbb | 758 | FILINFO finfo; |
| WFKnight | 0:9b3d4731edbb | 759 | |
| WFKnight | 0:9b3d4731edbb | 760 | lock(); |
| WFKnight | 0:9b3d4731edbb | 761 | FRESULT res = f_readdir(dh, &finfo); |
| WFKnight | 0:9b3d4731edbb | 762 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 763 | |
| WFKnight | 0:9b3d4731edbb | 764 | if (res != FR_OK) { |
| WFKnight | 0:9b3d4731edbb | 765 | return fat_error_remap(res); |
| WFKnight | 0:9b3d4731edbb | 766 | } else if (finfo.fname[0] == 0) { |
| WFKnight | 0:9b3d4731edbb | 767 | return 0; |
| WFKnight | 0:9b3d4731edbb | 768 | } |
| WFKnight | 0:9b3d4731edbb | 769 | |
| WFKnight | 0:9b3d4731edbb | 770 | ent->d_type = (finfo.fattrib & AM_DIR) ? DT_DIR : DT_REG; |
| WFKnight | 0:9b3d4731edbb | 771 | |
| WFKnight | 0:9b3d4731edbb | 772 | #if FF_USE_LFN |
| WFKnight | 0:9b3d4731edbb | 773 | if (ent->d_name[0] == 0) { |
| WFKnight | 0:9b3d4731edbb | 774 | // No long filename so use short filename. |
| WFKnight | 0:9b3d4731edbb | 775 | strncpy(ent->d_name, finfo.fname, FF_LFN_BUF); |
| WFKnight | 0:9b3d4731edbb | 776 | } |
| WFKnight | 0:9b3d4731edbb | 777 | #else |
| WFKnight | 0:9b3d4731edbb | 778 | strncpy(ent->d_name, finfo.fname, FF_SFN_BUF); |
| WFKnight | 0:9b3d4731edbb | 779 | #endif |
| WFKnight | 0:9b3d4731edbb | 780 | |
| WFKnight | 0:9b3d4731edbb | 781 | return 1; |
| WFKnight | 0:9b3d4731edbb | 782 | } |
| WFKnight | 0:9b3d4731edbb | 783 | |
| WFKnight | 0:9b3d4731edbb | 784 | void FATFileSystem::dir_seek(fs_dir_t dir, off_t offset) |
| WFKnight | 0:9b3d4731edbb | 785 | { |
| WFKnight | 0:9b3d4731edbb | 786 | FATFS_DIR *dh = static_cast<FATFS_DIR*>(dir); |
| WFKnight | 0:9b3d4731edbb | 787 | off_t dptr = static_cast<off_t>(dh->dptr); |
| WFKnight | 0:9b3d4731edbb | 788 | |
| WFKnight | 0:9b3d4731edbb | 789 | lock(); |
| WFKnight | 0:9b3d4731edbb | 790 | |
| WFKnight | 0:9b3d4731edbb | 791 | if (offset < dptr) { |
| WFKnight | 0:9b3d4731edbb | 792 | f_rewinddir(dh); |
| WFKnight | 0:9b3d4731edbb | 793 | } |
| WFKnight | 0:9b3d4731edbb | 794 | while (dptr < offset) { |
| WFKnight | 0:9b3d4731edbb | 795 | FILINFO finfo; |
| WFKnight | 0:9b3d4731edbb | 796 | FRESULT res; |
| WFKnight | 0:9b3d4731edbb | 797 | |
| WFKnight | 0:9b3d4731edbb | 798 | res = f_readdir(dh, &finfo); |
| WFKnight | 0:9b3d4731edbb | 799 | dptr = dh->dptr; |
| WFKnight | 0:9b3d4731edbb | 800 | if (res != FR_OK) { |
| WFKnight | 0:9b3d4731edbb | 801 | break; |
| WFKnight | 0:9b3d4731edbb | 802 | } else if (finfo.fname[0] == 0) { |
| WFKnight | 0:9b3d4731edbb | 803 | break; |
| WFKnight | 0:9b3d4731edbb | 804 | } |
| WFKnight | 0:9b3d4731edbb | 805 | } |
| WFKnight | 0:9b3d4731edbb | 806 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 807 | } |
| WFKnight | 0:9b3d4731edbb | 808 | |
| WFKnight | 0:9b3d4731edbb | 809 | off_t FATFileSystem::dir_tell(fs_dir_t dir) |
| WFKnight | 0:9b3d4731edbb | 810 | { |
| WFKnight | 0:9b3d4731edbb | 811 | FATFS_DIR *dh = static_cast<FATFS_DIR*>(dir); |
| WFKnight | 0:9b3d4731edbb | 812 | |
| WFKnight | 0:9b3d4731edbb | 813 | lock(); |
| WFKnight | 0:9b3d4731edbb | 814 | off_t offset = dh->dptr; |
| WFKnight | 0:9b3d4731edbb | 815 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 816 | |
| WFKnight | 0:9b3d4731edbb | 817 | return offset; |
| WFKnight | 0:9b3d4731edbb | 818 | } |
| WFKnight | 0:9b3d4731edbb | 819 | |
| WFKnight | 0:9b3d4731edbb | 820 | void FATFileSystem::dir_rewind(fs_dir_t dir) |
| WFKnight | 0:9b3d4731edbb | 821 | { |
| WFKnight | 0:9b3d4731edbb | 822 | FATFS_DIR *dh = static_cast<FATFS_DIR*>(dir); |
| WFKnight | 0:9b3d4731edbb | 823 | |
| WFKnight | 0:9b3d4731edbb | 824 | lock(); |
| WFKnight | 0:9b3d4731edbb | 825 | f_rewinddir(dh); |
| WFKnight | 0:9b3d4731edbb | 826 | unlock(); |
| WFKnight | 0:9b3d4731edbb | 827 | } |
| WFKnight | 0:9b3d4731edbb | 828 |