files_littlefs.cpp
1 #include "files_littlefs.h" 2 3 // if we don't have *some* platform headers, we won't even see logs. 4 #include "sdk_common.h" 5 #include "boards.h" 6 #include "cwalk.h" 7 8 #include "files_flash_qspi.h" 9 #include "files_flash_mx25r6435f.h" 10 11 #include "files_log_module.ii" 12 13 #define LFS_REPORT_AND_RETURN(result) \ 14 do { if (result == LFS_ERR_OK || result > 0) return true; NRF_LOG_WARNING("Flash FS Error: %d", result); return false; } while (0) 15 16 namespace files 17 { 18 namespace 19 { 20 int LittlefsApiRead(const struct lfs_config* c, lfs_block_t block, 21 lfs_off_t off, void *buffer, lfs_size_t size); 22 int LittlefsApiProgram(const struct lfs_config* c, lfs_block_t block, 23 lfs_off_t off, const void *buffer, lfs_size_t size); 24 int LittlefsApiErase(const struct lfs_config* c, lfs_block_t block); 25 int LittlefsApiSync(const struct lfs_config* c); 26 27 struct lfs_config littlefsConfig = { 28 // block device operations 29 .read = LittlefsApiRead, 30 .prog = LittlefsApiProgram, 31 .erase = LittlefsApiErase, 32 .sync = LittlefsApiSync, 33 34 // littlefs operation sizes 35 .read_size = 256, 36 .prog_size = 256, 37 38 // block device configuration set in mount 39 .block_size = 0, 40 .block_count = 0, 41 42 // lfs configuratoin 43 .block_cycles = 500, 44 .cache_size = 512, 45 .lookahead_size = 32, 46 }; 47 48 files_storage_driver_t* flash = &files_flash_qspi_driver; 49 files_storage_info_t* flashInfo = &files_flash_mx25r6435f_info; 50 } 51 52 bool Littlefs::Mount() 53 { 54 NRF_LOG_VERBOSE("Littlefs::Mount()"); 55 56 if (mounted) 57 { 58 return true; 59 } 60 61 // we'll stick to unix-style paths for now. 62 cwk_path_set_style(CWK_STYLE_UNIX); 63 64 NRF_LOG_INFO("Initializing flash for littlefs..."); 65 66 // give the flash api info about the flash chip 67 flash->info = flashInfo; 68 69 files_storage_error_t flashInitStatus = flash->initialize(); 70 71 if (flashInitStatus != FILES_STORAGE_SUCCESS) 72 { 73 NRF_LOG_ERROR("Failed to initialize QSPI flash device! (%d)", flashInitStatus); 74 return false; 75 } 76 else 77 { 78 NRF_LOG_INFO("Flash initialization and configure complete."); 79 } 80 81 // hook up the flash info for littlefs' config 82 // using sector size, because it's smaller on our chip, 83 // we could use 32k/64k blocks, but that seems overkill. 84 littlefsConfig.block_size = flashInfo->sector_size; 85 littlefsConfig.block_count = flashInfo->sector_count; 86 87 NRF_LOG_VERBOSE("Mounting littlefs on flash device..."); 88 89 int error = lfs_mount(&littlefs, &littlefsConfig); 90 91 if (error) 92 { 93 NRF_LOG_WARNING("Unable to mount littlefs on flash: (%d). Will attempt to format.", error); 94 lfs_format(&littlefs, &littlefsConfig); 95 error = lfs_mount(&littlefs, &littlefsConfig); 96 97 if (error) 98 { 99 NRF_LOG_ERROR("Littlefs mount after format failed: (%d) Cannot mount flash file system.", error); 100 return false; 101 } 102 } 103 104 uint32_t flashSize = flashInfo->sector_size * flashInfo->sector_count; 105 uint32_t flashUsedApprox = lfs_fs_size(&littlefs) * littlefsConfig.block_size; 106 uint32_t flashSizeMb = flashSize / 1024 / 1024; 107 float flashUsedApproxMb = flashUsedApprox / 1024.0f / 1024.0f; 108 uint32_t percent = flashUsedApprox / flashSize; 109 NRF_LOG_INFO("Mounted `%s`", flashInfo->name); 110 NRF_LOG_INFO("`%s`: Approx " NRF_LOG_FLOAT_MARKER "MB of %dMB used. (%d%%)", 111 flashInfo->name, NRF_LOG_FLOAT(flashUsedApproxMb), flashSizeMb, percent); 112 113 mounted = true; 114 return true; 115 } 116 117 bool Littlefs::Unmount() 118 { 119 NRF_LOG_VERBOSE("Littlefs::Unmount()"); 120 121 if (mounted == false) 122 { 123 return true; 124 } 125 126 NRF_LOG_INFO("Unmounting littlefs on flash device..."); 127 128 int error = lfs_unmount(&littlefs); 129 130 if (error) 131 { 132 NRF_LOG_ERROR("Littlefs unmount failed: (%d)", error); 133 return false; 134 } 135 136 flash->uninitialize(); 137 138 NRF_LOG_INFO("Unmounted."); 139 140 mounted = false; 141 142 return true; 143 } 144 145 bool Littlefs::FileOpen(FlashFile& file, const char* filePath, uint16_t flags) 146 { 147 NRF_LOG_VERBOSE("Littlefs::FileOpen(): path: %s", NRF_LOG_PUSH((char*)filePath)); 148 int result = lfs_file_open(&littlefs, &file, filePath, flags); 149 150 // if the error was no directory entry and we are allowing file creation, 151 if (result == LFS_ERR_NOENT && (flags & LFS_O_CREAT) != 0) 152 { 153 // try creating the directory, then open/create the file. 154 char path[LFS_NAME_MAX]; 155 size_t length; 156 cwk_path_get_dirname(filePath, &length); 157 sprintf(path, "%.*s", length - 1, filePath); // -1 on length for no trailing slash. 158 159 FlashDirectory dir; 160 if (DirectoryCreate(dir, path)) 161 { 162 NRF_LOG_VERBOSE("Fallback Littlefs::DirectoryCreate() succeded, retrying open."); 163 // directory created, now try opening the file again. 164 result = lfs_file_open(&littlefs, &file, filePath, flags); 165 166 // if it still failed, may god have mercy on their souls... 167 } 168 } 169 170 NRF_LOG_VERBOSE("Littlefs::FileOpen(): Finished, file.id: %d", file.id); 171 LFS_REPORT_AND_RETURN(result); 172 } 173 174 bool Littlefs::FileRead(FlashFile& file, void* buffer, size_t amountToRead, size_t* amountRead) 175 { 176 NRF_LOG_VERBOSE("Littlefs::FileRead(): file: %d", file.id); 177 lfs_ssize_t readResult = lfs_file_read(&littlefs, &file, buffer, amountToRead); 178 179 if (readResult >= 0) 180 { 181 (*amountRead) = readResult; 182 } 183 184 LFS_REPORT_AND_RETURN(readResult); 185 } 186 187 bool Littlefs::FileWrite(FlashFile& file, const void* buffer, size_t bufferLength, size_t* amountWritten) 188 { 189 NRF_LOG_VERBOSE("Littlefs::FileWrite(): file: %d", file.id); 190 lfs_ssize_t writeResult = lfs_file_write(&littlefs, &file, buffer, bufferLength); 191 192 if (writeResult >= 0) 193 { 194 (*amountWritten) = writeResult; 195 } 196 197 LFS_REPORT_AND_RETURN(writeResult); 198 } 199 200 bool Littlefs::FileSeek(FlashFile& file, size_t offset) 201 { 202 NRF_LOG_VERBOSE("Littlefs::FileSeek(): file: %d", file.id); 203 lfs_off_t resultOffset = lfs_file_seek(&littlefs, &file, offset, LFS_SEEK_SET); 204 LFS_REPORT_AND_RETURN(resultOffset); 205 } 206 207 bool Littlefs::FileClose(FlashFile& file) 208 { 209 NRF_LOG_VERBOSE("Littlefs::FileClose(): file: %d", file.id); 210 int result = lfs_file_close(&littlefs, &file); 211 LFS_REPORT_AND_RETURN(result); 212 } 213 214 bool Littlefs::DirectoryOpen(FlashDirectory& dir, const char* directoryPath) 215 { 216 NRF_LOG_VERBOSE("Littlefs::DirectoryOpen(): path: %s", NRF_LOG_PUSH((char*)directoryPath)); 217 int result = lfs_dir_open(&littlefs, &dir, directoryPath); 218 NRF_LOG_VERBOSE("Littlefs::DirectoryOpen(): Finished, dir.id: %d", dir.id); 219 LFS_REPORT_AND_RETURN(result); 220 } 221 222 bool Littlefs::DirectoryRead(FlashDirectory& dir, FlashFileInfo& info) 223 { 224 NRF_LOG_VERBOSE("Littlefs::DirectoryRead(): dir: %d", dir.id); 225 int result = lfs_dir_read(&littlefs, &dir, &info); 226 LFS_REPORT_AND_RETURN(result); 227 } 228 229 bool Littlefs::DirectoryClose(FlashDirectory& dir) 230 { 231 NRF_LOG_VERBOSE("Littlefs::DirectoryClose(): dir: %d", dir.id); 232 int result = lfs_dir_close(&littlefs, &dir); 233 LFS_REPORT_AND_RETURN(result); 234 } 235 236 bool Littlefs::DirectoryCreate(FlashDirectory& dir, const char* directoryPath) 237 { 238 NRF_LOG_VERBOSE("Littlefs::DirectoryCreate(): path: %s", NRF_LOG_PUSH((char*)directoryPath)); 239 int result = lfs_mkdir(&littlefs, directoryPath); 240 LFS_REPORT_AND_RETURN(result); 241 } 242 243 bool Littlefs::FileStat(FlashFileInfo& fileInfo, const char* path) 244 { 245 NRF_LOG_VERBOSE("Littlefs::FileStat(): path: %s", NRF_LOG_PUSH((char*)path)); 246 int result = lfs_stat(&littlefs, path, &fileInfo); 247 LFS_REPORT_AND_RETURN(result); 248 } 249 250 const char* Littlefs::GetFlashName() 251 { 252 return flashInfo->name; 253 } 254 255 namespace 256 { 257 int StorageDriverToLfsError(int error) 258 { 259 if (error >= 0 || error == FILES_STORAGE_SUCCESS) 260 { 261 return LFS_ERR_OK; 262 } 263 264 // errors come back from the API negative, 265 // but defines are positive. 266 error = -error; 267 268 switch (error) 269 { 270 default: 271 case FILES_STORAGE_ERROR_INTERNAL: 272 case FILES_STORAGE_ERROR_INVALID_LENGTH: 273 case FILES_STORAGE_ERROR_NULL: 274 case FILES_STORAGE_ERROR_BUSY: 275 case FILES_STORAGE_ERROR_FORBIDDEN: 276 case FILES_STORAGE_ERROR_TIMEOUT: 277 case FILES_STORAGE_ERROR_NOT_SUPPORTED: 278 return LFS_ERR_IO; 279 case FILES_STORAGE_ERROR_INVALID_PARAM: 280 case FILES_STORAGE_ERROR_INVALID_STATE: 281 case FILES_STORAGE_ERROR_INVALID_ADDR: 282 return LFS_ERR_INVAL; 283 case FILES_STORAGE_ERROR_NO_MEM: 284 return LFS_ERR_NOMEM; 285 case FILES_STORAGE_ERROR_ALREADY_INITIALIZED: 286 return LFS_ERR_OK; 287 } 288 } 289 290 int LittlefsApiRead(const struct lfs_config* c, lfs_block_t block, 291 lfs_off_t off, void *buffer, lfs_size_t size) 292 { 293 size_t address = (block * c->block_size) + off; 294 295 int32_t result = flash->read(address, (uint8_t*)buffer, size); 296 297 return StorageDriverToLfsError(result); 298 } 299 300 int LittlefsApiProgram(const struct lfs_config* c, lfs_block_t block, 301 lfs_off_t off, const void *buffer, lfs_size_t size) 302 { 303 size_t address = (block * c->block_size) + off; 304 305 int32_t result = flash->program(address, (uint8_t*)buffer, size); 306 307 return StorageDriverToLfsError(result); 308 } 309 310 int LittlefsApiErase(const struct lfs_config* c, lfs_block_t block) 311 { 312 // no offset, because we have to erase the whole block! 313 size_t address = (block * c->block_size); 314 315 uint32_t result = flash->erase_sector(address); 316 317 return StorageDriverToLfsError(result); 318 } 319 320 int LittlefsApiSync(const struct lfs_config* c) 321 { 322 int32_t result = flash->sync(); 323 324 return StorageDriverToLfsError(result); 325 } 326 } 327 } // namespace files