From 7b01d59b3ba564afa819eb97137c482f45ac6438 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 26 Nov 2020 03:27:07 -0800 Subject: [PATCH] pf2: add volume init, context register/unregister --- .../libvapours/include/vapours/prfile2.hpp | 2 + .../vapours/prfile2/pdm/prfile2_pdm_api.hpp | 3 + .../prfile2/pf/prfile2_pf_api_common.hpp | 2 + .../vapours/prfile2/pf/prfile2_pf_types.hpp | 121 +++++++--- .../vapours/prfile2/prfile2_common.hpp | 80 +++--- .../include/vapours/prfile2/prfile2_entry.hpp | 62 +++++ .../include/vapours/prfile2/prfile2_fat.hpp | 64 +++++ .../include/vapours/prfile2/prfile2_fatfs.hpp | 23 ++ .../vapours/prfile2/prfile2_handle.hpp | 2 +- .../vapours/prfile2/prfile2_system.hpp | 2 +- .../vapours/prfile2/prfile2_volume.hpp | 196 +++++++++++++++ .../source/prfile2/pdm/prfile2_pdm_api.cpp | 23 ++ .../source/prfile2/pf/prfile2_pf_api.cpp | 62 +++++ .../source/prfile2/pf/prfile2_pf_errnum.hpp | 28 +++ .../prfile2/prfile2_critical_section.cpp | 2 +- .../source/prfile2/prfile2_fatfs.cpp | 32 +++ .../source/prfile2/prfile2_system.cpp | 6 +- .../source/prfile2/prfile2_volume.cpp | 227 ++++++++++++++++++ .../source/prfile2/prfile2_volume_set.hpp | 31 +++ 19 files changed, 883 insertions(+), 85 deletions(-) create mode 100644 libraries/libvapours/include/vapours/prfile2/prfile2_entry.hpp create mode 100644 libraries/libvapours/include/vapours/prfile2/prfile2_fat.hpp create mode 100644 libraries/libvapours/include/vapours/prfile2/prfile2_fatfs.hpp create mode 100644 libraries/libvapours/include/vapours/prfile2/prfile2_volume.hpp create mode 100644 libraries/libvapours/source/prfile2/pf/prfile2_pf_api.cpp create mode 100644 libraries/libvapours/source/prfile2/pf/prfile2_pf_errnum.hpp create mode 100644 libraries/libvapours/source/prfile2/prfile2_fatfs.cpp create mode 100644 libraries/libvapours/source/prfile2/prfile2_volume.cpp create mode 100644 libraries/libvapours/source/prfile2/prfile2_volume_set.hpp diff --git a/libraries/libvapours/include/vapours/prfile2.hpp b/libraries/libvapours/include/vapours/prfile2.hpp index 9e712c6de..45a147200 100644 --- a/libraries/libvapours/include/vapours/prfile2.hpp +++ b/libraries/libvapours/include/vapours/prfile2.hpp @@ -25,3 +25,5 @@ #include #include #include +#include +#include diff --git a/libraries/libvapours/include/vapours/prfile2/pdm/prfile2_pdm_api.hpp b/libraries/libvapours/include/vapours/prfile2/pdm/prfile2_pdm_api.hpp index 66a56caab..e1acc527a 100644 --- a/libraries/libvapours/include/vapours/prfile2/pdm/prfile2_pdm_api.hpp +++ b/libraries/libvapours/include/vapours/prfile2/pdm/prfile2_pdm_api.hpp @@ -25,4 +25,7 @@ namespace ams::prfile2::pdm { pdm::Error OpenDisk(InitDisk *init_disk_table, HandleType *out); pdm::Error CloseDisk(HandleType handle); + pdm::Error OpenPartition(HandleType disk_handle, u16 part_id, HandleType *out); + pdm::Error ClosePartition(HandleType handle); + } diff --git a/libraries/libvapours/include/vapours/prfile2/pf/prfile2_pf_api_common.hpp b/libraries/libvapours/include/vapours/prfile2/pf/prfile2_pf_api_common.hpp index c8f13b2b6..1fe17f7b9 100644 --- a/libraries/libvapours/include/vapours/prfile2/pf/prfile2_pf_api_common.hpp +++ b/libraries/libvapours/include/vapours/prfile2/pf/prfile2_pf_api_common.hpp @@ -22,4 +22,6 @@ namespace ams::prfile2::pf { int Initialize(u32 config, void *param); + int Attach(DriveTable **drive_table); + } diff --git a/libraries/libvapours/include/vapours/prfile2/pf/prfile2_pf_types.hpp b/libraries/libvapours/include/vapours/prfile2/pf/prfile2_pf_types.hpp index 870d254fb..6944cd977 100644 --- a/libraries/libvapours/include/vapours/prfile2/pf/prfile2_pf_types.hpp +++ b/libraries/libvapours/include/vapours/prfile2/pf/prfile2_pf_types.hpp @@ -15,46 +15,59 @@ */ #pragma once #include +#include namespace ams::prfile2::pf { + using DriveCharacter = char; + enum Error { - Error_NoError = 0, - Error_Ok = Error_NoError, + Error_NoError = 0, + Error_Ok = Error_NoError, - Error_EPERM = 1, - Error_ENOENT = 2, - Error_ESRCH = 3, - Error_EIO = 5, - Error_ENOEXEC = 8, - Error_EBADF = 9, - Error_ENOMEM = 12, - Error_EACCES = 13, - Error_EBUSY = 16, - Error_EEXIST = 17, - Error_ENODEV = 19, - Error_EISDIR = 21, - Error_EINVAL = 22, - Error_ENFILE = 23, - Error_EMFILE = 24, - Error_EFBIG = 27, - Error_ENOSPC = 28, - Error_ENOLCK = 46, - Error_ENOSYS = 88, - Error_ENOTEMPTY = 90, + Error_Generic = -1, - Error_EMOD_NOTREG = 100, - Error_EMOD_NOTSPRT = 101, - Error_EMOD_FCS = 102, - Error_EMOD_SAFE = 103, - - Error_ENOMEDIUM = 123, - - Error_EEXFAT_NOTSPRT = 200, - - Error_DFNC = 0x1000, - - Error_SYSTEM = -1, + Error_InvalidFileName = 1, + Error_InvalidPathName = 2, + Error_FileNotFound = 3, + Error_TooManyVolumesAttached = 4, + Error_DirectoryFull = 5, + Error_VolumeFull = 6, + Error_InvalidDiskFormat = 7, + Error_FileAlreadyExists = 8, + Error_VolumeNotMounted = 9, + Error_InvalidParameter = 10, + Error_WriteProtected = 11, + Error_UnsupportedFormat = 12, + Error_BrokenClusterChain = 13, + Error_InvalidClusterNum = 14, + Error_InvalidBpb = 15, + Error_AccessOutOfVolume = 16, + Error_DriverError = 17, + Error_InvalidVolumeLabel = 18, + Error_FileOpened = 19, + Error_NotADirectory = 20, + Error_TooManyFilesOpenedS = 21, + Error_TooManyFilesOpenedU = 22, + Error_NotAFile = 23, + Error_ReadOnly = 24, + Error_LockError = 25, + Error_InternalError = 26, + Error_EndOfFile = 27, + Error_AccessNotAllowed = 28, + Error_DirectoryNotEmpty = 29, + Error_NotEnoughCachePages = 30, + Error_DifferentDrive = 31, + Error_DifferentEntry = 32, + Error_InvalidEntry = 33, + Error_InvalidSector = 34, + Error_BrokenVolume = 35, + Error_NotEffective = 36, + Error_FileSizeOver = 37, + Error_InvalidFileDiscriptor = 38, + Error_InvalidLockFile = 39, + Error_ExtensionNotRegistered = 40, + Error_ExtensionError = 41, }; constexpr inline const int ReturnValueNoError = 0; @@ -68,4 +81,44 @@ namespace ams::prfile2::pf { } } + struct CachePage { + u16 status; + u16 option; + u8 *buffer_head; + u8 *buffer_cur; + u8 *buffer_dirty_start; + u8 *buffer_dirty_end; + u32 size; + u32 sector; + void *signature; + CachePage *next; + CachePage *prev; + }; + static_assert(util::is_pod::value); + + constexpr inline auto SectorBufferSize = 0x200; + constexpr inline auto Log2SectorBufferSize = 9; + static_assert((1u << Log2SectorBufferSize) == SectorBufferSize); + + using SectorBuffer = u8[SectorBufferSize]; + static_assert(sizeof(SectorBuffer) == SectorBufferSize); + + struct CacheSetting { + CachePage *pages; + SectorBuffer *buffers; + u16 num_fat_pages; + u16 num_data_pages; + u32 fat_buf_size; + u32 data_buf_size; + }; + + struct DriveTable { + CacheSetting *cache; + HandleType partition_handle; + DriveCharacter drive_char; + u8 status; + }; + + using TailBuf = u32; + } diff --git a/libraries/libvapours/include/vapours/prfile2/prfile2_common.hpp b/libraries/libvapours/include/vapours/prfile2/prfile2_common.hpp index c9a1bf9a6..7aec135ff 100644 --- a/libraries/libvapours/include/vapours/prfile2/prfile2_common.hpp +++ b/libraries/libvapours/include/vapours/prfile2/prfile2_common.hpp @@ -25,53 +25,43 @@ namespace ams::prfile2 { constexpr inline const auto MaximumOpenDirectoryCountSystem = pf::MaximumDirectoryCount; constexpr inline const auto MaximumOpenDirectoryCountUser = pf::MaximumDirectoryCount; - enum InternalError { - InternalError_NoError = 0, - InternalError_Ok = InternalError_NoError, + enum Error { + Error_NoError = 0, + Error_Ok = Error_NoError, - InternalError_Generic = -1, + Error_EPERM = 1, + Error_ENOENT = 2, + Error_ESRCH = 3, + Error_EIO = 5, + Error_ENOEXEC = 8, + Error_EBADF = 9, + Error_ENOMEM = 12, + Error_EACCES = 13, + Error_EBUSY = 16, + Error_EEXIST = 17, + Error_ENODEV = 19, + Error_EISDIR = 21, + Error_EINVAL = 22, + Error_ENFILE = 23, + Error_EMFILE = 24, + Error_EFBIG = 27, + Error_ENOSPC = 28, + Error_ENOLCK = 46, + Error_ENOSYS = 88, + Error_ENOTEMPTY = 90, - InternalError_InvalidFileName = 1, - InternalError_InvalidPathName = 2, - InternalError_FileNotFound = 3, - InternalError_TooManyVolumesAttached = 4, - InternalError_DirectoryFull = 5, - InternalError_VolumeFull = 6, - InternalError_InvalidDiskFormat = 7, - InternalError_FileAlreadyExists = 8, - InternalError_VolumeNotMounted = 9, - InternalError_InvalidParameter = 10, - InternalError_WriteProtected = 11, - InternalError_UnsupportedFormat = 12, - InternalError_BrokenClusterChain = 13, - InternalError_InvalidClusterNum = 14, - InternalError_InvalidBpb = 15, - InternalError_AccessOutOfVolume = 16, - InternalError_DriverError = 17, - InternalError_InvalidVolumeLabel = 18, - InternalError_FileOpened = 19, - InternalError_NotADirectory = 20, - InternalError_TooManyFilesOpenedS = 21, - InternalError_TooManyFilesOpenedU = 22, - InternalError_NotAFile = 23, - InternalError_ReadOnly = 24, - InternalError_LockError = 25, - InternalError_InternalError = 26, - InternalError_EndOfFile = 27, - InternalError_AccessNotAllowed = 28, - InternalError_DirectoryNotEmpty = 29, - InternalError_NotEnoughCachePages = 30, - InternalError_DifferentDrive = 31, - InternalError_DifferentEntry = 32, - InternalError_InvalidEntry = 33, - InternalError_InvalidSector = 34, - InternalError_BrokenVolume = 35, - InternalError_NotEffective = 36, - InternalError_FileSizeOver = 37, - InternalError_InvalidFileDiscriptor = 38, - InternalError_InvalidLockFile = 39, - InternalError_ExtensionNotRegistered = 40, - InternalError_ExtensionError = 41, + Error_EMOD_NOTREG = 100, + Error_EMOD_NOTSPRT = 101, + Error_EMOD_FCS = 102, + Error_EMOD_SAFE = 103, + + Error_ENOMEDIUM = 123, + + Error_EEXFAT_NOTSPRT = 200, + + Error_DFNC = 0x1000, + + Error_SYSTEM = -1, }; constexpr inline const u32 InvalidSector = std::numeric_limits::max(); diff --git a/libraries/libvapours/include/vapours/prfile2/prfile2_entry.hpp b/libraries/libvapours/include/vapours/prfile2/prfile2_entry.hpp new file mode 100644 index 000000000..f40c3d42e --- /dev/null +++ b/libraries/libvapours/include/vapours/prfile2/prfile2_entry.hpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::prfile2 { + + constexpr inline const auto LongNamePathCharacters = 260; + + struct Volume; + + struct DirectoryEntry { + u16 long_name[LongNamePathCharacters]; + u32 attr; + u64 file_size; + u8 create_time_ms; + u16 create_time; + u16 create_date; + u8 create_flags; + u8 access_time_ms; + u16 access_time; + u16 access_date; + u8 access_flags; + u8 modify_time_ms; + u16 modify_time; + u16 modify_date; + u8 modify_flags; + Volume *volume; + u32 path_len; + u32 start_cluster; + u32 entry_sector; + u16 entry_offset; + /* ... */ + u8 num_entry_lfns; + u8 ordinal; + u8 check_sum; + char short_name[13]; + u8 small_letter_flag; + /* ... */ + u16 full_path[LongNamePathCharacters]; + }; + + struct DirectoryTail { + u32 tracker_size; + pf::TailBuf tracker_buf[1]; + u32 *tracker_bits; + }; + +} diff --git a/libraries/libvapours/include/vapours/prfile2/prfile2_fat.hpp b/libraries/libvapours/include/vapours/prfile2/prfile2_fat.hpp new file mode 100644 index 000000000..c4050495d --- /dev/null +++ b/libraries/libvapours/include/vapours/prfile2/prfile2_fat.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::prfile2 { + + struct FatHint { + u32 chain_index; + u32 cluster; + }; + + using LastAccess = FatHint; + + struct LastCluster { + u32 num_last_cluster; + u32 max_chain_index; + }; + + struct ClusterLinkForVolume { + u16 flag; + u16 interval; + u32 *buffer; + u32 link_max; + }; + + using ClusterBuf = u32; + + struct ClusterLink { + u64 position; + ClusterBuf *buffer; + u16 interval; + u16 interval_offset; + u32 save_index; + u32 max_count; + }; + + struct Volume; + + struct FatFileDescriptor { + u32 start_cluster; + u32 *p_start_cluster; + LastCluster last_cluster; + LastAccess last_access; + ClusterLink cluster_link; + FatHint *hint; + Volume *volume; + /* ... */ + }; + +} diff --git a/libraries/libvapours/include/vapours/prfile2/prfile2_fatfs.hpp b/libraries/libvapours/include/vapours/prfile2/prfile2_fatfs.hpp new file mode 100644 index 000000000..9b1576343 --- /dev/null +++ b/libraries/libvapours/include/vapours/prfile2/prfile2_fatfs.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::prfile2::fatfs { + + pf::Error Initialize(u32 config, void *param); + +} diff --git a/libraries/libvapours/include/vapours/prfile2/prfile2_handle.hpp b/libraries/libvapours/include/vapours/prfile2/prfile2_handle.hpp index f2c67fba4..2b04c3873 100644 --- a/libraries/libvapours/include/vapours/prfile2/prfile2_handle.hpp +++ b/libraries/libvapours/include/vapours/prfile2/prfile2_handle.hpp @@ -14,7 +14,7 @@ * along with this program. If not, see . */ #pragma once -#include +#include namespace ams::prfile2 { diff --git a/libraries/libvapours/include/vapours/prfile2/prfile2_system.hpp b/libraries/libvapours/include/vapours/prfile2/prfile2_system.hpp index 3f4798097..6203b7eb3 100644 --- a/libraries/libvapours/include/vapours/prfile2/prfile2_system.hpp +++ b/libraries/libvapours/include/vapours/prfile2/prfile2_system.hpp @@ -20,6 +20,6 @@ namespace ams::prfile2::system { void Initialize(); - pf::Error GetCurrentContextId(u64 *out); + int GetCurrentContextId(u64 *out); } diff --git a/libraries/libvapours/include/vapours/prfile2/prfile2_volume.hpp b/libraries/libvapours/include/vapours/prfile2/prfile2_volume.hpp new file mode 100644 index 000000000..fe06f6e9f --- /dev/null +++ b/libraries/libvapours/include/vapours/prfile2/prfile2_volume.hpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::prfile2 { + + constexpr inline const auto MaxVolumes = 5; + + struct Cursor { + u64 position; + u32 sector; + u32 file_sector_index; + u16 offset_in_sector; + }; + + struct DirectoryCursor { + u32 physical_entry_index; + u32 logical_entry_index; + u32 logical_seek_index; + }; + + struct File; + + struct FileLock { + u16 mode; + u16 count; + u16 waiting_count; + File *owner; + u32 resource; + }; + + struct SystemFileDescriptor { + u32 status; + FatFileDescriptor ffd; + DirectoryEntry dir_entry; + FileLock lock; + u16 num_handlers; + SystemFileDescriptor *next_sfd; + }; + + struct SystemDirectoryDescriptor { + u32 status; + u16 num_handlers; + FatFileDescriptor ffd; + DirectoryEntry dir_entry; + /* ... */ + u64 context_id; + /* ... */ + }; + + struct File { + u32 status; + /* TODO OpenMode */ u32 open_mode; + SystemFileDescriptor *sfd; + FatHint hint; + LastAccess last_access; + pf::Error last_error; + Cursor cursor; + u16 lock_count; + }; + + struct Directory { + u32 stat; + SystemDirectoryDescriptor *sdd; + FatHint hint; + DirectoryCursor cursor; + /* ... */ + }; + + struct BiosParameterBlock { + u16 bytes_per_sector; + u32 num_reserved_sectors; + u16 num_root_dir_entries; + u32 sectors_per_cluster; + u8 num_fats; + u32 total_sectors; + u32 sectors_per_fat; + u32 root_dir_cluster; + u16 fs_info_sector; + u16 backup_boot_sector; + u16 ext_flags; + u16 ext_flags_; + u8 media; + FatFormat fat_fmt; + u8 log2_bytes_per_sector; + u8 log2_sectors_per_cluster; + u8 num_active_fats; + u8 num_active_fats_; + u16 num_root_dir_sectors; + u32 active_fat_sector; + u32 active_fat_sector_; + u32 first_root_dir_sector; + u32 first_data_sector; + u32 num_clusters; + /* ... */ + }; + + using VolumeCallback = void (*)(); + + struct VolumeContext { + u64 context_id; + u32 volume_id; + DirectoryEntry dir_entries[MaxVolumes]; + pf::Error last_error; + pf::Error last_driver_error[MaxVolumes]; + pf::Error last_unk_error[MaxVolumes]; + /* ... */ + VolumeContext *next_used_context; + union { + VolumeContext *prev_used_context; + VolumeContext *next_free_context; + }; + }; + + struct Volume { + BiosParameterBlock bpb; + u32 num_free_clusters; + u32 num_free_clusters_; + u32 last_free_cluster; + u32 last_free_cluster_; + SystemFileDescriptor sfds[MaximumOpenFileCountSystem]; + File ufds[MaximumOpenFileCountUser]; + SystemDirectoryDescriptor sdds[MaximumOpenDirectoryCountSystem]; + Directory udds[MaximumOpenDirectoryCountUser]; + u32 num_open_files; + u32 num_open_directories; + /* ... */ + /* TODO: SectorCache cache; */ + VolumeContext *context; + u64 context_id; + DirectoryTail tail_entry; + u32 volume_config; + u32 file_config; + u32 flags; + pf::DriveCharacter drive_char; + CriticalSection critical_section; + u16 fsi_flag; + ClusterLinkForVolume cluster_link; + pf::Error last_error; + pf::Error last_driver_error; + /* ... */ + void *partition; + VolumeCallback callback; + const u8 *format_param; + /* ... */ + /* TODO: ExtensionTable extension_table; */ + /* TODO: ExtensionData ext; */ + /* ... */ + }; + + struct VolumeSet { + bool initialized; + u32 num_attached_drives; + u32 num_mounted_volumes; + u32 config; + void *param; + /* TODO: CodeSet codeset; */ + u32 setting; + CriticalSection critical_section; + VolumeContext default_context; + VolumeContext contexts[MaxVolumes]; + VolumeContext *used_context_head; + VolumeContext *used_context_tail; + VolumeContext *free_context_head; + Volume volumes[MaxVolumes]; + }; + +} + +namespace ams::prfile2::vol { + + pf::Error Initialize(u32 config, void *param); + + pf::Error Attach(pf::DriveTable *drive_table); + + VolumeContext *RegisterContext(u64 *out_context_id); + pf::Error UnregisterContext(); + +} diff --git a/libraries/libvapours/source/prfile2/pdm/prfile2_pdm_api.cpp b/libraries/libvapours/source/prfile2/pdm/prfile2_pdm_api.cpp index fc3859086..5c01aec9d 100644 --- a/libraries/libvapours/source/prfile2/pdm/prfile2_pdm_api.cpp +++ b/libraries/libvapours/source/prfile2/pdm/prfile2_pdm_api.cpp @@ -64,4 +64,27 @@ namespace ams::prfile2::pdm { return disk::CloseDisk(handle); } + pdm::Error OpenPartition(HandleType disk_handle, u16 part_id, HandleType *out) { + /* Check the arguments. */ + if (out == nullptr || IsInvalidHandle(disk_handle)) { + return pdm::Error_InvalidParameter; + } + + /* Set the output as invalid. */ + *out = InvalidHandle; + + /* Open the partition. */ + return part::OpenPartition(disk_handle, part_id, out); + } + + pdm::Error ClosePartition(HandleType handle) { + /* Check the input. */ + if (IsInvalidHandle(handle)) { + return pdm::Error_InvalidParameter; + } + + /* Close the partition. */ + return part::ClosePartition(handle); + } + } diff --git a/libraries/libvapours/source/prfile2/pf/prfile2_pf_api.cpp b/libraries/libvapours/source/prfile2/pf/prfile2_pf_api.cpp new file mode 100644 index 000000000..0b42906ca --- /dev/null +++ b/libraries/libvapours/source/prfile2/pf/prfile2_pf_api.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "prfile2_pf_errnum.hpp" + +namespace ams::prfile2::pf { + + + int Initialize(u32 config, void *param) { + /* Initialize the fatfs api. */ + if (auto err = fatfs::Initialize(config, param)) { + return ConvertReturnValue(err); + } + + /* Initialize the system api. */ + system::Initialize(); + return ConvertReturnValue(pf::Error_Ok); + } + + int Attach(DriveTable **drive_tables) { + /* Check parameters. */ + if (drive_tables == nullptr || *drive_tables == nullptr) { + return ConvertReturnValue(SetInternalErrorAndReturn(pf::Error_InvalidParameter)); + } + + /* Attach each volume in the list. */ + for (auto *table = *drive_tables; table != nullptr; table = *(++drive_tables)) { + if (auto err = vol::Attach(table); err != pf::Error_Ok) { + /* Clear each unattached drive character. */ + for (table = *drive_tables; table != nullptr; table = *(++drive_tables)) { + table->drive_char = 0; + } + return ConvertReturnValue(err); + } + } + + return ConvertReturnValue(pf::Error_Ok); + } + + +} diff --git a/libraries/libvapours/source/prfile2/pf/prfile2_pf_errnum.hpp b/libraries/libvapours/source/prfile2/pf/prfile2_pf_errnum.hpp new file mode 100644 index 000000000..a65d38cfa --- /dev/null +++ b/libraries/libvapours/source/prfile2/pf/prfile2_pf_errnum.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::prfile2::pf { + + void SetInternalError(pf::Error err); + + ALWAYS_INLINE pf::Error SetInternalErrorAndReturn(pf::Error err) { + SetInternalError(err); + return err; + } + +} diff --git a/libraries/libvapours/source/prfile2/prfile2_critical_section.cpp b/libraries/libvapours/source/prfile2/prfile2_critical_section.cpp index 18eeb9d58..e58b5e030 100644 --- a/libraries/libvapours/source/prfile2/prfile2_critical_section.cpp +++ b/libraries/libvapours/source/prfile2/prfile2_critical_section.cpp @@ -34,7 +34,7 @@ namespace ams::prfile2 { namespace { - constexpr inline const auto NumCriticalSectionResources = 5 /* TODO: NumVolumes */; + constexpr inline const auto NumCriticalSectionResources = MaxVolumes + 1; constinit os::SdkMutex g_crit_resource_mutex; constinit CriticalSection::Resource g_crit_resources[NumCriticalSectionResources]; diff --git a/libraries/libvapours/source/prfile2/prfile2_fatfs.cpp b/libraries/libvapours/source/prfile2/prfile2_fatfs.cpp new file mode 100644 index 000000000..39dc618d3 --- /dev/null +++ b/libraries/libvapours/source/prfile2/prfile2_fatfs.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif + +namespace ams::prfile2::fatfs { + + pf::Error Initialize(u32 config, void *param) { + return vol::Initialize(config, param); + } + +} diff --git a/libraries/libvapours/source/prfile2/prfile2_system.cpp b/libraries/libvapours/source/prfile2/prfile2_system.cpp index 26cfffa13..02fec8617 100644 --- a/libraries/libvapours/source/prfile2/prfile2_system.cpp +++ b/libraries/libvapours/source/prfile2/prfile2_system.cpp @@ -29,10 +29,10 @@ namespace ams::prfile2::system { /* ... */ } - pf::Error GetCurrentContextId(u64 *out) { + int GetCurrentContextId(u64 *out) { /* Check that out isn't null. */ if (out == nullptr) { - return static_cast(-2); + return -2; } /* Set the output. */ @@ -42,7 +42,7 @@ namespace ams::prfile2::system { *out = 0; #endif - return pf::Error_Ok; + return 0; } } diff --git a/libraries/libvapours/source/prfile2/prfile2_volume.cpp b/libraries/libvapours/source/prfile2/prfile2_volume.cpp new file mode 100644 index 000000000..88c8b96c9 --- /dev/null +++ b/libraries/libvapours/source/prfile2/prfile2_volume.cpp @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "prfile2_volume_set.hpp" + +namespace ams::prfile2::vol { + + /* Global volume context object. */ + constinit VolumeContext g_vol_set; + + namespace { + + constexpr inline u32 CharacterCheckDisable = 0x10000; + constexpr inline u32 CharacterCheckEnable = 0x20000; + + constexpr inline u32 CharacterCheckMask = CharacterCheckDisable | CharacterCheckEnable; + + constexpr inline u32 VolumeSetConfigMask = 0x5FFFFFFF; + + VolumeContext *GetVolumeContextById(u64 context_id) { + /* Get the volume set. */ + auto &vol_set = GetVolumeSet(); + + /* Acquire exclusive access to the volume set. */ + ScopedCriticalSection lk(vol_set.critical_section); + + /* Find a matching context. */ + for (auto *ctx = vol_set.used_context_head; ctx != nullptr; ctx = ctx->next_used_context) { + if (ctx->context_id == context_id) { + return ctx; + } + } + + return nullptr; + } + + } + + pf::Error Initialize(u32 config, void *param) { + /* Check the input config. */ + if ((config & ~CharacterCheckMask) != 0) { + return pf::Error_InvalidParameter; + } + if ((config & CharacterCheckMask) == CharacterCheckMask) { + return pf::Error_InvalidParameter; + } + + /* Get the volume set. */ + auto &vol_set = GetVolumeSet(); + + /* Clear the default volume context. */ + std::memset(std::addressof(vol_set.default_context), 0, sizeof(VolumeContext)); + vol_set.default_context.volume_id = 0; + + /* Setup the context lists. */ + vol_set.used_context_head = nullptr; + vol_set.used_context_tail = nullptr; + vol_set.free_context_head = vol_set.contexts; + for (auto i = 0; i < MaxVolumes - 1; ++i) { + vol_set.contexts[i].next_free_context = std::addressof(vol_set.contexts[i + 1]); + } + vol_set.contexts[MaxVolumes - 1].next_free_context = nullptr; + + /* Set the setting. */ + vol_set.setting = 1; + + /* Set the config. */ + if ((config & CharacterCheckEnable) != 0) { + vol_set.config |= CharacterCheckDisable; + } else { + vol_set.config &= ~CharacterCheckDisable; + } + vol_set.config &= VolumeSetConfigMask; + + /* Clear number of attached drives/volumes. */ + vol_set.num_attached_drives = 0; + vol_set.num_mounted_volumes = 0; + + /* Set the parameter. */ + vol_set.param = param; + + /* Set the codeset. */ + /* TODO */ + + /* Clear the volumes. */ + for (auto &volume : vol_set.volumes) { + std::memset(std::addressof(volume), 0, sizeof(volume)); + } + + /* Initialize the volume set critical section. */ + InitializeCriticalSection(std::addressof(vol_set.critical_section)); + + /* NOTE: Here "InitLockFile()" is called, but this doesn't seem used so far. TODO: Add if used? */ + + /* Mark initialized. */ + vol_set.initialized = true; + + return pf::Error_Ok; + } + + pf::Error Attach(pf::DriveTable *drive_table) { + AMS_UNUSED(drive_table); + AMS_ABORT("vol::Attach"); + } + + VolumeContext *RegisterContext(u64 *out_context_id) { + /* Get the current context id. */ + u64 context_id = 0; + system::GetCurrentContextId(std::addressof(context_id)); + if (out_context_id != nullptr) { + *out_context_id = context_id; + } + + /* Get the volume set. */ + auto &vol_set = GetVolumeSet(); + + /* Acquire exclusive access to the volume set. */ + ScopedCriticalSection lk(vol_set.critical_section); + + /* Get the volume context by ID. If we already have a context, return it. */ + if (VolumeContext *match = GetVolumeContextById(context_id); match != nullptr) { + return match; + } + + /* Try to find a free context in the list. */ + VolumeContext *ctx = vol_set.free_context_head; + if (ctx == nullptr) { + vol_set.default_context.last_error = pf::Error_InternalError; + return nullptr; + } + + /* Update the free lists. */ + vol_set.free_context_head = ctx->next_free_context; + if (VolumeContext *next = vol_set.used_context_head; next != nullptr) { + next->prev_used_context = ctx; + ctx->next_used_context = next; + ctx->prev_used_context = nullptr; + vol_set.used_context_head = ctx; + } else { + ctx->next_used_context = nullptr; + ctx->prev_used_context = nullptr; + vol_set.used_context_head = ctx; + vol_set.used_context_tail = ctx; + } + + /* Set the context's fields. */ + ctx->context_id = context_id; + ctx->last_error = pf::Error_Ok; + for (auto i = 0; i < MaxVolumes; ++i) { + ctx->last_driver_error[i] = pf::Error_Ok; + ctx->last_unk_error[i] = pf::Error_Ok; + } + + /* Copy from the default context. */ + const auto volume_id = vol_set.default_context.volume_id; + ctx->volume_id = volume_id; + ctx->dir_entries[volume_id] = vol_set.default_context.dir_entries[volume_id]; + + return ctx; + } + + pf::Error UnregisterContext() { + /* Get the current context id. */ + u64 context_id = 0; + system::GetCurrentContextId(std::addressof(context_id)); + + /* Get the volume set. */ + auto &vol_set = GetVolumeSet(); + + /* Acquire exclusive access to the volume set. */ + ScopedCriticalSection lk(vol_set.critical_section); + + /* Get the volume context by ID. */ + VolumeContext *ctx = GetVolumeContextById(context_id); + if (ctx == nullptr) { + vol_set.default_context.last_error = pf::Error_InternalError; + return pf::Error_InternalError; + } + + /* Update the lists. */ + auto *prev_used = ctx->prev_used_context; + auto *next_used = ctx->next_used_context; + if (prev_used != nullptr) { + if (next_used != nullptr) { + prev_used->next_used_context = next_used; + next_used->prev_used_context = prev_used; + } else { + prev_used->next_used_context = nullptr; + vol_set.used_context_tail = prev_used; + } + } else if (next_used != nullptr) { + next_used->prev_used_context = nullptr; + vol_set.used_context_head = next_used; + } else { + vol_set.used_context_head = nullptr; + vol_set.used_context_tail = nullptr; + } + + ctx->next_used_context = nullptr; + ctx->next_free_context = vol_set.free_context_head; + vol_set.free_context_head = ctx; + + return pf::Error_Ok; + } + +} diff --git a/libraries/libvapours/source/prfile2/prfile2_volume_set.hpp b/libraries/libvapours/source/prfile2/prfile2_volume_set.hpp new file mode 100644 index 000000000..a3531add1 --- /dev/null +++ b/libraries/libvapours/source/prfile2/prfile2_volume_set.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::prfile2 { + + namespace impl { + + extern VolumeSet g_vol_set; + + } + + ALWAYS_INLINE VolumeSet &GetVolumeSet() { + return impl::g_vol_set; + } + +}