Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4ca7fce9e | ||
|
|
7872fc0299 | ||
|
|
874f5b22e0 | ||
|
|
5b0821cb03 | ||
|
|
b93fd40550 | ||
|
|
a4d7c90da7 | ||
|
|
4357086181 | ||
|
|
ec655069d0 | ||
|
|
2b5e8b5c65 | ||
|
|
f31ecb5c23 | ||
|
|
604f899553 | ||
|
|
25d7ab88e1 | ||
|
|
a231d7164b | ||
|
|
a804e62293 | ||
|
|
6706fa49dc | ||
|
|
3fd094436e | ||
|
|
45709aa9cd |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -95,5 +95,3 @@ sept/sept-secondary/KEYS.py
|
|||||||
**/build_nintendo_nx_arm
|
**/build_nintendo_nx_arm
|
||||||
**/build_nintendo_nx_x64
|
**/build_nintendo_nx_x64
|
||||||
**/build_nintendo_nx_x86
|
**/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/sc7fw/sc7fw.elf atmosphere-$(AMSVER)-debug/exosphere-sc7fw.elf
|
||||||
cp exosphere/program/rebootstub/rebootstub.elf atmosphere-$(AMSVER)-debug/exosphere-rebootstub.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_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/ams_mitm/ams_mitm.elf atmosphere-$(AMSVER)-debug/ams_mitm.elf
|
||||||
cp stratosphere/boot/boot.elf atmosphere-$(AMSVER)-debug/boot.elf
|
cp stratosphere/boot/boot.elf atmosphere-$(AMSVER)-debug/boot.elf
|
||||||
cp stratosphere/boot2/boot2.elf atmosphere-$(AMSVER)-debug/boot2.elf
|
cp stratosphere/boot2/boot2.elf atmosphere-$(AMSVER)-debug/boot2.elf
|
||||||
|
|||||||
@@ -1,30 +1,4 @@
|
|||||||
# Changelog
|
# 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.
|
|
||||||
+ By default atmosphère redirects resolution requests for official telemetry servers to a loopback address.
|
|
||||||
+ Documentation on how to configure `dns.mitm` to meet your more specific needs may be found [here](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/features/dns_mitm.md).
|
|
||||||
+ The service framework API (`sf`) was refactored to be more accurate to official logic and greatly reduce memory requirements.
|
|
||||||
+ The comparison of atmosphère module memory usage versus Nintendo's found [here](https://github.com/Atmosphere-NX/Atmosphere/wiki/Memory-Comparisons) was updated to reflect this.
|
|
||||||
+ **Please Note**: If you are a developer using the libstratosphere service APIs, some updating may be required. Contact SciresM#0524 on discord for assistance if required.
|
|
||||||
+ A number of deprecations were removed, following a general codebase cleanup:
|
|
||||||
+ The `sm` extension to not unregister services on connection close was superseded by official opt-in logic in 11.0.0, and has been removed in favor of official logic.
|
|
||||||
+ This should have zero impact on users.
|
|
||||||
+ The temporary `hid-mitm` added in 0.9.0 has finally been removed, following over a year of deprecation.
|
|
||||||
+ There shouldn't be any homebrew in use still affected by this, but the situation will be monitored.
|
|
||||||
+ If this is somehow still a real issue, an unaffiliated hid mitm sysmodule providing the same functionality can be created and released, separate from atmosphère itself.
|
|
||||||
+ Several issues were fixed, and usability and stability were improved.
|
|
||||||
## 0.17.1
|
## 0.17.1
|
||||||
+ A number of atmosphère's modules were using more memory than their Nintendo equivalent's in 0.17.0; a number of code generatio tweaks have been applied to fix this across the board.
|
+ A number of atmosphère's modules were using more memory than their Nintendo equivalent's in 0.17.0; a number of code generatio tweaks have been applied to fix this across the board.
|
||||||
+ A detailed comparison of atmosphère module memory usage versus Nintendo's was made and can be found [here](https://github.com/Atmosphere-NX/Atmosphere/wiki/Memory-Comparisons).
|
+ A detailed comparison of atmosphère module memory usage versus Nintendo's was made and can be found [here](https://github.com/Atmosphere-NX/Atmosphere/wiki/Memory-Comparisons).
|
||||||
|
|||||||
@@ -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)
|
+ `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)
|
+ 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
|
### Firmware Version
|
||||||
set_mitm intercepts the `GetFirmwareVersion` command, if the requester is `qlaunch` or `maintenance`.
|
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
|
It modifies the `display_version` field of the returned system version, causing the version to display
|
||||||
@@ -33,8 +39,3 @@ in settings as `#.#.#|AMS #.#.#|?` with `? = S` when running under system eMMC o
|
|||||||
### System Settings
|
### System Settings
|
||||||
set_mitm intercepts the `GetSettingsItemValueSize` and `GetSettingsItemValue` commands for all requesters.
|
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.
|
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).
|
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ In particular, hosts files parsed by DNS.mitm have the following extensions to t
|
|||||||
+ `*` is treated as a wildcard character, matching any collection of 0 or more characters wherever it occurs in a hostname.
|
+ `*` is treated as a wildcard character, matching any collection of 0 or more characters wherever it occurs in a hostname.
|
||||||
+ `%` is treated as a stand-in for the value of `nsd!environment_identifier`. This is always `lp1`, on production devices.
|
+ `%` is treated as a stand-in for the value of `nsd!environment_identifier`. This is always `lp1`, on production devices.
|
||||||
|
|
||||||
If multiple entries in a host file match a domain, the last-defined match is used.
|
|
||||||
|
|
||||||
Please note that homebrew may trigger a hosts file re-parse by sending the extension IPC command 65000 ("AtmosphereReloadHostsFile") to a connected `sfdnsres` session.
|
Please note that homebrew may trigger a hosts file re-parse by sending the extension IPC command 65000 ("AtmosphereReloadHostsFile") to a connected `sfdnsres` session.
|
||||||
|
|
||||||
### Hosts file selection
|
### Hosts file selection
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
[subrepo]
|
[subrepo]
|
||||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||||
branch = master
|
branch = master
|
||||||
commit = bc08912dd31bb172467add8e24b4f0adac431939
|
commit = 6c11c07e2a7f03952a4e70eb89b47bf528de39c6
|
||||||
parent = 71add1add8521e0c2115ec612c514400ac7ba688
|
parent = 9e104bb83f1302e9f126542fbf57c7f666aae953
|
||||||
method = merge
|
method = merge
|
||||||
cmdver = 0.4.1
|
cmdver = 0.4.1
|
||||||
|
|||||||
@@ -45,21 +45,7 @@
|
|||||||
|
|
||||||
namespace ams::kern {
|
namespace ams::kern {
|
||||||
|
|
||||||
namespace cpu {
|
static_assert(cpu::NumCores <= static_cast<s32>(BITSIZEOF(u64)));
|
||||||
|
static_assert(util::size(cpu::VirtualToPhysicalCoreMap) == BITSIZEOF(u64));
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace ams::kern {
|
|||||||
/* Most fields have already been cleared by our constructor. */
|
/* Most fields have already been cleared by our constructor. */
|
||||||
|
|
||||||
/* Initial processes may run on all cores. */
|
/* 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. */
|
/* Initial processes may use any user priority they like. */
|
||||||
m_priority_mask = ~0xFul;
|
m_priority_mask = ~0xFul;
|
||||||
@@ -55,17 +55,18 @@ namespace ams::kern {
|
|||||||
const auto max_prio = cap.Get<CorePriority::LowestThreadPriority>();
|
const auto max_prio = cap.Get<CorePriority::LowestThreadPriority>();
|
||||||
const auto min_prio = cap.Get<CorePriority::HighestThreadPriority>();
|
const auto min_prio = cap.Get<CorePriority::HighestThreadPriority>();
|
||||||
|
|
||||||
R_UNLESS(min_core <= max_core, svc::ResultInvalidCombination());
|
R_UNLESS(min_core <= max_core, svc::ResultInvalidCombination());
|
||||||
R_UNLESS(min_prio <= max_prio, svc::ResultInvalidCombination());
|
R_UNLESS(min_prio <= max_prio, svc::ResultInvalidCombination());
|
||||||
R_UNLESS(max_core < cpu::NumVirtualCores, svc::ResultInvalidCoreId());
|
R_UNLESS(max_core < cpu::NumCores, svc::ResultInvalidCoreId());
|
||||||
|
|
||||||
|
MESOSPHERE_ASSERT(max_core < BITSIZEOF(u64));
|
||||||
MESOSPHERE_ASSERT(max_prio < BITSIZEOF(u64));
|
MESOSPHERE_ASSERT(max_prio < BITSIZEOF(u64));
|
||||||
|
|
||||||
/* Set core mask. */
|
/* Set core mask. */
|
||||||
for (auto core_id = min_core; core_id <= max_core; core_id++) {
|
for (auto core_id = min_core; core_id <= max_core; core_id++) {
|
||||||
m_core_mask |= (1ul << 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. */
|
/* Set priority mask. */
|
||||||
for (auto prio = min_prio; prio <= max_prio; prio++) {
|
for (auto prio = min_prio; prio <= max_prio; prio++) {
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ namespace ams::kern::svc {
|
|||||||
case ams::svc::InfoType_ThreadTickCount:
|
case ams::svc::InfoType_ThreadTickCount:
|
||||||
{
|
{
|
||||||
/* Verify the requested core is valid. */
|
/* 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());
|
R_UNLESS(core_valid, svc::ResultInvalidCombination());
|
||||||
|
|
||||||
/* Get the thread from its handle. */
|
/* Get the thread from its handle. */
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ namespace ams::kern::svc {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr bool IsValidVirtualCoreId(int32_t core_id) {
|
constexpr bool IsValidCoreId(int32_t core_id) {
|
||||||
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumVirtualCores));
|
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumCores));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExitProcess() {
|
void ExitProcess() {
|
||||||
@@ -275,7 +275,7 @@ namespace ams::kern::svc {
|
|||||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||||
|
|
||||||
/* Validate the core id. */
|
/* 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());
|
R_UNLESS(((1ul << core_id) & process->GetCoreMask()) != 0, svc::ResultInvalidCoreId());
|
||||||
|
|
||||||
/* Validate the priority. */
|
/* Validate the priority. */
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ namespace ams::kern::svc {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr bool IsValidVirtualCoreId(int32_t core_id) {
|
constexpr bool IsValidCoreId(int32_t core_id) {
|
||||||
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumVirtualCores));
|
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) {
|
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. */
|
/* 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(((1ul << core_id) & process.GetCoreMask()) != 0, svc::ResultInvalidCoreId());
|
||||||
|
|
||||||
R_UNLESS(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority, svc::ResultInvalidPriority());
|
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());
|
R_UNLESS(affinity_mask != 0, svc::ResultInvalidCombination());
|
||||||
|
|
||||||
/* Validate the core id. */
|
/* Validate the core id. */
|
||||||
if (IsValidVirtualCoreId(core_id)) {
|
if (IsValidCoreId(core_id)) {
|
||||||
R_UNLESS(((1ul << core_id) & affinity_mask) != 0, svc::ResultInvalidCombination());
|
R_UNLESS(((1ul << core_id) & affinity_mask) != 0, svc::ResultInvalidCombination());
|
||||||
} else {
|
} else {
|
||||||
R_UNLESS(core_id == ams::svc::IdealCoreNoUpdate || core_id == ams::svc::IdealCoreDontCare, svc::ResultInvalidCoreId());
|
R_UNLESS(core_id == ams::svc::IdealCoreNoUpdate || core_id == ams::svc::IdealCoreDontCare, svc::ResultInvalidCoreId());
|
||||||
|
|||||||
@@ -79,7 +79,6 @@
|
|||||||
#include <stratosphere/spl.hpp>
|
#include <stratosphere/spl.hpp>
|
||||||
#include <stratosphere/time.hpp>
|
#include <stratosphere/time.hpp>
|
||||||
#include <stratosphere/updater.hpp>
|
#include <stratosphere/updater.hpp>
|
||||||
#include <stratosphere/usb.hpp>
|
|
||||||
#include <stratosphere/wec.hpp>
|
#include <stratosphere/wec.hpp>
|
||||||
|
|
||||||
/* Include FS last. */
|
/* Include FS last. */
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
namespace ams::psc {
|
namespace ams::psc {
|
||||||
|
|
||||||
enum PmModuleId : u32 {
|
enum PmModuleId : u16 {
|
||||||
PmModuleId_Usb = 4,
|
PmModuleId_Usb = 4,
|
||||||
PmModuleId_Ethernet = 5,
|
PmModuleId_Ethernet = 5,
|
||||||
PmModuleId_Fgm = 6,
|
PmModuleId_Fgm = 6,
|
||||||
|
|||||||
@@ -1,21 +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/usb/usb_limits.hpp>
|
|
||||||
#include <stratosphere/usb/usb_types.hpp>
|
|
||||||
#include <stratosphere/usb/usb_device_types.hpp>
|
|
||||||
#include <stratosphere/usb/usb_device.hpp>
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -40,12 +40,14 @@ namespace ams::hid {
|
|||||||
|
|
||||||
/* Helper. */
|
/* Helper. */
|
||||||
void InitializeHid() {
|
void InitializeHid() {
|
||||||
sm::DoWithSession([&]() {
|
R_ABORT_UNLESS(smInitialize());
|
||||||
|
ON_SCOPE_EXIT { smExit(); };
|
||||||
|
{
|
||||||
R_ABORT_UNLESS(hidInitialize());
|
R_ABORT_UNLESS(hidInitialize());
|
||||||
hidInitializeNpad();
|
hidInitializeNpad();
|
||||||
R_ABORT_UNLESS(hidSetSupportedNpadIdType(NpadIdTypes, NumNpadIdTypes));
|
R_ABORT_UNLESS(hidSetSupportedNpadIdType(NpadIdTypes, NumNpadIdTypes));
|
||||||
R_ABORT_UNLESS(hidSetSupportedNpadStyleSet(HidNpadStyleSet_NpadStandard | HidNpadStyleTag_NpadSystemExt));
|
R_ABORT_UNLESS(hidSetSupportedNpadStyleSet(HidNpadStyleSet_NpadStandard | HidNpadStyleTag_NpadSystemExt));
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result EnsureHidInitialized() {
|
Result EnsureHidInitialized() {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace ams::ncm {
|
|||||||
MakeContentPathFunction make_content_path_func;
|
MakeContentPathFunction make_content_path_func;
|
||||||
bool disabled;
|
bool disabled;
|
||||||
protected:
|
protected:
|
||||||
ContentStorageImplBase() : make_content_path_func(), disabled(false) { /* ... */ }
|
ContentStorageImplBase() { /* ... */ }
|
||||||
protected:
|
protected:
|
||||||
/* Helpers. */
|
/* Helpers. */
|
||||||
Result EnsureEnabled() const {
|
Result EnsureEnabled() const {
|
||||||
|
|||||||
@@ -46,9 +46,9 @@ namespace ams::psc {
|
|||||||
Result PmModule::Initialize(const PmModuleId mid, const PmModuleId *dependencies, u32 dependency_count, os::EventClearMode clear_mode) {
|
Result PmModule::Initialize(const PmModuleId mid, const PmModuleId *dependencies, u32 dependency_count, os::EventClearMode clear_mode) {
|
||||||
R_UNLESS(!this->initialized, psc::ResultAlreadyInitialized());
|
R_UNLESS(!this->initialized, psc::ResultAlreadyInitialized());
|
||||||
|
|
||||||
static_assert(sizeof(*dependencies) == sizeof(u32));
|
static_assert(sizeof(*dependencies) == sizeof(u16));
|
||||||
::PscPmModule module;
|
::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->intf = RemoteObjectFactory::CreateSharedEmplaced<psc::sf::IPmModule, RemotePmModule>(module);
|
||||||
this->system_event.AttachReadableHandle(module.event.revent, false, clear_mode);
|
this->system_event.AttachReadableHandle(module.event.revent, false, clear_mode);
|
||||||
|
|||||||
@@ -1,45 +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::impl {
|
|
||||||
|
|
||||||
constexpr int GetEndpointIndex(u8 address) {
|
|
||||||
int idx = address & UsbEndpointAddressMask_EndpointNumber;
|
|
||||||
if ((address & UsbEndpointAddressMask_Dir) == UsbEndpointAddressMask_DirDevicetoHost) {
|
|
||||||
idx += 0x10;
|
|
||||||
}
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
ALWAYS_INLINE ~ScopedRefCount() {
|
|
||||||
--m_obj;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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,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 <stratosphere.hpp>
|
|
||||||
|
|
||||||
namespace ams::usb {
|
|
||||||
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
static_assert(ds::IsIDsEndpoint<RemoteDsEndpoint>);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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,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/>.
|
|
||||||
*/
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
#include "usb_remote_ds_root_service.hpp"
|
|
||||||
#include "usb_remote_ds_service.hpp"
|
|
||||||
|
|
||||||
namespace ams::usb {
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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>);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
|
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MINOR 18
|
#define ATMOSPHERE_RELEASE_VERSION_MINOR 17
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 1
|
#define ATMOSPHERE_RELEASE_VERSION_MICRO 1
|
||||||
|
|
||||||
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
|
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
|
||||||
|
|||||||
@@ -59,7 +59,6 @@
|
|||||||
#include <vapours/results/svc_results.hpp>
|
#include <vapours/results/svc_results.hpp>
|
||||||
#include <vapours/results/time_results.hpp>
|
#include <vapours/results/time_results.hpp>
|
||||||
#include <vapours/results/updater_results.hpp>
|
#include <vapours/results/updater_results.hpp>
|
||||||
#include <vapours/results/usb_results.hpp>
|
|
||||||
#include <vapours/results/vi_results.hpp>
|
#include <vapours/results/vi_results.hpp>
|
||||||
|
|
||||||
/* Unofficial. */
|
/* 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_format_string.hpp>
|
||||||
#include <vapours/util/util_range.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
|
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||||
#include <vapours/util/util_mutex_utils.hpp>
|
#include <vapours/util/util_mutex_utils.hpp>
|
||||||
#endif
|
#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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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,28 +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 Element, typename Compare = std::less<Element>, size_t BufferAlignment = 8>
|
|
||||||
class FixedSet : public ::ams::util::FixedTree<Element, Compare, const Element, BufferAlignment> {
|
|
||||||
/* ... */
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* 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
|
#pragma once
|
||||||
#include <vapours/common.hpp>
|
#include <vapours/common.hpp>
|
||||||
#include <vapours/assert.hpp>
|
#include <vapours/assert.hpp>
|
||||||
|
|||||||
@@ -69,21 +69,7 @@ namespace ams::mitm::socket::resolver {
|
|||||||
"127.0.0.1 receive-%.dg.srv.nintendo.net receive-%.er.srv.nintendo.net\n";
|
"127.0.0.1 receive-%.dg.srv.nintendo.net receive-%.er.srv.nintendo.net\n";
|
||||||
|
|
||||||
constinit os::SdkMutex g_redirection_lock;
|
constinit os::SdkMutex g_redirection_lock;
|
||||||
std::vector<std::pair<std::string, ams::socket::InAddrT>> g_redirection_list;
|
std::unordered_map<std::string, ams::socket::InAddrT> g_redirection_map;
|
||||||
|
|
||||||
void RemoveRedirection(const char *hostname) {
|
|
||||||
for (auto it = g_redirection_list.begin(); it != g_redirection_list.end(); ++it) {
|
|
||||||
if (std::strcmp(it->first.c_str(), hostname) == 0) {
|
|
||||||
g_redirection_list.erase(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddRedirection(const char *hostname, ams::socket::InAddrT addr) {
|
|
||||||
RemoveRedirection(hostname);
|
|
||||||
g_redirection_list.emplace(g_redirection_list.begin(), std::string(hostname), addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
constinit char g_specific_emummc_hosts_path[0x40] = {};
|
constinit char g_specific_emummc_hosts_path[0x40] = {};
|
||||||
|
|
||||||
@@ -125,8 +111,6 @@ namespace ams::mitm::socket::resolver {
|
|||||||
current_address = 0;
|
current_address = 0;
|
||||||
work = static_cast<u32>(c - '0');
|
work = static_cast<u32>(c - '0');
|
||||||
state = State::Ip1;
|
state = State::Ip1;
|
||||||
} else if (c == '\n') {
|
|
||||||
state = State::BeginLine;
|
|
||||||
} else {
|
} else {
|
||||||
state = State::IgnoredLine;
|
state = State::IgnoredLine;
|
||||||
}
|
}
|
||||||
@@ -222,7 +206,7 @@ namespace ams::mitm::socket::resolver {
|
|||||||
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
|
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
|
||||||
current_hostname[work] = '\x00';
|
current_hostname[work] = '\x00';
|
||||||
|
|
||||||
AddRedirection(current_hostname, current_address);
|
g_redirection_map[static_cast<const char *>(current_hostname)] = current_address;
|
||||||
work = 0;
|
work = 0;
|
||||||
|
|
||||||
if (c == '\n') {
|
if (c == '\n') {
|
||||||
@@ -245,7 +229,7 @@ namespace ams::mitm::socket::resolver {
|
|||||||
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
|
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
|
||||||
current_hostname[work] = '\x00';
|
current_hostname[work] = '\x00';
|
||||||
|
|
||||||
AddRedirection(current_hostname, current_address);
|
g_redirection_map[static_cast<const char *>(current_hostname)] = current_address;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,7 +293,7 @@ namespace ams::mitm::socket::resolver {
|
|||||||
std::scoped_lock lk(g_redirection_lock);
|
std::scoped_lock lk(g_redirection_lock);
|
||||||
|
|
||||||
/* Clear the redirections map. */
|
/* Clear the redirections map. */
|
||||||
g_redirection_list.clear();
|
g_redirection_map.clear();
|
||||||
|
|
||||||
/* Open log file. */
|
/* Open log file. */
|
||||||
::FsFile log_file;
|
::FsFile log_file;
|
||||||
@@ -378,7 +362,7 @@ namespace ams::mitm::socket::resolver {
|
|||||||
|
|
||||||
/* Print the redirections. */
|
/* Print the redirections. */
|
||||||
Log(log_file, "Redirections:\n");
|
Log(log_file, "Redirections:\n");
|
||||||
for (const auto &[host, address] : g_redirection_list) {
|
for (const auto &[host, address] : g_redirection_map) {
|
||||||
Log(log_file, " `%s` -> %u.%u.%u.%u\n", host.c_str(), (address >> 0) & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF, (address >> 24) & 0xFF);
|
Log(log_file, " `%s` -> %u.%u.%u.%u\n", host.c_str(), (address >> 0) & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF, (address >> 24) & 0xFF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,7 +370,7 @@ namespace ams::mitm::socket::resolver {
|
|||||||
bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname) {
|
bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname) {
|
||||||
std::scoped_lock lk(g_redirection_lock);
|
std::scoped_lock lk(g_redirection_lock);
|
||||||
|
|
||||||
for (const auto &[host, address] : g_redirection_list) {
|
for (const auto &[host, address] : g_redirection_map) {
|
||||||
if (wildcardcmp(host.c_str(), hostname)) {
|
if (wildcardcmp(host.c_str(), hostname)) {
|
||||||
*out = address;
|
*out = address;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -111,12 +111,10 @@ namespace ams::mitm::socket::resolver {
|
|||||||
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
|
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
|
||||||
|
|
||||||
u16 port = 0;
|
u16 port = 0;
|
||||||
if (srv.GetPointer() != nullptr) {
|
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
|
||||||
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
|
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
|
||||||
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
|
port *= 10;
|
||||||
port *= 10;
|
port += *cur - '0';
|
||||||
port += *cur - '0';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LogDebug("[%016lx]: Redirecting %s:%u to %u.%u.%u.%u\n", this->client_info.program_id.value, hostname, port, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
|
LogDebug("[%016lx]: Redirecting %s:%u to %u.%u.%u.%u\n", this->client_info.program_id.value, hostname, port, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
|
||||||
@@ -164,12 +162,10 @@ namespace ams::mitm::socket::resolver {
|
|||||||
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
|
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
|
||||||
|
|
||||||
u16 port = 0;
|
u16 port = 0;
|
||||||
if (srv.GetPointer() != nullptr) {
|
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
|
||||||
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
|
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
|
||||||
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
|
port *= 10;
|
||||||
port *= 10;
|
port += *cur - '0';
|
||||||
port += *cur - '0';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LogDebug("[%016lx]: Redirecting %s:%u to %u.%u.%u.%u\n", this->client_info.program_id.value, hostname, port, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
|
LogDebug("[%016lx]: Redirecting %s:%u to %u.%u.%u.%u\n", this->client_info.program_id.value, hostname, port, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
|
||||||
|
|||||||
@@ -380,7 +380,7 @@ namespace ams::mitm::fs {
|
|||||||
|
|
||||||
/* Try to get a storage from the cache. */
|
/* 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) {
|
if (cached_storage != nullptr) {
|
||||||
out.SetValue(MakeSharedStorage(cached_storage), target_object_id);
|
out.SetValue(MakeSharedStorage(cached_storage), target_object_id);
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
@@ -403,7 +403,7 @@ namespace ams::mitm::fs {
|
|||||||
new_storage = std::move(layered_storage);
|
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);
|
out.SetValue(MakeSharedStorage(new_storage), target_object_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user