Compare commits

...

101 Commits

Author SHA1 Message Date
comex
994f497781 Overhaul EmuWindow::PollEvents to fix yuzu-cmd calling SDL_PollEvents off main thread
EmuWindow::PollEvents was called from the GPU thread (or the CPU thread
in sync-GPU mode) when swapping buffers.  It had three implementations:

- In GRenderWindow, it didn't actually poll events, just set a flag and
  emit a signal to indicate that a frame was displayed.

- In EmuWindow_SDL2_Hide, it did nothing.

- In EmuWindow_SDL2, it did call SDL_PollEvents, but this is wrong
  because SDL_PollEvents is supposed to be called on the thread that set
  up video - in this case, the main thread, which was sleeping in a
  busyloop (regardless of whether sync-GPU was enabled).  On macOS this
  causes a crash.

To fix this:

- Rename EmuWindow::PollEvents to OnFrameDisplayed, and give it a
  default implementation that does nothing.

- In EmuWindow_SDL2, do not override OnFrameDisplayed, but instead have
  the main thread call SDL_WaitEvent in a loop.
2020-11-23 17:58:49 -05:00
bunnei
5d1447897a Merge pull request #4451 from slashiee/extended-logging
logging/settings: Increase maximum log size to 100 MB and add extended logging option
2020-11-23 13:34:15 -08:00
bunnei
2b05c32343 Merge pull request #4969 from liushuyu/master
CI: move refreshenv to the configure step
2020-11-22 20:27:16 -08:00
liushuyu
b546640c41 CI: move refreshenv to the configure step...
... so that cmake can find the Vulkan SDK binaries
2020-11-22 16:19:34 -07:00
bunnei
3a85bc1e77 Merge pull request #4944 from lioncash/system-rem
patch_manager: Remove usages of the global system instance
2020-11-21 22:12:34 -08:00
Morph
e13a91fa9b Merge pull request #4954 from lioncash/compare
gl_rasterizer: Make floating-point literal a float
2020-11-22 09:55:23 +08:00
bunnei
5502f39125 Merge pull request #4955 from lioncash/move3
async_shaders: std::move data within QueueVulkanShader()
2020-11-21 01:21:08 -08:00
Rodrigo Locatti
ba3dd7b78f Merge pull request #4960 from liushuyu/master
ci: install Vulkan SDK in MSVC build
2020-11-21 03:47:17 -03:00
bunnei
afd0e2ee87 Merge pull request #4907 from ogniK5377/nvdrv-cleanup
core: Make nvservices more standardized
2020-11-20 22:15:44 -08:00
liushuyu
185bf3fd28 ci: install Vulkan SDK in MSVC build 2020-11-20 23:01:59 -07:00
LC
d88baa746b Merge pull request #4957 from ReinUsesLisp/alpha-test-rt
gl_rasterizer: Remove warning of untested alpha test
2020-11-20 21:19:06 -05:00
ReinUsesLisp
acc14d233f gl_rasterizer: Remove warning of untested alpha test
Alpha test has been proven to only affect the first render target.
2020-11-20 23:17:40 -03:00
bunnei
b00f4abe36 Merge pull request #4953 from lioncash/shader-shadow
shader_bytecode: Eliminate variable shadowing
2020-11-20 16:58:14 -08:00
bunnei
c47c3d723f Merge pull request #4951 from bunnei/olsc-stub
hle: service: Stub OLSC Initialize and SetSaveDataBackupSettingEnabled functions.
2020-11-20 14:06:37 -08:00
bunnei
3794c91145 olsc: Move member initialization to after member functions. 2020-11-20 10:50:50 -08:00
Lioncash
01db5cf203 async_shaders: emplace threads into the worker thread vector
Same behavior, but constructs the threads in place instead of moving
them.
2020-11-20 04:46:56 -05:00
Lioncash
ba3916fc67 async_shaders: Simplify implementation of GetCompletedWork()
This is equivalent to moving all the contents and then clearing the
vector. This avoids a redundant allocation.
2020-11-20 04:44:44 -05:00
Lioncash
3fcc98e11a async_shaders: Simplify moving data into the pending queue 2020-11-20 04:41:29 -05:00
Lioncash
5b441fa25d async_shaders: std::move data within QueueVulkanShader()
Same behavior, but avoids redundant copies.

While we're at it, we can simplify the pushing of the parameters into
the pending queue.
2020-11-20 04:38:18 -05:00
Lioncash
8469b76630 gl_rasterizer: Make floating-point literal a float
Gets rid of an unnecessary expansion from float to double.
2020-11-20 04:24:33 -05:00
Lioncash
b7cd5d742e shader_bytecode: Make use of [[nodiscard]] where applicable
Ensures that all queried values are made use of.
2020-11-20 02:20:37 -05:00
Lioncash
56ecafc204 shader_bytecode: Eliminate variable shadowing 2020-11-20 02:13:45 -05:00
Morph
715f0c3b0c Merge pull request #4941 from lioncash/config
configure_input_player: Use static qualifier for IsProfileNameValid()
2020-11-20 14:16:01 +08:00
LC
bba7e8ea4b Merge pull request #4950 from german77/RumbleStrenght
Modify rumble amplification
2020-11-20 00:40:09 -05:00
LC
e883101999 Merge pull request #4952 from ReinUsesLisp/bit-cast
common/bit_cast: Add function matching std::bit_cast without constexpr
2020-11-20 00:39:30 -05:00
Rodrigo Locatti
1889b641d9 Merge pull request #4308 from ReinUsesLisp/maxwell-3d-funcs
maxwell_3d: Move code to separate functions and insert instead of push_back
2020-11-20 01:57:22 -03:00
ReinUsesLisp
3f2e605dd1 common/bit_cast: Add function matching std::bit_cast without constexpr
Add a std::bit_cast-like function archiving the same runtime results as
the standard function, without compile time support.

This allows us to use bit_cast while we wait for compiler support, it
can be trivially replaced in the future.
2020-11-20 01:52:37 -03:00
bunnei
6971d08893 Merge pull request #4948 from lioncash/page-resize
virtual_buffer: Do nothing on resize() calls with same sizes
2020-11-19 12:39:38 -08:00
bunnei
6e37676482 hle: service: Stub OLSC Initialize and SetSaveDataBackupSettingEnabled functions.
- Used by Animal Cross: New Horizons v1.6.0 update, minimal stub gets this update working.
2020-11-19 12:36:09 -08:00
german77
5b6545b141 Modify rumble amplification 2020-11-19 11:30:52 -06:00
Lioncash
412044960a virtual_buffer: Do nothing on resize() calls with same sizes
Prevents us from churning memory by freeing and reallocating a memory
block that would have already been adequate as is.
2020-11-19 07:54:03 -05:00
bunnei
92344da20c Merge pull request #4936 from lioncash/page
page_table: Allow page tables to be moved
2020-11-18 20:40:10 -08:00
Lioncash
6f8a06bac5 patch_manager: Remove usages of the global system instance
With this, only 19 usages of the global system instance remain within
the core library.

We're almost there.
2020-11-18 09:36:48 -05:00
Lioncash
be4fc777c0 configure_input_player: Use static qualifier for IsProfileNameValid()
This is a static member function, so we don't need use an existing
instance to call this function.
2020-11-17 23:12:44 -05:00
bunnei
abda366362 Merge pull request #4866 from Morph1984/mjolnir-p3-prod
Project Mjölnir: Part 3 - Controller Profiles and Vibration Rework
2020-11-17 20:02:27 -08:00
Lioncash
0ca91ced2d virtual_buffer: Add compile-time type-safety guarantees with VirtualBuffer
VirtualBuffer makes use of VirtualAlloc (on Windows) and mmap() (on
other platforms). Neither of these ensure that non-trivial objects are
properly constructed in the allocated memory.

To prevent potential undefined behavior occurring due to that, we can
add a static assert to loudly complain about cases where that is done.
2020-11-17 20:09:58 -05:00
Lioncash
b3c8997829 page_table: Allow page tables to be moved
Makes page tables and virtual buffers able to be moved, but not copied,
making the interface more flexible.

Previously, with the destructor specified, but no move assignment or
constructor specified, they wouldn't be implicitly generated.
2020-11-17 20:08:20 -05:00
Lioncash
3cfd962ef4 page_table: Add missing doxygen parameters to Resize()
Resolves two -Wdocumentation warnings.
2020-11-17 19:45:20 -05:00
Lioncash
0890451c55 page_table: Remove unnecessary header inclusions
Prevents indirect inclusions for these headers.
2020-11-17 19:43:27 -05:00
Chloe
2dc9dbb809 Merge pull request #4933 from lioncash/nodisc-gpu
[gpu, render_base, rasterizer_interface]: Make use of [[nodiscard]] where applicable
2020-11-18 01:40:03 +11:00
Lioncash
70812ec57b rasterizer_interface: Make use of [[nodiscard]] where applicable 2020-11-17 07:19:13 -05:00
Lioncash
a78021580d render_base: Make use of [[nodiscard]] where applicable 2020-11-17 07:19:12 -05:00
Lioncash
b928fca114 gpu: Make use of [[nodiscard]] where applicable 2020-11-17 07:19:09 -05:00
bunnei
8ace3959a5 Merge pull request #4929 from lioncash/nodiscard-input
motion_input: Mark member functions as [[nodiscard]] where applicable
2020-11-16 21:40:16 -08:00
Morph
e7e8a87927 sdl_impl: Pump SDL Events at 1000 Hz 2020-11-15 23:33:21 -05:00
Morph
b254d528bc configure_input: Accommodate for the mouse input device engine 2020-11-15 23:33:21 -05:00
Morph
ad50209383 hid: Reimplement Begin/EndPermitVibrationSession
Upon further investigation, these commands allow temporary vibrations even when the "Controller Vibration" system setting is disabled. As a result, vibrations are allowed when either the system setting or this flag is set to true. Therefore, we can only block vibrations when both flags are set to false.
2020-11-15 23:33:21 -05:00
Morph
d8ad2f3484 controllers/npad: Load input devices on init 2020-11-15 23:33:21 -05:00
Morph
6f5b942897 configure_input: Update the input profiles for other player tabs 2020-11-15 23:33:21 -05:00
Morph
97b2220a82 general: Fix compiler warnings on linux and miscellaneous changes 2020-11-15 23:33:21 -05:00
Morph
117bdc71e0 sdl_impl: Revert to the "old" method of mapping sticks
Not all controllers have a SDL_GameController binding. This caused controllers not present in the SDL GameController database to have buttons mapped instead of axes.

Furthermore, it was not possible to invert the axes when it could be useful such as emulating a horizontal single joycon or other potential cases. This allows us to invert the axes by reversing the order of mapping (vertical, then horizontal).
2020-11-15 23:33:21 -05:00
Morph
760a9e8693 applets/controller: Change the input button to create input profiles
Co-authored-by: Its-Rei <kupfel@gmail.com>
2020-11-15 23:33:21 -05:00
Morph
30e0d1c973 controllers/npad: Remove the old vibration filter
Previously we used a vibration filter that filters out amplitudes close to each other. It turns out there are cases where this results into vibrations that are too inaccurate. Remove this and move the 100Hz vibration filter (Only allowing a maximum of 100 vibrations per second) from sdl_impl to npad when enable_accurate_vibrations is set to false.
2020-11-15 23:33:21 -05:00
Morph
91c06dae1a input: Disconnect a controller prior to connecting a new one
Some games do not respond to a change in controller type if 1) The controller is not disconnected prior to being reconnected and/or 2) The controller is reconnected instantly after being disconnected.

Since it is not possible to change controllers instantly on hardware and requiring a disconnect prior to connecting a new one, we should emulate this as well with a small delay, fixing the aforementioned issue.
2020-11-15 23:33:21 -05:00
Morph
978ca65f59 hid: Implement InitializeVibrationDevice and IsVibrationDeviceMounted 2020-11-15 23:33:20 -05:00
Morph
e9e1876e82 input_common: Add VibrationDevice and VibrationDeviceFactory
A vibration device is an input device that returns an unsigned byte as status.
It represents whether the vibration device supports vibration or not.
If the status returns 1, it supports vibration. Otherwise, it does not support vibration.
2020-11-15 23:33:20 -05:00
Morph
38110dd485 configure_input: Add per-player vibration
Allows for enabling and modifying vibration and vibration strength per player.
Also adds a toggle for enabling/disabling accurate vibrations.

Co-authored-by: Its-Rei <kupfel@gmail.com>
2020-11-15 23:33:20 -05:00
Morph
d6a41cfc21 settings: Remove global vibration strength modifier
This will be replaced in favor of per-player vibration strength modifiers.
2020-11-15 23:33:20 -05:00
Morph
92fa5257c7 hid: Mark Begin/EndPermitVibrationSession as stubs
The implementation of these commands seem incomplete and causes rumble in Super Mario Party to stop working since only EndPermitVibrationSession is called. Thus, these are better off being marked as a stub until this can be investigated more thoroughly.
2020-11-15 23:33:20 -05:00
Morph
373408ae8c controllers/npad: Send an empty vibration on destruction/deactivation
This stops all controllers from continuously vibrating when emulation is stopped.
2020-11-15 23:33:20 -05:00
Morph
70f16f1722 hid: Stub IsVibrationDeviceMounted
- Used in Super Mario Odyssey
2020-11-15 23:33:20 -05:00
Morph
9b501af8e3 controllers/npad: Add heuristics to reduce rumble state changes
Sending too many state changes in a short period of time can cause massive performance issues.
As a result, we have to use several heuristics to reduce the number of state changes to minimize/eliminate this performance impact while maintaining the quality of these vibrations as much as possible.
2020-11-15 23:33:20 -05:00
Morph
652d6766d5 configure_input: Hook up the vibration percentage spinbox
This allows setting the vibration strength percentage anywhere from 1% to 100%.
Also hooks up the remaining motion button and checkbox in the Controller Applet.
2020-11-15 23:33:20 -05:00
Morph
e02ef3c3be controllers/npad: Stop games from vibrating incorrect controllers
Fixes vibration in 1-2 Switch and potentially other games where they would vibrate both players' joycons at the same time.
2020-11-15 23:33:20 -05:00
Morph
07b81f57ba hid: Fix controller rumble based on new research
This fixes the issue where rumble is only sent to the first controller.
Now, individual controllers can receive their own rumble commands.
2020-11-15 23:33:20 -05:00
Morph
31de52513e hid: Pop a struct of parameters instead of popping individual parameters
Some parameters need to be doubleword aligned due to the presence of the applet_resource_user_id.
Previously, this value was invalid in many commands where it was not doubleword aligned when popped.
2020-11-15 23:33:20 -05:00
Morph
e3c2749986 hid: Reorder all HID commands
Reorders all HID commands in command id order.
2020-11-15 23:33:20 -05:00
Morph
b92bf51ae1 hid: Implement GetVibrationDeviceInfo
The first u32 describes the vibration device type which is a Linear Resonant Actuator used in Nintendo Switch controller hardware.

The second u32 describes the vibration device position, in this case distinguishing between left and right vibration actuators.

Pro Controllers have 2 LRAs each that can vibrate independently of each other, which means they have 2 distinct vibration device handles to distinguish between the two actuators.

Similarly for joycons, the left joycon can be distinguished from the right joycon through the vibration device handle since each joycon has 1 LRA.
2020-11-15 23:33:20 -05:00
Morph
16e2e1c45f hid: Stub InitializeVibrationDevice 2020-11-15 23:33:20 -05:00
Morph
428ce8ec29 controllers/npad: Rename NPadType to NpadStyleSet
This more accurately represents the underlying type and avoids confusion with NpadType
2020-11-15 23:33:20 -05:00
Morph
0a966e2cac controllers/npad: Add DeviceHandle struct
A DeviceHandle describes a vibration device or six-axis sensor based on the npad type, npad id, and device index/position
2020-11-15 23:33:20 -05:00
Morph
ceb7b11f16 configure_input_player: Change "Defaults" button behavior
RestoreDefaults() now restores the selected devices' mappings using UpdateMappingWithDefaults().
This allows us to move the keyboard mapping from RestoreDefaults() to UpdateMappingWithDefaults().
2020-11-15 23:33:20 -05:00
Morph
8f2959f680 settings: Preparation for per-game input settings 2020-11-15 23:33:20 -05:00
Morph
8ead176639 udp/client: Reduce testing period to 5 seconds 2020-11-15 23:33:19 -05:00
Morph
64e174237e config: Migrate config files into config/custom
Co-authored-by: lat9nq <lat9nq@virginia.edu>
2020-11-15 23:33:19 -05:00
Morph
c0c4ed0d3b controllers/npad: Connect a controller on init if none are connected 2020-11-15 23:33:19 -05:00
Morph
5cafa70d3b applets/controller: Auto accept a valid single player configuration 2020-11-15 23:33:19 -05:00
Morph
484623cd61 bootmanager: Allow mouse clicks only if touch is disabled
Previously mouse clicks will not register when touch is disabled.
This rectifies that and allows mouse clicks to be mapped to other buttons if the touchscreen is disabled.
2020-11-15 23:33:19 -05:00
Morph
57d89e291d input_profiles: Implement input profiles 2020-11-15 23:33:19 -05:00
Morph
75eaab2e0f configure_input_player: Implement input exclusivity and persistence
With this, the "Input Devices" combobox should accurately reflect the input device being used and disallows inputs from other input devices unless the input device is set to "Any".
2020-11-15 23:33:19 -05:00
Morph
9d4edd4e88 ui/themes: Cleanup UI 2020-11-15 23:33:19 -05:00
Lioncash
0a50ba3bd1 motion_input: Mark constructor as explicit 2020-11-15 14:20:41 -05:00
Lioncash
cb826bcee7 motion_input: Mark member functions as [[nodiscard]] where applicable 2020-11-15 14:18:09 -05:00
LC
ce718522bc Merge pull request #4914 from lat9nq/gl-warnings
bootmanager: Log and show GL_RENDERER string when GPU is insufficient
2020-11-15 06:33:48 -05:00
bunnei
87f220efff Merge pull request #4895 from Morph1984/cave-story-plus-applet-fix
applets/controller: Introduce additional checks for mode and caller
2020-11-12 21:55:06 -08:00
ReinUsesLisp
622830f4e1 maxwell_3d: Use insert instead of loop push_back
This reduces the overhead of bounds checking on each element.
It won't reduce the cost of allocation because usually this vector's
capacity is usually large enough to hold whatever we push to it.
2020-11-11 19:52:19 -03:00
ReinUsesLisp
9ea8cffe35 maxwell_3d: Move code to separate functions
Deduplicate some code and put it in separate functions so it's easier to
understand and profile.
2020-11-11 19:52:19 -03:00
bunnei
c22d0d9945 Merge pull request #4901 from bunnei/caps-stub
hle: service: caps_u: Stub GetAlbumFileList3AaeAruid.
2020-11-09 21:20:08 -08:00
Chloe Marcec
fc4d692c50 Addressed issues 2020-11-10 15:57:36 +11:00
Chloe Marcec
31c12de0fe core: Make nvservices more standardized 2020-11-10 15:57:35 +11:00
lat9nq
c433c0a746 bootmanager: Address review comments
Changes QMessageBox usages to warnings, as the problems they bring to
light are being safely handled by the application and do not warrant
something of the "critical" level.

Changes LOG_CRITICAL to LOG_ERROR for the same reason. Preferring ERROR
to WARNING as yuzu is denying loading of any guest applications after
checking for these conditions.

Moved logging the GL_RENDERER string into GetUnsupportedGLExtensions()
to make more clear that unsupported extensions were already being
logged. Makes placement of the logs easier to understand later, as well.
2020-11-09 22:55:05 -05:00
lat9nq
945cfe234b bootmanager: Log and show GL_RENDERER string when GPU is insufficient
Changes the first message to not include the OpenGL version, as the
error is caused by OpenGL failing to load.

Adds a new check for OpenGL version 4.3. This will display a message
with a similar error as well as the GL_RENDERER string. Adds a CRITICAL
log message when triggered. This prevents a crash with yuzu trying to
use older OpenGL versions.

Modifies the unsupported extension message to output the GL_RENDERER
string in the message, as well as logging the string.
2020-11-09 22:12:41 -05:00
Rodrigo Locatti
9b24197ca0 Merge pull request #4909 from lioncash/interrupt
cpu_interrupt_handler: Mark move contructor/assignment as deleted
2020-11-08 22:09:40 -03:00
Rodrigo Locatti
8008b5ddc9 Merge pull request #4910 from lioncash/service
ipc_helpers: Remove usage of the global system instance
2020-11-08 19:11:31 -03:00
Lioncash
da7be67daf ipc_helpers: Remove usage of the global system instance
Resolves numerous deprecation warnings throughout the codebase due to
inclusion of this header. Now building core should be significantly less
noisy (and also relying on less global state).

This also uncovered quite a few modules that were relying on indirect
includes, which have also been fixed.
2020-11-08 15:58:11 -05:00
Morph
a6ecdf42bc applets: Rename LibraryAppletVersion to ControllerAppletVersion 2020-11-08 10:04:12 -05:00
Morph
9efbf5309f applets/controller: Pop normal data for StrapGuide and FirmwareUpdate 2020-11-08 09:35:25 -05:00
Morph
af1183a993 applets/controller: Introduce additional checks for mode and caller
Some games like Cave Story+ set invalid values in the ControllerPrivateArg's mode and caller fields.
Use other fields to determine the appropriate mode and caller should either or both fields be invalid.
2020-11-08 09:35:25 -05:00
Morph
88192af8ac applets/controller: Add ControllerUpdateFirmwareArg struct 2020-11-08 09:35:25 -05:00
bunnei
a0d7a2732d hle: service: caps_u: Stub GetAlbumFileList3AaeAruid.
- This works similiar to GetAlbumContentsFileListForApplication.
- Since we do not implement the album, this should be safe to stub for now.
- Used by Super Smash Bros. Ultimate (newer updates) in World of Light.
2020-11-06 22:23:15 -08:00
M&M
43ce33b6cc logging/settings: Increase maximum log size to 100 MB and add extended logging option
The extended logging option is automatically disabled on boot but can be enabled afterwards, allowing the log file to go up to 1 GB during that session.
This commit also fixes a few errors that are present in the general debug menu.
2020-08-24 21:39:56 -07:00
166 changed files with 5625 additions and 3132 deletions

View File

@@ -4,9 +4,11 @@ parameters:
version: ''
steps:
- script: choco install vulkan-sdk
displayName: 'Install vulkan-sdk'
- script: python -m pip install --upgrade pip conan
displayName: 'Install conan'
- script: mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd ..
- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd ..
displayName: 'Configure CMake'
- task: MSBuild@1
displayName: 'Build'

View File

@@ -1,3 +1,7 @@
QAbstractSpinBox {
min-height: 19px;
}
QPushButton#TogglableStatusBarButton {
color: #959595;
border: 1px solid transparent;
@@ -35,10 +39,10 @@ QPushButton#RendererStatusBarButton:!checked {
}
QPushButton#buttonRefreshDevices {
min-width: 20px;
min-height: 20px;
max-width: 20px;
max-height: 20px;
min-width: 21px;
min-height: 21px;
max-width: 21px;
max-height: 21px;
}
QWidget#bottomPerGameInput,
@@ -71,7 +75,7 @@ QWidget#middleControllerApplet {
QWidget#topPerGameInput QComboBox,
QWidget#middleControllerApplet QComboBox {
width: 123px;
width: 120px;
}
QWidget#connectedControllers {

View File

@@ -99,12 +99,19 @@ QGroupBox::indicator:unchecked:disabled {
}
QRadioButton {
spacing: 5px;
outline: none;
color: #eff0f1;
spacing: 3px;
padding: 0px;
border: none;
outline: none;
margin-bottom: 2px;
}
QGroupBox QRadioButton {
padding-left: 0px;
padding-right: 7px;
}
QRadioButton:disabled {
color: #76797C;
}
@@ -522,13 +529,12 @@ QToolButton#qt_toolbar_ext_button {
QPushButton {
color: #eff0f1;
border-width: 1px;
border-color: #54575B;
border-style: solid;
padding: 6px 4px;
border: 1px solid #54575B;
border-radius: 2px;
padding: 5px 0px 5px 0px;
outline: none;
min-width: 100px;
min-height: 13px;
background-color: #232629;
}
@@ -553,8 +559,9 @@ QComboBox {
selection-background-color: #3daee9;
border: 1px solid #54575B;
border-radius: 2px;
padding: 4px 6px;
min-width: 75px;
padding: 0px 4px 0px 4px;
min-width: 60px;
min-height: 23px;
background-color: #232629;
}
@@ -608,26 +615,26 @@ QComboBox::down-arrow:focus {
}
QAbstractSpinBox {
padding: 4px 6px;
border: 1px solid #54575B;
background-color: #232629;
color: #eff0f1;
border-radius: 2px;
min-width: 75px;
min-width: 52px;
min-height: 23px;
}
QAbstractSpinBox:up-button {
background-color: transparent;
subcontrol-origin: border;
subcontrol-position: center right;
left: -6px;
left: -2px;
}
QAbstractSpinBox:down-button {
background-color: transparent;
subcontrol-origin: border;
subcontrol-position: center left;
right: -6px;
right: -2px;
}
QAbstractSpinBox::up-arrow,
@@ -1277,41 +1284,33 @@ QPushButton#RendererStatusBarButton:!checked {
}
QPushButton#buttonRefreshDevices {
min-width: 24px;
min-height: 24px;
max-width: 24px;
max-height: 24px;
min-width: 23px;
min-height: 23px;
max-width: 23px;
max-height: 23px;
padding: 0px 0px;
}
QSpinBox#spinboxLStickRange,
QSpinBox#spinboxRStickRange {
padding: 4px 0px 5px 0px;
min-width: 63px;
}
QSpinBox#vibrationSpin {
padding: 4px 0px 5px 0px;
min-width: 63px;
}
QSpinBox#spinboxLStickRange:up-button,
QSpinBox#spinboxRStickRange:up-button,
QSpinBox#vibrationSpin:up-button {
left: -2px;
}
QSpinBox#spinboxLStickRange:down-button,
QSpinBox#spinboxRStickRange:down-button,
QSpinBox#vibrationSpin:down-button {
right: -1px;
QSpinBox#spinboxRStickRange,
QSpinBox#vibrationSpinPlayer1,
QSpinBox#vibrationSpinPlayer2,
QSpinBox#vibrationSpinPlayer3,
QSpinBox#vibrationSpinPlayer4,
QSpinBox#vibrationSpinPlayer5,
QSpinBox#vibrationSpinPlayer6,
QSpinBox#vibrationSpinPlayer7,
QSpinBox#vibrationSpinPlayer8 {
min-width: 68px;
}
QDialog#ConfigureVibration QGroupBox::indicator,
QGroupBox#motionGroup::indicator,
QGroupBox#vibrationGroup::indicator {
margin-left: 0px;
}
QDialog#ConfigureVibration QGroupBox::title,
QGroupBox#motionGroup::title,
QGroupBox#vibrationGroup::title {
spacing: 2px;
@@ -1340,16 +1339,7 @@ QWidget#middleControllerApplet {
QWidget#topPerGameInput QComboBox,
QWidget#middleControllerApplet QComboBox {
width: 119px;
}
QRadioButton#radioDocked {
margin-left: -3px;
}
QRadioButton#radioUndocked {
margin-right: 5px;
width: 120px;
}
QWidget#connectedControllers {

View File

@@ -172,8 +172,8 @@ QCheckBox {
color: #F0F0F0;
spacing: 4px;
outline: none;
padding-top: 4px;
padding-bottom: 4px;
padding-top: 2px;
padding-bottom: 2px;
}
QCheckBox:focus {
@@ -239,7 +239,7 @@ QGroupBox {
border: 1px solid #32414B;
border-radius: 4px;
margin-top: 12px;
padding: 4px;
padding: 2px;
}
QGroupBox::title {
@@ -247,7 +247,7 @@ QGroupBox::title {
subcontrol-position: top left;
padding-left: 3px;
padding-right: 5px;
padding-top: 4px;
padding-top: 2px;
}
QGroupBox::indicator {
@@ -298,6 +298,11 @@ QRadioButton {
outline: none;
}
QGroupBox QRadioButton {
padding-left: 0px;
padding-right: 7px;
}
QRadioButton:focus {
border: none;
}
@@ -321,7 +326,6 @@ QRadioButton QWidget {
QRadioButton::indicator {
border: none;
outline: none;
margin-left: 4px;
height: 16px;
width: 16px;
}
@@ -785,14 +789,8 @@ QAbstractSpinBox {
background-color: #19232D;
border: 1px solid #32414B;
color: #F0F0F0;
/* This fixes 103, 111 */
padding-top: 2px;
/* This fixes 103, 111 */
padding-bottom: 2px;
padding-left: 4px;
padding-right: 4px;
border-radius: 4px;
/* min-width: 5px; removed to fix 109 */
min-height: 19px;
}
QAbstractSpinBox:up-button {
@@ -997,10 +995,11 @@ QPushButton {
border: 1px solid #32414B;
color: #F0F0F0;
border-radius: 4px;
padding: 3px;
padding: 3px 0px 3px 0px;
outline: none;
/* Issue #194 - Special case of QPushButton inside dialogs, for better UI */
min-width: 80px;
min-height: 13px;
}
QPushButton:disabled {
@@ -1008,14 +1007,14 @@ QPushButton:disabled {
border: 1px solid #32414B;
color: #787878;
border-radius: 4px;
padding: 3px;
padding: 3px 0px 3px 0px;
}
QPushButton:checked {
background-color: #32414B;
border: 1px solid #32414B;
border-radius: 4px;
padding: 3px;
padding: 3px 0px 3px 0px;
outline: none;
}
@@ -1024,7 +1023,7 @@ QPushButton:checked:disabled {
border: 1px solid #32414B;
color: #787878;
border-radius: 4px;
padding: 3px;
padding: 3px 0px 3px 0px;
outline: none;
}
@@ -1197,15 +1196,9 @@ QComboBox {
border: 1px solid #32414B;
border-radius: 4px;
selection-background-color: #1464A0;
padding-left: 4px;
padding-right: 36px;
/* 4 + 16*2 See scrollbar size */
/* Fixes #103, #111 */
min-height: 1.5em;
/* padding-top: 2px; removed to fix #132 */
/* padding-bottom: 2px; removed to fix #132 */
/* min-width: 75px; removed to fix #109 */
/* Needed to remove indicator - fix #132 */
padding: 0px 4px 0px 4px;
min-width: 60px;
min-height: 19px;
}
QComboBox QAbstractItemView {
@@ -2198,29 +2191,40 @@ QPushButton#RendererStatusBarButton:!checked {
}
QPushButton#buttonRefreshDevices {
min-width: 20px;
min-height: 20px;
max-width: 20px;
max-height: 20px;
min-width: 19px;
min-height: 19px;
max-width: 19px;
max-height: 19px;
padding: 0px 0px;
}
QSpinBox#spinboxLStickRange,
QSpinBox#spinboxRStickRange {
min-width: 38px;
QSpinBox#spinboxRStickRange,
QSpinBox#vibrationSpinPlayer1,
QSpinBox#vibrationSpinPlayer2,
QSpinBox#vibrationSpinPlayer3,
QSpinBox#vibrationSpinPlayer4,
QSpinBox#vibrationSpinPlayer5,
QSpinBox#vibrationSpinPlayer6,
QSpinBox#vibrationSpinPlayer7,
QSpinBox#vibrationSpinPlayer8 {
min-width: 68px;
}
QDialog#ConfigureVibration QGroupBox::indicator,
QGroupBox#motionGroup::indicator,
QGroupBox#vibrationGroup::indicator {
margin-left: 0px;
}
QDialog#ConfigureVibration QGroupBox,
QWidget#bottomPerGameInput QGroupBox#motionGroup,
QWidget#bottomPerGameInput QGroupBox#vibrationGroup,
QWidget#bottomPerGameInput QGroupBox#inputConfigGroup {
padding: 0px;
}
QDialog#ConfigureVibration QGroupBox::title,
QGroupBox#motionGroup::title,
QGroupBox#vibrationGroup::title {
spacing: 2px;
@@ -2260,26 +2264,7 @@ QWidget#middleControllerApplet {
QWidget#topPerGameInput QComboBox,
QWidget#middleControllerApplet QComboBox {
padding-right: 2px;
width: 127px;
}
QGroupBox#handheldGroup {
padding-left: 0px;
}
QRadioButton#radioDocked {
margin-left: -1px;
padding-left: 0px;
}
QRadioButton#radioDocked::indicator {
margin-left: 0px;
}
QRadioButton#radioUndocked {
margin-right: 2px;
width: 120px;
}
QWidget#connectedControllers {
@@ -2352,7 +2337,7 @@ QCheckBox#checkboxPlayer5Connected,
QCheckBox#checkboxPlayer6Connected,
QCheckBox#checkboxPlayer7Connected,
QCheckBox#checkboxPlayer8Connected {
spacing: 0px;
spacing: 0px;
}
QWidget#connectedControllers QLabel {
@@ -2427,7 +2412,7 @@ QCheckBox#checkboxPlayer7Connected::indicator,
QCheckBox#checkboxPlayer8Connected::indicator {
width: 14px;
height: 14px;
margin-left: 2px;
margin-left: 0px;
}
QWidget#Player1LEDs QCheckBox::indicator:checked,

View File

@@ -102,6 +102,7 @@ add_library(common STATIC
atomic_ops.h
detached_tasks.cpp
detached_tasks.h
bit_cast.h
bit_field.h
bit_util.h
cityhash.cpp

22
src/common/bit_cast.h Normal file
View File

@@ -0,0 +1,22 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstring>
#include <type_traits>
namespace Common {
template <typename To, typename From>
[[nodiscard]] std::enable_if_t<sizeof(To) == sizeof(From) && std::is_trivially_copyable_v<From> &&
std::is_trivially_copyable_v<To>,
To>
BitCast(const From& src) noexcept {
To dst;
std::memcpy(&dst, &src, sizeof(To));
return dst;
}
} // namespace Common

View File

@@ -23,6 +23,7 @@
#include "common/logging/text_formatter.h"
#include "common/string_util.h"
#include "common/threadsafe_queue.h"
#include "core/settings.h"
namespace Log {
@@ -152,10 +153,19 @@ FileBackend::FileBackend(const std::string& filename)
void FileBackend::Write(const Entry& entry) {
// prevent logs from going over the maximum size (in case its spamming and the user doesn't
// know)
constexpr std::size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L;
if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
constexpr std::size_t MAX_BYTES_WRITTEN = 100 * 1024 * 1024;
constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1024 * 1024 * 1024;
if (!file.IsOpen()) {
return;
}
if (Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN_EXTENDED) {
return;
} else if (!Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN) {
return;
}
bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
if (entry.log_level >= Level::Error) {
file.Flush();
@@ -222,6 +232,7 @@ void DebuggerBackend::Write(const Entry& entry) {
SUB(Service, NPNS) \
SUB(Service, NS) \
SUB(Service, NVDRV) \
SUB(Service, OLSC) \
SUB(Service, PCIE) \
SUB(Service, PCTL) \
SUB(Service, PCV) \

View File

@@ -95,6 +95,7 @@ enum class Class : ClassType {
Service_NPNS, ///< The NPNS service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
Service_OLSC, ///< The OLSC service
Service_PCIE, ///< The PCIe service
Service_PCTL, ///< The PCTL (Parental control) service
Service_PCV, ///< The PCV service

View File

@@ -8,7 +8,7 @@ namespace Common {
PageTable::PageTable() = default;
PageTable::~PageTable() = default;
PageTable::~PageTable() noexcept = default;
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
bool has_attribute) {

View File

@@ -4,9 +4,7 @@
#pragma once
#include <vector>
#include <boost/icl/interval_map.hpp>
#include <tuple>
#include "common/common_types.h"
#include "common/memory_hook.h"
@@ -51,13 +49,21 @@ struct SpecialRegion {
*/
struct PageTable {
PageTable();
~PageTable();
~PageTable() noexcept;
PageTable(const PageTable&) = delete;
PageTable& operator=(const PageTable&) = delete;
PageTable(PageTable&&) noexcept = default;
PageTable& operator=(PageTable&&) noexcept = default;
/**
* Resizes the page table to be able to accomodate enough pages within
* a given address space.
*
* @param address_space_width_in_bits The address size width in bits.
* @param page_size_in_bits The page size in bits.
* @param has_attribute Whether or not this page has any backing attributes.
*/
void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
bool has_attribute);

View File

@@ -13,7 +13,7 @@
namespace Common {
void* AllocateMemoryPages(std::size_t size) {
void* AllocateMemoryPages(std::size_t size) noexcept {
#ifdef _WIN32
void* base{VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE)};
#else
@@ -29,7 +29,7 @@ void* AllocateMemoryPages(std::size_t size) {
return base;
}
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) {
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
if (!base) {
return;
}

View File

@@ -4,29 +4,53 @@
#pragma once
#include "common/common_funcs.h"
#include <type_traits>
#include <utility>
namespace Common {
void* AllocateMemoryPages(std::size_t size);
void FreeMemoryPages(void* base, std::size_t size);
void* AllocateMemoryPages(std::size_t size) noexcept;
void FreeMemoryPages(void* base, std::size_t size) noexcept;
template <typename T>
class VirtualBuffer final : NonCopyable {
class VirtualBuffer final {
public:
static_assert(
std::is_trivially_constructible_v<T>,
"T must be trivially constructible, as non-trivial constructors will not be executed "
"with the current allocator");
constexpr VirtualBuffer() = default;
explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} {
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
}
~VirtualBuffer() {
~VirtualBuffer() noexcept {
FreeMemoryPages(base_ptr, alloc_size);
}
VirtualBuffer(const VirtualBuffer&) = delete;
VirtualBuffer& operator=(const VirtualBuffer&) = delete;
VirtualBuffer(VirtualBuffer&& other) noexcept
: alloc_size{std::exchange(other.alloc_size, 0)}, base_ptr{std::exchange(other.base_ptr),
nullptr} {}
VirtualBuffer& operator=(VirtualBuffer&& other) noexcept {
alloc_size = std::exchange(other.alloc_size, 0);
base_ptr = std::exchange(other.base_ptr, nullptr);
return *this;
}
void resize(std::size_t count) {
const auto new_size = count * sizeof(T);
if (new_size == alloc_size) {
return;
}
FreeMemoryPages(base_ptr, alloc_size);
alloc_size = count * sizeof(T);
alloc_size = new_size;
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
}

View File

@@ -458,6 +458,8 @@ add_library(core STATIC
hle/service/nvflinger/buffer_queue.h
hle/service/nvflinger/nvflinger.cpp
hle/service/nvflinger/nvflinger.h
hle/service/olsc/olsc.cpp
hle/service/olsc/olsc.h
hle/service/pcie/pcie.cpp
hle/service/pcie/pcie.h
hle/service/pctl/module.cpp

View File

@@ -210,7 +210,7 @@ struct System::Impl {
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
const std::string& filepath) {
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath));
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
return ResultStatus::ErrorGetLoader;
@@ -224,7 +224,7 @@ struct System::Impl {
return init_result;
}
telemetry_session->AddInitialInfo(*app_loader);
telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
auto main_process =
Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland);
const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
@@ -338,7 +338,7 @@ struct System::Impl {
Service::Glue::ApplicationLaunchProperty launch{};
launch.title_id = process.GetTitleID();
FileSys::PatchManager pm{launch.title_id};
FileSys::PatchManager pm{launch.title_id, fs_controller, *content_provider};
launch.version = pm.GetGameVersion().value_or(0);
// TODO(DarkLordZach): When FSController/Game Card Support is added, if

View File

@@ -112,7 +112,10 @@ bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
}
} // Anonymous namespace
PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
PatchManager::PatchManager(u64 title_id_,
const Service::FileSystem::FileSystemController& fs_controller_,
const ContentProvider& content_provider_)
: title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
PatchManager::~PatchManager() = default;
@@ -128,34 +131,30 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
if (Settings::values.dump_exefs) {
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
const auto dump_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
VfsRawCopyD(exefs, exefs_dir);
}
}
const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto update_disabled =
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
const auto update = installed.GetEntry(update_tid, ContentRecordType::Program);
const auto update = content_provider.GetEntry(update_tid, ContentRecordType::Program);
if (!update_disabled && update != nullptr && update->GetExeFS() != nullptr &&
update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0)));
FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0)));
exefs = update->GetExeFS();
}
// LayeredExeFS
const auto load_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir != nullptr && load_dir->GetSize() > 0) {
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(
@@ -241,8 +240,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
if (Settings::values.dump_nso) {
LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id,
title_id);
const auto dump_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id));
@@ -254,8 +252,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
const auto load_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return nso;
@@ -298,8 +295,7 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_) const {
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
const auto load_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return false;
@@ -313,8 +309,8 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_) const {
}
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
const Core::System& system, const BuildID& build_id_) const {
const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id);
const BuildID& build_id_) const {
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return {};
@@ -347,9 +343,9 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
return out;
}
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
const auto load_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
const Service::FileSystem::FileSystemController& fs_controller) {
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
load_dir == nullptr || load_dir->GetSize() <= 0) {
return;
@@ -411,19 +407,19 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}",
title_id, static_cast<u8>(type));
if (type == ContentRecordType::Program || type == ContentRecordType::Data)
if (type == ContentRecordType::Program || type == ContentRecordType::Data) {
LOG_INFO(Loader, "{}", log_string);
else
} else {
LOG_DEBUG(Loader, "{}", log_string);
}
if (romfs == nullptr)
if (romfs == nullptr) {
return romfs;
const auto& installed = Core::System::GetInstance().GetContentProvider();
}
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
const auto update = installed.GetEntryRaw(update_tid, type);
const auto update = content_provider.GetEntryRaw(update_tid, type);
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto update_disabled =
@@ -434,7 +430,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
new_nca->GetRomFS() != nullptr) {
LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0)));
FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0)));
romfs = new_nca->GetRomFS();
}
} else if (!update_disabled && update_raw != nullptr) {
@@ -447,7 +443,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
}
// LayeredFS
ApplyLayeredFS(romfs, title_id, type);
ApplyLayeredFS(romfs, title_id, type, fs_controller);
return romfs;
}
@@ -458,12 +454,11 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
}
std::map<std::string, std::string, std::less<>> out;
const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto& disabled = Settings::values.disabled_addons[title_id];
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
PatchManager update{update_tid};
PatchManager update{update_tid, fs_controller, content_provider};
const auto metadata = update.GetControlMetadata();
const auto& nacp = metadata.first;
@@ -474,8 +469,8 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
if (nacp != nullptr) {
out.insert_or_assign(update_label, nacp->GetVersionString());
} else {
if (installed.HasEntry(update_tid, ContentRecordType::Program)) {
const auto meta_ver = installed.GetEntryVersion(update_tid);
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
const auto meta_ver = content_provider.GetEntryVersion(update_tid);
if (meta_ver.value_or(0) == 0) {
out.insert_or_assign(update_label, "");
} else {
@@ -487,8 +482,7 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
}
// General Mods (LayeredFS and IPS)
const auto mod_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id);
if (mod_dir != nullptr && mod_dir->GetSize() > 0) {
for (const auto& mod : mod_dir->GetSubdirectories()) {
std::string types;
@@ -532,13 +526,15 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
}
// DLC
const auto dlc_entries = installed.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
const auto dlc_entries =
content_provider.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
std::vector<ContentProviderEntry> dlc_match;
dlc_match.reserve(dlc_entries.size());
std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
[this, &installed](const ContentProviderEntry& entry) {
[this](const ContentProviderEntry& entry) {
return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id &&
installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
content_provider.GetEntry(entry)->GetStatus() ==
Loader::ResultStatus::Success;
});
if (!dlc_match.empty()) {
// Ensure sorted so DLC IDs show in order.
@@ -559,19 +555,16 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
}
std::optional<u32> PatchManager::GetGameVersion() const {
const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto update_tid = GetUpdateTitleID(title_id);
if (installed.HasEntry(update_tid, ContentRecordType::Program)) {
return installed.GetEntryVersion(update_tid);
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
return content_provider.GetEntryVersion(update_tid);
}
return installed.GetEntryVersion(title_id);
return content_provider.GetEntryVersion(title_id);
}
PatchManager::Metadata PatchManager::GetControlMetadata() const {
const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control);
const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
if (base_control_nca == nullptr) {
return {};
}

View File

@@ -17,8 +17,13 @@ namespace Core {
class System;
}
namespace Service::FileSystem {
class FileSystemController;
}
namespace FileSys {
class ContentProvider;
class NCA;
class NACP;
@@ -29,7 +34,9 @@ public:
using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>;
using PatchVersionNames = std::map<std::string, std::string, std::less<>>;
explicit PatchManager(u64 title_id);
explicit PatchManager(u64 title_id_,
const Service::FileSystem::FileSystemController& fs_controller_,
const ContentProvider& content_provider_);
~PatchManager();
[[nodiscard]] u64 GetTitleID() const;
@@ -50,7 +57,7 @@ public:
// Creates a CheatList object with all
[[nodiscard]] std::vector<Core::Memory::CheatEntry> CreateCheatList(
const Core::System& system, const BuildID& build_id) const;
const BuildID& build_id) const;
// Currently tracked RomFS patches:
// - Game Updates
@@ -80,6 +87,8 @@ private:
const std::string& build_id) const;
u64 title_id;
const Service::FileSystem::FileSystemController& fs_controller;
const ContentProvider& content_provider;
};
} // namespace FileSys

View File

@@ -37,10 +37,12 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) {
}
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess(u64 current_process_title_id) const {
if (!updatable)
if (!updatable) {
return MakeResult<VirtualFile>(file);
}
const PatchManager patch_manager(current_process_title_id);
const PatchManager patch_manager{current_process_title_id, filesystem_controller,
content_provider};
return MakeResult<VirtualFile>(
patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw));
}

View File

@@ -27,19 +27,19 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
->GetAppletResource()
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
auto& players = Settings::values.players;
auto& players = Settings::values.players.GetValue();
const std::size_t min_supported_players =
parameters.enable_single_mode ? 1 : parameters.min_players;
// Disconnect Handheld first.
npad.DisconnectNPadAtIndex(8);
npad.DisconnectNpadAtIndex(8);
// Deduce the best configuration based on the input parameters.
for (std::size_t index = 0; index < players.size() - 2; ++index) {
// First, disconnect all controllers regardless of the value of keep_controllers_connected.
// This makes it easy to connect the desired controllers.
npad.DisconnectNPadAtIndex(index);
npad.DisconnectNpadAtIndex(index);
// Only connect the minimum number of required players.
if (index >= min_supported_players) {
@@ -66,7 +66,7 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index);
}
} else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
!Settings::values.use_docked_mode) {
!Settings::values.use_docked_mode.GetValue()) {
// We should *never* reach here under any normal circumstances.
npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld),
index);

View File

@@ -102,8 +102,8 @@ public:
float render_surface_scale = 1.0f;
};
/// Polls window events
virtual void PollEvents() = 0;
/// Called from GPU thread when a frame is displayed.
virtual void OnFrameDisplayed() {}
/**
* Returns a GraphicsContext that the frontend provides to be used for rendering.

View File

@@ -47,7 +47,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
u32 width, height;
if (Settings::values.use_docked_mode) {
if (Settings::values.use_docked_mode.GetValue()) {
width = ScreenDocked::Width * res_scale;
height = ScreenDocked::Height * res_scale;
} else {

View File

@@ -33,7 +33,7 @@ public:
virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const {
return {};
}
virtual bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const {
virtual bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const {
return {};
}
};
@@ -121,6 +121,13 @@ using ButtonDevice = InputDevice<bool>;
*/
using AnalogDevice = InputDevice<std::tuple<float, float>>;
/**
* A vibration device is an input device that returns an unsigned byte as status.
* It represents whether the vibration device supports vibration or not.
* If the status returns 1, it supports vibration. Otherwise, it does not support vibration.
*/
using VibrationDevice = InputDevice<u8>;
/**
* A motion status is an object that returns a tuple of accelerometer state vector,
* gyroscope state vector, rotation state vector and orientation state matrix.

View File

@@ -12,7 +12,6 @@
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/core.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
@@ -73,14 +72,12 @@ public:
AlwaysMoveHandles = 1,
};
explicit ResponseBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {}
explicit ResponseBuilder(Kernel::HLERequestContext& context, u32 normal_params_size,
u32 num_handles_to_copy = 0, u32 num_objects_to_move = 0,
Flags flags = Flags::None)
: RequestHelperBase(context), normal_params_size(normal_params_size),
num_handles_to_copy(num_handles_to_copy), num_objects_to_move(num_objects_to_move) {
num_handles_to_copy(num_handles_to_copy),
num_objects_to_move(num_objects_to_move), kernel{context.kernel} {
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
@@ -140,7 +137,6 @@ public:
if (context->Session()->IsDomain()) {
context->AddDomainObject(std::move(iface));
} else {
auto& kernel = Core::System::GetInstance().Kernel();
auto [client, server] = Kernel::Session::Create(kernel, iface->GetServiceName());
context->AddMoveObject(std::move(client));
iface->ClientConnected(std::move(server));
@@ -214,6 +210,7 @@ private:
u32 num_handles_to_copy{};
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
std::ptrdiff_t datapayload_index{};
Kernel::KernelCore& kernel;
};
/// Push ///

View File

@@ -24,6 +24,10 @@ namespace Core::Memory {
class Memory;
}
namespace IPC {
class ResponseBuilder;
}
namespace Service {
class ServiceFrameworkBase;
}
@@ -287,6 +291,8 @@ public:
}
private:
friend class IPC::ResponseBuilder;
void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;

View File

@@ -11,6 +11,7 @@
#include "common/string_util.h"
#include "common/swap.h"
#include "core/constants.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
@@ -741,8 +742,10 @@ void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx
bool is_locked = false;
if (res != Loader::ResultStatus::Success) {
FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
auto nacp_unique = pm.GetControlMetadata().first;
const FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID(),
system.GetFileSystemController(),
system.GetContentProvider()};
const auto nacp_unique = pm.GetControlMetadata().first;
if (nacp_unique != nullptr) {
is_locked = nacp_unique->GetUserAccountSwitchLock();

View File

@@ -751,7 +751,7 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
if (Settings::values.use_docked_mode) {
if (Settings::values.use_docked_mode.GetValue()) {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
@@ -824,7 +824,7 @@ void IStorage::Open(Kernel::HLERequestContext& ctx) {
}
void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
const bool use_docked_mode{Settings::values.use_docked_mode};
const bool use_docked_mode{Settings::values.use_docked_mode.GetValue()};
LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode);
IPC::ResponseBuilder rb{ctx, 3};
@@ -1381,13 +1381,16 @@ void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) {
const auto res = [this] {
const auto title_id = system.CurrentProcess()->GetTitleID();
FileSys::PatchManager pm{title_id};
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
system.GetContentProvider()};
auto res = pm.GetControlMetadata();
if (res.first != nullptr) {
return res;
}
FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)};
const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id),
system.GetFileSystemController(),
system.GetContentProvider()};
return pm_update.GetControlMetadata();
}();
@@ -1415,13 +1418,16 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
const auto res = [this] {
const auto title_id = system.CurrentProcess()->GetTitleID();
FileSys::PatchManager pm{title_id};
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
system.GetContentProvider()};
auto res = pm.GetControlMetadata();
if (res.first != nullptr) {
return res;
}
FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)};
const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id),
system.GetFileSystemController(),
system.GetContentProvider()};
return pm_update.GetControlMetadata();
}();

View File

@@ -3,8 +3,8 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/nvflinger/nvflinger.h"

View File

@@ -25,7 +25,7 @@ namespace Service::AM::Applets {
static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
HID::Controller_NPad::NPadType npad_style_set;
HID::Controller_NPad::NpadStyleSet npad_style_set;
npad_style_set.raw = private_arg.style_set;
return {
@@ -62,7 +62,7 @@ void Controller::Initialize() {
common_args.play_startup_sound, common_args.size, common_args.system_tick,
common_args.theme_color);
library_applet_version = LibraryAppletVersion{common_args.library_version};
controller_applet_version = ControllerAppletVersion{common_args.library_version};
const auto private_arg_storage = broker.PopNormalDataToApplet();
ASSERT(private_arg_storage != nullptr);
@@ -70,39 +70,78 @@ void Controller::Initialize() {
const auto& private_arg = private_arg_storage->GetData();
ASSERT(private_arg.size() == sizeof(ControllerSupportArgPrivate));
std::memcpy(&controller_private_arg, private_arg.data(), sizeof(ControllerSupportArgPrivate));
std::memcpy(&controller_private_arg, private_arg.data(), private_arg.size());
ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate),
"Unknown ControllerSupportArgPrivate revision={} with size={}",
library_applet_version, controller_private_arg.arg_private_size);
controller_applet_version, controller_private_arg.arg_private_size);
// Some games such as Cave Story+ set invalid values for the ControllerSupportMode.
// Defer to arg_size to set the ControllerSupportMode.
if (controller_private_arg.mode >= ControllerSupportMode::MaxControllerSupportMode) {
switch (controller_private_arg.arg_size) {
case sizeof(ControllerSupportArgOld):
case sizeof(ControllerSupportArgNew):
controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport;
break;
case sizeof(ControllerUpdateFirmwareArg):
controller_private_arg.mode = ControllerSupportMode::ShowControllerFirmwareUpdate;
break;
default:
UNIMPLEMENTED_MSG("Unknown ControllerPrivateArg mode={} with arg_size={}",
controller_private_arg.mode, controller_private_arg.arg_size);
controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport;
break;
}
}
// Some games such as Cave Story+ set invalid values for the ControllerSupportCaller.
// This is always 0 (Application) except with ShowControllerFirmwareUpdateForSystem.
if (controller_private_arg.caller >= ControllerSupportCaller::MaxControllerSupportCaller) {
if (controller_private_arg.flag_1 &&
controller_private_arg.mode == ControllerSupportMode::ShowControllerFirmwareUpdate) {
controller_private_arg.caller = ControllerSupportCaller::System;
} else {
controller_private_arg.caller = ControllerSupportCaller::Application;
}
}
switch (controller_private_arg.mode) {
case ControllerSupportMode::ShowControllerSupport: {
case ControllerSupportMode::ShowControllerSupport:
case ControllerSupportMode::ShowControllerStrapGuide: {
const auto user_arg_storage = broker.PopNormalDataToApplet();
ASSERT(user_arg_storage != nullptr);
const auto& user_arg = user_arg_storage->GetData();
switch (library_applet_version) {
case LibraryAppletVersion::Version3:
case LibraryAppletVersion::Version4:
case LibraryAppletVersion::Version5:
switch (controller_applet_version) {
case ControllerAppletVersion::Version3:
case ControllerAppletVersion::Version4:
case ControllerAppletVersion::Version5:
ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld));
std::memcpy(&controller_user_arg_old, user_arg.data(), sizeof(ControllerSupportArgOld));
std::memcpy(&controller_user_arg_old, user_arg.data(), user_arg.size());
break;
case LibraryAppletVersion::Version7:
case ControllerAppletVersion::Version7:
ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew));
std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew));
std::memcpy(&controller_user_arg_new, user_arg.data(), user_arg.size());
break;
default:
UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}",
library_applet_version, controller_private_arg.arg_size);
controller_applet_version, controller_private_arg.arg_size);
ASSERT(user_arg.size() >= sizeof(ControllerSupportArgNew));
std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew));
break;
}
break;
}
case ControllerSupportMode::ShowControllerStrapGuide:
case ControllerSupportMode::ShowControllerFirmwareUpdate:
case ControllerSupportMode::ShowControllerFirmwareUpdate: {
const auto update_arg_storage = broker.PopNormalDataToApplet();
ASSERT(update_arg_storage != nullptr);
const auto& update_arg = update_arg_storage->GetData();
ASSERT(update_arg.size() == sizeof(ControllerUpdateFirmwareArg));
std::memcpy(&controller_update_arg, update_arg.data(), update_arg.size());
break;
}
default: {
UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode);
break;
@@ -126,10 +165,10 @@ void Controller::Execute() {
switch (controller_private_arg.mode) {
case ControllerSupportMode::ShowControllerSupport: {
const auto parameters = [this] {
switch (library_applet_version) {
case LibraryAppletVersion::Version3:
case LibraryAppletVersion::Version4:
case LibraryAppletVersion::Version5:
switch (controller_applet_version) {
case ControllerAppletVersion::Version3:
case ControllerAppletVersion::Version4:
case ControllerAppletVersion::Version5:
return ConvertToFrontendParameters(
controller_private_arg, controller_user_arg_old.header,
controller_user_arg_old.enable_explain_text,
@@ -138,7 +177,7 @@ void Controller::Execute() {
controller_user_arg_old.identification_colors.end()),
std::vector<ExplainText>(controller_user_arg_old.explain_text.begin(),
controller_user_arg_old.explain_text.end()));
case LibraryAppletVersion::Version7:
case ControllerAppletVersion::Version7:
default:
return ConvertToFrontendParameters(
controller_private_arg, controller_user_arg_new.header,
@@ -170,6 +209,9 @@ void Controller::Execute() {
}
case ControllerSupportMode::ShowControllerStrapGuide:
case ControllerSupportMode::ShowControllerFirmwareUpdate:
UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented",
controller_private_arg.mode);
[[fallthrough]];
default: {
ConfigurationComplete();
break;
@@ -180,7 +222,7 @@ void Controller::Execute() {
void Controller::ConfigurationComplete() {
ControllerSupportResultInfo result_info{};
const auto& players = Settings::values.players;
const auto& players = Settings::values.players.GetValue();
// If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
// Otherwise, only count connected players from P1-P8.

View File

@@ -21,7 +21,7 @@ namespace Service::AM::Applets {
using IdentificationColor = std::array<u8, 4>;
using ExplainText = std::array<char, 0x81>;
enum class LibraryAppletVersion : u32_le {
enum class ControllerAppletVersion : u32_le {
Version3 = 0x3, // 1.0.0 - 2.3.0
Version4 = 0x4, // 3.0.0 - 5.1.0
Version5 = 0x5, // 6.0.0 - 7.0.1
@@ -29,14 +29,18 @@ enum class LibraryAppletVersion : u32_le {
};
enum class ControllerSupportMode : u8 {
ShowControllerSupport = 0,
ShowControllerStrapGuide = 1,
ShowControllerFirmwareUpdate = 2,
ShowControllerSupport,
ShowControllerStrapGuide,
ShowControllerFirmwareUpdate,
MaxControllerSupportMode,
};
enum class ControllerSupportCaller : u8 {
Application = 0,
System = 1,
Application,
System,
MaxControllerSupportCaller,
};
struct ControllerSupportArgPrivate {
@@ -84,6 +88,13 @@ struct ControllerSupportArgNew {
static_assert(sizeof(ControllerSupportArgNew) == 0x430,
"ControllerSupportArgNew has incorrect size.");
struct ControllerUpdateFirmwareArg {
bool enable_force_update{};
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(ControllerUpdateFirmwareArg) == 0x4,
"ControllerUpdateFirmwareArg has incorrect size.");
struct ControllerSupportResultInfo {
s8 player_count{};
INSERT_PADDING_BYTES(3);
@@ -110,10 +121,11 @@ public:
private:
const Core::Frontend::ControllerApplet& frontend;
LibraryAppletVersion library_applet_version;
ControllerAppletVersion controller_applet_version;
ControllerSupportArgPrivate controller_private_arg;
ControllerSupportArgOld controller_user_arg_old;
ControllerSupportArgNew controller_user_arg_new;
ControllerUpdateFirmwareArg controller_update_arg;
bool complete{false};
ResultCode status{RESULT_SUCCESS};
bool is_single_mode{false};

View File

@@ -6,6 +6,7 @@
#include <numeric>
#include <vector>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/nca_metadata.h"
@@ -163,7 +164,8 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
const auto title_id = system.CurrentProcess()->GetTitleID();
FileSys::PatchManager pm{title_id};
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
system.GetContentProvider()};
const auto res = pm.GetControlMetadata();
if (res.first == nullptr) {

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/apm/interface.h"

View File

@@ -69,7 +69,8 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
}
PerformanceMode Controller::GetCurrentPerformanceMode() const {
return Settings::values.use_docked_mode ? PerformanceMode::Docked : PerformanceMode::Handheld;
return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked
: PerformanceMode::Handheld;
}
PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) {

View File

@@ -8,6 +8,7 @@
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/vfs.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"

View File

@@ -5,6 +5,7 @@
#include <memory>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"

View File

@@ -41,7 +41,7 @@ CAPS_U::CAPS_U() : ServiceFramework("caps:u") {
{130, nullptr, "PrecheckToCreateContentsForApplication"},
{140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"},
{141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"},
{142, nullptr, "GetAlbumFileList3AaeAruid"},
{142, &CAPS_U::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"},
{143, nullptr, "GetAlbumFileList4AaeUidAruid"},
{60002, nullptr, "OpenAccessorSessionForApplication"},
};
@@ -77,17 +77,24 @@ void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& c
// TODO: Update this when we implement the album.
// Currently we do not have a method of accessing album entries, set this to 0 for now.
constexpr s32 total_entries{0};
constexpr u32 total_entries_1{};
constexpr u32 total_entries_2{};
LOG_WARNING(Service_Capture,
"(STUBBED) called. pid={}, content_type={}, start_posix_time={}, "
"end_posix_time={}, applet_resource_user_id={}, total_entries={}",
pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id,
total_entries);
LOG_WARNING(
Service_Capture,
"(STUBBED) called. pid={}, content_type={}, start_posix_time={}, "
"end_posix_time={}, applet_resource_user_id={}, total_entries_1={}, total_entries_2={}",
pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id,
total_entries_1, total_entries_2);
IPC::ResponseBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(total_entries);
rb.Push(total_entries_1);
rb.Push(total_entries_2);
}
void CAPS_U::GetAlbumFileList3AaeAruid(Kernel::HLERequestContext& ctx) {
GetAlbumContentsFileListForApplication(ctx);
}
} // namespace Service::Capture

View File

@@ -20,6 +20,7 @@ public:
private:
void SetShimLibraryVersion(Kernel::HLERequestContext& ctx);
void GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx);
void GetAlbumFileList3AaeAruid(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Capture

View File

@@ -455,7 +455,9 @@ FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataTy
const auto res = system.GetAppLoader().ReadControlData(nacp);
if (res != Loader::ResultStatus::Success) {
FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
const FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID(),
system.GetFileSystemController(),
system.GetContentProvider()};
const auto metadata = pm.GetControlMetadata();
const auto& nacp_unique = metadata.first;
@@ -728,7 +730,8 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
void InstallInterfaces(Core::System& system) {
std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager());
std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager());
std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetReporter())
std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetContentProvider(),
system.GetReporter())
->InstallAsService(system.ServiceManager());
}

View File

@@ -650,8 +650,10 @@ private:
u64 next_entry_index = 0;
};
FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
: ServiceFramework("fsp-srv"), fsc(fsc), reporter(reporter) {
FSP_SRV::FSP_SRV(FileSystemController& fsc_, const FileSys::ContentProvider& content_provider_,
const Core::Reporter& reporter_)
: ServiceFramework("fsp-srv"), fsc(fsc_), content_provider{content_provider_},
reporter(reporter_) {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "OpenFileSystem"},
@@ -968,7 +970,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
return;
}
FileSys::PatchManager pm{title_id};
const FileSys::PatchManager pm{title_id, fsc, content_provider};
auto storage = std::make_shared<IStorage>(
pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data));

View File

@@ -12,8 +12,9 @@ class Reporter;
}
namespace FileSys {
class ContentProvider;
class FileSystemBackend;
}
} // namespace FileSys
namespace Service::FileSystem {
@@ -32,7 +33,8 @@ enum class LogMode : u32 {
class FSP_SRV final : public ServiceFramework<FSP_SRV> {
public:
explicit FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter);
explicit FSP_SRV(FileSystemController& fsc_, const FileSys::ContentProvider& content_provider_,
const Core::Reporter& reporter_);
~FSP_SRV() override;
private:
@@ -55,6 +57,7 @@ private:
void OpenMultiCommitManager(Kernel::HLERequestContext& ctx);
FileSystemController& fsc;
const FileSys::ContentProvider& content_provider;
FileSys::VirtualFile romfs;
u64 current_process_id = 0;

View File

@@ -5,6 +5,7 @@
#include <queue>
#include "common/logging/log.h"
#include "common/uuid.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/writable_event.h"

View File

@@ -5,6 +5,7 @@
#include <memory>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"

View File

@@ -117,7 +117,10 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
}
Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
Controller_NPad::~Controller_NPad() = default;
Controller_NPad::~Controller_NPad() {
OnRelease();
}
void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
const auto controller_type = connected_controllers[controller_idx].type;
@@ -139,7 +142,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
controller.properties.is_vertical.Assign(1);
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
controller.pad_assignment = NpadAssignments::Single;
break;
case NPadControllerType::Handheld:
controller.joy_styles.handheld.Assign(1);
@@ -147,7 +150,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
controller.properties.is_vertical.Assign(1);
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
controller.pad_assignment = NpadAssignments::Dual;
break;
case NPadControllerType::JoyDual:
controller.joy_styles.joycon_dual.Assign(1);
@@ -156,26 +159,26 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
controller.properties.is_vertical.Assign(1);
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
controller.pad_assignment = NpadAssignments::Dual;
break;
case NPadControllerType::JoyLeft:
controller.joy_styles.joycon_left.Assign(1);
controller.device_type.joycon_left.Assign(1);
controller.properties.is_horizontal.Assign(1);
controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
controller.pad_assignment = NpadAssignments::Single;
break;
case NPadControllerType::JoyRight:
controller.joy_styles.joycon_right.Assign(1);
controller.device_type.joycon_right.Assign(1);
controller.properties.is_horizontal.Assign(1);
controller.properties.use_plus.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
controller.pad_assignment = NpadAssignments::Single;
break;
case NPadControllerType::Pokeball:
controller.joy_styles.pokeball.Assign(1);
controller.device_type.pokeball.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
controller.pad_assignment = NpadAssignments::Single;
break;
}
@@ -184,11 +187,14 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
controller.single_color.button_color = 0;
controller.dual_color_error = ColorReadError::ReadOk;
controller.left_color.body_color = Settings::values.players[controller_idx].body_color_left;
controller.left_color.button_color = Settings::values.players[controller_idx].button_color_left;
controller.right_color.body_color = Settings::values.players[controller_idx].body_color_right;
controller.left_color.body_color =
Settings::values.players.GetValue()[controller_idx].body_color_left;
controller.left_color.button_color =
Settings::values.players.GetValue()[controller_idx].button_color_left;
controller.right_color.body_color =
Settings::values.players.GetValue()[controller_idx].body_color_right;
controller.right_color.button_color =
Settings::values.players[controller_idx].button_color_right;
Settings::values.players.GetValue()[controller_idx].button_color_right;
controller.battery_level[0] = BATTERY_FULL;
controller.battery_level[1] = BATTERY_FULL;
@@ -199,7 +205,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
void Controller_NPad::OnInit() {
auto& kernel = system.Kernel();
for (std::size_t i = 0; i < styleset_changed_events.size(); i++) {
for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair(
kernel, fmt::format("npad:NpadStyleSetChanged_{}", i));
}
@@ -208,6 +214,8 @@ void Controller_NPad::OnInit() {
return;
}
OnLoadInputDevices();
if (style.raw == 0) {
// We want to support all controllers
style.handheld.Assign(1);
@@ -218,12 +226,27 @@ void Controller_NPad::OnInit() {
style.pokeball.Assign(1);
}
std::transform(Settings::values.players.begin(), Settings::values.players.end(),
connected_controllers.begin(), [](const Settings::PlayerInput& player) {
std::transform(Settings::values.players.GetValue().begin(),
Settings::values.players.GetValue().end(), connected_controllers.begin(),
[](const Settings::PlayerInput& player) {
return ControllerHolder{MapSettingsTypeToNPad(player.controller_type),
player.connected};
});
// Connect the Player 1 or Handheld controller if none are connected.
if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
[](const ControllerHolder& controller) { return controller.is_connected; })) {
const auto controller =
MapSettingsTypeToNPad(Settings::values.players.GetValue()[0].controller_type);
if (controller == NPadControllerType::Handheld) {
Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
connected_controllers[HANDHELD_INDEX] = {controller, true};
} else {
Settings::values.players.GetValue()[0].connected = true;
connected_controllers[0] = {controller, true};
}
}
// Account for handheld
if (connected_controllers[HANDHELD_INDEX].is_connected) {
connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld;
@@ -242,7 +265,7 @@ void Controller_NPad::OnInit() {
}
void Controller_NPad::OnLoadInputDevices() {
const auto& players = Settings::values.players;
const auto& players = Settings::values.players.GetValue();
for (std::size_t i = 0; i < players.size(); ++i) {
std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
@@ -250,13 +273,26 @@ void Controller_NPad::OnLoadInputDevices() {
std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
std::transform(players[i].vibrations.begin() +
Settings::NativeVibration::VIBRATION_HID_BEGIN,
players[i].vibrations.begin() + Settings::NativeVibration::VIBRATION_HID_END,
vibrations[i].begin(), Input::CreateDevice<Input::VibrationDevice>);
std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
for (std::size_t device_idx = 0; device_idx < vibrations[i].size(); ++device_idx) {
InitializeVibrationDeviceAtIndex(i, device_idx);
}
}
}
void Controller_NPad::OnRelease() {}
void Controller_NPad::OnRelease() {
for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) {
for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) {
VibrateControllerAtIndex(npad_idx, device_idx, {});
}
}
}
void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
const auto controller_idx = NPadIdToIndex(npad_id);
@@ -339,7 +375,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
if (!IsControllerActivated()) {
return;
}
for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
auto& npad = shared_memory_entries[i];
const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
&npad.handheld_states,
@@ -481,7 +517,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
if (!IsControllerActivated()) {
return;
}
for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
auto& npad = shared_memory_entries[i];
const auto& controller_type = connected_controllers[i].type;
@@ -515,7 +551,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
// Try to read sixaxis sensor states
std::array<MotionDevice, 2> motion_devices;
if (sixaxis_sensors_enabled && Settings::values.motion_enabled) {
if (sixaxis_sensors_enabled && Settings::values.motion_enabled.GetValue()) {
sixaxis_at_rest = true;
for (std::size_t e = 0; e < motion_devices.size(); ++e) {
const auto& device = motions[i][e];
@@ -601,15 +637,15 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
shared_memory_entries.size() * sizeof(NPadEntry));
}
void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
void Controller_NPad::SetSupportedStyleSet(NpadStyleSet style_set) {
style.raw = style_set.raw;
}
Controller_NPad::NPadType Controller_NPad::GetSupportedStyleSet() const {
Controller_NPad::NpadStyleSet Controller_NPad::GetSupportedStyleSet() const {
return style;
}
void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) {
ASSERT(length > 0 && (length % sizeof(u32)) == 0);
supported_npad_id_types.clear();
supported_npad_id_types.resize(length / sizeof(u32));
@@ -621,7 +657,7 @@ void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length)
std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size());
}
std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
return supported_npad_id_types.size();
}
@@ -641,7 +677,7 @@ Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActi
return handheld_activation_mode;
}
void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) {
const std::size_t npad_index = NPadIdToIndex(npad_id);
ASSERT(npad_index < shared_memory_entries.size());
if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) {
@@ -649,35 +685,140 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode)
}
}
void Controller_NPad::VibrateController(const std::vector<u32>& controllers,
const std::vector<Vibration>& vibrations) {
LOG_TRACE(Service_HID, "called");
bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
const VibrationValue& vibration_value) {
if (!connected_controllers[npad_index].is_connected || !vibrations[npad_index][device_index]) {
return false;
}
if (!Settings::values.vibration_enabled || !can_controllers_vibrate) {
const auto& player = Settings::values.players.GetValue()[npad_index];
if (!player.vibration_enabled) {
if (latest_vibration_values[npad_index][device_index].amp_low != 0.0f ||
latest_vibration_values[npad_index][device_index].amp_high != 0.0f) {
// Send an empty vibration to stop any vibrations.
vibrations[npad_index][device_index]->SetRumblePlay(0.0f, 160.0f, 0.0f, 320.0f);
// Then reset the vibration value to its default value.
latest_vibration_values[npad_index][device_index] = {};
}
return false;
}
if (!Settings::values.enable_accurate_vibrations.GetValue()) {
using std::chrono::duration_cast;
using std::chrono::milliseconds;
using std::chrono::steady_clock;
const auto now = steady_clock::now();
// Filter out non-zero vibrations that are within 10ms of each other.
if ((vibration_value.amp_low != 0.0f || vibration_value.amp_high != 0.0f) &&
duration_cast<milliseconds>(now - last_vibration_timepoints[npad_index][device_index]) <
milliseconds(10)) {
return false;
}
last_vibration_timepoints[npad_index][device_index] = now;
}
auto& vibration = vibrations[npad_index][device_index];
const auto player_vibration_strength = static_cast<f32>(player.vibration_strength);
const auto amp_low =
std::min(vibration_value.amp_low * player_vibration_strength / 100.0f, 1.0f);
const auto amp_high =
std::min(vibration_value.amp_high * player_vibration_strength / 100.0f, 1.0f);
return vibration->SetRumblePlay(amp_low, vibration_value.freq_low, amp_high,
vibration_value.freq_high);
}
void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_handle,
const VibrationValue& vibration_value) {
if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
return;
}
bool success = true;
for (std::size_t i = 0; i < controllers.size(); ++i) {
if (!connected_controllers[i].is_connected) {
continue;
}
using namespace Settings::NativeButton;
const auto& button_state = buttons[i];
if (button_state[A - BUTTON_HID_BEGIN]) {
if (button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay(
vibrations[0].amp_high, vibrations[0].amp_low, vibrations[0].freq_high,
vibrations[0].freq_low)) {
success = false;
}
}
const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
if (!vibration_devices_mounted[npad_index][device_index] ||
!connected_controllers[npad_index].is_connected) {
return;
}
if (success) {
last_processed_vibration = vibrations.back();
if (vibration_device_handle.device_index == DeviceIndex::None) {
UNREACHABLE_MSG("DeviceIndex should never be None!");
return;
}
// Some games try to send mismatched parameters in the device handle, block these.
if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft &&
(vibration_device_handle.npad_type == NpadType::JoyconRight ||
vibration_device_handle.device_index == DeviceIndex::Right)) ||
(connected_controllers[npad_index].type == NPadControllerType::JoyRight &&
(vibration_device_handle.npad_type == NpadType::JoyconLeft ||
vibration_device_handle.device_index == DeviceIndex::Left))) {
return;
}
// Filter out vibrations with equivalent values to reduce unnecessary state changes.
if (vibration_value.amp_low == latest_vibration_values[npad_index][device_index].amp_low &&
vibration_value.amp_high == latest_vibration_values[npad_index][device_index].amp_high) {
return;
}
if (VibrateControllerAtIndex(npad_index, device_index, vibration_value)) {
latest_vibration_values[npad_index][device_index] = vibration_value;
}
}
Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
return last_processed_vibration;
void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
const std::vector<VibrationValue>& vibration_values) {
if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
return;
}
ASSERT_OR_EXECUTE_MSG(
vibration_device_handles.size() == vibration_values.size(), { return; },
"The amount of device handles does not match with the amount of vibration values,"
"this is undefined behavior!");
for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) {
VibrateController(vibration_device_handles[i], vibration_values[i]);
}
}
Controller_NPad::VibrationValue Controller_NPad::GetLastVibration(
const DeviceHandle& vibration_device_handle) const {
const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
return latest_vibration_values[npad_index][device_index];
}
void Controller_NPad::InitializeVibrationDevice(const DeviceHandle& vibration_device_handle) {
const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
InitializeVibrationDeviceAtIndex(npad_index, device_index);
}
void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index,
std::size_t device_index) {
if (vibrations[npad_index][device_index]) {
vibration_devices_mounted[npad_index][device_index] =
vibrations[npad_index][device_index]->GetStatus() == 1;
} else {
vibration_devices_mounted[npad_index][device_index] = false;
}
}
void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
permit_vibration_session_enabled = permit_vibration_session;
}
bool Controller_NPad::IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const {
const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
return vibration_devices_mounted[npad_index][device_index];
}
std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const {
@@ -696,31 +837,38 @@ void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::siz
void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
bool connected) {
if (!connected) {
DisconnectNPadAtIndex(npad_index);
DisconnectNpadAtIndex(npad_index);
return;
}
if (controller == NPadControllerType::Handheld) {
Settings::values.players[HANDHELD_INDEX].controller_type =
Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type =
MapNPadToSettingsType(controller);
Settings::values.players[HANDHELD_INDEX].connected = true;
Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
connected_controllers[HANDHELD_INDEX] = {controller, true};
InitNewlyAddedController(HANDHELD_INDEX);
return;
}
Settings::values.players[npad_index].controller_type = MapNPadToSettingsType(controller);
Settings::values.players[npad_index].connected = true;
Settings::values.players.GetValue()[npad_index].controller_type =
MapNPadToSettingsType(controller);
Settings::values.players.GetValue()[npad_index].connected = true;
connected_controllers[npad_index] = {controller, true};
InitNewlyAddedController(npad_index);
}
void Controller_NPad::DisconnectNPad(u32 npad_id) {
DisconnectNPadAtIndex(NPadIdToIndex(npad_id));
void Controller_NPad::DisconnectNpad(u32 npad_id) {
DisconnectNpadAtIndex(NPadIdToIndex(npad_id));
}
void Controller_NPad::DisconnectNPadAtIndex(std::size_t npad_index) {
Settings::values.players[npad_index].connected = false;
void Controller_NPad::DisconnectNpadAtIndex(std::size_t npad_index) {
for (std::size_t device_idx = 0; device_idx < vibrations[npad_index].size(); ++device_idx) {
// Send an empty vibration to stop any vibrations.
VibrateControllerAtIndex(npad_index, device_idx, {});
vibration_devices_mounted[npad_index][device_idx] = false;
}
Settings::values.players.GetValue()[npad_index].connected = false;
connected_controllers[npad_index].is_connected = false;
auto& controller = shared_memory_entries[npad_index];
@@ -758,7 +906,7 @@ void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) {
(connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft &&
connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) {
// Disconnect the joycon at the second id and connect the dual joycon at the first index.
DisconnectNPad(npad_id_2);
DisconnectNpad(npad_id_2);
AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1);
}
}
@@ -830,14 +978,6 @@ void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_prot
unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled;
}
void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
can_controllers_vibrate = can_vibrate;
}
bool Controller_NPad::IsVibrationEnabled() const {
return can_controllers_vibrate;
}
void Controller_NPad::ClearAllConnectedControllers() {
for (auto& controller : connected_controllers) {
if (controller.is_connected && controller.type != NPadControllerType::None) {
@@ -882,7 +1022,7 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
return false;
}
// Handheld should not be supported in docked mode
if (Settings::values.use_docked_mode) {
if (Settings::values.use_docked_mode.GetValue()) {
return false;
}

View File

@@ -39,28 +39,30 @@ public:
// Called when input devices should be loaded
void OnLoadInputDevices() override;
struct NPadType {
union {
u32_le raw{};
BitField<0, 1, u32> pro_controller;
BitField<1, 1, u32> handheld;
BitField<2, 1, u32> joycon_dual;
BitField<3, 1, u32> joycon_left;
BitField<4, 1, u32> joycon_right;
BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible
};
enum class NPadControllerType {
None,
ProController,
Handheld,
JoyDual,
JoyLeft,
JoyRight,
Pokeball,
};
static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size");
struct Vibration {
f32 amp_low;
f32 freq_low;
f32 amp_high;
f32 freq_high;
enum class NpadType : u8 {
ProController = 3,
Handheld = 4,
JoyconDual = 5,
JoyconLeft = 6,
JoyconRight = 7,
Pokeball = 9,
};
enum class DeviceIndex : u8 {
Left = 0,
Right = 1,
None = 2,
};
static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size");
enum class GyroscopeZeroDriftMode : u32 {
Loose = 0,
@@ -73,7 +75,7 @@ public:
Horizontal = 1,
};
enum class NPadAssignments : u32_le {
enum class NpadAssignments : u32 {
Dual = 0,
Single = 1,
};
@@ -84,15 +86,36 @@ public:
None = 2,
};
enum class NPadControllerType {
None,
ProController,
Handheld,
JoyDual,
JoyLeft,
JoyRight,
Pokeball,
struct DeviceHandle {
NpadType npad_type{};
u8 npad_id{};
DeviceIndex device_index{};
INSERT_PADDING_BYTES(1);
};
static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size");
struct NpadStyleSet {
union {
u32_le raw{};
BitField<0, 1, u32> pro_controller;
BitField<1, 1, u32> handheld;
BitField<2, 1, u32> joycon_dual;
BitField<3, 1, u32> joycon_left;
BitField<4, 1, u32> joycon_right;
BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible
};
};
static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
struct VibrationValue {
f32 amp_low{0.0f};
f32 freq_low{160.0f};
f32 amp_high{0.0f};
f32 freq_high{320.0f};
};
static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size");
struct LedPattern {
explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
@@ -110,12 +133,12 @@ public:
};
};
void SetSupportedStyleSet(NPadType style_set);
NPadType GetSupportedStyleSet() const;
void SetSupportedStyleSet(NpadStyleSet style_set);
NpadStyleSet GetSupportedStyleSet() const;
void SetSupportedNPadIdTypes(u8* data, std::size_t length);
void SetSupportedNpadIdTypes(u8* data, std::size_t length);
void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
std::size_t GetSupportedNPadIdTypesSize() const;
std::size_t GetSupportedNpadIdTypesSize() const;
void SetHoldType(NpadHoldType joy_hold_type);
NpadHoldType GetHoldType() const;
@@ -123,12 +146,26 @@ public:
void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode);
NpadHandheldActivationMode GetNpadHandheldActivationMode() const;
void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode);
void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode);
void VibrateController(const std::vector<u32>& controllers,
const std::vector<Vibration>& vibrations);
bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
const VibrationValue& vibration_value);
Vibration GetLastVibration() const;
void VibrateController(const DeviceHandle& vibration_device_handle,
const VibrationValue& vibration_value);
void VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
const std::vector<VibrationValue>& vibration_values);
VibrationValue GetLastVibration(const DeviceHandle& vibration_device_handle) const;
void InitializeVibrationDevice(const DeviceHandle& vibration_device_handle);
void InitializeVibrationDeviceAtIndex(std::size_t npad_index, std::size_t device_index);
void SetPermitVibrationSession(bool permit_vibration_session);
bool IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const;
std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
void SignalStyleSetChangedEvent(u32 npad_id) const;
@@ -138,8 +175,8 @@ public:
// Adds a new controller at an index with connection status.
void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected);
void DisconnectNPad(u32 npad_id);
void DisconnectNPadAtIndex(std::size_t index);
void DisconnectNpad(u32 npad_id);
void DisconnectNpadAtIndex(std::size_t index);
void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
@@ -148,8 +185,6 @@ public:
LedPattern GetLedPattern(u32 npad_id);
bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const;
void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id);
void SetVibrationEnabled(bool can_vibrate);
bool IsVibrationEnabled() const;
void ClearAllConnectedControllers();
void DisconnectAllConnectedControllers();
void ConnectAllDisconnectedControllers();
@@ -324,8 +359,8 @@ private:
};
struct NPadEntry {
NPadType joy_styles;
NPadAssignments pad_assignment;
NpadStyleSet joy_styles;
NpadAssignments pad_assignment;
ColorReadError single_color_error;
ControllerColor single_color;
@@ -368,7 +403,7 @@ private:
u32 press_state{};
NPadType style{};
NpadStyleSet style{};
std::array<NPadEntry, 10> shared_memory_entries{};
using ButtonArray = std::array<
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
@@ -376,22 +411,28 @@ private:
using StickArray = std::array<
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
10>;
using VibrationArray = std::array<std::array<std::unique_ptr<Input::VibrationDevice>,
Settings::NativeVibration::NUM_VIBRATIONS_HID>,
10>;
using MotionArray = std::array<
std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTION_HID>,
std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>,
10>;
ButtonArray buttons;
StickArray sticks;
VibrationArray vibrations;
MotionArray motions;
std::vector<u32> supported_npad_id_types{};
NpadHoldType hold_type{NpadHoldType::Vertical};
NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
// Each controller should have their own styleset changed event
std::array<Kernel::EventPair, 10> styleset_changed_events;
Vibration last_processed_vibration{};
std::array<std::array<std::chrono::steady_clock::time_point, 2>, 10> last_vibration_timepoints;
std::array<std::array<VibrationValue, 2>, 10> latest_vibration_values{};
bool permit_vibration_session_enabled{false};
std::array<std::array<bool, 2>, 10> vibration_devices_mounted{};
std::array<ControllerHolder, 10> connected_controllers{};
std::array<bool, 10> unintended_home_button_input_protection{};
GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
bool can_controllers_vibrate{true};
bool sixaxis_sensors_enabled{true};
bool sixaxis_at_rest{true};
std::array<ControllerPad, 10> npad_pad_states{};

File diff suppressed because it is too large Load Diff

View File

@@ -86,17 +86,15 @@ public:
private:
void CreateAppletResource(Kernel::HLERequestContext& ctx);
void ActivateXpad(Kernel::HLERequestContext& ctx);
void GetXpadIDs(Kernel::HLERequestContext& ctx);
void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx);
void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
void ActivateDebugPad(Kernel::HLERequestContext& ctx);
void ActivateTouchScreen(Kernel::HLERequestContext& ctx);
void ActivateMouse(Kernel::HLERequestContext& ctx);
void ActivateKeyboard(Kernel::HLERequestContext& ctx);
void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx);
void ActivateGesture(Kernel::HLERequestContext& ctx);
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
void ActivateXpad(Kernel::HLERequestContext& ctx);
void GetXpadIDs(Kernel::HLERequestContext& ctx);
void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx);
void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
@@ -104,6 +102,7 @@ private:
void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
void ActivateGesture(Kernel::HLERequestContext& ctx);
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx);
@@ -112,6 +111,7 @@ private:
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx);
void DisconnectNpad(Kernel::HLERequestContext& ctx);
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx);
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx);
@@ -125,15 +125,16 @@ private:
void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx);
void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx);
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
void SendVibrationValue(Kernel::HLERequestContext& ctx);
void SendVibrationValues(Kernel::HLERequestContext& ctx);
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
void SendVibrationValue(Kernel::HLERequestContext& ctx);
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx);
void PermitVibration(Kernel::HLERequestContext& ctx);
void IsVibrationPermitted(Kernel::HLERequestContext& ctx);
void SendVibrationValues(Kernel::HLERequestContext& ctx);
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
void IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx);
void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
void StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
@@ -146,6 +147,22 @@ private:
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
enum class VibrationDeviceType : u32 {
LinearResonantActuator = 1,
};
enum class VibrationDevicePosition : u32 {
None = 0,
Left = 1,
Right = 2,
};
struct VibrationDeviceInfo {
VibrationDeviceType type{};
VibrationDevicePosition position{};
};
static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
std::shared_ptr<IAppletResource> applet_resource;
Core::System& system;
};

View File

@@ -9,6 +9,7 @@
#include "common/alignment.h"
#include "common/hex_util.h"
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/memory/page_table.h"

View File

@@ -7,6 +7,7 @@
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/lm/lm.h"
#include "core/hle/service/lm/manager.h"

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/vfs.h"
@@ -29,8 +30,8 @@ IAccountProxyInterface::IAccountProxyInterface() : ServiceFramework{"IAccountPro
IAccountProxyInterface::~IAccountProxyInterface() = default;
IApplicationManagerInterface::IApplicationManagerInterface()
: ServiceFramework{"IApplicationManagerInterface"} {
IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_)
: ServiceFramework{"IApplicationManagerInterface"}, system{system_} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "ListApplicationRecord"},
@@ -298,7 +299,8 @@ void IApplicationManagerInterface::GetApplicationControlData(Kernel::HLERequestC
const auto size = ctx.GetWriteBufferSize();
const FileSys::PatchManager pm{title_id};
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
system.GetContentProvider()};
const auto control = pm.GetControlMetadata();
std::vector<u8> out;
@@ -538,14 +540,14 @@ IFactoryResetInterface::IFactoryResetInterface::IFactoryResetInterface()
IFactoryResetInterface::~IFactoryResetInterface() = default;
NS::NS(const char* name) : ServiceFramework{name} {
NS::NS(const char* name, Core::System& system_) : ServiceFramework{name}, system{system_} {
// clang-format off
static const FunctionInfo functions[] = {
{7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
{7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
{7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"},
{7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
{7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"},
{7996, &NS::PushIApplicationManagerInterface, "GetApplicationManagerInterface"},
{7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
{7998, &NS::PushInterface<IContentManagementInterface>, "GetContentManagementInterface"},
{7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
@@ -558,7 +560,7 @@ NS::NS(const char* name) : ServiceFramework{name} {
NS::~NS() = default;
std::shared_ptr<IApplicationManagerInterface> NS::GetApplicationManagerInterface() const {
return GetInterface<IApplicationManagerInterface>();
return GetInterface<IApplicationManagerInterface>(system);
}
class NS_DEV final : public ServiceFramework<NS_DEV> {
@@ -678,11 +680,11 @@ public:
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
std::make_shared<NS>("ns:am2")->InstallAsService(service_manager);
std::make_shared<NS>("ns:ec")->InstallAsService(service_manager);
std::make_shared<NS>("ns:rid")->InstallAsService(service_manager);
std::make_shared<NS>("ns:rt")->InstallAsService(service_manager);
std::make_shared<NS>("ns:web")->InstallAsService(service_manager);
std::make_shared<NS>("ns:am2", system)->InstallAsService(service_manager);
std::make_shared<NS>("ns:ec", system)->InstallAsService(service_manager);
std::make_shared<NS>("ns:rid", system)->InstallAsService(service_manager);
std::make_shared<NS>("ns:rt", system)->InstallAsService(service_manager);
std::make_shared<NS>("ns:web", system)->InstallAsService(service_manager);
std::make_shared<NS_DEV>()->InstallAsService(service_manager);
std::make_shared<NS_SU>()->InstallAsService(service_manager);

View File

@@ -6,6 +6,10 @@
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service {
namespace FileSystem {
@@ -22,7 +26,7 @@ public:
class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
public:
explicit IApplicationManagerInterface();
explicit IApplicationManagerInterface(Core::System& system_);
~IApplicationManagerInterface() override;
ResultVal<u8> GetApplicationDesiredLanguage(u32 supported_languages);
@@ -32,6 +36,8 @@ private:
void GetApplicationControlData(Kernel::HLERequestContext& ctx);
void GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx);
void ConvertApplicationLanguageToLanguageCode(Kernel::HLERequestContext& ctx);
Core::System& system;
};
class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
@@ -72,13 +78,13 @@ public:
class NS final : public ServiceFramework<NS> {
public:
explicit NS(const char* name);
explicit NS(const char* name, Core::System& system_);
~NS() override;
std::shared_ptr<IApplicationManagerInterface> GetApplicationManagerInterface() const;
private:
template <typename T>
template <typename T, typename... Args>
void PushInterface(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called");
@@ -87,13 +93,23 @@ private:
rb.PushIpcInterface<T>();
}
template <typename T>
std::shared_ptr<T> GetInterface() const {
void PushIApplicationManagerInterface(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationManagerInterface>(system);
}
template <typename T, typename... Args>
std::shared_ptr<T> GetInterface(Args&&... args) const {
static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>,
"Not a base of ServiceFrameworkBase");
return std::make_shared<T>();
return std::make_shared<T>(std::forward<Args>(args)...);
}
Core::System& system;
};
/// Registers all NS services with the specified service manager.

View File

@@ -24,25 +24,37 @@ public:
explicit nvdevice(Core::System& system) : system{system} {}
virtual ~nvdevice() = default;
union Ioctl {
u32_le raw;
BitField<0, 8, u32> cmd;
BitField<8, 8, u32> group;
BitField<16, 14, u32> length;
BitField<30, 1, u32> is_in;
BitField<31, 1, u32> is_out;
};
/**
* Handles an ioctl request.
* Handles an ioctl1 request.
* @param command The ioctl command id.
* @param input A buffer containing the input data for the ioctl.
* @param output A buffer where the output data will be written to.
* @returns The result code of the ioctl.
*/
virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) = 0;
virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input,
std::vector<u8>& output) = 0;
/**
* Handles an ioctl2 request.
* @param command The ioctl command id.
* @param input A buffer containing the input data for the ioctl.
* @param inline_input A buffer containing the input data for the ioctl which has been inlined.
* @param output A buffer where the output data will be written to.
* @returns The result code of the ioctl.
*/
virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
/**
* Handles an ioctl3 request.
* @param command The ioctl command id.
* @param input A buffer containing the input data for the ioctl.
* @param output A buffer where the output data will be written to.
* @param inline_output A buffer where the inlined output data will be written to.
* @returns The result code of the ioctl.
*/
virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) = 0;
protected:
Core::System& system;

View File

@@ -18,11 +18,22 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_de
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
nvdisp_disp0 ::~nvdisp_disp0() = default;
u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) {
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input,
std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvdisp_disp0::Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvdisp_disp0::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,

View File

@@ -20,9 +20,11 @@ public:
explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvdisp_disp0() override;
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) override;
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) override;
/// Performs a screen flip, drawing the buffer pointed to by the handle.
void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,

View File

@@ -17,59 +17,77 @@
namespace Service::Nvidia::Devices {
namespace NvErrCodes {
constexpr u32 Success{};
constexpr u32 OutOfMemory{static_cast<u32>(-12)};
constexpr u32 InvalidInput{static_cast<u32>(-22)};
} // namespace NvErrCodes
nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
nvhost_as_gpu::~nvhost_as_gpu() = default;
u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocInitalizeExCommand:
return InitalizeEx(input, output);
case IoctlCommand::IocAllocateSpaceCommand:
return AllocateSpace(input, output);
case IoctlCommand::IocMapBufferExCommand:
return MapBufferEx(input, output);
case IoctlCommand::IocBindChannelCommand:
return BindChannel(input, output);
case IoctlCommand::IocGetVaRegionsCommand:
return GetVARegions(input, output);
case IoctlCommand::IocUnmapBufferCommand:
return UnmapBuffer(input, output);
case IoctlCommand::IocFreeSpaceCommand:
return FreeSpace(input, output);
NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
std::vector<u8>& output) {
switch (command.group) {
case 'A':
switch (command.cmd) {
case 0x1:
return BindChannel(input, output);
case 0x2:
return AllocateSpace(input, output);
case 0x3:
return FreeSpace(input, output);
case 0x5:
return UnmapBuffer(input, output);
case 0x6:
return MapBufferEx(input, output);
case 0x8:
return GetVARegions(input, output);
case 0x9:
return InitalizeEx(input, output);
case 0x14:
return Remap(input, output);
default:
break;
}
break;
default:
break;
}
if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) {
return Remap(input, output);
}
UNIMPLEMENTED_MSG("Unimplemented ioctl command");
return 0;
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_as_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvhost_as_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) {
switch (command.group) {
case 'A':
switch (command.cmd) {
case 0x8:
return GetVARegions(input, output, inline_output);
default:
break;
}
break;
default:
break;
}
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlInitalizeEx params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size);
return 0;
return NvResult::Success;
}
u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlAllocSpace params{};
std::memcpy(&params, input.data(), input.size());
@@ -83,17 +101,17 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>&
params.offset = system.GPU().MemoryManager().Allocate(size, params.align);
}
auto result{NvErrCodes::Success};
auto result = NvResult::Success;
if (!params.offset) {
LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size);
result = NvErrCodes::OutOfMemory;
result = NvResult::InsufficientMemory;
}
std::memcpy(output.data(), &params, output.size());
return result;
}
u32 nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlFreeSpace params{};
std::memcpy(&params, input.data(), input.size());
@@ -104,15 +122,15 @@ u32 nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& outp
static_cast<std::size_t>(params.pages) * params.page_size);
std::memcpy(output.data(), &params, output.size());
return NvErrCodes::Success;
return NvResult::Success;
}
u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
const auto num_entries = input.size() / sizeof(IoctlRemapEntry);
LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
auto result{NvErrCodes::Success};
auto result = NvResult::Success;
std::vector<IoctlRemapEntry> entries(num_entries);
std::memcpy(entries.data(), input.data(), input.size());
@@ -123,7 +141,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
const auto object{nvmap_dev->GetObject(entry.nvmap_handle)};
if (!object) {
LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle);
result = NvErrCodes::InvalidInput;
result = NvResult::InvalidState;
break;
}
@@ -134,7 +152,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
if (!addr) {
LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!");
result = NvErrCodes::InvalidInput;
result = NvResult::InvalidState;
break;
}
}
@@ -143,7 +161,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
return result;
}
u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlMapBufferEx params{};
std::memcpy(&params, input.data(), input.size());
@@ -157,7 +175,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
if (!object) {
LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle);
std::memcpy(output.data(), &params, output.size());
return NvErrCodes::InvalidInput;
return NvResult::InvalidState;
}
// The real nvservices doesn't make a distinction between handles and ids, and
@@ -184,16 +202,16 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
params.mapping_size, params.offset);
std::memcpy(output.data(), &params, output.size());
return NvErrCodes::InvalidInput;
return NvResult::InvalidState;
}
std::memcpy(output.data(), &params, output.size());
return NvErrCodes::Success;
return NvResult::Success;
} else {
LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset);
std::memcpy(output.data(), &params, output.size());
return NvErrCodes::InvalidInput;
return NvResult::InvalidState;
}
}
@@ -213,10 +231,10 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size);
}
auto result{NvErrCodes::Success};
auto result = NvResult::Success;
if (!params.offset) {
LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size);
result = NvErrCodes::InvalidInput;
result = NvResult::InvalidState;
} else {
AddBufferMap(params.offset, size, physical_address, is_alloc);
}
@@ -225,7 +243,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
return result;
}
u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlUnmapBuffer params{};
std::memcpy(&params, input.data(), input.size());
@@ -238,20 +256,19 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
}
std::memcpy(output.data(), &params, output.size());
return NvErrCodes::Success;
return NvResult::Success;
}
u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlBindChannel params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}", params.fd);
channel = params.fd;
return 0;
return NvResult::Success;
}
u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetVaRegions params{};
std::memcpy(&params, input.data(), input.size());
@@ -270,7 +287,31 @@ u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& o
// TODO(ogniK): This probably can stay stubbed but should add support way way later
std::memcpy(output.data(), &params, output.size());
return 0;
return NvResult::Success;
}
NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) {
IoctlGetVaRegions params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
params.buf_size);
params.buf_size = 0x30;
params.regions[0].offset = 0x04000000;
params.regions[0].page_size = 0x1000;
params.regions[0].pages = 0x3fbfff;
params.regions[1].offset = 0x04000000;
params.regions[1].page_size = 0x10000;
params.regions[1].pages = 0x1bffff;
// TODO(ogniK): This probably can stay stubbed but should add support way way later
std::memcpy(output.data(), &params, output.size());
std::memcpy(inline_output.data(), &params.regions, inline_output.size());
return NvResult::Success;
}
std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const {

View File

@@ -30,9 +30,11 @@ public:
explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvhost_as_gpu() override;
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) override;
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) override;
private:
class BufferMap final {
@@ -74,32 +76,21 @@ private:
bool is_allocated{};
};
enum class IoctlCommand : u32_le {
IocInitalizeExCommand = 0x40284109,
IocAllocateSpaceCommand = 0xC0184102,
IocRemapCommand = 0x00000014,
IocMapBufferExCommand = 0xC0284106,
IocBindChannelCommand = 0x40044101,
IocGetVaRegionsCommand = 0xC0404108,
IocUnmapBufferCommand = 0xC0084105,
IocFreeSpaceCommand = 0xC0104103,
};
struct IoctlInitalizeEx {
u32_le big_page_size; // depends on GPU's available_big_page_sizes; 0=default
s32_le as_fd; // ignored; passes 0
u32_le flags; // passes 0
u32_le reserved; // ignored; passes 0
u64_le unk0;
u64_le unk1;
u64_le unk2;
u32_le big_page_size{}; // depends on GPU's available_big_page_sizes; 0=default
s32_le as_fd{}; // ignored; passes 0
u32_le flags{}; // passes 0
u32_le reserved{}; // ignored; passes 0
u64_le unk0{};
u64_le unk1{};
u64_le unk2{};
};
static_assert(sizeof(IoctlInitalizeEx) == 40, "IoctlInitalizeEx is incorrect size");
struct IoctlAllocSpace {
u32_le pages;
u32_le page_size;
AddressSpaceFlags flags;
u32_le pages{};
u32_le page_size{};
AddressSpaceFlags flags{};
INSERT_PADDING_WORDS(1);
union {
u64_le offset;
@@ -109,70 +100,73 @@ private:
static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size");
struct IoctlFreeSpace {
u64_le offset;
u32_le pages;
u32_le page_size;
u64_le offset{};
u32_le pages{};
u32_le page_size{};
};
static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size");
struct IoctlRemapEntry {
u16_le flags;
u16_le kind;
u32_le nvmap_handle;
u32_le map_offset;
u32_le offset;
u32_le pages;
u16_le flags{};
u16_le kind{};
u32_le nvmap_handle{};
u32_le map_offset{};
u32_le offset{};
u32_le pages{};
};
static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size");
struct IoctlMapBufferEx {
AddressSpaceFlags flags; // bit0: fixed_offset, bit2: cacheable
u32_le kind; // -1 is default
u32_le nvmap_handle;
u32_le page_size; // 0 means don't care
s64_le buffer_offset;
u64_le mapping_size;
s64_le offset;
AddressSpaceFlags flags{}; // bit0: fixed_offset, bit2: cacheable
u32_le kind{}; // -1 is default
u32_le nvmap_handle{};
u32_le page_size{}; // 0 means don't care
s64_le buffer_offset{};
u64_le mapping_size{};
s64_le offset{};
};
static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size");
struct IoctlUnmapBuffer {
s64_le offset;
s64_le offset{};
};
static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size");
struct IoctlBindChannel {
u32_le fd;
s32_le fd{};
};
static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size");
struct IoctlVaRegion {
u64_le offset;
u32_le page_size;
u64_le offset{};
u32_le page_size{};
INSERT_PADDING_WORDS(1);
u64_le pages;
u64_le pages{};
};
static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size");
struct IoctlGetVaRegions {
u64_le buf_addr; // (contained output user ptr on linux, ignored)
u32_le buf_size; // forced to 2*sizeof(struct va_region)
u32_le reserved;
IoctlVaRegion regions[2];
u64_le buf_addr{}; // (contained output user ptr on linux, ignored)
u32_le buf_size{}; // forced to 2*sizeof(struct va_region)
u32_le reserved{};
IoctlVaRegion regions[2]{};
};
static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2,
"IoctlGetVaRegions is incorrect size");
u32 channel{};
s32 channel{};
u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output);
u32 AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output);
u32 Remap(const std::vector<u8>& input, std::vector<u8>& output);
u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
u32 FreeSpace(const std::vector<u8>& input, std::vector<u8>& output);
u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
NvResult InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output);
NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output);
NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output);
NvResult MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output);
NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output);
std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);

View File

@@ -20,41 +20,54 @@ nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface,
: nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {}
nvhost_ctrl::~nvhost_ctrl() = default;
u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocGetConfigCommand:
return NvOsGetConfigU32(input, output);
case IoctlCommand::IocCtrlEventWaitCommand:
return IocCtrlEventWait(input, output, false, ctrl);
case IoctlCommand::IocCtrlEventWaitAsyncCommand:
return IocCtrlEventWait(input, output, true, ctrl);
case IoctlCommand::IocCtrlEventRegisterCommand:
return IocCtrlEventRegister(input, output);
case IoctlCommand::IocCtrlEventUnregisterCommand:
return IocCtrlEventUnregister(input, output);
case IoctlCommand::IocCtrlClearEventWaitCommand:
return IocCtrlClearEventWait(input, output);
NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
switch (command.group) {
case 0x0:
switch (command.cmd) {
case 0x1b:
return NvOsGetConfigU32(input, output);
case 0x1c:
return IocCtrlClearEventWait(input, output);
case 0x1d:
return IocCtrlEventWait(input, output, false);
case 0x1e:
return IocCtrlEventWait(input, output, true);
case 0x1f:
return IocCtrlEventRegister(input, output);
case 0x20:
return IocCtrlEventUnregister(input, output);
}
break;
default:
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
break;
}
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_ctrl::Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvhost_ctrl::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
IocGetConfigParams params{};
std::memcpy(&params, input.data(), sizeof(params));
LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(),
params.param_str.data());
return 0x30006; // Returns error on production mode
return NvResult::ConfigVarNotFound; // Returns error on production mode
}
u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
bool is_async, IoctlCtrl& ctrl) {
NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
bool is_async) {
IocCtrlEventWaitParams params{};
std::memcpy(&params, input.data(), sizeof(params));
LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
@@ -126,10 +139,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
params.value |= event_id;
event.event.writable->Clear();
gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
if (!is_async && ctrl.fresh_call) {
ctrl.must_delay = true;
ctrl.timeout = params.timeout;
ctrl.event_id = event_id;
if (!is_async) {
return NvResult::Timeout;
}
std::memcpy(output.data(), &params, sizeof(params));
@@ -139,7 +149,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
return NvResult::BadParameter;
}
u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
IocCtrlEventRegisterParams params{};
std::memcpy(&params, input.data(), sizeof(params));
const u32 event_id = params.user_event_id & 0x00FF;
@@ -154,7 +164,8 @@ u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<
return NvResult::Success;
}
u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input,
std::vector<u8>& output) {
IocCtrlEventUnregisterParams params{};
std::memcpy(&params, input.data(), sizeof(params));
const u32 event_id = params.user_event_id & 0x00FF;
@@ -169,7 +180,7 @@ u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vecto
return NvResult::Success;
}
u32 nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
IocCtrlEventSignalParams params{};
std::memcpy(&params, input.data(), sizeof(params));

View File

@@ -18,132 +18,113 @@ public:
SyncpointManager& syncpoint_manager);
~nvhost_ctrl() override;
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) override;
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) override;
private:
enum class IoctlCommand : u32_le {
IocSyncptReadCommand = 0xC0080014,
IocSyncptIncrCommand = 0x40040015,
IocSyncptWaitCommand = 0xC00C0016,
IocModuleMutexCommand = 0x40080017,
IocModuleRegRDWRCommand = 0xC0180018,
IocSyncptWaitexCommand = 0xC0100019,
IocSyncptReadMaxCommand = 0xC008001A,
IocGetConfigCommand = 0xC183001B,
IocCtrlClearEventWaitCommand = 0xC004001C,
IocCtrlEventWaitCommand = 0xC010001D,
IocCtrlEventWaitAsyncCommand = 0xC010001E,
IocCtrlEventRegisterCommand = 0xC004001F,
IocCtrlEventUnregisterCommand = 0xC0040020,
IocCtrlEventKillCommand = 0x40080021,
};
struct IocSyncptReadParams {
u32_le id;
u32_le value;
u32_le id{};
u32_le value{};
};
static_assert(sizeof(IocSyncptReadParams) == 8, "IocSyncptReadParams is incorrect size");
struct IocSyncptIncrParams {
u32_le id;
u32_le id{};
};
static_assert(sizeof(IocSyncptIncrParams) == 4, "IocSyncptIncrParams is incorrect size");
struct IocSyncptWaitParams {
u32_le id;
u32_le thresh;
s32_le timeout;
u32_le id{};
u32_le thresh{};
s32_le timeout{};
};
static_assert(sizeof(IocSyncptWaitParams) == 12, "IocSyncptWaitParams is incorrect size");
struct IocModuleMutexParams {
u32_le id;
u32_le lock; // (0 = unlock and 1 = lock)
u32_le id{};
u32_le lock{}; // (0 = unlock and 1 = lock)
};
static_assert(sizeof(IocModuleMutexParams) == 8, "IocModuleMutexParams is incorrect size");
struct IocModuleRegRDWRParams {
u32_le id;
u32_le num_offsets;
u32_le block_size;
u32_le offsets;
u32_le values;
u32_le write;
u32_le id{};
u32_le num_offsets{};
u32_le block_size{};
u32_le offsets{};
u32_le values{};
u32_le write{};
};
static_assert(sizeof(IocModuleRegRDWRParams) == 24, "IocModuleRegRDWRParams is incorrect size");
struct IocSyncptWaitexParams {
u32_le id;
u32_le thresh;
s32_le timeout;
u32_le value;
u32_le id{};
u32_le thresh{};
s32_le timeout{};
u32_le value{};
};
static_assert(sizeof(IocSyncptWaitexParams) == 16, "IocSyncptWaitexParams is incorrect size");
struct IocSyncptReadMaxParams {
u32_le id;
u32_le value;
u32_le id{};
u32_le value{};
};
static_assert(sizeof(IocSyncptReadMaxParams) == 8, "IocSyncptReadMaxParams is incorrect size");
struct IocGetConfigParams {
std::array<char, 0x41> domain_str;
std::array<char, 0x41> param_str;
std::array<char, 0x101> config_str;
std::array<char, 0x41> domain_str{};
std::array<char, 0x41> param_str{};
std::array<char, 0x101> config_str{};
};
static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
struct IocCtrlEventSignalParams {
u32_le event_id;
u32_le event_id{};
};
static_assert(sizeof(IocCtrlEventSignalParams) == 4,
"IocCtrlEventSignalParams is incorrect size");
struct IocCtrlEventWaitParams {
u32_le syncpt_id;
u32_le threshold;
s32_le timeout;
u32_le value;
u32_le syncpt_id{};
u32_le threshold{};
s32_le timeout{};
u32_le value{};
};
static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size");
struct IocCtrlEventWaitAsyncParams {
u32_le syncpt_id;
u32_le threshold;
u32_le timeout;
u32_le value;
u32_le syncpt_id{};
u32_le threshold{};
u32_le timeout{};
u32_le value{};
};
static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16,
"IocCtrlEventWaitAsyncParams is incorrect size");
struct IocCtrlEventRegisterParams {
u32_le user_event_id;
u32_le user_event_id{};
};
static_assert(sizeof(IocCtrlEventRegisterParams) == 4,
"IocCtrlEventRegisterParams is incorrect size");
struct IocCtrlEventUnregisterParams {
u32_le user_event_id;
u32_le user_event_id{};
};
static_assert(sizeof(IocCtrlEventUnregisterParams) == 4,
"IocCtrlEventUnregisterParams is incorrect size");
struct IocCtrlEventKill {
u64_le user_events;
u64_le user_events{};
};
static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size");
u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async,
IoctlCtrl& ctrl);
u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
EventInterface& events_interface;
SyncpointManager& syncpoint_manager;

View File

@@ -15,39 +15,112 @@ namespace Service::Nvidia::Devices {
nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}
nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& input2, std::vector<u8>& output,
std::vector<u8>& output2, IoctlCtrl& ctrl, IoctlVersion version) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocGetCharacteristicsCommand:
return GetCharacteristics(input, output, output2, version);
case IoctlCommand::IocGetTPCMasksCommand:
return GetTPCMasks(input, output, output2, version);
case IoctlCommand::IocGetActiveSlotMaskCommand:
return GetActiveSlotMask(input, output);
case IoctlCommand::IocZcullGetCtxSizeCommand:
return ZCullGetCtxSize(input, output);
case IoctlCommand::IocZcullGetInfo:
return ZCullGetInfo(input, output);
case IoctlCommand::IocZbcSetTable:
return ZBCSetTable(input, output);
case IoctlCommand::IocZbcQueryTable:
return ZBCQueryTable(input, output);
case IoctlCommand::IocFlushL2:
return FlushL2(input, output);
case IoctlCommand::IocGetGpuTime:
return GetGpuTime(input, output);
default:
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
std::vector<u8>& output) {
switch (command.group) {
case 'G':
switch (command.cmd) {
case 0x1:
return ZCullGetCtxSize(input, output);
case 0x2:
return ZCullGetInfo(input, output);
case 0x3:
return ZBCSetTable(input, output);
case 0x4:
return ZBCQueryTable(input, output);
case 0x5:
return GetCharacteristics(input, output);
case 0x6:
return GetTPCMasks(input, output);
case 0x7:
return FlushL2(input, output);
case 0x14:
return GetActiveSlotMask(input, output);
case 0x1c:
return GetGpuTime(input, output);
default:
break;
}
break;
}
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& output2, IoctlVersion version) {
NvResult nvhost_ctrl_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvhost_ctrl_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input,
std::vector<u8>& output, std::vector<u8>& inline_output) {
switch (command.group) {
case 'G':
switch (command.cmd) {
case 0x5:
return GetCharacteristics(input, output, inline_output);
case 0x6:
return GetTPCMasks(input, output, inline_output);
default:
break;
}
break;
default:
break;
}
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input,
std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlCharacteristics params{};
std::memcpy(&params, input.data(), input.size());
params.gc.arch = 0x120;
params.gc.impl = 0xb;
params.gc.rev = 0xa1;
params.gc.num_gpc = 0x1;
params.gc.l2_cache_size = 0x40000;
params.gc.on_board_video_memory_size = 0x0;
params.gc.num_tpc_per_gpc = 0x2;
params.gc.bus_type = 0x20;
params.gc.big_page_size = 0x20000;
params.gc.compression_page_size = 0x20000;
params.gc.pde_coverage_bit_count = 0x1B;
params.gc.available_big_page_sizes = 0x30000;
params.gc.gpc_mask = 0x1;
params.gc.sm_arch_sm_version = 0x503;
params.gc.sm_arch_spa_version = 0x503;
params.gc.sm_arch_warp_count = 0x80;
params.gc.gpu_va_bit_count = 0x28;
params.gc.reserved = 0x0;
params.gc.flags = 0x55;
params.gc.twod_class = 0x902D;
params.gc.threed_class = 0xB197;
params.gc.compute_class = 0xB1C0;
params.gc.gpfifo_class = 0xB06F;
params.gc.inline_to_memory_class = 0xA140;
params.gc.dma_copy_class = 0xB0B5;
params.gc.max_fbps_count = 0x1;
params.gc.fbp_en_mask = 0x0;
params.gc.max_ltc_per_fbp = 0x2;
params.gc.max_lts_per_ltc = 0x1;
params.gc.max_tex_per_tpc = 0x0;
params.gc.max_gpc_count = 0x1;
params.gc.rop_l2_en_mask_0 = 0x21D70;
params.gc.rop_l2_en_mask_1 = 0x0;
params.gc.chipname = 0x6230326D67;
params.gc.gr_compbit_store_base_hw = 0x0;
params.gpu_characteristics_buf_size = 0xA0;
params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
std::memcpy(output.data(), &params, output.size());
return NvResult::Success;
}
NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlCharacteristics params{};
std::memcpy(&params, input.data(), input.size());
@@ -89,35 +162,36 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto
params.gpu_characteristics_buf_size = 0xA0;
params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
if (version == IoctlVersion::Version3) {
std::memcpy(output.data(), input.data(), output.size());
std::memcpy(output2.data(), &params.gc, output2.size());
} else {
std::memcpy(output.data(), &params, output.size());
}
return 0;
std::memcpy(output.data(), input.data(), output.size());
std::memcpy(inline_output.data(), &params.gc, inline_output.size());
return NvResult::Success;
}
u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& output2, IoctlVersion version) {
NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGpuGetTpcMasksArgs params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
if (params.mask_buffer_size != 0) {
params.tcp_mask = 3;
}
if (version == IoctlVersion::Version3) {
std::memcpy(output.data(), input.data(), output.size());
std::memcpy(output2.data(), &params.tcp_mask, output2.size());
} else {
std::memcpy(output.data(), &params, output.size());
}
return 0;
std::memcpy(output.data(), &params, output.size());
return NvResult::Success;
}
u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) {
IoctlGpuGetTpcMasksArgs params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
if (params.mask_buffer_size != 0) {
params.tcp_mask = 3;
}
std::memcpy(output.data(), &params, output.size());
std::memcpy(inline_output.data(), &params.tcp_mask, inline_output.size());
return NvResult::Success;
}
NvResult nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlActiveSlotMask params{};
@@ -127,10 +201,10 @@ u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector
params.slot = 0x07;
params.mask = 0x01;
std::memcpy(output.data(), &params, output.size());
return 0;
return NvResult::Success;
}
u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlZcullGetCtxSize params{};
@@ -139,10 +213,10 @@ u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u
}
params.size = 0x1;
std::memcpy(output.data(), &params, output.size());
return 0;
return NvResult::Success;
}
u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlNvgpuGpuZcullGetInfoArgs params{};
@@ -162,47 +236,47 @@ u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>&
params.subregion_height_align_pixels = 0x40;
params.subregion_count = 0x10;
std::memcpy(output.data(), &params, output.size());
return 0;
return NvResult::Success;
}
u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IoctlZbcSetTable params{};
std::memcpy(&params, input.data(), input.size());
// TODO(ogniK): What does this even actually do?
std::memcpy(output.data(), &params, output.size());
return 0;
return NvResult::Success;
}
u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IoctlZbcQueryTable params{};
std::memcpy(&params, input.data(), input.size());
// TODO : To implement properly
std::memcpy(output.data(), &params, output.size());
return 0;
return NvResult::Success;
}
u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IoctlFlushL2 params{};
std::memcpy(&params, input.data(), input.size());
// TODO : To implement properly
std::memcpy(output.data(), &params, output.size());
return 0;
return NvResult::Success;
}
u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlGetGpuTime params{};
std::memcpy(&params, input.data(), input.size());
params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count());
std::memcpy(output.data(), &params, output.size());
return 0;
return NvResult::Success;
}
} // namespace Service::Nvidia::Devices

View File

@@ -16,32 +16,13 @@ public:
explicit nvhost_ctrl_gpu(Core::System& system);
~nvhost_ctrl_gpu() override;
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) override;
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) override;
private:
enum class IoctlCommand : u32_le {
IocGetCharacteristicsCommand = 0xC0B04705,
IocGetTPCMasksCommand = 0xC0184706,
IocGetActiveSlotMaskCommand = 0x80084714,
IocZcullGetCtxSizeCommand = 0x80044701,
IocZcullGetInfo = 0x80284702,
IocZbcSetTable = 0x402C4703,
IocZbcQueryTable = 0xC0344704,
IocFlushL2 = 0x40084707,
IocInvalICache = 0x4008470D,
IocSetMmudebugMode = 0x4008470E,
IocSetSmDebugMode = 0x4010470F,
IocWaitForPause = 0xC0084710,
IocGetTcpExceptionEnStatus = 0x80084711,
IocNumVsms = 0x80084712,
IocVsmsMapping = 0xC0044713,
IocGetErrorChannelUserData = 0xC008471B,
IocGetGpuTime = 0xC010471C,
IocGetCpuTimeCorrelationInfo = 0xC108471D,
};
struct IoctlGpuCharacteristics {
u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200)
u32_le impl; // 0xB (NVGPU_GPU_IMPL_GM20B)
@@ -159,17 +140,21 @@ private:
};
static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size");
u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& output2, IoctlVersion version);
u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, std::vector<u8>& output2,
IoctlVersion version);
u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output);
u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output);
NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output);
NvResult GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
NvResult ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
NvResult ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output);
NvResult ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Service::Nvidia::Devices

View File

@@ -23,107 +23,132 @@ nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
nvhost_gpu::~nvhost_gpu() = default;
u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocSetNVMAPfdCommand:
return SetNVMAPfd(input, output);
case IoctlCommand::IocSetClientDataCommand:
return SetClientData(input, output);
case IoctlCommand::IocGetClientDataCommand:
return GetClientData(input, output);
case IoctlCommand::IocZCullBind:
return ZCullBind(input, output);
case IoctlCommand::IocSetErrorNotifierCommand:
return SetErrorNotifier(input, output);
case IoctlCommand::IocChannelSetPriorityCommand:
return SetChannelPriority(input, output);
case IoctlCommand::IocAllocGPFIFOEx2Command:
return AllocGPFIFOEx2(input, output);
case IoctlCommand::IocAllocObjCtxCommand:
return AllocateObjectContext(input, output);
case IoctlCommand::IocChannelGetWaitbaseCommand:
return GetWaitbase(input, output);
case IoctlCommand::IocChannelSetTimeoutCommand:
return ChannelSetTimeout(input, output);
case IoctlCommand::IocChannelSetTimeslice:
return ChannelSetTimeslice(input, output);
default:
NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
switch (command.group) {
case 0x0:
switch (command.cmd) {
case 0x3:
return GetWaitbase(input, output);
default:
break;
}
break;
case 'H':
switch (command.cmd) {
case 0x1:
return SetNVMAPfd(input, output);
case 0x3:
return ChannelSetTimeout(input, output);
case 0x8:
return SubmitGPFIFOBase(input, output, false);
case 0x9:
return AllocateObjectContext(input, output);
case 0xb:
return ZCullBind(input, output);
case 0xc:
return SetErrorNotifier(input, output);
case 0xd:
return SetChannelPriority(input, output);
case 0x1a:
return AllocGPFIFOEx2(input, output);
case 0x1b:
return SubmitGPFIFOBase(input, output, true);
case 0x1d:
return ChannelSetTimeslice(input, output);
default:
break;
}
break;
case 'G':
switch (command.cmd) {
case 0x14:
return SetClientData(input, output);
case 0x15:
return GetClientData(input, output);
default:
break;
}
break;
}
if (command.group == NVGPU_IOCTL_MAGIC) {
if (command.cmd == NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO) {
return SubmitGPFIFO(input, output);
}
if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) {
return KickoffPB(input, output, input2, version);
}
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
};
u32 nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) {
switch (command.group) {
case 'H':
switch (command.cmd) {
case 0x1b:
return SubmitGPFIFOBase(input, inline_input, output);
}
break;
}
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvhost_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
nvmap_fd = params.nvmap_fd;
return 0;
return NvResult::Success;
}
u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlClientData params{};
std::memcpy(&params, input.data(), input.size());
user_data = params.data;
return 0;
return NvResult::Success;
}
u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlClientData params{};
std::memcpy(&params, input.data(), input.size());
params.data = user_data;
std::memcpy(output.data(), &params, output.size());
return 0;
return NvResult::Success;
}
u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) {
std::memcpy(&zcull_params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va,
zcull_params.mode);
std::memcpy(output.data(), &zcull_params, output.size());
return 0;
return NvResult::Success;
}
u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetErrorNotifier params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset,
params.size, params.mem);
std::memcpy(output.data(), &params, output.size());
return 0;
return NvResult::Success;
}
u32 nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) {
std::memcpy(&channel_priority, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
return 0;
return NvResult::Success;
}
u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlAllocGpfifoEx2 params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV,
@@ -137,10 +162,10 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
params.fence_out = channel_fence;
std::memcpy(output.data(), &params, output.size());
return 0;
return NvResult::Success;
}
u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlAllocObjCtx params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num,
@@ -148,7 +173,7 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
params.obj_id = 0x0;
std::memcpy(output.data(), &params, output.size());
return 0;
return NvResult::Success;
}
static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
@@ -192,8 +217,8 @@ static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence
return result;
}
u32 nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
Tegra::CommandList&& entries) {
NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
Tegra::CommandList&& entries) {
LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
params.num_entries, params.flags.raw);
@@ -227,69 +252,70 @@ u32 nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& out
}
std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
return 0;
return NvResult::Success;
}
u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output,
bool kickoff) {
if (input.size() < sizeof(IoctlSubmitGpfifo)) {
UNIMPLEMENTED();
return NvResult::InvalidSize;
}
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
Tegra::CommandList entries(params.num_entries);
std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)],
params.num_entries * sizeof(Tegra::CommandListHeader));
return SubmitGPFIFOImpl(params, output, std::move(entries));
}
u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
const std::vector<u8>& input2, IoctlVersion version) {
if (input.size() < sizeof(IoctlSubmitGpfifo)) {
UNIMPLEMENTED();
}
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
Tegra::CommandList entries(params.num_entries);
if (version == IoctlVersion::Version2) {
std::memcpy(entries.command_lists.data(), input2.data(),
params.num_entries * sizeof(Tegra::CommandListHeader));
} else {
if (kickoff) {
system.Memory().ReadBlock(params.address, entries.command_lists.data(),
params.num_entries * sizeof(Tegra::CommandListHeader));
} else {
std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)],
params.num_entries * sizeof(Tegra::CommandListHeader));
}
return SubmitGPFIFOImpl(params, output, std::move(entries));
}
u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input,
const std::vector<u8>& input_inline,
std::vector<u8>& output) {
if (input.size() < sizeof(IoctlSubmitGpfifo)) {
UNIMPLEMENTED();
return NvResult::InvalidSize;
}
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
Tegra::CommandList entries(params.num_entries);
std::memcpy(entries.command_lists.data(), input_inline.data(), input_inline.size());
return SubmitGPFIFOImpl(params, output, std::move(entries));
}
NvResult nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetWaitbase params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
params.value = 0; // Seems to be hard coded at 0
std::memcpy(output.data(), &params, output.size());
return 0;
return NvResult::Success;
}
u32 nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlChannelSetTimeout params{};
std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout));
LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
return 0;
return NvResult::Success;
}
u32 nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetTimeslice params{};
std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice));
LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice);
channel_timeslice = params.timeslice;
return 0;
return NvResult::Success;
}
} // namespace Service::Nvidia::Devices

View File

@@ -20,43 +20,19 @@ class SyncpointManager;
namespace Service::Nvidia::Devices {
class nvmap;
constexpr u32 NVGPU_IOCTL_MAGIC('H');
constexpr u32 NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO(0x8);
constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
class nvhost_gpu final : public nvdevice {
public:
explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
SyncpointManager& syncpoint_manager);
~nvhost_gpu() override;
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) override;
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) override;
private:
enum class IoctlCommand : u32_le {
IocSetNVMAPfdCommand = 0x40044801,
IocAllocGPFIFOCommand = 0x40084805,
IocSetClientDataCommand = 0x40084714,
IocGetClientDataCommand = 0x80084715,
IocZCullBind = 0xc010480b,
IocSetErrorNotifierCommand = 0xC018480C,
IocChannelSetPriorityCommand = 0x4004480D,
IocEnableCommand = 0x0000480E,
IocDisableCommand = 0x0000480F,
IocPreemptCommand = 0x00004810,
IocForceResetCommand = 0x00004811,
IocEventIdControlCommand = 0x40084812,
IocGetErrorNotificationCommand = 0xC0104817,
IocAllocGPFIFOExCommand = 0x40204818,
IocAllocGPFIFOEx2Command = 0xC020481A,
IocAllocObjCtxCommand = 0xC0104809,
IocChannelGetWaitbaseCommand = 0xC0080003,
IocChannelSetTimeoutCommand = 0x40044803,
IocChannelSetTimeslice = 0xC004481D,
};
enum class CtxObjects : u32_le {
Ctx2D = 0x902D,
Ctx3D = 0xB197,
@@ -67,63 +43,63 @@ private:
};
struct IoctlSetNvmapFD {
u32_le nvmap_fd;
s32_le nvmap_fd{};
};
static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
struct IoctlChannelSetTimeout {
u32_le timeout;
u32_le timeout{};
};
static_assert(sizeof(IoctlChannelSetTimeout) == 4, "IoctlChannelSetTimeout is incorrect size");
struct IoctlAllocGPFIFO {
u32_le num_entries;
u32_le flags;
u32_le num_entries{};
u32_le flags{};
};
static_assert(sizeof(IoctlAllocGPFIFO) == 8, "IoctlAllocGPFIFO is incorrect size");
struct IoctlClientData {
u64_le data;
u64_le data{};
};
static_assert(sizeof(IoctlClientData) == 8, "IoctlClientData is incorrect size");
struct IoctlZCullBind {
u64_le gpu_va;
u32_le mode; // 0=global, 1=no_ctxsw, 2=separate_buffer, 3=part_of_regular_buf
u64_le gpu_va{};
u32_le mode{}; // 0=global, 1=no_ctxsw, 2=separate_buffer, 3=part_of_regular_buf
INSERT_PADDING_WORDS(1);
};
static_assert(sizeof(IoctlZCullBind) == 16, "IoctlZCullBind is incorrect size");
struct IoctlSetErrorNotifier {
u64_le offset;
u64_le size;
u32_le mem; // nvmap object handle
u64_le offset{};
u64_le size{};
u32_le mem{}; // nvmap object handle
INSERT_PADDING_WORDS(1);
};
static_assert(sizeof(IoctlSetErrorNotifier) == 24, "IoctlSetErrorNotifier is incorrect size");
struct IoctlChannelSetPriority {
u32_le priority;
u32_le priority{};
};
static_assert(sizeof(IoctlChannelSetPriority) == 4,
"IoctlChannelSetPriority is incorrect size");
struct IoctlSetTimeslice {
u32_le timeslice;
u32_le timeslice{};
};
static_assert(sizeof(IoctlSetTimeslice) == 4, "IoctlSetTimeslice is incorrect size");
struct IoctlEventIdControl {
u32_le cmd; // 0=disable, 1=enable, 2=clear
u32_le id;
u32_le cmd{}; // 0=disable, 1=enable, 2=clear
u32_le id{};
};
static_assert(sizeof(IoctlEventIdControl) == 8, "IoctlEventIdControl is incorrect size");
struct IoctlGetErrorNotification {
u64_le timestamp;
u32_le info32;
u16_le info16;
u16_le status; // always 0xFFFF
u64_le timestamp{};
u32_le info32{};
u16_le info16{};
u16_le status{}; // always 0xFFFF
};
static_assert(sizeof(IoctlGetErrorNotification) == 16,
"IoctlGetErrorNotification is incorrect size");
@@ -131,39 +107,39 @@ private:
static_assert(sizeof(Fence) == 8, "Fence is incorrect size");
struct IoctlAllocGpfifoEx {
u32_le num_entries;
u32_le flags;
u32_le unk0;
u32_le unk1;
u32_le unk2;
u32_le unk3;
u32_le unk4;
u32_le unk5;
u32_le num_entries{};
u32_le flags{};
u32_le unk0{};
u32_le unk1{};
u32_le unk2{};
u32_le unk3{};
u32_le unk4{};
u32_le unk5{};
};
static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size");
struct IoctlAllocGpfifoEx2 {
u32_le num_entries; // in
u32_le flags; // in
u32_le unk0; // in (1 works)
Fence fence_out; // out
u32_le unk1; // in
u32_le unk2; // in
u32_le unk3; // in
u32_le num_entries{}; // in
u32_le flags{}; // in
u32_le unk0{}; // in (1 works)
Fence fence_out{}; // out
u32_le unk1{}; // in
u32_le unk2{}; // in
u32_le unk3{}; // in
};
static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size");
struct IoctlAllocObjCtx {
u32_le class_num; // 0x902D=2d, 0xB197=3d, 0xB1C0=compute, 0xA140=kepler, 0xB0B5=DMA,
// 0xB06F=channel_gpfifo
u32_le flags;
u64_le obj_id; // (ignored) used for FREE_OBJ_CTX ioctl, which is not supported
u32_le class_num{}; // 0x902D=2d, 0xB197=3d, 0xB1C0=compute, 0xA140=kepler, 0xB0B5=DMA,
// 0xB06F=channel_gpfifo
u32_le flags{};
u64_le obj_id{}; // (ignored) used for FREE_OBJ_CTX ioctl, which is not supported
};
static_assert(sizeof(IoctlAllocObjCtx) == 16, "IoctlAllocObjCtx is incorrect size");
struct IoctlSubmitGpfifo {
u64_le address; // pointer to gpfifo entry structs
u32_le num_entries; // number of fence objects being submitted
u64_le address{}; // pointer to gpfifo entry structs
u32_le num_entries{}; // number of fence objects being submitted
union {
u32_le raw;
BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list
@@ -172,7 +148,7 @@ private:
BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
BitField<8, 1, u32_le> increment; // increment the returned fence
} flags;
Fence fence_out; // returned new fence object for others to wait on
Fence fence_out{}; // returned new fence object for others to wait on
u32 AddIncrementValue() const {
return flags.add_increment.Value() << 1;
@@ -182,33 +158,34 @@ private:
"IoctlSubmitGpfifo is incorrect size");
struct IoctlGetWaitbase {
u32 unknown; // seems to be ignored? Nintendo added this
u32 value;
u32 unknown{}; // seems to be ignored? Nintendo added this
u32 value{};
};
static_assert(sizeof(IoctlGetWaitbase) == 8, "IoctlGetWaitbase is incorrect size");
u32_le nvmap_fd{};
s32_le nvmap_fd{};
u64_le user_data{};
IoctlZCullBind zcull_params{};
u32_le channel_priority{};
u32_le channel_timeslice{};
u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
u32 SetClientData(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetClientData(const std::vector<u8>& input, std::vector<u8>& output);
u32 ZCullBind(const std::vector<u8>& input, std::vector<u8>& output);
u32 SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output);
u32 SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output);
u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
u32 SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
Tegra::CommandList&& entries);
u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
const std::vector<u8>& input2, IoctlVersion version);
u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
u32 ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
NvResult SetClientData(const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetClientData(const std::vector<u8>& input, std::vector<u8>& output);
NvResult ZCullBind(const std::vector<u8>& input, std::vector<u8>& output);
NvResult SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output);
NvResult SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output);
NvResult AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
NvResult AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
Tegra::CommandList&& entries);
NvResult SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output,
bool kickoff = false);
NvResult SubmitGPFIFOBase(const std::vector<u8>& input, const std::vector<u8>& input_inline,
std::vector<u8>& output);
NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
std::shared_ptr<nvmap> nvmap_dev;
SyncpointManager& syncpoint_manager;

View File

@@ -15,46 +15,58 @@ nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_de
: nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
nvhost_nvdec::~nvhost_nvdec() = default;
u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocSetNVMAPfdCommand:
return SetNVMAPfd(input);
case IoctlCommand::IocSubmit:
return Submit(input, output);
case IoctlCommand::IocGetSyncpoint:
return GetSyncpoint(input, output);
case IoctlCommand::IocGetWaitbase:
return GetWaitbase(input, output);
case IoctlCommand::IocMapBuffer:
case IoctlCommand::IocMapBuffer2:
case IoctlCommand::IocMapBuffer3:
case IoctlCommand::IocMapBufferEx:
return MapBuffer(input, output);
case IoctlCommand::IocUnmapBufferEx: {
// This command is sent when the video stream has ended, flush all video contexts
// This is usually sent in the folowing order: vic, nvdec, vic.
// Inform the GPU to clear any remaining nvdec buffers when this is detected.
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
Tegra::ChCommandHeaderList cmdlist(1);
cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F};
system.GPU().PushCommandBuffer(cmdlist);
[[fallthrough]]; // fallthrough to unmap buffers
};
case IoctlCommand::IocUnmapBuffer:
case IoctlCommand::IocUnmapBuffer2:
case IoctlCommand::IocUnmapBuffer3:
return UnmapBuffer(input, output);
case IoctlCommand::IocSetSubmitTimeout:
return SetSubmitTimeout(input, output);
NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input,
std::vector<u8>& output) {
switch (command.group) {
case 0x0:
switch (command.cmd) {
case 0x1:
return Submit(input, output);
case 0x2:
return GetSyncpoint(input, output);
case 0x3:
return GetWaitbase(input, output);
case 0x7:
return SetSubmitTimeout(input, output);
case 0x9:
return MapBuffer(input, output);
case 0xa: {
if (command.length == 0x1c) {
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
Tegra::ChCommandHeaderList cmdlist(1);
cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F};
system.GPU().PushCommandBuffer(cmdlist);
}
return UnmapBuffer(input, output);
}
default:
break;
}
break;
case 'H':
switch (command.cmd) {
case 0x1:
return SetNVMAPfd(input);
default:
break;
}
break;
}
UNIMPLEMENTED_MSG("Unimplemented ioctl 0x{:X}", command.raw);
return 0;
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvhost_nvdec::Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvhost_nvdec::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
} // namespace Service::Nvidia::Devices

View File

@@ -14,26 +14,11 @@ public:
explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvhost_nvdec() override;
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) override;
private:
enum class IoctlCommand : u32_le {
IocSetNVMAPfdCommand = 0x40044801,
IocSubmit = 0xC0400001,
IocGetSyncpoint = 0xC0080002,
IocGetWaitbase = 0xC0080003,
IocMapBuffer = 0xC01C0009,
IocMapBuffer2 = 0xC16C0009,
IocMapBuffer3 = 0xC15C0009,
IocMapBufferEx = 0xC0A40009,
IocUnmapBuffer = 0xC0A4000A,
IocUnmapBuffer2 = 0xC16C000A,
IocUnmapBufferEx = 0xC01C000A,
IocUnmapBuffer3 = 0xC15C000A,
IocSetSubmitTimeout = 0x40040007,
};
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) override;
};
} // namespace Service::Nvidia::Devices

View File

@@ -36,26 +36,20 @@ std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::s
}
} // Anonymous namespace
namespace NvErrCodes {
constexpr u32 Success{};
[[maybe_unused]] constexpr u32 OutOfMemory{static_cast<u32>(-12)};
constexpr u32 InvalidInput{static_cast<u32>(-22)};
} // namespace NvErrCodes
nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
nvhost_nvdec_common::~nvhost_nvdec_common() = default;
u32 nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
nvmap_fd = params.nvmap_fd;
return 0;
return NvResult::Success;
}
u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSubmit params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
@@ -83,12 +77,12 @@ u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& o
for (const auto& cmd_buffer : command_buffers) {
auto object = nvmap_dev->GetObject(cmd_buffer.memory_id);
ASSERT_OR_EXECUTE(object, return NvErrCodes::InvalidInput;);
ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
const auto map = FindBufferMap(object->dma_map_addr);
if (!map) {
LOG_ERROR(Service_NVDRV, "Tried to submit an invalid offset 0x{:X} dma 0x{:X}",
object->addr, object->dma_map_addr);
return 0;
return NvResult::Success;
}
Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
gpu.MemoryManager().ReadBlock(map->StartAddr() + cmd_buffer.offset, cmdlist.data(),
@@ -105,10 +99,10 @@ u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& o
offset = WriteVectors(output, syncpt_increments, offset);
offset = WriteVectors(output, wait_checks, offset);
return NvErrCodes::Success;
return NvResult::Success;
}
u32 nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetSyncpoint params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
@@ -118,18 +112,18 @@ u32 nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<
params.value = 0;
std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
return NvErrCodes::Success;
return NvResult::Success;
}
u32 nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetWaitbase params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
params.value = 0; // Seems to be hard coded at 0
std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
return 0;
return NvResult::Success;
}
u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlMapBuffer params{};
std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
@@ -143,7 +137,7 @@ u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>
if (!object) {
LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
std::memcpy(output.data(), &params, output.size());
return NvErrCodes::InvalidInput;
return NvResult::InvalidState;
}
if (object->dma_map_addr == 0) {
// NVDEC and VIC memory is in the 32-bit address space
@@ -165,10 +159,10 @@ u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>
std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(),
cmd_buffer_handles.size() * sizeof(MapBufferEntry));
return NvErrCodes::Success;
return NvResult::Success;
}
u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlMapBuffer params{};
std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
@@ -181,7 +175,7 @@ u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u
if (!object) {
LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
std::memcpy(output.data(), &params, output.size());
return NvErrCodes::InvalidInput;
return NvResult::InvalidState;
}
if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) {
gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
@@ -193,13 +187,14 @@ u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u
object->dma_map_addr = 0;
}
std::memset(output.data(), 0, output.size());
return NvErrCodes::Success;
return NvResult::Success;
}
u32 nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input,
std::vector<u8>& output) {
std::memcpy(&submit_timeout, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
return NvErrCodes::Success;
return NvResult::Success;
}
std::optional<nvhost_nvdec_common::BufferMap> nvhost_nvdec_common::FindBufferMap(

View File

@@ -18,9 +18,37 @@ public:
explicit nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvhost_nvdec_common() override;
virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) = 0;
/**
* Handles an ioctl1 request.
* @param command The ioctl command id.
* @param input A buffer containing the input data for the ioctl.
* @param output A buffer where the output data will be written to.
* @returns The result code of the ioctl.
*/
virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input,
std::vector<u8>& output) = 0;
/**
* Handles an ioctl2 request.
* @param command The ioctl command id.
* @param input A buffer containing the input data for the ioctl.
* @param inline_input A buffer containing the input data for the ioctl which has been inlined.
* @param output A buffer where the output data will be written to.
* @returns The result code of the ioctl.
*/
virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
/**
* Handles an ioctl3 request.
* @param command The ioctl command id.
* @param input A buffer containing the input data for the ioctl.
* @param output A buffer where the output data will be written to.
* @param inline_output A buffer where the inlined output data will be written to.
* @returns The result code of the ioctl.
*/
virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) = 0;
protected:
class BufferMap final {
@@ -63,102 +91,102 @@ protected:
};
struct IoctlSetNvmapFD {
u32_le nvmap_fd;
s32_le nvmap_fd{};
};
static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
struct IoctlSubmitCommandBuffer {
u32_le id;
u32_le offset;
u32_le count;
u32_le id{};
u32_le offset{};
u32_le count{};
};
static_assert(sizeof(IoctlSubmitCommandBuffer) == 0xC,
"IoctlSubmitCommandBuffer is incorrect size");
struct IoctlSubmit {
u32_le cmd_buffer_count;
u32_le relocation_count;
u32_le syncpoint_count;
u32_le fence_count;
u32_le cmd_buffer_count{};
u32_le relocation_count{};
u32_le syncpoint_count{};
u32_le fence_count{};
};
static_assert(sizeof(IoctlSubmit) == 0x10, "IoctlSubmit has incorrect size");
struct CommandBuffer {
s32 memory_id;
u32 offset;
s32 word_count;
s32 memory_id{};
u32 offset{};
s32 word_count{};
};
static_assert(sizeof(CommandBuffer) == 0xC, "CommandBuffer has incorrect size");
struct Reloc {
s32 cmdbuffer_memory;
s32 cmdbuffer_offset;
s32 target;
s32 target_offset;
s32 cmdbuffer_memory{};
s32 cmdbuffer_offset{};
s32 target{};
s32 target_offset{};
};
static_assert(sizeof(Reloc) == 0x10, "CommandBuffer has incorrect size");
struct SyncptIncr {
u32 id;
u32 increments;
u32 id{};
u32 increments{};
};
static_assert(sizeof(SyncptIncr) == 0x8, "CommandBuffer has incorrect size");
struct Fence {
u32 id;
u32 value;
u32 id{};
u32 value{};
};
static_assert(sizeof(Fence) == 0x8, "CommandBuffer has incorrect size");
struct IoctlGetSyncpoint {
// Input
u32_le param;
u32_le param{};
// Output
u32_le value;
u32_le value{};
};
static_assert(sizeof(IoctlGetSyncpoint) == 8, "IocGetIdParams has wrong size");
struct IoctlGetWaitbase {
u32_le unknown; // seems to be ignored? Nintendo added this
u32_le value;
u32_le unknown{}; // seems to be ignored? Nintendo added this
u32_le value{};
};
static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size");
struct IoctlMapBuffer {
u32_le num_entries;
u32_le data_address; // Ignored by the driver.
u32_le attach_host_ch_das;
u32_le num_entries{};
u32_le data_address{}; // Ignored by the driver.
u32_le attach_host_ch_das{};
};
static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
struct IocGetIdParams {
// Input
u32_le param;
u32_le param{};
// Output
u32_le value;
u32_le value{};
};
static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
// Used for mapping and unmapping command buffers
struct MapBufferEntry {
u32_le map_handle;
u32_le map_address;
u32_le map_handle{};
u32_le map_address{};
};
static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
/// Ioctl command implementations
u32 SetNVMAPfd(const std::vector<u8>& input);
u32 Submit(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
u32 SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
NvResult SetNVMAPfd(const std::vector<u8>& input);
NvResult Submit(const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
u32_le nvmap_fd{};
s32_le nvmap_fd{};
u32_le submit_timeout{};
std::shared_ptr<nvmap> nvmap_dev;

View File

@@ -13,28 +13,44 @@ namespace Service::Nvidia::Devices {
nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
nvhost_nvjpg::~nvhost_nvjpg() = default;
u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocSetNVMAPfdCommand:
return SetNVMAPfd(input, output);
NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input,
std::vector<u8>& output) {
switch (command.group) {
case 'H':
switch (command.cmd) {
case 0x1:
return SetNVMAPfd(input, output);
default:
break;
}
break;
default:
break;
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
u32 nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_nvjpg::Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvhost_nvjpg::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
nvmap_fd = params.nvmap_fd;
return 0;
return NvResult::Success;
}
} // namespace Service::Nvidia::Devices

View File

@@ -16,23 +16,21 @@ public:
explicit nvhost_nvjpg(Core::System& system);
~nvhost_nvjpg() override;
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) override;
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) override;
private:
enum class IoctlCommand : u32_le {
IocSetNVMAPfdCommand = 0x40044801,
};
struct IoctlSetNvmapFD {
u32_le nvmap_fd;
s32_le nvmap_fd{};
};
static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
u32_le nvmap_fd{};
s32_le nvmap_fd{};
u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Service::Nvidia::Devices

View File

@@ -15,36 +15,50 @@ nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
nvhost_vic::~nvhost_vic() = default;
u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocSetNVMAPfdCommand:
return SetNVMAPfd(input);
case IoctlCommand::IocSubmit:
return Submit(input, output);
case IoctlCommand::IocGetSyncpoint:
return GetSyncpoint(input, output);
case IoctlCommand::IocGetWaitbase:
return GetWaitbase(input, output);
case IoctlCommand::IocMapBuffer:
case IoctlCommand::IocMapBuffer2:
case IoctlCommand::IocMapBuffer3:
case IoctlCommand::IocMapBuffer4:
case IoctlCommand::IocMapBufferEx:
return MapBuffer(input, output);
case IoctlCommand::IocUnmapBuffer:
case IoctlCommand::IocUnmapBuffer2:
case IoctlCommand::IocUnmapBuffer3:
case IoctlCommand::IocUnmapBufferEx:
return UnmapBuffer(input, output);
NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
switch (command.group) {
case 0x0:
switch (command.cmd) {
case 0x1:
return Submit(input, output);
case 0x2:
return GetSyncpoint(input, output);
case 0x3:
return GetWaitbase(input, output);
case 0x9:
return MapBuffer(input, output);
case 0xa:
return UnmapBuffer(input, output);
default:
break;
}
break;
case 'H':
switch (command.cmd) {
case 0x1:
return SetNVMAPfd(input);
default:
break;
}
break;
default:
break;
}
UNIMPLEMENTED_MSG("Unimplemented ioctl 0x{:X}", command.raw);
return 0;
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvhost_vic::Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvhost_vic::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
} // namespace Service::Nvidia::Devices

View File

@@ -13,25 +13,11 @@ class nvhost_vic final : public nvhost_nvdec_common {
public:
explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvhost_vic();
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) override;
private:
enum class IoctlCommand : u32_le {
IocSetNVMAPfdCommand = 0x40044801,
IocSubmit = 0xC0400001,
IocGetSyncpoint = 0xC0080002,
IocGetWaitbase = 0xC0080003,
IocMapBuffer = 0xC01C0009,
IocMapBuffer2 = 0xC0340009,
IocMapBuffer3 = 0xC0140009,
IocMapBuffer4 = 0xC00C0009,
IocMapBufferEx = 0xC03C0009,
IocUnmapBuffer = 0xC03C000A,
IocUnmapBuffer2 = 0xC034000A,
IocUnmapBuffer3 = 0xC00C000A,
IocUnmapBufferEx = 0xC01C000A,
};
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) override;
};
} // namespace Service::Nvidia::Devices

View File

@@ -11,13 +11,6 @@
namespace Service::Nvidia::Devices {
namespace NvErrCodes {
enum {
OperationNotPermitted = -1,
InvalidValue = -22,
};
}
nvmap::nvmap(Core::System& system) : nvdevice(system) {
// Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to
// represent this.
@@ -26,6 +19,46 @@ nvmap::nvmap(Core::System& system) : nvdevice(system) {
nvmap::~nvmap() = default;
NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
switch (command.group) {
case 0x1:
switch (command.cmd) {
case 0x1:
return IocCreate(input, output);
case 0x3:
return IocFromId(input, output);
case 0x4:
return IocAlloc(input, output);
case 0x5:
return IocFree(input, output);
case 0x9:
return IocParam(input, output);
case 0xe:
return IocGetId(input, output);
default:
break;
}
break;
default:
break;
}
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvmap::Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
NvResult nvmap::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
VAddr nvmap::GetObjectAddress(u32 handle) const {
auto object = GetObject(handle);
ASSERT(object);
@@ -33,28 +66,6 @@ VAddr nvmap::GetObjectAddress(u32 handle) const {
return object->addr;
}
u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) {
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::Create:
return IocCreate(input, output);
case IoctlCommand::Alloc:
return IocAlloc(input, output);
case IoctlCommand::GetId:
return IocGetId(input, output);
case IoctlCommand::FromId:
return IocFromId(input, output);
case IoctlCommand::Param:
return IocParam(input, output);
case IoctlCommand::Free:
return IocFree(input, output);
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
u32 nvmap::CreateObject(u32 size) {
// Create a new nvmap object and obtain a handle to it.
auto object = std::make_shared<Object>();
@@ -70,35 +81,35 @@ u32 nvmap::CreateObject(u32 size) {
return handle;
}
u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
IocCreateParams params;
std::memcpy(&params, input.data(), sizeof(params));
LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
if (!params.size) {
LOG_ERROR(Service_NVDRV, "Size is 0");
return static_cast<u32>(NvErrCodes::InvalidValue);
return NvResult::BadValue;
}
params.handle = CreateObject(params.size);
std::memcpy(output.data(), &params, sizeof(params));
return 0;
return NvResult::Success;
}
u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
IocAllocParams params;
std::memcpy(&params, input.data(), sizeof(params));
LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
if (!params.handle) {
LOG_ERROR(Service_NVDRV, "Handle is 0");
return static_cast<u32>(NvErrCodes::InvalidValue);
return NvResult::BadValue;
}
if ((params.align - 1) & params.align) {
LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align);
return static_cast<u32>(NvErrCodes::InvalidValue);
return NvResult::BadValue;
}
const u32 min_alignment = 0x1000;
@@ -109,12 +120,12 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
auto object = GetObject(params.handle);
if (!object) {
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
return static_cast<u32>(NvErrCodes::InvalidValue);
return NvResult::BadValue;
}
if (object->status == Object::Status::Allocated) {
LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle);
return static_cast<u32>(NvErrCodes::OperationNotPermitted);
return NvResult::InsufficientMemory;
}
object->flags = params.flags;
@@ -124,10 +135,10 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
object->status = Object::Status::Allocated;
std::memcpy(output.data(), &params, sizeof(params));
return 0;
return NvResult::Success;
}
u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
IocGetIdParams params;
std::memcpy(&params, input.data(), sizeof(params));
@@ -135,22 +146,22 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
if (!params.handle) {
LOG_ERROR(Service_NVDRV, "Handle is zero");
return static_cast<u32>(NvErrCodes::InvalidValue);
return NvResult::BadValue;
}
auto object = GetObject(params.handle);
if (!object) {
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
return static_cast<u32>(NvErrCodes::OperationNotPermitted);
return NvResult::BadValue;
}
params.id = object->id;
std::memcpy(output.data(), &params, sizeof(params));
return 0;
return NvResult::Success;
}
u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
IocFromIdParams params;
std::memcpy(&params, input.data(), sizeof(params));
@@ -160,13 +171,13 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
[&](const auto& entry) { return entry.second->id == params.id; });
if (itr == handles.end()) {
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
return static_cast<u32>(NvErrCodes::InvalidValue);
return NvResult::BadValue;
}
auto& object = itr->second;
if (object->status != Object::Status::Allocated) {
LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
return static_cast<u32>(NvErrCodes::InvalidValue);
return NvResult::BadValue;
}
itr->second->refcount++;
@@ -175,10 +186,10 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
params.handle = itr->first;
std::memcpy(output.data(), &params, sizeof(params));
return 0;
return NvResult::Success;
}
u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
IocParamParams params;
@@ -189,12 +200,12 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
auto object = GetObject(params.handle);
if (!object) {
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
return static_cast<u32>(NvErrCodes::InvalidValue);
return NvResult::BadValue;
}
if (object->status != Object::Status::Allocated) {
LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
return static_cast<u32>(NvErrCodes::OperationNotPermitted);
return NvResult::BadValue;
}
switch (static_cast<ParamTypes>(params.param)) {
@@ -216,10 +227,10 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
}
std::memcpy(output.data(), &params, sizeof(params));
return 0;
return NvResult::Success;
}
u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
// TODO(Subv): These flags are unconfirmed.
enum FreeFlags {
Freed = 0,
@@ -234,14 +245,14 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
auto itr = handles.find(params.handle);
if (itr == handles.end()) {
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
return static_cast<u32>(NvErrCodes::InvalidValue);
return NvResult::BadValue;
}
if (!itr->second->refcount) {
LOG_ERROR(
Service_NVDRV,
"There is no references to this object. The object is already freed. handle={:08X}",
params.handle);
return static_cast<u32>(NvErrCodes::InvalidValue);
return NvResult::BadValue;
}
itr->second->refcount--;
@@ -261,7 +272,7 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
handles.erase(params.handle);
std::memcpy(output.data(), &params, sizeof(params));
return 0;
return NvResult::Success;
}
} // namespace Service::Nvidia::Devices

View File

@@ -19,13 +19,15 @@ public:
explicit nvmap(Core::System& system);
~nvmap() override;
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output) override;
/// Returns the allocated address of an nvmap object given its handle.
VAddr GetObjectAddress(u32 handle) const;
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) override;
/// Represents an nvmap object.
struct Object {
enum class Status { Created, Allocated };
@@ -58,76 +60,68 @@ private:
/// Mapping of currently allocated handles to the objects they represent.
std::unordered_map<u32, std::shared_ptr<Object>> handles;
enum class IoctlCommand : u32 {
Create = 0xC0080101,
FromId = 0xC0080103,
Alloc = 0xC0200104,
Free = 0xC0180105,
Param = 0xC00C0109,
GetId = 0xC008010E,
};
struct IocCreateParams {
// Input
u32_le size;
u32_le size{};
// Output
u32_le handle;
u32_le handle{};
};
static_assert(sizeof(IocCreateParams) == 8, "IocCreateParams has wrong size");
struct IocFromIdParams {
// Input
u32_le id;
u32_le id{};
// Output
u32_le handle;
u32_le handle{};
};
static_assert(sizeof(IocFromIdParams) == 8, "IocFromIdParams has wrong size");
struct IocAllocParams {
// Input
u32_le handle;
u32_le heap_mask;
u32_le flags;
u32_le align;
u8 kind;
u32_le handle{};
u32_le heap_mask{};
u32_le flags{};
u32_le align{};
u8 kind{};
INSERT_PADDING_BYTES(7);
u64_le addr;
u64_le addr{};
};
static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size");
struct IocFreeParams {
u32_le handle;
u32_le handle{};
INSERT_PADDING_BYTES(4);
u64_le address;
u32_le size;
u32_le flags;
u64_le address{};
u32_le size{};
u32_le flags{};
};
static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size");
struct IocParamParams {
// Input
u32_le handle;
u32_le param;
u32_le handle{};
u32_le param{};
// Output
u32_le result;
u32_le result{};
};
static_assert(sizeof(IocParamParams) == 12, "IocParamParams has wrong size");
struct IocGetIdParams {
// Output
u32_le id;
u32_le id{};
// Input
u32_le handle;
u32_le handle{};
};
static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
u32 CreateObject(u32 size);
u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocParam(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocFree(const std::vector<u8>& input, std::vector<u8>& output);
NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output);
NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Service::Nvidia::Devices

View File

@@ -23,124 +23,170 @@ void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
void NVDRV::Open(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NVDRV, "called");
const auto& buffer = ctx.ReadBuffer();
std::string device_name(buffer.begin(), buffer.end());
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
return;
}
const auto& buffer = ctx.ReadBuffer();
const std::string device_name(buffer.begin(), buffer.end());
DeviceFD fd = nvdrv->Open(device_name);
u32 fd = nvdrv->Open(device_name);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(fd);
rb.Push<u32>(0);
rb.Push<DeviceFD>(fd);
rb.PushEnum(fd != INVALID_NVDRV_FD ? NvResult::Success : NvResult::FileOperationFailed);
}
void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) {
IPC::RequestParser rp{ctx};
u32 fd = rp.Pop<u32>();
u32 command = rp.Pop<u32>();
/// Ioctl 3 has 2 outputs, first in the input params, second is the result
std::vector<u8> output(ctx.GetWriteBufferSize(0));
std::vector<u8> output2;
if (version == IoctlVersion::Version3) {
output2.resize((ctx.GetWriteBufferSize(1)));
}
/// Ioctl2 has 2 inputs. It's used to pass data directly instead of providing a pointer.
/// KickOfPB uses this
auto input = ctx.ReadBuffer(0);
std::vector<u8> input2;
if (version == IoctlVersion::Version2) {
input2 = ctx.ReadBuffer(1);
}
IoctlCtrl ctrl{};
u32 result = nvdrv->Ioctl(fd, command, input, input2, output, output2, ctrl, version);
if (ctrl.must_delay) {
ctrl.fresh_call = false;
ctx.SleepClientThread(
"NVServices::DelayedResponse", ctrl.timeout,
[=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
Kernel::ThreadWakeupReason reason) {
IoctlCtrl ctrl2{ctrl};
std::vector<u8> tmp_output = output;
std::vector<u8> tmp_output2 = output2;
const u32 ioctl_result = nvdrv->Ioctl(fd, command, input, input2, tmp_output,
tmp_output2, ctrl2, version);
ctx_.WriteBuffer(tmp_output, 0);
if (version == IoctlVersion::Version3) {
ctx_.WriteBuffer(tmp_output2, 1);
}
IPC::ResponseBuilder rb{ctx_, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(ioctl_result);
},
nvdrv->GetEventWriteable(ctrl.event_id));
} else {
ctx.WriteBuffer(output);
if (version == IoctlVersion::Version3) {
ctx.WriteBuffer(output2, 1);
}
}
void NVDRV::ServiceError(Kernel::HLERequestContext& ctx, NvResult result) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(result);
rb.PushEnum(result);
}
void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlBase(ctx, IoctlVersion::Version1);
void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto fd = rp.Pop<DeviceFD>();
const auto command = rp.PopRaw<Ioctl>();
LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
return;
}
// Check device
std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
const auto input_buffer = ctx.ReadBuffer(0);
const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer);
if (command.is_out != 0) {
ctx.WriteBuffer(output_buffer);
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushEnum(nv_result);
}
void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlBase(ctx, IoctlVersion::Version2);
IPC::RequestParser rp{ctx};
const auto fd = rp.Pop<DeviceFD>();
const auto command = rp.PopRaw<Ioctl>();
LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
return;
}
const auto input_buffer = ctx.ReadBuffer(0);
const auto input_inlined_buffer = ctx.ReadBuffer(1);
std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
const auto nv_result =
nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer);
if (command.is_out != 0) {
ctx.WriteBuffer(output_buffer);
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushEnum(nv_result);
}
void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlBase(ctx, IoctlVersion::Version3);
IPC::RequestParser rp{ctx};
const auto fd = rp.Pop<DeviceFD>();
const auto command = rp.PopRaw<Ioctl>();
LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
return;
}
const auto input_buffer = ctx.ReadBuffer(0);
std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1));
const auto nv_result =
nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline);
if (command.is_out != 0) {
ctx.WriteBuffer(output_buffer, 0);
ctx.WriteBuffer(output_buffer_inline, 1);
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushEnum(nv_result);
}
void NVDRV::Close(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NVDRV, "called");
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
return;
}
IPC::RequestParser rp{ctx};
u32 fd = rp.Pop<u32>();
const auto fd = rp.Pop<DeviceFD>();
const auto result = nvdrv->Close(fd);
auto result = nvdrv->Close(fd);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushEnum(result);
}
void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
is_initialized = true;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
rb.PushEnum(NvResult::Success);
}
void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u32 fd = rp.Pop<u32>();
// TODO(Blinkhawk): Figure the meaning of the flag at bit 16
u32 event_id = rp.Pop<u32>() & 0x000000FF;
const auto fd = rp.Pop<DeviceFD>();
const auto event_id = rp.Pop<u32>() & 0x00FF;
LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id);
IPC::ResponseBuilder rb{ctx, 3, 1};
rb.Push(RESULT_SUCCESS);
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
return;
}
const auto nv_result = nvdrv->VerifyFD(fd);
if (nv_result != NvResult::Success) {
LOG_ERROR(Service_NVDRV, "Invalid FD specified DeviceFD={}!", fd);
ServiceError(ctx, nv_result);
return;
}
if (event_id < MaxNvEvents) {
IPC::ResponseBuilder rb{ctx, 3, 1};
rb.Push(RESULT_SUCCESS);
auto event = nvdrv->GetEvent(event_id);
event->Clear();
rb.PushCopyObjects(event);
rb.Push<u32>(NvResult::Success);
rb.PushEnum(NvResult::Success);
} else {
rb.Push<u32>(0);
rb.Push<u32>(NvResult::BadParameter);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushEnum(NvResult::BadParameter);
}
}
@@ -151,7 +197,7 @@ void NVDRV::SetAruid(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
rb.PushEnum(NvResult::Success);
}
void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx) {
@@ -164,8 +210,9 @@ void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ct
void NVDRV::GetStatus(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushEnum(NvResult::Success);
}
void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) {
@@ -181,7 +228,7 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
: ServiceFramework(name), nvdrv(std::move(nvdrv)) {
static const FunctionInfo functions[] = {
{0, &NVDRV::Open, "Open"},
{1, &NVDRV::Ioctl, "Ioctl"},
{1, &NVDRV::Ioctl1, "Ioctl"},
{2, &NVDRV::Close, "Close"},
{3, &NVDRV::Initialize, "Initialize"},
{4, &NVDRV::QueryEvent, "QueryEvent"},

View File

@@ -23,7 +23,7 @@ public:
private:
void Open(Kernel::HLERequestContext& ctx);
void Ioctl(Kernel::HLERequestContext& ctx);
void Ioctl1(Kernel::HLERequestContext& ctx);
void Ioctl2(Kernel::HLERequestContext& ctx);
void Ioctl3(Kernel::HLERequestContext& ctx);
void Close(Kernel::HLERequestContext& ctx);
@@ -33,11 +33,13 @@ private:
void SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx);
void GetStatus(Kernel::HLERequestContext& ctx);
void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx);
void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version);
void ServiceError(Kernel::HLERequestContext& ctx, NvResult result);
std::shared_ptr<Module> nvdrv;
u64 pid{};
bool is_initialized{};
};
} // namespace Service::Nvidia

View File

@@ -1,12 +1,16 @@
#pragma once
#include <array>
#include "common/bit_field.h"
#include "common/common_types.h"
namespace Service::Nvidia {
constexpr u32 MaxSyncPoints = 192;
constexpr u32 MaxNvEvents = 64;
using DeviceFD = s32;
constexpr DeviceFD INVALID_NVDRV_FD = -1;
struct Fence {
s32 id;
@@ -20,11 +24,61 @@ struct MultiFence {
std::array<Fence, 4> fences;
};
enum NvResult : u32 {
Success = 0,
BadParameter = 4,
Timeout = 5,
ResourceError = 15,
enum class NvResult : u32 {
Success = 0x0,
NotImplemented = 0x1,
NotSupported = 0x2,
NotInitialized = 0x3,
BadParameter = 0x4,
Timeout = 0x5,
InsufficientMemory = 0x6,
ReadOnlyAttribute = 0x7,
InvalidState = 0x8,
InvalidAddress = 0x9,
InvalidSize = 0xA,
BadValue = 0xB,
AlreadyAllocated = 0xD,
Busy = 0xE,
ResourceError = 0xF,
CountMismatch = 0x10,
OverFlow = 0x11,
InsufficientTransferMemory = 0x1000,
InsufficientVideoMemory = 0x10000,
BadSurfaceColorScheme = 0x10001,
InvalidSurface = 0x10002,
SurfaceNotSupported = 0x10003,
DispInitFailed = 0x20000,
DispAlreadyAttached = 0x20001,
DispTooManyDisplays = 0x20002,
DispNoDisplaysAttached = 0x20003,
DispModeNotSupported = 0x20004,
DispNotFound = 0x20005,
DispAttachDissallowed = 0x20006,
DispTypeNotSupported = 0x20007,
DispAuthenticationFailed = 0x20008,
DispNotAttached = 0x20009,
DispSamePwrState = 0x2000A,
DispEdidFailure = 0x2000B,
DispDsiReadAckError = 0x2000C,
DispDsiReadInvalidResp = 0x2000D,
FileWriteFailed = 0x30000,
FileReadFailed = 0x30001,
EndOfFile = 0x30002,
FileOperationFailed = 0x30003,
DirOperationFailed = 0x30004,
EndOfDirList = 0x30005,
ConfigVarNotFound = 0x30006,
InvalidConfigVar = 0x30007,
LibraryNotFound = 0x30008,
SymbolNotFound = 0x30009,
MemoryMapFailed = 0x3000A,
IoctlFailed = 0x3000F,
AccessDenied = 0x30010,
DeviceNotFound = 0x30011,
KernelDriverNotFound = 0x30012,
FileNotFound = 0x30013,
PathAlreadyExists = 0x30014,
ModuleNotPresent = 0xA000E,
};
enum class EventState {
@@ -34,21 +88,13 @@ enum class EventState {
Busy = 3,
};
enum class IoctlVersion : u32 {
Version1,
Version2,
Version3,
};
struct IoctlCtrl {
// First call done to the servioce for services that call itself again after a call.
bool fresh_call{true};
// Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep
bool must_delay{};
// Timeout for the delay
s64 timeout{};
// NV Event Id
s32 event_id{-1};
union Ioctl {
u32_le raw;
BitField<0, 8, u32> cmd;
BitField<8, 8, u32> group;
BitField<16, 14, u32> length;
BitField<30, 1, u32> is_in;
BitField<31, 1, u32> is_out;
};
} // namespace Service::Nvidia

View File

@@ -5,6 +5,7 @@
#include <utility>
#include <fmt/format.h>
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/writable_event.h"
@@ -61,36 +62,101 @@ Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} {
Module::~Module() = default;
u32 Module::Open(const std::string& device_name) {
ASSERT_MSG(devices.find(device_name) != devices.end(), "Trying to open unknown device {}",
device_name);
NvResult Module::VerifyFD(DeviceFD fd) const {
if (fd < 0) {
LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
return NvResult::InvalidState;
}
if (open_files.find(fd) == open_files.end()) {
LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
return NvResult::NotImplemented;
}
return NvResult::Success;
}
DeviceFD Module::Open(const std::string& device_name) {
if (devices.find(device_name) == devices.end()) {
LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name);
return INVALID_NVDRV_FD;
}
auto device = devices[device_name];
const u32 fd = next_fd++;
const DeviceFD fd = next_fd++;
open_files[fd] = std::move(device);
return fd;
}
u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version) {
auto itr = open_files.find(fd);
ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
std::vector<u8>& output) {
if (fd < 0) {
LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
return NvResult::InvalidState;
}
auto& device = itr->second;
return device->ioctl({command}, input, input2, output, output2, ctrl, version);
const auto itr = open_files.find(fd);
if (itr == open_files.end()) {
LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
return NvResult::NotImplemented;
}
return itr->second->Ioctl1(command, input, output);
}
ResultCode Module::Close(u32 fd) {
auto itr = open_files.find(fd);
ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output) {
if (fd < 0) {
LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
return NvResult::InvalidState;
}
const auto itr = open_files.find(fd);
if (itr == open_files.end()) {
LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
return NvResult::NotImplemented;
}
return itr->second->Ioctl2(command, input, inline_input, output);
}
NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
std::vector<u8>& output, std::vector<u8>& inline_output) {
if (fd < 0) {
LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
return NvResult::InvalidState;
}
const auto itr = open_files.find(fd);
if (itr == open_files.end()) {
LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
return NvResult::NotImplemented;
}
return itr->second->Ioctl3(command, input, output, inline_output);
}
NvResult Module::Close(DeviceFD fd) {
if (fd < 0) {
LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
return NvResult::InvalidState;
}
const auto itr = open_files.find(fd);
if (itr == open_files.end()) {
LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
return NvResult::NotImplemented;
}
open_files.erase(itr);
// TODO(flerovium): return correct result code if operation failed.
return RESULT_SUCCESS;
return NvResult::Success;
}
void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {

View File

@@ -112,14 +112,23 @@ public:
return std::static_pointer_cast<T>(itr->second);
}
NvResult VerifyFD(DeviceFD fd) const;
/// Opens a device node and returns a file descriptor to it.
u32 Open(const std::string& device_name);
DeviceFD Open(const std::string& device_name);
/// Sends an ioctl command to the specified file descriptor.
u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2,
std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
IoctlVersion version);
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
std::vector<u8>& output);
NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
const std::vector<u8>& inline_input, std::vector<u8>& output);
NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
std::vector<u8>& output, std::vector<u8>& inline_output);
/// Closes a device file descriptor and returns operation success.
ResultCode Close(u32 fd);
NvResult Close(DeviceFD fd);
void SignalSyncpt(const u32 syncpoint_id, const u32 value);
@@ -132,10 +141,10 @@ private:
SyncpointManager syncpoint_manager;
/// Id to use for the next open file descriptor.
u32 next_fd = 1;
DeviceFD next_fd = 1;
/// Mapping of file descriptors to the devices they reference.
std::unordered_map<u32, std::shared_ptr<Devices::nvdevice>> open_files;
std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>> open_files;
/// Mapping of device node names to their implementation.
std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices;

View File

@@ -0,0 +1,69 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/olsc/olsc.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::OLSC {
class OLSC final : public ServiceFramework<OLSC> {
public:
explicit OLSC() : ServiceFramework{"olsc:u"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &OLSC::Initialize, "Initialize"},
{10, nullptr, "VerifySaveDataBackupLicenseAsync"},
{13, nullptr, "GetSaveDataBackupSetting"},
{14, &OLSC::SetSaveDataBackupSettingEnabled, "SetSaveDataBackupSettingEnabled"},
{15, nullptr, "SetCustomData"},
{16, nullptr, "DeleteSaveDataBackupSetting"},
{18, nullptr, "GetSaveDataBackupInfoCache"},
{19, nullptr, "UpdateSaveDataBackupInfoCacheAsync"},
{22, nullptr, "DeleteSaveDataBackupAsync"},
{25, nullptr, "ListDownloadableSaveDataBackupInfoAsync"},
{26, nullptr, "DownloadSaveDataBackupAsync"},
{9010, nullptr, "VerifySaveDataBackupLicenseAsyncForDebug"},
{9013, nullptr, "GetSaveDataBackupSettingForDebug"},
{9014, nullptr, "SetSaveDataBackupSettingEnabledForDebug"},
{9015, nullptr, "SetCustomDataForDebug"},
{9016, nullptr, "DeleteSaveDataBackupSettingForDebug"},
{9018, nullptr, "GetSaveDataBackupInfoCacheForDebug"},
{9019, nullptr, "UpdateSaveDataBackupInfoCacheAsyncForDebug"},
{9022, nullptr, "DeleteSaveDataBackupAsyncForDebug"},
{9025, nullptr, "ListDownloadableSaveDataBackupInfoAsyncForDebug"},
{9026, nullptr, "DownloadSaveDataBackupAsyncForDebug"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void Initialize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_OLSC, "(STUBBED) called");
initialized = true;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void SetSaveDataBackupSettingEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_OLSC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
bool initialized{};
};
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<OLSC>()->InstallAsService(service_manager);
}
} // namespace Service::OLSC

View File

@@ -0,0 +1,16 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
namespace Service::SM {
class ServiceManager;
}
namespace Service::OLSC {
/// Registers all SSL services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
} // namespace Service::OLSC

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"

View File

@@ -4,6 +4,7 @@
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/acc/profile_manager.h"

View File

@@ -51,6 +51,7 @@
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/nvflinger.h"
#include "core/hle/service/olsc/olsc.h"
#include "core/hle/service/pcie/pcie.h"
#include "core/hle/service/pctl/module.h"
#include "core/hle/service/pcv/pcv.h"
@@ -231,6 +232,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
NPNS::InstallInterfaces(*sm);
NS::InstallInterfaces(*sm, system);
Nvidia::InstallInterfaces(*sm, *nv_flinger, system);
OLSC::InstallInterfaces(*sm);
PCIe::InstallInterfaces(*sm);
PCTL::InstallInterfaces(*sm);
PCV::InstallInterfaces(*sm);

View File

@@ -771,7 +771,7 @@ private:
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
if (Settings::values.use_docked_mode) {
if (Settings::values.use_docked_mode.GetValue()) {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *

View File

@@ -114,7 +114,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
}
if (override_update) {
const FileSys::PatchManager patch_manager(metadata.GetTitleID());
const FileSys::PatchManager patch_manager(
metadata.GetTitleID(), system.GetFileSystemController(), system.GetContentProvider());
dir = patch_manager.PatchExeFS(dir);
}
@@ -160,7 +161,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
modules.clear();
const VAddr base_address{process.PageTable().GetCodeRegionStart()};
VAddr next_load_addr{base_address};
const FileSys::PatchManager pm{metadata.GetTitleID()};
const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(),
system.GetContentProvider()};
for (const auto& module : static_modules) {
const FileSys::VirtualFile module_file{dir->GetFile(module)};
if (!module_file) {

View File

@@ -10,6 +10,7 @@
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/kernel/process.h"
#include "core/loader/deconstructed_rom_directory.h"
#include "core/loader/elf.h"
@@ -194,15 +195,14 @@ AppLoader::~AppLoader() = default;
/**
* Get a loader for a file with a specific type
* @param file The file to load
* @param type The type of the file
* @param file the file to retrieve the loader for
* @param type the file type
* @param system The system context to use.
* @param file The file to retrieve the loader for
* @param type The file type
* @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
*/
static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileType type) {
static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file,
FileType type) {
switch (type) {
// Standard ELF file format.
case FileType::ELF:
return std::make_unique<AppLoader_ELF>(std::move(file));
@@ -221,7 +221,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
// NX XCI (nX Card Image) file format.
case FileType::XCI:
return std::make_unique<AppLoader_XCI>(std::move(file));
return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(),
system.GetContentProvider());
// NX NAX (NintendoAesXts) file format.
case FileType::NAX:
@@ -229,7 +230,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
// NX NSP (Nintendo Submission Package) file format
case FileType::NSP:
return std::make_unique<AppLoader_NSP>(std::move(file));
return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(),
system.GetContentProvider());
// NX KIP (Kernel Internal Process) file format
case FileType::KIP:
@@ -244,20 +246,21 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
}
}
std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file) {
std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file) {
FileType type = IdentifyFile(file);
FileType filename_type = GuessFromFilename(file->GetName());
const FileType filename_type = GuessFromFilename(file->GetName());
// Special case: 00 is either a NCA or NAX.
if (type != filename_type && !(file->GetName() == "00" && type == FileType::NAX)) {
LOG_WARNING(Loader, "File {} has a different type than its extension.", file->GetName());
if (FileType::Unknown == type)
if (FileType::Unknown == type) {
type = filename_type;
}
}
LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type));
return GetFileLoader(std::move(file), type);
return GetFileLoader(system, std::move(file), type);
}
} // namespace Loader

View File

@@ -290,9 +290,12 @@ protected:
/**
* Identifies a bootable file and return a suitable loader
* @param file The bootable file
* @return the best loader for this file
*
* @param system The system context.
* @param file The bootable file.
*
* @return the best loader for this file.
*/
std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file);
std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file);
} // namespace Loader

View File

@@ -149,7 +149,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, Core::S
// Apply cheats if they exist and the program has a valid title ID
if (pm) {
system.SetCurrentProcessBuildID(nso_header.build_id);
const auto cheats = pm->CreateCheatList(system, nso_header.build_id);
const auto cheats = pm->CreateCheatList(nso_header.build_id);
if (!cheats.empty()) {
system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size);
}

View File

@@ -21,26 +21,33 @@
namespace Loader {
AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file,
const Service::FileSystem::FileSystemController& fsc,
const FileSys::ContentProvider& content_provider)
: AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file)),
title_id(nsp->GetProgramTitleID()) {
if (nsp->GetStatus() != ResultStatus::Success)
if (nsp->GetStatus() != ResultStatus::Success) {
return;
}
if (nsp->IsExtractedType()) {
secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
} else {
const auto control_nca =
nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) {
return;
}
std::tie(nacp_file, icon_file) =
FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
std::tie(nacp_file, icon_file) = [this, &content_provider, &control_nca, &fsc] {
const FileSys::PatchManager pm{nsp->GetProgramTitleID(), fsc, content_provider};
return pm.ParseControlNCA(*control_nca);
}();
if (title_id == 0)
if (title_id == 0) {
return;
}
secondary_loader = std::make_unique<AppLoader_NCA>(
nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));

View File

@@ -9,15 +9,16 @@
#include "core/file_sys/vfs.h"
#include "core/loader/loader.h"
namespace Core {
class System;
}
namespace FileSys {
class ContentProvider;
class NACP;
class NSP;
} // namespace FileSys
namespace Service::FileSystem {
class FileSystemController;
}
namespace Loader {
class AppLoader_NCA;
@@ -25,7 +26,9 @@ class AppLoader_NCA;
/// Loads an XCI file
class AppLoader_NSP final : public AppLoader {
public:
explicit AppLoader_NSP(FileSys::VirtualFile file);
explicit AppLoader_NSP(FileSys::VirtualFile file,
const Service::FileSystem::FileSystemController& fsc,
const FileSys::ContentProvider& content_provider);
~AppLoader_NSP() override;
/**

View File

@@ -20,18 +20,24 @@
namespace Loader {
AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file)
AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file,
const Service::FileSystem::FileSystemController& fsc,
const FileSys::ContentProvider& content_provider)
: AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)),
nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) {
if (xci->GetStatus() != ResultStatus::Success)
if (xci->GetStatus() != ResultStatus::Success) {
return;
}
const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control);
if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) {
return;
}
std::tie(nacp_file, icon_file) =
FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(*control_nca);
std::tie(nacp_file, icon_file) = [this, &content_provider, &control_nca, &fsc] {
const FileSys::PatchManager pm{xci->GetProgramTitleID(), fsc, content_provider};
return pm.ParseControlNCA(*control_nca);
}();
}
AppLoader_XCI::~AppLoader_XCI() = default;

View File

@@ -9,15 +9,16 @@
#include "core/file_sys/vfs.h"
#include "core/loader/loader.h"
namespace Core {
class System;
}
namespace FileSys {
class ContentProvider;
class NACP;
class XCI;
} // namespace FileSys
namespace Service::FileSystem {
class FileSystemController;
}
namespace Loader {
class AppLoader_NCA;
@@ -25,7 +26,9 @@ class AppLoader_NCA;
/// Loads an XCI file
class AppLoader_XCI final : public AppLoader {
public:
explicit AppLoader_XCI(FileSys::VirtualFile file);
explicit AppLoader_XCI(FileSys::VirtualFile file,
const Service::FileSystem::FileSystemController& fsc,
const FileSys::ContentProvider& content_provider);
~AppLoader_XCI() override;
/**

View File

@@ -49,7 +49,7 @@ void LogSettings() {
};
LOG_INFO(Config, "yuzu Configuration:");
log_setting("Controls_UseDockedMode", values.use_docked_mode);
log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue());
log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0));
log_setting("System_CurrentUser", values.current_user);
log_setting("System_LanguageIndex", values.language_index.GetValue());
@@ -145,6 +145,12 @@ void RestoreGlobalState() {
values.rng_seed.SetGlobal(true);
values.custom_rtc.SetGlobal(true);
values.sound_index.SetGlobal(true);
// Controls
values.players.SetGlobal(true);
values.use_docked_mode.SetGlobal(true);
values.vibration_enabled.SetGlobal(true);
values.motion_enabled.SetGlobal(true);
}
void Sanitize() {

View File

@@ -65,6 +65,38 @@ private:
Type local{};
};
/**
* The InputSetting class allows for getting a reference to either the global or local members.
* This is required as we cannot easily modify the values of user-defined types within containers
* using the SetValue() member function found in the Setting class. The primary purpose of this
* class is to store an array of 10 PlayerInput structs for both the global and local (per-game)
* setting and allows for easily accessing and modifying both settings.
*/
template <typename Type>
class InputSetting final {
public:
InputSetting() = default;
explicit InputSetting(Type val) : global{val} {}
~InputSetting() = default;
void SetGlobal(bool to_global) {
use_global = to_global;
}
bool UsingGlobal() const {
return use_global;
}
Type& GetValue(bool need_global = false) {
if (use_global || need_global) {
return global;
}
return local;
}
private:
bool use_global = true;
Type global{};
Type local{};
};
struct TouchFromButtonMap {
std::string name;
std::vector<std::string> buttons;
@@ -133,9 +165,18 @@ struct Values {
Setting<s32> sound_index;
// Controls
std::array<PlayerInput, 10> players;
InputSetting<std::array<PlayerInput, 10>> players;
bool use_docked_mode;
Setting<bool> use_docked_mode;
Setting<bool> vibration_enabled;
Setting<bool> enable_accurate_vibrations;
Setting<bool> motion_enabled;
std::string motion_device;
std::string udp_input_address;
u16 udp_input_port;
u8 udp_pad_index;
bool mouse_enabled;
std::string mouse_device;
@@ -149,20 +190,15 @@ struct Values {
ButtonsRaw debug_pad_buttons;
AnalogsRaw debug_pad_analogs;
bool vibration_enabled;
bool motion_enabled;
std::string motion_device;
std::string touch_device;
TouchscreenInput touchscreen;
std::atomic_bool is_device_reload_pending{true};
bool use_touch_from_button;
std::string touch_device;
int touch_from_button_map_index;
std::string udp_input_address;
u16 udp_input_port;
u8 udp_pad_index;
std::vector<TouchFromButtonMap> touch_from_button_maps;
std::atomic_bool is_device_reload_pending{true};
// Data Storage
bool use_virtual_sd;
bool gamecard_inserted;
@@ -179,6 +215,7 @@ struct Values {
bool reporting_services;
bool quest_flag;
bool disable_macro_jit;
bool extended_logging;
// Misceallaneous
std::string log_filter;

View File

@@ -147,7 +147,9 @@ TelemetrySession::~TelemetrySession() {
}
}
void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
const Service::FileSystem::FileSystemController& fsc,
const FileSys::ContentProvider& content_provider) {
// Log one-time top-level information
AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId());
@@ -167,7 +169,10 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
app_loader.ReadTitle(name);
if (name.empty()) {
const auto metadata = FileSys::PatchManager(program_id).GetControlMetadata();
const auto metadata = [&content_provider, &fsc, program_id] {
const FileSys::PatchManager pm{program_id, fsc, content_provider};
return pm.GetControlMetadata();
}();
if (metadata.first != nullptr) {
name = metadata.first->GetApplicationName();
}
@@ -213,7 +218,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
Settings::values.use_assembly_shaders.GetValue());
AddField(field_type, "Renderer_UseAsynchronousShaders",
Settings::values.use_asynchronous_shaders.GetValue());
AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode.GetValue());
}
bool TelemetrySession::SubmitTestcase() {

View File

@@ -7,10 +7,18 @@
#include <string>
#include "common/telemetry.h"
namespace FileSys {
class ContentProvider;
}
namespace Loader {
class AppLoader;
}
namespace Service::FileSystem {
class FileSystemController;
}
namespace Core {
/**
@@ -40,10 +48,14 @@ public:
* - Title file format
* - Miscellaneous settings values.
*
* @param app_loader The application loader to use to retrieve
* title-specific information.
* @param app_loader The application loader to use to retrieve
* title-specific information.
* @param fsc Filesystem controller to use to retrieve info.
* @param content_provider Content provider to use to retrieve info.
*/
void AddInitialInfo(Loader::AppLoader& app_loader);
void AddInitialInfo(Loader::AppLoader& app_loader,
const Service::FileSystem::FileSystemController& fsc,
const FileSys::ContentProvider& content_provider);
/**
* Wrapper around the Telemetry::FieldCollection::AddField method.

View File

@@ -230,10 +230,8 @@ void Adapter::SendVibrations() {
vibration_changed = false;
}
bool Adapter::RumblePlay(std::size_t port, f32 amplitude) {
amplitude = std::clamp(amplitude, 0.0f, 1.0f);
const auto raw_amp = static_cast<u8>(amplitude * 0x8);
pads[port].rumble_amplitude = raw_amp;
bool Adapter::RumblePlay(std::size_t port, u8 amplitude) {
pads[port].rumble_amplitude = amplitude;
return rumble_enabled;
}

View File

@@ -77,8 +77,8 @@ public:
Adapter();
~Adapter();
/// Request a vibration for a controlelr
bool RumblePlay(std::size_t port, f32 amplitude);
/// Request a vibration for a controller
bool RumblePlay(std::size_t port, u8 amplitude);
/// Used for polling
void BeginConfiguration();

View File

@@ -15,7 +15,7 @@ namespace InputCommon {
class GCButton final : public Input::ButtonDevice {
public:
explicit GCButton(u32 port_, s32 button_, GCAdapter::Adapter* adapter)
explicit GCButton(u32 port_, s32 button_, const GCAdapter::Adapter* adapter)
: port(port_), button(button_), gcadapter(adapter) {}
~GCButton() override;
@@ -27,18 +27,10 @@ public:
return false;
}
bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
const float amplitude = amp_high + amp_low > 2.0f ? 1.0f : (amp_high + amp_low) * 0.5f;
const auto new_amp =
static_cast<f32>(pow(amplitude, 0.5f) * (3.0f - 2.0f * pow(amplitude, 0.15f)));
return gcadapter->RumblePlay(port, new_amp);
}
private:
const u32 port;
const s32 button;
GCAdapter::Adapter* gcadapter;
const GCAdapter::Adapter* gcadapter;
};
class GCAxisButton final : public Input::ButtonDevice {
@@ -299,4 +291,42 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
return params;
}
class GCVibration final : public Input::VibrationDevice {
public:
explicit GCVibration(u32 port_, GCAdapter::Adapter* adapter)
: port(port_), gcadapter(adapter) {}
u8 GetStatus() const override {
return gcadapter->RumblePlay(port, 0);
}
bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override {
const auto mean_amplitude = (amp_low + amp_high) * 0.5f;
const auto processed_amplitude =
static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8);
return gcadapter->RumblePlay(port, processed_amplitude);
}
private:
const u32 port;
GCAdapter::Adapter* gcadapter;
};
/// An vibration device factory that creates vibration devices from GC Adapter
GCVibrationFactory::GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
: adapter(std::move(adapter_)) {}
/**
* Creates a vibration device from a joystick
* @param params contains parameters for creating the device:
* - "port": the nth gcpad on the adapter
*/
std::unique_ptr<Input::VibrationDevice> GCVibrationFactory::Create(
const Common::ParamPackage& params) {
const auto port = static_cast<u32>(params.Get("port", 0));
return std::make_unique<GCVibration>(port, adapter.get());
}
} // namespace InputCommon

View File

@@ -64,4 +64,15 @@ private:
bool polling = false;
};
/// A vibration device factory creates vibration devices from GC Adapter
class GCVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
public:
explicit GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override;
private:
std::shared_ptr<GCAdapter::Adapter> adapter;
};
} // namespace InputCommon

View File

@@ -28,6 +28,8 @@ struct InputSubsystem::Impl {
Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
gcvibration = std::make_shared<GCVibrationFactory>(gcadapter);
Input::RegisterFactory<Input::VibrationDevice>("gcpad", gcvibration);
keyboard = std::make_shared<Keyboard>();
Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
@@ -64,9 +66,11 @@ struct InputSubsystem::Impl {
#endif
Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
Input::UnregisterFactory<Input::VibrationDevice>("gcpad");
gcbuttons.reset();
gcanalog.reset();
gcvibration.reset();
Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
@@ -78,7 +82,7 @@ struct InputSubsystem::Impl {
[[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
std::vector<Common::ParamPackage> devices = {
Common::ParamPackage{{"display", "Any"}, {"class", "any"}},
Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "key"}},
Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}},
};
#ifdef HAVE_SDL2
auto sdl_devices = sdl->GetInputDevices();
@@ -96,10 +100,6 @@ struct InputSubsystem::Impl {
if (!params.Has("class") || params.Get("class", "") == "any") {
return {};
}
if (params.Get("class", "") == "key") {
// TODO consider returning the SDL key codes for the default keybindings
return {};
}
if (params.Get("class", "") == "gcpad") {
return gcadapter->GetAnalogMappingForDevice(params);
}
@@ -116,10 +116,6 @@ struct InputSubsystem::Impl {
if (!params.Has("class") || params.Get("class", "") == "any") {
return {};
}
if (params.Get("class", "") == "key") {
// TODO consider returning the SDL key codes for the default keybindings
return {};
}
if (params.Get("class", "") == "gcpad") {
return gcadapter->GetButtonMappingForDevice(params);
}
@@ -150,6 +146,7 @@ struct InputSubsystem::Impl {
#endif
std::shared_ptr<GCButtonFactory> gcbuttons;
std::shared_ptr<GCAnalogFactory> gcanalog;
std::shared_ptr<GCVibrationFactory> gcvibration;
std::shared_ptr<UDPMotionFactory> udpmotion;
std::shared_ptr<UDPTouchFactory> udptouch;
std::shared_ptr<CemuhookUDP::Client> udp;

View File

@@ -13,7 +13,7 @@ namespace InputCommon {
class MotionInput {
public:
MotionInput(f32 new_kp, f32 new_ki, f32 new_kd);
explicit MotionInput(f32 new_kp, f32 new_ki, f32 new_kd);
MotionInput(const MotionInput&) = default;
MotionInput& operator=(const MotionInput&) = default;
@@ -33,16 +33,17 @@ public:
void UpdateRotation(u64 elapsed_time);
void UpdateOrientation(u64 elapsed_time);
std::array<Common::Vec3f, 3> GetOrientation() const;
Common::Vec3f GetAcceleration() const;
Common::Vec3f GetGyroscope() const;
Common::Vec3f GetRotations() const;
Common::Quaternion<f32> GetQuaternion() const;
Input::MotionStatus GetMotion() const;
Input::MotionStatus GetRandomMotion(int accel_magnitude, int gyro_magnitude) const;
[[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
[[nodiscard]] Common::Vec3f GetAcceleration() const;
[[nodiscard]] Common::Vec3f GetGyroscope() const;
[[nodiscard]] Common::Vec3f GetRotations() const;
[[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
[[nodiscard]] Input::MotionStatus GetMotion() const;
[[nodiscard]] Input::MotionStatus GetRandomMotion(int accel_magnitude,
int gyro_magnitude) const;
bool IsMoving(f32 sensitivity) const;
bool IsCalibrated(f32 sensitivity) const;
[[nodiscard]] bool IsMoving(f32 sensitivity) const;
[[nodiscard]] bool IsCalibrated(f32 sensitivity) const;
private:
void ResetOrientation();

View File

@@ -80,30 +80,13 @@ public:
return static_cast<float>(state.axes.at(axis)) / (32767.0f * range);
}
bool RumblePlay(f32 amp_low, f32 amp_high, u32 time) {
const u16 raw_amp_low = static_cast<u16>(amp_low * 0xFFFF);
const u16 raw_amp_high = static_cast<u16>(amp_high * 0xFFFF);
// Lower drastically the number of state changes
if (raw_amp_low >> 11 == last_state_rumble_low >> 11 &&
raw_amp_high >> 11 == last_state_rumble_high >> 11) {
if (raw_amp_low + raw_amp_high != 0 ||
last_state_rumble_low + last_state_rumble_high == 0) {
return false;
}
}
// Don't change state if last vibration was < 20ms
const auto now = std::chrono::system_clock::now();
if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_vibration) <
std::chrono::milliseconds(20)) {
return raw_amp_low + raw_amp_high == 0;
bool RumblePlay(u16 amp_low, u16 amp_high) {
if (sdl_controller) {
return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0;
} else if (sdl_joystick) {
return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0;
}
last_vibration = now;
last_state_rumble_low = raw_amp_low;
last_state_rumble_high = raw_amp_high;
if (sdl_joystick) {
SDL_JoystickRumble(sdl_joystick.get(), raw_amp_low, raw_amp_high, time);
}
return false;
}
@@ -172,9 +155,6 @@ private:
} state;
std::string guid;
int port;
u16 last_state_rumble_high = 0;
u16 last_state_rumble_low = 0;
std::chrono::time_point<std::chrono::system_clock> last_vibration;
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
mutable std::mutex mutex;
@@ -327,12 +307,6 @@ public:
return joystick->GetButton(button);
}
bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
const f32 new_amp_low = pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f));
const f32 new_amp_high = pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f));
return joystick->RumblePlay(new_amp_low, new_amp_high, 250);
}
private:
std::shared_ptr<SDLJoystick> joystick;
int button;
@@ -416,6 +390,31 @@ private:
const float range;
};
class SDLVibration final : public Input::VibrationDevice {
public:
explicit SDLVibration(std::shared_ptr<SDLJoystick> joystick_)
: joystick(std::move(joystick_)) {}
u8 GetStatus() const override {
joystick->RumblePlay(1, 1);
return joystick->RumblePlay(0, 0);
}
bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override {
const auto process_amplitude = [](f32 amplitude) {
return static_cast<u16>((amplitude + std::pow(amplitude, 0.3f)) * 0.5f * 0xFFFF);
};
const auto processed_amp_low = process_amplitude(amp_low);
const auto processed_amp_high = process_amplitude(amp_high);
return joystick->RumblePlay(processed_amp_low, processed_amp_high);
}
private:
std::shared_ptr<SDLJoystick> joystick;
};
class SDLDirectionMotion final : public Input::MotionDevice {
public:
explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
@@ -558,7 +557,7 @@ class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
public:
explicit SDLAnalogFactory(SDLState& state_) : state(state_) {}
/**
* Creates analog device from joystick axes
* Creates an analog device from joystick axes
* @param params contains parameters for creating the device:
* - "guid": the guid of the joystick to bind
* - "port": the nth joystick of the same type
@@ -584,6 +583,26 @@ private:
SDLState& state;
};
/// An vibration device factory that creates vibration devices from SDL joystick
class SDLVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
public:
explicit SDLVibrationFactory(SDLState& state_) : state(state_) {}
/**
* Creates a vibration device from a joystick
* @param params contains parameters for creating the device:
* - "guid": the guid of the joystick to bind
* - "port": the nth joystick of the same type
*/
std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override {
const std::string guid = params.Get("guid", "0");
const int port = params.Get("port", 0);
return std::make_unique<SDLVibration>(state.GetSDLJoystickByGUID(guid, port));
}
private:
SDLState& state;
};
/// A motion device factory that creates motion devices from SDL joystick
class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> {
public:
@@ -650,11 +669,13 @@ private:
SDLState::SDLState() {
using namespace Input;
analog_factory = std::make_shared<SDLAnalogFactory>(*this);
button_factory = std::make_shared<SDLButtonFactory>(*this);
analog_factory = std::make_shared<SDLAnalogFactory>(*this);
vibration_factory = std::make_shared<SDLVibrationFactory>(*this);
motion_factory = std::make_shared<SDLMotionFactory>(*this);
RegisterFactory<AnalogDevice>("sdl", analog_factory);
RegisterFactory<ButtonDevice>("sdl", button_factory);
RegisterFactory<AnalogDevice>("sdl", analog_factory);
RegisterFactory<VibrationDevice>("sdl", vibration_factory);
RegisterFactory<MotionDevice>("sdl", motion_factory);
// If the frontend is going to manage the event loop, then we don't start one here
@@ -676,7 +697,7 @@ SDLState::SDLState() {
using namespace std::chrono_literals;
while (initialized) {
SDL_PumpEvents();
std::this_thread::sleep_for(5ms);
std::this_thread::sleep_for(1ms);
}
});
}
@@ -691,6 +712,7 @@ SDLState::~SDLState() {
using namespace Input;
UnregisterFactory<ButtonDevice>("sdl");
UnregisterFactory<AnalogDevice>("sdl");
UnregisterFactory<VibrationDevice>("sdl");
UnregisterFactory<MotionDevice>("sdl");
CloseJoysticks();
@@ -1045,7 +1067,6 @@ public:
void Start(const std::string& device_id) override {
SDLPoller::Start(device_id);
// Load the game controller
// Reset stored axes
analog_x_axis = -1;
analog_y_axis = -1;
@@ -1058,40 +1079,21 @@ public:
if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) {
continue;
}
// Simplify controller config by testing if game controller support is enabled.
if (event.type == SDL_JOYAXISMOTION) {
const auto axis = event.jaxis.axis;
if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
auto* const controller = joystick->GetSDLGameController()) {
const auto axis_left_x =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX)
.value.axis;
const auto axis_left_y =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY)
.value.axis;
const auto axis_right_x =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX)
.value.axis;
const auto axis_right_y =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY)
.value.axis;
if (axis == axis_left_x || axis == axis_left_y) {
analog_x_axis = axis_left_x;
analog_y_axis = axis_left_y;
break;
} else if (axis == axis_right_x || axis == axis_right_y) {
analog_x_axis = axis_right_x;
analog_y_axis = axis_right_y;
break;
}
// In order to return a complete analog param, we need inputs for both axes.
// First we take the x-axis (horizontal) input, then the y-axis (vertical) input.
if (analog_x_axis == -1) {
analog_x_axis = axis;
} else if (analog_y_axis == -1 && analog_x_axis != axis) {
analog_y_axis = axis;
}
} else {
// If the press wasn't accepted as a joy axis, check for a button press
auto button_press = button_poller.FromEvent(event);
if (button_press) {
return *button_press;
}
}
// If the press wasn't accepted as a joy axis, check for a button press
auto button_press = button_poller.FromEvent(event);
if (button_press) {
return *button_press;
}
}
@@ -1104,6 +1106,7 @@ public:
return params;
}
}
return {};
}

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