summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt50
-rw-r--r--src/core/announce_multiplayer_session.cpp164
-rw-r--r--src/core/announce_multiplayer_session.h98
-rw-r--r--src/core/arm/arm_interface.cpp71
-rw-r--r--src/core/arm/arm_interface.h30
-rw-r--r--src/core/arm/cpu_interrupt_handler.cpp24
-rw-r--r--src/core/arm/cpu_interrupt_handler.h39
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp186
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h13
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp154
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h14
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.cpp36
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.h7
-rw-r--r--src/core/core.cpp87
-rw-r--r--src/core/core.h34
-rw-r--r--src/core/core_timing.cpp82
-rw-r--r--src/core/core_timing.h23
-rw-r--r--src/core/cpu_manager.cpp161
-rw-r--r--src/core/cpu_manager.h27
-rw-r--r--src/core/debugger/debugger.cpp28
-rw-r--r--src/core/debugger/debugger.h8
-rw-r--r--src/core/debugger/debugger_interface.h8
-rw-r--r--src/core/debugger/gdbstub.cpp140
-rw-r--r--src/core/debugger/gdbstub.h3
-rw-r--r--src/core/debugger/gdbstub_arch.cpp8
-rw-r--r--src/core/file_sys/errors.h23
-rw-r--r--src/core/file_sys/ips_layer.cpp7
-rw-r--r--src/core/file_sys/patch_manager.cpp14
-rw-r--r--src/core/frontend/applets/error.cpp6
-rw-r--r--src/core/frontend/applets/error.h12
-rw-r--r--src/core/frontend/applets/software_keyboard.h2
-rw-r--r--src/core/frontend/emu_window.cpp5
-rw-r--r--src/core/frontend/emu_window.h5
-rw-r--r--src/core/hardware_interrupt_manager.cpp5
-rw-r--r--src/core/hardware_properties.h3
-rw-r--r--src/core/hid/emulated_controller.cpp89
-rw-r--r--src/core/hid/emulated_controller.h47
-rw-r--r--src/core/hid/hid_types.h13
-rw-r--r--src/core/hid/input_converter.cpp14
-rw-r--r--src/core/hid/input_converter.h8
-rw-r--r--src/core/hid/irs_types.h301
-rw-r--r--src/core/hle/ipc.h5
-rw-r--r--src/core/hle/ipc_helpers.h15
-rw-r--r--src/core/hle/kernel/global_scheduler_context.cpp7
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp44
-rw-r--r--src/core/hle/kernel/hle_ipc.h21
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp17
-rw-r--r--src/core/hle/kernel/k_address_arbiter.h19
-rw-r--r--src/core/hle/kernel/k_client_port.cpp9
-rw-r--r--src/core/hle/kernel/k_client_port.h9
-rw-r--r--src/core/hle/kernel/k_client_session.cpp4
-rw-r--r--src/core/hle/kernel/k_client_session.h6
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp12
-rw-r--r--src/core/hle/kernel/k_code_memory.h20
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp18
-rw-r--r--src/core/hle/kernel/k_condition_variable.h6
-rw-r--r--src/core/hle/kernel/k_handle_table.cpp6
-rw-r--r--src/core/hle/kernel/k_handle_table.h8
-rw-r--r--src/core/hle/kernel/k_interrupt_manager.cpp12
-rw-r--r--src/core/hle/kernel/k_light_condition_variable.cpp3
-rw-r--r--src/core/hle/kernel/k_light_lock.cpp3
-rw-r--r--src/core/hle/kernel/k_memory_manager.cpp16
-rw-r--r--src/core/hle/kernel/k_memory_manager.h16
-rw-r--r--src/core/hle/kernel/k_page_group.h (renamed from src/core/hle/kernel/k_page_linked_list.h)10
-rw-r--r--src/core/hle/kernel/k_page_table.cpp178
-rw-r--r--src/core/hle/kernel/k_page_table.h173
-rw-r--r--src/core/hle/kernel/k_port.cpp2
-rw-r--r--src/core/hle/kernel/k_port.h2
-rw-r--r--src/core/hle/kernel/k_process.cpp113
-rw-r--r--src/core/hle/kernel/k_process.h66
-rw-r--r--src/core/hle/kernel/k_readable_event.cpp6
-rw-r--r--src/core/hle/kernel/k_readable_event.h6
-rw-r--r--src/core/hle/kernel/k_resource_limit.cpp2
-rw-r--r--src/core/hle/kernel/k_resource_limit.h4
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp739
-rw-r--r--src/core/hle/kernel/k_scheduler.h227
-rw-r--r--src/core/hle/kernel/k_scheduler_lock.h2
-rw-r--r--src/core/hle/kernel/k_server_session.cpp12
-rw-r--r--src/core/hle/kernel/k_server_session.h12
-rw-r--r--src/core/hle/kernel/k_shared_memory.cpp21
-rw-r--r--src/core/hle/kernel/k_shared_memory.h23
-rw-r--r--src/core/hle/kernel/k_synchronization_object.cpp13
-rw-r--r--src/core/hle/kernel/k_synchronization_object.h8
-rw-r--r--src/core/hle/kernel/k_thread.cpp135
-rw-r--r--src/core/hle/kernel/k_thread.h89
-rw-r--r--src/core/hle/kernel/k_thread_local_page.cpp4
-rw-r--r--src/core/hle/kernel/k_thread_local_page.h4
-rw-r--r--src/core/hle/kernel/k_thread_queue.cpp9
-rw-r--r--src/core/hle/kernel/k_thread_queue.h9
-rw-r--r--src/core/hle/kernel/k_transfer_memory.cpp4
-rw-r--r--src/core/hle/kernel/k_transfer_memory.h4
-rw-r--r--src/core/hle/kernel/k_writable_event.cpp4
-rw-r--r--src/core/hle/kernel/k_writable_event.h4
-rw-r--r--src/core/hle/kernel/kernel.cpp132
-rw-r--r--src/core/hle/kernel/kernel.h14
-rw-r--r--src/core/hle/kernel/physical_core.cpp30
-rw-r--r--src/core/hle/kernel/physical_core.h17
-rw-r--r--src/core/hle/kernel/process_capability.cpp43
-rw-r--r--src/core/hle/kernel/process_capability.h38
-rw-r--r--src/core/hle/kernel/svc.cpp338
-rw-r--r--src/core/hle/kernel/svc_results.h58
-rw-r--r--src/core/hle/kernel/svc_wrap.h124
-rw-r--r--src/core/hle/kernel/time_manager.cpp20
-rw-r--r--src/core/hle/result.h49
-rw-r--r--src/core/hle/service/acc/acc.cpp34
-rw-r--r--src/core/hle/service/acc/acc.h2
-rw-r--r--src/core/hle/service/acc/async_context.h2
-rw-r--r--src/core/hle/service/acc/errors.h4
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp23
-rw-r--r--src/core/hle/service/acc/profile_manager.h21
-rw-r--r--src/core/hle/service/am/am.cpp37
-rw-r--r--src/core/hle/service/am/am.h15
-rw-r--r--src/core/hle/service/am/applets/applet_controller.cpp6
-rw-r--r--src/core/hle/service/am/applets/applet_controller.h4
-rw-r--r--src/core/hle/service/am/applets/applet_error.cpp18
-rw-r--r--src/core/hle/service/am/applets/applet_error.h4
-rw-r--r--src/core/hle/service/am/applets/applet_general_backend.cpp10
-rw-r--r--src/core/hle/service/am/applets/applet_general_backend.h6
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit.h2
-rw-r--r--src/core/hle/service/am/applets/applet_profile_select.cpp4
-rw-r--r--src/core/hle/service/am/applets/applet_profile_select.h4
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.cpp8
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.h4
-rw-r--r--src/core/hle/service/am/applets/applet_web_browser.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_web_browser.h4
-rw-r--r--src/core/hle/service/am/applets/applets.h4
-rw-r--r--src/core/hle/service/audio/audin_u.cpp384
-rw-r--r--src/core/hle/service/audio/audin_u.h47
-rw-r--r--src/core/hle/service/audio/audout_u.cpp327
-rw-r--r--src/core/hle/service/audio/audout_u.h21
-rw-r--r--src/core/hle/service/audio/audren_u.cpp707
-rw-r--r--src/core/hle/service/audio/audren_u.h22
-rw-r--r--src/core/hle/service/audio/errors.h15
-rw-r--r--src/core/hle/service/audio/hwopus.cpp2
-rw-r--r--src/core/hle/service/bcat/backend/backend.cpp2
-rw-r--r--src/core/hle/service/bcat/backend/backend.h4
-rw-r--r--src/core/hle/service/bcat/bcat_module.cpp14
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp5
-rw-r--r--src/core/hle/service/btm/btm.cpp8
-rw-r--r--src/core/hle/service/es/es.cpp4
-rw-r--r--src/core/hle/service/fatal/fatal.cpp11
-rw-r--r--src/core/hle/service/fatal/fatal_p.cpp8
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp29
-rw-r--r--src/core/hle/service/filesystem/filesystem.h26
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp3
-rw-r--r--src/core/hle/service/friend/errors.h2
-rw-r--r--src/core/hle/service/glue/arp.cpp2
-rw-r--r--src/core/hle/service/glue/errors.h8
-rw-r--r--src/core/hle/service/glue/glue_manager.cpp6
-rw-r--r--src/core/hle/service/glue/glue_manager.h4
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp175
-rw-r--r--src/core/hle/service/hid/controllers/npad.h72
-rw-r--r--src/core/hle/service/hid/errors.h24
-rw-r--r--src/core/hle/service/hid/hid.cpp126
-rw-r--r--src/core/hle/service/hid/hidbus.cpp16
-rw-r--r--src/core/hle/service/hid/hidbus.h2
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.h2
-rw-r--r--src/core/hle/service/hid/irs.cpp324
-rw-r--r--src/core/hle/service/hid/irs.h295
-rw-r--r--src/core/hle/service/hid/irs_ring_lifo.h47
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.cpp265
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.h110
-rw-r--r--src/core/hle/service/hid/irsensor/image_transfer_processor.cpp150
-rw-r--r--src/core/hle/service/hid/irsensor/image_transfer_processor.h73
-rw-r--r--src/core/hle/service/hid/irsensor/ir_led_processor.cpp27
-rw-r--r--src/core/hle/service/hid/irsensor/ir_led_processor.h47
-rw-r--r--src/core/hle/service/hid/irsensor/moment_processor.cpp34
-rw-r--r--src/core/hle/service/hid/irsensor/moment_processor.h61
-rw-r--r--src/core/hle/service/hid/irsensor/pointing_processor.cpp26
-rw-r--r--src/core/hle/service/hid/irsensor/pointing_processor.h61
-rw-r--r--src/core/hle/service/hid/irsensor/processor_base.cpp67
-rw-r--r--src/core/hle/service/hid/irsensor/processor_base.h33
-rw-r--r--src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp29
-rw-r--r--src/core/hle/service/hid/irsensor/tera_plugin_processor.h53
-rw-r--r--src/core/hle/service/ldn/errors.h12
-rw-r--r--src/core/hle/service/ldn/ldn.cpp436
-rw-r--r--src/core/hle/service/ldn/ldn.h6
-rw-r--r--src/core/hle/service/ldn/ldn_results.h27
-rw-r--r--src/core/hle/service/ldn/ldn_types.h284
-rw-r--r--src/core/hle/service/ldr/ldr.cpp41
-rw-r--r--src/core/hle/service/mii/mii.cpp2
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp4
-rw-r--r--src/core/hle/service/mii/mii_manager.h2
-rw-r--r--src/core/hle/service/nfp/nfp.cpp32
-rw-r--r--src/core/hle/service/nfp/nfp.h28
-rw-r--r--src/core/hle/service/nifm/nifm.cpp354
-rw-r--r--src/core/hle/service/nifm/nifm.h27
-rw-r--r--src/core/hle/service/ns/errors.h2
-rw-r--r--src/core/hle/service/nvflinger/binder.h1
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp31
-rw-r--r--src/core/hle/service/pctl/pctl_module.cpp8
-rw-r--r--src/core/hle/service/pcv/pcv.cpp93
-rw-r--r--src/core/hle/service/pcv/pcv.h91
-rw-r--r--src/core/hle/service/pm/pm.cpp12
-rw-r--r--src/core/hle/service/ptm/psm.cpp122
-rw-r--r--src/core/hle/service/ptm/psm.h31
-rw-r--r--src/core/hle/service/ptm/ptm.cpp18
-rw-r--r--src/core/hle/service/ptm/ptm.h18
-rw-r--r--src/core/hle/service/ptm/ts.cpp41
-rw-r--r--src/core/hle/service/ptm/ts.h25
-rw-r--r--src/core/hle/service/service.cpp15
-rw-r--r--src/core/hle/service/service.h4
-rw-r--r--src/core/hle/service/set/set.cpp2
-rw-r--r--src/core/hle/service/set/set_sys.cpp2
-rw-r--r--src/core/hle/service/sm/sm.cpp18
-rw-r--r--src/core/hle/service/sm/sm.h6
-rw-r--r--src/core/hle/service/sm/sm_controller.cpp2
-rw-r--r--src/core/hle/service/sockets/bsd.cpp64
-rw-r--r--src/core/hle/service/sockets/bsd.h15
-rw-r--r--src/core/hle/service/sockets/sockets.h6
-rw-r--r--src/core/hle/service/sockets/sockets_translate.cpp4
-rw-r--r--src/core/hle/service/sockets/sockets_translate.h2
-rw-r--r--src/core/hle/service/spl/spl_results.h32
-rw-r--r--src/core/hle/service/time/clock_types.h8
-rw-r--r--src/core/hle/service/time/errors.h20
-rw-r--r--src/core/hle/service/time/local_system_clock_context_writer.h2
-rw-r--r--src/core/hle/service/time/network_system_clock_context_writer.h2
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.cpp22
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.h10
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.cpp6
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.h4
-rw-r--r--src/core/hle/service/time/system_clock_core.cpp14
-rw-r--r--src/core/hle/service/time/system_clock_core.h14
-rw-r--r--src/core/hle/service/time/time.cpp25
-rw-r--r--src/core/hle/service/time/time.h2
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp10
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.h6
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp46
-rw-r--r--src/core/hle/service/time/time_zone_manager.h20
-rw-r--r--src/core/hle/service/time/time_zone_service.cpp15
-rw-r--r--src/core/hle/service/vi/vi.cpp8
-rw-r--r--src/core/internal_network/network.cpp (renamed from src/core/network/network.cpp)68
-rw-r--r--src/core/internal_network/network.h (renamed from src/core/network/network.h)43
-rw-r--r--src/core/internal_network/network_interface.cpp (renamed from src/core/network/network_interface.cpp)2
-rw-r--r--src/core/internal_network/network_interface.h (renamed from src/core/network/network_interface.h)0
-rw-r--r--src/core/internal_network/socket_proxy.cpp284
-rw-r--r--src/core/internal_network/socket_proxy.h97
-rw-r--r--src/core/internal_network/sockets.h163
-rw-r--r--src/core/loader/elf.cpp263
-rw-r--r--src/core/loader/elf.h36
-rw-r--r--src/core/loader/kip.cpp2
-rw-r--r--src/core/loader/loader.cpp11
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/nro.cpp2
-rw-r--r--src/core/loader/nso.cpp2
-rw-r--r--src/core/memory.cpp155
-rw-r--r--src/core/memory.h35
-rw-r--r--src/core/memory/cheat_engine.cpp8
-rw-r--r--src/core/network/sockets.h94
-rw-r--r--src/core/perf_stats.cpp5
-rw-r--r--src/core/perf_stats.h5
-rw-r--r--src/core/reporter.cpp8
-rw-r--r--src/core/reporter.h6
-rw-r--r--src/core/telemetry_session.cpp5
-rw-r--r--src/core/telemetry_session.h5
-rw-r--r--src/core/tools/freezer.cpp4
257 files changed, 8226 insertions, 4305 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 670410e75..8db9a3c65 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -1,8 +1,11 @@
+# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
+# SPDX-License-Identifier: GPL-2.0-or-later
+
add_library(core STATIC
+ announce_multiplayer_session.cpp
+ announce_multiplayer_session.h
arm/arm_interface.h
arm/arm_interface.cpp
- arm/cpu_interrupt_handler.cpp
- arm/cpu_interrupt_handler.h
arm/dynarmic/arm_dynarmic_32.cpp
arm/dynarmic/arm_dynarmic_32.h
arm/dynarmic/arm_dynarmic_64.cpp
@@ -158,6 +161,7 @@ add_library(core STATIC
hid/input_converter.h
hid/input_interpreter.cpp
hid/input_interpreter.h
+ hid/irs_types.h
hid/motion_input.cpp
hid/motion_input.h
hle/api_version.h
@@ -222,7 +226,7 @@ add_library(core STATIC
hle/kernel/k_page_buffer.h
hle/kernel/k_page_heap.cpp
hle/kernel/k_page_heap.h
- hle/kernel/k_page_linked_list.h
+ hle/kernel/k_page_group.h
hle/kernel/k_page_table.cpp
hle/kernel/k_page_table.h
hle/kernel/k_port.cpp
@@ -445,6 +449,7 @@ add_library(core STATIC
hle/service/hid/hidbus.h
hle/service/hid/irs.cpp
hle/service/hid/irs.h
+ hle/service/hid/irs_ring_lifo.h
hle/service/hid/ring_lifo.h
hle/service/hid/xcd.cpp
hle/service/hid/xcd.h
@@ -477,15 +482,30 @@ add_library(core STATIC
hle/service/hid/hidbus/starlink.h
hle/service/hid/hidbus/stubbed.cpp
hle/service/hid/hidbus/stubbed.h
+ hle/service/hid/irsensor/clustering_processor.cpp
+ hle/service/hid/irsensor/clustering_processor.h
+ hle/service/hid/irsensor/image_transfer_processor.cpp
+ hle/service/hid/irsensor/image_transfer_processor.h
+ hle/service/hid/irsensor/ir_led_processor.cpp
+ hle/service/hid/irsensor/ir_led_processor.h
+ hle/service/hid/irsensor/moment_processor.cpp
+ hle/service/hid/irsensor/moment_processor.h
+ hle/service/hid/irsensor/pointing_processor.cpp
+ hle/service/hid/irsensor/pointing_processor.h
+ hle/service/hid/irsensor/processor_base.cpp
+ hle/service/hid/irsensor/processor_base.h
+ hle/service/hid/irsensor/tera_plugin_processor.cpp
+ hle/service/hid/irsensor/tera_plugin_processor.h
hle/service/jit/jit_context.cpp
hle/service/jit/jit_context.h
hle/service/jit/jit.cpp
hle/service/jit/jit.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
- hle/service/ldn/errors.h
+ hle/service/ldn/ldn_results.h
hle/service/ldn/ldn.cpp
hle/service/ldn/ldn.h
+ hle/service/ldn/ldn_types.h
hle/service/ldr/ldr.cpp
hle/service/ldr/ldr.h
hle/service/lm/lm.cpp
@@ -605,6 +625,10 @@ add_library(core STATIC
hle/service/psc/psc.h
hle/service/ptm/psm.cpp
hle/service/ptm/psm.h
+ hle/service/ptm/ptm.cpp
+ hle/service/ptm/ptm.h
+ hle/service/ptm/ts.cpp
+ hle/service/ptm/ts.h
hle/service/kernel_helpers.cpp
hle/service/kernel_helpers.h
hle/service/service.cpp
@@ -695,10 +719,15 @@ add_library(core STATIC
hle/service/vi/vi_u.h
hle/service/wlan/wlan.cpp
hle/service/wlan/wlan.h
+ internal_network/network.cpp
+ internal_network/network.h
+ internal_network/network_interface.cpp
+ internal_network/network_interface.h
+ internal_network/sockets.h
+ internal_network/socket_proxy.cpp
+ internal_network/socket_proxy.h
loader/deconstructed_rom_directory.cpp
loader/deconstructed_rom_directory.h
- loader/elf.cpp
- loader/elf.h
loader/kip.cpp
loader/kip.h
loader/loader.cpp
@@ -722,11 +751,6 @@ add_library(core STATIC
memory/dmnt_cheat_vm.h
memory.cpp
memory.h
- network/network.cpp
- network/network.h
- network/network_interface.cpp
- network/network_interface.h
- network/sockets.h
perf_stats.cpp
perf_stats.h
reporter.cpp
@@ -761,8 +785,8 @@ endif()
create_target_directory_groups(core)
-target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
-target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus)
+target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)
+target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus)
if (MINGW)
target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
endif()
diff --git a/src/core/announce_multiplayer_session.cpp b/src/core/announce_multiplayer_session.cpp
new file mode 100644
index 000000000..6737ce85a
--- /dev/null
+++ b/src/core/announce_multiplayer_session.cpp
@@ -0,0 +1,164 @@
+// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <chrono>
+#include <future>
+#include <vector>
+#include "announce_multiplayer_session.h"
+#include "common/announce_multiplayer_room.h"
+#include "common/assert.h"
+#include "common/settings.h"
+#include "network/network.h"
+
+#ifdef ENABLE_WEB_SERVICE
+#include "web_service/announce_room_json.h"
+#endif
+
+namespace Core {
+
+// Time between room is announced to web_service
+static constexpr std::chrono::seconds announce_time_interval(15);
+
+AnnounceMultiplayerSession::AnnounceMultiplayerSession(Network::RoomNetwork& room_network_)
+ : room_network{room_network_} {
+#ifdef ENABLE_WEB_SERVICE
+ backend = std::make_unique<WebService::RoomJson>(Settings::values.web_api_url.GetValue(),
+ Settings::values.yuzu_username.GetValue(),
+ Settings::values.yuzu_token.GetValue());
+#else
+ backend = std::make_unique<AnnounceMultiplayerRoom::NullBackend>();
+#endif
+}
+
+WebService::WebResult AnnounceMultiplayerSession::Register() {
+ auto room = room_network.GetRoom().lock();
+ if (!room) {
+ return WebService::WebResult{WebService::WebResult::Code::LibError,
+ "Network is not initialized", ""};
+ }
+ if (room->GetState() != Network::Room::State::Open) {
+ return WebService::WebResult{WebService::WebResult::Code::LibError, "Room is not open", ""};
+ }
+ UpdateBackendData(room);
+ WebService::WebResult result = backend->Register();
+ if (result.result_code != WebService::WebResult::Code::Success) {
+ return result;
+ }
+ LOG_INFO(WebService, "Room has been registered");
+ room->SetVerifyUID(result.returned_data);
+ registered = true;
+ return WebService::WebResult{WebService::WebResult::Code::Success, "", ""};
+}
+
+void AnnounceMultiplayerSession::Start() {
+ if (announce_multiplayer_thread) {
+ Stop();
+ }
+ shutdown_event.Reset();
+ announce_multiplayer_thread =
+ std::make_unique<std::thread>(&AnnounceMultiplayerSession::AnnounceMultiplayerLoop, this);
+}
+
+void AnnounceMultiplayerSession::Stop() {
+ if (announce_multiplayer_thread) {
+ shutdown_event.Set();
+ announce_multiplayer_thread->join();
+ announce_multiplayer_thread.reset();
+ backend->Delete();
+ registered = false;
+ }
+}
+
+AnnounceMultiplayerSession::CallbackHandle AnnounceMultiplayerSession::BindErrorCallback(
+ std::function<void(const WebService::WebResult&)> function) {
+ std::lock_guard lock(callback_mutex);
+ auto handle = std::make_shared<std::function<void(const WebService::WebResult&)>>(function);
+ error_callbacks.insert(handle);
+ return handle;
+}
+
+void AnnounceMultiplayerSession::UnbindErrorCallback(CallbackHandle handle) {
+ std::lock_guard lock(callback_mutex);
+ error_callbacks.erase(handle);
+}
+
+AnnounceMultiplayerSession::~AnnounceMultiplayerSession() {
+ Stop();
+}
+
+void AnnounceMultiplayerSession::UpdateBackendData(std::shared_ptr<Network::Room> room) {
+ Network::RoomInformation room_information = room->GetRoomInformation();
+ std::vector<AnnounceMultiplayerRoom::Member> memberlist = room->GetRoomMemberList();
+ backend->SetRoomInformation(room_information.name, room_information.description,
+ room_information.port, room_information.member_slots,
+ Network::network_version, room->HasPassword(),
+ room_information.preferred_game);
+ backend->ClearPlayers();
+ for (const auto& member : memberlist) {
+ backend->AddPlayer(member);
+ }
+}
+
+void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() {
+ // Invokes all current bound error callbacks.
+ const auto ErrorCallback = [this](WebService::WebResult result) {
+ std::lock_guard lock(callback_mutex);
+ for (auto callback : error_callbacks) {
+ (*callback)(result);
+ }
+ };
+
+ if (!registered) {
+ WebService::WebResult result = Register();
+ if (result.result_code != WebService::WebResult::Code::Success) {
+ ErrorCallback(result);
+ return;
+ }
+ }
+
+ auto update_time = std::chrono::steady_clock::now();
+ std::future<WebService::WebResult> future;
+ while (!shutdown_event.WaitUntil(update_time)) {
+ update_time += announce_time_interval;
+ auto room = room_network.GetRoom().lock();
+ if (!room) {
+ break;
+ }
+ if (room->GetState() != Network::Room::State::Open) {
+ break;
+ }
+ UpdateBackendData(room);
+ WebService::WebResult result = backend->Update();
+ if (result.result_code != WebService::WebResult::Code::Success) {
+ ErrorCallback(result);
+ }
+ if (result.result_string == "404") {
+ registered = false;
+ // Needs to register the room again
+ WebService::WebResult register_result = Register();
+ if (register_result.result_code != WebService::WebResult::Code::Success) {
+ ErrorCallback(register_result);
+ }
+ }
+ }
+}
+
+AnnounceMultiplayerRoom::RoomList AnnounceMultiplayerSession::GetRoomList() {
+ return backend->GetRoomList();
+}
+
+bool AnnounceMultiplayerSession::IsRunning() const {
+ return announce_multiplayer_thread != nullptr;
+}
+
+void AnnounceMultiplayerSession::UpdateCredentials() {
+ ASSERT_MSG(!IsRunning(), "Credentials can only be updated when session is not running");
+
+#ifdef ENABLE_WEB_SERVICE
+ backend = std::make_unique<WebService::RoomJson>(Settings::values.web_api_url.GetValue(),
+ Settings::values.yuzu_username.GetValue(),
+ Settings::values.yuzu_token.GetValue());
+#endif
+}
+
+} // namespace Core
diff --git a/src/core/announce_multiplayer_session.h b/src/core/announce_multiplayer_session.h
new file mode 100644
index 000000000..db790f7d2
--- /dev/null
+++ b/src/core/announce_multiplayer_session.h
@@ -0,0 +1,98 @@
+// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <atomic>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <thread>
+#include "common/announce_multiplayer_room.h"
+#include "common/common_types.h"
+#include "common/thread.h"
+
+namespace Network {
+class Room;
+class RoomNetwork;
+} // namespace Network
+
+namespace Core {
+
+/**
+ * Instruments AnnounceMultiplayerRoom::Backend.
+ * Creates a thread that regularly updates the room information and submits them
+ * An async get of room information is also possible
+ */
+class AnnounceMultiplayerSession {
+public:
+ using CallbackHandle = std::shared_ptr<std::function<void(const WebService::WebResult&)>>;
+ AnnounceMultiplayerSession(Network::RoomNetwork& room_network_);
+ ~AnnounceMultiplayerSession();
+
+ /**
+ * Allows to bind a function that will get called if the announce encounters an error
+ * @param function The function that gets called
+ * @return A handle that can be used the unbind the function
+ */
+ CallbackHandle BindErrorCallback(std::function<void(const WebService::WebResult&)> function);
+
+ /**
+ * Unbind a function from the error callbacks
+ * @param handle The handle for the function that should get unbind
+ */
+ void UnbindErrorCallback(CallbackHandle handle);
+
+ /**
+ * Registers a room to web services
+ * @return The result of the registration attempt.
+ */
+ WebService::WebResult Register();
+
+ /**
+ * Starts the announce of a room to web services
+ */
+ void Start();
+
+ /**
+ * Stops the announce to web services
+ */
+ void Stop();
+
+ /**
+ * Returns a list of all room information the backend got
+ * @param func A function that gets executed when the async get finished, e.g. a signal
+ * @return a list of rooms received from the web service
+ */
+ AnnounceMultiplayerRoom::RoomList GetRoomList();
+
+ /**
+ * Whether the announce session is still running
+ */
+ bool IsRunning() const;
+
+ /**
+ * Recreates the backend, updating the credentials.
+ * This can only be used when the announce session is not running.
+ */
+ void UpdateCredentials();
+
+private:
+ void UpdateBackendData(std::shared_ptr<Network::Room> room);
+ void AnnounceMultiplayerLoop();
+
+ Common::Event shutdown_event;
+ std::mutex callback_mutex;
+ std::set<CallbackHandle> error_callbacks;
+ std::unique_ptr<std::thread> announce_multiplayer_thread;
+
+ /// Backend interface that logs fields
+ std::unique_ptr<AnnounceMultiplayerRoom::Backend> backend;
+
+ std::atomic_bool registered = false; ///< Whether the room has been registered
+
+ Network::RoomNetwork& room_network;
+};
+
+} // namespace Core
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 9a285dfc6..953d96439 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -1,6 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef _MSC_VER
+#include <cxxabi.h>
+#endif
+
#include <map>
#include <optional>
#include "common/bit_field.h"
@@ -68,8 +72,19 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<Backt
if (symbol_set != symbols.end()) {
const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
if (symbol.has_value()) {
+#ifdef _MSC_VER
// TODO(DarkLordZach): Add demangling of symbol names.
entry.name = *symbol;
+#else
+ int status{-1};
+ char* demangled{abi::__cxa_demangle(symbol->c_str(), nullptr, nullptr, &status)};
+ if (status == 0 && demangled != nullptr) {
+ entry.name = demangled;
+ std::free(demangled);
+ } else {
+ entry.name = *symbol;
+ }
+#endif
}
}
}
@@ -95,7 +110,7 @@ void ARM_Interface::Run() {
using Kernel::SuspendType;
while (true) {
- Kernel::KThread* current_thread{system.Kernel().CurrentScheduler()->GetCurrentThread()};
+ Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())};
Dynarmic::HaltReason hr{};
// Notify the debugger and go to sleep if a step was performed
@@ -119,16 +134,30 @@ void ARM_Interface::Run() {
}
system.ExitDynarmicProfile();
- // Notify the debugger and go to sleep if a breakpoint was hit.
- if (Has(hr, breakpoint)) {
- system.GetDebugger().NotifyThreadStopped(current_thread);
+ // Notify the debugger and go to sleep if a breakpoint was hit,
+ // or if the thread is unable to continue for any reason.
+ if (Has(hr, breakpoint) || Has(hr, no_execute)) {
+ RewindBreakpointInstruction();
+ if (system.DebuggerEnabled()) {
+ system.GetDebugger().NotifyThreadStopped(current_thread);
+ }
current_thread->RequestSuspend(Kernel::SuspendType::Debug);
break;
}
- // Handle syscalls and scheduling (this may change the current thread)
+ // Notify the debugger and go to sleep if a watchpoint was hit.
+ if (Has(hr, watchpoint)) {
+ if (system.DebuggerEnabled()) {
+ system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint());
+ }
+ current_thread->RequestSuspend(SuspendType::Debug);
+ break;
+ }
+
+ // Handle syscalls and scheduling (this may change the current thread/core)
if (Has(hr, svc_call)) {
Kernel::Svc::Call(system, GetSvcNumber());
+ break;
}
if (Has(hr, break_loop) || !uses_wall_clock) {
break;
@@ -136,4 +165,36 @@ void ARM_Interface::Run() {
}
}
+void ARM_Interface::LoadWatchpointArray(const WatchpointArray& wp) {
+ watchpoints = &wp;
+}
+
+const Kernel::DebugWatchpoint* ARM_Interface::MatchingWatchpoint(
+ VAddr addr, u64 size, Kernel::DebugWatchpointType access_type) const {
+ if (!watchpoints) {
+ return nullptr;
+ }
+
+ const VAddr start_address{addr};
+ const VAddr end_address{addr + size};
+
+ for (size_t i = 0; i < Core::Hardware::NUM_WATCHPOINTS; i++) {
+ const auto& watch{(*watchpoints)[i]};
+
+ if (end_address <= watch.start_address) {
+ continue;
+ }
+ if (start_address >= watch.end_address) {
+ continue;
+ }
+ if ((access_type & watch.type) == Kernel::DebugWatchpointType::None) {
+ continue;
+ }
+
+ return &watch;
+ }
+
+ return nullptr;
+}
+
} // namespace Core
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 66f6107e9..7d62d030e 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -1,10 +1,10 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
+#include <span>
#include <vector>
#include <dynarmic/interface/halt_reason.h>
@@ -19,13 +19,15 @@ struct PageTable;
namespace Kernel {
enum class VMAPermission : u8;
-}
+enum class DebugWatchpointType : u8;
+struct DebugWatchpoint;
+} // namespace Kernel
namespace Core {
class System;
class CPUInterruptHandler;
-using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>;
+using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>;
/// Generic ARMv8 CPU interface
class ARM_Interface {
@@ -33,10 +35,8 @@ public:
YUZU_NON_COPYABLE(ARM_Interface);
YUZU_NON_MOVEABLE(ARM_Interface);
- explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_,
- bool uses_wall_clock_)
- : system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{
- uses_wall_clock_} {}
+ explicit ARM_Interface(System& system_, bool uses_wall_clock_)
+ : system{system_}, uses_wall_clock{uses_wall_clock_} {}
virtual ~ARM_Interface() = default;
struct ThreadContext32 {
@@ -170,6 +170,7 @@ public:
virtual void SaveContext(ThreadContext64& ctx) = 0;
virtual void LoadContext(const ThreadContext32& ctx) = 0;
virtual void LoadContext(const ThreadContext64& ctx) = 0;
+ void LoadWatchpointArray(const WatchpointArray& wp);
/// Clears the exclusive monitor's state.
virtual void ClearExclusiveState() = 0;
@@ -177,6 +178,9 @@ public:
/// Signal an interrupt and ask the core to halt as soon as possible.
virtual void SignalInterrupt() = 0;
+ /// Clear a previous interrupt.
+ virtual void ClearInterrupt() = 0;
+
struct BacktraceEntry {
std::string module;
u64 address;
@@ -198,18 +202,24 @@ public:
static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
+ static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::MemoryAbort;
+ static constexpr Dynarmic::HaltReason no_execute = Dynarmic::HaltReason::UserDefined6;
protected:
/// System context that this ARM interface is running under.
System& system;
- CPUInterrupts& interrupt_handlers;
+ const WatchpointArray* watchpoints;
bool uses_wall_clock;
static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
+ const Kernel::DebugWatchpoint* MatchingWatchpoint(
+ VAddr addr, u64 size, Kernel::DebugWatchpointType access_type) const;
virtual Dynarmic::HaltReason RunJit() = 0;
virtual Dynarmic::HaltReason StepJit() = 0;
virtual u32 GetSvcNumber() const = 0;
+ virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0;
+ virtual void RewindBreakpointInstruction() = 0;
};
} // namespace Core
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp
deleted file mode 100644
index 77b6194d7..000000000
--- a/src/core/arm/cpu_interrupt_handler.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "common/thread.h"
-#include "core/arm/cpu_interrupt_handler.h"
-
-namespace Core {
-
-CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {}
-
-CPUInterruptHandler::~CPUInterruptHandler() = default;
-
-void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) {
- if (is_interrupted_) {
- interrupt_event->Set();
- }
- is_interrupted = is_interrupted_;
-}
-
-void CPUInterruptHandler::AwaitInterrupt() {
- interrupt_event->Wait();
-}
-
-} // namespace Core
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h
deleted file mode 100644
index 286e12e53..000000000
--- a/src/core/arm/cpu_interrupt_handler.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <atomic>
-#include <memory>
-
-namespace Common {
-class Event;
-}
-
-namespace Core {
-
-class CPUInterruptHandler {
-public:
- CPUInterruptHandler();
- ~CPUInterruptHandler();
-
- CPUInterruptHandler(const CPUInterruptHandler&) = delete;
- CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete;
-
- CPUInterruptHandler(CPUInterruptHandler&&) = delete;
- CPUInterruptHandler& operator=(CPUInterruptHandler&&) = delete;
-
- bool IsInterrupted() const {
- return is_interrupted;
- }
-
- void SetInterrupt(bool is_interrupted);
-
- void AwaitInterrupt();
-
-private:
- std::unique_ptr<Common::Event> interrupt_event;
- std::atomic_bool is_interrupted{false};
-};
-
-} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 7c82d0b96..d1e70f19d 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -11,7 +11,6 @@
#include "common/logging/log.h"
#include "common/page_table.h"
#include "common/settings.h"
-#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_cp15.h"
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
@@ -29,64 +28,94 @@ using namespace Common::Literals;
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public:
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
- : parent{parent_}, memory(parent.system.Memory()) {}
+ : parent{parent_},
+ memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {}
u8 MemoryRead8(u32 vaddr) override {
+ CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
return memory.Read8(vaddr);
}
u16 MemoryRead16(u32 vaddr) override {
+ CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
return memory.Read16(vaddr);
}
u32 MemoryRead32(u32 vaddr) override {
+ CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return memory.Read32(vaddr);
}
u64 MemoryRead64(u32 vaddr) override {
+ CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return memory.Read64(vaddr);
}
+ std::optional<u32> MemoryReadCode(u32 vaddr) override {
+ if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) {
+ return std::nullopt;
+ }
+ return memory.Read32(vaddr);
+ }
void MemoryWrite8(u32 vaddr, u8 value) override {
- memory.Write8(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
+ memory.Write8(vaddr, value);
+ }
}
void MemoryWrite16(u32 vaddr, u16 value) override {
- memory.Write16(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
+ memory.Write16(vaddr, value);
+ }
}
void MemoryWrite32(u32 vaddr, u32 value) override {
- memory.Write32(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
+ memory.Write32(vaddr, value);
+ }
}
void MemoryWrite64(u32 vaddr, u64 value) override {
- memory.Write64(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
+ memory.Write64(vaddr, value);
+ }
}
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override {
- return memory.WriteExclusive8(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive8(vaddr, value, expected);
}
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override {
- return memory.WriteExclusive16(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive16(vaddr, value, expected);
}
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override {
- return memory.WriteExclusive32(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive32(vaddr, value, expected);
}
bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override {
- return memory.WriteExclusive64(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive64(vaddr, value, expected);
}
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
parent.LogBacktrace();
- UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc,
- MemoryReadCode(pc));
+ LOG_ERROR(Core_ARM,
+ "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
+ num_instructions, memory.Read32(pc));
}
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
- if (parent.system.DebuggerEnabled()) {
- parent.jit.load()->Regs()[15] = pc;
- parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
+ switch (exception) {
+ case Dynarmic::A32::Exception::NoExecuteFault:
+ LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#08x}", pc);
+ ReturnException(pc, ARM_Interface::no_execute);
return;
- }
+ default:
+ if (debugger_enabled) {
+ ReturnException(pc, ARM_Interface::breakpoint);
+ return;
+ }
- parent.LogBacktrace();
- LOG_CRITICAL(Core_ARM,
- "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
- exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
+ parent.LogBacktrace();
+ LOG_CRITICAL(Core_ARM,
+ "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
+ exception, pc, memory.Read32(pc), parent.IsInThumbMode());
+ }
}
void CallSVC(u32 swi) override {
@@ -95,7 +124,9 @@ public:
}
void AddTicks(u64 ticks) override {
- ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
+ if (parent.uses_wall_clock) {
+ return;
+ }
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
// rough approximation of the amount of executed ticks in the system, it may be thrown off
@@ -112,15 +143,46 @@ public:
}
u64 GetTicksRemaining() override {
- ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
+ if (parent.uses_wall_clock) {
+ if (!IsInterrupted()) {
+ return minimum_run_cycles;
+ }
+ return 0U;
+ }
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
}
+ bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
+ if (!debugger_enabled) {
+ return true;
+ }
+
+ const auto match{parent.MatchingWatchpoint(addr, size, type)};
+ if (match) {
+ parent.halted_watchpoint = match;
+ parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
+ return false;
+ }
+
+ return true;
+ }
+
+ void ReturnException(u32 pc, Dynarmic::HaltReason hr) {
+ parent.SaveContext(parent.breakpoint_context);
+ parent.breakpoint_context.cpu_registers[15] = pc;
+ parent.jit.load()->HaltExecution(hr);
+ }
+
+ bool IsInterrupted() {
+ return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted();
+ }
+
ARM_Dynarmic_32& parent;
Core::Memory::Memory& memory;
std::size_t num_interpreted_instructions{};
- static constexpr u64 minimum_run_cycles = 1000U;
+ bool debugger_enabled{};
+ static constexpr u64 minimum_run_cycles = 10000U;
};
std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* page_table) const {
@@ -128,19 +190,21 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
config.callbacks = cb.get();
config.coprocessors[15] = cp15;
config.define_unpredictable_behaviour = true;
- static constexpr std::size_t PAGE_BITS = 12;
- static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS);
+ static constexpr std::size_t YUZU_PAGEBITS = 12;
+ static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - YUZU_PAGEBITS);
if (page_table) {
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>(
page_table->pointers.data());
+ config.absolute_offset_page_table = true;
+ config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
+ config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
+ config.only_detect_misalignment_via_page_table_on_page_boundary = true;
+
config.fastmem_pointer = page_table->fastmem_arena;
+
+ config.fastmem_exclusive_access = config.fastmem_pointer != nullptr;
+ config.recompile_on_exclusive_fastmem_failure = true;
}
- config.absolute_offset_page_table = true;
- config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
- config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
- config.only_detect_misalignment_via_page_table_on_page_boundary = true;
- config.fastmem_exclusive_access = true;
- config.recompile_on_exclusive_fastmem_failure = true;
// Multi-process state
config.processor_id = core_index;
@@ -148,17 +212,20 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
// Timing
config.wall_clock_cntpct = uses_wall_clock;
- config.enable_cycle_counting = !uses_wall_clock;
+ config.enable_cycle_counting = true;
// Code cache size
config.code_cache_size = 512_MiB;
- config.far_code_offset = 400_MiB;
+
+ // Allow memory fault handling to work
+ if (system.DebuggerEnabled()) {
+ config.check_halt_on_memory_access = true;
+ }
// null_jit
if (!page_table) {
// Don't waste too much memory on null_jit
config.code_cache_size = 8_MiB;
- config.far_code_offset = 4_MiB;
}
// Safe optimizations
@@ -189,6 +256,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
}
if (!Settings::values.cpuopt_fastmem) {
config.fastmem_pointer = nullptr;
+ config.fastmem_exclusive_access = false;
}
if (!Settings::values.cpuopt_fastmem_exclusives) {
config.fastmem_exclusive_access = false;
@@ -248,11 +316,17 @@ u32 ARM_Dynarmic_32::GetSvcNumber() const {
return svc_swi;
}
-ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_,
- bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
- std::size_t core_index_)
- : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_},
- cb(std::make_unique<DynarmicCallbacks32>(*this)),
+const Kernel::DebugWatchpoint* ARM_Dynarmic_32::HaltedWatchpoint() const {
+ return halted_watchpoint;
+}
+
+void ARM_Dynarmic_32::RewindBreakpointInstruction() {
+ LoadContext(breakpoint_context);
+}
+
+ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, bool uses_wall_clock_,
+ ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_)
+ : ARM_Interface{system_, uses_wall_clock_}, cb(std::make_unique<DynarmicCallbacks32>(*this)),
cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {}
@@ -331,6 +405,10 @@ void ARM_Dynarmic_32::SignalInterrupt() {
jit.load()->HaltExecution(break_loop);
}
+void ARM_Dynarmic_32::ClearInterrupt() {
+ jit.load()->ClearHalt(break_loop);
+}
+
void ARM_Dynarmic_32::ClearInstructionCache() {
jit.load()->ClearCache();
}
@@ -362,18 +440,38 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
}
std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system,
- u64 sp, u64 lr) {
- // No way to get accurate stack traces in A32 yet
- return {};
+ u64 fp, u64 lr, u64 pc) {
+ std::vector<BacktraceEntry> out;
+ auto& memory = system.Memory();
+
+ out.push_back({"", 0, pc, 0, ""});
+
+ // fp (= r11) points to the last frame record.
+ // Frame records are two words long:
+ // fp+0 : pointer to previous frame record
+ // fp+4 : value of lr for frame
+ while (true) {
+ out.push_back({"", 0, lr, 0, ""});
+ if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) {
+ break;
+ }
+ lr = memory.Read32(fp + 4);
+ fp = memory.Read32(fp);
+ }
+
+ SymbolicateBacktrace(system, out);
+
+ return out;
}
std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext(
System& system, const ThreadContext32& ctx) {
- return GetBacktrace(system, ctx.cpu_registers[13], ctx.cpu_registers[14]);
+ const auto& reg = ctx.cpu_registers;
+ return GetBacktrace(system, reg[11], reg[14], reg[15]);
}
std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const {
- return GetBacktrace(system, GetReg(13), GetReg(14));
+ return GetBacktrace(system, GetReg(11), GetReg(14), GetReg(15));
}
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 5b1d60005..d24ba2289 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -28,8 +28,8 @@ class System;
class ARM_Dynarmic_32 final : public ARM_Interface {
public:
- ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_,
- ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_);
+ ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
+ std::size_t core_index_);
~ARM_Dynarmic_32() override;
void SetPC(u64 pc) override;
@@ -56,6 +56,7 @@ public:
void LoadContext(const ThreadContext64& ctx) override {}
void SignalInterrupt() override;
+ void ClearInterrupt() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;
@@ -72,11 +73,13 @@ protected:
Dynarmic::HaltReason RunJit() override;
Dynarmic::HaltReason StepJit() override;
u32 GetSvcNumber() const override;
+ const Kernel::DebugWatchpoint* HaltedWatchpoint() const override;
+ void RewindBreakpointInstruction() override;
private:
std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
- static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 sp, u64 lr);
+ static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc);
using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
using JitCacheType =
@@ -98,6 +101,10 @@ private:
// SVC callback
u32 svc_swi{};
+
+ // Watchpoint info
+ const Kernel::DebugWatchpoint* halted_watchpoint;
+ ThreadContext32 breakpoint_context;
};
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index d4c67eafd..1d46f6d40 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -10,7 +10,6 @@
#include "common/logging/log.h"
#include "common/page_table.h"
#include "common/settings.h"
-#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
#include "core/core.h"
@@ -29,62 +28,89 @@ using namespace Common::Literals;
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
- : parent{parent_}, memory(parent.system.Memory()) {}
+ : parent{parent_},
+ memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {}
u8 MemoryRead8(u64 vaddr) override {
+ CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
return memory.Read8(vaddr);
}
u16 MemoryRead16(u64 vaddr) override {
+ CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
return memory.Read16(vaddr);
}
u32 MemoryRead32(u64 vaddr) override {
+ CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return memory.Read32(vaddr);
}
u64 MemoryRead64(u64 vaddr) override {
+ CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return memory.Read64(vaddr);
}
Vector MemoryRead128(u64 vaddr) override {
+ CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
return {memory.Read64(vaddr), memory.Read64(vaddr + 8)};
}
+ std::optional<u32> MemoryReadCode(u64 vaddr) override {
+ if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) {
+ return std::nullopt;
+ }
+ return memory.Read32(vaddr);
+ }
void MemoryWrite8(u64 vaddr, u8 value) override {
- memory.Write8(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
+ memory.Write8(vaddr, value);
+ }
}
void MemoryWrite16(u64 vaddr, u16 value) override {
- memory.Write16(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
+ memory.Write16(vaddr, value);
+ }
}
void MemoryWrite32(u64 vaddr, u32 value) override {
- memory.Write32(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
+ memory.Write32(vaddr, value);
+ }
}
void MemoryWrite64(u64 vaddr, u64 value) override {
- memory.Write64(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
+ memory.Write64(vaddr, value);
+ }
}
void MemoryWrite128(u64 vaddr, Vector value) override {
- memory.Write64(vaddr, value[0]);
- memory.Write64(vaddr + 8, value[1]);
+ if (CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write)) {
+ memory.Write64(vaddr, value[0]);
+ memory.Write64(vaddr + 8, value[1]);
+ }
}
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override {
- return memory.WriteExclusive8(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive8(vaddr, value, expected);
}
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override {
- return memory.WriteExclusive16(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive16(vaddr, value, expected);
}
bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override {
- return memory.WriteExclusive32(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive32(vaddr, value, expected);
}
bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override {
- return memory.WriteExclusive64(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive64(vaddr, value, expected);
}
bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override {
- return memory.WriteExclusive128(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive128(vaddr, value, expected);
}
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
parent.LogBacktrace();
LOG_ERROR(Core_ARM,
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
- num_instructions, MemoryReadCode(pc));
+ num_instructions, memory.Read32(pc));
}
void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
@@ -117,16 +143,19 @@ public:
case Dynarmic::A64::Exception::SendEventLocal:
case Dynarmic::A64::Exception::Yield:
return;
+ case Dynarmic::A64::Exception::NoExecuteFault:
+ LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc);
+ ReturnException(pc, ARM_Interface::no_execute);
+ return;
default:
- if (parent.system.DebuggerEnabled()) {
- parent.jit.load()->SetPC(pc);
- parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
+ if (debugger_enabled) {
+ ReturnException(pc, ARM_Interface::breakpoint);
return;
}
parent.LogBacktrace();
- ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
- static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
+ LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
+ static_cast<std::size_t>(exception), pc, memory.Read32(pc));
}
}
@@ -136,7 +165,9 @@ public:
}
void AddTicks(u64 ticks) override {
- ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
+ if (parent.uses_wall_clock) {
+ return;
+ }
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
// rough approximation of the amount of executed ticks in the system, it may be thrown off
@@ -151,7 +182,12 @@ public:
}
u64 GetTicksRemaining() override {
- ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
+ if (parent.uses_wall_clock) {
+ if (!IsInterrupted()) {
+ return minimum_run_cycles;
+ }
+ return 0U;
+ }
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
}
@@ -160,11 +196,37 @@ public:
return parent.system.CoreTiming().GetClockTicks();
}
+ bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
+ if (!debugger_enabled) {
+ return true;
+ }
+
+ const auto match{parent.MatchingWatchpoint(addr, size, type)};
+ if (match) {
+ parent.halted_watchpoint = match;
+ parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
+ return false;
+ }
+
+ return true;
+ }
+
+ void ReturnException(u64 pc, Dynarmic::HaltReason hr) {
+ parent.SaveContext(parent.breakpoint_context);
+ parent.breakpoint_context.pc = pc;
+ parent.jit.load()->HaltExecution(hr);
+ }
+
+ bool IsInterrupted() {
+ return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted();
+ }
+
ARM_Dynarmic_64& parent;
Core::Memory::Memory& memory;
u64 tpidrro_el0 = 0;
u64 tpidr_el0 = 0;
- static constexpr u64 minimum_run_cycles = 1000U;
+ bool debugger_enabled{};
+ static constexpr u64 minimum_run_cycles = 10000U;
};
std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* page_table,
@@ -188,7 +250,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
config.fastmem_address_space_bits = address_space_bits;
config.silently_mirror_fastmem = false;
- config.fastmem_exclusive_access = true;
+ config.fastmem_exclusive_access = config.fastmem_pointer != nullptr;
config.recompile_on_exclusive_fastmem_failure = true;
}
@@ -208,17 +270,20 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
// Timing
config.wall_clock_cntpct = uses_wall_clock;
- config.enable_cycle_counting = !uses_wall_clock;
+ config.enable_cycle_counting = true;
// Code cache size
config.code_cache_size = 512_MiB;
- config.far_code_offset = 400_MiB;
+
+ // Allow memory fault handling to work
+ if (system.DebuggerEnabled()) {
+ config.check_halt_on_memory_access = true;
+ }
// null_jit
if (!page_table) {
// Don't waste too much memory on null_jit
config.code_cache_size = 8_MiB;
- config.far_code_offset = 4_MiB;
}
// Safe optimizations
@@ -249,6 +314,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
}
if (!Settings::values.cpuopt_fastmem) {
config.fastmem_pointer = nullptr;
+ config.fastmem_exclusive_access = false;
}
if (!Settings::values.cpuopt_fastmem_exclusives) {
config.fastmem_exclusive_access = false;
@@ -308,10 +374,17 @@ u32 ARM_Dynarmic_64::GetSvcNumber() const {
return svc_swi;
}
-ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
- bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
- std::size_t core_index_)
- : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_},
+const Kernel::DebugWatchpoint* ARM_Dynarmic_64::HaltedWatchpoint() const {
+ return halted_watchpoint;
+}
+
+void ARM_Dynarmic_64::RewindBreakpointInstruction() {
+ LoadContext(breakpoint_context);
+}
+
+ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, bool uses_wall_clock_,
+ ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_)
+ : ARM_Interface{system_, uses_wall_clock_},
cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {}
@@ -398,6 +471,10 @@ void ARM_Dynarmic_64::SignalInterrupt() {
jit.load()->HaltExecution(break_loop);
}
+void ARM_Dynarmic_64::ClearInterrupt() {
+ jit.load()->ClearHalt(break_loop);
+}
+
void ARM_Dynarmic_64::ClearInstructionCache() {
jit.load()->ClearCache();
}
@@ -429,22 +506,22 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
}
std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system,
- u64 fp, u64 lr) {
+ u64 fp, u64 lr, u64 pc) {
std::vector<BacktraceEntry> out;
auto& memory = system.Memory();
- // fp (= r29) points to the last frame record.
- // Note that this is the frame record for the *previous* frame, not the current one.
- // Note we need to subtract 4 from our last read to get the proper address
+ out.push_back({"", 0, pc, 0, ""});
+
+ // fp (= x29) points to the previous frame record.
// Frame records are two words long:
// fp+0 : pointer to previous frame record
// fp+8 : value of lr for frame
while (true) {
out.push_back({"", 0, lr, 0, ""});
- if (!fp) {
+ if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) {
break;
}
- lr = memory.Read64(fp + 8) - 4;
+ lr = memory.Read64(fp + 8);
fp = memory.Read64(fp);
}
@@ -455,11 +532,12 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::S
std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext(
System& system, const ThreadContext64& ctx) {
- return GetBacktrace(system, ctx.cpu_registers[29], ctx.cpu_registers[30]);
+ const auto& reg = ctx.cpu_registers;
+ return GetBacktrace(system, reg[29], reg[30], ctx.pc);
}
std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const {
- return GetBacktrace(system, GetReg(29), GetReg(30));
+ return GetBacktrace(system, GetReg(29), GetReg(30), GetPC());
}
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index abfbc3c3f..ed1a5eb96 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -20,14 +20,13 @@ class Memory;
namespace Core {
class DynarmicCallbacks64;
-class CPUInterruptHandler;
class DynarmicExclusiveMonitor;
class System;
class ARM_Dynarmic_64 final : public ARM_Interface {
public:
- ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_,
- ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_);
+ ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
+ std::size_t core_index_);
~ARM_Dynarmic_64() override;
void SetPC(u64 pc) override;
@@ -50,6 +49,7 @@ public:
void LoadContext(const ThreadContext64& ctx) override;
void SignalInterrupt() override;
+ void ClearInterrupt() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;
@@ -66,12 +66,14 @@ protected:
Dynarmic::HaltReason RunJit() override;
Dynarmic::HaltReason StepJit() override;
u32 GetSvcNumber() const override;
+ const Kernel::DebugWatchpoint* HaltedWatchpoint() const override;
+ void RewindBreakpointInstruction() override;
private:
std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
std::size_t address_space_bits) const;
- static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr);
+ static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc);
using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
using JitCacheType =
@@ -91,6 +93,10 @@ private:
// SVC callback
u32 svc_swi{};
+
+ // Breakpoint info
+ const Kernel::DebugWatchpoint* halted_watchpoint;
+ ThreadContext64 breakpoint_context;
};
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
index 6aae79c48..200efe4db 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <fmt/format.h>
#include "common/logging/log.h"
@@ -9,6 +8,10 @@
#include "core/core.h"
#include "core/core_timing.h"
+#ifdef _MSC_VER
+#include <intrin.h>
+#endif
+
using Callback = Dynarmic::A32::Coprocessor::Callback;
using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord;
using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords;
@@ -48,12 +51,31 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
switch (opc2) {
case 4:
// CP15_DATA_SYNC_BARRIER
- // This is a dummy write, we ignore the value written here.
- return &dummy_value;
+ return Callback{
+ [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
+#ifdef _MSC_VER
+ _mm_mfence();
+ _mm_lfence();
+#else
+ asm volatile("mfence\n\tlfence\n\t" : : : "memory");
+#endif
+ return 0;
+ },
+ std::nullopt,
+ };
case 5:
// CP15_DATA_MEMORY_BARRIER
- // This is a dummy write, we ignore the value written here.
- return &dummy_value;
+ return Callback{
+ [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
+#ifdef _MSC_VER
+ _mm_mfence();
+#else
+ asm volatile("mfence\n\t" : : : "memory");
+#endif
+ return 0;
+ },
+ std::nullopt,
+ };
}
}
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
index f271b2070..d90b3e568 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -36,6 +35,8 @@ public:
ARM_Dynarmic_32& parent;
u32 uprw = 0;
u32 uro = 0;
+
+ friend class ARM_Dynarmic_32;
};
} // namespace Core
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 7723d9782..ea32a4a8d 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <atomic>
@@ -8,6 +7,7 @@
#include <memory>
#include <utility>
+#include "audio_core/audio_core.h"
#include "common/fs/fs.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
@@ -42,14 +42,15 @@
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/time/time_manager.h"
+#include "core/internal_network/network.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/memory/cheat_engine.h"
-#include "core/network/network.h"
#include "core/perf_stats.h"
#include "core/reporter.h"
#include "core/telemetry_session.h"
#include "core/tools/freezer.h"
+#include "network/network.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -129,7 +130,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl {
explicit Impl(System& system)
- : kernel{system}, fs_controller{system}, memory{system}, hid_core{},
+ : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{},
cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
SystemResultStatus Run() {
@@ -140,6 +141,8 @@ struct System::Impl {
core_timing.SyncPause(false);
is_paused = false;
+ audio_core->PauseSinks(false);
+
return status;
}
@@ -147,6 +150,8 @@ struct System::Impl {
std::unique_lock<std::mutex> lk(suspend_guard);
status = SystemResultStatus::Success;
+ audio_core->PauseSinks(true);
+
core_timing.SyncPause(true);
kernel.Suspend(true);
is_paused = true;
@@ -154,6 +159,11 @@ struct System::Impl {
return status;
}
+ bool IsPaused() const {
+ std::unique_lock lk(suspend_guard);
+ return is_paused;
+ }
+
std::unique_lock<std::mutex> StallProcesses() {
std::unique_lock<std::mutex> lk(suspend_guard);
kernel.Suspend(true);
@@ -214,6 +224,8 @@ struct System::Impl {
return SystemResultStatus::ErrorVideoCore;
}
+ audio_core = std::make_unique<AudioCore::AudioCore>(system);
+
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
services = std::make_unique<Service::Services>(service_manager, system);
interrupt_manager = std::make_unique<Hardware::InterruptManager>(system);
@@ -290,7 +302,7 @@ struct System::Impl {
if (Settings::values.gamecard_current_game) {
fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath));
} else if (!Settings::values.gamecard_path.GetValue().empty()) {
- const auto gamecard_path = Settings::values.gamecard_path.GetValue();
+ const auto& gamecard_path = Settings::values.gamecard_path.GetValue();
fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, gamecard_path));
}
}
@@ -303,11 +315,24 @@ struct System::Impl {
GetAndResetPerfStats();
perf_stats->BeginSystemFrame();
+ std::string name = "Unknown Game";
+ if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) {
+ LOG_ERROR(Core, "Failed to read title for ROM (Error {})", load_result);
+ }
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ Network::GameInfo game_info;
+ game_info.name = name;
+ game_info.id = program_id;
+ room_member->SendGameInfo(game_info);
+ }
+
status = SystemResultStatus::Success;
return status;
}
void Shutdown() {
+ SetShuttingDown(true);
+
// Log last frame performance stats if game was loded
if (perf_stats) {
const auto perf_results = GetAndResetPerfStats();
@@ -333,23 +358,37 @@ struct System::Impl {
kernel.ShutdownCores();
cpu_manager.Shutdown();
debugger.reset();
+ kernel.CloseServices();
services.reset();
service_manager.reset();
cheat_engine.reset();
telemetry_session.reset();
- cpu_manager.Shutdown();
time_manager.Shutdown();
core_timing.Shutdown();
app_loader.reset();
+ audio_core.reset();
gpu_core.reset();
perf_stats.reset();
kernel.Shutdown();
memory.Reset();
applet_manager.ClearAll();
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ Network::GameInfo game_info{};
+ room_member->SendGameInfo(game_info);
+ }
+
LOG_DEBUG(Core, "Shutdown OK");
}
+ bool IsShuttingDown() const {
+ return is_shutting_down;
+ }
+
+ void SetShuttingDown(bool shutting_down) {
+ is_shutting_down = shutting_down;
+ }
+
Loader::ResultStatus GetGameName(std::string& out) const {
if (app_loader == nullptr)
return Loader::ResultStatus::ErrorNotInitialized;
@@ -392,8 +431,9 @@ struct System::Impl {
return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs());
}
- std::mutex suspend_guard;
+ mutable std::mutex suspend_guard;
bool is_paused{};
+ std::atomic<bool> is_shutting_down{};
Timing::CoreTiming core_timing;
Kernel::KernelCore kernel;
@@ -407,8 +447,11 @@ struct System::Impl {
std::unique_ptr<Tegra::GPU> gpu_core;
std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
std::unique_ptr<Core::DeviceMemory> device_memory;
+ std::unique_ptr<AudioCore::AudioCore> audio_core;
Core::Memory::Memory memory;
Core::HID::HIDCore hid_core;
+ Network::RoomNetwork room_network;
+
CpuManager cpu_manager;
std::atomic_bool is_powered_on{};
bool exit_lock = false;
@@ -479,6 +522,10 @@ SystemResultStatus System::Pause() {
return impl->Pause();
}
+bool System::IsPaused() const {
+ return impl->IsPaused();
+}
+
void System::InvalidateCpuInstructionCaches() {
impl->kernel.InvalidateAllInstructionCaches();
}
@@ -491,6 +538,14 @@ void System::Shutdown() {
impl->Shutdown();
}
+bool System::IsShuttingDown() const {
+ return impl->IsShuttingDown();
+}
+
+void System::SetShuttingDown(bool shutting_down) {
+ impl->SetShuttingDown(shutting_down);
+}
+
void System::DetachDebugger() {
if (impl->debugger) {
impl->debugger->NotifyShutdown();
@@ -640,6 +695,14 @@ const HID::HIDCore& System::HIDCore() const {
return impl->hid_core;
}
+AudioCore::AudioCore& System::AudioCore() {
+ return *impl->audio_core;
+}
+
+const AudioCore::AudioCore& System::AudioCore() const {
+ return *impl->audio_core;
+}
+
Timing::CoreTiming& System::CoreTiming() {
return impl->core_timing;
}
@@ -834,6 +897,14 @@ const Core::Debugger& System::GetDebugger() const {
return *impl->debugger;
}
+Network::RoomNetwork& System::GetRoomNetwork() {
+ return impl->room_network;
+}
+
+const Network::RoomNetwork& System::GetRoomNetwork() const {
+ return impl->room_network;
+}
+
void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) {
impl->execute_program_callback = std::move(callback);
}
diff --git a/src/core/core.h b/src/core/core.h
index 60efe4410..0ce3b1d60 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -81,6 +80,10 @@ namespace VideoCore {
class RendererBase;
} // namespace VideoCore
+namespace AudioCore {
+class AudioCore;
+} // namespace AudioCore
+
namespace Core::Timing {
class CoreTiming;
}
@@ -93,6 +96,10 @@ namespace Core::HID {
class HIDCore;
}
+namespace Network {
+class RoomNetwork;
+}
+
namespace Core {
class ARM_Interface;
@@ -148,6 +155,9 @@ public:
*/
[[nodiscard]] SystemResultStatus Pause();
+ /// Check if the core is currently paused.
+ [[nodiscard]] bool IsPaused() const;
+
/**
* Invalidate the CPU instruction caches
* This function should only be used by GDB Stub to support breakpoints, memory updates and
@@ -160,6 +170,12 @@ public:
/// Shutdown the emulated system.
void Shutdown();
+ /// Check if the core is shutting down.
+ [[nodiscard]] bool IsShuttingDown() const;
+
+ /// Set the shutting down state.
+ void SetShuttingDown(bool shutting_down);
+
/// Forcibly detach the debugger if it is running.
void DetachDebugger();
@@ -250,6 +266,12 @@ public:
/// Gets an immutable reference to the renderer.
[[nodiscard]] const VideoCore::RendererBase& Renderer() const;
+ /// Gets a mutable reference to the audio interface
+ [[nodiscard]] AudioCore::AudioCore& AudioCore();
+
+ /// Gets an immutable reference to the audio interface.
+ [[nodiscard]] const AudioCore::AudioCore& AudioCore() const;
+
/// Gets the global scheduler
[[nodiscard]] Kernel::GlobalSchedulerContext& GlobalSchedulerContext();
@@ -360,6 +382,12 @@ public:
[[nodiscard]] Core::Debugger& GetDebugger();
[[nodiscard]] const Core::Debugger& GetDebugger() const;
+ /// Gets a mutable reference to the Room Network.
+ [[nodiscard]] Network::RoomNetwork& GetRoomNetwork();
+
+ /// Gets an immutable reference to the Room Network.
+ [[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const;
+
void SetExitLock(bool locked);
[[nodiscard]] bool GetExitLock() const;
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 29e7dba9b..2dbb99c8b 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -20,10 +20,11 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac
}
struct CoreTiming::Event {
- u64 time;
+ s64 time;
u64 fifo_order;
std::uintptr_t user_data;
std::weak_ptr<EventType> type;
+ s64 reschedule_time;
// Sort by time, unless the times are the same, in which case sort by
// the order added to the queue
@@ -45,7 +46,7 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) {
constexpr char name[] = "yuzu:HostTiming";
MicroProfileOnThreadCreate(name);
Common::SetCurrentThreadName(name);
- Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
+ Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
instance.on_thread_init();
instance.ThreadLoop();
MicroProfileOnThreadExit();
@@ -56,7 +57,8 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
event_fifo_id = 0;
shutting_down = false;
ticks = 0;
- const auto empty_timed_callback = [](std::uintptr_t, std::chrono::nanoseconds) {};
+ const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds)
+ -> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
ev_lost = CreateEvent("_lost_event", empty_timed_callback);
if (is_multicore) {
timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this));
@@ -71,6 +73,7 @@ void CoreTiming::Shutdown() {
if (timer_thread) {
timer_thread->join();
}
+ pause_callbacks.clear();
ClearPendingEvents();
timer_thread.reset();
has_started = false;
@@ -79,12 +82,21 @@ void CoreTiming::Shutdown() {
void CoreTiming::Pause(bool is_paused) {
paused = is_paused;
pause_event.Set();
+
+ if (!is_paused) {
+ pause_end_time = GetGlobalTimeNs().count();
+ }
+
+ for (auto& cb : pause_callbacks) {
+ cb(is_paused);
+ }
}
void CoreTiming::SyncPause(bool is_paused) {
if (is_paused == paused && paused_set == paused) {
return;
}
+
Pause(is_paused);
if (timer_thread) {
if (!is_paused) {
@@ -94,6 +106,14 @@ void CoreTiming::SyncPause(bool is_paused) {
while (paused_set != is_paused)
;
}
+
+ if (!is_paused) {
+ pause_end_time = GetGlobalTimeNs().count();
+ }
+
+ for (auto& cb : pause_callbacks) {
+ cb(is_paused);
+ }
}
bool CoreTiming::IsRunning() const {
@@ -106,18 +126,32 @@ bool CoreTiming::HasPendingEvents() const {
void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
const std::shared_ptr<EventType>& event_type,
- std::uintptr_t user_data) {
+ std::uintptr_t user_data, bool absolute_time) {
{
std::scoped_lock scope{basic_lock};
- const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count());
-
- event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type});
+ const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
+ event_queue.emplace_back(
+ Event{next_time.count(), event_fifo_id++, user_data, event_type, 0});
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
+
event.Set();
}
+void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
+ std::chrono::nanoseconds resched_time,
+ const std::shared_ptr<EventType>& event_type,
+ std::uintptr_t user_data, bool absolute_time) {
+ std::scoped_lock scope{basic_lock};
+ const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
+
+ event_queue.emplace_back(
+ Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()});
+
+ std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
+}
+
void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data) {
std::scoped_lock scope{basic_lock};
@@ -185,6 +219,11 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
}
}
+void CoreTiming::RegisterPauseCallback(PauseCallback&& callback) {
+ std::scoped_lock lock{basic_lock};
+ pause_callbacks.emplace_back(std::move(callback));
+}
+
std::optional<s64> CoreTiming::Advance() {
std::scoped_lock lock{advance_lock, basic_lock};
global_timer = GetGlobalTimeNs().count();
@@ -193,14 +232,34 @@ std::optional<s64> CoreTiming::Advance() {
Event evt = std::move(event_queue.front());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
event_queue.pop_back();
- basic_lock.unlock();
if (const auto event_type{evt.type.lock()}) {
- event_type->callback(
- evt.user_data, std::chrono::nanoseconds{static_cast<s64>(global_timer - evt.time)});
+ basic_lock.unlock();
+
+ const auto new_schedule_time{event_type->callback(
+ evt.user_data, evt.time,
+ std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
+
+ basic_lock.lock();
+
+ if (evt.reschedule_time != 0) {
+ // If this event was scheduled into a pause, its time now is going to be way behind.
+ // Re-set this event to continue from the end of the pause.
+ auto next_time{evt.time + evt.reschedule_time};
+ if (evt.time < pause_end_time) {
+ next_time = pause_end_time + evt.reschedule_time;
+ }
+
+ const auto next_schedule_time{new_schedule_time.has_value()
+ ? new_schedule_time.value().count()
+ : evt.reschedule_time};
+
+ event_queue.emplace_back(
+ Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time});
+ std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
+ }
}
- basic_lock.lock();
global_timer = GetGlobalTimeNs().count();
}
@@ -229,6 +288,7 @@ void CoreTiming::ThreadLoop() {
}
wait_set = false;
}
+
paused_set = true;
clock->Pause(true);
pause_event.Wait();
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index d27773009..6aa3ae923 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -20,8 +20,9 @@
namespace Core::Timing {
/// A callback that may be scheduled for a particular core timing event.
-using TimedCallback =
- std::function<void(std::uintptr_t user_data, std::chrono::nanoseconds ns_late)>;
+using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>(
+ std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>;
+using PauseCallback = std::function<void(bool paused)>;
/// Contains the characteristics of a particular event.
struct EventType {
@@ -93,7 +94,15 @@ public:
/// Schedules an event in core timing
void ScheduleEvent(std::chrono::nanoseconds ns_into_future,
- const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0);
+ const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0,
+ bool absolute_time = false);
+
+ /// Schedules an event which will automatically re-schedule itself with the given time, until
+ /// unscheduled
+ void ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
+ std::chrono::nanoseconds resched_time,
+ const std::shared_ptr<EventType>& event_type,
+ std::uintptr_t user_data = 0, bool absolute_time = false);
void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data);
@@ -125,6 +134,9 @@ public:
/// Checks for events manually and returns time in nanoseconds for next event, threadsafe.
std::optional<s64> Advance();
+ /// Register a callback function to be called when coretiming pauses.
+ void RegisterPauseCallback(PauseCallback&& callback);
+
private:
struct Event;
@@ -136,7 +148,7 @@ private:
std::unique_ptr<Common::WallClock> clock;
- u64 global_timer = 0;
+ s64 global_timer = 0;
// The queue is a min-heap using std::make_heap/push_heap/pop_heap.
// We don't use std::priority_queue because we need to be able to serialize, unserialize and
@@ -159,10 +171,13 @@ private:
std::function<void()> on_thread_init{};
bool is_multicore{};
+ s64 pause_end_time{};
/// Cycle timing
u64 ticks{};
s64 downcount{};
+
+ std::vector<PauseCallback> pause_callbacks{};
};
/// Creates a core timing event with the given name and callback.
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index d69b2602a..9b1565ae1 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -8,6 +8,7 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/cpu_manager.h"
+#include "core/hle/kernel/k_interrupt_manager.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
@@ -41,51 +42,31 @@ void CpuManager::Shutdown() {
}
}
-std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
- return GuestThreadFunction;
-}
-
-std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() {
- return IdleThreadFunction;
-}
-
-std::function<void(void*)> CpuManager::GetShutdownThreadStartFunc() {
- return ShutdownThreadFunction;
-}
-
-void CpuManager::GuestThreadFunction(void* cpu_manager_) {
- CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
- if (cpu_manager->is_multicore) {
- cpu_manager->MultiCoreRunGuestThread();
+void CpuManager::GuestThreadFunction() {
+ if (is_multicore) {
+ MultiCoreRunGuestThread();
} else {
- cpu_manager->SingleCoreRunGuestThread();
+ SingleCoreRunGuestThread();
}
}
-void CpuManager::GuestRewindFunction(void* cpu_manager_) {
- CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
- if (cpu_manager->is_multicore) {
- cpu_manager->MultiCoreRunGuestLoop();
+void CpuManager::IdleThreadFunction() {
+ if (is_multicore) {
+ MultiCoreRunIdleThread();
} else {
- cpu_manager->SingleCoreRunGuestLoop();
+ SingleCoreRunIdleThread();
}
}
-void CpuManager::IdleThreadFunction(void* cpu_manager_) {
- CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
- if (cpu_manager->is_multicore) {
- cpu_manager->MultiCoreRunIdleThread();
- } else {
- cpu_manager->SingleCoreRunIdleThread();
- }
+void CpuManager::ShutdownThreadFunction() {
+ ShutdownThread();
}
-void CpuManager::ShutdownThreadFunction(void* cpu_manager) {
- static_cast<CpuManager*>(cpu_manager)->ShutdownThread();
-}
+void CpuManager::HandleInterrupt() {
+ auto& kernel = system.Kernel();
+ auto core_index = kernel.CurrentPhysicalCoreIndex();
-void* CpuManager::GetStartFuncParameter() {
- return this;
+ Kernel::KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_index));
}
///////////////////////////////////////////////////////////////////////////////
@@ -93,16 +74,9 @@ void* CpuManager::GetStartFuncParameter() {
///////////////////////////////////////////////////////////////////////////////
void CpuManager::MultiCoreRunGuestThread() {
+ // Similar to UserModeThreadStarter in HOS
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
- auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
- auto& host_context = thread->GetHostContext();
- host_context->SetRewindPoint(GuestRewindFunction, this);
- MultiCoreRunGuestLoop();
-}
-
-void CpuManager::MultiCoreRunGuestLoop() {
- auto& kernel = system.Kernel();
while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore();
@@ -110,18 +84,26 @@ void CpuManager::MultiCoreRunGuestLoop() {
physical_core->Run();
physical_core = &kernel.CurrentPhysicalCore();
}
- {
- Kernel::KScopedDisableDispatch dd(kernel);
- physical_core->ArmInterface().ClearExclusiveState();
- }
+
+ HandleInterrupt();
}
}
void CpuManager::MultiCoreRunIdleThread() {
+ // Not accurate to HOS. Remove this entire method when singlecore is removed.
+ // See notes in KScheduler::ScheduleImpl for more information about why this
+ // is inaccurate.
+
auto& kernel = system.Kernel();
+ kernel.CurrentScheduler()->OnThreadStart();
+
while (true) {
- Kernel::KScopedDisableDispatch dd(kernel);
- kernel.CurrentPhysicalCore().Idle();
+ auto& physical_core = kernel.CurrentPhysicalCore();
+ if (!physical_core.IsInterrupted()) {
+ physical_core.Idle();
+ }
+
+ HandleInterrupt();
}
}
@@ -132,80 +114,73 @@ void CpuManager::MultiCoreRunIdleThread() {
void CpuManager::SingleCoreRunGuestThread() {
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
- auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
- auto& host_context = thread->GetHostContext();
- host_context->SetRewindPoint(GuestRewindFunction, this);
- SingleCoreRunGuestLoop();
-}
-void CpuManager::SingleCoreRunGuestLoop() {
- auto& kernel = system.Kernel();
while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore();
if (!physical_core->IsInterrupted()) {
physical_core->Run();
physical_core = &kernel.CurrentPhysicalCore();
}
+
kernel.SetIsPhantomModeForSingleCore(true);
system.CoreTiming().Advance();
kernel.SetIsPhantomModeForSingleCore(false);
- physical_core->ArmInterface().ClearExclusiveState();
+
PreemptSingleCore();
- auto& scheduler = kernel.Scheduler(current_core);
- scheduler.RescheduleCurrentCore();
+ HandleInterrupt();
}
}
void CpuManager::SingleCoreRunIdleThread() {
auto& kernel = system.Kernel();
+ kernel.CurrentScheduler()->OnThreadStart();
+
while (true) {
- auto& physical_core = kernel.CurrentPhysicalCore();
PreemptSingleCore(false);
system.CoreTiming().AddTicks(1000U);
idle_count++;
- auto& scheduler = physical_core.Scheduler();
- scheduler.RescheduleCurrentCore();
+ HandleInterrupt();
}
}
-void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
- {
- auto& kernel = system.Kernel();
- auto& scheduler = kernel.Scheduler(current_core);
- Kernel::KThread* current_thread = scheduler.GetCurrentThread();
- if (idle_count >= 4 || from_running_enviroment) {
- if (!from_running_enviroment) {
- system.CoreTiming().Idle();
- idle_count = 0;
- }
- kernel.SetIsPhantomModeForSingleCore(true);
- system.CoreTiming().Advance();
- kernel.SetIsPhantomModeForSingleCore(false);
- }
- current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
- system.CoreTiming().ResetTicks();
- scheduler.Unload(scheduler.GetCurrentThread());
-
- auto& next_scheduler = kernel.Scheduler(current_core);
- Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext());
- }
+void CpuManager::PreemptSingleCore(bool from_running_environment) {
+ auto& kernel = system.Kernel();
- // May have changed scheduler
- {
- auto& scheduler = system.Kernel().Scheduler(current_core);
- scheduler.Reload(scheduler.GetCurrentThread());
- if (!scheduler.IsIdle()) {
+ if (idle_count >= 4 || from_running_environment) {
+ if (!from_running_environment) {
+ system.CoreTiming().Idle();
idle_count = 0;
}
+ kernel.SetIsPhantomModeForSingleCore(true);
+ system.CoreTiming().Advance();
+ kernel.SetIsPhantomModeForSingleCore(false);
+ }
+ current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
+ system.CoreTiming().ResetTicks();
+ kernel.Scheduler(current_core).PreemptSingleCore();
+
+ // We've now been scheduled again, and we may have exchanged schedulers.
+ // Reload the scheduler in case it's different.
+ if (!kernel.Scheduler(current_core).IsIdle()) {
+ idle_count = 0;
}
}
+void CpuManager::GuestActivate() {
+ // Similar to the HorizonKernelMain callback in HOS
+ auto& kernel = system.Kernel();
+ auto* scheduler = kernel.CurrentScheduler();
+
+ scheduler->Activate();
+ UNREACHABLE();
+}
+
void CpuManager::ShutdownThread() {
auto& kernel = system.Kernel();
+ auto* thread = kernel.GetCurrentEmuThread();
auto core = is_multicore ? kernel.CurrentPhysicalCoreIndex() : 0;
- auto* current_thread = kernel.GetCurrentEmuThread();
- Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
+ Common::Fiber::YieldTo(thread->GetHostContext(), *core_data[core].host_context);
UNREACHABLE();
}
@@ -237,8 +212,12 @@ void CpuManager::RunThread(std::size_t core) {
system.GPU().ObtainContext();
}
- auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
- Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
+ auto& kernel = system.Kernel();
+ auto& scheduler = *kernel.CurrentScheduler();
+ auto* thread = scheduler.GetSchedulerCurrentThread();
+ Kernel::SetCurrentThread(kernel, thread);
+
+ Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
}
} // namespace Core
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index f0751fc58..95ea3ef39 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -50,10 +50,18 @@ public:
void Initialize();
void Shutdown();
- static std::function<void(void*)> GetGuestThreadStartFunc();
- static std::function<void(void*)> GetIdleThreadStartFunc();
- static std::function<void(void*)> GetShutdownThreadStartFunc();
- void* GetStartFuncParameter();
+ std::function<void()> GetGuestActivateFunc() {
+ return [this] { GuestActivate(); };
+ }
+ std::function<void()> GetGuestThreadFunc() {
+ return [this] { GuestThreadFunction(); };
+ }
+ std::function<void()> GetIdleThreadStartFunc() {
+ return [this] { IdleThreadFunction(); };
+ }
+ std::function<void()> GetShutdownThreadStartFunc() {
+ return [this] { ShutdownThreadFunction(); };
+ }
void PreemptSingleCore(bool from_running_enviroment = true);
@@ -62,21 +70,20 @@ public:
}
private:
- static void GuestThreadFunction(void* cpu_manager);
- static void GuestRewindFunction(void* cpu_manager);
- static void IdleThreadFunction(void* cpu_manager);
- static void ShutdownThreadFunction(void* cpu_manager);
+ void GuestThreadFunction();
+ void IdleThreadFunction();
+ void ShutdownThreadFunction();
void MultiCoreRunGuestThread();
- void MultiCoreRunGuestLoop();
void MultiCoreRunIdleThread();
void SingleCoreRunGuestThread();
- void SingleCoreRunGuestLoop();
void SingleCoreRunIdleThread();
static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
+ void GuestActivate();
+ void HandleInterrupt();
void ShutdownThread();
void RunThread(std::size_t core);
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
index ab3940922..e42bdd17d 100644
--- a/src/core/debugger/debugger.cpp
+++ b/src/core/debugger/debugger.cpp
@@ -15,6 +15,7 @@
#include "core/debugger/debugger_interface.h"
#include "core/debugger/gdbstub.h"
#include "core/hle/kernel/global_scheduler_context.h"
+#include "core/hle/kernel/k_scheduler.h"
template <typename Readable, typename Buffer, typename Callback>
static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
@@ -44,12 +45,14 @@ static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
enum class SignalType {
Stopped,
+ Watchpoint,
ShuttingDown,
};
struct SignalInfo {
SignalType type;
Kernel::KThread* thread;
+ const Kernel::DebugWatchpoint* watchpoint;
};
namespace Core {
@@ -157,13 +160,19 @@ private:
void PipeData(std::span<const u8> data) {
switch (info.type) {
case SignalType::Stopped:
+ case SignalType::Watchpoint:
// Stop emulation.
PauseEmulation();
// Notify the client.
active_thread = info.thread;
UpdateActiveThread();
- frontend->Stopped(active_thread);
+
+ if (info.type == SignalType::Watchpoint) {
+ frontend->Watchpoint(active_thread, *info.watchpoint);
+ } else {
+ frontend->Stopped(active_thread);
+ }
break;
case SignalType::ShuttingDown:
@@ -222,13 +231,12 @@ private:
}
void PauseEmulation() {
+ Kernel::KScopedSchedulerLock sl{system.Kernel()};
+
// Put all threads to sleep on next scheduler round.
for (auto* thread : ThreadList()) {
thread->RequestSuspend(Kernel::SuspendType::Debug);
}
-
- // Signal an interrupt so that scheduler will fire.
- system.Kernel().InterruptAllPhysicalCores();
}
void ResumeEmulation(Kernel::KThread* except = nullptr) {
@@ -245,7 +253,8 @@ private:
template <typename Callback>
void MarkResumed(Callback&& cb) {
- std::scoped_lock lk{connection_lock};
+ Kernel::KScopedSchedulerLock sl{system.Kernel()};
+ std::scoped_lock cl{connection_lock};
stopped = false;
cb();
}
@@ -290,12 +299,17 @@ Debugger::Debugger(Core::System& system, u16 port) {
Debugger::~Debugger() = default;
bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
- return impl && impl->SignalDebugger(SignalInfo{SignalType::Stopped, thread});
+ return impl && impl->SignalDebugger(SignalInfo{SignalType::Stopped, thread, nullptr});
+}
+
+bool Debugger::NotifyThreadWatchpoint(Kernel::KThread* thread,
+ const Kernel::DebugWatchpoint& watch) {
+ return impl && impl->SignalDebugger(SignalInfo{SignalType::Watchpoint, thread, &watch});
}
void Debugger::NotifyShutdown() {
if (impl) {
- impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr});
+ impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr, nullptr});
}
}
diff --git a/src/core/debugger/debugger.h b/src/core/debugger/debugger.h
index f9738ca3d..b2f503376 100644
--- a/src/core/debugger/debugger.h
+++ b/src/core/debugger/debugger.h
@@ -9,7 +9,8 @@
namespace Kernel {
class KThread;
-}
+struct DebugWatchpoint;
+} // namespace Kernel
namespace Core {
class System;
@@ -40,6 +41,11 @@ public:
*/
void NotifyShutdown();
+ /*
+ * Notify the debugger that the given thread has stopped due to hitting a watchpoint.
+ */
+ bool NotifyThreadWatchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch);
+
private:
std::unique_ptr<DebuggerImpl> impl;
};
diff --git a/src/core/debugger/debugger_interface.h b/src/core/debugger/debugger_interface.h
index c0bb4ecaf..5b31edc43 100644
--- a/src/core/debugger/debugger_interface.h
+++ b/src/core/debugger/debugger_interface.h
@@ -11,7 +11,8 @@
namespace Kernel {
class KThread;
-}
+struct DebugWatchpoint;
+} // namespace Kernel
namespace Core {
@@ -71,6 +72,11 @@ public:
*/
virtual void ShuttingDown() = 0;
+ /*
+ * Called when emulation has stopped on a watchpoint.
+ */
+ virtual void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) = 0;
+
/**
* Called when new data is asynchronously received on the client socket.
* A list of actions to perform is returned.
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index 52e76f659..884229c77 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -112,6 +112,23 @@ void GDBStub::Stopped(Kernel::KThread* thread) {
SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
}
+void GDBStub::Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) {
+ const auto status{arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)};
+
+ switch (watch.type) {
+ case Kernel::DebugWatchpointType::Read:
+ SendReply(fmt::format("{}rwatch:{:x};", status, watch.start_address));
+ break;
+ case Kernel::DebugWatchpointType::Write:
+ SendReply(fmt::format("{}watch:{:x};", status, watch.start_address));
+ break;
+ case Kernel::DebugWatchpointType::ReadOrWrite:
+ default:
+ SendReply(fmt::format("{}awatch:{:x};", status, watch.start_address));
+ break;
+ }
+}
+
std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) {
std::vector<DebuggerAction> actions;
current_command.insert(current_command.end(), data.begin(), data.end());
@@ -235,6 +252,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
const auto sep{std::find(command.begin(), command.end(), '=') - command.begin() + 1};
const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
arch->RegWrite(backend.GetActiveThread(), reg, std::string_view(command).substr(sep));
+ SendReply(GDB_STUB_REPLY_OK);
break;
}
case 'm': {
@@ -278,41 +296,121 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
case 'c':
actions.push_back(DebuggerAction::Continue);
break;
- case 'Z': {
- const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
- const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
+ case 'Z':
+ HandleBreakpointInsert(command);
+ break;
+ case 'z':
+ HandleBreakpointRemove(command);
+ break;
+ default:
+ SendReply(GDB_STUB_REPLY_EMPTY);
+ break;
+ }
+}
- if (system.Memory().IsValidVirtualAddress(addr)) {
- replaced_instructions[addr] = system.Memory().Read32(addr);
- system.Memory().Write32(addr, arch->BreakpointInstruction());
- system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
+enum class BreakpointType {
+ Software = 0,
+ Hardware = 1,
+ WriteWatch = 2,
+ ReadWatch = 3,
+ AccessWatch = 4,
+};
+
+void GDBStub::HandleBreakpointInsert(std::string_view command) {
+ const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
+ const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
+ const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
+ command.begin() + 1};
+ const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
+ const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
+
+ if (!system.Memory().IsValidVirtualAddressRange(addr, size)) {
+ SendReply(GDB_STUB_REPLY_ERR);
+ return;
+ }
- SendReply(GDB_STUB_REPLY_OK);
- } else {
- SendReply(GDB_STUB_REPLY_ERR);
- }
+ bool success{};
+
+ switch (type) {
+ case BreakpointType::Software:
+ replaced_instructions[addr] = system.Memory().Read32(addr);
+ system.Memory().Write32(addr, arch->BreakpointInstruction());
+ system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
+ success = true;
+ break;
+ case BreakpointType::WriteWatch:
+ success = system.CurrentProcess()->InsertWatchpoint(system, addr, size,
+ Kernel::DebugWatchpointType::Write);
break;
+ case BreakpointType::ReadWatch:
+ success = system.CurrentProcess()->InsertWatchpoint(system, addr, size,
+ Kernel::DebugWatchpointType::Read);
+ break;
+ case BreakpointType::AccessWatch:
+ success = system.CurrentProcess()->InsertWatchpoint(
+ system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
+ break;
+ case BreakpointType::Hardware:
+ default:
+ SendReply(GDB_STUB_REPLY_EMPTY);
+ return;
+ }
+
+ if (success) {
+ SendReply(GDB_STUB_REPLY_OK);
+ } else {
+ SendReply(GDB_STUB_REPLY_ERR);
}
- case 'z': {
- const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
- const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
+}
+
+void GDBStub::HandleBreakpointRemove(std::string_view command) {
+ const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
+ const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
+ const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
+ command.begin() + 1};
+ const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
+ const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
+
+ if (!system.Memory().IsValidVirtualAddressRange(addr, size)) {
+ SendReply(GDB_STUB_REPLY_ERR);
+ return;
+ }
+
+ bool success{};
+ switch (type) {
+ case BreakpointType::Software: {
const auto orig_insn{replaced_instructions.find(addr)};
- if (system.Memory().IsValidVirtualAddress(addr) &&
- orig_insn != replaced_instructions.end()) {
+ if (orig_insn != replaced_instructions.end()) {
system.Memory().Write32(addr, orig_insn->second);
system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
replaced_instructions.erase(addr);
-
- SendReply(GDB_STUB_REPLY_OK);
- } else {
- SendReply(GDB_STUB_REPLY_ERR);
+ success = true;
}
break;
}
+ case BreakpointType::WriteWatch:
+ success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size,
+ Kernel::DebugWatchpointType::Write);
+ break;
+ case BreakpointType::ReadWatch:
+ success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size,
+ Kernel::DebugWatchpointType::Read);
+ break;
+ case BreakpointType::AccessWatch:
+ success = system.CurrentProcess()->RemoveWatchpoint(
+ system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
+ break;
+ case BreakpointType::Hardware:
default:
SendReply(GDB_STUB_REPLY_EMPTY);
- break;
+ return;
+ }
+
+ if (success) {
+ SendReply(GDB_STUB_REPLY_OK);
+ } else {
+ SendReply(GDB_STUB_REPLY_ERR);
}
}
diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h
index ec934c77e..0b0f56e4b 100644
--- a/src/core/debugger/gdbstub.h
+++ b/src/core/debugger/gdbstub.h
@@ -24,6 +24,7 @@ public:
void Connected() override;
void Stopped(Kernel::KThread* thread) override;
void ShuttingDown() override;
+ void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) override;
std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
private:
@@ -31,6 +32,8 @@ private:
void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
void HandleQuery(std::string_view command);
+ void HandleBreakpointInsert(std::string_view command);
+ void HandleBreakpointRemove(std::string_view command);
std::vector<char>::const_iterator CommandEnd() const;
std::optional<std::string> DetachCommand();
Kernel::KThread* GetThreadByID(u64 thread_id);
diff --git a/src/core/debugger/gdbstub_arch.cpp b/src/core/debugger/gdbstub_arch.cpp
index 750c353b9..4bef09bd7 100644
--- a/src/core/debugger/gdbstub_arch.cpp
+++ b/src/core/debugger/gdbstub_arch.cpp
@@ -191,8 +191,10 @@ std::string GDBStubA64::RegRead(const Kernel::KThread* thread, size_t id) const
const auto& gprs{context.cpu_registers};
const auto& fprs{context.vector_registers};
- if (id <= SP_REGISTER) {
+ if (id < SP_REGISTER) {
return ValueToHex(gprs[id]);
+ } else if (id == SP_REGISTER) {
+ return ValueToHex(context.sp);
} else if (id == PC_REGISTER) {
return ValueToHex(context.pc);
} else if (id == PSTATE_REGISTER) {
@@ -215,8 +217,10 @@ void GDBStubA64::RegWrite(Kernel::KThread* thread, size_t id, std::string_view v
auto& context{thread->GetContext64()};
- if (id <= SP_REGISTER) {
+ if (id < SP_REGISTER) {
context.cpu_registers[id] = HexToValue<u64>(value);
+ } else if (id == SP_REGISTER) {
+ context.sp = HexToValue<u64>(value);
} else if (id == PC_REGISTER) {
context.pc = HexToValue<u64>(value);
} else if (id == PSTATE_REGISTER) {
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
index 1a920b45d..7cee0c7df 100644
--- a/src/core/file_sys/errors.h
+++ b/src/core/file_sys/errors.h
@@ -1,6 +1,5 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,14 +7,14 @@
namespace FileSys {
-constexpr ResultCode ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
-constexpr ResultCode ERROR_PATH_ALREADY_EXISTS{ErrorModule::FS, 2};
-constexpr ResultCode ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
-constexpr ResultCode ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
-constexpr ResultCode ERROR_OUT_OF_BOUNDS{ErrorModule::FS, 3005};
-constexpr ResultCode ERROR_FAILED_MOUNT_ARCHIVE{ErrorModule::FS, 3223};
-constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::FS, 6001};
-constexpr ResultCode ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
-constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
+constexpr Result ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
+constexpr Result ERROR_PATH_ALREADY_EXISTS{ErrorModule::FS, 2};
+constexpr Result ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
+constexpr Result ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
+constexpr Result ERROR_OUT_OF_BOUNDS{ErrorModule::FS, 3005};
+constexpr Result ERROR_FAILED_MOUNT_ARCHIVE{ErrorModule::FS, 3223};
+constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::FS, 6001};
+constexpr Result ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
+constexpr Result ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
} // namespace FileSys
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index 4b35ca82f..5aab428bb 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -217,9 +217,7 @@ void IPSwitchCompiler::Parse() {
break;
} else if (StartsWith(line, "@nsobid-")) {
// NSO Build ID Specifier
- auto raw_build_id = line.substr(8);
- if (raw_build_id.size() != 0x40)
- raw_build_id.resize(0x40, '0');
+ const auto raw_build_id = fmt::format("{:0<64}", line.substr(8));
nso_build_id = Common::HexStringToArray<0x20>(raw_build_id);
} else if (StartsWith(line, "#")) {
// Mandatory Comment
@@ -287,7 +285,8 @@ void IPSwitchCompiler::Parse() {
std::copy(value.begin(), value.end(), std::back_inserter(replace));
} else {
// hex replacement
- const auto value = patch_line.substr(9);
+ const auto value =
+ patch_line.substr(9, patch_line.find_first_of(" /\r\n", 9) - 9);
replace = Common::HexStringToVector(value, is_little_endian);
}
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index bd525b26c..4c80e13a9 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -191,6 +191,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) const {
const auto& disabled = Settings::values.disabled_addons[title_id];
+ const auto nso_build_id = fmt::format("{:0<64}", build_id);
std::vector<VirtualFile> out;
out.reserve(patch_dirs.size());
@@ -203,21 +204,18 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
for (const auto& file : exefs_dir->GetFiles()) {
if (file->GetExtension() == "ips") {
auto name = file->GetName();
- const auto p1 = name.substr(0, name.find('.'));
- const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1);
- if (build_id == this_build_id)
+ const auto this_build_id =
+ fmt::format("{:0<64}", name.substr(0, name.find('.')));
+ if (nso_build_id == this_build_id)
out.push_back(file);
} else if (file->GetExtension() == "pchtxt") {
IPSwitchCompiler compiler{file};
if (!compiler.IsValid())
continue;
- auto this_build_id = Common::HexToString(compiler.GetBuildID());
- this_build_id =
- this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1);
-
- if (build_id == this_build_id)
+ const auto this_build_id = Common::HexToString(compiler.GetBuildID());
+ if (nso_build_id == this_build_id)
out.push_back(file);
}
}
diff --git a/src/core/frontend/applets/error.cpp b/src/core/frontend/applets/error.cpp
index f2ec4b10e..f8b961098 100644
--- a/src/core/frontend/applets/error.cpp
+++ b/src/core/frontend/applets/error.cpp
@@ -8,12 +8,12 @@ namespace Core::Frontend {
ErrorApplet::~ErrorApplet() = default;
-void DefaultErrorApplet::ShowError(ResultCode error, std::function<void()> finished) const {
+void DefaultErrorApplet::ShowError(Result error, std::function<void()> finished) const {
LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})",
error.module.Value(), error.description.Value(), error.raw);
}
-void DefaultErrorApplet::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
+void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
std::function<void()> finished) const {
LOG_CRITICAL(
Service_Fatal,
@@ -21,7 +21,7 @@ void DefaultErrorApplet::ShowErrorWithTimestamp(ResultCode error, std::chrono::s
error.module.Value(), error.description.Value(), error.raw, time.count());
}
-void DefaultErrorApplet::ShowCustomErrorText(ResultCode error, std::string main_text,
+void DefaultErrorApplet::ShowCustomErrorText(Result error, std::string main_text,
std::string detail_text,
std::function<void()> finished) const {
LOG_CRITICAL(Service_Fatal,
diff --git a/src/core/frontend/applets/error.h b/src/core/frontend/applets/error.h
index 8a1134561..f378f8805 100644
--- a/src/core/frontend/applets/error.h
+++ b/src/core/frontend/applets/error.h
@@ -14,22 +14,22 @@ class ErrorApplet {
public:
virtual ~ErrorApplet();
- virtual void ShowError(ResultCode error, std::function<void()> finished) const = 0;
+ virtual void ShowError(Result error, std::function<void()> finished) const = 0;
- virtual void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
+ virtual void ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
std::function<void()> finished) const = 0;
- virtual void ShowCustomErrorText(ResultCode error, std::string dialog_text,
+ virtual void ShowCustomErrorText(Result error, std::string dialog_text,
std::string fullscreen_text,
std::function<void()> finished) const = 0;
};
class DefaultErrorApplet final : public ErrorApplet {
public:
- void ShowError(ResultCode error, std::function<void()> finished) const override;
- void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
+ void ShowError(Result error, std::function<void()> finished) const override;
+ void ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
std::function<void()> finished) const override;
- void ShowCustomErrorText(ResultCode error, std::string main_text, std::string detail_text,
+ void ShowCustomErrorText(Result error, std::string main_text, std::string detail_text,
std::function<void()> finished) const override;
};
diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h
index a405e3c94..094d1e713 100644
--- a/src/core/frontend/applets/software_keyboard.h
+++ b/src/core/frontend/applets/software_keyboard.h
@@ -17,6 +17,8 @@ struct KeyboardInitializeParameters {
std::u16string sub_text;
std::u16string guide_text;
std::u16string initial_text;
+ char16_t left_optional_symbol_key;
+ char16_t right_optional_symbol_key;
u32 max_text_length;
u32 min_text_length;
s32 initial_cursor_position;
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index 57c6ffc43..1be2dccb0 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <mutex>
#include "core/frontend/emu_window.h"
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index b3bffecb2..ac1906d5e 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp
index d2d968a76..d08cc3315 100644
--- a/src/core/hardware_interrupt_manager.cpp
+++ b/src/core/hardware_interrupt_manager.cpp
@@ -11,11 +11,14 @@ namespace Core::Hardware {
InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {
gpu_interrupt_event = Core::Timing::CreateEvent(
- "GPUInterrupt", [this](std::uintptr_t message, std::chrono::nanoseconds) {
+ "GPUInterrupt",
+ [this](std::uintptr_t message, u64 time,
+ std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
const u32 syncpt = static_cast<u32>(message >> 32);
const u32 value = static_cast<u32>(message);
nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
+ return std::nullopt;
});
}
diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h
index aac362c51..13cbdb734 100644
--- a/src/core/hardware_properties.h
+++ b/src/core/hardware_properties.h
@@ -25,6 +25,9 @@ constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
};
+// Cortex-A57 supports 4 memory watchpoints
+constexpr u64 NUM_WATCHPOINTS = 4;
+
} // namespace Hardware
} // namespace Core
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index bd2384515..049602e7d 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "common/thread.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/input_converter.h"
@@ -84,18 +85,19 @@ void EmulatedController::ReloadFromSettings() {
motion_params[index] = Common::ParamPackage(player.motions[index]);
}
+ controller.colors_state.fullkey = {
+ .body = GetNpadColor(player.body_color_left),
+ .button = GetNpadColor(player.button_color_left),
+ };
controller.colors_state.left = {
- .body = player.body_color_left,
- .button = player.button_color_left,
+ .body = GetNpadColor(player.body_color_left),
+ .button = GetNpadColor(player.button_color_left),
};
-
- controller.colors_state.right = {
- .body = player.body_color_right,
- .button = player.button_color_right,
+ controller.colors_state.left = {
+ .body = GetNpadColor(player.body_color_right),
+ .button = GetNpadColor(player.button_color_right),
};
- controller.colors_state.fullkey = controller.colors_state.left;
-
// Other or debug controller should always be a pro controller
if (npad_id_type != NpadIdType::Other) {
SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
@@ -126,10 +128,14 @@ void EmulatedController::LoadDevices() {
battery_params[LeftIndex].Set("battery", true);
battery_params[RightIndex].Set("battery", true);
+ camera_params = Common::ParamPackage{"engine:camera,camera:1"};
+
output_params[LeftIndex] = left_joycon;
output_params[RightIndex] = right_joycon;
+ output_params[2] = camera_params;
output_params[LeftIndex].Set("output", true);
output_params[RightIndex].Set("output", true);
+ output_params[2].Set("output", true);
LoadTASParams();
@@ -146,6 +152,7 @@ void EmulatedController::LoadDevices() {
Common::Input::CreateDevice<Common::Input::InputDevice>);
std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
Common::Input::CreateDevice<Common::Input::InputDevice>);
+ camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params);
std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
Common::Input::CreateDevice<Common::Input::OutputDevice>);
@@ -267,6 +274,14 @@ void EmulatedController::ReloadInput() {
motion_devices[index]->ForceUpdate();
}
+ if (camera_devices) {
+ camera_devices->SetCallback({
+ .on_change =
+ [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); },
+ });
+ camera_devices->ForceUpdate();
+ }
+
// Use a common UUID for TAS
static constexpr Common::UUID TAS_UUID = Common::UUID{
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
@@ -851,6 +866,25 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
TriggerOnChange(ControllerTriggerType::Battery, true);
}
+void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) {
+ std::unique_lock lock{mutex};
+ controller.camera_values = TransformToCamera(callback);
+
+ if (is_configuring) {
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::IrSensor, false);
+ return;
+ }
+
+ controller.camera_state.sample++;
+ controller.camera_state.format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format);
+ controller.camera_state.data = controller.camera_values.data;
+
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::IrSensor, true);
+}
+
bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
if (device_index >= output_devices.size()) {
return false;
@@ -917,6 +951,9 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
// Send a slight vibration to test for rumble support
output_devices[device_index]->SetVibration(test_vibration);
+ // Wait for about 15ms to ensure the controller is ready for the stop command
+ std::this_thread::sleep_for(std::chrono::milliseconds(15));
+
// Stop any vibration and return the result
return output_devices[device_index]->SetVibration(zero_vibration) ==
Common::Input::VibrationError::None;
@@ -928,6 +965,23 @@ bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode)
return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None;
}
+bool EmulatedController::SetCameraFormat(
+ Core::IrSensor::ImageTransferProcessorFormat camera_format) {
+ LOG_INFO(Service_HID, "Set camera format {}", camera_format);
+
+ auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
+ auto& camera_output_device = output_devices[2];
+
+ if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
+ camera_format)) == Common::Input::CameraError::None) {
+ return true;
+ }
+
+ // Fallback to Qt camera if native device doesn't have support
+ return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
+ camera_format)) == Common::Input::CameraError::None;
+}
+
void EmulatedController::SetLedPattern() {
for (auto& device : output_devices) {
if (!device) {
@@ -1163,6 +1217,11 @@ BatteryValues EmulatedController::GetBatteryValues() const {
return controller.battery_values;
}
+CameraValues EmulatedController::GetCameraValues() const {
+ std::scoped_lock lock{mutex};
+ return controller.camera_values;
+}
+
HomeButtonState EmulatedController::GetHomeButtons() const {
std::scoped_lock lock{mutex};
if (is_configuring) {
@@ -1251,6 +1310,20 @@ BatteryLevelState EmulatedController::GetBattery() const {
return controller.battery_state;
}
+const CameraState& EmulatedController::GetCamera() const {
+ std::scoped_lock lock{mutex};
+ return controller.camera_state;
+}
+
+NpadColor EmulatedController::GetNpadColor(u32 color) {
+ return {
+ .r = static_cast<u8>((color >> 16) & 0xFF),
+ .g = static_cast<u8>((color >> 8) & 0xFF),
+ .b = static_cast<u8>(color & 0xFF),
+ .a = 0xff,
+ };
+}
+
void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index 3f02ed3c0..cbd7c26d3 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -15,10 +15,12 @@
#include "common/settings.h"
#include "common/vector_math.h"
#include "core/hid/hid_types.h"
+#include "core/hid/irs_types.h"
#include "core/hid/motion_input.h"
namespace Core::HID {
const std::size_t max_emulated_controllers = 2;
+const std::size_t output_devices = 3;
struct ControllerMotionInfo {
Common::Input::MotionStatus raw_status{};
MotionInput emulated{};
@@ -34,15 +36,16 @@ using TriggerDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
using BatteryDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
-using OutputDevices =
- std::array<std::unique_ptr<Common::Input::OutputDevice>, max_emulated_controllers>;
+using CameraDevices = std::unique_ptr<Common::Input::InputDevice>;
+using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices>;
using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
-using OutputParams = std::array<Common::ParamPackage, max_emulated_controllers>;
+using CameraParams = Common::ParamPackage;
+using OutputParams = std::array<Common::ParamPackage, output_devices>;
using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
@@ -51,6 +54,7 @@ using TriggerValues =
using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
+using CameraValues = Common::Input::CameraStatus;
using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
struct AnalogSticks {
@@ -70,6 +74,12 @@ struct BatteryLevelState {
NpadPowerInfo right{};
};
+struct CameraState {
+ Core::IrSensor::ImageTransferProcessorFormat format{};
+ std::vector<u8> data{};
+ std::size_t sample{};
+};
+
struct ControllerMotion {
Common::Vec3f accel{};
Common::Vec3f gyro{};
@@ -96,6 +106,7 @@ struct ControllerStatus {
ColorValues color_values{};
BatteryValues battery_values{};
VibrationValues vibration_values{};
+ CameraValues camera_values{};
// Data for HID serices
HomeButtonState home_button_state{};
@@ -107,6 +118,7 @@ struct ControllerStatus {
NpadGcTriggerState gc_trigger_state{};
ControllerColors colors_state{};
BatteryLevelState battery_state{};
+ CameraState camera_state{};
};
enum class ControllerTriggerType {
@@ -117,6 +129,7 @@ enum class ControllerTriggerType {
Color,
Battery,
Vibration,
+ IrSensor,
Connected,
Disconnected,
Type,
@@ -269,6 +282,9 @@ public:
/// Returns the latest battery status from the controller with parameters
BatteryValues GetBatteryValues() const;
+ /// Returns the latest camera status from the controller with parameters
+ CameraValues GetCameraValues() const;
+
/// Returns the latest status of button input for the hid::HomeButton service
HomeButtonState GetHomeButtons() const;
@@ -296,6 +312,9 @@ public:
/// Returns the latest battery status from the controller
BatteryLevelState GetBattery() const;
+ /// Returns the latest camera status from the controller
+ const CameraState& GetCamera() const;
+
/**
* Sends a specific vibration to the output device
* @return true if vibration had no errors
@@ -315,6 +334,13 @@ public:
*/
bool SetPollingMode(Common::Input::PollingMode polling_mode);
+ /**
+ * Sets the desired camera format to be polled from a controller
+ * @param camera_format size of each frame
+ * @return true if SetCameraFormat was successfull
+ */
+ bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format);
+
/// Returns the led pattern corresponding to this emulated controller
LedPattern GetLedPattern() const;
@@ -393,6 +419,19 @@ private:
void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
+ * Updates the camera status of the controller
+ * @param callback A CallbackStatus containing the camera status
+ */
+ void SetCamera(const Common::Input::CallbackStatus& callback);
+
+ /**
+ * Converts a color format from bgra to rgba
+ * @param color in bgra format
+ * @return NpadColor in rgba format
+ */
+ NpadColor GetNpadColor(u32 color);
+
+ /**
* Triggers a callback that something has changed on the controller status
* @param type Input type of the event to trigger
* @param is_service_update indicates if this event should only be sent to HID services
@@ -417,6 +456,7 @@ private:
ControllerMotionParams motion_params;
TriggerParams trigger_params;
BatteryParams battery_params;
+ CameraParams camera_params;
OutputParams output_params;
ButtonDevices button_devices;
@@ -424,6 +464,7 @@ private:
ControllerMotionDevices motion_devices;
TriggerDevices trigger_devices;
BatteryDevices battery_devices;
+ CameraDevices camera_devices;
OutputDevices output_devices;
// TAS related variables
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index 9f76f9bcb..e3b1cfbc6 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -272,6 +272,7 @@ enum class VibrationDeviceType : u32 {
Unknown = 0,
LinearResonantActuator = 1,
GcErm = 2,
+ N64 = 3,
};
// This is nn::hid::VibrationGcErmCommand
@@ -326,10 +327,18 @@ struct TouchState {
};
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
+struct NpadColor {
+ u8 r{};
+ u8 g{};
+ u8 b{};
+ u8 a{};
+};
+static_assert(sizeof(NpadColor) == 4, "NpadColor is an invalid size");
+
// This is nn::hid::NpadControllerColor
struct NpadControllerColor {
- u32 body{};
- u32 button{};
+ NpadColor body{};
+ NpadColor button{};
};
static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size");
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index 18d9f042d..68d143a01 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -270,6 +270,20 @@ Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatu
return status;
}
+Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback) {
+ Common::Input::CameraStatus camera{};
+ switch (callback.type) {
+ case Common::Input::InputType::IrSensor:
+ camera = callback.camera_status;
+ break;
+ default:
+ LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type);
+ break;
+ }
+
+ return camera;
+}
+
void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
const auto& properties = analog.properties;
float& raw_value = analog.raw_value;
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h
index 2be36889f..143c50cc0 100644
--- a/src/core/hid/input_converter.h
+++ b/src/core/hid/input_converter.h
@@ -77,6 +77,14 @@ Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackSta
Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);
/**
+ * Converts raw input data into a valid camera status.
+ *
+ * @param callback Supported callbacks: Camera.
+ * @return A valid CameraObject object.
+ */
+Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback);
+
+/**
* Converts raw analog data into a valid analog value
* @param analog An analog object containing raw data and properties
* @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
diff --git a/src/core/hid/irs_types.h b/src/core/hid/irs_types.h
new file mode 100644
index 000000000..88c5b016d
--- /dev/null
+++ b/src/core/hid/irs_types.h
@@ -0,0 +1,301 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "core/hid/hid_types.h"
+
+namespace Core::IrSensor {
+
+// This is nn::irsensor::CameraAmbientNoiseLevel
+enum class CameraAmbientNoiseLevel : u32 {
+ Low,
+ Medium,
+ High,
+ Unkown3, // This level can't be reached
+};
+
+// This is nn::irsensor::CameraLightTarget
+enum class CameraLightTarget : u32 {
+ AllLeds,
+ BrightLeds,
+ DimLeds,
+ None,
+};
+
+// This is nn::irsensor::PackedCameraLightTarget
+enum class PackedCameraLightTarget : u8 {
+ AllLeds,
+ BrightLeds,
+ DimLeds,
+ None,
+};
+
+// This is nn::irsensor::AdaptiveClusteringMode
+enum class AdaptiveClusteringMode : u32 {
+ StaticFov,
+ DynamicFov,
+};
+
+// This is nn::irsensor::AdaptiveClusteringTargetDistance
+enum class AdaptiveClusteringTargetDistance : u32 {
+ Near,
+ Middle,
+ Far,
+};
+
+// This is nn::irsensor::ImageTransferProcessorFormat
+enum class ImageTransferProcessorFormat : u32 {
+ Size320x240,
+ Size160x120,
+ Size80x60,
+ Size40x30,
+ Size20x15,
+};
+
+// This is nn::irsensor::PackedImageTransferProcessorFormat
+enum class PackedImageTransferProcessorFormat : u8 {
+ Size320x240,
+ Size160x120,
+ Size80x60,
+ Size40x30,
+ Size20x15,
+};
+
+// This is nn::irsensor::IrCameraStatus
+enum class IrCameraStatus : u32 {
+ Available,
+ Unsupported,
+ Unconnected,
+};
+
+// This is nn::irsensor::IrCameraInternalStatus
+enum class IrCameraInternalStatus : u32 {
+ Stopped,
+ FirmwareUpdateNeeded,
+ Unkown2,
+ Unkown3,
+ Unkown4,
+ FirmwareVersionRequested,
+ FirmwareVersionIsInvalid,
+ Ready,
+ Setting,
+};
+
+// This is nn::irsensor::detail::StatusManager::IrSensorMode
+enum class IrSensorMode : u64 {
+ None,
+ MomentProcessor,
+ ClusteringProcessor,
+ ImageTransferProcessor,
+ PointingProcessorMarker,
+ TeraPluginProcessor,
+ IrLedProcessor,
+};
+
+// This is nn::irsensor::ImageProcessorStatus
+enum ImageProcessorStatus : u32 {
+ Stopped,
+ Running,
+};
+
+// This is nn::irsensor::HandAnalysisMode
+enum class HandAnalysisMode : u32 {
+ None,
+ Silhouette,
+ Image,
+ SilhoueteAndImage,
+ SilhuetteOnly,
+};
+
+// This is nn::irsensor::IrSensorFunctionLevel
+enum class IrSensorFunctionLevel : u8 {
+ unknown0,
+ unknown1,
+ unknown2,
+ unknown3,
+ unknown4,
+};
+
+// This is nn::irsensor::MomentProcessorPreprocess
+enum class MomentProcessorPreprocess : u32 {
+ Unkown0,
+ Unkown1,
+};
+
+// This is nn::irsensor::PackedMomentProcessorPreprocess
+enum class PackedMomentProcessorPreprocess : u8 {
+ Unkown0,
+ Unkown1,
+};
+
+// This is nn::irsensor::PointingStatus
+enum class PointingStatus : u32 {
+ Unkown0,
+ Unkown1,
+};
+
+struct IrsRect {
+ s16 x;
+ s16 y;
+ s16 width;
+ s16 height;
+};
+
+struct IrsCentroid {
+ f32 x;
+ f32 y;
+};
+
+struct CameraConfig {
+ u64 exposure_time;
+ CameraLightTarget light_target;
+ u32 gain;
+ bool is_negative_used;
+ INSERT_PADDING_BYTES(7);
+};
+static_assert(sizeof(CameraConfig) == 0x18, "CameraConfig is an invalid size");
+
+struct PackedCameraConfig {
+ u64 exposure_time;
+ PackedCameraLightTarget light_target;
+ u8 gain;
+ bool is_negative_used;
+ INSERT_PADDING_BYTES(5);
+};
+static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an invalid size");
+
+// This is nn::irsensor::IrCameraHandle
+struct IrCameraHandle {
+ u8 npad_id{};
+ Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
+ INSERT_PADDING_BYTES(2);
+};
+static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
+
+// This is nn::irsensor::PackedMcuVersion
+struct PackedMcuVersion {
+ u16 major;
+ u16 minor;
+};
+static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
+
+// This is nn::irsensor::PackedMomentProcessorConfig
+struct PackedMomentProcessorConfig {
+ PackedCameraConfig camera_config;
+ IrsRect window_of_interest;
+ PackedMcuVersion required_mcu_version;
+ PackedMomentProcessorPreprocess preprocess;
+ u8 preprocess_intensity_threshold;
+ INSERT_PADDING_BYTES(2);
+};
+static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
+ "PackedMomentProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedClusteringProcessorConfig
+struct PackedClusteringProcessorConfig {
+ PackedCameraConfig camera_config;
+ IrsRect window_of_interest;
+ PackedMcuVersion required_mcu_version;
+ u32 pixel_count_min;
+ u32 pixel_count_max;
+ u8 object_intensity_min;
+ bool is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(2);
+};
+static_assert(sizeof(PackedClusteringProcessorConfig) == 0x28,
+ "PackedClusteringProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedImageTransferProcessorConfig
+struct PackedImageTransferProcessorConfig {
+ PackedCameraConfig camera_config;
+ PackedMcuVersion required_mcu_version;
+ PackedImageTransferProcessorFormat format;
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
+ "PackedImageTransferProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedTeraPluginProcessorConfig
+struct PackedTeraPluginProcessorConfig {
+ PackedMcuVersion required_mcu_version;
+ u8 mode;
+ u8 unknown_1;
+ u8 unknown_2;
+ u8 unknown_3;
+};
+static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
+ "PackedTeraPluginProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedPointingProcessorConfig
+struct PackedPointingProcessorConfig {
+ IrsRect window_of_interest;
+ PackedMcuVersion required_mcu_version;
+};
+static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
+ "PackedPointingProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedFunctionLevel
+struct PackedFunctionLevel {
+ IrSensorFunctionLevel function_level;
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
+
+// This is nn::irsensor::PackedImageTransferProcessorExConfig
+struct PackedImageTransferProcessorExConfig {
+ PackedCameraConfig camera_config;
+ PackedMcuVersion required_mcu_version;
+ PackedImageTransferProcessorFormat origin_format;
+ PackedImageTransferProcessorFormat trimming_format;
+ u16 trimming_start_x;
+ u16 trimming_start_y;
+ bool is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(5);
+};
+static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
+ "PackedImageTransferProcessorExConfig is an invalid size");
+
+// This is nn::irsensor::PackedIrLedProcessorConfig
+struct PackedIrLedProcessorConfig {
+ PackedMcuVersion required_mcu_version;
+ u8 light_target;
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
+ "PackedIrLedProcessorConfig is an invalid size");
+
+// This is nn::irsensor::HandAnalysisConfig
+struct HandAnalysisConfig {
+ HandAnalysisMode mode;
+};
+static_assert(sizeof(HandAnalysisConfig) == 0x4, "HandAnalysisConfig is an invalid size");
+
+// This is nn::irsensor::detail::ProcessorState contents are different for each processor
+struct ProcessorState {
+ std::array<u8, 0xE20> processor_raw_data{};
+};
+static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid size");
+
+// This is nn::irsensor::detail::DeviceFormat
+struct DeviceFormat {
+ Core::IrSensor::IrCameraStatus camera_status{Core::IrSensor::IrCameraStatus::Unconnected};
+ Core::IrSensor::IrCameraInternalStatus camera_internal_status{
+ Core::IrSensor::IrCameraInternalStatus::Ready};
+ Core::IrSensor::IrSensorMode mode{Core::IrSensor::IrSensorMode::None};
+ ProcessorState state{};
+};
+static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size");
+
+// This is nn::irsensor::ImageTransferProcessorState
+struct ImageTransferProcessorState {
+ u64 sampling_number;
+ Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
+ INSERT_PADDING_BYTES(4);
+};
+static_assert(sizeof(ImageTransferProcessorState) == 0x10,
+ "ImageTransferProcessorState is an invalid size");
+
+} // namespace Core::IrSensor
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 602e12606..416da15ec 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -1,6 +1,5 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 3c4e45fcd..d631c0357 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -1,6 +1,5 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -19,7 +18,7 @@
namespace IPC {
-constexpr ResultCode ERR_REMOTE_PROCESS_DEAD{ErrorModule::HIPC, 301};
+constexpr Result ERR_REMOTE_PROCESS_DEAD{ErrorModule::HIPC, 301};
class RequestHelperBase {
protected:
@@ -176,7 +175,7 @@ public:
void PushImpl(float value);
void PushImpl(double value);
void PushImpl(bool value);
- void PushImpl(ResultCode value);
+ void PushImpl(Result value);
template <typename T>
void Push(T value) {
@@ -251,7 +250,7 @@ void ResponseBuilder::PushRaw(const T& value) {
index += (sizeof(T) + 3) / 4; // round up to word length
}
-inline void ResponseBuilder::PushImpl(ResultCode value) {
+inline void ResponseBuilder::PushImpl(Result value) {
// Result codes are actually 64-bit in the IPC buffer, but only the high part is discarded.
Push(value.raw);
Push<u32>(0);
@@ -481,8 +480,8 @@ inline bool RequestParser::Pop() {
}
template <>
-inline ResultCode RequestParser::Pop() {
- return ResultCode{Pop<u32>()};
+inline Result RequestParser::Pop() {
+ return Result{Pop<u32>()};
}
template <typename T>
diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp
index 164436b26..65576b8c4 100644
--- a/src/core/hle/kernel/global_scheduler_context.cpp
+++ b/src/core/hle/kernel/global_scheduler_context.cpp
@@ -41,12 +41,7 @@ void GlobalSchedulerContext::PreemptThreads() {
ASSERT(IsLocked());
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
const u32 priority = preemption_priorities[core_id];
- kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority);
-
- // Signal an interrupt occurred. For core 3, this is a certainty, as preemption will result
- // in the rotator thread being scheduled. For cores 0-2, this is to simulate or system
- // interrupts that may have occurred.
- kernel.PhysicalCore(core_id).Interrupt();
+ KScheduler::RotateScheduledQueue(kernel, core_id, priority);
}
}
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 4650d25b0..5b3feec66 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -188,8 +188,8 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
}
-ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table,
- u32_le* src_cmdbuf) {
+Result HLERequestContext::PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table,
+ u32_le* src_cmdbuf) {
ParseCommandBuffer(handle_table, src_cmdbuf, true);
if (command_header->IsCloseCommand()) {
@@ -202,7 +202,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const KHandleTab
return ResultSuccess;
}
-ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_thread) {
+Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_thread) {
auto current_offset = handles_offset;
auto& owner_process = *requesting_thread.GetOwnerProcess();
auto& handle_table = owner_process.GetHandleTable();
@@ -287,18 +287,52 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
BufferDescriptorB().size() > buffer_index &&
BufferDescriptorB()[buffer_index].Size() >= size,
{ return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size);
- memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
+ WriteBufferB(buffer, size, buffer_index);
} else {
ASSERT_OR_EXECUTE_MSG(
BufferDescriptorC().size() > buffer_index &&
BufferDescriptorC()[buffer_index].Size() >= size,
{ return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size);
- memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
+ WriteBufferC(buffer, size, buffer_index);
}
return size;
}
+std::size_t HLERequestContext::WriteBufferB(const void* buffer, std::size_t size,
+ std::size_t buffer_index) const {
+ if (buffer_index >= BufferDescriptorB().size() || size == 0) {
+ return 0;
+ }
+
+ const auto buffer_size{BufferDescriptorB()[buffer_index].Size()};
+ if (size > buffer_size) {
+ LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
+ buffer_size);
+ size = buffer_size; // TODO(bunnei): This needs to be HW tested
+ }
+
+ memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
+ return size;
+}
+
+std::size_t HLERequestContext::WriteBufferC(const void* buffer, std::size_t size,
+ std::size_t buffer_index) const {
+ if (buffer_index >= BufferDescriptorC().size() || size == 0) {
+ return 0;
+ }
+
+ const auto buffer_size{BufferDescriptorC()[buffer_index].Size()};
+ if (size > buffer_size) {
+ LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
+ buffer_size);
+ size = buffer_size; // TODO(bunnei): This needs to be HW tested
+ }
+
+ memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
+ return size;
+}
+
std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const {
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
BufferDescriptorA()[buffer_index].Size()};
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 0ddc8df9e..99265ce90 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -18,7 +18,7 @@
#include "core/hle/ipc.h"
#include "core/hle/kernel/svc_common.h"
-union ResultCode;
+union Result;
namespace Core::Memory {
class Memory;
@@ -71,10 +71,10 @@ public:
* it should be used to differentiate which client (As in ClientSession) we're answering to.
* TODO(Subv): Use a wrapper structure to hold all the information relevant to
* this request (ServerSession, Originator thread, Translated command buffer, etc).
- * @returns ResultCode the result code of the translate operation.
+ * @returns Result the result code of the translate operation.
*/
- virtual ResultCode HandleSyncRequest(Kernel::KServerSession& session,
- Kernel::HLERequestContext& context) = 0;
+ virtual Result HandleSyncRequest(Kernel::KServerSession& session,
+ Kernel::HLERequestContext& context) = 0;
/**
* Signals that a client has just connected to this HLE handler and keeps the
@@ -212,11 +212,10 @@ public:
}
/// Populates this context with data from the requesting process/thread.
- ResultCode PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table,
- u32_le* src_cmdbuf);
+ Result PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table, u32_le* src_cmdbuf);
/// Writes data from this context back to the requesting process/thread.
- ResultCode WriteToOutgoingCommandBuffer(KThread& requesting_thread);
+ Result WriteToOutgoingCommandBuffer(KThread& requesting_thread);
u32_le GetHipcCommand() const {
return command;
@@ -278,6 +277,14 @@ public:
std::size_t WriteBuffer(const void* buffer, std::size_t size,
std::size_t buffer_index = 0) const;
+ /// Helper function to write buffer B
+ std::size_t WriteBufferB(const void* buffer, std::size_t size,
+ std::size_t buffer_index = 0) const;
+
+ /// Helper function to write buffer C
+ std::size_t WriteBufferC(const void* buffer, std::size_t size,
+ std::size_t buffer_index = 0) const;
+
/* Helper function to write a buffer using the appropriate buffer descriptor
*
* @tparam T an arbitrary container that satisfies the
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
index 04cf86d52..f85b11557 100644
--- a/src/core/hle/kernel/k_address_arbiter.cpp
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -90,8 +90,7 @@ public:
explicit ThreadQueueImplForKAddressArbiter(KernelCore& kernel_, KAddressArbiter::ThreadTree* t)
: KThreadQueue(kernel_), m_tree(t) {}
- void CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task) override {
+ void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// If the thread is waiting on an address arbiter, remove it from the tree.
if (waiting_thread->IsWaitingForAddressArbiter()) {
m_tree->erase(m_tree->iterator_to(*waiting_thread));
@@ -108,7 +107,7 @@ private:
} // namespace
-ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
+Result KAddressArbiter::Signal(VAddr addr, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
@@ -131,7 +130,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
return ResultSuccess;
}
-ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) {
+Result KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
@@ -164,7 +163,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
return ResultSuccess;
}
-ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) {
+Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
@@ -232,9 +231,9 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
return ResultSuccess;
}
-ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
+Result KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
// Prepare to wait.
- KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
+ KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{
@@ -285,9 +284,9 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
return cur_thread->GetWaitResult();
}
-ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
+Result KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Prepare to wait.
- KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
+ KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{
diff --git a/src/core/hle/kernel/k_address_arbiter.h b/src/core/hle/kernel/k_address_arbiter.h
index 5fa19d386..e4085ae22 100644
--- a/src/core/hle/kernel/k_address_arbiter.h
+++ b/src/core/hle/kernel/k_address_arbiter.h
@@ -8,7 +8,7 @@
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/svc_types.h"
-union ResultCode;
+union Result;
namespace Core {
class System;
@@ -25,8 +25,7 @@ public:
explicit KAddressArbiter(Core::System& system_);
~KAddressArbiter();
- [[nodiscard]] ResultCode SignalToAddress(VAddr addr, Svc::SignalType type, s32 value,
- s32 count) {
+ [[nodiscard]] Result SignalToAddress(VAddr addr, Svc::SignalType type, s32 value, s32 count) {
switch (type) {
case Svc::SignalType::Signal:
return Signal(addr, count);
@@ -39,8 +38,8 @@ public:
return ResultUnknown;
}
- [[nodiscard]] ResultCode WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value,
- s64 timeout) {
+ [[nodiscard]] Result WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value,
+ s64 timeout) {
switch (type) {
case Svc::ArbitrationType::WaitIfLessThan:
return WaitIfLessThan(addr, value, false, timeout);
@@ -54,11 +53,11 @@ public:
}
private:
- [[nodiscard]] ResultCode Signal(VAddr addr, s32 count);
- [[nodiscard]] ResultCode SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count);
- [[nodiscard]] ResultCode SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count);
- [[nodiscard]] ResultCode WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout);
- [[nodiscard]] ResultCode WaitIfEqual(VAddr addr, s32 value, s64 timeout);
+ [[nodiscard]] Result Signal(VAddr addr, s32 count);
+ [[nodiscard]] Result SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count);
+ [[nodiscard]] Result SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count);
+ [[nodiscard]] Result WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout);
+ [[nodiscard]] Result WaitIfEqual(VAddr addr, s32 value, s64 timeout);
ThreadTree thread_tree;
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp
index ef168fe87..3cb22ff4d 100644
--- a/src/core/hle/kernel/k_client_port.cpp
+++ b/src/core/hle/kernel/k_client_port.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2021 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/scope_exit.h"
#include "core/hle/kernel/hle_ipc.h"
@@ -59,8 +58,8 @@ bool KClientPort::IsSignaled() const {
return num_sessions < max_sessions;
}
-ResultCode KClientPort::CreateSession(KClientSession** out,
- std::shared_ptr<SessionRequestManager> session_manager) {
+Result KClientPort::CreateSession(KClientSession** out,
+ std::shared_ptr<SessionRequestManager> session_manager) {
// Reserve a new session from the resource limit.
KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
LimitableResource::Sessions);
diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h
index 54bb05e20..e17eff28f 100644
--- a/src/core/hle/kernel/k_client_port.h
+++ b/src/core/hle/kernel/k_client_port.h
@@ -1,6 +1,5 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -53,8 +52,8 @@ public:
void Destroy() override;
bool IsSignaled() const override;
- ResultCode CreateSession(KClientSession** out,
- std::shared_ptr<SessionRequestManager> session_manager = nullptr);
+ Result CreateSession(KClientSession** out,
+ std::shared_ptr<SessionRequestManager> session_manager = nullptr);
private:
std::atomic<s32> num_sessions{};
diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp
index 731af079c..b2a887b14 100644
--- a/src/core/hle/kernel/k_client_session.cpp
+++ b/src/core/hle/kernel/k_client_session.cpp
@@ -21,8 +21,8 @@ void KClientSession::Destroy() {
void KClientSession::OnServerClosed() {}
-ResultCode KClientSession::SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
- Core::Timing::CoreTiming& core_timing) {
+Result KClientSession::SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
+ Core::Timing::CoreTiming& core_timing) {
// Signal the server session that new data is available
return parent->GetServerSession().HandleSyncRequest(thread, memory, core_timing);
}
diff --git a/src/core/hle/kernel/k_client_session.h b/src/core/hle/kernel/k_client_session.h
index 7a7ec8450..0c750d756 100644
--- a/src/core/hle/kernel/k_client_session.h
+++ b/src/core/hle/kernel/k_client_session.h
@@ -9,7 +9,7 @@
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
-union ResultCode;
+union Result;
namespace Core::Memory {
class Memory;
@@ -46,8 +46,8 @@ public:
return parent;
}
- ResultCode SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
- Core::Timing::CoreTiming& core_timing);
+ Result SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
+ Core::Timing::CoreTiming& core_timing);
void OnServerClosed();
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index 4ae40ec8e..da57ceb21 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -7,7 +7,7 @@
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
-#include "core/hle/kernel/k_page_linked_list.h"
+#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/slab_helpers.h"
@@ -19,7 +19,7 @@ namespace Kernel {
KCodeMemory::KCodeMemory(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, m_lock(kernel_) {}
-ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) {
+Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) {
// Set members.
m_owner = kernel.CurrentProcess();
@@ -62,7 +62,7 @@ void KCodeMemory::Finalize() {
m_owner->Close();
}
-ResultCode KCodeMemory::Map(VAddr address, size_t size) {
+Result KCodeMemory::Map(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
@@ -82,7 +82,7 @@ ResultCode KCodeMemory::Map(VAddr address, size_t size) {
return ResultSuccess;
}
-ResultCode KCodeMemory::Unmap(VAddr address, size_t size) {
+Result KCodeMemory::Unmap(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
@@ -99,7 +99,7 @@ ResultCode KCodeMemory::Unmap(VAddr address, size_t size) {
return ResultSuccess;
}
-ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) {
+Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
@@ -133,7 +133,7 @@ ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermis
return ResultSuccess;
}
-ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
+Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h
index ab06b6f29..2e7e1436a 100644
--- a/src/core/hle/kernel/k_code_memory.h
+++ b/src/core/hle/kernel/k_code_memory.h
@@ -7,7 +7,7 @@
#include "core/device_memory.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_light_lock.h"
-#include "core/hle/kernel/k_page_linked_list.h"
+#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/kernel/svc_types.h"
@@ -29,20 +29,20 @@ class KCodeMemory final
public:
explicit KCodeMemory(KernelCore& kernel_);
- ResultCode Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
- void Finalize();
+ Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
+ void Finalize() override;
- ResultCode Map(VAddr address, size_t size);
- ResultCode Unmap(VAddr address, size_t size);
- ResultCode MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
- ResultCode UnmapFromOwner(VAddr address, size_t size);
+ Result Map(VAddr address, size_t size);
+ Result Unmap(VAddr address, size_t size);
+ Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
+ Result UnmapFromOwner(VAddr address, size_t size);
- bool IsInitialized() const {
+ bool IsInitialized() const override {
return m_is_initialized;
}
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
- KProcess* GetOwner() const {
+ KProcess* GetOwner() const override {
return m_owner;
}
VAddr GetSourceAddress() const {
@@ -53,7 +53,7 @@ public:
}
private:
- KPageLinkedList m_page_group{};
+ KPageGroup m_page_group{};
KProcess* m_owner{};
VAddr m_address{};
KLightLock m_lock;
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index 43bcd253d..124149697 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -61,8 +61,7 @@ public:
explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel_)
: KThreadQueue(kernel_) {}
- void CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task) override {
+ void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread);
@@ -80,8 +79,7 @@ public:
KernelCore& kernel_, KConditionVariable::ThreadTree* t)
: KThreadQueue(kernel_), m_tree(t) {}
- void CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task) override {
+ void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(waiting_thread);
@@ -105,8 +103,8 @@ KConditionVariable::KConditionVariable(Core::System& system_)
KConditionVariable::~KConditionVariable() = default;
-ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
- KThread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread();
+Result KConditionVariable::SignalToAddress(VAddr addr) {
+ KThread* owner_thread = GetCurrentThreadPointer(kernel);
// Signal the address.
{
@@ -126,7 +124,7 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
}
// Write the value to userspace.
- ResultCode result{ResultSuccess};
+ Result result{ResultSuccess};
if (WriteToUser(system, addr, std::addressof(next_value))) [[likely]] {
result = ResultSuccess;
} else {
@@ -146,8 +144,8 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
}
}
-ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
- KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
+Result KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
+ KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
// Wait for the address.
@@ -261,7 +259,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
}
}
-ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
+Result KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(
diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h
index 7bc749d98..fad4ed011 100644
--- a/src/core/hle/kernel/k_condition_variable.h
+++ b/src/core/hle/kernel/k_condition_variable.h
@@ -25,12 +25,12 @@ public:
~KConditionVariable();
// Arbitration
- [[nodiscard]] ResultCode SignalToAddress(VAddr addr);
- [[nodiscard]] ResultCode WaitForAddress(Handle handle, VAddr addr, u32 value);
+ [[nodiscard]] Result SignalToAddress(VAddr addr);
+ [[nodiscard]] Result WaitForAddress(Handle handle, VAddr addr, u32 value);
// Condition variable
void Signal(u64 cv_key, s32 count);
- [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout);
+ [[nodiscard]] Result Wait(VAddr addr, u64 key, u32 value, s64 timeout);
private:
void SignalImpl(KThread* thread);
diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp
index c453927ad..e830ca46e 100644
--- a/src/core/hle/kernel/k_handle_table.cpp
+++ b/src/core/hle/kernel/k_handle_table.cpp
@@ -8,7 +8,7 @@ namespace Kernel {
KHandleTable::KHandleTable(KernelCore& kernel_) : kernel{kernel_} {}
KHandleTable::~KHandleTable() = default;
-ResultCode KHandleTable::Finalize() {
+Result KHandleTable::Finalize() {
// Get the table and clear our record of it.
u16 saved_table_size = 0;
{
@@ -62,7 +62,7 @@ bool KHandleTable::Remove(Handle handle) {
return true;
}
-ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
+Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
@@ -85,7 +85,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
return ResultSuccess;
}
-ResultCode KHandleTable::Reserve(Handle* out_handle) {
+Result KHandleTable::Reserve(Handle* out_handle) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h
index befdb2ec9..0864a737c 100644
--- a/src/core/hle/kernel/k_handle_table.h
+++ b/src/core/hle/kernel/k_handle_table.h
@@ -30,7 +30,7 @@ public:
explicit KHandleTable(KernelCore& kernel_);
~KHandleTable();
- ResultCode Initialize(s32 size) {
+ Result Initialize(s32 size) {
R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
// Initialize all fields.
@@ -60,7 +60,7 @@ public:
return m_max_count;
}
- ResultCode Finalize();
+ Result Finalize();
bool Remove(Handle handle);
template <typename T = KAutoObject>
@@ -100,10 +100,10 @@ public:
return this->template GetObjectWithoutPseudoHandle<T>(handle);
}
- ResultCode Reserve(Handle* out_handle);
+ Result Reserve(Handle* out_handle);
void Unreserve(Handle handle);
- ResultCode Add(Handle* out_handle, KAutoObject* obj);
+ Result Add(Handle* out_handle, KAutoObject* obj);
void Register(Handle handle, KAutoObject* obj);
template <typename T>
diff --git a/src/core/hle/kernel/k_interrupt_manager.cpp b/src/core/hle/kernel/k_interrupt_manager.cpp
index cf9ed80d0..1b577a5b3 100644
--- a/src/core/hle/kernel/k_interrupt_manager.cpp
+++ b/src/core/hle/kernel/k_interrupt_manager.cpp
@@ -6,6 +6,7 @@
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/physical_core.h"
namespace Kernel::KInterruptManager {
@@ -15,8 +16,10 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
return;
}
- auto& scheduler = kernel.Scheduler(core_id);
- auto& current_thread = *scheduler.GetCurrentThread();
+ // Acknowledge the interrupt.
+ kernel.PhysicalCore(core_id).ClearInterrupt();
+
+ auto& current_thread = GetCurrentThread(kernel);
// If the user disable count is set, we may need to pin the current thread.
if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
@@ -26,8 +29,11 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
process->PinCurrentThread(core_id);
// Set the interrupt flag for the thread.
- scheduler.GetCurrentThread()->SetInterruptFlag();
+ GetCurrentThread(kernel).SetInterruptFlag();
}
+
+ // Request interrupt scheduling.
+ kernel.CurrentScheduler()->RequestScheduleOnInterrupt();
}
} // namespace Kernel::KInterruptManager
diff --git a/src/core/hle/kernel/k_light_condition_variable.cpp b/src/core/hle/kernel/k_light_condition_variable.cpp
index a40f35f45..cade99cfd 100644
--- a/src/core/hle/kernel/k_light_condition_variable.cpp
+++ b/src/core/hle/kernel/k_light_condition_variable.cpp
@@ -17,8 +17,7 @@ public:
bool term)
: KThreadQueue(kernel_), m_wait_list(wl), m_allow_terminating_thread(term) {}
- void CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task) override {
+ void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Only process waits if we're allowed to.
if (ResultTerminationRequested == wait_result && m_allow_terminating_thread) {
return;
diff --git a/src/core/hle/kernel/k_light_lock.cpp b/src/core/hle/kernel/k_light_lock.cpp
index 0225734b4..43185320d 100644
--- a/src/core/hle/kernel/k_light_lock.cpp
+++ b/src/core/hle/kernel/k_light_lock.cpp
@@ -15,8 +15,7 @@ class ThreadQueueImplForKLightLock final : public KThreadQueue {
public:
explicit ThreadQueueImplForKLightLock(KernelCore& kernel_) : KThreadQueue(kernel_) {}
- void CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task) override {
+ void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(waiting_thread);
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index 58e540f31..5b0a9963a 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -11,7 +11,7 @@
#include "core/device_memory.h"
#include "core/hle/kernel/initial_process.h"
#include "core/hle/kernel/k_memory_manager.h"
-#include "core/hle/kernel/k_page_linked_list.h"
+#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
@@ -208,8 +208,8 @@ PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_p
return allocated_block;
}
-ResultCode KMemoryManager::AllocatePageGroupImpl(KPageLinkedList* out, size_t num_pages, Pool pool,
- Direction dir, bool random) {
+Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool,
+ Direction dir, bool random) {
// Choose a heap based on our page size request.
const s32 heap_index = KPageHeap::GetBlockIndex(num_pages);
R_UNLESS(0 <= heap_index, ResultOutOfMemory);
@@ -257,7 +257,7 @@ ResultCode KMemoryManager::AllocatePageGroupImpl(KPageLinkedList* out, size_t nu
return ResultSuccess;
}
-ResultCode KMemoryManager::AllocateAndOpen(KPageLinkedList* out, size_t num_pages, u32 option) {
+Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option) {
ASSERT(out != nullptr);
ASSERT(out->GetNumPages() == 0);
@@ -293,8 +293,8 @@ ResultCode KMemoryManager::AllocateAndOpen(KPageLinkedList* out, size_t num_page
return ResultSuccess;
}
-ResultCode KMemoryManager::AllocateAndOpenForProcess(KPageLinkedList* out, size_t num_pages,
- u32 option, u64 process_id, u8 fill_pattern) {
+Result KMemoryManager::AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option,
+ u64 process_id, u8 fill_pattern) {
ASSERT(out != nullptr);
ASSERT(out->GetNumPages() == 0);
@@ -370,12 +370,12 @@ void KMemoryManager::Close(PAddr address, size_t num_pages) {
}
}
-void KMemoryManager::Close(const KPageLinkedList& pg) {
+void KMemoryManager::Close(const KPageGroup& pg) {
for (const auto& node : pg.Nodes()) {
Close(node.GetAddress(), node.GetNumPages());
}
}
-void KMemoryManager::Open(const KPageLinkedList& pg) {
+void KMemoryManager::Open(const KPageGroup& pg) {
for (const auto& node : pg.Nodes()) {
Open(node.GetAddress(), node.GetNumPages());
}
diff --git a/src/core/hle/kernel/k_memory_manager.h b/src/core/hle/kernel/k_memory_manager.h
index c7923cb82..dcb9b6348 100644
--- a/src/core/hle/kernel/k_memory_manager.h
+++ b/src/core/hle/kernel/k_memory_manager.h
@@ -19,7 +19,7 @@ class System;
namespace Kernel {
-class KPageLinkedList;
+class KPageGroup;
class KMemoryManager final {
public:
@@ -65,17 +65,17 @@ public:
}
PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
- ResultCode AllocateAndOpen(KPageLinkedList* out, size_t num_pages, u32 option);
- ResultCode AllocateAndOpenForProcess(KPageLinkedList* out, size_t num_pages, u32 option,
- u64 process_id, u8 fill_pattern);
+ Result AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option);
+ Result AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option, u64 process_id,
+ u8 fill_pattern);
static constexpr size_t MaxManagerCount = 10;
void Close(PAddr address, size_t num_pages);
- void Close(const KPageLinkedList& pg);
+ void Close(const KPageGroup& pg);
void Open(PAddr address, size_t num_pages);
- void Open(const KPageLinkedList& pg);
+ void Open(const KPageGroup& pg);
public:
static size_t CalculateManagementOverheadSize(size_t region_size) {
@@ -262,8 +262,8 @@ private:
}
}
- ResultCode AllocatePageGroupImpl(KPageLinkedList* out, size_t num_pages, Pool pool,
- Direction dir, bool random);
+ Result AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool, Direction dir,
+ bool random);
private:
Core::System& system;
diff --git a/src/core/hle/kernel/k_page_linked_list.h b/src/core/hle/kernel/k_page_group.h
index 1f79c8330..968753992 100644
--- a/src/core/hle/kernel/k_page_linked_list.h
+++ b/src/core/hle/kernel/k_page_group.h
@@ -12,7 +12,7 @@
namespace Kernel {
-class KPageLinkedList final {
+class KPageGroup final {
public:
class Node final {
public:
@@ -36,8 +36,8 @@ public:
};
public:
- KPageLinkedList() = default;
- KPageLinkedList(u64 address, u64 num_pages) {
+ KPageGroup() = default;
+ KPageGroup(u64 address, u64 num_pages) {
ASSERT(AddBlock(address, num_pages).IsSuccess());
}
@@ -57,7 +57,7 @@ public:
return num_pages;
}
- bool IsEqual(KPageLinkedList& other) const {
+ bool IsEqual(KPageGroup& other) const {
auto this_node = nodes.begin();
auto other_node = other.nodes.begin();
while (this_node != nodes.end() && other_node != other.nodes.end()) {
@@ -72,7 +72,7 @@ public:
return this_node == nodes.end() && other_node == other.nodes.end();
}
- ResultCode AddBlock(u64 address, u64 num_pages) {
+ Result AddBlock(u64 address, u64 num_pages) {
if (!num_pages) {
return ResultSuccess;
}
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 504e22cb9..d975de844 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -9,7 +9,7 @@
#include "core/hle/kernel/k_address_space_info.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_memory_block_manager.h"
-#include "core/hle/kernel/k_page_linked_list.h"
+#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
@@ -47,9 +47,9 @@ KPageTable::KPageTable(Core::System& system_)
KPageTable::~KPageTable() = default;
-ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type,
- bool enable_aslr, VAddr code_addr,
- std::size_t code_size, KMemoryManager::Pool pool) {
+Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
+ VAddr code_addr, std::size_t code_size,
+ KMemoryManager::Pool pool) {
const auto GetSpaceStart = [this](KAddressSpaceInfo::Type type) {
return KAddressSpaceInfo::GetAddressSpaceStart(address_space_width, type);
@@ -65,7 +65,6 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
std::size_t alias_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Alias)};
std::size_t heap_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Heap)};
- ASSERT(start <= code_addr);
ASSERT(code_addr < code_addr + code_size);
ASSERT(code_addr + code_size - 1 <= end - 1);
@@ -258,8 +257,8 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
return InitializeMemoryLayout(start, end);
}
-ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryState state,
- KMemoryPermission perm) {
+Result KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryState state,
+ KMemoryPermission perm) {
const u64 size{num_pages * PageSize};
// Validate the mapping request.
@@ -272,7 +271,7 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free,
KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::None, KMemoryAttribute::None));
- KPageLinkedList pg;
+ KPageGroup pg;
R_TRY(system.Kernel().MemoryManager().AllocateAndOpen(
&pg, num_pages,
KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, allocation_option)));
@@ -284,7 +283,7 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
return ResultSuccess;
}
-ResultCode KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) {
+Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) {
// Validate the mapping request.
R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
ResultInvalidMemoryRegion);
@@ -314,7 +313,7 @@ ResultCode KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::
const std::size_t num_pages = size / PageSize;
// Create page groups for the memory being mapped.
- KPageLinkedList pg;
+ KPageGroup pg;
AddRegionToPages(src_address, num_pages, pg);
// Reprotect the source as kernel-read/not mapped.
@@ -345,8 +344,8 @@ ResultCode KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::
return ResultSuccess;
}
-ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size,
- ICacheInvalidationStrategy icache_invalidation_strategy) {
+Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size,
+ ICacheInvalidationStrategy icache_invalidation_strategy) {
// Validate the mapping request.
R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
ResultInvalidMemoryRegion);
@@ -490,7 +489,7 @@ VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
return address;
}
-ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages) {
+Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) {
ASSERT(this->IsLockedByCurrentThread());
const size_t size = num_pages * PageSize;
@@ -542,7 +541,7 @@ ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num
return ResultSuccess;
}
-bool KPageTable::IsValidPageGroup(const KPageLinkedList& pg_ll, VAddr addr, size_t num_pages) {
+bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t num_pages) {
ASSERT(this->IsLockedByCurrentThread());
const size_t size = num_pages * PageSize;
@@ -631,8 +630,8 @@ bool KPageTable::IsValidPageGroup(const KPageLinkedList& pg_ll, VAddr addr, size
return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize);
}
-ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
- KPageTable& src_page_table, VAddr src_addr) {
+Result KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
+ VAddr src_addr) {
KScopedLightLock lk(general_lock);
const std::size_t num_pages{size / PageSize};
@@ -661,7 +660,7 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
return ResultSuccess;
}
-ResultCode KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) {
+Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) {
// Lock the physical memory lock.
KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
@@ -722,7 +721,7 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) {
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
// Allocate pages for the new memory.
- KPageLinkedList pg;
+ KPageGroup pg;
R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess(
&pg, (size - mapped_size) / PageSize,
KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0));
@@ -904,7 +903,7 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) {
}
}
-ResultCode KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) {
+Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) {
// Lock the physical memory lock.
KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
@@ -973,7 +972,7 @@ ResultCode KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) {
}
// Make a page group for the unmap region.
- KPageLinkedList pg;
+ KPageGroup pg;
{
auto& impl = this->PageTableImpl();
@@ -1135,7 +1134,7 @@ ResultCode KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) {
return ResultSuccess;
}
-ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
+Result KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
KScopedLightLock lk(general_lock);
KMemoryState src_state{};
@@ -1148,7 +1147,7 @@ ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t siz
return ResultInvalidCurrentMemory;
}
- KPageLinkedList page_linked_list;
+ KPageGroup page_linked_list;
const std::size_t num_pages{size / PageSize};
AddRegionToPages(src_addr, num_pages, page_linked_list);
@@ -1174,7 +1173,7 @@ ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t siz
return ResultSuccess;
}
-ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
+Result KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
KScopedLightLock lk(general_lock);
KMemoryState src_state{};
@@ -1189,8 +1188,8 @@ ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t s
KMemoryPermission::None, KMemoryAttribute::Mask,
KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
- KPageLinkedList src_pages;
- KPageLinkedList dst_pages;
+ KPageGroup src_pages;
+ KPageGroup dst_pages;
const std::size_t num_pages{size / PageSize};
AddRegionToPages(src_addr, num_pages, src_pages);
@@ -1216,8 +1215,8 @@ ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t s
return ResultSuccess;
}
-ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
- KMemoryPermission perm) {
+Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list,
+ KMemoryPermission perm) {
ASSERT(this->IsLockedByCurrentThread());
VAddr cur_addr{addr};
@@ -1240,8 +1239,8 @@ ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_l
return ResultSuccess;
}
-ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list,
- KMemoryState state, KMemoryPermission perm) {
+Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state,
+ KMemoryPermission perm) {
// Check that the map is in range.
const std::size_t num_pages{page_linked_list.GetNumPages()};
const std::size_t size{num_pages * PageSize};
@@ -1264,10 +1263,10 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list
return ResultSuccess;
}
-ResultCode KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
- PAddr phys_addr, bool is_pa_valid, VAddr region_start,
- std::size_t region_num_pages, KMemoryState state,
- KMemoryPermission perm) {
+Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
+ PAddr phys_addr, bool is_pa_valid, VAddr region_start,
+ std::size_t region_num_pages, KMemoryState state,
+ KMemoryPermission perm) {
ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
// Ensure this is a valid map request.
@@ -1304,7 +1303,7 @@ ResultCode KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::siz
return ResultSuccess;
}
-ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
+Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) {
ASSERT(this->IsLockedByCurrentThread());
VAddr cur_addr{addr};
@@ -1322,8 +1321,7 @@ ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked
return ResultSuccess;
}
-ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
- KMemoryState state) {
+Result KPageTable::UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state) {
// Check that the unmap is in range.
const std::size_t num_pages{page_linked_list.GetNumPages()};
const std::size_t size{num_pages * PageSize};
@@ -1346,7 +1344,7 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
return ResultSuccess;
}
-ResultCode KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state) {
+Result KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state) {
// Check that the unmap is in range.
const std::size_t size = num_pages * PageSize;
R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
@@ -1370,10 +1368,10 @@ ResultCode KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryS
return ResultSuccess;
}
-ResultCode KPageTable::MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
- KMemoryState state_mask, KMemoryState state,
- KMemoryPermission perm_mask, KMemoryPermission perm,
- KMemoryAttribute attr_mask, KMemoryAttribute attr) {
+Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr) {
// Ensure that the page group isn't null.
ASSERT(out != nullptr);
@@ -1395,8 +1393,8 @@ ResultCode KPageTable::MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address,
return ResultSuccess;
}
-ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
- Svc::MemoryPermission svc_perm) {
+Result KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
+ Svc::MemoryPermission svc_perm) {
const size_t num_pages = size / PageSize;
// Lock the table.
@@ -1468,7 +1466,7 @@ KMemoryInfo KPageTable::QueryInfo(VAddr addr) {
return QueryInfoImpl(addr);
}
-ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) {
+Result KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) {
KScopedLightLock lk(general_lock);
KMemoryState state{};
@@ -1486,7 +1484,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo
return ResultSuccess;
}
-ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
+Result KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
KScopedLightLock lk(general_lock);
KMemoryState state{};
@@ -1501,8 +1499,8 @@ ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
return ResultSuccess;
}
-ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
- Svc::MemoryPermission svc_perm) {
+Result KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
+ Svc::MemoryPermission svc_perm) {
const size_t num_pages = size / PageSize;
// Lock the table.
@@ -1529,7 +1527,7 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
return ResultSuccess;
}
-ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr) {
+Result KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr) {
const size_t num_pages = size / PageSize;
ASSERT((static_cast<KMemoryAttribute>(mask) | KMemoryAttribute::SetMask) ==
KMemoryAttribute::SetMask);
@@ -1564,7 +1562,7 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask
return ResultSuccess;
}
-ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
+Result KPageTable::SetMaxHeapSize(std::size_t size) {
// Lock the table.
KScopedLightLock lk(general_lock);
@@ -1576,7 +1574,7 @@ ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
return ResultSuccess;
}
-ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
+Result KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
// Lock the physical memory mutex.
KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
@@ -1643,7 +1641,7 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
// Allocate pages for the heap extension.
- KPageLinkedList pg;
+ KPageGroup pg;
R_TRY(system.Kernel().MemoryManager().AllocateAndOpen(
&pg, allocation_size / PageSize,
KMemoryManager::EncodeOption(memory_pool, allocation_option)));
@@ -1718,7 +1716,7 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
if (is_map_only) {
R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
} else {
- KPageLinkedList page_group;
+ KPageGroup page_group;
R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess(
&page_group, needed_num_pages,
KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0));
@@ -1730,11 +1728,11 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
return addr;
}
-ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
+Result KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
KScopedLightLock lk(general_lock);
KMemoryPermission perm{};
- if (const ResultCode result{CheckMemoryState(
+ if (const Result result{CheckMemoryState(
nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
@@ -1753,11 +1751,11 @@ ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
return ResultSuccess;
}
-ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) {
+Result KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) {
KScopedLightLock lk(general_lock);
KMemoryPermission perm{};
- if (const ResultCode result{CheckMemoryState(
+ if (const Result result{CheckMemoryState(
nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
@@ -1776,7 +1774,7 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
return ResultSuccess;
}
-ResultCode KPageTable::LockForCodeMemory(KPageLinkedList* out, VAddr addr, std::size_t size) {
+Result KPageTable::LockForCodeMemory(KPageGroup* out, VAddr addr, std::size_t size) {
return this->LockMemoryAndOpen(
out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
@@ -1786,15 +1784,14 @@ ResultCode KPageTable::LockForCodeMemory(KPageLinkedList* out, VAddr addr, std::
KMemoryAttribute::Locked);
}
-ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size,
- const KPageLinkedList& pg) {
+Result KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageGroup& pg) {
return this->UnlockMemory(
addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All,
KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg);
}
-ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
+Result KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
block_manager = std::make_unique<KMemoryBlockManager>(start, end);
return ResultSuccess;
@@ -1819,7 +1816,7 @@ bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const {
}
void KPageTable::AddRegionToPages(VAddr start, std::size_t num_pages,
- KPageLinkedList& page_linked_list) {
+ KPageGroup& page_linked_list) {
VAddr addr{start};
while (addr < start + (num_pages * PageSize)) {
const PAddr paddr{GetPhysicalAddr(addr)};
@@ -1838,8 +1835,8 @@ VAddr KPageTable::AllocateVirtualMemory(VAddr start, std::size_t region_num_page
IsKernel() ? 1 : 4);
}
-ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLinkedList& page_group,
- OperationType operation) {
+Result KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageGroup& page_group,
+ OperationType operation) {
ASSERT(this->IsLockedByCurrentThread());
ASSERT(Common::IsAligned(addr, PageSize));
@@ -1863,8 +1860,8 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLin
return ResultSuccess;
}
-ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
- OperationType operation, PAddr map_addr) {
+Result KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
+ OperationType operation, PAddr map_addr) {
ASSERT(this->IsLockedByCurrentThread());
ASSERT(num_pages > 0);
@@ -2006,10 +2003,10 @@ bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) co
}
}
-ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
- KMemoryState state, KMemoryPermission perm_mask,
- KMemoryPermission perm, KMemoryAttribute attr_mask,
- KMemoryAttribute attr) const {
+Result KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
+ KMemoryState state, KMemoryPermission perm_mask,
+ KMemoryPermission perm, KMemoryAttribute attr_mask,
+ KMemoryAttribute attr) const {
// Validate the states match expectation.
R_UNLESS((info.state & state_mask) == state, ResultInvalidCurrentMemory);
R_UNLESS((info.perm & perm_mask) == perm, ResultInvalidCurrentMemory);
@@ -2018,12 +2015,11 @@ ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState st
return ResultSuccess;
}
-ResultCode KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
- std::size_t size, KMemoryState state_mask,
- KMemoryState state, KMemoryPermission perm_mask,
- KMemoryPermission perm,
- KMemoryAttribute attr_mask,
- KMemoryAttribute attr) const {
+Result KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
+ std::size_t size, KMemoryState state_mask,
+ KMemoryState state, KMemoryPermission perm_mask,
+ KMemoryPermission perm, KMemoryAttribute attr_mask,
+ KMemoryAttribute attr) const {
ASSERT(this->IsLockedByCurrentThread());
// Get information about the first block.
@@ -2061,12 +2057,12 @@ ResultCode KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed
return ResultSuccess;
}
-ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
- KMemoryAttribute* out_attr, std::size_t* out_blocks_needed,
- VAddr addr, std::size_t size, KMemoryState state_mask,
- KMemoryState state, KMemoryPermission perm_mask,
- KMemoryPermission perm, KMemoryAttribute attr_mask,
- KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
+Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
+ KMemoryAttribute* out_attr, std::size_t* out_blocks_needed,
+ VAddr addr, std::size_t size, KMemoryState state_mask,
+ KMemoryState state, KMemoryPermission perm_mask,
+ KMemoryPermission perm, KMemoryAttribute attr_mask,
+ KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
ASSERT(this->IsLockedByCurrentThread());
// Get information about the first block.
@@ -2123,11 +2119,11 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
return ResultSuccess;
}
-ResultCode KPageTable::LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr,
- size_t size, KMemoryState state_mask, KMemoryState state,
- KMemoryPermission perm_mask, KMemoryPermission perm,
- KMemoryAttribute attr_mask, KMemoryAttribute attr,
- KMemoryPermission new_perm, KMemoryAttribute lock_attr) {
+Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr,
+ KMemoryPermission new_perm, KMemoryAttribute lock_attr) {
// Validate basic preconditions.
ASSERT((lock_attr & attr) == KMemoryAttribute::None);
ASSERT((lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
@@ -2181,11 +2177,11 @@ ResultCode KPageTable::LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_pad
return ResultSuccess;
}
-ResultCode KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask,
- KMemoryState state, KMemoryPermission perm_mask,
- KMemoryPermission perm, KMemoryAttribute attr_mask,
- KMemoryAttribute attr, KMemoryPermission new_perm,
- KMemoryAttribute lock_attr, const KPageLinkedList* pg) {
+Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask,
+ KMemoryState state, KMemoryPermission perm_mask,
+ KMemoryPermission perm, KMemoryAttribute attr_mask,
+ KMemoryAttribute attr, KMemoryPermission new_perm,
+ KMemoryAttribute lock_attr, const KPageGroup* pg) {
// Validate basic preconditions.
ASSERT((attr_mask & lock_attr) == lock_attr);
ASSERT((attr & lock_attr) == lock_attr);
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 6312eb682..25774f232 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -33,51 +33,49 @@ public:
explicit KPageTable(Core::System& system_);
~KPageTable();
- ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
- VAddr code_addr, std::size_t code_size,
- KMemoryManager::Pool pool);
- ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state,
- KMemoryPermission perm);
- ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size);
- ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size,
- ICacheInvalidationStrategy icache_invalidation_strategy);
- ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
- VAddr src_addr);
- ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
- ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size);
- ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
- ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
- ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
- KMemoryPermission perm);
- ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
- PAddr phys_addr, KMemoryState state, KMemoryPermission perm) {
+ Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
+ VAddr code_addr, std::size_t code_size, KMemoryManager::Pool pool);
+ Result MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state,
+ KMemoryPermission perm);
+ Result MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size);
+ Result UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size,
+ ICacheInvalidationStrategy icache_invalidation_strategy);
+ Result UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
+ VAddr src_addr);
+ Result MapPhysicalMemory(VAddr addr, std::size_t size);
+ Result UnmapPhysicalMemory(VAddr addr, std::size_t size);
+ Result MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
+ Result UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
+ Result MapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state,
+ KMemoryPermission perm);
+ Result MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, PAddr phys_addr,
+ KMemoryState state, KMemoryPermission perm) {
return this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize,
state, perm);
}
- ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
- ResultCode UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state);
- ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size,
- Svc::MemoryPermission svc_perm);
+ Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state);
+ Result UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state);
+ Result SetProcessMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission svc_perm);
KMemoryInfo QueryInfo(VAddr addr);
- ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
- ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
- ResultCode SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm);
- ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr);
- ResultCode SetMaxHeapSize(std::size_t size);
- ResultCode SetHeapSize(VAddr* out, std::size_t size);
+ Result ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
+ Result ResetTransferMemory(VAddr addr, std::size_t size);
+ Result SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm);
+ Result SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr);
+ Result SetMaxHeapSize(std::size_t size);
+ Result SetHeapSize(VAddr* out, std::size_t size);
ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
bool is_map_only, VAddr region_start,
std::size_t region_num_pages, KMemoryState state,
KMemoryPermission perm, PAddr map_addr = 0);
- ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
- ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
- ResultCode LockForCodeMemory(KPageLinkedList* out, VAddr addr, std::size_t size);
- ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageLinkedList& pg);
- ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
- KMemoryState state_mask, KMemoryState state,
- KMemoryPermission perm_mask, KMemoryPermission perm,
- KMemoryAttribute attr_mask, KMemoryAttribute attr);
+ Result LockForDeviceAddressSpace(VAddr addr, std::size_t size);
+ Result UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
+ Result LockForCodeMemory(KPageGroup* out, VAddr addr, std::size_t size);
+ Result UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageGroup& pg);
+ Result MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr);
Common::PageTable& PageTableImpl() {
return page_table_impl;
@@ -102,83 +100,78 @@ private:
KMemoryAttribute::IpcLocked |
KMemoryAttribute::DeviceShared;
- ResultCode InitializeMemoryLayout(VAddr start, VAddr end);
- ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
- KMemoryPermission perm);
- ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
- PAddr phys_addr, bool is_pa_valid, VAddr region_start,
- std::size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
- ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list);
+ Result InitializeMemoryLayout(VAddr start, VAddr end);
+ Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm);
+ Result MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, PAddr phys_addr,
+ bool is_pa_valid, VAddr region_start, std::size_t region_num_pages,
+ KMemoryState state, KMemoryPermission perm);
+ Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list);
bool IsRegionMapped(VAddr address, u64 size);
bool IsRegionContiguous(VAddr addr, u64 size) const;
- void AddRegionToPages(VAddr start, std::size_t num_pages, KPageLinkedList& page_linked_list);
+ void AddRegionToPages(VAddr start, std::size_t num_pages, KPageGroup& page_linked_list);
KMemoryInfo QueryInfoImpl(VAddr addr);
VAddr AllocateVirtualMemory(VAddr start, std::size_t region_num_pages, u64 needed_num_pages,
std::size_t align);
- ResultCode Operate(VAddr addr, std::size_t num_pages, const KPageLinkedList& page_group,
- OperationType operation);
- ResultCode Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
- OperationType operation, PAddr map_addr = 0);
+ Result Operate(VAddr addr, std::size_t num_pages, const KPageGroup& page_group,
+ OperationType operation);
+ Result Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
+ OperationType operation, PAddr map_addr = 0);
VAddr GetRegionAddress(KMemoryState state) const;
std::size_t GetRegionSize(KMemoryState state) const;
VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages,
std::size_t alignment, std::size_t offset, std::size_t guard_pages);
- ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
- std::size_t size, KMemoryState state_mask,
- KMemoryState state, KMemoryPermission perm_mask,
- KMemoryPermission perm, KMemoryAttribute attr_mask,
- KMemoryAttribute attr) const;
- ResultCode CheckMemoryStateContiguous(VAddr addr, std::size_t size, KMemoryState state_mask,
- KMemoryState state, KMemoryPermission perm_mask,
- KMemoryPermission perm, KMemoryAttribute attr_mask,
- KMemoryAttribute attr) const {
+ Result CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, std::size_t size,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
+ Result CheckMemoryStateContiguous(VAddr addr, std::size_t size, KMemoryState state_mask,
+ KMemoryState state, KMemoryPermission perm_mask,
+ KMemoryPermission perm, KMemoryAttribute attr_mask,
+ KMemoryAttribute attr) const {
return this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
perm, attr_mask, attr);
}
- ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
- KMemoryState state, KMemoryPermission perm_mask,
- KMemoryPermission perm, KMemoryAttribute attr_mask,
- KMemoryAttribute attr) const;
- ResultCode CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
- KMemoryAttribute* out_attr, std::size_t* out_blocks_needed,
- VAddr addr, std::size_t size, KMemoryState state_mask,
- KMemoryState state, KMemoryPermission perm_mask,
- KMemoryPermission perm, KMemoryAttribute attr_mask,
- KMemoryAttribute attr,
- KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
- ResultCode CheckMemoryState(std::size_t* out_blocks_needed, VAddr addr, std::size_t size,
- KMemoryState state_mask, KMemoryState state,
- KMemoryPermission perm_mask, KMemoryPermission perm,
- KMemoryAttribute attr_mask, KMemoryAttribute attr,
- KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
+ Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
+ Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
+ KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, VAddr addr,
+ std::size_t size, KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr,
+ KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
+ Result CheckMemoryState(std::size_t* out_blocks_needed, VAddr addr, std::size_t size,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr,
+ KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr);
}
- ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
- KMemoryState state, KMemoryPermission perm_mask,
- KMemoryPermission perm, KMemoryAttribute attr_mask,
- KMemoryAttribute attr,
- KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
+ Result CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
+ KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr,
+ KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
return this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
attr_mask, attr, ignore_attr);
}
- ResultCode LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr, size_t size,
- KMemoryState state_mask, KMemoryState state,
- KMemoryPermission perm_mask, KMemoryPermission perm,
- KMemoryAttribute attr_mask, KMemoryAttribute attr,
- KMemoryPermission new_perm, KMemoryAttribute lock_attr);
- ResultCode UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state,
- KMemoryPermission perm_mask, KMemoryPermission perm,
- KMemoryAttribute attr_mask, KMemoryAttribute attr,
- KMemoryPermission new_perm, KMemoryAttribute lock_attr,
- const KPageLinkedList* pg);
+ Result LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr,
+ KMemoryPermission new_perm, KMemoryAttribute lock_attr);
+ Result UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr,
+ KMemoryPermission new_perm, KMemoryAttribute lock_attr,
+ const KPageGroup* pg);
- ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages);
- bool IsValidPageGroup(const KPageLinkedList& pg, VAddr addr, size_t num_pages);
+ Result MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages);
+ bool IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_pages);
bool IsLockedByCurrentThread() const {
return general_lock.IsLockedByCurrentThread();
diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp
index 51c2cd1ef..7a5a9dc2a 100644
--- a/src/core/hle/kernel/k_port.cpp
+++ b/src/core/hle/kernel/k_port.cpp
@@ -50,7 +50,7 @@ bool KPort::IsServerClosed() const {
return state == State::ServerClosed;
}
-ResultCode KPort::EnqueueSession(KServerSession* session) {
+Result KPort::EnqueueSession(KServerSession* session) {
KScopedSchedulerLock sl{kernel};
R_UNLESS(state == State::Normal, ResultPortClosed);
diff --git a/src/core/hle/kernel/k_port.h b/src/core/hle/kernel/k_port.h
index 1bfecf8c3..0cfc16dab 100644
--- a/src/core/hle/kernel/k_port.h
+++ b/src/core/hle/kernel/k_port.h
@@ -34,7 +34,7 @@ public:
bool IsServerClosed() const;
- ResultCode EnqueueSession(KServerSession* session);
+ Result EnqueueSession(KServerSession* session);
KClientPort& GetClientPort() {
return client;
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index cd863e715..d3e99665f 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -1,6 +1,5 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2015 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <bitset>
@@ -57,23 +56,18 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
thread->GetContext64().cpu_registers[0] = 0;
thread->GetContext32().cpu_registers[1] = thread_handle;
thread->GetContext64().cpu_registers[1] = thread_handle;
- thread->DisableDispatch();
- auto& kernel = system.Kernel();
- // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
- {
- KScopedSchedulerLock lock{kernel};
- thread->SetState(ThreadState::Runnable);
-
- if (system.DebuggerEnabled()) {
- thread->RequestSuspend(SuspendType::Debug);
- }
+ if (system.DebuggerEnabled()) {
+ thread->RequestSuspend(SuspendType::Debug);
}
+
+ // Run our thread.
+ void(thread->Run());
}
} // Anonymous namespace
-ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name,
- ProcessType type, KResourceLimit* res_limit) {
+Result KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name,
+ ProcessType type, KResourceLimit* res_limit) {
auto& kernel = system.Kernel();
process->name = std::move(process_name);
@@ -166,7 +160,7 @@ bool KProcess::ReleaseUserException(KThread* thread) {
std::addressof(num_waiters),
reinterpret_cast<uintptr_t>(std::addressof(exception_thread)));
next != nullptr) {
- next->SetState(ThreadState::Runnable);
+ next->EndWait(ResultSuccess);
}
KScheduler::SetSchedulerUpdateNeeded(kernel);
@@ -181,7 +175,8 @@ void KProcess::PinCurrentThread(s32 core_id) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Get the current thread.
- KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
+ KThread* cur_thread =
+ kernel.Scheduler(static_cast<std::size_t>(core_id)).GetSchedulerCurrentThread();
// If the thread isn't terminated, pin it.
if (!cur_thread->IsTerminationRequested()) {
@@ -198,7 +193,8 @@ void KProcess::UnpinCurrentThread(s32 core_id) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Get the current thread.
- KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
+ KThread* cur_thread =
+ kernel.Scheduler(static_cast<std::size_t>(core_id)).GetSchedulerCurrentThread();
// Unpin it.
cur_thread->Unpin();
@@ -222,8 +218,8 @@ void KProcess::UnpinThread(KThread* thread) {
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
-ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address,
- [[maybe_unused]] size_t size) {
+Result KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address,
+ [[maybe_unused]] size_t size) {
// Lock ourselves, to prevent concurrent access.
KScopedLightLock lk(state_lock);
@@ -287,7 +283,7 @@ void KProcess::UnregisterThread(KThread* thread) {
thread_list.remove(thread);
}
-ResultCode KProcess::Reset() {
+Result KProcess::Reset() {
// Lock the process and the scheduler.
KScopedLightLock lk(state_lock);
KScopedSchedulerLock sl{kernel};
@@ -301,7 +297,7 @@ ResultCode KProcess::Reset() {
return ResultSuccess;
}
-ResultCode KProcess::SetActivity(ProcessActivity activity) {
+Result KProcess::SetActivity(ProcessActivity activity) {
// Lock ourselves and the scheduler.
KScopedLightLock lk{state_lock};
KScopedLightLock list_lk{list_lock};
@@ -345,8 +341,7 @@ ResultCode KProcess::SetActivity(ProcessActivity activity) {
return ResultSuccess;
}
-ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
- std::size_t code_size) {
+Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size) {
program_id = metadata.GetTitleID();
ideal_core = metadata.GetMainThreadCore();
is_64bit_process = metadata.Is64BitProgram();
@@ -361,24 +356,24 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
return ResultLimitReached;
}
// Initialize proces address space
- if (const ResultCode result{
- page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, 0x8000000,
- code_size, KMemoryManager::Pool::Application)};
+ if (const Result result{page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false,
+ 0x8000000, code_size,
+ KMemoryManager::Pool::Application)};
result.IsError()) {
return result;
}
// Map process code region
- if (const ResultCode result{page_table->MapProcessCode(page_table->GetCodeRegionStart(),
- code_size / PageSize, KMemoryState::Code,
- KMemoryPermission::None)};
+ if (const Result result{page_table->MapProcessCode(page_table->GetCodeRegionStart(),
+ code_size / PageSize, KMemoryState::Code,
+ KMemoryPermission::None)};
result.IsError()) {
return result;
}
// Initialize process capabilities
const auto& caps{metadata.GetKernelCapabilities()};
- if (const ResultCode result{
+ if (const Result result{
capabilities.InitializeForUserProcess(caps.data(), caps.size(), *page_table)};
result.IsError()) {
return result;
@@ -425,11 +420,11 @@ void KProcess::PrepareForTermination() {
ChangeStatus(ProcessStatus::Exiting);
const auto stop_threads = [this](const std::vector<KThread*>& in_thread_list) {
- for (auto& thread : in_thread_list) {
+ for (auto* thread : in_thread_list) {
if (thread->GetOwnerProcess() != this)
continue;
- if (thread == kernel.CurrentScheduler()->GetCurrentThread())
+ if (thread == GetCurrentThreadPointer(kernel))
continue;
// TODO(Subv): When are the other running/ready threads terminated?
@@ -485,7 +480,7 @@ void KProcess::Finalize() {
KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize();
}
-ResultCode KProcess::CreateThreadLocalRegion(VAddr* out) {
+Result KProcess::CreateThreadLocalRegion(VAddr* out) {
KThreadLocalPage* tlp = nullptr;
VAddr tlr = 0;
@@ -536,7 +531,7 @@ ResultCode KProcess::CreateThreadLocalRegion(VAddr* out) {
return ResultSuccess;
}
-ResultCode KProcess::DeleteThreadLocalRegion(VAddr addr) {
+Result KProcess::DeleteThreadLocalRegion(VAddr addr) {
KThreadLocalPage* page_to_free = nullptr;
// Release the region.
@@ -584,6 +579,52 @@ ResultCode KProcess::DeleteThreadLocalRegion(VAddr addr) {
return ResultSuccess;
}
+bool KProcess::InsertWatchpoint(Core::System& system, VAddr addr, u64 size,
+ DebugWatchpointType type) {
+ const auto watch{std::find_if(watchpoints.begin(), watchpoints.end(), [&](const auto& wp) {
+ return wp.type == DebugWatchpointType::None;
+ })};
+
+ if (watch == watchpoints.end()) {
+ return false;
+ }
+
+ watch->start_address = addr;
+ watch->end_address = addr + size;
+ watch->type = type;
+
+ for (VAddr page = Common::AlignDown(addr, PageSize); page < addr + size; page += PageSize) {
+ debug_page_refcounts[page]++;
+ system.Memory().MarkRegionDebug(page, PageSize, true);
+ }
+
+ return true;
+}
+
+bool KProcess::RemoveWatchpoint(Core::System& system, VAddr addr, u64 size,
+ DebugWatchpointType type) {
+ const auto watch{std::find_if(watchpoints.begin(), watchpoints.end(), [&](const auto& wp) {
+ return wp.start_address == addr && wp.end_address == addr + size && wp.type == type;
+ })};
+
+ if (watch == watchpoints.end()) {
+ return false;
+ }
+
+ watch->start_address = 0;
+ watch->end_address = 0;
+ watch->type = DebugWatchpointType::None;
+
+ for (VAddr page = Common::AlignDown(addr, PageSize); page < addr + size; page += PageSize) {
+ debug_page_refcounts[page]--;
+ if (!debug_page_refcounts[page]) {
+ system.Memory().MarkRegionDebug(page, PageSize, false);
+ }
+ }
+
+ return true;
+}
+
void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
Svc::MemoryPermission permission) {
@@ -621,7 +662,7 @@ void KProcess::ChangeStatus(ProcessStatus new_status) {
NotifyAvailable();
}
-ResultCode KProcess::AllocateMainThreadStack(std::size_t stack_size) {
+Result KProcess::AllocateMainThreadStack(std::size_t stack_size) {
ASSERT(stack_size);
// The kernel always ensures that the given stack size is page aligned.
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index e562a79b8..d56d73bab 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -1,12 +1,12 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2015 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <cstddef>
#include <list>
+#include <map>
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/k_address_arbiter.h"
@@ -68,6 +68,20 @@ enum class ProcessActivity : u32 {
Paused,
};
+enum class DebugWatchpointType : u8 {
+ None = 0,
+ Read = 1 << 0,
+ Write = 1 << 1,
+ ReadOrWrite = Read | Write,
+};
+DECLARE_ENUM_FLAG_OPERATORS(DebugWatchpointType);
+
+struct DebugWatchpoint {
+ VAddr start_address;
+ VAddr end_address;
+ DebugWatchpointType type;
+};
+
class KProcess final : public KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask> {
KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject);
@@ -95,8 +109,8 @@ public:
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
- static ResultCode Initialize(KProcess* process, Core::System& system, std::string process_name,
- ProcessType type, KResourceLimit* res_limit);
+ static Result Initialize(KProcess* process, Core::System& system, std::string process_name,
+ ProcessType type, KResourceLimit* res_limit);
/// Gets a reference to the process' page table.
KPageTable& PageTable() {
@@ -118,11 +132,11 @@ public:
return handle_table;
}
- ResultCode SignalToAddress(VAddr address) {
+ Result SignalToAddress(VAddr address) {
return condition_var.SignalToAddress(address);
}
- ResultCode WaitForAddress(Handle handle, VAddr address, u32 tag) {
+ Result WaitForAddress(Handle handle, VAddr address, u32 tag) {
return condition_var.WaitForAddress(handle, address, tag);
}
@@ -130,17 +144,16 @@ public:
return condition_var.Signal(cv_key, count);
}
- ResultCode WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) {
+ Result WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) {
return condition_var.Wait(address, cv_key, tag, ns);
}
- ResultCode SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value,
- s32 count) {
+ Result SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value, s32 count) {
return address_arbiter.SignalToAddress(address, signal_type, value, count);
}
- ResultCode WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value,
- s64 timeout) {
+ Result WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value,
+ s64 timeout) {
return address_arbiter.WaitForAddress(address, arb_type, value, timeout);
}
@@ -307,7 +320,7 @@ public:
/// @pre The process must be in a signaled state. If this is called on a
/// process instance that is not signaled, ERR_INVALID_STATE will be
/// returned.
- ResultCode Reset();
+ Result Reset();
/**
* Loads process-specifics configuration info with metadata provided
@@ -318,7 +331,7 @@ public:
* @returns ResultSuccess if all relevant metadata was able to be
* loaded and parsed. Otherwise, an error code is returned.
*/
- ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size);
+ Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size);
/**
* Starts the main application thread for this process.
@@ -352,7 +365,7 @@ public:
void DoWorkerTaskImpl();
- ResultCode SetActivity(ProcessActivity activity);
+ Result SetActivity(ProcessActivity activity);
void PinCurrentThread(s32 core_id);
void UnpinCurrentThread(s32 core_id);
@@ -362,17 +375,30 @@ public:
return state_lock;
}
- ResultCode AddSharedMemory(KSharedMemory* shmem, VAddr address, size_t size);
+ Result AddSharedMemory(KSharedMemory* shmem, VAddr address, size_t size);
void RemoveSharedMemory(KSharedMemory* shmem, VAddr address, size_t size);
///////////////////////////////////////////////////////////////////////////////////////////////
// Thread-local storage management
// Marks the next available region as used and returns the address of the slot.
- [[nodiscard]] ResultCode CreateThreadLocalRegion(VAddr* out);
+ [[nodiscard]] Result CreateThreadLocalRegion(VAddr* out);
// Frees a used TLS slot identified by the given address
- ResultCode DeleteThreadLocalRegion(VAddr addr);
+ Result DeleteThreadLocalRegion(VAddr addr);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Debug watchpoint management
+
+ // Attempts to insert a watchpoint into a free slot. Returns false if none are available.
+ bool InsertWatchpoint(Core::System& system, VAddr addr, u64 size, DebugWatchpointType type);
+
+ // Attempts to remove the watchpoint specified by the given parameters.
+ bool RemoveWatchpoint(Core::System& system, VAddr addr, u64 size, DebugWatchpointType type);
+
+ const std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>& GetWatchpoints() const {
+ return watchpoints;
+ }
private:
void PinThread(s32 core_id, KThread* thread) {
@@ -395,7 +421,7 @@ private:
void ChangeStatus(ProcessStatus new_status);
/// Allocates the main thread stack for the process, given the stack size in bytes.
- ResultCode AllocateMainThreadStack(std::size_t stack_size);
+ Result AllocateMainThreadStack(std::size_t stack_size);
/// Memory manager for this process
std::unique_ptr<KPageTable> page_table;
@@ -478,6 +504,8 @@ private:
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> running_threads{};
std::array<u64, Core::Hardware::NUM_CPU_CORES> running_thread_idle_counts{};
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> pinned_threads{};
+ std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> watchpoints{};
+ std::map<VAddr, u64> debug_page_refcounts;
KThread* exception_thread{};
diff --git a/src/core/hle/kernel/k_readable_event.cpp b/src/core/hle/kernel/k_readable_event.cpp
index dddba554d..94c5464fe 100644
--- a/src/core/hle/kernel/k_readable_event.cpp
+++ b/src/core/hle/kernel/k_readable_event.cpp
@@ -27,7 +27,7 @@ void KReadableEvent::Destroy() {
}
}
-ResultCode KReadableEvent::Signal() {
+Result KReadableEvent::Signal() {
KScopedSchedulerLock lk{kernel};
if (!is_signaled) {
@@ -38,13 +38,13 @@ ResultCode KReadableEvent::Signal() {
return ResultSuccess;
}
-ResultCode KReadableEvent::Clear() {
+Result KReadableEvent::Clear() {
Reset();
return ResultSuccess;
}
-ResultCode KReadableEvent::Reset() {
+Result KReadableEvent::Reset() {
KScopedSchedulerLock lk{kernel};
if (!is_signaled) {
diff --git a/src/core/hle/kernel/k_readable_event.h b/src/core/hle/kernel/k_readable_event.h
index 5065c7cc0..18dcad289 100644
--- a/src/core/hle/kernel/k_readable_event.h
+++ b/src/core/hle/kernel/k_readable_event.h
@@ -33,9 +33,9 @@ public:
bool IsSignaled() const override;
void Destroy() override;
- ResultCode Signal();
- ResultCode Clear();
- ResultCode Reset();
+ Result Signal();
+ Result Clear();
+ Result Reset();
private:
bool is_signaled{};
diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp
index 3e0ecffdb..010dcf99e 100644
--- a/src/core/hle/kernel/k_resource_limit.cpp
+++ b/src/core/hle/kernel/k_resource_limit.cpp
@@ -73,7 +73,7 @@ s64 KResourceLimit::GetFreeValue(LimitableResource which) const {
return value;
}
-ResultCode KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
+Result KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
const auto index = static_cast<std::size_t>(which);
KScopedLightLock lk(lock);
R_UNLESS(current_values[index] <= value, ResultInvalidState);
diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h
index 43bf74b8d..65c98c979 100644
--- a/src/core/hle/kernel/k_resource_limit.h
+++ b/src/core/hle/kernel/k_resource_limit.h
@@ -8,7 +8,7 @@
#include "core/hle/kernel/k_light_condition_variable.h"
#include "core/hle/kernel/k_light_lock.h"
-union ResultCode;
+union Result;
namespace Core::Timing {
class CoreTiming;
@@ -46,7 +46,7 @@ public:
s64 GetPeakValue(LimitableResource which) const;
s64 GetFreeValue(LimitableResource which) const;
- ResultCode SetLimitValue(LimitableResource which, s64 value);
+ Result SetLimitValue(LimitableResource which, s64 value);
bool Reserve(LimitableResource which, s64 value);
bool Reserve(LimitableResource which, s64 value, s64 timeout);
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index 2d4e8637b..c34ce7a17 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -27,69 +27,185 @@ static void IncrementScheduledCount(Kernel::KThread* thread) {
}
}
-void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule) {
- auto scheduler = kernel.CurrentScheduler();
-
- u32 current_core{0xF};
- bool must_context_switch{};
- if (scheduler) {
- current_core = scheduler->core_id;
- // TODO(bunnei): Should be set to true when we deprecate single core
- must_context_switch = !kernel.IsPhantomModeForSingleCore();
- }
-
- while (cores_pending_reschedule != 0) {
- const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule));
- ASSERT(core < Core::Hardware::NUM_CPU_CORES);
- if (!must_context_switch || core != current_core) {
- auto& phys_core = kernel.PhysicalCore(core);
- phys_core.Interrupt();
+KScheduler::KScheduler(KernelCore& kernel_) : kernel{kernel_} {
+ m_switch_fiber = std::make_shared<Common::Fiber>([this] {
+ while (true) {
+ ScheduleImplFiber();
}
- cores_pending_reschedule &= ~(1ULL << core);
+ });
+
+ m_state.needs_scheduling = true;
+}
+
+KScheduler::~KScheduler() = default;
+
+void KScheduler::SetInterruptTaskRunnable() {
+ m_state.interrupt_task_runnable = true;
+ m_state.needs_scheduling = true;
+}
+
+void KScheduler::RequestScheduleOnInterrupt() {
+ m_state.needs_scheduling = true;
+
+ if (CanSchedule(kernel)) {
+ ScheduleOnInterrupt();
}
+}
- for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) {
- if (kernel.PhysicalCore(core_id).IsInterrupted()) {
- KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_id));
- }
+void KScheduler::DisableScheduling(KernelCore& kernel) {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
+ GetCurrentThread(kernel).DisableDispatch();
+}
+
+void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 1);
+
+ auto* scheduler{kernel.CurrentScheduler()};
+
+ if (!scheduler || kernel.IsPhantomModeForSingleCore()) {
+ KScheduler::RescheduleCores(kernel, cores_needing_scheduling);
+ KScheduler::RescheduleCurrentHLEThread(kernel);
+ return;
}
- if (must_context_switch) {
- auto core_scheduler = kernel.CurrentScheduler();
- kernel.ExitSVCProfile();
- core_scheduler->RescheduleCurrentCore();
- kernel.EnterSVCProfile();
+ scheduler->RescheduleOtherCores(cores_needing_scheduling);
+
+ if (GetCurrentThread(kernel).GetDisableDispatchCount() > 1) {
+ GetCurrentThread(kernel).EnableDispatch();
+ } else {
+ scheduler->RescheduleCurrentCore();
}
}
+void KScheduler::RescheduleCurrentHLEThread(KernelCore& kernel) {
+ // HACK: we cannot schedule from this thread, it is not a core thread
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
+
+ // Special case to ensure dummy threads that are waiting block
+ GetCurrentThread(kernel).IfDummyThreadTryWait();
+
+ ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting);
+ GetCurrentThread(kernel).EnableDispatch();
+}
+
+u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
+ if (IsSchedulerUpdateNeeded(kernel)) {
+ return UpdateHighestPriorityThreadsImpl(kernel);
+ } else {
+ return 0;
+ }
+}
+
+void KScheduler::Schedule() {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
+ ASSERT(m_core_id == GetCurrentCoreId(kernel));
+
+ ScheduleImpl();
+}
+
+void KScheduler::ScheduleOnInterrupt() {
+ GetCurrentThread(kernel).DisableDispatch();
+ Schedule();
+ GetCurrentThread(kernel).EnableDispatch();
+}
+
+void KScheduler::PreemptSingleCore() {
+ GetCurrentThread(kernel).DisableDispatch();
+
+ auto* thread = GetCurrentThreadPointer(kernel);
+ auto& previous_scheduler = kernel.Scheduler(thread->GetCurrentCore());
+ previous_scheduler.Unload(thread);
+
+ Common::Fiber::YieldTo(thread->GetHostContext(), *m_switch_fiber);
+
+ GetCurrentThread(kernel).EnableDispatch();
+}
+
+void KScheduler::RescheduleCurrentCore() {
+ ASSERT(!kernel.IsPhantomModeForSingleCore());
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
+
+ GetCurrentThread(kernel).EnableDispatch();
+
+ if (m_state.needs_scheduling.load()) {
+ // Disable interrupts, and then check again if rescheduling is needed.
+ // KScopedInterruptDisable intr_disable;
+
+ kernel.CurrentScheduler()->RescheduleCurrentCoreImpl();
+ }
+}
+
+void KScheduler::RescheduleCurrentCoreImpl() {
+ // Check that scheduling is needed.
+ if (m_state.needs_scheduling.load()) [[likely]] {
+ GetCurrentThread(kernel).DisableDispatch();
+ Schedule();
+ GetCurrentThread(kernel).EnableDispatch();
+ }
+}
+
+void KScheduler::Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id) {
+ // Set core ID/idle thread/interrupt task manager.
+ m_core_id = core_id;
+ m_idle_thread = idle_thread;
+ // m_state.idle_thread_stack = m_idle_thread->GetStackTop();
+ // m_state.interrupt_task_manager = &kernel.GetInterruptTaskManager();
+
+ // Insert the main thread into the priority queue.
+ // {
+ // KScopedSchedulerLock lk{kernel};
+ // GetPriorityQueue(kernel).PushBack(GetCurrentThreadPointer(kernel));
+ // SetSchedulerUpdateNeeded(kernel);
+ // }
+
+ // Bind interrupt handler.
+ // kernel.GetInterruptManager().BindHandler(
+ // GetSchedulerInterruptHandler(kernel), KInterruptName::Scheduler, m_core_id,
+ // KInterruptController::PriorityLevel::Scheduler, false, false);
+
+ // Set the current thread.
+ m_current_thread = main_thread;
+}
+
+void KScheduler::Activate() {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
+
+ // m_state.should_count_idle = KTargetSystem::IsDebugMode();
+ m_is_active = true;
+ RescheduleCurrentCore();
+}
+
+void KScheduler::OnThreadStart() {
+ GetCurrentThread(kernel).EnableDispatch();
+}
+
u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
- KScopedSpinLock lk{guard};
- if (KThread* prev_highest_thread = state.highest_priority_thread;
- prev_highest_thread != highest_thread) {
- if (prev_highest_thread != nullptr) {
+ if (KThread* prev_highest_thread = m_state.highest_priority_thread;
+ prev_highest_thread != highest_thread) [[likely]] {
+ if (prev_highest_thread != nullptr) [[likely]] {
IncrementScheduledCount(prev_highest_thread);
- prev_highest_thread->SetLastScheduledTick(system.CoreTiming().GetCPUTicks());
+ prev_highest_thread->SetLastScheduledTick(kernel.System().CoreTiming().GetCPUTicks());
}
- if (state.should_count_idle) {
- if (highest_thread != nullptr) {
+ if (m_state.should_count_idle) {
+ if (highest_thread != nullptr) [[likely]] {
if (KProcess* process = highest_thread->GetOwnerProcess(); process != nullptr) {
- process->SetRunningThread(core_id, highest_thread, state.idle_count);
+ process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count);
}
} else {
- state.idle_count++;
+ m_state.idle_count++;
}
}
- state.highest_priority_thread = highest_thread;
- state.needs_scheduling.store(true);
- return (1ULL << core_id);
+ m_state.highest_priority_thread = highest_thread;
+ m_state.needs_scheduling = true;
+ return (1ULL << m_core_id);
} else {
return 0;
}
}
u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// Clear that we need to update.
ClearSchedulerUpdateNeeded(kernel);
@@ -98,18 +214,20 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
KThread* top_threads[Core::Hardware::NUM_CPU_CORES];
auto& priority_queue = GetPriorityQueue(kernel);
- /// We want to go over all cores, finding the highest priority thread and determining if
- /// scheduling is needed for that core.
+ // We want to go over all cores, finding the highest priority thread and determining if
+ // scheduling is needed for that core.
for (size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
KThread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id));
if (top_thread != nullptr) {
- // If the thread has no waiters, we need to check if the process has a thread pinned.
- if (top_thread->GetNumKernelWaiters() == 0) {
- if (KProcess* parent = top_thread->GetOwnerProcess(); parent != nullptr) {
- if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id));
- pinned != nullptr && pinned != top_thread) {
- // We prefer our parent's pinned thread if possible. However, we also don't
- // want to schedule un-runnable threads.
+ // We need to check if the thread's process has a pinned thread.
+ if (KProcess* parent = top_thread->GetOwnerProcess()) {
+ // Check that there's a pinned thread other than the current top thread.
+ if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id));
+ pinned != nullptr && pinned != top_thread) {
+ // We need to prefer threads with kernel waiters to the pinned thread.
+ if (top_thread->GetNumKernelWaiters() ==
+ 0 /* && top_thread != parent->GetExceptionThread() */) {
+ // If the pinned thread is runnable, use it.
if (pinned->GetRawState() == ThreadState::Runnable) {
top_thread = pinned;
} else {
@@ -129,7 +247,8 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// Idle cores are bad. We're going to try to migrate threads to each idle core in turn.
while (idle_cores != 0) {
- const auto core_id = static_cast<u32>(std::countr_zero(idle_cores));
+ const s32 core_id = static_cast<s32>(std::countr_zero(idle_cores));
+
if (KThread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) {
s32 migration_candidates[Core::Hardware::NUM_CPU_CORES];
size_t num_candidates = 0;
@@ -150,7 +269,6 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// The suggested thread isn't bound to its core, so we can migrate it!
suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(suggested_core, suggested);
-
top_threads[core_id] = suggested;
cores_needing_scheduling |=
kernel.Scheduler(core_id).UpdateHighestPriorityThread(top_threads[core_id]);
@@ -183,7 +301,6 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// Perform the migration.
suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(candidate_core, suggested);
-
top_threads[core_id] = suggested;
cores_needing_scheduling |=
kernel.Scheduler(core_id).UpdateHighestPriorityThread(
@@ -200,24 +317,210 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
return cores_needing_scheduling;
}
+void KScheduler::SwitchThread(KThread* next_thread) {
+ KProcess* const cur_process = kernel.CurrentProcess();
+ KThread* const cur_thread = GetCurrentThreadPointer(kernel);
+
+ // We never want to schedule a null thread, so use the idle thread if we don't have a next.
+ if (next_thread == nullptr) {
+ next_thread = m_idle_thread;
+ }
+
+ if (next_thread->GetCurrentCore() != m_core_id) {
+ next_thread->SetCurrentCore(m_core_id);
+ }
+
+ // If we're not actually switching thread, there's nothing to do.
+ if (next_thread == cur_thread) {
+ return;
+ }
+
+ // Next thread is now known not to be nullptr, and must not be dispatchable.
+ ASSERT(next_thread->GetDisableDispatchCount() == 1);
+ ASSERT(!next_thread->IsDummyThread());
+
+ // Update the CPU time tracking variables.
+ const s64 prev_tick = m_last_context_switch_time;
+ const s64 cur_tick = kernel.System().CoreTiming().GetCPUTicks();
+ const s64 tick_diff = cur_tick - prev_tick;
+ cur_thread->AddCpuTime(m_core_id, tick_diff);
+ if (cur_process != nullptr) {
+ cur_process->UpdateCPUTimeTicks(tick_diff);
+ }
+ m_last_context_switch_time = cur_tick;
+
+ // Update our previous thread.
+ if (cur_process != nullptr) {
+ if (!cur_thread->IsTerminationRequested() && cur_thread->GetActiveCore() == m_core_id)
+ [[likely]] {
+ m_state.prev_thread = cur_thread;
+ } else {
+ m_state.prev_thread = nullptr;
+ }
+ }
+
+ // Switch the current process, if we're switching processes.
+ // if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) {
+ // KProcess::Switch(cur_process, next_process);
+ // }
+
+ // Set the new thread.
+ SetCurrentThread(kernel, next_thread);
+ m_current_thread = next_thread;
+
+ // Set the new Thread Local region.
+ // cpu::SwitchThreadLocalRegion(GetInteger(next_thread->GetThreadLocalRegionAddress()));
+}
+
+void KScheduler::ScheduleImpl() {
+ // First, clear the needs scheduling bool.
+ m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
+
+ // Load the appropriate thread pointers for scheduling.
+ KThread* const cur_thread{GetCurrentThreadPointer(kernel)};
+ KThread* highest_priority_thread{m_state.highest_priority_thread};
+
+ // Check whether there are runnable interrupt tasks.
+ if (m_state.interrupt_task_runnable) {
+ // The interrupt task is runnable.
+ // We want to switch to the interrupt task/idle thread.
+ highest_priority_thread = nullptr;
+ }
+
+ // If there aren't, we want to check if the highest priority thread is the same as the current
+ // thread.
+ if (highest_priority_thread == cur_thread) {
+ // If they're the same, then we can just return.
+ return;
+ }
+
+ // The highest priority thread is not the same as the current thread.
+ // Jump to the switcher and continue executing from there.
+ m_switch_cur_thread = cur_thread;
+ m_switch_highest_priority_thread = highest_priority_thread;
+ m_switch_from_schedule = true;
+ Common::Fiber::YieldTo(cur_thread->host_context, *m_switch_fiber);
+
+ // Returning from ScheduleImpl occurs after this thread has been scheduled again.
+}
+
+void KScheduler::ScheduleImplFiber() {
+ KThread* const cur_thread{m_switch_cur_thread};
+ KThread* highest_priority_thread{m_switch_highest_priority_thread};
+
+ // If we're not coming from scheduling (i.e., we came from SC preemption),
+ // we should restart the scheduling loop directly. Not accurate to HOS.
+ if (!m_switch_from_schedule) {
+ goto retry;
+ }
+
+ // Mark that we are not coming from scheduling anymore.
+ m_switch_from_schedule = false;
+
+ // Save the original thread context.
+ Unload(cur_thread);
+
+ // The current thread's context has been entirely taken care of.
+ // Now we want to loop until we successfully switch the thread context.
+ while (true) {
+ // We're starting to try to do the context switch.
+ // Check if the highest priority thread is null.
+ if (!highest_priority_thread) {
+ // The next thread is nullptr!
+
+ // Switch to the idle thread. Note: HOS treats idling as a special case for
+ // performance. This is not *required* for yuzu's purposes, and for singlecore
+ // compatibility, we can just move the logic that would go here into the execution
+ // of the idle thread. If we ever remove singlecore, we should implement this
+ // accurately to HOS.
+ highest_priority_thread = m_idle_thread;
+ }
+
+ // We want to try to lock the highest priority thread's context.
+ // Try to take it.
+ while (!highest_priority_thread->context_guard.try_lock()) {
+ // The highest priority thread's context is already locked.
+ // Check if we need scheduling. If we don't, we can retry directly.
+ if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
+ // If we do, another core is interfering, and we must start again.
+ goto retry;
+ }
+ }
+
+ // It's time to switch the thread.
+ // Switch to the highest priority thread.
+ SwitchThread(highest_priority_thread);
+
+ // Check if we need scheduling. If we do, then we can't complete the switch and should
+ // retry.
+ if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
+ // Our switch failed.
+ // We should unlock the thread context, and then retry.
+ highest_priority_thread->context_guard.unlock();
+ goto retry;
+ } else {
+ break;
+ }
+
+ retry:
+
+ // We failed to successfully do the context switch, and need to retry.
+ // Clear needs_scheduling.
+ m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
+
+ // Refresh the highest priority thread.
+ highest_priority_thread = m_state.highest_priority_thread;
+ }
+
+ // Reload the guest thread context.
+ Reload(highest_priority_thread);
+
+ // Reload the host thread.
+ Common::Fiber::YieldTo(m_switch_fiber, *highest_priority_thread->host_context);
+}
+
+void KScheduler::Unload(KThread* thread) {
+ auto& cpu_core = kernel.System().ArmInterface(m_core_id);
+ cpu_core.SaveContext(thread->GetContext32());
+ cpu_core.SaveContext(thread->GetContext64());
+ // Save the TPIDR_EL0 system register in case it was modified.
+ thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
+ cpu_core.ClearExclusiveState();
+
+ // Check if the thread is terminated by checking the DPC flags.
+ if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) {
+ // The thread isn't terminated, so we want to unlock it.
+ thread->context_guard.unlock();
+ }
+}
+
+void KScheduler::Reload(KThread* thread) {
+ auto& cpu_core = kernel.System().ArmInterface(m_core_id);
+ cpu_core.LoadContext(thread->GetContext32());
+ cpu_core.LoadContext(thread->GetContext64());
+ cpu_core.SetTlsAddress(thread->GetTLSAddress());
+ cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
+ cpu_core.LoadWatchpointArray(thread->GetOwnerProcess()->GetWatchpoints());
+ cpu_core.ClearExclusiveState();
+}
+
void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; ++i) {
// Get an atomic reference to the core scheduler's previous thread.
- std::atomic_ref<KThread*> prev_thread(kernel.Scheduler(static_cast<s32>(i)).prev_thread);
- static_assert(std::atomic_ref<KThread*>::is_always_lock_free);
+ auto& prev_thread{kernel.Scheduler(i).m_state.prev_thread};
// Atomically clear the previous thread if it's our target.
KThread* compare = thread;
- prev_thread.compare_exchange_strong(compare, nullptr);
+ prev_thread.compare_exchange_strong(compare, nullptr, std::memory_order_seq_cst);
}
}
void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// Check if the state has changed, because if it hasn't there's nothing to do.
- const auto cur_state = thread->GetRawState();
+ const ThreadState cur_state = thread->GetRawState();
if (cur_state == old_state) {
return;
}
@@ -237,12 +540,12 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, Threa
}
void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// If the thread is runnable, we want to change its priority in the queue.
if (thread->GetRawState() == ThreadState::Runnable) {
GetPriorityQueue(kernel).ChangePriority(old_priority,
- thread == kernel.GetCurrentEmuThread(), thread);
+ thread == GetCurrentThreadPointer(kernel), thread);
IncrementScheduledCount(thread);
SetSchedulerUpdateNeeded(kernel);
}
@@ -250,7 +553,7 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s3
void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// If the thread is runnable, we want to change its affinity in the queue.
if (thread->GetRawState() == ThreadState::Runnable) {
@@ -260,15 +563,14 @@ void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread
}
}
-void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
- ASSERT(system.GlobalSchedulerContext().IsLocked());
+void KScheduler::RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 priority) {
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// Get a reference to the priority queue.
- auto& kernel = system.Kernel();
auto& priority_queue = GetPriorityQueue(kernel);
// Rotate the front of the queue to the end.
- KThread* top_thread = priority_queue.GetScheduledFront(cpu_core_id, priority);
+ KThread* top_thread = priority_queue.GetScheduledFront(core_id, priority);
KThread* next_thread = nullptr;
if (top_thread != nullptr) {
next_thread = priority_queue.MoveToScheduledBack(top_thread);
@@ -280,7 +582,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
// While we have a suggested thread, try to migrate it!
{
- KThread* suggested = priority_queue.GetSuggestedFront(cpu_core_id, priority);
+ KThread* suggested = priority_queue.GetSuggestedFront(core_id, priority);
while (suggested != nullptr) {
// Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore();
@@ -301,7 +603,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
// to the front of the queue.
if (top_on_suggested_core == nullptr ||
top_on_suggested_core->GetPriority() >= HighestCoreMigrationAllowedPriority) {
- suggested->SetActiveCore(cpu_core_id);
+ suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(suggested_core, suggested, true);
IncrementScheduledCount(suggested);
break;
@@ -309,22 +611,21 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
}
// Get the next suggestion.
- suggested = priority_queue.GetSamePriorityNext(cpu_core_id, suggested);
+ suggested = priority_queue.GetSamePriorityNext(core_id, suggested);
}
}
// Now that we might have migrated a thread with the same priority, check if we can do better.
-
{
- KThread* best_thread = priority_queue.GetScheduledFront(cpu_core_id);
- if (best_thread == GetCurrentThread()) {
- best_thread = priority_queue.GetScheduledNext(cpu_core_id, best_thread);
+ KThread* best_thread = priority_queue.GetScheduledFront(core_id);
+ if (best_thread == GetCurrentThreadPointer(kernel)) {
+ best_thread = priority_queue.GetScheduledNext(core_id, best_thread);
}
// If the best thread we can choose has a priority the same or worse than ours, try to
// migrate a higher priority thread.
if (best_thread != nullptr && best_thread->GetPriority() >= priority) {
- KThread* suggested = priority_queue.GetSuggestedFront(cpu_core_id);
+ KThread* suggested = priority_queue.GetSuggestedFront(core_id);
while (suggested != nullptr) {
// If the suggestion's priority is the same as ours, don't bother.
if (suggested->GetPriority() >= best_thread->GetPriority()) {
@@ -343,7 +644,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
if (top_on_suggested_core == nullptr ||
top_on_suggested_core->GetPriority() >=
HighestCoreMigrationAllowedPriority) {
- suggested->SetActiveCore(cpu_core_id);
+ suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(suggested_core, suggested, true);
IncrementScheduledCount(suggested);
break;
@@ -351,7 +652,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
}
// Get the next suggestion.
- suggested = priority_queue.GetSuggestedNext(cpu_core_id, suggested);
+ suggested = priority_queue.GetSuggestedNext(core_id, suggested);
}
}
}
@@ -360,71 +661,13 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
SetSchedulerUpdateNeeded(kernel);
}
-bool KScheduler::CanSchedule(KernelCore& kernel) {
- return kernel.GetCurrentEmuThread()->GetDisableDispatchCount() <= 1;
-}
-
-bool KScheduler::IsSchedulerUpdateNeeded(const KernelCore& kernel) {
- return kernel.GlobalSchedulerContext().scheduler_update_needed.load(std::memory_order_acquire);
-}
-
-void KScheduler::SetSchedulerUpdateNeeded(KernelCore& kernel) {
- kernel.GlobalSchedulerContext().scheduler_update_needed.store(true, std::memory_order_release);
-}
-
-void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) {
- kernel.GlobalSchedulerContext().scheduler_update_needed.store(false, std::memory_order_release);
-}
-
-void KScheduler::DisableScheduling(KernelCore& kernel) {
- // If we are shutting down the kernel, none of this is relevant anymore.
- if (kernel.IsShuttingDown()) {
- return;
- }
-
- ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0);
- GetCurrentThreadPointer(kernel)->DisableDispatch();
-}
-
-void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
- // If we are shutting down the kernel, none of this is relevant anymore.
- if (kernel.IsShuttingDown()) {
- return;
- }
-
- auto* current_thread = GetCurrentThreadPointer(kernel);
-
- ASSERT(current_thread->GetDisableDispatchCount() >= 1);
-
- if (current_thread->GetDisableDispatchCount() > 1) {
- current_thread->EnableDispatch();
- } else {
- RescheduleCores(kernel, cores_needing_scheduling);
- }
-
- // Special case to ensure dummy threads that are waiting block.
- current_thread->IfDummyThreadTryWait();
-}
-
-u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
- if (IsSchedulerUpdateNeeded(kernel)) {
- return UpdateHighestPriorityThreadsImpl(kernel);
- } else {
- return 0;
- }
-}
-
-KSchedulerPriorityQueue& KScheduler::GetPriorityQueue(KernelCore& kernel) {
- return kernel.GlobalSchedulerContext().priority_queue;
-}
-
void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
// Validate preconditions.
ASSERT(CanSchedule(kernel));
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
- KThread& cur_thread = Kernel::GetCurrentThread(kernel);
+ KThread& cur_thread = GetCurrentThread(kernel);
KProcess& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -437,7 +680,7 @@ void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
// Perform the yield.
{
- KScopedSchedulerLock lock(kernel);
+ KScopedSchedulerLock sl{kernel};
const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) {
@@ -463,7 +706,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
- KThread& cur_thread = Kernel::GetCurrentThread(kernel);
+ KThread& cur_thread = GetCurrentThread(kernel);
KProcess& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -476,7 +719,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
// Perform the yield.
{
- KScopedSchedulerLock lock(kernel);
+ KScopedSchedulerLock sl{kernel};
const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) {
@@ -496,7 +739,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
if (KThread* running_on_suggested_core =
(suggested_core >= 0)
- ? kernel.Scheduler(suggested_core).state.highest_priority_thread
+ ? kernel.Scheduler(suggested_core).m_state.highest_priority_thread
: nullptr;
running_on_suggested_core != suggested) {
// If the current thread's priority is higher than our suggestion's we prefer
@@ -551,7 +794,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) {
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
- KThread& cur_thread = Kernel::GetCurrentThread(kernel);
+ KThread& cur_thread = GetCurrentThread(kernel);
KProcess& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -564,7 +807,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) {
// Perform the yield.
{
- KScopedSchedulerLock lock(kernel);
+ KScopedSchedulerLock sl{kernel};
const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) {
@@ -621,219 +864,19 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) {
}
}
-KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, core_id{core_id_} {
- switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this);
- state.needs_scheduling.store(true);
- state.interrupt_task_thread_runnable = false;
- state.should_count_idle = false;
- state.idle_count = 0;
- state.idle_thread_stack = nullptr;
- state.highest_priority_thread = nullptr;
-}
-
-void KScheduler::Finalize() {
- if (idle_thread) {
- idle_thread->Close();
- idle_thread = nullptr;
- }
-}
-
-KScheduler::~KScheduler() {
- ASSERT(!idle_thread);
-}
-
-KThread* KScheduler::GetCurrentThread() const {
- if (auto result = current_thread.load(); result) {
- return result;
- }
- return idle_thread;
-}
-
-u64 KScheduler::GetLastContextSwitchTicks() const {
- return last_context_switch_time;
-}
-
-void KScheduler::RescheduleCurrentCore() {
- ASSERT(GetCurrentThread()->GetDisableDispatchCount() == 1);
-
- auto& phys_core = system.Kernel().PhysicalCore(core_id);
- if (phys_core.IsInterrupted()) {
- phys_core.ClearInterrupt();
+void KScheduler::RescheduleOtherCores(u64 cores_needing_scheduling) {
+ if (const u64 core_mask = cores_needing_scheduling & ~(1ULL << m_core_id); core_mask != 0) {
+ RescheduleCores(kernel, core_mask);
}
-
- guard.Lock();
- if (state.needs_scheduling.load()) {
- Schedule();
- } else {
- GetCurrentThread()->EnableDispatch();
- guard.Unlock();
- }
-}
-
-void KScheduler::OnThreadStart() {
- SwitchContextStep2();
}
-void KScheduler::Unload(KThread* thread) {
- ASSERT(thread);
-
- LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
-
- if (thread->IsCallingSvc()) {
- thread->ClearIsCallingSvc();
- }
-
- auto& physical_core = system.Kernel().PhysicalCore(core_id);
- if (!physical_core.IsInitialized()) {
- return;
- }
-
- Core::ARM_Interface& cpu_core = physical_core.ArmInterface();
- cpu_core.SaveContext(thread->GetContext32());
- cpu_core.SaveContext(thread->GetContext64());
- // Save the TPIDR_EL0 system register in case it was modified.
- thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
- cpu_core.ClearExclusiveState();
-
- if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) {
- prev_thread = thread;
- } else {
- prev_thread = nullptr;
- }
-
- thread->context_guard.unlock();
-}
-
-void KScheduler::Reload(KThread* thread) {
- LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread->GetName());
-
- Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
- cpu_core.LoadContext(thread->GetContext32());
- cpu_core.LoadContext(thread->GetContext64());
- cpu_core.SetTlsAddress(thread->GetTLSAddress());
- cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
- cpu_core.ClearExclusiveState();
-}
-
-void KScheduler::SwitchContextStep2() {
- // Load context of new thread
- Reload(GetCurrentThread());
-
- RescheduleCurrentCore();
-}
-
-void KScheduler::ScheduleImpl() {
- KThread* previous_thread = GetCurrentThread();
- KThread* next_thread = state.highest_priority_thread;
-
- state.needs_scheduling.store(false);
-
- // We never want to schedule a null thread, so use the idle thread if we don't have a next.
- if (next_thread == nullptr) {
- next_thread = idle_thread;
- }
-
- if (next_thread->GetCurrentCore() != core_id) {
- next_thread->SetCurrentCore(core_id);
- }
-
- // We never want to schedule a dummy thread, as these are only used by host threads for locking.
- if (next_thread->GetThreadType() == ThreadType::Dummy) {
- ASSERT_MSG(false, "Dummy threads should never be scheduled!");
- next_thread = idle_thread;
- }
-
- // If we're not actually switching thread, there's nothing to do.
- if (next_thread == current_thread.load()) {
- previous_thread->EnableDispatch();
- guard.Unlock();
- return;
- }
-
- // Update the CPU time tracking variables.
- KProcess* const previous_process = system.Kernel().CurrentProcess();
- UpdateLastContextSwitchTime(previous_thread, previous_process);
-
- // Save context for previous thread
- Unload(previous_thread);
-
- std::shared_ptr<Common::Fiber>* old_context;
- old_context = &previous_thread->GetHostContext();
-
- // Set the new thread.
- current_thread.store(next_thread);
-
- guard.Unlock();
-
- Common::Fiber::YieldTo(*old_context, *switch_fiber);
- /// When a thread wakes up, the scheduler may have changed to other in another core.
- auto& next_scheduler = *system.Kernel().CurrentScheduler();
- next_scheduler.SwitchContextStep2();
-}
-
-void KScheduler::OnSwitch(void* this_scheduler) {
- KScheduler* sched = static_cast<KScheduler*>(this_scheduler);
- sched->SwitchToCurrent();
-}
-
-void KScheduler::SwitchToCurrent() {
- while (true) {
- {
- KScopedSpinLock lk{guard};
- current_thread.store(state.highest_priority_thread);
- state.needs_scheduling.store(false);
+void KScheduler::RescheduleCores(KernelCore& kernel, u64 core_mask) {
+ // Send IPI
+ for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
+ if (core_mask & (1ULL << i)) {
+ kernel.PhysicalCore(i).Interrupt();
}
- const auto is_switch_pending = [this] {
- KScopedSpinLock lk{guard};
- return state.needs_scheduling.load();
- };
- do {
- auto next_thread = current_thread.load();
- if (next_thread != nullptr) {
- const auto locked = next_thread->context_guard.try_lock();
- if (state.needs_scheduling.load()) {
- next_thread->context_guard.unlock();
- break;
- }
- if (next_thread->GetActiveCore() != core_id) {
- next_thread->context_guard.unlock();
- break;
- }
- if (!locked) {
- continue;
- }
- }
- auto thread = next_thread ? next_thread : idle_thread;
- Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
- } while (!is_switch_pending());
}
}
-void KScheduler::UpdateLastContextSwitchTime(KThread* thread, KProcess* process) {
- const u64 prev_switch_ticks = last_context_switch_time;
- const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks();
- const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
-
- if (thread != nullptr) {
- thread->AddCpuTime(core_id, update_ticks);
- }
-
- if (process != nullptr) {
- process->UpdateCPUTimeTicks(update_ticks);
- }
-
- last_context_switch_time = most_recent_switch_ticks;
-}
-
-void KScheduler::Initialize() {
- idle_thread = KThread::Create(system.Kernel());
- ASSERT(KThread::InitializeIdleThread(system, idle_thread, core_id).IsSuccess());
- idle_thread->SetName(fmt::format("IdleThread:{}", core_id));
-}
-
-KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel)
- : KScopedLock(kernel.GlobalSchedulerContext().SchedulerLock()) {}
-
-KScopedSchedulerLock::~KScopedSchedulerLock() = default;
-
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h
index 729e006f2..534321d8d 100644
--- a/src/core/hle/kernel/k_scheduler.h
+++ b/src/core/hle/kernel/k_scheduler.h
@@ -11,6 +11,7 @@
#include "core/hle/kernel/k_scheduler_lock.h"
#include "core/hle/kernel/k_scoped_lock.h"
#include "core/hle/kernel/k_spin_lock.h"
+#include "core/hle/kernel/k_thread.h"
namespace Common {
class Fiber;
@@ -23,188 +24,150 @@ class System;
namespace Kernel {
class KernelCore;
+class KInterruptTaskManager;
class KProcess;
-class SchedulerLock;
class KThread;
+class KScopedDisableDispatch;
+class KScopedSchedulerLock;
+class KScopedSchedulerLockAndSleep;
class KScheduler final {
public:
- explicit KScheduler(Core::System& system_, s32 core_id_);
- ~KScheduler();
-
- void Finalize();
+ YUZU_NON_COPYABLE(KScheduler);
+ YUZU_NON_MOVEABLE(KScheduler);
- /// Reschedules to the next available thread (call after current thread is suspended)
- void RescheduleCurrentCore();
+ using LockType = KAbstractSchedulerLock<KScheduler>;
- /// Reschedules cores pending reschedule, to be called on EnableScheduling.
- static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule);
+ explicit KScheduler(KernelCore& kernel);
+ ~KScheduler();
- /// The next two are for SingleCore Only.
- /// Unload current thread before preempting core.
+ void Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id);
+ void Activate();
+ void OnThreadStart();
void Unload(KThread* thread);
-
- /// Reload current thread after core preemption.
void Reload(KThread* thread);
- /// Gets the current running thread
- [[nodiscard]] KThread* GetCurrentThread() const;
+ void SetInterruptTaskRunnable();
+ void RequestScheduleOnInterrupt();
+ void PreemptSingleCore();
- /// Gets the idle thread
- [[nodiscard]] KThread* GetIdleThread() const {
- return idle_thread;
+ u64 GetIdleCount() {
+ return m_state.idle_count;
}
- /// Returns true if the scheduler is idle
- [[nodiscard]] bool IsIdle() const {
- return GetCurrentThread() == idle_thread;
+ KThread* GetIdleThread() const {
+ return m_idle_thread;
}
- /// Gets the timestamp for the last context switch in ticks.
- [[nodiscard]] u64 GetLastContextSwitchTicks() const;
-
- [[nodiscard]] bool ContextSwitchPending() const {
- return state.needs_scheduling.load(std::memory_order_relaxed);
+ bool IsIdle() const {
+ return m_current_thread.load() == m_idle_thread;
}
- void Initialize();
-
- void OnThreadStart();
+ KThread* GetPreviousThread() const {
+ return m_state.prev_thread;
+ }
- [[nodiscard]] std::shared_ptr<Common::Fiber>& ControlContext() {
- return switch_fiber;
+ KThread* GetSchedulerCurrentThread() const {
+ return m_current_thread.load();
}
- [[nodiscard]] const std::shared_ptr<Common::Fiber>& ControlContext() const {
- return switch_fiber;
+ s64 GetLastContextSwitchTime() const {
+ return m_last_context_switch_time;
}
- [[nodiscard]] u64 UpdateHighestPriorityThread(KThread* highest_thread);
+ // Static public API.
+ static bool CanSchedule(KernelCore& kernel) {
+ return GetCurrentThread(kernel).GetDisableDispatchCount() == 0;
+ }
+ static bool IsSchedulerLockedByCurrentThread(KernelCore& kernel) {
+ return kernel.GlobalSchedulerContext().scheduler_lock.IsLockedByCurrentThread();
+ }
- /**
- * Takes a thread and moves it to the back of the it's priority list.
- *
- * @note This operation can be redundant and no scheduling is changed if marked as so.
- */
- static void YieldWithoutCoreMigration(KernelCore& kernel);
+ static bool IsSchedulerUpdateNeeded(KernelCore& kernel) {
+ return kernel.GlobalSchedulerContext().scheduler_update_needed;
+ }
+ static void SetSchedulerUpdateNeeded(KernelCore& kernel) {
+ kernel.GlobalSchedulerContext().scheduler_update_needed = true;
+ }
+ static void ClearSchedulerUpdateNeeded(KernelCore& kernel) {
+ kernel.GlobalSchedulerContext().scheduler_update_needed = false;
+ }
- /**
- * 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.
- *
- * @note This operation can be redundant and no scheduling is changed if marked as so.
- */
- static void YieldWithCoreMigration(KernelCore& kernel);
+ static void DisableScheduling(KernelCore& kernel);
+ static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling);
- /**
- * Takes a thread and moves it out of the scheduling queue.
- * and into the suggested queue. If no thread can be scheduled afterwards in that core,
- * a suggested thread is obtained instead.
- *
- * @note This operation can be redundant and no scheduling is changed if marked as so.
- */
- static void YieldToAnyThread(KernelCore& kernel);
+ static u64 UpdateHighestPriorityThreads(KernelCore& kernel);
static void ClearPreviousThread(KernelCore& kernel, KThread* thread);
- /// Notify the scheduler a thread's status has changed.
static void OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state);
-
- /// Notify the scheduler a thread's priority has changed.
static void OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority);
-
- /// Notify the scheduler a thread's core and/or affinity mask has changed.
static void OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core);
- static bool CanSchedule(KernelCore& kernel);
- static bool IsSchedulerUpdateNeeded(const KernelCore& kernel);
- static void SetSchedulerUpdateNeeded(KernelCore& kernel);
- static void ClearSchedulerUpdateNeeded(KernelCore& kernel);
- static void DisableScheduling(KernelCore& kernel);
- static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling);
- [[nodiscard]] static u64 UpdateHighestPriorityThreads(KernelCore& kernel);
+ static void RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 priority);
+ static void RescheduleCores(KernelCore& kernel, u64 cores_needing_scheduling);
+
+ static void YieldWithoutCoreMigration(KernelCore& kernel);
+ static void YieldWithCoreMigration(KernelCore& kernel);
+ static void YieldToAnyThread(KernelCore& kernel);
private:
- friend class GlobalSchedulerContext;
-
- /**
- * Takes care of selecting the new scheduled threads in three steps:
- *
- * 1. 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.
- *
- * 2. 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.
- *
- * 3. 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.
- *
- * returns the cores needing scheduling.
- */
- [[nodiscard]] static u64 UpdateHighestPriorityThreadsImpl(KernelCore& kernel);
-
- [[nodiscard]] static KSchedulerPriorityQueue& GetPriorityQueue(KernelCore& kernel);
-
- void RotateScheduledQueue(s32 cpu_core_id, s32 priority);
-
- void Schedule() {
- ASSERT(GetCurrentThread()->GetDisableDispatchCount() == 1);
- this->ScheduleImpl();
+ // Static private API.
+ static KSchedulerPriorityQueue& GetPriorityQueue(KernelCore& kernel) {
+ return kernel.GlobalSchedulerContext().priority_queue;
}
+ static u64 UpdateHighestPriorityThreadsImpl(KernelCore& kernel);
- /// Switches the CPU's active thread context to that of the specified thread
- void ScheduleImpl();
-
- /// When a thread wakes up, it must run this through it's new scheduler
- void SwitchContextStep2();
+ static void RescheduleCurrentHLEThread(KernelCore& kernel);
- /**
- * Called on every context switch to update the internal timestamp
- * This also updates the running time ticks for the given thread and
- * process using the following difference:
- *
- * ticks += most_recent_ticks - last_context_switch_ticks
- *
- * The internal tick timestamp for the scheduler is simply the
- * most recent tick count retrieved. No special arithmetic is
- * applied to it.
- */
- void UpdateLastContextSwitchTime(KThread* thread, KProcess* process);
+ // Instanced private API.
+ void ScheduleImpl();
+ void ScheduleImplFiber();
+ void SwitchThread(KThread* next_thread);
- static void OnSwitch(void* this_scheduler);
- void SwitchToCurrent();
+ void Schedule();
+ void ScheduleOnInterrupt();
- KThread* prev_thread{};
- std::atomic<KThread*> current_thread{};
+ void RescheduleOtherCores(u64 cores_needing_scheduling);
+ void RescheduleCurrentCore();
+ void RescheduleCurrentCoreImpl();
- KThread* idle_thread{};
+ u64 UpdateHighestPriorityThread(KThread* thread);
- std::shared_ptr<Common::Fiber> switch_fiber{};
+private:
+ friend class KScopedDisableDispatch;
struct SchedulingState {
- std::atomic<bool> needs_scheduling{};
- bool interrupt_task_thread_runnable{};
- bool should_count_idle{};
- u64 idle_count{};
- KThread* highest_priority_thread{};
- void* idle_thread_stack{};
+ std::atomic<bool> needs_scheduling{false};
+ bool interrupt_task_runnable{false};
+ bool should_count_idle{false};
+ u64 idle_count{0};
+ KThread* highest_priority_thread{nullptr};
+ void* idle_thread_stack{nullptr};
+ std::atomic<KThread*> prev_thread{nullptr};
+ KInterruptTaskManager* interrupt_task_manager{nullptr};
};
- SchedulingState state;
-
- Core::System& system;
- u64 last_context_switch_time{};
- const s32 core_id;
-
- KSpinLock guard{};
+ KernelCore& kernel;
+ SchedulingState m_state;
+ bool m_is_active{false};
+ s32 m_core_id{0};
+ s64 m_last_context_switch_time{0};
+ KThread* m_idle_thread{nullptr};
+ std::atomic<KThread*> m_current_thread{nullptr};
+
+ std::shared_ptr<Common::Fiber> m_switch_fiber{};
+ KThread* m_switch_cur_thread{};
+ KThread* m_switch_highest_priority_thread{};
+ bool m_switch_from_schedule{};
};
-class [[nodiscard]] KScopedSchedulerLock : KScopedLock<GlobalSchedulerContext::LockType> {
+class KScopedSchedulerLock : public KScopedLock<KScheduler::LockType> {
public:
- explicit KScopedSchedulerLock(KernelCore& kernel);
- ~KScopedSchedulerLock();
+ explicit KScopedSchedulerLock(KernelCore& kernel)
+ : KScopedLock(kernel.GlobalSchedulerContext().scheduler_lock) {}
+ ~KScopedSchedulerLock() = default;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h
index 4fa256970..73314b45e 100644
--- a/src/core/hle/kernel/k_scheduler_lock.h
+++ b/src/core/hle/kernel/k_scheduler_lock.h
@@ -5,9 +5,11 @@
#include <atomic>
#include "common/assert.h"
+#include "core/hle/kernel/k_interrupt_manager.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/physical_core.h"
namespace Kernel {
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index 60f8ed470..802c646a6 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -79,7 +79,7 @@ std::size_t KServerSession::NumDomainRequestHandlers() const {
return manager->DomainHandlerCount();
}
-ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
+Result KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
if (!context.HasDomainMessageHeader()) {
return ResultSuccess;
}
@@ -123,7 +123,7 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co
return ResultSuccess;
}
-ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) {
+Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) {
u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))};
auto context = std::make_shared<HLERequestContext>(kernel, memory, this, thread);
@@ -143,8 +143,8 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor
return ResultSuccess;
}
-ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
- ResultCode result = ResultSuccess;
+Result KServerSession::CompleteSyncRequest(HLERequestContext& context) {
+ Result result = ResultSuccess;
// If the session has been converted to a domain, handle the domain request
if (manager->HasSessionRequestHandler(context)) {
@@ -173,8 +173,8 @@ ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
return result;
}
-ResultCode KServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
- Core::Timing::CoreTiming& core_timing) {
+Result KServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
+ Core::Timing::CoreTiming& core_timing) {
return QueueSyncRequest(thread, memory);
}
diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h
index b628a843f..6d0821945 100644
--- a/src/core/hle/kernel/k_server_session.h
+++ b/src/core/hle/kernel/k_server_session.h
@@ -73,10 +73,10 @@ public:
* @param memory Memory context to handle the sync request under.
* @param core_timing Core timing context to schedule the request event under.
*
- * @returns ResultCode from the operation.
+ * @returns Result from the operation.
*/
- ResultCode HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
- Core::Timing::CoreTiming& core_timing);
+ Result HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
+ Core::Timing::CoreTiming& core_timing);
/// Adds a new domain request handler to the collection of request handlers within
/// this ServerSession instance.
@@ -103,14 +103,14 @@ public:
private:
/// Queues a sync request from the emulated application.
- ResultCode QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory);
+ Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory);
/// Completes a sync request from the emulated application.
- ResultCode CompleteSyncRequest(HLERequestContext& context);
+ Result CompleteSyncRequest(HLERequestContext& context);
/// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
/// object handle.
- ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context);
+ Result HandleDomainSyncRequest(Kernel::HLERequestContext& context);
/// This session's HLE request handlers
std::shared_ptr<SessionRequestManager> manager;
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp
index 51d7538ca..8ff1545b6 100644
--- a/src/core/hle/kernel/k_shared_memory.cpp
+++ b/src/core/hle/kernel/k_shared_memory.cpp
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "core/core.h"
@@ -18,12 +17,10 @@ KSharedMemory::~KSharedMemory() {
kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size);
}
-ResultCode KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
- KPageLinkedList&& page_list_,
- Svc::MemoryPermission owner_permission_,
- Svc::MemoryPermission user_permission_,
- PAddr physical_address_, std::size_t size_,
- std::string name_) {
+Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
+ KPageGroup&& page_list_, Svc::MemoryPermission owner_permission_,
+ Svc::MemoryPermission user_permission_, PAddr physical_address_,
+ std::size_t size_, std::string name_) {
// Set members.
owner_process = owner_process_;
device_memory = &device_memory_;
@@ -67,8 +64,8 @@ void KSharedMemory::Finalize() {
KAutoObjectWithSlabHeapAndContainer<KSharedMemory, KAutoObjectWithList>::Finalize();
}
-ResultCode KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t map_size,
- Svc::MemoryPermission permissions) {
+Result KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t map_size,
+ Svc::MemoryPermission permissions) {
const u64 page_count{(map_size + PageSize - 1) / PageSize};
if (page_list.GetNumPages() != page_count) {
@@ -86,7 +83,7 @@ ResultCode KSharedMemory::Map(KProcess& target_process, VAddr address, std::size
ConvertToKMemoryPermission(permissions));
}
-ResultCode KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) {
+Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) {
const u64 page_count{(unmap_size + PageSize - 1) / PageSize};
if (page_list.GetNumPages() != page_count) {
diff --git a/src/core/hle/kernel/k_shared_memory.h b/src/core/hle/kernel/k_shared_memory.h
index 81de36136..34cb98456 100644
--- a/src/core/hle/kernel/k_shared_memory.h
+++ b/src/core/hle/kernel/k_shared_memory.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -9,7 +8,7 @@
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_memory_block.h"
-#include "core/hle/kernel/k_page_linked_list.h"
+#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
@@ -26,10 +25,10 @@ public:
explicit KSharedMemory(KernelCore& kernel_);
~KSharedMemory() override;
- ResultCode Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
- KPageLinkedList&& page_list_, Svc::MemoryPermission owner_permission_,
- Svc::MemoryPermission user_permission_, PAddr physical_address_,
- std::size_t size_, std::string name_);
+ Result Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
+ KPageGroup&& page_list_, Svc::MemoryPermission owner_permission_,
+ Svc::MemoryPermission user_permission_, PAddr physical_address_,
+ std::size_t size_, std::string name_);
/**
* Maps a shared memory block to an address in the target process' address space
@@ -38,8 +37,8 @@ public:
* @param map_size Size of the shared memory block to map
* @param permissions Memory block map permissions (specified by SVC field)
*/
- ResultCode Map(KProcess& target_process, VAddr address, std::size_t map_size,
- Svc::MemoryPermission permissions);
+ Result Map(KProcess& target_process, VAddr address, std::size_t map_size,
+ Svc::MemoryPermission permissions);
/**
* Unmaps a shared memory block from an address in the target process' address space
@@ -47,7 +46,7 @@ public:
* @param address Address in system memory to unmap shared memory block
* @param unmap_size Size of the shared memory block to unmap
*/
- ResultCode Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size);
+ Result Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size);
/**
* Gets a pointer to the shared memory block
@@ -77,7 +76,7 @@ public:
private:
Core::DeviceMemory* device_memory;
KProcess* owner_process{};
- KPageLinkedList page_list;
+ KPageGroup page_list;
Svc::MemoryPermission owner_permission{};
Svc::MemoryPermission user_permission{};
PAddr physical_address{};
diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp
index 8554144d5..802dca046 100644
--- a/src/core/hle/kernel/k_synchronization_object.cpp
+++ b/src/core/hle/kernel/k_synchronization_object.cpp
@@ -22,7 +22,7 @@ public:
: KThreadQueueWithoutEndWait(kernel_), m_objects(o), m_nodes(n), m_count(c) {}
void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
- ResultCode wait_result) override {
+ Result wait_result) override {
// Determine the sync index, and unlink all nodes.
s32 sync_index = -1;
for (auto i = 0; i < m_count; ++i) {
@@ -45,8 +45,7 @@ public:
KThreadQueue::EndWait(waiting_thread, wait_result);
}
- void CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task) override {
+ void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove all nodes from our list.
for (auto i = 0; i < m_count; ++i) {
m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
@@ -72,9 +71,9 @@ void KSynchronizationObject::Finalize() {
KAutoObject::Finalize();
}
-ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
- KSynchronizationObject** objects, const s32 num_objects,
- s64 timeout) {
+Result KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
+ KSynchronizationObject** objects, const s32 num_objects,
+ s64 timeout) {
// Allocate space on stack for thread nodes.
std::vector<ThreadListNode> thread_nodes(num_objects);
@@ -148,7 +147,7 @@ KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
KSynchronizationObject::~KSynchronizationObject() = default;
-void KSynchronizationObject::NotifyAvailable(ResultCode result) {
+void KSynchronizationObject::NotifyAvailable(Result result) {
KScopedSchedulerLock sl(kernel);
// If we're not signaled, we've nothing to notify.
diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h
index d7540d6c7..8d8122ab7 100644
--- a/src/core/hle/kernel/k_synchronization_object.h
+++ b/src/core/hle/kernel/k_synchronization_object.h
@@ -24,9 +24,9 @@ public:
KThread* thread{};
};
- [[nodiscard]] static ResultCode Wait(KernelCore& kernel, s32* out_index,
- KSynchronizationObject** objects, const s32 num_objects,
- s64 timeout);
+ [[nodiscard]] static Result Wait(KernelCore& kernel, s32* out_index,
+ KSynchronizationObject** objects, const s32 num_objects,
+ s64 timeout);
void Finalize() override;
@@ -72,7 +72,7 @@ protected:
virtual void OnFinalizeSynchronizationObject() {}
- void NotifyAvailable(ResultCode result);
+ void NotifyAvailable(Result result);
void NotifyAvailable() {
return this->NotifyAvailable(ResultSuccess);
}
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 8d48a7901..174afc80d 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -80,8 +80,7 @@ public:
explicit ThreadQueueImplForKThreadSetProperty(KernelCore& kernel_, KThread::WaiterList* wl)
: KThreadQueue(kernel_), m_wait_list(wl) {}
- void CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task) override {
+ void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove the thread from the wait list.
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
@@ -99,8 +98,8 @@ KThread::KThread(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, activity_pause_lock{kernel_} {}
KThread::~KThread() = default;
-ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio,
- s32 virt_core, KProcess* owner, ThreadType type) {
+Result KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio,
+ s32 virt_core, KProcess* owner, ThreadType type) {
// Assert parameters are valid.
ASSERT((type == ThreadType::Main) || (type == ThreadType::Dummy) ||
(Svc::HighestThreadPriority <= prio && prio <= Svc::LowestThreadPriority));
@@ -225,7 +224,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
// Setup the stack parameters.
StackParameters& sp = GetStackParameters();
sp.cur_thread = this;
- sp.disable_count = 0;
+ sp.disable_count = 1;
SetInExceptionHandler();
// Set thread ID.
@@ -245,46 +244,51 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
return ResultSuccess;
}
-ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg,
- VAddr user_stack_top, s32 prio, s32 core, KProcess* owner,
- ThreadType type, std::function<void(void*)>&& init_func,
- void* init_func_parameter) {
+Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg,
+ VAddr user_stack_top, s32 prio, s32 core, KProcess* owner,
+ ThreadType type, std::function<void()>&& init_func) {
// Initialize the thread.
R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type));
// Initialize emulation parameters.
- thread->host_context =
- std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter);
+ thread->host_context = std::make_shared<Common::Fiber>(std::move(init_func));
thread->is_single_core = !Settings::values.use_multi_core.GetValue();
return ResultSuccess;
}
-ResultCode KThread::InitializeDummyThread(KThread* thread) {
- return thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy);
+Result KThread::InitializeDummyThread(KThread* thread) {
+ // Initialize the thread.
+ R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy));
+
+ // Initialize emulation parameters.
+ thread->stack_parameters.disable_count = 0;
+
+ return ResultSuccess;
}
-ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
+Result KThread::InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core) {
return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main,
- Core::CpuManager::GetIdleThreadStartFunc(),
- system.GetCpuManager().GetStartFuncParameter());
+ system.GetCpuManager().GetGuestActivateFunc());
}
-ResultCode KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread,
- KThreadFunction func, uintptr_t arg,
- s32 virt_core) {
+Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
+ return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main,
+ system.GetCpuManager().GetIdleThreadStartFunc());
+}
+
+Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread,
+ KThreadFunction func, uintptr_t arg, s32 virt_core) {
return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority,
- Core::CpuManager::GetShutdownThreadStartFunc(),
- system.GetCpuManager().GetStartFuncParameter());
+ system.GetCpuManager().GetShutdownThreadStartFunc());
}
-ResultCode KThread::InitializeUserThread(Core::System& system, KThread* thread,
- KThreadFunction func, uintptr_t arg, VAddr user_stack_top,
- s32 prio, s32 virt_core, KProcess* owner) {
+Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func,
+ uintptr_t arg, VAddr user_stack_top, s32 prio, s32 virt_core,
+ KProcess* owner) {
system.Kernel().GlobalSchedulerContext().AddThread(thread);
return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner,
- ThreadType::User, Core::CpuManager::GetGuestThreadStartFunc(),
- system.GetCpuManager().GetStartFuncParameter());
+ ThreadType::User, system.GetCpuManager().GetGuestThreadFunc());
}
void KThread::PostDestroy(uintptr_t arg) {
@@ -315,14 +319,20 @@ void KThread::Finalize() {
auto it = waiter_list.begin();
while (it != waiter_list.end()) {
- // Clear the lock owner
- it->SetLockOwner(nullptr);
+ // Get the thread.
+ KThread* const waiter = std::addressof(*it);
+
+ // The thread shouldn't be a kernel waiter.
+ ASSERT(!IsKernelAddressKey(waiter->GetAddressKey()));
+
+ // Clear the lock owner.
+ waiter->SetLockOwner(nullptr);
// Erase the waiter from our list.
it = waiter_list.erase(it);
// Cancel the thread's wait.
- it->CancelWait(ResultInvalidState, true);
+ waiter->CancelWait(ResultInvalidState, true);
}
}
@@ -382,7 +392,7 @@ void KThread::FinishTermination() {
for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
KThread* core_thread{};
do {
- core_thread = kernel.Scheduler(i).GetCurrentThread();
+ core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread();
} while (core_thread == this);
}
}
@@ -487,9 +497,7 @@ void KThread::Unpin() {
// Resume any threads that began waiting on us while we were pinned.
for (auto it = pinned_waiter_list.begin(); it != pinned_waiter_list.end(); ++it) {
- if (it->GetState() == ThreadState::Waiting) {
- it->SetState(ThreadState::Runnable);
- }
+ it->EndWait(ResultSuccess);
}
}
@@ -523,7 +531,7 @@ void KThread::ClearInterruptFlag() {
memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 0);
}
-ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
+Result KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
KScopedSchedulerLock sl{kernel};
// Get the virtual mask.
@@ -533,7 +541,7 @@ ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
return ResultSuccess;
}
-ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
+Result KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
KScopedSchedulerLock sl{kernel};
ASSERT(num_core_migration_disables >= 0);
@@ -549,7 +557,7 @@ ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_m
return ResultSuccess;
}
-ResultCode KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) {
+Result KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) {
ASSERT(parent != nullptr);
ASSERT(v_affinity_mask != 0);
KScopedLightLock lk(activity_pause_lock);
@@ -631,7 +639,7 @@ ResultCode KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) {
s32 thread_core;
for (thread_core = 0; thread_core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES);
++thread_core) {
- if (kernel.Scheduler(thread_core).GetCurrentThread() == this) {
+ if (kernel.Scheduler(thread_core).GetSchedulerCurrentThread() == this) {
thread_is_current = true;
break;
}
@@ -748,7 +756,20 @@ void KThread::Continue() {
KScheduler::OnThreadStateChanged(kernel, this, old_state);
}
-ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
+void KThread::WaitUntilSuspended() {
+ // Make sure we have a suspend requested.
+ ASSERT(IsSuspendRequested());
+
+ // Loop until the thread is not executing on any core.
+ for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
+ KThread* core_thread{};
+ do {
+ core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread();
+ } while (core_thread == this);
+ }
+}
+
+Result KThread::SetActivity(Svc::ThreadActivity activity) {
// Lock ourselves.
KScopedLightLock lk(activity_pause_lock);
@@ -809,7 +830,7 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
// Check if the thread is currently running.
// If it is, we'll need to retry.
for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
- if (kernel.Scheduler(i).GetCurrentThread() == this) {
+ if (kernel.Scheduler(i).GetSchedulerCurrentThread() == this) {
thread_is_current = true;
break;
}
@@ -821,7 +842,7 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
return ResultSuccess;
}
-ResultCode KThread::GetThreadContext3(std::vector<u8>& out) {
+Result KThread::GetThreadContext3(std::vector<u8>& out) {
// Lock ourselves.
KScopedLightLock lk{activity_pause_lock};
@@ -871,6 +892,7 @@ void KThread::AddWaiterImpl(KThread* thread) {
// Keep track of how many kernel waiters we have.
if (IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT((num_kernel_waiters++) >= 0);
+ KScheduler::SetSchedulerUpdateNeeded(kernel);
}
// Insert the waiter.
@@ -884,6 +906,7 @@ void KThread::RemoveWaiterImpl(KThread* thread) {
// Keep track of how many kernel waiters we have.
if (IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT((num_kernel_waiters--) > 0);
+ KScheduler::SetSchedulerUpdateNeeded(kernel);
}
// Remove the waiter.
@@ -959,6 +982,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
// Keep track of how many kernel waiters we have.
if (IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT((num_kernel_waiters--) > 0);
+ KScheduler::SetSchedulerUpdateNeeded(kernel);
}
it = waiter_list.erase(it);
@@ -986,7 +1010,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
return next_lock_owner;
}
-ResultCode KThread::Run() {
+Result KThread::Run() {
while (true) {
KScopedSchedulerLock lk{kernel};
@@ -1014,8 +1038,6 @@ ResultCode KThread::Run() {
// Set our state and finish.
SetState(ThreadState::Runnable);
- DisableDispatch();
-
return ResultSuccess;
}
}
@@ -1047,9 +1069,11 @@ void KThread::Exit() {
// Register the thread as a work task.
KWorkerTaskManager::AddTask(kernel, KWorkerTaskManager::WorkerType::Exit, this);
}
+
+ UNREACHABLE_MSG("KThread::Exit() would return");
}
-ResultCode KThread::Sleep(s64 timeout) {
+Result KThread::Sleep(s64 timeout) {
ASSERT(!kernel.GlobalSchedulerContext().IsLocked());
ASSERT(this == GetCurrentThreadPointer(kernel));
ASSERT(timeout > 0);
@@ -1082,6 +1106,8 @@ void KThread::IfDummyThreadTryWait() {
return;
}
+ ASSERT(!kernel.IsPhantomModeForSingleCore());
+
// Block until we are no longer waiting.
std::unique_lock lk(dummy_wait_lock);
dummy_wait_cv.wait(
@@ -1105,7 +1131,7 @@ void KThread::BeginWait(KThreadQueue* queue) {
wait_queue = queue;
}
-void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
+void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, Result wait_result_) {
// Lock the scheduler.
KScopedSchedulerLock sl(kernel);
@@ -1115,7 +1141,7 @@ void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCod
}
}
-void KThread::EndWait(ResultCode wait_result_) {
+void KThread::EndWait(Result wait_result_) {
// Lock the scheduler.
KScopedSchedulerLock sl(kernel);
@@ -1134,7 +1160,7 @@ void KThread::EndWait(ResultCode wait_result_) {
}
}
-void KThread::CancelWait(ResultCode wait_result_, bool cancel_timer_task) {
+void KThread::CancelWait(Result wait_result_, bool cancel_timer_task) {
// Lock the scheduler.
KScopedSchedulerLock sl(kernel);
@@ -1164,6 +1190,10 @@ std::shared_ptr<Common::Fiber>& KThread::GetHostContext() {
return host_context;
}
+void SetCurrentThread(KernelCore& kernel, KThread* thread) {
+ kernel.SetCurrentEmuThread(thread);
+}
+
KThread* GetCurrentThreadPointer(KernelCore& kernel) {
return kernel.GetCurrentEmuThread();
}
@@ -1182,16 +1212,13 @@ KScopedDisableDispatch::~KScopedDisableDispatch() {
return;
}
- // Skip the reschedule if single-core, as dispatch tracking is disabled here.
- if (!Settings::values.use_multi_core.GetValue()) {
- return;
- }
-
if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) {
- auto scheduler = kernel.CurrentScheduler();
+ auto* scheduler = kernel.CurrentScheduler();
- if (scheduler) {
+ if (scheduler && !kernel.IsPhantomModeForSingleCore()) {
scheduler->RescheduleCurrentCore();
+ } else {
+ KScheduler::RescheduleCurrentHLEThread(kernel);
}
} else {
GetCurrentThread(kernel).EnableDispatch();
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index f4d83f99a..9ee20208e 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -106,6 +106,7 @@ enum class StepState : u32 {
StepPerformed, ///< Thread has stepped, waiting to be scheduled again
};
+void SetCurrentThread(KernelCore& kernel, KThread* thread);
[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel);
[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
@@ -175,7 +176,7 @@ public:
void SetBasePriority(s32 value);
- [[nodiscard]] ResultCode Run();
+ [[nodiscard]] Result Run();
void Exit();
@@ -207,6 +208,8 @@ public:
void Continue();
+ void WaitUntilSuspended();
+
constexpr void SetSyncedIndex(s32 index) {
synced_index = index;
}
@@ -215,11 +218,11 @@ public:
return synced_index;
}
- constexpr void SetWaitResult(ResultCode wait_res) {
+ constexpr void SetWaitResult(Result wait_res) {
wait_result = wait_res;
}
- [[nodiscard]] constexpr ResultCode GetWaitResult() const {
+ [[nodiscard]] constexpr Result GetWaitResult() const {
return wait_result;
}
@@ -342,15 +345,15 @@ public:
return physical_affinity_mask;
}
- [[nodiscard]] ResultCode GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
+ [[nodiscard]] Result GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
- [[nodiscard]] ResultCode GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
+ [[nodiscard]] Result GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
- [[nodiscard]] ResultCode SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask);
+ [[nodiscard]] Result SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask);
- [[nodiscard]] ResultCode SetActivity(Svc::ThreadActivity activity);
+ [[nodiscard]] Result SetActivity(Svc::ThreadActivity activity);
- [[nodiscard]] ResultCode Sleep(s64 timeout);
+ [[nodiscard]] Result Sleep(s64 timeout);
[[nodiscard]] s64 GetYieldScheduleCount() const {
return schedule_count;
@@ -408,20 +411,22 @@ public:
static void PostDestroy(uintptr_t arg);
- [[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread);
+ [[nodiscard]] static Result InitializeDummyThread(KThread* thread);
+
+ [[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread,
+ s32 virt_core);
- [[nodiscard]] static ResultCode InitializeIdleThread(Core::System& system, KThread* thread,
- s32 virt_core);
+ [[nodiscard]] static Result InitializeIdleThread(Core::System& system, KThread* thread,
+ s32 virt_core);
- [[nodiscard]] static ResultCode InitializeHighPriorityThread(Core::System& system,
- KThread* thread,
- KThreadFunction func,
- uintptr_t arg, s32 virt_core);
+ [[nodiscard]] static Result InitializeHighPriorityThread(Core::System& system, KThread* thread,
+ KThreadFunction func, uintptr_t arg,
+ s32 virt_core);
- [[nodiscard]] static ResultCode InitializeUserThread(Core::System& system, KThread* thread,
- KThreadFunction func, uintptr_t arg,
- VAddr user_stack_top, s32 prio,
- s32 virt_core, KProcess* owner);
+ [[nodiscard]] static Result InitializeUserThread(Core::System& system, KThread* thread,
+ KThreadFunction func, uintptr_t arg,
+ VAddr user_stack_top, s32 prio, s32 virt_core,
+ KProcess* owner);
public:
struct StackParameters {
@@ -478,39 +483,16 @@ public:
return per_core_priority_queue_entry[core];
}
- [[nodiscard]] bool IsKernelThread() const {
- return GetActiveCore() == 3;
- }
-
- [[nodiscard]] bool IsDispatchTrackingDisabled() const {
- return is_single_core || IsKernelThread();
- }
-
[[nodiscard]] s32 GetDisableDispatchCount() const {
- if (IsDispatchTrackingDisabled()) {
- // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
- return 1;
- }
-
return this->GetStackParameters().disable_count;
}
void DisableDispatch() {
- if (IsDispatchTrackingDisabled()) {
- // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
- return;
- }
-
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
this->GetStackParameters().disable_count++;
}
void EnableDispatch() {
- if (IsDispatchTrackingDisabled()) {
- // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
- return;
- }
-
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
this->GetStackParameters().disable_count--;
}
@@ -607,7 +589,7 @@ public:
void RemoveWaiter(KThread* thread);
- [[nodiscard]] ResultCode GetThreadContext3(std::vector<u8>& out);
+ [[nodiscard]] Result GetThreadContext3(std::vector<u8>& out);
[[nodiscard]] KThread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key);
@@ -633,9 +615,9 @@ public:
}
void BeginWait(KThreadQueue* queue);
- void NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_);
- void EndWait(ResultCode wait_result_);
- void CancelWait(ResultCode wait_result_, bool cancel_timer_task);
+ void NotifyAvailable(KSynchronizationObject* signaled_object, Result wait_result_);
+ void EndWait(Result wait_result_);
+ void CancelWait(Result wait_result_, bool cancel_timer_task);
[[nodiscard]] bool HasWaiters() const {
return !waiter_list.empty();
@@ -721,14 +703,13 @@ private:
void FinishTermination();
- [[nodiscard]] ResultCode Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top,
- s32 prio, s32 virt_core, KProcess* owner, ThreadType type);
+ [[nodiscard]] Result Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top,
+ s32 prio, s32 virt_core, KProcess* owner, ThreadType type);
- [[nodiscard]] static ResultCode InitializeThread(KThread* thread, KThreadFunction func,
- uintptr_t arg, VAddr user_stack_top, s32 prio,
- s32 core, KProcess* owner, ThreadType type,
- std::function<void(void*)>&& init_func,
- void* init_func_parameter);
+ [[nodiscard]] static Result InitializeThread(KThread* thread, KThreadFunction func,
+ uintptr_t arg, VAddr user_stack_top, s32 prio,
+ s32 core, KProcess* owner, ThreadType type,
+ std::function<void()>&& init_func);
static void RestorePriority(KernelCore& kernel_ctx, KThread* thread);
@@ -765,7 +746,7 @@ private:
u32 suspend_request_flags{};
u32 suspend_allowed_flags{};
s32 synced_index{};
- ResultCode wait_result{ResultSuccess};
+ Result wait_result{ResultSuccess};
s32 base_priority{};
s32 physical_ideal_core_id{};
s32 virtual_ideal_core_id{};
diff --git a/src/core/hle/kernel/k_thread_local_page.cpp b/src/core/hle/kernel/k_thread_local_page.cpp
index fbdc40b3a..563560114 100644
--- a/src/core/hle/kernel/k_thread_local_page.cpp
+++ b/src/core/hle/kernel/k_thread_local_page.cpp
@@ -13,7 +13,7 @@
namespace Kernel {
-ResultCode KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
+Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
// Set that this process owns us.
m_owner = process;
m_kernel = &kernel;
@@ -35,7 +35,7 @@ ResultCode KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
return ResultSuccess;
}
-ResultCode KThreadLocalPage::Finalize() {
+Result KThreadLocalPage::Finalize() {
// Get the physical address of the page.
const PAddr phys_addr = m_owner->PageTable().GetPhysicalAddr(m_virt_addr);
ASSERT(phys_addr);
diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h
index a4fe43ee5..0a7f22680 100644
--- a/src/core/hle/kernel/k_thread_local_page.h
+++ b/src/core/hle/kernel/k_thread_local_page.h
@@ -34,8 +34,8 @@ public:
return m_virt_addr;
}
- ResultCode Initialize(KernelCore& kernel, KProcess* process);
- ResultCode Finalize();
+ Result Initialize(KernelCore& kernel, KProcess* process);
+ Result Finalize();
VAddr Reserve();
void Release(VAddr addr);
diff --git a/src/core/hle/kernel/k_thread_queue.cpp b/src/core/hle/kernel/k_thread_queue.cpp
index 1c338904a..9f4e081ba 100644
--- a/src/core/hle/kernel/k_thread_queue.cpp
+++ b/src/core/hle/kernel/k_thread_queue.cpp
@@ -9,9 +9,9 @@ namespace Kernel {
void KThreadQueue::NotifyAvailable([[maybe_unused]] KThread* waiting_thread,
[[maybe_unused]] KSynchronizationObject* signaled_object,
- [[maybe_unused]] ResultCode wait_result) {}
+ [[maybe_unused]] Result wait_result) {}
-void KThreadQueue::EndWait(KThread* waiting_thread, ResultCode wait_result) {
+void KThreadQueue::EndWait(KThread* waiting_thread, Result wait_result) {
// Set the thread's wait result.
waiting_thread->SetWaitResult(wait_result);
@@ -25,8 +25,7 @@ void KThreadQueue::EndWait(KThread* waiting_thread, ResultCode wait_result) {
kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
}
-void KThreadQueue::CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task) {
+void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) {
// Set the thread's wait result.
waiting_thread->SetWaitResult(wait_result);
@@ -43,6 +42,6 @@ void KThreadQueue::CancelWait(KThread* waiting_thread, ResultCode wait_result,
}
void KThreadQueueWithoutEndWait::EndWait([[maybe_unused]] KThread* waiting_thread,
- [[maybe_unused]] ResultCode wait_result) {}
+ [[maybe_unused]] Result wait_result) {}
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread_queue.h b/src/core/hle/kernel/k_thread_queue.h
index 4a7dbdd47..8d76ece81 100644
--- a/src/core/hle/kernel/k_thread_queue.h
+++ b/src/core/hle/kernel/k_thread_queue.h
@@ -14,10 +14,9 @@ public:
virtual ~KThreadQueue() = default;
virtual void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
- ResultCode wait_result);
- virtual void EndWait(KThread* waiting_thread, ResultCode wait_result);
- virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task);
+ Result wait_result);
+ virtual void EndWait(KThread* waiting_thread, Result wait_result);
+ virtual void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task);
private:
KernelCore& kernel;
@@ -28,7 +27,7 @@ class KThreadQueueWithoutEndWait : public KThreadQueue {
public:
explicit KThreadQueueWithoutEndWait(KernelCore& kernel_) : KThreadQueue(kernel_) {}
- void EndWait(KThread* waiting_thread, ResultCode wait_result) override final;
+ void EndWait(KThread* waiting_thread, Result wait_result) override final;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp
index 1ed4b0f6f..b0320eb73 100644
--- a/src/core/hle/kernel/k_transfer_memory.cpp
+++ b/src/core/hle/kernel/k_transfer_memory.cpp
@@ -13,8 +13,8 @@ KTransferMemory::KTransferMemory(KernelCore& kernel_)
KTransferMemory::~KTransferMemory() = default;
-ResultCode KTransferMemory::Initialize(VAddr address_, std::size_t size_,
- Svc::MemoryPermission owner_perm_) {
+Result KTransferMemory::Initialize(VAddr address_, std::size_t size_,
+ Svc::MemoryPermission owner_perm_) {
// Set members.
owner = kernel.CurrentProcess();
diff --git a/src/core/hle/kernel/k_transfer_memory.h b/src/core/hle/kernel/k_transfer_memory.h
index 9ad80ba30..85d508ee7 100644
--- a/src/core/hle/kernel/k_transfer_memory.h
+++ b/src/core/hle/kernel/k_transfer_memory.h
@@ -7,7 +7,7 @@
#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h"
-union ResultCode;
+union Result;
namespace Core::Memory {
class Memory;
@@ -26,7 +26,7 @@ public:
explicit KTransferMemory(KernelCore& kernel_);
~KTransferMemory() override;
- ResultCode Initialize(VAddr address_, std::size_t size_, Svc::MemoryPermission owner_perm_);
+ Result Initialize(VAddr address_, std::size_t size_, Svc::MemoryPermission owner_perm_);
void Finalize() override;
diff --git a/src/core/hle/kernel/k_writable_event.cpp b/src/core/hle/kernel/k_writable_event.cpp
index 26c8489ad..ff88c5acd 100644
--- a/src/core/hle/kernel/k_writable_event.cpp
+++ b/src/core/hle/kernel/k_writable_event.cpp
@@ -18,11 +18,11 @@ void KWritableEvent::Initialize(KEvent* parent_event_, std::string&& name_) {
parent->GetReadableEvent().Open();
}
-ResultCode KWritableEvent::Signal() {
+Result KWritableEvent::Signal() {
return parent->GetReadableEvent().Signal();
}
-ResultCode KWritableEvent::Clear() {
+Result KWritableEvent::Clear() {
return parent->GetReadableEvent().Clear();
}
diff --git a/src/core/hle/kernel/k_writable_event.h b/src/core/hle/kernel/k_writable_event.h
index e289e80c4..3fd0c7d0a 100644
--- a/src/core/hle/kernel/k_writable_event.h
+++ b/src/core/hle/kernel/k_writable_event.h
@@ -25,8 +25,8 @@ public:
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
void Initialize(KEvent* parent_, std::string&& name_);
- ResultCode Signal();
- ResultCode Clear();
+ Result Signal();
+ Result Clear();
KEvent* GetParent() const {
return parent;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 73593c7a0..ce7fa8275 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -17,7 +17,6 @@
#include "common/thread.h"
#include "common/thread_worker.h"
#include "core/arm/arm_interface.h"
-#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_timing.h"
@@ -64,8 +63,6 @@ struct KernelCore::Impl {
is_phantom_mode_for_singlecore = false;
- InitializePhysicalCores();
-
// Derive the initial memory layout from the emulated board
Init::InitializeSlabResourceCounts(kernel);
DeriveInitialMemoryLayout();
@@ -75,16 +72,16 @@ struct KernelCore::Impl {
InitializeSystemResourceLimit(kernel, system.CoreTiming());
InitializeMemoryLayout();
Init::InitializeKPageBufferSlabHeap(system);
- InitializeSchedulers();
InitializeShutdownThreads();
InitializePreemption(kernel);
+ InitializePhysicalCores();
RegisterHostThread();
}
void InitializeCores() {
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
- cores[core_id].Initialize((*current_process).Is64BitProcess());
+ cores[core_id]->Initialize((*current_process).Is64BitProcess());
system.Memory().SetCurrentPageTable(*current_process, core_id);
}
}
@@ -95,26 +92,16 @@ struct KernelCore::Impl {
process_list.clear();
- // Close all open server sessions and ports.
- std::unordered_set<KAutoObject*> server_objects_;
- {
- std::scoped_lock lk(server_objects_lock);
- server_objects_ = server_objects;
- server_objects.clear();
- }
- for (auto* server_object : server_objects_) {
- server_object->Close();
- }
-
- // Ensures all service threads gracefully shutdown.
- ClearServiceThreads();
+ CloseServices();
next_object_id = 0;
next_kernel_process_id = KProcess::InitialKIPIDMin;
next_user_process_id = KProcess::ProcessIDMin;
next_thread_id = 1;
- cores.clear();
+ for (auto& core : cores) {
+ core = nullptr;
+ }
global_handle_table->Finalize();
global_handle_table.reset();
@@ -148,7 +135,6 @@ struct KernelCore::Impl {
shutdown_threads[core_id] = nullptr;
}
- schedulers[core_id]->Finalize();
schedulers[core_id].reset();
}
@@ -191,18 +177,41 @@ struct KernelCore::Impl {
global_object_list_container.reset();
}
+ void CloseServices() {
+ // Close all open server sessions and ports.
+ std::unordered_set<KAutoObject*> server_objects_;
+ {
+ std::scoped_lock lk(server_objects_lock);
+ server_objects_ = server_objects;
+ server_objects.clear();
+ }
+ for (auto* server_object : server_objects_) {
+ server_object->Close();
+ }
+
+ // Ensures all service threads gracefully shutdown.
+ ClearServiceThreads();
+ }
+
void InitializePhysicalCores() {
exclusive_monitor =
Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES);
for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
- schedulers[i] = std::make_unique<Kernel::KScheduler>(system, i);
- cores.emplace_back(i, system, *schedulers[i], interrupts);
- }
- }
+ const s32 core{static_cast<s32>(i)};
- void InitializeSchedulers() {
- for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
- cores[i].Scheduler().Initialize();
+ schedulers[i] = std::make_unique<Kernel::KScheduler>(system.Kernel());
+ cores[i] = std::make_unique<Kernel::PhysicalCore>(i, system, *schedulers[i]);
+
+ auto* main_thread{Kernel::KThread::Create(system.Kernel())};
+ main_thread->SetName(fmt::format("MainThread:{}", core));
+ main_thread->SetCurrentCore(core);
+ ASSERT(Kernel::KThread::InitializeMainThread(system, main_thread, core).IsSuccess());
+
+ auto* idle_thread{Kernel::KThread::Create(system.Kernel())};
+ idle_thread->SetCurrentCore(core);
+ ASSERT(Kernel::KThread::InitializeIdleThread(system, idle_thread, core).IsSuccess());
+
+ schedulers[i]->Initialize(main_thread, idle_thread, core);
}
}
@@ -234,17 +243,18 @@ struct KernelCore::Impl {
void InitializePreemption(KernelCore& kernel) {
preemption_event = Core::Timing::CreateEvent(
- "PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) {
+ "PreemptionCallback",
+ [this, &kernel](std::uintptr_t, s64 time,
+ std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
{
KScopedSchedulerLock lock(kernel);
global_scheduler_context->PreemptThreads();
}
- const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
- system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
+ return std::nullopt;
});
const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
- system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
+ system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event);
}
void InitializeShutdownThreads() {
@@ -254,7 +264,6 @@ struct KernelCore::Impl {
core_id)
.IsSuccess());
shutdown_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id));
- shutdown_threads[core_id]->DisableDispatch();
}
}
@@ -332,6 +341,8 @@ struct KernelCore::Impl {
return is_shutting_down.load(std::memory_order_relaxed);
}
+ static inline thread_local KThread* current_thread{nullptr};
+
KThread* GetCurrentEmuThread() {
// If we are shutting down the kernel, none of this is relevant anymore.
if (IsShuttingDown()) {
@@ -342,7 +353,12 @@ struct KernelCore::Impl {
if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
return GetHostDummyThread();
}
- return schedulers[thread_id]->GetCurrentThread();
+
+ return current_thread;
+ }
+
+ void SetCurrentEmuThread(KThread* thread) {
+ current_thread = thread;
}
void DeriveInitialMemoryLayout() {
@@ -746,7 +762,7 @@ struct KernelCore::Impl {
std::unordered_set<KAutoObject*> registered_in_use_objects;
std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
- std::vector<Kernel::PhysicalCore> cores;
+ std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores;
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES};
@@ -770,7 +786,6 @@ struct KernelCore::Impl {
Common::ThreadWorker service_threads_manager;
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads;
- std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
bool is_multicore{};
@@ -806,6 +821,10 @@ void KernelCore::Shutdown() {
impl->Shutdown();
}
+void KernelCore::CloseServices() {
+ impl->CloseServices();
+}
+
const KResourceLimit* KernelCore::GetSystemResourceLimit() const {
return impl->system_resource_limit;
}
@@ -855,11 +874,11 @@ const Kernel::KScheduler& KernelCore::Scheduler(std::size_t id) const {
}
Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) {
- return impl->cores[id];
+ return *impl->cores[id];
}
const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
- return impl->cores[id];
+ return *impl->cores[id];
}
size_t KernelCore::CurrentPhysicalCoreIndex() const {
@@ -871,11 +890,11 @@ size_t KernelCore::CurrentPhysicalCoreIndex() const {
}
Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
- return impl->cores[CurrentPhysicalCoreIndex()];
+ return *impl->cores[CurrentPhysicalCoreIndex()];
}
const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
- return impl->cores[CurrentPhysicalCoreIndex()];
+ return *impl->cores[CurrentPhysicalCoreIndex()];
}
Kernel::KScheduler* KernelCore::CurrentScheduler() {
@@ -887,15 +906,6 @@ Kernel::KScheduler* KernelCore::CurrentScheduler() {
return impl->schedulers[core_id].get();
}
-std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() {
- return impl->interrupts;
-}
-
-const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts()
- const {
- return impl->interrupts;
-}
-
Kernel::TimeManager& KernelCore::TimeManager() {
return impl->time_manager;
}
@@ -920,24 +930,18 @@ const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
return *impl->global_object_list_container;
}
-void KernelCore::InterruptAllPhysicalCores() {
- for (auto& physical_core : impl->cores) {
- physical_core.Interrupt();
- }
-}
-
void KernelCore::InvalidateAllInstructionCaches() {
for (auto& physical_core : impl->cores) {
- physical_core.ArmInterface().ClearInstructionCache();
+ physical_core->ArmInterface().ClearInstructionCache();
}
}
void KernelCore::InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size) {
for (auto& physical_core : impl->cores) {
- if (!physical_core.IsInitialized()) {
+ if (!physical_core->IsInitialized()) {
continue;
}
- physical_core.ArmInterface().InvalidateCacheRange(addr, size);
+ physical_core->ArmInterface().InvalidateCacheRange(addr, size);
}
}
@@ -1025,6 +1029,10 @@ KThread* KernelCore::GetCurrentEmuThread() const {
return impl->GetCurrentEmuThread();
}
+void KernelCore::SetCurrentEmuThread(KThread* thread) {
+ impl->SetCurrentEmuThread(thread);
+}
+
KMemoryManager& KernelCore::MemoryManager() {
return *impl->memory_manager;
}
@@ -1079,14 +1087,22 @@ void KernelCore::Suspend(bool suspended) {
for (auto* process : GetProcessList()) {
process->SetActivity(activity);
+
+ if (should_suspend) {
+ // Wait for execution to stop
+ for (auto* thread : process->GetThreadList()) {
+ thread->WaitUntilSuspended();
+ }
+ }
}
}
void KernelCore::ShutdownCores() {
+ KScopedSchedulerLock lk{*this};
+
for (auto* thread : impl->shutdown_threads) {
void(thread->Run());
}
- InterruptAllPhysicalCores();
}
bool KernelCore::IsMulticore() const {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 4e7beab0e..bcf016a97 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -9,14 +9,12 @@
#include <string>
#include <unordered_map>
#include <vector>
-#include "core/arm/cpu_interrupt_handler.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_slab_heap.h"
#include "core/hle/kernel/svc_common.h"
namespace Core {
-class CPUInterruptHandler;
class ExclusiveMonitor;
class System;
} // namespace Core
@@ -109,6 +107,9 @@ public:
/// Clears all resources in use by the kernel instance.
void Shutdown();
+ /// Close all active services in use by the kernel instance.
+ void CloseServices();
+
/// Retrieves a shared pointer to the system resource limit instance.
const KResourceLimit* GetSystemResourceLimit() const;
@@ -180,12 +181,6 @@ public:
const KAutoObjectWithListContainer& ObjectListContainer() const;
- std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts();
-
- const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts() const;
-
- void InterruptAllPhysicalCores();
-
void InvalidateAllInstructionCaches();
void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size);
@@ -226,6 +221,9 @@ public:
/// Gets the current host_thread/guest_thread pointer.
KThread* GetCurrentEmuThread() const;
+ /// Sets the current guest_thread pointer.
+ void SetCurrentEmuThread(KThread* thread);
+
/// Gets the current host_thread handle.
u32 GetCurrentHostThreadID() const;
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index a5b16ae2e..d4375962f 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#include "core/core.h"
@@ -11,16 +10,14 @@
namespace Kernel {
-PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_,
- Core::CPUInterrupts& interrupts_)
- : core_index{core_index_}, system{system_}, scheduler{scheduler_},
- interrupts{interrupts_}, guard{std::make_unique<std::mutex>()} {
+PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_)
+ : core_index{core_index_}, system{system_}, scheduler{scheduler_} {
#ifdef ARCHITECTURE_x86_64
// TODO(bunnei): Initialization relies on a core being available. We may later replace this with
// a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
auto& kernel = system.Kernel();
arm_interface = std::make_unique<Core::ARM_Dynarmic_64>(
- system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
+ system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
#else
#error Platform not supported yet.
#endif
@@ -34,7 +31,7 @@ void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) {
if (!is_64_bit) {
// We already initialized a 64-bit core, replace with a 32-bit one.
arm_interface = std::make_unique<Core::ARM_Dynarmic_32>(
- system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
+ system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
}
#else
#error Platform not supported yet.
@@ -43,27 +40,30 @@ void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) {
void PhysicalCore::Run() {
arm_interface->Run();
+ arm_interface->ClearExclusiveState();
}
void PhysicalCore::Idle() {
- interrupts[core_index].AwaitInterrupt();
+ std::unique_lock lk{guard};
+ on_interrupt.wait(lk, [this] { return is_interrupted; });
}
bool PhysicalCore::IsInterrupted() const {
- return interrupts[core_index].IsInterrupted();
+ return is_interrupted;
}
void PhysicalCore::Interrupt() {
- guard->lock();
- interrupts[core_index].SetInterrupt(true);
+ std::unique_lock lk{guard};
+ is_interrupted = true;
arm_interface->SignalInterrupt();
- guard->unlock();
+ on_interrupt.notify_all();
}
void PhysicalCore::ClearInterrupt() {
- guard->lock();
- interrupts[core_index].SetInterrupt(false);
- guard->unlock();
+ std::unique_lock lk{guard};
+ is_interrupted = false;
+ arm_interface->ClearInterrupt();
+ on_interrupt.notify_all();
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
index 898d1e5db..2fc8d4be2 100644
--- a/src/core/hle/kernel/physical_core.h
+++ b/src/core/hle/kernel/physical_core.h
@@ -14,7 +14,6 @@ class KScheduler;
} // namespace Kernel
namespace Core {
-class CPUInterruptHandler;
class ExclusiveMonitor;
class System;
} // namespace Core
@@ -23,15 +22,11 @@ namespace Kernel {
class PhysicalCore {
public:
- PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_,
- Core::CPUInterrupts& interrupts_);
+ PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_);
~PhysicalCore();
- PhysicalCore(const PhysicalCore&) = delete;
- PhysicalCore& operator=(const PhysicalCore&) = delete;
-
- PhysicalCore(PhysicalCore&&) = default;
- PhysicalCore& operator=(PhysicalCore&&) = delete;
+ YUZU_NON_COPYABLE(PhysicalCore);
+ YUZU_NON_MOVEABLE(PhysicalCore);
/// Initialize the core for the specified parameters.
void Initialize(bool is_64_bit);
@@ -86,9 +81,11 @@ private:
const std::size_t core_index;
Core::System& system;
Kernel::KScheduler& scheduler;
- Core::CPUInterrupts& interrupts;
- std::unique_ptr<std::mutex> guard;
+
+ std::mutex guard;
+ std::condition_variable on_interrupt;
std::unique_ptr<Core::ARM_Interface> arm_interface;
+ bool is_interrupted;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp
index 54872626e..773319ad8 100644
--- a/src/core/hle/kernel/process_capability.cpp
+++ b/src/core/hle/kernel/process_capability.cpp
@@ -68,9 +68,9 @@ u32 GetFlagBitOffset(CapabilityType type) {
} // Anonymous namespace
-ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities,
- std::size_t num_capabilities,
- KPageTable& page_table) {
+Result ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities,
+ std::size_t num_capabilities,
+ KPageTable& page_table) {
Clear();
// Allow all cores and priorities.
@@ -81,9 +81,9 @@ ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabiliti
return ParseCapabilities(capabilities, num_capabilities, page_table);
}
-ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities,
- std::size_t num_capabilities,
- KPageTable& page_table) {
+Result ProcessCapabilities::InitializeForUserProcess(const u32* capabilities,
+ std::size_t num_capabilities,
+ KPageTable& page_table) {
Clear();
return ParseCapabilities(capabilities, num_capabilities, page_table);
@@ -107,9 +107,8 @@ void ProcessCapabilities::InitializeForMetadatalessProcess() {
can_force_debug = true;
}
-ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
- std::size_t num_capabilities,
- KPageTable& page_table) {
+Result ProcessCapabilities::ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
+ KPageTable& page_table) {
u32 set_flags = 0;
u32 set_svc_bits = 0;
@@ -155,8 +154,8 @@ ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
return ResultSuccess;
}
-ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits,
- u32 flag, KPageTable& page_table) {
+Result ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
+ KPageTable& page_table) {
const auto type = GetCapabilityType(flag);
if (type == CapabilityType::Unset) {
@@ -224,7 +223,7 @@ void ProcessCapabilities::Clear() {
can_force_debug = false;
}
-ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
+Result ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
if (priority_mask != 0 || core_mask != 0) {
LOG_ERROR(Kernel, "Core or priority mask are not zero! priority_mask={}, core_mask={}",
priority_mask, core_mask);
@@ -266,7 +265,7 @@ ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) {
+Result ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) {
const u32 index = flags >> 29;
const u32 svc_bit = 1U << index;
@@ -290,23 +289,23 @@ ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags)
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags,
- KPageTable& page_table) {
+Result ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags,
+ KPageTable& page_table) {
// TODO(Lioncache): Implement once the memory manager can handle this.
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, KPageTable& page_table) {
+Result ProcessCapabilities::HandleMapIOFlags(u32 flags, KPageTable& page_table) {
// TODO(Lioncache): Implement once the memory manager can handle this.
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleMapRegionFlags(u32 flags, KPageTable& page_table) {
+Result ProcessCapabilities::HandleMapRegionFlags(u32 flags, KPageTable& page_table) {
// TODO(Lioncache): Implement once the memory manager can handle this.
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) {
+Result ProcessCapabilities::HandleInterruptFlags(u32 flags) {
constexpr u32 interrupt_ignore_value = 0x3FF;
const u32 interrupt0 = (flags >> 12) & 0x3FF;
const u32 interrupt1 = (flags >> 22) & 0x3FF;
@@ -333,7 +332,7 @@ ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) {
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
+Result ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
const u32 reserved = flags >> 17;
if (reserved != 0) {
LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
@@ -344,7 +343,7 @@ ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
+Result ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
// Yes, the internal member variable is checked in the actual kernel here.
// This might look odd for options that are only allowed to be initialized
// just once, however the kernel has a separate initialization function for
@@ -364,7 +363,7 @@ ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
+Result ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
const u32 reserved = flags >> 26;
if (reserved != 0) {
LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
@@ -375,7 +374,7 @@ ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) {
+Result ProcessCapabilities::HandleDebugFlags(u32 flags) {
const u32 reserved = flags >> 19;
if (reserved != 0) {
LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h
index 7f3a2339d..ff05dc5ff 100644
--- a/src/core/hle/kernel/process_capability.h
+++ b/src/core/hle/kernel/process_capability.h
@@ -7,7 +7,7 @@
#include "common/common_types.h"
-union ResultCode;
+union Result;
namespace Kernel {
@@ -86,8 +86,8 @@ public:
/// @returns ResultSuccess if this capabilities instance was able to be initialized,
/// otherwise, an error code upon failure.
///
- ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities,
- KPageTable& page_table);
+ Result InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities,
+ KPageTable& page_table);
/// Initializes this process capabilities instance for a userland process.
///
@@ -99,8 +99,8 @@ public:
/// @returns ResultSuccess if this capabilities instance was able to be initialized,
/// otherwise, an error code upon failure.
///
- ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities,
- KPageTable& page_table);
+ Result InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities,
+ KPageTable& page_table);
/// Initializes this process capabilities instance for a process that does not
/// have any metadata to parse.
@@ -185,8 +185,8 @@ private:
///
/// @return ResultSuccess if no errors occur, otherwise an error code.
///
- ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
- KPageTable& page_table);
+ Result ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
+ KPageTable& page_table);
/// Attempts to parse a capability descriptor that is only represented by a
/// single flag set.
@@ -200,8 +200,8 @@ private:
///
/// @return ResultSuccess if no errors occurred, otherwise an error code.
///
- ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
- KPageTable& page_table);
+ Result ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
+ KPageTable& page_table);
/// Clears the internal state of this process capability instance. Necessary,
/// to have a sane starting point due to us allowing running executables without
@@ -219,34 +219,34 @@ private:
void Clear();
/// Handles flags related to the priority and core number capability flags.
- ResultCode HandlePriorityCoreNumFlags(u32 flags);
+ Result HandlePriorityCoreNumFlags(u32 flags);
/// Handles flags related to determining the allowable SVC mask.
- ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags);
+ Result HandleSyscallFlags(u32& set_svc_bits, u32 flags);
/// Handles flags related to mapping physical memory pages.
- ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, KPageTable& page_table);
+ Result HandleMapPhysicalFlags(u32 flags, u32 size_flags, KPageTable& page_table);
/// Handles flags related to mapping IO pages.
- ResultCode HandleMapIOFlags(u32 flags, KPageTable& page_table);
+ Result HandleMapIOFlags(u32 flags, KPageTable& page_table);
/// Handles flags related to mapping physical memory regions.
- ResultCode HandleMapRegionFlags(u32 flags, KPageTable& page_table);
+ Result HandleMapRegionFlags(u32 flags, KPageTable& page_table);
/// Handles flags related to the interrupt capability flags.
- ResultCode HandleInterruptFlags(u32 flags);
+ Result HandleInterruptFlags(u32 flags);
/// Handles flags related to the program type.
- ResultCode HandleProgramTypeFlags(u32 flags);
+ Result HandleProgramTypeFlags(u32 flags);
/// Handles flags related to the handle table size.
- ResultCode HandleHandleTableFlags(u32 flags);
+ Result HandleHandleTableFlags(u32 flags);
/// Handles flags related to the kernel version capability flags.
- ResultCode HandleKernelVersionFlags(u32 flags);
+ Result HandleKernelVersionFlags(u32 flags);
/// Handles flags related to debug-specific capabilities.
- ResultCode HandleDebugFlags(u32 flags);
+ Result HandleDebugFlags(u32 flags);
SyscallCapabilities svc_capabilities;
InterruptCapabilities interrupt_capabilities;
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 2ff6d5fa6..27e5a805d 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -58,8 +58,8 @@ constexpr bool IsValidAddressRange(VAddr address, u64 size) {
// Helper function that performs the common sanity checks for svcMapMemory
// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
// in the same order.
-ResultCode MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr,
- u64 size) {
+Result MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr,
+ u64 size) {
if (!Common::Is4KBAligned(dst_addr)) {
LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
return ResultInvalidAddress;
@@ -135,7 +135,7 @@ enum class ResourceLimitValueType {
} // Anonymous namespace
/// Set the process heap to a given Size. It can both extend and shrink the heap.
-static ResultCode SetHeapSize(Core::System& system, VAddr* out_address, u64 size) {
+static Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size) {
LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size);
// Validate size.
@@ -148,9 +148,9 @@ static ResultCode SetHeapSize(Core::System& system, VAddr* out_address, u64 size
return ResultSuccess;
}
-static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) {
+static Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) {
VAddr temp_heap_addr{};
- const ResultCode result{SetHeapSize(system, &temp_heap_addr, heap_size)};
+ const Result result{SetHeapSize(system, &temp_heap_addr, heap_size)};
*heap_addr = static_cast<u32>(temp_heap_addr);
return result;
}
@@ -166,8 +166,8 @@ constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
}
}
-static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 size,
- MemoryPermission perm) {
+static Result SetMemoryPermission(Core::System& system, VAddr address, u64 size,
+ MemoryPermission perm) {
LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size,
perm);
@@ -188,8 +188,8 @@ static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 s
return page_table.SetMemoryPermission(address, size, perm);
}
-static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
- u32 attr) {
+static Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
+ u32 attr) {
LOG_DEBUG(Kernel_SVC,
"called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
size, mask, attr);
@@ -213,19 +213,19 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si
return page_table.SetMemoryAttribute(address, size, mask, attr);
}
-static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask,
- u32 attr) {
+static Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask,
+ u32 attr) {
return SetMemoryAttribute(system, address, size, mask, attr);
}
/// Maps a memory range into a different range.
-static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
+static Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
- if (const ResultCode result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
+ if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
result.IsError()) {
return result;
}
@@ -233,18 +233,18 @@ static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr
return page_table.MapMemory(dst_addr, src_addr, size);
}
-static ResultCode MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
+static Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
return MapMemory(system, dst_addr, src_addr, size);
}
/// Unmaps a region that was previously mapped with svcMapMemory
-static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
+static Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
- if (const ResultCode result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
+ if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
result.IsError()) {
return result;
}
@@ -252,12 +252,12 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad
return page_table.UnmapMemory(dst_addr, src_addr, size);
}
-static ResultCode UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
+static Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
return UnmapMemory(system, dst_addr, src_addr, size);
}
/// Connect to an OS service given the port name, returns the handle to the port to out
-static ResultCode ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
+static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
auto& memory = system.Memory();
if (!memory.IsValidVirtualAddress(port_name_address)) {
LOG_ERROR(Kernel_SVC,
@@ -307,14 +307,14 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out, VAddr po
return ResultSuccess;
}
-static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle,
- u32 port_name_address) {
+static Result ConnectToNamedPort32(Core::System& system, Handle* out_handle,
+ u32 port_name_address) {
return ConnectToNamedPort(system, out_handle, port_name_address);
}
/// Makes a blocking IPC call to an OS service.
-static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
+static Result SendSyncRequest(Core::System& system, Handle handle) {
auto& kernel = system.Kernel();
// Create the wait queue.
@@ -327,7 +327,6 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
- auto thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLock lock(kernel);
@@ -337,15 +336,15 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming());
}
- return thread->GetWaitResult();
+ return GetCurrentThread(kernel).GetWaitResult();
}
-static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
+static Result SendSyncRequest32(Core::System& system, Handle handle) {
return SendSyncRequest(system, handle);
}
/// Get the ID for the specified thread.
-static ResultCode GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
+static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
// Get the thread from its handle.
KScopedAutoObject thread =
system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
@@ -356,10 +355,10 @@ static ResultCode GetThreadId(Core::System& system, u64* out_thread_id, Handle t
return ResultSuccess;
}
-static ResultCode GetThreadId32(Core::System& system, u32* out_thread_id_low,
- u32* out_thread_id_high, Handle thread_handle) {
+static Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high,
+ Handle thread_handle) {
u64 out_thread_id{};
- const ResultCode result{GetThreadId(system, &out_thread_id, thread_handle)};
+ const Result result{GetThreadId(system, &out_thread_id, thread_handle)};
*out_thread_id_low = static_cast<u32>(out_thread_id >> 32);
*out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max());
@@ -368,7 +367,7 @@ static ResultCode GetThreadId32(Core::System& system, u32* out_thread_id_low,
}
/// Gets the ID of the specified process or a specified thread's owning process.
-static ResultCode GetProcessId(Core::System& system, u64* out_process_id, Handle handle) {
+static Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
// Get the object from the handle table.
@@ -399,8 +398,8 @@ static ResultCode GetProcessId(Core::System& system, u64* out_process_id, Handle
return ResultSuccess;
}
-static ResultCode GetProcessId32(Core::System& system, u32* out_process_id_low,
- u32* out_process_id_high, Handle handle) {
+static Result GetProcessId32(Core::System& system, u32* out_process_id_low,
+ u32* out_process_id_high, Handle handle) {
u64 out_process_id{};
const auto result = GetProcessId(system, &out_process_id, handle);
*out_process_id_low = static_cast<u32>(out_process_id);
@@ -409,8 +408,8 @@ static ResultCode GetProcessId32(Core::System& system, u32* out_process_id_low,
}
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
-static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr handles_address,
- s32 num_handles, s64 nano_seconds) {
+static Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address,
+ s32 num_handles, s64 nano_seconds) {
LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}",
handles_address, num_handles, nano_seconds);
@@ -445,14 +444,14 @@ static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr ha
nano_seconds);
}
-static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
- s32 num_handles, u32 timeout_high, s32* index) {
+static Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
+ s32 num_handles, u32 timeout_high, s32* index) {
const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)};
return WaitSynchronization(system, index, handles_address, num_handles, nano_seconds);
}
/// Resumes a thread waiting on WaitSynchronization
-static ResultCode CancelSynchronization(Core::System& system, Handle handle) {
+static Result CancelSynchronization(Core::System& system, Handle handle) {
LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle);
// Get the thread from its handle.
@@ -465,13 +464,12 @@ static ResultCode CancelSynchronization(Core::System& system, Handle handle) {
return ResultSuccess;
}
-static ResultCode CancelSynchronization32(Core::System& system, Handle handle) {
+static Result CancelSynchronization32(Core::System& system, Handle handle) {
return CancelSynchronization(system, handle);
}
/// Attempts to locks a mutex
-static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address,
- u32 tag) {
+static Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag) {
LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}",
thread_handle, address, tag);
@@ -489,13 +487,12 @@ static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAdd
return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag);
}
-static ResultCode ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address,
- u32 tag) {
+static Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag) {
return ArbitrateLock(system, thread_handle, address, tag);
}
/// Unlock a mutex
-static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) {
+static Result ArbitrateUnlock(Core::System& system, VAddr address) {
LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address);
// Validate the input address.
@@ -513,7 +510,7 @@ static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) {
return system.Kernel().CurrentProcess()->SignalToAddress(address);
}
-static ResultCode ArbitrateUnlock32(Core::System& system, u32 address) {
+static Result ArbitrateUnlock32(Core::System& system, u32 address) {
return ArbitrateUnlock(system, address);
}
@@ -624,7 +621,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
handle_debug_buffer(info1, info2);
- auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
+ auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
const auto thread_processor_id = current_thread->GetActiveCore();
system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
}
@@ -656,8 +653,8 @@ static void OutputDebugString32(Core::System& system, u32 address, u32 len) {
}
/// Gets system/memory information for the current process
-static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle,
- u64 info_sub_id) {
+static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle,
+ u64 info_sub_id) {
LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
info_sub_id, handle);
@@ -692,6 +689,9 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
// 6.0.0+
TotalPhysicalMemoryAvailableWithoutSystemResource = 21,
TotalPhysicalMemoryUsedWithoutSystemResource = 22,
+
+ // Homebrew only
+ MesosphereCurrentProcess = 65001,
};
const auto info_id_type = static_cast<GetInfoType>(info_id);
@@ -884,10 +884,10 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
const auto& core_timing = system.CoreTiming();
const auto& scheduler = *system.Kernel().CurrentScheduler();
- const auto* const current_thread = scheduler.GetCurrentThread();
+ const auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
const bool same_thread = current_thread == thread.GetPointerUnsafe();
- const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
+ const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime();
u64 out_ticks = 0;
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
const u64 thread_ticks = current_thread->GetCpuTime();
@@ -914,18 +914,39 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
*result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
return ResultSuccess;
}
+ case GetInfoType::MesosphereCurrentProcess: {
+ // Verify the input handle is invalid.
+ R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
+
+ // Verify the sub-type is valid.
+ R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
+
+ // Get the handle table.
+ KProcess* current_process = system.Kernel().CurrentProcess();
+ KHandleTable& handle_table = current_process->GetHandleTable();
+
+ // Get a new handle for the current process.
+ Handle tmp;
+ R_TRY(handle_table.Add(&tmp, current_process));
+
+ // Set the output.
+ *result = tmp;
+
+ // We succeeded.
+ return ResultSuccess;
+ }
default:
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
return ResultInvalidEnumValue;
}
}
-static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low,
- u32 info_id, u32 handle, u32 sub_id_high) {
+static Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low,
+ u32 info_id, u32 handle, u32 sub_id_high) {
const u64 sub_id{u64{sub_id_low} | (u64{sub_id_high} << 32)};
u64 res_value{};
- const ResultCode result{GetInfo(system, &res_value, info_id, handle, sub_id)};
+ const Result result{GetInfo(system, &res_value, info_id, handle, sub_id)};
*result_high = static_cast<u32>(res_value >> 32);
*result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max());
@@ -933,7 +954,7 @@ static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_h
}
/// Maps memory at a desired address
-static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
+static Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
if (!Common::Is4KBAligned(addr)) {
@@ -981,12 +1002,12 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size)
return page_table.MapPhysicalMemory(addr, size);
}
-static ResultCode MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
+static Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
return MapPhysicalMemory(system, addr, size);
}
/// Unmaps memory previously mapped via MapPhysicalMemory
-static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
+static Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
if (!Common::Is4KBAligned(addr)) {
@@ -1034,13 +1055,13 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size
return page_table.UnmapPhysicalMemory(addr, size);
}
-static ResultCode UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
+static Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
return UnmapPhysicalMemory(system, addr, size);
}
/// Sets the thread activity
-static ResultCode SetThreadActivity(Core::System& system, Handle thread_handle,
- ThreadActivity thread_activity) {
+static Result SetThreadActivity(Core::System& system, Handle thread_handle,
+ ThreadActivity thread_activity) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle,
thread_activity);
@@ -1065,13 +1086,13 @@ static ResultCode SetThreadActivity(Core::System& system, Handle thread_handle,
return ResultSuccess;
}
-static ResultCode SetThreadActivity32(Core::System& system, Handle thread_handle,
- Svc::ThreadActivity thread_activity) {
+static Result SetThreadActivity32(Core::System& system, Handle thread_handle,
+ Svc::ThreadActivity thread_activity) {
return SetThreadActivity(system, thread_handle, thread_activity);
}
/// Gets the thread context
-static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) {
+static Result GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) {
LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context,
thread_handle);
@@ -1103,7 +1124,7 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand
if (thread->GetRawState() != ThreadState::Runnable) {
bool current = false;
for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
- if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetCurrentThread()) {
+ if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) {
current = true;
break;
}
@@ -1128,12 +1149,12 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand
return ResultSuccess;
}
-static ResultCode GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) {
+static Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) {
return GetThreadContext(system, out_context, thread_handle);
}
/// Gets the priority for the specified thread
-static ResultCode GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) {
+static Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) {
LOG_TRACE(Kernel_SVC, "called");
// Get the thread from its handle.
@@ -1146,12 +1167,12 @@ static ResultCode GetThreadPriority(Core::System& system, u32* out_priority, Han
return ResultSuccess;
}
-static ResultCode GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) {
+static Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) {
return GetThreadPriority(system, out_priority, handle);
}
/// Sets the priority for the specified thread
-static ResultCode SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) {
+static Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) {
// Get the current process.
KProcess& process = *system.Kernel().CurrentProcess();
@@ -1169,7 +1190,7 @@ static ResultCode SetThreadPriority(Core::System& system, Handle thread_handle,
return ResultSuccess;
}
-static ResultCode SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) {
+static Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) {
return SetThreadPriority(system, thread_handle, priority);
}
@@ -1229,8 +1250,8 @@ constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission p
} // Anonymous namespace
-static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
- u64 size, Svc::MemoryPermission map_perm) {
+static Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size,
+ Svc::MemoryPermission map_perm) {
LOG_TRACE(Kernel_SVC,
"called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
shmem_handle, address, size, map_perm);
@@ -1270,13 +1291,13 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAd
return ResultSuccess;
}
-static ResultCode MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address,
- u32 size, Svc::MemoryPermission map_perm) {
+static Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size,
+ Svc::MemoryPermission map_perm) {
return MapSharedMemory(system, shmem_handle, address, size, map_perm);
}
-static ResultCode UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
- u64 size) {
+static Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
+ u64 size) {
// Validate the address/size.
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
@@ -1303,13 +1324,13 @@ static ResultCode UnmapSharedMemory(Core::System& system, Handle shmem_handle, V
return ResultSuccess;
}
-static ResultCode UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address,
- u32 size) {
+static Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address,
+ u32 size) {
return UnmapSharedMemory(system, shmem_handle, address, size);
}
-static ResultCode SetProcessMemoryPermission(Core::System& system, Handle process_handle,
- VAddr address, u64 size, Svc::MemoryPermission perm) {
+static Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address,
+ u64 size, Svc::MemoryPermission perm) {
LOG_TRACE(Kernel_SVC,
"called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
process_handle, address, size, perm);
@@ -1338,8 +1359,8 @@ static ResultCode SetProcessMemoryPermission(Core::System& system, Handle proces
return page_table.SetProcessMemoryPermission(address, size, perm);
}
-static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
- VAddr src_address, u64 size) {
+static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
+ VAddr src_address, u64 size) {
LOG_TRACE(Kernel_SVC,
"called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
dst_address, process_handle, src_address, size);
@@ -1368,7 +1389,7 @@ static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Hand
ResultInvalidMemoryRegion);
// Create a new page group.
- KPageLinkedList pg;
+ KPageGroup pg;
R_TRY(src_pt.MakeAndOpenPageGroup(
std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
@@ -1381,8 +1402,8 @@ static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Hand
return ResultSuccess;
}
-static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
- VAddr src_address, u64 size) {
+static Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
+ VAddr src_address, u64 size) {
LOG_TRACE(Kernel_SVC,
"called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
dst_address, process_handle, src_address, size);
@@ -1416,7 +1437,7 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha
return ResultSuccess;
}
-static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
+static Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
// Get kernel instance.
@@ -1451,12 +1472,12 @@ static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr addr
return ResultSuccess;
}
-static ResultCode CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) {
+static Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) {
return CreateCodeMemory(system, out, address, size);
}
-static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
- VAddr address, size_t size, Svc::MemoryPermission perm) {
+static Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
+ VAddr address, size_t size, Svc::MemoryPermission perm) {
LOG_TRACE(Kernel_SVC,
"called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
@@ -1534,15 +1555,13 @@ static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_han
return ResultSuccess;
}
-static ResultCode ControlCodeMemory32(Core::System& system, Handle code_memory_handle,
- u32 operation, u64 address, u64 size,
- Svc::MemoryPermission perm) {
+static Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation,
+ u64 address, u64 size, Svc::MemoryPermission perm) {
return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm);
}
-static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
- VAddr page_info_address, Handle process_handle,
- VAddr address) {
+static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address,
+ VAddr page_info_address, Handle process_handle, VAddr address) {
LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
@@ -1570,8 +1589,8 @@ static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_add
return ResultSuccess;
}
-static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address,
- VAddr page_info_address, VAddr query_address) {
+static Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address,
+ VAddr query_address) {
LOG_TRACE(Kernel_SVC,
"called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, "
"query_address=0x{:016X}",
@@ -1581,13 +1600,13 @@ static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address,
query_address);
}
-static ResultCode QueryMemory32(Core::System& system, u32 memory_info_address,
- u32 page_info_address, u32 query_address) {
+static Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address,
+ u32 query_address) {
return QueryMemory(system, memory_info_address, page_info_address, query_address);
}
-static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
- u64 src_address, u64 size) {
+static Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
+ u64 src_address, u64 size) {
LOG_DEBUG(Kernel_SVC,
"called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
"src_address=0x{:016X}, size=0x{:016X}",
@@ -1654,8 +1673,8 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
return page_table.MapCodeMemory(dst_address, src_address, size);
}
-static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle,
- u64 dst_address, u64 src_address, u64 size) {
+static Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
+ u64 src_address, u64 size) {
LOG_DEBUG(Kernel_SVC,
"called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
"size=0x{:016X}",
@@ -1747,8 +1766,8 @@ constexpr bool IsValidVirtualCoreId(int32_t core_id) {
} // Anonymous namespace
/// Creates a new thread
-static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
- VAddr stack_bottom, u32 priority, s32 core_id) {
+static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
+ VAddr stack_bottom, u32 priority, s32 core_id) {
LOG_DEBUG(Kernel_SVC,
"called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
"priority=0x{:08X}, core_id=0x{:08X}",
@@ -1819,13 +1838,13 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
return ResultSuccess;
}
-static ResultCode CreateThread32(Core::System& system, Handle* out_handle, u32 priority,
- u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) {
+static Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority,
+ u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) {
return CreateThread(system, out_handle, entry_point, arg, stack_top, priority, processor_id);
}
/// Starts the thread for the provided handle
-static ResultCode StartThread(Core::System& system, Handle thread_handle) {
+static Result StartThread(Core::System& system, Handle thread_handle) {
LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
// Get the thread from its handle.
@@ -1843,7 +1862,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) {
return ResultSuccess;
}
-static ResultCode StartThread32(Core::System& system, Handle thread_handle) {
+static Result StartThread32(Core::System& system, Handle thread_handle) {
return StartThread(system, thread_handle);
}
@@ -1851,7 +1870,7 @@ static ResultCode StartThread32(Core::System& system, Handle thread_handle) {
static void ExitThread(Core::System& system) {
LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
- auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
+ auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
system.GlobalSchedulerContext().RemoveThread(current_thread);
current_thread->Exit();
system.Kernel().UnregisterInUseObject(current_thread);
@@ -1894,8 +1913,8 @@ static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanosec
}
/// Wait process wide key atomic
-static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key,
- u32 tag, s64 timeout_ns) {
+static Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag,
+ s64 timeout_ns) {
LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address,
cv_key, tag, timeout_ns);
@@ -1930,8 +1949,8 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address,
address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout);
}
-static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag,
- u32 timeout_ns_low, u32 timeout_ns_high) {
+static Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag,
+ u32 timeout_ns_low, u32 timeout_ns_high) {
const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns);
}
@@ -1976,8 +1995,8 @@ constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) {
} // namespace
// Wait for an address (via Address Arbiter)
-static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type,
- s32 value, s64 timeout_ns) {
+static Result WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type,
+ s32 value, s64 timeout_ns) {
LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}",
address, arb_type, value, timeout_ns);
@@ -2014,15 +2033,15 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::Arbit
return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout);
}
-static ResultCode WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type,
- s32 value, u32 timeout_ns_low, u32 timeout_ns_high) {
+static Result WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type,
+ s32 value, u32 timeout_ns_low, u32 timeout_ns_high) {
const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
return WaitForAddress(system, address, arb_type, value, timeout);
}
// Signals to an address (via Address Arbiter)
-static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type,
- s32 value, s32 count) {
+static Result SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type,
+ s32 value, s32 count) {
LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}",
address, signal_type, value, count);
@@ -2063,8 +2082,8 @@ static void SynchronizePreemptionState(Core::System& system) {
}
}
-static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type,
- s32 value, s32 count) {
+static Result SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type,
+ s32 value, s32 count) {
return SignalToAddress(system, address, signal_type, value, count);
}
@@ -2102,7 +2121,7 @@ static void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high)
}
/// Close a handle
-static ResultCode CloseHandle(Core::System& system, Handle handle) {
+static Result CloseHandle(Core::System& system, Handle handle) {
LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
// Remove the handle.
@@ -2112,12 +2131,12 @@ static ResultCode CloseHandle(Core::System& system, Handle handle) {
return ResultSuccess;
}
-static ResultCode CloseHandle32(Core::System& system, Handle handle) {
+static Result CloseHandle32(Core::System& system, Handle handle) {
return CloseHandle(system, handle);
}
/// Clears the signaled state of an event or process.
-static ResultCode ResetSignal(Core::System& system, Handle handle) {
+static Result ResetSignal(Core::System& system, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
// Get the current handle table.
@@ -2144,7 +2163,7 @@ static ResultCode ResetSignal(Core::System& system, Handle handle) {
return ResultInvalidHandle;
}
-static ResultCode ResetSignal32(Core::System& system, Handle handle) {
+static Result ResetSignal32(Core::System& system, Handle handle) {
return ResetSignal(system, handle);
}
@@ -2164,8 +2183,8 @@ constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
} // Anonymous namespace
/// Creates a TransferMemory object
-static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
- MemoryPermission map_perm) {
+static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
+ MemoryPermission map_perm) {
auto& kernel = system.Kernel();
// Validate the size.
@@ -2211,13 +2230,13 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr
return ResultSuccess;
}
-static ResultCode CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size,
- MemoryPermission map_perm) {
+static Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size,
+ MemoryPermission map_perm) {
return CreateTransferMemory(system, out, address, size, map_perm);
}
-static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id,
- u64* out_affinity_mask) {
+static Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id,
+ u64* out_affinity_mask) {
LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
// Get the thread from its handle.
@@ -2231,8 +2250,8 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle,
return ResultSuccess;
}
-static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id,
- u32* out_affinity_mask_low, u32* out_affinity_mask_high) {
+static Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id,
+ u32* out_affinity_mask_low, u32* out_affinity_mask_high) {
u64 out_affinity_mask{};
const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask);
*out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32);
@@ -2240,8 +2259,8 @@ static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle
return result;
}
-static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
- u64 affinity_mask) {
+static Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
+ u64 affinity_mask) {
// Determine the core id/affinity mask.
if (core_id == IdealCoreUseProcessValue) {
core_id = system.Kernel().CurrentProcess()->GetIdealCoreId();
@@ -2272,13 +2291,13 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle,
return ResultSuccess;
}
-static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id,
- u32 affinity_mask_low, u32 affinity_mask_high) {
+static Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id,
+ u32 affinity_mask_low, u32 affinity_mask_high) {
const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32);
return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask);
}
-static ResultCode SignalEvent(Core::System& system, Handle event_handle) {
+static Result SignalEvent(Core::System& system, Handle event_handle) {
LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
// Get the current handle table.
@@ -2291,11 +2310,11 @@ static ResultCode SignalEvent(Core::System& system, Handle event_handle) {
return writable_event->Signal();
}
-static ResultCode SignalEvent32(Core::System& system, Handle event_handle) {
+static Result SignalEvent32(Core::System& system, Handle event_handle) {
return SignalEvent(system, event_handle);
}
-static ResultCode ClearEvent(Core::System& system, Handle event_handle) {
+static Result ClearEvent(Core::System& system, Handle event_handle) {
LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
// Get the current handle table.
@@ -2322,11 +2341,11 @@ static ResultCode ClearEvent(Core::System& system, Handle event_handle) {
return ResultInvalidHandle;
}
-static ResultCode ClearEvent32(Core::System& system, Handle event_handle) {
+static Result ClearEvent32(Core::System& system, Handle event_handle) {
return ClearEvent(system, event_handle);
}
-static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) {
+static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) {
LOG_DEBUG(Kernel_SVC, "called");
// Get the kernel reference and handle table.
@@ -2371,11 +2390,11 @@ static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* o
return ResultSuccess;
}
-static ResultCode CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) {
+static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) {
return CreateEvent(system, out_write, out_read);
}
-static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
+static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
// This function currently only allows retrieving a process' status.
@@ -2401,7 +2420,7 @@ static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_
return ResultSuccess;
}
-static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) {
+static Result CreateResourceLimit(Core::System& system, Handle* out_handle) {
LOG_DEBUG(Kernel_SVC, "called");
// Create a new resource limit.
@@ -2424,9 +2443,8 @@ static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle)
return ResultSuccess;
}
-static ResultCode GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value,
- Handle resource_limit_handle,
- LimitableResource which) {
+static Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value,
+ Handle resource_limit_handle, LimitableResource which) {
LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
which);
@@ -2445,9 +2463,8 @@ static ResultCode GetResourceLimitLimitValue(Core::System& system, u64* out_limi
return ResultSuccess;
}
-static ResultCode GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value,
- Handle resource_limit_handle,
- LimitableResource which) {
+static Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value,
+ Handle resource_limit_handle, LimitableResource which) {
LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
which);
@@ -2466,8 +2483,8 @@ static ResultCode GetResourceLimitCurrentValue(Core::System& system, u64* out_cu
return ResultSuccess;
}
-static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle,
- LimitableResource which, u64 limit_value) {
+static Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle,
+ LimitableResource which, u64 limit_value) {
LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}, limit_value={}",
resource_limit_handle, which, limit_value);
@@ -2486,8 +2503,8 @@ static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resour
return ResultSuccess;
}
-static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
- VAddr out_process_ids, u32 out_process_ids_size) {
+static Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids,
+ u32 out_process_ids_size) {
LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}",
out_process_ids, out_process_ids_size);
@@ -2523,8 +2540,8 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
return ResultSuccess;
}
-static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
- u32 out_thread_ids_size, Handle debug_handle) {
+static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
+ u32 out_thread_ids_size, Handle debug_handle) {
// TODO: Handle this case when debug events are supported.
UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
@@ -2563,9 +2580,9 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd
return ResultSuccess;
}
-static ResultCode FlushProcessDataCache32([[maybe_unused]] Core::System& system,
- [[maybe_unused]] Handle handle,
- [[maybe_unused]] u32 address, [[maybe_unused]] u32 size) {
+static Result FlushProcessDataCache32([[maybe_unused]] Core::System& system,
+ [[maybe_unused]] Handle handle, [[maybe_unused]] u32 address,
+ [[maybe_unused]] u32 size) {
// Note(Blinkhawk): For emulation purposes of the data cache this is mostly a no-op,
// as all emulation is done in the same cache level in host architecture, thus data cache
// does not need flushing.
@@ -2993,7 +3010,7 @@ void Call(Core::System& system, u32 immediate) {
auto& kernel = system.Kernel();
kernel.EnterSVCProfile();
- auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
+ auto* thread = GetCurrentThreadPointer(kernel);
thread->SetIsCallingSvc();
const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate)
@@ -3009,11 +3026,6 @@ void Call(Core::System& system, u32 immediate) {
}
kernel.ExitSVCProfile();
-
- if (!thread->IsCallingSvc()) {
- auto* host_context = thread->GetHostContext().get();
- host_context->Rewind();
- }
}
} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h
index fab12d070..f27cade33 100644
--- a/src/core/hle/kernel/svc_results.h
+++ b/src/core/hle/kernel/svc_results.h
@@ -9,34 +9,34 @@ namespace Kernel {
// Confirmed Switch kernel error codes
-constexpr ResultCode ResultOutOfSessions{ErrorModule::Kernel, 7};
-constexpr ResultCode ResultInvalidArgument{ErrorModule::Kernel, 14};
-constexpr ResultCode ResultNoSynchronizationObject{ErrorModule::Kernel, 57};
-constexpr ResultCode ResultTerminationRequested{ErrorModule::Kernel, 59};
-constexpr ResultCode ResultInvalidSize{ErrorModule::Kernel, 101};
-constexpr ResultCode ResultInvalidAddress{ErrorModule::Kernel, 102};
-constexpr ResultCode ResultOutOfResource{ErrorModule::Kernel, 103};
-constexpr ResultCode ResultOutOfMemory{ErrorModule::Kernel, 104};
-constexpr ResultCode ResultOutOfHandles{ErrorModule::Kernel, 105};
-constexpr ResultCode ResultInvalidCurrentMemory{ErrorModule::Kernel, 106};
-constexpr ResultCode ResultInvalidNewMemoryPermission{ErrorModule::Kernel, 108};
-constexpr ResultCode ResultInvalidMemoryRegion{ErrorModule::Kernel, 110};
-constexpr ResultCode ResultInvalidPriority{ErrorModule::Kernel, 112};
-constexpr ResultCode ResultInvalidCoreId{ErrorModule::Kernel, 113};
-constexpr ResultCode ResultInvalidHandle{ErrorModule::Kernel, 114};
-constexpr ResultCode ResultInvalidPointer{ErrorModule::Kernel, 115};
-constexpr ResultCode ResultInvalidCombination{ErrorModule::Kernel, 116};
-constexpr ResultCode ResultTimedOut{ErrorModule::Kernel, 117};
-constexpr ResultCode ResultCancelled{ErrorModule::Kernel, 118};
-constexpr ResultCode ResultOutOfRange{ErrorModule::Kernel, 119};
-constexpr ResultCode ResultInvalidEnumValue{ErrorModule::Kernel, 120};
-constexpr ResultCode ResultNotFound{ErrorModule::Kernel, 121};
-constexpr ResultCode ResultBusy{ErrorModule::Kernel, 122};
-constexpr ResultCode ResultSessionClosed{ErrorModule::Kernel, 123};
-constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125};
-constexpr ResultCode ResultReservedUsed{ErrorModule::Kernel, 126};
-constexpr ResultCode ResultPortClosed{ErrorModule::Kernel, 131};
-constexpr ResultCode ResultLimitReached{ErrorModule::Kernel, 132};
-constexpr ResultCode ResultInvalidId{ErrorModule::Kernel, 519};
+constexpr Result ResultOutOfSessions{ErrorModule::Kernel, 7};
+constexpr Result ResultInvalidArgument{ErrorModule::Kernel, 14};
+constexpr Result ResultNoSynchronizationObject{ErrorModule::Kernel, 57};
+constexpr Result ResultTerminationRequested{ErrorModule::Kernel, 59};
+constexpr Result ResultInvalidSize{ErrorModule::Kernel, 101};
+constexpr Result ResultInvalidAddress{ErrorModule::Kernel, 102};
+constexpr Result ResultOutOfResource{ErrorModule::Kernel, 103};
+constexpr Result ResultOutOfMemory{ErrorModule::Kernel, 104};
+constexpr Result ResultOutOfHandles{ErrorModule::Kernel, 105};
+constexpr Result ResultInvalidCurrentMemory{ErrorModule::Kernel, 106};
+constexpr Result ResultInvalidNewMemoryPermission{ErrorModule::Kernel, 108};
+constexpr Result ResultInvalidMemoryRegion{ErrorModule::Kernel, 110};
+constexpr Result ResultInvalidPriority{ErrorModule::Kernel, 112};
+constexpr Result ResultInvalidCoreId{ErrorModule::Kernel, 113};
+constexpr Result ResultInvalidHandle{ErrorModule::Kernel, 114};
+constexpr Result ResultInvalidPointer{ErrorModule::Kernel, 115};
+constexpr Result ResultInvalidCombination{ErrorModule::Kernel, 116};
+constexpr Result ResultTimedOut{ErrorModule::Kernel, 117};
+constexpr Result ResultCancelled{ErrorModule::Kernel, 118};
+constexpr Result ResultOutOfRange{ErrorModule::Kernel, 119};
+constexpr Result ResultInvalidEnumValue{ErrorModule::Kernel, 120};
+constexpr Result ResultNotFound{ErrorModule::Kernel, 121};
+constexpr Result ResultBusy{ErrorModule::Kernel, 122};
+constexpr Result ResultSessionClosed{ErrorModule::Kernel, 123};
+constexpr Result ResultInvalidState{ErrorModule::Kernel, 125};
+constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126};
+constexpr Result ResultPortClosed{ErrorModule::Kernel, 131};
+constexpr Result ResultLimitReached{ErrorModule::Kernel, 132};
+constexpr Result ResultInvalidId{ErrorModule::Kernel, 519};
} // namespace Kernel
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 2271bb80c..4bc49087e 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -33,24 +33,24 @@ static inline void FuncReturn32(Core::System& system, u32 result) {
}
////////////////////////////////////////////////////////////////////////////////////////////////////
-// Function wrappers that return type ResultCode
+// Function wrappers that return type Result
-template <ResultCode func(Core::System&, u64)>
+template <Result func(Core::System&, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0)).raw);
}
-template <ResultCode func(Core::System&, u64, u64)>
+template <Result func(Core::System&, u64, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1)).raw);
}
-template <ResultCode func(Core::System&, u32)>
+template <Result func(Core::System&, u32)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
}
-template <ResultCode func(Core::System&, u32, u32)>
+template <Result func(Core::System&, u32, u32)>
void SvcWrap64(Core::System& system) {
FuncReturn(
system,
@@ -58,14 +58,14 @@ void SvcWrap64(Core::System& system) {
}
// Used by SetThreadActivity
-template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)>
+template <Result func(Core::System&, Handle, Svc::ThreadActivity)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
static_cast<Svc::ThreadActivity>(Param(system, 1)))
.raw);
}
-template <ResultCode func(Core::System&, u32, u64, u64, u64)>
+template <Result func(Core::System&, u32, u64, u64, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
Param(system, 2), Param(system, 3))
@@ -73,7 +73,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by MapProcessMemory and UnmapProcessMemory
-template <ResultCode func(Core::System&, u64, u32, u64, u64)>
+template <Result func(Core::System&, u64, u32, u64, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
Param(system, 2), Param(system, 3))
@@ -81,7 +81,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by ControlCodeMemory
-template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
+template <Result func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3),
@@ -89,7 +89,7 @@ void SvcWrap64(Core::System& system) {
.raw);
}
-template <ResultCode func(Core::System&, u32*)>
+template <Result func(Core::System&, u32*)>
void SvcWrap64(Core::System& system) {
u32 param = 0;
const u32 retval = func(system, &param).raw;
@@ -97,7 +97,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u32*, u32)>
+template <Result func(Core::System&, u32*, u32)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
@@ -105,7 +105,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u32*, u32*)>
+template <Result func(Core::System&, u32*, u32*)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
u32 param_2 = 0;
@@ -118,7 +118,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u32*, u64)>
+template <Result func(Core::System&, u32*, u64)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1)).raw;
@@ -126,7 +126,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u32*, u64, u32)>
+template <Result func(Core::System&, u32*, u64, u32)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval =
@@ -136,7 +136,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u64*, u32)>
+template <Result func(Core::System&, u64*, u32)>
void SvcWrap64(Core::System& system) {
u64 param_1 = 0;
const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
@@ -145,12 +145,12 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u64, u32)>
+template <Result func(Core::System&, u64, u32)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw);
}
-template <ResultCode func(Core::System&, u64*, u64)>
+template <Result func(Core::System&, u64*, u64)>
void SvcWrap64(Core::System& system) {
u64 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1)).raw;
@@ -159,7 +159,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u64*, u32, u32)>
+template <Result func(Core::System&, u64*, u32, u32)>
void SvcWrap64(Core::System& system) {
u64 param_1 = 0;
const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1)),
@@ -171,7 +171,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by GetResourceLimitLimitValue.
-template <ResultCode func(Core::System&, u64*, Handle, LimitableResource)>
+template <Result func(Core::System&, u64*, Handle, LimitableResource)>
void SvcWrap64(Core::System& system) {
u64 param_1 = 0;
const u32 retval = func(system, &param_1, static_cast<Handle>(Param(system, 1)),
@@ -182,13 +182,13 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u32, u64)>
+template <Result func(Core::System&, u32, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw);
}
// Used by SetResourceLimitLimitValue
-template <ResultCode func(Core::System&, Handle, LimitableResource, u64)>
+template <Result func(Core::System&, Handle, LimitableResource, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
static_cast<LimitableResource>(Param(system, 1)), Param(system, 2))
@@ -196,7 +196,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by SetThreadCoreMask
-template <ResultCode func(Core::System&, Handle, s32, u64)>
+template <Result func(Core::System&, Handle, s32, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
static_cast<s32>(Param(system, 1)), Param(system, 2))
@@ -204,44 +204,44 @@ void SvcWrap64(Core::System& system) {
}
// Used by GetThreadCoreMask
-template <ResultCode func(Core::System&, Handle, s32*, u64*)>
+template <Result func(Core::System&, Handle, s32*, u64*)>
void SvcWrap64(Core::System& system) {
s32 param_1 = 0;
u64 param_2 = 0;
- const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2);
+ const Result retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2);
system.CurrentArmInterface().SetReg(1, param_1);
system.CurrentArmInterface().SetReg(2, param_2);
FuncReturn(system, retval.raw);
}
-template <ResultCode func(Core::System&, u64, u64, u32, u32)>
+template <Result func(Core::System&, u64, u64, u32, u32)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
.raw);
}
-template <ResultCode func(Core::System&, u64, u64, u32, u64)>
+template <Result func(Core::System&, u64, u64, u32, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<u32>(Param(system, 2)), Param(system, 3))
.raw);
}
-template <ResultCode func(Core::System&, u32, u64, u32)>
+template <Result func(Core::System&, u32, u64, u32)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
static_cast<u32>(Param(system, 2)))
.raw);
}
-template <ResultCode func(Core::System&, u64, u64, u64)>
+template <Result func(Core::System&, u64, u64, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw);
}
-template <ResultCode func(Core::System&, u64, u64, u32)>
+template <Result func(Core::System&, u64, u64, u32)>
void SvcWrap64(Core::System& system) {
FuncReturn(
system,
@@ -249,7 +249,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by SetMemoryPermission
-template <ResultCode func(Core::System&, u64, u64, Svc::MemoryPermission)>
+template <Result func(Core::System&, u64, u64, Svc::MemoryPermission)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<Svc::MemoryPermission>(Param(system, 2)))
@@ -257,14 +257,14 @@ void SvcWrap64(Core::System& system) {
}
// Used by MapSharedMemory
-template <ResultCode func(Core::System&, Handle, u64, u64, Svc::MemoryPermission)>
+template <Result func(Core::System&, Handle, u64, u64, Svc::MemoryPermission)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), Param(system, 1),
Param(system, 2), static_cast<Svc::MemoryPermission>(Param(system, 3)))
.raw);
}
-template <ResultCode func(Core::System&, u32, u64, u64)>
+template <Result func(Core::System&, u32, u64, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(
system,
@@ -272,7 +272,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by WaitSynchronization
-template <ResultCode func(Core::System&, s32*, u64, s32, s64)>
+template <Result func(Core::System&, s32*, u64, s32, s64)>
void SvcWrap64(Core::System& system) {
s32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), static_cast<s32>(Param(system, 2)),
@@ -283,7 +283,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u64, u64, u32, s64)>
+template <Result func(Core::System&, u64, u64, u32, s64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
@@ -291,7 +291,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by GetInfo
-template <ResultCode func(Core::System&, u64*, u64, Handle, u64)>
+template <Result func(Core::System&, u64*, u64, Handle, u64)>
void SvcWrap64(Core::System& system) {
u64 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1),
@@ -302,7 +302,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)>
+template <Result func(Core::System&, u32*, u64, u64, u64, u32, s32)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3),
@@ -314,7 +314,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by CreateTransferMemory
-template <ResultCode func(Core::System&, Handle*, u64, u64, Svc::MemoryPermission)>
+template <Result func(Core::System&, Handle*, u64, u64, Svc::MemoryPermission)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2),
@@ -326,7 +326,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by CreateCodeMemory
-template <ResultCode func(Core::System&, Handle*, u64, u64)>
+template <Result func(Core::System&, Handle*, u64, u64)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2)).raw;
@@ -335,7 +335,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
+template <Result func(Core::System&, Handle*, u64, u32, u32)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
@@ -347,7 +347,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by WaitForAddress
-template <ResultCode func(Core::System&, u64, Svc::ArbitrationType, s32, s64)>
+template <Result func(Core::System&, u64, Svc::ArbitrationType, s32, s64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system,
func(system, Param(system, 0), static_cast<Svc::ArbitrationType>(Param(system, 1)),
@@ -356,7 +356,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by SignalToAddress
-template <ResultCode func(Core::System&, u64, Svc::SignalType, s32, s32)>
+template <Result func(Core::System&, u64, Svc::SignalType, s32, s32)>
void SvcWrap64(Core::System& system) {
FuncReturn(system,
func(system, Param(system, 0), static_cast<Svc::SignalType>(Param(system, 1)),
@@ -425,7 +425,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by QueryMemory32, ArbitrateLock32
-template <ResultCode func(Core::System&, u32, u32, u32)>
+template <Result func(Core::System&, u32, u32, u32)>
void SvcWrap32(Core::System& system) {
FuncReturn32(system,
func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw);
@@ -456,7 +456,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by CreateThread32
-template <ResultCode func(Core::System&, Handle*, u32, u32, u32, u32, s32)>
+template <Result func(Core::System&, Handle*, u32, u32, u32, u32, s32)>
void SvcWrap32(Core::System& system) {
Handle param_1 = 0;
@@ -469,7 +469,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by GetInfo32
-template <ResultCode func(Core::System&, u32*, u32*, u32, u32, u32, u32)>
+template <Result func(Core::System&, u32*, u32*, u32, u32, u32, u32)>
void SvcWrap32(Core::System& system) {
u32 param_1 = 0;
u32 param_2 = 0;
@@ -484,7 +484,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by GetThreadPriority32, ConnectToNamedPort32
-template <ResultCode func(Core::System&, u32*, u32)>
+template <Result func(Core::System&, u32*, u32)>
void SvcWrap32(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param32(system, 1)).raw;
@@ -493,7 +493,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by GetThreadId32
-template <ResultCode func(Core::System&, u32*, u32*, u32)>
+template <Result func(Core::System&, u32*, u32*, u32)>
void SvcWrap32(Core::System& system) {
u32 param_1 = 0;
u32 param_2 = 0;
@@ -516,7 +516,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by CreateEvent32
-template <ResultCode func(Core::System&, Handle*, Handle*)>
+template <Result func(Core::System&, Handle*, Handle*)>
void SvcWrap32(Core::System& system) {
Handle param_1 = 0;
Handle param_2 = 0;
@@ -528,7 +528,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by GetThreadId32
-template <ResultCode func(Core::System&, Handle, u32*, u32*, u32*)>
+template <Result func(Core::System&, Handle, u32*, u32*, u32*)>
void SvcWrap32(Core::System& system) {
u32 param_1 = 0;
u32 param_2 = 0;
@@ -542,7 +542,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by GetThreadCoreMask32
-template <ResultCode func(Core::System&, Handle, s32*, u32*, u32*)>
+template <Result func(Core::System&, Handle, s32*, u32*, u32*)>
void SvcWrap32(Core::System& system) {
s32 param_1 = 0;
u32 param_2 = 0;
@@ -562,7 +562,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by SetThreadActivity32
-template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)>
+template <Result func(Core::System&, Handle, Svc::ThreadActivity)>
void SvcWrap32(Core::System& system) {
const u32 retval = func(system, static_cast<Handle>(Param(system, 0)),
static_cast<Svc::ThreadActivity>(Param(system, 1)))
@@ -571,7 +571,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by SetThreadPriority32
-template <ResultCode func(Core::System&, Handle, u32)>
+template <Result func(Core::System&, Handle, u32)>
void SvcWrap32(Core::System& system) {
const u32 retval =
func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw;
@@ -579,7 +579,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by SetMemoryAttribute32
-template <ResultCode func(Core::System&, Handle, u32, u32, u32)>
+template <Result func(Core::System&, Handle, u32, u32, u32)>
void SvcWrap32(Core::System& system) {
const u32 retval =
func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1)),
@@ -589,7 +589,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by MapSharedMemory32
-template <ResultCode func(Core::System&, Handle, u32, u32, Svc::MemoryPermission)>
+template <Result func(Core::System&, Handle, u32, u32, Svc::MemoryPermission)>
void SvcWrap32(Core::System& system) {
const u32 retval = func(system, static_cast<Handle>(Param(system, 0)),
static_cast<u32>(Param(system, 1)), static_cast<u32>(Param(system, 2)),
@@ -599,7 +599,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by SetThreadCoreMask32
-template <ResultCode func(Core::System&, Handle, s32, u32, u32)>
+template <Result func(Core::System&, Handle, s32, u32, u32)>
void SvcWrap32(Core::System& system) {
const u32 retval =
func(system, static_cast<Handle>(Param(system, 0)), static_cast<s32>(Param(system, 1)),
@@ -609,7 +609,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by WaitProcessWideKeyAtomic32
-template <ResultCode func(Core::System&, u32, u32, Handle, u32, u32)>
+template <Result func(Core::System&, u32, u32, Handle, u32, u32)>
void SvcWrap32(Core::System& system) {
const u32 retval =
func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)),
@@ -620,7 +620,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by WaitForAddress32
-template <ResultCode func(Core::System&, u32, Svc::ArbitrationType, s32, u32, u32)>
+template <Result func(Core::System&, u32, Svc::ArbitrationType, s32, u32, u32)>
void SvcWrap32(Core::System& system) {
const u32 retval = func(system, static_cast<u32>(Param(system, 0)),
static_cast<Svc::ArbitrationType>(Param(system, 1)),
@@ -631,7 +631,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by SignalToAddress32
-template <ResultCode func(Core::System&, u32, Svc::SignalType, s32, s32)>
+template <Result func(Core::System&, u32, Svc::SignalType, s32, s32)>
void SvcWrap32(Core::System& system) {
const u32 retval = func(system, static_cast<u32>(Param(system, 0)),
static_cast<Svc::SignalType>(Param(system, 1)),
@@ -641,13 +641,13 @@ void SvcWrap32(Core::System& system) {
}
// Used by SendSyncRequest32, ArbitrateUnlock32
-template <ResultCode func(Core::System&, u32)>
+template <Result func(Core::System&, u32)>
void SvcWrap32(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
}
// Used by CreateTransferMemory32
-template <ResultCode func(Core::System&, Handle*, u32, u32, Svc::MemoryPermission)>
+template <Result func(Core::System&, Handle*, u32, u32, Svc::MemoryPermission)>
void SvcWrap32(Core::System& system) {
Handle handle = 0;
const u32 retval = func(system, &handle, Param32(system, 1), Param32(system, 2),
@@ -658,7 +658,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by WaitSynchronization32
-template <ResultCode func(Core::System&, u32, u32, s32, u32, s32*)>
+template <Result func(Core::System&, u32, u32, s32, u32, s32*)>
void SvcWrap32(Core::System& system) {
s32 param_1 = 0;
const u32 retval = func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2),
@@ -669,7 +669,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by CreateCodeMemory32
-template <ResultCode func(Core::System&, Handle*, u32, u32)>
+template <Result func(Core::System&, Handle*, u32, u32)>
void SvcWrap32(Core::System& system) {
Handle handle = 0;
@@ -680,7 +680,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by ControlCodeMemory32
-template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
+template <Result func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
void SvcWrap32(Core::System& system) {
const u32 retval =
func(system, Param32(system, 0), Param32(system, 1), Param(system, 2), Param(system, 4),
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index 2724c3782..5ee72c432 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -11,15 +11,17 @@
namespace Kernel {
TimeManager::TimeManager(Core::System& system_) : system{system_} {
- time_manager_event_type =
- Core::Timing::CreateEvent("Kernel::TimeManagerCallback",
- [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
- KThread* thread = reinterpret_cast<KThread*>(thread_handle);
- {
- KScopedSchedulerLock sl(system.Kernel());
- thread->OnTimer();
- }
- });
+ time_manager_event_type = Core::Timing::CreateEvent(
+ "Kernel::TimeManagerCallback",
+ [this](std::uintptr_t thread_handle, s64 time,
+ std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
+ KThread* thread = reinterpret_cast<KThread*>(thread_handle);
+ {
+ KScopedSchedulerLock sl(system.Kernel());
+ thread->OnTimer();
+ }
+ return std::nullopt;
+ });
}
void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 569dd9f38..4de44cd06 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -112,15 +111,15 @@ enum class ErrorModule : u32 {
};
/// Encapsulates a Horizon OS error code, allowing it to be separated into its constituent fields.
-union ResultCode {
+union Result {
u32 raw;
BitField<0, 9, ErrorModule> module;
BitField<9, 13, u32> description;
- constexpr explicit ResultCode(u32 raw_) : raw(raw_) {}
+ constexpr explicit Result(u32 raw_) : raw(raw_) {}
- constexpr ResultCode(ErrorModule module_, u32 description_)
+ constexpr Result(ErrorModule module_, u32 description_)
: raw(module.FormatValue(module_) | description.FormatValue(description_)) {}
[[nodiscard]] constexpr bool IsSuccess() const {
@@ -132,18 +131,18 @@ union ResultCode {
}
};
-[[nodiscard]] constexpr bool operator==(const ResultCode& a, const ResultCode& b) {
+[[nodiscard]] constexpr bool operator==(const Result& a, const Result& b) {
return a.raw == b.raw;
}
-[[nodiscard]] constexpr bool operator!=(const ResultCode& a, const ResultCode& b) {
+[[nodiscard]] constexpr bool operator!=(const Result& a, const Result& b) {
return !operator==(a, b);
}
// Convenience functions for creating some common kinds of errors:
-/// The default success `ResultCode`.
-constexpr ResultCode ResultSuccess(0);
+/// The default success `Result`.
+constexpr Result ResultSuccess(0);
/**
* Placeholder result code used for unknown error codes.
@@ -151,24 +150,24 @@ constexpr ResultCode ResultSuccess(0);
* @note This should only be used when a particular error code
* is not known yet.
*/
-constexpr ResultCode ResultUnknown(UINT32_MAX);
+constexpr Result ResultUnknown(UINT32_MAX);
/**
* A ResultRange defines an inclusive range of error descriptions within an error module.
- * This can be used to check whether the description of a given ResultCode falls within the range.
- * The conversion function returns a ResultCode with its description set to description_start.
+ * This can be used to check whether the description of a given Result falls within the range.
+ * The conversion function returns a Result with its description set to description_start.
*
* An example of how it could be used:
* \code
* constexpr ResultRange ResultCommonError{ErrorModule::Common, 0, 9999};
*
- * ResultCode Example(int value) {
- * const ResultCode result = OtherExample(value);
+ * Result Example(int value) {
+ * const Result result = OtherExample(value);
*
* // This will only evaluate to true if result.module is ErrorModule::Common and
* // result.description is in between 0 and 9999 inclusive.
* if (ResultCommonError.Includes(result)) {
- * // This returns ResultCode{ErrorModule::Common, 0};
+ * // This returns Result{ErrorModule::Common, 0};
* return ResultCommonError;
* }
*
@@ -181,22 +180,22 @@ public:
consteval ResultRange(ErrorModule module, u32 description_start, u32 description_end_)
: code{module, description_start}, description_end{description_end_} {}
- [[nodiscard]] constexpr operator ResultCode() const {
+ [[nodiscard]] constexpr operator Result() const {
return code;
}
- [[nodiscard]] constexpr bool Includes(ResultCode other) const {
+ [[nodiscard]] constexpr bool Includes(Result other) const {
return code.module == other.module && code.description <= other.description &&
other.description <= description_end;
}
private:
- ResultCode code;
+ Result code;
u32 description_end;
};
/**
- * This is an optional value type. It holds a `ResultCode` and, if that code is ResultSuccess, it
+ * This is an optional value type. It holds a `Result` and, if that code is ResultSuccess, it
* also holds a result of type `T`. If the code is an error code (not ResultSuccess), then trying
* to access the inner value with operator* is undefined behavior and will assert with Unwrap().
* Users of this class must be cognizant to check the status of the ResultVal with operator bool(),
@@ -207,7 +206,7 @@ private:
* ResultVal<int> Frobnicate(float strength) {
* if (strength < 0.f || strength > 1.0f) {
* // Can't frobnicate too weakly or too strongly
- * return ResultCode{ErrorModule::Common, 1};
+ * return Result{ErrorModule::Common, 1};
* } else {
* // Frobnicated! Give caller a cookie
* return 42;
@@ -230,7 +229,7 @@ class ResultVal {
public:
constexpr ResultVal() : expected{} {}
- constexpr ResultVal(ResultCode code) : expected{Common::Unexpected(code)} {}
+ constexpr ResultVal(Result code) : expected{Common::Unexpected(code)} {}
constexpr ResultVal(ResultRange range) : expected{Common::Unexpected(range)} {}
@@ -252,7 +251,7 @@ public:
return expected.has_value();
}
- [[nodiscard]] constexpr ResultCode Code() const {
+ [[nodiscard]] constexpr Result Code() const {
return expected.has_value() ? ResultSuccess : expected.error();
}
@@ -320,7 +319,7 @@ public:
private:
// TODO (Morph): Replace this with C++23 std::expected.
- Common::Expected<T, ResultCode> expected;
+ Common::Expected<T, Result> expected;
};
/**
@@ -337,7 +336,7 @@ private:
target = std::move(*CONCAT2(check_result_L, __LINE__))
/**
- * Analogous to CASCADE_RESULT, but for a bare ResultCode. The code will be propagated if
+ * Analogous to CASCADE_RESULT, but for a bare Result. The code will be propagated if
* non-success, or discarded otherwise.
*/
#define CASCADE_CODE(source) \
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 88b74cbb0..def105832 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -28,11 +28,11 @@
namespace Service::Account {
-constexpr ResultCode ERR_INVALID_USER_ID{ErrorModule::Account, 20};
-constexpr ResultCode ERR_INVALID_APPLICATION_ID{ErrorModule::Account, 22};
-constexpr ResultCode ERR_INVALID_BUFFER{ErrorModule::Account, 30};
-constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 31};
-constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
+constexpr Result ERR_INVALID_USER_ID{ErrorModule::Account, 20};
+constexpr Result ERR_INVALID_APPLICATION_ID{ErrorModule::Account, 22};
+constexpr Result ERR_INVALID_BUFFER{ErrorModule::Account, 30};
+constexpr Result ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 31};
+constexpr Result ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
// Thumbnails are hard coded to be at least this size
constexpr std::size_t THUMBNAIL_SIZE = 0x24000;
@@ -290,7 +290,7 @@ protected:
void Get(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
ProfileBase profile_base{};
- ProfileData data{};
+ UserData data{};
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 16};
@@ -373,18 +373,18 @@ protected:
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.RawString());
- if (user_data.size() < sizeof(ProfileData)) {
- LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
+ if (user_data.size() < sizeof(UserData)) {
+ LOG_ERROR(Service_ACC, "UserData buffer too small!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_INVALID_BUFFER);
return;
}
- ProfileData data;
- std::memcpy(&data, user_data.data(), sizeof(ProfileData));
+ UserData data;
+ std::memcpy(&data, user_data.data(), sizeof(UserData));
if (!profile_manager.SetProfileBaseAndData(user_id, base, data)) {
- LOG_ERROR(Service_ACC, "Failed to update profile data and base!");
+ LOG_ERROR(Service_ACC, "Failed to update user data and base!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_FAILED_SAVE_DATA);
return;
@@ -406,15 +406,15 @@ protected:
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.RawString());
- if (user_data.size() < sizeof(ProfileData)) {
- LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
+ if (user_data.size() < sizeof(UserData)) {
+ LOG_ERROR(Service_ACC, "UserData buffer too small!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_INVALID_BUFFER);
return;
}
- ProfileData data;
- std::memcpy(&data, user_data.data(), sizeof(ProfileData));
+ UserData data;
+ std::memcpy(&data, user_data.data(), sizeof(UserData));
Common::FS::IOFile image(GetImagePath(user_id), Common::FS::FileAccessMode::Write,
Common::FS::FileType::BinaryFile);
@@ -505,7 +505,7 @@ protected:
void Cancel() override {}
- ResultCode GetResult() const override {
+ Result GetResult() const override {
return ResultSuccess;
}
};
@@ -747,7 +747,7 @@ void Module::Interface::InitializeApplicationInfoRestricted(Kernel::HLERequestCo
rb.Push(InitializeApplicationInfoBase());
}
-ResultCode Module::Interface::InitializeApplicationInfoBase() {
+Result Module::Interface::InitializeApplicationInfoBase() {
if (application_info) {
LOG_ERROR(Service_ACC, "Application already initialized");
return ERR_ACCOUNTINFO_ALREADY_INITIALIZED;
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index fff447fc3..1621e7c0a 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -41,7 +41,7 @@ public:
void StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx);
private:
- ResultCode InitializeApplicationInfoBase();
+ Result InitializeApplicationInfoBase();
void StoreSaveDataThumbnail(Kernel::HLERequestContext& ctx, const Common::UUID& uuid,
const u64 tid);
diff --git a/src/core/hle/service/acc/async_context.h b/src/core/hle/service/acc/async_context.h
index e4929f7f0..26332d241 100644
--- a/src/core/hle/service/acc/async_context.h
+++ b/src/core/hle/service/acc/async_context.h
@@ -26,7 +26,7 @@ public:
protected:
virtual bool IsComplete() const = 0;
virtual void Cancel() = 0;
- virtual ResultCode GetResult() const = 0;
+ virtual Result GetResult() const = 0;
void MarkComplete();
diff --git a/src/core/hle/service/acc/errors.h b/src/core/hle/service/acc/errors.h
index eafb75713..e9c16b951 100644
--- a/src/core/hle/service/acc/errors.h
+++ b/src/core/hle/service/acc/errors.h
@@ -7,7 +7,7 @@
namespace Service::Account {
-constexpr ResultCode ERR_ACCOUNTINFO_BAD_APPLICATION{ErrorModule::Account, 22};
-constexpr ResultCode ERR_ACCOUNTINFO_ALREADY_INITIALIZED{ErrorModule::Account, 41};
+constexpr Result ERR_ACCOUNTINFO_BAD_APPLICATION{ErrorModule::Account, 22};
+constexpr Result ERR_ACCOUNTINFO_ALREADY_INITIALIZED{ErrorModule::Account, 41};
} // namespace Service::Account
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 0ef298180..a58da4d5f 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -22,7 +22,7 @@ struct UserRaw {
UUID uuid2{};
u64 timestamp{};
ProfileUsername username{};
- ProfileData extra_data{};
+ UserData extra_data{};
};
static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size.");
@@ -33,9 +33,9 @@ struct ProfileDataRaw {
static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size.");
// TODO(ogniK): Get actual error codes
-constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, u32(-1));
-constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, u32(-2));
-constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
+constexpr Result ERROR_TOO_MANY_USERS(ErrorModule::Account, u32(-1));
+constexpr Result ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, u32(-2));
+constexpr Result ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "system/save/8000000000000010/su/avators";
@@ -87,7 +87,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) {
}
/// Helper function to register a user to the system
-ResultCode ProfileManager::AddUser(const ProfileInfo& user) {
+Result ProfileManager::AddUser(const ProfileInfo& user) {
if (!AddToProfiles(user)) {
return ERROR_TOO_MANY_USERS;
}
@@ -96,7 +96,7 @@ ResultCode ProfileManager::AddUser(const ProfileInfo& user) {
/// Create a new user on the system. If the uuid of the user already exists, the user is not
/// created.
-ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& username) {
+Result ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& username) {
if (user_count == MAX_USERS) {
return ERROR_TOO_MANY_USERS;
}
@@ -123,7 +123,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern
/// Creates a new user on the system. This function allows a much simpler method of registration
/// specifically by allowing an std::string for the username. This is required specifically since
/// we're loading a string straight from the config
-ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) {
+Result ProfileManager::CreateNewUser(UUID uuid, const std::string& username) {
ProfileUsername username_output{};
if (username.size() > username_output.size()) {
@@ -263,7 +263,7 @@ UUID ProfileManager::GetLastOpenedUser() const {
/// Return the users profile base and the unknown arbitary data.
bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
- ProfileData& data) const {
+ UserData& data) const {
if (GetProfileBase(index, profile)) {
data = profiles[*index].data;
return true;
@@ -272,15 +272,14 @@ bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, Pro
}
/// Return the users profile base and the unknown arbitary data.
-bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile,
- ProfileData& data) const {
+bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, UserData& data) const {
const auto idx = GetUserIndex(uuid);
return GetProfileBaseAndData(idx, profile, data);
}
/// Return the users profile base and the unknown arbitary data.
bool ProfileManager::GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
- ProfileData& data) const {
+ UserData& data) const {
return GetProfileBaseAndData(user.user_uuid, profile, data);
}
@@ -318,7 +317,7 @@ bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
}
bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new,
- const ProfileData& data_new) {
+ const UserData& data_new) {
const auto index = GetUserIndex(uuid);
if (index.has_value() && SetProfileBase(uuid, profile_new)) {
profiles[*index].data = data_new;
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 955dbd3d6..135f7d0d5 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -22,7 +22,7 @@ using UserIDArray = std::array<Common::UUID, MAX_USERS>;
/// Contains extra data related to a user.
/// TODO: RE this structure
-struct ProfileData {
+struct UserData {
INSERT_PADDING_WORDS_NOINIT(1);
u32 icon_id;
u8 bg_color_id;
@@ -30,7 +30,7 @@ struct ProfileData {
INSERT_PADDING_BYTES_NOINIT(0x10);
INSERT_PADDING_BYTES_NOINIT(0x60);
};
-static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect size");
+static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
/// This holds general information about a users profile. This is where we store all the information
/// based on a specific user
@@ -38,7 +38,7 @@ struct ProfileInfo {
Common::UUID user_uuid{};
ProfileUsername username{};
u64 creation_time{};
- ProfileData data{}; // TODO(ognik): Work out what this is
+ UserData data{}; // TODO(ognik): Work out what this is
bool is_open{};
};
@@ -64,9 +64,9 @@ public:
ProfileManager();
~ProfileManager();
- ResultCode AddUser(const ProfileInfo& user);
- ResultCode CreateNewUser(Common::UUID uuid, const ProfileUsername& username);
- ResultCode CreateNewUser(Common::UUID uuid, const std::string& username);
+ Result AddUser(const ProfileInfo& user);
+ Result CreateNewUser(Common::UUID uuid, const ProfileUsername& username);
+ Result CreateNewUser(Common::UUID uuid, const std::string& username);
std::optional<Common::UUID> GetUser(std::size_t index) const;
std::optional<std::size_t> GetUserIndex(const Common::UUID& uuid) const;
std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
@@ -74,10 +74,9 @@ public:
bool GetProfileBase(Common::UUID uuid, ProfileBase& profile) const;
bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
- ProfileData& data) const;
- bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, ProfileData& data) const;
- bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
- ProfileData& data) const;
+ UserData& data) const;
+ bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, UserData& data) const;
+ bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, UserData& data) const;
std::size_t GetUserCount() const;
std::size_t GetOpenUserCount() const;
bool UserExists(Common::UUID uuid) const;
@@ -93,7 +92,7 @@ public:
bool RemoveUser(Common::UUID uuid);
bool SetProfileBase(Common::UUID uuid, const ProfileBase& profile_new);
bool SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new,
- const ProfileData& data_new);
+ const UserData& data_new);
private:
void ParseUserSaveFile();
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 4657bdabc..118f226e4 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -5,7 +5,6 @@
#include <array>
#include <cinttypes>
#include <cstring>
-#include "audio_core/audio_renderer.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
@@ -40,9 +39,9 @@
namespace Service::AM {
-constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 2};
-constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 3};
-constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 503};
+constexpr Result ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 2};
+constexpr Result ERR_NO_MESSAGES{ErrorModule::AM, 3};
+constexpr Result ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 503};
enum class LaunchParameterKind : u32 {
ApplicationSpecific = 1,
@@ -238,6 +237,7 @@ IDebugFunctions::IDebugFunctions(Core::System& system_)
{130, nullptr, "FriendInvitationSetApplicationParameter"},
{131, nullptr, "FriendInvitationClearApplicationParameter"},
{132, nullptr, "FriendInvitationPushApplicationParameter"},
+ {140, nullptr, "RestrictPowerOperationForSecureLaunchModeForDebug"},
{900, nullptr, "GetGrcProcessLaunchedSystemEvent"},
};
// clang-format on
@@ -285,7 +285,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
{62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
{63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
{64, nullptr, "SetInputDetectionSourceSet"},
- {65, nullptr, "ReportUserIsActive"},
+ {65, &ISelfController::ReportUserIsActive, "ReportUserIsActive"},
{66, nullptr, "GetCurrentIlluminance"},
{67, nullptr, "IsIlluminanceAvailable"},
{68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"},
@@ -365,7 +365,7 @@ void ISelfController::LeaveFatalSection(Kernel::HLERequestContext& ctx) {
// Entry and exit of fatal sections must be balanced.
if (num_fatal_sections_entered == 0) {
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultCode{ErrorModule::AM, 512});
+ rb.Push(Result{ErrorModule::AM, 512});
return;
}
@@ -517,6 +517,13 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
rb.Push<u32>(idle_time_detection_extension);
}
+void ISelfController::ReportUserIsActive(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void ISelfController::SetAutoSleepDisabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
is_auto_sleep_disabled = rp.Pop<bool>();
@@ -635,6 +642,10 @@ void AppletMessageQueue::RequestExit() {
PushMessage(AppletMessage::Exit);
}
+void AppletMessageQueue::RequestResume() {
+ PushMessage(AppletMessage::Resume);
+}
+
void AppletMessageQueue::FocusStateChanged() {
PushMessage(AppletMessage::FocusStateChanged);
}
@@ -686,7 +697,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
{66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"},
{67, nullptr, "CancelCpuBoostMode"},
{68, nullptr, "GetBuiltInDisplayType"},
- {80, nullptr, "PerformSystemButtonPressingIfInFocus"},
+ {80, &ICommonStateGetter::PerformSystemButtonPressingIfInFocus, "PerformSystemButtonPressingIfInFocus"},
{90, nullptr, "SetPerformanceConfigurationChangedNotification"},
{91, nullptr, "GetCurrentPerformanceConfiguration"},
{100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"},
@@ -826,6 +837,16 @@ void ICommonStateGetter::SetCpuBoostMode(Kernel::HLERequestContext& ctx) {
apm_sys->SetCpuBoostMode(ctx);
}
+void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto system_button{rp.PopEnum<SystemButtonType>()};
+
+ LOG_WARNING(Service_AM, "(STUBBED) called, system_button={}", system_button);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(
Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
@@ -1300,6 +1321,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
{34, nullptr, "SelectApplicationLicense"},
{35, nullptr, "GetDeviceSaveDataSizeMax"},
+ {36, nullptr, "GetLimitedApplicationLicense"},
+ {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"},
{40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
{50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
{60, nullptr, "SetMediaPlaybackStateForApplication"},
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 06f13aa09..bb75c6281 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -90,6 +90,7 @@ public:
AppletMessage PopMessage();
std::size_t GetMessageCount() const;
void RequestExit();
+ void RequestResume();
void FocusStateChanged();
void OperationModeChanged();
@@ -174,6 +175,7 @@ private:
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
+ void ReportUserIsActive(Kernel::HLERequestContext& ctx);
void SetAutoSleepDisabled(Kernel::HLERequestContext& ctx);
void IsAutoSleepDisabled(Kernel::HLERequestContext& ctx);
void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
@@ -220,6 +222,18 @@ private:
Docked = 1,
};
+ // This is nn::am::service::SystemButtonType
+ enum class SystemButtonType {
+ None,
+ HomeButtonShortPressing,
+ HomeButtonLongPressing,
+ PowerButtonShortPressing,
+ PowerButtonLongPressing,
+ ShutdownSystem,
+ CaptureButtonShortPressing,
+ CaptureButtonLongPressing,
+ };
+
void GetEventHandle(Kernel::HLERequestContext& ctx);
void ReceiveMessage(Kernel::HLERequestContext& ctx);
void GetCurrentFocusState(Kernel::HLERequestContext& ctx);
@@ -234,6 +248,7 @@ private:
void EndVrModeEx(Kernel::HLERequestContext& ctx);
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
void SetCpuBoostMode(Kernel::HLERequestContext& ctx);
+ void PerformSystemButtonPressingIfInFocus(Kernel::HLERequestContext& ctx);
void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(Kernel::HLERequestContext& ctx);
std::shared_ptr<AppletMessageQueue> msg_queue;
diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp
index 0a5603d18..b418031de 100644
--- a/src/core/hle/service/am/applets/applet_controller.cpp
+++ b/src/core/hle/service/am/applets/applet_controller.cpp
@@ -20,9 +20,9 @@
namespace Service::AM::Applets {
// This error code (0x183ACA) is thrown when the applet fails to initialize.
-[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101};
+[[maybe_unused]] constexpr Result ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101};
// This error code (0x183CCA) is thrown when the u32 result in ControllerSupportResultInfo is 2.
-[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102};
+[[maybe_unused]] constexpr Result ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102};
static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
@@ -173,7 +173,7 @@ bool Controller::TransactionComplete() const {
return complete;
}
-ResultCode Controller::GetStatus() const {
+Result Controller::GetStatus() const {
return status;
}
diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h
index e1a34853d..1f9adec65 100644
--- a/src/core/hle/service/am/applets/applet_controller.h
+++ b/src/core/hle/service/am/applets/applet_controller.h
@@ -126,7 +126,7 @@ public:
void Initialize() override;
bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
+ Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
@@ -143,7 +143,7 @@ private:
ControllerUpdateFirmwareArg controller_update_arg;
ControllerKeyRemappingArg controller_key_remapping_arg;
bool complete{false};
- ResultCode status{ResultSuccess};
+ Result status{ResultSuccess};
bool is_single_mode{false};
std::vector<u8> out_data;
};
diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp
index 0b87c60b9..fcf34bf7e 100644
--- a/src/core/hle/service/am/applets/applet_error.cpp
+++ b/src/core/hle/service/am/applets/applet_error.cpp
@@ -25,15 +25,15 @@ struct ErrorCode {
};
}
- static constexpr ErrorCode FromResultCode(ResultCode result) {
+ static constexpr ErrorCode FromResult(Result result) {
return {
.error_category{2000 + static_cast<u32>(result.module.Value())},
.error_number{result.description.Value()},
};
}
- constexpr ResultCode ToResultCode() const {
- return ResultCode{static_cast<ErrorModule>(error_category - 2000), error_number};
+ constexpr Result ToResult() const {
+ return Result{static_cast<ErrorModule>(error_category - 2000), error_number};
}
};
static_assert(sizeof(ErrorCode) == 0x8, "ErrorCode has incorrect size.");
@@ -97,8 +97,8 @@ void CopyArgumentData(const std::vector<u8>& data, T& variable) {
std::memcpy(&variable, data.data(), sizeof(T));
}
-ResultCode Decode64BitError(u64 error) {
- return ErrorCode::FromU64(error).ToResultCode();
+Result Decode64BitError(u64 error) {
+ return ErrorCode::FromU64(error).ToResult();
}
} // Anonymous namespace
@@ -127,16 +127,16 @@ void Error::Initialize() {
if (args->error.use_64bit_error_code) {
error_code = Decode64BitError(args->error.error_code_64);
} else {
- error_code = ResultCode(args->error.error_code_32);
+ error_code = Result(args->error.error_code_32);
}
break;
case ErrorAppletMode::ShowSystemError:
CopyArgumentData(data, args->system_error);
- error_code = ResultCode(Decode64BitError(args->system_error.error_code_64));
+ error_code = Result(Decode64BitError(args->system_error.error_code_64));
break;
case ErrorAppletMode::ShowApplicationError:
CopyArgumentData(data, args->application_error);
- error_code = ResultCode(args->application_error.error_code);
+ error_code = Result(args->application_error.error_code);
break;
case ErrorAppletMode::ShowErrorRecord:
CopyArgumentData(data, args->error_record);
@@ -151,7 +151,7 @@ bool Error::TransactionComplete() const {
return complete;
}
-ResultCode Error::GetStatus() const {
+Result Error::GetStatus() const {
return ResultSuccess;
}
diff --git a/src/core/hle/service/am/applets/applet_error.h b/src/core/hle/service/am/applets/applet_error.h
index 43487f647..d78d6f1d1 100644
--- a/src/core/hle/service/am/applets/applet_error.h
+++ b/src/core/hle/service/am/applets/applet_error.h
@@ -31,7 +31,7 @@ public:
void Initialize() override;
bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
+ Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
@@ -41,7 +41,7 @@ private:
union ErrorArguments;
const Core::Frontend::ErrorApplet& frontend;
- ResultCode error_code = ResultSuccess;
+ Result error_code = ResultSuccess;
ErrorAppletMode mode = ErrorAppletMode::ShowError;
std::unique_ptr<ErrorArguments> args;
diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp
index 41c002ef2..c34ef08b3 100644
--- a/src/core/hle/service/am/applets/applet_general_backend.cpp
+++ b/src/core/hle/service/am/applets/applet_general_backend.cpp
@@ -13,7 +13,7 @@
namespace Service::AM::Applets {
-constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221};
+constexpr Result ERROR_INVALID_PIN{ErrorModule::PCTL, 221};
static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) {
std::shared_ptr<IStorage> storage = broker.PopNormalDataToApplet();
@@ -71,7 +71,7 @@ bool Auth::TransactionComplete() const {
return complete;
}
-ResultCode Auth::GetStatus() const {
+Result Auth::GetStatus() const {
return successful ? ResultSuccess : ERROR_INVALID_PIN;
}
@@ -136,7 +136,7 @@ void Auth::AuthFinished(bool is_successful) {
successful = is_successful;
struct Return {
- ResultCode result_code;
+ Result result_code;
};
static_assert(sizeof(Return) == 0x4, "Return (AuthApplet) has incorrect size.");
@@ -170,7 +170,7 @@ bool PhotoViewer::TransactionComplete() const {
return complete;
}
-ResultCode PhotoViewer::GetStatus() const {
+Result PhotoViewer::GetStatus() const {
return ResultSuccess;
}
@@ -223,7 +223,7 @@ bool StubApplet::TransactionComplete() const {
return true;
}
-ResultCode StubApplet::GetStatus() const {
+Result StubApplet::GetStatus() const {
LOG_WARNING(Service_AM, "called (STUBBED)");
return ResultSuccess;
}
diff --git a/src/core/hle/service/am/applets/applet_general_backend.h b/src/core/hle/service/am/applets/applet_general_backend.h
index e647d0f41..a9f2535a2 100644
--- a/src/core/hle/service/am/applets/applet_general_backend.h
+++ b/src/core/hle/service/am/applets/applet_general_backend.h
@@ -25,7 +25,7 @@ public:
void Initialize() override;
bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
+ Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
@@ -56,7 +56,7 @@ public:
void Initialize() override;
bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
+ Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
@@ -77,7 +77,7 @@ public:
void Initialize() override;
bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
+ Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
diff --git a/src/core/hle/service/am/applets/applet_mii_edit.cpp b/src/core/hle/service/am/applets/applet_mii_edit.cpp
index 8d847c3f6..ae80ef506 100644
--- a/src/core/hle/service/am/applets/applet_mii_edit.cpp
+++ b/src/core/hle/service/am/applets/applet_mii_edit.cpp
@@ -62,7 +62,7 @@ bool MiiEdit::TransactionComplete() const {
return is_complete;
}
-ResultCode MiiEdit::GetStatus() const {
+Result MiiEdit::GetStatus() const {
return ResultSuccess;
}
diff --git a/src/core/hle/service/am/applets/applet_mii_edit.h b/src/core/hle/service/am/applets/applet_mii_edit.h
index 900754e57..d18dd3cf5 100644
--- a/src/core/hle/service/am/applets/applet_mii_edit.h
+++ b/src/core/hle/service/am/applets/applet_mii_edit.h
@@ -22,7 +22,7 @@ public:
void Initialize() override;
bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
+ Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
diff --git a/src/core/hle/service/am/applets/applet_profile_select.cpp b/src/core/hle/service/am/applets/applet_profile_select.cpp
index 02049fd9f..c738db028 100644
--- a/src/core/hle/service/am/applets/applet_profile_select.cpp
+++ b/src/core/hle/service/am/applets/applet_profile_select.cpp
@@ -12,7 +12,7 @@
namespace Service::AM::Applets {
-constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1};
+constexpr Result ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1};
ProfileSelect::ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_,
const Core::Frontend::ProfileSelectApplet& frontend_)
@@ -39,7 +39,7 @@ bool ProfileSelect::TransactionComplete() const {
return complete;
}
-ResultCode ProfileSelect::GetStatus() const {
+Result ProfileSelect::GetStatus() const {
return status;
}
diff --git a/src/core/hle/service/am/applets/applet_profile_select.h b/src/core/hle/service/am/applets/applet_profile_select.h
index 3a6e50eaa..b77f1d205 100644
--- a/src/core/hle/service/am/applets/applet_profile_select.h
+++ b/src/core/hle/service/am/applets/applet_profile_select.h
@@ -39,7 +39,7 @@ public:
void Initialize() override;
bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
+ Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
@@ -50,7 +50,7 @@ private:
UserSelectionConfig config;
bool complete = false;
- ResultCode status = ResultSuccess;
+ Result status = ResultSuccess;
std::vector<u8> final_data;
Core::System& system;
};
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.cpp b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
index 4116fbaa7..c18236045 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
@@ -80,7 +80,7 @@ bool SoftwareKeyboard::TransactionComplete() const {
return complete;
}
-ResultCode SoftwareKeyboard::GetStatus() const {
+Result SoftwareKeyboard::GetStatus() const {
return status;
}
@@ -536,6 +536,8 @@ void SoftwareKeyboard::InitializeFrontendNormalKeyboard() {
.sub_text{std::move(sub_text)},
.guide_text{std::move(guide_text)},
.initial_text{initial_text},
+ .left_optional_symbol_key{swkbd_config_common.left_optional_symbol_key},
+ .right_optional_symbol_key{swkbd_config_common.right_optional_symbol_key},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
@@ -591,6 +593,8 @@ void SoftwareKeyboard::InitializeFrontendInlineKeyboardOld() {
.sub_text{},
.guide_text{},
.initial_text{current_text},
+ .left_optional_symbol_key{appear_arg.left_optional_symbol_key},
+ .right_optional_symbol_key{appear_arg.right_optional_symbol_key},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
@@ -632,6 +636,8 @@ void SoftwareKeyboard::InitializeFrontendInlineKeyboardNew() {
.sub_text{},
.guide_text{},
.initial_text{current_text},
+ .left_optional_symbol_key{appear_arg.left_optional_symbol_key},
+ .right_optional_symbol_key{appear_arg.right_optional_symbol_key},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.h b/src/core/hle/service/am/applets/applet_software_keyboard.h
index c36806a72..b01b31c98 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.h
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.h
@@ -28,7 +28,7 @@ public:
void Initialize() override;
bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
+ Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
@@ -180,7 +180,7 @@ private:
bool is_background{false};
bool complete{false};
- ResultCode status{ResultSuccess};
+ Result status{ResultSuccess};
};
} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp
index 7b3f77a51..4b804b78c 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.cpp
+++ b/src/core/hle/service/am/applets/applet_web_browser.cpp
@@ -288,7 +288,7 @@ bool WebBrowser::TransactionComplete() const {
return complete;
}
-ResultCode WebBrowser::GetStatus() const {
+Result WebBrowser::GetStatus() const {
return status;
}
diff --git a/src/core/hle/service/am/applets/applet_web_browser.h b/src/core/hle/service/am/applets/applet_web_browser.h
index 6b602769b..fd727fac8 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.h
+++ b/src/core/hle/service/am/applets/applet_web_browser.h
@@ -32,7 +32,7 @@ public:
void Initialize() override;
bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
+ Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
@@ -66,7 +66,7 @@ private:
const Core::Frontend::WebBrowserApplet& frontend;
bool complete{false};
- ResultCode status{ResultSuccess};
+ Result status{ResultSuccess};
WebAppletVersion web_applet_version{};
WebArgHeader web_arg_header{};
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 2861fed0e..e78a57657 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -9,7 +9,7 @@
#include "common/swap.h"
#include "core/hle/service/kernel_helpers.h"
-union ResultCode;
+union Result;
namespace Core {
class System;
@@ -138,7 +138,7 @@ public:
virtual void Initialize();
virtual bool TransactionComplete() const = 0;
- virtual ResultCode GetStatus() const = 0;
+ virtual Result GetStatus() const = 0;
virtual void ExecuteInteractive() = 0;
virtual void Execute() = 0;
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index 18d3ae682..48a9a73a0 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -1,68 +1,211 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "audio_core/in/audio_in_system.h"
+#include "audio_core/renderer/audio_device.h"
+#include "common/common_funcs.h"
#include "common/logging/log.h"
+#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/audio/audin_u.h"
namespace Service::Audio {
+using namespace AudioCore::AudioIn;
-IAudioIn::IAudioIn(Core::System& system_)
- : ServiceFramework{system_, "IAudioIn"}, service_context{system_, "IAudioIn"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "GetAudioInState"},
- {1, &IAudioIn::Start, "Start"},
- {2, nullptr, "Stop"},
- {3, nullptr, "AppendAudioInBuffer"},
- {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"},
- {5, nullptr, "GetReleasedAudioInBuffer"},
- {6, nullptr, "ContainsAudioInBuffer"},
- {7, nullptr, "AppendUacInBuffer"},
- {8, &IAudioIn::AppendAudioInBufferAuto, "AppendAudioInBufferAuto"},
- {9, nullptr, "GetReleasedAudioInBuffersAuto"},
- {10, nullptr, "AppendUacInBufferAuto"},
- {11, nullptr, "GetAudioInBufferCount"},
- {12, nullptr, "SetDeviceGain"},
- {13, nullptr, "GetDeviceGain"},
- {14, nullptr, "FlushAudioInBuffers"},
- };
- // clang-format on
+class IAudioIn final : public ServiceFramework<IAudioIn> {
+public:
+ explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
+ std::string& device_name, const AudioInParameter& in_params, u32 handle,
+ u64 applet_resource_user_id)
+ : ServiceFramework{system_, "IAudioIn"},
+ service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")},
+ impl{std::make_shared<In>(system_, manager, event, session_id)} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IAudioIn::GetAudioInState, "GetAudioInState"},
+ {1, &IAudioIn::Start, "Start"},
+ {2, &IAudioIn::Stop, "Stop"},
+ {3, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBuffer"},
+ {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"},
+ {5, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffer"},
+ {6, &IAudioIn::ContainsAudioInBuffer, "ContainsAudioInBuffer"},
+ {7, &IAudioIn::AppendAudioInBuffer, "AppendUacInBuffer"},
+ {8, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBufferAuto"},
+ {9, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffersAuto"},
+ {10, &IAudioIn::AppendAudioInBuffer, "AppendUacInBufferAuto"},
+ {11, &IAudioIn::GetAudioInBufferCount, "GetAudioInBufferCount"},
+ {12, &IAudioIn::SetDeviceGain, "SetDeviceGain"},
+ {13, &IAudioIn::GetDeviceGain, "GetDeviceGain"},
+ {14, &IAudioIn::FlushAudioInBuffers, "FlushAudioInBuffers"},
+ };
+ // clang-format on
- RegisterHandlers(functions);
+ RegisterHandlers(functions);
- buffer_event = service_context.CreateEvent("IAudioIn:BufferEvent");
-}
+ if (impl->GetSystem()
+ .Initialize(device_name, in_params, handle, applet_resource_user_id)
+ .IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to initialize the AudioIn System!");
+ }
+ }
-IAudioIn::~IAudioIn() {
- service_context.CloseEvent(buffer_event);
-}
+ ~IAudioIn() override {
+ impl->Free();
+ service_context.CloseEvent(event);
+ }
-void IAudioIn::Start(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ [[nodiscard]] std::shared_ptr<In> GetImpl() {
+ return impl;
+ }
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
+private:
+ void GetAudioInState(Kernel::HLERequestContext& ctx) {
+ const auto state = static_cast<u32>(impl->GetState());
-void IAudioIn::RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "called. State={}", state);
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
-}
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(state);
+ }
-void IAudioIn::AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ void Start(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
+ auto result = impl->StartSystem();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
+ void Stop(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
+ auto result = impl->StopSystem();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
+ void AppendAudioInBuffer(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ u64 tag = rp.PopRaw<u64>();
-AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} {
+ const auto in_buffer_size{ctx.GetReadBufferSize()};
+ if (in_buffer_size < sizeof(AudioInBuffer)) {
+ LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioInBuffer!");
+ }
+
+ const auto& in_buffer = ctx.ReadBuffer();
+ AudioInBuffer buffer{};
+ std::memcpy(&buffer, in_buffer.data(), sizeof(AudioInBuffer));
+
+ [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
+ LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag);
+
+ auto result = impl->AppendBuffer(buffer, tag);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
+ void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
+ auto& buffer_event = impl->GetBufferEvent();
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(buffer_event);
+ }
+
+ void GetReleasedAudioInBuffer(Kernel::HLERequestContext& ctx) {
+ auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64);
+ std::vector<u64> released_buffers(write_buffer_size, 0);
+
+ auto count = impl->GetReleasedBuffers(released_buffers);
+
+ [[maybe_unused]] std::string tags{};
+ for (u32 i = 0; i < count; i++) {
+ tags += fmt::format("{:08X}, ", released_buffers[i]);
+ }
+ [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
+ LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
+ tags);
+
+ ctx.WriteBuffer(released_buffers);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(count);
+ }
+
+ void ContainsAudioInBuffer(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const u64 tag{rp.Pop<u64>()};
+ const auto buffer_queued{impl->ContainsAudioBuffer(tag)};
+
+ LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(buffer_queued);
+ }
+
+ void GetAudioInBufferCount(Kernel::HLERequestContext& ctx) {
+ const auto buffer_count = impl->GetBufferCount();
+
+ LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+
+ rb.Push(ResultSuccess);
+ rb.Push(buffer_count);
+ }
+
+ void SetDeviceGain(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const auto volume{rp.Pop<f32>()};
+ LOG_DEBUG(Service_Audio, "called. Gain {}", volume);
+
+ impl->SetVolume(volume);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void GetDeviceGain(Kernel::HLERequestContext& ctx) {
+ auto volume{impl->GetVolume()};
+
+ LOG_DEBUG(Service_Audio, "called. Gain {}", volume);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(volume);
+ }
+
+ void FlushAudioInBuffers(Kernel::HLERequestContext& ctx) {
+ bool flushed{impl->FlushAudioInBuffers()};
+
+ LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(flushed);
+ }
+
+ KernelHelpers::ServiceContext service_context;
+ Kernel::KEvent* event;
+ std::shared_ptr<AudioCore::AudioIn::In> impl;
+};
+
+AudInU::AudInU(Core::System& system_)
+ : ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew},
+ service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>(
+ system_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &AudInU::ListAudioIns, "ListAudioIns"},
@@ -80,59 +223,152 @@ AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} {
AudInU::~AudInU() = default;
void AudInU::ListAudioIns(Kernel::HLERequestContext& ctx) {
+ using namespace AudioCore::AudioRenderer;
+
LOG_DEBUG(Service_Audio, "called");
- const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioInDeviceName);
- const std::size_t device_count = std::min(count, audio_device_names.size());
- std::vector<AudioInDeviceName> device_names;
- device_names.reserve(device_count);
+ const auto write_count =
+ static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
+ std::vector<AudioDevice::AudioDeviceName> device_names{};
- for (std::size_t i = 0; i < device_count; i++) {
- const auto& device_name = audio_device_names[i];
- auto& entry = device_names.emplace_back();
- device_name.copy(entry.data(), device_name.size());
+ u32 out_count{0};
+ if (write_count > 0) {
+ out_count = impl->GetDeviceNames(device_names, write_count, false);
+ ctx.WriteBuffer(device_names);
}
- ctx.WriteBuffer(device_names);
-
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(device_names.size()));
+ rb.Push(out_count);
}
void AudInU::ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx) {
+ using namespace AudioCore::AudioRenderer;
+
LOG_DEBUG(Service_Audio, "called");
- constexpr u32 device_count = 0;
- // Since we don't actually use any other audio input devices, we return 0 devices. Filtered
- // device listing just omits the default input device
+ const auto write_count =
+ static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
+ std::vector<AudioDevice::AudioDeviceName> device_names{};
+
+ u32 out_count{0};
+ if (write_count > 0) {
+ out_count = impl->GetDeviceNames(device_names, write_count, true);
+ ctx.WriteBuffer(device_names);
+ }
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(device_count));
+ rb.Push(out_count);
}
-void AudInU::OpenInOutImpl(Kernel::HLERequestContext& ctx) {
- AudInOutParams params{};
- params.channel_count = 2;
- params.sample_format = SampleFormat::PCM16;
- params.sample_rate = 48000;
- params.state = State::Started;
+void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto in_params{rp.PopRaw<AudioInParameter>()};
+ auto applet_resource_user_id{rp.PopRaw<u64>()};
+ const auto device_name_data{ctx.ReadBuffer()};
+ auto device_name = Common::StringFromBuffer(device_name_data);
+ auto handle{ctx.GetCopyHandle(0)};
+
+ std::scoped_lock l{impl->mutex};
+ auto link{impl->LinkToManager()};
+ if (link.IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(link);
+ return;
+ }
+
+ size_t new_session_id{};
+ auto result{impl->AcquireSessionId(new_session_id)};
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
+ impl->num_free_sessions);
+
+ auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name,
+ in_params, handle, applet_resource_user_id);
+ impl->sessions[new_session_id] = audio_in->GetImpl();
+ impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
+
+ auto& out_system = impl->sessions[new_session_id]->GetSystem();
+ AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
+ .channel_count = out_system.GetChannelCount(),
+ .sample_format =
+ static_cast<u32>(out_system.GetSampleFormat()),
+ .state = static_cast<u32>(out_system.GetState())};
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushRaw<AudInOutParams>(params);
- rb.PushIpcInterface<IAudioIn>(system);
-}
-void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
- OpenInOutImpl(ctx);
+ std::string out_name{out_system.GetName()};
+ ctx.WriteBuffer(out_name);
+
+ rb.Push(ResultSuccess);
+ rb.PushRaw<AudioInParameterInternal>(out_params);
+ rb.PushIpcInterface<IAudioIn>(audio_in);
}
void AudInU::OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
- OpenInOutImpl(ctx);
+ IPC::RequestParser rp{ctx};
+ auto protocol_specified{rp.PopRaw<u64>()};
+ auto in_params{rp.PopRaw<AudioInParameter>()};
+ auto applet_resource_user_id{rp.PopRaw<u64>()};
+ const auto device_name_data{ctx.ReadBuffer()};
+ auto device_name = Common::StringFromBuffer(device_name_data);
+ auto handle{ctx.GetCopyHandle(0)};
+
+ std::scoped_lock l{impl->mutex};
+ auto link{impl->LinkToManager()};
+ if (link.IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(link);
+ return;
+ }
+
+ size_t new_session_id{};
+ auto result{impl->AcquireSessionId(new_session_id)};
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
+ impl->num_free_sessions);
+
+ auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name,
+ in_params, handle, applet_resource_user_id);
+ impl->sessions[new_session_id] = audio_in->GetImpl();
+ impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
+
+ auto& out_system = impl->sessions[new_session_id]->GetSystem();
+ AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
+ .channel_count = out_system.GetChannelCount(),
+ .sample_format =
+ static_cast<u32>(out_system.GetSampleFormat()),
+ .state = static_cast<u32>(out_system.GetState())};
+
+ IPC::ResponseBuilder rb{ctx, 6, 0, 1};
+
+ std::string out_name{out_system.GetName()};
+ if (protocol_specified == 0) {
+ if (out_system.IsUac()) {
+ out_name = "UacIn";
+ } else {
+ out_name = "DeviceIn";
+ }
+ }
+
+ ctx.WriteBuffer(out_name);
+
+ rb.Push(ResultSuccess);
+ rb.PushRaw<AudioInParameterInternal>(out_params);
+ rb.PushIpcInterface<IAudioIn>(audio_in);
}
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audin_u.h b/src/core/hle/service/audio/audin_u.h
index 2bfa38ecc..b45fda78a 100644
--- a/src/core/hle/service/audio/audin_u.h
+++ b/src/core/hle/service/audio/audin_u.h
@@ -3,6 +3,8 @@
#pragma once
+#include "audio_core/audio_in_manager.h"
+#include "audio_core/in/audio_in.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
@@ -14,22 +16,12 @@ namespace Kernel {
class HLERequestContext;
}
-namespace Service::Audio {
-
-class IAudioIn final : public ServiceFramework<IAudioIn> {
-public:
- explicit IAudioIn(Core::System& system_);
- ~IAudioIn() override;
-
-private:
- void Start(Kernel::HLERequestContext& ctx);
- void RegisterBufferEvent(Kernel::HLERequestContext& ctx);
- void AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx);
-
- KernelHelpers::ServiceContext service_context;
+namespace AudioCore::AudioOut {
+class Manager;
+class In;
+} // namespace AudioCore::AudioOut
- Kernel::KEvent* buffer_event;
-};
+namespace Service::Audio {
class AudInU final : public ServiceFramework<AudInU> {
public:
@@ -37,33 +29,14 @@ public:
~AudInU() override;
private:
- enum class SampleFormat : u32_le {
- PCM16 = 2,
- };
-
- enum class State : u32_le {
- Started = 0,
- Stopped = 1,
- };
-
- struct AudInOutParams {
- u32_le sample_rate{};
- u32_le channel_count{};
- SampleFormat sample_format{};
- State state{};
- };
- static_assert(sizeof(AudInOutParams) == 0x10, "AudInOutParams is an invalid size");
-
- using AudioInDeviceName = std::array<char, 256>;
- static constexpr std::array<std::string_view, 1> audio_device_names{{
- "BuiltInHeadset",
- }};
-
void ListAudioIns(Kernel::HLERequestContext& ctx);
void ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx);
void OpenInOutImpl(Kernel::HLERequestContext& ctx);
void OpenAudioIn(Kernel::HLERequestContext& ctx);
void OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx);
+
+ KernelHelpers::ServiceContext service_context;
+ std::unique_ptr<AudioCore::AudioIn::Manager> impl;
};
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index b0dad6053..a44dd842a 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -5,56 +5,43 @@
#include <cstring>
#include <vector>
-#include "audio_core/audio_out.h"
-#include "audio_core/codec.h"
+#include "audio_core/out/audio_out_system.h"
+#include "audio_core/renderer/audio_device.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
+#include "common/string_util.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/audio/audout_u.h"
#include "core/hle/service/audio/errors.h"
-#include "core/hle/service/kernel_helpers.h"
#include "core/memory.h"
namespace Service::Audio {
-
-constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}};
-constexpr int DefaultSampleRate{48000};
-
-struct AudoutParams {
- s32_le sample_rate;
- u16_le channel_count;
- INSERT_PADDING_BYTES_NOINIT(2);
-};
-static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size");
-
-enum class AudioState : u32 {
- Started,
- Stopped,
-};
+using namespace AudioCore::AudioOut;
class IAudioOut final : public ServiceFramework<IAudioOut> {
public:
- explicit IAudioOut(Core::System& system_, AudoutParams audio_params_,
- AudioCore::AudioOut& audio_core_, std::string&& device_name_,
- std::string&& unique_name)
+ explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager,
+ size_t session_id, std::string& device_name,
+ const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id)
: ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
- audio_core{audio_core_}, device_name{std::move(device_name_)},
- audio_params{audio_params_}, main_memory{system.Memory()}, service_context{system_,
- "IAudioOut"} {
+ service_context{system_, "IAudioOut"}, event{service_context.CreateEvent(
+ "AudioOutEvent")},
+ impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} {
+
// clang-format off
static const FunctionInfo functions[] = {
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
- {1, &IAudioOut::StartAudioOut, "Start"},
- {2, &IAudioOut::StopAudioOut, "Stop"},
- {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"},
+ {1, &IAudioOut::Start, "Start"},
+ {2, &IAudioOut::Stop, "Stop"},
+ {3, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBuffer"},
{4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
- {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffers"},
+ {5, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffers"},
{6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"},
- {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},
- {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
+ {7, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBufferAuto"},
+ {8, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffersAuto"},
{9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
{10, &IAudioOut::GetAudioOutPlayedSampleCount, "GetAudioOutPlayedSampleCount"},
{11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"},
@@ -64,241 +51,263 @@ public:
// clang-format on
RegisterHandlers(functions);
- // This is the event handle used to check if the audio buffer was released
- buffer_event = service_context.CreateEvent("IAudioOutBufferReleased");
-
- stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
- audio_params.channel_count, std::move(unique_name), [this] {
- const auto guard = LockService();
- buffer_event->GetWritableEvent().Signal();
- });
+ if (impl->GetSystem()
+ .Initialize(device_name, in_params, handle, applet_resource_user_id)
+ .IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!");
+ }
}
~IAudioOut() override {
- service_context.CloseEvent(buffer_event);
+ impl->Free();
+ service_context.CloseEvent(event);
}
-private:
- struct AudioBuffer {
- u64_le next;
- u64_le buffer;
- u64_le buffer_capacity;
- u64_le buffer_size;
- u64_le offset;
- };
- static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size");
+ [[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() {
+ return impl;
+ }
+private:
void GetAudioOutState(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto state = static_cast<u32>(impl->GetState());
+
+ LOG_DEBUG(Service_Audio, "called. State={}", state);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped));
+ rb.Push(state);
}
- void StartAudioOut(Kernel::HLERequestContext& ctx) {
+ void Start(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
- if (stream->IsPlaying()) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_OPERATION_FAILED);
- return;
- }
-
- audio_core.StartStream(stream);
+ auto result = impl->StartSystem();
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
- void StopAudioOut(Kernel::HLERequestContext& ctx) {
+ void Stop(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
- if (stream->IsPlaying()) {
- audio_core.StopStream(stream);
- }
+ auto result = impl->StopSystem();
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
- void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
- }
-
- void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description());
+ void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
+ u64 tag = rp.PopRaw<u64>();
- const auto& input_buffer{ctx.ReadBuffer()};
- ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer),
- "AudioBuffer input is an invalid size!");
- AudioBuffer audio_buffer{};
- std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
- const u64 tag{rp.Pop<u64>()};
+ const auto in_buffer_size{ctx.GetReadBufferSize()};
+ if (in_buffer_size < sizeof(AudioOutBuffer)) {
+ LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioOutBuffer!");
+ }
- std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16));
- main_memory.ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
+ const auto& in_buffer = ctx.ReadBuffer();
+ AudioOutBuffer buffer{};
+ std::memcpy(&buffer, in_buffer.data(), sizeof(AudioOutBuffer));
- if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_BUFFER_COUNT_EXCEEDED);
- return;
- }
+ [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
+ LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag);
+
+ auto result = impl->AppendBuffer(buffer, tag);
IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
+ void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
+ auto& buffer_event = impl->GetBufferEvent();
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
+ rb.PushCopyObjects(buffer_event);
}
- void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called {}", ctx.Description());
+ void GetReleasedAudioOutBuffers(Kernel::HLERequestContext& ctx) {
+ auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64);
+ std::vector<u64> released_buffers(write_buffer_size, 0);
- const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)};
- const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)};
+ auto count = impl->GetReleasedBuffers(released_buffers);
- std::vector<u64> tags{released_buffers};
- tags.resize(max_count);
- ctx.WriteBuffer(tags);
+ [[maybe_unused]] std::string tags{};
+ for (u32 i = 0; i < count; i++) {
+ tags += fmt::format("{:08X}, ", released_buffers[i]);
+ }
+ [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
+ LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
+ tags);
+ ctx.WriteBuffer(released_buffers);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(static_cast<u32>(released_buffers.size()));
+ rb.Push(count);
}
void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
IPC::RequestParser rp{ctx};
+
const u64 tag{rp.Pop<u64>()};
+ const auto buffer_queued{impl->ContainsAudioBuffer(tag)};
+
+ LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued);
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(stream->ContainsBuffer(tag));
+ rb.Push(buffer_queued);
}
void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto buffer_count = impl->GetBufferCount();
+
+ LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count);
IPC::ResponseBuilder rb{ctx, 3};
+
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(stream->GetQueueSize()));
+ rb.Push(buffer_count);
}
void GetAudioOutPlayedSampleCount(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto samples_played = impl->GetPlayedSampleCount();
+
+ LOG_DEBUG(Service_Audio, "called. Played samples={}", samples_played);
IPC::ResponseBuilder rb{ctx, 4};
+
rb.Push(ResultSuccess);
- rb.Push(stream->GetPlayedSampleCount());
+ rb.Push(samples_played);
}
void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ bool flushed{impl->FlushAudioOutBuffers()};
+
+ LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(stream->Flush());
+ rb.Push(flushed);
}
void SetAudioOutVolume(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const float volume = rp.Pop<float>();
- LOG_DEBUG(Service_Audio, "called, volume={}", volume);
+ const auto volume = rp.Pop<f32>();
+
+ LOG_DEBUG(Service_Audio, "called. Volume={}", volume);
- stream->SetVolume(volume);
+ impl->SetVolume(volume);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void GetAudioOutVolume(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto volume = impl->GetVolume();
+
+ LOG_DEBUG(Service_Audio, "called. Volume={}", volume);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(stream->GetVolume());
+ rb.Push(volume);
}
- AudioCore::AudioOut& audio_core;
- AudioCore::StreamPtr stream;
- std::string device_name;
-
- [[maybe_unused]] AudoutParams audio_params{};
-
- Core::Memory::Memory& main_memory;
-
KernelHelpers::ServiceContext service_context;
-
- /// This is the event handle used to check if the audio buffer was released
- Kernel::KEvent* buffer_event;
+ Kernel::KEvent* event;
+ std::shared_ptr<AudioCore::AudioOut::Out> impl;
};
-AudOutU::AudOutU(Core::System& system_) : ServiceFramework{system_, "audout:u"} {
+AudOutU::AudOutU(Core::System& system_)
+ : ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew},
+ service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>(
+ system_)} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"},
- {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"},
- {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"},
- {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"},
+ {0, &AudOutU::ListAudioOuts, "ListAudioOuts"},
+ {1, &AudOutU::OpenAudioOut, "OpenAudioOut"},
+ {2, &AudOutU::ListAudioOuts, "ListAudioOutsAuto"},
+ {3, &AudOutU::OpenAudioOut, "OpenAudioOutAuto"},
};
// clang-format on
RegisterHandlers(functions);
- audio_core = std::make_unique<AudioCore::AudioOut>();
}
AudOutU::~AudOutU() = default;
-void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
+ using namespace AudioCore::AudioRenderer;
- ctx.WriteBuffer(DefaultDevice);
+ std::scoped_lock l{impl->mutex};
+
+ const auto write_count =
+ static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
+ std::vector<AudioDevice::AudioDeviceName> device_names{};
+ std::string print_names{};
+ if (write_count > 0) {
+ device_names.push_back(AudioDevice::AudioDeviceName("DeviceOut"));
+ LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut");
+ } else {
+ LOG_DEBUG(Service_Audio, "called. Empty buffer passed in.");
+ }
+
+ ctx.WriteBuffer(device_names);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(1); // Amount of audio devices
+ rb.Push<u32>(static_cast<u32>(device_names.size()));
}
-void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
+void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto in_params{rp.PopRaw<AudioOutParameter>()};
+ auto applet_resource_user_id{rp.PopRaw<u64>()};
const auto device_name_data{ctx.ReadBuffer()};
- std::string device_name;
- if (device_name_data[0] != '\0') {
- device_name.assign(device_name_data.begin(), device_name_data.end());
- } else {
- device_name.assign(DefaultDevice.begin(), DefaultDevice.end());
- }
- ctx.WriteBuffer(device_name);
+ auto device_name = Common::StringFromBuffer(device_name_data);
+ auto handle{ctx.GetCopyHandle(0)};
- IPC::RequestParser rp{ctx};
- auto params{rp.PopRaw<AudoutParams>()};
- if (params.channel_count <= 2) {
- // Mono does not exist for audout
- params.channel_count = 2;
- } else {
- params.channel_count = 6;
+ auto link{impl->LinkToManager()};
+ if (link.IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(link);
+ return;
}
- if (!params.sample_rate) {
- params.sample_rate = DefaultSampleRate;
+
+ size_t new_session_id{};
+ auto result{impl->AcquireSessionId(new_session_id)};
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
}
- std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())};
- auto audio_out_interface = std::make_shared<IAudioOut>(
- system, params, *audio_core, std::move(device_name), std::move(unique_name));
+ LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
+ impl->num_free_sessions);
+
+ auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name,
+ in_params, handle, applet_resource_user_id);
+
+ impl->sessions[new_session_id] = audio_out->GetImpl();
+ impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
+
+ auto& out_system = impl->sessions[new_session_id]->GetSystem();
+ AudioOutParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
+ .channel_count = out_system.GetChannelCount(),
+ .sample_format =
+ static_cast<u32>(out_system.GetSampleFormat()),
+ .state = static_cast<u32>(out_system.GetState())};
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
- rb.Push(ResultSuccess);
- rb.Push<u32>(DefaultSampleRate);
- rb.Push<u32>(params.channel_count);
- rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
- rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
- rb.PushIpcInterface<IAudioOut>(audio_out_interface);
- audio_out_interfaces.push_back(std::move(audio_out_interface));
+ ctx.WriteBuffer(out_system.GetName());
+
+ rb.Push(ResultSuccess);
+ rb.PushRaw<AudioOutParameterInternal>(out_params);
+ rb.PushIpcInterface<IAudioOut>(audio_out);
}
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index d82004c2e..fdc0ee754 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -3,13 +3,11 @@
#pragma once
-#include <vector>
+#include "audio_core/audio_out_manager.h"
+#include "audio_core/out/audio_out.h"
+#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
-namespace AudioCore {
-class AudioOut;
-}
-
namespace Core {
class System;
}
@@ -18,6 +16,11 @@ namespace Kernel {
class HLERequestContext;
}
+namespace AudioCore::AudioOut {
+class Manager;
+class Out;
+} // namespace AudioCore::AudioOut
+
namespace Service::Audio {
class IAudioOut;
@@ -28,11 +31,11 @@ public:
~AudOutU() override;
private:
- void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
- void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
+ void ListAudioOuts(Kernel::HLERequestContext& ctx);
+ void OpenAudioOut(Kernel::HLERequestContext& ctx);
- std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces;
- std::unique_ptr<AudioCore::AudioOut> audio_core;
+ KernelHelpers::ServiceContext service_context;
+ std::unique_ptr<AudioCore::AudioOut::Manager> impl;
};
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 2ce63c004..bc69117c6 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -4,7 +4,12 @@
#include <array>
#include <memory>
-#include "audio_core/audio_renderer.h"
+#include "audio_core/audio_core.h"
+#include "audio_core/common/audio_renderer_parameter.h"
+#include "audio_core/common/feature_support.h"
+#include "audio_core/renderer/audio_device.h"
+#include "audio_core/renderer/audio_renderer.h"
+#include "audio_core/renderer/voice/voice_info.h"
#include "common/alignment.h"
#include "common/bit_util.h"
#include "common/common_funcs.h"
@@ -13,91 +18,127 @@
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/audio/audren_u.h"
#include "core/hle/service/audio/errors.h"
+#include "core/memory.h"
+
+using namespace AudioCore::AudioRenderer;
namespace Service::Audio {
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
public:
- explicit IAudioRenderer(Core::System& system_,
- const AudioCommon::AudioRendererParameter& audren_params,
- const std::size_t instance_number)
+ explicit IAudioRenderer(Core::System& system_, Manager& manager_,
+ AudioCore::AudioRendererParameterInternal& params,
+ Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
+ u32 process_handle, u64 applet_resource_user_id, s32 session_id)
: ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew},
- service_context{system_, "IAudioRenderer"} {
+ service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent(
+ "IAudioRendererEvent")},
+ manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
{1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
{2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
{3, &IAudioRenderer::GetState, "GetState"},
- {4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"},
+ {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"},
{5, &IAudioRenderer::Start, "Start"},
{6, &IAudioRenderer::Stop, "Stop"},
{7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
{8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
{9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
- {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"},
- {11, &IAudioRenderer::ExecuteAudioRendererRendering, "ExecuteAudioRendererRendering"},
+ {10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"},
+ {11, nullptr, "ExecuteAudioRendererRendering"},
};
// clang-format on
RegisterHandlers(functions);
- system_event = service_context.CreateEvent("IAudioRenderer:SystemEvent");
- renderer = std::make_unique<AudioCore::AudioRenderer>(
- system.CoreTiming(), system.Memory(), audren_params,
- [this]() {
- const auto guard = LockService();
- system_event->GetWritableEvent().Signal();
- },
- instance_number);
+ impl->Initialize(params, transfer_memory, transfer_memory_size, process_handle,
+ applet_resource_user_id, session_id);
}
~IAudioRenderer() override {
- service_context.CloseEvent(system_event);
+ impl->Finalize();
+ service_context.CloseEvent(rendered_event);
}
private:
void GetSampleRate(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto sample_rate{impl->GetSystem().GetSampleRate()};
+
+ LOG_DEBUG(Service_Audio, "called. Sample rate {}", sample_rate);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(renderer->GetSampleRate());
+ rb.Push(sample_rate);
}
void GetSampleCount(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto sample_count{impl->GetSystem().GetSampleCount()};
+
+ LOG_DEBUG(Service_Audio, "called. Sample count {}", sample_count);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(renderer->GetSampleCount());
+ rb.Push(sample_count);
}
void GetState(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const u32 state{!impl->GetSystem().IsActive()};
+
+ LOG_DEBUG(Service_Audio, "called, state {}", state);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(static_cast<u32>(renderer->GetStreamState()));
+ rb.Push(state);
}
void GetMixBufferCount(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
+ const auto buffer_count{impl->GetSystem().GetMixBufferCount()};
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(renderer->GetMixBufferCount());
+ rb.Push(buffer_count);
}
- void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "(STUBBED) called");
+ void RequestUpdate(Kernel::HLERequestContext& ctx) {
+ LOG_TRACE(Service_Audio, "called");
+
+ std::vector<u8> input{ctx.ReadBuffer(0)};
+
+ // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for
+ // checking size 0. Performance size is 0 for most games.
+
+ std::vector<u8> output{};
+ std::vector<u8> performance{};
+ auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0};
+ if (is_buffer_b) {
+ const auto buffersB{ctx.BufferDescriptorB()};
+ output.resize(buffersB[0].Size(), 0);
+ performance.resize(buffersB[1].Size(), 0);
+ } else {
+ const auto buffersC{ctx.BufferDescriptorC()};
+ output.resize(buffersC[0].Size(), 0);
+ performance.resize(buffersC[1].Size(), 0);
+ }
- std::vector<u8> output_params(ctx.GetWriteBufferSize(), 0);
- auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params);
+ auto result = impl->RequestUpdate(input, performance, output);
if (result.IsSuccess()) {
- ctx.WriteBuffer(output_params);
+ if (is_buffer_b) {
+ ctx.WriteBufferB(output.data(), output.size(), 0);
+ ctx.WriteBufferB(performance.data(), performance.size(), 1);
+ } else {
+ ctx.WriteBufferC(output.data(), output.size(), 0);
+ ctx.WriteBufferC(performance.data(), performance.size(), 1);
+ }
+ } else {
+ LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description);
}
IPC::ResponseBuilder rb{ctx, 2};
@@ -105,38 +146,45 @@ private:
}
void Start(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "called");
- const auto result = renderer->Start();
+ impl->Start();
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ rb.Push(ResultSuccess);
}
void Stop(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "called");
- const auto result = renderer->Stop();
+ impl->Stop();
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ rb.Push(ResultSuccess);
}
void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "called");
+
+ if (impl->GetSystem().GetExecutionMode() == AudioCore::ExecutionMode::Manual) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_NOT_SUPPORTED);
+ return;
+ }
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(system_event->GetReadableEvent());
+ rb.PushCopyObjects(rendered_event->GetReadableEvent());
}
void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
IPC::RequestParser rp{ctx};
- rendering_time_limit_percent = rp.Pop<u32>();
- LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}",
- rendering_time_limit_percent);
+ auto limit = rp.PopRaw<u32>();
- ASSERT(rendering_time_limit_percent <= 100);
+ auto& system_ = impl->GetSystem();
+ system_.SetRenderingTimeLimit(limit);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -145,34 +193,34 @@ private:
void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
+ auto& system_ = impl->GetSystem();
+ auto time = system_.GetRenderingTimeLimit();
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(rendering_time_limit_percent);
+ rb.Push(time);
}
void ExecuteAudioRendererRendering(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
-
- // This service command currently only reports an unsupported operation
- // error code, or aborts. Given that, we just always return an error
- // code in this case.
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_NOT_SUPPORTED);
}
KernelHelpers::ServiceContext service_context;
-
- Kernel::KEvent* system_event;
- std::unique_ptr<AudioCore::AudioRenderer> renderer;
- u32 rendering_time_limit_percent = 100;
+ Kernel::KEvent* rendered_event;
+ Manager& manager;
+ std::unique_ptr<Renderer> impl;
};
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
+
public:
- explicit IAudioDevice(Core::System& system_, Kernel::KEvent* buffer_event_, u32_le revision_)
- : ServiceFramework{system_, "IAudioDevice"}, buffer_event{buffer_event_}, revision{
- revision_} {
+ explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision,
+ u32 device_num)
+ : ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew},
+ service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>(
+ system_, applet_resource_user_id,
+ revision)},
+ event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} {
static const FunctionInfo functions[] = {
{0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
{1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
@@ -186,54 +234,45 @@ public:
{10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
{11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"},
{12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"},
- {13, nullptr, "GetActiveAudioOutputDeviceName"},
- {14, nullptr, "ListAudioOutputDeviceName"},
+ {13, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioOutputDeviceName"},
+ {14, &IAudioDevice::ListAudioOutputDeviceName, "ListAudioOutputDeviceName"},
};
RegisterHandlers(functions);
+
+ event->GetWritableEvent().Signal();
}
-private:
- using AudioDeviceName = std::array<char, 256>;
- static constexpr std::array<std::string_view, 4> audio_device_names{{
- "AudioStereoJackOutput",
- "AudioBuiltInSpeakerOutput",
- "AudioTvOutput",
- "AudioUsbDeviceOutput",
- }};
- enum class DeviceType {
- AHUBHeadphones,
- AHUBSpeakers,
- HDA,
- USBOutput,
- };
+ ~IAudioDevice() override {
+ service_context.CloseEvent(event);
+ }
+private:
void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName);
- const bool usb_output_supported =
- IsFeatureSupported(AudioFeatures::AudioUSBDeviceOutput, revision);
- const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioDeviceName);
+ std::vector<AudioDevice::AudioDeviceName> out_names{};
- std::vector<AudioDeviceName> name_buffer;
- name_buffer.reserve(audio_device_names.size());
+ u32 out_count = impl->ListAudioDeviceName(out_names, in_count);
- for (std::size_t i = 0; i < count && i < audio_device_names.size(); i++) {
- const auto type = static_cast<DeviceType>(i);
-
- if (!usb_output_supported && type == DeviceType::USBOutput) {
- continue;
+ std::string out{};
+ for (u32 i = 0; i < out_count; i++) {
+ std::string a{};
+ u32 j = 0;
+ while (out_names[i].name[j] != '\0') {
+ a += out_names[i].name[j];
+ j++;
}
-
- const auto& device_name = audio_device_names[i];
- auto& entry = name_buffer.emplace_back();
- device_name.copy(entry.data(), device_name.size());
+ out += "\n\t" + a;
}
- ctx.WriteBuffer(name_buffer);
+ LOG_DEBUG(Service_Audio, "called.\nNames={}", out);
IPC::ResponseBuilder rb{ctx, 3};
+
+ ctx.WriteBuffer(out_names);
+
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(name_buffer.size()));
+ rb.Push(out_count);
}
void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
@@ -243,7 +282,11 @@ private:
const auto device_name_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(device_name_buffer);
- LOG_WARNING(Service_Audio, "(STUBBED) called. name={}, volume={}", name, volume);
+ LOG_DEBUG(Service_Audio, "called. name={}, volume={}", name, volume);
+
+ if (name == "AudioTvOutput") {
+ impl->SetDeviceVolumes(volume);
+ }
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -253,53 +296,60 @@ private:
const auto device_name_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(device_name_buffer);
- LOG_WARNING(Service_Audio, "(STUBBED) called. name={}", name);
+ LOG_DEBUG(Service_Audio, "called. Name={}", name);
+
+ f32 volume{1.0f};
+ if (name == "AudioTvOutput") {
+ volume = impl->GetDeviceVolume(name);
+ }
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(1.0f);
+ rb.Push(volume);
}
void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ const auto write_size = ctx.GetWriteBufferSize() / sizeof(char);
+ std::string out_name{"AudioTvOutput"};
- // Currently set to always be TV audio output.
- const auto& device_name = audio_device_names[2];
+ LOG_DEBUG(Service_Audio, "(STUBBED) called. Name={}", out_name);
- AudioDeviceName out_device_name{};
- device_name.copy(out_device_name.data(), device_name.size());
+ out_name.resize(write_size);
- ctx.WriteBuffer(out_device_name);
+ ctx.WriteBuffer(out_name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "(STUBBED) called");
- buffer_event->GetWritableEvent().Signal();
+ event->GetWritableEvent().Signal();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
+ rb.PushCopyObjects(event->GetReadableEvent());
}
void GetActiveChannelCount(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ const auto& sink{system.AudioCore().GetOutputSink()};
+ u32 channel_count{sink.GetDeviceChannels()};
+
+ LOG_DEBUG(Service_Audio, "(STUBBED) called. Channels={}", channel_count);
IPC::ResponseBuilder rb{ctx, 3};
+
rb.Push(ResultSuccess);
- rb.Push<u32>(2);
+ rb.Push<u32>(channel_count);
}
- // Should be similar to QueryAudioDeviceOutputEvent
void QueryAudioDeviceInputEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
+ rb.PushCopyObjects(event->GetReadableEvent());
}
void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) {
@@ -307,402 +357,167 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
+ rb.PushCopyObjects(event->GetReadableEvent());
}
- Kernel::KEvent* buffer_event;
- u32_le revision = 0;
+ void ListAudioOutputDeviceName(Kernel::HLERequestContext& ctx) {
+ const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName);
+
+ std::vector<AudioDevice::AudioDeviceName> out_names{};
+
+ u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count);
+
+ std::string out{};
+ for (u32 i = 0; i < out_count; i++) {
+ std::string a{};
+ u32 j = 0;
+ while (out_names[i].name[j] != '\0') {
+ a += out_names[i].name[j];
+ j++;
+ }
+ out += "\n\t" + a;
+ }
+
+ LOG_DEBUG(Service_Audio, "called.\nNames={}", out);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+
+ ctx.WriteBuffer(out_names);
+
+ rb.Push(ResultSuccess);
+ rb.Push(out_count);
+ }
+
+ KernelHelpers::ServiceContext service_context;
+ std::unique_ptr<AudioDevice> impl;
+ Kernel::KEvent* event;
};
AudRenU::AudRenU(Core::System& system_)
- : ServiceFramework{system_, "audren:u"}, service_context{system_, "audren:u"} {
+ : ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew},
+ service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
- {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetWorkBufferSize"},
+ {1, &AudRenU::GetWorkBufferSize, "GetWorkBufferSize"},
{2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"},
- {3, &AudRenU::OpenAudioRendererForManualExecution, "OpenAudioRendererForManualExecution"},
+ {3, nullptr, "OpenAudioRendererForManualExecution"},
{4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"},
};
// clang-format on
RegisterHandlers(functions);
-
- buffer_event = service_context.CreateEvent("IAudioOutBufferReleasedEvent");
}
-AudRenU::~AudRenU() {
- service_context.CloseEvent(buffer_event);
-}
+AudRenU::~AudRenU() = default;
void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
- OpenAudioRendererImpl(ctx);
-}
-
-static u64 CalculateNumPerformanceEntries(const AudioCommon::AudioRendererParameter& params) {
- // +1 represents the final mix.
- return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count +
- 1;
-}
-
-void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
- // Several calculations below align the sizes being calculated
- // onto a 64 byte boundary.
- static constexpr u64 buffer_alignment_size = 64;
-
- // Some calculations that calculate portions of the buffer
- // that will contain information, on the other hand, align
- // the result of some of their calcularions on a 16 byte boundary.
- static constexpr u64 info_field_alignment_size = 16;
-
- // Maximum detail entries that may exist at one time for performance
- // frame statistics.
- static constexpr u64 max_perf_detail_entries = 100;
-
- // Size of the data structure representing the bulk of the voice-related state.
- static constexpr u64 voice_state_size_bytes = 0x100;
-
- // Size of the upsampler manager data structure
- constexpr u64 upsampler_manager_size = 0x48;
-
- // Calculates the part of the size that relates to mix buffers.
- const auto calculate_mix_buffer_sizes = [](const AudioCommon::AudioRendererParameter& params) {
- // As of 8.0.0 this is the maximum on voice channels.
- constexpr u64 max_voice_channels = 6;
-
- // The service expects the sample_count member of the parameters to either be
- // a value of 160 or 240, so the maximum sample count is assumed in order
- // to adequately handle all values at runtime.
- constexpr u64 default_max_sample_count = 240;
-
- const u64 total_mix_buffers = params.mix_buffer_count + max_voice_channels;
-
- u64 size = 0;
- size += total_mix_buffers * (sizeof(s32) * params.sample_count);
- size += total_mix_buffers * (sizeof(s32) * default_max_sample_count);
- size += u64{params.submix_count} + params.sink_count;
- size = Common::AlignUp(size, buffer_alignment_size);
- size += Common::AlignUp(params.unknown_30, buffer_alignment_size);
- size += Common::AlignUp(sizeof(s32) * params.mix_buffer_count, buffer_alignment_size);
- return size;
- };
-
- // Calculates the portion of the size related to the mix data (and the sorting thereof).
- const auto calculate_mix_info_size = [](const AudioCommon::AudioRendererParameter& params) {
- // The size of the mixing info data structure.
- constexpr u64 mix_info_size = 0x940;
-
- // Consists of total submixes with the final mix included.
- const u64 total_mix_count = u64{params.submix_count} + 1;
-
- // The total number of effects that may be available to the audio renderer at any time.
- constexpr u64 max_effects = 256;
-
- // Calculates the part of the size related to the audio node state.
- // This will only be used if the audio revision supports the splitter.
- const auto calculate_node_state_size = [](std::size_t num_nodes) {
- // Internally within a nodestate, it appears to use a data structure
- // similar to a std::bitset<64> twice.
- constexpr u64 bit_size = Common::BitSize<u64>();
- constexpr u64 num_bitsets = 2;
-
- // Node state instances have three states internally for performing
- // depth-first searches of nodes. Initialized, Found, and Done Sorting.
- constexpr u64 num_states = 3;
-
- u64 size = 0;
- size += (num_nodes * num_nodes) * sizeof(s32);
- size += num_states * (num_nodes * sizeof(s32));
- size += num_bitsets * (Common::AlignUp(num_nodes, bit_size) / Common::BitSize<u8>());
- return size;
- };
-
- // Calculates the part of the size related to the adjacency (aka edge) matrix.
- const auto calculate_edge_matrix_size = [](std::size_t num_nodes) {
- return (num_nodes * num_nodes) * sizeof(s32);
- };
-
- u64 size = 0;
- size += Common::AlignUp(sizeof(void*) * total_mix_count, info_field_alignment_size);
- size += Common::AlignUp(mix_info_size * total_mix_count, info_field_alignment_size);
- size += Common::AlignUp(sizeof(s32) * max_effects * params.submix_count,
- info_field_alignment_size);
-
- if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
- size += Common::AlignUp(calculate_node_state_size(total_mix_count) +
- calculate_edge_matrix_size(total_mix_count),
- info_field_alignment_size);
- }
-
- return size;
- };
-
- // Calculates the part of the size related to voice channel info.
- const auto calculate_voice_info_size = [](const AudioCommon::AudioRendererParameter& params) {
- constexpr u64 voice_info_size = 0x220;
- constexpr u64 voice_resource_size = 0xD0;
-
- u64 size = 0;
- size += Common::AlignUp(sizeof(void*) * params.voice_count, info_field_alignment_size);
- size += Common::AlignUp(voice_info_size * params.voice_count, info_field_alignment_size);
- size +=
- Common::AlignUp(voice_resource_size * params.voice_count, info_field_alignment_size);
- size +=
- Common::AlignUp(voice_state_size_bytes * params.voice_count, info_field_alignment_size);
- return size;
- };
-
- // Calculates the part of the size related to memory pools.
- const auto calculate_memory_pools_size = [](const AudioCommon::AudioRendererParameter& params) {
- const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count);
- const u64 memory_pool_info_size = 0x20;
- return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size);
- };
-
- // Calculates the part of the size related to the splitter context.
- const auto calculate_splitter_context_size =
- [](const AudioCommon::AudioRendererParameter& params) -> u64 {
- if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
- return 0;
- }
-
- constexpr u64 splitter_info_size = 0x20;
- constexpr u64 splitter_destination_data_size = 0xE0;
-
- u64 size = 0;
- size += params.num_splitter_send_channels;
- size +=
- Common::AlignUp(splitter_info_size * params.splitter_count, info_field_alignment_size);
- size += Common::AlignUp(splitter_destination_data_size * params.num_splitter_send_channels,
- info_field_alignment_size);
-
- return size;
- };
-
- // Calculates the part of the size related to the upsampler info.
- const auto calculate_upsampler_info_size =
- [](const AudioCommon::AudioRendererParameter& params) {
- constexpr u64 upsampler_info_size = 0x280;
- // Yes, using the buffer size over info alignment size is intentional here.
- return Common::AlignUp(upsampler_info_size *
- (u64{params.submix_count} + params.sink_count),
- buffer_alignment_size);
- };
-
- // Calculates the part of the size related to effect info.
- const auto calculate_effect_info_size = [](const AudioCommon::AudioRendererParameter& params) {
- constexpr u64 effect_info_size = 0x2B0;
- return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size);
- };
-
- // Calculates the part of the size related to audio sink info.
- const auto calculate_sink_info_size = [](const AudioCommon::AudioRendererParameter& params) {
- const u64 sink_info_size = 0x170;
- return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size);
- };
-
- // Calculates the part of the size related to voice state info.
- const auto calculate_voice_state_size = [](const AudioCommon::AudioRendererParameter& params) {
- const u64 voice_state_size = 0x100;
- const u64 additional_size = buffer_alignment_size - 1;
- return Common::AlignUp(voice_state_size * params.voice_count + additional_size,
- info_field_alignment_size);
- };
-
- // Calculates the part of the size related to performance statistics.
- const auto calculate_perf_size = [](const AudioCommon::AudioRendererParameter& params) {
- // Extra size value appended to the end of the calculation.
- constexpr u64 appended = 128;
-
- // Whether or not we assume the newer version of performance metrics data structures.
- const bool is_v2 =
- IsFeatureSupported(AudioFeatures::PerformanceMetricsVersion2, params.revision);
-
- // Data structure sizes
- constexpr u64 perf_statistics_size = 0x0C;
- const u64 header_size = is_v2 ? 0x30 : 0x18;
- const u64 entry_size = is_v2 ? 0x18 : 0x10;
- const u64 detail_size = is_v2 ? 0x18 : 0x10;
-
- const u64 entry_count = CalculateNumPerformanceEntries(params);
- const u64 size_per_frame =
- header_size + (entry_size * entry_count) + (detail_size * max_perf_detail_entries);
-
- u64 size = 0;
- size += Common::AlignUp(size_per_frame * params.performance_frame_count + 1,
- buffer_alignment_size);
- size += Common::AlignUp(perf_statistics_size, buffer_alignment_size);
- size += appended;
- return size;
- };
-
- // Calculates the part of the size that relates to the audio command buffer.
- const auto calculate_command_buffer_size =
- [](const AudioCommon::AudioRendererParameter& params) {
- constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
-
- if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
- constexpr u64 command_buffer_size = 0x18000;
-
- return command_buffer_size + alignment;
- }
-
- // When the variadic command buffer is supported, this means
- // the command generator for the audio renderer can issue commands
- // that are (as one would expect), variable in size. So what we need to do
- // is determine the maximum possible size for a few command data structures
- // then multiply them by the amount of present commands indicated by the given
- // respective audio parameters.
-
- constexpr u64 max_biquad_filters = 2;
- constexpr u64 max_mix_buffers = 24;
-
- constexpr u64 biquad_filter_command_size = 0x2C;
-
- constexpr u64 depop_mix_command_size = 0x24;
- constexpr u64 depop_setup_command_size = 0x50;
-
- constexpr u64 effect_command_max_size = 0x540;
-
- constexpr u64 mix_command_size = 0x1C;
- constexpr u64 mix_ramp_command_size = 0x24;
- constexpr u64 mix_ramp_grouped_command_size = 0x13C;
-
- constexpr u64 perf_command_size = 0x28;
-
- constexpr u64 sink_command_size = 0x130;
-
- constexpr u64 submix_command_max_size =
- depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
-
- constexpr u64 volume_command_size = 0x1C;
- constexpr u64 volume_ramp_command_size = 0x20;
-
- constexpr u64 voice_biquad_filter_command_size =
- biquad_filter_command_size * max_biquad_filters;
- constexpr u64 voice_data_command_size = 0x9C;
- const u64 voice_command_max_size =
- (params.splitter_count * depop_setup_command_size) +
- (voice_data_command_size + voice_biquad_filter_command_size +
- volume_ramp_command_size + mix_ramp_grouped_command_size);
+ IPC::RequestParser rp{ctx};
- // Now calculate the individual elements that comprise the size and add them together.
- const u64 effect_commands_size = params.effect_count * effect_command_max_size;
+ AudioCore::AudioRendererParameterInternal params;
+ rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params);
+ auto transfer_memory_handle = ctx.GetCopyHandle(0);
+ auto process_handle = ctx.GetCopyHandle(1);
+ auto transfer_memory_size = rp.Pop<u64>();
+ auto applet_resource_user_id = rp.Pop<u64>();
- const u64 final_mix_commands_size =
- depop_mix_command_size + volume_command_size * max_mix_buffers;
+ if (impl->GetSessionCount() + 1 > AudioCore::MaxRendererSessions) {
+ LOG_ERROR(Service_Audio, "Too many AudioRenderer sessions open!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_MAXIMUM_SESSIONS_REACHED);
+ return;
+ }
- const u64 perf_commands_size =
- perf_command_size *
- (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
+ const auto& handle_table{system.CurrentProcess()->GetHandleTable()};
+ auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)};
+ auto transfer_memory{
+ process->GetHandleTable().GetObject<Kernel::KTransferMemory>(transfer_memory_handle)};
- const u64 sink_commands_size = params.sink_count * sink_command_size;
+ const auto session_id{impl->GetSessionId()};
+ if (session_id == -1) {
+ LOG_ERROR(Service_Audio, "Tried to open a session that's already in use!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_MAXIMUM_SESSIONS_REACHED);
+ return;
+ }
- const u64 splitter_commands_size =
- params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
+ LOG_DEBUG(Service_Audio, "Opened new AudioRenderer session {} sessions open {}", session_id,
+ impl->GetSessionCount());
- const u64 submix_commands_size = params.submix_count * submix_command_max_size;
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IAudioRenderer>(system, *impl, params, transfer_memory.GetPointerUnsafe(),
+ transfer_memory_size, process_handle,
+ applet_resource_user_id, session_id);
+}
- const u64 voice_commands_size = params.voice_count * voice_command_max_size;
-
- return effect_commands_size + final_mix_commands_size + perf_commands_size +
- sink_commands_size + splitter_commands_size + submix_commands_size +
- voice_commands_size + alignment;
- };
+void AudRenU::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
+ AudioCore::AudioRendererParameterInternal params;
IPC::RequestParser rp{ctx};
- const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
-
- u64 size = 0;
- size += calculate_mix_buffer_sizes(params);
- size += calculate_mix_info_size(params);
- size += calculate_voice_info_size(params);
- size += upsampler_manager_size;
- size += calculate_memory_pools_size(params);
- size += calculate_splitter_context_size(params);
-
- size = Common::AlignUp(size, buffer_alignment_size);
-
- size += calculate_upsampler_info_size(params);
- size += calculate_effect_info_size(params);
- size += calculate_sink_info_size(params);
- size += calculate_voice_state_size(params);
- size += calculate_perf_size(params);
- size += calculate_command_buffer_size(params);
-
- // finally, 4KB page align the size, and we're done.
- size = Common::AlignUp(size, 4096);
+ rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params);
+
+ u64 size{0};
+ auto result = impl->GetWorkBufferSize(params, size);
+
+ std::string output_info{};
+ output_info += fmt::format("\tRevision {}", AudioCore::GetRevisionNum(params.revision));
+ output_info +=
+ fmt::format("\n\tSample Rate {}, Sample Count {}", params.sample_rate, params.sample_count);
+ output_info += fmt::format("\n\tExecution Mode {}, Voice Drop Enabled {}",
+ static_cast<u32>(params.execution_mode), params.voice_drop_enabled);
+ output_info += fmt::format(
+ "\n\tSizes: Effects {:04X}, Mixes {:04X}, Sinks {:04X}, Submixes {:04X}, Splitter Infos "
+ "{:04X}, Splitter Destinations {:04X}, Voices {:04X}, Performance Frames {:04X} External "
+ "Context {:04X}",
+ params.effects, params.mixes, params.sinks, params.sub_mixes, params.splitter_infos,
+ params.splitter_destinations, params.voices, params.perf_frames,
+ params.external_context_size);
+
+ LOG_DEBUG(Service_Audio, "called.\nInput params:\n{}\nOutput params:\n\tWorkbuffer size {:08X}",
+ output_info, size);
IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
+ rb.Push(result);
rb.Push<u64>(size);
-
- LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", size);
}
void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const u64 aruid = rp.Pop<u64>();
- LOG_DEBUG(Service_Audio, "called. aruid={:016X}", aruid);
+ const auto applet_resource_user_id = rp.Pop<u64>();
+
+ LOG_DEBUG(Service_Audio, "called. Applet resource id {}", applet_resource_user_id);
- // Revisionless variant of GetAudioDeviceServiceWithRevisionInfo that
- // always assumes the initial release revision (REV1).
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IAudioDevice>(system, buffer_event, Common::MakeMagic('R', 'E', 'V', '1'));
+ rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id,
+ ::Common::MakeMagic('R', 'E', 'V', '1'), num_audio_devices++);
}
void AudRenU::OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
-
- OpenAudioRendererImpl(ctx);
}
void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) {
struct Parameters {
u32 revision;
- u64 aruid;
+ u64 applet_resource_user_id;
};
IPC::RequestParser rp{ctx};
- const auto [revision, aruid] = rp.PopRaw<Parameters>();
- LOG_DEBUG(Service_Audio, "called. revision={:08X}, aruid={:016X}", revision, aruid);
+ const auto [revision, applet_resource_user_id] = rp.PopRaw<Parameters>();
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IAudioDevice>(system, buffer_event, revision);
-}
+ LOG_DEBUG(Service_Audio, "called. Revision {} Applet resource id {}",
+ AudioCore::GetRevisionNum(revision), applet_resource_user_id);
-void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IAudioRenderer>(system, params, audren_instance_count++);
-}
-
-bool IsFeatureSupported(AudioFeatures feature, u32_le revision) {
- // Byte swap
- const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0');
-
- switch (feature) {
- case AudioFeatures::AudioUSBDeviceOutput:
- return version_num >= 4U;
- case AudioFeatures::Splitter:
- return version_num >= 2U;
- case AudioFeatures::PerformanceMetricsVersion2:
- case AudioFeatures::VariadicCommandBuffer:
- return version_num >= 5U;
- default:
- return false;
- }
+ rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id, revision,
+ num_audio_devices++);
}
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index 869d39002..4384a9b3c 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -3,6 +3,7 @@
#pragma once
+#include "audio_core/audio_render_manager.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
@@ -15,6 +16,7 @@ class HLERequestContext;
}
namespace Service::Audio {
+class IAudioRenderer;
class AudRenU final : public ServiceFramework<AudRenU> {
public:
@@ -23,28 +25,14 @@ public:
private:
void OpenAudioRenderer(Kernel::HLERequestContext& ctx);
- void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
+ void GetWorkBufferSize(Kernel::HLERequestContext& ctx);
void GetAudioDeviceService(Kernel::HLERequestContext& ctx);
void OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx);
void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx);
- void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx);
-
KernelHelpers::ServiceContext service_context;
-
- std::size_t audren_instance_count = 0;
- Kernel::KEvent* buffer_event;
+ std::unique_ptr<AudioCore::AudioRenderer::Manager> impl;
+ u32 num_audio_devices{0};
};
-// Describes a particular audio feature that may be supported in a particular revision.
-enum class AudioFeatures : u32 {
- AudioUSBDeviceOutput,
- Splitter,
- PerformanceMetricsVersion2,
- VariadicCommandBuffer,
-};
-
-// Tests if a particular audio feature is supported with a given audio revision.
-bool IsFeatureSupported(AudioFeatures feature, u32_le revision);
-
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h
index 542fec899..d706978cb 100644
--- a/src/core/hle/service/audio/errors.h
+++ b/src/core/hle/service/audio/errors.h
@@ -7,8 +7,17 @@
namespace Service::Audio {
-constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::Audio, 2};
-constexpr ResultCode ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8};
-constexpr ResultCode ERR_NOT_SUPPORTED{ErrorModule::Audio, 513};
+constexpr Result ERR_INVALID_DEVICE_NAME{ErrorModule::Audio, 1};
+constexpr Result ERR_OPERATION_FAILED{ErrorModule::Audio, 2};
+constexpr Result ERR_INVALID_SAMPLE_RATE{ErrorModule::Audio, 3};
+constexpr Result ERR_INSUFFICIENT_BUFFER_SIZE{ErrorModule::Audio, 4};
+constexpr Result ERR_MAXIMUM_SESSIONS_REACHED{ErrorModule::Audio, 5};
+constexpr Result ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8};
+constexpr Result ERR_INVALID_CHANNEL_COUNT{ErrorModule::Audio, 10};
+constexpr Result ERR_INVALID_UPDATE_DATA{ErrorModule::Audio, 41};
+constexpr Result ERR_POOL_MAPPING_FAILED{ErrorModule::Audio, 42};
+constexpr Result ERR_NOT_SUPPORTED{ErrorModule::Audio, 513};
+constexpr Result ERR_INVALID_PROCESS_HANDLE{ErrorModule::Audio, 1536};
+constexpr Result ERR_INVALID_REVISION{ErrorModule::Audio, 1537};
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 75da659e5..4f2ed2d52 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -298,7 +298,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx) {
const auto sample_rate = rp.Pop<u32>();
const auto channel_count = rp.Pop<u32>();
- LOG_CRITICAL(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count);
+ LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count);
ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
sample_rate == 12000 || sample_rate == 8000,
diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp
index 7e6d16230..cd0b405ff 100644
--- a/src/core/hle/service/bcat/backend/backend.cpp
+++ b/src/core/hle/service/bcat/backend/backend.cpp
@@ -74,7 +74,7 @@ void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) {
SignalUpdate();
}
-void ProgressServiceBackend::FinishDownload(ResultCode result) {
+void ProgressServiceBackend::FinishDownload(Result result) {
impl.total_downloaded_bytes = impl.total_bytes;
impl.status = DeliveryCacheProgressImpl::Status::Done;
impl.result = result;
diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h
index 7e8026c75..205ed0702 100644
--- a/src/core/hle/service/bcat/backend/backend.h
+++ b/src/core/hle/service/bcat/backend/backend.h
@@ -49,7 +49,7 @@ struct DeliveryCacheProgressImpl {
};
Status status;
- ResultCode result = ResultSuccess;
+ Result result = ResultSuccess;
DirectoryName current_directory;
FileName current_file;
s64 current_downloaded_bytes; ///< Bytes downloaded on current file.
@@ -90,7 +90,7 @@ public:
void CommitDirectory(std::string_view dir_name);
// Notifies the application that the operation completed with result code result.
- void FinishDownload(ResultCode result);
+ void FinishDownload(Result result);
private:
explicit ProgressServiceBackend(Core::System& system, std::string_view event_name);
diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp
index 076fd79e7..bc08ac487 100644
--- a/src/core/hle/service/bcat/bcat_module.cpp
+++ b/src/core/hle/service/bcat/bcat_module.cpp
@@ -18,15 +18,15 @@
namespace Service::BCAT {
-constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1};
-constexpr ResultCode ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2};
-constexpr ResultCode ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6};
-constexpr ResultCode ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7};
+constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1};
+constexpr Result ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2};
+constexpr Result ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6};
+constexpr Result ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7};
// The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the files
// and if any of them have a non-zero result it just forwards that result. This is the FS error code
// for permission denied, which is the closest approximation of this scenario.
-constexpr ResultCode ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400};
+constexpr Result ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400};
using BCATDigest = std::array<u8, 0x10>;
@@ -140,8 +140,8 @@ public:
{20401, nullptr, "UnregisterSystemApplicationDeliveryTask"},
{20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"},
{30100, &IBcatService::SetPassphrase, "SetPassphrase"},
- {30101, nullptr, "Unknown"},
- {30102, nullptr, "Unknown2"},
+ {30101, nullptr, "Unknown30101"},
+ {30102, nullptr, "Unknown30102"},
{30200, nullptr, "RegisterBackgroundDeliveryTask"},
{30201, nullptr, "UnregisterBackgroundDeliveryTask"},
{30202, nullptr, "BlockDeliveryTask"},
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index f9ee2b624..ec7e5320c 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -181,6 +181,11 @@ public:
{147, nullptr, "RegisterAudioControlNotification"},
{148, nullptr, "SendAudioControlPassthroughCommand"},
{149, nullptr, "SendAudioControlSetAbsoluteVolumeCommand"},
+ {150, nullptr, "AcquireAudioSinkVolumeLocallyChangedEvent"},
+ {151, nullptr, "AcquireAudioSinkVolumeUpdateRequestCompletedEvent"},
+ {152, nullptr, "GetAudioSinkVolume"},
+ {153, nullptr, "RequestUpdateAudioSinkVolume"},
+ {154, nullptr, "IsAudioSinkVolumeSupported"},
{256, nullptr, "IsManufacturingMode"},
{257, nullptr, "EmulateBluetoothCrash"},
{258, nullptr, "GetBleChannelMap"},
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index 3fa88cbd3..eebf85e03 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -214,8 +214,12 @@ public:
{76, nullptr, "Unknown76"},
{100, nullptr, "Unknown100"},
{101, nullptr, "Unknown101"},
- {110, nullptr, "Unknown102"},
- {111, nullptr, "Unknown103"},
+ {110, nullptr, "Unknown110"},
+ {111, nullptr, "Unknown111"},
+ {112, nullptr, "Unknown112"},
+ {113, nullptr, "Unknown113"},
+ {114, nullptr, "Unknown114"},
+ {115, nullptr, "Unknown115"},
};
// clang-format on
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index cbe9d5f7c..ff9b0427c 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -8,8 +8,8 @@
namespace Service::ES {
-constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::ETicket, 2};
-constexpr ResultCode ERROR_INVALID_RIGHTS_ID{ErrorModule::ETicket, 3};
+constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::ETicket, 2};
+constexpr Result ERROR_INVALID_RIGHTS_ID{ErrorModule::ETicket, 3};
class ETicket final : public ServiceFramework<ETicket> {
public:
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index a99c90479..27675615b 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -62,8 +62,7 @@ enum class FatalType : u32 {
ErrorScreen = 2,
};
-static void GenerateErrorReport(Core::System& system, ResultCode error_code,
- const FatalInfo& info) {
+static void GenerateErrorReport(Core::System& system, Result error_code, const FatalInfo& info) {
const auto title_id = system.GetCurrentProcessProgramID();
std::string crash_report = fmt::format(
"Yuzu {}-{} crash report\n"
@@ -106,7 +105,7 @@ static void GenerateErrorReport(Core::System& system, ResultCode error_code,
info.backtrace_size, info.ArchAsString(), info.unk10);
}
-static void ThrowFatalError(Core::System& system, ResultCode error_code, FatalType fatal_type,
+static void ThrowFatalError(Core::System& system, Result error_code, FatalType fatal_type,
const FatalInfo& info) {
LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}", fatal_type,
error_code.raw);
@@ -129,7 +128,7 @@ static void ThrowFatalError(Core::System& system, ResultCode error_code, FatalTy
void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp{ctx};
- const auto error_code = rp.Pop<ResultCode>();
+ const auto error_code = rp.Pop<Result>();
ThrowFatalError(system, error_code, FatalType::ErrorScreen, {});
IPC::ResponseBuilder rb{ctx, 2};
@@ -139,7 +138,7 @@ void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp(ctx);
- const auto error_code = rp.Pop<ResultCode>();
+ const auto error_code = rp.Pop<Result>();
const auto fatal_type = rp.PopEnum<FatalType>();
ThrowFatalError(system, error_code, fatal_type,
@@ -151,7 +150,7 @@ void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp(ctx);
- const auto error_code = rp.Pop<ResultCode>();
+ const auto error_code = rp.Pop<Result>();
const auto fatal_type = rp.PopEnum<FatalType>();
const auto fatal_info = ctx.ReadBuffer();
FatalInfo info{};
diff --git a/src/core/hle/service/fatal/fatal_p.cpp b/src/core/hle/service/fatal/fatal_p.cpp
index 7d35b4208..4a81bb5e2 100644
--- a/src/core/hle/service/fatal/fatal_p.cpp
+++ b/src/core/hle/service/fatal/fatal_p.cpp
@@ -6,7 +6,13 @@
namespace Service::Fatal {
Fatal_P::Fatal_P(std::shared_ptr<Module> module_, Core::System& system_)
- : Interface(std::move(module_), system_, "fatal:p") {}
+ : Interface(std::move(module_), system_, "fatal:p") {
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetFatalEvent"},
+ {10, nullptr, "GetFatalContext"},
+ };
+ RegisterHandlers(functions);
+}
Fatal_P::~Fatal_P() = default;
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index f8e7519ca..11c604a0f 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -49,7 +49,7 @@ std::string VfsDirectoryServiceWrapper::GetName() const {
return backing->GetName();
}
-ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
+Result VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr) {
@@ -73,7 +73,7 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
return ResultSuccess;
}
-ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
+Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
if (path.empty()) {
// TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
@@ -92,7 +92,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons
return ResultSuccess;
}
-ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
+Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
// NOTE: This is inaccurate behavior. CreateDirectory is not recursive.
@@ -116,7 +116,7 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_)
return ResultSuccess;
}
-ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
+Result VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) {
@@ -126,7 +126,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_)
return ResultSuccess;
}
-ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
+Result VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) {
@@ -136,7 +136,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str
return ResultSuccess;
}
-ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
+Result VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
const std::string sanitized_path(Common::FS::SanitizePath(path));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path));
@@ -148,8 +148,8 @@ ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::stri
return ResultSuccess;
}
-ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
- const std::string& dest_path_) const {
+Result VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
+ const std::string& dest_path_) const {
std::string src_path(Common::FS::SanitizePath(src_path_));
std::string dest_path(Common::FS::SanitizePath(dest_path_));
auto src = backing->GetFileRelative(src_path);
@@ -183,8 +183,8 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
return ResultSuccess;
}
-ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
- const std::string& dest_path_) const {
+Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
+ const std::string& dest_path_) const {
std::string src_path(Common::FS::SanitizePath(src_path_));
std::string dest_path(Common::FS::SanitizePath(dest_path_));
auto src = GetDirectoryRelativeWrapped(backing, src_path);
@@ -273,28 +273,27 @@ FileSystemController::FileSystemController(Core::System& system_) : system{syste
FileSystemController::~FileSystemController() = default;
-ResultCode FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
+Result FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
romfs_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered RomFS");
return ResultSuccess;
}
-ResultCode FileSystemController::RegisterSaveData(
- std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
+Result FileSystemController::RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data");
save_data_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered save data");
return ResultSuccess;
}
-ResultCode FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
+Result FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC");
sdmc_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered SDMC");
return ResultSuccess;
}
-ResultCode FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
+Result FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS");
bis_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered BIS");
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 8dd2652fe..5b27de9fa 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -58,10 +58,10 @@ public:
explicit FileSystemController(Core::System& system_);
~FileSystemController();
- ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
- ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
- ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
- ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
+ Result RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
+ Result RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
+ Result RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
+ Result RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
void SetPackedUpdate(FileSys::VirtualFile update_raw);
ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() const;
@@ -141,7 +141,7 @@ private:
void InstallInterfaces(Core::System& system);
-// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
+// A class that wraps a VfsDirectory with methods that return ResultVal and Result instead of
// pointers and booleans. This makes using a VfsDirectory with switch services much easier and
// avoids repetitive code.
class VfsDirectoryServiceWrapper {
@@ -160,35 +160,35 @@ public:
* @param size The size of the new file, filled with zeroes
* @return Result of the operation
*/
- ResultCode CreateFile(const std::string& path, u64 size) const;
+ Result CreateFile(const std::string& path, u64 size) const;
/**
* Delete a file specified by its path
* @param path Path relative to the archive
* @return Result of the operation
*/
- ResultCode DeleteFile(const std::string& path) const;
+ Result DeleteFile(const std::string& path) const;
/**
* Create a directory specified by its path
* @param path Path relative to the archive
* @return Result of the operation
*/
- ResultCode CreateDirectory(const std::string& path) const;
+ Result CreateDirectory(const std::string& path) const;
/**
* Delete a directory specified by its path
* @param path Path relative to the archive
* @return Result of the operation
*/
- ResultCode DeleteDirectory(const std::string& path) const;
+ Result DeleteDirectory(const std::string& path) const;
/**
* Delete a directory specified by its path and anything under it
* @param path Path relative to the archive
* @return Result of the operation
*/
- ResultCode DeleteDirectoryRecursively(const std::string& path) const;
+ Result DeleteDirectoryRecursively(const std::string& path) const;
/**
* Cleans the specified directory. This is similar to DeleteDirectoryRecursively,
@@ -200,7 +200,7 @@ public:
*
* @return Result of the operation.
*/
- ResultCode CleanDirectoryRecursively(const std::string& path) const;
+ Result CleanDirectoryRecursively(const std::string& path) const;
/**
* Rename a File specified by its path
@@ -208,7 +208,7 @@ public:
* @param dest_path Destination path relative to the archive
* @return Result of the operation
*/
- ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const;
+ Result RenameFile(const std::string& src_path, const std::string& dest_path) const;
/**
* Rename a Directory specified by its path
@@ -216,7 +216,7 @@ public:
* @param dest_path Destination path relative to the archive
* @return Result of the operation
*/
- ResultCode RenameDirectory(const std::string& src_path, const std::string& dest_path) const;
+ Result RenameDirectory(const std::string& src_path, const std::string& dest_path) const;
/**
* Open a file specified by its path, using the specified mode
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index fae6e5aff..e23eae36a 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -246,7 +246,8 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec
entries.reserve(entries.size() + new_data.size());
for (const auto& new_entry : new_data) {
- entries.emplace_back(new_entry->GetName(), type, new_entry->GetSize());
+ entries.emplace_back(new_entry->GetName(), type,
+ type == FileSys::EntryType::Directory ? 0 : new_entry->GetSize());
}
}
diff --git a/src/core/hle/service/friend/errors.h b/src/core/hle/service/friend/errors.h
index bc9fe0aca..ff525d865 100644
--- a/src/core/hle/service/friend/errors.h
+++ b/src/core/hle/service/friend/errors.h
@@ -7,5 +7,5 @@
namespace Service::Friend {
-constexpr ResultCode ERR_NO_NOTIFICATIONS{ErrorModule::Account, 15};
+constexpr Result ERR_NO_NOTIFICATIONS{ErrorModule::Account, 15};
}
diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp
index fec7787ab..49b6d45fe 100644
--- a/src/core/hle/service/glue/arp.cpp
+++ b/src/core/hle/service/glue/arp.cpp
@@ -153,7 +153,7 @@ class IRegistrar final : public ServiceFramework<IRegistrar> {
friend class ARP_W;
public:
- using IssuerFn = std::function<ResultCode(u64, ApplicationLaunchProperty, std::vector<u8>)>;
+ using IssuerFn = std::function<Result(u64, ApplicationLaunchProperty, std::vector<u8>)>;
explicit IRegistrar(Core::System& system_, IssuerFn&& issuer)
: ServiceFramework{system_, "IRegistrar"}, issue_process_id{std::move(issuer)} {
diff --git a/src/core/hle/service/glue/errors.h b/src/core/hle/service/glue/errors.h
index aefbe1f3e..d4ce7f44e 100644
--- a/src/core/hle/service/glue/errors.h
+++ b/src/core/hle/service/glue/errors.h
@@ -7,9 +7,9 @@
namespace Service::Glue {
-constexpr ResultCode ERR_INVALID_RESOURCE{ErrorModule::ARP, 30};
-constexpr ResultCode ERR_INVALID_PROCESS_ID{ErrorModule::ARP, 31};
-constexpr ResultCode ERR_INVALID_ACCESS{ErrorModule::ARP, 42};
-constexpr ResultCode ERR_NOT_REGISTERED{ErrorModule::ARP, 102};
+constexpr Result ERR_INVALID_RESOURCE{ErrorModule::ARP, 30};
+constexpr Result ERR_INVALID_PROCESS_ID{ErrorModule::ARP, 31};
+constexpr Result ERR_INVALID_ACCESS{ErrorModule::ARP, 42};
+constexpr Result ERR_NOT_REGISTERED{ErrorModule::ARP, 102};
} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/glue_manager.cpp b/src/core/hle/service/glue/glue_manager.cpp
index f1655b33e..8a654cdca 100644
--- a/src/core/hle/service/glue/glue_manager.cpp
+++ b/src/core/hle/service/glue/glue_manager.cpp
@@ -41,8 +41,8 @@ ResultVal<std::vector<u8>> ARPManager::GetControlProperty(u64 title_id) const {
return iter->second.control;
}
-ResultCode ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch,
- std::vector<u8> control) {
+Result ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch,
+ std::vector<u8> control) {
if (title_id == 0) {
return ERR_INVALID_PROCESS_ID;
}
@@ -56,7 +56,7 @@ ResultCode ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch,
return ResultSuccess;
}
-ResultCode ARPManager::Unregister(u64 title_id) {
+Result ARPManager::Unregister(u64 title_id) {
if (title_id == 0) {
return ERR_INVALID_PROCESS_ID;
}
diff --git a/src/core/hle/service/glue/glue_manager.h b/src/core/hle/service/glue/glue_manager.h
index 6246fd2ff..cd0b092ac 100644
--- a/src/core/hle/service/glue/glue_manager.h
+++ b/src/core/hle/service/glue/glue_manager.h
@@ -42,12 +42,12 @@ public:
// Adds a new entry to the internal database with the provided parameters, returning
// ERR_INVALID_ACCESS if attempting to re-register a title ID without an intermediate Unregister
// step, and ERR_INVALID_PROCESS_ID if the title ID is 0.
- ResultCode Register(u64 title_id, ApplicationLaunchProperty launch, std::vector<u8> control);
+ Result Register(u64 title_id, ApplicationLaunchProperty launch, std::vector<u8> control);
// Removes the registration for the provided title ID from the database, returning
// ERR_NOT_REGISTERED if it doesn't exist in the database and ERR_INVALID_PROCESS_ID if the
// title ID is 0.
- ResultCode Unregister(u64 title_id);
+ Result Unregister(u64 title_id);
// Removes all entries from the database, always succeeds. Should only be used when resetting
// system state.
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index ac5c38cc6..cb29004e8 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -49,28 +49,41 @@ bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) {
}
}
-bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
+Result Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
- return npad_id && npad_type && device_index;
+
+ if (!npad_type) {
+ return VibrationInvalidStyleIndex;
+ }
+ if (!npad_id) {
+ return VibrationInvalidNpadId;
+ }
+ if (!device_index) {
+ return VibrationDeviceIndexOutOfRange;
+ }
+
+ return ResultSuccess;
}
-ResultCode Controller_NPad::VerifyValidSixAxisSensorHandle(
+Result Controller_NPad::VerifyValidSixAxisSensorHandle(
const Core::HID::SixAxisSensorHandle& device_handle) {
const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
+ const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
+ const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
+
if (!npad_id) {
return InvalidNpadId;
}
- const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
if (!device_index) {
return NpadDeviceIndexOutOfRange;
}
// This doesn't get validated on nnsdk
- const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
if (!npad_type) {
return NpadInvalidHandle;
}
+
return ResultSuccess;
}
@@ -150,28 +163,51 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
}
LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
const auto controller_type = controller.device->GetNpadStyleIndex();
+ const auto& body_colors = controller.device->GetColors();
+ const auto& battery_level = controller.device->GetBattery();
auto* shared_memory = controller.shared_memory;
if (controller_type == Core::HID::NpadStyleIndex::None) {
controller.styleset_changed_event->GetWritableEvent().Signal();
return;
}
+
+ // Reset memory values
shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None;
shared_memory->device_type.raw = 0;
shared_memory->system_properties.raw = 0;
+ shared_memory->joycon_color.attribute = ColorAttribute::NoController;
+ shared_memory->joycon_color.attribute = ColorAttribute::NoController;
+ shared_memory->fullkey_color = {};
+ shared_memory->joycon_color.left = {};
+ shared_memory->joycon_color.right = {};
+ shared_memory->battery_level_dual = {};
+ shared_memory->battery_level_left = {};
+ shared_memory->battery_level_right = {};
+
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
ASSERT(false);
break;
case Core::HID::NpadStyleIndex::ProController:
+ shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
+ shared_memory->fullkey_color.fullkey = body_colors.fullkey;
+ shared_memory->battery_level_dual = battery_level.dual.battery_level;
shared_memory->style_tag.fullkey.Assign(1);
shared_memory->device_type.fullkey.Assign(1);
shared_memory->system_properties.is_vertical.Assign(1);
shared_memory->system_properties.use_plus.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
+ shared_memory->system_properties.is_charging_joy_dual.Assign(
+ battery_level.dual.is_charging);
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController;
shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::Handheld:
+ shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
+ shared_memory->joycon_color.attribute = ColorAttribute::Ok;
+ shared_memory->fullkey_color.fullkey = body_colors.fullkey;
+ shared_memory->joycon_color.left = body_colors.left;
+ shared_memory->joycon_color.right = body_colors.right;
shared_memory->style_tag.handheld.Assign(1);
shared_memory->device_type.handheld_left.Assign(1);
shared_memory->device_type.handheld_right.Assign(1);
@@ -179,47 +215,86 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory->system_properties.use_plus.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
shared_memory->system_properties.use_directional_buttons.Assign(1);
+ shared_memory->system_properties.is_charging_joy_dual.Assign(
+ battery_level.left.is_charging);
+ shared_memory->system_properties.is_charging_joy_left.Assign(
+ battery_level.left.is_charging);
+ shared_memory->system_properties.is_charging_joy_right.Assign(
+ battery_level.right.is_charging);
shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual;
shared_memory->applet_nfc_xcd.applet_footer.type =
AppletFooterUiType::HandheldJoyConLeftJoyConRight;
shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::JoyconDual:
+ shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
+ shared_memory->joycon_color.attribute = ColorAttribute::Ok;
shared_memory->style_tag.joycon_dual.Assign(1);
if (controller.is_dual_left_connected) {
+ shared_memory->joycon_color.left = body_colors.left;
+ shared_memory->battery_level_left = battery_level.left.battery_level;
shared_memory->device_type.joycon_left.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
+ shared_memory->system_properties.is_charging_joy_left.Assign(
+ battery_level.left.is_charging);
shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1);
}
if (controller.is_dual_right_connected) {
+ shared_memory->joycon_color.right = body_colors.right;
+ shared_memory->battery_level_right = battery_level.right.battery_level;
shared_memory->device_type.joycon_right.Assign(1);
shared_memory->system_properties.use_plus.Assign(1);
+ shared_memory->system_properties.is_charging_joy_right.Assign(
+ battery_level.right.is_charging);
shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1);
}
shared_memory->system_properties.use_directional_buttons.Assign(1);
shared_memory->system_properties.is_vertical.Assign(1);
shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual;
+
if (controller.is_dual_left_connected && controller.is_dual_right_connected) {
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDual;
+ shared_memory->fullkey_color.fullkey = body_colors.left;
+ shared_memory->battery_level_dual = battery_level.left.battery_level;
+ shared_memory->system_properties.is_charging_joy_dual.Assign(
+ battery_level.left.is_charging);
} else if (controller.is_dual_left_connected) {
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly;
+ shared_memory->fullkey_color.fullkey = body_colors.left;
+ shared_memory->battery_level_dual = battery_level.left.battery_level;
+ shared_memory->system_properties.is_charging_joy_dual.Assign(
+ battery_level.left.is_charging);
} else {
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualRightOnly;
+ shared_memory->fullkey_color.fullkey = body_colors.right;
+ shared_memory->battery_level_dual = battery_level.right.battery_level;
+ shared_memory->system_properties.is_charging_joy_dual.Assign(
+ battery_level.right.is_charging);
}
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
+ shared_memory->joycon_color.attribute = ColorAttribute::Ok;
+ shared_memory->joycon_color.left = body_colors.left;
+ shared_memory->battery_level_dual = battery_level.left.battery_level;
shared_memory->style_tag.joycon_left.Assign(1);
shared_memory->device_type.joycon_left.Assign(1);
shared_memory->system_properties.is_horizontal.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
+ shared_memory->system_properties.is_charging_joy_left.Assign(
+ battery_level.left.is_charging);
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
+ shared_memory->joycon_color.attribute = ColorAttribute::Ok;
+ shared_memory->joycon_color.right = body_colors.right;
+ shared_memory->battery_level_right = battery_level.right.battery_level;
shared_memory->style_tag.joycon_right.Assign(1);
shared_memory->device_type.joycon_right.Assign(1);
shared_memory->system_properties.is_horizontal.Assign(1);
shared_memory->system_properties.use_plus.Assign(1);
+ shared_memory->system_properties.is_charging_joy_right.Assign(
+ battery_level.right.is_charging);
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1);
break;
@@ -256,21 +331,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
break;
}
- const auto& body_colors = controller.device->GetColors();
-
- shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
- shared_memory->fullkey_color.fullkey = body_colors.fullkey;
-
- shared_memory->joycon_color.attribute = ColorAttribute::Ok;
- shared_memory->joycon_color.left = body_colors.left;
- shared_memory->joycon_color.right = body_colors.right;
-
- // TODO: Investigate when we should report all batery types
- const auto& battery_level = controller.device->GetBattery();
- shared_memory->battery_level_dual = battery_level.dual.battery_level;
- shared_memory->battery_level_left = battery_level.left.battery_level;
- shared_memory->battery_level_right = battery_level.right.battery_level;
-
controller.is_connected = true;
controller.device->Connect();
SignalStyleSetChangedEvent(npad_id);
@@ -705,6 +765,11 @@ Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const {
}
void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
+ if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) {
+ ASSERT_MSG(false, "Activation mode should be always None, Single or Dual");
+ return;
+ }
+
handheld_activation_mode = activation_mode;
}
@@ -720,9 +785,9 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
return communication_mode;
}
-ResultCode Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
- NpadJoyDeviceType npad_device_type,
- NpadJoyAssignmentMode assignment_mode) {
+Result Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
+ NpadJoyDeviceType npad_device_type,
+ NpadJoyAssignmentMode assignment_mode) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
return InvalidNpadId;
@@ -820,11 +885,11 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
const auto now = steady_clock::now();
- // Filter out non-zero vibrations that are within 10ms of each other.
+ // Filter out non-zero vibrations that are within 15ms of each other.
if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) &&
duration_cast<milliseconds>(
now - controller.vibration[device_index].last_vibration_timepoint) <
- milliseconds(10)) {
+ milliseconds(15)) {
return false;
}
@@ -840,7 +905,7 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
void Controller_NPad::VibrateController(
const Core::HID::VibrationDeviceHandle& vibration_device_handle,
const Core::HID::VibrationValue& vibration_value) {
- if (!IsDeviceHandleValid(vibration_device_handle)) {
+ if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
return;
}
@@ -903,7 +968,7 @@ void Controller_NPad::VibrateControllers(
Core::HID::VibrationValue Controller_NPad::GetLastVibration(
const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
- if (!IsDeviceHandleValid(vibration_device_handle)) {
+ if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
return {};
}
@@ -914,7 +979,7 @@ Core::HID::VibrationValue Controller_NPad::GetLastVibration(
void Controller_NPad::InitializeVibrationDevice(
const Core::HID::VibrationDeviceHandle& vibration_device_handle) {
- if (!IsDeviceHandleValid(vibration_device_handle)) {
+ if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
return;
}
@@ -941,7 +1006,7 @@ void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
bool Controller_NPad::IsVibrationDeviceMounted(
const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
- if (!IsDeviceHandleValid(vibration_device_handle)) {
+ if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
return false;
}
@@ -984,7 +1049,7 @@ void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
InitNewlyAddedController(npad_id);
}
-ResultCode Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
+Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
return InvalidNpadId;
@@ -1032,7 +1097,7 @@ ResultCode Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
WriteEmptyEntry(shared_memory);
return ResultSuccess;
}
-ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(
+Result Controller_NPad::SetGyroscopeZeroDriftMode(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, GyroscopeZeroDriftMode drift_mode) {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
@@ -1046,7 +1111,7 @@ ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(
return ResultSuccess;
}
-ResultCode Controller_NPad::GetGyroscopeZeroDriftMode(
+Result Controller_NPad::GetGyroscopeZeroDriftMode(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
GyroscopeZeroDriftMode& drift_mode) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
@@ -1061,8 +1126,8 @@ ResultCode Controller_NPad::GetGyroscopeZeroDriftMode(
return ResultSuccess;
}
-ResultCode Controller_NPad::IsSixAxisSensorAtRest(
- const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_at_rest) const {
+Result Controller_NPad::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ bool& is_at_rest) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
@@ -1074,7 +1139,7 @@ ResultCode Controller_NPad::IsSixAxisSensorAtRest(
return ResultSuccess;
}
-ResultCode Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
+Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
@@ -1087,7 +1152,7 @@ ResultCode Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
return ResultSuccess;
}
-ResultCode Controller_NPad::EnableSixAxisSensorUnalteredPassthrough(
+Result Controller_NPad::EnableSixAxisSensorUnalteredPassthrough(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
@@ -1100,7 +1165,7 @@ ResultCode Controller_NPad::EnableSixAxisSensorUnalteredPassthrough(
return ResultSuccess;
}
-ResultCode Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled(
+Result Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
@@ -1113,7 +1178,7 @@ ResultCode Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled(
return ResultSuccess;
}
-ResultCode Controller_NPad::LoadSixAxisSensorCalibrationParameter(
+Result Controller_NPad::LoadSixAxisSensorCalibrationParameter(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
@@ -1128,7 +1193,7 @@ ResultCode Controller_NPad::LoadSixAxisSensorCalibrationParameter(
return ResultSuccess;
}
-ResultCode Controller_NPad::GetSixAxisSensorIcInformation(
+Result Controller_NPad::GetSixAxisSensorIcInformation(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorIcInformation& ic_information) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
@@ -1143,7 +1208,7 @@ ResultCode Controller_NPad::GetSixAxisSensorIcInformation(
return ResultSuccess;
}
-ResultCode Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
+Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
@@ -1157,8 +1222,8 @@ ResultCode Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
return ResultSuccess;
}
-ResultCode Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
- bool sixaxis_status) {
+Result Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ bool sixaxis_status) {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
@@ -1170,7 +1235,7 @@ ResultCode Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHand
return ResultSuccess;
}
-ResultCode Controller_NPad::IsSixAxisSensorFusionEnabled(
+Result Controller_NPad::IsSixAxisSensorFusionEnabled(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
@@ -1183,7 +1248,7 @@ ResultCode Controller_NPad::IsSixAxisSensorFusionEnabled(
return ResultSuccess;
}
-ResultCode Controller_NPad::SetSixAxisFusionEnabled(
+Result Controller_NPad::SetSixAxisFusionEnabled(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
@@ -1197,7 +1262,7 @@ ResultCode Controller_NPad::SetSixAxisFusionEnabled(
return ResultSuccess;
}
-ResultCode Controller_NPad::SetSixAxisFusionParameters(
+Result Controller_NPad::SetSixAxisFusionParameters(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
@@ -1217,7 +1282,7 @@ ResultCode Controller_NPad::SetSixAxisFusionParameters(
return ResultSuccess;
}
-ResultCode Controller_NPad::GetSixAxisFusionParameters(
+Result Controller_NPad::GetSixAxisFusionParameters(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters& parameters) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
@@ -1232,8 +1297,8 @@ ResultCode Controller_NPad::GetSixAxisFusionParameters(
return ResultSuccess;
}
-ResultCode Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
- Core::HID::NpadIdType npad_id_2) {
+Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
+ Core::HID::NpadIdType npad_id_2) {
if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
npad_id_2);
@@ -1304,8 +1369,8 @@ void Controller_NPad::StopLRAssignmentMode() {
is_in_lr_assignment_mode = false;
}
-ResultCode Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
- Core::HID::NpadIdType npad_id_2) {
+Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
+ Core::HID::NpadIdType npad_id_2) {
if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
npad_id_2);
@@ -1336,8 +1401,8 @@ ResultCode Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
return ResultSuccess;
}
-ResultCode Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id,
- Core::HID::LedPattern& pattern) const {
+Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id,
+ Core::HID::LedPattern& pattern) const {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
return InvalidNpadId;
@@ -1347,8 +1412,8 @@ ResultCode Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id,
return ResultSuccess;
}
-ResultCode Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(
- Core::HID::NpadIdType npad_id, bool& is_valid) const {
+Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
+ bool& is_valid) const {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
return InvalidNpadId;
@@ -1358,7 +1423,7 @@ ResultCode Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(
return ResultSuccess;
}
-ResultCode Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(
+Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(
bool is_protection_enabled, Core::HID::NpadIdType npad_id) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 0b662b7f8..1a589cca2 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -29,7 +29,7 @@ namespace Service::KernelHelpers {
class ServiceContext;
} // namespace Service::KernelHelpers
-union ResultCode;
+union Result;
namespace Service::HID {
@@ -81,6 +81,7 @@ public:
Dual = 0,
Single = 1,
None = 2,
+ MaxActivationMode = 3,
};
// This is nn::hid::NpadCommunicationMode
@@ -107,8 +108,8 @@ public:
void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
NpadCommunicationMode GetNpadCommunicationMode() const;
- ResultCode SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
- NpadJoyAssignmentMode assignment_mode);
+ Result SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
+ NpadJoyAssignmentMode assignment_mode);
bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
const Core::HID::VibrationValue& vibration_value);
@@ -141,64 +142,63 @@ public:
void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id,
bool connected);
- ResultCode DisconnectNpad(Core::HID::NpadIdType npad_id);
+ Result DisconnectNpad(Core::HID::NpadIdType npad_id);
- ResultCode SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
- GyroscopeZeroDriftMode drift_mode);
- ResultCode GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
- GyroscopeZeroDriftMode& drift_mode) const;
- ResultCode IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
- bool& is_at_rest) const;
- ResultCode IsFirmwareUpdateAvailableForSixAxisSensor(
+ Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ GyroscopeZeroDriftMode drift_mode);
+ Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ GyroscopeZeroDriftMode& drift_mode) const;
+ Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ bool& is_at_rest) const;
+ Result IsFirmwareUpdateAvailableForSixAxisSensor(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const;
- ResultCode EnableSixAxisSensorUnalteredPassthrough(
+ Result EnableSixAxisSensorUnalteredPassthrough(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
- ResultCode IsSixAxisSensorUnalteredPassthroughEnabled(
+ Result IsSixAxisSensorUnalteredPassthroughEnabled(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
- ResultCode LoadSixAxisSensorCalibrationParameter(
+ Result LoadSixAxisSensorCalibrationParameter(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
- ResultCode GetSixAxisSensorIcInformation(
+ Result GetSixAxisSensorIcInformation(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorIcInformation& ic_information) const;
- ResultCode ResetIsSixAxisSensorDeviceNewlyAssigned(
+ Result ResetIsSixAxisSensorDeviceNewlyAssigned(
const Core::HID::SixAxisSensorHandle& sixaxis_handle);
- ResultCode SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
- bool sixaxis_status);
- ResultCode IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
- bool& is_fusion_enabled) const;
- ResultCode SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
- bool is_fusion_enabled);
- ResultCode SetSixAxisFusionParameters(
+ Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ bool sixaxis_status);
+ Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ bool& is_fusion_enabled) const;
+ Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ bool is_fusion_enabled);
+ Result SetSixAxisFusionParameters(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
- ResultCode GetSixAxisFusionParameters(
- const Core::HID::SixAxisSensorHandle& sixaxis_handle,
- Core::HID::SixAxisSensorFusionParameters& parameters) const;
- ResultCode GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
- ResultCode IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
- bool& is_enabled) const;
- ResultCode SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
- Core::HID::NpadIdType npad_id);
+ Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ Core::HID::SixAxisSensorFusionParameters& parameters) const;
+ Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
+ Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
+ bool& is_enabled) const;
+ Result SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
+ Core::HID::NpadIdType npad_id);
void SetAnalogStickUseCenterClamp(bool use_center_clamp);
void ClearAllConnectedControllers();
void DisconnectAllConnectedControllers();
void ConnectAllDisconnectedControllers();
void ClearAllControllers();
- ResultCode MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
- Core::HID::NpadIdType npad_id_2);
+ Result MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
+ Core::HID::NpadIdType npad_id_2);
void StartLRAssignmentMode();
void StopLRAssignmentMode();
- ResultCode SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
+ Result SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
// Logical OR for all buttons presses on all controllers
// Specifically for cheat engine and other features.
Core::HID::NpadButton GetAndResetPressState();
static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
- static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
- static ResultCode VerifyValidSixAxisSensorHandle(
+ static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
+ static Result VerifyValidSixAxisSensorHandle(
const Core::HID::SixAxisSensorHandle& device_handle);
private:
diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h
index 6c8ad04af..4613a4e60 100644
--- a/src/core/hle/service/hid/errors.h
+++ b/src/core/hle/service/hid/errors.h
@@ -7,12 +7,22 @@
namespace Service::HID {
-constexpr ResultCode NpadInvalidHandle{ErrorModule::HID, 100};
-constexpr ResultCode NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
-constexpr ResultCode InvalidSixAxisFusionRange{ErrorModule::HID, 423};
-constexpr ResultCode NpadIsDualJoycon{ErrorModule::HID, 601};
-constexpr ResultCode NpadIsSameType{ErrorModule::HID, 602};
-constexpr ResultCode InvalidNpadId{ErrorModule::HID, 709};
-constexpr ResultCode NpadNotConnected{ErrorModule::HID, 710};
+constexpr Result NpadInvalidHandle{ErrorModule::HID, 100};
+constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
+constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122};
+constexpr Result VibrationInvalidNpadId{ErrorModule::HID, 123};
+constexpr Result VibrationDeviceIndexOutOfRange{ErrorModule::HID, 124};
+constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423};
+constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601};
+constexpr Result NpadIsSameType{ErrorModule::HID, 602};
+constexpr Result InvalidNpadId{ErrorModule::HID, 709};
+constexpr Result NpadNotConnected{ErrorModule::HID, 710};
} // namespace Service::HID
+
+namespace Service::IRS {
+
+constexpr Result InvalidProcessorState{ErrorModule::Irsensor, 78};
+constexpr Result InvalidIrCameraHandle{ErrorModule::Irsensor, 204};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index dc5d0366d..7909141c0 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -74,26 +74,34 @@ IAppletResource::IAppletResource(Core::System& system_,
// Register update callbacks
pad_update_event = Core::Timing::CreateEvent(
"HID::UpdatePadCallback",
- [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ [this](std::uintptr_t user_data, s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
UpdateControllers(user_data, ns_late);
+ return std::nullopt;
});
mouse_keyboard_update_event = Core::Timing::CreateEvent(
"HID::UpdateMouseKeyboardCallback",
- [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ [this](std::uintptr_t user_data, s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
UpdateMouseKeyboard(user_data, ns_late);
+ return std::nullopt;
});
motion_update_event = Core::Timing::CreateEvent(
"HID::UpdateMotionCallback",
- [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ [this](std::uintptr_t user_data, s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
UpdateMotion(user_data, ns_late);
+ return std::nullopt;
});
- system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event);
- system.CoreTiming().ScheduleEvent(mouse_keyboard_update_ns, mouse_keyboard_update_event);
- system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event);
+ system.CoreTiming().ScheduleLoopingEvent(pad_update_ns, pad_update_ns, pad_update_event);
+ system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
+ mouse_keyboard_update_event);
+ system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
+ motion_update_event);
system.HIDCore().ReloadInputDevices();
}
@@ -135,13 +143,6 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
}
controller->OnUpdate(core_timing);
}
-
- // If ns_late is higher than the update rate ignore the delay
- if (ns_late > pad_update_ns) {
- ns_late = {};
- }
-
- core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event);
}
void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
@@ -150,26 +151,12 @@ void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing);
controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing);
-
- // If ns_late is higher than the update rate ignore the delay
- if (ns_late > mouse_keyboard_update_ns) {
- ns_late = {};
- }
-
- core_timing.ScheduleEvent(mouse_keyboard_update_ns - ns_late, mouse_keyboard_update_event);
}
void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing);
-
- // If ns_late is higher than the update rate ignore the delay
- if (ns_late > motion_update_ns) {
- ns_late = {};
- }
-
- core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event);
}
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
@@ -778,7 +765,7 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
bool is_at_rest{};
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
- const auto result = controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest);
+ controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -786,7 +773,7 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(result);
+ rb.Push(ResultSuccess);
rb.Push(is_at_rest);
}
@@ -803,8 +790,8 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
bool is_firmware_available{};
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
- const auto result = controller.IsFirmwareUpdateAvailableForSixAxisSensor(
- parameters.sixaxis_handle, is_firmware_available);
+ controller.IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle,
+ is_firmware_available);
LOG_WARNING(
Service_HID,
@@ -813,7 +800,7 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(result);
+ rb.Push(ResultSuccess);
rb.Push(is_firmware_available);
}
@@ -1083,13 +1070,13 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
- const auto result = controller.DisconnectNpad(parameters.npad_id);
+ controller.DisconnectNpad(parameters.npad_id);
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ rb.Push(ResultSuccess);
}
void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
@@ -1165,15 +1152,14 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
const auto parameters{rp.PopRaw<Parameters>()};
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
- const auto result =
- controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
- Controller_NPad::NpadJoyAssignmentMode::Single);
+ controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
+ Controller_NPad::NpadJoyAssignmentMode::Single);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ rb.Push(ResultSuccess);
}
void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
@@ -1189,15 +1175,15 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
- const auto result = controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
- Controller_NPad::NpadJoyAssignmentMode::Single);
+ controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
+ Controller_NPad::NpadJoyAssignmentMode::Single);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
parameters.npad_id, parameters.applet_resource_user_id,
parameters.npad_joy_device_type);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ rb.Push(ResultSuccess);
}
void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
@@ -1212,14 +1198,13 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
- const auto result = controller.SetNpadMode(parameters.npad_id, {},
- Controller_NPad::NpadJoyAssignmentMode::Dual);
+ controller.SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ rb.Push(ResultSuccess);
}
void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
@@ -1412,8 +1397,11 @@ void Hid::ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) {
void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
+ const auto& controller =
+ GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
Core::HID::VibrationDeviceInfo vibration_device_info;
+ bool check_device_index = false;
switch (vibration_device_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
@@ -1421,34 +1409,46 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::JoyconLeft:
case Core::HID::NpadStyleIndex::JoyconRight:
- default:
vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
+ check_device_index = true;
break;
case Core::HID::NpadStyleIndex::GameCube:
vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
break;
- case Core::HID::NpadStyleIndex::Pokeball:
+ case Core::HID::NpadStyleIndex::N64:
+ vibration_device_info.type = Core::HID::VibrationDeviceType::N64;
+ break;
+ default:
vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
break;
}
- switch (vibration_device_handle.device_index) {
- case Core::HID::DeviceIndex::Left:
- vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
- break;
- case Core::HID::DeviceIndex::Right:
- vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
- break;
- case Core::HID::DeviceIndex::None:
- default:
- ASSERT_MSG(false, "DeviceIndex should never be None!");
- vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
- break;
+ vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
+ if (check_device_index) {
+ switch (vibration_device_handle.device_index) {
+ case Core::HID::DeviceIndex::Left:
+ vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
+ break;
+ case Core::HID::DeviceIndex::Right:
+ vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
+ break;
+ case Core::HID::DeviceIndex::None:
+ default:
+ ASSERT_MSG(false, "DeviceIndex should never be None!");
+ break;
+ }
}
LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}",
vibration_device_info.type, vibration_device_info.position);
+ const auto result = controller.IsDeviceHandleValid(vibration_device_handle);
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.PushRaw(vibration_device_info);
@@ -2146,12 +2146,18 @@ public:
{324, nullptr, "GetUniquePadButtonSet"},
{325, nullptr, "GetUniquePadColor"},
{326, nullptr, "GetUniquePadAppletDetailedUiType"},
+ {327, nullptr, "GetAbstractedPadIdDataFromNpad"},
+ {328, nullptr, "AttachAbstractedPadToNpad"},
+ {329, nullptr, "DetachAbstractedPadAll"},
+ {330, nullptr, "CheckAbstractedPadConnection"},
{500, nullptr, "SetAppletResourceUserId"},
{501, nullptr, "RegisterAppletResourceUserId"},
{502, nullptr, "UnregisterAppletResourceUserId"},
{503, nullptr, "EnableAppletToGetInput"},
{504, nullptr, "SetAruidValidForVibration"},
{505, nullptr, "EnableAppletToGetSixAxisSensor"},
+ {506, nullptr, "EnableAppletToGetPadInput"},
+ {507, nullptr, "EnableAppletToGetTouchScreen"},
{510, nullptr, "SetVibrationMasterVolume"},
{511, nullptr, "GetVibrationMasterVolume"},
{512, nullptr, "BeginPermitVibrationSession"},
@@ -2345,8 +2351,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system
std::make_shared<HidSys>(system)->InstallAsService(service_manager);
std::make_shared<HidTmp>(system)->InstallAsService(service_manager);
- std::make_shared<IRS>(system)->InstallAsService(service_manager);
- std::make_shared<IRS_SYS>(system)->InstallAsService(service_manager);
+ std::make_shared<Service::IRS::IRS>(system)->InstallAsService(service_manager);
+ std::make_shared<Service::IRS::IRS_SYS>(system)->InstallAsService(service_manager);
std::make_shared<XCD_SYS>(system)->InstallAsService(service_manager);
}
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
index fa6153b4c..e5e50845f 100644
--- a/src/core/hle/service/hid/hidbus.cpp
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -50,12 +50,15 @@ HidBus::HidBus(Core::System& system_)
// Register update callbacks
hidbus_update_event = Core::Timing::CreateEvent(
"Hidbus::UpdateCallback",
- [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ [this](std::uintptr_t user_data, s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
UpdateHidbus(user_data, ns_late);
+ return std::nullopt;
});
- system_.CoreTiming().ScheduleEvent(hidbus_update_ns, hidbus_update_event);
+ system_.CoreTiming().ScheduleLoopingEvent(hidbus_update_ns, hidbus_update_ns,
+ hidbus_update_event);
}
HidBus::~HidBus() {
@@ -63,8 +66,6 @@ HidBus::~HidBus() {
}
void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
- auto& core_timing = system.CoreTiming();
-
if (is_hidbus_enabled) {
for (std::size_t i = 0; i < devices.size(); ++i) {
if (!devices[i].is_device_initializated) {
@@ -82,13 +83,6 @@ void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_
sizeof(HidbusStatusManagerEntry));
}
}
-
- // If ns_late is higher than the update rate ignore the delay
- if (ns_late > hidbus_update_ns) {
- ns_late = {};
- }
-
- core_timing.ScheduleEvent(hidbus_update_ns - ns_late, hidbus_update_event);
}
std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const {
diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h
index 6b3015a0f..8c687f678 100644
--- a/src/core/hle/service/hid/hidbus.h
+++ b/src/core/hle/service/hid/hidbus.h
@@ -71,7 +71,7 @@ private:
struct HidbusStatusManagerEntry {
u8 is_connected{};
INSERT_PADDING_BYTES(0x3);
- ResultCode is_connected_result{0};
+ Result is_connected_result{0};
u8 is_enabled{};
u8 is_in_focus{};
u8 is_polling_mode{};
diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.h b/src/core/hle/service/hid/hidbus/hidbus_base.h
index 01c52051b..d3960f506 100644
--- a/src/core/hle/service/hid/hidbus/hidbus_base.h
+++ b/src/core/hle/service/hid/hidbus/hidbus_base.h
@@ -26,7 +26,7 @@ enum class JoyPollingMode : u32 {
};
struct DataAccessorHeader {
- ResultCode result{ResultUnknown};
+ Result result{ResultUnknown};
INSERT_PADDING_WORDS(0x1);
std::array<u8, 0x18> unused{};
u64 latest_entry{};
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index d2a91d913..c4b44cbf9 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -1,16 +1,28 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include <algorithm>
+#include <random>
+
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/irs.h"
+#include "core/hle/service/hid/irsensor/clustering_processor.h"
+#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
+#include "core/hle/service/hid/irsensor/ir_led_processor.h"
+#include "core/hle/service/hid/irsensor/moment_processor.h"
+#include "core/hle/service/hid/irsensor/pointing_processor.h"
+#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
+#include "core/memory.h"
-namespace Service::HID {
+namespace Service::IRS {
IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
// clang-format off
@@ -36,14 +48,19 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
};
// clang-format on
+ u8* raw_shared_memory = system.Kernel().GetIrsSharedMem().GetPointer();
RegisterHandlers(functions);
+ shared_memory = std::construct_at(reinterpret_cast<StatusManager*>(raw_shared_memory));
+
+ npad_device = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
}
+IRS::~IRS() = default;
void IRS::ActivateIrsensor(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={}",
+ LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -54,7 +71,7 @@ void IRS::DeactivateIrsensor(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={}",
+ LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -75,7 +92,7 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -88,17 +105,23 @@ void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Stop Image processor
+ result = ResultSuccess;
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
- PackedMomentProcessorConfig processor_config;
+ Core::IrSensor::PackedMomentProcessorConfig processor_config;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
@@ -109,19 +132,28 @@ void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
+ const auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessor<MomentProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
- PackedClusteringProcessorConfig processor_config;
+ Core::IrSensor::PackedClusteringProcessorConfig processor_config;
};
- static_assert(sizeof(Parameters) == 0x40, "Parameters has incorrect size.");
+ static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
@@ -130,17 +162,27 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessorWithCoreContext<ClusteringProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor =
+ GetProcessor<ClusteringProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
- PackedImageTransferProcessorConfig processor_config;
+ Core::IrSensor::PackedImageTransferProcessorConfig processor_config;
u32 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
@@ -151,20 +193,42 @@ void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
auto t_mem =
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
- LOG_WARNING(Service_IRS,
- "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
- "applet_resource_user_id={}",
- parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
- parameters.transfer_memory_size, parameters.applet_resource_user_id);
+ if (t_mem.IsNull()) {
+ LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size");
+
+ u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress());
+
+ LOG_INFO(Service_IRS,
+ "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, "
+ "applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id);
+
+ const auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor =
+ GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -172,32 +236,68 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- LOG_WARNING(Service_IRS,
- "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
- parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
- parameters.applet_resource_user_id);
+ LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.applet_resource_user_id);
+
+ const auto result = IsIrCameraHandleValid(parameters.camera_handle);
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+
+ if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(InvalidProcessorState);
+ return;
+ }
- IPC::ResponseBuilder rb{ctx, 5};
+ std::vector<u8> data{};
+ const auto& image_transfer_processor =
+ GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
+ const auto& state = image_transfer_processor.GetState(data);
+
+ ctx.WriteBuffer(data);
+ IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
- rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks());
- rb.PushRaw<u32>(0);
+ rb.PushRaw(state);
}
void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
- const auto processor_config{rp.PopRaw<PackedTeraPluginProcessorConfig>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ Core::IrSensor::IrCameraHandle camera_handle;
+ Core::IrSensor::PackedTeraPluginProcessorConfig processor_config;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
- LOG_WARNING(Service_IRS,
- "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
- "applet_resource_user_id={}",
- camera_handle.npad_type, camera_handle.npad_id, processor_config.mode,
- processor_config.required_mcu_version.major,
- processor_config.required_mcu_version.minor, applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(
+ Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
+ "applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major,
+ parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id);
+
+ const auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor =
+ GetProcessor<TeraPluginProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
@@ -207,17 +307,17 @@ void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
npad_id != Core::HID::NpadIdType::Handheld) {
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(InvalidNpadId);
+ rb.Push(Service::HID::InvalidNpadId);
return;
}
- IrCameraHandle camera_handle{
+ Core::IrSensor::IrCameraHandle camera_handle{
.npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)),
.npad_type = Core::HID::NpadStyleIndex::None,
};
- LOG_WARNING(Service_IRS, "(STUBBED) called, npad_id={}, camera_npad_id={}, camera_npad_type={}",
- npad_id, camera_handle.npad_id, camera_handle.npad_type);
+ LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id,
+ camera_handle.npad_id, camera_handle.npad_type);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -226,8 +326,8 @@ void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
- const auto processor_config{rp.PopRaw<PackedPointingProcessorConfig>()};
+ const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
+ const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(
@@ -236,14 +336,23 @@ void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
+ MakeProcessor<PointingProcessor>(camera_handle, device);
+ auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
+ image_transfer_processor.SetConfig(processor_config);
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -256,14 +365,20 @@ void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Suspend image processor
+ result = ResultSuccess;
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
- const auto mcu_version{rp.PopRaw<PackedMcuVersion>()};
+ const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
+ const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(
@@ -272,37 +387,45 @@ void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major,
mcu_version.minor);
+ auto result = IsIrCameraHandleValid(camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Check firmware version
+ result = ResultSuccess;
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- struct Parameters {
- IrCameraHandle camera_handle;
- PackedFunctionLevel function_level;
- u64 applet_resource_user_id;
- };
- static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
-
- const auto parameters{rp.PopRaw<Parameters>()};
+ const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
+ const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_WARNING(Service_IRS,
- "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
- parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
- parameters.applet_resource_user_id);
+ LOG_WARNING(
+ Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}",
+ camera_handle.npad_type, camera_handle.npad_id, function_level.function_level,
+ applet_resource_user_id);
+
+ auto result = IsIrCameraHandleValid(camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Set Function level
+ result = ResultSuccess;
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
- PackedImageTransferProcessorExConfig processor_config;
+ Core::IrSensor::PackedImageTransferProcessorExConfig processor_config;
u64 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
@@ -313,20 +436,33 @@ void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
auto t_mem =
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
- LOG_WARNING(Service_IRS,
- "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
- "applet_resource_user_id={}",
- parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
- parameters.transfer_memory_size, parameters.applet_resource_user_id);
+ u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress());
+
+ LOG_INFO(Service_IRS,
+ "called, npad_type={}, npad_id={}, transfer_memory_size={}, "
+ "applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.transfer_memory_size, parameters.applet_resource_user_id);
+
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor =
+ GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
- const auto processor_config{rp.PopRaw<PackedIrLedProcessorConfig>()};
+ const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
+ const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_IRS,
@@ -336,14 +472,23 @@ void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
+ MakeProcessor<IrLedProcessor>(camera_handle, device);
+ auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
+ image_transfer_processor.SetConfig(processor_config);
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -356,14 +501,20 @@ void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Stop image processor async
+ result = ResultSuccess;
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- PackedFunctionLevel function_level;
+ Core::IrSensor::PackedFunctionLevel function_level;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -378,7 +529,22 @@ void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
-IRS::~IRS() = default;
+Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const {
+ if (camera_handle.npad_id >
+ static_cast<u8>(NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) {
+ return InvalidIrCameraHandle;
+ }
+ if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) {
+ return InvalidIrCameraHandle;
+ }
+ return ResultSuccess;
+}
+
+Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry(
+ const Core::IrSensor::IrCameraHandle& camera_handle) {
+ ASSERT_MSG(sizeof(StatusManager::device) > camera_handle.npad_id, "invalid npad_id");
+ return shared_memory->device[camera_handle.npad_id];
+}
IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
// clang-format off
@@ -395,4 +561,4 @@ IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
IRS_SYS::~IRS_SYS() = default;
-} // namespace Service::HID
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h
index 361dc2213..2e6115c73 100644
--- a/src/core/hle/service/hid/irs.h
+++ b/src/core/hle/service/hid/irs.h
@@ -4,13 +4,19 @@
#pragma once
#include "core/hid/hid_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
-namespace Service::HID {
+namespace Core::HID {
+class EmulatedController;
+} // namespace Core::HID
+
+namespace Service::IRS {
class IRS final : public ServiceFramework<IRS> {
public:
@@ -18,234 +24,19 @@ public:
~IRS() override;
private:
- // This is nn::irsensor::IrCameraStatus
- enum IrCameraStatus : u32 {
- Available,
- Unsupported,
- Unconnected,
- };
-
- // This is nn::irsensor::IrCameraInternalStatus
- enum IrCameraInternalStatus : u32 {
- Stopped,
- FirmwareUpdateNeeded,
- Unkown2,
- Unkown3,
- Unkown4,
- FirmwareVersionRequested,
- FirmwareVersionIsInvalid,
- Ready,
- Setting,
- };
-
- // This is nn::irsensor::detail::StatusManager::IrSensorMode
- enum IrSensorMode : u64 {
- None,
- MomentProcessor,
- ClusteringProcessor,
- ImageTransferProcessor,
- PointingProcessorMarker,
- TeraPluginProcessor,
- IrLedProcessor,
- };
-
- // This is nn::irsensor::ImageProcessorStatus
- enum ImageProcessorStatus : u8 {
- stopped,
- running,
- };
-
- // This is nn::irsensor::ImageTransferProcessorFormat
- enum ImageTransferProcessorFormat : u8 {
- Size320x240,
- Size160x120,
- Size80x60,
- Size40x30,
- Size20x15,
- };
-
- // This is nn::irsensor::AdaptiveClusteringMode
- enum AdaptiveClusteringMode : u8 {
- StaticFov,
- DynamicFov,
- };
-
- // This is nn::irsensor::AdaptiveClusteringTargetDistance
- enum AdaptiveClusteringTargetDistance : u8 {
- Near,
- Middle,
- Far,
- };
-
- // This is nn::irsensor::IrsHandAnalysisMode
- enum IrsHandAnalysisMode : u8 {
- Silhouette,
- Image,
- SilhoueteAndImage,
- SilhuetteOnly,
- };
-
- // This is nn::irsensor::IrSensorFunctionLevel
- enum IrSensorFunctionLevel : u8 {
- unknown0,
- unknown1,
- unknown2,
- unknown3,
- unknown4,
- };
-
- // This is nn::irsensor::IrCameraHandle
- struct IrCameraHandle {
- u8 npad_id{};
- Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
- INSERT_PADDING_BYTES(2);
- };
- static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
-
- struct IrsRect {
- s16 x;
- s16 y;
- s16 width;
- s16 height;
+ // This is nn::irsensor::detail::AruidFormat
+ struct AruidFormat {
+ u64 sensor_aruid;
+ u64 sensor_aruid_status;
};
+ static_assert(sizeof(AruidFormat) == 0x10, "AruidFormat is an invalid size");
- // This is nn::irsensor::PackedMcuVersion
- struct PackedMcuVersion {
- u16 major;
- u16 minor;
+ // This is nn::irsensor::detail::StatusManager
+ struct StatusManager {
+ std::array<Core::IrSensor::DeviceFormat, 9> device;
+ std::array<AruidFormat, 5> aruid;
};
- static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
-
- // This is nn::irsensor::MomentProcessorConfig
- struct MomentProcessorConfig {
- u64 exposire_time;
- u8 light_target;
- u8 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(7);
- IrsRect window_of_interest;
- u8 preprocess;
- u8 preprocess_intensity_threshold;
- INSERT_PADDING_BYTES(5);
- };
- static_assert(sizeof(MomentProcessorConfig) == 0x28,
- "MomentProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedMomentProcessorConfig
- struct PackedMomentProcessorConfig {
- u64 exposire_time;
- u8 light_target;
- u8 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(5);
- IrsRect window_of_interest;
- PackedMcuVersion required_mcu_version;
- u8 preprocess;
- u8 preprocess_intensity_threshold;
- INSERT_PADDING_BYTES(2);
- };
- static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
- "PackedMomentProcessorConfig is an invalid size");
-
- // This is nn::irsensor::ClusteringProcessorConfig
- struct ClusteringProcessorConfig {
- u64 exposire_time;
- u32 light_target;
- u32 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(7);
- IrsRect window_of_interest;
- u32 pixel_count_min;
- u32 pixel_count_max;
- u32 object_intensity_min;
- u8 is_external_light_filter_enabled;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
- "ClusteringProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedClusteringProcessorConfig
- struct PackedClusteringProcessorConfig {
- u64 exposire_time;
- u8 light_target;
- u8 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(5);
- IrsRect window_of_interest;
- PackedMcuVersion required_mcu_version;
- u32 pixel_count_min;
- u32 pixel_count_max;
- u32 object_intensity_min;
- u8 is_external_light_filter_enabled;
- INSERT_PADDING_BYTES(2);
- };
- static_assert(sizeof(PackedClusteringProcessorConfig) == 0x30,
- "PackedClusteringProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedImageTransferProcessorConfig
- struct PackedImageTransferProcessorConfig {
- u64 exposire_time;
- u8 light_target;
- u8 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(5);
- PackedMcuVersion required_mcu_version;
- u8 format;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
- "PackedImageTransferProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedTeraPluginProcessorConfig
- struct PackedTeraPluginProcessorConfig {
- PackedMcuVersion required_mcu_version;
- u8 mode;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
- "PackedTeraPluginProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedPointingProcessorConfig
- struct PackedPointingProcessorConfig {
- IrsRect window_of_interest;
- PackedMcuVersion required_mcu_version;
- };
- static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
- "PackedPointingProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedFunctionLevel
- struct PackedFunctionLevel {
- IrSensorFunctionLevel function_level;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
-
- // This is nn::irsensor::PackedImageTransferProcessorExConfig
- struct PackedImageTransferProcessorExConfig {
- u64 exposire_time;
- u8 light_target;
- u8 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(5);
- PackedMcuVersion required_mcu_version;
- ImageTransferProcessorFormat origin_format;
- ImageTransferProcessorFormat trimming_format;
- u16 trimming_start_x;
- u16 trimming_start_y;
- u8 is_external_light_filter_enabled;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
- "PackedImageTransferProcessorExConfig is an invalid size");
-
- // This is nn::irsensor::PackedIrLedProcessorConfig
- struct PackedIrLedProcessorConfig {
- PackedMcuVersion required_mcu_version;
- u8 light_target;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
- "PackedIrLedProcessorConfig is an invalid size");
+ static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size");
void ActivateIrsensor(Kernel::HLERequestContext& ctx);
void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
@@ -265,6 +56,56 @@ private:
void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
+
+ Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const;
+ Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry(
+ const Core::IrSensor::IrCameraHandle& camera_handle);
+
+ template <typename T>
+ void MakeProcessor(const Core::IrSensor::IrCameraHandle& handle,
+ Core::IrSensor::DeviceFormat& device_state) {
+ const auto index = static_cast<std::size_t>(handle.npad_id);
+ if (index > sizeof(processors)) {
+ LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
+ return;
+ }
+ processors[index] = std::make_unique<T>(device_state);
+ }
+
+ template <typename T>
+ void MakeProcessorWithCoreContext(const Core::IrSensor::IrCameraHandle& handle,
+ Core::IrSensor::DeviceFormat& device_state) {
+ const auto index = static_cast<std::size_t>(handle.npad_id);
+ if (index > sizeof(processors)) {
+ LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
+ return;
+ }
+ processors[index] = std::make_unique<T>(system.HIDCore(), device_state, index);
+ }
+
+ template <typename T>
+ T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) {
+ const auto index = static_cast<std::size_t>(handle.npad_id);
+ if (index > sizeof(processors)) {
+ LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
+ return static_cast<T&>(*processors[0]);
+ }
+ return static_cast<T&>(*processors[index]);
+ }
+
+ template <typename T>
+ const T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) const {
+ const auto index = static_cast<std::size_t>(handle.npad_id);
+ if (index > sizeof(processors)) {
+ LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
+ return static_cast<T&>(*processors[0]);
+ }
+ return static_cast<T&>(*processors[index]);
+ }
+
+ Core::HID::EmulatedController* npad_device = nullptr;
+ StatusManager* shared_memory = nullptr;
+ std::array<std::unique_ptr<ProcessorBase>, 9> processors{};
};
class IRS_SYS final : public ServiceFramework<IRS_SYS> {
@@ -273,4 +114,4 @@ public:
~IRS_SYS() override;
};
-} // namespace Service::HID
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irs_ring_lifo.h b/src/core/hle/service/hid/irs_ring_lifo.h
new file mode 100644
index 000000000..255d1d296
--- /dev/null
+++ b/src/core/hle/service/hid/irs_ring_lifo.h
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+
+#include "common/common_types.h"
+
+namespace Service::IRS {
+
+template <typename State, std::size_t max_buffer_size>
+struct Lifo {
+ s64 sampling_number{};
+ s64 buffer_count{};
+ std::array<State, max_buffer_size> entries{};
+
+ const State& ReadCurrentEntry() const {
+ return entries[GetBufferTail()];
+ }
+
+ const State& ReadPreviousEntry() const {
+ return entries[GetPreviousEntryIndex()];
+ }
+
+ s64 GetBufferTail() const {
+ return sampling_number % max_buffer_size;
+ }
+
+ std::size_t GetPreviousEntryIndex() const {
+ return static_cast<size_t>((GetBufferTail() + max_buffer_size - 1) % max_buffer_size);
+ }
+
+ std::size_t GetNextEntryIndex() const {
+ return static_cast<size_t>((GetBufferTail() + 1) % max_buffer_size);
+ }
+
+ void WriteNextEntry(const State& new_state) {
+ if (buffer_count < static_cast<s64>(max_buffer_size)) {
+ buffer_count++;
+ }
+ sampling_number++;
+ entries[GetBufferTail()] = new_state;
+ }
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.cpp b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
new file mode 100644
index 000000000..e2f4ae876
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
@@ -0,0 +1,265 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <queue>
+
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "core/hle/service/hid/irsensor/clustering_processor.h"
+
+namespace Service::IRS {
+ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_,
+ Core::IrSensor::DeviceFormat& device_format,
+ std::size_t npad_index)
+ : device{device_format} {
+ npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
+
+ device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+ SetDefaultConfig();
+
+ shared_memory = std::construct_at(
+ reinterpret_cast<ClusteringSharedMemory*>(&device_format.state.processor_raw_data));
+
+ Core::HID::ControllerUpdateCallback engine_callback{
+ .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
+ .is_npad_service = true,
+ };
+ callback_key = npad_device->SetCallback(engine_callback);
+}
+
+ClusteringProcessor::~ClusteringProcessor() {
+ npad_device->DeleteCallback(callback_key);
+};
+
+void ClusteringProcessor::StartProcessor() {
+ device.camera_status = Core::IrSensor::IrCameraStatus::Available;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
+}
+
+void ClusteringProcessor::SuspendProcessor() {}
+
+void ClusteringProcessor::StopProcessor() {}
+
+void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
+ if (type != Core::HID::ControllerTriggerType::IrSensor) {
+ return;
+ }
+
+ next_state = {};
+ const auto camera_data = npad_device->GetCamera();
+ auto filtered_image = camera_data.data;
+
+ RemoveLowIntensityData(filtered_image);
+
+ const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x);
+ const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y);
+ const auto window_end_x =
+ window_start_x + static_cast<std::size_t>(current_config.window_of_interest.width);
+ const auto window_end_y =
+ window_start_y + static_cast<std::size_t>(current_config.window_of_interest.height);
+
+ for (std::size_t y = window_start_y; y < window_end_y; y++) {
+ for (std::size_t x = window_start_x; x < window_end_x; x++) {
+ u8 pixel = GetPixel(filtered_image, x, y);
+ if (pixel == 0) {
+ continue;
+ }
+ const auto cluster = GetClusterProperties(filtered_image, x, y);
+ if (cluster.pixel_count > current_config.pixel_count_max) {
+ continue;
+ }
+ if (cluster.pixel_count < current_config.pixel_count_min) {
+ continue;
+ }
+ // Cluster object limit reached
+ if (next_state.object_count >= next_state.data.size()) {
+ continue;
+ }
+ next_state.data[next_state.object_count] = cluster;
+ next_state.object_count++;
+ }
+ }
+
+ next_state.sampling_number = camera_data.sample;
+ next_state.timestamp = next_state.timestamp + 131;
+ next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
+ shared_memory->clustering_lifo.WriteNextEntry(next_state);
+
+ if (!IsProcessorActive()) {
+ StartProcessor();
+ }
+}
+
+void ClusteringProcessor::RemoveLowIntensityData(std::vector<u8>& data) {
+ for (u8& pixel : data) {
+ if (pixel < current_config.pixel_count_min) {
+ pixel = 0;
+ }
+ }
+}
+
+ClusteringProcessor::ClusteringData ClusteringProcessor::GetClusterProperties(std::vector<u8>& data,
+ std::size_t x,
+ std::size_t y) {
+ using DataPoint = Common::Point<std::size_t>;
+ std::queue<DataPoint> search_points{};
+ ClusteringData current_cluster = GetPixelProperties(data, x, y);
+ SetPixel(data, x, y, 0);
+ search_points.emplace<DataPoint>({x, y});
+
+ while (!search_points.empty()) {
+ const auto point = search_points.front();
+ search_points.pop();
+
+ // Avoid negative numbers
+ if (point.x == 0 || point.y == 0) {
+ continue;
+ }
+
+ std::array<DataPoint, 4> new_points{
+ DataPoint{point.x - 1, point.y},
+ {point.x, point.y - 1},
+ {point.x + 1, point.y},
+ {point.x, point.y + 1},
+ };
+
+ for (const auto new_point : new_points) {
+ if (new_point.x >= width) {
+ continue;
+ }
+ if (new_point.y >= height) {
+ continue;
+ }
+ if (GetPixel(data, new_point.x, new_point.y) < current_config.object_intensity_min) {
+ continue;
+ }
+ const ClusteringData cluster = GetPixelProperties(data, new_point.x, new_point.y);
+ current_cluster = MergeCluster(current_cluster, cluster);
+ SetPixel(data, new_point.x, new_point.y, 0);
+ search_points.emplace<DataPoint>({new_point.x, new_point.y});
+ }
+ }
+
+ return current_cluster;
+}
+
+ClusteringProcessor::ClusteringData ClusteringProcessor::GetPixelProperties(
+ const std::vector<u8>& data, std::size_t x, std::size_t y) const {
+ return {
+ .average_intensity = GetPixel(data, x, y) / 255.0f,
+ .centroid =
+ {
+ .x = static_cast<f32>(x),
+ .y = static_cast<f32>(y),
+
+ },
+ .pixel_count = 1,
+ .bound =
+ {
+ .x = static_cast<s16>(x),
+ .y = static_cast<s16>(y),
+ .width = 1,
+ .height = 1,
+ },
+ };
+}
+
+ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster(
+ const ClusteringData a, const ClusteringData b) const {
+ const f32 a_pixel_count = static_cast<f32>(a.pixel_count);
+ const f32 b_pixel_count = static_cast<f32>(b.pixel_count);
+ const f32 pixel_count = a_pixel_count + b_pixel_count;
+ const f32 average_intensity =
+ (a.average_intensity * a_pixel_count + b.average_intensity * b_pixel_count) / pixel_count;
+ const Core::IrSensor::IrsCentroid centroid = {
+ .x = (a.centroid.x * a_pixel_count + b.centroid.x * b_pixel_count) / pixel_count,
+ .y = (a.centroid.y * a_pixel_count + b.centroid.y * b_pixel_count) / pixel_count,
+ };
+ s16 bound_start_x = a.bound.x < b.bound.x ? a.bound.x : b.bound.x;
+ s16 bound_start_y = a.bound.y < b.bound.y ? a.bound.y : b.bound.y;
+ s16 a_bound_end_x = a.bound.x + a.bound.width;
+ s16 a_bound_end_y = a.bound.y + a.bound.height;
+ s16 b_bound_end_x = b.bound.x + b.bound.width;
+ s16 b_bound_end_y = b.bound.y + b.bound.height;
+
+ const Core::IrSensor::IrsRect bound = {
+ .x = bound_start_x,
+ .y = bound_start_y,
+ .width = a_bound_end_x > b_bound_end_x ? static_cast<s16>(a_bound_end_x - bound_start_x)
+ : static_cast<s16>(b_bound_end_x - bound_start_x),
+ .height = a_bound_end_y > b_bound_end_y ? static_cast<s16>(a_bound_end_y - bound_start_y)
+ : static_cast<s16>(b_bound_end_y - bound_start_y),
+ };
+
+ return {
+ .average_intensity = average_intensity,
+ .centroid = centroid,
+ .pixel_count = static_cast<u32>(pixel_count),
+ .bound = bound,
+ };
+}
+
+u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
+ if ((y * width) + x > data.size()) {
+ return 0;
+ }
+ return data[(y * width) + x];
+}
+
+void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) {
+ if ((y * width) + x > data.size()) {
+ return;
+ }
+ data[(y * width) + x] = value;
+}
+
+void ClusteringProcessor::SetDefaultConfig() {
+ using namespace std::literals::chrono_literals;
+ current_config.camera_config.exposure_time = std::chrono::microseconds(200ms).count();
+ current_config.camera_config.gain = 2;
+ current_config.camera_config.is_negative_used = false;
+ current_config.camera_config.light_target = Core::IrSensor::CameraLightTarget::BrightLeds;
+ current_config.window_of_interest = {
+ .x = 0,
+ .y = 0,
+ .width = width,
+ .height = height,
+ };
+ current_config.pixel_count_min = 3;
+ current_config.pixel_count_max = static_cast<u32>(GetDataSize(format));
+ current_config.is_external_light_filter_enabled = true;
+ current_config.object_intensity_min = 150;
+
+ npad_device->SetCameraFormat(format);
+}
+
+void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) {
+ current_config.camera_config.exposure_time = config.camera_config.exposure_time;
+ current_config.camera_config.gain = config.camera_config.gain;
+ current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
+ current_config.camera_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
+ current_config.window_of_interest = config.window_of_interest;
+ current_config.pixel_count_min = config.pixel_count_min;
+ current_config.pixel_count_max = config.pixel_count_max;
+ current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled;
+ current_config.object_intensity_min = config.object_intensity_min;
+
+ LOG_INFO(Service_IRS,
+ "Processor config, exposure_time={}, gain={}, is_negative_used={}, "
+ "light_target={}, window_of_interest=({}, {}, {}, {}), pixel_count_min={}, "
+ "pixel_count_max={}, is_external_light_filter_enabled={}, object_intensity_min={}",
+ current_config.camera_config.exposure_time, current_config.camera_config.gain,
+ current_config.camera_config.is_negative_used,
+ current_config.camera_config.light_target, current_config.window_of_interest.x,
+ current_config.window_of_interest.y, current_config.window_of_interest.width,
+ current_config.window_of_interest.height, current_config.pixel_count_min,
+ current_config.pixel_count_max, current_config.is_external_light_filter_enabled,
+ current_config.object_intensity_min);
+
+ npad_device->SetCameraFormat(format);
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.h b/src/core/hle/service/hid/irsensor/clustering_processor.h
new file mode 100644
index 000000000..dc01a8ea7
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.h
@@ -0,0 +1,110 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irs_ring_lifo.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Core::HID {
+class EmulatedController;
+} // namespace Core::HID
+
+namespace Service::IRS {
+class ClusteringProcessor final : public ProcessorBase {
+public:
+ explicit ClusteringProcessor(Core::HID::HIDCore& hid_core_,
+ Core::IrSensor::DeviceFormat& device_format,
+ std::size_t npad_index);
+ ~ClusteringProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config);
+
+private:
+ static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size320x240;
+ static constexpr std::size_t width = 320;
+ static constexpr std::size_t height = 240;
+
+ // This is nn::irsensor::ClusteringProcessorConfig
+ struct ClusteringProcessorConfig {
+ Core::IrSensor::CameraConfig camera_config;
+ Core::IrSensor::IrsRect window_of_interest;
+ u32 pixel_count_min;
+ u32 pixel_count_max;
+ u32 object_intensity_min;
+ bool is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(3);
+ };
+ static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
+ "ClusteringProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::AdaptiveClusteringProcessorConfig
+ struct AdaptiveClusteringProcessorConfig {
+ Core::IrSensor::AdaptiveClusteringMode mode;
+ Core::IrSensor::AdaptiveClusteringTargetDistance target_distance;
+ };
+ static_assert(sizeof(AdaptiveClusteringProcessorConfig) == 0x8,
+ "AdaptiveClusteringProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::ClusteringData
+ struct ClusteringData {
+ f32 average_intensity;
+ Core::IrSensor::IrsCentroid centroid;
+ u32 pixel_count;
+ Core::IrSensor::IrsRect bound;
+ };
+ static_assert(sizeof(ClusteringData) == 0x18, "ClusteringData is an invalid size");
+
+ // This is nn::irsensor::ClusteringProcessorState
+ struct ClusteringProcessorState {
+ s64 sampling_number;
+ u64 timestamp;
+ u8 object_count;
+ INSERT_PADDING_BYTES(3);
+ Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
+ std::array<ClusteringData, 0x10> data;
+ };
+ static_assert(sizeof(ClusteringProcessorState) == 0x198,
+ "ClusteringProcessorState is an invalid size");
+
+ struct ClusteringSharedMemory {
+ Service::IRS::Lifo<ClusteringProcessorState, 6> clustering_lifo;
+ static_assert(sizeof(clustering_lifo) == 0x9A0, "clustering_lifo is an invalid size");
+ INSERT_PADDING_WORDS(0x11F);
+ };
+ static_assert(sizeof(ClusteringSharedMemory) == 0xE20,
+ "ClusteringSharedMemory is an invalid size");
+
+ void OnControllerUpdate(Core::HID::ControllerTriggerType type);
+ void RemoveLowIntensityData(std::vector<u8>& data);
+ ClusteringData GetClusterProperties(std::vector<u8>& data, std::size_t x, std::size_t y);
+ ClusteringData GetPixelProperties(const std::vector<u8>& data, std::size_t x,
+ std::size_t y) const;
+ ClusteringData MergeCluster(const ClusteringData a, const ClusteringData b) const;
+ u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const;
+ void SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value);
+
+ // Sets config parameters of the camera
+ void SetDefaultConfig();
+
+ ClusteringSharedMemory* shared_memory = nullptr;
+ ClusteringProcessorState next_state{};
+
+ ClusteringProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+ Core::HID::EmulatedController* npad_device;
+ int callback_key{};
+};
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
new file mode 100644
index 000000000..98f0c579d
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
@@ -0,0 +1,150 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
+
+namespace Service::IRS {
+ImageTransferProcessor::ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
+ Core::IrSensor::DeviceFormat& device_format,
+ std::size_t npad_index)
+ : device{device_format} {
+ npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
+
+ Core::HID::ControllerUpdateCallback engine_callback{
+ .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
+ .is_npad_service = true,
+ };
+ callback_key = npad_device->SetCallback(engine_callback);
+
+ device.mode = Core::IrSensor::IrSensorMode::ImageTransferProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+ImageTransferProcessor::~ImageTransferProcessor() {
+ npad_device->DeleteCallback(callback_key);
+};
+
+void ImageTransferProcessor::StartProcessor() {
+ is_active = true;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Available;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
+ processor_state.sampling_number = 0;
+ processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
+}
+
+void ImageTransferProcessor::SuspendProcessor() {}
+
+void ImageTransferProcessor::StopProcessor() {}
+
+void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
+ if (type != Core::HID::ControllerTriggerType::IrSensor) {
+ return;
+ }
+ if (!is_transfer_memory_set) {
+ return;
+ }
+
+ const auto camera_data = npad_device->GetCamera();
+
+ // This indicates how much ambient light is precent
+ processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
+ processor_state.sampling_number = camera_data.sample;
+
+ if (camera_data.format != current_config.origin_format) {
+ LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format,
+ current_config.origin_format);
+ memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
+ return;
+ }
+
+ if (current_config.origin_format > current_config.trimming_format) {
+ LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}",
+ current_config.origin_format, current_config.trimming_format);
+ memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
+ return;
+ }
+
+ std::vector<u8> window_data{};
+ const auto origin_width = GetDataWidth(current_config.origin_format);
+ const auto origin_height = GetDataHeight(current_config.origin_format);
+ const auto trimming_width = GetDataWidth(current_config.trimming_format);
+ const auto trimming_height = GetDataHeight(current_config.trimming_format);
+ window_data.resize(GetDataSize(current_config.trimming_format));
+
+ if (trimming_width + current_config.trimming_start_x > origin_width ||
+ trimming_height + current_config.trimming_start_y > origin_height) {
+ LOG_WARNING(Service_IRS,
+ "Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})",
+ current_config.trimming_start_x, current_config.trimming_start_y,
+ trimming_width, trimming_height, origin_width, origin_height);
+ memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
+ return;
+ }
+
+ for (std::size_t y = 0; y < trimming_height; y++) {
+ for (std::size_t x = 0; x < trimming_width; x++) {
+ const std::size_t window_index = (y * trimming_width) + x;
+ const std::size_t origin_index =
+ ((y + current_config.trimming_start_y) * origin_width) + x +
+ current_config.trimming_start_x;
+ window_data[window_index] = camera_data.data[origin_index];
+ }
+ }
+
+ memcpy(transfer_memory, window_data.data(), GetDataSize(current_config.trimming_format));
+
+ if (!IsProcessorActive()) {
+ StartProcessor();
+ }
+}
+
+void ImageTransferProcessor::SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config) {
+ current_config.camera_config.exposure_time = config.camera_config.exposure_time;
+ current_config.camera_config.gain = config.camera_config.gain;
+ current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
+ current_config.camera_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
+ current_config.origin_format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
+ current_config.trimming_format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
+ current_config.trimming_start_x = 0;
+ current_config.trimming_start_y = 0;
+
+ npad_device->SetCameraFormat(current_config.origin_format);
+}
+
+void ImageTransferProcessor::SetConfig(
+ Core::IrSensor::PackedImageTransferProcessorExConfig config) {
+ current_config.camera_config.exposure_time = config.camera_config.exposure_time;
+ current_config.camera_config.gain = config.camera_config.gain;
+ current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
+ current_config.camera_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
+ current_config.origin_format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.origin_format);
+ current_config.trimming_format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.trimming_format);
+ current_config.trimming_start_x = config.trimming_start_x;
+ current_config.trimming_start_y = config.trimming_start_y;
+
+ npad_device->SetCameraFormat(current_config.origin_format);
+}
+
+void ImageTransferProcessor::SetTransferMemoryPointer(u8* t_mem) {
+ is_transfer_memory_set = true;
+ transfer_memory = t_mem;
+}
+
+Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState(
+ std::vector<u8>& data) const {
+ const auto size = GetDataSize(current_config.trimming_format);
+ data.resize(size);
+ memcpy(data.data(), transfer_memory, size);
+ return processor_state;
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.h b/src/core/hle/service/hid/irsensor/image_transfer_processor.h
new file mode 100644
index 000000000..393df492d
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.h
@@ -0,0 +1,73 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Core::HID {
+class EmulatedController;
+} // namespace Core::HID
+
+namespace Service::IRS {
+class ImageTransferProcessor final : public ProcessorBase {
+public:
+ explicit ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
+ Core::IrSensor::DeviceFormat& device_format,
+ std::size_t npad_index);
+ ~ImageTransferProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config);
+ void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config);
+
+ // Transfer memory where the image data will be stored
+ void SetTransferMemoryPointer(u8* t_mem);
+
+ Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const;
+
+private:
+ // This is nn::irsensor::ImageTransferProcessorConfig
+ struct ImageTransferProcessorConfig {
+ Core::IrSensor::CameraConfig camera_config;
+ Core::IrSensor::ImageTransferProcessorFormat format;
+ };
+ static_assert(sizeof(ImageTransferProcessorConfig) == 0x20,
+ "ImageTransferProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::ImageTransferProcessorExConfig
+ struct ImageTransferProcessorExConfig {
+ Core::IrSensor::CameraConfig camera_config;
+ Core::IrSensor::ImageTransferProcessorFormat origin_format;
+ Core::IrSensor::ImageTransferProcessorFormat trimming_format;
+ u16 trimming_start_x;
+ u16 trimming_start_y;
+ bool is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(3);
+ };
+ static_assert(sizeof(ImageTransferProcessorExConfig) == 0x28,
+ "ImageTransferProcessorExConfig is an invalid size");
+
+ void OnControllerUpdate(Core::HID::ControllerTriggerType type);
+
+ ImageTransferProcessorExConfig current_config{};
+ Core::IrSensor::ImageTransferProcessorState processor_state{};
+ Core::IrSensor::DeviceFormat& device;
+ Core::HID::EmulatedController* npad_device;
+ int callback_key{};
+
+ u8* transfer_memory = nullptr;
+ bool is_transfer_memory_set = false;
+};
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.cpp b/src/core/hle/service/hid/irsensor/ir_led_processor.cpp
new file mode 100644
index 000000000..8e6dd99e4
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/ir_led_processor.cpp
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/ir_led_processor.h"
+
+namespace Service::IRS {
+IrLedProcessor::IrLedProcessor(Core::IrSensor::DeviceFormat& device_format)
+ : device(device_format) {
+ device.mode = Core::IrSensor::IrSensorMode::IrLedProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+IrLedProcessor::~IrLedProcessor() = default;
+
+void IrLedProcessor::StartProcessor() {}
+
+void IrLedProcessor::SuspendProcessor() {}
+
+void IrLedProcessor::StopProcessor() {}
+
+void IrLedProcessor::SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config) {
+ current_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.light_target);
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.h b/src/core/hle/service/hid/irsensor/ir_led_processor.h
new file mode 100644
index 000000000..c3d8693c9
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/ir_led_processor.h
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+class IrLedProcessor final : public ProcessorBase {
+public:
+ explicit IrLedProcessor(Core::IrSensor::DeviceFormat& device_format);
+ ~IrLedProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config);
+
+private:
+ // This is nn::irsensor::IrLedProcessorConfig
+ struct IrLedProcessorConfig {
+ Core::IrSensor::CameraLightTarget light_target;
+ };
+ static_assert(sizeof(IrLedProcessorConfig) == 0x4, "IrLedProcessorConfig is an invalid size");
+
+ struct IrLedProcessorState {
+ s64 sampling_number;
+ u64 timestamp;
+ std::array<u8, 0x8> data;
+ };
+ static_assert(sizeof(IrLedProcessorState) == 0x18, "IrLedProcessorState is an invalid size");
+
+ IrLedProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.cpp b/src/core/hle/service/hid/irsensor/moment_processor.cpp
new file mode 100644
index 000000000..dbaca420a
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/moment_processor.cpp
@@ -0,0 +1,34 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/moment_processor.h"
+
+namespace Service::IRS {
+MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format)
+ : device(device_format) {
+ device.mode = Core::IrSensor::IrSensorMode::MomentProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+MomentProcessor::~MomentProcessor() = default;
+
+void MomentProcessor::StartProcessor() {}
+
+void MomentProcessor::SuspendProcessor() {}
+
+void MomentProcessor::StopProcessor() {}
+
+void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) {
+ current_config.camera_config.exposure_time = config.camera_config.exposure_time;
+ current_config.camera_config.gain = config.camera_config.gain;
+ current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
+ current_config.camera_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
+ current_config.window_of_interest = config.window_of_interest;
+ current_config.preprocess =
+ static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess);
+ current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold;
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.h b/src/core/hle/service/hid/irsensor/moment_processor.h
new file mode 100644
index 000000000..d4bd22e0f
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/moment_processor.h
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+class MomentProcessor final : public ProcessorBase {
+public:
+ explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format);
+ ~MomentProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config);
+
+private:
+ // This is nn::irsensor::MomentProcessorConfig
+ struct MomentProcessorConfig {
+ Core::IrSensor::CameraConfig camera_config;
+ Core::IrSensor::IrsRect window_of_interest;
+ Core::IrSensor::MomentProcessorPreprocess preprocess;
+ u32 preprocess_intensity_threshold;
+ };
+ static_assert(sizeof(MomentProcessorConfig) == 0x28,
+ "MomentProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::MomentStatistic
+ struct MomentStatistic {
+ f32 average_intensity;
+ Core::IrSensor::IrsCentroid centroid;
+ };
+ static_assert(sizeof(MomentStatistic) == 0xC, "MomentStatistic is an invalid size");
+
+ // This is nn::irsensor::MomentProcessorState
+ struct MomentProcessorState {
+ s64 sampling_number;
+ u64 timestamp;
+ Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
+ INSERT_PADDING_BYTES(4);
+ std::array<MomentStatistic, 0x30> stadistic;
+ };
+ static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size");
+
+ MomentProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.cpp b/src/core/hle/service/hid/irsensor/pointing_processor.cpp
new file mode 100644
index 000000000..929f177fc
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/pointing_processor.cpp
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/pointing_processor.h"
+
+namespace Service::IRS {
+PointingProcessor::PointingProcessor(Core::IrSensor::DeviceFormat& device_format)
+ : device(device_format) {
+ device.mode = Core::IrSensor::IrSensorMode::PointingProcessorMarker;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+PointingProcessor::~PointingProcessor() = default;
+
+void PointingProcessor::StartProcessor() {}
+
+void PointingProcessor::SuspendProcessor() {}
+
+void PointingProcessor::StopProcessor() {}
+
+void PointingProcessor::SetConfig(Core::IrSensor::PackedPointingProcessorConfig config) {
+ current_config.window_of_interest = config.window_of_interest;
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.h b/src/core/hle/service/hid/irsensor/pointing_processor.h
new file mode 100644
index 000000000..cf4930794
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/pointing_processor.h
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+class PointingProcessor final : public ProcessorBase {
+public:
+ explicit PointingProcessor(Core::IrSensor::DeviceFormat& device_format);
+ ~PointingProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedPointingProcessorConfig config);
+
+private:
+ // This is nn::irsensor::PointingProcessorConfig
+ struct PointingProcessorConfig {
+ Core::IrSensor::IrsRect window_of_interest;
+ };
+ static_assert(sizeof(PointingProcessorConfig) == 0x8,
+ "PointingProcessorConfig is an invalid size");
+
+ struct PointingProcessorMarkerData {
+ u8 pointing_status;
+ INSERT_PADDING_BYTES(3);
+ u32 unknown;
+ float unkown_float1;
+ float position_x;
+ float position_y;
+ float unkown_float2;
+ Core::IrSensor::IrsRect window_of_interest;
+ };
+ static_assert(sizeof(PointingProcessorMarkerData) == 0x20,
+ "PointingProcessorMarkerData is an invalid size");
+
+ struct PointingProcessorMarkerState {
+ s64 sampling_number;
+ u64 timestamp;
+ std::array<PointingProcessorMarkerData, 0x3> data;
+ };
+ static_assert(sizeof(PointingProcessorMarkerState) == 0x70,
+ "PointingProcessorMarkerState is an invalid size");
+
+ PointingProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/processor_base.cpp b/src/core/hle/service/hid/irsensor/processor_base.cpp
new file mode 100644
index 000000000..4d43ca17a
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/processor_base.cpp
@@ -0,0 +1,67 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+
+ProcessorBase::ProcessorBase() {}
+ProcessorBase::~ProcessorBase() = default;
+
+bool ProcessorBase::IsProcessorActive() const {
+ return is_active;
+}
+
+std::size_t ProcessorBase::GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const {
+ switch (format) {
+ case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
+ return 320 * 240;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
+ return 160 * 120;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
+ return 80 * 60;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
+ return 40 * 30;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
+ return 20 * 15;
+ default:
+ return 0;
+ }
+}
+
+std::size_t ProcessorBase::GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const {
+ switch (format) {
+ case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
+ return 320;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
+ return 160;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
+ return 80;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
+ return 40;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
+ return 20;
+ default:
+ return 0;
+ }
+}
+
+std::size_t ProcessorBase::GetDataHeight(
+ Core::IrSensor::ImageTransferProcessorFormat format) const {
+ switch (format) {
+ case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
+ return 240;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
+ return 120;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
+ return 60;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
+ return 30;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
+ return 15;
+ default:
+ return 0;
+ }
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/processor_base.h b/src/core/hle/service/hid/irsensor/processor_base.h
new file mode 100644
index 000000000..bc0d2977b
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/processor_base.h
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+
+namespace Service::IRS {
+class ProcessorBase {
+public:
+ explicit ProcessorBase();
+ virtual ~ProcessorBase();
+
+ virtual void StartProcessor() = 0;
+ virtual void SuspendProcessor() = 0;
+ virtual void StopProcessor() = 0;
+
+ bool IsProcessorActive() const;
+
+protected:
+ /// Returns the number of bytes the image uses
+ std::size_t GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const;
+
+ /// Returns the width of the image
+ std::size_t GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const;
+
+ /// Returns the height of the image
+ std::size_t GetDataHeight(Core::IrSensor::ImageTransferProcessorFormat format) const;
+
+ bool is_active{false};
+};
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp b/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp
new file mode 100644
index 000000000..e691c840a
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
+
+namespace Service::IRS {
+TeraPluginProcessor::TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format)
+ : device(device_format) {
+ device.mode = Core::IrSensor::IrSensorMode::TeraPluginProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+TeraPluginProcessor::~TeraPluginProcessor() = default;
+
+void TeraPluginProcessor::StartProcessor() {}
+
+void TeraPluginProcessor::SuspendProcessor() {}
+
+void TeraPluginProcessor::StopProcessor() {}
+
+void TeraPluginProcessor::SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config) {
+ current_config.mode = config.mode;
+ current_config.unknown_1 = config.unknown_1;
+ current_config.unknown_2 = config.unknown_2;
+ current_config.unknown_3 = config.unknown_3;
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.h b/src/core/hle/service/hid/irsensor/tera_plugin_processor.h
new file mode 100644
index 000000000..bbea7ed0b
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/tera_plugin_processor.h
@@ -0,0 +1,53 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+class TeraPluginProcessor final : public ProcessorBase {
+public:
+ explicit TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format);
+ ~TeraPluginProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config);
+
+private:
+ // This is nn::irsensor::TeraPluginProcessorConfig
+ struct TeraPluginProcessorConfig {
+ u8 mode;
+ u8 unknown_1;
+ u8 unknown_2;
+ u8 unknown_3;
+ };
+ static_assert(sizeof(TeraPluginProcessorConfig) == 0x4,
+ "TeraPluginProcessorConfig is an invalid size");
+
+ struct TeraPluginProcessorState {
+ s64 sampling_number;
+ u64 timestamp;
+ Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
+ std::array<u8, 0x12c> data;
+ };
+ static_assert(sizeof(TeraPluginProcessorState) == 0x140,
+ "TeraPluginProcessorState is an invalid size");
+
+ TeraPluginProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/ldn/errors.h b/src/core/hle/service/ldn/errors.h
deleted file mode 100644
index fb86b9402..000000000
--- a/src/core/hle/service/ldn/errors.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/result.h"
-
-namespace Service::LDN {
-
-constexpr ResultCode ERROR_DISABLED{ErrorModule::LDN, 22};
-
-} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index 125d4dc4c..c11daff54 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -3,11 +3,15 @@
#include <memory>
-#include "core/hle/ipc_helpers.h"
-#include "core/hle/result.h"
-#include "core/hle/service/ldn/errors.h"
+#include "core/core.h"
#include "core/hle/service/ldn/ldn.h"
-#include "core/hle/service/sm/sm.h"
+#include "core/hle/service/ldn/ldn_results.h"
+#include "core/hle/service/ldn/ldn_types.h"
+#include "core/internal_network/network.h"
+#include "core/internal_network/network_interface.h"
+
+// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent
+#undef CreateEvent
namespace Service::LDN {
@@ -100,74 +104,418 @@ class IUserLocalCommunicationService final
: public ServiceFramework<IUserLocalCommunicationService> {
public:
explicit IUserLocalCommunicationService(Core::System& system_)
- : ServiceFramework{system_, "IUserLocalCommunicationService"} {
+ : ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew},
+ service_context{system, "IUserLocalCommunicationService"}, room_network{
+ system_.GetRoomNetwork()} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IUserLocalCommunicationService::GetState, "GetState"},
- {1, nullptr, "GetNetworkInfo"},
+ {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"},
{2, nullptr, "GetIpv4Address"},
- {3, nullptr, "GetDisconnectReason"},
- {4, nullptr, "GetSecurityParameter"},
- {5, nullptr, "GetNetworkConfig"},
- {100, nullptr, "AttachStateChangeEvent"},
- {101, nullptr, "GetNetworkInfoLatestUpdate"},
- {102, nullptr, "Scan"},
- {103, nullptr, "ScanPrivate"},
+ {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"},
+ {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"},
+ {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"},
+ {100, &IUserLocalCommunicationService::AttachStateChangeEvent, "AttachStateChangeEvent"},
+ {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"},
+ {102, &IUserLocalCommunicationService::Scan, "Scan"},
+ {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"},
{104, nullptr, "SetWirelessControllerRestriction"},
- {200, nullptr, "OpenAccessPoint"},
- {201, nullptr, "CloseAccessPoint"},
- {202, nullptr, "CreateNetwork"},
- {203, nullptr, "CreateNetworkPrivate"},
- {204, nullptr, "DestroyNetwork"},
+ {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"},
+ {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"},
+ {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"},
+ {203, &IUserLocalCommunicationService::CreateNetworkPrivate, "CreateNetworkPrivate"},
+ {204, &IUserLocalCommunicationService::DestroyNetwork, "DestroyNetwork"},
{205, nullptr, "Reject"},
- {206, nullptr, "SetAdvertiseData"},
- {207, nullptr, "SetStationAcceptPolicy"},
- {208, nullptr, "AddAcceptFilterEntry"},
+ {206, &IUserLocalCommunicationService::SetAdvertiseData, "SetAdvertiseData"},
+ {207, &IUserLocalCommunicationService::SetStationAcceptPolicy, "SetStationAcceptPolicy"},
+ {208, &IUserLocalCommunicationService::AddAcceptFilterEntry, "AddAcceptFilterEntry"},
{209, nullptr, "ClearAcceptFilter"},
- {300, nullptr, "OpenStation"},
- {301, nullptr, "CloseStation"},
- {302, nullptr, "Connect"},
+ {300, &IUserLocalCommunicationService::OpenStation, "OpenStation"},
+ {301, &IUserLocalCommunicationService::CloseStation, "CloseStation"},
+ {302, &IUserLocalCommunicationService::Connect, "Connect"},
{303, nullptr, "ConnectPrivate"},
- {304, nullptr, "Disconnect"},
- {400, nullptr, "Initialize"},
- {401, nullptr, "Finalize"},
- {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, // 7.0.0+
+ {304, &IUserLocalCommunicationService::Disconnect, "Disconnect"},
+ {400, &IUserLocalCommunicationService::Initialize, "Initialize"},
+ {401, &IUserLocalCommunicationService::Finalize, "Finalize"},
+ {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"},
};
// clang-format on
RegisterHandlers(functions);
+
+ state_change_event =
+ service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent");
+ }
+
+ ~IUserLocalCommunicationService() {
+ service_context.CloseEvent(state_change_event);
+ }
+
+ void OnEventFired() {
+ state_change_event->GetWritableEvent().Signal();
}
void GetState(Kernel::HLERequestContext& ctx) {
+ State state = State::Error;
+ LOG_WARNING(Service_LDN, "(STUBBED) called, state = {}", state);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushEnum(state);
+ }
+
+ void GetNetworkInfo(Kernel::HLERequestContext& ctx) {
+ const auto write_buffer_size = ctx.GetWriteBufferSize();
+
+ if (write_buffer_size != sizeof(NetworkInfo)) {
+ LOG_ERROR(Service_LDN, "Invalid buffer size {}", write_buffer_size);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultBadInput);
+ return;
+ }
+
+ NetworkInfo network_info{};
+ const auto rc = ResultSuccess;
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ return;
+ }
+
+ LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}",
+ network_info.common.ssid.GetStringValue(), network_info.ldn.node_count);
+
+ ctx.WriteBuffer<NetworkInfo>(network_info);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ }
+
+ void GetDisconnectReason(Kernel::HLERequestContext& ctx) {
+ const auto disconnect_reason = DisconnectReason::None;
+
+ LOG_WARNING(Service_LDN, "(STUBBED) called, disconnect_reason={}", disconnect_reason);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushEnum(disconnect_reason);
+ }
+
+ void GetSecurityParameter(Kernel::HLERequestContext& ctx) {
+ SecurityParameter security_parameter{};
+ NetworkInfo info{};
+ const Result rc = ResultSuccess;
+
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ return;
+ }
+
+ security_parameter.session_id = info.network_id.session_id;
+ std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(),
+ sizeof(SecurityParameter::data));
+
LOG_WARNING(Service_LDN, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 10};
+ rb.Push(rc);
+ rb.PushRaw<SecurityParameter>(security_parameter);
+ }
+
+ void GetNetworkConfig(Kernel::HLERequestContext& ctx) {
+ NetworkConfig config{};
+ NetworkInfo info{};
+ const Result rc = ResultSuccess;
+
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ return;
+ }
+
+ config.intent_id = info.network_id.intent_id;
+ config.channel = info.common.channel;
+ config.node_count_max = info.ldn.node_count_max;
+ config.local_communication_version = info.ldn.nodes[0].local_communication_version;
+
+ LOG_WARNING(Service_LDN,
+ "(STUBBED) called, intent_id={}/{}, channel={}, node_count_max={}, "
+ "local_communication_version={}",
+ config.intent_id.local_communication_id, config.intent_id.scene_id,
+ config.channel, config.node_count_max, config.local_communication_version);
+
+ IPC::ResponseBuilder rb{ctx, 10};
+ rb.Push(rc);
+ rb.PushRaw<NetworkConfig>(config);
+ }
+
+ void AttachStateChangeEvent(Kernel::HLERequestContext& ctx) {
+ LOG_INFO(Service_LDN, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(state_change_event->GetReadableEvent());
+ }
+
+ void GetNetworkInfoLatestUpdate(Kernel::HLERequestContext& ctx) {
+ const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0);
+ const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate);
+
+ if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) {
+ LOG_ERROR(Service_LDN, "Invalid buffer size {}, {}", network_buffer_size,
+ node_buffer_count);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultBadInput);
+ return;
+ }
+
+ NetworkInfo info;
+ std::vector<NodeLatestUpdate> latest_update(node_buffer_count);
+
+ const auto rc = ResultSuccess;
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ return;
+ }
+
+ LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}",
+ info.common.ssid.GetStringValue(), info.ldn.node_count);
+
+ ctx.WriteBuffer(info, 0);
+ ctx.WriteBuffer(latest_update, 1);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void Scan(Kernel::HLERequestContext& ctx) {
+ ScanImpl(ctx);
+ }
+
+ void ScanPrivate(Kernel::HLERequestContext& ctx) {
+ ScanImpl(ctx, true);
+ }
+
+ void ScanImpl(Kernel::HLERequestContext& ctx, bool is_private = false) {
+ IPC::RequestParser rp{ctx};
+ const auto channel{rp.PopEnum<WifiChannel>()};
+ const auto scan_filter{rp.PopRaw<ScanFilter>()};
+
+ const std::size_t network_info_size = ctx.GetWriteBufferSize() / sizeof(NetworkInfo);
+
+ if (network_info_size == 0) {
+ LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultBadInput);
+ return;
+ }
+
+ u16 count = 0;
+ std::vector<NetworkInfo> network_infos(network_info_size);
+
+ LOG_WARNING(Service_LDN,
+ "(STUBBED) called, channel={}, filter_scan_flag={}, filter_network_type={}",
+ channel, scan_filter.flag, scan_filter.network_type);
+
+ ctx.WriteBuffer(network_infos);
+
IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u32>(count);
+ }
+
+ void OpenAccessPoint(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void CloseAccessPoint(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void CreateNetwork(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ SecurityConfig security_config;
+ UserConfig user_config;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ NetworkConfig network_config;
+ };
+ static_assert(sizeof(Parameters) == 0x98, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_LDN,
+ "(STUBBED) called, passphrase_size={}, security_mode={}, "
+ "local_communication_version={}",
+ parameters.security_config.passphrase_size,
+ parameters.security_config.security_mode,
+ parameters.network_config.local_communication_version);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ SecurityConfig security_config;
+ SecurityParameter security_parameter;
+ UserConfig user_config;
+ NetworkConfig network_config;
+ };
+ static_assert(sizeof(Parameters) == 0xB8, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_LDN,
+ "(STUBBED) called, passphrase_size={}, security_mode={}, "
+ "local_communication_version={}",
+ parameters.security_config.passphrase_size,
+ parameters.security_config.security_mode,
+ parameters.network_config.local_communication_version);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void DestroyNetwork(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void SetAdvertiseData(Kernel::HLERequestContext& ctx) {
+ std::vector<u8> read_buffer = ctx.ReadBuffer();
+
+ LOG_WARNING(Service_LDN, "(STUBBED) called, size {}", read_buffer.size());
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
- // Indicate a network error, as we do not actually emulate LDN
- rb.Push(static_cast<u32>(State::Error));
+ void AddAcceptFilterEntry(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void OpenStation(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void CloseStation(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void Connect(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ SecurityConfig security_config;
+ UserConfig user_config;
+ u32 local_communication_version;
+ u32 option;
+ };
+ static_assert(sizeof(Parameters) == 0x7C, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_LDN,
+ "(STUBBED) called, passphrase_size={}, security_mode={}, "
+ "local_communication_version={}",
+ parameters.security_config.passphrase_size,
+ parameters.security_config.security_mode,
+ parameters.local_communication_version);
+
+ const std::vector<u8> read_buffer = ctx.ReadBuffer();
+ NetworkInfo network_info{};
+
+ if (read_buffer.size() != sizeof(NetworkInfo)) {
+ LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultBadInput);
+ return;
+ }
+
+ std::memcpy(&network_info, read_buffer.data(), read_buffer.size());
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void Disconnect(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+ void Initialize(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ const auto rc = InitializeImpl(ctx);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ }
+
+ void Finalize(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ is_initialized = false;
+
+ IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Initialize2(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_LDN, "called");
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
- is_initialized = true;
+ const auto rc = InitializeImpl(ctx);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_DISABLED);
+ rb.Push(rc);
+ }
+
+ Result InitializeImpl(Kernel::HLERequestContext& ctx) {
+ const auto network_interface = Network::GetSelectedNetworkInterface();
+ if (!network_interface) {
+ LOG_ERROR(Service_LDN, "No network interface is set");
+ return ResultAirplaneModeEnabled;
+ }
+
+ is_initialized = true;
+ // TODO (flTobi): Change this to ResultSuccess when LDN is fully implemented
+ return ResultAirplaneModeEnabled;
}
-private:
- enum class State {
- None,
- Initialized,
- AccessPointOpened,
- AccessPointCreated,
- StationOpened,
- StationConnected,
- Error,
- };
+ KernelHelpers::ServiceContext service_context;
+ Kernel::KEvent* state_change_event;
+ Network::RoomNetwork& room_network;
bool is_initialized{};
};
@@ -273,7 +621,7 @@ public:
LOG_WARNING(Service_LDN, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_DISABLED);
+ rb.Push(ResultDisabled);
}
};
diff --git a/src/core/hle/service/ldn/ldn.h b/src/core/hle/service/ldn/ldn.h
index a0031ac71..6afe2ea6f 100644
--- a/src/core/hle/service/ldn/ldn.h
+++ b/src/core/hle/service/ldn/ldn.h
@@ -3,6 +3,12 @@
#pragma once
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/result.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/sm/sm.h"
+
namespace Core {
class System;
}
diff --git a/src/core/hle/service/ldn/ldn_results.h b/src/core/hle/service/ldn/ldn_results.h
new file mode 100644
index 000000000..f340bda42
--- /dev/null
+++ b/src/core/hle/service/ldn/ldn_results.h
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Service::LDN {
+
+constexpr Result ResultAdvertiseDataTooLarge{ErrorModule::LDN, 10};
+constexpr Result ResultAuthenticationFailed{ErrorModule::LDN, 20};
+constexpr Result ResultDisabled{ErrorModule::LDN, 22};
+constexpr Result ResultAirplaneModeEnabled{ErrorModule::LDN, 23};
+constexpr Result ResultInvalidNodeCount{ErrorModule::LDN, 30};
+constexpr Result ResultConnectionFailed{ErrorModule::LDN, 31};
+constexpr Result ResultBadState{ErrorModule::LDN, 32};
+constexpr Result ResultNoIpAddress{ErrorModule::LDN, 33};
+constexpr Result ResultInvalidBufferCount{ErrorModule::LDN, 50};
+constexpr Result ResultAccessPointConnectionFailed{ErrorModule::LDN, 65};
+constexpr Result ResultAuthenticationTimeout{ErrorModule::LDN, 66};
+constexpr Result ResultMaximumNodeCount{ErrorModule::LDN, 67};
+constexpr Result ResultBadInput{ErrorModule::LDN, 96};
+constexpr Result ResultLocalCommunicationIdNotFound{ErrorModule::LDN, 97};
+constexpr Result ResultLocalCommunicationVersionTooLow{ErrorModule::LDN, 113};
+constexpr Result ResultLocalCommunicationVersionTooHigh{ErrorModule::LDN, 114};
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h
new file mode 100644
index 000000000..0c07a7397
--- /dev/null
+++ b/src/core/hle/service/ldn/ldn_types.h
@@ -0,0 +1,284 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <fmt/format.h>
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "network/network.h"
+
+namespace Service::LDN {
+
+constexpr size_t SsidLengthMax = 32;
+constexpr size_t AdvertiseDataSizeMax = 384;
+constexpr size_t UserNameBytesMax = 32;
+constexpr int NodeCountMax = 8;
+constexpr int StationCountMax = NodeCountMax - 1;
+constexpr size_t PassphraseLengthMax = 64;
+
+enum class SecurityMode : u16 {
+ All,
+ Retail,
+ Debug,
+};
+
+enum class NodeStateChange : u8 {
+ None,
+ Connect,
+ Disconnect,
+ DisconnectAndConnect,
+};
+
+enum class ScanFilterFlag : u32 {
+ None = 0,
+ LocalCommunicationId = 1 << 0,
+ SessionId = 1 << 1,
+ NetworkType = 1 << 2,
+ Ssid = 1 << 4,
+ SceneId = 1 << 5,
+ IntentId = LocalCommunicationId | SceneId,
+ NetworkId = IntentId | SessionId,
+};
+
+enum class NetworkType : u32 {
+ None,
+ General,
+ Ldn,
+ All,
+};
+
+enum class PackedNetworkType : u8 {
+ None,
+ General,
+ Ldn,
+ All,
+};
+
+enum class State : u32 {
+ None,
+ Initialized,
+ AccessPointOpened,
+ AccessPointCreated,
+ StationOpened,
+ StationConnected,
+ Error,
+};
+
+enum class DisconnectReason : s16 {
+ Unknown = -1,
+ None,
+ DisconnectedByUser,
+ DisconnectedBySystem,
+ DestroyedByUser,
+ DestroyedBySystem,
+ Rejected,
+ SignalLost,
+};
+
+enum class NetworkError {
+ Unknown = -1,
+ None = 0,
+ PortUnreachable,
+ TooManyPlayers,
+ VersionTooLow,
+ VersionTooHigh,
+ ConnectFailure,
+ ConnectNotFound,
+ ConnectTimeout,
+ ConnectRejected,
+ RejectFailed,
+};
+
+enum class AcceptPolicy : u8 {
+ AcceptAll,
+ RejectAll,
+ BlackList,
+ WhiteList,
+};
+
+enum class WifiChannel : s16 {
+ Default = 0,
+ wifi24_1 = 1,
+ wifi24_6 = 6,
+ wifi24_11 = 11,
+ wifi50_36 = 36,
+ wifi50_40 = 40,
+ wifi50_44 = 44,
+ wifi50_48 = 48,
+};
+
+enum class LinkLevel : s8 {
+ Bad,
+ Low,
+ Good,
+ Excelent,
+};
+
+struct NodeLatestUpdate {
+ NodeStateChange state_change;
+ INSERT_PADDING_BYTES(0x7); // Unknown
+};
+static_assert(sizeof(NodeLatestUpdate) == 0x8, "NodeLatestUpdate is an invalid size");
+
+struct SessionId {
+ u64 high;
+ u64 low;
+
+ bool operator==(const SessionId&) const = default;
+};
+static_assert(sizeof(SessionId) == 0x10, "SessionId is an invalid size");
+
+struct IntentId {
+ u64 local_communication_id;
+ INSERT_PADDING_BYTES(0x2); // Reserved
+ u16 scene_id;
+ INSERT_PADDING_BYTES(0x4); // Reserved
+};
+static_assert(sizeof(IntentId) == 0x10, "IntentId is an invalid size");
+
+struct NetworkId {
+ IntentId intent_id;
+ SessionId session_id;
+};
+static_assert(sizeof(NetworkId) == 0x20, "NetworkId is an invalid size");
+
+struct Ssid {
+ u8 length;
+ std::array<char, SsidLengthMax + 1> raw;
+
+ std::string GetStringValue() const {
+ return std::string(raw.data(), length);
+ }
+};
+static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size");
+
+struct Ipv4Address {
+ union {
+ u32 raw{};
+ std::array<u8, 4> bytes;
+ };
+
+ std::string GetStringValue() const {
+ return fmt::format("{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]);
+ }
+};
+static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size");
+
+struct MacAddress {
+ std::array<u8, 6> raw{};
+
+ friend bool operator==(const MacAddress& lhs, const MacAddress& rhs) = default;
+};
+static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size");
+
+struct ScanFilter {
+ NetworkId network_id;
+ NetworkType network_type;
+ MacAddress mac_address;
+ Ssid ssid;
+ INSERT_PADDING_BYTES(0x10);
+ ScanFilterFlag flag;
+};
+static_assert(sizeof(ScanFilter) == 0x60, "ScanFilter is an invalid size");
+
+struct CommonNetworkInfo {
+ MacAddress bssid;
+ Ssid ssid;
+ WifiChannel channel;
+ LinkLevel link_level;
+ PackedNetworkType network_type;
+ INSERT_PADDING_BYTES(0x4);
+};
+static_assert(sizeof(CommonNetworkInfo) == 0x30, "CommonNetworkInfo is an invalid size");
+
+struct NodeInfo {
+ Ipv4Address ipv4_address;
+ MacAddress mac_address;
+ s8 node_id;
+ u8 is_connected;
+ std::array<u8, UserNameBytesMax + 1> user_name;
+ INSERT_PADDING_BYTES(0x1); // Reserved
+ s16 local_communication_version;
+ INSERT_PADDING_BYTES(0x10); // Reserved
+};
+static_assert(sizeof(NodeInfo) == 0x40, "NodeInfo is an invalid size");
+
+struct LdnNetworkInfo {
+ std::array<u8, 0x10> security_parameter;
+ SecurityMode security_mode;
+ AcceptPolicy station_accept_policy;
+ u8 has_action_frame;
+ INSERT_PADDING_BYTES(0x2); // Padding
+ u8 node_count_max;
+ u8 node_count;
+ std::array<NodeInfo, NodeCountMax> nodes;
+ INSERT_PADDING_BYTES(0x2); // Reserved
+ u16 advertise_data_size;
+ std::array<u8, AdvertiseDataSizeMax> advertise_data;
+ INSERT_PADDING_BYTES(0x8C); // Reserved
+ u64 random_authentication_id;
+};
+static_assert(sizeof(LdnNetworkInfo) == 0x430, "LdnNetworkInfo is an invalid size");
+
+struct NetworkInfo {
+ NetworkId network_id;
+ CommonNetworkInfo common;
+ LdnNetworkInfo ldn;
+};
+static_assert(sizeof(NetworkInfo) == 0x480, "NetworkInfo is an invalid size");
+
+struct SecurityConfig {
+ SecurityMode security_mode;
+ u16 passphrase_size;
+ std::array<u8, PassphraseLengthMax> passphrase;
+};
+static_assert(sizeof(SecurityConfig) == 0x44, "SecurityConfig is an invalid size");
+
+struct UserConfig {
+ std::array<u8, UserNameBytesMax + 1> user_name;
+ INSERT_PADDING_BYTES(0xF); // Reserved
+};
+static_assert(sizeof(UserConfig) == 0x30, "UserConfig is an invalid size");
+
+#pragma pack(push, 4)
+struct ConnectRequest {
+ SecurityConfig security_config;
+ UserConfig user_config;
+ u32 local_communication_version;
+ u32 option_unknown;
+ NetworkInfo network_info;
+};
+static_assert(sizeof(ConnectRequest) == 0x4FC, "ConnectRequest is an invalid size");
+#pragma pack(pop)
+
+struct SecurityParameter {
+ std::array<u8, 0x10> data; // Data, used with the same key derivation as SecurityConfig
+ SessionId session_id;
+};
+static_assert(sizeof(SecurityParameter) == 0x20, "SecurityParameter is an invalid size");
+
+struct NetworkConfig {
+ IntentId intent_id;
+ WifiChannel channel;
+ u8 node_count_max;
+ INSERT_PADDING_BYTES(0x1); // Reserved
+ u16 local_communication_version;
+ INSERT_PADDING_BYTES(0xA); // Reserved
+};
+static_assert(sizeof(NetworkConfig) == 0x20, "NetworkConfig is an invalid size");
+
+struct AddressEntry {
+ Ipv4Address ipv4_address;
+ MacAddress mac_address;
+ INSERT_PADDING_BYTES(0x2); // Reserved
+};
+static_assert(sizeof(AddressEntry) == 0xC, "AddressEntry is an invalid size");
+
+struct AddressList {
+ std::array<AddressEntry, 0x8> addresses;
+};
+static_assert(sizeof(AddressList) == 0x60, "AddressList is an invalid size");
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 72e4902cb..becd6d1b9 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -20,20 +20,20 @@
namespace Service::LDR {
-constexpr ResultCode ERROR_INSUFFICIENT_ADDRESS_SPACE{ErrorModule::RO, 2};
-
-[[maybe_unused]] constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
-constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52};
-constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53};
-constexpr ResultCode ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54};
-constexpr ResultCode ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55};
-constexpr ResultCode ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56};
-constexpr ResultCode ERROR_ALREADY_LOADED{ErrorModule::Loader, 57};
-constexpr ResultCode ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81};
-constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::Loader, 82};
-constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84};
-[[maybe_unused]] constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
-constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
+constexpr Result ERROR_INSUFFICIENT_ADDRESS_SPACE{ErrorModule::RO, 2};
+
+[[maybe_unused]] constexpr Result ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
+constexpr Result ERROR_INVALID_NRO{ErrorModule::Loader, 52};
+constexpr Result ERROR_INVALID_NRR{ErrorModule::Loader, 53};
+constexpr Result ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54};
+constexpr Result ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55};
+constexpr Result ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56};
+constexpr Result ERROR_ALREADY_LOADED{ErrorModule::Loader, 57};
+constexpr Result ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81};
+constexpr Result ERROR_INVALID_SIZE{ErrorModule::Loader, 82};
+constexpr Result ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84};
+[[maybe_unused]] constexpr Result ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
+constexpr Result ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
constexpr std::size_t MAXIMUM_LOADED_RO{0x40};
constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200};
@@ -307,7 +307,7 @@ public:
return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize());
}
- ResultCode GetAvailableMapRegion(Kernel::KPageTable& page_table, u64 size, VAddr& out_addr) {
+ Result GetAvailableMapRegion(Kernel::KPageTable& page_table, u64 size, VAddr& out_addr) {
size = Common::AlignUp(size, Kernel::PageSize);
size += page_table.GetNumGuardPages() * Kernel::PageSize * 4;
@@ -364,7 +364,7 @@ public:
for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
R_TRY(GetAvailableMapRegion(page_table, size, addr));
- const ResultCode result{page_table.MapCodeMemory(addr, base_addr, size)};
+ const Result result{page_table.MapCodeMemory(addr, base_addr, size)};
if (result == Kernel::ResultInvalidCurrentMemory) {
continue;
}
@@ -397,8 +397,7 @@ public:
Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
});
- const ResultCode result{
- page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)};
+ const Result result{page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)};
if (result == Kernel::ResultInvalidCurrentMemory) {
continue;
@@ -419,8 +418,8 @@ public:
return ERROR_INSUFFICIENT_ADDRESS_SPACE;
}
- ResultCode LoadNro(Kernel::KProcess* process, const NROHeader& nro_header, VAddr nro_addr,
- VAddr start) const {
+ Result LoadNro(Kernel::KProcess* process, const NROHeader& nro_header, VAddr nro_addr,
+ VAddr start) const {
const VAddr text_start{start + nro_header.segment_headers[TEXT_INDEX].memory_offset};
const VAddr ro_start{start + nro_header.segment_headers[RO_INDEX].memory_offset};
const VAddr data_start{start + nro_header.segment_headers[DATA_INDEX].memory_offset};
@@ -569,7 +568,7 @@ public:
rb.Push(*map_result);
}
- ResultCode UnmapNro(const NROInfo& info) {
+ Result UnmapNro(const NROInfo& info) {
// Each region must be unmapped separately to validate memory state
auto& page_table{system.CurrentProcess()->PageTable()};
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp
index 41755bf0b..efb569993 100644
--- a/src/core/hle/service/mii/mii.cpp
+++ b/src/core/hle/service/mii/mii.cpp
@@ -12,7 +12,7 @@
namespace Service::Mii {
-constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1};
+constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1};
class IDatabaseService final : public ServiceFramework<IDatabaseService> {
public:
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
index 08300a1a6..544c92a00 100644
--- a/src/core/hle/service/mii/mii_manager.cpp
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -16,7 +16,7 @@ namespace Service::Mii {
namespace {
-constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
+constexpr Result ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
constexpr std::size_t BaseMiiCount{2};
constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()};
@@ -441,7 +441,7 @@ ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_
return result;
}
-ResultCode MiiManager::GetIndex([[maybe_unused]] const MiiInfo& info, u32& index) {
+Result MiiManager::GetIndex([[maybe_unused]] const MiiInfo& info, u32& index) {
constexpr u32 INVALID_INDEX{0xFFFFFFFF};
index = INVALID_INDEX;
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
index db217b9a5..6a286bd96 100644
--- a/src/core/hle/service/mii/mii_manager.h
+++ b/src/core/hle/service/mii/mii_manager.h
@@ -23,7 +23,7 @@ public:
MiiInfo BuildRandom(Age age, Gender gender, Race race);
MiiInfo BuildDefault(std::size_t index);
ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag);
- ResultCode GetIndex(const MiiInfo& info, u32& index);
+ Result GetIndex(const MiiInfo& info, u32& index);
private:
const Common::UUID user_id{};
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 74891da57..6c5b41dd1 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -17,10 +17,10 @@
namespace Service::NFP {
namespace ErrCodes {
-constexpr ResultCode DeviceNotFound(ErrorModule::NFP, 64);
-constexpr ResultCode WrongDeviceState(ErrorModule::NFP, 73);
-constexpr ResultCode ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
-constexpr ResultCode ApplicationAreaExist(ErrorModule::NFP, 168);
+constexpr Result DeviceNotFound(ErrorModule::NFP, 64);
+constexpr Result WrongDeviceState(ErrorModule::NFP, 73);
+constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
+constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168);
} // namespace ErrCodes
constexpr u32 ApplicationAreaSize = 0xD8;
@@ -585,7 +585,7 @@ void Module::Interface::Finalize() {
application_area_data.clear();
}
-ResultCode Module::Interface::StartDetection(s32 protocol_) {
+Result Module::Interface::StartDetection(s32 protocol_) {
auto npad_device = system.HIDCore().GetEmulatedController(npad_id);
// TODO(german77): Add callback for when nfc data is available
@@ -601,7 +601,7 @@ ResultCode Module::Interface::StartDetection(s32 protocol_) {
return ErrCodes::WrongDeviceState;
}
-ResultCode Module::Interface::StopDetection() {
+Result Module::Interface::StopDetection() {
auto npad_device = system.HIDCore().GetEmulatedController(npad_id);
npad_device->SetPollingMode(Common::Input::PollingMode::Active);
@@ -618,7 +618,7 @@ ResultCode Module::Interface::StopDetection() {
return ErrCodes::WrongDeviceState;
}
-ResultCode Module::Interface::Mount() {
+Result Module::Interface::Mount() {
if (device_state == DeviceState::TagFound) {
device_state = DeviceState::TagMounted;
return ResultSuccess;
@@ -628,7 +628,7 @@ ResultCode Module::Interface::Mount() {
return ErrCodes::WrongDeviceState;
}
-ResultCode Module::Interface::Unmount() {
+Result Module::Interface::Unmount() {
if (device_state == DeviceState::TagMounted) {
is_application_area_initialized = false;
application_area_id = 0;
@@ -641,7 +641,7 @@ ResultCode Module::Interface::Unmount() {
return ErrCodes::WrongDeviceState;
}
-ResultCode Module::Interface::GetTagInfo(TagInfo& tag_info) const {
+Result Module::Interface::GetTagInfo(TagInfo& tag_info) const {
if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) {
tag_info = {
.uuid = tag_data.uuid,
@@ -656,7 +656,7 @@ ResultCode Module::Interface::GetTagInfo(TagInfo& tag_info) const {
return ErrCodes::WrongDeviceState;
}
-ResultCode Module::Interface::GetCommonInfo(CommonInfo& common_info) const {
+Result Module::Interface::GetCommonInfo(CommonInfo& common_info) const {
if (device_state != DeviceState::TagMounted) {
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
return ErrCodes::WrongDeviceState;
@@ -674,7 +674,7 @@ ResultCode Module::Interface::GetCommonInfo(CommonInfo& common_info) const {
return ResultSuccess;
}
-ResultCode Module::Interface::GetModelInfo(ModelInfo& model_info) const {
+Result Module::Interface::GetModelInfo(ModelInfo& model_info) const {
if (device_state != DeviceState::TagMounted) {
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
return ErrCodes::WrongDeviceState;
@@ -684,7 +684,7 @@ ResultCode Module::Interface::GetModelInfo(ModelInfo& model_info) const {
return ResultSuccess;
}
-ResultCode Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const {
+Result Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const {
if (device_state != DeviceState::TagMounted) {
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
return ErrCodes::WrongDeviceState;
@@ -704,7 +704,7 @@ ResultCode Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const
return ResultSuccess;
}
-ResultCode Module::Interface::OpenApplicationArea(u32 access_id) {
+Result Module::Interface::OpenApplicationArea(u32 access_id) {
if (device_state != DeviceState::TagMounted) {
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
return ErrCodes::WrongDeviceState;
@@ -721,7 +721,7 @@ ResultCode Module::Interface::OpenApplicationArea(u32 access_id) {
return ResultSuccess;
}
-ResultCode Module::Interface::GetApplicationArea(std::vector<u8>& data) const {
+Result Module::Interface::GetApplicationArea(std::vector<u8>& data) const {
if (device_state != DeviceState::TagMounted) {
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
return ErrCodes::WrongDeviceState;
@@ -736,7 +736,7 @@ ResultCode Module::Interface::GetApplicationArea(std::vector<u8>& data) const {
return ResultSuccess;
}
-ResultCode Module::Interface::SetApplicationArea(const std::vector<u8>& data) {
+Result Module::Interface::SetApplicationArea(const std::vector<u8>& data) {
if (device_state != DeviceState::TagMounted) {
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
return ErrCodes::WrongDeviceState;
@@ -750,7 +750,7 @@ ResultCode Module::Interface::SetApplicationArea(const std::vector<u8>& data) {
return ResultSuccess;
}
-ResultCode Module::Interface::CreateApplicationArea(u32 access_id, const std::vector<u8>& data) {
+Result Module::Interface::CreateApplicationArea(u32 access_id, const std::vector<u8>& data) {
if (device_state != DeviceState::TagMounted) {
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
return ErrCodes::WrongDeviceState;
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index d307c6a35..0fc808781 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -178,20 +178,20 @@ public:
void Initialize();
void Finalize();
- ResultCode StartDetection(s32 protocol_);
- ResultCode StopDetection();
- ResultCode Mount();
- ResultCode Unmount();
-
- ResultCode GetTagInfo(TagInfo& tag_info) const;
- ResultCode GetCommonInfo(CommonInfo& common_info) const;
- ResultCode GetModelInfo(ModelInfo& model_info) const;
- ResultCode GetRegisterInfo(RegisterInfo& register_info) const;
-
- ResultCode OpenApplicationArea(u32 access_id);
- ResultCode GetApplicationArea(std::vector<u8>& data) const;
- ResultCode SetApplicationArea(const std::vector<u8>& data);
- ResultCode CreateApplicationArea(u32 access_id, const std::vector<u8>& data);
+ Result StartDetection(s32 protocol_);
+ Result StopDetection();
+ Result Mount();
+ Result Unmount();
+
+ Result GetTagInfo(TagInfo& tag_info) const;
+ Result GetCommonInfo(CommonInfo& common_info) const;
+ Result GetModelInfo(ModelInfo& model_info) const;
+ Result GetRegisterInfo(RegisterInfo& register_info) const;
+
+ Result OpenApplicationArea(u32 access_id);
+ Result GetApplicationArea(std::vector<u8>& data) const;
+ Result SetApplicationArea(const std::vector<u8>& data);
+ Result CreateApplicationArea(u32 access_id, const std::vector<u8>& data);
u64 GetHandle() const;
DeviceState GetCurrentState() const;
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 0310ce883..e3ef06481 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -6,7 +6,6 @@
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nifm/nifm.h"
-#include "core/hle/service/service.h"
namespace {
@@ -18,8 +17,8 @@ namespace {
} // Anonymous namespace
-#include "core/network/network.h"
-#include "core/network/network_interface.h"
+#include "core/internal_network/network.h"
+#include "core/internal_network/network_interface.h"
namespace Service::NIFM {
@@ -30,6 +29,19 @@ enum class RequestState : u32 {
Connected = 3,
};
+enum class InternetConnectionType : u8 {
+ WiFi = 1,
+ Ethernet = 2,
+};
+
+enum class InternetConnectionStatus : u8 {
+ ConnectingUnknown1,
+ ConnectingUnknown2,
+ ConnectingUnknown3,
+ ConnectingUnknown4,
+ Connected,
+};
+
struct IpAddressSetting {
bool is_automatic{};
Network::IPv4Address current_address{};
@@ -258,135 +270,45 @@ public:
}
};
-class IGeneralService final : public ServiceFramework<IGeneralService> {
-public:
- explicit IGeneralService(Core::System& system_);
-
-private:
- void GetClientId(Kernel::HLERequestContext& ctx) {
- static constexpr u32 client_id = 1;
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+void IGeneralService::GetClientId(Kernel::HLERequestContext& ctx) {
+ static constexpr u32 client_id = 1;
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
- }
- void CreateScanRequest(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NIFM, "called");
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
+}
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+void IGeneralService::CreateScanRequest(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIFM, "called");
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IScanRequest>(system);
- }
- void CreateRequest(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NIFM, "called");
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IScanRequest>(system);
+}
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IRequest>(system);
- }
- void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+void IGeneralService::CreateRequest(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIFM, "called");
- const auto net_iface = Network::GetSelectedNetworkInterface();
-
- const SfNetworkProfileData network_profile_data = [&net_iface] {
- if (!net_iface) {
- return SfNetworkProfileData{};
- }
-
- return SfNetworkProfileData{
- .ip_setting_data{
- .ip_address_setting{
- .is_automatic{true},
- .current_address{Network::TranslateIPv4(net_iface->ip_address)},
- .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
- .gateway{Network::TranslateIPv4(net_iface->gateway)},
- },
- .dns_setting{
- .is_automatic{true},
- .primary_dns{1, 1, 1, 1},
- .secondary_dns{1, 0, 0, 1},
- },
- .proxy_setting{
- .enabled{false},
- .port{},
- .proxy_server{},
- .automatic_auth_enabled{},
- .user{},
- .password{},
- },
- .mtu{1500},
- },
- .uuid{0xdeadbeef, 0xdeadbeef},
- .network_name{"yuzu Network"},
- .wireless_setting_data{
- .ssid_length{12},
- .ssid{"yuzu Network"},
- .passphrase{"yuzupassword"},
- },
- };
- }();
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- ctx.WriteBuffer(network_profile_data);
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IRequest>(system);
+}
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
- void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
- void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+ const auto net_iface = Network::GetSelectedNetworkInterface();
- auto ipv4 = Network::GetHostIPv4Address();
- if (!ipv4) {
- LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
- ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
+ SfNetworkProfileData network_profile_data = [&net_iface] {
+ if (!net_iface) {
+ return SfNetworkProfileData{};
}
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushRaw(*ipv4);
- }
- void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NIFM, "called");
-
- ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c,
- "SfNetworkProfileData is not the correct size");
- u128 uuid{};
- auto buffer = ctx.ReadBuffer();
- std::memcpy(&uuid, buffer.data() + 8, sizeof(u128));
-
- IPC::ResponseBuilder rb{ctx, 6, 0, 1};
-
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<INetworkProfile>(system);
- rb.PushRaw<u128>(uuid);
- }
- void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
-
- struct IpConfigInfo {
- IpAddressSetting ip_address_setting{};
- DnsSetting dns_setting{};
- };
- static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
- "IpConfigInfo has incorrect size.");
-
- const auto net_iface = Network::GetSelectedNetworkInterface();
-
- const IpConfigInfo ip_config_info = [&net_iface] {
- if (!net_iface) {
- return IpConfigInfo{};
- }
-
- return IpConfigInfo{
+ return SfNetworkProfileData{
+ .ip_setting_data{
.ip_address_setting{
.is_automatic{true},
.current_address{Network::TranslateIPv4(net_iface->ip_address)},
@@ -398,46 +320,178 @@ private:
.primary_dns{1, 1, 1, 1},
.secondary_dns{1, 0, 0, 1},
},
- };
- }();
+ .proxy_setting{
+ .enabled{false},
+ .port{},
+ .proxy_server{},
+ .automatic_auth_enabled{},
+ .user{},
+ .password{},
+ },
+ .mtu{1500},
+ },
+ .uuid{0xdeadbeef, 0xdeadbeef},
+ .network_name{"yuzu Network"},
+ .wireless_setting_data{
+ .ssid_length{12},
+ .ssid{"yuzu Network"},
+ .passphrase{"yuzupassword"},
+ },
+ };
+ }();
- IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
- rb.Push(ResultSuccess);
- rb.PushRaw<IpConfigInfo>(ip_config_info);
+ // When we're connected to a room, spoof the hosts IP address
+ if (auto room_member = network.GetRoomMember().lock()) {
+ if (room_member->IsConnected()) {
+ network_profile_data.ip_setting_data.ip_address_setting.current_address =
+ room_member->GetFakeIpAddress();
+ }
}
- void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u8>(0);
+ ctx.WriteBuffer(network_profile_data);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IGeneralService::RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IGeneralService::GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ auto ipv4 = Network::GetHostIPv4Address();
+ if (!ipv4) {
+ LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
+ ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
}
- void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- if (Network::GetHostIPv4Address().has_value()) {
- rb.Push<u8>(1);
- } else {
- rb.Push<u8>(0);
+ // When we're connected to a room, spoof the hosts IP address
+ if (auto room_member = network.GetRoomMember().lock()) {
+ if (room_member->IsConnected()) {
+ ipv4 = room_member->GetFakeIpAddress();
}
}
- void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- if (Network::GetHostIPv4Address().has_value()) {
- rb.Push<u8>(1);
- } else {
- rb.Push<u8>(0);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushRaw(*ipv4);
+}
+
+void IGeneralService::CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIFM, "called");
+
+ ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "SfNetworkProfileData is not the correct size");
+ u128 uuid{};
+ auto buffer = ctx.ReadBuffer();
+ std::memcpy(&uuid, buffer.data() + 8, sizeof(u128));
+
+ IPC::ResponseBuilder rb{ctx, 6, 0, 1};
+
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<INetworkProfile>(system);
+ rb.PushRaw<u128>(uuid);
+}
+
+void IGeneralService::GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ struct IpConfigInfo {
+ IpAddressSetting ip_address_setting{};
+ DnsSetting dns_setting{};
+ };
+ static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
+ "IpConfigInfo has incorrect size.");
+
+ const auto net_iface = Network::GetSelectedNetworkInterface();
+
+ IpConfigInfo ip_config_info = [&net_iface] {
+ if (!net_iface) {
+ return IpConfigInfo{};
+ }
+
+ return IpConfigInfo{
+ .ip_address_setting{
+ .is_automatic{true},
+ .current_address{Network::TranslateIPv4(net_iface->ip_address)},
+ .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
+ .gateway{Network::TranslateIPv4(net_iface->gateway)},
+ },
+ .dns_setting{
+ .is_automatic{true},
+ .primary_dns{1, 1, 1, 1},
+ .secondary_dns{1, 0, 0, 1},
+ },
+ };
+ }();
+
+ // When we're connected to a room, spoof the hosts IP address
+ if (auto room_member = network.GetRoomMember().lock()) {
+ if (room_member->IsConnected()) {
+ ip_config_info.ip_address_setting.current_address = room_member->GetFakeIpAddress();
}
}
-};
+
+ IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
+ rb.Push(ResultSuccess);
+ rb.PushRaw<IpConfigInfo>(ip_config_info);
+}
+
+void IGeneralService::IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u8>(1);
+}
+
+void IGeneralService::GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ struct Output {
+ InternetConnectionType type{InternetConnectionType::WiFi};
+ u8 wifi_strength{3};
+ InternetConnectionStatus state{InternetConnectionStatus::Connected};
+ };
+ static_assert(sizeof(Output) == 0x3, "Output has incorrect size.");
+
+ constexpr Output out{};
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushRaw(out);
+}
+
+void IGeneralService::IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ if (Network::GetHostIPv4Address().has_value()) {
+ rb.Push<u8>(1);
+ } else {
+ rb.Push<u8>(0);
+ }
+}
+
+void IGeneralService::IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
+ LOG_ERROR(Service_NIFM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ if (Network::GetHostIPv4Address().has_value()) {
+ rb.Push<u8>(1);
+ } else {
+ rb.Push<u8>(0);
+ }
+}
IGeneralService::IGeneralService(Core::System& system_)
- : ServiceFramework{system_, "IGeneralService"} {
+ : ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} {
// clang-format off
static const FunctionInfo functions[] = {
{1, &IGeneralService::GetClientId, "GetClientId"},
@@ -456,7 +510,7 @@ IGeneralService::IGeneralService(Core::System& system_)
{15, &IGeneralService::GetCurrentIpConfigInfo, "GetCurrentIpConfigInfo"},
{16, nullptr, "SetWirelessCommunicationEnabled"},
{17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"},
- {18, nullptr, "GetInternetConnectionStatus"},
+ {18, &IGeneralService::GetInternetConnectionStatus, "GetInternetConnectionStatus"},
{19, nullptr, "SetEthernetCommunicationEnabled"},
{20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"},
{21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"},
@@ -488,6 +542,8 @@ IGeneralService::IGeneralService(Core::System& system_)
RegisterHandlers(functions);
}
+IGeneralService::~IGeneralService() = default;
+
class NetworkInterface final : public ServiceFramework<NetworkInterface> {
public:
explicit NetworkInterface(const char* name, Core::System& system_)
diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h
index 5f62d0014..48161be28 100644
--- a/src/core/hle/service/nifm/nifm.h
+++ b/src/core/hle/service/nifm/nifm.h
@@ -3,6 +3,11 @@
#pragma once
+#include "core/hle/service/service.h"
+#include "network/network.h"
+#include "network/room.h"
+#include "network/room_member.h"
+
namespace Core {
class System;
}
@@ -16,4 +21,26 @@ namespace Service::NIFM {
/// Registers all NIFM services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
+class IGeneralService final : public ServiceFramework<IGeneralService> {
+public:
+ explicit IGeneralService(Core::System& system_);
+ ~IGeneralService() override;
+
+private:
+ void GetClientId(Kernel::HLERequestContext& ctx);
+ void CreateScanRequest(Kernel::HLERequestContext& ctx);
+ void CreateRequest(Kernel::HLERequestContext& ctx);
+ void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx);
+ void RemoveNetworkProfile(Kernel::HLERequestContext& ctx);
+ void GetCurrentIpAddress(Kernel::HLERequestContext& ctx);
+ void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx);
+ void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx);
+ void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx);
+ void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx);
+ void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx);
+ void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx);
+
+ Network::RoomNetwork& network;
+};
+
} // namespace Service::NIFM
diff --git a/src/core/hle/service/ns/errors.h b/src/core/hle/service/ns/errors.h
index 3c50c66e0..8a7621798 100644
--- a/src/core/hle/service/ns/errors.h
+++ b/src/core/hle/service/ns/errors.h
@@ -7,5 +7,5 @@
namespace Service::NS {
-constexpr ResultCode ERR_APPLICATION_LANGUAGE_NOT_FOUND{ErrorModule::NS, 300};
+constexpr Result ERR_APPLICATION_LANGUAGE_NOT_FOUND{ErrorModule::NS, 300};
} \ No newline at end of file
diff --git a/src/core/hle/service/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h
index 21aaa40cd..157333ff8 100644
--- a/src/core/hle/service/nvflinger/binder.h
+++ b/src/core/hle/service/nvflinger/binder.h
@@ -34,6 +34,7 @@ enum class TransactionId {
class IBinder {
public:
+ virtual ~IBinder() = default;
virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code,
u32 flags) = 0;
virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 2b2985a2d..5574269eb 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -67,21 +67,20 @@ NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_dr
// Schedule the screen composition events
composition_event = Core::Timing::CreateEvent(
- "ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) {
+ "ScreenComposition",
+ [this](std::uintptr_t, s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto lock_guard = Lock();
Compose();
- const auto ticks = std::chrono::nanoseconds{GetNextTicks()};
- const auto ticks_delta = ticks - ns_late;
- const auto future_ns = std::max(std::chrono::nanoseconds::zero(), ticks_delta);
-
- this->system.CoreTiming().ScheduleEvent(future_ns, composition_event);
+ return std::max(std::chrono::nanoseconds::zero(),
+ std::chrono::nanoseconds(GetNextTicks()) - ns_late);
});
if (system.IsMulticore()) {
vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); });
} else {
- system.CoreTiming().ScheduleEvent(frame_ns, composition_event);
+ system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, composition_event);
}
}
@@ -288,9 +287,21 @@ s64 NVFlinger::GetNextTicks() const {
static constexpr s64 max_hertz = 120LL;
const auto& settings = Settings::values;
- const bool unlocked_fps = settings.disable_fps_limit.GetValue();
- const s64 fps_cap = unlocked_fps ? static_cast<s64>(settings.fps_cap.GetValue()) : 1;
- return (1000000000 * (1LL << swap_interval)) / (max_hertz * fps_cap);
+ auto speed_scale = 1.f;
+ if (settings.use_multi_core.GetValue()) {
+ if (settings.use_speed_limit.GetValue()) {
+ // Scales the speed based on speed_limit setting on MC. SC is handled by
+ // SpeedLimiter::DoSpeedLimiting.
+ speed_scale = 100.f / settings.speed_limit.GetValue();
+ } else {
+ // Run at unlocked framerate.
+ speed_scale = 0.01f;
+ }
+ }
+
+ const auto next_ticks = ((1000000000 * (1LL << swap_interval)) / max_hertz);
+
+ return static_cast<s64>(speed_scale * static_cast<float>(next_ticks));
}
} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/pctl/pctl_module.cpp b/src/core/hle/service/pctl/pctl_module.cpp
index 8d5729003..2a123b42d 100644
--- a/src/core/hle/service/pctl/pctl_module.cpp
+++ b/src/core/hle/service/pctl/pctl_module.cpp
@@ -13,10 +13,10 @@ namespace Service::PCTL {
namespace Error {
-constexpr ResultCode ResultNoFreeCommunication{ErrorModule::PCTL, 101};
-constexpr ResultCode ResultStereoVisionRestricted{ErrorModule::PCTL, 104};
-constexpr ResultCode ResultNoCapability{ErrorModule::PCTL, 131};
-constexpr ResultCode ResultNoRestrictionEnabled{ErrorModule::PCTL, 181};
+constexpr Result ResultNoFreeCommunication{ErrorModule::PCTL, 101};
+constexpr Result ResultStereoVisionRestricted{ErrorModule::PCTL, 104};
+constexpr Result ResultNoCapability{ErrorModule::PCTL, 131};
+constexpr Result ResultNoRestrictionEnabled{ErrorModule::PCTL, 181};
} // namespace Error
diff --git a/src/core/hle/service/pcv/pcv.cpp b/src/core/hle/service/pcv/pcv.cpp
index 0989474be..f7a497a14 100644
--- a/src/core/hle/service/pcv/pcv.cpp
+++ b/src/core/hle/service/pcv/pcv.cpp
@@ -3,6 +3,7 @@
#include <memory>
+#include "core/hle/ipc_helpers.h"
#include "core/hle/service/pcv/pcv.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
@@ -77,10 +78,102 @@ public:
}
};
+class IClkrstSession final : public ServiceFramework<IClkrstSession> {
+public:
+ explicit IClkrstSession(Core::System& system_, DeviceCode deivce_code_)
+ : ServiceFramework{system_, "IClkrstSession"}, deivce_code(deivce_code_) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "SetClockEnabled"},
+ {1, nullptr, "SetClockDisabled"},
+ {2, nullptr, "SetResetAsserted"},
+ {3, nullptr, "SetResetDeasserted"},
+ {4, nullptr, "SetPowerEnabled"},
+ {5, nullptr, "SetPowerDisabled"},
+ {6, nullptr, "GetState"},
+ {7, &IClkrstSession::SetClockRate, "SetClockRate"},
+ {8, &IClkrstSession::GetClockRate, "GetClockRate"},
+ {9, nullptr, "SetMinVClockRate"},
+ {10, nullptr, "GetPossibleClockRates"},
+ {11, nullptr, "GetDvfsTable"},
+ };
+ // clang-format on
+ RegisterHandlers(functions);
+ }
+
+private:
+ void SetClockRate(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ clock_rate = rp.Pop<u32>();
+ LOG_DEBUG(Service_PCV, "(STUBBED) called, clock_rate={}", clock_rate);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void GetClockRate(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PCV, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u32>(clock_rate);
+ }
+
+ DeviceCode deivce_code;
+ u32 clock_rate{};
+};
+
+class CLKRST final : public ServiceFramework<CLKRST> {
+public:
+ explicit CLKRST(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &CLKRST::OpenSession, "OpenSession"},
+ {1, nullptr, "GetTemperatureThresholds"},
+ {2, nullptr, "SetTemperature"},
+ {3, nullptr, "GetModuleStateTable"},
+ {4, nullptr, "GetModuleStateTableEvent"},
+ {5, nullptr, "GetModuleStateTableMaxCount"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void OpenSession(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_code = static_cast<DeviceCode>(rp.Pop<u32>());
+ const auto unkonwn_input = rp.Pop<u32>();
+
+ LOG_DEBUG(Service_PCV, "called, device_code={}, input={}", device_code, unkonwn_input);
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IClkrstSession>(system, device_code);
+ }
+};
+
+class CLKRST_A final : public ServiceFramework<CLKRST_A> {
+public:
+ explicit CLKRST_A(Core::System& system_) : ServiceFramework{system_, "clkrst:a"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "ReleaseControl"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
std::make_shared<PCV>(system)->InstallAsService(sm);
std::make_shared<PCV_ARB>(system)->InstallAsService(sm);
std::make_shared<PCV_IMM>(system)->InstallAsService(sm);
+ std::make_shared<CLKRST>(system, "clkrst")->InstallAsService(sm);
+ std::make_shared<CLKRST>(system, "clkrst:i")->InstallAsService(sm);
+ std::make_shared<CLKRST_A>(system)->InstallAsService(sm);
}
} // namespace Service::PCV
diff --git a/src/core/hle/service/pcv/pcv.h b/src/core/hle/service/pcv/pcv.h
index a42e6f8f6..6b26b6fa7 100644
--- a/src/core/hle/service/pcv/pcv.h
+++ b/src/core/hle/service/pcv/pcv.h
@@ -13,6 +13,97 @@ class ServiceManager;
namespace Service::PCV {
+enum class DeviceCode : u32 {
+ Cpu = 0x40000001,
+ Gpu = 0x40000002,
+ I2s1 = 0x40000003,
+ I2s2 = 0x40000004,
+ I2s3 = 0x40000005,
+ Pwm = 0x40000006,
+ I2c1 = 0x02000001,
+ I2c2 = 0x02000002,
+ I2c3 = 0x02000003,
+ I2c4 = 0x02000004,
+ I2c5 = 0x02000005,
+ I2c6 = 0x02000006,
+ Spi1 = 0x07000000,
+ Spi2 = 0x07000001,
+ Spi3 = 0x07000002,
+ Spi4 = 0x07000003,
+ Disp1 = 0x40000011,
+ Disp2 = 0x40000012,
+ Isp = 0x40000013,
+ Vi = 0x40000014,
+ Sdmmc1 = 0x40000015,
+ Sdmmc2 = 0x40000016,
+ Sdmmc3 = 0x40000017,
+ Sdmmc4 = 0x40000018,
+ Owr = 0x40000019,
+ Csite = 0x4000001A,
+ Tsec = 0x4000001B,
+ Mselect = 0x4000001C,
+ Hda2codec2x = 0x4000001D,
+ Actmon = 0x4000001E,
+ I2cSlow = 0x4000001F,
+ Sor1 = 0x40000020,
+ Sata = 0x40000021,
+ Hda = 0x40000022,
+ XusbCoreHostSrc = 0x40000023,
+ XusbFalconSrc = 0x40000024,
+ XusbFsSrc = 0x40000025,
+ XusbCoreDevSrc = 0x40000026,
+ XusbSsSrc = 0x40000027,
+ UartA = 0x03000001,
+ UartB = 0x35000405,
+ UartC = 0x3500040F,
+ UartD = 0x37000001,
+ Host1x = 0x4000002C,
+ Entropy = 0x4000002D,
+ SocTherm = 0x4000002E,
+ Vic = 0x4000002F,
+ Nvenc = 0x40000030,
+ Nvjpg = 0x40000031,
+ Nvdec = 0x40000032,
+ Qspi = 0x40000033,
+ ViI2c = 0x40000034,
+ Tsecb = 0x40000035,
+ Ape = 0x40000036,
+ AudioDsp = 0x40000037,
+ AudioUart = 0x40000038,
+ Emc = 0x40000039,
+ Plle = 0x4000003A,
+ PlleHwSeq = 0x4000003B,
+ Dsi = 0x4000003C,
+ Maud = 0x4000003D,
+ Dpaux1 = 0x4000003E,
+ MipiCal = 0x4000003F,
+ UartFstMipiCal = 0x40000040,
+ Osc = 0x40000041,
+ SysBus = 0x40000042,
+ SorSafe = 0x40000043,
+ XusbSs = 0x40000044,
+ XusbHost = 0x40000045,
+ XusbDevice = 0x40000046,
+ Extperiph1 = 0x40000047,
+ Ahub = 0x40000048,
+ Hda2hdmicodec = 0x40000049,
+ Gpuaux = 0x4000004A,
+ UsbD = 0x4000004B,
+ Usb2 = 0x4000004C,
+ Pcie = 0x4000004D,
+ Afi = 0x4000004E,
+ PciExClk = 0x4000004F,
+ PExUsbPhy = 0x40000050,
+ XUsbPadCtl = 0x40000051,
+ Apbdma = 0x40000052,
+ Usb2TrkClk = 0x40000053,
+ XUsbIoPll = 0x40000054,
+ XUsbIoPllHwSeq = 0x40000055,
+ Cec = 0x40000056,
+ Extperiph2 = 0x40000057,
+ OscClk = 0x40000080
+};
+
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
} // namespace Service::PCV
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index a8e2a5cbd..b10e86c8f 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.cpp
@@ -12,12 +12,12 @@ namespace Service::PM {
namespace {
-constexpr ResultCode ResultProcessNotFound{ErrorModule::PM, 1};
-[[maybe_unused]] constexpr ResultCode ResultAlreadyStarted{ErrorModule::PM, 2};
-[[maybe_unused]] constexpr ResultCode ResultNotTerminated{ErrorModule::PM, 3};
-[[maybe_unused]] constexpr ResultCode ResultDebugHookInUse{ErrorModule::PM, 4};
-[[maybe_unused]] constexpr ResultCode ResultApplicationRunning{ErrorModule::PM, 5};
-[[maybe_unused]] constexpr ResultCode ResultInvalidSize{ErrorModule::PM, 6};
+constexpr Result ResultProcessNotFound{ErrorModule::PM, 1};
+[[maybe_unused]] constexpr Result ResultAlreadyStarted{ErrorModule::PM, 2};
+[[maybe_unused]] constexpr Result ResultNotTerminated{ErrorModule::PM, 3};
+[[maybe_unused]] constexpr Result ResultDebugHookInUse{ErrorModule::PM, 4};
+[[maybe_unused]] constexpr Result ResultApplicationRunning{ErrorModule::PM, 5};
+[[maybe_unused]] constexpr Result ResultInvalidSize{ErrorModule::PM, 6};
constexpr u64 NO_PROCESS_FOUND_PID{0};
diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp
index 9e0eb6ac0..2c31e9485 100644
--- a/src/core/hle/service/ptm/psm.cpp
+++ b/src/core/hle/service/ptm/psm.cpp
@@ -9,10 +9,8 @@
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/ptm/psm.h"
-#include "core/hle/service/service.h"
-#include "core/hle/service/sm/sm.h"
-namespace Service::PSM {
+namespace Service::PTM {
class IPsmSession final : public ServiceFramework<IPsmSession> {
public:
@@ -57,7 +55,7 @@ public:
private:
void BindStateChangeEvent(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+ LOG_DEBUG(Service_PTM, "called");
should_signal = true;
@@ -67,7 +65,7 @@ private:
}
void UnbindStateChangeEvent(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+ LOG_DEBUG(Service_PTM, "called");
should_signal = false;
@@ -78,7 +76,7 @@ private:
void SetChargerTypeChangeEventEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto state = rp.Pop<bool>();
- LOG_DEBUG(Service_PSM, "called, state={}", state);
+ LOG_DEBUG(Service_PTM, "called, state={}", state);
should_signal_charger_type = state;
@@ -89,7 +87,7 @@ private:
void SetPowerSupplyChangeEventEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto state = rp.Pop<bool>();
- LOG_DEBUG(Service_PSM, "called, state={}", state);
+ LOG_DEBUG(Service_PTM, "called, state={}", state);
should_signal_power_supply = state;
@@ -100,7 +98,7 @@ private:
void SetBatteryVoltageStateChangeEventEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto state = rp.Pop<bool>();
- LOG_DEBUG(Service_PSM, "called, state={}", state);
+ LOG_DEBUG(Service_PTM, "called, state={}", state);
should_signal_battery_voltage = state;
@@ -117,76 +115,58 @@ private:
Kernel::KEvent* state_change_event;
};
-class PSM final : public ServiceFramework<PSM> {
-public:
- explicit PSM(Core::System& system_) : ServiceFramework{system_, "psm"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"},
- {1, &PSM::GetChargerType, "GetChargerType"},
- {2, nullptr, "EnableBatteryCharging"},
- {3, nullptr, "DisableBatteryCharging"},
- {4, nullptr, "IsBatteryChargingEnabled"},
- {5, nullptr, "AcquireControllerPowerSupply"},
- {6, nullptr, "ReleaseControllerPowerSupply"},
- {7, &PSM::OpenSession, "OpenSession"},
- {8, nullptr, "EnableEnoughPowerChargeEmulation"},
- {9, nullptr, "DisableEnoughPowerChargeEmulation"},
- {10, nullptr, "EnableFastBatteryCharging"},
- {11, nullptr, "DisableFastBatteryCharging"},
- {12, nullptr, "GetBatteryVoltageState"},
- {13, nullptr, "GetRawBatteryChargePercentage"},
- {14, nullptr, "IsEnoughPowerSupplied"},
- {15, nullptr, "GetBatteryAgePercentage"},
- {16, nullptr, "GetBatteryChargeInfoEvent"},
- {17, nullptr, "GetBatteryChargeInfoFields"},
- {18, nullptr, "GetBatteryChargeCalibratedEvent"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
- ~PSM() override = default;
-
-private:
- void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+PSM::PSM(Core::System& system_) : ServiceFramework{system_, "psm"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"},
+ {1, &PSM::GetChargerType, "GetChargerType"},
+ {2, nullptr, "EnableBatteryCharging"},
+ {3, nullptr, "DisableBatteryCharging"},
+ {4, nullptr, "IsBatteryChargingEnabled"},
+ {5, nullptr, "AcquireControllerPowerSupply"},
+ {6, nullptr, "ReleaseControllerPowerSupply"},
+ {7, &PSM::OpenSession, "OpenSession"},
+ {8, nullptr, "EnableEnoughPowerChargeEmulation"},
+ {9, nullptr, "DisableEnoughPowerChargeEmulation"},
+ {10, nullptr, "EnableFastBatteryCharging"},
+ {11, nullptr, "DisableFastBatteryCharging"},
+ {12, nullptr, "GetBatteryVoltageState"},
+ {13, nullptr, "GetRawBatteryChargePercentage"},
+ {14, nullptr, "IsEnoughPowerSupplied"},
+ {15, nullptr, "GetBatteryAgePercentage"},
+ {16, nullptr, "GetBatteryChargeInfoEvent"},
+ {17, nullptr, "GetBatteryChargeInfoFields"},
+ {18, nullptr, "GetBatteryChargeCalibratedEvent"},
+ };
+ // clang-format on
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(battery_charge_percentage);
- }
+ RegisterHandlers(functions);
+}
- void GetChargerType(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+PSM::~PSM() = default;
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(charger_type);
- }
+void PSM::GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PTM, "called");
- void OpenSession(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u32>(battery_charge_percentage);
+}
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IPsmSession>(system);
- }
+void PSM::GetChargerType(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PTM, "called");
- enum class ChargerType : u32 {
- Unplugged = 0,
- RegularCharger = 1,
- LowPowerCharger = 2,
- Unknown = 3,
- };
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushEnum(charger_type);
+}
- u32 battery_charge_percentage{100}; // 100%
- ChargerType charger_type{ChargerType::RegularCharger};
-};
+void PSM::OpenSession(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PTM, "called");
-void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
- std::make_shared<PSM>(system)->InstallAsService(sm);
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IPsmSession>(system);
}
-} // namespace Service::PSM
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/psm.h b/src/core/hle/service/ptm/psm.h
index 94a1044db..f674ba8bc 100644
--- a/src/core/hle/service/ptm/psm.h
+++ b/src/core/hle/service/ptm/psm.h
@@ -3,16 +3,29 @@
#pragma once
-namespace Core {
-class System;
-}
+#include "core/hle/service/service.h"
-namespace Service::SM {
-class ServiceManager;
-}
+namespace Service::PTM {
-namespace Service::PSM {
+class PSM final : public ServiceFramework<PSM> {
+public:
+ explicit PSM(Core::System& system_);
+ ~PSM() override;
-void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
+private:
+ enum class ChargerType : u32 {
+ Unplugged = 0,
+ RegularCharger = 1,
+ LowPowerCharger = 2,
+ Unknown = 3,
+ };
-} // namespace Service::PSM
+ void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx);
+ void GetChargerType(Kernel::HLERequestContext& ctx);
+ void OpenSession(Kernel::HLERequestContext& ctx);
+
+ u32 battery_charge_percentage{100};
+ ChargerType charger_type{ChargerType::RegularCharger};
+};
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
new file mode 100644
index 000000000..4bea995c6
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -0,0 +1,18 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <memory>
+
+#include "core/core.h"
+#include "core/hle/service/ptm/psm.h"
+#include "core/hle/service/ptm/ptm.h"
+#include "core/hle/service/ptm/ts.h"
+
+namespace Service::PTM {
+
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
+ std::make_shared<PSM>(system)->InstallAsService(sm);
+ std::make_shared<TS>(system)->InstallAsService(sm);
+}
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h
new file mode 100644
index 000000000..06224a24e
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm.h
@@ -0,0 +1,18 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+namespace Core {
+class System;
+}
+
+namespace Service::SM {
+class ServiceManager;
+}
+
+namespace Service::PTM {
+
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ts.cpp b/src/core/hle/service/ptm/ts.cpp
new file mode 100644
index 000000000..65c3f135f
--- /dev/null
+++ b/src/core/hle/service/ptm/ts.cpp
@@ -0,0 +1,41 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <memory>
+
+#include "core/core.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/ptm/ts.h"
+
+namespace Service::PTM {
+
+TS::TS(Core::System& system_) : ServiceFramework{system_, "ts"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetTemperatureRange"},
+ {1, &TS::GetTemperature, "GetTemperature"},
+ {2, nullptr, "SetMeasurementMode"},
+ {3, nullptr, "GetTemperatureMilliC"},
+ {4, nullptr, "OpenSession"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+TS::~TS() = default;
+
+void TS::GetTemperature(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto location{rp.PopEnum<Location>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called. location={}", location);
+
+ const s32 temperature = location == Location::Internal ? 35 : 20;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(temperature);
+}
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ts.h b/src/core/hle/service/ptm/ts.h
new file mode 100644
index 000000000..39a734ef7
--- /dev/null
+++ b/src/core/hle/service/ptm/ts.h
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::PTM {
+
+class TS final : public ServiceFramework<TS> {
+public:
+ explicit TS(Core::System& system_);
+ ~TS() override;
+
+private:
+ enum class Location : u8 {
+ Internal,
+ External,
+ };
+
+ void GetTemperature(Kernel::HLERequestContext& ctx);
+};
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 574272b0c..dadaf897f 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -58,7 +58,7 @@
#include "core/hle/service/pm/pm.h"
#include "core/hle/service/prepo/prepo.h"
#include "core/hle/service/psc/psc.h"
-#include "core/hle/service/ptm/psm.h"
+#include "core/hle/service/ptm/ptm.h"
#include "core/hle/service/service.h"
#include "core/hle/service/set/settings.h"
#include "core/hle/service/sm/sm.h"
@@ -190,17 +190,20 @@ void ServiceFrameworkBase::InvokeRequestTipc(Kernel::HLERequestContext& ctx) {
handler_invoker(this, info->handler_callback, ctx);
}
-ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
- Kernel::HLERequestContext& ctx) {
+Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
+ Kernel::HLERequestContext& ctx) {
const auto guard = LockService();
+ Result result = ResultSuccess;
+
switch (ctx.GetCommandType()) {
case IPC::CommandType::Close:
case IPC::CommandType::TIPC_Close: {
session.Close();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
- return IPC::ERR_REMOTE_PROCESS_DEAD;
+ result = IPC::ERR_REMOTE_PROCESS_DEAD;
+ break;
}
case IPC::CommandType::ControlWithContext:
case IPC::CommandType::Control: {
@@ -227,7 +230,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& sessi
ctx.WriteToOutgoingCommandBuffer(ctx.GetThread());
}
- return ResultSuccess;
+ return result;
}
/// Initialize Services
@@ -287,7 +290,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
PlayReport::InstallInterfaces(*sm, system);
PM::InstallInterfaces(system);
PSC::InstallInterfaces(*sm, system);
- PSM::InstallInterfaces(*sm, system);
+ PTM::InstallInterfaces(*sm, system);
Set::InstallInterfaces(*sm, system);
Sockets::InstallInterfaces(*sm, system);
SPL::InstallInterfaces(*sm, system);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index f23e0cd64..5bf197c51 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -79,8 +79,8 @@ public:
Kernel::KClientPort& CreatePort();
/// Handles a synchronization request for the service.
- ResultCode HandleSyncRequest(Kernel::KServerSession& session,
- Kernel::HLERequestContext& context) override;
+ Result HandleSyncRequest(Kernel::KServerSession& session,
+ Kernel::HLERequestContext& context) override;
protected:
/// Member-function pointer type of SyncRequest handlers.
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index 2839cffcf..f761c2da4 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -74,7 +74,7 @@ constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> language_to_la
constexpr std::size_t PRE_4_0_0_MAX_ENTRIES = 0xF;
constexpr std::size_t POST_4_0_0_MAX_ENTRIES = 0x40;
-constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625};
+constexpr Result ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625};
void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_language_codes) {
IPC::ResponseBuilder rb{ctx, 3};
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index 87c6f7f85..2a0b812c1 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -32,7 +32,7 @@ void GetFirmwareVersionImpl(Kernel::HLERequestContext& ctx, GetFirmwareVersionTy
// consistence (currently reports as 5.1.0-0.0)
const auto archive = FileSys::SystemArchive::SystemVersion();
- const auto early_exit_failure = [&ctx](std::string_view desc, ResultCode code) {
+ const auto early_exit_failure = [&ctx](std::string_view desc, Result code) {
LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).",
desc);
IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 925608875..246c94623 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -17,10 +17,10 @@
namespace Service::SM {
-constexpr ResultCode ERR_NOT_INITIALIZED(ErrorModule::SM, 2);
-constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::SM, 4);
-constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6);
-constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7);
+constexpr Result ERR_NOT_INITIALIZED(ErrorModule::SM, 2);
+constexpr Result ERR_ALREADY_REGISTERED(ErrorModule::SM, 4);
+constexpr Result ERR_INVALID_NAME(ErrorModule::SM, 6);
+constexpr Result ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7);
ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {}
ServiceManager::~ServiceManager() = default;
@@ -29,7 +29,7 @@ void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
controller_interface->InvokeRequest(context);
}
-static ResultCode ValidateServiceName(const std::string& name) {
+static Result ValidateServiceName(const std::string& name) {
if (name.empty() || name.size() > 8) {
LOG_ERROR(Service_SM, "Invalid service name! service={}", name);
return ERR_INVALID_NAME;
@@ -43,8 +43,8 @@ Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core
return self.sm_interface->CreatePort();
}
-ResultCode ServiceManager::RegisterService(std::string name, u32 max_sessions,
- Kernel::SessionRequestHandlerPtr handler) {
+Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
+ Kernel::SessionRequestHandlerPtr handler) {
CASCADE_CODE(ValidateServiceName(name));
@@ -58,7 +58,7 @@ ResultCode ServiceManager::RegisterService(std::string name, u32 max_sessions,
return ResultSuccess;
}
-ResultCode ServiceManager::UnregisterService(const std::string& name) {
+Result ServiceManager::UnregisterService(const std::string& name) {
CASCADE_CODE(ValidateServiceName(name));
const auto iter = registered_services.find(name);
@@ -94,7 +94,7 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name
* Inputs:
* 0: 0x00000000
* Outputs:
- * 0: ResultCode
+ * 0: Result
*/
void SM::Initialize(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SM, "called");
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index 43d445e97..878decc6f 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -55,9 +55,9 @@ public:
explicit ServiceManager(Kernel::KernelCore& kernel_);
~ServiceManager();
- ResultCode RegisterService(std::string name, u32 max_sessions,
- Kernel::SessionRequestHandlerPtr handler);
- ResultCode UnregisterService(const std::string& name);
+ Result RegisterService(std::string name, u32 max_sessions,
+ Kernel::SessionRequestHandlerPtr handler);
+ Result UnregisterService(const std::string& name);
ResultVal<Kernel::KPort*> GetServicePort(const std::string& name);
template <Common::DerivedFrom<Kernel::SessionRequestHandler> T>
diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp
index a4ed4193e..2a4bd64ab 100644
--- a/src/core/hle/service/sm/sm_controller.cpp
+++ b/src/core/hle/service/sm/sm_controller.cpp
@@ -33,7 +33,7 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
// Create a session.
Kernel::KClientSession* session{};
- const ResultCode result = parent_port.CreateSession(std::addressof(session), session_manager);
+ const Result result = parent_port.CreateSession(std::addressof(session), session_manager);
if (result.IsError()) {
LOG_CRITICAL(Service, "CreateSession failed with error 0x{:08X}", result.raw);
IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 5114b8be2..e08c3cb67 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -9,12 +9,16 @@
#include <fmt/format.h>
#include "common/microprofile.h"
+#include "common/socket_types.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/service/sockets/bsd.h"
#include "core/hle/service/sockets/sockets_translate.h"
-#include "core/network/network.h"
-#include "core/network/sockets.h"
+#include "core/internal_network/network.h"
+#include "core/internal_network/socket_proxy.h"
+#include "core/internal_network/sockets.h"
+#include "network/network.h"
namespace Service::Sockets {
@@ -472,7 +476,13 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
LOG_INFO(Service, "New socket fd={}", fd);
- descriptor.socket = std::make_unique<Network::Socket>();
+ auto room_member = room_network.GetRoomMember().lock();
+ if (room_member && room_member->IsConnected()) {
+ descriptor.socket = std::make_unique<Network::ProxySocket>(room_network);
+ } else {
+ descriptor.socket = std::make_unique<Network::Socket>();
+ }
+
descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol));
descriptor.is_connection_based = IsConnectionBased(type);
@@ -648,7 +658,7 @@ std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
ASSERT(arg == 0);
return {descriptor.flags, Errno::SUCCESS};
case FcntlCmd::SETFL: {
- const bool enable = (arg & FLAG_O_NONBLOCK) != 0;
+ const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0;
const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
if (bsd_errno != Errno::SUCCESS) {
return {-1, bsd_errno};
@@ -669,7 +679,7 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con
return Errno::BADF;
}
- Network::Socket* const socket = file_descriptors[fd]->socket.get();
+ Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
if (optname == OptName::LINGER) {
ASSERT(optlen == sizeof(Linger));
@@ -720,7 +730,27 @@ std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message)
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
}
- return Translate(file_descriptors[fd]->socket->Recv(flags, message));
+
+ FileDescriptor& descriptor = *file_descriptors[fd];
+
+ // Apply flags
+ using Network::FLAG_MSG_DONTWAIT;
+ using Network::FLAG_O_NONBLOCK;
+ if ((flags & FLAG_MSG_DONTWAIT) != 0) {
+ flags &= ~FLAG_MSG_DONTWAIT;
+ if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
+ descriptor.socket->SetNonBlock(true);
+ }
+ }
+
+ const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message));
+
+ // Restore original state
+ if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
+ descriptor.socket->SetNonBlock(false);
+ }
+
+ return {ret, bsd_errno};
}
std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
@@ -741,6 +771,8 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
}
// Apply flags
+ using Network::FLAG_MSG_DONTWAIT;
+ using Network::FLAG_O_NONBLOCK;
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
flags &= ~FLAG_MSG_DONTWAIT;
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
@@ -839,8 +871,19 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co
rb.PushEnum(bsd_errno);
}
+void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
+ for (auto& optional_descriptor : file_descriptors) {
+ if (!optional_descriptor.has_value()) {
+ continue;
+ }
+ FileDescriptor& descriptor = *optional_descriptor;
+ descriptor.socket.get()->HandleProxyPacket(packet);
+ }
+}
+
BSD::BSD(Core::System& system_, const char* name)
- : ServiceFramework{system_, name, ServiceThreadType::CreateNew} {
+ : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, room_network{
+ system_.GetRoomNetwork()} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &BSD::RegisterClient, "RegisterClient"},
@@ -881,6 +924,13 @@ BSD::BSD(Core::System& system_, const char* name)
// clang-format on
RegisterHandlers(functions);
+
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ proxy_packet_received = room_member->BindOnProxyPacketReceived(
+ [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); });
+ } else {
+ LOG_ERROR(Service, "Network isn't initalized");
+ }
}
BSD::~BSD() = default;
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index fed740d87..81e855e0f 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -7,16 +7,19 @@
#include <vector>
#include "common/common_types.h"
+#include "common/socket_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sockets/sockets.h"
+#include "network/network.h"
namespace Core {
class System;
}
namespace Network {
+class SocketBase;
class Socket;
-}
+} // namespace Network
namespace Service::Sockets {
@@ -30,7 +33,7 @@ private:
static constexpr size_t MAX_FD = 128;
struct FileDescriptor {
- std::unique_ptr<Network::Socket> socket;
+ std::unique_ptr<Network::SocketBase> socket;
s32 flags = 0;
bool is_connection_based = false;
};
@@ -165,6 +168,14 @@ private:
void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept;
std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors;
+
+ Network::RoomNetwork& room_network;
+
+ /// Callback to parse and handle a received wifi packet.
+ void OnProxyPacketReceived(const Network::ProxyPacket& packet);
+
+ // Callback identifier for the OnProxyPacketReceived event.
+ Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received;
};
class BSDCFG final : public ServiceFramework<BSDCFG> {
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h
index b735b00fc..31b7dad33 100644
--- a/src/core/hle/service/sockets/sockets.h
+++ b/src/core/hle/service/sockets/sockets.h
@@ -22,7 +22,9 @@ enum class Errno : u32 {
AGAIN = 11,
INVAL = 22,
MFILE = 24,
+ MSGSIZE = 90,
NOTCONN = 107,
+ TIMEDOUT = 110,
};
enum class Domain : u32 {
@@ -96,10 +98,6 @@ struct Linger {
u32 linger;
};
-constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
-
-constexpr u32 FLAG_O_NONBLOCK = 0x800;
-
/// Registers all Sockets services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp
index 9c0936d97..023aa0486 100644
--- a/src/core/hle/service/sockets/sockets_translate.cpp
+++ b/src/core/hle/service/sockets/sockets_translate.cpp
@@ -7,7 +7,7 @@
#include "common/common_types.h"
#include "core/hle/service/sockets/sockets.h"
#include "core/hle/service/sockets/sockets_translate.h"
-#include "core/network/network.h"
+#include "core/internal_network/network.h"
namespace Service::Sockets {
@@ -25,6 +25,8 @@ Errno Translate(Network::Errno value) {
return Errno::MFILE;
case Network::Errno::NOTCONN:
return Errno::NOTCONN;
+ case Network::Errno::TIMEDOUT:
+ return Errno::TIMEDOUT;
default:
UNIMPLEMENTED_MSG("Unimplemented errno={}", value);
return Errno::SUCCESS;
diff --git a/src/core/hle/service/sockets/sockets_translate.h b/src/core/hle/service/sockets/sockets_translate.h
index 5e9809add..c93291d3e 100644
--- a/src/core/hle/service/sockets/sockets_translate.h
+++ b/src/core/hle/service/sockets/sockets_translate.h
@@ -7,7 +7,7 @@
#include "common/common_types.h"
#include "core/hle/service/sockets/sockets.h"
-#include "core/network/network.h"
+#include "core/internal_network/network.h"
namespace Service::Sockets {
diff --git a/src/core/hle/service/spl/spl_results.h b/src/core/hle/service/spl/spl_results.h
index 17ef655a9..dd7ba11f3 100644
--- a/src/core/hle/service/spl/spl_results.h
+++ b/src/core/hle/service/spl/spl_results.h
@@ -8,23 +8,23 @@
namespace Service::SPL {
// Description 0 - 99
-constexpr ResultCode ResultSecureMonitorError{ErrorModule::SPL, 0};
-constexpr ResultCode ResultSecureMonitorNotImplemented{ErrorModule::SPL, 1};
-constexpr ResultCode ResultSecureMonitorInvalidArgument{ErrorModule::SPL, 2};
-constexpr ResultCode ResultSecureMonitorBusy{ErrorModule::SPL, 3};
-constexpr ResultCode ResultSecureMonitorNoAsyncOperation{ErrorModule::SPL, 4};
-constexpr ResultCode ResultSecureMonitorInvalidAsyncOperation{ErrorModule::SPL, 5};
-constexpr ResultCode ResultSecureMonitorNotPermitted{ErrorModule::SPL, 6};
-constexpr ResultCode ResultSecureMonitorNotInitialized{ErrorModule::SPL, 7};
+constexpr Result ResultSecureMonitorError{ErrorModule::SPL, 0};
+constexpr Result ResultSecureMonitorNotImplemented{ErrorModule::SPL, 1};
+constexpr Result ResultSecureMonitorInvalidArgument{ErrorModule::SPL, 2};
+constexpr Result ResultSecureMonitorBusy{ErrorModule::SPL, 3};
+constexpr Result ResultSecureMonitorNoAsyncOperation{ErrorModule::SPL, 4};
+constexpr Result ResultSecureMonitorInvalidAsyncOperation{ErrorModule::SPL, 5};
+constexpr Result ResultSecureMonitorNotPermitted{ErrorModule::SPL, 6};
+constexpr Result ResultSecureMonitorNotInitialized{ErrorModule::SPL, 7};
-constexpr ResultCode ResultInvalidSize{ErrorModule::SPL, 100};
-constexpr ResultCode ResultUnknownSecureMonitorError{ErrorModule::SPL, 101};
-constexpr ResultCode ResultDecryptionFailed{ErrorModule::SPL, 102};
+constexpr Result ResultInvalidSize{ErrorModule::SPL, 100};
+constexpr Result ResultUnknownSecureMonitorError{ErrorModule::SPL, 101};
+constexpr Result ResultDecryptionFailed{ErrorModule::SPL, 102};
-constexpr ResultCode ResultOutOfKeySlots{ErrorModule::SPL, 104};
-constexpr ResultCode ResultInvalidKeySlot{ErrorModule::SPL, 105};
-constexpr ResultCode ResultBootReasonAlreadySet{ErrorModule::SPL, 106};
-constexpr ResultCode ResultBootReasonNotSet{ErrorModule::SPL, 107};
-constexpr ResultCode ResultInvalidArgument{ErrorModule::SPL, 108};
+constexpr Result ResultOutOfKeySlots{ErrorModule::SPL, 104};
+constexpr Result ResultInvalidKeySlot{ErrorModule::SPL, 105};
+constexpr Result ResultBootReasonAlreadySet{ErrorModule::SPL, 106};
+constexpr Result ResultBootReasonNotSet{ErrorModule::SPL, 107};
+constexpr Result ResultInvalidArgument{ErrorModule::SPL, 108};
} // namespace Service::SPL
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h
index d0af06d94..ef070f32f 100644
--- a/src/core/hle/service/time/clock_types.h
+++ b/src/core/hle/service/time/clock_types.h
@@ -22,7 +22,7 @@ struct SteadyClockTimePoint {
s64 time_point;
Common::UUID clock_source_id;
- ResultCode GetSpanBetween(SteadyClockTimePoint other, s64& span) const {
+ Result GetSpanBetween(SteadyClockTimePoint other, s64& span) const {
span = 0;
if (clock_source_id != other.clock_source_id) {
@@ -92,9 +92,9 @@ struct ClockSnapshot {
TimeType type;
INSERT_PADDING_BYTES_NOINIT(0x2);
- static ResultCode GetCurrentTime(s64& current_time,
- const SteadyClockTimePoint& steady_clock_time_point,
- const SystemClockContext& context) {
+ static Result GetCurrentTime(s64& current_time,
+ const SteadyClockTimePoint& steady_clock_time_point,
+ const SystemClockContext& context) {
if (steady_clock_time_point.clock_source_id != context.steady_time_point.clock_source_id) {
current_time = 0;
return ERROR_TIME_MISMATCH;
diff --git a/src/core/hle/service/time/errors.h b/src/core/hle/service/time/errors.h
index 592921f6b..6655d30e1 100644
--- a/src/core/hle/service/time/errors.h
+++ b/src/core/hle/service/time/errors.h
@@ -7,15 +7,15 @@
namespace Service::Time {
-constexpr ResultCode ERROR_PERMISSION_DENIED{ErrorModule::Time, 1};
-constexpr ResultCode ERROR_TIME_MISMATCH{ErrorModule::Time, 102};
-constexpr ResultCode ERROR_UNINITIALIZED_CLOCK{ErrorModule::Time, 103};
-constexpr ResultCode ERROR_TIME_NOT_FOUND{ErrorModule::Time, 200};
-constexpr ResultCode ERROR_OVERFLOW{ErrorModule::Time, 201};
-constexpr ResultCode ERROR_LOCATION_NAME_TOO_LONG{ErrorModule::Time, 801};
-constexpr ResultCode ERROR_OUT_OF_RANGE{ErrorModule::Time, 902};
-constexpr ResultCode ERROR_TIME_ZONE_CONVERSION_FAILED{ErrorModule::Time, 903};
-constexpr ResultCode ERROR_TIME_ZONE_NOT_FOUND{ErrorModule::Time, 989};
-constexpr ResultCode ERROR_NOT_IMPLEMENTED{ErrorModule::Time, 990};
+constexpr Result ERROR_PERMISSION_DENIED{ErrorModule::Time, 1};
+constexpr Result ERROR_TIME_MISMATCH{ErrorModule::Time, 102};
+constexpr Result ERROR_UNINITIALIZED_CLOCK{ErrorModule::Time, 103};
+constexpr Result ERROR_TIME_NOT_FOUND{ErrorModule::Time, 200};
+constexpr Result ERROR_OVERFLOW{ErrorModule::Time, 201};
+constexpr Result ERROR_LOCATION_NAME_TOO_LONG{ErrorModule::Time, 801};
+constexpr Result ERROR_OUT_OF_RANGE{ErrorModule::Time, 902};
+constexpr Result ERROR_TIME_ZONE_CONVERSION_FAILED{ErrorModule::Time, 903};
+constexpr Result ERROR_TIME_ZONE_NOT_FOUND{ErrorModule::Time, 989};
+constexpr Result ERROR_NOT_IMPLEMENTED{ErrorModule::Time, 990};
} // namespace Service::Time
diff --git a/src/core/hle/service/time/local_system_clock_context_writer.h b/src/core/hle/service/time/local_system_clock_context_writer.h
index 0977806b3..1639ef2b9 100644
--- a/src/core/hle/service/time/local_system_clock_context_writer.h
+++ b/src/core/hle/service/time/local_system_clock_context_writer.h
@@ -14,7 +14,7 @@ public:
: SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {}
protected:
- ResultCode Update() override {
+ Result Update() override {
shared_memory.UpdateLocalSystemClockContext(context);
return ResultSuccess;
}
diff --git a/src/core/hle/service/time/network_system_clock_context_writer.h b/src/core/hle/service/time/network_system_clock_context_writer.h
index 975089af8..655e4c06d 100644
--- a/src/core/hle/service/time/network_system_clock_context_writer.h
+++ b/src/core/hle/service/time/network_system_clock_context_writer.h
@@ -15,7 +15,7 @@ public:
: SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {}
protected:
- ResultCode Update() override {
+ Result Update() override {
shared_memory.UpdateNetworkSystemClockContext(context);
return ResultSuccess;
}
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.cpp b/src/core/hle/service/time/standard_user_system_clock_core.cpp
index 508091dc2..b033757ed 100644
--- a/src/core/hle/service/time/standard_user_system_clock_core.cpp
+++ b/src/core/hle/service/time/standard_user_system_clock_core.cpp
@@ -27,9 +27,9 @@ StandardUserSystemClockCore::~StandardUserSystemClockCore() {
service_context.CloseEvent(auto_correction_event);
}
-ResultCode StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system,
- bool value) {
- if (const ResultCode result{ApplyAutomaticCorrection(system, value)}; result != ResultSuccess) {
+Result StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system,
+ bool value) {
+ if (const Result result{ApplyAutomaticCorrection(system, value)}; result != ResultSuccess) {
return result;
}
@@ -38,27 +38,27 @@ ResultCode StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::Syst
return ResultSuccess;
}
-ResultCode StandardUserSystemClockCore::GetClockContext(Core::System& system,
- SystemClockContext& ctx) const {
- if (const ResultCode result{ApplyAutomaticCorrection(system, false)}; result != ResultSuccess) {
+Result StandardUserSystemClockCore::GetClockContext(Core::System& system,
+ SystemClockContext& ctx) const {
+ if (const Result result{ApplyAutomaticCorrection(system, false)}; result != ResultSuccess) {
return result;
}
return local_system_clock_core.GetClockContext(system, ctx);
}
-ResultCode StandardUserSystemClockCore::Flush(const SystemClockContext&) {
+Result StandardUserSystemClockCore::Flush(const SystemClockContext&) {
UNIMPLEMENTED();
return ERROR_NOT_IMPLEMENTED;
}
-ResultCode StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) {
+Result StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) {
UNIMPLEMENTED();
return ERROR_NOT_IMPLEMENTED;
}
-ResultCode StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& system,
- bool value) const {
+Result StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& system,
+ bool value) const {
if (auto_correction_enabled == value) {
return ResultSuccess;
}
@@ -68,7 +68,7 @@ ResultCode StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& s
}
SystemClockContext ctx{};
- if (const ResultCode result{network_system_clock_core.GetClockContext(system, ctx)};
+ if (const Result result{network_system_clock_core.GetClockContext(system, ctx)};
result != ResultSuccess) {
return result;
}
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.h b/src/core/hle/service/time/standard_user_system_clock_core.h
index 22df23b29..ee6e29487 100644
--- a/src/core/hle/service/time/standard_user_system_clock_core.h
+++ b/src/core/hle/service/time/standard_user_system_clock_core.h
@@ -28,9 +28,9 @@ public:
~StandardUserSystemClockCore() override;
- ResultCode SetAutomaticCorrectionEnabled(Core::System& system, bool value);
+ Result SetAutomaticCorrectionEnabled(Core::System& system, bool value);
- ResultCode GetClockContext(Core::System& system, SystemClockContext& ctx) const override;
+ Result GetClockContext(Core::System& system, SystemClockContext& ctx) const override;
bool IsAutomaticCorrectionEnabled() const {
return auto_correction_enabled;
@@ -41,11 +41,11 @@ public:
}
protected:
- ResultCode Flush(const SystemClockContext&) override;
+ Result Flush(const SystemClockContext&) override;
- ResultCode SetClockContext(const SystemClockContext&) override;
+ Result SetClockContext(const SystemClockContext&) override;
- ResultCode ApplyAutomaticCorrection(Core::System& system, bool value) const;
+ Result ApplyAutomaticCorrection(Core::System& system, bool value) const;
const SteadyClockTimePoint& GetAutomaticCorrectionUpdatedTime() const {
return auto_correction_time;
diff --git a/src/core/hle/service/time/system_clock_context_update_callback.cpp b/src/core/hle/service/time/system_clock_context_update_callback.cpp
index 37c140c6f..a649bed3a 100644
--- a/src/core/hle/service/time/system_clock_context_update_callback.cpp
+++ b/src/core/hle/service/time/system_clock_context_update_callback.cpp
@@ -30,8 +30,8 @@ void SystemClockContextUpdateCallback::BroadcastOperationEvent() {
}
}
-ResultCode SystemClockContextUpdateCallback::Update(const SystemClockContext& value) {
- ResultCode result{ResultSuccess};
+Result SystemClockContextUpdateCallback::Update(const SystemClockContext& value) {
+ Result result{ResultSuccess};
if (NeedUpdate(value)) {
context = value;
@@ -47,7 +47,7 @@ ResultCode SystemClockContextUpdateCallback::Update(const SystemClockContext& va
return result;
}
-ResultCode SystemClockContextUpdateCallback::Update() {
+Result SystemClockContextUpdateCallback::Update() {
return ResultSuccess;
}
diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h
index bee90e329..9c6caf196 100644
--- a/src/core/hle/service/time/system_clock_context_update_callback.h
+++ b/src/core/hle/service/time/system_clock_context_update_callback.h
@@ -28,10 +28,10 @@ public:
void BroadcastOperationEvent();
- ResultCode Update(const SystemClockContext& value);
+ Result Update(const SystemClockContext& value);
protected:
- virtual ResultCode Update();
+ virtual Result Update();
SystemClockContext context{};
diff --git a/src/core/hle/service/time/system_clock_core.cpp b/src/core/hle/service/time/system_clock_core.cpp
index cb132239c..da078241f 100644
--- a/src/core/hle/service/time/system_clock_core.cpp
+++ b/src/core/hle/service/time/system_clock_core.cpp
@@ -14,13 +14,13 @@ SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core_)
SystemClockCore::~SystemClockCore() = default;
-ResultCode SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const {
+Result SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const {
posix_time = 0;
const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
SystemClockContext clock_context{};
- if (const ResultCode result{GetClockContext(system, clock_context)}; result != ResultSuccess) {
+ if (const Result result{GetClockContext(system, clock_context)}; result != ResultSuccess) {
return result;
}
@@ -33,26 +33,26 @@ ResultCode SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time
return ResultSuccess;
}
-ResultCode SystemClockCore::SetCurrentTime(Core::System& system, s64 posix_time) {
+Result SystemClockCore::SetCurrentTime(Core::System& system, s64 posix_time) {
const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
const SystemClockContext clock_context{posix_time - current_time_point.time_point,
current_time_point};
- if (const ResultCode result{SetClockContext(clock_context)}; result != ResultSuccess) {
+ if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) {
return result;
}
return Flush(clock_context);
}
-ResultCode SystemClockCore::Flush(const SystemClockContext& clock_context) {
+Result SystemClockCore::Flush(const SystemClockContext& clock_context) {
if (!system_clock_context_update_callback) {
return ResultSuccess;
}
return system_clock_context_update_callback->Update(clock_context);
}
-ResultCode SystemClockCore::SetSystemClockContext(const SystemClockContext& clock_context) {
- if (const ResultCode result{SetClockContext(clock_context)}; result != ResultSuccess) {
+Result SystemClockCore::SetSystemClockContext(const SystemClockContext& clock_context) {
+ if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) {
return result;
}
return Flush(clock_context);
diff --git a/src/core/hle/service/time/system_clock_core.h b/src/core/hle/service/time/system_clock_core.h
index 76d82f976..8cb34126f 100644
--- a/src/core/hle/service/time/system_clock_core.h
+++ b/src/core/hle/service/time/system_clock_core.h
@@ -29,28 +29,28 @@ public:
return steady_clock_core;
}
- ResultCode GetCurrentTime(Core::System& system, s64& posix_time) const;
+ Result GetCurrentTime(Core::System& system, s64& posix_time) const;
- ResultCode SetCurrentTime(Core::System& system, s64 posix_time);
+ Result SetCurrentTime(Core::System& system, s64 posix_time);
- virtual ResultCode GetClockContext([[maybe_unused]] Core::System& system,
- SystemClockContext& value) const {
+ virtual Result GetClockContext([[maybe_unused]] Core::System& system,
+ SystemClockContext& value) const {
value = context;
return ResultSuccess;
}
- virtual ResultCode SetClockContext(const SystemClockContext& value) {
+ virtual Result SetClockContext(const SystemClockContext& value) {
context = value;
return ResultSuccess;
}
- virtual ResultCode Flush(const SystemClockContext& clock_context);
+ virtual Result Flush(const SystemClockContext& clock_context);
void SetUpdateCallbackInstance(std::shared_ptr<SystemClockContextUpdateCallback> callback) {
system_clock_context_update_callback = std::move(callback);
}
- ResultCode SetSystemClockContext(const SystemClockContext& context);
+ Result SetSystemClockContext(const SystemClockContext& context);
bool IsInitialized() const {
return is_initialized;
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 095fa021c..f77cdbb43 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -43,8 +43,7 @@ private:
}
s64 posix_time{};
- if (const ResultCode result{clock_core.GetCurrentTime(system, posix_time)};
- result.IsError()) {
+ if (const Result result{clock_core.GetCurrentTime(system, posix_time)}; result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
@@ -65,7 +64,7 @@ private:
}
Clock::SystemClockContext system_clock_context{};
- if (const ResultCode result{clock_core.GetClockContext(system, system_clock_context)};
+ if (const Result result{clock_core.GetClockContext(system, system_clock_context)};
result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
@@ -116,7 +115,7 @@ private:
Clock::SteadyClockCore& clock_core;
};
-ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
+Result Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
Kernel::KThread* thread, Clock::SystemClockContext user_context,
Clock::SystemClockContext network_context, Clock::TimeType type,
Clock::ClockSnapshot& clock_snapshot) {
@@ -129,7 +128,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled();
clock_snapshot.type = type;
- if (const ResultCode result{
+ if (const Result result{
time_manager.GetTimeZoneContentManager().GetTimeZoneManager().GetDeviceLocationName(
clock_snapshot.location_name)};
result != ResultSuccess) {
@@ -138,7 +137,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
clock_snapshot.user_context = user_context;
- if (const ResultCode result{Clock::ClockSnapshot::GetCurrentTime(
+ if (const Result result{Clock::ClockSnapshot::GetCurrentTime(
clock_snapshot.user_time, clock_snapshot.steady_clock_time_point,
clock_snapshot.user_context)};
result != ResultSuccess) {
@@ -146,7 +145,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
}
TimeZone::CalendarInfo userCalendarInfo{};
- if (const ResultCode result{
+ if (const Result result{
time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
clock_snapshot.user_time, userCalendarInfo)};
result != ResultSuccess) {
@@ -165,7 +164,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
}
TimeZone::CalendarInfo networkCalendarInfo{};
- if (const ResultCode result{
+ if (const Result result{
time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
clock_snapshot.network_time, networkCalendarInfo)};
result != ResultSuccess) {
@@ -262,7 +261,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called, type={}", type);
Clock::SystemClockContext user_context{};
- if (const ResultCode result{
+ if (const Result result{
system.GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(system,
user_context)};
result.IsError()) {
@@ -272,7 +271,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
}
Clock::SystemClockContext network_context{};
- if (const ResultCode result{
+ if (const Result result{
system.GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
system, network_context)};
result.IsError()) {
@@ -282,7 +281,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
}
Clock::ClockSnapshot clock_snapshot{};
- if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal(
+ if (const Result result{GetClockSnapshotFromSystemClockContextInternal(
&ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -308,7 +307,7 @@ void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLEReques
LOG_DEBUG(Service_Time, "called, type={}", type);
Clock::ClockSnapshot clock_snapshot{};
- if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal(
+ if (const Result result{GetClockSnapshotFromSystemClockContextInternal(
&ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -365,7 +364,7 @@ void Module::Interface::CalculateSpanBetween(Kernel::HLERequestContext& ctx) {
Clock::TimeSpanType time_span_type{};
s64 span{};
- if (const ResultCode result{snapshot_a.steady_clock_time_point.GetSpanBetween(
+ if (const Result result{snapshot_a.steady_clock_time_point.GetSpanBetween(
snapshot_b.steady_clock_time_point, span)};
result != ResultSuccess) {
if (snapshot_a.network_time && snapshot_b.network_time) {
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index 017f20a23..76a46cfc7 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -36,7 +36,7 @@ public:
void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx);
private:
- ResultCode GetClockSnapshotFromSystemClockContextInternal(
+ Result GetClockSnapshotFromSystemClockContextInternal(
Kernel::KThread* thread, Clock::SystemClockContext user_context,
Clock::SystemClockContext network_context, Clock::TimeType type,
Clock::ClockSnapshot& cloc_snapshot);
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
index 80818eb70..afbfe9715 100644
--- a/src/core/hle/service/time/time_zone_content_manager.cpp
+++ b/src/core/hle/service/time/time_zone_content_manager.cpp
@@ -90,10 +90,10 @@ void TimeZoneContentManager::Initialize(TimeManager& time_manager) {
}
}
-ResultCode TimeZoneContentManager::LoadTimeZoneRule(TimeZoneRule& rules,
- const std::string& location_name) const {
+Result TimeZoneContentManager::LoadTimeZoneRule(TimeZoneRule& rules,
+ const std::string& location_name) const {
FileSys::VirtualFile vfs_file;
- if (const ResultCode result{GetTimeZoneInfoFile(location_name, vfs_file)};
+ if (const Result result{GetTimeZoneInfoFile(location_name, vfs_file)};
result != ResultSuccess) {
return result;
}
@@ -106,8 +106,8 @@ bool TimeZoneContentManager::IsLocationNameValid(const std::string& location_nam
location_name_cache.end();
}
-ResultCode TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_name,
- FileSys::VirtualFile& vfs_file) const {
+Result TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_name,
+ FileSys::VirtualFile& vfs_file) const {
if (!IsLocationNameValid(location_name)) {
return ERROR_TIME_NOT_FOUND;
}
diff --git a/src/core/hle/service/time/time_zone_content_manager.h b/src/core/hle/service/time/time_zone_content_manager.h
index c6c94bcc0..3d94b6428 100644
--- a/src/core/hle/service/time/time_zone_content_manager.h
+++ b/src/core/hle/service/time/time_zone_content_manager.h
@@ -32,12 +32,12 @@ public:
return time_zone_manager;
}
- ResultCode LoadTimeZoneRule(TimeZoneRule& rules, const std::string& location_name) const;
+ Result LoadTimeZoneRule(TimeZoneRule& rules, const std::string& location_name) const;
private:
bool IsLocationNameValid(const std::string& location_name) const;
- ResultCode GetTimeZoneInfoFile(const std::string& location_name,
- FileSys::VirtualFile& vfs_file) const;
+ Result GetTimeZoneInfoFile(const std::string& location_name,
+ FileSys::VirtualFile& vfs_file) const;
Core::System& system;
TimeZoneManager time_zone_manager;
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp
index fee05ec7a..2aa675df9 100644
--- a/src/core/hle/service/time/time_zone_manager.cpp
+++ b/src/core/hle/service/time/time_zone_manager.cpp
@@ -666,8 +666,8 @@ static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFi
return true;
}
-static ResultCode CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& calendar_time,
- CalendarAdditionalInfo& calendar_additional_info) {
+static Result CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& calendar_time,
+ CalendarAdditionalInfo& calendar_additional_info) {
s64 year{epoch_year};
s64 time_days{time / seconds_per_day};
s64 remaining_seconds{time % seconds_per_day};
@@ -741,9 +741,9 @@ static ResultCode CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInter
return ResultSuccess;
}
-static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
- CalendarTimeInternal& calendar_time,
- CalendarAdditionalInfo& calendar_additional_info) {
+static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
+ CalendarTimeInternal& calendar_time,
+ CalendarAdditionalInfo& calendar_additional_info) {
if ((rules.go_ahead && time < rules.ats[0]) ||
(rules.go_back && time > rules.ats[rules.time_count - 1])) {
s64 seconds{};
@@ -766,7 +766,7 @@ static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
if (new_time < rules.ats[0] && new_time > rules.ats[rules.time_count - 1]) {
return ERROR_TIME_NOT_FOUND;
}
- if (const ResultCode result{
+ if (const Result result{
ToCalendarTimeInternal(rules, new_time, calendar_time, calendar_additional_info)};
result != ResultSuccess) {
return result;
@@ -797,8 +797,8 @@ static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
tti_index = rules.types[low - 1];
}
- if (const ResultCode result{CreateCalendarTime(time, rules.ttis[tti_index].gmt_offset,
- calendar_time, calendar_additional_info)};
+ if (const Result result{CreateCalendarTime(time, rules.ttis[tti_index].gmt_offset,
+ calendar_time, calendar_additional_info)};
result != ResultSuccess) {
return result;
}
@@ -811,9 +811,9 @@ static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
return ResultSuccess;
}
-static ResultCode ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) {
+static Result ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) {
CalendarTimeInternal calendar_time{};
- const ResultCode result{
+ const Result result{
ToCalendarTimeInternal(rules, time, calendar_time, calendar.additional_info)};
calendar.time.year = static_cast<s16>(calendar_time.year);
@@ -830,13 +830,13 @@ static ResultCode ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, Calend
TimeZoneManager::TimeZoneManager() = default;
TimeZoneManager::~TimeZoneManager() = default;
-ResultCode TimeZoneManager::ToCalendarTime(const TimeZoneRule& rules, s64 time,
- CalendarInfo& calendar) const {
+Result TimeZoneManager::ToCalendarTime(const TimeZoneRule& rules, s64 time,
+ CalendarInfo& calendar) const {
return ToCalendarTimeImpl(rules, time, calendar);
}
-ResultCode TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
- FileSys::VirtualFile& vfs_file) {
+Result TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
+ FileSys::VirtualFile& vfs_file) {
TimeZoneRule rule{};
if (ParseTimeZoneBinary(rule, vfs_file)) {
device_location_name = location_name;
@@ -846,12 +846,12 @@ ResultCode TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::str
return ERROR_TIME_ZONE_CONVERSION_FAILED;
}
-ResultCode TimeZoneManager::SetUpdatedTime(const Clock::SteadyClockTimePoint& value) {
+Result TimeZoneManager::SetUpdatedTime(const Clock::SteadyClockTimePoint& value) {
time_zone_update_time_point = value;
return ResultSuccess;
}
-ResultCode TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const {
+Result TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const {
if (is_initialized) {
return ToCalendarTime(time_zone_rule, time, calendar);
} else {
@@ -859,16 +859,16 @@ ResultCode TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& ca
}
}
-ResultCode TimeZoneManager::ParseTimeZoneRuleBinary(TimeZoneRule& rules,
- FileSys::VirtualFile& vfs_file) const {
+Result TimeZoneManager::ParseTimeZoneRuleBinary(TimeZoneRule& rules,
+ FileSys::VirtualFile& vfs_file) const {
if (!ParseTimeZoneBinary(rules, vfs_file)) {
return ERROR_TIME_ZONE_CONVERSION_FAILED;
}
return ResultSuccess;
}
-ResultCode TimeZoneManager::ToPosixTime(const TimeZoneRule& rules,
- const CalendarTime& calendar_time, s64& posix_time) const {
+Result TimeZoneManager::ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time,
+ s64& posix_time) const {
posix_time = 0;
CalendarTimeInternal internal_time{
@@ -1020,8 +1020,8 @@ ResultCode TimeZoneManager::ToPosixTime(const TimeZoneRule& rules,
return ResultSuccess;
}
-ResultCode TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_time,
- s64& posix_time) const {
+Result TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_time,
+ s64& posix_time) const {
if (is_initialized) {
return ToPosixTime(time_zone_rule, calendar_time, posix_time);
}
@@ -1029,7 +1029,7 @@ ResultCode TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_t
return ERROR_UNINITIALIZED_CLOCK;
}
-ResultCode TimeZoneManager::GetDeviceLocationName(LocationName& value) const {
+Result TimeZoneManager::GetDeviceLocationName(LocationName& value) const {
if (!is_initialized) {
return ERROR_UNINITIALIZED_CLOCK;
}
diff --git a/src/core/hle/service/time/time_zone_manager.h b/src/core/hle/service/time/time_zone_manager.h
index 8c1b19f81..5ebd4035e 100644
--- a/src/core/hle/service/time/time_zone_manager.h
+++ b/src/core/hle/service/time/time_zone_manager.h
@@ -29,16 +29,16 @@ public:
is_initialized = true;
}
- ResultCode SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
- FileSys::VirtualFile& vfs_file);
- ResultCode SetUpdatedTime(const Clock::SteadyClockTimePoint& value);
- ResultCode GetDeviceLocationName(TimeZone::LocationName& value) const;
- ResultCode ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const;
- ResultCode ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const;
- ResultCode ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const;
- ResultCode ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time,
- s64& posix_time) const;
- ResultCode ToPosixTimeWithMyRule(const CalendarTime& calendar_time, s64& posix_time) const;
+ Result SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
+ FileSys::VirtualFile& vfs_file);
+ Result SetUpdatedTime(const Clock::SteadyClockTimePoint& value);
+ Result GetDeviceLocationName(TimeZone::LocationName& value) const;
+ Result ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const;
+ Result ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const;
+ Result ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const;
+ Result ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time,
+ s64& posix_time) const;
+ Result ToPosixTimeWithMyRule(const CalendarTime& calendar_time, s64& posix_time) const;
private:
bool is_initialized{};
diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp
index cbf0ef6fa..961040bfc 100644
--- a/src/core/hle/service/time/time_zone_service.cpp
+++ b/src/core/hle/service/time/time_zone_service.cpp
@@ -32,7 +32,7 @@ void ITimeZoneService::GetDeviceLocationName(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
TimeZone::LocationName location_name{};
- if (const ResultCode result{
+ if (const Result result{
time_zone_content_manager.GetTimeZoneManager().GetDeviceLocationName(location_name)};
result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -61,7 +61,7 @@ void ITimeZoneService::LoadTimeZoneRule(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called, location_name={}", location_name);
TimeZone::TimeZoneRule time_zone_rule{};
- if (const ResultCode result{
+ if (const Result result{
time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)};
result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -88,7 +88,7 @@ void ITimeZoneService::ToCalendarTime(Kernel::HLERequestContext& ctx) {
std::memcpy(&time_zone_rule, buffer.data(), buffer.size());
TimeZone::CalendarInfo calendar_info{};
- if (const ResultCode result{time_zone_content_manager.GetTimeZoneManager().ToCalendarTime(
+ if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToCalendarTime(
time_zone_rule, posix_time, calendar_info)};
result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -108,7 +108,7 @@ void ITimeZoneService::ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx)
LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time);
TimeZone::CalendarInfo calendar_info{};
- if (const ResultCode result{
+ if (const Result result{
time_zone_content_manager.GetTimeZoneManager().ToCalendarTimeWithMyRules(
posix_time, calendar_info)};
result != ResultSuccess) {
@@ -131,7 +131,7 @@ void ITimeZoneService::ToPosixTime(Kernel::HLERequestContext& ctx) {
std::memcpy(&time_zone_rule, ctx.ReadBuffer().data(), sizeof(TimeZone::TimeZoneRule));
s64 posix_time{};
- if (const ResultCode result{time_zone_content_manager.GetTimeZoneManager().ToPosixTime(
+ if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTime(
time_zone_rule, calendar_time, posix_time)};
result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -154,9 +154,8 @@ void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()};
s64 posix_time{};
- if (const ResultCode result{
- time_zone_content_manager.GetTimeZoneManager().ToPosixTimeWithMyRule(calendar_time,
- posix_time)};
+ if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTimeWithMyRule(
+ calendar_time, posix_time)};
result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index a7b53d7dc..546879648 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -34,10 +34,10 @@
namespace Service::VI {
-constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1};
-constexpr ResultCode ERR_PERMISSION_DENIED{ErrorModule::VI, 5};
-constexpr ResultCode ERR_UNSUPPORTED{ErrorModule::VI, 6};
-constexpr ResultCode ERR_NOT_FOUND{ErrorModule::VI, 7};
+constexpr Result ERR_OPERATION_FAILED{ErrorModule::VI, 1};
+constexpr Result ERR_PERMISSION_DENIED{ErrorModule::VI, 5};
+constexpr Result ERR_UNSUPPORTED{ErrorModule::VI, 6};
+constexpr Result ERR_NOT_FOUND{ErrorModule::VI, 7};
struct DisplayInfo {
/// The name of this particular display.
diff --git a/src/core/network/network.cpp b/src/core/internal_network/network.cpp
index fdafbea92..cdf38a2a4 100644
--- a/src/core/network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -29,9 +29,10 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/settings.h"
-#include "core/network/network.h"
-#include "core/network/network_interface.h"
-#include "core/network/sockets.h"
+#include "core/internal_network/network.h"
+#include "core/internal_network/network_interface.h"
+#include "core/internal_network/sockets.h"
+#include "network/network.h"
namespace Network {
@@ -114,7 +115,10 @@ Errno TranslateNativeError(int e) {
return Errno::NETDOWN;
case WSAENETUNREACH:
return Errno::NETUNREACH;
+ case WSAEMSGSIZE:
+ return Errno::MSGSIZE;
default:
+ UNIMPLEMENTED_MSG("Unimplemented errno={}", e);
return Errno::OTHER;
}
}
@@ -125,7 +129,6 @@ using SOCKET = int;
using WSAPOLLFD = pollfd;
using ULONG = u64;
-constexpr SOCKET INVALID_SOCKET = -1;
constexpr SOCKET SOCKET_ERROR = -1;
constexpr int SD_RECEIVE = SHUT_RD;
@@ -206,7 +209,10 @@ Errno TranslateNativeError(int e) {
return Errno::NETDOWN;
case ENETUNREACH:
return Errno::NETUNREACH;
+ case EMSGSIZE:
+ return Errno::MSGSIZE;
default:
+ UNIMPLEMENTED_MSG("Unimplemented errno={}", e);
return Errno::OTHER;
}
}
@@ -329,16 +335,6 @@ PollEvents TranslatePollRevents(short revents) {
return result;
}
-template <typename T>
-Errno SetSockOpt(SOCKET fd, int option, T value) {
- const int result =
- setsockopt(fd, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value));
- if (result != SOCKET_ERROR) {
- return Errno::SUCCESS;
- }
- return GetAndLogLastError();
-}
-
} // Anonymous namespace
NetworkInstance::NetworkInstance() {
@@ -350,26 +346,16 @@ NetworkInstance::~NetworkInstance() {
}
std::optional<IPv4Address> GetHostIPv4Address() {
- const std::string& selected_network_interface = Settings::values.network_interface.GetValue();
- const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
- if (network_interfaces.size() == 0) {
- LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
+ const auto network_interface = Network::GetSelectedNetworkInterface();
+ if (!network_interface.has_value()) {
+ LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface");
return {};
}
- const auto res =
- std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) {
- return iface.name == selected_network_interface;
- });
-
- if (res != network_interfaces.end()) {
- char ip_addr[16] = {};
- ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr);
- return TranslateIPv4(res->ip_address);
- } else {
- LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
- return {};
- }
+ std::array<char, 16> ip_addr = {};
+ ASSERT(inet_ntop(AF_INET, &network_interface->ip_address, ip_addr.data(), sizeof(ip_addr)) !=
+ nullptr);
+ return TranslateIPv4(network_interface->ip_address);
}
std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
@@ -412,7 +398,19 @@ Socket::~Socket() {
fd = INVALID_SOCKET;
}
-Socket::Socket(Socket&& rhs) noexcept : fd{std::exchange(rhs.fd, INVALID_SOCKET)} {}
+Socket::Socket(Socket&& rhs) noexcept {
+ fd = std::exchange(rhs.fd, INVALID_SOCKET);
+}
+
+template <typename T>
+Errno Socket::SetSockOpt(SOCKET fd_, int option, T value) {
+ const int result =
+ setsockopt(fd_, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value));
+ if (result != SOCKET_ERROR) {
+ return Errno::SUCCESS;
+ }
+ return GetAndLogLastError();
+}
Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol));
@@ -423,7 +421,7 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
return GetAndLogLastError();
}
-std::pair<Socket::AcceptResult, Errno> Socket::Accept() {
+std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() {
sockaddr addr;
socklen_t addrlen = sizeof(addr);
const SOCKET new_socket = accept(fd, &addr, &addrlen);
@@ -634,4 +632,8 @@ bool Socket::IsOpened() const {
return fd != INVALID_SOCKET;
}
+void Socket::HandleProxyPacket(const ProxyPacket& packet) {
+ LOG_WARNING(Network, "ProxyPacket received, but not in Proxy mode!");
+}
+
} // namespace Network
diff --git a/src/core/network/network.h b/src/core/internal_network/network.h
index 10e5ef10d..36994c22e 100644
--- a/src/core/network/network.h
+++ b/src/core/internal_network/network.h
@@ -8,6 +8,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
+#include "common/socket_types.h"
#ifdef _WIN32
#include <winsock2.h>
@@ -17,6 +18,7 @@
namespace Network {
+class SocketBase;
class Socket;
/// Error code for network functions
@@ -31,46 +33,11 @@ enum class Errno {
HOSTUNREACH,
NETDOWN,
NETUNREACH,
+ TIMEDOUT,
+ MSGSIZE,
OTHER,
};
-/// Address families
-enum class Domain {
- INET, ///< Address family for IPv4
-};
-
-/// Socket types
-enum class Type {
- STREAM,
- DGRAM,
- RAW,
- SEQPACKET,
-};
-
-/// Protocol values for sockets
-enum class Protocol {
- ICMP,
- TCP,
- UDP,
-};
-
-/// Shutdown mode
-enum class ShutdownHow {
- RD,
- WR,
- RDWR,
-};
-
-/// Array of IPv4 address
-using IPv4Address = std::array<u8, 4>;
-
-/// Cross-platform sockaddr structure
-struct SockAddrIn {
- Domain family;
- IPv4Address ip;
- u16 portno;
-};
-
/// Cross-platform poll fd structure
enum class PollEvents : u16 {
@@ -86,7 +53,7 @@ enum class PollEvents : u16 {
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
struct PollFD {
- Socket* socket;
+ SocketBase* socket;
PollEvents events;
PollEvents revents;
};
diff --git a/src/core/network/network_interface.cpp b/src/core/internal_network/network_interface.cpp
index 15ecc6abf..0f0a66160 100644
--- a/src/core/network/network_interface.cpp
+++ b/src/core/internal_network/network_interface.cpp
@@ -11,7 +11,7 @@
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/string_util.h"
-#include "core/network/network_interface.h"
+#include "core/internal_network/network_interface.h"
#ifdef _WIN32
#include <iphlpapi.h>
diff --git a/src/core/network/network_interface.h b/src/core/internal_network/network_interface.h
index 9b98b6b42..9b98b6b42 100644
--- a/src/core/network/network_interface.h
+++ b/src/core/internal_network/network_interface.h
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp
new file mode 100644
index 000000000..49d067f4c
--- /dev/null
+++ b/src/core/internal_network/socket_proxy.cpp
@@ -0,0 +1,284 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <chrono>
+#include <thread>
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "core/internal_network/network.h"
+#include "core/internal_network/network_interface.h"
+#include "core/internal_network/socket_proxy.h"
+
+namespace Network {
+
+ProxySocket::ProxySocket(RoomNetwork& room_network_) noexcept : room_network{room_network_} {}
+
+ProxySocket::~ProxySocket() {
+ if (fd == INVALID_SOCKET) {
+ return;
+ }
+ fd = INVALID_SOCKET;
+}
+
+void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
+ if (protocol != packet.protocol || local_endpoint.portno != packet.remote_endpoint.portno ||
+ closed) {
+ return;
+ }
+ std::lock_guard guard(packets_mutex);
+ received_packets.push(packet);
+}
+
+template <typename T>
+Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) {
+ LOG_DEBUG(Network, "(STUBBED) called");
+ return Errno::SUCCESS;
+}
+
+Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
+ protocol = socket_protocol;
+ SetSockOpt(fd, SO_TYPE, type);
+
+ return Errno::SUCCESS;
+}
+
+std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return {AcceptResult{}, Errno::SUCCESS};
+}
+
+Errno ProxySocket::Connect(SockAddrIn addr_in) {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return Errno::SUCCESS;
+}
+
+std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return {SockAddrIn{}, Errno::SUCCESS};
+}
+
+std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return {SockAddrIn{}, Errno::SUCCESS};
+}
+
+Errno ProxySocket::Bind(SockAddrIn addr) {
+ if (is_bound) {
+ LOG_WARNING(Network, "Rebinding Socket is unimplemented!");
+ return Errno::SUCCESS;
+ }
+ local_endpoint = addr;
+ is_bound = true;
+
+ return Errno::SUCCESS;
+}
+
+Errno ProxySocket::Listen(s32 backlog) {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return Errno::SUCCESS;
+}
+
+Errno ProxySocket::Shutdown(ShutdownHow how) {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return Errno::SUCCESS;
+}
+
+std::pair<s32, Errno> ProxySocket::Recv(int flags, std::vector<u8>& message) {
+ LOG_WARNING(Network, "(STUBBED) called");
+ ASSERT(flags == 0);
+ ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
+
+ return {static_cast<s32>(0), Errno::SUCCESS};
+}
+
+std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) {
+ ASSERT(flags == 0);
+ ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
+
+ // TODO (flTobi): Verify the timeout behavior and break when connection is lost
+ const auto timestamp = std::chrono::steady_clock::now();
+ // When receive_timeout is set to zero, the socket is supposed to wait indefinitely until a
+ // packet arrives. In order to prevent lost packets from hanging the emulation thread, we set
+ // the timeout to 5s instead
+ const auto timeout = receive_timeout == 0 ? 5000 : receive_timeout;
+ while (true) {
+ {
+ std::lock_guard guard(packets_mutex);
+ if (received_packets.size() > 0) {
+ return ReceivePacket(flags, message, addr, message.size());
+ }
+ }
+
+ if (!blocking) {
+ return {-1, Errno::AGAIN};
+ }
+
+ std::this_thread::yield();
+
+ const auto time_diff = std::chrono::steady_clock::now() - timestamp;
+ const auto time_diff_ms =
+ std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
+
+ if (time_diff_ms > timeout) {
+ return {-1, Errno::TIMEDOUT};
+ }
+ }
+}
+
+std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& message,
+ SockAddrIn* addr, std::size_t max_length) {
+ ProxyPacket& packet = received_packets.front();
+ if (addr) {
+ addr->family = Domain::INET;
+ addr->ip = packet.local_endpoint.ip; // The senders ip address
+ addr->portno = packet.local_endpoint.portno; // The senders port number
+ }
+
+ bool peek = (flags & FLAG_MSG_PEEK) != 0;
+ std::size_t read_bytes;
+ if (packet.data.size() > max_length) {
+ read_bytes = max_length;
+ message.clear();
+ std::copy(packet.data.begin(), packet.data.begin() + read_bytes,
+ std::back_inserter(message));
+ message.resize(max_length);
+
+ if (protocol == Protocol::UDP) {
+ if (!peek) {
+ received_packets.pop();
+ }
+ return {-1, Errno::MSGSIZE};
+ } else if (protocol == Protocol::TCP) {
+ std::vector<u8> numArray(packet.data.size() - max_length);
+ std::copy(packet.data.begin() + max_length, packet.data.end(),
+ std::back_inserter(numArray));
+ packet.data = numArray;
+ }
+ } else {
+ read_bytes = packet.data.size();
+ message.clear();
+ std::copy(packet.data.begin(), packet.data.end(), std::back_inserter(message));
+ message.resize(max_length);
+ if (!peek) {
+ received_packets.pop();
+ }
+ }
+
+ return {static_cast<u32>(read_bytes), Errno::SUCCESS};
+}
+
+std::pair<s32, Errno> ProxySocket::Send(const std::vector<u8>& message, int flags) {
+ LOG_WARNING(Network, "(STUBBED) called");
+ ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
+ ASSERT(flags == 0);
+
+ return {static_cast<s32>(0), Errno::SUCCESS};
+}
+
+void ProxySocket::SendPacket(ProxyPacket& packet) {
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (room_member->IsConnected()) {
+ room_member->SendProxyPacket(packet);
+ }
+ }
+}
+
+std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, const std::vector<u8>& message,
+ const SockAddrIn* addr) {
+ ASSERT(flags == 0);
+
+ if (!is_bound) {
+ LOG_ERROR(Network, "ProxySocket is not bound!");
+ return {static_cast<s32>(message.size()), Errno::SUCCESS};
+ }
+
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (!room_member->IsConnected()) {
+ return {static_cast<s32>(message.size()), Errno::SUCCESS};
+ }
+ }
+
+ ProxyPacket packet;
+ packet.local_endpoint = local_endpoint;
+ packet.remote_endpoint = *addr;
+ packet.protocol = protocol;
+ packet.broadcast = broadcast;
+
+ auto& ip = local_endpoint.ip;
+ auto ipv4 = Network::GetHostIPv4Address();
+ // If the ip is all zeroes (INADDR_ANY) or if it matches the hosts ip address,
+ // replace it with a "fake" routing address
+ if (std::all_of(ip.begin(), ip.end(), [](u8 i) { return i == 0; }) || (ipv4 && ipv4 == ip)) {
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ packet.local_endpoint.ip = room_member->GetFakeIpAddress();
+ }
+ }
+
+ packet.data.clear();
+ std::copy(message.begin(), message.end(), std::back_inserter(packet.data));
+
+ SendPacket(packet);
+
+ return {static_cast<s32>(message.size()), Errno::SUCCESS};
+}
+
+Errno ProxySocket::Close() {
+ fd = INVALID_SOCKET;
+ closed = true;
+
+ return Errno::SUCCESS;
+}
+
+Errno ProxySocket::SetLinger(bool enable, u32 linger) {
+ struct Linger {
+ u16 linger_enable;
+ u16 linger_time;
+ } values;
+ values.linger_enable = enable ? 1 : 0;
+ values.linger_time = static_cast<u16>(linger);
+
+ return SetSockOpt(fd, SO_LINGER, values);
+}
+
+Errno ProxySocket::SetReuseAddr(bool enable) {
+ return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
+}
+
+Errno ProxySocket::SetBroadcast(bool enable) {
+ broadcast = enable;
+ return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
+}
+
+Errno ProxySocket::SetSndBuf(u32 value) {
+ return SetSockOpt(fd, SO_SNDBUF, value);
+}
+
+Errno ProxySocket::SetKeepAlive(bool enable) {
+ return Errno::SUCCESS;
+}
+
+Errno ProxySocket::SetRcvBuf(u32 value) {
+ return SetSockOpt(fd, SO_RCVBUF, value);
+}
+
+Errno ProxySocket::SetSndTimeo(u32 value) {
+ send_timeout = value;
+ return SetSockOpt(fd, SO_SNDTIMEO, static_cast<int>(value));
+}
+
+Errno ProxySocket::SetRcvTimeo(u32 value) {
+ receive_timeout = value;
+ return SetSockOpt(fd, SO_RCVTIMEO, static_cast<int>(value));
+}
+
+Errno ProxySocket::SetNonBlock(bool enable) {
+ blocking = !enable;
+ return Errno::SUCCESS;
+}
+
+bool ProxySocket::IsOpened() const {
+ return fd != INVALID_SOCKET;
+}
+
+} // namespace Network
diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h
new file mode 100644
index 000000000..f12b5f567
--- /dev/null
+++ b/src/core/internal_network/socket_proxy.h
@@ -0,0 +1,97 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <mutex>
+#include <vector>
+#include <queue>
+
+#include "common/common_funcs.h"
+#include "core/internal_network/sockets.h"
+#include "network/network.h"
+
+namespace Network {
+
+class ProxySocket : public SocketBase {
+public:
+ YUZU_NON_COPYABLE(ProxySocket);
+ YUZU_NON_MOVEABLE(ProxySocket);
+
+ explicit ProxySocket(RoomNetwork& room_network_) noexcept;
+ ~ProxySocket() override;
+
+ void HandleProxyPacket(const ProxyPacket& packet) override;
+
+ Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override;
+
+ Errno Close() override;
+
+ std::pair<AcceptResult, Errno> Accept() override;
+
+ Errno Connect(SockAddrIn addr_in) override;
+
+ std::pair<SockAddrIn, Errno> GetPeerName() override;
+
+ std::pair<SockAddrIn, Errno> GetSockName() override;
+
+ Errno Bind(SockAddrIn addr) override;
+
+ Errno Listen(s32 backlog) override;
+
+ Errno Shutdown(ShutdownHow how) override;
+
+ std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override;
+
+ std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override;
+
+ std::pair<s32, Errno> ReceivePacket(int flags, std::vector<u8>& message, SockAddrIn* addr,
+ std::size_t max_length);
+
+ std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override;
+
+ void SendPacket(ProxyPacket& packet);
+
+ std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
+ const SockAddrIn* addr) override;
+
+ Errno SetLinger(bool enable, u32 linger) override;
+
+ Errno SetReuseAddr(bool enable) override;
+
+ Errno SetBroadcast(bool enable) override;
+
+ Errno SetKeepAlive(bool enable) override;
+
+ Errno SetSndBuf(u32 value) override;
+
+ Errno SetRcvBuf(u32 value) override;
+
+ Errno SetSndTimeo(u32 value) override;
+
+ Errno SetRcvTimeo(u32 value) override;
+
+ Errno SetNonBlock(bool enable) override;
+
+ template <typename T>
+ Errno SetSockOpt(SOCKET fd, int option, T value);
+
+ bool IsOpened() const override;
+
+private:
+ bool broadcast = false;
+ bool closed = false;
+ u32 send_timeout = 0;
+ u32 receive_timeout = 0;
+ bool is_bound = false;
+ SockAddrIn local_endpoint{};
+ bool blocking = true;
+ std::queue<ProxyPacket> received_packets;
+ Protocol protocol;
+
+ std::mutex packets_mutex;
+
+ RoomNetwork& room_network;
+};
+
+} // namespace Network
diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h
new file mode 100644
index 000000000..a70429b19
--- /dev/null
+++ b/src/core/internal_network/sockets.h
@@ -0,0 +1,163 @@
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#if defined(_WIN32)
+#elif !YUZU_UNIX
+#error "Platform not implemented"
+#endif
+
+#include "common/common_types.h"
+#include "core/internal_network/network.h"
+#include "network/network.h"
+
+// TODO: C++20 Replace std::vector usages with std::span
+
+namespace Network {
+
+class SocketBase {
+public:
+#ifdef YUZU_UNIX
+ using SOCKET = int;
+ static constexpr SOCKET INVALID_SOCKET = -1;
+ static constexpr SOCKET SOCKET_ERROR = -1;
+#endif
+
+ struct AcceptResult {
+ std::unique_ptr<SocketBase> socket;
+ SockAddrIn sockaddr_in;
+ };
+ virtual ~SocketBase() = default;
+
+ virtual SocketBase& operator=(const SocketBase&) = delete;
+
+ // Avoid closing sockets implicitly
+ virtual SocketBase& operator=(SocketBase&&) noexcept = delete;
+
+ virtual Errno Initialize(Domain domain, Type type, Protocol protocol) = 0;
+
+ virtual Errno Close() = 0;
+
+ virtual std::pair<AcceptResult, Errno> Accept() = 0;
+
+ virtual Errno Connect(SockAddrIn addr_in) = 0;
+
+ virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0;
+
+ virtual std::pair<SockAddrIn, Errno> GetSockName() = 0;
+
+ virtual Errno Bind(SockAddrIn addr) = 0;
+
+ virtual Errno Listen(s32 backlog) = 0;
+
+ virtual Errno Shutdown(ShutdownHow how) = 0;
+
+ virtual std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) = 0;
+
+ virtual std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message,
+ SockAddrIn* addr) = 0;
+
+ virtual std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) = 0;
+
+ virtual std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
+ const SockAddrIn* addr) = 0;
+
+ virtual Errno SetLinger(bool enable, u32 linger) = 0;
+
+ virtual Errno SetReuseAddr(bool enable) = 0;
+
+ virtual Errno SetKeepAlive(bool enable) = 0;
+
+ virtual Errno SetBroadcast(bool enable) = 0;
+
+ virtual Errno SetSndBuf(u32 value) = 0;
+
+ virtual Errno SetRcvBuf(u32 value) = 0;
+
+ virtual Errno SetSndTimeo(u32 value) = 0;
+
+ virtual Errno SetRcvTimeo(u32 value) = 0;
+
+ virtual Errno SetNonBlock(bool enable) = 0;
+
+ virtual bool IsOpened() const = 0;
+
+ virtual void HandleProxyPacket(const ProxyPacket& packet) = 0;
+
+ SOCKET fd = INVALID_SOCKET;
+};
+
+class Socket : public SocketBase {
+public:
+ Socket() = default;
+ ~Socket() override;
+
+ Socket(const Socket&) = delete;
+ Socket& operator=(const Socket&) = delete;
+
+ Socket(Socket&& rhs) noexcept;
+
+ // Avoid closing sockets implicitly
+ Socket& operator=(Socket&&) noexcept = delete;
+
+ Errno Initialize(Domain domain, Type type, Protocol protocol) override;
+
+ Errno Close() override;
+
+ std::pair<AcceptResult, Errno> Accept() override;
+
+ Errno Connect(SockAddrIn addr_in) override;
+
+ std::pair<SockAddrIn, Errno> GetPeerName() override;
+
+ std::pair<SockAddrIn, Errno> GetSockName() override;
+
+ Errno Bind(SockAddrIn addr) override;
+
+ Errno Listen(s32 backlog) override;
+
+ Errno Shutdown(ShutdownHow how) override;
+
+ std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override;
+
+ std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override;
+
+ std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override;
+
+ std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
+ const SockAddrIn* addr) override;
+
+ Errno SetLinger(bool enable, u32 linger) override;
+
+ Errno SetReuseAddr(bool enable) override;
+
+ Errno SetKeepAlive(bool enable) override;
+
+ Errno SetBroadcast(bool enable) override;
+
+ Errno SetSndBuf(u32 value) override;
+
+ Errno SetRcvBuf(u32 value) override;
+
+ Errno SetSndTimeo(u32 value) override;
+
+ Errno SetRcvTimeo(u32 value) override;
+
+ Errno SetNonBlock(bool enable) override;
+
+ template <typename T>
+ Errno SetSockOpt(SOCKET fd, int option, T value);
+
+ bool IsOpened() const override;
+
+ void HandleProxyPacket(const ProxyPacket& packet) override;
+};
+
+std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
+
+} // namespace Network
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
deleted file mode 100644
index dfb10c34f..000000000
--- a/src/core/loader/elf.cpp
+++ /dev/null
@@ -1,263 +0,0 @@
-// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
-// SPDX-FileCopyrightText: 2014 Citra Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <cstring>
-#include <memory>
-#include "common/common_funcs.h"
-#include "common/common_types.h"
-#include "common/elf.h"
-#include "common/logging/log.h"
-#include "core/hle/kernel/code_set.h"
-#include "core/hle/kernel/k_page_table.h"
-#include "core/hle/kernel/k_process.h"
-#include "core/loader/elf.h"
-#include "core/memory.h"
-
-using namespace Common::ELF;
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// ElfReader class
-
-typedef int SectionID;
-
-class ElfReader {
-private:
- char* base;
- u32* base32;
-
- Elf32_Ehdr* header;
- Elf32_Phdr* segments;
- Elf32_Shdr* sections;
-
- u32* sectionAddrs;
- bool relocate;
- VAddr entryPoint;
-
-public:
- explicit ElfReader(void* ptr);
-
- u32 Read32(int off) const {
- return base32[off >> 2];
- }
-
- // Quick accessors
- u16 GetType() const {
- return header->e_type;
- }
- u16 GetMachine() const {
- return header->e_machine;
- }
- VAddr GetEntryPoint() const {
- return entryPoint;
- }
- u32 GetFlags() const {
- return (u32)(header->e_flags);
- }
- Kernel::CodeSet LoadInto(VAddr vaddr);
-
- int GetNumSegments() const {
- return (int)(header->e_phnum);
- }
- int GetNumSections() const {
- return (int)(header->e_shnum);
- }
- const u8* GetPtr(int offset) const {
- return (u8*)base + offset;
- }
- const char* GetSectionName(int section) const;
- const u8* GetSectionDataPtr(int section) const {
- if (section < 0 || section >= header->e_shnum)
- return nullptr;
- if (sections[section].sh_type != ElfShtNobits)
- return GetPtr(sections[section].sh_offset);
- else
- return nullptr;
- }
- bool IsCodeSection(int section) const {
- return sections[section].sh_type == ElfShtProgBits;
- }
- const u8* GetSegmentPtr(int segment) {
- return GetPtr(segments[segment].p_offset);
- }
- u32 GetSectionAddr(SectionID section) const {
- return sectionAddrs[section];
- }
- unsigned int GetSectionSize(SectionID section) const {
- return sections[section].sh_size;
- }
- SectionID GetSectionByName(const char* name, int firstSection = 0) const; //-1 for not found
-
- bool DidRelocate() const {
- return relocate;
- }
-};
-
-ElfReader::ElfReader(void* ptr) {
- base = (char*)ptr;
- base32 = (u32*)ptr;
- header = (Elf32_Ehdr*)ptr;
-
- segments = (Elf32_Phdr*)(base + header->e_phoff);
- sections = (Elf32_Shdr*)(base + header->e_shoff);
-
- entryPoint = header->e_entry;
-}
-
-const char* ElfReader::GetSectionName(int section) const {
- if (sections[section].sh_type == ElfShtNull)
- return nullptr;
-
- int name_offset = sections[section].sh_name;
- const char* ptr = reinterpret_cast<const char*>(GetSectionDataPtr(header->e_shstrndx));
-
- if (ptr)
- return ptr + name_offset;
-
- return nullptr;
-}
-
-Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
- LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
-
- // Should we relocate?
- relocate = (header->e_type != ElfTypeExec);
-
- if (relocate) {
- LOG_DEBUG(Loader, "Relocatable module");
- entryPoint += vaddr;
- } else {
- LOG_DEBUG(Loader, "Prerelocated executable");
- }
- LOG_DEBUG(Loader, "{} segments:", header->e_phnum);
-
- // First pass : Get the bits into RAM
- const VAddr base_addr = relocate ? vaddr : 0;
-
- u64 total_image_size = 0;
- for (unsigned int i = 0; i < header->e_phnum; ++i) {
- const Elf32_Phdr* p = &segments[i];
- if (p->p_type == ElfPtLoad) {
- total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF;
- }
- }
-
- Kernel::PhysicalMemory program_image(total_image_size);
- std::size_t current_image_position = 0;
-
- Kernel::CodeSet codeset;
-
- for (unsigned int i = 0; i < header->e_phnum; ++i) {
- const Elf32_Phdr* p = &segments[i];
- LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type,
- p->p_vaddr, p->p_filesz, p->p_memsz);
-
- if (p->p_type == ElfPtLoad) {
- Kernel::CodeSet::Segment* codeset_segment;
- u32 permission_flags = p->p_flags & (ElfPfRead | ElfPfWrite | ElfPfExec);
- if (permission_flags == (ElfPfRead | ElfPfExec)) {
- codeset_segment = &codeset.CodeSegment();
- } else if (permission_flags == (ElfPfRead)) {
- codeset_segment = &codeset.RODataSegment();
- } else if (permission_flags == (ElfPfRead | ElfPfWrite)) {
- codeset_segment = &codeset.DataSegment();
- } else {
- LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
- p->p_flags);
- continue;
- }
-
- if (codeset_segment->size != 0) {
- LOG_ERROR(Loader,
- "ELF has more than one segment of the same type. Skipping extra "
- "segment (id {})",
- i);
- continue;
- }
-
- const VAddr segment_addr = base_addr + p->p_vaddr;
- const u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF;
-
- codeset_segment->offset = current_image_position;
- codeset_segment->addr = segment_addr;
- codeset_segment->size = aligned_size;
-
- std::memcpy(program_image.data() + current_image_position, GetSegmentPtr(i),
- p->p_filesz);
- current_image_position += aligned_size;
- }
- }
-
- codeset.entrypoint = base_addr + header->e_entry;
- codeset.memory = std::move(program_image);
-
- LOG_DEBUG(Loader, "Done loading.");
-
- return codeset;
-}
-
-SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const {
- for (int i = firstSection; i < header->e_shnum; i++) {
- const char* secname = GetSectionName(i);
-
- if (secname != nullptr && strcmp(name, secname) == 0)
- return i;
- }
- return -1;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Loader namespace
-
-namespace Loader {
-
-AppLoader_ELF::AppLoader_ELF(FileSys::VirtualFile file_) : AppLoader(std::move(file_)) {}
-
-FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& elf_file) {
- static constexpr u16 ELF_MACHINE_ARM{0x28};
-
- u32 magic = 0;
- if (4 != elf_file->ReadObject(&magic)) {
- return FileType::Error;
- }
-
- u16 machine = 0;
- if (2 != elf_file->ReadObject(&machine, 18)) {
- return FileType::Error;
- }
-
- if (Common::MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine) {
- return FileType::ELF;
- }
-
- return FileType::Error;
-}
-
-AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::KProcess& process,
- [[maybe_unused]] Core::System& system) {
- if (is_loaded) {
- return {ResultStatus::ErrorAlreadyLoaded, {}};
- }
-
- std::vector<u8> buffer = file->ReadAllBytes();
- if (buffer.size() != file->GetSize()) {
- return {ResultStatus::ErrorIncorrectELFFileSize, {}};
- }
-
- const VAddr base_address = process.PageTable().GetCodeRegionStart();
- ElfReader elf_reader(&buffer[0]);
- Kernel::CodeSet codeset = elf_reader.LoadInto(base_address);
- const VAddr entry_point = codeset.entrypoint;
-
- // Setup the process code layout
- if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), buffer.size()).IsError()) {
- return {ResultStatus::ErrorNotInitialized, {}};
- }
-
- process.LoadModule(std::move(codeset), entry_point);
-
- is_loaded = true;
- return {ResultStatus::Success, LoadParameters{48, Core::Memory::DEFAULT_STACK_SIZE}};
-}
-
-} // namespace Loader
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
deleted file mode 100644
index acd33dc3d..000000000
--- a/src/core/loader/elf.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
-// SPDX-FileCopyrightText: 2014 Citra Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/loader/loader.h"
-
-namespace Core {
-class System;
-}
-
-namespace Loader {
-
-/// Loads an ELF/AXF file
-class AppLoader_ELF final : public AppLoader {
-public:
- explicit AppLoader_ELF(FileSys::VirtualFile file);
-
- /**
- * Identifies whether or not the given file is an ELF file.
- *
- * @param elf_file The file to identify.
- *
- * @return FileType::ELF, or FileType::Error if the file is not an ELF file.
- */
- static FileType IdentifyType(const FileSys::VirtualFile& elf_file);
-
- FileType GetFileType() const override {
- return IdentifyType(file);
- }
-
- LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
-};
-
-} // namespace Loader
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
index 9af46a0f7..d8a1bf82a 100644
--- a/src/core/loader/kip.cpp
+++ b/src/core/loader/kip.cpp
@@ -14,7 +14,7 @@ namespace Loader {
namespace {
constexpr u32 PageAlignSize(u32 size) {
- return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
+ return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
}
} // Anonymous namespace
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 994ee891f..104d16efa 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -12,7 +12,6 @@
#include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/loader/deconstructed_rom_directory.h"
-#include "core/loader/elf.h"
#include "core/loader/kip.h"
#include "core/loader/nax.h"
#include "core/loader/nca.h"
@@ -39,8 +38,6 @@ std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) {
FileType IdentifyFile(FileSys::VirtualFile file) {
if (const auto romdir_type = IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) {
return *romdir_type;
- } else if (const auto elf_type = IdentifyFileLoader<AppLoader_ELF>(file)) {
- return *elf_type;
} else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) {
return *nso_type;
} else if (const auto nro_type = IdentifyFileLoader<AppLoader_NRO>(file)) {
@@ -69,8 +66,6 @@ FileType GuessFromFilename(const std::string& name) {
const std::string extension =
Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
- if (extension == "elf")
- return FileType::ELF;
if (extension == "nro")
return FileType::NRO;
if (extension == "nso")
@@ -89,8 +84,6 @@ FileType GuessFromFilename(const std::string& name) {
std::string GetFileTypeString(FileType type) {
switch (type) {
- case FileType::ELF:
- return "ELF";
case FileType::NRO:
return "NRO";
case FileType::NSO:
@@ -208,10 +201,6 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
FileType type, u64 program_id,
std::size_t program_index) {
switch (type) {
- // Standard ELF file format.
- case FileType::ELF:
- return std::make_unique<AppLoader_ELF>(std::move(file));
-
// NX NSO file format.
case FileType::NSO:
return std::make_unique<AppLoader_NSO>(std::move(file));
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 7bf4faaf1..7b43f70ed 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -34,7 +34,6 @@ namespace Loader {
enum class FileType {
Error,
Unknown,
- ELF,
NSO,
NRO,
NCA,
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 1b0bb0876..73d04d7ee 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -125,7 +125,7 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& nro_file) {
}
static constexpr u32 PageAlignSize(u32 size) {
- return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
+ return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
}
static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) {
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 8dd956fc6..4c3b3c655 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -45,7 +45,7 @@ std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
}
constexpr u32 PageAlignSize(u32 size) {
- return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
+ return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
}
} // Anonymous namespace
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 7534de01e..34ad7cadd 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -1,6 +1,5 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2015 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
@@ -37,10 +36,11 @@ struct Memory::Impl {
}
void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) {
- ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
- ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
+ ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
+ ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", base);
ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", target);
- MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
+ MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, target,
+ Common::PageType::Memory);
if (Settings::IsFastmemEnabled()) {
system.DeviceMemory().buffer.Map(base, target - DramMemoryMap::Base, size);
@@ -48,9 +48,10 @@ struct Memory::Impl {
}
void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
- ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
- ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
- MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, 0, Common::PageType::Unmapped);
+ ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
+ ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", base);
+ MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0,
+ Common::PageType::Unmapped);
if (Settings::IsFastmemEnabled()) {
system.DeviceMemory().buffer.Unmap(base, size);
@@ -58,7 +59,7 @@ struct Memory::Impl {
}
[[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const {
- const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]};
+ const PAddr paddr{current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]};
if (!paddr) {
return {};
@@ -67,6 +68,16 @@ struct Memory::Impl {
return system.DeviceMemory().GetPointer(paddr) + vaddr;
}
+ [[nodiscard]] u8* GetPointerFromDebugMemory(VAddr vaddr) const {
+ const PAddr paddr{current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]};
+
+ if (paddr == 0) {
+ return {};
+ }
+
+ return system.DeviceMemory().GetPointer(paddr) + vaddr;
+ }
+
u8 Read8(const VAddr addr) {
return Read<u8>(addr);
}
@@ -167,13 +178,14 @@ struct Memory::Impl {
auto on_unmapped, auto on_memory, auto on_rasterizer, auto increment) {
const auto& page_table = process.PageTable().PageTableImpl();
std::size_t remaining_size = size;
- std::size_t page_index = addr >> PAGE_BITS;
- std::size_t page_offset = addr & PAGE_MASK;
+ std::size_t page_index = addr >> YUZU_PAGEBITS;
+ std::size_t page_offset = addr & YUZU_PAGEMASK;
while (remaining_size) {
const std::size_t copy_amount =
- std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
- const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
+ std::min(static_cast<std::size_t>(YUZU_PAGESIZE) - page_offset, remaining_size);
+ const auto current_vaddr =
+ static_cast<VAddr>((page_index << YUZU_PAGEBITS) + page_offset);
const auto [pointer, type] = page_table.pointers[page_index].PointerType();
switch (type) {
@@ -183,7 +195,13 @@ struct Memory::Impl {
}
case Common::PageType::Memory: {
DEBUG_ASSERT(pointer);
- u8* mem_ptr = pointer + page_offset + (page_index << PAGE_BITS);
+ u8* mem_ptr = pointer + page_offset + (page_index << YUZU_PAGEBITS);
+ on_memory(copy_amount, mem_ptr);
+ break;
+ }
+ case Common::PageType::DebugMemory: {
+ DEBUG_ASSERT(pointer);
+ u8* const mem_ptr{GetPointerFromDebugMemory(current_vaddr)};
on_memory(copy_amount, mem_ptr);
break;
}
@@ -316,6 +334,58 @@ struct Memory::Impl {
});
}
+ void MarkRegionDebug(VAddr vaddr, u64 size, bool debug) {
+ if (vaddr == 0) {
+ return;
+ }
+
+ // Iterate over a contiguous CPU address space, marking/unmarking the region.
+ // The region is at a granularity of CPU pages.
+
+ const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
+ for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
+ const Common::PageType page_type{
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type()};
+ if (debug) {
+ // Switch page type to debug if now debug
+ switch (page_type) {
+ case Common::PageType::Unmapped:
+ ASSERT_MSG(false, "Attempted to mark unmapped pages as debug");
+ break;
+ case Common::PageType::RasterizerCachedMemory:
+ case Common::PageType::DebugMemory:
+ // Page is already marked.
+ break;
+ case Common::PageType::Memory:
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
+ nullptr, Common::PageType::DebugMemory);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ } else {
+ // Switch page type to non-debug if now non-debug
+ switch (page_type) {
+ case Common::PageType::Unmapped:
+ ASSERT_MSG(false, "Attempted to mark unmapped pages as non-debug");
+ break;
+ case Common::PageType::RasterizerCachedMemory:
+ case Common::PageType::Memory:
+ // Don't mess with already non-debug or rasterizer memory.
+ break;
+ case Common::PageType::DebugMemory: {
+ u8* const pointer{GetPointerFromDebugMemory(vaddr & ~YUZU_PAGEMASK)};
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
+ pointer - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+ }
+ }
+ }
+
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
if (vaddr == 0) {
return;
@@ -331,10 +401,10 @@ struct Memory::Impl {
// granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size
// is different). This assumes the specified GPU address region is contiguous as well.
- const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
- for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
+ const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
+ for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
const Common::PageType page_type{
- current_page_table->pointers[vaddr >> PAGE_BITS].Type()};
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type()};
if (cached) {
// Switch page type to cached if now cached
switch (page_type) {
@@ -342,8 +412,9 @@ struct Memory::Impl {
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
+ case Common::PageType::DebugMemory:
case Common::PageType::Memory:
- current_page_table->pointers[vaddr >> PAGE_BITS].Store(
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
nullptr, Common::PageType::RasterizerCachedMemory);
break;
case Common::PageType::RasterizerCachedMemory:
@@ -360,21 +431,22 @@ struct Memory::Impl {
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
+ case Common::PageType::DebugMemory:
case Common::PageType::Memory:
// There can be more than one GPU region mapped per CPU region, so it's common
// that this area is already unmarked as cached.
break;
case Common::PageType::RasterizerCachedMemory: {
- u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)};
+ u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~YUZU_PAGEMASK)};
if (pointer == nullptr) {
// It's possible that this function has been called while updating the
// pagetable after unmapping a VMA. In that case the underlying VMA will no
// longer exist, and we should just leave the pagetable entry blank.
- current_page_table->pointers[vaddr >> PAGE_BITS].Store(
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
nullptr, Common::PageType::Unmapped);
} else {
- current_page_table->pointers[vaddr >> PAGE_BITS].Store(
- pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory);
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
+ pointer - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
}
break;
}
@@ -396,8 +468,8 @@ struct Memory::Impl {
*/
void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target,
Common::PageType type) {
- LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", target, base * PAGE_SIZE,
- (base + size) * PAGE_SIZE);
+ LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", target, base * YUZU_PAGESIZE,
+ (base + size) * YUZU_PAGESIZE);
// During boot, current_page_table might not be set yet, in which case we need not flush
if (system.IsPoweredOn()) {
@@ -405,7 +477,7 @@ struct Memory::Impl {
for (u64 i = 0; i < size; i++) {
const auto page = base + i;
if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) {
- gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
+ gpu.FlushAndInvalidateRegion(page << YUZU_PAGEBITS, YUZU_PAGESIZE);
}
}
}
@@ -416,7 +488,7 @@ struct Memory::Impl {
if (!target) {
ASSERT_MSG(type != Common::PageType::Memory,
- "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE);
+ "Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE);
while (base != end) {
page_table.pointers[base].Store(nullptr, type);
@@ -427,21 +499,21 @@ struct Memory::Impl {
} else {
while (base != end) {
page_table.pointers[base].Store(
- system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS), type);
- page_table.backing_addr[base] = target - (base << PAGE_BITS);
+ system.DeviceMemory().GetPointer(target) - (base << YUZU_PAGEBITS), type);
+ page_table.backing_addr[base] = target - (base << YUZU_PAGEBITS);
ASSERT_MSG(page_table.pointers[base].Pointer(),
"memory mapping base yield a nullptr within the table");
base += 1;
- target += PAGE_SIZE;
+ target += YUZU_PAGESIZE;
}
}
}
[[nodiscard]] u8* GetPointerImpl(VAddr vaddr, auto on_unmapped, auto on_rasterizer) const {
// AARCH64 masks the upper 16 bit of all memory accesses
- vaddr &= 0xffffffffffffLL;
+ vaddr &= 0xffffffffffffULL;
if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
on_unmapped();
@@ -449,7 +521,7 @@ struct Memory::Impl {
}
// Avoid adding any extra logic to this fast-path block
- const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
+ const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Raw();
if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
return &pointer[vaddr];
}
@@ -460,6 +532,8 @@ struct Memory::Impl {
case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr);
return nullptr;
+ case Common::PageType::DebugMemory:
+ return GetPointerFromDebugMemory(vaddr);
case Common::PageType::RasterizerCachedMemory: {
u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
on_rasterizer();
@@ -586,19 +660,20 @@ void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
const Kernel::KProcess& process = *system.CurrentProcess();
const auto& page_table = process.PageTable().PageTableImpl();
- const size_t page = vaddr >> PAGE_BITS;
+ const size_t page = vaddr >> YUZU_PAGEBITS;
if (page >= page_table.pointers.size()) {
return false;
}
const auto [pointer, type] = page_table.pointers[page].PointerType();
- return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
+ return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory ||
+ type == Common::PageType::DebugMemory;
}
bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const {
VAddr end = base + size;
- VAddr page = Common::AlignDown(base, PAGE_SIZE);
+ VAddr page = Common::AlignDown(base, YUZU_PAGESIZE);
- for (; page < end; page += PAGE_SIZE) {
+ for (; page < end; page += YUZU_PAGESIZE) {
if (!IsValidVirtualAddress(page)) {
return false;
}
@@ -703,8 +778,16 @@ void Memory::CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr s
impl->CopyBlock(process, dest_addr, src_addr, size);
}
+void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, const std::size_t size) {
+ impl->ZeroBlock(process, dest_addr, size);
+}
+
void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
impl->RasterizerMarkRegionCached(vaddr, size, cached);
}
+void Memory::MarkRegionDebug(VAddr vaddr, u64 size, bool debug) {
+ impl->MarkRegionDebug(vaddr, size, debug);
+}
+
} // namespace Core::Memory
diff --git a/src/core/memory.h b/src/core/memory.h
index 58cc27b29..a11ff8766 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -28,9 +27,9 @@ namespace Core::Memory {
* Page size used by the ARM architecture. This is the smallest granularity with which memory can
* be mapped.
*/
-constexpr std::size_t PAGE_BITS = 12;
-constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS;
-constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
+constexpr std::size_t YUZU_PAGEBITS = 12;
+constexpr u64 YUZU_PAGESIZE = 1ULL << YUZU_PAGEBITS;
+constexpr u64 YUZU_PAGEMASK = YUZU_PAGESIZE - 1;
/// Virtual user-space memory regions
enum : VAddr {
@@ -437,6 +436,19 @@ public:
std::size_t size);
/**
+ * Zeros a range of bytes within the current process' address space at the specified
+ * virtual address.
+ *
+ * @param process The process that will have data zeroed within its address space.
+ * @param dest_addr The destination virtual address to zero the data from.
+ * @param size The size of the range to zero out, in bytes.
+ *
+ * @post The range [dest_addr, size) within the process' address space contains the
+ * value 0.
+ */
+ void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
+
+ /**
* Marks each page within the specified address range as cached or uncached.
*
* @param vaddr The virtual address indicating the start of the address range.
@@ -446,6 +458,17 @@ public:
*/
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached);
+ /**
+ * Marks each page within the specified address range as debug or non-debug.
+ * Debug addresses are not accessible from fastmem pointers.
+ *
+ * @param vaddr The virtual address indicating the start of the address range.
+ * @param size The size of the address range in bytes.
+ * @param debug Whether or not any pages within the address range should be
+ * marked as debug or non-debug.
+ */
+ void MarkRegionDebug(VAddr vaddr, u64 size, bool debug);
+
private:
Core::System& system;
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 5f71f0ff5..ffdbacc18 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -184,10 +184,12 @@ CheatEngine::~CheatEngine() {
void CheatEngine::Initialize() {
event = Core::Timing::CreateEvent(
"CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
- [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ [this](std::uintptr_t user_data, s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
FrameCallback(user_data, ns_late);
+ return std::nullopt;
});
- core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event);
+ core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
metadata.process_id = system.CurrentProcess()->GetProcessID();
metadata.title_id = system.GetCurrentProcessProgramID();
@@ -237,8 +239,6 @@ void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late
MICROPROFILE_SCOPE(Cheat_Engine);
vm.Execute(metadata);
-
- core_timing.ScheduleEvent(CHEAT_ENGINE_NS - ns_late, event);
}
} // namespace Core::Memory
diff --git a/src/core/network/sockets.h b/src/core/network/sockets.h
deleted file mode 100644
index f889159f5..000000000
--- a/src/core/network/sockets.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <memory>
-#include <utility>
-
-#if defined(_WIN32)
-#elif !YUZU_UNIX
-#error "Platform not implemented"
-#endif
-
-#include "common/common_types.h"
-#include "core/network/network.h"
-
-// TODO: C++20 Replace std::vector usages with std::span
-
-namespace Network {
-
-class Socket {
-public:
- struct AcceptResult {
- std::unique_ptr<Socket> socket;
- SockAddrIn sockaddr_in;
- };
-
- explicit Socket() = default;
- ~Socket();
-
- Socket(const Socket&) = delete;
- Socket& operator=(const Socket&) = delete;
-
- Socket(Socket&& rhs) noexcept;
-
- // Avoid closing sockets implicitly
- Socket& operator=(Socket&&) noexcept = delete;
-
- Errno Initialize(Domain domain, Type type, Protocol protocol);
-
- Errno Close();
-
- std::pair<AcceptResult, Errno> Accept();
-
- Errno Connect(SockAddrIn addr_in);
-
- std::pair<SockAddrIn, Errno> GetPeerName();
-
- std::pair<SockAddrIn, Errno> GetSockName();
-
- Errno Bind(SockAddrIn addr);
-
- Errno Listen(s32 backlog);
-
- Errno Shutdown(ShutdownHow how);
-
- std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message);
-
- std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr);
-
- std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags);
-
- std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, const SockAddrIn* addr);
-
- Errno SetLinger(bool enable, u32 linger);
-
- Errno SetReuseAddr(bool enable);
-
- Errno SetKeepAlive(bool enable);
-
- Errno SetBroadcast(bool enable);
-
- Errno SetSndBuf(u32 value);
-
- Errno SetRcvBuf(u32 value);
-
- Errno SetSndTimeo(u32 value);
-
- Errno SetRcvTimeo(u32 value);
-
- Errno SetNonBlock(bool enable);
-
- bool IsOpened() const;
-
-#if defined(_WIN32)
- SOCKET fd = INVALID_SOCKET;
-#elif YUZU_UNIX
- int fd = -1;
-#endif
-};
-
-std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
-
-} // namespace Network
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 6ef459b7a..f09c176f8 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <chrono>
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h
index 816202588..dd6becc02 100644
--- a/src/core/perf_stats.h
+++ b/src/core/perf_stats.h
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index fa2729f54..6e21296f6 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -63,7 +63,7 @@ json GetYuzuVersionData() {
};
}
-json GetReportCommonData(u64 title_id, ResultCode result, const std::string& timestamp,
+json GetReportCommonData(u64 title_id, Result result, const std::string& timestamp,
std::optional<u128> user_id = {}) {
auto out = json{
{"title_id", fmt::format("{:016X}", title_id)},
@@ -198,8 +198,8 @@ Reporter::Reporter(System& system_) : system(system_) {
Reporter::~Reporter() = default;
-void Reporter::SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point,
- u64 sp, u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far,
+void Reporter::SaveCrashReport(u64 title_id, Result result, u64 set_flags, u64 entry_point, u64 sp,
+ u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far,
const std::array<u64, 31>& registers,
const std::array<u64, 32>& backtrace, u32 backtrace_size,
const std::string& arch, u32 unk10) const {
@@ -338,7 +338,7 @@ void Reporter::SavePlayReport(PlayReportType type, u64 title_id, std::vector<std
SaveToFile(std::move(out), GetPath("play_report", title_id, timestamp));
}
-void Reporter::SaveErrorReport(u64 title_id, ResultCode result,
+void Reporter::SaveErrorReport(u64 title_id, Result result,
std::optional<std::string> custom_text_main,
std::optional<std::string> custom_text_detail) const {
if (!IsReportingEnabled()) {
diff --git a/src/core/reporter.h b/src/core/reporter.h
index a5386bc83..68755cbde 100644
--- a/src/core/reporter.h
+++ b/src/core/reporter.h
@@ -9,7 +9,7 @@
#include <vector>
#include "common/common_types.h"
-union ResultCode;
+union Result;
namespace Kernel {
class HLERequestContext;
@@ -29,7 +29,7 @@ public:
~Reporter();
// Used by fatal services
- void SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, u64 sp,
+ void SaveCrashReport(u64 title_id, Result result, u64 set_flags, u64 entry_point, u64 sp,
u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far,
const std::array<u64, 31>& registers, const std::array<u64, 32>& backtrace,
u32 backtrace_size, const std::string& arch, u32 unk10) const;
@@ -60,7 +60,7 @@ public:
std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const;
// Used by error applet
- void SaveErrorReport(u64 title_id, ResultCode result,
+ void SaveErrorReport(u64 title_id, Result result,
std::optional<std::string> custom_text_main = {},
std::optional<std::string> custom_text_detail = {}) const;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 654db0b52..abcf6eb11 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index 6f3d45bea..887dc98f3 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 5cc99fbe4..98ebbbf32 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -53,8 +53,10 @@ Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& m
: core_timing{core_timing_}, memory{memory_} {
event = Core::Timing::CreateEvent(
"MemoryFreezer::FrameCallback",
- [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ [this](std::uintptr_t user_data, s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
FrameCallback(user_data, ns_late);
+ return std::nullopt;
});
core_timing.ScheduleEvent(memory_freezer_ns, event);
}