/ src / files / files_fat32.cpp
files_fat32.cpp
  1  #include "files_fat32.h"
  2  
  3  #include "diskio.h"
  4  #include "diskio_blkdev.h"
  5  #include "cwalk.h"
  6  
  7  #include "global/global_data.h"
  8  
  9  #include "files_log_module.ii"
 10  
 11  namespace files
 12  {
 13  	namespace
 14  	{
 15  		constexpr const char* DriveRoot = "";
 16  		constexpr uint8_t MountOptionDelayed = 0;
 17  		constexpr uint8_t MountOptionImmediate = 1;
 18  		constexpr uint8_t UnmountOptionNone = 0;
 19  
 20  		const nrf_block_dev_sdc_t* sdCardBlockDevice = nullptr;
 21  	}
 22  
 23  	Fat32::Fat32()
 24  	{
 25  
 26  	}
 27  
 28  	//////////////////////////////////////////////////////////////////////////
 29  	// Disk Operations
 30  
 31  	bool Fat32::Mount()
 32  	{
 33  		if (mounted)
 34  		{
 35  			// already mounted.
 36  			return true;
 37  		}
 38  
 39  		if (!Initialize())
 40  		{
 41  			return false;
 42  		}
 43  
 44  		NRF_LOG_INFO("Mounting FAT filesystem on SDC...");
 45  
 46  		FRESULT mountResult = f_mount(&fileSystem, DriveRoot, MountOptionImmediate);
 47  
 48  		if (mountResult != FR_OK)
 49  		{
 50  			NRF_LOG_ERROR("Failed to mount filesystem on SDC! (%d)", mountResult);
 51  			return false;
 52  		}
 53  
 54  		f_getlabel(DriveRoot, driveLabel, &driveSerial);
 55  
 56  		NRF_LOG_INFO("Mounted `%s` (%08X).", nrf_log_push(driveLabel), driveSerial);
 57  
 58  		mounted = true;
 59  		return true;
 60  	}
 61  
 62  	bool Fat32::Unmount()
 63  	{
 64  		if (mounted == false)
 65  		{
 66  			return true;
 67  		}
 68  
 69  		NRF_LOG_INFO("Unmounting SDC...");
 70  
 71  		FRESULT unmountResult = f_mount(nullptr, DriveRoot, UnmountOptionNone);
 72  
 73  		if (unmountResult != FR_OK)
 74  		{
 75  			NRF_LOG_ERROR("Failed to unmount SDC! (%d)", unmountResult);
 76  			return false;
 77  		}
 78  
 79  		memset(driveLabel, 0, sizeof(driveLabel));
 80  		driveSerial = 0;
 81  
 82  		NRF_LOG_INFO("Unmounted.");
 83  
 84  		mounted = false;
 85  		return true;
 86  	}
 87  
 88  	//////////////////////////////////////////////////////////////////////////
 89  	// Directory Operations
 90  
 91  	bool Fat32::DirectoryOpen(Fat32Directory& dir, const char *directoryPath)
 92  	{
 93  		if (mounted == false)
 94  		{
 95  			NRF_LOG_WARNING("Cannot DirectoryOpen while SDC is not mounted.");
 96  			return false;
 97  		}
 98  
 99  		FRESULT status = f_opendir(&dir, directoryPath);
100  		if (status != FR_OK)
101  		{
102  			NRF_LOG_WARNING("Failed to DirectoryOpen `%s`: %d", nrf_log_push((char*)directoryPath), status);
103  			return false;
104  		}
105  
106  		return true;
107  	}
108  
109  	bool Fat32::DirectoryRead(Fat32Directory& dir, Fat32FileInfo& info)
110  	{
111  		if (mounted == false)
112  		{
113  			NRF_LOG_WARNING("Cannot DirectoryRead while SDC is not mounted.");
114  			return false;
115  		}
116  
117  		FRESULT status = f_readdir(&dir, &info);
118  		if (status != FR_OK)
119  		{
120  			NRF_LOG_WARNING("Failed to DirectoryRead: %d", status);
121  			return false;
122  		}
123  
124  		// if we read something with a null name, we're done.
125  		// otherwise, the read was good.
126  		return info.fname[0] != '\0';
127  	}
128  
129  	bool Fat32::DirectoryClose(Fat32Directory& dir)
130  	{
131  		if (mounted == false)
132  		{
133  			NRF_LOG_WARNING("Cannot DirectoryClose while SDC is not mounted.");
134  			return false;
135  		}
136  
137  		FRESULT status = f_closedir(&dir);
138  		if (status != FR_OK)
139  		{
140  			NRF_LOG_WARNING("Failed to DirectoryClose: %d", status);
141  			return false;
142  		}
143  
144  		return true;
145  	}
146  
147  	//////////////////////////////////////////////////////////////////////////
148  	// File Operations
149  
150  	bool Fat32::FileOpen(Fat32File& file, const char *filePath, uint16_t mode)
151  	{
152  		if (mounted == false)
153  		{
154  			NRF_LOG_WARNING("Cannot FileOpen while SDC is not mounted.");
155  			return false;
156  		}
157  
158  		FRESULT status = f_open(&file, filePath, mode);
159  		if (status != FR_OK)
160  		{
161  			NRF_LOG_WARNING("Failed to FileOpen `%s`: %d", filePath, status);
162  			return false;
163  		}
164  
165  		return true;
166  	}
167  
168  	bool Fat32::FileRead(Fat32File& file, void *buffer, size_t amountToRead, size_t *amountRead)
169  	{
170  		if (mounted == false)
171  		{
172  			NRF_LOG_WARNING("Cannot FileRead while SDC is not mounted.");
173  			return false;
174  		}
175  
176  		FRESULT status = f_read(&file, buffer, amountToRead, amountRead);
177  		if (status != FR_OK)
178  		{
179  			NRF_LOG_WARNING("Failed to FileRead: %d", status);
180  			return false;
181  		}
182  
183  		return true;
184  	}
185  
186  	bool Fat32::FileWrite(Fat32File& file, const void *buffer, size_t bufferLength, size_t *amountWritten)
187  	{
188  		if (mounted == false)
189  		{
190  			NRF_LOG_WARNING("Cannot FileWrite while SDC is not mounted.");
191  			return false;
192  		}
193  
194  		FRESULT status = f_write(&file, buffer, bufferLength, amountWritten);
195  		if (status != FR_OK)
196  		{
197  			NRF_LOG_WARNING("Failed to FileWrite: %d", status);
198  			return false;
199  		}
200  
201  		return true;
202  	}
203  
204  	bool Fat32::FileSeek(Fat32File& file, size_t offset)
205  	{
206  		if (mounted == false)
207  		{
208  			NRF_LOG_WARNING("Cannot FileSeek while SDC is not mounted.");
209  			return false;
210  		}
211  
212  		FRESULT status = f_lseek(&file, offset);
213  		if (status != FR_OK)
214  		{
215  			NRF_LOG_WARNING("Failed to FileSeek: %d", status);
216  			return false;
217  		}
218  
219  		return true;
220  	}
221  
222  	bool Fat32::FileClose(Fat32File& file)
223  	{
224  		if (mounted == false)
225  		{
226  			NRF_LOG_WARNING("Cannot FileClose while SDC is not mounted.");
227  			return false;
228  		}
229  
230  		FRESULT status = f_close(&file);
231  		if (status != FR_OK)
232  		{
233  			NRF_LOG_WARNING("Failed to FileClose: %d", status);
234  			return false;
235  		}
236  
237  		return true;
238  	}
239  
240  	bool Fat32::FileStat(Fat32FileInfo& fileInfo, const char *path)
241  	{
242  		if (mounted == false)
243  		{
244  			NRF_LOG_WARNING("Cannot FileStat while SDC is not mounted.");
245  			return false;
246  		}
247  
248  		FRESULT status = f_stat(path, &fileInfo);
249  		if (status != FR_OK)
250  		{
251  			NRF_LOG_WARNING("Failed to FileStat: %d", status);
252  			return false;
253  		}
254  
255  		return true;
256  	}
257  
258  	//////////////////////////////////////////////////////////////////////////
259  	// Internal Operations
260  
261  	// USB Handlers
262  
263  	void Fat32::UsbDidDisable(app_usbd_event_type_t event)
264  	{
265  		if (mounted == false && autoRemountAfterUsbDisconnect)
266  		{
267  			NRF_LOG_INFO("USB stopped, automatically remounting SDC.");
268  			Initialize();
269  			Mount();
270  		}
271  		else
272  		{
273  			NRF_LOG_INFO("USB stopped, but we don't care about remounting.");
274  		}
275  	}
276  
277  	void Fat32::UsbWillEnable(app_usbd_event_type_t event)
278  	{
279  		if (initialized)
280  		{
281  			NRF_LOG_INFO("USB got power, uninitializing SDC to give it priority.");
282  			Uninitialize();
283  		}
284  		else
285  		{
286  			NRF_LOG_INFO("USB got power, but we were not initialized, ignoring.");
287  		}
288  	}
289  
290  	bool Fat32::Initialize()
291  	{
292  		if (initialized)
293  		{
294  			return true;
295  		}
296  
297  		// zero filesystem
298  		memset(&fileSystem, 0, sizeof(FATFS));
299  
300  		if (registered == false && RegisterBlockDevice() == false)
301  		{
302  			// we weren't able to register, can't init.
303  			NRF_LOG_ERROR("Cannot initialize SDC, registration failed.");
304  			return false;
305  		}
306  
307  		// we'll stick to unix-style paths for now.
308  		cwk_path_set_style(CWK_STYLE_UNIX);
309  
310  		// #hardcode -- we only support one disk right now.
311  		diskIndex = 0;
312  
313  		NRF_LOG_VERBOSE("Initializing disk %d (SDC)...", diskIndex);
314  
315  		// trying 3 times, cause that's what they did in the example
316  		// and here: https://devzone.nordicsemi.com/f/nordic-q-a/59811/fatfs-example-bug
317  		DSTATUS diskStatus = STA_NOINIT;
318  		for (uint32_t retries = 3; retries && diskStatus; --retries)
319  		{
320  			diskStatus = disk_initialize(0);
321  		}
322  
323  		if (diskStatus)
324  		{
325  			NRF_LOG_ERROR("Failed to initialize SDC (disk %d) with error: %d.", diskIndex, diskStatus);
326  			return false;
327  		}
328  
329  		uint32_t blocks_per_mb = (1024uL * 1024uL) / sdCardBlockDevice->block_dev.p_ops->geometry(&sdCardBlockDevice->block_dev)->blk_size;
330  		uint32_t capacity = sdCardBlockDevice->block_dev.p_ops->geometry(&sdCardBlockDevice->block_dev)->blk_count / blocks_per_mb;
331  
332  		NRF_LOG_INFO("Initialized SDC (disk %d) with a capacity of %d MB", diskIndex, capacity);
333  
334  		initialized = true;
335  
336  		return true;
337  	}
338  
339  	bool Fat32::Uninitialize()
340  	{
341  		if (initialized == false)
342  		{
343  			return true;
344  		}
345  
346  		if (Unmount() == false)
347  		{
348  			NRF_LOG_ERROR("Cannot uninitialize SDC, was unable to unmount.");
349  			return false;
350  		}
351  
352  		NRF_LOG_INFO("Uninitializing SDC...");
353  
354  		DSTATUS status = disk_uninitialize(diskIndex);
355  
356  		if (status != STA_NOINIT)
357  		{
358  			NRF_LOG_ERROR("Failed to uninitialize SDC %d with error: %d.", diskIndex, status);
359  			return false;
360  		}
361  
362  		diskIndex = -1;
363  		initialized = false;
364  		registered = false;
365  		memset(&fileSystem, 0, sizeof(FATFS));
366  
367  		NRF_LOG_INFO("Uninitialized.");
368  
369  		return true;
370  	}
371  
372  	bool Fat32::RegisterBlockDevice()
373  	{
374  		if (registered)
375  		{
376  			return true;
377  		}
378  
379  		NRF_LOG_VERBOSE("Registering SDC Block Device...");
380  
381  		sdCardBlockDevice = get_sdc_block_device();
382  
383  		if (sdCardBlockDevice == nullptr)
384  		{
385  			// uh oh.
386  			NRF_LOG_ERROR("Failed to get valid SDC block device. Cannot register drive.");
387  			return false;
388  		}
389  
390  		static diskio_blkdev_t drives[] = {
391  			DISKIO_BLOCKDEV_CONFIG(NRF_BLOCKDEV_BASE_ADDR(*sdCardBlockDevice, block_dev), NULL)
392  		};
393  
394  		diskio_blockdev_register(drives, ARRAY_SIZE(drives));
395  
396  		NRF_LOG_VERBOSE("Registered.");
397  
398  		registered = true;
399  
400  		return true;
401  	}
402  } // namespace files