pf2: add volume init, context register/unregister

This commit is contained in:
Michael Scire
2020-11-26 03:27:07 -08:00
parent f9c5470ac9
commit 7b01d59b3b
19 changed files with 883 additions and 85 deletions

View File

@@ -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);
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#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);
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::prfile2::pf {
void SetInternalError(pf::Error err);
ALWAYS_INLINE pf::Error SetInternalErrorAndReturn(pf::Error err) {
SetInternalError(err);
return err;
}
}

View File

@@ -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];

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
namespace ams::prfile2::fatfs {
pf::Error Initialize(u32 config, void *param) {
return vol::Initialize(config, param);
}
}

View File

@@ -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<pf::Error>(-2);
return -2;
}
/* Set the output. */
@@ -42,7 +42,7 @@ namespace ams::prfile2::system {
*out = 0;
#endif
return pf::Error_Ok;
return 0;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#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;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::prfile2 {
namespace impl {
extern VolumeSet g_vol_set;
}
ALWAYS_INLINE VolumeSet &GetVolumeSet() {
return impl::g_vol_set;
}
}