Compare commits
78
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e397cf57b5 | ||
|
|
e51659c11e | ||
|
|
782f9248c2 | ||
|
|
b4613f538a | ||
|
|
440a1d8de0 | ||
|
|
89e1819037 | ||
|
|
4427f8b29a | ||
|
|
1d86b7557a | ||
|
|
78304982fe | ||
|
|
376f1a4432 | ||
|
|
2f5545f8de | ||
|
|
921dae5e83 | ||
|
|
15805f4e01 | ||
|
|
851c5d67ae | ||
|
|
14248685af | ||
|
|
cc3db2aa43 | ||
|
|
aaec1562f8 | ||
|
|
a9abf4e7f8 | ||
|
|
b835d76311 | ||
|
|
b7725812ac | ||
|
|
9f3bf6d157 | ||
|
|
e31c15606b | ||
|
|
e22e0eb8d7 | ||
|
|
a755f24369 | ||
|
|
59fd910355 | ||
|
|
654427d4d0 | ||
|
|
4ace69de9c | ||
|
|
82979296d2 | ||
|
|
9d69206cd0 | ||
|
|
822ca65d69 | ||
|
|
3bfba23362 | ||
|
|
68f5aff64f | ||
|
|
9513abbb0a | ||
|
|
e73ac40eaa | ||
|
|
aed884d121 | ||
|
|
bd1c4ec9a0 | ||
|
|
fcdbf0bc53 | ||
|
|
4d220964df | ||
|
|
2c6e4ce0ad | ||
|
|
36a97dd8a2 | ||
|
|
d6e830d877 | ||
|
|
f21ab654db | ||
|
|
8d3ff2b127 | ||
|
|
ad53dc0106 | ||
|
|
8df2a98f75 | ||
|
|
482a03f8a5 | ||
|
|
07823b61a1 | ||
|
|
28181919a6 | ||
|
|
a9e9570d84 | ||
|
|
a40e5b2def | ||
|
|
c33faabb27 | ||
|
|
f2b61ff073 | ||
|
|
7da8e3f812 | ||
|
|
2dbfac652e | ||
|
|
60c2e9e675 | ||
|
|
6325c3044c | ||
|
|
e58e3719d8 | ||
|
|
a7fda84902 | ||
|
|
4c1c8801a5 | ||
|
|
bbc1437188 | ||
|
|
d3783fcc52 | ||
|
|
01fc969a5f | ||
|
|
366e900376 | ||
|
|
55d272efe6 | ||
|
|
44000971e2 | ||
|
|
675f23aedc | ||
|
|
4de0f1e1c8 | ||
|
|
e9d19add7d | ||
|
|
433e764bb0 | ||
|
|
b54cdeb284 | ||
|
|
0740758b25 | ||
|
|
7761e44d18 | ||
|
|
d2ea592ddb | ||
|
|
c17655ce74 | ||
|
|
7606da5611 | ||
|
|
ba02d564f8 | ||
|
|
f9259c0383 | ||
|
|
af809b491e |
No files matched your search
@@ -0,0 +1,5 @@
|
||||
steps:
|
||||
- script: mkdir artifacts || echo 'X' > artifacts/T1.txt
|
||||
- publish: artifacts
|
||||
artifact: 'yuzu-$(BuildName)-$(BuildSuffix)'
|
||||
displayName: 'Upload Artifacts'
|
||||
@@ -3,17 +3,18 @@ parameters:
|
||||
cache: 'false'
|
||||
|
||||
steps:
|
||||
- script: export DATE=`date '+%Y.%m.%d'` && export CI=true && AZURE_REPO_NAME=yuzu-emu/yuzu-$(BuildName) && AZURE_REPO_TAG=$(BuildName)-$DATE
|
||||
displayName: 'Determine Build Name'
|
||||
- task: DockerInstaller@0
|
||||
displayName: 'Prepare Environment'
|
||||
inputs:
|
||||
dockerVersion: '17.09.0-ce'
|
||||
- ${{ if eq(parameters.cache, 'true') }}:
|
||||
- task: CacheBeta@0
|
||||
displayName: 'Cache Build System'
|
||||
inputs:
|
||||
key: yuzu-v1-$(BuildName)-$(BuildSuffix)-$(CacheSuffix)
|
||||
path: $(System.DefaultWorkingDirectory)/ccache
|
||||
cacheHitVar: CACHE_RESTORED
|
||||
- task: CacheBeta@0
|
||||
displayName: 'Cache Build System'
|
||||
inputs:
|
||||
key: yuzu-v1-$(BuildName)-$(BuildSuffix)-$(CacheSuffix)
|
||||
path: $(System.DefaultWorkingDirectory)/ccache
|
||||
cacheHitVar: CACHE_RESTORED
|
||||
- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/exec.sh && ./.ci/scripts/$(ScriptFolder)/exec.sh
|
||||
displayName: 'Build'
|
||||
- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/upload.sh && RELEASE_NAME=$(BuildName) ./.ci/scripts/$(ScriptFolder)/upload.sh
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
steps:
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download Windows Release'
|
||||
inputs:
|
||||
artifactName: 'yuzu-$(BuildName)-windows-mingw'
|
||||
buildType: 'current'
|
||||
targetPath: '$(Build.ArtifactStagingDirectory)'
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download Linux Release'
|
||||
inputs:
|
||||
artifactName: 'yuzu-$(BuildName)-linux'
|
||||
buildType: 'current'
|
||||
targetPath: '$(Build.ArtifactStagingDirectory)'
|
||||
@@ -0,0 +1,11 @@
|
||||
steps:
|
||||
- template: ./release-download.yml
|
||||
- task: GitHubRelease@0
|
||||
inputs:
|
||||
action: 'create'
|
||||
title: 'yuzu $(BuildName) #$(Build.BuildId)'
|
||||
assets: '$(Build.ArtifactStagingDirectory)/*'
|
||||
gitHubConnection: $(GitHubReleaseConnectionName)
|
||||
repositoryName: '$(Build.Repository.Name)'
|
||||
target: '$(Build.SourceVersion)'
|
||||
tagSource: 'auto'
|
||||
@@ -0,0 +1,10 @@
|
||||
steps:
|
||||
- template: ./release-download.yml
|
||||
- task: UniversalPackages@0
|
||||
displayName: Publish Artifacts
|
||||
inputs:
|
||||
command: publish
|
||||
publishDirectory: '$(Build.ArtifactStagingDirectory)'
|
||||
vstsFeedPublish: 'yuzu-$(BuildName)'
|
||||
vstsFeedPackagePublish: 'main'
|
||||
packagePublishDescription: 'Yuzu Windows and Linux Executable Packages'
|
||||
@@ -0,0 +1,8 @@
|
||||
trigger:
|
||||
- master
|
||||
|
||||
stages:
|
||||
- stage: merge
|
||||
displayName: 'merge'
|
||||
jobs:
|
||||
- template: ./templates/merge.yml
|
||||
@@ -2,12 +2,7 @@ trigger:
|
||||
- master
|
||||
|
||||
stages:
|
||||
- stage: merge
|
||||
displayName: 'merge'
|
||||
jobs:
|
||||
- template: ./templates/merge.yml
|
||||
- stage: format
|
||||
dependsOn: merge
|
||||
displayName: 'format'
|
||||
jobs:
|
||||
- job: format
|
||||
@@ -17,9 +12,17 @@ stages:
|
||||
steps:
|
||||
- template: ./templates/format-check.yml
|
||||
- stage: build
|
||||
displayName: 'build'
|
||||
dependsOn: format
|
||||
displayName: 'build'
|
||||
jobs:
|
||||
- template: ./templates/build-standard.yml
|
||||
parameters:
|
||||
cache: 'true'
|
||||
- stage: release
|
||||
displayName: 'Release'
|
||||
dependsOn: build
|
||||
jobs:
|
||||
- job: github
|
||||
displayName: 'GitHub Release'
|
||||
steps:
|
||||
- template: ./templates/release-github.yml
|
||||
@@ -107,6 +107,11 @@ Stream::State AudioRenderer::GetStreamState() const {
|
||||
return stream->GetState();
|
||||
}
|
||||
|
||||
static constexpr u32 VersionFromRevision(u32_le rev) {
|
||||
// "REV7" -> 7
|
||||
return ((rev >> 24) & 0xff) - 0x30;
|
||||
}
|
||||
|
||||
std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
|
||||
// Copy UpdateDataHeader struct
|
||||
UpdateDataHeader config{};
|
||||
@@ -166,6 +171,11 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
|
||||
// Copy output header
|
||||
UpdateDataHeader response_data{worker_params};
|
||||
std::vector<u8> output_params(response_data.total_size);
|
||||
const auto audren_revision = VersionFromRevision(config.revision);
|
||||
if (audren_revision >= 5) {
|
||||
response_data.frame_count = 0x10;
|
||||
response_data.total_size += 0x10;
|
||||
}
|
||||
std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
|
||||
|
||||
// Copy output memory pool entries
|
||||
|
||||
@@ -194,21 +194,24 @@ struct UpdateDataHeader {
|
||||
mixes_size = 0x0;
|
||||
sinks_size = config.sink_count * 0x20;
|
||||
performance_manager_size = 0x10;
|
||||
frame_count = 0;
|
||||
total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
|
||||
effects_size + sinks_size + performance_manager_size;
|
||||
}
|
||||
|
||||
u32_le revision;
|
||||
u32_le behavior_size;
|
||||
u32_le memory_pools_size;
|
||||
u32_le voices_size;
|
||||
u32_le voice_resource_size;
|
||||
u32_le effects_size;
|
||||
u32_le mixes_size;
|
||||
u32_le sinks_size;
|
||||
u32_le performance_manager_size;
|
||||
INSERT_PADDING_WORDS(6);
|
||||
u32_le total_size;
|
||||
u32_le revision{};
|
||||
u32_le behavior_size{};
|
||||
u32_le memory_pools_size{};
|
||||
u32_le voices_size{};
|
||||
u32_le voice_resource_size{};
|
||||
u32_le effects_size{};
|
||||
u32_le mixes_size{};
|
||||
u32_le sinks_size{};
|
||||
u32_le performance_manager_size{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u32_le frame_count{};
|
||||
INSERT_PADDING_WORDS(4);
|
||||
u32_le total_size{};
|
||||
};
|
||||
static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@ if (DEFINED ENV{CI})
|
||||
elseif(DEFINED ENV{APPVEYOR})
|
||||
set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME})
|
||||
set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME})
|
||||
elseif(DEFINED ENV{AZURE})
|
||||
set(BUILD_REPOSITORY $ENV{AZURE_REPO_NAME})
|
||||
set(BUILD_TAG $ENV{AZURE_REPO_TAG})
|
||||
endif()
|
||||
endif()
|
||||
add_custom_command(OUTPUT scm_rev.cpp
|
||||
|
||||
+6
-10
@@ -28,18 +28,14 @@ __declspec(noinline, noreturn)
|
||||
}
|
||||
|
||||
#define ASSERT(_a_) \
|
||||
do \
|
||||
if (!(_a_)) { \
|
||||
assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
|
||||
} \
|
||||
while (0)
|
||||
if (!(_a_)) { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed!"); \
|
||||
}
|
||||
|
||||
#define ASSERT_MSG(_a_, ...) \
|
||||
do \
|
||||
if (!(_a_)) { \
|
||||
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
|
||||
} \
|
||||
while (0)
|
||||
if (!(_a_)) { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed! " __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!")
|
||||
#define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#define LOAD_DIR "load"
|
||||
#define DUMP_DIR "dump"
|
||||
#define SHADER_DIR "shader"
|
||||
#define RESCALING_DIR "rescaling"
|
||||
#define LOG_DIR "log"
|
||||
|
||||
// Filenames
|
||||
|
||||
@@ -695,6 +695,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
|
||||
paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::ShaderDir, user_path + SHADER_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::RescalingDir, user_path + RESCALING_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP);
|
||||
// TODO: Put the logs in a better location for each OS
|
||||
|
||||
@@ -33,6 +33,7 @@ enum class UserPath {
|
||||
LoadDir,
|
||||
DumpDir,
|
||||
ShaderDir,
|
||||
RescalingDir,
|
||||
SysDataDir,
|
||||
UserDir,
|
||||
};
|
||||
|
||||
@@ -248,6 +248,9 @@ void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::PrepareReschedule() {
|
||||
if (jit == nullptr)
|
||||
return;
|
||||
|
||||
jit->HaltExecution();
|
||||
}
|
||||
|
||||
|
||||
@@ -163,6 +163,7 @@ struct System::Impl {
|
||||
gpu_core = VideoCore::CreateGPU(system);
|
||||
|
||||
is_powered_on = true;
|
||||
exit_lock = false;
|
||||
|
||||
LOG_DEBUG(Core, "Initialized OK");
|
||||
|
||||
@@ -249,6 +250,7 @@ struct System::Impl {
|
||||
perf_stats->GetMeanFrametime());
|
||||
|
||||
is_powered_on = false;
|
||||
exit_lock = false;
|
||||
|
||||
// Shutdown emulation session
|
||||
renderer.reset();
|
||||
@@ -333,6 +335,7 @@ struct System::Impl {
|
||||
std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager;
|
||||
CpuCoreManager cpu_core_manager;
|
||||
bool is_powered_on = false;
|
||||
bool exit_lock = false;
|
||||
|
||||
std::unique_ptr<Memory::CheatEngine> cheat_engine;
|
||||
std::unique_ptr<Tools::Freezer> memory_freezer;
|
||||
@@ -396,6 +399,12 @@ void System::PrepareReschedule() {
|
||||
CurrentCpuCore().PrepareReschedule();
|
||||
}
|
||||
|
||||
void System::PrepareReschedule(const u32 core_index) {
|
||||
if (core_index < GlobalScheduler().CpuCoresCount()) {
|
||||
CpuCore(core_index).PrepareReschedule();
|
||||
}
|
||||
}
|
||||
|
||||
PerfStatsResults System::GetAndResetPerfStats() {
|
||||
return impl->GetAndResetPerfStats();
|
||||
}
|
||||
@@ -436,6 +445,16 @@ const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const {
|
||||
return CpuCore(core_index).Scheduler();
|
||||
}
|
||||
|
||||
/// Gets the global scheduler
|
||||
Kernel::GlobalScheduler& System::GlobalScheduler() {
|
||||
return impl->kernel.GlobalScheduler();
|
||||
}
|
||||
|
||||
/// Gets the global scheduler
|
||||
const Kernel::GlobalScheduler& System::GlobalScheduler() const {
|
||||
return impl->kernel.GlobalScheduler();
|
||||
}
|
||||
|
||||
Kernel::Process* System::CurrentProcess() {
|
||||
return impl->kernel.CurrentProcess();
|
||||
}
|
||||
@@ -629,6 +648,14 @@ const Service::APM::Controller& System::GetAPMController() const {
|
||||
return impl->apm_controller;
|
||||
}
|
||||
|
||||
void System::SetExitLock(bool locked) {
|
||||
impl->exit_lock = locked;
|
||||
}
|
||||
|
||||
bool System::GetExitLock() const {
|
||||
return impl->exit_lock;
|
||||
}
|
||||
|
||||
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
|
||||
return impl->Init(*this, emu_window);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ class VfsFilesystem;
|
||||
} // namespace FileSys
|
||||
|
||||
namespace Kernel {
|
||||
class GlobalScheduler;
|
||||
class KernelCore;
|
||||
class Process;
|
||||
class Scheduler;
|
||||
@@ -179,6 +180,9 @@ public:
|
||||
/// Prepare the core emulation for a reschedule
|
||||
void PrepareReschedule();
|
||||
|
||||
/// Prepare the core emulation for a reschedule
|
||||
void PrepareReschedule(u32 core_index);
|
||||
|
||||
/// Gets and resets core performance statistics
|
||||
PerfStatsResults GetAndResetPerfStats();
|
||||
|
||||
@@ -233,6 +237,12 @@ public:
|
||||
/// Gets the scheduler for the CPU core with the specified index
|
||||
const Kernel::Scheduler& Scheduler(std::size_t core_index) const;
|
||||
|
||||
/// Gets the global scheduler
|
||||
Kernel::GlobalScheduler& GlobalScheduler();
|
||||
|
||||
/// Gets the global scheduler
|
||||
const Kernel::GlobalScheduler& GlobalScheduler() const;
|
||||
|
||||
/// Provides a pointer to the current process
|
||||
Kernel::Process* CurrentProcess();
|
||||
|
||||
@@ -326,6 +336,10 @@ public:
|
||||
|
||||
const Service::APM::Controller& GetAPMController() const;
|
||||
|
||||
void SetExitLock(bool locked);
|
||||
|
||||
bool GetExitLock() const;
|
||||
|
||||
private:
|
||||
System();
|
||||
|
||||
|
||||
+9
-11
@@ -52,7 +52,8 @@ bool CpuBarrier::Rendezvous() {
|
||||
|
||||
Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier,
|
||||
std::size_t core_index)
|
||||
: cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} {
|
||||
: cpu_barrier{cpu_barrier}, global_scheduler{system.GlobalScheduler()},
|
||||
core_timing{system.CoreTiming()}, core_index{core_index} {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index);
|
||||
#else
|
||||
@@ -60,7 +61,7 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba
|
||||
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
|
||||
#endif
|
||||
|
||||
scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface);
|
||||
scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface, core_index);
|
||||
}
|
||||
|
||||
Cpu::~Cpu() = default;
|
||||
@@ -81,6 +82,8 @@ void Cpu::RunLoop(bool tight_loop) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reschedule();
|
||||
|
||||
// If we don't have a currently active thread then don't execute instructions,
|
||||
// instead advance to the next event and try to yield to the next thread
|
||||
if (Kernel::GetCurrentThread() == nullptr) {
|
||||
@@ -92,7 +95,6 @@ void Cpu::RunLoop(bool tight_loop) {
|
||||
core_timing.Advance();
|
||||
}
|
||||
|
||||
PrepareReschedule();
|
||||
} else {
|
||||
if (IsMainCore()) {
|
||||
core_timing.Advance();
|
||||
@@ -114,18 +116,14 @@ void Cpu::SingleStep() {
|
||||
|
||||
void Cpu::PrepareReschedule() {
|
||||
arm_interface->PrepareReschedule();
|
||||
reschedule_pending = true;
|
||||
}
|
||||
|
||||
void Cpu::Reschedule() {
|
||||
if (!reschedule_pending) {
|
||||
return;
|
||||
}
|
||||
|
||||
reschedule_pending = false;
|
||||
// Lock the global kernel mutex when we manipulate the HLE state
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
scheduler->Reschedule();
|
||||
std::lock_guard lock(HLE::g_hle_lock);
|
||||
|
||||
global_scheduler.SelectThread(core_index);
|
||||
scheduler->TryDoContextSwitch();
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
+3
-1
@@ -12,8 +12,9 @@
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
class GlobalScheduler;
|
||||
class Scheduler;
|
||||
}
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
@@ -90,6 +91,7 @@ private:
|
||||
|
||||
std::unique_ptr<ARM_Interface> arm_interface;
|
||||
CpuBarrier& cpu_barrier;
|
||||
Kernel::GlobalScheduler& global_scheduler;
|
||||
std::unique_ptr<Kernel::Scheduler> scheduler;
|
||||
Timing::CoreTiming& core_timing;
|
||||
|
||||
|
||||
@@ -202,13 +202,11 @@ void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
|
||||
}
|
||||
|
||||
static Kernel::Thread* FindThreadById(s64 id) {
|
||||
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
|
||||
const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
|
||||
for (auto& thread : threads) {
|
||||
if (thread->GetThreadID() == static_cast<u64>(id)) {
|
||||
current_core = core;
|
||||
return thread.get();
|
||||
}
|
||||
const auto& threads = Core::System::GetInstance().GlobalScheduler().GetThreadList();
|
||||
for (auto& thread : threads) {
|
||||
if (thread->GetThreadID() == static_cast<u64>(id)) {
|
||||
current_core = thread->GetProcessorID();
|
||||
return thread.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
@@ -646,11 +644,9 @@ static void HandleQuery() {
|
||||
SendReply(buffer.c_str());
|
||||
} else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
|
||||
std::string val = "m";
|
||||
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
|
||||
const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
|
||||
for (const auto& thread : threads) {
|
||||
val += fmt::format("{:x},", thread->GetThreadID());
|
||||
}
|
||||
const auto& threads = Core::System::GetInstance().GlobalScheduler().GetThreadList();
|
||||
for (const auto& thread : threads) {
|
||||
val += fmt::format("{:x},", thread->GetThreadID());
|
||||
}
|
||||
val.pop_back();
|
||||
SendReply(val.c_str());
|
||||
@@ -660,13 +656,11 @@ static void HandleQuery() {
|
||||
std::string buffer;
|
||||
buffer += "l<?xml version=\"1.0\"?>";
|
||||
buffer += "<threads>";
|
||||
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
|
||||
const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
|
||||
for (const auto& thread : threads) {
|
||||
buffer +=
|
||||
fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
|
||||
thread->GetThreadID(), core, thread->GetThreadID());
|
||||
}
|
||||
const auto& threads = Core::System::GetInstance().GlobalScheduler().GetThreadList();
|
||||
for (const auto& thread : threads) {
|
||||
buffer +=
|
||||
fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
|
||||
thread->GetThreadID(), thread->GetProcessorID(), thread->GetThreadID());
|
||||
}
|
||||
buffer += "</threads>";
|
||||
SendReply(buffer.c_str());
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Kernel {
|
||||
namespace {
|
||||
// Wake up num_to_wake (or all) threads in a vector.
|
||||
void WakeThreads(const std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) {
|
||||
auto& system = Core::System::GetInstance();
|
||||
// Only process up to 'target' threads, unless 'target' is <= 0, in which case process
|
||||
// them all.
|
||||
std::size_t last = waiting_threads.size();
|
||||
@@ -35,6 +36,7 @@ void WakeThreads(const std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_
|
||||
waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
|
||||
waiting_threads[i]->SetArbiterWaitAddress(0);
|
||||
waiting_threads[i]->ResumeFromWait();
|
||||
system.PrepareReschedule(waiting_threads[i]->GetProcessorID());
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
@@ -169,30 +171,22 @@ ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) {
|
||||
|
||||
current_thread->WakeAfterDelay(timeout);
|
||||
|
||||
system.CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
|
||||
system.PrepareReschedule(current_thread->GetProcessorID());
|
||||
return RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
std::vector<SharedPtr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(VAddr address) const {
|
||||
const auto RetrieveWaitingThreads = [this](std::size_t core_index,
|
||||
std::vector<SharedPtr<Thread>>& waiting_threads,
|
||||
VAddr arb_addr) {
|
||||
const auto& scheduler = system.Scheduler(core_index);
|
||||
const auto& thread_list = scheduler.GetThreadList();
|
||||
|
||||
for (const auto& thread : thread_list) {
|
||||
if (thread->GetArbiterWaitAddress() == arb_addr) {
|
||||
waiting_threads.push_back(thread);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Retrieve all threads that are waiting for this address.
|
||||
std::vector<SharedPtr<Thread>> threads;
|
||||
RetrieveWaitingThreads(0, threads, address);
|
||||
RetrieveWaitingThreads(1, threads, address);
|
||||
RetrieveWaitingThreads(2, threads, address);
|
||||
RetrieveWaitingThreads(3, threads, address);
|
||||
const auto& scheduler = system.GlobalScheduler();
|
||||
const auto& thread_list = scheduler.GetThreadList();
|
||||
|
||||
for (const auto& thread : thread_list) {
|
||||
if (thread->GetArbiterWaitAddress() == address) {
|
||||
threads.push_back(thread);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort them by priority, such that the highest priority ones come first.
|
||||
std::sort(threads.begin(), threads.end(),
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -88,7 +89,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
|
||||
}
|
||||
|
||||
struct KernelCore::Impl {
|
||||
explicit Impl(Core::System& system) : system{system} {}
|
||||
explicit Impl(Core::System& system) : system{system}, global_scheduler{system} {}
|
||||
|
||||
void Initialize(KernelCore& kernel) {
|
||||
Shutdown();
|
||||
@@ -140,6 +141,7 @@ struct KernelCore::Impl {
|
||||
// Lists all processes that exist in the current session.
|
||||
std::vector<SharedPtr<Process>> process_list;
|
||||
Process* current_process = nullptr;
|
||||
Kernel::GlobalScheduler global_scheduler;
|
||||
|
||||
SharedPtr<ResourceLimit> system_resource_limit;
|
||||
|
||||
@@ -203,6 +205,14 @@ const std::vector<SharedPtr<Process>>& KernelCore::GetProcessList() const {
|
||||
return impl->process_list;
|
||||
}
|
||||
|
||||
Kernel::GlobalScheduler& KernelCore::GlobalScheduler() {
|
||||
return impl->global_scheduler;
|
||||
}
|
||||
|
||||
const Kernel::GlobalScheduler& KernelCore::GlobalScheduler() const {
|
||||
return impl->global_scheduler;
|
||||
}
|
||||
|
||||
void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
|
||||
impl->named_ports.emplace(std::move(name), std::move(port));
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ class HandleTable;
|
||||
class Process;
|
||||
class ResourceLimit;
|
||||
class Thread;
|
||||
class GlobalScheduler;
|
||||
|
||||
/// Represents a single instance of the kernel.
|
||||
class KernelCore {
|
||||
@@ -75,6 +76,12 @@ public:
|
||||
/// Retrieves the list of processes.
|
||||
const std::vector<SharedPtr<Process>>& GetProcessList() const;
|
||||
|
||||
/// Gets the sole instance of the global scheduler
|
||||
Kernel::GlobalScheduler& GlobalScheduler();
|
||||
|
||||
/// Gets the sole instance of the global scheduler
|
||||
const Kernel::GlobalScheduler& GlobalScheduler() const;
|
||||
|
||||
/// Adds a port to the named port table
|
||||
void AddNamedPort(std::string name, SharedPtr<ClientPort> port);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@@ -78,7 +79,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
|
||||
// thread.
|
||||
ASSERT(requesting_thread == current_thread);
|
||||
|
||||
const u32 addr_value = Memory::Read32(address);
|
||||
u32 addr_value = Memory::Read32(address);
|
||||
|
||||
// If the mutex isn't being held, just return success.
|
||||
if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
|
||||
@@ -89,6 +90,20 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
// This a workaround where an unknown bug writes the mutex value to give ownership to a cond var
|
||||
// waiting thread.
|
||||
if (holding_thread->GetStatus() == ThreadStatus::WaitCondVar) {
|
||||
if (holding_thread->GetMutexWaitAddress() == address) {
|
||||
Release(address, holding_thread.get());
|
||||
addr_value = Memory::Read32(address);
|
||||
if (addr_value == 0)
|
||||
return RESULT_SUCCESS;
|
||||
else {
|
||||
holding_thread = handle_table.Get<Thread>(addr_value & Mutex::MutexOwnerMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until the mutex is released
|
||||
current_thread->SetMutexWaitAddress(address);
|
||||
current_thread->SetWaitHandle(requesting_thread_handle);
|
||||
@@ -104,14 +119,13 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode Mutex::Release(VAddr address) {
|
||||
ResultCode Mutex::Release(VAddr address, Thread* holding_thread) {
|
||||
// The mutex address must be 4-byte aligned
|
||||
if ((address % sizeof(u32)) != 0) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
|
||||
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address);
|
||||
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(holding_thread, address);
|
||||
|
||||
// There are no more threads waiting for the mutex, release it completely.
|
||||
if (thread == nullptr) {
|
||||
@@ -120,7 +134,7 @@ ResultCode Mutex::Release(VAddr address) {
|
||||
}
|
||||
|
||||
// Transfer the ownership of the mutex from the previous owner to the new one.
|
||||
TransferMutexOwnership(address, current_thread, thread);
|
||||
TransferMutexOwnership(address, holding_thread, thread);
|
||||
|
||||
u32 mutex_value = thread->GetWaitHandle();
|
||||
|
||||
@@ -140,6 +154,11 @@ ResultCode Mutex::Release(VAddr address) {
|
||||
thread->SetMutexWaitAddress(0);
|
||||
thread->SetWaitHandle(0);
|
||||
|
||||
if (thread->GetProcessorID() >= 0)
|
||||
system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||
if (holding_thread->GetProcessorID() >= 0)
|
||||
system.CpuCore(holding_thread->GetProcessorID()).PrepareReschedule();
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
} // namespace Kernel
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
Handle requesting_thread_handle);
|
||||
|
||||
/// Releases the mutex at the specified address.
|
||||
ResultCode Release(VAddr address);
|
||||
ResultCode Release(VAddr address, Thread* holding_thread);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
|
||||
@@ -213,10 +213,7 @@ void Process::PrepareForTermination() {
|
||||
}
|
||||
};
|
||||
|
||||
stop_threads(system.Scheduler(0).GetThreadList());
|
||||
stop_threads(system.Scheduler(1).GetThreadList());
|
||||
stop_threads(system.Scheduler(2).GetThreadList());
|
||||
stop_threads(system.Scheduler(3).GetThreadList());
|
||||
stop_threads(system.GlobalScheduler().GetThreadList());
|
||||
|
||||
FreeTLSRegion(tls_region_address);
|
||||
tls_region_address = 0;
|
||||
|
||||
+290
-155
@@ -1,8 +1,13 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
//
|
||||
// SelectThreads, Yield functions originally by TuxSH.
|
||||
// licensed under GPLv2 or later under exception provided by the author.
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include "common/assert.h"
|
||||
@@ -17,56 +22,277 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
std::mutex Scheduler::scheduler_mutex;
|
||||
GlobalScheduler::GlobalScheduler(Core::System& system) : system{system} {
|
||||
reselection_pending = false;
|
||||
}
|
||||
|
||||
Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core)
|
||||
: cpu_core{cpu_core}, system{system} {}
|
||||
void GlobalScheduler::AddThread(SharedPtr<Thread> thread) {
|
||||
thread_list.push_back(std::move(thread));
|
||||
}
|
||||
|
||||
Scheduler::~Scheduler() {
|
||||
for (auto& thread : thread_list) {
|
||||
thread->Stop();
|
||||
void GlobalScheduler::RemoveThread(const Thread* thread) {
|
||||
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
|
||||
thread_list.end());
|
||||
}
|
||||
|
||||
/*
|
||||
* UnloadThread selects a core and forces it to unload its current thread's context
|
||||
*/
|
||||
void GlobalScheduler::UnloadThread(s32 core) {
|
||||
Scheduler& sched = system.Scheduler(core);
|
||||
sched.UnloadThread();
|
||||
}
|
||||
|
||||
/*
|
||||
* SelectThread takes care of selecting the new scheduled thread.
|
||||
* It does it in 3 steps:
|
||||
* - First a thread is selected from the top of the priority queue. If no thread
|
||||
* is obtained then we move to step two, else we are done.
|
||||
* - Second we try to get a suggested thread that's not assigned to any core or
|
||||
* that is not the top thread in that core.
|
||||
* - Third is no suggested thread is found, we do a second pass and pick a running
|
||||
* thread in another core and swap it with its current thread.
|
||||
*/
|
||||
void GlobalScheduler::SelectThread(u32 core) {
|
||||
const auto update_thread = [](Thread* thread, Scheduler& sched) {
|
||||
if (thread != sched.selected_thread) {
|
||||
if (thread == nullptr) {
|
||||
++sched.idle_selection_count;
|
||||
}
|
||||
sched.selected_thread = thread;
|
||||
}
|
||||
sched.context_switch_pending = sched.selected_thread != sched.current_thread;
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
};
|
||||
Scheduler& sched = system.Scheduler(core);
|
||||
Thread* current_thread = nullptr;
|
||||
// Step 1: Get top thread in schedule queue.
|
||||
current_thread = scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front();
|
||||
if (current_thread) {
|
||||
update_thread(current_thread, sched);
|
||||
return;
|
||||
}
|
||||
// Step 2: Try selecting a suggested thread.
|
||||
Thread* winner = nullptr;
|
||||
std::set<s32> sug_cores;
|
||||
for (auto thread : suggested_queue[core]) {
|
||||
s32 this_core = thread->GetProcessorID();
|
||||
Thread* thread_on_core = nullptr;
|
||||
if (this_core >= 0) {
|
||||
thread_on_core = scheduled_queue[this_core].front();
|
||||
}
|
||||
if (this_core < 0 || thread != thread_on_core) {
|
||||
winner = thread;
|
||||
break;
|
||||
}
|
||||
sug_cores.insert(this_core);
|
||||
}
|
||||
// if we got a suggested thread, select it, else do a second pass.
|
||||
if (winner && winner->GetPriority() > 2) {
|
||||
if (winner->IsRunning()) {
|
||||
UnloadThread(winner->GetProcessorID());
|
||||
}
|
||||
TransferToCore(winner->GetPriority(), core, winner);
|
||||
update_thread(winner, sched);
|
||||
return;
|
||||
}
|
||||
// Step 3: Select a suggested thread from another core
|
||||
for (auto& src_core : sug_cores) {
|
||||
auto it = scheduled_queue[src_core].begin();
|
||||
it++;
|
||||
if (it != scheduled_queue[src_core].end()) {
|
||||
Thread* thread_on_core = scheduled_queue[src_core].front();
|
||||
Thread* to_change = *it;
|
||||
if (thread_on_core->IsRunning() || to_change->IsRunning()) {
|
||||
UnloadThread(src_core);
|
||||
}
|
||||
TransferToCore(thread_on_core->GetPriority(), core, thread_on_core);
|
||||
current_thread = thread_on_core;
|
||||
break;
|
||||
}
|
||||
}
|
||||
update_thread(current_thread, sched);
|
||||
}
|
||||
|
||||
/*
|
||||
* YieldThread takes a thread and moves it to the back of the it's priority list
|
||||
* This operation can be redundant and no scheduling is changed if marked as so.
|
||||
*/
|
||||
void GlobalScheduler::YieldThread(Thread* yielding_thread) {
|
||||
// Note: caller should use critical section, etc.
|
||||
const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
|
||||
const u32 priority = yielding_thread->GetPriority();
|
||||
|
||||
// Yield the thread
|
||||
ASSERT_MSG(yielding_thread == scheduled_queue[core_id].front(priority),
|
||||
"Thread yielding without being in front");
|
||||
scheduled_queue[core_id].yield(priority);
|
||||
|
||||
Thread* winner = scheduled_queue[core_id].front(priority);
|
||||
AskForReselectionOrMarkRedundant(yielding_thread, winner);
|
||||
}
|
||||
|
||||
/*
|
||||
* YieldThreadAndBalanceLoad takes a thread and moves it to the back of the it's priority list.
|
||||
* Afterwards, tries to pick a suggested thread from the suggested queue that has worse time or
|
||||
* a better priority than the next thread in the core.
|
||||
* This operation can be redundant and no scheduling is changed if marked as so.
|
||||
*/
|
||||
void GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) {
|
||||
// Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section,
|
||||
// etc.
|
||||
const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
|
||||
const u32 priority = yielding_thread->GetPriority();
|
||||
|
||||
// Yield the thread
|
||||
ASSERT_MSG(yielding_thread == scheduled_queue[core_id].front(priority),
|
||||
"Thread yielding without being in front");
|
||||
scheduled_queue[core_id].yield(priority);
|
||||
|
||||
std::array<Thread*, NUM_CPU_CORES> current_threads;
|
||||
for (u32 i = 0; i < NUM_CPU_CORES; i++) {
|
||||
current_threads[i] = scheduled_queue[i].empty() ? nullptr : scheduled_queue[i].front();
|
||||
}
|
||||
|
||||
Thread* next_thread = scheduled_queue[core_id].front(priority);
|
||||
Thread* winner = nullptr;
|
||||
for (auto& thread : suggested_queue[core_id]) {
|
||||
const s32 source_core = thread->GetProcessorID();
|
||||
if (source_core >= 0) {
|
||||
if (current_threads[source_core] != nullptr) {
|
||||
if (thread == current_threads[source_core] ||
|
||||
current_threads[source_core]->GetPriority() < min_regular_priority) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (next_thread->GetLastRunningTicks() >= thread->GetLastRunningTicks() ||
|
||||
next_thread->GetPriority() < thread->GetPriority()) {
|
||||
if (thread->GetPriority() <= priority) {
|
||||
winner = thread;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (winner != nullptr) {
|
||||
if (winner != yielding_thread) {
|
||||
if (winner->IsRunning()) {
|
||||
UnloadThread(winner->GetProcessorID());
|
||||
}
|
||||
TransferToCore(winner->GetPriority(), core_id, winner);
|
||||
}
|
||||
} else {
|
||||
winner = next_thread;
|
||||
}
|
||||
|
||||
AskForReselectionOrMarkRedundant(yielding_thread, winner);
|
||||
}
|
||||
|
||||
/*
|
||||
* YieldThreadAndWaitForLoadBalancing takes a thread and moves it out of the scheduling queue
|
||||
* and into the suggested queue. If no thread can be squeduled afterwards in that core,
|
||||
* a suggested thread is obtained instead.
|
||||
* This operation can be redundant and no scheduling is changed if marked as so.
|
||||
*/
|
||||
void GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread) {
|
||||
// Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section,
|
||||
// etc.
|
||||
Thread* winner = nullptr;
|
||||
const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
|
||||
|
||||
// Remove the thread from its scheduled mlq, put it on the corresponding "suggested" one instead
|
||||
TransferToCore(yielding_thread->GetPriority(), -1, yielding_thread);
|
||||
|
||||
// If the core is idle, perform load balancing, excluding the threads that have just used this
|
||||
// function...
|
||||
if (scheduled_queue[core_id].empty()) {
|
||||
// Here, "current_threads" is calculated after the ""yield"", unlike yield -1
|
||||
std::array<Thread*, NUM_CPU_CORES> current_threads;
|
||||
for (u32 i = 0; i < NUM_CPU_CORES; i++) {
|
||||
current_threads[i] = scheduled_queue[i].empty() ? nullptr : scheduled_queue[i].front();
|
||||
}
|
||||
for (auto& thread : suggested_queue[core_id]) {
|
||||
const s32 source_core = thread->GetProcessorID();
|
||||
if (source_core < 0 || thread == current_threads[source_core]) {
|
||||
continue;
|
||||
}
|
||||
if (current_threads[source_core] == nullptr ||
|
||||
current_threads[source_core]->GetPriority() >= min_regular_priority) {
|
||||
winner = thread;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (winner != nullptr) {
|
||||
if (winner != yielding_thread) {
|
||||
if (winner->IsRunning()) {
|
||||
UnloadThread(winner->GetProcessorID());
|
||||
}
|
||||
TransferToCore(winner->GetPriority(), core_id, winner);
|
||||
}
|
||||
} else {
|
||||
winner = yielding_thread;
|
||||
}
|
||||
}
|
||||
|
||||
AskForReselectionOrMarkRedundant(yielding_thread, winner);
|
||||
}
|
||||
|
||||
void GlobalScheduler::Schedule(u32 priority, u32 core, Thread* thread) {
|
||||
ASSERT_MSG(thread->GetProcessorID() == core, "Thread must be assigned to this core.");
|
||||
scheduled_queue[core].add(thread, priority);
|
||||
}
|
||||
|
||||
void GlobalScheduler::SchedulePrepend(u32 priority, u32 core, Thread* thread) {
|
||||
ASSERT_MSG(thread->GetProcessorID() == core, "Thread must be assigned to this core.");
|
||||
scheduled_queue[core].add(thread, priority, false);
|
||||
}
|
||||
|
||||
void GlobalScheduler::AskForReselectionOrMarkRedundant(Thread* current_thread, Thread* winner) {
|
||||
if (current_thread == winner) {
|
||||
// TODO(blinkhawk): manage redundant operations, this is not implemented.
|
||||
// as its mostly an optimization.
|
||||
// current_thread->SetRedundantSchedulerOperation();
|
||||
} else {
|
||||
reselection_pending.store(true, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
GlobalScheduler::~GlobalScheduler() = default;
|
||||
|
||||
Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, u32 core_id)
|
||||
: system(system), cpu_core(cpu_core), core_id(core_id) {}
|
||||
|
||||
Scheduler::~Scheduler() = default;
|
||||
|
||||
bool Scheduler::HaveReadyThreads() const {
|
||||
std::lock_guard lock{scheduler_mutex};
|
||||
return !ready_queue.empty();
|
||||
return system.GlobalScheduler().HaveReadyThreads(core_id);
|
||||
}
|
||||
|
||||
Thread* Scheduler::GetCurrentThread() const {
|
||||
return current_thread.get();
|
||||
}
|
||||
|
||||
Thread* Scheduler::GetSelectedThread() const {
|
||||
return selected_thread.get();
|
||||
}
|
||||
|
||||
void Scheduler::SelectThreads() {
|
||||
system.GlobalScheduler().SelectThread(core_id);
|
||||
}
|
||||
|
||||
u64 Scheduler::GetLastContextSwitchTicks() const {
|
||||
return last_context_switch_time;
|
||||
}
|
||||
|
||||
Thread* Scheduler::PopNextReadyThread() {
|
||||
Thread* next = nullptr;
|
||||
Thread* thread = GetCurrentThread();
|
||||
|
||||
if (thread && thread->GetStatus() == ThreadStatus::Running) {
|
||||
if (ready_queue.empty()) {
|
||||
return thread;
|
||||
}
|
||||
// We have to do better than the current thread.
|
||||
// This call returns null when that's not possible.
|
||||
next = ready_queue.front();
|
||||
if (next == nullptr || next->GetPriority() >= thread->GetPriority()) {
|
||||
next = thread;
|
||||
}
|
||||
} else {
|
||||
if (ready_queue.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
next = ready_queue.front();
|
||||
void Scheduler::TryDoContextSwitch() {
|
||||
if (context_switch_pending) {
|
||||
SwitchContext();
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
Thread* previous_thread = GetCurrentThread();
|
||||
void Scheduler::UnloadThread() {
|
||||
Thread* const previous_thread = GetCurrentThread();
|
||||
Process* const previous_process = system.Kernel().CurrentProcess();
|
||||
|
||||
UpdateLastContextSwitchTime(previous_thread, previous_process);
|
||||
@@ -80,23 +306,52 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
if (previous_thread->GetStatus() == ThreadStatus::Running) {
|
||||
// This is only the case when a reschedule is triggered without the current thread
|
||||
// yielding execution (i.e. an event triggered, system core time-sliced, etc)
|
||||
ready_queue.add(previous_thread, previous_thread->GetPriority(), false);
|
||||
previous_thread->SetStatus(ThreadStatus::Ready);
|
||||
}
|
||||
previous_thread->SetIsRunning(false);
|
||||
}
|
||||
current_thread = nullptr;
|
||||
}
|
||||
|
||||
void Scheduler::SwitchContext() {
|
||||
Thread* const previous_thread = GetCurrentThread();
|
||||
Thread* const new_thread = GetSelectedThread();
|
||||
|
||||
context_switch_pending = false;
|
||||
if (new_thread == previous_thread) {
|
||||
return;
|
||||
}
|
||||
|
||||
Process* const previous_process = system.Kernel().CurrentProcess();
|
||||
|
||||
UpdateLastContextSwitchTime(previous_thread, previous_process);
|
||||
|
||||
// Save context for previous thread
|
||||
if (previous_thread) {
|
||||
cpu_core.SaveContext(previous_thread->GetContext());
|
||||
// Save the TPIDR_EL0 system register in case it was modified.
|
||||
previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
|
||||
|
||||
if (previous_thread->GetStatus() == ThreadStatus::Running) {
|
||||
// This is only the case when a reschedule is triggered without the current thread
|
||||
// yielding execution (i.e. an event triggered, system core time-sliced, etc)
|
||||
previous_thread->SetStatus(ThreadStatus::Ready);
|
||||
}
|
||||
previous_thread->SetIsRunning(false);
|
||||
}
|
||||
|
||||
// Load context of new thread
|
||||
if (new_thread) {
|
||||
ASSERT_MSG(new_thread->GetProcessorID() == this->core_id,
|
||||
"Thread must be assigned to this core.");
|
||||
ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready,
|
||||
"Thread must be ready to become running.");
|
||||
|
||||
// Cancel any outstanding wakeup events for this thread
|
||||
new_thread->CancelWakeupTimer();
|
||||
|
||||
current_thread = new_thread;
|
||||
|
||||
ready_queue.remove(new_thread, new_thread->GetPriority());
|
||||
new_thread->SetStatus(ThreadStatus::Running);
|
||||
new_thread->SetIsRunning(true);
|
||||
|
||||
auto* const thread_owner_process = current_thread->GetOwnerProcess();
|
||||
if (previous_process != thread_owner_process) {
|
||||
@@ -130,124 +385,4 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
|
||||
last_context_switch_time = most_recent_switch_ticks;
|
||||
}
|
||||
|
||||
void Scheduler::Reschedule() {
|
||||
std::lock_guard lock{scheduler_mutex};
|
||||
|
||||
Thread* cur = GetCurrentThread();
|
||||
Thread* next = PopNextReadyThread();
|
||||
|
||||
if (cur && next) {
|
||||
LOG_TRACE(Kernel, "context switch {} -> {}", cur->GetObjectId(), next->GetObjectId());
|
||||
} else if (cur) {
|
||||
LOG_TRACE(Kernel, "context switch {} -> idle", cur->GetObjectId());
|
||||
} else if (next) {
|
||||
LOG_TRACE(Kernel, "context switch idle -> {}", next->GetObjectId());
|
||||
}
|
||||
|
||||
SwitchContext(next);
|
||||
}
|
||||
|
||||
void Scheduler::AddThread(SharedPtr<Thread> thread) {
|
||||
std::lock_guard lock{scheduler_mutex};
|
||||
|
||||
thread_list.push_back(std::move(thread));
|
||||
}
|
||||
|
||||
void Scheduler::RemoveThread(Thread* thread) {
|
||||
std::lock_guard lock{scheduler_mutex};
|
||||
|
||||
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
|
||||
thread_list.end());
|
||||
}
|
||||
|
||||
void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
|
||||
std::lock_guard lock{scheduler_mutex};
|
||||
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
|
||||
ready_queue.add(thread, priority);
|
||||
}
|
||||
|
||||
void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
|
||||
std::lock_guard lock{scheduler_mutex};
|
||||
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
|
||||
ready_queue.remove(thread, priority);
|
||||
}
|
||||
|
||||
void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
|
||||
std::lock_guard lock{scheduler_mutex};
|
||||
if (thread->GetPriority() == priority) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If thread was ready, adjust queues
|
||||
if (thread->GetStatus() == ThreadStatus::Ready)
|
||||
ready_queue.adjust(thread, thread->GetPriority(), priority);
|
||||
}
|
||||
|
||||
Thread* Scheduler::GetNextSuggestedThread(u32 core, u32 maximum_priority) const {
|
||||
std::lock_guard lock{scheduler_mutex};
|
||||
|
||||
const u32 mask = 1U << core;
|
||||
for (auto* thread : ready_queue) {
|
||||
if ((thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority) {
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
|
||||
ASSERT(thread != nullptr);
|
||||
// Avoid yielding if the thread isn't even running.
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::Running);
|
||||
|
||||
// Sanity check that the priority is valid
|
||||
ASSERT(thread->GetPriority() < THREADPRIO_COUNT);
|
||||
|
||||
// Yield this thread -- sleep for zero time and force reschedule to different thread
|
||||
GetCurrentThread()->Sleep(0);
|
||||
}
|
||||
|
||||
void Scheduler::YieldWithLoadBalancing(Thread* thread) {
|
||||
ASSERT(thread != nullptr);
|
||||
const auto priority = thread->GetPriority();
|
||||
const auto core = static_cast<u32>(thread->GetProcessorID());
|
||||
|
||||
// Avoid yielding if the thread isn't even running.
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::Running);
|
||||
|
||||
// Sanity check that the priority is valid
|
||||
ASSERT(priority < THREADPRIO_COUNT);
|
||||
|
||||
// Sleep for zero time to be able to force reschedule to different thread
|
||||
GetCurrentThread()->Sleep(0);
|
||||
|
||||
Thread* suggested_thread = nullptr;
|
||||
|
||||
// Search through all of the cpu cores (except this one) for a suggested thread.
|
||||
// Take the first non-nullptr one
|
||||
for (unsigned cur_core = 0; cur_core < Core::NUM_CPU_CORES; ++cur_core) {
|
||||
const auto res =
|
||||
system.CpuCore(cur_core).Scheduler().GetNextSuggestedThread(core, priority);
|
||||
|
||||
// If scheduler provides a suggested thread
|
||||
if (res != nullptr) {
|
||||
// And its better than the current suggested thread (or is the first valid one)
|
||||
if (suggested_thread == nullptr ||
|
||||
suggested_thread->GetPriority() > res->GetPriority()) {
|
||||
suggested_thread = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a suggested thread was found, queue that for this core
|
||||
if (suggested_thread != nullptr)
|
||||
suggested_thread->ChangeCore(core, suggested_thread->GetAffinityMask());
|
||||
}
|
||||
|
||||
void Scheduler::YieldAndWaitForLoadBalancing(Thread* thread) {
|
||||
UNIMPLEMENTED_MSG("Wait for load balancing thread yield type is not implemented!");
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
+162
-111
@@ -20,124 +20,178 @@ namespace Kernel {
|
||||
|
||||
class Process;
|
||||
|
||||
class Scheduler final {
|
||||
class GlobalScheduler final {
|
||||
public:
|
||||
explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core);
|
||||
~Scheduler();
|
||||
|
||||
/// Returns whether there are any threads that are ready to run.
|
||||
bool HaveReadyThreads() const;
|
||||
|
||||
/// Reschedules to the next available thread (call after current thread is suspended)
|
||||
void Reschedule();
|
||||
|
||||
/// Gets the current running thread
|
||||
Thread* GetCurrentThread() const;
|
||||
|
||||
/// Gets the timestamp for the last context switch in ticks.
|
||||
u64 GetLastContextSwitchTicks() const;
|
||||
static constexpr u32 NUM_CPU_CORES = 4;
|
||||
|
||||
explicit GlobalScheduler(Core::System& system);
|
||||
~GlobalScheduler();
|
||||
/// Adds a new thread to the scheduler
|
||||
void AddThread(SharedPtr<Thread> thread);
|
||||
|
||||
/// Removes a thread from the scheduler
|
||||
void RemoveThread(Thread* thread);
|
||||
|
||||
/// Schedules a thread that has become "ready"
|
||||
void ScheduleThread(Thread* thread, u32 priority);
|
||||
|
||||
/// Unschedules a thread that was already scheduled
|
||||
void UnscheduleThread(Thread* thread, u32 priority);
|
||||
|
||||
/// Sets the priority of a thread in the scheduler
|
||||
void SetThreadPriority(Thread* thread, u32 priority);
|
||||
|
||||
/// Gets the next suggested thread for load balancing
|
||||
Thread* GetNextSuggestedThread(u32 core, u32 minimum_priority) const;
|
||||
|
||||
/**
|
||||
* YieldWithoutLoadBalancing -- analogous to normal yield on a system
|
||||
* Moves the thread to the end of the ready queue for its priority, and then reschedules the
|
||||
* system to the new head of the queue.
|
||||
*
|
||||
* Example (Single Core -- but can be extrapolated to multi):
|
||||
* ready_queue[prio=0]: ThreadA, ThreadB, ThreadC (->exec order->)
|
||||
* Currently Running: ThreadR
|
||||
*
|
||||
* ThreadR calls YieldWithoutLoadBalancing
|
||||
*
|
||||
* ThreadR is moved to the end of ready_queue[prio=0]:
|
||||
* ready_queue[prio=0]: ThreadA, ThreadB, ThreadC, ThreadR (->exec order->)
|
||||
* Currently Running: Nothing
|
||||
*
|
||||
* System is rescheduled (ThreadA is popped off of queue):
|
||||
* ready_queue[prio=0]: ThreadB, ThreadC, ThreadR (->exec order->)
|
||||
* Currently Running: ThreadA
|
||||
*
|
||||
* If the queue is empty at time of call, no yielding occurs. This does not cross between cores
|
||||
* or priorities at all.
|
||||
*/
|
||||
void YieldWithoutLoadBalancing(Thread* thread);
|
||||
|
||||
/**
|
||||
* YieldWithLoadBalancing -- yield but with better selection of the new running thread
|
||||
* Moves the current thread to the end of the ready queue for its priority, then selects a
|
||||
* 'suggested thread' (a thread on a different core that could run on this core) from the
|
||||
* scheduler, changes its core, and reschedules the current core to that thread.
|
||||
*
|
||||
* Example (Dual Core -- can be extrapolated to Quad Core, this is just normal yield if it were
|
||||
* single core):
|
||||
* ready_queue[core=0][prio=0]: ThreadA, ThreadB (affinities not pictured as irrelevant
|
||||
* ready_queue[core=1][prio=0]: ThreadC[affinity=both], ThreadD[affinity=core1only]
|
||||
* Currently Running: ThreadQ on Core 0 || ThreadP on Core 1
|
||||
*
|
||||
* ThreadQ calls YieldWithLoadBalancing
|
||||
*
|
||||
* ThreadQ is moved to the end of ready_queue[core=0][prio=0]:
|
||||
* ready_queue[core=0][prio=0]: ThreadA, ThreadB
|
||||
* ready_queue[core=1][prio=0]: ThreadC[affinity=both], ThreadD[affinity=core1only]
|
||||
* Currently Running: ThreadQ on Core 0 || ThreadP on Core 1
|
||||
*
|
||||
* A list of suggested threads for each core is compiled
|
||||
* Suggested Threads: {ThreadC on Core 1}
|
||||
* If this were quad core (as the switch is), there could be between 0 and 3 threads in this
|
||||
* list. If there are more than one, the thread is selected by highest prio.
|
||||
*
|
||||
* ThreadC is core changed to Core 0:
|
||||
* ready_queue[core=0][prio=0]: ThreadC, ThreadA, ThreadB, ThreadQ
|
||||
* ready_queue[core=1][prio=0]: ThreadD
|
||||
* Currently Running: None on Core 0 || ThreadP on Core 1
|
||||
*
|
||||
* System is rescheduled (ThreadC is popped off of queue):
|
||||
* ready_queue[core=0][prio=0]: ThreadA, ThreadB, ThreadQ
|
||||
* ready_queue[core=1][prio=0]: ThreadD
|
||||
* Currently Running: ThreadC on Core 0 || ThreadP on Core 1
|
||||
*
|
||||
* If no suggested threads can be found this will behave just as normal yield. If there are
|
||||
* multiple candidates for the suggested thread on a core, the highest prio is taken.
|
||||
*/
|
||||
void YieldWithLoadBalancing(Thread* thread);
|
||||
|
||||
/// Currently unknown -- asserts as unimplemented on call
|
||||
void YieldAndWaitForLoadBalancing(Thread* thread);
|
||||
void RemoveThread(const Thread* thread);
|
||||
|
||||
/// Returns a list of all threads managed by the scheduler
|
||||
const std::vector<SharedPtr<Thread>>& GetThreadList() const {
|
||||
return thread_list;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Pops and returns the next thread from the thread queue
|
||||
* @return A pointer to the next ready thread
|
||||
*/
|
||||
Thread* PopNextReadyThread();
|
||||
// Add a thread to the suggested queue of a cpu core. Suggested threads may be
|
||||
// picked if no thread is scheduled to run on the core.
|
||||
void Suggest(u32 priority, u32 core, Thread* thread) {
|
||||
suggested_queue[core].add(thread, priority);
|
||||
}
|
||||
|
||||
// Remove a thread to the suggested queue of a cpu core. Suggested threads may be
|
||||
// picked if no thread is scheduled to run on the core.
|
||||
void Unsuggest(u32 priority, u32 core, Thread* thread) {
|
||||
suggested_queue[core].remove(thread, priority);
|
||||
}
|
||||
|
||||
// Add a thread to the scheduling queue of a cpu core. The thread is added at the
|
||||
// back the queue in its priority level
|
||||
void Schedule(u32 priority, u32 core, Thread* thread);
|
||||
|
||||
// Add a thread to the scheduling queue of a cpu core. The thread is added at the
|
||||
// front the queue in its priority level
|
||||
void SchedulePrepend(u32 priority, u32 core, Thread* thread);
|
||||
|
||||
// Reschedule an already scheduled thread based on a new priority
|
||||
void Reschedule(u32 priority, u32 core, Thread* thread) {
|
||||
scheduled_queue[core].remove(thread, priority);
|
||||
scheduled_queue[core].add(thread, priority);
|
||||
}
|
||||
|
||||
// Unschedule a thread.
|
||||
void Unschedule(u32 priority, u32 core, Thread* thread) {
|
||||
scheduled_queue[core].remove(thread, priority);
|
||||
}
|
||||
|
||||
// Transfers a thread into an specific core. If the destination_core is -1
|
||||
// it will be unscheduled from its source code and added into its suggested
|
||||
// queue.
|
||||
void TransferToCore(u32 priority, s32 destination_core, Thread* thread) {
|
||||
const bool schedulable = thread->GetPriority() < THREADPRIO_COUNT;
|
||||
const s32 source_core = thread->GetProcessorID();
|
||||
if (source_core == destination_core || !schedulable) {
|
||||
return;
|
||||
}
|
||||
thread->SetProcessorID(destination_core);
|
||||
if (source_core >= 0) {
|
||||
Unschedule(priority, source_core, thread);
|
||||
}
|
||||
if (destination_core >= 0) {
|
||||
Unsuggest(priority, destination_core, thread);
|
||||
Schedule(priority, destination_core, thread);
|
||||
}
|
||||
if (source_core >= 0) {
|
||||
Suggest(priority, source_core, thread);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* UnloadThread selects a core and forces it to unload its current thread's context
|
||||
*/
|
||||
void UnloadThread(s32 core);
|
||||
|
||||
/*
|
||||
* SelectThread takes care of selecting the new scheduled thread.
|
||||
* It does it in 3 steps:
|
||||
* - First a thread is selected from the top of the priority queue. If no thread
|
||||
* is obtained then we move to step two, else we are done.
|
||||
* - Second we try to get a suggested thread that's not assigned to any core or
|
||||
* that is not the top thread in that core.
|
||||
* - Third is no suggested thread is found, we do a second pass and pick a running
|
||||
* thread in another core and swap it with its current thread.
|
||||
*/
|
||||
void SelectThread(u32 core);
|
||||
|
||||
bool HaveReadyThreads(u32 core_id) const {
|
||||
return !scheduled_queue[core_id].empty();
|
||||
}
|
||||
|
||||
/*
|
||||
* YieldThread takes a thread and moves it to the back of the it's priority list
|
||||
* This operation can be redundant and no scheduling is changed if marked as so.
|
||||
*/
|
||||
void YieldThread(Thread* thread);
|
||||
|
||||
/*
|
||||
* YieldThreadAndBalanceLoad takes a thread and moves it to the back of the it's priority list.
|
||||
* Afterwards, tries to pick a suggested thread from the suggested queue that has worse time or
|
||||
* a better priority than the next thread in the core.
|
||||
* This operation can be redundant and no scheduling is changed if marked as so.
|
||||
*/
|
||||
void YieldThreadAndBalanceLoad(Thread* thread);
|
||||
|
||||
/*
|
||||
* YieldThreadAndWaitForLoadBalancing takes a thread and moves it out of the scheduling queue
|
||||
* and into the suggested queue. If no thread can be squeduled afterwards in that core,
|
||||
* a suggested thread is obtained instead.
|
||||
* This operation can be redundant and no scheduling is changed if marked as so.
|
||||
*/
|
||||
void YieldThreadAndWaitForLoadBalancing(Thread* thread);
|
||||
|
||||
u32 CpuCoresCount() const {
|
||||
return NUM_CPU_CORES;
|
||||
}
|
||||
|
||||
void SetReselectionPending() {
|
||||
reselection_pending.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool IsReselectionPending() const {
|
||||
return reselection_pending.load();
|
||||
}
|
||||
|
||||
private:
|
||||
void AskForReselectionOrMarkRedundant(Thread* current_thread, Thread* winner);
|
||||
|
||||
static constexpr u32 min_regular_priority = 2;
|
||||
std::array<Common::MultiLevelQueue<Thread*, THREADPRIO_COUNT>, NUM_CPU_CORES> scheduled_queue;
|
||||
std::array<Common::MultiLevelQueue<Thread*, THREADPRIO_COUNT>, NUM_CPU_CORES> suggested_queue;
|
||||
std::atomic<bool> reselection_pending;
|
||||
|
||||
/// Lists all thread ids that aren't deleted/etc.
|
||||
std::vector<SharedPtr<Thread>> thread_list;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
class Scheduler final {
|
||||
public:
|
||||
explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, const u32 core_id);
|
||||
~Scheduler();
|
||||
|
||||
/// Returns whether there are any threads that are ready to run.
|
||||
bool HaveReadyThreads() const;
|
||||
|
||||
/// Reschedules to the next available thread (call after current thread is suspended)
|
||||
void TryDoContextSwitch();
|
||||
|
||||
void UnloadThread();
|
||||
|
||||
void SelectThreads();
|
||||
|
||||
/// Gets the current running thread
|
||||
Thread* GetCurrentThread() const;
|
||||
|
||||
Thread* GetSelectedThread() const;
|
||||
|
||||
/// Gets the timestamp for the last context switch in ticks.
|
||||
u64 GetLastContextSwitchTicks() const;
|
||||
|
||||
bool ContextSwitchPending() const {
|
||||
return context_switch_pending;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class GlobalScheduler;
|
||||
/**
|
||||
* Switches the CPU's active thread context to that of the specified thread
|
||||
* @param new_thread The thread to switch to
|
||||
*/
|
||||
void SwitchContext(Thread* new_thread);
|
||||
void SwitchContext();
|
||||
|
||||
/**
|
||||
* Called on every context switch to update the internal timestamp
|
||||
@@ -152,19 +206,16 @@ private:
|
||||
*/
|
||||
void UpdateLastContextSwitchTime(Thread* thread, Process* process);
|
||||
|
||||
/// Lists all thread ids that aren't deleted/etc.
|
||||
std::vector<SharedPtr<Thread>> thread_list;
|
||||
|
||||
/// Lists only ready thread ids.
|
||||
Common::MultiLevelQueue<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
|
||||
|
||||
SharedPtr<Thread> current_thread = nullptr;
|
||||
|
||||
Core::ARM_Interface& cpu_core;
|
||||
u64 last_context_switch_time = 0;
|
||||
SharedPtr<Thread> selected_thread = nullptr;
|
||||
|
||||
Core::System& system;
|
||||
static std::mutex scheduler_mutex;
|
||||
Core::ARM_Interface& cpu_core;
|
||||
u64 last_context_switch_time = 0;
|
||||
u64 idle_selection_count = 0;
|
||||
const u32 core_id;
|
||||
|
||||
bool context_switch_pending = false;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
+40
-34
@@ -516,7 +516,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
|
||||
thread->WakeAfterDelay(nano_seconds);
|
||||
thread->SetWakeupCallback(DefaultThreadWakeupCallback);
|
||||
|
||||
system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||
system.PrepareReschedule(thread->GetProcessorID());
|
||||
|
||||
return RESULT_TIMEOUT;
|
||||
}
|
||||
@@ -534,6 +534,7 @@ static ResultCode CancelSynchronization(Core::System& system, Handle thread_hand
|
||||
}
|
||||
|
||||
thread->CancelWait();
|
||||
system.PrepareReschedule(thread->GetProcessorID());
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -577,7 +578,8 @@ static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) {
|
||||
}
|
||||
|
||||
auto* const current_process = system.Kernel().CurrentProcess();
|
||||
return current_process->GetMutex().Release(mutex_addr);
|
||||
return current_process->GetMutex().Release(mutex_addr,
|
||||
system.CurrentScheduler().GetCurrentThread());
|
||||
}
|
||||
|
||||
enum class BreakType : u32 {
|
||||
@@ -1066,6 +1068,8 @@ static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 act
|
||||
}
|
||||
|
||||
thread->SetActivity(static_cast<ThreadActivity>(activity));
|
||||
|
||||
system.PrepareReschedule(thread->GetProcessorID());
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1147,7 +1151,7 @@ static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 pri
|
||||
|
||||
thread->SetPriority(priority);
|
||||
|
||||
system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||
system.PrepareReschedule(thread->GetProcessorID());
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1503,7 +1507,7 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
|
||||
thread->SetName(
|
||||
fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle));
|
||||
|
||||
system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||
system.PrepareReschedule(thread->GetProcessorID());
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@@ -1525,7 +1529,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) {
|
||||
thread->ResumeFromWait();
|
||||
|
||||
if (thread->GetStatus() == ThreadStatus::Ready) {
|
||||
system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||
system.PrepareReschedule(thread->GetProcessorID());
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
@@ -1537,7 +1541,7 @@ static void ExitThread(Core::System& system) {
|
||||
|
||||
auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
|
||||
current_thread->Stop();
|
||||
system.CurrentScheduler().RemoveThread(current_thread);
|
||||
system.GlobalScheduler().RemoveThread(current_thread);
|
||||
system.PrepareReschedule();
|
||||
}
|
||||
|
||||
@@ -1557,13 +1561,13 @@ static void SleepThread(Core::System& system, s64 nanoseconds) {
|
||||
if (nanoseconds <= 0) {
|
||||
switch (static_cast<SleepType>(nanoseconds)) {
|
||||
case SleepType::YieldWithoutLoadBalancing:
|
||||
scheduler.YieldWithoutLoadBalancing(current_thread);
|
||||
current_thread->YieldSimple();
|
||||
break;
|
||||
case SleepType::YieldWithLoadBalancing:
|
||||
scheduler.YieldWithLoadBalancing(current_thread);
|
||||
current_thread->YieldAndBalanceLoad();
|
||||
break;
|
||||
case SleepType::YieldAndWaitForLoadBalancing:
|
||||
scheduler.YieldAndWaitForLoadBalancing(current_thread);
|
||||
current_thread->YieldAndWaitForLoadBalancing();
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
|
||||
@@ -1572,10 +1576,7 @@ static void SleepThread(Core::System& system, s64 nanoseconds) {
|
||||
current_thread->Sleep(nanoseconds);
|
||||
}
|
||||
|
||||
// Reschedule all CPU cores
|
||||
for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) {
|
||||
system.CpuCore(i).PrepareReschedule();
|
||||
}
|
||||
system.PrepareReschedule(current_thread->GetProcessorID());
|
||||
}
|
||||
|
||||
/// Wait process wide key atomic
|
||||
@@ -1606,12 +1607,14 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add
|
||||
SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
ASSERT(thread);
|
||||
|
||||
const auto release_result = current_process->GetMutex().Release(mutex_addr);
|
||||
SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread();
|
||||
|
||||
const auto release_result =
|
||||
current_process->GetMutex().Release(mutex_addr, current_thread.get());
|
||||
if (release_result.IsError()) {
|
||||
return release_result;
|
||||
}
|
||||
|
||||
SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread();
|
||||
current_thread->SetCondVarWaitAddress(condition_variable_addr);
|
||||
current_thread->SetMutexWaitAddress(mutex_addr);
|
||||
current_thread->SetWaitHandle(thread_handle);
|
||||
@@ -1622,7 +1625,7 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add
|
||||
|
||||
// Note: Deliberately don't attempt to inherit the lock owner's priority.
|
||||
|
||||
system.CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
|
||||
system.PrepareReschedule(current_thread->GetProcessorID());
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1632,24 +1635,17 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
|
||||
LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}",
|
||||
condition_variable_addr, target);
|
||||
|
||||
const auto RetrieveWaitingThreads = [&system](std::size_t core_index,
|
||||
std::vector<SharedPtr<Thread>>& waiting_threads,
|
||||
VAddr condvar_addr) {
|
||||
const auto& scheduler = system.Scheduler(core_index);
|
||||
const auto& thread_list = scheduler.GetThreadList();
|
||||
|
||||
for (const auto& thread : thread_list) {
|
||||
if (thread->GetCondVarWaitAddress() == condvar_addr)
|
||||
waiting_threads.push_back(thread);
|
||||
}
|
||||
};
|
||||
|
||||
// Retrieve a list of all threads that are waiting for this condition variable.
|
||||
std::vector<SharedPtr<Thread>> waiting_threads;
|
||||
RetrieveWaitingThreads(0, waiting_threads, condition_variable_addr);
|
||||
RetrieveWaitingThreads(1, waiting_threads, condition_variable_addr);
|
||||
RetrieveWaitingThreads(2, waiting_threads, condition_variable_addr);
|
||||
RetrieveWaitingThreads(3, waiting_threads, condition_variable_addr);
|
||||
const auto& scheduler = system.GlobalScheduler();
|
||||
const auto& thread_list = scheduler.GetThreadList();
|
||||
|
||||
for (const auto& thread : thread_list) {
|
||||
if (thread->GetCondVarWaitAddress() == condition_variable_addr) {
|
||||
waiting_threads.push_back(thread);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort them by priority, such that the highest priority ones come first.
|
||||
std::sort(waiting_threads.begin(), waiting_threads.end(),
|
||||
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
|
||||
@@ -1704,7 +1700,7 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
|
||||
thread->SetLockOwner(nullptr);
|
||||
thread->SetMutexWaitAddress(0);
|
||||
thread->SetWaitHandle(0);
|
||||
system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||
system.PrepareReschedule(thread->GetProcessorID());
|
||||
} else {
|
||||
// Atomically signal that the mutex now has a waiting thread.
|
||||
do {
|
||||
@@ -1728,6 +1724,7 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
|
||||
thread->SetStatus(ThreadStatus::WaitMutex);
|
||||
|
||||
owner->AddMutexWaiter(thread);
|
||||
system.PrepareReschedule(thread->GetProcessorID());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1754,7 +1751,12 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type,
|
||||
|
||||
const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type);
|
||||
auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter();
|
||||
return address_arbiter.WaitForAddress(address, arbitration_type, value, timeout);
|
||||
const ResultCode result =
|
||||
address_arbiter.WaitForAddress(address, arbitration_type, value, timeout);
|
||||
if (result == RESULT_SUCCESS) {
|
||||
system.PrepareReschedule();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Signals to an address (via Address Arbiter)
|
||||
@@ -2040,7 +2042,10 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle,
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
system.PrepareReschedule(thread->GetProcessorID());
|
||||
thread->ChangeCore(core, affinity_mask);
|
||||
system.PrepareReschedule(thread->GetProcessorID());
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -2151,6 +2156,7 @@ static ResultCode SignalEvent(Core::System& system, Handle handle) {
|
||||
}
|
||||
|
||||
writable_event->Signal();
|
||||
system.PrepareReschedule();
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
+189
-59
@@ -45,15 +45,7 @@ void Thread::Stop() {
|
||||
callback_handle);
|
||||
kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
|
||||
callback_handle = 0;
|
||||
|
||||
// Clean up thread from ready queue
|
||||
// This is only needed when the thread is terminated forcefully (SVC TerminateProcess)
|
||||
if (status == ThreadStatus::Ready || status == ThreadStatus::Paused) {
|
||||
scheduler->UnscheduleThread(this, current_priority);
|
||||
}
|
||||
|
||||
status = ThreadStatus::Dead;
|
||||
|
||||
SetStatus(ThreadStatus::Dead);
|
||||
WakeupAllWaitingThreads();
|
||||
|
||||
// Clean up any dangling references in objects that this thread was waiting for
|
||||
@@ -132,13 +124,11 @@ void Thread::ResumeFromWait() {
|
||||
wakeup_callback = nullptr;
|
||||
|
||||
if (activity == ThreadActivity::Paused) {
|
||||
status = ThreadStatus::Paused;
|
||||
SetStatus(ThreadStatus::Paused);
|
||||
return;
|
||||
}
|
||||
|
||||
status = ThreadStatus::Ready;
|
||||
|
||||
ChangeScheduler();
|
||||
SetStatus(ThreadStatus::Ready);
|
||||
}
|
||||
|
||||
void Thread::CancelWait() {
|
||||
@@ -205,9 +195,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
|
||||
thread->name = std::move(name);
|
||||
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
|
||||
thread->owner_process = &owner_process;
|
||||
auto& scheduler = kernel.GlobalScheduler();
|
||||
scheduler.AddThread(thread);
|
||||
thread->tls_address = thread->owner_process->CreateTLSRegion();
|
||||
thread->scheduler = &system.Scheduler(processor_id);
|
||||
thread->scheduler->AddThread(thread);
|
||||
|
||||
thread->owner_process->RegisterThread(thread.get());
|
||||
|
||||
@@ -250,6 +240,22 @@ void Thread::SetStatus(ThreadStatus new_status) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (new_status) {
|
||||
case ThreadStatus::Ready:
|
||||
case ThreadStatus::Running:
|
||||
SetSchedulingStatus(ThreadSchedStatus::Runnable);
|
||||
break;
|
||||
case ThreadStatus::Dormant:
|
||||
SetSchedulingStatus(ThreadSchedStatus::None);
|
||||
break;
|
||||
case ThreadStatus::Dead:
|
||||
SetSchedulingStatus(ThreadSchedStatus::Exited);
|
||||
break;
|
||||
default:
|
||||
SetSchedulingStatus(ThreadSchedStatus::Paused);
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == ThreadStatus::Running) {
|
||||
last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks();
|
||||
}
|
||||
@@ -311,8 +317,7 @@ void Thread::UpdatePriority() {
|
||||
return;
|
||||
}
|
||||
|
||||
scheduler->SetThreadPriority(this, new_priority);
|
||||
current_priority = new_priority;
|
||||
SetCurrentPriority(new_priority);
|
||||
|
||||
if (!lock_owner) {
|
||||
return;
|
||||
@@ -328,47 +333,7 @@ void Thread::UpdatePriority() {
|
||||
}
|
||||
|
||||
void Thread::ChangeCore(u32 core, u64 mask) {
|
||||
ideal_core = core;
|
||||
affinity_mask = mask;
|
||||
ChangeScheduler();
|
||||
}
|
||||
|
||||
void Thread::ChangeScheduler() {
|
||||
if (status != ThreadStatus::Ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
|
||||
|
||||
if (!new_processor_id) {
|
||||
new_processor_id = processor_id;
|
||||
}
|
||||
if (ideal_core != -1 && system.Scheduler(ideal_core).GetCurrentThread() == nullptr) {
|
||||
new_processor_id = ideal_core;
|
||||
}
|
||||
|
||||
ASSERT(*new_processor_id < 4);
|
||||
|
||||
// Add thread to new core's scheduler
|
||||
auto& next_scheduler = system.Scheduler(*new_processor_id);
|
||||
|
||||
if (*new_processor_id != processor_id) {
|
||||
// Remove thread from previous core's scheduler
|
||||
scheduler->RemoveThread(this);
|
||||
next_scheduler.AddThread(this);
|
||||
}
|
||||
|
||||
processor_id = *new_processor_id;
|
||||
|
||||
// If the thread was ready, unschedule from the previous core and schedule on the new core
|
||||
scheduler->UnscheduleThread(this, current_priority);
|
||||
next_scheduler.ScheduleThread(this, current_priority);
|
||||
|
||||
// Change thread's scheduler
|
||||
scheduler = &next_scheduler;
|
||||
|
||||
system.CpuCore(processor_id).PrepareReschedule();
|
||||
SetCoreAndAffinityMask(core, mask);
|
||||
}
|
||||
|
||||
bool Thread::AllWaitObjectsReady() const {
|
||||
@@ -391,7 +356,7 @@ void Thread::SetActivity(ThreadActivity value) {
|
||||
if (status == ThreadStatus::Ready) {
|
||||
status = ThreadStatus::Paused;
|
||||
} else if (status == ThreadStatus::Running) {
|
||||
status = ThreadStatus::Paused;
|
||||
SetStatus(ThreadStatus::Paused);
|
||||
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
|
||||
}
|
||||
} else if (status == ThreadStatus::Paused) {
|
||||
@@ -408,6 +373,171 @@ void Thread::Sleep(s64 nanoseconds) {
|
||||
WakeAfterDelay(nanoseconds);
|
||||
}
|
||||
|
||||
void Thread::YieldSimple() {
|
||||
auto& scheduler = kernel.GlobalScheduler();
|
||||
scheduler.YieldThread(this);
|
||||
}
|
||||
|
||||
void Thread::YieldAndBalanceLoad() {
|
||||
auto& scheduler = kernel.GlobalScheduler();
|
||||
scheduler.YieldThreadAndBalanceLoad(this);
|
||||
}
|
||||
|
||||
void Thread::YieldAndWaitForLoadBalancing() {
|
||||
auto& scheduler = kernel.GlobalScheduler();
|
||||
scheduler.YieldThreadAndWaitForLoadBalancing(this);
|
||||
}
|
||||
|
||||
void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) {
|
||||
const u32 old_flags = scheduling_state;
|
||||
scheduling_state =
|
||||
(scheduling_state & ThreadSchedMasks::HighMask) | static_cast<u32>(new_status);
|
||||
AdjustSchedulingOnStatus(old_flags);
|
||||
}
|
||||
|
||||
void Thread::SetCurrentPriority(u32 new_priority) {
|
||||
u32 old_priority = std::exchange(current_priority, new_priority);
|
||||
AdjustSchedulingOnPriority(old_priority);
|
||||
}
|
||||
|
||||
ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
|
||||
const auto HighestSetCore = [](u64 mask, u32 max_cores) {
|
||||
for (s32 core = max_cores - 1; core >= 0; core--) {
|
||||
if (((mask >> core) & 1) != 0) {
|
||||
return core;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
const bool use_override = affinity_override_count != 0;
|
||||
// The value -3 is "do not change the ideal core".
|
||||
if (new_core == -3) {
|
||||
new_core = use_override ? ideal_core_override : ideal_core;
|
||||
if ((new_affinity_mask & (1 << new_core)) == 0) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
}
|
||||
if (use_override) {
|
||||
ideal_core_override = new_core;
|
||||
affinity_mask_override = new_affinity_mask;
|
||||
} else {
|
||||
const u64 old_affinity_mask = std::exchange(affinity_mask, new_affinity_mask);
|
||||
ideal_core = new_core;
|
||||
if (old_affinity_mask != new_affinity_mask) {
|
||||
const s32 old_core = processor_id;
|
||||
if (processor_id >= 0 && ((affinity_mask >> processor_id) & 1) == 0) {
|
||||
if (ideal_core < 0) {
|
||||
processor_id = HighestSetCore(affinity_mask, GlobalScheduler::NUM_CPU_CORES);
|
||||
} else {
|
||||
processor_id = ideal_core;
|
||||
}
|
||||
}
|
||||
AdjustSchedulingOnAffinity(old_affinity_mask, old_core);
|
||||
}
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void Thread::AdjustSchedulingOnStatus(u32 old_flags) {
|
||||
if (old_flags == scheduling_state) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& scheduler = kernel.GlobalScheduler();
|
||||
if (static_cast<ThreadSchedStatus>(old_flags & ThreadSchedMasks::LowMask) ==
|
||||
ThreadSchedStatus::Runnable) {
|
||||
// In this case the thread was running, now it's pausing/exitting
|
||||
if (processor_id >= 0) {
|
||||
scheduler.Unschedule(current_priority, processor_id, this);
|
||||
}
|
||||
|
||||
for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
|
||||
if (core != processor_id && ((affinity_mask >> core) & 1) != 0) {
|
||||
scheduler.Unsuggest(current_priority, core, this);
|
||||
}
|
||||
}
|
||||
} else if (GetSchedulingStatus() == ThreadSchedStatus::Runnable) {
|
||||
// The thread is now set to running from being stopped
|
||||
if (processor_id >= 0) {
|
||||
scheduler.Schedule(current_priority, processor_id, this);
|
||||
}
|
||||
|
||||
for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
|
||||
if (core != processor_id && ((affinity_mask >> core) & 1) != 0) {
|
||||
scheduler.Suggest(current_priority, core, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scheduler.SetReselectionPending();
|
||||
}
|
||||
|
||||
void Thread::AdjustSchedulingOnPriority(u32 old_priority) {
|
||||
if (GetSchedulingStatus() != ThreadSchedStatus::Runnable) {
|
||||
return;
|
||||
}
|
||||
auto& scheduler = Core::System::GetInstance().GlobalScheduler();
|
||||
if (processor_id >= 0) {
|
||||
scheduler.Unschedule(old_priority, processor_id, this);
|
||||
}
|
||||
|
||||
for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
|
||||
if (core != processor_id && ((affinity_mask >> core) & 1) != 0) {
|
||||
scheduler.Unsuggest(old_priority, core, this);
|
||||
}
|
||||
}
|
||||
|
||||
// Add thread to the new priority queues.
|
||||
Thread* current_thread = GetCurrentThread();
|
||||
|
||||
if (processor_id >= 0) {
|
||||
if (current_thread == this) {
|
||||
scheduler.SchedulePrepend(current_priority, processor_id, this);
|
||||
} else {
|
||||
scheduler.Schedule(current_priority, processor_id, this);
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
|
||||
if (core != processor_id && ((affinity_mask >> core) & 1) != 0) {
|
||||
scheduler.Suggest(current_priority, core, this);
|
||||
}
|
||||
}
|
||||
|
||||
scheduler.SetReselectionPending();
|
||||
}
|
||||
|
||||
void Thread::AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core) {
|
||||
auto& scheduler = Core::System::GetInstance().GlobalScheduler();
|
||||
if (GetSchedulingStatus() != ThreadSchedStatus::Runnable ||
|
||||
current_priority >= THREADPRIO_COUNT) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
|
||||
if (((old_affinity_mask >> core) & 1) != 0) {
|
||||
if (core == old_core) {
|
||||
scheduler.Unschedule(current_priority, core, this);
|
||||
} else {
|
||||
scheduler.Unsuggest(current_priority, core, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
|
||||
if (((affinity_mask >> core) & 1) != 0) {
|
||||
if (core == processor_id) {
|
||||
scheduler.Schedule(current_priority, core, this);
|
||||
} else {
|
||||
scheduler.Suggest(current_priority, core, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scheduler.SetReselectionPending();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
|
||||
@@ -75,6 +75,26 @@ enum class ThreadActivity : u32 {
|
||||
Paused = 1,
|
||||
};
|
||||
|
||||
enum class ThreadSchedStatus : u32 {
|
||||
None = 0,
|
||||
Paused = 1,
|
||||
Runnable = 2,
|
||||
Exited = 3,
|
||||
};
|
||||
|
||||
enum ThreadSchedFlags : u32 {
|
||||
ProcessPauseFlag = 1 << 4,
|
||||
ThreadPauseFlag = 1 << 5,
|
||||
ProcessDebugPauseFlag = 1 << 6,
|
||||
KernelInitPauseFlag = 1 << 8,
|
||||
};
|
||||
|
||||
enum ThreadSchedMasks : u32 {
|
||||
LowMask = 0x000f,
|
||||
HighMask = 0xfff0,
|
||||
ForcePauseMask = 0x0070,
|
||||
};
|
||||
|
||||
class Thread final : public WaitObject {
|
||||
public:
|
||||
using MutexWaitingThreads = std::vector<SharedPtr<Thread>>;
|
||||
@@ -278,6 +298,10 @@ public:
|
||||
return processor_id;
|
||||
}
|
||||
|
||||
void SetProcessorID(s32 new_core) {
|
||||
processor_id = new_core;
|
||||
}
|
||||
|
||||
Process* GetOwnerProcess() {
|
||||
return owner_process;
|
||||
}
|
||||
@@ -383,11 +407,38 @@ public:
|
||||
/// Sleeps this thread for the given amount of nanoseconds.
|
||||
void Sleep(s64 nanoseconds);
|
||||
|
||||
/// Yields this thread without rebalancing loads.
|
||||
void YieldSimple();
|
||||
|
||||
/// Yields this thread and does a load rebalancing.
|
||||
void YieldAndBalanceLoad();
|
||||
|
||||
/// Yields this thread and if the core is left idle, loads are rebalanced
|
||||
void YieldAndWaitForLoadBalancing();
|
||||
|
||||
ThreadSchedStatus GetSchedulingStatus() const {
|
||||
return static_cast<ThreadSchedStatus>(scheduling_state & ThreadSchedMasks::LowMask);
|
||||
}
|
||||
|
||||
bool IsRunning() const {
|
||||
return is_running;
|
||||
}
|
||||
|
||||
void SetIsRunning(bool value) {
|
||||
is_running = value;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit Thread(KernelCore& kernel);
|
||||
~Thread() override;
|
||||
|
||||
void ChangeScheduler();
|
||||
void SetSchedulingStatus(ThreadSchedStatus new_status);
|
||||
void SetCurrentPriority(u32 new_priority);
|
||||
ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask);
|
||||
|
||||
void AdjustSchedulingOnStatus(u32 old_flags);
|
||||
void AdjustSchedulingOnPriority(u32 old_priority);
|
||||
void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core);
|
||||
|
||||
Core::ARM_Interface::ThreadContext context{};
|
||||
|
||||
@@ -453,6 +504,13 @@ private:
|
||||
|
||||
ThreadActivity activity = ThreadActivity::Normal;
|
||||
|
||||
s32 ideal_core_override = -1;
|
||||
u64 affinity_mask_override = 0x1;
|
||||
u32 affinity_override_count = 0;
|
||||
|
||||
u32 scheduling_state = 0;
|
||||
bool is_running = false;
|
||||
|
||||
std::string name;
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_
|
||||
transfer_memory->owner_permissions = permissions;
|
||||
transfer_memory->owner_process = kernel.CurrentProcess();
|
||||
|
||||
transfer_memory->MapMemory(base_address, size, permissions);
|
||||
|
||||
return transfer_memory;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
@@ -48,17 +50,8 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() const {
|
||||
if (ShouldWait(thread.get()))
|
||||
continue;
|
||||
|
||||
// A thread is ready to run if it's either in ThreadStatus::WaitSynch
|
||||
// and the rest of the objects it is waiting on are ready.
|
||||
bool ready_to_run = true;
|
||||
if (thread_status == ThreadStatus::WaitSynch) {
|
||||
ready_to_run = thread->AllWaitObjectsReady();
|
||||
}
|
||||
|
||||
if (ready_to_run) {
|
||||
candidate = thread.get();
|
||||
candidate_priority = thread->GetPriority();
|
||||
}
|
||||
candidate = thread.get();
|
||||
candidate_priority = thread->GetPriority();
|
||||
}
|
||||
|
||||
return candidate;
|
||||
@@ -95,6 +88,7 @@ void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) {
|
||||
}
|
||||
if (resume) {
|
||||
thread->ResumeFromWait();
|
||||
Core::System::GetInstance().PrepareReschedule(thread->GetProcessorID());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
|
||||
protected:
|
||||
void Get(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
|
||||
ProfileBase profile_base{};
|
||||
ProfileData data{};
|
||||
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
|
||||
@@ -89,7 +89,7 @@ protected:
|
||||
}
|
||||
|
||||
void GetBase(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
|
||||
ProfileBase profile_base{};
|
||||
if (profile_manager.GetProfileBase(user_id, profile_base)) {
|
||||
IPC::ResponseBuilder rb{ctx, 16};
|
||||
@@ -263,7 +263,7 @@ private:
|
||||
};
|
||||
|
||||
void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_ACC, "called");
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(static_cast<u32>(profile_manager->GetUserCount()));
|
||||
@@ -272,7 +272,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
|
||||
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
Common::UUID user_id = rp.PopRaw<Common::UUID>();
|
||||
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -280,21 +280,21 @@ void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_ACC, "called");
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
ctx.WriteBuffer(profile_manager->GetAllUsers());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_ACC, "called");
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
ctx.WriteBuffer(profile_manager->GetOpenUsers());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_ACC, "called");
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<Common::UUID>(profile_manager->GetLastOpenedUser());
|
||||
|
||||
@@ -232,12 +232,12 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
|
||||
|
||||
IDebugFunctions::~IDebugFunctions() = default;
|
||||
|
||||
ISelfController::ISelfController(Core::System& system_,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger_)
|
||||
: ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger_)) {
|
||||
ISelfController::ISelfController(Core::System& system,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("ISelfController"), system(system), nvflinger(std::move(nvflinger)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Exit"},
|
||||
{0, &ISelfController::Exit, "Exit"},
|
||||
{1, &ISelfController::LockExit, "LockExit"},
|
||||
{2, &ISelfController::UnlockExit, "UnlockExit"},
|
||||
{3, &ISelfController::EnterFatalSection, "EnterFatalSection"},
|
||||
@@ -282,7 +282,7 @@ ISelfController::ISelfController(Core::System& system_,
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = system_.Kernel();
|
||||
auto& kernel = system.Kernel();
|
||||
launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"ISelfController:LaunchableEvent");
|
||||
|
||||
@@ -298,15 +298,28 @@ ISelfController::ISelfController(Core::System& system_,
|
||||
|
||||
ISelfController::~ISelfController() = default;
|
||||
|
||||
void ISelfController::Exit(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
system.Shutdown();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ISelfController::LockExit(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
system.SetExitLock(true);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
system.SetExitLock(false);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -550,6 +563,10 @@ void AppletMessageQueue::OperationModeChanged() {
|
||||
on_operation_mode_changed.writable->Signal();
|
||||
}
|
||||
|
||||
void AppletMessageQueue::RequestExit() {
|
||||
PushMessage(AppletMessage::ExitRequested);
|
||||
}
|
||||
|
||||
ICommonStateGetter::ICommonStateGetter(Core::System& system,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("ICommonStateGetter"), system(system), msg_queue(std::move(msg_queue)) {
|
||||
@@ -824,17 +841,16 @@ private:
|
||||
void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
const auto storage = applet->GetBroker().PopInteractiveDataToGame();
|
||||
if (storage == nullptr) {
|
||||
LOG_ERROR(Service_AM,
|
||||
"storage is a nullptr. There is no data in the current interactive channel");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_NO_DATA_IN_CHANNEL);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IStorage>(std::move(*storage));
|
||||
}
|
||||
@@ -1066,7 +1082,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto& kernel = system.Kernel();
|
||||
gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent");
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ class AppletMessageQueue {
|
||||
public:
|
||||
enum class AppletMessage : u32 {
|
||||
NoMessage = 0,
|
||||
ExitRequested = 4,
|
||||
FocusStateChanged = 15,
|
||||
OperationModeChanged = 30,
|
||||
PerformanceModeChanged = 31,
|
||||
@@ -59,6 +60,7 @@ public:
|
||||
AppletMessage PopMessage();
|
||||
std::size_t GetMessageCount() const;
|
||||
void OperationModeChanged();
|
||||
void RequestExit();
|
||||
|
||||
private:
|
||||
std::queue<AppletMessage> messages;
|
||||
@@ -123,6 +125,7 @@ public:
|
||||
~ISelfController() override;
|
||||
|
||||
private:
|
||||
void Exit(Kernel::HLERequestContext& ctx);
|
||||
void LockExit(Kernel::HLERequestContext& ctx);
|
||||
void UnlockExit(Kernel::HLERequestContext& ctx);
|
||||
void EnterFatalSection(Kernel::HLERequestContext& ctx);
|
||||
@@ -151,6 +154,8 @@ private:
|
||||
u32 idle_time_detection_extension = 0;
|
||||
u64 num_fatal_sections_entered = 0;
|
||||
bool is_auto_sleep_disabled = false;
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
|
||||
|
||||
@@ -19,6 +19,8 @@ class NVFlinger;
|
||||
|
||||
namespace AM {
|
||||
|
||||
class AppletMessageQueue;
|
||||
|
||||
class AppletAE final : public ServiceFramework<AppletAE> {
|
||||
public:
|
||||
explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
|
||||
@@ -19,6 +19,8 @@ class NVFlinger;
|
||||
|
||||
namespace AM {
|
||||
|
||||
class AppletMessageQueue;
|
||||
|
||||
class AppletOE final : public ServiceFramework<AppletOE> {
|
||||
public:
|
||||
explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
|
||||
@@ -27,9 +27,9 @@ AppletDataBroker::AppletDataBroker(Kernel::KernelCore& kernel) {
|
||||
state_changed_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:StateChangedEvent");
|
||||
pop_out_data_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopDataOutEvent");
|
||||
kernel, Kernel::ResetType::Automatic, "ILibraryAppletAccessor:PopDataOutEvent");
|
||||
pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
|
||||
kernel, Kernel::ResetType::Automatic, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
|
||||
}
|
||||
|
||||
AppletDataBroker::~AppletDataBroker() = default;
|
||||
|
||||
@@ -91,6 +91,7 @@ void SoftwareKeyboard::ExecuteInteractive() {
|
||||
|
||||
if (status == INTERACTIVE_STATUS_OK) {
|
||||
complete = true;
|
||||
broker.SignalStateChanged();
|
||||
} else {
|
||||
std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
|
||||
std::memcpy(string.data(), data.data() + 4, string.size() * 2);
|
||||
|
||||
@@ -29,9 +29,9 @@ static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
|
||||
return (title_id & DLC_BASE_TITLE_ID_MASK) == base;
|
||||
}
|
||||
|
||||
static std::vector<u64> AccumulateAOCTitleIDs() {
|
||||
static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
|
||||
std::vector<u64> add_on_content;
|
||||
const auto& rcu = Core::System::GetInstance().GetContentProvider();
|
||||
const auto& rcu = system.GetContentProvider();
|
||||
const auto list =
|
||||
rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
|
||||
std::transform(list.begin(), list.end(), std::back_inserter(add_on_content),
|
||||
@@ -47,7 +47,8 @@ static std::vector<u64> AccumulateAOCTitleIDs() {
|
||||
return add_on_content;
|
||||
}
|
||||
|
||||
AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs()) {
|
||||
AOC_U::AOC_U(Core::System& system)
|
||||
: ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs(system)), system(system) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CountAddOnContentByApplicationId"},
|
||||
@@ -65,7 +66,7 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto& kernel = system.Kernel();
|
||||
aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"GetAddOnContentListChanged:Event");
|
||||
}
|
||||
@@ -86,7 +87,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
|
||||
const auto current = system.CurrentProcess()->GetTitleID();
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
|
||||
@@ -113,7 +114,7 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
|
||||
process_id);
|
||||
|
||||
const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
|
||||
const auto current = system.CurrentProcess()->GetTitleID();
|
||||
|
||||
std::vector<u32> out;
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
@@ -159,7 +160,7 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
|
||||
const auto title_id = system.CurrentProcess()->GetTitleID();
|
||||
FileSys::PatchManager pm{title_id};
|
||||
|
||||
const auto res = pm.GetControlMetadata();
|
||||
@@ -196,8 +197,8 @@ void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushCopyObjects(aoc_change_event.readable);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
std::make_shared<AOC_U>()->InstallAsService(service_manager);
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
|
||||
std::make_shared<AOC_U>(system)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace Service::AOC
|
||||
@@ -14,7 +14,7 @@ namespace Service::AOC {
|
||||
|
||||
class AOC_U final : public ServiceFramework<AOC_U> {
|
||||
public:
|
||||
AOC_U();
|
||||
explicit AOC_U(Core::System& system);
|
||||
~AOC_U() override;
|
||||
|
||||
private:
|
||||
@@ -26,9 +26,10 @@ private:
|
||||
|
||||
std::vector<u64> add_on_content;
|
||||
Kernel::EventPair aoc_change_event;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
/// Registers all AOC services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
|
||||
|
||||
} // namespace Service::AOC
|
||||
@@ -16,7 +16,7 @@ namespace Service::BtDrv {
|
||||
|
||||
class Bt final : public ServiceFramework<Bt> {
|
||||
public:
|
||||
explicit Bt() : ServiceFramework{"bt"} {
|
||||
explicit Bt(Core::System& system) : ServiceFramework{"bt"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "LeClientReadCharacteristic"},
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto& kernel = system.Kernel();
|
||||
register_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "BT:RegisterEvent");
|
||||
}
|
||||
@@ -163,9 +163,9 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
|
||||
std::make_shared<BtDrv>()->InstallAsService(sm);
|
||||
std::make_shared<Bt>()->InstallAsService(sm);
|
||||
std::make_shared<Bt>(system)->InstallAsService(sm);
|
||||
}
|
||||
|
||||
} // namespace Service::BtDrv
|
||||
@@ -8,9 +8,13 @@ namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::BtDrv {
|
||||
|
||||
/// Registers all BtDrv services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& sm);
|
||||
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
|
||||
|
||||
} // namespace Service::BtDrv
|
||||
@@ -17,7 +17,7 @@ namespace Service::BTM {
|
||||
|
||||
class IBtmUserCore final : public ServiceFramework<IBtmUserCore> {
|
||||
public:
|
||||
explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} {
|
||||
explicit IBtmUserCore(Core::System& system) : ServiceFramework{"IBtmUserCore"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IBtmUserCore::AcquireBleScanEvent, "AcquireBleScanEvent"},
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto& kernel = system.Kernel();
|
||||
scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IBtmUserCore:ScanEvent");
|
||||
connection_event = Kernel::WritableEvent::CreateEventPair(
|
||||
@@ -108,7 +108,7 @@ private:
|
||||
|
||||
class BTM_USR final : public ServiceFramework<BTM_USR> {
|
||||
public:
|
||||
explicit BTM_USR() : ServiceFramework{"btm:u"} {
|
||||
explicit BTM_USR(Core::System& system) : ServiceFramework{"btm:u"}, system(system) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &BTM_USR::GetCore, "GetCore"},
|
||||
@@ -123,8 +123,10 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IBtmUserCore>();
|
||||
rb.PushIpcInterface<IBtmUserCore>(system);
|
||||
}
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
class BTM final : public ServiceFramework<BTM> {
|
||||
@@ -268,11 +270,11 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
|
||||
std::make_shared<BTM>()->InstallAsService(sm);
|
||||
std::make_shared<BTM_DBG>()->InstallAsService(sm);
|
||||
std::make_shared<BTM_SYS>()->InstallAsService(sm);
|
||||
std::make_shared<BTM_USR>()->InstallAsService(sm);
|
||||
std::make_shared<BTM_USR>(system)->InstallAsService(sm);
|
||||
}
|
||||
|
||||
} // namespace Service::BTM
|
||||
@@ -8,8 +8,12 @@ namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
};
|
||||
|
||||
namespace Service::BTM {
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm);
|
||||
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
|
||||
|
||||
} // namespace Service::BTM
|
||||
@@ -20,8 +20,8 @@
|
||||
|
||||
namespace Service::Fatal {
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)) {}
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)), system(system) {}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
@@ -64,7 +64,8 @@ enum class FatalType : u32 {
|
||||
ErrorScreen = 2,
|
||||
};
|
||||
|
||||
static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
|
||||
static void GenerateErrorReport(Core::System& system, ResultCode error_code,
|
||||
const FatalInfo& info) {
|
||||
const auto title_id = Core::CurrentProcess()->GetTitleID();
|
||||
std::string crash_report = fmt::format(
|
||||
"Yuzu {}-{} crash report\n"
|
||||
@@ -101,18 +102,19 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
|
||||
|
||||
LOG_ERROR(Service_Fatal, "{}", crash_report);
|
||||
|
||||
Core::System::GetInstance().GetReporter().SaveCrashReport(
|
||||
system.GetReporter().SaveCrashReport(
|
||||
title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc,
|
||||
info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace,
|
||||
info.backtrace_size, info.ArchAsString(), info.unk10);
|
||||
}
|
||||
|
||||
static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) {
|
||||
static void ThrowFatalError(Core::System& system, ResultCode error_code, FatalType fatal_type,
|
||||
const FatalInfo& info) {
|
||||
LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}",
|
||||
static_cast<u32>(fatal_type), error_code.raw);
|
||||
switch (fatal_type) {
|
||||
case FatalType::ErrorReportAndScreen:
|
||||
GenerateErrorReport(error_code, info);
|
||||
GenerateErrorReport(system, error_code, info);
|
||||
[[fallthrough]];
|
||||
case FatalType::ErrorScreen:
|
||||
// Since we have no fatal:u error screen. We should just kill execution instead
|
||||
@@ -120,7 +122,7 @@ static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const F
|
||||
break;
|
||||
// Should not throw a fatal screen but should generate an error report
|
||||
case FatalType::ErrorReport:
|
||||
GenerateErrorReport(error_code, info);
|
||||
GenerateErrorReport(system, error_code, info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -130,7 +132,7 @@ void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto error_code = rp.Pop<ResultCode>();
|
||||
|
||||
ThrowFatalError(error_code, FatalType::ErrorScreen, {});
|
||||
ThrowFatalError(system, error_code, FatalType::ErrorScreen, {});
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
@@ -141,7 +143,8 @@ void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
|
||||
const auto error_code = rp.Pop<ResultCode>();
|
||||
const auto fatal_type = rp.PopEnum<FatalType>();
|
||||
|
||||
ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy
|
||||
ThrowFatalError(system, error_code, fatal_type,
|
||||
{}); // No info is passed with ThrowFatalWithPolicy
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
@@ -157,15 +160,15 @@ void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx)
|
||||
ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!");
|
||||
std::memcpy(&info, fatal_info.data(), sizeof(FatalInfo));
|
||||
|
||||
ThrowFatalError(error_code, fatal_type, info);
|
||||
ThrowFatalError(system, error_code, fatal_type, info);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
|
||||
auto module = std::make_shared<Module>();
|
||||
std::make_shared<Fatal_P>(module)->InstallAsService(service_manager);
|
||||
std::make_shared<Fatal_U>(module)->InstallAsService(service_manager);
|
||||
std::make_shared<Fatal_P>(module, system)->InstallAsService(service_manager);
|
||||
std::make_shared<Fatal_U>(module, system)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace Service::Fatal
|
||||
@@ -6,13 +6,17 @@
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Fatal {
|
||||
|
||||
class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
explicit Interface(std::shared_ptr<Module> module, const char* name);
|
||||
explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name);
|
||||
~Interface() override;
|
||||
|
||||
void ThrowFatal(Kernel::HLERequestContext& ctx);
|
||||
@@ -21,9 +25,10 @@ public:
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
Core::System& system;
|
||||
};
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
|
||||
|
||||
} // namespace Service::Fatal
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
namespace Service::Fatal {
|
||||
|
||||
Fatal_P::Fatal_P(std::shared_ptr<Module> module)
|
||||
: Module::Interface(std::move(module), "fatal:p") {}
|
||||
Fatal_P::Fatal_P(std::shared_ptr<Module> module, Core::System& system)
|
||||
: Module::Interface(std::move(module), system, "fatal:p") {}
|
||||
|
||||
Fatal_P::~Fatal_P() = default;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Service::Fatal {
|
||||
|
||||
class Fatal_P final : public Module::Interface {
|
||||
public:
|
||||
explicit Fatal_P(std::shared_ptr<Module> module);
|
||||
explicit Fatal_P(std::shared_ptr<Module> module, Core::System& system);
|
||||
~Fatal_P() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
|
||||
namespace Service::Fatal {
|
||||
|
||||
Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") {
|
||||
Fatal_U::Fatal_U(std::shared_ptr<Module> module, Core::System& system)
|
||||
: Module::Interface(std::move(module), system, "fatal:u") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &Fatal_U::ThrowFatal, "ThrowFatal"},
|
||||
{1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"},
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Service::Fatal {
|
||||
|
||||
class Fatal_U final : public Module::Interface {
|
||||
public:
|
||||
explicit Fatal_U(std::shared_ptr<Module> module);
|
||||
explicit Fatal_U(std::shared_ptr<Module> module, Core::System& system);
|
||||
~Fatal_U() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -40,7 +40,10 @@ static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
|
||||
if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
|
||||
return base;
|
||||
|
||||
return base->GetDirectoryRelative(dir_name);
|
||||
const auto res = base->GetDirectoryRelative(dir_name);
|
||||
if (res == nullptr)
|
||||
return base->CreateDirectoryRelative(dir_name);
|
||||
return res;
|
||||
}
|
||||
|
||||
VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)
|
||||
|
||||
@@ -149,7 +149,8 @@ private:
|
||||
|
||||
class INotificationService final : public ServiceFramework<INotificationService> {
|
||||
public:
|
||||
INotificationService(Common::UUID uuid) : ServiceFramework("INotificationService"), uuid(uuid) {
|
||||
INotificationService(Common::UUID uuid, Core::System& system)
|
||||
: ServiceFramework("INotificationService"), uuid(uuid) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &INotificationService::GetEvent, "GetEvent"},
|
||||
@@ -159,6 +160,9 @@ public:
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
notification_event = Kernel::WritableEvent::CreateEventPair(
|
||||
system.Kernel(), Kernel::ResetType::Manual, "INotificationService:NotifyEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -167,13 +171,6 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
if (!is_event_created) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
notification_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "INotificationService:NotifyEvent");
|
||||
is_event_created = true;
|
||||
}
|
||||
rb.PushCopyObjects(notification_event.readable);
|
||||
}
|
||||
|
||||
@@ -261,21 +258,21 @@ void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<INotificationService>(uuid);
|
||||
rb.PushIpcInterface<INotificationService>(uuid, system);
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)) {}
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)), system(system) {}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
|
||||
auto module = std::make_shared<Module>();
|
||||
std::make_shared<Friend>(module, "friend:a")->InstallAsService(service_manager);
|
||||
std::make_shared<Friend>(module, "friend:m")->InstallAsService(service_manager);
|
||||
std::make_shared<Friend>(module, "friend:s")->InstallAsService(service_manager);
|
||||
std::make_shared<Friend>(module, "friend:u")->InstallAsService(service_manager);
|
||||
std::make_shared<Friend>(module, "friend:v")->InstallAsService(service_manager);
|
||||
std::make_shared<Friend>(module, system, "friend:a")->InstallAsService(service_manager);
|
||||
std::make_shared<Friend>(module, system, "friend:m")->InstallAsService(service_manager);
|
||||
std::make_shared<Friend>(module, system, "friend:s")->InstallAsService(service_manager);
|
||||
std::make_shared<Friend>(module, system, "friend:u")->InstallAsService(service_manager);
|
||||
std::make_shared<Friend>(module, system, "friend:v")->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace Service::Friend
|
||||
@@ -6,13 +6,17 @@
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Friend {
|
||||
|
||||
class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
explicit Interface(std::shared_ptr<Module> module, const char* name);
|
||||
explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name);
|
||||
~Interface() override;
|
||||
|
||||
void CreateFriendService(Kernel::HLERequestContext& ctx);
|
||||
@@ -20,10 +24,11 @@ public:
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
Core::System& system;
|
||||
};
|
||||
};
|
||||
|
||||
/// Registers all Friend services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
|
||||
|
||||
} // namespace Service::Friend
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
namespace Service::Friend {
|
||||
|
||||
Friend::Friend(std::shared_ptr<Module> module, const char* name)
|
||||
: Interface(std::move(module), name) {
|
||||
Friend::Friend(std::shared_ptr<Module> module, Core::System& system, const char* name)
|
||||
: Interface(std::move(module), system, name) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &Friend::CreateFriendService, "CreateFriendService"},
|
||||
{1, &Friend::CreateNotificationService, "CreateNotificationService"},
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Service::Friend {
|
||||
|
||||
class Friend final : public Module::Interface {
|
||||
public:
|
||||
explicit Friend(std::shared_ptr<Module> module, const char* name);
|
||||
explicit Friend(std::shared_ptr<Module> module, Core::System& system, const char* name);
|
||||
~Friend() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
ControllerBase::ControllerBase() = default;
|
||||
ControllerBase::ControllerBase(Core::System& system) : system(system) {}
|
||||
ControllerBase::~ControllerBase() = default;
|
||||
|
||||
void ControllerBase::ActivateController() {
|
||||
|
||||
@@ -11,10 +11,14 @@ namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::HID {
|
||||
class ControllerBase {
|
||||
public:
|
||||
ControllerBase();
|
||||
explicit ControllerBase(Core::System& system);
|
||||
virtual ~ControllerBase();
|
||||
|
||||
// Called when the controller is initialized
|
||||
@@ -46,5 +50,7 @@ protected:
|
||||
s64_le entry_count;
|
||||
};
|
||||
static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
@@ -14,7 +14,8 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
|
||||
constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
|
||||
enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
|
||||
|
||||
Controller_DebugPad::Controller_DebugPad() = default;
|
||||
Controller_DebugPad::Controller_DebugPad(Core::System& system)
|
||||
: ControllerBase(system), system(system) {}
|
||||
Controller_DebugPad::~Controller_DebugPad() = default;
|
||||
|
||||
void Controller_DebugPad::OnInit() {}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
namespace Service::HID {
|
||||
class Controller_DebugPad final : public ControllerBase {
|
||||
public:
|
||||
Controller_DebugPad();
|
||||
explicit Controller_DebugPad(Core::System& system);
|
||||
~Controller_DebugPad() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
@@ -89,5 +89,6 @@ private:
|
||||
buttons;
|
||||
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
|
||||
analogs;
|
||||
Core::System& system;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
@@ -10,7 +10,8 @@
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
|
||||
|
||||
Controller_Gesture::Controller_Gesture() = default;
|
||||
Controller_Gesture::Controller_Gesture(Core::System& system)
|
||||
: ControllerBase(system), system(system) {}
|
||||
Controller_Gesture::~Controller_Gesture() = default;
|
||||
|
||||
void Controller_Gesture::OnInit() {}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
namespace Service::HID {
|
||||
class Controller_Gesture final : public ControllerBase {
|
||||
public:
|
||||
Controller_Gesture();
|
||||
explicit Controller_Gesture(Core::System& system);
|
||||
~Controller_Gesture() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
@@ -59,5 +59,6 @@ private:
|
||||
std::array<GestureState, 17> gesture_states;
|
||||
};
|
||||
SharedMemory shared_memory{};
|
||||
Core::System& system;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
@@ -12,7 +12,8 @@ namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
|
||||
constexpr u8 KEYS_PER_BYTE = 8;
|
||||
|
||||
Controller_Keyboard::Controller_Keyboard() = default;
|
||||
Controller_Keyboard::Controller_Keyboard(Core::System& system)
|
||||
: ControllerBase(system), system(system) {}
|
||||
Controller_Keyboard::~Controller_Keyboard() = default;
|
||||
|
||||
void Controller_Keyboard::OnInit() {}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
namespace Service::HID {
|
||||
class Controller_Keyboard final : public ControllerBase {
|
||||
public:
|
||||
Controller_Keyboard();
|
||||
explicit Controller_Keyboard(Core::System& system);
|
||||
~Controller_Keyboard() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
@@ -53,5 +53,6 @@ private:
|
||||
keyboard_keys;
|
||||
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
|
||||
keyboard_mods;
|
||||
Core::System& system;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
|
||||
|
||||
Controller_Mouse::Controller_Mouse() = default;
|
||||
Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system), system(system) {}
|
||||
Controller_Mouse::~Controller_Mouse() = default;
|
||||
|
||||
void Controller_Mouse::OnInit() {}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
namespace Service::HID {
|
||||
class Controller_Mouse final : public ControllerBase {
|
||||
public:
|
||||
Controller_Mouse();
|
||||
explicit Controller_Mouse(Core::System& system);
|
||||
~Controller_Mouse() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
@@ -53,5 +53,6 @@ private:
|
||||
std::unique_ptr<Input::MouseDevice> mouse_device;
|
||||
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
|
||||
mouse_button_devices;
|
||||
Core::System& system;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
@@ -93,7 +93,7 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
|
||||
};
|
||||
}
|
||||
|
||||
Controller_NPad::Controller_NPad() = default;
|
||||
Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
|
||||
Controller_NPad::~Controller_NPad() = default;
|
||||
|
||||
void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
|
||||
@@ -168,9 +168,11 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
|
||||
}
|
||||
|
||||
void Controller_NPad::OnInit() {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
styleset_changed_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "npad:NpadStyleSetChanged");
|
||||
auto& kernel = system.Kernel();
|
||||
for (std::size_t i = 0; i < styleset_changed_events.size(); i++) {
|
||||
styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, fmt::format("npad:NpadStyleSetChanged_{}", i));
|
||||
}
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
return;
|
||||
@@ -453,7 +455,7 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
|
||||
had_controller_update = true;
|
||||
}
|
||||
if (had_controller_update) {
|
||||
styleset_changed_event.writable->Signal();
|
||||
styleset_changed_events[i].writable->Signal();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -468,7 +470,6 @@ std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
|
||||
}
|
||||
|
||||
void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
|
||||
styleset_changed_event.writable->Signal();
|
||||
hold_type = joy_hold_type;
|
||||
}
|
||||
|
||||
@@ -479,7 +480,10 @@ Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
|
||||
void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
|
||||
const std::size_t npad_index = NPadIdToIndex(npad_id);
|
||||
ASSERT(npad_index < shared_memory_entries.size());
|
||||
shared_memory_entries[npad_index].pad_assignment = assignment_mode;
|
||||
if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) {
|
||||
styleset_changed_events[npad_index].writable->Signal();
|
||||
shared_memory_entries[npad_index].pad_assignment = assignment_mode;
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
|
||||
@@ -498,11 +502,13 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
|
||||
last_processed_vibration = vibrations.back();
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent() const {
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(
|
||||
u32 npad_id) const {
|
||||
// TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
|
||||
// be signalled at least once, and signaled after a new controller is connected?
|
||||
styleset_changed_event.writable->Signal();
|
||||
return styleset_changed_event.readable;
|
||||
const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
|
||||
styleset_event.writable->Signal();
|
||||
return styleset_event.readable;
|
||||
}
|
||||
|
||||
Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
|
||||
|
||||
@@ -20,7 +20,7 @@ constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
|
||||
|
||||
class Controller_NPad final : public ControllerBase {
|
||||
public:
|
||||
Controller_NPad();
|
||||
explicit Controller_NPad(Core::System& system);
|
||||
~Controller_NPad() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
@@ -109,7 +109,7 @@ public:
|
||||
void VibrateController(const std::vector<u32>& controller_ids,
|
||||
const std::vector<Vibration>& vibrations);
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> GetStyleSetChangedEvent() const;
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
|
||||
Vibration GetLastVibration() const;
|
||||
|
||||
void AddNewController(NPadControllerType controller);
|
||||
@@ -315,7 +315,8 @@ private:
|
||||
sticks;
|
||||
std::vector<u32> supported_npad_id_types{};
|
||||
NpadHoldType hold_type{NpadHoldType::Vertical};
|
||||
Kernel::EventPair styleset_changed_event;
|
||||
// Each controller should have their own styleset changed event
|
||||
std::array<Kernel::EventPair, 10> styleset_changed_events;
|
||||
Vibration last_processed_vibration{};
|
||||
std::array<ControllerHolder, 10> connected_controllers{};
|
||||
bool can_controllers_vibrate{true};
|
||||
@@ -327,5 +328,6 @@ private:
|
||||
std::array<ControllerPad, 10> npad_pad_states{};
|
||||
bool IsControllerSupported(NPadControllerType controller);
|
||||
bool is_in_lr_assignment_mode{false};
|
||||
Core::System& system;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
@@ -9,7 +9,8 @@
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
Controller_Stubbed::Controller_Stubbed() = default;
|
||||
Controller_Stubbed::Controller_Stubbed(Core::System& system)
|
||||
: ControllerBase(system), system(system) {}
|
||||
Controller_Stubbed::~Controller_Stubbed() = default;
|
||||
|
||||
void Controller_Stubbed::OnInit() {}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
namespace Service::HID {
|
||||
class Controller_Stubbed final : public ControllerBase {
|
||||
public:
|
||||
Controller_Stubbed();
|
||||
explicit Controller_Stubbed(Core::System& system);
|
||||
~Controller_Stubbed() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
@@ -30,5 +30,6 @@ public:
|
||||
private:
|
||||
bool smart_update{};
|
||||
std::size_t common_offset{};
|
||||
Core::System& system;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
@@ -13,7 +13,8 @@
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
|
||||
|
||||
Controller_Touchscreen::Controller_Touchscreen() = default;
|
||||
Controller_Touchscreen::Controller_Touchscreen(Core::System& system)
|
||||
: ControllerBase(system), system(system) {}
|
||||
Controller_Touchscreen::~Controller_Touchscreen() = default;
|
||||
|
||||
void Controller_Touchscreen::OnInit() {}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
namespace Service::HID {
|
||||
class Controller_Touchscreen final : public ControllerBase {
|
||||
public:
|
||||
Controller_Touchscreen();
|
||||
explicit Controller_Touchscreen(Core::System& system);
|
||||
~Controller_Touchscreen() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
@@ -69,5 +69,6 @@ private:
|
||||
TouchScreenSharedMemory shared_memory{};
|
||||
std::unique_ptr<Input::TouchDevice> touch_device;
|
||||
s64_le last_touch{};
|
||||
Core::System& system;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
@@ -10,7 +10,7 @@
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
|
||||
|
||||
Controller_XPad::Controller_XPad() = default;
|
||||
Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system), system(system) {}
|
||||
Controller_XPad::~Controller_XPad() = default;
|
||||
|
||||
void Controller_XPad::OnInit() {}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
namespace Service::HID {
|
||||
class Controller_XPad final : public ControllerBase {
|
||||
public:
|
||||
Controller_XPad();
|
||||
explicit Controller_XPad(Core::System& system);
|
||||
~Controller_XPad() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
@@ -56,5 +56,6 @@ private:
|
||||
};
|
||||
static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
|
||||
SharedMemory shared_memory{};
|
||||
Core::System& system;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
@@ -42,13 +42,14 @@ constexpr s64 accelerometer_update_ticks = static_cast<s64>(Core::Timing::BASE_C
|
||||
constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
|
||||
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
|
||||
|
||||
IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
|
||||
IAppletResource::IAppletResource(Core::System& system)
|
||||
: ServiceFramework("IAppletResource"), system(system) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto& kernel = system.Kernel();
|
||||
shared_mem = Kernel::SharedMemory::Create(
|
||||
kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
|
||||
Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
|
||||
@@ -74,7 +75,7 @@ IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
|
||||
GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
|
||||
|
||||
// Register update callbacks
|
||||
auto& core_timing = Core::System::GetInstance().CoreTiming();
|
||||
auto& core_timing = system.CoreTiming();
|
||||
pad_update_event =
|
||||
core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) {
|
||||
UpdateControllers(userdata, cycles_late);
|
||||
@@ -96,7 +97,7 @@ void IAppletResource::DeactivateController(HidController controller) {
|
||||
}
|
||||
|
||||
IAppletResource ::~IAppletResource() {
|
||||
Core::System::GetInstance().CoreTiming().UnscheduleEvent(pad_update_event, 0);
|
||||
system.CoreTiming().UnscheduleEvent(pad_update_event, 0);
|
||||
}
|
||||
|
||||
void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
|
||||
@@ -108,7 +109,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) {
|
||||
auto& core_timing = Core::System::GetInstance().CoreTiming();
|
||||
auto& core_timing = system.CoreTiming();
|
||||
|
||||
const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
|
||||
for (const auto& controller : controllers) {
|
||||
@@ -141,13 +142,13 @@ private:
|
||||
|
||||
std::shared_ptr<IAppletResource> Hid::GetAppletResource() {
|
||||
if (applet_resource == nullptr) {
|
||||
applet_resource = std::make_shared<IAppletResource>();
|
||||
applet_resource = std::make_shared<IAppletResource>(system);
|
||||
}
|
||||
|
||||
return applet_resource;
|
||||
}
|
||||
|
||||
Hid::Hid() : ServiceFramework("hid") {
|
||||
Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &Hid::CreateAppletResource, "CreateAppletResource"},
|
||||
@@ -200,13 +201,13 @@ Hid::Hid() : ServiceFramework("hid") {
|
||||
{120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
|
||||
{121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
|
||||
{122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"},
|
||||
{123, nullptr, "SetNpadJoyAssignmentModeSingleByDefault"},
|
||||
{123, &Hid::SetNpadJoyAssignmentModeSingle, "SetNpadJoyAssignmentModeSingle"},
|
||||
{124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"},
|
||||
{125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"},
|
||||
{126, &Hid::StartLrAssignmentMode, "StartLrAssignmentMode"},
|
||||
{127, &Hid::StopLrAssignmentMode, "StopLrAssignmentMode"},
|
||||
{128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
|
||||
{129, nullptr, "GetNpadHandheldActivationMode"},
|
||||
{129, &Hid::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"},
|
||||
{130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"},
|
||||
{131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"},
|
||||
{132, nullptr, "EnableUnintendedHomeButtonInputProtection"},
|
||||
@@ -286,7 +287,7 @@ void Hid::CreateAppletResource(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||
|
||||
if (applet_resource == nullptr) {
|
||||
applet_resource = std::make_shared<IAppletResource>();
|
||||
applet_resource = std::make_shared<IAppletResource>(system);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
@@ -479,7 +480,7 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.GetStyleSetChangedEvent());
|
||||
.GetStyleSetChangedEvent(npad_id));
|
||||
}
|
||||
|
||||
void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
|
||||
@@ -543,10 +544,126 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", npad_id,
|
||||
applet_resource_user_id);
|
||||
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Single);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
|
||||
// TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto npad_id{rp.Pop<u32>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
const auto npad_joy_device_type{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(Service_HID,
|
||||
"(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
|
||||
npad_id, applet_resource_user_id, npad_joy_device_type);
|
||||
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Single);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto npad_id{rp.Pop<u32>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id,
|
||||
applet_resource_user_id);
|
||||
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto unknown_1{rp.Pop<u32>()};
|
||||
const auto unknown_2{rp.Pop<u32>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(Service_HID,
|
||||
"(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
|
||||
unknown_1, unknown_2, applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.StartLRAssignmentMode();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.StopLRAssignmentMode();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
const auto mode{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}",
|
||||
applet_resource_user_id, mode);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
|
||||
applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto npad_1{rp.Pop<u32>()};
|
||||
const auto npad_2{rp.Pop<u32>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, npad_1={}, npad_2={}",
|
||||
applet_resource_user_id, npad_1, npad_2);
|
||||
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
if (controller.SwapNpadAssignment(npad_1, npad_2)) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
} else {
|
||||
LOG_ERROR(Service_HID, "Npads are not connected!");
|
||||
rb.Push(ERR_NPAD_NOT_CONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
@@ -621,47 +738,6 @@ void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad).GetLastVibration());
|
||||
}
|
||||
|
||||
void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto npad_id{rp.Pop<u32>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id,
|
||||
applet_resource_user_id);
|
||||
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto unknown_1{rp.Pop<u32>()};
|
||||
const auto unknown_2{rp.Pop<u32>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(Service_HID,
|
||||
"(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
|
||||
unknown_1, unknown_2, applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
const auto mode{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}",
|
||||
applet_resource_user_id, mode);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_HID, "called");
|
||||
|
||||
@@ -755,49 +831,6 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.StartLRAssignmentMode();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
controller.StopLRAssignmentMode();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto npad_1{rp.Pop<u32>()};
|
||||
const auto npad_2{rp.Pop<u32>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, npad_1={}, npad_2={}",
|
||||
applet_resource_user_id, npad_1, npad_2);
|
||||
|
||||
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
if (controller.SwapNpadAssignment(npad_1, npad_2)) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
} else {
|
||||
LOG_ERROR(Service_HID, "Npads are not connected!");
|
||||
rb.Push(ERR_NPAD_NOT_CONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
class HidDbg final : public ServiceFramework<HidDbg> {
|
||||
public:
|
||||
explicit HidDbg() : ServiceFramework{"hid:dbg"} {
|
||||
@@ -1053,14 +1086,14 @@ void ReloadInputDevices() {
|
||||
Settings::values.is_device_reload_pending.store(true);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
std::make_shared<Hid>()->InstallAsService(service_manager);
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
|
||||
std::make_shared<Hid>(system)->InstallAsService(service_manager);
|
||||
std::make_shared<HidBus>()->InstallAsService(service_manager);
|
||||
std::make_shared<HidDbg>()->InstallAsService(service_manager);
|
||||
std::make_shared<HidSys>()->InstallAsService(service_manager);
|
||||
std::make_shared<HidTmp>()->InstallAsService(service_manager);
|
||||
|
||||
std::make_shared<IRS>()->InstallAsService(service_manager);
|
||||
std::make_shared<IRS>(system)->InstallAsService(service_manager);
|
||||
std::make_shared<IRS_SYS>()->InstallAsService(service_manager);
|
||||
|
||||
std::make_shared<XCD_SYS>()->InstallAsService(service_manager);
|
||||
|
||||
@@ -42,7 +42,7 @@ enum class HidController : std::size_t {
|
||||
|
||||
class IAppletResource final : public ServiceFramework<IAppletResource> {
|
||||
public:
|
||||
IAppletResource();
|
||||
explicit IAppletResource(Core::System& system);
|
||||
~IAppletResource() override;
|
||||
|
||||
void ActivateController(HidController controller);
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
private:
|
||||
template <typename T>
|
||||
void MakeController(HidController controller) {
|
||||
controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>();
|
||||
controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system);
|
||||
}
|
||||
|
||||
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
|
||||
@@ -70,6 +70,7 @@ private:
|
||||
Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
|
||||
|
||||
Core::Timing::EventType* pad_update_event;
|
||||
Core::System& system;
|
||||
|
||||
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
|
||||
controllers{};
|
||||
@@ -77,7 +78,7 @@ private:
|
||||
|
||||
class Hid final : public ServiceFramework<Hid> {
|
||||
public:
|
||||
Hid();
|
||||
explicit Hid(Core::System& system);
|
||||
~Hid() override;
|
||||
|
||||
std::shared_ptr<IAppletResource> GetAppletResource();
|
||||
@@ -104,14 +105,19 @@ private:
|
||||
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
|
||||
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx);
|
||||
void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx);
|
||||
void StartLrAssignmentMode(Kernel::HLERequestContext& ctx);
|
||||
void StopLrAssignmentMode(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
|
||||
void GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
|
||||
void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
|
||||
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
|
||||
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
|
||||
void SendVibrationValue(Kernel::HLERequestContext& ctx);
|
||||
void SendVibrationValues(Kernel::HLERequestContext& ctx);
|
||||
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx);
|
||||
void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
|
||||
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
|
||||
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx);
|
||||
void PermitVibration(Kernel::HLERequestContext& ctx);
|
||||
@@ -121,17 +127,15 @@ private:
|
||||
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
|
||||
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
|
||||
void StartLrAssignmentMode(Kernel::HLERequestContext& ctx);
|
||||
void StopLrAssignmentMode(Kernel::HLERequestContext& ctx);
|
||||
void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<IAppletResource> applet_resource;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
/// Reload input devices. Used when input configuration changed
|
||||
void ReloadInputDevices();
|
||||
|
||||
/// Registers all HID services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
|
||||
|
||||
} // namespace Service::HID
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
IRS::IRS() : ServiceFramework{"irs"} {
|
||||
IRS::IRS(Core::System& system) : ServiceFramework{"irs"}, system(system) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{302, &IRS::ActivateIrsensor, "ActivateIrsensor"},
|
||||
@@ -37,7 +37,7 @@ IRS::IRS() : ServiceFramework{"irs"} {
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto& kernel = system.Kernel();
|
||||
shared_mem = Kernel::SharedMemory::Create(
|
||||
kernel, nullptr, 0x8000, Kernel::MemoryPermission::ReadWrite,
|
||||
Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "IRS:SharedMemory");
|
||||
@@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 5};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<u64>(Core::System::GetInstance().CoreTiming().GetTicks());
|
||||
rb.PushRaw<u64>(system.CoreTiming().GetTicks());
|
||||
rb.PushRaw<u32>(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Service::HID {
|
||||
|
||||
class IRS final : public ServiceFramework<IRS> {
|
||||
public:
|
||||
explicit IRS();
|
||||
explicit IRS(Core::System& system);
|
||||
~IRS() override;
|
||||
|
||||
private:
|
||||
@@ -39,6 +39,7 @@ private:
|
||||
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
|
||||
Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
|
||||
const u32 device_handle{0xABCD};
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
class IRS_SYS final : public ServiceFramework<IRS_SYS> {
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "core/hle/service/lbl/lbl.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
|
||||
namespace Service::LBL {
|
||||
|
||||
@@ -18,21 +20,21 @@ public:
|
||||
explicit LBL() : ServiceFramework{"lbl"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "SaveCurrentSetting"},
|
||||
{1, nullptr, "LoadCurrentSetting"},
|
||||
{2, nullptr, "SetCurrentBrightnessSetting"},
|
||||
{3, nullptr, "GetCurrentBrightnessSetting"},
|
||||
{4, nullptr, "ApplyCurrentBrightnessSettingToBacklight"},
|
||||
{5, nullptr, "GetBrightnessSettingAppliedToBacklight"},
|
||||
{6, nullptr, "SwitchBacklightOn"},
|
||||
{7, nullptr, "SwitchBacklightOff"},
|
||||
{8, nullptr, "GetBacklightSwitchStatus"},
|
||||
{9, nullptr, "EnableDimming"},
|
||||
{10, nullptr, "DisableDimming"},
|
||||
{11, nullptr, "IsDimmingEnabled"},
|
||||
{12, nullptr, "EnableAutoBrightnessControl"},
|
||||
{13, nullptr, "DisableAutoBrightnessControl"},
|
||||
{14, nullptr, "IsAutoBrightnessControlEnabled"},
|
||||
{0, &LBL::SaveCurrentSetting, "SaveCurrentSetting"},
|
||||
{1, &LBL::LoadCurrentSetting, "LoadCurrentSetting"},
|
||||
{2, &LBL::SetCurrentBrightnessSetting, "SetCurrentBrightnessSetting"},
|
||||
{3, &LBL::GetCurrentBrightnessSetting, "GetCurrentBrightnessSetting"},
|
||||
{4, &LBL::ApplyCurrentBrightnessSettingToBacklight, "ApplyCurrentBrightnessSettingToBacklight"},
|
||||
{5, &LBL::GetBrightnessSettingAppliedToBacklight, "GetBrightnessSettingAppliedToBacklight"},
|
||||
{6, &LBL::SwitchBacklightOn, "SwitchBacklightOn"},
|
||||
{7, &LBL::SwitchBacklightOff, "SwitchBacklightOff"},
|
||||
{8, &LBL::GetBacklightSwitchStatus, "GetBacklightSwitchStatus"},
|
||||
{9, &LBL::EnableDimming, "EnableDimming"},
|
||||
{10, &LBL::DisableDimming, "DisableDimming"},
|
||||
{11, &LBL::IsDimmingEnabled, "IsDimmingEnabled"},
|
||||
{12, &LBL::EnableAutoBrightnessControl, "EnableAutoBrightnessControl"},
|
||||
{13, &LBL::DisableAutoBrightnessControl, "DisableAutoBrightnessControl"},
|
||||
{14, &LBL::IsAutoBrightnessControlEnabled, "IsAutoBrightnessControlEnabled"},
|
||||
{15, nullptr, "SetAmbientLightSensorValue"},
|
||||
{16, nullptr, "GetAmbientLightSensorValue"},
|
||||
{17, nullptr, "SetBrightnessReflectionDelayLevel"},
|
||||
@@ -42,8 +44,8 @@ public:
|
||||
{21, nullptr, "SetCurrentAmbientLightSensorMapping"},
|
||||
{22, nullptr, "GetCurrentAmbientLightSensorMapping"},
|
||||
{23, nullptr, "IsAmbientLightSensorAvailable"},
|
||||
{24, nullptr, "SetCurrentBrightnessSettingForVrMode"},
|
||||
{25, nullptr, "GetCurrentBrightnessSettingForVrMode"},
|
||||
{24, &LBL::SetCurrentBrightnessSettingForVrMode, "SetCurrentBrightnessSettingForVrMode"},
|
||||
{25, &LBL::GetCurrentBrightnessSettingForVrMode, "GetCurrentBrightnessSettingForVrMode"},
|
||||
{26, &LBL::EnableVrMode, "EnableVrMode"},
|
||||
{27, &LBL::DisableVrMode, "DisableVrMode"},
|
||||
{28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"},
|
||||
@@ -53,13 +55,209 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void LoadFromSettings() {
|
||||
current_brightness = Settings::values.backlight_brightness;
|
||||
current_vr_mode_brightness = Settings::values.backlight_brightness;
|
||||
|
||||
if (auto_brightness_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (vr_mode_enabled) {
|
||||
Renderer().SetCurrentBrightness(current_vr_mode_brightness);
|
||||
} else {
|
||||
Renderer().SetCurrentBrightness(current_brightness);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
f32 GetAutoBrightnessValue() const {
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
VideoCore::RendererBase& Renderer() {
|
||||
return Core::System::GetInstance().Renderer();
|
||||
}
|
||||
|
||||
void SaveCurrentSetting(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
Settings::values.backlight_brightness = current_brightness;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void LoadCurrentSetting(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
LoadFromSettings();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void SetCurrentBrightnessSetting(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto value = rp.PopRaw<f32>();
|
||||
|
||||
LOG_DEBUG(Service_LBL, "called, value={:.3f}", value);
|
||||
|
||||
current_brightness = std::clamp(value, 0.0f, 1.0f);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetCurrentBrightnessSetting(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(current_brightness);
|
||||
}
|
||||
|
||||
void ApplyCurrentBrightnessSettingToBacklight(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
if (!auto_brightness_enabled) {
|
||||
Renderer().SetCurrentBrightness(vr_mode_enabled ? current_vr_mode_brightness
|
||||
: current_brightness);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetBrightnessSettingAppliedToBacklight(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(Renderer().GetCurrentResultantBrightness());
|
||||
}
|
||||
|
||||
void SwitchBacklightOn(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto fade_time = rp.PopRaw<u64>();
|
||||
|
||||
LOG_DEBUG(Service_LBL, "called, fade_time={:016X}", fade_time);
|
||||
|
||||
Renderer().SetBacklightStatus(true, fade_time);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void SwitchBacklightOff(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto fade_time = rp.PopRaw<u64>();
|
||||
|
||||
LOG_DEBUG(Service_LBL, "called, fade_time={:016X}", fade_time);
|
||||
|
||||
Renderer().SetBacklightStatus(false, fade_time);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetBacklightSwitchStatus(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u8>(Renderer().GetBacklightStatus());
|
||||
}
|
||||
|
||||
void EnableDimming(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
dimming_enabled = true;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void DisableDimming(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "callled");
|
||||
|
||||
dimming_enabled = false;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void IsDimmingEnabled(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u8>(dimming_enabled);
|
||||
}
|
||||
|
||||
void EnableAutoBrightnessControl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
auto_brightness_enabled = true;
|
||||
Renderer().SetCurrentBrightness(GetAutoBrightnessValue());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void DisableAutoBrightnessControl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
auto_brightness_enabled = false;
|
||||
Renderer().SetCurrentBrightness(current_brightness);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void IsAutoBrightnessControlEnabled(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u8>(auto_brightness_enabled);
|
||||
}
|
||||
|
||||
void SetCurrentBrightnessSettingForVrMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto value = rp.PopRaw<f32>();
|
||||
|
||||
LOG_DEBUG(Service_LBL, "called, value={:.3f}", value);
|
||||
|
||||
current_vr_mode_brightness = std::clamp(value, 0.0f, 1.0f);
|
||||
|
||||
if (vr_mode_enabled && !auto_brightness_enabled) {
|
||||
Renderer().SetCurrentBrightness(value);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetCurrentBrightnessSettingForVrMode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(current_vr_mode_brightness);
|
||||
}
|
||||
|
||||
void EnableVrMode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
if (!vr_mode_enabled && !auto_brightness_enabled &&
|
||||
current_brightness != current_vr_mode_brightness) {
|
||||
Renderer().SetCurrentBrightness(current_vr_mode_brightness);
|
||||
}
|
||||
|
||||
vr_mode_enabled = true;
|
||||
}
|
||||
|
||||
@@ -69,6 +267,11 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
if (vr_mode_enabled && !auto_brightness_enabled &&
|
||||
current_brightness != current_vr_mode_brightness) {
|
||||
Renderer().SetCurrentBrightness(current_brightness);
|
||||
}
|
||||
|
||||
vr_mode_enabled = false;
|
||||
}
|
||||
|
||||
@@ -80,9 +283,27 @@ private:
|
||||
rb.Push(vr_mode_enabled);
|
||||
}
|
||||
|
||||
bool auto_brightness_enabled = false;
|
||||
bool dimming_enabled = true;
|
||||
|
||||
f32 current_brightness = GetAutoBrightnessValue();
|
||||
f32 current_vr_mode_brightness = GetAutoBrightnessValue();
|
||||
|
||||
bool vr_mode_enabled = false;
|
||||
};
|
||||
|
||||
void RequestLoadCurrentSetting(SM::ServiceManager& sm) {
|
||||
if (&sm == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto lbl = sm.GetService<LBL>("lbl");
|
||||
|
||||
if (lbl) {
|
||||
lbl->LoadFromSettings();
|
||||
}
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
std::make_shared<LBL>()->InstallAsService(sm);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ class ServiceManager;
|
||||
|
||||
namespace Service::LBL {
|
||||
|
||||
// Requests the LBL service passed to load brightness values from Settings
|
||||
void RequestLoadCurrentSetting(SM::ServiceManager& sm);
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm);
|
||||
|
||||
} // namespace Service::LBL
|
||||
@@ -78,7 +78,7 @@ public:
|
||||
|
||||
class RelocatableObject final : public ServiceFramework<RelocatableObject> {
|
||||
public:
|
||||
explicit RelocatableObject() : ServiceFramework{"ldr:ro"} {
|
||||
explicit RelocatableObject(Core::System& system) : ServiceFramework{"ldr:ro"}, system(system) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &RelocatableObject::LoadNro, "LoadNro"},
|
||||
@@ -364,7 +364,7 @@ public:
|
||||
vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size,
|
||||
Kernel::VMAPermission::ReadWrite);
|
||||
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
system.InvalidateCpuInstructionCaches();
|
||||
|
||||
nro.insert_or_assign(*map_address,
|
||||
NROInfo{hash, nro_address, nro_size, bss_address, bss_size});
|
||||
@@ -430,7 +430,7 @@ public:
|
||||
.IsSuccess());
|
||||
}
|
||||
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
system.InvalidateCpuInstructionCaches();
|
||||
|
||||
nro.erase(iter);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -516,13 +516,14 @@ private:
|
||||
Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) &&
|
||||
Common::Is4KBAligned(header.rw_size);
|
||||
}
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
|
||||
std::make_shared<DebugMonitor>()->InstallAsService(sm);
|
||||
std::make_shared<ProcessManager>()->InstallAsService(sm);
|
||||
std::make_shared<Shell>()->InstallAsService(sm);
|
||||
std::make_shared<RelocatableObject>()->InstallAsService(sm);
|
||||
std::make_shared<RelocatableObject>(system)->InstallAsService(sm);
|
||||
}
|
||||
|
||||
} // namespace Service::LDR
|
||||
@@ -11,6 +11,6 @@ class ServiceManager;
|
||||
namespace Service::LDR {
|
||||
|
||||
/// Registers all LDR services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& sm);
|
||||
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
|
||||
|
||||
} // namespace Service::LDR
|
||||
@@ -23,9 +23,9 @@ constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
|
||||
constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
|
||||
} // namespace ErrCodes
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)), system(system) {
|
||||
auto& kernel = system.Kernel();
|
||||
nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IUser:NFCTagDetected");
|
||||
}
|
||||
@@ -34,8 +34,8 @@ Module::Interface::~Interface() = default;
|
||||
|
||||
class IUser final : public ServiceFramework<IUser> {
|
||||
public:
|
||||
IUser(Module::Interface& nfp_interface)
|
||||
: ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) {
|
||||
IUser(Module::Interface& nfp_interface, Core::System& system)
|
||||
: ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface), system(system) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IUser::Initialize, "Initialize"},
|
||||
{1, &IUser::Finalize, "Finalize"},
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto& kernel = system.Kernel();
|
||||
deactivate_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "IUser:DeactivateEvent");
|
||||
availability_change_event = Kernel::WritableEvent::CreateEventPair(
|
||||
@@ -324,6 +324,7 @@ private:
|
||||
Kernel::EventPair deactivate_event;
|
||||
Kernel::EventPair availability_change_event;
|
||||
const Module::Interface& nfp_interface;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
|
||||
@@ -331,7 +332,7 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IUser>(*this);
|
||||
rb.PushIpcInterface<IUser>(*this, system);
|
||||
}
|
||||
|
||||
bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
|
||||
@@ -353,9 +354,9 @@ const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const
|
||||
return amiibo;
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
|
||||
auto module = std::make_shared<Module>();
|
||||
std::make_shared<NFP_User>(module)->InstallAsService(service_manager);
|
||||
std::make_shared<NFP_User>(module, system)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace Service::NFP
|
||||
@@ -16,7 +16,7 @@ class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
explicit Interface(std::shared_ptr<Module> module, const char* name);
|
||||
explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name);
|
||||
~Interface() override;
|
||||
|
||||
struct ModelInfo {
|
||||
@@ -43,9 +43,10 @@ public:
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
Core::System& system;
|
||||
};
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
|
||||
|
||||
} // namespace Service::NFP
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
namespace Service::NFP {
|
||||
|
||||
NFP_User::NFP_User(std::shared_ptr<Module> module)
|
||||
: Module::Interface(std::move(module), "nfp:user") {
|
||||
NFP_User::NFP_User(std::shared_ptr<Module> module, Core::System& system)
|
||||
: Module::Interface(std::move(module), system, "nfp:user") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &NFP_User::CreateUserInterface, "CreateUserInterface"},
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Service::NFP {
|
||||
|
||||
class NFP_User final : public Module::Interface {
|
||||
public:
|
||||
explicit NFP_User(std::shared_ptr<Module> module);
|
||||
explicit NFP_User(std::shared_ptr<Module> module, Core::System& system);
|
||||
~NFP_User() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
|
||||
class IRequest final : public ServiceFramework<IRequest> {
|
||||
public:
|
||||
explicit IRequest() : ServiceFramework("IRequest") {
|
||||
explicit IRequest(Core::System& system) : ServiceFramework("IRequest") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IRequest::GetRequestState, "GetRequestState"},
|
||||
{1, &IRequest::GetResult, "GetResult"},
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto& kernel = system.Kernel();
|
||||
event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IRequest:Event1");
|
||||
event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
@@ -130,7 +130,7 @@ public:
|
||||
|
||||
class IGeneralService final : public ServiceFramework<IGeneralService> {
|
||||
public:
|
||||
IGeneralService();
|
||||
IGeneralService(Core::System& system);
|
||||
|
||||
private:
|
||||
void GetClientId(Kernel::HLERequestContext& ctx) {
|
||||
@@ -155,7 +155,7 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IRequest>();
|
||||
rb.PushIpcInterface<IRequest>(system);
|
||||
}
|
||||
void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
@@ -198,9 +198,11 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u8>(0);
|
||||
}
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
|
||||
IGeneralService::IGeneralService(Core::System& system)
|
||||
: ServiceFramework("IGeneralService"), system(system) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, &IGeneralService::GetClientId, "GetClientId"},
|
||||
{2, &IGeneralService::CreateScanRequest, "CreateScanRequest"},
|
||||
@@ -245,7 +247,8 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
|
||||
|
||||
class NetworkInterface final : public ServiceFramework<NetworkInterface> {
|
||||
public:
|
||||
explicit NetworkInterface(const char* name) : ServiceFramework{name} {
|
||||
explicit NetworkInterface(const char* name, Core::System& system)
|
||||
: ServiceFramework{name}, system(system) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{4, &NetworkInterface::CreateGeneralServiceOld, "CreateGeneralServiceOld"},
|
||||
{5, &NetworkInterface::CreateGeneralService, "CreateGeneralService"},
|
||||
@@ -258,7 +261,7 @@ public:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
rb.PushIpcInterface<IGeneralService>(system);
|
||||
}
|
||||
|
||||
void CreateGeneralService(Kernel::HLERequestContext& ctx) {
|
||||
@@ -266,14 +269,17 @@ public:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
rb.PushIpcInterface<IGeneralService>(system);
|
||||
}
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
std::make_shared<NetworkInterface>("nifm:a")->InstallAsService(service_manager);
|
||||
std::make_shared<NetworkInterface>("nifm:s")->InstallAsService(service_manager);
|
||||
std::make_shared<NetworkInterface>("nifm:u")->InstallAsService(service_manager);
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
|
||||
std::make_shared<NetworkInterface>("nifm:a", system)->InstallAsService(service_manager);
|
||||
std::make_shared<NetworkInterface>("nifm:s", system)->InstallAsService(service_manager);
|
||||
std::make_shared<NetworkInterface>("nifm:u", system)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace Service::NIFM
|
||||
@@ -8,9 +8,13 @@ namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::NIFM {
|
||||
|
||||
/// Registers all NIFM services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
|
||||
|
||||
} // namespace Service::NIFM
|
||||
@@ -126,7 +126,7 @@ public:
|
||||
class IEnsureNetworkClockAvailabilityService final
|
||||
: public ServiceFramework<IEnsureNetworkClockAvailabilityService> {
|
||||
public:
|
||||
IEnsureNetworkClockAvailabilityService()
|
||||
explicit IEnsureNetworkClockAvailabilityService(Core::System& system)
|
||||
: ServiceFramework("IEnsureNetworkClockAvailabilityService") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IEnsureNetworkClockAvailabilityService::StartTask, "StartTask"},
|
||||
@@ -139,7 +139,7 @@ public:
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto& kernel = system.Kernel();
|
||||
finished_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic,
|
||||
"IEnsureNetworkClockAvailabilityService:FinishEvent");
|
||||
@@ -200,7 +200,7 @@ private:
|
||||
|
||||
class NTC final : public ServiceFramework<NTC> {
|
||||
public:
|
||||
explicit NTC() : ServiceFramework{"ntc"} {
|
||||
explicit NTC(Core::System& system) : ServiceFramework{"ntc"}, system(system) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &NTC::OpenEnsureNetworkClockAvailabilityService, "OpenEnsureNetworkClockAvailabilityService"},
|
||||
@@ -218,7 +218,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>();
|
||||
rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(system);
|
||||
}
|
||||
|
||||
// TODO(ogniK): Do we need these?
|
||||
@@ -235,13 +235,14 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
|
||||
std::make_shared<NIM>()->InstallAsService(sm);
|
||||
std::make_shared<NIM_ECA>()->InstallAsService(sm);
|
||||
std::make_shared<NIM_SHP>()->InstallAsService(sm);
|
||||
std::make_shared<NTC>()->InstallAsService(sm);
|
||||
std::make_shared<NTC>(system)->InstallAsService(sm);
|
||||
}
|
||||
|
||||
} // namespace Service::NIM
|
||||
@@ -8,8 +8,12 @@ namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::NIM {
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm);
|
||||
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
|
||||
|
||||
} // namespace Service::NIM
|
||||
@@ -617,7 +617,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, FileSystem::FileSystemController& fsc) {
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
|
||||
|
||||
std::make_shared<NS>("ns:am2")->InstallAsService(service_manager);
|
||||
std::make_shared<NS>("ns:ec")->InstallAsService(service_manager);
|
||||
std::make_shared<NS>("ns:rid")->InstallAsService(service_manager);
|
||||
@@ -628,7 +629,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager, FileSystem::FileSyst
|
||||
std::make_shared<NS_SU>()->InstallAsService(service_manager);
|
||||
std::make_shared<NS_VM>()->InstallAsService(service_manager);
|
||||
|
||||
std::make_shared<PL_U>(fsc)->InstallAsService(service_manager);
|
||||
std::make_shared<PL_U>(system)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace Service::NS
|
||||
@@ -97,8 +97,7 @@ private:
|
||||
};
|
||||
|
||||
/// Registers all NS services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, FileSystem::FileSystemController& fsc);
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
|
||||
|
||||
} // namespace NS
|
||||
|
||||
} // namespace Service
|
||||
@@ -150,8 +150,9 @@ struct PL_U::Impl {
|
||||
std::vector<FontRegion> shared_font_regions;
|
||||
};
|
||||
|
||||
PL_U::PL_U(FileSystem::FileSystemController& fsc)
|
||||
: ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
|
||||
PL_U::PL_U(Core::System& system)
|
||||
: ServiceFramework("pl:u"), impl{std::make_unique<Impl>()}, system(system) {
|
||||
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &PL_U::RequestLoad, "RequestLoad"},
|
||||
{1, &PL_U::GetLoadState, "GetLoadState"},
|
||||
@@ -161,6 +162,9 @@ PL_U::PL_U(FileSystem::FileSystemController& fsc)
|
||||
{5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& fsc = system.GetFileSystemController();
|
||||
|
||||
// Attempt to load shared font data from disk
|
||||
const auto* nand = fsc.GetSystemNANDContents();
|
||||
std::size_t offset = 0;
|
||||
@@ -325,7 +329,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
|
||||
Kernel::MemoryState::Shared);
|
||||
|
||||
// Create shared font memory object
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto& kernel = system.Kernel();
|
||||
impl->shared_font_mem = Kernel::SharedMemory::Create(
|
||||
kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
|
||||
Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE,
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace NS {
|
||||
|
||||
class PL_U final : public ServiceFramework<PL_U> {
|
||||
public:
|
||||
PL_U(FileSystem::FileSystemController& fsc);
|
||||
explicit PL_U(Core::System& system);
|
||||
~PL_U() override;
|
||||
|
||||
private:
|
||||
@@ -30,6 +30,7 @@ private:
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace NS
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
@@ -38,8 +39,9 @@ public:
|
||||
* @param output A buffer where the output data will be written to.
|
||||
* @returns The result code of the ioctl.
|
||||
*/
|
||||
virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) = 0;
|
||||
virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
|
||||
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
|
||||
IoctlVersion version) = 0;
|
||||
|
||||
protected:
|
||||
Core::System& system;
|
||||
|
||||
@@ -17,8 +17,9 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_de
|
||||
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
|
||||
nvdisp_disp0 ::~nvdisp_disp0() = default;
|
||||
|
||||
u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
|
||||
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
|
||||
IoctlVersion version) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -20,8 +20,9 @@ public:
|
||||
explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
||||
~nvdisp_disp0() override;
|
||||
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
|
||||
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
|
||||
IoctlVersion version) override;
|
||||
|
||||
/// Performs a screen flip, drawing the buffer pointed to by the handle.
|
||||
void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
|
||||
|
||||
@@ -26,8 +26,9 @@ nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_
|
||||
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
|
||||
nvhost_as_gpu::~nvhost_as_gpu() = default;
|
||||
|
||||
u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
|
||||
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
|
||||
IoctlVersion version) {
|
||||
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
|
||||
command.raw, input.size(), output.size());
|
||||
|
||||
|
||||
@@ -20,8 +20,9 @@ public:
|
||||
explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
||||
~nvhost_as_gpu() override;
|
||||
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
|
||||
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
|
||||
IoctlVersion version) override;
|
||||
|
||||
private:
|
||||
enum class IoctlCommand : u32_le {
|
||||
|
||||
Loaded 100 of 183 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user