Compare commits
12 Commits
uart_mitm
...
experiment
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3107ec0127 | ||
|
|
60ea1dade2 | ||
|
|
a2f611edb7 | ||
|
|
8719e6da02 | ||
|
|
102e1e0e74 | ||
|
|
ceef76c428 | ||
|
|
7b01d59b3b | ||
|
|
f9c5470ac9 | ||
|
|
75f006b002 | ||
|
|
4466a74e40 | ||
|
|
dd6c9e1de1 | ||
|
|
7418c80e7f |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -95,5 +95,3 @@ sept/sept-secondary/KEYS.py
|
||||
**/build_nintendo_nx_arm
|
||||
**/build_nintendo_nx_x64
|
||||
**/build_nintendo_nx_x86
|
||||
|
||||
stratosphere/test/
|
||||
|
||||
1
Makefile
1
Makefile
@@ -135,7 +135,6 @@ dist: dist-no-debug
|
||||
cp exosphere/program/sc7fw/sc7fw.elf atmosphere-$(AMSVER)-debug/exosphere-sc7fw.elf
|
||||
cp exosphere/program/rebootstub/rebootstub.elf atmosphere-$(AMSVER)-debug/exosphere-rebootstub.elf
|
||||
cp mesosphere/kernel_ldr/kernel_ldr.elf atmosphere-$(AMSVER)-debug/kernel_ldr.elf
|
||||
cp mesosphere/kernel/kernel.elf atmosphere-$(AMSVER)-debug/kernel.elf
|
||||
cp stratosphere/ams_mitm/ams_mitm.elf atmosphere-$(AMSVER)-debug/ams_mitm.elf
|
||||
cp stratosphere/boot/boot.elf atmosphere-$(AMSVER)-debug/boot.elf
|
||||
cp stratosphere/boot2/boot2.elf atmosphere-$(AMSVER)-debug/boot2.elf
|
||||
|
||||
@@ -52,11 +52,6 @@
|
||||
; Controls whether dns.mitm logs to the sd card for debugging
|
||||
; 0 = Disabled, 1 = Enabled
|
||||
; enable_dns_mitm_debug_log = u8!0x0
|
||||
; Controls whether to enable uart mitm
|
||||
; for logging bluetooth HCI to btsnoop captures.
|
||||
; This is only implemented for [7.0.0+].
|
||||
; 0 = Do not enable, 1 = Enable.
|
||||
; enable_uart_mitm = u8!0x0
|
||||
[hbloader]
|
||||
; Controls the size of the homebrew heap when running as applet.
|
||||
; If set to zero, all available applet memory is used as heap.
|
||||
|
||||
@@ -1,15 +1,4 @@
|
||||
# Changelog
|
||||
## 0.18.1
|
||||
+ A number of minor issues were fixed, including:
|
||||
+ The new `dns.mitm` module added in 0.18.0 no longer fatal errors when receiving port=nullptr.
|
||||
+ This fixes youtube ad-blocking, and possibly other usecases.
|
||||
+ A bug was fixed that caused ams.mitm to incorrectly cache data storages.
|
||||
+ This potentially broke DLC when using romfs mods, and could have caused other issues (e.g. with custom themes, and maybe other cases).
|
||||
+ A bug was fixed in power state control module registration.
|
||||
+ This might fix a weird edge case with system module dependencies on sleep/wake, but probably nobody should notice any differences.
|
||||
+ A bug was fixed where mesosphere sometimes treated virtual core IDs as though they were physical core IDs.
|
||||
+ This had zero impact, because for Switch virtual core == physical core, but it could have affected future platforms if it had remained unresolved.
|
||||
+ Several issues were fixed, and usability and stability were improved.
|
||||
## 0.18.0
|
||||
+ A new mitm module was added (`dns.mitm`).
|
||||
+ This provides a highly configurable mechanism for redirecting DNS resolution requests.
|
||||
|
||||
@@ -25,6 +25,12 @@ set_mitm enables intercepting requests to the system settings service. It curren
|
||||
+ `ns` system module and games (to allow for overriding game locales)
|
||||
+ All firmware debug settings requests (to allow modification of system settings not directly exposed to the user)
|
||||
|
||||
## dns_mitm
|
||||
|
||||
dns_mitm enables intercepting requests to dns resolution services, to enable redirecting requests for specified hostnames.
|
||||
|
||||
For documentation, see [here](../../features/dns_mitm.md).
|
||||
|
||||
### Firmware Version
|
||||
set_mitm intercepts the `GetFirmwareVersion` command, if the requester is `qlaunch` or `maintenance`.
|
||||
It modifies the `display_version` field of the returned system version, causing the version to display
|
||||
@@ -33,19 +39,3 @@ in settings as `#.#.#|AMS #.#.#|?` with `? = S` when running under system eMMC o
|
||||
### System Settings
|
||||
set_mitm intercepts the `GetSettingsItemValueSize` and `GetSettingsItemValue` commands for all requesters.
|
||||
It does so in order to enable user configuration of system settings, which are parsed from `/atmosphere/system_settings.ini` on boot. See [here](../../features/configurations.md) for more information on the system settings format.
|
||||
|
||||
## dns_mitm
|
||||
dns_mitm enables intercepting requests to dns resolution services, to enable redirecting requests for specified hostnames.
|
||||
|
||||
For documentation, see [here](../../features/dns_mitm.md).
|
||||
|
||||
## uart_mitm
|
||||
`uart_mitm` intercepts the uart service used by bluetooth, on 7.0.0+ when enabled by [system_settings.ini](../../features/configurations.md). This allows logging bluetooth traffic.
|
||||
|
||||
Usage of bluetooth devices will be less responsive when this is enabled.
|
||||
|
||||
Logs are written to directory `/atmosphere/uart_logs/{PosixTime}_{TickTimestamp}_{ProgramId}`, which then contains the following:
|
||||
+ `cmd_log` Text log for uart IPortSession commands, and any warning messages.
|
||||
+ `btsnoop_hci.log` Bluetooth HCI log in the btsnoop format. This file is not accessible while it's the current log being used with HOS running.
|
||||
|
||||
4 directories are created for each system-boot. btsnoop logging is disabled for the first 3, with only the 4th enabled (enabled when a certain HCI vendor command is detected).
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
commit = bc08912dd31bb172467add8e24b4f0adac431939
|
||||
parent = 71add1add8521e0c2115ec612c514400ac7ba688
|
||||
commit = 17960517bad5d2d07effb28b744ac8d907d571e0
|
||||
parent = ee2e9d50fd93721b365daf0eae1eef17c8ba62e8
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
|
||||
@@ -45,21 +45,7 @@
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
namespace cpu {
|
||||
|
||||
static constexpr inline size_t NumVirtualCores = BITSIZEOF(u64);
|
||||
|
||||
static constexpr inline u64 VirtualCoreMask = [] {
|
||||
u64 mask = 0;
|
||||
for (size_t i = 0; i < NumVirtualCores; ++i) {
|
||||
mask |= (UINT64_C(1) << i);
|
||||
}
|
||||
return mask;
|
||||
}();
|
||||
|
||||
}
|
||||
|
||||
static_assert(cpu::NumCores <= cpu::NumVirtualCores);
|
||||
static_assert(util::size(cpu::VirtualToPhysicalCoreMap) == cpu::NumVirtualCores);
|
||||
static_assert(cpu::NumCores <= static_cast<s32>(BITSIZEOF(u64)));
|
||||
static_assert(util::size(cpu::VirtualToPhysicalCoreMap) == BITSIZEOF(u64));
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace ams::kern {
|
||||
/* Most fields have already been cleared by our constructor. */
|
||||
|
||||
/* Initial processes may run on all cores. */
|
||||
m_core_mask = cpu::VirtualCoreMask;
|
||||
m_core_mask = (1ul << cpu::NumCores) - 1;
|
||||
|
||||
/* Initial processes may use any user priority they like. */
|
||||
m_priority_mask = ~0xFul;
|
||||
@@ -55,17 +55,18 @@ namespace ams::kern {
|
||||
const auto max_prio = cap.Get<CorePriority::LowestThreadPriority>();
|
||||
const auto min_prio = cap.Get<CorePriority::HighestThreadPriority>();
|
||||
|
||||
R_UNLESS(min_core <= max_core, svc::ResultInvalidCombination());
|
||||
R_UNLESS(min_prio <= max_prio, svc::ResultInvalidCombination());
|
||||
R_UNLESS(max_core < cpu::NumVirtualCores, svc::ResultInvalidCoreId());
|
||||
R_UNLESS(min_core <= max_core, svc::ResultInvalidCombination());
|
||||
R_UNLESS(min_prio <= max_prio, svc::ResultInvalidCombination());
|
||||
R_UNLESS(max_core < cpu::NumCores, svc::ResultInvalidCoreId());
|
||||
|
||||
MESOSPHERE_ASSERT(max_core < BITSIZEOF(u64));
|
||||
MESOSPHERE_ASSERT(max_prio < BITSIZEOF(u64));
|
||||
|
||||
/* Set core mask. */
|
||||
for (auto core_id = min_core; core_id <= max_core; core_id++) {
|
||||
m_core_mask |= (1ul << core_id);
|
||||
}
|
||||
MESOSPHERE_ASSERT((m_core_mask & cpu::VirtualCoreMask) == m_core_mask);
|
||||
MESOSPHERE_ASSERT((m_core_mask & ((1ul << cpu::NumCores) - 1)) == m_core_mask);
|
||||
|
||||
/* Set priority mask. */
|
||||
for (auto prio = min_prio; prio <= max_prio; prio++) {
|
||||
|
||||
@@ -216,7 +216,7 @@ namespace ams::kern::svc {
|
||||
case ams::svc::InfoType_ThreadTickCount:
|
||||
{
|
||||
/* Verify the requested core is valid. */
|
||||
const bool core_valid = (info_subtype == static_cast<u64>(-1ul)) || (info_subtype < cpu::NumVirtualCores);
|
||||
const bool core_valid = (info_subtype == static_cast<u64>(-1ul)) || (info_subtype < util::size(cpu::VirtualToPhysicalCoreMap));
|
||||
R_UNLESS(core_valid, svc::ResultInvalidCombination());
|
||||
|
||||
/* Get the thread from its handle. */
|
||||
|
||||
@@ -21,8 +21,8 @@ namespace ams::kern::svc {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsValidVirtualCoreId(int32_t core_id) {
|
||||
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumVirtualCores));
|
||||
constexpr bool IsValidCoreId(int32_t core_id) {
|
||||
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumCores));
|
||||
}
|
||||
|
||||
void ExitProcess() {
|
||||
@@ -275,7 +275,7 @@ namespace ams::kern::svc {
|
||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Validate the core id. */
|
||||
R_UNLESS(IsValidVirtualCoreId(core_id), svc::ResultInvalidCoreId());
|
||||
R_UNLESS(IsValidCoreId(core_id), svc::ResultInvalidCoreId());
|
||||
R_UNLESS(((1ul << core_id) & process->GetCoreMask()) != 0, svc::ResultInvalidCoreId());
|
||||
|
||||
/* Validate the priority. */
|
||||
|
||||
@@ -21,8 +21,8 @@ namespace ams::kern::svc {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsValidVirtualCoreId(int32_t core_id) {
|
||||
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumVirtualCores));
|
||||
constexpr bool IsValidCoreId(int32_t core_id) {
|
||||
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumCores));
|
||||
}
|
||||
|
||||
Result CreateThread(ams::svc::Handle *out, ams::svc::ThreadFunc f, uintptr_t arg, uintptr_t stack_bottom, int32_t priority, int32_t core_id) {
|
||||
@@ -33,7 +33,7 @@ namespace ams::kern::svc {
|
||||
}
|
||||
|
||||
/* Validate arguments. */
|
||||
R_UNLESS(IsValidVirtualCoreId(core_id), svc::ResultInvalidCoreId());
|
||||
R_UNLESS(IsValidCoreId(core_id), svc::ResultInvalidCoreId());
|
||||
R_UNLESS(((1ul << core_id) & process.GetCoreMask()) != 0, svc::ResultInvalidCoreId());
|
||||
|
||||
R_UNLESS(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority, svc::ResultInvalidPriority());
|
||||
@@ -168,7 +168,7 @@ namespace ams::kern::svc {
|
||||
R_UNLESS(affinity_mask != 0, svc::ResultInvalidCombination());
|
||||
|
||||
/* Validate the core id. */
|
||||
if (IsValidVirtualCoreId(core_id)) {
|
||||
if (IsValidCoreId(core_id)) {
|
||||
R_UNLESS(((1ul << core_id) & affinity_mask) != 0, svc::ResultInvalidCombination());
|
||||
} else {
|
||||
R_UNLESS(core_id == ams::svc::IdealCoreNoUpdate || core_id == ams::svc::IdealCoreDontCare, svc::ResultInvalidCoreId());
|
||||
|
||||
@@ -79,7 +79,6 @@
|
||||
#include <stratosphere/spl.hpp>
|
||||
#include <stratosphere/time.hpp>
|
||||
#include <stratosphere/updater.hpp>
|
||||
#include <stratosphere/usb.hpp>
|
||||
#include <stratosphere/wec.hpp>
|
||||
|
||||
/* Include FS last. */
|
||||
|
||||
@@ -109,9 +109,6 @@ namespace ams::impl {
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, settings, Main);
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, settings, IpcServer);
|
||||
|
||||
/* Bus. */
|
||||
AMS_DEFINE_SYSTEM_THREAD(-12, uart, IpcServer);
|
||||
|
||||
/* erpt. */
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, erpt, Main);
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, erpt, IpcServer);
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
namespace ams::psc {
|
||||
|
||||
enum PmModuleId : u32 {
|
||||
enum PmModuleId : u16 {
|
||||
PmModuleId_Usb = 4,
|
||||
PmModuleId_Ethernet = 5,
|
||||
PmModuleId_Fgm = 6,
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#include <stratosphere/usb/usb_limits.hpp>
|
||||
#include <stratosphere/usb/usb_types.hpp>
|
||||
#include <stratosphere/usb/usb_device_types.hpp>
|
||||
|
||||
#define AMS_USB_I_DS_ENDPOINT_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, PostBufferAsync, (sf::Out<u32> out_urb_id, u64 address, u32 size), (out_urb_id, address, size)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, Cancel, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, GetCompletionEvent, (sf::OutCopyHandle out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, GetUrbReport, (sf::Out<usb::UrbReport> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, Stall, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, SetZlt, (bool zlt), (zlt))
|
||||
|
||||
/* TODO: Deprecated interface? */
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsEndpoint, AMS_USB_I_DS_ENDPOINT_INTERFACE_INFO)
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#include <stratosphere/usb/usb_limits.hpp>
|
||||
#include <stratosphere/usb/usb_types.hpp>
|
||||
#include <stratosphere/usb/usb_device_types.hpp>
|
||||
#include <stratosphere/usb/ds/usb_i_ds_endpoint.hpp>
|
||||
|
||||
#define AMS_USB_I_DS_INTERFACE_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, RegisterEndpoint, (u8 endpoint_address, sf::Out<sf::SharedPointer<usb::ds::IDsEndpoint>> out), (endpoint_address, out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, GetSetupEvent, (sf::OutCopyHandle out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, GetSetupPacket, (const sf::OutBuffer &out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, CtrlInAsync, (sf::Out<u32> out_urb_id, u64 address, u32 size), (out_urb_id, address, size)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, CtrlOutAsync, (sf::Out<u32> out_urb_id, u64 address, u32 size), (out_urb_id, address, size)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, GetCtrlInCompletionEvent, (sf::OutCopyHandle out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, GetCtrlInUrbReport, (sf::Out<usb::UrbReport> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7, Result, GetCtrlOutCompletionEvent, (sf::OutCopyHandle out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 8, Result, GetCtrlOutUrbReport, (sf::Out<usb::UrbReport> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 9, Result, CtrlStall, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 10, Result, AppendConfigurationData, (u8 bInterfaceNumber, usb::UsbDeviceSpeed device_speed, const sf::InBuffer &data), (bInterfaceNumber, device_speed, data)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 65000, Result, Enable, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 65001, Result, Disable, (), ())
|
||||
|
||||
/* TODO: Deprecated interface? */
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsInterface, AMS_USB_I_DS_INTERFACE_INTERFACE_INFO)
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#include <stratosphere/usb/usb_limits.hpp>
|
||||
#include <stratosphere/usb/usb_types.hpp>
|
||||
#include <stratosphere/usb/usb_device_types.hpp>
|
||||
#include <stratosphere/usb/ds/usb_i_ds_interface.hpp>
|
||||
|
||||
#define AMS_USB_I_DS_SERVICE_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, Bind, (usb::ComplexId complex_id, sf::CopyHandle process_h), (complex_id, process_h)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, RegisterInterface, (sf::Out<sf::SharedPointer<usb::ds::IDsInterface>> out, u8 bInterfaceNumber), (out, bInterfaceNumber)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, GetStateChangeEvent, (sf::OutCopyHandle out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, GetState, (sf::Out<usb::UsbState> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, ClearDeviceData, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, AddUsbStringDescriptor, (sf::Out<u8> out, const sf::InBuffer &desc), (out, desc)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, DeleteUsbStringDescriptor, (u8 index), (index)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7, Result, SetUsbDeviceDescriptor, (const sf::InBuffer &desc, usb::UsbDeviceSpeed speed), (desc, speed)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 8, Result, SetBinaryObjectStore, (const sf::InBuffer &bos), (bos)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 9, Result, Enable, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 10, Result, Disable, (), ())
|
||||
|
||||
/* TODO: Deprecated interface? */
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsService, AMS_USB_I_DS_SERVICE_INTERFACE_INFO)
|
||||
|
||||
#define AMS_USB_I_DS_ROOT_SERVICE_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, GetService, (sf::Out<sf::SharedPointer<usb::ds::IDsService>> out), (out))
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsRootService, AMS_USB_I_DS_ROOT_SERVICE_INTERFACE_INFO)
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#include <stratosphere/os/os_system_event.hpp>
|
||||
#include <stratosphere/sf/sf_lmem_utility.hpp>
|
||||
#include <stratosphere/usb/usb_limits.hpp>
|
||||
#include <stratosphere/usb/usb_device_types.hpp>
|
||||
#include <stratosphere/usb/ds/usb_i_ds_service.hpp>
|
||||
|
||||
namespace ams::usb {
|
||||
|
||||
class DsInterface;
|
||||
class DsEndpoint;
|
||||
|
||||
class DsClient {
|
||||
friend class DsInterface;
|
||||
friend class DsEndpoint;
|
||||
private:
|
||||
/* NOTE: Nintendo uses a UnitHeap here on newer firmware versions. */
|
||||
/* For now, we'll use an ExpHeap and do it the old way. */
|
||||
sf::ExpHeapAllocator m_allocator{};
|
||||
u8 m_heap_buffer[32_KB];
|
||||
lmem::HeapHandle m_heap_handle{};
|
||||
sf::SharedPointer<ds::IDsRootService> m_root_service{};
|
||||
sf::SharedPointer<ds::IDsService> m_ds_service{};
|
||||
bool m_is_initialized{false};
|
||||
std::atomic<int> m_reference_count{0};
|
||||
os::SystemEventType m_state_change_event{};
|
||||
DsInterface *m_interfaces[DsLimitMaxInterfacesPerConfigurationCount]{};
|
||||
bool m_is_enabled{false};
|
||||
public:
|
||||
DsClient() = default;
|
||||
~DsClient() { /* ... */ }
|
||||
public:
|
||||
Result Initialize(ComplexId complex_id);
|
||||
Result Finalize();
|
||||
|
||||
bool IsInitialized();
|
||||
|
||||
Result EnableDevice();
|
||||
Result DisableDevice();
|
||||
|
||||
os::SystemEventType *GetStateChangeEvent();
|
||||
Result GetState(UsbState *out);
|
||||
|
||||
Result ClearDeviceData();
|
||||
|
||||
Result AddUsbStringDescriptor(u8 *out_index, UsbStringDescriptor *desc);
|
||||
Result DeleteUsbStringDescriptor(u8 index);
|
||||
|
||||
Result SetUsbDeviceDescriptor(UsbDeviceDescriptor *desc, UsbDeviceSpeed speed);
|
||||
|
||||
Result SetBinaryObjectStore(u8 *data, int size);
|
||||
private:
|
||||
Result AddInterface(DsInterface *intf, sf::SharedPointer<ds::IDsInterface> *out_srv, uint8_t bInterfaceNumber);
|
||||
Result DeleteInterface(uint8_t bInterfaceNumber);
|
||||
};
|
||||
|
||||
class DsInterface {
|
||||
friend class DsEndpoint;
|
||||
private:
|
||||
DsClient *m_client;
|
||||
sf::SharedPointer<ds::IDsInterface> m_interface;
|
||||
bool m_is_initialized;
|
||||
std::atomic<int> m_reference_count;
|
||||
os::SystemEventType m_setup_event;
|
||||
os::SystemEventType m_ctrl_in_completion_event;
|
||||
os::SystemEventType m_ctrl_out_completion_event;
|
||||
UrbReport m_report;
|
||||
u8 m_interface_num;
|
||||
DsEndpoint *m_endpoints[UsbLimitMaxEndpointsCount];
|
||||
public:
|
||||
DsInterface() : m_client(nullptr), m_is_initialized(false), m_reference_count(0) { /* ... */ }
|
||||
~DsInterface() { /* ... */ }
|
||||
public:
|
||||
Result Initialize(DsClient *client, u8 bInterfaceNumber);
|
||||
Result Finalize();
|
||||
|
||||
Result AppendConfigurationData(UsbDeviceSpeed speed, void *data, u32 size);
|
||||
|
||||
bool IsInitialized();
|
||||
|
||||
os::SystemEventType *GetSetupEvent();
|
||||
Result GetSetupPacket(UsbCtrlRequest *out);
|
||||
|
||||
Result Enable();
|
||||
Result Disable();
|
||||
|
||||
Result CtrlRead(u32 *out_transferred, void *dst, u32 size);
|
||||
Result CtrlWrite(u32 *out_transferred, void *dst, u32 size);
|
||||
Result CtrlDone();
|
||||
Result CtrlStall();
|
||||
private:
|
||||
Result AddEndpoint(DsEndpoint *ep, u8 bEndpointAddress, sf::SharedPointer<ds::IDsEndpoint> *out);
|
||||
Result DeleteEndpoint(u8 bEndpointAddress);
|
||||
|
||||
Result CtrlIn(u32 *out_transferred, void *dst, u32 size);
|
||||
Result CtrlOut(u32 *out_transferred, void *dst, u32 size);
|
||||
};
|
||||
|
||||
class DsEndpoint {
|
||||
private:
|
||||
bool m_is_initialized;
|
||||
bool m_is_new_format;
|
||||
std::atomic<int> m_reference_count;
|
||||
DsInterface *m_interface;
|
||||
sf::SharedPointer<ds::IDsEndpoint> m_endpoint;
|
||||
u8 m_address;
|
||||
os::SystemEventType m_completion_event;
|
||||
os::SystemEventType m_unknown_event;
|
||||
public:
|
||||
DsEndpoint() : m_is_initialized(false), m_is_new_format(false), m_reference_count(0) { /* ... */ }
|
||||
public:
|
||||
Result Initialize(DsInterface *interface, u8 bEndpointAddress);
|
||||
Result Finalize();
|
||||
|
||||
bool IsInitialized();
|
||||
|
||||
Result PostBuffer(u32 *out_transferred, void *buf, u32 size);
|
||||
Result PostBufferAsync(u32 *out_urb_id, void *buf, u32 size);
|
||||
|
||||
os::SystemEventType *GetCompletionEvent();
|
||||
|
||||
Result GetUrbReport(UrbReport *out);
|
||||
|
||||
Result Cancel();
|
||||
|
||||
Result SetZeroLengthTransfer(bool zlt);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#include <stratosphere/usb/usb_limits.hpp>
|
||||
#include <stratosphere/usb/usb_types.hpp>
|
||||
|
||||
namespace ams::usb {
|
||||
|
||||
constexpr inline u8 InterfaceNumberAuto = DsLimitMaxInterfacesPerConfigurationCount;
|
||||
constexpr inline u8 EndpointAddressAutoIn = UsbEndpointAddressMask_DirDevicetoHost;
|
||||
constexpr inline u8 EndpointAddressAutoOut = UsbEndpointAddressMask_DirHostToDevice;
|
||||
|
||||
enum UrbStatus {
|
||||
UrbStatus_Invalid = 0,
|
||||
UrbStatus_Pending = 1,
|
||||
UrbStatus_Running = 2,
|
||||
UrbStatus_Finished = 3,
|
||||
UrbStatus_Cancelled = 4,
|
||||
UrbStatus_Failed = 5,
|
||||
};
|
||||
|
||||
struct UrbReport {
|
||||
struct Report {
|
||||
u32 id;
|
||||
u32 requested_size;
|
||||
u32 transferred_size;
|
||||
UrbStatus status;
|
||||
} reports[DsLimitRingSize];
|
||||
u32 count;
|
||||
};
|
||||
|
||||
enum DsString {
|
||||
DsString_Max = 0x20,
|
||||
};
|
||||
|
||||
struct DsVidPidBcd {
|
||||
uint16_t idVendor;
|
||||
uint16_t idProduct;
|
||||
uint16_t bcdDevice;
|
||||
|
||||
char manufacturer[DsString_Max];
|
||||
char product[DsString_Max];
|
||||
char serial_number[DsString_Max];
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#include <stratosphere/dd/dd_device_address_space_common.hpp>
|
||||
|
||||
namespace ams::usb {
|
||||
|
||||
constexpr inline int HwLimitDmaBufferAlignmentSize = dd::DeviceAddressSpaceMemoryRegionAlignment;
|
||||
constexpr inline int HwLimitDataCacheLineSize = 0x40;
|
||||
constexpr inline int HwLimitMaxPortCount = 0x4;
|
||||
|
||||
constexpr inline int UsbLimitMaxEndpointsCount = 0x20;
|
||||
constexpr inline int UsbLimitMaxEndpointPairCount = 0x10;
|
||||
|
||||
constexpr inline int DsLimitMaxConfigurationsPerDeviceCount = 1;
|
||||
constexpr inline int DsLimitMaxInterfacesPerConfigurationCount = 4;
|
||||
constexpr inline int DsLimitMaxNameSize = 0x40;
|
||||
constexpr inline int DsLimitRingSize = 8;
|
||||
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#include <stratosphere/usb/usb_limits.hpp>
|
||||
|
||||
namespace ams::usb {
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsDmaAligned(u64 address) {
|
||||
return util::IsAligned(address, static_cast<u64>(HwLimitDmaBufferAlignmentSize));
|
||||
}
|
||||
|
||||
enum ComplexId {
|
||||
ComplexId_Tegra21x = 2,
|
||||
};
|
||||
|
||||
enum UsbDescriptorType {
|
||||
UsbDescriptorType_Device = 1,
|
||||
UsbDescriptorType_Config = 2,
|
||||
UsbDescriptorType_String = 3,
|
||||
UsbDescriptorType_Interface = 4,
|
||||
UsbDescriptorType_Endpoint = 5,
|
||||
UsbDescriptorType_DeviceQualifier = 6,
|
||||
UsbDescriptorType_OtherSpeedConfig = 7,
|
||||
UsbDescriptorType_InterfacePower = 8,
|
||||
UsbDescriptorType_Otg = 9,
|
||||
UsbDescriptorType_Debug = 10,
|
||||
UsbDescriptorType_InterfaceAssociation = 11,
|
||||
UsbDescriptorType_Bos = 15,
|
||||
UsbDescriptorType_DeviceCapability = 16,
|
||||
|
||||
UsbDescriptorType_Hid = 33,
|
||||
UsbDescriptorType_Report = 34,
|
||||
UsbDescriptorType_Physical = 35,
|
||||
|
||||
UsbDescriptorType_Hub = 41,
|
||||
|
||||
UsbDescriptorType_EndpointCompanion = 48,
|
||||
UsbDescriptorType_IsocEndpointCompanion = 49,
|
||||
};
|
||||
|
||||
struct UsbDescriptorHeader {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
} PACKED;
|
||||
|
||||
struct UsbInterfaceDescriptor {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bInterfaceNumber;
|
||||
uint8_t bAlternateSetting;
|
||||
uint8_t bNumEndpoints;
|
||||
uint8_t bInterfaceClass;
|
||||
uint8_t bInterfaceSubClass;
|
||||
uint8_t bInterfaceProtocol;
|
||||
uint8_t iInterface;
|
||||
} PACKED;
|
||||
|
||||
struct UsbEndpointDescriptor {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bEndpointAddress;
|
||||
uint8_t bmAttributes;
|
||||
uint16_t wMaxPacketSize;
|
||||
uint8_t bInterval;
|
||||
} PACKED;
|
||||
|
||||
struct UsbDeviceDescriptor {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t bcdUSB;
|
||||
uint8_t bDeviceClass;
|
||||
uint8_t bDeviceSubClass;
|
||||
uint8_t bDeviceProtocol;
|
||||
uint8_t bMaxPacketSize0;
|
||||
uint16_t idVendor;
|
||||
uint16_t idProduct;
|
||||
uint16_t bcdDevice;
|
||||
uint8_t iManufacturer;
|
||||
uint8_t iProduct;
|
||||
uint8_t iSerialNumber;
|
||||
uint8_t bNumConfigurations;
|
||||
} PACKED;
|
||||
|
||||
struct UsbConfigDescriptor {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t wTotalLength;
|
||||
uint8_t bNumInterfaces;
|
||||
uint8_t bConfigurationValue;
|
||||
uint8_t iConfiguration;
|
||||
uint8_t bmAttributes;
|
||||
uint8_t bMaxPower;
|
||||
} PACKED;
|
||||
|
||||
struct UsbEndpointCompanionDescriptor {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bMaxBurst;
|
||||
uint8_t bmAttributes;
|
||||
uint16_t wBytesPerInterval;
|
||||
} PACKED;
|
||||
|
||||
struct UsbStringDescriptor {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t wData[DsLimitMaxNameSize];
|
||||
} PACKED;
|
||||
|
||||
struct UsbCtrlRequest {
|
||||
uint8_t bmRequestType;
|
||||
uint8_t bRequest;
|
||||
uint16_t wValue;
|
||||
uint16_t wIndex;
|
||||
uint16_t wLength;
|
||||
} PACKED;
|
||||
|
||||
enum UsbState {
|
||||
UsbState_Detached = 0,
|
||||
UsbState_Attached = 1,
|
||||
UsbState_Powered = 2,
|
||||
UsbState_Default = 3,
|
||||
UsbState_Address = 4,
|
||||
UsbState_Configured = 5,
|
||||
UsbState_Suspended = 6,
|
||||
};
|
||||
|
||||
enum UsbDescriptorSize {
|
||||
UsbDescriptorSize_Interface = sizeof(UsbInterfaceDescriptor),
|
||||
UsbDescriptorSize_Endpoint = sizeof(UsbEndpointDescriptor),
|
||||
UsbDescriptorSize_Device = sizeof(UsbDeviceDescriptor),
|
||||
UsbDescriptorSize_Config = sizeof(UsbConfigDescriptor),
|
||||
UsbDescriptorSize_EndpointCompanion = sizeof(UsbEndpointCompanionDescriptor),
|
||||
};
|
||||
|
||||
enum UsbDeviceSpeed {
|
||||
UsbDeviceSpeed_Invalid = 0,
|
||||
UsbDeviceSpeed_Low = 1,
|
||||
UsbDeviceSpeed_Full = 2,
|
||||
UsbDeviceSpeed_High = 3,
|
||||
UsbDeviceSpeed_Super = 4,
|
||||
UsbDeviceSpeed_SuperPlus = 5,
|
||||
};
|
||||
|
||||
|
||||
enum UsbEndpointAddressMask {
|
||||
UsbEndpointAddressMask_EndpointNumber = (0xF << 0),
|
||||
|
||||
UsbEndpointAddressMask_Dir = (0x1 << 7),
|
||||
|
||||
UsbEndpointAddressMask_DirHostToDevice = (0x0 << 7),
|
||||
UsbEndpointAddressMask_DirDevicetoHost = (0x1 << 7),
|
||||
};
|
||||
|
||||
enum UsbEndpointAttributeMask {
|
||||
UsbEndpointAttributeMask_XferType = (0x3 << 0),
|
||||
|
||||
UsbEndpointAttributeMask_XferTypeControl = (0x0 << 0),
|
||||
UsbEndpointAttributeMask_XferTypeIsoc = (0x1 << 0),
|
||||
UsbEndpointAttributeMask_XferTypeBulk = (0x2 << 0),
|
||||
UsbEndpointAttributeMask_XferTypeInt = (0x3 << 0),
|
||||
};
|
||||
|
||||
enum UsbEndpointDirection {
|
||||
UsbEndpointDirection_Invalid = 0,
|
||||
UsbEndpointDirection_ToDevice = 1,
|
||||
UsbEndpointDirection_ToHost = 2,
|
||||
UsbEndpointDirection_Control = 3,
|
||||
};
|
||||
|
||||
constexpr inline u8 UsbGetEndpointNumber(const UsbEndpointDescriptor *desc) {
|
||||
return desc->bEndpointAddress & UsbEndpointAddressMask_EndpointNumber;
|
||||
}
|
||||
|
||||
constexpr inline bool UsbEndpointIsHostToDevice(const UsbEndpointDescriptor *desc) {
|
||||
return (desc->bEndpointAddress & UsbEndpointAddressMask_Dir) == UsbEndpointAddressMask_DirHostToDevice;
|
||||
}
|
||||
|
||||
constexpr inline bool UsbEndpointIsDeviceToHost(const UsbEndpointDescriptor *desc) {
|
||||
return (desc->bEndpointAddress & UsbEndpointAddressMask_Dir) == UsbEndpointAddressMask_DirDevicetoHost;
|
||||
}
|
||||
|
||||
constexpr inline u8 UsbGetEndpointAddress(u8 number, UsbEndpointDirection dir) {
|
||||
u8 val = static_cast<u8>(number & UsbEndpointAddressMask_EndpointNumber);
|
||||
if (dir == UsbEndpointDirection_ToHost) {
|
||||
val |= UsbEndpointAddressMask_DirDevicetoHost;
|
||||
} else {
|
||||
val |= UsbEndpointAddressMask_DirHostToDevice;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
constexpr inline UsbEndpointDirection GetUsbEndpointDirection(const UsbEndpointDescriptor *desc) {
|
||||
if (UsbEndpointIsDeviceToHost(desc)) {
|
||||
return UsbEndpointDirection_ToHost;
|
||||
} else {
|
||||
return UsbEndpointDirection_ToDevice;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline bool UsbEndpointIsValid(const UsbEndpointDescriptor *desc) {
|
||||
return desc != nullptr && desc->bLength >= UsbDescriptorSize_Endpoint && desc->bEndpointAddress != 0;
|
||||
}
|
||||
|
||||
constexpr inline void UsbMarkEndpointInvalid(UsbEndpointDescriptor *desc) {
|
||||
desc->bLength = 0;
|
||||
desc->bEndpointAddress = 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,9 +46,9 @@ namespace ams::psc {
|
||||
Result PmModule::Initialize(const PmModuleId mid, const PmModuleId *dependencies, u32 dependency_count, os::EventClearMode clear_mode) {
|
||||
R_UNLESS(!this->initialized, psc::ResultAlreadyInitialized());
|
||||
|
||||
static_assert(sizeof(*dependencies) == sizeof(u32));
|
||||
static_assert(sizeof(*dependencies) == sizeof(u16));
|
||||
::PscPmModule module;
|
||||
R_TRY(::pscmGetPmModule(std::addressof(module), static_cast<::PscPmModuleId>(mid), reinterpret_cast<const u32 *>(dependencies), dependency_count, clear_mode == os::EventClearMode_AutoClear));
|
||||
R_TRY(::pscmGetPmModule(std::addressof(module), static_cast<::PscPmModuleId>(mid), reinterpret_cast<const u16 *>(dependencies), dependency_count, clear_mode == os::EventClearMode_AutoClear));
|
||||
|
||||
this->intf = RemoteObjectFactory::CreateSharedEmplaced<psc::sf::IPmModule, RemotePmModule>(module);
|
||||
this->system_event.AttachReadableHandle(module.event.revent, false, clear_mode);
|
||||
|
||||
@@ -1,803 +0,0 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "usb_remote_ds_root_service.hpp"
|
||||
#include "usb_remote_ds_service.hpp"
|
||||
#include "impl/usb_util.hpp"
|
||||
|
||||
namespace ams::usb {
|
||||
|
||||
Result DsClient::Initialize(ComplexId complex_id) {
|
||||
/* Clear interfaces. */
|
||||
for (size_t i = 0; i < util::size(m_interfaces); ++i) {
|
||||
m_interfaces[i] = nullptr;
|
||||
}
|
||||
|
||||
/* Initialize heap. */
|
||||
m_heap_handle = lmem::CreateExpHeap(m_heap_buffer, sizeof(m_heap_buffer), lmem::CreateOption_None);
|
||||
R_UNLESS(m_heap_handle != nullptr, usb::ResultMemAllocFailure());
|
||||
|
||||
/* Attach our allocator. */
|
||||
m_allocator.Attach(m_heap_handle);
|
||||
|
||||
/* Connect to usb:ds. */
|
||||
/* NOTE: Here, Nintendo does m_domain.InitializeByDomain<...>(...); m_domain.SetSessionCount(1); */
|
||||
{
|
||||
Service srv;
|
||||
R_TRY(sm::GetService(std::addressof(srv), sm::ServiceName::Encode("usb:ds")));
|
||||
|
||||
R_ABORT_UNLESS(serviceConvertToDomain(std::addressof(srv)));
|
||||
|
||||
using Allocator = decltype(m_allocator);
|
||||
using ObjectFactory = sf::ObjectFactory<Allocator::Policy>;
|
||||
|
||||
if (hos::GetVersion() >= hos::Version_11_0_0) {
|
||||
m_root_service = ObjectFactory::CreateSharedEmplaced<ds::IDsRootService, RemoteDsRootService>(std::addressof(m_allocator), srv, std::addressof(m_allocator));
|
||||
|
||||
R_TRY(m_root_service->GetService(std::addressof(m_ds_service)));
|
||||
} else {
|
||||
m_ds_service = ObjectFactory::CreateSharedEmplaced<ds::IDsService, RemoteDsService>(std::addressof(m_allocator), srv, std::addressof(m_allocator));
|
||||
}
|
||||
}
|
||||
|
||||
/* Bind the client process. */
|
||||
R_TRY(m_ds_service->Bind(complex_id, dd::GetCurrentProcessHandle()));
|
||||
|
||||
/* Get the state change event. */
|
||||
sf::CopyHandle event_handle;
|
||||
R_TRY(m_ds_service->GetStateChangeEvent(std::addressof(event_handle)));
|
||||
|
||||
/* Attach the state change event handle to our event. */
|
||||
os::AttachReadableHandleToSystemEvent(std::addressof(m_state_change_event), event_handle.GetValue(), true, os::EventClearMode_ManualClear);
|
||||
|
||||
/* Mark ourselves as initialized. */
|
||||
m_is_initialized = true;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result DsClient::Finalize() {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Disable and finalize all interfaces. */
|
||||
R_TRY(this->DisableDevice());
|
||||
for (size_t i = 0; i < util::size(m_interfaces); ++i) {
|
||||
if (m_interfaces[i] != nullptr) {
|
||||
R_TRY(m_interfaces[i]->Finalize());
|
||||
}
|
||||
}
|
||||
|
||||
/* Check our reference count .*/
|
||||
R_UNLESS(m_reference_count <= 1, usb::ResultResourceBusy());
|
||||
|
||||
/* Finalize members. */
|
||||
m_is_initialized = false;
|
||||
os::DestroySystemEvent(std::addressof(m_state_change_event));
|
||||
lmem::DestroyExpHeap(m_heap_handle);
|
||||
m_heap_handle = nullptr;
|
||||
|
||||
/* Destroy interface objects. */
|
||||
m_ds_service = nullptr;
|
||||
m_root_service = nullptr;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
bool DsClient::IsInitialized() {
|
||||
return m_is_initialized;
|
||||
}
|
||||
|
||||
Result DsClient::EnableDevice() {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Enable all interfaces. */
|
||||
if (hos::GetVersion() < hos::Version_11_0_0) {
|
||||
for (size_t i = 0; i < util::size(m_interfaces); ++i) {
|
||||
if (m_interfaces[i] != nullptr) {
|
||||
R_TRY(m_interfaces[i]->Enable());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable the device. */
|
||||
R_TRY(m_ds_service->Enable());
|
||||
|
||||
/* Mark disabled. */
|
||||
m_is_enabled = true;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result DsClient::DisableDevice() {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Disable the device. */
|
||||
R_TRY(m_ds_service->Disable());
|
||||
|
||||
/* Disable all interfaces. */
|
||||
if (hos::GetVersion() < hos::Version_11_0_0) {
|
||||
for (size_t i = 0; i < util::size(m_interfaces); ++i) {
|
||||
if (m_interfaces[i] != nullptr) {
|
||||
R_TRY(m_interfaces[i]->Disable());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark disabled. */
|
||||
m_is_enabled = false;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
os::SystemEventType *DsClient::GetStateChangeEvent() {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
return m_is_initialized ? std::addressof(m_state_change_event) : nullptr;
|
||||
}
|
||||
|
||||
Result DsClient::GetState(UsbState *out) {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Check that we have a service. */
|
||||
AMS_ABORT_UNLESS(m_ds_service != nullptr);
|
||||
|
||||
return m_ds_service->GetState(out);
|
||||
}
|
||||
|
||||
Result DsClient::ClearDeviceData() {
|
||||
return m_ds_service->ClearDeviceData();
|
||||
}
|
||||
|
||||
Result DsClient::AddUsbStringDescriptor(u8 *out_index, UsbStringDescriptor *desc) {
|
||||
return m_ds_service->AddUsbStringDescriptor(out_index, sf::InBuffer(reinterpret_cast<const u8 *>(desc), sizeof(*desc)));
|
||||
}
|
||||
|
||||
Result DsClient::DeleteUsbStringDescriptor(u8 index) {
|
||||
return m_ds_service->DeleteUsbStringDescriptor(index);
|
||||
}
|
||||
|
||||
Result DsClient::SetUsbDeviceDescriptor(UsbDeviceDescriptor *desc, UsbDeviceSpeed speed) {
|
||||
return m_ds_service->SetUsbDeviceDescriptor(sf::InBuffer(reinterpret_cast<const u8 *>(desc), sizeof(*desc)), speed);
|
||||
}
|
||||
|
||||
Result DsClient::SetBinaryObjectStore(u8 *data, int size) {
|
||||
return m_ds_service->SetBinaryObjectStore(sf::InBuffer(reinterpret_cast<const u8 *>(data), size));
|
||||
}
|
||||
|
||||
Result DsClient::AddInterface(DsInterface *intf, sf::SharedPointer<ds::IDsInterface> *out_srv, uint8_t bInterfaceNumber) {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Check that we have a service. */
|
||||
AMS_ABORT_UNLESS(m_ds_service != nullptr);
|
||||
|
||||
/* Register the interface. */
|
||||
R_TRY(m_ds_service->RegisterInterface(out_srv, bInterfaceNumber));
|
||||
|
||||
/* Set interface. */
|
||||
m_interfaces[bInterfaceNumber] = intf;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result DsClient::DeleteInterface(uint8_t bInterfaceNumber) {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Check that we have the interface. */
|
||||
R_UNLESS(m_interfaces[bInterfaceNumber] != nullptr, usb::ResultOperationDenied());
|
||||
|
||||
/* Clear the interface. */
|
||||
m_interfaces[bInterfaceNumber] = nullptr;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result DsInterface::Initialize(DsClient *client, u8 bInterfaceNumber) {
|
||||
/* Check that we haven't already initialized. */
|
||||
R_UNLESS(!m_is_initialized, usb::ResultAlreadyInitialized());
|
||||
|
||||
/* Set our client. */
|
||||
m_client = client;
|
||||
|
||||
/* Clear all endpoints. */
|
||||
for (size_t i = 0; i < util::size(m_endpoints); ++i) {
|
||||
m_endpoints[i] = nullptr;
|
||||
}
|
||||
|
||||
/* Set our interface number. */
|
||||
R_UNLESS(bInterfaceNumber < util::size(m_client->m_interfaces), usb::ResultInvalidParameter());
|
||||
m_interface_num = bInterfaceNumber;
|
||||
|
||||
/* Add the interface. */
|
||||
R_TRY(m_client->AddInterface(this, std::addressof(m_interface), m_interface_num));
|
||||
|
||||
/* Ensure we cleanup if we fail after this. */
|
||||
auto intf_guard = SCOPE_GUARD { m_client->DeleteInterface(m_interface_num); m_interface = nullptr; };
|
||||
|
||||
/* Get events. */
|
||||
sf::CopyHandle setup_event_handle;
|
||||
sf::CopyHandle ctrl_in_event_handle;
|
||||
sf::CopyHandle ctrl_out_event_handle;
|
||||
R_TRY(m_interface->GetSetupEvent(std::addressof(setup_event_handle)));
|
||||
R_TRY(m_interface->GetCtrlInCompletionEvent(std::addressof(ctrl_in_event_handle)));
|
||||
R_TRY(m_interface->GetCtrlOutCompletionEvent(std::addressof(ctrl_out_event_handle)));
|
||||
|
||||
/* Attach events. */
|
||||
os::AttachReadableHandleToSystemEvent(std::addressof(m_setup_event), setup_event_handle.GetValue(), true, os::EventClearMode_ManualClear);
|
||||
os::AttachReadableHandleToSystemEvent(std::addressof(m_ctrl_in_completion_event), ctrl_in_event_handle.GetValue(), true, os::EventClearMode_ManualClear);
|
||||
os::AttachReadableHandleToSystemEvent(std::addressof(m_ctrl_out_completion_event), ctrl_out_event_handle.GetValue(), true, os::EventClearMode_ManualClear);
|
||||
|
||||
/* Increment our client's reference count. */
|
||||
++m_client->m_reference_count;
|
||||
|
||||
/* Set ourselves as initialized. */
|
||||
m_is_initialized = true;
|
||||
|
||||
intf_guard.Cancel();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result DsInterface::Finalize() {
|
||||
/* Validate that we have a service. */
|
||||
R_ABORT_UNLESS(m_interface != nullptr);
|
||||
|
||||
/* We must be disabled. */
|
||||
R_UNLESS(!m_client->m_is_enabled, usb::ResultResourceBusy());
|
||||
|
||||
/* Finalize all endpoints. */
|
||||
for (size_t i = 0; i < util::size(m_endpoints); ++i) {
|
||||
if (m_endpoints[i] != nullptr) {
|
||||
R_TRY(m_endpoints[i]->Finalize());
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Check our reference count .*/
|
||||
R_UNLESS(m_reference_count <= 1, usb::ResultResourceBusy());
|
||||
|
||||
/* Finalize members. */
|
||||
m_is_initialized = false;
|
||||
os::DestroySystemEvent(std::addressof(m_setup_event));
|
||||
os::DestroySystemEvent(std::addressof(m_ctrl_in_completion_event));
|
||||
os::DestroySystemEvent(std::addressof(m_ctrl_out_completion_event));
|
||||
|
||||
/* Delete ourselves from our cleint. */
|
||||
m_client->DeleteInterface(m_interface_num);
|
||||
|
||||
/* Destroy our service. */
|
||||
m_interface = nullptr;
|
||||
|
||||
/* Close our reference to our client. */
|
||||
--m_client->m_reference_count;
|
||||
m_client = nullptr;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result DsInterface::AppendConfigurationData(UsbDeviceSpeed speed, void *data, u32 size) {
|
||||
return m_interface->AppendConfigurationData(m_interface_num, speed, sf::InBuffer(data, size));
|
||||
}
|
||||
|
||||
bool DsInterface::IsInitialized() {
|
||||
return m_is_initialized;
|
||||
}
|
||||
|
||||
os::SystemEventType *DsInterface::GetSetupEvent() {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
return m_is_initialized ? std::addressof(m_setup_event) : nullptr;
|
||||
}
|
||||
|
||||
Result DsInterface::GetSetupPacket(UsbCtrlRequest *out) {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Check that we have a service. */
|
||||
AMS_ABORT_UNLESS(m_interface != nullptr);
|
||||
|
||||
return m_interface->GetSetupPacket(sf::OutBuffer(out, sizeof(*out)));
|
||||
}
|
||||
|
||||
Result DsInterface::Enable() {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* If we're already enabled, nothing to do. */
|
||||
R_SUCCEED_IF(m_client->m_is_enabled);
|
||||
|
||||
/* Check that we have a service. */
|
||||
AMS_ABORT_UNLESS(m_interface != nullptr);
|
||||
|
||||
/* Perform the enable. */
|
||||
R_TRY(m_interface->Enable());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result DsInterface::Disable() {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* If we're already disabled, nothing to do. */
|
||||
R_SUCCEED_IF(!m_client->m_is_enabled);
|
||||
|
||||
/* Check that we have a service. */
|
||||
AMS_ABORT_UNLESS(m_interface != nullptr);
|
||||
|
||||
/* Perform the disable. */
|
||||
R_TRY(m_interface->Disable());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result DsInterface::AddEndpoint(DsEndpoint *ep, u8 bEndpointAddress, sf::SharedPointer<ds::IDsEndpoint> *out) {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Check that we're not already enabled. */
|
||||
R_UNLESS(!m_client->m_is_enabled, usb::ResultOperationDenied());
|
||||
|
||||
/* Check that we have a service. */
|
||||
AMS_ABORT_UNLESS(m_interface != nullptr);
|
||||
|
||||
/* Register the endpoint. */
|
||||
R_TRY(m_interface->RegisterEndpoint(bEndpointAddress, out));
|
||||
|
||||
/* Set the endpoint. */
|
||||
m_endpoints[impl::GetEndpointIndex(bEndpointAddress)] = ep;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result DsInterface::DeleteEndpoint(u8 bEndpointAddress) {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Check that we're disabled and have the endpoint. */
|
||||
const auto index = impl::GetEndpointIndex(bEndpointAddress);
|
||||
R_UNLESS(!m_client->m_is_enabled, usb::ResultOperationDenied());
|
||||
R_UNLESS(m_endpoints[index] != nullptr, usb::ResultOperationDenied());
|
||||
|
||||
/* Clear the endpoint. */
|
||||
m_endpoints[index] = nullptr;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result DsInterface::CtrlIn(u32 *out_transferred, void *dst, u32 size) {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Check that we're enabled. */
|
||||
R_UNLESS(m_client->m_is_enabled, usb::ResultOperationDenied());
|
||||
|
||||
/* Check that the data is aligned. */
|
||||
R_UNLESS(usb::IsDmaAligned(reinterpret_cast<u64>(dst)), usb::ResultAlignmentError());
|
||||
|
||||
/* If we should, flush cache. */
|
||||
if (size != 0) {
|
||||
dd::FlushDataCache(dst, size);
|
||||
}
|
||||
|
||||
/* Check that we have a service. */
|
||||
AMS_ABORT_UNLESS(m_interface != nullptr);
|
||||
|
||||
/* Perform the transfer. */
|
||||
u32 urb_id;
|
||||
R_TRY(m_interface->CtrlInAsync(std::addressof(urb_id), reinterpret_cast<u64>(dst), size));
|
||||
|
||||
/* Wait for control to finish. */
|
||||
os::WaitSystemEvent(std::addressof(m_ctrl_in_completion_event));
|
||||
os::ClearSystemEvent(std::addressof(m_ctrl_in_completion_event));
|
||||
|
||||
/* Get the urb report. */
|
||||
R_ABORT_UNLESS(m_interface->GetCtrlInUrbReport(std::addressof(m_report)));
|
||||
|
||||
/* Check the report is for our urb. */
|
||||
R_UNLESS(m_report.count == 1, usb::ResultInternalStateError());
|
||||
R_UNLESS(m_report.reports[0].id == urb_id, usb::ResultInternalStateError());
|
||||
|
||||
/* Set output bytes. */
|
||||
if (out_transferred != nullptr) {
|
||||
*out_transferred = m_report.reports[0].transferred_size;
|
||||
}
|
||||
|
||||
/* Handle the report. */
|
||||
switch (m_report.reports[0].status) {
|
||||
case UrbStatus_Cancelled:
|
||||
return usb::ResultInterrupted();
|
||||
case UrbStatus_Failed:
|
||||
return usb::ResultTransactionError();
|
||||
case UrbStatus_Finished:
|
||||
return ResultSuccess();
|
||||
default:
|
||||
return usb::ResultInternalStateError();
|
||||
}
|
||||
}
|
||||
|
||||
Result DsInterface::CtrlOut(u32 *out_transferred, void *dst, u32 size) {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Check that we're enabled. */
|
||||
R_UNLESS(m_client->m_is_enabled, usb::ResultOperationDenied());
|
||||
|
||||
/* Check that the data is aligned. */
|
||||
R_UNLESS(usb::IsDmaAligned(reinterpret_cast<u64>(dst)), usb::ResultAlignmentError());
|
||||
|
||||
/* If we should, invalidate cache. */
|
||||
if (size != 0) {
|
||||
dd::InvalidateDataCache(dst, size);
|
||||
}
|
||||
|
||||
/* Check that we have a service. */
|
||||
AMS_ABORT_UNLESS(m_interface != nullptr);
|
||||
|
||||
/* Perform the transfer. */
|
||||
u32 urb_id;
|
||||
R_TRY(m_interface->CtrlOutAsync(std::addressof(urb_id), reinterpret_cast<u64>(dst), size));
|
||||
|
||||
/* Wait for control to finish. */
|
||||
os::WaitSystemEvent(std::addressof(m_ctrl_out_completion_event));
|
||||
os::ClearSystemEvent(std::addressof(m_ctrl_out_completion_event));
|
||||
|
||||
/* Ensure that cache remains consistent. */
|
||||
ON_SCOPE_EXIT {
|
||||
if (size != 0) {
|
||||
dd::InvalidateDataCache(dst, size);
|
||||
}
|
||||
};
|
||||
|
||||
/* Get the urb report. */
|
||||
R_ABORT_UNLESS(m_interface->GetCtrlOutUrbReport(std::addressof(m_report)));
|
||||
|
||||
/* Check the report is for our urb. */
|
||||
R_UNLESS(m_report.count == 1, usb::ResultInternalStateError());
|
||||
R_UNLESS(m_report.reports[0].id == urb_id, usb::ResultInternalStateError());
|
||||
|
||||
/* Set output bytes. */
|
||||
if (out_transferred != nullptr) {
|
||||
*out_transferred = m_report.reports[0].transferred_size;
|
||||
}
|
||||
|
||||
/* Handle the report. */
|
||||
switch (m_report.reports[0].status) {
|
||||
case UrbStatus_Cancelled:
|
||||
return usb::ResultInterrupted();
|
||||
case UrbStatus_Failed:
|
||||
return usb::ResultTransactionError();
|
||||
case UrbStatus_Finished:
|
||||
return ResultSuccess();
|
||||
default:
|
||||
return usb::ResultInternalStateError();
|
||||
}
|
||||
}
|
||||
|
||||
Result DsInterface::CtrlRead(u32 *out_transferred, void *dst, u32 size) {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Do the data transfer. */
|
||||
Result result = this->CtrlOut(out_transferred, dst, size);
|
||||
|
||||
/* Do the status transfer. */
|
||||
if (R_SUCCEEDED(result)) {
|
||||
result = this->CtrlIn(nullptr, nullptr, 0);
|
||||
}
|
||||
|
||||
/* If we failed, stall. */
|
||||
if (R_FAILED(result)) {
|
||||
result = this->CtrlStall();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Result DsInterface::CtrlWrite(u32 *out_transferred, void *dst, u32 size) {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Do the data transfer. */
|
||||
Result result = this->CtrlIn(out_transferred, dst, size);
|
||||
|
||||
/* Do the status transfer. */
|
||||
if (R_SUCCEEDED(result)) {
|
||||
result = this->CtrlOut(nullptr, nullptr, 0);
|
||||
}
|
||||
|
||||
/* If we failed, stall. */
|
||||
if (R_FAILED(result)) {
|
||||
result = this->CtrlStall();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Result DsInterface::CtrlDone() {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Do the status transfer. */
|
||||
Result result = this->CtrlIn(nullptr, nullptr, 0);
|
||||
|
||||
/* If we failed, stall. */
|
||||
if (R_FAILED(result)) {
|
||||
result = this->CtrlStall();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Result DsInterface::CtrlStall() {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Check that we're enabled. */
|
||||
R_UNLESS(m_client->m_is_enabled, usb::ResultOperationDenied());
|
||||
|
||||
/* Check that we have a service. */
|
||||
AMS_ABORT_UNLESS(m_interface != nullptr);
|
||||
|
||||
return m_interface->CtrlStall();
|
||||
}
|
||||
|
||||
Result DsEndpoint::Initialize(DsInterface *interface, u8 bEndpointAddress) {
|
||||
/* Check that the interface is valid. */
|
||||
AMS_ABORT_UNLESS(interface != nullptr);
|
||||
|
||||
/* Check that we're not already initialized. */
|
||||
R_UNLESS(!m_is_initialized, usb::ResultAlreadyInitialized());
|
||||
|
||||
/* Set our interface. */
|
||||
m_interface = interface;
|
||||
|
||||
/* Add the endpoint. */
|
||||
R_TRY(m_interface->AddEndpoint(this, bEndpointAddress, std::addressof(m_endpoint)));
|
||||
|
||||
/* Set our address. */
|
||||
m_address = bEndpointAddress;
|
||||
|
||||
/* Ensure we clean up if we fail after this. */
|
||||
auto ep_guard = SCOPE_GUARD { m_interface->DeleteEndpoint(m_address); m_endpoint = nullptr; };
|
||||
|
||||
/* Get completion event. */
|
||||
sf::CopyHandle event_handle;
|
||||
R_TRY(m_endpoint->GetCompletionEvent(std::addressof(event_handle)));
|
||||
|
||||
/* Increment our interface's reference count. */
|
||||
++m_interface->m_reference_count;
|
||||
++m_interface->m_client->m_reference_count;
|
||||
|
||||
/* Attach our event. */
|
||||
os::AttachReadableHandleToSystemEvent(std::addressof(m_completion_event), event_handle, true, os::EventClearMode_ManualClear);
|
||||
|
||||
/* Mark initialized. */
|
||||
m_is_initialized = true;
|
||||
|
||||
ep_guard.Cancel();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result DsEndpoint::Finalize() {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Cancel any pending transactions. */
|
||||
m_endpoint->Cancel();
|
||||
|
||||
/* Wait for us to be at one reference count. */
|
||||
while (m_reference_count > 1) {
|
||||
os::SleepThread(TimeSpan::FromMilliSeconds(25));
|
||||
}
|
||||
|
||||
/* Destroy our event. */
|
||||
os::DestroySystemEvent(std::addressof(m_completion_event));
|
||||
|
||||
/* Decrement our interface's reference count. */
|
||||
--m_interface->m_reference_count;
|
||||
--m_interface->m_client->m_reference_count;
|
||||
|
||||
/* Delete ourselves. */
|
||||
R_TRY(m_interface->DeleteEndpoint(m_address));
|
||||
|
||||
/* Clear ourselves. */
|
||||
m_interface = nullptr;
|
||||
m_endpoint = nullptr;
|
||||
|
||||
/* Mark uninitialized. */
|
||||
m_is_initialized = false;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
bool DsEndpoint::IsInitialized() {
|
||||
return m_is_initialized;
|
||||
}
|
||||
|
||||
Result DsEndpoint::PostBuffer(u32 *out_transferred, void *buf, u32 size) {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Post buffer. */
|
||||
u32 urb_id;
|
||||
R_TRY(this->PostBufferAsync(std::addressof(urb_id), buf, size));
|
||||
|
||||
/* Wait for completion. */
|
||||
os::WaitSystemEvent(std::addressof(m_completion_event));
|
||||
os::ClearSystemEvent(std::addressof(m_completion_event));
|
||||
|
||||
/* Get URB report. */
|
||||
UrbReport report;
|
||||
AMS_ABORT_UNLESS(m_endpoint != nullptr);
|
||||
R_ABORT_UNLESS(m_endpoint->GetUrbReport(std::addressof(report)));
|
||||
|
||||
/* Check the report is for our urb. */
|
||||
R_UNLESS(report.count == 1, usb::ResultInternalStateError());
|
||||
R_UNLESS(report.reports[0].id == urb_id, usb::ResultInternalStateError());
|
||||
|
||||
/* Set output bytes. */
|
||||
if (out_transferred != nullptr) {
|
||||
*out_transferred = report.reports[0].transferred_size;
|
||||
}
|
||||
|
||||
/* Handle the report. */
|
||||
switch (report.reports[0].status) {
|
||||
case UrbStatus_Cancelled:
|
||||
return usb::ResultInterrupted();
|
||||
case UrbStatus_Failed:
|
||||
return usb::ResultTransactionError();
|
||||
case UrbStatus_Finished:
|
||||
return ResultSuccess();
|
||||
default:
|
||||
return usb::ResultInternalStateError();
|
||||
}
|
||||
}
|
||||
|
||||
Result DsEndpoint::PostBufferAsync(u32 *out_urb_id, void *buf, u32 size) {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Check that the buffer is DMA aligned. */
|
||||
R_UNLESS(usb::IsDmaAligned(reinterpret_cast<u64>(buf)), usb::ResultAlignmentError());
|
||||
|
||||
/* Check that we have a service. */
|
||||
AMS_ABORT_UNLESS(m_endpoint != nullptr);
|
||||
|
||||
/* Post */
|
||||
u32 urb_id = 0;
|
||||
R_TRY(m_endpoint->PostBufferAsync(std::addressof(urb_id), reinterpret_cast<u64>(buf), size));
|
||||
|
||||
*out_urb_id = urb_id;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
os::SystemEventType *DsEndpoint::GetCompletionEvent() {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
return m_is_initialized ? std::addressof(m_completion_event) : nullptr;
|
||||
}
|
||||
|
||||
Result DsEndpoint::GetUrbReport(UrbReport *out) {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Check that we have a service. */
|
||||
AMS_ABORT_UNLESS(m_endpoint != nullptr);
|
||||
|
||||
return m_endpoint->GetUrbReport(out);
|
||||
}
|
||||
|
||||
Result DsEndpoint::Cancel() {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Check that we have a service. */
|
||||
AMS_ABORT_UNLESS(m_endpoint != nullptr);
|
||||
|
||||
return m_endpoint->Cancel();
|
||||
}
|
||||
|
||||
Result DsEndpoint::SetZeroLengthTransfer(bool zlt) {
|
||||
/* Create a scoped reference. */
|
||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
||||
|
||||
/* Check that we're initialized. */
|
||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
||||
|
||||
/* Check that we have a service. */
|
||||
AMS_ABORT_UNLESS(m_endpoint != nullptr);
|
||||
|
||||
return m_endpoint->SetZlt(zlt);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "usb_remote_ds_endpoint.hpp"
|
||||
|
||||
namespace ams::usb {
|
||||
|
||||
Result RemoteDsEndpoint::PostBufferAsync(sf::Out<u32> out_urb_id, u64 address, u32 size) {
|
||||
const struct {
|
||||
u32 size;
|
||||
u64 address;
|
||||
} in = { size, address };
|
||||
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatchInOut(std::addressof(m_srv), 0, in, *out_urb_id);
|
||||
}
|
||||
|
||||
Result RemoteDsEndpoint::Cancel() {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatch(std::addressof(m_srv), 1);
|
||||
}
|
||||
|
||||
Result RemoteDsEndpoint::GetCompletionEvent(sf::OutCopyHandle out) {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatch(std::addressof(m_srv), 2,
|
||||
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
|
||||
.out_handles = out.GetHandlePointer(),
|
||||
);
|
||||
}
|
||||
|
||||
Result RemoteDsEndpoint::GetUrbReport(sf::Out<usb::UrbReport> out) {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatchOut(std::addressof(m_srv), 3, *out);
|
||||
}
|
||||
|
||||
Result RemoteDsEndpoint::Stall() {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatch(std::addressof(m_srv), 4);
|
||||
}
|
||||
|
||||
Result RemoteDsEndpoint::SetZlt(bool zlt) {
|
||||
const u8 in = zlt ? 1 : 0;
|
||||
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatchIn(std::addressof(m_srv), 5, in);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "usb_remote_ds_interface.hpp"
|
||||
#include "usb_remote_ds_endpoint.hpp"
|
||||
|
||||
namespace ams::usb {
|
||||
|
||||
Result RemoteDsInterface::RegisterEndpoint(u8 endpoint_address, sf::Out<sf::SharedPointer<usb::ds::IDsEndpoint>> out) {
|
||||
Service srv;
|
||||
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
R_TRY(serviceDispatchIn(std::addressof(m_srv), 0, endpoint_address,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = std::addressof(srv),
|
||||
));
|
||||
|
||||
*out = ObjectFactory::CreateSharedEmplaced<ds::IDsEndpoint, RemoteDsEndpoint>(m_allocator, srv);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result RemoteDsInterface::GetSetupEvent(sf::OutCopyHandle out) {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatch(std::addressof(m_srv), 1,
|
||||
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
|
||||
.out_handles = out.GetHandlePointer(),
|
||||
);
|
||||
}
|
||||
|
||||
Result RemoteDsInterface::GetSetupPacket(const sf::OutBuffer &out) {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatch(std::addressof(m_srv), 2,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
||||
.buffers = { { out.GetPointer(), out.GetSize() } },
|
||||
);
|
||||
}
|
||||
|
||||
Result RemoteDsInterface::CtrlInAsync(sf::Out<u32> out_urb_id, u64 address, u32 size) {
|
||||
const struct {
|
||||
u32 size;
|
||||
u64 address;
|
||||
} in = { size, address };
|
||||
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatchInOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 3 : 5, in, *out_urb_id);
|
||||
}
|
||||
|
||||
Result RemoteDsInterface::CtrlOutAsync(sf::Out<u32> out_urb_id, u64 address, u32 size) {
|
||||
const struct {
|
||||
u32 size;
|
||||
u64 address;
|
||||
} in = { size, address };
|
||||
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatchInOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 4 : 6, in, *out_urb_id);
|
||||
}
|
||||
|
||||
Result RemoteDsInterface::GetCtrlInCompletionEvent(sf::OutCopyHandle out) {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 5 : 7,
|
||||
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
|
||||
.out_handles = out.GetHandlePointer(),
|
||||
);
|
||||
}
|
||||
|
||||
Result RemoteDsInterface::GetCtrlInUrbReport(sf::Out<usb::UrbReport> out) {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 6 : 8, *out);
|
||||
}
|
||||
|
||||
Result RemoteDsInterface::GetCtrlOutCompletionEvent(sf::OutCopyHandle out) {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 7 : 9,
|
||||
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
|
||||
.out_handles = out.GetHandlePointer(),
|
||||
);
|
||||
}
|
||||
|
||||
Result RemoteDsInterface::GetCtrlOutUrbReport(sf::Out<usb::UrbReport> out) {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 8 : 10, *out);
|
||||
}
|
||||
|
||||
Result RemoteDsInterface::CtrlStall() {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 9 : 11);
|
||||
}
|
||||
|
||||
Result RemoteDsInterface::AppendConfigurationData(u8 bInterfaceNumber, usb::UsbDeviceSpeed device_speed, const sf::InBuffer &data) {
|
||||
if (hos::GetVersion() >= hos::Version_11_0_0) {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatchIn(std::addressof(m_srv), 10, device_speed,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
||||
.buffers = { { data.GetPointer(), data.GetSize() } },
|
||||
);
|
||||
} else {
|
||||
const struct {
|
||||
u8 bInterfaceNumber;
|
||||
usb::UsbDeviceSpeed device_speed;
|
||||
} in = { bInterfaceNumber, device_speed };
|
||||
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatchIn(std::addressof(m_srv), 12, in,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
||||
.buffers = { { data.GetPointer(), data.GetSize() } },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Result RemoteDsInterface::Enable() {
|
||||
R_SUCCEED_IF(hos::GetVersion() >= hos::Version_11_0_0);
|
||||
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatch(std::addressof(m_srv), 3);
|
||||
}
|
||||
|
||||
Result RemoteDsInterface::Disable() {
|
||||
R_SUCCEED_IF(hos::GetVersion() >= hos::Version_11_0_0);
|
||||
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatch(std::addressof(m_srv), 3);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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 <stratosphere.hpp>
|
||||
|
||||
namespace ams::usb {
|
||||
|
||||
class RemoteDsInterface {
|
||||
private:
|
||||
using Allocator = sf::ExpHeapAllocator;
|
||||
using ObjectFactory = sf::ObjectFactory<Allocator::Policy>;
|
||||
private:
|
||||
Service m_srv;
|
||||
Allocator *m_allocator;
|
||||
public:
|
||||
RemoteDsInterface(Service &srv, sf::ExpHeapAllocator *allocator) : m_srv(srv), m_allocator(allocator) { /* ... */ }
|
||||
virtual ~RemoteDsInterface() { serviceClose(std::addressof(m_srv)); }
|
||||
public:
|
||||
Result RegisterEndpoint(u8 endpoint_address, sf::Out<sf::SharedPointer<usb::ds::IDsEndpoint>> out);
|
||||
Result GetSetupEvent(sf::OutCopyHandle out);
|
||||
Result GetSetupPacket(const sf::OutBuffer & out);
|
||||
Result CtrlInAsync(sf::Out<u32> out_urb_id, u64 address, u32 size);
|
||||
Result CtrlOutAsync(sf::Out<u32> out_urb_id, u64 address, u32 size);
|
||||
Result GetCtrlInCompletionEvent(sf::OutCopyHandle out);
|
||||
Result GetCtrlInUrbReport(sf::Out<usb::UrbReport> out);
|
||||
Result GetCtrlOutCompletionEvent(sf::OutCopyHandle out);
|
||||
Result GetCtrlOutUrbReport(sf::Out<usb::UrbReport> out);
|
||||
Result CtrlStall();
|
||||
Result AppendConfigurationData(u8 bInterfaceNumber, usb::UsbDeviceSpeed device_speed, const sf::InBuffer &data);
|
||||
Result Enable();
|
||||
Result Disable();
|
||||
};
|
||||
static_assert(ds::IsIDsInterface<RemoteDsInterface>);
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* 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 <stratosphere.hpp>
|
||||
|
||||
namespace ams::usb {
|
||||
|
||||
class RemoteDsRootService {
|
||||
private:
|
||||
using Allocator = sf::ExpHeapAllocator;
|
||||
using ObjectFactory = sf::ObjectFactory<Allocator::Policy>;
|
||||
private:
|
||||
Service m_srv;
|
||||
Allocator *m_allocator;
|
||||
public:
|
||||
RemoteDsRootService(Service &srv, sf::ExpHeapAllocator *allocator) : m_srv(srv), m_allocator(allocator) { /* ... */ }
|
||||
virtual ~RemoteDsRootService() { serviceClose(std::addressof(m_srv)); }
|
||||
public:
|
||||
Result GetService(sf::Out<sf::SharedPointer<usb::ds::IDsService>> out);
|
||||
};
|
||||
static_assert(ds::IsIDsRootService<RemoteDsRootService>);
|
||||
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "usb_remote_ds_service.hpp"
|
||||
#include "usb_remote_ds_interface.hpp"
|
||||
|
||||
namespace ams::usb {
|
||||
|
||||
Result RemoteDsService::Bind(usb::ComplexId complex_id, sf::CopyHandle process_h) {
|
||||
if (hos::GetVersion() >= hos::Version_11_0_0) {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
R_TRY(serviceDispatchIn(std::addressof(m_srv), 0, complex_id,
|
||||
.in_num_handles = 1,
|
||||
.in_handles = { process_h.GetValue() }
|
||||
));
|
||||
} else {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
R_TRY(serviceDispatchIn(std::addressof(m_srv), 0, complex_id));
|
||||
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
R_TRY(serviceDispatch(std::addressof(m_srv), 1,
|
||||
.in_num_handles = 1,
|
||||
.in_handles = { process_h.GetValue() })
|
||||
);
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result RemoteDsService::RegisterInterface(sf::Out<sf::SharedPointer<usb::ds::IDsInterface>> out, u8 bInterfaceNumber) {
|
||||
Service srv;
|
||||
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
R_TRY(serviceDispatchIn(std::addressof(m_srv), (hos::GetVersion() >= hos::Version_11_0_0 ? 1 : 2), bInterfaceNumber,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = std::addressof(srv),
|
||||
));
|
||||
|
||||
*out = ObjectFactory::CreateSharedEmplaced<ds::IDsInterface, RemoteDsInterface>(m_allocator, srv, m_allocator);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result RemoteDsService::GetStateChangeEvent(sf::OutCopyHandle out) {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 2 : 3,
|
||||
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
|
||||
.out_handles = out.GetHandlePointer(),
|
||||
);
|
||||
}
|
||||
|
||||
Result RemoteDsService::GetState(sf::Out<usb::UsbState> out) {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 3 : 4, *out);
|
||||
}
|
||||
|
||||
Result RemoteDsService::ClearDeviceData() {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 4 : 5);
|
||||
}
|
||||
|
||||
Result RemoteDsService::AddUsbStringDescriptor(sf::Out<u8> out, const sf::InBuffer &desc) {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 5 : 6, *out,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
||||
.buffers = { { desc.GetPointer(), desc.GetSize() } },
|
||||
);
|
||||
}
|
||||
|
||||
Result RemoteDsService::DeleteUsbStringDescriptor(u8 index) {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatchIn(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 6 : 7, index);
|
||||
}
|
||||
|
||||
Result RemoteDsService::SetUsbDeviceDescriptor(const sf::InBuffer &desc, usb::UsbDeviceSpeed speed) {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatchIn(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 7 : 8, speed,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
||||
.buffers = { { desc.GetPointer(), desc.GetSize() } },
|
||||
);
|
||||
}
|
||||
|
||||
Result RemoteDsService::SetBinaryObjectStore(const sf::InBuffer &bos) {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 8 : 9,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
||||
.buffers = { { bos.GetPointer(), bos.GetSize() } },
|
||||
);
|
||||
}
|
||||
|
||||
Result RemoteDsService::Enable() {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 9 : 10);
|
||||
}
|
||||
|
||||
Result RemoteDsService::Disable() {
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 10 : 11);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* 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 <stratosphere.hpp>
|
||||
|
||||
namespace ams::usb {
|
||||
|
||||
class RemoteDsService {
|
||||
private:
|
||||
using Allocator = sf::ExpHeapAllocator;
|
||||
using ObjectFactory = sf::ObjectFactory<Allocator::Policy>;
|
||||
private:
|
||||
Service m_srv;
|
||||
Allocator *m_allocator;
|
||||
public:
|
||||
RemoteDsService(Service &srv, sf::ExpHeapAllocator *allocator) : m_srv(srv), m_allocator(allocator) { /* ... */ }
|
||||
virtual ~RemoteDsService() { serviceClose(std::addressof(m_srv)); }
|
||||
public:
|
||||
Result Bind(usb::ComplexId complex_id, sf::CopyHandle process_h);
|
||||
Result RegisterInterface(sf::Out<sf::SharedPointer<usb::ds::IDsInterface>> out, u8 bInterfaceNumber);
|
||||
Result GetStateChangeEvent(sf::OutCopyHandle out);
|
||||
Result GetState(sf::Out<usb::UsbState> out);
|
||||
Result ClearDeviceData();
|
||||
Result AddUsbStringDescriptor(sf::Out<u8> out, const sf::InBuffer &desc);
|
||||
Result DeleteUsbStringDescriptor(u8 index);
|
||||
Result SetUsbDeviceDescriptor(const sf::InBuffer &desc, usb::UsbDeviceSpeed speed);
|
||||
Result SetBinaryObjectStore(const sf::InBuffer &bos);
|
||||
Result Enable();
|
||||
Result Disable();
|
||||
};
|
||||
static_assert(ds::IsIDsService<RemoteDsService>);
|
||||
|
||||
}
|
||||
@@ -38,4 +38,5 @@
|
||||
#include <vapours/ams/ams_fatal_error_context.hpp>
|
||||
|
||||
#include <vapours/dd.hpp>
|
||||
#include <vapours/sdmmc.hpp>
|
||||
#include <vapours/sdmmc.hpp>
|
||||
#include <vapours/prfile2.hpp>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MINOR 18
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 1
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 0
|
||||
|
||||
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
|
||||
|
||||
|
||||
31
libraries/libvapours/include/vapours/prfile2.hpp
Normal file
31
libraries/libvapours/include/vapours/prfile2.hpp
Normal 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/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/svc.hpp>
|
||||
|
||||
#include <vapours/prfile2/prfile2_common.hpp>
|
||||
#include <vapours/prfile2/prfile2_str.hpp>
|
||||
#include <vapours/prfile2/prfile2_system.hpp>
|
||||
#include <vapours/prfile2/pdm/prfile2_pdm_api.hpp>
|
||||
#include <vapours/prfile2/pdm/prfile2_pdm_disk_management.hpp>
|
||||
#include <vapours/prfile2/pdm/prfile2_pdm_upper_layer_api.hpp>
|
||||
#include <vapours/prfile2/prfile2_fatfs.hpp>
|
||||
#include <vapours/prfile2/prfile2_volume.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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/prfile2/pdm/prfile2_pdm_types.hpp>
|
||||
#include <vapours/prfile2/pdm/prfile2_pdm_common.hpp>
|
||||
#include <vapours/prfile2/pdm/prfile2_pdm_disk_management.hpp>
|
||||
|
||||
namespace ams::prfile2::pdm {
|
||||
|
||||
pdm::Error Initialize(u32 config, void *param);
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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/prfile2/prfile2_build_config.hpp>
|
||||
#include <vapours/prfile2/prfile2_handle.hpp>
|
||||
#include <vapours/prfile2/pdm/prfile2_pdm_types.hpp>
|
||||
|
||||
namespace ams::prfile2::pdm {
|
||||
|
||||
constexpr inline const auto MaxDisks = 5;
|
||||
constexpr inline const auto MaxPartitions = 5;
|
||||
|
||||
struct Disk;
|
||||
struct Partition;
|
||||
|
||||
using PartitionEventCallback = void (*)(u32 event, void *param);
|
||||
|
||||
using EraseFunction = pdm::Error (*)(u32, u32);
|
||||
|
||||
struct DiskInfo {
|
||||
u32 total_sectors;
|
||||
u16 cylinders;
|
||||
u8 heads;
|
||||
u8 sectors_per_track;
|
||||
u16 bytes_per_sector;
|
||||
u32 media_attr;
|
||||
void *format_param;
|
||||
/* ... */
|
||||
};
|
||||
|
||||
struct FunctionTable {
|
||||
pdm::Error (*initialize)(HandleType disk_handle);
|
||||
pdm::Error (*finalize)(HandleType disk_handle);
|
||||
pdm::Error (*mount)(HandleType disk_handle);
|
||||
pdm::Error (*unmount)(HandleType disk_handle);
|
||||
pdm::Error (*format)(HandleType disk_handle, const u8 *);
|
||||
pdm::Error (*physical_read)(HandleType disk_handle, u8 *dst, u32 block, u32 count, u32 *num_read);
|
||||
pdm::Error (*physical_write)(HandleType disk_handle, const u8 *src, u32 block, u32 count, u32 *num_read);
|
||||
pdm::Error (*get_disk_info)(HandleType disk_handle, DiskInfo *out);
|
||||
};
|
||||
|
||||
struct DiskTable {
|
||||
FunctionTable *function_table;
|
||||
u64 ui_ext;
|
||||
};
|
||||
|
||||
struct InitDisk {
|
||||
pdm::Error (*function)(DiskTable *disk_table, u64 ui_ext);
|
||||
u64 ui_ext;
|
||||
};
|
||||
|
||||
/* ... */
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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/prfile2/prfile2_handle.hpp>
|
||||
#include <vapours/prfile2/pdm/prfile2_pdm_common.hpp>
|
||||
|
||||
namespace ams::prfile2::pdm {
|
||||
|
||||
struct Disk {
|
||||
u32 status;
|
||||
DiskTable disk_table;
|
||||
u32 signature;
|
||||
u16 open_count;
|
||||
u16 lock_count;
|
||||
Disk *lock_handle;
|
||||
DiskInfo disk_info;
|
||||
InitDisk *init_disk_table;
|
||||
HandleType current_partition_handle;
|
||||
DiskCallback erase_callback;
|
||||
bool is_inserted;
|
||||
volatile NonBlockingProtocolType nbc;
|
||||
volatile NonBlockingProtocolType nbc_detect;
|
||||
volatile NonBlockingProtocolType nbc_req;
|
||||
|
||||
template<size_t Ix>
|
||||
constexpr ALWAYS_INLINE bool GetStatusBit() const {
|
||||
constexpr u32 Mask = (1u << Ix);
|
||||
return (this->status & Mask) != 0;
|
||||
}
|
||||
|
||||
template<size_t Ix>
|
||||
constexpr ALWAYS_INLINE void SetStatusBit(bool en) {
|
||||
constexpr u32 Mask = (1u << Ix);
|
||||
if (en) {
|
||||
this->status |= Mask;
|
||||
} else {
|
||||
this->status &= ~Mask;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool IsOpen() const { return this->GetStatusBit<0>(); }
|
||||
constexpr void SetOpen(bool en) { this->SetStatusBit<0>(en); }
|
||||
|
||||
constexpr bool IsLocked() const { return this->GetStatusBit<1>(); }
|
||||
constexpr void SetLocked(bool en) { this->SetStatusBit<1>(en); }
|
||||
};
|
||||
|
||||
namespace disk {
|
||||
|
||||
pdm::Error OpenDisk(InitDisk *init_disk_table, HandleType *out);
|
||||
pdm::Error CloseDisk(HandleType handle);
|
||||
|
||||
/* ... */
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,24 +14,30 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include <vapours/prfile2/prfile2_handle.hpp>
|
||||
#include <vapours/prfile2/pdm/prfile2_pdm_common.hpp>
|
||||
#include <vapours/prfile2/pdm/prfile2_pdm_disk.hpp>
|
||||
#include <vapours/prfile2/pdm/prfile2_pdm_partition.hpp>
|
||||
|
||||
namespace ams::usb {
|
||||
namespace ams::prfile2::pdm {
|
||||
|
||||
class RemoteDsEndpoint {
|
||||
private:
|
||||
Service m_srv;
|
||||
public:
|
||||
RemoteDsEndpoint(Service &srv) : m_srv(srv) { /* ... */ }
|
||||
virtual ~RemoteDsEndpoint() { serviceClose(std::addressof(m_srv)); }
|
||||
public:
|
||||
Result PostBufferAsync(sf::Out<u32> out_urb_id, u64 address, u32 size);
|
||||
Result Cancel();
|
||||
Result GetCompletionEvent(sf::OutCopyHandle out);
|
||||
Result GetUrbReport(sf::Out<usb::UrbReport> out);
|
||||
Result Stall();
|
||||
Result SetZlt(bool zlt);
|
||||
struct PartitionHolder {
|
||||
u32 signature;
|
||||
Partition *partition;
|
||||
};
|
||||
|
||||
struct DiskHolder {
|
||||
u32 signature;
|
||||
Disk *disk;
|
||||
};
|
||||
|
||||
struct DiskSet {
|
||||
u16 num_partitions;
|
||||
u16 num_allocated_disks;
|
||||
DiskHolder disk_holders[MaxDisks];
|
||||
PartitionHolder partition_holders[MaxPartitions];
|
||||
Disk disks[MaxDisks];
|
||||
Partition partitions[MaxPartitions];
|
||||
};
|
||||
static_assert(ds::IsIDsEndpoint<RemoteDsEndpoint>);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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/prfile2/prfile2_handle.hpp>
|
||||
#include <vapours/prfile2/pdm/prfile2_pdm_common.hpp>
|
||||
|
||||
namespace ams::prfile2::pdm {
|
||||
|
||||
struct Partition {
|
||||
u32 status;
|
||||
HandleType disk_handle;
|
||||
u32 signature;
|
||||
u16 partition_id;
|
||||
u16 open_count;
|
||||
Partition *lock_handle;
|
||||
u32 start_sector;
|
||||
u32 total_sector;
|
||||
u32 mbr_sector;
|
||||
u8 partition_type;
|
||||
pdm::Error last_driver_error;
|
||||
void *volume;
|
||||
volatile NonBlockingProtocolType nbc_detect;
|
||||
PartitionEventCallback event_callback;
|
||||
void *event_callback_param;
|
||||
|
||||
template<size_t Ix>
|
||||
constexpr ALWAYS_INLINE bool GetStatusBit() const {
|
||||
constexpr u32 Mask = (1u << Ix);
|
||||
return (this->status & Mask) != 0;
|
||||
}
|
||||
|
||||
template<size_t Ix>
|
||||
constexpr ALWAYS_INLINE void SetStatusBit(bool en) {
|
||||
constexpr u32 Mask = (1u << Ix);
|
||||
if (en) {
|
||||
this->status |= Mask;
|
||||
} else {
|
||||
this->status &= ~Mask;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool IsOpen() const { return this->GetStatusBit<0>(); }
|
||||
constexpr void SetOpen(bool en) { this->SetStatusBit<0>(en); }
|
||||
|
||||
constexpr bool IsLocked() const { return this->GetStatusBit<1>(); }
|
||||
constexpr void SetLocked(bool en) { this->SetStatusBit<1>(en); }
|
||||
};
|
||||
|
||||
namespace part {
|
||||
|
||||
pdm::Error OpenPartition(HandleType disk_handle, u16 partition_id, HandleType *out);
|
||||
pdm::Error ClosePartition(HandleType part_handle);
|
||||
|
||||
void SetDriverErrorCode(HandleType part_handle, pdm::Error err);
|
||||
|
||||
void CheckPartitionOpen(HandleType disk_handle, bool *out);
|
||||
void NotifyMediaEvent(HandleType disk_handle, u32 event);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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/prfile2/prfile2_build_config.hpp>
|
||||
#include <vapours/prfile2/prfile2_handle.hpp>
|
||||
|
||||
namespace ams::prfile2::pdm {
|
||||
|
||||
enum Error {
|
||||
Error_NoError = 0,
|
||||
Error_Ok = Error_NoError,
|
||||
|
||||
Error_InvalidParameter = 0x0001,
|
||||
Error_InvalidMasterBoot = 0x0002,
|
||||
Error_InvalidBootSector = 0x0003,
|
||||
Error_InvalidBpb = 0x0004,
|
||||
Error_NotExistMbr = 0x0005,
|
||||
Error_NotExistEpbr = 0x0006,
|
||||
Error_NotExistPartition = 0x0007,
|
||||
Error_NotExistFreeDiskStruct = 0x0008,
|
||||
Error_NotExistPartitionStruct = 0x0009,
|
||||
Error_NotExistFreePartitionStruct = 0x000A,
|
||||
Error_StateOpened = 0x000B,
|
||||
Error_StateClosed = 0x000C,
|
||||
Error_StateLocked = 0x000D,
|
||||
Error_StateUnlocked = 0x000E,
|
||||
Error_AccessPermission = 0x000F,
|
||||
Error_WriteProtected = 0x0010,
|
||||
Error_MediaEjected = 0x0011,
|
||||
Error_OutOfRange = 0x0012,
|
||||
Error_SystemCallError = 0x0013,
|
||||
Error_LockError = 0x0014,
|
||||
Error_DriverError = 0x0015,
|
||||
Error_UnsupportDiskFormat = 0x0016,
|
||||
};
|
||||
|
||||
using NonBlockingProtocolType = int;
|
||||
|
||||
using DiskCallback = void (*)();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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/prfile2/pdm/prfile2_pdm_types.hpp>
|
||||
#include <vapours/prfile2/pdm/prfile2_pdm_common.hpp>
|
||||
#include <vapours/prfile2/pdm/prfile2_pdm_disk_management.hpp>
|
||||
|
||||
namespace ams::prfile2::pdm {
|
||||
|
||||
namespace part {
|
||||
|
||||
pdm::Error CheckDataEraseRequest(HandleType part_handle, bool *out);
|
||||
pdm::Error CheckMediaDetect(HandleType part_handle, bool *out);
|
||||
pdm::Error CheckMediaInsert(HandleType part_handle, bool *out);
|
||||
|
||||
}
|
||||
|
||||
namespace disk {
|
||||
|
||||
pdm::Error CheckDataEraseRequest(HandleType disk_handle, bool *out);
|
||||
pdm::Error CheckMediaDetect(HandleType disk_handle, bool *out);
|
||||
pdm::Error CheckMediaInsert(HandleType disk_handle, bool *out);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,15 +14,14 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_fixed_tree.hpp>
|
||||
#include <vapours/prfile2/pf/prfile2_pf_config.hpp>
|
||||
#include <vapours/prfile2/pf/prfile2_pf_types.hpp>
|
||||
#include <vapours/prfile2/prfile2_critical_section.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
namespace ams::prfile2::pf {
|
||||
|
||||
template<typename Element, typename Compare = std::less<Element>, size_t BufferAlignment = 8>
|
||||
class FixedSet : public ::ams::util::FixedTree<Element, Compare, const Element, BufferAlignment> {
|
||||
/* ... */
|
||||
};
|
||||
int Initialize(u32 config, void *param);
|
||||
|
||||
int Attach(DriveTable **drive_table);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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/prfile2/prfile2_build_config.hpp>
|
||||
|
||||
namespace ams::prfile2::pf {
|
||||
|
||||
constexpr inline const auto MaximumFileCount = 256;
|
||||
constexpr inline const auto MaximumDirectoryCount = 32;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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/prfile2/prfile2_build_config.hpp>
|
||||
#include <vapours/prfile2/prfile2_handle.hpp>
|
||||
|
||||
namespace ams::prfile2::pf {
|
||||
|
||||
using DriveCharacter = char;
|
||||
|
||||
enum Error {
|
||||
Error_NoError = 0,
|
||||
Error_Ok = Error_NoError,
|
||||
|
||||
Error_Generic = -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;
|
||||
constexpr inline const int ReturnValueError = -1;
|
||||
|
||||
constexpr inline int ConvertReturnValue(Error err) {
|
||||
if (AMS_LIKELY(err == Error_NoError)) {
|
||||
return ReturnValueNoError;
|
||||
} else {
|
||||
return ReturnValueError;
|
||||
}
|
||||
}
|
||||
|
||||
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<CachePage>::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;
|
||||
|
||||
template<size_t Ix>
|
||||
constexpr ALWAYS_INLINE bool GetStatusBit() const {
|
||||
constexpr u32 Mask = (1u << Ix);
|
||||
return (this->status & Mask) != 0;
|
||||
}
|
||||
|
||||
template<size_t Ix>
|
||||
constexpr ALWAYS_INLINE void SetStatusBit(bool en) {
|
||||
constexpr u32 Mask = (1u << Ix);
|
||||
if (en) {
|
||||
this->status |= Mask;
|
||||
} else {
|
||||
this->status &= ~Mask;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool IsAttached() const { return this->GetStatusBit<0>(); }
|
||||
constexpr void SetAttached(bool en) { this->SetStatusBit<0>(en); }
|
||||
|
||||
constexpr bool IsMounted() const { return this->GetStatusBit<1>(); }
|
||||
constexpr void SetMounted(bool en) { this->SetStatusBit<1>(en); }
|
||||
|
||||
constexpr bool IsDiskInserted() const { return this->GetStatusBit<2>(); }
|
||||
constexpr void SetDiskInserted(bool en) { this->SetStatusBit<2>(en); }
|
||||
|
||||
constexpr bool IsFlag12() const { return this->GetStatusBit<12>(); }
|
||||
constexpr void SetFlag12(bool en) { this->SetStatusBit<12>(en); }
|
||||
};
|
||||
|
||||
using TailBuf = u32;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/svc.hpp>
|
||||
#include <vapours/dd.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_IS_EXOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
|
||||
//#define AMS_PRFILE2_THREAD_SAFE
|
||||
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
|
||||
//#define AMS_PRFILE2_THREAD_SAFE
|
||||
|
||||
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
|
||||
#define AMS_PRFILE2_THREAD_SAFE
|
||||
|
||||
#else
|
||||
#error "Unknown execution context for ams::prfile2!"
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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/prfile2/prfile2_common.hpp>
|
||||
|
||||
namespace ams::prfile2 {
|
||||
|
||||
constexpr inline const auto MinimumFatBufferSize = 1;
|
||||
constexpr inline const auto MaximumFatBufferSize = (1 << 16) - 1;
|
||||
constexpr inline const auto MinimumDataBufferSize = 1;
|
||||
constexpr inline const auto MaximumDataBufferSize = (1 << 15) - 1;
|
||||
|
||||
constexpr inline const auto MinimumFatPages = 1;
|
||||
constexpr inline const auto MinimumDataPages = 4;
|
||||
|
||||
struct SectorCache {
|
||||
u32 mode;
|
||||
u16 num_fat_pages;
|
||||
u16 num_data_pages;
|
||||
pf::CachePage *pages;
|
||||
pf::CachePage *current_fat;
|
||||
pf::CachePage *current_data;
|
||||
pf::SectorBuffer *buffers;
|
||||
u32 fat_buf_size;
|
||||
u32 data_buf_size;
|
||||
void *signature;
|
||||
};
|
||||
|
||||
struct Volume;
|
||||
|
||||
}
|
||||
|
||||
namespace ams::prfile2::cache {
|
||||
|
||||
void SetCache(Volume *vol, pf::CachePage *cache_page, pf::SectorBuffer *cache_buf, u16 num_fat_pages, u16 num_data_pages);
|
||||
void SetFatBufferSize(Volume *vol, u32 size);
|
||||
void SetDataBufferSize(Volume *vol, u32 size);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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/prfile2/prfile2_build_config.hpp>
|
||||
#include <vapours/prfile2/pf/prfile2_pf_config.hpp>
|
||||
#include <vapours/prfile2/pf/prfile2_pf_api_common.hpp>
|
||||
#include <vapours/prfile2/prfile2_wide_string.hpp>
|
||||
|
||||
namespace ams::prfile2 {
|
||||
|
||||
constexpr inline const auto MaximumOpenFileCountSystem = pf::MaximumFileCount;
|
||||
constexpr inline const auto MaximumOpenFileCountUser = pf::MaximumFileCount;
|
||||
constexpr inline const auto MaximumOpenDirectoryCountSystem = pf::MaximumDirectoryCount;
|
||||
constexpr inline const auto MaximumOpenDirectoryCountUser = pf::MaximumDirectoryCount;
|
||||
|
||||
enum Error {
|
||||
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_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<u32>::max();
|
||||
constexpr inline const u32 InvalidCluster = std::numeric_limits<u32>::max();
|
||||
|
||||
enum OpenMode {
|
||||
OpenMode_None = 0,
|
||||
OpenMode_Write = (1u << 0),
|
||||
OpenMode_Read = (1u << 1),
|
||||
OpenMode_Append = (1u << 2),
|
||||
OpenMode_Plus = (1u << 3),
|
||||
OpenMode_NoOverwrite = (1u << 4),
|
||||
OpenMode_V = (1u << 5),
|
||||
OpenMode_ContCluster = (1u << 6),
|
||||
};
|
||||
|
||||
enum FatFormat {
|
||||
FatFormat_Fat12 = 0,
|
||||
FatFormat_Fat16 = 1,
|
||||
FatFormat_Fat32 = 2,
|
||||
FatFormat_ExFat = 3,
|
||||
|
||||
FatFormat_Invalid = -1,
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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/prfile2/pf/prfile2_pf_config.hpp>
|
||||
|
||||
namespace ams::prfile2 {
|
||||
|
||||
struct CriticalSection {
|
||||
enum State {
|
||||
State_NotInitialized = 0,
|
||||
State_Initialized = 1,
|
||||
};
|
||||
|
||||
struct Resource;
|
||||
|
||||
u8 state;
|
||||
int lock_count;
|
||||
Resource *resource;
|
||||
u64 owner;
|
||||
};
|
||||
|
||||
void InitializeCriticalSection(CriticalSection *cs);
|
||||
void FinalizeCriticalSection(CriticalSection *cs);
|
||||
|
||||
void EnterCriticalSection(CriticalSection *cs);
|
||||
void ExitCriticalSection(CriticalSection *cs);
|
||||
|
||||
class ScopedCriticalSection {
|
||||
private:
|
||||
CriticalSection *cs;
|
||||
public:
|
||||
ALWAYS_INLINE ScopedCriticalSection(CriticalSection *c) : cs(c) { EnterCriticalSection(this->cs); }
|
||||
ALWAYS_INLINE ScopedCriticalSection(CriticalSection &c) : ScopedCriticalSection(std::addressof(c)) { /* ... */ }
|
||||
|
||||
ALWAYS_INLINE ~ScopedCriticalSection() { ExitCriticalSection(this->cs); }
|
||||
};
|
||||
|
||||
}
|
||||
32
libraries/libvapours/include/vapours/prfile2/prfile2_drv.hpp
Normal file
32
libraries/libvapours/include/vapours/prfile2/prfile2_drv.hpp
Normal 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/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/prfile2/prfile2_common.hpp>
|
||||
|
||||
namespace ams::prfile2 {
|
||||
|
||||
struct Volume;
|
||||
|
||||
}
|
||||
|
||||
namespace ams::prfile2::drv {
|
||||
|
||||
pf::Error Initialize(Volume *volume);
|
||||
|
||||
bool IsDetected(Volume *volume);
|
||||
bool IsInserted(Volume *volume);
|
||||
|
||||
}
|
||||
@@ -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/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/prfile2/prfile2_common.hpp>
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
64
libraries/libvapours/include/vapours/prfile2/prfile2_fat.hpp
Normal file
64
libraries/libvapours/include/vapours/prfile2/prfile2_fat.hpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/prfile2/prfile2_common.hpp>
|
||||
|
||||
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;
|
||||
/* ... */
|
||||
};
|
||||
|
||||
}
|
||||
@@ -14,8 +14,10 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/prfile2/prfile2_common.hpp>
|
||||
|
||||
#include <stratosphere/usb/usb_limits.hpp>
|
||||
#include <stratosphere/usb/usb_types.hpp>
|
||||
#include <stratosphere/usb/usb_device_types.hpp>
|
||||
#include <stratosphere/usb/usb_device.hpp>
|
||||
namespace ams::prfile2::fatfs {
|
||||
|
||||
pf::Error Initialize(u32 config, void *param);
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/prfile2/prfile2_build_config.hpp>
|
||||
|
||||
namespace ams::prfile2 {
|
||||
|
||||
using HandleType = util::BitPack32;
|
||||
|
||||
struct HandleField {
|
||||
using Id = util::BitPack32::Field< 0, 8>;
|
||||
using Kind = util::BitPack32::Field< 8, 8>;
|
||||
using Signature = util::BitPack32::Field<16, 16>;
|
||||
};
|
||||
|
||||
enum HandleKind {
|
||||
HandleKind_Disk = 3,
|
||||
HandleKind_Partition = 4,
|
||||
};
|
||||
|
||||
constexpr ALWAYS_INLINE HandleType ConstructHandle(u8 id, HandleKind kind, u16 signature) {
|
||||
/* Construct the handle. */
|
||||
HandleType val = {};
|
||||
val.Set<HandleField::Id>(id);
|
||||
val.Set<HandleField::Kind>(kind);
|
||||
val.Set<HandleField::Signature>(signature);
|
||||
return val;
|
||||
};
|
||||
|
||||
constexpr ALWAYS_INLINE u8 GetHandleId(HandleType handle) {
|
||||
return handle.Get<HandleField::Id>();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE u16 GetHandleSignature(HandleType handle) {
|
||||
return handle.Get<HandleField::Signature>();
|
||||
}
|
||||
|
||||
constexpr inline const HandleType InvalidHandle = {};
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsInvalidHandle(HandleType handle) {
|
||||
return handle.value == 0;
|
||||
}
|
||||
static_assert(IsInvalidHandle(InvalidHandle));
|
||||
|
||||
constexpr ALWAYS_INLINE HandleType ConstructDiskHandle(u8 id, u16 signature) { return ConstructHandle(id, HandleKind_Disk, signature); }
|
||||
constexpr ALWAYS_INLINE HandleType ConstructPartitionHandle(u8 id, u16 signature) { return ConstructHandle(id, HandleKind_Partition, signature); }
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsDiskHandle(HandleType handle) { return handle.Get<HandleField::Kind>() == HandleKind_Disk; }
|
||||
constexpr ALWAYS_INLINE bool IsPartitionHandle(HandleType handle) { return handle.Get<HandleField::Kind>() == HandleKind_Partition; }
|
||||
|
||||
}
|
||||
65
libraries/libvapours/include/vapours/prfile2/prfile2_str.hpp
Normal file
65
libraries/libvapours/include/vapours/prfile2/prfile2_str.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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/prfile2/prfile2_common.hpp>
|
||||
|
||||
namespace ams::prfile2::str {
|
||||
|
||||
enum CodeMode {
|
||||
CodeMode_Invalid = 0,
|
||||
CodeMode_Local = 1,
|
||||
CodeMode_Unicode = 2,
|
||||
};
|
||||
|
||||
enum TargetString {
|
||||
TargetString_Head = 1,
|
||||
TargetString_Tail = 2,
|
||||
};
|
||||
|
||||
struct String {
|
||||
const char *head;
|
||||
const char *tail;
|
||||
CodeMode code_mode;
|
||||
};
|
||||
|
||||
pf::Error Initialize(String *str, const char *s, CodeMode code_mode);
|
||||
|
||||
void SetCodeMode(String *str, CodeMode code_mode);
|
||||
CodeMode GetCodeMode(const String *str);
|
||||
|
||||
char *GetPos(String *str, TargetString target);
|
||||
void MovePos(String *str, s16 num_char);
|
||||
|
||||
u16 GetLength(String *str);
|
||||
u16 GetNumChar(String *str, TargetString target);
|
||||
|
||||
int Compare(const String *str, const char *rhs);
|
||||
int Compare(const String *str, const WideChar *rhs);
|
||||
|
||||
/* TODO: StrNCmp */
|
||||
/* TODO: ToUpperNStr */
|
||||
|
||||
constexpr bool IsNull(const String *str) {
|
||||
return str->head == nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ams::prfile2 {
|
||||
|
||||
using String = str::String;
|
||||
|
||||
}
|
||||
@@ -14,11 +14,12 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_module.hpp"
|
||||
#include <vapours/prfile2/prfile2_common.hpp>
|
||||
|
||||
namespace ams::mitm::uart {
|
||||
namespace ams::prfile2::system {
|
||||
|
||||
DEFINE_MITM_MODULE_CLASS(0x4000, AMS_GET_SYSTEM_THREAD_PRIORITY(uart, IpcServer) - 1);
|
||||
void Initialize();
|
||||
|
||||
int GetCurrentContextId(u64 *out);
|
||||
|
||||
}
|
||||
238
libraries/libvapours/include/vapours/prfile2/prfile2_volume.hpp
Normal file
238
libraries/libvapours/include/vapours/prfile2/prfile2_volume.hpp
Normal file
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* 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/prfile2/prfile2_common.hpp>
|
||||
#include <vapours/prfile2/prfile2_cache.hpp>
|
||||
#include <vapours/prfile2/prfile2_critical_section.hpp>
|
||||
#include <vapours/prfile2/prfile2_drv.hpp>
|
||||
#include <vapours/prfile2/prfile2_entry.hpp>
|
||||
#include <vapours/prfile2/prfile2_fat.hpp>
|
||||
|
||||
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;
|
||||
};
|
||||
};
|
||||
|
||||
namespace pdm {
|
||||
|
||||
struct Partition;
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
/* ... */
|
||||
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;
|
||||
/* ... */
|
||||
HandleType partition_handle;
|
||||
VolumeCallback callback;
|
||||
const u8 *format_param;
|
||||
/* ... */
|
||||
/* TODO: ExtensionTable extension_table; */
|
||||
/* TODO: ExtensionData ext; */
|
||||
/* ... */
|
||||
|
||||
template<size_t Ix>
|
||||
constexpr ALWAYS_INLINE bool GetFlagsBit() const {
|
||||
constexpr u32 Mask = (1u << Ix);
|
||||
return (this->flags & Mask) != 0;
|
||||
}
|
||||
|
||||
template<size_t Ix>
|
||||
constexpr ALWAYS_INLINE void SetFlagsBit(bool en) {
|
||||
constexpr u32 Mask = (1u << Ix);
|
||||
if (en) {
|
||||
this->flags |= Mask;
|
||||
} else {
|
||||
this->flags &= ~Mask;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool IsAttached() const { return this->GetFlagsBit<0>(); }
|
||||
constexpr void SetAttached(bool en) { this->SetFlagsBit<0>(en); }
|
||||
|
||||
constexpr bool IsMounted() const { return this->GetFlagsBit<1>(); }
|
||||
constexpr void SetMounted(bool en) { this->SetFlagsBit<1>(en); }
|
||||
|
||||
constexpr bool IsFormatAfterMountRequested() const { return this->GetFlagsBit<4>(); }
|
||||
constexpr void SetFormatAfterMountRequested(bool en) { this->SetFlagsBit<4>(en); }
|
||||
|
||||
constexpr bool IsNoFormattingByFatFsLayer() const { return this->GetFlagsBit<5>(); }
|
||||
constexpr void SetNoFormattingByFatFsLayer(bool en) { this->SetFlagsBit<5>(en); }
|
||||
|
||||
constexpr bool IsDataEraseRequested() const { return this->GetFlagsBit<6>(); }
|
||||
constexpr void SetDataEraseRequested(bool en) { this->SetFlagsBit<6>(en); }
|
||||
|
||||
constexpr bool IsFlag12() const { return this->GetFlagsBit<12>(); }
|
||||
constexpr void SetFlag12(bool en) { this->SetFlagsBit<12>(en); }
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
@@ -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/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/prfile2/prfile2_common.hpp>
|
||||
|
||||
namespace ams::prfile2 {
|
||||
|
||||
using WideChar = u16;
|
||||
|
||||
size_t w_strlen(const WideChar *s);
|
||||
size_t w_strnlen(const WideChar *s, size_t length);
|
||||
|
||||
WideChar *w_strcpy(WideChar *dst, const WideChar *src);
|
||||
WideChar *w_strncpy(WideChar *dst, const WideChar *src, size_t length);
|
||||
|
||||
int w_strcmp(const WideChar *lhs, const WideChar *rhs);
|
||||
int w_strncmp(const WideChar *lhs, const WideChar *rhs, size_t length);
|
||||
|
||||
}
|
||||
@@ -59,7 +59,6 @@
|
||||
#include <vapours/results/svc_results.hpp>
|
||||
#include <vapours/results/time_results.hpp>
|
||||
#include <vapours/results/updater_results.hpp>
|
||||
#include <vapours/results/usb_results.hpp>
|
||||
#include <vapours/results/vi_results.hpp>
|
||||
|
||||
/* Unofficial. */
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* 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/results/results_common.hpp>
|
||||
|
||||
namespace ams::usb {
|
||||
|
||||
R_DEFINE_NAMESPACE_RESULT_MODULE(140);
|
||||
|
||||
R_DEFINE_ERROR_RESULT(NotInitialized, 0);
|
||||
R_DEFINE_ERROR_RESULT(AlreadyInitialized, 1);
|
||||
|
||||
R_DEFINE_ERROR_RANGE(InvalidParameter, 100, 199);
|
||||
R_DEFINE_ERROR_RESULT(AlignmentError, 103);
|
||||
|
||||
R_DEFINE_ERROR_RESULT(OperationDenied, 201);
|
||||
R_DEFINE_ERROR_RESULT(MemAllocFailure, 202);
|
||||
R_DEFINE_ERROR_RESULT(ResourceBusy, 206);
|
||||
R_DEFINE_ERROR_RESULT(InternalStateError, 207);
|
||||
|
||||
R_DEFINE_ERROR_RESULT(TransactionError, 401);
|
||||
R_DEFINE_ERROR_RESULT(Interrupted, 409);
|
||||
|
||||
}
|
||||
@@ -46,9 +46,6 @@
|
||||
#include <vapours/util/util_format_string.hpp>
|
||||
#include <vapours/util/util_range.hpp>
|
||||
|
||||
#include <vapours/util/util_fixed_map.hpp>
|
||||
#include <vapours/util/util_fixed_set.hpp>
|
||||
|
||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||
#include <vapours/util/util_mutex_utils.hpp>
|
||||
#endif
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
/*
|
||||
* 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/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_fourcc.hpp>
|
||||
|
||||
namespace ams::util::impl {
|
||||
|
||||
class AvailableIndexFinder {
|
||||
private:
|
||||
static constexpr int MaxDepthOfBox = 5;
|
||||
|
||||
struct BitFlags64 {
|
||||
private:
|
||||
u64 m_data;
|
||||
public:
|
||||
constexpr ALWAYS_INLINE bool GetFlag(int index) const {
|
||||
AMS_ASSERT(index < 64);
|
||||
return ((m_data >> index) & UINT64_C(1)) != 0;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void SetFlag(int index) {
|
||||
AMS_ASSERT(index < 64);
|
||||
m_data |= (UINT64_C(1) << index);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void ClearFlag(int index) {
|
||||
AMS_ASSERT(index < 64);
|
||||
m_data &= ~(UINT64_C(1) << index);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsFull() const {
|
||||
return m_data == ~(UINT64_C(0));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE int FindIndexOfBitZero() const {
|
||||
AMS_ASSERT(!this->IsFull());
|
||||
return __builtin_ctzll(~m_data);
|
||||
}
|
||||
};
|
||||
private:
|
||||
int *m_p_current_index;
|
||||
int *m_p_map_index;
|
||||
void *m_buffer;
|
||||
int m_depth;
|
||||
BitFlags64 *m_flags;
|
||||
private:
|
||||
static constexpr int Pow64(int e) {
|
||||
switch (e) {
|
||||
case 0: return 0x1;
|
||||
case 1: return 0x40;
|
||||
case 2: return 0x1000;
|
||||
case 3: return 0x40000;
|
||||
case 4: return 0x1000000;
|
||||
case 5: return 0x40000000;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr u64 Roundup64(u64 value) {
|
||||
return (value + (64 - 1)) / 64;
|
||||
}
|
||||
|
||||
static constexpr size_t GetRequiredNumOfBox(int depth, size_t num_elements) {
|
||||
if (depth == 1) {
|
||||
return Roundup64(num_elements);
|
||||
} else if (depth == 2) {
|
||||
return Roundup64(num_elements) + Pow64(0);
|
||||
} else if (depth == 3) {
|
||||
return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2);
|
||||
} else if (depth == 4) {
|
||||
return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2) + Pow64(3);
|
||||
} else if (depth == 5) {
|
||||
return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2) + Pow64(3) + Pow64(4);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr int CalcOffset(int *arr, int depth) {
|
||||
int offset = 0;
|
||||
for (auto i = 0; i < depth; ++i) {
|
||||
offset += Pow64(i);
|
||||
}
|
||||
for (auto i = 0; i < depth; ++i) {
|
||||
offset += Pow64(i) - arr[depth - 1 - i];
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
public:
|
||||
static consteval int GetSignature() {
|
||||
return static_cast<int>(util::ReverseFourCC<'B', 'I', 'T', 'S'>::Code);
|
||||
}
|
||||
|
||||
static constexpr int GetNeedDepth(size_t num_elements) {
|
||||
if (num_elements <= 0x40) {
|
||||
return 1;
|
||||
} else if (num_elements <= 0x1000) {
|
||||
return 2;
|
||||
} else if (num_elements <= 0x40000) {
|
||||
return 3;
|
||||
} else if (num_elements <= 0x1000000) {
|
||||
return 4;
|
||||
} else if (num_elements <= 0x40000000) {
|
||||
return 5;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr size_t GetRequiredMemorySize(size_t num_elements) {
|
||||
return sizeof(BitFlags64) * GetRequiredNumOfBox(GetNeedDepth(num_elements), num_elements);
|
||||
}
|
||||
public:
|
||||
void Initialize(int *cur, int *map, u8 *buf) {
|
||||
const size_t num_elements = static_cast<size_t>(*map);
|
||||
AMS_ASSERT(num_elements <= static_cast<size_t>(Pow64(MaxDepthOfBox - 1)));
|
||||
|
||||
/* Set fields. */
|
||||
m_p_current_index = cur;
|
||||
m_p_map_index = map;
|
||||
m_buffer = buf;
|
||||
m_depth = GetNeedDepth(num_elements);
|
||||
|
||||
/* Validate fields. */
|
||||
AMS_ASSERT(m_depth > 0);
|
||||
|
||||
/* Setup memory. */
|
||||
std::memset(m_buffer, 0, GetRequiredMemorySize(num_elements));
|
||||
m_flags = reinterpret_cast<BitFlags64 *>(m_buffer);
|
||||
}
|
||||
|
||||
int AcquireIndex() {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(*m_p_current_index < *m_p_map_index);
|
||||
|
||||
/* Build up arrays. */
|
||||
int table[MaxDepthOfBox];
|
||||
BitFlags64 *pos[MaxDepthOfBox];
|
||||
for (auto i = 0; i < m_depth; ++i) {
|
||||
/* Determine the position. */
|
||||
pos[i] = std::addressof(m_flags[CalcOffset(table, i)]);
|
||||
|
||||
/* Set table entry. */
|
||||
table[i] = pos[i]->FindIndexOfBitZero();
|
||||
AMS_ASSERT(table[i] != BITSIZEOF(u64));
|
||||
}
|
||||
|
||||
/* Validate that the index is not acquired. */
|
||||
AMS_ASSERT(!pos[m_depth - 1]->GetFlag(table[m_depth - 1]));
|
||||
|
||||
/* Acquire the index. */
|
||||
pos[m_depth - 1]->SetFlag(table[m_depth - 1]);
|
||||
|
||||
/* Validate that the index was acquired. */
|
||||
AMS_ASSERT(pos[m_depth - 1]->GetFlag(table[m_depth - 1]));
|
||||
|
||||
/* Update tracking flags. */
|
||||
for (auto i = m_depth - 1; i > 0; --i) {
|
||||
if (pos[i]->IsFull()) {
|
||||
pos[i - 1]->SetFlag(table[i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate the index we acquired. */
|
||||
int index = 0, pow = 0;
|
||||
for (auto i = m_depth; i > 0; --i, ++pow) {
|
||||
index += Pow64(pow) * table[i - 1];
|
||||
}
|
||||
|
||||
/* Increment current index. */
|
||||
++(*m_p_current_index);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void ReleaseIndex(int index) {
|
||||
/* Convert index to table. */
|
||||
int table[MaxDepthOfBox];
|
||||
for (auto i = 0; i < m_depth; ++i) {
|
||||
table[m_depth - 1 - i] = index % BITSIZEOF(u64);
|
||||
index /= BITSIZEOF(u64);
|
||||
}
|
||||
|
||||
/* Build up arrays. */
|
||||
BitFlags64 *pos[MaxDepthOfBox];
|
||||
for (auto i = 0; i < m_depth; ++i) {
|
||||
/* Determine the position. */
|
||||
pos[i] = std::addressof(m_flags[CalcOffset(table, i)]);
|
||||
}
|
||||
|
||||
/* Validate that the flag is set. */
|
||||
AMS_ASSERT(pos[m_depth - 1]->GetFlag(table[m_depth - 1]));
|
||||
|
||||
/* Clear the flags. */
|
||||
for (auto i = m_depth - 1; i >= 0; --i) {
|
||||
pos[i]->ClearFlag(table[i]);
|
||||
}
|
||||
|
||||
/* Decrement current index. */
|
||||
--(*m_p_current_index);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -75,6 +75,14 @@ namespace ams::util {
|
||||
this->value &= ~FieldMask;
|
||||
this->value |= (static_cast<IntegralStorageType>(field_value) << FieldType::Index) & FieldMask;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator==(const BitPack &rhs) {
|
||||
return this->value == rhs.value;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator!=(const BitPack &rhs) {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* 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/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_fixed_tree.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<typename Key, typename Value, typename Compare = std::less<Key>, size_t BufferAlignment = 8>
|
||||
class FixedMap {
|
||||
private:
|
||||
using KeyValuePair = std::pair<Key const, Value>;
|
||||
|
||||
struct LessTypeForMap {
|
||||
constexpr ALWAYS_INLINE bool operator()(const KeyValuePair &lhs, const KeyValuePair &rhs) const {
|
||||
return Compare{}(lhs.first, rhs.first);
|
||||
}
|
||||
};
|
||||
|
||||
using TreeType = ::ams::util::FixedTree<KeyValuePair, LessTypeForMap, KeyValuePair, BufferAlignment>;
|
||||
|
||||
using iterator = typename TreeType::iterator;
|
||||
using const_iterator = typename TreeType::const_iterator;
|
||||
public:
|
||||
static constexpr size_t GetRequiredMemorySize(size_t num_elements) {
|
||||
return TreeType::GetRequiredMemorySize(num_elements);
|
||||
}
|
||||
private:
|
||||
TreeType m_tree;
|
||||
public:
|
||||
FixedMap() : m_tree() { /* ... */ }
|
||||
|
||||
void Initialize(size_t num_elements, void *buffer, size_t buffer_size) {
|
||||
return m_tree.Initialize(num_elements, buffer, buffer_size);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE iterator begin() { return m_tree.begin(); }
|
||||
ALWAYS_INLINE const_iterator begin() const { return m_tree.begin(); }
|
||||
|
||||
ALWAYS_INLINE iterator end() { return m_tree.end(); }
|
||||
ALWAYS_INLINE const_iterator end() const { return m_tree.end(); }
|
||||
|
||||
ALWAYS_INLINE bool erase(const Key &key) { const KeyValuePair pair(key, Value{}); return m_tree.erase(pair); }
|
||||
|
||||
ALWAYS_INLINE iterator find(const Key &key) { const KeyValuePair pair(key, Value{}); return m_tree.find(pair); }
|
||||
ALWAYS_INLINE const_iterator find(const Key &key) const { const KeyValuePair pair(key, Value{}); return m_tree.find(pair); }
|
||||
|
||||
ALWAYS_INLINE std::pair<iterator, bool> insert(const KeyValuePair &pair) { return m_tree.insert(pair); }
|
||||
|
||||
ALWAYS_INLINE size_t size() const { return m_tree.size(); }
|
||||
|
||||
ALWAYS_INLINE void clear() { return m_tree.clear(); }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,998 +0,0 @@
|
||||
/*
|
||||
* 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/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/impl/util_available_index_finder.hpp>
|
||||
#include <vapours/util/util_alignment.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<typename Member, typename Compare, typename IteratorMember, size_t BufferAlignment = 8> requires std::convertible_to<Member &, IteratorMember &>
|
||||
class FixedTree {
|
||||
private:
|
||||
class IteratorBase;
|
||||
friend class IteratorBase;
|
||||
private:
|
||||
enum class Color : u8 {
|
||||
Red = 0,
|
||||
Black = 1,
|
||||
};
|
||||
|
||||
static constexpr inline int Index_Nil = -1;
|
||||
static constexpr inline int Index_Leaf = -2;
|
||||
static constexpr inline int Index_BeforeBegin = -3;
|
||||
static constexpr inline int Index_AfterEnd = -4;
|
||||
|
||||
static constexpr inline size_t max_size = 0x40000000;
|
||||
|
||||
struct Header {
|
||||
/* "Nintendo Red-Black tree" */
|
||||
static constexpr u32 Signature = util::ReverseFourCC<'N','N','R','B'>::Code;
|
||||
|
||||
u32 header_size;
|
||||
u32 header_signature;
|
||||
u32 _08;
|
||||
s32 max_elements;
|
||||
s32 cur_elements;
|
||||
s32 root_index;
|
||||
s32 left_most_index;
|
||||
s32 right_most_index;
|
||||
s32 index_signature;
|
||||
u32 buffer_size;
|
||||
u32 node_size;
|
||||
u32 element_size;
|
||||
u32 _30;
|
||||
u32 _34;
|
||||
u32 _38;
|
||||
u32 _3C;
|
||||
u32 _40;
|
||||
u32 _44;
|
||||
u32 _48;
|
||||
u32 _4C;
|
||||
|
||||
void InitializeHeader(u32 _08, s32 max_e, s32 cur_e, u32 ind_sig, u32 buf_sz, u32 node_sz, u32 e_sz, u32 _30, u32 _34, u32 _38, u32 _3C, u32 _40, u32 _44) {
|
||||
this->header_size = sizeof(Header);
|
||||
this->header_signature = Signature;
|
||||
this->_08 = _08;
|
||||
this->max_elements = max_e;
|
||||
this->cur_elements = cur_e;
|
||||
this->root_index = Index_Nil;
|
||||
this->left_most_index = Index_Nil;
|
||||
this->right_most_index = Index_Nil;
|
||||
this->index_signature = ind_sig;
|
||||
this->buffer_size = buf_sz;
|
||||
this->node_size = node_sz;
|
||||
this->element_size = e_sz;
|
||||
this->_30 = _30;
|
||||
this->_34 = _34;
|
||||
this->_38 = _38;
|
||||
this->_3C = _3C;
|
||||
this->_40 = _40;
|
||||
this->_44 = _44;
|
||||
this->_48 = 0;
|
||||
this->_4C = 0;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Header) == 0x50);
|
||||
|
||||
struct IndexPair {
|
||||
int first;
|
||||
int last;
|
||||
};
|
||||
|
||||
struct Node {
|
||||
Member m_data;
|
||||
int m_parent;
|
||||
int m_right;
|
||||
int m_left;
|
||||
Color m_color;
|
||||
|
||||
void SetLeft(int l, Node *n, int p) {
|
||||
m_left = l;
|
||||
n->m_parent = p;
|
||||
}
|
||||
|
||||
void SetRight(int r, Node *n, int p) {
|
||||
m_right = r;
|
||||
n->m_parent = p;
|
||||
}
|
||||
};
|
||||
|
||||
class Iterator;
|
||||
class ConstIterator;
|
||||
|
||||
class IteratorBase {
|
||||
private:
|
||||
friend class ConstIterator;
|
||||
private:
|
||||
const FixedTree *m_this;
|
||||
int m_index;
|
||||
protected:
|
||||
constexpr ALWAYS_INLINE IteratorBase(const FixedTree *tree, int index) : m_this(tree), m_index(index) { /* ... */ }
|
||||
|
||||
constexpr bool IsEqualImpl(const IteratorBase &rhs) const {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_this);
|
||||
|
||||
/* Check for tree equality. */
|
||||
if (m_this != rhs.m_this) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check for nil. */
|
||||
if (m_this->IsNil(m_index) && m_this->IsNil(rhs.m_index)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check for index equality. */
|
||||
return m_index == rhs.m_index;
|
||||
}
|
||||
|
||||
constexpr IteratorMember &DereferenceImpl() const {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_this);
|
||||
|
||||
if (!m_this->IsNil(m_index)) {
|
||||
return m_this->m_nodes[m_index].m_data;
|
||||
} else {
|
||||
AMS_ASSERT(false);
|
||||
return m_this->GetNode(std::numeric_limits<int>::max())->m_data;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IteratorBase &IncrementImpl() {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_this);
|
||||
|
||||
this->OperateIndex(true);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IteratorBase &DecrementImpl() {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_this);
|
||||
|
||||
this->OperateIndex(false);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr void OperateIndex(bool increment) {
|
||||
if (increment) {
|
||||
/* We're incrementing. */
|
||||
if (m_index == Index_BeforeBegin) {
|
||||
m_index = 0;
|
||||
} else {
|
||||
m_index = m_this->UncheckedPP(m_index);
|
||||
if (m_this->IsNil(m_index)) {
|
||||
m_index = Index_AfterEnd;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* We're decrementing. */
|
||||
if (m_index == Index_AfterEnd) {
|
||||
m_index = static_cast<int>(m_this->size()) - 1;
|
||||
} else {
|
||||
m_index = m_this->UncheckedMM(m_index);
|
||||
if (m_this->IsNil(m_index)) {
|
||||
m_index = Index_BeforeBegin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Iterator : public IteratorBase {
|
||||
public:
|
||||
constexpr ALWAYS_INLINE Iterator(const FixedTree &tree) : IteratorBase(std::addressof(tree), tree.size() ? tree.GetLMost() : Index_Leaf) { /* ... */ }
|
||||
constexpr ALWAYS_INLINE Iterator(const FixedTree &tree, int index) : IteratorBase(std::addressof(tree), index) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator(const Iterator &rhs) = default;
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator==(const Iterator &rhs) const {
|
||||
return this->IsEqualImpl(rhs);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator!=(const Iterator &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IteratorMember &operator*() const {
|
||||
return static_cast<IteratorMember &>(this->DereferenceImpl());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IteratorMember *operator->() const {
|
||||
return std::addressof(this->operator *());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator &operator++() {
|
||||
return static_cast<Iterator &>(this->IncrementImpl());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator &operator--() {
|
||||
return static_cast<Iterator &>(this->DecrementImpl());
|
||||
}
|
||||
};
|
||||
|
||||
class ConstIterator : public IteratorBase {
|
||||
public:
|
||||
constexpr ALWAYS_INLINE ConstIterator(const FixedTree &tree) : IteratorBase(std::addressof(tree), tree.size() ? tree.GetLMost() : Index_Leaf) { /* ... */ }
|
||||
constexpr ALWAYS_INLINE ConstIterator(const FixedTree &tree, int index) : IteratorBase(std::addressof(tree), index) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE ConstIterator(const ConstIterator &rhs) = default;
|
||||
constexpr ALWAYS_INLINE ConstIterator(const Iterator &rhs) : IteratorBase(rhs.m_this, rhs.m_index) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator==(const ConstIterator &rhs) const {
|
||||
return this->IsEqualImpl(rhs);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator!=(const ConstIterator &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const IteratorMember &operator*() const {
|
||||
return static_cast<const IteratorMember &>(this->DereferenceImpl());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const IteratorMember *operator->() const {
|
||||
return std::addressof(this->operator *());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE ConstIterator &operator++() {
|
||||
return static_cast<ConstIterator &>(this->IncrementImpl());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE ConstIterator &operator--() {
|
||||
return static_cast<ConstIterator &>(this->DecrementImpl());
|
||||
}
|
||||
};
|
||||
public:
|
||||
using iterator = Iterator;
|
||||
using const_iterator = ConstIterator;
|
||||
private:
|
||||
impl::AvailableIndexFinder m_index_finder;
|
||||
Node m_dummy_leaf;
|
||||
Node *m_p_dummy_leaf;
|
||||
u8 *m_buffer;
|
||||
Header *m_header;
|
||||
Node *m_nodes;
|
||||
iterator m_end_iterator;
|
||||
public:
|
||||
FixedTree() : m_end_iterator(*this, Index_Nil) {
|
||||
this->SetDummyMemory();
|
||||
}
|
||||
protected:
|
||||
void InitializeImpl(int num_elements, void *buffer, size_t buffer_size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(num_elements > 0);
|
||||
AMS_ASSERT(static_cast<size_t>(num_elements) <= max_size);
|
||||
AMS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(buffer), BufferAlignment));
|
||||
AMS_ASSERT(buffer_size == GetRequiredMemorySize(num_elements));
|
||||
|
||||
/* Set buffer. */
|
||||
m_buffer = static_cast<u8 *>(buffer);
|
||||
m_header = reinterpret_cast<Header *>(m_buffer);
|
||||
|
||||
/* Setup memory. */
|
||||
this->InitializeMemory(num_elements, buffer_size, impl::AvailableIndexFinder::GetSignature());
|
||||
|
||||
/* Check that buffer was set up correctly. */
|
||||
AMS_ASSERT(static_cast<u32>(buffer_size) == m_header->buffer_size);
|
||||
|
||||
/* Setup dummy leaf. */
|
||||
this->SetDummyMemory();
|
||||
}
|
||||
public:
|
||||
static constexpr size_t SizeOfNodes(size_t num_elements) {
|
||||
return util::AlignUp(sizeof(Node) * num_elements, BufferAlignment);
|
||||
}
|
||||
|
||||
static constexpr size_t SizeOfIndex(size_t num_elements) {
|
||||
return impl::AvailableIndexFinder::GetRequiredMemorySize(num_elements);
|
||||
}
|
||||
|
||||
static constexpr size_t GetRequiredMemorySize(size_t num_elements) {
|
||||
return sizeof(Header) + SizeOfNodes(num_elements) + SizeOfIndex(num_elements);
|
||||
}
|
||||
private:
|
||||
void SetDummyMemory() {
|
||||
m_dummy_leaf.m_color = Color::Black;
|
||||
m_dummy_leaf.m_parent = Index_Nil;
|
||||
m_dummy_leaf.m_left = Index_Leaf;
|
||||
m_dummy_leaf.m_right = Index_Leaf;
|
||||
m_p_dummy_leaf = std::addressof(m_dummy_leaf);
|
||||
}
|
||||
|
||||
void InitializeMemory(int num_elements, u32 buffer_size, u32 signature) {
|
||||
/* Initialize the header. */
|
||||
m_header->InitializeHeader(1, num_elements, 0, signature, buffer_size, sizeof(Node), sizeof(Member), 4, 4, 4, 4, 4, BufferAlignment);
|
||||
|
||||
/* Setup index finder. */
|
||||
m_index_finder.Initialize(std::addressof(m_header->cur_elements), std::addressof(m_header->max_elements), m_buffer + sizeof(*m_header) + SizeOfNodes(num_elements));
|
||||
|
||||
/* Set nodes array. */
|
||||
m_nodes = reinterpret_cast<Node *>(m_buffer + sizeof(*m_header));
|
||||
}
|
||||
|
||||
Node *GetNode(int index) const {
|
||||
if (index >= 0) {
|
||||
return m_nodes + index;
|
||||
} else {
|
||||
return m_p_dummy_leaf;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsNil(int index) const {
|
||||
return index < 0;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsLeaf(int index) const {
|
||||
return index == Index_Leaf;
|
||||
}
|
||||
|
||||
int GetRoot() const { return m_header->root_index; }
|
||||
void SetRoot(int index) {
|
||||
if (index == Index_Leaf) {
|
||||
index = Index_Nil;
|
||||
}
|
||||
|
||||
m_header->root_index = index;
|
||||
}
|
||||
|
||||
int GetLMost() const { return m_header->left_most_index; }
|
||||
void SetLMost(int index) { m_header->left_most_index = index; }
|
||||
|
||||
int GetRMost() const { return m_header->right_most_index; }
|
||||
void SetRMost(int index) { m_header->right_most_index = index; }
|
||||
|
||||
int GetParent(int index) const {
|
||||
return this->GetNode(index)->m_parent;
|
||||
}
|
||||
|
||||
int AcquireIndex() { return m_index_finder.AcquireIndex(); }
|
||||
void ReleaseIndex(int index) { return m_index_finder.ReleaseIndex(index); }
|
||||
|
||||
int EraseByIndex(int target_index) {
|
||||
/* Setup tracking variables. */
|
||||
const auto next_index = this->UncheckedPP(target_index);
|
||||
auto *target_node = this->GetNode(target_index);
|
||||
|
||||
auto a_index = Index_Leaf;
|
||||
auto *a_node = this->GetNode(a_index);
|
||||
auto b_index = Index_Leaf;
|
||||
auto *b_node = this->GetNode(b_index);
|
||||
auto cur_index = target_index;
|
||||
auto *cur_node = this->GetNode(cur_index);
|
||||
|
||||
if (cur_node->m_left == Index_Leaf) {
|
||||
a_index = cur_node->m_right;
|
||||
a_node = this->GetNode(a_index);
|
||||
|
||||
m_p_dummy_leaf->m_parent = cur_index;
|
||||
} else {
|
||||
if (cur_node->m_right == Index_Leaf) {
|
||||
a_index = cur_node->m_left;
|
||||
} else {
|
||||
cur_index = next_index;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
a_index = cur_node->m_right;
|
||||
}
|
||||
a_node = this->GetNode(a_index);
|
||||
|
||||
m_p_dummy_leaf->m_parent = cur_index;
|
||||
}
|
||||
|
||||
/* Ensure the a node is updated (redundant) */
|
||||
a_node = this->GetNode(a_index);
|
||||
|
||||
/* Update relevant metrics/links. */
|
||||
if (cur_index == target_index) {
|
||||
/* No left, but has right. */
|
||||
b_index = target_node->m_parent;
|
||||
b_node = this->GetNode(b_index);
|
||||
|
||||
if (a_index != Index_Leaf) {
|
||||
a_node->m_parent = b_index;
|
||||
}
|
||||
|
||||
if (this->GetRoot() == target_index) {
|
||||
this->SetRoot(a_index);
|
||||
} else if (b_node->m_left == target_index) {
|
||||
b_node->m_left = a_index;
|
||||
} else {
|
||||
b_node->m_right = a_index;
|
||||
}
|
||||
|
||||
if (this->GetLMost() == target_index) {
|
||||
this->SetLMost((a_index != Index_Leaf) ? this->FindMinInSubtree(a_index) : b_index);
|
||||
}
|
||||
|
||||
if (this->GetRMost() == target_index) {
|
||||
this->SetRMost((a_index != Index_Leaf) ? this->FindMaxInSubtree(a_index) : b_index);
|
||||
}
|
||||
} else {
|
||||
/* Has left or doesn't have right. */
|
||||
|
||||
/* Fix left links. */
|
||||
this->GetNode(target_node->m_left)->m_parent = cur_index;
|
||||
cur_node->m_left = target_node->m_left;
|
||||
|
||||
if (cur_index == target_node->m_right) {
|
||||
b_index = cur_index;
|
||||
b_node = this->GetNode(b_index);
|
||||
} else {
|
||||
b_index = cur_node->m_parent;
|
||||
b_node = this->GetNode(b_index);
|
||||
|
||||
if (!this->IsNil(a_index)) {
|
||||
a_node->m_parent = b_index;
|
||||
}
|
||||
|
||||
b_node->m_left = a_index;
|
||||
cur_node->m_right = target_node->m_right;
|
||||
|
||||
this->GetNode(target_node->m_right)->m_parent = cur_index;
|
||||
}
|
||||
|
||||
if (this->GetRoot() == target_index) {
|
||||
this->SetRoot(cur_index);
|
||||
} else {
|
||||
if (this->GetNode(target_node->m_parent)->m_left == target_index) {
|
||||
this->GetNode(target_node->m_parent)->m_left = cur_index;
|
||||
} else {
|
||||
this->GetNode(target_node->m_parent)->m_right = cur_index;
|
||||
}
|
||||
}
|
||||
|
||||
cur_node->m_parent = target_node->m_parent;
|
||||
std::swap(cur_node->m_color, target_node->m_color);
|
||||
}
|
||||
|
||||
/* Ensure the tree remains balanced. */
|
||||
if (target_node->m_color == Color::Black) {
|
||||
while (true) {
|
||||
if (a_index == this->GetRoot() || a_node->m_color != Color::Black) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (a_index == b_node->m_left) {
|
||||
cur_index = b_node->m_right;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
|
||||
if (cur_node->m_color == Color::Red) {
|
||||
cur_node->m_color = Color::Black;
|
||||
b_node->m_color = Color::Red;
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
||||
|
||||
this->RotateLeft(b_index);
|
||||
|
||||
cur_index = b_node->m_right;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
}
|
||||
|
||||
if (this->IsNil(cur_index)) {
|
||||
a_index = b_index;
|
||||
a_node = b_node;
|
||||
} else {
|
||||
if (this->GetNode(cur_node->m_left)->m_color != Color::Black || this->GetNode(cur_node->m_right)->m_color != Color::Black) {
|
||||
if (this->GetNode(cur_node->m_right)->m_color == Color::Black) {
|
||||
this->GetNode(cur_node->m_left)->m_color = Color::Black;
|
||||
cur_node->m_color = Color::Red;
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
||||
|
||||
this->RotateRight(cur_index);
|
||||
|
||||
cur_index = b_node->m_right;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
}
|
||||
|
||||
cur_node->m_color = b_node->m_color;
|
||||
b_node->m_color = Color::Black;
|
||||
|
||||
this->GetNode(cur_node->m_right)->m_color = Color::Black;
|
||||
|
||||
this->RotateLeft(b_index);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
cur_node->m_color = Color::Red;
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
||||
|
||||
a_index = b_index;
|
||||
a_node = b_node;
|
||||
}
|
||||
} else {
|
||||
cur_index = b_node->m_left;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
|
||||
if (cur_node->m_color == Color::Red) {
|
||||
cur_node->m_color = Color::Black;
|
||||
b_node->m_color = Color::Red;
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
||||
|
||||
this->RotateRight(b_index);
|
||||
|
||||
cur_index = b_node->m_left;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
}
|
||||
|
||||
if (this->IsNil(cur_index)) {
|
||||
a_index = b_index;
|
||||
a_node = b_node;
|
||||
} else {
|
||||
if (this->GetNode(cur_node->m_right)->m_color != Color::Black || this->GetNode(cur_node->m_left)->m_color != Color::Black) {
|
||||
if (this->GetNode(cur_node->m_left)->m_color == Color::Black) {
|
||||
this->GetNode(cur_node->m_right)->m_color = Color::Black;
|
||||
cur_node->m_color = Color::Red;
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
||||
|
||||
this->RotateLeft(cur_index);
|
||||
|
||||
cur_index = b_node->m_left;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
}
|
||||
|
||||
cur_node->m_color = b_node->m_color;
|
||||
b_node->m_color = Color::Black;
|
||||
|
||||
this->GetNode(cur_node->m_left)->m_color = Color::Black;
|
||||
|
||||
this->RotateRight(b_index);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
cur_node->m_color = Color::Red;
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
||||
|
||||
a_index = b_index;
|
||||
a_node = b_node;
|
||||
}
|
||||
}
|
||||
|
||||
b_index = a_node->m_parent;
|
||||
b_node = this->GetNode(b_index);
|
||||
}
|
||||
|
||||
a_node->m_color = Color::Black;
|
||||
}
|
||||
|
||||
/* Release the index. */
|
||||
this->ReleaseIndex(target_index);
|
||||
return target_index;
|
||||
}
|
||||
|
||||
int FindIndex(const Member &elem) const {
|
||||
return this->FindIndexSub(this->GetRoot(), elem);
|
||||
}
|
||||
|
||||
int FindIndexSub(int index, const Member &elem) const {
|
||||
if (index != Index_Nil) {
|
||||
auto *node = this->GetNode(index);
|
||||
if (Compare{}(elem, node->m_data)) {
|
||||
if (!this->IsLeaf(node->m_left)) {
|
||||
return this->FindIndexSub(node->m_left, elem);
|
||||
}
|
||||
} else {
|
||||
if (!Compare{}(node->m_data, elem)) {
|
||||
return index;
|
||||
}
|
||||
|
||||
if (!this->IsLeaf(node->m_right)) {
|
||||
return this->FindIndexSub(node->m_right, elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Index_Nil;
|
||||
}
|
||||
|
||||
int FindMaxInSubtree(int index) const {
|
||||
int max = index;
|
||||
for (auto *node = this->GetNode(index); !this->IsNil(node->m_right); node = this->GetNode(node->m_right)) {
|
||||
max = node->m_right;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
int FindMinInSubtree(int index) const {
|
||||
int min = index;
|
||||
for (auto *node = this->GetNode(index); !this->IsNil(node->m_left); node = this->GetNode(node->m_left)) {
|
||||
min = node->m_left;
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
int InsertAt(bool before, int parent, const Member &elem) {
|
||||
/* Get an index for the new element. */
|
||||
const auto index = this->AcquireIndex();
|
||||
|
||||
/* Create the node. */
|
||||
auto *node = this->GetNode(index);
|
||||
node->m_color = Color::Red;
|
||||
node->m_parent = parent;
|
||||
node->m_right = Index_Leaf;
|
||||
node->m_left = Index_Leaf;
|
||||
std::memcpy(reinterpret_cast<u8 *>(std::addressof(node->m_data)), reinterpret_cast<const u8 *>(std::addressof(elem)), sizeof(node->m_data));
|
||||
|
||||
/* Fix up the parent node. */
|
||||
auto *parent_node = this->GetNode(parent);
|
||||
if (before) {
|
||||
parent_node->m_left = index;
|
||||
if (parent == this->GetLMost()) {
|
||||
this->SetLMost(index);
|
||||
}
|
||||
} else {
|
||||
parent_node->m_right = index;
|
||||
if (parent == this->GetRMost()) {
|
||||
this->SetRMost(index);
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure the tree is balanced. */
|
||||
int cur_index = index;
|
||||
while (true) {
|
||||
auto *cur_node = this->GetNode(cur_index);
|
||||
if (this->GetNode(cur_node->m_parent)->m_color != Color::Red) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto *p_node = this->GetNode(cur_node->m_parent);
|
||||
auto *g_node = this->GetNode(p_node->m_parent);
|
||||
if (cur_node->m_parent == g_node->m_left) {
|
||||
if (auto *gr_node = this->GetNode(g_node->m_right); gr_node->m_color == Color::Red) {
|
||||
p_node->m_color = Color::Black;
|
||||
gr_node->m_color = Color::Black;
|
||||
g_node->m_color = Color::Red;
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
|
||||
|
||||
cur_index = p_node->m_parent;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur_index == p_node->m_right) {
|
||||
cur_index = cur_node->m_parent;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
this->RotateLeft(cur_index);
|
||||
}
|
||||
|
||||
p_node = this->GetNode(cur_node->m_parent);
|
||||
p_node->m_color = Color::Black;
|
||||
|
||||
g_node = this->GetNode(p_node->m_parent);
|
||||
g_node->m_color = Color::Red;
|
||||
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
|
||||
|
||||
this->RotateRight(p_node->m_parent);
|
||||
} else {
|
||||
if (auto *gl_node = this->GetNode(g_node->m_left); gl_node->m_color == Color::Red) {
|
||||
p_node->m_color = Color::Black;
|
||||
gl_node->m_color = Color::Black;
|
||||
g_node->m_color = Color::Red;
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
|
||||
|
||||
cur_index = p_node->m_parent;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur_index == p_node->m_left) {
|
||||
cur_index = cur_node->m_parent;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
this->RotateRight(cur_index);
|
||||
}
|
||||
|
||||
p_node = this->GetNode(cur_node->m_parent);
|
||||
p_node->m_color = Color::Black;
|
||||
|
||||
g_node = this->GetNode(p_node->m_parent);
|
||||
g_node->m_color = Color::Red;
|
||||
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
|
||||
|
||||
this->RotateLeft(p_node->m_parent);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set root color. */
|
||||
this->GetNode(this->GetRoot())->m_color = Color::Black;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
int InsertNoHint(bool before, const Member &elem) {
|
||||
int cur_index = this->GetRoot();
|
||||
int prev_index = Index_Nil;
|
||||
bool less = true;
|
||||
while (cur_index != Index_Nil && cur_index != Index_Leaf) {
|
||||
auto *node = this->GetNode(cur_index);
|
||||
prev_index = cur_index;
|
||||
|
||||
if (before) {
|
||||
less = Compare{}(node->m_data, elem);
|
||||
} else {
|
||||
less = Compare{}(elem, node->m_data);
|
||||
}
|
||||
|
||||
if (less) {
|
||||
cur_index = node->m_left;
|
||||
} else {
|
||||
cur_index = node->m_right;
|
||||
}
|
||||
}
|
||||
|
||||
if (cur_index == Index_Nil) {
|
||||
/* Create a new node. */
|
||||
const auto index = this->AcquireIndex();
|
||||
auto *node = this->GetNode(index);
|
||||
node->m_color = Color::Black;
|
||||
node->m_parent = Index_Nil;
|
||||
node->m_right = Index_Leaf;
|
||||
node->m_left = Index_Leaf;
|
||||
std::memcpy(reinterpret_cast<u8 *>(std::addressof(node->m_data)), reinterpret_cast<const u8 *>(std::addressof(elem)), sizeof(node->m_data));
|
||||
|
||||
this->SetRoot(index);
|
||||
this->SetLMost(index);
|
||||
this->SetRMost(index);
|
||||
|
||||
return index;
|
||||
} else {
|
||||
auto *compare_node = this->GetNode(prev_index);
|
||||
if (less) {
|
||||
if (prev_index == this->GetLMost()) {
|
||||
return this->InsertAt(less, prev_index, elem);
|
||||
} else {
|
||||
compare_node = this->GetNode(this->UncheckedMM(prev_index));
|
||||
}
|
||||
}
|
||||
|
||||
if (Compare{}(compare_node->m_data, elem)) {
|
||||
return this->InsertAt(less, prev_index, elem);
|
||||
} else {
|
||||
return Index_Nil;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RotateLeft(int index) {
|
||||
/* Determine indices. */
|
||||
const auto p_index = this->GetParent(index);
|
||||
const auto r_index = this->GetNode(index)->m_right;
|
||||
const auto l_index = this->GetNode(index)->m_left;
|
||||
const auto rl_index = this->GetNode(r_index)->m_left;
|
||||
const auto rr_index = this->GetNode(r_index)->m_right;
|
||||
|
||||
/* Get nodes. */
|
||||
auto *node = this->GetNode(index);
|
||||
auto *p_node = this->GetNode(p_index);
|
||||
auto *r_node = this->GetNode(r_index);
|
||||
auto *l_node = this->GetNode(l_index);
|
||||
auto *rl_node = this->GetNode(rl_index);
|
||||
auto *rr_node = this->GetNode(rr_index);
|
||||
|
||||
/* Perform the rotation. */
|
||||
if (p_index == Index_Nil) {
|
||||
r_node->m_parent = Index_Nil;
|
||||
m_header->root_index = r_index;
|
||||
} else if (p_node->m_left == index) {
|
||||
p_node->SetLeft(r_index, r_node, p_index);
|
||||
} else {
|
||||
p_node->SetRight(r_index, r_node, p_index);
|
||||
}
|
||||
r_node->SetLeft(index, node, r_index);
|
||||
r_node->SetRight(rr_index, rr_node, r_index);
|
||||
node->SetLeft(l_index, l_node, index);
|
||||
node->SetRight(rl_index, rl_node, index);
|
||||
}
|
||||
|
||||
void RotateRight(int index) {
|
||||
/* Determine indices. */
|
||||
const auto p_index = this->GetParent(index);
|
||||
const auto l_index = this->GetNode(index)->m_left;
|
||||
const auto ll_index = this->GetNode(l_index)->m_left;
|
||||
const auto lr_index = this->GetNode(l_index)->m_right;
|
||||
const auto r_index = this->GetNode(index)->m_right;
|
||||
|
||||
/* Get nodes. */
|
||||
auto *node = this->GetNode(index);
|
||||
auto *p_node = this->GetNode(p_index);
|
||||
auto *l_node = this->GetNode(l_index);
|
||||
auto *ll_node = this->GetNode(ll_index);
|
||||
auto *lr_node = this->GetNode(lr_index);
|
||||
auto *r_node = this->GetNode(r_index);
|
||||
|
||||
/* Perform the rotation. */
|
||||
if (p_index == Index_Nil) {
|
||||
l_node->m_parent = Index_Nil;
|
||||
m_header->root_index = l_index;
|
||||
} else if (p_node->m_left == index) {
|
||||
p_node->SetLeft(l_index, l_node, p_index);
|
||||
} else {
|
||||
p_node->SetRight(l_index, l_node, p_index);
|
||||
}
|
||||
l_node->SetLeft(ll_index, ll_node, l_index);
|
||||
l_node->SetRight(index, node, l_index);
|
||||
node->SetLeft(lr_index, lr_node, index);
|
||||
node->SetRight(r_index, r_node, index);
|
||||
}
|
||||
|
||||
int UncheckedMM(int index) const {
|
||||
auto *node = this->GetNode(index);
|
||||
if (this->IsNil(index)) {
|
||||
index = this->GetRMost();
|
||||
node = this->GetNode(index);
|
||||
} else if (this->IsNil(node->m_left)) {
|
||||
int parent = node->m_parent;
|
||||
Node *p;
|
||||
|
||||
for (p = this->GetNode(parent); !this->IsNil(parent) && index == p->m_left; p = this->GetNode(parent)) {
|
||||
index = parent;
|
||||
node = p;
|
||||
parent = p->m_parent;
|
||||
}
|
||||
|
||||
if (!this->IsNil(index)) {
|
||||
index = parent;
|
||||
node = p;
|
||||
}
|
||||
} else {
|
||||
index = this->FindMaxInSubtree(node->m_left);
|
||||
node = this->GetNode(index);
|
||||
}
|
||||
|
||||
if (this->IsNil(index)) {
|
||||
return Index_Leaf;
|
||||
} else {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
int UncheckedPP(int index) const {
|
||||
auto *node = this->GetNode(index);
|
||||
|
||||
if (!this->IsNil(index)) {
|
||||
if (this->IsNil(node->m_right)) {
|
||||
int parent = node->m_parent;
|
||||
Node *p;
|
||||
|
||||
for (p = this->GetNode(parent); !this->IsNil(parent) && index == p->m_right; p = this->GetNode(parent)) {
|
||||
index = parent;
|
||||
node = p;
|
||||
parent = p->m_parent;
|
||||
}
|
||||
|
||||
index = parent;
|
||||
node = p;
|
||||
} else {
|
||||
index = this->FindMinInSubtree(node->m_right);
|
||||
node = this->GetNode(index);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->IsNil(index)) {
|
||||
return Index_Leaf;
|
||||
} else {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
public:
|
||||
void Initialize(size_t num_elements, void *buffer, size_t buffer_size) {
|
||||
AMS_ASSERT(num_elements <= max_size);
|
||||
|
||||
return this->InitializeImpl(static_cast<int>(num_elements), buffer, buffer_size);
|
||||
}
|
||||
|
||||
iterator begin() { return iterator(*this); }
|
||||
const_iterator begin() const { return const_iterator(*this); }
|
||||
|
||||
iterator end() { return m_end_iterator; }
|
||||
const_iterator end() const { return m_end_iterator; }
|
||||
|
||||
size_t size() const { return m_header->cur_elements; }
|
||||
|
||||
void clear() {
|
||||
const auto num_elements = m_header->max_elements;
|
||||
const auto buffer_size = m_header->buffer_size;
|
||||
AMS_ASSERT(buffer_size == static_cast<u32>(GetRequiredMemorySize(num_elements)));
|
||||
|
||||
return this->InitializeMemory(num_elements, buffer_size, impl::AvailableIndexFinder::GetSignature());
|
||||
}
|
||||
|
||||
bool erase(const Member &elem) {
|
||||
const auto range = this->equal_range(elem);
|
||||
if (range.first != range.last) {
|
||||
this->EraseByIndex(range.first);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
iterator find(const Member &elem) {
|
||||
if (const auto index = this->FindIndex(elem); index >= 0) {
|
||||
return iterator(*this, index);
|
||||
} else {
|
||||
return this->end();
|
||||
}
|
||||
}
|
||||
|
||||
const_iterator find(const Member &elem) const {
|
||||
if (const auto index = this->FindIndex(elem); index >= 0) {
|
||||
return const_iterator(*this, index);
|
||||
} else {
|
||||
return this->end();
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const Member &elem) {
|
||||
const auto index = this->InsertNoHint(false, elem);
|
||||
const auto it = iterator(*this, index);
|
||||
return std::make_pair(it, !this->IsNil(index));
|
||||
}
|
||||
|
||||
IndexPair equal_range(const Member &elem) {
|
||||
/* Get node to start iteration. */
|
||||
auto cur_index = this->GetRoot();
|
||||
auto cur_node = this->GetNode(cur_index);
|
||||
|
||||
auto min_index = Index_Leaf;
|
||||
auto min_node = this->GetNode(min_index);
|
||||
|
||||
auto max_index = Index_Leaf;
|
||||
auto max_node = this->GetNode(max_index);
|
||||
|
||||
/* Iterate until current is leaf, to find min/max. */
|
||||
while (cur_index != Index_Leaf) {
|
||||
if (Compare{}(cur_node->m_data, elem)) {
|
||||
cur_index = cur_node->m_right;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
} else {
|
||||
if (max_index == Index_Leaf && Compare{}(elem, cur_node->m_data)) {
|
||||
max_index = cur_index;
|
||||
max_node = this->GetNode(max_index);
|
||||
}
|
||||
min_index = cur_index;
|
||||
min_node = this->GetNode(min_index);
|
||||
|
||||
cur_index = cur_node->m_left;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterate again, to find correct range extent for max. */
|
||||
cur_index = (max_index == Index_Leaf) ? this->GetRoot() : max_node->m_left;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
while (cur_index != Index_Leaf) {
|
||||
if (Compare{}(elem, cur_node->m_data)) {
|
||||
max_index = cur_index;
|
||||
max_node = cur_node;
|
||||
cur_index = cur_node->m_left;
|
||||
} else {
|
||||
cur_index = cur_node->m_right;
|
||||
}
|
||||
cur_node = this->GetNode(cur_index);
|
||||
}
|
||||
|
||||
AMS_UNUSED(min_node);
|
||||
return IndexPair{min_index, max_index};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -13,7 +13,8 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
|
||||
|
||||
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
90
libraries/libvapours/source/prfile2/pdm/prfile2_pdm_api.cpp
Normal file
90
libraries/libvapours/source/prfile2/pdm/prfile2_pdm_api.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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_pdm_disk_set.hpp"
|
||||
|
||||
namespace ams::prfile2::pdm {
|
||||
|
||||
namespace impl {
|
||||
|
||||
constinit DiskSet g_disk_set;
|
||||
|
||||
}
|
||||
|
||||
pdm::Error Initialize(u32 config, void *param) {
|
||||
AMS_UNUSED(config, param);
|
||||
|
||||
/* Clear the disk set. */
|
||||
std::memset(std::addressof(impl::g_disk_set), 0, sizeof(impl::g_disk_set));
|
||||
|
||||
return pdm::Error_Ok;
|
||||
}
|
||||
|
||||
pdm::Error OpenDisk(InitDisk *init_disk_table, HandleType *out) {
|
||||
/* Check the arguments. */
|
||||
if (out == nullptr) {
|
||||
return pdm::Error_InvalidParameter;
|
||||
}
|
||||
|
||||
/* Set the output as invalid. */
|
||||
*out = InvalidHandle;
|
||||
|
||||
/* Open the disk. */
|
||||
return disk::OpenDisk(init_disk_table, out);
|
||||
}
|
||||
|
||||
pdm::Error CloseDisk(HandleType handle) {
|
||||
/* Check the input. */
|
||||
if (IsInvalidHandle(handle)) {
|
||||
return pdm::Error_InvalidParameter;
|
||||
}
|
||||
|
||||
/* Close the disk. */
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
148
libraries/libvapours/source/prfile2/pdm/prfile2_pdm_disk.cpp
Normal file
148
libraries/libvapours/source/prfile2/pdm/prfile2_pdm_disk.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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_pdm_disk_set.hpp"
|
||||
|
||||
namespace ams::prfile2::pdm::disk {
|
||||
|
||||
pdm::Error OpenDisk(InitDisk *init_disk_table, HandleType *out) {
|
||||
/* Check the arguments. */
|
||||
if (out == nullptr || init_disk_table == nullptr || init_disk_table->function == nullptr) {
|
||||
return pdm::Error_InvalidParameter;
|
||||
}
|
||||
|
||||
/* Find a free disk holder. */
|
||||
DiskHolder *holder = nullptr;
|
||||
for (auto &h : impl::g_disk_set.disk_holders) {
|
||||
if (h.disk == nullptr) {
|
||||
holder = std::addressof(h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (holder == nullptr) {
|
||||
return pdm::Error_NotExistFreeDiskStruct;
|
||||
}
|
||||
|
||||
/* Find a free disk. */
|
||||
Disk *disk = nullptr;
|
||||
for (auto &d : impl::g_disk_set.disks) {
|
||||
if (!d.IsOpen()) {
|
||||
disk = std::addressof(d);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (disk == nullptr) {
|
||||
return pdm::Error_NotExistFreeDiskStruct;
|
||||
}
|
||||
const auto disk_id = disk - impl::g_disk_set.disks;
|
||||
|
||||
/* Call the disk initialze function. */
|
||||
init_disk_table->function(std::addressof(disk->disk_table), init_disk_table->ui_ext);
|
||||
|
||||
/* Set the disk as open .*/
|
||||
disk->SetOpen(true);
|
||||
|
||||
/* Set the init disk table. */
|
||||
disk->init_disk_table = init_disk_table;
|
||||
|
||||
/* Note the opened disk. */
|
||||
++impl::g_disk_set.num_allocated_disks;
|
||||
|
||||
/* Increment the disk's signature. */
|
||||
disk->signature = static_cast<u16>(disk->signature + 1);
|
||||
|
||||
/* Increment the disk's open count. */
|
||||
++disk->open_count;
|
||||
|
||||
/* Set the disk in the holder. */
|
||||
holder->signature = disk->signature;
|
||||
holder->disk = disk;
|
||||
|
||||
/* Set the output handle. */
|
||||
*out = ConstructDiskHandle(disk_id, disk->signature);
|
||||
|
||||
/* If this was the first opening for the disk, initialize it. */
|
||||
if (disk->open_count == 1) {
|
||||
if (auto err = disk->disk_table.function_table->initialize(*out); err != pdm::Error_Ok) {
|
||||
/* Close the disk. */
|
||||
--disk->open_count;
|
||||
disk->SetOpen(false);
|
||||
--impl::g_disk_set.num_allocated_disks;
|
||||
|
||||
/* Reset the holder. */
|
||||
holder->disk = nullptr;
|
||||
|
||||
return pdm::Error_DriverError;
|
||||
}
|
||||
}
|
||||
|
||||
return pdm::Error_Ok;
|
||||
}
|
||||
|
||||
pdm::Error CloseDisk(HandleType handle) {
|
||||
/* Get the disk. */
|
||||
Disk *disk = GetDisk(handle);
|
||||
if (disk == nullptr) {
|
||||
return pdm::Error_InvalidParameter;
|
||||
}
|
||||
|
||||
/* Check that the disk is open and unlocked. */
|
||||
if (!disk->IsOpen()) {
|
||||
return pdm::Error_StateClosed;
|
||||
}
|
||||
if (disk->IsLocked()) {
|
||||
return pdm::Error_StateLocked;
|
||||
}
|
||||
|
||||
/* Get the disk holder. */
|
||||
DiskHolder *holder = GetDiskHolder(handle);
|
||||
if (holder == nullptr) {
|
||||
return pdm::Error_InvalidParameter;
|
||||
}
|
||||
|
||||
/* Close the disk. */
|
||||
if (disk->open_count == 1) {
|
||||
/* Finalize the disk. */
|
||||
if (auto err = disk->disk_table.function_table->finalize(handle); err != pdm::Error_Ok) {
|
||||
if (auto part = disk->current_partition_handle; part != InvalidHandle) {
|
||||
part::SetDriverErrorCode(part, err);
|
||||
}
|
||||
return pdm::Error_DriverError;
|
||||
}
|
||||
|
||||
/* Set the disk as closed. */
|
||||
disk->SetOpen(false);
|
||||
--impl::g_disk_set.num_allocated_disks;
|
||||
|
||||
/* Clear the disk holder. */
|
||||
holder->disk = nullptr;
|
||||
}
|
||||
|
||||
/* Decrement the disk's open count. */
|
||||
--disk->open_count;
|
||||
|
||||
return pdm::Error_Ok;
|
||||
}
|
||||
|
||||
}
|
||||
103
libraries/libvapours/source/prfile2/pdm/prfile2_pdm_disk_set.hpp
Normal file
103
libraries/libvapours/source/prfile2/pdm/prfile2_pdm_disk_set.hpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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::pdm {
|
||||
|
||||
namespace impl {
|
||||
|
||||
extern DiskSet g_disk_set;
|
||||
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Disk *GetDisk(HandleType handle) {
|
||||
if (AMS_LIKELY(IsDiskHandle(handle))) {
|
||||
if (const auto id = GetHandleId(handle); AMS_LIKELY(id < MaxDisks)) {
|
||||
const auto signature = GetHandleSignature(handle);
|
||||
Disk *disk = std::addressof(impl::g_disk_set.disks[id]);
|
||||
|
||||
for (const auto &holder : impl::g_disk_set.disk_holders) {
|
||||
if (holder.disk == disk && holder.signature == signature) {
|
||||
return disk;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Disk *GetDiskUnsafe(HandleType handle) {
|
||||
return std::addressof(impl::g_disk_set.disks[GetHandleId(handle)]);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE DiskHolder *GetDiskHolder(HandleType handle) {
|
||||
if (AMS_LIKELY(IsDiskHandle(handle))) {
|
||||
if (const auto id = GetHandleId(handle); AMS_LIKELY(id < MaxDisks)) {
|
||||
const auto signature = GetHandleSignature(handle);
|
||||
Disk *disk = std::addressof(impl::g_disk_set.disks[id]);
|
||||
|
||||
for (auto &holder : impl::g_disk_set.disk_holders) {
|
||||
if (holder.disk == disk && holder.signature == signature) {
|
||||
return std::addressof(holder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Partition *GetPartition(HandleType handle) {
|
||||
if (AMS_LIKELY(IsPartitionHandle(handle))) {
|
||||
if (const auto id = GetHandleId(handle); AMS_LIKELY(id < MaxPartitions)) {
|
||||
const auto signature = GetHandleSignature(handle);
|
||||
Partition *part = std::addressof(impl::g_disk_set.partitions[id]);
|
||||
|
||||
for (const auto &holder : impl::g_disk_set.partition_holders) {
|
||||
if (holder.partition == part && holder.signature == signature) {
|
||||
return part;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Partition *GetPartitionUnsafe(HandleType handle) {
|
||||
return std::addressof(impl::g_disk_set.partitions[GetHandleId(handle)]);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE PartitionHolder *GetPartitionHolder(HandleType handle) {
|
||||
if (AMS_LIKELY(IsPartitionHandle(handle))) {
|
||||
if (const auto id = GetHandleId(handle); AMS_LIKELY(id < MaxPartitions)) {
|
||||
const auto signature = GetHandleSignature(handle);
|
||||
Partition *part = std::addressof(impl::g_disk_set.partitions[id]);
|
||||
|
||||
for (auto &holder : impl::g_disk_set.partition_holders) {
|
||||
if (holder.partition == part && holder.signature == signature) {
|
||||
return std::addressof(holder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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_pdm_disk_set.hpp"
|
||||
|
||||
namespace ams::prfile2::pdm::part {
|
||||
|
||||
pdm::Error OpenPartition(HandleType disk_handle, u16 partition_id, HandleType *out) {
|
||||
/* Check the arguments. */
|
||||
if (out == nullptr || disk_handle == InvalidHandle) {
|
||||
return pdm::Error_InvalidParameter;
|
||||
}
|
||||
|
||||
/* Find a free partition holder. */
|
||||
PartitionHolder *holder = nullptr;
|
||||
for (auto &h : impl::g_disk_set.partition_holders) {
|
||||
if (h.partition == nullptr) {
|
||||
holder = std::addressof(h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (holder == nullptr) {
|
||||
return pdm::Error_NotExistFreePartitionStruct;
|
||||
}
|
||||
|
||||
/* Locate a free (or matching) partition. */
|
||||
Partition *part = nullptr;
|
||||
bool found_matching = false;
|
||||
for (auto &p : impl::g_disk_set.partitions) {
|
||||
if (p.IsOpen()) {
|
||||
if (p.disk_handle == disk_handle && p.partition_id == partition_id) {
|
||||
part = std::addressof(p);
|
||||
found_matching = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (part == nullptr) {
|
||||
part = std::addressof(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (part == nullptr) {
|
||||
return pdm::Error_NotExistFreePartitionStruct;
|
||||
}
|
||||
const auto part_id = part - impl::g_disk_set.partitions;
|
||||
|
||||
/* If we're not working with a match, open the new partition. */
|
||||
if (!found_matching) {
|
||||
/* Set the partition as open. */
|
||||
part->SetOpen(true);
|
||||
|
||||
/* Increment the number of open partitions. */
|
||||
++impl::g_disk_set.num_partitions;
|
||||
|
||||
/* Set the partition's disk/id. */
|
||||
part->disk_handle = disk_handle;
|
||||
part->partition_id = partition_id;
|
||||
}
|
||||
|
||||
/* Increment the partition's signature. */
|
||||
part->signature = static_cast<u16>(part->signature + 1);
|
||||
|
||||
/* Increment the partition's open count. */
|
||||
++part->open_count;
|
||||
|
||||
/* Set the holder. */
|
||||
holder->signature = part->signature;
|
||||
holder->partition = part;
|
||||
|
||||
/* Set the output handle. */
|
||||
*out = ConstructPartitionHandle(part_id, part->signature);
|
||||
|
||||
return pdm::Error_Ok;
|
||||
}
|
||||
|
||||
pdm::Error ClosePartition(HandleType part_handle) {
|
||||
/* Get the partition. */
|
||||
Partition *part = GetPartition(part_handle);
|
||||
if (part == nullptr) {
|
||||
return pdm::Error_InvalidParameter;
|
||||
}
|
||||
|
||||
/* Check that the partition is open and unlocked. */
|
||||
if (!part->IsOpen()) {
|
||||
return pdm::Error_StateClosed;
|
||||
}
|
||||
if (part->IsLocked()) {
|
||||
return pdm::Error_StateLocked;
|
||||
}
|
||||
|
||||
/* Get the partition holder. */
|
||||
PartitionHolder *holder = GetPartitionHolder(part_handle);
|
||||
if (holder == nullptr) {
|
||||
return pdm::Error_InvalidParameter;
|
||||
}
|
||||
|
||||
/* Close the partition. */
|
||||
if (part->open_count == 1) {
|
||||
/* Set the partition as closed. */
|
||||
part->SetOpen(false);
|
||||
--impl::g_disk_set.num_partitions;
|
||||
|
||||
/* Clear the partition holder. */
|
||||
holder->partition = nullptr;
|
||||
}
|
||||
|
||||
/* Decrement the partition's open count. */
|
||||
--part->open_count;
|
||||
|
||||
return pdm::Error_Ok;
|
||||
}
|
||||
|
||||
void SetDriverErrorCode(HandleType part_handle, pdm::Error err) {
|
||||
GetPartitionUnsafe(part_handle)->last_driver_error = err;
|
||||
}
|
||||
|
||||
void CheckPartitionOpen(HandleType disk_handle, bool *out) {
|
||||
/* TODO */
|
||||
AMS_UNUSED(disk_handle, out);
|
||||
AMS_ABORT("CheckPartitionOpen");
|
||||
}
|
||||
|
||||
void NotifyMediaEvent(HandleType disk_handle, u32 event) {
|
||||
/* TODO */
|
||||
AMS_UNUSED(disk_handle, event);
|
||||
AMS_ABORT("NotifyMediaEvent");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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_pdm_disk_set.hpp"
|
||||
|
||||
namespace ams::prfile2::pdm::disk {
|
||||
|
||||
pdm::Error CheckDataEraseRequest(HandleType disk_handle, bool *out) {
|
||||
/* Check parameters. */
|
||||
Disk *disk = GetDisk(disk_handle);
|
||||
if (out == nullptr || disk == nullptr) {
|
||||
return pdm::Error_InvalidParameter;
|
||||
}
|
||||
|
||||
/* Check for data erase function. */
|
||||
*out = disk->erase_callback != nullptr;
|
||||
return pdm::Error_Ok;
|
||||
}
|
||||
|
||||
pdm::Error CheckMediaDetect(HandleType disk_handle, bool *out) {
|
||||
/* Check parameters. */
|
||||
Disk *disk = GetDisk(disk_handle);
|
||||
if (out == nullptr || disk == nullptr) {
|
||||
return pdm::Error_InvalidParameter;
|
||||
}
|
||||
|
||||
/* Default to no status change detected. */
|
||||
*out = false;
|
||||
|
||||
/* Detect status change via disk nbc detect. */
|
||||
volatile NonBlockingProtocolType nbc;
|
||||
do {
|
||||
do {
|
||||
nbc = disk->nbc;
|
||||
} while ((nbc & 1) != 0);
|
||||
if (nbc != disk->nbc_detect) {
|
||||
*out = true;
|
||||
}
|
||||
} while (nbc != disk->nbc);
|
||||
|
||||
return pdm::Error_Ok;
|
||||
}
|
||||
|
||||
|
||||
pdm::Error CheckMediaInsert(HandleType disk_handle, bool *out) {
|
||||
/* Check parameters. */
|
||||
Disk *disk = GetDisk(disk_handle);
|
||||
if (out == nullptr || disk == nullptr) {
|
||||
return pdm::Error_InvalidParameter;
|
||||
}
|
||||
|
||||
/* Get whether the disk is inserted. */
|
||||
*out = disk->is_inserted;
|
||||
return pdm::Error_Ok;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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_pdm_disk_set.hpp"
|
||||
|
||||
namespace ams::prfile2::pdm::part {
|
||||
|
||||
pdm::Error CheckDataEraseRequest(HandleType part_handle, bool *out) {
|
||||
/* Check parameters. */
|
||||
Partition *part = GetPartition(part_handle);
|
||||
if (out == nullptr || part == nullptr) {
|
||||
return pdm::Error_InvalidParameter;
|
||||
}
|
||||
|
||||
/* Check the disk. */
|
||||
return pdm::disk::CheckDataEraseRequest(part->disk_handle, out);
|
||||
}
|
||||
|
||||
pdm::Error CheckMediaDetect(HandleType part_handle, bool *out) {
|
||||
/* Check parameters. */
|
||||
Partition *part = GetPartition(part_handle);
|
||||
if (out == nullptr || part == nullptr) {
|
||||
return pdm::Error_InvalidParameter;
|
||||
}
|
||||
|
||||
/* Get the disk (unsafe/not checked). */
|
||||
Disk *disk = GetDiskUnsafe(part->disk_handle);
|
||||
|
||||
/* Default to no status change detected. */
|
||||
*out = false;
|
||||
|
||||
/* Detect status change via partition nbc detect. */
|
||||
volatile NonBlockingProtocolType nbc;
|
||||
do {
|
||||
do {
|
||||
nbc = disk->nbc;
|
||||
} while ((nbc & 1) != 0);
|
||||
if (nbc != part->nbc_detect) {
|
||||
*out = true;
|
||||
}
|
||||
} while (nbc != disk->nbc);
|
||||
|
||||
return pdm::Error_Ok;
|
||||
}
|
||||
|
||||
pdm::Error CheckMediaInsert(HandleType part_handle, bool *out) {
|
||||
/* Check parameters. */
|
||||
Partition *part = GetPartition(part_handle);
|
||||
if (out == nullptr || part == nullptr) {
|
||||
return pdm::Error_InvalidParameter;
|
||||
}
|
||||
|
||||
/* Check if the disk is inserted. */
|
||||
return pdm::disk::CheckMediaInsert(part->disk_handle, out);
|
||||
}
|
||||
|
||||
}
|
||||
62
libraries/libvapours/source/prfile2/pf/prfile2_pf_api.cpp
Normal file
62
libraries/libvapours/source/prfile2/pf/prfile2_pf_api.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
28
libraries/libvapours/source/prfile2/pf/prfile2_pf_errnum.hpp
Normal file
28
libraries/libvapours/source/prfile2/pf/prfile2_pf_errnum.hpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
49
libraries/libvapours/source/prfile2/prfile2_cache.cpp
Normal file
49
libraries/libvapours/source/prfile2/prfile2_cache.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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::cache {
|
||||
|
||||
void SetCache(Volume *vol, pf::CachePage *cache_page, pf::SectorBuffer *cache_buf, u16 num_fat_pages, u16 num_data_pages) {
|
||||
/* Set the cache fields. */
|
||||
vol->cache.pages = cache_page;
|
||||
vol->cache.buffers = cache_buf;
|
||||
vol->cache.num_fat_pages = num_fat_pages;
|
||||
vol->cache.num_data_pages = num_data_pages;
|
||||
std::memset(vol->cache.pages, 0, sizeof(*vol->cache.pages) * (num_fat_pages + num_data_pages));
|
||||
}
|
||||
|
||||
void SetFatBufferSize(Volume *vol, u32 size) {
|
||||
if (size > 0) {
|
||||
vol->cache.fat_buf_size = size;
|
||||
}
|
||||
}
|
||||
|
||||
void SetDataBufferSize(Volume *vol, u32 size) {
|
||||
if (size > 0) {
|
||||
vol->cache.data_buf_size = size;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
141
libraries/libvapours/source/prfile2/prfile2_critical_section.cpp
Normal file
141
libraries/libvapours/source/prfile2/prfile2_critical_section.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
#if defined(AMS_PRFILE2_THREAD_SAFE)
|
||||
|
||||
struct CriticalSection::Resource {
|
||||
os::MutexType mutex;
|
||||
bool in_use;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline const auto NumCriticalSectionResources = MaxVolumes + 1;
|
||||
|
||||
constinit os::SdkMutex g_crit_resource_mutex;
|
||||
constinit CriticalSection::Resource g_crit_resources[NumCriticalSectionResources];
|
||||
|
||||
CriticalSection::Resource *AllocateCriticalSectionResource() {
|
||||
std::scoped_lock lk(g_crit_resource_mutex);
|
||||
|
||||
for (auto &resource : g_crit_resources) {
|
||||
if (!resource.in_use) {
|
||||
resource.in_use = true;
|
||||
return std::addressof(resource);
|
||||
}
|
||||
}
|
||||
|
||||
AMS_ABORT("Failed to allocate critical section resource");
|
||||
}
|
||||
|
||||
void AcquireCriticalSectionResource(CriticalSection::Resource *resource) {
|
||||
return os::LockMutex(std::addressof(resource->mutex));
|
||||
}
|
||||
|
||||
void ReleaseCriticalSectionResource(CriticalSection::Resource *resource) {
|
||||
return os::UnlockMutex(std::addressof(resource->mutex));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InitializeCriticalSection(CriticalSection *cs) {
|
||||
/* Check pre-condition. */
|
||||
AMS_ASSERT(cs->state == CriticalSection::State_NotInitialized);
|
||||
|
||||
/* Perform initialization. */
|
||||
if (cs->state == CriticalSection::State_NotInitialized) {
|
||||
cs->resource = AllocateCriticalSectionResource();
|
||||
cs->owner = 0;
|
||||
cs->state = CriticalSection::State_Initialized;
|
||||
}
|
||||
}
|
||||
|
||||
void FinalizeCriticalSection(CriticalSection *cs) {
|
||||
/* Check pre-condition. */
|
||||
AMS_ASSERT(cs->state == CriticalSection::State_Initialized);
|
||||
|
||||
/* TODO */
|
||||
AMS_UNUSED(cs);
|
||||
AMS_ABORT("prfile2::FinalizeCriticalSection");
|
||||
}
|
||||
|
||||
void EnterCriticalSection(CriticalSection *cs) {
|
||||
/* Check pre-condition. */
|
||||
AMS_ASSERT(cs->state == CriticalSection::State_Initialized);
|
||||
|
||||
if (AMS_LIKELY(cs->lock_count == 0)) {
|
||||
/* Acquire the lock .*/
|
||||
AcquireCriticalSectionResource(cs->resource);
|
||||
system::GetCurrentContextId(std::addressof(cs->owner));
|
||||
} else {
|
||||
/* Get the current context id. */
|
||||
u64 current_context_id;
|
||||
system::GetCurrentContextId(std::addressof(current_context_id));
|
||||
|
||||
/* If the lock isn't already held, acquire it. */
|
||||
if (cs->owner != current_context_id) {
|
||||
AcquireCriticalSectionResource(cs->resource);
|
||||
cs->owner = current_context_id;
|
||||
}
|
||||
}
|
||||
|
||||
/* Increment the lock count. */
|
||||
++cs->lock_count;
|
||||
}
|
||||
|
||||
void ExitCriticalSection(CriticalSection *cs) {
|
||||
/* Check pre-condition. */
|
||||
AMS_ASSERT(cs->state == CriticalSection::State_Initialized);
|
||||
|
||||
/* Unlock. */
|
||||
if ((--cs->lock_count) == 0) {
|
||||
cs->owner = 0;
|
||||
ReleaseCriticalSectionResource(cs->resource);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void InitializeCriticalSection(CriticalSection *cs) {
|
||||
AMS_UNUSED(cs);
|
||||
}
|
||||
|
||||
void FinalizeCriticalSection(CriticalSection *cs) {
|
||||
AMS_UNUSED(cs);
|
||||
}
|
||||
|
||||
void EnterCriticalSection(CriticalSection *cs) {
|
||||
AMS_UNUSED(cs);
|
||||
}
|
||||
|
||||
void ExitCriticalSection(CriticalSection *cs) {
|
||||
AMS_UNUSED(cs);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
61
libraries/libvapours/source/prfile2/prfile2_drv.cpp
Normal file
61
libraries/libvapours/source/prfile2/prfile2_drv.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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::drv {
|
||||
|
||||
pf::Error Initialize(Volume *volume) {
|
||||
/* Check the volume. */
|
||||
if (volume == nullptr) {
|
||||
return pf::Error_InvalidParameter;
|
||||
}
|
||||
|
||||
/* Check the data erase request. */
|
||||
bool data_erase;
|
||||
if (auto pdm_err = pdm::part::CheckDataEraseRequest(volume->partition_handle, std::addressof(data_erase)); pdm_err != pdm::Error_Ok) {
|
||||
return pf::Error_Generic;
|
||||
}
|
||||
|
||||
/* Set the data erase request flag. */
|
||||
volume->SetDataEraseRequested(data_erase);
|
||||
return pf::Error_Ok;
|
||||
}
|
||||
|
||||
bool IsDetected(Volume *volume) {
|
||||
/* Check detect status changed. */
|
||||
/* NOTE: Error is not checked here. */
|
||||
bool detected = false;
|
||||
pdm::part::CheckMediaDetect(volume->partition_handle, std::addressof(detected));
|
||||
return detected;
|
||||
}
|
||||
|
||||
bool IsInserted(Volume *volume) {
|
||||
/* Check inserted. */
|
||||
/* NOTE: Error is not checked here. */
|
||||
bool inserted = false;
|
||||
pdm::part::CheckMediaInsert(volume->partition_handle, std::addressof(inserted));
|
||||
return inserted;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,21 +13,20 @@
|
||||
* 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>
|
||||
#include "usb_remote_ds_root_service.hpp"
|
||||
#include "usb_remote_ds_service.hpp"
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
|
||||
namespace ams::usb {
|
||||
namespace ams::prfile2::fatfs {
|
||||
|
||||
Result RemoteDsRootService::GetService(sf::Out<sf::SharedPointer<usb::ds::IDsService>> out) {
|
||||
Service srv;
|
||||
|
||||
serviceAssumeDomain(std::addressof(m_srv));
|
||||
R_TRY(serviceDispatch(std::addressof(m_srv), 0, .out_num_objects = 1, .out_objects = std::addressof(srv)));
|
||||
|
||||
*out = ObjectFactory::CreateSharedEmplaced<ds::IDsService, RemoteDsService>(m_allocator, srv, m_allocator);
|
||||
|
||||
return ResultSuccess();
|
||||
pf::Error Initialize(u32 config, void *param) {
|
||||
return vol::Initialize(config, param);
|
||||
}
|
||||
|
||||
}
|
||||
110
libraries/libvapours/source/prfile2/prfile2_str.cpp
Normal file
110
libraries/libvapours/source/prfile2/prfile2_str.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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::str {
|
||||
|
||||
namespace {
|
||||
|
||||
/* TODO: Where does this come from? */
|
||||
/* It's maximum path length * 2, but where should the definition live? */
|
||||
constexpr inline size_t StringLengthMax = 520;
|
||||
|
||||
}
|
||||
|
||||
pf::Error Initialize(String *str, const char *s, CodeMode code_mode) {
|
||||
/* Check parameters. */
|
||||
if (str == nullptr || s == nullptr) {
|
||||
return pf::Error_InvalidParameter;
|
||||
}
|
||||
|
||||
/* Initialize the string. */
|
||||
switch (code_mode) {
|
||||
case CodeMode_Local:
|
||||
{
|
||||
str->head = s;
|
||||
str->tail = s + sizeof(char) * strnlen(s, StringLengthMax);
|
||||
}
|
||||
break;
|
||||
case CodeMode_Unicode:
|
||||
{
|
||||
str->head = s;
|
||||
str->tail = s + sizeof(WideChar) * w_strnlen(reinterpret_cast<const WideChar *>(s), StringLengthMax);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return pf::Error_InvalidParameter;
|
||||
}
|
||||
|
||||
/* Set the code mode. */
|
||||
str->code_mode = code_mode;
|
||||
|
||||
return pf::Error_Ok;
|
||||
}
|
||||
|
||||
void SetCodeMode(String *str, CodeMode code_mode) {
|
||||
str->code_mode = code_mode;
|
||||
}
|
||||
|
||||
CodeMode GetCodeMode(const String *str) {
|
||||
return str->code_mode;
|
||||
}
|
||||
|
||||
char *GetPos(String *str, TargetString target) {
|
||||
if (target == TargetString_Head) {
|
||||
return const_cast<char *>(str->head);
|
||||
} else {
|
||||
return const_cast<char *>(str->tail);
|
||||
}
|
||||
}
|
||||
|
||||
void MovePos(String *str, s16 num_char) {
|
||||
AMS_UNUSED(str, num_char);
|
||||
AMS_ABORT("TODO: oem charset");
|
||||
}
|
||||
|
||||
u16 GetLength(String *str) {
|
||||
if (str->code_mode == CodeMode_Unicode) {
|
||||
return (str->tail - str->head) / sizeof(WideChar);
|
||||
} else {
|
||||
return (str->tail - str->head) / sizeof(char);
|
||||
}
|
||||
}
|
||||
|
||||
u16 GetNumChar(String *str, TargetString target) {
|
||||
AMS_UNUSED(str, target);
|
||||
AMS_ABORT("TODO: oem charset");
|
||||
}
|
||||
|
||||
int Compare(const String *str, const char *rhs) {
|
||||
AMS_UNUSED(str, rhs);
|
||||
AMS_ABORT("TODO: oem charset");
|
||||
}
|
||||
|
||||
int Compare(const String *str, const WideChar *rhs) {
|
||||
AMS_UNUSED(str, rhs);
|
||||
AMS_ABORT("TODO: oem charset");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,33 +13,36 @@
|
||||
* 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
|
||||
#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::usb::impl {
|
||||
namespace ams::prfile2::system {
|
||||
|
||||
constexpr int GetEndpointIndex(u8 address) {
|
||||
int idx = address & UsbEndpointAddressMask_EndpointNumber;
|
||||
if ((address & UsbEndpointAddressMask_Dir) == UsbEndpointAddressMask_DirDevicetoHost) {
|
||||
idx += 0x10;
|
||||
}
|
||||
return idx;
|
||||
void Initialize() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class ScopedRefCount {
|
||||
NON_COPYABLE(ScopedRefCount);
|
||||
NON_MOVEABLE(ScopedRefCount);
|
||||
private:
|
||||
T &m_obj;
|
||||
public:
|
||||
ALWAYS_INLINE ScopedRefCount(T &o) : m_obj(o) {
|
||||
++m_obj;
|
||||
}
|
||||
int GetCurrentContextId(u64 *out) {
|
||||
/* Check that out isn't null. */
|
||||
if (out == nullptr) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE ~ScopedRefCount() {
|
||||
--m_obj;
|
||||
}
|
||||
};
|
||||
/* Set the output. */
|
||||
#if defined(AMS_PRFILE2_THREAD_SAFE)
|
||||
*out = reinterpret_cast<u64>(os::GetCurrentThread());
|
||||
#else
|
||||
*out = 0;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
433
libraries/libvapours/source/prfile2/prfile2_volume.cpp
Normal file
433
libraries/libvapours/source/prfile2/prfile2_volume.cpp
Normal file
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
VolumeContext *GetCurrentVolumeContext(u64 *out_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 current context id. */
|
||||
u64 context_id = 0;
|
||||
system::GetCurrentContextId(std::addressof(context_id));
|
||||
if (out_context_id != nullptr) {
|
||||
*out_context_id = context_id;
|
||||
}
|
||||
|
||||
if (auto *ctx = GetVolumeContextById(context_id); ctx != nullptr) {
|
||||
return ctx;
|
||||
} else {
|
||||
return std::addressof(vol_set.default_context);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsValidDriveCharacter(pf::DriveCharacter drive_char) {
|
||||
return static_cast<u8>((drive_char & 0xDF) - 'A') < 26;
|
||||
}
|
||||
|
||||
Volume *GetVolumeByDriveCharacter(pf::DriveCharacter drive_char) {
|
||||
/* Get the volume set. */
|
||||
auto &vol_set = GetVolumeSet();
|
||||
|
||||
/* Calculate the volume index. */
|
||||
const auto index = std::toupper(static_cast<unsigned char>(drive_char)) - 'A';
|
||||
|
||||
/* Acquire exclusive access to the volume set. */
|
||||
ScopedCriticalSection lk(vol_set.critical_section);
|
||||
|
||||
if (index < MaxVolumes) {
|
||||
return std::addressof(vol_set.volumes[index]);
|
||||
} else {
|
||||
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) {
|
||||
/* Get the volume set. */
|
||||
auto &vol_set = GetVolumeSet();
|
||||
|
||||
/* Get the volume context for error tracking. */
|
||||
u64 context_id = 0;
|
||||
auto *vol_ctx = GetCurrentVolumeContext(std::addressof(context_id));
|
||||
|
||||
auto SetLastErrorAndReturn = [&] ALWAYS_INLINE_LAMBDA (pf::Error err) -> pf::Error { vol_ctx->last_error = err; return err; };
|
||||
|
||||
/* Check the drive table. */
|
||||
if (drive_table == nullptr) {
|
||||
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||
}
|
||||
|
||||
/* Clear the drive table's character/status. */
|
||||
const auto drive_char = drive_table->drive_char;
|
||||
drive_table->drive_char = 0;
|
||||
drive_table->status = 0;
|
||||
|
||||
/* Check that we can attach. */
|
||||
if (vol_set.num_attached_drives >= MaxVolumes) {
|
||||
return SetLastErrorAndReturn(pf::Error_TooManyVolumesAttached);
|
||||
}
|
||||
|
||||
/* Check the cache setting. */
|
||||
auto *cache_setting = drive_table->cache;
|
||||
if (cache_setting == nullptr) {
|
||||
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||
}
|
||||
if (cache_setting->fat_buf_size > MaximumFatBufferSize) {
|
||||
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||
}
|
||||
if (cache_setting->data_buf_size > MaximumDataBufferSize) {
|
||||
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||
}
|
||||
if (cache_setting->num_fat_pages < MinimumFatPages) {
|
||||
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||
}
|
||||
if (cache_setting->num_data_pages < MinimumDataPages) {
|
||||
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||
}
|
||||
if (cache_setting->pages == nullptr) {
|
||||
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||
}
|
||||
if (cache_setting->buffers == nullptr) {
|
||||
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||
}
|
||||
if (!util::IsAligned(reinterpret_cast<uintptr_t>(cache_setting->pages), alignof(u32))) {
|
||||
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||
}
|
||||
if (!util::IsAligned(reinterpret_cast<uintptr_t>(cache_setting->buffers), alignof(u32))) {
|
||||
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||
}
|
||||
|
||||
/* Adjust the cache setting. */
|
||||
cache_setting->fat_buf_size = std::max<u32>(cache_setting->fat_buf_size, MinimumFatBufferSize);
|
||||
cache_setting->data_buf_size = std::max<u32>(cache_setting->data_buf_size, MinimumDataBufferSize);
|
||||
|
||||
/* Check the adjusted setting. */
|
||||
if (cache_setting->fat_buf_size > cache_setting->num_fat_pages) {
|
||||
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||
}
|
||||
if ((cache_setting->num_data_pages / cache_setting->data_buf_size) < MinimumDataPages) {
|
||||
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||
}
|
||||
|
||||
/* Validate the drive character. */
|
||||
if (drive_char != 0) {
|
||||
if (!IsValidDriveCharacter(drive_char)) {
|
||||
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||
}
|
||||
|
||||
if (auto *vol = GetVolumeByDriveCharacter(drive_char); vol == nullptr || vol->IsAttached()) {
|
||||
return SetLastErrorAndReturn(pf::Error_InvalidVolumeLabel);
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform the bulk of the attach. */
|
||||
Volume *vol = nullptr;
|
||||
{
|
||||
/* Lock the volume set. */
|
||||
ScopedCriticalSection lk(vol_set.critical_section);
|
||||
|
||||
/* Find a free volume. */
|
||||
for (auto &v : vol_set.volumes) {
|
||||
if (!v.IsAttached()) {
|
||||
vol = std::addressof(v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (vol == nullptr) {
|
||||
return SetLastErrorAndReturn(pf::Error_TooManyVolumesAttached);
|
||||
}
|
||||
const auto vol_id = vol - vol_set.volumes;
|
||||
|
||||
/* Clear the volume. */
|
||||
std::memset(vol, 0, sizeof(*vol));
|
||||
|
||||
/* Initialize the volume. */
|
||||
vol->num_free_clusters = InvalidCluster;
|
||||
vol->num_free_clusters_ = InvalidCluster;
|
||||
vol->last_free_cluster = InvalidCluster;
|
||||
vol->partition_handle = drive_table->partition_handle;
|
||||
InitializeCriticalSection(std::addressof(vol->critical_section));
|
||||
vol->drive_char = 'A' + vol_id;
|
||||
|
||||
/* Setup directory tail. */
|
||||
vol->tail_entry.tracker_size = util::size(vol->tail_entry.tracker_buf);
|
||||
vol->tail_entry.tracker_bits = vol->tail_entry.tracker_buf;
|
||||
|
||||
/* NOTE: Cluster link is cleared here, but we already memset vol to zero, so it's unnecessary. */
|
||||
|
||||
/* Initialize driver for volume. */
|
||||
if (auto err = drv::Initialize(vol); err != pf::Error_Ok) {
|
||||
return SetLastErrorAndReturn(err);
|
||||
}
|
||||
|
||||
/* Setup the cache. */
|
||||
cache::SetCache(vol, drive_table->cache->pages, drive_table->cache->buffers, drive_table->cache->num_fat_pages, drive_table->cache->num_data_pages);
|
||||
cache::SetFatBufferSize(vol, drive_table->cache->fat_buf_size);
|
||||
cache::SetDataBufferSize(vol, drive_table->cache->data_buf_size);
|
||||
|
||||
/* Set flags. */
|
||||
vol->SetAttached(true);
|
||||
vol->SetFlag12(true);
|
||||
|
||||
/* Update the drive table. */
|
||||
drive_table->SetAttached(true);
|
||||
drive_table->drive_char = vol->drive_char;
|
||||
|
||||
/* Update the number of attached drives. */
|
||||
if ((vol_set.num_attached_drives++) == 0) {
|
||||
vol_set.default_context.volume_id = vol_id;
|
||||
}
|
||||
}
|
||||
|
||||
/* Acquire exclusive access to the volume set. */
|
||||
ScopedCriticalSection lk(vol_set.critical_section);
|
||||
|
||||
/* Associate the volume to our context while we operate on it. */
|
||||
vol->context = vol_ctx;
|
||||
vol->context_id = context_id;
|
||||
ON_SCOPE_EXIT { vol->context_id = 0; };
|
||||
|
||||
/* TODO: Copy volume root dir entry to all contexts. */
|
||||
|
||||
/* TODO: Clear tracking fields at the end of the volume. */
|
||||
|
||||
/* Perform mount as appropriate. */
|
||||
const auto check_mount_err = /* TODO vol::CheckMediaInsertForAttachMount(vol) */ pf::Error_Ok;
|
||||
const bool inserted = drv::IsInserted(vol);
|
||||
if (check_mount_err != pf::Error_Ok) {
|
||||
if (inserted) {
|
||||
drive_table->SetDiskInserted(true);
|
||||
}
|
||||
vol_ctx->last_error = check_mount_err;
|
||||
} else if (inserted) {
|
||||
drive_table->SetDiskInserted(true);
|
||||
if (auto mount_err = /* TODO vol::MountImpl(vol, 0x1B, false) */pf::Error_InternalError; mount_err != pf::Error_Ok) {
|
||||
vol_ctx->last_error = mount_err;
|
||||
} else {
|
||||
drive_table->SetMounted(true);
|
||||
}
|
||||
}
|
||||
|
||||
return pf::Error_Ok;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
31
libraries/libvapours/source/prfile2/prfile2_volume_set.hpp
Normal file
31
libraries/libvapours/source/prfile2/prfile2_volume_set.hpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
109
libraries/libvapours/source/prfile2/prfile2_wide_string.cpp
Normal file
109
libraries/libvapours/source/prfile2/prfile2_wide_string.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
size_t w_strlen(const WideChar *s) {
|
||||
const WideChar *cur;
|
||||
for (cur = s; *cur != 0; ++cur) {
|
||||
/* ... */
|
||||
}
|
||||
return cur - s;
|
||||
}
|
||||
|
||||
size_t w_strnlen(const WideChar *s, size_t length) {
|
||||
const WideChar *cur;
|
||||
for (cur = s; *cur != 0 && length != 0; ++cur, --length) {
|
||||
/* ... */
|
||||
}
|
||||
return cur - s;
|
||||
}
|
||||
|
||||
WideChar *w_strcpy(WideChar *dst, const WideChar *src) {
|
||||
WideChar * const ret = dst;
|
||||
while (true) {
|
||||
const auto c = *(src++);
|
||||
*(dst++) = c;
|
||||
|
||||
if (c == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
WideChar *w_strncpy(WideChar *dst, const WideChar *src, size_t length) {
|
||||
WideChar * const ret = dst;
|
||||
while (length > 1) {
|
||||
const auto c = *(src++);
|
||||
*(dst++) = c;
|
||||
|
||||
if (c == 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (length == 1) {
|
||||
*(dst++) = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int w_strcmp(const WideChar *lhs, const WideChar *rhs) {
|
||||
WideChar l, r;
|
||||
while (true) {
|
||||
l = *(lhs++);
|
||||
r = *(rhs++);
|
||||
if (l == 0 || r == 0 || l != r) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return l - r;
|
||||
}
|
||||
|
||||
int w_strncmp(const WideChar *lhs, const WideChar *rhs, size_t length) {
|
||||
if (length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
WideChar l, r;
|
||||
while (true) {
|
||||
l = *(lhs++);
|
||||
r = *(rhs++);
|
||||
if (l == 0 || r == 0 || l != r) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((--length) == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return l - r;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "ns_mitm/nsmitm_module.hpp"
|
||||
#include "dns_mitm/dnsmitm_module.hpp"
|
||||
#include "sysupdater/sysupdater_module.hpp"
|
||||
#include "uart_mitm/uartmitm_module.hpp"
|
||||
|
||||
namespace ams::mitm {
|
||||
|
||||
@@ -38,7 +37,6 @@ namespace ams::mitm {
|
||||
ModuleId_NsMitm,
|
||||
ModuleId_DnsMitm,
|
||||
ModuleId_Sysupdater,
|
||||
ModuleId_UartMitm,
|
||||
|
||||
ModuleId_Count,
|
||||
};
|
||||
@@ -72,7 +70,6 @@ namespace ams::mitm {
|
||||
GetModuleDefinition<ns::MitmModule>(),
|
||||
GetModuleDefinition<socket::resolver::MitmModule>(),
|
||||
GetModuleDefinition<sysupdater::MitmModule>(),
|
||||
GetModuleDefinition<uart::MitmModule>(),
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -380,7 +380,7 @@ namespace ams::mitm::fs {
|
||||
|
||||
/* Try to get a storage from the cache. */
|
||||
{
|
||||
std::shared_ptr<fs::IStorage> cached_storage = GetStorageCacheEntry(data_id);
|
||||
std::shared_ptr<fs::IStorage> cached_storage = GetStorageCacheEntry(this->client_info.program_id);
|
||||
if (cached_storage != nullptr) {
|
||||
out.SetValue(MakeSharedStorage(cached_storage), target_object_id);
|
||||
return ResultSuccess();
|
||||
@@ -403,7 +403,7 @@ namespace ams::mitm::fs {
|
||||
new_storage = std::move(layered_storage);
|
||||
}
|
||||
|
||||
SetStorageCacheEntry(data_id, &new_storage);
|
||||
SetStorageCacheEntry(this->client_info.program_id, &new_storage);
|
||||
out.SetValue(MakeSharedStorage(new_storage), target_object_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -362,12 +362,6 @@ namespace ams::settings::fwdbg {
|
||||
/* 0 = Disabled, 1 = Enabled */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_dns_mitm_debug_log", "u8!0x0"));
|
||||
|
||||
/* Controls whether to enable uart mitm */
|
||||
/* for logging bluetooth HCI to btsnoop captures. */
|
||||
/* This is only implemented for [7.0.0+]. */
|
||||
/* 0 = Do not enable, 1 = Enable. */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_uart_mitm", "u8!0x0"));
|
||||
|
||||
/* Hbloader custom settings. */
|
||||
|
||||
/* Controls the size of the homebrew heap when running as applet. */
|
||||
|
||||
@@ -1,257 +0,0 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "uart_mitm_logger.hpp"
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
|
||||
namespace ams::mitm::uart {
|
||||
|
||||
alignas(os::ThreadStackAlignment) u8 g_logger_stack[0x1000];
|
||||
|
||||
std::shared_ptr<UartLogger> g_logger;
|
||||
|
||||
UartLogger::UartLogger() : m_request_event(os::EventClearMode_ManualClear), m_finish_event(os::EventClearMode_ManualClear), m_client_queue(m_client_queue_list, this->QueueSize), m_thread_queue(m_thread_queue_list, this->QueueSize) {
|
||||
for (size_t i=0; i<this->QueueSize; i++) {
|
||||
UartLogMessage *msg = &this->m_queue_list_msgs[i];
|
||||
std::memset(msg, 0, sizeof(UartLogMessage));
|
||||
|
||||
msg->data = static_cast<u8 *>(std::malloc(this->QueueBufferSize));
|
||||
AMS_ABORT_UNLESS(msg->data != nullptr);
|
||||
std::memset(msg->data, 0, this->QueueBufferSize);
|
||||
|
||||
this->m_client_queue.Send(reinterpret_cast<uintptr_t>(msg));
|
||||
}
|
||||
|
||||
/* Create and start the logger thread. */
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(this->m_thread), this->ThreadEntry, this, g_logger_stack, sizeof(g_logger_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(uart, IpcServer) - 2));
|
||||
os::StartThread(std::addressof(this->m_thread));
|
||||
}
|
||||
|
||||
UartLogger::~UartLogger() {
|
||||
/* Tell the logger thread to exit. */
|
||||
UartLogMessage *msg=nullptr;
|
||||
this->m_client_queue.Receive(reinterpret_cast<uintptr_t *>(&msg));
|
||||
|
||||
msg->type = 0;
|
||||
|
||||
this->m_finish_event.Clear();
|
||||
this->m_thread_queue.Send(reinterpret_cast<uintptr_t>(msg));
|
||||
this->m_request_event.Signal();
|
||||
|
||||
/* Wait on the logger thread, then destroy it. */
|
||||
os::WaitThread(std::addressof(this->m_thread));
|
||||
os::DestroyThread(std::addressof(this->m_thread));
|
||||
|
||||
for (size_t i=0; i<this->QueueSize; i++) {
|
||||
UartLogMessage *msg = &this->m_queue_list_msgs[i];
|
||||
std::free(msg->data);
|
||||
msg->data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void UartLogger::ThreadFunction() {
|
||||
bool exit_flag=false;
|
||||
|
||||
this->m_cache_count = 0;
|
||||
this->m_cache_pos = 0;
|
||||
std::memset(this->m_cache_list, 0, sizeof(this->m_cache_list));
|
||||
std::memset(this->m_cache_buffer, 0, sizeof(this->m_cache_buffer));
|
||||
|
||||
while (!exit_flag) {
|
||||
this->m_request_event.Wait();
|
||||
|
||||
/* Receive messages, process them, then Send them. */
|
||||
UartLogMessage *msg=nullptr;
|
||||
while (this->m_thread_queue.TryReceive(reinterpret_cast<uintptr_t *>(&msg))) {
|
||||
if (msg->type==0) {
|
||||
exit_flag = true;
|
||||
}
|
||||
else if (msg->type==1) {
|
||||
this->WriteCache(msg);
|
||||
}
|
||||
else if (msg->type==2) {
|
||||
this->WriteCmdLog(reinterpret_cast<const char*>(msg->data), reinterpret_cast<const char*>(&msg->data[std::strlen((const char*)msg->data)+1]), msg->file_pos);
|
||||
}
|
||||
else if (msg->type==3) {
|
||||
this->FlushCache();
|
||||
}
|
||||
this->m_client_queue.Send(reinterpret_cast<uintptr_t>(msg));
|
||||
}
|
||||
|
||||
this->m_request_event.Clear();
|
||||
this->m_finish_event.Signal();
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for the thread to finish processing messages. */
|
||||
void UartLogger::WaitFinished() {
|
||||
/* Tell the thread to flush the cache. */
|
||||
UartLogMessage *msg=nullptr;
|
||||
this->m_client_queue.Receive(reinterpret_cast<uintptr_t *>(&msg));
|
||||
|
||||
msg->type = 3;
|
||||
|
||||
this->m_finish_event.Clear();
|
||||
this->m_thread_queue.Send(reinterpret_cast<uintptr_t>(msg));
|
||||
this->m_request_event.Signal();
|
||||
|
||||
/* Wait for processing to finish. */
|
||||
m_finish_event.Wait();
|
||||
}
|
||||
|
||||
/* Initialize the specified btsnoop log file. */
|
||||
void UartLogger::InitializeDataLog(FsFile *f, size_t *datalog_pos) {
|
||||
*datalog_pos = 0;
|
||||
|
||||
/* Setup the btsnoop header. */
|
||||
|
||||
struct {
|
||||
char id[8];
|
||||
u32 version;
|
||||
u32 datalink_type;
|
||||
} btsnoop_header = { .id = "btsnoop" };
|
||||
|
||||
u32 version = 1;
|
||||
u32 datalink_type = 1002; /* HCI UART (H4) */
|
||||
ams::util::StoreBigEndian(&btsnoop_header.version, version);
|
||||
ams::util::StoreBigEndian(&btsnoop_header.datalink_type, datalink_type);
|
||||
|
||||
/* Write the btsnoop header to the datalog. */
|
||||
this->WriteLog(f, datalog_pos, &btsnoop_header, sizeof(btsnoop_header));
|
||||
}
|
||||
|
||||
/* Flush the cache into the file. */
|
||||
void UartLogger::FlushCache() {
|
||||
for (size_t i=0; i<this->m_cache_count; i++) {
|
||||
UartLogMessage *cache_msg=&this->m_cache_list[i];
|
||||
this->WriteLogPacket(cache_msg->datalog_file, cache_msg->file_pos, cache_msg->timestamp, cache_msg->dir, cache_msg->data, cache_msg->size);
|
||||
}
|
||||
|
||||
this->m_cache_count = 0;
|
||||
this->m_cache_pos = 0;
|
||||
}
|
||||
|
||||
/* Write the specified message into the cache. */
|
||||
/* dir: false = Send (host->controller), true = Receive (controller->host). */
|
||||
void UartLogger::WriteCache(UartLogMessage *msg) {
|
||||
if (this->m_cache_count >= this->CacheListSize || this->m_cache_pos + msg->size >= this->CacheBufferSize) {
|
||||
this->FlushCache();
|
||||
}
|
||||
|
||||
UartLogMessage *cache_msg=&this->m_cache_list[this->m_cache_count];
|
||||
*cache_msg = *msg;
|
||||
cache_msg->data = &this->m_cache_buffer[this->m_cache_pos];
|
||||
std::memcpy(cache_msg->data, msg->data, msg->size);
|
||||
this->m_cache_count++;
|
||||
this->m_cache_pos+= msg->size;
|
||||
}
|
||||
|
||||
/* Append the specified string to the text file. */
|
||||
void UartLogger::WriteCmdLog(const char *path, const char *str, size_t *file_pos) {
|
||||
Result rc=0;
|
||||
FsFile file={};
|
||||
size_t len = std::strlen(str);
|
||||
rc = ams::mitm::fs::OpenAtmosphereSdFile(&file, path, FsOpenMode_Read | FsOpenMode_Write | FsOpenMode_Append);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = fsFileWrite(&file, *file_pos, str, len, FsWriteOption_None);
|
||||
}
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
*file_pos += len;
|
||||
}
|
||||
fsFileClose(&file);
|
||||
}
|
||||
|
||||
/* Append the specified data to the datalog file. */
|
||||
void UartLogger::WriteLog(FsFile *f, size_t *datalog_pos, const void* buffer, size_t size) {
|
||||
if (R_SUCCEEDED(fsFileWrite(f, *datalog_pos, buffer, size, FsWriteOption_None))) {
|
||||
*datalog_pos += size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Append the specified packet to the datalog via WriteLog. */
|
||||
/* dir: false = Send (host->controller), true = Receive (controller->host). */
|
||||
void UartLogger::WriteLogPacket(FsFile *f, size_t *datalog_pos, s64 timestamp, bool dir, const void* buffer, size_t size) {
|
||||
struct {
|
||||
u32 original_length;
|
||||
u32 included_length;
|
||||
u32 packet_flags;
|
||||
u32 cumulative_drops;
|
||||
s64 timestamp_microseconds;
|
||||
} pkt_hdr = {};
|
||||
|
||||
u32 flags = 0;
|
||||
if (dir) {
|
||||
flags |= BIT(0);
|
||||
}
|
||||
ams::util::StoreBigEndian(&pkt_hdr.original_length, static_cast<u32>(size));
|
||||
ams::util::StoreBigEndian(&pkt_hdr.included_length, static_cast<u32>(size));
|
||||
ams::util::StoreBigEndian(&pkt_hdr.packet_flags, flags);
|
||||
ams::util::StoreBigEndian(&pkt_hdr.timestamp_microseconds, timestamp);
|
||||
|
||||
this->WriteLog(f, datalog_pos, &pkt_hdr, sizeof(pkt_hdr));
|
||||
this->WriteLog(f, datalog_pos, buffer, size);
|
||||
}
|
||||
|
||||
/* Send the specified data to the Logger thread. */
|
||||
/* dir: false = Send (host->controller), true = Receive (controller->host). */
|
||||
bool UartLogger::SendLogData(FsFile *f, size_t *file_pos, s64 timestamp_base, s64 tick_base, bool dir, const void* buffer, size_t size) {
|
||||
/* Ignore log data which is too large. */
|
||||
if (size > this->QueueBufferSize) return false;
|
||||
|
||||
UartLogMessage *msg=nullptr;
|
||||
this->m_client_queue.Receive(reinterpret_cast<uintptr_t *>(&msg));
|
||||
AMS_ABORT_UNLESS(msg->data != nullptr);
|
||||
|
||||
/* Setup the msg and send it. */
|
||||
msg->type = 1;
|
||||
msg->dir = dir;
|
||||
if (timestamp_base) {
|
||||
msg->timestamp = (armTicksToNs(armGetSystemTick() - tick_base) / 1000) + timestamp_base;
|
||||
}
|
||||
else {
|
||||
msg->timestamp = 0;
|
||||
}
|
||||
msg->datalog_file = f;
|
||||
msg->file_pos = file_pos;
|
||||
msg->size = size;
|
||||
std::memcpy(msg->data, buffer, size);
|
||||
|
||||
this->m_finish_event.Clear();
|
||||
this->m_thread_queue.Send(reinterpret_cast<uintptr_t>(msg));
|
||||
this->m_request_event.Signal();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Send the specified text log to the Logger thread. */
|
||||
void UartLogger::SendTextLogData(const char *path, size_t *file_pos, const char *str) {
|
||||
/* Ignore log data which is too large. */
|
||||
if (std::strlen(path)+1 + std::strlen(str)+1 > this->QueueBufferSize) return;
|
||||
|
||||
UartLogMessage *msg=nullptr;
|
||||
this->m_client_queue.Receive(reinterpret_cast<uintptr_t *>(&msg));
|
||||
AMS_ABORT_UNLESS(msg->data != nullptr);
|
||||
|
||||
/* Setup the msg and send it. */
|
||||
msg->type = 2;
|
||||
msg->file_pos = file_pos;
|
||||
std::memcpy(msg->data, path, std::strlen(path)+1);
|
||||
std::memcpy(&msg->data[std::strlen(path)+1], str, std::strlen(str)+1);
|
||||
|
||||
this->m_finish_event.Clear();
|
||||
this->m_thread_queue.Send(reinterpret_cast<uintptr_t>(msg));
|
||||
this->m_request_event.Signal();
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* 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 <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::uart {
|
||||
|
||||
struct UartLogMessage {
|
||||
u8 type;
|
||||
bool dir;
|
||||
s64 timestamp;
|
||||
FsFile *datalog_file;
|
||||
size_t *file_pos;
|
||||
size_t size;
|
||||
u8 *data;
|
||||
};
|
||||
|
||||
class UartLogger {
|
||||
private:
|
||||
ams::os::ThreadType m_thread;
|
||||
os::Event m_request_event;
|
||||
os::Event m_finish_event;
|
||||
os::MessageQueue m_client_queue;
|
||||
os::MessageQueue m_thread_queue;
|
||||
|
||||
static constexpr inline size_t QueueSize = 0x20;
|
||||
static constexpr inline size_t QueueBufferSize = 0x400;
|
||||
static constexpr inline size_t CacheListSize = 0x80;
|
||||
static constexpr inline size_t CacheBufferSize = 0x1000;
|
||||
|
||||
uintptr_t m_client_queue_list[QueueSize];
|
||||
uintptr_t m_thread_queue_list[QueueSize];
|
||||
UartLogMessage m_queue_list_msgs[QueueSize];
|
||||
|
||||
size_t m_cache_count;
|
||||
size_t m_cache_pos;
|
||||
UartLogMessage m_cache_list[CacheListSize];
|
||||
u8 m_cache_buffer[CacheBufferSize];
|
||||
|
||||
static void ThreadEntry(void *arg) { static_cast<UartLogger *>(arg)->ThreadFunction(); }
|
||||
void ThreadFunction();
|
||||
|
||||
void FlushCache();
|
||||
void WriteCache(UartLogMessage *msg);
|
||||
|
||||
void WriteCmdLog(const char *path, const char *str, size_t *file_pos);
|
||||
void WriteLog(FsFile *f, size_t *datalog_pos, const void* buffer, size_t size);
|
||||
void WriteLogPacket(FsFile *f, size_t *datalog_pos, s64 timestamp, bool dir, const void* buffer, size_t size);
|
||||
public:
|
||||
UartLogger();
|
||||
~UartLogger();
|
||||
|
||||
void WaitFinished();
|
||||
|
||||
void InitializeDataLog(FsFile *f, size_t *datalog_pos);
|
||||
|
||||
bool SendLogData(FsFile *f, size_t *file_pos, s64 timestamp_base, s64 tick_base, bool dir, const void* buffer, size_t size);
|
||||
void SendTextLogData(const char *path, size_t *file_pos, const char *str);
|
||||
};
|
||||
|
||||
extern std::shared_ptr<UartLogger> g_logger;
|
||||
}
|
||||
@@ -1,322 +0,0 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "uart_mitm_service.hpp"
|
||||
#include "uart_mitm_logger.hpp"
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
|
||||
namespace ams::mitm::uart {
|
||||
|
||||
/* Helper functions. */
|
||||
bool UartPortService::TryGetCurrentTimestamp(u64 *out) {
|
||||
/* Clear output. */
|
||||
*out = 0;
|
||||
|
||||
/* Check if we have time service. */
|
||||
{
|
||||
bool has_time_service = false;
|
||||
if (R_FAILED(sm::HasService(&has_time_service, sm::ServiceName::Encode("time:s"))) || !has_time_service) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to get the current time. */
|
||||
{
|
||||
sm::ScopedServiceHolder<timeInitialize, timeExit> time_holder;
|
||||
return time_holder && R_SUCCEEDED(timeGetCurrentTime(TimeType_LocalSystemClock, out));
|
||||
}
|
||||
}
|
||||
|
||||
UartPortService::UartPortService(const sm::MitmProcessInfo &cl, std::unique_ptr<::UartPortSession> s) : m_client_info(cl), m_srv(std::move(s)) {
|
||||
Result rc=0;
|
||||
/* Get a timestamp. */
|
||||
u64 timestamp0=0, timestamp1;
|
||||
this->TryGetCurrentTimestamp(×tamp0);
|
||||
timestamp1 = armGetSystemTick();
|
||||
|
||||
/* Setup the btsnoop base timestamps. */
|
||||
this->m_timestamp_base = timestamp0;
|
||||
if (this->m_timestamp_base) {
|
||||
this->m_timestamp_base = 0x00E03AB44A676000 + (this->m_timestamp_base - 946684800) * 1000000;
|
||||
}
|
||||
this->m_tick_base = timestamp1;
|
||||
|
||||
/* Setup/create the logging directory. */
|
||||
std::snprintf(this->m_base_path, sizeof(this->m_base_path), "uart_logs/%011lu_%011lu_%016lx", timestamp0, timestamp1, static_cast<u64>(this->m_client_info.program_id));
|
||||
ams::mitm::fs::CreateAtmosphereSdDirectory("uart_logs");
|
||||
ams::mitm::fs::CreateAtmosphereSdDirectory(this->m_base_path);
|
||||
|
||||
/* Create/initialize the text cmd_log. */
|
||||
char tmp_path[256];
|
||||
std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->m_base_path, "cmd_log");
|
||||
ams::mitm::fs::CreateAtmosphereSdFile(tmp_path, 0, 0);
|
||||
this->m_cmdlog_pos = 0;
|
||||
|
||||
/* Initialize the Send cache-buffer. */
|
||||
this->m_send_cache_buffer = static_cast<u8 *>(std::malloc(this->CacheBufferSize));
|
||||
AMS_ABORT_UNLESS(this->m_send_cache_buffer != nullptr);
|
||||
std::memset(this->m_send_cache_buffer, 0, this->CacheBufferSize);
|
||||
this->m_send_cache_pos = 0;
|
||||
|
||||
/* Initialize the Receive cache-buffer. */
|
||||
this->m_receive_cache_buffer = static_cast<u8 *>(std::malloc(this->CacheBufferSize));
|
||||
AMS_ABORT_UNLESS(this->m_receive_cache_buffer != nullptr);
|
||||
std::memset(this->m_receive_cache_buffer, 0, this->CacheBufferSize);
|
||||
this->m_receive_cache_pos = 0;
|
||||
|
||||
/* Initialize the datalog. */
|
||||
std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->m_base_path, "btsnoop_hci.log");
|
||||
ams::mitm::fs::CreateAtmosphereSdFile(tmp_path, 0, 0);
|
||||
rc = ams::mitm::fs::OpenAtmosphereSdFile(&this->m_datalog_file, tmp_path, FsOpenMode_Read | FsOpenMode_Write | FsOpenMode_Append);
|
||||
/* Set datalog_ready to whether initialization was successful. */
|
||||
this->m_datalog_ready = R_SUCCEEDED(rc);
|
||||
|
||||
if (this->m_datalog_ready) {
|
||||
std::shared_ptr<UartLogger> logger = mitm::uart::g_logger;
|
||||
logger->InitializeDataLog(&this->m_datalog_file, &this->m_datalog_pos);
|
||||
}
|
||||
|
||||
/* This will be enabled by WriteUartData once a certain command is detected. */
|
||||
/* If you want to log all HCI traffic during system-boot initialization, you can change this field to true. */
|
||||
/* When changed to true, qlaunch will hang at a black-screen during system-boot, due to the bluetooth slowdown. */
|
||||
this->m_data_logging_enabled = false;
|
||||
}
|
||||
|
||||
/* Append the specified string to the text cmd_log file. */
|
||||
void UartPortService::WriteCmdLog(const char *str) {
|
||||
char tmp_path[256];
|
||||
std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->m_base_path, "cmd_log");
|
||||
std::shared_ptr<UartLogger> logger = mitm::uart::g_logger;
|
||||
logger->SendTextLogData(tmp_path, &this->m_cmdlog_pos, str);
|
||||
}
|
||||
|
||||
/* Log data from Send/Receive. */
|
||||
/* dir: false = Send (host->controller), true = Receive (controller->host). */
|
||||
void UartPortService::WriteUartData(bool dir, const void* buffer, size_t size) {
|
||||
/* Select which cache buffer/pos to use via dir. */
|
||||
u8 *cache_buffer = !dir ? this->m_send_cache_buffer : this->m_receive_cache_buffer;
|
||||
size_t *cache_pos = !dir ? &this->m_send_cache_pos : &this->m_receive_cache_pos;
|
||||
|
||||
/* Verify that the input size is non-zero, and within cache buffer bounds. */
|
||||
if (size && *cache_pos + size <= this->CacheBufferSize) {
|
||||
struct {
|
||||
u8 opcode[0x2];
|
||||
u8 param_len;
|
||||
} *hci_cmd = reinterpret_cast<decltype(hci_cmd)>(&cache_buffer[0x1]);
|
||||
static_assert(sizeof(*hci_cmd) == 0x3);
|
||||
|
||||
struct {
|
||||
u8 handle_flags[0x2];
|
||||
u16 data_len;
|
||||
} *hci_acl_data = reinterpret_cast<decltype(hci_acl_data)>(&cache_buffer[0x1]);
|
||||
static_assert(sizeof(*hci_acl_data) == 0x4);
|
||||
|
||||
struct {
|
||||
u8 handle_flags[0x2];
|
||||
u8 data_len;
|
||||
} *hci_sco_data = reinterpret_cast<decltype(hci_sco_data)>(&cache_buffer[0x1]);
|
||||
static_assert(sizeof(*hci_sco_data) == 0x3);
|
||||
|
||||
struct {
|
||||
u8 event_code;
|
||||
u8 param_len;
|
||||
} *hci_event = reinterpret_cast<decltype(hci_event)>(&cache_buffer[0x1]);
|
||||
static_assert(sizeof(*hci_event) == 0x2);
|
||||
|
||||
struct {
|
||||
u8 handle_flags[0x2];
|
||||
u16 data_load_len : 14;
|
||||
u8 rfu1 : 2;
|
||||
} *hci_iso_data = reinterpret_cast<decltype(hci_iso_data)>(&cache_buffer[0x1]);
|
||||
static_assert(sizeof(*hci_iso_data) == 0x4);
|
||||
|
||||
/* Copy the input data into the cache and update the pos. */
|
||||
std::memcpy(&cache_buffer[*cache_pos], buffer, size);
|
||||
(*cache_pos)+= size;
|
||||
|
||||
/* Process the packets in the cache. */
|
||||
do {
|
||||
size_t orig_pkt_len = 0x0;
|
||||
size_t pkt_len = 0x1;
|
||||
|
||||
/* Determine which HCI packet this is, via the packet indicator. */
|
||||
/* These are supported regardless of whether the official bluetooth-sysmodule supports it. */
|
||||
|
||||
if (cache_buffer[0] == 0x1) { /* HCI Command */
|
||||
if (*cache_pos >= 0x1+sizeof(*hci_cmd)) {
|
||||
orig_pkt_len = sizeof(*hci_cmd) + hci_cmd->param_len;
|
||||
/* Check for the first command used in the port which is opened last by bluetooth-sysmodule. */
|
||||
/* This is a vendor command. */
|
||||
/* Once detected, data-logging will be enabled. */
|
||||
if (!this->m_data_logging_enabled && hci_cmd->opcode[1] == 0xFC && hci_cmd->opcode[0] == 0x16) {
|
||||
this->m_data_logging_enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cache_buffer[0] == 0x2) { /* HCI ACL Data */
|
||||
if (*cache_pos >= 0x1+sizeof(*hci_acl_data)) {
|
||||
orig_pkt_len = sizeof(*hci_acl_data) + hci_acl_data->data_len;
|
||||
}
|
||||
}
|
||||
else if (cache_buffer[0] == 0x3) { /* HCI Synchronous Data (SCO) */
|
||||
if (*cache_pos >= 0x1+sizeof(*hci_sco_data)) {
|
||||
orig_pkt_len = sizeof(*hci_sco_data) + hci_sco_data->data_len;
|
||||
}
|
||||
}
|
||||
else if (cache_buffer[0] == 0x4) { /* HCI Event */
|
||||
if (*cache_pos >= 0x1+sizeof(*hci_event)) {
|
||||
orig_pkt_len = sizeof(*hci_event) + hci_event->param_len;
|
||||
}
|
||||
}
|
||||
else if (cache_buffer[0] == 0x5) { /* HCI ISO Data */
|
||||
if (*cache_pos >= 0x1+sizeof(*hci_iso_data)) {
|
||||
orig_pkt_len = sizeof(*hci_iso_data) + hci_iso_data->data_load_len;
|
||||
}
|
||||
}
|
||||
else { /* Unknown HCI packet */
|
||||
char str[256];
|
||||
std::snprintf(str, sizeof(str), "WriteUartData(dir = %s): Unknown HCI packet indicator 0x%x, ignoring the packet and emptying the cache.\n", !dir ? "send" : "receive", cache_buffer[0]);
|
||||
this->WriteCmdLog(str);
|
||||
*cache_pos = 0;
|
||||
}
|
||||
|
||||
/* If a full packet is available in the cache, update pkt_len. */
|
||||
if (orig_pkt_len) {
|
||||
if (*cache_pos >= 0x1+orig_pkt_len) {
|
||||
pkt_len+= orig_pkt_len;
|
||||
}
|
||||
}
|
||||
|
||||
/* If a packet is available, log it and update the cache. */
|
||||
if (pkt_len>0x1) {
|
||||
/* Only write to the file if data-logging is enabled and initialized. */
|
||||
if (this->m_data_logging_enabled && this->m_datalog_ready) {
|
||||
std::shared_ptr<UartLogger> logger = mitm::uart::g_logger;
|
||||
if (!logger->SendLogData(&this->m_datalog_file, &this->m_datalog_pos, this->m_timestamp_base, this->m_tick_base, dir, cache_buffer, pkt_len)) {
|
||||
char str[256];
|
||||
std::snprintf(str, sizeof(str), "WriteUartData(): SendLogData dropped packet with size = 0x%lx\n", pkt_len);
|
||||
this->WriteCmdLog(str);
|
||||
}
|
||||
}
|
||||
(*cache_pos)-= pkt_len;
|
||||
if (*cache_pos) {
|
||||
std::memmove(cache_buffer, &cache_buffer[pkt_len], *cache_pos);
|
||||
}
|
||||
}
|
||||
/* Otherwise, exit the loop. */
|
||||
else break;
|
||||
} while(*cache_pos);
|
||||
}
|
||||
}
|
||||
|
||||
/* Forward OpenPort and write to the cmd_log. */
|
||||
Result UartPortService::OpenPort(sf::Out<bool> out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length) {
|
||||
Result rc = uartPortSessionOpenPortFwd(this->m_srv.get(), reinterpret_cast<bool *>(out.GetPointer()), port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle.GetValue(), receive_handle.GetValue(), send_buffer_length, receive_buffer_length);
|
||||
svcCloseHandle(send_handle.GetValue());
|
||||
svcCloseHandle(receive_handle.GetValue());
|
||||
|
||||
char str[256];
|
||||
std::snprintf(str, sizeof(str), "OpenPort(port = 0x%x, baud_rate = %u, flow_control_mode = %u, device_variation = %u, is_invert_tx = %d, is_invert_rx = %d, is_invert_rts = %d, is_invert_cts = %d, send_buffer_length = 0x%lx, receive_buffer_length = 0x%lx): rc = 0x%x, out = %d\n", port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_buffer_length, receive_buffer_length, rc.GetValue(), out.GetValue());
|
||||
this->WriteCmdLog(str);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Forward OpenPortForDev and write to the cmd_log. */
|
||||
Result UartPortService::OpenPortForDev(sf::Out<bool> out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length) {
|
||||
Result rc = uartPortSessionOpenPortForDevFwd(this->m_srv.get(), reinterpret_cast<bool *>(out.GetPointer()), port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle.GetValue(), receive_handle.GetValue(), send_buffer_length, receive_buffer_length);
|
||||
svcCloseHandle(send_handle.GetValue());
|
||||
svcCloseHandle(receive_handle.GetValue());
|
||||
|
||||
char str[256];
|
||||
std::snprintf(str, sizeof(str), "OpenPortForDev(port = 0x%x, baud_rate = %u, flow_control_mode = %u, device_variation = %u, is_invert_tx = %d, is_invert_rx = %d, is_invert_rts = %d, is_invert_cts = %d, send_buffer_length = 0x%lx, receive_buffer_length = 0x%lx): rc = 0x%x, out = %d\n", port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_buffer_length, receive_buffer_length, rc.GetValue(), out.GetValue());
|
||||
this->WriteCmdLog(str);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Forward GetWritableLength and write to the cmd_log. */
|
||||
Result UartPortService::GetWritableLength(sf::Out<u64> out) {
|
||||
Result rc = uartPortSessionGetWritableLength(this->m_srv.get(), reinterpret_cast<u64 *>(out.GetPointer()));
|
||||
|
||||
char str[256];
|
||||
std::snprintf(str, sizeof(str), "GetWritableLength(): rc = 0x%x, out = 0x%lx\n", rc.GetValue(), out.GetValue());
|
||||
this->WriteCmdLog(str);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Forward Send and log the data if the out_size is non-zero. */
|
||||
Result UartPortService::Send(sf::Out<u64> out_size, const sf::InAutoSelectBuffer &data) {
|
||||
Result rc = uartPortSessionSend(this->m_srv.get(), data.GetPointer(), data.GetSize(), reinterpret_cast<u64 *>(out_size.GetPointer()));
|
||||
|
||||
if (R_SUCCEEDED(rc) && out_size.GetValue()) {
|
||||
this->WriteUartData(false, data.GetPointer(), out_size.GetValue());
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Forward GetReadableLength and write to the cmd_log. */
|
||||
Result UartPortService::GetReadableLength(sf::Out<u64> out) {
|
||||
Result rc = uartPortSessionGetReadableLength(this->m_srv.get(), reinterpret_cast<u64 *>(out.GetPointer()));
|
||||
|
||||
char str[256];
|
||||
std::snprintf(str, sizeof(str), "GetReadableLength(): rc = 0x%x, out = 0x%lx\n", rc.GetValue(), out.GetValue());
|
||||
this->WriteCmdLog(str);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Forward Receive and log the data if the out_size is non-zero. */
|
||||
Result UartPortService::Receive(sf::Out<u64> out_size, const sf::OutAutoSelectBuffer &data) {
|
||||
Result rc = uartPortSessionReceive(this->m_srv.get(), data.GetPointer(), data.GetSize(), reinterpret_cast<u64 *>(out_size.GetPointer()));
|
||||
|
||||
if (R_SUCCEEDED(rc) && out_size.GetValue()) {
|
||||
this->WriteUartData(true, data.GetPointer(), out_size.GetValue());
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Forward BindPortEvent and write to the cmd_log. */
|
||||
Result UartPortService::BindPortEvent(sf::Out<bool> out, sf::OutCopyHandle out_event_handle, UartPortEventType port_event_type, s64 threshold) {
|
||||
Result rc = uartPortSessionBindPortEventFwd(this->m_srv.get(), port_event_type, threshold, reinterpret_cast<bool *>(out.GetPointer()), out_event_handle.GetHandlePointer());
|
||||
|
||||
char str[256];
|
||||
std::snprintf(str, sizeof(str), "BindPortEvent(port_event_type = 0x%x, threshold = 0x%lx): rc = 0x%x, out = %d\n", port_event_type, threshold, rc.GetValue(), out.GetValue());
|
||||
this->WriteCmdLog(str);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Forward UnbindPortEvent and write to the cmd_log. */
|
||||
Result UartPortService::UnbindPortEvent(sf::Out<bool> out, UartPortEventType port_event_type) {
|
||||
Result rc = uartPortSessionUnbindPortEvent(this->m_srv.get(), port_event_type, reinterpret_cast<bool *>(out.GetPointer()));
|
||||
|
||||
char str[256];
|
||||
std::snprintf(str, sizeof(str), "UnbindPortEvent(port_event_type = 0x%x): rc = 0x%x, out = %d\n", port_event_type, rc.GetValue(), out.GetValue());
|
||||
this->WriteCmdLog(str);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result UartMitmService::CreatePortSession(sf::Out<sf::SharedPointer<impl::IPortSession>> out) {
|
||||
/* Open a port interface. */
|
||||
UartPortSession port;
|
||||
R_TRY(uartCreatePortSessionFwd(this->forward_service.get(), &port));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(&port.s)};
|
||||
|
||||
out.SetValue(sf::CreateSharedObjectEmplaced<impl::IPortSession, UartPortService>(this->client_info, std::make_unique<UartPortSession>(port)), target_object_id);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* 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 <stratosphere.hpp>
|
||||
|
||||
#include "uart_mitm_logger.hpp"
|
||||
#include "uart_shim.h"
|
||||
|
||||
#define AMS_UART_IPORTSESSION_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, OpenPort, (sf::Out<bool> out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length), (out, port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle, receive_handle, send_buffer_length, receive_buffer_length)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, OpenPortForDev, (sf::Out<bool> out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length), (out, port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle, receive_handle, send_buffer_length, receive_buffer_length)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, GetWritableLength, (sf::Out<u64> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, Send, (sf::Out<u64> out_size, const sf::InAutoSelectBuffer &data), (out_size, data)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, GetReadableLength, (sf::Out<u64> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, Receive, (sf::Out<u64> out_size, const sf::OutAutoSelectBuffer &data), (out_size, data)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, BindPortEvent, (sf::Out<bool> out, sf::OutCopyHandle out_event_handle, UartPortEventType port_event_type, s64 threshold), (out, out_event_handle, port_event_type, threshold)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7, Result, UnbindPortEvent, (sf::Out<bool> out, UartPortEventType port_event_type), (out, port_event_type))
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::mitm::uart::impl, IPortSession, AMS_UART_IPORTSESSION_MITM_INTERFACE_INFO)
|
||||
|
||||
#define AMS_UART_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, CreatePortSession, (sf::Out<sf::SharedPointer<::ams::mitm::uart::impl::IPortSession>> out), (out))
|
||||
|
||||
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::uart::impl, IUartMitmInterface, AMS_UART_MITM_INTERFACE_INFO)
|
||||
|
||||
namespace ams::mitm::uart {
|
||||
|
||||
class UartPortService {
|
||||
private:
|
||||
sm::MitmProcessInfo m_client_info;
|
||||
std::unique_ptr<::UartPortSession> m_srv;
|
||||
|
||||
static constexpr inline size_t CacheBufferSize = 0x1000;
|
||||
|
||||
s64 m_timestamp_base;
|
||||
s64 m_tick_base;
|
||||
|
||||
char m_base_path[256];
|
||||
|
||||
size_t m_cmdlog_pos;
|
||||
size_t m_datalog_pos;
|
||||
|
||||
bool m_datalog_ready;
|
||||
bool m_data_logging_enabled;
|
||||
|
||||
FsFile m_datalog_file;
|
||||
|
||||
u8 *m_send_cache_buffer;
|
||||
u8 *m_receive_cache_buffer;
|
||||
size_t m_send_cache_pos;
|
||||
size_t m_receive_cache_pos;
|
||||
|
||||
bool TryGetCurrentTimestamp(u64 *out);
|
||||
void WriteCmdLog(const char *str);
|
||||
void WriteUartData(bool dir, const void* buffer, size_t size);
|
||||
public:
|
||||
UartPortService(const sm::MitmProcessInfo &cl, std::unique_ptr<::UartPortSession> s);
|
||||
|
||||
virtual ~UartPortService() {
|
||||
std::shared_ptr<UartLogger> logger = mitm::uart::g_logger;
|
||||
logger->WaitFinished();
|
||||
uartPortSessionClose(this->m_srv.get());
|
||||
fsFileClose(&this->m_datalog_file);
|
||||
std::free(this->m_send_cache_buffer);
|
||||
std::free(this->m_receive_cache_buffer);
|
||||
}
|
||||
public:
|
||||
/* Actual command API. */
|
||||
Result OpenPort(sf::Out<bool> out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length);
|
||||
Result OpenPortForDev(sf::Out<bool> out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length);
|
||||
Result GetWritableLength(sf::Out<u64> out);
|
||||
Result Send(sf::Out<u64> out_size, const sf::InAutoSelectBuffer &data);
|
||||
Result GetReadableLength(sf::Out<u64> out);
|
||||
Result Receive(sf::Out<u64> out_size, const sf::OutAutoSelectBuffer &data);
|
||||
Result BindPortEvent(sf::Out<bool> out, sf::OutCopyHandle out_event_handle, UartPortEventType port_event_type, s64 threshold);
|
||||
Result UnbindPortEvent(sf::Out<bool> out, UartPortEventType port_event_type);
|
||||
};
|
||||
static_assert(impl::IsIPortSession<UartPortService>);
|
||||
|
||||
class UartMitmService : public sf::MitmServiceImplBase {
|
||||
public:
|
||||
using MitmServiceImplBase::MitmServiceImplBase;
|
||||
public:
|
||||
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||
/* We will mitm:
|
||||
* - bluetooth, for logging HCI.
|
||||
*/
|
||||
return client_info.program_id == ncm::SystemProgramId::Bluetooth;
|
||||
}
|
||||
public:
|
||||
Result CreatePortSession(sf::Out<sf::SharedPointer<impl::IPortSession>> out);
|
||||
};
|
||||
static_assert(impl::IsIUartMitmInterface<UartMitmService>);
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <switch.h>
|
||||
#include "uart_shim.h"
|
||||
|
||||
/* Command forwarders. */
|
||||
Result uartCreatePortSessionFwd(Service* s, UartPortSession* out) {
|
||||
return serviceDispatch(s, 6,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out->s,
|
||||
);
|
||||
}
|
||||
|
||||
// [7.0.0+]
|
||||
static Result _uartPortSessionOpenPortFwd(UartPortSession* s, bool *out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, Handle send_handle, Handle receive_handle, u64 send_buffer_length, u64 receive_buffer_length, u32 cmd_id) {
|
||||
const struct {
|
||||
u8 is_invert_tx;
|
||||
u8 is_invert_rx;
|
||||
u8 is_invert_rts;
|
||||
u8 is_invert_cts;
|
||||
u32 port;
|
||||
u32 baud_rate;
|
||||
u32 flow_control_mode;
|
||||
u32 device_variation;
|
||||
u32 pad;
|
||||
u64 send_buffer_length;
|
||||
u64 receive_buffer_length;
|
||||
} in = { is_invert_tx!=0, is_invert_rx!=0, is_invert_rts!=0, is_invert_cts!=0, port, baud_rate, flow_control_mode, device_variation, 0, send_buffer_length, receive_buffer_length };
|
||||
|
||||
u8 tmp=0;
|
||||
Result rc = serviceDispatchInOut(&s->s, cmd_id, in, tmp,
|
||||
.in_num_handles = 2,
|
||||
.in_handles = { send_handle, receive_handle },
|
||||
);
|
||||
if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result uartPortSessionOpenPortFwd(UartPortSession* s, bool *out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, Handle send_handle, Handle receive_handle, u64 send_buffer_length, u64 receive_buffer_length) {
|
||||
return _uartPortSessionOpenPortFwd(s, out, port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle, receive_handle, send_buffer_length, receive_buffer_length, 0);
|
||||
}
|
||||
|
||||
Result uartPortSessionOpenPortForDevFwd(UartPortSession* s, bool *out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, Handle send_handle, Handle receive_handle, u64 send_buffer_length, u64 receive_buffer_length) {
|
||||
return _uartPortSessionOpenPortFwd(s, out, port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle, receive_handle, send_buffer_length, receive_buffer_length, 1);
|
||||
}
|
||||
|
||||
Result uartPortSessionBindPortEventFwd(UartPortSession* s, UartPortEventType port_event_type, s64 threshold, bool *out, Handle *handle_out) {
|
||||
const struct {
|
||||
u32 port_event_type;
|
||||
u32 pad;
|
||||
u64 threshold;
|
||||
} in = { port_event_type, 0, threshold };
|
||||
|
||||
u8 tmp=0;
|
||||
Result rc = serviceDispatchInOut(&s->s, 6, in, tmp,
|
||||
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
|
||||
.out_handles = handle_out,
|
||||
);
|
||||
if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* @file uart_shim.h
|
||||
* @brief UART IPC wrapper.
|
||||
* @author yellows8
|
||||
* @copyright libnx Authors
|
||||
*/
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Command forwarders. */
|
||||
Result uartCreatePortSessionFwd(Service* s, UartPortSession* out);
|
||||
|
||||
Result uartPortSessionOpenPortFwd(UartPortSession* s, bool *out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, Handle send_handle, Handle receive_handle, u64 send_buffer_length, u64 receive_buffer_length);
|
||||
Result uartPortSessionOpenPortForDevFwd(UartPortSession* s, bool *out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, Handle send_handle, Handle receive_handle, u64 send_buffer_length, u64 receive_buffer_length);
|
||||
Result uartPortSessionBindPortEventFwd(UartPortSession* s, UartPortEventType port_event_type, s64 threshold, bool *out, Handle *handle_out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "uartmitm_module.hpp"
|
||||
#include "uart_mitm_service.hpp"
|
||||
#include "uart_mitm_logger.hpp"
|
||||
|
||||
namespace ams::mitm::uart {
|
||||
|
||||
namespace {
|
||||
|
||||
enum PortIndex {
|
||||
PortIndex_Mitm,
|
||||
PortIndex_Count,
|
||||
};
|
||||
|
||||
constexpr sm::ServiceName UartMitmServiceName = sm::ServiceName::Encode("uart");
|
||||
|
||||
struct ServerOptions {
|
||||
static constexpr size_t PointerBufferSize = 0x1000;
|
||||
static constexpr size_t MaxDomains = 0;
|
||||
static constexpr size_t MaxDomainObjects = 0;
|
||||
};
|
||||
|
||||
constexpr size_t MaxServers = 1;
|
||||
constexpr size_t MaxSessions = 10;
|
||||
|
||||
class ServerManager final : public sf::hipc::ServerManager<MaxServers, ServerOptions, MaxSessions> {
|
||||
private:
|
||||
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
|
||||
};
|
||||
|
||||
ServerManager g_server_manager;
|
||||
|
||||
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
|
||||
/* Acknowledge the mitm session. */
|
||||
std::shared_ptr<::Service> fsrv;
|
||||
sm::MitmProcessInfo client_info;
|
||||
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
|
||||
|
||||
switch (port_index) {
|
||||
case PortIndex_Mitm:
|
||||
return this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<impl::IUartMitmInterface, UartMitmService>(decltype(fsrv)(fsrv), client_info), fsrv);
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
bool ShouldMitmUart() {
|
||||
u8 en = 0;
|
||||
if (settings::fwdbg::GetSettingsItemValue(&en, sizeof(en), "atmosphere", "enable_uart_mitm") == sizeof(en)) {
|
||||
return (en != 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void MitmModule::ThreadFunction(void *arg) {
|
||||
/* The OpenPort cmds had the params changed with 6.x/7.x, so only support 7.x+. */
|
||||
if (hos::GetVersion() < hos::Version_7_0_0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Wait until initialization is complete. */
|
||||
mitm::WaitInitialized();
|
||||
|
||||
/* Only use uart-mitm if enabled by the sys-setting. */
|
||||
if (!ShouldMitmUart()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create mitm servers. */
|
||||
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<UartMitmService>(PortIndex_Mitm, UartMitmServiceName)));
|
||||
|
||||
mitm::uart::g_logger = std::make_shared<UartLogger>();
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
g_server_manager.LoopProcess();
|
||||
|
||||
mitm::uart::g_logger.reset();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user