/ src / files / files_littlefs.cpp
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