Compare commits

..

144 Commits

Author SHA1 Message Date
Fernando Sahmkow
c4bfbc6d25 Buffer Cache: Clear sync code. 2023-05-15 01:50:21 +02:00
Fernando S
9c739f1506 Merge pull request #10244 from liamwhite/lower-upper
time: implement ContinuousAdjustmentTimePoint
2023-05-13 03:51:05 +02:00
Fernando S
075d73f076 Merge pull request #10243 from Kelebek1/red_dot
Correctly track render target index in the framebuffer for image aspects
2023-05-13 03:50:31 +02:00
bunnei
021e503cc8 Merge pull request #10237 from liamwhite/cache-storage
fs: stub cache storage
2023-05-12 16:42:17 -07:00
bunnei
1805de0301 Merge pull request #10236 from liamwhite/thats-not-an-ibinder
nvnflinger: fix Parcel serialization
2023-05-12 16:07:35 -07:00
Liam
a22c5a3880 time: implement ContinuousAdjustmentTimePoint 2023-05-11 21:05:27 -04:00
Kelebek1
cd0ded7771 Correctly track RT indexes for image aspect lookup during clears 2023-05-12 01:40:21 +01:00
Liam
351079a4ba fs: adjust future save path 2023-05-11 17:30:30 -04:00
Liam
62bcb99ba8 am: stub CreateCacheStorage 2023-05-11 17:26:02 -04:00
Liam
13e4ceb990 fs: stub cache storage and fix params alignment 2023-05-11 17:23:28 -04:00
Liam
bb94beed15 nvnflinger: fix Parcel serialization 2023-05-11 17:09:19 -04:00
Liam
6e10a0c130 nvnflinger: fix producer slot fence init 2023-05-11 17:08:14 -04:00
liamwhite
182221b9ff Merge pull request #10132 from Kelebek1/fermi_blit2
Allow Fermi blit accelerate to work without images in cache
2023-05-11 10:45:59 -04:00
liamwhite
2643ea80df Merge pull request #10216 from Kelebek1/buffer_cache_region_checks
Swap order of checking/setting region modifications in the buffer_cache
2023-05-11 10:45:47 -04:00
liamwhite
f94186d3c3 Merge pull request #10222 from liamwhite/q
renderer_vulkan: separate guest and host compute descriptor queues
2023-05-11 10:45:36 -04:00
Kelebek1
bf08bc3c0f Allow Fermi blit accelerate to add src/dst to the cache if they don't exist already. Use ScratchBuffers in the software blit path. 2023-05-11 06:42:38 +01:00
Fernando S
871e7cacf6 Merge pull request #10224 from yuzu-emu/readme-update
Update README.md to remove Skyline license exception.
2023-05-11 05:51:32 +02:00
bunnei
2fe922aae5 Update README.md to remove Skyline license exception. 2023-05-10 15:52:30 -07:00
Liam
67fd1df762 renderer_vulkan: separate guest and host compute descriptor queues 2023-05-10 13:46:48 -04:00
liamwhite
b7f60e9123 Merge pull request #10207 from german77/amiibo_cheater
service: nfp: Allow to load with a different amiibo id
2023-05-10 10:25:48 -04:00
Narr the Reg
3ec027400e Merge pull request #10119 from marius851000/improved_non_hd_feeback
Attempt at improving HD Rumble emulation
2023-05-09 22:59:32 -06:00
Narr the Reg
42e1db4b0e service: nfc: Seed all random values 2023-05-09 17:54:07 -06:00
german77
1968cc7b10 service: nfp: Allow to load with a different amiibo id 2023-05-09 17:51:59 -06:00
Kelebek1
b3691fc33c Swap order of checking/setting region modifications in the buffer_cache 2023-05-09 20:21:08 +01:00
liamwhite
7944f271dc Merge pull request #10183 from liamwhite/mods
vfs_vector: avoid n^2 lookup in layeredfs building
2023-05-09 09:47:36 -04:00
liamwhite
5890b96ce5 Merge pull request #10203 from german77/calibration
core: hid: Allow to calibrate gyro sensor
2023-05-09 09:47:29 -04:00
liamwhite
1f14b58315 Merge pull request #10206 from FernandoS27/astc-3d
Texture Cache: Fix 3D ASTC textures
2023-05-09 09:47:22 -04:00
liamwhite
3f048770d3 Merge pull request #10208 from german77/amiibo_joycon
input_common: Fix nfc detection for joycons
2023-05-09 09:47:14 -04:00
german77
cb1487d774 input_common: Fix nfc detection for joycons 2023-05-09 00:32:53 -06:00
Fernando Sahmkow
8a214e5530 Texture Cache: Fix ASTC textures 2023-05-09 02:42:10 +02:00
liamwhite
15ec8d3e44 Merge pull request #10205 from jbeich/freebsd
qt_common: unbreak build on BSDs
2023-05-08 17:29:33 -04:00
Jan Beich
a4362765a6 qt_common: consistently ifdef QPlatform after cbd79df233
src/yuzu/qt_common.cpp:45:33: error: member access into incomplete type 'QPlatformNativeInterface'
    wsi.display_connection = pni->nativeResourceForWindow("display", window);
                                ^
/usr/include/qt6/QtGui/qguiapplication.h:20:7: note: forward declaration of 'QPlatformNativeInterface'
class QPlatformNativeInterface;
      ^
src/yuzu/qt_common.cpp:47:42: error: member access into incomplete type 'QPlatformNativeInterface'
        wsi.render_surface = window ? pni->nativeResourceForWindow("surface", window) : nullptr;
                                         ^
/usr/include/qt6/QtGui/qguiapplication.h:20:7: note: forward declaration of 'QPlatformNativeInterface'
class QPlatformNativeInterface;
      ^
2023-05-08 20:47:16 +00:00
Narr the Reg
e1838f51a3 yuzu: Make 3d cube with joycon shape 2023-05-08 12:06:39 -06:00
Narr the Reg
97bd6d6418 core: hid: Allow to calibrate gyro sensor 2023-05-08 12:06:38 -06:00
bunnei
b70a205a96 Merge pull request #10075 from Kelebek1/silence_nifm_spam
Silence network spam
2023-05-07 17:45:32 -07:00
bunnei
3d8eca92f9 Merge pull request #10197 from liamwhite/resume-token
bootmanager: remove stop_token header
2023-05-07 17:43:15 -07:00
bunnei
d6d60f7104 Merge pull request #10194 from bunnei/update-dynarmic-3
externals: Update dynarmic to include latest patch.
2023-05-07 17:13:32 -07:00
Liam
bdb7c11d8e bootmanager: remove stop_token header 2023-05-07 19:20:09 -04:00
liamwhite
8f605b542c Merge pull request #10195 from german77/mutex
core: hid: Update motion on a better place
2023-05-07 19:06:10 -04:00
liamwhite
2688fb1aa2 Merge pull request #10155 from FernandoS27/reactive-flushing-new
Y.F.C. bring back Reactive Flushing
2023-05-07 19:05:56 -04:00
Liam
d100de27ee vfs_layered: avoid n^2 lookup in layeredfs building 2023-05-07 19:03:41 -04:00
german77
cf023aa8ec core: hid: Update motion on a better place 2023-05-07 17:01:57 -06:00
bunnei
7eb17f3aa5 externals: Update dynarmic to include latest patch. 2023-05-07 15:06:58 -07:00
Fernando Sahmkow
8014dd8259 Texture cache: Only force flush the dma downloads 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
2df19ef0fd Buffer Cache: disable reactive flushing in it. 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
016c6feb49 Texture cache: reverse inmediate flush changes 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
36c302fa32 Buffer cache: always use async buffer downloads and fix regression. 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
6f90dff293 Address feedback, add CR notice, etc 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
ab0c0a469c Query cache: stop updating pages as it's not affected by cpu writes 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
92da86290c Settings: add option to enable / disable reactive flushing 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
0f4f18265f Texture cache: sync the first flush. 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
c6cac2ffaa GPU: Add Reactive flushing 2023-05-07 23:46:12 +02:00
Liam
5792a72c29 vfs_vector: avoid n^2 lookup in layeredfs building 2023-05-07 16:50:35 -04:00
bunnei
e58090c9c7 Merge pull request #10097 from german77/nfp_full
service: nfc: Merge device interfaces and create the device manager
2023-05-07 12:18:09 -07:00
bunnei
ed98afdebc Merge pull request #10192 from bunnei/update-dynarmic-2
externals: Update dynarmic to include latest patch.
2023-05-07 12:17:40 -07:00
bunnei
e46074a2e3 externals: Update dynarmic to include latest patch. 2023-05-07 12:04:42 -07:00
liamwhite
0c5bdc7241 Merge pull request #10081 from Kelebek1/copy_overlap_tick
Sort overlap_ids by modification tick before copy
2023-05-07 14:09:10 -04:00
liamwhite
cb49c3b64c Merge pull request #10172 from Kelebek1/debug_validation_names
Log object names with debug renderer, add a GPU address to ImageViews
2023-05-07 14:09:03 -04:00
liamwhite
35862b21d6 Merge pull request #10189 from Qigo42/master
yuzu/applets/qt_profile_select: connect double-click to accept()
2023-05-07 14:08:57 -04:00
QGJ
4366a21eae yuzu/applets/qt_profile_select: connect double-click to accept()
In the profile selection window:

Allow the user to start the game by double-clicking a profile to avoid having to additionally click the OK button. This avoids an unnecessary "step" to the start of the game...
2023-05-07 18:05:42 +02:00
Fernando S
1166ca3a57 Merge pull request #10187 from Kelebek1/address_space
Fix address space allocator slow path to avoid OOB
2023-05-07 17:06:36 +02:00
Kelebek1
6fed48b3a4 Fix address space allocator slow path to avoid OOB 2023-05-07 15:11:16 +01:00
bunnei
f36b5dfd18 Merge pull request #10180 from german77/debug
input_common: Revert debugging changes
2023-05-06 22:26:53 -07:00
german77
91695a453b input_common: Revert debugging changes 2023-05-06 23:06:44 -06:00
bunnei
3547248ec2 Merge pull request #10125 from lat9nq/vsync-select
configuration: Expose separate swap present modes
2023-05-06 21:55:39 -07:00
bunnei
82188b2f79 Merge pull request #10174 from german77/motriod
input_common: Add experimental motion to button
2023-05-06 21:40:20 -07:00
bunnei
5467d39bc3 Merge pull request #10171 from german77/powera
input_common: Add property to invert an axis button
2023-05-06 21:39:44 -07:00
liamwhite
27ff00acb1 Merge pull request #10162 from lat9nq/sdl-remove-old
yuzu-sdl,audio_core: Remove antiquated warning ignore
2023-05-06 23:10:09 -04:00
liamwhite
f1b770a8f0 Merge pull request #10165 from merryhime/update-dynarmic
externals: Update dynarmic to 6.4.7
2023-05-06 23:10:02 -04:00
liamwhite
9c9b4616c3 Merge pull request #10167 from german77/motion_preview
yuzu: Add motion preview to controller input
2023-05-06 23:09:55 -04:00
liamwhite
e7b6389e44 Merge pull request #10178 from ronikirla/2-hour-crash
Fix read access violation after 2 hours of gameplay in PMD: RTDX
2023-05-06 23:09:36 -04:00
Roni Kirla
a13fd5f7cc Fix read access violation 2023-05-07 00:13:33 +03:00
german77
9b771bcb8f input_common: Add experimental motion to button 2023-05-05 22:49:56 -06:00
Narr the Reg
94151097b9 service: nfc: Merge device interfaces and create the device manager 2023-05-05 22:02:59 -06:00
Kelebek1
ca6bf06ef7 Log object names with debug renderer, add a GPU address to ImageViews 2023-05-06 04:48:32 +01:00
Narr the Reg
f017335fef input_common: Add property to invert an axis button 2023-05-05 17:18:35 -06:00
Narr the Reg
f764223f93 input_common: Add property to invert an axis button 2023-05-05 17:14:20 -06:00
Narr the Reg
46e835f2d6 yuzu: Add motion preview to controller input 2023-05-05 13:53:38 -06:00
bunnei
bb2e407772 Merge pull request #10159 from german77/home_screenshot
core: hid: Fix state of capture and home buttons
2023-05-05 12:02:15 -07:00
marius david
0a6bd8b236 Improve emulation of HD Rumble 2023-05-05 19:30:40 +02:00
Merry
432d754d7d externals: Update dynarmic to 6.4.7 2023-05-05 16:30:18 +01:00
lat9nq
740c349820 yuzu-sdl,audio_core: Remove antiquated warning ignore
Issue was fixed a long time ago, both by SDL2 and in yuzu by including
SDL2 as a system library.
2023-05-05 03:48:28 -04:00
german77
8df3aed2f1 core: hid: Fix state of capture and home buttons 2023-05-04 22:36:59 -06:00
liamwhite
16939b1a6e Merge pull request #10128 from Kelebek1/audren_terminate
Wait for the terminate event before destroying a system instance
2023-05-04 14:44:09 -04:00
liamwhite
60d54d911e Merge pull request #10145 from Kelebek1/code_size
Fix shader code resize to use word size rather than byte size
2023-05-04 14:44:02 -04:00
liamwhite
e2b81ae5fe Merge pull request #10156 from v1993/looks-decent-to-me
Remove LGTM config
2023-05-04 14:43:55 -04:00
Valeri
b095a0242d Remove LGTM config
LGTM.com is no longer available since it was superseded by CodeQL.
2023-05-04 15:36:47 +03:00
Fernando S
2506594c50 Merge pull request #10153 from FernandoS27/a-quickie-fixie
Memory manager: Fix possible softlock
2023-05-04 03:56:53 +02:00
Fernando S
7d5683c63c Merge pull request #10154 from liamwhite/optimistic
settings: remove pessimistic flushing
2023-05-04 01:55:51 +02:00
bunnei
055ee84024 Merge pull request #10142 from FernandoS27/missing-astc
GPU: implement missing ASTC
2023-05-03 16:49:27 -07:00
Liam
ae59ffc56c settings: remove pessimistic flushing 2023-05-03 18:52:42 -04:00
Fernando Sahmkow
1c13c74295 Memory manager: Fix possible softlock 2023-05-04 00:15:21 +02:00
lat9nq
a71498d163 qt_common: Remove yuzu prefix 2023-05-03 18:11:53 -04:00
bunnei
a661c547d8 Merge pull request #10088 from FernandoS27/100-gelato-flavor-test-builds-later
Y.F.C Implement Asynchronous Fence manager and Rework Query async downloads
2023-05-03 15:10:22 -07:00
bunnei
737e1ca101 Merge pull request #10117 from liamwhite/sync-register
kernel: match calls to Register and Unregister
2023-05-03 09:07:19 -07:00
Fernando Sahmkow
87a9be8dec GPU: implement missing ASTC 2023-05-03 11:33:28 -04:00
liamwhite
ffeb5cdd8d Merge pull request #10151 from GPUCode/no-softlocks-please
Fix softlocks when disabling async present
2023-05-03 10:54:24 -04:00
Morph
3ba95402fd Merge pull request #10146 from liamwhite/catch3
catch2: update to 3.3.1
2023-05-03 10:53:12 -04:00
Morph
8dd3baa562 Merge pull request #10144 from liamwhite/dont-turbo
vulkan: disable turbo when debugging tool is attached
2023-05-03 10:53:03 -04:00
Morph
daf7936095 Merge pull request #10143 from liamwhite/fruit-company-moment
video_core: fix build on Apple Clang
2023-05-03 10:52:56 -04:00
Morph
627022bef9 Merge pull request #10124 from liamwhite/pebkac
settings: rename extended memory layout to unsafe, move from general to system
2023-05-03 10:52:45 -04:00
GPUCode
f3fcc15ad5 vk_present_manager: Fix softlocks when disabling async present 2023-05-03 07:50:10 +03:00
lat9nq
35e7f36a39 configure_graphics: No there isn't a hyphen in VSync
Most sources seem to suggest VSync and not V-Sync
2023-05-02 21:52:43 -04:00
lat9nq
d82cad3fb3 configure_input_player: Add missing include
Cleaning up includes in bootmanager and configure_graphics has exposed a
missing include here.
2023-05-02 21:52:43 -04:00
lat9nq
cd2981ee12 configure_graphics: Clean up includes [IWYU] 2023-05-02 21:52:43 -04:00
lat9nq
0c0f5b7ccc bootmanager: Clean up includes [IWYU]
bootmanager: Remove system-specific headers

IWYU can be too complete I suppose.
2023-05-02 21:52:43 -04:00
lat9nq
a546ecbb12 configure_graphics: Actively find present modes
When Vulkan devices are enumerated, this also determines the available
present modes for each device, maps them to a vector, and gives
those options to the user.
OpenGL options are limited to On/Off.

Required creating a VkSurfaceKHR during device enumeration, which
may or may not be desireable. For the sake of a less confusing UI.

Also fixes a bug where if a graphics device disappears on the host, we
don't try and select the non-existant devices.

configure_graphics: Remove vsync runtime lock for Vulkan

configure_graphics: Recommend Mailbox present mode

configure_graphics: Fix type-limits warning

configure_graphics: Clean up includes

configure_graphics: Add tooltip
2023-05-02 21:52:43 -04:00
lat9nq
952b271092 vk_swapchain: Use certain modes for unlocked
Uses mailbox, then immediate for unlocked framerate depending on
support for either. Also adds support for FIFO_RELAXED.

This function now assumes vsync_mode was originially configured to a value
that the driver supports.

vk_swapchain: ChooseSwapPresentMode determines updates

Simplifies swapchain a bit and allows us to change the present mode
during guest runtime.

vk_swapchain: Fix MSVC error

vk_swapchain: Enforce available present modes

Some frontends don't check the value of vsync_mode before comitting it.
Just as well, since a driver update or misconfiguration could problems
in the swap chain.

vk_swapchain: Silence warnings

Silences GCC warnings implicit-fallthrough and shadow, which apparently
are not enabled on clang.
2023-05-02 21:52:43 -04:00
lat9nq
a090a380be bootmanager: Remove inaccurate switch
Those vulkan settings do not correspond 1:1 to the swap intervals that
they set for OpenGL, so remove it.

bootmanager: Add missing include

I didn't add this log why did it break
2023-05-02 21:51:30 -04:00
lat9nq
cbd79df233 qt_common: Move window info function out of bootmanager
Function is useful outside of bootmanager, so put it in a common place.

qt_common: Add missing include

qt_common: Add some newlines

qt_common: Add trailing newline

qt_common: Add trainline newline
2023-05-02 21:51:30 -04:00
lat9nq
c6c11c1553 vulkan_surface: Pass only window info for surface creation
We don't need the whole EmuWindow when creating a surface,
and it creates onerous requirements outside of typical usage for
creating a surface elsewhere.
2023-05-02 21:51:30 -04:00
lat9nq
2528cf7c54 settings: Enable FIFO relaxed
Not entirely sure if we need this, but there's also no reason not to
support it.

settings: Give VSyncMode values
2023-05-02 21:51:30 -04:00
lat9nq
6b973c5986 configure_graphics: Fix another typo 2023-05-02 21:51:29 -04:00
lat9nq
40f062f749 telemetry_session: Make translate function static
Addresses review feedback

Co-authored-by: Lioncash <mathew1800@gmail.com>
2023-05-02 21:51:29 -04:00
lat9nq
29a56496bf bootmanager: Return value in impossible case
The setting is ranged, so this return statement is unreachable.
But GCC can't tell I guess.
2023-05-02 21:51:29 -04:00
lat9nq
41a103c0fc configure_graphics: Fix typo 2023-05-02 21:51:29 -04:00
lat9nq
66ed1c1872 default_ini: Update V-Sync description
default_ini: Update vsync text

default_ini: Add tooltip from configure_graphics
2023-05-02 21:51:29 -04:00
lat9nq
6f0929df82 configuration: Expose separate swap present modes
Previously, yuzu would try and guess which vsync mode to use given
different scenarios, but apparently we didn't always get it right. This
exposes the separate modes in a drop-down the user can select.

If a mode isn't available in Vulkan, it defaults to FIFO.
2023-05-02 21:51:29 -04:00
Liam
d1dd54cbfa catch2: update to 3.3.1 2023-05-02 21:27:17 -04:00
bunnei
8f43b05d6b Merge pull request #9973 from GPUCode/async-present
Implement asynchronous presentation
2023-05-02 17:54:57 -07:00
Kelebek1
f902cc2a2b Fix code resize to use word size rather than byte size 2023-05-02 23:52:21 +01:00
liamwhite
451b1bba26 Merge pull request #10133 from lat9nq/clang-shadow-and-fallthrough
CMakeLists: Enable more checks on Clang
2023-05-02 18:18:46 -04:00
liamwhite
494cc992eb Merge pull request #10130 from liamwhite/keys
qt: warn on inoperable keys
2023-05-02 18:17:24 -04:00
liamwhite
f603dc9763 Merge pull request #10123 from Kelebek1/sample_mask
Define SampleMask as an array
2023-05-02 18:17:15 -04:00
Liam
4df49631de vulkan: disable turbo when debugging tool is attached 2023-05-02 18:14:57 -04:00
Liam
e1c74cea10 video_core: fix build on Apple Clang 2023-05-02 18:05:30 -04:00
lat9nq
f7292c776b CMake: Enable type limits on Clang 2023-05-02 02:07:36 -04:00
lat9nq
2007d0e4a0 CMakeLists: Enable checks on Clang
Enables shadow-uncaptured-locals and implicit-fallthrough for Clang.
implicit-fallthrough is not enabled by default in -Wall or -Wextra, and
shadow-uncaptured-local doesn't seem to be enabled by default by
-Wshadow, even though GCC has both of these by their respective cases.
2023-05-02 01:57:22 -04:00
Liam
7ec66db22c qt: warn on inoperable keys 2023-04-30 23:47:42 -04:00
Kelebek1
2feb40f14d Wait for the terminate event before destroying a system instance 2023-05-01 00:27:12 +01:00
Liam
2cd9e1ecb6 settings: rename extended memory layout to unsafe, move from general to system 2023-04-30 14:24:22 -04:00
Kelebek1
b566c98258 Define SampleMask as an array 2023-04-30 18:37:37 +01:00
Liam
1b5c87ab6a kernel: match calls to Register and Unregister 2023-04-29 21:52:26 -04:00
Fernando Sahmkow
2f15876524 QueryCache: Fix write invalidation. 2023-04-28 23:53:46 +02:00
Fernando Sahmkow
9a7c172f76 MemoryManager: Fix race conditions. 2023-04-28 23:53:02 +02:00
german77
5e16fe4579 core: service: Add FunctionInfoTyped to allow expanding existing interfaces 2023-04-25 23:36:24 -06:00
Fernando Sahmkow
e4dc73f61e Clang format and ddress feedback 2023-04-24 12:38:47 +02:00
Narr the Reg
00d76fc5f5 service: nfc: Create mifare interface 2023-04-23 23:28:09 -06:00
Narr the Reg
a3fa64fcc4 service: nfc: Create interface 2023-04-23 23:28:09 -06:00
Fernando Sahmkow
e29ced29fa QueryCache: rework async downloads. 2023-04-23 22:04:14 +02:00
Fernando Sahmkow
7e76c1642c Accuracy Normal: reduce accuracy further for perf improvements in Project Lime 2023-04-23 22:03:44 +02:00
Fernando Sahmkow
fca72beb2d Fence Manager: implement async fence management in a sepparate thread. 2023-04-23 04:48:50 +02:00
Kelebek1
4da4ecb1ff Silence nifm spam 2023-04-22 14:29:58 +01:00
Kelebek1
3346de365a Sort overlap_ids by modification tick before copy 2023-04-22 14:02:10 +01:00
191 changed files with 4272 additions and 3385 deletions

View File

@@ -1,13 +0,0 @@
# SPDX-FileCopyrightText: 2020 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
path_classifiers:
library: "externals"
extraction:
cpp:
prepare:
packages:
- "libsdl2-dev"
- "qtmultimedia5-dev"
- "libtbb-dev"
- "libjack-jackd2-dev"

View File

@@ -83,5 +83,3 @@ If you wish to support us a different way, please join our [Discord](https://dis
## License
yuzu is licensed under the GPLv3 (or any later version). Refer to the [LICENSE.txt](https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt) file.
The [Skyline-Emulator Team](https://github.com/skyline-emu/skyline) may choose to use the code from these contributors under the GPL-3.0-or-later OR MPL-2.0: [FernandoS27](https://github.com/FernandoS27), [lioncash](https://github.com/lioncash), [bunnei](https://github.com/bunnei), [ReinUsesLisp](https://github.com/ReinUsesLisp), [Morph1984](https://github.com/Morph1984), [ogniK5377](https://github.com/ogniK5377), [german77](https://github.com/german77), [ameerj](https://github.com/ameerj), [Kelebek1](https://github.com/Kelebek1) and [lat9nq](https://github.com/lat9nq)

View File

@@ -113,6 +113,9 @@ else()
$<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init>
$<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field>
$<$<CXX_COMPILER_ID:Clang>:-Werror=shadow-uncaptured-local>
$<$<CXX_COMPILER_ID:Clang>:-Werror=implicit-fallthrough>
$<$<CXX_COMPILER_ID:Clang>:-Werror=type-limits>
$<$<CXX_COMPILER_ID:AppleClang>:-Wno-braced-scalar-init>
$<$<CXX_COMPILER_ID:AppleClang>:-Wno-unused-private-field>
)

View File

@@ -436,10 +436,7 @@ void System::Stop() {
}
if (execution_mode == ExecutionMode::Auto) {
// Should wait for the system to terminate here, but core timing (should have) already
// stopped, so this isn't needed. Find a way to make this definite.
// terminate_event.Wait();
terminate_event.Wait();
}
}

View File

@@ -3,6 +3,7 @@
#include <span>
#include <vector>
#include <SDL.h>
#include "audio_core/common/common.h"
#include "audio_core/sink/sdl2_sink.h"
@@ -10,16 +11,6 @@
#include "common/logging/log.h"
#include "core/core.h"
// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#endif
#include <SDL.h>
#ifdef __clang__
#pragma clang diagnostic pop
#endif
namespace AudioCore::Sink {
/**
* SDL sink stream, responsible for sinking samples to hardware.

View File

@@ -72,7 +72,7 @@ MAP_MEMBER(void)::MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInf
}
}()};
if (block_end_predecessor->virt >= virt) {
if (block_end_predecessor != blocks.begin() && block_end_predecessor->virt >= virt) {
// If this block's start would be overlapped by the map then reuse it as a tail
// block
block_end_predecessor->virt = virt_end;
@@ -336,7 +336,7 @@ ALLOC_MEMBER(VaType)::Allocate(VaType size) {
ASSERT_MSG(false, "Unexpected allocator state!");
}
auto search_predecessor{this->blocks.begin()};
auto search_predecessor{std::next(this->blocks.begin())};
auto search_successor{std::next(search_predecessor)};
while (search_successor != this->blocks.end() &&

View File

@@ -111,6 +111,8 @@ struct AnalogProperties {
float offset{};
// Invert direction of the sensor data
bool inverted{};
// Invert the state if it's converted to a button
bool inverted_button{};
// Press once to activate, press again to release
bool toggle{};
};

View File

@@ -23,7 +23,10 @@ public:
buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {}
~ScratchBuffer() = default;
ScratchBuffer(const ScratchBuffer&) = delete;
ScratchBuffer& operator=(const ScratchBuffer&) = delete;
ScratchBuffer(ScratchBuffer&&) = default;
ScratchBuffer& operator=(ScratchBuffer&&) = default;
/// This will only grow the buffer's capacity if size is greater than the current capacity.
/// The previously held data will remain intact.
@@ -87,6 +90,12 @@ public:
return buffer_capacity;
}
void swap(ScratchBuffer& other) noexcept {
std::swap(last_requested_size, other.last_requested_size);
std::swap(buffer_capacity, other.buffer_capacity);
std::swap(buffer, other.buffer);
}
private:
size_t last_requested_size{};
size_t buffer_capacity{};

View File

@@ -45,6 +45,7 @@ void LogSettings() {
log_setting("System_LanguageIndex", values.language_index.GetValue());
log_setting("System_RegionIndex", values.region_index.GetValue());
log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue());
log_setting("System_UnsafeMemoryLayout", values.use_unsafe_extended_memory_layout.GetValue());
log_setting("Core_UseMultiCore", values.use_multi_core.GetValue());
log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue());
@@ -60,7 +61,8 @@ void LogSettings() {
log_setting("Renderer_NvdecEmulation", values.nvdec_emulation.GetValue());
log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue());
log_setting("Renderer_AsyncASTC", values.async_astc.GetValue());
log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
log_setting("Renderer_UseVsync", values.vsync_mode.GetValue());
log_setting("Renderer_UseReactiveFlushing", values.use_reactive_flushing.GetValue());
log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
@@ -191,7 +193,7 @@ void RestoreGlobalState(bool is_powered_on) {
// Core
values.use_multi_core.SetGlobal(true);
values.use_extended_memory_layout.SetGlobal(true);
values.use_unsafe_extended_memory_layout.SetGlobal(true);
// CPU
values.cpu_accuracy.SetGlobal(true);
@@ -222,11 +224,10 @@ void RestoreGlobalState(bool is_powered_on) {
values.nvdec_emulation.SetGlobal(true);
values.accelerate_astc.SetGlobal(true);
values.async_astc.SetGlobal(true);
values.use_vsync.SetGlobal(true);
values.use_reactive_flushing.SetGlobal(true);
values.shader_backend.SetGlobal(true);
values.use_asynchronous_shaders.SetGlobal(true);
values.use_fast_gpu_time.SetGlobal(true);
values.use_pessimistic_flushes.SetGlobal(true);
values.use_vulkan_driver_pipeline_cache.SetGlobal(true);
values.bg_red.SetGlobal(true);
values.bg_green.SetGlobal(true);

View File

@@ -16,6 +16,13 @@
namespace Settings {
enum class VSyncMode : u32 {
Immediate = 0,
Mailbox = 1,
FIFO = 2,
FIFORelaxed = 3,
};
enum class RendererBackend : u32 {
OpenGL = 0,
Vulkan = 1,
@@ -388,7 +395,8 @@ struct Values {
// Core
SwitchableSetting<bool> use_multi_core{true, "use_multi_core"};
SwitchableSetting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"};
SwitchableSetting<bool> use_unsafe_extended_memory_layout{false,
"use_unsafe_extended_memory_layout"};
// Cpu
SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
@@ -455,12 +463,13 @@ struct Values {
SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
SwitchableSetting<bool> async_astc{false, "async_astc"};
SwitchableSetting<bool> use_vsync{true, "use_vsync"};
Setting<VSyncMode, true> vsync_mode{VSyncMode::FIFO, VSyncMode::Immediate,
VSyncMode::FIFORelaxed, "use_vsync"};
SwitchableSetting<bool> use_reactive_flushing{true, "use_reactive_flushing"};
SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL,
ShaderBackend::SPIRV, "shader_backend"};
SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
SwitchableSetting<bool> use_pessimistic_flushes{false, "use_pessimistic_flushes"};
SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{true,
"use_vulkan_driver_pipeline_cache"};
@@ -526,6 +535,8 @@ struct Values {
Setting<bool> enable_ir_sensor{false, "enable_ir_sensor"};
Setting<std::string> ir_sensor_device{"auto", "ir_sensor_device"};
Setting<bool> random_amiibo_id{false, "random_amiibo_id"};
// Data Storage
Setting<bool> use_virtual_sd{true, "use_virtual_sd"};
Setting<bool> gamecard_inserted{false, "gamecard_inserted"};

View File

@@ -259,6 +259,20 @@ public:
return *this;
}
void RotateFromOrigin(float roll, float pitch, float yaw) {
float temp = y;
y = std::cos(roll) * y - std::sin(roll) * z;
z = std::sin(roll) * temp + std::cos(roll) * z;
temp = x;
x = std::cos(pitch) * x + std::sin(pitch) * z;
z = -std::sin(pitch) * temp + std::cos(pitch) * z;
temp = x;
x = std::cos(yaw) * x - std::sin(yaw) * y;
y = std::sin(yaw) * temp + std::cos(yaw) * y;
}
[[nodiscard]] constexpr T Length2() const {
return x * x + y * y + z * z;
}

View File

@@ -555,21 +555,22 @@ add_library(core STATIC
hle/service/mnpp/mnpp_app.h
hle/service/ncm/ncm.cpp
hle/service/ncm/ncm.h
hle/service/nfc/mifare_user.cpp
hle/service/nfc/mifare_user.h
hle/service/nfc/common/amiibo_crypto.cpp
hle/service/nfc/common/amiibo_crypto.h
hle/service/nfc/common/device.cpp
hle/service/nfc/common/device.h
hle/service/nfc/common/device_manager.cpp
hle/service/nfc/common/device_manager.h
hle/service/nfc/mifare_result.h
hle/service/nfc/mifare_types.h
hle/service/nfc/nfc.cpp
hle/service/nfc/nfc.h
hle/service/nfc/nfc_device.cpp
hle/service/nfc/nfc_device.h
hle/service/nfc/nfc_interface.cpp
hle/service/nfc/nfc_interface.h
hle/service/nfc/nfc_result.h
hle/service/nfc/nfc_user.cpp
hle/service/nfc/nfc_user.h
hle/service/nfp/amiibo_crypto.cpp
hle/service/nfp/amiibo_crypto.h
hle/service/nfc/nfc_types.h
hle/service/nfp/nfp.cpp
hle/service/nfp/nfp.h
hle/service/nfp/nfp_device.cpp
hle/service/nfp/nfp_device.h
hle/service/nfp/nfp_interface.cpp
hle/service/nfp/nfp_interface.h
hle/service/nfp/nfp_result.h

View File

@@ -137,7 +137,7 @@ struct System::Impl {
device_memory = std::make_unique<Core::DeviceMemory>();
is_multicore = Settings::values.use_multi_core.GetValue();
extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue();
extended_memory_layout = Settings::values.use_unsafe_extended_memory_layout.GetValue();
core_timing.SetMulticore(is_multicore);
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
@@ -169,7 +169,7 @@ struct System::Impl {
void ReinitializeIfNecessary(System& system) {
const bool must_reinitialize =
is_multicore != Settings::values.use_multi_core.GetValue() ||
extended_memory_layout != Settings::values.use_extended_memory_layout.GetValue();
extended_memory_layout != Settings::values.use_unsafe_extended_memory_layout.GetValue();
if (!must_reinitialize) {
return;
@@ -178,7 +178,7 @@ struct System::Impl {
LOG_DEBUG(Kernel, "Re-initializing");
is_multicore = Settings::values.use_multi_core.GetValue();
extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue();
extended_memory_layout = Settings::values.use_unsafe_extended_memory_layout.GetValue();
Initialize(system);
}
@@ -293,6 +293,7 @@ struct System::Impl {
ASSERT(Kernel::KProcess::Initialize(main_process, system, "main",
Kernel::KProcess::ProcessType::Userland, resource_limit)
.IsSuccess());
Kernel::KProcess::Register(system.Kernel(), main_process);
kernel.MakeApplicationProcess(main_process);
const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
if (load_result != Loader::ResultStatus::Success) {
@@ -611,6 +612,10 @@ void System::PrepareReschedule(const u32 core_index) {
impl->kernel.PrepareReschedule(core_index);
}
size_t System::GetCurrentHostThreadID() const {
return impl->kernel.GetCurrentHostThreadID();
}
PerfStatsResults System::GetAndResetPerfStats() {
return impl->GetAndResetPerfStats();
}

View File

@@ -222,6 +222,8 @@ public:
/// Prepare the core emulation for a reschedule
void PrepareReschedule(u32 core_index);
[[nodiscard]] size_t GetCurrentHostThreadID() const;
/// Gets and resets core performance statistics
[[nodiscard]] PerfStatsResults GetAndResetPerfStats();

View File

@@ -82,9 +82,9 @@ std::string GetFutureSaveDataPath(SaveDataSpaceId space_id, SaveDataType type, u
// Only detect account/device saves from the future location.
switch (type) {
case SaveDataType::SaveData:
return fmt::format("{}/account/{}/{:016X}/1", space_id_path, uuid.RawString(), title_id);
return fmt::format("{}/account/{}/{:016X}/0", space_id_path, uuid.RawString(), title_id);
case SaveDataType::DeviceSaveData:
return fmt::format("{}/device/{:016X}/1", space_id_path, title_id);
return fmt::format("{}/device/{:016X}/0", space_id_path, title_id);
default:
return "";
}

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <set>
#include <utility>
#include "core/file_sys/vfs_layered.h"
@@ -58,11 +59,13 @@ std::string LayeredVfsDirectory::GetFullPath() const {
std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
std::vector<VirtualFile> out;
std::set<std::string, std::less<>> out_names;
for (const auto& layer : dirs) {
for (const auto& file : layer->GetFiles()) {
if (std::find_if(out.begin(), out.end(), [&file](const VirtualFile& comp) {
return comp->GetName() == file->GetName();
}) == out.end()) {
auto file_name = file->GetName();
if (!out_names.contains(file_name)) {
out_names.emplace(std::move(file_name));
out.push_back(file);
}
}

View File

@@ -67,6 +67,23 @@ VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_,
VectorVfsDirectory::~VectorVfsDirectory() = default;
VirtualFile VectorVfsDirectory::GetFile(std::string_view file_name) const {
if (!optimized_file_index_built) {
optimized_file_index.clear();
for (size_t i = 0; i < files.size(); i++) {
optimized_file_index.emplace(files[i]->GetName(), i);
}
optimized_file_index_built = true;
}
const auto it = optimized_file_index.find(file_name);
if (it != optimized_file_index.end()) {
return files[it->second];
}
return nullptr;
}
std::vector<VirtualFile> VectorVfsDirectory::GetFiles() const {
return files;
}
@@ -107,6 +124,7 @@ bool VectorVfsDirectory::DeleteSubdirectory(std::string_view subdir_name) {
}
bool VectorVfsDirectory::DeleteFile(std::string_view file_name) {
optimized_file_index_built = false;
return FindAndRemoveVectorElement(files, file_name);
}
@@ -124,6 +142,7 @@ VirtualFile VectorVfsDirectory::CreateFile(std::string_view file_name) {
}
void VectorVfsDirectory::AddFile(VirtualFile file) {
optimized_file_index_built = false;
files.push_back(std::move(file));
}

View File

@@ -105,6 +105,7 @@ public:
VirtualDir parent = nullptr);
~VectorVfsDirectory() override;
VirtualFile GetFile(std::string_view file_name) const override;
std::vector<VirtualFile> GetFiles() const override;
std::vector<VirtualDir> GetSubdirectories() const override;
bool IsWritable() const override;
@@ -126,6 +127,9 @@ private:
VirtualDir parent;
std::string name;
mutable std::map<std::string, size_t, std::less<>> optimized_file_index;
mutable bool optimized_file_index_built{};
};
} // namespace FileSys

View File

@@ -14,7 +14,7 @@ void DefaultCabinetApplet::Close() const {}
void DefaultCabinetApplet::ShowCabinetApplet(
const CabinetCallback& callback, const CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const {
std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const {
LOG_WARNING(Service_AM, "(STUBBED) called");
callback(false, {});
}

View File

@@ -7,9 +7,9 @@
#include "core/frontend/applets/applet.h"
#include "core/hle/service/nfp/nfp_types.h"
namespace Service::NFP {
class NfpDevice;
} // namespace Service::NFP
namespace Service::NFC {
class NfcDevice;
} // namespace Service::NFC
namespace Core::Frontend {
@@ -26,14 +26,14 @@ public:
virtual ~CabinetApplet();
virtual void ShowCabinetApplet(const CabinetCallback& callback,
const CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const = 0;
std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const = 0;
};
class DefaultCabinetApplet final : public CabinetApplet {
public:
void Close() const override;
void ShowCabinetApplet(const CabinetCallback& callback, const CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override;
std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const override;
};
} // namespace Core::Frontend

View File

@@ -376,6 +376,7 @@ void EmulatedController::ReloadInput() {
motion.accel = emulated_motion.GetAcceleration();
motion.gyro = emulated_motion.GetGyroscope();
motion.rotation = emulated_motion.GetRotations();
motion.euler = emulated_motion.GetEulerAngles();
motion.orientation = emulated_motion.GetOrientation();
motion.is_at_rest = !emulated_motion.IsMoving(motion_sensitivity);
}
@@ -551,6 +552,8 @@ void EmulatedController::EnableSystemButtons() {
void EmulatedController::DisableSystemButtons() {
std::scoped_lock lock{mutex};
system_buttons_enabled = false;
controller.home_button_state.raw = 0;
controller.capture_button_state.raw = 0;
}
void EmulatedController::ResetSystemButtons() {
@@ -685,6 +688,12 @@ void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage
ReloadInput();
}
void EmulatedController::StartMotionCalibration() {
for (ControllerMotionInfo& motion : controller.motion_values) {
motion.emulated.Calibrate();
}
}
void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
Common::UUID uuid) {
if (index >= controller.button_values.size()) {
@@ -734,6 +743,8 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
if (is_configuring) {
controller.npad_button_state.raw = NpadButton::None;
controller.debug_pad_button_state.raw = 0;
controller.home_button_state.raw = 0;
controller.capture_button_state.raw = 0;
lock.unlock();
TriggerOnChange(ControllerTriggerType::Button, false);
return;
@@ -974,16 +985,12 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
emulated.SetUserGyroThreshold(raw_status.gyro.x.properties.threshold);
emulated.UpdateRotation(raw_status.delta_timestamp);
emulated.UpdateOrientation(raw_status.delta_timestamp);
force_update_motion = raw_status.force_update;
if (is_configuring) {
return;
}
auto& motion = controller.motion_state[index];
motion.accel = emulated.GetAcceleration();
motion.gyro = emulated.GetGyroscope();
motion.rotation = emulated.GetRotations();
motion.euler = emulated.GetEulerAngles();
motion.orientation = emulated.GetOrientation();
motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
}
@@ -1616,19 +1623,6 @@ NpadGcTriggerState EmulatedController::GetTriggers() const {
MotionState EmulatedController::GetMotions() const {
std::unique_lock lock{mutex};
// Some drivers like mouse motion need constant refreshing
if (force_update_motion) {
for (auto& device : motion_devices) {
if (!device) {
continue;
}
lock.unlock();
device->ForceUpdate();
lock.lock();
}
}
return controller.motion_state;
}
@@ -1694,8 +1688,21 @@ void EmulatedController::DeleteCallback(int key) {
callback_list.erase(iterator);
}
void EmulatedController::TurboButtonUpdate() {
void EmulatedController::StatusUpdate() {
turbo_button_state = (turbo_button_state + 1) % (TURBO_BUTTON_DELAY * 2);
// Some drivers like key motion need constant refreshing
for (std::size_t index = 0; index < motion_devices.size(); ++index) {
const auto& raw_status = controller.motion_values[index].raw_status;
auto& device = motion_devices[index];
if (!raw_status.force_update) {
continue;
}
if (!device) {
continue;
}
device->ForceUpdate();
}
}
NpadButton EmulatedController::GetTurboButtonMask() const {

View File

@@ -106,6 +106,7 @@ struct ControllerMotion {
Common::Vec3f accel{};
Common::Vec3f gyro{};
Common::Vec3f rotation{};
Common::Vec3f euler{};
std::array<Common::Vec3f, 3> orientation{};
bool is_at_rest{};
};
@@ -289,6 +290,9 @@ public:
*/
void SetMotionParam(std::size_t index, Common::ParamPackage param);
/// Auto calibrates the current motion devices
void StartMotionCalibration();
/// Returns the latest button status from the controller with parameters
ButtonValues GetButtonsValues() const;
@@ -414,8 +418,8 @@ public:
*/
void DeleteCallback(int key);
/// Swaps the state of the turbo buttons
void TurboButtonUpdate();
/// Swaps the state of the turbo buttons and updates motion input
void StatusUpdate();
private:
/// creates input devices from params
@@ -527,7 +531,6 @@ private:
bool is_configuring{false};
bool system_buttons_enabled{true};
f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
bool force_update_motion{false};
u32 turbo_button_state{0};
// Temporary values to avoid doing changes while the controller is in configuring mode

View File

@@ -54,6 +54,7 @@ Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatu
case Common::Input::InputType::Analog:
status.value = TransformToTrigger(callback).pressed.value;
status.toggle = callback.analog_status.properties.toggle;
status.inverted = callback.analog_status.properties.inverted_button;
break;
case Common::Input::InputType::Trigger:
status.value = TransformToTrigger(callback).pressed.value;
@@ -61,6 +62,9 @@ Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatu
case Common::Input::InputType::Button:
status = callback.button_status;
break;
case Common::Input::InputType::Motion:
status.value = std::abs(callback.motion_status.gyro.x.raw_value) > 1.0f;
break;
default:
LOG_ERROR(Input, "Conversion from type {} to button not implemented", callback.type);
break;
@@ -82,7 +86,7 @@ Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatu
.range = 1.0f,
.offset = 0.0f,
};
status.delta_timestamp = 5000;
status.delta_timestamp = 1000;
status.force_update = true;
status.accel.x = {
.value = 0.0f,
@@ -226,6 +230,10 @@ Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackSta
status = callback.trigger_status;
calculate_button_value = false;
break;
case Common::Input::InputType::Motion:
status.analog.properties.range = 1.0f;
raw_value = callback.motion_status.accel.x.raw_value;
break;
default:
LOG_ERROR(Input, "Conversion from type {} to trigger not implemented", callback.type);
break;

View File

@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cmath>
#include "common/math_util.h"
#include "core/hid/motion_input.h"
@@ -35,11 +37,17 @@ void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
gyro.y = std::clamp(gyro.y, -GyroMaxValue, GyroMaxValue);
gyro.z = std::clamp(gyro.z, -GyroMaxValue, GyroMaxValue);
// Auto adjust drift to minimize drift
// Auto adjust gyro_bias to minimize drift
if (!IsMoving(IsAtRestRelaxed)) {
gyro_bias = (gyro_bias * 0.9999f) + (gyroscope * 0.0001f);
}
// Adjust drift when calibration mode is enabled
if (calibration_mode) {
gyro_bias = (gyro_bias * 0.99f) + (gyroscope * 0.01f);
StopCalibration();
}
if (gyro.Length() < gyro_threshold * user_gyro_threshold) {
gyro = {};
} else {
@@ -51,6 +59,20 @@ void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) {
quat = quaternion;
}
void MotionInput::SetEulerAngles(const Common::Vec3f& euler_angles) {
const float cr = std::cos(euler_angles.x * 0.5f);
const float sr = std::sin(euler_angles.x * 0.5f);
const float cp = std::cos(euler_angles.y * 0.5f);
const float sp = std::sin(euler_angles.y * 0.5f);
const float cy = std::cos(euler_angles.z * 0.5f);
const float sy = std::sin(euler_angles.z * 0.5f);
quat.w = cr * cp * cy + sr * sp * sy;
quat.xyz.x = sr * cp * cy - cr * sp * sy;
quat.xyz.y = cr * sp * cy + sr * cp * sy;
quat.xyz.z = cr * cp * sy - sr * sp * cy;
}
void MotionInput::SetGyroBias(const Common::Vec3f& bias) {
gyro_bias = bias;
}
@@ -91,6 +113,19 @@ void MotionInput::UpdateRotation(u64 elapsed_time) {
rotations += gyro * sample_period;
}
void MotionInput::Calibrate() {
calibration_mode = true;
calibration_counter = 0;
}
void MotionInput::StopCalibration() {
if (calibration_counter++ > CalibrationSamples) {
calibration_mode = false;
ResetQuaternion();
ResetRotations();
}
}
// Based on Madgwick's implementation of Mayhony's AHRS algorithm.
// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs
void MotionInput::UpdateOrientation(u64 elapsed_time) {
@@ -222,6 +257,26 @@ Common::Vec3f MotionInput::GetRotations() const {
return rotations;
}
Common::Vec3f MotionInput::GetEulerAngles() const {
// roll (x-axis rotation)
const float sinr_cosp = 2 * (quat.w * quat.xyz.x + quat.xyz.y * quat.xyz.z);
const float cosr_cosp = 1 - 2 * (quat.xyz.x * quat.xyz.x + quat.xyz.y * quat.xyz.y);
// pitch (y-axis rotation)
const float sinp = std::sqrt(1 + 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z));
const float cosp = std::sqrt(1 - 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z));
// yaw (z-axis rotation)
const float siny_cosp = 2 * (quat.w * quat.xyz.z + quat.xyz.x * quat.xyz.y);
const float cosy_cosp = 1 - 2 * (quat.xyz.y * quat.xyz.y + quat.xyz.z * quat.xyz.z);
return {
std::atan2(sinr_cosp, cosr_cosp),
2 * std::atan2(sinp, cosp) - Common::PI / 2,
std::atan2(siny_cosp, cosy_cosp),
};
}
void MotionInput::ResetOrientation() {
if (!reset_enabled || only_accelerometer) {
return;

View File

@@ -23,6 +23,8 @@ public:
static constexpr float GyroMaxValue = 5.0f;
static constexpr float AccelMaxValue = 7.0f;
static constexpr std::size_t CalibrationSamples = 300;
explicit MotionInput();
MotionInput(const MotionInput&) = default;
@@ -35,6 +37,7 @@ public:
void SetAcceleration(const Common::Vec3f& acceleration);
void SetGyroscope(const Common::Vec3f& gyroscope);
void SetQuaternion(const Common::Quaternion<f32>& quaternion);
void SetEulerAngles(const Common::Vec3f& euler_angles);
void SetGyroBias(const Common::Vec3f& bias);
void SetGyroThreshold(f32 threshold);
@@ -48,17 +51,21 @@ public:
void UpdateRotation(u64 elapsed_time);
void UpdateOrientation(u64 elapsed_time);
void Calibrate();
[[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
[[nodiscard]] Common::Vec3f GetAcceleration() const;
[[nodiscard]] Common::Vec3f GetGyroscope() const;
[[nodiscard]] Common::Vec3f GetGyroBias() const;
[[nodiscard]] Common::Vec3f GetRotations() const;
[[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
[[nodiscard]] Common::Vec3f GetEulerAngles() const;
[[nodiscard]] bool IsMoving(f32 sensitivity) const;
[[nodiscard]] bool IsCalibrated(f32 sensitivity) const;
private:
void StopCalibration();
void ResetOrientation();
void SetOrientationFromAccelerometer();
@@ -101,6 +108,12 @@ private:
// Use accelerometer values to calculate position
bool only_accelerometer = true;
// When enabled it will aggressively adjust for gyro drift
bool calibration_mode = false;
// Used to auto disable calibration mode
std::size_t calibration_counter = 0;
};
} // namespace Core::HID

View File

@@ -35,12 +35,13 @@ namespace {
using namespace Common::Literals;
u32 GetMemorySizeForInit() {
return Settings::values.use_extended_memory_layout ? Smc::MemorySize_8GB : Smc::MemorySize_4GB;
return Settings::values.use_unsafe_extended_memory_layout ? Smc::MemorySize_8GB
: Smc::MemorySize_4GB;
}
Smc::MemoryArrangement GetMemoryArrangeForInit() {
return Settings::values.use_extended_memory_layout ? Smc::MemoryArrangement_8GB
: Smc::MemoryArrangement_4GB;
return Settings::values.use_unsafe_extended_memory_layout ? Smc::MemoryArrangement_8GB
: Smc::MemoryArrangement_4GB;
}
} // namespace

View File

@@ -182,8 +182,8 @@ public:
explicit KAutoObjectWithList(KernelCore& kernel) : KAutoObject(kernel) {}
static int Compare(const KAutoObjectWithList& lhs, const KAutoObjectWithList& rhs) {
const u64 lid = lhs.GetId();
const u64 rid = rhs.GetId();
const uintptr_t lid = reinterpret_cast<uintptr_t>(std::addressof(lhs));
const uintptr_t rid = reinterpret_cast<uintptr_t>(std::addressof(rhs));
if (lid < rid) {
return -1;

View File

@@ -95,7 +95,7 @@ struct KernelCore::Impl {
pt_heap_region.GetSize());
}
InitializeHackSharedMemory();
InitializeHackSharedMemory(kernel);
RegisterHostThread(nullptr);
}
@@ -216,10 +216,12 @@ struct KernelCore::Impl {
auto* main_thread{Kernel::KThread::Create(system.Kernel())};
main_thread->SetCurrentCore(core);
ASSERT(Kernel::KThread::InitializeMainThread(system, main_thread, core).IsSuccess());
KThread::Register(system.Kernel(), main_thread);
auto* idle_thread{Kernel::KThread::Create(system.Kernel())};
idle_thread->SetCurrentCore(core);
ASSERT(Kernel::KThread::InitializeIdleThread(system, idle_thread, core).IsSuccess());
KThread::Register(system.Kernel(), idle_thread);
schedulers[i]->Initialize(main_thread, idle_thread, core);
}
@@ -230,6 +232,7 @@ struct KernelCore::Impl {
const Core::Timing::CoreTiming& core_timing) {
system_resource_limit = KResourceLimit::Create(system.Kernel());
system_resource_limit->Initialize(&core_timing);
KResourceLimit::Register(kernel, system_resource_limit);
const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()};
const auto total_size{sizes.first};
@@ -355,6 +358,7 @@ struct KernelCore::Impl {
ASSERT(KThread::InitializeHighPriorityThread(system, shutdown_threads[core_id], {}, {},
core_id)
.IsSuccess());
KThread::Register(system.Kernel(), shutdown_threads[core_id]);
}
}
@@ -729,7 +733,7 @@ struct KernelCore::Impl {
memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize());
}
void InitializeHackSharedMemory() {
void InitializeHackSharedMemory(KernelCore& kernel) {
// Setup memory regions for emulated processes
// TODO(bunnei): These should not be hardcoded regions initialized within the kernel
constexpr std::size_t hid_size{0x40000};
@@ -746,14 +750,23 @@ struct KernelCore::Impl {
hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
Svc::MemoryPermission::Read, hid_size);
KSharedMemory::Register(kernel, hid_shared_mem);
font_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
Svc::MemoryPermission::Read, font_size);
KSharedMemory::Register(kernel, font_shared_mem);
irs_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
Svc::MemoryPermission::Read, irs_size);
KSharedMemory::Register(kernel, irs_shared_mem);
time_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
Svc::MemoryPermission::Read, time_size);
KSharedMemory::Register(kernel, time_shared_mem);
hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
Svc::MemoryPermission::Read, hidbus_size);
KSharedMemory::Register(kernel, hidbus_shared_mem);
}
std::mutex registered_objects_lock;
@@ -1072,12 +1085,15 @@ static std::jthread RunHostThreadFunc(KernelCore& kernel, KProcess* process,
// Commit the thread reservation.
thread_reservation.Commit();
// Register the thread.
KThread::Register(kernel, thread);
return std::jthread(
[&kernel, thread, thread_name{std::move(thread_name)}, func{std::move(func)}] {
// Set the thread name.
Common::SetCurrentThreadName(thread_name.c_str());
// Register the thread.
// Set the thread as current.
kernel.RegisterHostThread(thread);
// Run the callback.
@@ -1099,6 +1115,9 @@ std::jthread KernelCore::RunOnHostCoreProcess(std::string&& process_name,
// Ensure that we don't hold onto any extra references.
SCOPE_EXIT({ process->Close(); });
// Register the new process.
KProcess::Register(*this, process);
// Run the host thread.
return RunHostThreadFunc(*this, process, std::move(process_name), std::move(func));
}
@@ -1124,6 +1143,9 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function
// Ensure that we don't hold onto any extra references.
SCOPE_EXIT({ process->Close(); });
// Register the new process.
KProcess::Register(*this, process);
// Reserve a new thread from the process resource limit.
KScopedResourceReservation thread_reservation(process, LimitableResource::ThreadCountMax);
ASSERT(thread_reservation.Succeeded());
@@ -1136,6 +1158,9 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function
// Commit the thread reservation.
thread_reservation.Commit();
// Register the new thread.
KThread::Register(*this, thread);
// Begin running the thread.
ASSERT(R_SUCCEEDED(thread->Run()));
}

View File

@@ -13,6 +13,7 @@
#include "core/file_sys/savedata_factory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/result.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
@@ -1335,7 +1336,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{24, nullptr, "GetLaunchStorageInfoForDebug"},
{25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"},
{26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"},
{27, nullptr, "CreateCacheStorage"},
{27, &IApplicationFunctions::CreateCacheStorage, "CreateCacheStorage"},
{28, nullptr, "GetSaveDataSizeMax"},
{29, nullptr, "GetCacheStorageMax"},
{30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
@@ -1738,6 +1739,36 @@ void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) {
rb.Push(size.journal);
}
void IApplicationFunctions::CreateCacheStorage(HLERequestContext& ctx) {
struct InputParameters {
u16 index;
s64 size;
s64 journal_size;
};
static_assert(sizeof(InputParameters) == 24);
struct OutputParameters {
u32 storage_target;
u64 required_size;
};
static_assert(sizeof(OutputParameters) == 16);
IPC::RequestParser rp{ctx};
const auto params = rp.PopRaw<InputParameters>();
LOG_WARNING(Service_AM, "(STUBBED) called with index={}, size={:#x}, journal_size={:#x}",
params.index, params.size, params.journal_size);
const OutputParameters resp{
.storage_target = 1,
.required_size = 0,
};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(resp);
}
void IApplicationFunctions::QueryApplicationPlayStatistics(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");

View File

@@ -333,6 +333,7 @@ private:
void GetPseudoDeviceId(HLERequestContext& ctx);
void ExtendSaveData(HLERequestContext& ctx);
void GetSaveDataSize(HLERequestContext& ctx);
void CreateCacheStorage(HLERequestContext& ctx);
void BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx);
void EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx);
void BeginBlockingHomeButton(HLERequestContext& ctx);

View File

@@ -11,7 +11,7 @@
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applet_cabinet.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/nfp/nfp_device.h"
#include "core/hle/service/nfc/common/device.h"
namespace Service::AM::Applets {
@@ -72,10 +72,10 @@ void Cabinet::Execute() {
// TODO: listen on all controllers
if (nfp_device == nullptr) {
nfp_device = std::make_shared<Service::NFP::NfpDevice>(
nfp_device = std::make_shared<Service::NFC::NfcDevice>(
system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event);
nfp_device->Initialize();
nfp_device->StartDetection(Service::NFP::TagProtocol::All);
nfp_device->StartDetection(Service::NFC::NfcProtocol::All);
}
const Core::Frontend::CabinetParameters parameters{
@@ -106,20 +106,22 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
Cancel();
}
if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound &&
nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) {
if (nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagFound &&
nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagMounted) {
Cancel();
}
if (nfp_device->GetCurrentState() == Service::NFP::DeviceState::TagFound) {
nfp_device->Mount(Service::NFP::MountTarget::All);
if (nfp_device->GetCurrentState() == Service::NFC::DeviceState::TagFound) {
nfp_device->Mount(Service::NFP::ModelType::Amiibo, Service::NFP::MountTarget::All);
}
switch (applet_input_common.applet_mode) {
case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: {
Service::NFP::AmiiboName name{};
std::memcpy(name.data(), amiibo_name.data(), std::min(amiibo_name.size(), name.size() - 1));
nfp_device->SetRegisterInfoPrivate(name);
Service::NFP::RegisterInfoPrivate register_info{};
std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(),
std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1));
nfp_device->SetRegisterInfoPrivate(register_info);
break;
}
case Service::NFP::CabinetMode::StartGameDataEraser:
@@ -139,7 +141,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
applet_output.device_handle = applet_input_common.device_handle;
applet_output.result = CabinetResult::Cancel;
const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info);
const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info);
const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info, false);
nfp_device->Finalize();
if (reg_result.IsSuccess()) {

View File

@@ -19,8 +19,8 @@ namespace Core {
class System;
} // namespace Core
namespace Service::NFP {
class NfpDevice;
namespace Service::NFC {
class NfcDevice;
}
namespace Service::AM::Applets {
@@ -96,7 +96,7 @@ private:
Core::System& system;
bool is_complete{false};
std::shared_ptr<Service::NFP::NfpDevice> nfp_device;
std::shared_ptr<Service::NFC::NfcDevice> nfp_device;
Kernel::KEvent* availability_change_event;
KernelHelpers::ServiceContext service_context;
StartParamForAmiiboSettings applet_input_common{};

View File

@@ -24,8 +24,10 @@
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/system_archive/system_archive.h"
#include "core/file_sys/vfs.h"
#include "core/hle/result.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp_srv.h"
#include "core/hle/service/hle_ipc.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/reporter.h"
@@ -552,9 +554,9 @@ public:
// Write the data to memory
ctx.WriteBuffer(begin, range_size);
IPC::ResponseBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u32>(static_cast<u32>(actual_entries));
rb.Push<u64>(actual_entries);
}
private:
@@ -712,7 +714,7 @@ FSP_SRV::FSP_SRV(Core::System& system_)
{59, nullptr, "WriteSaveDataFileSystemExtraData"},
{60, nullptr, "OpenSaveDataInfoReader"},
{61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"},
{62, nullptr, "OpenCacheStorageList"},
{62, &FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage, "OpenSaveDataInfoReaderOnlyCacheStorage"},
{64, nullptr, "OpenSaveDataInternalStorageFileSystem"},
{65, nullptr, "UpdateSaveDataMacForDebug"},
{66, nullptr, "WriteSaveDataFileSystemExtraData2"},
@@ -921,6 +923,15 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx) {
std::make_shared<ISaveDataInfoReader>(system, space, fsc));
}
void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISaveDataInfoReader>(system, FileSys::SaveDataSpaceId::TemporaryStorage,
fsc);
}
void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called.");

View File

@@ -42,6 +42,7 @@ private:
void OpenSaveDataFileSystem(HLERequestContext& ctx);
void OpenReadOnlySaveDataFileSystem(HLERequestContext& ctx);
void OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx);
void OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx);
void WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx);
void ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(HLERequestContext& ctx);
void OpenDataStorageByCurrentProcess(HLERequestContext& ctx);

View File

@@ -423,8 +423,8 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
return;
}
// This function is unique to yuzu for the turbo buttons to work properly
controller.device->TurboButtonUpdate();
// This function is unique to yuzu for the turbo buttons and motion to work properly
controller.device->StatusUpdate();
auto& pad_entry = controller.npad_pad_state;
auto& trigger_entry = controller.npad_trigger_state;

View File

@@ -156,6 +156,7 @@ public:
auto* session = Kernel::KSession::Create(kernel);
session->Initialize(nullptr, 0);
Kernel::KSession::Register(kernel, session);
auto next_manager = std::make_shared<Service::SessionRequestManager>(
kernel, manager->GetServerManager());

View File

@@ -25,6 +25,9 @@ ServiceContext::ServiceContext(Core::System& system_, std::string name_)
Kernel::KProcess::ProcessType::KernelInternal,
kernel.GetSystemResourceLimit())
.IsSuccess());
// Register the process.
Kernel::KProcess::Register(kernel, process);
process_created = true;
}

View File

@@ -12,6 +12,9 @@ Mutex::Mutex(Core::System& system) : m_system(system) {
m_event = Kernel::KEvent::Create(system.Kernel());
m_event->Initialize(nullptr);
// Register the event.
Kernel::KEvent::Register(system.Kernel(), m_event);
ASSERT(R_SUCCEEDED(m_event->Signal()));
}

View File

@@ -12,7 +12,7 @@
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "core/hle/service/nfp/amiibo_crypto.h"
#include "core/hle/service/nfc/common/amiibo_crypto.h"
namespace Service::NFP::AmiiboCrypto {
@@ -55,7 +55,7 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
if (amiibo_data.constant_value != 0xA5) {
return false;
}
if (amiibo_data.model_info.tag_type != PackedTagType::Type2) {
if (amiibo_data.model_info.tag_type != NFC::PackedTagType::Type2) {
return false;
}
if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001U) {

View File

@@ -24,9 +24,9 @@ using DrgbOutput = std::array<u8, 0x20>;
struct HashSeed {
u16_be magic;
std::array<u8, 0xE> padding;
UniqueSerialNumber uid_1;
NFC::UniqueSerialNumber uid_1;
u8 nintendo_id_1;
UniqueSerialNumber uid_2;
NFC::UniqueSerialNumber uid_2;
u8 nintendo_id_2;
std::array<u8, 0x20> keygen_salt;
};

View File

@@ -0,0 +1,138 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include "common/common_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfc/mifare_types.h"
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/time/clock_types.h"
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Core {
class System;
} // namespace Core
namespace Core::HID {
class EmulatedController;
enum class ControllerTriggerType;
enum class NpadIdType : u32;
} // namespace Core::HID
namespace Service::NFC {
class NfcDevice {
public:
NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
KernelHelpers::ServiceContext& service_context_,
Kernel::KEvent* availability_change_event_);
~NfcDevice();
void Initialize();
void Finalize();
Result StartDetection(NfcProtocol allowed_protocol);
Result StopDetection();
Result GetTagInfo(TagInfo& tag_info, bool is_mifare) const;
Result ReadMifare(std::span<const MifareReadBlockParameter> parameters,
std::span<MifareReadBlockData> read_block_data) const;
Result ReadMifare(const MifareReadBlockParameter& parameter,
MifareReadBlockData& read_block_data) const;
Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters);
Result WriteMifare(const MifareWriteBlockParameter& parameter);
Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout,
std::span<const u8> command_data, std::span<u8> out_data);
Result Mount(NFP::ModelType model_type, NFP::MountTarget mount_target);
Result Unmount();
Result Flush();
Result FlushDebug();
Result FlushWithBreak(NFP::BreakType break_type);
Result Restore();
Result GetCommonInfo(NFP::CommonInfo& common_info) const;
Result GetModelInfo(NFP::ModelInfo& model_info) const;
Result GetRegisterInfo(NFP::RegisterInfo& register_info) const;
Result GetRegisterInfoPrivate(NFP::RegisterInfoPrivate& register_info) const;
Result GetAdminInfo(NFP::AdminInfo& admin_info) const;
Result DeleteRegisterInfo();
Result SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& register_info);
Result RestoreAmiibo();
Result Format();
Result OpenApplicationArea(u32 access_id);
Result GetApplicationAreaId(u32& application_area_id) const;
Result GetApplicationArea(std::span<u8> data) const;
Result SetApplicationArea(std::span<const u8> data);
Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
Result RecreateApplicationArea(u32 access_id, std::span<const u8> data);
Result DeleteApplicationArea();
Result ExistsApplicationArea(bool& has_application_area) const;
Result GetAll(NFP::NfpData& data) const;
Result SetAll(const NFP::NfpData& data);
Result BreakTag(NFP::BreakType break_type);
Result ReadBackupData(std::span<u8> data) const;
Result WriteBackupData(std::span<const u8> data);
Result WriteNtf(std::span<const u8> data);
u64 GetHandle() const;
DeviceState GetCurrentState() const;
Result GetNpadId(Core::HID::NpadIdType& out_npad_id) const;
Kernel::KReadableEvent& GetActivateEvent() const;
Kernel::KReadableEvent& GetDeactivateEvent() const;
private:
void NpadUpdate(Core::HID::ControllerTriggerType type);
bool LoadNfcTag(std::span<const u8> data);
void CloseNfcTag();
NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const;
void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name);
NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const;
u64 GetCurrentPosixTime() const;
u64 RemoveVersionByte(u64 application_id) const;
void UpdateSettingsCrc();
void UpdateRegisterInfoCrc();
bool is_controller_set{};
int callback_key;
const Core::HID::NpadIdType npad_id;
Core::System& system;
Core::HID::EmulatedController* npad_device = nullptr;
KernelHelpers::ServiceContext& service_context;
Kernel::KEvent* activate_event = nullptr;
Kernel::KEvent* deactivate_event = nullptr;
Kernel::KEvent* availability_change_event = nullptr;
bool is_initalized{};
NfcProtocol allowed_protocols{};
DeviceState device_state{DeviceState::Unavailable};
// NFP data
bool is_data_moddified{};
bool is_app_area_open{};
bool is_plain_amiibo{};
NFP::MountTarget mount_target{NFP::MountTarget::None};
NFP::NTAG215File tag_data{};
std::vector<u8> mifare_data{};
NFP::EncryptedNTAG215File encrypted_tag_data{};
};
} // namespace Service::NFC

View File

@@ -0,0 +1,695 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/common/device.h"
#include "core/hle/service/nfc/common/device_manager.h"
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/time/clock_types.h"
namespace Service::NFC {
DeviceManager::DeviceManager(Core::System& system_, KernelHelpers::ServiceContext& service_context_)
: system{system_}, service_context{service_context_} {
availability_change_event =
service_context.CreateEvent("Nfc:DeviceManager:AvailabilityChangeEvent");
for (u32 device_index = 0; device_index < devices.size(); device_index++) {
devices[device_index] =
std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system,
service_context, availability_change_event);
}
is_initialized = false;
}
DeviceManager ::~DeviceManager() {
service_context.CloseEvent(availability_change_event);
}
Result DeviceManager::Initialize() {
for (auto& device : devices) {
device->Initialize();
}
is_initialized = true;
return ResultSuccess;
}
Result DeviceManager::Finalize() {
for (auto& device : devices) {
device->Finalize();
}
is_initialized = false;
return ResultSuccess;
}
Result DeviceManager::ListDevices(std::vector<u64>& nfp_devices,
std::size_t max_allowed_devices) const {
for (auto& device : devices) {
if (nfp_devices.size() >= max_allowed_devices) {
continue;
}
if (device->GetCurrentState() != DeviceState::Unavailable) {
nfp_devices.push_back(device->GetHandle());
}
}
if (nfp_devices.empty()) {
return ResultDeviceNotFound;
}
return ResultSuccess;
}
DeviceState DeviceManager::GetDeviceState(u64 device_handle) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
const auto result = GetDeviceFromHandle(device_handle, device, false);
if (result.IsSuccess()) {
return device->GetCurrentState();
}
return DeviceState::Unavailable;
}
Result DeviceManager::GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetNpadId(npad_id);
result = VerifyDeviceResult(device, result);
}
return result;
}
Kernel::KReadableEvent& DeviceManager::AttachAvailabilityChangeEvent() const {
return availability_change_event->GetReadableEvent();
}
Result DeviceManager::StartDetection(u64 device_handle, NfcProtocol tag_protocol) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->StartDetection(tag_protocol);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::StopDetection(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->StopDetection();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info, bool is_mifare) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetTagInfo(tag_info, is_mifare);
result = VerifyDeviceResult(device, result);
}
return result;
}
Kernel::KReadableEvent& DeviceManager::AttachActivateEvent(u64 device_handle) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
GetDeviceFromHandle(device_handle, device, false);
// TODO: Return proper error code on failure
return device->GetActivateEvent();
}
Kernel::KReadableEvent& DeviceManager::AttachDeactivateEvent(u64 device_handle) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
GetDeviceFromHandle(device_handle, device, false);
// TODO: Return proper error code on failure
return device->GetDeactivateEvent();
}
Result DeviceManager::ReadMifare(u64 device_handle,
std::span<const MifareReadBlockParameter> read_parameters,
std::span<MifareReadBlockData> read_data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->ReadMifare(read_parameters, read_data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::WriteMifare(u64 device_handle,
std::span<const MifareWriteBlockParameter> write_parameters) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->WriteMifare(write_parameters);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::SendCommandByPassThrough(u64 device_handle,
const Time::Clock::TimeSpanType& timeout,
std::span<const u8> command_data,
std::span<u8> out_data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->SendCommandByPassThrough(timeout, command_data, out_data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::Mount(u64 device_handle, NFP::ModelType model_type,
NFP::MountTarget mount_target) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->Mount(model_type, mount_target);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::Unmount(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->Unmount();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::OpenApplicationArea(u64 device_handle, u32 access_id) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->OpenApplicationArea(access_id);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetApplicationArea(u64 device_handle, std::span<u8> data) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetApplicationArea(data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::SetApplicationArea(u64 device_handle, std::span<const u8> data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->SetApplicationArea(data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::Flush(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->Flush();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::Restore(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->Restore();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::CreateApplicationArea(u64 device_handle, u32 access_id,
std::span<const u8> data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->CreateApplicationArea(access_id, data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetRegisterInfo(register_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetCommonInfo(common_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetModelInfo(model_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
u32 DeviceManager::GetApplicationAreaSize() const {
return sizeof(NFP::ApplicationArea);
}
Result DeviceManager::RecreateApplicationArea(u64 device_handle, u32 access_id,
std::span<const u8> data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->RecreateApplicationArea(access_id, data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::Format(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->Format();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetAdminInfo(admin_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetRegisterInfoPrivate(u64 device_handle,
NFP::RegisterInfoPrivate& register_info) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetRegisterInfoPrivate(register_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::SetRegisterInfoPrivate(u64 device_handle,
const NFP::RegisterInfoPrivate& register_info) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->SetRegisterInfoPrivate(register_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::DeleteRegisterInfo(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->DeleteRegisterInfo();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::DeleteApplicationArea(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->DeleteApplicationArea();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::ExistsApplicationArea(u64 device_handle, bool& has_application_area) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->ExistsApplicationArea(has_application_area);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetAll(u64 device_handle, NFP::NfpData& nfp_data) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetAll(nfp_data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::SetAll(u64 device_handle, const NFP::NfpData& nfp_data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->SetAll(nfp_data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::FlushDebug(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->FlushDebug();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::BreakTag(u64 device_handle, NFP::BreakType break_type) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->BreakTag(break_type);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->ReadBackupData(data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->WriteBackupData(data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::WriteNtf(u64 device_handle, NFP::WriteType, std::span<const u8> data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->WriteNtf(data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetDeviceFromHandle(u64 handle, std::shared_ptr<NfcDevice>& nfc_device,
bool check_state) const {
if (check_state) {
const Result is_parameter_set = IsNfcParameterSet();
if (is_parameter_set.IsError()) {
return is_parameter_set;
}
const Result is_enabled = IsNfcEnabled();
if (is_enabled.IsError()) {
return is_enabled;
}
const Result is_nfc_initialized = IsNfcInitialized();
if (is_nfc_initialized.IsError()) {
return is_nfc_initialized;
}
}
for (auto& device : devices) {
if (device->GetHandle() == handle) {
nfc_device = device;
return ResultSuccess;
}
}
return ResultDeviceNotFound;
}
std::optional<std::shared_ptr<NfcDevice>> DeviceManager::GetNfcDevice(u64 handle) {
for (auto& device : devices) {
if (device->GetHandle() == handle) {
return device;
}
}
return std::nullopt;
}
const std::optional<std::shared_ptr<NfcDevice>> DeviceManager::GetNfcDevice(u64 handle) const {
for (auto& device : devices) {
if (device->GetHandle() == handle) {
return device;
}
}
return std::nullopt;
}
Result DeviceManager::GetDeviceHandle(u64 handle, std::shared_ptr<NfcDevice>& device) const {
const auto result = GetDeviceFromHandle(handle, device, true);
if (result.IsError()) {
return result;
}
return CheckDeviceState(device);
}
Result DeviceManager::VerifyDeviceResult(std::shared_ptr<NfcDevice> device,
Result operation_result) const {
if (operation_result.IsSuccess()) {
return operation_result;
}
const Result is_parameter_set = IsNfcParameterSet();
if (is_parameter_set.IsError()) {
return is_parameter_set;
}
const Result is_enabled = IsNfcEnabled();
if (is_enabled.IsError()) {
return is_enabled;
}
const Result is_nfc_initialized = IsNfcInitialized();
if (is_nfc_initialized.IsError()) {
return is_nfc_initialized;
}
const Result device_state = CheckDeviceState(device);
if (device_state.IsError()) {
return device_state;
}
return operation_result;
}
Result DeviceManager::CheckDeviceState(std::shared_ptr<NfcDevice> device) const {
if (device == nullptr) {
return ResultInvalidArgument;
}
return ResultSuccess;
}
Result DeviceManager::IsNfcEnabled() const {
// TODO: This calls nn::settings::detail::GetNfcEnableFlag
const bool is_enabled = true;
if (!is_enabled) {
return ResultNfcDisabled;
}
return ResultSuccess;
}
Result DeviceManager::IsNfcParameterSet() const {
// TODO: This calls checks against a bool on offset 0x450
const bool is_set = true;
if (!is_set) {
return ResultUnknown76;
}
return ResultSuccess;
}
Result DeviceManager::IsNfcInitialized() const {
if (!is_initialized) {
return ResultNfcNotInitialized;
}
return ResultSuccess;
}
} // namespace Service::NFC

View File

@@ -0,0 +1,100 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include <memory>
#include <optional>
#include <span>
#include "core/hid/hid_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfc/mifare_types.h"
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/time/clock_types.h"
namespace Service::NFC {
class NfcDevice;
class DeviceManager {
public:
explicit DeviceManager(Core::System& system_, KernelHelpers::ServiceContext& service_context_);
~DeviceManager();
// Nfc device manager
Result Initialize();
Result Finalize();
Result ListDevices(std::vector<u64>& nfp_devices, std::size_t max_allowed_devices) const;
DeviceState GetDeviceState(u64 device_handle) const;
Result GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id) const;
Kernel::KReadableEvent& AttachAvailabilityChangeEvent() const;
Result StartDetection(u64 device_handle, NfcProtocol tag_protocol);
Result StopDetection(u64 device_handle);
Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info, bool is_mifare) const;
Kernel::KReadableEvent& AttachActivateEvent(u64 device_handle) const;
Kernel::KReadableEvent& AttachDeactivateEvent(u64 device_handle) const;
Result ReadMifare(u64 device_handle,
const std::span<const MifareReadBlockParameter> read_parameters,
std::span<MifareReadBlockData> read_data);
Result WriteMifare(u64 device_handle,
std::span<const MifareWriteBlockParameter> write_parameters);
Result SendCommandByPassThrough(u64 device_handle, const Time::Clock::TimeSpanType& timeout,
std::span<const u8> command_data, std::span<u8> out_data);
// Nfp device manager
Result Mount(u64 device_handle, NFP::ModelType model_type, NFP::MountTarget mount_target);
Result Unmount(u64 device_handle);
Result OpenApplicationArea(u64 device_handle, u32 access_id);
Result GetApplicationArea(u64 device_handle, std::span<u8> data) const;
Result SetApplicationArea(u64 device_handle, std::span<const u8> data);
Result Flush(u64 device_handle);
Result Restore(u64 device_handle);
Result CreateApplicationArea(u64 device_handle, u32 access_id, std::span<const u8> data);
Result GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info) const;
Result GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info) const;
Result GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info) const;
u32 GetApplicationAreaSize() const;
Result RecreateApplicationArea(u64 device_handle, u32 access_id, std::span<const u8> data);
Result Format(u64 device_handle);
Result GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info) const;
Result GetRegisterInfoPrivate(u64 device_handle, NFP::RegisterInfoPrivate& register_info) const;
Result SetRegisterInfoPrivate(u64 device_handle, const NFP::RegisterInfoPrivate& register_info);
Result DeleteRegisterInfo(u64 device_handle);
Result DeleteApplicationArea(u64 device_handle);
Result ExistsApplicationArea(u64 device_handle, bool& has_application_area) const;
Result GetAll(u64 device_handle, NFP::NfpData& nfp_data) const;
Result SetAll(u64 device_handle, const NFP::NfpData& nfp_data);
Result FlushDebug(u64 device_handle);
Result BreakTag(u64 device_handle, NFP::BreakType break_type);
Result ReadBackupData(u64 device_handle, std::span<u8> data) const;
Result WriteBackupData(u64 device_handle, std::span<const u8> data);
Result WriteNtf(u64 device_handle, NFP::WriteType, std::span<const u8> data);
private:
Result IsNfcEnabled() const;
Result IsNfcParameterSet() const;
Result IsNfcInitialized() const;
Result GetDeviceFromHandle(u64 handle, std::shared_ptr<NfcDevice>& device,
bool check_state) const;
Result GetDeviceHandle(u64 handle, std::shared_ptr<NfcDevice>& device) const;
Result VerifyDeviceResult(std::shared_ptr<NfcDevice> device, Result operation_result) const;
Result CheckDeviceState(std::shared_ptr<NfcDevice> device) const;
std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
const std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle) const;
bool is_initialized = false;
mutable std::mutex mutex;
std::array<std::shared_ptr<NfcDevice>, 10> devices{};
Core::System& system;
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* availability_change_event;
};
} // namespace Service::NFC

View File

@@ -0,0 +1,17 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Service::NFC::Mifare {
constexpr Result ResultDeviceNotFound(ErrorModule::NFCMifare, 64);
constexpr Result ResultInvalidArgument(ErrorModule::NFCMifare, 65);
constexpr Result ResultWrongDeviceState(ErrorModule::NFCMifare, 73);
constexpr Result ResultNfcDisabled(ErrorModule::NFCMifare, 80);
constexpr Result ResultTagRemoved(ErrorModule::NFCMifare, 97);
constexpr Result ResultReadError(ErrorModule::NFCMifare, 288);
} // namespace Service::NFC::Mifare

View File

@@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Service::NFC {
enum class MifareCmd : u8 {
AuthA = 0x60,
AuthB = 0x61,
Read = 0x30,
Write = 0xA0,
Transfer = 0xB0,
Decrement = 0xC0,
Increment = 0xC1,
Store = 0xC2
};
using DataBlock = std::array<u8, 0x10>;
using KeyData = std::array<u8, 0x6>;
struct SectorKey {
MifareCmd command;
u8 unknown; // Usually 1
INSERT_PADDING_BYTES(0x6);
KeyData sector_key;
INSERT_PADDING_BYTES(0x2);
};
static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size");
// This is nn::nfc::MifareReadBlockParameter
struct MifareReadBlockParameter {
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
SectorKey sector_key;
};
static_assert(sizeof(MifareReadBlockParameter) == 0x18,
"MifareReadBlockParameter is an invalid size");
// This is nn::nfc::MifareReadBlockData
struct MifareReadBlockData {
DataBlock data;
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
};
static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size");
// This is nn::nfc::MifareWriteBlockParameter
struct MifareWriteBlockParameter {
DataBlock data;
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
SectorKey sector_key;
};
static_assert(sizeof(MifareWriteBlockParameter) == 0x28,
"MifareWriteBlockParameter is an invalid size");
} // namespace Service::NFC

View File

@@ -1,400 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/mifare_user.h"
#include "core/hle/service/nfc/nfc_device.h"
#include "core/hle/service/nfc/nfc_result.h"
namespace Service::NFC {
MFIUser::MFIUser(Core::System& system_)
: ServiceFramework{system_, "NFC::MFIUser"}, service_context{system_, service_name} {
static const FunctionInfo functions[] = {
{0, &MFIUser::Initialize, "Initialize"},
{1, &MFIUser::Finalize, "Finalize"},
{2, &MFIUser::ListDevices, "ListDevices"},
{3, &MFIUser::StartDetection, "StartDetection"},
{4, &MFIUser::StopDetection, "StopDetection"},
{5, &MFIUser::Read, "Read"},
{6, &MFIUser::Write, "Write"},
{7, &MFIUser::GetTagInfo, "GetTagInfo"},
{8, &MFIUser::GetActivateEventHandle, "GetActivateEventHandle"},
{9, &MFIUser::GetDeactivateEventHandle, "GetDeactivateEventHandle"},
{10, &MFIUser::GetState, "GetState"},
{11, &MFIUser::GetDeviceState, "GetDeviceState"},
{12, &MFIUser::GetNpadId, "GetNpadId"},
{13, &MFIUser::GetAvailabilityChangeEventHandle, "GetAvailabilityChangeEventHandle"},
};
RegisterHandlers(functions);
availability_change_event = service_context.CreateEvent("MFIUser:AvailabilityChangeEvent");
for (u32 device_index = 0; device_index < 10; device_index++) {
devices[device_index] =
std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system,
service_context, availability_change_event);
}
}
MFIUser ::~MFIUser() {
availability_change_event->Close();
}
void MFIUser::Initialize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
state = State::Initialized;
for (auto& device : devices) {
device->Initialize();
}
IPC::ResponseBuilder rb{ctx, 2, 0};
rb.Push(ResultSuccess);
}
void MFIUser::Finalize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
state = State::NonInitialized;
for (auto& device : devices) {
device->Finalize();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void MFIUser::ListDevices(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
if (!ctx.CanWriteBuffer()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareInvalidArgument);
return;
}
if (ctx.GetWriteBufferSize() == 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareInvalidArgument);
return;
}
std::vector<u64> nfp_devices;
const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
for (const auto& device : devices) {
if (nfp_devices.size() >= max_allowed_devices) {
continue;
}
if (device->GetCurrentState() != NFP::DeviceState::Unavailable) {
nfp_devices.push_back(device->GetHandle());
}
}
if (nfp_devices.empty()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
ctx.WriteBuffer(nfp_devices);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<s32>(nfp_devices.size()));
}
void MFIUser::StartDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
const auto result = device.value()->StartDetection(NFP::TagProtocol::All);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::StopDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
const auto result = device.value()->StopDetection();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::Read(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto buffer{ctx.ReadBuffer()};
const auto number_of_commands{ctx.GetReadBufferNumElements<NFP::MifareReadBlockParameter>()};
std::vector<NFP::MifareReadBlockParameter> read_commands(number_of_commands);
memcpy(read_commands.data(), buffer.data(),
number_of_commands * sizeof(NFP::MifareReadBlockParameter));
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}",
device_handle, number_of_commands);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
Result result = ResultSuccess;
std::vector<NFP::MifareReadBlockData> out_data(number_of_commands);
for (std::size_t i = 0; i < number_of_commands; i++) {
result = device.value()->MifareRead(read_commands[i], out_data[i]);
if (result.IsError()) {
break;
}
}
ctx.WriteBuffer(out_data);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::Write(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto buffer{ctx.ReadBuffer()};
const auto number_of_commands{ctx.GetReadBufferNumElements<NFP::MifareWriteBlockParameter>()};
std::vector<NFP::MifareWriteBlockParameter> write_commands(number_of_commands);
memcpy(write_commands.data(), buffer.data(),
number_of_commands * sizeof(NFP::MifareWriteBlockParameter));
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, write_commands_size={}",
device_handle, number_of_commands);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
Result result = ResultSuccess;
std::vector<NFP::MifareReadBlockData> out_data(number_of_commands);
for (std::size_t i = 0; i < number_of_commands; i++) {
result = device.value()->MifareWrite(write_commands[i]);
if (result.IsError()) {
break;
}
}
if (result.IsSuccess()) {
result = device.value()->Flush();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::GetTagInfo(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
NFP::TagInfo tag_info{};
const auto result = device.value()->GetTagInfo(tag_info, true);
ctx.WriteBuffer(tag_info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::GetActivateEventHandle(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device.value()->GetActivateEvent());
}
void MFIUser::GetDeactivateEventHandle(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device.value()->GetDeactivateEvent());
}
void MFIUser::GetState(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(state);
}
void MFIUser::GetDeviceState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device.value()->GetCurrentState());
}
void MFIUser::GetNpadId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device.value()->GetNpadId());
}
void MFIUser::GetAvailabilityChangeEventHandle(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(availability_change_event->GetReadableEvent());
}
std::optional<std::shared_ptr<NfcDevice>> MFIUser::GetNfcDevice(u64 handle) {
for (auto& device : devices) {
if (device->GetHandle() == handle) {
return device;
}
}
return std::nullopt;
}
} // namespace Service::NFC

View File

@@ -1,52 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <memory>
#include <optional>
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Service::NFC {
class NfcDevice;
class MFIUser final : public ServiceFramework<MFIUser> {
public:
explicit MFIUser(Core::System& system_);
~MFIUser();
private:
enum class State : u32 {
NonInitialized,
Initialized,
};
void Initialize(HLERequestContext& ctx);
void Finalize(HLERequestContext& ctx);
void ListDevices(HLERequestContext& ctx);
void StartDetection(HLERequestContext& ctx);
void StopDetection(HLERequestContext& ctx);
void Read(HLERequestContext& ctx);
void Write(HLERequestContext& ctx);
void GetTagInfo(HLERequestContext& ctx);
void GetActivateEventHandle(HLERequestContext& ctx);
void GetDeactivateEventHandle(HLERequestContext& ctx);
void GetState(HLERequestContext& ctx);
void GetDeviceState(HLERequestContext& ctx);
void GetNpadId(HLERequestContext& ctx);
void GetAvailabilityChangeEventHandle(HLERequestContext& ctx);
std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
KernelHelpers::ServiceContext service_context;
std::array<std::shared_ptr<NfcDevice>, 10> devices{};
State state{State::NonInitialized};
Kernel::KEvent* availability_change_event;
};
} // namespace Service::NFC

View File

@@ -6,14 +6,115 @@
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/mifare_user.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/hle/service/nfc/nfc_user.h"
#include "core/hle/service/nfc/nfc_interface.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Service::NFC {
class IUser final : public NfcInterface {
public:
explicit IUser(Core::System& system_) : NfcInterface(system_, "NFC::IUser", BackendType::Nfc) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NfcInterface::Initialize, "InitializeOld"},
{1, &NfcInterface::Finalize, "FinalizeOld"},
{2, &NfcInterface::GetState, "GetStateOld"},
{3, &NfcInterface::IsNfcEnabled, "IsNfcEnabledOld"},
{400, &NfcInterface::Initialize, "Initialize"},
{401, &NfcInterface::Finalize, "Finalize"},
{402, &NfcInterface::GetState, "GetState"},
{403, &NfcInterface::IsNfcEnabled, "IsNfcEnabled"},
{404, &NfcInterface::ListDevices, "ListDevices"},
{405, &NfcInterface::GetDeviceState, "GetDeviceState"},
{406, &NfcInterface::GetNpadId, "GetNpadId"},
{407, &NfcInterface::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
{408, &NfcInterface::StartDetection, "StartDetection"},
{409, &NfcInterface::StopDetection, "StopDetection"},
{410, &NfcInterface::GetTagInfo, "GetTagInfo"},
{411, &NfcInterface::AttachActivateEvent, "AttachActivateEvent"},
{412, &NfcInterface::AttachDeactivateEvent, "AttachDeactivateEvent"},
{1000, &NfcInterface::ReadMifare, "ReadMifare"},
{1001, &NfcInterface::WriteMifare ,"WriteMifare"},
{1300, &NfcInterface::SendCommandByPassThrough, "SendCommandByPassThrough"},
{1301, nullptr, "KeepPassThroughSession"},
{1302, nullptr, "ReleasePassThroughSession"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class ISystem final : public NfcInterface {
public:
explicit ISystem(Core::System& system_)
: NfcInterface{system_, "NFC::ISystem", BackendType::Nfc} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NfcInterface::Initialize, "InitializeOld"},
{1, &NfcInterface::Finalize, "FinalizeOld"},
{2, &NfcInterface::GetState, "GetStateOld"},
{3, &NfcInterface::IsNfcEnabled, "IsNfcEnabledOld"},
{100, nullptr, "SetNfcEnabledOld"},
{400, &NfcInterface::Initialize, "Initialize"},
{401, &NfcInterface::Finalize, "Finalize"},
{402, &NfcInterface::GetState, "GetState"},
{403, &NfcInterface::IsNfcEnabled, "IsNfcEnabled"},
{404, &NfcInterface::ListDevices, "ListDevices"},
{405, &NfcInterface::GetDeviceState, "GetDeviceState"},
{406, &NfcInterface::GetNpadId, "GetNpadId"},
{407, &NfcInterface::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
{408, &NfcInterface::StartDetection, "StartDetection"},
{409, &NfcInterface::StopDetection, "StopDetection"},
{410, &NfcInterface::GetTagInfo, "GetTagInfo"},
{411, &NfcInterface::AttachActivateEvent, "AttachActivateEvent"},
{412, &NfcInterface::AttachDeactivateEvent, "AttachDeactivateEvent"},
{500, nullptr, "SetNfcEnabled"},
{510, nullptr, "OutputTestWave"},
{1000, &NfcInterface::ReadMifare, "ReadMifare"},
{1001, &NfcInterface::WriteMifare, "WriteMifare"},
{1300, &NfcInterface::SendCommandByPassThrough, "SendCommandByPassThrough"},
{1301, nullptr, "KeepPassThroughSession"},
{1302, nullptr, "ReleasePassThroughSession"},
};
// clang-format on
RegisterHandlers(functions);
}
};
// MFInterface has an unique interface but it's identical to NfcInterface so we can keep the code
// simpler
using MFInterface = NfcInterface;
class MFIUser final : public MFInterface {
public:
explicit MFIUser(Core::System& system_)
: MFInterface{system_, "NFC::MFInterface", BackendType::Mifare} {
// clang-format off
static const FunctionInfoTyped<MFIUser> functions[] = {
{0, &MFIUser::Initialize, "Initialize"},
{1, &MFIUser::Finalize, "Finalize"},
{2, &MFIUser::ListDevices, "ListDevices"},
{3, &MFIUser::StartDetection, "StartDetection"},
{4, &MFIUser::StopDetection, "StopDetection"},
{5, &MFIUser::ReadMifare, "Read"},
{6, &MFIUser::WriteMifare, "Write"},
{7, &MFIUser::GetTagInfo, "GetTagInfo"},
{8, &MFIUser::AttachActivateEvent, "GetActivateEventHandle"},
{9, &MFIUser::AttachDeactivateEvent, "GetDeactivateEventHandle"},
{10, &MFIUser::GetState, "GetState"},
{11, &MFIUser::GetDeviceState, "GetDeviceState"},
{12, &MFIUser::GetNpadId, "GetNpadId"},
{13, &MFIUser::AttachAvailabilityChangeEvent, "GetAvailabilityChangeEventHandle"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class IAm final : public ServiceFramework<IAm> {
public:
explicit IAm(Core::System& system_) : ServiceFramework{system_, "NFC::IAm"} {
@@ -34,7 +135,7 @@ public:
explicit NFC_AM(Core::System& system_) : ServiceFramework{system_, "nfc:am"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NFC_AM::CreateAmInterface, "CreateAmInterface"},
{0, &NFC_AM::CreateAmNfcInterface, "CreateAmNfcInterface"},
};
// clang-format on
@@ -42,7 +143,7 @@ public:
}
private:
void CreateAmInterface(HLERequestContext& ctx) {
void CreateAmNfcInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -56,7 +157,7 @@ public:
explicit NFC_MF_U(Core::System& system_) : ServiceFramework{system_, "nfc:mf:u"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NFC_MF_U::CreateUserInterface, "CreateUserInterface"},
{0, &NFC_MF_U::CreateUserNfcInterface, "CreateUserNfcInterface"},
};
// clang-format on
@@ -64,7 +165,7 @@ public:
}
private:
void CreateUserInterface(HLERequestContext& ctx) {
void CreateUserNfcInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -78,7 +179,7 @@ public:
explicit NFC_U(Core::System& system_) : ServiceFramework{system_, "nfc:user"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NFC_U::CreateUserInterface, "CreateUserInterface"},
{0, &NFC_U::CreateUserNfcInterface, "CreateUserNfcInterface"},
};
// clang-format on
@@ -86,7 +187,7 @@ public:
}
private:
void CreateUserInterface(HLERequestContext& ctx) {
void CreateUserNfcInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -95,49 +196,12 @@ private:
}
};
class ISystem final : public ServiceFramework<ISystem> {
public:
explicit ISystem(Core::System& system_) : ServiceFramework{system_, "ISystem"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Initialize"},
{1, nullptr, "Finalize"},
{2, nullptr, "GetStateOld"},
{3, nullptr, "IsNfcEnabledOld"},
{100, nullptr, "SetNfcEnabledOld"},
{400, nullptr, "InitializeSystem"},
{401, nullptr, "FinalizeSystem"},
{402, nullptr, "GetState"},
{403, nullptr, "IsNfcEnabled"},
{404, nullptr, "ListDevices"},
{405, nullptr, "GetDeviceState"},
{406, nullptr, "GetNpadId"},
{407, nullptr, "AttachAvailabilityChangeEvent"},
{408, nullptr, "StartDetection"},
{409, nullptr, "StopDetection"},
{410, nullptr, "GetTagInfo"},
{411, nullptr, "AttachActivateEvent"},
{412, nullptr, "AttachDeactivateEvent"},
{500, nullptr, "SetNfcEnabled"},
{510, nullptr, "OutputTestWave"},
{1000, nullptr, "ReadMifare"},
{1001, nullptr, "WriteMifare"},
{1300, nullptr, "SendCommandByPassThrough"},
{1301, nullptr, "KeepPassThroughSession"},
{1302, nullptr, "ReleasePassThroughSession"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class NFC_SYS final : public ServiceFramework<NFC_SYS> {
public:
explicit NFC_SYS(Core::System& system_) : ServiceFramework{system_, "nfc:sys"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NFC_SYS::CreateSystemInterface, "CreateSystemInterface"},
{0, &NFC_SYS::CreateSystemNfcInterface, "CreateSystemNfcInterface"},
};
// clang-format on
@@ -145,7 +209,7 @@ public:
}
private:
void CreateSystemInterface(HLERequestContext& ctx) {
void CreateSystemNfcInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -161,6 +225,7 @@ void LoopProcess(Core::System& system) {
server_manager->RegisterNamedService("nfc:mf:u", std::make_shared<NFC_MF_U>(system));
server_manager->RegisterNamedService("nfc:user", std::make_shared<NFC_U>(system));
server_manager->RegisterNamedService("nfc:sys", std::make_shared<NFC_SYS>(system));
ServerManager::RunServer(std::move(server_manager));
}

View File

@@ -1,288 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/input.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/nfc_device.h"
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/nfc/nfc_user.h"
namespace Service::NFC {
NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
KernelHelpers::ServiceContext& service_context_,
Kernel::KEvent* availability_change_event_)
: npad_id{npad_id_}, system{system_}, service_context{service_context_},
availability_change_event{availability_change_event_} {
activate_event = service_context.CreateEvent("IUser:NFCActivateEvent");
deactivate_event = service_context.CreateEvent("IUser:NFCDeactivateEvent");
npad_device = system.HIDCore().GetEmulatedController(npad_id);
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); },
.is_npad_service = false,
};
is_controller_set = true;
callback_key = npad_device->SetCallback(engine_callback);
}
NfcDevice::~NfcDevice() {
activate_event->Close();
deactivate_event->Close();
if (!is_controller_set) {
return;
}
npad_device->DeleteCallback(callback_key);
is_controller_set = false;
};
void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
if (!is_initalized) {
return;
}
if (type == Core::HID::ControllerTriggerType::Connected) {
Initialize();
availability_change_event->Signal();
return;
}
if (type == Core::HID::ControllerTriggerType::Disconnected) {
device_state = NFP::DeviceState::Unavailable;
availability_change_event->Signal();
return;
}
if (type != Core::HID::ControllerTriggerType::Nfc) {
return;
}
if (!npad_device->IsConnected()) {
return;
}
const auto nfc_status = npad_device->GetNfc();
switch (nfc_status.state) {
case Common::Input::NfcState::NewAmiibo:
LoadNfcTag(nfc_status.data);
break;
case Common::Input::NfcState::AmiiboRemoved:
if (device_state != NFP::DeviceState::SearchingForTag) {
CloseNfcTag();
}
break;
default:
break;
}
}
bool NfcDevice::LoadNfcTag(std::span<const u8> data) {
if (device_state != NFP::DeviceState::SearchingForTag) {
LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state);
return false;
}
if (data.size() < sizeof(NFP::EncryptedNTAG215File)) {
LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size());
return false;
}
tag_data.resize(data.size());
memcpy(tag_data.data(), data.data(), data.size());
memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
device_state = NFP::DeviceState::TagFound;
deactivate_event->GetReadableEvent().Clear();
activate_event->Signal();
return true;
}
void NfcDevice::CloseNfcTag() {
LOG_INFO(Service_NFC, "Remove nfc tag");
device_state = NFP::DeviceState::TagRemoved;
encrypted_tag_data = {};
activate_event->GetReadableEvent().Clear();
deactivate_event->Signal();
}
Kernel::KReadableEvent& NfcDevice::GetActivateEvent() const {
return activate_event->GetReadableEvent();
}
Kernel::KReadableEvent& NfcDevice::GetDeactivateEvent() const {
return deactivate_event->GetReadableEvent();
}
void NfcDevice::Initialize() {
device_state =
npad_device->HasNfc() ? NFP::DeviceState::Initialized : NFP::DeviceState::Unavailable;
encrypted_tag_data = {};
is_initalized = true;
}
void NfcDevice::Finalize() {
if (device_state == NFP::DeviceState::SearchingForTag ||
device_state == NFP::DeviceState::TagRemoved) {
StopDetection();
}
device_state = NFP::DeviceState::Unavailable;
is_initalized = false;
}
Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) {
if (device_state != NFP::DeviceState::Initialized &&
device_state != NFP::DeviceState::TagRemoved) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
return WrongDeviceState;
}
if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::NFC) !=
Common::Input::DriverResult::Success) {
LOG_ERROR(Service_NFC, "Nfc not supported");
return NfcDisabled;
}
device_state = NFP::DeviceState::SearchingForTag;
allowed_protocols = allowed_protocol;
return ResultSuccess;
}
Result NfcDevice::StopDetection() {
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
if (device_state == NFP::DeviceState::Initialized) {
return ResultSuccess;
}
if (device_state == NFP::DeviceState::TagFound ||
device_state == NFP::DeviceState::TagMounted) {
CloseNfcTag();
return ResultSuccess;
}
if (device_state == NFP::DeviceState::SearchingForTag ||
device_state == NFP::DeviceState::TagRemoved) {
device_state = NFP::DeviceState::Initialized;
return ResultSuccess;
}
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
return WrongDeviceState;
}
Result NfcDevice::Flush() {
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
if (device_state == NFP::DeviceState::TagRemoved) {
return TagRemoved;
}
return WrongDeviceState;
}
if (!npad_device->WriteNfc(tag_data)) {
LOG_ERROR(Service_NFP, "Error writing to file");
return MifareReadError;
}
return ResultSuccess;
}
Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
if (device_state == NFP::DeviceState::TagRemoved) {
return TagRemoved;
}
return WrongDeviceState;
}
if (is_mifare) {
tag_info = {
.uuid = encrypted_tag_data.uuid.uid,
.uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
.protocol = NFP::TagProtocol::TypeA,
.tag_type = NFP::TagType::Type4,
};
return ResultSuccess;
}
// Protocol and tag type may change here
tag_info = {
.uuid = encrypted_tag_data.uuid.uid,
.uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
.protocol = NFP::TagProtocol::TypeA,
.tag_type = NFP::TagType::Type2,
};
return ResultSuccess;
}
Result NfcDevice::MifareRead(const NFP::MifareReadBlockParameter& parameter,
NFP::MifareReadBlockData& read_block_data) {
const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock);
read_block_data.sector_number = parameter.sector_number;
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
if (device_state == NFP::DeviceState::TagRemoved) {
return TagRemoved;
}
return WrongDeviceState;
}
if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) {
return MifareReadError;
}
// TODO: Use parameter.sector_key to read encrypted data
memcpy(read_block_data.data.data(), tag_data.data() + sector_index, sizeof(NFP::DataBlock));
return ResultSuccess;
}
Result NfcDevice::MifareWrite(const NFP::MifareWriteBlockParameter& parameter) {
const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock);
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
if (device_state == NFP::DeviceState::TagRemoved) {
return TagRemoved;
}
return WrongDeviceState;
}
if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) {
return MifareReadError;
}
// TODO: Use parameter.sector_key to encrypt the data
memcpy(tag_data.data() + sector_index, parameter.data.data(), sizeof(NFP::DataBlock));
return ResultSuccess;
}
u64 NfcDevice::GetHandle() const {
// Generate a handle based of the npad id
return static_cast<u64>(npad_id);
}
NFP::DeviceState NfcDevice::GetCurrentState() const {
return device_state;
}
Core::HID::NpadIdType NfcDevice::GetNpadId() const {
return npad_id;
}
} // namespace Service::NFC

View File

@@ -1,78 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Core {
class System;
} // namespace Core
namespace Core::HID {
class EmulatedController;
enum class ControllerTriggerType;
enum class NpadIdType : u32;
} // namespace Core::HID
namespace Service::NFC {
class NfcDevice {
public:
NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
KernelHelpers::ServiceContext& service_context_,
Kernel::KEvent* availability_change_event_);
~NfcDevice();
void Initialize();
void Finalize();
Result StartDetection(NFP::TagProtocol allowed_protocol);
Result StopDetection();
Result Flush();
Result GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const;
Result MifareRead(const NFP::MifareReadBlockParameter& parameter,
NFP::MifareReadBlockData& read_block_data);
Result MifareWrite(const NFP::MifareWriteBlockParameter& parameter);
u64 GetHandle() const;
NFP::DeviceState GetCurrentState() const;
Core::HID::NpadIdType GetNpadId() const;
Kernel::KReadableEvent& GetActivateEvent() const;
Kernel::KReadableEvent& GetDeactivateEvent() const;
private:
void NpadUpdate(Core::HID::ControllerTriggerType type);
bool LoadNfcTag(std::span<const u8> data);
void CloseNfcTag();
bool is_controller_set{};
int callback_key;
const Core::HID::NpadIdType npad_id;
Core::System& system;
Core::HID::EmulatedController* npad_device = nullptr;
KernelHelpers::ServiceContext& service_context;
Kernel::KEvent* activate_event = nullptr;
Kernel::KEvent* deactivate_event = nullptr;
Kernel::KEvent* availability_change_event = nullptr;
bool is_initalized{};
NFP::TagProtocol allowed_protocols{};
NFP::DeviceState device_state{NFP::DeviceState::Unavailable};
NFP::EncryptedNTAG215File encrypted_tag_data{};
std::vector<u8> tag_data{};
};
} // namespace Service::NFC

View File

@@ -0,0 +1,382 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/common/device.h"
#include "core/hle/service/nfc/common/device_manager.h"
#include "core/hle/service/nfc/mifare_result.h"
#include "core/hle/service/nfc/mifare_types.h"
#include "core/hle/service/nfc/nfc_interface.h"
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/nfp/nfp_result.h"
#include "core/hle/service/time/clock_types.h"
namespace Service::NFC {
NfcInterface::NfcInterface(Core::System& system_, const char* name, BackendType service_backend)
: ServiceFramework{system_, name}, service_context{system_, service_name},
backend_type{service_backend} {}
NfcInterface ::~NfcInterface() = default;
void NfcInterface::Initialize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
auto manager = GetManager();
auto result = manager->Initialize();
if (result.IsSuccess()) {
state = State::Initialized;
} else {
manager->Finalize();
}
IPC::ResponseBuilder rb{ctx, 2, 0};
rb.Push(result);
}
void NfcInterface::Finalize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
if (state != State::NonInitialized) {
if (GetBackendType() != BackendType::None) {
GetManager()->Finalize();
}
device_manager = nullptr;
state = State::NonInitialized;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void NfcInterface::GetState(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(state);
}
void NfcInterface::IsNfcEnabled(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
// TODO: This calls nn::settings::detail::GetNfcEnableFlag
const bool is_enabled = true;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(is_enabled);
}
void NfcInterface::ListDevices(HLERequestContext& ctx) {
std::vector<u64> nfp_devices;
const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
LOG_DEBUG(Service_NFC, "called");
auto result = GetManager()->ListDevices(nfp_devices, max_allowed_devices);
result = TranslateResultToServiceError(result);
if (result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
ctx.WriteBuffer(nfp_devices);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<s32>(nfp_devices.size()));
}
void NfcInterface::GetDeviceState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
const auto device_state = GetManager()->GetDeviceState(device_handle);
if (device_state > DeviceState::Finalized) {
ASSERT_MSG(false, "Invalid device state");
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device_state);
}
void NfcInterface::GetNpadId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
Core::HID::NpadIdType npad_id{};
auto result = GetManager()->GetNpadId(device_handle, npad_id);
result = TranslateResultToServiceError(result);
if (result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(npad_id);
}
void NfcInterface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(GetManager()->AttachAvailabilityChangeEvent());
}
void NfcInterface::StartDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto tag_protocol{rp.PopEnum<NfcProtocol>()};
LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, tag_protocol);
auto result = GetManager()->StartDetection(device_handle, tag_protocol);
result = TranslateResultToServiceError(result);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void NfcInterface::StopDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
auto result = GetManager()->StopDetection(device_handle);
result = TranslateResultToServiceError(result);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void NfcInterface::GetTagInfo(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
TagInfo tag_info{};
auto result =
GetManager()->GetTagInfo(device_handle, tag_info, backend_type == BackendType::Mifare);
result = TranslateResultToServiceError(result);
if (result.IsSuccess()) {
ctx.WriteBuffer(tag_info);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void NfcInterface::AttachActivateEvent(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(GetManager()->AttachActivateEvent(device_handle));
}
void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(GetManager()->AttachDeactivateEvent(device_handle));
}
void NfcInterface::ReadMifare(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto buffer{ctx.ReadBuffer()};
const auto number_of_commands{ctx.GetReadBufferNumElements<MifareReadBlockParameter>()};
std::vector<MifareReadBlockParameter> read_commands(number_of_commands);
memcpy(read_commands.data(), buffer.data(),
number_of_commands * sizeof(MifareReadBlockParameter));
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}",
device_handle, number_of_commands);
std::vector<MifareReadBlockData> out_data(number_of_commands);
auto result = GetManager()->ReadMifare(device_handle, read_commands, out_data);
result = TranslateResultToServiceError(result);
if (result.IsSuccess()) {
ctx.WriteBuffer(out_data);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void NfcInterface::WriteMifare(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto buffer{ctx.ReadBuffer()};
const auto number_of_commands{ctx.GetReadBufferNumElements<MifareWriteBlockParameter>()};
std::vector<MifareWriteBlockParameter> write_commands(number_of_commands);
memcpy(write_commands.data(), buffer.data(),
number_of_commands * sizeof(MifareWriteBlockParameter));
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, write_commands_size={}",
device_handle, number_of_commands);
auto result = GetManager()->WriteMifare(device_handle, write_commands);
result = TranslateResultToServiceError(result);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void NfcInterface::SendCommandByPassThrough(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto timeout{rp.PopRaw<Time::Clock::TimeSpanType>()};
const auto command_data{ctx.ReadBuffer()};
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}",
device_handle, timeout.ToSeconds(), command_data.size());
std::vector<u8> out_data(1);
auto result =
GetManager()->SendCommandByPassThrough(device_handle, timeout, command_data, out_data);
result = TranslateResultToServiceError(result);
if (result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
ctx.WriteBuffer(out_data);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<u32>(out_data.size()));
}
std::shared_ptr<DeviceManager> NfcInterface::GetManager() {
if (device_manager == nullptr) {
device_manager = std::make_shared<DeviceManager>(system, service_context);
}
return device_manager;
}
BackendType NfcInterface::GetBackendType() const {
return backend_type;
}
Result NfcInterface::TranslateResultToServiceError(Result result) const {
const auto backend = GetBackendType();
if (result.IsSuccess()) {
return result;
}
if (result.module != ErrorModule::NFC) {
return result;
}
switch (backend) {
case BackendType::Mifare:
return TranslateResultToNfp(result);
case BackendType::Nfp: {
return TranslateResultToNfp(result);
}
default:
if (result != ResultUnknown216) {
return result;
}
return ResultUnknown74;
}
}
Result NfcInterface::TranslateResultToNfp(Result result) const {
if (result == ResultDeviceNotFound) {
return NFP::ResultDeviceNotFound;
}
if (result == ResultInvalidArgument) {
return NFP::ResultInvalidArgument;
}
if (result == ResultWrongApplicationAreaSize) {
return NFP::ResultWrongApplicationAreaSize;
}
if (result == ResultWrongDeviceState) {
return NFP::ResultWrongDeviceState;
}
if (result == ResultUnknown74) {
return NFP::ResultUnknown74;
}
if (result == ResultNfcDisabled) {
return NFP::ResultNfcDisabled;
}
if (result == ResultNfcNotInitialized) {
return NFP::ResultNfcDisabled;
}
if (result == ResultWriteAmiiboFailed) {
return NFP::ResultWriteAmiiboFailed;
}
if (result == ResultTagRemoved) {
return NFP::ResultTagRemoved;
}
if (result == ResultRegistrationIsNotInitialized) {
return NFP::ResultRegistrationIsNotInitialized;
}
if (result == ResultApplicationAreaIsNotInitialized) {
return NFP::ResultApplicationAreaIsNotInitialized;
}
if (result == ResultCorruptedData) {
return NFP::ResultCorruptedData;
}
if (result == ResultWrongApplicationAreaId) {
return NFP::ResultWrongApplicationAreaId;
}
if (result == ResultApplicationAreaExist) {
return NFP::ResultApplicationAreaExist;
}
if (result == ResultNotAnAmiibo) {
return NFP::ResultNotAnAmiibo;
}
LOG_WARNING(Service_NFC, "Result conversion not handled");
return result;
}
Result NfcInterface::TranslateResultToMifare(Result result) const {
if (result == ResultDeviceNotFound) {
return Mifare::ResultDeviceNotFound;
}
if (result == ResultInvalidArgument) {
return Mifare::ResultInvalidArgument;
}
if (result == ResultWrongDeviceState) {
return Mifare::ResultWrongDeviceState;
}
if (result == ResultNfcDisabled) {
return Mifare::ResultNfcDisabled;
}
if (result == ResultTagRemoved) {
return Mifare::ResultTagRemoved;
}
LOG_WARNING(Service_NFC, "Result conversion not handled");
return result;
}
} // namespace Service::NFC

View File

@@ -3,26 +3,17 @@
#pragma once
#include <array>
#include <memory>
#include <optional>
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/service.h"
namespace Service::NFC {
class NfcDevice;
class DeviceManager;
class IUser final : public ServiceFramework<IUser> {
class NfcInterface : public ServiceFramework<NfcInterface> {
public:
explicit IUser(Core::System& system_);
~IUser();
private:
enum class State : u32 {
NonInitialized,
Initialized,
};
explicit NfcInterface(Core::System& system_, const char* name, BackendType service_backend);
~NfcInterface();
void Initialize(HLERequestContext& ctx);
void Finalize(HLERequestContext& ctx);
@@ -37,16 +28,22 @@ private:
void GetTagInfo(HLERequestContext& ctx);
void AttachActivateEvent(HLERequestContext& ctx);
void AttachDeactivateEvent(HLERequestContext& ctx);
void ReadMifare(HLERequestContext& ctx);
void WriteMifare(HLERequestContext& ctx);
void SendCommandByPassThrough(HLERequestContext& ctx);
std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
protected:
std::shared_ptr<DeviceManager> GetManager();
BackendType GetBackendType() const;
Result TranslateResultToServiceError(Result result) const;
Result TranslateResultToNfp(Result result) const;
Result TranslateResultToMifare(Result result) const;
KernelHelpers::ServiceContext service_context;
std::array<std::shared_ptr<NfcDevice>, 10> devices{};
BackendType backend_type;
State state{State::NonInitialized};
Kernel::KEvent* availability_change_event;
std::shared_ptr<DeviceManager> device_manager = nullptr;
};
} // namespace Service::NFC

View File

@@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
@@ -7,17 +7,22 @@
namespace Service::NFC {
constexpr Result DeviceNotFound(ErrorModule::NFC, 64);
constexpr Result InvalidArgument(ErrorModule::NFC, 65);
constexpr Result WrongDeviceState(ErrorModule::NFC, 73);
constexpr Result NfcDisabled(ErrorModule::NFC, 80);
constexpr Result TagRemoved(ErrorModule::NFC, 97);
constexpr Result MifareDeviceNotFound(ErrorModule::NFCMifare, 64);
constexpr Result MifareInvalidArgument(ErrorModule::NFCMifare, 65);
constexpr Result MifareWrongDeviceState(ErrorModule::NFCMifare, 73);
constexpr Result MifareNfcDisabled(ErrorModule::NFCMifare, 80);
constexpr Result MifareTagRemoved(ErrorModule::NFCMifare, 97);
constexpr Result MifareReadError(ErrorModule::NFCMifare, 288);
constexpr Result ResultDeviceNotFound(ErrorModule::NFC, 64);
constexpr Result ResultInvalidArgument(ErrorModule::NFC, 65);
constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFP, 68);
constexpr Result ResultWrongDeviceState(ErrorModule::NFC, 73);
constexpr Result ResultUnknown74(ErrorModule::NFC, 74);
constexpr Result ResultUnknown76(ErrorModule::NFC, 76);
constexpr Result ResultNfcNotInitialized(ErrorModule::NFC, 77);
constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80);
constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88);
constexpr Result ResultTagRemoved(ErrorModule::NFC, 97);
constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120);
constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
constexpr Result ResultCorruptedData(ErrorModule::NFP, 144);
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152);
constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168);
constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178);
constexpr Result ResultUnknown216(ErrorModule::NFC, 216);
} // namespace Service::NFC

View File

@@ -0,0 +1,90 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Service::NFC {
enum class BackendType : u32 {
None,
Nfc,
Nfp,
Mifare,
};
// This is nn::nfc::DeviceState
enum class DeviceState : u32 {
Initialized,
SearchingForTag,
TagFound,
TagRemoved,
TagMounted,
Unavailable,
Finalized,
};
// This is nn::nfc::State
enum class State : u32 {
NonInitialized,
Initialized,
};
// This is nn::nfc::TagType
enum class TagType : u32 {
None,
Type1, // ISO14443A RW 96-2k bytes 106kbit/s
Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s
Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
};
enum class PackedTagType : u8 {
None,
Type1, // ISO14443A RW 96-2k bytes 106kbit/s
Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s
Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
};
// This is nn::nfc::NfcProtocol
// Verify this enum. It might be completely wrong default protocol is 0x48
enum class NfcProtocol : u32 {
None,
TypeA = 1U << 0, // ISO14443A
TypeB = 1U << 1, // ISO14443B
TypeF = 1U << 2, // Sony FeliCa
Unknown1 = 1U << 3,
Unknown2 = 1U << 5,
All = 0xFFFFFFFFU,
};
// this is nn::nfc::TestWaveType
enum class TestWaveType : u32 {
Unknown,
};
using UniqueSerialNumber = std::array<u8, 7>;
using UniqueSerialNumberExtension = std::array<u8, 3>;
// This is nn::nfc::DeviceHandle
using DeviceHandle = u64;
// This is nn::nfc::TagInfo
struct TagInfo {
UniqueSerialNumber uuid;
UniqueSerialNumberExtension uuid_extension;
u8 uuid_length;
INSERT_PADDING_BYTES(0x15);
NfcProtocol protocol;
TagType tag_type;
INSERT_PADDING_BYTES(0x30);
};
static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
} // namespace Service::NFC

View File

@@ -1,365 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/nfc_device.h"
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/nfc/nfc_user.h"
#include "core/hle/service/time/clock_types.h"
namespace Service::NFC {
IUser::IUser(Core::System& system_)
: ServiceFramework{system_, "NFC::IUser"}, service_context{system_, service_name} {
static const FunctionInfo functions[] = {
{0, &IUser::Initialize, "InitializeOld"},
{1, &IUser::Finalize, "FinalizeOld"},
{2, &IUser::GetState, "GetStateOld"},
{3, &IUser::IsNfcEnabled, "IsNfcEnabledOld"},
{400, &IUser::Initialize, "Initialize"},
{401, &IUser::Finalize, "Finalize"},
{402, &IUser::GetState, "GetState"},
{403, &IUser::IsNfcEnabled, "IsNfcEnabled"},
{404, &IUser::ListDevices, "ListDevices"},
{405, &IUser::GetDeviceState, "GetDeviceState"},
{406, &IUser::GetNpadId, "GetNpadId"},
{407, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
{408, &IUser::StartDetection, "StartDetection"},
{409, &IUser::StopDetection, "StopDetection"},
{410, &IUser::GetTagInfo, "GetTagInfo"},
{411, &IUser::AttachActivateEvent, "AttachActivateEvent"},
{412, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
{1000, nullptr, "ReadMifare"},
{1001, nullptr, "WriteMifare"},
{1300, &IUser::SendCommandByPassThrough, "SendCommandByPassThrough"},
{1301, nullptr, "KeepPassThroughSession"},
{1302, nullptr, "ReleasePassThroughSession"},
};
RegisterHandlers(functions);
availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent");
for (u32 device_index = 0; device_index < 10; device_index++) {
devices[device_index] =
std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system,
service_context, availability_change_event);
}
}
IUser ::~IUser() {
availability_change_event->Close();
}
void IUser::Initialize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
state = State::Initialized;
for (auto& device : devices) {
device->Initialize();
}
IPC::ResponseBuilder rb{ctx, 2, 0};
rb.Push(ResultSuccess);
}
void IUser::Finalize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
state = State::NonInitialized;
for (auto& device : devices) {
device->Finalize();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IUser::GetState(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(state);
}
void IUser::IsNfcEnabled(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(state != State::NonInitialized);
}
void IUser::ListDevices(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
if (!ctx.CanWriteBuffer()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(InvalidArgument);
return;
}
if (ctx.GetWriteBufferSize() == 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(InvalidArgument);
return;
}
std::vector<u64> nfp_devices;
const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
for (auto& device : devices) {
if (nfp_devices.size() >= max_allowed_devices) {
continue;
}
if (device->GetCurrentState() != NFP::DeviceState::Unavailable) {
nfp_devices.push_back(device->GetHandle());
}
}
if (nfp_devices.empty()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
ctx.WriteBuffer(nfp_devices);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<s32>(nfp_devices.size()));
}
void IUser::GetDeviceState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device.value()->GetCurrentState());
}
void IUser::GetNpadId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device.value()->GetNpadId());
}
void IUser::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(availability_change_event->GetReadableEvent());
}
void IUser::StartDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto nfp_protocol{rp.PopEnum<NFP::TagProtocol>()};
LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
const auto result = device.value()->StartDetection(nfp_protocol);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IUser::StopDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
const auto result = device.value()->StopDetection();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IUser::GetTagInfo(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
NFP::TagInfo tag_info{};
const auto result = device.value()->GetTagInfo(tag_info, false);
ctx.WriteBuffer(tag_info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IUser::AttachActivateEvent(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device.value()->GetActivateEvent());
}
void IUser::AttachDeactivateEvent(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device.value()->GetDeactivateEvent());
}
void IUser::SendCommandByPassThrough(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto timeout{rp.PopRaw<Time::Clock::TimeSpanType>()};
const auto command_data{ctx.ReadBuffer()};
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}",
device_handle, timeout.ToSeconds(), command_data.size());
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
std::vector<u8> out_data(1);
// TODO: Request data from nfc device
ctx.WriteBuffer(out_data);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<u32>(out_data.size()));
}
std::optional<std::shared_ptr<NfcDevice>> IUser::GetNfcDevice(u64 handle) {
for (auto& device : devices) {
if (device->GetHandle() == handle) {
return device;
}
}
return std::nullopt;
}
} // namespace Service::NFC

View File

@@ -13,7 +13,7 @@ class IUser final : public Interface {
public:
explicit IUser(Core::System& system_) : Interface(system_, "NFP:IUser") {
// clang-format off
static const FunctionInfo functions[] = {
static const FunctionInfoTyped<IUser> functions[] = {
{0, &IUser::Initialize, "Initialize"},
{1, &IUser::Finalize, "Finalize"},
{2, &IUser::ListDevices, "ListDevices"},
@@ -50,7 +50,7 @@ class ISystem final : public Interface {
public:
explicit ISystem(Core::System& system_) : Interface(system_, "NFP:ISystem") {
// clang-format off
static const FunctionInfo functions[] = {
static const FunctionInfoTyped<ISystem> functions[] = {
{0, &ISystem::InitializeSystem, "InitializeSystem"},
{1, &ISystem::FinalizeSystem, "FinalizeSystem"},
{2, &ISystem::ListDevices, "ListDevices"},
@@ -89,7 +89,7 @@ class IDebug final : public Interface {
public:
explicit IDebug(Core::System& system_) : Interface(system_, "NFP:IDebug") {
// clang-format off
static const FunctionInfo functions[] = {
static const FunctionInfoTyped<IDebug> functions[] = {
{0, &IDebug::InitializeDebug, "InitializeDebug"},
{1, &IDebug::FinalizeDebug, "FinalizeDebug"},
{2, &IDebug::ListDevices, "ListDevices"},
@@ -126,9 +126,9 @@ public:
{201, &IDebug::SetAll, "SetAll"},
{202, &IDebug::FlushDebug, "FlushDebug"},
{203, &IDebug::BreakTag, "BreakTag"},
{204, nullptr, "ReadBackupData"},
{205, nullptr, "WriteBackupData"},
{206, nullptr, "WriteNtf"},
{204, &IDebug::ReadBackupData, "ReadBackupData"},
{205, &IDebug::WriteBackupData, "WriteBackupData"},
{206, &IDebug::WriteNtf, "WriteNtf"},
};
// clang-format on
@@ -152,16 +152,10 @@ private:
void CreateUserInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
if (user_interface == nullptr) {
user_interface = std::make_shared<IUser>(system);
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IUser>(user_interface);
rb.PushIpcInterface<IUser>(system);
}
std::shared_ptr<IUser> user_interface;
};
class ISystemManager final : public ServiceFramework<ISystemManager> {
@@ -180,16 +174,10 @@ private:
void CreateSystemInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
if (system_interface == nullptr) {
system_interface = std::make_shared<ISystem>(system);
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISystem>(system_interface);
rb.PushIpcInterface<ISystem>(system);
}
std::shared_ptr<ISystem> system_interface;
};
class IDebugManager final : public ServiceFramework<IDebugManager> {
@@ -208,16 +196,10 @@ private:
void CreateDebugInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
if (system_interface == nullptr) {
system_interface = std::make_shared<IDebug>(system);
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebug>(system_interface);
rb.PushIpcInterface<IDebug>(system);
}
std::shared_ptr<IDebug> system_interface;
};
void LoopProcess(Core::System& system) {

View File

@@ -1,120 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include <vector>
#include "common/common_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Core {
class System;
} // namespace Core
namespace Core::HID {
class EmulatedController;
enum class ControllerTriggerType;
enum class NpadIdType : u32;
} // namespace Core::HID
namespace Service::NFP {
class NfpDevice {
public:
NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
KernelHelpers::ServiceContext& service_context_,
Kernel::KEvent* availability_change_event_);
~NfpDevice();
void Initialize();
void Finalize();
Result StartDetection(TagProtocol allowed_protocol);
Result StopDetection();
Result Mount(MountTarget mount_target);
Result Unmount();
Result Flush();
Result FlushDebug();
Result FlushWithBreak(BreakType break_type);
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 GetRegisterInfoPrivate(RegisterInfoPrivate& register_info) const;
Result GetAdminInfo(AdminInfo& admin_info) const;
Result DeleteRegisterInfo();
Result SetRegisterInfoPrivate(const AmiiboName& amiibo_name);
Result RestoreAmiibo();
Result Format();
Result OpenApplicationArea(u32 access_id);
Result GetApplicationAreaId(u32& application_area_id) const;
Result GetApplicationArea(std::vector<u8>& data) const;
Result SetApplicationArea(std::span<const u8> data);
Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
Result RecreateApplicationArea(u32 access_id, std::span<const u8> data);
Result DeleteApplicationArea();
Result ExistApplicationArea(bool& has_application_area);
Result GetAll(NfpData& data) const;
Result SetAll(const NfpData& data);
Result BreakTag(BreakType break_type);
Result ReadBackupData();
Result WriteBackupData();
Result WriteNtf();
u64 GetHandle() const;
u32 GetApplicationAreaSize() const;
DeviceState GetCurrentState() const;
Core::HID::NpadIdType GetNpadId() const;
Kernel::KReadableEvent& GetActivateEvent() const;
Kernel::KReadableEvent& GetDeactivateEvent() const;
private:
void NpadUpdate(Core::HID::ControllerTriggerType type);
bool LoadAmiibo(std::span<const u8> data);
void CloseAmiibo();
AmiiboName GetAmiiboName(const AmiiboSettings& settings) const;
void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name);
AmiiboDate GetAmiiboDate(s64 posix_time) const;
u64 RemoveVersionByte(u64 application_id) const;
void UpdateSettingsCrc();
void UpdateRegisterInfoCrc();
bool is_controller_set{};
int callback_key;
const Core::HID::NpadIdType npad_id;
Core::System& system;
Core::HID::EmulatedController* npad_device = nullptr;
KernelHelpers::ServiceContext& service_context;
Kernel::KEvent* activate_event = nullptr;
Kernel::KEvent* deactivate_event = nullptr;
Kernel::KEvent* availability_change_event = nullptr;
bool is_initalized{};
bool is_data_moddified{};
bool is_app_area_open{};
bool is_plain_amiibo{};
TagProtocol allowed_protocols{};
s64 current_posix_time{};
MountTarget mount_target{MountTarget::None};
DeviceState device_state{DeviceState::Unavailable};
NTAG215File tag_data{};
EncryptedNTAG215File encrypted_tag_data{};
};
} // namespace Service::NFP

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +1,23 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <memory>
#include <optional>
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfc/nfc_interface.h"
#include "core/hle/service/service.h"
namespace Service::NFP {
class NfpDevice;
class Interface : public ServiceFramework<Interface> {
class Interface : public NFC::NfcInterface {
public:
explicit Interface(Core::System& system_, const char* name);
~Interface() override;
void Initialize(HLERequestContext& ctx);
void InitializeSystem(HLERequestContext& ctx);
void InitializeDebug(HLERequestContext& ctx);
void Finalize(HLERequestContext& ctx);
void FinalizeSystem(HLERequestContext& ctx);
void FinalizeDebug(HLERequestContext& ctx);
void ListDevices(HLERequestContext& ctx);
void StartDetection(HLERequestContext& ctx);
void StopDetection(HLERequestContext& ctx);
void Mount(HLERequestContext& ctx);
void Unmount(HLERequestContext& ctx);
void OpenApplicationArea(HLERequestContext& ctx);
@@ -35,17 +26,10 @@ public:
void Flush(HLERequestContext& ctx);
void Restore(HLERequestContext& ctx);
void CreateApplicationArea(HLERequestContext& ctx);
void GetTagInfo(HLERequestContext& ctx);
void GetRegisterInfo(HLERequestContext& ctx);
void GetCommonInfo(HLERequestContext& ctx);
void GetModelInfo(HLERequestContext& ctx);
void AttachActivateEvent(HLERequestContext& ctx);
void AttachDeactivateEvent(HLERequestContext& ctx);
void GetState(HLERequestContext& ctx);
void GetDeviceState(HLERequestContext& ctx);
void GetNpadId(HLERequestContext& ctx);
void GetApplicationAreaSize(HLERequestContext& ctx);
void AttachAvailabilityChangeEvent(HLERequestContext& ctx);
void RecreateApplicationArea(HLERequestContext& ctx);
void Format(HLERequestContext& ctx);
void GetAdminInfo(HLERequestContext& ctx);
@@ -61,21 +45,6 @@ public:
void ReadBackupData(HLERequestContext& ctx);
void WriteBackupData(HLERequestContext& ctx);
void WriteNtf(HLERequestContext& ctx);
private:
enum class State : u32 {
NonInitialized,
Initialized,
};
std::optional<std::shared_ptr<NfpDevice>> GetNfpDevice(u64 handle);
KernelHelpers::ServiceContext service_context;
std::array<std::shared_ptr<NfpDevice>, 10> devices{};
State state{State::NonInitialized};
Kernel::KEvent* availability_change_event;
};
} // namespace Service::NFP

View File

@@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -7,18 +7,19 @@
namespace Service::NFP {
constexpr Result DeviceNotFound(ErrorModule::NFP, 64);
constexpr Result InvalidArgument(ErrorModule::NFP, 65);
constexpr Result WrongApplicationAreaSize(ErrorModule::NFP, 68);
constexpr Result WrongDeviceState(ErrorModule::NFP, 73);
constexpr Result NfcDisabled(ErrorModule::NFP, 80);
constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88);
constexpr Result TagRemoved(ErrorModule::NFP, 97);
constexpr Result RegistrationIsNotInitialized(ErrorModule::NFP, 120);
constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
constexpr Result CorruptedData(ErrorModule::NFP, 144);
constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152);
constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168);
constexpr Result NotAnAmiibo(ErrorModule::NFP, 178);
constexpr Result ResultDeviceNotFound(ErrorModule::NFP, 64);
constexpr Result ResultInvalidArgument(ErrorModule::NFP, 65);
constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFP, 68);
constexpr Result ResultWrongDeviceState(ErrorModule::NFP, 73);
constexpr Result ResultUnknown74(ErrorModule::NFC, 74);
constexpr Result ResultNfcDisabled(ErrorModule::NFP, 80);
constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88);
constexpr Result ResultTagRemoved(ErrorModule::NFP, 97);
constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120);
constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
constexpr Result ResultCorruptedData(ErrorModule::NFP, 144);
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152);
constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168);
constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178);
} // namespace Service::NFP

View File

@@ -7,32 +7,19 @@
#include "common/swap.h"
#include "core/hle/service/mii/types.h"
#include "core/hle/service/nfc/nfc_types.h"
namespace Service::NFP {
static constexpr std::size_t amiibo_name_length = 0xA;
static constexpr std::size_t application_id_version_offset = 0x1c;
static constexpr std::size_t counter_limit = 0xffff;
enum class ServiceType : u32 {
User,
Debug,
System,
};
enum class DeviceState : u32 {
Initialized,
SearchingForTag,
TagFound,
TagRemoved,
TagMounted,
Unavailable,
Finalized,
};
// This is nn::nfp::ModelType
enum class ModelType : u32 {
Amiibo,
};
// This is nn::nfp::MountTarget
enum class MountTarget : u32 {
None,
Rom,
@@ -72,35 +59,6 @@ enum class AmiiboSeries : u8 {
Diablo,
};
enum class TagType : u32 {
None,
Type1, // ISO14443A RW 96-2k bytes 106kbit/s
Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
Type3, // Sony Felica RW/RO 2k bytes 212kbit/s
Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
};
enum class PackedTagType : u8 {
None,
Type1, // ISO14443A RW 96-2k bytes 106kbit/s
Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
Type3, // Sony Felica RW/RO 2k bytes 212kbit/s
Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
};
// Verify this enum. It might be completely wrong default protocol is 0x48
enum class TagProtocol : u32 {
None,
TypeA = 1U << 0, // ISO14443A
TypeB = 1U << 1, // ISO14443B
TypeF = 1U << 2, // Sony Felica
Unknown1 = 1U << 3,
Unknown2 = 1U << 5,
All = 0xFFFFFFFFU,
};
enum class AppAreaVersion : u8 {
Nintendo3DS = 0,
NintendoWiiU = 1,
@@ -115,6 +73,11 @@ enum class BreakType : u32 {
Unknown2,
};
enum class WriteType : u32 {
Unknown0,
Unknown1,
};
enum class CabinetMode : u8 {
StartNicknameAndOwnerSettings,
StartGameDataEraser,
@@ -122,27 +85,16 @@ enum class CabinetMode : u8 {
StartFormatter,
};
enum class MifareCmd : u8 {
AuthA = 0x60,
AuthB = 0x61,
Read = 0x30,
Write = 0xA0,
Transfer = 0xB0,
Decrement = 0xC0,
Increment = 0xC1,
Store = 0xC2
};
using UniqueSerialNumber = std::array<u8, 7>;
using LockBytes = std::array<u8, 2>;
using HashData = std::array<u8, 0x20>;
using ApplicationArea = std::array<u8, 0xD8>;
using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
using DataBlock = std::array<u8, 0x10>;
using KeyData = std::array<u8, 0x6>;
// This is nn::nfp::TagInfo
using TagInfo = NFC::TagInfo;
struct TagUuid {
UniqueSerialNumber uid;
NFC::UniqueSerialNumber uid;
u8 nintendo_id;
LockBytes lock_bytes;
};
@@ -243,7 +195,7 @@ struct AmiiboModelInfo {
AmiiboType amiibo_type;
u16_be model_number;
AmiiboSeries series;
PackedTagType tag_type;
NFC::PackedTagType tag_type;
INSERT_PADDING_BYTES(0x4); // Unknown
};
static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size");
@@ -298,7 +250,7 @@ struct NTAG215File {
u32_be register_info_crc;
ApplicationArea application_area; // Encrypted Game data
HashData hmac_tag; // Hash
UniqueSerialNumber uid; // Unique serial number
NFC::UniqueSerialNumber uid; // Unique serial number
u8 nintendo_id; // Tag UUID
AmiiboModelInfo model_info;
HashData keygen_salt; // Salt
@@ -326,17 +278,7 @@ static_assert(sizeof(EncryptedNTAG215File) == sizeof(NTAG215File),
static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>,
"EncryptedNTAG215File must be trivially copyable.");
struct TagInfo {
UniqueSerialNumber uuid;
INSERT_PADDING_BYTES(0x3);
u8 uuid_length;
INSERT_PADDING_BYTES(0x15);
TagProtocol protocol;
TagType tag_type;
INSERT_PADDING_BYTES(0x30);
};
static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
// This is nn::nfp::CommonInfo
struct CommonInfo {
WriteDate last_write_date;
u16 write_counter;
@@ -347,6 +289,7 @@ struct CommonInfo {
};
static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
// This is nn::nfp::ModelInfo
struct ModelInfo {
u16 character_id;
u8 character_variant;
@@ -357,6 +300,7 @@ struct ModelInfo {
};
static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
// This is nn::nfp::RegisterInfo
struct RegisterInfo {
Service::Mii::CharInfo mii_char_info;
WriteDate creation_date;
@@ -366,6 +310,7 @@ struct RegisterInfo {
};
static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
// This is nn::nfp::RegisterInfoPrivate
struct RegisterInfoPrivate {
Service::Mii::MiiStoreData mii_store_data;
WriteDate creation_date;
@@ -375,12 +320,13 @@ struct RegisterInfoPrivate {
};
static_assert(sizeof(RegisterInfoPrivate) == 0x100, "RegisterInfoPrivate is an invalid size");
// This is nn::nfp::AdminInfo
struct AdminInfo {
u64 application_id;
u32 application_area_id;
u16 crc_change_counter;
u8 flags;
PackedTagType tag_type;
NFC::PackedTagType tag_type;
AppAreaVersion app_area_version;
INSERT_PADDING_BYTES(0x7);
INSERT_PADDING_BYTES(0x28);
@@ -411,7 +357,7 @@ struct NfpData {
u32 access_id;
u16 settings_crc_counter;
u8 font_region;
PackedTagType tag_type;
NFC::PackedTagType tag_type;
AppAreaVersion console_type;
u8 application_id_byte;
INSERT_PADDING_BYTES(0x2E);
@@ -420,37 +366,4 @@ struct NfpData {
static_assert(sizeof(NfpData) == 0x298, "NfpData is an invalid size");
#pragma pack()
struct SectorKey {
MifareCmd command;
u8 unknown; // Usually 1
INSERT_PADDING_BYTES(0x6);
KeyData sector_key;
INSERT_PADDING_BYTES(0x2);
};
static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size");
struct MifareReadBlockParameter {
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
SectorKey sector_key;
};
static_assert(sizeof(MifareReadBlockParameter) == 0x18,
"MifareReadBlockParameter is an invalid size");
struct MifareReadBlockData {
DataBlock data;
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
};
static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size");
struct MifareWriteBlockParameter {
DataBlock data;
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
SectorKey sector_key;
};
static_assert(sizeof(MifareWriteBlockParameter) == 0x28,
"MifareWriteBlockParameter is an invalid size");
} // namespace Service::NFP

View File

@@ -218,7 +218,7 @@ public:
private:
void Submit(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
LOG_DEBUG(Service_NIFM, "(STUBBED) called");
if (state == RequestState::NotSubmitted) {
UpdateState(RequestState::OnHold);
@@ -229,7 +229,7 @@ private:
}
void GetRequestState(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
LOG_DEBUG(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -237,7 +237,7 @@ private:
}
void GetResult(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
LOG_DEBUG(Service_NIFM, "(STUBBED) called");
const auto result = [this] {
const auto has_connection = Network::GetHostIPv4Address().has_value();

View File

@@ -793,6 +793,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
std::scoped_lock lock{core->mutex};
slots[slot] = {};
slots[slot].fence = Fence::NoFence();
slots[slot].graphic_buffer = buffer;
slots[slot].frame_number = 0;
@@ -854,7 +855,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
status = DequeueBuffer(&slot, &fence, is_async, width, height, pixel_format, usage);
parcel_out.Write(slot);
parcel_out.WriteObject(&fence);
parcel_out.WriteFlattenedObject(&fence);
break;
}
case TransactionId::RequestBuffer: {
@@ -864,7 +865,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
status = RequestBuffer(slot, &buf);
parcel_out.WriteObject(buf);
parcel_out.WriteFlattenedObject(buf);
break;
}
case TransactionId::QueueBuffer: {

View File

@@ -117,61 +117,67 @@ private:
class OutputParcel final {
public:
static constexpr std::size_t DefaultBufferSize = 0x40;
OutputParcel() : buffer(DefaultBufferSize) {}
template <typename T>
explicit OutputParcel(const T& out_data) : buffer(DefaultBufferSize) {
Write(out_data);
}
OutputParcel() = default;
template <typename T>
void Write(const T& val) {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
if (buffer.size() < write_index + sizeof(T)) {
buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
}
std::memcpy(buffer.data() + write_index, &val, sizeof(T));
write_index += sizeof(T);
write_index = Common::AlignUp(write_index, 4);
this->WriteImpl(val, m_data_buffer);
}
template <typename T>
void WriteObject(const T* ptr) {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
void WriteFlattenedObject(const T* ptr) {
if (!ptr) {
Write<u32>(0);
this->Write<u32>(0);
return;
}
Write<u32>(1);
Write<s64>(sizeof(T));
Write(*ptr);
this->Write<u32>(1);
this->Write<s64>(sizeof(T));
this->Write(*ptr);
}
template <typename T>
void WriteObject(const std::shared_ptr<T> ptr) {
WriteObject(ptr.get());
void WriteFlattenedObject(const std::shared_ptr<T> ptr) {
this->WriteFlattenedObject(ptr.get());
}
template <typename T>
void WriteInterface(const T& val) {
this->WriteImpl(val, m_data_buffer);
this->WriteImpl(0U, m_object_buffer);
}
std::vector<u8> Serialize() const {
ParcelHeader header{};
header.data_size = static_cast<u32>(write_index - sizeof(ParcelHeader));
header.data_offset = sizeof(ParcelHeader);
header.objects_size = 4;
header.objects_offset = static_cast<u32>(sizeof(ParcelHeader) + header.data_size);
std::memcpy(buffer.data(), &header, sizeof(ParcelHeader));
std::vector<u8> output_buffer(sizeof(ParcelHeader) + m_data_buffer.size() +
m_object_buffer.size());
return buffer;
ParcelHeader header{};
header.data_size = static_cast<u32>(m_data_buffer.size());
header.data_offset = sizeof(ParcelHeader);
header.objects_size = static_cast<u32>(m_object_buffer.size());
header.objects_offset = header.data_offset + header.data_size;
std::memcpy(output_buffer.data(), &header, sizeof(header));
std::ranges::copy(m_data_buffer, output_buffer.data() + header.data_offset);
std::ranges::copy(m_object_buffer, output_buffer.data() + header.objects_offset);
return output_buffer;
}
private:
mutable std::vector<u8> buffer;
std::size_t write_index = sizeof(ParcelHeader);
template <typename T>
requires(std::is_trivially_copyable_v<T>)
void WriteImpl(const T& val, std::vector<u8>& buffer) {
const size_t aligned_size = Common::AlignUp(sizeof(T), 4);
const size_t old_size = buffer.size();
buffer.resize(old_size + aligned_size);
std::memcpy(buffer.data() + old_size, &val, sizeof(T));
}
private:
std::vector<u8> m_data_buffer;
std::vector<u8> m_object_buffer;
};
} // namespace Service::android

View File

@@ -33,6 +33,9 @@ ServerManager::ServerManager(Core::System& system) : m_system{system}, m_serve_m
// Initialize event.
m_event = Kernel::KEvent::Create(system.Kernel());
m_event->Initialize(nullptr);
// Register event.
Kernel::KEvent::Register(system.Kernel(), m_event);
}
ServerManager::~ServerManager() {
@@ -160,6 +163,9 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
// Initialize the event.
m_deferral_event->Initialize(nullptr);
// Register the event.
Kernel::KEvent::Register(m_system.Kernel(), m_deferral_event);
// Set the output.
*out_event = m_deferral_event;

View File

@@ -142,7 +142,8 @@ template <typename Self>
class ServiceFramework : public ServiceFrameworkBase {
protected:
/// Contains information about a request type which is handled by the service.
struct FunctionInfo : FunctionInfoBase {
template <typename T>
struct FunctionInfoTyped : FunctionInfoBase {
// TODO(yuriks): This function could be constexpr, but clang is the only compiler that
// doesn't emit an ICE or a wrong diagnostic because of the static_cast.
@@ -155,12 +156,13 @@ protected:
* the request
* @param name_ human-friendly name for the request. Used mostly for logging purposes.
*/
FunctionInfo(u32 expected_header_, HandlerFnP<Self> handler_callback_, const char* name_)
FunctionInfoTyped(u32 expected_header_, HandlerFnP<T> handler_callback_, const char* name_)
: FunctionInfoBase{
expected_header_,
// Type-erase member function pointer by casting it down to the base class.
static_cast<HandlerFnP<ServiceFrameworkBase>>(handler_callback_), name_} {}
};
using FunctionInfo = FunctionInfoTyped<Self>;
/**
* Initializes the handler with no functions installed.
@@ -175,8 +177,8 @@ protected:
: ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {}
/// Registers handlers in the service.
template <std::size_t N>
void RegisterHandlers(const FunctionInfo (&functions)[N]) {
template <typename T = Self, std::size_t N>
void RegisterHandlers(const FunctionInfoTyped<T> (&functions)[N]) {
RegisterHandlers(functions, N);
}
@@ -184,13 +186,14 @@ protected:
* Registers handlers in the service. Usually prefer using the other RegisterHandlers
* overload in order to avoid needing to specify the array size.
*/
void RegisterHandlers(const FunctionInfo* functions, std::size_t n) {
template <typename T = Self>
void RegisterHandlers(const FunctionInfoTyped<T>* functions, std::size_t n) {
RegisterHandlersBase(functions, n);
}
/// Registers handlers in the service.
template <std::size_t N>
void RegisterHandlersTipc(const FunctionInfo (&functions)[N]) {
template <typename T = Self, std::size_t N>
void RegisterHandlersTipc(const FunctionInfoTyped<T> (&functions)[N]) {
RegisterHandlersTipc(functions, N);
}
@@ -198,7 +201,8 @@ protected:
* Registers handlers in the service. Usually prefer using the other RegisterHandlers
* overload in order to avoid needing to specify the array size.
*/
void RegisterHandlersTipc(const FunctionInfo* functions, std::size_t n) {
template <typename T = Self>
void RegisterHandlersTipc(const FunctionInfoTyped<T>* functions, std::size_t n) {
RegisterHandlersBaseTipc(functions, n);
}

View File

@@ -64,6 +64,9 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
auto* port = Kernel::KPort::Create(kernel);
port->Initialize(ServerSessionCountMax, false, 0);
// Register the port.
Kernel::KPort::Register(kernel, port);
service_ports.emplace(name, port);
registered_services.emplace(name, handler);
if (deferral_event) {

View File

@@ -49,6 +49,9 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) {
// Commit the session reservation.
session_reservation.Commit();
// Register the session.
Kernel::KSession::Register(system.Kernel(), session);
// Register with server manager.
session_manager->GetServerManager().RegisterSession(&session->GetServerSession(),
session_manager);

View File

@@ -59,6 +59,18 @@ static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext is incorre
static_assert(std::is_trivially_copyable_v<SystemClockContext>,
"SystemClockContext must be trivially copyable");
struct ContinuousAdjustmentTimePoint {
s64 measurement_offset;
s64 diff_scale;
u32 shift_amount;
s64 lower;
s64 upper;
Common::UUID clock_source_id;
};
static_assert(sizeof(ContinuousAdjustmentTimePoint) == 0x38);
static_assert(std::is_trivially_copyable_v<ContinuousAdjustmentTimePoint>,
"ContinuousAdjustmentTimePoint must be trivially copyable");
/// https://switchbrew.org/wiki/Glue_services#TimeSpanType
struct TimeSpanType {
s64 nanoseconds{};

View File

@@ -30,6 +30,25 @@ void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id,
}
void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) {
// lower and upper are related to the measurement point for the steady time point,
// and compare equal on boot
const s64 time_point_ns = context.steady_time_point.time_point * 1'000'000'000LL;
// This adjusts for some sort of time skew
// Both 0 on boot
const s64 diff_scale = 0;
const u32 shift_amount = 0;
const Clock::ContinuousAdjustmentTimePoint adjustment{
.measurement_offset = system.CoreTiming().GetGlobalTimeNs().count(),
.diff_scale = diff_scale,
.shift_amount = shift_amount,
.lower = time_point_ns,
.upper = time_point_ns,
.clock_source_id = context.steady_time_point.clock_source_id,
};
StoreToLockFreeAtomicType(&GetFormat()->continuous_adjustment_timepoint, adjustment);
StoreToLockFreeAtomicType(&GetFormat()->standard_local_system_clock_context, context);
}

View File

@@ -65,14 +65,15 @@ public:
LockFreeAtomicType<Clock::SystemClockContext> standard_local_system_clock_context;
LockFreeAtomicType<Clock::SystemClockContext> standard_network_system_clock_context;
LockFreeAtomicType<bool> is_standard_user_system_clock_automatic_correction_enabled;
u32 format_version;
LockFreeAtomicType<Clock::ContinuousAdjustmentTimePoint> continuous_adjustment_timepoint;
};
static_assert(offsetof(Format, standard_steady_clock_timepoint) == 0x0);
static_assert(offsetof(Format, standard_local_system_clock_context) == 0x38);
static_assert(offsetof(Format, standard_network_system_clock_context) == 0x80);
static_assert(offsetof(Format, is_standard_user_system_clock_automatic_correction_enabled) ==
0xc8);
static_assert(sizeof(Format) == 0xd8, "Format is an invalid size");
static_assert(offsetof(Format, continuous_adjustment_timepoint) == 0xd0);
static_assert(sizeof(Format) == 0x148, "Format is an invalid size");
void SetupStandardSteadyClock(const Common::UUID& clock_source_id,
Clock::TimeSpanType current_time_point);

View File

@@ -64,8 +64,8 @@ public:
private:
const u32 magic = 2;
const u32 process_id = 1;
const u32 id;
INSERT_PADDING_WORDS(3);
const u64 id;
INSERT_PADDING_WORDS(2);
std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'};
INSERT_PADDING_WORDS(2);
};
@@ -608,7 +608,9 @@ private:
return;
}
const auto parcel = android::OutputParcel{NativeWindow{*buffer_queue_id}};
android::OutputParcel parcel;
parcel.WriteInterface(NativeWindow{*buffer_queue_id});
const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
IPC::ResponseBuilder rb{ctx, 4};
@@ -654,7 +656,9 @@ private:
return;
}
const auto parcel = android::OutputParcel{NativeWindow{*buffer_queue_id}};
android::OutputParcel parcel;
parcel.WriteInterface(NativeWindow{*buffer_queue_id});
const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
IPC::ResponseBuilder rb{ctx, 6};

View File

@@ -356,7 +356,7 @@ NetworkInstance::~NetworkInstance() {
std::optional<IPv4Address> GetHostIPv4Address() {
const auto network_interface = Network::GetSelectedNetworkInterface();
if (!network_interface.has_value()) {
LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface");
LOG_DEBUG(Network, "GetSelectedNetworkInterface returned no interface");
return {};
}

View File

@@ -200,7 +200,7 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() {
});
if (res == network_interfaces.end()) {
LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
LOG_DEBUG(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
return std::nullopt;
}

View File

@@ -13,10 +13,12 @@
#include "common/swap.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/memory.h"
#include "video_core/gpu.h"
#include "video_core/rasterizer_download_area.h"
namespace Core::Memory {
@@ -243,7 +245,7 @@ struct Memory::Impl {
[&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount,
const u8* const host_ptr) {
if constexpr (!UNSAFE) {
system.GPU().FlushRegion(GetInteger(current_vaddr), copy_amount);
HandleRasterizerDownload(GetInteger(current_vaddr), copy_amount);
}
std::memcpy(dest_buffer, host_ptr, copy_amount);
},
@@ -334,7 +336,7 @@ struct Memory::Impl {
},
[&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount,
u8* const host_ptr) {
system.GPU().FlushRegion(GetInteger(current_vaddr), copy_amount);
HandleRasterizerDownload(GetInteger(current_vaddr), copy_amount);
WriteBlockImpl<false>(process, dest_addr, host_ptr, copy_amount);
},
[&](const std::size_t copy_amount) {
@@ -373,7 +375,7 @@ struct Memory::Impl {
const std::size_t block_size) {
// dc ivac: Invalidate to point of coherency
// GPU flush -> CPU invalidate
system.GPU().FlushRegion(GetInteger(current_vaddr), block_size);
HandleRasterizerDownload(GetInteger(current_vaddr), block_size);
};
return PerformCacheOperation(process, dest_addr, size, on_rasterizer);
}
@@ -462,7 +464,8 @@ struct Memory::Impl {
}
if (Settings::IsFastmemEnabled()) {
const bool is_read_enable = Settings::IsGPULevelHigh() || !cached;
const bool is_read_enable =
!Settings::values.use_reactive_flushing.GetValue() || !cached;
system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
}
@@ -651,7 +654,7 @@ struct Memory::Impl {
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8,
GetInteger(vaddr));
},
[&]() { system.GPU().FlushRegion(GetInteger(vaddr), sizeof(T)); });
[&]() { HandleRasterizerDownload(GetInteger(vaddr), sizeof(T)); });
if (ptr) {
std::memcpy(&result, ptr, sizeof(T));
}
@@ -712,7 +715,19 @@ struct Memory::Impl {
return true;
}
void HandleRasterizerDownload(VAddr address, size_t size) {
const size_t core = system.GetCurrentHostThreadID();
auto& current_area = rasterizer_areas[core];
const VAddr end_address = address + size;
if (current_area.start_address <= address && end_address <= current_area.end_address)
[[likely]] {
return;
}
current_area = system.GPU().OnCPURead(address, size);
}
Common::PageTable* current_page_table = nullptr;
std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES> rasterizer_areas{};
Core::System& system;
};

View File

@@ -85,6 +85,20 @@ static const char* TranslateNvdecEmulation(Settings::NvdecEmulation backend) {
return "Unknown";
}
static constexpr const char* TranslateVSyncMode(Settings::VSyncMode mode) {
switch (mode) {
case Settings::VSyncMode::Immediate:
return "Immediate";
case Settings::VSyncMode::Mailbox:
return "Mailbox";
case Settings::VSyncMode::FIFO:
return "FIFO";
case Settings::VSyncMode::FIFORelaxed:
return "FIFO Relaxed";
}
return "Unknown";
}
u64 GetTelemetryId() {
u64 telemetry_id{};
const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id";
@@ -241,7 +255,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
AddField(field_type, "Renderer_NvdecEmulation",
TranslateNvdecEmulation(Settings::values.nvdec_emulation.GetValue()));
AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue());
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
AddField(field_type, "Renderer_UseVsync",
TranslateVSyncMode(Settings::values.vsync_mode.GetValue()));
AddField(field_type, "Renderer_ShaderBackend",
static_cast<u32>(Settings::values.shader_backend.GetValue()));
AddField(field_type, "Renderer_UseAsynchronousShaders",

View File

@@ -109,14 +109,37 @@ public:
}
bool RumblePlay(const Common::Input::VibrationStatus vibration) {
constexpr u32 rumble_max_duration_ms = 1000;
constexpr u32 rumble_max_duration_ms = 2000;
constexpr f32 low_start_sensitivity_limit = 140.0;
constexpr f32 low_width_sensitivity_limit = 400.0;
constexpr f32 high_start_sensitivity_limit = 200.0;
constexpr f32 high_width_sensitivity_limit = 700.0;
// Try to provide some feeling of the frequency by reducing the amplitude depending on it.
f32 low_frequency_scale = 1.0;
if (vibration.low_frequency > low_start_sensitivity_limit) {
low_frequency_scale =
std::max(1.0f - (vibration.low_frequency - low_start_sensitivity_limit) /
low_width_sensitivity_limit,
0.3f);
}
f32 low_amplitude = vibration.low_amplitude * low_frequency_scale;
f32 high_frequency_scale = 1.0;
if (vibration.high_frequency > high_start_sensitivity_limit) {
high_frequency_scale =
std::max(1.0f - (vibration.high_frequency - high_start_sensitivity_limit) /
high_width_sensitivity_limit,
0.3f);
}
f32 high_amplitude = vibration.high_amplitude * high_frequency_scale;
if (sdl_controller) {
return SDL_GameControllerRumble(
sdl_controller.get(), static_cast<u16>(vibration.low_amplitude),
static_cast<u16>(vibration.high_amplitude), rumble_max_duration_ms) != -1;
return SDL_GameControllerRumble(sdl_controller.get(), static_cast<u16>(low_amplitude),
static_cast<u16>(high_amplitude),
rumble_max_duration_ms) != -1;
} else if (sdl_joystick) {
return SDL_JoystickRumble(sdl_joystick.get(), static_cast<u16>(vibration.low_amplitude),
static_cast<u16>(vibration.high_amplitude),
return SDL_JoystickRumble(sdl_joystick.get(), static_cast<u16>(low_amplitude),
static_cast<u16>(high_amplitude),
rumble_max_duration_ms) != -1;
}

View File

@@ -236,13 +236,13 @@ DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode,
return DriverResult::Success;
}
DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubCommand sc,
DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, MCUSubCommand sc,
std::span<const u8> buffer,
MCUCommandResponse& output) {
SubCommandPacket packet{
.output_report = OutputReport::MCU_DATA,
.packet_counter = GetCounter(),
.sub_command = sc,
.mcu_sub_command = sc,
.command_data = {},
};
@@ -269,8 +269,7 @@ DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMod
std::size_t tries{};
do {
const std::vector<u8> mcu_data{static_cast<u8>(MCUMode::Standby)};
const auto result = SendMCUData(report_mode, SubCommand::STATE, mcu_data, output);
const auto result = SendMCUData(report_mode, MCUSubCommand::SetDeviceMode, {}, output);
if (result != DriverResult::Success) {
return result;

View File

@@ -156,7 +156,7 @@ public:
* @param buffer data to be send
* @returns output buffer containing the response
*/
DriverResult SendMCUData(ReportMode report_mode, SubCommand sc, std::span<const u8> buffer,
DriverResult SendMCUData(ReportMode report_mode, MCUSubCommand sc, std::span<const u8> buffer,
MCUCommandResponse& output);
/**

View File

@@ -575,7 +575,6 @@ struct NFCPollingCommandData {
static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size");
struct NFCRequestState {
MCUSubCommand sub_command;
NFCReadCommand command_argument;
u8 packet_id;
INSERT_PADDING_BYTES(0x1);
@@ -587,6 +586,7 @@ struct NFCRequestState {
NFCPollingCommandData nfc_polling;
};
u8 crc;
INSERT_PADDING_BYTES(0x1);
};
static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size");
@@ -659,7 +659,10 @@ struct SubCommandPacket {
OutputReport output_report;
u8 packet_counter;
INSERT_PADDING_BYTES(0x8); // This contains vibration data
SubCommand sub_command;
union {
SubCommand sub_command;
MCUSubCommand mcu_sub_command;
};
std::array<u8, 0x26> command_data;
};
static_assert(sizeof(SubCommandPacket) == 0x31, "SubCommandPacket is an invalid size");

View File

@@ -278,7 +278,6 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
NFCRequestState request{
.sub_command = MCUSubCommand::ReadDeviceMode,
.command_argument = NFCReadCommand::StartPolling,
.packet_id = 0x0,
.packet_flag = MCUPacketFlag::LastCommandPacket,
@@ -296,13 +295,13 @@ DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
std::array<u8, sizeof(NFCRequestState)> request_data{};
memcpy(request_data.data(), &request, sizeof(NFCRequestState));
request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
output);
}
DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
NFCRequestState request{
.sub_command = MCUSubCommand::ReadDeviceMode,
.command_argument = NFCReadCommand::StopPolling,
.packet_id = 0x0,
.packet_flag = MCUPacketFlag::LastCommandPacket,
@@ -313,13 +312,13 @@ DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
std::array<u8, sizeof(NFCRequestState)> request_data{};
memcpy(request_data.data(), &request, sizeof(NFCRequestState));
request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
output);
}
DriverResult NfcProtocol::SendStartWaitingRecieveRequest(MCUCommandResponse& output) {
NFCRequestState request{
.sub_command = MCUSubCommand::ReadDeviceMode,
.command_argument = NFCReadCommand::StartWaitingRecieve,
.packet_id = 0x0,
.packet_flag = MCUPacketFlag::LastCommandPacket,
@@ -330,13 +329,13 @@ DriverResult NfcProtocol::SendStartWaitingRecieveRequest(MCUCommandResponse& out
std::vector<u8> request_data(sizeof(NFCRequestState));
memcpy(request_data.data(), &request, sizeof(NFCRequestState));
request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
output);
}
DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) {
NFCRequestState request{
.sub_command = MCUSubCommand::ReadDeviceMode,
.command_argument = NFCReadCommand::Ntag,
.packet_id = 0x0,
.packet_flag = MCUPacketFlag::LastCommandPacket,
@@ -355,8 +354,9 @@ DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCP
std::array<u8, sizeof(NFCRequestState)> request_data{};
memcpy(request_data.data(), &request, sizeof(NFCRequestState));
request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
output);
}
NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {

View File

@@ -82,6 +82,9 @@ void MappingFactory::RegisterButton(const MappingData& data) {
new_input.Set("axis", data.index);
new_input.Set("threshold", 0.5f);
break;
case EngineInputType::Motion:
new_input.Set("motion", data.index);
break;
default:
return;
}

View File

@@ -667,7 +667,7 @@ public:
.raw_value = input_engine->GetAxis(identifier, axis_z),
.properties = properties_z,
};
status.delta_timestamp = 5000;
status.delta_timestamp = 1000;
status.force_update = true;
return status;
}
@@ -939,6 +939,7 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateAnalogDevice(
.threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f),
.offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f),
.inverted = params.Get("invert", "+") == "-",
.inverted_button = params.Get("inverted", false) != 0,
.toggle = params.Get("toggle", false) != 0,
};
input_engine->PreSetController(identifier);

View File

@@ -473,7 +473,8 @@ void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value) {
}
void EmitSetSampleMask(EmitContext& ctx, Id value) {
ctx.OpStore(ctx.sample_mask, value);
const Id pointer{ctx.OpAccessChain(ctx.output_u32, ctx.sample_mask, ctx.u32_zero_value)};
ctx.OpStore(pointer, value);
}
void EmitSetFragDepth(EmitContext& ctx, Id value) {

View File

@@ -1572,7 +1572,8 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
Decorate(frag_depth, spv::Decoration::BuiltIn, spv::BuiltIn::FragDepth);
}
if (info.stores_sample_mask) {
sample_mask = DefineOutput(*this, U32[1], std::nullopt);
const Id array_type{TypeArray(U32[1], Const(1U))};
sample_mask = DefineOutput(*this, array_type, std::nullopt);
Decorate(sample_mask, spv::Decoration::BuiltIn, spv::BuiltIn::SampleMask);
}
break;

View File

@@ -535,12 +535,12 @@ TEST_CASE("MemoryTracker: Cached write downloads") {
memory_track->MarkRegionAsGpuModified(c + PAGE, PAGE);
int num = 0;
memory_track->ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; });
REQUIRE(num == 1);
REQUIRE(num == 0);
num = 0;
memory_track->ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
REQUIRE(num == 0);
REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE, PAGE));
REQUIRE(!memory_track->IsRegionGpuModified(c + PAGE, PAGE));
REQUIRE(memory_track->IsRegionGpuModified(c + PAGE, PAGE));
memory_track->FlushCachedWrites();
REQUIRE(memory_track->IsRegionCpuModified(c + PAGE, PAGE));
REQUIRE(!memory_track->IsRegionGpuModified(c + PAGE, PAGE));

View File

@@ -18,6 +18,7 @@ namespace VideoCommon {
enum class BufferFlagBits {
Picked = 1 << 0,
CachedWrites = 1 << 1,
PreemtiveDownload = 1 << 2,
};
DECLARE_ENUM_FLAG_OPERATORS(BufferFlagBits)
@@ -54,6 +55,10 @@ public:
flags |= BufferFlagBits::Picked;
}
void MarkPreemtiveDownload() noexcept {
flags |= BufferFlagBits::PreemtiveDownload;
}
/// Unmark buffer as picked
void Unpick() noexcept {
flags &= ~BufferFlagBits::Picked;
@@ -84,6 +89,10 @@ public:
return True(flags & BufferFlagBits::CachedWrites);
}
bool IsPreemtiveDownload() const noexcept {
return True(flags & BufferFlagBits::PreemtiveDownload);
}
/// Returns the base CPU address of the buffer
[[nodiscard]] VAddr CpuAddr() const noexcept {
return cpu_addr;

View File

@@ -23,8 +23,6 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
common_ranges.clear();
inline_buffer_id = NULL_BUFFER_ID;
active_async_buffers = !Settings::IsGPULevelHigh();
if (!runtime.CanReportMemoryUsage()) {
minimum_memory = DEFAULT_EXPECTED_MEMORY;
critical_memory = DEFAULT_CRITICAL_MEMORY;
@@ -75,8 +73,6 @@ void BufferCache<P>::TickFrame() {
uniform_cache_hits[0] = 0;
uniform_cache_shots[0] = 0;
active_async_buffers = !Settings::IsGPULevelHigh();
const bool skip_preferred = hits * 256 < shots * 251;
uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
@@ -100,51 +96,50 @@ void BufferCache<P>::TickFrame() {
template <class P>
void BufferCache<P>::WriteMemory(VAddr cpu_addr, u64 size) {
memory_tracker.MarkRegionAsCpuModified(cpu_addr, size);
if (memory_tracker.IsRegionGpuModified(cpu_addr, size)) {
const IntervalType subtract_interval{cpu_addr, cpu_addr + size};
ClearDownload(subtract_interval);
common_ranges.subtract(subtract_interval);
}
memory_tracker.MarkRegionAsCpuModified(cpu_addr, size);
}
template <class P>
void BufferCache<P>::CachedWriteMemory(VAddr cpu_addr, u64 size) {
memory_tracker.CachedCpuWrite(cpu_addr, size);
const IntervalType add_interval{Common::AlignDown(cpu_addr, YUZU_PAGESIZE),
Common::AlignUp(cpu_addr + size, YUZU_PAGESIZE)};
cached_ranges.add(add_interval);
}
template <class P>
std::optional<VideoCore::RasterizerDownloadArea> BufferCache<P>::GetFlushArea(VAddr cpu_addr,
u64 size) {
std::optional<VideoCore::RasterizerDownloadArea> area{};
area.emplace();
VAddr cpu_addr_start_aligned = Common::AlignDown(cpu_addr, Core::Memory::YUZU_PAGESIZE);
VAddr cpu_addr_end_aligned = Common::AlignUp(cpu_addr + size, Core::Memory::YUZU_PAGESIZE);
area->start_address = cpu_addr_start_aligned;
area->end_address = cpu_addr_end_aligned;
if (memory_tracker.IsRegionPreflushable(cpu_addr, size)) {
area->preemtive = true;
return area;
};
area->preemtive =
!IsRegionGpuModified(cpu_addr_start_aligned, cpu_addr_end_aligned - cpu_addr_start_aligned);
memory_tracker.MarkRegionAsPreflushable(cpu_addr_start_aligned,
cpu_addr_end_aligned - cpu_addr_start_aligned);
return area;
}
template <class P>
void BufferCache<P>::DownloadMemory(VAddr cpu_addr, u64 size) {
WaitOnAsyncFlushes(cpu_addr, size);
ForEachBufferInRange(cpu_addr, size, [&](BufferId, Buffer& buffer) {
DownloadBufferMemory(buffer, cpu_addr, size);
});
}
template <class P>
void BufferCache<P>::WaitOnAsyncFlushes(VAddr cpu_addr, u64 size) {
bool must_wait = false;
ForEachInOverlapCounter(async_downloads, cpu_addr, size,
[&](VAddr, VAddr, int) { must_wait = true; });
bool must_release = false;
ForEachInRangeSet(pending_ranges, cpu_addr, size, [&](VAddr, VAddr) { must_release = true; });
if (must_release) {
std::function<void()> tmp([]() {});
rasterizer.SignalFence(std::move(tmp));
}
if (must_wait || must_release) {
rasterizer.ReleaseFences();
}
}
template <class P>
void BufferCache<P>::ClearDownload(IntervalType subtract_interval) {
RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1024);
uncommitted_ranges.subtract(subtract_interval);
pending_ranges.subtract(subtract_interval);
for (auto& interval_set : committed_ranges) {
interval_set.subtract(subtract_interval);
}
@@ -164,7 +159,6 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
}
const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount};
WaitOnAsyncFlushes(*cpu_src_address, static_cast<u32>(amount));
ClearDownload(subtract_interval);
BufferId buffer_a;
@@ -192,7 +186,6 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
const IntervalType add_interval{new_base_address, new_base_address + size};
tmp_intervals.push_back(add_interval);
uncommitted_ranges.add(add_interval);
pending_ranges.add(add_interval);
};
ForEachInRangeSet(common_ranges, *cpu_src_address, amount, mirror);
// This subtraction in this order is important for overlapping copies.
@@ -205,7 +198,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
if (has_new_downloads) {
memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
}
std::vector<u8> tmp_buffer(amount);
tmp_buffer.resize(amount);
cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount);
cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount);
return true;
@@ -441,9 +434,7 @@ void BufferCache<P>::BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_add
template <class P>
void BufferCache<P>::FlushCachedWrites() {
cached_write_buffer_ids.clear();
memory_tracker.FlushCachedWrites();
cached_ranges.clear();
}
template <class P>
@@ -474,15 +465,13 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
if (committed_ranges.empty()) {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
if (active_async_buffers) {
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
return;
}
MICROPROFILE_SCOPE(GPU_DownloadMemory);
pending_ranges.clear();
auto it = committed_ranges.begin();
while (it != committed_ranges.end()) {
auto& current_intervals = *it;
@@ -537,64 +526,65 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
committed_ranges.clear();
if (downloads.empty()) {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
if (active_async_buffers) {
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
return;
}
if (active_async_buffers) {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true);
boost::container::small_vector<BufferCopy, 4> normalized_copies;
IntervalSet new_async_range{};
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : downloads) {
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
BufferCopy second_copy{copy};
Buffer& buffer = slot_buffers[buffer_id];
second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset);
const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size};
async_downloads += std::make_pair(base_interval, 1);
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
normalized_copies.push_back(second_copy);
}
runtime.PostCopyBarrier();
pending_downloads.emplace_back(std::move(normalized_copies));
async_buffers.emplace_back(download_staging);
} else {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true);
boost::container::small_vector<BufferCopy, 4> normalized_copies;
IntervalSet new_async_range{};
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : downloads) {
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
BufferCopy second_copy{copy};
Buffer& buffer = slot_buffers[buffer_id];
second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset);
const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size};
async_downloads += std::make_pair(base_interval, 1);
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
normalized_copies.push_back(second_copy);
}
runtime.PostCopyBarrier();
pending_downloads.emplace_back(std::move(normalized_copies));
async_buffers.emplace_back(download_staging);
} else {
if (!Settings::IsGPULevelHigh()) {
committed_ranges.clear();
uncommitted_ranges.clear();
}
} else {
if constexpr (USE_MEMORY_MAPS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : downloads) {
// Have in mind the staging buffer offset for the copy
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies, false);
}
runtime.PostCopyBarrier();
runtime.Finish();
for (const auto& [copy, buffer_id] : downloads) {
const Buffer& buffer = slot_buffers[buffer_id];
const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
// Undo the modified offset
const u64 dst_offset = copy.dst_offset - download_staging.offset;
const u8* read_mapped_memory = download_staging.mapped_span.data() + dst_offset;
cpu_memory.WriteBlockUnsafe(cpu_addr, read_mapped_memory, copy.size);
}
} else {
const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
for (const auto& [copy, buffer_id] : downloads) {
Buffer& buffer = slot_buffers[buffer_id];
buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size));
const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
cpu_memory.WriteBlockUnsafe(cpu_addr, immediate_buffer.data(), copy.size);
if constexpr (USE_MEMORY_MAPS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : downloads) {
// Have in mind the staging buffer offset for the copy
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies,
false);
}
runtime.PostCopyBarrier();
runtime.Finish();
for (const auto& [copy, buffer_id] : downloads) {
const Buffer& buffer = slot_buffers[buffer_id];
const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
// Undo the modified offset
const u64 dst_offset = copy.dst_offset - download_staging.offset;
const u8* read_mapped_memory = download_staging.mapped_span.data() + dst_offset;
cpu_memory.WriteBlockUnsafe(cpu_addr, read_mapped_memory, copy.size);
}
} else {
const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
for (const auto& [copy, buffer_id] : downloads) {
Buffer& buffer = slot_buffers[buffer_id];
buffer.ImmediateDownload(copy.src_offset,
immediate_buffer.subspan(0, copy.size));
const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
cpu_memory.WriteBlockUnsafe(cpu_addr, immediate_buffer.data(), copy.size);
}
}
}
}
@@ -1213,16 +1203,14 @@ void BufferCache<P>::UpdateComputeTextureBuffers() {
template <class P>
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 size) {
memory_tracker.MarkRegionAsGpuModified(cpu_addr, size);
if (memory_tracker.IsRegionCpuModified(cpu_addr, size)) {
SynchronizeBuffer(slot_buffers[buffer_id], cpu_addr, size);
}
memory_tracker.MarkRegionAsGpuModified(cpu_addr, size);
const IntervalType base_interval{cpu_addr, cpu_addr + size};
common_ranges.add(base_interval);
uncommitted_ranges.add(base_interval);
pending_ranges.add(base_interval);
}
template <class P>
@@ -1426,7 +1414,7 @@ bool BufferCache<P>::SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr,
.size = sub_size,
});
total_size_bytes += sub_size;
largest_copy = std::max(largest_copy, sub_size);
largest_copy = std::max<u64>(largest_copy, sub_size);
}
const std::span<BufferCopy> copies_span(copies.data(), copies.size());
UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);
@@ -1629,7 +1617,6 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
replace(transform_feedback_buffers);
replace(compute_uniform_buffers);
replace(compute_storage_buffers);
std::erase(cached_write_buffer_ids, buffer_id);
// Mark the whole buffer as CPU written to stop tracking CPU writes
if (!do_not_mark) {
@@ -1668,14 +1655,15 @@ typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr s
const bool is_nvn_cbuf = cbuf_index == 0;
// The NVN driver buffer (index 0) is known to pack the SSBO address followed by its size.
if (is_nvn_cbuf) {
return gpu_memory->Read<u32>(ssbo_addr + 8);
const u32 ssbo_size = gpu_memory->Read<u32>(ssbo_addr + 8);
if (ssbo_size != 0) {
return ssbo_size;
}
}
// Other titles (notably Doom Eternal) may use STG/LDG on buffer addresses in custom defined
// cbufs, which do not store the sizes adjacent to the addresses, so use the fully
// mapped buffer size for now.
const u32 memory_layout_size = static_cast<u32>(gpu_memory->GetMemoryLayoutSize(gpu_addr));
LOG_INFO(HW_GPU, "Binding storage buffer for cbuf index {}, MemoryLayoutSize 0x{:X}",
cbuf_index, memory_layout_size);
return memory_layout_size;
}();
const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);

View File

@@ -188,6 +188,8 @@ public:
void DownloadMemory(VAddr cpu_addr, u64 size);
std::optional<VideoCore::RasterizerDownloadArea> GetFlushArea(VAddr cpu_addr, u64 size);
bool InlineMemory(VAddr dest_address, size_t copy_size, std::span<const u8> inlined_buffer);
void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size);
@@ -379,8 +381,6 @@ private:
void RunGarbageCollector();
void WaitOnAsyncFlushes(VAddr cpu_addr, u64 size);
void BindHostIndexBuffer();
void BindHostVertexBuffers();
@@ -541,13 +541,10 @@ private:
std::array<std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES>, Empty>
uniform_buffer_binding_sizes{};
std::vector<BufferId> cached_write_buffer_ids;
MemoryTracker memory_tracker;
IntervalSet uncommitted_ranges;
IntervalSet common_ranges;
IntervalSet cached_ranges;
IntervalSet pending_ranges;
std::deque<IntervalSet> committed_ranges;
// Async Buffers
@@ -572,9 +569,8 @@ private:
u64 critical_memory = 0;
BufferId inline_buffer_id;
bool active_async_buffers = false;
std::array<BufferId, ((1ULL << 39) >> CACHING_PAGEBITS)> page_table;
std::vector<u8> tmp_buffer;
};
} // namespace VideoCommon

View File

@@ -66,6 +66,14 @@ public:
});
}
/// Returns true if a region has been marked as Preflushable
[[nodiscard]] bool IsRegionPreflushable(VAddr query_cpu_addr, u64 query_size) noexcept {
return IteratePages<false>(
query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) {
return manager->template IsRegionModified<Type::Preflushable>(offset, size);
});
}
/// Mark region as CPU modified, notifying the rasterizer about this change
void MarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 query_size) {
IteratePages<true>(dirty_cpu_addr, query_size,
@@ -93,6 +101,15 @@ public:
});
}
/// Mark region as modified from the host GPU
void MarkRegionAsPreflushable(VAddr dirty_cpu_addr, u64 query_size) noexcept {
IteratePages<true>(dirty_cpu_addr, query_size,
[](Manager* manager, u64 offset, size_t size) {
manager->template ChangeRegionState<Type::Preflushable, true>(
manager->GetCpuAddr() + offset, size);
});
}
/// Unmark region as modified from the host GPU
void UnmarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 query_size) noexcept {
IteratePages<true>(dirty_cpu_addr, query_size,
@@ -102,6 +119,15 @@ public:
});
}
/// Unmark region as modified from the host GPU
void UnmarkRegionAsPreflushable(VAddr dirty_cpu_addr, u64 query_size) noexcept {
IteratePages<true>(dirty_cpu_addr, query_size,
[](Manager* manager, u64 offset, size_t size) {
manager->template ChangeRegionState<Type::Preflushable, false>(
manager->GetCpuAddr() + offset, size);
});
}
/// Mark region as modified from the CPU
/// but don't mark it as modified until FlusHCachedWrites is called.
void CachedCpuWrite(VAddr dirty_cpu_addr, u64 query_size) {
@@ -170,7 +196,8 @@ private:
std::size_t page_index{cpu_address >> HIGHER_PAGE_BITS};
u64 page_offset{cpu_address & HIGHER_PAGE_MASK};
while (remaining_size > 0) {
const std::size_t copy_amount{std::min(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
const std::size_t copy_amount{
std::min<std::size_t>(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
auto* manager{top_tier[page_index]};
if (manager) {
if constexpr (BOOL_BREAK) {
@@ -206,7 +233,8 @@ private:
u64 begin = std::numeric_limits<u64>::max();
u64 end = 0;
while (remaining_size > 0) {
const std::size_t copy_amount{std::min(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
const std::size_t copy_amount{
std::min<std::size_t>(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
auto* manager{top_tier[page_index]};
const auto execute = [&] {
auto [new_begin, new_end] = func(manager, page_offset, copy_amount);

View File

@@ -26,6 +26,7 @@ enum class Type {
GPU,
CachedCPU,
Untracked,
Preflushable,
};
/// Vector tracking modified pages tightly packed with small vector optimization
@@ -55,17 +56,20 @@ struct Words {
gpu.stack.fill(0);
cached_cpu.stack.fill(0);
untracked.stack.fill(~u64{0});
preflushable.stack.fill(0);
} else {
// Share allocation between CPU and GPU pages and set their default values
u64* const alloc = new u64[num_words * 4];
u64* const alloc = new u64[num_words * 5];
cpu.heap = alloc;
gpu.heap = alloc + num_words;
cached_cpu.heap = alloc + num_words * 2;
untracked.heap = alloc + num_words * 3;
preflushable.heap = alloc + num_words * 4;
std::fill_n(cpu.heap, num_words, ~u64{0});
std::fill_n(gpu.heap, num_words, 0);
std::fill_n(cached_cpu.heap, num_words, 0);
std::fill_n(untracked.heap, num_words, ~u64{0});
std::fill_n(preflushable.heap, num_words, 0);
}
// Clean up tailing bits
const u64 last_word_size = size_bytes % BYTES_PER_WORD;
@@ -88,13 +92,14 @@ struct Words {
gpu = rhs.gpu;
cached_cpu = rhs.cached_cpu;
untracked = rhs.untracked;
preflushable = rhs.preflushable;
rhs.cpu.heap = nullptr;
return *this;
}
Words(Words&& rhs) noexcept
: size_bytes{rhs.size_bytes}, num_words{rhs.num_words}, cpu{rhs.cpu}, gpu{rhs.gpu},
cached_cpu{rhs.cached_cpu}, untracked{rhs.untracked} {
cached_cpu{rhs.cached_cpu}, untracked{rhs.untracked}, preflushable{rhs.preflushable} {
rhs.cpu.heap = nullptr;
}
@@ -129,6 +134,8 @@ struct Words {
return std::span<u64>(cached_cpu.Pointer(IsShort()), num_words);
} else if constexpr (type == Type::Untracked) {
return std::span<u64>(untracked.Pointer(IsShort()), num_words);
} else if constexpr (type == Type::Preflushable) {
return std::span<u64>(preflushable.Pointer(IsShort()), num_words);
}
}
@@ -142,6 +149,8 @@ struct Words {
return std::span<const u64>(cached_cpu.Pointer(IsShort()), num_words);
} else if constexpr (type == Type::Untracked) {
return std::span<const u64>(untracked.Pointer(IsShort()), num_words);
} else if constexpr (type == Type::Preflushable) {
return std::span<const u64>(preflushable.Pointer(IsShort()), num_words);
}
}
@@ -151,6 +160,7 @@ struct Words {
WordsArray<stack_words> gpu;
WordsArray<stack_words> cached_cpu;
WordsArray<stack_words> untracked;
WordsArray<stack_words> preflushable;
};
template <class RasterizerInterface, size_t stack_words = 1>
@@ -292,6 +302,9 @@ public:
(pending_pointer - pending_offset) * BYTES_PER_PAGE);
};
IterateWords(offset, size, [&](size_t index, u64 mask) {
if constexpr (type == Type::GPU) {
mask &= ~untracked_words[index];
}
const u64 word = state_words[index] & mask;
if constexpr (clear) {
if constexpr (type == Type::CPU || type == Type::CachedCPU) {
@@ -340,8 +353,13 @@ public:
static_assert(type != Type::Untracked);
const std::span<const u64> state_words = words.template Span<type>();
[[maybe_unused]] const std::span<const u64> untracked_words =
words.template Span<Type::Untracked>();
bool result = false;
IterateWords(offset, size, [&](size_t index, u64 mask) {
if constexpr (type == Type::GPU) {
mask &= ~untracked_words[index];
}
const u64 word = state_words[index] & mask;
if (word != 0) {
result = true;
@@ -362,9 +380,14 @@ public:
[[nodiscard]] std::pair<u64, u64> ModifiedRegion(u64 offset, u64 size) const noexcept {
static_assert(type != Type::Untracked);
const std::span<const u64> state_words = words.template Span<type>();
[[maybe_unused]] const std::span<const u64> untracked_words =
words.template Span<Type::Untracked>();
u64 begin = std::numeric_limits<u64>::max();
u64 end = 0;
IterateWords(offset, size, [&](size_t index, u64 mask) {
if constexpr (type == Type::GPU) {
mask &= ~untracked_words[index];
}
const u64 word = state_words[index] & mask;
if (word == 0) {
return;

View File

@@ -126,15 +126,14 @@ constexpr std::array VIEW_CLASS_ASTC_8x8_RGBA{
PixelFormat::ASTC_2D_8X8_SRGB,
};
// Missing formats:
// PixelFormat::ASTC_2D_10X5_UNORM
// PixelFormat::ASTC_2D_10X5_SRGB
// Missing formats:
// PixelFormat::ASTC_2D_10X6_SRGB
constexpr std::array VIEW_CLASS_ASTC_10x5_RGBA{
PixelFormat::ASTC_2D_10X5_UNORM,
PixelFormat::ASTC_2D_10X5_SRGB,
};
constexpr std::array VIEW_CLASS_ASTC_10x6_RGBA{
PixelFormat::ASTC_2D_10X6_UNORM,
PixelFormat::ASTC_2D_10X6_SRGB,
};
constexpr std::array VIEW_CLASS_ASTC_10x8_RGBA{
@@ -147,9 +146,10 @@ constexpr std::array VIEW_CLASS_ASTC_10x10_RGBA{
PixelFormat::ASTC_2D_10X10_SRGB,
};
// Missing formats
// ASTC_2D_12X10_UNORM,
// ASTC_2D_12X10_SRGB,
constexpr std::array VIEW_CLASS_ASTC_12x10_RGBA{
PixelFormat::ASTC_2D_12X10_UNORM,
PixelFormat::ASTC_2D_12X10_SRGB,
};
constexpr std::array VIEW_CLASS_ASTC_12x12_RGBA{
PixelFormat::ASTC_2D_12X12_UNORM,
@@ -229,9 +229,11 @@ constexpr Table MakeViewTable() {
EnableRange(view, VIEW_CLASS_ASTC_6x6_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_8x5_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_8x8_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_10x5_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_10x6_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_10x8_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_10x10_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_12x10_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_12x12_RGBA);
return view;
}

View File

@@ -223,7 +223,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
write_buffer.resize_destructive(dst_size);
memory_manager.ReadBlock(src_operand.address, read_buffer.data(), src_size);
memory_manager.ReadBlockUnsafe(dst_operand.address, write_buffer.data(), dst_size);
memory_manager.ReadBlock(dst_operand.address, write_buffer.data(), dst_size);
UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset,
src_params.origin.y, x_elements, regs.line_count, block_height, block_depth,
@@ -288,11 +288,7 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
write_buffer.resize_destructive(dst_size);
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
if (Settings::IsGPULevelExtreme()) {
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
} else {
memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
}
memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
// If the input is linear and the output is tiled, swizzle the input and copy it over.
SwizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset,

View File

@@ -5,6 +5,7 @@
#include <cmath>
#include <vector>
#include "common/scratch_buffer.h"
#include "video_core/engines/sw_blitter/blitter.h"
#include "video_core/engines/sw_blitter/converter.h"
#include "video_core/memory_manager.h"
@@ -112,11 +113,11 @@ void Bilinear(std::span<const f32> input, std::span<f32> output, size_t src_widt
} // namespace
struct SoftwareBlitEngine::BlitEngineImpl {
std::vector<u8> tmp_buffer;
std::vector<u8> src_buffer;
std::vector<u8> dst_buffer;
std::vector<f32> intermediate_src;
std::vector<f32> intermediate_dst;
Common::ScratchBuffer<u8> tmp_buffer;
Common::ScratchBuffer<u8> src_buffer;
Common::ScratchBuffer<u8> dst_buffer;
Common::ScratchBuffer<f32> intermediate_src;
Common::ScratchBuffer<f32> intermediate_dst;
ConverterFactory converter_factory;
};
@@ -158,14 +159,14 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
const auto src_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
const auto dst_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(dst.format));
const size_t src_size = get_surface_size(src, src_bytes_per_pixel);
impl->tmp_buffer.resize(src_size);
impl->tmp_buffer.resize_destructive(src_size);
memory_manager.ReadBlock(src.Address(), impl->tmp_buffer.data(), src_size);
const size_t src_copy_size = src_extent_x * src_extent_y * src_bytes_per_pixel;
const size_t dst_copy_size = dst_extent_x * dst_extent_y * dst_bytes_per_pixel;
impl->src_buffer.resize(src_copy_size);
impl->src_buffer.resize_destructive(src_copy_size);
const bool no_passthrough =
src.format != dst.format || src_extent_x != dst_extent_x || src_extent_y != dst_extent_y;
@@ -177,8 +178,10 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
const auto convertion_phase_ir = [&]() {
auto* input_converter = impl->converter_factory.GetFormatConverter(src.format);
impl->intermediate_src.resize((src_copy_size / src_bytes_per_pixel) * ir_components);
impl->intermediate_dst.resize((dst_copy_size / dst_bytes_per_pixel) * ir_components);
impl->intermediate_src.resize_destructive((src_copy_size / src_bytes_per_pixel) *
ir_components);
impl->intermediate_dst.resize_destructive((dst_copy_size / dst_bytes_per_pixel) *
ir_components);
input_converter->ConvertTo(impl->src_buffer, impl->intermediate_src);
if (config.filter != Fermi2D::Filter::Bilinear) {
@@ -195,7 +198,7 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
// Do actual Blit
impl->dst_buffer.resize(dst_copy_size);
impl->dst_buffer.resize_destructive(dst_copy_size);
if (src.linear == Fermi2D::MemoryLayout::BlockLinear) {
UnswizzleSubrect(impl->src_buffer, impl->tmp_buffer, src_bytes_per_pixel, src.width,
src.height, src.depth, config.src_x0, config.src_y0, src_extent_x,
@@ -218,7 +221,7 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
}
const size_t dst_size = get_surface_size(dst, dst_bytes_per_pixel);
impl->tmp_buffer.resize(dst_size);
impl->tmp_buffer.resize_destructive(dst_size);
memory_manager.ReadBlock(dst.Address(), impl->tmp_buffer.data(), dst_size);
if (dst.linear == Fermi2D::MemoryLayout::BlockLinear) {

View File

@@ -4,13 +4,20 @@
#pragma once
#include <algorithm>
#include <condition_variable>
#include <cstring>
#include <deque>
#include <functional>
#include <memory>
#include <mutex>
#include <thread>
#include <queue>
#include "common/common_types.h"
#include "common/microprofile.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "common/thread.h"
#include "video_core/delayed_destruction_ring.h"
#include "video_core/gpu.h"
#include "video_core/host1x/host1x.h"
@@ -23,15 +30,26 @@ class FenceBase {
public:
explicit FenceBase(bool is_stubbed_) : is_stubbed{is_stubbed_} {}
bool IsStubbed() const {
return is_stubbed;
}
protected:
bool is_stubbed;
};
template <typename TFence, typename TTextureCache, typename TTBufferCache, typename TQueryCache>
template <typename Traits>
class FenceManager {
using TFence = typename Traits::FenceType;
using TTextureCache = typename Traits::TextureCacheType;
using TBufferCache = typename Traits::BufferCacheType;
using TQueryCache = typename Traits::QueryCacheType;
static constexpr bool can_async_check = Traits::HAS_ASYNC_CHECK;
public:
/// Notify the fence manager about a new frame
void TickFrame() {
std::unique_lock lock(ring_guard);
delayed_destruction_ring.Tick();
}
@@ -41,22 +59,43 @@ public:
buffer_cache.AccumulateFlushes();
}
void SignalReference() {
std::function<void()> do_nothing([] {});
SignalFence(std::move(do_nothing));
}
void SyncOperation(std::function<void()>&& func) {
uncommitted_operations.emplace_back(std::move(func));
}
void SignalFence(std::function<void()>&& func) {
TryReleasePendingFences();
rasterizer.InvalidateGPUCache();
bool delay_fence = Settings::IsGPULevelHigh();
if constexpr (!can_async_check) {
TryReleasePendingFences<false>();
}
const bool should_flush = ShouldFlush();
CommitAsyncFlushes();
uncommitted_operations.emplace_back(std::move(func));
CommitOperations();
TFence new_fence = CreateFence(!should_flush);
fences.push(new_fence);
if constexpr (can_async_check) {
guard.lock();
}
if (delay_fence) {
uncommitted_operations.emplace_back(std::move(func));
}
pending_operations.emplace_back(std::move(uncommitted_operations));
QueueFence(new_fence);
if (!delay_fence) {
func();
}
fences.push(std::move(new_fence));
if (should_flush) {
rasterizer.FlushCommands();
}
if constexpr (can_async_check) {
guard.unlock();
cv.notify_all();
}
}
void SignalSyncPoint(u32 value) {
@@ -66,29 +105,30 @@ public:
}
void WaitPendingFences() {
while (!fences.empty()) {
TFence& current_fence = fences.front();
if (ShouldWait()) {
WaitFence(current_fence);
}
PopAsyncFlushes();
auto operations = std::move(pending_operations.front());
pending_operations.pop_front();
for (auto& operation : operations) {
operation();
}
PopFence();
if constexpr (!can_async_check) {
TryReleasePendingFences<true>();
}
}
protected:
explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_,
TTextureCache& texture_cache_, TTBufferCache& buffer_cache_,
TTextureCache& texture_cache_, TBufferCache& buffer_cache_,
TQueryCache& query_cache_)
: rasterizer{rasterizer_}, gpu{gpu_}, syncpoint_manager{gpu.Host1x().GetSyncpointManager()},
texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {}
texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {
if constexpr (can_async_check) {
fence_thread =
std::jthread([this](std::stop_token token) { ReleaseThreadFunc(token); });
}
}
virtual ~FenceManager() = default;
virtual ~FenceManager() {
if constexpr (can_async_check) {
fence_thread.request_stop();
cv.notify_all();
fence_thread.join();
}
}
/// Creates a Fence Interface, does not create a backend fence if 'is_stubbed' is
/// true
@@ -104,15 +144,20 @@ protected:
Tegra::GPU& gpu;
Tegra::Host1x::SyncpointManager& syncpoint_manager;
TTextureCache& texture_cache;
TTBufferCache& buffer_cache;
TBufferCache& buffer_cache;
TQueryCache& query_cache;
private:
template <bool force_wait>
void TryReleasePendingFences() {
while (!fences.empty()) {
TFence& current_fence = fences.front();
if (ShouldWait() && !IsFenceSignaled(current_fence)) {
return;
if constexpr (force_wait) {
WaitFence(current_fence);
} else {
return;
}
}
PopAsyncFlushes();
auto operations = std::move(pending_operations.front());
@@ -120,7 +165,49 @@ private:
for (auto& operation : operations) {
operation();
}
PopFence();
{
std::unique_lock lock(ring_guard);
delayed_destruction_ring.Push(std::move(current_fence));
}
fences.pop();
}
}
void ReleaseThreadFunc(std::stop_token stop_token) {
std::string name = "GPUFencingThread";
MicroProfileOnThreadCreate(name.c_str());
// Cleanup
SCOPE_EXIT({ MicroProfileOnThreadExit(); });
Common::SetCurrentThreadName(name.c_str());
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
TFence current_fence;
std::deque<std::function<void()>> current_operations;
while (!stop_token.stop_requested()) {
{
std::unique_lock lock(guard);
cv.wait(lock, [&] { return stop_token.stop_requested() || !fences.empty(); });
if (stop_token.stop_requested()) [[unlikely]] {
return;
}
current_fence = std::move(fences.front());
current_operations = std::move(pending_operations.front());
fences.pop();
pending_operations.pop_front();
}
if (!current_fence->IsStubbed()) {
WaitFence(current_fence);
}
PopAsyncFlushes();
for (auto& operation : current_operations) {
operation();
}
{
std::unique_lock lock(ring_guard);
delayed_destruction_ring.Push(std::move(current_fence));
}
}
}
@@ -154,19 +241,16 @@ private:
query_cache.CommitAsyncFlushes();
}
void PopFence() {
delayed_destruction_ring.Push(std::move(fences.front()));
fences.pop();
}
void CommitOperations() {
pending_operations.emplace_back(std::move(uncommitted_operations));
}
std::queue<TFence> fences;
std::deque<std::function<void()>> uncommitted_operations;
std::deque<std::deque<std::function<void()>>> pending_operations;
std::mutex guard;
std::mutex ring_guard;
std::condition_variable cv;
std::jthread fence_thread;
DelayedDestructionRing<TFence, 6> delayed_destruction_ring;
};

View File

@@ -283,6 +283,21 @@ struct GPU::Impl {
gpu_thread.FlushRegion(addr, size);
}
VideoCore::RasterizerDownloadArea OnCPURead(VAddr addr, u64 size) {
auto raster_area = rasterizer->GetFlushArea(addr, size);
if (raster_area.preemtive) {
return raster_area;
}
raster_area.preemtive = true;
const u64 fence = RequestSyncOperation([this, &raster_area]() {
rasterizer->FlushRegion(raster_area.start_address,
raster_area.end_address - raster_area.start_address);
});
gpu_thread.TickGPU();
WaitForSyncOperation(fence);
return raster_area;
}
/// Notify rasterizer that any caches of the specified region should be invalidated
void InvalidateRegion(VAddr addr, u64 size) {
gpu_thread.InvalidateRegion(addr, size);
@@ -538,6 +553,10 @@ void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
impl->SwapBuffers(framebuffer);
}
VideoCore::RasterizerDownloadArea GPU::OnCPURead(VAddr addr, u64 size) {
return impl->OnCPURead(addr, size);
}
void GPU::FlushRegion(VAddr addr, u64 size) {
impl->FlushRegion(addr, size);
}

View File

@@ -10,6 +10,7 @@
#include "core/hle/service/nvdrv/nvdata.h"
#include "video_core/cdma_pusher.h"
#include "video_core/framebuffer_config.h"
#include "video_core/rasterizer_download_area.h"
namespace Core {
class System;
@@ -240,6 +241,9 @@ public:
/// Swap buffers (render frame)
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
[[nodiscard]] VideoCore::RasterizerDownloadArea OnCPURead(VAddr addr, u64 size);
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
void FlushRegion(VAddr addr, u64 size);

View File

@@ -82,6 +82,7 @@ void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) {
}
PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const {
std::unique_lock<std::mutex> lock(guard);
return kind_map.GetValueAt(gpu_addr);
}
@@ -160,7 +161,10 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr
}
remaining_size -= big_page_size;
}
kind_map.Map(gpu_addr, gpu_addr + size, kind);
{
std::unique_lock<std::mutex> lock(guard);
kind_map.Map(gpu_addr, gpu_addr + size, kind);
}
return gpu_addr;
}
@@ -553,6 +557,7 @@ size_t MemoryManager::MaxContinuousRange(GPUVAddr gpu_addr, size_t size) const {
}
size_t MemoryManager::GetMemoryLayoutSize(GPUVAddr gpu_addr, size_t max_size) const {
std::unique_lock<std::mutex> lock(guard);
return kind_map.GetContinuousSizeFrom(gpu_addr);
}
@@ -745,10 +750,10 @@ void MemoryManager::FlushCaching() {
return;
}
accumulator->Callback([this](GPUVAddr addr, size_t size) {
GetSubmappedRangeImpl<false>(addr, size, page_stash);
GetSubmappedRangeImpl<false>(addr, size, page_stash2);
});
rasterizer->InnerInvalidation(page_stash);
page_stash.clear();
rasterizer->InnerInvalidation(page_stash2);
page_stash2.clear();
accumulator->Clear();
}

Some files were not shown because too many files have changed in this diff Show More