// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md // ffFAT glue layer code #include #include #include #include #include #include #include #include #include #include #include #include #include namespace purefs::fs::drivers::ffat::internal { namespace { cpp_freertos::MutexRecursive g_lock; // Device manager part / ffat LUN map std::unordered_map g_registered_vol; fs::internal::handle_mapper> g_disk_handles; std::weak_ptr g_disk_mm; } // namespace namespace { inline auto disk_handle_from_pdrive(size_t pdrive) { cpp_freertos::LockGuard _lck(g_lock); return (g_disk_handles.exists(pdrive)) ? g_disk_handles[pdrive] : nullptr; } } // namespace int append_volume(blkdev::disk_fd diskh) { cpp_freertos::LockGuard _lck(g_lock); const auto vol = g_registered_vol.find(std::string(diskh->name())); if (vol != std::end(g_registered_vol)) { return vol->second; } if (g_disk_handles.size() >= FF_VOLUMES) { return -EOVERFLOW; } auto diskm = g_disk_mm.lock(); if (!diskm) { return -EIO; } const auto hwnd = g_disk_handles.insert(diskh); g_registered_vol.emplace(std::make_pair(diskh->name(), hwnd)); return hwnd; } int remove_volume(blkdev::disk_fd diskh) { cpp_freertos::LockGuard _lck(g_lock); const auto vol_it = g_registered_vol.find(std::string(diskh->name())); if (vol_it == std::end(g_registered_vol)) { return -EBADF; } g_disk_handles.remove(vol_it->second); g_registered_vol.erase(vol_it); return 0; } void reset_volumes(std::shared_ptr diskmm) { cpp_freertos::LockGuard _lck(g_lock); g_disk_mm = diskmm; g_registered_vol.clear(); g_disk_handles.clear(); } extern "C" { DSTATUS disk_initialize(BYTE pdrv) { return disk_handle_from_pdrive(pdrv) ? (0) : (STA_NOINIT); } DSTATUS disk_status(BYTE pdrv) { const auto diskh = disk_handle_from_pdrive(pdrv); if (!diskh) { return RES_ERROR; } auto diskmm = g_disk_mm.lock(); if (!diskmm) { return RES_ERROR; } const auto status = diskmm->status(diskh); switch (status) { case blkdev::media_status::healthly: return 0; case blkdev::media_status::error: return STA_NOINIT; case blkdev::media_status::nomedia: return STA_NODISK; case blkdev::media_status::uninit: return STA_NOINIT; case blkdev::media_status::wprotect: return RES_WRPRT; } return STA_NOINIT; } DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) { const auto diskh = disk_handle_from_pdrive(pdrv); if (!diskh) { return RES_NOTRDY; } const auto diskmm = g_disk_mm.lock(); if (!diskmm) { return RES_NOTRDY; } const auto res = diskmm->read(diskh, buff, sector, count); if (res < 0) { LOG_ERROR("Read error %i", res); return (res == -ERANGE) ? (RES_PARERR) : (RES_ERROR); } else { return RES_OK; } } DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) { const auto diskh = disk_handle_from_pdrive(pdrv); if (!diskh) { return RES_NOTRDY; } const auto diskmm = g_disk_mm.lock(); if (!diskmm) { return RES_NOTRDY; } const auto res = diskmm->write(diskh, buff, sector, count); if (res < 0) { LOG_ERROR("Write error %i", res); return (res == -ERANGE) ? (RES_PARERR) : (RES_ERROR); } else { return RES_OK; } } DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) { const auto diskh = disk_handle_from_pdrive(pdrv); if (!diskh) { return RES_NOTRDY; } const auto diskmm = g_disk_mm.lock(); if (!diskmm) { return RES_NOTRDY; } blkdev::scount_t ret; switch (cmd) { case CTRL_SYNC: ret = diskmm->sync(diskh); break; case GET_SECTOR_COUNT: ret = diskmm->get_info(diskh, blkdev::info_type::sector_count); if (ret >= 0) { if (!buff) { ret = -EINVAL; } else { *reinterpret_cast(buff) = ret; ret = 0; } } break; case GET_SECTOR_SIZE: ret = diskmm->get_info(diskh, blkdev::info_type::sector_size); if (ret >= 0) { if (!buff) { ret = -EINVAL; } else { *reinterpret_cast(buff) = ret; } } break; case GET_BLOCK_SIZE: ret = diskmm->get_info(diskh, blkdev::info_type::erase_block); if (ret >= 0) { if (!buff) { ret = -EINVAL; } else { *reinterpret_cast(buff) = ret; } } break; case CTRL_TRIM: if (!buff) { ret = -EINVAL; } else { auto lba = reinterpret_cast(buff); const auto lba_start = lba[0]; const auto lba_end = lba[1]; // Only when trim is supported call method ret = diskmm->get_info(diskh, blkdev::info_type::erase_block); if (ret > 0) { ret = diskmm->erase(diskh, lba_start, lba_end - lba_start + 1); } else { ret = 0; } } break; default: ret = -EINVAL; break; } // Finally translate error switch (ret) { case 0: return RES_OK; case -EINVAL: return RES_PARERR; default: return RES_ERROR; } } } } // namespace purefs::fs::drivers::ffat::internal