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