Compare commits

..

41 Commits

Author SHA1 Message Date
Michael Scire
1545fa9d44 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "bc08912d"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "bc08912d"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-02-15 19:52:28 -08:00
Michael Scire
71add1add8 ams: bump version to 0.18.1 2021-02-15 19:51:48 -08:00
Michael Scire
a96786fd2c ams: add kernel to debug elf zip 2021-02-15 19:40:00 -08:00
Michael Scire
74e4e70053 fs.mitm: fix cache of non-current-process data storages (closes #1371) 2021-02-15 19:39:32 -08:00
znxDomain
26b6216fa0 Correct ams_mitm.md formatting
Move set_mitm H3's under correct H2 element
2021-02-11 05:47:31 -08:00
Michael Scire
fe5c850e69 psc: fix pm module init 2021-02-10 02:57:22 -08:00
Michael Scire
2b825d56dc usb: fix wrong command id for AppendConfigurationData 2021-02-07 17:14:48 -08:00
Michael Scire
13b17a5848 usb: add ds client api 2021-02-07 16:29:38 -08:00
Michael Scire
621520c30b kern: fix support for virtual core IDs 2021-02-05 14:59:03 -08:00
Michael Scire
846cc0b47a util: add FixedSet 2021-02-04 02:29:54 -08:00
Michael Scire
e82ad1cdc5 util: add FixedMap::clear 2021-02-04 02:16:37 -08:00
Michael Scire
b40da8f445 ams: add transient unit testing sysmodule to gitignore 2021-02-04 02:08:49 -08:00
Michael Scire
a9c6476416 util: various FixedMap fixes 2021-02-04 02:08:21 -08:00
Michael Scire
c1d93a9495 util: add FixedMap/FixedTree 2021-02-04 01:00:19 -08:00
Michael Scire
201b17f100 util: impl::AvailableIndexFinder 2021-02-03 17:47:32 -08:00
Michael Scire
c0e3cee657 dns.mitm: allow nullptr for port string (closes #1352) 2021-02-03 12:41:23 -08:00
Michael Scire
26d8db74f8 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "17960517"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "17960517"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-02-02 18:33:22 -08:00
Michael Scire
ee2e9d50fd hid: fix sm usage 2021-02-02 18:32:29 -08:00
Michael Scire
fd1a39996e ncm: fix ContentStorageImplBase constructor 2021-02-02 17:12:02 -08:00
Michael Scire
8eb65ab401 dns.mitm: fix hosts file parsing bug 2021-02-02 17:11:53 -08:00
Michael Scire
bcda834980 dns.mitm: make line ordering explicit, rather than implicit.
This doesn't actually change functionality, because this is how std::unordered_map worked anyway...

...but it's better for us to be explicit, I think.
2021-02-02 13:48:40 -08:00
Michael Scire
408b81b881 docs: fix missing meme (important) 2021-02-02 12:17:00 -08:00
Michael Scire
d854b94382 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "90d85295"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "90d85295"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-02-02 12:16:49 -08:00
Michael Scire
63440cab18 ams: bump version to 0.18.0 2021-02-02 12:15:53 -08:00
Michael Scire
4fa585a23f docs: expand dns_mitm wildcard wording 2021-02-02 11:41:49 -08:00
Michael Scire
e521ae805d dns.mitm: add documentation 2021-02-02 11:41:49 -08:00
Michael Scire
6950989552 dns.mitm: support % in hosts file as stand-in for environment identifier 2021-02-02 11:41:49 -08:00
Michael Scire
ffbdf29c10 dns.mitm: fix ABORT_UNLESS -> ABORT 2021-02-02 11:41:49 -08:00
Michael Scire
135d42ffee dns.mitm: hint = {} fix in options 2021-02-02 11:41:49 -08:00
Michael Scire
1306d03136 dns.mitm: add GetAddrInfo redir, AtmosphereReloadHostsFile, debug logging control 2021-02-02 11:41:49 -08:00
Michael Scire
97aa209c43 dns.mitm: fix inverted hostname detection 2021-02-02 11:41:49 -08:00
Michael Scire
4ce2a6deb3 meso: update for new fatal encoding 2021-02-02 11:41:49 -08:00
Michael Scire
e45d8cd7d8 ams: take three tries to edit a structure 2021-02-02 11:41:49 -08:00
Michael Scire
79211e1159 ams: afsr0 -> 32 bits in fatal error 2021-02-02 11:41:49 -08:00
Michael Scire
ca8e8ce487 ams: fix fatal error context 2021-02-02 11:41:49 -08:00
Michael Scire
b30311be65 dns.mitm: support wildcards in hosts 2021-02-02 11:41:49 -08:00
Michael Scire
6694d8c2d9 dns.mitm: edit default behavior/hosts filenames 2021-02-02 11:41:49 -08:00
Michael Scire
658389fc60 dns.mitm: fix issue in host file parsing 2021-02-02 11:41:49 -08:00
Michael Scire
d6477cf024 dns.mitm: parse redirections from hosts file 2021-02-02 11:41:49 -08:00
Michael Scire
400f5142ee dns: implement GetHostByName redirection (backend TODO) 2021-02-02 11:41:49 -08:00
Michael Scire
8bf8df43e2 dns: skeleton passthrough mitm 2021-02-02 11:41:49 -08:00
46 changed files with 3431 additions and 51 deletions

2
.gitignore vendored
View File

@@ -95,3 +95,5 @@ sept/sept-secondary/KEYS.py
**/build_nintendo_nx_arm
**/build_nintendo_nx_x64
**/build_nintendo_nx_x86
stratosphere/test/

View File

@@ -135,6 +135,7 @@ dist: dist-no-debug
cp exosphere/program/sc7fw/sc7fw.elf atmosphere-$(AMSVER)-debug/exosphere-sc7fw.elf
cp exosphere/program/rebootstub/rebootstub.elf atmosphere-$(AMSVER)-debug/exosphere-rebootstub.elf
cp mesosphere/kernel_ldr/kernel_ldr.elf atmosphere-$(AMSVER)-debug/kernel_ldr.elf
cp mesosphere/kernel/kernel.elf atmosphere-$(AMSVER)-debug/kernel.elf
cp stratosphere/ams_mitm/ams_mitm.elf atmosphere-$(AMSVER)-debug/ams_mitm.elf
cp stratosphere/boot/boot.elf atmosphere-$(AMSVER)-debug/boot.elf
cp stratosphere/boot2/boot2.elf atmosphere-$(AMSVER)-debug/boot2.elf

View File

@@ -1,4 +1,30 @@
# 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
+ 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).

View File

@@ -25,12 +25,6 @@ set_mitm enables intercepting requests to the system settings service. It curren
+ `ns` system module and games (to allow for overriding game locales)
+ All firmware debug settings requests (to allow modification of system settings not directly exposed to the user)
## dns_mitm
dns_mitm enables intercepting requests to dns resolution services, to enable redirecting requests for specified hostnames.
For documentation, see [here](../../features/dns_mitm.md).
### Firmware Version
set_mitm intercepts the `GetFirmwareVersion` command, if the requester is `qlaunch` or `maintenance`.
It modifies the `display_version` field of the returned system version, causing the version to display
@@ -39,3 +33,8 @@ in settings as `#.#.#|AMS #.#.#|?` with `? = S` when running under system eMMC o
### System Settings
set_mitm intercepts the `GetSettingsItemValueSize` and `GetSettingsItemValue` commands for all requesters.
It does so in order to enable user configuration of system settings, which are parsed from `/atmosphere/system_settings.ini` on boot. See [here](../../features/configurations.md) for more information on the system settings format.
## dns_mitm
dns_mitm enables intercepting requests to dns resolution services, to enable redirecting requests for specified hostnames.
For documentation, see [here](../../features/dns_mitm.md).

View File

@@ -11,6 +11,8 @@ 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 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.
### Hosts file selection

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
branch = master
commit = 6c11c07e2a7f03952a4e70eb89b47bf528de39c6
parent = 9e104bb83f1302e9f126542fbf57c7f666aae953
commit = bc08912dd31bb172467add8e24b4f0adac431939
parent = 71add1add8521e0c2115ec612c514400ac7ba688
method = merge
cmdver = 0.4.1

View File

@@ -45,7 +45,21 @@
namespace ams::kern {
static_assert(cpu::NumCores <= static_cast<s32>(BITSIZEOF(u64)));
static_assert(util::size(cpu::VirtualToPhysicalCoreMap) == BITSIZEOF(u64));
namespace cpu {
static constexpr inline size_t NumVirtualCores = BITSIZEOF(u64);
static constexpr inline u64 VirtualCoreMask = [] {
u64 mask = 0;
for (size_t i = 0; i < NumVirtualCores; ++i) {
mask |= (UINT64_C(1) << i);
}
return mask;
}();
}
static_assert(cpu::NumCores <= cpu::NumVirtualCores);
static_assert(util::size(cpu::VirtualToPhysicalCoreMap) == cpu::NumVirtualCores);
}

View File

@@ -22,7 +22,7 @@ namespace ams::kern {
/* Most fields have already been cleared by our constructor. */
/* Initial processes may run on all cores. */
m_core_mask = (1ul << cpu::NumCores) - 1;
m_core_mask = cpu::VirtualCoreMask;
/* Initial processes may use any user priority they like. */
m_priority_mask = ~0xFul;
@@ -55,18 +55,17 @@ namespace ams::kern {
const auto max_prio = cap.Get<CorePriority::LowestThreadPriority>();
const auto min_prio = cap.Get<CorePriority::HighestThreadPriority>();
R_UNLESS(min_core <= max_core, svc::ResultInvalidCombination());
R_UNLESS(min_prio <= max_prio, svc::ResultInvalidCombination());
R_UNLESS(max_core < cpu::NumCores, svc::ResultInvalidCoreId());
R_UNLESS(min_core <= max_core, svc::ResultInvalidCombination());
R_UNLESS(min_prio <= max_prio, svc::ResultInvalidCombination());
R_UNLESS(max_core < cpu::NumVirtualCores, svc::ResultInvalidCoreId());
MESOSPHERE_ASSERT(max_core < BITSIZEOF(u64));
MESOSPHERE_ASSERT(max_prio < BITSIZEOF(u64));
/* Set core mask. */
for (auto core_id = min_core; core_id <= max_core; core_id++) {
m_core_mask |= (1ul << core_id);
}
MESOSPHERE_ASSERT((m_core_mask & ((1ul << cpu::NumCores) - 1)) == m_core_mask);
MESOSPHERE_ASSERT((m_core_mask & cpu::VirtualCoreMask) == m_core_mask);
/* Set priority mask. */
for (auto prio = min_prio; prio <= max_prio; prio++) {

View File

@@ -216,7 +216,7 @@ namespace ams::kern::svc {
case ams::svc::InfoType_ThreadTickCount:
{
/* Verify the requested core is valid. */
const bool core_valid = (info_subtype == static_cast<u64>(-1ul)) || (info_subtype < util::size(cpu::VirtualToPhysicalCoreMap));
const bool core_valid = (info_subtype == static_cast<u64>(-1ul)) || (info_subtype < cpu::NumVirtualCores);
R_UNLESS(core_valid, svc::ResultInvalidCombination());
/* Get the thread from its handle. */

View File

@@ -21,8 +21,8 @@ namespace ams::kern::svc {
namespace {
constexpr bool IsValidCoreId(int32_t core_id) {
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumCores));
constexpr bool IsValidVirtualCoreId(int32_t core_id) {
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumVirtualCores));
}
void ExitProcess() {
@@ -275,7 +275,7 @@ namespace ams::kern::svc {
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
/* Validate the core id. */
R_UNLESS(IsValidCoreId(core_id), svc::ResultInvalidCoreId());
R_UNLESS(IsValidVirtualCoreId(core_id), svc::ResultInvalidCoreId());
R_UNLESS(((1ul << core_id) & process->GetCoreMask()) != 0, svc::ResultInvalidCoreId());
/* Validate the priority. */

View File

@@ -21,8 +21,8 @@ namespace ams::kern::svc {
namespace {
constexpr bool IsValidCoreId(int32_t core_id) {
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumCores));
constexpr bool IsValidVirtualCoreId(int32_t core_id) {
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumVirtualCores));
}
Result CreateThread(ams::svc::Handle *out, ams::svc::ThreadFunc f, uintptr_t arg, uintptr_t stack_bottom, int32_t priority, int32_t core_id) {
@@ -33,7 +33,7 @@ namespace ams::kern::svc {
}
/* Validate arguments. */
R_UNLESS(IsValidCoreId(core_id), svc::ResultInvalidCoreId());
R_UNLESS(IsValidVirtualCoreId(core_id), svc::ResultInvalidCoreId());
R_UNLESS(((1ul << core_id) & process.GetCoreMask()) != 0, svc::ResultInvalidCoreId());
R_UNLESS(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority, svc::ResultInvalidPriority());
@@ -168,7 +168,7 @@ namespace ams::kern::svc {
R_UNLESS(affinity_mask != 0, svc::ResultInvalidCombination());
/* Validate the core id. */
if (IsValidCoreId(core_id)) {
if (IsValidVirtualCoreId(core_id)) {
R_UNLESS(((1ul << core_id) & affinity_mask) != 0, svc::ResultInvalidCombination());
} else {
R_UNLESS(core_id == ams::svc::IdealCoreNoUpdate || core_id == ams::svc::IdealCoreDontCare, svc::ResultInvalidCoreId());

View File

@@ -79,6 +79,7 @@
#include <stratosphere/spl.hpp>
#include <stratosphere/time.hpp>
#include <stratosphere/updater.hpp>
#include <stratosphere/usb.hpp>
#include <stratosphere/wec.hpp>
/* Include FS last. */

View File

@@ -19,7 +19,7 @@
namespace ams::psc {
enum PmModuleId : u16 {
enum PmModuleId : u32 {
PmModuleId_Usb = 4,
PmModuleId_Ethernet = 5,
PmModuleId_Fgm = 6,

View File

@@ -0,0 +1,21 @@
/*
* 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>

View File

@@ -0,0 +1,33 @@
/*
* 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)

View File

@@ -0,0 +1,41 @@
/*
* 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)

View File

@@ -0,0 +1,44 @@
/*
* 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)

View File

@@ -0,0 +1,145 @@
/*
* 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);
};
}

View File

@@ -0,0 +1,60 @@
/*
* 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];
};
}

View File

@@ -0,0 +1,34 @@
/*
* 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;
}

View File

@@ -0,0 +1,223 @@
/*
* 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;
}
}

View File

@@ -40,14 +40,12 @@ namespace ams::hid {
/* Helper. */
void InitializeHid() {
R_ABORT_UNLESS(smInitialize());
ON_SCOPE_EXIT { smExit(); };
{
sm::DoWithSession([&]() {
R_ABORT_UNLESS(hidInitialize());
hidInitializeNpad();
R_ABORT_UNLESS(hidSetSupportedNpadIdType(NpadIdTypes, NumNpadIdTypes));
R_ABORT_UNLESS(hidSetSupportedNpadStyleSet(HidNpadStyleSet_NpadStandard | HidNpadStyleTag_NpadSystemExt));
}
});
}
Result EnsureHidInitialized() {

View File

@@ -26,7 +26,7 @@ namespace ams::ncm {
MakeContentPathFunction make_content_path_func;
bool disabled;
protected:
ContentStorageImplBase() { /* ... */ }
ContentStorageImplBase() : make_content_path_func(), disabled(false) { /* ... */ }
protected:
/* Helpers. */
Result EnsureEnabled() const {

View File

@@ -46,9 +46,9 @@ namespace ams::psc {
Result PmModule::Initialize(const PmModuleId mid, const PmModuleId *dependencies, u32 dependency_count, os::EventClearMode clear_mode) {
R_UNLESS(!this->initialized, psc::ResultAlreadyInitialized());
static_assert(sizeof(*dependencies) == sizeof(u16));
static_assert(sizeof(*dependencies) == sizeof(u32));
::PscPmModule module;
R_TRY(::pscmGetPmModule(std::addressof(module), static_cast<::PscPmModuleId>(mid), reinterpret_cast<const u16 *>(dependencies), dependency_count, clear_mode == os::EventClearMode_AutoClear));
R_TRY(::pscmGetPmModule(std::addressof(module), static_cast<::PscPmModuleId>(mid), reinterpret_cast<const u32 *>(dependencies), dependency_count, clear_mode == os::EventClearMode_AutoClear));
this->intf = RemoteObjectFactory::CreateSharedEmplaced<psc::sf::IPmModule, RemotePmModule>(module);
this->system_event.AttachReadableHandle(module.event.revent, false, clear_mode);

View File

@@ -0,0 +1,45 @@
/*
* 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;
}
};
}

View File

@@ -0,0 +1,803 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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);
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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>);
}

View File

@@ -0,0 +1,139 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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>);
}

View File

@@ -0,0 +1,33 @@
/*
* 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();
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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>);
}

View File

@@ -0,0 +1,114 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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>);
}

View File

@@ -16,7 +16,7 @@
#pragma once
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
#define ATMOSPHERE_RELEASE_VERSION_MINOR 17
#define ATMOSPHERE_RELEASE_VERSION_MINOR 18
#define ATMOSPHERE_RELEASE_VERSION_MICRO 1
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO

View File

@@ -59,6 +59,7 @@
#include <vapours/results/svc_results.hpp>
#include <vapours/results/time_results.hpp>
#include <vapours/results/updater_results.hpp>
#include <vapours/results/usb_results.hpp>
#include <vapours/results/vi_results.hpp>
/* Unofficial. */

View File

@@ -0,0 +1,37 @@
/*
* 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);
}

View File

@@ -46,6 +46,9 @@
#include <vapours/util/util_format_string.hpp>
#include <vapours/util/util_range.hpp>
#include <vapours/util/util_fixed_map.hpp>
#include <vapours/util/util_fixed_set.hpp>
#ifdef ATMOSPHERE_IS_STRATOSPHERE
#include <vapours/util/util_mutex_utils.hpp>
#endif

View File

@@ -0,0 +1,220 @@
/*
* 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);
}
};
}

View File

@@ -0,0 +1,69 @@
/*
* 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(); }
};
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/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> {
/* ... */
};
}

View File

@@ -0,0 +1,998 @@
/*
* 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};
}
};
}

View File

@@ -13,8 +13,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>

View File

@@ -69,7 +69,21 @@ namespace ams::mitm::socket::resolver {
"127.0.0.1 receive-%.dg.srv.nintendo.net receive-%.er.srv.nintendo.net\n";
constinit os::SdkMutex g_redirection_lock;
std::unordered_map<std::string, ams::socket::InAddrT> g_redirection_map;
std::vector<std::pair<std::string, ams::socket::InAddrT>> g_redirection_list;
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] = {};
@@ -111,6 +125,8 @@ namespace ams::mitm::socket::resolver {
current_address = 0;
work = static_cast<u32>(c - '0');
state = State::Ip1;
} else if (c == '\n') {
state = State::BeginLine;
} else {
state = State::IgnoredLine;
}
@@ -206,7 +222,7 @@ namespace ams::mitm::socket::resolver {
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
current_hostname[work] = '\x00';
g_redirection_map[static_cast<const char *>(current_hostname)] = current_address;
AddRedirection(current_hostname, current_address);
work = 0;
if (c == '\n') {
@@ -229,7 +245,7 @@ namespace ams::mitm::socket::resolver {
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
current_hostname[work] = '\x00';
g_redirection_map[static_cast<const char *>(current_hostname)] = current_address;
AddRedirection(current_hostname, current_address);
}
}
@@ -293,7 +309,7 @@ namespace ams::mitm::socket::resolver {
std::scoped_lock lk(g_redirection_lock);
/* Clear the redirections map. */
g_redirection_map.clear();
g_redirection_list.clear();
/* Open log file. */
::FsFile log_file;
@@ -362,7 +378,7 @@ namespace ams::mitm::socket::resolver {
/* Print the redirections. */
Log(log_file, "Redirections:\n");
for (const auto &[host, address] : g_redirection_map) {
for (const auto &[host, address] : g_redirection_list) {
Log(log_file, " `%s` -> %u.%u.%u.%u\n", host.c_str(), (address >> 0) & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF, (address >> 24) & 0xFF);
}
}
@@ -370,7 +386,7 @@ namespace ams::mitm::socket::resolver {
bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname) {
std::scoped_lock lk(g_redirection_lock);
for (const auto &[host, address] : g_redirection_map) {
for (const auto &[host, address] : g_redirection_list) {
if (wildcardcmp(host.c_str(), hostname)) {
*out = address;
return true;

View File

@@ -111,10 +111,12 @@ namespace ams::mitm::socket::resolver {
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
u16 port = 0;
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
port *= 10;
port += *cur - '0';
if (srv.GetPointer() != nullptr) {
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
port *= 10;
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);
@@ -162,10 +164,12 @@ namespace ams::mitm::socket::resolver {
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
u16 port = 0;
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
port *= 10;
port += *cur - '0';
if (srv.GetPointer() != nullptr) {
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
port *= 10;
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);

View File

@@ -380,7 +380,7 @@ namespace ams::mitm::fs {
/* Try to get a storage from the cache. */
{
std::shared_ptr<fs::IStorage> cached_storage = GetStorageCacheEntry(this->client_info.program_id);
std::shared_ptr<fs::IStorage> cached_storage = GetStorageCacheEntry(data_id);
if (cached_storage != nullptr) {
out.SetValue(MakeSharedStorage(cached_storage), target_object_id);
return ResultSuccess();
@@ -403,7 +403,7 @@ namespace ams::mitm::fs {
new_storage = std::move(layered_storage);
}
SetStorageCacheEntry(this->client_info.program_id, &new_storage);
SetStorageCacheEntry(data_id, &new_storage);
out.SetValue(MakeSharedStorage(new_storage), target_object_id);
}